Module Context.Cc_backend

The cc backend's own module, re-exported because the library's interface module is this one and a test outside arrayjit can otherwise not name it. What needs naming is Cc_backend.compiler_command: the generated-kernel census (test/operations/cc_march_census.ml, gh-ocannl-650) compiles emitted sources under foreign -march flags, and it must do so with the SAME toolchain that builds them for real -- a census against a different compiler would describe guarded arms nothing here selects. The interface is cc_backend.mli's, so this exposes no more than that file already publishes.

include Ir.Backend_impl.Lowered_no_device_backend
include Ir.Backend_impl.Buffer
type buffer_ptr
val name : Base.string
val codegen_capabilities : Base.unit -> Ir.Backend_intf.codegen_capabilities
type procedure
val sexp_of_procedure : procedure -> Sexplib0.Sexp.t
val compile : name:Base.string -> Ir.Indexing.unit_bindings -> Ir.Low_level.optimized -> procedure
val compile_batch : names:Base.string Base.array -> Ir.Indexing.unit_bindings -> Ir.Low_level.optimized Base.array -> procedure Base.array

Compiles the given procedures -- the segment kernels of one fissioned routine -- as a single compilation unit: one generated source and one C-compiler invocation, with the resulting (dyn-loaded) library shared by every returned procedure.

resolve is the device's backend-private buffer_loc -> base lookup, supplied at the backend boundary so that the shared layer never handles a raw pointer: this function resolves both the context's ctx_buffers (eagerly, at link time) and the lazily-set merge_buffer (at execution time). runner_label is get_name device of the device holding the buffers. lowered_bindings, when given, supplies the static-index refs to bind (looked up by symbol) instead of freshly minted ones, and is returned as-is: procedures linked as a batch — in particular the segment kernels of one fissioned routine — must share their binding refs, so setting a static index through the routine's bindings reaches every kernel.

include Ir.Backend_impl.No_device_buffer_and_copying with type buffer_ptr := buffer_ptr
include Ir.Backend_impl.Buffer with type buffer_ptr := buffer_ptr
val sexp_of_buffer_ptr : buffer_ptr -> Sexplib0.Sexp.t
val get_used_memory : Base.unit -> Base.int

Returns (an upper bound of) the memory used for arrays, in bytes.

Raw slab primitives used by Make_slab to back the device-level Backend_intf.Slab_alloc. They allocate / free / zero contiguous backend buffers by byte size; pool-id bookkeeping lives in the shared slab wrapper.

val alloc_pool_raw : size_in_bytes:Base.int -> buffer_ptr
val free_pool_raw : (buffer_ptr -> Base.unit) Base.option
val memset_zero_raw : buffer_ptr -> offset:Base.int -> size_in_bytes:Base.int -> Base.unit
val offset_buffer : buffer_ptr -> bytes:Base.int -> buffer_ptr

Returns a handle for the slab pointer advanced by bytes. Used by Make_slab.resolve_pool to turn a { pool_id; offset } into the concrete pointer for a sub-region of a multi-tenant pool. For bytes = 0 this must return the base unchanged.

val buffer_to_buffer : dst:buffer_ptr -> src:buffer_ptr -> size_in_bytes:Base.int -> Base.unit
val host_to_buffer : Ir.Ndarray.t -> dst:buffer_ptr -> Base.unit
val buffer_to_host : Ir.Ndarray.t -> src:buffer_ptr -> Base.unit
val compiler_command : unit -> string

The C compiler command this backend builds kernels with: cc_backend_compiler_command when set, else the toolchain's own C compiler as reported by ocamlc -config. Exposed for the generated-kernel census (gh-ocannl-650), which must compile the emitted sources with the SAME toolchain that will build them for real -- a census run against a different compiler would describe guarded arms nothing here selects.

val kernel_compiler_flags : unit -> string

The flags a kernel compile passes after compiler_command: optimization level, arch and SIMD flags, fast math, contraction and the parallel-grid runtime, all resolved. Exposed for test/operations/bridge_arm_coverage (gh-ocannl-1145), which preprocesses retained kernel sources under exactly these flags to read which storage-bridge arm each one compiled into.

val contraction_off : unit -> bool

Whether the kernel compile pins contraction off (cc_backend_fp_contract=off, placed after any -ffast-math; Compiler_options.clang_contraction_off over the flags in compile order): the cc C_syntax_config.compiler_contraction_off (gh-ocannl-1457).

