#include "llvm/Transforms/Scalar/LoopVersioningLICM.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/Analysis/AliasSetTracker.h"
#include "llvm/Analysis/GlobalsModRef.h"
#include "llvm/Analysis/LoopAccessAnalysis.h"
#include "llvm/Analysis/LoopInfo.h"
#include "llvm/Analysis/LoopPass.h"
#include "llvm/Analysis/OptimizationRemarkEmitter.h"
#include "llvm/Analysis/ScalarEvolution.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/Instruction.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/MDBuilder.h"
#include "llvm/IR/Metadata.h"
#include "llvm/IR/Value.h"
#include "llvm/InitializePasses.h"
#include "llvm/Pass.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Transforms/Scalar.h"
#include "llvm/Transforms/Utils.h"
#include "llvm/Transforms/Utils/LoopUtils.h"
#include "llvm/Transforms/Utils/LoopVersioning.h"
#include <cassert>
#include <memory>
using namespace llvm;
#define DEBUG_TYPE "loop-versioning-licm"
static const char *LICMVersioningMetaData = "llvm.loop.licm_versioning.disable";
static cl::opt<float>
LVInvarThreshold("licm-versioning-invariant-threshold",
cl::desc("LoopVersioningLICM's minimum allowed percentage"
"of possible invariant instructions per loop"),
cl::init(25), cl::Hidden);
static cl::opt<unsigned> LVLoopDepthThreshold(
"licm-versioning-max-depth-threshold",
cl::desc(
"LoopVersioningLICM's threshold for maximum allowed loop nest/depth"),
cl::init(2), cl::Hidden);
namespace {
struct LoopVersioningLICMLegacyPass : public LoopPass {
static char ID;
LoopVersioningLICMLegacyPass() : LoopPass(ID) {
initializeLoopVersioningLICMLegacyPassPass(
*PassRegistry::getPassRegistry());
}
bool runOnLoop(Loop *L, LPPassManager &LPM) override;
StringRef getPassName() const override { return "Loop Versioning for LICM"; }
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.setPreservesCFG();
AU.addRequired<AAResultsWrapperPass>();
AU.addRequired<DominatorTreeWrapperPass>();
AU.addRequiredID(LCSSAID);
AU.addRequired<LoopAccessLegacyAnalysis>();
AU.addRequired<LoopInfoWrapperPass>();
AU.addRequiredID(LoopSimplifyID);
AU.addRequired<ScalarEvolutionWrapperPass>();
AU.addPreserved<AAResultsWrapperPass>();
AU.addPreserved<GlobalsAAWrapperPass>();
AU.addRequired<OptimizationRemarkEmitterWrapperPass>();
}
};
struct LoopVersioningLICM {
LoopVersioningLICM(AliasAnalysis *AA, ScalarEvolution *SE,
OptimizationRemarkEmitter *ORE,
function_ref<const LoopAccessInfo &(Loop *)> GetLAI)
: AA(AA), SE(SE), GetLAI(GetLAI),
LoopDepthThreshold(LVLoopDepthThreshold),
InvariantThreshold(LVInvarThreshold), ORE(ORE) {}
bool runOnLoop(Loop *L, LoopInfo *LI, DominatorTree *DT);
void reset() {
AA = nullptr;
SE = nullptr;
CurLoop = nullptr;
LoadAndStoreCounter = 0;
InvariantCounter = 0;
IsReadOnlyLoop = true;
ORE = nullptr;
CurAST.reset();
}
class AutoResetter {
public:
AutoResetter(LoopVersioningLICM &LVLICM) : LVLICM(LVLICM) {}
~AutoResetter() { LVLICM.reset(); }
private:
LoopVersioningLICM &LVLICM;
};
private:
AliasAnalysis *AA = nullptr;
ScalarEvolution *SE = nullptr;
const LoopAccessInfo *LAI = nullptr;
function_ref<const LoopAccessInfo &(Loop *)> GetLAI;
Loop *CurLoop = nullptr;
std::unique_ptr<AliasSetTracker> CurAST;
unsigned LoopDepthThreshold;
float InvariantThreshold;
unsigned LoadAndStoreCounter = 0;
unsigned InvariantCounter = 0;
bool IsReadOnlyLoop = true;
OptimizationRemarkEmitter *ORE;
bool isLegalForVersioning();
bool legalLoopStructure();
bool legalLoopInstructions();
bool legalLoopMemoryAccesses();
bool isLoopAlreadyVisited();
void setNoAliasToLoop(Loop *VerLoop);
bool instructionSafeForVersioning(Instruction *I);
};
}
bool LoopVersioningLICM::legalLoopStructure() {
if (!CurLoop->isLoopSimplifyForm()) {
LLVM_DEBUG(dbgs() << " loop is not in loop-simplify form.\n");
return false;
}
if (!CurLoop->getSubLoops().empty()) {
LLVM_DEBUG(dbgs() << " loop is not innermost\n");
return false;
}
if (CurLoop->getNumBackEdges() != 1) {
LLVM_DEBUG(dbgs() << " loop has multiple backedges\n");
return false;
}
if (!CurLoop->getExitingBlock()) {
LLVM_DEBUG(dbgs() << " loop has multiple exiting block\n");
return false;
}
if (CurLoop->getExitingBlock() != CurLoop->getLoopLatch()) {
LLVM_DEBUG(dbgs() << " loop is not bottom tested\n");
return false;
}
if (CurLoop->isAnnotatedParallel()) {
LLVM_DEBUG(dbgs() << " Parallel loop is not worth versioning\n");
return false;
}
if (CurLoop->getLoopDepth() > LoopDepthThreshold) {
LLVM_DEBUG(dbgs() << " loop depth is more then threshold\n");
return false;
}
const SCEV *ExitCount = SE->getBackedgeTakenCount(CurLoop);
if (isa<SCEVCouldNotCompute>(ExitCount)) {
LLVM_DEBUG(dbgs() << " loop does not has trip count\n");
return false;
}
return true;
}
bool LoopVersioningLICM::legalLoopMemoryAccesses() {
bool HasMayAlias = false;
bool TypeSafety = false;
bool HasMod = false;
for (const auto &I : *CurAST) {
const AliasSet &AS = I;
if (AS.isForwardingAliasSet())
continue;
if (AS.isMustAlias())
return false;
Value *SomePtr = AS.begin()->getValue();
bool TypeCheck = true;
HasMayAlias |= AS.isMayAlias();
HasMod |= AS.isMod();
for (const auto &A : AS) {
Value *Ptr = A.getValue();
TypeCheck = (TypeCheck && (SomePtr->getType() == Ptr->getType()));
}
TypeSafety |= TypeCheck;
}
if (!TypeSafety) {
LLVM_DEBUG(dbgs() << " Alias tracker type safety failed!\n");
return false;
}
if (!HasMod) {
LLVM_DEBUG(dbgs() << " No memory modified in loop body\n");
return false;
}
if (!HasMayAlias) {
LLVM_DEBUG(dbgs() << " No ambiguity in memory access.\n");
return false;
}
return true;
}
bool LoopVersioningLICM::instructionSafeForVersioning(Instruction *I) {
assert(I != nullptr && "Null instruction found!");
if (auto *Call = dyn_cast<CallBase>(I)) {
if (Call->isConvergent() || Call->cannotDuplicate()) {
LLVM_DEBUG(dbgs() << " Convergent call site found.\n");
return false;
}
if (!AA->doesNotAccessMemory(Call)) {
LLVM_DEBUG(dbgs() << " Unsafe call site found.\n");
return false;
}
}
if (I->mayThrow()) {
LLVM_DEBUG(dbgs() << " May throw instruction found in loop body\n");
return false;
}
if (I->mayReadFromMemory()) {
LoadInst *Ld = dyn_cast<LoadInst>(I);
if (!Ld || !Ld->isSimple()) {
LLVM_DEBUG(dbgs() << " Found a non-simple load.\n");
return false;
}
LoadAndStoreCounter++;
Value *Ptr = Ld->getPointerOperand();
if (SE->isLoopInvariant(SE->getSCEV(Ptr), CurLoop))
InvariantCounter++;
}
else if (I->mayWriteToMemory()) {
StoreInst *St = dyn_cast<StoreInst>(I);
if (!St || !St->isSimple()) {
LLVM_DEBUG(dbgs() << " Found a non-simple store.\n");
return false;
}
LoadAndStoreCounter++;
Value *Ptr = St->getPointerOperand();
if (SE->isLoopInvariant(SE->getSCEV(Ptr), CurLoop))
InvariantCounter++;
IsReadOnlyLoop = false;
}
return true;
}
bool LoopVersioningLICM::legalLoopInstructions() {
LoadAndStoreCounter = 0;
InvariantCounter = 0;
IsReadOnlyLoop = true;
using namespace ore;
for (auto *Block : CurLoop->getBlocks())
for (auto &Inst : *Block) {
if (!instructionSafeForVersioning(&Inst)) {
ORE->emit([&]() {
return OptimizationRemarkMissed(DEBUG_TYPE, "IllegalLoopInst", &Inst)
<< " Unsafe Loop Instruction";
});
return false;
}
}
LAI = &GetLAI(CurLoop);
if (LAI->getRuntimePointerChecking()->getChecks().empty()) {
LLVM_DEBUG(dbgs() << " LAA: Runtime check not found !!\n");
return false;
}
if (LAI->getNumRuntimePointerChecks() >
VectorizerParams::RuntimeMemoryCheckThreshold) {
LLVM_DEBUG(
dbgs() << " LAA: Runtime checks are more than threshold !!\n");
ORE->emit([&]() {
return OptimizationRemarkMissed(DEBUG_TYPE, "RuntimeCheck",
CurLoop->getStartLoc(),
CurLoop->getHeader())
<< "Number of runtime checks "
<< NV("RuntimeChecks", LAI->getNumRuntimePointerChecks())
<< " exceeds threshold "
<< NV("Threshold", VectorizerParams::RuntimeMemoryCheckThreshold);
});
return false;
}
if (!InvariantCounter) {
LLVM_DEBUG(dbgs() << " Invariant not found !!\n");
return false;
}
if (IsReadOnlyLoop) {
LLVM_DEBUG(dbgs() << " Found a read-only loop!\n");
return false;
}
if (InvariantCounter * 100 < InvariantThreshold * LoadAndStoreCounter) {
LLVM_DEBUG(
dbgs()
<< " Invariant load & store are less then defined threshold\n");
LLVM_DEBUG(dbgs() << " Invariant loads & stores: "
<< ((InvariantCounter * 100) / LoadAndStoreCounter)
<< "%\n");
LLVM_DEBUG(dbgs() << " Invariant loads & store threshold: "
<< InvariantThreshold << "%\n");
ORE->emit([&]() {
return OptimizationRemarkMissed(DEBUG_TYPE, "InvariantThreshold",
CurLoop->getStartLoc(),
CurLoop->getHeader())
<< "Invariant load & store "
<< NV("LoadAndStoreCounter",
((InvariantCounter * 100) / LoadAndStoreCounter))
<< " are less then defined threshold "
<< NV("Threshold", InvariantThreshold);
});
return false;
}
return true;
}
bool LoopVersioningLICM::isLoopAlreadyVisited() {
if (findStringMetadataForLoop(CurLoop, LICMVersioningMetaData)) {
return true;
}
return false;
}
bool LoopVersioningLICM::isLegalForVersioning() {
using namespace ore;
LLVM_DEBUG(dbgs() << "Loop: " << *CurLoop);
if (isLoopAlreadyVisited()) {
LLVM_DEBUG(
dbgs() << " Revisiting loop in LoopVersioningLICM not allowed.\n\n");
return false;
}
if (!legalLoopStructure()) {
LLVM_DEBUG(
dbgs() << " Loop structure not suitable for LoopVersioningLICM\n\n");
ORE->emit([&]() {
return OptimizationRemarkMissed(DEBUG_TYPE, "IllegalLoopStruct",
CurLoop->getStartLoc(),
CurLoop->getHeader())
<< " Unsafe Loop structure";
});
return false;
}
if (!legalLoopInstructions()) {
LLVM_DEBUG(
dbgs()
<< " Loop instructions not suitable for LoopVersioningLICM\n\n");
return false;
}
if (!legalLoopMemoryAccesses()) {
LLVM_DEBUG(
dbgs()
<< " Loop memory access not suitable for LoopVersioningLICM\n\n");
ORE->emit([&]() {
return OptimizationRemarkMissed(DEBUG_TYPE, "IllegalLoopMemoryAccess",
CurLoop->getStartLoc(),
CurLoop->getHeader())
<< " Unsafe Loop memory access";
});
return false;
}
LLVM_DEBUG(dbgs() << " Loop Versioning found to be beneficial\n\n");
ORE->emit([&]() {
return OptimizationRemark(DEBUG_TYPE, "IsLegalForVersioning",
CurLoop->getStartLoc(), CurLoop->getHeader())
<< " Versioned loop for LICM."
<< " Number of runtime checks we had to insert "
<< NV("RuntimeChecks", LAI->getNumRuntimePointerChecks());
});
return true;
}
void LoopVersioningLICM::setNoAliasToLoop(Loop *VerLoop) {
Instruction *I = VerLoop->getLoopLatch()->getTerminator();
MDBuilder MDB(I->getContext());
MDNode *NewDomain = MDB.createAnonymousAliasScopeDomain("LVDomain");
StringRef Name = "LVAliasScope";
MDNode *NewScope = MDB.createAnonymousAliasScope(NewDomain, Name);
SmallVector<Metadata *, 4> Scopes{NewScope}, NoAliases{NewScope};
for (auto *Block : CurLoop->getBlocks()) {
for (auto &Inst : *Block) {
if (!Inst.mayReadFromMemory() && !Inst.mayWriteToMemory())
continue;
Inst.setMetadata(
LLVMContext::MD_noalias,
MDNode::concatenate(Inst.getMetadata(LLVMContext::MD_noalias),
MDNode::get(Inst.getContext(), NoAliases)));
Inst.setMetadata(
LLVMContext::MD_alias_scope,
MDNode::concatenate(Inst.getMetadata(LLVMContext::MD_alias_scope),
MDNode::get(Inst.getContext(), Scopes)));
}
}
}
bool LoopVersioningLICMLegacyPass::runOnLoop(Loop *L, LPPassManager &LPM) {
if (skipLoop(L))
return false;
AliasAnalysis *AA = &getAnalysis<AAResultsWrapperPass>().getAAResults();
ScalarEvolution *SE = &getAnalysis<ScalarEvolutionWrapperPass>().getSE();
OptimizationRemarkEmitter *ORE =
&getAnalysis<OptimizationRemarkEmitterWrapperPass>().getORE();
LoopInfo *LI = &getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
DominatorTree *DT = &getAnalysis<DominatorTreeWrapperPass>().getDomTree();
auto GetLAI = [&](Loop *L) -> const LoopAccessInfo & {
return getAnalysis<LoopAccessLegacyAnalysis>().getInfo(L);
};
return LoopVersioningLICM(AA, SE, ORE, GetLAI).runOnLoop(L, LI, DT);
}
bool LoopVersioningLICM::runOnLoop(Loop *L, LoopInfo *LI, DominatorTree *DT) {
AutoResetter Resetter(*this);
if (hasLICMVersioningTransformation(L) & TM_Disable)
return false;
CurLoop = L;
CurAST.reset(new AliasSetTracker(*AA));
for (auto *Block : L->getBlocks()) {
if (LI->getLoopFor(Block) == L) CurAST->add(*Block); }
bool Changed = false;
if (isLegalForVersioning()) {
LoopVersioning LVer(*LAI, LAI->getRuntimePointerChecking()->getChecks(),
CurLoop, LI, DT, SE);
LVer.versionLoop();
addStringMetadataToLoop(LVer.getNonVersionedLoop(), LICMVersioningMetaData);
addStringMetadataToLoop(LVer.getVersionedLoop(), LICMVersioningMetaData);
addStringMetadataToLoop(LVer.getVersionedLoop(),
"llvm.mem.parallel_loop_access");
setNoAliasToLoop(LVer.getVersionedLoop());
Changed = true;
}
return Changed;
}
char LoopVersioningLICMLegacyPass::ID = 0;
INITIALIZE_PASS_BEGIN(LoopVersioningLICMLegacyPass, "loop-versioning-licm",
"Loop Versioning For LICM", false, false)
INITIALIZE_PASS_DEPENDENCY(AAResultsWrapperPass)
INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass)
INITIALIZE_PASS_DEPENDENCY(GlobalsAAWrapperPass)
INITIALIZE_PASS_DEPENDENCY(LCSSAWrapperPass)
INITIALIZE_PASS_DEPENDENCY(LoopAccessLegacyAnalysis)
INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass)
INITIALIZE_PASS_DEPENDENCY(LoopSimplify)
INITIALIZE_PASS_DEPENDENCY(ScalarEvolutionWrapperPass)
INITIALIZE_PASS_DEPENDENCY(OptimizationRemarkEmitterWrapperPass)
INITIALIZE_PASS_END(LoopVersioningLICMLegacyPass, "loop-versioning-licm",
"Loop Versioning For LICM", false, false)
Pass *llvm::createLoopVersioningLICMPass() {
return new LoopVersioningLICMLegacyPass();
}
namespace llvm {
PreservedAnalyses LoopVersioningLICMPass::run(Loop &L, LoopAnalysisManager &AM,
LoopStandardAnalysisResults &LAR,
LPMUpdater &U) {
AliasAnalysis *AA = &LAR.AA;
ScalarEvolution *SE = &LAR.SE;
DominatorTree *DT = &LAR.DT;
LoopInfo *LI = &LAR.LI;
const Function *F = L.getHeader()->getParent();
OptimizationRemarkEmitter ORE(F);
auto GetLAI = [&](Loop *L) -> const LoopAccessInfo & {
return AM.getResult<LoopAccessAnalysis>(*L, LAR);
};
if (!LoopVersioningLICM(AA, SE, &ORE, GetLAI).runOnLoop(&L, LI, DT))
return PreservedAnalyses::all();
return getLoopPassPreservedAnalyses();
}
}