[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