Tines, through and sweeps

A tine selects lanes, a through block computes under that mask, and the sweep chooses the value that leaves each lane.

tine #valid(values: f32s) means values >= <0.0>

rake safe_root(values: f32s) -> f32s:
  through #valid(values) into rooted:
    sqrt(values)

  sweep:
    | #valid(values) => rooted
    | #valid(values) gaps => <0.0>

run roots(x: []f32, out: mut []f32, <n: i32>):
  for <i: i32> from <0> up to <n> by <4>:
    out[<i>] <- safe_root(x[<i>])

slow main() -> i32:
  values: [8]f32 := [4.0, -1.0, 9.0, 16.0, -4.0, 25.0, 0.0, 1.0]
  rooted: [8]f32 := [0.0; 8]
  roots(values, rooted, <8>)
  total := 0.0
  for i from 0 up to 8:
    total <- total + rooted[i]
  return i32(total)

A rake's body has any setup bindings, any local tines, at least one through block, and its final sweep, in that order. Global tines are defined outside the rake. Rakes compile on SSE2, AVX2, AVX-512, NEON and WebAssembly. The example uses f32s, and the development compiler also accepts i32s and u32s results with the native integer operation subset. A scratch, rake or run can call one.

Tines

tine #valid(values: f32s) means values >= <0.0> defines a reusable predicate with an explicit input. Applying #valid(values) computes one Boolean per lane. A global tine captures no surrounding values and stores no runtime mask. Every parameter has a type, with angle brackets around a uniform parameter and its argument:

tine #at_least(x: f32s, <limit: f32>) means x >= <limit>
tine #valid(x: f32s) means #at_least(x, <0.0>)

rake clipped_root(values: f32s) -> f32s:
  through #valid(values) into rooted:
    sqrt(values)
  sweep:
    | #valid(values) => rooted
    | #valid(values) gaps => <0.0>

Global tines can refer to other global tines, including later ones in the file, but not recursively. Their arguments use pure arithmetic and fields, with no ordinary function calls.

A rake can also define a local tine such as tine #valid means values >= <0.0>. It refers directly to that rake's inputs or setup bindings and is applied as #valid, without arguments. Local tines can refer only to earlier local tines. A predicate combines comparisons and tines with not, and and or.

#valid(values) gaps selects the complement: every lane outside the tine. Parentheses invert a composed mask, as in (#positive or #negative) gaps. You might read the gaps of x >= <0.0> as x < <0.0>, but gaps inverts the Boolean mask instead of the comparison, so a NaN, which fails both comparisons, belongs to the gaps.

Through blocks

through #valid(values) into rooted: computes roots in the selected lanes and binds them to rooted. Without else, the other lanes have no defined value. The compiler permits a later read only under a mask that implies the original mask. Reading rooted under #valid(values) gaps, or moving its undefined lanes into a selected lane with a shuffle, is a compile error.

The header accepts a local tine, a global application, or a composed predicate in parentheses, such as through (#valid and #large gaps). A fallback such as else <0.0> supplies a value for every inactive lane, so a later calculation can read the binding as a complete rack under any mask.

The body is any let and fused bindings, then a final expression giving the result. It can't assign, loop, call a function other than a built-in operation, reduce across lanes, scan, or use %.

Bindings inside the body are local to it, while the identifier after into belongs to the enclosing rake.

An inactive lane's computation doesn't happen. It can't fail on an invalid operand, raise a floating-point exception, touch memory or have any other effect from its through body, and rakec --interpret skips it.

A vector backend computes every lane, so it replaces the operands of inactive lanes with benign values before each operation that could raise an exception, then selects the results:

OperationBenign operands in inactive lanes
sqrt1.0
+, -, min, max, comparisons0.0
*1.0
/0.0 divided by 1.0
fma0.0

The native IR records each replacement, and the IR verifier rejects an exception-capable operation under a mask whose operands weren't replaced. WebAssembly has no floating-point exception state, so on wasm-simd128 the compiler replaces no operands and the final selection discards each inactive lane's value. Memory accesses stay masked on every target.

Sweeps

tine #high(scores: f32s) means scores >= <90.0>
tine #pass(scores: f32s) means scores >= <50.0>

rake grade(scores: f32s) -> f32s:
  through #high(scores) into top:
    <3.0>
  through #pass(scores) into middle:
    <2.0>

  sweep:
    | #high(scores) => top
    | #pass(scores) => middle
    | (#high(scores) or #pass(scores)) gaps => <1.0>

run grades(x: []f32, out: mut []f32):
  out[<0>] <- grade(x[<0>])

slow main() -> i32:
  scores: [4]f32 := [95.0, 60.0, 10.0, 50.0]
  result: [4]f32 := [0.0; 4]
  grades(scores, result)
  return i32(result[0] * 1000.0 + result[1] * 100.0 + result[2] * 10.0 + result[3])

A sweep lists arms in priority order, and for each lane the first arm whose tine holds gives the result, so a score of 95, which is in both tines, gets 3. A final _ arm is optional when the preceding masks provably cover every lane. Otherwise the sweep needs _ or an explicit complementary mask using gaps. Unreachable arms are rejected.

The coverage proof follows the Boolean relationships between tines, so it can prove #a or #a gaps, but it doesn't relate separate numeric comparisons: x >= <0.0> and x < <0.0> leave NaNs uncovered. A later read is checked under its arm's effective mask, after earlier arms have taken priority.

Backends implement the sweep with vector selections. Checked source can't read their temporary values in inactive lanes or use them as a fallback.

Intermediate and final fallbacks

A through block's optional else fills the inactive lanes of its intermediate rack. A sweep's optional _ supplies a final result for every lane left unselected by earlier arms, so they have different scopes even when both are zero.

rake safe_root(values: f32s) -> f32s:
  tine #valid means values >= <0.0>
  through #valid else <0.0> into rooted:
    sqrt(values)
  sweep:
    | #valid => rooted
    | _      => <-1.0>

run roots(x: []f32, out: mut []f32):
  out[<0>] <- safe_root(x[<0>])

slow main() -> i32:
  values: [4]f32 := [16.0, -4.0, 9.0, 1.0]
  result: [4]f32 := [0.0; 4]
  roots(values, result)
  return i32(result[0] + result[1] + result[2] + result[3])
StageLane 0Lane 1Lane 2Lane 3
Input16−491
#validtruefalsetruetrue
rooted4031
Result4−131

Here the sweep reads rooted only where #valid holds, so the intermediate zero in lane 1 is unused and a fallback of <999.0> would give the same result. The optimiser collapses the nested selections on that same mask into one selection. If the sweep's _ arm read rooted instead, its fallback lanes would become part of the final result.

Tines, through blocks and sweeps are pure computations, evaluated where they appear, and the compiler optimises their data flow together. A sweep selects a value for each lane in place, with no scattering, compaction or rearrangement of lanes.