Sarek_backend_error.Backend_errortype codegen_error = | Unknown_intrinsic of {}Intrinsic function not recognized by this backend
*)| Invalid_arg_count of {}Wrong number of arguments to intrinsic
*)| Unsupported_construct of {}IR construct not supported by this backend
*)| Type_error of {}Type mismatch in expression
*)| Invalid_memory_space of {}Invalid memory space qualifier for declaration
*)| Unsupported_type of {}Type not supported by backend (e.g., fp64 without cl_khr_fp64)
*)Error types for backend code generation (IR → source translation)
type runtime_error = | No_device_selected of {}Operation requires a device but none is set
*)| Device_not_found of {}Device ID out of range
*)| Compilation_failed of {}Kernel compilation failed
*)| Module_load_failed of {}Failed to load compiled module/program
*)| Kernel_launch_failed of {}Failed to launch kernel on device
*)| Memory_allocation_failed of {}Device memory allocation failed
*)| Memory_copy_failed of {}Memory transfer between host and device failed
*)| Context_error of {}GPU context creation/management failed
*)| Synchronization_failed of {}Device synchronization failed
*)Error types for backend runtime operations
type plugin_error = Error types for backend plugin operations
type t = | Codegen of {backend : string;error : codegen_error;}| Runtime of {backend : string;error : runtime_error;}| Plugin of {backend : string;error : plugin_error;}Union type for backend errors, parameterized by backend name
exception Backend_error of tException wrapper for backend errors
val codegen : backend:string -> codegen_error -> tCreate codegen error for a specific backend
val runtime : backend:string -> runtime_error -> tCreate runtime error for a specific backend
val plugin : backend:string -> plugin_error -> tCreate plugin error for a specific backend
val unknown_intrinsic : backend:string -> string -> tval invalid_arg_count : backend:string -> string -> int -> int -> tval unsupported_construct : backend:string -> string -> string -> tval type_error : backend:string -> string -> string -> string -> tval invalid_memory_space : backend:string -> string -> string -> tval unsupported_type : backend:string -> string -> tval no_device_selected : backend:string -> string -> tval device_not_found : backend:string -> int -> int -> tval compilation_failed : backend:string -> string -> string -> tval module_load_failed : backend:string -> int -> string -> tval kernel_launch_failed : backend:string -> string -> string -> tval memory_allocation_failed : backend:string -> int64 -> string -> tval memory_copy_failed : backend:string -> string -> int -> string -> tval context_error : backend:string -> string -> string -> tval synchronization_failed : backend:string -> string -> tval unsupported_source_lang : backend:string -> string -> tval library_not_found : backend:string -> string -> string list -> tval initialization_failed : backend:string -> string -> tval feature_not_supported : backend:string -> string -> tval to_string : t -> stringConvert error to human-readable string
val raise_error : t -> 'aRaise backend error as exception
Refuse a non-empty ~soa_params on a backend whose emitter has no Structure-of-Arrays lowering (backlog-214).
Framework_sig.generate_source offers ?soa_params to every backend, but only an emitter that actually lowers a named vector parameter to N per-leaf bindings plus one shared length can honour it. A backend without that lowering used to bind the argument away as ?soa_params:_ and return its ordinary packed-AoS source — ONE binding per vector — while the launch side expands an SoA-dispatched vector into N RSA_Buffers plus one RSA_Vector_Length. That mismatch is never a compile error, and how badly it fails is per backend rather than uniform, which is why the refusal is stated here in terms of the mismatch and not of its symptom:
THE GENERAL FORM, and the only part that holds for every kernel shape: every parameter declared AFTER the vector receives the wrong entry. Nothing catches it on three of the five: Execute.check_launch_args checks arity, but against the CALLER's vector list before expansion, so it cannot see this, and it is the shift Execute.expand_to_run_source_args warns about for a leaf-count disagreement. What that shift then MEANS is a property of the parameter list, not of SoA, and the three cases below are examples rather than the consequence:
So "not a crash" and "silently wrong data" are each too narrow, and so is any single mechanism.
cuLaunchKernel/hipModuleLaunchKernel take (Cuda_shared.bind_args, Hip_shared.bind_args) with nothing comparing it against the compiled kernel's signature, so a caller-supplied value can reach the device as an address and a trap IS among the outcomes;Metal_plugin_base by list position via atIndex:, its expected_count being Kernel_args.count and so caller-derived), but no caller-supplied ADDRESS ever crosses: buffers go through setBuffer as MTLBuffer objects and scalars through setBytes as a driver-allocated inline copy (Metal_api, the Buffer/Int32 arms), against parameters declared device T* x [[buffer(k)]], constant int &sarek_x_length [[buffer(k+1)]] (Sarek_ir_metal.gen_buffer_param). Under the shift a length index receives a buffer whose first 4 bytes are read as the length, and a pointer index receives inline setBytes data (a valid 4-byte allocation indexed out of bounds) or another buffer (valid address, wrong buffer). Wrong data, out-of-bounds reads within valid allocations, or an encode Metal's own API validation rejects — but nothing that traps in the CUDA sense, so this backend does NOT inherit that half of the claim;clSetKernelArg funnel (Opencl_api.Kernel.set_arg_mem), which raises on CL_INVALID_ARG_INDEX once the index runs past the compiled kernel's argument list;Vulkan_api_kernel.validate_buffer_indices, whose expected_count is read from the GLSL binding = N declarations), so there the mismatch surfaces late as a buffer-count rejection naming the two numbers — and saying nothing about SoA.As of backlog-214 no caller in this tree reaches any of that: Execute.soa_dispatch restricts SoA to the CUDA/PTX device and Soa_launch gates on PTX being in the backend's supported_source_langs. So this is a boundary, not a live bug fix. What it changes is where the guarantee lives: it was one caller-side predicate and nothing else, and a backend that cannot honour the request now says so instead of answering with the wrong ABI.
Scope of what this raises, stated narrowly on purpose: it says what THIS backend does with vector parameters. It deliberately does not name which other backend does support SoA — that set is expected to grow (backlog-215), and a message enumerating it would go stale in a file that is not edited when it does. It is also not re-exported through Make: Make closes over one backend string per error module, and Cuda_error's is "CUDA" for both the CUDA/PTX backend that implements SoA and the CUDA/C backend that refuses it, so a Make-based version could not tell the caller which one answered.
One case is deliberately refused although it would happen to work: a record with a SINGLE leaf. Soa.plan permits it, and at N = 1 the SoA argument list (one leaf buffer plus one length) has the same shape as the AoS one, so the AoS source would bind correctly. It is refused anyway, because that correctness is a coincidence of the leaf count rather than a property of the emitter: the same call means something different the moment the record gains a second field, and a carve-out that silently changes meaning under an unrelated edit is worse than a refusal.
[] returns (), so every in-tree caller — all of which pass an omitted or empty list — and the caller-side fast path are byte-for-byte unaffected. An out-of-tree caller already passing a non-empty list to one of these five now raises; that is the intended change.
val print_error : t -> unitPrint error to stderr
val to_result : (unit -> 'a) -> ('a, t) Stdlib.resultConvert error to Result type
val result_to_string : ('a, t) Stdlib.result -> ('a, string) Stdlib.resultMap Result error to string