[llvm] [ARM] Selection should prefer immediates on the right hand side instead of shifted regs (PR #205126)

via llvm-commits llvm-commits at lists.llvm.org
Mon Jun 22 09:05:29 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-backend-arm

Author: AZero13 (AZero13)

<details>
<summary>Changes</summary>



---
Full diff: https://github.com/llvm/llvm-project/pull/205126.diff


2 Files Affected:

- (modified) llvm/lib/Target/ARM/ARMISelDAGToDAG.cpp (+83) 
- (modified) llvm/test/CodeGen/ARM/cmse-harden-entry-arguments.ll (+4-4) 


``````````diff
diff --git a/llvm/lib/Target/ARM/ARMISelDAGToDAG.cpp b/llvm/lib/Target/ARM/ARMISelDAGToDAG.cpp
index 61b679d55fb47..d31574f21b8b8 100644
--- a/llvm/lib/Target/ARM/ARMISelDAGToDAG.cpp
+++ b/llvm/lib/Target/ARM/ARMISelDAGToDAG.cpp
@@ -188,6 +188,10 @@ class ARMDAGToDAGISel : public SelectionDAGISel {
 private:
   void transferMemOperands(SDNode *Src, SDNode *Dst);
 
+  // Preference helper: for SUB with encodable immediate LHS, select RSBri
+  // and materialize any RHS shift first when needed. Returns true if handled.
+  bool tryPreferRSBForSUB(SDNode *N);
+
   /// Indexed (pre/post inc/dec) load matching code for ARM.
   bool tryARMIndexedLoad(SDNode *N);
   bool tryT1IndexedLoad(SDNode *N);
@@ -3588,6 +3592,81 @@ getContiguousRangeOfSetBits(const APInt &A) {
   return std::make_pair(FirstOne, LastOne);
 }
 
+bool ARMDAGToDAGISel::tryPreferRSBForSUB(SDNode *N) {
+  if (Subtarget->isThumb1Only())
+    return false;
+
+  SDValue LHS = N->getOperand(0);
+  SDValue RHS = N->getOperand(1);
+  const auto *CI = dyn_cast<ConstantSDNode>(LHS);
+  if (!CI)
+    return false;
+
+  unsigned Imm = (unsigned)CI->getZExtValue();
+  bool Encodable = Subtarget->isThumb() ? is_t2_so_imm(Imm) : is_so_imm(Imm);
+  if (!Encodable)
+    return false;
+
+  SDLoc dl(N);
+  SDValue Reg0 = CurDAG->getRegister(0, MVT::i32);
+  SDValue ImmOp = CurDAG->getTargetConstant(Imm, dl, MVT::i32);
+
+  // Materialize shift if RHS is shifted
+  ARM_AM::ShiftOpc ShOpcVal = ARM_AM::getShiftOpcForNode(RHS.getOpcode());
+  SDValue Rn = RHS;
+  if (ShOpcVal != ARM_AM::no_shift) {
+    const ConstantSDNode *ShC = dyn_cast<ConstantSDNode>(RHS.getOperand(1));
+    if (!ShC)
+      return false; // can't safely materialize variable shift here
+    unsigned ShAmt = ShC->getZExtValue();
+    if (Subtarget->isThumb()) {
+      unsigned ShOpc = 0;
+      switch (ShOpcVal) {
+      default:
+        ShOpc = 0;
+        break;
+      case ARM_AM::lsl:
+        ShOpc = ARM::t2LSLri;
+        break;
+      case ARM_AM::lsr:
+        ShOpc = ARM::t2LSRri;
+        break;
+      case ARM_AM::asr:
+        ShOpc = ARM::t2ASRri;
+        break;
+      case ARM_AM::ror:
+        ShOpc = ARM::t2RORri;
+        break;
+      }
+      if (!ShOpc)
+        return false;
+      SDValue ShAmtOp = CurDAG->getTargetConstant(ShAmt, dl, MVT::i32);
+      SDValue OpsShift[] = {RHS.getOperand(0), ShAmtOp, getAL(CurDAG, dl), Reg0,
+                            Reg0};
+      MachineSDNode *ShN =
+          CurDAG->getMachineNode(ShOpc, dl, MVT::i32, OpsShift);
+      Rn = SDValue(ShN, 0);
+    } else {
+      unsigned SOpc = ARM_AM::getSORegOpc(ShOpcVal, ShAmt);
+      SDValue ShImmOp = CurDAG->getTargetConstant(SOpc, dl, MVT::i32);
+      SDValue OpsShift[] = {RHS.getOperand(0), ShImmOp, getAL(CurDAG, dl), Reg0,
+                            Reg0};
+      MachineSDNode *ShN =
+          CurDAG->getMachineNode(ARM::MOVsi, dl, MVT::i32, OpsShift);
+      Rn = SDValue(ShN, 0);
+    }
+  }
+
+  if (Subtarget->isThumb()) {
+    SDValue Ops[] = {Rn, ImmOp, getAL(CurDAG, dl), Reg0, Reg0};
+    CurDAG->SelectNodeTo(N, ARM::t2RSBri, MVT::i32, Ops);
+  } else {
+    SDValue Ops[] = {Rn, ImmOp, getAL(CurDAG, dl), Reg0, Reg0};
+    CurDAG->SelectNodeTo(N, ARM::RSBri, MVT::i32, Ops);
+  }
+  return true;
+}
+
 void ARMDAGToDAGISel::SelectCMPZ(SDNode *N, bool &SwitchEQNEToPLMI) {
   assert(N->getOpcode() == ARMISD::CMPZ);
   SwitchEQNEToPLMI = false;
@@ -3739,6 +3818,10 @@ void ARMDAGToDAGISel::Select(SDNode *N) {
     if (tryInlineAsm(N))
       return;
     break;
+  case ISD::SUB:
+    if (tryPreferRSBForSUB(N))
+      return;
+    break;
   case ISD::Constant: {
     unsigned Val = N->getAsZExtVal();
     // If we can't materialize the constant we need to use a literal pool
diff --git a/llvm/test/CodeGen/ARM/cmse-harden-entry-arguments.ll b/llvm/test/CodeGen/ARM/cmse-harden-entry-arguments.ll
index c66ab00566ddf..35a46ffde206e 100644
--- a/llvm/test/CodeGen/ARM/cmse-harden-entry-arguments.ll
+++ b/llvm/test/CodeGen/ARM/cmse-harden-entry-arguments.ll
@@ -297,8 +297,8 @@ define i32 @access_i65(ptr byval(i65) %0) "cmse_nonsecure_entry" {
 ; V8M-LE-NEXT:        ldrb.w r0, [sp, #8]
 ; V8M-LE-NEXT:        and r0, r0, #1
 ; V8M-LE-NEXT:        rsbs r0, r0, #0
-; V8M-BE-NEXT:        movs r1, #0
-; V8M-BE-NEXT:        sub.w r0, r1, r0, lsr #24
+; V8M-BE-NEXT:        lsrs r0, r0, #24
+; V8M-BE-NEXT:        rsbs r0, r0, #0
 ; V8M-COMMON-NEXT:    add sp, #16
 ; V8M-COMMON-NEXT:    mov r1, lr
 ; V8M-COMMON-NEXT:    mov r2, lr
@@ -316,8 +316,8 @@ define i32 @access_i65(ptr byval(i65) %0) "cmse_nonsecure_entry" {
 ; V81M-LE-NEXT:        ldrb.w r0, [sp, #8]
 ; V81M-LE-NEXT:        and r0, r0, #1
 ; V81M-LE-NEXT:        rsbs r0, r0, #0
-; V81M-BE-NEXT:        movs r1, #0
-; V81M-BE-NEXT:        sub.w r0, r1, r0, lsr #24
+; V81M-BE-NEXT:        lsrs r0, r0, #24
+; V81M-BE-NEXT:        rsbs r0, r0, #0
 ; V81M-COMMON-NEXT:    sub sp, #4
 ; V81M-COMMON-NEXT:    add sp, #16
 ; V81M-COMMON-NEXT:    vscclrm {s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, vpr}

``````````

</details>


https://github.com/llvm/llvm-project/pull/205126


More information about the llvm-commits mailing list