/// Rewrites function signitures and updates the call graph.
ChangeStatus cleanupIR();
+ /// Identify internal functions that are effectively dead, thus not reachable
+ /// from a live entry point. The functions are added to ToBeDeletedFunctions.
+ void identifyDeadInternalFunctions();
+
/// Run `::update` on \p AA and track the dependences queried while doing so.
/// Also adjust the state if we know further updates are not necessary.
ChangeStatus updateAA(AbstractAttribute &AA);
return ManifestChange;
}
+void Attributor::identifyDeadInternalFunctions() {
+ // 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
+ // below fixpoint loop will identify and eliminate them.
+ SmallVector<Function *, 8> InternalFns;
+ for (Function *F : Functions)
+ if (F->hasLocalLinkage())
+ InternalFns.push_back(F);
+
+ SmallPtrSet<Function *, 8> LiveInternalFns;
+ bool FoundLiveInternal = true;
+ while (FoundLiveInternal) {
+ FoundLiveInternal = false;
+ for (unsigned u = 0, e = InternalFns.size(); u < e; ++u) {
+ Function *F = InternalFns[u];
+ if (!F)
+ continue;
+
+ bool AllCallSitesKnown;
+ if (checkForAllCallSites(
+ [&](AbstractCallSite ACS) {
+ Function *Callee = ACS.getInstruction()->getFunction();
+ return ToBeDeletedFunctions.count(Callee) ||
+ (Functions.count(Callee) && Callee->hasLocalLinkage() &&
+ !LiveInternalFns.count(Callee));
+ },
+ *F, true, nullptr, AllCallSitesKnown)) {
+ continue;
+ }
+
+ LiveInternalFns.insert(F);
+ InternalFns[u] = nullptr;
+ FoundLiveInternal = true;
+ }
+ }
+
+ for (unsigned u = 0, e = InternalFns.size(); u < e; ++u)
+ if (Function *F = InternalFns[u])
+ ToBeDeletedFunctions.insert(F);
+}
+
ChangeStatus Attributor::cleanupIR() {
TimeTraceScope TimeScope("Attributor::cleanupIR");
// Delete stuff at the end to avoid invalid references and a nice order.
DetatchDeadBlocks(ToBeDeletedBBs, nullptr);
}
- // 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
- // below fixpoint loop will identify and eliminate them.
- SmallVector<Function *, 8> InternalFns;
- for (Function *F : Functions)
- if (F->hasLocalLinkage())
- InternalFns.push_back(F);
-
- bool FoundDeadFn = true;
- while (FoundDeadFn) {
- FoundDeadFn = false;
- for (unsigned u = 0, e = InternalFns.size(); u < e; ++u) {
- Function *F = InternalFns[u];
- if (!F)
- continue;
-
- bool AllCallSitesKnown;
- if (!checkForAllCallSites(
- [this](AbstractCallSite ACS) {
- return ToBeDeletedFunctions.count(
- ACS.getInstruction()->getFunction());
- },
- *F, true, nullptr, AllCallSitesKnown))
- continue;
-
- ToBeDeletedFunctions.insert(F);
- InternalFns[u] = nullptr;
- FoundDeadFn = true;
- }
- }
+ identifyDeadInternalFunctions();
// Rewrite the functions as requested during manifest.
ChangeStatus ManifestChange = rewriteFunctionSignatures(CGModifiedFunctions);
for (Function *Fn : CGModifiedFunctions)
- CGUpdater.reanalyzeFunction(*Fn);
+ if (!ToBeDeletedFunctions.count(Fn))
+ CGUpdater.reanalyzeFunction(*Fn);
for (Function *Fn : ToBeDeletedFunctions) {
if (!Functions.count(Fn))
; 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
define internal fastcc i32 @term_SharingList(i32* %Term, i32* %List) nounwind {
-; IS__TUNIT____: Function Attrs: nounwind
-; IS__TUNIT____-LABEL: define {{[^@]+}}@term_SharingList
-; IS__TUNIT____-SAME: (i32* [[TERM:%.*]], i32* [[LIST:%.*]]) [[ATTR0:#.*]] {
-; IS__TUNIT____-NEXT: entry:
-; IS__TUNIT____-NEXT: br i1 false, label [[BB:%.*]], label [[BB5:%.*]]
-; IS__TUNIT____: bb:
-; IS__TUNIT____-NEXT: [[TMP0:%.*]] = call fastcc i32 @term_SharingList(i32* null, i32* [[LIST]]) [[ATTR0]]
-; IS__TUNIT____-NEXT: unreachable
-; IS__TUNIT____: bb5:
-; IS__TUNIT____-NEXT: ret i32 0
-;
; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind readnone willreturn
; IS__CGSCC____-LABEL: define {{[^@]+}}@term_SharingList
; IS__CGSCC____-SAME: () [[ATTR0:#.*]] {
define i32 @term_Sharing(i32* %Term) nounwind {
; IS__TUNIT____: Function Attrs: nofree nosync nounwind readnone willreturn
; IS__TUNIT____-LABEL: define {{[^@]+}}@term_Sharing
-; IS__TUNIT____-SAME: (i32* nocapture nofree readnone [[TERM:%.*]]) [[ATTR1:#.*]] {
+; IS__TUNIT____-SAME: (i32* nocapture nofree readnone [[TERM:%.*]]) [[ATTR0:#.*]] {
; IS__TUNIT____-NEXT: entry:
; IS__TUNIT____-NEXT: br i1 false, label [[BB_I:%.*]], label [[BB14:%.*]]
; IS__TUNIT____: bb.i:
; RUN: 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
define internal void @foo(i32 %X) {
-; CHECK-LABEL: define {{[^@]+}}@foo
-; CHECK-SAME: (i32 [[X:%.*]]) {
-; CHECK-NEXT: call void @foo(i32 [[X]])
-; CHECK-NEXT: ret void
+; IS__CGSCC____-LABEL: define {{[^@]+}}@foo
+; IS__CGSCC____-SAME: (i32 [[X:%.*]]) {
+; IS__CGSCC____-NEXT: call void @foo(i32 [[X]])
+; IS__CGSCC____-NEXT: ret void
;
call void @foo( i32 %X )
ret void
}
+define void @bar() {
+; IS__TUNIT____: Function Attrs: nofree nosync nounwind readnone willreturn
+; IS__TUNIT____-LABEL: define {{[^@]+}}@bar
+; IS__TUNIT____-SAME: () [[ATTR0:#.*]] {
+; IS__TUNIT____-NEXT: ret void
+;
+; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind readnone willreturn
+; IS__CGSCC____-LABEL: define {{[^@]+}}@bar
+; IS__CGSCC____-SAME: () [[ATTR0:#.*]] {
+; IS__CGSCC____-NEXT: ret void
+;
+ ret void
+}
@a2 = common global i8 0, align 16
define internal i8* @f1(i8* readnone %0) local_unnamed_addr #0 {
-; CHECK-LABEL: define {{[^@]+}}@f1
-; CHECK-SAME: (i8* readnone [[TMP0:%.*]]) local_unnamed_addr {
-; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i8* [[TMP0]], null
-; CHECK-NEXT: br i1 [[TMP2]], label [[TMP3:%.*]], label [[TMP5:%.*]]
-; CHECK: 3:
-; CHECK-NEXT: [[TMP4:%.*]] = tail call i8* @f2(i8* nonnull @a1)
-; CHECK-NEXT: br label [[TMP5]]
-; CHECK: 5:
-; CHECK-NEXT: [[TMP6:%.*]] = phi i8* [ [[TMP4]], [[TMP3]] ], [ [[TMP0]], [[TMP1:%.*]] ]
-; CHECK-NEXT: ret i8* [[TMP6]]
+; IS__CGSCC____-LABEL: define {{[^@]+}}@f1
+; IS__CGSCC____-SAME: (i8* readnone [[TMP0:%.*]]) local_unnamed_addr {
+; IS__CGSCC____-NEXT: [[TMP2:%.*]] = icmp eq i8* [[TMP0]], null
+; IS__CGSCC____-NEXT: br i1 [[TMP2]], label [[TMP3:%.*]], label [[TMP5:%.*]]
+; IS__CGSCC____: 3:
+; IS__CGSCC____-NEXT: [[TMP4:%.*]] = tail call i8* @f2(i8* nonnull @a1)
+; IS__CGSCC____-NEXT: br label [[TMP5]]
+; IS__CGSCC____: 5:
+; IS__CGSCC____-NEXT: [[TMP6:%.*]] = phi i8* [ [[TMP4]], [[TMP3]] ], [ [[TMP0]], [[TMP1:%.*]] ]
+; IS__CGSCC____-NEXT: ret i8* [[TMP6]]
;
%2 = icmp eq i8* %0, null
br i1 %2, label %3, label %5
}
define internal i8* @f2(i8* readnone %0) local_unnamed_addr #0 {
-; CHECK-LABEL: define {{[^@]+}}@f2
-; CHECK-SAME: (i8* readnone [[TMP0:%.*]]) local_unnamed_addr {
-; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i8* [[TMP0]], null
-; CHECK-NEXT: br i1 [[TMP2]], label [[TMP5:%.*]], label [[TMP3:%.*]]
-; CHECK: 3:
-; CHECK-NEXT: [[TMP4:%.*]] = tail call i8* @f1(i8* nonnull [[TMP0]])
-; CHECK-NEXT: br label [[TMP7:%.*]]
-; CHECK: 5:
-; CHECK-NEXT: [[TMP6:%.*]] = tail call i8* @f3(i8* nonnull @a2)
-; CHECK-NEXT: br label [[TMP7]]
-; CHECK: 7:
-; CHECK-NEXT: [[TMP8:%.*]] = phi i8* [ [[TMP4]], [[TMP3]] ], [ [[TMP6]], [[TMP5]] ]
-; CHECK-NEXT: ret i8* [[TMP8]]
+; IS__CGSCC____-LABEL: define {{[^@]+}}@f2
+; IS__CGSCC____-SAME: (i8* readnone [[TMP0:%.*]]) local_unnamed_addr {
+; IS__CGSCC____-NEXT: [[TMP2:%.*]] = icmp eq i8* [[TMP0]], null
+; IS__CGSCC____-NEXT: br i1 [[TMP2]], label [[TMP5:%.*]], label [[TMP3:%.*]]
+; IS__CGSCC____: 3:
+; IS__CGSCC____-NEXT: [[TMP4:%.*]] = tail call i8* @f1(i8* nonnull [[TMP0]])
+; IS__CGSCC____-NEXT: br label [[TMP7:%.*]]
+; IS__CGSCC____: 5:
+; IS__CGSCC____-NEXT: [[TMP6:%.*]] = tail call i8* @f3(i8* nonnull @a2)
+; IS__CGSCC____-NEXT: br label [[TMP7]]
+; IS__CGSCC____: 7:
+; IS__CGSCC____-NEXT: [[TMP8:%.*]] = phi i8* [ [[TMP4]], [[TMP3]] ], [ [[TMP6]], [[TMP5]] ]
+; IS__CGSCC____-NEXT: ret i8* [[TMP8]]
;
%2 = icmp eq i8* %0, null
br i1 %2, label %5, label %3
}
define internal i8* @f3(i8* readnone %0) local_unnamed_addr #0 {
-; CHECK-LABEL: define {{[^@]+}}@f3
-; CHECK-SAME: (i8* readnone [[TMP0:%.*]]) local_unnamed_addr {
-; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i8* [[TMP0]], null
-; CHECK-NEXT: br i1 [[TMP2]], label [[TMP3:%.*]], label [[TMP5:%.*]]
-; CHECK: 3:
-; CHECK-NEXT: [[TMP4:%.*]] = tail call i8* @f1(i8* nonnull @a2)
-; CHECK-NEXT: br label [[TMP5]]
-; CHECK: 5:
-; CHECK-NEXT: [[TMP6:%.*]] = phi i8* [ [[TMP4]], [[TMP3]] ], [ @a1, [[TMP1:%.*]] ]
-; CHECK-NEXT: ret i8* [[TMP6]]
+; IS__CGSCC____-LABEL: define {{[^@]+}}@f3
+; IS__CGSCC____-SAME: (i8* readnone [[TMP0:%.*]]) local_unnamed_addr {
+; IS__CGSCC____-NEXT: [[TMP2:%.*]] = icmp eq i8* [[TMP0]], null
+; IS__CGSCC____-NEXT: br i1 [[TMP2]], label [[TMP3:%.*]], label [[TMP5:%.*]]
+; IS__CGSCC____: 3:
+; IS__CGSCC____-NEXT: [[TMP4:%.*]] = tail call i8* @f1(i8* nonnull @a2)
+; IS__CGSCC____-NEXT: br label [[TMP5]]
+; IS__CGSCC____: 5:
+; IS__CGSCC____-NEXT: [[TMP6:%.*]] = phi i8* [ [[TMP4]], [[TMP3]] ], [ @a1, [[TMP1:%.*]] ]
+; IS__CGSCC____-NEXT: ret i8* [[TMP6]]
;
%2 = icmp eq i8* %0, null
br i1 %2, label %3, label %5