Index: llvm/include/llvm/Transforms/Utils/NoAliasUtils.h =================================================================== --- /dev/null +++ llvm/include/llvm/Transforms/Utils/NoAliasUtils.h @@ -0,0 +1,25 @@ +//===- llvm/Transforms/Utils/NoAliasUtils.h - NoAlias utilities -*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines utilities for noalias metadata and intrinsics. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TRANSFORMS_UTILS_NOALIASUTILS_H +#define LLVM_TRANSFORMS_UTILS_NOALIASUTILS_H + +namespace llvm { +class Function; + +/// Connect llvm.noalias.decl to noalias/provenance.noalias intrinsics that are +/// associated with the unknown function scope and based on the same alloca. +/// At the same time, propagate the p.addr, p.objId and p.scope. +bool propagateAndConnectNoAliasDecl(Function *F); +} // end namespace llvm + +#endif // LLVM_TRANSFORMS_UTILS_NOALIASUTILS_H Index: llvm/lib/Analysis/ScopedNoAliasAA.cpp =================================================================== --- llvm/lib/Analysis/ScopedNoAliasAA.cpp +++ llvm/lib/Analysis/ScopedNoAliasAA.cpp @@ -100,11 +100,11 @@ LLVM_DEBUG(llvm::dbgs() << "ScopedNoAliasAAResult::alias\n"); if (noAliasByIntrinsic(ANoAlias, selectMemoryProvenance(LocA), BNoAlias, selectMemoryProvenance(LocB), nullptr, nullptr, AAQI)) - return NoAlias; + return AliasResult::NoAlias; if (noAliasByIntrinsic(BNoAlias, selectMemoryProvenance(LocB), ANoAlias, selectMemoryProvenance(LocA), nullptr, nullptr, AAQI)) - return NoAlias; + return AliasResult::NoAlias; // If they may alias, chain to the next AliasAnalysis. return AAResultBase::alias(LocA, LocB, AAQI); Index: llvm/lib/Transforms/Utils/CMakeLists.txt =================================================================== --- llvm/lib/Transforms/Utils/CMakeLists.txt +++ llvm/lib/Transforms/Utils/CMakeLists.txt @@ -52,6 +52,7 @@ MetaRenamer.cpp ModuleUtils.cpp NameAnonGlobals.cpp + NoAliasUtils.cpp PredicateInfo.cpp PromoteMemoryToRegister.cpp RelLookupTableConverter.cpp Index: llvm/lib/Transforms/Utils/CloneFunction.cpp =================================================================== --- llvm/lib/Transforms/Utils/CloneFunction.cpp +++ llvm/lib/Transforms/Utils/CloneFunction.cpp @@ -39,6 +39,21 @@ #define DEBUG_TYPE "clone-function" +static void PropagateNoAliasProvenanceInfo(Instruction *To, + const Instruction *From) { + // The ptr_provenance is not automatically copied over in a 'clone()' + // Let's do it here. + if (auto *LI = dyn_cast(From)) { + if (LI->hasNoaliasProvenanceOperand()) + cast(To)->setNoaliasProvenanceOperand( + LI->getNoaliasProvenanceOperand()); + } else if (auto SI = dyn_cast(From)) { + if (SI->hasNoaliasProvenanceOperand()) + cast(To)->setNoaliasProvenanceOperand( + SI->getNoaliasProvenanceOperand()); + } +} + /// See comments in Cloning.h. BasicBlock *llvm::CloneBasicBlock(const BasicBlock *BB, ValueToValueMapTy &VMap, const Twine &NameSuffix, Function *F, @@ -58,6 +73,8 @@ DIFinder->processInstruction(*TheModule, I); Instruction *NewInst = I.clone(); + PropagateNoAliasProvenanceInfo(NewInst, &I); + if (I.hasName()) NewInst->setName(I.getName() + NameSuffix); NewBB->getInstList().push_back(NewInst); @@ -381,6 +398,7 @@ ++II) { Instruction *NewInst = II->clone(); + PropagateNoAliasProvenanceInfo(NewInst, &*II); // Eagerly remap operands to the newly cloned instruction, except for PHI // nodes for which we defer processing until we update the CFG. @@ -910,6 +928,7 @@ // terminator gets replaced and StopAt == BB's terminator. for (; StopAt != &*BI && BB->getTerminator() != &*BI; ++BI) { Instruction *New = BI->clone(); + PropagateNoAliasProvenanceInfo(New, &*BI); New->setName(BI->getName()); New->insertBefore(NewTerm); ValueMapping[&*BI] = New; Index: llvm/lib/Transforms/Utils/InlineFunction.cpp =================================================================== --- llvm/lib/Transforms/Utils/InlineFunction.cpp +++ llvm/lib/Transforms/Utils/InlineFunction.cpp @@ -46,6 +46,7 @@ #include "llvm/IR/Function.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/InlineAsm.h" +#include "llvm/IR/InstIterator.h" #include "llvm/IR/InstrTypes.h" #include "llvm/IR/Instruction.h" #include "llvm/IR/Instructions.h" @@ -64,6 +65,7 @@ #include "llvm/Transforms/Utils/AssumeBundleBuilder.h" #include "llvm/Transforms/Utils/Cloning.h" #include "llvm/Transforms/Utils/Local.h" +#include "llvm/Transforms/Utils/NoAliasUtils.h" #include "llvm/Transforms/Utils/ValueMapper.h" #include #include @@ -82,11 +84,15 @@ cl::Hidden, cl::desc("Convert noalias attributes to metadata during inlining.")); -static cl::opt - UseNoAliasIntrinsic("use-noalias-intrinsic-during-inlining", cl::Hidden, - cl::ZeroOrMore, cl::init(true), - cl::desc("Use the llvm.experimental.noalias.scope.decl " - "intrinsic during inlining.")); +enum NoAliasIntrinsicKind { NAIK_none, NAIK_scopes, NAIK_full }; +static cl::opt UseNoAliasIntrinsic( + "use-noalias-intrinsic-during-inlining", cl::Hidden, cl::ZeroOrMore, + cl::init(NAIK_scopes), cl::desc("Use noalias intrinsics during inlining."), + cl::values(clEnumValN(NAIK_none, "none", "no intrinsics"), + clEnumValN(NAIK_scopes, "scopes", + "use llvm.experimental.noalias.scope.decl"), + clEnumValN(NAIK_full, "full", + "use llvm.noalias.decl (full restrict)"))); // Disabled by default, because the added alignment assumptions may increase // compile-time and block optimizations. This option is not suitable for use @@ -790,44 +796,72 @@ /// When inlining a call site that has !llvm.mem.parallel_loop_access, /// !llvm.access.group, !alias.scope or !noalias metadata, that metadata should /// be propagated to all memory-accessing cloned instructions. -static void PropagateCallSiteMetadata(CallBase &CB, Function::iterator FStart, - Function::iterator FEnd) { +static void +PropagateCallSiteMetadata(CallBase &CB, MDNode *NewNoAliasScopeList, + const SmallVectorImpl &NewNoAliasInst, + Function::iterator FStart, Function::iterator FEnd) { MDNode *MemParallelLoopAccess = CB.getMetadata(LLVMContext::MD_mem_parallel_loop_access); MDNode *AccessGroup = CB.getMetadata(LLVMContext::MD_access_group); MDNode *AliasScope = CB.getMetadata(LLVMContext::MD_alias_scope); - MDNode *NoAlias = CB.getMetadata(LLVMContext::MD_noalias); + MDNode *NoAlias = MDNode::concatenate(CB.getMetadata(LLVMContext::MD_noalias), + NewNoAliasScopeList); if (!MemParallelLoopAccess && !AccessGroup && !AliasScope && !NoAlias) return; - for (BasicBlock &BB : make_range(FStart, FEnd)) { - for (Instruction &I : BB) { - // This metadata is only relevant for instructions that access memory. - if (!I.mayReadOrWriteMemory()) - continue; - - if (MemParallelLoopAccess) { - // TODO: This probably should not overwrite MemParalleLoopAccess. - MemParallelLoopAccess = MDNode::concatenate( - I.getMetadata(LLVMContext::MD_mem_parallel_loop_access), - MemParallelLoopAccess); - I.setMetadata(LLVMContext::MD_mem_parallel_loop_access, - MemParallelLoopAccess); + auto AdaptMetaData = [&](Instruction &I) { + if (NoAlias) { + // FIXME: should we handle AliasScope metadata on noalias intrinsics ? + if (auto *II = dyn_cast(&I)) { + auto ID = II->getIntrinsicID(); + if (ID == Intrinsic::noalias || ID == Intrinsic::noalias_decl || + ID == Intrinsic::provenance_noalias || + ID == Intrinsic::noalias_copy_guard) { + I.setMetadata(LLVMContext::MD_noalias, + MDNode::concatenate( + I.getMetadata(LLVMContext::MD_noalias), NoAlias)); + return; + } } + } - if (AccessGroup) - I.setMetadata(LLVMContext::MD_access_group, uniteAccessGroups( - I.getMetadata(LLVMContext::MD_access_group), AccessGroup)); + // This metadata is only relevant for instructions that access memory. + if (!I.mayReadOrWriteMemory()) + return; + + if (MemParallelLoopAccess) { + // TODO: This probably should not overwrite MemParalleLoopAccess. + MemParallelLoopAccess = MDNode::concatenate( + I.getMetadata(LLVMContext::MD_mem_parallel_loop_access), + MemParallelLoopAccess); + I.setMetadata(LLVMContext::MD_mem_parallel_loop_access, + MemParallelLoopAccess); + } - if (AliasScope) - I.setMetadata(LLVMContext::MD_alias_scope, MDNode::concatenate( - I.getMetadata(LLVMContext::MD_alias_scope), AliasScope)); + if (AccessGroup) + I.setMetadata( + LLVMContext::MD_access_group, + uniteAccessGroups(I.getMetadata(LLVMContext::MD_access_group), + AccessGroup)); + + if (AliasScope) + I.setMetadata( + LLVMContext::MD_alias_scope, + MDNode::concatenate(I.getMetadata(LLVMContext::MD_alias_scope), + AliasScope)); + + if (NoAlias) + I.setMetadata( + LLVMContext::MD_noalias, + MDNode::concatenate(I.getMetadata(LLVMContext::MD_noalias), NoAlias)); + }; - if (NoAlias) - I.setMetadata(LLVMContext::MD_noalias, MDNode::concatenate( - I.getMetadata(LLVMContext::MD_noalias), NoAlias)); - } - } + for (Instruction *NI : NewNoAliasInst) + AdaptMetaData(*NI); + + for (BasicBlock &BB : make_range(FStart, FEnd)) + for (Instruction &I : BB) + AdaptMetaData(I); } /// Utility for cloning !noalias and !alias.scope metadata. When a code region @@ -838,22 +872,55 @@ using MetadataMap = DenseMap; SetVector MD; MetadataMap MDMap; + MDNode *CallerNoAlias = nullptr; + MDNode *CalleeNoAlias = nullptr; + MDNode *NewUnknownScope = nullptr; void addRecursiveMetadataUses(); public: - ScopedAliasMetadataDeepCloner(const Function *F); + ScopedAliasMetadataDeepCloner(Function *F, Function *Caller); /// Create a new clone of the scoped alias metadata, which will be used by /// subsequent remap() calls. - void clone(); + void clone(LLVMContext &Context); /// Remap instructions in the given range from the original to the cloned /// metadata. void remap(Function::iterator FStart, Function::iterator FEnd); + + /// Update all memory instructions with the new unknown scope. + void updateNewUnknownScope(Function *Caller); }; -ScopedAliasMetadataDeepCloner::ScopedAliasMetadataDeepCloner( - const Function *F) { +ScopedAliasMetadataDeepCloner::ScopedAliasMetadataDeepCloner(Function *F, + Function *Caller) { + const auto *CalledFunc = F; + + // Track function level !noalias metadata ('unknown function' scope). This + // should be merged with the data from the callee. + CallerNoAlias = Caller->getMetadata("noalias"); + CalleeNoAlias = CalledFunc->getMetadata("noalias"); + + if ((CalleeNoAlias != nullptr) && (CallerNoAlias == nullptr)) { + // - NOTE: keep in sync with (clang) CGExpr: EmitLoadOfScalar + // - NOTE: keep in sync with (clang) CGDecl: EmitAutoVarNoAlias + // - EmitNoAliasDecl + // - NOTE: keep in sync with (llvm) InlineFunction: CloneAliasScopeMetadata + llvm::MDBuilder MDB(Caller->getContext()); + std::string Name(Caller->getName()); + auto NoAliasDomain = MDB.createAnonymousAliasScopeDomain(Name); + Name += ": unknown scope"; + + llvm::MDNode *UnknownScope = + MDB.createAnonymousAliasScope(NoAliasDomain, Name); + + { + SmallVector ScopeListEntries(1, UnknownScope); + CallerNoAlias = llvm::MDNode::get(Caller->getContext(), ScopeListEntries); + Caller->setMetadata("noalias", CallerNoAlias); + } + NewUnknownScope = UnknownScope; + } for (const BasicBlock &BB : *F) { for (const Instruction &I : BB) { if (const MDNode *M = I.getMetadata(LLVMContext::MD_alias_scope)) @@ -864,6 +931,34 @@ // We also need to clone the metadata in noalias intrinsics. if (const auto *Decl = dyn_cast(&I)) MD.insert(Decl->getScopeList()); + + // We also need to clone the metadata in noalias intrinsics. + if (const auto *II = dyn_cast(&I)) { + if (II->getIntrinsicID() == Intrinsic::noalias) + if (const auto *M = dyn_cast( + cast( + II->getOperand(Intrinsic::NoAliasScopeArg)) + ->getMetadata())) + MD.insert(M); + if (II->getIntrinsicID() == Intrinsic::provenance_noalias) + if (const auto *M = dyn_cast( + cast( + II->getOperand(Intrinsic::ProvenanceNoAliasScopeArg)) + ->getMetadata())) + MD.insert(M); + if (II->getIntrinsicID() == Intrinsic::noalias_decl) + if (const auto *M = dyn_cast( + cast( + II->getOperand(Intrinsic::NoAliasDeclScopeArg)) + ->getMetadata())) + MD.insert(M); + if (II->getIntrinsicID() == Intrinsic::noalias_copy_guard) + if (const auto *M = dyn_cast( + cast( + II->getOperand(Intrinsic::NoAliasCopyGuardScopeArg)) + ->getMetadata())) + MD.insert(M); + } } } addRecursiveMetadataUses(); @@ -880,10 +975,17 @@ } } -void ScopedAliasMetadataDeepCloner::clone() { +void ScopedAliasMetadataDeepCloner::clone(LLVMContext &Context) { assert(MDMap.empty() && "clone() already called ?"); SmallVector DummyNodes; + if ((CalleeNoAlias != nullptr) && (CalleeNoAlias != CallerNoAlias)) { + // Map CalleeNoAlias onto CallerNoAlias + MD.remove(CalleeNoAlias); + DummyNodes.push_back(MDTuple::getTemporary(Context, None)); + MDMap[CalleeNoAlias].reset(DummyNodes.back().get()); + cast(MDMap[CalleeNoAlias])->replaceAllUsesWith(CallerNoAlias); + } for (const MDNode *I : MD) { DummyNodes.push_back(MDTuple::getTemporary(I->getContext(), None)); MDMap[I].reset(DummyNodes.back().get()); @@ -919,6 +1021,10 @@ for (Instruction &I : BB) { // TODO: The null checks for the MDMap.lookup() results should no longer // be necessary. + + // Only update scopes when we find them in the map. If they are not, it is + // because we already handled that instruction before. This is faster than + // tracking which instructions we already updated. if (MDNode *M = I.getMetadata(LLVMContext::MD_alias_scope)) if (MDNode *MNew = MDMap.lookup(M)) I.setMetadata(LLVMContext::MD_alias_scope, MNew); @@ -930,17 +1036,142 @@ if (auto *Decl = dyn_cast(&I)) if (MDNode *MNew = MDMap.lookup(Decl->getScopeList())) Decl->setScopeList(MNew); + + // Update the metadata referenced by a noalias intrinsic + if (auto *II = dyn_cast(&I)) { + auto ID = II->getIntrinsicID(); + if (ID == Intrinsic::noalias || ID == Intrinsic::provenance_noalias || + ID == Intrinsic::noalias_decl || + ID == Intrinsic::noalias_copy_guard) { + int NoAliasScope = 0; + if (ID == Intrinsic::noalias) + NoAliasScope = Intrinsic::NoAliasScopeArg; + if (ID == Intrinsic::provenance_noalias) + NoAliasScope = Intrinsic::ProvenanceNoAliasScopeArg; + if (ID == Intrinsic::noalias_decl) + NoAliasScope = Intrinsic::NoAliasDeclScopeArg; + if (ID == Intrinsic::noalias_copy_guard) + NoAliasScope = Intrinsic::NoAliasCopyGuardScopeArg; + + if (auto *M = dyn_cast( + cast(II->getOperand(NoAliasScope)) + ->getMetadata())) { + // If the metadata is not in the map, it could be a new intrinsic + // that was just added. + auto MI = MDMap.find(M); + if (MI != MDMap.end()) + II->setOperand(NoAliasScope, MetadataAsValue::get( + II->getContext(), MI->second)); + } + } + } + } + } +} + +void ScopedAliasMetadataDeepCloner::updateNewUnknownScope(Function *Caller) { + if (!NewUnknownScope) + return; + + // We now need to add the out-of-function scope to _all_ instructions with + // noalias data in the 'caller' + // Note: following strange choice of variables names is similar to how it is + // done later + // FIXME: hmm this might be less than fast :( + // hmm it is also needed to do this _after_ the metadata cloning, otherwise + // we seem to lose information ! + for (BasicBlock &I : *Caller) { + for (Instruction &J : I) { + if (const MDNode *M = J.getMetadata(LLVMContext::MD_noalias)) { + SmallVector NewScopeList; + for (auto &MDOp : M->operands()) { + NewScopeList.push_back(MDOp); + } + NewScopeList.push_back(NewUnknownScope); + J.setMetadata(LLVMContext::MD_noalias, + MDNode::get(Caller->getContext(), NewScopeList)); + } else if (J.mayReadOrWriteMemory()) { + // no Noalias, but we need to add the (new) 'unknown scope' ! + J.setMetadata(LLVMContext::MD_noalias, CallerNoAlias); + } } } } +/// If the inlined function has noalias arguments, +/// then add a new alias scope to instructions that might access memory, and +/// noalias intrinsics corresponding to the noalias arguments. +static void AddNoAliasIntrinsics(CallBase &CB, ValueToValueMapTy &VMap, + MDNode *&NewScopeList, + SmallVectorImpl &NewNoAlias) { + if (!EnableNoAliasConversion || (UseNoAliasIntrinsic != NAIK_full)) + return; + + const Function *CalledFunc = CB.getCalledFunction(); + SmallVector NoAliasArgs; + + for (const auto &Arg : CalledFunc->args()) { + if (CB.paramHasAttr(Arg.getArgNo(), Attribute::NoAlias) && !Arg.use_empty()) + NoAliasArgs.push_back(&Arg); + } + + if (NoAliasArgs.empty()) + return; + + MDBuilder MDB(CalledFunc->getContext()); + // Create a new scope domain for this function. + MDNode *NewDomain = + MDB.createAnonymousAliasScopeDomain(CalledFunc->getName()); + + // Create a new scope for each noalias argument. + SmallVector Scopes; + + // For each noalias argument, add a noalias intrinsic call, and update the + // value map to refer to the new result of the noalias call. + for (const Argument *A : NoAliasArgs) { + Value *MappedA = VMap[A]; + if (isa(MappedA)) { + // Skip generating restrict intrinsics for known 'null' pointers + continue; + } + + std::string Name(CalledFunc->getName()); + if (A->hasName()) { + Name += ": %"; + Name += A->getName(); + } else { + Name += ": argument "; + Name += utostr(A->getArgNo()); + } + + MDNode *AScope = MDB.createAnonymousAliasScope(NewDomain, Name); + Scopes.push_back(AScope); + + MDNode *AScopeList = MDNode::get(CalledFunc->getContext(), AScope); + + // The alloca was optimized away -> use a nullptr + auto *IdentifyPAlloca = + ConstantPointerNull::get(MappedA->getType()->getPointerTo()); + auto *NoAliasDecl = + IRBuilder<>(&CB).CreateNoAliasDeclaration(IdentifyPAlloca, AScopeList); + Value *NA = IRBuilder<>(&CB).CreateNoAliasPointer( + MappedA, NoAliasDecl, IdentifyPAlloca, AScopeList); + VMap[A] = NA; + // Ensure that PropagateCallSiteMetadata also tracks this instruction. + NewNoAlias.emplace_back(cast(NA)); + } + + if (!Scopes.empty()) + NewScopeList = MDNode::get(CalledFunc->getContext(), Scopes); +} + /// If the inlined function has noalias arguments, /// then add new alias scopes for each noalias argument, tag the mapped noalias /// parameters with noalias metadata specifying the new scope, and tag all /// non-derived loads, stores and memory intrinsics with the new alias scopes. static void AddAliasScopeMetadata(CallBase &CB, ValueToValueMapTy &VMap, const DataLayout &DL, AAResults *CalleeAAR) { - if (!EnableNoAliasConversion) + if (!EnableNoAliasConversion || (UseNoAliasIntrinsic != NAIK_scopes)) return; const Function *CalledFunc = CB.getCalledFunction(); @@ -988,7 +1219,7 @@ MDNode *NewScope = MDB.createAnonymousAliasScope(NewDomain, Name); NewScopes.insert(std::make_pair(A, NewScope)); - if (UseNoAliasIntrinsic) { + if (UseNoAliasIntrinsic == NAIK_scopes) { // FIXME: already checked at entry // Introduce a llvm.experimental.noalias.scope.decl for the noalias // argument. MDNode *AScopeList = MDNode::get(CalledFunc->getContext(), NewScope); @@ -1894,6 +2125,7 @@ ValueToValueMapTy VMap; // Keep a list of pair (dst, src) to emit byval initializations. SmallVector, 4> ByValInit; + MDNode *NAScopeList = nullptr; // When inlining a function that contains noalias scope metadata, // this metadata needs to be cloned so that the inlined blocks @@ -1901,7 +2133,8 @@ // Track the metadata that must be cloned. Do this before other changes to // the function, so that we do not get in trouble when inlining caller == // callee. - ScopedAliasMetadataDeepCloner SAMetadataCloner(CB.getCalledFunction()); + ScopedAliasMetadataDeepCloner SAMetadataCloner(CB.getCalledFunction(), + Caller); auto &DL = Caller->getParent()->getDataLayout(); @@ -1939,6 +2172,10 @@ /// Preserve all attributes on of the call and its parameters. salvageKnowledge(&CB, AC); + SmallVector NewNoAliasIntrinsics; + // Add noalias intrinsics corresponding to noalias function arguments. + AddNoAliasIntrinsics(CB, VMap, NAScopeList, NewNoAliasIntrinsics); + // We want the inliner to prune the code as it copies. We would LOVE to // have no dead or constant instructions leftover after inlining occurs // (which can happen, e.g., because an argument was constant), but we'll be @@ -2031,8 +2268,9 @@ CalledFunc->getSubprogram() != nullptr); // Now clone the inlined noalias scope metadata. - SAMetadataCloner.clone(); + SAMetadataCloner.clone(CalledFunc->getContext()); SAMetadataCloner.remap(FirstNewBlock, Caller->end()); + SAMetadataCloner.updateNewUnknownScope(Caller); // Add noalias metadata if necessary. AddAliasScopeMetadata(CB, VMap, DL, CalleeAAR); @@ -2042,7 +2280,8 @@ AddReturnAttributes(CB, VMap); // Propagate metadata on the callsite if necessary. - PropagateCallSiteMetadata(CB, FirstNewBlock, Caller->end()); + PropagateCallSiteMetadata(CB, NAScopeList, NewNoAliasIntrinsics, + FirstNewBlock, Caller->end()); // Register any cloned assumptions. if (IFI.GetAssumptionCache) @@ -2478,6 +2717,9 @@ // Since we are now done with the return instruction, delete it also. Returns[0]->eraseFromParent(); + // Already try to connect llvm.noalias.decl where possible + propagateAndConnectNoAliasDecl(Caller); + // We are now done with the inlining. return InlineResult::success(); } @@ -2628,6 +2870,9 @@ // Now we can remove the CalleeEntry block, which is now empty. Caller->getBasicBlockList().erase(CalleeEntry); + // Already try to connect llvm.noalias.decl where possible + propagateAndConnectNoAliasDecl(Caller); + // If we inserted a phi node, check to see if it has a single value (e.g. all // the entries are the same or undef). If so, remove the PHI so it doesn't // block other optimizations. Index: llvm/lib/Transforms/Utils/NoAliasUtils.cpp =================================================================== --- /dev/null +++ llvm/lib/Transforms/Utils/NoAliasUtils.cpp @@ -0,0 +1,160 @@ +//===-- NoAliasUtils.cpp - NoAlias Utility functions ----------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines common noalias metadatt and intrinsic utility functions. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Transforms/Utils/NoAliasUtils.h" +#include "llvm/ADT/MapVector.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Analysis/ValueTracking.h" +#include "llvm/IR/InstIterator.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/MDBuilder.h" +#include "llvm/IR/Metadata.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +#define DEBUG_TYPE "noalias-utils" + +bool llvm::propagateAndConnectNoAliasDecl(Function *F) { + auto *UnknownFunctionScope = F->getMetadata("noalias"); + if (UnknownFunctionScope == nullptr) + return false; + + SmallVector InterestingNoalias; + SmallMapVector KnownAllocaNoAliasDecl; + + auto TrackIfIsUnknownFunctionScope = [&](IntrinsicInst *I, unsigned Index) { + auto V = I->getOperand(Index); + if (cast(V)->getMetadata() == UnknownFunctionScope) { + InterestingNoalias.push_back(I); + } + }; + + for (Instruction &I : llvm::instructions(*F)) { + if (IntrinsicInst *II = dyn_cast(&I)) { + switch (II->getIntrinsicID()) { + case Intrinsic::noalias: { + TrackIfIsUnknownFunctionScope(II, Intrinsic::NoAliasScopeArg); + break; + } + case Intrinsic::provenance_noalias: { + TrackIfIsUnknownFunctionScope(II, Intrinsic::ProvenanceNoAliasScopeArg); + break; + } + case Intrinsic::noalias_copy_guard: { + TrackIfIsUnknownFunctionScope(II, Intrinsic::NoAliasCopyGuardScopeArg); + break; + } + case Intrinsic::noalias_decl: { + auto *depAlloca = dyn_cast(II->getOperand(0)); + if (depAlloca) { + KnownAllocaNoAliasDecl[depAlloca] = II; + } + break; + } + default: + break; + } + } + } + + if (KnownAllocaNoAliasDecl.empty() || InterestingNoalias.empty()) + return false; + + bool Changed = false; + for (auto *II : InterestingNoalias) { + SmallVector UO; + unsigned Index = + (II->getIntrinsicID() == Intrinsic::noalias + ? 0 + : (II->getIntrinsicID() == Intrinsic::provenance_noalias ? 1 : 2)); + const int IdentifyPArg[] = {Intrinsic::NoAliasIdentifyPArg, + Intrinsic::ProvenanceNoAliasIdentifyPArg, + Intrinsic::NoAliasCopyGuardIdentifyPBaseObject}; + const int ScopeArg[] = {Intrinsic::NoAliasScopeArg, + Intrinsic::ProvenanceNoAliasScopeArg, + Intrinsic::NoAliasCopyGuardScopeArg}; + const int NoAliasDeclArg[] = {Intrinsic::NoAliasNoAliasDeclArg, + Intrinsic::ProvenanceNoAliasNoAliasDeclArg, + Intrinsic::NoAliasCopyGuardNoAliasDeclArg}; + const int ObjIdArg[] = {Intrinsic::NoAliasIdentifyPObjIdArg, + Intrinsic::ProvenanceNoAliasIdentifyPObjIdArg, -1}; + + llvm::getUnderlyingObjects(II->getOperand(IdentifyPArg[Index]), UO); + if (UO.size() != 1) { + // Multiple objects possible - It would be nice to propagate, but we do + // not do it yet. That is ok as the unknown function scope assumes more + // aliasing. + LLVM_DEBUG(llvm::dbgs() + << "WARNING: no llvm.noalias.decl reconnect accross " + "PHI/select - YET (" + << UO.size() << " underlying objects)\n"); + continue; + } + + if (auto *UA = dyn_cast(UO[0])) { + auto it = KnownAllocaNoAliasDecl.find(UA); + if (it != KnownAllocaNoAliasDecl.end()) { + Instruction *Decl = it->second; + // found a simple matching declaration - propagate + II->setOperand(ScopeArg[Index], + Decl->getOperand(Intrinsic::NoAliasDeclScopeArg)); + II->setOperand(NoAliasDeclArg[Index], Decl); + + auto ObjIdIndex = ObjIdArg[Index]; + if (ObjIdIndex != -1) { + II->setOperand(ObjIdIndex, + Decl->getOperand(Intrinsic::NoAliasDeclObjIdArg)); + } + Changed = true; + } else if (UnknownFunctionScope && isa(UA)) { + if (cast(II->getOperand(ScopeArg[Index])) + ->getMetadata() == UnknownFunctionScope) { + // we have an alloca, but no llvm.noalias.decl and we have unknown + // function scope This is an indication of a temporary that (through a + // pointer or reference to a restrict pointer) introduces restrict. + // - the unknown scope is too broad for these cases + // - conceptually, the scope should be the lifetime of the local, but + // we don't have that information + // - the real restrictness should have been brought in through the + // 'depends on' relationship + // -> so we fall back on the 'depends on' and remove the restrictness + // information at this level. + LLVM_DEBUG( + llvm::dbgs() + << "- Temporary noalias object (without llvm.noalias.decl) " + "detected. Ignore restrictness: " + << *II << "\n"); + II->replaceAllUsesWith(II->getOperand(0)); + II->eraseFromParent(); + Changed = true; + } + } + } else { +#if !defined(NDEBUG) + if (isa(UO[0]) || isa(UO[0])) { + // Multiple objects possible - It would be nice to propagate, but we do + // not do it yet. That is ok as the unknown function scope assumes more + // aliasing. + LLVM_DEBUG(llvm::dbgs() + << "WARNING: no llvm.noalias.decl reconnect accross " + "PHI/select - YET: " + << *UO[0] << "\n"); + } +#endif + } + } + return Changed; +} Index: llvm/test/Transforms/Inline/noalias-calls.ll =================================================================== --- llvm/test/Transforms/Inline/noalias-calls.ll +++ llvm/test/Transforms/Inline/noalias-calls.ll @@ -1,15 +1,19 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature -; RUN: opt -basic-aa -inline -enable-noalias-to-md-conversion -S < %s | FileCheck %s -; RUN: opt -aa-pipeline=basic-aa -passes=inline -enable-noalias-to-md-conversion -S < %s | FileCheck %s +; RUN: opt -basic-aa -inline -enable-noalias-to-md-conversion -use-noalias-intrinsic-during-inlining=none -S < %s | FileCheck %s -check-prefixes=CHECK,NONE +; RUN: opt -basic-aa -inline -enable-noalias-to-md-conversion -use-noalias-intrinsic-during-inlining=scopes -S < %s | FileCheck %s -check-prefixes=CHECK,SCOPES +; RUN: opt -basic-aa -inline -enable-noalias-to-md-conversion -use-noalias-intrinsic-during-inlining=full -S < %s | FileCheck %s -check-prefixes=CHECK,FULL +; RUN: opt -aa-pipeline=basic-aa -passes=inline -enable-noalias-to-md-conversion -use-noalias-intrinsic-during-inlining=none -S < %s | FileCheck %s -check-prefixes=CHECK,NONE +; RUN: opt -aa-pipeline=basic-aa -passes=inline -enable-noalias-to-md-conversion -use-noalias-intrinsic-during-inlining=scopes -S < %s | FileCheck %s -check-prefixes=CHECK,SCOPES +; RUN: opt -aa-pipeline=basic-aa -passes=inline -enable-noalias-to-md-conversion -use-noalias-intrinsic-during-inlining=full -S < %s | FileCheck %s -check-prefixes=CHECK,FULL target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128" target triple = "x86_64-unknown-linux-gnu" declare void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture, i8* nocapture readonly, i64, i1) #0 -declare void @hey() #0 +declare void @hey() #1 define void @hello(i8* noalias nocapture %a, i8* noalias nocapture readonly %c, i8* nocapture %b) #1 { ; CHECK-LABEL: define {{[^@]+}}@hello -; CHECK-SAME: (i8* noalias nocapture [[A:%.*]], i8* noalias nocapture readonly [[C:%.*]], i8* nocapture [[B:%.*]]) [[ATTR1:#.*]] { +; CHECK-SAME: (i8* noalias nocapture [[A:%.*]], i8* noalias nocapture readonly [[C:%.*]], i8* nocapture [[B:%.*]]) #[[ATTR1:[0-9]+]] { ; CHECK-NEXT: entry: ; CHECK-NEXT: [[L:%.*]] = alloca i8, i32 512, align 1 ; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 [[A]], i8* align 16 [[B]], i64 16, i1 false) @@ -30,20 +34,50 @@ } define void @foo(i8* nocapture %a, i8* nocapture readonly %c, i8* nocapture %b) #2 { -; CHECK-LABEL: define {{[^@]+}}@foo -; CHECK-SAME: (i8* nocapture [[A:%.*]], i8* nocapture readonly [[C:%.*]], i8* nocapture [[B:%.*]]) [[ATTR2:#.*]] { -; CHECK-NEXT: entry: -; CHECK-NEXT: [[L_I:%.*]] = alloca i8, i32 512, align 1 -; CHECK-NEXT: call void @llvm.experimental.noalias.scope.decl([[META0:metadata !.*]]) -; CHECK-NEXT: call void @llvm.experimental.noalias.scope.decl([[META3:metadata !.*]]) -; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 512, i8* [[L_I]]) -; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 [[A]], i8* align 16 [[B]], i64 16, i1 false) [[ATTR2]], !noalias !3 -; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 [[B]], i8* align 16 [[C]], i64 16, i1 false) [[ATTR2]], !noalias !0 -; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 [[A]], i8* align 16 [[C]], i64 16, i1 false) [[ATTR2]], !alias.scope !5 -; CHECK-NEXT: call void @hey() [[ATTR2]], !noalias !5 -; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 [[L_I]], i8* align 16 [[C]], i64 16, i1 false) [[ATTR2]], !noalias !0 -; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 512, i8* [[L_I]]) -; CHECK-NEXT: ret void +; NONE-LABEL: define {{[^@]+}}@foo +; NONE-SAME: (i8* nocapture [[A:%.*]], i8* nocapture readonly [[C:%.*]], i8* nocapture [[B:%.*]]) #[[ATTR2:[0-9]+]] { +; NONE-NEXT: entry: +; NONE-NEXT: [[L_I:%.*]] = alloca i8, i32 512, align 1 +; NONE-NEXT: call void @llvm.lifetime.start.p0i8(i64 512, i8* [[L_I]]) +; NONE-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 [[A]], i8* align 16 [[B]], i64 16, i1 false) #[[ATTR2]] +; NONE-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 [[B]], i8* align 16 [[C]], i64 16, i1 false) #[[ATTR2]] +; NONE-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 [[A]], i8* align 16 [[C]], i64 16, i1 false) #[[ATTR2]] +; NONE-NEXT: call void @hey() #[[ATTR2]] +; NONE-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 [[L_I]], i8* align 16 [[C]], i64 16, i1 false) #[[ATTR2]] +; NONE-NEXT: call void @llvm.lifetime.end.p0i8(i64 512, i8* [[L_I]]) +; NONE-NEXT: ret void +; +; SCOPES-LABEL: define {{[^@]+}}@foo +; SCOPES-SAME: (i8* nocapture [[A:%.*]], i8* nocapture readonly [[C:%.*]], i8* nocapture [[B:%.*]]) #[[ATTR2:[0-9]+]] { +; SCOPES-NEXT: entry: +; SCOPES-NEXT: [[L_I:%.*]] = alloca i8, i32 512, align 1 +; SCOPES-NEXT: call void @llvm.experimental.noalias.scope.decl(metadata [[META0:![0-9]+]]) +; SCOPES-NEXT: call void @llvm.experimental.noalias.scope.decl(metadata [[META3:![0-9]+]]) +; SCOPES-NEXT: call void @llvm.lifetime.start.p0i8(i64 512, i8* [[L_I]]) +; SCOPES-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 [[A]], i8* align 16 [[B]], i64 16, i1 false) #[[ATTR2]], !noalias !3 +; SCOPES-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 [[B]], i8* align 16 [[C]], i64 16, i1 false) #[[ATTR2]], !noalias !0 +; SCOPES-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 [[A]], i8* align 16 [[C]], i64 16, i1 false) #[[ATTR2]], !alias.scope !5 +; SCOPES-NEXT: call void @hey() #[[ATTR2]], !noalias !5 +; SCOPES-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 [[L_I]], i8* align 16 [[C]], i64 16, i1 false) #[[ATTR2]], !noalias !0 +; SCOPES-NEXT: call void @llvm.lifetime.end.p0i8(i64 512, i8* [[L_I]]) +; SCOPES-NEXT: ret void +; +; FULL-LABEL: define {{[^@]+}}@foo +; FULL-SAME: (i8* nocapture [[A:%.*]], i8* nocapture readonly [[C:%.*]], i8* nocapture [[B:%.*]]) #[[ATTR2:[0-9]+]] { +; FULL-NEXT: entry: +; FULL-NEXT: [[L_I:%.*]] = alloca i8, i32 512, align 1 +; FULL-NEXT: [[TMP0:%.*]] = call i8* @llvm.noalias.decl.p0i8.p0p0i8.i64(i8** null, i64 0, metadata [[META0:![0-9]+]]) +; FULL-NEXT: [[TMP1:%.*]] = call i8* @llvm.noalias.p0i8.p0i8.p0p0i8.i64(i8* [[A]], i8* [[TMP0]], i8** null, i64 0, metadata [[META0]]), !noalias !3 +; FULL-NEXT: [[TMP2:%.*]] = call i8* @llvm.noalias.decl.p0i8.p0p0i8.i64(i8** null, i64 0, metadata [[META5:![0-9]+]]) +; FULL-NEXT: [[TMP3:%.*]] = call i8* @llvm.noalias.p0i8.p0i8.p0p0i8.i64(i8* [[C]], i8* [[TMP2]], i8** null, i64 0, metadata [[META5]]), !noalias !3 +; FULL-NEXT: call void @llvm.lifetime.start.p0i8(i64 512, i8* [[L_I]]) +; FULL-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 [[TMP1]], i8* align 16 [[B]], i64 16, i1 false) #[[ATTR2]], !noalias !3 +; FULL-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 [[B]], i8* align 16 [[TMP3]], i64 16, i1 false) #[[ATTR2]], !noalias !3 +; FULL-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 [[TMP1]], i8* align 16 [[TMP3]], i64 16, i1 false) #[[ATTR2]], !noalias !3 +; FULL-NEXT: call void @hey() #[[ATTR2]], !noalias !3 +; FULL-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 [[L_I]], i8* align 16 [[TMP3]], i64 16, i1 false) #[[ATTR2]], !noalias !3 +; FULL-NEXT: call void @llvm.lifetime.end.p0i8(i64 512, i8* [[L_I]]) +; FULL-NEXT: ret void ; entry: tail call void @hello(i8* %a, i8* %c, i8* %b) @@ -52,7 +86,7 @@ define void @hello_cs(i8* nocapture %a, i8* nocapture readonly %c, i8* nocapture %b) #1 { ; CHECK-LABEL: define {{[^@]+}}@hello_cs -; CHECK-SAME: (i8* nocapture [[A:%.*]], i8* nocapture readonly [[C:%.*]], i8* nocapture [[B:%.*]]) [[ATTR1]] { +; CHECK-SAME: (i8* nocapture [[A:%.*]], i8* nocapture readonly [[C:%.*]], i8* nocapture [[B:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: entry: ; CHECK-NEXT: [[L:%.*]] = alloca i8, i32 512, align 1 ; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 [[A]], i8* align 16 [[B]], i64 16, i1 false) @@ -73,20 +107,50 @@ } define void @foo_cs(i8* nocapture %a, i8* nocapture readonly %c, i8* nocapture %b) #2 { -; CHECK-LABEL: define {{[^@]+}}@foo_cs -; CHECK-SAME: (i8* nocapture [[A:%.*]], i8* nocapture readonly [[C:%.*]], i8* nocapture [[B:%.*]]) [[ATTR2]] { -; CHECK-NEXT: entry: -; CHECK-NEXT: [[L_I:%.*]] = alloca i8, i32 512, align 1 -; CHECK-NEXT: call void @llvm.experimental.noalias.scope.decl([[META6:metadata !.*]]) -; CHECK-NEXT: call void @llvm.experimental.noalias.scope.decl([[META9:metadata !.*]]) -; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 512, i8* [[L_I]]) -; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 [[A]], i8* align 16 [[B]], i64 16, i1 false) [[ATTR2]], !noalias !9 -; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 [[B]], i8* align 16 [[C]], i64 16, i1 false) [[ATTR2]], !noalias !6 -; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 [[A]], i8* align 16 [[C]], i64 16, i1 false) [[ATTR2]], !alias.scope !11 -; CHECK-NEXT: call void @hey() [[ATTR2]], !noalias !11 -; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 [[L_I]], i8* align 16 [[C]], i64 16, i1 false) [[ATTR2]], !noalias !6 -; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 512, i8* [[L_I]]) -; CHECK-NEXT: ret void +; NONE-LABEL: define {{[^@]+}}@foo_cs +; NONE-SAME: (i8* nocapture [[A:%.*]], i8* nocapture readonly [[C:%.*]], i8* nocapture [[B:%.*]]) #[[ATTR2]] { +; NONE-NEXT: entry: +; NONE-NEXT: [[L_I:%.*]] = alloca i8, i32 512, align 1 +; NONE-NEXT: call void @llvm.lifetime.start.p0i8(i64 512, i8* [[L_I]]) +; NONE-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 [[A]], i8* align 16 [[B]], i64 16, i1 false) #[[ATTR2]] +; NONE-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 [[B]], i8* align 16 [[C]], i64 16, i1 false) #[[ATTR2]] +; NONE-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 [[A]], i8* align 16 [[C]], i64 16, i1 false) #[[ATTR2]] +; NONE-NEXT: call void @hey() #[[ATTR2]] +; NONE-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 [[L_I]], i8* align 16 [[C]], i64 16, i1 false) #[[ATTR2]] +; NONE-NEXT: call void @llvm.lifetime.end.p0i8(i64 512, i8* [[L_I]]) +; NONE-NEXT: ret void +; +; SCOPES-LABEL: define {{[^@]+}}@foo_cs +; SCOPES-SAME: (i8* nocapture [[A:%.*]], i8* nocapture readonly [[C:%.*]], i8* nocapture [[B:%.*]]) #[[ATTR2]] { +; SCOPES-NEXT: entry: +; SCOPES-NEXT: [[L_I:%.*]] = alloca i8, i32 512, align 1 +; SCOPES-NEXT: call void @llvm.experimental.noalias.scope.decl(metadata [[META6:![0-9]+]]) +; SCOPES-NEXT: call void @llvm.experimental.noalias.scope.decl(metadata [[META9:![0-9]+]]) +; SCOPES-NEXT: call void @llvm.lifetime.start.p0i8(i64 512, i8* [[L_I]]) +; SCOPES-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 [[A]], i8* align 16 [[B]], i64 16, i1 false) #[[ATTR2]], !noalias !9 +; SCOPES-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 [[B]], i8* align 16 [[C]], i64 16, i1 false) #[[ATTR2]], !noalias !6 +; SCOPES-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 [[A]], i8* align 16 [[C]], i64 16, i1 false) #[[ATTR2]], !alias.scope !11 +; SCOPES-NEXT: call void @hey() #[[ATTR2]], !noalias !11 +; SCOPES-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 [[L_I]], i8* align 16 [[C]], i64 16, i1 false) #[[ATTR2]], !noalias !6 +; SCOPES-NEXT: call void @llvm.lifetime.end.p0i8(i64 512, i8* [[L_I]]) +; SCOPES-NEXT: ret void +; +; FULL-LABEL: define {{[^@]+}}@foo_cs +; FULL-SAME: (i8* nocapture [[A:%.*]], i8* nocapture readonly [[C:%.*]], i8* nocapture [[B:%.*]]) #[[ATTR2]] { +; FULL-NEXT: entry: +; FULL-NEXT: [[L_I:%.*]] = alloca i8, i32 512, align 1 +; FULL-NEXT: [[TMP0:%.*]] = call i8* @llvm.noalias.decl.p0i8.p0p0i8.i64(i8** null, i64 0, metadata [[META6:![0-9]+]]) +; FULL-NEXT: [[TMP1:%.*]] = call i8* @llvm.noalias.p0i8.p0i8.p0p0i8.i64(i8* [[A]], i8* [[TMP0]], i8** null, i64 0, metadata [[META6]]), !noalias !9 +; FULL-NEXT: [[TMP2:%.*]] = call i8* @llvm.noalias.decl.p0i8.p0p0i8.i64(i8** null, i64 0, metadata [[META11:![0-9]+]]) +; FULL-NEXT: [[TMP3:%.*]] = call i8* @llvm.noalias.p0i8.p0i8.p0p0i8.i64(i8* [[C]], i8* [[TMP2]], i8** null, i64 0, metadata [[META11]]), !noalias !9 +; FULL-NEXT: call void @llvm.lifetime.start.p0i8(i64 512, i8* [[L_I]]) +; FULL-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 [[TMP1]], i8* align 16 [[B]], i64 16, i1 false) #[[ATTR2]], !noalias !9 +; FULL-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 [[B]], i8* align 16 [[TMP3]], i64 16, i1 false) #[[ATTR2]], !noalias !9 +; FULL-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 [[TMP1]], i8* align 16 [[TMP3]], i64 16, i1 false) #[[ATTR2]], !noalias !9 +; FULL-NEXT: call void @hey() #[[ATTR2]], !noalias !9 +; FULL-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 [[L_I]], i8* align 16 [[TMP3]], i64 16, i1 false) #[[ATTR2]], !noalias !9 +; FULL-NEXT: call void @llvm.lifetime.end.p0i8(i64 512, i8* [[L_I]]) +; FULL-NEXT: ret void ; entry: tail call void @hello_cs(i8* noalias %a, i8* noalias %c, i8* %b) @@ -98,16 +162,25 @@ attributes #2 = { nounwind } attributes #3 = { nounwind uwtable } -; CHECK: !0 = !{!1} -; CHECK: !1 = distinct !{!1, !2, !"hello: %a"} -; CHECK: !2 = distinct !{!2, !"hello"} -; CHECK: !3 = !{!4} -; CHECK: !4 = distinct !{!4, !2, !"hello: %c"} -; CHECK: !5 = !{!1, !4} +; NONE-NOT: !0 = + +; SCOPES: !0 = !{!1} +; SCOPES-NEXT: !1 = distinct !{!1, !2, !"hello: %a"} +; SCOPES-NEXT: !2 = distinct !{!2, !"hello"} +; SCOPES-NEXT: !3 = !{!4} +; SCOPES-NEXT: !4 = distinct !{!4, !2, !"hello: %c"} +; SCOPES-NEXT: !5 = !{!1, !4} +; SCOPES-NEXT: !6 = !{!7} +; SCOPES-NEXT: !7 = distinct !{!7, !8, !"hello_cs: %a"} +; SCOPES-NEXT: !8 = distinct !{!8, !"hello_cs"} +; SCOPES-NEXT: !9 = !{!10} +; SCOPES-NEXT: !10 = distinct !{!10, !8, !"hello_cs: %c"} +; SCOPES-NEXT: !11 = !{!7, !10} + -; CHECK: !6 = !{!7} -; CHECK: !7 = distinct !{!7, !8, !"hello_cs: %a"} -; CHECK: !8 = distinct !{!8, !"hello_cs"} -; CHECK: !9 = !{!10} -; CHECK: !10 = distinct !{!10, !8, !"hello_cs: %c"} -; CHECK: !11 = !{!7, !10} +; FULL: !0 = !{!1} +; FULL-NEXT: !1 = distinct !{!1, !2, !"hello: %a"} +; FULL-NEXT: !2 = distinct !{!2, !"hello"} +; FULL-NEXT: !3 = !{!1, !4} +; FULL-NEXT: !4 = distinct !{!4, !2, !"hello: %c"} +; FULL-NEXT: !5 = !{!4} Index: llvm/test/Transforms/Inline/noalias-recursive.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/Inline/noalias-recursive.ll @@ -0,0 +1,199 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -inline -enable-noalias-to-md-conversion -use-noalias-intrinsic-during-inlining=scopes -S < %s | FileCheck %s --check-prefixes=SCOPES +; RUN: opt -inline -enable-noalias-to-md-conversion -use-noalias-intrinsic-during-inlining=full -S < %s | FileCheck %s --check-prefixes=FULL + +%class.ah = type { [8 x i8] } + +; Test for self recursion: + +; Function Attrs: nounwind uwtable +define void @Test01(%class.ah* noalias sret(%class.ah) align 8 %agg.result, i32 %n) local_unnamed_addr #0 !noalias !1 { +; SCOPES-LABEL: @Test01( +; SCOPES-NEXT: entry: +; SCOPES-NEXT: [[SWITCH:%.*]] = icmp eq i32 [[N:%.*]], 0 +; SCOPES-NEXT: br i1 [[SWITCH]], label [[SW_BB:%.*]], label [[SW_BB1:%.*]] +; SCOPES: sw.bb: +; SCOPES-NEXT: [[TMP0:%.*]] = getelementptr inbounds [[CLASS_AH:%.*]], %class.ah* [[AGG_RESULT:%.*]], i64 0, i32 0, i64 0 +; SCOPES-NEXT: store i8 42, i8* [[TMP0]], align 1, !noalias !1 +; SCOPES-NEXT: ret void +; SCOPES: sw.bb1: +; SCOPES-NEXT: call void @llvm.experimental.noalias.scope.decl(metadata [[META4:![0-9]+]]) +; SCOPES-NEXT: [[TMP1:%.*]] = getelementptr inbounds [[CLASS_AH]], %class.ah* [[AGG_RESULT]], i64 0, i32 0, i64 0 +; SCOPES-NEXT: store i8 42, i8* [[TMP1]], align 1, !alias.scope !4, !noalias !7 +; SCOPES-NEXT: ret void +; +; FULL-LABEL: @Test01( +; FULL-NEXT: entry: +; FULL-NEXT: [[SWITCH:%.*]] = icmp eq i32 [[N:%.*]], 0 +; FULL-NEXT: br i1 [[SWITCH]], label [[SW_BB:%.*]], label [[SW_BB1:%.*]] +; FULL: sw.bb: +; FULL-NEXT: [[TMP0:%.*]] = getelementptr inbounds [[CLASS_AH:%.*]], %class.ah* [[AGG_RESULT:%.*]], i64 0, i32 0, i64 0 +; FULL-NEXT: store i8 42, i8* [[TMP0]], align 1, !noalias !1 +; FULL-NEXT: ret void +; FULL: sw.bb1: +; FULL-NEXT: [[TMP1:%.*]] = call i8* @llvm.noalias.decl.p0i8.p0p0s_class.ahs.i64(%class.ah** null, i64 0, metadata [[META4:![0-9]+]]) +; FULL-NEXT: [[TMP2:%.*]] = call %class.ah* @llvm.noalias.p0s_class.ahs.p0i8.p0p0s_class.ahs.i64(%class.ah* [[AGG_RESULT]], i8* [[TMP1]], %class.ah** null, i64 0, metadata [[META4]]), !noalias !7 +; FULL-NEXT: [[TMP3:%.*]] = getelementptr inbounds [[CLASS_AH]], %class.ah* [[TMP2]], i64 0, i32 0, i64 0 +; FULL-NEXT: store i8 42, i8* [[TMP3]], align 1, !noalias !8 +; FULL-NEXT: ret void +; +entry: + %switch = icmp eq i32 %n, 0 + br i1 %switch, label %sw.bb, label %sw.bb1 + +sw.bb: ; preds = %entry + %0 = getelementptr inbounds %class.ah, %class.ah* %agg.result, i64 0, i32 0, i64 0 + store i8 42, i8* %0, !noalias !1 + ret void + +sw.bb1: ; preds = %entry + call void @Test01(%class.ah* nonnull sret(%class.ah) align 8 %agg.result, i32 0), !noalias !1 + ret void +} + +; And equivalent version, but without the selfrecursion: + +; Function Attrs: nounwind uwtable +declare void @Test02c(%class.ah* noalias sret(%class.ah) align 8 %agg.result, i32 %n) local_unnamed_addr #0 + +; Function Attrs: nounwind uwtable +define void @Test02b(%class.ah* noalias sret(%class.ah) align 8 %agg.result, i32 %n) local_unnamed_addr #0 !noalias !4 { +; SCOPES-LABEL: @Test02b( +; SCOPES-NEXT: entry: +; SCOPES-NEXT: [[SWITCH:%.*]] = icmp eq i32 [[N:%.*]], 0 +; SCOPES-NEXT: br i1 [[SWITCH]], label [[SW_BB:%.*]], label [[SW_BB1:%.*]] +; SCOPES: sw.bb: +; SCOPES-NEXT: [[TMP0:%.*]] = getelementptr inbounds [[CLASS_AH:%.*]], %class.ah* [[AGG_RESULT:%.*]], i64 0, i32 0, i64 0 +; SCOPES-NEXT: store i8 42, i8* [[TMP0]], align 1, !noalias !10 +; SCOPES-NEXT: ret void +; SCOPES: sw.bb1: +; SCOPES-NEXT: call void @Test02c(%class.ah* nonnull sret([[CLASS_AH]]) align 8 [[AGG_RESULT]], i32 0), !noalias !10 +; SCOPES-NEXT: ret void +; +; FULL-LABEL: @Test02b( +; FULL-NEXT: entry: +; FULL-NEXT: [[SWITCH:%.*]] = icmp eq i32 [[N:%.*]], 0 +; FULL-NEXT: br i1 [[SWITCH]], label [[SW_BB:%.*]], label [[SW_BB1:%.*]] +; FULL: sw.bb: +; FULL-NEXT: [[TMP0:%.*]] = getelementptr inbounds [[CLASS_AH:%.*]], %class.ah* [[AGG_RESULT:%.*]], i64 0, i32 0, i64 0 +; FULL-NEXT: store i8 42, i8* [[TMP0]], align 1, !noalias !11 +; FULL-NEXT: ret void +; FULL: sw.bb1: +; FULL-NEXT: call void @Test02c(%class.ah* nonnull sret([[CLASS_AH]]) align 8 [[AGG_RESULT]], i32 0), !noalias !11 +; FULL-NEXT: ret void +; +entry: + %switch = icmp eq i32 %n, 0 + br i1 %switch, label %sw.bb, label %sw.bb1 + +sw.bb: ; preds = %entry + %0 = getelementptr inbounds %class.ah, %class.ah* %agg.result, i64 0, i32 0, i64 0 + store i8 42, i8* %0, !noalias !4 + ret void + +sw.bb1: ; preds = %entry + call void @Test02c(%class.ah* nonnull sret(%class.ah) align 8 %agg.result, i32 0), !noalias !4 + ret void +} + +; Function Attrs: nounwind uwtable +define void @Test02a(%class.ah* noalias sret(%class.ah) align 8 %agg.result, i32 %n) local_unnamed_addr #0 !noalias !7 { +; SCOPES-LABEL: @Test02a( +; SCOPES-NEXT: entry: +; SCOPES-NEXT: [[SWITCH:%.*]] = icmp eq i32 [[N:%.*]], 0 +; SCOPES-NEXT: br i1 [[SWITCH]], label [[SW_BB:%.*]], label [[SW_BB1:%.*]] +; SCOPES: sw.bb: +; SCOPES-NEXT: [[TMP0:%.*]] = getelementptr inbounds [[CLASS_AH:%.*]], %class.ah* [[AGG_RESULT:%.*]], i64 0, i32 0, i64 0 +; SCOPES-NEXT: store i8 42, i8* [[TMP0]], align 1, !noalias !13 +; SCOPES-NEXT: ret void +; SCOPES: sw.bb1: +; SCOPES-NEXT: call void @llvm.experimental.noalias.scope.decl(metadata [[META16:![0-9]+]]) +; SCOPES-NEXT: [[TMP1:%.*]] = getelementptr inbounds [[CLASS_AH]], %class.ah* [[AGG_RESULT]], i64 0, i32 0, i64 0 +; SCOPES-NEXT: store i8 42, i8* [[TMP1]], align 1, !alias.scope !16, !noalias !13 +; SCOPES-NEXT: ret void +; +; FULL-LABEL: @Test02a( +; FULL-NEXT: entry: +; FULL-NEXT: [[SWITCH:%.*]] = icmp eq i32 [[N:%.*]], 0 +; FULL-NEXT: br i1 [[SWITCH]], label [[SW_BB:%.*]], label [[SW_BB1:%.*]] +; FULL: sw.bb: +; FULL-NEXT: [[TMP0:%.*]] = getelementptr inbounds [[CLASS_AH:%.*]], %class.ah* [[AGG_RESULT:%.*]], i64 0, i32 0, i64 0 +; FULL-NEXT: store i8 42, i8* [[TMP0]], align 1, !noalias !14 +; FULL-NEXT: ret void +; FULL: sw.bb1: +; FULL-NEXT: [[TMP1:%.*]] = call i8* @llvm.noalias.decl.p0i8.p0p0s_class.ahs.i64(%class.ah** null, i64 0, metadata [[META17:![0-9]+]]) +; FULL-NEXT: [[TMP2:%.*]] = call %class.ah* @llvm.noalias.p0s_class.ahs.p0i8.p0p0s_class.ahs.i64(%class.ah* [[AGG_RESULT]], i8* [[TMP1]], %class.ah** null, i64 0, metadata [[META17]]), !noalias !20 +; FULL-NEXT: [[TMP3:%.*]] = getelementptr inbounds [[CLASS_AH]], %class.ah* [[TMP2]], i64 0, i32 0, i64 0 +; FULL-NEXT: store i8 42, i8* [[TMP3]], align 1, !noalias !20 +; FULL-NEXT: ret void +; +entry: + %switch = icmp eq i32 %n, 0 + br i1 %switch, label %sw.bb, label %sw.bb1 + +sw.bb: ; preds = %entry + %0 = getelementptr inbounds %class.ah, %class.ah* %agg.result, i64 0, i32 0, i64 0 + store i8 42, i8* %0, !noalias !7 + ret void + +sw.bb1: ; preds = %entry + call void @Test02b(%class.ah* nonnull sret(%class.ah) align 8 %agg.result, i32 0), !noalias !7 + ret void +} + +attributes #0 = { nounwind uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" "unsafe-fp-math"="false" "use-soft-float"="false" } + +!llvm.ident = !{!0} + +!0 = !{!"clang"} +!1 = !{!2} +!2 = distinct !{!2, !3, !"Test01: unknown function scope"} +!3 = distinct !{!3, !"Test01"} +!4 = !{!5} +!5 = distinct !{!5, !6, !"Test02b: unknown function scope"} +!6 = distinct !{!6, !"Test02b"} +!7 = !{!8} +!8 = distinct !{!8, !9, !"Test02a: unknown function scope"} +!9 = distinct !{!9, !"Test02a"} + +; SCOPES: !0 = !{!"clang"} +; SCOPES-NEXT: !1 = !{!2} +; SCOPES-NEXT: !2 = distinct !{!2, !3, !"Test01: unknown function scope"} +; SCOPES-NEXT: !3 = distinct !{!3, !"Test01"} +; SCOPES-NEXT: !4 = !{!5} +; SCOPES-NEXT: !5 = distinct !{!5, !6, !"Test01: %agg.result"} +; SCOPES-NEXT: !6 = distinct !{!6, !"Test01"} +; SCOPES-NEXT: !7 = !{!8, !2} +; SCOPES-NEXT: !8 = distinct !{!8, !9, !"Test01: unknown function scope"} +; SCOPES-NEXT: !9 = distinct !{!9, !"Test01"} +; SCOPES-NEXT: !10 = !{!11} +; SCOPES-NEXT: !11 = distinct !{!11, !12, !"Test02b: unknown function scope"} +; SCOPES-NEXT: !12 = distinct !{!12, !"Test02b"} +; SCOPES-NEXT: !13 = !{!14} +; SCOPES-NEXT: !14 = distinct !{!14, !15, !"Test02a: unknown function scope"} +; SCOPES-NEXT: !15 = distinct !{!15, !"Test02a"} +; SCOPES-NEXT: !16 = !{!17} +; SCOPES-NEXT: !17 = distinct !{!17, !18, !"Test02b: %agg.result"} +; SCOPES-NEXT: !18 = distinct !{!18, !"Test02b"} + +; FULL: !0 = !{!"clang"} +; FULL-NEXT: !1 = !{!2} +; FULL-NEXT: !2 = distinct !{!2, !3, !"Test01: unknown function scope"} +; FULL-NEXT: !3 = distinct !{!3, !"Test01"} +; FULL-NEXT: !4 = !{!5} +; FULL-NEXT: !5 = distinct !{!5, !6, !"Test01: %agg.result"} +; FULL-NEXT: !6 = distinct !{!6, !"Test01"} +; FULL-NEXT: !7 = !{!2, !5} +; FULL-NEXT: !8 = !{!9, !2, !5} +; FULL-NEXT: !9 = distinct !{!9, !10, !"Test01: unknown function scope"} +; FULL-NEXT: !10 = distinct !{!10, !"Test01"} +; FULL-NEXT: !11 = !{!12} +; FULL-NEXT: !12 = distinct !{!12, !13, !"Test02b: unknown function scope"} +; FULL-NEXT: !13 = distinct !{!13, !"Test02b"} +; FULL-NEXT: !14 = !{!15} +; FULL-NEXT: !15 = distinct !{!15, !16, !"Test02a: unknown function scope"} +; FULL-NEXT: !16 = distinct !{!16, !"Test02a"} +; FULL-NEXT: !17 = !{!18} +; FULL-NEXT: !18 = distinct !{!18, !19, !"Test02b: %agg.result"} +; FULL-NEXT: !19 = distinct !{!19, !"Test02b"} +; FULL-NEXT: !20 = !{!15, !18} Index: llvm/test/Transforms/Inline/noalias-scopes.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/Inline/noalias-scopes.ll @@ -0,0 +1,218 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -inline -enable-noalias-to-md-conversion -use-noalias-intrinsic-during-inlining=full -S < %s | FileCheck %s + +; verify that inlining result in scope duplication +; verify that llvm.noalias.decl is introduced at the location of the inlining + +target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +; Function Attrs: nofree norecurse nounwind +define dso_local void @copy_npnp(i32* noalias nocapture %dst, i32* noalias nocapture readonly %src) local_unnamed_addr #0 { +; CHECK-LABEL: @copy_npnp( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* [[SRC:%.*]], ptr_provenance i32* undef, align 4, !tbaa [[TBAA2:![0-9]+]] +; CHECK-NEXT: store i32 [[TMP0]], i32* [[DST:%.*]], ptr_provenance i32* undef, align 4, !tbaa [[TBAA2]] +; CHECK-NEXT: ret void +; +entry: + %0 = load i32, i32* %src, ptr_provenance i32* undef, align 4, !tbaa !2 + store i32 %0, i32* %dst, ptr_provenance i32* undef, align 4, !tbaa !2 + ret void +} + +; Function Attrs: nounwind +define dso_local void @copy_rprp(i32* nocapture %dst, i32* nocapture readonly %src) local_unnamed_addr #1 { +; CHECK-LABEL: @copy_rprp( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TMP0:%.*]] = tail call i8* @llvm.noalias.decl.p0i8.p0p0i32.i64(i32** null, i64 0, metadata [[META6:![0-9]+]]) +; CHECK-NEXT: [[TMP1:%.*]] = tail call i8* @llvm.noalias.decl.p0i8.p0p0i32.i64(i32** null, i64 0, metadata [[META9:![0-9]+]]) +; CHECK-NEXT: [[TMP2:%.*]] = tail call i32* @llvm.provenance.noalias.p0i32.p0i8.p0p0i32.p0p0i32.i64(i32* [[SRC:%.*]], i8* [[TMP1]], i32** null, i32** undef, i64 0, metadata [[META9]]), !tbaa [[TBAA11:![0-9]+]], !noalias !13 +; CHECK-NEXT: [[TMP3:%.*]] = load i32, i32* [[SRC]], ptr_provenance i32* [[TMP2]], align 4, !tbaa [[TBAA2]], !noalias !13 +; CHECK-NEXT: [[TMP4:%.*]] = tail call i32* @llvm.provenance.noalias.p0i32.p0i8.p0p0i32.p0p0i32.i64(i32* [[DST:%.*]], i8* [[TMP0]], i32** null, i32** undef, i64 0, metadata [[META6]]), !tbaa [[TBAA11]], !noalias !13 +; CHECK-NEXT: store i32 [[TMP3]], i32* [[DST]], ptr_provenance i32* [[TMP4]], align 4, !tbaa [[TBAA2]], !noalias !13 +; CHECK-NEXT: ret void +; +entry: + %0 = tail call i8* @llvm.noalias.decl.p0i8.p0p0i32.i64(i32** null, i64 0, metadata !6) + %1 = tail call i8* @llvm.noalias.decl.p0i8.p0p0i32.i64(i32** null, i64 0, metadata !9) + %2 = tail call i32* @llvm.provenance.noalias.p0i32.p0i8.p0p0i32.p0p0i32.i64(i32* %src, i8* %1, i32** null, i32** undef, i64 0, metadata !9), !tbaa !11, !noalias !13 + %3 = load i32, i32* %src, ptr_provenance i32* %2, align 4, !tbaa !2, !noalias !13 + %4 = tail call i32* @llvm.provenance.noalias.p0i32.p0i8.p0p0i32.p0p0i32.i64(i32* %dst, i8* %0, i32** null, i32** undef, i64 0, metadata !6), !tbaa !11, !noalias !13 + store i32 %3, i32* %dst, ptr_provenance i32* %4, align 4, !tbaa !2, !noalias !13 + ret void +} + +; Function Attrs: argmemonly nounwind +declare i8* @llvm.noalias.decl.p0i8.p0p0i32.i64(i32**, i64, metadata) #2 + +; Function Attrs: nofree norecurse nounwind +define dso_local void @test_npnp(i32* nocapture %dst, i32* nocapture readonly %src, i32 %n) local_unnamed_addr #0 { +; CHECK-LABEL: @test_npnp( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TMP0:%.*]] = call i8* @llvm.noalias.decl.p0i8.p0p0i32.i64(i32** null, i64 0, metadata [[META14:![0-9]+]]) +; CHECK-NEXT: [[TMP1:%.*]] = call i32* @llvm.noalias.p0i32.p0i8.p0p0i32.i64(i32* [[DST:%.*]], i8* [[TMP0]], i32** null, i64 0, metadata [[META14]]), !noalias !17 +; CHECK-NEXT: [[TMP2:%.*]] = call i8* @llvm.noalias.decl.p0i8.p0p0i32.i64(i32** null, i64 0, metadata [[META19:![0-9]+]]) +; CHECK-NEXT: [[TMP3:%.*]] = call i32* @llvm.noalias.p0i32.p0i8.p0p0i32.i64(i32* [[SRC:%.*]], i8* [[TMP2]], i32** null, i64 0, metadata [[META19]]), !noalias !17 +; CHECK-NEXT: [[TMP4:%.*]] = load i32, i32* [[TMP3]], ptr_provenance i32* undef, align 4, !tbaa [[TBAA2]], !noalias !17 +; CHECK-NEXT: store i32 [[TMP4]], i32* [[TMP1]], ptr_provenance i32* undef, align 4, !tbaa [[TBAA2]], !noalias !17 +; CHECK-NEXT: br label [[DO_BODY:%.*]] +; CHECK: do.body: +; CHECK-NEXT: [[N_ADDR_0:%.*]] = phi i32 [ [[N:%.*]], [[ENTRY:%.*]] ], [ [[DEC:%.*]], [[DO_BODY]] ] +; CHECK-NEXT: [[TMP5:%.*]] = call i8* @llvm.noalias.decl.p0i8.p0p0i32.i64(i32** null, i64 0, metadata [[META20:![0-9]+]]) +; CHECK-NEXT: [[TMP6:%.*]] = call i32* @llvm.noalias.p0i32.p0i8.p0p0i32.i64(i32* [[DST]], i8* [[TMP5]], i32** null, i64 0, metadata [[META20]]), !noalias !23 +; CHECK-NEXT: [[TMP7:%.*]] = call i8* @llvm.noalias.decl.p0i8.p0p0i32.i64(i32** null, i64 0, metadata [[META25:![0-9]+]]) +; CHECK-NEXT: [[TMP8:%.*]] = call i32* @llvm.noalias.p0i32.p0i8.p0p0i32.i64(i32* [[SRC]], i8* [[TMP7]], i32** null, i64 0, metadata [[META25]]), !noalias !23 +; CHECK-NEXT: [[TMP9:%.*]] = load i32, i32* [[TMP8]], ptr_provenance i32* undef, align 4, !tbaa [[TBAA2]], !noalias !23 +; CHECK-NEXT: store i32 [[TMP9]], i32* [[TMP6]], ptr_provenance i32* undef, align 4, !tbaa [[TBAA2]], !noalias !23 +; CHECK-NEXT: [[TMP10:%.*]] = call i8* @llvm.noalias.decl.p0i8.p0p0i32.i64(i32** null, i64 0, metadata [[META26:![0-9]+]]) +; CHECK-NEXT: [[TMP11:%.*]] = call i32* @llvm.noalias.p0i32.p0i8.p0p0i32.i64(i32* [[DST]], i8* [[TMP10]], i32** null, i64 0, metadata [[META26]]), !noalias !29 +; CHECK-NEXT: [[TMP12:%.*]] = call i8* @llvm.noalias.decl.p0i8.p0p0i32.i64(i32** null, i64 0, metadata [[META31:![0-9]+]]) +; CHECK-NEXT: [[TMP13:%.*]] = call i32* @llvm.noalias.p0i32.p0i8.p0p0i32.i64(i32* [[SRC]], i8* [[TMP12]], i32** null, i64 0, metadata [[META31]]), !noalias !29 +; CHECK-NEXT: [[TMP14:%.*]] = load i32, i32* [[TMP13]], ptr_provenance i32* undef, align 4, !tbaa [[TBAA2]], !noalias !29 +; CHECK-NEXT: store i32 [[TMP14]], i32* [[TMP11]], ptr_provenance i32* undef, align 4, !tbaa [[TBAA2]], !noalias !29 +; CHECK-NEXT: [[DEC]] = add nsw i32 [[N_ADDR_0]], -1 +; CHECK-NEXT: [[TOBOOL:%.*]] = icmp eq i32 [[N_ADDR_0]], 0 +; CHECK-NEXT: br i1 [[TOBOOL]], label [[DO_END:%.*]], label [[DO_BODY]] +; CHECK: do.end: +; CHECK-NEXT: ret void +; +entry: + tail call void @copy_npnp(i32* %dst, i32* %src) + br label %do.body + +do.body: ; preds = %do.body, %entry + %n.addr.0 = phi i32 [ %n, %entry ], [ %dec, %do.body ] + tail call void @copy_npnp(i32* %dst, i32* %src) + tail call void @copy_npnp(i32* %dst, i32* %src) + %dec = add nsw i32 %n.addr.0, -1 + %tobool = icmp eq i32 %n.addr.0, 0 + br i1 %tobool, label %do.end, label %do.body + +do.end: ; preds = %do.body + ret void +} + +; Function Attrs: nounwind +define dso_local void @test_rprp(i32* nocapture %dst, i32* nocapture readonly %src, i32 %n) local_unnamed_addr #1 { +; CHECK-LABEL: @test_rprp( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TMP0:%.*]] = tail call i8* @llvm.noalias.decl.p0i8.p0p0i32.i64(i32** null, i64 0, metadata [[META32:![0-9]+]]) #[[ATTR5:[0-9]+]] +; CHECK-NEXT: [[TMP1:%.*]] = tail call i8* @llvm.noalias.decl.p0i8.p0p0i32.i64(i32** null, i64 0, metadata [[META35:![0-9]+]]) #[[ATTR5]] +; CHECK-NEXT: [[TMP2:%.*]] = tail call i32* @llvm.provenance.noalias.p0i32.p0i8.p0p0i32.p0p0i32.i64(i32* [[SRC:%.*]], i8* [[TMP1]], i32** null, i32** undef, i64 0, metadata [[META35]]) #[[ATTR5]], !tbaa [[TBAA11]], !noalias !37 +; CHECK-NEXT: [[TMP3:%.*]] = load i32, i32* [[SRC]], ptr_provenance i32* [[TMP2]], align 4, !tbaa [[TBAA2]], !noalias !37 +; CHECK-NEXT: [[TMP4:%.*]] = tail call i32* @llvm.provenance.noalias.p0i32.p0i8.p0p0i32.p0p0i32.i64(i32* [[DST:%.*]], i8* [[TMP0]], i32** null, i32** undef, i64 0, metadata [[META32]]) #[[ATTR5]], !tbaa [[TBAA11]], !noalias !37 +; CHECK-NEXT: store i32 [[TMP3]], i32* [[DST]], ptr_provenance i32* [[TMP4]], align 4, !tbaa [[TBAA2]], !noalias !37 +; CHECK-NEXT: br label [[DO_BODY:%.*]] +; CHECK: do.body: +; CHECK-NEXT: [[N_ADDR_0:%.*]] = phi i32 [ [[N:%.*]], [[ENTRY:%.*]] ], [ [[DEC:%.*]], [[DO_BODY]] ] +; CHECK-NEXT: [[TMP5:%.*]] = tail call i8* @llvm.noalias.decl.p0i8.p0p0i32.i64(i32** null, i64 0, metadata [[META38:![0-9]+]]) #[[ATTR5]] +; CHECK-NEXT: [[TMP6:%.*]] = tail call i8* @llvm.noalias.decl.p0i8.p0p0i32.i64(i32** null, i64 0, metadata [[META41:![0-9]+]]) #[[ATTR5]] +; CHECK-NEXT: [[TMP7:%.*]] = tail call i32* @llvm.provenance.noalias.p0i32.p0i8.p0p0i32.p0p0i32.i64(i32* [[SRC]], i8* [[TMP6]], i32** null, i32** undef, i64 0, metadata [[META41]]) #[[ATTR5]], !tbaa [[TBAA11]], !noalias !43 +; CHECK-NEXT: [[TMP8:%.*]] = load i32, i32* [[SRC]], ptr_provenance i32* [[TMP7]], align 4, !tbaa [[TBAA2]], !noalias !43 +; CHECK-NEXT: [[TMP9:%.*]] = tail call i32* @llvm.provenance.noalias.p0i32.p0i8.p0p0i32.p0p0i32.i64(i32* [[DST]], i8* [[TMP5]], i32** null, i32** undef, i64 0, metadata [[META38]]) #[[ATTR5]], !tbaa [[TBAA11]], !noalias !43 +; CHECK-NEXT: store i32 [[TMP8]], i32* [[DST]], ptr_provenance i32* [[TMP9]], align 4, !tbaa [[TBAA2]], !noalias !43 +; CHECK-NEXT: [[TMP10:%.*]] = tail call i8* @llvm.noalias.decl.p0i8.p0p0i32.i64(i32** null, i64 0, metadata [[META44:![0-9]+]]) #[[ATTR5]] +; CHECK-NEXT: [[TMP11:%.*]] = tail call i8* @llvm.noalias.decl.p0i8.p0p0i32.i64(i32** null, i64 0, metadata [[META47:![0-9]+]]) #[[ATTR5]] +; CHECK-NEXT: [[TMP12:%.*]] = tail call i32* @llvm.provenance.noalias.p0i32.p0i8.p0p0i32.p0p0i32.i64(i32* [[SRC]], i8* [[TMP11]], i32** null, i32** undef, i64 0, metadata [[META47]]) #[[ATTR5]], !tbaa [[TBAA11]], !noalias !49 +; CHECK-NEXT: [[TMP13:%.*]] = load i32, i32* [[SRC]], ptr_provenance i32* [[TMP12]], align 4, !tbaa [[TBAA2]], !noalias !49 +; CHECK-NEXT: [[TMP14:%.*]] = tail call i32* @llvm.provenance.noalias.p0i32.p0i8.p0p0i32.p0p0i32.i64(i32* [[DST]], i8* [[TMP10]], i32** null, i32** undef, i64 0, metadata [[META44]]) #[[ATTR5]], !tbaa [[TBAA11]], !noalias !49 +; CHECK-NEXT: store i32 [[TMP13]], i32* [[DST]], ptr_provenance i32* [[TMP14]], align 4, !tbaa [[TBAA2]], !noalias !49 +; CHECK-NEXT: [[DEC]] = add nsw i32 [[N_ADDR_0]], -1 +; CHECK-NEXT: [[TOBOOL:%.*]] = icmp eq i32 [[N_ADDR_0]], 0 +; CHECK-NEXT: br i1 [[TOBOOL]], label [[DO_END:%.*]], label [[DO_BODY]] +; CHECK: do.end: +; CHECK-NEXT: ret void +; +entry: + tail call void @copy_rprp(i32* %dst, i32* %src) + br label %do.body + +do.body: ; preds = %do.body, %entry + %n.addr.0 = phi i32 [ %n, %entry ], [ %dec, %do.body ] + tail call void @copy_rprp(i32* %dst, i32* %src) + tail call void @copy_rprp(i32* %dst, i32* %src) + %dec = add nsw i32 %n.addr.0, -1 + %tobool = icmp eq i32 %n.addr.0, 0 + br i1 %tobool, label %do.end, label %do.body + +do.end: ; preds = %do.body + ret void +} + +; Function Attrs: nounwind readnone speculatable +declare i32* @llvm.provenance.noalias.p0i32.p0i8.p0p0i32.p0p0i32.i64(i32*, i8*, i32**, i32**, i64, metadata) #3 + +attributes #0 = { nofree norecurse nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #2 = { argmemonly nounwind } +attributes #3 = { nounwind readnone speculatable } + +!llvm.module.flags = !{!0} +!llvm.ident = !{!1} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{!"clang"} +!2 = !{!3, !3, i64 0, i64 4} +!3 = !{!4, i64 4, !"int"} +!4 = !{!5, i64 1, !"omnipotent char"} +!5 = !{!"Simple C/C++ TBAA"} +!6 = !{!7} +!7 = distinct !{!7, !8, !"copy_rprp: rdst"} +!8 = distinct !{!8, !"copy_rprp"} +!9 = !{!10} +!10 = distinct !{!10, !8, !"copy_rprp: rsrc"} +!11 = !{!12, !12, i64 0, i64 4} +!12 = !{!4, i64 4, !"any pointer"} +!13 = !{!7, !10} + +; CHECK: !0 = !{i32 1, !"wchar_size", i32 4} +; CHECK-NEXT: !1 = !{!"clang"} +; CHECK-NEXT: !2 = !{!3, !3, i64 0, i64 4} +; CHECK-NEXT: !3 = !{!4, i64 4, !"int"} +; CHECK-NEXT: !4 = !{!5, i64 1, !"omnipotent char"} +; CHECK-NEXT: !5 = !{!"Simple C/C++ TBAA"} +; CHECK-NEXT: !6 = !{!7} +; CHECK-NEXT: !7 = distinct !{!7, !8, !"copy_rprp: rdst"} +; CHECK-NEXT: !8 = distinct !{!8, !"copy_rprp"} +; CHECK-NEXT: !9 = !{!10} +; CHECK-NEXT: !10 = distinct !{!10, !8, !"copy_rprp: rsrc"} +; CHECK-NEXT: !11 = !{!12, !12, i64 0, i64 4} +; CHECK-NEXT: !12 = !{!4, i64 4, !"any pointer"} +; CHECK-NEXT: !13 = !{!7, !10} +; CHECK-NEXT: !14 = !{!15} +; CHECK-NEXT: !15 = distinct !{!15, !16, !"copy_npnp: %dst"} +; CHECK-NEXT: !16 = distinct !{!16, !"copy_npnp"} +; CHECK-NEXT: !17 = !{!15, !18} +; CHECK-NEXT: !18 = distinct !{!18, !16, !"copy_npnp: %src"} +; CHECK-NEXT: !19 = !{!18} +; CHECK-NEXT: !20 = !{!21} +; CHECK-NEXT: !21 = distinct !{!21, !22, !"copy_npnp: %dst"} +; CHECK-NEXT: !22 = distinct !{!22, !"copy_npnp"} +; CHECK-NEXT: !23 = !{!21, !24} +; CHECK-NEXT: !24 = distinct !{!24, !22, !"copy_npnp: %src"} +; CHECK-NEXT: !25 = !{!24} +; CHECK-NEXT: !26 = !{!27} +; CHECK-NEXT: !27 = distinct !{!27, !28, !"copy_npnp: %dst"} +; CHECK-NEXT: !28 = distinct !{!28, !"copy_npnp"} +; CHECK-NEXT: !29 = !{!27, !30} +; CHECK-NEXT: !30 = distinct !{!30, !28, !"copy_npnp: %src"} +; CHECK-NEXT: !31 = !{!30} +; CHECK-NEXT: !32 = !{!33} +; CHECK-NEXT: !33 = distinct !{!33, !34, !"copy_rprp: rdst"} +; CHECK-NEXT: !34 = distinct !{!34, !"copy_rprp"} +; CHECK-NEXT: !35 = !{!36} +; CHECK-NEXT: !36 = distinct !{!36, !34, !"copy_rprp: rsrc"} +; CHECK-NEXT: !37 = !{!33, !36} +; CHECK-NEXT: !38 = !{!39} +; CHECK-NEXT: !39 = distinct !{!39, !40, !"copy_rprp: rdst"} +; CHECK-NEXT: !40 = distinct !{!40, !"copy_rprp"} +; CHECK-NEXT: !41 = !{!42} +; CHECK-NEXT: !42 = distinct !{!42, !40, !"copy_rprp: rsrc"} +; CHECK-NEXT: !43 = !{!39, !42} +; CHECK-NEXT: !44 = !{!45} +; CHECK-NEXT: !45 = distinct !{!45, !46, !"copy_rprp: rdst"} +; CHECK-NEXT: !46 = distinct !{!46, !"copy_rprp"} +; CHECK-NEXT: !47 = !{!48} +; CHECK-NEXT: !48 = distinct !{!48, !46, !"copy_rprp: rsrc"} +; CHECK-NEXT: !49 = !{!45, !48} Index: llvm/test/Transforms/Inline/noalias.ll =================================================================== --- llvm/test/Transforms/Inline/noalias.ll +++ llvm/test/Transforms/Inline/noalias.ll @@ -1,8 +1,18 @@ -; RUN: opt -inline -enable-noalias-to-md-conversion -S < %s | FileCheck %s +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -inline -enable-noalias-to-md-conversion -use-noalias-intrinsic-during-inlining=none -S < %s | FileCheck %s -check-prefixes=CHECK,NONE +; RUN: opt -inline -enable-noalias-to-md-conversion -use-noalias-intrinsic-during-inlining=scopes -S < %s | FileCheck %s -check-prefixes=CHECK,SCOPES +; RUN: opt -inline -enable-noalias-to-md-conversion -use-noalias-intrinsic-during-inlining=full -S < %s | FileCheck %s -check-prefixes=CHECK,FULL target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128" target triple = "x86_64-unknown-linux-gnu" define void @hello(float* noalias nocapture %a, float* nocapture readonly %c) #0 { +; CHECK-LABEL: @hello( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TMP0:%.*]] = load float, float* [[C:%.*]], align 4 +; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds float, float* [[A:%.*]], i64 5 +; CHECK-NEXT: store float [[TMP0]], float* [[ARRAYIDX]], align 4 +; CHECK-NEXT: ret void +; entry: %0 = load float, float* %c, align 4 %arrayidx = getelementptr inbounds float, float* %a, i64 5 @@ -11,6 +21,39 @@ } define void @foo(float* nocapture %a, float* nocapture readonly %c) #0 { +; NONE-LABEL: @foo( +; NONE-NEXT: entry: +; NONE-NEXT: [[TMP0:%.*]] = load float, float* [[C:%.*]], align 4 +; NONE-NEXT: [[ARRAYIDX_I:%.*]] = getelementptr inbounds float, float* [[A:%.*]], i64 5 +; NONE-NEXT: store float [[TMP0]], float* [[ARRAYIDX_I]], align 4 +; NONE-NEXT: [[TMP1:%.*]] = load float, float* [[C]], align 4 +; NONE-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds float, float* [[A]], i64 7 +; NONE-NEXT: store float [[TMP1]], float* [[ARRAYIDX]], align 4 +; NONE-NEXT: ret void +; +; SCOPES-LABEL: @foo( +; SCOPES-NEXT: entry: +; SCOPES-NEXT: call void @llvm.experimental.noalias.scope.decl(metadata [[META0:![0-9]+]]) +; SCOPES-NEXT: [[TMP0:%.*]] = load float, float* [[C:%.*]], align 4, !noalias !0 +; SCOPES-NEXT: [[ARRAYIDX_I:%.*]] = getelementptr inbounds float, float* [[A:%.*]], i64 5 +; SCOPES-NEXT: store float [[TMP0]], float* [[ARRAYIDX_I]], align 4, !alias.scope !0 +; SCOPES-NEXT: [[TMP1:%.*]] = load float, float* [[C]], align 4 +; SCOPES-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds float, float* [[A]], i64 7 +; SCOPES-NEXT: store float [[TMP1]], float* [[ARRAYIDX]], align 4 +; SCOPES-NEXT: ret void +; +; FULL-LABEL: @foo( +; FULL-NEXT: entry: +; FULL-NEXT: [[TMP0:%.*]] = call i8* @llvm.noalias.decl.p0i8.p0p0f32.i64(float** null, i64 0, metadata [[META0:![0-9]+]]) +; FULL-NEXT: [[TMP1:%.*]] = call float* @llvm.noalias.p0f32.p0i8.p0p0f32.i64(float* [[A:%.*]], i8* [[TMP0]], float** null, i64 0, metadata [[META0]]), !noalias !0 +; FULL-NEXT: [[TMP2:%.*]] = load float, float* [[C:%.*]], align 4, !noalias !0 +; FULL-NEXT: [[ARRAYIDX_I:%.*]] = getelementptr inbounds float, float* [[TMP1]], i64 5 +; FULL-NEXT: store float [[TMP2]], float* [[ARRAYIDX_I]], align 4, !noalias !0 +; FULL-NEXT: [[TMP3:%.*]] = load float, float* [[C]], align 4 +; FULL-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds float, float* [[A]], i64 7 +; FULL-NEXT: store float [[TMP3]], float* [[ARRAYIDX]], align 4 +; FULL-NEXT: ret void +; entry: tail call void @hello(float* %a, float* %c) %0 = load float, float* %c, align 4 @@ -19,19 +62,16 @@ ret void } -; CHECK-LABEL: define void @foo(float* nocapture %a, float* nocapture readonly %c) #0 { -; CHECK: entry: -; CHECK: call void @llvm.experimental.noalias.scope.decl -; CHECK: [[TMP0:%.+]] = load float, float* %c, align 4, !noalias !0 -; CHECK: %arrayidx.i = getelementptr inbounds float, float* %a, i64 5 -; CHECK: store float [[TMP0]], float* %arrayidx.i, align 4, !alias.scope !0 -; CHECK: [[TMP1:%.+]] = load float, float* %c, align 4 -; CHECK: %arrayidx = getelementptr inbounds float, float* %a, i64 7 -; CHECK: store float [[TMP1]], float* %arrayidx, align 4 -; CHECK: ret void -; CHECK: } - define void @hello2(float* noalias nocapture %a, float* noalias nocapture %b, float* nocapture readonly %c) #0 { +; CHECK-LABEL: @hello2( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TMP0:%.*]] = load float, float* [[C:%.*]], align 4 +; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds float, float* [[A:%.*]], i64 5 +; CHECK-NEXT: store float [[TMP0]], float* [[ARRAYIDX]], align 4 +; CHECK-NEXT: [[ARRAYIDX1:%.*]] = getelementptr inbounds float, float* [[B:%.*]], i64 8 +; CHECK-NEXT: store float [[TMP0]], float* [[ARRAYIDX1]], align 4 +; CHECK-NEXT: ret void +; entry: %0 = load float, float* %c, align 4 %arrayidx = getelementptr inbounds float, float* %a, i64 5 @@ -42,6 +82,48 @@ } define void @foo2(float* nocapture %a, float* nocapture %b, float* nocapture readonly %c) #0 { +; NONE-LABEL: @foo2( +; NONE-NEXT: entry: +; NONE-NEXT: [[TMP0:%.*]] = load float, float* [[C:%.*]], align 4 +; NONE-NEXT: [[ARRAYIDX_I:%.*]] = getelementptr inbounds float, float* [[A:%.*]], i64 5 +; NONE-NEXT: store float [[TMP0]], float* [[ARRAYIDX_I]], align 4 +; NONE-NEXT: [[ARRAYIDX1_I:%.*]] = getelementptr inbounds float, float* [[B:%.*]], i64 8 +; NONE-NEXT: store float [[TMP0]], float* [[ARRAYIDX1_I]], align 4 +; NONE-NEXT: [[TMP1:%.*]] = load float, float* [[C]], align 4 +; NONE-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds float, float* [[A]], i64 7 +; NONE-NEXT: store float [[TMP1]], float* [[ARRAYIDX]], align 4 +; NONE-NEXT: ret void +; +; SCOPES-LABEL: @foo2( +; SCOPES-NEXT: entry: +; SCOPES-NEXT: call void @llvm.experimental.noalias.scope.decl(metadata [[META3:![0-9]+]]) +; SCOPES-NEXT: call void @llvm.experimental.noalias.scope.decl(metadata [[META6:![0-9]+]]) +; SCOPES-NEXT: [[TMP0:%.*]] = load float, float* [[C:%.*]], align 4, !noalias !8 +; SCOPES-NEXT: [[ARRAYIDX_I:%.*]] = getelementptr inbounds float, float* [[A:%.*]], i64 5 +; SCOPES-NEXT: store float [[TMP0]], float* [[ARRAYIDX_I]], align 4, !alias.scope !3, !noalias !6 +; SCOPES-NEXT: [[ARRAYIDX1_I:%.*]] = getelementptr inbounds float, float* [[B:%.*]], i64 8 +; SCOPES-NEXT: store float [[TMP0]], float* [[ARRAYIDX1_I]], align 4, !alias.scope !6, !noalias !3 +; SCOPES-NEXT: [[TMP1:%.*]] = load float, float* [[C]], align 4 +; SCOPES-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds float, float* [[A]], i64 7 +; SCOPES-NEXT: store float [[TMP1]], float* [[ARRAYIDX]], align 4 +; SCOPES-NEXT: ret void +; +; FULL-LABEL: @foo2( +; FULL-NEXT: entry: +; FULL-NEXT: [[TMP0:%.*]] = call i8* @llvm.noalias.decl.p0i8.p0p0f32.i64(float** null, i64 0, metadata [[META3:![0-9]+]]) +; FULL-NEXT: [[TMP1:%.*]] = call float* @llvm.noalias.p0f32.p0i8.p0p0f32.i64(float* [[A:%.*]], i8* [[TMP0]], float** null, i64 0, metadata [[META3]]), !noalias !6 +; FULL-NEXT: [[TMP2:%.*]] = call i8* @llvm.noalias.decl.p0i8.p0p0f32.i64(float** null, i64 0, metadata [[META8:![0-9]+]]) +; FULL-NEXT: [[TMP3:%.*]] = call float* @llvm.noalias.p0f32.p0i8.p0p0f32.i64(float* [[B:%.*]], i8* [[TMP2]], float** null, i64 0, metadata [[META8]]), !noalias !6 +; FULL-NEXT: [[TMP4:%.*]] = load float, float* [[C:%.*]], align 4, !noalias !6 +; FULL-NEXT: [[ARRAYIDX_I:%.*]] = getelementptr inbounds float, float* [[TMP1]], i64 5 +; FULL-NEXT: store float [[TMP4]], float* [[ARRAYIDX_I]], align 4, !noalias !6 +; FULL-NEXT: [[ARRAYIDX1_I:%.*]] = getelementptr inbounds float, float* [[TMP3]], i64 8 +; FULL-NEXT: store float [[TMP4]], float* [[ARRAYIDX1_I]], align 4, !noalias !6 +; FULL-NEXT: [[TMP5:%.*]] = load float, float* [[C]], align 4 +; FULL-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds float, float* [[A]], i64 7 +; FULL-NEXT: store float [[TMP5]], float* [[ARRAYIDX]], align 4 +; FULL-NEXT: ret void +; entry: tail call void @hello2(float* %a, float* %b, float* %c) %0 = load float, float* %c, align 4 @@ -50,29 +132,27 @@ ret void } -; CHECK-LABEL: define void @foo2(float* nocapture %a, float* nocapture %b, float* nocapture readonly %c) #0 { -; CHECK: entry: -; CHECK: call void @llvm.experimental.noalias.scope.decl(metadata !3) -; CHECK: call void @llvm.experimental.noalias.scope.decl(metadata !6) -; CHECK: [[TMP0:%.+]] = load float, float* %c, align 4, !noalias !8 -; CHECK: %arrayidx.i = getelementptr inbounds float, float* %a, i64 5 -; CHECK: store float [[TMP0]], float* %arrayidx.i, align 4, !alias.scope !3, !noalias !6 -; CHECK: %arrayidx1.i = getelementptr inbounds float, float* %b, i64 8 -; CHECK: store float [[TMP0]], float* %arrayidx1.i, align 4, !alias.scope !6, !noalias !3 -; CHECK: [[TMP1:%.+]] = load float, float* %c, align 4 -; CHECK: %arrayidx = getelementptr inbounds float, float* %a, i64 7 -; CHECK: store float [[TMP1]], float* %arrayidx, align 4 -; CHECK: ret void -; CHECK: } - attributes #0 = { nounwind uwtable } +attributes #1 = { argmemonly nounwind } + +; NONE-NOT: !0 = + +; SCOPES: !0 = !{!1} +; SCOPES-NEXT: !1 = distinct !{!1, !2, !"hello: %a"} +; SCOPES-NEXT: !2 = distinct !{!2, !"hello"} +; SCOPES-NEXT: !3 = !{!4} +; SCOPES-NEXT: !4 = distinct !{!4, !5, !"hello2: %a"} +; SCOPES-NEXT: !5 = distinct !{!5, !"hello2"} +; SCOPES-NEXT: !6 = !{!7} +; SCOPES-NEXT: !7 = distinct !{!7, !5, !"hello2: %b"} +; SCOPES-NEXT: !8 = !{!4, !7} -; CHECK: !0 = !{!1} -; CHECK: !1 = distinct !{!1, !2, !"hello: %a"} -; CHECK: !2 = distinct !{!2, !"hello"} -; CHECK: !3 = !{!4} -; CHECK: !4 = distinct !{!4, !5, !"hello2: %a"} -; CHECK: !5 = distinct !{!5, !"hello2"} -; CHECK: !6 = !{!7} -; CHECK: !7 = distinct !{!7, !5, !"hello2: %b"} -; CHECK: !8 = !{!4, !7} +; FULL: !0 = !{!1} +; FULL-NEXT: !1 = distinct !{!1, !2, !"hello: %a"} +; FULL-NEXT: !2 = distinct !{!2, !"hello"} +; FULL-NEXT: !3 = !{!4} +; FULL-NEXT: !4 = distinct !{!4, !5, !"hello2: %a"} +; FULL-NEXT: !5 = distinct !{!5, !"hello2"} +; FULL-NEXT: !6 = !{!4, !7} +; FULL-NEXT: !7 = distinct !{!7, !5, !"hello2: %b"} +; FULL-NEXT: !8 = !{!7} Index: llvm/test/Transforms/Inline/noalias2.ll =================================================================== --- llvm/test/Transforms/Inline/noalias2.ll +++ llvm/test/Transforms/Inline/noalias2.ll @@ -1,13 +1,17 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature -; RUN: opt -inline -enable-noalias-to-md-conversion -S < %s | FileCheck %s -; RUN: opt -inline -enable-noalias-to-md-conversion --enable-knowledge-retention -S < %s | FileCheck %s +; RUN: opt -inline -enable-noalias-to-md-conversion -use-noalias-intrinsic-during-inlining=none -S < %s | FileCheck %s --check-prefixes=CHECK,NONE +; RUN: opt -inline -enable-noalias-to-md-conversion -use-noalias-intrinsic-during-inlining=none --enable-knowledge-retention -S < %s | FileCheck %s --check-prefixes=CHECK,NONE +; RUN: opt -inline -enable-noalias-to-md-conversion -use-noalias-intrinsic-during-inlining=scopes -S < %s | FileCheck %s -check-prefixes=CHECK,SCOPES +; RUN: opt -inline -enable-noalias-to-md-conversion -use-noalias-intrinsic-during-inlining=scopes --enable-knowledge-retention -S < %s | FileCheck %s --check-prefixes=CHECK,SCOPES +; RUN: opt -inline -enable-noalias-to-md-conversion -use-noalias-intrinsic-during-inlining=full -S < %s | FileCheck %s -check-prefixes=CHECK,FULL +; RUN: opt -inline -enable-noalias-to-md-conversion -use-noalias-intrinsic-during-inlining=full --enable-knowledge-retention -S < %s | FileCheck %s --check-prefixes=CHECK,FULL target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128" target triple = "x86_64-unknown-linux-gnu" define void @hello(float* noalias nocapture %a, float* noalias nocapture readonly %c) #0 { ; CHECK-LABEL: define {{[^@]+}}@hello -; CHECK-SAME: (float* noalias nocapture [[A:%.*]], float* noalias nocapture readonly [[C:%.*]]) [[ATTR0:#.*]] { +; CHECK-SAME: (float* noalias nocapture [[A:%.*]], float* noalias nocapture readonly [[C:%.*]]) #[[ATTR0:[0-9]+]] { ; CHECK-NEXT: entry: ; CHECK-NEXT: [[TMP0:%.*]] = load float, float* [[C]], align 4 ; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds float, float* [[A]], i64 5 @@ -22,18 +26,44 @@ } define void @foo(float* noalias nocapture %a, float* noalias nocapture readonly %c) #0 { -; CHECK-LABEL: define {{[^@]+}}@foo -; CHECK-SAME: (float* noalias nocapture [[A:%.*]], float* noalias nocapture readonly [[C:%.*]]) [[ATTR0]] { -; CHECK-NEXT: entry: -; CHECK-NEXT: call void @llvm.experimental.noalias.scope.decl(metadata !0) -; CHECK-NEXT: call void @llvm.experimental.noalias.scope.decl(metadata !3) -; CHECK-NEXT: [[TMP0:%.*]] = load float, float* [[C]], align 4, !alias.scope !3, !noalias !0 -; CHECK-NEXT: [[ARRAYIDX_I:%.*]] = getelementptr inbounds float, float* [[A]], i64 5 -; CHECK-NEXT: store float [[TMP0]], float* [[ARRAYIDX_I]], align 4, !alias.scope !0, !noalias !3 -; CHECK-NEXT: [[TMP1:%.*]] = load float, float* [[C]], align 4 -; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds float, float* [[A]], i64 7 -; CHECK-NEXT: store float [[TMP1]], float* [[ARRAYIDX]], align 4 -; CHECK-NEXT: ret void +; NONE-LABEL: define {{[^@]+}}@foo +; NONE-SAME: (float* noalias nocapture [[A:%.*]], float* noalias nocapture readonly [[C:%.*]]) #[[ATTR0]] { +; NONE-NEXT: entry: +; NONE-NEXT: [[TMP0:%.*]] = load float, float* [[C]], align 4 +; NONE-NEXT: [[ARRAYIDX_I:%.*]] = getelementptr inbounds float, float* [[A]], i64 5 +; NONE-NEXT: store float [[TMP0]], float* [[ARRAYIDX_I]], align 4 +; NONE-NEXT: [[TMP1:%.*]] = load float, float* [[C]], align 4 +; NONE-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds float, float* [[A]], i64 7 +; NONE-NEXT: store float [[TMP1]], float* [[ARRAYIDX]], align 4 +; NONE-NEXT: ret void +; +; SCOPES-LABEL: define {{[^@]+}}@foo +; SCOPES-SAME: (float* noalias nocapture [[A:%.*]], float* noalias nocapture readonly [[C:%.*]]) #[[ATTR0]] { +; SCOPES-NEXT: entry: +; SCOPES-NEXT: call void @llvm.experimental.noalias.scope.decl(metadata [[META0:![0-9]+]]) +; SCOPES-NEXT: call void @llvm.experimental.noalias.scope.decl(metadata [[META3:![0-9]+]]) +; SCOPES-NEXT: [[TMP0:%.*]] = load float, float* [[C]], align 4, !alias.scope !3, !noalias !0 +; SCOPES-NEXT: [[ARRAYIDX_I:%.*]] = getelementptr inbounds float, float* [[A]], i64 5 +; SCOPES-NEXT: store float [[TMP0]], float* [[ARRAYIDX_I]], align 4, !alias.scope !0, !noalias !3 +; SCOPES-NEXT: [[TMP1:%.*]] = load float, float* [[C]], align 4 +; SCOPES-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds float, float* [[A]], i64 7 +; SCOPES-NEXT: store float [[TMP1]], float* [[ARRAYIDX]], align 4 +; SCOPES-NEXT: ret void +; +; FULL-LABEL: define {{[^@]+}}@foo +; FULL-SAME: (float* noalias nocapture [[A:%.*]], float* noalias nocapture readonly [[C:%.*]]) #[[ATTR0]] { +; FULL-NEXT: entry: +; FULL-NEXT: [[TMP0:%.*]] = call i8* @llvm.noalias.decl.p0i8.p0p0f32.i64(float** null, i64 0, metadata [[META0:![0-9]+]]) +; FULL-NEXT: [[TMP1:%.*]] = call float* @llvm.noalias.p0f32.p0i8.p0p0f32.i64(float* [[A]], i8* [[TMP0]], float** null, i64 0, metadata [[META0]]), !noalias !3 +; FULL-NEXT: [[TMP2:%.*]] = call i8* @llvm.noalias.decl.p0i8.p0p0f32.i64(float** null, i64 0, metadata [[META5:![0-9]+]]) +; FULL-NEXT: [[TMP3:%.*]] = call float* @llvm.noalias.p0f32.p0i8.p0p0f32.i64(float* [[C]], i8* [[TMP2]], float** null, i64 0, metadata [[META5]]), !noalias !3 +; FULL-NEXT: [[TMP4:%.*]] = load float, float* [[TMP3]], align 4, !noalias !3 +; FULL-NEXT: [[ARRAYIDX_I:%.*]] = getelementptr inbounds float, float* [[TMP1]], i64 5 +; FULL-NEXT: store float [[TMP4]], float* [[ARRAYIDX_I]], align 4, !noalias !3 +; FULL-NEXT: [[TMP5:%.*]] = load float, float* [[C]], align 4 +; FULL-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds float, float* [[A]], i64 7 +; FULL-NEXT: store float [[TMP5]], float* [[ARRAYIDX]], align 4 +; FULL-NEXT: ret void ; entry: tail call void @hello(float* %a, float* %c) @@ -45,7 +75,7 @@ define void @hello2(float* noalias nocapture %a, float* noalias nocapture %b, float* nocapture readonly %c) #0 { ; CHECK-LABEL: define {{[^@]+}}@hello2 -; CHECK-SAME: (float* noalias nocapture [[A:%.*]], float* noalias nocapture [[B:%.*]], float* nocapture readonly [[C:%.*]]) [[ATTR0]] { +; CHECK-SAME: (float* noalias nocapture [[A:%.*]], float* noalias nocapture [[B:%.*]], float* nocapture readonly [[C:%.*]]) #[[ATTR0]] { ; CHECK-NEXT: entry: ; CHECK-NEXT: [[TMP0:%.*]] = load float, float* [[C]], align 4 ; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds float, float* [[A]], i64 6 @@ -66,30 +96,80 @@ ; Check that when hello() is inlined into foo(), and then foo() is inlined into ; foo2(), the noalias scopes are properly concatenated. define void @foo2(float* nocapture %a, float* nocapture %b, float* nocapture readonly %c) #0 { -; CHECK-LABEL: define {{[^@]+}}@foo2 -; CHECK-SAME: (float* nocapture [[A:%.*]], float* nocapture [[B:%.*]], float* nocapture readonly [[C:%.*]]) [[ATTR0]] { -; CHECK-NEXT: entry: -; CHECK-NEXT: call void @llvm.experimental.noalias.scope.decl(metadata !5) -; CHECK-NEXT: call void @llvm.experimental.noalias.scope.decl(metadata !8) -; CHECK-NEXT: call void @llvm.experimental.noalias.scope.decl(metadata !10) [[ATTR2:#.*]] -; CHECK-NEXT: call void @llvm.experimental.noalias.scope.decl(metadata !13) [[ATTR2]] -; CHECK-NEXT: [[TMP0:%.*]] = load float, float* [[C]], align 4, !alias.scope !15, !noalias !16 -; CHECK-NEXT: [[ARRAYIDX_I_I:%.*]] = getelementptr inbounds float, float* [[A]], i64 5 -; CHECK-NEXT: store float [[TMP0]], float* [[ARRAYIDX_I_I]], align 4, !alias.scope !16, !noalias !15 -; CHECK-NEXT: [[TMP1:%.*]] = load float, float* [[C]], align 4, !alias.scope !8, !noalias !5 -; CHECK-NEXT: [[ARRAYIDX_I:%.*]] = getelementptr inbounds float, float* [[A]], i64 7 -; CHECK-NEXT: store float [[TMP1]], float* [[ARRAYIDX_I]], align 4, !alias.scope !5, !noalias !8 -; CHECK-NEXT: call void @llvm.experimental.noalias.scope.decl(metadata !17) -; CHECK-NEXT: call void @llvm.experimental.noalias.scope.decl(metadata !20) -; CHECK-NEXT: [[TMP2:%.*]] = load float, float* [[C]], align 4, !noalias !22 -; CHECK-NEXT: [[ARRAYIDX_I1:%.*]] = getelementptr inbounds float, float* [[A]], i64 6 -; CHECK-NEXT: store float [[TMP2]], float* [[ARRAYIDX_I1]], align 4, !alias.scope !17, !noalias !20 -; CHECK-NEXT: [[ARRAYIDX1_I:%.*]] = getelementptr inbounds float, float* [[B]], i64 8 -; CHECK-NEXT: store float [[TMP2]], float* [[ARRAYIDX1_I]], align 4, !alias.scope !20, !noalias !17 -; CHECK-NEXT: [[TMP3:%.*]] = load float, float* [[C]], align 4 -; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds float, float* [[A]], i64 7 -; CHECK-NEXT: store float [[TMP3]], float* [[ARRAYIDX]], align 4 -; CHECK-NEXT: ret void +; NONE-LABEL: define {{[^@]+}}@foo2 +; NONE-SAME: (float* nocapture [[A:%.*]], float* nocapture [[B:%.*]], float* nocapture readonly [[C:%.*]]) #[[ATTR0]] { +; NONE-NEXT: entry: +; NONE-NEXT: [[TMP0:%.*]] = load float, float* [[C]], align 4 +; NONE-NEXT: [[ARRAYIDX_I_I:%.*]] = getelementptr inbounds float, float* [[A]], i64 5 +; NONE-NEXT: store float [[TMP0]], float* [[ARRAYIDX_I_I]], align 4 +; NONE-NEXT: [[TMP1:%.*]] = load float, float* [[C]], align 4 +; NONE-NEXT: [[ARRAYIDX_I:%.*]] = getelementptr inbounds float, float* [[A]], i64 7 +; NONE-NEXT: store float [[TMP1]], float* [[ARRAYIDX_I]], align 4 +; NONE-NEXT: [[TMP2:%.*]] = load float, float* [[C]], align 4 +; NONE-NEXT: [[ARRAYIDX_I1:%.*]] = getelementptr inbounds float, float* [[A]], i64 6 +; NONE-NEXT: store float [[TMP2]], float* [[ARRAYIDX_I1]], align 4 +; NONE-NEXT: [[ARRAYIDX1_I:%.*]] = getelementptr inbounds float, float* [[B]], i64 8 +; NONE-NEXT: store float [[TMP2]], float* [[ARRAYIDX1_I]], align 4 +; NONE-NEXT: [[TMP3:%.*]] = load float, float* [[C]], align 4 +; NONE-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds float, float* [[A]], i64 7 +; NONE-NEXT: store float [[TMP3]], float* [[ARRAYIDX]], align 4 +; NONE-NEXT: ret void +; +; SCOPES-LABEL: define {{[^@]+}}@foo2 +; SCOPES-SAME: (float* nocapture [[A:%.*]], float* nocapture [[B:%.*]], float* nocapture readonly [[C:%.*]]) #[[ATTR0]] { +; SCOPES-NEXT: entry: +; SCOPES-NEXT: call void @llvm.experimental.noalias.scope.decl(metadata [[META5:![0-9]+]]) +; SCOPES-NEXT: call void @llvm.experimental.noalias.scope.decl(metadata [[META8:![0-9]+]]) +; SCOPES-NEXT: call void @llvm.experimental.noalias.scope.decl(metadata [[META10:![0-9]+]]) #[[ATTR2:[0-9]+]] +; SCOPES-NEXT: call void @llvm.experimental.noalias.scope.decl(metadata [[META13:![0-9]+]]) #[[ATTR2]] +; SCOPES-NEXT: [[TMP0:%.*]] = load float, float* [[C]], align 4, !alias.scope !15, !noalias !16 +; SCOPES-NEXT: [[ARRAYIDX_I_I:%.*]] = getelementptr inbounds float, float* [[A]], i64 5 +; SCOPES-NEXT: store float [[TMP0]], float* [[ARRAYIDX_I_I]], align 4, !alias.scope !16, !noalias !15 +; SCOPES-NEXT: [[TMP1:%.*]] = load float, float* [[C]], align 4, !alias.scope !8, !noalias !5 +; SCOPES-NEXT: [[ARRAYIDX_I:%.*]] = getelementptr inbounds float, float* [[A]], i64 7 +; SCOPES-NEXT: store float [[TMP1]], float* [[ARRAYIDX_I]], align 4, !alias.scope !5, !noalias !8 +; SCOPES-NEXT: call void @llvm.experimental.noalias.scope.decl(metadata [[META17:![0-9]+]]) +; SCOPES-NEXT: call void @llvm.experimental.noalias.scope.decl(metadata [[META20:![0-9]+]]) +; SCOPES-NEXT: [[TMP2:%.*]] = load float, float* [[C]], align 4, !noalias !22 +; SCOPES-NEXT: [[ARRAYIDX_I1:%.*]] = getelementptr inbounds float, float* [[A]], i64 6 +; SCOPES-NEXT: store float [[TMP2]], float* [[ARRAYIDX_I1]], align 4, !alias.scope !17, !noalias !20 +; SCOPES-NEXT: [[ARRAYIDX1_I:%.*]] = getelementptr inbounds float, float* [[B]], i64 8 +; SCOPES-NEXT: store float [[TMP2]], float* [[ARRAYIDX1_I]], align 4, !alias.scope !20, !noalias !17 +; SCOPES-NEXT: [[TMP3:%.*]] = load float, float* [[C]], align 4 +; SCOPES-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds float, float* [[A]], i64 7 +; SCOPES-NEXT: store float [[TMP3]], float* [[ARRAYIDX]], align 4 +; SCOPES-NEXT: ret void +; +; FULL-LABEL: define {{[^@]+}}@foo2 +; FULL-SAME: (float* nocapture [[A:%.*]], float* nocapture [[B:%.*]], float* nocapture readonly [[C:%.*]]) #[[ATTR0]] { +; FULL-NEXT: entry: +; FULL-NEXT: [[TMP0:%.*]] = call i8* @llvm.noalias.decl.p0i8.p0p0f32.i64(float** null, i64 0, metadata [[META6:![0-9]+]]) +; FULL-NEXT: [[TMP1:%.*]] = call float* @llvm.noalias.p0f32.p0i8.p0p0f32.i64(float* [[A]], i8* [[TMP0]], float** null, i64 0, metadata [[META6]]), !noalias !9 +; FULL-NEXT: [[TMP2:%.*]] = call i8* @llvm.noalias.decl.p0i8.p0p0f32.i64(float** null, i64 0, metadata [[META11:![0-9]+]]) +; FULL-NEXT: [[TMP3:%.*]] = call float* @llvm.noalias.p0f32.p0i8.p0p0f32.i64(float* [[C]], i8* [[TMP2]], float** null, i64 0, metadata [[META11]]), !noalias !9 +; FULL-NEXT: [[TMP4:%.*]] = call i8* @llvm.noalias.decl.p0i8.p0p0f32.i64(float** null, i64 0, metadata [[META12:![0-9]+]]) #[[ATTR3:[0-9]+]], !noalias !9 +; FULL-NEXT: [[TMP5:%.*]] = call float* @llvm.noalias.p0f32.p0i8.p0p0f32.i64(float* [[TMP1]], i8* [[TMP4]], float** null, i64 0, metadata [[META12]]) #[[ATTR3]], !noalias !15 +; FULL-NEXT: [[TMP6:%.*]] = call i8* @llvm.noalias.decl.p0i8.p0p0f32.i64(float** null, i64 0, metadata [[META17:![0-9]+]]) #[[ATTR3]], !noalias !9 +; FULL-NEXT: [[TMP7:%.*]] = call float* @llvm.noalias.p0f32.p0i8.p0p0f32.i64(float* [[TMP3]], i8* [[TMP6]], float** null, i64 0, metadata [[META17]]) #[[ATTR3]], !noalias !15 +; FULL-NEXT: [[TMP8:%.*]] = load float, float* [[TMP7]], align 4, !noalias !15 +; FULL-NEXT: [[ARRAYIDX_I_I:%.*]] = getelementptr inbounds float, float* [[TMP5]], i64 5 +; FULL-NEXT: store float [[TMP8]], float* [[ARRAYIDX_I_I]], align 4, !noalias !15 +; FULL-NEXT: [[TMP9:%.*]] = load float, float* [[TMP3]], align 4, !noalias !9 +; FULL-NEXT: [[ARRAYIDX_I:%.*]] = getelementptr inbounds float, float* [[TMP1]], i64 7 +; FULL-NEXT: store float [[TMP9]], float* [[ARRAYIDX_I]], align 4, !noalias !9 +; FULL-NEXT: [[TMP10:%.*]] = call i8* @llvm.noalias.decl.p0i8.p0p0f32.i64(float** null, i64 0, metadata [[META18:![0-9]+]]) +; FULL-NEXT: [[TMP11:%.*]] = call float* @llvm.noalias.p0f32.p0i8.p0p0f32.i64(float* [[A]], i8* [[TMP10]], float** null, i64 0, metadata [[META18]]), !noalias !21 +; FULL-NEXT: [[TMP12:%.*]] = call i8* @llvm.noalias.decl.p0i8.p0p0f32.i64(float** null, i64 0, metadata [[META23:![0-9]+]]) +; FULL-NEXT: [[TMP13:%.*]] = call float* @llvm.noalias.p0f32.p0i8.p0p0f32.i64(float* [[B]], i8* [[TMP12]], float** null, i64 0, metadata [[META23]]), !noalias !21 +; FULL-NEXT: [[TMP14:%.*]] = load float, float* [[C]], align 4, !noalias !21 +; FULL-NEXT: [[ARRAYIDX_I1:%.*]] = getelementptr inbounds float, float* [[TMP11]], i64 6 +; FULL-NEXT: store float [[TMP14]], float* [[ARRAYIDX_I1]], align 4, !noalias !21 +; FULL-NEXT: [[ARRAYIDX1_I:%.*]] = getelementptr inbounds float, float* [[TMP13]], i64 8 +; FULL-NEXT: store float [[TMP14]], float* [[ARRAYIDX1_I]], align 4, !noalias !21 +; FULL-NEXT: [[TMP15:%.*]] = load float, float* [[C]], align 4 +; FULL-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds float, float* [[A]], i64 7 +; FULL-NEXT: store float [[TMP15]], float* [[ARRAYIDX]], align 4 +; FULL-NEXT: ret void ; entry: tail call void @foo(float* %a, float* %c) @@ -100,29 +180,55 @@ ret void } -; CHECK: !0 = !{!1} -; CHECK: !1 = distinct !{!1, !2, !"hello: %a"} -; CHECK: !2 = distinct !{!2, !"hello"} -; CHECK: !3 = !{!4} -; CHECK: !4 = distinct !{!4, !2, !"hello: %c"} -; CHECK: !5 = !{!6} -; CHECK: !6 = distinct !{!6, !7, !"foo: %a"} -; CHECK: !7 = distinct !{!7, !"foo"} -; CHECK: !8 = !{!9} -; CHECK: !9 = distinct !{!9, !7, !"foo: %c"} -; CHECK: !10 = !{!11} -; CHECK: !11 = distinct !{!11, !12, !"hello: %a"} -; CHECK: !12 = distinct !{!12, !"hello"} -; CHECK: !13 = !{!14} -; CHECK: !14 = distinct !{!14, !12, !"hello: %c"} -; CHECK: !15 = !{!14, !9} -; CHECK: !16 = !{!11, !6} -; CHECK: !17 = !{!18} -; CHECK: !18 = distinct !{!18, !19, !"hello2: %a"} -; CHECK: !19 = distinct !{!19, !"hello2"} -; CHECK: !20 = !{!21} -; CHECK: !21 = distinct !{!21, !19, !"hello2: %b"} -; CHECK: !22 = !{!18, !21} - attributes #0 = { nounwind uwtable } +; NONE-NOT: !0 = + +; SCOPES: !0 = !{!1} +; SCOPES-NEXT: !1 = distinct !{!1, !2, !"hello: %a"} +; SCOPES-NEXT: !2 = distinct !{!2, !"hello"} +; SCOPES-NEXT: !3 = !{!4} +; SCOPES-NEXT: !4 = distinct !{!4, !2, !"hello: %c"} +; SCOPES-NEXT: !5 = !{!6} +; SCOPES-NEXT: !6 = distinct !{!6, !7, !"foo: %a"} +; SCOPES-NEXT: !7 = distinct !{!7, !"foo"} +; SCOPES-NEXT: !8 = !{!9} +; SCOPES-NEXT: !9 = distinct !{!9, !7, !"foo: %c"} +; SCOPES-NEXT: !10 = !{!11} +; SCOPES-NEXT: !11 = distinct !{!11, !12, !"hello: %a"} +; SCOPES-NEXT: !12 = distinct !{!12, !"hello"} +; SCOPES-NEXT: !13 = !{!14} +; SCOPES-NEXT: !14 = distinct !{!14, !12, !"hello: %c"} +; SCOPES-NEXT: !15 = !{!14, !9} +; SCOPES-NEXT: !16 = !{!11, !6} +; SCOPES-NEXT: !17 = !{!18} +; SCOPES-NEXT: !18 = distinct !{!18, !19, !"hello2: %a"} +; SCOPES-NEXT: !19 = distinct !{!19, !"hello2"} +; SCOPES-NEXT: !20 = !{!21} +; SCOPES-NEXT: !21 = distinct !{!21, !19, !"hello2: %b"} +; SCOPES-NEXT: !22 = !{!18, !21} + +; FULL: !0 = !{!1} +; FULL-NEXT: !1 = distinct !{!1, !2, !"hello: %a"} +; FULL-NEXT: !2 = distinct !{!2, !"hello"} +; FULL-NEXT: !3 = !{!1, !4} +; FULL-NEXT: !4 = distinct !{!4, !2, !"hello: %c"} +; FULL-NEXT: !5 = !{!4} +; FULL-NEXT: !6 = !{!7} +; FULL-NEXT: !7 = distinct !{!7, !8, !"foo: %a"} +; FULL-NEXT: !8 = distinct !{!8, !"foo"} +; FULL-NEXT: !9 = !{!7, !10} +; FULL-NEXT: !10 = distinct !{!10, !8, !"foo: %c"} +; FULL-NEXT: !11 = !{!10} +; FULL-NEXT: !12 = !{!13} +; FULL-NEXT: !13 = distinct !{!13, !14, !"hello: %a"} +; FULL-NEXT: !14 = distinct !{!14, !"hello"} +; FULL-NEXT: !15 = !{!13, !16, !7, !10} +; FULL-NEXT: !16 = distinct !{!16, !14, !"hello: %c"} +; FULL-NEXT: !17 = !{!16} +; FULL-NEXT: !18 = !{!19} +; FULL-NEXT: !19 = distinct !{!19, !20, !"hello2: %a"} +; FULL-NEXT: !20 = distinct !{!20, !"hello2"} +; FULL-NEXT: !21 = !{!19, !22} +; FULL-NEXT: !22 = distinct !{!22, !20, !"hello2: %b"} +; FULL-NEXT: !23 = !{!22}