Branches: block, br, br_if, br_table
Branching in WebAssembly is always relative to a labeled construct — never to a raw memory address. The br family of instructions targets the nesting level, not a line number. This makes the behaviour of br depend entirely on what kind of construct it targets.
block and br
Section titled “block and br”A block is a forward-only group of instructions. When br $label targets a block, execution jumps to the instruction immediately after the block’s closing end. Think of it as a labeled break.
(module (func (export "earlyExit") (result i32) (block $b (result i32) i32.const 42 br $b ;; exits the block; 42 is the block result i32.const 99) ;; never reached ))The (result i32) annotation on the block declares that the block leaves one i32 on the stack when it exits — either by falling through to end or by branching out with br.
br targets direction
Section titled “br targets direction”The same br instruction behaves differently depending on what it targets:
flowchart LR A["block $b\n ...body...\n br $b"] --> B["jumps to END\nof block (forward)"] C["loop $l\n ...body...\n br $l"] --> D["jumps to START\nof loop (backward)"]
br_if: conditional branch
Section titled “br_if: conditional branch”br_if $label pops an i32 condition from the stack. If it is non-zero the branch fires; otherwise execution continues with the next instruction. This is the idiomatic way to write a conditional early exit.
(module (func (export "relu") (param $x i32) (result i32) (block $done (result i32) i32.const 0 ;; block result if we exit early local.get $x i32.const 0 i32.lt_s ;; x < 0? br_if $done ;; if yes, exit block — 0 is already on stack drop ;; discard the 0; x >= 0 local.get $x))) ;; return xbr_table: jump table
Section titled “br_table: jump table”br_table takes a list of labels and a default. It pops an i32 index and jumps to the label at that index, or to the default if the index is out of range. It is the WAT equivalent of a switch statement.
(module (func (export "describe") (param $n i32) (result i32) (block $two (block $one (block $zero local.get $n br_table $zero $one $two $two) ;; index 0→$zero, 1→$one, ≥2→$two i32.const 0) ;; $zero case: return 0 i32.const 1) ;; $one case: return 1 i32.const 2)) ;; $two case: return 2