forked from OSchip/llvm-project
[WPD] Use new llvm.public.type.test intrinsic for potentially publicly visible classes
Turning on opaque pointers has uncovered an issue with WPD where we currently pattern match away `assume(type.test)` in WPD so that a later LTT doesn't resolve the type test to undef and introduce an `assume(false)`. The pattern matching can fail in cases where we transform two `assume(type.test)`s into `assume(phi(type.test.1, type.test.2))`. Currently we create `assume(type.test)` for all virtual calls that might be devirtualized. This is to support `-Wl,--lto-whole-program-visibility`. To prevent this, all virtual calls that may not be in the same LTO module instead use a new `llvm.public.type.test` intrinsic in place of the `llvm.type.test`. Then when we know if `-Wl,--lto-whole-program-visibility` is passed or not, we can either replace all `llvm.public.type.test` with `llvm.type.test`, or replace all `llvm.public.type.test` with `true`. This prevents WPD from trying to pattern match away `assume(type.test)` for public virtual calls when failing the pattern matching will result in miscompiles. Reviewed By: tejohnson Differential Revision: https://reviews.llvm.org/D128955
This commit is contained in:
parent
e43621b09c
commit
2eade1dba4
|
@ -2698,15 +2698,21 @@ void CodeGenFunction::EmitTypeMetadataCodeForVCall(const CXXRecordDecl *RD,
|
|||
// Don't insert type test assumes if we are forcing public
|
||||
// visibility.
|
||||
!CGM.AlwaysHasLTOVisibilityPublic(RD)) {
|
||||
llvm::Metadata *MD =
|
||||
CGM.CreateMetadataIdentifierForType(QualType(RD->getTypeForDecl(), 0));
|
||||
QualType Ty = QualType(RD->getTypeForDecl(), 0);
|
||||
llvm::Metadata *MD = CGM.CreateMetadataIdentifierForType(Ty);
|
||||
llvm::Value *TypeId =
|
||||
llvm::MetadataAsValue::get(CGM.getLLVMContext(), MD);
|
||||
|
||||
llvm::Value *CastedVTable = Builder.CreateBitCast(VTable, Int8PtrTy);
|
||||
// If we already know that the call has hidden LTO visibility, emit
|
||||
// @llvm.type.test(). Otherwise emit @llvm.public.type.test(), which WPD
|
||||
// will convert to @llvm.type.test() if we assert at link time that we have
|
||||
// whole program visibility.
|
||||
llvm::Intrinsic::ID IID = CGM.HasHiddenLTOVisibility(RD)
|
||||
? llvm::Intrinsic::type_test
|
||||
: llvm::Intrinsic::public_type_test;
|
||||
llvm::Value *TypeTest =
|
||||
Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::type_test),
|
||||
{CastedVTable, TypeId});
|
||||
Builder.CreateCall(CGM.getIntrinsic(IID), {CastedVTable, TypeId});
|
||||
Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::assume), TypeTest);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -707,8 +707,12 @@ CGCallee ItaniumCXXABI::EmitLoadOfMemberFunctionPointer(
|
|||
if (ShouldEmitCFICheck || ShouldEmitWPDInfo) {
|
||||
llvm::Value *VFPAddr =
|
||||
Builder.CreateGEP(CGF.Int8Ty, VTable, VTableOffset);
|
||||
llvm::Intrinsic::ID IID = CGM.HasHiddenLTOVisibility(RD)
|
||||
? llvm::Intrinsic::type_test
|
||||
: llvm::Intrinsic::public_type_test;
|
||||
|
||||
CheckResult = Builder.CreateCall(
|
||||
CGM.getIntrinsic(llvm::Intrinsic::type_test),
|
||||
CGM.getIntrinsic(IID),
|
||||
{Builder.CreateBitCast(VFPAddr, CGF.Int8PtrTy), TypeId});
|
||||
}
|
||||
|
||||
|
|
|
@ -10,11 +10,12 @@ struct B3 : B2 {};
|
|||
struct S : B1, B3 {};
|
||||
|
||||
// DEFAULT-NOT: llvm.type.test
|
||||
// DEFAULT-NOT: llvm.public.type.test
|
||||
|
||||
void f(S *s, void (S::*p)()) {
|
||||
// WPV: [[OFFSET:%.*]] = sub i64 {{.*}}, 1
|
||||
// WPV: [[VFPTR:%.*]] = getelementptr i8, i8* %{{.*}}, i64 [[OFFSET]]
|
||||
// WPV: [[TT:%.*]] = call i1 @llvm.type.test(i8* [[VFPTR]], metadata !"_ZTSM1SFvvE.virtual")
|
||||
// WPV: [[TT:%.*]] = call i1 @llvm.public.type.test(i8* [[VFPTR]], metadata !"_ZTSM1SFvvE.virtual")
|
||||
// CHECK: [[OFFSET:%.*]] = sub i64 {{.*}}, 1
|
||||
// CHECK: [[VFPTR:%.*]] = getelementptr i8, i8* %{{.*}}, i64 [[OFFSET]]
|
||||
// CHECK: [[TT:%.*]] = call i1 @llvm.type.test(i8* [[VFPTR]], metadata !"_ZTSM1SFvvE.virtual")
|
||||
|
|
|
@ -13,21 +13,19 @@
|
|||
|
||||
// The pre-link bitcode produced by clang should contain a type test assume
|
||||
// sequence.
|
||||
// TT: [[TTREG:%[0-9]+]] = call i1 @llvm.type.test({{.*}}, metadata !"_ZTS1A")
|
||||
// TT: [[TTREG:%[0-9]+]] = call i1 @llvm.public.type.test({{.*}}, metadata !"_ZTS1A")
|
||||
// TT: void @llvm.assume(i1 [[TTREG]])
|
||||
|
||||
// The ThinLTO backend optimized bitcode should not have any type test assume
|
||||
// sequences.
|
||||
// The ThinLTO backend optimized bitcode should not have any type tests.
|
||||
// OPT-NOT: @llvm.type.test
|
||||
// OPT-NOT: call void @llvm.assume
|
||||
// OPT-NOT: @llvm.public.type.test
|
||||
// We should have only one @llvm.assume call, the one that was expanded
|
||||
// from the builtin in the IR below, not the one fed by the type test.
|
||||
// OPT: %cmp = icmp ne %struct.A* %{{.*}}, null
|
||||
// OPT: void @llvm.assume(i1 %cmp)
|
||||
// Check after the builtin assume again that we don't have a type test assume
|
||||
// sequence.
|
||||
// Check after the builtin assume again that we don't have any type tests
|
||||
// OPT-NOT: @llvm.type.test
|
||||
// OPT-NOT: call void @llvm.assume
|
||||
// OPT-NOT: @llvm.public.type.test
|
||||
|
||||
// NM: T _Z2afP1A
|
||||
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
; REQUIRES: x86-registered-target
|
||||
|
||||
; Check that we properly update @llvm.public.type.test with distributed ThinLTO.
|
||||
|
||||
; RUN: opt -thinlto-bc -o %t.o %s
|
||||
|
||||
; RUN: llvm-lto2 run -thinlto-distributed-indexes %t.o \
|
||||
; RUN: -o %t2.index \
|
||||
; RUN: -r=%t.o,f,px
|
||||
|
||||
; RUN: %clang_cc1 -triple x86_64-grtev4-linux-gnu \
|
||||
; RUN: -emit-obj -fthinlto-index=%t.o.thinlto.bc \
|
||||
; RUN: -o %t.native.o -x ir %t.o --save-temps=obj
|
||||
; RUN: llvm-dis %t.native.o.0.preopt.bc -o - | FileCheck %s --check-prefix=PUBLIC
|
||||
|
||||
; RUN: llvm-lto2 run -thinlto-distributed-indexes %t.o --whole-program-visibility \
|
||||
; RUN: -o %t2.index \
|
||||
; RUN: -r=%t.o,f,px
|
||||
|
||||
; RUN: %clang_cc1 -triple x86_64-grtev4-linux-gnu \
|
||||
; RUN: -emit-obj -fthinlto-index=%t.o.thinlto.bc \
|
||||
; RUN: -o %t.native.o -x ir %t.o --save-temps=obj
|
||||
; RUN: llvm-dis %t.native.o.0.preopt.bc -o - | FileCheck %s --check-prefix=HIDDEN
|
||||
|
||||
; PUBLIC-NOT: call {{.*}}@llvm.public.type.test
|
||||
; PUBLIC-NOT: call {{.*}}@llvm.type.test
|
||||
; PUBLIC: call void @llvm.assume(i1 true)
|
||||
|
||||
; HIDDEN-NOT: call {{.*}}@llvm.public.type.test
|
||||
; HIDDEN: call {{.*}}@llvm.type.test
|
||||
|
||||
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"
|
||||
|
||||
define i32 @f(ptr %vtable) {
|
||||
entry:
|
||||
%p = call i1 @llvm.public.type.test(ptr %vtable, metadata !"_ZTS1A")
|
||||
call void @llvm.assume(i1 %p)
|
||||
ret i32 0
|
||||
}
|
||||
|
||||
declare void @llvm.assume(i1)
|
||||
declare i1 @llvm.public.type.test(ptr, metadata)
|
|
@ -1,12 +1,12 @@
|
|||
// Tests for the cfi-vcall feature:
|
||||
// RUN: %clang_cc1 -no-opaque-pointers -flto -flto-unit -triple x86_64-unknown-linux -fvisibility hidden -fsanitize=cfi-vcall -fsanitize-trap=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CFI --check-prefix=CFI-NVT --check-prefix=ITANIUM --check-prefix=TT-ITANIUM --check-prefix=NDIAG %s
|
||||
// RUN: %clang_cc1 -no-opaque-pointers -flto -flto-unit -triple x86_64-unknown-linux -fvisibility hidden -fsanitize=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CFI --check-prefix=CFI-NVT --check-prefix=ITANIUM --check-prefix=TT-ITANIUM --check-prefix=ITANIUM-DIAG --check-prefix=DIAG --check-prefix=DIAG-ABORT %s
|
||||
// RUN: %clang_cc1 -no-opaque-pointers -flto -flto-unit -triple x86_64-unknown-linux -fvisibility hidden -fsanitize=cfi-vcall -fsanitize-recover=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CFI --check-prefix=CFI-NVT --check-prefix=ITANIUM --check-prefix=TT-ITANIUM --check-prefix=ITANIUM-DIAG --check-prefix=DIAG --check-prefix=DIAG-RECOVER %s
|
||||
// RUN: %clang_cc1 -no-opaque-pointers -flto -flto-unit -triple x86_64-unknown-linux -fvisibility hidden -fsanitize=cfi-vcall -fsanitize-trap=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CFI --check-prefix=CFI-NVT --check-prefix=ITANIUM --check-prefix=TT-ITANIUM-HIDDEN --check-prefix=NDIAG %s
|
||||
// RUN: %clang_cc1 -no-opaque-pointers -flto -flto-unit -triple x86_64-unknown-linux -fvisibility hidden -fsanitize=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CFI --check-prefix=CFI-NVT --check-prefix=ITANIUM --check-prefix=TT-ITANIUM-HIDDEN --check-prefix=ITANIUM-DIAG --check-prefix=DIAG --check-prefix=DIAG-ABORT %s
|
||||
// RUN: %clang_cc1 -no-opaque-pointers -flto -flto-unit -triple x86_64-unknown-linux -fvisibility hidden -fsanitize=cfi-vcall -fsanitize-recover=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CFI --check-prefix=CFI-NVT --check-prefix=ITANIUM --check-prefix=TT-ITANIUM-HIDDEN --check-prefix=ITANIUM-DIAG --check-prefix=DIAG --check-prefix=DIAG-RECOVER %s
|
||||
// RUN: %clang_cc1 -no-opaque-pointers -flto -flto-unit -triple x86_64-pc-windows-msvc -fsanitize=cfi-vcall -fsanitize-trap=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CFI --check-prefix=CFI-NVT --check-prefix=MS --check-prefix=TT-MS --check-prefix=NDIAG %s
|
||||
|
||||
// Tests for the whole-program-vtables feature:
|
||||
// RUN: %clang_cc1 -no-opaque-pointers -flto -flto-unit -triple x86_64-unknown-linux -fvisibility hidden -fwhole-program-vtables -emit-llvm -o - %s | FileCheck --check-prefix=VTABLE-OPT --check-prefix=ITANIUM --check-prefix=TT-ITANIUM %s
|
||||
// RUN: %clang_cc1 -no-opaque-pointers -flto -flto-unit -triple x86_64-unknown-linux -fwhole-program-vtables -emit-llvm -o - %s | FileCheck --check-prefix=VTABLE-OPT --check-prefix=ITANIUM-DEFAULTVIS --check-prefix=TT-ITANIUM %s
|
||||
// RUN: %clang_cc1 -no-opaque-pointers -flto -flto-unit -triple x86_64-unknown-linux -fvisibility hidden -fwhole-program-vtables -emit-llvm -o - %s | FileCheck --check-prefix=VTABLE-OPT --check-prefix=ITANIUM --check-prefix=TT-ITANIUM-HIDDEN %s
|
||||
// RUN: %clang_cc1 -no-opaque-pointers -flto -flto-unit -triple x86_64-unknown-linux -fwhole-program-vtables -emit-llvm -o - %s | FileCheck --check-prefix=VTABLE-OPT --check-prefix=ITANIUM-DEFAULTVIS --check-prefix=TT-ITANIUM-DEFAULT %s
|
||||
// RUN: %clang_cc1 -no-opaque-pointers -O2 -flto -flto-unit -triple x86_64-unknown-linux -fwhole-program-vtables -emit-llvm -o - %s | FileCheck --check-prefix=ITANIUM-OPT %s
|
||||
// RUN: %clang_cc1 -no-opaque-pointers -flto -flto-unit -triple x86_64-pc-windows-msvc -fwhole-program-vtables -emit-llvm -o - %s | FileCheck --check-prefix=VTABLE-OPT --check-prefix=MS --check-prefix=TT-MS %s
|
||||
|
||||
|
@ -141,7 +141,8 @@ void D::h() {
|
|||
// ITANIUM-DEFAULTVIS: define{{.*}} void @_Z2afP1A
|
||||
// MS: define dso_local void @"?af@@YAXPEAUA@@@Z"
|
||||
void af(A *a) {
|
||||
// TT-ITANIUM: [[P:%[^ ]*]] = call i1 @llvm.type.test(i8* [[VT:%[^ ]*]], metadata !"_ZTS1A")
|
||||
// TT-ITANIUM-HIDDEN: [[P:%[^ ]*]] = call i1 @llvm.type.test(i8* [[VT:%[^ ]*]], metadata !"_ZTS1A")
|
||||
// TT-ITANIUM-DEFAULT: [[P:%[^ ]*]] = call i1 @llvm.public.type.test(i8* [[VT:%[^ ]*]], metadata !"_ZTS1A")
|
||||
// TT-MS: [[P:%[^ ]*]] = call i1 @llvm.type.test(i8* [[VT:%[^ ]*]], metadata !"?AUA@@")
|
||||
// TC-ITANIUM: [[PAIR:%[^ ]*]] = call { i8*, i1 } @llvm.type.checked.load(i8* {{%[^ ]*}}, i32 0, metadata !"_ZTS1A")
|
||||
// TC-MS: [[PAIR:%[^ ]*]] = call { i8*, i1 } @llvm.type.checked.load(i8* {{%[^ ]*}}, i32 0, metadata !"?AUA@@")
|
||||
|
@ -173,7 +174,8 @@ void af(A *a) {
|
|||
// ITANIUM: define internal void @_Z3df1PN12_GLOBAL__N_11DE
|
||||
// MS: define internal void @"?df1@@YAXPEAUD@?A0x{{[^@]*}}@@@Z"
|
||||
void df1(D *d) {
|
||||
// TT-ITANIUM: {{%[^ ]*}} = call i1 @llvm.type.test(i8* {{%[^ ]*}}, metadata ![[DTYPE:[0-9]+]])
|
||||
// TT-ITANIUM-HIDDEN: {{%[^ ]*}} = call i1 @llvm.type.test(i8* {{%[^ ]*}}, metadata ![[DTYPE:[0-9]+]])
|
||||
// TT-ITANIUM-DEFAULT: {{%[^ ]*}} = call i1 @llvm.type.test(i8* {{%[^ ]*}}, metadata ![[DTYPE:[0-9]+]])
|
||||
// TT-MS: {{%[^ ]*}} = call i1 @llvm.type.test(i8* {{%[^ ]*}}, metadata !"?AUA@@")
|
||||
// TC-ITANIUM: {{%[^ ]*}} = call { i8*, i1 } @llvm.type.checked.load(i8* {{%[^ ]*}}, i32 0, metadata ![[DTYPE:[0-9]+]])
|
||||
// TC-MS: {{%[^ ]*}} = call { i8*, i1 } @llvm.type.checked.load(i8* {{%[^ ]*}}, i32 0, metadata !"?AUA@@")
|
||||
|
@ -183,7 +185,8 @@ void df1(D *d) {
|
|||
// ITANIUM: define internal void @_Z3dg1PN12_GLOBAL__N_11DE
|
||||
// MS: define internal void @"?dg1@@YAXPEAUD@?A0x{{[^@]*}}@@@Z"
|
||||
void dg1(D *d) {
|
||||
// TT-ITANIUM: {{%[^ ]*}} = call i1 @llvm.type.test(i8* {{%[^ ]*}}, metadata !"_ZTS1B")
|
||||
// TT-ITANIUM-HIDDEN: {{%[^ ]*}} = call i1 @llvm.type.test(i8* {{%[^ ]*}}, metadata !"_ZTS1B")
|
||||
// TT-ITANIUM-DEFAULT: {{%[^ ]*}} = call i1 @llvm.public.type.test(i8* {{%[^ ]*}}, metadata !"_ZTS1B")
|
||||
// TT-MS: {{%[^ ]*}} = call i1 @llvm.type.test(i8* {{%[^ ]*}}, metadata !"?AUB@@")
|
||||
// TC-ITANIUM: {{%[^ ]*}} = call { i8*, i1 } @llvm.type.checked.load(i8* {{%[^ ]*}}, i32 8, metadata !"_ZTS1B")
|
||||
// TC-MS: {{%[^ ]*}} = call { i8*, i1 } @llvm.type.checked.load(i8* {{%[^ ]*}}, i32 0, metadata !"?AUB@@")
|
||||
|
@ -193,7 +196,8 @@ void dg1(D *d) {
|
|||
// ITANIUM: define internal void @_Z3dh1PN12_GLOBAL__N_11DE
|
||||
// MS: define internal void @"?dh1@@YAXPEAUD@?A0x{{[^@]*}}@@@Z"
|
||||
void dh1(D *d) {
|
||||
// TT-ITANIUM: {{%[^ ]*}} = call i1 @llvm.type.test(i8* {{%[^ ]*}}, metadata ![[DTYPE]])
|
||||
// TT-ITANIUM-HIDDEN: {{%[^ ]*}} = call i1 @llvm.type.test(i8* {{%[^ ]*}}, metadata ![[DTYPE]])
|
||||
// TT-ITANIUM-DEFAULT: {{%[^ ]*}} = call i1 @llvm.type.test(i8* {{%[^ ]*}}, metadata ![[DTYPE]])
|
||||
// TT-MS: {{%[^ ]*}} = call i1 @llvm.type.test(i8* {{%[^ ]*}}, metadata ![[DTYPE:[0-9]+]])
|
||||
// TC-ITANIUM: {{%[^ ]*}} = call { i8*, i1 } @llvm.type.checked.load(i8* {{%[^ ]*}}, i32 16, metadata ![[DTYPE]])
|
||||
// TC-MS: {{%[^ ]*}} = call { i8*, i1 } @llvm.type.checked.load(i8* {{%[^ ]*}}, i32 8, metadata ![[DTYPE:[0-9]+]])
|
||||
|
@ -252,7 +256,8 @@ struct D : C {
|
|||
// ITANIUM-DEFAULTVIS: define{{.*}} void @_ZN5test21fEPNS_1DE
|
||||
// MS: define dso_local void @"?f@test2@@YAXPEAUD@1@@Z"
|
||||
void f(D *d) {
|
||||
// TT-ITANIUM: {{%[^ ]*}} = call i1 @llvm.type.test(i8* {{%[^ ]*}}, metadata !"_ZTSN5test21DE")
|
||||
// TT-ITANIUM-HIDDEN: {{%[^ ]*}} = call i1 @llvm.type.test(i8* {{%[^ ]*}}, metadata !"_ZTSN5test21DE")
|
||||
// TT-ITANIUM-DEFAULT: {{%[^ ]*}} = call i1 @llvm.public.type.test(i8* {{%[^ ]*}}, metadata !"_ZTSN5test21DE")
|
||||
// TT-MS: {{%[^ ]*}} = call i1 @llvm.type.test(i8* {{%[^ ]*}}, metadata !"?AUA@test2@@")
|
||||
// TC-ITANIUM: {{%[^ ]*}} = call { i8*, i1 } @llvm.type.checked.load(i8* {{%[^ ]*}}, i32 8, metadata !"_ZTSN5test21DE")
|
||||
// TC-MS: {{%[^ ]*}} = call { i8*, i1 } @llvm.type.checked.load(i8* {{%[^ ]*}}, i32 0, metadata !"?AUA@test2@@")
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
; REQUIRES: x86
|
||||
|
||||
; Check that we RAUW llvm.public.type.test with either llvm.type.test when --lto-whole-program-visibility is specified, or with true otherwise.
|
||||
|
||||
; RUN: opt --thinlto-bc -o %t.o %s
|
||||
; RUN: ld.lld %t.o -o %t2.o --save-temps
|
||||
; RUN: llvm-dis %t.o.0.preopt.bc -o - | FileCheck %s --check-prefix=PUB
|
||||
; RUN: ld.lld %t.o -o %t3.o --save-temps --lto-whole-program-visibility
|
||||
; RUN: llvm-dis %t.o.0.preopt.bc -o - | FileCheck %s --check-prefix=WPV
|
||||
|
||||
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"
|
||||
|
||||
declare i1 @llvm.public.type.test(ptr, metadata)
|
||||
declare void @llvm.assume(i1)
|
||||
|
||||
; PUB-NOT: call {{.*}}@llvm.public.type.test
|
||||
; PUB-NOT: call {{.*}}@llvm.type.test
|
||||
; PUB: call {{.*}}@llvm.assume(i1 true)
|
||||
; WPV: call {{.*}}@llvm.type.test
|
||||
; WPV: call {{.*}}@llvm.assume
|
||||
|
||||
define void @f(ptr %a) {
|
||||
%i = call i1 @llvm.public.type.test(ptr %a, metadata !"_ZTS1A")
|
||||
call void @llvm.assume(i1 %i)
|
||||
ret void
|
||||
}
|
|
@ -1759,6 +1759,12 @@ def int_type_checked_load : DefaultAttrsIntrinsic<[llvm_ptr_ty, llvm_i1_ty],
|
|||
[llvm_ptr_ty, llvm_i32_ty, llvm_metadata_ty],
|
||||
[IntrNoMem, IntrWillReturn]>;
|
||||
|
||||
// Test whether a pointer is associated with a type metadata identifier. Used
|
||||
// for public visibility classes that may later be refined to private
|
||||
// visibility.
|
||||
def int_public_type_test : DefaultAttrsIntrinsic<[llvm_i1_ty], [llvm_ptr_ty, llvm_metadata_ty],
|
||||
[IntrNoMem, IntrWillReturn, IntrSpeculatable]>;
|
||||
|
||||
// Create a branch funnel that implements an indirect call to a limited set of
|
||||
// callees. This needs to be a musttail call.
|
||||
def int_icall_branch_funnel : DefaultAttrsIntrinsic<[], [llvm_vararg_ty], []>;
|
||||
|
|
|
@ -1122,6 +1122,9 @@ private:
|
|||
/// every summary of a GV is synchronized.
|
||||
bool WithDSOLocalPropagation = false;
|
||||
|
||||
/// Indicates that we have whole program visibility.
|
||||
bool WithWholeProgramVisibility = false;
|
||||
|
||||
/// Indicates that summary-based synthetic entry count propagation has run
|
||||
bool HasSyntheticEntryCounts = false;
|
||||
|
||||
|
@ -1280,6 +1283,9 @@ public:
|
|||
bool withDSOLocalPropagation() const { return WithDSOLocalPropagation; }
|
||||
void setWithDSOLocalPropagation() { WithDSOLocalPropagation = true; }
|
||||
|
||||
bool withWholeProgramVisibility() const { return WithWholeProgramVisibility; }
|
||||
void setWithWholeProgramVisibility() { WithWholeProgramVisibility = true; }
|
||||
|
||||
bool isReadOnly(const GlobalVarSummary *GVS) const {
|
||||
return WithAttributePropagation && GVS->maybeReadOnly();
|
||||
}
|
||||
|
|
|
@ -102,6 +102,9 @@ struct LTOCodeGenerator {
|
|||
|
||||
void setShouldInternalize(bool Value) { ShouldInternalize = Value; }
|
||||
void setShouldEmbedUselists(bool Value) { ShouldEmbedUselists = Value; }
|
||||
void setSaveIRBeforeOptPath(std::string Value) {
|
||||
SaveIRBeforeOptPath = Value;
|
||||
}
|
||||
|
||||
/// Restore linkage of globals
|
||||
///
|
||||
|
@ -237,6 +240,7 @@ private:
|
|||
bool ShouldRestoreGlobalsLinkage = false;
|
||||
std::unique_ptr<ToolOutputFile> DiagnosticOutputFile;
|
||||
std::unique_ptr<ToolOutputFile> StatsFile = nullptr;
|
||||
std::string SaveIRBeforeOptPath;
|
||||
|
||||
lto::Config Config;
|
||||
};
|
||||
|
|
|
@ -239,7 +239,9 @@ struct VTableSlotSummary {
|
|||
StringRef TypeID;
|
||||
uint64_t ByteOffset;
|
||||
};
|
||||
|
||||
bool hasWholeProgramVisibility(bool WholeProgramVisibilityEnabledInLTO);
|
||||
void updatePublicTypeTestCalls(Module &M,
|
||||
bool WholeProgramVisibilityEnabledInLTO);
|
||||
void updateVCallVisibilityInModule(
|
||||
Module &M, bool WholeProgramVisibilityEnabledInLTO,
|
||||
const DenseSet<GlobalValue::GUID> &DynamicExportSymbols);
|
||||
|
|
|
@ -164,7 +164,8 @@ static void addIntrinsicToSummary(
|
|||
SetVector<FunctionSummary::ConstVCall> &TypeCheckedLoadConstVCalls,
|
||||
DominatorTree &DT) {
|
||||
switch (CI->getCalledFunction()->getIntrinsicID()) {
|
||||
case Intrinsic::type_test: {
|
||||
case Intrinsic::type_test:
|
||||
case Intrinsic::public_type_test: {
|
||||
auto *TypeMDVal = cast<MetadataAsValue>(CI->getArgOperand(1));
|
||||
auto *TypeId = dyn_cast<MDString>(TypeMDVal->getMetadata());
|
||||
if (!TypeId)
|
||||
|
|
|
@ -75,7 +75,9 @@ void llvm::findDevirtualizableCallsForTypeTest(
|
|||
SmallVectorImpl<DevirtCallSite> &DevirtCalls,
|
||||
SmallVectorImpl<CallInst *> &Assumes, const CallInst *CI,
|
||||
DominatorTree &DT) {
|
||||
assert(CI->getCalledFunction()->getIntrinsicID() == Intrinsic::type_test);
|
||||
assert(CI->getCalledFunction()->getIntrinsicID() == Intrinsic::type_test ||
|
||||
CI->getCalledFunction()->getIntrinsicID() ==
|
||||
Intrinsic::public_type_test);
|
||||
|
||||
const Module *M = CI->getParent()->getParent()->getParent();
|
||||
|
||||
|
|
|
@ -7788,7 +7788,7 @@ static Expected<bool> getEnableSplitLTOUnitFlag(BitstreamCursor &Stream,
|
|||
case bitc::FS_FLAGS: { // [flags]
|
||||
uint64_t Flags = Record[0];
|
||||
// Scan flags.
|
||||
assert(Flags <= 0x7f && "Unexpected bits in flag");
|
||||
assert(Flags <= 0xff && "Unexpected bits in flag");
|
||||
|
||||
return Flags & 0x8;
|
||||
}
|
||||
|
|
|
@ -105,11 +105,13 @@ uint64_t ModuleSummaryIndex::getFlags() const {
|
|||
Flags |= 0x20;
|
||||
if (withDSOLocalPropagation())
|
||||
Flags |= 0x40;
|
||||
if (withWholeProgramVisibility())
|
||||
Flags |= 0x80;
|
||||
return Flags;
|
||||
}
|
||||
|
||||
void ModuleSummaryIndex::setFlags(uint64_t Flags) {
|
||||
assert(Flags <= 0x7f && "Unexpected bits in flag");
|
||||
assert(Flags <= 0xff && "Unexpected bits in flag");
|
||||
// 1 bit: WithGlobalValueDeadStripping flag.
|
||||
// Set on combined index only.
|
||||
if (Flags & 0x1)
|
||||
|
@ -139,6 +141,10 @@ void ModuleSummaryIndex::setFlags(uint64_t Flags) {
|
|||
// Set on combined index only.
|
||||
if (Flags & 0x40)
|
||||
setWithDSOLocalPropagation();
|
||||
// 1 bit: WithWholeProgramVisibility flag.
|
||||
// Set on combined index only.
|
||||
if (Flags & 0x80)
|
||||
setWithWholeProgramVisibility();
|
||||
}
|
||||
|
||||
// Collect for the given module the list of function it defines
|
||||
|
|
|
@ -1103,6 +1103,8 @@ Error LTO::runRegularLTO(AddStreamFn AddStream) {
|
|||
updateVCallVisibilityInModule(*RegularLTO.CombinedModule,
|
||||
Conf.HasWholeProgramVisibility,
|
||||
DynamicExportSymbols);
|
||||
updatePublicTypeTestCalls(*RegularLTO.CombinedModule,
|
||||
Conf.HasWholeProgramVisibility);
|
||||
|
||||
if (Conf.PreOptModuleHook &&
|
||||
!Conf.PreOptModuleHook(0, *RegularLTO.CombinedModule))
|
||||
|
@ -1482,6 +1484,8 @@ Error LTO::runThinLTO(AddStreamFn AddStream, FileCache Cache,
|
|||
|
||||
std::set<GlobalValue::GUID> ExportedGUIDs;
|
||||
|
||||
if (hasWholeProgramVisibility(Conf.HasWholeProgramVisibility))
|
||||
ThinLTO.CombinedIndex.setWithWholeProgramVisibility();
|
||||
// If allowed, upgrade public vcall visibility to linkage unit visibility in
|
||||
// the summaries before whole program devirtualization below.
|
||||
updateVCallVisibilityInIndex(ThinLTO.CombinedIndex,
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
#include "llvm/Support/ToolOutputFile.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include "llvm/Target/TargetMachine.h"
|
||||
#include "llvm/Transforms/IPO/WholeProgramDevirt.h"
|
||||
#include "llvm/Transforms/Scalar/LoopPassManager.h"
|
||||
#include "llvm/Transforms/Utils/FunctionImportUtils.h"
|
||||
#include "llvm/Transforms/Utils/SplitModule.h"
|
||||
|
@ -560,6 +561,8 @@ Error lto::thinBackend(const Config &Conf, unsigned Task, AddStreamFn AddStream,
|
|||
// the module, if applicable.
|
||||
Mod.setPartialSampleProfileRatio(CombinedIndex);
|
||||
|
||||
updatePublicTypeTestCalls(Mod, CombinedIndex.withWholeProgramVisibility());
|
||||
|
||||
if (Conf.CodeGenOnly) {
|
||||
codegen(Conf, TM.get(), AddStream, Task, Mod, CombinedIndex);
|
||||
return finalizeOptimizationRemarks(std::move(DiagnosticOutputFile));
|
||||
|
|
|
@ -520,6 +520,8 @@ bool LTOCodeGenerator::optimize() {
|
|||
// linker option in the old LTO API, but this call allows it to be specified
|
||||
// via the internal option. Must be done before WPD invoked via the optimizer
|
||||
// pipeline run below.
|
||||
updatePublicTypeTestCalls(*MergedModule,
|
||||
/* WholeProgramVisibilityEnabledInLTO */ false);
|
||||
updateVCallVisibilityInModule(*MergedModule,
|
||||
/* WholeProgramVisibilityEnabledInLTO */ false,
|
||||
// FIXME: This needs linker information via a
|
||||
|
@ -539,6 +541,16 @@ bool LTOCodeGenerator::optimize() {
|
|||
// Add an appropriate DataLayout instance for this module...
|
||||
MergedModule->setDataLayout(TargetMach->createDataLayout());
|
||||
|
||||
if (!SaveIRBeforeOptPath.empty()) {
|
||||
std::error_code EC;
|
||||
raw_fd_ostream OS(SaveIRBeforeOptPath, EC, sys::fs::OF_None);
|
||||
if (EC)
|
||||
report_fatal_error(Twine("Failed to open ") + SaveIRBeforeOptPath +
|
||||
" to save optimized bitcode\n");
|
||||
WriteBitcodeToFile(*MergedModule, OS,
|
||||
/* ShouldPreserveUseListOrder */ true);
|
||||
}
|
||||
|
||||
ModuleSummaryIndex CombinedIndex(false);
|
||||
TargetMach = createTargetMachine();
|
||||
if (!opt(Config, TargetMach.get(), 0, *MergedModule, /*IsThinLTO=*/false,
|
||||
|
|
|
@ -452,6 +452,10 @@ ProcessThinLTOModule(Module &TheModule, ModuleSummaryIndex &Index,
|
|||
bool DisableCodeGen, StringRef SaveTempsDir,
|
||||
bool Freestanding, unsigned OptLevel, unsigned count,
|
||||
bool DebugPassManager) {
|
||||
// See comment at call to updateVCallVisibilityInIndex() for why
|
||||
// WholeProgramVisibilityEnabledInLTO is false.
|
||||
updatePublicTypeTestCalls(TheModule,
|
||||
/* WholeProgramVisibilityEnabledInLTO */ false);
|
||||
|
||||
// "Benchmark"-like optimization: single-source case
|
||||
bool SingleModule = (ModuleMap.size() == 1);
|
||||
|
@ -1047,6 +1051,8 @@ void ThinLTOCodeGenerator::run() {
|
|||
// Currently there is no support for enabling whole program visibility via a
|
||||
// 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.
|
||||
if (hasWholeProgramVisibility(/* WholeProgramVisibilityEnabledInLTO */ false))
|
||||
Index->setWithWholeProgramVisibility();
|
||||
updateVCallVisibilityInIndex(*Index,
|
||||
/* WholeProgramVisibilityEnabledInLTO */ false,
|
||||
// FIXME: This needs linker information via a
|
||||
|
|
|
@ -1778,35 +1778,48 @@ void LowerTypeTestsModule::replaceDirectCalls(Value *Old, Value *New) {
|
|||
Old->replaceUsesWithIf(New, isDirectCall);
|
||||
}
|
||||
|
||||
static void dropTypeTests(Module &M, Function &TypeTestFunc) {
|
||||
for (Use &U : llvm::make_early_inc_range(TypeTestFunc.uses())) {
|
||||
auto *CI = cast<CallInst>(U.getUser());
|
||||
// Find and erase llvm.assume intrinsics for this llvm.type.test call.
|
||||
for (Use &CIU : llvm::make_early_inc_range(CI->uses()))
|
||||
if (auto *Assume = dyn_cast<AssumeInst>(CIU.getUser()))
|
||||
Assume->eraseFromParent();
|
||||
// If the assume was merged with another assume, we might have a use on a
|
||||
// phi (which will feed the assume). Simply replace the use on the phi
|
||||
// with "true" and leave the merged assume.
|
||||
if (!CI->use_empty()) {
|
||||
assert(
|
||||
all_of(CI->users(), [](User *U) -> bool { return isa<PHINode>(U); }));
|
||||
CI->replaceAllUsesWith(ConstantInt::getTrue(M.getContext()));
|
||||
}
|
||||
CI->eraseFromParent();
|
||||
}
|
||||
}
|
||||
|
||||
bool LowerTypeTestsModule::lower() {
|
||||
Function *TypeTestFunc =
|
||||
M.getFunction(Intrinsic::getName(Intrinsic::type_test));
|
||||
|
||||
if (DropTypeTests && TypeTestFunc) {
|
||||
for (Use &U : llvm::make_early_inc_range(TypeTestFunc->uses())) {
|
||||
auto *CI = cast<CallInst>(U.getUser());
|
||||
// Find and erase llvm.assume intrinsics for this llvm.type.test call.
|
||||
for (Use &CIU : llvm::make_early_inc_range(CI->uses()))
|
||||
if (auto *Assume = dyn_cast<AssumeInst>(CIU.getUser()))
|
||||
Assume->eraseFromParent();
|
||||
// If the assume was merged with another assume, we might have a use on a
|
||||
// phi (which will feed the assume). Simply replace the use on the phi
|
||||
// with "true" and leave the merged assume.
|
||||
if (!CI->use_empty()) {
|
||||
assert(all_of(CI->users(),
|
||||
[](User *U) -> bool { return isa<PHINode>(U); }));
|
||||
CI->replaceAllUsesWith(ConstantInt::getTrue(M.getContext()));
|
||||
}
|
||||
CI->eraseFromParent();
|
||||
if (DropTypeTests) {
|
||||
if (TypeTestFunc)
|
||||
dropTypeTests(M, *TypeTestFunc);
|
||||
// Normally we'd have already removed all @llvm.public.type.test calls,
|
||||
// except for in the case where we originally were performing ThinLTO but
|
||||
// decided not to in the backend.
|
||||
Function *PublicTypeTestFunc =
|
||||
M.getFunction(Intrinsic::getName(Intrinsic::public_type_test));
|
||||
if (PublicTypeTestFunc)
|
||||
dropTypeTests(M, *PublicTypeTestFunc);
|
||||
if (TypeTestFunc || PublicTypeTestFunc) {
|
||||
// We have deleted the type intrinsics, so we no longer have enough
|
||||
// information to reason about the liveness of virtual function pointers
|
||||
// in GlobalDCE.
|
||||
for (GlobalVariable &GV : M.globals())
|
||||
GV.eraseMetadata(LLVMContext::MD_vcall_visibility);
|
||||
return true;
|
||||
}
|
||||
|
||||
// We have deleted the type intrinsics, so we no longer have enough
|
||||
// information to reason about the liveness of virtual function pointers
|
||||
// in GlobalDCE.
|
||||
for (GlobalVariable &GV : M.globals())
|
||||
GV.eraseMetadata(LLVMContext::MD_vcall_visibility);
|
||||
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// If only some of the modules were split, we cannot correctly perform
|
||||
|
|
|
@ -132,6 +132,14 @@ void promoteTypeIds(Module &M, StringRef ModuleId) {
|
|||
}
|
||||
}
|
||||
|
||||
if (Function *PublicTypeTestFunc =
|
||||
M.getFunction(Intrinsic::getName(Intrinsic::public_type_test))) {
|
||||
for (const Use &U : PublicTypeTestFunc->uses()) {
|
||||
auto CI = cast<CallInst>(U.getUser());
|
||||
ExternalizeTypeId(CI, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (Function *TypeCheckedLoadFunc =
|
||||
M.getFunction(Intrinsic::getName(Intrinsic::type_checked_load))) {
|
||||
for (const Use &U : TypeCheckedLoadFunc->uses()) {
|
||||
|
|
|
@ -773,15 +773,14 @@ PreservedAnalyses WholeProgramDevirtPass::run(Module &M,
|
|||
return PreservedAnalyses::none();
|
||||
}
|
||||
|
||||
namespace llvm {
|
||||
// Enable whole program visibility if enabled by client (e.g. linker) or
|
||||
// internal option, and not force disabled.
|
||||
static bool hasWholeProgramVisibility(bool WholeProgramVisibilityEnabledInLTO) {
|
||||
bool hasWholeProgramVisibility(bool WholeProgramVisibilityEnabledInLTO) {
|
||||
return (WholeProgramVisibilityEnabledInLTO || WholeProgramVisibility) &&
|
||||
!DisableWholeProgramVisibility;
|
||||
}
|
||||
|
||||
namespace llvm {
|
||||
|
||||
/// If whole program visibility asserted, then upgrade all public vcall
|
||||
/// visibility metadata on vtable definitions to linkage unit visibility in
|
||||
/// Module IR (for regular or hybrid LTO).
|
||||
|
@ -790,7 +789,7 @@ void updateVCallVisibilityInModule(
|
|||
const DenseSet<GlobalValue::GUID> &DynamicExportSymbols) {
|
||||
if (!hasWholeProgramVisibility(WholeProgramVisibilityEnabledInLTO))
|
||||
return;
|
||||
for (GlobalVariable &GV : M.globals())
|
||||
for (GlobalVariable &GV : M.globals()) {
|
||||
// Add linkage unit visibility to any variable with type metadata, which are
|
||||
// the vtable definitions. We won't have an existing vcall_visibility
|
||||
// metadata on vtable definitions with public visibility.
|
||||
|
@ -800,6 +799,34 @@ void updateVCallVisibilityInModule(
|
|||
// linker, as we have no information on their eventual use.
|
||||
!DynamicExportSymbols.count(GV.getGUID()))
|
||||
GV.setVCallVisibilityMetadata(GlobalObject::VCallVisibilityLinkageUnit);
|
||||
}
|
||||
}
|
||||
|
||||
void updatePublicTypeTestCalls(Module &M,
|
||||
bool WholeProgramVisibilityEnabledInLTO) {
|
||||
Function *PublicTypeTestFunc =
|
||||
M.getFunction(Intrinsic::getName(Intrinsic::public_type_test));
|
||||
if (!PublicTypeTestFunc)
|
||||
return;
|
||||
if (hasWholeProgramVisibility(WholeProgramVisibilityEnabledInLTO)) {
|
||||
Function *TypeTestFunc =
|
||||
Intrinsic::getDeclaration(&M, Intrinsic::type_test);
|
||||
for (Use &U : make_early_inc_range(PublicTypeTestFunc->uses())) {
|
||||
auto *CI = cast<CallInst>(U.getUser());
|
||||
auto *NewCI = CallInst::Create(
|
||||
TypeTestFunc, {CI->getArgOperand(0), CI->getArgOperand(1)}, None, "",
|
||||
CI);
|
||||
CI->replaceAllUsesWith(NewCI);
|
||||
CI->eraseFromParent();
|
||||
}
|
||||
} else {
|
||||
auto *True = ConstantInt::getTrue(M.getContext());
|
||||
for (Use &U : make_early_inc_range(PublicTypeTestFunc->uses())) {
|
||||
auto *CI = cast<CallInst>(U.getUser());
|
||||
CI->replaceAllUsesWith(True);
|
||||
CI->eraseFromParent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// If whole program visibility asserted, then upgrade all public vcall
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
; Test to ensure that the LTO API (legacy and new) lowers @llvm.public.type.test.
|
||||
|
||||
; RUN: llvm-as < %s > %t1
|
||||
; RUN: llvm-lto -exported-symbol=_main %t1 -o %t2 --lto-save-before-opt --whole-program-visibility
|
||||
; RUN: llvm-dis -o - %t2.0.preopt.bc | FileCheck %s --check-prefix=HIDDEN
|
||||
; RUN: llvm-lto -exported-symbol=_main %t1 -o %t2 --lto-save-before-opt
|
||||
; RUN: llvm-dis -o - %t2.0.preopt.bc | FileCheck %s --check-prefix=PUBLIC
|
||||
|
||||
; RUN: llvm-lto2 run %t1 -save-temps -pass-remarks=. \
|
||||
; RUN: -whole-program-visibility \
|
||||
; RUN: -o %t2 \
|
||||
; RUN: -r=%t1,_main,px
|
||||
; RUN: llvm-dis %t2.0.0.preopt.bc -o - | FileCheck %s --check-prefix=HIDDEN
|
||||
; RUN: llvm-lto2 run %t1 -save-temps -pass-remarks=. \
|
||||
; RUN: -o %t2 \
|
||||
; RUN: -r=%t1,_main,px
|
||||
; RUN: llvm-dis %t2.0.0.preopt.bc -o - | FileCheck %s --check-prefix=PUBLIC
|
||||
|
||||
; PUBLIC-NOT: call {{.*}}@llvm.public.type.test
|
||||
; PUBLIC-NOT: call {{.*}}@llvm.type.test
|
||||
; HIDDEN-NOT: call {{.*}}@llvm.public.type.test
|
||||
; HIDDEN: call {{.*}}@llvm.type.test
|
||||
|
||||
target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
|
||||
target triple = "x86_64-apple-macosx10.9"
|
||||
|
||||
define i32 @main(ptr %vtable) {
|
||||
entry:
|
||||
%p = call i1 @llvm.public.type.test(ptr %vtable, metadata !"_ZTS1A")
|
||||
call void @llvm.assume(i1 %p)
|
||||
ret i32 0
|
||||
}
|
||||
|
||||
declare void @llvm.assume(i1)
|
||||
declare i1 @llvm.public.type.test(ptr, metadata)
|
|
@ -10,6 +10,7 @@
|
|||
; RUN: -whole-program-visibility \
|
||||
; RUN: -o %t3 \
|
||||
; RUN: -r=%t2.o,test,px \
|
||||
; RUN: -r=%t2.o,test_public,px \
|
||||
; RUN: -r=%t2.o,_ZN1A1nEi,p \
|
||||
; RUN: -r=%t2.o,_ZN1B1fEi,p \
|
||||
; RUN: -r=%t2.o,_ZN1C1fEi,p \
|
||||
|
@ -17,6 +18,7 @@
|
|||
; RUN: -r=%t2.o,_ZTV1B,px \
|
||||
; RUN: -r=%t2.o,_ZTV1C,px \
|
||||
; RUN: -r=%t2.o,_ZTV1D,px 2>&1 | FileCheck %s --check-prefix=REMARK
|
||||
; RUN: llvm-dis %t3.1.0.preopt.bc -o - | FileCheck %s --check-prefix=CHECK-TT
|
||||
; RUN: llvm-dis %t3.1.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-IR
|
||||
|
||||
; Hybrid WPD
|
||||
|
@ -26,6 +28,7 @@
|
|||
; RUN: -whole-program-visibility \
|
||||
; RUN: -o %t3 \
|
||||
; RUN: -r=%t.o,test,px \
|
||||
; RUN: -r=%t.o,test_public,px \
|
||||
; RUN: -r=%t.o,_ZN1A1nEi,p \
|
||||
; RUN: -r=%t.o,_ZN1B1fEi,p \
|
||||
; RUN: -r=%t.o,_ZN1C1fEi,p \
|
||||
|
@ -40,6 +43,7 @@
|
|||
; RUN: -r=%t.o,_ZTV1B,px \
|
||||
; RUN: -r=%t.o,_ZTV1C,px \
|
||||
; RUN: -r=%t.o,_ZTV1D,px 2>&1 | FileCheck %s --check-prefix=REMARK --dump-input=fail
|
||||
; RUN: llvm-dis %t3.1.0.preopt.bc -o - | FileCheck %s --check-prefix=CHECK-TT
|
||||
; RUN: llvm-dis %t3.1.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-IR
|
||||
|
||||
; Regular LTO WPD
|
||||
|
@ -48,6 +52,7 @@
|
|||
; RUN: -whole-program-visibility \
|
||||
; RUN: -o %t5 \
|
||||
; RUN: -r=%t4.o,test,px \
|
||||
; RUN: -r=%t4.o,test_public,px \
|
||||
; RUN: -r=%t4.o,_ZN1A1nEi,p \
|
||||
; RUN: -r=%t4.o,_ZN1B1fEi,p \
|
||||
; RUN: -r=%t4.o,_ZN1C1fEi,p \
|
||||
|
@ -55,8 +60,11 @@
|
|||
; RUN: -r=%t4.o,_ZTV1B,px \
|
||||
; RUN: -r=%t4.o,_ZTV1C,px \
|
||||
; RUN: -r=%t4.o,_ZTV1D,px 2>&1 | FileCheck %s --check-prefix=REMARK
|
||||
; RUN: llvm-dis %t5.0.0.preopt.bc -o - | FileCheck %s --check-prefix=CHECK-TT
|
||||
; RUN: llvm-dis %t5.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
|
||||
; REMARK-DAG: single-impl: devirtualized a call to _ZN1A1nEi
|
||||
; REMARK-DAG: single-impl: devirtualized a call to _ZN1D1mEi
|
||||
|
||||
|
@ -67,6 +75,7 @@
|
|||
; RUN: llvm-lto2 run %t2.o -save-temps -pass-remarks=. \
|
||||
; RUN: -o %t3 \
|
||||
; RUN: -r=%t2.o,test,px \
|
||||
; RUN: -r=%t2.o,test_public,px \
|
||||
; RUN: -r=%t2.o,_ZN1A1nEi,p \
|
||||
; RUN: -r=%t2.o,_ZN1B1fEi,p \
|
||||
; RUN: -r=%t2.o,_ZN1C1fEi,p \
|
||||
|
@ -74,12 +83,14 @@
|
|||
; RUN: -r=%t2.o,_ZTV1B,px \
|
||||
; RUN: -r=%t2.o,_ZTV1C,px \
|
||||
; RUN: -r=%t2.o,_ZTV1D,px 2>&1 | FileCheck %s --implicit-check-not single-impl --allow-empty
|
||||
; RUN: llvm-dis %t3.1.0.preopt.bc -o - | FileCheck %s --check-prefix=CHECK-TT
|
||||
; RUN: llvm-dis %t3.1.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-NODEVIRT-IR
|
||||
|
||||
; Hybrid WPD
|
||||
; RUN: llvm-lto2 run %t.o -save-temps -pass-remarks=. \
|
||||
; RUN: -o %t3 \
|
||||
; RUN: -r=%t.o,test,px \
|
||||
; RUN: -r=%t.o,test_public,px \
|
||||
; RUN: -r=%t.o,_ZN1A1nEi,p \
|
||||
; RUN: -r=%t.o,_ZN1B1fEi,p \
|
||||
; RUN: -r=%t.o,_ZN1C1fEi,p \
|
||||
|
@ -94,12 +105,14 @@
|
|||
; RUN: -r=%t.o,_ZTV1B,px \
|
||||
; RUN: -r=%t.o,_ZTV1C,px \
|
||||
; RUN: -r=%t.o,_ZTV1D,px 2>&1 | FileCheck %s --implicit-check-not single-impl --allow-empty
|
||||
; RUN: llvm-dis %t3.1.0.preopt.bc -o - | FileCheck %s --check-prefix=CHECK-TT
|
||||
; RUN: llvm-dis %t3.1.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-NODEVIRT-IR
|
||||
|
||||
; Regular LTO WPD
|
||||
; RUN: llvm-lto2 run %t4.o -save-temps -pass-remarks=. \
|
||||
; RUN: -o %t5 \
|
||||
; RUN: -r=%t4.o,test,px \
|
||||
; RUN: -r=%t4.o,test_public,px \
|
||||
; RUN: -r=%t4.o,_ZN1A1nEi,p \
|
||||
; RUN: -r=%t4.o,_ZN1B1fEi,p \
|
||||
; RUN: -r=%t4.o,_ZN1C1fEi,p \
|
||||
|
@ -107,6 +120,7 @@
|
|||
; RUN: -r=%t4.o,_ZTV1B,px \
|
||||
; RUN: -r=%t4.o,_ZTV1C,px \
|
||||
; RUN: -r=%t4.o,_ZTV1D,px 2>&1 | FileCheck %s --implicit-check-not single-impl --allow-empty
|
||||
; RUN: llvm-dis %t5.0.0.preopt.bc -o - | FileCheck %s --check-prefix=CHECK-TT
|
||||
; RUN: llvm-dis %t5.0.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-NODEVIRT-IR
|
||||
|
||||
; Try index-based WPD again with both -whole-program-visibility and
|
||||
|
@ -117,6 +131,7 @@
|
|||
; RUN: -disable-whole-program-visibility \
|
||||
; RUN: -o %t3 \
|
||||
; RUN: -r=%t2.o,test,px \
|
||||
; RUN: -r=%t2.o,test_public,px \
|
||||
; RUN: -r=%t2.o,_ZN1A1nEi,p \
|
||||
; RUN: -r=%t2.o,_ZN1B1fEi,p \
|
||||
; RUN: -r=%t2.o,_ZN1C1fEi,p \
|
||||
|
@ -124,8 +139,11 @@
|
|||
; RUN: -r=%t2.o,_ZTV1B,px \
|
||||
; RUN: -r=%t2.o,_ZTV1C,px \
|
||||
; RUN: -r=%t2.o,_ZTV1D,px 2>&1 | FileCheck %s --implicit-check-not single-impl --allow-empty
|
||||
; RUN: llvm-dis %t3.1.0.preopt.bc -o - | FileCheck %s --check-prefix=CHECK-TT
|
||||
; RUN: llvm-dis %t3.1.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-NODEVIRT-IR
|
||||
|
||||
; CHECK-TT-NOT: call {{.*}}@llvm.public.type.test
|
||||
|
||||
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"
|
||||
|
||||
|
@ -175,7 +193,44 @@ entry:
|
|||
; CHECK-IR-LABEL: ret i32
|
||||
; CHECK-IR-LABEL: }
|
||||
|
||||
; CHECK-IR-LABEL: define i32 @test_public
|
||||
define i32 @test_public(ptr %obj, ptr %obj2, i32 %a) {
|
||||
entry:
|
||||
%vtable = load ptr, ptr %obj
|
||||
%p = call i1 @llvm.public.type.test(ptr %vtable, metadata !"_ZTS1A")
|
||||
call void @llvm.assume(i1 %p)
|
||||
%fptrptr = getelementptr ptr, ptr %vtable, i32 1
|
||||
%fptr1 = load ptr, ptr %fptrptr, align 8
|
||||
|
||||
; Check that the call was devirtualized.
|
||||
; CHECK-IR: %call = tail call i32 @_ZN1A1nEi
|
||||
; CHECK-NODEVIRT-IR: %call = tail call i32 %fptr1
|
||||
%call = tail call i32 %fptr1(ptr nonnull %obj, i32 %a)
|
||||
|
||||
%fptr22 = load ptr, ptr %vtable, align 8
|
||||
|
||||
; We still have to call it as virtual.
|
||||
; CHECK-IR: %call3 = tail call i32 %fptr22
|
||||
; CHECK-NODEVIRT-IR: %call3 = tail call i32 %fptr22
|
||||
%call3 = tail call i32 %fptr22(ptr nonnull %obj, i32 %call)
|
||||
|
||||
%vtable2 = load ptr, ptr %obj2
|
||||
%p2 = call i1 @llvm.public.type.test(ptr %vtable2, metadata !4)
|
||||
call void @llvm.assume(i1 %p2)
|
||||
|
||||
%fptr33 = load ptr, ptr %vtable2, align 8
|
||||
|
||||
; Check that the call was devirtualized.
|
||||
; CHECK-IR: %call4 = tail call i32 @_ZN1D1mEi
|
||||
; CHECK-NODEVIRT-IR: %call4 = tail call i32 %fptr33
|
||||
%call4 = tail call i32 %fptr33(ptr nonnull %obj2, i32 %call3)
|
||||
ret i32 %call4
|
||||
}
|
||||
; CHECK-IR-LABEL: ret i32
|
||||
; CHECK-IR-LABEL: }
|
||||
|
||||
declare i1 @llvm.type.test(ptr, metadata)
|
||||
declare i1 @llvm.public.type.test(ptr, metadata)
|
||||
declare void @llvm.assume(i1)
|
||||
|
||||
define i32 @_ZN1B1fEi(ptr %this, i32 %a) #0 {
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
; Test to ensure that the legacy LTO API lowers @llvm.public.type.test.
|
||||
|
||||
; RUN: opt -module-summary %s -o %t.bc
|
||||
; RUN: llvm-lto --thinlto-action=run -exported-symbol=_main %t.bc --thinlto-save-temps=%t2
|
||||
; RUN: llvm-dis -o - %t20.2.internalized.bc | FileCheck %s --check-prefix=PUBLIC
|
||||
; RUN: llvm-lto --thinlto-action=run -exported-symbol=_main %t.bc --thinlto-save-temps=%t2 --whole-program-visibility
|
||||
; RUN: llvm-dis -o - %t20.2.internalized.bc | FileCheck %s --check-prefix=HIDDEN
|
||||
|
||||
; PUBLIC-NOT: call {{.*}}@llvm.public.type.test
|
||||
; PUBLIC-NOT: call {{.*}}@llvm.type.test
|
||||
; HIDDEN-NOT: call {{.*}}@llvm.public.type.test
|
||||
; HIDDEN: call {{.*}}@llvm.type.test
|
||||
|
||||
target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
|
||||
target triple = "x86_64-apple-macosx10.9"
|
||||
|
||||
define i32 @main(ptr %vtable) {
|
||||
entry:
|
||||
%p = call i1 @llvm.public.type.test(ptr %vtable, metadata !"_ZTS1A")
|
||||
call void @llvm.assume(i1 %p)
|
||||
ret i32 0
|
||||
}
|
||||
|
||||
declare void @llvm.assume(i1)
|
||||
declare i1 @llvm.public.type.test(ptr, metadata)
|
|
@ -261,6 +261,10 @@ static cl::opt<bool>
|
|||
cl::desc("Print pass management debugging information"),
|
||||
cl::cat(LTOCategory));
|
||||
|
||||
static cl::opt<bool>
|
||||
LTOSaveBeforeOpt("lto-save-before-opt", cl::init(false),
|
||||
cl::desc("Save the IR before running optimizations"));
|
||||
|
||||
namespace {
|
||||
|
||||
struct ModuleInfo {
|
||||
|
@ -1069,6 +1073,9 @@ int main(int argc, char **argv) {
|
|||
CodeGen.setFileType(*FT);
|
||||
|
||||
if (!OutputFilename.empty()) {
|
||||
if (LTOSaveBeforeOpt)
|
||||
CodeGen.setSaveIRBeforeOptPath(OutputFilename + ".0.preopt.bc");
|
||||
|
||||
if (SaveLinkedModuleFile) {
|
||||
std::string ModuleFilename = OutputFilename;
|
||||
ModuleFilename += ".linked.bc";
|
||||
|
|
Loading…
Reference in New Issue