Blocks can consume values from the stack and push results back to it, which is cool, but I cannot find any detail on how this works.
The spec for loop
says (at 5) that the values are popped from the stack (before any code within the block gets executed), and the other block instructions (block
and if
) are described similarly. They all pop the params before executing the block:
loop blocktype instr* end
- Assert: due to validation, expandπΉ(blocktype) is defined.
- Let [π‘π1]β[π‘π2] be the function type expandπΉ(blocktype).
- Let πΏ be the label whose arity is π and whose continuation is the start of the loop.
- Assert: due to validation, there are at least π values on the top of the stack.
- Pop the values valπ from the stack.
- Enter the block valπ instrβ with label πΏ.
Where are block params popped to? Blocks do not have their own locals, so I have no idea how to actually use the params (presumably they are not just thrown away).
The spec does not say anything about loop
blocks popping params on each iteration, but I thought that was how they work.
I also struggled to establish whether loop
blocks push results to the stack on each iteration, or only once, when the loop exits. Presumably, it's once, but with my ignorance on the other points, I'm not confident about that either.
I found this code in the proposal that may make things clearer (to somebody):
(func $fac (param i64) (result i64)
(i64.const 1) (get_local 0)
(loop $l (param i64 i64) (result i64)
(pick 1) (pick 1) (i64.mul)
(pick 1) (i64.const 1) (i64.sub)
(pick 0) (i64.const 0) (i64.gt_u)
(br_if $l)
(pick 1) (return)
)
)