Skip to content

Commit 8fc9e3d

Browse files
authored
[DAG] Lower frem of power-2 using div/trunc/mul+sub (#91148)
If we are lowering a frem and the divisor is known to be an integer power-2, we can use the formula 'frem = x - trunc(x / d) * d'. This avoids the more expensive call to fmod. The results are identical as fmod so long as d is a power-2 (so the mul does not round incorrectly), and the sign of the return is either always positive or not important for zeroes (nsz). Unfortunately Alive2 does not handle this well at the moment. I was using exhaustive checking to test this: (https://gist.github.com/davemgreen/6078015f30d3bacd1e9572f8db5d4b64). I found this in cpythons implementation of float_pow. I currently added it as a DAG combine for frem with power-2 fp constants.
1 parent 4cf3f03 commit 8fc9e3d

File tree

5 files changed

+273
-163
lines changed

5 files changed

+273
-163
lines changed

llvm/include/llvm/CodeGen/SelectionDAG.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1996,6 +1996,10 @@ class SelectionDAG {
19961996
/// is set.
19971997
bool isKnownToBeAPowerOfTwo(SDValue Val, unsigned Depth = 0) const;
19981998

1999+
/// Test if the given _fp_ value is known to be an integer power-of-2, either
2000+
/// positive or negative.
2001+
bool isKnownToBeAPowerOfTwoFP(SDValue Val, unsigned Depth = 0) const;
2002+
19992003
/// Return the number of times the sign bit of the register is replicated into
20002004
/// the other bits. We know that at least 1 bit is always equal to the sign
20012005
/// bit (itself), but other cases can give us information. For example,
@@ -2111,6 +2115,10 @@ class SelectionDAG {
21112115
/// Test whether the given SDValue is known to contain non-zero value(s).
21122116
bool isKnownNeverZero(SDValue Op, unsigned Depth = 0) const;
21132117

2118+
/// Test whether the given float value is known to be positive. +0.0, +inf and
2119+
/// +nan are considered positive, -0.0, -inf and -nan are not.
2120+
bool cannotBeOrderedNegativeFP(SDValue Op) const;
2121+
21142122
/// Test whether two SDValues are known to compare equal. This
21152123
/// is true if they are the same value, or if one is negative zero and the
21162124
/// other positive zero.

llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17365,17 +17365,35 @@ SDValue DAGCombiner::visitFREM(SDNode *N) {
1736517365
EVT VT = N->getValueType(0);
1736617366
SDNodeFlags Flags = N->getFlags();
1736717367
SelectionDAG::FlagInserter FlagsInserter(DAG, N);
17368+
SDLoc DL(N);
1736817369

1736917370
if (SDValue R = DAG.simplifyFPBinop(N->getOpcode(), N0, N1, Flags))
1737017371
return R;
1737117372

1737217373
// fold (frem c1, c2) -> fmod(c1,c2)
17373-
if (SDValue C = DAG.FoldConstantArithmetic(ISD::FREM, SDLoc(N), VT, {N0, N1}))
17374+
if (SDValue C = DAG.FoldConstantArithmetic(ISD::FREM, DL, VT, {N0, N1}))
1737417375
return C;
1737517376

1737617377
if (SDValue NewSel = foldBinOpIntoSelect(N))
1737717378
return NewSel;
1737817379

17380+
// Lower frem N0, N1 => x - trunc(N0 / N1) * N1, providing N1 is an integer
17381+
// power of 2.
17382+
if (!TLI.isOperationLegal(ISD::FREM, VT) &&
17383+
TLI.isOperationLegalOrCustom(ISD::FMUL, VT) &&
17384+
TLI.isOperationLegalOrCustom(ISD::FDIV, VT) &&
17385+
TLI.isOperationLegalOrCustom(ISD::FTRUNC, VT) &&
17386+
DAG.isKnownToBeAPowerOfTwoFP(N1) &&
17387+
(Flags.hasNoSignedZeros() || DAG.cannotBeOrderedNegativeFP(N0))) {
17388+
SDValue Div = DAG.getNode(ISD::FDIV, DL, VT, N0, N1);
17389+
SDValue Rnd = DAG.getNode(ISD::FTRUNC, DL, VT, Div);
17390+
if (TLI.isFMAFasterThanFMulAndFAdd(DAG.getMachineFunction(), VT))
17391+
return DAG.getNode(ISD::FMA, DL, VT, DAG.getNode(ISD::FNEG, DL, VT, Rnd),
17392+
N1, N0);
17393+
SDValue Mul = DAG.getNode(ISD::FMUL, DL, VT, Rnd, N1);
17394+
return DAG.getNode(ISD::FSUB, DL, VT, N0, Mul);
17395+
}
17396+
1737917397
return SDValue();
1738017398
}
1738117399

llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4373,6 +4373,16 @@ bool SelectionDAG::isKnownToBeAPowerOfTwo(SDValue Val, unsigned Depth) const {
43734373
return false;
43744374
}
43754375

4376+
bool SelectionDAG::isKnownToBeAPowerOfTwoFP(SDValue Val, unsigned Depth) const {
4377+
if (ConstantFPSDNode *C1 = isConstOrConstSplatFP(Val, true))
4378+
return C1->getValueAPF().getExactLog2Abs() >= 0;
4379+
4380+
if (Val.getOpcode() == ISD::UINT_TO_FP || Val.getOpcode() == ISD::SINT_TO_FP)
4381+
return isKnownToBeAPowerOfTwo(Val.getOperand(0), Depth + 1);
4382+
4383+
return false;
4384+
}
4385+
43764386
unsigned SelectionDAG::ComputeNumSignBits(SDValue Op, unsigned Depth) const {
43774387
EVT VT = Op.getValueType();
43784388

@@ -5555,6 +5565,13 @@ bool SelectionDAG::isKnownNeverZero(SDValue Op, unsigned Depth) const {
55555565
return computeKnownBits(Op, Depth).isNonZero();
55565566
}
55575567

5568+
bool SelectionDAG::cannotBeOrderedNegativeFP(SDValue Op) const {
5569+
if (ConstantFPSDNode *C1 = isConstOrConstSplatFP(Op, true))
5570+
return !C1->isNegative();
5571+
5572+
return Op.getOpcode() == ISD::FABS;
5573+
}
5574+
55585575
bool SelectionDAG::isEqualTo(SDValue A, SDValue B) const {
55595576
// Check the obvious case.
55605577
if (A == B) return true;

0 commit comments

Comments
 (0)