Skip to content

Commit ae88d88

Browse files
rotaterighttstellar
authored andcommitted
[SDAG] move x86 select-with-identity-constant fold behind a target hook; NFC
This is no-functional-change-intended because only the x86 target enables the TLI hook currently. We can add fmul/fdiv opcodes to the switch similar to the proposal D119111, but we don't need to make other changes like enabling target-specific combines. We can also add integer opcodes (add, or, shl, etc.) to the switch because this function is called from all of the generic binary opcodes. The goal is to incrementally enable the profitable diffs from D90113 while avoiding regressions. Differential Revision: https://reviews.llvm.org/D119150 (cherry picked from commit a68e098)
1 parent 92f6212 commit ae88d88

File tree

4 files changed

+92
-83
lines changed

4 files changed

+92
-83
lines changed

llvm/include/llvm/CodeGen/TargetLowering.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2851,6 +2851,14 @@ class TargetLoweringBase {
28512851
return false;
28522852
}
28532853

2854+
/// Return true if pulling a binary operation into a select with an identity
2855+
/// constant is profitable. This is the inverse of an IR transform.
2856+
/// Example: X + (Cond ? Y : 0) --> Cond ? (X + Y) : X
2857+
virtual bool shouldFoldSelectWithIdentityConstant(unsigned BinOpcode,
2858+
EVT VT) const {
2859+
return false;
2860+
}
2861+
28542862
/// Return true if it is beneficial to convert a load of a constant to
28552863
/// just the constant itself.
28562864
/// On some targets it might be more efficient to use a combination of

llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp

Lines changed: 67 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2101,10 +2101,77 @@ static bool canFoldInAddressingMode(SDNode *N, SDNode *Use, SelectionDAG &DAG,
21012101
VT.getTypeForEVT(*DAG.getContext()), AS);
21022102
}
21032103

