Examples of br_table
in WebAssembly text
2020-01-29
Somehow I found it surprisingly hard to find examples of how to use the br_table
instruction from WebAssembly text (*.wat
) files. Therefore I’ve written down a few examples here for reference:
export "nop")
(func (1) ;; value used to select a branch
(i32.const 0)) ;; table branch with only a default branch (br_table
This is the simplest valid usage of br_table
: It specifies a default branch target and nothing else. So whatever value is on the top of the stack; the br_table
uses the default branch target (0
in this case).
export "early_exit") (result i32)
(func (21) ;; push some constant value to the stack, to be
(i32.const ;; returned from the function
0) ;; value used to select a branch
(i32.const 0) ;; table branch with only a default branch
(br_table ;; default -> (br 0) ;; exits the function
;; code below here is never executed!
(drop) 42)
(i32.const )
The example above shows that br_table
does not create a branch target on it’s own. Branch target 0
means the next outermost structured block, which in this case is the entire function. Therefore the drop
and push of the 42 won’t be executed; thus this function returns 21.
This also shows that in order for a br_table
to be useful (in the sense that it may branch to different branch targets) it has to be wrapped in at least one block
(or loop
etc). There is no way to continue after a br_table
instruction.
A “switch” like construct will thus look like this:
export "switch_like") (param $p i32) (result i32)
(func (block
(block
(block
(block (get_local $p)
(
(br_table2 ;; p == 0 => (br 2)
1 ;; p == 1 => (br 1)
0 ;; p == 2 => (br 0)
3)) ;; else => (br 3)
;; Target for (br 0)
100)
(i32.const return))
(;; Target for (br 1)
101)
(i32.const return))
(;; Target for (br 2)
102)
(i32.const return))
(;; Target for (br 3)
103)
(i32.const return)) (
This creates 4 nested block
s to give the br_table
4 different branch targets to jump to. The last specified is the “default” branch which is taken when the value on the stack is not an index into the list of the branches. Since jumping to a block
means jumping to its end the actual code for each of the branches follows the end of a more nested block
. This also implies that in order to “leave” this “switch like” construct another branch is necessary. In this case I took the easy route and just exited the function (branch to the outermost block) via (return)
. switch_like(0)
will branch to branch target 2
(after the third enclosing block of the br_table
, counted from inside to outside) and thus return 102.
A slightly more complex example, showing more jumping:
export "A") (mut i32) (i32.const 0))
(global $A (export "B") (mut i32) (i32.const 0))
(global $B (export "set") (param $select i32) (param $value i32)
(func (block
(block
(
(get_local $select)1 0 2)) ;; default (br 2) == (return)
(br_table ;; Branch target 0 of br_table, used for select == 1
(get_local $value);; set A = value
(set_global $A)
(get_local $value)42)
(i32.const eq)
(i32.0) ;; if value == 42, jump out of this block
(br_if return)) ;; else, return from function
(;; Branch target 1 of br_table, used for select == 0
;; Branch target 0 of br_if, used if value == 42
(get_local $value);; set B = value (set_global $B))
set(0, 10)
setsB = 10
and leavesA
as it isset(1, 20)
setsA = 20
and leavesB
as it isset(2, 30)
does nothingset(0, 42)
setsB = 42
and leavesA
as it isset(1, 42)
setsA = 42
and then alsoB = 42