Skip to content

Commit

Permalink
[Inline] Attempt to delete any discardable if unused functions
Browse files Browse the repository at this point in the history
  • Loading branch information
aeubanks committed Jan 12, 2022
1 parent d202c76 commit d5be48c
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 17 deletions.
52 changes: 35 additions & 17 deletions llvm/lib/Transforms/IPO/Inliner.cpp
Expand Up @@ -823,6 +823,10 @@ PreservedAnalyses InlinerPass::run(LazyCallGraph::SCC &InitialC,
// defer deleting these to make it easier to handle the call graph updates.
SmallVector<Function *, 4> DeadFunctions;

// Track potentially dead non-local functions with comdats to see if they can
// be deleted as a batch after inlining.
SmallVector<Function *, 4> DeadFunctionsInComdats;

// Loop forward over all of the calls.
while (!Calls->empty()) {
// We expect the calls to typically be batched with sequences of calls that
Expand Down Expand Up @@ -935,28 +939,33 @@ PreservedAnalyses InlinerPass::run(LazyCallGraph::SCC &InitialC,
// Merge the attributes based on the inlining.
AttributeFuncs::mergeAttributesForInlining(F, Callee);

// For local functions, check whether this makes the callee trivially
// dead. In that case, we can drop the body of the function eagerly
// which may reduce the number of callers of other functions to one,
// changing inline cost thresholds.
// For local functions or discardable functions without comdats, check
// whether this makes the callee trivially dead. In that case, we can drop
// the body of the function eagerly which may reduce the number of callers
// of other functions to one, changing inline cost thresholds. Non-local
// discardable functions with comdats are checked later on.
bool CalleeWasDeleted = false;
if (Callee.hasLocalLinkage()) {
if (Callee.isDiscardableIfUnused()) {
// To check this we also need to nuke any dead constant uses (perhaps
// made dead by this operation on other functions).
Callee.removeDeadConstantUsers();
if (Callee.use_empty() && !CG.isLibFunction(Callee)) {
Calls->erase_if([&](const std::pair<CallBase *, int> &Call) {
return Call.first->getCaller() == &Callee;
});
// Clear the body and queue the function itself for deletion when we
// finish inlining and call graph updates.
// Note that after this point, it is an error to do anything other
// than use the callee's address or delete it.
Callee.dropAllReferences();
assert(!is_contained(DeadFunctions, &Callee) &&
"Cannot put cause a function to become dead twice!");
DeadFunctions.push_back(&Callee);
CalleeWasDeleted = true;
if (Callee.hasLocalLinkage() || !Callee.hasComdat()) {
Calls->erase_if([&](const std::pair<CallBase *, int> &Call) {
return Call.first->getCaller() == &Callee;
});
// Clear the body and queue the function itself for deletion when we
// finish inlining and call graph updates.
// Note that after this point, it is an error to do anything other
// than use the callee's address or delete it.
Callee.dropAllReferences();
assert(!is_contained(DeadFunctions, &Callee) &&
"Cannot put cause a function to become dead twice!");
DeadFunctions.push_back(&Callee);
CalleeWasDeleted = true;
} else {
DeadFunctionsInComdats.push_back(&Callee);
}
}
}
if (CalleeWasDeleted)
Expand Down Expand Up @@ -1019,6 +1028,15 @@ PreservedAnalyses InlinerPass::run(LazyCallGraph::SCC &InitialC,
FAM.invalidate(F, PreservedAnalyses::none());
}

// We must ensure that we only delete functions with comdats if every function
// in the comdat is going to be deleted.
if (!DeadFunctionsInComdats.empty()) {
filterDeadComdatFunctions(DeadFunctionsInComdats);
for (auto *Callee : DeadFunctionsInComdats)
Callee->dropAllReferences();
DeadFunctions.append(DeadFunctionsInComdats);
}

// Now that we've finished inlining all of the calls across this SCC, delete
// all of the trivially dead functions, updating the call graph and the CGSCC
// pass manager in the process.
Expand Down
39 changes: 39 additions & 0 deletions llvm/test/Transforms/Inline/delete-function-with-metadata-use.ll
@@ -0,0 +1,39 @@
; RUN: opt -passes=inline < %s -S | FileCheck %s

; CHECK: define {{.*}}@f1
; CHECK-NOT: define

%a = type { i8*, i8* }

$f3 = comdat any

define linkonce_odr void @f1() {
call void @f2(void ()* @f3)
ret void
}

define linkonce_odr void @f2(void ()* %__f) {
call void @llvm.dbg.value(metadata void ()* %__f, metadata !2, metadata !DIExpression()), !dbg !10
call void %__f()
ret void
}

define linkonce_odr void @f3() comdat {
ret void
}

declare void @llvm.dbg.value(metadata, metadata, metadata)

!llvm.module.flags = !{!0, !1}

!0 = !{i32 7, !"Dwarf Version", i32 4}
!1 = !{i32 2, !"Debug Info Version", i32 3}
!2 = !DILocalVariable(name: "__f", arg: 3, scope: !3, file: !4, line: 3814, type: !9)
!3 = distinct !DISubprogram(scope: !5, file: !4, line: 3814, type: !6, scopeLine: 3815, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !8, templateParams: !7, retainedNodes: !7)
!4 = !DIFile(filename: "a", directory: "")
!5 = !DINamespace(name: "std", scope: null)
!6 = !DISubroutineType(types: !7)
!7 = !{}
!8 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !4, isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !7, retainedTypes: !7, globals: !7, imports: !7, splitDebugInlining: false, nameTableKind: None)
!9 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !6, size: 64)
!10 = !DILocation(line: 0, scope: !3)
63 changes: 63 additions & 0 deletions llvm/test/Transforms/Inline/delete-unused-function.ll
@@ -0,0 +1,63 @@
; RUN: opt -passes=inline < %s -S | FileCheck %s

; CHECK: define {{.*}}@caller
; CHECK: define {{.*}}@f1
; CHECK-NOT: define {{.*}}@f2
; CHECK-NOT: define {{.*}}@f3
; CHECK-NOT: define {{.*}}@f4
; CHECK-NOT: define {{.*}}@f5
; CHECK: define {{.*}}@f6
; CHECK-NOT: define {{.*}}@f7
; CHECK-NOT: define {{.*}}@f8

$c1 = comdat any
$c2 = comdat any
$c3 = comdat any

define void @caller() {
call void @f1()
call void @f2()
call void @f3()
call void @f4()
call void @f5()
call void @f6()
call void @f7()
call void @f8()
ret void
}

define void @f1() {
ret void
}

define internal void @f2() {
ret void
}

define private void @f3() {
ret void
}

define linkonce_odr void @f4() {
ret void
}

define linkonce_odr void @f5() comdat($c1) {
ret void
}

define linkonce_odr void @f6() comdat($c2) {
ret void
}

define linkonce_odr void @g() comdat($c2) {
ret void
}

define linkonce_odr void @f7() comdat($c3) {
ret void
}

define linkonce_odr void @f8() comdat($c3) {
ret void
}

0 comments on commit d5be48c

Please sign in to comment.