Skip to content

[OptimizeInstructions] Generalize sign-ext folding - #9296

Open
tlively wants to merge 4 commits into
mainfrom
opt-inst-1-2-sign-ext-loads-eq
Open

tlively wants to merge 4 commits into
mainfrom
opt-inst-1-2-sign-ext-loads-eq

Conversation

@tlively

@tlively tlively commented Oct 23, 2026

Copy link
Copy Markdown
Member

Extend Properties::getSignExtValue, getSignExtBits, and getAlmostSignExt to 64-bit integers and unary extend{8,16,32}_s operations. Fold unary and shift-pair sign extensions of narrow loads into signed loads in visitUnary and visitBinary, and generalize Eq/Ne sign-extension comparisons to i64.

Extend Properties::getSignExtValue, getSignExtBits, and getAlmostSignExt to 64-bit integers and unary extend{8,16,32}_s operations. Fold unary and shift-pair sign extensions of narrow loads into signed loads in visitUnary and visitBinary, and generalize Eq/Ne sign-extension comparisons to i64.
@tlively
tlively requested a review from a team as a code owner October 23, 2026 01:51
@tlively
tlively requested review from stevenfontanella and removed request for a team October 23, 2026 01:51

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request generalizes sign-extension optimizations in Binaryen to support both i32 and i64 types. It updates helper functions in src/ir/properties.h to handle arbitrary integer types and extends various optimizations in src/passes/OptimizeInstructions.cpp (such as folding sign-extensions into narrow loads and simplifying comparisons with constants) to work with i64 operations. Feedback on the changes highlights a critical crash risk in OptimizeInstructions.cpp where getSignExtBits can be called on a non-sign-extend expression, as well as a robustness issue in properties.h where shift amounts are compared literally instead of using their effective modulo values.

Comment on lines +1080 to 1083
auto signExtBits = getSignExtBits(ext);
if (signExtBits > 0 && signExtBits <= bits) {
return replaceCurrent(ext);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

critical

The call to getSignExtBits(ext) is unsafe because ext (which is curr->value) is not guaranteed to be a sign-extend expression. For example, if curr is (i32.extend8_s (local.get $x)), ext will be (local.get $x). Since LocalGet is neither a Unary nor a Binary sign-extend, getSignExtBits will fall through to the else branch and attempt to perform curr->cast<Binary>() on the LocalGet, resulting in an assertion failure/crash.

To fix this, you should check if ext is actually a sign-extend expression before calling getSignExtBits(ext).

      if (Properties::getSignExtValue(ext)) {
        auto signExtBits = getSignExtBits(ext);
        if (signExtBits > 0 && signExtBits <= bits) {
          return replaceCurrent(ext);
        }
      }

Comment thread src/ir/properties.h
@tlively

tlively commented Oct 23, 2026

Copy link
Copy Markdown
Member Author

/review

Comment thread src/passes/OptimizeInstructions.cpp
Comment thread src/passes/OptimizeInstructions.cpp Outdated
if (LoadUtils::canBeSigned(load) && load->bytes * 8 == bits) {
// if the value falls through, we can't alter the load, as it might be
// captured in a tee
if (load->signed_ || load == ext) {

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.

What is handled here that is not handled in the above appearance of this code (new line 614)?

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.

Here we're optimizing the unary sign extension instructions. The previous location was optimizing shift-based sign extensions and almost-sign-extensions.

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.

I see. Can they both use a helper, then? it seems like both can use the same logic?

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.

Good idea. Done.

return replaceCurrent(curr->value);
if (auto* ext = Properties::getSignExtValue(curr)) {
auto bits = Properties::getSignExtBits(curr);
if (trySignExtLoad(ext, bits)) {

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.

Can we not use getAlmostSignExtBits like the other call to this helper? If not, why must we be more precise here?

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.

Two things:

  1. The other call is guarded by extraLeftShifts == 0, so it is also looking at an exact sign extension. The only reason there is a Properties::getAlmostSignExtBits call is because if there are extra left shifts, it falls through and tries other optimizations. The trySignExtLoad optimization only works for exact sign extensions because it is folding the sign extension into a sign-extending load.
  2. getAlmostSignExt and getAlmostSignExtendBits only work with shift-based sign extensions right now. We could generalize them, but there wouldn't be any benefit in this PR due to the previous point.

This branch has not been deployed

No deployments
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