[llvm] cd4a743 - FastISel: Mark implicit physreg defs dead when emitting instructions (#227598)
via llvm-commits
llvm-commits at lists.llvm.org
Wed Sep 30 12:55:25 PDT 2026
Author: Matt Arsenault
Date: 2026-09-30T21:55:18+02:00
New Revision: cd4a743905b27afafc26915091b29137e9d0c385
URL: https://github.com/llvm/llvm-project/commit/cd4a743905b27afafc26915091b29137e9d0c385
DIFF: https://github.com/llvm/llvm-project/commit/cd4a743905b27afafc26915091b29137e9d0c385.diff
LOG: FastISel: Mark implicit physreg defs dead when emitting instructions (#227598)
fastEmitInst_* callers only use the explicit result, so the implicit
physical register defs of the emitted instruction are unused.
InstrEmitter would mark these as dead, but fast isel would end up
relying on
later dead flag computation in LiveVariables or RegAllocFast.
The X86 with-overflow lowering is the one caller that reads an implicit
def, so handle that separately.
Co-authored-by: Claude Opus 5 <noreply at anthropic.com>
Added:
llvm/test/CodeGen/X86/fast-isel-dead-implicit-defs.ll
Modified:
llvm/include/llvm/CodeGen/MachineInstr.h
llvm/lib/CodeGen/SelectionDAG/FastISel.cpp
llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp
llvm/lib/Target/X86/X86FastISel.cpp
llvm/test/CodeGen/X86/expand-large-fp-optnone.ll
llvm/test/CodeGen/X86/fast-isel-sext-dead-eflags.ll
Removed:
################################################################################
diff --git a/llvm/include/llvm/CodeGen/MachineInstr.h b/llvm/include/llvm/CodeGen/MachineInstr.h
index a18ee9bde66b7..f66dff6e5bb3b 100644
--- a/llvm/include/llvm/CodeGen/MachineInstr.h
+++ b/llvm/include/llvm/CodeGen/MachineInstr.h
@@ -1776,6 +1776,14 @@ class MachineInstr
LLVM_ABI void setPhysRegsDeadExcept(ArrayRef<Register> UsedRegs,
const TargetRegisterInfo &TRI);
+ /// Mark the implicit physreg defs named by the instruction description as
+ /// dead.
+ void setImplicitPhysRegDefsDead() {
+ unsigned Idx = getNumExplicitOperands();
+ for (unsigned E = Idx + MCID->implicit_defs().size(); Idx != E; ++Idx)
+ getOperand(Idx).setIsDead();
+ }
+
/// Return true if it is safe to move this instruction. If
/// SawStore is set to true, it means that there is a store (or call) between
/// the instruction's location and its intended destination.
diff --git a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp
index 67404a20c474e..4f39139070508 100644
--- a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp
@@ -1984,7 +1984,8 @@ Register FastISel::fastEmitInst_(unsigned MachineInstOpcode,
Register ResultReg = createResultReg(RC);
const MCInstrDesc &II = TII.get(MachineInstOpcode);
- BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD, II, ResultReg);
+ BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD, II, ResultReg)
+ ->setImplicitPhysRegDefsDead();
return ResultReg;
}
@@ -1996,7 +1997,9 @@ Register FastISel::fastEmitInst_r(unsigned MachineInstOpcode,
Op0 = constrainOperandRegClass(II, Op0, II.getNumDefs());
assert(II.getNumDefs() >= 1 && "instruction must define the result");
- BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD, II, ResultReg).addReg(Op0);
+ BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD, II, ResultReg)
+ .addReg(Op0)
+ ->setImplicitPhysRegDefsDead();
return ResultReg;
}
@@ -2013,7 +2016,8 @@ Register FastISel::fastEmitInst_rr(unsigned MachineInstOpcode,
assert(II.getNumDefs() >= 1 && "instruction must define the result");
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD, II, ResultReg)
.addReg(Op0)
- .addReg(Op1);
+ .addReg(Op1)
+ ->setImplicitPhysRegDefsDead();
return ResultReg;
}
@@ -2031,7 +2035,8 @@ Register FastISel::fastEmitInst_rrr(unsigned MachineInstOpcode,
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD, II, ResultReg)
.addReg(Op0)
.addReg(Op1)
- .addReg(Op2);
+ .addReg(Op2)
+ ->setImplicitPhysRegDefsDead();
return ResultReg;
}
@@ -2046,7 +2051,8 @@ Register FastISel::fastEmitInst_ri(unsigned MachineInstOpcode,
assert(II.getNumDefs() >= 1 && "instruction must define the result");
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD, II, ResultReg)
.addReg(Op0)
- .addImm(Imm);
+ .addImm(Imm)
+ ->setImplicitPhysRegDefsDead();
return ResultReg;
}
@@ -2062,7 +2068,8 @@ Register FastISel::fastEmitInst_rii(unsigned MachineInstOpcode,
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD, II, ResultReg)
.addReg(Op0)
.addImm(Imm1)
- .addImm(Imm2);
+ .addImm(Imm2)
+ ->setImplicitPhysRegDefsDead();
return ResultReg;
}
@@ -2075,7 +2082,8 @@ Register FastISel::fastEmitInst_f(unsigned MachineInstOpcode,
assert(II.getNumDefs() >= 1 && "instruction must define the result");
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD, II, ResultReg)
- .addFPImm(FPImm);
+ .addFPImm(FPImm)
+ ->setImplicitPhysRegDefsDead();
return ResultReg;
}
@@ -2092,7 +2100,8 @@ Register FastISel::fastEmitInst_rri(unsigned MachineInstOpcode,
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD, II, ResultReg)
.addReg(Op0)
.addReg(Op1)
- .addImm(Imm);
+ .addImm(Imm)
+ ->setImplicitPhysRegDefsDead();
return ResultReg;
}
@@ -2102,7 +2111,9 @@ Register FastISel::fastEmitInst_i(unsigned MachineInstOpcode,
const MCInstrDesc &II = TII.get(MachineInstOpcode);
assert(II.getNumDefs() >= 1 && "instruction must define the result");
- BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD, II, ResultReg).addImm(Imm);
+ BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD, II, ResultReg)
+ .addImm(Imm)
+ ->setImplicitPhysRegDefsDead();
return ResultReg;
}
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp
index db03f5874234c..0ca52cc691034 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp
@@ -1322,7 +1322,7 @@ bool WebAssemblyFastISel::selectBitCast(const Instruction *I) {
MachineBasicBlock::iterator Iter = FuncInfo.InsertPt;
--Iter;
assert(Iter->isBitcast());
- Iter->setPhysRegsDeadExcept(ArrayRef<Register>(), TRI);
+ Iter->setImplicitPhysRegDefsDead();
updateValueMap(I, Reg);
return true;
}
diff --git a/llvm/lib/Target/X86/X86FastISel.cpp b/llvm/lib/Target/X86/X86FastISel.cpp
index 68390010cf441..6f482038d3843 100644
--- a/llvm/lib/Target/X86/X86FastISel.cpp
+++ b/llvm/lib/Target/X86/X86FastISel.cpp
@@ -88,11 +88,36 @@ class X86FastISel final : public FastISel {
bool X86FastEmitExtend(ISD::NodeType Opc, EVT DstVT, Register Src, EVT SrcVT,
Register &ResultReg);
+ /// Emit \p Opc with the register operands \p Op0 and \p Op1, leaving its
+ /// implicit EFLAGS def live for a following SETcc. EFLAGS must be the
+ /// instruction's only implicit def.
+ Register X86FastEmitLiveEFLAGS_rr(unsigned Opc, const TargetRegisterClass *RC,
+ Register Op0, Register Op1);
+
+ /// Emit \p Opc with the register operand \p Op0 and the immediate \p Imm,
+ /// leaving its implicit EFLAGS def live for a following SETcc. EFLAGS must
+ /// be the instruction's only implicit def.
+ Register X86FastEmitLiveEFLAGS_ri(unsigned Opc, const TargetRegisterClass *RC,
+ Register Op0, uint64_t Imm);
+
/// Emit a MUL or IMUL of \p LHSReg and \p RHSReg. \p AccReg is the
/// accumulator the instruction implicitly reads and implicitly defines with
- /// the low half of the product.
+ /// the low half of the product. The high half is marked dead, as is EFLAGS
+ /// unless \p LiveEFLAGS is set for a following SETcc.
Register X86FastEmitMul(unsigned Opc, MVT VT, MCRegister AccReg,
- Register LHSReg, Register RHSReg);
+ Register LHSReg, Register RHSReg,
+ bool LiveEFLAGS = false);
+
+ /// Emit an add or sub of \p LHSReg and \p RHSReg, leaving the EFLAGS def
+ /// live for a following SETcc.
+ Register X86FastEmitAddSub_rr(unsigned BaseOpc, MVT VT, Register LHSReg,
+ Register RHSReg);
+
+ /// Emit an add or sub of \p LHSReg and \p CI, leaving the EFLAGS def live
+ /// for a following SETcc reading \p CondCode. Returns an invalid register
+ /// if the immediate form does not apply.
+ Register X86FastEmitAddSub_ri(unsigned BaseOpc, MVT VT, Register LHSReg,
+ const ConstantInt *CI, unsigned CondCode);
bool X86SelectAddress(const Value *V, X86AddressMode &AM);
bool X86SelectCallAddress(const Value *V, X86AddressMode &AM);
@@ -719,8 +744,44 @@ bool X86FastISel::X86FastEmitExtend(ISD::NodeType Opc, EVT DstVT, Register Src,
return true;
}
+Register X86FastISel::X86FastEmitLiveEFLAGS_rr(unsigned Opc,
+ const TargetRegisterClass *RC,
+ Register Op0, Register Op1) {
+ const MCInstrDesc &II = TII.get(Opc);
+ assert(II.getNumDefs() >= 1 && "instruction must define the result");
+ assert(II.implicit_defs().size() == 1 &&
+ II.implicit_defs()[0] == X86::EFLAGS && "unexpected implicit def");
+
+ Register ResultReg = createResultReg(RC);
+ Op0 = constrainOperandRegClass(II, Op0, II.getNumDefs());
+ Op1 = constrainOperandRegClass(II, Op1, II.getNumDefs() + 1);
+
+ BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD, II, ResultReg)
+ .addReg(Op0)
+ .addReg(Op1);
+ return ResultReg;
+}
+
+Register X86FastISel::X86FastEmitLiveEFLAGS_ri(unsigned Opc,
+ const TargetRegisterClass *RC,
+ Register Op0, uint64_t Imm) {
+ const MCInstrDesc &II = TII.get(Opc);
+ assert(II.getNumDefs() >= 1 && "instruction must define the result");
+ assert(II.implicit_defs().size() == 1 &&
+ II.implicit_defs()[0] == X86::EFLAGS && "unexpected implicit def");
+
+ Register ResultReg = createResultReg(RC);
+ Op0 = constrainOperandRegClass(II, Op0, II.getNumDefs());
+
+ BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD, II, ResultReg)
+ .addReg(Op0)
+ .addImm(Imm);
+ return ResultReg;
+}
+
Register X86FastISel::X86FastEmitMul(unsigned Opc, MVT VT, MCRegister AccReg,
- Register LHSReg, Register RHSReg) {
+ Register LHSReg, Register RHSReg,
+ bool LiveEFLAGS) {
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD, TII.get(TargetOpcode::COPY),
AccReg)
.addReg(LHSReg);
@@ -729,7 +790,25 @@ Register X86FastISel::X86FastEmitMul(unsigned Opc, MVT VT, MCRegister AccReg,
Register ResultReg = createResultReg(TLI.getRegClassFor(VT));
RHSReg = constrainOperandRegClass(II, RHSReg, II.getNumDefs());
- BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD, II).addReg(RHSReg);
+ MachineInstrBuilder MIB =
+ BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD, II).addReg(RHSReg);
+
+ // Operand 0 is the explicit source, followed by the implicit defs. MUL8r and
+ // IMUL8r define AL, EFLAGS and AX, where AX overlaps the AL result. The wider
+ // forms define the accumulator, the separate high half register and EFLAGS.
+ assert(MIB->getOperand(1).getReg() == AccReg && "unexpected operand order");
+ if (VT == MVT::i8) {
+ assert(MIB->getOperand(2).getReg() == X86::EFLAGS &&
+ "unexpected operand order");
+ if (!LiveEFLAGS)
+ MIB.setOperandDead(2);
+ } else {
+ assert(MIB->getOperand(3).getReg() == X86::EFLAGS &&
+ "unexpected operand order");
+ MIB.setOperandDead(2);
+ if (!LiveEFLAGS)
+ MIB.setOperandDead(3);
+ }
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD, TII.get(TargetOpcode::COPY),
ResultReg)
.addReg(AccReg);
@@ -2682,6 +2761,54 @@ bool X86FastISel::TryEmitSmallMemcpy(X86AddressMode DestAM,
return true;
}
+Register X86FastISel::X86FastEmitAddSub_rr(unsigned BaseOpc, MVT VT,
+ Register LHSReg, Register RHSReg) {
+ static const uint16_t Opc[2][2][4] = {
+ {{X86::ADD8rr, X86::ADD16rr, X86::ADD32rr, X86::ADD64rr},
+ {X86::ADD8rr_ND, X86::ADD16rr_ND, X86::ADD32rr_ND, X86::ADD64rr_ND}},
+ {{X86::SUB8rr, X86::SUB16rr, X86::SUB32rr, X86::SUB64rr},
+ {X86::SUB8rr_ND, X86::SUB16rr_ND, X86::SUB32rr_ND, X86::SUB64rr_ND}}};
+
+ bool IsSub = BaseOpc == ISD::SUB;
+ unsigned TypeIdx = VT.SimpleTy - MVT::i8;
+ return X86FastEmitLiveEFLAGS_rr(Opc[IsSub][Subtarget->hasNDD()][TypeIdx],
+ TLI.getRegClassFor(VT), LHSReg, RHSReg);
+}
+
+Register X86FastISel::X86FastEmitAddSub_ri(unsigned BaseOpc, MVT VT,
+ Register LHSReg,
+ const ConstantInt *CI,
+ unsigned CondCode) {
+ bool IsSub = BaseOpc == ISD::SUB;
+ unsigned TypeIdx = VT.SimpleTy - MVT::i8;
+
+ if (CI->isOne() && CondCode == X86::COND_O) {
+ // We can use INC/DEC.
+ static const uint16_t IncDecOpc[2][4] = {
+ {X86::INC8r, X86::INC16r, X86::INC32r, X86::INC64r},
+ {X86::DEC8r, X86::DEC16r, X86::DEC32r, X86::DEC64r}};
+
+ Register ResultReg = createResultReg(TLI.getRegClassFor(VT));
+ BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD,
+ TII.get(IncDecOpc[IsSub][TypeIdx]), ResultReg)
+ .addReg(LHSReg);
+ return ResultReg;
+ }
+
+ if (VT == MVT::i64 && !isInt<32>(CI->getSExtValue()))
+ return Register();
+
+ static const uint16_t Opc[2][2][4] = {
+ {{X86::ADD8ri, X86::ADD16ri, X86::ADD32ri, X86::ADD64ri32},
+ {X86::ADD8ri_ND, X86::ADD16ri_ND, X86::ADD32ri_ND, X86::ADD64ri32_ND}},
+ {{X86::SUB8ri, X86::SUB16ri, X86::SUB32ri, X86::SUB64ri32},
+ {X86::SUB8ri_ND, X86::SUB16ri_ND, X86::SUB32ri_ND, X86::SUB64ri32_ND}}};
+
+ return X86FastEmitLiveEFLAGS_ri(Opc[IsSub][Subtarget->hasNDD()][TypeIdx],
+ TLI.getRegClassFor(VT), LHSReg,
+ CI->getZExtValue());
+}
+
bool X86FastISel::fastLowerIntrinsicCall(const IntrinsicInst *II) {
// FIXME: Handle more intrinsics.
switch (II->getIntrinsicID()) {
@@ -2924,54 +3051,45 @@ bool X86FastISel::fastLowerIntrinsicCall(const IntrinsicInst *II) {
if (!LHSReg)
return false;
+ bool IsAddSub = BaseOpc == ISD::ADD || BaseOpc == ISD::SUB;
+
Register ResultReg;
// Check if we have an immediate version.
- if (const auto *CI = dyn_cast<ConstantInt>(RHS)) {
- static const uint16_t Opc[2][4] = {
- { X86::INC8r, X86::INC16r, X86::INC32r, X86::INC64r },
- { X86::DEC8r, X86::DEC16r, X86::DEC32r, X86::DEC64r }
- };
-
- if (CI->isOne() && (BaseOpc == ISD::ADD || BaseOpc == ISD::SUB) &&
- CondCode == X86::COND_O) {
- // We can use INC/DEC.
- ResultReg = createResultReg(TLI.getRegClassFor(VT));
- bool IsDec = BaseOpc == ISD::SUB;
- BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD,
- TII.get(Opc[IsDec][VT.SimpleTy-MVT::i8]), ResultReg)
- .addReg(LHSReg);
- } else
- ResultReg = fastEmit_ri(VT, VT, BaseOpc, LHSReg, CI->getZExtValue());
- }
+ if (const auto *CI = dyn_cast<ConstantInt>(RHS); CI && IsAddSub)
+ ResultReg = X86FastEmitAddSub_ri(BaseOpc, VT, LHSReg, CI, CondCode);
Register RHSReg;
if (!ResultReg) {
RHSReg = getRegForValue(RHS);
if (!RHSReg)
return false;
- ResultReg = fastEmit_rr(VT, VT, BaseOpc, LHSReg, RHSReg);
+
+ if (IsAddSub)
+ ResultReg = X86FastEmitAddSub_rr(BaseOpc, VT, LHSReg, RHSReg);
}
- // FastISel doesn't have a pattern for all X86::MUL*r and X86::IMUL*r. Emit
- // it manually.
- if (BaseOpc == X86ISD::UMUL && !ResultReg) {
+ if (BaseOpc == X86ISD::UMUL) {
static const uint16_t MULOpc[] =
{ X86::MUL8r, X86::MUL16r, X86::MUL32r, X86::MUL64r };
static const MCPhysReg Reg[] = { X86::AL, X86::AX, X86::EAX, X86::RAX };
// The first operand goes in RAX, which is an implicit input to the
// X86::MUL*r instruction.
ResultReg = X86FastEmitMul(MULOpc[VT.SimpleTy - MVT::i8], VT,
- Reg[VT.SimpleTy - MVT::i8], LHSReg, RHSReg);
- } else if (BaseOpc == X86ISD::SMUL && !ResultReg) {
+ Reg[VT.SimpleTy - MVT::i8], LHSReg, RHSReg,
+ /*LiveEFLAGS=*/true);
+ } else if (BaseOpc == X86ISD::SMUL) {
static const uint16_t MULOpc[] =
{ X86::IMUL8r, X86::IMUL16rr, X86::IMUL32rr, X86::IMUL64rr };
if (VT == MVT::i8) {
// The first operand goes in AL, which is an implicit input to the
// X86::IMUL8r instruction.
- ResultReg = X86FastEmitMul(MULOpc[0], VT, X86::AL, LHSReg, RHSReg);
- } else
- ResultReg = fastEmitInst_rr(MULOpc[VT.SimpleTy-MVT::i8],
- TLI.getRegClassFor(VT), LHSReg, RHSReg);
+ ResultReg = X86FastEmitMul(MULOpc[0], VT, X86::AL, LHSReg, RHSReg,
+ /*LiveEFLAGS=*/true);
+ } else {
+ ResultReg =
+ X86FastEmitLiveEFLAGS_rr(MULOpc[VT.SimpleTy - MVT::i8],
+ TLI.getRegClassFor(VT), LHSReg, RHSReg);
+ }
}
if (!ResultReg)
diff --git a/llvm/test/CodeGen/X86/expand-large-fp-optnone.ll b/llvm/test/CodeGen/X86/expand-large-fp-optnone.ll
index 16fe756bfc4e6..0df3c0b03dc33 100644
--- a/llvm/test/CodeGen/X86/expand-large-fp-optnone.ll
+++ b/llvm/test/CodeGen/X86/expand-large-fp-optnone.ll
@@ -220,8 +220,8 @@ define double @main(i224 %0) #0 {
; CHECK-NEXT: shll $20, %r10d
; CHECK-NEXT: addl $1072693248, %r10d # imm = 0x3FF00000
; CHECK-NEXT: andl $1048575, %eax # imm = 0xFFFFF
+; CHECK-NEXT: orl %r10d, %r9d
; CHECK-NEXT: orl %r9d, %eax
-; CHECK-NEXT: orl %r10d, %eax
; CHECK-NEXT: movl %eax, %eax
; CHECK-NEXT: shlq $32, %rax
; CHECK-NEXT: movabsq $4294967295, %rcx # imm = 0xFFFFFFFF
diff --git a/llvm/test/CodeGen/X86/fast-isel-dead-implicit-defs.ll b/llvm/test/CodeGen/X86/fast-isel-dead-implicit-defs.ll
new file mode 100644
index 0000000000000..aa243d6d80b75
--- /dev/null
+++ b/llvm/test/CodeGen/X86/fast-isel-dead-implicit-defs.ll
@@ -0,0 +1,65 @@
+; NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mtriple=x86_64-- -fast-isel -fast-isel-abort=1 -stop-after=finalize-isel < %s | FileCheck %s
+
+; The implicit $eflags def is unused.
+define i32 @add(i32 %a, i32 %b) {
+ ; CHECK-LABEL: name: add
+ ; CHECK: bb.0 (%ir-block.0):
+ ; CHECK-NEXT: liveins: $edi, $esi
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: [[COPY:%[0-9]+]]:gr32 = COPY $esi
+ ; CHECK-NEXT: [[COPY1:%[0-9]+]]:gr32 = COPY $edi
+ ; CHECK-NEXT: [[COPY2:%[0-9]+]]:gr32 = COPY killed [[COPY1]]
+ ; CHECK-NEXT: [[COPY3:%[0-9]+]]:gr32 = COPY killed [[COPY]]
+ ; CHECK-NEXT: [[ADD32rr:%[0-9]+]]:gr32 = ADD32rr [[COPY2]], [[COPY3]], implicit-def dead $eflags
+ ; CHECK-NEXT: $eax = COPY [[ADD32rr]]
+ ; CHECK-NEXT: RET64 implicit $eax
+ %r = add i32 %a, %b
+ ret i32 %r
+}
+
+; The implicit $eflags def is read by the SETcc.
+define i32 @uaddo(i32 %a, i32 %b) {
+ ; CHECK-LABEL: name: uaddo
+ ; CHECK: bb.0 (%ir-block.0):
+ ; CHECK-NEXT: liveins: $edi, $esi
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: [[COPY:%[0-9]+]]:gr32 = COPY $esi
+ ; CHECK-NEXT: [[COPY1:%[0-9]+]]:gr32 = COPY $edi
+ ; CHECK-NEXT: [[COPY2:%[0-9]+]]:gr32 = COPY killed [[COPY1]]
+ ; CHECK-NEXT: [[COPY3:%[0-9]+]]:gr32 = COPY killed [[COPY]]
+ ; CHECK-NEXT: [[ADD32rr:%[0-9]+]]:gr32 = ADD32rr [[COPY2]], [[COPY3]], implicit-def $eflags
+ ; CHECK-NEXT: [[SETCCr:%[0-9]+]]:gr8 = SETCCr 2, implicit $eflags
+ ; CHECK-NEXT: [[AND8ri:%[0-9]+]]:gr8 = AND8ri [[SETCCr]], 1, implicit-def dead $eflags
+ ; CHECK-NEXT: [[MOVZX32rr8_:%[0-9]+]]:gr32 = MOVZX32rr8 [[AND8ri]]
+ ; CHECK-NEXT: $eax = COPY [[MOVZX32rr8_]]
+ ; CHECK-NEXT: RET64 implicit $eax
+ %t = call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 %a, i32 %b)
+ %o = extractvalue { i32, i1 } %t, 1
+ %z = zext i1 %o to i32
+ ret i32 %z
+}
+
+; $edx is unused, but the implicit $eflags def is read by the SETcc.
+define i32 @umulo(i32 %a, i32 %b) {
+ ; CHECK-LABEL: name: umulo
+ ; CHECK: bb.0 (%ir-block.0):
+ ; CHECK-NEXT: liveins: $edi, $esi
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: [[COPY:%[0-9]+]]:gr32 = COPY $esi
+ ; CHECK-NEXT: [[COPY1:%[0-9]+]]:gr32 = COPY $edi
+ ; CHECK-NEXT: [[COPY2:%[0-9]+]]:gr32 = COPY killed [[COPY1]]
+ ; CHECK-NEXT: [[COPY3:%[0-9]+]]:gr32 = COPY killed [[COPY]]
+ ; CHECK-NEXT: $eax = COPY [[COPY2]]
+ ; CHECK-NEXT: MUL32r [[COPY3]], implicit-def $eax, implicit-def dead $edx, implicit-def $eflags, implicit $eax
+ ; CHECK-NEXT: [[COPY4:%[0-9]+]]:gr32 = COPY $eax
+ ; CHECK-NEXT: [[SETCCr:%[0-9]+]]:gr8 = SETCCr 2, implicit $eflags
+ ; CHECK-NEXT: [[AND8ri:%[0-9]+]]:gr8 = AND8ri [[SETCCr]], 1, implicit-def dead $eflags
+ ; CHECK-NEXT: [[MOVZX32rr8_:%[0-9]+]]:gr32 = MOVZX32rr8 [[AND8ri]]
+ ; CHECK-NEXT: $eax = COPY [[MOVZX32rr8_]]
+ ; CHECK-NEXT: RET64 implicit $eax
+ %t = call { i32, i1 } @llvm.umul.with.overflow.i32(i32 %a, i32 %b)
+ %o = extractvalue { i32, i1 } %t, 1
+ %z = zext i1 %o to i32
+ ret i32 %z
+}
diff --git a/llvm/test/CodeGen/X86/fast-isel-sext-dead-eflags.ll b/llvm/test/CodeGen/X86/fast-isel-sext-dead-eflags.ll
index 727ae4edc4568..85ea89461c124 100644
--- a/llvm/test/CodeGen/X86/fast-isel-sext-dead-eflags.ll
+++ b/llvm/test/CodeGen/X86/fast-isel-sext-dead-eflags.ll
@@ -8,7 +8,7 @@ define i32 @sext_i1_to_i32(i1 %x) {
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: [[COPY:%[0-9]+]]:gr32 = COPY $edi
; CHECK-NEXT: [[COPY1:%[0-9]+]]:gr8 = COPY [[COPY]].sub_8bit
- ; CHECK-NEXT: [[AND8ri:%[0-9]+]]:gr8 = AND8ri [[COPY1]], 1, implicit-def $eflags
+ ; CHECK-NEXT: [[AND8ri:%[0-9]+]]:gr8 = AND8ri [[COPY1]], 1, implicit-def dead $eflags
; CHECK-NEXT: [[NEG8r:%[0-9]+]]:gr8 = NEG8r [[AND8ri]], implicit-def dead $eflags
; CHECK-NEXT: [[MOVSX32rr8_:%[0-9]+]]:gr32 = MOVSX32rr8 [[NEG8r]]
; CHECK-NEXT: $eax = COPY [[MOVSX32rr8_]]
More information about the llvm-commits
mailing list