Sarek_convergencemodule StringSet : sig ... endtype helper_def = {hd_self : string;the name it was defined under
*)hd_params : string list;hd_body : Sarek_typed_ast.texpr;hd_defs : (string * helper_def) list;The helpers in scope at its definition (itself excluded): a call resolves names lexically, as the generated code does, not in the caller's scope.
*)hd_captured : StringSet.t;Thread-varying variables a nested helper captures from its definition site; empty for a module-level function.
*)}A helper function in scope: what a call to it can be analysed against.
type variance = {strict : bool;defs : (string * helper_def) list;helpers in scope, innermost first
*)visiting : string list;helpers being unfolded: recursion guard
*)}How variance is computed. The legacy analysis is the first, cheap pass. The strict analysis is applied to every synchronisation point (warp collectives, whose missed divergence is a silent wrong answer, and barriers, whose missed divergence is a deadlock or undefined behaviour): it also follows the value of an if/match, a helper result that reads the thread, and a mutable assigned under thread-varying control flow (specs/convergence-semantics.md section 3).
val legacy_variance : variancetype assigned_entry = {ae_body : Sarek_typed_ast.texpr;ae_env : StringSet.t;ae_vi : variance;ae_result : bool;}Memo of assigned_varying, per mutable name: the scan of an inner let mut body is repeated by every enclosing let mut, which is exponential in their nesting. An entry is valid for one body (physical equality), one variance environment and one set of visible helpers. Cleared at each kernel check.
val assigned_memo : (string, assigned_entry list) Stdlib.Hashtbl.tval find_assigned :
variance ->
StringSet.t ->
string ->
Sarek_typed_ast.texpr ->
assigned_entry optiontype ctx = {mode : exec_mode;varying_vars : StringSet.t;Variables known to carry thread-varying values at this point
*)sync_helpers : (string * sync_kind) list;Helper functions in scope that contain a synchronisation point, transitively (specs/convergence-semantics.md, rule H-2): a call to one in diverged flow is an error at the call site.
*)variance : variance;call_stack : string list;Helpers whose body is being re-checked at a call (strict analysis)
*)}Analysis context
val init_ctx : ctxInitial context - start converged
val is_thread_varying_intrinsic : Sarek_env.intrinsic_ref -> boolCheck if an intrinsic ref is thread-varying using core primitives
val is_barrier_ref : Sarek_env.intrinsic_ref -> boolCheck if an intrinsic ref is a barrier/convergence point
val has_reachable_return : Sarek_typed_ast.texpr -> boolTrue if texpr e contains a TEReturn reachable on some execution path
val sub_exprs : Sarek_typed_ast.texpr -> Sarek_typed_ast.texpr listThe direct sub-expressions of te, in evaluation order.
val helper_name : Sarek_typed_ast.texpr -> string optionval body_defs : helper_def -> (string * helper_def) listThe helpers in scope inside the body of def: those of its definition site plus itself.
val helper_call_env : helper_def -> bool list -> StringSet.tThe environment a helper body is evaluated in at a call: what it captured, minus its own parameters, plus the parameters whose argument is varying.
val varying : variance -> StringSet.t -> Sarek_typed_ast.texpr -> boolCheck if an expression's value varies per-thread.
Thread-varying (different per thread):
Uniform (same for all threads in workgroup):
With vi.strict the value of control flow, helper results and mutables assigned in divergence are followed too (see variance).
val alias_def : variance -> string -> Sarek_typed_ast.texpr -> variancelet g = f: g is another name for the helper f (strict analysis).
val add_def :
variance ->
StringSet.t ->
string ->
Sarek_typed_ast.tparam list ->
Sarek_typed_ast.texpr ->
varianceRegister a nested helper.
val helper_result_varying :
variance ->
StringSet.t ->
Sarek_typed_ast.texpr ->
Sarek_typed_ast.texpr list ->
boolStrict analysis: does a call to a known helper return a thread-dependent value, given which arguments vary? The body is unfolded once per call (a recursion guard stops cycles).
val assigned_varying :
variance ->
StringSet.t ->
string ->
Sarek_typed_ast.texpr ->
boolStrict analysis: is the mutable name assigned, anywhere in body, a thread-varying value or under thread-varying control flow? Then its value after the assignment differs per thread.
val assigned_varying_scan :
variance ->
StringSet.t ->
string ->
Sarek_typed_ast.texpr ->
boolval has_varying_return_with :
variance ->
StringSet.t ->
Sarek_typed_ast.texpr ->
boolTrue if e contains a return reachable under a thread-varying condition (some threads exit early).
val is_thread_varying_env : StringSet.t -> Sarek_typed_ast.texpr -> boolval has_varying_return : StringSet.t -> Sarek_typed_ast.texpr -> boolTrue if e contains a TEReturn that is reachable under a thread-varying condition. Such a return causes SOME threads (those that take the varying branch) to exit early, while other threads continue, creating an implicit divergence point at every subsequent barrier in the enclosing sequence.
val is_thread_varying : Sarek_typed_ast.texpr -> boolBackward-compatible wrapper using an empty environment
val is_barrier_intrinsic : Sarek_env.intrinsic_ref -> boolCheck if an intrinsic reference is a barrier
val is_warp_convergence_ref : Sarek_env.intrinsic_ref -> boolCheck if an intrinsic ref requires warp convergence
val first_sync :
helpers:(string * sync_kind) list ->
Sarek_typed_ast.texpr ->
sync_kind optionThe first synchronisation point te contains: a barrier, a superstep, a warp collective, or a call to a helper of helpers (which contains one).
val sync_error : sync_kind -> Sarek_ast.loc -> Sarek_error.errorThe error a call reaching kind gives in diverged flow.
val is_sync_error : Sarek_error.error -> boolval check_expr : ctx -> Sarek_typed_ast.texpr -> Sarek_error.error listCollect errors from convergence analysis
val check_helper_call :
ctx ->
Sarek_typed_ast.texpr ->
Sarek_typed_ast.texpr list ->
Sarek_error.error listStrict analysis: a call to a helper whose body is checked with the parameters that the call makes thread-varying. The definition itself is checked with uniform parameters, so a shuffle under if param > k is only seen here. Only synchronisation errors are reported; a helper already being re-checked is not entered again.
val check_module_item :
ctx ->
Sarek_typed_ast.tmodule_item ->
Sarek_error.error listCheck a module item
val check_kernel_with :
ctx ->
Sarek_typed_ast.tkernel ->
Sarek_error.error listRun check over the module items and the body of kernel with ctx.
val add_strict_sync_errors :
legacy:Sarek_error.error list ->
strict:Sarek_error.error list ->
Sarek_error.error listThe synchronisation errors only the strict analysis sees in legacy's blind spots: strict errors that legacy did not already report.
val has_sync_point : Sarek_typed_ast.texpr -> boolDoes te contain a synchronisation point (barrier, superstep or warp collective)? The strict analysis is skipped for a kernel without one (it only reports synchronisation errors).
val check_toplevel_expr : Sarek_typed_ast.texpr -> Sarek_error.error listCheck an expression as a whole kernel body: the legacy analysis, then the strict analysis for synchronisation points.
val check_kernel :
Sarek_typed_ast.tkernel ->
(unit, Sarek_error.error list) Stdlib.resultCheck a kernel for convergence safety
val expr_uses_barriers : Sarek_typed_ast.texpr -> boolCheck if a kernel uses any barriers (explicit or implicit). Used for compile-time optimization of native kernel execution.
val kernel_uses_barriers : Sarek_typed_ast.tkernel -> boolCheck if a kernel uses barriers
Detect which thread/block dimensions a kernel uses to enable optimized native CPU execution. Simple kernels that only use global_idx_x can be run with a simple parallel for loop without the thread_state overhead.
type dim_usage = {uses_x : bool;uses_y : bool;uses_z : bool;uses_block_dim : bool;Uses block_dim_x/y/z
*)uses_grid_dim : bool;Uses grid_dim_x/y/z
*)uses_thread_idx : bool;Uses thread_idx_x/y/z directly
*)uses_block_idx : bool;Uses block_idx_x/y/z directly
*)}Dimension usage record
val empty_dim_usage : dim_usageval dim_usage_of_name : string -> dim_usageCheck if an intrinsic name affects dimension usage
val dim_usage_of_intrinsic_ref : Sarek_env.intrinsic_ref -> dim_usageGet dimension usage from an intrinsic reference
val expr_dim_usage : Sarek_typed_ast.texpr -> dim_usageAnalyze expression for dimension usage
val kernel_exec_strategy : Sarek_typed_ast.tkernel -> exec_strategyDetermine the optimal execution strategy for a kernel