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 @@ -2375,6 +2375,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. @@ -3350,6 +3354,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)) 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 @@ -3749,6 +3749,101 @@ 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, + &AAReturnedValues::ID, + &AAInterFnReachability::ID, + &AAIntraFnReachability::ID, + &AACallEdges::ID, + &AANoFPClass::ID, + &AAMustProgress::ID, + &AANoAlias::ID, + &AANonNull::ID, + }); + AC.Allowed = &Allowed; + + 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() { @@ -3829,6 +3924,62 @@ return PreservedAnalyses::all(); } +PreservedAnalyses AttributorLightPass::run(Module &M, + ModuleAnalysisManager &AM) { + FunctionAnalysisManager &FAM = + AM.getResult(M).getManager(); + AnalysisGetter AG(FAM); + + 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-light -attributor-manifest-internal -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT declare float @llvm.round.f32(float) declare ppc_fp128 @llvm.round.ppcf128(ppc_fp128) 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,24 +1,25 @@ ; 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 @g = global i32 20 define void @test_no_read_or_write() { -; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) -; CHECK-LABEL: @test_no_read_or_write( -; CHECK-NEXT: entry: -; CHECK-NEXT: ret void +; COMMON: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) +; COMMON-LABEL: @test_no_read_or_write( +; 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: @test_only_read_arg( -; CHECK-NEXT: entry: -; CHECK-NEXT: [[L:%.*]] = load i32, ptr [[PTR:%.*]], align 4 -; CHECK-NEXT: ret i32 [[L]] +; COMMON: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read) +; COMMON-LABEL: @test_only_read_arg( +; COMMON-NEXT: entry: +; COMMON-NEXT: [[L:%.*]] = load i32, ptr [[PTR:%.*]], align 4 +; COMMON-NEXT: ret i32 [[L]] ; entry: %l = load i32, ptr %ptr @@ -26,11 +27,11 @@ } 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: @test_only_read_arg_already_has_argmemonly( -; CHECK-NEXT: entry: -; CHECK-NEXT: [[L:%.*]] = load i32, ptr [[PTR:%.*]], align 4 -; CHECK-NEXT: ret i32 [[L]] +; COMMON: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read) +; COMMON-LABEL: @test_only_read_arg_already_has_argmemonly( +; COMMON-NEXT: entry: +; COMMON-NEXT: [[L:%.*]] = load i32, ptr [[PTR:%.*]], align 4 +; COMMON-NEXT: ret i32 [[L]] ; entry: %l = load i32, ptr %ptr @@ -38,11 +39,17 @@ } define i32 @test_read_global() { -; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, argmem: none, inaccessiblemem: none) -; CHECK-LABEL: @test_read_global( -; 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: @test_read_global( +; 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: @test_read_global( +; ATTRIBUTOR-NEXT: entry: +; ATTRIBUTOR-NEXT: [[L:%.*]] = load i32, ptr @g, align 4 +; ATTRIBUTOR-NEXT: ret i32 [[L]] ; entry: %l = load i32, ptr @g @@ -50,12 +57,19 @@ } define i32 @test_read_loaded_ptr(ptr %ptr) { -; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, inaccessiblemem: none) -; CHECK-LABEL: @test_read_loaded_ptr( -; 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: @test_read_loaded_ptr( +; 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: @test_read_loaded_ptr( +; 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 @@ -64,11 +78,11 @@ } define void @test_only_write_arg(ptr %ptr) { -; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) -; CHECK-LABEL: @test_only_write_arg( -; CHECK-NEXT: entry: -; CHECK-NEXT: store i32 0, ptr [[PTR:%.*]], align 4 -; CHECK-NEXT: ret void +; COMMON: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) +; COMMON-LABEL: @test_only_write_arg( +; COMMON-NEXT: entry: +; COMMON-NEXT: store i32 0, ptr [[PTR:%.*]], align 4 +; COMMON-NEXT: ret void ; entry: store i32 0, ptr %ptr @@ -76,11 +90,17 @@ } define void @test_write_global() { -; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none) -; CHECK-LABEL: @test_write_global( -; 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: @test_write_global( +; 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: @test_write_global( +; ATTRIBUTOR-NEXT: entry: +; ATTRIBUTOR-NEXT: store i32 0, ptr @g, align 4 +; ATTRIBUTOR-NEXT: ret void ; entry: store i32 0, ptr @g @@ -90,10 +110,10 @@ declare void @fn_may_access_memory() define void @test_call_may_access_memory() { -; CHECK-LABEL: @test_call_may_access_memory( -; CHECK-NEXT: entry: -; CHECK-NEXT: call void @fn_may_access_memory() -; CHECK-NEXT: ret void +; COMMON-LABEL: @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() @@ -103,12 +123,19 @@ declare i32 @fn_readnone() readnone define void @test_call_readnone(ptr %ptr) { -; CHECK: Function Attrs: memory(argmem: write) -; CHECK-LABEL: @test_call_readnone( -; 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: @test_call_readnone( +; 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: @test_call_readnone( +; ATTRIBUTOR-NEXT: entry: +; ATTRIBUTOR-NEXT: [[C:%.*]] = call i32 @fn_readnone() +; ATTRIBUTOR-NEXT: store i32 [[C]], ptr [[PTR:%.*]], align 4 +; ATTRIBUTOR-NEXT: ret void ; entry: %c = call i32 @fn_readnone() @@ -119,11 +146,11 @@ declare i32 @fn_argmemonly(ptr) argmemonly define i32 @test_call_argmemonly(ptr %ptr) { -; CHECK: Function Attrs: memory(argmem: readwrite) -; CHECK-LABEL: @test_call_argmemonly( -; CHECK-NEXT: entry: -; CHECK-NEXT: [[C:%.*]] = call i32 @fn_argmemonly(ptr [[PTR:%.*]]) -; CHECK-NEXT: ret i32 [[C]] +; COMMON: Function Attrs: memory(argmem: readwrite) +; COMMON-LABEL: @test_call_argmemonly( +; COMMON-NEXT: entry: +; COMMON-NEXT: [[C:%.*]] = call i32 @fn_argmemonly(ptr [[PTR:%.*]]) +; COMMON-NEXT: ret i32 [[C]] ; entry: %c = call i32 @fn_argmemonly(ptr %ptr) @@ -131,11 +158,17 @@ } 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: @test_call_fn_where_argmemonly_can_be_inferred( -; 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: @test_call_fn_where_argmemonly_can_be_inferred( +; 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: @test_call_fn_where_argmemonly_can_be_inferred( +; ATTRIBUTOR-NEXT: entry: +; ATTRIBUTOR-NEXT: [[C:%.*]] = call i32 @test_only_read_arg(ptr nocapture nofree readonly [[PTR:%.*]]) #[[ATTR15:[0-9]+]] +; ATTRIBUTOR-NEXT: ret i32 [[C]] ; entry: %c = call i32 @test_only_read_arg(ptr %ptr) @@ -143,11 +176,17 @@ } define void @test_memcpy_argonly(ptr %dst, ptr %src) { -; CHECK: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(argmem: readwrite) -; CHECK-LABEL: @test_memcpy_argonly( -; 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: @test_memcpy_argonly( +; 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: @test_memcpy_argonly( +; ATTRIBUTOR-NEXT: entry: +; ATTRIBUTOR-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr nocapture writeonly [[DST:%.*]], ptr nocapture readonly [[SRC:%.*]], i64 32, i1 false) #[[ATTR16:[0-9]+]] +; ATTRIBUTOR-NEXT: ret void ; entry: call void @llvm.memcpy.p0.p0.i64(ptr %dst, ptr %src, i64 32, i1 false) @@ -159,11 +198,17 @@ @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: @test_memcpy_src_global( -; 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: @test_memcpy_src_global( +; 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: @test_memcpy_src_global( +; ATTRIBUTOR-NEXT: entry: +; ATTRIBUTOR-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr nocapture writeonly [[DST:%.*]], ptr readonly @arr, i64 32, i1 false) #[[ATTR16]] +; ATTRIBUTOR-NEXT: ret void ; entry: call void @llvm.memcpy.p0.p0.i64(ptr %dst, ptr @arr, i64 32, i1 false) @@ -171,11 +216,17 @@ } define void @test_memcpy_dst_global(ptr %src) { -; CHECK: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(readwrite, inaccessiblemem: none) -; CHECK-LABEL: @test_memcpy_dst_global( -; 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: @test_memcpy_dst_global( +; 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: @test_memcpy_dst_global( +; ATTRIBUTOR-NEXT: entry: +; ATTRIBUTOR-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr writeonly @arr, ptr nocapture readonly [[SRC:%.*]], i64 32, i1 false) #[[ATTR16]] +; ATTRIBUTOR-NEXT: ret void ; entry: call void @llvm.memcpy.p0.p0.i64(ptr @arr, ptr %src, i64 32, i1 false) @@ -183,14 +234,23 @@ } define i32 @test_read_arg_access_alloca(ptr %ptr) { -; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read) -; CHECK-LABEL: @test_read_arg_access_alloca( -; 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: @test_read_arg_access_alloca( +; 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: @test_read_arg_access_alloca( +; 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 @@ -203,31 +263,42 @@ declare void @fn_inaccessiblememonly() inaccessiblememonly define void @test_inaccessiblememonly() { -; CHECK: Function Attrs: memory(inaccessiblemem: readwrite) -; CHECK-LABEL: @test_inaccessiblememonly( -; CHECK-NEXT: call void @fn_inaccessiblememonly() -; CHECK-NEXT: ret void +; COMMON: Function Attrs: memory(inaccessiblemem: readwrite) +; COMMON-LABEL: @test_inaccessiblememonly( +; COMMON-NEXT: call void @fn_inaccessiblememonly() +; COMMON-NEXT: ret void ; call void @fn_inaccessiblememonly() ret void } define void @test_inaccessiblememonly_readonly() { -; CHECK: Function Attrs: nofree memory(inaccessiblemem: read) -; CHECK-LABEL: @test_inaccessiblememonly_readonly( -; CHECK-NEXT: call void @fn_inaccessiblememonly() #[[ATTR16:[0-9]+]] -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: nofree memory(inaccessiblemem: read) +; FNATTRS-LABEL: @test_inaccessiblememonly_readonly( +; FNATTRS-NEXT: call void @fn_inaccessiblememonly() #[[ATTR16:[0-9]+]] +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: memory(inaccessiblemem: read) +; ATTRIBUTOR-LABEL: @test_inaccessiblememonly_readonly( +; ATTRIBUTOR-NEXT: call void @fn_inaccessiblememonly() #[[ATTR17:[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: @test_inaccessibleorargmemonly_readonly( -; CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[ARG:%.*]], align 4 -; CHECK-NEXT: call void @fn_inaccessiblememonly() #[[ATTR16]] -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: nofree memory(argmem: read, inaccessiblemem: read) +; FNATTRS-LABEL: @test_inaccessibleorargmemonly_readonly( +; FNATTRS-NEXT: [[TMP1:%.*]] = load i32, ptr [[ARG:%.*]], align 4 +; FNATTRS-NEXT: call void @fn_inaccessiblememonly() #[[ATTR16]] +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: memory(argmem: read, inaccessiblemem: read) +; ATTRIBUTOR-LABEL: @test_inaccessibleorargmemonly_readonly( +; ATTRIBUTOR-NEXT: [[TMP1:%.*]] = load i32, ptr [[ARG:%.*]], align 4 +; ATTRIBUTOR-NEXT: call void @fn_inaccessiblememonly() #[[ATTR17]] +; ATTRIBUTOR-NEXT: ret void ; load i32, ptr %arg call void @fn_inaccessiblememonly() readonly @@ -235,11 +306,17 @@ } define void @test_inaccessibleorargmemonly_readwrite(ptr %arg) { -; CHECK: Function Attrs: memory(argmem: write, inaccessiblemem: read) -; CHECK-LABEL: @test_inaccessibleorargmemonly_readwrite( -; CHECK-NEXT: store i32 0, ptr [[ARG:%.*]], align 4 -; CHECK-NEXT: call void @fn_inaccessiblememonly() #[[ATTR16]] -; CHECK-NEXT: ret void +; FNATTRS: Function Attrs: memory(argmem: write, inaccessiblemem: read) +; FNATTRS-LABEL: @test_inaccessibleorargmemonly_readwrite( +; FNATTRS-NEXT: store i32 0, ptr [[ARG:%.*]], align 4 +; FNATTRS-NEXT: call void @fn_inaccessiblememonly() #[[ATTR16]] +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: memory(argmem: readwrite, inaccessiblemem: readwrite) +; ATTRIBUTOR-LABEL: @test_inaccessibleorargmemonly_readwrite( +; ATTRIBUTOR-NEXT: store i32 0, ptr [[ARG:%.*]], align 4 +; ATTRIBUTOR-NEXT: call void @fn_inaccessiblememonly() #[[ATTR17]] +; ATTRIBUTOR-NEXT: ret void ; store i32 0, ptr %arg call void @fn_inaccessiblememonly() readonly 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 returned [[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]]) #[[ATTR13:[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 nocapture 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: memory(read) +; ATTRIBUTOR-LABEL: define i1 @c6 +; ATTRIBUTOR-SAME: (ptr readonly [[Q:%.*]], i8 [[BIT:%.*]]) #[[ATTR3:[0-9]+]] personality ptr @__gxx_personality_v0 { +; ATTRIBUTOR-NEXT: invoke void @throw_if_bit_set(ptr [[Q]], i8 [[BIT]]) #[[ATTR3]] +; 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 noalias nofree readnone [[Q]], i32 [[BITNO]]) #[[ATTR14:[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:%.*]]) #[[ATTR4:[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:%.*]]) #[[ATTR4]] { +; 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:%.*]]) #[[ATTR4]] { +; ATTRIBUTOR-NEXT: [[TMP1:%.*]] = call i32 @nc1(ptr nofree [[Q]], ptr nocapture nofree [[P]], i1 false) #[[ATTR15:[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: nounwind memory(read) +; ATTRIBUTOR-LABEL: define void @nc4 +; ATTRIBUTOR-SAME: (ptr nocapture readonly [[P:%.*]]) #[[ATTR5:[0-9]+]] { +; ATTRIBUTOR-NEXT: call void @external(ptr nocapture readonly [[P]]) #[[ATTR3]] +; 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]]) #[[ATTR5]] +; 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:%.*]]) #[[ATTR6:[0-9]+]] { +; ATTRIBUTOR-NEXT: [[TMP1:%.*]] = call ptr @test1_2(ptr noalias nocapture nofree readnone [[X1_1]], ptr noalias nofree readnone [[Y1_1]], i1 [[C]]) #[[ATTR6]] +; 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 returned [[Y1_2:%.*]], i1 [[C:%.*]]) #[[ATTR6]] { +; ATTRIBUTOR-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] +; ATTRIBUTOR: t: +; ATTRIBUTOR-NEXT: call void @test1_1(ptr noalias nocapture nofree readnone [[X1_2]], ptr noalias nocapture nofree readnone [[Y1_2]], i1 [[C]]) #[[ATTR6]] +; 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:%.*]]) #[[ATTR6]] { +; ATTRIBUTOR-NEXT: call void @test2(ptr noalias nocapture nofree readnone [[X2]]) #[[ATTR6]] +; 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:%.*]]) #[[ATTR6]] { +; ATTRIBUTOR-NEXT: call void @test3(ptr noalias nocapture nofree readnone [[Z3]], ptr noalias nocapture nofree readnone [[Y3]], ptr noalias nocapture nofree readnone [[X3]]) #[[ATTR6]] +; 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:%.*]]) #[[ATTR6]] { +; ATTRIBUTOR-NEXT: [[TMP1:%.*]] = call ptr @test4_2(ptr noalias nocapture nofree readnone [[X4_1]], ptr noalias nofree readnone [[X4_1]], ptr noalias nocapture nofree readnone [[X4_1]], i1 [[C]]) #[[ATTR6]] +; 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 returned [[Y4_2:%.*]], ptr nocapture nofree readnone [[Z4_2:%.*]], i1 [[C:%.*]]) #[[ATTR6]] { +; ATTRIBUTOR-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] +; ATTRIBUTOR: t: +; ATTRIBUTOR-NEXT: call void @test4_1(ptr noalias nocapture nofree readnone null, i1 [[C]]) #[[ATTR6]] +; 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:%.*]]) #[[ATTR7:[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:%.*]]) #[[ATTR7]] { +; 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:%.*]]) #[[ATTR7]] { +; 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 +; ATTRIBUTOR-LABEL: define void @test_volatile +; ATTRIBUTOR-SAME: (ptr nofree [[X:%.*]]) #[[ATTR8:[0-9]+]] { +; 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 +; ATTRIBUTOR-LABEL: define void @nocaptureLaunder +; ATTRIBUTOR-SAME: (ptr nocapture nofree [[P:%.*]]) #[[ATTR4]] { +; ATTRIBUTOR-NEXT: entry: +; ATTRIBUTOR-NEXT: [[B:%.*]] = call ptr @llvm.launder.invariant.group.p0(ptr [[P]]) #[[ATTR14]] +; 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:%.*]]) #[[ATTR4]] { +; ATTRIBUTOR-NEXT: [[B:%.*]] = call ptr @llvm.launder.invariant.group.p0(ptr [[P]]) #[[ATTR14]] +; 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(write) +; ATTRIBUTOR-LABEL: define void @nocaptureStrip +; ATTRIBUTOR-SAME: (ptr nocapture nofree writeonly [[P:%.*]]) #[[ATTR1]] { +; ATTRIBUTOR-NEXT: entry: +; ATTRIBUTOR-NEXT: [[B:%.*]] = call ptr @llvm.strip.invariant.group.p0(ptr [[P]]) #[[ATTR14]] +; 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]]) #[[ATTR14]] +; 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:%.*]]) #[[ATTR9:[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:%.*]]) #[[ATTR10:[0-9]+]] { +; ATTRIBUTOR-NEXT: call void [[F]](ptr [[P]]) #[[ATTR16:[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 +; RUN: opt -S -passes=attributor-light %s | FileCheck %s --check-prefixes=FNATTR target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" 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 noalias readnone @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,59 @@ ; 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 @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 readonly [[Y1_2:%.*]], ptr [[Z1_2:%.*]]) { +; ATTRIBUTOR-NEXT: call void (ptr, ptr, ...) @test1_1(ptr [[X1_2]], ptr nocapture readonly [[Y1_2]], ptr [[Z1_2]]) +; ATTRIBUTOR-NEXT: store i32 0, ptr @x, align 4 +; ATTRIBUTOR-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 returned [[P:%.*]]) #[[ATTR0:[0-9]+]] { +; ATTRIBUTOR-NEXT: store i32 0, ptr @x, align 4 +; ATTRIBUTOR-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]] ; %A = icmp ult ptr %p, %q ret i1 %A @@ -42,11 +62,17 @@ 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: memory(read) +; ATTRIBUTOR-LABEL: define {{[^@]+}}@test4_2 +; ATTRIBUTOR-SAME: (ptr nocapture readonly [[P:%.*]]) #[[ATTR2:[0-9]+]] { +; ATTRIBUTOR-NEXT: call void @test4_1(ptr nocapture readonly [[P]]) #[[ATTR2]] +; ATTRIBUTOR-NEXT: ret void ; call void @test4_1(ptr %p) ret void @@ -54,11 +80,17 @@ ; 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:%.*]]) #[[ATTR3:[0-9]+]] { +; ATTRIBUTOR-NEXT: store ptr [[Q]], ptr [[P]], align 8 +; ATTRIBUTOR-NEXT: ret void ; store ptr %q, ptr %p ret void @@ -68,11 +100,17 @@ ; 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 ; store ptr %q, ptr %p call void @test6_1() @@ -81,43 +119,67 @@ ; 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 ; 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 ; 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 returned [[P:%.*]]) #[[ATTR1]] { +; ATTRIBUTOR-NEXT: entry: +; ATTRIBUTOR-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 noalias nofree readnone [[P]]) #[[ATTR11:[0-9]+]] +; ATTRIBUTOR-NEXT: store i32 10, ptr [[CALL]], align 4 +; ATTRIBUTOR-NEXT: ret void ; entry: %call = call ptr @test8_1(ptr %p) @@ -128,11 +190,17 @@ 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> ) #[[ATTR12:[0-9]+]] +; ATTRIBUTOR-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 +208,17 @@ 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:%.*]]) #[[ATTR6:[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) #[[ATTR13:[0-9]+]] +; ATTRIBUTOR-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 +226,17 @@ 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: nounwind memory(argmem: read) +; ATTRIBUTOR-LABEL: define {{[^@]+}}@test11_2 +; ATTRIBUTOR-SAME: (<4 x ptr> [[PTRS:%.*]]) #[[ATTR7:[0-9]+]] { +; ATTRIBUTOR-NEXT: [[RES:%.*]] = call <4 x i32> @test11_1(<4 x ptr> [[PTRS]]) #[[ATTR2]] +; ATTRIBUTOR-NEXT: ret <4 x i32> [[RES]] ; %res = call <4 x i32> @test11_1(<4 x ptr> %ptrs) ret <4 x i32> %res @@ -164,22 +244,34 @@ 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:%.*]]) #[[ATTR8:[0-9]+]] { +; ATTRIBUTOR-NEXT: [[RES:%.*]] = call <4 x i32> @test12_1(<4 x ptr> [[PTRS]]) +; ATTRIBUTOR-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:%.*]]) #[[ATTR9:[0-9]+]] { +; ATTRIBUTOR-NEXT: [[LOAD:%.*]] = load volatile i32, ptr [[P]], align 4 +; ATTRIBUTOR-NEXT: ret i32 [[LOAD]] ; %load = load volatile i32, ptr %p ret i32 %load @@ -193,13 +285,21 @@ ; 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 [[ESCAPED_THEN_WRITTEN:%.*]]) { +; ATTRIBUTOR-NEXT: [[ADDR:%.*]] = alloca ptr, align 8 +; ATTRIBUTOR-NEXT: call void @escape_readnone_ptr(ptr [[ADDR]], ptr [[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 ; %addr = alloca ptr call void @escape_readnone_ptr(ptr %addr, ptr %escaped_then_written) @@ -209,13 +309,21 @@ } 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 [[ESCAPED_THEN_WRITTEN:%.*]]) { +; ATTRIBUTOR-NEXT: [[ADDR:%.*]] = alloca ptr, align 8 +; ATTRIBUTOR-NEXT: call void @escape_readonly_ptr(ptr [[ADDR]], ptr [[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 ; %addr = alloca ptr call void @escape_readonly_ptr(ptr %addr, ptr %escaped_then_written) @@ -225,10 +333,15 @@ } 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 [[P:%.*]], ptr nocapture nofree nonnull [[F:%.*]]) { +; ATTRIBUTOR-NEXT: call void [[F]](ptr nocapture readnone [[P]]) +; ATTRIBUTOR-NEXT: ret void ; call void %f(ptr nocapture readnone %p) ret void @@ -236,31 +349,47 @@ ; 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 [[P:%.*]], ptr nocapture nofree nonnull [[F:%.*]]) { +; ATTRIBUTOR-NEXT: call void [[F]](ptr readnone [[P]]) +; ATTRIBUTOR-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 readonly [[P:%.*]], ptr nocapture nofree nonnull readonly [[F:%.*]]) #[[ATTR2]] { +; ATTRIBUTOR-NEXT: call void [[F]](ptr readnone [[P]]) #[[ATTR2]] +; ATTRIBUTOR-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 [[P:%.*]], ptr nocapture nofree nonnull [[F:%.*]]) { +; ATTRIBUTOR-NEXT: call void [[F]](ptr nocapture readonly [[P]]) +; ATTRIBUTOR-NEXT: ret void ; call void %f(ptr nocapture readonly %p) ret void @@ -268,43 +397,71 @@ 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 [[P:%.*]], ptr nocapture nofree nonnull [[F:%.*]]) { +; ATTRIBUTOR-NEXT: call void [[F]](ptr readonly [[P]]) +; ATTRIBUTOR-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 readonly [[P:%.*]], ptr nocapture nofree nonnull readonly [[F:%.*]]) #[[ATTR2]] { +; ATTRIBUTOR-NEXT: call void [[F]](ptr readonly [[P]]) #[[ATTR2]] +; ATTRIBUTOR-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: mustprogress nofree norecurse nosync nounwind willreturn +; ATTRIBUTOR-LABEL: define {{[^@]+}}@alloca_recphi +; ATTRIBUTOR-SAME: () #[[ATTR10:[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 ; entry: %a = alloca [8 x i32] @@ -328,41 +485,63 @@ ; 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 [[P:%.*]]) { +; ATTRIBUTOR-NEXT: call void @readnone_param(ptr nocapture [[P]]) [ "deopt"() ] +; ATTRIBUTOR-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 [[P:%.*]]) { +; ATTRIBUTOR-NEXT: call void @readnone_param(ptr nocapture [[P]]) [ "unknown"() ] +; ATTRIBUTOR-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 [[P:%.*]]) { +; ATTRIBUTOR-NEXT: call void @readonly_param(ptr nocapture [[P]]) [ "deopt"() ] +; ATTRIBUTOR-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 [[P:%.*]]) { +; ATTRIBUTOR-NEXT: call void @readonly_param(ptr nocapture [[P]]) [ "unknown"() ] +; ATTRIBUTOR-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 noalias 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 noalias 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,29 @@ ; 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 +; FNATTRS: Function Attrs: nofree norecurse nosync nounwind memory(none) +; FNATTRS-LABEL: @willreturn_finite_loop( +; FNATTRS-NEXT: entry: +; FNATTRS-NEXT: br label [[LOOP:%.*]] +; FNATTRS: loop: +; FNATTRS-NEXT: [[I:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[I_INC:%.*]], [[LOOP]] ] +; FNATTRS-NEXT: [[I_INC]] = add nuw i32 [[I]], 1 +; FNATTRS-NEXT: [[C:%.*]] = icmp ne i32 [[I_INC]], 100 +; FNATTRS-NEXT: br i1 [[C]], label [[LOOP]], label [[END:%.*]] +; FNATTRS: end: +; FNATTRS-NEXT: ret void +; +; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) +; ATTRIBUTOR-LABEL: @willreturn_finite_loop( +; ATTRIBUTOR-NEXT: entry: +; ATTRIBUTOR-NEXT: br label [[LOOP:%.*]] +; ATTRIBUTOR: loop: +; ATTRIBUTOR-NEXT: [[I:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[I_INC:%.*]], [[LOOP]] ] +; ATTRIBUTOR-NEXT: [[I_INC]] = add nuw i32 [[I]], 1 +; ATTRIBUTOR-NEXT: [[C:%.*]] = icmp ne i32 [[I_INC]], 100 +; ATTRIBUTOR-NEXT: br i1 [[C]], label [[LOOP]], label [[END:%.*]] +; ATTRIBUTOR: end: +; ATTRIBUTOR-NEXT: ret void ; entry: br label %loop @@ -183,10 +241,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 +257,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 +275,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(write) +; ATTRIBUTOR-LABEL: define {{[^@]+}}@test_addressing +; ATTRIBUTOR-SAME: (ptr nocapture nofree writeonly [[P:%.*]]) #[[ATTR2]] { +; 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