kernel — Standalone Kernel Runner

3
Source Files
~1.2k
Lines of Code
20
CLI Flags
C++
Language

CLI Tool Standalone Execution Simulation File Format Testing & Development

📚 What is this?

The kernel module provides the oclgrind-kernel command-line tool — a standalone entry point for executing OpenCL kernels without writing an OpenCL host application. Instead of calling clCreateContext, clBuildProgram, clSetKernelArg, etc., you describe everything in a simple text file (a “sim file”), and the Simulation class orchestrates the entire lifecycle for you.

💡 Real-world analogy

Imagine you are a chef who normally works in a large restaurant kitchen (the full OpenCL host program) — with waiters taking orders, a cashier, and a seating host. Now imagine a tiny food truck with a single window. You hand in a recipe card (the sim file) listing the dish name and ingredients, and the truck cooks it immediately — no waiters, no seating chart, no cashier. That food truck is oclgrind-kernel: a stripped-down shortcut to get straight to the cooking (kernel execution) without the ceremony of the full restaurant (runtime API).

Module Architecture

The kernel module consists of two key components: a Simulation class that manages the entire kernel execution lifecycle, and a CLI entry point that translates command-line flags into environment variables understood by the core engine.

flowchart TD
  CLI["oclgrind-kernel.cpp
CLI Entry Point"] PA["parseArguments()
Process 20+ flags"] SE["setEnvironment()
Map flags → OCLGRIND_* vars"] SIM["Simulation::load()
Parse sim file"] OP["Open program file
(source or bitcode)"] BUILD["Program::build()
Compile OpenCL C → LLVM IR"] BC["Program::createFromBitcodeFile()
Load precompiled bitcode"] KRN["Program::createKernel()
Extract named kernel"] ARGS["parseArgument() × N
Parse typed argument headers"] ALLOC["Memory::allocateBuffer()
Allocate global buffers"] RUN["Simulation::run()"] KI["KernelInvocation::run()
Execute NDRange"] DUMP["dumpArgument()
Print output buffers"] CLI --> PA PA --> SE PA -->|"simfile path"| SIM SIM --> OP OP -->|"magic 0x4243"| BC OP -->|"else source"| BUILD BC --> KRN BUILD --> KRN KRN --> ARGS ARGS --> ALLOC ALLOC --> RUN RUN --> KI KI --> DUMP

Simulation Lifecycle

sequenceDiagram
  participant User as User / Shell
  participant CLI as oclgrind-kernel
  participant SIM as Simulation
  participant CTX as Context
  participant PRG as Program
  participant KRN as Kernel
  participant MEM as Memory
  participant KI as KernelInvocation

  User->>CLI: oclgrind-kernel [flags] sim.file
  CLI->>CLI: parseArguments() → setEnvironment()
  CLI->>SIM: Simulation()
  SIM->>CTX: new Context()
  CLI->>SIM: load("sim.file")
  SIM->>SIM: Read program path, kernel name, NDRange, WGSize
  alt Source (.cl)
    SIM->>PRG: new Program(ctx, source)
    SIM->>PRG: build(BUILD, "")
  else Bitcode (.bc)
    SIM->>PRG: createFromBitcodeFile(ctx, path)
  end
  SIM->>KRN: program→createKernel(name)
  loop For each argument
    SIM->>SIM: parseArgument(index) — read header tokens
    SIM->>MEM: allocateBuffer(size, flags)
    SIM->>MEM: store(data, address, size)
    SIM->>KRN: setArgument(index, value)
  end
  CLI->>SIM: run(dumpGlobalMemory)
  SIM->>KI: KernelInvocation::run(ctx, kernel, 3, offset, ndrange, wgsize)
  KI-->>SIM: Execution complete
  SIM->>SIM: dumpArgument() for each dump-flagged arg
  SIM-->>CLI: return
  CLI-->>User: Output results

Key Classes & Functions

SymbolFileDescription
SimulationSimulation.hMain class: owns Context, Kernel, and Program. Orchestrates load → parse → run lifecycle.
Simulation::load()Simulation.cppParses the sim file: reads program path, kernel name, NDRange, work-group size, and all arguments. Returns false on any error.
Simulation::run()Simulation.cppAsserts all arguments are set, calls KernelInvocation::run(), then dumps flagged output buffers.
Simulation::parseArgument()Simulation.cppParses a single argument: reads header tokens (size, type, fill, range, dump, ro/wo, null, noinit, hex), allocates memory, stores data.
Simulation::get<T>()Simulation.cppTemplate helper that reads a typed value from the sim file, skipping comments (#) and blank lines.
Simulation::dumpArgument<T>()Simulation.cppReads back a buffer from global memory and prints element-by-element to stdout, supporting hex formatting.
Simulation::parseFill<T>()Simulation.cppFills an argument buffer with a single repeated value (e.g., fill=0).
Simulation::parseRange<T>()Simulation.cppFills an argument buffer with a range of values: start:increment:end.
ArgDataType (enum)Simulation.hEnumerates 11 supported types: TYPE_NONE, TYPE_CHAR through TYPE_DOUBLE.
DumpArg (struct)Simulation.hStores address, size, type, name, and hex flag for an argument marked with dump.
main()oclgrind-kernel.cppEntry point: calls parseArguments(), creates Simulation, calls load() then run().
parseArguments()oclgrind-kernel.cppIterates argv, matches 20+ flags via strcmp, calls setEnvironment() for each.
setEnvironment()oclgrind-kernel.cppCross-platform wrapper: calls setenv() on POSIX or _putenv_s() on Windows.
printUsage()oclgrind-kernel.cppPrints the full help text with all flags and descriptions to stdout.
readValue<T>()Simulation.cppFree-standing template utility that reads a typed value from an istream, with special 1-byte char handling.

Interactions with Other Modules

DependencyModuleDirectionHow & Why
Contextcorekernel → coreSimulation creates and owns a Context instance, which provides global memory and plugin lifecycle management.
Programcorekernel → coreSimulation::load() either builds from source via Program::build() or loads precompiled bitcode via Program::createFromBitcodeFile().
Kernelcorekernel → coreObtained from Program::createKernel(). The Simulation calls getNumArguments(), getArgumentSize(), getArgumentTypeName(), and setArgument().
KernelInvocationcorekernel → coreSimulation::run() calls KernelInvocation::run() to dispatch the NDRange execution with context, kernel, sizes.
Memorycorekernel → coreparseArgument() calls getGlobalMemory() to allocate buffers and store initial data. dumpArgument() calls load() to read results back.
Plugins (all)pluginscore → pluginsIndirectly activated via environment variables. CLI flags like --data-races set OCLGRIND_DATA_RACES, which Context reads to load the corresponding plugin.
Runtime (ICD)runtimenoneThe kernel module bypasses the runtime entirely — it talks directly to core, providing a simpler alternative execution path.

CLI Flags & Configuration

Every CLI flag maps directly to an OCLGRIND_* environment variable via the setEnvironment() helper. The core engine reads these variables at Context construction time.

CLI FlagShortEnv VariableArgumentDescription
--build-options—OCLGRIND_BUILD_OPTIONSOPTIONSAdditional options passed to the OpenCL compiler (Clang)
--compute-units—OCLGRIND_COMPUTE_UNITSUNITSOverride the number of compute units reported by the device
--constant-mem-size—OCLGRIND_CONSTANT_MEM_SIZEBYTESSet the constant memory size of the simulated device
--data-races—OCLGRIND_DATA_RACES—Enable data-race detection plugin (RaceDetector)
--disable-pch—OCLGRIND_DISABLE_PCH—Disable precompiled headers (slower but avoids PCH issues)
--dump-spir—OCLGRIND_DUMP_SPIR—Dump compiled SPIR / LLVM IR to temporary files for inspection
--global-mem-ginternal flag—Output entire global memory contents at exit
--global-mem-size—OCLGRIND_GLOBAL_MEM_SIZEBYTESSet the global memory size of the simulated device
--help-h——Display usage information and exit
--inst-counts—OCLGRIND_INST_COUNTS—Enable instruction counting plugin (opcode histograms)
--interactive-iOCLGRIND_INTERACTIVE—Enable interactive debugger (GDB-like kernel debugging)
--local-mem-size—OCLGRIND_LOCAL_MEM_SIZEBYTESSet the local memory size per work-group
--log—OCLGRIND_LOGLOGFILERedirect log and error messages to a file
--max-errors—OCLGRIND_MAX_ERRORSNUMLimit the number of error/warning messages before suppression
--max-wgsize—OCLGRIND_MAX_WGSIZEWGSIZEOverride the maximum work-group size of the device
--num-threads—OCLGRIND_NUM_THREADSNUMSet the number of worker threads for parallel work-group execution
--pch-dir—OCLGRIND_PCH_DIRDIROverride directory containing precompiled OpenCL builtin headers
--plugins—OCLGRIND_PLUGINSPLUGINSLoad colon-separated list of additional plugin shared libraries
--quick-qOCLGRIND_QUICK—Only run first and last work-group (fast smoke test)
--uniform-writes—OCLGRIND_UNIFORM_WRITES—Don’t suppress uniform write-write data-races
--uninitialized—OCLGRIND_UNINITIALIZED—Report usage of uninitialized memory values
--version-v——Display version information and exit

Simulation File Format

The sim file is a plain-text format parsed by Simulation::load(). Lines starting with # are comments. The format is:

// Sim file structure (parsed by Simulation::load):
// Line 1: program file path  (e.g., "vecadd.cl" or "vecadd.bc")
// Line 2: kernel name         (e.g., "vecadd")
// Line 3: NDRange x y z       (e.g., "1024 1 1")
// Line 4: work-group size x y z (e.g., "64 1 1")
// Lines 5+: argument headers + data, one per kernel argument
//
// Argument header format:  <token token ...>
// Tokens: size=N, type (int/float/...), fill=V, range=S:I:E,
//         dump, hex, noinit, null, ro, wo

Example Sim File

// vecadd.sim — add two vectors of 8 floats
vecadd.cl          # OpenCL source file
vecadd             # Kernel name
8 1 1              # NDRange: 8 work-items
8 1 1              # Work-group size: single group of 8

# Argument 0: input buffer A (read-only, 8 floats filled with 1.0)
<float size=32 ro fill=1.0>

# Argument 1: input buffer B (read-only, range 0..7)
<float size=32 ro range=0:1:7>

# Argument 2: output buffer C (write-only, dump results)
<float size=32 wo noinit dump>

Code Examples

Simulation Construction & Lifecycle

// From oclgrind-kernel.cpp — the complete main() flow
int main(int argc, char* argv[])
{
  // Step 1: Parse CLI flags and set environment variables
  if (!parseArguments(argc, argv))
    return 1;

  // Step 2: Create simulation (allocates Context)
  Simulation simulation;

  // Step 3: Load sim file (compile program, create kernel, parse args)
  if (!simulation.load(simfile))
    return 1;

  // Step 4: Execute the kernel and dump results
  simulation.run(outputGlobalMemory);
}

Argument Parsing — Header Token Processing

// Simplified from Simulation::parseArgument()
// Each argument has a header like: <float size=32 ro fill=1.0 dump>
void Simulation::parseArgument(size_t index)
{
  size_t size = SIZE_MAX;
  ArgDataType type = TYPE_NONE;
  bool dump = false, hex = false, noinit = false, null = false;
  string fill, range;

  // Read header tokens between < and >
  // Recognized tokens: char/uchar/.../double, size=N, fill=V,
  //   range=S:I:E, dump, hex, noinit, null, ro, wo

  // After parsing: allocate buffer, store initial data
  Memory* globalMemory = m_context->getGlobalMemory();
  size_t address = globalMemory->allocateBuffer(size, flags);
  globalMemory->store(data, address, size);

  // Bind argument to kernel
  m_kernel->setArgument(index, value);
}

Directory Map

Source tree for src/kernel/
src/kernel/
├── Simulation.h           ← Class declaration: Simulation, ArgDataType enum, DumpArg struct
├── Simulation.cpp         ← Implementation: load(), run(), parseArgument(), get<T>(), dumpArgument<T>()
│                             parseFill<T>(), parseRange<T>(), parseArgumentData<T>(), readValue<T>()
│                             ~780 lines — the core simulation engine
└── oclgrind-kernel.cpp    ← CLI entry point: main(), parseArguments(), setEnvironment(), printUsage()
                              ~317 lines — flag parsing and orchestration

Deep Dives

🔍 Focus pages provide implementation-level detail on specific mechanisms within this module.

Simulation Lifecycle FOCUS

Step-by-step walkthrough of the load → parse → execute pipeline inside the Simulation class.

  • Sim file parsing with line-by-line error recovery
  • Bitcode vs. source detection (magic bytes 0x42 0x43)
  • Argument header tokenizer and type inference
  • Buffer allocation, fill/range initializers, dump logic

CLI Interface FOCUS

How oclgrind-kernel bridges the gap between shell invocations and the core simulation engine.

  • Complete flag → environment variable mapping
  • Cross-platform setEnvironment() (POSIX vs. Win32)
  • Error handling and usage output
  • Integration with plugin activation