WebAssembly offers a block
instruction for creating a block construct. After entering a block, you cannot see existing values of the stack. You may imagine that a new partition (or a new stack) is built on top of the stack. You'll push values and carry out instructions on the basis of the partition.
So, the following code would cause an error:
(module
(import "env" "log" (func $log (param i32)))
(func $main
i32.const 1
block
i32.const 2
i32.add
call $log
end
)
(start $main)
)
Because you cannot see those values pushed before entering the block, you only push one value to the new partition but i32.add
has to pop two values. Pushing two values after entering the block would solve the problem.
(module
(import "env" "log" (func $log (param i32)))
(func $main
i32.const 1
block
i32.const 2
i32.const 3
i32.add
call $log
end
call $log
)
(start $main)
)
The above block doesn't use result
to define the result type. You have to empty the new stack before leaving the block. If not, an error happens. If you define the result type of a block, a value can be left on the stack. The value can be popped by the instruction after leaving the block. For example:
(module
(import "env" "log" (func $log (param i32)))
(func $main
i32.const 1
block (result i32)
i32.const 2
i32.const 3
i32.add
end
i32.add
call $log ;; print 6
)
(start $main)
)
The br
instruction can branch to a given level or label in an enclosing construct. You provide br
a number. If the number is n
, the control flow would branch to the end
of the n
-th outer block. That is, branches may only reference levels or labels defined by a construct in which they are enclosed. For example:
(module
(import "env" "log" (func $log (param i32)))
(func $main
block
block
br 0
i32.const 3
call $log
end
i32.const 2
call $log
end
i32.const 1
call $log
)
(start $main)
)
The br 0
instruction branches the control flow to the end
of the 0th outer block. The code after the end
of the current block will be executed so the console will print 2 and 1. If you change br 0
to br 1
, it will branch the control flow to the end
of the first outer block so only 1 is printed.
Using numbers is not convenient. You can place a label when declaring a block so that br
can branch to a given label.
(module
(import "env" "log" (func $log (param i32)))
(func $main
block $B0
block $B1
br $B1
i32.const 3
call $log
end
i32.const 2
call $log
end
i32.const 1
call $log
)
(start $main)
)
Similarly, the code will print 2 and 1. If you change br $B1
to br $B0
, 1 is printed.
The br_if
instruction can conditionally branch to a given label in an enclosing construct. It pops one value from the stack, do nothing if the value is 0. If not, branch to a given level or label. Let's implement a unless
construct (the inverse of if
) by using br_if
.
(module
(import "env" "log" (func $log (param i32)))
(func $main
block $UNLESS_BLOCK
block $THEN
block $UNLESS
i32.const 0 ;; unless false
br_if $THEN
end
;; executed unless false
i32.const 10
call $log
br $UNLESS_BLOCK
end
;; executed unless true
i32.const 20
call $log
end
)
(start $main)
)
Because of i32.const 0
, br_if
doesn't branch and br $UNLESS_BLOCK
is executed. The control flow branch to the end of $UNLESS_BLOCK
so the code after the end of block $THEN
won't be executed. The console will print 10. If you change i32.const 0
to i32.const 1
, the console prints 20.
If you have a list of branching conditions, br_table
may be feasible. It pops a number from the stack, uses the number as an index and jumps to the label in an enclosing construct. For example:
(module
(import "env" "log" (func $log (param i32)))
(func $main
(local $n i32)
(set_local $n (i32.const 0))
block $B0
block $B1
block $B2
get_local $n
br_table $B2 $B1 $B0 ;; branch according to $n
end
i32.const 2
call $log
end
i32.const 1
call $log
end
i32.const 0
call $log
)
(start $main)
)
If $n
is 0, br_table
branches to $B2
and 1
would branch to $B1
, etc.
WebAssembly provides if..else..end
and loop..end
so using block
to implement conditionally branching is not necessary. In fact, if..else..end
, loop..end
and func
all build blocks. block
can be an auxiliary instruction when using if..else..end
and loop..end
when defining more diversified branches.