[ThinLTO/WPD] Ensure devirtualized targets use promoted symbol when necessary

Summary:
This fixes a hole in the handling of devirtualized targets that were
local but need promoting due to devirtualization in another module. We
were not correctly referencing the promoted symbol in some cases. Make
sure the code that updates the name also looks at the ExportedGUIDs set
by utilizing a callback that checks all conditions (the callback
utilized by the internalization/promotion code).

Reviewers: pcc, davidxl, hiraditya

Subscribers: mehdi_amini, Prazek, inglorion, steven_wu, dexonsmith, dang, llvm-commits

Tags: #llvm

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

llvm-svn: 373485
This commit is contained in:
Teresa Johnson 2019-10-02 16:36:59 +00:00
parent e3456e84d6
commit d2f6522f79
5 changed files with 120 additions and 10 deletions

View File

@ -251,7 +251,7 @@ void runWholeProgramDevirtOnIndex(
/// devirt target names for any locals that were exported.
void updateIndexWPDForExports(
ModuleSummaryIndex &Summary,
StringMap<FunctionImporter::ExportSetTy> &ExportLists,
function_ref<bool(StringRef, GlobalValue::GUID)> isExported,
std::map<ValueInfo, std::vector<VTableSlotSummary>> &LocalWPDTargetsMap);
} // end namespace llvm

View File

