Skip to content

Commit affe0e5

Browse files
committed
[DAG] Lower frem of power-2 using div/trunk/mul+sub.
If we are lowering a frem and the divisor is known to me 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 signed-zero not important (nsz). Unfortunately Alive2 does not handle this well at the moment. I was using exhaustive checking to test this, hopefully I didn't make a mistake in it (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, with some extra utility functions for checking if a floating-point value is known non-negative or a integer power-2.
1 parent 196e1fd commit affe0e5

File tree

4 files changed

+258
-155
lines changed

4 files changed

+258
-155
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 not be negative. 0.0 is
2119+
/// considered non-negative, -0.0 is considered negative.
2120+
bool isKnownNonNegativeFP(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: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17264,17 +17264,32 @@ SDValue DAGCombiner::visitFREM(SDNode *N) {
1726417264
EVT VT = N->getValueType(0);
1726517265
SDNodeFlags Flags = N->getFlags();
1726617266
SelectionDAG::FlagInserter FlagsInserter(DAG, N);
17267+
SDLoc DL(N);
1726717268

1726817269
if (SDValue R = DAG.simplifyFPBinop(N->getOpcode(), N0, N1, Flags))
1726917270
return R;
1727017271

1727117272
// fold (frem c1, c2) -> fmod(c1,c2)
17272-
if (SDValue C = DAG.FoldConstantArithmetic(ISD::FREM, SDLoc(N), VT, {N0, N1}))
17273+
if (SDValue C = DAG.FoldConstantArithmetic(ISD::FREM, DL, VT, {N0, N1}))
1727317274
return C;
1727417275

1727517276
if (SDValue NewSel = foldBinOpIntoSelect(N))
1727617277
return NewSel;
1727717278

17279+
// Lower frem N0, N1 => x - trunc(N0 / N1) * N1, providing N1 is an integer
17280+
// power of 2.
17281+
if (DAG.isKnownToBeAPowerOfTwoFP(N1) &&
17282+
(Flags.hasNoSignedZeros() || DAG.isKnownNonNegativeFP(N0)) &&
17283+
!TLI.isOperationLegal(ISD::FREM, VT) &&
17284+
TLI.isOperationLegalOrCustom(ISD::FMUL, VT) &&
17285+
TLI.isOperationLegalOrCustom(ISD::FDIV, VT) &&
17286+
TLI.isOperationLegalOrCustom(ISD::FTRUNC, VT)) {
17287+
SDValue Div = DAG.getNode(ISD::FDIV, DL, VT, N0, N1);
17288+
SDValue Rnd = DAG.getNode(ISD::FTRUNC, DL, VT, Div);
17289+
SDValue Mul = DAG.getNode(ISD::FMUL, DL, VT, Rnd, N1);
17290+
return DAG.getNode(ISD::FSUB, DL, VT, N0, Mul);
17291+
}
17292+
1727817293
return SDValue();
1727917294
}
1728017295

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::isKnownNonNegativeFP(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)