X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;f=lib%2FAnalysis%2FLoopAccessAnalysis.cpp;h=e9815eb379f376a5ee27d055a8d5015980333ee1;hb=c182ce09e9cc3e384f5c5c322fb8b48c8dfb6b17;hp=8d64553d1deaee5c365355e8aa0a3e2671f784bb;hpb=7b507eb2a5f37d03aec153244f443de17ea0e9b9;p=oota-llvm.git diff --git a/lib/Analysis/LoopAccessAnalysis.cpp b/lib/Analysis/LoopAccessAnalysis.cpp index 8d64553d1de..e9815eb379f 100644 --- a/lib/Analysis/LoopAccessAnalysis.cpp +++ b/lib/Analysis/LoopAccessAnalysis.cpp @@ -23,7 +23,7 @@ #include "llvm/Transforms/Utils/VectorUtils.h" using namespace llvm; -#define DEBUG_TYPE "loop-vectorize" +#define DEBUG_TYPE "loop-accesses" static cl::opt VectorizationFactor("force-vector-width", cl::Hidden, @@ -50,13 +50,14 @@ bool VectorizerParams::isInterleaveForced() { return ::VectorizationInterleave.getNumOccurrences() > 0; } -void VectorizationReport::emitAnalysis(VectorizationReport &Message, - const Function *TheFunction, - const Loop *TheLoop) { +void LoopAccessReport::emitAnalysis(const LoopAccessReport &Message, + const Function *TheFunction, + const Loop *TheLoop, + const char *PassName) { DebugLoc DL = TheLoop->getStartLoc(); - if (Instruction *I = Message.getInstr()) + if (const Instruction *I = Message.getInstr()) DL = I->getDebugLoc(); - emitOptimizationRemarkAnalysis(TheFunction->getContext(), DEBUG_TYPE, + emitOptimizationRemarkAnalysis(TheFunction->getContext(), PassName, *TheFunction, DL, Message.str()); } @@ -89,7 +90,7 @@ const SCEV *llvm::replaceSymbolicStrideSCEV(ScalarEvolution *SE, const SCEV *ByOne = SCEVParameterRewriter::rewrite(OrigSCEV, *SE, RewriteMap, true); - DEBUG(dbgs() << "LV: Replacing SCEV: " << *OrigSCEV << " by: " << *ByOne + DEBUG(dbgs() << "LAA: Replacing SCEV: " << *OrigSCEV << " by: " << *ByOne << "\n"); return ByOne; } @@ -134,6 +135,23 @@ bool LoopAccessInfo::RuntimePointerCheck::needsChecking(unsigned I, return true; } +void LoopAccessInfo::RuntimePointerCheck::print(raw_ostream &OS, + unsigned Depth) const { + unsigned NumPointers = Pointers.size(); + if (NumPointers == 0) + return; + + OS.indent(Depth) << "Run-time memory checks:\n"; + unsigned N = 0; + for (unsigned I = 0; I < NumPointers; ++I) + for (unsigned J = I + 1; J < NumPointers; ++J) + if (needsChecking(I, J)) { + OS.indent(Depth) << N++ << ":\n"; + OS.indent(Depth + 2) << *Pointers[I] << "\n"; + OS.indent(Depth + 2) << *Pointers[J] << "\n"; + } +} + namespace { /// \brief Analyses memory accesses in a loop. /// @@ -289,7 +307,7 @@ bool AccessAnalysis::canCheckPtrAtRT( RtCheck.insert(SE, TheLoop, Ptr, IsWrite, DepId, ASId, StridesMap); - DEBUG(dbgs() << "LV: Found a runtime check ptr:" << *Ptr << '\n'); + DEBUG(dbgs() << "LAA: Found a runtime check ptr:" << *Ptr << '\n'); } else { CanDoRT = false; } @@ -326,7 +344,7 @@ bool AccessAnalysis::canCheckPtrAtRT( unsigned ASi = PtrI->getType()->getPointerAddressSpace(); unsigned ASj = PtrJ->getType()->getPointerAddressSpace(); if (ASi != ASj) { - DEBUG(dbgs() << "LV: Runtime check would require comparison between" + DEBUG(dbgs() << "LAA: Runtime check would require comparison between" " different address spaces\n"); return false; } @@ -341,9 +359,9 @@ void AccessAnalysis::processMemAccesses() { // process read-only pointers. This allows us to skip dependence tests for // read-only pointers. - DEBUG(dbgs() << "LV: Processing memory accesses...\n"); + DEBUG(dbgs() << "LAA: Processing memory accesses...\n"); DEBUG(dbgs() << " AST: "; AST.dump()); - DEBUG(dbgs() << "LV: Accesses:\n"); + DEBUG(dbgs() << "LAA: Accesses:\n"); DEBUG({ for (auto A : Accesses) dbgs() << "\t" << *A.getPointer() << " (" << @@ -574,8 +592,8 @@ static int isStridedPtr(ScalarEvolution *SE, const DataLayout *DL, Value *Ptr, // Make sure that the pointer does not point to aggregate types. const PointerType *PtrTy = cast(Ty); if (PtrTy->getElementType()->isAggregateType()) { - DEBUG(dbgs() << "LV: Bad stride - Not a pointer to a scalar type" << *Ptr << - "\n"); + DEBUG(dbgs() << "LAA: Bad stride - Not a pointer to a scalar type" + << *Ptr << "\n"); return 0; } @@ -583,14 +601,14 @@ static int isStridedPtr(ScalarEvolution *SE, const DataLayout *DL, Value *Ptr, const SCEVAddRecExpr *AR = dyn_cast(PtrScev); if (!AR) { - DEBUG(dbgs() << "LV: Bad stride - Not an AddRecExpr pointer " + DEBUG(dbgs() << "LAA: Bad stride - Not an AddRecExpr pointer " << *Ptr << " SCEV: " << *PtrScev << "\n"); return 0; } // The accesss function must stride over the innermost loop. if (Lp != AR->getLoop()) { - DEBUG(dbgs() << "LV: Bad stride - Not striding over innermost loop " << + DEBUG(dbgs() << "LAA: Bad stride - Not striding over innermost loop " << *Ptr << " SCEV: " << *PtrScev << "\n"); } @@ -605,7 +623,7 @@ static int isStridedPtr(ScalarEvolution *SE, const DataLayout *DL, Value *Ptr, bool IsNoWrapAddRec = AR->getNoWrapFlags(SCEV::NoWrapMask); bool IsInAddressSpaceZero = PtrTy->getAddressSpace() == 0; if (!IsNoWrapAddRec && !IsInBoundsGEP && !IsInAddressSpaceZero) { - DEBUG(dbgs() << "LV: Bad stride - Pointer may wrap in the address space " + DEBUG(dbgs() << "LAA: Bad stride - Pointer may wrap in the address space " << *Ptr << " SCEV: " << *PtrScev << "\n"); return 0; } @@ -616,7 +634,7 @@ static int isStridedPtr(ScalarEvolution *SE, const DataLayout *DL, Value *Ptr, // Calculate the pointer stride and check if it is consecutive. const SCEVConstant *C = dyn_cast(Step); if (!C) { - DEBUG(dbgs() << "LV: Bad stride - Not a constant strided " << *Ptr << + DEBUG(dbgs() << "LAA: Bad stride - Not a constant strided " << *Ptr << " SCEV: " << *PtrScev << "\n"); return 0; } @@ -673,7 +691,7 @@ bool MemoryDepChecker::couldPreventStoreLoadForward(unsigned Distance, } if (MaxVFWithoutSLForwardIssues< 2*TypeByteSize) { - DEBUG(dbgs() << "LV: Distance " << Distance << + DEBUG(dbgs() << "LAA: Distance " << Distance << " that could cause a store-load forwarding conflict\n"); return true; } @@ -727,9 +745,9 @@ bool MemoryDepChecker::isDependent(const MemAccessInfo &A, unsigned AIdx, const SCEV *Dist = SE->getMinusSCEV(Sink, Src); - DEBUG(dbgs() << "LV: Src Scev: " << *Src << "Sink Scev: " << *Sink + DEBUG(dbgs() << "LAA: Src Scev: " << *Src << "Sink Scev: " << *Sink << "(Induction step: " << StrideAPtr << ")\n"); - DEBUG(dbgs() << "LV: Distance for " << *InstMap[AIdx] << " to " + DEBUG(dbgs() << "LAA: Distance for " << *InstMap[AIdx] << " to " << *InstMap[BIdx] << ": " << *Dist << "\n"); // Need consecutive accesses. We don't want to vectorize @@ -742,7 +760,7 @@ bool MemoryDepChecker::isDependent(const MemAccessInfo &A, unsigned AIdx, const SCEVConstant *C = dyn_cast(Dist); if (!C) { - DEBUG(dbgs() << "LV: Dependence because of non-constant distance\n"); + DEBUG(dbgs() << "LAA: Dependence because of non-constant distance\n"); ShouldRetryWithRuntimeCheck = true; return true; } @@ -760,7 +778,7 @@ bool MemoryDepChecker::isDependent(const MemAccessInfo &A, unsigned AIdx, ATy != BTy)) return true; - DEBUG(dbgs() << "LV: Dependence is negative: NoDep\n"); + DEBUG(dbgs() << "LAA: Dependence is negative: NoDep\n"); return false; } @@ -769,7 +787,7 @@ bool MemoryDepChecker::isDependent(const MemAccessInfo &A, unsigned AIdx, if (Val == 0) { if (ATy == BTy) return false; - DEBUG(dbgs() << "LV: Zero dependence difference but different types\n"); + DEBUG(dbgs() << "LAA: Zero dependence difference but different types\n"); return true; } @@ -778,7 +796,7 @@ bool MemoryDepChecker::isDependent(const MemAccessInfo &A, unsigned AIdx, // Positive distance bigger than max vectorization factor. if (ATy != BTy) { DEBUG(dbgs() << - "LV: ReadWrite-Write positive dependency with different types\n"); + "LAA: ReadWrite-Write positive dependency with different types\n"); return false; } @@ -796,7 +814,7 @@ bool MemoryDepChecker::isDependent(const MemAccessInfo &A, unsigned AIdx, if (Distance < 2*TypeByteSize || 2*TypeByteSize > MaxSafeDepDistBytes || Distance < TypeByteSize * ForcedUnroll * ForcedFactor) { - DEBUG(dbgs() << "LV: Failure because of Positive distance " + DEBUG(dbgs() << "LAA: Failure because of Positive distance " << Val.getSExtValue() << '\n'); return true; } @@ -809,7 +827,7 @@ bool MemoryDepChecker::isDependent(const MemAccessInfo &A, unsigned AIdx, couldPreventStoreLoadForward(Distance, TypeByteSize)) return true; - DEBUG(dbgs() << "LV: Positive distance " << Val.getSExtValue() << + DEBUG(dbgs() << "LAA: Positive distance " << Val.getSExtValue() << " with max VF = " << MaxSafeDepDistBytes / TypeByteSize << '\n'); return false; @@ -854,7 +872,56 @@ bool MemoryDepChecker::areDepsSafe(AccessAnalysis::DepCandidates &AccessSets, return true; } -bool LoopAccessInfo::canVectorizeMemory(ValueToValueMap &Strides) { +bool LoopAccessInfo::canAnalyzeLoop() { + // We can only analyze innermost loops. + if (!TheLoop->empty()) { + emitAnalysis(LoopAccessReport() << "loop is not the innermost loop"); + return false; + } + + // We must have a single backedge. + if (TheLoop->getNumBackEdges() != 1) { + emitAnalysis( + LoopAccessReport() << + "loop control flow is not understood by analyzer"); + return false; + } + + // We must have a single exiting block. + if (!TheLoop->getExitingBlock()) { + emitAnalysis( + LoopAccessReport() << + "loop control flow is not understood by analyzer"); + return false; + } + + // We only handle bottom-tested loops, i.e. loop in which the condition is + // checked at the end of each iteration. With that we can assume that all + // instructions in the loop are executed the same number of times. + if (TheLoop->getExitingBlock() != TheLoop->getLoopLatch()) { + emitAnalysis( + LoopAccessReport() << + "loop control flow is not understood by analyzer"); + return false; + } + + // We need to have a loop header. + DEBUG(dbgs() << "LAA: Found a loop: " << + TheLoop->getHeader()->getName() << '\n'); + + // ScalarEvolution needs to be able to find the exit count. + const SCEV *ExitCount = SE->getBackedgeTakenCount(TheLoop); + if (ExitCount == SE->getCouldNotCompute()) { + emitAnalysis(LoopAccessReport() << + "could not determine number of loop iterations"); + DEBUG(dbgs() << "LAA: SCEV could not compute the loop exit count.\n"); + return false; + } + + return true; +} + +void LoopAccessInfo::analyzeLoop(ValueToValueMap &Strides) { typedef SmallVector ValueVector; typedef SmallPtrSet ValueSet; @@ -894,10 +961,11 @@ bool LoopAccessInfo::canVectorizeMemory(ValueToValueMap &Strides) { LoadInst *Ld = dyn_cast(it); if (!Ld || (!Ld->isSimple() && !IsAnnotatedParallel)) { - emitAnalysis(VectorizationReport(Ld) + emitAnalysis(LoopAccessReport(Ld) << "read with atomic ordering or volatile read"); - DEBUG(dbgs() << "LV: Found a non-simple load.\n"); - return false; + DEBUG(dbgs() << "LAA: Found a non-simple load.\n"); + CanVecMem = false; + return; } NumLoads++; Loads.push_back(Ld); @@ -909,15 +977,17 @@ bool LoopAccessInfo::canVectorizeMemory(ValueToValueMap &Strides) { if (it->mayWriteToMemory()) { StoreInst *St = dyn_cast(it); if (!St) { - emitAnalysis(VectorizationReport(it) << + emitAnalysis(LoopAccessReport(it) << "instruction cannot be vectorized"); - return false; + CanVecMem = false; + return; } if (!St->isSimple() && !IsAnnotatedParallel) { - emitAnalysis(VectorizationReport(St) + emitAnalysis(LoopAccessReport(St) << "write with atomic ordering or volatile write"); - DEBUG(dbgs() << "LV: Found a non-simple store.\n"); - return false; + DEBUG(dbgs() << "LAA: Found a non-simple store.\n"); + CanVecMem = false; + return; } NumStores++; Stores.push_back(St); @@ -932,8 +1002,9 @@ bool LoopAccessInfo::canVectorizeMemory(ValueToValueMap &Strides) { // Check if we see any stores. If there are no stores, then we don't // care if the pointers are *restrict*. if (!Stores.size()) { - DEBUG(dbgs() << "LV: Found a read-only loop!\n"); - return true; + DEBUG(dbgs() << "LAA: Found a read-only loop!\n"); + CanVecMem = true; + return; } AccessAnalysis::DepCandidates DependentAccesses; @@ -953,10 +1024,11 @@ bool LoopAccessInfo::canVectorizeMemory(ValueToValueMap &Strides) { if (isUniform(Ptr)) { emitAnalysis( - VectorizationReport(ST) + LoopAccessReport(ST) << "write to a loop invariant address could not be vectorized"); - DEBUG(dbgs() << "LV: We don't allow storing to uniform addresses\n"); - return false; + DEBUG(dbgs() << "LAA: We don't allow storing to uniform addresses\n"); + CanVecMem = false; + return; } // If we did *not* see this pointer before, insert it to the read-write @@ -977,9 +1049,10 @@ bool LoopAccessInfo::canVectorizeMemory(ValueToValueMap &Strides) { if (IsAnnotatedParallel) { DEBUG(dbgs() - << "LV: A loop annotated parallel, ignore memory dependency " + << "LAA: A loop annotated parallel, ignore memory dependency " << "checks.\n"); - return true; + CanVecMem = true; + return; } for (I = Loads.begin(), IE = Loads.end(); I != IE; ++I) { @@ -1013,8 +1086,9 @@ bool LoopAccessInfo::canVectorizeMemory(ValueToValueMap &Strides) { // If we write (or read-write) to a single destination and there are no // other reads in this loop then is it safe to vectorize. if (NumReadWrites == 1 && NumReads == 0) { - DEBUG(dbgs() << "LV: Found a write-only loop!\n"); - return true; + DEBUG(dbgs() << "LAA: Found a write-only loop!\n"); + CanVecMem = true; + return; } // Build dependence sets and check whether we need a runtime pointer bounds @@ -1030,7 +1104,7 @@ bool LoopAccessInfo::canVectorizeMemory(ValueToValueMap &Strides) { CanDoRT = Accesses.canCheckPtrAtRT(PtrRtCheck, NumComparisons, SE, TheLoop, Strides); - DEBUG(dbgs() << "LV: We need to do " << NumComparisons << + DEBUG(dbgs() << "LAA: We need to do " << NumComparisons << " pointer comparisons.\n"); // If we only have one set of dependences to check pointers among we don't @@ -1047,28 +1121,29 @@ bool LoopAccessInfo::canVectorizeMemory(ValueToValueMap &Strides) { } if (CanDoRT) { - DEBUG(dbgs() << "LV: We can perform a memory runtime check if needed.\n"); + DEBUG(dbgs() << "LAA: We can perform a memory runtime check if needed.\n"); } if (NeedRTCheck && !CanDoRT) { - emitAnalysis(VectorizationReport() << "cannot identify array bounds"); - DEBUG(dbgs() << "LV: We can't vectorize because we can't find " << + emitAnalysis(LoopAccessReport() << "cannot identify array bounds"); + DEBUG(dbgs() << "LAA: We can't vectorize because we can't find " << "the array bounds.\n"); PtrRtCheck.reset(); - return false; + CanVecMem = false; + return; } PtrRtCheck.Need = NeedRTCheck; - bool CanVecMem = true; + CanVecMem = true; if (Accesses.isDependencyCheckNeeded()) { - DEBUG(dbgs() << "LV: Checking memory dependencies\n"); + DEBUG(dbgs() << "LAA: Checking memory dependencies\n"); CanVecMem = DepChecker.areDepsSafe( DependentAccesses, Accesses.getDependenciesToCheck(), Strides); MaxSafeDepDistBytes = DepChecker.getMaxSafeDepDistBytes(); if (!CanVecMem && DepChecker.shouldRetryWithRuntimeCheck()) { - DEBUG(dbgs() << "LV: Retrying with memory checks\n"); + DEBUG(dbgs() << "LAA: Retrying with memory checks\n"); NeedRTCheck = true; // Clear the dependency checks. We assume they are not needed. @@ -1084,16 +1159,17 @@ bool LoopAccessInfo::canVectorizeMemory(ValueToValueMap &Strides) { if (!CanDoRT || NumComparisons > VectorizerParams::RuntimeMemoryCheckThreshold) { if (!CanDoRT && NumComparisons > 0) - emitAnalysis(VectorizationReport() + emitAnalysis(LoopAccessReport() << "cannot check memory dependencies at runtime"); else - emitAnalysis(VectorizationReport() + emitAnalysis(LoopAccessReport() << NumComparisons << " exceeds limit of " << VectorizerParams::RuntimeMemoryCheckThreshold << " dependent memory operations checked at runtime"); - DEBUG(dbgs() << "LV: Can't vectorize with memory checks\n"); + DEBUG(dbgs() << "LAA: Can't vectorize with memory checks\n"); PtrRtCheck.reset(); - return false; + CanVecMem = false; + return; } CanVecMem = true; @@ -1101,13 +1177,11 @@ bool LoopAccessInfo::canVectorizeMemory(ValueToValueMap &Strides) { } if (!CanVecMem) - emitAnalysis(VectorizationReport() << + emitAnalysis(LoopAccessReport() << "unsafe dependent memory operations in loop"); - DEBUG(dbgs() << "LV: We" << (NeedRTCheck ? "" : " don't") << + DEBUG(dbgs() << "LAA: We" << (NeedRTCheck ? "" : " don't") << " need a runtime memory check.\n"); - - return CanVecMem; } bool LoopAccessInfo::blockNeedsPredication(BasicBlock *BB, Loop *TheLoop, @@ -1119,7 +1193,7 @@ bool LoopAccessInfo::blockNeedsPredication(BasicBlock *BB, Loop *TheLoop, return !DT->dominates(BB, Latch); } -void LoopAccessInfo::emitAnalysis(VectorizationReport &Message) { +void LoopAccessInfo::emitAnalysis(LoopAccessReport &Message) { assert(!Report && "Multiple reports generated"); Report = Message; } @@ -1158,12 +1232,12 @@ LoopAccessInfo::addRuntimeCheck(Instruction *Loc) { const SCEV *Sc = SE->getSCEV(Ptr); if (SE->isLoopInvariant(Sc, TheLoop)) { - DEBUG(dbgs() << "LV: Adding RT check for a loop invariant ptr:" << + DEBUG(dbgs() << "LAA: Adding RT check for a loop invariant ptr:" << *Ptr <<"\n"); Starts.push_back(Ptr); Ends.push_back(Ptr); } else { - DEBUG(dbgs() << "LV: Adding RT check for range:" << *Ptr << '\n'); + DEBUG(dbgs() << "LAA: Adding RT check for range:" << *Ptr << '\n'); unsigned AS = Ptr->getType()->getPointerAddressSpace(); // Use this type for pointer arithmetic. @@ -1223,3 +1297,99 @@ LoopAccessInfo::addRuntimeCheck(Instruction *Loc) { FirstInst = getFirstInst(FirstInst, Check, Loc); return std::make_pair(FirstInst, Check); } + +LoopAccessInfo::LoopAccessInfo(Loop *L, ScalarEvolution *SE, + const DataLayout *DL, + const TargetLibraryInfo *TLI, AliasAnalysis *AA, + DominatorTree *DT, ValueToValueMap &Strides) + : TheLoop(L), SE(SE), DL(DL), TLI(TLI), AA(AA), DT(DT), NumLoads(0), + NumStores(0), MaxSafeDepDistBytes(-1U), CanVecMem(false) { + if (canAnalyzeLoop()) + analyzeLoop(Strides); +} + +void LoopAccessInfo::print(raw_ostream &OS, unsigned Depth) const { + if (CanVecMem) { + if (PtrRtCheck.empty()) + OS.indent(Depth) << "Memory dependences are safe\n"; + else + OS.indent(Depth) << "Memory dependences are safe with run-time checks\n"; + } + + if (Report) + OS.indent(Depth) << "Report: " << Report->str() << "\n"; + + // FIXME: Print unsafe dependences + + // List the pair of accesses need run-time checks to prove independence. + PtrRtCheck.print(OS, Depth); + OS << "\n"; +} + +LoopAccessInfo &LoopAccessAnalysis::getInfo(Loop *L, ValueToValueMap &Strides) { + auto &LAI = LoopAccessInfoMap[L]; + +#ifndef NDEBUG + assert((!LAI || LAI->NumSymbolicStrides == Strides.size()) && + "Symbolic strides changed for loop"); +#endif + + if (!LAI) { + LAI = llvm::make_unique(L, SE, DL, TLI, AA, DT, Strides); +#ifndef NDEBUG + LAI->NumSymbolicStrides = Strides.size(); +#endif + } + return *LAI.get(); +} + +void LoopAccessAnalysis::print(raw_ostream &OS, const Module *M) const { + LoopAccessAnalysis &LAA = *const_cast(this); + + LoopInfo *LI = &getAnalysis().getLoopInfo(); + ValueToValueMap NoSymbolicStrides; + + for (Loop *TopLevelLoop : *LI) + for (Loop *L : depth_first(TopLevelLoop)) { + OS.indent(2) << L->getHeader()->getName() << ":\n"; + auto &LAI = LAA.getInfo(L, NoSymbolicStrides); + LAI.print(OS, 4); + } +} + +bool LoopAccessAnalysis::runOnFunction(Function &F) { + SE = &getAnalysis(); + DL = F.getParent()->getDataLayout(); + auto *TLIP = getAnalysisIfAvailable(); + TLI = TLIP ? &TLIP->getTLI() : nullptr; + AA = &getAnalysis(); + DT = &getAnalysis().getDomTree(); + + return false; +} + +void LoopAccessAnalysis::getAnalysisUsage(AnalysisUsage &AU) const { + AU.addRequired(); + AU.addRequired(); + AU.addRequired(); + AU.addRequired(); + + AU.setPreservesAll(); +} + +char LoopAccessAnalysis::ID = 0; +static const char laa_name[] = "Loop Access Analysis"; +#define LAA_NAME "loop-accesses" + +INITIALIZE_PASS_BEGIN(LoopAccessAnalysis, LAA_NAME, laa_name, false, true) +INITIALIZE_AG_DEPENDENCY(AliasAnalysis) +INITIALIZE_PASS_DEPENDENCY(ScalarEvolution) +INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass) +INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass) +INITIALIZE_PASS_END(LoopAccessAnalysis, LAA_NAME, laa_name, false, true) + +namespace llvm { + Pass *createLAAPass() { + return new LoopAccessAnalysis(); + } +}