[llvm] [RegAlloc][X86] Rematerialization of instructions with physical register (PR #198853)

via llvm-commits llvm-commits at lists.llvm.org
Fri Aug 14 10:53:40 PDT 2026


https://github.com/weiguozhi updated https://github.com/llvm/llvm-project/pull/198853

>From a3bf7a03d1180a228f6a5c665d1dd3af2e47dff0 Mon Sep 17 00:00:00 2001
From: Guozhi Wei <carrot at google.com>
Date: Wed, 20 May 2026 16:18:45 +0000
Subject: [PATCH 1/4] [RegAlloc][X86] Rematerialization of instructions with
 physical register definition

We can rematerialize instructions with physical register definition,
with additional check if the physical register is dead at the use site.
---
 llvm/include/llvm/CodeGen/TargetInstrInfo.h |   7 +
 llvm/lib/CodeGen/CalcSpillWeights.cpp       |  41 ++++-
 llvm/lib/CodeGen/TargetInstrInfo.cpp        |   8 +-
 llvm/lib/Target/X86/X86InstrInfo.cpp        |  34 ++++
 llvm/lib/Target/X86/X86InstrInfo.h          |   2 +
 llvm/lib/Target/X86/X86InstrMisc.td         |   9 +-
 llvm/test/CodeGen/X86/remat-flags.ll        | 165 ++++++++++++++++++++
 7 files changed, 256 insertions(+), 10 deletions(-)
 create mode 100644 llvm/test/CodeGen/X86/remat-flags.ll

diff --git a/llvm/include/llvm/CodeGen/TargetInstrInfo.h b/llvm/include/llvm/CodeGen/TargetInstrInfo.h
index 4749d06501cb2..b1e817c630bfe 100644
--- a/llvm/include/llvm/CodeGen/TargetInstrInfo.h
+++ b/llvm/include/llvm/CodeGen/TargetInstrInfo.h
@@ -192,6 +192,13 @@ class LLVM_ABI TargetInstrInfo : public MCInstrInfo {
            (MI.getDesc().isRematerializable() && isReMaterializableImpl(MI));
   }
 
+  /// Return true if an instruction can be rematerialized as a different
+  /// instruction without clobbering the physical register.
+  virtual bool canRematerializeIgnorePhysRegDef(
+      const MachineInstr &MI, const MachineOperand &MO) const {
+    return false;
+  }
+
   /// Given \p MO is a PhysReg use return if it can be ignored for the purpose
   /// of instruction rematerialization or sinking.
   virtual bool isIgnorableUse(const MachineOperand &MO) const {
diff --git a/llvm/lib/CodeGen/CalcSpillWeights.cpp b/llvm/lib/CodeGen/CalcSpillWeights.cpp
index 51cce4eb78c94..df10c9f914bc2 100644
--- a/llvm/lib/CodeGen/CalcSpillWeights.cpp
+++ b/llvm/lib/CodeGen/CalcSpillWeights.cpp
@@ -30,6 +30,12 @@ using namespace llvm;
 
 #define DEBUG_TYPE "calcspillweights"
 
+cl::opt<bool> AllowRematerializePhysReg(
+    "allow-rematerialize-phys-reg",
+    cl::desc("Allow rematerialize instructions with physical register "
+             "definition, like POPCNT on x86 can write EFLAGS register."),
+    cl::init(false), cl::Hidden);
+
 void VirtRegAuxInfo::calculateSpillWeightsAndHints() {
   LLVM_DEBUG(dbgs() << "********** Compute Spill Weights **********\n"
                     << "********** Function: " << MF.getName() << '\n');
@@ -146,17 +152,44 @@ bool VirtRegAuxInfo::isRematerializable(const LiveInterval &LI,
   return true;
 }
 
-bool VirtRegAuxInfo::allUsesAvailableAt(const MachineInstr *MI,
+bool VirtRegAuxInfo::allUsesAvailableAt(const MachineInstr *OrigMI,
                                         SlotIndex UseIdx,
                                         const LiveIntervals &LIS,
                                         const MachineRegisterInfo &MRI,
                                         const TargetInstrInfo &TII) {
-  SlotIndex OrigIdx = LIS.getInstructionIndex(*MI).getRegSlot(true);
+  SlotIndex OrigIdx = LIS.getInstructionIndex(*OrigMI).getRegSlot(true);
+  auto MBB = LIS.getMBBFromIndex(UseIdx);
+  MachineInstr *UseMI = LIS.getInstructionFromIndex(UseIdx);
+  MachineBasicBlock::iterator UseIt = UseMI;
+  if (!UseMI) {
+    if (MBB->empty() ||
+        UseIdx < LIS.getInstructionIndex(*MBB->begin()).getBaseIndex())
+      UseIt = MBB->begin();
+    else {
+      UseIt = MBB->end();
+    }
+  }
   UseIdx = std::max(UseIdx, UseIdx.getRegSlot(true));
-  for (const MachineOperand &MO : MI->operands()) {
-    if (!MO.isReg() || !MO.getReg() || !MO.readsReg())
+
+  for (const MachineOperand &MO : OrigMI->operands()) {
+    if (!MO.isReg() || !MO.getReg())
       continue;
 
+    if (!MO.readsReg()) {
+      if (MO.getReg().isVirtual() || !AllowRematerializePhysReg)
+        continue;
+      // A physical register is defined here, and we allow rematerialization of
+      // an instruction with physical register definition, it must be dead at
+      // this position.
+      const TargetRegisterInfo *TRI = MRI.getTargetRegisterInfo();
+      if ((MBB->computeRegisterLiveness(TRI, MO.getReg(), UseIt) ==
+           MachineBasicBlock::LQR_Dead) ||
+          TII.canRematerializeIgnorePhysRegDef(*OrigMI, MO))
+        continue;
+      else
+        return false;
+    }
+
     // We can't remat physreg uses, unless it is a constant or target wants
     // to ignore this use.
     if (MO.getReg().isPhysical()) {
diff --git a/llvm/lib/CodeGen/TargetInstrInfo.cpp b/llvm/lib/CodeGen/TargetInstrInfo.cpp
index 92fc628e888e5..e5837b4dfc708 100644
--- a/llvm/lib/CodeGen/TargetInstrInfo.cpp
+++ b/llvm/lib/CodeGen/TargetInstrInfo.cpp
@@ -57,6 +57,8 @@ static cl::opt<unsigned int> MaxAccumulatorWidth(
     "acc-max-width", cl::Hidden, cl::init(3),
     cl::desc("Maximum number of branches in the accumulator tree"));
 
+extern cl::opt<bool> AllowRematerializePhysReg;
+
 TargetInstrInfo::~TargetInstrInfo() = default;
 
 const TargetRegisterClass *TargetInstrInfo::getRegClass(const MCInstrDesc &MCID,
@@ -1660,8 +1662,10 @@ bool TargetInstrInfo::isReMaterializableImpl(
         if (!MRI.isConstantPhysReg(Reg))
           return false;
       } else {
-        // A physreg def. We can't remat it.
-        return false;
+        // If a physreg def is allowed, we must make sure it is dead at the use
+        // site.
+        if (!AllowRematerializePhysReg)
+          return false;
       }
       continue;
     }
diff --git a/llvm/lib/Target/X86/X86InstrInfo.cpp b/llvm/lib/Target/X86/X86InstrInfo.cpp
index 2087f2bfc9c88..8ef2973cb9013 100644
--- a/llvm/lib/Target/X86/X86InstrInfo.cpp
+++ b/llvm/lib/Target/X86/X86InstrInfo.cpp
@@ -761,6 +761,26 @@ static bool regIsPICBase(Register BaseReg, const MachineRegisterInfo &MRI) {
   return isPICBase;
 }
 
+/// Return true if an instruction can be rematerialized as a different
+/// instruction without clobbering eflags register, or the definition of the
+/// physical register really doesn't matter.
+bool X86InstrInfo::canRematerializeIgnorePhysRegDef(
+    const MachineInstr &MI, const MachineOperand &MO) const {
+  assert(MO.isReg() && MO.getReg() && MO.getReg().isPhysical());
+
+  switch (MI.getOpcode()) {
+  case X86::MOV32r0:
+  case X86::MOV32r1:
+  case X86::MOV32r_1:
+    if (MO.getReg() == X86::EFLAGS)
+      return true;
+    else
+      return false;
+  }
+
+  return false;
+}
+
 bool X86InstrInfo::isReMaterializableImpl(
     const MachineInstr &MI) const {
   switch (MI.getOpcode()) {
@@ -957,7 +977,21 @@ bool X86InstrInfo::isReMaterializableImpl(
     }
     break;
   }
+
+  case X86::LZCNT16rr:
+  case X86::LZCNT32rr:
+  case X86::LZCNT64rr:
+  case X86::TZCNT16rr:
+  case X86::TZCNT32rr:
+  case X86::TZCNT64rr:
+  case X86::POPCNT16rr:
+  case X86::POPCNT32rr:
+  case X86::POPCNT64rr:
+    // Let TargetInstrInfo::isReMaterializableImpl to do general operands
+    // checking.
+    break;
   }
+
   return TargetInstrInfo::isReMaterializableImpl(MI);
 }
 
diff --git a/llvm/lib/Target/X86/X86InstrInfo.h b/llvm/lib/Target/X86/X86InstrInfo.h
index b6fac55373d05..9610cd795435d 100644
--- a/llvm/lib/Target/X86/X86InstrInfo.h
+++ b/llvm/lib/Target/X86/X86InstrInfo.h
@@ -351,6 +351,8 @@ class X86InstrInfo final : public X86GenInstrInfo {
   Register isStoreToStackSlotPostFE(const MachineInstr &MI,
                                     int &FrameIndex) const override;
 
+  bool canRematerializeIgnorePhysRegDef(const MachineInstr &MI,
+                                        const MachineOperand &) const override;
   bool isReMaterializableImpl(const MachineInstr &MI) const override;
   void
   reMaterialize(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI,
diff --git a/llvm/lib/Target/X86/X86InstrMisc.td b/llvm/lib/Target/X86/X86InstrMisc.td
index 14a2f477450a3..5247d3092f67a 100644
--- a/llvm/lib/Target/X86/X86InstrMisc.td
+++ b/llvm/lib/Target/X86/X86InstrMisc.td
@@ -1161,10 +1161,11 @@ let Predicates = [HasRDSEED], Defs = [EFLAGS], SchedRW = [WriteSystem] in {
 //
 multiclass Lzcnt<bits<8> o, string m, SDPatternOperator node, X86TypeInfo t,
                  SchedWrite schedrr, SchedWrite schedrm, string suffix = ""> {
-  def rr#suffix : ITy<o, MRMSrcReg, t, (outs t.RegClass:$dst),
-                      (ins t.RegClass:$src1), m, unaryop_ndd_args,
-                      [(set t.RegClass:$dst, (node t.RegClass:$src1))]>,
-                  TB, Sched<[schedrr]>;
+  let isReMaterializable = 1 in
+    def rr#suffix : ITy<o, MRMSrcReg, t, (outs t.RegClass:$dst),
+                        (ins t.RegClass:$src1), m, unaryop_ndd_args,
+                        [(set t.RegClass:$dst, (node t.RegClass:$src1))]>,
+                    TB, Sched<[schedrr]>;
   let mayLoad = 1 in
     def rm#suffix : ITy<o, MRMSrcMem, t, (outs t.RegClass:$dst),
                         (ins t.MemOperand:$src1), m, unaryop_ndd_args,
diff --git a/llvm/test/CodeGen/X86/remat-flags.ll b/llvm/test/CodeGen/X86/remat-flags.ll
new file mode 100644
index 0000000000000..0ddb822c71974
--- /dev/null
+++ b/llvm/test/CodeGen/X86/remat-flags.ll
@@ -0,0 +1,165 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2
+; RUN: llc -mtriple=i686-unknown-unknown -mattr=+popcnt -mattr=cmov -frame-pointer=all -allow-rematerialize-phys-reg=true < %s | FileCheck %s
+
+; This file tests rematerialization of instructions with physical register
+; modified.
+
+; Instruction popcnt can be rematerialized.
+define void @test1(i32 %v1, i32 %v2, i32* %ptr) {
+; CHECK-LABEL: test1:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    pushl %ebp
+; CHECK-NEXT:    .cfi_def_cfa_offset 8
+; CHECK-NEXT:    .cfi_offset %ebp, -8
+; CHECK-NEXT:    movl %esp, %ebp
+; CHECK-NEXT:    .cfi_def_cfa_register %ebp
+; CHECK-NEXT:    pushl %ebx
+; CHECK-NEXT:    pushl %edi
+; CHECK-NEXT:    pushl %esi
+; CHECK-NEXT:    .cfi_offset %esi, -20
+; CHECK-NEXT:    .cfi_offset %edi, -16
+; CHECK-NEXT:    .cfi_offset %ebx, -12
+; CHECK-NEXT:    movl 16(%ebp), %eax
+; CHECK-NEXT:    movl 12(%ebp), %edi
+; CHECK-NEXT:    movl 8(%ebp), %esi
+; CHECK-NEXT:    leal (%esi,%edi), %edx
+; CHECK-NEXT:    popcntl %esi, %ecx
+; CHECK-NEXT:    movl %edx, 4(%eax)
+; CHECK-NEXT:    movl %ecx, 4(%eax)
+; CHECK-NEXT:    movl 8(%eax), %ebx
+; CHECK-NEXT:    addl %esi, %ebx
+; CHECK-NEXT:    movl %edi, %ecx
+; CHECK-NEXT:    subl %ebx, %ecx
+; CHECK-NEXT:    movl %ecx, 8(%eax)
+; CHECK-NEXT:    movl 48(%eax), %ebx
+; CHECK-NEXT:    movl 52(%eax), %edi
+; CHECK-NEXT:    movl %edi, 52(%eax)
+; CHECK-NEXT:    movl %ebx, 48(%eax)
+; CHECK-NEXT:    movl %ecx, 8(%eax)
+; CHECK-NEXT:    popcntl %esi, %ecx
+; CHECK-NEXT:    movl %ecx, 8(%eax)
+; CHECK-NEXT:    movl %esi, 16(%eax)
+; CHECK-NEXT:    movl 12(%ebp), %ecx
+; CHECK-NEXT:    movl %ecx, 20(%eax)
+; CHECK-NEXT:    movl %edx, 12(%eax)
+; CHECK-NEXT:    popl %esi
+; CHECK-NEXT:    popl %edi
+; CHECK-NEXT:    popl %ebx
+; CHECK-NEXT:    popl %ebp
+; CHECK-NEXT:    .cfi_def_cfa %esp, 4
+; CHECK-NEXT:    retl
+  %v3 = add i32 %v1, %v2
+  %cnt = tail call i32 @llvm.ctpop.i32(i32 %v1)
+  %p1 = getelementptr i32, i32* %ptr, i32 1
+  store volatile i32 %v3, i32* %p1, align 8
+  store volatile i32 %cnt, i32* %p1, align 8
+
+  %p2 = getelementptr i32, i32* %ptr, i32 2
+  %v4 = load volatile i32, i32* %p2, align 8
+  %v5 = add i32 %v1, %v4
+  %v6 = sub i32 %v2, %v5
+  store volatile i32 %v6, i32* %p2, align 8
+
+  %p64 = getelementptr i64, i64* %ptr, i32 6
+  %v7  = load volatile i64, i64* %p64, align 8
+  store volatile i64 %v7, i64* %p64, align 8
+
+  store volatile i32 %v6, i32* %p2, align 8
+  store volatile i32 %cnt, i32* %p2, align 8
+
+  %p4 = getelementptr i32, i32* %ptr, i32 4
+  store volatile i32 %v1, i32* %p4, align 8
+  %p5 = getelementptr i32, i32* %ptr, i32 5
+  store volatile i32 %v2, i32* %p5, align 8
+
+  %p3 = getelementptr i32, i32* %ptr, i32 3
+  store volatile i32 %v3, i32* %p3, align 8
+
+  ret void
+}
+
+; Instruction popcnt can't be rematerialized in this test case. Because this
+; instruction modifies flags register, but the flags register is not dead at the
+; use site.
+define void @test2(i32 %v1, i32 %v2, i32* %ptr) {
+; CHECK-LABEL: test2:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    pushl %ebp
+; CHECK-NEXT:    .cfi_def_cfa_offset 8
+; CHECK-NEXT:    .cfi_offset %ebp, -8
+; CHECK-NEXT:    movl %esp, %ebp
+; CHECK-NEXT:    .cfi_def_cfa_register %ebp
+; CHECK-NEXT:    pushl %ebx
+; CHECK-NEXT:    pushl %edi
+; CHECK-NEXT:    pushl %esi
+; CHECK-NEXT:    .cfi_offset %esi, -20
+; CHECK-NEXT:    .cfi_offset %edi, -16
+; CHECK-NEXT:    .cfi_offset %ebx, -12
+; CHECK-NEXT:    movl 16(%ebp), %eax
+; CHECK-NEXT:    movl 12(%ebp), %ecx
+; CHECK-NEXT:    movl 8(%ebp), %esi
+; CHECK-NEXT:    leal (%esi,%ecx), %edx
+; CHECK-NEXT:    popcntl %esi, %edi
+; CHECK-NEXT:    movl %edx, 4(%eax)
+; CHECK-NEXT:    movl %edi, 4(%eax)
+; CHECK-NEXT:    movl 8(%eax), %ebx
+; CHECK-NEXT:    addl %esi, %ebx
+; CHECK-NEXT:    subl %ebx, %ecx
+; CHECK-NEXT:    movl %ecx, 8(%eax)
+; CHECK-NEXT:    movl 48(%eax), %ecx
+; CHECK-NEXT:    movl 52(%eax), %ebx
+; CHECK-NEXT:    movl %ebx, 52(%eax)
+; CHECK-NEXT:    movl %ecx, 48(%eax)
+; CHECK-NEXT:    movl 12(%ebp), %ebx
+; CHECK-NEXT:    movl %ebx, %ecx
+; CHECK-NEXT:    cmovel %edx, %ecx
+; CHECK-NEXT:    movl %ecx, 8(%eax)
+; CHECK-NEXT:    movl %edi, 8(%eax)
+; CHECK-NEXT:    cmovnel %edx, %esi
+; CHECK-NEXT:    movl %esi, 16(%eax)
+; CHECK-NEXT:    movl %ebx, 20(%eax)
+; CHECK-NEXT:    movl %edx, 12(%eax)
+; CHECK-NEXT:    popl %esi
+; CHECK-NEXT:    popl %edi
+; CHECK-NEXT:    popl %ebx
+; CHECK-NEXT:    popl %ebp
+; CHECK-NEXT:    .cfi_def_cfa %esp, 4
+; CHECK-NEXT:    retl
+  %v3 = add i32 %v1, %v2
+  %cnt = tail call i32 @llvm.ctpop.i32(i32 %v1)
+  %p1 = getelementptr i32, i32* %ptr, i32 1
+  store volatile i32 %v3, i32* %p1, align 8
+  store volatile i32 %cnt, i32* %p1, align 8
+
+  %p2 = getelementptr i32, i32* %ptr, i32 2
+  %v4 = load volatile i32, i32* %p2, align 8
+  %v5 = add i32 %v1, %v4
+  %v6 = sub i32 %v2, %v5
+  store volatile i32 %v6, i32* %p2, align 8
+
+  %p64 = getelementptr i64, i64* %ptr, i32 6
+  %v7  = load volatile i64, i64* %p64, align 8
+  store volatile i64 %v7, i64* %p64, align 8
+
+  ; flag register is defined here.
+  %b = icmp eq i32 %v6, 0
+  %v8 = select i1 %b, i32 %v3, i32 %v2
+  store volatile i32 %v8, i32* %p2, align 8
+
+  ; %cnt can't be rematerialized because flag register is not dead.
+  store volatile i32 %cnt, i32* %p2, align 8
+
+  ; flag register is used and killed here.
+  %v9 = select i1 %b, i32 %v1, i32 %v3
+  %p4 = getelementptr i32, i32* %ptr, i32 4
+  store volatile i32 %v9, i32* %p4, align 8
+  %p5 = getelementptr i32, i32* %ptr, i32 5
+  store volatile i32 %v2, i32* %p5, align 8
+
+  %p3 = getelementptr i32, i32* %ptr, i32 3
+  store volatile i32 %v3, i32* %p3, align 8
+
+  ret void
+}
+
+declare i32 @llvm.ctpop.i32(i32) nounwind readnone

>From fcf784b993a8d2e54bfd4b6531c7255bec5051cf Mon Sep 17 00:00:00 2001
From: Guozhi Wei <carrot at google.com>
Date: Wed, 27 May 2026 17:56:50 +0000
Subject: [PATCH 2/4] Reformat.

---
 llvm/include/llvm/CodeGen/TargetInstrInfo.h | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/llvm/include/llvm/CodeGen/TargetInstrInfo.h b/llvm/include/llvm/CodeGen/TargetInstrInfo.h
index b1e817c630bfe..5243b432224bb 100644
--- a/llvm/include/llvm/CodeGen/TargetInstrInfo.h
+++ b/llvm/include/llvm/CodeGen/TargetInstrInfo.h
@@ -194,8 +194,9 @@ class LLVM_ABI TargetInstrInfo : public MCInstrInfo {
 
   /// Return true if an instruction can be rematerialized as a different
   /// instruction without clobbering the physical register.
-  virtual bool canRematerializeIgnorePhysRegDef(
-      const MachineInstr &MI, const MachineOperand &MO) const {
+  virtual bool
+  canRematerializeIgnorePhysRegDef(const MachineInstr &MI,
+                                   const MachineOperand &MO) const {
     return false;
   }
 

>From 677d9b035d97f06851822c1f41f0ba7a0caf0f72 Mon Sep 17 00:00:00 2001
From: Guozhi Wei <carrot at google.com>
Date: Wed, 3 Jun 2026 17:28:14 +0000
Subject: [PATCH 3/4] Add an mir test.

---
 llvm/include/llvm/CodeGen/TargetInstrInfo.h |  11 +++
 llvm/test/CodeGen/X86/remat-flags.ll        |  86 +++++++----------
 llvm/test/CodeGen/X86/remat-flags.mir       | 100 ++++++++++++++++++++
 3 files changed, 147 insertions(+), 50 deletions(-)
 create mode 100644 llvm/test/CodeGen/X86/remat-flags.mir

diff --git a/llvm/include/llvm/CodeGen/TargetInstrInfo.h b/llvm/include/llvm/CodeGen/TargetInstrInfo.h
index 5243b432224bb..9122c46867b5f 100644
--- a/llvm/include/llvm/CodeGen/TargetInstrInfo.h
+++ b/llvm/include/llvm/CodeGen/TargetInstrInfo.h
@@ -194,6 +194,17 @@ class LLVM_ABI TargetInstrInfo : public MCInstrInfo {
 
   /// Return true if an instruction can be rematerialized as a different
   /// instruction without clobbering the physical register.
+  ///
+  /// For example in x86 we can rematerialize
+  ///     %100 = MOV32r0
+  /// it is actually translated to
+  ///     xor  %100, %100
+  /// Instruction xor clobbers $eflags, if $eflags is not dead at the use site,
+  /// we can't simply clone the MOV32r0(xor) instruction, instead it can be
+  /// rematerialized as
+  ///     mov  %100, 0
+  /// The mov instructions doesn't clobber $eflags, so it is safe to insert the
+  /// mov instruction at the use site.
   virtual bool
   canRematerializeIgnorePhysRegDef(const MachineInstr &MI,
                                    const MachineOperand &MO) const {
diff --git a/llvm/test/CodeGen/X86/remat-flags.ll b/llvm/test/CodeGen/X86/remat-flags.ll
index 0ddb822c71974..e558004068828 100644
--- a/llvm/test/CodeGen/X86/remat-flags.ll
+++ b/llvm/test/CodeGen/X86/remat-flags.ll
@@ -5,20 +5,14 @@
 ; modified.
 
 ; Instruction popcnt can be rematerialized.
-define void @test1(i32 %v1, i32 %v2, i32* %ptr) {
+define void @test1(i32 %v1, i32 %v2, ptr %ptr) nounwind {
 ; CHECK-LABEL: test1:
 ; CHECK:       # %bb.0:
 ; CHECK-NEXT:    pushl %ebp
-; CHECK-NEXT:    .cfi_def_cfa_offset 8
-; CHECK-NEXT:    .cfi_offset %ebp, -8
 ; CHECK-NEXT:    movl %esp, %ebp
-; CHECK-NEXT:    .cfi_def_cfa_register %ebp
 ; CHECK-NEXT:    pushl %ebx
 ; CHECK-NEXT:    pushl %edi
 ; CHECK-NEXT:    pushl %esi
-; CHECK-NEXT:    .cfi_offset %esi, -20
-; CHECK-NEXT:    .cfi_offset %edi, -16
-; CHECK-NEXT:    .cfi_offset %ebx, -12
 ; CHECK-NEXT:    movl 16(%ebp), %eax
 ; CHECK-NEXT:    movl 12(%ebp), %edi
 ; CHECK-NEXT:    movl 8(%ebp), %esi
@@ -46,34 +40,33 @@ define void @test1(i32 %v1, i32 %v2, i32* %ptr) {
 ; CHECK-NEXT:    popl %edi
 ; CHECK-NEXT:    popl %ebx
 ; CHECK-NEXT:    popl %ebp
-; CHECK-NEXT:    .cfi_def_cfa %esp, 4
 ; CHECK-NEXT:    retl
   %v3 = add i32 %v1, %v2
   %cnt = tail call i32 @llvm.ctpop.i32(i32 %v1)
-  %p1 = getelementptr i32, i32* %ptr, i32 1
-  store volatile i32 %v3, i32* %p1, align 8
-  store volatile i32 %cnt, i32* %p1, align 8
+  %p1 = getelementptr i32, ptr %ptr, i32 1
+  store volatile i32 %v3, ptr %p1, align 8
+  store volatile i32 %cnt, ptr %p1, align 8
 
-  %p2 = getelementptr i32, i32* %ptr, i32 2
-  %v4 = load volatile i32, i32* %p2, align 8
+  %p2 = getelementptr i32, ptr %ptr, i32 2
+  %v4 = load volatile i32, ptr %p2, align 8
   %v5 = add i32 %v1, %v4
   %v6 = sub i32 %v2, %v5
-  store volatile i32 %v6, i32* %p2, align 8
+  store volatile i32 %v6, ptr %p2, align 8
 
-  %p64 = getelementptr i64, i64* %ptr, i32 6
-  %v7  = load volatile i64, i64* %p64, align 8
-  store volatile i64 %v7, i64* %p64, align 8
+  %p64 = getelementptr i64, ptr %ptr, i32 6
+  %v7  = load volatile i64, ptr %p64, align 8
+  store volatile i64 %v7, ptr %p64, align 8
 
-  store volatile i32 %v6, i32* %p2, align 8
-  store volatile i32 %cnt, i32* %p2, align 8
+  store volatile i32 %v6, ptr %p2, align 8
+  store volatile i32 %cnt, ptr %p2, align 8
 
-  %p4 = getelementptr i32, i32* %ptr, i32 4
-  store volatile i32 %v1, i32* %p4, align 8
-  %p5 = getelementptr i32, i32* %ptr, i32 5
-  store volatile i32 %v2, i32* %p5, align 8
+  %p4 = getelementptr i32, ptr %ptr, i32 4
+  store volatile i32 %v1, ptr %p4, align 8
+  %p5 = getelementptr i32, ptr %ptr, i32 5
+  store volatile i32 %v2, ptr %p5, align 8
 
-  %p3 = getelementptr i32, i32* %ptr, i32 3
-  store volatile i32 %v3, i32* %p3, align 8
+  %p3 = getelementptr i32, ptr %ptr, i32 3
+  store volatile i32 %v3, ptr %p3, align 8
 
   ret void
 }
@@ -81,20 +74,14 @@ define void @test1(i32 %v1, i32 %v2, i32* %ptr) {
 ; Instruction popcnt can't be rematerialized in this test case. Because this
 ; instruction modifies flags register, but the flags register is not dead at the
 ; use site.
-define void @test2(i32 %v1, i32 %v2, i32* %ptr) {
+define void @test2(i32 %v1, i32 %v2, ptr %ptr) nounwind {
 ; CHECK-LABEL: test2:
 ; CHECK:       # %bb.0:
 ; CHECK-NEXT:    pushl %ebp
-; CHECK-NEXT:    .cfi_def_cfa_offset 8
-; CHECK-NEXT:    .cfi_offset %ebp, -8
 ; CHECK-NEXT:    movl %esp, %ebp
-; CHECK-NEXT:    .cfi_def_cfa_register %ebp
 ; CHECK-NEXT:    pushl %ebx
 ; CHECK-NEXT:    pushl %edi
 ; CHECK-NEXT:    pushl %esi
-; CHECK-NEXT:    .cfi_offset %esi, -20
-; CHECK-NEXT:    .cfi_offset %edi, -16
-; CHECK-NEXT:    .cfi_offset %ebx, -12
 ; CHECK-NEXT:    movl 16(%ebp), %eax
 ; CHECK-NEXT:    movl 12(%ebp), %ecx
 ; CHECK-NEXT:    movl 8(%ebp), %esi
@@ -123,41 +110,40 @@ define void @test2(i32 %v1, i32 %v2, i32* %ptr) {
 ; CHECK-NEXT:    popl %edi
 ; CHECK-NEXT:    popl %ebx
 ; CHECK-NEXT:    popl %ebp
-; CHECK-NEXT:    .cfi_def_cfa %esp, 4
 ; CHECK-NEXT:    retl
   %v3 = add i32 %v1, %v2
   %cnt = tail call i32 @llvm.ctpop.i32(i32 %v1)
-  %p1 = getelementptr i32, i32* %ptr, i32 1
-  store volatile i32 %v3, i32* %p1, align 8
-  store volatile i32 %cnt, i32* %p1, align 8
+  %p1 = getelementptr i32, ptr %ptr, i32 1
+  store volatile i32 %v3, ptr %p1, align 8
+  store volatile i32 %cnt, ptr %p1, align 8
 
-  %p2 = getelementptr i32, i32* %ptr, i32 2
-  %v4 = load volatile i32, i32* %p2, align 8
+  %p2 = getelementptr i32, ptr %ptr, i32 2
+  %v4 = load volatile i32, ptr %p2, align 8
   %v5 = add i32 %v1, %v4
   %v6 = sub i32 %v2, %v5
-  store volatile i32 %v6, i32* %p2, align 8
+  store volatile i32 %v6, ptr %p2, align 8
 
-  %p64 = getelementptr i64, i64* %ptr, i32 6
-  %v7  = load volatile i64, i64* %p64, align 8
-  store volatile i64 %v7, i64* %p64, align 8
+  %p64 = getelementptr i64, ptr %ptr, i32 6
+  %v7  = load volatile i64, ptr %p64, align 8
+  store volatile i64 %v7, ptr %p64, align 8
 
   ; flag register is defined here.
   %b = icmp eq i32 %v6, 0
   %v8 = select i1 %b, i32 %v3, i32 %v2
-  store volatile i32 %v8, i32* %p2, align 8
+  store volatile i32 %v8, ptr %p2, align 8
 
   ; %cnt can't be rematerialized because flag register is not dead.
-  store volatile i32 %cnt, i32* %p2, align 8
+  store volatile i32 %cnt, ptr %p2, align 8
 
   ; flag register is used and killed here.
   %v9 = select i1 %b, i32 %v1, i32 %v3
-  %p4 = getelementptr i32, i32* %ptr, i32 4
-  store volatile i32 %v9, i32* %p4, align 8
-  %p5 = getelementptr i32, i32* %ptr, i32 5
-  store volatile i32 %v2, i32* %p5, align 8
+  %p4 = getelementptr i32, ptr %ptr, i32 4
+  store volatile i32 %v9, ptr %p4, align 8
+  %p5 = getelementptr i32, ptr %ptr, i32 5
+  store volatile i32 %v2, ptr %p5, align 8
 
-  %p3 = getelementptr i32, i32* %ptr, i32 3
-  store volatile i32 %v3, i32* %p3, align 8
+  %p3 = getelementptr i32, ptr %ptr, i32 3
+  store volatile i32 %v3, ptr %p3, align 8
 
   ret void
 }
diff --git a/llvm/test/CodeGen/X86/remat-flags.mir b/llvm/test/CodeGen/X86/remat-flags.mir
new file mode 100644
index 0000000000000..2f7ba714701e7
--- /dev/null
+++ b/llvm/test/CodeGen/X86/remat-flags.mir
@@ -0,0 +1,100 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+# RUN: llc -mtriple=i686-unknown-unknown -mattr=+popcnt -mattr=cmov -frame-pointer=all -allow-rematerialize-phys-reg=true -run-pass=greedy %s -o - | FileCheck %s
+
+# This file tests rematerialization of instructions with physical register
+# modified.
+
+# In test1, instruction popcnt can be rematerialized.
+
+--- |
+  declare i32 @llvm.ctpop.i32(i32) nounwind readnone
+
+  define void @test1(i32 %v1, i32 %v2, ptr %ptr) nounwind {
+    %v3 = add i32 %v1, %v2
+    %cnt = tail call i32 @llvm.ctpop.i32(i32 %v1)
+    %p1 = getelementptr i32, ptr %ptr, i32 1
+    store volatile i32 %v3, ptr %p1, align 8
+    store volatile i32 %cnt, ptr %p1, align 8
+
+    %p2 = getelementptr i32, ptr %ptr, i32 2
+    %v4 = load volatile i32, ptr %p2, align 8
+    %v5 = add i32 %v1, %v4
+    %v6 = sub i32 %v2, %v5
+    store volatile i32 %v6, ptr %p2, align 8
+
+    %p64 = getelementptr i64, ptr %ptr, i32 6
+    %v7  = load volatile i64, ptr %p64, align 8
+    store volatile i64 %v7, ptr %p64, align 8
+
+    store volatile i32 %v6, ptr %p2, align 8
+    store volatile i32 %cnt, ptr %p2, align 8
+
+    %p4 = getelementptr i32, ptr %ptr, i32 4
+    store volatile i32 %v1, ptr %p4, align 8
+    %p5 = getelementptr i32, ptr %ptr, i32 5
+    store volatile i32 %v2, ptr %p5, align 8
+
+    %p3 = getelementptr i32, ptr %ptr, i32 3
+    store volatile i32 %v3, ptr %p3, align 8
+
+    ret void
+  }
+
+...
+---
+name:            test1
+tracksRegLiveness: true
+fixedStack:
+  - { id: 0, offset: 8, size: 4, alignment: 4, isImmutable: true }
+  - { id: 1, offset: 4, size: 4, alignment: 4, isImmutable: true }
+  - { id: 2, offset: 0, size: 4, alignment: 4, isImmutable: true }
+body: |
+    bb.0 (%ir-block.0):
+    ; CHECK-LABEL: name: test1
+    ; CHECK: [[MOV32rm:%[0-9]+]]:gr32 = MOV32rm %fixed-stack.2, 1, $noreg, 0, $noreg :: (load (s32) from %fixed-stack.2)
+    ; CHECK-NEXT: [[MOV32rm1:%[0-9]+]]:gr32_nosp = MOV32rm %fixed-stack.1, 1, $noreg, 0, $noreg :: (load (s32) from %fixed-stack.1)
+    ; CHECK-NEXT: [[MOV32rm2:%[0-9]+]]:gr32 = MOV32rm %fixed-stack.0, 1, $noreg, 0, $noreg :: (load (s32) from %fixed-stack.0)
+    ; CHECK-NEXT: [[LEA32r:%[0-9]+]]:gr32 = LEA32r [[MOV32rm2]], 1, [[MOV32rm1]], 0, $noreg
+    ; CHECK-NEXT: [[POPCNT32rr:%[0-9]+]]:gr32 = POPCNT32rr [[MOV32rm2]], implicit-def dead $eflags
+    ; CHECK-NEXT: MOV32mr [[MOV32rm]], 1, $noreg, 4, $noreg, [[LEA32r]] :: (volatile store (s32) into %ir.p1, align 8)
+    ; CHECK-NEXT: MOV32mr [[MOV32rm]], 1, $noreg, 4, $noreg, [[POPCNT32rr]] :: (volatile store (s32) into %ir.p1, align 8)
+    ; CHECK-NEXT: [[MOV32rm3:%[0-9]+]]:gr32 = MOV32rm [[MOV32rm]], 1, $noreg, 8, $noreg :: (volatile load (s32) from %ir.p2, align 8)
+    ; CHECK-NEXT: [[MOV32rm3:%[0-9]+]]:gr32 = ADD32rr [[MOV32rm3]], [[MOV32rm2]], implicit-def dead $eflags
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:gr32_nosp = COPY [[MOV32rm1]]
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:gr32_nosp = SUB32rr [[COPY]], [[MOV32rm3]], implicit-def dead $eflags
+    ; CHECK-NEXT: MOV32mr [[MOV32rm]], 1, $noreg, 8, $noreg, [[COPY]] :: (volatile store (s32) into %ir.p2, align 8)
+    ; CHECK-NEXT: [[MOV32rm4:%[0-9]+]]:gr32 = MOV32rm [[MOV32rm]], 1, $noreg, 48, $noreg :: (volatile load (s32) from %ir.p64, align 8)
+    ; CHECK-NEXT: [[MOV32rm5:%[0-9]+]]:gr32 = MOV32rm [[MOV32rm]], 1, $noreg, 52, $noreg :: (volatile load (s32) from %ir.p64 + 4, basealign 8)
+    ; CHECK-NEXT: MOV32mr [[MOV32rm]], 1, $noreg, 52, $noreg, [[MOV32rm5]] :: (volatile store (s32) into %ir.p64 + 4, basealign 8)
+    ; CHECK-NEXT: MOV32mr [[MOV32rm]], 1, $noreg, 48, $noreg, [[MOV32rm4]] :: (volatile store (s32) into %ir.p64, align 8)
+    ; CHECK-NEXT: MOV32mr [[MOV32rm]], 1, $noreg, 8, $noreg, [[COPY]] :: (volatile store (s32) into %ir.p2, align 8)
+    ; CHECK-NEXT: [[POPCNT32rr1:%[0-9]+]]:gr32 = POPCNT32rr [[MOV32rm2]], implicit-def dead $eflags
+    ; CHECK-NEXT: MOV32mr [[MOV32rm]], 1, $noreg, 8, $noreg, [[POPCNT32rr1]] :: (volatile store (s32) into %ir.p2, align 8)
+    ; CHECK-NEXT: MOV32mr [[MOV32rm]], 1, $noreg, 16, $noreg, [[MOV32rm2]] :: (volatile store (s32) into %ir.p4, align 8)
+    ; CHECK-NEXT: [[MOV32rm6:%[0-9]+]]:gr32 = MOV32rm %fixed-stack.1, 1, $noreg, 0, $noreg :: (load (s32) from %fixed-stack.1)
+    ; CHECK-NEXT: MOV32mr [[MOV32rm]], 1, $noreg, 20, $noreg, [[MOV32rm6]] :: (volatile store (s32) into %ir.p5, align 8)
+    ; CHECK-NEXT: MOV32mr [[MOV32rm]], 1, $noreg, 12, $noreg, [[LEA32r]] :: (volatile store (s32) into %ir.p3, align 8)
+    ; CHECK-NEXT: RET 0
+      %0:gr32 = MOV32rm %fixed-stack.0, 1, $noreg, 0, $noreg :: (load (s32) from %fixed-stack.0)
+      %1:gr32_nosp = MOV32rm %fixed-stack.1, 1, $noreg, 0, $noreg :: (load (s32) from %fixed-stack.1)
+      %2:gr32 = MOV32rm %fixed-stack.2, 1, $noreg, 0, $noreg :: (load (s32) from %fixed-stack.2)
+      %3:gr32 = LEA32r %2:gr32, 1, %1:gr32_nosp, 0, $noreg
+      %4:gr32 = POPCNT32rr %2:gr32, implicit-def dead $eflags
+      MOV32mr %0:gr32, 1, $noreg, 4, $noreg, %3:gr32 :: (volatile store (s32) into %ir.p1, align 8)
+      MOV32mr %0:gr32, 1, $noreg, 4, $noreg, %4:gr32 :: (volatile store (s32) into %ir.p1, align 8)
+      %5:gr32 = MOV32rm %0:gr32, 1, $noreg, 8, $noreg :: (volatile load (s32) from %ir.p2, align 8)
+      %5:gr32 = ADD32rr %5:gr32, %2:gr32, implicit-def dead $eflags
+      %6:gr32_nosp = COPY %1:gr32_nosp
+      %6:gr32_nosp = SUB32rr %6:gr32_nosp, %5:gr32, implicit-def dead $eflags
+      MOV32mr %0:gr32, 1, $noreg, 8, $noreg, %6:gr32_nosp :: (volatile store (s32) into %ir.p2, align 8)
+      %7:gr32 = MOV32rm %0:gr32, 1, $noreg, 48, $noreg :: (volatile load (s32) from %ir.p64, align 8)
+      %8:gr32 = MOV32rm %0:gr32, 1, $noreg, 52, $noreg :: (volatile load (s32) from %ir.p64 + 4, basealign 8)
+      MOV32mr %0:gr32, 1, $noreg, 52, $noreg, %8:gr32 :: (volatile store (s32) into %ir.p64 + 4, basealign 8)
+      MOV32mr %0:gr32, 1, $noreg, 48, $noreg, %7:gr32 :: (volatile store (s32) into %ir.p64, align 8)
+      MOV32mr %0:gr32, 1, $noreg, 8, $noreg, %6:gr32_nosp :: (volatile store (s32) into %ir.p2, align 8)
+      MOV32mr %0:gr32, 1, $noreg, 8, $noreg, %4:gr32 :: (volatile store (s32) into %ir.p2, align 8)
+      MOV32mr %0:gr32, 1, $noreg, 16, $noreg, %2:gr32 :: (volatile store (s32) into %ir.p4, align 8)
+      MOV32mr %0:gr32, 1, $noreg, 20, $noreg, %1:gr32_nosp :: (volatile store (s32) into %ir.p5, align 8)
+      MOV32mr %0:gr32, 1, $noreg, 12, $noreg, %3:gr32 :: (volatile store (s32) into %ir.p3, align 8)
+      RET 0
+...

>From 8d098f9b755d8bf9284f6e9bfc384f7a17353a9d Mon Sep 17 00:00:00 2001
From: Guozhi Wei <carrot at google.com>
Date: Fri, 17 Jul 2026 14:07:34 -0700
Subject: [PATCH 4/4] Add comment to describe that
 canRematerializeIgnorePhysRegDef is applicable to AArch64 and PowerPC.

---
 llvm/include/llvm/CodeGen/TargetInstrInfo.h | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/llvm/include/llvm/CodeGen/TargetInstrInfo.h b/llvm/include/llvm/CodeGen/TargetInstrInfo.h
index 9122c46867b5f..d398b6bd79f27 100644
--- a/llvm/include/llvm/CodeGen/TargetInstrInfo.h
+++ b/llvm/include/llvm/CodeGen/TargetInstrInfo.h
@@ -205,6 +205,9 @@ class LLVM_ABI TargetInstrInfo : public MCInstrInfo {
   ///     mov  %100, 0
   /// The mov instructions doesn't clobber $eflags, so it is safe to insert the
   /// mov instruction at the use site.
+  ///
+  /// It is more useful for ISAs that can optionally set flag register, such as
+  /// AArch64 and PowerPC.
   virtual bool
   canRematerializeIgnorePhysRegDef(const MachineInstr &MI,
                                    const MachineOperand &MO) const {



More information about the llvm-commits mailing list