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,100 @@ 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, + &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 +3923,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 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 nocapture nofree readnone [[X1_1]], ptr 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 nocapture nofree readnone [[X1_2]], ptr 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 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 nocapture nofree readnone [[Z3]], ptr nocapture nofree readnone [[Y3]], ptr 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 nocapture nofree readnone [[X4_1]], ptr nofree readnone [[X4_1]], ptr 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 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" @@ -11,771 +12,3 @@ %ret = call ptr @ret_nonnull() ret ptr %ret } - -; Return a pointer trivially nonnull (argument attribute) -define ptr @test2(ptr nonnull %p) { -; FNATTR: define nonnull ptr @test2 - ret ptr %p -} - -; Given an SCC where one of the functions can not be marked nonnull, -; can we still mark the other one which is trivially nonnull -define ptr @scc_binder(i1 %c) { -; FNATTR: define ptr @scc_binder - br i1 %c, label %rec, label %end -rec: - call ptr @test3(i1 %c) - br label %end -end: - ret ptr null -} - -define ptr @test3(i1 %c) { -; FNATTR: define nonnull ptr @test3 - call ptr @scc_binder(i1 %c) - %ret = call ptr @ret_nonnull() - ret ptr %ret -} - -; Given a mutual recursive set of functions, we can mark them -; nonnull if neither can ever return null. (In this case, they -; just never return period.) -define ptr @test4_helper() { -; FNATTR: define noalias nonnull ptr @test4_helper - %ret = call ptr @test4() - ret ptr %ret -} - -define ptr @test4() { -; FNATTR: define noalias nonnull ptr @test4 - %ret = call ptr @test4_helper() - ret ptr %ret -} - -; Given a mutual recursive set of functions which *can* return null -; make sure we haven't marked them as nonnull. -define ptr @test5_helper(i1 %c) { -; FNATTR: define noalias ptr @test5_helper - br i1 %c, label %rec, label %end -rec: - %ret = call ptr @test5(i1 %c) - br label %end -end: - ret ptr null -} - -define ptr @test5(i1 %c) { -; FNATTR: define noalias ptr @test5 - %ret = call ptr @test5_helper(i1 %c) - ret ptr %ret -} - -; Local analysis, but going through a self recursive phi -define ptr @test6a() { -entry: - %ret = call ptr @ret_nonnull() - br label %loop -loop: - %phi = phi ptr [%ret, %entry], [%phi, %loop] - br i1 undef, label %loop, label %exit -exit: - ret ptr %phi -} - -define ptr @test6b(i1 %c) { -entry: - %ret = call ptr @ret_nonnull() - br label %loop -loop: - %phi = phi ptr [%ret, %entry], [%phi, %loop] - br i1 %c, label %loop, label %exit -exit: - ret ptr %phi -} - -; FNATTR: define ptr @test7 -define ptr @test7(ptr %a) { - ret ptr %a -} - -; FNATTR: define nonnull ptr @test8 -define ptr @test8(ptr %a) { - %b = getelementptr inbounds i8, ptr %a, i64 1 - ret ptr %b -} - -; FNATTR: define ptr @test9 -define ptr @test9(ptr %a, i64 %n) { - %b = getelementptr inbounds i8, ptr %a, i64 %n - ret ptr %b -} - -declare void @llvm.assume(i1) -; FNATTR: define ptr @test10 -; FIXME: missing nonnull -define ptr @test10(ptr %a, i64 %n) { - %cmp = icmp ne i64 %n, 0 - call void @llvm.assume(i1 %cmp) - %b = getelementptr inbounds i8, ptr %a, i64 %n - ret ptr %b -} - -; TEST 11 -; char* test11(char *p) { -; return p? p: nonnull(); -; } -; FNATTR: define ptr @test11 -; FIXME: missing nonnull -define ptr @test11(ptr) local_unnamed_addr { - %2 = icmp eq ptr %0, null - br i1 %2, label %3, label %5 - -;