Sarek_execute.Coverage_affineEpilogue coverage without enumeration, for affine store indices (specs/epilogue-fusion.md).
Answers the question Launch_epilogue asks of Index_slice (does P store each element of I at most once, at a non-negative index, and every element C writes?) in time independent of the launch size, when the relevant slice (Index_slice.relevant) of both kernels has this form:
lets and ifs, no loop, return, match or variable assignment in the slice, and no else branch that matters;if condition is a conjunction (&&, or c ? d : false) of <, <=, >, >=, == comparisons;Digits. The six thread and work-group indices start as one digit each, ranging over [0, extent) (a launch with an extent of 0 runs no thread and stores nothing). Dividing a value that is non-negative over the whole launch (guards are not taken into account, so a guarded (tid - 1) / 2 is Unknown) by 2^k, taking it modulo 2^k (or land (2^k - 1), or shifting it right by k) is exact when every coefficient but one digit's is a multiple of 2^k, that digit's coefficient is 1, the constant is a multiple of 2^k, and the digit's extent divides by 2^k or is at most 2^k: the digit d is then split into d = lo + 2^k * hi, lo in [0, 2^k), and the quotient and remainder are affine in the new digits. A warp-per-row index row = block * 8 + tid / 32 with the guard tid mod 32 == 0 has this form.
Solving. The digits with a non-zero coefficient in one guard must form a composite: sorted by magnitude, the coefficients are g times 1, e1, e1*e2, ... where e_i are the digits' extents. The composite L ranges bijectively over [0, product of extents) and the guard is an interval of L. Composites of different guards are equal or share no digit. Every other digit, and every composite whose interval is all of it, counts as free. P's store set is the image of this box. The index must be sum k_u * u + c over these units u. The image has each element exactly once and is one contiguous interval when the units with more than one value, sorted by k, form a mixed radix (first k is 1, each next is the previous times the previous unit's size). A k smaller than the span below it (zero included) repeats an element: P stores something twice, and the answer is false. C's store sets only need bounds.
Integer range. A digit of extent e >= 2 whose coefficient is a in a checked value satisfies |a| * (e - 1) <= 2^32 (every value is checked to stay in int32 over the launch), so |a| * e <= 2^33: products of a coefficient and a digit value, and composite weights, stay far below OCaml's 63-bit max_int. scale refuses a constant product that could overflow.
Any other form is Unknown: the caller enumerates instead.
type space = {dims : int array;block x y z, grid x y z
*)extent : (int, int) Stdlib.Hashtbl.t;of each digit; positions are 0..5
*)split : (int, int * int * int) Stdlib.Hashtbl.t;digit -> (lo, hi, m): digit = lo + m * hi
*)mutable next : int;next fresh digit
*)env : env;}and env = {vars : (int, aff) Stdlib.Hashtbl.t;let-bound relevant variables by id
*)scalars : (string * int32) list;mutable used : string list;scalar parameters read
*)}val const : int -> affval extent : space -> int -> intval fresh : space -> int -> intval digit : int -> affval eval : space -> Sarek_ir_types.expr -> affval guard : space -> Sarek_ir_types.expr -> aff listval sites :
space ->
is_target:(string -> bool) ->
Index_slice.Ids.t ->
aff list ->
Sarek_ir_types.stmt ->
site list ->
site listval space : dims:Index_slice.dims -> scalars:(string * int32) list -> spaceval store_sites :
is_target:(string -> bool) ->
scalars:(string * int32) list ->
dims:Index_slice.dims ->
Sarek_ir_types.stmt ->
space * site listval size_of : space -> (int * 'a) list -> intval live : space -> int listval covers :
p_body:Sarek_ir_types.stmt ->
p_array:string ->
p_scalars:(string * int32) list ->
p_dims:Index_slice.dims ->
c_body:Sarek_ir_types.stmt ->
c_written:string list ->
c_scalars:(string * int32) list ->
c_dims:Index_slice.dims ->
bool * (string * string) listcovers returns the verdict and the scalar parameters it read (("p", n) for P's, ("c", n) for C's), as Launch_epilogue's enumeration does. Raises Unknown when either kernel is outside the affine form.