forked from OSchip/llvm-project
[LTO] Prevent devirtualization for symbols dynamically exported
Identify dynamically exported symbols (--export-dynamic[-symbol=], --dynamic-list=, or definitions needed to preempt shared objects) and prevent their LTO visibility from being upgraded. This helps avoid use of whole program devirtualization when there may be overrides in dynamic libraries. Differential Revision: https://reviews.llvm.org/D91583
This commit is contained in:
parent
6110e7716c
commit
1487747e99
|
@ -247,6 +247,10 @@ void BitcodeCompiler::add(BitcodeFile &f) {
|
||||||
r.VisibleToRegularObj = config->relocatable || sym->isUsedInRegularObj ||
|
r.VisibleToRegularObj = config->relocatable || sym->isUsedInRegularObj ||
|
||||||
(r.Prevailing && sym->includeInDynsym()) ||
|
(r.Prevailing && sym->includeInDynsym()) ||
|
||||||
usedStartStop.count(objSym.getSectionName());
|
usedStartStop.count(objSym.getSectionName());
|
||||||
|
// Identify symbols exported dynamically, and that therefore could be
|
||||||
|
// referenced by a shared library not visible to the linker.
|
||||||
|
r.ExportDynamic = sym->isExportDynamic(sym->kind(), sym->visibility) ||
|
||||||
|
sym->exportDynamic || sym->inDynamicList;
|
||||||
const auto *dr = dyn_cast<Defined>(sym);
|
const auto *dr = dyn_cast<Defined>(sym);
|
||||||
r.FinalDefinitionInLinkageUnit =
|
r.FinalDefinitionInLinkageUnit =
|
||||||
(isExec || sym->visibility != STV_DEFAULT) && dr &&
|
(isExec || sym->visibility != STV_DEFAULT) && dr &&
|
||||||
|
|
|
@ -223,13 +223,13 @@ public:
|
||||||
// non-lazy object causes a runtime error.
|
// non-lazy object causes a runtime error.
|
||||||
void fetch() const;
|
void fetch() const;
|
||||||
|
|
||||||
private:
|
|
||||||
static bool isExportDynamic(Kind k, uint8_t visibility) {
|
static bool isExportDynamic(Kind k, uint8_t visibility) {
|
||||||
if (k == SharedKind)
|
if (k == SharedKind)
|
||||||
return visibility == llvm::ELF::STV_DEFAULT;
|
return visibility == llvm::ELF::STV_DEFAULT;
|
||||||
return config->shared || config->exportDynamic;
|
return config->shared || config->exportDynamic;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
void resolveUndefined(const Undefined &other);
|
void resolveUndefined(const Undefined &other);
|
||||||
void resolveCommon(const CommonSymbol &other);
|
void resolveCommon(const CommonSymbol &other);
|
||||||
void resolveDefined(const Defined &other);
|
void resolveDefined(const Defined &other);
|
||||||
|
|
|
@ -0,0 +1,206 @@
|
||||||
|
; REQUIRES: x86
|
||||||
|
;; Test that dynamically exported symbols prevent devirtualization.
|
||||||
|
|
||||||
|
;; First check that we get devirtualization without any export dynamic options.
|
||||||
|
|
||||||
|
;; Index based WPD
|
||||||
|
;; Generate unsplit module with summary for ThinLTO index-based WPD.
|
||||||
|
; RUN: opt --thinlto-bc -o %t2.o %s
|
||||||
|
; RUN: ld.lld %t2.o -o %t3 -save-temps --lto-whole-program-visibility \
|
||||||
|
; RUN: -mllvm -pass-remarks=. 2>&1 | FileCheck %s --check-prefix=REMARK
|
||||||
|
; RUN: llvm-dis %t2.o.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-IR
|
||||||
|
|
||||||
|
;; Hybrid WPD
|
||||||
|
;; Generate split module with summary for hybrid Thin/Regular LTO WPD.
|
||||||
|
; RUN: opt --thinlto-bc --thinlto-split-lto-unit -o %t.o %s
|
||||||
|
; RUN: ld.lld %t.o -o %t3 -save-temps --lto-whole-program-visibility \
|
||||||
|
; RUN: -mllvm -pass-remarks=. 2>&1 | FileCheck %s --check-prefix=REMARK
|
||||||
|
; RUN: llvm-dis %t.o.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-IR
|
||||||
|
|
||||||
|
;; Regular LTO WPD
|
||||||
|
; RUN: opt -o %t4.o %s
|
||||||
|
; RUN: ld.lld %t4.o -o %t3 -save-temps --lto-whole-program-visibility \
|
||||||
|
; RUN: -mllvm -pass-remarks=. 2>&1 | FileCheck %s --check-prefix=REMARK
|
||||||
|
; RUN: llvm-dis %t3.0.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-IR
|
||||||
|
|
||||||
|
; REMARK-DAG: single-impl: devirtualized a call to _ZN1A1nEi
|
||||||
|
; REMARK-DAG: single-impl: devirtualized a call to _ZN1D1mEi
|
||||||
|
|
||||||
|
;; Check that all WPD fails with --export-dynamic.
|
||||||
|
|
||||||
|
;; Index based WPD
|
||||||
|
; RUN: ld.lld %t2.o -o %t3 -save-temps --lto-whole-program-visibility \
|
||||||
|
; RUN: -mllvm -pass-remarks=. \
|
||||||
|
; RUN: --export-dynamic 2>&1 | FileCheck /dev/null --implicit-check-not single-impl --allow-empty
|
||||||
|
; RUN: llvm-dis %t2.o.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-NODEVIRT-IR
|
||||||
|
|
||||||
|
;; Hybrid WPD
|
||||||
|
; RUN: ld.lld %t.o -o %t3 -save-temps --lto-whole-program-visibility \
|
||||||
|
; RUN: -mllvm -pass-remarks=. \
|
||||||
|
; RUN: --export-dynamic 2>&1 | FileCheck /dev/null --implicit-check-not single-impl --allow-empty
|
||||||
|
; RUN: llvm-dis %t.o.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-NODEVIRT-IR
|
||||||
|
|
||||||
|
;; Regular LTO WPD
|
||||||
|
; RUN: ld.lld %t4.o -o %t3 -save-temps --lto-whole-program-visibility \
|
||||||
|
; RUN: -mllvm -pass-remarks=. \
|
||||||
|
; RUN: --export-dynamic 2>&1 | FileCheck /dev/null --implicit-check-not single-impl --allow-empty
|
||||||
|
; RUN: llvm-dis %t3.0.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-NODEVIRT-IR
|
||||||
|
|
||||||
|
;; Check that WPD fails for target _ZN1D1mEi with --export-dynamic-symbol=_ZTV1D.
|
||||||
|
|
||||||
|
;; Index based WPD
|
||||||
|
; RUN: ld.lld %t2.o -o %t3 -save-temps --lto-whole-program-visibility \
|
||||||
|
; RUN: -mllvm -pass-remarks=. \
|
||||||
|
; RUN: --export-dynamic-symbol=_ZTV1D 2>&1 | FileCheck %s --check-prefix=REMARK-AONLY
|
||||||
|
; RUN: llvm-dis %t2.o.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-AONLY-IR
|
||||||
|
|
||||||
|
;; Hybrid WPD
|
||||||
|
; RUN: ld.lld %t.o -o %t3 -save-temps --lto-whole-program-visibility \
|
||||||
|
; RUN: -mllvm -pass-remarks=. \
|
||||||
|
; RUN: --export-dynamic-symbol=_ZTV1D 2>&1 | FileCheck %s --check-prefix=REMARK-AONLY
|
||||||
|
; RUN: llvm-dis %t.o.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-AONLY-IR
|
||||||
|
|
||||||
|
;; Regular LTO WPD
|
||||||
|
; RUN: ld.lld %t4.o -o %t3 -save-temps --lto-whole-program-visibility \
|
||||||
|
; RUN: -mllvm -pass-remarks=. \
|
||||||
|
; RUN: --export-dynamic-symbol=_ZTV1D 2>&1 | FileCheck %s --check-prefix=REMARK-AONLY
|
||||||
|
; RUN: llvm-dis %t3.0.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-AONLY-IR
|
||||||
|
|
||||||
|
; REMARK-AONLY-NOT: single-impl:
|
||||||
|
; REMARK-AONLY: single-impl: devirtualized a call to _ZN1A1nEi
|
||||||
|
; REMARK-AONLY-NOT: single-impl:
|
||||||
|
|
||||||
|
;; Check that WPD fails for target _ZN1D1mEi with _ZTV1D in --dynamic-list.
|
||||||
|
; RUN: echo "{ _ZTV1D; };" > %t.list
|
||||||
|
|
||||||
|
;; Index based WPD
|
||||||
|
; RUN: ld.lld %t2.o -o %t3 -save-temps --lto-whole-program-visibility \
|
||||||
|
; RUN: -mllvm -pass-remarks=. \
|
||||||
|
; RUN: --dynamic-list=%t.list 2>&1 | FileCheck %s --check-prefix=REMARK-AONLY
|
||||||
|
; RUN: llvm-dis %t2.o.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-AONLY-IR
|
||||||
|
|
||||||
|
;; Hybrid WPD
|
||||||
|
; RUN: ld.lld %t.o -o %t3 -save-temps --lto-whole-program-visibility \
|
||||||
|
; RUN: -mllvm -pass-remarks=. \
|
||||||
|
; RUN: --dynamic-list=%t.list 2>&1 | FileCheck %s --check-prefix=REMARK-AONLY
|
||||||
|
; RUN: llvm-dis %t.o.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-AONLY-IR
|
||||||
|
|
||||||
|
;; Regular LTO WPD
|
||||||
|
; RUN: ld.lld %t4.o -o %t3 -save-temps --lto-whole-program-visibility \
|
||||||
|
; RUN: -mllvm -pass-remarks=. \
|
||||||
|
; RUN: --dynamic-list=%t.list 2>&1 | FileCheck %s --check-prefix=REMARK-AONLY
|
||||||
|
; RUN: llvm-dis %t3.0.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-AONLY-IR
|
||||||
|
|
||||||
|
|
||||||
|
;; Check that all WPD fails with when linking against a shared library containing
|
||||||
|
;; preemptible versions of the vtables. In this case the symbols in the object being
|
||||||
|
;; linked against the shared library must be exported to .dynsym to allow the runtime
|
||||||
|
;; preemption, even without any options.
|
||||||
|
|
||||||
|
;; Index based WPD
|
||||||
|
; RUN: opt -relocation-model=pic -o %t5.o %s
|
||||||
|
; RUN: ld.lld %t5.o -o %t5.so -shared
|
||||||
|
; RUN: ld.lld %t5.o %t5.so -o %t5 -save-temps --lto-whole-program-visibility \
|
||||||
|
; RUN: -mllvm -pass-remarks=. 2>&1 | FileCheck /dev/null --implicit-check-not single-impl --allow-empty
|
||||||
|
|
||||||
|
;; Hybrid WPD
|
||||||
|
; RUN: opt -relocation-model=pic --thinlto-bc -o %t5.o %s
|
||||||
|
; RUN: ld.lld %t5.o -o %t5.so -shared
|
||||||
|
; RUN: ld.lld %t5.o %t5.so -o %t5 -save-temps --lto-whole-program-visibility \
|
||||||
|
; RUN: -mllvm -pass-remarks=. 2>&1 | FileCheck /dev/null --implicit-check-not single-impl --allow-empty
|
||||||
|
|
||||||
|
;; Regular LTO WPD
|
||||||
|
; RUN: opt -relocation-model=pic --thinlto-bc --thinlto-split-lto-unit -o %t5.o %s
|
||||||
|
; RUN: ld.lld %t5.o -o %t5.so -shared
|
||||||
|
; RUN: ld.lld %t5.o %t5.so -o %t5 -save-temps --lto-whole-program-visibility \
|
||||||
|
; RUN: -mllvm -pass-remarks=. 2>&1 | FileCheck /dev/null --implicit-check-not single-impl --allow-empty
|
||||||
|
|
||||||
|
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 }
|
||||||
|
%struct.C = type { %struct.A }
|
||||||
|
%struct.D = type { i32 (...)** }
|
||||||
|
|
||||||
|
@_ZTV1B = linkonce_odr unnamed_addr 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, !vcall_visibility !5
|
||||||
|
@_ZTV1C = linkonce_odr unnamed_addr constant { [4 x i8*] } { [4 x i8*] [i8* null, i8* undef, i8* bitcast (i32 (%struct.C*, i32)* @_ZN1C1fEi to i8*), i8* bitcast (i32 (%struct.A*, i32)* @_ZN1A1nEi to i8*)] }, !type !0, !type !2, !vcall_visibility !5
|
||||||
|
@_ZTV1D = linkonce_odr unnamed_addr constant { [3 x i8*] } { [3 x i8*] [i8* null, i8* undef, i8* bitcast (i32 (%struct.D*, i32)* @_ZN1D1mEi to i8*)] }, !type !3, !vcall_visibility !5
|
||||||
|
|
||||||
|
;; Prevent the vtables from being dead code eliminated.
|
||||||
|
@llvm.used = appending global [3 x i8*] [ i8* bitcast ( { [4 x i8*] }* @_ZTV1B to i8*), i8* bitcast ( { [4 x i8*] }* @_ZTV1C to i8*), i8* bitcast ( { [3 x i8*] }* @_ZTV1D to i8*)]
|
||||||
|
|
||||||
|
; CHECK-IR-LABEL: define dso_local i32 @_start
|
||||||
|
define i32 @_start(%struct.A* %obj, %struct.D* %obj2, 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-IR: %call = tail call i32 @_ZN1A1nEi
|
||||||
|
; CHECK-AONLY-IR: %call = tail call i32 @_ZN1A1nEi
|
||||||
|
; CHECK-NODEVIRT-IR: %call = tail call i32 %fptr1
|
||||||
|
%call = tail call i32 %fptr1(%struct.A* nonnull %obj, i32 %a)
|
||||||
|
|
||||||
|
%3 = bitcast i8** %vtable to i32 (%struct.A*, i32)**
|
||||||
|
%fptr22 = load i32 (%struct.A*, i32)*, i32 (%struct.A*, i32)** %3, align 8
|
||||||
|
|
||||||
|
;; We still have to call it as virtual.
|
||||||
|
; CHECK-IR: %call3 = tail call i32 %fptr22
|
||||||
|
; CHECK-AONLY-IR: %call3 = tail call i32 %fptr22
|
||||||
|
; CHECK-NODEVIRT-IR: %call3 = tail call i32 %fptr22
|
||||||
|
%call3 = tail call i32 %fptr22(%struct.A* nonnull %obj, i32 %call)
|
||||||
|
|
||||||
|
%4 = bitcast %struct.D* %obj2 to i8***
|
||||||
|
%vtable2 = load i8**, i8*** %4
|
||||||
|
%5 = bitcast i8** %vtable2 to i8*
|
||||||
|
%p2 = call i1 @llvm.type.test(i8* %5, metadata !4)
|
||||||
|
call void @llvm.assume(i1 %p2)
|
||||||
|
|
||||||
|
%6 = bitcast i8** %vtable2 to i32 (%struct.D*, i32)**
|
||||||
|
%fptr33 = load i32 (%struct.D*, i32)*, i32 (%struct.D*, i32)** %6, align 8
|
||||||
|
|
||||||
|
;; Check that the call was devirtualized.
|
||||||
|
; CHECK-IR: %call4 = tail call i32 @_ZN1D1mEi
|
||||||
|
; CHECK-AONLY-IR: %call4 = tail call i32 %fptr33
|
||||||
|
; CHECK-NODEVIRT-IR: %call4 = tail call i32 %fptr33
|
||||||
|
%call4 = tail call i32 %fptr33(%struct.D* nonnull %obj2, i32 %call3)
|
||||||
|
ret i32 %call4
|
||||||
|
}
|
||||||
|
; CHECK-IR-LABEL: ret i32
|
||||||
|
; CHECK-IR-LABEL: }
|
||||||
|
|
||||||
|
declare i1 @llvm.type.test(i8*, metadata)
|
||||||
|
declare void @llvm.assume(i1)
|
||||||
|
|
||||||
|
define i32 @_ZN1B1fEi(%struct.B* %this, i32 %a) #0 {
|
||||||
|
ret i32 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
define i32 @_ZN1A1nEi(%struct.A* %this, i32 %a) #0 {
|
||||||
|
ret i32 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
define i32 @_ZN1C1fEi(%struct.C* %this, i32 %a) #0 {
|
||||||
|
ret i32 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
define i32 @_ZN1D1mEi(%struct.D* %this, i32 %a) #0 {
|
||||||
|
ret i32 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
;; Make sure we don't inline or otherwise optimize out the direct calls.
|
||||||
|
attributes #0 = { noinline optnone }
|
||||||
|
|
||||||
|
!0 = !{i64 16, !"_ZTS1A"}
|
||||||
|
!1 = !{i64 16, !"_ZTS1B"}
|
||||||
|
!2 = !{i64 16, !"_ZTS1C"}
|
||||||
|
!3 = !{i64 16, !4}
|
||||||
|
!4 = distinct !{}
|
||||||
|
!5 = !{i64 0}
|
|
@ -1,27 +1,24 @@
|
||||||
; REQUIRES: x86
|
; REQUIRES: x86
|
||||||
;; Test that --lto-whole-program-visibility enables devirtualization.
|
;; Test that --lto-whole-program-visibility enables devirtualization.
|
||||||
|
|
||||||
;; Note that the --export-dynamic used below is simply to ensure symbols are
|
|
||||||
;; retained during linking.
|
|
||||||
|
|
||||||
;; Index based WPD
|
;; Index based WPD
|
||||||
;; Generate unsplit module with summary for ThinLTO index-based WPD.
|
;; Generate unsplit module with summary for ThinLTO index-based WPD.
|
||||||
; RUN: opt --thinlto-bc -o %t2.o %s
|
; RUN: opt --thinlto-bc -o %t2.o %s
|
||||||
; RUN: ld.lld %t2.o -o %t3 -save-temps --lto-whole-program-visibility \
|
; RUN: ld.lld %t2.o -o %t3 -save-temps --lto-whole-program-visibility \
|
||||||
; RUN: -mllvm -pass-remarks=. --export-dynamic 2>&1 | FileCheck %s --check-prefix=REMARK
|
; RUN: -mllvm -pass-remarks=. 2>&1 | FileCheck %s --check-prefix=REMARK
|
||||||
; RUN: llvm-dis %t2.o.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-IR
|
; RUN: llvm-dis %t2.o.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-IR
|
||||||
|
|
||||||
;; Hybrid WPD
|
;; Hybrid WPD
|
||||||
;; Generate split module with summary for hybrid Thin/Regular LTO WPD.
|
;; Generate split module with summary for hybrid Thin/Regular LTO WPD.
|
||||||
; RUN: opt --thinlto-bc --thinlto-split-lto-unit -o %t.o %s
|
; RUN: opt --thinlto-bc --thinlto-split-lto-unit -o %t.o %s
|
||||||
; RUN: ld.lld %t.o -o %t3 -save-temps --lto-whole-program-visibility \
|
; RUN: ld.lld %t.o -o %t3 -save-temps --lto-whole-program-visibility \
|
||||||
; RUN: -mllvm -pass-remarks=. --export-dynamic 2>&1 | FileCheck %s --check-prefix=REMARK
|
; RUN: -mllvm -pass-remarks=. 2>&1 | FileCheck %s --check-prefix=REMARK
|
||||||
; RUN: llvm-dis %t.o.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-IR
|
; RUN: llvm-dis %t.o.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-IR
|
||||||
|
|
||||||
;; Regular LTO WPD
|
;; Regular LTO WPD
|
||||||
; RUN: opt -o %t4.o %s
|
; RUN: opt -o %t4.o %s
|
||||||
; RUN: ld.lld %t4.o -o %t3 -save-temps --lto-whole-program-visibility \
|
; RUN: ld.lld %t4.o -o %t3 -save-temps --lto-whole-program-visibility \
|
||||||
; RUN: -mllvm -pass-remarks=. --export-dynamic 2>&1 | FileCheck %s --check-prefix=REMARK
|
; RUN: -mllvm -pass-remarks=. 2>&1 | FileCheck %s --check-prefix=REMARK
|
||||||
; RUN: llvm-dis %t3.0.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-IR
|
; RUN: llvm-dis %t3.0.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-IR
|
||||||
|
|
||||||
; REMARK-DAG: single-impl: devirtualized a call to _ZN1A1nEi
|
; REMARK-DAG: single-impl: devirtualized a call to _ZN1A1nEi
|
||||||
|
@ -32,21 +29,25 @@
|
||||||
|
|
||||||
;; Index based WPD
|
;; Index based WPD
|
||||||
; RUN: ld.lld %t2.o -o %t3 -save-temps \
|
; RUN: ld.lld %t2.o -o %t3 -save-temps \
|
||||||
; RUN: -mllvm -pass-remarks=. --export-dynamic 2>&1 | FileCheck %s --implicit-check-not single-impl --allow-empty
|
; RUN: -mllvm -pass-remarks=. \
|
||||||
|
; RUN: 2>&1 | FileCheck /dev/null --implicit-check-not single-impl --allow-empty
|
||||||
; RUN: llvm-dis %t2.o.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-NODEVIRT-IR
|
; RUN: llvm-dis %t2.o.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-NODEVIRT-IR
|
||||||
;; Ensure --no-lto-whole-program-visibility overrides explicit --lto-whole-program-visibility.
|
;; Ensure --no-lto-whole-program-visibility overrides explicit --lto-whole-program-visibility.
|
||||||
; RUN: ld.lld %t2.o -o %t3 -save-temps --lto-whole-program-visibility --no-lto-whole-program-visibility \
|
; RUN: ld.lld %t2.o -o %t3 -save-temps --lto-whole-program-visibility --no-lto-whole-program-visibility \
|
||||||
; RUN: -mllvm -pass-remarks=. --export-dynamic 2>&1 | FileCheck %s --implicit-check-not single-impl --allow-empty
|
; RUN: -mllvm -pass-remarks=. \
|
||||||
|
; RUN: 2>&1 | FileCheck /dev/null --implicit-check-not single-impl --allow-empty
|
||||||
; RUN: llvm-dis %t2.o.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-NODEVIRT-IR
|
; RUN: llvm-dis %t2.o.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-NODEVIRT-IR
|
||||||
|
|
||||||
;; Hybrid WPD
|
;; Hybrid WPD
|
||||||
; RUN: ld.lld %t.o -o %t3 -save-temps \
|
; RUN: ld.lld %t.o -o %t3 -save-temps \
|
||||||
; RUN: -mllvm -pass-remarks=. --export-dynamic 2>&1 | FileCheck %s --implicit-check-not single-impl --allow-empty
|
; RUN: -mllvm -pass-remarks=. \
|
||||||
|
; RUN: 2>&1 | FileCheck /dev/null --implicit-check-not single-impl --allow-empty
|
||||||
; RUN: llvm-dis %t.o.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-NODEVIRT-IR
|
; RUN: llvm-dis %t.o.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-NODEVIRT-IR
|
||||||
|
|
||||||
;; Regular LTO WPD
|
;; Regular LTO WPD
|
||||||
; RUN: ld.lld %t4.o -o %t3 -save-temps \
|
; RUN: ld.lld %t4.o -o %t3 -save-temps \
|
||||||
; RUN: -mllvm -pass-remarks=. --export-dynamic 2>&1 | FileCheck %s --implicit-check-not single-impl --allow-empty
|
; RUN: -mllvm -pass-remarks=. \
|
||||||
|
; RUN: 2>&1 | FileCheck /dev/null --implicit-check-not single-impl --allow-empty
|
||||||
; RUN: llvm-dis %t3.0.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-NODEVIRT-IR
|
; RUN: llvm-dis %t3.0.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-NODEVIRT-IR
|
||||||
|
|
||||||
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
|
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
|
||||||
|
@ -61,6 +62,8 @@ target triple = "x86_64-grtev4-linux-gnu"
|
||||||
@_ZTV1C = constant { [4 x i8*] } { [4 x i8*] [i8* null, i8* undef, i8* bitcast (i32 (%struct.C*, i32)* @_ZN1C1fEi to i8*), i8* bitcast (i32 (%struct.A*, i32)* @_ZN1A1nEi to i8*)] }, !type !0, !type !2, !vcall_visibility !5
|
@_ZTV1C = constant { [4 x i8*] } { [4 x i8*] [i8* null, i8* undef, i8* bitcast (i32 (%struct.C*, i32)* @_ZN1C1fEi to i8*), i8* bitcast (i32 (%struct.A*, i32)* @_ZN1A1nEi to i8*)] }, !type !0, !type !2, !vcall_visibility !5
|
||||||
@_ZTV1D = constant { [3 x i8*] } { [3 x i8*] [i8* null, i8* undef, i8* bitcast (i32 (%struct.D*, i32)* @_ZN1D1mEi to i8*)] }, !type !3, !vcall_visibility !5
|
@_ZTV1D = constant { [3 x i8*] } { [3 x i8*] [i8* null, i8* undef, i8* bitcast (i32 (%struct.D*, i32)* @_ZN1D1mEi to i8*)] }, !type !3, !vcall_visibility !5
|
||||||
|
|
||||||
|
; Prevent the vtables from being dead code eliminated.
|
||||||
|
@llvm.used = appending global [3 x i8*] [ i8* bitcast ( { [4 x i8*] }* @_ZTV1B to i8*), i8* bitcast ( { [4 x i8*] }* @_ZTV1C to i8*), i8* bitcast ( { [3 x i8*] }* @_ZTV1D to i8*)]
|
||||||
|
|
||||||
; CHECK-IR-LABEL: define dso_local i32 @_start
|
; CHECK-IR-LABEL: define dso_local i32 @_start
|
||||||
define i32 @_start(%struct.A* %obj, %struct.D* %obj2, i32 %a) {
|
define i32 @_start(%struct.A* %obj, %struct.D* %obj2, i32 %a) {
|
||||||
|
|
|
@ -364,6 +364,10 @@ private:
|
||||||
/// summary).
|
/// summary).
|
||||||
bool VisibleOutsideSummary = false;
|
bool VisibleOutsideSummary = false;
|
||||||
|
|
||||||
|
/// The symbol was exported dynamically, and therefore could be referenced
|
||||||
|
/// by a shared library not visible to the linker.
|
||||||
|
bool ExportDynamic = false;
|
||||||
|
|
||||||
bool UnnamedAddr = true;
|
bool UnnamedAddr = true;
|
||||||
|
|
||||||
/// True if module contains the prevailing definition.
|
/// True if module contains the prevailing definition.
|
||||||
|
@ -434,6 +438,10 @@ private:
|
||||||
|
|
||||||
// Use Optional to distinguish false from not yet initialized.
|
// Use Optional to distinguish false from not yet initialized.
|
||||||
Optional<bool> EnableSplitLTOUnit;
|
Optional<bool> EnableSplitLTOUnit;
|
||||||
|
|
||||||
|
// Identify symbols exported dynamically, and that therefore could be
|
||||||
|
// referenced by a shared library not visible to the linker.
|
||||||
|
DenseSet<GlobalValue::GUID> DynamicExportSymbols;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The resolution for a symbol. The linker must provide a SymbolResolution for
|
/// The resolution for a symbol. The linker must provide a SymbolResolution for
|
||||||
|
@ -441,7 +449,7 @@ private:
|
||||||
struct SymbolResolution {
|
struct SymbolResolution {
|
||||||
SymbolResolution()
|
SymbolResolution()
|
||||||
: Prevailing(0), FinalDefinitionInLinkageUnit(0), VisibleToRegularObj(0),
|
: Prevailing(0), FinalDefinitionInLinkageUnit(0), VisibleToRegularObj(0),
|
||||||
LinkerRedefined(0) {}
|
ExportDynamic(0), LinkerRedefined(0) {}
|
||||||
|
|
||||||
/// The linker has chosen this definition of the symbol.
|
/// The linker has chosen this definition of the symbol.
|
||||||
unsigned Prevailing : 1;
|
unsigned Prevailing : 1;
|
||||||
|
@ -453,6 +461,10 @@ struct SymbolResolution {
|
||||||
/// The definition of this symbol is visible outside of the LTO unit.
|
/// The definition of this symbol is visible outside of the LTO unit.
|
||||||
unsigned VisibleToRegularObj : 1;
|
unsigned VisibleToRegularObj : 1;
|
||||||
|
|
||||||
|
/// The symbol was exported dynamically, and therefore could be referenced
|
||||||
|
/// by a shared library not visible to the linker.
|
||||||
|
unsigned ExportDynamic : 1;
|
||||||
|
|
||||||
/// Linker redefined version of the symbol which appeared in -wrap or -defsym
|
/// Linker redefined version of the symbol which appeared in -wrap or -defsym
|
||||||
/// linker option.
|
/// linker option.
|
||||||
unsigned LinkerRedefined : 1;
|
unsigned LinkerRedefined : 1;
|
||||||
|
|
|
@ -239,10 +239,12 @@ struct VTableSlotSummary {
|
||||||
uint64_t ByteOffset;
|
uint64_t ByteOffset;
|
||||||
};
|
};
|
||||||
|
|
||||||
void updateVCallVisibilityInModule(Module &M,
|
void updateVCallVisibilityInModule(
|
||||||
bool WholeProgramVisibilityEnabledInLTO);
|
Module &M, bool WholeProgramVisibilityEnabledInLTO,
|
||||||
void updateVCallVisibilityInIndex(ModuleSummaryIndex &Index,
|
const DenseSet<GlobalValue::GUID> &DynamicExportSymbols);
|
||||||
bool WholeProgramVisibilityEnabledInLTO);
|
void updateVCallVisibilityInIndex(
|
||||||
|
ModuleSummaryIndex &Index, bool WholeProgramVisibilityEnabledInLTO,
|
||||||
|
const DenseSet<GlobalValue::GUID> &DynamicExportSymbols);
|
||||||
|
|
||||||
/// Perform index-based whole program devirtualization on the \p Summary
|
/// Perform index-based whole program devirtualization on the \p Summary
|
||||||
/// index. Any devirtualized targets used by a type test in another module
|
/// index. Any devirtualized targets used by a type test in another module
|
||||||
|
|
|
@ -577,6 +577,8 @@ void LTO::addModuleToGlobalRes(ArrayRef<InputFile::Symbol> Syms,
|
||||||
// from a module that does not have a summary.
|
// from a module that does not have a summary.
|
||||||
GlobalRes.VisibleOutsideSummary |=
|
GlobalRes.VisibleOutsideSummary |=
|
||||||
(Res.VisibleToRegularObj || Sym.isUsed() || !InSummary);
|
(Res.VisibleToRegularObj || Sym.isUsed() || !InSummary);
|
||||||
|
|
||||||
|
GlobalRes.ExportDynamic |= Res.ExportDynamic;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -977,6 +979,9 @@ Error LTO::run(AddStreamFn AddStream, NativeObjectCache Cache) {
|
||||||
if (Res.second.VisibleOutsideSummary && Res.second.Prevailing)
|
if (Res.second.VisibleOutsideSummary && Res.second.Prevailing)
|
||||||
GUIDPreservedSymbols.insert(GUID);
|
GUIDPreservedSymbols.insert(GUID);
|
||||||
|
|
||||||
|
if (Res.second.ExportDynamic)
|
||||||
|
DynamicExportSymbols.insert(GUID);
|
||||||
|
|
||||||
GUIDPrevailingResolutions[GUID] =
|
GUIDPrevailingResolutions[GUID] =
|
||||||
Res.second.Prevailing ? PrevailingType::Yes : PrevailingType::No;
|
Res.second.Prevailing ? PrevailingType::Yes : PrevailingType::No;
|
||||||
}
|
}
|
||||||
|
@ -1061,7 +1066,8 @@ Error LTO::runRegularLTO(AddStreamFn AddStream) {
|
||||||
// If allowed, upgrade public vcall visibility metadata to linkage unit
|
// If allowed, upgrade public vcall visibility metadata to linkage unit
|
||||||
// visibility before whole program devirtualization in the optimizer.
|
// visibility before whole program devirtualization in the optimizer.
|
||||||
updateVCallVisibilityInModule(*RegularLTO.CombinedModule,
|
updateVCallVisibilityInModule(*RegularLTO.CombinedModule,
|
||||||
Conf.HasWholeProgramVisibility);
|
Conf.HasWholeProgramVisibility,
|
||||||
|
DynamicExportSymbols);
|
||||||
|
|
||||||
if (Conf.PreOptModuleHook &&
|
if (Conf.PreOptModuleHook &&
|
||||||
!Conf.PreOptModuleHook(0, *RegularLTO.CombinedModule))
|
!Conf.PreOptModuleHook(0, *RegularLTO.CombinedModule))
|
||||||
|
@ -1409,7 +1415,8 @@ Error LTO::runThinLTO(AddStreamFn AddStream, NativeObjectCache Cache,
|
||||||
// If allowed, upgrade public vcall visibility to linkage unit visibility in
|
// If allowed, upgrade public vcall visibility to linkage unit visibility in
|
||||||
// the summaries before whole program devirtualization below.
|
// the summaries before whole program devirtualization below.
|
||||||
updateVCallVisibilityInIndex(ThinLTO.CombinedIndex,
|
updateVCallVisibilityInIndex(ThinLTO.CombinedIndex,
|
||||||
Conf.HasWholeProgramVisibility);
|
Conf.HasWholeProgramVisibility,
|
||||||
|
DynamicExportSymbols);
|
||||||
|
|
||||||
// Perform index-based WPD. This will return immediately if there are
|
// Perform index-based WPD. This will return immediately if there are
|
||||||
// no index entries in the typeIdMetadata map (e.g. if we are instead
|
// no index entries in the typeIdMetadata map (e.g. if we are instead
|
||||||
|
|
|
@ -553,7 +553,10 @@ bool LTOCodeGenerator::optimize() {
|
||||||
// via the internal option. Must be done before WPD invoked via the optimizer
|
// via the internal option. Must be done before WPD invoked via the optimizer
|
||||||
// pipeline run below.
|
// pipeline run below.
|
||||||
updateVCallVisibilityInModule(*MergedModule,
|
updateVCallVisibilityInModule(*MergedModule,
|
||||||
/* WholeProgramVisibilityEnabledInLTO */ false);
|
/* WholeProgramVisibilityEnabledInLTO */ false,
|
||||||
|
// FIXME: This needs linker information via a
|
||||||
|
// TBD new interface.
|
||||||
|
/* DynamicExportSymbols */ {});
|
||||||
|
|
||||||
// We always run the verifier once on the merged module, the `DisableVerify`
|
// We always run the verifier once on the merged module, the `DisableVerify`
|
||||||
// parameter only applies to subsequent verify.
|
// parameter only applies to subsequent verify.
|
||||||
|
|
|
@ -1009,7 +1009,10 @@ void ThinLTOCodeGenerator::run() {
|
||||||
// linker option in the old LTO API, but this call allows it to be specified
|
// linker option in the old LTO API, but this call allows it to be specified
|
||||||
// via the internal option. Must be done before WPD below.
|
// via the internal option. Must be done before WPD below.
|
||||||
updateVCallVisibilityInIndex(*Index,
|
updateVCallVisibilityInIndex(*Index,
|
||||||
/* WholeProgramVisibilityEnabledInLTO */ false);
|
/* WholeProgramVisibilityEnabledInLTO */ false,
|
||||||
|
// FIXME: This needs linker information via a
|
||||||
|
// TBD new interface.
|
||||||
|
/* DynamicExportSymbols */ {});
|
||||||
|
|
||||||
// Perform index-based WPD. This will return immediately if there are
|
// Perform index-based WPD. This will return immediately if there are
|
||||||
// no index entries in the typeIdMetadata map (e.g. if we are instead
|
// no index entries in the typeIdMetadata map (e.g. if we are instead
|
||||||
|
|
|
@ -777,8 +777,9 @@ namespace llvm {
|
||||||
/// If whole program visibility asserted, then upgrade all public vcall
|
/// If whole program visibility asserted, then upgrade all public vcall
|
||||||
/// visibility metadata on vtable definitions to linkage unit visibility in
|
/// visibility metadata on vtable definitions to linkage unit visibility in
|
||||||
/// Module IR (for regular or hybrid LTO).
|
/// Module IR (for regular or hybrid LTO).
|
||||||
void updateVCallVisibilityInModule(Module &M,
|
void updateVCallVisibilityInModule(
|
||||||
bool WholeProgramVisibilityEnabledInLTO) {
|
Module &M, bool WholeProgramVisibilityEnabledInLTO,
|
||||||
|
const DenseSet<GlobalValue::GUID> &DynamicExportSymbols) {
|
||||||
if (!hasWholeProgramVisibility(WholeProgramVisibilityEnabledInLTO))
|
if (!hasWholeProgramVisibility(WholeProgramVisibilityEnabledInLTO))
|
||||||
return;
|
return;
|
||||||
for (GlobalVariable &GV : M.globals())
|
for (GlobalVariable &GV : M.globals())
|
||||||
|
@ -786,22 +787,29 @@ void updateVCallVisibilityInModule(Module &M,
|
||||||
// the vtable definitions. We won't have an existing vcall_visibility
|
// the vtable definitions. We won't have an existing vcall_visibility
|
||||||
// metadata on vtable definitions with public visibility.
|
// metadata on vtable definitions with public visibility.
|
||||||
if (GV.hasMetadata(LLVMContext::MD_type) &&
|
if (GV.hasMetadata(LLVMContext::MD_type) &&
|
||||||
GV.getVCallVisibility() == GlobalObject::VCallVisibilityPublic)
|
GV.getVCallVisibility() == GlobalObject::VCallVisibilityPublic &&
|
||||||
|
// Don't upgrade the visibility for symbols exported to the dynamic
|
||||||
|
// linker, as we have no information on their eventual use.
|
||||||
|
!DynamicExportSymbols.count(GV.getGUID()))
|
||||||
GV.setVCallVisibilityMetadata(GlobalObject::VCallVisibilityLinkageUnit);
|
GV.setVCallVisibilityMetadata(GlobalObject::VCallVisibilityLinkageUnit);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If whole program visibility asserted, then upgrade all public vcall
|
/// If whole program visibility asserted, then upgrade all public vcall
|
||||||
/// visibility metadata on vtable definition summaries to linkage unit
|
/// visibility metadata on vtable definition summaries to linkage unit
|
||||||
/// visibility in Module summary index (for ThinLTO).
|
/// visibility in Module summary index (for ThinLTO).
|
||||||
void updateVCallVisibilityInIndex(ModuleSummaryIndex &Index,
|
void updateVCallVisibilityInIndex(
|
||||||
bool WholeProgramVisibilityEnabledInLTO) {
|
ModuleSummaryIndex &Index, bool WholeProgramVisibilityEnabledInLTO,
|
||||||
|
const DenseSet<GlobalValue::GUID> &DynamicExportSymbols) {
|
||||||
if (!hasWholeProgramVisibility(WholeProgramVisibilityEnabledInLTO))
|
if (!hasWholeProgramVisibility(WholeProgramVisibilityEnabledInLTO))
|
||||||
return;
|
return;
|
||||||
for (auto &P : Index) {
|
for (auto &P : Index) {
|
||||||
for (auto &S : P.second.SummaryList) {
|
for (auto &S : P.second.SummaryList) {
|
||||||
auto *GVar = dyn_cast<GlobalVarSummary>(S.get());
|
auto *GVar = dyn_cast<GlobalVarSummary>(S.get());
|
||||||
if (!GVar || GVar->vTableFuncs().empty() ||
|
if (!GVar || GVar->vTableFuncs().empty() ||
|
||||||
GVar->getVCallVisibility() != GlobalObject::VCallVisibilityPublic)
|
GVar->getVCallVisibility() != GlobalObject::VCallVisibilityPublic ||
|
||||||
|
// Don't upgrade the visibility for symbols exported to the dynamic
|
||||||
|
// linker, as we have no information on their eventual use.
|
||||||
|
DynamicExportSymbols.count(P.first))
|
||||||
continue;
|
continue;
|
||||||
GVar->setVCallVisibility(GlobalObject::VCallVisibilityLinkageUnit);
|
GVar->setVCallVisibility(GlobalObject::VCallVisibilityLinkageUnit);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,217 @@
|
||||||
|
;; Test that --export-dynamic[-symbol] and --dynamic-list prevents devirtualization.
|
||||||
|
|
||||||
|
;; First check that we get devirtualization without any export dynamic options.
|
||||||
|
|
||||||
|
;; Index based WPD
|
||||||
|
;; Generate unsplit module with summary for ThinLTO index-based WPD.
|
||||||
|
; RUN: opt -thinlto-bc -o %t2.o %s
|
||||||
|
; RUN: %gold -m elf_x86_64 -plugin %llvmshlibdir/LLVMgold%shlibext \
|
||||||
|
; RUN: --plugin-opt=whole-program-visibility \
|
||||||
|
; RUN: --plugin-opt=save-temps \
|
||||||
|
; RUN: --plugin-opt=-pass-remarks=. \
|
||||||
|
; RUN: %t2.o -o %t3 2>&1 | FileCheck %s --check-prefix=REMARK
|
||||||
|
; RUN: llvm-dis %t2.o.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-IR
|
||||||
|
|
||||||
|
;; Hybrid WPD
|
||||||
|
;; Generate split module with summary for hybrid Thin/Regular LTO WPD.
|
||||||
|
; RUN: opt -thinlto-bc -thinlto-split-lto-unit -o %t.o %s
|
||||||
|
; RUN: %gold -m elf_x86_64 -plugin %llvmshlibdir/LLVMgold%shlibext \
|
||||||
|
; RUN: --plugin-opt=whole-program-visibility \
|
||||||
|
; RUN: --plugin-opt=save-temps \
|
||||||
|
; RUN: --plugin-opt=-pass-remarks=. \
|
||||||
|
; RUN: %t.o -o %t3 2>&1 | FileCheck %s --check-prefix=REMARK
|
||||||
|
; RUN: llvm-dis %t.o.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-IR
|
||||||
|
|
||||||
|
;; Regular LTO WPD
|
||||||
|
; RUN: opt -o %t4.o %s
|
||||||
|
; RUN: %gold -m elf_x86_64 -plugin %llvmshlibdir/LLVMgold%shlibext \
|
||||||
|
; RUN: --plugin-opt=whole-program-visibility \
|
||||||
|
; RUN: --plugin-opt=save-temps \
|
||||||
|
; RUN: --plugin-opt=-pass-remarks=. \
|
||||||
|
; RUN: %t4.o -o %t3 2>&1 | FileCheck %s --check-prefix=REMARK
|
||||||
|
; RUN: llvm-dis %t3.0.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-IR
|
||||||
|
|
||||||
|
; REMARK-DAG: single-impl: devirtualized a call to _ZN1A1nEi
|
||||||
|
; REMARK-DAG: single-impl: devirtualized a call to _ZN1D1mEi
|
||||||
|
|
||||||
|
;; Check that all WPD fails with --export-dynamic.
|
||||||
|
|
||||||
|
;; Index based WPD
|
||||||
|
; RUN: %gold -m elf_x86_64 -plugin %llvmshlibdir/LLVMgold%shlibext \
|
||||||
|
; RUN: --plugin-opt=whole-program-visibility \
|
||||||
|
; RUN: --plugin-opt=save-temps \
|
||||||
|
; RUN: --plugin-opt=-pass-remarks=. \
|
||||||
|
; RUN: %t2.o -o %t3 \
|
||||||
|
; RUN: --export-dynamic 2>&1 | FileCheck /dev/null --implicit-check-not single-impl --allow-empty
|
||||||
|
; RUN: llvm-dis %t2.o.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-NODEVIRT-IR
|
||||||
|
|
||||||
|
;; Hybrid WPD
|
||||||
|
; RUN: %gold -m elf_x86_64 -plugin %llvmshlibdir/LLVMgold%shlibext \
|
||||||
|
; RUN: --plugin-opt=whole-program-visibility \
|
||||||
|
; RUN: --plugin-opt=save-temps \
|
||||||
|
; RUN: --plugin-opt=-pass-remarks=. \
|
||||||
|
; RUN: %t.o -o %t3 \
|
||||||
|
; RUN: --export-dynamic 2>&1 | FileCheck /dev/null --implicit-check-not single-impl --allow-empty
|
||||||
|
; RUN: llvm-dis %t.o.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-NODEVIRT-IR
|
||||||
|
|
||||||
|
;; Regular LTO WPD
|
||||||
|
; RUN: %gold -m elf_x86_64 -plugin %llvmshlibdir/LLVMgold%shlibext \
|
||||||
|
; RUN: --plugin-opt=whole-program-visibility \
|
||||||
|
; RUN: --plugin-opt=save-temps \
|
||||||
|
; RUN: --plugin-opt=-pass-remarks=. \
|
||||||
|
; RUN: %t4.o -o %t3 \
|
||||||
|
; RUN: --export-dynamic 2>&1 | FileCheck /dev/null --implicit-check-not single-impl --allow-empty
|
||||||
|
; RUN: llvm-dis %t3.0.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-NODEVIRT-IR
|
||||||
|
|
||||||
|
;; Check that WPD fails for target _ZN1D1mEi with --export-dynamic-symbol=_ZTV1D.
|
||||||
|
|
||||||
|
;; Index based WPD
|
||||||
|
; RUN: %gold -m elf_x86_64 -plugin %llvmshlibdir/LLVMgold%shlibext \
|
||||||
|
; RUN: --plugin-opt=whole-program-visibility \
|
||||||
|
; RUN: --plugin-opt=save-temps \
|
||||||
|
; RUN: --plugin-opt=-pass-remarks=. \
|
||||||
|
; RUN: %t2.o -o %t3 \
|
||||||
|
; RUN: --export-dynamic-symbol=_ZTV1D 2>&1 | FileCheck %s --check-prefix=REMARK-AONLY
|
||||||
|
; RUN: llvm-dis %t2.o.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-AONLY-IR
|
||||||
|
|
||||||
|
;; Hybrid WPD
|
||||||
|
; RUN: %gold -m elf_x86_64 -plugin %llvmshlibdir/LLVMgold%shlibext \
|
||||||
|
; RUN: --plugin-opt=whole-program-visibility \
|
||||||
|
; RUN: --plugin-opt=save-temps \
|
||||||
|
; RUN: --plugin-opt=-pass-remarks=. \
|
||||||
|
; RUN: %t.o -o %t3 \
|
||||||
|
; RUN: --export-dynamic-symbol=_ZTV1D 2>&1 | FileCheck %s --check-prefix=REMARK-AONLY
|
||||||
|
; RUN: llvm-dis %t.o.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-AONLY-IR
|
||||||
|
|
||||||
|
;; Regular LTO WPD
|
||||||
|
; RUN: %gold -m elf_x86_64 -plugin %llvmshlibdir/LLVMgold%shlibext \
|
||||||
|
; RUN: --plugin-opt=whole-program-visibility \
|
||||||
|
; RUN: --plugin-opt=save-temps \
|
||||||
|
; RUN: --plugin-opt=-pass-remarks=. \
|
||||||
|
; RUN: %t4.o -o %t3 \
|
||||||
|
; RUN: --export-dynamic-symbol=_ZTV1D 2>&1 | FileCheck %s --check-prefix=REMARK-AONLY
|
||||||
|
; RUN: llvm-dis %t3.0.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-AONLY-IR
|
||||||
|
|
||||||
|
; REMARK-AONLY-NOT: single-impl:
|
||||||
|
; REMARK-AONLY: single-impl: devirtualized a call to _ZN1A1nEi
|
||||||
|
; REMARK-AONLY-NOT: single-impl:
|
||||||
|
|
||||||
|
;; Check that WPD fails for target _ZN1D1mEi with _ZTV1D in --dynamic-list.
|
||||||
|
; RUN: echo "{ _ZTV1D; };" > %t.list
|
||||||
|
|
||||||
|
;; Index based WPD
|
||||||
|
; RUN: %gold -m elf_x86_64 -plugin %llvmshlibdir/LLVMgold%shlibext \
|
||||||
|
; RUN: --plugin-opt=whole-program-visibility \
|
||||||
|
; RUN: --plugin-opt=save-temps \
|
||||||
|
; RUN: --plugin-opt=-pass-remarks=. \
|
||||||
|
; RUN: %t2.o -o %t3 \
|
||||||
|
; RUN: --dynamic-list=%t.list 2>&1 | FileCheck %s --check-prefix=REMARK-AONLY
|
||||||
|
; RUN: llvm-dis %t2.o.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-AONLY-IR
|
||||||
|
|
||||||
|
;; Hybrid WPD
|
||||||
|
; RUN: %gold -m elf_x86_64 -plugin %llvmshlibdir/LLVMgold%shlibext \
|
||||||
|
; RUN: --plugin-opt=whole-program-visibility \
|
||||||
|
; RUN: --plugin-opt=save-temps \
|
||||||
|
; RUN: --plugin-opt=-pass-remarks=. \
|
||||||
|
; RUN: %t.o -o %t3 \
|
||||||
|
; RUN: --dynamic-list=%t.list 2>&1 | FileCheck %s --check-prefix=REMARK-AONLY
|
||||||
|
; RUN: llvm-dis %t.o.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-AONLY-IR
|
||||||
|
|
||||||
|
;; Regular LTO WPD
|
||||||
|
; RUN: %gold -m elf_x86_64 -plugin %llvmshlibdir/LLVMgold%shlibext \
|
||||||
|
; RUN: --plugin-opt=whole-program-visibility \
|
||||||
|
; RUN: --plugin-opt=save-temps \
|
||||||
|
; RUN: --plugin-opt=-pass-remarks=. \
|
||||||
|
; RUN: %t4.o -o %t3 \
|
||||||
|
; RUN: --dynamic-list=%t.list 2>&1 | FileCheck %s --check-prefix=REMARK-AONLY
|
||||||
|
; RUN: llvm-dis %t3.0.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-AONLY-IR
|
||||||
|
|
||||||
|
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 }
|
||||||
|
%struct.C = type { %struct.A }
|
||||||
|
%struct.D = type { i32 (...)** }
|
||||||
|
|
||||||
|
@_ZTV1B = linkonce_odr unnamed_addr 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, !vcall_visibility !5
|
||||||
|
@_ZTV1C = linkonce_odr unnamed_addr constant { [4 x i8*] } { [4 x i8*] [i8* null, i8* undef, i8* bitcast (i32 (%struct.C*, i32)* @_ZN1C1fEi to i8*), i8* bitcast (i32 (%struct.A*, i32)* @_ZN1A1nEi to i8*)] }, !type !0, !type !2, !vcall_visibility !5
|
||||||
|
@_ZTV1D = linkonce_odr unnamed_addr constant { [3 x i8*] } { [3 x i8*] [i8* null, i8* undef, i8* bitcast (i32 (%struct.D*, i32)* @_ZN1D1mEi to i8*)] }, !type !3, !vcall_visibility !5
|
||||||
|
|
||||||
|
;; Prevent the vtables from being dead code eliminated.
|
||||||
|
@llvm.used = appending global [3 x i8*] [ i8* bitcast ( { [4 x i8*] }* @_ZTV1B to i8*), i8* bitcast ( { [4 x i8*] }* @_ZTV1C to i8*), i8* bitcast ( { [3 x i8*] }* @_ZTV1D to i8*)]
|
||||||
|
|
||||||
|
; CHECK-IR-LABEL: define dso_local i32 @_start
|
||||||
|
define i32 @_start(%struct.A* %obj, %struct.D* %obj2, 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-IR: %call = tail call i32 @_ZN1A1nEi
|
||||||
|
; CHECK-AONLY-IR: %call = tail call i32 @_ZN1A1nEi
|
||||||
|
; CHECK-NODEVIRT-IR: %call = tail call i32 %fptr1
|
||||||
|
%call = tail call i32 %fptr1(%struct.A* nonnull %obj, i32 %a)
|
||||||
|
|
||||||
|
%3 = bitcast i8** %vtable to i32 (%struct.A*, i32)**
|
||||||
|
%fptr22 = load i32 (%struct.A*, i32)*, i32 (%struct.A*, i32)** %3, align 8
|
||||||
|
|
||||||
|
;; We still have to call it as virtual.
|
||||||
|
; CHECK-IR: %call3 = tail call i32 %fptr22
|
||||||
|
; CHECK-AONLY-IR: %call3 = tail call i32 %fptr22
|
||||||
|
; CHECK-NODEVIRT-IR: %call3 = tail call i32 %fptr22
|
||||||
|
%call3 = tail call i32 %fptr22(%struct.A* nonnull %obj, i32 %call)
|
||||||
|
|
||||||
|
%4 = bitcast %struct.D* %obj2 to i8***
|
||||||
|
%vtable2 = load i8**, i8*** %4
|
||||||
|
%5 = bitcast i8** %vtable2 to i8*
|
||||||
|
%p2 = call i1 @llvm.type.test(i8* %5, metadata !4)
|
||||||
|
call void @llvm.assume(i1 %p2)
|
||||||
|
|
||||||
|
%6 = bitcast i8** %vtable2 to i32 (%struct.D*, i32)**
|
||||||
|
%fptr33 = load i32 (%struct.D*, i32)*, i32 (%struct.D*, i32)** %6, align 8
|
||||||
|
|
||||||
|
;; Check that the call was devirtualized.
|
||||||
|
; CHECK-IR: %call4 = tail call i32 @_ZN1D1mEi
|
||||||
|
; CHECK-AONLY-IR: %call4 = tail call i32 %fptr33
|
||||||
|
; CHECK-NODEVIRT-IR: %call4 = tail call i32 %fptr33
|
||||||
|
%call4 = tail call i32 %fptr33(%struct.D* nonnull %obj2, i32 %call3)
|
||||||
|
ret i32 %call4
|
||||||
|
}
|
||||||
|
; CHECK-IR-LABEL: ret i32
|
||||||
|
; CHECK-IR-LABEL: }
|
||||||
|
|
||||||
|
declare i1 @llvm.type.test(i8*, metadata)
|
||||||
|
declare void @llvm.assume(i1)
|
||||||
|
|
||||||
|
define i32 @_ZN1B1fEi(%struct.B* %this, i32 %a) #0 {
|
||||||
|
ret i32 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
define i32 @_ZN1A1nEi(%struct.A* %this, i32 %a) #0 {
|
||||||
|
ret i32 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
define i32 @_ZN1C1fEi(%struct.C* %this, i32 %a) #0 {
|
||||||
|
ret i32 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
define i32 @_ZN1D1mEi(%struct.D* %this, i32 %a) #0 {
|
||||||
|
ret i32 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
;; Make sure we don't inline or otherwise optimize out the direct calls.
|
||||||
|
attributes #0 = { noinline optnone }
|
||||||
|
|
||||||
|
!0 = !{i64 16, !"_ZTS1A"}
|
||||||
|
!1 = !{i64 16, !"_ZTS1B"}
|
||||||
|
!2 = !{i64 16, !"_ZTS1C"}
|
||||||
|
!3 = !{i64 16, !4}
|
||||||
|
!4 = distinct !{}
|
||||||
|
!5 = !{i64 0}
|
|
@ -7,8 +7,7 @@
|
||||||
; RUN: --plugin-opt=whole-program-visibility \
|
; RUN: --plugin-opt=whole-program-visibility \
|
||||||
; RUN: --plugin-opt=save-temps \
|
; RUN: --plugin-opt=save-temps \
|
||||||
; RUN: --plugin-opt=-pass-remarks=. \
|
; RUN: --plugin-opt=-pass-remarks=. \
|
||||||
; RUN: %t2.o -o %t3 \
|
; RUN: %t2.o -o %t3 2>&1 | FileCheck %s --check-prefix=REMARK
|
||||||
; RUN: --export-dynamic 2>&1 | FileCheck %s --check-prefix=REMARK
|
|
||||||
; RUN: llvm-dis %t2.o.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-IR
|
; RUN: llvm-dis %t2.o.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-IR
|
||||||
|
|
||||||
;; Hybrid WPD
|
;; Hybrid WPD
|
||||||
|
@ -18,8 +17,7 @@
|
||||||
; RUN: --plugin-opt=whole-program-visibility \
|
; RUN: --plugin-opt=whole-program-visibility \
|
||||||
; RUN: --plugin-opt=save-temps \
|
; RUN: --plugin-opt=save-temps \
|
||||||
; RUN: --plugin-opt=-pass-remarks=. \
|
; RUN: --plugin-opt=-pass-remarks=. \
|
||||||
; RUN: %t.o -o %t3 \
|
; RUN: %t.o -o %t3 2>&1 | FileCheck %s --check-prefix=REMARK
|
||||||
; RUN: --export-dynamic 2>&1 | FileCheck %s --check-prefix=REMARK
|
|
||||||
; RUN: llvm-dis %t.o.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-IR
|
; RUN: llvm-dis %t.o.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-IR
|
||||||
|
|
||||||
;; Regular LTO WPD
|
;; Regular LTO WPD
|
||||||
|
@ -28,8 +26,7 @@
|
||||||
; RUN: --plugin-opt=whole-program-visibility \
|
; RUN: --plugin-opt=whole-program-visibility \
|
||||||
; RUN: --plugin-opt=save-temps \
|
; RUN: --plugin-opt=save-temps \
|
||||||
; RUN: --plugin-opt=-pass-remarks=. \
|
; RUN: --plugin-opt=-pass-remarks=. \
|
||||||
; RUN: %t4.o -o %t3 \
|
; RUN: %t4.o -o %t3 2>&1 | FileCheck %s --check-prefix=REMARK
|
||||||
; RUN: --export-dynamic 2>&1 | FileCheck %s --check-prefix=REMARK
|
|
||||||
; RUN: llvm-dis %t3.0.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-IR
|
; RUN: llvm-dis %t3.0.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-IR
|
||||||
|
|
||||||
; REMARK-DAG: single-impl: devirtualized a call to _ZN1A1nEi
|
; REMARK-DAG: single-impl: devirtualized a call to _ZN1A1nEi
|
||||||
|
@ -43,7 +40,7 @@
|
||||||
; RUN: --plugin-opt=save-temps \
|
; RUN: --plugin-opt=save-temps \
|
||||||
; RUN: --plugin-opt=-pass-remarks=. \
|
; RUN: --plugin-opt=-pass-remarks=. \
|
||||||
; RUN: %t2.o -o %t3 \
|
; RUN: %t2.o -o %t3 \
|
||||||
; RUN: --export-dynamic 2>&1 | FileCheck %s --implicit-check-not single-impl --allow-empty
|
; RUN: 2>&1 | FileCheck /dev/null --implicit-check-not single-impl --allow-empty
|
||||||
; RUN: llvm-dis %t2.o.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-NODEVIRT-IR
|
; RUN: llvm-dis %t2.o.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-NODEVIRT-IR
|
||||||
|
|
||||||
;; Hybrid WPD
|
;; Hybrid WPD
|
||||||
|
@ -51,7 +48,7 @@
|
||||||
; RUN: --plugin-opt=save-temps \
|
; RUN: --plugin-opt=save-temps \
|
||||||
; RUN: --plugin-opt=-pass-remarks=. \
|
; RUN: --plugin-opt=-pass-remarks=. \
|
||||||
; RUN: %t.o -o %t3 \
|
; RUN: %t.o -o %t3 \
|
||||||
; RUN: --export-dynamic 2>&1 | FileCheck %s --implicit-check-not single-impl --allow-empty
|
; RUN: 2>&1 | FileCheck /dev/null --implicit-check-not single-impl --allow-empty
|
||||||
; RUN: llvm-dis %t.o.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-NODEVIRT-IR
|
; RUN: llvm-dis %t.o.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-NODEVIRT-IR
|
||||||
|
|
||||||
;; Regular LTO WPD
|
;; Regular LTO WPD
|
||||||
|
@ -59,7 +56,7 @@
|
||||||
; RUN: --plugin-opt=save-temps \
|
; RUN: --plugin-opt=save-temps \
|
||||||
; RUN: --plugin-opt=-pass-remarks=. \
|
; RUN: --plugin-opt=-pass-remarks=. \
|
||||||
; RUN: %t4.o -o %t3 \
|
; RUN: %t4.o -o %t3 \
|
||||||
; RUN: --export-dynamic 2>&1 | FileCheck %s --implicit-check-not single-impl --allow-empty
|
; RUN: 2>&1 | FileCheck /dev/null --implicit-check-not single-impl --allow-empty
|
||||||
; RUN: llvm-dis %t3.0.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-NODEVIRT-IR
|
; RUN: llvm-dis %t3.0.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-NODEVIRT-IR
|
||||||
|
|
||||||
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
|
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
|
||||||
|
@ -74,6 +71,8 @@ target triple = "x86_64-grtev4-linux-gnu"
|
||||||
@_ZTV1C = constant { [4 x i8*] } { [4 x i8*] [i8* null, i8* undef, i8* bitcast (i32 (%struct.C*, i32)* @_ZN1C1fEi to i8*), i8* bitcast (i32 (%struct.A*, i32)* @_ZN1A1nEi to i8*)] }, !type !0, !type !2, !vcall_visibility !5
|
@_ZTV1C = constant { [4 x i8*] } { [4 x i8*] [i8* null, i8* undef, i8* bitcast (i32 (%struct.C*, i32)* @_ZN1C1fEi to i8*), i8* bitcast (i32 (%struct.A*, i32)* @_ZN1A1nEi to i8*)] }, !type !0, !type !2, !vcall_visibility !5
|
||||||
@_ZTV1D = constant { [3 x i8*] } { [3 x i8*] [i8* null, i8* undef, i8* bitcast (i32 (%struct.D*, i32)* @_ZN1D1mEi to i8*)] }, !type !3, !vcall_visibility !5
|
@_ZTV1D = constant { [3 x i8*] } { [3 x i8*] [i8* null, i8* undef, i8* bitcast (i32 (%struct.D*, i32)* @_ZN1D1mEi to i8*)] }, !type !3, !vcall_visibility !5
|
||||||
|
|
||||||
|
; Prevent the vtables from being dead code eliminated.
|
||||||
|
@llvm.used = appending global [3 x i8*] [ i8* bitcast ( { [4 x i8*] }* @_ZTV1B to i8*), i8* bitcast ( { [4 x i8*] }* @_ZTV1C to i8*), i8* bitcast ( { [3 x i8*] }* @_ZTV1D to i8*)]
|
||||||
|
|
||||||
; CHECK-IR-LABEL: define dso_local i32 @_start
|
; CHECK-IR-LABEL: define dso_local i32 @_start
|
||||||
define i32 @_start(%struct.A* %obj, %struct.D* %obj2, i32 %a) {
|
define i32 @_start(%struct.A* %obj, %struct.D* %obj2, i32 %a) {
|
||||||
|
|
|
@ -765,6 +765,9 @@ static void addModule(LTO &Lto, claimed_file &F, const void *View,
|
||||||
|
|
||||||
case LDPR_PREVAILING_DEF_IRONLY_EXP:
|
case LDPR_PREVAILING_DEF_IRONLY_EXP:
|
||||||
R.Prevailing = !isUndefined(Sym);
|
R.Prevailing = !isUndefined(Sym);
|
||||||
|
// Identify symbols exported dynamically, and that therefore could be
|
||||||
|
// referenced by a shared library not visible to the linker.
|
||||||
|
R.ExportDynamic = true;
|
||||||
if (!Res.CanOmitFromDynSym)
|
if (!Res.CanOmitFromDynSym)
|
||||||
R.VisibleToRegularObj = true;
|
R.VisibleToRegularObj = true;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -654,7 +654,8 @@ int main(int argc, char **argv) {
|
||||||
// specified by an internal option. This is normally done during LTO which is
|
// specified by an internal option. This is normally done during LTO which is
|
||||||
// not performed via opt.
|
// not performed via opt.
|
||||||
updateVCallVisibilityInModule(*M,
|
updateVCallVisibilityInModule(*M,
|
||||||
/* WholeProgramVisibilityEnabledInLTO */ false);
|
/* WholeProgramVisibilityEnabledInLTO */ false,
|
||||||
|
/* DynamicExportSymbols */ {});
|
||||||
|
|
||||||
// Figure out what stream we are supposed to write to...
|
// Figure out what stream we are supposed to write to...
|
||||||
std::unique_ptr<ToolOutputFile> Out;
|
std::unique_ptr<ToolOutputFile> Out;
|
||||||
|
|
Loading…
Reference in New Issue