[llvm] [MachineCSE] Clear kill flags once per register in copy propagation (PR #226781)
MMS IT GmbH via llvm-commits
llvm-commits at lists.llvm.org
Sun Sep 27 05:25:23 PDT 2026
https://github.com/mms-it-ch created https://github.com/llvm/llvm-project/pull/226781
`MachineCSEImpl::PerformTrivialCopyPropagation` called `MRI->clearKillFlags(SrcReg)` for every copy it propagates. `clearKillFlags` walks all uses of the register, so a virtual register that is copied many times makes the pass quadratic in the size of the function.
On z/OS this is the normal case: the address of every global is loaded from the ADA with a `COPY` of the ADA base register as operand. A function with 20000 such accesses spends 29.8 s in MachineCSE (0.04 s for the same IR on `s390x-unknown-linux-gnu`); a large function generated from COBOL spent 139 of 169 s of `llc` in MachineCSE. Reproducer and numbers are in #226778.
MachineCSE never adds kill flags, so clearing them once per register is enough. The rewritten operand itself may still carry a kill flag of the replaced register, which is cleared directly. The CSE path (`replaceRegWith` + `clearKillFlags(NewReg)`) is unchanged, since it can bring kill flags of the replaced register to `NewReg`.
With the change, MachineCSE takes 0.048 s for the 20000-global case, and the COBOL case takes 34 s instead of 160 s in the backend. The generated code is unchanged (objects built from COBOL with and without the change have the same size; the largest differs only in the compile timestamps). NFC in terms of output, so no new test.
Tests: `ninja check-llvm-codegen` on `main` (6909c17f1) with all targets passes (31891 passed, 62 expectedly failed, 242 unsupported, 0 failed).
Fixes #226778.
Assisted-by: Claude Code (Anthropic)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
>From 5b680e345a790065e7737b7f7170389a4b6ed8bb Mon Sep 17 00:00:00 2001
From: mms-it-ch <info at mms-it.ch>
Date: Sun, 27 Sep 2026 13:46:32 +0200
Subject: [PATCH] [MachineCSE] Clear kill flags once per register in copy
propagation
PerformTrivialCopyPropagation called MRI->clearKillFlags(SrcReg) for every
propagated copy. clearKillFlags walks all uses of the register, so a
register that is copied many times makes the pass quadratic. On z/OS every
access to a global loads its address from the ADA through a copy of the ADA
base register; for a large function generated from COBOL MachineCSE took
139 of 169 seconds of llc.
MachineCSE never adds kill flags, so clearing them once per register is
enough; the kill flag of the rewritten operand itself is cleared directly.
The generated code is unchanged.
Fixes #226778.
Assisted-by: Claude Code (Anthropic)
---
llvm/lib/CodeGen/MachineCSE.cpp | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/llvm/lib/CodeGen/MachineCSE.cpp b/llvm/lib/CodeGen/MachineCSE.cpp
index 23ddccbdacd7a..df7ef395226c5 100644
--- a/llvm/lib/CodeGen/MachineCSE.cpp
+++ b/llvm/lib/CodeGen/MachineCSE.cpp
@@ -14,6 +14,7 @@
#include "llvm/CodeGen/MachineCSE.h"
#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/ScopedHashTable.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallSet.h"
@@ -99,6 +100,8 @@ class MachineCSEImpl {
PREMap;
ScopedHTType VNT;
SmallVector<MachineInstr *, 64> Exps;
+ /// Registers whose kill flags were already cleared by copy propagation.
+ DenseSet<Register> KillFlagsCleared;
unsigned CurrVN = 0;
bool PerformTrivialCopyPropagation(MachineInstr *MI, MachineBasicBlock *MBB);
@@ -200,9 +203,15 @@ bool MachineCSEImpl::PerformTrivialCopyPropagation(MachineInstr *MI,
LLVM_DEBUG(dbgs() << "Coalescing: " << *DefMI);
LLVM_DEBUG(dbgs() << "*** to: " << *MI);
- // Propagate SrcReg of copies to MI.
+ // Propagate SrcReg of copies to MI. Kill flags of SrcReg may be wrong
+ // now; MachineCSE never adds kill flags, so clearing them once per
+ // register is enough (clearing is linear in the number of uses and would
+ // otherwise be quadratic for a register copied many times, e.g. the ADA
+ // base register on z/OS).
MO.setReg(SrcReg);
- MRI->clearKillFlags(SrcReg);
+ MO.setIsKill(false);
+ if (KillFlagsCleared.insert(SrcReg).second)
+ MRI->clearKillFlags(SrcReg);
// Coalesce single use copies.
if (OnlyOneUse) {
// If (and only if) we've eliminated all uses of the copy, also
@@ -932,6 +941,7 @@ void MachineCSEImpl::releaseMemory() {
ScopeMap.clear();
PREMap.clear();
Exps.clear();
+ KillFlagsCleared.clear();
}
bool MachineCSEImpl::run(MachineFunction &MF) {
More information about the llvm-commits
mailing list