llvm-project/llvm/lib/Transforms/Utils/BypassSlowDivision.cpp

480 lines
17 KiB
C++

//===-- BypassSlowDivision.cpp - Bypass slow division ---------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file contains an optimization for div and rem on architectures that
// execute short instructions significantly faster than longer instructions.
// For example, on Intel Atom 32-bit divides are slow enough that during
// runtime it is profitable to check the value of the operands, and if they are
// positive and less than 256 use an unsigned 8-bit divide.
//
//===----------------------------------------------------------------------===//
#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"
#include "llvm/IR/Instructions.h"
#include "llvm/Support/KnownBits.h"
#include "llvm/Transforms/Utils/Local.h"
using namespace llvm;
#define DEBUG_TYPE "bypass-slow-division"
namespace {
struct DivOpInfo {
bool SignedOp;
Value *Dividend;
Value *Divisor;
DivOpInfo(bool InSignedOp, Value *InDividend, Value *InDivisor)
: SignedOp(InSignedOp), Dividend(InDividend), Divisor(InDivisor) {}
};
struct QuotRemPair {
Value *Quotient;
Value *Remainder;
QuotRemPair(Value *InQuotient, Value *InRemainder)
: Quotient(InQuotient), Remainder(InRemainder) {}
};
/// A quotient and remainder, plus a BB from which they logically "originate".
/// If you use Quotient or Remainder in a Phi node, you should use BB as its
/// corresponding predecessor.
struct QuotRemWithBB {
BasicBlock *BB = nullptr;
Value *Quotient = nullptr;
Value *Remainder = nullptr;
};
}
namespace llvm {
template<>
struct DenseMapInfo<DivOpInfo> {
static bool isEqual(const DivOpInfo &Val1, const DivOpInfo &Val2) {
return Val1.SignedOp == Val2.SignedOp &&
Val1.Dividend == Val2.Dividend &&
Val1.Divisor == Val2.Divisor;
}
static DivOpInfo getEmptyKey() {
return DivOpInfo(false, nullptr, nullptr);
}
static DivOpInfo getTombstoneKey() {
return DivOpInfo(true, nullptr, nullptr);
}
static unsigned getHashValue(const DivOpInfo &Val) {
return (unsigned)(reinterpret_cast<uintptr_t>(Val.Dividend) ^
reinterpret_cast<uintptr_t>(Val.Divisor)) ^
(unsigned)Val.SignedOp;
}
};
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_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_LIKELY_LONG
};
class FastDivInsertionTask {
bool IsValidTask = false;
Instruction *SlowDivOrRem = nullptr;
IntegerType *BypassType = nullptr;
BasicBlock *MainBB = nullptr;
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,
BasicBlock *PhiBB);
Value *insertOperandRuntimeCheck(Value *Op1, Value *Op2);
Optional<QuotRemPair> insertFastDivAndRem();
bool isSignedOp() {
return SlowDivOrRem->getOpcode() == Instruction::SDiv ||
SlowDivOrRem->getOpcode() == Instruction::SRem;
}
bool isDivisionOp() {
return SlowDivOrRem->getOpcode() == Instruction::SDiv ||
SlowDivOrRem->getOpcode() == Instruction::UDiv;
}
Type *getSlowType() { return SlowDivOrRem->getType(); }
public:
FastDivInsertionTask(Instruction *I, const BypassWidthsTy &BypassWidths);
Value *getReplacement(DivCacheTy &Cache);
};
} // anonymous namespace
FastDivInsertionTask::FastDivInsertionTask(Instruction *I,
const BypassWidthsTy &BypassWidths) {
switch (I->getOpcode()) {
case Instruction::UDiv:
case Instruction::SDiv:
case Instruction::URem:
case Instruction::SRem:
SlowDivOrRem = I;
break;
default:
// I is not a div/rem operation.
return;
}
// Skip division on vector types. Only optimize integer instructions.
IntegerType *SlowType = dyn_cast<IntegerType>(SlowDivOrRem->getType());
if (!SlowType)
return;
// Skip if this bitwidth is not bypassed.
auto BI = BypassWidths.find(SlowType->getBitWidth());
if (BI == BypassWidths.end())
return;
// Get type for div/rem instruction with bypass bitwidth.
IntegerType *BT = IntegerType::get(I->getContext(), BI->second);
BypassType = BT;
// The original basic block.
MainBB = I->getParent();
// The instruction is indeed a slow div or rem operation.
IsValidTask = true;
}
/// Reuses previously-computed dividend or remainder from the current BB if
/// operands and operation are identical. Otherwise calls insertFastDivAndRem to
/// perform the optimization and caches the resulting dividend and remainder.
/// If no replacement can be generated, nullptr is returned.
Value *FastDivInsertionTask::getReplacement(DivCacheTy &Cache) {
// First, make sure that the task is valid.
if (!IsValidTask)
return nullptr;
// Then, look for a value in Cache.
Value *Dividend = SlowDivOrRem->getOperand(0);
Value *Divisor = SlowDivOrRem->getOperand(1);
DivOpInfo Key(isSignedOp(), Dividend, Divisor);
auto CacheI = Cache.find(Key);
if (CacheI == Cache.end()) {
// If previous instance does not exist, try to insert fast div.
Optional<QuotRemPair> OptResult = insertFastDivAndRem();
// Bail out if insertFastDivAndRem has failed.
if (!OptResult)
return nullptr;
CacheI = Cache.insert({Key, *OptResult}).first;
}
QuotRemPair &Value = CacheI->second;
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,
VisitedSetTy &Visited) {
unsigned ShortLen = BypassType->getBitWidth();
unsigned LongLen = V->getType()->getIntegerBitWidth();
assert(LongLen > ShortLen && "Value type must be wider than BypassType");
unsigned HiBits = LongLen - ShortLen;
const DataLayout &DL = SlowDivOrRem->getModule()->getDataLayout();
KnownBits Known(LongLen);
computeKnownBits(V, Known, DL);
if (Known.Zero.countLeadingOnes() >= HiBits)
return VALRNG_KNOWN_SHORT;
if (Known.One.countLeadingZeros() < HiBits)
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;
}
/// Add new basic block for slow div and rem operations and put it before
/// SuccessorBB.
QuotRemWithBB FastDivInsertionTask::createSlowBB(BasicBlock *SuccessorBB) {
QuotRemWithBB DivRemPair;
DivRemPair.BB = BasicBlock::Create(MainBB->getParent()->getContext(), "",
MainBB->getParent(), SuccessorBB);
IRBuilder<> Builder(DivRemPair.BB, DivRemPair.BB->begin());
Value *Dividend = SlowDivOrRem->getOperand(0);
Value *Divisor = SlowDivOrRem->getOperand(1);
if (isSignedOp()) {
DivRemPair.Quotient = Builder.CreateSDiv(Dividend, Divisor);
DivRemPair.Remainder = Builder.CreateSRem(Dividend, Divisor);
} else {
DivRemPair.Quotient = Builder.CreateUDiv(Dividend, Divisor);
DivRemPair.Remainder = Builder.CreateURem(Dividend, Divisor);
}
Builder.CreateBr(SuccessorBB);
return DivRemPair;
}
/// Add new basic block for fast div and rem operations and put it before
/// SuccessorBB.
QuotRemWithBB FastDivInsertionTask::createFastBB(BasicBlock *SuccessorBB) {
QuotRemWithBB DivRemPair;
DivRemPair.BB = BasicBlock::Create(MainBB->getParent()->getContext(), "",
MainBB->getParent(), SuccessorBB);
IRBuilder<> Builder(DivRemPair.BB, DivRemPair.BB->begin());
Value *Dividend = SlowDivOrRem->getOperand(0);
Value *Divisor = SlowDivOrRem->getOperand(1);
Value *ShortDivisorV =
Builder.CreateCast(Instruction::Trunc, Divisor, BypassType);
Value *ShortDividendV =
Builder.CreateCast(Instruction::Trunc, Dividend, BypassType);
// udiv/urem because this optimization only handles positive numbers.
Value *ShortQV = Builder.CreateUDiv(ShortDividendV, ShortDivisorV);
Value *ShortRV = Builder.CreateURem(ShortDividendV, ShortDivisorV);
DivRemPair.Quotient =
Builder.CreateCast(Instruction::ZExt, ShortQV, getSlowType());
DivRemPair.Remainder =
Builder.CreateCast(Instruction::ZExt, ShortRV, getSlowType());
Builder.CreateBr(SuccessorBB);
return DivRemPair;
}
/// Creates Phi nodes for result of Div and Rem.
QuotRemPair FastDivInsertionTask::createDivRemPhiNodes(QuotRemWithBB &LHS,
QuotRemWithBB &RHS,
BasicBlock *PhiBB) {
IRBuilder<> Builder(PhiBB, PhiBB->begin());
PHINode *QuoPhi = Builder.CreatePHI(getSlowType(), 2);
QuoPhi->addIncoming(LHS.Quotient, LHS.BB);
QuoPhi->addIncoming(RHS.Quotient, RHS.BB);
PHINode *RemPhi = Builder.CreatePHI(getSlowType(), 2);
RemPhi->addIncoming(LHS.Remainder, LHS.BB);
RemPhi->addIncoming(RHS.Remainder, RHS.BB);
return QuotRemPair(QuoPhi, RemPhi);
}
/// Creates a runtime check to test whether both the divisor and dividend fit
/// into BypassType. The check is inserted at the end of MainBB. True return
/// value means that the operands fit. Either of the operands may be NULL if it
/// doesn't need a runtime check.
Value *FastDivInsertionTask::insertOperandRuntimeCheck(Value *Op1, Value *Op2) {
assert((Op1 || Op2) && "Nothing to check");
IRBuilder<> Builder(MainBB, MainBB->end());
Value *OrV;
if (Op1 && Op2)
OrV = Builder.CreateOr(Op1, Op2);
else
OrV = Op1 ? Op1 : Op2;
// BitMask is inverted to check if the operands are
// larger than the bypass type
uint64_t BitMask = ~BypassType->getBitMask();
Value *AndV = Builder.CreateAnd(OrV, BitMask);
// Compare operand values
Value *ZeroV = ConstantInt::getSigned(getSlowType(), 0);
return Builder.CreateICmpEQ(AndV, ZeroV);
}
/// Substitutes the div/rem instruction with code that checks the value of the
/// operands and uses a shorter-faster div/rem instruction when possible.
Optional<QuotRemPair> FastDivInsertionTask::insertFastDivAndRem() {
Value *Dividend = SlowDivOrRem->getOperand(0);
Value *Divisor = SlowDivOrRem->getOperand(1);
if (isa<ConstantInt>(Divisor)) {
// Keep division by a constant for DAGCombiner.
return None;
}
VisitedSetTy SetL;
ValueRange DividendRange = getValueRange(Dividend, SetL);
if (DividendRange == VALRNG_LIKELY_LONG)
return None;
VisitedSetTy SetR;
ValueRange DivisorRange = getValueRange(Divisor, SetR);
if (DivisorRange == VALRNG_LIKELY_LONG)
return None;
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
// division with a short one in-place.
IRBuilder<> Builder(SlowDivOrRem);
Value *TruncDividend = Builder.CreateTrunc(Dividend, BypassType);
Value *TruncDivisor = Builder.CreateTrunc(Divisor, BypassType);
Value *TruncDiv = Builder.CreateUDiv(TruncDividend, TruncDivisor);
Value *TruncRem = Builder.CreateURem(TruncDividend, TruncDivisor);
Value *ExtDiv = Builder.CreateZExt(TruncDiv, getSlowType());
Value *ExtRem = Builder.CreateZExt(TruncRem, getSlowType());
return QuotRemPair(ExtDiv, ExtRem);
} else if (DividendShort && !isSignedOp()) {
// If the division is unsigned and Dividend is known to be short, then
// either
// 1) Divisor is less or equal to Dividend, and the result can be computed
// with a short division.
// 2) Divisor is greater than Dividend. In this case, no division is needed
// at all: The quotient is 0 and the remainder is equal to Dividend.
//
// So instead of checking at runtime whether Divisor fits into BypassType,
// we emit a runtime check to differentiate between these two cases. This
// lets us entirely avoid a long div.
// Split the basic block before the div/rem.
BasicBlock *SuccessorBB = MainBB->splitBasicBlock(SlowDivOrRem);
// Remove the unconditional branch from MainBB to SuccessorBB.
MainBB->getInstList().back().eraseFromParent();
QuotRemWithBB Long;
Long.BB = MainBB;
Long.Quotient = ConstantInt::get(getSlowType(), 0);
Long.Remainder = Dividend;
QuotRemWithBB Fast = createFastBB(SuccessorBB);
QuotRemPair Result = createDivRemPhiNodes(Fast, Long, SuccessorBB);
IRBuilder<> Builder(MainBB, MainBB->end());
Value *CmpV = Builder.CreateICmpUGE(Dividend, Divisor);
Builder.CreateCondBr(CmpV, Fast.BB, SuccessorBB);
return Result;
} else {
// General case. Create both slow and fast div/rem pairs and choose one of
// them at runtime.
// Split the basic block before the div/rem.
BasicBlock *SuccessorBB = MainBB->splitBasicBlock(SlowDivOrRem);
// Remove the unconditional branch from MainBB to SuccessorBB.
MainBB->getInstList().back().eraseFromParent();
QuotRemWithBB Fast = createFastBB(SuccessorBB);
QuotRemWithBB Slow = createSlowBB(SuccessorBB);
QuotRemPair Result = createDivRemPhiNodes(Fast, Slow, SuccessorBB);
Value *CmpV = insertOperandRuntimeCheck(DividendShort ? nullptr : Dividend,
DivisorShort ? nullptr : Divisor);
IRBuilder<> Builder(MainBB, MainBB->end());
Builder.CreateCondBr(CmpV, Fast.BB, Slow.BB);
return Result;
}
}
/// This optimization identifies DIV/REM instructions in a BB that can be
/// profitably bypassed and carried out with a shorter, faster divide.
bool llvm::bypassSlowDivision(BasicBlock *BB,
const BypassWidthsTy &BypassWidths) {
DivCacheTy PerBBDivCache;
bool MadeChange = false;
Instruction* Next = &*BB->begin();
while (Next != nullptr) {
// We may add instructions immediately after I, but we want to skip over
// them.
Instruction* I = Next;
Next = Next->getNextNode();
FastDivInsertionTask Task(I, BypassWidths);
if (Value *Replacement = Task.getReplacement(PerBBDivCache)) {
I->replaceAllUsesWith(Replacement);
I->eraseFromParent();
MadeChange = true;
}
}
// Above we eagerly create divs and rems, as pairs, so that we can efficiently
// create divrem machine instructions. Now erase any unused divs / rems so we
// don't leave extra instructions sitting around.
for (auto &KV : PerBBDivCache)
for (Value *V : {KV.second.Quotient, KV.second.Remainder})
RecursivelyDeleteTriviallyDeadInstructions(V);
return MadeChange;
}