val compiler_executable_identity : string -> string

Resolves every executable token in a compiler command and fingerprints its path, size and modification time. Exposed for the generated-kernel census's persistent listing identity: an in-place toolchain replacement must not replay assembly from the previous executable.

val pool_parallel_grid : unit -> bool

Whether this backend renders the outermost Grid loops it proves safe as concurrent chunks of the native pool — the gate of C_syntax.collect_parallel_grid: a pool syntax was probed, more than one chunk is configured, and no kernel logging is on. Exposed so a test can say what a bound grid axis means on cc (gh-ocannl-959): a binding the legality check judges where this holds, a serial loop where it does not.

val vector_bytes_setting : unit -> int

The vector register width in bytes for the explicit SIMD renderings (config cc_vector_bytes; auto-probed when unset). Read by vector_file_setting, and alone wherever only the width matters.

val auto_vector_bytes : unit -> int

What vector_bytes_setting answers when cc_vector_bytes is unset: the widest vector the configured target holds (64 with AVX-512 F/BW/VL, 32 with AVX2, else 16). Exposed for test/operations/bridge_arm_coverage, which forces every width up to this one.

val vector_file_setting : unit -> Ir.Vector_file.t

The vector register file of the explicit SIMD renderings, width and count as one value (gh-ocannl-1399): the vector_bytes_setting width, and how many registers of that width the kernels' target has (config cc_vector_registers; auto-probed when unset) -- the native file's count, 32 on AArch64 and AVX-512, 16 on other x86 targets, probed under the kernel flags -- divided where the width is wider than a native register; 0 registers when explicit SIMD is off. Exposed for Schedulers.cpu_mma_limits's simd_vector, which the register tile's Register_tile.rn_cap reads in the seeding, as the renderer does here (gh-ocannl-1285).

val effective_pool_width : unit -> int

The worker-pool width after the pool policy: the restricted class's logical CPU count when the restriction fired, the process's affinity-respecting CPU count otherwise. Sizes the auto cc_parallel_chunks.

val pool_tag : unit -> string

Compact pool signature (w8P, w24, ...) identifying the pool the process executes on. Exposed for Schedulers.cpu_mma_limits's worker_pool_tag: schedules crowned on one pool do not transfer to another (gh-ocannl-530), so the tag enters the autotune disk-cache key.

val timing_identity : unit -> Ir.Backend_intf.timing_identity option

CPU model and host partition, plus observed compiler target/executable metadata and macOS hardware UUID/OS build when available. OpenMP and non-macOS persistence remain supported. Executable provenance retains the probe cache's path/size/mtime contract; opaque wrappers and separately updated runtime libraries are not completely fingerprinted.

val codegen_tag : unit -> string

A short digest of this backend's resolved codegen configuration: the compiler command and its flags, the vector width, the fp16-arithmetic support, the parallel-grid syntax and chunking, and the per-chunk privatization cap. Exposed for Schedulers.cpu_mma_limits's Ir.Backend_intf.hardware_limits.codegen_tag: these settings are consulted at codegen, after the lowered code the canonical digest names, so they enter the autotune disk-cache key (gh-ocannl-572) and a knob flip re-tunes rather than replaying a winner from another codegen regime.

val host_capabilities : unit -> (string * string) list

Which kind of host this process runs on, as the kernels see it (gh-ocannl-1371): the CPU model, the logical CPU count and pool tag, the ISA the SIMD probes found under the kernel flags (avx2+fma, avx512f+bw+vl, neon), the resolved vector_bytes_setting beside auto_vector_bytes, the register count of vector_file_setting and the native register file, the fp16 arithmetic support, and the compiler with the arch and SIMD flags those were resolved under. Read from the same probes and settings the renderer reads, so it cannot disagree with them. tools/host_caps.exe prints it as the header line of every CI build job: GitHub's x86 runners come with or without AVX-512, and the auto-probed width changes what the autotune pre-filter keeps.

val has_native_fp16_arithmetic : unit -> bool

Whether the configured C compiler and target execute _Float16 arithmetic natively, at twice f32's lane count (ARMv8.2-FP16, AVX512-FP16) -- as opposed to lacking the type, or having it with every operation promoted to float (correct, but no throughput win). Probed once per process by test-compiling; overridable with cc_fp16_arithmetic. Exposed for Schedulers.cpu_mma_limits's native_fp16_arithmetic and for the compute-precision decision in CC_syntax_config.

