#include "llvm/Transforms/Utils/LoopUtils.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/PriorityWorklist.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/Analysis/BasicAliasAnalysis.h"
#include "llvm/Analysis/DomTreeUpdater.h"
#include "llvm/Analysis/GlobalsModRef.h"
#include "llvm/Analysis/InstSimplifyFolder.h"
#include "llvm/Analysis/LoopAccessAnalysis.h"
#include "llvm/Analysis/LoopInfo.h"
#include "llvm/Analysis/LoopPass.h"
#include "llvm/Analysis/MemorySSA.h"
#include "llvm/Analysis/MemorySSAUpdater.h"
#include "llvm/Analysis/ScalarEvolution.h"
#include "llvm/Analysis/ScalarEvolutionAliasAnalysis.h"
#include "llvm/Analysis/ScalarEvolutionExpressions.h"
#include "llvm/IR/DIBuilder.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/MDBuilder.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/PatternMatch.h"
#include "llvm/IR/ValueHandle.h"
#include "llvm/InitializePasses.h"
#include "llvm/Pass.h"
#include "llvm/Support/Debug.h"
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
#include "llvm/Transforms/Utils/Local.h"
#include "llvm/Transforms/Utils/ScalarEvolutionExpander.h"
using namespace llvm;
using namespace llvm::PatternMatch;
#define DEBUG_TYPE "loop-utils"
static const char *LLVMLoopDisableNonforced = "llvm.loop.disable_nonforced";
static const char *LLVMLoopDisableLICM = "llvm.licm.disable";
bool llvm::formDedicatedExitBlocks(Loop *L, DominatorTree *DT, LoopInfo *LI,
MemorySSAUpdater *MSSAU,
bool PreserveLCSSA) {
bool Changed = false;
SmallVector<BasicBlock *, 4> InLoopPredecessors;
auto RewriteExit = [&](BasicBlock *BB) {
assert(InLoopPredecessors.empty() &&
"Must start with an empty predecessors list!");
auto Cleanup = make_scope_exit([&] { InLoopPredecessors.clear(); });
bool IsDedicatedExit = true;
for (auto *PredBB : predecessors(BB))
if (L->contains(PredBB)) {
if (isa<IndirectBrInst>(PredBB->getTerminator()))
return false;
InLoopPredecessors.push_back(PredBB);
} else {
IsDedicatedExit = false;
}
assert(!InLoopPredecessors.empty() && "Must have *some* loop predecessor!");
if (IsDedicatedExit)
return false;
auto *NewExitBB = SplitBlockPredecessors(
BB, InLoopPredecessors, ".loopexit", DT, LI, MSSAU, PreserveLCSSA);
if (!NewExitBB)
LLVM_DEBUG(
dbgs() << "WARNING: Can't create a dedicated exit block for loop: "
<< *L << "\n");
else
LLVM_DEBUG(dbgs() << "LoopSimplify: Creating dedicated exit block "
<< NewExitBB->getName() << "\n");
return true;
};
SmallPtrSet<BasicBlock *, 4> Visited;
for (auto *BB : L->blocks())
for (auto *SuccBB : successors(BB)) {
if (L->contains(SuccBB))
continue;
if (!Visited.insert(SuccBB).second)
continue;
Changed |= RewriteExit(SuccBB);
}
return Changed;
}
SmallVector<Instruction *, 8> llvm::findDefsUsedOutsideOfLoop(Loop *L) {
SmallVector<Instruction *, 8> UsedOutside;
for (auto *Block : L->getBlocks())
for (auto &Inst : *Block) {
auto Users = Inst.users();
if (any_of(Users, [&](User *U) {
auto *Use = cast<Instruction>(U);
return !L->contains(Use->getParent());
}))
UsedOutside.push_back(&Inst);
}
return UsedOutside;
}
void llvm::getLoopAnalysisUsage(AnalysisUsage &AU) {
AU.addRequired<DominatorTreeWrapperPass>();
AU.addPreserved<DominatorTreeWrapperPass>();
AU.addRequired<LoopInfoWrapperPass>();
AU.addPreserved<LoopInfoWrapperPass>();
extern char &LoopSimplifyID;
extern char &LCSSAID;
AU.addRequiredID(LoopSimplifyID);
AU.addPreservedID(LoopSimplifyID);
AU.addRequiredID(LCSSAID);
AU.addPreservedID(LCSSAID);
AU.addRequired<LCSSAVerificationPass>();
AU.addPreserved<LCSSAVerificationPass>();
AU.addRequired<AAResultsWrapperPass>();
AU.addPreserved<AAResultsWrapperPass>();
AU.addPreserved<BasicAAWrapperPass>();
AU.addPreserved<GlobalsAAWrapperPass>();
AU.addPreserved<SCEVAAWrapperPass>();
AU.addRequired<ScalarEvolutionWrapperPass>();
AU.addPreserved<ScalarEvolutionWrapperPass>();
}
void llvm::initializeLoopPassPass(PassRegistry &Registry) {
INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass)
INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass)
INITIALIZE_PASS_DEPENDENCY(LoopSimplify)
INITIALIZE_PASS_DEPENDENCY(LCSSAWrapperPass)
INITIALIZE_PASS_DEPENDENCY(AAResultsWrapperPass)
INITIALIZE_PASS_DEPENDENCY(BasicAAWrapperPass)
INITIALIZE_PASS_DEPENDENCY(GlobalsAAWrapperPass)
INITIALIZE_PASS_DEPENDENCY(SCEVAAWrapperPass)
INITIALIZE_PASS_DEPENDENCY(ScalarEvolutionWrapperPass)
INITIALIZE_PASS_DEPENDENCY(MemorySSAWrapperPass)
}
static MDNode *createStringMetadata(Loop *TheLoop, StringRef Name, unsigned V) {
LLVMContext &Context = TheLoop->getHeader()->getContext();
Metadata *MDs[] = {
MDString::get(Context, Name),
ConstantAsMetadata::get(ConstantInt::get(Type::getInt32Ty(Context), V))};
return MDNode::get(Context, MDs);
}
void llvm::addStringMetadataToLoop(Loop *TheLoop, const char *StringMD,
unsigned V) {
SmallVector<Metadata *, 4> MDs(1);
MDNode *LoopID = TheLoop->getLoopID();
if (LoopID) {
for (unsigned i = 1, ie = LoopID->getNumOperands(); i < ie; ++i) {
MDNode *Node = cast<MDNode>(LoopID->getOperand(i));
if (Node->getNumOperands() == 2) {
MDString *S = dyn_cast<MDString>(Node->getOperand(0));
if (S && S->getString().equals(StringMD)) {
ConstantInt *IntMD =
mdconst::extract_or_null<ConstantInt>(Node->getOperand(1));
if (IntMD && IntMD->getSExtValue() == V)
return;
continue;
}
}
MDs.push_back(Node);
}
}
MDs.push_back(createStringMetadata(TheLoop, StringMD, V));
LLVMContext &Context = TheLoop->getHeader()->getContext();
MDNode *NewLoopID = MDNode::get(Context, MDs);
NewLoopID->replaceOperandWith(0, NewLoopID);
TheLoop->setLoopID(NewLoopID);
}
Optional<ElementCount>
llvm::getOptionalElementCountLoopAttribute(const Loop *TheLoop) {
Optional<int> Width =
getOptionalIntLoopAttribute(TheLoop, "llvm.loop.vectorize.width");
if (Width) {
Optional<int> IsScalable = getOptionalIntLoopAttribute(
TheLoop, "llvm.loop.vectorize.scalable.enable");
return ElementCount::get(*Width, IsScalable.value_or(false));
}
return None;
}
Optional<MDNode *> llvm::makeFollowupLoopID(
MDNode *OrigLoopID, ArrayRef<StringRef> FollowupOptions,
const char *InheritOptionsExceptPrefix, bool AlwaysNew) {
if (!OrigLoopID) {
if (AlwaysNew)
return nullptr;
return None;
}
assert(OrigLoopID->getOperand(0) == OrigLoopID);
bool InheritAllAttrs = !InheritOptionsExceptPrefix;
bool InheritSomeAttrs =
InheritOptionsExceptPrefix && InheritOptionsExceptPrefix[0] != '\0';
SmallVector<Metadata *, 8> MDs;
MDs.push_back(nullptr);
bool Changed = false;
if (InheritAllAttrs || InheritSomeAttrs) {
for (const MDOperand &Existing : drop_begin(OrigLoopID->operands())) {
MDNode *Op = cast<MDNode>(Existing.get());
auto InheritThisAttribute = [InheritSomeAttrs,
InheritOptionsExceptPrefix](MDNode *Op) {
if (!InheritSomeAttrs)
return false;
if (Op->getNumOperands() == 0)
return true;
Metadata *NameMD = Op->getOperand(0).get();
if (!isa<MDString>(NameMD))
return true;
StringRef AttrName = cast<MDString>(NameMD)->getString();
return !AttrName.startswith(InheritOptionsExceptPrefix);
};
if (InheritThisAttribute(Op))
MDs.push_back(Op);
else
Changed = true;
}
} else {
Changed = OrigLoopID->getNumOperands() > 1;
}
bool HasAnyFollowup = false;
for (StringRef OptionName : FollowupOptions) {
MDNode *FollowupNode = findOptionMDForLoopID(OrigLoopID, OptionName);
if (!FollowupNode)
continue;
HasAnyFollowup = true;
for (const MDOperand &Option : drop_begin(FollowupNode->operands())) {
MDs.push_back(Option.get());
Changed = true;
}
}
if (!AlwaysNew && !HasAnyFollowup)
return None;
if (!AlwaysNew && !Changed)
return OrigLoopID;
if (MDs.size() == 1)
return nullptr;
MDTuple *FollowupLoopID = MDNode::get(OrigLoopID->getContext(), MDs);
FollowupLoopID->replaceOperandWith(0, FollowupLoopID);
return FollowupLoopID;
}
bool llvm::hasDisableAllTransformsHint(const Loop *L) {
return getBooleanLoopAttribute(L, LLVMLoopDisableNonforced);
}
bool llvm::hasDisableLICMTransformsHint(const Loop *L) {
return getBooleanLoopAttribute(L, LLVMLoopDisableLICM);
}
TransformationMode llvm::hasUnrollTransformation(const Loop *L) {
if (getBooleanLoopAttribute(L, "llvm.loop.unroll.disable"))
return TM_SuppressedByUser;
Optional<int> Count =
getOptionalIntLoopAttribute(L, "llvm.loop.unroll.count");
if (Count)
return Count.value() == 1 ? TM_SuppressedByUser : TM_ForcedByUser;
if (getBooleanLoopAttribute(L, "llvm.loop.unroll.enable"))
return TM_ForcedByUser;
if (getBooleanLoopAttribute(L, "llvm.loop.unroll.full"))
return TM_ForcedByUser;
if (hasDisableAllTransformsHint(L))
return TM_Disable;
return TM_Unspecified;
}
TransformationMode llvm::hasUnrollAndJamTransformation(const Loop *L) {
if (getBooleanLoopAttribute(L, "llvm.loop.unroll_and_jam.disable"))
return TM_SuppressedByUser;
Optional<int> Count =
getOptionalIntLoopAttribute(L, "llvm.loop.unroll_and_jam.count");
if (Count)
return Count.value() == 1 ? TM_SuppressedByUser : TM_ForcedByUser;
if (getBooleanLoopAttribute(L, "llvm.loop.unroll_and_jam.enable"))
return TM_ForcedByUser;
if (hasDisableAllTransformsHint(L))
return TM_Disable;
return TM_Unspecified;
}
TransformationMode llvm::hasVectorizeTransformation(const Loop *L) {
Optional<bool> Enable =
getOptionalBoolLoopAttribute(L, "llvm.loop.vectorize.enable");
if (Enable == false)
return TM_SuppressedByUser;
Optional<ElementCount> VectorizeWidth =
getOptionalElementCountLoopAttribute(L);
Optional<int> InterleaveCount =
getOptionalIntLoopAttribute(L, "llvm.loop.interleave.count");
if (Enable == true && VectorizeWidth && VectorizeWidth->isScalar() &&
InterleaveCount == 1)
return TM_SuppressedByUser;
if (getBooleanLoopAttribute(L, "llvm.loop.isvectorized"))
return TM_Disable;
if (Enable == true)
return TM_ForcedByUser;
if ((VectorizeWidth && VectorizeWidth->isScalar()) && InterleaveCount == 1)
return TM_Disable;
if ((VectorizeWidth && VectorizeWidth->isVector()) || InterleaveCount > 1)
return TM_Enable;
if (hasDisableAllTransformsHint(L))
return TM_Disable;
return TM_Unspecified;
}
TransformationMode llvm::hasDistributeTransformation(const Loop *L) {
if (getBooleanLoopAttribute(L, "llvm.loop.distribute.enable"))
return TM_ForcedByUser;
if (hasDisableAllTransformsHint(L))
return TM_Disable;
return TM_Unspecified;
}
TransformationMode llvm::hasLICMVersioningTransformation(const Loop *L) {
if (getBooleanLoopAttribute(L, "llvm.loop.licm_versioning.disable"))
return TM_SuppressedByUser;
if (hasDisableAllTransformsHint(L))
return TM_Disable;
return TM_Unspecified;
}
SmallVector<DomTreeNode *, 16>
llvm::collectChildrenInLoop(DomTreeNode *N, const Loop *CurLoop) {
SmallVector<DomTreeNode *, 16> Worklist;
auto AddRegionToWorklist = [&](DomTreeNode *DTN) {
BasicBlock *BB = DTN->getBlock();
if (CurLoop->contains(BB))
Worklist.push_back(DTN);
};
AddRegionToWorklist(N);
for (size_t I = 0; I < Worklist.size(); I++) {
for (DomTreeNode *Child : Worklist[I]->children())
AddRegionToWorklist(Child);
}
return Worklist;
}
void llvm::deleteDeadLoop(Loop *L, DominatorTree *DT, ScalarEvolution *SE,
LoopInfo *LI, MemorySSA *MSSA) {
assert((!DT || L->isLCSSAForm(*DT)) && "Expected LCSSA!");
auto *Preheader = L->getLoopPreheader();
assert(Preheader && "Preheader should exist!");
std::unique_ptr<MemorySSAUpdater> MSSAU;
if (MSSA)
MSSAU = std::make_unique<MemorySSAUpdater>(MSSA);
if (SE)
SE->forgetLoop(L);
Instruction *OldTerm = Preheader->getTerminator();
assert(!OldTerm->mayHaveSideEffects() &&
"Preheader must end with a side-effect-free terminator");
assert(OldTerm->getNumSuccessors() == 1 &&
"Preheader must have a single successor");
IRBuilder<> Builder(OldTerm);
auto *ExitBlock = L->getUniqueExitBlock();
DomTreeUpdater DTU(DT, DomTreeUpdater::UpdateStrategy::Eager);
if (ExitBlock) {
assert(ExitBlock && "Should have a unique exit block!");
assert(L->hasDedicatedExits() && "Loop should have dedicated exits!");
Builder.CreateCondBr(Builder.getFalse(), L->getHeader(), ExitBlock);
OldTerm->eraseFromParent();
for (PHINode &P : ExitBlock->phis()) {
int PredIndex = 0;
P.setIncomingBlock(PredIndex, Preheader);
for (unsigned i = 0, e = P.getNumIncomingValues() - 1; i != e; ++i)
P.removeIncomingValue(e - i, false);
assert((P.getNumIncomingValues() == 1 &&
P.getIncomingBlock(PredIndex) == Preheader) &&
"Should have exactly one value and that's from the preheader!");
}
if (DT) {
DTU.applyUpdates({{DominatorTree::Insert, Preheader, ExitBlock}});
if (MSSA) {
MSSAU->applyUpdates({{DominatorTree::Insert, Preheader, ExitBlock}},
*DT);
if (VerifyMemorySSA)
MSSA->verifyMemorySSA();
}
}
Builder.SetInsertPoint(Preheader->getTerminator());
Builder.CreateBr(ExitBlock);
Preheader->getTerminator()->eraseFromParent();
} else {
assert(L->hasNoExitBlocks() &&
"Loop should have either zero or one exit blocks.");
Builder.SetInsertPoint(OldTerm);
Builder.CreateUnreachable();
Preheader->getTerminator()->eraseFromParent();
}
if (DT) {
DTU.applyUpdates({{DominatorTree::Delete, Preheader, L->getHeader()}});
if (MSSA) {
MSSAU->applyUpdates({{DominatorTree::Delete, Preheader, L->getHeader()}},
*DT);
SmallSetVector<BasicBlock *, 8> DeadBlockSet(L->block_begin(),
L->block_end());
MSSAU->removeBlocks(DeadBlockSet);
if (VerifyMemorySSA)
MSSA->verifyMemorySSA();
}
}
llvm::SmallDenseSet<std::pair<DIVariable *, DIExpression *>, 4> DeadDebugSet;
llvm::SmallVector<DbgVariableIntrinsic *, 4> DeadDebugInst;
if (ExitBlock) {
for (auto *Block : L->blocks())
for (Instruction &I : *Block) {
auto *Poison = PoisonValue::get(I.getType());
for (Use &U : llvm::make_early_inc_range(I.uses())) {
if (auto *Usr = dyn_cast<Instruction>(U.getUser()))
if (L->contains(Usr->getParent()))
continue;
if (DT)
assert(!DT->isReachableFromEntry(U) &&
"Unexpected user in reachable block");
U.set(Poison);
}
auto *DVI = dyn_cast<DbgVariableIntrinsic>(&I);
if (!DVI)
continue;
auto Key =
DeadDebugSet.find({DVI->getVariable(), DVI->getExpression()});
if (Key != DeadDebugSet.end())
continue;
DeadDebugSet.insert({DVI->getVariable(), DVI->getExpression()});
DeadDebugInst.push_back(DVI);
}
DIBuilder DIB(*ExitBlock->getModule());
Instruction *InsertDbgValueBefore = ExitBlock->getFirstNonPHI();
assert(InsertDbgValueBefore &&
"There should be a non-PHI instruction in exit block, else these "
"instructions will have no parent.");
for (auto *DVI : DeadDebugInst)
DIB.insertDbgValueIntrinsic(UndefValue::get(Builder.getInt32Ty()),
DVI->getVariable(), DVI->getExpression(),
DVI->getDebugLoc(), InsertDbgValueBefore);
}
for (auto *Block : L->blocks())
Block->dropAllReferences();
if (MSSA && VerifyMemorySSA)
MSSA->verifyMemorySSA();
if (LI) {
for (BasicBlock *BB : L->blocks())
BB->eraseFromParent();
SmallPtrSet<BasicBlock *, 8> blocks;
blocks.insert(L->block_begin(), L->block_end());
for (BasicBlock *BB : blocks)
LI->removeBlock(BB);
if (Loop *ParentLoop = L->getParentLoop()) {
Loop::iterator I = find(*ParentLoop, L);
assert(I != ParentLoop->end() && "Couldn't find loop");
ParentLoop->removeChildLoop(I);
} else {
Loop::iterator I = find(*LI, L);
assert(I != LI->end() && "Couldn't find loop");
LI->removeLoop(I);
}
LI->destroy(L);
}
}
void llvm::breakLoopBackedge(Loop *L, DominatorTree &DT, ScalarEvolution &SE,
LoopInfo &LI, MemorySSA *MSSA) {
auto *Latch = L->getLoopLatch();
assert(Latch && "multiple latches not yet supported");
auto *Header = L->getHeader();
Loop *OutermostLoop = L->getOutermostLoop();
SE.forgetLoop(L);
std::unique_ptr<MemorySSAUpdater> MSSAU;
if (MSSA)
MSSAU = std::make_unique<MemorySSAUpdater>(MSSA);
[&]() -> void {
if (auto *BI = dyn_cast<BranchInst>(Latch->getTerminator())) {
if (!BI->isConditional()) {
DomTreeUpdater DTU(&DT, DomTreeUpdater::UpdateStrategy::Eager);
(void)changeToUnreachable(BI, true, &DTU,
MSSAU.get());
return;
}
if (L->isLoopExiting(Latch)) {
const unsigned ExitIdx = L->contains(BI->getSuccessor(0)) ? 1 : 0;
BasicBlock *ExitBB = BI->getSuccessor(ExitIdx);
DomTreeUpdater DTU(&DT, DomTreeUpdater::UpdateStrategy::Eager);
Header->removePredecessor(Latch, true);
IRBuilder<> Builder(BI);
auto *NewBI = Builder.CreateBr(ExitBB);
NewBI->copyMetadata(*BI, {LLVMContext::MD_dbg,
LLVMContext::MD_annotation});
BI->eraseFromParent();
DTU.applyUpdates({{DominatorTree::Delete, Latch, Header}});
if (MSSA)
MSSAU->applyUpdates({{DominatorTree::Delete, Latch, Header}}, DT);
return;
}
}
auto *BackedgeBB = SplitEdge(Latch, Header, &DT, &LI, MSSAU.get());
DomTreeUpdater DTU(&DT, DomTreeUpdater::UpdateStrategy::Eager);
(void)changeToUnreachable(BackedgeBB->getTerminator(),
true, &DTU, MSSAU.get());
}();
LI.erase(L);
if (OutermostLoop != L)
formLCSSARecursively(*OutermostLoop, DT, &LI, &SE);
}
static BranchInst *getExpectedExitLoopLatchBranch(Loop *L) {
BasicBlock *Latch = L->getLoopLatch();
if (!Latch)
return nullptr;
BranchInst *LatchBR = dyn_cast<BranchInst>(Latch->getTerminator());
if (!LatchBR || LatchBR->getNumSuccessors() != 2 || !L->isLoopExiting(Latch))
return nullptr;
assert((LatchBR->getSuccessor(0) == L->getHeader() ||
LatchBR->getSuccessor(1) == L->getHeader()) &&
"At least one edge out of the latch must go to the header");
return LatchBR;
}
static Optional<uint64_t>
getEstimatedTripCount(BranchInst *ExitingBranch, Loop *L,
uint64_t &OrigExitWeight) {
uint64_t LoopWeight, ExitWeight;
if (!ExitingBranch->extractProfMetadata(LoopWeight, ExitWeight))
return None;
if (L->contains(ExitingBranch->getSuccessor(1)))
std::swap(LoopWeight, ExitWeight);
if (!ExitWeight)
return None;
OrigExitWeight = ExitWeight;
uint64_t ExitCount = llvm::divideNearest(LoopWeight, ExitWeight);
return ExitCount + 1;
}
Optional<unsigned>
llvm::getLoopEstimatedTripCount(Loop *L,
unsigned *EstimatedLoopInvocationWeight) {
if (BranchInst *LatchBranch = getExpectedExitLoopLatchBranch(L)) {
uint64_t ExitWeight;
if (Optional<uint64_t> EstTripCount =
getEstimatedTripCount(LatchBranch, L, ExitWeight)) {
if (EstimatedLoopInvocationWeight)
*EstimatedLoopInvocationWeight = ExitWeight;
return *EstTripCount;
}
}
return None;
}
bool llvm::setLoopEstimatedTripCount(Loop *L, unsigned EstimatedTripCount,
unsigned EstimatedloopInvocationWeight) {
BranchInst *LatchBranch = getExpectedExitLoopLatchBranch(L);
if (!LatchBranch)
return false;
unsigned LatchExitWeight = 0;
unsigned BackedgeTakenWeight = 0;
if (EstimatedTripCount > 0) {
LatchExitWeight = EstimatedloopInvocationWeight;
BackedgeTakenWeight = (EstimatedTripCount - 1) * LatchExitWeight;
}
if (LatchBranch->getSuccessor(0) != L->getHeader())
std::swap(BackedgeTakenWeight, LatchExitWeight);
MDBuilder MDB(LatchBranch->getContext());
LatchBranch->setMetadata(
LLVMContext::MD_prof,
MDB.createBranchWeights(BackedgeTakenWeight, LatchExitWeight));
return true;
}
bool llvm::hasIterationCountInvariantInParent(Loop *InnerLoop,
ScalarEvolution &SE) {
Loop *OuterL = InnerLoop->getParentLoop();
if (!OuterL)
return true;
BasicBlock *InnerLoopLatch = InnerLoop->getLoopLatch();
const SCEV *InnerLoopBECountSC = SE.getExitCount(InnerLoop, InnerLoopLatch);
if (isa<SCEVCouldNotCompute>(InnerLoopBECountSC) ||
!InnerLoopBECountSC->getType()->isIntegerTy())
return false;
ScalarEvolution::LoopDisposition LD =
SE.getLoopDisposition(InnerLoopBECountSC, OuterL);
if (LD != ScalarEvolution::LoopInvariant)
return false;
return true;
}
CmpInst::Predicate llvm::getMinMaxReductionPredicate(RecurKind RK) {
switch (RK) {
default:
llvm_unreachable("Unknown min/max recurrence kind");
case RecurKind::UMin:
return CmpInst::ICMP_ULT;
case RecurKind::UMax:
return CmpInst::ICMP_UGT;
case RecurKind::SMin:
return CmpInst::ICMP_SLT;
case RecurKind::SMax:
return CmpInst::ICMP_SGT;
case RecurKind::FMin:
return CmpInst::FCMP_OLT;
case RecurKind::FMax:
return CmpInst::FCMP_OGT;
}
}
Value *llvm::createSelectCmpOp(IRBuilderBase &Builder, Value *StartVal,
RecurKind RK, Value *Left, Value *Right) {
if (auto VTy = dyn_cast<VectorType>(Left->getType()))
StartVal = Builder.CreateVectorSplat(VTy->getElementCount(), StartVal);
Value *Cmp =
Builder.CreateCmp(CmpInst::ICMP_NE, Left, StartVal, "rdx.select.cmp");
return Builder.CreateSelect(Cmp, Left, Right, "rdx.select");
}
Value *llvm::createMinMaxOp(IRBuilderBase &Builder, RecurKind RK, Value *Left,
Value *Right) {
CmpInst::Predicate Pred = getMinMaxReductionPredicate(RK);
Value *Cmp = Builder.CreateCmp(Pred, Left, Right, "rdx.minmax.cmp");
Value *Select = Builder.CreateSelect(Cmp, Left, Right, "rdx.minmax.select");
return Select;
}
Value *llvm::getOrderedReduction(IRBuilderBase &Builder, Value *Acc, Value *Src,
unsigned Op, RecurKind RdxKind) {
unsigned VF = cast<FixedVectorType>(Src->getType())->getNumElements();
Value *Result = Acc;
for (unsigned ExtractIdx = 0; ExtractIdx != VF; ++ExtractIdx) {
Value *Ext =
Builder.CreateExtractElement(Src, Builder.getInt32(ExtractIdx));
if (Op != Instruction::ICmp && Op != Instruction::FCmp) {
Result = Builder.CreateBinOp((Instruction::BinaryOps)Op, Result, Ext,
"bin.rdx");
} else {
assert(RecurrenceDescriptor::isMinMaxRecurrenceKind(RdxKind) &&
"Invalid min/max");
Result = createMinMaxOp(Builder, RdxKind, Result, Ext);
}
}
return Result;
}
Value *llvm::getShuffleReduction(IRBuilderBase &Builder, Value *Src,
unsigned Op, RecurKind RdxKind) {
unsigned VF = cast<FixedVectorType>(Src->getType())->getNumElements();
assert(isPowerOf2_32(VF) &&
"Reduction emission only supported for pow2 vectors!");
Value *TmpVec = Src;
SmallVector<int, 32> ShuffleMask(VF);
for (unsigned i = VF; i != 1; i >>= 1) {
for (unsigned j = 0; j != i / 2; ++j)
ShuffleMask[j] = i / 2 + j;
std::fill(&ShuffleMask[i / 2], ShuffleMask.end(), -1);
Value *Shuf = Builder.CreateShuffleVector(TmpVec, ShuffleMask, "rdx.shuf");
if (Op != Instruction::ICmp && Op != Instruction::FCmp) {
TmpVec = Builder.CreateBinOp((Instruction::BinaryOps)Op, TmpVec, Shuf,
"bin.rdx");
} else {
assert(RecurrenceDescriptor::isMinMaxRecurrenceKind(RdxKind) &&
"Invalid min/max");
TmpVec = createMinMaxOp(Builder, RdxKind, TmpVec, Shuf);
}
}
return Builder.CreateExtractElement(TmpVec, Builder.getInt32(0));
}
Value *llvm::createSelectCmpTargetReduction(IRBuilderBase &Builder,
const TargetTransformInfo *TTI,
Value *Src,
const RecurrenceDescriptor &Desc,
PHINode *OrigPhi) {
assert(RecurrenceDescriptor::isSelectCmpRecurrenceKind(
Desc.getRecurrenceKind()) &&
"Unexpected reduction kind");
Value *InitVal = Desc.getRecurrenceStartValue();
Value *NewVal = nullptr;
SelectInst *SI = nullptr;
for (auto *U : OrigPhi->users()) {
if ((SI = dyn_cast<SelectInst>(U)))
break;
}
assert(SI && "One user of the original phi should be a select");
if (SI->getTrueValue() == OrigPhi)
NewVal = SI->getFalseValue();
else {
assert(SI->getFalseValue() == OrigPhi &&
"At least one input to the select should be the original Phi");
NewVal = SI->getTrueValue();
}
ElementCount EC = cast<VectorType>(Src->getType())->getElementCount();
Value *Right = Builder.CreateVectorSplat(EC, InitVal);
Value *Cmp =
Builder.CreateCmp(CmpInst::ICMP_NE, Src, Right, "rdx.select.cmp");
Cmp = Builder.CreateOrReduce(Cmp);
return Builder.CreateSelect(Cmp, NewVal, InitVal, "rdx.select");
}
Value *llvm::createSimpleTargetReduction(IRBuilderBase &Builder,
const TargetTransformInfo *TTI,
Value *Src, RecurKind RdxKind) {
auto *SrcVecEltTy = cast<VectorType>(Src->getType())->getElementType();
switch (RdxKind) {
case RecurKind::Add:
return Builder.CreateAddReduce(Src);
case RecurKind::Mul:
return Builder.CreateMulReduce(Src);
case RecurKind::And:
return Builder.CreateAndReduce(Src);
case RecurKind::Or:
return Builder.CreateOrReduce(Src);
case RecurKind::Xor:
return Builder.CreateXorReduce(Src);
case RecurKind::FMulAdd:
case RecurKind::FAdd:
return Builder.CreateFAddReduce(ConstantFP::getNegativeZero(SrcVecEltTy),
Src);
case RecurKind::FMul:
return Builder.CreateFMulReduce(ConstantFP::get(SrcVecEltTy, 1.0), Src);
case RecurKind::SMax:
return Builder.CreateIntMaxReduce(Src, true);
case RecurKind::SMin:
return Builder.CreateIntMinReduce(Src, true);
case RecurKind::UMax:
return Builder.CreateIntMaxReduce(Src, false);
case RecurKind::UMin:
return Builder.CreateIntMinReduce(Src, false);
case RecurKind::FMax:
return Builder.CreateFPMaxReduce(Src);
case RecurKind::FMin:
return Builder.CreateFPMinReduce(Src);
default:
llvm_unreachable("Unhandled opcode");
}
}
Value *llvm::createTargetReduction(IRBuilderBase &B,
const TargetTransformInfo *TTI,
const RecurrenceDescriptor &Desc, Value *Src,
PHINode *OrigPhi) {
IRBuilderBase::FastMathFlagGuard FMFGuard(B);
B.setFastMathFlags(Desc.getFastMathFlags());
RecurKind RK = Desc.getRecurrenceKind();
if (RecurrenceDescriptor::isSelectCmpRecurrenceKind(RK))
return createSelectCmpTargetReduction(B, TTI, Src, Desc, OrigPhi);
return createSimpleTargetReduction(B, TTI, Src, RK);
}
Value *llvm::createOrderedReduction(IRBuilderBase &B,
const RecurrenceDescriptor &Desc,
Value *Src, Value *Start) {
assert((Desc.getRecurrenceKind() == RecurKind::FAdd ||
Desc.getRecurrenceKind() == RecurKind::FMulAdd) &&
"Unexpected reduction kind");
assert(Src->getType()->isVectorTy() && "Expected a vector type");
assert(!Start->getType()->isVectorTy() && "Expected a scalar type");
return B.CreateFAddReduce(Start, Src);
}
void llvm::propagateIRFlags(Value *I, ArrayRef<Value *> VL, Value *OpValue,
bool IncludeWrapFlags) {
auto *VecOp = dyn_cast<Instruction>(I);
if (!VecOp)
return;
auto *Intersection = (OpValue == nullptr) ? dyn_cast<Instruction>(VL[0])
: dyn_cast<Instruction>(OpValue);
if (!Intersection)
return;
const unsigned Opcode = Intersection->getOpcode();
VecOp->copyIRFlags(Intersection, IncludeWrapFlags);
for (auto *V : VL) {
auto *Instr = dyn_cast<Instruction>(V);
if (!Instr)
continue;
if (OpValue == nullptr || Opcode == Instr->getOpcode())
VecOp->andIRFlags(V);
}
}
bool llvm::isKnownNegativeInLoop(const SCEV *S, const Loop *L,
ScalarEvolution &SE) {
const SCEV *Zero = SE.getZero(S->getType());
return SE.isAvailableAtLoopEntry(S, L) &&
SE.isLoopEntryGuardedByCond(L, ICmpInst::ICMP_SLT, S, Zero);
}
bool llvm::isKnownNonNegativeInLoop(const SCEV *S, const Loop *L,
ScalarEvolution &SE) {
const SCEV *Zero = SE.getZero(S->getType());
return SE.isAvailableAtLoopEntry(S, L) &&
SE.isLoopEntryGuardedByCond(L, ICmpInst::ICMP_SGE, S, Zero);
}
bool llvm::cannotBeMinInLoop(const SCEV *S, const Loop *L, ScalarEvolution &SE,
bool Signed) {
unsigned BitWidth = cast<IntegerType>(S->getType())->getBitWidth();
APInt Min = Signed ? APInt::getSignedMinValue(BitWidth) :
APInt::getMinValue(BitWidth);
auto Predicate = Signed ? ICmpInst::ICMP_SGT : ICmpInst::ICMP_UGT;
return SE.isAvailableAtLoopEntry(S, L) &&
SE.isLoopEntryGuardedByCond(L, Predicate, S,
SE.getConstant(Min));
}
bool llvm::cannotBeMaxInLoop(const SCEV *S, const Loop *L, ScalarEvolution &SE,
bool Signed) {
unsigned BitWidth = cast<IntegerType>(S->getType())->getBitWidth();
APInt Max = Signed ? APInt::getSignedMaxValue(BitWidth) :
APInt::getMaxValue(BitWidth);
auto Predicate = Signed ? ICmpInst::ICMP_SLT : ICmpInst::ICMP_ULT;
return SE.isAvailableAtLoopEntry(S, L) &&
SE.isLoopEntryGuardedByCond(L, Predicate, S,
SE.getConstant(Max));
}
static bool hasHardUserWithinLoop(const Loop *L, const Instruction *I) {
SmallPtrSet<const Instruction *, 8> Visited;
SmallVector<const Instruction *, 8> WorkList;
Visited.insert(I);
WorkList.push_back(I);
while (!WorkList.empty()) {
const Instruction *Curr = WorkList.pop_back_val();
if (!L->contains(Curr))
continue;
if (Curr->mayHaveSideEffects())
return true;
for (auto U : Curr->users()) {
auto *UI = cast<Instruction>(U);
if (Visited.insert(UI).second)
WorkList.push_back(UI);
}
}
return false;
}
struct RewritePhi {
PHINode *PN; unsigned Ith; const SCEV *ExpansionSCEV; Instruction *ExpansionPoint; bool HighCost;
RewritePhi(PHINode *P, unsigned I, const SCEV *Val, Instruction *ExpansionPt,
bool H)
: PN(P), Ith(I), ExpansionSCEV(Val), ExpansionPoint(ExpansionPt),
HighCost(H) {}
};
static bool canLoopBeDeleted(Loop *L, SmallVector<RewritePhi, 8> &RewritePhiSet) {
BasicBlock *Preheader = L->getLoopPreheader();
if (!Preheader)
return false;
SmallVector<BasicBlock *, 4> ExitingBlocks;
L->getExitingBlocks(ExitingBlocks);
SmallVector<BasicBlock *, 8> ExitBlocks;
L->getUniqueExitBlocks(ExitBlocks);
if (ExitBlocks.size() != 1 || ExitingBlocks.size() != 1)
return false;
BasicBlock *ExitBlock = ExitBlocks[0];
BasicBlock::iterator BI = ExitBlock->begin();
while (PHINode *P = dyn_cast<PHINode>(BI)) {
Value *Incoming = P->getIncomingValueForBlock(ExitingBlocks[0]);
bool found = false;
for (const RewritePhi &Phi : RewritePhiSet) {
unsigned i = Phi.Ith;
if (Phi.PN == P && (Phi.PN)->getIncomingValue(i) == Incoming) {
found = true;
break;
}
}
Instruction *I;
if (!found && (I = dyn_cast<Instruction>(Incoming)))
if (!L->hasLoopInvariantOperands(I))
return false;
++BI;
}
for (auto *BB : L->blocks())
if (llvm::any_of(*BB, [](Instruction &I) {
return I.mayHaveSideEffects();
}))
return false;
return true;
}
static bool checkIsIndPhi(PHINode *Phi, Loop *L, ScalarEvolution *SE,
InductionDescriptor &ID) {
if (!Phi)
return false;
if (!L->getLoopPreheader())
return false;
if (Phi->getParent() != L->getHeader())
return false;
return InductionDescriptor::isInductionPHI(Phi, L, SE, ID);
}
int llvm::rewriteLoopExitValues(Loop *L, LoopInfo *LI, TargetLibraryInfo *TLI,
ScalarEvolution *SE,
const TargetTransformInfo *TTI,
SCEVExpander &Rewriter, DominatorTree *DT,
ReplaceExitVal ReplaceExitValue,
SmallVector<WeakTrackingVH, 16> &DeadInsts) {
assert(L->isRecursivelyLCSSAForm(*DT, *LI) &&
"Indvars did not preserve LCSSA!");
SmallVector<BasicBlock*, 8> ExitBlocks;
L->getUniqueExitBlocks(ExitBlocks);
SmallVector<RewritePhi, 8> RewritePhiSet;
for (BasicBlock *ExitBB : ExitBlocks) {
PHINode *PN = dyn_cast<PHINode>(ExitBB->begin());
if (!PN) continue;
unsigned NumPreds = PN->getNumIncomingValues();
BasicBlock::iterator BBI = ExitBB->begin();
while ((PN = dyn_cast<PHINode>(BBI++))) {
if (PN->use_empty())
continue;
if (!SE->isSCEVable(PN->getType()))
continue;
for (unsigned i = 0; i != NumPreds; ++i) {
Value *InVal = PN->getIncomingValue(i);
if (!isa<Instruction>(InVal))
continue;
if (LI->getLoopFor(PN->getIncomingBlock(i)) != L)
continue;
Instruction *Inst = cast<Instruction>(InVal);
if (!L->contains(Inst))
continue;
if (ReplaceExitValue == UnusedIndVarInLoop) {
InductionDescriptor ID;
PHINode *IndPhi = dyn_cast<PHINode>(Inst);
if (IndPhi) {
if (!checkIsIndPhi(IndPhi, L, SE, ID))
continue;
if (llvm::any_of(Inst->users(), [&](User *U) {
if (!isa<PHINode>(U) && !isa<BinaryOperator>(U))
return true;
BinaryOperator *B = dyn_cast<BinaryOperator>(U);
if (B && B != ID.getInductionBinOp())
return true;
return false;
}))
continue;
} else {
BinaryOperator *B = dyn_cast<BinaryOperator>(Inst);
if (!B)
continue;
if (llvm::any_of(Inst->users(), [&](User *U) {
PHINode *Phi = dyn_cast<PHINode>(U);
if (Phi != PN && !checkIsIndPhi(Phi, L, SE, ID))
return true;
return false;
}))
continue;
if (B != ID.getInductionBinOp())
continue;
}
}
const SCEV *ExitValue = SE->getSCEVAtScope(Inst, L->getParentLoop());
if (isa<SCEVCouldNotCompute>(ExitValue) ||
!SE->isLoopInvariant(ExitValue, L) ||
!Rewriter.isSafeToExpand(ExitValue)) {
const SCEV *ExitCount = SE->getExitCount(L, PN->getIncomingBlock(i));
if (isa<SCEVCouldNotCompute>(ExitCount))
continue;
if (auto *AddRec = dyn_cast<SCEVAddRecExpr>(SE->getSCEV(Inst)))
if (AddRec->getLoop() == L)
ExitValue = AddRec->evaluateAtIteration(ExitCount, *SE);
if (isa<SCEVCouldNotCompute>(ExitValue) ||
!SE->isLoopInvariant(ExitValue, L) ||
!Rewriter.isSafeToExpand(ExitValue))
continue;
}
if (ReplaceExitValue != AlwaysRepl && !isa<SCEVConstant>(ExitValue) &&
!isa<SCEVUnknown>(ExitValue) && hasHardUserWithinLoop(L, Inst))
continue;
bool HighCost = Rewriter.isHighCostExpansion(
ExitValue, L, SCEVCheapExpansionBudget, TTI, Inst);
Instruction *InsertPt =
(isa<PHINode>(Inst) || isa<LandingPadInst>(Inst)) ?
&*Inst->getParent()->getFirstInsertionPt() : Inst;
RewritePhiSet.emplace_back(PN, i, ExitValue, InsertPt, HighCost);
}
}
}
bool LoopCanBeDel = canLoopBeDeleted(L, RewritePhiSet);
int NumReplaced = 0;
for (const RewritePhi &Phi : RewritePhiSet) {
PHINode *PN = Phi.PN;
if ((ReplaceExitValue == OnlyCheapRepl ||
ReplaceExitValue == UnusedIndVarInLoop) &&
!LoopCanBeDel && Phi.HighCost)
continue;
Value *ExitVal = Rewriter.expandCodeFor(
Phi.ExpansionSCEV, Phi.PN->getType(), Phi.ExpansionPoint);
LLVM_DEBUG(dbgs() << "rewriteLoopExitValues: AfterLoopVal = " << *ExitVal
<< '\n'
<< " LoopVal = " << *(Phi.ExpansionPoint) << "\n");
#ifndef NDEBUG
if (auto *ExitInsn = dyn_cast<Instruction>(ExitVal))
if (auto *EVL = LI->getLoopFor(ExitInsn->getParent()))
if (EVL != L)
assert(EVL->contains(L) && "LCSSA breach detected!");
#endif
NumReplaced++;
Instruction *Inst = cast<Instruction>(PN->getIncomingValue(Phi.Ith));
PN->setIncomingValue(Phi.Ith, ExitVal);
SE->forgetValue(PN);
if (isInstructionTriviallyDead(Inst, TLI))
DeadInsts.push_back(Inst);
if (PN->getNumIncomingValues() == 1 &&
LI->replacementPreservesLCSSAForm(PN, ExitVal)) {
PN->replaceAllUsesWith(ExitVal);
PN->eraseFromParent();
}
}
Rewriter.clearInsertPoint();
return NumReplaced;
}
void llvm::setProfileInfoAfterUnrolling(Loop *OrigLoop, Loop *UnrolledLoop,
Loop *RemainderLoop, uint64_t UF) {
assert(UF > 0 && "Zero unrolled factor is not supported");
assert(UnrolledLoop != RemainderLoop &&
"Unrolled and Remainder loops are expected to distinct");
unsigned OrigLoopInvocationWeight = 0;
Optional<unsigned> OrigAverageTripCount =
getLoopEstimatedTripCount(OrigLoop, &OrigLoopInvocationWeight);
if (!OrigAverageTripCount)
return;
unsigned UnrolledAverageTripCount = *OrigAverageTripCount / UF;
unsigned RemainderAverageTripCount = *OrigAverageTripCount % UF;
setLoopEstimatedTripCount(UnrolledLoop, UnrolledAverageTripCount,
OrigLoopInvocationWeight);
setLoopEstimatedTripCount(RemainderLoop, RemainderAverageTripCount,
OrigLoopInvocationWeight);
}
template <typename RangeT>
void llvm::appendReversedLoopsToWorklist(
RangeT &&Loops, SmallPriorityWorklist<Loop *, 4> &Worklist) {
SmallVector<Loop *, 4> PreOrderLoops, PreOrderWorklist;
for (Loop *RootL : Loops) {
assert(PreOrderLoops.empty() && "Must start with an empty preorder walk.");
assert(PreOrderWorklist.empty() &&
"Must start with an empty preorder walk worklist.");
PreOrderWorklist.push_back(RootL);
do {
Loop *L = PreOrderWorklist.pop_back_val();
PreOrderWorklist.append(L->begin(), L->end());
PreOrderLoops.push_back(L);
} while (!PreOrderWorklist.empty());
Worklist.insert(std::move(PreOrderLoops));
PreOrderLoops.clear();
}
}
template <typename RangeT>
void llvm::appendLoopsToWorklist(RangeT &&Loops,
SmallPriorityWorklist<Loop *, 4> &Worklist) {
appendReversedLoopsToWorklist(reverse(Loops), Worklist);
}
template void llvm::appendLoopsToWorklist<ArrayRef<Loop *> &>(
ArrayRef<Loop *> &Loops, SmallPriorityWorklist<Loop *, 4> &Worklist);
template void
llvm::appendLoopsToWorklist<Loop &>(Loop &L,
SmallPriorityWorklist<Loop *, 4> &Worklist);
void llvm::appendLoopsToWorklist(LoopInfo &LI,
SmallPriorityWorklist<Loop *, 4> &Worklist) {
appendReversedLoopsToWorklist(LI, Worklist);
}
Loop *llvm::cloneLoop(Loop *L, Loop *PL, ValueToValueMapTy &VM,
LoopInfo *LI, LPPassManager *LPM) {
Loop &New = *LI->AllocateLoop();
if (PL)
PL->addChildLoop(&New);
else
LI->addTopLevelLoop(&New);
if (LPM)
LPM->addLoop(New);
for (BasicBlock *BB : L->blocks())
if (LI->getLoopFor(BB) == L)
New.addBasicBlockToLoop(cast<BasicBlock>(VM[BB]), *LI);
for (Loop *I : *L)
cloneLoop(I, &New, VM, LI, LPM);
return &New;
}
struct PointerBounds {
TrackingVH<Value> Start;
TrackingVH<Value> End;
};
static PointerBounds expandBounds(const RuntimeCheckingPtrGroup *CG,
Loop *TheLoop, Instruction *Loc,
SCEVExpander &Exp) {
LLVMContext &Ctx = Loc->getContext();
Type *PtrArithTy = Type::getInt8PtrTy(Ctx, CG->AddressSpace);
Value *Start = nullptr, *End = nullptr;
LLVM_DEBUG(dbgs() << "LAA: Adding RT check for range:\n");
Start = Exp.expandCodeFor(CG->Low, PtrArithTy, Loc);
End = Exp.expandCodeFor(CG->High, PtrArithTy, Loc);
if (CG->NeedsFreeze) {
IRBuilder<> Builder(Loc);
Start = Builder.CreateFreeze(Start, Start->getName() + ".fr");
End = Builder.CreateFreeze(End, End->getName() + ".fr");
}
LLVM_DEBUG(dbgs() << "Start: " << *CG->Low << " End: " << *CG->High << "\n");
return {Start, End};
}
static SmallVector<std::pair<PointerBounds, PointerBounds>, 4>
expandBounds(const SmallVectorImpl<RuntimePointerCheck> &PointerChecks, Loop *L,
Instruction *Loc, SCEVExpander &Exp) {
SmallVector<std::pair<PointerBounds, PointerBounds>, 4> ChecksWithBounds;
transform(PointerChecks, std::back_inserter(ChecksWithBounds),
[&](const RuntimePointerCheck &Check) {
PointerBounds First = expandBounds(Check.first, L, Loc, Exp),
Second = expandBounds(Check.second, L, Loc, Exp);
return std::make_pair(First, Second);
});
return ChecksWithBounds;
}
Value *llvm::addRuntimeChecks(
Instruction *Loc, Loop *TheLoop,
const SmallVectorImpl<RuntimePointerCheck> &PointerChecks,
SCEVExpander &Exp) {
auto ExpandedChecks = expandBounds(PointerChecks, TheLoop, Loc, Exp);
LLVMContext &Ctx = Loc->getContext();
IRBuilder<InstSimplifyFolder> ChkBuilder(Ctx,
Loc->getModule()->getDataLayout());
ChkBuilder.SetInsertPoint(Loc);
Value *MemoryRuntimeCheck = nullptr;
for (const auto &Check : ExpandedChecks) {
const PointerBounds &A = Check.first, &B = Check.second;
unsigned AS0 = A.Start->getType()->getPointerAddressSpace();
unsigned AS1 = B.Start->getType()->getPointerAddressSpace();
assert((AS0 == B.End->getType()->getPointerAddressSpace()) &&
(AS1 == A.End->getType()->getPointerAddressSpace()) &&
"Trying to bounds check pointers with different address spaces");
Type *PtrArithTy0 = Type::getInt8PtrTy(Ctx, AS0);
Type *PtrArithTy1 = Type::getInt8PtrTy(Ctx, AS1);
Value *Start0 = ChkBuilder.CreateBitCast(A.Start, PtrArithTy0, "bc");
Value *Start1 = ChkBuilder.CreateBitCast(B.Start, PtrArithTy1, "bc");
Value *End0 = ChkBuilder.CreateBitCast(A.End, PtrArithTy1, "bc");
Value *End1 = ChkBuilder.CreateBitCast(B.End, PtrArithTy0, "bc");
Value *Cmp0 = ChkBuilder.CreateICmpULT(Start0, End1, "bound0");
Value *Cmp1 = ChkBuilder.CreateICmpULT(Start1, End0, "bound1");
Value *IsConflict = ChkBuilder.CreateAnd(Cmp0, Cmp1, "found.conflict");
if (MemoryRuntimeCheck) {
IsConflict =
ChkBuilder.CreateOr(MemoryRuntimeCheck, IsConflict, "conflict.rdx");
}
MemoryRuntimeCheck = IsConflict;
}
return MemoryRuntimeCheck;
}
Value *llvm::addDiffRuntimeChecks(
Instruction *Loc, Loop *TheLoop, ArrayRef<PointerDiffInfo> Checks,
SCEVExpander &Expander,
function_ref<Value *(IRBuilderBase &, unsigned)> GetVF, unsigned IC) {
LLVMContext &Ctx = Loc->getContext();
IRBuilder<InstSimplifyFolder> ChkBuilder(Ctx,
Loc->getModule()->getDataLayout());
ChkBuilder.SetInsertPoint(Loc);
Value *MemoryRuntimeCheck = nullptr;
for (auto &C : Checks) {
Type *Ty = C.SinkStart->getType();
auto *VFTimesUFTimesSize =
ChkBuilder.CreateMul(GetVF(ChkBuilder, Ty->getScalarSizeInBits()),
ConstantInt::get(Ty, IC * C.AccessSize));
Value *Sink = Expander.expandCodeFor(C.SinkStart, Ty, Loc);
Value *Src = Expander.expandCodeFor(C.SrcStart, Ty, Loc);
if (C.NeedsFreeze) {
IRBuilder<> Builder(Loc);
Sink = Builder.CreateFreeze(Sink, Sink->getName() + ".fr");
Src = Builder.CreateFreeze(Src, Src->getName() + ".fr");
}
Value *Diff = ChkBuilder.CreateSub(Sink, Src);
Value *IsConflict =
ChkBuilder.CreateICmpULT(Diff, VFTimesUFTimesSize, "diff.check");
if (MemoryRuntimeCheck) {
IsConflict =
ChkBuilder.CreateOr(MemoryRuntimeCheck, IsConflict, "conflict.rdx");
}
MemoryRuntimeCheck = IsConflict;
}
return MemoryRuntimeCheck;
}
Optional<IVConditionInfo> llvm::hasPartialIVCondition(Loop &L,
unsigned MSSAThreshold,
MemorySSA &MSSA,
AAResults &AA) {
auto *TI = dyn_cast<BranchInst>(L.getHeader()->getTerminator());
if (!TI || !TI->isConditional())
return {};
auto *CondI = dyn_cast<CmpInst>(TI->getCondition());
if (!CondI || !L.contains(CondI))
return {};
SmallVector<Instruction *> InstToDuplicate;
InstToDuplicate.push_back(CondI);
SmallVector<Value *, 4> WorkList;
WorkList.append(CondI->op_begin(), CondI->op_end());
SmallVector<MemoryAccess *, 4> AccessesToCheck;
SmallVector<MemoryLocation, 4> AccessedLocs;
while (!WorkList.empty()) {
Instruction *I = dyn_cast<Instruction>(WorkList.pop_back_val());
if (!I || !L.contains(I))
continue;
if (!isa<LoadInst>(I) && !isa<GetElementPtrInst>(I))
return {};
if (auto *LI = dyn_cast<LoadInst>(I))
if (LI->isVolatile() || LI->isAtomic())
return {};
InstToDuplicate.push_back(I);
if (MemoryAccess *MA = MSSA.getMemoryAccess(I)) {
if (auto *MemUse = dyn_cast_or_null<MemoryUse>(MA)) {
AccessesToCheck.push_back(MemUse->getDefiningAccess());
AccessedLocs.push_back(MemoryLocation::get(I));
} else {
return {};
}
}
WorkList.append(I->op_begin(), I->op_end());
}
if (InstToDuplicate.empty())
return {};
SmallVector<BasicBlock *, 4> ExitingBlocks;
L.getExitingBlocks(ExitingBlocks);
auto HasNoClobbersOnPath =
[&L, &AA, &AccessedLocs, &ExitingBlocks, &InstToDuplicate,
MSSAThreshold](BasicBlock *Succ, BasicBlock *Header,
SmallVector<MemoryAccess *, 4> AccessesToCheck)
-> Optional<IVConditionInfo> {
IVConditionInfo Info;
SmallVector<BasicBlock *, 4> WorkList;
WorkList.push_back(Succ);
WorkList.push_back(Header);
SmallPtrSet<BasicBlock *, 4> Seen;
Seen.insert(Header);
Info.PathIsNoop &=
all_of(*Header, [](Instruction &I) { return !I.mayHaveSideEffects(); });
while (!WorkList.empty()) {
BasicBlock *Current = WorkList.pop_back_val();
if (!L.contains(Current))
continue;
const auto &SeenIns = Seen.insert(Current);
if (!SeenIns.second)
continue;
Info.PathIsNoop &= all_of(
*Current, [](Instruction &I) { return !I.mayHaveSideEffects(); });
WorkList.append(succ_begin(Current), succ_end(Current));
}
if (Seen.size() < 2)
return {};
SmallPtrSet<MemoryAccess *, 4> SeenAccesses;
while (!AccessesToCheck.empty()) {
MemoryAccess *Current = AccessesToCheck.pop_back_val();
auto SeenI = SeenAccesses.insert(Current);
if (!SeenI.second || !Seen.contains(Current->getBlock()))
continue;
if (SeenAccesses.size() >= MSSAThreshold)
return {};
if (isa<MemoryUse>(Current))
continue;
if (auto *CurrentDef = dyn_cast<MemoryDef>(Current)) {
if (any_of(AccessedLocs, [&AA, CurrentDef](MemoryLocation &Loc) {
return isModSet(
AA.getModRefInfo(CurrentDef->getMemoryInst(), Loc));
}))
return {};
}
for (Use &U : Current->uses())
AccessesToCheck.push_back(cast<MemoryAccess>(U.getUser()));
}
Info.PathIsNoop &= isMustProgress(&L);
if (Info.PathIsNoop) {
for (auto *Exiting : ExitingBlocks) {
if (!Seen.contains(Exiting))
continue;
for (auto *Succ : successors(Exiting)) {
if (L.contains(Succ))
continue;
Info.PathIsNoop &= llvm::empty(Succ->phis()) &&
(!Info.ExitForPath || Info.ExitForPath == Succ);
if (!Info.PathIsNoop)
break;
assert((!Info.ExitForPath || Info.ExitForPath == Succ) &&
"cannot have multiple exit blocks");
Info.ExitForPath = Succ;
}
}
}
if (!Info.ExitForPath)
Info.PathIsNoop = false;
Info.InstToDuplicate = InstToDuplicate;
return Info;
};
if (TI->getSuccessor(0) == TI->getSuccessor(1))
return {};
if (auto Info = HasNoClobbersOnPath(TI->getSuccessor(0), L.getHeader(),
AccessesToCheck)) {
Info->KnownValue = ConstantInt::getTrue(TI->getContext());
return Info;
}
if (auto Info = HasNoClobbersOnPath(TI->getSuccessor(1), L.getHeader(),
AccessesToCheck)) {
Info->KnownValue = ConstantInt::getFalse(TI->getContext());
return Info;
}
return {};
}