[llvm] [SelectionDAG] Keep inline asm clobbers live when read by a glued CopyFromReg (PR #227203)
Fangrui Song via llvm-commits
llvm-commits at lists.llvm.org
Tue Sep 29 00:03:44 PDT 2026
https://github.com/MaskRay created https://github.com/llvm/llvm-project/pull/227203
PR #227026 marks every clobber def dead, but a flag output such as X86
`={@ccz}` is lowered to a glued `CopyFromReg $eflags` whose only
definition is the `~{flags}` clobber. With EXPENSIVE_CHECKS the verifier
reports "Using an undefined physical register" for the SETcc that reads
EFLAGS (bittest-intrin.ll, inline-asm-flag-output.ll, pr40737.ll).
Don't set the dead flag on a clobber that overlaps a physical register
read by a glued CopyFromReg.
Aided by Opus 5.5
>From ae8c5345b10581418b9d2e437bb27fda74c7b79e Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Mon, 28 Sep 2026 23:42:11 -0700
Subject: [PATCH] [SelectionDAG] Keep inline asm clobbers live when read by a
glued CopyFromReg
PR #227026 marks every clobber def dead, but a flag output such as X86
`={@ccz}` is lowered to a glued `CopyFromReg $eflags` whose only
definition is the `~{flags}` clobber. With EXPENSIVE_CHECKS the verifier
reports "Using an undefined physical register" for the SETcc that reads
EFLAGS (bittest-intrin.ll, inline-asm-flag-output.ll, pr40737.ll).
Don't set the dead flag on a clobber that overlaps a physical register
read by a glued CopyFromReg.
Aided by Opus 5.5
---
llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp | 18 +++++++++++++++++-
.../CodeGen/X86/inline-asm-default-clobbers.ll | 13 ++++++++++++-
2 files changed, 29 insertions(+), 2 deletions(-)
diff --git a/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp b/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp
index 6db23303bbc13a..0b32c30b446a26 100644
--- a/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp
@@ -1369,6 +1369,19 @@ EmitSpecialNode(SDNode *Node, bool IsClone, bool IsCloned,
// Remember registers that are part of early-clobber defs.
SmallVector<Register, 8> ECRegs;
+ // A glued CopyFromReg may read a clobbered register, e.g. a flag output
+ // like X86 "={@ccz}" reads EFLAGS defined only by "~{flags}".
+ SmallVector<Register, 2> GluedUses;
+ if (Node->getValueType(Node->getNumValues() - 1) == MVT::Glue) {
+ for (SDNode *G = Node->getGluedUser(); G; G = G->getGluedUser()) {
+ if (G->getOpcode() != ISD::CopyFromReg)
+ continue;
+ Register Reg = cast<RegisterSDNode>(G->getOperand(1))->getReg();
+ if (Reg.isPhysical())
+ GluedUses.push_back(Reg);
+ }
+ }
+
// Add all of the operand registers to the instruction.
for (unsigned i = InlineAsm::Op_FirstOperand; i != NumOps;) {
unsigned Flags = Node->getConstantOperandVal(i);
@@ -1393,8 +1406,11 @@ EmitSpecialNode(SDNode *Node, bool IsClone, bool IsCloned,
case InlineAsm::Kind::Clobber:
for (unsigned j = 0; j != NumVals; ++j, ++i) {
Register Reg = cast<RegisterSDNode>(Node->getOperand(i))->getReg();
+ bool IsDead = F.isClobberKind() && none_of(GluedUses, [&](Register U) {
+ return TRI->regsOverlap(U, Reg);
+ });
MIB.addReg(Reg, RegState::Define | RegState::EarlyClobber |
- getDeadRegState(F.isClobberKind()) |
+ getDeadRegState(IsDead) |
getImplRegState(Reg.isPhysical()));
ECRegs.push_back(Reg);
}
diff --git a/llvm/test/CodeGen/X86/inline-asm-default-clobbers.ll b/llvm/test/CodeGen/X86/inline-asm-default-clobbers.ll
index 7a23b6ec2e9e4e..61023b5e92e21c 100644
--- a/llvm/test/CodeGen/X86/inline-asm-default-clobbers.ll
+++ b/llvm/test/CodeGen/X86/inline-asm-default-clobbers.ll
@@ -1,8 +1,19 @@
-; RUN: llc < %s -mtriple=i686 -stop-after=finalize-isel | FileCheck %s
+; RUN: llc < %s -mtriple=i686 -stop-after=finalize-isel -verify-machineinstrs | FileCheck %s
+; CHECK-LABEL: name: foo
; CHECK: INLINEASM &"", sideeffect attdialect, clobber, implicit-def dead early-clobber $df, clobber, implicit-def dead early-clobber $fpsw, clobber, implicit-def dead early-clobber $eflags
define void @foo() {
entry:
call void asm sideeffect "", "~{dirflag},~{fpsr},~{flags}"()
ret void
}
+
+;; The flag output reads EFLAGS, which only the clobber defines.
+; CHECK-LABEL: name: flag_output
+; CHECK: INLINEASM &"", maystore attdialect, regdef:GR32, def %{{[0-9]+}}, clobber, implicit-def dead early-clobber $df, clobber, implicit-def dead early-clobber $fpsw, clobber, implicit-def early-clobber $eflags
+; CHECK-NEXT: SETCCr 4, implicit $eflags
+define i8 @flag_output() {
+entry:
+ %r = call i8 asm "", "={@ccz},~{dirflag},~{fpsr},~{flags}"()
+ ret i8 %r
+}
More information about the llvm-commits
mailing list