Skip to content

Fix unreachable BrOn branch analysis - #8970

Merged
kripken merged 2 commits into
WebAssembly:mainfrom
kripken:br_on_unreach
Aug 7, 2026
Merged

Fix unreachable BrOn branch analysis#8970
kripken merged 2 commits into
WebAssembly:mainfrom
kripken:br_on_unreach

Conversation

@kripken

@kripken kripken commented Aug 5, 2026

Copy link
Copy Markdown
Member

We skipped the branch if we were unreachable, but that is not correct, see
visitBreak above - we need to note all the branches, taken or not, or else
we can hit an assertion. (We rely on optimization passes to remove
unreachable branch instructions.)

@kripken
kripken requested a review from a team as a code owner August 5, 2026 16:32
@kripken
kripken requested review from tlively and removed request for a team August 5, 2026 16:32
Comment thread test/lit/passes/monomorphize-desc.wast Outdated
;; We can monomorphize the call to $target, pushing the ref.is_null into
;; $target_2. The parameter we send there is now nullref, so everything there
;; gets optimized away. In this function, after refinalizing we end up with
;; an unreachable br_on, and while doing so, we should not hit an internal

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.

The br_on is not actually unreachable, right? It just unconditionally traps because of the null descriptor.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Right, I meant to say it is not reached/taken 😄

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

No, wait, the comment is already right. It does become unreachable after refinalizing, see the output.

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.

How can that be right? This is the output:

 ;; CHECK-NEXT:    (block ;; (replaces unreachable BrOn we can't emit)
 ;; CHECK-NEXT:     (drop
 ;; CHECK-NEXT:      (ref.null none)
 ;; CHECK-NEXT:     )
 ;; CHECK-NEXT:     (drop
 ;; CHECK-NEXT:      (ref.null none)
 ;; CHECK-NEXT:     )
 ;; CHECK-NEXT:     (unreachable)
 ;; CHECK-NEXT:    )

Neither child of the BrOn is unreachable, so the BrOn should not be unreachable.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The BrOn is not actually unreachable, but it is unPrintable, here is the Printing logic:

binaryen/src/passes/Print.cpp

Lines 2918 to 2921 in a0463cb

if (Properties::hasUnwritableTypeImmediate(curr)) {
printUnreachableReplacement(curr);
return;
}

When we lack a defined type to write out the br_on* line, we emit this. So it happens for unwritable type immediates, not just for unreachable BrOns.

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.

Right. Let's update the comment to describe it as unconditionally trapping rather than unreachable.

@tlively tlively 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.

LGTM with wording tweak.

@kripken
kripken enabled auto-merge (squash) August 7, 2026 18:25
@kripken
kripken merged commit f1b0e46 into WebAssembly:main Aug 7, 2026
15 of 16 checks passed
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.

2 participants