diff options
author | Nikolai Bozhenov <nikolai.bozhenov@intel.com> | 2017-04-02 13:14:30 +0000 |
---|---|---|
committer | Nikolai Bozhenov <nikolai.bozhenov@intel.com> | 2017-04-02 13:14:30 +0000 |
commit | fca527af5c5454efdde8cdf2c54d21074ac76c90 (patch) | |
tree | f1f65b1617f4aba73c93c177d711bf0820c282fe /llvm/lib/Transforms | |
parent | dddce31eb4961578564e0cd2671286b967e06b78 (diff) | |
download | bcm5719-llvm-fca527af5c5454efdde8cdf2c54d21074ac76c90.tar.gz bcm5719-llvm-fca527af5c5454efdde8cdf2c54d21074ac76c90.zip |
[BypassSlowDivision] Do not bypass division of hash-like values
Disable bypassing if one of the operands looks like a hash value. Slow
division often occurs in hashtable implementations and fast division is
never taken there because a hash value is extremely unlikely to have
enough upper bits set to zero.
A value is considered to be hash-like if it is produced by
1) XOR operation
2) Multiplication by a constant wider than the shorter type
3) PHI node with all incoming values being hash-like
Differential Revision: https://reviews.llvm.org/D28200
llvm-svn: 299329
Diffstat (limited to 'llvm/lib/Transforms')
-rw-r--r-- | llvm/lib/Transforms/Utils/BypassSlowDivision.cpp | 93 |
1 files changed, 81 insertions, 12 deletions
diff --git a/llvm/lib/Transforms/Utils/BypassSlowDivision.cpp b/llvm/lib/Transforms/Utils/BypassSlowDivision.cpp index 9d8cb3187ee..1cfe3bd5364 100644 --- a/llvm/lib/Transforms/Utils/BypassSlowDivision.cpp +++ b/llvm/lib/Transforms/Utils/BypassSlowDivision.cpp @@ -17,6 +17,7 @@ #include "llvm/Transforms/Utils/BypassSlowDivision.h" #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallPtrSet.h" #include "llvm/Analysis/ValueTracking.h" #include "llvm/IR/Function.h" #include "llvm/IR/IRBuilder.h" @@ -81,17 +82,18 @@ namespace llvm { typedef DenseMap<DivOpInfo, QuotRemPair> DivCacheTy; typedef DenseMap<unsigned, unsigned> BypassWidthsTy; + typedef SmallPtrSet<Instruction *, 4> VisitedSetTy; } namespace { enum ValueRange { /// Operand definitely fits into BypassType. No runtime checks are needed. - VALRNG_SHORT, + VALRNG_KNOWN_SHORT, /// A runtime check is required, as value range is unknown. VALRNG_UNKNOWN, /// Operand is unlikely to fit into BypassType. The bypassing should be /// disabled. - VALRNG_LONG + VALRNG_LIKELY_LONG }; class FastDivInsertionTask { @@ -100,7 +102,8 @@ class FastDivInsertionTask { IntegerType *BypassType = nullptr; BasicBlock *MainBB = nullptr; - ValueRange getValueRange(Value *Op); + bool isHashLikeValue(Value *V, VisitedSetTy &Visited); + ValueRange getValueRange(Value *Op, VisitedSetTy &Visited); QuotRemWithBB createSlowBB(BasicBlock *Successor); QuotRemWithBB createFastBB(BasicBlock *Successor); QuotRemPair createDivRemPhiNodes(QuotRemWithBB &LHS, QuotRemWithBB &RHS, @@ -187,8 +190,65 @@ Value *FastDivInsertionTask::getReplacement(DivCacheTy &Cache) { return isDivisionOp() ? Value.Quotient : Value.Remainder; } +/// \brief Check if a value looks like a hash. +/// +/// The routine is expected to detect values computed using the most common hash +/// algorithms. Typically, hash computations end with one of the following +/// instructions: +/// +/// 1) MUL with a constant wider than BypassType +/// 2) XOR instruction +/// +/// And even if we are wrong and the value is not a hash, it is still quite +/// unlikely that such values will fit into BypassType. +/// +/// To detect string hash algorithms like FNV we have to look through PHI-nodes. +/// It is implemented as a depth-first search for values that look neither long +/// nor hash-like. +bool FastDivInsertionTask::isHashLikeValue(Value *V, VisitedSetTy &Visited) { + Instruction *I = dyn_cast<Instruction>(V); + if (!I) + return false; + + switch (I->getOpcode()) { + case Instruction::Xor: + return true; + case Instruction::Mul: { + // After Constant Hoisting pass, long constants may be represented as + // bitcast instructions. As a result, some constants may look like an + // instruction at first, and an additional check is necessary to find out if + // an operand is actually a constant. + Value *Op1 = I->getOperand(1); + ConstantInt *C = dyn_cast<ConstantInt>(Op1); + if (!C && isa<BitCastInst>(Op1)) + C = dyn_cast<ConstantInt>(cast<BitCastInst>(Op1)->getOperand(0)); + return C && C->getValue().getMinSignedBits() > BypassType->getBitWidth(); + } + case Instruction::PHI: { + // Stop IR traversal in case of a crazy input code. This limits recursion + // depth. + if (Visited.size() >= 16) + return false; + // Do not visit nodes that have been visited already. We return true because + // it means that we couldn't find any value that doesn't look hash-like. + if (Visited.find(I) != Visited.end()) + return true; + Visited.insert(I); + return llvm::all_of(cast<PHINode>(I)->incoming_values(), [&](Value *V) { + // Ignore undef values as they probably don't affect the division + // operands. + return getValueRange(V, Visited) == VALRNG_LIKELY_LONG || + isa<UndefValue>(V); + }); + } + default: + return false; + } +} + /// Check if an integer value fits into our bypass type. -ValueRange FastDivInsertionTask::getValueRange(Value *V) { +ValueRange FastDivInsertionTask::getValueRange(Value *V, + VisitedSetTy &Visited) { unsigned ShortLen = BypassType->getBitWidth(); unsigned LongLen = V->getType()->getIntegerBitWidth(); @@ -201,10 +261,17 @@ ValueRange FastDivInsertionTask::getValueRange(Value *V) { computeKnownBits(V, Zeros, Ones, DL); if (Zeros.countLeadingOnes() >= HiBits) - return VALRNG_SHORT; + return VALRNG_KNOWN_SHORT; if (Ones.countLeadingZeros() < HiBits) - return VALRNG_LONG; + return VALRNG_LIKELY_LONG; + + // Long integer divisions are often used in hashtable implementations. It's + // not worth bypassing such divisions because hash values are extremely + // unlikely to have enough leading zeros. The call below tries to detect + // values that are unlikely to fit BypassType (including hashes). + if (isHashLikeValue(V, Visited)) + return VALRNG_LIKELY_LONG; return VALRNG_UNKNOWN; } @@ -308,16 +375,18 @@ Optional<QuotRemPair> FastDivInsertionTask::insertFastDivAndRem() { return None; } - ValueRange DividendRange = getValueRange(Dividend); - if (DividendRange == VALRNG_LONG) + VisitedSetTy SetL; + ValueRange DividendRange = getValueRange(Dividend, SetL); + if (DividendRange == VALRNG_LIKELY_LONG) return None; - ValueRange DivisorRange = getValueRange(Divisor); - if (DivisorRange == VALRNG_LONG) + VisitedSetTy SetR; + ValueRange DivisorRange = getValueRange(Divisor, SetR); + if (DivisorRange == VALRNG_LIKELY_LONG) return None; - bool DividendShort = (DividendRange == VALRNG_SHORT); - bool DivisorShort = (DivisorRange == VALRNG_SHORT); + bool DividendShort = (DividendRange == VALRNG_KNOWN_SHORT); + bool DivisorShort = (DivisorRange == VALRNG_KNOWN_SHORT); if (DividendShort && DivisorShort) { // If both operands are known to be short then just replace the long |