Skip to content

cranelift: mask shift amount in (x << k) >> k mid-end rules - #14526

Merged
alexcrichton merged 2 commits into
bytecodealliance:mainfrom
kevaundray:fix-shifts-ishl-shr-out-of-range
Oct 4, 2026
Merged

alexcrichton merged 2 commits into
bytecodealliance:mainfrom
kevaundray:fix-shifts-ishl-shr-out-of-range

Conversation

@kevaundray

Copy link
Copy Markdown
Contributor

Like the other PR, this PR description and commits was generated using AI. Feel free to take the test case and apply a different fix

Bug

In cranelift/codegen/src/opts/shifts.isle, the two rules after ;; (x << N) >> N == x as T_SMALL as T_LARGE compute the narrow type as ty_bits(ty) - N from the raw shift constant. CLIF shift amounts are taken modulo the bit width of the shifted type, so iconst.i64 -8 shifts an i8 by 0, but 8 - 0xffff_ffff_ffff_fff8 wraps to 16. shift_amt_to_type then returns i16, and the rule builds sextend.i8 (ireduce.i16 v0) with v0: i8, which is ill-typed. With opt_level=speed compilation aborts in the verifier, even though the original expression is just v0.

function %sshr_ishl_neg8(i8) -> i8 {
block0(v0: i8):
    v1 = iconst.i64 -8
    v2 = ishl v0, v1
    v3 = sshr v2, v1
    return v3
}

The bug affects every target because it's in the mid-end. Other affected amounts: -24 on i8 (picks i32) and -16 on i16 (picks i32). The ushr rule builds the same ill-typed uextend (ireduce ..) node. In the tests here, elaboration happens to pick v0 from the same e-class, so the bad node never reaches the verifier.

Before (commit 1, without the fix)

$ clif-util test cranelift/filetests/filetests/egraph/shifts.clif cranelift/filetests/filetests/runtests/shift-left-right-same-amount.clif
FAIL cranelift/filetests/filetests/egraph/shifts.clif: optimize

Caused by:
    function %i8_shl_sshr_neg8(i8) -> i8 fast {
    block0(v0: i8):
        v7 = ireduce.i16 v0
    ;   ^~~~~~~~~~~~~~~~~~~
    ; error: inst6 (v7 = ireduce.i16 v0): arg 0 (v0) with type i8 failed to satisfy type set ValueTypeSet { ... }

        v8 = sextend.i8 v7
    ;   ^~~~~~~~~~~~~~~~~~
    ; error: inst7 (v8 = sextend.i8 v7): arg 0 (v7) with type i16 failed to satisfy type set ValueTypeSet { ... }

        return v8
    }

    ; 2 verifier errors detected (see above). Compilation aborted.

FAIL cranelift/filetests/filetests/runtests/shift-left-right-same-amount.clif: Compilation error: Verifier errors

Caused by:
    0: Verifier errors
    1: - inst6 (v7 = ireduce.i16 v0): arg 0 (v0) with type i8 failed to satisfy type set ...
       - inst7 (v8 = sextend.i8 v7): arg 0 (v7) with type i16 failed to satisfy type set ...

2 tests
Error: 2 failures

When each sshr function is run on its own, they fail the same way: -24/i8 builds ireduce.i32 v0 / sextend.i8, and -16/i16 builds ireduce.i32 v0 / sextend.i16.

After (commit 2)

$ cargo run -p cranelift-tools -- test cranelift/filetests/filetests/egraph/ cranelift/filetests/filetests/runtests/shift-left-right-same-amount.clif
78 tests

$ clif-util test cranelift/filetests/filetests
1322 tests

$ cargo test -p cranelift-codegen
test result: ok. 201 passed; 0 failed; 0 ignored
test result: ok. 22 passed; 0 failed; 4 ignored

All of these pass on x86_64. The runtest runs natively on x86_64, on the pulley targets and in the interpreter. After the fix, the ushr cases optimize to return v0. The sshr cases leave the shifts by 0 in place: there is no sshr x, 0 simplification, which is a separate issue.

The mid-end rules rewriting `(x << k) >> k` into
`sextend`/`uextend` of an `ireduce` compute the narrow type from
`ty_bits(ty) - k` using the raw shift constant. Shift amounts are taken
modulo the bit width of the shifted type, so for example
`iconst.i64 -8` shifts an `i8` by 0, but `8 - 0xffff_ffff_ffff_fff8`
wraps to 16 and the rule builds `sextend.i8 (ireduce.i16 v0)` with
`v0: i8`, which is ill-typed. With `opt_level=speed` compilation then
aborts with a verifier error, even though the original expression is
just `v0`.

Add a runtest with such out-of-range amounts (plus in-range and
congruent-to-in-range amounts) and `test optimize` cases showing the
expected optimized output. The `sshr` cases currently fail with a
verifier error. The `ushr` cases happen to pass because elaboration
picks `v0` over the ill-typed `uextend` node in the same e-class.
The rules rewriting `(x << k) >> k` into `sextend`/`uextend` of an
`ireduce` used the raw shift constant to compute the narrow type as
`ty_bits(ty) - k`. Shift amounts are taken modulo the bit width of the
shifted type, so an out-of-range `k` could select a narrow type that is
wider than `ty` (e.g. `k = -8` on an `i8` yields `i16`), producing
ill-typed IR that fails the verifier.

Mask the shift amount with `ty_shift_mask` before using it, as the
neighbouring shift rules do. This also lets the rules fire directly on
amounts congruent to an in-range amount, such as `56` on an `i32`.
@github-actions github-actions Bot added cranelift Issues related to the Cranelift code generator isle Related to the ISLE domain-specific language labels Oct 4, 2026
@github-actions

github-actions Bot commented Oct 4, 2026

Copy link
Copy Markdown

Subscribe to Label Action

cc @avanhatt, @cfallin, @fitzgen, @mmcloughlin

Details This issue or pull request has been labeled: "cranelift", "isle"

Thus the following users have been cc'd because of the following labels:

  • avanhatt: isle
  • cfallin: isle
  • fitzgen: isle
  • mmcloughlin: isle

To subscribe or unsubscribe from this label, edit the .github/subscribe-to-label.json configuration file.

Learn more.

@kevaundray
kevaundray marked this pull request as ready for review October 4, 2026 21:59
@kevaundray
kevaundray requested a review from a team as a code owner October 4, 2026 21:59
@kevaundray
kevaundray requested review from alexcrichton and removed request for a team October 4, 2026 21:59

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

Thanks!

cc @avanhatt and @mmcloughlin for an ISLE opt bug which wasn't caught through verification

@alexcrichton
alexcrichton added this pull request to the merge queue Oct 4, 2026
Merged via the queue into bytecodealliance:main with commit a01376f Oct 4, 2026
80 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cranelift Issues related to the Cranelift code generator isle Related to the ISLE domain-specific language

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants