diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp index e895a46b8cd77..b12be4ac86aae 100644 --- a/llvm/lib/LTO/LTO.cpp +++ b/llvm/lib/LTO/LTO.cpp @@ -1314,7 +1314,8 @@ Error LTO::runRegularLTO(AddStreamFn AddStream) { // expected to be handled separately. auto IsVisibleToRegularObj = [&](StringRef name) { auto It = GlobalResolutions->find(name); - return (It == GlobalResolutions->end() || It->second.VisibleOutsideSummary); + return (It == GlobalResolutions->end() || + It->second.VisibleOutsideSummary || !It->second.Prevailing); }; // If allowed, upgrade public vcall visibility metadata to linkage unit @@ -1905,7 +1906,7 @@ Error LTO::runThinLTO(AddStreamFn AddStream, FileCache Cache, auto IsVisibleToRegularObj = [&](StringRef name) { auto It = GlobalResolutions->find(name); return (It == GlobalResolutions->end() || - It->second.VisibleOutsideSummary); + It->second.VisibleOutsideSummary || !It->second.Prevailing); }; getVisibleToRegularObjVtableGUIDs(ThinLTO.CombinedIndex, diff --git a/llvm/test/ThinLTO/X86/devirt_prevailing.ll b/llvm/test/ThinLTO/X86/devirt_prevailing.ll new file mode 100644 index 0000000000000..497b8f8a8603e --- /dev/null +++ b/llvm/test/ThinLTO/X86/devirt_prevailing.ll @@ -0,0 +1,158 @@ +; RUN: rm -rf %t && mkdir %t && cd %t + +; Tests that devirtualization is suppressed on a class when the LTO unit doesn't +; have the prevailing definition of the base class. + +; Generate unsplit module with summary for ThinLTO index-based WPD. +; RUN: opt -thinlto-bc -o summary.o %s + +; Index based WPD +; The callsite inside @_ZN4Base8dispatchEv gets devirtualized when symbol +; resolution shows there is a prevailing definition of `_ZTI7Derived` in the +; LTO unit. +; RUN: llvm-lto2 run summary.o -save-temps -pass-remarks=. \ +; RUN: -thinlto-threads=1 \ +; RUN: -o tmp \ +; RUN: --whole-program-visibility-enabled-in-lto=true \ +; RUN: --validate-all-vtables-have-type-infos=true \ +; RUN: --all-vtables-have-type-infos=true \ +; RUN: -r=summary.o,_ZN4Base8dispatchEv,px \ +; RUN: -r=summary.o,_ZN8DerivedN5printEv,px \ +; RUN: -r=summary.o,_ZTV8DerivedN,p \ +; RUN: -r=summary.o,_ZTI8DerivedN,p \ +; RUN: -r=summary.o,_ZTS8DerivedN,p \ +; RUN: -r=summary.o,_ZTI7Derived,p \ +; RUN: 2>&1 | FileCheck --allow-empty %s --check-prefix=REMARK + + +; Index based WPD +; The callsite inside @_ZN4Base8dispatchEv remains indirect and not de-virtualized +; when symbol resolution shows there isn't a prevailing definition of +; `_ZTI7Derived` in the LTO unit. +; RUN: llvm-lto2 run summary.o -save-temps -pass-remarks=. \ +; RUN: -thinlto-threads=1 \ +; RUN: -o tmp \ +; RUN: --whole-program-visibility-enabled-in-lto=true \ +; RUN: --validate-all-vtables-have-type-infos=true \ +; RUN: --all-vtables-have-type-infos=true \ +; RUN: -r=summary.o,_ZN4Base8dispatchEv,px \ +; RUN: -r=summary.o,_ZN8DerivedN5printEv,px \ +; RUN: -r=summary.o,_ZTV8DerivedN,p \ +; RUN: -r=summary.o,_ZTI8DerivedN,p \ +; RUN: -r=summary.o,_ZTS8DerivedN,p \ +; RUN: -r=summary.o,_ZTI7Derived, \ +; RUN: 2>&1 | FileCheck %s --allow-empty --implicit-check-not='single-impl: devirtualized a call to' + +; Repeat the above tests for WPD in hybrid LTO. +; RUN: opt --thinlto-bc --thinlto-split-lto-unit -o hybrid.o %s + +; RUN: llvm-lto2 run hybrid.o -save-temps -pass-remarks=. \ +; RUN: -thinlto-threads=1 \ +; RUN: -o hybrid-tmp \ +; RUN: --whole-program-visibility-enabled-in-lto=true \ +; RUN: --validate-all-vtables-have-type-infos=true \ +; RUN: --all-vtables-have-type-infos=true \ +; RUN: -r=hybrid.o,_ZN4Base8dispatchEv,px \ +; RUN: -r=hybrid.o,_ZN8DerivedN5printEv,px \ +; RUN: -r=hybrid.o,_ZTV8DerivedN, \ +; RUN: -r=hybrid.o,_ZTI8DerivedN, \ +; RUN: -r=hybrid.o,_ZTS8DerivedN,p \ +; RUN: -r=hybrid.o,_ZTI7Derived,p \ +; RUN: -r=hybrid.o,_ZN8DerivedN5printEv, \ +; RUN: -r=hybrid.o,_ZTV8DerivedN,p \ +; RUN: -r=hybrid.o,_ZTI8DerivedN,p \ +; RUN: 2>&1 | FileCheck --allow-empty %s --check-prefix=REMARK + + +; RUN: llvm-lto2 run hybrid.o -save-temps -pass-remarks=. \ +; RUN: -thinlto-threads=1 \ +; RUN: -o hybrid-tmp \ +; RUN: --whole-program-visibility-enabled-in-lto=true \ +; RUN: --validate-all-vtables-have-type-infos=true \ +; RUN: --all-vtables-have-type-infos=true \ +; RUN: -r=hybrid.o,_ZN4Base8dispatchEv,px \ +; RUN: -r=hybrid.o,_ZN8DerivedN5printEv,px \ +; RUN: -r=hybrid.o,_ZTV8DerivedN, \ +; RUN: -r=hybrid.o,_ZTI8DerivedN, \ +; RUN: -r=hybrid.o,_ZTS8DerivedN,p \ +; RUN: -r=hybrid.o,_ZTI7Derived, \ +; RUN: -r=hybrid.o,_ZN8DerivedN5printEv, \ +; RUN: -r=hybrid.o,_ZTV8DerivedN,p \ +; RUN: -r=hybrid.o,_ZTI8DerivedN,p \ +; RUN: 2>&1 | FileCheck --allow-empty %s --implicit-check-not='single-impl: devirtualized a call to' + + +; Repeat the above tests for WPD in regular LTO. +; RUN: opt -module-summary -o regular.o %s + +; RUN: llvm-lto2 run regular.o -save-temps -pass-remarks=. \ +; RUN: -thinlto-threads=1 \ +; RUN: -o regular-temp \ +; RUN: --whole-program-visibility-enabled-in-lto=true \ +; RUN: --validate-all-vtables-have-type-infos=true \ +; RUN: --all-vtables-have-type-infos=true \ +; RUN: -r=regular.o,_ZN4Base8dispatchEv,px \ +; RUN: -r=regular.o,_ZN8DerivedN5printEv,px \ +; RUN: -r=regular.o,_ZTS8DerivedN,p \ +; RUN: -r=regular.o,_ZTI7Derived,p \ +; RUN: -r=regular.o,_ZTV8DerivedN,p \ +; RUN: -r=regular.o,_ZTI8DerivedN,p \ +; RUN: 2>&1 | FileCheck --allow-empty %s --check-prefix=REMARK + +; RUN: llvm-lto2 run regular.o -save-temps -pass-remarks=. \ +; RUN: -thinlto-threads=1 \ +; RUN: -o regular-temp \ +; RUN: --whole-program-visibility-enabled-in-lto=true \ +; RUN: --validate-all-vtables-have-type-infos=true \ +; RUN: --all-vtables-have-type-infos=true \ +; RUN: -r=regular.o,_ZN4Base8dispatchEv,px \ +; RUN: -r=regular.o,_ZN8DerivedN5printEv,px \ +; RUN: -r=regular.o,_ZTS8DerivedN,p \ +; RUN: -r=regular.o,_ZTI7Derived, \ +; RUN: -r=regular.o,_ZTV8DerivedN,p \ +; RUN: -r=regular.o,_ZTI8DerivedN,p \ +; RUN: 2>&1 | FileCheck --allow-empty %s --implicit-check-not='single-impl: devirtualized a call to' + +; REMARK: single-impl: devirtualized a call to _ZN8DerivedN5printEv + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +@_ZTV8DerivedN = linkonce_odr hidden constant { [3 x ptr] } { [3 x ptr] [ptr null, ptr @_ZTI8DerivedN, ptr @_ZN8DerivedN5printEv] }, !type !0, !type !1, !type !2, !type !3, !vcall_visibility !4 +@_ZTI8DerivedN = linkonce_odr hidden constant { ptr, ptr, ptr } { ptr null, ptr @_ZTS8DerivedN, ptr @_ZTI7Derived } +@_ZTS8DerivedN = linkonce_odr hidden constant [10 x i8] c"8DerivedN\00", align 1 +@_ZTI7Derived = external constant ptr + +; Whole program devirtualization will ignore summaries that are not live. +; Mark '_ZTV8DerivedN' as used so it remains live. +@llvm.used = appending global [1 x ptr] [ptr @_ZTV8DerivedN], section "llvm.metadata" + +define hidden void @_ZN4Base8dispatchEv(ptr %this) { +entry: + %this.addr = alloca ptr + store ptr %this, ptr %this.addr + %this1 = load ptr, ptr %this.addr + %vtable = load ptr, ptr %this1 + %0 = call i1 @llvm.type.test(ptr %vtable, metadata !"_ZTS7Derived") + call void @llvm.assume(i1 %0) + %vfn = getelementptr inbounds ptr, ptr %vtable, i64 0 + %1 = load ptr, ptr %vfn + call void %1(ptr %this1) + ret void +} + +define linkonce_odr hidden void @_ZN8DerivedN5printEv(ptr %this) #0 { +entry: + ret void +} + +attributes #0 = { noinline optnone } + +declare i1 @llvm.type.test(ptr, metadata) +declare void @llvm.assume(i1) + +!0 = !{i64 16, !"_ZTS7Derived"} +!1 = !{i64 16, !"_ZTSM7DerivedFvvE.virtual"} +!2 = !{i64 16, !"_ZTS8DerivedN"} +!3 = !{i64 16, !"_ZTSM8DerivedNFvvE.virtual"} +!4 = !{i64 0} diff --git a/llvm/tools/llvm-lto2/llvm-lto2.cpp b/llvm/tools/llvm-lto2/llvm-lto2.cpp index dbb7f2f3028aa..4c9b47d78a1bb 100644 --- a/llvm/tools/llvm-lto2/llvm-lto2.cpp +++ b/llvm/tools/llvm-lto2/llvm-lto2.cpp @@ -187,6 +187,18 @@ static cl::opt EnableFreestanding( cl::desc("Enable Freestanding (disable builtins / TLI) during LTO"), cl::Hidden); +static cl::opt WholeProgramVisibilityEnabledInLTO( + "whole-program-visibility-enabled-in-lto", + cl::desc("Enable whole program visibility during LTO"), cl::Hidden); + +static cl::opt ValidateAllVtablesHaveTypeInfos( + "validate-all-vtables-have-type-infos", + cl::desc("Validate that all vtables have type infos in LTO"), cl::Hidden); + +static cl::opt + AllVtablesHaveTypeInfos("all-vtables-have-type-infos", cl::Hidden, + cl::desc("All vtables have type infos")); + extern cl::opt LoadBitcodeIntoNewDbgInfoFormat; extern cl::opt PreserveInputDbgFormat; @@ -332,6 +344,13 @@ static int run(int argc, char **argv) { Conf.PTO.LoopVectorization = Conf.OptLevel > 1; Conf.PTO.SLPVectorization = Conf.OptLevel > 1; + if (WholeProgramVisibilityEnabledInLTO.getNumOccurrences() > 0) + Conf.HasWholeProgramVisibility = WholeProgramVisibilityEnabledInLTO; + if (ValidateAllVtablesHaveTypeInfos.getNumOccurrences() > 0) + Conf.ValidateAllVtablesHaveTypeInfos = ValidateAllVtablesHaveTypeInfos; + if (AllVtablesHaveTypeInfos.getNumOccurrences() > 0) + Conf.AllVtablesHaveTypeInfos = AllVtablesHaveTypeInfos; + ThinBackend Backend; if (ThinLTODistributedIndexes) Backend = createWriteIndexesThinBackend(llvm::hardware_concurrency(Threads),