Module Low_level.Access_fold

Ordered read-only Low_level traversal. Every policy decision is mandatory. Statements are visited before children and after their reads; Seq is left-to-right. Tile_mma descends through its equivalent fallback. A Prune skips children AND the after hook. Hooks may prune Tile_mma or Scan_loop when the consumer treats them as opaque.

Visit discarded operands is structural; Skip follows rendering. Gated operands are visited in source order (both Where arms) by default. Skip gated operands skips the entire subtree, including hoisted definitions: it is NOT an execution floor. The arms of a Ops.ternop_conditionality.Cond_and_one_arm ternary -- exactly one of which evaluates per visit -- reach the branches hook as one continuation each (in source order, under gated), with the ternary itself as select; the default in_order folds them in sequence, and a consumer with a branch algebra (a per-arm maximum, gh-ocannl-1451; the union of a select the backend blends, gh-ocannl-1775) supplies its own reduction there. Scope bodies reset gated because definitions hoist, while preserving statement guards. Dead-loop bodies visited structurally carry live=false; this does not evaluate constant guards or perform cleanup's placement-dependent setter elimination.

Scan init assignments precede the body; rotation assignments follow it. With scan_implicit = Skip, init expressions are still visited but synthetic Set_local events are absent. Scans must satisfy validate_scan_loops (in particular nonempty ranges). The fold visits a loop body once, not once per iteration; rotation events describe value dependencies rather than simulating simultaneous assignment. implicit identifies synthetic statements and their descendants; scope_depth counts Local_scope bodies; loops lists the enclosing For_loop / Scan_loop binders, innermost first (a scan's init expressions sit outside its binder, its body and rotations inside).

type visit =
  1. | Visit
  2. | Skip
type guards =
  1. | Track
  2. | Ignore
type implicit =
  1. | Explicit
  2. | Scan_init
  3. | Scan_rotate
type binder = {
  1. index : Indexing.symbol;
  2. from_ : Base.int;
  3. to_ : Base.int;
  4. axis : axis_type Base.option;
    (*

    None: a Scan_loop's index.

    *)
}
type policy = {
  1. discarded_operands : visit;
  2. gated_operands : visit;
  3. dead_loops : visit;
  4. local_scopes : visit;
  5. guards : guards;
  6. scan_implicit : visit;
}
type context = {
  1. live : Base.bool;
  2. guards : scalar_arg Base.list;
  3. gated : Base.bool;
  4. scope_depth : Base.int;
  5. implicit : implicit;
  6. loops : binder Base.list;
}
type 'a descent =
  1. | Continue of 'a
  2. | Prune of 'a
type 'a hooks = {
  1. statement : context -> 'a -> t -> 'a descent;
  2. scalar : context -> 'a -> scalar_t -> 'a descent;
  3. after_statement : context -> 'a -> t -> 'a;
  4. after_scalar : context -> 'a -> scalar_t -> 'a;
  5. branches : context -> 'a -> select:scalar_t -> ('a -> 'a) Base.list -> 'a;
}
val in_order : context -> 'a -> select:scalar_t -> ('a -> 'a) Base.list -> 'a

The default branches: the arms folded in source order (union semantics).

val hooks : Base.unit -> 'a hooks
val fold : policy:policy -> hooks:'a hooks -> init:'a -> t -> 'a
val fold_scalar : policy:policy -> hooks:'a hooks -> init:'a -> scalar_t -> 'a

fold entered at an expression, in the top-level context.