[ICP] Don't promote when target not defined in module
authorTeresa Johnson <tejohnson@google.com>
Tue, 8 Dec 2020 01:13:49 +0000 (17:13 -0800)
committerTeresa Johnson <tejohnson@google.com>
Tue, 8 Dec 2020 15:45:36 +0000 (07:45 -0800)
This guards against cases where the symbol was dead code eliminated in
the binary by ThinLTO, and we have a sample profile collected for one
binary but used to optimize another.

Most of the benefit from ICP comes from inlining the target, which we
can't do with only a declaration anyway. If this is in the pre-ThinLTO
link step (e.g. for instrumentation based PGO), we will attempt the
promotion again in the ThinLTO backend after importing anyway, and we
don't need the early promotion to facilitate that.

Differential Revision: https://reviews.llvm.org/D92804

llvm/lib/Transforms/Instrumentation/IndirectCallPromotion.cpp
llvm/test/LTO/Resolution/X86/load-sample-prof-icp.ll
llvm/test/Transforms/PGOProfile/icp_covariant_call_return.ll
llvm/test/Transforms/PGOProfile/icp_covariant_invoke_return.ll
llvm/test/Transforms/PGOProfile/indirect_call_promotion_musttail.ll

index bcd4e2e..5b9557a 100644 (file)
@@ -263,8 +263,15 @@ ICallPromotionFunc::getPromotionCandidatesForCallSite(
       break;
     }
 
+    // Don't promote if the symbol is not defined in the module. This avoids
+    // creating a reference to a symbol that doesn't exist in the module
+    // This can happen when we compile with a sample profile collected from
+    // one binary but used for another, which may have profiled targets that
+    // aren't used in the new binary. We might have a declaration initially in
+    // the case where the symbol is globally dead in the binary and removed by
+    // ThinLTO.
     Function *TargetFunction = Symtab->getFunction(Target);
-    if (TargetFunction == nullptr) {
+    if (TargetFunction == nullptr || TargetFunction->isDeclaration()) {
       LLVM_DEBUG(dbgs() << " Not promote: Cannot find the target\n");
       ORE.emit([&]() {
         return OptimizationRemarkMissed(DEBUG_TYPE, "UnableToFindTarget", &CB)
index 8ebb0a1..d4af2c4 100644 (file)
@@ -3,11 +3,11 @@
 ;
 ; RUN: opt -module-summary < %s -o %t.bc
 ; RUN: llvm-lto2 run -o %t.out %t.bc -save-temps \
-; RUN:   -r %t.bc,test,px -r %t.bc,bar,x \
+; RUN:   -r %t.bc,test,px -r %t.bc,bar,px -r %t.bc,externfunc,x \
 ; RUN:   -lto-sample-profile-file=%S/Inputs/load-sample-prof-icp.prof
 ; RUN: llvm-dis %t.out.1.4.opt.bc -o - | FileCheck %s
 ; RUN: llvm-lto2 run -o %t.out %t.bc -save-temps \
-; RUN:   -r %t.bc,test,px -r %t.bc,bar,x -use-new-pm \
+; RUN:   -r %t.bc,test,px -r %t.bc,bar,px -r %t.bc,externfunc,x -use-new-pm \
 ; RUN:   -lto-sample-profile-file=%S/Inputs/load-sample-prof-icp.prof
 ; RUN: llvm-dis %t.out.1.4.opt.bc -o - | FileCheck %s
 
@@ -26,9 +26,14 @@ define void @test(void ()*) #0 !dbg !7 {
   ret void
 }
 
-declare void @bar() local_unnamed_addr
+declare void @externfunc()
 
-attributes #0 = {"use-sample-profile"}
+define void @bar() #0 {
+  call void @externfunc()
+  ret void
+}
+
+attributes #0 = {"use-sample-profile" noinline}
 
 !llvm.dbg.cu = !{!0}
 !llvm.module.flags = !{!3, !4, !5}
index c2101a9..a4f10c6 100644 (file)
@@ -10,7 +10,9 @@ target triple = "x86_64-unknown-linux-gnu"
 
 declare noalias i8* @_Znwm(i64)
 declare void @_ZN1DC2Ev(%struct.D*);
-declare %struct.Derived* @_ZN1D4funcEv(%struct.D*);
+define %struct.Derived* @_ZN1D4funcEv(%struct.D*) {
+  ret %struct.Derived* null
+}
 
 define %struct.Base* @bar() {
 entry:
index d0f27ac..c36b2e9 100644 (file)
@@ -10,7 +10,9 @@ target triple = "x86_64-unknown-linux-gnu"
 @_ZTIi = external constant i8*
 declare i8* @_Znwm(i64)
 declare void @_ZN1DC2Ev(%struct.D*)
-declare %struct.Derived* @_ZN1D4funcEv(%struct.D*)
+define %struct.Derived* @_ZN1D4funcEv(%struct.D*) {
+  ret %struct.Derived* null
+}
 declare void @_ZN1DD0Ev(%struct.D*)
 declare void @_ZdlPv(i8*)
 declare i32 @__gxx_personality_v0(...)
index e79e533..6dea060 100644 (file)
@@ -6,13 +6,21 @@ target triple = "x86_64-unknown-linux-gnu"
 
 @foo = common global i32* ()* null, align 8
 
-declare i32* @func1()
+define i32* @func1() {
+  ret i32* null
+}
 
-declare i32* @func2()
+define i32* @func2() {
+  ret i32* null
+}
 
-declare i32* @func3()
+define i32* @func3() {
+  ret i32* null
+}
 
-declare i32* @func4()
+define i32* @func4() {
+  ret i32* null
+}
 
 define i32* @bar() {
 entry: