[llvm] [AArch64][Peephole] Remove redundant mask for `ldrb + add 0xff` (PR #189518)

Henry Jiang via llvm-commits llvm-commits at lists.llvm.org
Mon Mar 30 18:52:03 PDT 2026


https://github.com/mustartt updated https://github.com/llvm/llvm-project/pull/189518

>From d9d34cccd3f6292d939032b46afd7c6ee93b6d0b Mon Sep 17 00:00:00 2001
From: Henry Jiang <henry_jiang2 at apple.com>
Date: Mon, 30 Mar 2026 14:24:25 -0700
Subject: [PATCH 1/2] Remove redundant AND mask after zero-extending loads

---
 .../Target/AArch64/AArch64MIPeepholeOpt.cpp   |  48 +++
 .../AArch64/peephole-redundant-and-mask.mir   | 282 ++++++++++++++++++
 2 files changed, 330 insertions(+)
 create mode 100644 llvm/test/CodeGen/AArch64/peephole-redundant-and-mask.mir

diff --git a/llvm/lib/Target/AArch64/AArch64MIPeepholeOpt.cpp b/llvm/lib/Target/AArch64/AArch64MIPeepholeOpt.cpp
index 900f22abcf8b0..01b42eaae3b08 100644
--- a/llvm/lib/Target/AArch64/AArch64MIPeepholeOpt.cpp
+++ b/llvm/lib/Target/AArch64/AArch64MIPeepholeOpt.cpp
@@ -122,6 +122,8 @@ class AArch64MIPeepholeOptImpl {
   bool checkMovImmInstr(MachineInstr &MI, MachineInstr *&MovMI,
                         MachineInstr *&SubregToRegMI);
 
+  bool removeRedundantAndMask(MachineInstr &MI);
+
   template <typename T>
   bool visitADDSUB(unsigned PosOpc, unsigned NegOpc, MachineInstr &MI);
   template <typename T>
@@ -652,6 +654,48 @@ bool AArch64MIPeepholeOptImpl::splitTwoPartImm(MachineInstr &MI,
   return true;
 }
 
+// Remove AND[W|X]ri when the mask is redundant because the source
+// operand's width already guarantees the upper bits are zero.
+//   %1:gpr32common = LDRBBui %0, 0
+//   %2:gpr32common = ANDWri %1, #0xff
+// All uses of %2 are replaced by %1, since the load is already zero extending.
+bool AArch64MIPeepholeOptImpl::removeRedundantAndMask(MachineInstr &MI) {
+  assert((MI.getOpcode() == AArch64::ANDWri ||
+         MI.getOpcode() == AArch64::ANDXri) &&
+             "Unsupported masking instructions");
+
+  unsigned RegSize = MI.getOpcode() == AArch64::ANDWri ? 32 : 64;
+  auto EncodedImm = MI.getOperand(2).getImm();
+  uint64_t Mask = AArch64_AM::decodeLogicalImmediate(EncodedImm, RegSize);
+
+  MachineInstr *SrcMI = MRI->getUniqueVRegDef(MI.getOperand(1).getReg());
+  if (!SrcMI || !SrcMI->hasOneMemOperand())
+    return false;
+
+  const MachineMemOperand *MMO = *SrcMI->memoperands_begin();
+  if (!MMO || !MMO->isLoad())
+    return false;
+
+  if (!AArch64InstrInfo::isZExtLoad(*SrcMI))
+    return false;
+
+  uint64_t LoadSize = MMO->getSizeInBits().getValue();
+  if (Mask != maskTrailingOnes<uint64_t>(LoadSize))
+    return false;
+
+  Register DstReg = MI.getOperand(0).getReg();
+  Register SrcReg = SrcMI->getOperand(0).getReg();
+  if (DstReg.isVirtual()) {
+    MRI->replaceRegWith(DstReg, SrcReg);
+  } else {
+    BuildMI(*MI.getParent(), MI, MI.getDebugLoc(), TII->get(TargetOpcode::COPY),
+            DstReg)
+        .addReg(SrcReg);
+  }
+  MI.eraseFromParent();
+  return true;
+}
+
 bool AArch64MIPeepholeOptImpl::visitINSviGPR(MachineInstr &MI, unsigned Opc) {
   // Check if this INSvi[X]gpr comes from COPY of a source FPR128
   //
@@ -970,6 +1014,10 @@ bool AArch64MIPeepholeOptImpl::run(MachineFunction &MF) {
         Changed |= trySplitLogicalImm<uint64_t>(
             AArch64::ANDXri, MI, SplitStrategy::Intersect, AArch64::ANDSXri);
         break;
+      case AArch64::ANDXri:
+      case AArch64::ANDWri:
+        Changed |= removeRedundantAndMask(MI);
+        break;
       case AArch64::EORWrr:
         Changed |= trySplitLogicalImm<uint32_t>(AArch64::EORWri, MI,
                                                 SplitStrategy::Disjoint);
diff --git a/llvm/test/CodeGen/AArch64/peephole-redundant-and-mask.mir b/llvm/test/CodeGen/AArch64/peephole-redundant-and-mask.mir
new file mode 100644
index 0000000000000..89b3299d440ba
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/peephole-redundant-and-mask.mir
@@ -0,0 +1,282 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
+# RUN: llc -run-pass=aarch64-mi-peephole-opt -o - -mtriple=aarch64-unknown-linux -verify-machineinstrs %s | FileCheck %s
+---
+name: zext_byte_load_ui
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $x0
+    ; CHECK-LABEL: name: zext_byte_load_ui
+    ; CHECK: liveins: $x0
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:gpr64common = COPY $x0
+    ; CHECK-NEXT: [[LDRBBui:%[0-9]+]]:gpr32common = LDRBBui [[COPY]], 0 :: (load (s8))
+    ; CHECK-NEXT: $w0 = COPY [[LDRBBui]]
+    ; CHECK-NEXT: RET_ReallyLR implicit $w0
+    %0:gpr64common = COPY $x0
+    %1:gpr32common = LDRBBui %0, 0 :: (load (s8))
+    %2:gpr32common = ANDWri %1, 7
+    $w0 = COPY %2
+    RET_ReallyLR implicit $w0
+...
+---
+name: zext_byte_load_ur
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $x0
+    ; CHECK-LABEL: name: zext_byte_load_ur
+    ; CHECK: liveins: $x0
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:gpr64common = COPY $x0
+    ; CHECK-NEXT: [[LDURBBi:%[0-9]+]]:gpr32common = LDURBBi [[COPY]], 1 :: (load (s8))
+    ; CHECK-NEXT: $w0 = COPY [[LDURBBi]]
+    ; CHECK-NEXT: RET_ReallyLR implicit $w0
+    %0:gpr64common = COPY $x0
+    %1:gpr32common = LDURBBi %0, 1 :: (load (s8))
+    %2:gpr32common = ANDWri %1, 7
+    $w0 = COPY %2
+    RET_ReallyLR implicit $w0
+...
+---
+name: zext_byte_load_roX
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $x0, $x1
+    ; CHECK-LABEL: name: zext_byte_load_roX
+    ; CHECK: liveins: $x0, $x1
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:gpr64common = COPY $x0
+    ; CHECK-NEXT: [[COPY1:%[0-9]+]]:gpr64 = COPY $x1
+    ; CHECK-NEXT: [[LDRBBroX:%[0-9]+]]:gpr32common = LDRBBroX [[COPY]], [[COPY1]], 0, 0 :: (load (s8))
+    ; CHECK-NEXT: $w0 = COPY [[LDRBBroX]]
+    ; CHECK-NEXT: RET_ReallyLR implicit $w0
+    %0:gpr64common = COPY $x0
+    %1:gpr64 = COPY $x1
+    %2:gpr32common = LDRBBroX %0, %1, 0, 0 :: (load (s8))
+    %3:gpr32common = ANDWri %2, 7
+    $w0 = COPY %3
+    RET_ReallyLR implicit $w0
+...
+---
+name: zext_byte_load_roW
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $x0, $w1
+    ; CHECK-LABEL: name: zext_byte_load_roW
+    ; CHECK: liveins: $x0, $w1
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:gpr64common = COPY $x0
+    ; CHECK-NEXT: [[COPY1:%[0-9]+]]:gpr32 = COPY $w1
+    ; CHECK-NEXT: [[LDRBBroW:%[0-9]+]]:gpr32common = LDRBBroW [[COPY]], [[COPY1]], 0, 0 :: (load (s8))
+    ; CHECK-NEXT: $w0 = COPY [[LDRBBroW]]
+    ; CHECK-NEXT: RET_ReallyLR implicit $w0
+    %0:gpr64common = COPY $x0
+    %1:gpr32 = COPY $w1
+    %2:gpr32common = LDRBBroW %0, %1, 0, 0 :: (load (s8))
+    %3:gpr32common = ANDWri %2, 7
+    $w0 = COPY %3
+    RET_ReallyLR implicit $w0
+...
+---
+name: zext_half_load_ui
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $x0
+    ; CHECK-LABEL: name: zext_half_load_ui
+    ; CHECK: liveins: $x0
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:gpr64common = COPY $x0
+    ; CHECK-NEXT: [[LDRHHui:%[0-9]+]]:gpr32common = LDRHHui [[COPY]], 0 :: (load (s16))
+    ; CHECK-NEXT: $w0 = COPY [[LDRHHui]]
+    ; CHECK-NEXT: RET_ReallyLR implicit $w0
+    %0:gpr64common = COPY $x0
+    %1:gpr32common = LDRHHui %0, 0 :: (load (s16))
+    %2:gpr32common = ANDWri %1, 15
+    $w0 = COPY %2
+    RET_ReallyLR implicit $w0
+...
+---
+name: zext_half_load_ur
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $x0
+    ; CHECK-LABEL: name: zext_half_load_ur
+    ; CHECK: liveins: $x0
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:gpr64common = COPY $x0
+    ; CHECK-NEXT: [[LDURHHi:%[0-9]+]]:gpr32common = LDURHHi [[COPY]], 2 :: (load (s16))
+    ; CHECK-NEXT: $w0 = COPY [[LDURHHi]]
+    ; CHECK-NEXT: RET_ReallyLR implicit $w0
+    %0:gpr64common = COPY $x0
+    %1:gpr32common = LDURHHi %0, 2 :: (load (s16))
+    %2:gpr32common = ANDWri %1, 15
+    $w0 = COPY %2
+    RET_ReallyLR implicit $w0
+...
+---
+name: zext_half_load_roX
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $x0, $x1
+    ; CHECK-LABEL: name: zext_half_load_roX
+    ; CHECK: liveins: $x0, $x1
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:gpr64common = COPY $x0
+    ; CHECK-NEXT: [[COPY1:%[0-9]+]]:gpr64 = COPY $x1
+    ; CHECK-NEXT: [[LDRHHroX:%[0-9]+]]:gpr32common = LDRHHroX [[COPY]], [[COPY1]], 0, 0 :: (load (s16))
+    ; CHECK-NEXT: $w0 = COPY [[LDRHHroX]]
+    ; CHECK-NEXT: RET_ReallyLR implicit $w0
+    %0:gpr64common = COPY $x0
+    %1:gpr64 = COPY $x1
+    %2:gpr32common = LDRHHroX %0, %1, 0, 0 :: (load (s16))
+    %3:gpr32common = ANDWri %2, 15
+    $w0 = COPY %3
+    RET_ReallyLR implicit $w0
+...
+---
+name: zext_half_load_roW
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $x0, $w1
+    ; CHECK-LABEL: name: zext_half_load_roW
+    ; CHECK: liveins: $x0, $w1
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:gpr64common = COPY $x0
+    ; CHECK-NEXT: [[COPY1:%[0-9]+]]:gpr32 = COPY $w1
+    ; CHECK-NEXT: [[LDRHHroW:%[0-9]+]]:gpr32common = LDRHHroW [[COPY]], [[COPY1]], 0, 0 :: (load (s16))
+    ; CHECK-NEXT: $w0 = COPY [[LDRHHroW]]
+    ; CHECK-NEXT: RET_ReallyLR implicit $w0
+    %0:gpr64common = COPY $x0
+    %1:gpr32 = COPY $w1
+    %2:gpr32common = LDRHHroW %0, %1, 0, 0 :: (load (s16))
+    %3:gpr32common = ANDWri %2, 15
+    $w0 = COPY %3
+    RET_ReallyLR implicit $w0
+...
+---
+name: sext_byte_load_not_removed
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $x0
+    ; CHECK-LABEL: name: sext_byte_load_not_removed
+    ; CHECK: liveins: $x0
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:gpr64common = COPY $x0
+    ; CHECK-NEXT: [[LDRSBWui:%[0-9]+]]:gpr32common = LDRSBWui [[COPY]], 0 :: (load (s8))
+    ; CHECK-NEXT: [[ANDWri:%[0-9]+]]:gpr32common = ANDWri [[LDRSBWui]], 7
+    ; CHECK-NEXT: $w0 = COPY [[ANDWri]]
+    ; CHECK-NEXT: RET_ReallyLR implicit $w0
+    %0:gpr64common = COPY $x0
+    %1:gpr32common = LDRSBWui %0, 0 :: (load (s8))
+    %2:gpr32common = ANDWri %1, 7
+    $w0 = COPY %2
+    RET_ReallyLR implicit $w0
+...
+---
+name: sext_half_load_not_removed
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $x0
+    ; CHECK-LABEL: name: sext_half_load_not_removed
+    ; CHECK: liveins: $x0
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:gpr64common = COPY $x0
+    ; CHECK-NEXT: [[LDRSHWui:%[0-9]+]]:gpr32common = LDRSHWui [[COPY]], 0 :: (load (s16))
+    ; CHECK-NEXT: [[ANDWri:%[0-9]+]]:gpr32common = ANDWri [[LDRSHWui]], 15
+    ; CHECK-NEXT: $w0 = COPY [[ANDWri]]
+    ; CHECK-NEXT: RET_ReallyLR implicit $w0
+    %0:gpr64common = COPY $x0
+    %1:gpr32common = LDRSHWui %0, 0 :: (load (s16))
+    %2:gpr32common = ANDWri %1, 15
+    $w0 = COPY %2
+    RET_ReallyLR implicit $w0
+...
+---
+name: wrong_mask_not_removed
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $x0
+    ; CHECK-LABEL: name: wrong_mask_not_removed
+    ; CHECK: liveins: $x0
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:gpr64common = COPY $x0
+    ; CHECK-NEXT: [[LDRBBui:%[0-9]+]]:gpr32common = LDRBBui [[COPY]], 0 :: (load (s8))
+    ; CHECK-NEXT: [[ANDWri:%[0-9]+]]:gpr32common = ANDWri [[LDRBBui]], 15
+    ; CHECK-NEXT: $w0 = COPY [[ANDWri]]
+    ; CHECK-NEXT: RET_ReallyLR implicit $w0
+    %0:gpr64common = COPY $x0
+    %1:gpr32common = LDRBBui %0, 0 :: (load (s8))
+    %2:gpr32common = ANDWri %1, 15
+    $w0 = COPY %2
+    RET_ReallyLR implicit $w0
+...
+---
+name: small_mask_not_removed
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $x0
+    ; CHECK-LABEL: name: small_mask_not_removed
+    ; CHECK: liveins: $x0
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:gpr64common = COPY $x0
+    ; CHECK-NEXT: [[LDRBBui:%[0-9]+]]:gpr32common = LDRBBui [[COPY]], 0 :: (load (s8))
+    ; CHECK-NEXT: [[ANDWri:%[0-9]+]]:gpr32common = ANDWri [[LDRBBui]], 3
+    ; CHECK-NEXT: $w0 = COPY [[ANDWri]]
+    ; CHECK-NEXT: RET_ReallyLR implicit $w0
+    %0:gpr64common = COPY $x0
+    %1:gpr32common = LDRBBui %0, 0 :: (load (s8))
+    %2:gpr32common = ANDWri %1, 3
+    $w0 = COPY %2
+    RET_ReallyLR implicit $w0
+...
+---
+name: non_load_not_removed
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $w0, $w1
+    ; CHECK-LABEL: name: non_load_not_removed
+    ; CHECK: liveins: $w0, $w1
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:gpr32 = COPY $w0
+    ; CHECK-NEXT: [[COPY1:%[0-9]+]]:gpr32 = COPY $w1
+    ; CHECK-NEXT: [[ADDWrr:%[0-9]+]]:gpr32common = ADDWrr [[COPY]], [[COPY1]]
+    ; CHECK-NEXT: [[ANDWri:%[0-9]+]]:gpr32common = ANDWri [[ADDWrr]], 7
+    ; CHECK-NEXT: $w0 = COPY [[ANDWri]]
+    ; CHECK-NEXT: RET_ReallyLR implicit $w0
+    %0:gpr32 = COPY $w0
+    %1:gpr32 = COPY $w1
+    %2:gpr32common = ADDWrr %0, %1
+    %3:gpr32common = ANDWri %2, 7
+    $w0 = COPY %3
+    RET_ReallyLR implicit $w0
+...
+---
+name: physreg_dest_not_removed
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $x0
+    ; CHECK-LABEL: name: physreg_dest_not_removed
+    ; CHECK: liveins: $x0
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:gpr64common = COPY $x0
+    ; CHECK-NEXT: [[LDRHHui:%[0-9]+]]:gpr32 = LDRHHui [[COPY]], 0 :: (load (s16))
+    ; CHECK-NEXT: $w2 = COPY [[LDRHHui]]
+    ; CHECK-NEXT: RET_ReallyLR implicit $w2
+    %0:gpr64common = COPY $x0
+    %1:gpr32 = LDRHHui %0, 0 :: (load (s16))
+    $w2 = ANDWri %1, 15
+    RET_ReallyLR implicit $w2
+...

>From 9c1ef28c0088c85543418ac6b77e8432b8d09c95 Mon Sep 17 00:00:00 2001
From: Henry Jiang <henry_jiang2 at apple.com>
Date: Mon, 30 Mar 2026 18:51:46 -0700
Subject: [PATCH 2/2] reformat

---
 llvm/lib/Target/AArch64/AArch64MIPeepholeOpt.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/Target/AArch64/AArch64MIPeepholeOpt.cpp b/llvm/lib/Target/AArch64/AArch64MIPeepholeOpt.cpp
index 01b42eaae3b08..5072cf16bbbd1 100644
--- a/llvm/lib/Target/AArch64/AArch64MIPeepholeOpt.cpp
+++ b/llvm/lib/Target/AArch64/AArch64MIPeepholeOpt.cpp
@@ -661,8 +661,8 @@ bool AArch64MIPeepholeOptImpl::splitTwoPartImm(MachineInstr &MI,
 // All uses of %2 are replaced by %1, since the load is already zero extending.
 bool AArch64MIPeepholeOptImpl::removeRedundantAndMask(MachineInstr &MI) {
   assert((MI.getOpcode() == AArch64::ANDWri ||
-         MI.getOpcode() == AArch64::ANDXri) &&
-             "Unsupported masking instructions");
+          MI.getOpcode() == AArch64::ANDXri) &&
+         "Unsupported masking instructions");
 
   unsigned RegSize = MI.getOpcode() == AArch64::ANDWri ? 32 : 64;
   auto EncodedImm = MI.getOperand(2).getImm();



More information about the llvm-commits mailing list