Skip to content

Fix folded branch hints - #2578

Open
vouillon wants to merge 3 commits into
bytecodealliance:mainfrom
vouillon:fix-folded-branch-hints
Open

Fix folded branch hints#2578
vouillon wants to merge 3 commits into
bytecodealliance:mainfrom
vouillon:fix-folded-branch-hints

Conversation

@vouillon

Copy link
Copy Markdown
Contributor

This fixes the parsing and printing of branch hints, so that the hint is attached to the if and not to the local.get in the example below (from the test suite).

        (@metadata.code.branch_hint "\01")
        (if (result i32) (local.get 1)
          (then (call $dummy) (i32.const 9))
          (else (call $dummy) (i32.const 10))
        )

The form previously emitted (with the branch hint right before the then) is still accepted on parsing.

        (if (result i32) (local.get 1)
          (@metadata.code.branch_hint "\01")
          (then (call $dummy) (i32.const 9))
          (else (call $dummy) (i32.const 10))
        )

vouillon added 3 commits July 24, 2026 15:19
The branch hint annotation `(@metadata.code.branch_hint ...)` records the
instruction it applies to by index. This index was recorded eagerly as the
index of the next instruction to be pushed, which is correct for the flat
form (e.g. `... (@...) if`) but wrong for the folded form
(e.g. `(@...) (if (cond) ...)`): there the operands of the folded
instruction are pushed first and the head instruction (the `if` or
`br_if`) is pushed afterwards, so the hint ended up attached to the first
operand instead of the branch instruction.

Defer the index assignment instead: keep the parsed annotation pending and
attach it to the head instruction of the following (flat or folded) form
when that instruction is actually pushed. This also accepts the annotation
appearing inside the folded form, after the operands (e.g.
`(if (cond) (@...) (then ...))` or `(br_if ... (@...))`). Using both a
preceding and a trailing annotation on one folded instruction targets it
twice and is rejected as a duplicate, matching two adjacent annotations.
The folded printer emitted the `(@metadata.code.branch_hint ...)` annotation
inside the folded form: as a pseudo-operand between an `if`'s condition and
its `(then`, and after a `br_if`'s operand. The annotation applies to the
`if` / `br_if` instruction, so print it on its own line immediately before
that instruction instead, matching the flat printer and the natural input
form.
Cover the folded "inside" placement of branch hint annotations (between an
`if`'s condition and its `(then`, and after a `br_if`'s operand). This is
the form older versions of this tool emitted when printing folded, so the
parser must keep accepting it and attach the hint to the `if` / `br_if`.
Nothing else in the test suite exercises this now that the folded printer
emits the annotation before the instruction instead.

Also assert that a preceding and a trailing annotation on one folded
instruction are rejected as a duplicate.
@vouillon
vouillon requested a review from a team as a code owner July 24, 2026 13:34
@vouillon
vouillon requested review from fitzgen and removed request for a team July 24, 2026 13:34

@fitzgen fitzgen left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems reasonable to me but I think @alexcrichton should probably take a look, since I've really only done minor work on the wast crate

@keithw

keithw commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Thank you for fixing this! Now that branch-hints is merged and there's a normative spec, I would be in favor making the wast or wasmparser crate stricter to match the reference interpreter, and be able to catch printing errors automatically in the future. This PR is being generous in having wast continue to support the previously printed (incorrect) syntax, but I would tend to think that's not necessary. E.g., the reference interpreter rejects all three of these:

(func
 (@metadata.code.branch_hint "\00")
)
$ ./wasm -u -ca -cr /tmp/f1.wat 
/tmp/f1.wat:2.2-2.29: custom annotation syntax error: @metadata.code.branch_hint annotation: invalid placement
(func
 (@metadata.code.branch_hint "\00")
 nop
)
$ ./wasm -ca -cr /tmp/f2.wat
/tmp/f2.wat:2.2-2.35: custom validation error: @metadata.code.branch_hint annotation: invalid target
(func
 (if (i32.const 0) (@metadata.code.branch_hint "\00")
  (then))
)
$ ./wasm -ca -cr /tmp/f3.wat
/tmp/f3.wat:5.7-5.34: custom annotation syntax error: @metadata.code.branch_hint annotation: invalid placement

Currently (pre and post this PR) wasm-tools accepts these as well-formed and valid. I think it probably should reject them too... and this will make it easier to catch future issues when it does the roundtrip tests.

@alexcrichton

Copy link
Copy Markdown
Member

Thanks! I agree with @keithw that there's no need to keep parsing the old syntax and it's ok to just break things here. I think there's code in wast trying to parse the older syntax and that can all be deleted.

I'll note though that @keithw syntax like:

(func
 (@metadata.code.branch_hint "\00")
 nop
)

will be relatively difficult to reject I think as that requires running the validator to determine that it's not actually tied to an if or br_if.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants