Skip to content

aarch64: lower ishl + ushr/sshr pairs to a single ubfm/sbfm instruction - #14187

Open
Rafferty97 wants to merge 8 commits into
bytecodealliance:mainfrom
Rafferty97:aarch64-ubfm-sbfm
Open

aarch64: lower ishl + ushr/sshr pairs to a single ubfm/sbfm instruction#14187
Rafferty97 wants to merge 8 commits into
bytecodealliance:mainfrom
Rafferty97:aarch64-ubfm-sbfm

Conversation

@Rafferty97

@Rafferty97 Rafferty97 commented Aug 21, 2026

Copy link
Copy Markdown

Motivation

AArch64 can express a left-shift followed by a right-shift as a single bitfield-move instruction, either ubfm or sbfm, which are commonly aliased to sbfx/sbfiz/ubfx/ubfiz. The aarch64 backend currently emits two instructions, since no rule inspects a sshr/ushr's operand for a producing ishl. The appropriate encoder already exists in emit.rs as the function enc_bfm, but only serves MInst::Extend, which only covers fixed-width sign extension.

There has been previous discussion around adding support for this kind of lowering here: #1067

Changes

I've added MInst::BitfieldMove to express the bitfield move family of AArch64 instructions (bfm, ubfm, sbfm) more generally than the pre-existing MInst::Extend. I then added lowering rules to recognise a sequence of ishl + ushr or ishl + sshr operations that could be lowered to ubfm or sbfm respectively. This necessitated two helper functions (sbfm_immr and sbfm_imms) to calculate the appropriate values for the immr and imms immediates.

I have taken care to support both 32-bit and 64-bit instructions, and to mask off the shift amounts as required by CLIF's semantics. I've added tests to shift-rotate.clif that cover all these cases.

I've also lightly modified the enc_bfm function signature to take a BfmOp rather than raw bits, for better separation of concerns.

Future work

Now that MInst can represent the full suite of bitfield-move instructions precisely, there's an argument for removing the Extend variant and instead lowering zero- and sign-extension operations to BitfieldMove directly. To bound the scope of this PR, though, I've left it in place.

@Rafferty97
Rafferty97 requested a review from a team as a code owner August 21, 2026 09:13
@Rafferty97
Rafferty97 requested review from cfallin and removed request for a team August 21, 2026 09:13
@github-actions github-actions Bot added cranelift Issues related to the Cranelift code generator cranelift:area:aarch64 Issues related to AArch64 backend. isle Related to the ISLE domain-specific language labels Aug 21, 2026
@github-actions

Copy link
Copy Markdown

Subscribe to Label Action

cc @cfallin, @fitzgen

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

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

  • cfallin: isle
  • fitzgen: isle

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

Learn more.

@cfallin cfallin 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! This looks generally good. Just a few comments below.

Also: would you be interested in seeing if you can use the verification framework (cranelift/isle/veri) that is in-tree, but not yet in enforcing mode, to verify these new lowerings? (I will soon turn it on but this would be a good test-case for usability in the meantime.) It might just work if the instruction specs for ubfm/sbfm are already generated; I'm not sure. Let us know if any issues with this!

Comment thread cranelift/codegen/src/isa/aarch64/inst/mod.rs
let w = ty.lane_bits() as u8;
let a = (a as u8) & (w - 1);
let b = (b as u8) & (w - 1);
UImm6::maybe_from_u8(if a <= b { b - a } else { w - (a - b) }).unwrap()

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.

Comment here that the unwrap should always succeed because w is at most 64? Probably also debug_assert!(w <= 64) above.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Sure, good suggestions. I usually prefer expect over unwrap with a comment as it surfaces the reasoning in panic messages too. Will update.

(to_bits u8))

;; A bitfield move instruction, which encompasses
;; the BFM, UBFM and SBFM instructions.

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.

Add "Overwrites whole rd (the bits outside the specified bitfield are zeroed)." here for clarity, per above.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

As mentioned above, the situation's a bit more nuanced. How does this read?

;; A bitfield move instruction, which encompasses
;; the BFM, UBFM and SBFM instructions.
;;
;; The net effect of these instructions is to move a
;; contiguous subset of bits in `rn` into `rd`, possibly
;; at an offset, then:
;; * For BFM: the other bits in `rd` are preserved
;; * For UBFM/SBFM: the other bits are cleared/sign-extended

@Rafferty97
Rafferty97 requested a review from cfallin August 25, 2026 22:08
@Rafferty97

Copy link
Copy Markdown
Author

Thanks! This looks generally good. Just a few comments below.

Also: would you be interested in seeing if you can use the verification framework (cranelift/isle/veri) that is in-tree, but not yet in enforcing mode, to verify these new lowerings? (I will soon turn it on but this would be a good test-case for usability in the meantime.) It might just work if the instruction specs for ubfm/sbfm are already generated; I'm not sure. Let us know if any issues with this!

@cfallin Ah, I forgot to reply to this one. Sure, I'd be interested in adding some tests in the new verification framework. Is it alright if this comes via a seperate PR?

@cfallin

cfallin commented Aug 25, 2026

Copy link
Copy Markdown
Member

@cfallin Ah, I forgot to reply to this one. Sure, I'd be interested in adding some tests in the new verification framework. Is it alright if this comes via a seperate PR?

Yes, that's fine -- it was an opportunistic request, we don't have it on in fully gating mode yet, so it's not yet an expectation :-)

@cfallin cfallin 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!

@cfallin
cfallin added this pull request to the merge queue Aug 25, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Aug 25, 2026
@Rafferty97

Copy link
Copy Markdown
Author

@cfallin Thanks again for reviewing this so quickly. It looks like the verifier check has failed, due to these errors:

=== EXPANSION ERRORS (2) ===
ERROR #3702	sbfm	expanding constructor 'sbfm_immr': no spec for term sbfm_immr
ERROR #3703	ubfm	expanding constructor 'sbfm_immr': no spec for term sbfm_immr

These are the two helper functions that calculate immr and imms. I assume these errors mean I need to add a(spec ...) declaration for each one? I'll have a look to see if I can work out where to put these and what to put in them, but if you had any guidance that'd be appreciated :)

@cfallin

cfallin commented Aug 25, 2026

Copy link
Copy Markdown
Member

Ah, yes, we don't have full verification on but we do check that specs are present. All helpers that bottom out in Rust (and that are reachable) need specs -- hopefully you'll be able to follow the examples on the existing helpers, but feel free to ping if not and we can help.

@Rafferty97

Copy link
Copy Markdown
Author

Ah, yes, we don't have full verification on but we do check that specs are present. All helpers that bottom out in Rust (and that are reachable) need specs -- hopefully you'll be able to follow the examples on the existing helpers, but feel free to ping if not and we can help.

Thanks. I've added the spec clauses, and just assert that both inputs and the output are <64 - not sure if there are any stronger pre- or post-conditions applicable to these helpers.

I'm curious why this PR passed CI before approval, but failed in the merge queue?

@Rafferty97
Rafferty97 requested a review from cfallin August 26, 2026 07:07
@avanhatt

Copy link
Copy Markdown
Member

The spec stubs do still need to type-check under the current merge queue CI gate, so I'd suggest just making them vacuous "return true".

(spec (bfm_immr ty a b) (provide true))
(spec (bfm_imms ty a b) (provide true))

I checked locally that this should pass the current CI.

Sorry for the churn as we work through the kinks of the new verifier integration/instructions!

@avanhatt

avanhatt commented Aug 26, 2026

Copy link
Copy Markdown
Member

Also re: the merge queue, it runs a superset of tests; you can run all for a commit by including prtest:full in the commit string.

@Rafferty97

Copy link
Copy Markdown
Author

@avanhatt Thanks for the helpful answers - I'll update my PR now as per your suggestion

@Rafferty97

Copy link
Copy Markdown
Author

I was also wondering - would there be interest in a follow up PR that removes MInst::Extend since this is now representable as an MInst:BitfieldMove, which is more general?

@Rafferty97

Copy link
Copy Markdown
Author

Hi @cfallin, @avanhatt - I've hit another wall. I'm now seeing this output from the "ISLE verifier basic check":

=== EXPANSION ERRORS (2) ===
ERROR #3683	sbfm	expanding constructor 'MInst.BitfieldMove': no spec for term MInst.BitfieldMove
ERROR #3684	ubfm	expanding constructor 'MInst.BitfieldMove': no spec for term MInst.BitfieldMove

I've looked at MInst.Expand as a template, so I think I need to add a define_bitfield_move function in cranelift/isle/veri/isaspec/src/instructions.rs, but I'm not sure.

@Rafferty97

Copy link
Copy Markdown
Author

Hi @cfallin, @avanhatt - I've hit another wall. I'm now seeing this output from the "ISLE verifier basic check":

=== EXPANSION ERRORS (2) ===
ERROR #3683	sbfm	expanding constructor 'MInst.BitfieldMove': no spec for term MInst.BitfieldMove
ERROR #3684	ubfm	expanding constructor 'MInst.BitfieldMove': no spec for term MInst.BitfieldMove

I've looked at MInst.Expand as a template, so I think I need to add a define_bitfield_move function in cranelift/isle/veri/isaspec/src/instructions.rs, but I'm not sure.

I've attempted to do this - with a lot of help from Claude - and the solution it came up with seems to make the CI test pass now. However, I'm not confident that it's the best solution, or even needed at all right now, and the fact that it generates a 50k ISLE file seems a bit off.

The code is in this commit, if you wouldn't mind having a quick glance to see if it's going in the right direction: Rafferty97@dc043ad

@avanhatt

Copy link
Copy Markdown
Member

@Rafferty97 sorry, I should have caught this! For this to not block this PR, you can just mark this as a TODO via:

(attr MInst.BitfieldMove (tag TODO))

I'm happy to help with adding a real spec forMInst.BitfieldMove in a followup PR, the ISA spec generation logic can result in that large of an auto-generated spec, but I'll have more time to look it over in a later PR.

@avanhatt

Copy link
Copy Markdown
Member

I can also take over doing that PR in its entirely if you're not interested in it, since this may be more than you bargained for! Thanks again for your patience with this new process.

@Rafferty97

Copy link
Copy Markdown
Author

I can also take over doing that PR in its entirely if you're not interested in it, since this may be more than you bargained for! Thanks again for your patience with this new process.

I'd appreciate that, actually :) I'm not sure if the LLM-assisted code in the commit I mentioned is helpful at all, but the reason it generates so much ISLE is that it has to enumerate all 64x64 combination of immr and imms immediate values - I think.

I've just pushed a commit with the added (attr MInst.BitfieldMove (tag TODO)). Hopefully that gets this across the line :)

@Rafferty97

Copy link
Copy Markdown
Author

One other minor thing: While working through that latest CI issue, I tried running the CI command locally, i.e.:

cargo run -p cranelift-isle-veri --bin veri -- --config cranelift/isle/veri/configs/aarch64-fast.args --skip-solver

But in order to get it working, I had to make a small fix to cranelift/isle/veri/isaspec/Cargo.toml, adding "std" to the feature list:

cranelift-codegen = { workspace = true, features = ["all-arch", "std"] }

I'm not sure if this is just an issue with my setup, though, and I'd guess it is.

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

Labels

cranelift:area:aarch64 Issues related to AArch64 backend. 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.

3 participants