Platform-specific kernel shared-library flags. In particular, ELF kernels bind their own builtin calls locally even when the host stubs use a different native-half ABI. Runtime compiler probes use this same policy rather than restating the linker flags.

val compile_failure_detail : exit_code:int -> source_note:string -> command:string -> compiler_output:string -> string

The detail of the Backend_rejected a failed C compile raises: the exit code, the Ir.C_syntax.codegen_bug_claim asking for source_note's file, the command and the compiler's output. Exposed so a test can hand Ir.C_syntax.attribute_routine_name_failure the report this backend actually raises: attributed to a routine's name, it is quoted without that claim; not attributed, it propagates with the claim intact.

type split_settings = {
  1. part_weight : int;
  2. jobs : int;
  3. unit_min_bytes : int;
}

gh-ocannl-1603: how a cc compile is split. part_weight: a kernel weighing more than sixteen parts of this weight is rendered outlined, into noinline parts it calls in order (0: never; config cc_kernel_part_weight). jobs: a batch is compiled as up to this many translation units, by concurrent compiler processes, linked into one library (cc_compile_jobs; always 1 off Unix). unit_min_bytes: the least source a unit holds (cc_compile_unit_min_bytes).

val split_settings : unit -> split_settings

The settings in force: the configuration, or with_split_settings's.

val with_split_settings : split_settings -> (unit -> 'a) -> 'a

Runs f with these settings instead of the configured ones, then restores the previous ones: for a test comparing a split compile with a whole one in one process. Not domain-local.

val translation_units_compiled : unit -> int

How many translation units this process has compiled as parts of a split batch (each unit of a batch split into several counts; an unsplit compile counts none).

type load_refusal =
  1. | Load_defect
    (*

    Anything else, an undefined symbol or a missing dependency in particular: the kernel's link line is OCANNL's bug (gh-ocannl-1077).

    *)
  2. | Code_signing_refusal
    (*

    The loader's code-signing or system policy refused the library (macOS dyld).

    *)
  3. | Library_missing
    (*

    The library the compiler built is not there to load.

    *)
val sexp_of_load_refusal : load_refusal -> Sexplib0.Sexp.t
val equal_load_refusal : load_refusal -> load_refusal -> bool
val classify_load_refusal : library_present:bool -> string -> load_refusal

classify_load_refusal ~library_present dlerror says why dlopen refused a library the compile produced (gh-ocannl-1717): the two environment classes are reported as Schedule_outcome.Environment rejections, Load_defect as a Compiler_bug. The signing class is read off the text; the missing class off library_present alone (whether the library's path exists after the refusal), since a missing dependency and a signing refusal's list of tried paths both read like a missing file. Exposed for test/operations/cc_load_refusal_classes.

val load_refusal_severity : load_refusal -> Ir.Schedule_outcome.severity

The severity of the dlopen-stage rejection each class raises: Compiler_bug for Load_defect, Environment for the other two.

val signing_retry_pause : float

Seconds load_retrying_signing_refusal's caller in the backend pauses before its one retry.

type 'lib load_outcome =
  1. | Loaded of 'lib
  2. | Loaded_on_retry of {
    1. lib : 'lib;
    2. first_dlerror : string;
    }
    (*

    The first load was a Code_signing_refusal; the retry loaded.

    *)
  3. | Refused of {
    1. refusal : load_refusal;
    2. dlerror : string;
    3. first_dlerror : string option;
    }
    (*

    refusal classifies the last attempt's dlerror; first_dlerror is set when that attempt was the retry.

    *)
val load_retrying_signing_refusal : load:(unit -> ('lib, string) Stdlib.Result.t) -> pause:(unit -> unit) -> library_present:(unit -> bool) -> 'lib load_outcome

load_retrying_signing_refusal ~load ~pause ~library_present runs load (a dlopen, its Error the dlerror()) and, when classify_load_refusal reads the refusal as a Code_signing_refusal, calls pause and load exactly once more (gh-ocannl-1728). Measured on arm64 macOS before adding it: dyld keeps no verdict on a refused path, so a reload after the cause clears succeeds, while a library the policy keeps refusing is refused again. The other classes are not retried. Exposed for test/operations/cc_load_refusal_classes.