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 @@ -1015,11 +1015,13 @@ /// the abstract attributes. /// \param CGUpdater Helper to update an underlying call graph. /// \param Allowed If not null, a set limiting the attribute opportunities. + /// \param DeleteFns Whether to delete functions Attributor(SetVector &Functions, InformationCache &InfoCache, CallGraphUpdater &CGUpdater, - DenseSet *Allowed = nullptr) + DenseSet *Allowed = nullptr, bool DeleteFns = true) : Allocator(InfoCache.Allocator), Functions(Functions), - InfoCache(InfoCache), CGUpdater(CGUpdater), Allowed(Allowed) {} + InfoCache(InfoCache), CGUpdater(CGUpdater), Allowed(Allowed), + DeleteFns(DeleteFns) {} ~Attributor(); @@ -1330,7 +1332,10 @@ void deleteAfterManifest(BasicBlock &BB) { ToBeDeletedBlocks.insert(&BB); } /// Record that \p F is deleted after information was manifested. - void deleteAfterManifest(Function &F) { ToBeDeletedFunctions.insert(&F); } + void deleteAfterManifest(Function &F) { + if (DeleteFns) + ToBeDeletedFunctions.insert(&F); + } /// If \p V is assumed to be a constant, return it, if it is unclear yet, /// return None, otherwise return `nullptr`. @@ -1656,6 +1661,9 @@ /// If not null, a set limiting the attribute opportunities. const DenseSet *Allowed; + /// Whether to delete functions. + const bool DeleteFns; + /// A set to remember the functions we already assume to be live and visited. DenseSet VisitedFunctions; 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 @@ -1175,6 +1175,10 @@ } void Attributor::identifyDeadInternalFunctions() { + // Early exit if we don't intend to delete functions. + if (!DeleteFns) + return; + // Identify dead internal functions and delete them. This happens outside // the other fixpoint analysis as we might treat potentially dead functions // as live to lower the number of iterations. If they happen to be dead, the @@ -2288,7 +2292,8 @@ static bool runAttributorOnFunctions(InformationCache &InfoCache, SetVector &Functions, AnalysisGetter &AG, - CallGraphUpdater &CGUpdater) { + CallGraphUpdater &CGUpdater, + bool DeleteFns) { if (Functions.empty()) return false; @@ -2297,7 +2302,8 @@ // Create an Attributor and initially empty information cache that is filled // while we identify default attribute opportunities. - Attributor A(Functions, InfoCache, CGUpdater); + Attributor A(Functions, InfoCache, CGUpdater, /* Allowed */ nullptr, + DeleteFns); // Create shallow wrappers for all functions that are not IPO amendable if (AllowShallowWrappers) @@ -2400,7 +2406,8 @@ CallGraphUpdater CGUpdater; BumpPtrAllocator Allocator; InformationCache InfoCache(M, AG, Allocator, /* CGSCC */ nullptr); - if (runAttributorOnFunctions(InfoCache, Functions, AG, CGUpdater)) { + if (runAttributorOnFunctions(InfoCache, Functions, AG, CGUpdater, + /* DeleteFns */ true)) { // FIXME: Think about passes we will preserve and add them here. return PreservedAnalyses::none(); } @@ -2427,7 +2434,8 @@ CGUpdater.initialize(CG, C, AM, UR); BumpPtrAllocator Allocator; InformationCache InfoCache(M, AG, Allocator, /* CGSCC */ &Functions); - if (runAttributorOnFunctions(InfoCache, Functions, AG, CGUpdater)) { + if (runAttributorOnFunctions(InfoCache, Functions, AG, CGUpdater, + /* DeleteFns */ false)) { // FIXME: Think about passes we will preserve and add them here. PreservedAnalyses PA; PA.preserve(); @@ -2502,7 +2510,8 @@ CallGraphUpdater CGUpdater; BumpPtrAllocator Allocator; InformationCache InfoCache(M, AG, Allocator, /* CGSCC */ nullptr); - return runAttributorOnFunctions(InfoCache, Functions, AG, CGUpdater); + return runAttributorOnFunctions(InfoCache, Functions, AG, CGUpdater, + /* DeleteFns*/ true); } void getAnalysisUsage(AnalysisUsage &AU) const override { @@ -2538,7 +2547,8 @@ Module &M = *Functions.back()->getParent(); BumpPtrAllocator Allocator; InformationCache InfoCache(M, AG, Allocator, /* CGSCC */ &Functions); - return runAttributorOnFunctions(InfoCache, Functions, AG, CGUpdater); + return runAttributorOnFunctions(InfoCache, Functions, AG, CGUpdater, + /* DeleteFns */ false); } void getAnalysisUsage(AnalysisUsage &AU) const override { diff --git a/llvm/test/Transforms/Attributor/IPConstantProp/dangling-block-address.ll b/llvm/test/Transforms/Attributor/IPConstantProp/dangling-block-address.ll --- a/llvm/test/Transforms/Attributor/IPConstantProp/dangling-block-address.ll +++ b/llvm/test/Transforms/Attributor/IPConstantProp/dangling-block-address.ll @@ -8,7 +8,7 @@ ; IPSCCP should prove that the blocks are dead and delete them, and ; properly handle the dangling blockaddress constants. -; NOT_CGSCC_OPM: @bar.l = internal constant [2 x i8*] [i8* inttoptr (i32 1 to i8*), i8* inttoptr (i32 1 to i8*)] +; NOT_CGSCC_OPM: @bar.l = internal constant [2 x i8*] [i8* blockaddress(@bar, %lab0), i8* blockaddress(@bar, %end)] ; IS__CGSCC_OPM: @bar.l = internal constant [2 x i8*] [i8* blockaddress(@bar, %lab0), i8* blockaddress(@bar, %end)] @code = global [5 x i32] [i32 0, i32 0, i32 0, i32 0, i32 1], align 4 ; <[5 x i32]*> [#uses=0] diff --git a/llvm/test/Transforms/Attributor/liveness.ll b/llvm/test/Transforms/Attributor/liveness.ll --- a/llvm/test/Transforms/Attributor/liveness.ll +++ b/llvm/test/Transforms/Attributor/liveness.ll @@ -5,7 +5,7 @@ ; opt -attributor-cgscc -enable-new-pm=0 -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_TUNIT_NPM,NOT_TUNIT_OPM,NOT_CGSCC_NPM,IS__CGSCC____,IS________OPM,IS__CGSCC_OPM ; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_TUNIT_NPM,NOT_TUNIT_OPM,NOT_CGSCC_OPM,IS__CGSCC____,IS________NPM,IS__CGSCC_NPM -; NOT_CGSCC_OPM: @dead_with_blockaddress_users.l = constant [2 x i8*] [i8* inttoptr (i32 1 to i8*), i8* inttoptr (i32 1 to i8*)] +; NOT_CGSCC_OPM: @dead_with_blockaddress_users.l = constant [2 x i8*] [i8* blockaddress(@dead_with_blockaddress_users, %lab0), i8* blockaddress(@dead_with_blockaddress_users, %end)] ; IS__CGSCC_OPM: @dead_with_blockaddress_users.l = constant [2 x i8*] [i8* blockaddress(@dead_with_blockaddress_users, %lab0), i8* blockaddress(@dead_with_blockaddress_users, %end)] @dead_with_blockaddress_users.l = constant [2 x i8*] [i8* blockaddress(@dead_with_blockaddress_users, %lab0), i8* blockaddress(@dead_with_blockaddress_users, %end)] @@ -2379,10 +2379,9 @@ declare void @use_i32p(i32*) ; Allow blockaddress users -; NOT_CGSCC_OPM-NOT: @dead_with_blockaddress_users define internal void @dead_with_blockaddress_users(i32* nocapture %pc) nounwind readonly { ; IS__CGSCC_OPM-LABEL: define {{[^@]+}}@dead_with_blockaddress_users -; IS__CGSCC_OPM-SAME: (i32* nocapture [[PC:%.*]]) +; IS__CGSCC_OPM-SAME: (i32* noalias nocapture nofree nonnull readonly align 536870912 dereferenceable(4294967295) [[PC:%.*]]) ; IS__CGSCC_OPM-NEXT: entry: ; IS__CGSCC_OPM-NEXT: br label [[INDIRECTGOTO:%.*]] ; IS__CGSCC_OPM: lab0: diff --git a/llvm/test/Transforms/Attributor/nodelete.ll b/llvm/test/Transforms/Attributor/nodelete.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/Attributor/nodelete.ll @@ -0,0 +1,81 @@ +; RUN: opt -attributor-cgscc -S < %s | FileCheck %s + +%"a" = type { i64 } +%"b" = type { i8 } + +define hidden i64 @f1() align 2 { +entry: + %ref.tmp = alloca %"a", align 8 + %call2 = call i64 @f2(%"a"* %ref.tmp) + ret i64 %call2 +} + +define internal i64 @f2(%"a"* %this) align 2 { +entry: + %this.addr = alloca %"a"*, align 8 + store %"a"* %this, %"a"** %this.addr, align 8 + %this1 = load %"a"*, %"a"** %this.addr, align 8 + %0 = bitcast %"a"* %this1 to %"b"* + call void @f3(%"b"* %0) + ret i64 undef +} + +define internal void @f3(%"b"* %this) align 2 { +entry: + %this.addr = alloca %"b"*, align 8 + store %"b"* %this, %"b"** %this.addr, align 8 + %this1 = load %"b"*, %"b"** %this.addr, align 8 + %call = call i1 @f4(%"b"* %this1) + ret void +} + +define internal i1 @f4(%"b"* %this) align 2 { +entry: + %this.addr = alloca %"b"*, align 8 + store %"b"* %this, %"b"** %this.addr, align 8 + %this1 = load %"b"*, %"b"** %this.addr, align 8 + %call = call %"a"* @f5(%"b"* %this1) + ret i1 undef +} + +define internal %"a"* @f5(%"b"* %this) align 2 { +entry: + %this.addr = alloca %"b"*, align 8 + store %"b"* %this, %"b"** %this.addr, align 8 + %this1 = load %"b"*, %"b"** %this.addr, align 8 + %0 = bitcast %"b"* %this1 to %"a"* + ret %"a"* %0 +} + +; CHECK: define hidden i64 @f1() +; CHECK-NEXT: entry: +; CHECK-NEXT: ret i64 undef +; CHECK-NEXT: } + +; CHECK: define internal i64 @f2(%a* noalias nocapture nofree noundef nonnull readnone align 8 dereferenceable(8) %this) +; CHECK-NEXT: entry: +; CHECK-NEXT: %this.addr = alloca %a*, align 8 +; CHECK-NEXT: store %a* %this, %a** %this.addr, align 8 +; CHECK-NEXT: ret i64 undef +; CHECK-NEXT: } + +; CHECK: define internal void @f3(%b* noalias nocapture nofree readnone %this) +; CHECK-NEXT: entry: +; CHECK-NEXT: %this.addr = alloca %b*, align 8 +; CHECK-NEXT: store %b* %this, %b** %this.addr, align 8 +; CHECK-NEXT: ret void +; CHECK-NEXT: } + +; CHECK: define internal i1 @f4(%b* noalias nocapture nofree readnone %this) +; CHECK-NEXT: entry: +; CHECK-NEXT: %this.addr = alloca %b*, align 8 +; CHECK-NEXT: store %b* %this, %b** %this.addr, align 8 +; CHECK-NEXT: ret i1 undef +; CHECK-NEXT: } + +; CHECK: define internal noalias nonnull %a* @f5(%b* noalias nocapture nofree readnone %this) +; CHECK-NEXT: entry: +; CHECK-NEXT: %this.addr = alloca %b*, align 8 +; CHECK-NEXT: store %b* %this, %b** %this.addr, align 8 +; CHECK-NEXT: ret %a* undef +; CHECK-NEXT: } \ No newline at end of file diff --git a/llvm/test/Transforms/OpenMP/parallel_deletion_cg_update.ll b/llvm/test/Transforms/OpenMP/parallel_deletion_cg_update.ll --- a/llvm/test/Transforms/OpenMP/parallel_deletion_cg_update.ll +++ b/llvm/test/Transforms/OpenMP/parallel_deletion_cg_update.ll @@ -2,11 +2,14 @@ ; CHECK: Call graph node <><<{{.*}}>> #uses=0 ; CHECK: CS calls function 'dead_fork_call' -; CHECK: CS calls function 'd' +; CHECK: CS calls function '.omp_outlined..0' ; CHECK: CS calls function '__kmpc_fork_call' ; CHECK: CS calls function 'live_fork_call' ; CHECK: CS calls function '.omp_outlined..1' +; CHECK: CS calls function 'd' ; +; CHECK: Call graph node for function: '.omp_outlined..0'<<{{.*}}>> #uses=1 +; ; CHECK: Call graph node for function: '.omp_outlined..1'<<{{.*}}>> #uses=3 ; CHECK: CS<{{.*}}> calls function 'd' ;