[llvm] [SelectionDAG] Preserver poison for abs INT_MIN lowering (PR #183851)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Mar 24 16:54:25 PDT 2026
https://github.com/aryanmagoon updated https://github.com/llvm/llvm-project/pull/183851
>From f49a30f0b5ee3f531a7a8a7fafcd1314cd0b23e1 Mon Sep 17 00:00:00 2001
From: aryanmagoon <amagoon at nvidia.com>
Date: Fri, 27 Feb 2026 19:13:40 +0000
Subject: [PATCH 1/6] SelectionDAG: Preserve poison for abs INT_MIN lowering.
Update affected CodeGen lit tests and added a new lit test for this behavior.
---
llvm/include/llvm/CodeGen/ISDOpcodes.h | 4 +
llvm/include/llvm/CodeGen/SDPatternMatch.h | 5 +-
.../include/llvm/Target/TargetSelectionDAG.td | 5 +-
llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp | 65 +++++++++-
llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp | 1 +
.../SelectionDAG/LegalizeIntegerTypes.cpp | 15 ++-
.../SelectionDAG/LegalizeVectorOps.cpp | 2 +
.../SelectionDAG/LegalizeVectorTypes.cpp | 3 +
.../lib/CodeGen/SelectionDAG/SelectionDAG.cpp | 20 ++-
.../SelectionDAG/SelectionDAGBuilder.cpp | 5 +-
.../SelectionDAG/SelectionDAGDumper.cpp | 1 +
.../CodeGen/SelectionDAG/TargetLowering.cpp | 10 ++
llvm/lib/CodeGen/TargetLoweringBase.cpp | 3 +
.../Target/AArch64/AArch64ISelLowering.cpp | 6 +-
llvm/lib/Target/NVPTX/NVPTXISelLowering.cpp | 17 ++-
llvm/lib/Target/NVPTX/NVPTXInstrInfo.td | 5 +-
llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 16 ++-
llvm/test/CodeGen/NVPTX/idioms.ll | 21 +--
llvm/test/CodeGen/X86/icmp-abs-C.ll | 120 ++++++++++--------
.../test/CodeGen/X86/icmp-pow2-logic-npow2.ll | 33 +++--
20 files changed, 254 insertions(+), 103 deletions(-)
diff --git a/llvm/include/llvm/CodeGen/ISDOpcodes.h b/llvm/include/llvm/CodeGen/ISDOpcodes.h
index b8c6788e0bc03..f3cc61259031d 100644
--- a/llvm/include/llvm/CodeGen/ISDOpcodes.h
+++ b/llvm/include/llvm/CodeGen/ISDOpcodes.h
@@ -746,6 +746,10 @@ enum NodeType {
/// is performed.
ABS,
+ /// ABS with a poison result for INT_MIN. This corresponds to
+ /// llvm.abs(x, true) where the "int min is poison" flag is set.
+ ABS_MIN_POISON,
+
/// Shift and rotation operations. After legalization, the type of the
/// shift amount is known to be TLI.getShiftAmountTy(). Before legalization
/// the shift amount can be any type, but care must be taken to ensure it is
diff --git a/llvm/include/llvm/CodeGen/SDPatternMatch.h b/llvm/include/llvm/CodeGen/SDPatternMatch.h
index b41443f85c3a9..a3cc419404759 100644
--- a/llvm/include/llvm/CodeGen/SDPatternMatch.h
+++ b/llvm/include/llvm/CodeGen/SDPatternMatch.h
@@ -1073,8 +1073,9 @@ template <typename Opnd> inline UnaryOpc_match<Opnd> m_Trunc(const Opnd &Op) {
return UnaryOpc_match<Opnd>(ISD::TRUNCATE, Op);
}
-template <typename Opnd> inline UnaryOpc_match<Opnd> m_Abs(const Opnd &Op) {
- return UnaryOpc_match<Opnd>(ISD::ABS, Op);
+template <typename Opnd> inline auto m_Abs(const Opnd &Op) {
+ return m_AnyOf(UnaryOpc_match<Opnd>(ISD::ABS, Op),
+ UnaryOpc_match<Opnd>(ISD::ABS_MIN_POISON, Op));
}
template <typename Opnd> inline UnaryOpc_match<Opnd> m_FAbs(const Opnd &Op) {
diff --git a/llvm/include/llvm/Target/TargetSelectionDAG.td b/llvm/include/llvm/Target/TargetSelectionDAG.td
index d689b3c1beda9..1e4940d4c1f8d 100644
--- a/llvm/include/llvm/Target/TargetSelectionDAG.td
+++ b/llvm/include/llvm/Target/TargetSelectionDAG.td
@@ -519,8 +519,9 @@ def sext_inreg : SDNode<"ISD::SIGN_EXTEND_INREG", SDTExtInreg>;
def sext_invec : SDNode<"ISD::SIGN_EXTEND_VECTOR_INREG", SDTExtInvec>;
def zext_invec : SDNode<"ISD::ZERO_EXTEND_VECTOR_INREG", SDTExtInvec>;
-def abs : SDNode<"ISD::ABS" , SDTIntUnaryOp>;
-def bitreverse : SDNode<"ISD::BITREVERSE" , SDTIntUnaryOp>;
+def abs : SDNode<"ISD::ABS" , SDTIntUnaryOp>;
+def abs_min_poison : SDNode<"ISD::ABS_MIN_POISON", SDTIntUnaryOp>;
+def bitreverse : SDNode<"ISD::BITREVERSE" , SDTIntUnaryOp>;
def bswap : SDNode<"ISD::BSWAP" , SDTIntUnaryOp>;
def ctlz : SDNode<"ISD::CTLZ" , SDTIntBitCountUnaryOp>;
def cttz : SDNode<"ISD::CTTZ" , SDTIntBitCountUnaryOp>;
diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
index 41e77e044d8a9..f4a0871e716d9 100644
--- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
@@ -477,6 +477,7 @@ namespace {
SDValue visitSHLSAT(SDNode *N);
SDValue visitRotate(SDNode *N);
SDValue visitABS(SDNode *N);
+ SDValue visitABS_MIN_POISON(SDNode *N);
SDValue visitCLMUL(SDNode *N);
SDValue visitBSWAP(SDNode *N);
SDValue visitBITREVERSE(SDNode *N);
@@ -1983,6 +1984,7 @@ SDValue DAGCombiner::visit(SDNode *N) {
case ISD::SSHLSAT:
case ISD::USHLSAT: return visitSHLSAT(N);
case ISD::ABS: return visitABS(N);
+ case ISD::ABS_MIN_POISON: return visitABS_MIN_POISON(N);
case ISD::CLMUL:
case ISD::CLMULR:
case ISD::CLMULH: return visitCLMUL(N);
@@ -4263,8 +4265,9 @@ SDValue DAGCombiner::visitSUB(SDNode *N) {
}
// Convert 0 - abs(x).
- if (N1.getOpcode() == ISD::ABS && N1.hasOneUse() &&
- !TLI.isOperationLegalOrCustom(ISD::ABS, VT))
+ if ((N1.getOpcode() == ISD::ABS || N1.getOpcode() == ISD::ABS_MIN_POISON) &&
+ N1.hasOneUse() &&
+ !TLI.isOperationLegalOrCustom(N1.getOpcode(), VT))
if (SDValue Result = TLI.expandABS(N1.getNode(), DAG, true))
return Result;
@@ -11916,7 +11919,8 @@ SDValue DAGCombiner::visitABS(SDNode *N) {
if (SDValue C = DAG.FoldConstantArithmetic(ISD::ABS, DL, VT, {N0}))
return C;
// fold (abs (abs x)) -> (abs x)
- if (N0.getOpcode() == ISD::ABS)
+ // fold (abs (abs_min_poison x)) -> (abs x)
+ if (N0.getOpcode() == ISD::ABS || N0.getOpcode() == ISD::ABS_MIN_POISON)
return N0;
// fold (abs x) -> x iff not-negative
if (DAG.SignBitIsZero(N0))
@@ -11942,6 +11946,55 @@ SDValue DAGCombiner::visitABS(SDNode *N) {
return SDValue();
}
+SDValue DAGCombiner::visitABS_MIN_POISON(SDNode *N) {
+ SDValue N0 = N->getOperand(0);
+ EVT VT = N->getValueType(0);
+ SDLoc DL(N);
+
+ // fold (abs_min_poison c1) -> c2 (or poison if c1 == INT_MIN)
+ if (SDValue C =
+ DAG.FoldConstantArithmetic(ISD::ABS_MIN_POISON, DL, VT, {N0}))
+ return C;
+ // fold (abs_min_poison (abs_min_poison x)) -> (abs_min_poison x)
+ // fold (abs_min_poison (abs x)) -> (abs x)
+ if (N0.getOpcode() == ISD::ABS_MIN_POISON || N0.getOpcode() == ISD::ABS)
+ return N0;
+ // fold (abs_min_poison (freeze (abs x))) -> (freeze (abs x))
+ // fold (abs_min_poison (freeze (abs_min_poison x))) -> (freeze (abs_min_poison x))
+ // Valid because: for x != INT_MIN both sides equal abs(x); for x == INT_MIN
+ // both forms produce a non-deterministic but well-defined value since freeze
+ // already consumed the poison.
+ if (N0.getOpcode() == ISD::FREEZE) {
+ unsigned InnerOpc = N0.getOperand(0).getOpcode();
+ if (InnerOpc == ISD::ABS || InnerOpc == ISD::ABS_MIN_POISON)
+ return N0;
+ }
+ // fold (abs_min_poison x) -> x iff not-negative
+ if (DAG.SignBitIsZero(N0))
+ return N0;
+
+ if (SDValue ABD = foldABSToABD(N, DL))
+ return ABD;
+
+ // fold (abs_min_poison (sign_extend_inreg x)) ->
+ // (zero_extend (abs_min_poison (truncate x)))
+ // iff zero_extend/truncate are free.
+ if (N0.getOpcode() == ISD::SIGN_EXTEND_INREG) {
+ EVT ExtVT = cast<VTSDNode>(N0.getOperand(1))->getVT();
+ if (TLI.isTruncateFree(VT, ExtVT) && TLI.isZExtFree(ExtVT, VT) &&
+ TLI.isTypeDesirableForOp(ISD::ABS, ExtVT) &&
+ hasOperation(ISD::ABS, ExtVT)) {
+ return DAG.getNode(
+ ISD::ZERO_EXTEND, DL, VT,
+ DAG.getNode(ISD::ABS_MIN_POISON, DL, ExtVT,
+ DAG.getNode(ISD::TRUNCATE, DL, ExtVT,
+ N0.getOperand(0))));
+ }
+ }
+
+ return SDValue();
+}
+
SDValue DAGCombiner::visitCLMUL(SDNode *N) {
unsigned Opcode = N->getOpcode();
SDValue N0 = N->getOperand(0);
@@ -15322,7 +15375,9 @@ static SDValue widenAbs(SDNode *Extend, SelectionDAG &DAG) {
return SDValue();
SDValue Abs = Extend->getOperand(0);
- if (Abs.getOpcode() != ISD::ABS || !Abs.hasOneUse())
+ unsigned AbsOpc = Abs.getOpcode();
+ if ((AbsOpc != ISD::ABS && AbsOpc != ISD::ABS_MIN_POISON) ||
+ !Abs.hasOneUse())
return SDValue();
EVT AbsVT = Abs.getValueType();
@@ -15335,7 +15390,7 @@ static SDValue widenAbs(SDNode *Extend, SelectionDAG &DAG) {
SDValue SExt =
DAG.getNode(ISD::SIGN_EXTEND, SDLoc(Abs), LegalVT, Abs.getOperand(0));
- SDValue NewAbs = DAG.getNode(ISD::ABS, SDLoc(Abs), LegalVT, SExt);
+ SDValue NewAbs = DAG.getNode(AbsOpc, SDLoc(Abs), LegalVT, SExt);
return DAG.getZExtOrTrunc(NewAbs, SDLoc(Extend), VT);
}
diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp
index eb20e7982a102..a28739d210933 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp
@@ -3223,6 +3223,7 @@ bool SelectionDAGLegalize::ExpandNode(SDNode *Node) {
bool NeedInvert;
switch (Node->getOpcode()) {
case ISD::ABS:
+ case ISD::ABS_MIN_POISON:
if ((Tmp1 = TLI.expandABS(Node, DAG)))
Results.push_back(Tmp1);
break;
diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp
index 0d5cba405d6e3..a2dd494086c68 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp
@@ -278,7 +278,8 @@ void DAGTypeLegalizer::PromoteIntegerResult(SDNode *N, unsigned ResNo) {
case ISD::UDIVFIX:
case ISD::UDIVFIXSAT: Res = PromoteIntRes_DIVFIX(N); break;
- case ISD::ABS: Res = PromoteIntRes_ABS(N); break;
+ case ISD::ABS:
+ case ISD::ABS_MIN_POISON: Res = PromoteIntRes_ABS(N); break;
case ISD::ATOMIC_LOAD:
Res = PromoteIntRes_Atomic0(cast<AtomicSDNode>(N)); break;
@@ -1871,6 +1872,7 @@ SDValue DAGTypeLegalizer::PromoteIntRes_SADDSUBO_CARRY(SDNode *N,
}
SDValue DAGTypeLegalizer::PromoteIntRes_ABS(SDNode *N) {
+ unsigned Opc = N->getOpcode();
EVT OVT = N->getValueType(0);
EVT NVT = TLI.getTypeToTransformTo(*DAG.getContext(), OVT);
@@ -1879,13 +1881,14 @@ SDValue DAGTypeLegalizer::PromoteIntRes_ABS(SDNode *N) {
// in sra+xor+sub expansion.
if (!OVT.isVector() &&
!TLI.isOperationLegalOrCustomOrPromote(ISD::ABS, NVT) &&
+ !TLI.isOperationLegalOrCustomOrPromote(ISD::ABS_MIN_POISON, NVT) &&
!TLI.isOperationLegal(ISD::SMAX, NVT)) {
if (SDValue Res = TLI.expandABS(N, DAG))
return DAG.getNode(ISD::ANY_EXTEND, SDLoc(N), NVT, Res);
}
SDValue Op0 = SExtPromotedInteger(N->getOperand(0));
- return DAG.getNode(ISD::ABS, SDLoc(N), Op0.getValueType(), Op0);
+ return DAG.getNode(Opc, SDLoc(N), Op0.getValueType(), Op0);
}
SDValue DAGTypeLegalizer::PromoteIntRes_XMULO(SDNode *N, unsigned ResNo) {
@@ -3061,7 +3064,8 @@ void DAGTypeLegalizer::ExpandIntegerResult(SDNode *N, unsigned ResNo) {
case ISD::BSWAP: ExpandIntRes_BSWAP(N, Lo, Hi); break;
case ISD::PARITY: ExpandIntRes_PARITY(N, Lo, Hi); break;
case ISD::Constant: ExpandIntRes_Constant(N, Lo, Hi); break;
- case ISD::ABS: ExpandIntRes_ABS(N, Lo, Hi); break;
+ case ISD::ABS:
+ case ISD::ABS_MIN_POISON: ExpandIntRes_ABS(N, Lo, Hi); break;
case ISD::ABDS:
case ISD::ABDU: ExpandIntRes_ABD(N, Lo, Hi); break;
case ISD::CTLZ_ZERO_UNDEF:
@@ -4091,7 +4095,10 @@ void DAGTypeLegalizer::ExpandIntRes_ABS(SDNode *N, SDValue &Lo, SDValue &Hi) {
EVT NVT = Lo.getValueType();
// If the upper half is all sign bits, then we can perform the ABS on the
- // lower half and zero-extend.
+ // lower half and zero-extend. We must use ISD::ABS here (not ABS_MIN_POISON)
+ // because the original poison contract is for INT_MIN of the wider type,
+ // but the lower half may be INT_MIN of the narrower type for a valid
+ // (non-INT_MIN) input of the original type.
if (DAG.ComputeNumSignBits(N0) > NVT.getScalarSizeInBits()) {
Lo = DAG.getNode(ISD::ABS, dl, NVT, Lo);
Hi = DAG.getConstant(0, dl, NVT);
diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorOps.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorOps.cpp
index 0b1d5bfd078d8..f082a76bc2a4e 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorOps.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorOps.cpp
@@ -376,6 +376,7 @@ SDValue VectorLegalizer::LegalizeOp(SDValue Op) {
case ISD::ROTL:
case ISD::ROTR:
case ISD::ABS:
+ case ISD::ABS_MIN_POISON:
case ISD::ABDS:
case ISD::ABDU:
case ISD::AVGCEILS:
@@ -1075,6 +1076,7 @@ void VectorLegalizer::Expand(SDNode *Node, SmallVectorImpl<SDValue> &Results) {
ExpandSETCC(Node, Results);
return;
case ISD::ABS:
+ case ISD::ABS_MIN_POISON:
if (SDValue Expanded = TLI.expandABS(Node, DAG)) {
Results.push_back(Expanded);
return;
diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp
index aeb9d4d7bdc1d..e11a6c508dc12 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp
@@ -89,6 +89,7 @@ void DAGTypeLegalizer::ScalarizeVectorResult(SDNode *N, unsigned ResNo) {
R = ScalarizeVecRes_VecInregOp(N);
break;
case ISD::ABS:
+ case ISD::ABS_MIN_POISON:
case ISD::ANY_EXTEND:
case ISD::BITREVERSE:
case ISD::BSWAP:
@@ -1311,6 +1312,7 @@ void DAGTypeLegalizer::SplitVectorResult(SDNode *N, unsigned ResNo) {
break;
case ISD::ABS:
+ case ISD::ABS_MIN_POISON:
case ISD::VP_ABS:
case ISD::BITREVERSE:
case ISD::VP_BITREVERSE:
@@ -5190,6 +5192,7 @@ void DAGTypeLegalizer::WidenVectorResult(SDNode *N, unsigned ResNo) {
[[fallthrough]];
case ISD::ABS:
+ case ISD::ABS_MIN_POISON:
case ISD::VP_ABS:
case ISD::BITREVERSE:
case ISD::VP_BITREVERSE:
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
index 4a2bd811b5214..2b8fda77ebec7 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
@@ -2979,6 +2979,7 @@ bool SelectionDAG::isSplatValue(SDValue V, const APInt &DemandedElts,
return false;
}
case ISD::ABS:
+ case ISD::ABS_MIN_POISON:
case ISD::TRUNCATE:
case ISD::SIGN_EXTEND:
case ISD::ZERO_EXTEND:
@@ -4320,7 +4321,8 @@ KnownBits SelectionDAG::computeKnownBits(SDValue Op, const APInt &DemandedElts,
Known = Known2.byteSwap();
break;
}
- case ISD::ABS: {
+ case ISD::ABS:
+ case ISD::ABS_MIN_POISON: {
Known2 = computeKnownBits(Op.getOperand(0), DemandedElts, Depth + 1);
Known = Known2.abs();
Known.Zero.setHighBits(
@@ -5799,6 +5801,9 @@ bool SelectionDAG::canCreateUndefOrPoison(SDValue Op, const APInt &DemandedElts,
// ISD::ABS defines abs(INT_MIN) -> INT_MIN and never generates poison.
// Different to Intrinsic::abs.
return false;
+ case ISD::ABS_MIN_POISON:
+ // ABS_MIN_POISON may produce poison if the input is INT_MIN.
+ return true;
case ISD::ADDC:
case ISD::SUBC:
@@ -6268,6 +6273,7 @@ bool SelectionDAG::isKnownNeverZero(SDValue Op, const APInt &DemandedElts,
case ISD::BSWAP:
case ISD::CTPOP:
case ISD::ABS:
+ case ISD::ABS_MIN_POISON:
return isKnownNeverZero(Op.getOperand(0), Depth + 1);
case ISD::SRA:
@@ -6662,6 +6668,7 @@ SDValue SelectionDAG::getNode(unsigned Opcode, const SDLoc &DL, EVT VT,
case ISD::BF16_TO_FP:
case ISD::BITCAST:
case ISD::ABS:
+ case ISD::ABS_MIN_POISON:
case ISD::BITREVERSE:
case ISD::BSWAP:
case ISD::CTLZ:
@@ -6891,6 +6898,12 @@ SDValue SelectionDAG::getNode(unsigned Opcode, const SDLoc &DL, EVT VT,
if (N1.isUndef())
return getConstant(0, DL, VT);
break;
+ case ISD::ABS_MIN_POISON:
+ assert(VT.isInteger() && VT == N1.getValueType() &&
+ "Invalid ABS_MIN_POISON!");
+ if (N1.isUndef())
+ return getConstant(0, DL, VT);
+ break;
case ISD::BSWAP:
assert(VT.isInteger() && VT == N1.getValueType() && "Invalid BSWAP!");
assert((VT.getScalarSizeInBits() % 16 == 0) &&
@@ -7192,6 +7205,11 @@ SDValue SelectionDAG::FoldConstantArithmetic(unsigned Opcode, const SDLoc &DL,
case ISD::ABS:
return getConstant(Val.abs(), DL, VT, C->isTargetOpcode(),
C->isOpaque());
+ case ISD::ABS_MIN_POISON:
+ if (Val.isMinSignedValue())
+ return getPOISON(VT);
+ return getConstant(Val.abs(), DL, VT, C->isTargetOpcode(),
+ C->isOpaque());
case ISD::BITREVERSE:
return getConstant(Val.reverseBits(), DL, VT, C->isTargetOpcode(),
C->isOpaque());
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
index 392e53b99c64e..18fb2319833fa 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
@@ -7427,9 +7427,10 @@ void SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I,
return;
}
case Intrinsic::abs: {
- // TODO: Preserve "int min is poison" arg in SDAG?
SDValue Op1 = getValue(I.getArgOperand(0));
- setValue(&I, DAG.getNode(ISD::ABS, sdl, Op1.getValueType(), Op1));
+ bool IntMinIsPoison = cast<ConstantInt>(I.getArgOperand(1))->isOne();
+ unsigned Opc = IntMinIsPoison ? ISD::ABS_MIN_POISON : ISD::ABS;
+ setValue(&I, DAG.getNode(Opc, sdl, Op1.getValueType(), Op1));
return;
}
case Intrinsic::scmp: {
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp
index 9453036455727..ea6d7ae14a1a6 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp
@@ -516,6 +516,7 @@ std::string SDNode::getOperationName(const SelectionDAG *G) const {
// Bit manipulation
case ISD::ABS: return "abs";
+ case ISD::ABS_MIN_POISON: return "abs_min_poison";
case ISD::BITREVERSE: return "bitreverse";
case ISD::BSWAP: return "bswap";
case ISD::CTPOP: return "ctpop";
diff --git a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
index cc719b1e67f53..01f37e3c39343 100644
--- a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
@@ -9911,6 +9911,16 @@ SDValue TargetLowering::expandABS(SDNode *N, SelectionDAG &DAG,
EVT VT = N->getValueType(0);
SDValue Op = N->getOperand(0);
+ // If expanding ABS_MIN_POISON, fall back to ABS if the target supports it.
+ if (N->getOpcode() == ISD::ABS_MIN_POISON &&
+ isOperationLegalOrCustom(ISD::ABS, VT)) {
+ SDValue AbsVal = DAG.getNode(ISD::ABS, dl, VT, Op);
+ if (IsNegative)
+ return DAG.getNode(ISD::SUB, dl, VT, DAG.getConstant(0, dl, VT),
+ AbsVal);
+ return AbsVal;
+ }
+
// abs(x) -> smax(x,sub(0,x))
if (!IsNegative && isOperationLegal(ISD::SUB, VT) &&
isOperationLegal(ISD::SMAX, VT)) {
diff --git a/llvm/lib/CodeGen/TargetLoweringBase.cpp b/llvm/lib/CodeGen/TargetLoweringBase.cpp
index cc5a4219536ac..8d59420c46125 100644
--- a/llvm/lib/CodeGen/TargetLoweringBase.cpp
+++ b/llvm/lib/CodeGen/TargetLoweringBase.cpp
@@ -1165,6 +1165,9 @@ void TargetLoweringBase::initActions() {
// These default to Expand so they will be expanded to CTLZ/CTTZ by default.
setOperationAction({ISD::CTLZ_ZERO_UNDEF, ISD::CTTZ_ZERO_UNDEF}, VT,
Expand);
+
+ // This defaults to Expand so it will be expanded to ABS by default.
+ setOperationAction(ISD::ABS_MIN_POISON, VT, Expand);
setOperationAction(ISD::CTLS, VT, Expand);
setOperationAction({ISD::BITREVERSE, ISD::PARITY}, VT, Expand);
diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index eb6e9146e3839..e6e5b42c887a1 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -19574,7 +19574,8 @@ static SDValue performVecReduceAddCombineWithUADDLP(SDNode *N,
// Look through an optional post-ABS ZEXT from v16i16 -> v16i32.
if (VecReduceOp0.getOpcode() == ISD::ZERO_EXTEND &&
VecReduceOp0->getValueType(0) == MVT::v16i32 &&
- VecReduceOp0->getOperand(0)->getOpcode() == ISD::ABS &&
+ (VecReduceOp0->getOperand(0)->getOpcode() == ISD::ABS ||
+ VecReduceOp0->getOperand(0)->getOpcode() == ISD::ABS_MIN_POISON) &&
VecReduceOp0->getOperand(0)->getValueType(0) == MVT::v16i16) {
SawTrailingZext = true;
VecReduceOp0 = VecReduceOp0.getOperand(0);
@@ -19584,7 +19585,8 @@ static SDValue performVecReduceAddCombineWithUADDLP(SDNode *N,
MVT AbsInputVT = SawTrailingZext ? MVT::v16i16 : MVT::v16i32;
// Assumed v16i16 or v16i32 abs input
unsigned Opcode = VecReduceOp0.getOpcode();
- if (Opcode != ISD::ABS || VecReduceOp0->getValueType(0) != AbsInputVT)
+ if ((Opcode != ISD::ABS && Opcode != ISD::ABS_MIN_POISON) ||
+ VecReduceOp0->getValueType(0) != AbsInputVT)
return SDValue();
SDValue ABS = VecReduceOp0;
diff --git a/llvm/lib/Target/NVPTX/NVPTXISelLowering.cpp b/llvm/lib/Target/NVPTX/NVPTXISelLowering.cpp
index f5554be155eac..2f256ac1f3e51 100644
--- a/llvm/lib/Target/NVPTX/NVPTXISelLowering.cpp
+++ b/llvm/lib/Target/NVPTX/NVPTXISelLowering.cpp
@@ -657,7 +657,8 @@ NVPTXTargetLowering::NVPTXTargetLowering(const NVPTXTargetMachine &TM,
// Only logical ops can be done on v4i8/v2i32 directly, others must be done
// elementwise.
setOperationAction(
- {ISD::ABS, ISD::ADD, ISD::ADDC, ISD::ADDE,
+ {ISD::ABS,
+ ISD::ADD, ISD::ADDC, ISD::ADDE,
ISD::BITREVERSE, ISD::CTLZ, ISD::CTPOP, ISD::CTTZ,
ISD::FP_TO_SINT, ISD::FP_TO_UINT, ISD::FSHL, ISD::FSHR,
ISD::MUL, ISD::MULHS, ISD::MULHU, ISD::PARITY,
@@ -807,15 +808,21 @@ NVPTXTargetLowering::NVPTXTargetLowering(const NVPTXTargetMachine &TM,
setOperationAction(ISD::VACOPY, MVT::Other, Expand);
setOperationAction(ISD::VAEND, MVT::Other, Expand);
- setOperationAction({ISD::ABS, ISD::SMIN, ISD::SMAX, ISD::UMIN, ISD::UMAX},
+ setOperationAction({ISD::SMIN, ISD::SMAX, ISD::UMIN, ISD::UMAX},
{MVT::i16, MVT::i32, MVT::i64}, Legal);
+ // PTX abs.s is undefined for INT_MIN, so ISD::ABS (which requires
+ // abs(INT_MIN) == INT_MIN) must be expanded. ABS_MIN_POISON matches
+ // PTX abs semantics since INT_MIN input is poison/undefined.
+ setOperationAction(ISD::ABS, {MVT::i16, MVT::i32, MVT::i64}, Expand);
+ setOperationAction(ISD::ABS_MIN_POISON, {MVT::i16, MVT::i32, MVT::i64},
+ Legal);
setOperationAction({ISD::CTPOP, ISD::CTLZ, ISD::CTLZ_ZERO_UNDEF}, MVT::i16,
Promote);
setOperationAction({ISD::CTPOP, ISD::CTLZ}, MVT::i32, Legal);
setOperationAction({ISD::CTPOP, ISD::CTLZ}, MVT::i64, Custom);
- setI16x2OperationAction(ISD::ABS, MVT::v2i16, Legal, Custom);
+ setI16x2OperationAction(ISD::ABS_MIN_POISON, MVT::v2i16, Legal, Custom);
setI16x2OperationAction(ISD::SMIN, MVT::v2i16, Legal, Custom);
setI16x2OperationAction(ISD::SMAX, MVT::v2i16, Legal, Custom);
setI16x2OperationAction(ISD::UMIN, MVT::v2i16, Legal, Custom);
@@ -837,7 +844,8 @@ NVPTXTargetLowering::NVPTXTargetLowering(const NVPTXTargetMachine &TM,
{MVT::v2i16, MVT::v2i32}, Expand);
// v2i32 is not supported for any arithmetic operations
- setOperationAction({ISD::ABS, ISD::SMIN, ISD::SMAX, ISD::UMIN, ISD::UMAX,
+ setOperationAction({ISD::ABS,
+ ISD::SMIN, ISD::SMAX, ISD::UMIN, ISD::UMAX,
ISD::CTPOP, ISD::CTLZ, ISD::ADD, ISD::SUB, ISD::MUL,
ISD::SHL, ISD::SRA, ISD::SRL, ISD::OR, ISD::AND, ISD::XOR,
ISD::SREM, ISD::UREM},
@@ -3491,6 +3499,7 @@ NVPTXTargetLowering::LowerOperation(SDValue Op, SelectionDAG &DAG) const {
case ISD::ROTR:
return lowerROT(Op, DAG);
case ISD::ABS:
+ case ISD::ABS_MIN_POISON:
case ISD::SMIN:
case ISD::SMAX:
case ISD::UMIN:
diff --git a/llvm/lib/Target/NVPTX/NVPTXInstrInfo.td b/llvm/lib/Target/NVPTX/NVPTXInstrInfo.td
index 096c5e470ed02..dde3ef7493e3f 100644
--- a/llvm/lib/Target/NVPTX/NVPTXInstrInfo.td
+++ b/llvm/lib/Target/NVPTX/NVPTXInstrInfo.td
@@ -935,10 +935,13 @@ defm SREM : I3<"rem.s", srem, commutative = false>;
defm UREM : I3<"rem.u", urem, commutative = false>;
foreach t = [I16RT, I32RT, I64RT] in {
+ // PTX abs.s is undefined for INT_MIN, so it matches ABS_MIN_POISON
+ // semantics (where INT_MIN input is poison). ISD::ABS requires
+ // abs(INT_MIN) == INT_MIN and is expanded separately.
def ABS_S # t.Size :
BasicNVPTXInst<(outs t.RC:$dst), (ins t.RC:$a),
"abs.s" # t.Size,
- [(set t.Ty:$dst, (abs t.Ty:$a))]>;
+ [(set t.Ty:$dst, (abs_min_poison t.Ty:$a))]>;
def NEG_S # t.Size :
BasicNVPTXInst<(outs t.RC:$dst), (ins t.RC:$src),
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index a8542be937a87..8cb3435c2436a 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -465,12 +465,12 @@ RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM,
(Subtarget.hasVendorXCValu() && !Subtarget.is64Bit())) {
setOperationAction(ISD::ABS, XLenVT, Legal);
if (Subtarget.is64Bit())
- setOperationAction(ISD::ABS, MVT::i32, Custom);
+ setOperationAction({ISD::ABS, ISD::ABS_MIN_POISON}, MVT::i32, Custom);
} else if (Subtarget.hasShortForwardBranchIALU()) {
// We can use PseudoCCSUB to implement ABS.
setOperationAction(ISD::ABS, XLenVT, Legal);
} else if (Subtarget.is64Bit()) {
- setOperationAction(ISD::ABS, MVT::i32, Custom);
+ setOperationAction({ISD::ABS, ISD::ABS_MIN_POISON}, MVT::i32, Custom);
}
if (!Subtarget.useMIPSCCMovInsn() && !Subtarget.hasVendorXTHeadCondMov())
@@ -1892,7 +1892,7 @@ RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM,
ISD::VP_STORE, ISD::VP_TRUNCATE, ISD::EXPERIMENTAL_VP_REVERSE,
ISD::MUL, ISD::SDIV, ISD::UDIV,
ISD::SREM, ISD::UREM, ISD::INSERT_VECTOR_ELT,
- ISD::ABS, ISD::CTPOP, ISD::VECTOR_SHUFFLE,
+ ISD::ABS, ISD::ABS_MIN_POISON, ISD::CTPOP, ISD::VECTOR_SHUFFLE,
ISD::FMA, ISD::VSELECT, ISD::VECREDUCE_ADD});
if (Subtarget.hasVendorXTHeadMemPair())
@@ -8910,6 +8910,7 @@ SDValue RISCVTargetLowering::LowerOperation(SDValue Op,
return DAG.getNode(ISD::SUB, dl, VT, Max, Min);
}
case ISD::ABS:
+ case ISD::ABS_MIN_POISON:
case ISD::VP_ABS:
return lowerABS(Op, DAG);
case ISD::CTLZ:
@@ -15590,7 +15591,8 @@ void RISCVTargetLowering::ReplaceNodeResults(SDNode *N,
Results.push_back(expandAddSubSat(N, DAG));
return;
}
- case ISD::ABS: {
+ case ISD::ABS:
+ case ISD::ABS_MIN_POISON: {
assert(N->getValueType(0) == MVT::i32 && Subtarget.is64Bit() &&
"Unexpected custom legalisation");
@@ -21546,7 +21548,8 @@ SDValue RISCVTargetLowering::PerformDAGCombine(SDNode *N,
return DAG.getNode(ISD::AND, DL, VT, NewFMV,
DAG.getConstant(~SignBit, DL, VT));
}
- case ISD::ABS: {
+ case ISD::ABS:
+ case ISD::ABS_MIN_POISON: {
EVT VT = N->getValueType(0);
SDValue N0 = N->getOperand(0);
// abs (sext) -> zext (abs)
@@ -21554,8 +21557,9 @@ SDValue RISCVTargetLowering::PerformDAGCombine(SDNode *N,
if (VT.isVector() && N0.hasOneUse() && N0.getOpcode() == ISD::SIGN_EXTEND) {
SDValue Src = N0.getOperand(0);
SDLoc DL(N);
+ unsigned Opc = N->getOpcode();
return DAG.getNode(ISD::ZERO_EXTEND, DL, VT,
- DAG.getNode(ISD::ABS, DL, Src.getValueType(), Src));
+ DAG.getNode(Opc, DL, Src.getValueType(), Src));
}
break;
}
diff --git a/llvm/test/CodeGen/NVPTX/idioms.ll b/llvm/test/CodeGen/NVPTX/idioms.ll
index 87c5ab27ecf9d..55e5aabe96a57 100644
--- a/llvm/test/CodeGen/NVPTX/idioms.ll
+++ b/llvm/test/CodeGen/NVPTX/idioms.ll
@@ -12,13 +12,14 @@
define i16 @abs_i16(i16 %a) {
; CHECK-LABEL: abs_i16(
; CHECK: {
-; CHECK-NEXT: .reg .b16 %rs<3>;
+; CHECK-NEXT: .reg .b16 %rs<4>;
; CHECK-NEXT: .reg .b32 %r<2>;
; CHECK-EMPTY:
; CHECK-NEXT: // %bb.0:
; CHECK-NEXT: ld.param.b16 %rs1, [abs_i16_param_0];
-; CHECK-NEXT: abs.s16 %rs2, %rs1;
-; CHECK-NEXT: cvt.u32.u16 %r1, %rs2;
+; CHECK-NEXT: neg.s16 %rs2, %rs1;
+; CHECK-NEXT: max.s16 %rs3, %rs1, %rs2;
+; CHECK-NEXT: cvt.u32.u16 %r1, %rs3;
; CHECK-NEXT: st.param.b32 [func_retval0], %r1;
; CHECK-NEXT: ret;
%neg = sub i16 0, %a
@@ -30,12 +31,13 @@ define i16 @abs_i16(i16 %a) {
define i32 @abs_i32(i32 %a) {
; CHECK-LABEL: abs_i32(
; CHECK: {
-; CHECK-NEXT: .reg .b32 %r<3>;
+; CHECK-NEXT: .reg .b32 %r<4>;
; CHECK-EMPTY:
; CHECK-NEXT: // %bb.0:
; CHECK-NEXT: ld.param.b32 %r1, [abs_i32_param_0];
-; CHECK-NEXT: abs.s32 %r2, %r1;
-; CHECK-NEXT: st.param.b32 [func_retval0], %r2;
+; CHECK-NEXT: neg.s32 %r2, %r1;
+; CHECK-NEXT: max.s32 %r3, %r1, %r2;
+; CHECK-NEXT: st.param.b32 [func_retval0], %r3;
; CHECK-NEXT: ret;
%neg = sub i32 0, %a
%abs.cond = icmp sge i32 %a, 0
@@ -46,12 +48,13 @@ define i32 @abs_i32(i32 %a) {
define i64 @abs_i64(i64 %a) {
; CHECK-LABEL: abs_i64(
; CHECK: {
-; CHECK-NEXT: .reg .b64 %rd<3>;
+; CHECK-NEXT: .reg .b64 %rd<4>;
; CHECK-EMPTY:
; CHECK-NEXT: // %bb.0:
; CHECK-NEXT: ld.param.b64 %rd1, [abs_i64_param_0];
-; CHECK-NEXT: abs.s64 %rd2, %rd1;
-; CHECK-NEXT: st.param.b64 [func_retval0], %rd2;
+; CHECK-NEXT: neg.s64 %rd2, %rd1;
+; CHECK-NEXT: max.s64 %rd3, %rd1, %rd2;
+; CHECK-NEXT: st.param.b64 [func_retval0], %rd3;
; CHECK-NEXT: ret;
%neg = sub i64 0, %a
%abs.cond = icmp sge i64 %a, 0
diff --git a/llvm/test/CodeGen/X86/icmp-abs-C.ll b/llvm/test/CodeGen/X86/icmp-abs-C.ll
index 71893a9e4be67..fe19174b53b42 100644
--- a/llvm/test/CodeGen/X86/icmp-abs-C.ll
+++ b/llvm/test/CodeGen/X86/icmp-abs-C.ll
@@ -13,29 +13,32 @@ define i64 @eq_or_with_dom_abs(i64 %x) nounwind {
; X86-NEXT: pushl %ebx
; X86-NEXT: pushl %edi
; X86-NEXT: pushl %esi
-; X86-NEXT: movl {{[0-9]+}}(%esp), %edx
-; X86-NEXT: movl %edx, %eax
-; X86-NEXT: sarl $31, %eax
-; X86-NEXT: xorl %eax, %edx
+; X86-NEXT: movl {{[0-9]+}}(%esp), %ecx
; X86-NEXT: movl {{[0-9]+}}(%esp), %esi
-; X86-NEXT: xorl %eax, %esi
-; X86-NEXT: subl %eax, %esi
-; X86-NEXT: sbbl %eax, %edx
-; X86-NEXT: movl %esi, %eax
+; X86-NEXT: movl %esi, %edi
+; X86-NEXT: sarl $31, %edi
+; X86-NEXT: movl %esi, %edx
+; X86-NEXT: xorl %edi, %edx
+; X86-NEXT: movl %ecx, %eax
+; X86-NEXT: xorl %edi, %eax
+; X86-NEXT: subl %edi, %eax
+; X86-NEXT: sbbl %edi, %edx
; X86-NEXT: xorl $12312, %eax # imm = 0x3018
-; X86-NEXT: xorl $64, %esi
-; X86-NEXT: xorl %ecx, %ecx
-; X86-NEXT: orl %edx, %esi
-; X86-NEXT: sete %bl
+; X86-NEXT: addl $64, %ecx
+; X86-NEXT: adcl $0, %esi
+; X86-NEXT: andl $-129, %ecx
+; X86-NEXT: xorl %ebx, %ebx
+; X86-NEXT: orl %esi, %ecx
+; X86-NEXT: sete %cl
; X86-NEXT: xorl %esi, %esi
; X86-NEXT: movl $2344, %edi # imm = 0x928
; X86-NEXT: cmpl %eax, %edi
; X86-NEXT: sbbl %edx, %esi
; X86-NEXT: jb .LBB0_2
; X86-NEXT: # %bb.1:
-; X86-NEXT: movb %bl, %cl
+; X86-NEXT: movb %cl, %bl
; X86-NEXT: xorl %edx, %edx
-; X86-NEXT: movl %ecx, %eax
+; X86-NEXT: movl %ebx, %eax
; X86-NEXT: .LBB0_2:
; X86-NEXT: popl %esi
; X86-NEXT: popl %edi
@@ -47,13 +50,13 @@ define i64 @eq_or_with_dom_abs(i64 %x) nounwind {
; X64-NEXT: movq %rdi, %rcx
; X64-NEXT: negq %rcx
; X64-NEXT: cmovsq %rdi, %rcx
-; X64-NEXT: movq %rcx, %rdx
-; X64-NEXT: xorq $12312, %rdx # imm = 0x3018
+; X64-NEXT: xorq $12312, %rcx # imm = 0x3018
+; X64-NEXT: addq $64, %rdi
; X64-NEXT: xorl %eax, %eax
-; X64-NEXT: cmpq $64, %rcx
+; X64-NEXT: testq $-129, %rdi
; X64-NEXT: sete %al
-; X64-NEXT: cmpq $2345, %rdx # imm = 0x929
-; X64-NEXT: cmovaeq %rdx, %rax
+; X64-NEXT: cmpq $2345, %rcx # imm = 0x929
+; X64-NEXT: cmovaeq %rcx, %rax
; X64-NEXT: retq
%absx = call i64 @llvm.abs.i64(i64 %x, i1 true)
%foo = xor i64 %absx, 12312
@@ -70,20 +73,21 @@ define i32 @eq_or_with_dom_abs_non_po2(i32 %x) nounwind {
; X86-LABEL: eq_or_with_dom_abs_non_po2:
; X86: # %bb.0:
; X86-NEXT: movl {{[0-9]+}}(%esp), %edx
+; X86-NEXT: movl %edx, %ecx
+; X86-NEXT: sarl $31, %ecx
; X86-NEXT: movl %edx, %eax
-; X86-NEXT: sarl $31, %eax
-; X86-NEXT: xorl %eax, %edx
-; X86-NEXT: subl %eax, %edx
-; X86-NEXT: movl %edx, %eax
+; X86-NEXT: xorl %ecx, %eax
+; X86-NEXT: subl %ecx, %eax
; X86-NEXT: xorl $12312, %eax # imm = 0x3018
-; X86-NEXT: xorl %ecx, %ecx
; X86-NEXT: cmpl $123, %edx
+; X86-NEXT: sete %cl
+; X86-NEXT: cmpl $-123, %edx
; X86-NEXT: sete %dl
; X86-NEXT: cmpl $2345, %eax # imm = 0x929
; X86-NEXT: jae .LBB1_2
; X86-NEXT: # %bb.1:
-; X86-NEXT: movb %dl, %cl
-; X86-NEXT: movl %ecx, %eax
+; X86-NEXT: orb %dl, %cl
+; X86-NEXT: movzbl %cl, %eax
; X86-NEXT: .LBB1_2:
; X86-NEXT: retl
;
@@ -92,13 +96,15 @@ define i32 @eq_or_with_dom_abs_non_po2(i32 %x) nounwind {
; X64-NEXT: movl %edi, %ecx
; X64-NEXT: negl %ecx
; X64-NEXT: cmovsl %edi, %ecx
-; X64-NEXT: movl %ecx, %edx
-; X64-NEXT: xorl $12312, %edx # imm = 0x3018
-; X64-NEXT: xorl %eax, %eax
-; X64-NEXT: cmpl $123, %ecx
+; X64-NEXT: xorl $12312, %ecx # imm = 0x3018
+; X64-NEXT: cmpl $123, %edi
; X64-NEXT: sete %al
-; X64-NEXT: cmpl $2345, %edx # imm = 0x929
-; X64-NEXT: cmovael %edx, %eax
+; X64-NEXT: cmpl $-123, %edi
+; X64-NEXT: sete %dl
+; X64-NEXT: orb %al, %dl
+; X64-NEXT: cmpl $2345, %ecx # imm = 0x929
+; X64-NEXT: movzbl %dl, %eax
+; X64-NEXT: cmovael %ecx, %eax
; X64-NEXT: retq
%absx = call i32 @llvm.abs.i32(i32 %x, i1 true)
%foo = xor i32 %absx, 12312
@@ -114,18 +120,21 @@ define i32 @eq_or_with_dom_abs_non_po2(i32 %x) nounwind {
define i8 @ne_and_with_dom_abs_non_pow2(i8 %x) nounwind {
; X86-LABEL: ne_and_with_dom_abs_non_pow2:
; X86: # %bb.0:
-; X86-NEXT: movzbl {{[0-9]+}}(%esp), %ecx
-; X86-NEXT: movl %ecx, %eax
-; X86-NEXT: sarb $7, %al
-; X86-NEXT: xorb %al, %cl
-; X86-NEXT: subb %al, %cl
-; X86-NEXT: movl %ecx, %eax
+; X86-NEXT: movzbl {{[0-9]+}}(%esp), %edx
+; X86-NEXT: movl %edx, %ecx
+; X86-NEXT: sarb $7, %cl
+; X86-NEXT: movl %edx, %eax
+; X86-NEXT: xorb %cl, %al
+; X86-NEXT: subb %cl, %al
; X86-NEXT: xorb $12, %al
-; X86-NEXT: cmpb $121, %cl
+; X86-NEXT: cmpb $121, %dl
; X86-NEXT: setne %cl
+; X86-NEXT: cmpb $-121, %dl
+; X86-NEXT: setne %dl
; X86-NEXT: cmpb $24, %al
; X86-NEXT: jae .LBB2_2
; X86-NEXT: # %bb.1:
+; X86-NEXT: andb %dl, %cl
; X86-NEXT: movl %ecx, %eax
; X86-NEXT: .LBB2_2:
; X86-NEXT: retl
@@ -134,16 +143,19 @@ define i8 @ne_and_with_dom_abs_non_pow2(i8 %x) nounwind {
; X64: # %bb.0:
; X64-NEXT: movl %edi, %eax
; X64-NEXT: sarb $7, %al
-; X64-NEXT: xorb %al, %dil
-; X64-NEXT: subb %al, %dil
; X64-NEXT: movl %edi, %ecx
+; X64-NEXT: xorb %al, %cl
+; X64-NEXT: subb %al, %cl
; X64-NEXT: xorb $12, %cl
-; X64-NEXT: xorl %eax, %eax
; X64-NEXT: cmpb $121, %dil
; X64-NEXT: setne %al
+; X64-NEXT: cmpb $-121, %dil
+; X64-NEXT: setne %dl
+; X64-NEXT: andb %al, %dl
; X64-NEXT: cmpb $24, %cl
-; X64-NEXT: movzbl %cl, %ecx
-; X64-NEXT: cmovael %ecx, %eax
+; X64-NEXT: movzbl %dl, %edx
+; X64-NEXT: movzbl %cl, %eax
+; X64-NEXT: cmovbl %edx, %eax
; X64-NEXT: # kill: def $al killed $al killed $eax
; X64-NEXT: retq
%absx = call i8 @llvm.abs.i8(i8 %x, i1 true)
@@ -161,16 +173,16 @@ define i16 @ne_and_with_dom_abs(i16 %x) nounwind {
; X86-LABEL: ne_and_with_dom_abs:
; X86: # %bb.0:
; X86-NEXT: pushl %esi
-; X86-NEXT: movswl {{[0-9]+}}(%esp), %eax
-; X86-NEXT: movl %eax, %ecx
+; X86-NEXT: movzwl {{[0-9]+}}(%esp), %eax
+; X86-NEXT: movswl %ax, %ecx
; X86-NEXT: sarl $15, %ecx
+; X86-NEXT: leal 64(%eax), %edx
; X86-NEXT: xorl %ecx, %eax
; X86-NEXT: subl %ecx, %eax
-; X86-NEXT: movl %eax, %edx
; X86-NEXT: xorl $12312, %eax # imm = 0x3018
; X86-NEXT: movzwl %ax, %esi
; X86-NEXT: xorl %ecx, %ecx
-; X86-NEXT: cmpw $64, %dx
+; X86-NEXT: testl $65407, %edx # imm = 0xFF7F
; X86-NEXT: setne %dl
; X86-NEXT: cmpl $2345, %esi # imm = 0x929
; X86-NEXT: jae .LBB3_2
@@ -187,14 +199,14 @@ define i16 @ne_and_with_dom_abs(i16 %x) nounwind {
; X64-NEXT: movl %edi, %ecx
; X64-NEXT: negw %cx
; X64-NEXT: cmovsw %di, %cx
-; X64-NEXT: movl %ecx, %edx
-; X64-NEXT: xorl $12312, %edx # imm = 0x3018
-; X64-NEXT: movzwl %dx, %esi
+; X64-NEXT: xorl $12312, %ecx # imm = 0x3018
+; X64-NEXT: movzwl %cx, %edx
+; X64-NEXT: addl $64, %edi
; X64-NEXT: xorl %eax, %eax
-; X64-NEXT: cmpw $64, %cx
+; X64-NEXT: testl $65407, %edi # imm = 0xFF7F
; X64-NEXT: setne %al
-; X64-NEXT: cmpl $2345, %esi # imm = 0x929
-; X64-NEXT: cmovael %edx, %eax
+; X64-NEXT: cmpl $2345, %edx # imm = 0x929
+; X64-NEXT: cmovael %ecx, %eax
; X64-NEXT: # kill: def $ax killed $ax killed $eax
; X64-NEXT: retq
%absx = call i16 @llvm.abs.i16(i16 %x, i1 true)
diff --git a/llvm/test/CodeGen/X86/icmp-pow2-logic-npow2.ll b/llvm/test/CodeGen/X86/icmp-pow2-logic-npow2.ll
index b14fedeaae57d..298e78103e709 100644
--- a/llvm/test/CodeGen/X86/icmp-pow2-logic-npow2.ll
+++ b/llvm/test/CodeGen/X86/icmp-pow2-logic-npow2.ll
@@ -154,15 +154,20 @@ define i1 @abs_eq_pow2(i32 %0) nounwind {
; X86-LABEL: abs_eq_pow2:
; X86: # %bb.0:
; X86-NEXT: movl {{[0-9]+}}(%esp), %eax
-; X86-NEXT: addl $4, %eax
-; X86-NEXT: testl $-9, %eax
+; X86-NEXT: movl %eax, %ecx
+; X86-NEXT: sarl $31, %ecx
+; X86-NEXT: xorl %ecx, %eax
+; X86-NEXT: subl %ecx, %eax
+; X86-NEXT: cmpl $4, %eax
; X86-NEXT: sete %al
; X86-NEXT: retl
;
; X64-LABEL: abs_eq_pow2:
; X64: # %bb.0:
-; X64-NEXT: addl $4, %edi
-; X64-NEXT: testl $-9, %edi
+; X64-NEXT: movl %edi, %eax
+; X64-NEXT: negl %eax
+; X64-NEXT: cmovsl %edi, %eax
+; X64-NEXT: cmpl $4, %eax
; X64-NEXT: sete %al
; X64-NEXT: retq
%2 = tail call i32 @llvm.abs.i32(i32 %0, i1 true)
@@ -174,18 +179,24 @@ define i1 @abs_ne_pow2(i64 %0) nounwind {
; X86-LABEL: abs_ne_pow2:
; X86: # %bb.0:
; X86-NEXT: movl {{[0-9]+}}(%esp), %eax
-; X86-NEXT: movl {{[0-9]+}}(%esp), %ecx
-; X86-NEXT: addl $2, %eax
-; X86-NEXT: adcl $0, %ecx
-; X86-NEXT: andl $-5, %eax
-; X86-NEXT: orl %ecx, %eax
+; X86-NEXT: movl %eax, %ecx
+; X86-NEXT: sarl $31, %ecx
+; X86-NEXT: xorl %ecx, %eax
+; X86-NEXT: movl {{[0-9]+}}(%esp), %edx
+; X86-NEXT: xorl %ecx, %edx
+; X86-NEXT: subl %ecx, %edx
+; X86-NEXT: sbbl %ecx, %eax
+; X86-NEXT: xorl $2, %edx
+; X86-NEXT: orl %eax, %edx
; X86-NEXT: setne %al
; X86-NEXT: retl
;
; X64-LABEL: abs_ne_pow2:
; X64: # %bb.0:
-; X64-NEXT: addq $2, %rdi
-; X64-NEXT: testq $-5, %rdi
+; X64-NEXT: movq %rdi, %rax
+; X64-NEXT: negq %rax
+; X64-NEXT: cmovsq %rdi, %rax
+; X64-NEXT: cmpq $2, %rax
; X64-NEXT: setne %al
; X64-NEXT: retq
%2 = tail call i64 @llvm.abs.i64(i64 %0, i1 true)
>From 19df666dd01477f0ef276c5d508e7efb346e9044 Mon Sep 17 00:00:00 2001
From: aryanmagoon <amagoon at nvidia.com>
Date: Fri, 13 Mar 2026 20:51:01 +0000
Subject: [PATCH 2/6] fixup! SelectionDAG: Preserve poison for abs INT_MIN
lowering. Update affected CodeGen lit tests and added a new lit test for this
behavior.
---
llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp | 4 ++-
llvm/lib/Target/X86/X86ISelLowering.cpp | 4 ++-
.../test/CodeGen/X86/icmp-pow2-logic-npow2.ll | 33 +++++++------------
3 files changed, 17 insertions(+), 24 deletions(-)
diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
index e93094e544bbd..a1bdfcf7e9c40 100644
--- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
@@ -6916,7 +6916,9 @@ static SDValue foldAndOrOfSETCC(SDNode *LogicOp, SelectionDAG &DAG) {
// case this is just a compare).
if (APLhs == (-APRhs) &&
((TargetPreference & AndOrSETCCFoldKind::ABS) ||
- DAG.doesNodeExist(ISD::ABS, DAG.getVTList(OpVT), {LHS0}))) {
+ DAG.doesNodeExist(ISD::ABS, DAG.getVTList(OpVT), {LHS0}) ||
+ DAG.doesNodeExist(ISD::ABS_MIN_POISON, DAG.getVTList(OpVT),
+ {LHS0}))) {
const APInt &C = APLhs.isNegative() ? APRhs : APLhs;
// (icmp eq A, C) | (icmp eq A, -C)
// -> (icmp eq Abs(A), C)
diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp
index 927a49b203968..612b7383e2e2e 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.cpp
+++ b/llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -57415,7 +57415,9 @@ static SDValue combineSetCC(SDNode *N, SelectionDAG &DAG,
// Both of these patterns can be better optimized in
// DAGCombiner::foldAndOrOfSETCC. Note this only applies for scalar
// integers which is checked above.
- if (LHS.getOpcode() == ISD::ABS && LHS.hasOneUse()) {
+ if ((LHS.getOpcode() == ISD::ABS ||
+ LHS.getOpcode() == ISD::ABS_MIN_POISON) &&
+ LHS.hasOneUse()) {
if (auto *C = dyn_cast<ConstantSDNode>(RHS)) {
const APInt &CInt = C->getAPIntValue();
// We can better optimize this case in DAGCombiner::foldAndOrOfSETCC.
diff --git a/llvm/test/CodeGen/X86/icmp-pow2-logic-npow2.ll b/llvm/test/CodeGen/X86/icmp-pow2-logic-npow2.ll
index 298e78103e709..b14fedeaae57d 100644
--- a/llvm/test/CodeGen/X86/icmp-pow2-logic-npow2.ll
+++ b/llvm/test/CodeGen/X86/icmp-pow2-logic-npow2.ll
@@ -154,20 +154,15 @@ define i1 @abs_eq_pow2(i32 %0) nounwind {
; X86-LABEL: abs_eq_pow2:
; X86: # %bb.0:
; X86-NEXT: movl {{[0-9]+}}(%esp), %eax
-; X86-NEXT: movl %eax, %ecx
-; X86-NEXT: sarl $31, %ecx
-; X86-NEXT: xorl %ecx, %eax
-; X86-NEXT: subl %ecx, %eax
-; X86-NEXT: cmpl $4, %eax
+; X86-NEXT: addl $4, %eax
+; X86-NEXT: testl $-9, %eax
; X86-NEXT: sete %al
; X86-NEXT: retl
;
; X64-LABEL: abs_eq_pow2:
; X64: # %bb.0:
-; X64-NEXT: movl %edi, %eax
-; X64-NEXT: negl %eax
-; X64-NEXT: cmovsl %edi, %eax
-; X64-NEXT: cmpl $4, %eax
+; X64-NEXT: addl $4, %edi
+; X64-NEXT: testl $-9, %edi
; X64-NEXT: sete %al
; X64-NEXT: retq
%2 = tail call i32 @llvm.abs.i32(i32 %0, i1 true)
@@ -179,24 +174,18 @@ define i1 @abs_ne_pow2(i64 %0) nounwind {
; X86-LABEL: abs_ne_pow2:
; X86: # %bb.0:
; X86-NEXT: movl {{[0-9]+}}(%esp), %eax
-; X86-NEXT: movl %eax, %ecx
-; X86-NEXT: sarl $31, %ecx
-; X86-NEXT: xorl %ecx, %eax
-; X86-NEXT: movl {{[0-9]+}}(%esp), %edx
-; X86-NEXT: xorl %ecx, %edx
-; X86-NEXT: subl %ecx, %edx
-; X86-NEXT: sbbl %ecx, %eax
-; X86-NEXT: xorl $2, %edx
-; X86-NEXT: orl %eax, %edx
+; X86-NEXT: movl {{[0-9]+}}(%esp), %ecx
+; X86-NEXT: addl $2, %eax
+; X86-NEXT: adcl $0, %ecx
+; X86-NEXT: andl $-5, %eax
+; X86-NEXT: orl %ecx, %eax
; X86-NEXT: setne %al
; X86-NEXT: retl
;
; X64-LABEL: abs_ne_pow2:
; X64: # %bb.0:
-; X64-NEXT: movq %rdi, %rax
-; X64-NEXT: negq %rax
-; X64-NEXT: cmovsq %rdi, %rax
-; X64-NEXT: cmpq $2, %rax
+; X64-NEXT: addq $2, %rdi
+; X64-NEXT: testq $-5, %rdi
; X64-NEXT: setne %al
; X64-NEXT: retq
%2 = tail call i64 @llvm.abs.i64(i64 %0, i1 true)
>From 8a2a95c79adb52a021b3285b3c2b3a116c0543e1 Mon Sep 17 00:00:00 2001
From: aryanmagoon <amagoon at nvidia.com>
Date: Fri, 13 Mar 2026 23:23:51 +0000
Subject: [PATCH 3/6] fixup! fixup! SelectionDAG: Preserve poison for abs
INT_MIN lowering. Update affected CodeGen lit tests and added a new lit test
for this behavior.
---
llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp | 7 +-
llvm/test/CodeGen/X86/icmp-abs-C.ll | 120 ++++++++----------
2 files changed, 60 insertions(+), 67 deletions(-)
diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
index a1bdfcf7e9c40..026da9eb10ec3 100644
--- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
@@ -6924,7 +6924,12 @@ static SDValue foldAndOrOfSETCC(SDNode *LogicOp, SelectionDAG &DAG) {
// -> (icmp eq Abs(A), C)
// (icmp ne A, C) & (icmp ne A, -C)
// -> (icmp ne Abs(A), C)
- SDValue AbsOp = DAG.getNode(ISD::ABS, DL, OpVT, LHS0);
+ // Use ABS_MIN_POISON if that node already exists, otherwise ABS.
+ unsigned AbsOpc =
+ DAG.doesNodeExist(ISD::ABS_MIN_POISON, DAG.getVTList(OpVT), {LHS0})
+ ? ISD::ABS_MIN_POISON
+ : ISD::ABS;
+ SDValue AbsOp = DAG.getNode(AbsOpc, DL, OpVT, LHS0);
return DAG.getNode(ISD::SETCC, DL, VT, AbsOp,
DAG.getConstant(C, DL, OpVT), LHS.getOperand(2));
} else if (TargetPreference &
diff --git a/llvm/test/CodeGen/X86/icmp-abs-C.ll b/llvm/test/CodeGen/X86/icmp-abs-C.ll
index fe19174b53b42..71893a9e4be67 100644
--- a/llvm/test/CodeGen/X86/icmp-abs-C.ll
+++ b/llvm/test/CodeGen/X86/icmp-abs-C.ll
@@ -13,32 +13,29 @@ define i64 @eq_or_with_dom_abs(i64 %x) nounwind {
; X86-NEXT: pushl %ebx
; X86-NEXT: pushl %edi
; X86-NEXT: pushl %esi
-; X86-NEXT: movl {{[0-9]+}}(%esp), %ecx
+; X86-NEXT: movl {{[0-9]+}}(%esp), %edx
+; X86-NEXT: movl %edx, %eax
+; X86-NEXT: sarl $31, %eax
+; X86-NEXT: xorl %eax, %edx
; X86-NEXT: movl {{[0-9]+}}(%esp), %esi
-; X86-NEXT: movl %esi, %edi
-; X86-NEXT: sarl $31, %edi
-; X86-NEXT: movl %esi, %edx
-; X86-NEXT: xorl %edi, %edx
-; X86-NEXT: movl %ecx, %eax
-; X86-NEXT: xorl %edi, %eax
-; X86-NEXT: subl %edi, %eax
-; X86-NEXT: sbbl %edi, %edx
+; X86-NEXT: xorl %eax, %esi
+; X86-NEXT: subl %eax, %esi
+; X86-NEXT: sbbl %eax, %edx
+; X86-NEXT: movl %esi, %eax
; X86-NEXT: xorl $12312, %eax # imm = 0x3018
-; X86-NEXT: addl $64, %ecx
-; X86-NEXT: adcl $0, %esi
-; X86-NEXT: andl $-129, %ecx
-; X86-NEXT: xorl %ebx, %ebx
-; X86-NEXT: orl %esi, %ecx
-; X86-NEXT: sete %cl
+; X86-NEXT: xorl $64, %esi
+; X86-NEXT: xorl %ecx, %ecx
+; X86-NEXT: orl %edx, %esi
+; X86-NEXT: sete %bl
; X86-NEXT: xorl %esi, %esi
; X86-NEXT: movl $2344, %edi # imm = 0x928
; X86-NEXT: cmpl %eax, %edi
; X86-NEXT: sbbl %edx, %esi
; X86-NEXT: jb .LBB0_2
; X86-NEXT: # %bb.1:
-; X86-NEXT: movb %cl, %bl
+; X86-NEXT: movb %bl, %cl
; X86-NEXT: xorl %edx, %edx
-; X86-NEXT: movl %ebx, %eax
+; X86-NEXT: movl %ecx, %eax
; X86-NEXT: .LBB0_2:
; X86-NEXT: popl %esi
; X86-NEXT: popl %edi
@@ -50,13 +47,13 @@ define i64 @eq_or_with_dom_abs(i64 %x) nounwind {
; X64-NEXT: movq %rdi, %rcx
; X64-NEXT: negq %rcx
; X64-NEXT: cmovsq %rdi, %rcx
-; X64-NEXT: xorq $12312, %rcx # imm = 0x3018
-; X64-NEXT: addq $64, %rdi
+; X64-NEXT: movq %rcx, %rdx
+; X64-NEXT: xorq $12312, %rdx # imm = 0x3018
; X64-NEXT: xorl %eax, %eax
-; X64-NEXT: testq $-129, %rdi
+; X64-NEXT: cmpq $64, %rcx
; X64-NEXT: sete %al
-; X64-NEXT: cmpq $2345, %rcx # imm = 0x929
-; X64-NEXT: cmovaeq %rcx, %rax
+; X64-NEXT: cmpq $2345, %rdx # imm = 0x929
+; X64-NEXT: cmovaeq %rdx, %rax
; X64-NEXT: retq
%absx = call i64 @llvm.abs.i64(i64 %x, i1 true)
%foo = xor i64 %absx, 12312
@@ -73,21 +70,20 @@ define i32 @eq_or_with_dom_abs_non_po2(i32 %x) nounwind {
; X86-LABEL: eq_or_with_dom_abs_non_po2:
; X86: # %bb.0:
; X86-NEXT: movl {{[0-9]+}}(%esp), %edx
-; X86-NEXT: movl %edx, %ecx
-; X86-NEXT: sarl $31, %ecx
; X86-NEXT: movl %edx, %eax
-; X86-NEXT: xorl %ecx, %eax
-; X86-NEXT: subl %ecx, %eax
+; X86-NEXT: sarl $31, %eax
+; X86-NEXT: xorl %eax, %edx
+; X86-NEXT: subl %eax, %edx
+; X86-NEXT: movl %edx, %eax
; X86-NEXT: xorl $12312, %eax # imm = 0x3018
+; X86-NEXT: xorl %ecx, %ecx
; X86-NEXT: cmpl $123, %edx
-; X86-NEXT: sete %cl
-; X86-NEXT: cmpl $-123, %edx
; X86-NEXT: sete %dl
; X86-NEXT: cmpl $2345, %eax # imm = 0x929
; X86-NEXT: jae .LBB1_2
; X86-NEXT: # %bb.1:
-; X86-NEXT: orb %dl, %cl
-; X86-NEXT: movzbl %cl, %eax
+; X86-NEXT: movb %dl, %cl
+; X86-NEXT: movl %ecx, %eax
; X86-NEXT: .LBB1_2:
; X86-NEXT: retl
;
@@ -96,15 +92,13 @@ define i32 @eq_or_with_dom_abs_non_po2(i32 %x) nounwind {
; X64-NEXT: movl %edi, %ecx
; X64-NEXT: negl %ecx
; X64-NEXT: cmovsl %edi, %ecx
-; X64-NEXT: xorl $12312, %ecx # imm = 0x3018
-; X64-NEXT: cmpl $123, %edi
+; X64-NEXT: movl %ecx, %edx
+; X64-NEXT: xorl $12312, %edx # imm = 0x3018
+; X64-NEXT: xorl %eax, %eax
+; X64-NEXT: cmpl $123, %ecx
; X64-NEXT: sete %al
-; X64-NEXT: cmpl $-123, %edi
-; X64-NEXT: sete %dl
-; X64-NEXT: orb %al, %dl
-; X64-NEXT: cmpl $2345, %ecx # imm = 0x929
-; X64-NEXT: movzbl %dl, %eax
-; X64-NEXT: cmovael %ecx, %eax
+; X64-NEXT: cmpl $2345, %edx # imm = 0x929
+; X64-NEXT: cmovael %edx, %eax
; X64-NEXT: retq
%absx = call i32 @llvm.abs.i32(i32 %x, i1 true)
%foo = xor i32 %absx, 12312
@@ -120,21 +114,18 @@ define i32 @eq_or_with_dom_abs_non_po2(i32 %x) nounwind {
define i8 @ne_and_with_dom_abs_non_pow2(i8 %x) nounwind {
; X86-LABEL: ne_and_with_dom_abs_non_pow2:
; X86: # %bb.0:
-; X86-NEXT: movzbl {{[0-9]+}}(%esp), %edx
-; X86-NEXT: movl %edx, %ecx
-; X86-NEXT: sarb $7, %cl
-; X86-NEXT: movl %edx, %eax
-; X86-NEXT: xorb %cl, %al
-; X86-NEXT: subb %cl, %al
+; X86-NEXT: movzbl {{[0-9]+}}(%esp), %ecx
+; X86-NEXT: movl %ecx, %eax
+; X86-NEXT: sarb $7, %al
+; X86-NEXT: xorb %al, %cl
+; X86-NEXT: subb %al, %cl
+; X86-NEXT: movl %ecx, %eax
; X86-NEXT: xorb $12, %al
-; X86-NEXT: cmpb $121, %dl
+; X86-NEXT: cmpb $121, %cl
; X86-NEXT: setne %cl
-; X86-NEXT: cmpb $-121, %dl
-; X86-NEXT: setne %dl
; X86-NEXT: cmpb $24, %al
; X86-NEXT: jae .LBB2_2
; X86-NEXT: # %bb.1:
-; X86-NEXT: andb %dl, %cl
; X86-NEXT: movl %ecx, %eax
; X86-NEXT: .LBB2_2:
; X86-NEXT: retl
@@ -143,19 +134,16 @@ define i8 @ne_and_with_dom_abs_non_pow2(i8 %x) nounwind {
; X64: # %bb.0:
; X64-NEXT: movl %edi, %eax
; X64-NEXT: sarb $7, %al
+; X64-NEXT: xorb %al, %dil
+; X64-NEXT: subb %al, %dil
; X64-NEXT: movl %edi, %ecx
-; X64-NEXT: xorb %al, %cl
-; X64-NEXT: subb %al, %cl
; X64-NEXT: xorb $12, %cl
+; X64-NEXT: xorl %eax, %eax
; X64-NEXT: cmpb $121, %dil
; X64-NEXT: setne %al
-; X64-NEXT: cmpb $-121, %dil
-; X64-NEXT: setne %dl
-; X64-NEXT: andb %al, %dl
; X64-NEXT: cmpb $24, %cl
-; X64-NEXT: movzbl %dl, %edx
-; X64-NEXT: movzbl %cl, %eax
-; X64-NEXT: cmovbl %edx, %eax
+; X64-NEXT: movzbl %cl, %ecx
+; X64-NEXT: cmovael %ecx, %eax
; X64-NEXT: # kill: def $al killed $al killed $eax
; X64-NEXT: retq
%absx = call i8 @llvm.abs.i8(i8 %x, i1 true)
@@ -173,16 +161,16 @@ define i16 @ne_and_with_dom_abs(i16 %x) nounwind {
; X86-LABEL: ne_and_with_dom_abs:
; X86: # %bb.0:
; X86-NEXT: pushl %esi
-; X86-NEXT: movzwl {{[0-9]+}}(%esp), %eax
-; X86-NEXT: movswl %ax, %ecx
+; X86-NEXT: movswl {{[0-9]+}}(%esp), %eax
+; X86-NEXT: movl %eax, %ecx
; X86-NEXT: sarl $15, %ecx
-; X86-NEXT: leal 64(%eax), %edx
; X86-NEXT: xorl %ecx, %eax
; X86-NEXT: subl %ecx, %eax
+; X86-NEXT: movl %eax, %edx
; X86-NEXT: xorl $12312, %eax # imm = 0x3018
; X86-NEXT: movzwl %ax, %esi
; X86-NEXT: xorl %ecx, %ecx
-; X86-NEXT: testl $65407, %edx # imm = 0xFF7F
+; X86-NEXT: cmpw $64, %dx
; X86-NEXT: setne %dl
; X86-NEXT: cmpl $2345, %esi # imm = 0x929
; X86-NEXT: jae .LBB3_2
@@ -199,14 +187,14 @@ define i16 @ne_and_with_dom_abs(i16 %x) nounwind {
; X64-NEXT: movl %edi, %ecx
; X64-NEXT: negw %cx
; X64-NEXT: cmovsw %di, %cx
-; X64-NEXT: xorl $12312, %ecx # imm = 0x3018
-; X64-NEXT: movzwl %cx, %edx
-; X64-NEXT: addl $64, %edi
+; X64-NEXT: movl %ecx, %edx
+; X64-NEXT: xorl $12312, %edx # imm = 0x3018
+; X64-NEXT: movzwl %dx, %esi
; X64-NEXT: xorl %eax, %eax
-; X64-NEXT: testl $65407, %edi # imm = 0xFF7F
+; X64-NEXT: cmpw $64, %cx
; X64-NEXT: setne %al
-; X64-NEXT: cmpl $2345, %edx # imm = 0x929
-; X64-NEXT: cmovael %ecx, %eax
+; X64-NEXT: cmpl $2345, %esi # imm = 0x929
+; X64-NEXT: cmovael %edx, %eax
; X64-NEXT: # kill: def $ax killed $ax killed $eax
; X64-NEXT: retq
%absx = call i16 @llvm.abs.i16(i16 %x, i1 true)
>From 241701fa6f04d8e6ec2ab3a9e0a17a589341fb0d Mon Sep 17 00:00:00 2001
From: aryanmagoon <amagoon at nvidia.com>
Date: Fri, 20 Mar 2026 22:01:45 +0000
Subject: [PATCH 4/6] fixup! fixup! fixup! SelectionDAG: Preserve poison for
abs INT_MIN lowering. Update affected CodeGen lit tests and added a new lit
test for this behavior.
---
llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp | 11 +---
.../lib/CodeGen/SelectionDAG/SelectionDAG.cpp | 2 +-
llvm/test/CodeGen/X86/icmp-abs-C.ll | 56 +++++++++++++++++--
3 files changed, 55 insertions(+), 14 deletions(-)
diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
index 026da9eb10ec3..e93094e544bbd 100644
--- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
@@ -6916,20 +6916,13 @@ static SDValue foldAndOrOfSETCC(SDNode *LogicOp, SelectionDAG &DAG) {
// case this is just a compare).
if (APLhs == (-APRhs) &&
((TargetPreference & AndOrSETCCFoldKind::ABS) ||
- DAG.doesNodeExist(ISD::ABS, DAG.getVTList(OpVT), {LHS0}) ||
- DAG.doesNodeExist(ISD::ABS_MIN_POISON, DAG.getVTList(OpVT),
- {LHS0}))) {
+ DAG.doesNodeExist(ISD::ABS, DAG.getVTList(OpVT), {LHS0}))) {
const APInt &C = APLhs.isNegative() ? APRhs : APLhs;
// (icmp eq A, C) | (icmp eq A, -C)
// -> (icmp eq Abs(A), C)
// (icmp ne A, C) & (icmp ne A, -C)
// -> (icmp ne Abs(A), C)
- // Use ABS_MIN_POISON if that node already exists, otherwise ABS.
- unsigned AbsOpc =
- DAG.doesNodeExist(ISD::ABS_MIN_POISON, DAG.getVTList(OpVT), {LHS0})
- ? ISD::ABS_MIN_POISON
- : ISD::ABS;
- SDValue AbsOp = DAG.getNode(AbsOpc, DL, OpVT, LHS0);
+ SDValue AbsOp = DAG.getNode(ISD::ABS, DL, OpVT, LHS0);
return DAG.getNode(ISD::SETCC, DL, VT, AbsOp,
DAG.getConstant(C, DL, OpVT), LHS.getOperand(2));
} else if (TargetPreference &
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
index d71ae66178430..c2dbe0dd70f8d 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
@@ -6349,7 +6349,7 @@ bool SelectionDAG::isKnownNeverZero(SDValue Op, const APInt &DemandedElts,
case ISD::CTPOP:
case ISD::ABS:
case ISD::ABS_MIN_POISON:
- return isKnownNeverZero(Op.getOperand(0), Depth + 1);
+ return isKnownNeverZero(Op.getOperand(0), DemandedElts, Depth + 1);
case ISD::SRA:
case ISD::SRL: {
diff --git a/llvm/test/CodeGen/X86/icmp-abs-C.ll b/llvm/test/CodeGen/X86/icmp-abs-C.ll
index 71893a9e4be67..5e2ccb4a51174 100644
--- a/llvm/test/CodeGen/X86/icmp-abs-C.ll
+++ b/llvm/test/CodeGen/X86/icmp-abs-C.ll
@@ -55,7 +55,7 @@ define i64 @eq_or_with_dom_abs(i64 %x) nounwind {
; X64-NEXT: cmpq $2345, %rdx # imm = 0x929
; X64-NEXT: cmovaeq %rdx, %rax
; X64-NEXT: retq
- %absx = call i64 @llvm.abs.i64(i64 %x, i1 true)
+ %absx = call i64 @llvm.abs.i64(i64 %x, i1 false)
%foo = xor i64 %absx, 12312
%bar = icmp ugt i64 %foo, 2344
%cmp0 = icmp eq i64 %x, 64
@@ -100,7 +100,7 @@ define i32 @eq_or_with_dom_abs_non_po2(i32 %x) nounwind {
; X64-NEXT: cmpl $2345, %edx # imm = 0x929
; X64-NEXT: cmovael %edx, %eax
; X64-NEXT: retq
- %absx = call i32 @llvm.abs.i32(i32 %x, i1 true)
+ %absx = call i32 @llvm.abs.i32(i32 %x, i1 false)
%foo = xor i32 %absx, 12312
%bar = icmp ugt i32 %foo, 2344
%cmp0 = icmp eq i32 %x, 123
@@ -146,7 +146,7 @@ define i8 @ne_and_with_dom_abs_non_pow2(i8 %x) nounwind {
; X64-NEXT: cmovael %ecx, %eax
; X64-NEXT: # kill: def $al killed $al killed $eax
; X64-NEXT: retq
- %absx = call i8 @llvm.abs.i8(i8 %x, i1 true)
+ %absx = call i8 @llvm.abs.i8(i8 %x, i1 false)
%foo = xor i8 %absx, 12
%bar = icmp ugt i8 %foo, 23
%cmp0 = icmp ne i8 %x, 121
@@ -197,7 +197,7 @@ define i16 @ne_and_with_dom_abs(i16 %x) nounwind {
; X64-NEXT: cmovael %edx, %eax
; X64-NEXT: # kill: def $ax killed $ax killed $eax
; X64-NEXT: retq
- %absx = call i16 @llvm.abs.i16(i16 %x, i1 true)
+ %absx = call i16 @llvm.abs.i16(i16 %x, i1 false)
%foo = xor i16 %absx, 12312
%bar = icmp ugt i16 %foo, 2344
%cmp0 = icmp ne i16 %x, 64
@@ -207,3 +207,51 @@ define i16 @ne_and_with_dom_abs(i16 %x) nounwind {
%r = select i1 %bar, i16 %foo, i16 %cmp64
ret i16 %r
}
+
+; X86-LABEL: eq_or_with_dom_abs_min_poison_non_po2:
+; X86: # %bb.0:
+; X86-NEXT: movl {{[0-9]+}}(%esp), %edx
+; X86-NEXT: movl %edx, %ecx
+; X86-NEXT: sarl $31, %ecx
+; X86-NEXT: movl %edx, %eax
+; X86-NEXT: xorl %ecx, %eax
+; X86-NEXT: subl %ecx, %eax
+; X86-NEXT: xorl $12312, %eax # imm = 0x3018
+; X86-NEXT: cmpl $123, %edx
+; X86-NEXT: sete %cl
+; X86-NEXT: cmpl $-123, %edx
+; X86-NEXT: sete %dl
+; X86-NEXT: cmpl $2345, %eax # imm = 0x929
+; X86-NEXT: jae .LBB4_2
+; X86-NEXT: # %bb.1:
+; X86-NEXT: orb %dl, %cl
+; X86-NEXT: movzbl %cl, %eax
+; X86-NEXT: .LBB4_2:
+; X86-NEXT: retl
+;
+; X64-LABEL: eq_or_with_dom_abs_min_poison_non_po2:
+; X64: # %bb.0:
+; X64-NEXT: movl %edi, %ecx
+; X64-NEXT: negl %ecx
+; X64-NEXT: cmovsl %edi, %ecx
+; X64-NEXT: xorl $12312, %ecx # imm = 0x3018
+; X64-NEXT: cmpl $123, %edi
+; X64-NEXT: sete %al
+; X64-NEXT: cmpl $-123, %edi
+; X64-NEXT: sete %dl
+; X64-NEXT: orb %al, %dl
+; X64-NEXT: cmpl $2345, %ecx # imm = 0x929
+; X64-NEXT: movzbl %dl, %eax
+; X64-NEXT: cmovael %ecx, %eax
+; X64-NEXT: retq
+define i32 @eq_or_with_dom_abs_min_poison_non_po2(i32 %x) nounwind {
+ %absx = call i32 @llvm.abs.i32(i32 %x, i1 true)
+ %foo = xor i32 %absx, 12312
+ %bar = icmp ugt i32 %foo, 2344
+ %cmp0 = icmp eq i32 %x, 123
+ %cmp1 = icmp eq i32 %x, -123
+ %cmp = or i1 %cmp0, %cmp1
+ %cmp64 = zext i1 %cmp to i32
+ %r = select i1 %bar, i32 %foo, i32 %cmp64
+ ret i32 %r
+}
>From b23666fe07c2629ddcb72a3be4a2c0db4d50ca39 Mon Sep 17 00:00:00 2001
From: aryanmagoon <amagoon at nvidia.com>
Date: Tue, 24 Mar 2026 21:04:33 +0000
Subject: [PATCH 5/6] fixup! fixup! fixup! fixup! SelectionDAG: Preserve poison
for abs INT_MIN lowering. Update affected CodeGen lit tests and added a new
lit test for this behavior.
---
llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp | 7 ++++---
.../SelectionDAG/LegalizeIntegerTypes.cpp | 17 +++++++++--------
.../lib/CodeGen/SelectionDAG/TargetLowering.cpp | 3 +--
llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 3 +--
4 files changed, 15 insertions(+), 15 deletions(-)
diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
index e93094e544bbd..20237d15561cc 100644
--- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
@@ -11930,7 +11930,7 @@ SDValue DAGCombiner::visitABS(SDNode *N) {
if (SDValue C = DAG.FoldConstantArithmetic(ISD::ABS, DL, VT, {N0}))
return C;
// fold (abs (abs x)) -> (abs x)
- // fold (abs (abs_min_poison x)) -> (abs x)
+ // fold (abs (abs_min_poison x)) -> (abs_min_poison x)
if (N0.getOpcode() == ISD::ABS || N0.getOpcode() == ISD::ABS_MIN_POISON)
return N0;
// fold (abs x) -> x iff not-negative
@@ -15389,7 +15389,8 @@ static SDValue widenCtPop(SDNode *Extend, SelectionDAG &DAG, const SDLoc &DL) {
}
// If we have (zext (abs X)) where X is a type that will be promoted by type
-// legalization, convert to (abs (sext X)). But don't extend past a legal type.
+// legalization, convert to (abs_min_poison (sext X)). But do not extend
+// past a legal type.
static SDValue widenAbs(SDNode *Extend, SelectionDAG &DAG) {
assert(Extend->getOpcode() == ISD::ZERO_EXTEND && "Expected zero extend.");
@@ -15413,7 +15414,7 @@ static SDValue widenAbs(SDNode *Extend, SelectionDAG &DAG) {
SDValue SExt =
DAG.getNode(ISD::SIGN_EXTEND, SDLoc(Abs), LegalVT, Abs.getOperand(0));
- SDValue NewAbs = DAG.getNode(AbsOpc, SDLoc(Abs), LegalVT, SExt);
+ SDValue NewAbs = DAG.getNode(ISD::ABS_MIN_POISON, SDLoc(Abs), LegalVT, SExt);
return DAG.getZExtOrTrunc(NewAbs, SDLoc(Extend), VT);
}
diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp
index a37af675dce95..26a9ad52929d3 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp
@@ -1872,7 +1872,6 @@ SDValue DAGTypeLegalizer::PromoteIntRes_SADDSUBO_CARRY(SDNode *N,
}
SDValue DAGTypeLegalizer::PromoteIntRes_ABS(SDNode *N) {
- unsigned Opc = N->getOpcode();
EVT OVT = N->getValueType(0);
EVT NVT = TLI.getTypeToTransformTo(*DAG.getContext(), OVT);
@@ -1888,7 +1887,7 @@ SDValue DAGTypeLegalizer::PromoteIntRes_ABS(SDNode *N) {
}
SDValue Op0 = SExtPromotedInteger(N->getOperand(0));
- return DAG.getNode(Opc, SDLoc(N), Op0.getValueType(), Op0);
+ return DAG.getNode(ISD::ABS_MIN_POISON, SDLoc(N), Op0.getValueType(), Op0);
}
SDValue DAGTypeLegalizer::PromoteIntRes_XMULO(SDNode *N, unsigned ResNo) {
@@ -4122,12 +4121,14 @@ void DAGTypeLegalizer::ExpandIntRes_ABS(SDNode *N, SDValue &Lo, SDValue &Hi) {
EVT NVT = Lo.getValueType();
// If the upper half is all sign bits, then we can perform the ABS on the
- // lower half and zero-extend. We must use ISD::ABS here (not ABS_MIN_POISON)
- // because the original poison contract is for INT_MIN of the wider type,
- // but the lower half may be INT_MIN of the narrower type for a valid
- // (non-INT_MIN) input of the original type.
- if (DAG.ComputeNumSignBits(N0) > NVT.getScalarSizeInBits()) {
- Lo = DAG.getNode(ISD::ABS, dl, NVT, Lo);
+ // lower half and zero-extend. We could use ISD::ABS_MIN_POISON here if
+ // DAG.ComputeNumSignBits(N0) is larger than NVT.getScalarSizeInBits() + 1.
+ unsigned NumSignBits = DAG.ComputeNumSignBits(N0);
+ if (NumSignBits > NVT.getScalarSizeInBits()) {
+ unsigned AbsOpc = NumSignBits > NVT.getScalarSizeInBits() + 1
+ ? ISD::ABS_MIN_POISON
+ : ISD::ABS;
+ Lo = DAG.getNode(AbsOpc, dl, NVT, Lo);
Hi = DAG.getConstant(0, dl, NVT);
return;
}
diff --git a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
index f6b4542e69159..541ca37e16af6 100644
--- a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
@@ -10078,8 +10078,7 @@ SDValue TargetLowering::expandABS(SDNode *N, SelectionDAG &DAG,
isOperationLegalOrCustom(ISD::ABS, VT)) {
SDValue AbsVal = DAG.getNode(ISD::ABS, dl, VT, Op);
if (IsNegative)
- return DAG.getNode(ISD::SUB, dl, VT, DAG.getConstant(0, dl, VT),
- AbsVal);
+ return DAG.getNegative(AbsVal, dl, VT);
return AbsVal;
}
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index 90b504f74ce3f..3f10a5981bdce 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -21725,9 +21725,8 @@ SDValue RISCVTargetLowering::PerformDAGCombine(SDNode *N,
if (VT.isVector() && N0.hasOneUse() && N0.getOpcode() == ISD::SIGN_EXTEND) {
SDValue Src = N0.getOperand(0);
SDLoc DL(N);
- unsigned Opc = N->getOpcode();
return DAG.getNode(ISD::ZERO_EXTEND, DL, VT,
- DAG.getNode(Opc, DL, Src.getValueType(), Src));
+ DAG.getNode(ISD::ABS, DL, Src.getValueType(), Src));
}
break;
}
>From 1ff4bfe6482212da560c56e5d5b2cab34b32d0d1 Mon Sep 17 00:00:00 2001
From: aryanmagoon <amagoon at nvidia.com>
Date: Tue, 24 Mar 2026 23:53:48 +0000
Subject: [PATCH 6/6] fixup! fixup! fixup! fixup! fixup! SelectionDAG: Preserve
poison for abs INT_MIN lowering. Update affected CodeGen lit tests and added
a new lit test for this behavior.
---
llvm/test/CodeGen/X86/icmp-abs-C.ll | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/llvm/test/CodeGen/X86/icmp-abs-C.ll b/llvm/test/CodeGen/X86/icmp-abs-C.ll
index 5e2ccb4a51174..4773a369326f7 100644
--- a/llvm/test/CodeGen/X86/icmp-abs-C.ll
+++ b/llvm/test/CodeGen/X86/icmp-abs-C.ll
@@ -208,7 +208,8 @@ define i16 @ne_and_with_dom_abs(i16 %x) nounwind {
ret i16 %r
}
-; X86-LABEL: eq_or_with_dom_abs_min_poison_non_po2:
+define i32 @eq_or_with_dom_abs_min_poison_non_pow2(i32 %x) nounwind {
+; X86-LABEL: eq_or_with_dom_abs_min_poison_non_pow2:
; X86: # %bb.0:
; X86-NEXT: movl {{[0-9]+}}(%esp), %edx
; X86-NEXT: movl %edx, %ecx
@@ -229,7 +230,7 @@ define i16 @ne_and_with_dom_abs(i16 %x) nounwind {
; X86-NEXT: .LBB4_2:
; X86-NEXT: retl
;
-; X64-LABEL: eq_or_with_dom_abs_min_poison_non_po2:
+; X64-LABEL: eq_or_with_dom_abs_min_poison_non_pow2:
; X64: # %bb.0:
; X64-NEXT: movl %edi, %ecx
; X64-NEXT: negl %ecx
@@ -244,7 +245,6 @@ define i16 @ne_and_with_dom_abs(i16 %x) nounwind {
; X64-NEXT: movzbl %dl, %eax
; X64-NEXT: cmovael %ecx, %eax
; X64-NEXT: retq
-define i32 @eq_or_with_dom_abs_min_poison_non_po2(i32 %x) nounwind {
%absx = call i32 @llvm.abs.i32(i32 %x, i1 true)
%foo = xor i32 %absx, 12312
%bar = icmp ugt i32 %foo, 2344
More information about the llvm-commits
mailing list