leonardchan updated this revision to Diff 188973.
leonardchan added a comment.
Herald added subscribers: llvm-commits, MaskRay, arichardson, emaste.
Herald added a reviewer: espindola.
Herald added a project: LLVM.
Update to current working version without needing LTO
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D58321/new/
https://reviews.llvm.org/D58321
Files:
clang/include/clang/AST/DeclCXX.h
clang/include/clang/AST/VTableBuilder.h
clang/include/clang/Basic/CodeGenOptions.def
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/include/clang/Basic/LangOptions.def
clang/include/clang/Driver/Options.td
clang/include/clang/Sema/Sema.h
clang/lib/AST/DeclCXX.cpp
clang/lib/CodeGen/CGClass.cpp
clang/lib/CodeGen/CGDebugInfo.cpp
clang/lib/CodeGen/CGVTables.cpp
clang/lib/CodeGen/CGVTables.h
clang/lib/CodeGen/CodeGenFunction.h
clang/lib/CodeGen/CodeGenModule.cpp
clang/lib/CodeGen/ItaniumCXXABI.cpp
clang/lib/CodeGen/MicrosoftCXXABI.cpp
clang/lib/Driver/ToolChains/Clang.cpp
clang/lib/Frontend/CompilerInvocation.cpp
clang/lib/Sema/SemaDeclCXX.cpp
clang/test/CodeGenCXX/debug-info-virtual-fn-relative.cpp
clang/test/CodeGenCXX/type-metadata.cpp
clang/test/CodeGenCXX/vtable-relative-abi.cpp
clang/test/Driver/unstable-cxx-abi-vtables.cpp
lld/ELF/Relocations.cpp
Index: lld/ELF/Relocations.cpp
===================================================================
--- lld/ELF/Relocations.cpp
+++ lld/ELF/Relocations.cpp
@@ -908,7 +908,7 @@
}
// Copy relocations are only possible if we are creating an executable.
- if (Config->Shared) {
+ if (Config->Shared && Type == Target->CopyRel) {
errorOrWarn("relocation " + toString(Type) +
" cannot be used against symbol " + toString(Sym) +
"; recompile with -fPIC" + getLocation(Sec, Sym, Offset));
Index: clang/test/Driver/unstable-cxx-abi-vtables.cpp
===================================================================
--- /dev/null
+++ clang/test/Driver/unstable-cxx-abi-vtables.cpp
@@ -0,0 +1,2 @@
+// RUN: %clang -flto -flto-relative-c++-abi-vtables -### %s 2>&1 | FileCheck -check-prefix=LTO-CLASSES %s
+// LTO-CLASSES: "-flto-relative-c++-abi-vtables"
Index: clang/test/CodeGenCXX/vtable-relative-abi.cpp
===================================================================
--- /dev/null
+++ clang/test/CodeGenCXX/vtable-relative-abi.cpp
@@ -0,0 +1,123 @@
+// RUN: %clang_cc1 %s -flto -flto-unit -triple x86_64-unknown-linux-gnu -fvisibility hidden -flto-relative-c++-abi-vtables -emit-llvm -o - | FileCheck --check-prefix=CHECK --check-prefix=CHECK-ITANIUM %s
+// RUN: %clang_cc1 %s -flto -flto-unit -triple x86_64-unknown-windows-msvc -flto-relative-c++-abi-vtables -emit-llvm -o - | FileCheck --check-prefix=CHECK --check-prefix=CHECK-MS %s
+// RUN: %clang_cc1 %s -flto -flto-unit -triple x86_64-unknown-linux-gnu -fvisibility hidden -emit-llvm -o - | FileCheck --check-prefix=CHECK-NOABI %s
+
+// CHECK-ITANIUM: @_ZTV1S = hidden unnamed_addr constant { { i8*, i8*, i32, i32 } } { { i8*, i8*, i32, i32 } { i8* null, i8* bitcast ({ i8*, i8* }* @_ZTI1S to i8*), i32 trunc (i64 sub (i64 ptrtoint (void (%struct.S*)* @_ZN1S2f1Ev to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i8*, i32, i32 } }, { { i8*, i8*, i32, i32 } }* @_ZTV1S, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%struct.S*)* @_ZN1S2f2Ev to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i8*, i32, i32 } }, { { i8*, i8*, i32, i32 } }* @_ZTV1S, i32 0, i32 0, i32 3) to i64)) to i32) } }, align 8
+// CHECK-MS: @anon.[[NUM:[a-z0-9]+]].0 = private unnamed_addr constant { { i8*, i32, i32 } } { { i8*, i32, i32 } { i8* bitcast (%rtti.CompleteObjectLocator* @"??_R4S@@6B@" to i8*), i32 trunc (i64 sub (i64 ptrtoint (void (%struct.S*)* @"?f1@S@@UEAAXXZ" to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32 } }, { { i8*, i32, i32 } }* @anon.[[NUM]].0, i32 0, i32 0, i32 1) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%struct.S*)* @"?f2@S@@UEAAXXZ" to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32 } }, { { i8*, i32, i32 } }* @anon.[[NUM]].0, i32 0, i32 0, i32 2) to i64)) to i32) } }, comdat($"??_7S@@6B@")
+struct S {
+ S();
+ virtual void f1();
+ virtual void f2();
+};
+
+// CHECK-ITANIUM: @_ZTV1T = hidden unnamed_addr constant { { i8*, i8*, i32 } } { { i8*, i8*, i32 } { i8* null, i8* bitcast ({ i8*, i8* }* @_ZTI1T to i8*), i32 trunc (i64 sub (i64 ptrtoint (void (%struct.T*)* @_ZN1T1gEv to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i8*, i32 } }, { { i8*, i8*, i32 } }* @_ZTV1T, i32 0, i32 0, i32 2) to i64)) to i32) } }, align 8
+// CHECK-MS: @anon.[[NUM]].1 = private unnamed_addr constant { { i8*, i32 } } { { i8*, i32 } { i8* bitcast (%rtti.CompleteObjectLocator* @"??_R4T@@6B@" to i8*), i32 trunc (i64 sub (i64 ptrtoint (void (%struct.T*)* @"?g@T@@UEAAXXZ" to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32 } }, { { i8*, i32 } }* @anon.[[NUM]].1, i32 0, i32 0, i32 1) to i64)) to i32) } }, comdat($"??_7T@@6B@")
+struct T {
+ T();
+ virtual void g();
+};
+
+// CHECK-ITANIUM: @_ZTV1U = hidden unnamed_addr constant { { i8*, i8*, i32, i32 }, { i8*, i8*, i32 } } { { i8*, i8*, i32, i32 } { i8* null, i8* bitcast ({ i8*, i8*, i32, i32, i8*, i64, i8*, i64 }* @_ZTI1U to i8*), i32 trunc (i64 sub (i64 ptrtoint (void (%struct.U*)* @_ZN1U2f1Ev to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i8*, i32, i32 }, { i8*, i8*, i32 } }, { { i8*, i8*, i32, i32 }, { i8*, i8*, i32 } }* @_ZTV1U, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%struct.S*)* @_ZN1S2f2Ev to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i8*, i32, i32 }, { i8*, i8*, i32 } }, { { i8*, i8*, i32, i32 }, { i8*, i8*, i32 } }* @_ZTV1U, i32 0, i32 0, i32 3) to i64)) to i32) }, { i8*, i8*, i32 } { i8* inttoptr (i64 -8 to i8*), i8* bitcast ({ i8*, i8*, i32, i32, i8*, i64, i8*, i64 }* @_ZTI1U to i8*), i32 trunc (i64 sub (i64 ptrtoint (void (%struct.T*)* @_ZN1T1gEv to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i8*, i32, i32 }, { i8*, i8*, i32 } }, { { i8*, i8*, i32, i32 }, { i8*, i8*, i32 } }* @_ZTV1U, i32 0, i32 1, i32 2) to i64)) to i32) } }, align 8
+// CHECK-MS: @anon.[[NUM]].2 = private unnamed_addr constant { { i8*, i32, i32 } } { { i8*, i32, i32 } { i8* bitcast (%rtti.CompleteObjectLocator* @"??_R4U@@6BS@@@" to i8*), i32 trunc (i64 sub (i64 ptrtoint (void (%struct.U*)* @"?f1@U@@UEAAXXZ" to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32 } }, { { i8*, i32, i32 } }* @anon.[[NUM]].2, i32 0, i32 0, i32 1) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%struct.S*)* @"?f2@S@@UEAAXXZ" to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32 } }, { { i8*, i32, i32 } }* @anon.[[NUM]].2, i32 0, i32 0, i32 2) to i64)) to i32) } }, comdat($"??_7U@@6BS@@@")
+// CHECK-MS: @anon.[[NUM]].3 = private unnamed_addr constant { { i8*, i32 } } { { i8*, i32 } { i8* bitcast (%rtti.CompleteObjectLocator* @"??_R4U@@6BT@@@" to i8*), i32 trunc (i64 sub (i64 ptrtoint (void (%struct.T*)* @"?g@T@@UEAAXXZ" to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32 } }, { { i8*, i32 } }* @anon.[[NUM]].3, i32 0, i32 0, i32 1) to i64)) to i32) } }, comdat($"??_7U@@6BT@@@")
+struct U : S, T {
+ U();
+ virtual void f1();
+};
+
+S::S() {}
+void S::f1() {}
+
+T::T() {}
+void T::g() {}
+
+U::U() {}
+void U::f1() {}
+
+struct V {
+ virtual void f();
+};
+
+struct V1 : virtual V {
+};
+
+struct V2 : virtual V {
+};
+
+// CHECK-ITANIUM: @_ZTC2V30_2V1 = linkonce_odr hidden unnamed_addr constant { { i8*, i8*, i8*, i8*, i32 } } { { i8*, i8*, i8*, i8*, i32 } { i8* null, i8* null, i8* null, i8* bitcast ({ i8*, i8*, i32, i32, i8*, i64 }* @_ZTI2V1 to i8*), i32 trunc (i64 sub (i64 ptrtoint (void (%struct.V*)* @_ZN1V1fEv to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i8*, i8*, i8*, i32 } }, { { i8*, i8*, i8*, i8*, i32 } }* @_ZTC2V30_2V1, i32 0, i32 0, i32 4) to i64)) to i32) } }, comdat, align 8
+// CHECK-ITANIUM: @_ZTC2V38_2V2 = linkonce_odr hidden unnamed_addr constant { { i8*, i8*, i8*, i8*, i32 }, { i8*, i8*, i8*, i32 } } { { i8*, i8*, i8*, i8*, i32 } { i8* inttoptr (i64 -8 to i8*), i8* inttoptr (i64 -8 to i8*), i8* null, i8* bitcast ({ i8*, i8*, i32, i32, i8*, i64 }* @_ZTI2V2 to i8*), i32 0 }, { i8*, i8*, i8*, i32 } { i8* null, i8* inttoptr (i64 8 to i8*), i8* bitcast ({ i8*, i8*, i32, i32, i8*, i64 }* @_ZTI2V2 to i8*), i32 trunc (i64 sub (i64 ptrtoint (void (%struct.V*)* @_ZN1V1fEv to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i8*, i8*, i8*, i32 }, { i8*, i8*, i8*, i32 } }, { { i8*, i8*, i8*, i8*, i32 }, { i8*, i8*, i8*, i32 } }* @_ZTC2V38_2V2, i32 0, i32 1, i32 3) to i64)) to i32) } }, comdat, align 8
+struct V3 : V1, V2 {
+ V3();
+};
+
+V3::V3() {}
+
+// CHECK-ITANIUM: define hidden void @_Z5call1P1S
+// CHECK-MS: define {{.*}} void @"?call1@@YAXPEAUS@@@Z"
+void call1(S *s) {
+ // CHECK: [[VTABLE:%.*]] = load void
+ // CHECK: [[VTABLEI8:%.*]] = bitcast {{.*}} [[VTABLE]] to i8*
+ // CHECK: [[VFN:%.*]] = call i8* @llvm.load.relative.i32(i8* [[VTABLEI8]], i32 0)
+ // CHECK: [[VFNCASTED:%.*]] = bitcast i8* [[VFN]] to void (
+ // CHECK: call void [[VFNCASTED]](
+ s->f1();
+}
+
+// CHECK-ITANIUM: define hidden void @_Z5call2P1S
+// CHECK-MS: define {{.*}} void @"?call2@@YAXPEAUS@@@Z"
+void call2(S *s) {
+ // CHECK: [[VTABLE:%.*]] = load void
+ // CHECK: [[VTABLEI8:%.*]] = bitcast {{.*}} [[VTABLE]] to i8*
+ // CHECK: [[VFN:%.*]] = call i8* @llvm.load.relative.i32(i8* [[VTABLEI8]], i32 4)
+ // CHECK: [[VFNCASTED:%.*]] = bitcast i8* [[VFN]] to void (
+ // CHECK: call void [[VFNCASTED]](
+ s->f2();
+}
+
+typedef void (S::*mfp)();
+
+// CHECK-ITANIUM: define hidden { i64, i64 } @_Z7getmfp1v()
+// CHECK-ITANIUM: ret { i64, i64 } { i64 1, i64 0 }
+// CHECK-MS: define {{.*}} i8* @"?getmfp1@@YAP8S@@EAAXXZXZ"()
+// CHECK-MS: ret i8* bitcast {{.*}} @"??_9S@@$BA@AA"
+// CHECK-MS: define linkonce_odr void @"??_9S@@$BA@AA"
+// CHECK-MS: [[VTABLE:%.*]] = load void
+// CHECK-MS: [[VTABLEI8:%.*]] = bitcast {{.*}} [[VTABLE]] to i8*
+// CHECK-MS: [[VFN:%.*]] = call i8* @llvm.load.relative.i32(i8* [[VTABLEI8]], i32 0)
+// CHECK-MS: [[VFNCASTED:%.*]] = bitcast i8* [[VFN]] to void (
+// CHECK-MS: musttail call {{.*}} [[VFNCASTED]](
+mfp getmfp1() {
+ return &S::f1;
+}
+
+// CHECK-ITANIUM: define hidden { i64, i64 } @_Z7getmfp2v()
+// CHECK-ITANIUM: ret { i64, i64 } { i64 5, i64 0 }
+// CHECK-MS: define {{.*}} i8* @"?getmfp2@@YAP8S@@EAAXXZXZ"()
+// CHECK-MS: ret i8* bitcast {{.*}} @"??_9S@@$B7AA"
+// CHECK-MS: define linkonce_odr void @"??_9S@@$B7AA"
+// CHECK-MS: [[VTABLE:%.*]] = load void
+// CHECK-MS: [[VTABLEI8:%.*]] = bitcast {{.*}} [[VTABLE]] to i8*
+// CHECK-MS: [[VFN:%.*]] = call i8* @llvm.load.relative.i32(i8* [[VTABLEI8]], i32 4)
+// CHECK-MS: [[VFNCASTED:%.*]] = bitcast i8* [[VFN]] to void (
+// CHECK-MS: musttail call {{.*}} [[VFNCASTED]](
+mfp getmfp2() {
+ return &S::f2;
+}
+
+// In the MS ABI virtual member function calls use thunks (which we already
+// tested above), so there's nothing to test that's specific to the relative
+// ABI.
+
+// CHECK-ITANIUM: define hidden void @_Z7callmfpP1SMS_FvvE
+void callmfp(S *s, mfp p) {
+ // CHECK-ITANIUM: [[VTOFFSET:%.*]] = sub i64 {{.*}}, 1
+ // CHECK-ITANIUM: [[VFN:%.*]] = call i8* @llvm.load.relative.i64(i8* {{.*}}, i64 [[VTOFFSET]])
+ // CHECK-ITANIUM: bitcast i8* [[VFN]] to void (
+ (s->*p)();
+}
+
+// CHECK-NOABI: !llvm.module.flags = !{{{![0-9]+}}, [[MF:![0-9]+]]}
+// CHECK-NOABI: [[MF]] = !{i32 1, !"lto-relative-c++-abi-vtables", i32 0}
+
+// CHECK: !llvm.module.flags = !{{{![0-9]+}}, [[MF:![0-9]+]]}
+// CHECK: [[MF]] = !{i32 1, !"lto-relative-c++-abi-vtables", i32 1}
Index: clang/test/CodeGenCXX/type-metadata.cpp
===================================================================
--- clang/test/CodeGenCXX/type-metadata.cpp
+++ clang/test/CodeGenCXX/type-metadata.cpp
@@ -5,18 +5,22 @@
// RUN: %clang_cc1 -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 -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 -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
+// RUN: %clang_cc1 -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 --check-prefix=ITANIUM-STABLE %s
+// RUN: %clang_cc1 -flto -flto-unit -triple x86_64-unknown-linux -fvisibility hidden -fwhole-program-vtables -flto-relative-c++-abi-vtables -emit-llvm -o - %s | FileCheck --check-prefix=VTABLE-OPT --check-prefix=TT-ITANIUM --check-prefix=ITANIUM-UNSTABLE %s
+// RUN: %clang_cc1 -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 --check-prefix=MS-STABLE %s
+// RUN: %clang_cc1 -flto -flto-unit -triple x86_64-pc-windows-msvc -fwhole-program-vtables -flto-relative-c++-abi-vtables -emit-llvm -o - %s | FileCheck --check-prefix=VTABLE-OPT --check-prefix=TT-MS --check-prefix=MS-UNSTABLE %s
// Tests for cfi + whole-program-vtables:
// RUN: %clang_cc1 -flto -flto-unit -triple x86_64-unknown-linux -fvisibility hidden -fsanitize=cfi-vcall -fsanitize-trap=cfi-vcall -fwhole-program-vtables -emit-llvm -o - %s | FileCheck --check-prefix=CFI --check-prefix=CFI-VT --check-prefix=ITANIUM --check-prefix=TC-ITANIUM %s
// RUN: %clang_cc1 -flto -flto-unit -triple x86_64-pc-windows-msvc -fsanitize=cfi-vcall -fsanitize-trap=cfi-vcall -fwhole-program-vtables -emit-llvm -o - %s | FileCheck --check-prefix=CFI --check-prefix=CFI-VT --check-prefix=MS --check-prefix=TC-MS %s
-// ITANIUM: @_ZTV1A = {{[^!]*}}, !type [[A16:![0-9]+]]
+// ITANIUM: @_ZTV1A = {{.*}} { [3 x i8*] } {{[^!]*}}, !type [[A16:![0-9]+]]
+// ITANIUM-UNSTABLE: @_ZTV1A = {{.*}} { { i8*, i8*, i32 } } {{[^!]*}}, !type [[A16:![0-9]+]]
// ITANIUM-DIAG-SAME: !type [[ALL16:![0-9]+]]
// ITANIUM-SAME: !type [[AF16:![0-9]+]]
-// ITANIUM: @_ZTV1B = {{[^!]*}}, !type [[A32:![0-9]+]]
+// ITANIUM: @_ZTV1B = {{.*}} { [7 x i8*] } {{[^!]*}}, !type [[A32:![0-9]+]]
+// ITANIUM-UNSTABLE: @_ZTV1B = {{.*}} { { i8*, i8*, i8*, i8*, i32, i32, i32 } } {{[^!]*}}, !type [[A32:![0-9]+]]
// ITANIUM-DIAG-SAME: !type [[ALL32:![0-9]+]]
// ITANIUM-SAME: !type [[AF32:![0-9]+]]
// ITANIUM-SAME: !type [[AF40:![0-9]+]]
@@ -27,7 +31,8 @@
// ITANIUM-SAME: !type [[BF40:![0-9]+]]
// ITANIUM-SAME: !type [[BF48:![0-9]+]]
-// ITANIUM: @_ZTV1C = {{[^!]*}}, !type [[A32]]
+// ITANIUM: @_ZTV1C = {{.*}} { [5 x i8*] } {{[^!]*}}, !type [[A32]]
+// ITANIUM-UNSTABLE: @_ZTV1C = {{.*}} { { i8*, i8*, i8*, i8*, i32 } } {{[^!]*}}, !type [[A32]]
// ITANIUM-DIAG-SAME: !type [[ALL32]]
// ITANIUM-SAME: !type [[AF32]]
// ITANIUM-SAME: !type [[C32:![0-9]+]]
@@ -38,7 +43,8 @@
// DIAG: @[[TYPE:.*]] = private unnamed_addr constant { i16, i16, [4 x i8] } { i16 -1, i16 0, [4 x i8] c"'A'\00" }
// DIAG: @[[BADTYPESTATIC:.*]] = private unnamed_addr global { i8, { [{{.*}} x i8]*, i32, i32 }, { i16, i16, [4 x i8] }* } { i8 0, { [{{.*}} x i8]*, i32, i32 } { [{{.*}} x i8]* @[[SRC]], i32 123, i32 3 }, { i16, i16, [4 x i8] }* @[[TYPE]] }
-// ITANIUM: @_ZTVN12_GLOBAL__N_11DE = {{[^!]*}}, !type [[A32]]
+// ITANIUM: @_ZTVN12_GLOBAL__N_11DE = {{.*}} { [7 x i8*], [5 x i8*] } {{[^!]*}}, !type [[A32]]
+// ITANIUM-UNSTABLE: @_ZTVN12_GLOBAL__N_11DE = {{.*}} { { i8*, i8*, i8*, i8*, i32, i32, i32 }, { i8*, i8*, i8*, i8*, i32 } } {{[^!]*}}, !type [[A32]]
// ITANIUM-DIAG-SAME: !type [[ALL32]]
// ITANIUM-SAME: !type [[AF32]]
// ITANIUM-SAME: !type [[AF40]]
@@ -59,32 +65,43 @@
// ITANIUM-SAME: !type [[DF40:![0-9]+]]
// ITANIUM-SAME: !type [[DF48:![0-9]+]]
-// ITANIUM: @_ZTCN12_GLOBAL__N_11DE0_1B = {{[^!]*}}, !type [[A32]]
+// ITANIUM: @_ZTCN12_GLOBAL__N_11DE0_1B = {{.*}} { [7 x i8*] } {{[^!]*}}, !type [[A32]]
+// ITANIUM-UNSTABLE: @_ZTCN12_GLOBAL__N_11DE0_1B = {{.*}} { { i8*, i8*, i8*, i8*, i32, i32, i32 } } {{[^!]*}}, !type [[A32]]
// ITANIUM-DIAG-SAME: !type [[ALL32]]
// ITANIUM-SAME: !type [[B32]]
// ITANIUM-DIAG-SAME: !type [[ALL32]]
-// ITANIUM: @_ZTCN12_GLOBAL__N_11DE8_1C = {{[^!]*}}, !type [[A64:![0-9]+]]
+// ITANIUM: @_ZTCN12_GLOBAL__N_11DE8_1C = {{.*}} { [5 x i8*], [4 x i8*] } {{[^!]*}}, !type [[A64:![0-9]+]]
+// ITANIUM-UNSTABLE: @_ZTCN12_GLOBAL__N_11DE8_1C = {{.*}} { { i8*, i8*, i8*, i8*, i32 }, { i8*, i8*, i8*, i32 } } {{[^!]*}}, !type [[A64:![0-9]+]]
// ITANIUM-DIAG-SAME: !type [[ALL64:![0-9]+]]
// ITANIUM-SAME: !type [[AF64:![0-9]+]]
// ITANIUM-SAME: !type [[C32]]
// ITANIUM-DIAG-SAME: !type [[ALL32]]
// ITANIUM-SAME: !type [[CF64:![0-9]+]]
-// ITANIUM: @_ZTVZ3foovE2FA = {{[^!]*}}, !type [[A16]]
+// ITANIUM: @_ZTVZ3foovE2FA = {{.*}} { [3 x i8*] } {{[^!]*}}, !type [[A16]]
+// ITANIUM-UNSTABLE: @_ZTVZ3foovE2FA = {{.*}} { { i8*, i8*, i32 } } {{[^!]*}}, !type [[A16]]
// ITANIUM-DIAG-SAME: !type [[ALL16]]
// ITANIUM-SAME: !type [[AF16]]
// ITANIUM-SAME: !type [[FA16:![0-9]+]]
// ITANIUM-DIAG-SAME: !type [[ALL16]]
// ITANIUM-SAME: !type [[FAF16:![0-9]+]]
-// MS: comdat($"??_7A@@6B@"), !type [[A8:![0-9]+]]
-// MS: comdat($"??_7B@@6B0@@"), !type [[B8:![0-9]+]]
-// MS: comdat($"??_7B@@6BA@@@"), !type [[A8]]
-// MS: comdat($"??_7C@@6B@"), !type [[A8]]
-// MS: comdat($"??_7D@?A0x{{[^@]*}}@@6BB@@@"), !type [[B8]], !type [[D8:![0-9]+]]
-// MS: comdat($"??_7D@?A0x{{[^@]*}}@@6BA@@@"), !type [[A8]]
-// MS: comdat($"??_7FA@?1??foo@@YAXXZ@6B@"), !type [[A8]], !type [[FA8:![0-9]+]]
+// MS-STABLE: @anon.[[NUM:[a-z0-9]+]].0 = {{.*}} { [2 x i8*] } {{.*}} comdat($"??_7A@@6B@"), !type [[A8:![0-9]+]]
+// MS-STABLE: @anon.[[NUM]].1 = {{.*}} { [3 x i8*] } {{.*}} comdat($"??_7B@@6B0@@"), !type [[B8:![0-9]+]]
+// MS-STABLE: @anon.[[NUM]].2 = {{.*}} { [2 x i8*] } {{.*}} comdat($"??_7B@@6BA@@@"), !type [[A8]]
+// MS-STABLE: @anon.[[NUM]].3 = {{.*}} { [2 x i8*] } {{.*}} comdat($"??_7C@@6B@"), !type [[A8]]
+// MS-STABLE: @anon.[[NUM]].4 = {{.*}} { [3 x i8*] } {{.*}} comdat($"??_7D@?A0x{{[^@]*}}@@6BB@@@"), !type [[B8]], !type [[D8:![0-9]+]]
+// MS-STABLE: @anon.[[NUM]].5 = {{.*}} { [2 x i8*] } {{.*}} comdat($"??_7D@?A0x{{[^@]*}}@@6BA@@@"), !type [[A8]]
+// MS-STABLE: @anon.[[NUM]].6 = {{.*}} { [2 x i8*] } {{.*}} comdat($"??_7FA@?1??foo@@YAXXZ@6B@"), !type [[A8]], !type [[FA8:![0-9]+]]
+
+// MS-UNSTABLE: @anon.[[NUM:[a-z0-9]+]].0 = {{.*}} { { i8*, i32 } } {{.*}} comdat($"??_7A@@6B@"), !type [[A8:![0-9]+]]
+// MS-UNSTABLE: @anon.[[NUM]].1 = {{.*}} { { i8*, i32, i32 } } {{.*}} comdat($"??_7B@@6B0@@"), !type [[B8:![0-9]+]]
+// MS-UNSTABLE: @anon.[[NUM]].2 = {{.*}} { { i8*, i32 } } {{.*}} comdat($"??_7B@@6BA@@@"), !type [[A8]]
+// MS-UNSTABLE: @anon.[[NUM]].3 = {{.*}} { { i8*, i32 } } {{.*}} comdat($"??_7C@@6B@"), !type [[A8]]
+// MS-UNSTABLE: @anon.[[NUM]].4 = {{.*}} { { i8*, i32, i32 } } {{.*}} comdat($"??_7D@?A0x{{[^@]*}}@@6BB@@@"), !type [[B8]], !type [[D8:![0-9]+]]
+// MS-UNSTABLE: @anon.[[NUM]].5 = {{.*}} { { i8*, i32 } } {{.*}} comdat($"??_7D@?A0x{{[^@]*}}@@6BA@@@"), !type [[A8]]
+// MS-UNSTABLE: @anon.[[NUM]].6 = {{.*}} { { i8*, i32 } } {{.*}} comdat($"??_7FA@?1??foo@@YAXXZ@6B@"), !type [[A8]], !type [[FA8:![0-9]+]]
struct A {
A();
@@ -283,9 +300,9 @@
// ITANIUM: [[FAF16]] = !{i64 16, [[FAF_ID:![0-9]+]]}
// ITANIUM: [[FAF_ID]] = distinct !{}
-// MS: [[A8]] = !{i64 8, !"?AUA@@"}
-// MS: [[B8]] = !{i64 8, !"?AUB@@"}
-// MS: [[D8]] = !{i64 8, [[D_ID:![0-9]+]]}
-// MS: [[D_ID]] = distinct !{}
-// MS: [[FA8]] = !{i64 8, [[FA_ID:![0-9]+]]}
-// MS: [[FA_ID]] = distinct !{}
+// MS-STABLE: [[A8]] = !{i64 8, !"?AUA@@"}
+// MS-STABLE: [[B8]] = !{i64 8, !"?AUB@@"}
+// MS-STABLE: [[D8]] = !{i64 8, [[D_ID:![0-9]+]]}
+// MS-STABLE: [[D_ID]] = distinct !{}
+// MS-STABLE: [[FA8]] = !{i64 8, [[FA_ID:![0-9]+]]}
+// MS-STABLE: [[FA_ID]] = distinct !{}
Index: clang/test/CodeGenCXX/debug-info-virtual-fn-relative.cpp
===================================================================
--- /dev/null
+++ clang/test/CodeGenCXX/debug-info-virtual-fn-relative.cpp
@@ -0,0 +1,12 @@
+// RUN: %clang_cc1 -fvisibility hidden -emit-llvm -debug-info-kind=limited %s -o - | FileCheck --check-prefix=CHECK-STABLE %s
+// RUN: %clang_cc1 -fvisibility hidden -flto-relative-c++-abi-vtables -emit-llvm -debug-info-kind=limited %s -o - | FileCheck --check-prefix=CHECK-UNSTABLE %s
+
+struct S {
+ S();
+ virtual void f();
+};
+
+// CHECK-STABLE: virtualIndex: 0,
+// CHECK-UNSTABLE: virtualIndex: 4294967295,
+S::S() {}
+void S::f() {}
Index: clang/lib/Sema/SemaDeclCXX.cpp
===================================================================
--- clang/lib/Sema/SemaDeclCXX.cpp
+++ clang/lib/Sema/SemaDeclCXX.cpp
@@ -5885,6 +5885,46 @@
}
}
+void Sema::checkClassABI(CXXRecordDecl *Record) {
+ if (!getLangOpts().LTORelativeCXXABIVTables)
+ return;
+
+ // This can only be done accurately for non-dependent types, as the
+ // determination uses the class's bases, which may be dependent.
+ if (Record->isDependentType())
+ return;
+
+ // No need to do this for non-dynamic classes.
+ if (!Record->isDynamicClass())
+ return;
+
+ // First, see if this class inherits an ABI from a dynamic base class. If the
+ // bases disagree on which ABI to use, diagnose.
+ bool InheritsABI = false;
+ bool InheritedABIIsRelative;
+ CXXRecordDecl *InheritedABIFrom;
+ for (CXXBaseSpecifier &B : Record->bases()) {
+ auto Base = B.getType()->getAsCXXRecordDecl();
+ // Base can be null in invalid programs (see PR16677).
+ if (!Base || !Base->isDynamicClass())
+ continue;
+ if (!InheritsABI) {
+ InheritsABI = true;
+ InheritedABIIsRelative = Base->isRelativeCXXABI();
+ InheritedABIFrom = Base;
+ }
+ }
+
+ // If the class's ABI is inherited, apply it.
+ if (InheritsABI) {
+ if (InheritedABIIsRelative)
+ Record->setIsRelativeCXXABI();
+ return;
+ }
+
+ Record->setIsRelativeCXXABI();
+}
+
/// Determine whether a type is permitted to be passed or returned in
/// registers, per C++ [class.temporary]p3.
static bool canPassInRegisters(Sema &S, CXXRecordDecl *D,
@@ -6164,6 +6204,7 @@
checkClassLevelDLLAttribute(Record);
checkClassLevelCodeSegAttribute(Record);
+ checkClassABI(Record);
bool ClangABICompat4 =
Context.getLangOpts().getClangABICompat() <= LangOptions::ClangABI::Ver4;
Index: clang/lib/Frontend/CompilerInvocation.cpp
===================================================================
--- clang/lib/Frontend/CompilerInvocation.cpp
+++ clang/lib/Frontend/CompilerInvocation.cpp
@@ -708,7 +708,6 @@
Opts.CodeViewGHash = Args.hasArg(OPT_gcodeview_ghash);
Opts.MacroDebugInfo = Args.hasArg(OPT_debug_info_macro);
Opts.WholeProgramVTables = Args.hasArg(OPT_fwhole_program_vtables);
- Opts.LTOVisibilityPublicStd = Args.hasArg(OPT_flto_visibility_public_std);
Opts.SplitDwarfFile = Args.getLastArgValue(OPT_split_dwarf_file);
Opts.SplitDwarfInlining = !Args.hasArg(OPT_fno_split_dwarf_inlining);
@@ -3030,6 +3029,10 @@
Opts.CompleteMemberPointers = Args.hasArg(OPT_fcomplete_member_pointers);
Opts.BuildingPCHWithObjectFile = Args.hasArg(OPT_building_pch_with_obj);
+
+ Opts.LTOVisibilityPublicStd = Args.hasArg(OPT_flto_visibility_public_std);
+ Opts.LTORelativeCXXABIVTables =
+ Args.hasArg(OPT_flto_relative_cxx_abi_vtables);
}
static bool isStrictlyPreprocessorAction(frontend::ActionKind Action) {
Index: clang/lib/Driver/ToolChains/Clang.cpp
===================================================================
--- clang/lib/Driver/ToolChains/Clang.cpp
+++ clang/lib/Driver/ToolChains/Clang.cpp
@@ -5265,6 +5265,10 @@
CmdArgs.push_back("-fwhole-program-vtables");
}
+ // Add unstable C++ ABI flags.
+ if (Args.hasArg(options::OPT_flto_relative_cxx_abi_vtables))
+ CmdArgs.push_back("-flto-relative-c++-abi-vtables");
+
bool RequiresSplitLTOUnit = WholeProgramVTables || Sanitize.needsLTO();
bool SplitLTOUnit =
Args.hasFlag(options::OPT_fsplit_lto_unit,
Index: clang/lib/CodeGen/MicrosoftCXXABI.cpp
===================================================================
--- clang/lib/CodeGen/MicrosoftCXXABI.cpp
+++ clang/lib/CodeGen/MicrosoftCXXABI.cpp
@@ -1646,10 +1646,7 @@
[](const VTableComponent &VTC) { return VTC.isRTTIKind(); }))
RTTI = getMSCompleteObjectLocator(RD, *Info);
- ConstantInitBuilder Builder(CGM);
- auto Components = Builder.beginStruct();
- CGVT.createVTableInitializer(Components, VTLayout, RTTI);
- Components.finishAndSetAsInitializer(VTable);
+ CGVT.SetVTableInitializer(VTable, VTLayout, RTTI, RD->isRelativeCXXABI());
emitVTableTypeMetadata(*Info, RD, VTable);
}
@@ -1775,7 +1772,7 @@
StringRef VTableName = VTableAliasIsRequred ? StringRef() : VFTableName.str();
- llvm::Type *VTableType = CGM.getVTables().getVTableType(VTLayout);
+ llvm::Type *VTableType = CGM.getVTables().getVTableType(RD, VTLayout);
// Create a backing variable for the contents of VTable. The VTable may
// or may not include space for a pointer to RTTI data.
@@ -1808,6 +1805,7 @@
if (C)
C->setSelectionKind(llvm::Comdat::Largest);
}
+ VTableGEP = llvm::ConstantExpr::getBitCast(VTableGEP, CGM.Int8PtrPtrTy);
VFTable = llvm::GlobalAlias::create(CGM.Int8PtrTy,
/*AddressSpace=*/0, VFTableLinkage,
VFTableName.str(), VTableGEP,
@@ -1835,14 +1833,12 @@
Address This,
llvm::Type *Ty,
SourceLocation Loc) {
- CGBuilderTy &Builder = CGF.Builder;
-
- Ty = Ty->getPointerTo()->getPointerTo();
Address VPtr =
adjustThisArgumentForVirtualFunctionCall(CGF, GD, This, true);
auto *MethodDecl = cast<CXXMethodDecl>(GD.getDecl());
- llvm::Value *VTable = CGF.GetVTablePtr(VPtr, Ty, MethodDecl->getParent());
+ llvm::Value *VTable = CGF.GetVTablePtr(
+ VPtr, Ty->getPointerTo()->getPointerTo(), MethodDecl->getParent());
MicrosoftVTableContext &VFTContext = CGM.getMicrosoftVTableContext();
MethodVFTableLocation ML = VFTContext.getMethodVFTableLocation(GD);
@@ -1868,9 +1864,8 @@
if (CGM.getCodeGenOpts().PrepareForLTO)
CGF.EmitTypeMetadataCodeForVCall(getObjectWithVPtr(), VTable, Loc);
- llvm::Value *VFuncPtr =
- Builder.CreateConstInBoundsGEP1_64(VTable, ML.Index, "vfn");
- VFunc = Builder.CreateAlignedLoad(VFuncPtr, CGF.getPointerAlign());
+ VFunc = CGF.GetVirtualFunctionFromVTable(MethodDecl->getParent(), VTable,
+ ML.Index, Ty);
}
CGCallee Callee(GD, VFunc);
@@ -1992,10 +1987,8 @@
llvm::Value *VTable = CGF.GetVTablePtr(
getThisAddress(CGF), ThunkTy->getPointerTo()->getPointerTo(), MD->getParent());
- llvm::Value *VFuncPtr =
- CGF.Builder.CreateConstInBoundsGEP1_64(VTable, ML.Index, "vfn");
- llvm::Value *Callee =
- CGF.Builder.CreateAlignedLoad(VFuncPtr, CGF.getPointerAlign());
+ llvm::Value *Callee = CGF.GetVirtualFunctionFromVTable(
+ MD->getParent(), VTable, ML.Index, ThunkTy);
CGF.EmitMustTailThunk(MD, getThisValue(CGF), {ThunkTy, Callee});
Index: clang/lib/CodeGen/ItaniumCXXABI.cpp
===================================================================
--- clang/lib/CodeGen/ItaniumCXXABI.cpp
+++ clang/lib/CodeGen/ItaniumCXXABI.cpp
@@ -629,8 +629,8 @@
// pointers is enabled.
llvm::Constant *CheckSourceLocation;
llvm::Constant *CheckTypeDesc;
- bool ShouldEmitCFICheck = CGF.SanOpts.has(SanitizerKind::CFIMFCall) &&
- CGM.HasHiddenLTOVisibility(RD);
+ bool ShouldEmitCFICheck =
+ CGF.SanOpts.has(SanitizerKind::CFIMFCall) && RD->hasHiddenLTOVisibility();
if (ShouldEmitCFICheck) {
CodeGenFunction::SanitizerScope SanScope(&CGF);
@@ -666,9 +666,20 @@
}
// Load the virtual function to call.
- VFPAddr = Builder.CreateBitCast(VFPAddr, FTy->getPointerTo()->getPointerTo());
- llvm::Value *VirtualFn = Builder.CreateAlignedLoad(
- VFPAddr, CGF.getPointerAlign(), "memptr.virtualfn");
+ llvm::Value *VirtualFn;
+ if (RD->hasDefinition() && RD->isRelativeCXXABI()) {
+ VirtualFn =
+ Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::load_relative,
+ {VTableOffset->getType()}),
+ {VTable, VTableOffset});
+ VirtualFn = Builder.CreateBitCast(VirtualFn, FTy->getPointerTo());
+ } else {
+ // Apply the offset.
+ llvm::Value *VTableSlotPtr =
+ Builder.CreateBitCast(VFPAddr, FTy->getPointerTo()->getPointerTo());
+ VirtualFn = Builder.CreateAlignedLoad(VTableSlotPtr, CGF.getPointerAlign(),
+ "memptr.virtualfn");
+ }
CGF.EmitBranch(FnEnd);
// In the non-virtual path, the function pointer is actually a
@@ -914,7 +925,11 @@
const ASTContext &Context = getContext();
CharUnits PointerWidth =
Context.toCharUnitsFromBits(Context.getTargetInfo().getPointerWidth(0));
- uint64_t VTableOffset = (Index * PointerWidth.getQuantity());
+ uint64_t VTableOffset = Index;
+ if (MD->getParent()->isRelativeCXXABI())
+ VTableOffset *= 4;
+ else
+ VTableOffset *= PointerWidth.getQuantity();
if (UseARMMethodPtrABI) {
// ARM C++ ABI 3.2.1:
@@ -1584,10 +1599,7 @@
CGM.GetAddrOfRTTIDescriptor(CGM.getContext().getTagDeclType(RD));
// Create and set the initializer.
- ConstantInitBuilder Builder(CGM);
- auto Components = Builder.beginStruct();
- CGVT.createVTableInitializer(Components, VTLayout, RTTI);
- Components.finishAndSetAsInitializer(VTable);
+ CGVT.SetVTableInitializer(VTable, VTLayout, RTTI, RD->isRelativeCXXABI());
// Set the correct linkage.
VTable->setLinkage(Linkage);
@@ -1695,7 +1707,7 @@
const VTableLayout &VTLayout =
CGM.getItaniumVTableContext().getVTableLayout(RD);
- llvm::Type *VTableType = CGM.getVTables().getVTableType(VTLayout);
+ llvm::Type *VTableType = CGM.getVTables().getVTableType(RD, VTLayout);
// Use pointer alignment for the vtable. Otherwise we would align them based
// on the size of the initializer which doesn't make sense as only single
@@ -1717,9 +1729,9 @@
Address This,
llvm::Type *Ty,
SourceLocation Loc) {
- Ty = Ty->getPointerTo()->getPointerTo();
auto *MethodDecl = cast<CXXMethodDecl>(GD.getDecl());
- llvm::Value *VTable = CGF.GetVTablePtr(This, Ty, MethodDecl->getParent());
+ llvm::Value *VTable = CGF.GetVTablePtr(
+ This, Ty->getPointerTo()->getPointerTo(), MethodDecl->getParent());
uint64_t VTableIndex = CGM.getItaniumVTableContext().getMethodVTableIndex(GD);
llvm::Value *VFunc;
@@ -1730,10 +1742,8 @@
} else {
CGF.EmitTypeMetadataCodeForVCall(MethodDecl->getParent(), VTable, Loc);
- llvm::Value *VFuncPtr =
- CGF.Builder.CreateConstInBoundsGEP1_64(VTable, VTableIndex, "vfn");
- auto *VFuncLoad =
- CGF.Builder.CreateAlignedLoad(VFuncPtr, CGF.getPointerAlign());
+ auto *VFuncLoad = CGF.GetVirtualFunctionFromVTable(MethodDecl->getParent(),
+ VTable, VTableIndex, Ty);
// Add !invariant.load md to virtual function load to indicate that
// function didn't change inside vtable.
@@ -1742,11 +1752,14 @@
// the same virtual function loads from the same vtable load, which won't
// happen without enabled devirtualization with -fstrict-vtable-pointers.
if (CGM.getCodeGenOpts().OptimizationLevel > 0 &&
- CGM.getCodeGenOpts().StrictVTablePointers)
- VFuncLoad->setMetadata(
- llvm::LLVMContext::MD_invariant_load,
- llvm::MDNode::get(CGM.getLLVMContext(),
- llvm::ArrayRef<llvm::Metadata *>()));
+ CGM.getCodeGenOpts().StrictVTablePointers) {
+ if (auto *VFuncLoadInstr = dyn_cast<llvm::Instruction>(VFuncLoad)) {
+ VFuncLoadInstr->setMetadata(
+ llvm::LLVMContext::MD_invariant_load,
+ llvm::MDNode::get(CGM.getLLVMContext(),
+ llvm::ArrayRef<llvm::Metadata *>()));
+ }
+ }
VFunc = VFuncLoad;
}
Index: clang/lib/CodeGen/CodeGenModule.cpp
===================================================================
--- clang/lib/CodeGen/CodeGenModule.cpp
+++ clang/lib/CodeGen/CodeGenModule.cpp
@@ -531,6 +531,12 @@
CodeGenOpts.FlushDenorm ? 1 : 0);
}
+ if (CodeGenOpts.PrepareForLTO) {
+ getModule().addModuleFlag(llvm::Module::Error,
+ "lto-relative-c++-abi-vtables",
+ getLangOpts().LTORelativeCXXABIVTables);
+ }
+
// Emit OpenCL specific module metadata: OpenCL/SPIR version.
if (LangOpts.OpenCL) {
EmitOpenCLMetadata();
@@ -1198,7 +1204,7 @@
const CXXMethodDecl *MD) {
// Check that the type metadata can ever actually be used by a call.
if (!CGM.getCodeGenOpts().LTOUnit ||
- !CGM.HasHiddenLTOVisibility(MD->getParent()))
+ !MD->getParent()->hasHiddenLTOVisibility())
return false;
// Only functions whose address can be taken with a member function pointer
@@ -2988,7 +2994,7 @@
F->setCallingConv(getRuntimeCC());
if (!Local && getTriple().isOSBinFormatCOFF() &&
- !getCodeGenOpts().LTOVisibilityPublicStd &&
+ !getLangOpts().LTOVisibilityPublicStd &&
!getTriple().isWindowsGNUEnvironment()) {
const FunctionDecl *FD = GetRuntimeFunctionDecl(Context, Name);
if (!FD || FD->hasAttr<DLLImportAttr>()) {
Index: clang/lib/CodeGen/CodeGenFunction.h
===================================================================
--- clang/lib/CodeGen/CodeGenFunction.h
+++ clang/lib/CodeGen/CodeGenFunction.h
@@ -1904,6 +1904,11 @@
llvm::Value *GetVTablePtr(Address This, llvm::Type *VTableTy,
const CXXRecordDecl *VTableClass);
+ llvm::Value *GetVirtualFunctionFromVTable(const CXXRecordDecl *RD,
+ llvm::Value *VTable,
+ uint64_t VTableIndex,
+ llvm::Type *Ty);
+
enum CFITypeCheckKind {
CFITCK_VCall,
CFITCK_NVCall,
Index: clang/lib/CodeGen/CGVTables.h
===================================================================
--- clang/lib/CodeGen/CGVTables.h
+++ clang/lib/CodeGen/CGVTables.h
@@ -61,17 +61,32 @@
const ThunkInfo &ThunkAdjustments,
bool ForVTable);
+ /// Get the constant to be added to a VTable.
+ llvm::Constant *getVTableComponent(const VTableLayout &layout,
+ unsigned componentIdx, unsigned vtableIdx,
+ llvm::Constant *rtti,
+ unsigned &nextVTableThunkIndex,
+ bool RelativeABI,
+ llvm::GlobalVariable *VTable);
+
+ /// Add a constant to a VTable through a builder.
void addVTableComponent(ConstantArrayBuilder &builder,
- const VTableLayout &layout, unsigned idx,
- llvm::Constant *rtti,
- unsigned &nextVTableThunkIndex);
+ const VTableLayout &layout, unsigned componentIdx,
+ unsigned vtableIdx, llvm::Constant *rtti,
+ unsigned &nextVTableThunkIndex, bool RelativeABI,
+ llvm::GlobalVariable *VTable);
+
+ /// Make a function pointer into a relative integer when using the relative
+ /// vtables ABI.
+ llvm::Constant *makeRelative(llvm::Constant *C, llvm::GlobalVariable *VTable,
+ unsigned vtableIdx, unsigned relCompIdx) const;
public:
/// Add vtable components for the given vtable layout to the given
/// global initializer.
- void createVTableInitializer(ConstantStructBuilder &builder,
- const VTableLayout &layout,
- llvm::Constant *rtti);
+ void SetVTableInitializer(llvm::GlobalVariable *VTable,
+ const VTableLayout &VTLayout, llvm::Constant *RTTI,
+ bool RelativeABI);
CodeGenVTables(CodeGenModule &CGM);
@@ -123,7 +138,8 @@
/// Returns the type of a vtable with the given layout. Normally a struct of
/// arrays of pointers, with one struct element for each vtable in the vtable
/// group.
- llvm::Type *getVTableType(const VTableLayout &layout);
+ llvm::Type *getVTableType(const CXXRecordDecl *RD,
+ const VTableLayout &layout);
};
} // end namespace CodeGen
Index: clang/lib/CodeGen/CGVTables.cpp
===================================================================
--- clang/lib/CodeGen/CGVTables.cpp
+++ clang/lib/CodeGen/CGVTables.cpp
@@ -586,15 +586,45 @@
maybeEmitThunk(GD, Thunk, /*ForVTable=*/false);
}
-void CodeGenVTables::addVTableComponent(
- ConstantArrayBuilder &builder, const VTableLayout &layout,
- unsigned idx, llvm::Constant *rtti, unsigned &nextVTableThunkIndex) {
- auto &component = layout.vtable_components()[idx];
+llvm::Constant *CodeGenVTables::makeRelative(llvm::Constant *C,
+ llvm::GlobalVariable *VTable,
+ unsigned vtableIdx,
+ unsigned relCompIdx) const {
+ llvm::Type *Int32Ty = CGM.Int32Ty;
+ llvm::Type *PtrDiffTy =
+ CGM.getTypes().ConvertType(CGM.getContext().getPointerDiffType());
+ llvm::Type *VTableTy = VTable->getValueType();
+ llvm::Constant *gep = llvm::ConstantExpr::getGetElementPtr(
+ VTableTy, VTable,
+ llvm::ArrayRef<llvm::Constant *>{
+ llvm::ConstantInt::get(Int32Ty, 0),
+ llvm::ConstantInt::get(Int32Ty, vtableIdx),
+ llvm::ConstantInt::get(Int32Ty, relCompIdx)});
+ llvm::Constant *AddrPointInt =
+ llvm::ConstantExpr::getPtrToInt(gep, PtrDiffTy);
+
+ // FIXME: Need a better way of identifying address points that works with
+ // the Itanium and MS ABIs.
+ llvm::Constant *ptrToInt = llvm::ConstantExpr::getPtrToInt(C, PtrDiffTy);
+ return llvm::ConstantExpr::getIntegerCast(
+ llvm::ConstantExpr::getSub(ptrToInt, AddrPointInt), Int32Ty,
+ /*isSigned=*/true);
+}
+
+llvm::Constant *CodeGenVTables::getVTableComponent(
+ const VTableLayout &layout, unsigned componentIdx, unsigned vtableIdx,
+ llvm::Constant *rtti, unsigned &nextVTableThunkIndex, bool RelativeABI,
+ llvm::GlobalVariable *VTable) {
+ auto &component = layout.getVTableComponent(componentIdx);
+
+ llvm::Type *Int8PtrTy = CGM.Int8PtrTy;
+ llvm::Type *Int32Ty = CGM.Int32Ty;
+ llvm::Type *FunctionPtrTy = RelativeABI ? Int32Ty : Int8PtrTy;
auto addOffsetConstant = [&](CharUnits offset) {
- builder.add(llvm::ConstantExpr::getIntToPtr(
+ return llvm::ConstantExpr::getIntToPtr(
llvm::ConstantInt::get(CGM.PtrDiffTy, offset.getQuantity()),
- CGM.Int8PtrTy));
+ CGM.Int8PtrTy);
};
switch (component.getKind()) {
@@ -608,7 +638,7 @@
return addOffsetConstant(component.getOffsetToTop());
case VTableComponent::CK_RTTI:
- return builder.add(llvm::ConstantExpr::getBitCast(rtti, CGM.Int8PtrTy));
+ return llvm::ConstantExpr::getBitCast(rtti, CGM.Int8PtrTy);
case VTableComponent::CK_FunctionPointer:
case VTableComponent::CK_CompleteDtorPointer:
@@ -642,7 +672,7 @@
? MD->hasAttr<CUDADeviceAttr>()
: (MD->hasAttr<CUDAHostAttr>() || !MD->hasAttr<CUDADeviceAttr>());
if (!CanEmitMethod)
- return builder.addNullPointer(CGM.Int8PtrTy);
+ return llvm::ConstantExpr::getNullValue(FunctionPtrTy);
// Method is acceptable, continue processing as usual.
}
@@ -674,7 +704,8 @@
// Thunks.
} else if (nextVTableThunkIndex < layout.vtable_thunks().size() &&
- layout.vtable_thunks()[nextVTableThunkIndex].first == idx) {
+ layout.vtable_thunks()[nextVTableThunkIndex].first ==
+ componentIdx) {
auto &thunkInfo = layout.vtable_thunks()[nextVTableThunkIndex].second;
nextVTableThunkIndex++;
@@ -687,39 +718,94 @@
}
fnPtr = llvm::ConstantExpr::getBitCast(fnPtr, CGM.Int8PtrTy);
- builder.add(fnPtr);
- return;
+ if (RelativeABI && component.isFunctionPointerKind()) {
+ size_t thisIndex = layout.getVTableOffset(vtableIdx);
+ return makeRelative(fnPtr, VTable, vtableIdx, componentIdx - thisIndex);
+ }
+ return fnPtr;
}
case VTableComponent::CK_UnusedFunctionPointer:
- return builder.addNullPointer(CGM.Int8PtrTy);
+ return llvm::ConstantExpr::getNullValue(FunctionPtrTy);
}
llvm_unreachable("Unexpected vtable component kind");
}
-llvm::Type *CodeGenVTables::getVTableType(const VTableLayout &layout) {
+void CodeGenVTables::addVTableComponent(
+ ConstantArrayBuilder &builder, const VTableLayout &layout,
+ unsigned componentIdx, unsigned vtableIdx, llvm::Constant *rtti,
+ unsigned &nextVTableThunkIndex, bool RelativeABI,
+ llvm::GlobalVariable *VTable) {
+ builder.add(getVTableComponent(layout, componentIdx, vtableIdx, rtti,
+ nextVTableThunkIndex, RelativeABI, VTable));
+}
+
+llvm::Type *CodeGenVTables::getVTableType(const CXXRecordDecl *RD,
+ const VTableLayout &layout) {
SmallVector<llvm::Type *, 4> tys;
for (unsigned i = 0, e = layout.getNumVTables(); i != e; ++i) {
- tys.push_back(llvm::ArrayType::get(CGM.Int8PtrTy, layout.getVTableSize(i)));
+ if (!RD->isRelativeCXXABI()) {
+ tys.push_back(
+ llvm::ArrayType::get(CGM.Int8PtrTy, layout.getVTableSize(i)));
+ } else {
+ SmallVector<llvm::Type *, 4> innerTypes;
+ size_t thisIndex = layout.getVTableOffset(i);
+ size_t nextIndex = thisIndex + layout.getVTableSize(i);
+ for (unsigned componentIdx = thisIndex; componentIdx != nextIndex;
+ ++componentIdx) {
+ auto &component = layout.getVTableComponent(componentIdx);
+ if (component.isFunctionPointerKind())
+ innerTypes.push_back(CGM.Int32Ty);
+ else
+ innerTypes.push_back(CGM.Int8PtrTy);
+ }
+ tys.push_back(llvm::StructType::get(CGM.getLLVMContext(), innerTypes));
+ }
}
return llvm::StructType::get(CGM.getLLVMContext(), tys);
}
-void CodeGenVTables::createVTableInitializer(ConstantStructBuilder &builder,
- const VTableLayout &layout,
- llvm::Constant *rtti) {
+void CodeGenVTables::SetVTableInitializer(llvm::GlobalVariable *VTable,
+ const VTableLayout &VTLayout,
+ llvm::Constant *RTTI,
+ bool RelativeABI) {
+ ConstantInitBuilder builder(CGM);
+ auto components = builder.beginStruct();
+
unsigned nextVTableThunkIndex = 0;
- for (unsigned i = 0, e = layout.getNumVTables(); i != e; ++i) {
- auto vtableElem = builder.beginArray(CGM.Int8PtrTy);
- size_t thisIndex = layout.getVTableOffset(i);
- size_t nextIndex = thisIndex + layout.getVTableSize(i);
- for (unsigned i = thisIndex; i != nextIndex; ++i) {
- addVTableComponent(vtableElem, layout, i, rtti, nextVTableThunkIndex);
+ for (unsigned vtableIdx = 0; vtableIdx != VTLayout.getNumVTables();
+ ++vtableIdx) {
+ size_t thisIndex = VTLayout.getVTableOffset(vtableIdx);
+ size_t nextIndex = thisIndex + VTLayout.getVTableSize(vtableIdx);
+
+ if (!RelativeABI) {
+ // Construct a normal array of pointers.
+ auto vtableElem = components.beginArray(CGM.Int8PtrTy);
+ for (unsigned componentIdx = thisIndex; componentIdx != nextIndex;
+ ++componentIdx) {
+ addVTableComponent(vtableElem, VTLayout, componentIdx, vtableIdx, RTTI,
+ nextVTableThunkIndex, RelativeABI, VTable);
+ }
+ vtableElem.finishAndAddTo(components);
+ } else {
+ // Instead use an i32 to indicate an offset.
+ SmallVector<llvm::Constant *, 64> Inits;
+ for (unsigned componentIdx = thisIndex; componentIdx != nextIndex;
+ ++componentIdx) {
+ Inits.push_back(getVTableComponent(VTLayout, componentIdx, vtableIdx,
+ RTTI, nextVTableThunkIndex,
+ RelativeABI, VTable));
+ }
+ auto *VTableTy = cast<llvm::StructType>(VTable->getValueType());
+ auto *StructTy =
+ cast<llvm::StructType>(VTableTy->getElementType(vtableIdx));
+ components.add(llvm::ConstantStruct::get(StructTy, Inits));
}
- vtableElem.finishAndAddTo(builder);
}
+
+ components.finishAndSetAsInitializer(VTable);
}
llvm::GlobalVariable *
@@ -746,7 +832,7 @@
Base.getBase(), Out);
StringRef Name = OutName.str();
- llvm::Type *VTType = getVTableType(*VTLayout);
+ llvm::Type *VTType = getVTableType(RD, *VTLayout);
// Construction vtable symbols are not part of the Itanium ABI, so we cannot
// guarantee that they actually will be available externally. Instead, when
@@ -769,10 +855,7 @@
CGM.getContext().getTagDeclType(Base.getBase()));
// Create and set the initializer.
- ConstantInitBuilder builder(CGM);
- auto components = builder.beginStruct();
- createVTableInitializer(components, *VTLayout, RTTI);
- components.finishAndSetAsInitializer(VTable);
+ SetVTableInitializer(VTable, *VTLayout, RTTI, RD->isRelativeCXXABI());
// Set properties only after the initializer has been set to ensure that the
// GV is treated as definition and not declaration.
@@ -976,40 +1059,6 @@
DeferredVTables.clear();
}
-bool CodeGenModule::HasHiddenLTOVisibility(const CXXRecordDecl *RD) {
- LinkageInfo LV = RD->getLinkageAndVisibility();
- if (!isExternallyVisible(LV.getLinkage()))
- return true;
-
- if (RD->hasAttr<LTOVisibilityPublicAttr>() || RD->hasAttr<UuidAttr>())
- return false;
-
- if (getTriple().isOSBinFormatCOFF()) {
- if (RD->hasAttr<DLLExportAttr>() || RD->hasAttr<DLLImportAttr>())
- return false;
- } else {
- if (LV.getVisibility() != HiddenVisibility)
- return false;
- }
-
- if (getCodeGenOpts().LTOVisibilityPublicStd) {
- const DeclContext *DC = RD;
- while (1) {
- auto *D = cast<Decl>(DC);
- DC = DC->getParent();
- if (isa<TranslationUnitDecl>(DC->getRedeclContext())) {
- if (auto *ND = dyn_cast<NamespaceDecl>(D))
- if (const IdentifierInfo *II = ND->getIdentifier())
- if (II->isStr("std") || II->isStr("stdext"))
- return false;
- break;
- }
- }
- }
-
- return true;
-}
-
void CodeGenModule::EmitVTableTypeMetadata(llvm::GlobalVariable *VTable,
const VTableLayout &VTLayout) {
if (!getCodeGenOpts().LTOUnit)
@@ -1053,8 +1102,10 @@
ArrayRef<VTableComponent> Comps = VTLayout.vtable_components();
for (auto AP : AddressPoints) {
+ CharUnits ByteOffset = PointerWidth * AP.second;
+
// Create type metadata for the address point.
- AddVTableTypeMetadata(VTable, PointerWidth * AP.second, AP.first);
+ AddVTableTypeMetadata(VTable, ByteOffset, AP.first);
// The class associated with each address point could also potentially be
// used for indirect calls via a member function pointer, so we need to
Index: clang/lib/CodeGen/CGDebugInfo.cpp
===================================================================
--- clang/lib/CodeGen/CGDebugInfo.cpp
+++ clang/lib/CodeGen/CGDebugInfo.cpp
@@ -1537,8 +1537,11 @@
if (CGM.getTarget().getCXXABI().isItaniumFamily()) {
// It doesn't make sense to give a virtual destructor a vtable index,
// since a single destructor has two entries in the vtable.
- if (!isa<CXXDestructorDecl>(Method))
+ if (!isa<CXXDestructorDecl>(Method) &&
+ !Method->getParent()->isRelativeCXXABI())
VIndex = CGM.getItaniumVTableContext().getMethodVTableIndex(Method);
+ else
+ VIndex = -1u;
} else {
// Emit MS ABI vftable information. There is only one entry for the
// deleting dtor.
Index: clang/lib/CodeGen/CGClass.cpp
===================================================================
--- clang/lib/CodeGen/CGClass.cpp
+++ clang/lib/CodeGen/CGClass.cpp
@@ -2573,6 +2573,23 @@
return VTable;
}
+llvm::Value *CodeGenFunction::GetVirtualFunctionFromVTable(
+ const CXXRecordDecl *RD, llvm::Value *VTable, uint64_t VTableIndex,
+ llvm::Type *Ty) {
+ if (!RD->isRelativeCXXABI()) {
+ VTable = Builder.CreateBitCast(VTable, Ty->getPointerTo()->getPointerTo());
+ llvm::Value *VTableSlotPtr =
+ Builder.CreateConstInBoundsGEP1_64(VTable, VTableIndex, "vfn");
+ return Builder.CreateAlignedLoad(VTableSlotPtr, getPointerAlign());
+ }
+
+ VTable = Builder.CreateBitCast(VTable, Int8PtrTy);
+ llvm::Value *Load = Builder.CreateCall(
+ CGM.getIntrinsic(llvm::Intrinsic::load_relative, {Int32Ty}),
+ {VTable, llvm::ConstantInt::get(Int32Ty, 4 * VTableIndex)});
+ return Builder.CreateBitCast(Load, Ty->getPointerTo());
+}
+
// If a class has a single non-virtual base and does not introduce or override
// virtual member functions or fields, it will have the same layout as its base.
// This function returns the least derived such class.
@@ -2614,7 +2631,7 @@
if (SanOpts.has(SanitizerKind::CFIVCall))
EmitVTablePtrCheckForCall(RD, VTable, CodeGenFunction::CFITCK_VCall, Loc);
else if (CGM.getCodeGenOpts().WholeProgramVTables &&
- CGM.HasHiddenLTOVisibility(RD)) {
+ RD->hasHiddenLTOVisibility()) {
llvm::Metadata *MD =
CGM.CreateMetadataIdentifierForType(QualType(RD->getTypeForDecl(), 0));
llvm::Value *TypeId =
@@ -2689,7 +2706,7 @@
CFITypeCheckKind TCK,
SourceLocation Loc) {
if (!CGM.getCodeGenOpts().SanitizeCfiCrossDso &&
- !CGM.HasHiddenLTOVisibility(RD))
+ !RD->hasHiddenLTOVisibility())
return;
SanitizerMask M;
@@ -2762,7 +2779,7 @@
if (!CGM.getCodeGenOpts().WholeProgramVTables ||
!SanOpts.has(SanitizerKind::CFIVCall) ||
!CGM.getCodeGenOpts().SanitizeTrap.has(SanitizerKind::CFIVCall) ||
- !CGM.HasHiddenLTOVisibility(RD))
+ !RD->hasHiddenLTOVisibility())
return false;
std::string TypeName = RD->getQualifiedNameAsString();
Index: clang/lib/AST/DeclCXX.cpp
===================================================================
--- clang/lib/AST/DeclCXX.cpp
+++ clang/lib/AST/DeclCXX.cpp
@@ -35,6 +35,7 @@
#include "clang/Basic/PartialDiagnostic.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/Specifiers.h"
+#include "clang/Basic/TargetInfo.h"
#include "llvm/ADT/None.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallVector.h"
@@ -102,7 +103,8 @@
ImplicitCopyAssignmentHasConstParam(true),
HasDeclaredCopyConstructorWithConstParam(false),
HasDeclaredCopyAssignmentWithConstParam(false), IsLambda(false),
- IsParsingBaseSpecifiers(false), HasODRHash(false), Definition(D) {}
+ IsParsingBaseSpecifiers(false), IsRelativeCXXABI(false),
+ HasODRHash(false), Definition(D) {}
CXXBaseSpecifier *CXXRecordDecl::DefinitionData::getBasesSlowCase() const {
return Bases.get(Definition->getASTContext().getExternalSource());
@@ -1860,6 +1862,41 @@
return false;
}
+bool CXXRecordDecl::hasHiddenLTOVisibility() const {
+ LinkageInfo LV = getLinkageAndVisibility();
+ if (!clang::isExternallyVisible(LV.getLinkage()))
+ return true;
+
+ if (hasAttr<LTOVisibilityPublicAttr>() || hasAttr<UuidAttr>())
+ return false;
+
+ ASTContext &Context = getASTContext();
+ if (Context.getTargetInfo().getTriple().isOSBinFormatCOFF()) {
+ if (hasAttr<DLLExportAttr>() || hasAttr<DLLImportAttr>())
+ return false;
+ } else {
+ if (LV.getVisibility() != HiddenVisibility)
+ return false;
+ }
+
+ if (Context.getLangOpts().LTOVisibilityPublicStd) {
+ const DeclContext *DC = this;
+ while (1) {
+ auto *D = cast<Decl>(DC);
+ DC = DC->getParent();
+ if (isa<TranslationUnitDecl>(DC->getRedeclContext())) {
+ if (auto *ND = dyn_cast<NamespaceDecl>(D))
+ if (const IdentifierInfo *II = ND->getIdentifier())
+ if (II->isStr("std") || II->isStr("stdext"))
+ return false;
+ break;
+ }
+ }
+ }
+
+ return true;
+}
+
void CXXDeductionGuideDecl::anchor() {}
CXXDeductionGuideDecl *CXXDeductionGuideDecl::Create(
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -10926,6 +10926,12 @@
// (including field initializers) is fully parsed.
SmallVector<CXXRecordDecl*, 4> DelayedDllExportClasses;
+ /// Determine the ABI for this class using its attributes, bases and implicit
+ /// contexts. Check for conflicts between bases or between a base and an
+ /// attribute. Set the class's isRelativeCXXABI() flag according to the
+ /// result.
+ void checkClassABI(CXXRecordDecl *RD);
+
private:
class SavePendingParsedClassStateRAII {
public:
Index: clang/include/clang/Driver/Options.td
===================================================================
--- clang/include/clang/Driver/Options.td
+++ clang/include/clang/Driver/Options.td
@@ -1245,6 +1245,9 @@
HelpText<"Enable LTO in 'full' mode">;
def fno_lto : Flag<["-"], "fno-lto">, Group<f_Group>,
HelpText<"Disable LTO mode (default)">;
+def flto_relative_cxx_abi_vtables : Flag<["-"], "flto-relative-c++-abi-vtables">,
+ Group<f_Group>, Flags<[CC1Option]>,
+ HelpText<"Use the unstable C++ class ABI for classes with hidden LTO visibility">;
def flto_jobs_EQ : Joined<["-"], "flto-jobs=">,
Flags<[CC1Option]>, Group<f_Group>,
HelpText<"Controls the backend parallelism of -flto=thin (default "
Index: clang/include/clang/Basic/LangOptions.def
===================================================================
--- clang/include/clang/Basic/LangOptions.def
+++ clang/include/clang/Basic/LangOptions.def
@@ -322,6 +322,14 @@
LANGOPT(RegisterStaticDestructors, 1, 1, "Register C++ static destructors")
+LANGOPT(LTOVisibilityPublicStd, 1, 0, "Whether to use public LTO visibility "
+ "for entities in std and stdext "
+ "namespaces. This is enabled by "
+ "clang-cl's /MT and /MTd flags.")
+LANGOPT(LTORelativeCXXABIVTables, 1, 0,
+ "Whether to use clang's relative C++ vtable ABI "
+ "for classes with hidden LTO visibility")
+
#undef LANGOPT
#undef COMPATIBLE_LANGOPT
#undef BENIGN_LANGOPT
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -9534,4 +9534,9 @@
"%select{non-pointer|function pointer|void pointer}0 argument to "
"'__builtin_launder' is not allowed">;
+def err_abi_mismatch : Error<
+ "inconsistent ABI for class %0">;
+def note_abi_relative_base : Note<
+ "base %0 uses the %select{platform|relative}1 ABI">;
+
} // end of sema component.
Index: clang/include/clang/Basic/CodeGenOptions.def
===================================================================
--- clang/include/clang/Basic/CodeGenOptions.def
+++ clang/include/clang/Basic/CodeGenOptions.def
@@ -274,10 +274,6 @@
CODEGENOPT(WholeProgramVTables, 1, 0) ///< Whether to apply whole-program
/// vtable optimization.
-/// Whether to use public LTO visibility for entities in std and stdext
-/// namespaces. This is enabled by clang-cl's /MT and /MTd flags.
-CODEGENOPT(LTOVisibilityPublicStd, 1, 0)
-
/// The user specified number of registers to be used for integral arguments,
/// or 0 if unspecified.
VALUE_CODEGENOPT(NumRegisterParameters, 32, 0)
Index: clang/include/clang/AST/VTableBuilder.h
===================================================================
--- clang/include/clang/AST/VTableBuilder.h
+++ clang/include/clang/AST/VTableBuilder.h
@@ -264,6 +264,10 @@
return VTableComponents;
}
+ VTableComponent &getVTableComponent(size_t i) const {
+ return VTableComponents[i];
+ }
+
ArrayRef<VTableThunkTy> vtable_thunks() const {
return VTableThunks;
}
Index: clang/include/clang/AST/DeclCXX.h
===================================================================
--- clang/include/clang/AST/DeclCXX.h
+++ clang/include/clang/AST/DeclCXX.h
@@ -527,6 +527,9 @@
/// Whether we are currently parsing base specifiers.
unsigned IsParsingBaseSpecifiers : 1;
+ /// \brief Whether the class uses the relative C++ vtable ABI.
+ unsigned IsRelativeCXXABI : 1;
+
unsigned HasODRHash : 1;
/// A hash of parts of the class to help in ODR checking.
@@ -807,6 +810,10 @@
return data().IsParsingBaseSpecifiers;
}
+ void setIsRelativeCXXABI() { data().IsRelativeCXXABI = true; }
+
+ bool isRelativeCXXABI() const { return data().IsRelativeCXXABI; }
+
unsigned getODRHash() const;
/// Sets the base classes of this struct or class.
@@ -1972,6 +1979,12 @@
return getLambdaData().MethodTyInfo;
}
+ /// Returns whether this class would have hidden LTO visibility if it were
+ /// built in a translation unit with LTO, and therefore may participate in
+ /// (single-module) CFI, whole-program vtable optimization and the relative
+ /// C++ ABI.
+ bool hasHiddenLTOVisibility() const;
+
// Determine whether this type is an Interface Like type for
// __interface inheritance purposes.
bool isInterfaceLike() const;
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits