[llvm] d5be48c - [Inline] Attempt to delete any discardable if unused functions

Arthur Eubanks via llvm-commits llvm-commits at lists.llvm.org
Wed Jan 12 08:42:33 PST 2022


Author: Arthur Eubanks
Date: 2022-01-12T08:36:04-08:00
New Revision: d5be48c66d3e5e8be21805c3a33dc67a20e258be

URL: https://github.com/llvm/llvm-project/commit/d5be48c66d3e5e8be21805c3a33dc67a20e258be
DIFF: https://github.com/llvm/llvm-project/commit/d5be48c66d3e5e8be21805c3a33dc67a20e258be.diff

LOG: [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

Added: 
    llvm/test/Transforms/Inline/delete-function-with-metadata-use.ll
    llvm/test/Transforms/Inline/delete-unused-function.ll

Modified: 
    llvm/lib/Transforms/IPO/Inliner.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Transforms/IPO/Inliner.cpp b/llvm/lib/Transforms/IPO/Inliner.cpp
index c5527c76e8882..a77781ad66b7d 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 0000000000000..ae0015f2a131a
--- /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 0000000000000..319a6a6bcab53
--- /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
+}


        


More information about the llvm-commits mailing list