diff --git a/llvm/include/llvm/Transforms/IPO/Attributor.h b/llvm/include/llvm/Transforms/IPO/Attributor.h --- a/llvm/include/llvm/Transforms/IPO/Attributor.h +++ b/llvm/include/llvm/Transforms/IPO/Attributor.h @@ -2387,6 +2387,10 @@ /// The allocator used to allocate memory, e.g. for `AbstractAttribute`s. BumpPtrAllocator &Allocator; + const SmallSetVector &getModifiedFunctions() { + return CGModifiedFunctions; + } + private: /// This method will do fixpoint iteration until fixpoint or the /// maximum iteration count is reached. @@ -3375,6 +3379,20 @@ LazyCallGraph &CG, CGSCCUpdateResult &UR); }; +/// A more lightweight version of the Attributor which only runs attribute +/// inference but no simplifications. +struct AttributorLightPass : public PassInfoMixin { + PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); +}; + +/// A more lightweight version of the Attributor which only runs attribute +/// inference but no simplifications. +struct AttributorLightCGSCCPass + : public PassInfoMixin { + PreservedAnalyses run(LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, + LazyCallGraph &CG, CGSCCUpdateResult &UR); +}; + /// Helper function to clamp a state \p S of type \p StateType with the /// information in \p R and indicate/return if \p S did change (as-in update is /// required to be run again). diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def --- a/llvm/lib/Passes/PassRegistry.def +++ b/llvm/lib/Passes/PassRegistry.def @@ -42,6 +42,7 @@ #endif MODULE_PASS("always-inline", AlwaysInlinerPass()) MODULE_PASS("attributor", AttributorPass()) +MODULE_PASS("attributor-light", AttributorLightPass()) MODULE_PASS("annotation2metadata", Annotation2MetadataPass()) MODULE_PASS("openmp-opt", OpenMPOptPass()) MODULE_PASS("openmp-opt-postlink", OpenMPOptPass(ThinOrFullLTOPhase::FullLTOPostLink)) @@ -206,6 +207,7 @@ CGSCC_PASS("argpromotion", ArgumentPromotionPass()) CGSCC_PASS("invalidate", InvalidateAllAnalysesPass()) CGSCC_PASS("attributor-cgscc", AttributorCGSCCPass()) +CGSCC_PASS("attributor-light-cgscc", AttributorLightCGSCCPass()) CGSCC_PASS("openmp-opt-cgscc", OpenMPOptCGSCCPass()) CGSCC_PASS("no-op-cgscc", NoOpCGSCCPass()) #undef CGSCC_PASS diff --git a/llvm/lib/Transforms/IPO/Attributor.cpp b/llvm/lib/Transforms/IPO/Attributor.cpp --- a/llvm/lib/Transforms/IPO/Attributor.cpp +++ b/llvm/lib/Transforms/IPO/Attributor.cpp @@ -3275,9 +3275,10 @@ AttributeSet Attrs) { bool IsKnown; if (!Attrs.hasAttribute(AK)) - if (!AA::hasAssumedIRAttr(*this, nullptr, IRP, DepClassTy::NONE, - IsKnown)) - getOrCreateAAFor(IRP); + if (!Configuration.Allowed || Configuration.Allowed->count(&AAType::ID)) + if (!AA::hasAssumedIRAttr(*this, nullptr, IRP, DepClassTy::NONE, + IsKnown)) + getOrCreateAAFor(IRP); } void Attributor::identifyDefaultAbstractAttributes(Function &F) { @@ -3798,6 +3799,88 @@ return Changed == ChangeStatus::CHANGED; } +static bool runAttributorLightOnFunctions(InformationCache &InfoCache, + SetVector &Functions, + AnalysisGetter &AG, + CallGraphUpdater &CGUpdater, + FunctionAnalysisManager &FAM, + bool IsModulePass) { + if (Functions.empty()) + return false; + + LLVM_DEBUG({ + dbgs() << "[AttributorLight] Run on module with " << Functions.size() + << " functions:\n"; + for (Function *Fn : Functions) + dbgs() << " - " << Fn->getName() << "\n"; + }); + + // Create an Attributor and initially empty information cache that is filled + // while we identify default attribute opportunities. + AttributorConfig AC(CGUpdater); + AC.IsModulePass = IsModulePass; + AC.DeleteFns = false; + DenseSet Allowed( + {&AAWillReturn::ID, &AANoUnwind::ID, &AANoRecurse::ID, &AANoSync::ID, + &AANoFree::ID, &AANoReturn::ID, &AAMemoryLocation::ID, + &AAMemoryBehavior::ID, &AAUnderlyingObjects::ID, &AANoCapture::ID, + &AAInterFnReachability::ID, &AAIntraFnReachability::ID, &AACallEdges::ID, + &AANoFPClass::ID, &AAMustProgress::ID, &AANonNull::ID}); + AC.Allowed = &Allowed; + AC.UseLiveness = false; + + Attributor A(Functions, InfoCache, AC); + + for (Function *F : Functions) { + if (F->hasExactDefinition()) + NumFnWithExactDefinition++; + else + NumFnWithoutExactDefinition++; + + // We look at internal functions only on-demand but if any use is not a + // direct call or outside the current set of analyzed functions, we have + // to do it eagerly. + if (F->hasLocalLinkage()) { + if (llvm::all_of(F->uses(), [&Functions](const Use &U) { + const auto *CB = dyn_cast(U.getUser()); + return CB && CB->isCallee(&U) && + Functions.count(const_cast(CB->getCaller())); + })) + continue; + } + + // Populate the Attributor with abstract attribute opportunities in the + // function and the information cache with IR information. + A.identifyDefaultAbstractAttributes(*F); + } + + ChangeStatus Changed = A.run(); + + if (Changed == ChangeStatus::CHANGED) { + // Invalidate analyses for modified functions so that we don't have to + // invalidate all analyses for all functions in this SCC. + PreservedAnalyses FuncPA; + // We haven't changed the CFG for modified functions. + FuncPA.preserveSet(); + for (Function *Changed : A.getModifiedFunctions()) { + FAM.invalidate(*Changed, FuncPA); + // Also invalidate any direct callers of changed functions since analyses + // may care about attributes of direct callees. For example, MemorySSA + // cares about whether or not a call's callee modifies memory and queries + // that through function attributes. + for (auto *U : Changed->users()) { + if (auto *Call = dyn_cast(U)) { + if (Call->getCalledFunction() == Changed) + FAM.invalidate(*Call->getFunction(), FuncPA); + } + } + } + } + LLVM_DEBUG(dbgs() << "[Attributor] Done with " << Functions.size() + << " functions, result: " << Changed << ".\n"); + return Changed == ChangeStatus::CHANGED; +} + void AADepGraph::viewGraph() { llvm::ViewGraph(this, "Dependency Graph"); } void AADepGraph::dumpGraph() { @@ -3878,6 +3961,62 @@ return PreservedAnalyses::all(); } +PreservedAnalyses AttributorLightPass::run(Module &M, + ModuleAnalysisManager &AM) { + FunctionAnalysisManager &FAM = + AM.getResult(M).getManager(); + AnalysisGetter AG(FAM, /* CachedOnly */ true); + + SetVector Functions; + for (Function &F : M) + Functions.insert(&F); + + CallGraphUpdater CGUpdater; + BumpPtrAllocator Allocator; + InformationCache InfoCache(M, AG, Allocator, /* CGSCC */ nullptr); + if (runAttributorLightOnFunctions(InfoCache, Functions, AG, CGUpdater, FAM, + /* IsModulePass */ true)) { + PreservedAnalyses PA; + // We have not added or removed functions. + PA.preserve(); + // We already invalidated all relevant function analyses above. + PA.preserveSet>(); + return PA; + } + return PreservedAnalyses::all(); +} + +PreservedAnalyses AttributorLightCGSCCPass::run(LazyCallGraph::SCC &C, + CGSCCAnalysisManager &AM, + LazyCallGraph &CG, + CGSCCUpdateResult &UR) { + FunctionAnalysisManager &FAM = + AM.getResult(C, CG).getManager(); + AnalysisGetter AG(FAM); + + SetVector Functions; + for (LazyCallGraph::Node &N : C) + Functions.insert(&N.getFunction()); + + if (Functions.empty()) + return PreservedAnalyses::all(); + + Module &M = *Functions.back()->getParent(); + CallGraphUpdater CGUpdater; + CGUpdater.initialize(CG, C, AM, UR); + BumpPtrAllocator Allocator; + InformationCache InfoCache(M, AG, Allocator, /* CGSCC */ &Functions); + if (runAttributorLightOnFunctions(InfoCache, Functions, AG, CGUpdater, FAM, + /* IsModulePass */ false)) { + PreservedAnalyses PA; + // We have not added or removed functions. + PA.preserve(); + // We already invalidated all relevant function analyses above. + PA.preserveSet>(); + return PA; + } + return PreservedAnalyses::all(); +} namespace llvm { template <> struct GraphTraits { diff --git a/llvm/test/Transforms/Attributor/nofpclass-round.ll b/llvm/test/Transforms/Attributor/nofpclass-round.ll --- a/llvm/test/Transforms/Attributor/nofpclass-round.ll +++ b/llvm/test/Transforms/Attributor/nofpclass-round.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 2 -; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT +; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal -S < %s | FileCheck %s --check-prefixes=CHECK +; RUN: opt -aa-pipeline=basic-aa -passes=attributor-light -attributor-manifest-internal -S < %s | FileCheck %s --check-prefixes=LIGHT declare float @llvm.round.f32(float) declare ppc_fp128 @llvm.round.ppcf128(ppc_fp128) @@ -9,6 +10,11 @@ ; CHECK-SAME: (float [[ARG0:%.*]]) #[[ATTR1:[0-9]+]] { ; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(sub) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2:[0-9]+]] ; CHECK-NEXT: ret float [[CALL]] +; +; LIGHT-LABEL: define float @ret_round +; LIGHT-SAME: (float [[ARG0:%.*]]) #[[ATTR1:[0-9]+]] { +; LIGHT-NEXT: [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2:[0-9]+]] +; LIGHT-NEXT: ret float [[CALL]] ; %call = call float @llvm.round.f32(float %arg0) ret float %call @@ -19,6 +25,11 @@ ; CHECK-SAME: (float nofpclass(inf) [[ARG0:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(inf sub) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] +; +; LIGHT-LABEL: define float @ret_round_noinf +; LIGHT-SAME: (float nofpclass(inf) [[ARG0:%.*]]) #[[ATTR1]] { +; LIGHT-NEXT: [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] +; LIGHT-NEXT: ret float [[CALL]] ; %call = call float @llvm.round.f32(float %arg0) ret float %call @@ -29,6 +40,11 @@ ; CHECK-SAME: (float nofpclass(pinf) [[ARG0:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(pinf sub) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] +; +; LIGHT-LABEL: define float @ret_round_nopinf +; LIGHT-SAME: (float nofpclass(pinf) [[ARG0:%.*]]) #[[ATTR1]] { +; LIGHT-NEXT: [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] +; LIGHT-NEXT: ret float [[CALL]] ; %call = call float @llvm.round.f32(float %arg0) ret float %call @@ -39,6 +55,11 @@ ; CHECK-SAME: (float nofpclass(ninf) [[ARG0:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(ninf sub) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] +; +; LIGHT-LABEL: define float @ret_round_noninf +; LIGHT-SAME: (float nofpclass(ninf) [[ARG0:%.*]]) #[[ATTR1]] { +; LIGHT-NEXT: [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] +; LIGHT-NEXT: ret float [[CALL]] ; %call = call float @llvm.round.f32(float %arg0) ret float %call @@ -49,6 +70,11 @@ ; CHECK-SAME: (float nofpclass(nan) [[ARG0:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(nan sub) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] +; +; LIGHT-LABEL: define float @ret_round_nonan +; LIGHT-SAME: (float nofpclass(nan) [[ARG0:%.*]]) #[[ATTR1]] { +; LIGHT-NEXT: [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] +; LIGHT-NEXT: ret float [[CALL]] ; %call = call float @llvm.round.f32(float %arg0) ret float %call @@ -59,6 +85,11 @@ ; CHECK-SAME: (float nofpclass(qnan) [[ARG0:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(sub) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] +; +; LIGHT-LABEL: define float @ret_round_noqnan +; LIGHT-SAME: (float nofpclass(qnan) [[ARG0:%.*]]) #[[ATTR1]] { +; LIGHT-NEXT: [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] +; LIGHT-NEXT: ret float [[CALL]] ; %call = call float @llvm.round.f32(float %arg0) ret float %call @@ -69,6 +100,11 @@ ; CHECK-SAME: (float nofpclass(snan) [[ARG0:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(snan sub) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] +; +; LIGHT-LABEL: define float @ret_round_nosnan +; LIGHT-SAME: (float nofpclass(snan) [[ARG0:%.*]]) #[[ATTR1]] { +; LIGHT-NEXT: [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] +; LIGHT-NEXT: ret float [[CALL]] ; %call = call float @llvm.round.f32(float %arg0) ret float %call @@ -79,6 +115,11 @@ ; CHECK-SAME: (float nofpclass(zero) [[ARG0:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(sub) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] +; +; LIGHT-LABEL: define float @ret_round_nozero +; LIGHT-SAME: (float nofpclass(zero) [[ARG0:%.*]]) #[[ATTR1]] { +; LIGHT-NEXT: [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] +; LIGHT-NEXT: ret float [[CALL]] ; %call = call float @llvm.round.f32(float %arg0) ret float %call @@ -89,6 +130,11 @@ ; CHECK-SAME: (float nofpclass(pzero) [[ARG0:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(sub) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] +; +; LIGHT-LABEL: define float @ret_round_nopzero +; LIGHT-SAME: (float nofpclass(pzero) [[ARG0:%.*]]) #[[ATTR1]] { +; LIGHT-NEXT: [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] +; LIGHT-NEXT: ret float [[CALL]] ; %call = call float @llvm.round.f32(float %arg0) ret float %call @@ -99,6 +145,11 @@ ; CHECK-SAME: (float nofpclass(nzero) [[ARG0:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(sub) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] +; +; LIGHT-LABEL: define float @ret_round_nonzero +; LIGHT-SAME: (float nofpclass(nzero) [[ARG0:%.*]]) #[[ATTR1]] { +; LIGHT-NEXT: [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] +; LIGHT-NEXT: ret float [[CALL]] ; %call = call float @llvm.round.f32(float %arg0) ret float %call @@ -109,6 +160,11 @@ ; CHECK-SAME: (float nofpclass(norm) [[ARG0:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(sub) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] +; +; LIGHT-LABEL: define float @ret_round_nonorm +; LIGHT-SAME: (float nofpclass(norm) [[ARG0:%.*]]) #[[ATTR1]] { +; LIGHT-NEXT: [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] +; LIGHT-NEXT: ret float [[CALL]] ; %call = call float @llvm.round.f32(float %arg0) ret float %call @@ -119,6 +175,11 @@ ; CHECK-SAME: (float nofpclass(nnorm) [[ARG0:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(sub) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] +; +; LIGHT-LABEL: define float @ret_round_nonnorm +; LIGHT-SAME: (float nofpclass(nnorm) [[ARG0:%.*]]) #[[ATTR1]] { +; LIGHT-NEXT: [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] +; LIGHT-NEXT: ret float [[CALL]] ; %call = call float @llvm.round.f32(float %arg0) ret float %call @@ -129,6 +190,11 @@ ; CHECK-SAME: (float nofpclass(pnorm) [[ARG0:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(sub) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] +; +; LIGHT-LABEL: define float @ret_round_nopnorm +; LIGHT-SAME: (float nofpclass(pnorm) [[ARG0:%.*]]) #[[ATTR1]] { +; LIGHT-NEXT: [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] +; LIGHT-NEXT: ret float [[CALL]] ; %call = call float @llvm.round.f32(float %arg0) ret float %call @@ -139,6 +205,11 @@ ; CHECK-SAME: (float nofpclass(nsub) [[ARG0:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(sub) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] +; +; LIGHT-LABEL: define float @ret_round_nonsub +; LIGHT-SAME: (float nofpclass(nsub) [[ARG0:%.*]]) #[[ATTR1]] { +; LIGHT-NEXT: [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] +; LIGHT-NEXT: ret float [[CALL]] ; %call = call float @llvm.round.f32(float %arg0) ret float %call @@ -149,6 +220,11 @@ ; CHECK-SAME: (float nofpclass(psub) [[ARG0:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(sub) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] +; +; LIGHT-LABEL: define float @ret_round_nopsub +; LIGHT-SAME: (float nofpclass(psub) [[ARG0:%.*]]) #[[ATTR1]] { +; LIGHT-NEXT: [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] +; LIGHT-NEXT: ret float [[CALL]] ; %call = call float @llvm.round.f32(float %arg0) ret float %call @@ -159,6 +235,11 @@ ; CHECK-SAME: (float nofpclass(sub norm) [[ARG0:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(sub) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] +; +; LIGHT-LABEL: define float @ret_round_nonorm_nosub +; LIGHT-SAME: (float nofpclass(sub norm) [[ARG0:%.*]]) #[[ATTR1]] { +; LIGHT-NEXT: [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] +; LIGHT-NEXT: ret float [[CALL]] ; %call = call float @llvm.round.f32(float %arg0) ret float %call @@ -169,6 +250,11 @@ ; CHECK-SAME: (float nofpclass(psub pnorm) [[ARG0:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(sub) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] +; +; LIGHT-LABEL: define float @ret_round_nopnorm_nopsub +; LIGHT-SAME: (float nofpclass(psub pnorm) [[ARG0:%.*]]) #[[ATTR1]] { +; LIGHT-NEXT: [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] +; LIGHT-NEXT: ret float [[CALL]] ; %call = call float @llvm.round.f32(float %arg0) ret float %call @@ -179,6 +265,11 @@ ; CHECK-SAME: (float nofpclass(nsub nnorm) [[ARG0:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(sub) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] +; +; LIGHT-LABEL: define float @ret_round_nonnorm_nonsub +; LIGHT-SAME: (float nofpclass(nsub nnorm) [[ARG0:%.*]]) #[[ATTR1]] { +; LIGHT-NEXT: [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] +; LIGHT-NEXT: ret float [[CALL]] ; %call = call float @llvm.round.f32(float %arg0) ret float %call @@ -189,6 +280,11 @@ ; CHECK-SAME: (float nofpclass(nsub pnorm) [[ARG0:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(sub) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] +; +; LIGHT-LABEL: define float @ret_round_nopnorm_nonsub +; LIGHT-SAME: (float nofpclass(nsub pnorm) [[ARG0:%.*]]) #[[ATTR1]] { +; LIGHT-NEXT: [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] +; LIGHT-NEXT: ret float [[CALL]] ; %call = call float @llvm.round.f32(float %arg0) ret float %call @@ -199,6 +295,11 @@ ; CHECK-SAME: (ppc_fp128 [[ARG0:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(sub) ppc_fp128 @llvm.round.ppcf128(ppc_fp128 [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret ppc_fp128 [[CALL]] +; +; LIGHT-LABEL: define ppc_fp128 @ret_round_ppcf128 +; LIGHT-SAME: (ppc_fp128 [[ARG0:%.*]]) #[[ATTR1]] { +; LIGHT-NEXT: [[CALL:%.*]] = call ppc_fp128 @llvm.round.ppcf128(ppc_fp128 [[ARG0]]) #[[ATTR2]] +; LIGHT-NEXT: ret ppc_fp128 [[CALL]] ; %call = call ppc_fp128 @llvm.round.ppcf128(ppc_fp128 %arg0) ret ppc_fp128 %call @@ -209,6 +310,11 @@ ; CHECK-SAME: (ppc_fp128 nofpclass(inf) [[ARG0:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(sub) ppc_fp128 @llvm.round.ppcf128(ppc_fp128 [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret ppc_fp128 [[CALL]] +; +; LIGHT-LABEL: define ppc_fp128 @ret_round_noinf_ppcf128 +; LIGHT-SAME: (ppc_fp128 nofpclass(inf) [[ARG0:%.*]]) #[[ATTR1]] { +; LIGHT-NEXT: [[CALL:%.*]] = call ppc_fp128 @llvm.round.ppcf128(ppc_fp128 [[ARG0]]) #[[ATTR2]] +; LIGHT-NEXT: ret ppc_fp128 [[CALL]] ; %call = call ppc_fp128 @llvm.round.ppcf128(ppc_fp128 %arg0) ret ppc_fp128 %call @@ -219,6 +325,11 @@ ; CHECK-SAME: (ppc_fp128 nofpclass(pinf) [[ARG0:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(sub) ppc_fp128 @llvm.round.ppcf128(ppc_fp128 [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret ppc_fp128 [[CALL]] +; +; LIGHT-LABEL: define ppc_fp128 @ret_round_nopinf_ppcf128 +; LIGHT-SAME: (ppc_fp128 nofpclass(pinf) [[ARG0:%.*]]) #[[ATTR1]] { +; LIGHT-NEXT: [[CALL:%.*]] = call ppc_fp128 @llvm.round.ppcf128(ppc_fp128 [[ARG0]]) #[[ATTR2]] +; LIGHT-NEXT: ret ppc_fp128 [[CALL]] ; %call = call ppc_fp128 @llvm.round.ppcf128(ppc_fp128 %arg0) ret ppc_fp128 %call @@ -229,6 +340,11 @@ ; CHECK-SAME: (ppc_fp128 nofpclass(ninf) [[ARG0:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(sub) ppc_fp128 @llvm.round.ppcf128(ppc_fp128 [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret ppc_fp128 [[CALL]] +; +; LIGHT-LABEL: define ppc_fp128 @ret_round_noninf_ppcf128 +; LIGHT-SAME: (ppc_fp128 nofpclass(ninf) [[ARG0:%.*]]) #[[ATTR1]] { +; LIGHT-NEXT: [[CALL:%.*]] = call ppc_fp128 @llvm.round.ppcf128(ppc_fp128 [[ARG0]]) #[[ATTR2]] +; LIGHT-NEXT: ret ppc_fp128 [[CALL]] ; %call = call ppc_fp128 @llvm.round.ppcf128(ppc_fp128 %arg0) ret ppc_fp128 %call @@ -239,6 +355,11 @@ ; CHECK-SAME: (ppc_fp128 nofpclass(nan) [[ARG0:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(nan sub) ppc_fp128 @llvm.round.ppcf128(ppc_fp128 [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret ppc_fp128 [[CALL]] +; +; LIGHT-LABEL: define ppc_fp128 @ret_round_nonan_ppcf128 +; LIGHT-SAME: (ppc_fp128 nofpclass(nan) [[ARG0:%.*]]) #[[ATTR1]] { +; LIGHT-NEXT: [[CALL:%.*]] = call ppc_fp128 @llvm.round.ppcf128(ppc_fp128 [[ARG0]]) #[[ATTR2]] +; LIGHT-NEXT: ret ppc_fp128 [[CALL]] ; %call = call ppc_fp128 @llvm.round.ppcf128(ppc_fp128 %arg0) ret ppc_fp128 %call @@ -249,6 +370,11 @@ ; CHECK-SAME: (float nofpclass(ninf nsub nnorm) [[ARG0:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(ninf sub) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] +; +; LIGHT-LABEL: define float @ret_round_noneg +; LIGHT-SAME: (float nofpclass(ninf nsub nnorm) [[ARG0:%.*]]) #[[ATTR1]] { +; LIGHT-NEXT: [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] +; LIGHT-NEXT: ret float [[CALL]] ; %call = call float @llvm.round.f32(float %arg0) ret float %call @@ -259,6 +385,11 @@ ; CHECK-SAME: (float nofpclass(ninf nzero nsub nnorm) [[ARG0:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(ninf nzero sub nnorm) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] +; +; LIGHT-LABEL: define float @ret_round_noneg_nonegzero +; LIGHT-SAME: (float nofpclass(ninf nzero nsub nnorm) [[ARG0:%.*]]) #[[ATTR1]] { +; LIGHT-NEXT: [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] +; LIGHT-NEXT: ret float [[CALL]] ; %call = call float @llvm.round.f32(float %arg0) ret float %call @@ -269,6 +400,11 @@ ; CHECK-SAME: (float nofpclass(nan ninf nzero nsub nnorm) [[ARG0:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(nan ninf nzero sub nnorm) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] +; +; LIGHT-LABEL: define float @ret_round_noneg_nonegzero_nonan +; LIGHT-SAME: (float nofpclass(nan ninf nzero nsub nnorm) [[ARG0:%.*]]) #[[ATTR1]] { +; LIGHT-NEXT: [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] +; LIGHT-NEXT: ret float [[CALL]] ; %call = call float @llvm.round.f32(float %arg0) ret float %call @@ -279,6 +415,11 @@ ; CHECK-SAME: (float nofpclass(ninf zero nsub nnorm) [[ARG0:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(ninf nzero sub nnorm) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] +; +; LIGHT-LABEL: define float @ret_round_noneg_nozero +; LIGHT-SAME: (float nofpclass(ninf zero nsub nnorm) [[ARG0:%.*]]) #[[ATTR1]] { +; LIGHT-NEXT: [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] +; LIGHT-NEXT: ret float [[CALL]] ; %call = call float @llvm.round.f32(float %arg0) ret float %call @@ -289,6 +430,11 @@ ; CHECK-SAME: (float nofpclass(nan ninf zero nsub nnorm) [[ARG0:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(nan ninf nzero sub nnorm) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] +; +; LIGHT-LABEL: define float @ret_round_noneg_nozero_nonan +; LIGHT-SAME: (float nofpclass(nan ninf zero nsub nnorm) [[ARG0:%.*]]) #[[ATTR1]] { +; LIGHT-NEXT: [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] +; LIGHT-NEXT: ret float [[CALL]] ; %call = call float @llvm.round.f32(float %arg0) ret float %call @@ -299,6 +445,11 @@ ; CHECK-SAME: (float nofpclass(pinf psub pnorm) [[ARG0:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(pinf sub) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] +; +; LIGHT-LABEL: define float @ret_round_nopos +; LIGHT-SAME: (float nofpclass(pinf psub pnorm) [[ARG0:%.*]]) #[[ATTR1]] { +; LIGHT-NEXT: [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] +; LIGHT-NEXT: ret float [[CALL]] ; %call = call float @llvm.round.f32(float %arg0) ret float %call @@ -309,6 +460,11 @@ ; CHECK-SAME: (float nofpclass(pinf pzero psub pnorm) [[ARG0:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(pinf pzero sub pnorm) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] +; +; LIGHT-LABEL: define float @ret_round_nopos_nopzero +; LIGHT-SAME: (float nofpclass(pinf pzero psub pnorm) [[ARG0:%.*]]) #[[ATTR1]] { +; LIGHT-NEXT: [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] +; LIGHT-NEXT: ret float [[CALL]] ; %call = call float @llvm.round.f32(float %arg0) ret float %call @@ -319,6 +475,11 @@ ; CHECK-SAME: (float nofpclass(nan pinf pzero psub pnorm) [[ARG0:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(nan pinf pzero sub pnorm) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] +; +; LIGHT-LABEL: define float @ret_round_nopos_nopzero_nonan +; LIGHT-SAME: (float nofpclass(nan pinf pzero psub pnorm) [[ARG0:%.*]]) #[[ATTR1]] { +; LIGHT-NEXT: [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] +; LIGHT-NEXT: ret float [[CALL]] ; %call = call float @llvm.round.f32(float %arg0) ret float %call @@ -329,6 +490,11 @@ ; CHECK-SAME: (float nofpclass(pinf zero psub pnorm) [[ARG0:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(pinf pzero sub pnorm) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] +; +; LIGHT-LABEL: define float @ret_round_nopos_nozero +; LIGHT-SAME: (float nofpclass(pinf zero psub pnorm) [[ARG0:%.*]]) #[[ATTR1]] { +; LIGHT-NEXT: [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] +; LIGHT-NEXT: ret float [[CALL]] ; %call = call float @llvm.round.f32(float %arg0) ret float %call @@ -339,6 +505,11 @@ ; CHECK-SAME: (float nofpclass(nan pinf zero psub pnorm) [[ARG0:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(nan pinf pzero sub pnorm) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] +; +; LIGHT-LABEL: define float @ret_round_nopos_nozero_nonan +; LIGHT-SAME: (float nofpclass(nan pinf zero psub pnorm) [[ARG0:%.*]]) #[[ATTR1]] { +; LIGHT-NEXT: [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] +; LIGHT-NEXT: ret float [[CALL]] ; %call = call float @llvm.round.f32(float %arg0) ret float %call @@ -349,6 +520,11 @@ ; CHECK-SAME: (float nofpclass(pzero pnorm) [[ARG0:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(sub) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] +; +; LIGHT-LABEL: define float @ret_round_nopzero_nopnorm +; LIGHT-SAME: (float nofpclass(pzero pnorm) [[ARG0:%.*]]) #[[ATTR1]] { +; LIGHT-NEXT: [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] +; LIGHT-NEXT: ret float [[CALL]] ; %call = call float @llvm.round.f32(float %arg0) ret float %call @@ -359,6 +535,11 @@ ; CHECK-SAME: (float nofpclass(nzero nnorm) [[ARG0:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(sub) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] +; +; LIGHT-LABEL: define float @ret_round_nonzero_nonnorm +; LIGHT-SAME: (float nofpclass(nzero nnorm) [[ARG0:%.*]]) #[[ATTR1]] { +; LIGHT-NEXT: [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] +; LIGHT-NEXT: ret float [[CALL]] ; %call = call float @llvm.round.f32(float %arg0) ret float %call @@ -369,10 +550,12 @@ ; CHECK-SAME: (float nofpclass(zero norm) [[ARG0:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(sub) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] +; +; LIGHT-LABEL: define float @ret_round_nozero_nonorm +; LIGHT-SAME: (float nofpclass(zero norm) [[ARG0:%.*]]) #[[ATTR1]] { +; LIGHT-NEXT: [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]] +; LIGHT-NEXT: ret float [[CALL]] ; %call = call float @llvm.round.f32(float %arg0) ret float %call } - -;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line: -; TUNIT: {{.*}} diff --git a/llvm/test/Transforms/FunctionAttrs/argmemonly.ll b/llvm/test/Transforms/FunctionAttrs/argmemonly.ll --- a/llvm/test/Transforms/FunctionAttrs/argmemonly.ll +++ b/llvm/test/Transforms/FunctionAttrs/argmemonly.ll @@ -1,26 +1,34 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --version 2 -; RUN: opt -passes=function-attrs -S %s | FileCheck %s +; RUN: opt -passes=function-attrs -S < %s | FileCheck --check-prefixes=COMMON,FNATTRS %s +; RUN: opt -passes=attributor-light -S < %s | FileCheck --check-prefixes=COMMON,ATTRIBUTOR %s @g = global i32 20 define void @test_no_read_or_write() { -; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) -; CHECK-LABEL: define void @test_no_read_or_write -; CHECK-SAME: () #[[ATTR0:[0-9]+]] { -; CHECK-NEXT: entry: -; CHECK-NEXT: ret void +; COMMON: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) +; COMMON-LABEL: define void @test_no_read_or_write +; COMMON-SAME: () #[[ATTR0:[0-9]+]] { +; COMMON-NEXT: entry: +; COMMON-NEXT: ret void ; entry: ret void } define i32 @test_only_read_arg(ptr %ptr) { -; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read) -; CHECK-LABEL: define i32 @test_only_read_arg -; CHECK-SAME: (ptr nocapture readonly [[PTR:%.*]]) #[[ATTR1:[0-9]+]] { -; CHECK-NEXT: entry: -; CHECK-NEXT: [[L:%.*]] = load i32, ptr [[PTR]], align 4 -; CHECK-NEXT: ret i32 [[L]] +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read) +; FNATTRS-LABEL: define i32 @test_only_read_arg +; FNATTRS-SAME: (ptr nocapture readonly [[PTR:%.*]]) #[[ATTR1:[0-9]+]] { +; FNATTRS-NEXT: entry: +; FNATTRS-NEXT: [[L:%.*]] = load i32, ptr [[PTR]], align 4 +; FNATTRS-NEXT: ret i32 [[L]] +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read) +; ATTRIBUTOR-LABEL: define i32 @test_only_read_arg +; ATTRIBUTOR-SAME: (ptr nocapture nofree nonnull readonly [[PTR:%.*]]) #[[ATTR1:[0-9]+]] { +; ATTRIBUTOR-NEXT: entry: +; ATTRIBUTOR-NEXT: [[L:%.*]] = load i32, ptr [[PTR]], align 4 +; ATTRIBUTOR-NEXT: ret i32 [[L]] ; entry: %l = load i32, ptr %ptr @@ -28,12 +36,19 @@ } define i32 @test_only_read_arg_already_has_argmemonly(ptr %ptr) argmemonly { -; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read) -; CHECK-LABEL: define i32 @test_only_read_arg_already_has_argmemonly -; CHECK-SAME: (ptr nocapture readonly [[PTR:%.*]]) #[[ATTR1]] { -; CHECK-NEXT: entry: -; CHECK-NEXT: [[L:%.*]] = load i32, ptr [[PTR]], align 4 -; CHECK-NEXT: ret i32 [[L]] +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read) +; FNATTRS-LABEL: define i32 @test_only_read_arg_already_has_argmemonly +; FNATTRS-SAME: (ptr nocapture readonly [[PTR:%.*]]) #[[ATTR1]] { +; FNATTRS-NEXT: entry: +; FNATTRS-NEXT: [[L:%.*]] = load i32, ptr [[PTR]], align 4 +; FNATTRS-NEXT: ret i32 [[L]] +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read) +; ATTRIBUTOR-LABEL: define i32 @test_only_read_arg_already_has_argmemonly +; ATTRIBUTOR-SAME: (ptr nocapture nofree nonnull readonly [[PTR:%.*]]) #[[ATTR1]] { +; ATTRIBUTOR-NEXT: entry: +; ATTRIBUTOR-NEXT: [[L:%.*]] = load i32, ptr [[PTR]], align 4 +; ATTRIBUTOR-NEXT: ret i32 [[L]] ; entry: %l = load i32, ptr %ptr @@ -41,12 +56,19 @@ } define i32 @test_read_global() { -; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, argmem: none, inaccessiblemem: none) -; CHECK-LABEL: define i32 @test_read_global -; CHECK-SAME: () #[[ATTR2:[0-9]+]] { -; CHECK-NEXT: entry: -; CHECK-NEXT: [[L:%.*]] = load i32, ptr @g, align 4 -; CHECK-NEXT: ret i32 [[L]] +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, argmem: none, inaccessiblemem: none) +; FNATTRS-LABEL: define i32 @test_read_global +; FNATTRS-SAME: () #[[ATTR2:[0-9]+]] { +; FNATTRS-NEXT: entry: +; FNATTRS-NEXT: [[L:%.*]] = load i32, ptr @g, align 4 +; FNATTRS-NEXT: ret i32 [[L]] +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read) +; ATTRIBUTOR-LABEL: define i32 @test_read_global +; ATTRIBUTOR-SAME: () #[[ATTR2:[0-9]+]] { +; ATTRIBUTOR-NEXT: entry: +; ATTRIBUTOR-NEXT: [[L:%.*]] = load i32, ptr @g, align 4 +; ATTRIBUTOR-NEXT: ret i32 [[L]] ; entry: %l = load i32, ptr @g @@ -54,13 +76,21 @@ } define i32 @test_read_loaded_ptr(ptr %ptr) { -; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, inaccessiblemem: none) -; CHECK-LABEL: define i32 @test_read_loaded_ptr -; CHECK-SAME: (ptr nocapture readonly [[PTR:%.*]]) #[[ATTR3:[0-9]+]] { -; CHECK-NEXT: entry: -; CHECK-NEXT: [[L:%.*]] = load ptr, ptr [[PTR]], align 8 -; CHECK-NEXT: [[L_2:%.*]] = load i32, ptr [[L]], align 4 -; CHECK-NEXT: ret i32 [[L_2]] +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, inaccessiblemem: none) +; FNATTRS-LABEL: define i32 @test_read_loaded_ptr +; FNATTRS-SAME: (ptr nocapture readonly [[PTR:%.*]]) #[[ATTR3:[0-9]+]] { +; FNATTRS-NEXT: entry: +; FNATTRS-NEXT: [[L:%.*]] = load ptr, ptr [[PTR]], align 8 +; FNATTRS-NEXT: [[L_2:%.*]] = load i32, ptr [[L]], align 4 +; FNATTRS-NEXT: ret i32 [[L_2]] +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read) +; ATTRIBUTOR-LABEL: define i32 @test_read_loaded_ptr +; ATTRIBUTOR-SAME: (ptr nocapture nofree nonnull readonly [[PTR:%.*]]) #[[ATTR2]] { +; ATTRIBUTOR-NEXT: entry: +; ATTRIBUTOR-NEXT: [[L:%.*]] = load ptr, ptr [[PTR]], align 8 +; ATTRIBUTOR-NEXT: [[L_2:%.*]] = load i32, ptr [[L]], align 4 +; ATTRIBUTOR-NEXT: ret i32 [[L_2]] ; entry: %l = load ptr, ptr %ptr @@ -69,12 +99,19 @@ } define void @test_only_write_arg(ptr %ptr) { -; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) -; CHECK-LABEL: define void @test_only_write_arg -; CHECK-SAME: (ptr nocapture writeonly [[PTR:%.*]]) #[[ATTR4:[0-9]+]] { -; CHECK-NEXT: entry: -; CHECK-NEXT: store i32 0, ptr [[PTR]], align 4 -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) +; FNATTRS-LABEL: define void @test_only_write_arg +; FNATTRS-SAME: (ptr nocapture writeonly [[PTR:%.*]]) #[[ATTR4:[0-9]+]] { +; FNATTRS-NEXT: entry: +; FNATTRS-NEXT: store i32 0, ptr [[PTR]], align 4 +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) +; ATTRIBUTOR-LABEL: define void @test_only_write_arg +; ATTRIBUTOR-SAME: (ptr nocapture nofree nonnull writeonly [[PTR:%.*]]) #[[ATTR3:[0-9]+]] { +; ATTRIBUTOR-NEXT: entry: +; ATTRIBUTOR-NEXT: store i32 0, ptr [[PTR]], align 4 +; ATTRIBUTOR-NEXT: ret void ; entry: store i32 0, ptr %ptr @@ -82,12 +119,19 @@ } define void @test_write_global() { -; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none) -; CHECK-LABEL: define void @test_write_global -; CHECK-SAME: () #[[ATTR5:[0-9]+]] { -; CHECK-NEXT: entry: -; CHECK-NEXT: store i32 0, ptr @g, align 4 -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none) +; FNATTRS-LABEL: define void @test_write_global +; FNATTRS-SAME: () #[[ATTR5:[0-9]+]] { +; FNATTRS-NEXT: entry: +; FNATTRS-NEXT: store i32 0, ptr @g, align 4 +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write) +; ATTRIBUTOR-LABEL: define void @test_write_global +; ATTRIBUTOR-SAME: () #[[ATTR4:[0-9]+]] { +; ATTRIBUTOR-NEXT: entry: +; ATTRIBUTOR-NEXT: store i32 0, ptr @g, align 4 +; ATTRIBUTOR-NEXT: ret void ; entry: store i32 0, ptr @g @@ -97,10 +141,10 @@ declare void @fn_may_access_memory() define void @test_call_may_access_memory() { -; CHECK-LABEL: define void @test_call_may_access_memory() { -; CHECK-NEXT: entry: -; CHECK-NEXT: call void @fn_may_access_memory() -; CHECK-NEXT: ret void +; COMMON-LABEL: define void @test_call_may_access_memory() { +; COMMON-NEXT: entry: +; COMMON-NEXT: call void @fn_may_access_memory() +; COMMON-NEXT: ret void ; entry: call void @fn_may_access_memory() @@ -110,13 +154,21 @@ declare i32 @fn_readnone() readnone define void @test_call_readnone(ptr %ptr) { -; CHECK: Function Attrs: memory(argmem: write) -; CHECK-LABEL: define void @test_call_readnone -; CHECK-SAME: (ptr nocapture writeonly [[PTR:%.*]]) #[[ATTR7:[0-9]+]] { -; CHECK-NEXT: entry: -; CHECK-NEXT: [[C:%.*]] = call i32 @fn_readnone() -; CHECK-NEXT: store i32 [[C]], ptr [[PTR]], align 4 -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: memory(argmem: write) +; FNATTRS-LABEL: define void @test_call_readnone +; FNATTRS-SAME: (ptr nocapture writeonly [[PTR:%.*]]) #[[ATTR7:[0-9]+]] { +; FNATTRS-NEXT: entry: +; FNATTRS-NEXT: [[C:%.*]] = call i32 @fn_readnone() +; FNATTRS-NEXT: store i32 [[C]], ptr [[PTR]], align 4 +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: nosync memory(argmem: write) +; ATTRIBUTOR-LABEL: define void @test_call_readnone +; ATTRIBUTOR-SAME: (ptr nocapture nofree writeonly [[PTR:%.*]]) #[[ATTR6:[0-9]+]] { +; ATTRIBUTOR-NEXT: entry: +; ATTRIBUTOR-NEXT: [[C:%.*]] = call i32 @fn_readnone() #[[ATTR19:[0-9]+]] +; ATTRIBUTOR-NEXT: store i32 [[C]], ptr [[PTR]], align 4 +; ATTRIBUTOR-NEXT: ret void ; entry: %c = call i32 @fn_readnone() @@ -127,12 +179,19 @@ declare i32 @fn_argmemonly(ptr) argmemonly define i32 @test_call_argmemonly(ptr %ptr) { -; CHECK: Function Attrs: memory(argmem: readwrite) -; CHECK-LABEL: define i32 @test_call_argmemonly -; CHECK-SAME: (ptr [[PTR:%.*]]) #[[ATTR8:[0-9]+]] { -; CHECK-NEXT: entry: -; CHECK-NEXT: [[C:%.*]] = call i32 @fn_argmemonly(ptr [[PTR]]) -; CHECK-NEXT: ret i32 [[C]] +; FNATTRS: Function Attrs: memory(argmem: readwrite) +; FNATTRS-LABEL: define i32 @test_call_argmemonly +; FNATTRS-SAME: (ptr [[PTR:%.*]]) #[[ATTR8:[0-9]+]] { +; FNATTRS-NEXT: entry: +; FNATTRS-NEXT: [[C:%.*]] = call i32 @fn_argmemonly(ptr [[PTR]]) +; FNATTRS-NEXT: ret i32 [[C]] +; +; ATTRIBUTOR: Function Attrs: memory(argmem: readwrite) +; ATTRIBUTOR-LABEL: define i32 @test_call_argmemonly +; ATTRIBUTOR-SAME: (ptr [[PTR:%.*]]) #[[ATTR7:[0-9]+]] { +; ATTRIBUTOR-NEXT: entry: +; ATTRIBUTOR-NEXT: [[C:%.*]] = call i32 @fn_argmemonly(ptr [[PTR]]) +; ATTRIBUTOR-NEXT: ret i32 [[C]] ; entry: %c = call i32 @fn_argmemonly(ptr %ptr) @@ -140,12 +199,19 @@ } define i32 @test_call_fn_where_argmemonly_can_be_inferred(ptr %ptr) { -; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read) -; CHECK-LABEL: define i32 @test_call_fn_where_argmemonly_can_be_inferred -; CHECK-SAME: (ptr nocapture readonly [[PTR:%.*]]) #[[ATTR1]] { -; CHECK-NEXT: entry: -; CHECK-NEXT: [[C:%.*]] = call i32 @test_only_read_arg(ptr [[PTR]]) -; CHECK-NEXT: ret i32 [[C]] +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read) +; FNATTRS-LABEL: define i32 @test_call_fn_where_argmemonly_can_be_inferred +; FNATTRS-SAME: (ptr nocapture readonly [[PTR:%.*]]) #[[ATTR1]] { +; FNATTRS-NEXT: entry: +; FNATTRS-NEXT: [[C:%.*]] = call i32 @test_only_read_arg(ptr [[PTR]]) +; FNATTRS-NEXT: ret i32 [[C]] +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read) +; ATTRIBUTOR-LABEL: define i32 @test_call_fn_where_argmemonly_can_be_inferred +; ATTRIBUTOR-SAME: (ptr nocapture nofree readonly [[PTR:%.*]]) #[[ATTR1]] { +; ATTRIBUTOR-NEXT: entry: +; ATTRIBUTOR-NEXT: [[C:%.*]] = call i32 @test_only_read_arg(ptr nocapture nofree readonly [[PTR]]) #[[ATTR20:[0-9]+]] +; ATTRIBUTOR-NEXT: ret i32 [[C]] ; entry: %c = call i32 @test_only_read_arg(ptr %ptr) @@ -153,12 +219,19 @@ } define void @test_memcpy_argonly(ptr %dst, ptr %src) { -; CHECK: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(argmem: readwrite) -; CHECK-LABEL: define void @test_memcpy_argonly -; CHECK-SAME: (ptr nocapture writeonly [[DST:%.*]], ptr nocapture readonly [[SRC:%.*]]) #[[ATTR9:[0-9]+]] { -; CHECK-NEXT: entry: -; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr [[DST]], ptr [[SRC]], i64 32, i1 false) -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(argmem: readwrite) +; FNATTRS-LABEL: define void @test_memcpy_argonly +; FNATTRS-SAME: (ptr nocapture writeonly [[DST:%.*]], ptr nocapture readonly [[SRC:%.*]]) #[[ATTR9:[0-9]+]] { +; FNATTRS-NEXT: entry: +; FNATTRS-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr [[DST]], ptr [[SRC]], i64 32, i1 false) +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite) +; ATTRIBUTOR-LABEL: define void @test_memcpy_argonly +; ATTRIBUTOR-SAME: (ptr nocapture nofree writeonly [[DST:%.*]], ptr nocapture nofree readonly [[SRC:%.*]]) #[[ATTR8:[0-9]+]] { +; ATTRIBUTOR-NEXT: entry: +; ATTRIBUTOR-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr nocapture writeonly [[DST]], ptr nocapture readonly [[SRC]], i64 32, i1 false) #[[ATTR21:[0-9]+]] +; ATTRIBUTOR-NEXT: ret void ; entry: call void @llvm.memcpy.p0.p0.i64(ptr %dst, ptr %src, i64 32, i1 false) @@ -170,12 +243,19 @@ @arr = global [32 x i8] zeroinitializer define void @test_memcpy_src_global(ptr %dst) { -; CHECK: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(readwrite, inaccessiblemem: none) -; CHECK-LABEL: define void @test_memcpy_src_global -; CHECK-SAME: (ptr nocapture writeonly [[DST:%.*]]) #[[ATTR11:[0-9]+]] { -; CHECK-NEXT: entry: -; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr [[DST]], ptr @arr, i64 32, i1 false) -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(readwrite, inaccessiblemem: none) +; FNATTRS-LABEL: define void @test_memcpy_src_global +; FNATTRS-SAME: (ptr nocapture writeonly [[DST:%.*]]) #[[ATTR11:[0-9]+]] { +; FNATTRS-NEXT: entry: +; FNATTRS-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr [[DST]], ptr @arr, i64 32, i1 false) +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn +; ATTRIBUTOR-LABEL: define void @test_memcpy_src_global +; ATTRIBUTOR-SAME: (ptr nocapture nofree writeonly [[DST:%.*]]) #[[ATTR10:[0-9]+]] { +; ATTRIBUTOR-NEXT: entry: +; ATTRIBUTOR-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr nocapture writeonly [[DST]], ptr readonly @arr, i64 32, i1 false) #[[ATTR21]] +; ATTRIBUTOR-NEXT: ret void ; entry: call void @llvm.memcpy.p0.p0.i64(ptr %dst, ptr @arr, i64 32, i1 false) @@ -183,12 +263,19 @@ } define void @test_memcpy_dst_global(ptr %src) { -; CHECK: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(readwrite, inaccessiblemem: none) -; CHECK-LABEL: define void @test_memcpy_dst_global -; CHECK-SAME: (ptr nocapture readonly [[SRC:%.*]]) #[[ATTR11]] { -; CHECK-NEXT: entry: -; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr @arr, ptr [[SRC]], i64 32, i1 false) -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(readwrite, inaccessiblemem: none) +; FNATTRS-LABEL: define void @test_memcpy_dst_global +; FNATTRS-SAME: (ptr nocapture readonly [[SRC:%.*]]) #[[ATTR11]] { +; FNATTRS-NEXT: entry: +; FNATTRS-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr @arr, ptr [[SRC]], i64 32, i1 false) +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn +; ATTRIBUTOR-LABEL: define void @test_memcpy_dst_global +; ATTRIBUTOR-SAME: (ptr nocapture nofree readonly [[SRC:%.*]]) #[[ATTR10]] { +; ATTRIBUTOR-NEXT: entry: +; ATTRIBUTOR-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr writeonly @arr, ptr nocapture readonly [[SRC]], i64 32, i1 false) #[[ATTR21]] +; ATTRIBUTOR-NEXT: ret void ; entry: call void @llvm.memcpy.p0.p0.i64(ptr @arr, ptr %src, i64 32, i1 false) @@ -196,15 +283,25 @@ } define i32 @test_read_arg_access_alloca(ptr %ptr) { -; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read) -; CHECK-LABEL: define i32 @test_read_arg_access_alloca -; CHECK-SAME: (ptr nocapture readonly [[PTR:%.*]]) #[[ATTR1]] { -; CHECK-NEXT: entry: -; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 -; CHECK-NEXT: [[L:%.*]] = load i32, ptr [[PTR]], align 4 -; CHECK-NEXT: store i32 [[L]], ptr [[A]], align 4 -; CHECK-NEXT: [[L_2:%.*]] = load i32, ptr [[A]], align 4 -; CHECK-NEXT: ret i32 [[L_2]] +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read) +; FNATTRS-LABEL: define i32 @test_read_arg_access_alloca +; FNATTRS-SAME: (ptr nocapture readonly [[PTR:%.*]]) #[[ATTR1]] { +; FNATTRS-NEXT: entry: +; FNATTRS-NEXT: [[A:%.*]] = alloca i32, align 4 +; FNATTRS-NEXT: [[L:%.*]] = load i32, ptr [[PTR]], align 4 +; FNATTRS-NEXT: store i32 [[L]], ptr [[A]], align 4 +; FNATTRS-NEXT: [[L_2:%.*]] = load i32, ptr [[A]], align 4 +; FNATTRS-NEXT: ret i32 [[L_2]] +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite) +; ATTRIBUTOR-LABEL: define i32 @test_read_arg_access_alloca +; ATTRIBUTOR-SAME: (ptr nocapture nofree nonnull readonly [[PTR:%.*]]) #[[ATTR8]] { +; ATTRIBUTOR-NEXT: entry: +; ATTRIBUTOR-NEXT: [[A:%.*]] = alloca i32, align 4 +; ATTRIBUTOR-NEXT: [[L:%.*]] = load i32, ptr [[PTR]], align 4 +; ATTRIBUTOR-NEXT: store i32 [[L]], ptr [[A]], align 4 +; ATTRIBUTOR-NEXT: [[L_2:%.*]] = load i32, ptr [[A]], align 4 +; ATTRIBUTOR-NEXT: ret i32 [[L_2]] ; entry: %a = alloca i32 @@ -217,34 +314,53 @@ declare void @fn_inaccessiblememonly() inaccessiblememonly define void @test_inaccessiblememonly() { -; CHECK: Function Attrs: memory(inaccessiblemem: readwrite) -; CHECK-LABEL: define void @test_inaccessiblememonly -; CHECK-SAME: () #[[ATTR12:[0-9]+]] { -; CHECK-NEXT: call void @fn_inaccessiblememonly() -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: memory(inaccessiblemem: readwrite) +; FNATTRS-LABEL: define void @test_inaccessiblememonly +; FNATTRS-SAME: () #[[ATTR12:[0-9]+]] { +; FNATTRS-NEXT: call void @fn_inaccessiblememonly() +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: memory(inaccessiblemem: readwrite) +; ATTRIBUTOR-LABEL: define void @test_inaccessiblememonly +; ATTRIBUTOR-SAME: () #[[ATTR11:[0-9]+]] { +; ATTRIBUTOR-NEXT: call void @fn_inaccessiblememonly() +; ATTRIBUTOR-NEXT: ret void ; call void @fn_inaccessiblememonly() ret void } define void @test_inaccessiblememonly_readonly() { -; CHECK: Function Attrs: nofree memory(inaccessiblemem: read) -; CHECK-LABEL: define void @test_inaccessiblememonly_readonly -; CHECK-SAME: () #[[ATTR13:[0-9]+]] { -; CHECK-NEXT: call void @fn_inaccessiblememonly() #[[ATTR18:[0-9]+]] -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: nofree memory(inaccessiblemem: read) +; FNATTRS-LABEL: define void @test_inaccessiblememonly_readonly +; FNATTRS-SAME: () #[[ATTR13:[0-9]+]] { +; FNATTRS-NEXT: call void @fn_inaccessiblememonly() #[[ATTR19:[0-9]+]] +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: nosync memory(inaccessiblemem: read) +; ATTRIBUTOR-LABEL: define void @test_inaccessiblememonly_readonly +; ATTRIBUTOR-SAME: () #[[ATTR12:[0-9]+]] { +; ATTRIBUTOR-NEXT: call void @fn_inaccessiblememonly() #[[ATTR22:[0-9]+]] +; ATTRIBUTOR-NEXT: ret void ; call void @fn_inaccessiblememonly() readonly ret void } define void @test_inaccessibleorargmemonly_readonly(ptr %arg) { -; CHECK: Function Attrs: nofree memory(argmem: read, inaccessiblemem: read) -; CHECK-LABEL: define void @test_inaccessibleorargmemonly_readonly -; CHECK-SAME: (ptr nocapture readonly [[ARG:%.*]]) #[[ATTR14:[0-9]+]] { -; CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[ARG]], align 4 -; CHECK-NEXT: call void @fn_inaccessiblememonly() #[[ATTR18]] -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: nofree memory(argmem: read, inaccessiblemem: read) +; FNATTRS-LABEL: define void @test_inaccessibleorargmemonly_readonly +; FNATTRS-SAME: (ptr nocapture readonly [[ARG:%.*]]) #[[ATTR14:[0-9]+]] { +; FNATTRS-NEXT: [[TMP1:%.*]] = load i32, ptr [[ARG]], align 4 +; FNATTRS-NEXT: call void @fn_inaccessiblememonly() #[[ATTR19]] +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: nosync memory(argmem: read, inaccessiblemem: read) +; ATTRIBUTOR-LABEL: define void @test_inaccessibleorargmemonly_readonly +; ATTRIBUTOR-SAME: (ptr nocapture nofree nonnull readonly [[ARG:%.*]]) #[[ATTR13:[0-9]+]] { +; ATTRIBUTOR-NEXT: [[TMP1:%.*]] = load i32, ptr [[ARG]], align 4 +; ATTRIBUTOR-NEXT: call void @fn_inaccessiblememonly() #[[ATTR22]] +; ATTRIBUTOR-NEXT: ret void ; load i32, ptr %arg call void @fn_inaccessiblememonly() readonly @@ -252,12 +368,19 @@ } define void @test_inaccessibleorargmemonly_readwrite(ptr %arg) { -; CHECK: Function Attrs: memory(argmem: write, inaccessiblemem: read) -; CHECK-LABEL: define void @test_inaccessibleorargmemonly_readwrite -; CHECK-SAME: (ptr nocapture writeonly [[ARG:%.*]]) #[[ATTR15:[0-9]+]] { -; CHECK-NEXT: store i32 0, ptr [[ARG]], align 4 -; CHECK-NEXT: call void @fn_inaccessiblememonly() #[[ATTR18]] -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: memory(argmem: write, inaccessiblemem: read) +; FNATTRS-LABEL: define void @test_inaccessibleorargmemonly_readwrite +; FNATTRS-SAME: (ptr nocapture writeonly [[ARG:%.*]]) #[[ATTR15:[0-9]+]] { +; FNATTRS-NEXT: store i32 0, ptr [[ARG]], align 4 +; FNATTRS-NEXT: call void @fn_inaccessiblememonly() #[[ATTR19]] +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: nosync memory(argmem: readwrite, inaccessiblemem: readwrite) +; ATTRIBUTOR-LABEL: define void @test_inaccessibleorargmemonly_readwrite +; ATTRIBUTOR-SAME: (ptr nocapture nofree nonnull writeonly [[ARG:%.*]]) #[[ATTR14:[0-9]+]] { +; ATTRIBUTOR-NEXT: store i32 0, ptr [[ARG]], align 4 +; ATTRIBUTOR-NEXT: call void @fn_inaccessiblememonly() #[[ATTR22]] +; ATTRIBUTOR-NEXT: ret void ; store i32 0, ptr %arg call void @fn_inaccessiblememonly() readonly @@ -265,12 +388,19 @@ } define void @test_recursive_argmem_read(ptr %p) { -; CHECK: Function Attrs: nofree nosync nounwind memory(argmem: read) -; CHECK-LABEL: define void @test_recursive_argmem_read -; CHECK-SAME: (ptr nocapture readonly [[P:%.*]]) #[[ATTR16:[0-9]+]] { -; CHECK-NEXT: [[PVAL:%.*]] = load ptr, ptr [[P]], align 8 -; CHECK-NEXT: call void @test_recursive_argmem_read(ptr [[PVAL]]) -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: nofree nosync nounwind memory(argmem: read) +; FNATTRS-LABEL: define void @test_recursive_argmem_read +; FNATTRS-SAME: (ptr nocapture readonly [[P:%.*]]) #[[ATTR16:[0-9]+]] { +; FNATTRS-NEXT: [[PVAL:%.*]] = load ptr, ptr [[P]], align 8 +; FNATTRS-NEXT: call void @test_recursive_argmem_read(ptr [[PVAL]]) +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: nofree nosync nounwind memory(read) +; ATTRIBUTOR-LABEL: define void @test_recursive_argmem_read +; ATTRIBUTOR-SAME: (ptr nocapture nofree nonnull readonly [[P:%.*]]) #[[ATTR15:[0-9]+]] { +; ATTRIBUTOR-NEXT: [[PVAL:%.*]] = load ptr, ptr [[P]], align 8 +; ATTRIBUTOR-NEXT: call void @test_recursive_argmem_read(ptr nocapture nofree readonly [[PVAL]]) #[[ATTR15]] +; ATTRIBUTOR-NEXT: ret void ; %pval = load ptr, ptr %p call void @test_recursive_argmem_read(ptr %pval) @@ -278,13 +408,21 @@ } define void @test_recursive_argmem_readwrite(ptr %p) { -; CHECK: Function Attrs: nofree nosync nounwind memory(argmem: readwrite) -; CHECK-LABEL: define void @test_recursive_argmem_readwrite -; CHECK-SAME: (ptr nocapture [[P:%.*]]) #[[ATTR17:[0-9]+]] { -; CHECK-NEXT: [[PVAL:%.*]] = load ptr, ptr [[P]], align 8 -; CHECK-NEXT: store i32 0, ptr [[P]], align 4 -; CHECK-NEXT: call void @test_recursive_argmem_readwrite(ptr [[PVAL]]) -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: nofree nosync nounwind memory(argmem: readwrite) +; FNATTRS-LABEL: define void @test_recursive_argmem_readwrite +; FNATTRS-SAME: (ptr nocapture [[P:%.*]]) #[[ATTR17:[0-9]+]] { +; FNATTRS-NEXT: [[PVAL:%.*]] = load ptr, ptr [[P]], align 8 +; FNATTRS-NEXT: store i32 0, ptr [[P]], align 4 +; FNATTRS-NEXT: call void @test_recursive_argmem_readwrite(ptr [[PVAL]]) +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: nofree nosync nounwind +; ATTRIBUTOR-LABEL: define void @test_recursive_argmem_readwrite +; ATTRIBUTOR-SAME: (ptr nocapture nofree nonnull [[P:%.*]]) #[[ATTR16:[0-9]+]] { +; ATTRIBUTOR-NEXT: [[PVAL:%.*]] = load ptr, ptr [[P]], align 8 +; ATTRIBUTOR-NEXT: store i32 0, ptr [[P]], align 4 +; ATTRIBUTOR-NEXT: call void @test_recursive_argmem_readwrite(ptr nocapture nofree [[PVAL]]) #[[ATTR16]] +; ATTRIBUTOR-NEXT: ret void ; %pval = load ptr, ptr %p store i32 0, ptr %p @@ -293,13 +431,21 @@ } define void @test_recursive_argmem_read_alloca(ptr %p) { -; CHECK: Function Attrs: nofree nosync nounwind memory(argmem: read) -; CHECK-LABEL: define void @test_recursive_argmem_read_alloca -; CHECK-SAME: (ptr nocapture readonly [[P:%.*]]) #[[ATTR16]] { -; CHECK-NEXT: [[A:%.*]] = alloca ptr, align 8 -; CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[P]], align 4 -; CHECK-NEXT: call void @test_recursive_argmem_read_alloca(ptr [[A]]) -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: nofree nosync nounwind memory(argmem: read) +; FNATTRS-LABEL: define void @test_recursive_argmem_read_alloca +; FNATTRS-SAME: (ptr nocapture readonly [[P:%.*]]) #[[ATTR16]] { +; FNATTRS-NEXT: [[A:%.*]] = alloca ptr, align 8 +; FNATTRS-NEXT: [[TMP1:%.*]] = load i32, ptr [[P]], align 4 +; FNATTRS-NEXT: call void @test_recursive_argmem_read_alloca(ptr [[A]]) +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: nofree nosync nounwind memory(argmem: read) +; ATTRIBUTOR-LABEL: define void @test_recursive_argmem_read_alloca +; ATTRIBUTOR-SAME: (ptr nocapture nofree nonnull readonly [[P:%.*]]) #[[ATTR17:[0-9]+]] { +; ATTRIBUTOR-NEXT: [[A:%.*]] = alloca ptr, align 8 +; ATTRIBUTOR-NEXT: [[TMP1:%.*]] = load i32, ptr [[P]], align 4 +; ATTRIBUTOR-NEXT: call void @test_recursive_argmem_read_alloca(ptr nocapture nofree nonnull readonly [[A]]) #[[ATTR15]] +; ATTRIBUTOR-NEXT: ret void ; %a = alloca ptr load i32, ptr %p @@ -308,24 +454,17 @@ } define void @test_scc_argmem_read_1(ptr %p) { -; CHECK: Function Attrs: nofree nosync nounwind memory(argmem: read) -; CHECK-LABEL: define void @test_scc_argmem_read_1 -; CHECK-SAME: (ptr nocapture readonly [[P:%.*]]) #[[ATTR16]] { -; CHECK-NEXT: [[PVAL:%.*]] = load ptr, ptr [[P]], align 8 -; CHECK-NEXT: call void @test_scc_argmem_read_2(ptr [[PVAL]]) -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: nofree nosync nounwind memory(none) +; FNATTRS-LABEL: define void @test_scc_argmem_read_1 +; FNATTRS-SAME: (ptr nocapture readnone [[P:%.*]]) #[[ATTR18:[0-9]+]] { +; FNATTRS-NEXT: call void @test_scc_argmem_read_1(ptr [[P]]) +; FNATTRS-NEXT: ret void ; - %pval = load ptr, ptr %p - call void @test_scc_argmem_read_2(ptr %pval) - ret void -} - -define void @test_scc_argmem_read_2(ptr %p) { -; CHECK: Function Attrs: nofree nosync nounwind memory(argmem: read) -; CHECK-LABEL: define void @test_scc_argmem_read_2 -; CHECK-SAME: (ptr nocapture readonly [[P:%.*]]) #[[ATTR16]] { -; CHECK-NEXT: call void @test_scc_argmem_read_1(ptr [[P]]) -; CHECK-NEXT: ret void +; ATTRIBUTOR: Function Attrs: nofree nosync nounwind memory(none) +; ATTRIBUTOR-LABEL: define void @test_scc_argmem_read_1 +; ATTRIBUTOR-SAME: (ptr nocapture nofree readnone [[P:%.*]]) #[[ATTR18:[0-9]+]] { +; ATTRIBUTOR-NEXT: call void @test_scc_argmem_read_1(ptr nocapture nofree readnone [[P]]) #[[ATTR18]] +; ATTRIBUTOR-NEXT: ret void ; call void @test_scc_argmem_read_1(ptr %p) ret void diff --git a/llvm/test/Transforms/FunctionAttrs/nocapture.ll b/llvm/test/Transforms/FunctionAttrs/nocapture.ll --- a/llvm/test/Transforms/FunctionAttrs/nocapture.ll +++ b/llvm/test/Transforms/FunctionAttrs/nocapture.ll @@ -1,48 +1,82 @@ -; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 2 -; RUN: opt -passes=function-attrs -S < %s | FileCheck %s +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --version 2 +; RUN: opt -passes=function-attrs -S < %s | FileCheck --check-prefixes=COMMON,FNATTRS %s +; RUN: opt -passes=attributor-light -S < %s | FileCheck --check-prefixes=COMMON,ATTRIBUTOR %s @g = global ptr null ; [#uses=1] define ptr @c1(ptr %q) { -; CHECK-LABEL: define ptr @c1 -; CHECK-SAME: (ptr readnone returned [[Q:%.*]]) #[[ATTR0:[0-9]+]] { -; CHECK-NEXT: ret ptr [[Q]] +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) +; FNATTRS-LABEL: define ptr @c1 +; FNATTRS-SAME: (ptr readnone returned [[Q:%.*]]) #[[ATTR0:[0-9]+]] { +; FNATTRS-NEXT: ret ptr [[Q]] +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) +; ATTRIBUTOR-LABEL: define ptr @c1 +; ATTRIBUTOR-SAME: (ptr nofree readnone [[Q:%.*]]) #[[ATTR0:[0-9]+]] { +; ATTRIBUTOR-NEXT: ret ptr [[Q]] ; ret ptr %q } ; It would also be acceptable to mark %q as readnone. Update @c3 too. define void @c2(ptr %q) { -; CHECK-LABEL: define void @c2 -; CHECK-SAME: (ptr [[Q:%.*]]) #[[ATTR1:[0-9]+]] { -; CHECK-NEXT: store ptr [[Q]], ptr @g, align 8 -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none) +; FNATTRS-LABEL: define void @c2 +; FNATTRS-SAME: (ptr [[Q:%.*]]) #[[ATTR1:[0-9]+]] { +; FNATTRS-NEXT: store ptr [[Q]], ptr @g, align 8 +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write) +; ATTRIBUTOR-LABEL: define void @c2 +; ATTRIBUTOR-SAME: (ptr nofree writeonly [[Q:%.*]]) #[[ATTR1:[0-9]+]] { +; ATTRIBUTOR-NEXT: store ptr [[Q]], ptr @g, align 8 +; ATTRIBUTOR-NEXT: ret void ; store ptr %q, ptr @g ret void } define void @c3(ptr %q) { -; CHECK-LABEL: define void @c3 -; CHECK-SAME: (ptr [[Q:%.*]]) #[[ATTR2:[0-9]+]] { -; CHECK-NEXT: call void @c2(ptr [[Q]]) -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, inaccessiblemem: none) +; FNATTRS-LABEL: define void @c3 +; FNATTRS-SAME: (ptr [[Q:%.*]]) #[[ATTR2:[0-9]+]] { +; FNATTRS-NEXT: call void @c2(ptr [[Q]]) +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write) +; ATTRIBUTOR-LABEL: define void @c3 +; ATTRIBUTOR-SAME: (ptr nofree writeonly [[Q:%.*]]) #[[ATTR1]] { +; ATTRIBUTOR-NEXT: call void @c2(ptr nofree writeonly [[Q]]) #[[ATTR16:[0-9]+]] +; ATTRIBUTOR-NEXT: ret void ; call void @c2(ptr %q) ret void } define i1 @c4(ptr %q, i32 %bitno) { -; CHECK-LABEL: define i1 @c4 -; CHECK-SAME: (ptr [[Q:%.*]], i32 [[BITNO:%.*]]) #[[ATTR0]] { -; CHECK-NEXT: [[TMP:%.*]] = ptrtoint ptr [[Q]] to i32 -; CHECK-NEXT: [[TMP2:%.*]] = lshr i32 [[TMP]], [[BITNO]] -; CHECK-NEXT: [[BIT:%.*]] = trunc i32 [[TMP2]] to i1 -; CHECK-NEXT: br i1 [[BIT]], label [[L1:%.*]], label [[L0:%.*]] -; CHECK: l0: -; CHECK-NEXT: ret i1 false -; CHECK: l1: -; CHECK-NEXT: ret i1 true +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) +; FNATTRS-LABEL: define i1 @c4 +; FNATTRS-SAME: (ptr [[Q:%.*]], i32 [[BITNO:%.*]]) #[[ATTR0]] { +; FNATTRS-NEXT: [[TMP:%.*]] = ptrtoint ptr [[Q]] to i32 +; FNATTRS-NEXT: [[TMP2:%.*]] = lshr i32 [[TMP]], [[BITNO]] +; FNATTRS-NEXT: [[BIT:%.*]] = trunc i32 [[TMP2]] to i1 +; FNATTRS-NEXT: br i1 [[BIT]], label [[L1:%.*]], label [[L0:%.*]] +; FNATTRS: l0: +; FNATTRS-NEXT: ret i1 false +; FNATTRS: l1: +; FNATTRS-NEXT: ret i1 true +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) +; ATTRIBUTOR-LABEL: define i1 @c4 +; ATTRIBUTOR-SAME: (ptr nofree readnone [[Q:%.*]], i32 [[BITNO:%.*]]) #[[ATTR0]] { +; ATTRIBUTOR-NEXT: [[TMP:%.*]] = ptrtoint ptr [[Q]] to i32 +; ATTRIBUTOR-NEXT: [[TMP2:%.*]] = lshr i32 [[TMP]], [[BITNO]] +; ATTRIBUTOR-NEXT: [[BIT:%.*]] = trunc i32 [[TMP2]] to i1 +; ATTRIBUTOR-NEXT: br i1 [[BIT]], label [[L1:%.*]], label [[L0:%.*]] +; ATTRIBUTOR: l0: +; ATTRIBUTOR-NEXT: ret i1 false +; ATTRIBUTOR: l1: +; ATTRIBUTOR-NEXT: ret i1 true ; %tmp = ptrtoint ptr %q to i32 %tmp2 = lshr i32 %tmp, %bitno @@ -56,16 +90,29 @@ ; c4b is c4 but without the escaping part define i1 @c4b(ptr %q, i32 %bitno) { -; CHECK-LABEL: define i1 @c4b -; CHECK-SAME: (ptr [[Q:%.*]], i32 [[BITNO:%.*]]) #[[ATTR0]] { -; CHECK-NEXT: [[TMP:%.*]] = ptrtoint ptr [[Q]] to i32 -; CHECK-NEXT: [[TMP2:%.*]] = lshr i32 [[TMP]], [[BITNO]] -; CHECK-NEXT: [[BIT:%.*]] = trunc i32 [[TMP2]] to i1 -; CHECK-NEXT: br i1 [[BIT]], label [[L1:%.*]], label [[L0:%.*]] -; CHECK: l0: -; CHECK-NEXT: ret i1 false -; CHECK: l1: -; CHECK-NEXT: ret i1 false +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) +; FNATTRS-LABEL: define i1 @c4b +; FNATTRS-SAME: (ptr [[Q:%.*]], i32 [[BITNO:%.*]]) #[[ATTR0]] { +; FNATTRS-NEXT: [[TMP:%.*]] = ptrtoint ptr [[Q]] to i32 +; FNATTRS-NEXT: [[TMP2:%.*]] = lshr i32 [[TMP]], [[BITNO]] +; FNATTRS-NEXT: [[BIT:%.*]] = trunc i32 [[TMP2]] to i1 +; FNATTRS-NEXT: br i1 [[BIT]], label [[L1:%.*]], label [[L0:%.*]] +; FNATTRS: l0: +; FNATTRS-NEXT: ret i1 false +; FNATTRS: l1: +; FNATTRS-NEXT: ret i1 false +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) +; ATTRIBUTOR-LABEL: define i1 @c4b +; ATTRIBUTOR-SAME: (ptr nofree readnone [[Q:%.*]], i32 [[BITNO:%.*]]) #[[ATTR0]] { +; ATTRIBUTOR-NEXT: [[TMP:%.*]] = ptrtoint ptr [[Q]] to i32 +; ATTRIBUTOR-NEXT: [[TMP2:%.*]] = lshr i32 [[TMP]], [[BITNO]] +; ATTRIBUTOR-NEXT: [[BIT:%.*]] = trunc i32 [[TMP2]] to i1 +; ATTRIBUTOR-NEXT: br i1 [[BIT]], label [[L1:%.*]], label [[L0:%.*]] +; ATTRIBUTOR: l0: +; ATTRIBUTOR-NEXT: ret i1 false +; ATTRIBUTOR: l1: +; ATTRIBUTOR-NEXT: ret i1 false ; %tmp = ptrtoint ptr %q to i32 %tmp2 = lshr i32 %tmp, %bitno @@ -80,14 +127,25 @@ @lookup_table = global [2 x i1] [ i1 0, i1 1 ] define i1 @c5(ptr %q, i32 %bitno) { -; CHECK-LABEL: define i1 @c5 -; CHECK-SAME: (ptr [[Q:%.*]], i32 [[BITNO:%.*]]) #[[ATTR3:[0-9]+]] { -; CHECK-NEXT: [[TMP:%.*]] = ptrtoint ptr [[Q]] to i32 -; CHECK-NEXT: [[TMP2:%.*]] = lshr i32 [[TMP]], [[BITNO]] -; CHECK-NEXT: [[BIT:%.*]] = and i32 [[TMP2]], 1 -; CHECK-NEXT: [[LOOKUP:%.*]] = getelementptr [2 x i1], ptr @lookup_table, i32 0, i32 [[BIT]] -; CHECK-NEXT: [[VAL:%.*]] = load i1, ptr [[LOOKUP]], align 1 -; CHECK-NEXT: ret i1 [[VAL]] +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, argmem: none, inaccessiblemem: none) +; FNATTRS-LABEL: define i1 @c5 +; FNATTRS-SAME: (ptr [[Q:%.*]], i32 [[BITNO:%.*]]) #[[ATTR3:[0-9]+]] { +; FNATTRS-NEXT: [[TMP:%.*]] = ptrtoint ptr [[Q]] to i32 +; FNATTRS-NEXT: [[TMP2:%.*]] = lshr i32 [[TMP]], [[BITNO]] +; FNATTRS-NEXT: [[BIT:%.*]] = and i32 [[TMP2]], 1 +; FNATTRS-NEXT: [[LOOKUP:%.*]] = getelementptr [2 x i1], ptr @lookup_table, i32 0, i32 [[BIT]] +; FNATTRS-NEXT: [[VAL:%.*]] = load i1, ptr [[LOOKUP]], align 1 +; FNATTRS-NEXT: ret i1 [[VAL]] +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read) +; ATTRIBUTOR-LABEL: define i1 @c5 +; ATTRIBUTOR-SAME: (ptr nofree readonly [[Q:%.*]], i32 [[BITNO:%.*]]) #[[ATTR2:[0-9]+]] { +; ATTRIBUTOR-NEXT: [[TMP:%.*]] = ptrtoint ptr [[Q]] to i32 +; ATTRIBUTOR-NEXT: [[TMP2:%.*]] = lshr i32 [[TMP]], [[BITNO]] +; ATTRIBUTOR-NEXT: [[BIT:%.*]] = and i32 [[TMP2]], 1 +; ATTRIBUTOR-NEXT: [[LOOKUP:%.*]] = getelementptr [2 x i1], ptr @lookup_table, i32 0, i32 [[BIT]] +; ATTRIBUTOR-NEXT: [[VAL:%.*]] = load i1, ptr [[LOOKUP]], align 1 +; ATTRIBUTOR-NEXT: ret i1 [[VAL]] ; %tmp = ptrtoint ptr %q to i32 %tmp2 = lshr i32 %tmp, %bitno @@ -101,16 +159,29 @@ declare void @throw_if_bit_set(ptr, i8) readonly define i1 @c6(ptr %q, i8 %bit) personality ptr @__gxx_personality_v0 { -; CHECK-LABEL: define i1 @c6 -; CHECK-SAME: (ptr readonly [[Q:%.*]], i8 [[BIT:%.*]]) #[[ATTR5:[0-9]+]] personality ptr @__gxx_personality_v0 { -; CHECK-NEXT: invoke void @throw_if_bit_set(ptr [[Q]], i8 [[BIT]]) -; CHECK-NEXT: to label [[RET0:%.*]] unwind label [[RET1:%.*]] -; CHECK: ret0: -; CHECK-NEXT: ret i1 false -; CHECK: ret1: -; CHECK-NEXT: [[EXN:%.*]] = landingpad { ptr, i32 } -; CHECK-NEXT: cleanup -; CHECK-NEXT: ret i1 true +; FNATTRS: Function Attrs: nofree memory(read) +; FNATTRS-LABEL: define i1 @c6 +; FNATTRS-SAME: (ptr readonly [[Q:%.*]], i8 [[BIT:%.*]]) #[[ATTR5:[0-9]+]] personality ptr @__gxx_personality_v0 { +; FNATTRS-NEXT: invoke void @throw_if_bit_set(ptr [[Q]], i8 [[BIT]]) +; FNATTRS-NEXT: to label [[RET0:%.*]] unwind label [[RET1:%.*]] +; FNATTRS: ret0: +; FNATTRS-NEXT: ret i1 false +; FNATTRS: ret1: +; FNATTRS-NEXT: [[EXN:%.*]] = landingpad { ptr, i32 } +; FNATTRS-NEXT: cleanup +; FNATTRS-NEXT: ret i1 true +; +; ATTRIBUTOR: Function Attrs: nosync memory(read) +; ATTRIBUTOR-LABEL: define i1 @c6 +; ATTRIBUTOR-SAME: (ptr readonly [[Q:%.*]], i8 [[BIT:%.*]]) #[[ATTR4:[0-9]+]] personality ptr @__gxx_personality_v0 { +; ATTRIBUTOR-NEXT: invoke void @throw_if_bit_set(ptr [[Q]], i8 [[BIT]]) #[[ATTR4]] +; ATTRIBUTOR-NEXT: to label [[RET0:%.*]] unwind label [[RET1:%.*]] +; ATTRIBUTOR: ret0: +; ATTRIBUTOR-NEXT: ret i1 false +; ATTRIBUTOR: ret1: +; ATTRIBUTOR-NEXT: [[EXN:%.*]] = landingpad { ptr, i32 } +; ATTRIBUTOR-NEXT: cleanup +; ATTRIBUTOR-NEXT: ret i1 true ; invoke void @throw_if_bit_set(ptr %q, i8 %bit) to label %ret0 unwind label %ret1 @@ -125,13 +196,23 @@ declare i32 @__gxx_personality_v0(...) define ptr @lookup_bit(ptr %q, i32 %bitno) readnone nounwind { -; CHECK-LABEL: define nonnull ptr @lookup_bit -; CHECK-SAME: (ptr [[Q:%.*]], i32 [[BITNO:%.*]]) #[[ATTR0]] { -; CHECK-NEXT: [[TMP:%.*]] = ptrtoint ptr [[Q]] to i32 -; CHECK-NEXT: [[TMP2:%.*]] = lshr i32 [[TMP]], [[BITNO]] -; CHECK-NEXT: [[BIT:%.*]] = and i32 [[TMP2]], 1 -; CHECK-NEXT: [[LOOKUP:%.*]] = getelementptr [2 x i1], ptr @lookup_table, i32 0, i32 [[BIT]] -; CHECK-NEXT: ret ptr [[LOOKUP]] +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) +; FNATTRS-LABEL: define nonnull ptr @lookup_bit +; FNATTRS-SAME: (ptr [[Q:%.*]], i32 [[BITNO:%.*]]) #[[ATTR0]] { +; FNATTRS-NEXT: [[TMP:%.*]] = ptrtoint ptr [[Q]] to i32 +; FNATTRS-NEXT: [[TMP2:%.*]] = lshr i32 [[TMP]], [[BITNO]] +; FNATTRS-NEXT: [[BIT:%.*]] = and i32 [[TMP2]], 1 +; FNATTRS-NEXT: [[LOOKUP:%.*]] = getelementptr [2 x i1], ptr @lookup_table, i32 0, i32 [[BIT]] +; FNATTRS-NEXT: ret ptr [[LOOKUP]] +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) +; ATTRIBUTOR-LABEL: define ptr @lookup_bit +; ATTRIBUTOR-SAME: (ptr nofree readnone [[Q:%.*]], i32 [[BITNO:%.*]]) #[[ATTR0]] { +; ATTRIBUTOR-NEXT: [[TMP:%.*]] = ptrtoint ptr [[Q]] to i32 +; ATTRIBUTOR-NEXT: [[TMP2:%.*]] = lshr i32 [[TMP]], [[BITNO]] +; ATTRIBUTOR-NEXT: [[BIT:%.*]] = and i32 [[TMP2]], 1 +; ATTRIBUTOR-NEXT: [[LOOKUP:%.*]] = getelementptr [2 x i1], ptr @lookup_table, i32 0, i32 [[BIT]] +; ATTRIBUTOR-NEXT: ret ptr [[LOOKUP]] ; %tmp = ptrtoint ptr %q to i32 %tmp2 = lshr i32 %tmp, %bitno @@ -141,11 +222,19 @@ } define i1 @c7(ptr %q, i32 %bitno) { -; CHECK-LABEL: define i1 @c7 -; CHECK-SAME: (ptr readonly [[Q:%.*]], i32 [[BITNO:%.*]]) #[[ATTR6:[0-9]+]] { -; CHECK-NEXT: [[PTR:%.*]] = call ptr @lookup_bit(ptr [[Q]], i32 [[BITNO]]) -; CHECK-NEXT: [[VAL:%.*]] = load i1, ptr [[PTR]], align 1 -; CHECK-NEXT: ret i1 [[VAL]] +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, inaccessiblemem: none) +; FNATTRS-LABEL: define i1 @c7 +; FNATTRS-SAME: (ptr readonly [[Q:%.*]], i32 [[BITNO:%.*]]) #[[ATTR6:[0-9]+]] { +; FNATTRS-NEXT: [[PTR:%.*]] = call ptr @lookup_bit(ptr [[Q]], i32 [[BITNO]]) +; FNATTRS-NEXT: [[VAL:%.*]] = load i1, ptr [[PTR]], align 1 +; FNATTRS-NEXT: ret i1 [[VAL]] +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read) +; ATTRIBUTOR-LABEL: define i1 @c7 +; ATTRIBUTOR-SAME: (ptr nofree readonly [[Q:%.*]], i32 [[BITNO:%.*]]) #[[ATTR2]] { +; ATTRIBUTOR-NEXT: [[PTR:%.*]] = call ptr @lookup_bit(ptr nofree readnone [[Q]], i32 [[BITNO]]) #[[ATTR17:[0-9]+]] +; ATTRIBUTOR-NEXT: [[VAL:%.*]] = load i1, ptr [[PTR]], align 1 +; ATTRIBUTOR-NEXT: ret i1 [[VAL]] ; %ptr = call ptr @lookup_bit(ptr %q, i32 %bitno) %val = load i1, ptr %ptr @@ -154,18 +243,33 @@ define i32 @nc1(ptr %q, ptr %p, i1 %b) { -; CHECK-LABEL: define i32 @nc1 -; CHECK-SAME: (ptr [[Q:%.*]], ptr nocapture [[P:%.*]], i1 [[B:%.*]]) #[[ATTR7:[0-9]+]] { -; CHECK-NEXT: e: -; CHECK-NEXT: br label [[L:%.*]] -; CHECK: l: -; CHECK-NEXT: [[X:%.*]] = phi ptr [ [[P]], [[E:%.*]] ] -; CHECK-NEXT: [[Y:%.*]] = phi ptr [ [[Q]], [[E]] ] -; CHECK-NEXT: [[TMP2:%.*]] = select i1 [[B]], ptr [[X]], ptr [[Y]] -; CHECK-NEXT: [[VAL:%.*]] = load i32, ptr [[TMP2]], align 4 -; CHECK-NEXT: store i32 0, ptr [[X]], align 4 -; CHECK-NEXT: store ptr [[Y]], ptr @g, align 8 -; CHECK-NEXT: ret i32 [[VAL]] +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none) +; FNATTRS-LABEL: define i32 @nc1 +; FNATTRS-SAME: (ptr [[Q:%.*]], ptr nocapture [[P:%.*]], i1 [[B:%.*]]) #[[ATTR7:[0-9]+]] { +; FNATTRS-NEXT: e: +; FNATTRS-NEXT: br label [[L:%.*]] +; FNATTRS: l: +; FNATTRS-NEXT: [[X:%.*]] = phi ptr [ [[P]], [[E:%.*]] ] +; FNATTRS-NEXT: [[Y:%.*]] = phi ptr [ [[Q]], [[E]] ] +; FNATTRS-NEXT: [[TMP2:%.*]] = select i1 [[B]], ptr [[X]], ptr [[Y]] +; FNATTRS-NEXT: [[VAL:%.*]] = load i32, ptr [[TMP2]], align 4 +; FNATTRS-NEXT: store i32 0, ptr [[X]], align 4 +; FNATTRS-NEXT: store ptr [[Y]], ptr @g, align 8 +; FNATTRS-NEXT: ret i32 [[VAL]] +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn +; ATTRIBUTOR-LABEL: define i32 @nc1 +; ATTRIBUTOR-SAME: (ptr nofree [[Q:%.*]], ptr nocapture nofree [[P:%.*]], i1 [[B:%.*]]) #[[ATTR5:[0-9]+]] { +; ATTRIBUTOR-NEXT: e: +; ATTRIBUTOR-NEXT: br label [[L:%.*]] +; ATTRIBUTOR: l: +; ATTRIBUTOR-NEXT: [[X:%.*]] = phi ptr [ [[P]], [[E:%.*]] ] +; ATTRIBUTOR-NEXT: [[Y:%.*]] = phi ptr [ [[Q]], [[E]] ] +; ATTRIBUTOR-NEXT: [[TMP2:%.*]] = select i1 [[B]], ptr [[X]], ptr [[Y]] +; ATTRIBUTOR-NEXT: [[VAL:%.*]] = load i32, ptr [[TMP2]], align 4 +; ATTRIBUTOR-NEXT: store i32 0, ptr [[X]], align 4 +; ATTRIBUTOR-NEXT: store ptr [[Y]], ptr @g, align 8 +; ATTRIBUTOR-NEXT: ret i32 [[VAL]] ; e: br label %l @@ -180,19 +284,35 @@ } define i32 @nc1_addrspace(ptr %q, ptr addrspace(1) %p, i1 %b) { -; CHECK-LABEL: define i32 @nc1_addrspace -; CHECK-SAME: (ptr [[Q:%.*]], ptr addrspace(1) nocapture [[P:%.*]], i1 [[B:%.*]]) #[[ATTR7]] { -; CHECK-NEXT: e: -; CHECK-NEXT: br label [[L:%.*]] -; CHECK: l: -; CHECK-NEXT: [[X:%.*]] = phi ptr addrspace(1) [ [[P]], [[E:%.*]] ] -; CHECK-NEXT: [[Y:%.*]] = phi ptr [ [[Q]], [[E]] ] -; CHECK-NEXT: [[TMP:%.*]] = addrspacecast ptr addrspace(1) [[X]] to ptr -; CHECK-NEXT: [[TMP2:%.*]] = select i1 [[B]], ptr [[TMP]], ptr [[Y]] -; CHECK-NEXT: [[VAL:%.*]] = load i32, ptr [[TMP2]], align 4 -; CHECK-NEXT: store i32 0, ptr [[TMP]], align 4 -; CHECK-NEXT: store ptr [[Y]], ptr @g, align 8 -; CHECK-NEXT: ret i32 [[VAL]] +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none) +; FNATTRS-LABEL: define i32 @nc1_addrspace +; FNATTRS-SAME: (ptr [[Q:%.*]], ptr addrspace(1) nocapture [[P:%.*]], i1 [[B:%.*]]) #[[ATTR7]] { +; FNATTRS-NEXT: e: +; FNATTRS-NEXT: br label [[L:%.*]] +; FNATTRS: l: +; FNATTRS-NEXT: [[X:%.*]] = phi ptr addrspace(1) [ [[P]], [[E:%.*]] ] +; FNATTRS-NEXT: [[Y:%.*]] = phi ptr [ [[Q]], [[E]] ] +; FNATTRS-NEXT: [[TMP:%.*]] = addrspacecast ptr addrspace(1) [[X]] to ptr +; FNATTRS-NEXT: [[TMP2:%.*]] = select i1 [[B]], ptr [[TMP]], ptr [[Y]] +; FNATTRS-NEXT: [[VAL:%.*]] = load i32, ptr [[TMP2]], align 4 +; FNATTRS-NEXT: store i32 0, ptr [[TMP]], align 4 +; FNATTRS-NEXT: store ptr [[Y]], ptr @g, align 8 +; FNATTRS-NEXT: ret i32 [[VAL]] +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn +; ATTRIBUTOR-LABEL: define i32 @nc1_addrspace +; ATTRIBUTOR-SAME: (ptr nofree [[Q:%.*]], ptr addrspace(1) nocapture nofree [[P:%.*]], i1 [[B:%.*]]) #[[ATTR5]] { +; ATTRIBUTOR-NEXT: e: +; ATTRIBUTOR-NEXT: br label [[L:%.*]] +; ATTRIBUTOR: l: +; ATTRIBUTOR-NEXT: [[X:%.*]] = phi ptr addrspace(1) [ [[P]], [[E:%.*]] ] +; ATTRIBUTOR-NEXT: [[Y:%.*]] = phi ptr [ [[Q]], [[E]] ] +; ATTRIBUTOR-NEXT: [[TMP:%.*]] = addrspacecast ptr addrspace(1) [[X]] to ptr +; ATTRIBUTOR-NEXT: [[TMP2:%.*]] = select i1 [[B]], ptr [[TMP]], ptr [[Y]] +; ATTRIBUTOR-NEXT: [[VAL:%.*]] = load i32, ptr [[TMP2]], align 4 +; ATTRIBUTOR-NEXT: store i32 0, ptr [[TMP]], align 4 +; ATTRIBUTOR-NEXT: store ptr [[Y]], ptr @g, align 8 +; ATTRIBUTOR-NEXT: ret i32 [[VAL]] ; e: br label %l @@ -208,10 +328,17 @@ } define void @nc2(ptr %p, ptr %q) { -; CHECK-LABEL: define void @nc2 -; CHECK-SAME: (ptr nocapture [[P:%.*]], ptr [[Q:%.*]]) #[[ATTR7]] { -; CHECK-NEXT: [[TMP1:%.*]] = call i32 @nc1(ptr [[Q]], ptr [[P]], i1 false) -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none) +; FNATTRS-LABEL: define void @nc2 +; FNATTRS-SAME: (ptr nocapture [[P:%.*]], ptr [[Q:%.*]]) #[[ATTR7]] { +; FNATTRS-NEXT: [[TMP1:%.*]] = call i32 @nc1(ptr [[Q]], ptr [[P]], i1 false) +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn +; ATTRIBUTOR-LABEL: define void @nc2 +; ATTRIBUTOR-SAME: (ptr nocapture nofree [[P:%.*]], ptr nofree [[Q:%.*]]) #[[ATTR5]] { +; ATTRIBUTOR-NEXT: [[TMP1:%.*]] = call i32 @nc1(ptr nofree [[Q]], ptr nocapture nofree [[P]], i1 false) #[[ATTR18:[0-9]+]] +; ATTRIBUTOR-NEXT: ret void ; %1 = call i32 @nc1(ptr %q, ptr %p, i1 0) ; [#uses=0] ret void @@ -219,10 +346,15 @@ define void @nc3(ptr %p) { -; CHECK-LABEL: define void @nc3 -; CHECK-SAME: (ptr nocapture readonly [[P:%.*]]) { -; CHECK-NEXT: call void [[P]]() -; CHECK-NEXT: ret void +; FNATTRS-LABEL: define void @nc3 +; FNATTRS-SAME: (ptr nocapture readonly [[P:%.*]]) { +; FNATTRS-NEXT: call void [[P]]() +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR-LABEL: define void @nc3 +; ATTRIBUTOR-SAME: (ptr nocapture nofree nonnull [[P:%.*]]) { +; ATTRIBUTOR-NEXT: call void [[P]]() +; ATTRIBUTOR-NEXT: ret void ; call void %p() ret void @@ -230,21 +362,34 @@ declare void @external(ptr) readonly nounwind define void @nc4(ptr %p) { -; CHECK-LABEL: define void @nc4 -; CHECK-SAME: (ptr nocapture readonly [[P:%.*]]) #[[ATTR9:[0-9]+]] { -; CHECK-NEXT: call void @external(ptr [[P]]) -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: nofree nounwind memory(read) +; FNATTRS-LABEL: define void @nc4 +; FNATTRS-SAME: (ptr nocapture readonly [[P:%.*]]) #[[ATTR9:[0-9]+]] { +; FNATTRS-NEXT: call void @external(ptr [[P]]) +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: nosync nounwind memory(read) +; ATTRIBUTOR-LABEL: define void @nc4 +; ATTRIBUTOR-SAME: (ptr nocapture readonly [[P:%.*]]) #[[ATTR7:[0-9]+]] { +; ATTRIBUTOR-NEXT: call void @external(ptr nocapture readonly [[P]]) #[[ATTR4]] +; ATTRIBUTOR-NEXT: ret void ; call void @external(ptr %p) ret void } define void @nc5(ptr %f, ptr %p) { -; CHECK-LABEL: define void @nc5 -; CHECK-SAME: (ptr nocapture readonly [[F:%.*]], ptr nocapture [[P:%.*]]) { -; CHECK-NEXT: call void [[F]](ptr [[P]]) #[[ATTR8:[0-9]+]] -; CHECK-NEXT: call void [[F]](ptr nocapture [[P]]) -; CHECK-NEXT: ret void +; FNATTRS-LABEL: define void @nc5 +; FNATTRS-SAME: (ptr nocapture readonly [[F:%.*]], ptr nocapture [[P:%.*]]) { +; FNATTRS-NEXT: call void [[F]](ptr [[P]]) #[[ATTR8:[0-9]+]] +; FNATTRS-NEXT: call void [[F]](ptr nocapture [[P]]) +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR-LABEL: define void @nc5 +; ATTRIBUTOR-SAME: (ptr nocapture nofree nonnull [[F:%.*]], ptr nocapture [[P:%.*]]) { +; ATTRIBUTOR-NEXT: call void [[F]](ptr [[P]]) #[[ATTR6:[0-9]+]] +; ATTRIBUTOR-NEXT: call void [[F]](ptr nocapture [[P]]) +; ATTRIBUTOR-NEXT: ret void ; call void %f(ptr %p) readonly nounwind call void %f(ptr nocapture %p) @@ -253,11 +398,19 @@ ; It would be acceptable to add readnone to %y1_1 and %y1_2. define void @test1_1(ptr %x1_1, ptr %y1_1, i1 %c) { -; CHECK-LABEL: define void @test1_1 -; CHECK-SAME: (ptr nocapture readnone [[X1_1:%.*]], ptr [[Y1_1:%.*]], i1 [[C:%.*]]) #[[ATTR10:[0-9]+]] { -; CHECK-NEXT: [[TMP1:%.*]] = call ptr @test1_2(ptr [[X1_1]], ptr [[Y1_1]], i1 [[C]]) -; CHECK-NEXT: store ptr null, ptr @g, align 8 -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: nofree nosync nounwind memory(write, argmem: none, inaccessiblemem: none) +; FNATTRS-LABEL: define void @test1_1 +; FNATTRS-SAME: (ptr nocapture readnone [[X1_1:%.*]], ptr [[Y1_1:%.*]], i1 [[C:%.*]]) #[[ATTR10:[0-9]+]] { +; FNATTRS-NEXT: [[TMP1:%.*]] = call ptr @test1_2(ptr [[X1_1]], ptr [[Y1_1]], i1 [[C]]) +; FNATTRS-NEXT: store ptr null, ptr @g, align 8 +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: nofree nosync nounwind memory(write) +; ATTRIBUTOR-LABEL: define void @test1_1 +; ATTRIBUTOR-SAME: (ptr nocapture nofree readnone [[X1_1:%.*]], ptr nocapture nofree readnone [[Y1_1:%.*]], i1 [[C:%.*]]) #[[ATTR8:[0-9]+]] { +; ATTRIBUTOR-NEXT: [[TMP1:%.*]] = call ptr @test1_2(ptr nocapture nofree readnone [[X1_1]], ptr nofree readnone [[Y1_1]], i1 [[C]]) #[[ATTR8]] +; ATTRIBUTOR-NEXT: store ptr null, ptr @g, align 8 +; ATTRIBUTOR-NEXT: ret void ; call ptr @test1_2(ptr %x1_1, ptr %y1_1, i1 %c) store ptr null, ptr @g @@ -265,15 +418,27 @@ } define ptr @test1_2(ptr %x1_2, ptr %y1_2, i1 %c) { -; CHECK-LABEL: define ptr @test1_2 -; CHECK-SAME: (ptr nocapture readnone [[X1_2:%.*]], ptr returned [[Y1_2:%.*]], i1 [[C:%.*]]) #[[ATTR10]] { -; CHECK-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] -; CHECK: t: -; CHECK-NEXT: call void @test1_1(ptr [[X1_2]], ptr [[Y1_2]], i1 [[C]]) -; CHECK-NEXT: store ptr null, ptr @g, align 8 -; CHECK-NEXT: br label [[F]] -; CHECK: f: -; CHECK-NEXT: ret ptr [[Y1_2]] +; FNATTRS: Function Attrs: nofree nosync nounwind memory(write, argmem: none, inaccessiblemem: none) +; FNATTRS-LABEL: define ptr @test1_2 +; FNATTRS-SAME: (ptr nocapture readnone [[X1_2:%.*]], ptr returned [[Y1_2:%.*]], i1 [[C:%.*]]) #[[ATTR10]] { +; FNATTRS-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] +; FNATTRS: t: +; FNATTRS-NEXT: call void @test1_1(ptr [[X1_2]], ptr [[Y1_2]], i1 [[C]]) +; FNATTRS-NEXT: store ptr null, ptr @g, align 8 +; FNATTRS-NEXT: br label [[F]] +; FNATTRS: f: +; FNATTRS-NEXT: ret ptr [[Y1_2]] +; +; ATTRIBUTOR: Function Attrs: nofree nosync nounwind memory(write) +; ATTRIBUTOR-LABEL: define ptr @test1_2 +; ATTRIBUTOR-SAME: (ptr nocapture nofree readnone [[X1_2:%.*]], ptr nofree readnone [[Y1_2:%.*]], i1 [[C:%.*]]) #[[ATTR8]] { +; ATTRIBUTOR-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] +; ATTRIBUTOR: t: +; ATTRIBUTOR-NEXT: call void @test1_1(ptr nocapture nofree readnone [[X1_2]], ptr nocapture nofree readnone [[Y1_2]], i1 [[C]]) #[[ATTR8]] +; ATTRIBUTOR-NEXT: store ptr null, ptr @g, align 8 +; ATTRIBUTOR-NEXT: br label [[F]] +; ATTRIBUTOR: f: +; ATTRIBUTOR-NEXT: ret ptr [[Y1_2]] ; br i1 %c, label %t, label %f t: @@ -285,11 +450,19 @@ } define void @test2(ptr %x2) { -; CHECK-LABEL: define void @test2 -; CHECK-SAME: (ptr nocapture readnone [[X2:%.*]]) #[[ATTR10]] { -; CHECK-NEXT: call void @test2(ptr [[X2]]) -; CHECK-NEXT: store ptr null, ptr @g, align 8 -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: nofree nosync nounwind memory(write, argmem: none, inaccessiblemem: none) +; FNATTRS-LABEL: define void @test2 +; FNATTRS-SAME: (ptr nocapture readnone [[X2:%.*]]) #[[ATTR10]] { +; FNATTRS-NEXT: call void @test2(ptr [[X2]]) +; FNATTRS-NEXT: store ptr null, ptr @g, align 8 +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: nofree nosync nounwind memory(write) +; ATTRIBUTOR-LABEL: define void @test2 +; ATTRIBUTOR-SAME: (ptr nocapture nofree readnone [[X2:%.*]]) #[[ATTR8]] { +; ATTRIBUTOR-NEXT: call void @test2(ptr nocapture nofree readnone [[X2]]) #[[ATTR8]] +; ATTRIBUTOR-NEXT: store ptr null, ptr @g, align 8 +; ATTRIBUTOR-NEXT: ret void ; call void @test2(ptr %x2) store ptr null, ptr @g @@ -297,11 +470,19 @@ } define void @test3(ptr %x3, ptr %y3, ptr %z3) { -; CHECK-LABEL: define void @test3 -; CHECK-SAME: (ptr nocapture readnone [[X3:%.*]], ptr nocapture readnone [[Y3:%.*]], ptr nocapture readnone [[Z3:%.*]]) #[[ATTR10]] { -; CHECK-NEXT: call void @test3(ptr [[Z3]], ptr [[Y3]], ptr [[X3]]) -; CHECK-NEXT: store ptr null, ptr @g, align 8 -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: nofree nosync nounwind memory(write, argmem: none, inaccessiblemem: none) +; FNATTRS-LABEL: define void @test3 +; FNATTRS-SAME: (ptr nocapture readnone [[X3:%.*]], ptr nocapture readnone [[Y3:%.*]], ptr nocapture readnone [[Z3:%.*]]) #[[ATTR10]] { +; FNATTRS-NEXT: call void @test3(ptr [[Z3]], ptr [[Y3]], ptr [[X3]]) +; FNATTRS-NEXT: store ptr null, ptr @g, align 8 +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: nofree nosync nounwind memory(write) +; ATTRIBUTOR-LABEL: define void @test3 +; ATTRIBUTOR-SAME: (ptr nocapture nofree readnone [[X3:%.*]], ptr nocapture nofree readnone [[Y3:%.*]], ptr nocapture nofree readnone [[Z3:%.*]]) #[[ATTR8]] { +; ATTRIBUTOR-NEXT: call void @test3(ptr nocapture nofree readnone [[Z3]], ptr nocapture nofree readnone [[Y3]], ptr nocapture nofree readnone [[X3]]) #[[ATTR8]] +; ATTRIBUTOR-NEXT: store ptr null, ptr @g, align 8 +; ATTRIBUTOR-NEXT: ret void ; call void @test3(ptr %z3, ptr %y3, ptr %x3) store ptr null, ptr @g @@ -309,11 +490,19 @@ } define void @test4_1(ptr %x4_1, i1 %c) { -; CHECK-LABEL: define void @test4_1 -; CHECK-SAME: (ptr [[X4_1:%.*]], i1 [[C:%.*]]) #[[ATTR10]] { -; CHECK-NEXT: [[TMP1:%.*]] = call ptr @test4_2(ptr [[X4_1]], ptr [[X4_1]], ptr [[X4_1]], i1 [[C]]) -; CHECK-NEXT: store ptr null, ptr @g, align 8 -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: nofree nosync nounwind memory(write, argmem: none, inaccessiblemem: none) +; FNATTRS-LABEL: define void @test4_1 +; FNATTRS-SAME: (ptr [[X4_1:%.*]], i1 [[C:%.*]]) #[[ATTR10]] { +; FNATTRS-NEXT: [[TMP1:%.*]] = call ptr @test4_2(ptr [[X4_1]], ptr [[X4_1]], ptr [[X4_1]], i1 [[C]]) +; FNATTRS-NEXT: store ptr null, ptr @g, align 8 +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: nofree nosync nounwind memory(write) +; ATTRIBUTOR-LABEL: define void @test4_1 +; ATTRIBUTOR-SAME: (ptr nocapture nofree readnone [[X4_1:%.*]], i1 [[C:%.*]]) #[[ATTR8]] { +; ATTRIBUTOR-NEXT: [[TMP1:%.*]] = call ptr @test4_2(ptr nocapture nofree readnone [[X4_1]], ptr nofree readnone [[X4_1]], ptr nocapture nofree readnone [[X4_1]], i1 [[C]]) #[[ATTR8]] +; ATTRIBUTOR-NEXT: store ptr null, ptr @g, align 8 +; ATTRIBUTOR-NEXT: ret void ; call ptr @test4_2(ptr %x4_1, ptr %x4_1, ptr %x4_1, i1 %c) store ptr null, ptr @g @@ -321,15 +510,27 @@ } define ptr @test4_2(ptr %x4_2, ptr %y4_2, ptr %z4_2, i1 %c) { -; CHECK-LABEL: define ptr @test4_2 -; CHECK-SAME: (ptr nocapture readnone [[X4_2:%.*]], ptr readnone returned [[Y4_2:%.*]], ptr nocapture readnone [[Z4_2:%.*]], i1 [[C:%.*]]) #[[ATTR10]] { -; CHECK-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] -; CHECK: t: -; CHECK-NEXT: call void @test4_1(ptr null, i1 [[C]]) -; CHECK-NEXT: store ptr null, ptr @g, align 8 -; CHECK-NEXT: br label [[F]] -; CHECK: f: -; CHECK-NEXT: ret ptr [[Y4_2]] +; FNATTRS: Function Attrs: nofree nosync nounwind memory(write, argmem: none, inaccessiblemem: none) +; FNATTRS-LABEL: define ptr @test4_2 +; FNATTRS-SAME: (ptr nocapture readnone [[X4_2:%.*]], ptr readnone returned [[Y4_2:%.*]], ptr nocapture readnone [[Z4_2:%.*]], i1 [[C:%.*]]) #[[ATTR10]] { +; FNATTRS-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] +; FNATTRS: t: +; FNATTRS-NEXT: call void @test4_1(ptr null, i1 [[C]]) +; FNATTRS-NEXT: store ptr null, ptr @g, align 8 +; FNATTRS-NEXT: br label [[F]] +; FNATTRS: f: +; FNATTRS-NEXT: ret ptr [[Y4_2]] +; +; ATTRIBUTOR: Function Attrs: nofree nosync nounwind memory(write) +; ATTRIBUTOR-LABEL: define ptr @test4_2 +; ATTRIBUTOR-SAME: (ptr nocapture nofree readnone [[X4_2:%.*]], ptr nofree readnone [[Y4_2:%.*]], ptr nocapture nofree readnone [[Z4_2:%.*]], i1 [[C:%.*]]) #[[ATTR8]] { +; ATTRIBUTOR-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] +; ATTRIBUTOR: t: +; ATTRIBUTOR-NEXT: call void @test4_1(ptr nofree readnone null, i1 [[C]]) #[[ATTR8]] +; ATTRIBUTOR-NEXT: store ptr null, ptr @g, align 8 +; ATTRIBUTOR-NEXT: br label [[F]] +; ATTRIBUTOR: f: +; ATTRIBUTOR-NEXT: ret ptr [[Y4_2]] ; br i1 %c, label %t, label %f t: @@ -343,11 +544,11 @@ declare ptr @test5_1(ptr %x5_1) define void @test5_2(ptr %x5_2) { -; CHECK-LABEL: define void @test5_2 -; CHECK-SAME: (ptr [[X5_2:%.*]]) { -; CHECK-NEXT: [[TMP1:%.*]] = call ptr @test5_1(ptr [[X5_2]]) -; CHECK-NEXT: store ptr null, ptr @g, align 8 -; CHECK-NEXT: ret void +; COMMON-LABEL: define void @test5_2 +; COMMON-SAME: (ptr [[X5_2:%.*]]) { +; COMMON-NEXT: [[TMP1:%.*]] = call ptr @test5_1(ptr [[X5_2]]) +; COMMON-NEXT: store ptr null, ptr @g, align 8 +; COMMON-NEXT: ret void ; call ptr @test5_1(ptr %x5_2) store ptr null, ptr @g @@ -357,11 +558,17 @@ declare void @test6_1(ptr %x6_1, ptr nocapture %y6_1, ...) define void @test6_2(ptr %x6_2, ptr %y6_2, ptr %z6_2) { -; CHECK-LABEL: define void @test6_2 -; CHECK-SAME: (ptr [[X6_2:%.*]], ptr nocapture [[Y6_2:%.*]], ptr [[Z6_2:%.*]]) { -; CHECK-NEXT: call void (ptr, ptr, ...) @test6_1(ptr [[X6_2]], ptr [[Y6_2]], ptr [[Z6_2]]) -; CHECK-NEXT: store ptr null, ptr @g, align 8 -; CHECK-NEXT: ret void +; FNATTRS-LABEL: define void @test6_2 +; FNATTRS-SAME: (ptr [[X6_2:%.*]], ptr nocapture [[Y6_2:%.*]], ptr [[Z6_2:%.*]]) { +; FNATTRS-NEXT: call void (ptr, ptr, ...) @test6_1(ptr [[X6_2]], ptr [[Y6_2]], ptr [[Z6_2]]) +; FNATTRS-NEXT: store ptr null, ptr @g, align 8 +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR-LABEL: define void @test6_2 +; ATTRIBUTOR-SAME: (ptr [[X6_2:%.*]], ptr nocapture [[Y6_2:%.*]], ptr [[Z6_2:%.*]]) { +; ATTRIBUTOR-NEXT: call void (ptr, ptr, ...) @test6_1(ptr [[X6_2]], ptr nocapture [[Y6_2]], ptr [[Z6_2]]) +; ATTRIBUTOR-NEXT: store ptr null, ptr @g, align 8 +; ATTRIBUTOR-NEXT: ret void ; call void (ptr, ptr, ...) @test6_1(ptr %x6_2, ptr %y6_2, ptr %z6_2) store ptr null, ptr @g @@ -369,42 +576,72 @@ } define void @test_cmpxchg(ptr %p) { -; CHECK-LABEL: define void @test_cmpxchg -; CHECK-SAME: (ptr nocapture [[P:%.*]]) #[[ATTR11:[0-9]+]] { -; CHECK-NEXT: [[TMP1:%.*]] = cmpxchg ptr [[P]], i32 0, i32 1 acquire monotonic, align 4 -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite) +; FNATTRS-LABEL: define void @test_cmpxchg +; FNATTRS-SAME: (ptr nocapture [[P:%.*]]) #[[ATTR11:[0-9]+]] { +; FNATTRS-NEXT: [[TMP1:%.*]] = cmpxchg ptr [[P]], i32 0, i32 1 acquire monotonic, align 4 +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite) +; ATTRIBUTOR-LABEL: define void @test_cmpxchg +; ATTRIBUTOR-SAME: (ptr nocapture nofree nonnull [[P:%.*]]) #[[ATTR9:[0-9]+]] { +; ATTRIBUTOR-NEXT: [[TMP1:%.*]] = cmpxchg ptr [[P]], i32 0, i32 1 acquire monotonic, align 4 +; ATTRIBUTOR-NEXT: ret void ; cmpxchg ptr %p, i32 0, i32 1 acquire monotonic ret void } define void @test_cmpxchg_ptr(ptr %p, ptr %q) { -; CHECK-LABEL: define void @test_cmpxchg_ptr -; CHECK-SAME: (ptr nocapture [[P:%.*]], ptr [[Q:%.*]]) #[[ATTR11]] { -; CHECK-NEXT: [[TMP1:%.*]] = cmpxchg ptr [[P]], ptr null, ptr [[Q]] acquire monotonic, align 8 -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite) +; FNATTRS-LABEL: define void @test_cmpxchg_ptr +; FNATTRS-SAME: (ptr nocapture [[P:%.*]], ptr [[Q:%.*]]) #[[ATTR11]] { +; FNATTRS-NEXT: [[TMP1:%.*]] = cmpxchg ptr [[P]], ptr null, ptr [[Q]] acquire monotonic, align 8 +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite) +; ATTRIBUTOR-LABEL: define void @test_cmpxchg_ptr +; ATTRIBUTOR-SAME: (ptr nocapture nofree nonnull [[P:%.*]], ptr nofree [[Q:%.*]]) #[[ATTR9]] { +; ATTRIBUTOR-NEXT: [[TMP1:%.*]] = cmpxchg ptr [[P]], ptr null, ptr [[Q]] acquire monotonic, align 8 +; ATTRIBUTOR-NEXT: ret void ; cmpxchg ptr %p, ptr null, ptr %q acquire monotonic ret void } define void @test_atomicrmw(ptr %p) { -; CHECK-LABEL: define void @test_atomicrmw -; CHECK-SAME: (ptr nocapture [[P:%.*]]) #[[ATTR11]] { -; CHECK-NEXT: [[TMP1:%.*]] = atomicrmw add ptr [[P]], i32 1 seq_cst, align 4 -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite) +; FNATTRS-LABEL: define void @test_atomicrmw +; FNATTRS-SAME: (ptr nocapture [[P:%.*]]) #[[ATTR11]] { +; FNATTRS-NEXT: [[TMP1:%.*]] = atomicrmw add ptr [[P]], i32 1 seq_cst, align 4 +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite) +; ATTRIBUTOR-LABEL: define void @test_atomicrmw +; ATTRIBUTOR-SAME: (ptr nocapture nofree nonnull [[P:%.*]]) #[[ATTR9]] { +; ATTRIBUTOR-NEXT: [[TMP1:%.*]] = atomicrmw add ptr [[P]], i32 1 seq_cst, align 4 +; ATTRIBUTOR-NEXT: ret void ; atomicrmw add ptr %p, i32 1 seq_cst ret void } define void @test_volatile(ptr %x) { -; CHECK-LABEL: define void @test_volatile -; CHECK-SAME: (ptr [[X:%.*]]) #[[ATTR12:[0-9]+]] { -; CHECK-NEXT: entry: -; CHECK-NEXT: [[GEP:%.*]] = getelementptr i32, ptr [[X]], i64 1 -; CHECK-NEXT: store volatile i32 0, ptr [[GEP]], align 4 -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: nofree norecurse nounwind memory(argmem: readwrite, inaccessiblemem: readwrite) +; FNATTRS-LABEL: define void @test_volatile +; FNATTRS-SAME: (ptr [[X:%.*]]) #[[ATTR12:[0-9]+]] { +; FNATTRS-NEXT: entry: +; FNATTRS-NEXT: [[GEP:%.*]] = getelementptr i32, ptr [[X]], i64 1 +; FNATTRS-NEXT: store volatile i32 0, ptr [[GEP]], align 4 +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite) +; ATTRIBUTOR-LABEL: define void @test_volatile +; ATTRIBUTOR-SAME: (ptr nofree [[X:%.*]]) #[[ATTR9]] { +; ATTRIBUTOR-NEXT: entry: +; ATTRIBUTOR-NEXT: [[GEP:%.*]] = getelementptr i32, ptr [[X]], i64 1 +; ATTRIBUTOR-NEXT: store volatile i32 0, ptr [[GEP]], align 4 +; ATTRIBUTOR-NEXT: ret void ; entry: %gep = getelementptr i32, ptr %x, i64 1 @@ -413,12 +650,21 @@ } define void @nocaptureLaunder(ptr %p) { -; CHECK-LABEL: define void @nocaptureLaunder -; CHECK-SAME: (ptr nocapture [[P:%.*]]) #[[ATTR13:[0-9]+]] { -; CHECK-NEXT: entry: -; CHECK-NEXT: [[B:%.*]] = call ptr @llvm.launder.invariant.group.p0(ptr [[P]]) -; CHECK-NEXT: store i8 42, ptr [[B]], align 1 -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(argmem: write, inaccessiblemem: readwrite) +; FNATTRS-LABEL: define void @nocaptureLaunder +; FNATTRS-SAME: (ptr nocapture [[P:%.*]]) #[[ATTR13:[0-9]+]] { +; FNATTRS-NEXT: entry: +; FNATTRS-NEXT: [[B:%.*]] = call ptr @llvm.launder.invariant.group.p0(ptr [[P]]) +; FNATTRS-NEXT: store i8 42, ptr [[B]], align 1 +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite, inaccessiblemem: readwrite) +; ATTRIBUTOR-LABEL: define void @nocaptureLaunder +; ATTRIBUTOR-SAME: (ptr nocapture nofree [[P:%.*]]) #[[ATTR10:[0-9]+]] { +; ATTRIBUTOR-NEXT: entry: +; ATTRIBUTOR-NEXT: [[B:%.*]] = call ptr @llvm.launder.invariant.group.p0(ptr [[P]]) #[[ATTR19:[0-9]+]] +; ATTRIBUTOR-NEXT: store i8 42, ptr [[B]], align 1 +; ATTRIBUTOR-NEXT: ret void ; entry: %b = call ptr @llvm.launder.invariant.group.p0(ptr %p) @@ -428,11 +674,19 @@ @g2 = global ptr null define void @captureLaunder(ptr %p) { -; CHECK-LABEL: define void @captureLaunder -; CHECK-SAME: (ptr [[P:%.*]]) #[[ATTR14:[0-9]+]] { -; CHECK-NEXT: [[B:%.*]] = call ptr @llvm.launder.invariant.group.p0(ptr [[P]]) -; CHECK-NEXT: store ptr [[B]], ptr @g2, align 8 -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: readwrite) +; FNATTRS-LABEL: define void @captureLaunder +; FNATTRS-SAME: (ptr [[P:%.*]]) #[[ATTR14:[0-9]+]] { +; FNATTRS-NEXT: [[B:%.*]] = call ptr @llvm.launder.invariant.group.p0(ptr [[P]]) +; FNATTRS-NEXT: store ptr [[B]], ptr @g2, align 8 +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn +; ATTRIBUTOR-LABEL: define void @captureLaunder +; ATTRIBUTOR-SAME: (ptr nofree [[P:%.*]]) #[[ATTR5]] { +; ATTRIBUTOR-NEXT: [[B:%.*]] = call ptr @llvm.launder.invariant.group.p0(ptr [[P]]) #[[ATTR19]] +; ATTRIBUTOR-NEXT: store ptr [[B]], ptr @g2, align 8 +; ATTRIBUTOR-NEXT: ret void ; %b = call ptr @llvm.launder.invariant.group.p0(ptr %p) store ptr %b, ptr @g2 @@ -440,12 +694,21 @@ } define void @nocaptureStrip(ptr %p) { -; CHECK-LABEL: define void @nocaptureStrip -; CHECK-SAME: (ptr nocapture writeonly [[P:%.*]]) #[[ATTR15:[0-9]+]] { -; CHECK-NEXT: entry: -; CHECK-NEXT: [[B:%.*]] = call ptr @llvm.strip.invariant.group.p0(ptr [[P]]) -; CHECK-NEXT: store i8 42, ptr [[B]], align 1 -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(argmem: write) +; FNATTRS-LABEL: define void @nocaptureStrip +; FNATTRS-SAME: (ptr nocapture writeonly [[P:%.*]]) #[[ATTR15:[0-9]+]] { +; FNATTRS-NEXT: entry: +; FNATTRS-NEXT: [[B:%.*]] = call ptr @llvm.strip.invariant.group.p0(ptr [[P]]) +; FNATTRS-NEXT: store i8 42, ptr [[B]], align 1 +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) +; ATTRIBUTOR-LABEL: define void @nocaptureStrip +; ATTRIBUTOR-SAME: (ptr nocapture nofree writeonly [[P:%.*]]) #[[ATTR11:[0-9]+]] { +; ATTRIBUTOR-NEXT: entry: +; ATTRIBUTOR-NEXT: [[B:%.*]] = call ptr @llvm.strip.invariant.group.p0(ptr [[P]]) #[[ATTR17]] +; ATTRIBUTOR-NEXT: store i8 42, ptr [[B]], align 1 +; ATTRIBUTOR-NEXT: ret void ; entry: %b = call ptr @llvm.strip.invariant.group.p0(ptr %p) @@ -455,11 +718,19 @@ @g3 = global ptr null define void @captureStrip(ptr %p) { -; CHECK-LABEL: define void @captureStrip -; CHECK-SAME: (ptr [[P:%.*]]) #[[ATTR16:[0-9]+]] { -; CHECK-NEXT: [[B:%.*]] = call ptr @llvm.strip.invariant.group.p0(ptr [[P]]) -; CHECK-NEXT: store ptr [[B]], ptr @g3, align 8 -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none) +; FNATTRS-LABEL: define void @captureStrip +; FNATTRS-SAME: (ptr [[P:%.*]]) #[[ATTR16:[0-9]+]] { +; FNATTRS-NEXT: [[B:%.*]] = call ptr @llvm.strip.invariant.group.p0(ptr [[P]]) +; FNATTRS-NEXT: store ptr [[B]], ptr @g3, align 8 +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write) +; ATTRIBUTOR-LABEL: define void @captureStrip +; ATTRIBUTOR-SAME: (ptr nofree writeonly [[P:%.*]]) #[[ATTR1]] { +; ATTRIBUTOR-NEXT: [[B:%.*]] = call ptr @llvm.strip.invariant.group.p0(ptr [[P]]) #[[ATTR17]] +; ATTRIBUTOR-NEXT: store ptr [[B]], ptr @g3, align 8 +; ATTRIBUTOR-NEXT: ret void ; %b = call ptr @llvm.strip.invariant.group.p0(ptr %p) store ptr %b, ptr @g3 @@ -467,31 +738,53 @@ } define i1 @captureICmp(ptr %x) { -; CHECK-LABEL: define i1 @captureICmp -; CHECK-SAME: (ptr readnone [[X:%.*]]) #[[ATTR0]] { -; CHECK-NEXT: [[TMP1:%.*]] = icmp eq ptr [[X]], null -; CHECK-NEXT: ret i1 [[TMP1]] +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) +; FNATTRS-LABEL: define i1 @captureICmp +; FNATTRS-SAME: (ptr readnone [[X:%.*]]) #[[ATTR0]] { +; FNATTRS-NEXT: [[TMP1:%.*]] = icmp eq ptr [[X]], null +; FNATTRS-NEXT: ret i1 [[TMP1]] +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) +; ATTRIBUTOR-LABEL: define i1 @captureICmp +; ATTRIBUTOR-SAME: (ptr nofree readnone [[X:%.*]]) #[[ATTR0]] { +; ATTRIBUTOR-NEXT: [[TMP1:%.*]] = icmp eq ptr [[X]], null +; ATTRIBUTOR-NEXT: ret i1 [[TMP1]] ; %1 = icmp eq ptr %x, null ret i1 %1 } define i1 @captureICmpRev(ptr %x) { -; CHECK-LABEL: define i1 @captureICmpRev -; CHECK-SAME: (ptr readnone [[X:%.*]]) #[[ATTR0]] { -; CHECK-NEXT: [[TMP1:%.*]] = icmp eq ptr null, [[X]] -; CHECK-NEXT: ret i1 [[TMP1]] +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) +; FNATTRS-LABEL: define i1 @captureICmpRev +; FNATTRS-SAME: (ptr readnone [[X:%.*]]) #[[ATTR0]] { +; FNATTRS-NEXT: [[TMP1:%.*]] = icmp eq ptr null, [[X]] +; FNATTRS-NEXT: ret i1 [[TMP1]] +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) +; ATTRIBUTOR-LABEL: define i1 @captureICmpRev +; ATTRIBUTOR-SAME: (ptr nofree readnone [[X:%.*]]) #[[ATTR0]] { +; ATTRIBUTOR-NEXT: [[TMP1:%.*]] = icmp eq ptr null, [[X]] +; ATTRIBUTOR-NEXT: ret i1 [[TMP1]] ; %1 = icmp eq ptr null, %x ret i1 %1 } define i1 @nocaptureInboundsGEPICmp(ptr %x) { -; CHECK-LABEL: define i1 @nocaptureInboundsGEPICmp -; CHECK-SAME: (ptr readnone [[X:%.*]]) #[[ATTR0]] { -; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[X]], i32 5 -; CHECK-NEXT: [[TMP2:%.*]] = icmp eq ptr [[TMP1]], null -; CHECK-NEXT: ret i1 [[TMP2]] +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) +; FNATTRS-LABEL: define i1 @nocaptureInboundsGEPICmp +; FNATTRS-SAME: (ptr readnone [[X:%.*]]) #[[ATTR0]] { +; FNATTRS-NEXT: [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[X]], i32 5 +; FNATTRS-NEXT: [[TMP2:%.*]] = icmp eq ptr [[TMP1]], null +; FNATTRS-NEXT: ret i1 [[TMP2]] +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) +; ATTRIBUTOR-LABEL: define i1 @nocaptureInboundsGEPICmp +; ATTRIBUTOR-SAME: (ptr nofree readnone [[X:%.*]]) #[[ATTR0]] { +; ATTRIBUTOR-NEXT: [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[X]], i32 5 +; ATTRIBUTOR-NEXT: [[TMP2:%.*]] = icmp eq ptr [[TMP1]], null +; ATTRIBUTOR-NEXT: ret i1 [[TMP2]] ; %1 = getelementptr inbounds i32, ptr %x, i32 5 %2 = icmp eq ptr %1, null @@ -499,11 +792,19 @@ } define i1 @nocaptureInboundsGEPICmpRev(ptr %x) { -; CHECK-LABEL: define i1 @nocaptureInboundsGEPICmpRev -; CHECK-SAME: (ptr readnone [[X:%.*]]) #[[ATTR0]] { -; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[X]], i32 5 -; CHECK-NEXT: [[TMP2:%.*]] = icmp eq ptr null, [[TMP1]] -; CHECK-NEXT: ret i1 [[TMP2]] +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) +; FNATTRS-LABEL: define i1 @nocaptureInboundsGEPICmpRev +; FNATTRS-SAME: (ptr readnone [[X:%.*]]) #[[ATTR0]] { +; FNATTRS-NEXT: [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[X]], i32 5 +; FNATTRS-NEXT: [[TMP2:%.*]] = icmp eq ptr null, [[TMP1]] +; FNATTRS-NEXT: ret i1 [[TMP2]] +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) +; ATTRIBUTOR-LABEL: define i1 @nocaptureInboundsGEPICmpRev +; ATTRIBUTOR-SAME: (ptr nofree readnone [[X:%.*]]) #[[ATTR0]] { +; ATTRIBUTOR-NEXT: [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[X]], i32 5 +; ATTRIBUTOR-NEXT: [[TMP2:%.*]] = icmp eq ptr null, [[TMP1]] +; ATTRIBUTOR-NEXT: ret i1 [[TMP2]] ; %1 = getelementptr inbounds i32, ptr %x, i32 5 %2 = icmp eq ptr null, %1 @@ -511,20 +812,34 @@ } define i1 @nocaptureDereferenceableOrNullICmp(ptr dereferenceable_or_null(4) %x) { -; CHECK-LABEL: define i1 @nocaptureDereferenceableOrNullICmp -; CHECK-SAME: (ptr nocapture readnone dereferenceable_or_null(4) [[X:%.*]]) #[[ATTR0]] { -; CHECK-NEXT: [[TMP1:%.*]] = icmp eq ptr [[X]], null -; CHECK-NEXT: ret i1 [[TMP1]] +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) +; FNATTRS-LABEL: define i1 @nocaptureDereferenceableOrNullICmp +; FNATTRS-SAME: (ptr nocapture readnone dereferenceable_or_null(4) [[X:%.*]]) #[[ATTR0]] { +; FNATTRS-NEXT: [[TMP1:%.*]] = icmp eq ptr [[X]], null +; FNATTRS-NEXT: ret i1 [[TMP1]] +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) +; ATTRIBUTOR-LABEL: define i1 @nocaptureDereferenceableOrNullICmp +; ATTRIBUTOR-SAME: (ptr nofree readnone dereferenceable_or_null(4) [[X:%.*]]) #[[ATTR0]] { +; ATTRIBUTOR-NEXT: [[TMP1:%.*]] = icmp eq ptr [[X]], null +; ATTRIBUTOR-NEXT: ret i1 [[TMP1]] ; %1 = icmp eq ptr %x, null ret i1 %1 } define i1 @captureDereferenceableOrNullICmp(ptr dereferenceable_or_null(4) %x) null_pointer_is_valid { -; CHECK-LABEL: define i1 @captureDereferenceableOrNullICmp -; CHECK-SAME: (ptr readnone dereferenceable_or_null(4) [[X:%.*]]) #[[ATTR17:[0-9]+]] { -; CHECK-NEXT: [[TMP1:%.*]] = icmp eq ptr [[X]], null -; CHECK-NEXT: ret i1 [[TMP1]] +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind null_pointer_is_valid willreturn memory(none) +; FNATTRS-LABEL: define i1 @captureDereferenceableOrNullICmp +; FNATTRS-SAME: (ptr readnone dereferenceable_or_null(4) [[X:%.*]]) #[[ATTR17:[0-9]+]] { +; FNATTRS-NEXT: [[TMP1:%.*]] = icmp eq ptr [[X]], null +; FNATTRS-NEXT: ret i1 [[TMP1]] +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind null_pointer_is_valid willreturn memory(none) +; ATTRIBUTOR-LABEL: define i1 @captureDereferenceableOrNullICmp +; ATTRIBUTOR-SAME: (ptr nofree readnone dereferenceable_or_null(4) [[X:%.*]]) #[[ATTR12:[0-9]+]] { +; ATTRIBUTOR-NEXT: [[TMP1:%.*]] = icmp eq ptr [[X]], null +; ATTRIBUTOR-NEXT: ret i1 [[TMP1]] ; %1 = icmp eq ptr %x, null ret i1 %1 @@ -533,11 +848,17 @@ declare void @capture(ptr) define void @nocapture_fptr(ptr %f, ptr %p) { -; CHECK-LABEL: define void @nocapture_fptr -; CHECK-SAME: (ptr nocapture readonly [[F:%.*]], ptr [[P:%.*]]) { -; CHECK-NEXT: [[RES:%.*]] = call ptr [[F]](ptr [[P]]) -; CHECK-NEXT: call void @capture(ptr [[RES]]) -; CHECK-NEXT: ret void +; FNATTRS-LABEL: define void @nocapture_fptr +; FNATTRS-SAME: (ptr nocapture readonly [[F:%.*]], ptr [[P:%.*]]) { +; FNATTRS-NEXT: [[RES:%.*]] = call ptr [[F]](ptr [[P]]) +; FNATTRS-NEXT: call void @capture(ptr [[RES]]) +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR-LABEL: define void @nocapture_fptr +; ATTRIBUTOR-SAME: (ptr nocapture nofree nonnull [[F:%.*]], ptr [[P:%.*]]) { +; ATTRIBUTOR-NEXT: [[RES:%.*]] = call ptr [[F]](ptr [[P]]) +; ATTRIBUTOR-NEXT: call void @capture(ptr [[RES]]) +; ATTRIBUTOR-NEXT: ret void ; %res = call ptr %f(ptr %p) call void @capture(ptr %res) @@ -545,11 +866,17 @@ } define void @recurse_fptr(ptr %f, ptr %p) { -; CHECK-LABEL: define void @recurse_fptr -; CHECK-SAME: (ptr nocapture readonly [[F:%.*]], ptr [[P:%.*]]) { -; CHECK-NEXT: [[RES:%.*]] = call ptr [[F]](ptr [[P]]) -; CHECK-NEXT: store i8 0, ptr [[RES]], align 1 -; CHECK-NEXT: ret void +; FNATTRS-LABEL: define void @recurse_fptr +; FNATTRS-SAME: (ptr nocapture readonly [[F:%.*]], ptr [[P:%.*]]) { +; FNATTRS-NEXT: [[RES:%.*]] = call ptr [[F]](ptr [[P]]) +; FNATTRS-NEXT: store i8 0, ptr [[RES]], align 1 +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR-LABEL: define void @recurse_fptr +; ATTRIBUTOR-SAME: (ptr nocapture nofree nonnull [[F:%.*]], ptr [[P:%.*]]) { +; ATTRIBUTOR-NEXT: [[RES:%.*]] = call ptr [[F]](ptr [[P]]) +; ATTRIBUTOR-NEXT: store i8 0, ptr [[RES]], align 1 +; ATTRIBUTOR-NEXT: ret void ; %res = call ptr %f(ptr %p) store i8 0, ptr %res @@ -557,10 +884,17 @@ } define void @readnone_indirec(ptr %f, ptr %p) { -; CHECK-LABEL: define void @readnone_indirec -; CHECK-SAME: (ptr nocapture readonly [[F:%.*]], ptr readnone [[P:%.*]]) #[[ATTR18:[0-9]+]] { -; CHECK-NEXT: call void [[F]](ptr [[P]]) #[[ATTR21:[0-9]+]] -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: nofree nosync memory(none) +; FNATTRS-LABEL: define void @readnone_indirec +; FNATTRS-SAME: (ptr nocapture readonly [[F:%.*]], ptr readnone [[P:%.*]]) #[[ATTR18:[0-9]+]] { +; FNATTRS-NEXT: call void [[F]](ptr [[P]]) #[[ATTR21:[0-9]+]] +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: nosync memory(none) +; ATTRIBUTOR-LABEL: define void @readnone_indirec +; ATTRIBUTOR-SAME: (ptr nocapture nofree nonnull readnone [[F:%.*]], ptr readnone [[P:%.*]]) #[[ATTR13:[0-9]+]] { +; ATTRIBUTOR-NEXT: call void [[F]](ptr [[P]]) #[[ATTR20:[0-9]+]] +; ATTRIBUTOR-NEXT: ret void ; call void %f(ptr %p) readnone ret void diff --git a/llvm/test/Transforms/FunctionAttrs/nonnull.ll b/llvm/test/Transforms/FunctionAttrs/nonnull.ll --- a/llvm/test/Transforms/FunctionAttrs/nonnull.ll +++ b/llvm/test/Transforms/FunctionAttrs/nonnull.ll @@ -1,5 +1,6 @@ -; NOTE: Assertions have been autogenerated by utils/update_test_checks.py -; RUN: opt -S -passes=function-attrs -enable-nonnull-arg-prop %s | FileCheck %s --check-prefixes=FNATTR +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 3 +; RUN: opt -S -passes=function-attrs -enable-nonnull-arg-prop %s | FileCheck %s --check-prefixes=COMMON,FNATTRS +; RUN: opt -S -passes=attributor-light %s | FileCheck %s --check-prefixes=COMMON,ATTRIBUTOR target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" @@ -7,21 +8,39 @@ ; Return a pointer trivially nonnull (call return attribute) define ptr @test1() { -; FNATTR: define nonnull ptr @test1 +; COMMON-LABEL: define nonnull ptr @test1() { +; COMMON-NEXT: [[RET:%.*]] = call ptr @ret_nonnull() +; COMMON-NEXT: ret ptr [[RET]] +; %ret = call ptr @ret_nonnull() ret ptr %ret } ; Return a pointer trivially nonnull (argument attribute) define ptr @test2(ptr nonnull %p) { -; FNATTR: define nonnull ptr @test2 +; FNATTRS-LABEL: define nonnull ptr @test2 +; FNATTRS-SAME: (ptr nonnull readnone returned [[P:%.*]]) #[[ATTR0:[0-9]+]] { +; FNATTRS-NEXT: ret ptr [[P]] +; +; ATTRIBUTOR-LABEL: define nonnull ptr @test2 +; ATTRIBUTOR-SAME: (ptr nofree nonnull readnone [[P:%.*]]) #[[ATTR0:[0-9]+]] { +; ATTRIBUTOR-NEXT: ret ptr [[P]] +; ret ptr %p } ; Given an SCC where one of the functions can not be marked nonnull, ; can we still mark the other one which is trivially nonnull define ptr @scc_binder(i1 %c) { -; FNATTR: define ptr @scc_binder +; COMMON-LABEL: define ptr @scc_binder +; COMMON-SAME: (i1 [[C:%.*]]) { +; COMMON-NEXT: br i1 [[C]], label [[REC:%.*]], label [[END:%.*]] +; COMMON: rec: +; COMMON-NEXT: [[TMP1:%.*]] = call ptr @test3(i1 [[C]]) +; COMMON-NEXT: br label [[END]] +; COMMON: end: +; COMMON-NEXT: ret ptr null +; br i1 %c, label %rec, label %end rec: call ptr @test3(i1 %c) @@ -31,7 +50,12 @@ } define ptr @test3(i1 %c) { -; FNATTR: define nonnull ptr @test3 +; COMMON-LABEL: define nonnull ptr @test3 +; COMMON-SAME: (i1 [[C:%.*]]) { +; COMMON-NEXT: [[TMP1:%.*]] = call ptr @scc_binder(i1 [[C]]) +; COMMON-NEXT: [[RET:%.*]] = call ptr @ret_nonnull() +; COMMON-NEXT: ret ptr [[RET]] +; call ptr @scc_binder(i1 %c) %ret = call ptr @ret_nonnull() ret ptr %ret @@ -41,13 +65,31 @@ ; nonnull if neither can ever return null. (In this case, they ; just never return period.) define ptr @test4_helper() { -; FNATTR: define noalias nonnull ptr @test4_helper +; FNATTRS-LABEL: define noalias nonnull ptr @test4_helper +; FNATTRS-SAME: () #[[ATTR1:[0-9]+]] { +; FNATTRS-NEXT: [[RET:%.*]] = call ptr @test4() +; FNATTRS-NEXT: ret ptr [[RET]] +; +; ATTRIBUTOR-LABEL: define ptr @test4_helper +; ATTRIBUTOR-SAME: () #[[ATTR1:[0-9]+]] { +; ATTRIBUTOR-NEXT: [[RET:%.*]] = call ptr @test4() #[[ATTR1]] +; ATTRIBUTOR-NEXT: ret ptr [[RET]] +; %ret = call ptr @test4() ret ptr %ret } define ptr @test4() { -; FNATTR: define noalias nonnull ptr @test4 +; FNATTRS-LABEL: define noalias nonnull ptr @test4 +; FNATTRS-SAME: () #[[ATTR1]] { +; FNATTRS-NEXT: [[RET:%.*]] = call ptr @test4_helper() +; FNATTRS-NEXT: ret ptr [[RET]] +; +; ATTRIBUTOR-LABEL: define ptr @test4 +; ATTRIBUTOR-SAME: () #[[ATTR1]] { +; ATTRIBUTOR-NEXT: [[RET:%.*]] = call ptr @test4_helper() #[[ATTR1]] +; ATTRIBUTOR-NEXT: ret ptr [[RET]] +; %ret = call ptr @test4_helper() ret ptr %ret } @@ -55,7 +97,24 @@ ; Given a mutual recursive set of functions which *can* return null ; make sure we haven't marked them as nonnull. define ptr @test5_helper(i1 %c) { -; FNATTR: define noalias ptr @test5_helper +; FNATTRS-LABEL: define noalias ptr @test5_helper +; FNATTRS-SAME: (i1 [[C:%.*]]) #[[ATTR1]] { +; FNATTRS-NEXT: br i1 [[C]], label [[REC:%.*]], label [[END:%.*]] +; FNATTRS: rec: +; FNATTRS-NEXT: [[RET:%.*]] = call ptr @test5(i1 [[C]]) +; FNATTRS-NEXT: br label [[END]] +; FNATTRS: end: +; FNATTRS-NEXT: ret ptr null +; +; ATTRIBUTOR-LABEL: define ptr @test5_helper +; ATTRIBUTOR-SAME: (i1 [[C:%.*]]) #[[ATTR1]] { +; ATTRIBUTOR-NEXT: br i1 [[C]], label [[REC:%.*]], label [[END:%.*]] +; ATTRIBUTOR: rec: +; ATTRIBUTOR-NEXT: [[RET:%.*]] = call ptr @test5(i1 [[C]]) #[[ATTR1]] +; ATTRIBUTOR-NEXT: br label [[END]] +; ATTRIBUTOR: end: +; ATTRIBUTOR-NEXT: ret ptr null +; br i1 %c, label %rec, label %end rec: %ret = call ptr @test5(i1 %c) @@ -65,13 +124,32 @@ } define ptr @test5(i1 %c) { -; FNATTR: define noalias ptr @test5 +; FNATTRS-LABEL: define noalias ptr @test5 +; FNATTRS-SAME: (i1 [[C:%.*]]) #[[ATTR1]] { +; FNATTRS-NEXT: [[RET:%.*]] = call ptr @test5_helper(i1 [[C]]) +; FNATTRS-NEXT: ret ptr [[RET]] +; +; ATTRIBUTOR-LABEL: define ptr @test5 +; ATTRIBUTOR-SAME: (i1 [[C:%.*]]) #[[ATTR1]] { +; ATTRIBUTOR-NEXT: [[RET:%.*]] = call ptr @test5_helper(i1 [[C]]) #[[ATTR1]] +; ATTRIBUTOR-NEXT: ret ptr [[RET]] +; %ret = call ptr @test5_helper(i1 %c) ret ptr %ret } ; Local analysis, but going through a self recursive phi define ptr @test6a() { +; COMMON-LABEL: define nonnull ptr @test6a() { +; COMMON-NEXT: entry: +; COMMON-NEXT: [[RET:%.*]] = call ptr @ret_nonnull() +; COMMON-NEXT: br label [[LOOP:%.*]] +; COMMON: loop: +; COMMON-NEXT: [[PHI:%.*]] = phi ptr [ [[RET]], [[ENTRY:%.*]] ], [ [[PHI]], [[LOOP]] ] +; COMMON-NEXT: br i1 undef, label [[LOOP]], label [[EXIT:%.*]] +; COMMON: exit: +; COMMON-NEXT: ret ptr [[PHI]] +; entry: %ret = call ptr @ret_nonnull() br label %loop @@ -83,6 +161,17 @@ } define ptr @test6b(i1 %c) { +; COMMON-LABEL: define nonnull ptr @test6b +; COMMON-SAME: (i1 [[C:%.*]]) { +; COMMON-NEXT: entry: +; COMMON-NEXT: [[RET:%.*]] = call ptr @ret_nonnull() +; COMMON-NEXT: br label [[LOOP:%.*]] +; COMMON: loop: +; COMMON-NEXT: [[PHI:%.*]] = phi ptr [ [[RET]], [[ENTRY:%.*]] ], [ [[PHI]], [[LOOP]] ] +; COMMON-NEXT: br i1 [[C]], label [[LOOP]], label [[EXIT:%.*]] +; COMMON: exit: +; COMMON-NEXT: ret ptr [[PHI]] +; entry: %ret = call ptr @ret_nonnull() br label %loop @@ -93,27 +182,65 @@ ret ptr %phi } -; FNATTR: define ptr @test7 define ptr @test7(ptr %a) { +; FNATTRS-LABEL: define ptr @test7 +; FNATTRS-SAME: (ptr readnone returned [[A:%.*]]) #[[ATTR0]] { +; FNATTRS-NEXT: ret ptr [[A]] +; +; ATTRIBUTOR-LABEL: define ptr @test7 +; ATTRIBUTOR-SAME: (ptr nofree readnone [[A:%.*]]) #[[ATTR0]] { +; ATTRIBUTOR-NEXT: ret ptr [[A]] +; ret ptr %a } -; FNATTR: define nonnull ptr @test8 define ptr @test8(ptr %a) { +; FNATTRS-LABEL: define nonnull ptr @test8 +; FNATTRS-SAME: (ptr readnone [[A:%.*]]) #[[ATTR0]] { +; FNATTRS-NEXT: [[B:%.*]] = getelementptr inbounds i8, ptr [[A]], i64 1 +; FNATTRS-NEXT: ret ptr [[B]] +; +; ATTRIBUTOR-LABEL: define nonnull ptr @test8 +; ATTRIBUTOR-SAME: (ptr nofree readnone [[A:%.*]]) #[[ATTR0]] { +; ATTRIBUTOR-NEXT: [[B:%.*]] = getelementptr inbounds i8, ptr [[A]], i64 1 +; ATTRIBUTOR-NEXT: ret ptr [[B]] +; %b = getelementptr inbounds i8, ptr %a, i64 1 ret ptr %b } -; FNATTR: define ptr @test9 define ptr @test9(ptr %a, i64 %n) { +; FNATTRS-LABEL: define ptr @test9 +; FNATTRS-SAME: (ptr readnone [[A:%.*]], i64 [[N:%.*]]) #[[ATTR0]] { +; FNATTRS-NEXT: [[B:%.*]] = getelementptr inbounds i8, ptr [[A]], i64 [[N]] +; FNATTRS-NEXT: ret ptr [[B]] +; +; ATTRIBUTOR-LABEL: define ptr @test9 +; ATTRIBUTOR-SAME: (ptr nofree readnone [[A:%.*]], i64 [[N:%.*]]) #[[ATTR0]] { +; ATTRIBUTOR-NEXT: [[B:%.*]] = getelementptr inbounds i8, ptr [[A]], i64 [[N]] +; ATTRIBUTOR-NEXT: ret ptr [[B]] +; %b = getelementptr inbounds i8, ptr %a, i64 %n ret ptr %b } declare void @llvm.assume(i1) -; FNATTR: define ptr @test10 ; FIXME: missing nonnull define ptr @test10(ptr %a, i64 %n) { +; FNATTRS-LABEL: define ptr @test10 +; FNATTRS-SAME: (ptr readnone [[A:%.*]], i64 [[N:%.*]]) #[[ATTR3:[0-9]+]] { +; FNATTRS-NEXT: [[CMP:%.*]] = icmp ne i64 [[N]], 0 +; FNATTRS-NEXT: call void @llvm.assume(i1 [[CMP]]) +; FNATTRS-NEXT: [[B:%.*]] = getelementptr inbounds i8, ptr [[A]], i64 [[N]] +; FNATTRS-NEXT: ret ptr [[B]] +; +; ATTRIBUTOR-LABEL: define ptr @test10 +; ATTRIBUTOR-SAME: (ptr nofree readnone [[A:%.*]], i64 [[N:%.*]]) #[[ATTR3:[0-9]+]] { +; ATTRIBUTOR-NEXT: [[CMP:%.*]] = icmp ne i64 [[N]], 0 +; ATTRIBUTOR-NEXT: call void @llvm.assume(i1 [[CMP]]) #[[ATTR14:[0-9]+]] +; ATTRIBUTOR-NEXT: [[B:%.*]] = getelementptr inbounds i8, ptr [[A]], i64 [[N]] +; ATTRIBUTOR-NEXT: ret ptr [[B]] +; %cmp = icmp ne i64 %n, 0 call void @llvm.assume(i1 %cmp) %b = getelementptr inbounds i8, ptr %a, i64 %n @@ -124,9 +251,30 @@ ; char* test11(char *p) { ; return p? p: nonnull(); ; } -; FNATTR: define ptr @test11 ; FIXME: missing nonnull define ptr @test11(ptr) local_unnamed_addr { +; FNATTRS-LABEL: define ptr @test11 +; FNATTRS-SAME: (ptr readnone [[TMP0:%.*]]) local_unnamed_addr { +; FNATTRS-NEXT: [[TMP2:%.*]] = icmp eq ptr [[TMP0]], null +; FNATTRS-NEXT: br i1 [[TMP2]], label [[TMP3:%.*]], label [[TMP5:%.*]] +; FNATTRS: 3: +; FNATTRS-NEXT: [[TMP4:%.*]] = tail call ptr @ret_nonnull() +; FNATTRS-NEXT: br label [[TMP5]] +; FNATTRS: 5: +; FNATTRS-NEXT: [[TMP6:%.*]] = phi ptr [ [[TMP4]], [[TMP3]] ], [ [[TMP0]], [[TMP1:%.*]] ] +; FNATTRS-NEXT: ret ptr [[TMP6]] +; +; ATTRIBUTOR-LABEL: define ptr @test11 +; ATTRIBUTOR-SAME: (ptr [[TMP0:%.*]]) local_unnamed_addr { +; ATTRIBUTOR-NEXT: [[TMP2:%.*]] = icmp eq ptr [[TMP0]], null +; ATTRIBUTOR-NEXT: br i1 [[TMP2]], label [[TMP3:%.*]], label [[TMP5:%.*]] +; ATTRIBUTOR: 3: +; ATTRIBUTOR-NEXT: [[TMP4:%.*]] = tail call ptr @ret_nonnull() +; ATTRIBUTOR-NEXT: br label [[TMP5]] +; ATTRIBUTOR: 5: +; ATTRIBUTOR-NEXT: [[TMP6:%.*]] = phi ptr [ [[TMP4]], [[TMP3]] ], [ [[TMP0]], [[TMP1:%.*]] ] +; ATTRIBUTOR-NEXT: ret ptr [[TMP6]] +; %2 = icmp eq ptr %0, null br i1 %2, label %3, label %5 @@ -143,6 +291,11 @@ ; Simple CallSite Test declare void @test12_helper(ptr) define void @test12(ptr nonnull %a) { +; COMMON-LABEL: define void @test12 +; COMMON-SAME: (ptr nonnull [[A:%.*]]) { +; COMMON-NEXT: tail call void @test12_helper(ptr [[A]]) +; COMMON-NEXT: ret void +; tail call void @test12_helper(ptr %a) ret void } @@ -151,6 +304,20 @@ ; Simple Argument Tests declare ptr @unknown() define void @test13_helper() { +; FNATTRS-LABEL: define void @test13_helper() { +; FNATTRS-NEXT: [[NONNULLPTR:%.*]] = tail call ptr @ret_nonnull() +; FNATTRS-NEXT: [[MAYBENULLPTR:%.*]] = tail call ptr @unknown() +; FNATTRS-NEXT: tail call void @test13(ptr [[NONNULLPTR]], ptr [[NONNULLPTR]], ptr [[MAYBENULLPTR]]) +; FNATTRS-NEXT: tail call void @test13(ptr [[NONNULLPTR]], ptr [[MAYBENULLPTR]], ptr [[NONNULLPTR]]) +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR-LABEL: define void @test13_helper() { +; ATTRIBUTOR-NEXT: [[NONNULLPTR:%.*]] = tail call ptr @ret_nonnull() +; ATTRIBUTOR-NEXT: [[MAYBENULLPTR:%.*]] = tail call ptr @unknown() +; ATTRIBUTOR-NEXT: tail call void @test13(ptr nocapture nofree nonnull readnone [[NONNULLPTR]], ptr nocapture nofree nonnull readnone [[NONNULLPTR]], ptr nocapture nofree readnone [[MAYBENULLPTR]]) +; ATTRIBUTOR-NEXT: tail call void @test13(ptr nocapture nofree nonnull readnone [[NONNULLPTR]], ptr nocapture nofree readnone [[MAYBENULLPTR]], ptr nocapture nofree nonnull readnone [[NONNULLPTR]]) +; ATTRIBUTOR-NEXT: ret void +; %nonnullptr = tail call ptr @ret_nonnull() %maybenullptr = tail call ptr @unknown() tail call void @test13(ptr %nonnullptr, ptr %nonnullptr, ptr %maybenullptr) @@ -158,6 +325,14 @@ ret void } define internal void @test13(ptr %a, ptr %b, ptr %c) { +; FNATTRS-LABEL: define internal void @test13 +; FNATTRS-SAME: (ptr nocapture readnone [[A:%.*]], ptr nocapture readnone [[B:%.*]], ptr nocapture readnone [[C:%.*]]) #[[ATTR0]] { +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR-LABEL: define internal void @test13 +; ATTRIBUTOR-SAME: (ptr nocapture nofree readnone [[A:%.*]], ptr nocapture nofree readnone [[B:%.*]], ptr nocapture nofree readnone [[C:%.*]]) #[[ATTR4:[0-9]+]] { +; ATTRIBUTOR-NEXT: ret void +; ret void } @@ -177,7 +352,48 @@ define internal ptr @f1(ptr %arg) { ; FIXME: missing nonnull It should be nonnull @f1(ptr nonnull readonly %arg) - +; FNATTRS-LABEL: define internal nonnull ptr @f1 +; FNATTRS-SAME: (ptr readonly [[ARG:%.*]]) #[[ATTR4:[0-9]+]] { +; FNATTRS-NEXT: bb: +; FNATTRS-NEXT: [[TMP:%.*]] = icmp eq ptr [[ARG]], null +; FNATTRS-NEXT: br i1 [[TMP]], label [[BB9:%.*]], label [[BB1:%.*]] +; FNATTRS: bb1: +; FNATTRS-NEXT: [[TMP2:%.*]] = load i32, ptr [[ARG]], align 4 +; FNATTRS-NEXT: [[TMP3:%.*]] = icmp eq i32 [[TMP2]], 0 +; FNATTRS-NEXT: br i1 [[TMP3]], label [[BB6:%.*]], label [[BB4:%.*]] +; FNATTRS: bb4: +; FNATTRS-NEXT: [[TMP5:%.*]] = getelementptr inbounds i32, ptr [[ARG]], i64 1 +; FNATTRS-NEXT: [[TMP5B:%.*]] = tail call ptr @f3(ptr [[TMP5]]) +; FNATTRS-NEXT: [[TMP5C:%.*]] = getelementptr inbounds i32, ptr [[TMP5B]], i64 -1 +; FNATTRS-NEXT: br label [[BB9]] +; FNATTRS: bb6: +; FNATTRS-NEXT: [[TMP7:%.*]] = tail call ptr @f2(ptr [[ARG]]) +; FNATTRS-NEXT: ret ptr [[TMP7]] +; FNATTRS: bb9: +; FNATTRS-NEXT: [[TMP10:%.*]] = phi ptr [ [[TMP5C]], [[BB4]] ], [ inttoptr (i64 4 to ptr), [[BB:%.*]] ] +; FNATTRS-NEXT: ret ptr [[TMP10]] +; +; ATTRIBUTOR-LABEL: define internal ptr @f1 +; ATTRIBUTOR-SAME: (ptr nofree readonly [[ARG:%.*]]) #[[ATTR5:[0-9]+]] { +; ATTRIBUTOR-NEXT: bb: +; ATTRIBUTOR-NEXT: [[TMP:%.*]] = icmp eq ptr [[ARG]], null +; ATTRIBUTOR-NEXT: br i1 [[TMP]], label [[BB9:%.*]], label [[BB1:%.*]] +; ATTRIBUTOR: bb1: +; ATTRIBUTOR-NEXT: [[TMP2:%.*]] = load i32, ptr [[ARG]], align 4 +; ATTRIBUTOR-NEXT: [[TMP3:%.*]] = icmp eq i32 [[TMP2]], 0 +; ATTRIBUTOR-NEXT: br i1 [[TMP3]], label [[BB6:%.*]], label [[BB4:%.*]] +; ATTRIBUTOR: bb4: +; ATTRIBUTOR-NEXT: [[TMP5:%.*]] = getelementptr inbounds i32, ptr [[ARG]], i64 1 +; ATTRIBUTOR-NEXT: [[TMP5B:%.*]] = tail call ptr @f3(ptr readonly [[TMP5]]) #[[ATTR15:[0-9]+]] +; ATTRIBUTOR-NEXT: [[TMP5C:%.*]] = getelementptr inbounds i32, ptr [[TMP5B]], i64 -1 +; ATTRIBUTOR-NEXT: br label [[BB9]] +; ATTRIBUTOR: bb6: +; ATTRIBUTOR-NEXT: [[TMP7:%.*]] = tail call ptr @f2(ptr readonly [[ARG]]) #[[ATTR15]] +; ATTRIBUTOR-NEXT: ret ptr [[TMP7]] +; ATTRIBUTOR: bb9: +; ATTRIBUTOR-NEXT: [[TMP10:%.*]] = phi ptr [ [[TMP5C]], [[BB4]] ], [ inttoptr (i64 4 to ptr), [[BB:%.*]] ] +; ATTRIBUTOR-NEXT: ret ptr [[TMP10]] +; bb: %tmp = icmp eq ptr %arg, null br i1 %tmp, label %bb9, label %bb1 @@ -205,6 +421,18 @@ define internal ptr @f2(ptr %arg) { ; FIXME: missing nonnull. It should be nonnull @f2(ptr nonnull %arg) +; FNATTRS-LABEL: define internal nonnull ptr @f2 +; FNATTRS-SAME: (ptr [[ARG:%.*]]) #[[ATTR4]] { +; FNATTRS-NEXT: bb: +; FNATTRS-NEXT: [[TMP:%.*]] = tail call ptr @f1(ptr [[ARG]]) +; FNATTRS-NEXT: ret ptr [[TMP]] +; +; ATTRIBUTOR-LABEL: define internal ptr @f2 +; ATTRIBUTOR-SAME: (ptr readonly [[ARG:%.*]]) #[[ATTR5]] { +; ATTRIBUTOR-NEXT: bb: +; ATTRIBUTOR-NEXT: [[TMP:%.*]] = tail call ptr @f1(ptr readonly [[ARG]]) #[[ATTR15]] +; ATTRIBUTOR-NEXT: ret ptr [[TMP]] +; bb: ; FIXME: missing nonnull. It should be @f1(ptr nonnull readonly %arg) @@ -214,6 +442,18 @@ define dso_local noalias ptr @f3(ptr %arg) { ; FIXME: missing nonnull. It should be nonnull @f3(ptr nonnull readonly %arg) +; FNATTRS-LABEL: define dso_local noalias nonnull ptr @f3 +; FNATTRS-SAME: (ptr [[ARG:%.*]]) #[[ATTR4]] { +; FNATTRS-NEXT: bb: +; FNATTRS-NEXT: [[TMP:%.*]] = call ptr @f1(ptr [[ARG]]) +; FNATTRS-NEXT: ret ptr [[TMP]] +; +; ATTRIBUTOR-LABEL: define dso_local noalias ptr @f3 +; ATTRIBUTOR-SAME: (ptr nofree readonly [[ARG:%.*]]) #[[ATTR5]] { +; ATTRIBUTOR-NEXT: bb: +; ATTRIBUTOR-NEXT: [[TMP:%.*]] = call ptr @f1(ptr nofree readonly [[ARG]]) #[[ATTR15]] +; ATTRIBUTOR-NEXT: ret ptr [[TMP]] +; bb: ; FIXME: missing nonnull. It should be @f1(ptr nonnull readonly %arg) %tmp = call ptr @f1(ptr %arg) @@ -222,7 +462,16 @@ ; TEST 15 define void @f15(ptr %arg) { - +; FNATTRS-LABEL: define void @f15 +; FNATTRS-SAME: (ptr [[ARG:%.*]]) { +; FNATTRS-NEXT: tail call void @use1(ptr dereferenceable(4) [[ARG]]) +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR-LABEL: define void @f15 +; ATTRIBUTOR-SAME: (ptr nonnull [[ARG:%.*]]) { +; ATTRIBUTOR-NEXT: tail call void @use1(ptr nonnull dereferenceable(4) [[ARG]]) +; ATTRIBUTOR-NEXT: ret void +; tail call void @use1(ptr dereferenceable(4) %arg) ret void } @@ -239,6 +488,28 @@ ; We can say that %a is nonnull but %b is not. define void @f16(ptr %a, ptr %b, i8 %c) { ; FIXME: missing nonnull on %a +; FNATTRS-LABEL: define void @f16 +; FNATTRS-SAME: (ptr [[A:%.*]], ptr [[B:%.*]], i8 [[C:%.*]]) #[[ATTR6:[0-9]+]] { +; FNATTRS-NEXT: [[CMP:%.*]] = icmp eq i8 [[C]], 0 +; FNATTRS-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]] +; FNATTRS: if.then: +; FNATTRS-NEXT: tail call void @fun2(ptr nonnull [[A]], ptr nonnull [[B]]) +; FNATTRS-NEXT: ret void +; FNATTRS: if.else: +; FNATTRS-NEXT: tail call void @fun2(ptr nonnull [[A]], ptr [[B]]) +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR-LABEL: define void @f16 +; ATTRIBUTOR-SAME: (ptr nonnull [[A:%.*]], ptr [[B:%.*]], i8 [[C:%.*]]) #[[ATTR7:[0-9]+]] { +; ATTRIBUTOR-NEXT: [[CMP:%.*]] = icmp eq i8 [[C]], 0 +; ATTRIBUTOR-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]] +; ATTRIBUTOR: if.then: +; ATTRIBUTOR-NEXT: tail call void @fun2(ptr nonnull [[A]], ptr nonnull [[B]]) #[[ATTR16:[0-9]+]] +; ATTRIBUTOR-NEXT: ret void +; ATTRIBUTOR: if.else: +; ATTRIBUTOR-NEXT: tail call void @fun2(ptr nonnull [[A]], ptr [[B]]) #[[ATTR16]] +; ATTRIBUTOR-NEXT: ret void +; %cmp = icmp eq i8 %c, 0 br i1 %cmp, label %if.then, label %if.else if.then: @@ -256,6 +527,34 @@ ; fun1(nonnull %a) ; We can say that %a is nonnull define void @f17(ptr %a, i8 %c) { +; FNATTRS-LABEL: define void @f17 +; FNATTRS-SAME: (ptr [[A:%.*]], i8 [[C:%.*]]) #[[ATTR6]] { +; FNATTRS-NEXT: [[CMP:%.*]] = icmp eq i8 [[C]], 0 +; FNATTRS-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]] +; FNATTRS: if.then: +; FNATTRS-NEXT: tail call void @fun0() +; FNATTRS-NEXT: br label [[CONT:%.*]] +; FNATTRS: if.else: +; FNATTRS-NEXT: tail call void @fun0() +; FNATTRS-NEXT: br label [[CONT]] +; FNATTRS: cont: +; FNATTRS-NEXT: tail call void @fun1(ptr nonnull [[A]]) +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR-LABEL: define void @f17 +; ATTRIBUTOR-SAME: (ptr nonnull [[A:%.*]], i8 [[C:%.*]]) #[[ATTR7]] { +; ATTRIBUTOR-NEXT: [[CMP:%.*]] = icmp eq i8 [[C]], 0 +; ATTRIBUTOR-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]] +; ATTRIBUTOR: if.then: +; ATTRIBUTOR-NEXT: tail call void @fun0() #[[ATTR16]] +; ATTRIBUTOR-NEXT: br label [[CONT:%.*]] +; ATTRIBUTOR: if.else: +; ATTRIBUTOR-NEXT: tail call void @fun0() #[[ATTR16]] +; ATTRIBUTOR-NEXT: br label [[CONT]] +; ATTRIBUTOR: cont: +; ATTRIBUTOR-NEXT: tail call void @fun1(ptr nonnull [[A]]) #[[ATTR16]] +; ATTRIBUTOR-NEXT: ret void +; %cmp = icmp eq i8 %c, 0 br i1 %cmp, label %if.then, label %if.else if.then: @@ -280,6 +579,52 @@ ; fun1(nonnull %a) define void @f18(ptr %a, ptr %b, i8 %c) { +; FNATTRS-LABEL: define void @f18 +; FNATTRS-SAME: (ptr [[A:%.*]], ptr [[B:%.*]], i8 [[C:%.*]]) #[[ATTR6]] { +; FNATTRS-NEXT: [[CMP1:%.*]] = icmp eq i8 [[C]], 0 +; FNATTRS-NEXT: br i1 [[CMP1]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]] +; FNATTRS: if.then: +; FNATTRS-NEXT: tail call void @fun0() +; FNATTRS-NEXT: br label [[CONT:%.*]] +; FNATTRS: if.else: +; FNATTRS-NEXT: tail call void @fun0() +; FNATTRS-NEXT: br label [[CONT]] +; FNATTRS: cont: +; FNATTRS-NEXT: [[CMP2:%.*]] = icmp eq i8 [[C]], 1 +; FNATTRS-NEXT: br i1 [[CMP2]], label [[CONT_THEN:%.*]], label [[CONT_ELSE:%.*]] +; FNATTRS: cont.then: +; FNATTRS-NEXT: tail call void @fun1(ptr nonnull [[B]]) +; FNATTRS-NEXT: br label [[CONT2:%.*]] +; FNATTRS: cont.else: +; FNATTRS-NEXT: tail call void @fun0() +; FNATTRS-NEXT: br label [[CONT2]] +; FNATTRS: cont2: +; FNATTRS-NEXT: tail call void @fun1(ptr nonnull [[A]]) +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR-LABEL: define void @f18 +; ATTRIBUTOR-SAME: (ptr nonnull [[A:%.*]], ptr [[B:%.*]], i8 [[C:%.*]]) #[[ATTR7]] { +; ATTRIBUTOR-NEXT: [[CMP1:%.*]] = icmp eq i8 [[C]], 0 +; ATTRIBUTOR-NEXT: br i1 [[CMP1]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]] +; ATTRIBUTOR: if.then: +; ATTRIBUTOR-NEXT: tail call void @fun0() #[[ATTR16]] +; ATTRIBUTOR-NEXT: br label [[CONT:%.*]] +; ATTRIBUTOR: if.else: +; ATTRIBUTOR-NEXT: tail call void @fun0() #[[ATTR16]] +; ATTRIBUTOR-NEXT: br label [[CONT]] +; ATTRIBUTOR: cont: +; ATTRIBUTOR-NEXT: [[CMP2:%.*]] = icmp eq i8 [[C]], 1 +; ATTRIBUTOR-NEXT: br i1 [[CMP2]], label [[CONT_THEN:%.*]], label [[CONT_ELSE:%.*]] +; ATTRIBUTOR: cont.then: +; ATTRIBUTOR-NEXT: tail call void @fun1(ptr nonnull [[B]]) #[[ATTR16]] +; ATTRIBUTOR-NEXT: br label [[CONT2:%.*]] +; ATTRIBUTOR: cont.else: +; ATTRIBUTOR-NEXT: tail call void @fun0() #[[ATTR16]] +; ATTRIBUTOR-NEXT: br label [[CONT2]] +; ATTRIBUTOR: cont2: +; ATTRIBUTOR-NEXT: tail call void @fun1(ptr nonnull [[A]]) #[[ATTR16]] +; ATTRIBUTOR-NEXT: ret void +; %cmp1 = icmp eq i8 %c, 0 br i1 %cmp1, label %if.then, label %if.else if.then: @@ -306,6 +651,34 @@ define void @f19(ptr %a, ptr %b, i8 %c) { ; FIXME: missing nonnull on %b +; FNATTRS-LABEL: define void @f19 +; FNATTRS-SAME: (ptr [[A:%.*]], ptr [[B:%.*]], i8 [[C:%.*]]) #[[ATTR7:[0-9]+]] { +; FNATTRS-NEXT: br label [[LOOP_HEADER:%.*]] +; FNATTRS: loop.header: +; FNATTRS-NEXT: [[CMP2:%.*]] = icmp eq i8 [[C]], 0 +; FNATTRS-NEXT: br i1 [[CMP2]], label [[LOOP_BODY:%.*]], label [[LOOP_EXIT:%.*]] +; FNATTRS: loop.body: +; FNATTRS-NEXT: tail call void @fun1(ptr nonnull [[B]]) +; FNATTRS-NEXT: tail call void @fun1(ptr nonnull [[A]]) +; FNATTRS-NEXT: br label [[LOOP_HEADER]] +; FNATTRS: loop.exit: +; FNATTRS-NEXT: tail call void @fun1(ptr nonnull [[B]]) +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR-LABEL: define void @f19 +; ATTRIBUTOR-SAME: (ptr [[A:%.*]], ptr nonnull [[B:%.*]], i8 [[C:%.*]]) #[[ATTR8:[0-9]+]] { +; ATTRIBUTOR-NEXT: br label [[LOOP_HEADER:%.*]] +; ATTRIBUTOR: loop.header: +; ATTRIBUTOR-NEXT: [[CMP2:%.*]] = icmp eq i8 [[C]], 0 +; ATTRIBUTOR-NEXT: br i1 [[CMP2]], label [[LOOP_BODY:%.*]], label [[LOOP_EXIT:%.*]] +; ATTRIBUTOR: loop.body: +; ATTRIBUTOR-NEXT: tail call void @fun1(ptr nonnull [[B]]) +; ATTRIBUTOR-NEXT: tail call void @fun1(ptr nonnull [[A]]) +; ATTRIBUTOR-NEXT: br label [[LOOP_HEADER]] +; ATTRIBUTOR: loop.exit: +; ATTRIBUTOR-NEXT: tail call void @fun1(ptr nonnull [[B]]) +; ATTRIBUTOR-NEXT: ret void +; br label %loop.header loop.header: %cmp2 = icmp eq i8 %c, 0 @@ -336,6 +709,16 @@ define void @parent_poison(ptr %a) { ; FNATTR-LABEL: @parent_poison(ptr %a) +; FNATTRS-LABEL: define void @parent_poison +; FNATTRS-SAME: (ptr [[A:%.*]]) { +; FNATTRS-NEXT: call void @use1nonnull_without_noundef(ptr [[A]]) +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR-LABEL: define void @parent_poison +; ATTRIBUTOR-SAME: (ptr nonnull [[A:%.*]]) { +; ATTRIBUTOR-NEXT: call void @use1nonnull_without_noundef(ptr nonnull [[A]]) +; ATTRIBUTOR-NEXT: ret void +; call void @use1nonnull_without_noundef(ptr %a) ret void } @@ -343,10 +726,12 @@ ; Can't extend non-null to parent for any argument because the 2nd call is not guaranteed to execute. define void @parent1(ptr %a, ptr %b, ptr %c) { -; FNATTR-LABEL: @parent1(ptr %a, ptr %b, ptr %c) -; FNATTR-NEXT: call void @use3(ptr %c, ptr %a, ptr %b) -; FNATTR-NEXT: call void @use3nonnull(ptr %b, ptr %c, ptr %a) -; FNATTR-NEXT: ret void +; COMMON-LABEL: define void @parent1 +; COMMON-SAME: (ptr [[A:%.*]], ptr [[B:%.*]], ptr [[C:%.*]]) { +; COMMON-NEXT: call void @use3(ptr [[C]], ptr [[A]], ptr [[B]]) +; COMMON-NEXT: call void @use3nonnull(ptr [[B]], ptr [[C]], ptr [[A]]) +; COMMON-NEXT: ret void +; call void @use3(ptr %c, ptr %a, ptr %b) call void @use3nonnull(ptr %b, ptr %c, ptr %a) ret void @@ -355,12 +740,20 @@ ; Extend non-null to parent for all arguments. define void @parent2(ptr %a, ptr %b, ptr %c) { -; FNATTR-LABEL: @parent2(ptr nonnull %a, ptr nonnull %b, ptr nonnull %c) -; FNATTR-NEXT: call void @use3nonnull(ptr %b, ptr %c, ptr %a) -; FNATTR-NEXT: call void @use3(ptr %c, ptr %a, ptr %b) +; FNATTRS-LABEL: define void @parent2 +; FNATTRS-SAME: (ptr nonnull [[A:%.*]], ptr nonnull [[B:%.*]], ptr nonnull [[C:%.*]]) { +; FNATTRS-NEXT: call void @use3nonnull(ptr [[B]], ptr [[C]], ptr [[A]]) +; FNATTRS-NEXT: call void @use3(ptr [[C]], ptr [[A]], ptr [[B]]) +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR-LABEL: define void @parent2 +; ATTRIBUTOR-SAME: (ptr nonnull [[A:%.*]], ptr nonnull [[B:%.*]], ptr nonnull [[C:%.*]]) { +; ATTRIBUTOR-NEXT: call void @use3nonnull(ptr nonnull [[B]], ptr nonnull [[C]], ptr nonnull [[A]]) +; ATTRIBUTOR-NEXT: call void @use3(ptr [[C]], ptr [[A]], ptr [[B]]) +; ATTRIBUTOR-NEXT: ret void +; -; FNATTR-NEXT: ret void call void @use3nonnull(ptr %b, ptr %c, ptr %a) call void @use3(ptr %c, ptr %a, ptr %b) ret void @@ -369,12 +762,20 @@ ; Extend non-null to parent for 1st argument. define void @parent3(ptr %a, ptr %b, ptr %c) { -; FNATTR-LABEL: @parent3(ptr nonnull %a, ptr %b, ptr %c) -; FNATTR-NEXT: call void @use1nonnull(ptr %a) -; FNATTR-NEXT: call void @use3(ptr %c, ptr %b, ptr %a) +; FNATTRS-LABEL: define void @parent3 +; FNATTRS-SAME: (ptr nonnull [[A:%.*]], ptr [[B:%.*]], ptr [[C:%.*]]) { +; FNATTRS-NEXT: call void @use1nonnull(ptr [[A]]) +; FNATTRS-NEXT: call void @use3(ptr [[C]], ptr [[B]], ptr [[A]]) +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR-LABEL: define void @parent3 +; ATTRIBUTOR-SAME: (ptr nonnull [[A:%.*]], ptr [[B:%.*]], ptr [[C:%.*]]) { +; ATTRIBUTOR-NEXT: call void @use1nonnull(ptr nonnull [[A]]) +; ATTRIBUTOR-NEXT: call void @use3(ptr [[C]], ptr [[B]], ptr [[A]]) +; ATTRIBUTOR-NEXT: ret void +; -; FNATTR-NEXT: ret void call void @use1nonnull(ptr %a) call void @use3(ptr %c, ptr %b, ptr %a) @@ -388,9 +789,20 @@ ; CHECK-NEXT: call void @use2nonnull(ptr %c, ptr %b) ; CHECK-NEXT: call void @use2(ptr %a, ptr %c) ; CHECK-NEXT: call void @use1(ptr %b) - - -; FNATTR: ret void +; FNATTRS-LABEL: define void @parent4 +; FNATTRS-SAME: (ptr [[A:%.*]], ptr nonnull [[B:%.*]], ptr nonnull [[C:%.*]]) { +; FNATTRS-NEXT: call void @use2nonnull(ptr [[C]], ptr [[B]]) +; FNATTRS-NEXT: call void @use2(ptr [[A]], ptr [[C]]) +; FNATTRS-NEXT: call void @use1(ptr [[B]]) +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR-LABEL: define void @parent4 +; ATTRIBUTOR-SAME: (ptr [[A:%.*]], ptr nonnull [[B:%.*]], ptr nonnull [[C:%.*]]) { +; ATTRIBUTOR-NEXT: call void @use2nonnull(ptr nonnull [[C]], ptr nonnull [[B]]) +; ATTRIBUTOR-NEXT: call void @use2(ptr [[A]], ptr [[C]]) +; ATTRIBUTOR-NEXT: call void @use1(ptr [[B]]) +; ATTRIBUTOR-NEXT: ret void +; call void @use2nonnull(ptr %c, ptr %b) call void @use2(ptr %a, ptr %c) @@ -403,13 +815,24 @@ ; because it would incorrectly propagate the wrong information to its callers. define void @parent5(ptr %a, i1 %a_is_notnull) { -; FNATTR: @parent5(ptr %a, i1 %a_is_notnull) -; FNATTR-NEXT: br i1 %a_is_notnull, label %t, label %f -; FNATTR: t: -; FNATTR-NEXT: call void @use1nonnull(ptr %a) -; FNATTR-NEXT: ret void -; FNATTR: f: -; FNATTR-NEXT: ret void +; FNATTRS-LABEL: define void @parent5 +; FNATTRS-SAME: (ptr [[A:%.*]], i1 [[A_IS_NOTNULL:%.*]]) { +; FNATTRS-NEXT: br i1 [[A_IS_NOTNULL]], label [[T:%.*]], label [[F:%.*]] +; FNATTRS: t: +; FNATTRS-NEXT: call void @use1nonnull(ptr [[A]]) +; FNATTRS-NEXT: ret void +; FNATTRS: f: +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR-LABEL: define void @parent5 +; ATTRIBUTOR-SAME: (ptr [[A:%.*]], i1 [[A_IS_NOTNULL:%.*]]) { +; ATTRIBUTOR-NEXT: br i1 [[A_IS_NOTNULL]], label [[T:%.*]], label [[F:%.*]] +; ATTRIBUTOR: t: +; ATTRIBUTOR-NEXT: call void @use1nonnull(ptr nonnull [[A]]) +; ATTRIBUTOR-NEXT: ret void +; ATTRIBUTOR: f: +; ATTRIBUTOR-NEXT: ret void +; br i1 %a_is_notnull, label %t, label %f t: @@ -423,10 +846,18 @@ ; The volatile load can't trap, so we can guarantee that we'll get to the call. define i8 @parent6(ptr %a, ptr %b) { -; FNATTR-LABEL: @parent6(ptr nonnull %a, ptr %b) -; FNATTR-NEXT: [[C:%.*]] = load volatile i8, ptr %b -; FNATTR-NEXT: call void @use1nonnull(ptr %a) -; FNATTR-NEXT: ret i8 [[C]] +; FNATTRS-LABEL: define i8 @parent6 +; FNATTRS-SAME: (ptr nonnull [[A:%.*]], ptr [[B:%.*]]) { +; FNATTRS-NEXT: [[C:%.*]] = load volatile i8, ptr [[B]], align 1 +; FNATTRS-NEXT: call void @use1nonnull(ptr [[A]]) +; FNATTRS-NEXT: ret i8 [[C]] +; +; ATTRIBUTOR-LABEL: define i8 @parent6 +; ATTRIBUTOR-SAME: (ptr nonnull [[A:%.*]], ptr nofree [[B:%.*]]) { +; ATTRIBUTOR-NEXT: [[C:%.*]] = load volatile i8, ptr [[B]], align 1 +; ATTRIBUTOR-NEXT: call void @use1nonnull(ptr nonnull [[A]]) +; ATTRIBUTOR-NEXT: ret i8 [[C]] +; %c = load volatile i8, ptr %b call void @use1nonnull(ptr %a) @@ -436,13 +867,21 @@ ; The nonnull callsite is guaranteed to execute, so the argument must be nonnull throughout the parent. define i8 @parent7(ptr %a) { -; FNATTR-LABEL: @parent7(ptr nonnull %a) -; FNATTR-NEXT: [[RET:%.*]] = call i8 @use1safecall(ptr %a) -; FNATTR-NEXT: call void @use1nonnull(ptr %a) +; FNATTRS-LABEL: define i8 @parent7 +; FNATTRS-SAME: (ptr nonnull [[A:%.*]]) { +; FNATTRS-NEXT: [[RET:%.*]] = call i8 @use1safecall(ptr [[A]]) +; FNATTRS-NEXT: call void @use1nonnull(ptr [[A]]) +; FNATTRS-NEXT: ret i8 [[RET]] +; +; ATTRIBUTOR-LABEL: define i8 @parent7 +; ATTRIBUTOR-SAME: (ptr nonnull [[A:%.*]]) { +; ATTRIBUTOR-NEXT: [[RET:%.*]] = call i8 @use1safecall(ptr nonnull [[A]]) #[[ATTR16]] +; ATTRIBUTOR-NEXT: call void @use1nonnull(ptr nonnull [[A]]) +; ATTRIBUTOR-NEXT: ret i8 [[RET]] +; -; FNATTR-NEXT: ret i8 [[RET]] %ret = call i8 @use1safecall(ptr %a) call void @use1nonnull(ptr %a) @@ -454,17 +893,32 @@ declare i32 @esfp(...) define i1 @parent8(ptr %a, ptr %bogus1, ptr %b) personality ptr @esfp{ -; FNATTR-LABEL: @parent8(ptr nonnull %a, ptr nocapture readnone %bogus1, ptr nonnull %b) -; FNATTR-NEXT: entry: -; FNATTR-NEXT: invoke void @use2nonnull(ptr %a, ptr %b) -; FNATTR-NEXT: to label %cont unwind label %exc -; FNATTR: cont: -; FNATTR-NEXT: [[NULL_CHECK:%.*]] = icmp eq ptr %b, null -; FNATTR-NEXT: ret i1 [[NULL_CHECK]] -; FNATTR: exc: -; FNATTR-NEXT: [[LP:%.*]] = landingpad { ptr, i32 } -; FNATTR-NEXT: filter [0 x ptr] zeroinitializer -; FNATTR-NEXT: unreachable +; FNATTRS-LABEL: define i1 @parent8 +; FNATTRS-SAME: (ptr nonnull [[A:%.*]], ptr nocapture readnone [[BOGUS1:%.*]], ptr nonnull [[B:%.*]]) #[[ATTR7]] personality ptr @esfp { +; FNATTRS-NEXT: entry: +; FNATTRS-NEXT: invoke void @use2nonnull(ptr [[A]], ptr [[B]]) +; FNATTRS-NEXT: to label [[CONT:%.*]] unwind label [[EXC:%.*]] +; FNATTRS: cont: +; FNATTRS-NEXT: [[NULL_CHECK:%.*]] = icmp eq ptr [[B]], null +; FNATTRS-NEXT: ret i1 [[NULL_CHECK]] +; FNATTRS: exc: +; FNATTRS-NEXT: [[LP:%.*]] = landingpad { ptr, i32 } +; FNATTRS-NEXT: filter [0 x ptr] zeroinitializer +; FNATTRS-NEXT: unreachable +; +; ATTRIBUTOR-LABEL: define i1 @parent8 +; ATTRIBUTOR-SAME: (ptr nonnull [[A:%.*]], ptr nocapture nofree readnone [[BOGUS1:%.*]], ptr nonnull [[B:%.*]]) #[[ATTR8]] personality ptr @esfp { +; ATTRIBUTOR-NEXT: entry: +; ATTRIBUTOR-NEXT: invoke void @use2nonnull(ptr nonnull [[A]], ptr nonnull [[B]]) +; ATTRIBUTOR-NEXT: to label [[CONT:%.*]] unwind label [[EXC:%.*]] +; ATTRIBUTOR: cont: +; ATTRIBUTOR-NEXT: [[NULL_CHECK:%.*]] = icmp eq ptr [[B]], null +; ATTRIBUTOR-NEXT: ret i1 [[NULL_CHECK]] +; ATTRIBUTOR: exc: +; ATTRIBUTOR-NEXT: [[LP:%.*]] = landingpad { ptr, i32 } +; ATTRIBUTOR-NEXT: filter [0 x ptr] zeroinitializer +; ATTRIBUTOR-NEXT: unreachable +; entry: invoke void @use2nonnull(ptr %a, ptr %b) @@ -480,69 +934,184 @@ unreachable } -; FNATTR: define nonnull ptr @gep1( define ptr @gep1(ptr %p) { +; FNATTRS-LABEL: define nonnull ptr @gep1 +; FNATTRS-SAME: (ptr readnone [[P:%.*]]) #[[ATTR0]] { +; FNATTRS-NEXT: [[Q:%.*]] = getelementptr inbounds i32, ptr [[P]], i32 1 +; FNATTRS-NEXT: ret ptr [[Q]] +; +; ATTRIBUTOR-LABEL: define nonnull ptr @gep1 +; ATTRIBUTOR-SAME: (ptr nofree readnone [[P:%.*]]) #[[ATTR0]] { +; ATTRIBUTOR-NEXT: [[Q:%.*]] = getelementptr inbounds i32, ptr [[P]], i32 1 +; ATTRIBUTOR-NEXT: ret ptr [[Q]] +; %q = getelementptr inbounds i32, ptr %p, i32 1 ret ptr %q } define ptr @gep1_no_null_opt(ptr %p) #0 { ; Should't be able to derive nonnull based on gep. -; FNATTR: define ptr @gep1_no_null_opt( +; FNATTRS-LABEL: define ptr @gep1_no_null_opt +; FNATTRS-SAME: (ptr readnone [[P:%.*]]) #[[ATTR8:[0-9]+]] { +; FNATTRS-NEXT: [[Q:%.*]] = getelementptr inbounds i32, ptr [[P]], i32 1 +; FNATTRS-NEXT: ret ptr [[Q]] +; +; ATTRIBUTOR-LABEL: define ptr @gep1_no_null_opt +; ATTRIBUTOR-SAME: (ptr nofree readnone [[P:%.*]]) #[[ATTR9:[0-9]+]] { +; ATTRIBUTOR-NEXT: [[Q:%.*]] = getelementptr inbounds i32, ptr [[P]], i32 1 +; ATTRIBUTOR-NEXT: ret ptr [[Q]] +; %q = getelementptr inbounds i32, ptr %p, i32 1 ret ptr %q } -; FNATTR: define ptr addrspace(3) @gep2( define ptr addrspace(3) @gep2(ptr addrspace(3) %p) { +; FNATTRS-LABEL: define ptr addrspace(3) @gep2 +; FNATTRS-SAME: (ptr addrspace(3) readnone [[P:%.*]]) #[[ATTR0]] { +; FNATTRS-NEXT: [[Q:%.*]] = getelementptr inbounds i32, ptr addrspace(3) [[P]], i32 1 +; FNATTRS-NEXT: ret ptr addrspace(3) [[Q]] +; +; ATTRIBUTOR-LABEL: define ptr addrspace(3) @gep2 +; ATTRIBUTOR-SAME: (ptr addrspace(3) nofree readnone [[P:%.*]]) #[[ATTR0]] { +; ATTRIBUTOR-NEXT: [[Q:%.*]] = getelementptr inbounds i32, ptr addrspace(3) [[P]], i32 1 +; ATTRIBUTOR-NEXT: ret ptr addrspace(3) [[Q]] +; %q = getelementptr inbounds i32, ptr addrspace(3) %p, i32 1 ret ptr addrspace(3) %q } -; FNATTR: define ptr addrspace(3) @as(ptr addrspace(3) readnone returned dereferenceable(4) %p) ; FIXME: We should propagate dereferenceable here but *not* nonnull define ptr addrspace(3) @as(ptr addrspace(3) dereferenceable(4) %p) { +; FNATTRS-LABEL: define ptr addrspace(3) @as +; FNATTRS-SAME: (ptr addrspace(3) readnone returned dereferenceable(4) [[P:%.*]]) #[[ATTR0]] { +; FNATTRS-NEXT: ret ptr addrspace(3) [[P]] +; +; ATTRIBUTOR-LABEL: define ptr addrspace(3) @as +; ATTRIBUTOR-SAME: (ptr addrspace(3) nofree readnone dereferenceable(4) [[P:%.*]]) #[[ATTR0]] { +; ATTRIBUTOR-NEXT: ret ptr addrspace(3) [[P]] +; ret ptr addrspace(3) %p } -; FNATTR: define internal nonnull ptr @g2() define internal ptr @g2() { +; FNATTRS-LABEL: define internal nonnull ptr @g2 +; FNATTRS-SAME: () #[[ATTR0]] { +; FNATTRS-NEXT: ret ptr inttoptr (i64 4 to ptr) +; +; ATTRIBUTOR-LABEL: define internal ptr @g2 +; ATTRIBUTOR-SAME: () #[[ATTR10:[0-9]+]] { +; ATTRIBUTOR-NEXT: ret ptr inttoptr (i64 4 to ptr) +; ret ptr inttoptr (i64 4 to ptr) } define ptr @g1() { - %c = call ptr @g2() +; FNATTRS-LABEL: define nonnull ptr @g1 +; FNATTRS-SAME: () #[[ATTR0]] { +; FNATTRS-NEXT: [[C:%.*]] = call ptr @g2() +; FNATTRS-NEXT: ret ptr [[C]] +; +; ATTRIBUTOR-LABEL: define ptr @g1 +; ATTRIBUTOR-SAME: () #[[ATTR0]] { +; ATTRIBUTOR-NEXT: [[C:%.*]] = call ptr @g2() #[[ATTR10]] +; ATTRIBUTOR-NEXT: ret ptr [[C]] +; + %c = call ptr @g2() ret ptr %c } declare void @use_i32_ptr(ptr) readnone nounwind define internal void @called_by_weak(ptr %a) { +; FNATTRS-LABEL: define internal void @called_by_weak +; FNATTRS-SAME: (ptr nocapture readnone [[A:%.*]]) #[[ATTR1]] { +; FNATTRS-NEXT: call void @use_i32_ptr(ptr [[A]]) +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR-LABEL: define internal void @called_by_weak +; ATTRIBUTOR-SAME: (ptr nocapture readnone [[A:%.*]]) #[[ATTR11:[0-9]+]] { +; ATTRIBUTOR-NEXT: call void @use_i32_ptr(ptr [[A]]) +; ATTRIBUTOR-NEXT: ret void +; call void @use_i32_ptr(ptr %a) ret void } ; Check we do not annotate the function interface of this weak function. define weak_odr void @weak_caller(ptr nonnull %a) { +; FNATTRS-LABEL: define weak_odr void @weak_caller +; FNATTRS-SAME: (ptr nonnull [[A:%.*]]) { +; FNATTRS-NEXT: call void @called_by_weak(ptr [[A]]) +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR-LABEL: define weak_odr void @weak_caller +; ATTRIBUTOR-SAME: (ptr nonnull [[A:%.*]]) { +; ATTRIBUTOR-NEXT: call void @called_by_weak(ptr nocapture nonnull readnone [[A]]) +; ATTRIBUTOR-NEXT: ret void +; call void @called_by_weak(ptr %a) ret void } ; Expect nonnull define internal void @control(ptr dereferenceable(4) %a) { +; FNATTRS-LABEL: define internal void @control +; FNATTRS-SAME: (ptr nocapture readnone dereferenceable(4) [[A:%.*]]) #[[ATTR1]] { +; FNATTRS-NEXT: call void @use_i32_ptr(ptr [[A]]) +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR-LABEL: define internal void @control +; ATTRIBUTOR-SAME: (ptr nocapture readnone dereferenceable(4) [[A:%.*]]) #[[ATTR11]] { +; ATTRIBUTOR-NEXT: call void @use_i32_ptr(ptr [[A]]) +; ATTRIBUTOR-NEXT: ret void +; call void @use_i32_ptr(ptr %a) ret void } ; Avoid nonnull as we do not touch naked functions define internal void @naked(ptr dereferenceable(4) %a) naked { +; FNATTRS-LABEL: define internal void @naked +; FNATTRS-SAME: (ptr dereferenceable(4) [[A:%.*]]) #[[ATTR10:[0-9]+]] { +; FNATTRS-NEXT: call void @use_i32_ptr(ptr [[A]]) +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR-LABEL: define internal void @naked +; ATTRIBUTOR-SAME: (ptr dereferenceable(4) [[A:%.*]]) #[[ATTR12:[0-9]+]] { +; ATTRIBUTOR-NEXT: call void @use_i32_ptr(ptr [[A]]) +; ATTRIBUTOR-NEXT: ret void +; call void @use_i32_ptr(ptr %a) ret void } ; Avoid nonnull as we do not touch optnone define internal void @optnone(ptr dereferenceable(4) %a) optnone noinline { +; FNATTRS-LABEL: define internal void @optnone +; FNATTRS-SAME: (ptr dereferenceable(4) [[A:%.*]]) #[[ATTR11:[0-9]+]] { +; FNATTRS-NEXT: call void @use_i32_ptr(ptr [[A]]) +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR-LABEL: define internal void @optnone +; ATTRIBUTOR-SAME: (ptr dereferenceable(4) [[A:%.*]]) #[[ATTR13:[0-9]+]] { +; ATTRIBUTOR-NEXT: call void @use_i32_ptr(ptr [[A]]) +; ATTRIBUTOR-NEXT: ret void +; call void @use_i32_ptr(ptr %a) ret void } define void @make_live(ptr nonnull dereferenceable(8) %a) { +; FNATTRS-LABEL: define void @make_live +; FNATTRS-SAME: (ptr nonnull dereferenceable(8) [[A:%.*]]) { +; FNATTRS-NEXT: call void @naked(ptr nonnull align 16 dereferenceable(8) [[A]]) +; FNATTRS-NEXT: call void @control(ptr nonnull align 16 dereferenceable(8) [[A]]) +; FNATTRS-NEXT: call void @optnone(ptr nonnull align 16 dereferenceable(8) [[A]]) +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR-LABEL: define void @make_live +; ATTRIBUTOR-SAME: (ptr nonnull dereferenceable(8) [[A:%.*]]) { +; ATTRIBUTOR-NEXT: call void @naked(ptr nonnull align 16 dereferenceable(8) [[A]]) +; ATTRIBUTOR-NEXT: call void @control(ptr nocapture nonnull readnone align 16 dereferenceable(8) [[A]]) +; ATTRIBUTOR-NEXT: call void @optnone(ptr nonnull align 16 dereferenceable(8) [[A]]) +; ATTRIBUTOR-NEXT: ret void +; call void @naked(ptr nonnull dereferenceable(8) align 16 %a) call void @control(ptr nonnull dereferenceable(8) align 16 %a) call void @optnone(ptr nonnull dereferenceable(8) align 16 %a) @@ -558,21 +1127,35 @@ declare void @h(ptr) willreturn nounwind declare i32 @g(ptr) willreturn nounwind define i32 @nonnull_exec_ctx_1(ptr %a, i32 %b) { -; FNATTR-LABEL: define {{[^@]+}}@nonnull_exec_ctx_1 -; FNATTR-SAME: (ptr [[A:%.*]], i32 [[B:%.*]]) -; FNATTR-NEXT: en: -; FNATTR-NEXT: [[TMP3:%.*]] = icmp eq i32 [[B:%.*]], 0 -; FNATTR-NEXT: br i1 [[TMP3]], label [[EX:%.*]], label [[HD:%.*]] -; FNATTR: ex: -; FNATTR-NEXT: [[TMP5:%.*]] = tail call i32 @g(ptr nonnull [[A:%.*]]) -; FNATTR-NEXT: ret i32 [[TMP5]] -; FNATTR: hd: -; FNATTR-NEXT: [[TMP7:%.*]] = phi i32 [ [[TMP8:%.*]], [[HD]] ], [ 0, [[EN:%.*]] ] -; FNATTR-NEXT: tail call void @h(ptr [[A]]) -; FNATTR-NEXT: [[TMP8]] = add nuw i32 [[TMP7]], 1 -; FNATTR-NEXT: [[TMP9:%.*]] = icmp eq i32 [[TMP8]], [[B]] -; FNATTR-NEXT: br i1 [[TMP9]], label [[EX]], label [[HD]] +; FNATTRS-LABEL: define i32 @nonnull_exec_ctx_1 +; FNATTRS-SAME: (ptr [[A:%.*]], i32 [[B:%.*]]) #[[ATTR7]] { +; FNATTRS-NEXT: en: +; FNATTRS-NEXT: [[TMP3:%.*]] = icmp eq i32 [[B]], 0 +; FNATTRS-NEXT: br i1 [[TMP3]], label [[EX:%.*]], label [[HD:%.*]] +; FNATTRS: ex: +; FNATTRS-NEXT: [[TMP5:%.*]] = tail call i32 @g(ptr nonnull [[A]]) +; FNATTRS-NEXT: ret i32 [[TMP5]] +; FNATTRS: hd: +; FNATTRS-NEXT: [[TMP7:%.*]] = phi i32 [ [[TMP8:%.*]], [[HD]] ], [ 0, [[EN:%.*]] ] +; FNATTRS-NEXT: tail call void @h(ptr [[A]]) +; FNATTRS-NEXT: [[TMP8]] = add nuw i32 [[TMP7]], 1 +; FNATTRS-NEXT: [[TMP9:%.*]] = icmp eq i32 [[TMP8]], [[B]] +; FNATTRS-NEXT: br i1 [[TMP9]], label [[EX]], label [[HD]] ; +; ATTRIBUTOR-LABEL: define i32 @nonnull_exec_ctx_1 +; ATTRIBUTOR-SAME: (ptr [[A:%.*]], i32 [[B:%.*]]) #[[ATTR8]] { +; ATTRIBUTOR-NEXT: en: +; ATTRIBUTOR-NEXT: [[TMP3:%.*]] = icmp eq i32 [[B]], 0 +; ATTRIBUTOR-NEXT: br i1 [[TMP3]], label [[EX:%.*]], label [[HD:%.*]] +; ATTRIBUTOR: ex: +; ATTRIBUTOR-NEXT: [[TMP5:%.*]] = tail call i32 @g(ptr nonnull [[A]]) +; ATTRIBUTOR-NEXT: ret i32 [[TMP5]] +; ATTRIBUTOR: hd: +; ATTRIBUTOR-NEXT: [[TMP7:%.*]] = phi i32 [ [[TMP8:%.*]], [[HD]] ], [ 0, [[EN:%.*]] ] +; ATTRIBUTOR-NEXT: tail call void @h(ptr [[A]]) +; ATTRIBUTOR-NEXT: [[TMP8]] = add nuw i32 [[TMP7]], 1 +; ATTRIBUTOR-NEXT: [[TMP9:%.*]] = icmp eq i32 [[TMP8]], [[B]] +; ATTRIBUTOR-NEXT: br i1 [[TMP9]], label [[EX]], label [[HD]] ; en: %tmp3 = icmp eq i32 %b, 0 @@ -591,23 +1174,39 @@ } define i32 @nonnull_exec_ctx_1b(ptr %a, i32 %b) { -; FNATTR-LABEL: define {{[^@]+}}@nonnull_exec_ctx_1b -; FNATTR-SAME: (ptr [[A:%.*]], i32 [[B:%.*]]) -; FNATTR-NEXT: en: -; FNATTR-NEXT: [[TMP3:%.*]] = icmp eq i32 [[B:%.*]], 0 -; FNATTR-NEXT: br i1 [[TMP3]], label [[EX:%.*]], label [[HD:%.*]] -; FNATTR: ex: -; FNATTR-NEXT: [[TMP5:%.*]] = tail call i32 @g(ptr nonnull [[A:%.*]]) -; FNATTR-NEXT: ret i32 [[TMP5]] -; FNATTR: hd: -; FNATTR-NEXT: [[TMP7:%.*]] = phi i32 [ [[TMP8:%.*]], [[HD2:%.*]] ], [ 0, [[EN:%.*]] ] -; FNATTR-NEXT: tail call void @h(ptr [[A]]) -; FNATTR-NEXT: br label [[HD2]] -; FNATTR: hd2: -; FNATTR-NEXT: [[TMP8]] = add nuw i32 [[TMP7]], 1 -; FNATTR-NEXT: [[TMP9:%.*]] = icmp eq i32 [[TMP8]], [[B]] -; FNATTR-NEXT: br i1 [[TMP9]], label [[EX]], label [[HD]] +; FNATTRS-LABEL: define i32 @nonnull_exec_ctx_1b +; FNATTRS-SAME: (ptr [[A:%.*]], i32 [[B:%.*]]) #[[ATTR7]] { +; FNATTRS-NEXT: en: +; FNATTRS-NEXT: [[TMP3:%.*]] = icmp eq i32 [[B]], 0 +; FNATTRS-NEXT: br i1 [[TMP3]], label [[EX:%.*]], label [[HD:%.*]] +; FNATTRS: ex: +; FNATTRS-NEXT: [[TMP5:%.*]] = tail call i32 @g(ptr nonnull [[A]]) +; FNATTRS-NEXT: ret i32 [[TMP5]] +; FNATTRS: hd: +; FNATTRS-NEXT: [[TMP7:%.*]] = phi i32 [ [[TMP8:%.*]], [[HD2:%.*]] ], [ 0, [[EN:%.*]] ] +; FNATTRS-NEXT: tail call void @h(ptr [[A]]) +; FNATTRS-NEXT: br label [[HD2]] +; FNATTRS: hd2: +; FNATTRS-NEXT: [[TMP8]] = add nuw i32 [[TMP7]], 1 +; FNATTRS-NEXT: [[TMP9:%.*]] = icmp eq i32 [[TMP8]], [[B]] +; FNATTRS-NEXT: br i1 [[TMP9]], label [[EX]], label [[HD]] ; +; ATTRIBUTOR-LABEL: define i32 @nonnull_exec_ctx_1b +; ATTRIBUTOR-SAME: (ptr [[A:%.*]], i32 [[B:%.*]]) #[[ATTR8]] { +; ATTRIBUTOR-NEXT: en: +; ATTRIBUTOR-NEXT: [[TMP3:%.*]] = icmp eq i32 [[B]], 0 +; ATTRIBUTOR-NEXT: br i1 [[TMP3]], label [[EX:%.*]], label [[HD:%.*]] +; ATTRIBUTOR: ex: +; ATTRIBUTOR-NEXT: [[TMP5:%.*]] = tail call i32 @g(ptr nonnull [[A]]) +; ATTRIBUTOR-NEXT: ret i32 [[TMP5]] +; ATTRIBUTOR: hd: +; ATTRIBUTOR-NEXT: [[TMP7:%.*]] = phi i32 [ [[TMP8:%.*]], [[HD2:%.*]] ], [ 0, [[EN:%.*]] ] +; ATTRIBUTOR-NEXT: tail call void @h(ptr [[A]]) +; ATTRIBUTOR-NEXT: br label [[HD2]] +; ATTRIBUTOR: hd2: +; ATTRIBUTOR-NEXT: [[TMP8]] = add nuw i32 [[TMP7]], 1 +; ATTRIBUTOR-NEXT: [[TMP9:%.*]] = icmp eq i32 [[TMP8]], [[B]] +; ATTRIBUTOR-NEXT: br i1 [[TMP9]], label [[EX]], label [[HD]] ; en: %tmp3 = icmp eq i32 %b, 0 @@ -629,21 +1228,35 @@ } define i32 @nonnull_exec_ctx_2(ptr %a, i32 %b) willreturn nounwind { -; FNATTR-LABEL: define {{[^@]+}}@nonnull_exec_ctx_2 -; FNATTR-SAME: (ptr [[A:%.*]], i32 [[B:%.*]]) -; FNATTR-NEXT: en: -; FNATTR-NEXT: [[TMP3:%.*]] = icmp eq i32 [[B:%.*]], 0 -; FNATTR-NEXT: br i1 [[TMP3]], label [[EX:%.*]], label [[HD:%.*]] -; FNATTR: ex: -; FNATTR-NEXT: [[TMP5:%.*]] = tail call i32 @g(ptr nonnull [[A:%.*]]) -; FNATTR-NEXT: ret i32 [[TMP5]] -; FNATTR: hd: -; FNATTR-NEXT: [[TMP7:%.*]] = phi i32 [ [[TMP8:%.*]], [[HD]] ], [ 0, [[EN:%.*]] ] -; FNATTR-NEXT: tail call void @h(ptr [[A]]) -; FNATTR-NEXT: [[TMP8]] = add nuw i32 [[TMP7]], 1 -; FNATTR-NEXT: [[TMP9:%.*]] = icmp eq i32 [[TMP8]], [[B]] -; FNATTR-NEXT: br i1 [[TMP9]], label [[EX]], label [[HD]] +; FNATTRS-LABEL: define i32 @nonnull_exec_ctx_2 +; FNATTRS-SAME: (ptr [[A:%.*]], i32 [[B:%.*]]) #[[ATTR6]] { +; FNATTRS-NEXT: en: +; FNATTRS-NEXT: [[TMP3:%.*]] = icmp eq i32 [[B]], 0 +; FNATTRS-NEXT: br i1 [[TMP3]], label [[EX:%.*]], label [[HD:%.*]] +; FNATTRS: ex: +; FNATTRS-NEXT: [[TMP5:%.*]] = tail call i32 @g(ptr nonnull [[A]]) +; FNATTRS-NEXT: ret i32 [[TMP5]] +; FNATTRS: hd: +; FNATTRS-NEXT: [[TMP7:%.*]] = phi i32 [ [[TMP8:%.*]], [[HD]] ], [ 0, [[EN:%.*]] ] +; FNATTRS-NEXT: tail call void @h(ptr [[A]]) +; FNATTRS-NEXT: [[TMP8]] = add nuw i32 [[TMP7]], 1 +; FNATTRS-NEXT: [[TMP9:%.*]] = icmp eq i32 [[TMP8]], [[B]] +; FNATTRS-NEXT: br i1 [[TMP9]], label [[EX]], label [[HD]] ; +; ATTRIBUTOR-LABEL: define i32 @nonnull_exec_ctx_2 +; ATTRIBUTOR-SAME: (ptr nonnull [[A:%.*]], i32 [[B:%.*]]) #[[ATTR7]] { +; ATTRIBUTOR-NEXT: en: +; ATTRIBUTOR-NEXT: [[TMP3:%.*]] = icmp eq i32 [[B]], 0 +; ATTRIBUTOR-NEXT: br i1 [[TMP3]], label [[EX:%.*]], label [[HD:%.*]] +; ATTRIBUTOR: ex: +; ATTRIBUTOR-NEXT: [[TMP5:%.*]] = tail call i32 @g(ptr nonnull [[A]]) +; ATTRIBUTOR-NEXT: ret i32 [[TMP5]] +; ATTRIBUTOR: hd: +; ATTRIBUTOR-NEXT: [[TMP7:%.*]] = phi i32 [ [[TMP8:%.*]], [[HD]] ], [ 0, [[EN:%.*]] ] +; ATTRIBUTOR-NEXT: tail call void @h(ptr nonnull [[A]]) +; ATTRIBUTOR-NEXT: [[TMP8]] = add nuw i32 [[TMP7]], 1 +; ATTRIBUTOR-NEXT: [[TMP9:%.*]] = icmp eq i32 [[TMP8]], [[B]] +; ATTRIBUTOR-NEXT: br i1 [[TMP9]], label [[EX]], label [[HD]] ; en: %tmp3 = icmp eq i32 %b, 0 @@ -662,23 +1275,39 @@ } define i32 @nonnull_exec_ctx_2b(ptr %a, i32 %b) willreturn nounwind { -; FNATTR-LABEL: define {{[^@]+}}@nonnull_exec_ctx_2b -; FNATTR-SAME: (ptr [[A:%.*]], i32 [[B:%.*]]) -; FNATTR-NEXT: en: -; FNATTR-NEXT: [[TMP3:%.*]] = icmp eq i32 [[B:%.*]], 0 -; FNATTR-NEXT: br i1 [[TMP3]], label [[EX:%.*]], label [[HD:%.*]] -; FNATTR: ex: -; FNATTR-NEXT: [[TMP5:%.*]] = tail call i32 @g(ptr nonnull [[A:%.*]]) -; FNATTR-NEXT: ret i32 [[TMP5]] -; FNATTR: hd: -; FNATTR-NEXT: [[TMP7:%.*]] = phi i32 [ [[TMP8:%.*]], [[HD2:%.*]] ], [ 0, [[EN:%.*]] ] -; FNATTR-NEXT: tail call void @h(ptr [[A]]) -; FNATTR-NEXT: br label [[HD2]] -; FNATTR: hd2: -; FNATTR-NEXT: [[TMP8]] = add nuw i32 [[TMP7]], 1 -; FNATTR-NEXT: [[TMP9:%.*]] = icmp eq i32 [[TMP8]], [[B]] -; FNATTR-NEXT: br i1 [[TMP9]], label [[EX]], label [[HD]] +; FNATTRS-LABEL: define i32 @nonnull_exec_ctx_2b +; FNATTRS-SAME: (ptr [[A:%.*]], i32 [[B:%.*]]) #[[ATTR6]] { +; FNATTRS-NEXT: en: +; FNATTRS-NEXT: [[TMP3:%.*]] = icmp eq i32 [[B]], 0 +; FNATTRS-NEXT: br i1 [[TMP3]], label [[EX:%.*]], label [[HD:%.*]] +; FNATTRS: ex: +; FNATTRS-NEXT: [[TMP5:%.*]] = tail call i32 @g(ptr nonnull [[A]]) +; FNATTRS-NEXT: ret i32 [[TMP5]] +; FNATTRS: hd: +; FNATTRS-NEXT: [[TMP7:%.*]] = phi i32 [ [[TMP8:%.*]], [[HD2:%.*]] ], [ 0, [[EN:%.*]] ] +; FNATTRS-NEXT: tail call void @h(ptr [[A]]) +; FNATTRS-NEXT: br label [[HD2]] +; FNATTRS: hd2: +; FNATTRS-NEXT: [[TMP8]] = add nuw i32 [[TMP7]], 1 +; FNATTRS-NEXT: [[TMP9:%.*]] = icmp eq i32 [[TMP8]], [[B]] +; FNATTRS-NEXT: br i1 [[TMP9]], label [[EX]], label [[HD]] ; +; ATTRIBUTOR-LABEL: define i32 @nonnull_exec_ctx_2b +; ATTRIBUTOR-SAME: (ptr nonnull [[A:%.*]], i32 [[B:%.*]]) #[[ATTR7]] { +; ATTRIBUTOR-NEXT: en: +; ATTRIBUTOR-NEXT: [[TMP3:%.*]] = icmp eq i32 [[B]], 0 +; ATTRIBUTOR-NEXT: br i1 [[TMP3]], label [[EX:%.*]], label [[HD:%.*]] +; ATTRIBUTOR: ex: +; ATTRIBUTOR-NEXT: [[TMP5:%.*]] = tail call i32 @g(ptr nonnull [[A]]) +; ATTRIBUTOR-NEXT: ret i32 [[TMP5]] +; ATTRIBUTOR: hd: +; ATTRIBUTOR-NEXT: [[TMP7:%.*]] = phi i32 [ [[TMP8:%.*]], [[HD2:%.*]] ], [ 0, [[EN:%.*]] ] +; ATTRIBUTOR-NEXT: tail call void @h(ptr nonnull [[A]]) +; ATTRIBUTOR-NEXT: br label [[HD2]] +; ATTRIBUTOR: hd2: +; ATTRIBUTOR-NEXT: [[TMP8]] = add nuw i32 [[TMP7]], 1 +; ATTRIBUTOR-NEXT: [[TMP9:%.*]] = icmp eq i32 [[TMP8]], [[B]] +; ATTRIBUTOR-NEXT: br i1 [[TMP9]], label [[EX]], label [[HD]] ; en: %tmp3 = icmp eq i32 %b, 0 @@ -704,21 +1333,22 @@ ; FIXME: the sink argument should be marked nonnull as in @PR43833_simple. define void @PR43833(ptr %0, i32 %1) { -; FNATTR-LABEL: @PR43833( -; FNATTR-NEXT: [[TMP3:%.*]] = icmp sgt i32 [[TMP1:%.*]], 1 -; FNATTR-NEXT: br i1 [[TMP3]], label [[TMP4:%.*]], label [[TMP7:%.*]] -; FNATTR: 4: -; FNATTR-NEXT: [[TMP5:%.*]] = zext i32 [[TMP1]] to i64 -; FNATTR-NEXT: [[TMP6:%.*]] = getelementptr inbounds i32, ptr [[TMP0:%.*]], i64 [[TMP5]] -; FNATTR-NEXT: br label [[TMP8:%.*]] -; FNATTR: 7: -; FNATTR-NEXT: ret void -; FNATTR: 8: -; FNATTR-NEXT: [[TMP9:%.*]] = phi i32 [ 1, [[TMP4]] ], [ [[TMP10:%.*]], [[TMP8]] ] -; FNATTR-NEXT: tail call void @sink(ptr [[TMP6]]) -; FNATTR-NEXT: [[TMP10]] = add nuw nsw i32 [[TMP9]], 1 -; FNATTR-NEXT: [[TMP11:%.*]] = icmp eq i32 [[TMP10]], [[TMP1]] -; FNATTR-NEXT: br i1 [[TMP11]], label [[TMP7]], label [[TMP8]] +; COMMON-LABEL: define void @PR43833 +; COMMON-SAME: (ptr [[TMP0:%.*]], i32 [[TMP1:%.*]]) { +; COMMON-NEXT: [[TMP3:%.*]] = icmp sgt i32 [[TMP1]], 1 +; COMMON-NEXT: br i1 [[TMP3]], label [[TMP4:%.*]], label [[TMP7:%.*]] +; COMMON: 4: +; COMMON-NEXT: [[TMP5:%.*]] = zext i32 [[TMP1]] to i64 +; COMMON-NEXT: [[TMP6:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 [[TMP5]] +; COMMON-NEXT: br label [[TMP8:%.*]] +; COMMON: 7: +; COMMON-NEXT: ret void +; COMMON: 8: +; COMMON-NEXT: [[TMP9:%.*]] = phi i32 [ 1, [[TMP4]] ], [ [[TMP10:%.*]], [[TMP8]] ] +; COMMON-NEXT: tail call void @sink(ptr [[TMP6]]) +; COMMON-NEXT: [[TMP10]] = add nuw nsw i32 [[TMP9]], 1 +; COMMON-NEXT: [[TMP11:%.*]] = icmp eq i32 [[TMP10]], [[TMP1]] +; COMMON-NEXT: br i1 [[TMP11]], label [[TMP7]], label [[TMP8]] ; %3 = icmp sgt i32 %1, 1 br i1 %3, label %4, label %7 @@ -741,22 +1371,22 @@ ; Adjusted from PR43833 define void @PR43833_simple(ptr %0, i32 %1) { -; FNATTR-LABEL: @PR43833_simple( -; FNATTR-NEXT: [[TMP3:%.*]] = icmp ne i32 [[TMP1:%.*]], 0 -; FNATTR-NEXT: br i1 [[TMP3]], label [[TMP4:%.*]], label [[TMP7:%.*]] -; FNATTR: 4: -; FNATTR-NEXT: [[TMP5:%.*]] = zext i32 [[TMP1]] to i64 -; FNATTR-NEXT: [[TMP6:%.*]] = getelementptr inbounds i32, ptr [[TMP0:%.*]], i64 [[TMP5]] -; FNATTR-NEXT: br label [[TMP8:%.*]] -; FNATTR: 7: -; FNATTR-NEXT: ret void -; FNATTR: 8: -; FNATTR-NEXT: [[TMP9:%.*]] = phi i32 [ 1, [[TMP4]] ], [ [[TMP10:%.*]], [[TMP8]] ] -; FNATTR-NEXT: tail call void @sink(ptr [[TMP6]]) -; FNATTR-NEXT: [[TMP10]] = add nuw nsw i32 [[TMP9]], 1 -; FNATTR-NEXT: [[TMP11:%.*]] = icmp eq i32 [[TMP10]], [[TMP1]] -; FNATTR-NEXT: br i1 [[TMP11]], label [[TMP7]], label [[TMP8]] -; +; COMMON-LABEL: define void @PR43833_simple +; COMMON-SAME: (ptr [[TMP0:%.*]], i32 [[TMP1:%.*]]) { +; COMMON-NEXT: [[TMP3:%.*]] = icmp ne i32 [[TMP1]], 0 +; COMMON-NEXT: br i1 [[TMP3]], label [[TMP4:%.*]], label [[TMP7:%.*]] +; COMMON: 4: +; COMMON-NEXT: [[TMP5:%.*]] = zext i32 [[TMP1]] to i64 +; COMMON-NEXT: [[TMP6:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 [[TMP5]] +; COMMON-NEXT: br label [[TMP8:%.*]] +; COMMON: 7: +; COMMON-NEXT: ret void +; COMMON: 8: +; COMMON-NEXT: [[TMP9:%.*]] = phi i32 [ 1, [[TMP4]] ], [ [[TMP10:%.*]], [[TMP8]] ] +; COMMON-NEXT: tail call void @sink(ptr [[TMP6]]) +; COMMON-NEXT: [[TMP10]] = add nuw nsw i32 [[TMP9]], 1 +; COMMON-NEXT: [[TMP11:%.*]] = icmp eq i32 [[TMP10]], [[TMP1]] +; COMMON-NEXT: br i1 [[TMP11]], label [[TMP7]], label [[TMP8]] ; %3 = icmp ne i32 %1, 0 br i1 %3, label %4, label %7 diff --git a/llvm/test/Transforms/FunctionAttrs/norecurse.ll b/llvm/test/Transforms/FunctionAttrs/norecurse.ll --- a/llvm/test/Transforms/FunctionAttrs/norecurse.ll +++ b/llvm/test/Transforms/FunctionAttrs/norecurse.ll @@ -1,54 +1,80 @@ -; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes -; RUN: opt < %s -aa-pipeline=basic-aa -passes='cgscc(function-attrs),rpo-function-attrs' -S | FileCheck %s +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes --check-globals +; RUN: opt < %s -aa-pipeline=basic-aa -passes='cgscc(function-attrs),rpo-function-attrs' -S | FileCheck --check-prefixes=COMMON,FNATTRS %s +; RUN: opt -passes=attributor-light -S < %s | FileCheck --check-prefixes=COMMON,ATTRIBUTOR %s + define i32 @leaf() { -; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) -; CHECK-LABEL: define {{[^@]+}}@leaf -; CHECK-SAME: () #[[ATTR0:[0-9]+]] { -; CHECK-NEXT: ret i32 1 +; COMMON: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) +; COMMON-LABEL: define {{[^@]+}}@leaf +; COMMON-SAME: () #[[ATTR0:[0-9]+]] { +; COMMON-NEXT: ret i32 1 ; ret i32 1 } define i32 @self_rec() { -; CHECK: Function Attrs: nofree nosync nounwind memory(none) -; CHECK-LABEL: define {{[^@]+}}@self_rec -; CHECK-SAME: () #[[ATTR1:[0-9]+]] { -; CHECK-NEXT: [[A:%.*]] = call i32 @self_rec() -; CHECK-NEXT: ret i32 4 +; FNATTRS: Function Attrs: nofree nosync nounwind memory(none) +; FNATTRS-LABEL: define {{[^@]+}}@self_rec +; FNATTRS-SAME: () #[[ATTR1:[0-9]+]] { +; FNATTRS-NEXT: [[A:%.*]] = call i32 @self_rec() +; FNATTRS-NEXT: ret i32 4 +; +; ATTRIBUTOR: Function Attrs: nofree nosync nounwind memory(none) +; ATTRIBUTOR-LABEL: define {{[^@]+}}@self_rec +; ATTRIBUTOR-SAME: () #[[ATTR1:[0-9]+]] { +; ATTRIBUTOR-NEXT: [[A:%.*]] = call i32 @self_rec() #[[ATTR1]] +; ATTRIBUTOR-NEXT: ret i32 4 ; %a = call i32 @self_rec() ret i32 4 } define i32 @indirect_rec() { -; CHECK: Function Attrs: nofree nosync nounwind memory(none) -; CHECK-LABEL: define {{[^@]+}}@indirect_rec -; CHECK-SAME: () #[[ATTR1]] { -; CHECK-NEXT: [[A:%.*]] = call i32 @indirect_rec2() -; CHECK-NEXT: ret i32 [[A]] +; FNATTRS: Function Attrs: nofree nosync nounwind memory(none) +; FNATTRS-LABEL: define {{[^@]+}}@indirect_rec +; FNATTRS-SAME: () #[[ATTR1]] { +; FNATTRS-NEXT: [[A:%.*]] = call i32 @indirect_rec2() +; FNATTRS-NEXT: ret i32 [[A]] +; +; ATTRIBUTOR: Function Attrs: nofree nosync nounwind memory(none) +; ATTRIBUTOR-LABEL: define {{[^@]+}}@indirect_rec +; ATTRIBUTOR-SAME: () #[[ATTR1]] { +; ATTRIBUTOR-NEXT: [[A:%.*]] = call i32 @indirect_rec2() #[[ATTR1]] +; ATTRIBUTOR-NEXT: ret i32 [[A]] ; %a = call i32 @indirect_rec2() ret i32 %a } define i32 @indirect_rec2() { -; CHECK: Function Attrs: nofree nosync nounwind memory(none) -; CHECK-LABEL: define {{[^@]+}}@indirect_rec2 -; CHECK-SAME: () #[[ATTR1]] { -; CHECK-NEXT: [[A:%.*]] = call i32 @indirect_rec() -; CHECK-NEXT: ret i32 [[A]] +; FNATTRS: Function Attrs: nofree nosync nounwind memory(none) +; FNATTRS-LABEL: define {{[^@]+}}@indirect_rec2 +; FNATTRS-SAME: () #[[ATTR1]] { +; FNATTRS-NEXT: [[A:%.*]] = call i32 @indirect_rec() +; FNATTRS-NEXT: ret i32 [[A]] +; +; ATTRIBUTOR: Function Attrs: nofree nosync nounwind memory(none) +; ATTRIBUTOR-LABEL: define {{[^@]+}}@indirect_rec2 +; ATTRIBUTOR-SAME: () #[[ATTR1]] { +; ATTRIBUTOR-NEXT: [[A:%.*]] = call i32 @indirect_rec() #[[ATTR1]] +; ATTRIBUTOR-NEXT: ret i32 [[A]] ; %a = call i32 @indirect_rec() ret i32 %a } define i32 @extern() { -; CHECK: Function Attrs: nofree nosync memory(none) -; CHECK-LABEL: define {{[^@]+}}@extern -; CHECK-SAME: () #[[ATTR2:[0-9]+]] { -; CHECK-NEXT: [[A:%.*]] = call i32 @k() -; CHECK-NEXT: ret i32 [[A]] +; FNATTRS: Function Attrs: nofree nosync memory(none) +; FNATTRS-LABEL: define {{[^@]+}}@extern +; FNATTRS-SAME: () #[[ATTR2:[0-9]+]] { +; FNATTRS-NEXT: [[A:%.*]] = call i32 @k() +; FNATTRS-NEXT: ret i32 [[A]] +; +; ATTRIBUTOR: Function Attrs: nosync memory(none) +; ATTRIBUTOR-LABEL: define {{[^@]+}}@extern +; ATTRIBUTOR-SAME: () #[[ATTR2:[0-9]+]] { +; ATTRIBUTOR-NEXT: [[A:%.*]] = call i32 @k() +; ATTRIBUTOR-NEXT: ret i32 [[A]] ; %a = call i32 @k() ret i32 %a @@ -57,11 +83,17 @@ declare i32 @k() readnone define void @intrinsic(ptr %dest, ptr %src, i32 %len) { -; CHECK: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(argmem: readwrite) -; CHECK-LABEL: define {{[^@]+}}@intrinsic -; CHECK-SAME: (ptr nocapture writeonly [[DEST:%.*]], ptr nocapture readonly [[SRC:%.*]], i32 [[LEN:%.*]]) #[[ATTR4:[0-9]+]] { -; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr [[DEST]], ptr [[SRC]], i32 [[LEN]], i1 false) -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(argmem: readwrite) +; FNATTRS-LABEL: define {{[^@]+}}@intrinsic +; FNATTRS-SAME: (ptr nocapture writeonly [[DEST:%.*]], ptr nocapture readonly [[SRC:%.*]], i32 [[LEN:%.*]]) #[[ATTR4:[0-9]+]] { +; FNATTRS-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr [[DEST]], ptr [[SRC]], i32 [[LEN]], i1 false) +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite) +; ATTRIBUTOR-LABEL: define {{[^@]+}}@intrinsic +; ATTRIBUTOR-SAME: (ptr nocapture nofree writeonly [[DEST:%.*]], ptr nocapture nofree readonly [[SRC:%.*]], i32 [[LEN:%.*]]) #[[ATTR4:[0-9]+]] { +; ATTRIBUTOR-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr nocapture writeonly [[DEST]], ptr nocapture readonly [[SRC]], i32 [[LEN]], i1 false) #[[ATTR7:[0-9]+]] +; ATTRIBUTOR-NEXT: ret void ; call void @llvm.memcpy.p0.p0.i32(ptr %dest, ptr %src, i32 %len, i1 false) ret void @@ -70,89 +102,155 @@ declare void @llvm.memcpy.p0.p0.i32(ptr, ptr, i32, i1) define internal i32 @called_by_norecurse() { -; CHECK: Function Attrs: nofree norecurse nosync memory(none) -; CHECK-LABEL: define {{[^@]+}}@called_by_norecurse -; CHECK-SAME: () #[[ATTR6:[0-9]+]] { -; CHECK-NEXT: [[A:%.*]] = call i32 @k() -; CHECK-NEXT: ret i32 [[A]] +; FNATTRS: Function Attrs: nofree norecurse nosync memory(none) +; FNATTRS-LABEL: define {{[^@]+}}@called_by_norecurse +; FNATTRS-SAME: () #[[ATTR6:[0-9]+]] { +; FNATTRS-NEXT: [[A:%.*]] = call i32 @k() +; FNATTRS-NEXT: ret i32 [[A]] +; +; ATTRIBUTOR: Function Attrs: nosync memory(none) +; ATTRIBUTOR-LABEL: define {{[^@]+}}@called_by_norecurse +; ATTRIBUTOR-SAME: () #[[ATTR2]] { +; ATTRIBUTOR-NEXT: [[A:%.*]] = call i32 @k() +; ATTRIBUTOR-NEXT: ret i32 [[A]] ; %a = call i32 @k() ret i32 %a } define void @m() norecurse { -; CHECK: Function Attrs: nofree norecurse nosync memory(none) -; CHECK-LABEL: define {{[^@]+}}@m -; CHECK-SAME: () #[[ATTR6]] { -; CHECK-NEXT: [[A:%.*]] = call i32 @called_by_norecurse() -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: nofree norecurse nosync memory(none) +; FNATTRS-LABEL: define {{[^@]+}}@m +; FNATTRS-SAME: () #[[ATTR6]] { +; FNATTRS-NEXT: [[A:%.*]] = call i32 @called_by_norecurse() +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: norecurse nosync memory(none) +; ATTRIBUTOR-LABEL: define {{[^@]+}}@m +; ATTRIBUTOR-SAME: () #[[ATTR6:[0-9]+]] { +; ATTRIBUTOR-NEXT: [[A:%.*]] = call i32 @called_by_norecurse() #[[ATTR2]] +; ATTRIBUTOR-NEXT: ret void ; %a = call i32 @called_by_norecurse() ret void } define internal i32 @called_by_norecurse_indirectly() { -; CHECK: Function Attrs: nofree norecurse nosync memory(none) -; CHECK-LABEL: define {{[^@]+}}@called_by_norecurse_indirectly -; CHECK-SAME: () #[[ATTR6]] { -; CHECK-NEXT: [[A:%.*]] = call i32 @k() -; CHECK-NEXT: ret i32 [[A]] +; FNATTRS: Function Attrs: nofree norecurse nosync memory(none) +; FNATTRS-LABEL: define {{[^@]+}}@called_by_norecurse_indirectly +; FNATTRS-SAME: () #[[ATTR6]] { +; FNATTRS-NEXT: [[A:%.*]] = call i32 @k() +; FNATTRS-NEXT: ret i32 [[A]] +; +; ATTRIBUTOR: Function Attrs: nosync memory(none) +; ATTRIBUTOR-LABEL: define {{[^@]+}}@called_by_norecurse_indirectly +; ATTRIBUTOR-SAME: () #[[ATTR2]] { +; ATTRIBUTOR-NEXT: [[A:%.*]] = call i32 @k() +; ATTRIBUTOR-NEXT: ret i32 [[A]] ; %a = call i32 @k() ret i32 %a } define internal void @o() { -; CHECK: Function Attrs: nofree norecurse nosync memory(none) -; CHECK-LABEL: define {{[^@]+}}@o -; CHECK-SAME: () #[[ATTR6]] { -; CHECK-NEXT: [[A:%.*]] = call i32 @called_by_norecurse_indirectly() -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: nofree norecurse nosync memory(none) +; FNATTRS-LABEL: define {{[^@]+}}@o +; FNATTRS-SAME: () #[[ATTR6]] { +; FNATTRS-NEXT: [[A:%.*]] = call i32 @called_by_norecurse_indirectly() +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: nosync memory(none) +; ATTRIBUTOR-LABEL: define {{[^@]+}}@o +; ATTRIBUTOR-SAME: () #[[ATTR2]] { +; ATTRIBUTOR-NEXT: [[A:%.*]] = call i32 @called_by_norecurse_indirectly() #[[ATTR2]] +; ATTRIBUTOR-NEXT: ret void ; %a = call i32 @called_by_norecurse_indirectly() ret void } define void @p() norecurse { -; CHECK: Function Attrs: nofree norecurse nosync memory(none) -; CHECK-LABEL: define {{[^@]+}}@p -; CHECK-SAME: () #[[ATTR6]] { -; CHECK-NEXT: call void @o() -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: nofree norecurse nosync memory(none) +; FNATTRS-LABEL: define {{[^@]+}}@p +; FNATTRS-SAME: () #[[ATTR6]] { +; FNATTRS-NEXT: call void @o() +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: norecurse nosync memory(none) +; ATTRIBUTOR-LABEL: define {{[^@]+}}@p +; ATTRIBUTOR-SAME: () #[[ATTR6]] { +; ATTRIBUTOR-NEXT: call void @o() #[[ATTR2]] +; ATTRIBUTOR-NEXT: ret void ; call void @o() ret void } define internal i32 @escapes_as_parameter(ptr %p) { -; CHECK: Function Attrs: nofree nosync memory(none) -; CHECK-LABEL: define {{[^@]+}}@escapes_as_parameter -; CHECK-SAME: (ptr nocapture readnone [[P:%.*]]) #[[ATTR2]] { -; CHECK-NEXT: [[A:%.*]] = call i32 @k() -; CHECK-NEXT: ret i32 [[A]] +; FNATTRS: Function Attrs: nofree nosync memory(none) +; FNATTRS-LABEL: define {{[^@]+}}@escapes_as_parameter +; FNATTRS-SAME: (ptr nocapture readnone [[P:%.*]]) #[[ATTR2]] { +; FNATTRS-NEXT: [[A:%.*]] = call i32 @k() +; FNATTRS-NEXT: ret i32 [[A]] +; +; ATTRIBUTOR: Function Attrs: nosync memory(none) +; ATTRIBUTOR-LABEL: define {{[^@]+}}@escapes_as_parameter +; ATTRIBUTOR-SAME: (ptr nocapture nofree readnone [[P:%.*]]) #[[ATTR2]] { +; ATTRIBUTOR-NEXT: [[A:%.*]] = call i32 @k() +; ATTRIBUTOR-NEXT: ret i32 [[A]] ; %a = call i32 @k() ret i32 %a } define internal void @q() { -; CHECK: Function Attrs: nofree norecurse nosync memory(none) -; CHECK-LABEL: define {{[^@]+}}@q -; CHECK-SAME: () #[[ATTR6]] { -; CHECK-NEXT: [[A:%.*]] = call i32 @escapes_as_parameter(ptr @escapes_as_parameter) -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: nofree norecurse nosync memory(none) +; FNATTRS-LABEL: define {{[^@]+}}@q +; FNATTRS-SAME: () #[[ATTR6]] { +; FNATTRS-NEXT: [[A:%.*]] = call i32 @escapes_as_parameter(ptr @escapes_as_parameter) +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: norecurse nosync memory(none) +; ATTRIBUTOR-LABEL: define {{[^@]+}}@q +; ATTRIBUTOR-SAME: () #[[ATTR6]] { +; ATTRIBUTOR-NEXT: [[A:%.*]] = call i32 @escapes_as_parameter(ptr nonnull @escapes_as_parameter) #[[ATTR2]] +; ATTRIBUTOR-NEXT: ret void ; %a = call i32 @escapes_as_parameter(ptr @escapes_as_parameter) ret void } define void @r() norecurse { -; CHECK: Function Attrs: nofree norecurse nosync memory(none) -; CHECK-LABEL: define {{[^@]+}}@r -; CHECK-SAME: () #[[ATTR6]] { -; CHECK-NEXT: call void @q() -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: nofree norecurse nosync memory(none) +; FNATTRS-LABEL: define {{[^@]+}}@r +; FNATTRS-SAME: () #[[ATTR6]] { +; FNATTRS-NEXT: call void @q() +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: norecurse nosync memory(none) +; ATTRIBUTOR-LABEL: define {{[^@]+}}@r +; ATTRIBUTOR-SAME: () #[[ATTR6]] { +; ATTRIBUTOR-NEXT: call void @q() #[[ATTR2]] +; ATTRIBUTOR-NEXT: ret void ; call void @q() ret void } +;. +; FNATTRS: attributes #[[ATTR0]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) } +; FNATTRS: attributes #[[ATTR1]] = { nofree nosync nounwind memory(none) } +; FNATTRS: attributes #[[ATTR2]] = { nofree nosync memory(none) } +; FNATTRS: attributes #[[ATTR3:[0-9]+]] = { memory(none) } +; FNATTRS: attributes #[[ATTR4]] = { mustprogress nofree nosync nounwind willreturn memory(argmem: readwrite) } +; FNATTRS: attributes #[[ATTR5:[0-9]+]] = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } +; FNATTRS: attributes #[[ATTR6]] = { nofree norecurse nosync memory(none) } +;. +; ATTRIBUTOR: attributes #[[ATTR0]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) } +; ATTRIBUTOR: attributes #[[ATTR1]] = { nofree nosync nounwind memory(none) } +; ATTRIBUTOR: attributes #[[ATTR2]] = { nosync memory(none) } +; ATTRIBUTOR: attributes #[[ATTR3:[0-9]+]] = { memory(none) } +; ATTRIBUTOR: attributes #[[ATTR4]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite) } +; ATTRIBUTOR: attributes #[[ATTR5:[0-9]+]] = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } +; ATTRIBUTOR: attributes #[[ATTR6]] = { norecurse nosync memory(none) } +; ATTRIBUTOR: attributes #[[ATTR7]] = { nofree willreturn } +;. diff --git a/llvm/test/Transforms/FunctionAttrs/nounwind.ll b/llvm/test/Transforms/FunctionAttrs/nounwind.ll --- a/llvm/test/Transforms/FunctionAttrs/nounwind.ll +++ b/llvm/test/Transforms/FunctionAttrs/nounwind.ll @@ -1,23 +1,30 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes -; RUN: opt < %s -passes=function-attrs -S | FileCheck %s +; RUN: opt -passes=function-attrs -S < %s | FileCheck --check-prefixes=COMMON,FNATTRS %s +; RUN: opt -passes=attributor-light -S < %s | FileCheck --check-prefixes=COMMON,ATTRIBUTOR %s ; TEST 1 define i32 @foo1() { -; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) -; CHECK-LABEL: define {{[^@]+}}@foo1 -; CHECK-SAME: () #[[ATTR0:[0-9]+]] { -; CHECK-NEXT: ret i32 1 +; COMMON: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) +; COMMON-LABEL: define {{[^@]+}}@foo1 +; COMMON-SAME: () #[[ATTR0:[0-9]+]] { +; COMMON-NEXT: ret i32 1 ; ret i32 1 } ; TEST 2 define i32 @scc1_foo() { -; CHECK: Function Attrs: nofree nosync nounwind memory(none) -; CHECK-LABEL: define {{[^@]+}}@scc1_foo -; CHECK-SAME: () #[[ATTR1:[0-9]+]] { -; CHECK-NEXT: [[TMP1:%.*]] = call i32 @scc1_bar() -; CHECK-NEXT: ret i32 1 +; FNATTRS: Function Attrs: nofree nosync nounwind memory(none) +; FNATTRS-LABEL: define {{[^@]+}}@scc1_foo +; FNATTRS-SAME: () #[[ATTR1:[0-9]+]] { +; FNATTRS-NEXT: [[TMP1:%.*]] = call i32 @scc1_bar() +; FNATTRS-NEXT: ret i32 1 +; +; ATTRIBUTOR: Function Attrs: nofree nosync nounwind memory(none) +; ATTRIBUTOR-LABEL: define {{[^@]+}}@scc1_foo +; ATTRIBUTOR-SAME: () #[[ATTR1:[0-9]+]] { +; ATTRIBUTOR-NEXT: [[TMP1:%.*]] = call i32 @scc1_bar() #[[ATTR1]] +; ATTRIBUTOR-NEXT: ret i32 1 ; %1 = call i32 @scc1_bar() ret i32 1 @@ -26,11 +33,17 @@ ; TEST 3 define i32 @scc1_bar() { -; CHECK: Function Attrs: nofree nosync nounwind memory(none) -; CHECK-LABEL: define {{[^@]+}}@scc1_bar -; CHECK-SAME: () #[[ATTR1]] { -; CHECK-NEXT: [[TMP1:%.*]] = call i32 @scc1_foo() -; CHECK-NEXT: ret i32 1 +; FNATTRS: Function Attrs: nofree nosync nounwind memory(none) +; FNATTRS-LABEL: define {{[^@]+}}@scc1_bar +; FNATTRS-SAME: () #[[ATTR1]] { +; FNATTRS-NEXT: [[TMP1:%.*]] = call i32 @scc1_foo() +; FNATTRS-NEXT: ret i32 1 +; +; ATTRIBUTOR: Function Attrs: nofree nosync nounwind memory(none) +; ATTRIBUTOR-LABEL: define {{[^@]+}}@scc1_bar +; ATTRIBUTOR-SAME: () #[[ATTR1]] { +; ATTRIBUTOR-NEXT: [[TMP1:%.*]] = call i32 @scc1_foo() #[[ATTR1]] +; ATTRIBUTOR-NEXT: ret i32 1 ; %1 = call i32 @scc1_foo() ret i32 1 @@ -40,9 +53,9 @@ ; TEST 4 define void @call_non_nounwind(){ -; CHECK-LABEL: define {{[^@]+}}@call_non_nounwind() { -; CHECK-NEXT: [[TMP1:%.*]] = tail call i32 @non_nounwind() -; CHECK-NEXT: ret void +; COMMON-LABEL: define {{[^@]+}}@call_non_nounwind() { +; COMMON-NEXT: [[TMP1:%.*]] = tail call i32 @non_nounwind() +; COMMON-NEXT: ret void ; tail call i32 @non_nounwind() ret void @@ -57,14 +70,14 @@ ; } define i32 @maybe_throw(i1 zeroext %0) { -; CHECK-LABEL: define {{[^@]+}}@maybe_throw -; CHECK-SAME: (i1 zeroext [[TMP0:%.*]]) { -; CHECK-NEXT: br i1 [[TMP0]], label [[TMP2:%.*]], label [[TMP3:%.*]] -; CHECK: 2: -; CHECK-NEXT: tail call void @__cxa_rethrow() -; CHECK-NEXT: unreachable -; CHECK: 3: -; CHECK-NEXT: ret i32 -1 +; COMMON-LABEL: define {{[^@]+}}@maybe_throw +; COMMON-SAME: (i1 zeroext [[TMP0:%.*]]) { +; COMMON-NEXT: br i1 [[TMP0]], label [[TMP2:%.*]], label [[TMP3:%.*]] +; COMMON: 2: +; COMMON-NEXT: tail call void @__cxa_rethrow() +; COMMON-NEXT: unreachable +; COMMON: 3: +; COMMON-NEXT: ret i32 -1 ; br i1 %0, label %2, label %3 @@ -88,18 +101,18 @@ ; } define i32 @catch_thing() personality ptr @__gxx_personality_v0 { -; CHECK-LABEL: define {{[^@]+}}@catch_thing() personality ptr @__gxx_personality_v0 { -; CHECK-NEXT: invoke void @__cxa_rethrow() -; CHECK-NEXT: to label [[TMP1:%.*]] unwind label [[TMP2:%.*]] -; CHECK: 1: -; CHECK-NEXT: unreachable -; CHECK: 2: -; CHECK-NEXT: [[TMP3:%.*]] = landingpad { ptr, i32 } -; CHECK-NEXT: catch ptr null -; CHECK-NEXT: [[TMP4:%.*]] = extractvalue { ptr, i32 } [[TMP3]], 0 -; CHECK-NEXT: [[TMP5:%.*]] = tail call ptr @__cxa_begin_catch(ptr [[TMP4]]) -; CHECK-NEXT: tail call void @__cxa_end_catch() -; CHECK-NEXT: ret i32 -1 +; COMMON-LABEL: define {{[^@]+}}@catch_thing() personality ptr @__gxx_personality_v0 { +; COMMON-NEXT: invoke void @__cxa_rethrow() +; COMMON-NEXT: to label [[TMP1:%.*]] unwind label [[TMP2:%.*]] +; COMMON: 1: +; COMMON-NEXT: unreachable +; COMMON: 2: +; COMMON-NEXT: [[TMP3:%.*]] = landingpad { ptr, i32 } +; COMMON-NEXT: catch ptr null +; COMMON-NEXT: [[TMP4:%.*]] = extractvalue { ptr, i32 } [[TMP3]], 0 +; COMMON-NEXT: [[TMP5:%.*]] = tail call ptr @__cxa_begin_catch(ptr [[TMP4]]) +; COMMON-NEXT: tail call void @__cxa_end_catch() +; COMMON-NEXT: ret i32 -1 ; invoke void @__cxa_rethrow() #1 to label %1 unwind label %2 @@ -117,9 +130,9 @@ } define i32 @catch_thing_user() { -; CHECK-LABEL: define {{[^@]+}}@catch_thing_user() { -; CHECK-NEXT: [[CATCH_THING_CALL:%.*]] = call i32 @catch_thing() -; CHECK-NEXT: ret i32 [[CATCH_THING_CALL]] +; COMMON-LABEL: define {{[^@]+}}@catch_thing_user() { +; COMMON-NEXT: [[CATCH_THING_CALL:%.*]] = call i32 @catch_thing() +; COMMON-NEXT: ret i32 [[CATCH_THING_CALL]] ; %catch_thing_call = call i32 @catch_thing() ret i32 %catch_thing_call @@ -130,18 +143,18 @@ @catch_ty = external global ptr define void @catch_specific_landingpad() personality ptr @__gxx_personality_v0 { -; CHECK: Function Attrs: noreturn -; CHECK-LABEL: define {{[^@]+}}@catch_specific_landingpad -; CHECK-SAME: () #[[ATTR3:[0-9]+]] personality ptr @__gxx_personality_v0 { -; CHECK-NEXT: invoke void @do_throw() -; CHECK-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]] -; CHECK: lpad: -; CHECK-NEXT: [[LP:%.*]] = landingpad { ptr, i32 } -; CHECK-NEXT: catch ptr @catch_ty -; CHECK-NEXT: call void @abort() -; CHECK-NEXT: unreachable -; CHECK: unreachable: -; CHECK-NEXT: unreachable +; COMMON: Function Attrs: noreturn +; COMMON-LABEL: define {{[^@]+}}@catch_specific_landingpad +; COMMON-SAME: () #[[ATTR3:[0-9]+]] personality ptr @__gxx_personality_v0 { +; COMMON-NEXT: invoke void @do_throw() +; COMMON-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]] +; COMMON: lpad: +; COMMON-NEXT: [[LP:%.*]] = landingpad { ptr, i32 } +; COMMON-NEXT: catch ptr @catch_ty +; COMMON-NEXT: call void @abort() +; COMMON-NEXT: unreachable +; COMMON: unreachable: +; COMMON-NEXT: unreachable ; invoke void @do_throw() to label %unreachable unwind label %lpad @@ -157,18 +170,18 @@ } define void @catch_all_landingpad() personality ptr @__gxx_personality_v0 { -; CHECK: Function Attrs: noreturn nounwind -; CHECK-LABEL: define {{[^@]+}}@catch_all_landingpad -; CHECK-SAME: () #[[ATTR4:[0-9]+]] personality ptr @__gxx_personality_v0 { -; CHECK-NEXT: invoke void @do_throw() -; CHECK-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]] -; CHECK: lpad: -; CHECK-NEXT: [[LP:%.*]] = landingpad { ptr, i32 } -; CHECK-NEXT: catch ptr null -; CHECK-NEXT: call void @abort() -; CHECK-NEXT: unreachable -; CHECK: unreachable: -; CHECK-NEXT: unreachable +; COMMON: Function Attrs: noreturn nounwind +; COMMON-LABEL: define {{[^@]+}}@catch_all_landingpad +; COMMON-SAME: () #[[ATTR4:[0-9]+]] personality ptr @__gxx_personality_v0 { +; COMMON-NEXT: invoke void @do_throw() +; COMMON-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]] +; COMMON: lpad: +; COMMON-NEXT: [[LP:%.*]] = landingpad { ptr, i32 } +; COMMON-NEXT: catch ptr null +; COMMON-NEXT: call void @abort() +; COMMON-NEXT: unreachable +; COMMON: unreachable: +; COMMON-NEXT: unreachable ; invoke void @do_throw() to label %unreachable unwind label %lpad @@ -184,18 +197,18 @@ } define void @filter_specific_landingpad() personality ptr @__gxx_personality_v0 { -; CHECK: Function Attrs: noreturn -; CHECK-LABEL: define {{[^@]+}}@filter_specific_landingpad -; CHECK-SAME: () #[[ATTR3]] personality ptr @__gxx_personality_v0 { -; CHECK-NEXT: invoke void @do_throw() -; CHECK-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]] -; CHECK: lpad: -; CHECK-NEXT: [[LP:%.*]] = landingpad { ptr, i32 } -; CHECK-NEXT: filter [1 x ptr] [ptr @catch_ty] -; CHECK-NEXT: call void @abort() -; CHECK-NEXT: unreachable -; CHECK: unreachable: -; CHECK-NEXT: unreachable +; COMMON: Function Attrs: noreturn +; COMMON-LABEL: define {{[^@]+}}@filter_specific_landingpad +; COMMON-SAME: () #[[ATTR3]] personality ptr @__gxx_personality_v0 { +; COMMON-NEXT: invoke void @do_throw() +; COMMON-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]] +; COMMON: lpad: +; COMMON-NEXT: [[LP:%.*]] = landingpad { ptr, i32 } +; COMMON-NEXT: filter [1 x ptr] [ptr @catch_ty] +; COMMON-NEXT: call void @abort() +; COMMON-NEXT: unreachable +; COMMON: unreachable: +; COMMON-NEXT: unreachable ; invoke void @do_throw() to label %unreachable unwind label %lpad @@ -211,18 +224,18 @@ } define void @filter_none_landingpad() personality ptr @__gxx_personality_v0 { -; CHECK: Function Attrs: noreturn nounwind -; CHECK-LABEL: define {{[^@]+}}@filter_none_landingpad -; CHECK-SAME: () #[[ATTR4]] personality ptr @__gxx_personality_v0 { -; CHECK-NEXT: invoke void @do_throw() -; CHECK-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]] -; CHECK: lpad: -; CHECK-NEXT: [[LP:%.*]] = landingpad { ptr, i32 } -; CHECK-NEXT: filter [0 x ptr] zeroinitializer -; CHECK-NEXT: call void @abort() -; CHECK-NEXT: unreachable -; CHECK: unreachable: -; CHECK-NEXT: unreachable +; COMMON: Function Attrs: noreturn nounwind +; COMMON-LABEL: define {{[^@]+}}@filter_none_landingpad +; COMMON-SAME: () #[[ATTR4]] personality ptr @__gxx_personality_v0 { +; COMMON-NEXT: invoke void @do_throw() +; COMMON-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]] +; COMMON: lpad: +; COMMON-NEXT: [[LP:%.*]] = landingpad { ptr, i32 } +; COMMON-NEXT: filter [0 x ptr] zeroinitializer +; COMMON-NEXT: call void @abort() +; COMMON-NEXT: unreachable +; COMMON: unreachable: +; COMMON-NEXT: unreachable ; invoke void @do_throw() to label %unreachable unwind label %lpad @@ -238,18 +251,18 @@ } define void @cleanup_landingpad() personality ptr @__gxx_personality_v0 { -; CHECK: Function Attrs: noreturn -; CHECK-LABEL: define {{[^@]+}}@cleanup_landingpad -; CHECK-SAME: () #[[ATTR3]] personality ptr @__gxx_personality_v0 { -; CHECK-NEXT: invoke void @do_throw() -; CHECK-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]] -; CHECK: lpad: -; CHECK-NEXT: [[LP:%.*]] = landingpad { ptr, i32 } -; CHECK-NEXT: cleanup -; CHECK-NEXT: call void @abort() -; CHECK-NEXT: unreachable -; CHECK: unreachable: -; CHECK-NEXT: unreachable +; COMMON: Function Attrs: noreturn +; COMMON-LABEL: define {{[^@]+}}@cleanup_landingpad +; COMMON-SAME: () #[[ATTR3]] personality ptr @__gxx_personality_v0 { +; COMMON-NEXT: invoke void @do_throw() +; COMMON-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]] +; COMMON: lpad: +; COMMON-NEXT: [[LP:%.*]] = landingpad { ptr, i32 } +; COMMON-NEXT: cleanup +; COMMON-NEXT: call void @abort() +; COMMON-NEXT: unreachable +; COMMON: unreachable: +; COMMON-NEXT: unreachable ; invoke void @do_throw() to label %unreachable unwind label %lpad @@ -265,17 +278,29 @@ } define void @cleanuppad() personality ptr @__gxx_personality_v0 { -; CHECK: Function Attrs: noreturn -; CHECK-LABEL: define {{[^@]+}}@cleanuppad -; CHECK-SAME: () #[[ATTR3]] personality ptr @__gxx_personality_v0 { -; CHECK-NEXT: invoke void @do_throw() -; CHECK-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[CPAD:%.*]] -; CHECK: cpad: -; CHECK-NEXT: [[CP:%.*]] = cleanuppad within none [] -; CHECK-NEXT: call void @abort() -; CHECK-NEXT: unreachable -; CHECK: unreachable: -; CHECK-NEXT: unreachable +; FNATTRS: Function Attrs: noreturn +; FNATTRS-LABEL: define {{[^@]+}}@cleanuppad +; FNATTRS-SAME: () #[[ATTR3]] personality ptr @__gxx_personality_v0 { +; FNATTRS-NEXT: invoke void @do_throw() +; FNATTRS-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[CPAD:%.*]] +; FNATTRS: cpad: +; FNATTRS-NEXT: [[CP:%.*]] = cleanuppad within none [] +; FNATTRS-NEXT: call void @abort() +; FNATTRS-NEXT: unreachable +; FNATTRS: unreachable: +; FNATTRS-NEXT: unreachable +; +; ATTRIBUTOR: Function Attrs: noreturn nounwind +; ATTRIBUTOR-LABEL: define {{[^@]+}}@cleanuppad +; ATTRIBUTOR-SAME: () #[[ATTR4]] personality ptr @__gxx_personality_v0 { +; ATTRIBUTOR-NEXT: invoke void @do_throw() +; ATTRIBUTOR-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[CPAD:%.*]] +; ATTRIBUTOR: cpad: +; ATTRIBUTOR-NEXT: [[CP:%.*]] = cleanuppad within none [] +; ATTRIBUTOR-NEXT: call void @abort() +; ATTRIBUTOR-NEXT: unreachable +; ATTRIBUTOR: unreachable: +; ATTRIBUTOR-NEXT: unreachable ; invoke void @do_throw() to label %unreachable unwind label %cpad @@ -290,23 +315,41 @@ } define void @catchswitch_cleanuppad() personality ptr @__gxx_personality_v0 { -; CHECK: Function Attrs: noreturn -; CHECK-LABEL: define {{[^@]+}}@catchswitch_cleanuppad -; CHECK-SAME: () #[[ATTR3]] personality ptr @__gxx_personality_v0 { -; CHECK-NEXT: invoke void @do_throw() -; CHECK-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[CS:%.*]] -; CHECK: cs: -; CHECK-NEXT: [[TOK:%.*]] = catchswitch within none [label %catch] unwind label [[CPAD:%.*]] -; CHECK: catch: -; CHECK-NEXT: [[C:%.*]] = catchpad within [[TOK]] [ptr @catch_ty, i32 0, ptr null] -; CHECK-NEXT: call void @abort() -; CHECK-NEXT: unreachable -; CHECK: cpad: -; CHECK-NEXT: [[CP:%.*]] = cleanuppad within none [] -; CHECK-NEXT: call void @abort() -; CHECK-NEXT: unreachable -; CHECK: unreachable: -; CHECK-NEXT: unreachable +; FNATTRS: Function Attrs: noreturn +; FNATTRS-LABEL: define {{[^@]+}}@catchswitch_cleanuppad +; FNATTRS-SAME: () #[[ATTR3]] personality ptr @__gxx_personality_v0 { +; FNATTRS-NEXT: invoke void @do_throw() +; FNATTRS-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[CS:%.*]] +; FNATTRS: cs: +; FNATTRS-NEXT: [[TOK:%.*]] = catchswitch within none [label %catch] unwind label [[CPAD:%.*]] +; FNATTRS: catch: +; FNATTRS-NEXT: [[C:%.*]] = catchpad within [[TOK]] [ptr @catch_ty, i32 0, ptr null] +; FNATTRS-NEXT: call void @abort() +; FNATTRS-NEXT: unreachable +; FNATTRS: cpad: +; FNATTRS-NEXT: [[CP:%.*]] = cleanuppad within none [] +; FNATTRS-NEXT: call void @abort() +; FNATTRS-NEXT: unreachable +; FNATTRS: unreachable: +; FNATTRS-NEXT: unreachable +; +; ATTRIBUTOR: Function Attrs: noreturn nounwind +; ATTRIBUTOR-LABEL: define {{[^@]+}}@catchswitch_cleanuppad +; ATTRIBUTOR-SAME: () #[[ATTR4]] personality ptr @__gxx_personality_v0 { +; ATTRIBUTOR-NEXT: invoke void @do_throw() +; ATTRIBUTOR-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[CS:%.*]] +; ATTRIBUTOR: cs: +; ATTRIBUTOR-NEXT: [[TOK:%.*]] = catchswitch within none [label %catch] unwind label [[CPAD:%.*]] +; ATTRIBUTOR: catch: +; ATTRIBUTOR-NEXT: [[C:%.*]] = catchpad within [[TOK]] [ptr @catch_ty, i32 0, ptr null] +; ATTRIBUTOR-NEXT: call void @abort() +; ATTRIBUTOR-NEXT: unreachable +; ATTRIBUTOR: cpad: +; ATTRIBUTOR-NEXT: [[CP:%.*]] = cleanuppad within none [] +; ATTRIBUTOR-NEXT: call void @abort() +; ATTRIBUTOR-NEXT: unreachable +; ATTRIBUTOR: unreachable: +; ATTRIBUTOR-NEXT: unreachable ; invoke void @do_throw() to label %unreachable unwind label %cs diff --git a/llvm/test/Transforms/FunctionAttrs/readattrs.ll b/llvm/test/Transforms/FunctionAttrs/readattrs.ll --- a/llvm/test/Transforms/FunctionAttrs/readattrs.ll +++ b/llvm/test/Transforms/FunctionAttrs/readattrs.ll @@ -1,39 +1,78 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes -; RUN: opt < %s -passes=function-attrs -S | FileCheck %s +; RUN: opt < %s -passes=function-attrs -S | FileCheck --check-prefixes=COMMON,FNATTRS %s +; RUN: opt < %s -passes=attributor-light -S | FileCheck --check-prefixes=COMMON,ATTRIBUTOR %s +; RUN: opt < %s -passes=attributor-light-cgscc -S | FileCheck --check-prefixes=COMMON,ATTRIBUTOR-CGSCC %s @x = global i32 0 declare void @test1_1(ptr %x1_1, ptr nocapture readonly %y1_1, ...) define void @test1_2(ptr %x1_2, ptr %y1_2, ptr %z1_2) { -; CHECK-LABEL: define {{[^@]+}}@test1_2 -; CHECK-SAME: (ptr [[X1_2:%.*]], ptr nocapture readonly [[Y1_2:%.*]], ptr [[Z1_2:%.*]]) { -; CHECK-NEXT: call void (ptr, ptr, ...) @test1_1(ptr [[X1_2]], ptr [[Y1_2]], ptr [[Z1_2]]) -; CHECK-NEXT: store i32 0, ptr @x, align 4 -; CHECK-NEXT: ret void +; FNATTRS-LABEL: define {{[^@]+}}@test1_2 +; FNATTRS-SAME: (ptr [[X1_2:%.*]], ptr nocapture readonly [[Y1_2:%.*]], ptr [[Z1_2:%.*]]) { +; FNATTRS-NEXT: call void (ptr, ptr, ...) @test1_1(ptr [[X1_2]], ptr [[Y1_2]], ptr [[Z1_2]]) +; FNATTRS-NEXT: store i32 0, ptr @x, align 4 +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR-LABEL: define {{[^@]+}}@test1_2 +; ATTRIBUTOR-SAME: (ptr [[X1_2:%.*]], ptr nocapture nofree readonly [[Y1_2:%.*]], ptr [[Z1_2:%.*]]) { +; ATTRIBUTOR-NEXT: call void (ptr, ptr, ...) @test1_1(ptr [[X1_2]], ptr nocapture nofree readonly [[Y1_2]], ptr [[Z1_2]]) +; ATTRIBUTOR-NEXT: store i32 0, ptr @x, align 4 +; ATTRIBUTOR-NEXT: ret void +; +; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@test1_2 +; ATTRIBUTOR-CGSCC-SAME: (ptr [[X1_2:%.*]], ptr nocapture nofree readonly [[Y1_2:%.*]], ptr [[Z1_2:%.*]]) { +; ATTRIBUTOR-CGSCC-NEXT: call void (ptr, ptr, ...) @test1_1(ptr [[X1_2]], ptr nocapture nofree readonly [[Y1_2]], ptr [[Z1_2]]) +; ATTRIBUTOR-CGSCC-NEXT: store i32 0, ptr @x, align 4 +; ATTRIBUTOR-CGSCC-NEXT: ret void ; call void (ptr, ptr, ...) @test1_1(ptr %x1_2, ptr %y1_2, ptr %z1_2) store i32 0, ptr @x ret void } +; TODO: Missing with attributor-light: argmem: none, inaccessiblemem: none define ptr @test2(ptr %p) { -; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none) -; CHECK-LABEL: define {{[^@]+}}@test2 -; CHECK-SAME: (ptr readnone returned [[P:%.*]]) #[[ATTR0:[0-9]+]] { -; CHECK-NEXT: store i32 0, ptr @x, align 4 -; CHECK-NEXT: ret ptr [[P]] +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none) +; FNATTRS-LABEL: define {{[^@]+}}@test2 +; FNATTRS-SAME: (ptr readnone returned [[P:%.*]]) #[[ATTR0:[0-9]+]] { +; FNATTRS-NEXT: store i32 0, ptr @x, align 4 +; FNATTRS-NEXT: ret ptr [[P]] +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write) +; ATTRIBUTOR-LABEL: define {{[^@]+}}@test2 +; ATTRIBUTOR-SAME: (ptr nofree readnone [[P:%.*]]) #[[ATTR0:[0-9]+]] { +; ATTRIBUTOR-NEXT: store i32 0, ptr @x, align 4 +; ATTRIBUTOR-NEXT: ret ptr [[P]] +; +; ATTRIBUTOR-CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write) +; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@test2 +; ATTRIBUTOR-CGSCC-SAME: (ptr nofree readnone [[P:%.*]]) #[[ATTR0:[0-9]+]] { +; ATTRIBUTOR-CGSCC-NEXT: store i32 0, ptr @x, align 4 +; ATTRIBUTOR-CGSCC-NEXT: ret ptr [[P]] ; store i32 0, ptr @x ret ptr %p } define i1 @test3(ptr %p, ptr %q) { -; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) -; CHECK-LABEL: define {{[^@]+}}@test3 -; CHECK-SAME: (ptr readnone [[P:%.*]], ptr readnone [[Q:%.*]]) #[[ATTR1:[0-9]+]] { -; CHECK-NEXT: [[A:%.*]] = icmp ult ptr [[P]], [[Q]] -; CHECK-NEXT: ret i1 [[A]] +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) +; FNATTRS-LABEL: define {{[^@]+}}@test3 +; FNATTRS-SAME: (ptr readnone [[P:%.*]], ptr readnone [[Q:%.*]]) #[[ATTR1:[0-9]+]] { +; FNATTRS-NEXT: [[A:%.*]] = icmp ult ptr [[P]], [[Q]] +; FNATTRS-NEXT: ret i1 [[A]] +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) +; ATTRIBUTOR-LABEL: define {{[^@]+}}@test3 +; ATTRIBUTOR-SAME: (ptr nofree readnone [[P:%.*]], ptr nofree readnone [[Q:%.*]]) #[[ATTR1:[0-9]+]] { +; ATTRIBUTOR-NEXT: [[A:%.*]] = icmp ult ptr [[P]], [[Q]] +; ATTRIBUTOR-NEXT: ret i1 [[A]] +; +; ATTRIBUTOR-CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) +; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@test3 +; ATTRIBUTOR-CGSCC-SAME: (ptr nofree readnone [[P:%.*]], ptr nofree readnone [[Q:%.*]]) #[[ATTR1:[0-9]+]] { +; ATTRIBUTOR-CGSCC-NEXT: [[A:%.*]] = icmp ult ptr [[P]], [[Q]] +; ATTRIBUTOR-CGSCC-NEXT: ret i1 [[A]] ; %A = icmp ult ptr %p, %q ret i1 %A @@ -42,11 +81,23 @@ declare void @test4_1(ptr nocapture) readonly define void @test4_2(ptr %p) { -; CHECK: Function Attrs: nofree memory(read) -; CHECK-LABEL: define {{[^@]+}}@test4_2 -; CHECK-SAME: (ptr nocapture readonly [[P:%.*]]) #[[ATTR3:[0-9]+]] { -; CHECK-NEXT: call void @test4_1(ptr [[P]]) -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: nofree memory(read) +; FNATTRS-LABEL: define {{[^@]+}}@test4_2 +; FNATTRS-SAME: (ptr nocapture readonly [[P:%.*]]) #[[ATTR3:[0-9]+]] { +; FNATTRS-NEXT: call void @test4_1(ptr [[P]]) +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: nosync memory(read) +; ATTRIBUTOR-LABEL: define {{[^@]+}}@test4_2 +; ATTRIBUTOR-SAME: (ptr nocapture readonly [[P:%.*]]) #[[ATTR3:[0-9]+]] { +; ATTRIBUTOR-NEXT: call void @test4_1(ptr nocapture readonly [[P]]) #[[ATTR3]] +; ATTRIBUTOR-NEXT: ret void +; +; ATTRIBUTOR-CGSCC: Function Attrs: nosync memory(read) +; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@test4_2 +; ATTRIBUTOR-CGSCC-SAME: (ptr nocapture readonly [[P:%.*]]) #[[ATTR3:[0-9]+]] { +; ATTRIBUTOR-CGSCC-NEXT: call void @test4_1(ptr nocapture readonly [[P]]) #[[ATTR3]] +; ATTRIBUTOR-CGSCC-NEXT: ret void ; call void @test4_1(ptr %p) ret void @@ -54,11 +105,23 @@ ; Missed optz'n: we could make %q readnone, but don't break test6! define void @test5(ptr %p, ptr %q) { -; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) -; CHECK-LABEL: define {{[^@]+}}@test5 -; CHECK-SAME: (ptr nocapture writeonly [[P:%.*]], ptr [[Q:%.*]]) #[[ATTR4:[0-9]+]] { -; CHECK-NEXT: store ptr [[Q]], ptr [[P]], align 8 -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) +; FNATTRS-LABEL: define {{[^@]+}}@test5 +; FNATTRS-SAME: (ptr nocapture writeonly [[P:%.*]], ptr [[Q:%.*]]) #[[ATTR4:[0-9]+]] { +; FNATTRS-NEXT: store ptr [[Q]], ptr [[P]], align 8 +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) +; ATTRIBUTOR-LABEL: define {{[^@]+}}@test5 +; ATTRIBUTOR-SAME: (ptr nocapture nofree nonnull writeonly [[P:%.*]], ptr nofree writeonly [[Q:%.*]]) #[[ATTR4:[0-9]+]] { +; ATTRIBUTOR-NEXT: store ptr [[Q]], ptr [[P]], align 8 +; ATTRIBUTOR-NEXT: ret void +; +; ATTRIBUTOR-CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) +; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@test5 +; ATTRIBUTOR-CGSCC-SAME: (ptr nocapture nofree nonnull writeonly [[P:%.*]], ptr nofree writeonly [[Q:%.*]]) #[[ATTR4:[0-9]+]] { +; ATTRIBUTOR-CGSCC-NEXT: store ptr [[Q]], ptr [[P]], align 8 +; ATTRIBUTOR-CGSCC-NEXT: ret void ; store ptr %q, ptr %p ret void @@ -68,11 +131,23 @@ ; This is not a missed optz'n. define void @test6_2(ptr %p, ptr %q) { -; CHECK-LABEL: define {{[^@]+}}@test6_2 -; CHECK-SAME: (ptr nocapture writeonly [[P:%.*]], ptr [[Q:%.*]]) { -; CHECK-NEXT: store ptr [[Q]], ptr [[P]], align 8 -; CHECK-NEXT: call void @test6_1() -; CHECK-NEXT: ret void +; FNATTRS-LABEL: define {{[^@]+}}@test6_2 +; FNATTRS-SAME: (ptr nocapture writeonly [[P:%.*]], ptr [[Q:%.*]]) { +; FNATTRS-NEXT: store ptr [[Q]], ptr [[P]], align 8 +; FNATTRS-NEXT: call void @test6_1() +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR-LABEL: define {{[^@]+}}@test6_2 +; ATTRIBUTOR-SAME: (ptr nocapture nofree nonnull writeonly [[P:%.*]], ptr nofree [[Q:%.*]]) { +; ATTRIBUTOR-NEXT: store ptr [[Q]], ptr [[P]], align 8 +; ATTRIBUTOR-NEXT: call void @test6_1() +; ATTRIBUTOR-NEXT: ret void +; +; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@test6_2 +; ATTRIBUTOR-CGSCC-SAME: (ptr nocapture nofree nonnull writeonly [[P:%.*]], ptr nofree [[Q:%.*]]) { +; ATTRIBUTOR-CGSCC-NEXT: store ptr [[Q]], ptr [[P]], align 8 +; ATTRIBUTOR-CGSCC-NEXT: call void @test6_1() +; ATTRIBUTOR-CGSCC-NEXT: ret void ; store ptr %q, ptr %p call void @test6_1() @@ -81,43 +156,91 @@ ; inalloca parameters are always considered written define void @test7_1(ptr inalloca(i32) %a) { -; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite) -; CHECK-LABEL: define {{[^@]+}}@test7_1 -; CHECK-SAME: (ptr nocapture inalloca(i32) [[A:%.*]]) #[[ATTR5:[0-9]+]] { -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite) +; FNATTRS-LABEL: define {{[^@]+}}@test7_1 +; FNATTRS-SAME: (ptr nocapture inalloca(i32) [[A:%.*]]) #[[ATTR5:[0-9]+]] { +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) +; ATTRIBUTOR-LABEL: define {{[^@]+}}@test7_1 +; ATTRIBUTOR-SAME: (ptr nocapture nofree nonnull writeonly inalloca(i32) [[A:%.*]]) #[[ATTR1]] { +; ATTRIBUTOR-NEXT: ret void +; +; ATTRIBUTOR-CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) +; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@test7_1 +; ATTRIBUTOR-CGSCC-SAME: (ptr nocapture nofree nonnull writeonly inalloca(i32) [[A:%.*]]) #[[ATTR1]] { +; ATTRIBUTOR-CGSCC-NEXT: ret void ; ret void } ; preallocated parameters are always considered written define void @test7_2(ptr preallocated(i32) %a) { -; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite) -; CHECK-LABEL: define {{[^@]+}}@test7_2 -; CHECK-SAME: (ptr nocapture preallocated(i32) [[A:%.*]]) #[[ATTR5]] { -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite) +; FNATTRS-LABEL: define {{[^@]+}}@test7_2 +; FNATTRS-SAME: (ptr nocapture preallocated(i32) [[A:%.*]]) #[[ATTR5]] { +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) +; ATTRIBUTOR-LABEL: define {{[^@]+}}@test7_2 +; ATTRIBUTOR-SAME: (ptr nocapture nofree nonnull writeonly preallocated(i32) [[A:%.*]]) #[[ATTR1]] { +; ATTRIBUTOR-NEXT: ret void +; +; ATTRIBUTOR-CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) +; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@test7_2 +; ATTRIBUTOR-CGSCC-SAME: (ptr nocapture nofree nonnull writeonly preallocated(i32) [[A:%.*]]) #[[ATTR1]] { +; ATTRIBUTOR-CGSCC-NEXT: ret void ; ret void } define ptr @test8_1(ptr %p) { -; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) -; CHECK-LABEL: define {{[^@]+}}@test8_1 -; CHECK-SAME: (ptr readnone returned [[P:%.*]]) #[[ATTR1]] { -; CHECK-NEXT: entry: -; CHECK-NEXT: ret ptr [[P]] +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) +; FNATTRS-LABEL: define {{[^@]+}}@test8_1 +; FNATTRS-SAME: (ptr readnone returned [[P:%.*]]) #[[ATTR1]] { +; FNATTRS-NEXT: entry: +; FNATTRS-NEXT: ret ptr [[P]] +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) +; ATTRIBUTOR-LABEL: define {{[^@]+}}@test8_1 +; ATTRIBUTOR-SAME: (ptr nofree readnone [[P:%.*]]) #[[ATTR1]] { +; ATTRIBUTOR-NEXT: entry: +; ATTRIBUTOR-NEXT: ret ptr [[P]] +; +; ATTRIBUTOR-CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) +; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@test8_1 +; ATTRIBUTOR-CGSCC-SAME: (ptr nofree readnone [[P:%.*]]) #[[ATTR1]] { +; ATTRIBUTOR-CGSCC-NEXT: entry: +; ATTRIBUTOR-CGSCC-NEXT: ret ptr [[P]] ; entry: ret ptr %p } define void @test8_2(ptr %p) { -; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) -; CHECK-LABEL: define {{[^@]+}}@test8_2 -; CHECK-SAME: (ptr writeonly [[P:%.*]]) #[[ATTR4]] { -; CHECK-NEXT: entry: -; CHECK-NEXT: [[CALL:%.*]] = call ptr @test8_1(ptr [[P]]) -; CHECK-NEXT: store i32 10, ptr [[CALL]], align 4 -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) +; FNATTRS-LABEL: define {{[^@]+}}@test8_2 +; FNATTRS-SAME: (ptr writeonly [[P:%.*]]) #[[ATTR4]] { +; FNATTRS-NEXT: entry: +; FNATTRS-NEXT: [[CALL:%.*]] = call ptr @test8_1(ptr [[P]]) +; FNATTRS-NEXT: store i32 10, ptr [[CALL]], align 4 +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write) +; ATTRIBUTOR-LABEL: define {{[^@]+}}@test8_2 +; ATTRIBUTOR-SAME: (ptr nocapture nofree writeonly [[P:%.*]]) #[[ATTR0]] { +; ATTRIBUTOR-NEXT: entry: +; ATTRIBUTOR-NEXT: [[CALL:%.*]] = call ptr @test8_1(ptr nofree readnone [[P]]) #[[ATTR13:[0-9]+]] +; ATTRIBUTOR-NEXT: store i32 10, ptr [[CALL]], align 4 +; ATTRIBUTOR-NEXT: ret void +; +; ATTRIBUTOR-CGSCC: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(write) +; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@test8_2 +; ATTRIBUTOR-CGSCC-SAME: (ptr nofree writeonly [[P:%.*]]) #[[ATTR5:[0-9]+]] { +; ATTRIBUTOR-CGSCC-NEXT: entry: +; ATTRIBUTOR-CGSCC-NEXT: [[CALL:%.*]] = call ptr @test8_1(ptr nofree readnone [[P]]) #[[ATTR13:[0-9]+]] +; ATTRIBUTOR-CGSCC-NEXT: store i32 10, ptr [[CALL]], align 4 +; ATTRIBUTOR-CGSCC-NEXT: ret void ; entry: %call = call ptr @test8_1(ptr %p) @@ -128,11 +251,23 @@ declare void @llvm.masked.scatter.v4i32.v4p0(<4 x i32>%val, <4 x ptr>, i32, <4 x i1>) define void @test9(<4 x ptr> %ptrs, <4 x i32>%val) { -; CHECK: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(write) -; CHECK-LABEL: define {{[^@]+}}@test9 -; CHECK-SAME: (<4 x ptr> [[PTRS:%.*]], <4 x i32> [[VAL:%.*]]) #[[ATTR7:[0-9]+]] { -; CHECK-NEXT: call void @llvm.masked.scatter.v4i32.v4p0(<4 x i32> [[VAL]], <4 x ptr> [[PTRS]], i32 4, <4 x i1> ) -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(write) +; FNATTRS-LABEL: define {{[^@]+}}@test9 +; FNATTRS-SAME: (<4 x ptr> [[PTRS:%.*]], <4 x i32> [[VAL:%.*]]) #[[ATTR7:[0-9]+]] { +; FNATTRS-NEXT: call void @llvm.masked.scatter.v4i32.v4p0(<4 x i32> [[VAL]], <4 x ptr> [[PTRS]], i32 4, <4 x i1> ) +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write) +; ATTRIBUTOR-LABEL: define {{[^@]+}}@test9 +; ATTRIBUTOR-SAME: (<4 x ptr> [[PTRS:%.*]], <4 x i32> [[VAL:%.*]]) #[[ATTR0]] { +; ATTRIBUTOR-NEXT: call void @llvm.masked.scatter.v4i32.v4p0(<4 x i32> [[VAL]], <4 x ptr> [[PTRS]], i32 4, <4 x i1> ) #[[ATTR14:[0-9]+]] +; ATTRIBUTOR-NEXT: ret void +; +; ATTRIBUTOR-CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write) +; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@test9 +; ATTRIBUTOR-CGSCC-SAME: (<4 x ptr> [[PTRS:%.*]], <4 x i32> [[VAL:%.*]]) #[[ATTR0]] { +; ATTRIBUTOR-CGSCC-NEXT: call void @llvm.masked.scatter.v4i32.v4p0(<4 x i32> [[VAL]], <4 x ptr> [[PTRS]], i32 4, <4 x i1> ) #[[ATTR14:[0-9]+]] +; ATTRIBUTOR-CGSCC-NEXT: ret void ; call void @llvm.masked.scatter.v4i32.v4p0(<4 x i32>%val, <4 x ptr> %ptrs, i32 4, <4 x i1>) ret void @@ -140,11 +275,23 @@ declare <4 x i32> @llvm.masked.gather.v4i32.v4p0(<4 x ptr>, i32, <4 x i1>, <4 x i32>) define <4 x i32> @test10(<4 x ptr> %ptrs) { -; CHECK: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(read) -; CHECK-LABEL: define {{[^@]+}}@test10 -; CHECK-SAME: (<4 x ptr> [[PTRS:%.*]]) #[[ATTR9:[0-9]+]] { -; CHECK-NEXT: [[RES:%.*]] = call <4 x i32> @llvm.masked.gather.v4i32.v4p0(<4 x ptr> [[PTRS]], i32 4, <4 x i1> , <4 x i32> undef) -; CHECK-NEXT: ret <4 x i32> [[RES]] +; FNATTRS: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(read) +; FNATTRS-LABEL: define {{[^@]+}}@test10 +; FNATTRS-SAME: (<4 x ptr> [[PTRS:%.*]]) #[[ATTR9:[0-9]+]] { +; FNATTRS-NEXT: [[RES:%.*]] = call <4 x i32> @llvm.masked.gather.v4i32.v4p0(<4 x ptr> [[PTRS]], i32 4, <4 x i1> , <4 x i32> undef) +; FNATTRS-NEXT: ret <4 x i32> [[RES]] +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read) +; ATTRIBUTOR-LABEL: define {{[^@]+}}@test10 +; ATTRIBUTOR-SAME: (<4 x ptr> [[PTRS:%.*]]) #[[ATTR7:[0-9]+]] { +; ATTRIBUTOR-NEXT: [[RES:%.*]] = call <4 x i32> @llvm.masked.gather.v4i32.v4p0(<4 x ptr> [[PTRS]], i32 4, <4 x i1> , <4 x i32> undef) #[[ATTR15:[0-9]+]] +; ATTRIBUTOR-NEXT: ret <4 x i32> [[RES]] +; +; ATTRIBUTOR-CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read) +; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@test10 +; ATTRIBUTOR-CGSCC-SAME: (<4 x ptr> [[PTRS:%.*]]) #[[ATTR8:[0-9]+]] { +; ATTRIBUTOR-CGSCC-NEXT: [[RES:%.*]] = call <4 x i32> @llvm.masked.gather.v4i32.v4p0(<4 x ptr> [[PTRS]], i32 4, <4 x i1> , <4 x i32> undef) #[[ATTR15:[0-9]+]] +; ATTRIBUTOR-CGSCC-NEXT: ret <4 x i32> [[RES]] ; %res = call <4 x i32> @llvm.masked.gather.v4i32.v4p0(<4 x ptr> %ptrs, i32 4, <4 x i1>, <4 x i32>undef) ret <4 x i32> %res @@ -152,11 +299,23 @@ declare <4 x i32> @test11_1(<4 x ptr>) argmemonly nounwind readonly define <4 x i32> @test11_2(<4 x ptr> %ptrs) { -; CHECK: Function Attrs: nofree nounwind memory(argmem: read) -; CHECK-LABEL: define {{[^@]+}}@test11_2 -; CHECK-SAME: (<4 x ptr> [[PTRS:%.*]]) #[[ATTR11:[0-9]+]] { -; CHECK-NEXT: [[RES:%.*]] = call <4 x i32> @test11_1(<4 x ptr> [[PTRS]]) -; CHECK-NEXT: ret <4 x i32> [[RES]] +; FNATTRS: Function Attrs: nofree nounwind memory(argmem: read) +; FNATTRS-LABEL: define {{[^@]+}}@test11_2 +; FNATTRS-SAME: (<4 x ptr> [[PTRS:%.*]]) #[[ATTR11:[0-9]+]] { +; FNATTRS-NEXT: [[RES:%.*]] = call <4 x i32> @test11_1(<4 x ptr> [[PTRS]]) +; FNATTRS-NEXT: ret <4 x i32> [[RES]] +; +; ATTRIBUTOR: Function Attrs: nosync nounwind memory(argmem: read) +; ATTRIBUTOR-LABEL: define {{[^@]+}}@test11_2 +; ATTRIBUTOR-SAME: (<4 x ptr> [[PTRS:%.*]]) #[[ATTR9:[0-9]+]] { +; ATTRIBUTOR-NEXT: [[RES:%.*]] = call <4 x i32> @test11_1(<4 x ptr> [[PTRS]]) #[[ATTR3]] +; ATTRIBUTOR-NEXT: ret <4 x i32> [[RES]] +; +; ATTRIBUTOR-CGSCC: Function Attrs: nosync nounwind memory(argmem: read) +; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@test11_2 +; ATTRIBUTOR-CGSCC-SAME: (<4 x ptr> [[PTRS:%.*]]) #[[ATTR10:[0-9]+]] { +; ATTRIBUTOR-CGSCC-NEXT: [[RES:%.*]] = call <4 x i32> @test11_1(<4 x ptr> [[PTRS]]) #[[ATTR3]] +; ATTRIBUTOR-CGSCC-NEXT: ret <4 x i32> [[RES]] ; %res = call <4 x i32> @test11_1(<4 x ptr> %ptrs) ret <4 x i32> %res @@ -164,22 +323,46 @@ declare <4 x i32> @test12_1(<4 x ptr>) argmemonly nounwind define <4 x i32> @test12_2(<4 x ptr> %ptrs) { -; CHECK: Function Attrs: nounwind memory(argmem: readwrite) -; CHECK-LABEL: define {{[^@]+}}@test12_2 -; CHECK-SAME: (<4 x ptr> [[PTRS:%.*]]) #[[ATTR12:[0-9]+]] { -; CHECK-NEXT: [[RES:%.*]] = call <4 x i32> @test12_1(<4 x ptr> [[PTRS]]) -; CHECK-NEXT: ret <4 x i32> [[RES]] +; FNATTRS: Function Attrs: nounwind memory(argmem: readwrite) +; FNATTRS-LABEL: define {{[^@]+}}@test12_2 +; FNATTRS-SAME: (<4 x ptr> [[PTRS:%.*]]) #[[ATTR12:[0-9]+]] { +; FNATTRS-NEXT: [[RES:%.*]] = call <4 x i32> @test12_1(<4 x ptr> [[PTRS]]) +; FNATTRS-NEXT: ret <4 x i32> [[RES]] +; +; ATTRIBUTOR: Function Attrs: nounwind memory(argmem: readwrite) +; ATTRIBUTOR-LABEL: define {{[^@]+}}@test12_2 +; ATTRIBUTOR-SAME: (<4 x ptr> [[PTRS:%.*]]) #[[ATTR10:[0-9]+]] { +; ATTRIBUTOR-NEXT: [[RES:%.*]] = call <4 x i32> @test12_1(<4 x ptr> [[PTRS]]) +; ATTRIBUTOR-NEXT: ret <4 x i32> [[RES]] +; +; ATTRIBUTOR-CGSCC: Function Attrs: nounwind memory(argmem: readwrite) +; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@test12_2 +; ATTRIBUTOR-CGSCC-SAME: (<4 x ptr> [[PTRS:%.*]]) #[[ATTR11:[0-9]+]] { +; ATTRIBUTOR-CGSCC-NEXT: [[RES:%.*]] = call <4 x i32> @test12_1(<4 x ptr> [[PTRS]]) +; ATTRIBUTOR-CGSCC-NEXT: ret <4 x i32> [[RES]] ; %res = call <4 x i32> @test12_1(<4 x ptr> %ptrs) ret <4 x i32> %res } define i32 @volatile_load(ptr %p) { -; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite, inaccessiblemem: readwrite) -; CHECK-LABEL: define {{[^@]+}}@volatile_load -; CHECK-SAME: (ptr [[P:%.*]]) #[[ATTR13:[0-9]+]] { -; CHECK-NEXT: [[LOAD:%.*]] = load volatile i32, ptr [[P]], align 4 -; CHECK-NEXT: ret i32 [[LOAD]] +; FNATTRS: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite, inaccessiblemem: readwrite) +; FNATTRS-LABEL: define {{[^@]+}}@volatile_load +; FNATTRS-SAME: (ptr [[P:%.*]]) #[[ATTR13:[0-9]+]] { +; FNATTRS-NEXT: [[LOAD:%.*]] = load volatile i32, ptr [[P]], align 4 +; FNATTRS-NEXT: ret i32 [[LOAD]] +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite) +; ATTRIBUTOR-LABEL: define {{[^@]+}}@volatile_load +; ATTRIBUTOR-SAME: (ptr nofree [[P:%.*]]) #[[ATTR11:[0-9]+]] { +; ATTRIBUTOR-NEXT: [[LOAD:%.*]] = load volatile i32, ptr [[P]], align 4 +; ATTRIBUTOR-NEXT: ret i32 [[LOAD]] +; +; ATTRIBUTOR-CGSCC: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite) +; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@volatile_load +; ATTRIBUTOR-CGSCC-SAME: (ptr nofree [[P:%.*]]) #[[ATTR12:[0-9]+]] { +; ATTRIBUTOR-CGSCC-NEXT: [[LOAD:%.*]] = load volatile i32, ptr [[P]], align 4 +; ATTRIBUTOR-CGSCC-NEXT: ret i32 [[LOAD]] ; %load = load volatile i32, ptr %p ret i32 %load @@ -193,13 +376,29 @@ ; is marked as readnone/only. However, the functions can write the pointer into ; %addr, causing the store to write to %escaped_then_written. define void @unsound_readnone(ptr %ignored, ptr %escaped_then_written) { -; CHECK-LABEL: define {{[^@]+}}@unsound_readnone -; CHECK-SAME: (ptr nocapture readnone [[IGNORED:%.*]], ptr [[ESCAPED_THEN_WRITTEN:%.*]]) { -; CHECK-NEXT: [[ADDR:%.*]] = alloca ptr, align 8 -; CHECK-NEXT: call void @escape_readnone_ptr(ptr [[ADDR]], ptr [[ESCAPED_THEN_WRITTEN]]) -; CHECK-NEXT: [[ADDR_LD:%.*]] = load ptr, ptr [[ADDR]], align 8 -; CHECK-NEXT: store i8 0, ptr [[ADDR_LD]], align 1 -; CHECK-NEXT: ret void +; FNATTRS-LABEL: define {{[^@]+}}@unsound_readnone +; FNATTRS-SAME: (ptr nocapture readnone [[IGNORED:%.*]], ptr [[ESCAPED_THEN_WRITTEN:%.*]]) { +; FNATTRS-NEXT: [[ADDR:%.*]] = alloca ptr, align 8 +; FNATTRS-NEXT: call void @escape_readnone_ptr(ptr [[ADDR]], ptr [[ESCAPED_THEN_WRITTEN]]) +; FNATTRS-NEXT: [[ADDR_LD:%.*]] = load ptr, ptr [[ADDR]], align 8 +; FNATTRS-NEXT: store i8 0, ptr [[ADDR_LD]], align 1 +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR-LABEL: define {{[^@]+}}@unsound_readnone +; ATTRIBUTOR-SAME: (ptr nocapture nofree readnone [[IGNORED:%.*]], ptr nofree [[ESCAPED_THEN_WRITTEN:%.*]]) { +; ATTRIBUTOR-NEXT: [[ADDR:%.*]] = alloca ptr, align 8 +; ATTRIBUTOR-NEXT: call void @escape_readnone_ptr(ptr [[ADDR]], ptr nofree [[ESCAPED_THEN_WRITTEN]]) +; ATTRIBUTOR-NEXT: [[ADDR_LD:%.*]] = load ptr, ptr [[ADDR]], align 8 +; ATTRIBUTOR-NEXT: store i8 0, ptr [[ADDR_LD]], align 1 +; ATTRIBUTOR-NEXT: ret void +; +; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@unsound_readnone +; ATTRIBUTOR-CGSCC-SAME: (ptr nocapture nofree readnone [[IGNORED:%.*]], ptr nofree [[ESCAPED_THEN_WRITTEN:%.*]]) { +; ATTRIBUTOR-CGSCC-NEXT: [[ADDR:%.*]] = alloca ptr, align 8 +; ATTRIBUTOR-CGSCC-NEXT: call void @escape_readnone_ptr(ptr [[ADDR]], ptr nofree [[ESCAPED_THEN_WRITTEN]]) +; ATTRIBUTOR-CGSCC-NEXT: [[ADDR_LD:%.*]] = load ptr, ptr [[ADDR]], align 8 +; ATTRIBUTOR-CGSCC-NEXT: store i8 0, ptr [[ADDR_LD]], align 1 +; ATTRIBUTOR-CGSCC-NEXT: ret void ; %addr = alloca ptr call void @escape_readnone_ptr(ptr %addr, ptr %escaped_then_written) @@ -209,13 +408,29 @@ } define void @unsound_readonly(ptr %ignored, ptr %escaped_then_written) { -; CHECK-LABEL: define {{[^@]+}}@unsound_readonly -; CHECK-SAME: (ptr nocapture readnone [[IGNORED:%.*]], ptr [[ESCAPED_THEN_WRITTEN:%.*]]) { -; CHECK-NEXT: [[ADDR:%.*]] = alloca ptr, align 8 -; CHECK-NEXT: call void @escape_readonly_ptr(ptr [[ADDR]], ptr [[ESCAPED_THEN_WRITTEN]]) -; CHECK-NEXT: [[ADDR_LD:%.*]] = load ptr, ptr [[ADDR]], align 8 -; CHECK-NEXT: store i8 0, ptr [[ADDR_LD]], align 1 -; CHECK-NEXT: ret void +; FNATTRS-LABEL: define {{[^@]+}}@unsound_readonly +; FNATTRS-SAME: (ptr nocapture readnone [[IGNORED:%.*]], ptr [[ESCAPED_THEN_WRITTEN:%.*]]) { +; FNATTRS-NEXT: [[ADDR:%.*]] = alloca ptr, align 8 +; FNATTRS-NEXT: call void @escape_readonly_ptr(ptr [[ADDR]], ptr [[ESCAPED_THEN_WRITTEN]]) +; FNATTRS-NEXT: [[ADDR_LD:%.*]] = load ptr, ptr [[ADDR]], align 8 +; FNATTRS-NEXT: store i8 0, ptr [[ADDR_LD]], align 1 +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR-LABEL: define {{[^@]+}}@unsound_readonly +; ATTRIBUTOR-SAME: (ptr nocapture nofree readnone [[IGNORED:%.*]], ptr nofree [[ESCAPED_THEN_WRITTEN:%.*]]) { +; ATTRIBUTOR-NEXT: [[ADDR:%.*]] = alloca ptr, align 8 +; ATTRIBUTOR-NEXT: call void @escape_readonly_ptr(ptr [[ADDR]], ptr nofree [[ESCAPED_THEN_WRITTEN]]) +; ATTRIBUTOR-NEXT: [[ADDR_LD:%.*]] = load ptr, ptr [[ADDR]], align 8 +; ATTRIBUTOR-NEXT: store i8 0, ptr [[ADDR_LD]], align 1 +; ATTRIBUTOR-NEXT: ret void +; +; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@unsound_readonly +; ATTRIBUTOR-CGSCC-SAME: (ptr nocapture nofree readnone [[IGNORED:%.*]], ptr nofree [[ESCAPED_THEN_WRITTEN:%.*]]) { +; ATTRIBUTOR-CGSCC-NEXT: [[ADDR:%.*]] = alloca ptr, align 8 +; ATTRIBUTOR-CGSCC-NEXT: call void @escape_readonly_ptr(ptr [[ADDR]], ptr nofree [[ESCAPED_THEN_WRITTEN]]) +; ATTRIBUTOR-CGSCC-NEXT: [[ADDR_LD:%.*]] = load ptr, ptr [[ADDR]], align 8 +; ATTRIBUTOR-CGSCC-NEXT: store i8 0, ptr [[ADDR_LD]], align 1 +; ATTRIBUTOR-CGSCC-NEXT: ret void ; %addr = alloca ptr call void @escape_readonly_ptr(ptr %addr, ptr %escaped_then_written) @@ -225,10 +440,20 @@ } define void @fptr_test1a(ptr %p, ptr %f) { -; CHECK-LABEL: define {{[^@]+}}@fptr_test1a -; CHECK-SAME: (ptr nocapture readnone [[P:%.*]], ptr nocapture readonly [[F:%.*]]) { -; CHECK-NEXT: call void [[F]](ptr nocapture readnone [[P]]) -; CHECK-NEXT: ret void +; FNATTRS-LABEL: define {{[^@]+}}@fptr_test1a +; FNATTRS-SAME: (ptr nocapture readnone [[P:%.*]], ptr nocapture readonly [[F:%.*]]) { +; FNATTRS-NEXT: call void [[F]](ptr nocapture readnone [[P]]) +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR-LABEL: define {{[^@]+}}@fptr_test1a +; ATTRIBUTOR-SAME: (ptr nocapture nofree [[P:%.*]], ptr nocapture nofree nonnull [[F:%.*]]) { +; ATTRIBUTOR-NEXT: call void [[F]](ptr nocapture nofree readnone [[P]]) +; ATTRIBUTOR-NEXT: ret void +; +; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@fptr_test1a +; ATTRIBUTOR-CGSCC-SAME: (ptr nocapture nofree [[P:%.*]], ptr nocapture nofree nonnull [[F:%.*]]) { +; ATTRIBUTOR-CGSCC-NEXT: call void [[F]](ptr nocapture nofree readnone [[P]]) +; ATTRIBUTOR-CGSCC-NEXT: ret void ; call void %f(ptr nocapture readnone %p) ret void @@ -236,31 +461,63 @@ ; Can't infer readnone here because call might capture %p define void @fptr_test1b(ptr %p, ptr %f) { -; CHECK-LABEL: define {{[^@]+}}@fptr_test1b -; CHECK-SAME: (ptr [[P:%.*]], ptr nocapture readonly [[F:%.*]]) { -; CHECK-NEXT: call void [[F]](ptr readnone [[P]]) -; CHECK-NEXT: ret void +; FNATTRS-LABEL: define {{[^@]+}}@fptr_test1b +; FNATTRS-SAME: (ptr [[P:%.*]], ptr nocapture readonly [[F:%.*]]) { +; FNATTRS-NEXT: call void [[F]](ptr readnone [[P]]) +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR-LABEL: define {{[^@]+}}@fptr_test1b +; ATTRIBUTOR-SAME: (ptr nofree [[P:%.*]], ptr nocapture nofree nonnull [[F:%.*]]) { +; ATTRIBUTOR-NEXT: call void [[F]](ptr nofree readnone [[P]]) +; ATTRIBUTOR-NEXT: ret void +; +; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@fptr_test1b +; ATTRIBUTOR-CGSCC-SAME: (ptr nofree [[P:%.*]], ptr nocapture nofree nonnull [[F:%.*]]) { +; ATTRIBUTOR-CGSCC-NEXT: call void [[F]](ptr nofree readnone [[P]]) +; ATTRIBUTOR-CGSCC-NEXT: ret void ; call void %f(ptr readnone %p) ret void } define void @fptr_test1c(ptr %p, ptr %f) { -; CHECK: Function Attrs: nofree memory(read) -; CHECK-LABEL: define {{[^@]+}}@fptr_test1c -; CHECK-SAME: (ptr readnone [[P:%.*]], ptr nocapture readonly [[F:%.*]]) #[[ATTR3]] { -; CHECK-NEXT: call void [[F]](ptr readnone [[P]]) #[[ATTR2:[0-9]+]] -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: nofree memory(read) +; FNATTRS-LABEL: define {{[^@]+}}@fptr_test1c +; FNATTRS-SAME: (ptr readnone [[P:%.*]], ptr nocapture readonly [[F:%.*]]) #[[ATTR3]] { +; FNATTRS-NEXT: call void [[F]](ptr readnone [[P]]) #[[ATTR2:[0-9]+]] +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: memory(read) +; ATTRIBUTOR-LABEL: define {{[^@]+}}@fptr_test1c +; ATTRIBUTOR-SAME: (ptr nofree readonly [[P:%.*]], ptr nocapture nofree nonnull readonly [[F:%.*]]) #[[ATTR2:[0-9]+]] { +; ATTRIBUTOR-NEXT: call void [[F]](ptr nofree readnone [[P]]) #[[ATTR2]] +; ATTRIBUTOR-NEXT: ret void +; +; ATTRIBUTOR-CGSCC: Function Attrs: memory(read) +; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@fptr_test1c +; ATTRIBUTOR-CGSCC-SAME: (ptr nofree readonly [[P:%.*]], ptr nocapture nofree nonnull readonly [[F:%.*]]) #[[ATTR2:[0-9]+]] { +; ATTRIBUTOR-CGSCC-NEXT: call void [[F]](ptr nofree readnone [[P]]) #[[ATTR2]] +; ATTRIBUTOR-CGSCC-NEXT: ret void ; call void %f(ptr readnone %p) readonly ret void } define void @fptr_test2a(ptr %p, ptr %f) { -; CHECK-LABEL: define {{[^@]+}}@fptr_test2a -; CHECK-SAME: (ptr nocapture readonly [[P:%.*]], ptr nocapture readonly [[F:%.*]]) { -; CHECK-NEXT: call void [[F]](ptr nocapture readonly [[P]]) -; CHECK-NEXT: ret void +; FNATTRS-LABEL: define {{[^@]+}}@fptr_test2a +; FNATTRS-SAME: (ptr nocapture readonly [[P:%.*]], ptr nocapture readonly [[F:%.*]]) { +; FNATTRS-NEXT: call void [[F]](ptr nocapture readonly [[P]]) +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR-LABEL: define {{[^@]+}}@fptr_test2a +; ATTRIBUTOR-SAME: (ptr nocapture nofree [[P:%.*]], ptr nocapture nofree nonnull [[F:%.*]]) { +; ATTRIBUTOR-NEXT: call void [[F]](ptr nocapture nofree readonly [[P]]) +; ATTRIBUTOR-NEXT: ret void +; +; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@fptr_test2a +; ATTRIBUTOR-CGSCC-SAME: (ptr nocapture nofree [[P:%.*]], ptr nocapture nofree nonnull [[F:%.*]]) { +; ATTRIBUTOR-CGSCC-NEXT: call void [[F]](ptr nocapture nofree readonly [[P]]) +; ATTRIBUTOR-CGSCC-NEXT: ret void ; call void %f(ptr nocapture readonly %p) ret void @@ -268,43 +525,99 @@ define void @fptr_test2b(ptr %p, ptr %f) { ; Can't infer readonly here because call might capture %p -; CHECK-LABEL: define {{[^@]+}}@fptr_test2b -; CHECK-SAME: (ptr [[P:%.*]], ptr nocapture readonly [[F:%.*]]) { -; CHECK-NEXT: call void [[F]](ptr readonly [[P]]) -; CHECK-NEXT: ret void +; FNATTRS-LABEL: define {{[^@]+}}@fptr_test2b +; FNATTRS-SAME: (ptr [[P:%.*]], ptr nocapture readonly [[F:%.*]]) { +; FNATTRS-NEXT: call void [[F]](ptr readonly [[P]]) +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR-LABEL: define {{[^@]+}}@fptr_test2b +; ATTRIBUTOR-SAME: (ptr nofree [[P:%.*]], ptr nocapture nofree nonnull [[F:%.*]]) { +; ATTRIBUTOR-NEXT: call void [[F]](ptr nofree readonly [[P]]) +; ATTRIBUTOR-NEXT: ret void +; +; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@fptr_test2b +; ATTRIBUTOR-CGSCC-SAME: (ptr nofree [[P:%.*]], ptr nocapture nofree nonnull [[F:%.*]]) { +; ATTRIBUTOR-CGSCC-NEXT: call void [[F]](ptr nofree readonly [[P]]) +; ATTRIBUTOR-CGSCC-NEXT: ret void ; call void %f(ptr readonly %p) ret void } define void @fptr_test2c(ptr %p, ptr %f) { -; CHECK: Function Attrs: nofree memory(read) -; CHECK-LABEL: define {{[^@]+}}@fptr_test2c -; CHECK-SAME: (ptr readonly [[P:%.*]], ptr nocapture readonly [[F:%.*]]) #[[ATTR3]] { -; CHECK-NEXT: call void [[F]](ptr readonly [[P]]) #[[ATTR2]] -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: nofree memory(read) +; FNATTRS-LABEL: define {{[^@]+}}@fptr_test2c +; FNATTRS-SAME: (ptr readonly [[P:%.*]], ptr nocapture readonly [[F:%.*]]) #[[ATTR3]] { +; FNATTRS-NEXT: call void [[F]](ptr readonly [[P]]) #[[ATTR2]] +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: memory(read) +; ATTRIBUTOR-LABEL: define {{[^@]+}}@fptr_test2c +; ATTRIBUTOR-SAME: (ptr nofree readonly [[P:%.*]], ptr nocapture nofree nonnull readonly [[F:%.*]]) #[[ATTR2]] { +; ATTRIBUTOR-NEXT: call void [[F]](ptr nofree readonly [[P]]) #[[ATTR2]] +; ATTRIBUTOR-NEXT: ret void +; +; ATTRIBUTOR-CGSCC: Function Attrs: memory(read) +; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@fptr_test2c +; ATTRIBUTOR-CGSCC-SAME: (ptr nofree readonly [[P:%.*]], ptr nocapture nofree nonnull readonly [[F:%.*]]) #[[ATTR2]] { +; ATTRIBUTOR-CGSCC-NEXT: call void [[F]](ptr nofree readonly [[P]]) #[[ATTR2]] +; ATTRIBUTOR-CGSCC-NEXT: ret void ; call void %f(ptr readonly %p) readonly ret void } define void @alloca_recphi() { -; CHECK: Function Attrs: nofree norecurse nosync nounwind memory(none) -; CHECK-LABEL: define {{[^@]+}}@alloca_recphi -; CHECK-SAME: () #[[ATTR14:[0-9]+]] { -; CHECK-NEXT: entry: -; CHECK-NEXT: [[A:%.*]] = alloca [8 x i32], align 4 -; CHECK-NEXT: [[A_END:%.*]] = getelementptr i32, ptr [[A]], i64 8 -; CHECK-NEXT: br label [[LOOP:%.*]] -; CHECK: loop: -; CHECK-NEXT: [[P:%.*]] = phi ptr [ [[A]], [[ENTRY:%.*]] ], [ [[P_NEXT:%.*]], [[LOOP]] ] -; CHECK-NEXT: store i32 0, ptr [[P]], align 4 -; CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[P]], align 4 -; CHECK-NEXT: [[P_NEXT]] = getelementptr i32, ptr [[P]], i64 1 -; CHECK-NEXT: [[C:%.*]] = icmp ne ptr [[P_NEXT]], [[A_END]] -; CHECK-NEXT: br i1 [[C]], label [[LOOP]], label [[EXIT:%.*]] -; CHECK: exit: -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: nofree norecurse nosync nounwind memory(none) +; FNATTRS-LABEL: define {{[^@]+}}@alloca_recphi +; FNATTRS-SAME: () #[[ATTR14:[0-9]+]] { +; FNATTRS-NEXT: entry: +; FNATTRS-NEXT: [[A:%.*]] = alloca [8 x i32], align 4 +; FNATTRS-NEXT: [[A_END:%.*]] = getelementptr i32, ptr [[A]], i64 8 +; FNATTRS-NEXT: br label [[LOOP:%.*]] +; FNATTRS: loop: +; FNATTRS-NEXT: [[P:%.*]] = phi ptr [ [[A]], [[ENTRY:%.*]] ], [ [[P_NEXT:%.*]], [[LOOP]] ] +; FNATTRS-NEXT: store i32 0, ptr [[P]], align 4 +; FNATTRS-NEXT: [[TMP0:%.*]] = load i32, ptr [[P]], align 4 +; FNATTRS-NEXT: [[P_NEXT]] = getelementptr i32, ptr [[P]], i64 1 +; FNATTRS-NEXT: [[C:%.*]] = icmp ne ptr [[P_NEXT]], [[A_END]] +; FNATTRS-NEXT: br i1 [[C]], label [[LOOP]], label [[EXIT:%.*]] +; FNATTRS: exit: +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: nofree norecurse nosync nounwind memory(none) +; ATTRIBUTOR-LABEL: define {{[^@]+}}@alloca_recphi +; ATTRIBUTOR-SAME: () #[[ATTR12:[0-9]+]] { +; ATTRIBUTOR-NEXT: entry: +; ATTRIBUTOR-NEXT: [[A:%.*]] = alloca [8 x i32], align 4 +; ATTRIBUTOR-NEXT: [[A_END:%.*]] = getelementptr i32, ptr [[A]], i64 8 +; ATTRIBUTOR-NEXT: br label [[LOOP:%.*]] +; ATTRIBUTOR: loop: +; ATTRIBUTOR-NEXT: [[P:%.*]] = phi ptr [ [[A]], [[ENTRY:%.*]] ], [ [[P_NEXT:%.*]], [[LOOP]] ] +; ATTRIBUTOR-NEXT: store i32 0, ptr [[P]], align 4 +; ATTRIBUTOR-NEXT: [[TMP0:%.*]] = load i32, ptr [[P]], align 4 +; ATTRIBUTOR-NEXT: [[P_NEXT]] = getelementptr i32, ptr [[P]], i64 1 +; ATTRIBUTOR-NEXT: [[C:%.*]] = icmp ne ptr [[P_NEXT]], [[A_END]] +; ATTRIBUTOR-NEXT: br i1 [[C]], label [[LOOP]], label [[EXIT:%.*]] +; ATTRIBUTOR: exit: +; ATTRIBUTOR-NEXT: ret void +; +; ATTRIBUTOR-CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) +; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@alloca_recphi +; ATTRIBUTOR-CGSCC-SAME: () #[[ATTR1]] { +; ATTRIBUTOR-CGSCC-NEXT: entry: +; ATTRIBUTOR-CGSCC-NEXT: [[A:%.*]] = alloca [8 x i32], align 4 +; ATTRIBUTOR-CGSCC-NEXT: [[A_END:%.*]] = getelementptr i32, ptr [[A]], i64 8 +; ATTRIBUTOR-CGSCC-NEXT: br label [[LOOP:%.*]] +; ATTRIBUTOR-CGSCC: loop: +; ATTRIBUTOR-CGSCC-NEXT: [[P:%.*]] = phi ptr [ [[A]], [[ENTRY:%.*]] ], [ [[P_NEXT:%.*]], [[LOOP]] ] +; ATTRIBUTOR-CGSCC-NEXT: store i32 0, ptr [[P]], align 4 +; ATTRIBUTOR-CGSCC-NEXT: [[TMP0:%.*]] = load i32, ptr [[P]], align 4 +; ATTRIBUTOR-CGSCC-NEXT: [[P_NEXT]] = getelementptr i32, ptr [[P]], i64 1 +; ATTRIBUTOR-CGSCC-NEXT: [[C:%.*]] = icmp ne ptr [[P_NEXT]], [[A_END]] +; ATTRIBUTOR-CGSCC-NEXT: br i1 [[C]], label [[LOOP]], label [[EXIT:%.*]] +; ATTRIBUTOR-CGSCC: exit: +; ATTRIBUTOR-CGSCC-NEXT: ret void ; entry: %a = alloca [8 x i32] @@ -328,41 +641,83 @@ ; FIXME: While this can't be readnone, this could be readonly. define void @op_bundle_readnone_deopt(ptr %p) { -; CHECK-LABEL: define {{[^@]+}}@op_bundle_readnone_deopt -; CHECK-SAME: (ptr nocapture [[P:%.*]]) { -; CHECK-NEXT: call void @readnone_param(ptr [[P]]) [ "deopt"() ] -; CHECK-NEXT: ret void +; FNATTRS-LABEL: define {{[^@]+}}@op_bundle_readnone_deopt +; FNATTRS-SAME: (ptr nocapture [[P:%.*]]) { +; FNATTRS-NEXT: call void @readnone_param(ptr [[P]]) [ "deopt"() ] +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR-LABEL: define {{[^@]+}}@op_bundle_readnone_deopt +; ATTRIBUTOR-SAME: (ptr nocapture nofree [[P:%.*]]) { +; ATTRIBUTOR-NEXT: call void @readnone_param(ptr nocapture nofree [[P]]) [ "deopt"() ] +; ATTRIBUTOR-NEXT: ret void +; +; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@op_bundle_readnone_deopt +; ATTRIBUTOR-CGSCC-SAME: (ptr nocapture nofree [[P:%.*]]) { +; ATTRIBUTOR-CGSCC-NEXT: call void @readnone_param(ptr nocapture nofree [[P]]) [ "deopt"() ] +; ATTRIBUTOR-CGSCC-NEXT: ret void ; call void @readnone_param(ptr %p) ["deopt"()] ret void } define void @op_bundle_readnone_unknown(ptr %p) { -; CHECK-LABEL: define {{[^@]+}}@op_bundle_readnone_unknown -; CHECK-SAME: (ptr nocapture [[P:%.*]]) { -; CHECK-NEXT: call void @readnone_param(ptr [[P]]) [ "unknown"() ] -; CHECK-NEXT: ret void +; FNATTRS-LABEL: define {{[^@]+}}@op_bundle_readnone_unknown +; FNATTRS-SAME: (ptr nocapture [[P:%.*]]) { +; FNATTRS-NEXT: call void @readnone_param(ptr [[P]]) [ "unknown"() ] +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR-LABEL: define {{[^@]+}}@op_bundle_readnone_unknown +; ATTRIBUTOR-SAME: (ptr nocapture nofree [[P:%.*]]) { +; ATTRIBUTOR-NEXT: call void @readnone_param(ptr nocapture nofree [[P]]) [ "unknown"() ] +; ATTRIBUTOR-NEXT: ret void +; +; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@op_bundle_readnone_unknown +; ATTRIBUTOR-CGSCC-SAME: (ptr nocapture nofree [[P:%.*]]) { +; ATTRIBUTOR-CGSCC-NEXT: call void @readnone_param(ptr nocapture nofree [[P]]) [ "unknown"() ] +; ATTRIBUTOR-CGSCC-NEXT: ret void ; call void @readnone_param(ptr %p) ["unknown"()] ret void } define void @op_bundle_readonly_deopt(ptr %p) { -; CHECK-LABEL: define {{[^@]+}}@op_bundle_readonly_deopt -; CHECK-SAME: (ptr nocapture readonly [[P:%.*]]) { -; CHECK-NEXT: call void @readonly_param(ptr [[P]]) [ "deopt"() ] -; CHECK-NEXT: ret void +; FNATTRS-LABEL: define {{[^@]+}}@op_bundle_readonly_deopt +; FNATTRS-SAME: (ptr nocapture readonly [[P:%.*]]) { +; FNATTRS-NEXT: call void @readonly_param(ptr [[P]]) [ "deopt"() ] +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR-LABEL: define {{[^@]+}}@op_bundle_readonly_deopt +; ATTRIBUTOR-SAME: (ptr nocapture nofree [[P:%.*]]) { +; ATTRIBUTOR-NEXT: call void @readonly_param(ptr nocapture nofree [[P]]) [ "deopt"() ] +; ATTRIBUTOR-NEXT: ret void +; +; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@op_bundle_readonly_deopt +; ATTRIBUTOR-CGSCC-SAME: (ptr nocapture nofree [[P:%.*]]) { +; ATTRIBUTOR-CGSCC-NEXT: call void @readonly_param(ptr nocapture nofree [[P]]) [ "deopt"() ] +; ATTRIBUTOR-CGSCC-NEXT: ret void ; call void @readonly_param(ptr %p) ["deopt"()] ret void } define void @op_bundle_readonly_unknown(ptr %p) { -; CHECK-LABEL: define {{[^@]+}}@op_bundle_readonly_unknown -; CHECK-SAME: (ptr nocapture [[P:%.*]]) { -; CHECK-NEXT: call void @readonly_param(ptr [[P]]) [ "unknown"() ] -; CHECK-NEXT: ret void +; FNATTRS-LABEL: define {{[^@]+}}@op_bundle_readonly_unknown +; FNATTRS-SAME: (ptr nocapture [[P:%.*]]) { +; FNATTRS-NEXT: call void @readonly_param(ptr [[P]]) [ "unknown"() ] +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR-LABEL: define {{[^@]+}}@op_bundle_readonly_unknown +; ATTRIBUTOR-SAME: (ptr nocapture nofree [[P:%.*]]) { +; ATTRIBUTOR-NEXT: call void @readonly_param(ptr nocapture nofree [[P]]) [ "unknown"() ] +; ATTRIBUTOR-NEXT: ret void +; +; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@op_bundle_readonly_unknown +; ATTRIBUTOR-CGSCC-SAME: (ptr nocapture nofree [[P:%.*]]) { +; ATTRIBUTOR-CGSCC-NEXT: call void @readonly_param(ptr nocapture nofree [[P]]) [ "unknown"() ] +; ATTRIBUTOR-CGSCC-NEXT: ret void ; call void @readonly_param(ptr %p) ["unknown"()] ret void } +;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line: +; COMMON: {{.*}} diff --git a/llvm/test/Transforms/FunctionAttrs/readnone.ll b/llvm/test/Transforms/FunctionAttrs/readnone.ll --- a/llvm/test/Transforms/FunctionAttrs/readnone.ll +++ b/llvm/test/Transforms/FunctionAttrs/readnone.ll @@ -1,13 +1,35 @@ -; RUN: opt < %s -passes=function-attrs -S | FileCheck %s +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 3 +; RUN: opt -passes=function-attrs -S < %s | FileCheck --check-prefixes=COMMON,FNATTRS %s +; RUN: opt -passes=attributor-light -S < %s | FileCheck --check-prefixes=COMMON,ATTRIBUTOR %s -; CHECK: define void @bar(ptr nocapture readnone %0) define void @bar(ptr readonly %0) { +; FNATTRS-LABEL: define void @bar +; FNATTRS-SAME: (ptr nocapture readnone [[TMP0:%.*]]) #[[ATTR0:[0-9]+]] { +; FNATTRS-NEXT: call void @foo(ptr [[TMP0]]) +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR-LABEL: define void @bar +; ATTRIBUTOR-SAME: (ptr nocapture nofree readnone [[TMP0:%.*]]) #[[ATTR0:[0-9]+]] { +; ATTRIBUTOR-NEXT: call void @foo(ptr nocapture nofree readnone [[TMP0]]) #[[ATTR0]] +; ATTRIBUTOR-NEXT: ret void +; call void @foo(ptr %0) - ret void + ret void } -; CHECK: define void @foo(ptr nocapture readnone %0) define void @foo(ptr readonly %0) { +; FNATTRS-LABEL: define void @foo +; FNATTRS-SAME: (ptr nocapture readnone [[TMP0:%.*]]) #[[ATTR0]] { +; FNATTRS-NEXT: call void @bar(ptr [[TMP0]]) +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR-LABEL: define void @foo +; ATTRIBUTOR-SAME: (ptr nocapture nofree readnone [[TMP0:%.*]]) #[[ATTR0]] { +; ATTRIBUTOR-NEXT: call void @bar(ptr nocapture nofree readnone [[TMP0]]) #[[ATTR0]] +; ATTRIBUTOR-NEXT: ret void +; call void @bar(ptr %0) ret void } +;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line: +; COMMON: {{.*}} diff --git a/llvm/test/Transforms/FunctionAttrs/willreturn.ll b/llvm/test/Transforms/FunctionAttrs/willreturn.ll --- a/llvm/test/Transforms/FunctionAttrs/willreturn.ll +++ b/llvm/test/Transforms/FunctionAttrs/willreturn.ll @@ -1,13 +1,21 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes -; RUN: opt -passes=function-attrs -S %s | FileCheck %s +; RUN: opt -passes=function-attrs -S %s | FileCheck --check-prefixes=COMMON,FNATTRS %s +; RUN: opt -passes=attributor-light -S %s | FileCheck --check-prefixes=COMMON,ATTRIBUTOR %s define void @mustprogress_readnone() mustprogress { -; CHECK: Function Attrs: mustprogress nofree norecurse noreturn nosync nounwind willreturn memory(none) -; CHECK-LABEL: @mustprogress_readnone( -; CHECK-NEXT: entry: -; CHECK-NEXT: br label [[WHILE_BODY:%.*]] -; CHECK: while.body: -; CHECK-NEXT: br label [[WHILE_BODY]] +; FNATTRS: Function Attrs: mustprogress nofree norecurse noreturn nosync nounwind willreturn memory(none) +; FNATTRS-LABEL: @mustprogress_readnone( +; FNATTRS-NEXT: entry: +; FNATTRS-NEXT: br label [[WHILE_BODY:%.*]] +; FNATTRS: while.body: +; FNATTRS-NEXT: br label [[WHILE_BODY]] +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse noreturn nosync nounwind memory(none) +; ATTRIBUTOR-LABEL: @mustprogress_readnone( +; ATTRIBUTOR-NEXT: entry: +; ATTRIBUTOR-NEXT: br label [[WHILE_BODY:%.*]] +; ATTRIBUTOR: while.body: +; ATTRIBUTOR-NEXT: br label [[WHILE_BODY]] ; entry: br label %while.body @@ -17,13 +25,21 @@ } define i32 @mustprogress_load(ptr %ptr) mustprogress { -; CHECK: Function Attrs: mustprogress nofree norecurse noreturn nosync nounwind willreturn memory(argmem: read) -; CHECK-LABEL: @mustprogress_load( -; CHECK-NEXT: entry: -; CHECK-NEXT: br label [[WHILE_BODY:%.*]] -; CHECK: while.body: -; CHECK-NEXT: [[R:%.*]] = load i32, ptr [[PTR:%.*]], align 4 -; CHECK-NEXT: br label [[WHILE_BODY]] +; FNATTRS: Function Attrs: mustprogress nofree norecurse noreturn nosync nounwind willreturn memory(argmem: read) +; FNATTRS-LABEL: @mustprogress_load( +; FNATTRS-NEXT: entry: +; FNATTRS-NEXT: br label [[WHILE_BODY:%.*]] +; FNATTRS: while.body: +; FNATTRS-NEXT: [[R:%.*]] = load i32, ptr [[PTR:%.*]], align 4 +; FNATTRS-NEXT: br label [[WHILE_BODY]] +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse noreturn nosync nounwind memory(argmem: read) +; ATTRIBUTOR-LABEL: @mustprogress_load( +; ATTRIBUTOR-NEXT: entry: +; ATTRIBUTOR-NEXT: br label [[WHILE_BODY:%.*]] +; ATTRIBUTOR: while.body: +; ATTRIBUTOR-NEXT: [[R:%.*]] = load i32, ptr [[PTR:%.*]], align 4 +; ATTRIBUTOR-NEXT: br label [[WHILE_BODY]] ; entry: br label %while.body @@ -34,13 +50,13 @@ } define void @mustprogress_store(ptr %ptr) mustprogress { -; CHECK: Function Attrs: mustprogress nofree norecurse noreturn nosync nounwind memory(argmem: write) -; CHECK-LABEL: @mustprogress_store( -; CHECK-NEXT: entry: -; CHECK-NEXT: br label [[WHILE_BODY:%.*]] -; CHECK: while.body: -; CHECK-NEXT: store i32 0, ptr [[PTR:%.*]], align 4 -; CHECK-NEXT: br label [[WHILE_BODY]] +; COMMON: Function Attrs: mustprogress nofree norecurse noreturn nosync nounwind memory(argmem: write) +; COMMON-LABEL: @mustprogress_store( +; COMMON-NEXT: entry: +; COMMON-NEXT: br label [[WHILE_BODY:%.*]] +; COMMON: while.body: +; COMMON-NEXT: store i32 0, ptr [[PTR:%.*]], align 4 +; COMMON-NEXT: br label [[WHILE_BODY]] ; entry: br label %while.body @@ -53,21 +69,27 @@ declare void @unknown_fn() define void @mustprogress_call_unknown_fn() mustprogress { -; CHECK: Function Attrs: mustprogress -; CHECK-LABEL: @mustprogress_call_unknown_fn( -; CHECK-NEXT: call void @unknown_fn() -; CHECK-NEXT: ret void +; COMMON: Function Attrs: mustprogress +; COMMON-LABEL: @mustprogress_call_unknown_fn( +; COMMON-NEXT: call void @unknown_fn() +; COMMON-NEXT: ret void ; call void @unknown_fn() ret void } define i32 @mustprogress_call_known_functions(ptr %ptr) mustprogress { -; CHECK: Function Attrs: mustprogress nofree norecurse noreturn nosync nounwind willreturn memory(argmem: read) -; CHECK-LABEL: @mustprogress_call_known_functions( -; CHECK-NEXT: call void @mustprogress_readnone() -; CHECK-NEXT: [[R:%.*]] = call i32 @mustprogress_load(ptr [[PTR:%.*]]) -; CHECK-NEXT: ret i32 [[R]] +; FNATTRS: Function Attrs: mustprogress nofree norecurse noreturn nosync nounwind willreturn memory(argmem: read) +; FNATTRS-LABEL: @mustprogress_call_known_functions( +; FNATTRS-NEXT: call void @mustprogress_readnone() +; FNATTRS-NEXT: [[R:%.*]] = call i32 @mustprogress_load(ptr [[PTR:%.*]]) +; FNATTRS-NEXT: ret i32 [[R]] +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read) +; ATTRIBUTOR-LABEL: @mustprogress_call_known_functions( +; ATTRIBUTOR-NEXT: call void @mustprogress_readnone() #[[ATTR9:[0-9]+]] +; ATTRIBUTOR-NEXT: [[R:%.*]] = call i32 @mustprogress_load(ptr nocapture nofree readonly [[PTR:%.*]]) #[[ATTR12:[0-9]+]] +; ATTRIBUTOR-NEXT: ret i32 [[R]] ; call void @mustprogress_readnone() %r = call i32 @mustprogress_load(ptr %ptr) @@ -77,16 +99,27 @@ declare i32 @__gxx_personality_v0(...) define i64 @mustprogress_mayunwind() mustprogress personality ptr @__gxx_personality_v0 { -; CHECK: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(none) -; CHECK-LABEL: @mustprogress_mayunwind( -; CHECK-NEXT: [[A:%.*]] = invoke i64 @fn_noread() -; CHECK-NEXT: to label [[A:%.*]] unwind label [[B:%.*]] -; CHECK: A: -; CHECK-NEXT: ret i64 10 -; CHECK: B: -; CHECK-NEXT: [[VAL:%.*]] = landingpad { ptr, i32 } -; CHECK-NEXT: catch ptr null -; CHECK-NEXT: ret i64 0 +; FNATTRS: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(none) +; FNATTRS-LABEL: @mustprogress_mayunwind( +; FNATTRS-NEXT: [[A:%.*]] = invoke i64 @fn_noread() +; FNATTRS-NEXT: to label [[A:%.*]] unwind label [[B:%.*]] +; FNATTRS: A: +; FNATTRS-NEXT: ret i64 10 +; FNATTRS: B: +; FNATTRS-NEXT: [[VAL:%.*]] = landingpad { ptr, i32 } +; FNATTRS-NEXT: catch ptr null +; FNATTRS-NEXT: ret i64 0 +; +; ATTRIBUTOR: Function Attrs: mustprogress nosync nounwind willreturn memory(none) +; ATTRIBUTOR-LABEL: @mustprogress_mayunwind( +; ATTRIBUTOR-NEXT: [[A:%.*]] = invoke i64 @fn_noread() +; ATTRIBUTOR-NEXT: to label [[A:%.*]] unwind label [[B:%.*]] +; ATTRIBUTOR: A: +; ATTRIBUTOR-NEXT: ret i64 10 +; ATTRIBUTOR: B: +; ATTRIBUTOR-NEXT: [[VAL:%.*]] = landingpad { ptr, i32 } +; ATTRIBUTOR-NEXT: catch ptr null +; ATTRIBUTOR-NEXT: ret i64 0 ; %a = invoke i64 @fn_noread() to label %A unwind label %B @@ -101,18 +134,31 @@ ; Function without loops or non-willreturn calls will return. define void @willreturn_no_loop(i1 %c, ptr %p) { -; CHECK: Function Attrs: mustprogress willreturn -; CHECK-LABEL: @willreturn_no_loop( -; CHECK-NEXT: br i1 [[C:%.*]], label [[IF:%.*]], label [[ELSE:%.*]] -; CHECK: if: -; CHECK-NEXT: [[TMP1:%.*]] = load atomic i32, ptr [[P:%.*]] seq_cst, align 4 -; CHECK-NEXT: call void @fn_willreturn() -; CHECK-NEXT: br label [[END:%.*]] -; CHECK: else: -; CHECK-NEXT: store atomic i32 0, ptr [[P]] seq_cst, align 4 -; CHECK-NEXT: br label [[END]] -; CHECK: end: -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: mustprogress willreturn +; FNATTRS-LABEL: @willreturn_no_loop( +; FNATTRS-NEXT: br i1 [[C:%.*]], label [[IF:%.*]], label [[ELSE:%.*]] +; FNATTRS: if: +; FNATTRS-NEXT: [[TMP1:%.*]] = load atomic i32, ptr [[P:%.*]] seq_cst, align 4 +; FNATTRS-NEXT: call void @fn_willreturn() +; FNATTRS-NEXT: br label [[END:%.*]] +; FNATTRS: else: +; FNATTRS-NEXT: store atomic i32 0, ptr [[P]] seq_cst, align 4 +; FNATTRS-NEXT: br label [[END]] +; FNATTRS: end: +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: mustprogress willreturn +; ATTRIBUTOR-LABEL: @willreturn_no_loop( +; ATTRIBUTOR-NEXT: br i1 [[C:%.*]], label [[IF:%.*]], label [[ELSE:%.*]] +; ATTRIBUTOR: if: +; ATTRIBUTOR-NEXT: [[TMP1:%.*]] = load atomic i32, ptr [[P:%.*]] seq_cst, align 4 +; ATTRIBUTOR-NEXT: call void @fn_willreturn() #[[ATTR11:[0-9]+]] +; ATTRIBUTOR-NEXT: br label [[END:%.*]] +; ATTRIBUTOR: else: +; ATTRIBUTOR-NEXT: store atomic i32 0, ptr [[P]] seq_cst, align 4 +; ATTRIBUTOR-NEXT: br label [[END]] +; ATTRIBUTOR: end: +; ATTRIBUTOR-NEXT: ret void ; br i1 %c, label %if, label %else @@ -131,9 +177,9 @@ ; Calls a function that is not guaranteed to return, not willreturn. define void @willreturn_non_returning_function(i1 %c, ptr %p) { -; CHECK-LABEL: @willreturn_non_returning_function( -; CHECK-NEXT: call void @unknown_fn() -; CHECK-NEXT: ret void +; COMMON-LABEL: @willreturn_non_returning_function( +; COMMON-NEXT: call void @unknown_fn() +; COMMON-NEXT: ret void ; call void @unknown_fn() ret void @@ -141,11 +187,11 @@ ; Infinite loop without mustprogress, will not return. define void @willreturn_loop() { -; CHECK: Function Attrs: nofree norecurse noreturn nosync nounwind memory(none) -; CHECK-LABEL: @willreturn_loop( -; CHECK-NEXT: br label [[LOOP:%.*]] -; CHECK: loop: -; CHECK-NEXT: br label [[LOOP]] +; COMMON: Function Attrs: nofree norecurse noreturn nosync nounwind memory(none) +; COMMON-LABEL: @willreturn_loop( +; COMMON-NEXT: br label [[LOOP:%.*]] +; COMMON: loop: +; COMMON-NEXT: br label [[LOOP]] ; br label %loop @@ -156,17 +202,17 @@ ; Finite loop. Could be willreturn but not detected. ; FIXME define void @willreturn_finite_loop() { -; CHECK: Function Attrs: nofree norecurse nosync nounwind memory(none) -; CHECK-LABEL: @willreturn_finite_loop( -; CHECK-NEXT: entry: -; CHECK-NEXT: br label [[LOOP:%.*]] -; CHECK: loop: -; CHECK-NEXT: [[I:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[I_INC:%.*]], [[LOOP]] ] -; CHECK-NEXT: [[I_INC]] = add nuw i32 [[I]], 1 -; CHECK-NEXT: [[C:%.*]] = icmp ne i32 [[I_INC]], 100 -; CHECK-NEXT: br i1 [[C]], label [[LOOP]], label [[END:%.*]] -; CHECK: end: -; CHECK-NEXT: ret void +; COMMON: Function Attrs: nofree norecurse nosync nounwind memory(none) +; COMMON-LABEL: @willreturn_finite_loop( +; COMMON-NEXT: entry: +; COMMON-NEXT: br label [[LOOP:%.*]] +; COMMON: loop: +; COMMON-NEXT: [[I:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[I_INC:%.*]], [[LOOP]] ] +; COMMON-NEXT: [[I_INC]] = add nuw i32 [[I]], 1 +; COMMON-NEXT: [[C:%.*]] = icmp ne i32 [[I_INC]], 100 +; COMMON-NEXT: br i1 [[C]], label [[LOOP]], label [[END:%.*]] +; COMMON: end: +; COMMON-NEXT: ret void ; entry: br label %loop @@ -183,10 +229,15 @@ ; Infinite recursion without mustprogress, will not return. define void @willreturn_recursion() { -; CHECK: Function Attrs: nofree nosync nounwind memory(none) -; CHECK-LABEL: @willreturn_recursion( -; CHECK-NEXT: tail call void @willreturn_recursion() -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: nofree nosync nounwind memory(none) +; FNATTRS-LABEL: @willreturn_recursion( +; FNATTRS-NEXT: tail call void @willreturn_recursion() +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: nofree nosync nounwind memory(none) +; ATTRIBUTOR-LABEL: @willreturn_recursion( +; ATTRIBUTOR-NEXT: tail call void @willreturn_recursion() #[[ATTR9]] +; ATTRIBUTOR-NEXT: ret void ; tail call void @willreturn_recursion() ret void @@ -194,13 +245,13 @@ ; Irreducible infinite loop, will not return. define void @willreturn_irreducible(i1 %c) { -; CHECK: Function Attrs: nofree norecurse noreturn nosync nounwind memory(none) -; CHECK-LABEL: @willreturn_irreducible( -; CHECK-NEXT: br i1 [[C:%.*]], label [[BB1:%.*]], label [[BB2:%.*]] -; CHECK: bb1: -; CHECK-NEXT: br label [[BB2]] -; CHECK: bb2: -; CHECK-NEXT: br label [[BB1]] +; COMMON: Function Attrs: nofree norecurse noreturn nosync nounwind memory(none) +; COMMON-LABEL: @willreturn_irreducible( +; COMMON-NEXT: br i1 [[C:%.*]], label [[BB1:%.*]], label [[BB2:%.*]] +; COMMON: bb1: +; COMMON-NEXT: br label [[BB2]] +; COMMON: bb2: +; COMMON-NEXT: br label [[BB1]] ; br i1 %c, label %bb1, label %bb2 @@ -212,9 +263,9 @@ } define linkonce i32 @square(i32) { -; CHECK-LABEL: @square( -; CHECK-NEXT: [[TMP2:%.*]] = mul nsw i32 [[TMP0:%.*]], [[TMP0]] -; CHECK-NEXT: ret i32 [[TMP2]] +; COMMON-LABEL: @square( +; COMMON-NEXT: [[TMP2:%.*]] = mul nsw i32 [[TMP0:%.*]], [[TMP0]] +; COMMON-NEXT: ret i32 [[TMP2]] ; %2 = mul nsw i32 %0, %0 ret i32 %2 diff --git a/llvm/test/Transforms/FunctionAttrs/writeonly.ll b/llvm/test/Transforms/FunctionAttrs/writeonly.ll --- a/llvm/test/Transforms/FunctionAttrs/writeonly.ll +++ b/llvm/test/Transforms/FunctionAttrs/writeonly.ll @@ -1,24 +1,38 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes -; RUN: opt < %s -passes=function-attrs -S | FileCheck %s +; RUN: opt -passes=function-attrs -S < %s | FileCheck --check-prefixes=COMMON,FNATTRS %s +; RUN: opt -passes=attributor-light -S < %s | FileCheck --check-prefixes=COMMON,ATTRIBUTOR %s define void @nouses-argworn-funrn(ptr writeonly %.aaa) { -; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) -; CHECK-LABEL: define {{[^@]+}}@nouses-argworn-funrn -; CHECK-SAME: (ptr nocapture readnone [[DOTAAA:%.*]]) #[[ATTR0:[0-9]+]] { -; CHECK-NEXT: nouses-argworn-funrn_entry: -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) +; FNATTRS-LABEL: define {{[^@]+}}@nouses-argworn-funrn +; FNATTRS-SAME: (ptr nocapture readnone [[DOTAAA:%.*]]) #[[ATTR0:[0-9]+]] { +; FNATTRS-NEXT: nouses-argworn-funrn_entry: +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) +; ATTRIBUTOR-LABEL: define {{[^@]+}}@nouses-argworn-funrn +; ATTRIBUTOR-SAME: (ptr nocapture nofree readnone [[DOTAAA:%.*]]) #[[ATTR0:[0-9]+]] { +; ATTRIBUTOR-NEXT: nouses-argworn-funrn_entry: +; ATTRIBUTOR-NEXT: ret void ; nouses-argworn-funrn_entry: ret void } define void @nouses-argworn-funro(ptr writeonly %.aaa, ptr %.bbb) { -; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read) -; CHECK-LABEL: define {{[^@]+}}@nouses-argworn-funro -; CHECK-SAME: (ptr nocapture readnone [[DOTAAA:%.*]], ptr nocapture readonly [[DOTBBB:%.*]]) #[[ATTR1:[0-9]+]] { -; CHECK-NEXT: nouses-argworn-funro_entry: -; CHECK-NEXT: [[VAL:%.*]] = load i32, ptr [[DOTBBB]], align 4 -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read) +; FNATTRS-LABEL: define {{[^@]+}}@nouses-argworn-funro +; FNATTRS-SAME: (ptr nocapture readnone [[DOTAAA:%.*]], ptr nocapture readonly [[DOTBBB:%.*]]) #[[ATTR1:[0-9]+]] { +; FNATTRS-NEXT: nouses-argworn-funro_entry: +; FNATTRS-NEXT: [[VAL:%.*]] = load i32, ptr [[DOTBBB]], align 4 +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read) +; ATTRIBUTOR-LABEL: define {{[^@]+}}@nouses-argworn-funro +; ATTRIBUTOR-SAME: (ptr nocapture nofree readnone [[DOTAAA:%.*]], ptr nocapture nofree nonnull readonly [[DOTBBB:%.*]]) #[[ATTR1:[0-9]+]] { +; ATTRIBUTOR-NEXT: nouses-argworn-funro_entry: +; ATTRIBUTOR-NEXT: [[VAL:%.*]] = load i32, ptr [[DOTBBB]], align 4 +; ATTRIBUTOR-NEXT: ret void ; nouses-argworn-funro_entry: %val = load i32 , ptr %.bbb @@ -30,12 +44,19 @@ @d-ccc = internal global %_type_of_d-ccc <{ ptr null, i8 1, i8 13, i8 0, i8 -127 }>, align 8 define void @nouses-argworn-funwo(ptr writeonly %.aaa) { -; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none) -; CHECK-LABEL: define {{[^@]+}}@nouses-argworn-funwo -; CHECK-SAME: (ptr nocapture readnone [[DOTAAA:%.*]]) #[[ATTR2:[0-9]+]] { -; CHECK-NEXT: nouses-argworn-funwo_entry: -; CHECK-NEXT: store i8 0, ptr getelementptr inbounds ([[_TYPE_OF_D_CCC:%.*]], ptr @d-ccc, i32 0, i32 3), align 1 -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none) +; FNATTRS-LABEL: define {{[^@]+}}@nouses-argworn-funwo +; FNATTRS-SAME: (ptr nocapture readnone [[DOTAAA:%.*]]) #[[ATTR2:[0-9]+]] { +; FNATTRS-NEXT: nouses-argworn-funwo_entry: +; FNATTRS-NEXT: store i8 0, ptr getelementptr inbounds ([[_TYPE_OF_D_CCC:%.*]], ptr @d-ccc, i32 0, i32 3), align 1 +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write) +; ATTRIBUTOR-LABEL: define {{[^@]+}}@nouses-argworn-funwo +; ATTRIBUTOR-SAME: (ptr nocapture nofree readnone [[DOTAAA:%.*]]) #[[ATTR2:[0-9]+]] { +; ATTRIBUTOR-NEXT: nouses-argworn-funwo_entry: +; ATTRIBUTOR-NEXT: store i8 0, ptr getelementptr inbounds ([[_TYPE_OF_D_CCC:%.*]], ptr @d-ccc, i32 0, i32 3), align 1 +; ATTRIBUTOR-NEXT: ret void ; nouses-argworn-funwo_entry: store i8 0, ptr getelementptr inbounds (%_type_of_d-ccc, ptr @d-ccc, i32 0, i32 3) @@ -43,11 +64,17 @@ } define void @test_store(ptr %p) { -; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) -; CHECK-LABEL: define {{[^@]+}}@test_store -; CHECK-SAME: (ptr nocapture writeonly [[P:%.*]]) #[[ATTR3:[0-9]+]] { -; CHECK-NEXT: store i8 0, ptr [[P]], align 1 -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) +; FNATTRS-LABEL: define {{[^@]+}}@test_store +; FNATTRS-SAME: (ptr nocapture writeonly [[P:%.*]]) #[[ATTR3:[0-9]+]] { +; FNATTRS-NEXT: store i8 0, ptr [[P]], align 1 +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) +; ATTRIBUTOR-LABEL: define {{[^@]+}}@test_store +; ATTRIBUTOR-SAME: (ptr nocapture nofree nonnull writeonly [[P:%.*]]) #[[ATTR3:[0-9]+]] { +; ATTRIBUTOR-NEXT: store i8 0, ptr [[P]], align 1 +; ATTRIBUTOR-NEXT: ret void ; store i8 0, ptr %p ret void @@ -55,13 +82,21 @@ @G = external global ptr define i8 @test_store_capture(ptr %p) { -; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, argmem: read, inaccessiblemem: none) -; CHECK-LABEL: define {{[^@]+}}@test_store_capture -; CHECK-SAME: (ptr [[P:%.*]]) #[[ATTR4:[0-9]+]] { -; CHECK-NEXT: store ptr [[P]], ptr @G, align 8 -; CHECK-NEXT: [[P2:%.*]] = load ptr, ptr @G, align 8 -; CHECK-NEXT: [[V:%.*]] = load i8, ptr [[P2]], align 1 -; CHECK-NEXT: ret i8 [[V]] +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, argmem: read, inaccessiblemem: none) +; FNATTRS-LABEL: define {{[^@]+}}@test_store_capture +; FNATTRS-SAME: (ptr [[P:%.*]]) #[[ATTR4:[0-9]+]] { +; FNATTRS-NEXT: store ptr [[P]], ptr @G, align 8 +; FNATTRS-NEXT: [[P2:%.*]] = load ptr, ptr @G, align 8 +; FNATTRS-NEXT: [[V:%.*]] = load i8, ptr [[P2]], align 1 +; FNATTRS-NEXT: ret i8 [[V]] +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn +; ATTRIBUTOR-LABEL: define {{[^@]+}}@test_store_capture +; ATTRIBUTOR-SAME: (ptr nofree [[P:%.*]]) #[[ATTR4:[0-9]+]] { +; ATTRIBUTOR-NEXT: store ptr [[P]], ptr @G, align 8 +; ATTRIBUTOR-NEXT: [[P2:%.*]] = load ptr, ptr @G, align 8 +; ATTRIBUTOR-NEXT: [[V:%.*]] = load i8, ptr [[P2]], align 1 +; ATTRIBUTOR-NEXT: ret i8 [[V]] ; store ptr %p, ptr @G %p2 = load ptr, ptr @G @@ -70,12 +105,19 @@ } define void @test_addressing(ptr %p) { -; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) -; CHECK-LABEL: define {{[^@]+}}@test_addressing -; CHECK-SAME: (ptr nocapture writeonly [[P:%.*]]) #[[ATTR3]] { -; CHECK-NEXT: [[GEP:%.*]] = getelementptr i8, ptr [[P]], i64 8 -; CHECK-NEXT: store i32 0, ptr [[GEP]], align 4 -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) +; FNATTRS-LABEL: define {{[^@]+}}@test_addressing +; FNATTRS-SAME: (ptr nocapture writeonly [[P:%.*]]) #[[ATTR3]] { +; FNATTRS-NEXT: [[GEP:%.*]] = getelementptr i8, ptr [[P]], i64 8 +; FNATTRS-NEXT: store i32 0, ptr [[GEP]], align 4 +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) +; ATTRIBUTOR-LABEL: define {{[^@]+}}@test_addressing +; ATTRIBUTOR-SAME: (ptr nocapture nofree writeonly [[P:%.*]]) #[[ATTR3]] { +; ATTRIBUTOR-NEXT: [[GEP:%.*]] = getelementptr i8, ptr [[P]], i64 8 +; ATTRIBUTOR-NEXT: store i32 0, ptr [[GEP]], align 4 +; ATTRIBUTOR-NEXT: ret void ; %gep = getelementptr i8, ptr %p, i64 8 store i32 0, ptr %gep @@ -83,12 +125,19 @@ } define void @test_readwrite(ptr %p) { -; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite) -; CHECK-LABEL: define {{[^@]+}}@test_readwrite -; CHECK-SAME: (ptr nocapture [[P:%.*]]) #[[ATTR5:[0-9]+]] { -; CHECK-NEXT: [[V:%.*]] = load i8, ptr [[P]], align 1 -; CHECK-NEXT: store i8 [[V]], ptr [[P]], align 1 -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite) +; FNATTRS-LABEL: define {{[^@]+}}@test_readwrite +; FNATTRS-SAME: (ptr nocapture [[P:%.*]]) #[[ATTR5:[0-9]+]] { +; FNATTRS-NEXT: [[V:%.*]] = load i8, ptr [[P]], align 1 +; FNATTRS-NEXT: store i8 [[V]], ptr [[P]], align 1 +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite) +; ATTRIBUTOR-LABEL: define {{[^@]+}}@test_readwrite +; ATTRIBUTOR-SAME: (ptr nocapture nofree nonnull [[P:%.*]]) #[[ATTR5:[0-9]+]] { +; ATTRIBUTOR-NEXT: [[V:%.*]] = load i8, ptr [[P]], align 1 +; ATTRIBUTOR-NEXT: store i8 [[V]], ptr [[P]], align 1 +; ATTRIBUTOR-NEXT: ret void ; %v = load i8, ptr %p store i8 %v, ptr %p @@ -96,22 +145,34 @@ } define void @test_volatile(ptr %p) { -; CHECK: Function Attrs: nofree norecurse nounwind memory(argmem: readwrite, inaccessiblemem: readwrite) -; CHECK-LABEL: define {{[^@]+}}@test_volatile -; CHECK-SAME: (ptr [[P:%.*]]) #[[ATTR6:[0-9]+]] { -; CHECK-NEXT: store volatile i8 0, ptr [[P]], align 1 -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: nofree norecurse nounwind memory(argmem: readwrite, inaccessiblemem: readwrite) +; FNATTRS-LABEL: define {{[^@]+}}@test_volatile +; FNATTRS-SAME: (ptr [[P:%.*]]) #[[ATTR6:[0-9]+]] { +; FNATTRS-NEXT: store volatile i8 0, ptr [[P]], align 1 +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite) +; ATTRIBUTOR-LABEL: define {{[^@]+}}@test_volatile +; ATTRIBUTOR-SAME: (ptr nofree [[P:%.*]]) #[[ATTR6:[0-9]+]] { +; ATTRIBUTOR-NEXT: store volatile i8 0, ptr [[P]], align 1 +; ATTRIBUTOR-NEXT: ret void ; store volatile i8 0, ptr %p ret void } define void @test_atomicrmw(ptr %p) { -; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite) -; CHECK-LABEL: define {{[^@]+}}@test_atomicrmw -; CHECK-SAME: (ptr nocapture [[P:%.*]]) #[[ATTR7:[0-9]+]] { -; CHECK-NEXT: [[TMP1:%.*]] = atomicrmw add ptr [[P]], i8 0 seq_cst, align 1 -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite) +; FNATTRS-LABEL: define {{[^@]+}}@test_atomicrmw +; FNATTRS-SAME: (ptr nocapture [[P:%.*]]) #[[ATTR7:[0-9]+]] { +; FNATTRS-NEXT: [[TMP1:%.*]] = atomicrmw add ptr [[P]], i8 0 seq_cst, align 1 +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite) +; ATTRIBUTOR-LABEL: define {{[^@]+}}@test_atomicrmw +; ATTRIBUTOR-SAME: (ptr nocapture nofree nonnull [[P:%.*]]) #[[ATTR6]] { +; ATTRIBUTOR-NEXT: [[TMP1:%.*]] = atomicrmw add ptr [[P]], i8 0 seq_cst, align 1 +; ATTRIBUTOR-NEXT: ret void ; atomicrmw add ptr %p, i8 0 seq_cst ret void @@ -121,10 +182,10 @@ declare void @direct1_callee(ptr %p) define void @direct1(ptr %p) { -; CHECK-LABEL: define {{[^@]+}}@direct1 -; CHECK-SAME: (ptr [[P:%.*]]) { -; CHECK-NEXT: call void @direct1_callee(ptr [[P]]) -; CHECK-NEXT: ret void +; COMMON-LABEL: define {{[^@]+}}@direct1 +; COMMON-SAME: (ptr [[P:%.*]]) { +; COMMON-NEXT: call void @direct1_callee(ptr [[P]]) +; COMMON-NEXT: ret void ; call void @direct1_callee(ptr %p) ret void @@ -134,11 +195,17 @@ ; writeonly w/o nocapture is not enough define void @direct2(ptr %p) { -; CHECK: Function Attrs: memory(write) -; CHECK-LABEL: define {{[^@]+}}@direct2 -; CHECK-SAME: (ptr [[P:%.*]]) #[[ATTR8:[0-9]+]] { -; CHECK-NEXT: call void @direct2_callee(ptr [[P]]) -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: memory(write) +; FNATTRS-LABEL: define {{[^@]+}}@direct2 +; FNATTRS-SAME: (ptr [[P:%.*]]) #[[ATTR8:[0-9]+]] { +; FNATTRS-NEXT: call void @direct2_callee(ptr [[P]]) +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: memory(write) +; ATTRIBUTOR-LABEL: define {{[^@]+}}@direct2 +; ATTRIBUTOR-SAME: (ptr writeonly [[P:%.*]]) #[[ATTR7:[0-9]+]] { +; ATTRIBUTOR-NEXT: call void @direct2_callee(ptr [[P]]) #[[ATTR7]] +; ATTRIBUTOR-NEXT: ret void ; call void @direct2_callee(ptr %p) ; read back from global, read through pointer... @@ -146,11 +213,17 @@ } define void @direct2b(ptr %p) { -; CHECK: Function Attrs: memory(write) -; CHECK-LABEL: define {{[^@]+}}@direct2b -; CHECK-SAME: (ptr nocapture [[P:%.*]]) #[[ATTR8]] { -; CHECK-NEXT: call void @direct2_callee(ptr nocapture [[P]]) -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: memory(write) +; FNATTRS-LABEL: define {{[^@]+}}@direct2b +; FNATTRS-SAME: (ptr nocapture [[P:%.*]]) #[[ATTR8]] { +; FNATTRS-NEXT: call void @direct2_callee(ptr nocapture [[P]]) +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: memory(write) +; ATTRIBUTOR-LABEL: define {{[^@]+}}@direct2b +; ATTRIBUTOR-SAME: (ptr nocapture writeonly [[P:%.*]]) #[[ATTR7]] { +; ATTRIBUTOR-NEXT: call void @direct2_callee(ptr nocapture writeonly [[P]]) #[[ATTR7]] +; ATTRIBUTOR-NEXT: ret void ; call void @direct2_callee(ptr nocapture %p) ret void @@ -159,61 +232,87 @@ declare void @direct3_callee(ptr nocapture writeonly %p) define void @direct3(ptr %p) { -; CHECK-LABEL: define {{[^@]+}}@direct3 -; CHECK-SAME: (ptr nocapture writeonly [[P:%.*]]) { -; CHECK-NEXT: call void @direct3_callee(ptr [[P]]) -; CHECK-NEXT: ret void +; FNATTRS-LABEL: define {{[^@]+}}@direct3 +; FNATTRS-SAME: (ptr nocapture writeonly [[P:%.*]]) { +; FNATTRS-NEXT: call void @direct3_callee(ptr [[P]]) +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR-LABEL: define {{[^@]+}}@direct3 +; ATTRIBUTOR-SAME: (ptr nocapture writeonly [[P:%.*]]) { +; ATTRIBUTOR-NEXT: call void @direct3_callee(ptr nocapture writeonly [[P]]) +; ATTRIBUTOR-NEXT: ret void ; call void @direct3_callee(ptr %p) ret void } define void @direct3b(ptr %p) { -; CHECK-LABEL: define {{[^@]+}}@direct3b -; CHECK-SAME: (ptr [[P:%.*]]) { -; CHECK-NEXT: call void @direct3_callee(ptr [[P]]) [ "may-read-and-capture"(ptr [[P]]) ] -; CHECK-NEXT: ret void +; COMMON-LABEL: define {{[^@]+}}@direct3b +; COMMON-SAME: (ptr [[P:%.*]]) { +; COMMON-NEXT: call void @direct3_callee(ptr [[P]]) [ "may-read-and-capture"(ptr [[P]]) ] +; COMMON-NEXT: ret void ; call void @direct3_callee(ptr %p) ["may-read-and-capture"(ptr %p)] ret void } define void @direct3c(ptr %p) { -; CHECK-LABEL: define {{[^@]+}}@direct3c -; CHECK-SAME: (ptr nocapture [[P:%.*]]) { -; CHECK-NEXT: call void @direct3_callee(ptr [[P]]) [ "may-read"() ] -; CHECK-NEXT: ret void +; FNATTRS-LABEL: define {{[^@]+}}@direct3c +; FNATTRS-SAME: (ptr nocapture [[P:%.*]]) { +; FNATTRS-NEXT: call void @direct3_callee(ptr [[P]]) [ "may-read"() ] +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR-LABEL: define {{[^@]+}}@direct3c +; ATTRIBUTOR-SAME: (ptr nocapture [[P:%.*]]) { +; ATTRIBUTOR-NEXT: call void @direct3_callee(ptr nocapture [[P]]) [ "may-read"() ] +; ATTRIBUTOR-NEXT: ret void ; call void @direct3_callee(ptr %p) ["may-read"()] ret void } define void @fptr_test1(ptr %p, ptr %f) { -; CHECK-LABEL: define {{[^@]+}}@fptr_test1 -; CHECK-SAME: (ptr [[P:%.*]], ptr nocapture readonly [[F:%.*]]) { -; CHECK-NEXT: call void [[F]](ptr [[P]]) -; CHECK-NEXT: ret void +; FNATTRS-LABEL: define {{[^@]+}}@fptr_test1 +; FNATTRS-SAME: (ptr [[P:%.*]], ptr nocapture readonly [[F:%.*]]) { +; FNATTRS-NEXT: call void [[F]](ptr [[P]]) +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR-LABEL: define {{[^@]+}}@fptr_test1 +; ATTRIBUTOR-SAME: (ptr [[P:%.*]], ptr nocapture nofree nonnull [[F:%.*]]) { +; ATTRIBUTOR-NEXT: call void [[F]](ptr [[P]]) +; ATTRIBUTOR-NEXT: ret void ; call void %f(ptr %p) ret void } define void @fptr_test2(ptr %p, ptr %f) { -; CHECK-LABEL: define {{[^@]+}}@fptr_test2 -; CHECK-SAME: (ptr nocapture writeonly [[P:%.*]], ptr nocapture readonly [[F:%.*]]) { -; CHECK-NEXT: call void [[F]](ptr nocapture writeonly [[P]]) -; CHECK-NEXT: ret void +; FNATTRS-LABEL: define {{[^@]+}}@fptr_test2 +; FNATTRS-SAME: (ptr nocapture writeonly [[P:%.*]], ptr nocapture readonly [[F:%.*]]) { +; FNATTRS-NEXT: call void [[F]](ptr nocapture writeonly [[P]]) +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR-LABEL: define {{[^@]+}}@fptr_test2 +; ATTRIBUTOR-SAME: (ptr nocapture [[P:%.*]], ptr nocapture nofree nonnull [[F:%.*]]) { +; ATTRIBUTOR-NEXT: call void [[F]](ptr nocapture writeonly [[P]]) +; ATTRIBUTOR-NEXT: ret void ; call void %f(ptr nocapture writeonly %p) ret void } define void @fptr_test3(ptr %p, ptr %f) { -; CHECK: Function Attrs: memory(write) -; CHECK-LABEL: define {{[^@]+}}@fptr_test3 -; CHECK-SAME: (ptr nocapture [[P:%.*]], ptr nocapture readonly [[F:%.*]]) #[[ATTR8]] { -; CHECK-NEXT: call void [[F]](ptr nocapture [[P]]) #[[ATTR8]] -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: memory(write) +; FNATTRS-LABEL: define {{[^@]+}}@fptr_test3 +; FNATTRS-SAME: (ptr nocapture [[P:%.*]], ptr nocapture readonly [[F:%.*]]) #[[ATTR8]] { +; FNATTRS-NEXT: call void [[F]](ptr nocapture [[P]]) #[[ATTR8]] +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: memory(write) +; ATTRIBUTOR-LABEL: define {{[^@]+}}@fptr_test3 +; ATTRIBUTOR-SAME: (ptr nocapture writeonly [[P:%.*]], ptr nocapture nofree nonnull writeonly [[F:%.*]]) #[[ATTR7]] { +; ATTRIBUTOR-NEXT: call void [[F]](ptr nocapture [[P]]) #[[ATTR7]] +; ATTRIBUTOR-NEXT: ret void ; call void %f(ptr nocapture %p) writeonly ret void