diff options
author | Arthur Eubanks <aeubanks@google.com> | 2022-01-07 09:47:55 -0800 |
---|---|---|
committer | Arthur Eubanks <aeubanks@google.com> | 2022-01-12 08:36:04 -0800 |
commit | d5be48c66d3e5e8be21805c3a33dc67a20e258be (patch) | |
tree | d523fdf1b8f2f047838146d4622206a30949983f | |
parent | d202c76441e114f933057492f4bf15aa0d444867 (diff) |
[Inline] Attempt to delete any discardable if unused functions
Previously we limited ourselves to only internal/private functions. We
can also delete linkonce_odr functions.
Minor compile time wins:
https://llvm-compile-time-tracker.com/compare.php?from=d51e3474e060cb0e90dc2e2487f778b0d3e6a8de&to=bccffe3f8d5dd4dda884c9ac1f93e51772519cad&stat=instructions
Major memory wins on tramp3d:
https://llvm-compile-time-tracker.com/compare.php?from=d51e3474e060cb0e90dc2e2487f778b0d3e6a8de&to=bccffe3f8d5dd4dda884c9ac1f93e51772519cad&stat=max-rss
Reviewed By: nikic, mtrofin
Differential Revision: https://reviews.llvm.org/D115545
-rw-r--r-- | llvm/lib/Transforms/IPO/Inliner.cpp | 52 | ||||
-rw-r--r-- | llvm/test/Transforms/Inline/delete-function-with-metadata-use.ll | 39 | ||||
-rw-r--r-- | llvm/test/Transforms/Inline/delete-unused-function.ll | 63 |
3 files changed, 137 insertions, 17 deletions
diff --git a/llvm/lib/Transforms/IPO/Inliner.cpp b/llvm/lib/Transforms/IPO/Inliner.cpp index c5527c76e888..a77781ad66b7 100644 --- a/llvm/lib/Transforms/IPO/Inliner.cpp +++ b/llvm/lib/Transforms/IPO/Inliner.cpp @@ -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 @@ -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) @@ -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. diff --git a/llvm/test/Transforms/Inline/delete-function-with-metadata-use.ll b/llvm/test/Transforms/Inline/delete-function-with-metadata-use.ll new file mode 100644 index 000000000000..ae0015f2a131 --- /dev/null +++ b/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) diff --git a/llvm/test/Transforms/Inline/delete-unused-function.ll b/llvm/test/Transforms/Inline/delete-unused-function.ll new file mode 100644 index 000000000000..319a6a6bcab5 --- /dev/null +++ b/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 +} |