[DAG][X86] Convert isNegatibleForFree/GetNegatedExpression to a target hook (PR42863)

This patch converts the DAGCombine isNegatibleForFree/GetNegatedExpression into overridable TLI hooks.

The intention is to let us extend existing FNEG combines to work more generally with negatible float ops, allowing it work with target specific combines and opcodes (e.g. X86's FMA variants).

Unlike the SimplifyDemandedBits, we can't just handle target nodes through a Target callback, we need to do this as an override to allow targets to handle generic opcodes as well. This does mean that the target implementations has to duplicate some checks (recursion depth etc.).

Partial reversion of rL372756 - I've identified the infinite loop issue inside the X86 override but haven't fixed it yet so I've only (re)committed the common TargetLowering refactoring part of the patch.

Differential Revision: https://reviews.llvm.org/D67557

llvm-svn: 373343
This commit is contained in:
Simon Pilgrim 2019-10-01 15:32:04 +00:00
parent 796cd312ac
commit 3c912c4abe
3 changed files with 284 additions and 276 deletions

View File

@ -3386,6 +3386,18 @@ public:
llvm_unreachable("Not Implemented"); llvm_unreachable("Not Implemented");
} }
/// Return 1 if we can compute the negated form of the specified expression
/// for the same cost as the expression itself, or 2 if we can compute the
/// negated form more cheaply than the expression itself. Else return 0.
virtual char isNegatibleForFree(SDValue Op, SelectionDAG &DAG,
bool LegalOperations, bool ForCodeSize,
unsigned Depth = 0) const;
/// If isNegatibleForFree returns true, return the newly negated expression.
virtual SDValue getNegatedExpression(SDValue Op, SelectionDAG &DAG,
bool LegalOperations, bool ForCodeSize,
unsigned Depth = 0) const;
//===--------------------------------------------------------------------===// //===--------------------------------------------------------------------===//
// Lowering methods - These methods must be implemented by targets so that // Lowering methods - These methods must be implemented by targets so that
// the SelectionDAGBuilder code knows how to lower these. // the SelectionDAGBuilder code knows how to lower these.

View File

