Design: Clarification of stack popping for conditional branches

Created on 27 Mar 2017  ·  5Comments  ·  Source: WebAssembly/design

I am toying w/ writing a WebAssembly compiler. I am looking at the test https://github.com/WebAssembly/spec/blob/634f0d9009404f498ef8d8bd510bd6f0941219cc/test/core/br_if.wast#L272 which looks like this expanded a bit:

(module
    (func
      $type-arg-void-vs-num-nested
      (result i32)

      block i32
        (i32.const 0)
        block
          (i32.const 1)
          (br_if 1)
        end
      end
    )
)

The error I get is:

Error: check failed:
test.wast:10:12
type stack size too small at br_if value. got 0, expected at least 1
          (br_if 1)
           ^^^^^^^

I would think the stack size when it reaches br_if is 2 which means that it pops 1 for the conditional and then has one leftover to satisfy the outer block. I assumed (i32.const 0) would fall through to the nested block. Which part am I missing? Thanks in advance.

Most helpful comment

>

Does this mean each block gets its own stack?

I wouldn't say that they get their own stacks, just that there is a limit
to what can be popped. That's actually how it's implemented in wabt, there
is a value stack and a "label" stack. Each label stores a "limit" which is
the size of the value stack when the label is pushed. Then whenever you
pop, you check against the top label's limit.

Both are valid views. Semantically, the "block-local stack" view has the
advantage that it requires no extra invariants. With "single stack"
additional correctness assumptions are necessary, such as all limits
occurring in the label stack must be in bounds of the current operand stack
height, they must occur in (not necessarily strictly) increasing order, and
the label stack must never be empty.

All 5 comments

Yes, the error reporting of wabt can improve here. Even though there are two i32s on the stack, operators will never pop past the beginning of a block, so there is effectively only one.

Does this mean each block gets its own stack? I was confused by the following statement in the semantics doc:

Executing a block or loop instruction has no effect on the value stack.

Maybe that should change to:

Executing a block or loop instruction has no effect on the value stack, but all stack values outside of the executing block are inaccessible.

Or am I misunderstanding?

Does this mean each block gets its own stack?

I wouldn't say that they get their own stacks, just that there is a limit to what can be popped. That's actually how it's implemented in wabt, there is a value stack and a "label" stack. Each label stores a "limit" which is the size of the value stack when the label is pushed. Then whenever you pop, you check against the top label's limit.

Executing a block or loop instruction has no effect on the value stack.

Maybe that should change to:

Executing a block or loop instruction has no effect on the value stack, but all stack values outside of the block executing block are inaccessible.

Or am I misunderstanding?

Yes, it's probably worth mentioning something like that in Semantics.md. Though I should mention that there is work on a formal prose spec as well.

>

Does this mean each block gets its own stack?

I wouldn't say that they get their own stacks, just that there is a limit
to what can be popped. That's actually how it's implemented in wabt, there
is a value stack and a "label" stack. Each label stores a "limit" which is
the size of the value stack when the label is pushed. Then whenever you
pop, you check against the top label's limit.

Both are valid views. Semantically, the "block-local stack" view has the
advantage that it requires no extra invariants. With "single stack"
additional correctness assumptions are necessary, such as all limits
occurring in the label stack must be in bounds of the current operand stack
height, they must occur in (not necessarily strictly) increasing order, and
the label stack must never be empty.

Thanks for the info. I am closing this as there is nothing actionable to come from it and more formal specs are coming along to spell this out.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

bobOnGitHub picture bobOnGitHub  ·  6Comments

beriberikix picture beriberikix  ·  7Comments

jfbastien picture jfbastien  ·  6Comments

dpw picture dpw  ·  3Comments

frehberg picture frehberg  ·  6Comments