2104+
/// This inverts a canonicalization in IR that replaces a variable select arm
2105+
/// with an identity constant. Codegen improves if we re-use the variable
2106+
/// operand rather than load a constant. This can also be converted into a
2107+
/// masked vector operation if the target supports it.
2108+
static SDValue foldSelectWithIdentityConstant(SDNode *N, SelectionDAG &DAG,
2109+
bool ShouldCommuteOperands) {
2110+
// Match a select as operand 1. The identity constant that we are looking for
2111+
// is only valid as operand 1 of a non-commutative binop.
2112+
SDValue N0 = N->getOperand(0);
2113+
SDValue N1 = N->getOperand(1);
2114+
if (ShouldCommuteOperands)
2115+
std::swap(N0, N1);
2116+
2117+
// TODO: Should this apply to scalar select too?
2118+
if (!N1.hasOneUse() || N1.getOpcode() != ISD::VSELECT)
2119+
return SDValue();
2120+
2121+
unsigned Opcode = N->getOpcode();
2122+
EVT VT = N->getValueType(0);
2123+
SDValue Cond = N1.getOperand(0);
2124+
SDValue TVal = N1.getOperand(1);
2125+
SDValue FVal = N1.getOperand(2);
2126+
2127+
// TODO: The cases should match with IR's ConstantExpr::getBinOpIdentity().
2128+
// TODO: Target-specific opcodes could be added. Ex: "isCommutativeBinOp()".
2129+
// TODO: With fast-math (NSZ), allow the opposite-sign form of zero?
2130+
auto isIdentityConstantForOpcode = [](unsigned Opcode, SDValue V) {
2131+
if (ConstantFPSDNode *C = isConstOrConstSplatFP(V)) {
2132+
switch (Opcode) {
2133+
case ISD::FADD: // X + -0.0 --> X
2134+
return C->isZero() && C->isNegative();
2135+
case ISD::FSUB: // X - 0.0 --> X
2136+
return C->isZero() && !C->isNegative();
2137+
}
2138+
}
2139+
return false;
2140+
};
2141+
2142+
// This transform increases uses of N0, so freeze it to be safe.
2143+
// binop N0, (vselect Cond, IDC, FVal) --> vselect Cond, N0, (binop N0, FVal)
2144+
if (isIdentityConstantForOpcode(Opcode, TVal)) {
2145+
SDValue F0 = DAG.getFreeze(N0);
2146+
SDValue NewBO = DAG.getNode(Opcode, SDLoc(N), VT, F0, FVal, N->getFlags());
2147+
return DAG.getSelect(SDLoc(N), VT, Cond, F0, NewBO);
2148+
}
2149+
// binop N0, (vselect Cond, TVal, IDC) --> vselect Cond, (binop N0, TVal), N0
2150+
if (isIdentityConstantForOpcode(Opcode, FVal)) {
2151+
SDValue F0 = DAG.getFreeze(N0);
2152+
SDValue NewBO = DAG.getNode(Opcode, SDLoc(N), VT, F0, TVal, N->getFlags());
2153+
return DAG.getSelect(SDLoc(N), VT, Cond, NewBO, F0);
2154+
}
2155+
2156+
return SDValue();
2157+
}
2158+
21042159
SDValue DAGCombiner::foldBinOpIntoSelect(SDNode *BO) {
21052160
assert(TLI.isBinOp(BO->getOpcode()) && BO->getNumValues() == 1 &&
21062161
"Unexpected binary operator");
21072162

2163+
const TargetLowering &TLI = DAG.getTargetLoweringInfo();
2164+
auto BinOpcode = BO->getOpcode();
2165+
EVT VT = BO->getValueType(0);
2166+
if (TLI.shouldFoldSelectWithIdentityConstant(BinOpcode, VT)) {
2167+
if (SDValue Sel = foldSelectWithIdentityConstant(BO, DAG, false))
2168+
return Sel;
2169+
2170+
if (TLI.isCommutativeBinOp(BO->getOpcode()))
2171+
if (SDValue Sel = foldSelectWithIdentityConstant(BO, DAG, true))
2172+
return Sel;
2173+
}
2174+
21082175
// Don't do this unless the old select is going away. We want to eliminate the
21092176
// binary operator, not replace a binop with a select.
21102177
// TODO: Handle ISD::SELECT_CC.
@@ -2133,7 +2200,6 @@ SDValue DAGCombiner::foldBinOpIntoSelect(SDNode *BO) {
21332200
// propagate non constant operands into select. I.e.:
21342201
// and (select Cond, 0, -1), X --> select Cond, 0, X
21352202
// or X, (select Cond, -1, 0) --> select Cond, -1, X
2136-
auto BinOpcode = BO->getOpcode();
21372203
bool CanFoldNonConst =
21382204
(BinOpcode == ISD::AND || BinOpcode == ISD::OR) &&
21392205
(isNullOrNullSplat(CT) || isAllOnesOrAllOnesSplat(CT)) &&
@@ -2145,8 +2211,6 @@ SDValue DAGCombiner::foldBinOpIntoSelect(SDNode *BO) {
21452211
!DAG.isConstantFPBuildVectorOrConstantFP(CBO))
21462212
return SDValue();
21472213

2148-
EVT VT = BO->getValueType(0);
2149-
21502214
// We have a select-of-constants followed by a binary operator with a
21512215
// constant. Eliminate the binop by pulling the constant math into the select.
21522216
// Example: add (select Cond, CT, CF), CBO --> select Cond, CT + CBO, CF + CBO

llvm/lib/Target/X86/X86ISelLowering.cpp

Lines changed: 14 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -33418,6 +33418,20 @@ bool X86TargetLowering::isNarrowingProfitable(EVT VT1, EVT VT2) const {
3341833418
return !(VT1 == MVT::i32 && VT2 == MVT::i16);
3341933419
}
3342033420

33421+
bool X86TargetLowering::shouldFoldSelectWithIdentityConstant(unsigned Opcode,
33422+
EVT VT) const {
33423+
// TODO: This is too general. There are cases where pre-AVX512 codegen would
33424+
// benefit. The transform may also be profitable for scalar code.
33425+
if (!Subtarget.hasAVX512())
33426+
return false;
33427+
if (!Subtarget.hasVLX() && !VT.is512BitVector())
33428+
return false;
33429+
if (!VT.isVector())
33430+
return false;
33431+
33432+
return true;
33433+
}
33434+
3342133435
/// Targets can use this to indicate that they only support *some*
3342233436
/// VECTOR_SHUFFLE operations, those with specific masks.
3342333437
/// By default, if a target supports the VECTOR_SHUFFLE node, all mask values
@@ -48920,83 +48934,6 @@ static SDValue combineFaddCFmul(SDNode *N, SelectionDAG &DAG,
4892048934
return DAG.getBitcast(VT, CFmul);
4892148935
}
4892248936

48923-
/// This inverts a canonicalization in IR that replaces a variable select arm
48924-
/// with an identity constant. Codegen improves if we re-use the variable
48925-
/// operand rather than load a constant. This can also be converted into a
48926-
/// masked vector operation if the target supports it.
48927-
static SDValue foldSelectWithIdentityConstant(SDNode *N, SelectionDAG &DAG,
48928-
bool ShouldCommuteOperands) {
48929-
// Match a select as operand 1. The identity constant that we are looking for
48930-
// is only valid as operand 1 of a non-commutative binop.
48931-
SDValue N0 = N->getOperand(0);
48932-
SDValue N1 = N->getOperand(1);
48933-
if (ShouldCommuteOperands)
48934-
std::swap(N0, N1);
48935-
48936-
// TODO: Should this apply to scalar select too?
48937-
if (!N1.hasOneUse() || N1.getOpcode() != ISD::VSELECT)
48938-
return SDValue();
48939-
48940-
unsigned Opcode = N->getOpcode();
48941-
EVT VT = N->getValueType(0);
48942-
SDValue Cond = N1.getOperand(0);
48943-
SDValue TVal = N1.getOperand(1);
48944-
SDValue FVal = N1.getOperand(2);
48945-
48946-
// TODO: This (and possibly the entire function) belongs in a
48947-
// target-independent location with target hooks.
48948-
// TODO: The cases should match with IR's ConstantExpr::getBinOpIdentity().
48949-
// TODO: With fast-math (NSZ), allow the opposite-sign form of zero?
48950-
auto isIdentityConstantForOpcode = [](unsigned Opcode, SDValue V) {
48951-
if (ConstantFPSDNode *C = isConstOrConstSplatFP(V)) {
48952-
switch (Opcode) {
48953-
case ISD::FADD: // X + -0.0 --> X
48954-
return C->isZero() && C->isNegative();
48955-
case ISD::FSUB: // X - 0.0 --> X
48956-
return C->isZero() && !C->isNegative();
48957-
}
48958-
}
48959-
return false;
48960-
};
48961-
48962-
// This transform increases uses of N0, so freeze it to be safe.
48963-
// binop N0, (vselect Cond, IDC, FVal) --> vselect Cond, N0, (binop N0, FVal)
48964-
if (isIdentityConstantForOpcode(Opcode, TVal)) {
48965-
SDValue F0 = DAG.getFreeze(N0);
48966-
SDValue NewBO = DAG.getNode(Opcode, SDLoc(N), VT, F0, FVal, N->getFlags());
48967-
return DAG.getSelect(SDLoc(N), VT, Cond, F0, NewBO);
48968-
}
48969-
// binop N0, (vselect Cond, TVal, IDC) --> vselect Cond, (binop N0, TVal), N0
48970-
if (isIdentityConstantForOpcode(Opcode, FVal)) {
48971-
SDValue F0 = DAG.getFreeze(N0);
48972-
SDValue NewBO = DAG.getNode(Opcode, SDLoc(N), VT, F0, TVal, N->getFlags());
48973-
return DAG.getSelect(SDLoc(N), VT, Cond, NewBO, F0);
48974-
}
48975-
48976-
return SDValue();
48977-
}
48978-
48979-
static SDValue combineBinopWithSelect(SDNode *N, SelectionDAG &DAG,
48980-
const X86Subtarget &Subtarget) {
48981-
// TODO: This is too general. There are cases where pre-AVX512 codegen would
48982-
// benefit. The transform may also be profitable for scalar code.
48983-
if (!Subtarget.hasAVX512())
48984-
return SDValue();
48985-
48986-
if (!Subtarget.hasVLX() && !N->getValueType(0).is512BitVector())
48987-
return SDValue();
48988-
48989-
if (SDValue Sel = foldSelectWithIdentityConstant(N, DAG, false))
48990-
return Sel;
48991-
48992-
const TargetLowering &TLI = DAG.getTargetLoweringInfo();
48993-
if (TLI.isCommutativeBinOp(N->getOpcode()))
48994-
if (SDValue Sel = foldSelectWithIdentityConstant(N, DAG, true))
48995-
return Sel;
48996-
48997-
return SDValue();
48998-
}
48999-
4900048937
/// Do target-specific dag combines on floating-point adds/subs.
4900148938
static SDValue combineFaddFsub(SDNode *N, SelectionDAG &DAG,
4900248939
const X86Subtarget &Subtarget) {
@@ -49006,9 +48943,6 @@ static SDValue combineFaddFsub(SDNode *N, SelectionDAG &DAG,
4900648943
if (SDValue COp = combineFaddCFmul(N, DAG, Subtarget))
4900748944
return COp;
4900848945

49009-
if (SDValue Sel = combineBinopWithSelect(N, DAG, Subtarget))
49010-
return Sel;
49011-
4901248946
return SDValue();
4901348947
}
4901448948

llvm/lib/Target/X86/X86ISelLowering.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1288,6 +1288,9 @@ namespace llvm {
12881288
/// from i32 to i8 but not from i32 to i16.
12891289
bool isNarrowingProfitable(EVT VT1, EVT VT2) const override;
12901290

1291+
bool shouldFoldSelectWithIdentityConstant(unsigned BinOpcode,
1292+
EVT VT) const override;
1293+
12911294
/// Given an intrinsic, checks if on the target the intrinsic will need to map
12921295
/// to a MemIntrinsicNode (touches memory). If this is the case, it returns
12931296
/// true and stores the intrinsic information into the IntrinsicInfo that was

0 commit comments

Comments
 (0)