summaryrefslogtreecommitdiffstats
path: root/llvm/lib/Transforms
diff options
context:
space:
mode:
authorNikolai Bozhenov <nikolai.bozhenov@intel.com>2017-04-02 13:14:30 +0000
committerNikolai Bozhenov <nikolai.bozhenov@intel.com>2017-04-02 13:14:30 +0000
commitfca527af5c5454efdde8cdf2c54d21074ac76c90 (patch)
treef1f65b1617f4aba73c93c177d711bf0820c282fe /llvm/lib/Transforms
parentdddce31eb4961578564e0cd2671286b967e06b78 (diff)
downloadbcm5719-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.cpp93
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
OpenPOWER on IntegriCloud