From 40f913c404c6335ac3d68229e9c630cf7a72f3a4 Mon Sep 17 00:00:00 2001 From: mingmingl Date: Mon, 17 Mar 2025 20:27:38 -0700 Subject: [PATCH 1/5] Propagate ExportDynamic from base type to compatible derived types in WPD. --- llvm/lib/LTO/LTO.cpp | 2 +- .../ThinLTO/X86/nodevirt_exort_dynamic.ll | 111 ++++++++++++++++++ llvm/tools/llvm-lto2/llvm-lto2.cpp | 21 ++++ 3 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 llvm/test/ThinLTO/X86/nodevirt_exort_dynamic.ll diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp index e895a46b8cd77..6ec2dc381a932 100644 --- a/llvm/lib/LTO/LTO.cpp +++ b/llvm/lib/LTO/LTO.cpp @@ -1905,7 +1905,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.ExportDynamic); }; getVisibleToRegularObjVtableGUIDs(ThinLTO.CombinedIndex, diff --git a/llvm/test/ThinLTO/X86/nodevirt_exort_dynamic.ll b/llvm/test/ThinLTO/X86/nodevirt_exort_dynamic.ll new file mode 100644 index 0000000000000..ea33291a75161 --- /dev/null +++ b/llvm/test/ThinLTO/X86/nodevirt_exort_dynamic.ll @@ -0,0 +1,111 @@ +; RUN: rm -rf %t && mkdir %t && cd %t + +; Generate unsplit module with summary for ThinLTO index-based WPD. +; RUN: opt -thinlto-bc -o summary.o %s + +; RUN: llvm-dis -o - summary.o + +;; TODO: Implement the fix for WPD in regular or hybrid LTO, and add test coverage. + +; Index based WPD +; For `_ZTI7Derived`, the 'llvm-lto2' resolution arguments specifies `VisibleOutsideSummary` as false +; and `ExportDynamic` as false. The callsite inside @_ZN4Base8dispatchEv +; got devirtualized. +; RUN: llvm-lto2 run summary.o -save-temps -pass-remarks=. \ +; 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,__cxa_pure_virtual, \ +; RUN: -r=summary.o,_ZN8DerivedNC2Ev,x \ +; RUN: -r=summary.o,_ZN4Base8dispatchEv,px \ +; RUN: -r=summary.o,_ZN7DerivedC2Ev, \ +; RUN: -r=summary.o,_ZN8DerivedN5printEv,px \ +; RUN: -r=summary.o,_ZTS4Base, \ +; RUN: -r=summary.o,_ZTV8DerivedN,p \ +; RUN: -r=summary.o,_ZTI8DerivedN,p \ +; RUN: -r=summary.o,_ZTI4Base, \ +; RUN: -r=summary.o,_ZTS8DerivedN,p \ +; RUN: -r=summary.o,_ZTI7Derived, \ +; RUN: -r=summary.o,_ZTV4Base 2>&1 | FileCheck --allow-empty %s --check-prefix=REMARK + +; REMARK: single-impl: devirtualized a call to _ZN8DerivedN5printEv + +; Index based WPD +; For `_ZTI7Derived`, the 'llvm-lto2' resolution arguments specifies `VisibleOutsideSummary` as false +; and `ExportDynamic` as true. The callsite inside @_ZN4Base8dispatchEv won't +; get devirtualized. +; RUN: llvm-lto2 run summary.o -save-temps -pass-remarks=. \ +; 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,__cxa_pure_virtual, \ +; RUN: -r=summary.o,_ZN8DerivedNC2Ev,x \ +; RUN: -r=summary.o,_ZN4Base8dispatchEv,px \ +; RUN: -r=summary.o,_ZN7DerivedC2Ev, \ +; RUN: -r=summary.o,_ZN8DerivedN5printEv,px \ +; RUN: -r=summary.o,_ZTS4Base, \ +; RUN: -r=summary.o,_ZTV8DerivedN,p \ +; RUN: -r=summary.o,_ZTI8DerivedN,p \ +; RUN: -r=summary.o,_ZTI4Base, \ +; RUN: -r=summary.o,_ZTS8DerivedN,p \ +; RUN: -r=summary.o,_ZTI7Derived,d \ +; RUN: -r=summary.o,_ZTV4Base 2>&1 | FileCheck %s --allow-empty --implicit-check-not='single-impl: devirtualized a call to' + +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, !type !4, !type !5, !vcall_visibility !6 +@_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 +@_ZTV4Base = linkonce_odr hidden constant { [3 x ptr] } { [3 x ptr] [ptr null, ptr @_ZTI4Base, ptr @__cxa_pure_virtual] }, !type !0, !type !1, !vcall_visibility !6 +@_ZTI4Base = linkonce_odr hidden constant { ptr, ptr } { ptr null, ptr @_ZTS4Base } +@_ZTS4Base = linkonce_odr hidden constant [6 x i8] c"4Base\00", align 1 + +@llvm.used = appending global [1 x ptr] [ptr @_ZN8DerivedNC2Ev], 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 @_ZN8DerivedNC2Ev(ptr %this) #0 { +entry: + %this.addr = alloca ptr + store ptr %this, ptr %this.addr + %this1 = load ptr, ptr %this.addr + call void @_ZN7DerivedC2Ev(ptr %this1) + store ptr getelementptr inbounds inrange(-16, 8) ({ [3 x ptr] }, ptr @_ZTV8DerivedN, i32 0, i32 0, i32 2), ptr %this1 + ret void +} + +define linkonce_odr hidden void @_ZN8DerivedN5printEv(ptr %this) #0 { +entry: + ret void +} + +attributes #0 = { noinline optnone } + +declare void @__cxa_pure_virtual() +declare i1 @llvm.type.test(ptr, metadata) +declare void @llvm.assume(i1) +declare void @_ZN7DerivedC2Ev(ptr) + +!0 = !{i64 16, !"_ZTS4Base"} +!1 = !{i64 16, !"_ZTSM4BaseFvvE.virtual"} +!2 = !{i64 16, !"_ZTS7Derived"} +!3 = !{i64 16, !"_ZTSM7DerivedFvvE.virtual"} +!4 = !{i64 16, !"_ZTS8DerivedN"} +!5 = !{i64 16, !"_ZTSM8DerivedNFvvE.virtual"} +!6 = !{i64 0} diff --git a/llvm/tools/llvm-lto2/llvm-lto2.cpp b/llvm/tools/llvm-lto2/llvm-lto2.cpp index dbb7f2f3028aa..0317a610a25a3 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; @@ -257,6 +269,8 @@ static int run(int argc, char **argv) { Res.VisibleToRegularObj = true; else if (C == 'r') Res.LinkerRedefined = true; + else if (C == 'd') + Res.ExportDynamic = true; else { llvm::errs() << "invalid character " << C << " in resolution: " << R << '\n'; @@ -332,6 +346,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), From 6be44643eacc9a4a17e90f51bb42b3107df869d7 Mon Sep 17 00:00:00 2001 From: mingmingl Date: Tue, 18 Mar 2025 11:14:20 -0700 Subject: [PATCH 2/5] implement the fix for hybrid LTO, and update test cases --- llvm/lib/LTO/LTO.cpp | 3 +- ..._dynamic.ll => nodevirt_export_dynamic.ll} | 85 ++++++++++++++----- 2 files changed, 66 insertions(+), 22 deletions(-) rename llvm/test/ThinLTO/X86/{nodevirt_exort_dynamic.ll => nodevirt_export_dynamic.ll} (55%) diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp index 6ec2dc381a932..fe829c868d3c2 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.ExportDynamic); }; // If allowed, upgrade public vcall visibility metadata to linkage unit diff --git a/llvm/test/ThinLTO/X86/nodevirt_exort_dynamic.ll b/llvm/test/ThinLTO/X86/nodevirt_export_dynamic.ll similarity index 55% rename from llvm/test/ThinLTO/X86/nodevirt_exort_dynamic.ll rename to llvm/test/ThinLTO/X86/nodevirt_export_dynamic.ll index ea33291a75161..5dc809d2cab1f 100644 --- a/llvm/test/ThinLTO/X86/nodevirt_exort_dynamic.ll +++ b/llvm/test/ThinLTO/X86/nodevirt_export_dynamic.ll @@ -1,12 +1,12 @@ ; RUN: rm -rf %t && mkdir %t && cd %t +; Tests that devirtualization is suppressed on a class when its compatible +; class could be referenced from dynamic linker that is not visible to the +; linker. + ; Generate unsplit module with summary for ThinLTO index-based WPD. ; RUN: opt -thinlto-bc -o summary.o %s -; RUN: llvm-dis -o - summary.o - -;; TODO: Implement the fix for WPD in regular or hybrid LTO, and add test coverage. - ; Index based WPD ; For `_ZTI7Derived`, the 'llvm-lto2' resolution arguments specifies `VisibleOutsideSummary` as false ; and `ExportDynamic` as false. The callsite inside @_ZN4Base8dispatchEv @@ -21,15 +21,12 @@ ; RUN: -r=summary.o,_ZN4Base8dispatchEv,px \ ; RUN: -r=summary.o,_ZN7DerivedC2Ev, \ ; RUN: -r=summary.o,_ZN8DerivedN5printEv,px \ -; RUN: -r=summary.o,_ZTS4Base, \ ; RUN: -r=summary.o,_ZTV8DerivedN,p \ ; RUN: -r=summary.o,_ZTI8DerivedN,p \ -; RUN: -r=summary.o,_ZTI4Base, \ ; RUN: -r=summary.o,_ZTS8DerivedN,p \ ; RUN: -r=summary.o,_ZTI7Derived, \ -; RUN: -r=summary.o,_ZTV4Base 2>&1 | FileCheck --allow-empty %s --check-prefix=REMARK +; RUN: 2>&1 | FileCheck --allow-empty %s --check-prefix=REMARK -; REMARK: single-impl: devirtualized a call to _ZN8DerivedN5printEv ; Index based WPD ; For `_ZTI7Derived`, the 'llvm-lto2' resolution arguments specifies `VisibleOutsideSummary` as false @@ -45,24 +42,70 @@ ; RUN: -r=summary.o,_ZN4Base8dispatchEv,px \ ; RUN: -r=summary.o,_ZN7DerivedC2Ev, \ ; RUN: -r=summary.o,_ZN8DerivedN5printEv,px \ -; RUN: -r=summary.o,_ZTS4Base, \ ; RUN: -r=summary.o,_ZTV8DerivedN,p \ ; RUN: -r=summary.o,_ZTI8DerivedN,p \ -; RUN: -r=summary.o,_ZTI4Base, \ ; RUN: -r=summary.o,_ZTS8DerivedN,p \ ; RUN: -r=summary.o,_ZTI7Derived,d \ -; RUN: -r=summary.o,_ZTV4Base 2>&1 | FileCheck %s --allow-empty --implicit-check-not='single-impl: devirtualized a call to' +; RUN: 2>&1 | FileCheck %s --allow-empty --implicit-check-not='single-impl: devirtualized a call to' + + +; Hybrid LTO WPD +; RUN: opt --thinlto-bc --thinlto-split-lto-unit -o hybrid.o %s +; RUN: llvm-lto2 run hybrid.o -save-temps -pass-remarks=. \ +; RUN: -o hybrid \ +; 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,__cxa_pure_virtual, \ +; RUN: -r=hybrid.o,_ZN8DerivedNC2Ev,x \ +; RUN: -r=hybrid.o,_ZN4Base8dispatchEv,px \ +; RUN: -r=hybrid.o,_ZN7DerivedC2Ev, \ +; RUN: -r=hybrid.o,_ZN8DerivedN5printEv,px \ +; RUN: -r=hybrid.o,_ZTV8DerivedN,p \ +; RUN: -r=hybrid.o,_ZTI8DerivedN,p \ +; RUN: -r=hybrid.o,_ZTS8DerivedN,p \ +; RUN: -r=hybrid.o,_ZTI7Derived, \ +; RUN: -r=hybrid.o,_ZN8DerivedN5printEv,px \ +; RUN: -r=hybrid.o,_ZTV8DerivedN,p \ +; RUN: -r=hybrid.o,_ZTI8DerivedN,p \ +; RUN: 2>&1 | FileCheck --allow-empty %s --check-prefix=REMARK + +; Hybrid LTO WPD +; RUN: llvm-lto2 run hybrid.o -save-temps -pass-remarks=. \ +; RUN: -o hybrid \ +; 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,__cxa_pure_virtual, \ +; RUN: -r=hybrid.o,_ZN8DerivedNC2Ev,x \ +; RUN: -r=hybrid.o,_ZN4Base8dispatchEv,px \ +; RUN: -r=hybrid.o,_ZN7DerivedC2Ev, \ +; RUN: -r=hybrid.o,_ZN8DerivedN5printEv,px \ +; RUN: -r=hybrid.o,_ZTV8DerivedN,p \ +; RUN: -r=hybrid.o,_ZTI8DerivedN,p \ +; RUN: -r=hybrid.o,_ZTS8DerivedN,p \ +; RUN: -r=hybrid.o,_ZTI7Derived,d \ +; RUN: -r=hybrid.o,_ZN8DerivedN5printEv,px \ +; 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' + + +; In regular LTO, global resolutions (as expected) show symbols are visible +; outside summary (when they come from regular LTO module without summaries). +; In the setting of this test case (equivalent of `-Wl,--lto-whole-program-visibility -Wl,--lto-validate-all-vtables-have-type-infos` in lld), +; devirtualization will be suppressed even if the compatible class is not +; referenced from shared libraries. So regular LTO test coverage is not meaningful. + +; 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, !type !4, !type !5, !vcall_visibility !6 +@_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 !6 @_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 -@_ZTV4Base = linkonce_odr hidden constant { [3 x ptr] } { [3 x ptr] [ptr null, ptr @_ZTI4Base, ptr @__cxa_pure_virtual] }, !type !0, !type !1, !vcall_visibility !6 -@_ZTI4Base = linkonce_odr hidden constant { ptr, ptr } { ptr null, ptr @_ZTS4Base } -@_ZTS4Base = linkonce_odr hidden constant [6 x i8] c"4Base\00", align 1 @llvm.used = appending global [1 x ptr] [ptr @_ZN8DerivedNC2Ev], section "llvm.metadata" @@ -102,10 +145,10 @@ declare i1 @llvm.type.test(ptr, metadata) declare void @llvm.assume(i1) declare void @_ZN7DerivedC2Ev(ptr) -!0 = !{i64 16, !"_ZTS4Base"} -!1 = !{i64 16, !"_ZTSM4BaseFvvE.virtual"} -!2 = !{i64 16, !"_ZTS7Derived"} -!3 = !{i64 16, !"_ZTSM7DerivedFvvE.virtual"} -!4 = !{i64 16, !"_ZTS8DerivedN"} -!5 = !{i64 16, !"_ZTSM8DerivedNFvvE.virtual"} +!0 = !{i64 16, !"_ZTS7Derived"} +!1 = !{i64 16, !"_ZTSM7DerivedFvvE.virtual"} +!2 = !{i64 16, !"_ZTS8DerivedN"} +!3 = !{i64 16, !"_ZTSM8DerivedNFvvE.virtual"} +;!4 = !{i64 16, !"_ZTS8DerivedN"} +;!5 = !{i64 16, !"_ZTSM8DerivedNFvvE.virtual"} !6 = !{i64 0} From dfe5970e495e409f28f30bab3b1db60ad413964e Mon Sep 17 00:00:00 2001 From: mingmingl Date: Wed, 19 Mar 2025 11:59:49 -0700 Subject: [PATCH 3/5] Look at the Prevailing bit as opposed to ExportDynamic bit, update regression test --- llvm/lib/LTO/LTO.cpp | 4 +- ...export_dynamic.ll => devirt_prevailing.ll} | 47 ++++++++++--------- llvm/tools/llvm-lto2/llvm-lto2.cpp | 2 - 3 files changed, 27 insertions(+), 26 deletions(-) rename llvm/test/ThinLTO/X86/{nodevirt_export_dynamic.ll => devirt_prevailing.ll} (84%) diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp index fe829c868d3c2..b12be4ac86aae 100644 --- a/llvm/lib/LTO/LTO.cpp +++ b/llvm/lib/LTO/LTO.cpp @@ -1315,7 +1315,7 @@ Error LTO::runRegularLTO(AddStreamFn AddStream) { auto IsVisibleToRegularObj = [&](StringRef name) { auto It = GlobalResolutions->find(name); return (It == GlobalResolutions->end() || - It->second.VisibleOutsideSummary || It->second.ExportDynamic); + It->second.VisibleOutsideSummary || !It->second.Prevailing); }; // If allowed, upgrade public vcall visibility metadata to linkage unit @@ -1906,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.ExportDynamic); + It->second.VisibleOutsideSummary || !It->second.Prevailing); }; getVisibleToRegularObjVtableGUIDs(ThinLTO.CombinedIndex, diff --git a/llvm/test/ThinLTO/X86/nodevirt_export_dynamic.ll b/llvm/test/ThinLTO/X86/devirt_prevailing.ll similarity index 84% rename from llvm/test/ThinLTO/X86/nodevirt_export_dynamic.ll rename to llvm/test/ThinLTO/X86/devirt_prevailing.ll index 5dc809d2cab1f..bbbf71d7fb852 100644 --- a/llvm/test/ThinLTO/X86/nodevirt_export_dynamic.ll +++ b/llvm/test/ThinLTO/X86/devirt_prevailing.ll @@ -1,17 +1,17 @@ ; RUN: rm -rf %t && mkdir %t && cd %t -; Tests that devirtualization is suppressed on a class when its compatible -; class could be referenced from dynamic linker that is not visible to the -; linker. +; Tests that devirtualization is suppressed on a class when the LTO unit doesn't +; have the prevailing definition of the class. ; Generate unsplit module with summary for ThinLTO index-based WPD. ; RUN: opt -thinlto-bc -o summary.o %s ; Index based WPD -; For `_ZTI7Derived`, the 'llvm-lto2' resolution arguments specifies `VisibleOutsideSummary` as false -; and `ExportDynamic` as false. The callsite inside @_ZN4Base8dispatchEv -; got devirtualized. +; 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 \ @@ -24,15 +24,16 @@ ; RUN: -r=summary.o,_ZTV8DerivedN,p \ ; RUN: -r=summary.o,_ZTI8DerivedN,p \ ; RUN: -r=summary.o,_ZTS8DerivedN,p \ -; RUN: -r=summary.o,_ZTI7Derived, \ +; RUN: -r=summary.o,_ZTI7Derived,p \ ; RUN: 2>&1 | FileCheck --allow-empty %s --check-prefix=REMARK ; Index based WPD -; For `_ZTI7Derived`, the 'llvm-lto2' resolution arguments specifies `VisibleOutsideSummary` as false -; and `ExportDynamic` as true. The callsite inside @_ZN4Base8dispatchEv won't -; get devirtualized. +; 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 \ @@ -45,13 +46,14 @@ ; RUN: -r=summary.o,_ZTV8DerivedN,p \ ; RUN: -r=summary.o,_ZTI8DerivedN,p \ ; RUN: -r=summary.o,_ZTS8DerivedN,p \ -; RUN: -r=summary.o,_ZTI7Derived,d \ +; RUN: -r=summary.o,_ZTI7Derived, \ ; RUN: 2>&1 | FileCheck %s --allow-empty --implicit-check-not='single-impl: devirtualized a call to' - -; Hybrid LTO WPD +; 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 \ ; RUN: --whole-program-visibility-enabled-in-lto=true \ ; RUN: --validate-all-vtables-have-type-infos=true \ @@ -61,17 +63,18 @@ ; RUN: -r=hybrid.o,_ZN4Base8dispatchEv,px \ ; RUN: -r=hybrid.o,_ZN7DerivedC2Ev, \ ; RUN: -r=hybrid.o,_ZN8DerivedN5printEv,px \ -; RUN: -r=hybrid.o,_ZTV8DerivedN,p \ -; RUN: -r=hybrid.o,_ZTI8DerivedN,p \ +; 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,px \ +; 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 -; Hybrid LTO WPD + ; RUN: llvm-lto2 run hybrid.o -save-temps -pass-remarks=. \ +; RUN: -thinlto-threads=1 \ ; RUN: -o hybrid \ ; RUN: --whole-program-visibility-enabled-in-lto=true \ ; RUN: --validate-all-vtables-have-type-infos=true \ @@ -81,11 +84,11 @@ ; RUN: -r=hybrid.o,_ZN4Base8dispatchEv,px \ ; RUN: -r=hybrid.o,_ZN7DerivedC2Ev, \ ; RUN: -r=hybrid.o,_ZN8DerivedN5printEv,px \ -; RUN: -r=hybrid.o,_ZTV8DerivedN,p \ -; RUN: -r=hybrid.o,_ZTI8DerivedN,p \ +; RUN: -r=hybrid.o,_ZTV8DerivedN, \ +; RUN: -r=hybrid.o,_ZTI8DerivedN, \ ; RUN: -r=hybrid.o,_ZTS8DerivedN,p \ -; RUN: -r=hybrid.o,_ZTI7Derived,d \ -; RUN: -r=hybrid.o,_ZN8DerivedN5printEv,px \ +; 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' diff --git a/llvm/tools/llvm-lto2/llvm-lto2.cpp b/llvm/tools/llvm-lto2/llvm-lto2.cpp index 0317a610a25a3..4c9b47d78a1bb 100644 --- a/llvm/tools/llvm-lto2/llvm-lto2.cpp +++ b/llvm/tools/llvm-lto2/llvm-lto2.cpp @@ -269,8 +269,6 @@ static int run(int argc, char **argv) { Res.VisibleToRegularObj = true; else if (C == 'r') Res.LinkerRedefined = true; - else if (C == 'd') - Res.ExportDynamic = true; else { llvm::errs() << "invalid character " << C << " in resolution: " << R << '\n'; From 27b1e88c43dc90381d4282df619c50206c2510a3 Mon Sep 17 00:00:00 2001 From: mingmingl Date: Wed, 19 Mar 2025 14:37:08 -0700 Subject: [PATCH 4/5] add test coverage for regular LTO --- llvm/test/ThinLTO/X86/devirt_prevailing.ll | 45 ++++++++++++++++++---- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/llvm/test/ThinLTO/X86/devirt_prevailing.ll b/llvm/test/ThinLTO/X86/devirt_prevailing.ll index bbbf71d7fb852..13a3c6ace75de 100644 --- a/llvm/test/ThinLTO/X86/devirt_prevailing.ll +++ b/llvm/test/ThinLTO/X86/devirt_prevailing.ll @@ -54,7 +54,7 @@ ; RUN: llvm-lto2 run hybrid.o -save-temps -pass-remarks=. \ ; RUN: -thinlto-threads=1 \ -; RUN: -o hybrid \ +; 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 \ @@ -75,7 +75,7 @@ ; RUN: llvm-lto2 run hybrid.o -save-temps -pass-remarks=. \ ; RUN: -thinlto-threads=1 \ -; RUN: -o hybrid \ +; 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 \ @@ -94,11 +94,42 @@ ; RUN: 2>&1 | FileCheck --allow-empty %s --implicit-check-not='single-impl: devirtualized a call to' -; In regular LTO, global resolutions (as expected) show symbols are visible -; outside summary (when they come from regular LTO module without summaries). -; In the setting of this test case (equivalent of `-Wl,--lto-whole-program-visibility -Wl,--lto-validate-all-vtables-have-type-infos` in lld), -; devirtualization will be suppressed even if the compatible class is not -; referenced from shared libraries. So regular LTO test coverage is not meaningful. +; 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,__cxa_pure_virtual, \ +; RUN: -r=regular.o,_ZN8DerivedNC2Ev,x \ +; RUN: -r=regular.o,_ZN4Base8dispatchEv,px \ +; RUN: -r=regular.o,_ZN7DerivedC2Ev, \ +; 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,__cxa_pure_virtual, \ +; RUN: -r=regular.o,_ZN8DerivedNC2Ev,x \ +; RUN: -r=regular.o,_ZN4Base8dispatchEv,px \ +; RUN: -r=regular.o,_ZN7DerivedC2Ev, \ +; 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 From f12edd158f4afcde6edf45d5aab79c4f225fb8ba Mon Sep 17 00:00:00 2001 From: mingmingl Date: Wed, 19 Mar 2025 21:08:47 -0700 Subject: [PATCH 5/5] resolve comments --- llvm/test/ThinLTO/X86/devirt_prevailing.ll | 42 ++++------------------ 1 file changed, 6 insertions(+), 36 deletions(-) diff --git a/llvm/test/ThinLTO/X86/devirt_prevailing.ll b/llvm/test/ThinLTO/X86/devirt_prevailing.ll index 13a3c6ace75de..497b8f8a8603e 100644 --- a/llvm/test/ThinLTO/X86/devirt_prevailing.ll +++ b/llvm/test/ThinLTO/X86/devirt_prevailing.ll @@ -1,7 +1,7 @@ ; 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 class. +; 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 @@ -16,10 +16,7 @@ ; 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,__cxa_pure_virtual, \ -; RUN: -r=summary.o,_ZN8DerivedNC2Ev,x \ ; RUN: -r=summary.o,_ZN4Base8dispatchEv,px \ -; RUN: -r=summary.o,_ZN7DerivedC2Ev, \ ; RUN: -r=summary.o,_ZN8DerivedN5printEv,px \ ; RUN: -r=summary.o,_ZTV8DerivedN,p \ ; RUN: -r=summary.o,_ZTI8DerivedN,p \ @@ -38,10 +35,7 @@ ; 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,__cxa_pure_virtual, \ -; RUN: -r=summary.o,_ZN8DerivedNC2Ev,x \ ; RUN: -r=summary.o,_ZN4Base8dispatchEv,px \ -; RUN: -r=summary.o,_ZN7DerivedC2Ev, \ ; RUN: -r=summary.o,_ZN8DerivedN5printEv,px \ ; RUN: -r=summary.o,_ZTV8DerivedN,p \ ; RUN: -r=summary.o,_ZTI8DerivedN,p \ @@ -58,10 +52,7 @@ ; 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,__cxa_pure_virtual, \ -; RUN: -r=hybrid.o,_ZN8DerivedNC2Ev,x \ ; RUN: -r=hybrid.o,_ZN4Base8dispatchEv,px \ -; RUN: -r=hybrid.o,_ZN7DerivedC2Ev, \ ; RUN: -r=hybrid.o,_ZN8DerivedN5printEv,px \ ; RUN: -r=hybrid.o,_ZTV8DerivedN, \ ; RUN: -r=hybrid.o,_ZTI8DerivedN, \ @@ -79,10 +70,7 @@ ; 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,__cxa_pure_virtual, \ -; RUN: -r=hybrid.o,_ZN8DerivedNC2Ev,x \ ; RUN: -r=hybrid.o,_ZN4Base8dispatchEv,px \ -; RUN: -r=hybrid.o,_ZN7DerivedC2Ev, \ ; RUN: -r=hybrid.o,_ZN8DerivedN5printEv,px \ ; RUN: -r=hybrid.o,_ZTV8DerivedN, \ ; RUN: -r=hybrid.o,_ZTI8DerivedN, \ @@ -103,10 +91,7 @@ ; 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,__cxa_pure_virtual, \ -; RUN: -r=regular.o,_ZN8DerivedNC2Ev,x \ ; RUN: -r=regular.o,_ZN4Base8dispatchEv,px \ -; RUN: -r=regular.o,_ZN7DerivedC2Ev, \ ; RUN: -r=regular.o,_ZN8DerivedN5printEv,px \ ; RUN: -r=regular.o,_ZTS8DerivedN,p \ ; RUN: -r=regular.o,_ZTI7Derived,p \ @@ -120,10 +105,7 @@ ; 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,__cxa_pure_virtual, \ -; RUN: -r=regular.o,_ZN8DerivedNC2Ev,x \ ; RUN: -r=regular.o,_ZN4Base8dispatchEv,px \ -; RUN: -r=regular.o,_ZN7DerivedC2Ev, \ ; RUN: -r=regular.o,_ZN8DerivedN5printEv,px \ ; RUN: -r=regular.o,_ZTS8DerivedN,p \ ; RUN: -r=regular.o,_ZTI7Derived, \ @@ -136,12 +118,14 @@ 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 !6 +@_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 -@llvm.used = appending global [1 x ptr] [ptr @_ZN8DerivedNC2Ev], section "llvm.metadata" +; 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: @@ -157,16 +141,6 @@ entry: ret void } -define linkonce_odr hidden void @_ZN8DerivedNC2Ev(ptr %this) #0 { -entry: - %this.addr = alloca ptr - store ptr %this, ptr %this.addr - %this1 = load ptr, ptr %this.addr - call void @_ZN7DerivedC2Ev(ptr %this1) - store ptr getelementptr inbounds inrange(-16, 8) ({ [3 x ptr] }, ptr @_ZTV8DerivedN, i32 0, i32 0, i32 2), ptr %this1 - ret void -} - define linkonce_odr hidden void @_ZN8DerivedN5printEv(ptr %this) #0 { entry: ret void @@ -174,15 +148,11 @@ entry: attributes #0 = { noinline optnone } -declare void @__cxa_pure_virtual() declare i1 @llvm.type.test(ptr, metadata) declare void @llvm.assume(i1) -declare void @_ZN7DerivedC2Ev(ptr) !0 = !{i64 16, !"_ZTS7Derived"} !1 = !{i64 16, !"_ZTSM7DerivedFvvE.virtual"} !2 = !{i64 16, !"_ZTS8DerivedN"} !3 = !{i64 16, !"_ZTSM8DerivedNFvvE.virtual"} -;!4 = !{i64 16, !"_ZTS8DerivedN"} -;!5 = !{i64 16, !"_ZTSM8DerivedNFvvE.virtual"} -!6 = !{i64 0} +!4 = !{i64 0}