@ -785,252 +785,6 @@ void DAGCombiner::deleteAndRecombine(SDNode *N) {
DAG.DeleteNode(N); DAG.DeleteNode(N);
} }
/// Return 1 if we can compute the negated form of the specified expression for
/// the same cost as the expression itself, or 2 if we can compute the negated
/// form more cheaply than the expression itself.
static char isNegatibleForFree(SDValue Op, bool LegalOperations,
const TargetLowering &TLI,
const TargetOptions *Options,
bool ForCodeSize,
unsigned Depth = 0) {
// fneg is removable even if it has multiple uses.
if (Op.getOpcode() == ISD::FNEG)
return 2;
// Don't allow anything with multiple uses unless we know it is free.
EVT VT = Op.getValueType();
const SDNodeFlags Flags = Op->getFlags();
if (!Op.hasOneUse() &&
!(Op.getOpcode() == ISD::FP_EXTEND &&
TLI.isFPExtFree(VT, Op.getOperand(0).getValueType())))
return 0;
// Don't recurse exponentially.
if (Depth > SelectionDAG::MaxRecursionDepth)
return 0;
switch (Op.getOpcode()) {
default: return false;
case ISD::ConstantFP: {
if (!LegalOperations)
return 1;
// Don't invert constant FP values after legalization unless the target says
// the negated constant is legal.
return TLI.isOperationLegal(ISD::ConstantFP, VT) ||
TLI.isFPImmLegal(neg(cast<ConstantFPSDNode>(Op)->getValueAPF()), VT,
ForCodeSize);
}
case ISD::BUILD_VECTOR: {
// Only permit BUILD_VECTOR of constants.
if (llvm::any_of(Op->op_values(), [&](SDValue N) {
return !N.isUndef() && !isa<ConstantFPSDNode>(N);
}))
return 0;
if (!LegalOperations)
return 1;
if (TLI.isOperationLegal(ISD::ConstantFP, VT) &&
TLI.isOperationLegal(ISD::BUILD_VECTOR, VT))
return 1;
return llvm::all_of(Op->op_values(), [&](SDValue N) {
return N.isUndef() ||
TLI.isFPImmLegal(neg(cast<ConstantFPSDNode>(N)->getValueAPF()), VT,
ForCodeSize);
});
}
case ISD::FADD:
if (!Options->NoSignedZerosFPMath && !Flags.hasNoSignedZeros())
return 0;
// After operation legalization, it might not be legal to create new FSUBs.
if (LegalOperations && !TLI.isOperationLegalOrCustom(ISD::FSUB, VT))
return 0;
// fold (fneg (fadd A, B)) -> (fsub (fneg A), B)
if (char V = isNegatibleForFree(Op.getOperand(0), LegalOperations, TLI,
Options, ForCodeSize, Depth + 1))
return V;
// fold (fneg (fadd A, B)) -> (fsub (fneg B), A)
return isNegatibleForFree(Op.getOperand(1), LegalOperations, TLI, Options,
ForCodeSize, Depth + 1);
case ISD::FSUB:
// We can't turn -(A-B) into B-A when we honor signed zeros.
if (!Options->NoSignedZerosFPMath && !Flags.hasNoSignedZeros())
return 0;
// fold (fneg (fsub A, B)) -> (fsub B, A)
return 1;
case ISD::FMUL:
case ISD::FDIV:
// fold (fneg (fmul X, Y)) -> (fmul (fneg X), Y) or (fmul X, (fneg Y))
if (char V = isNegatibleForFree(Op.getOperand(0), LegalOperations, TLI,
Options, ForCodeSize, Depth + 1))
return V;
// Ignore X * 2.0 because that is expected to be canonicalized to X + X.
if (auto *C = isConstOrConstSplatFP(Op.getOperand(1)))
if (C->isExactlyValue(2.0) && Op.getOpcode() == ISD::FMUL)
return 0;
return isNegatibleForFree(Op.getOperand(1), LegalOperations, TLI, Options,
ForCodeSize, Depth + 1);
case ISD::FMA:
case ISD::FMAD: {
if (!Options->NoSignedZerosFPMath && !Flags.hasNoSignedZeros())
return 0;
// fold (fneg (fma X, Y, Z)) -> (fma (fneg X), Y, (fneg Z))
// fold (fneg (fma X, Y, Z)) -> (fma X, (fneg Y), (fneg Z))
char V2 = isNegatibleForFree(Op.getOperand(2), LegalOperations, TLI,
Options, ForCodeSize, Depth + 1);
if (!V2)
return 0;
// One of Op0/Op1 must be cheaply negatible, then select the cheapest.
char V0 = isNegatibleForFree(Op.getOperand(0), LegalOperations, TLI,
Options, ForCodeSize, Depth + 1);
char V1 = isNegatibleForFree(Op.getOperand(1), LegalOperations, TLI,
Options, ForCodeSize, Depth + 1);
char V01 = std::max(V0, V1);
return V01 ? std::max(V01, V2) : 0;
}
case ISD::FP_EXTEND:
case ISD::FP_ROUND:
case ISD::FSIN:
return isNegatibleForFree(Op.getOperand(0), LegalOperations, TLI, Options,
ForCodeSize, Depth + 1);
}
}
/// If isNegatibleForFree returns true, return the newly negated expression.
static SDValue GetNegatedExpression(SDValue Op, SelectionDAG &DAG,
bool LegalOperations, bool ForCodeSize,
unsigned Depth = 0) {
// fneg is removable even if it has multiple uses.
if (Op.getOpcode() == ISD::FNEG)
return Op.getOperand(0);
assert(Depth <= SelectionDAG::MaxRecursionDepth &&
"GetNegatedExpression doesn't match isNegatibleForFree");
const TargetOptions &Options = DAG.getTarget().Options;
const SDNodeFlags Flags = Op->getFlags();
switch (Op.getOpcode()) {
default: llvm_unreachable("Unknown code");
case ISD::ConstantFP: {
APFloat V = cast<ConstantFPSDNode>(Op)->getValueAPF();
V.changeSign();
return DAG.getConstantFP(V, SDLoc(Op), Op.getValueType());
}
case ISD::BUILD_VECTOR: {
SmallVector<SDValue, 4> Ops;
for (SDValue C : Op->op_values()) {
if (C.isUndef()) {
Ops.push_back(C);
continue;
}
APFloat V = cast<ConstantFPSDNode>(C)->getValueAPF();
V.changeSign();
Ops.push_back(DAG.getConstantFP(V, SDLoc(Op), C.getValueType()));
}
return DAG.getBuildVector(Op.getValueType(), SDLoc(Op), Ops);
}
case ISD::FADD:
assert((Options.NoSignedZerosFPMath || Flags.hasNoSignedZeros()) &&
"Expected NSZ fp-flag");
// fold (fneg (fadd A, B)) -> (fsub (fneg A), B)
if (isNegatibleForFree(Op.getOperand(0), LegalOperations,
DAG.getTargetLoweringInfo(), &Options, ForCodeSize,
Depth + 1))
return DAG.getNode(ISD::FSUB, SDLoc(Op), Op.getValueType(),
GetNegatedExpression(Op.getOperand(0), DAG,
LegalOperations, ForCodeSize,
Depth + 1),
Op.getOperand(1), Flags);
// fold (fneg (fadd A, B)) -> (fsub (fneg B), A)
return DAG.getNode(ISD::FSUB, SDLoc(Op), Op.getValueType(),
GetNegatedExpression(Op.getOperand(1), DAG,
LegalOperations, ForCodeSize,
Depth + 1),
Op.getOperand(0), Flags);
case ISD::FSUB:
// fold (fneg (fsub 0, B)) -> B
if (ConstantFPSDNode *N0CFP =
isConstOrConstSplatFP(Op.getOperand(0), /*AllowUndefs*/ true))
if (N0CFP->isZero())
return Op.getOperand(1);
// fold (fneg (fsub A, B)) -> (fsub B, A)
return DAG.getNode(ISD::FSUB, SDLoc(Op), Op.getValueType(),
Op.getOperand(1), Op.getOperand(0), Flags);
case ISD::FMUL:
case ISD::FDIV:
// fold (fneg (fmul X, Y)) -> (fmul (fneg X), Y)
if (isNegatibleForFree(Op.getOperand(0), LegalOperations,
DAG.getTargetLoweringInfo(), &Options, ForCodeSize,
Depth + 1))
return DAG.getNode(Op.getOpcode(), SDLoc(Op), Op.getValueType(),
GetNegatedExpression(Op.getOperand(0), DAG,
LegalOperations, ForCodeSize,
Depth + 1),
Op.getOperand(1), Flags);
// fold (fneg (fmul X, Y)) -> (fmul X, (fneg Y))
return DAG.getNode(Op.getOpcode(), SDLoc(Op), Op.getValueType(),
Op.getOperand(0),
GetNegatedExpression(Op.getOperand(1), DAG,
LegalOperations, ForCodeSize,
Depth + 1), Flags);
case ISD::FMA:
case ISD::FMAD: {
assert((Options.NoSignedZerosFPMath || Flags.hasNoSignedZeros()) &&
"Expected NSZ fp-flag");
SDValue Neg2 = GetNegatedExpression(Op.getOperand(2), DAG, LegalOperations,
ForCodeSize, Depth + 1);
char V0 = isNegatibleForFree(Op.getOperand(0), LegalOperations,
DAG.getTargetLoweringInfo(), &Options,
ForCodeSize, Depth + 1);
char V1 = isNegatibleForFree(Op.getOperand(1), LegalOperations,
DAG.getTargetLoweringInfo(), &Options,
ForCodeSize, Depth + 1);
if (V0 >= V1) {
// fold (fneg (fma X, Y, Z)) -> (fma (fneg X), Y, (fneg Z))
SDValue Neg0 = GetNegatedExpression(
Op.getOperand(0), DAG, LegalOperations, ForCodeSize, Depth + 1);
return DAG.getNode(Op.getOpcode(), SDLoc(Op), Op.getValueType(), Neg0,
Op.getOperand(1), Neg2, Flags);
}
// fold (fneg (fma X, Y, Z)) -> (fma X, (fneg Y), (fneg Z))
SDValue Neg1 = GetNegatedExpression(Op.getOperand(1), DAG, LegalOperations,
ForCodeSize, Depth + 1);
return DAG.getNode(Op.getOpcode(), SDLoc(Op), Op.getValueType(),
Op.getOperand(0), Neg1, Neg2, Flags);
}
case ISD::FP_EXTEND:
case ISD::FSIN:
return DAG.getNode(Op.getOpcode(), SDLoc(Op), Op.getValueType(),
GetNegatedExpression(Op.getOperand(0), DAG,
LegalOperations, ForCodeSize,
Depth + 1));
case ISD::FP_ROUND:
return DAG.getNode(ISD::FP_ROUND, SDLoc(Op), Op.getValueType(),
GetNegatedExpression(Op.getOperand(0), DAG,
LegalOperations, ForCodeSize,
Depth + 1),
Op.getOperand(1));
}
}
// APInts must be the same size for most operations, this helper // APInts must be the same size for most operations, this helper
// function zero extends the shorter of the pair so that they match. // function zero extends the shorter of the pair so that they match.
// We provide an Offset so that we can create bitwidths that won't overflow. // We provide an Offset so that we can create bitwidths that won't overflow.
@ -12053,17 +11807,17 @@ SDValue DAGCombiner::visitFADD(SDNode *N) {
// fold (fadd A, (fneg B)) -> (fsub A, B) // fold (fadd A, (fneg B)) -> (fsub A, B)
if ((!LegalOperations || TLI.isOperationLegalOrCustom(ISD::FSUB, VT)) && if ((!LegalOperations || TLI.isOperationLegalOrCustom(ISD::FSUB, VT)) &&
isNegatibleForFree(N1, LegalOperations, TLI, &Options, ForCodeSize) == 2) TLI.isNegatibleForFree(N1, DAG, LegalOperations, ForCodeSize) == 2)
return DAG.getNode(ISD::FSUB, DL, VT, N0, return DAG.getNode(
GetNegatedExpression(N1, DAG, LegalOperations, ISD::FSUB, DL, VT, N0,
ForCodeSize), Flags); TLI.getNegatedExpression(N1, DAG, LegalOperations, ForCodeSize), Flags);
// fold (fadd (fneg A), B) -> (fsub B, A) // fold (fadd (fneg A), B) -> (fsub B, A)
if ((!LegalOperations || TLI.isOperationLegalOrCustom(ISD::FSUB, VT)) && if ((!LegalOperations || TLI.isOperationLegalOrCustom(ISD::FSUB, VT)) &&
isNegatibleForFree(N0, LegalOperations, TLI, &Options, ForCodeSize) == 2) TLI.isNegatibleForFree(N0, DAG, LegalOperations, ForCodeSize) == 2)
return DAG.getNode(ISD::FSUB, DL, VT, N1, return DAG.getNode(
GetNegatedExpression(N0, DAG, LegalOperations, ISD::FSUB, DL, VT, N1,
ForCodeSize), Flags); TLI.getNegatedExpression(N0, DAG, LegalOperations, ForCodeSize), Flags);
auto isFMulNegTwo = [](SDValue FMul) { auto isFMulNegTwo = [](SDValue FMul) {
if (!FMul.hasOneUse() || FMul.getOpcode() != ISD::FMUL) if (!FMul.hasOneUse() || FMul.getOpcode() != ISD::FMUL)
@ -12242,16 +11996,16 @@ SDValue DAGCombiner::visitFSUB(SDNode *N) {
if (N0CFP && N0CFP->isZero()) { if (N0CFP && N0CFP->isZero()) {
if (N0CFP->isNegative() || if (N0CFP->isNegative() ||
(Options.NoSignedZerosFPMath || Flags.hasNoSignedZeros())) { (Options.NoSignedZerosFPMath || Flags.hasNoSignedZeros())) {
if (isNegatibleForFree(N1, LegalOperations, TLI, &Options, ForCodeSize)) if (TLI.isNegatibleForFree(N1, DAG, LegalOperations, ForCodeSize))
return GetNegatedExpression(N1, DAG, LegalOperations, ForCodeSize); return TLI.getNegatedExpression(N1, DAG, LegalOperations, ForCodeSize);
if (!LegalOperations || TLI.isOperationLegal(ISD::FNEG, VT)) if (!LegalOperations || TLI.isOperationLegal(ISD::FNEG, VT))
return DAG.getNode(ISD::FNEG, DL, VT, N1, Flags); return DAG.getNode(ISD::FNEG, DL, VT, N1, Flags);
} }
} }
if (((Options.UnsafeFPMath && Options.NoSignedZerosFPMath) || if (((Options.UnsafeFPMath && Options.NoSignedZerosFPMath) ||
(Flags.hasAllowReassociation() && Flags.hasNoSignedZeros())) (Flags.hasAllowReassociation() && Flags.hasNoSignedZeros())) &&
&& N1.getOpcode() == ISD::FADD) { N1.getOpcode() == ISD::FADD) {
// X - (X + Y) -> -Y // X - (X + Y) -> -Y
if (N0 == N1->getOperand(0)) if (N0 == N1->getOperand(0))
return DAG.getNode(ISD::FNEG, DL, VT, N1->getOperand(1), Flags); return DAG.getNode(ISD::FNEG, DL, VT, N1->getOperand(1), Flags);
@ -12261,10 +12015,10 @@ SDValue DAGCombiner::visitFSUB(SDNode *N) {
} }
// fold (fsub A, (fneg B)) -> (fadd A, B) // fold (fsub A, (fneg B)) -> (fadd A, B)
if (isNegatibleForFree(N1, LegalOperations, TLI, &Options, ForCodeSize)) if (TLI.isNegatibleForFree(N1, DAG, LegalOperations, ForCodeSize))
return DAG.getNode(ISD::FADD, DL, VT, N0, return DAG.getNode(
GetNegatedExpression(N1, DAG, LegalOperations, ISD::FADD, DL, VT, N0,
ForCodeSize), Flags); TLI.getNegatedExpression(N1, DAG, LegalOperations, ForCodeSize), Flags);
// FSUB -> FMA combines: // FSUB -> FMA combines:
if (SDValue Fused = visitFSUBForFMACombine(N)) { if (SDValue Fused = visitFSUBForFMACombine(N)) {
@ -12278,11 +12032,10 @@ SDValue DAGCombiner::visitFSUB(SDNode *N) {
/// Return true if both inputs are at least as cheap in negated form and at /// Return true if both inputs are at least as cheap in negated form and at
/// least one input is strictly cheaper in negated form. /// least one input is strictly cheaper in negated form.
bool DAGCombiner::isCheaperToUseNegatedFPOps(SDValue X, SDValue Y) { bool DAGCombiner::isCheaperToUseNegatedFPOps(SDValue X, SDValue Y) {
const TargetOptions &Options = DAG.getTarget().Options; if (char LHSNeg =
if (char LHSNeg = isNegatibleForFree(X, LegalOperations, TLI, &Options, TLI.isNegatibleForFree(X, DAG, LegalOperations, ForCodeSize))
ForCodeSize)) if (char RHSNeg =
if (char RHSNeg = isNegatibleForFree(Y, LegalOperations, TLI, &Options, TLI.isNegatibleForFree(Y, DAG, LegalOperations, ForCodeSize))
ForCodeSize))
// Both negated operands are at least as cheap as their counterparts. // Both negated operands are at least as cheap as their counterparts.
// Check to see if at least one is cheaper negated. // Check to see if at least one is cheaper negated.
if (LHSNeg == 2 || RHSNeg == 2) if (LHSNeg == 2 || RHSNeg == 2)
@ -12363,8 +12116,10 @@ SDValue DAGCombiner::visitFMUL(SDNode *N) {
// -N0 * -N1 --> N0 * N1 // -N0 * -N1 --> N0 * N1
if (isCheaperToUseNegatedFPOps(N0, N1)) { if (isCheaperToUseNegatedFPOps(N0, N1)) {
SDValue NegN0 = GetNegatedExpression(N0, DAG, LegalOperations, ForCodeSize); SDValue NegN0 =
SDValue NegN1 = GetNegatedExpression(N1, DAG, LegalOperations, ForCodeSize); TLI.getNegatedExpression(N0, DAG, LegalOperations, ForCodeSize);
SDValue NegN1 =
TLI.getNegatedExpression(N1, DAG, LegalOperations, ForCodeSize);
return DAG.getNode(ISD::FMUL, DL, VT, NegN0, NegN1, Flags); return DAG.getNode(ISD::FMUL, DL, VT, NegN0, NegN1, Flags);
} }
@ -12446,8 +12201,10 @@ SDValue DAGCombiner::visitFMA(SDNode *N) {
// (-N0 * -N1) + N2 --> (N0 * N1) + N2 // (-N0 * -N1) + N2 --> (N0 * N1) + N2
if (isCheaperToUseNegatedFPOps(N0, N1)) { if (isCheaperToUseNegatedFPOps(N0, N1)) {
SDValue NegN0 = GetNegatedExpression(N0, DAG, LegalOperations, ForCodeSize); SDValue NegN0 =
SDValue NegN1 = GetNegatedExpression(N1, DAG, LegalOperations, ForCodeSize); TLI.getNegatedExpression(N0, DAG, LegalOperations, ForCodeSize);
SDValue NegN1 =
TLI.getNegatedExpression(N1, DAG, LegalOperations, ForCodeSize);
return DAG.getNode(ISD::FMA, DL, VT, NegN0, NegN1, N2, Flags); return DAG.getNode(ISD::FMA, DL, VT, NegN0, NegN1, N2, Flags);
} }
@ -12708,8 +12465,8 @@ SDValue DAGCombiner::visitFDIV(SDNode *N) {
if (isCheaperToUseNegatedFPOps(N0, N1)) if (isCheaperToUseNegatedFPOps(N0, N1))
return DAG.getNode( return DAG.getNode(
ISD::FDIV, SDLoc(N), VT, ISD::FDIV, SDLoc(N), VT,
GetNegatedExpression(N0, DAG, LegalOperations, ForCodeSize), TLI.getNegatedExpression(N0, DAG, LegalOperations, ForCodeSize),
GetNegatedExpression(N1, DAG, LegalOperations, ForCodeSize), Flags); TLI.getNegatedExpression(N1, DAG, LegalOperations, ForCodeSize), Flags);
return SDValue(); return SDValue();
} }
@ -13263,9 +13020,8 @@ SDValue DAGCombiner::visitFNEG(SDNode *N) {
if (isConstantFPBuildVectorOrConstantFP(N0)) if (isConstantFPBuildVectorOrConstantFP(N0))
return DAG.getNode(ISD::FNEG, SDLoc(N), VT, N0); return DAG.getNode(ISD::FNEG, SDLoc(N), VT, N0);
if (isNegatibleForFree(N0, LegalOperations, DAG.getTargetLoweringInfo(), if (TLI.isNegatibleForFree(N0, DAG, LegalOperations, ForCodeSize))
&DAG.getTarget().Options, ForCodeSize)) return TLI.getNegatedExpression(N0, DAG, LegalOperations, ForCodeSize);
return GetNegatedExpression(N0, DAG, LegalOperations, ForCodeSize);
// Transform fneg(bitconvert(x)) -> bitconvert(x ^ sign) to avoid loading // Transform fneg(bitconvert(x)) -> bitconvert(x ^ sign) to avoid loading
// constant pool values. // constant pool values.

View File

@ -5334,6 +5334,246 @@ verifyReturnAddressArgumentIsConstant(SDValue Op, SelectionDAG &DAG) const {
return false; return false;
} }
char TargetLowering::isNegatibleForFree(SDValue Op, SelectionDAG &DAG,
bool LegalOperations, bool ForCodeSize,
unsigned Depth) const {
// fneg is removable even if it has multiple uses.
if (Op.getOpcode() == ISD::FNEG)
return 2;
// Don't allow anything with multiple uses unless we know it is free.
EVT VT = Op.getValueType();
const SDNodeFlags Flags = Op->getFlags();
const TargetOptions &Options = DAG.getTarget().Options;
if (!Op.hasOneUse() && !(Op.getOpcode() == ISD::FP_EXTEND &&
isFPExtFree(VT, Op.getOperand(0).getValueType())))
return 0;
// Don't recurse exponentially.
if (Depth > SelectionDAG::MaxRecursionDepth)
return 0;
switch (Op.getOpcode()) {
case ISD::ConstantFP: {
if (!LegalOperations)
return 1;
// Don't invert constant FP values after legalization unless the target says
// the negated constant is legal.
return isOperationLegal(ISD::ConstantFP, VT) ||
isFPImmLegal(neg(cast<ConstantFPSDNode>(Op)->getValueAPF()), VT,
ForCodeSize);
}
case ISD::BUILD_VECTOR: {
// Only permit BUILD_VECTOR of constants.
if (llvm::any_of(Op->op_values(), [&](SDValue N) {
return !N.isUndef() && !isa<ConstantFPSDNode>(N);
}))
return 0;
if (!LegalOperations)
return 1;
if (isOperationLegal(ISD::ConstantFP, VT) &&
isOperationLegal(ISD::BUILD_VECTOR, VT))
return 1;
return llvm::all_of(Op->op_values(), [&](SDValue N) {
return N.isUndef() ||
isFPImmLegal(neg(cast<ConstantFPSDNode>(N)->getValueAPF()), VT,
ForCodeSize);
});
}
case ISD::FADD:
if (!Options.NoSignedZerosFPMath && !Flags.hasNoSignedZeros())
return 0;
// After operation legalization, it might not be legal to create new FSUBs.
if (LegalOperations && !isOperationLegalOrCustom(ISD::FSUB, VT))
return 0;
// fold (fneg (fadd A, B)) -> (fsub (fneg A), B)
if (char V = isNegatibleForFree(Op.getOperand(0), DAG, LegalOperations,
ForCodeSize, Depth + 1))
return V;
// fold (fneg (fadd A, B)) -> (fsub (fneg B), A)
return isNegatibleForFree(Op.getOperand(1), DAG, LegalOperations,
ForCodeSize, Depth + 1);
case ISD::FSUB:
// We can't turn -(A-B) into B-A when we honor signed zeros.
if (!Options.NoSignedZerosFPMath && !Flags.hasNoSignedZeros())
return 0;
// fold (fneg (fsub A, B)) -> (fsub B, A)
return 1;
case ISD::FMUL:
case ISD::FDIV:
// fold (fneg (fmul X, Y)) -> (fmul (fneg X), Y) or (fmul X, (fneg Y))
if (char V = isNegatibleForFree(Op.getOperand(0), DAG, LegalOperations,
ForCodeSize, Depth + 1))
return V;
// Ignore X * 2.0 because that is expected to be canonicalized to X + X.
if (auto *C = isConstOrConstSplatFP(Op.getOperand(1)))
if (C->isExactlyValue(2.0) && Op.getOpcode() == ISD::FMUL)
return 0;
return isNegatibleForFree(Op.getOperand(1), DAG, LegalOperations,
ForCodeSize, Depth + 1);
case ISD::FMA:
case ISD::FMAD: {
if (!Options.NoSignedZerosFPMath && !Flags.hasNoSignedZeros())
return 0;
// fold (fneg (fma X, Y, Z)) -> (fma (fneg X), Y, (fneg Z))
// fold (fneg (fma X, Y, Z)) -> (fma X, (fneg Y), (fneg Z))
char V2 = isNegatibleForFree(Op.getOperand(2), DAG, LegalOperations,
ForCodeSize, Depth + 1);
if (!V2)
return 0;
// One of Op0/Op1 must be cheaply negatible, then select the cheapest.
char V0 = isNegatibleForFree(Op.getOperand(0), DAG, LegalOperations,
ForCodeSize, Depth + 1);
char V1 = isNegatibleForFree(Op.getOperand(1), DAG, LegalOperations,
ForCodeSize, Depth + 1);
char V01 = std::max(V0, V1);
return V01 ? std::max(V01, V2) : 0;
}
case ISD::FP_EXTEND:
case ISD::FP_ROUND:
case ISD::FSIN:
return isNegatibleForFree(Op.getOperand(0), DAG, LegalOperations,
ForCodeSize, Depth + 1);
}
return 0;
}
SDValue TargetLowering::getNegatedExpression(SDValue Op, SelectionDAG &DAG,
bool LegalOperations,
bool ForCodeSize,
unsigned Depth) const {
// fneg is removable even if it has multiple uses.
if (Op.getOpcode() == ISD::FNEG)
return Op.getOperand(0);
assert(Depth <= SelectionDAG::MaxRecursionDepth &&
"getNegatedExpression doesn't match isNegatibleForFree");
const SDNodeFlags Flags = Op->getFlags();
switch (Op.getOpcode()) {
case ISD::ConstantFP: {
APFloat V = cast<ConstantFPSDNode>(Op)->getValueAPF();
V.changeSign();
return DAG.getConstantFP(V, SDLoc(Op), Op.getValueType());
}
case ISD::BUILD_VECTOR: {
SmallVector<SDValue, 4> Ops;
for (SDValue C : Op->op_values()) {
if (C.isUndef()) {
Ops.push_back(C);
continue;
}
APFloat V = cast<ConstantFPSDNode>(C)->getValueAPF();
V.changeSign();
Ops.push_back(DAG.getConstantFP(V, SDLoc(Op), C.getValueType()));
}
return DAG.getBuildVector(Op.getValueType(), SDLoc(Op), Ops);
}
case ISD::FADD:
assert((DAG.getTarget().Options.NoSignedZerosFPMath ||
Flags.hasNoSignedZeros()) &&
"Expected NSZ fp-flag");
// fold (fneg (fadd A, B)) -> (fsub (fneg A), B)
if (isNegatibleForFree(Op.getOperand(0), DAG, LegalOperations, ForCodeSize,
Depth + 1))
return DAG.getNode(ISD::FSUB, SDLoc(Op), Op.getValueType(),
getNegatedExpression(Op.getOperand(0), DAG,
LegalOperations, ForCodeSize,
Depth + 1),
Op.getOperand(1), Flags);
// fold (fneg (fadd A, B)) -> (fsub (fneg B), A)
return DAG.getNode(ISD::FSUB, SDLoc(Op), Op.getValueType(),
getNegatedExpression(Op.getOperand(1), DAG,
LegalOperations, ForCodeSize,
Depth + 1),
Op.getOperand(0), Flags);
case ISD::FSUB:
// fold (fneg (fsub 0, B)) -> B
if (ConstantFPSDNode *N0CFP =
isConstOrConstSplatFP(Op.getOperand(0), /*AllowUndefs*/ true))
if (N0CFP->isZero())
return Op.getOperand(1);
// fold (fneg (fsub A, B)) -> (fsub B, A)
return DAG.getNode(ISD::FSUB, SDLoc(Op), Op.getValueType(),
Op.getOperand(1), Op.getOperand(0), Flags);
case ISD::FMUL:
case ISD::FDIV:
// fold (fneg (fmul X, Y)) -> (fmul (fneg X), Y)
if (isNegatibleForFree(Op.getOperand(0), DAG, LegalOperations, ForCodeSize,
Depth + 1))
return DAG.getNode(Op.getOpcode(), SDLoc(Op), Op.getValueType(),
getNegatedExpression(Op.getOperand(0), DAG,
LegalOperations, ForCodeSize,
Depth + 1),
Op.getOperand(1), Flags);
// fold (fneg (fmul X, Y)) -> (fmul X, (fneg Y))
return DAG.getNode(
Op.getOpcode(), SDLoc(Op), Op.getValueType(), Op.getOperand(0),
getNegatedExpression(Op.getOperand(1), DAG, LegalOperations,
ForCodeSize, Depth + 1),
Flags);
case ISD::FMA:
case ISD::FMAD: {
assert((DAG.getTarget().Options.NoSignedZerosFPMath ||
Flags.hasNoSignedZeros()) &&
"Expected NSZ fp-flag");
SDValue Neg2 = getNegatedExpression(Op.getOperand(2), DAG, LegalOperations,
ForCodeSize, Depth + 1);
char V0 = isNegatibleForFree(Op.getOperand(0), DAG, LegalOperations,
ForCodeSize, Depth + 1);
char V1 = isNegatibleForFree(Op.getOperand(1), DAG, LegalOperations,
ForCodeSize, Depth + 1);
if (V0 >= V1) {
// fold (fneg (fma X, Y, Z)) -> (fma (fneg X), Y, (fneg Z))
SDValue Neg0 = getNegatedExpression(
Op.getOperand(0), DAG, LegalOperations, ForCodeSize, Depth + 1);
return DAG.getNode(Op.getOpcode(), SDLoc(Op), Op.getValueType(), Neg0,
Op.getOperand(1), Neg2, Flags);
}
// fold (fneg (fma X, Y, Z)) -> (fma X, (fneg Y), (fneg Z))
SDValue Neg1 = getNegatedExpression(Op.getOperand(1), DAG, LegalOperations,
ForCodeSize, Depth + 1);
return DAG.getNode(Op.getOpcode(), SDLoc(Op), Op.getValueType(),
Op.getOperand(0), Neg1, Neg2, Flags);
}
case ISD::FP_EXTEND:
case ISD::FSIN:
return DAG.getNode(Op.getOpcode(), SDLoc(Op), Op.getValueType(),
getNegatedExpression(Op.getOperand(0), DAG,
LegalOperations, ForCodeSize,
Depth + 1));
case ISD::FP_ROUND:
return DAG.getNode(ISD::FP_ROUND, SDLoc(Op), Op.getValueType(),
getNegatedExpression(Op.getOperand(0), DAG,
LegalOperations, ForCodeSize,
Depth + 1),
Op.getOperand(1));
}
llvm_unreachable("Unknown code");
}
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
// Legalization Utilities // Legalization Utilities
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//