Skip to content

Control flow

WAVE branches and loops use explicit begin and end markers. An active mask determines which lanes execute each instruction.

Comparisons write to p0 through p3:

icmp_lt p0, r0, r1 ; signed comparison
ucmp_le p1, r2, r3 ; unsigned comparison
fcmp_eq p2, r4, r5 ; floating-point comparison
SuffixMeaning
eqEqual
neNot equal
ltLess than
leLess than or equal
gtGreater than
geGreater than or equal

Signed integer comparisons support all six suffixes. Unsigned comparisons use ucmp_lt and ucmp_le; reverse the operands for greater-than comparisons. Floating-point comparisons support eq, ne, lt, le, and gt. They also support ord when neither operand is NaN and unord when at least one is NaN.

icmp_lt p0, r0, r1
if p0
iadd r2, r0, r1
else
isub r2, r0, r1
endif

The else block is optional. This example clamps negative floats to zero:

mov_imm r1, 0
fcmp_lt p0, r0, r1
if p0
mov_imm r0, 0
endif

A wave diverges when its lanes disagree on a branch condition. The execution model applies a mask to each branch:

  1. if records the entering mask and activates lanes whose predicate is true.
  2. else activates the entering lanes whose predicate was false.
  3. endif restores the entering mask, subject to lanes that exited or continued a surrounding loop.

Both paths contain work when lanes disagree. A uniform branch needs only the selected path. The backend determines how these masks map to hardware instructions.

Lane: 0 1 2 3
Predicate: T F T F
if: [0, -, 2, -]
else: [-, 1, -, 3]
endif: [0, 1, 2, 3]

Use loop and endloop with a predicated exit:

mov_imm r0, 0
mov_imm r1, 100
mov_imm r2, 0
mov_imm r3, 1
loop
icmp_ge p0, r0, r1
break p0
iadd r2, r2, r0
iadd r0, r0, r3
endloop

This loop sums the integers from 0 through 99.

break p0 exits the loop for lanes where p0 is true. continue p0 skips the rest of the current iteration for those lanes. Advance loop state before a continue that would skip the update.

mov_imm r0, 0
mov_imm r1, 100
mov_imm r2, 0
mov_imm r3, 1
mov_imm r4, 0
loop
icmp_ge p0, r0, r1
break p0
iadd r0, r0, r3
and r5, r0, r3
icmp_eq p1, r5, r4
continue p1
iadd r2, r2, r0
endloop

This loop adds the odd integers from 1 through 99.

Lanes that break stay inactive for subsequent iterations and rejoin after the loop. Lanes that continue rejoin at the next iteration. The loop finishes when all participating lanes have exited.

Branches and loops can nest. Each level needs its matching closing instruction:

icmp_lt p0, r0, r1
if p0
fcmp_lt p1, r2, r3
if p1
fadd r4, r2, r3
else
fsub r4, r2, r3
endif
endif

select chooses between register values without branching:

icmp_lt p0, r0, r1
select r2, p0, r3, r4 ; choose r3 when true, otherwise r4

Use it when both input values are available and computing them unconditionally is safe. Whether it is faster than a branch depends on the target and surrounding code.

  • Group work so neighboring lanes tend to follow the same branches.
  • Keep loop iteration counts similar across lanes when the algorithm permits it.
  • Use select for short assignments with safe unconditional inputs.
  • Keep barriers outside divergent branches unless every workgroup thread reaches the same barrier.
  • Measure changes on the target GPU. Source instruction counts do not determine execution time.
ConstructSyntaxLane behavior
Conditionalif p / else / endifSelect lanes by predicate
Looploop / break p / continue p / endloopRun until participating lanes exit
Selectselect r, p, a, bChoose a value without changing the active mask
NestingMatched blocksPreserve the surrounding control state

Next: Optimization.