Consider the following code:
loop $FIRST
block $SECOND (result i32)
i32.constant 1
block $THIRD (result i32)
i32.constant 1
br $FIRST
end
end
br $FIRST
end
First, should it get an error if one of the constant instructions is removed? Second, when the branch inside the third block is taken to come to the beginning of the loop, what does the loop "see" on its stack and why?
I am new to Wasm and I have no idea how I can understand what can be seen at each statement.
This piece of code is already ill-typed as is, because the second block declares a single result of type i32 when in fact two are on the stack.
In Wasm, every block conceptually has a local stack and cannot directly see or access the stacks of surrounding blocks.
What is (or must be) on that stack at the beginning and end of a block is declared by its block signature.
In your example, all blocks have empty parameters, so all their bodies start with an empty local stack.
The 2nd and 3rd declare a single i32 result, so that must be left at the end of their body.
In addition, the results are what is pushed on the stack of the surrounding block's stack when an inner block is exited (by reaching the end or with a branch).
Similarly, when a block is entered that has parameters, those are first popped from the surrounding block's stack.
After a branch, you can assume anything about the stack, since that code is unreachable. (However, unreachability is only considered per block, i.e., the type system does not propagate that knowledge past an end
— that is because branches may still reach the code after.)
Hence, your third block is fine, since its end is never reached.
The second block is not fine. At the end, there are two i32's on the stack (one from the constant, one the result of the inner block), but it declares there to be only one.
The loop again is okay, since its end also is never reached.
You can make the program well-typed by removing the first i32.const. The second i32.const is never consumed, so is redundant and can be removed as well.