@ -1304,11 +1304,6 @@ Error LTO::runThinLTO(AddStreamFn AddStream, NativeObjectCache Cache,
ComputeCrossModuleImport(ThinLTO.CombinedIndex, ModuleToDefinedGVSummaries,
ImportLists, ExportLists);
// Update local devirtualized targets that were exported by cross-module
// importing
updateIndexWPDForExports(ThinLTO.CombinedIndex, ExportLists,
LocalWPDTargetsMap);
// Figure out which symbols need to be internalized. This also needs to happen
// at -O0 because summary-based DCE is implemented using internalization, and
// we must apply DCE consistently with the full LTO module in order to avoid
@ -1338,6 +1333,12 @@ Error LTO::runThinLTO(AddStreamFn AddStream, NativeObjectCache Cache,
ExportList->second.count(GUID)) ||
ExportedGUIDs.count(GUID);
};
// Update local devirtualized targets that were exported by cross-module
// importing or by other devirtualizations marked in the ExportedGUIDs set.
updateIndexWPDForExports(ThinLTO.CombinedIndex, isExported,
LocalWPDTargetsMap);
auto isPrevailing = [&](GlobalValue::GUID GUID,
const GlobalValueSummary *S) {
return ThinLTO.PrevailingModuleForGUID[GUID] == S->modulePath();

View File

@ -713,7 +713,7 @@ void runWholeProgramDevirtOnIndex(
void updateIndexWPDForExports(
ModuleSummaryIndex &Summary,
StringMap<FunctionImporter::ExportSetTy> &ExportLists,
function_ref<bool(StringRef, GlobalValue::GUID)> isExported,
std::map<ValueInfo, std::vector<VTableSlotSummary>> &LocalWPDTargetsMap) {
for (auto &T : LocalWPDTargetsMap) {
auto &VI = T.first;
@ -721,9 +721,7 @@ void updateIndexWPDForExports(
assert(VI.getSummaryList().size() == 1 &&
"Devirt of local target has more than one copy");
auto &S = VI.getSummaryList()[0];
const auto &ExportList = ExportLists.find(S->modulePath());
if (ExportList == ExportLists.end() ||
!ExportList->second.count(VI.getGUID()))
if (!isExported(S->modulePath(), VI.getGUID()))
continue;
// It's been exported by a cross module import.

View File

@ -0,0 +1,39 @@
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-grtev4-linux-gnu"
%struct.A = type { i32 (...)** }
%struct.B = type { %struct.A }
@_ZTV1B = constant { [4 x i8*] } { [4 x i8*] [i8* null, i8* undef, i8* bitcast (i32 (%struct.B*, i32)* @_ZN1B1fEi to i8*), i8* bitcast (i32 (%struct.A*, i32)* @_ZN1A1nEi to i8*)] }, !type !0, !type !1
define i32 @_ZN1B1fEi(%struct.B* %this, i32 %a) #0 {
ret i32 0;
}
define internal i32 @_ZN1A1nEi(%struct.A* %this, i32 %a) #0 {
ret i32 0;
}
define i32 @test2(%struct.B* %obj, i32 %a) {
entry:
%0 = bitcast %struct.B* %obj to i8***
%vtable2 = load i8**, i8*** %0
%1 = bitcast i8** %vtable2 to i8*
%p2 = call i1 @llvm.type.test(i8* %1, metadata !"_ZTS1B")
call void @llvm.assume(i1 %p2)
%fptrptr = getelementptr i8*, i8** %vtable2, i32 1
%2 = bitcast i8** %fptrptr to i32 (%struct.B*, i32)**
%fptr33 = load i32 (%struct.B*, i32)*, i32 (%struct.B*, i32)** %2, align 8
%call4 = tail call i32 %fptr33(%struct.B* nonnull %obj, i32 %a)
ret i32 %call4
}
attributes #0 = { noinline optnone }
declare i1 @llvm.type.test(i8*, metadata)
declare void @llvm.assume(i1)
!0 = !{i64 16, !"_ZTS1A"}
!1 = !{i64 16, !"_ZTS1B"}

View File

@ -0,0 +1,72 @@
; REQUIRES: x86-registered-target
; Test devirtualization requiring promotion of local targets, where the
; promotion is required by one devirtualization and needs to be updated
; for a second devirtualization in the defining module as a post-pass
; update.
; Generate unsplit module with summary for ThinLTO index-based WPD.
; RUN: opt -thinlto-bc -o %t3.o %s
; RUN: opt -thinlto-bc -o %t4.o %p/Inputs/devirt_promote.ll
; RUN: llvm-lto2 run %t3.o %t4.o -save-temps -use-new-pm -pass-remarks=. \
; RUN: -wholeprogramdevirt-print-index-based \
; RUN: -o %t5 \
; RUN: -r=%t3.o,test,px \
; RUN: -r=%t4.o,_ZN1B1fEi,p \
; RUN: -r=%t4.o,test2,px \
; RUN: -r=%t4.o,_ZTV1B,px \
; RUN: 2>&1 | FileCheck %s --check-prefix=REMARK --check-prefix=PRINT
; RUN: llvm-dis %t5.1.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-IR1
; RUN: llvm-dis %t5.2.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-IR2
; RUN: llvm-nm %t5.1 | FileCheck %s --check-prefix=NM-INDEX1
; RUN: llvm-nm %t5.2 | FileCheck %s --check-prefix=NM-INDEX2
; NM-INDEX1: U _ZN1A1nEi.llvm.
; Make sure that not only did _ZN1A1nEi get promoted (due to the
; devirtualization in the other module) but the reference due to the
; devirtualization in its defining module should be to the promoted
; symbol.
; NM-INDEX2-NOT: U _ZN1A1nEi
; NM-INDEX2: T _ZN1A1nEi.llvm.
; NM-INDEX2-NOT: U _ZN1A1nEi
; We should devirt call to _ZN1A1nEi once in importing module and once
; in original (exporting) module.
; REMARK-COUNT-2: single-impl: devirtualized a call to _ZN1A1nEi.llvm.
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-grtev4-linux-gnu"
%struct.A = type { i32 (...)** }
; CHECK-IR1-LABEL: define i32 @test
define i32 @test(%struct.A* %obj, i32 %a) {
entry:
%0 = bitcast %struct.A* %obj to i8***
%vtable = load i8**, i8*** %0
%1 = bitcast i8** %vtable to i8*
%p = call i1 @llvm.type.test(i8* %1, metadata !"_ZTS1A")
call void @llvm.assume(i1 %p)
%fptrptr = getelementptr i8*, i8** %vtable, i32 1
%2 = bitcast i8** %fptrptr to i32 (%struct.A*, i32)**
%fptr1 = load i32 (%struct.A*, i32)*, i32 (%struct.A*, i32)** %2, align 8
; Check that the call was devirtualized.
; CHECK-IR1: %call = tail call i32 bitcast (void ()* @_ZN1A1nEi
%call = tail call i32 %fptr1(%struct.A* nonnull %obj, i32 %a)
ret i32 %call
}
; CHECK-IR1-LABEL: ret i32
; CHECK-IR1-LABEL: }
; CHECK-IR2: define i32 @test2
; Check that the call was devirtualized.
; CHECK-IR2: %call4 = tail call i32 @_ZN1A1nEi
declare i1 @llvm.type.test(i8*, metadata)
declare void @llvm.assume(i1)
attributes #0 = { noinline optnone }