zhaomo updated this revision to Diff 195148.
zhaomo marked 7 inline comments as done.
zhaomo added a comment.
Herald added a project: LLVM.
Herald added a subscriber: llvm-commits.
Fixed bugs in the previous patch.
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D51905/new/
https://reviews.llvm.org/D51905
Files:
clang/include/clang/AST/VTableBuilder.h
clang/include/clang/Basic/ABI.h
clang/include/clang/Basic/CodeGenOptions.def
clang/include/clang/Driver/CC1Options.td
clang/lib/AST/VTableBuilder.cpp
clang/lib/CodeGen/CGCXXABI.h
clang/lib/CodeGen/CGClass.cpp
clang/lib/CodeGen/CGVTables.cpp
clang/lib/CodeGen/CodeGenFunction.h
clang/lib/CodeGen/CodeGenModule.cpp
clang/lib/CodeGen/CodeGenModule.h
clang/lib/CodeGen/ItaniumCXXABI.cpp
clang/lib/CodeGen/MicrosoftCXXABI.cpp
clang/lib/Frontend/CompilerInvocation.cpp
clang/test/CodeGen/tbaa-for-vptr.cpp
clang/test/CodeGenCXX/alignment.cpp
clang/test/CodeGenCXX/arm.cpp
clang/test/CodeGenCXX/cfi-cross-dso.cpp
clang/test/CodeGenCXX/constructor-destructor-return-this.cpp
clang/test/CodeGenCXX/constructor-init.cpp
clang/test/CodeGenCXX/delete.cpp
clang/test/CodeGenCXX/devirtualize-virtual-function-calls-final.cpp
clang/test/CodeGenCXX/interleaving-member-function-pointers.cpp
clang/test/CodeGenCXX/interleaving-virtual-base.cpp
clang/test/CodeGenCXX/interleaving-virtual-calls.cpp
clang/test/CodeGenCXX/ubsan-vtable-checks.cpp
clang/test/CodeGenCXX/virtual-base-cast.cpp
compiler-rt/test/cfi/CMakeLists.txt
compiler-rt/test/cfi/anon-namespace.cpp
compiler-rt/test/cfi/cross-dso/lit.local.cfg
compiler-rt/test/cfi/icall/lit.local.cfg
compiler-rt/test/cfi/lit.cfg
compiler-rt/test/cfi/lit.site.cfg.in
compiler-rt/test/cfi/mfcall.cpp
compiler-rt/test/cfi/multiple-inheritance.cpp
compiler-rt/test/cfi/simple-fail.cpp
compiler-rt/test/cfi/vdtor.cpp
compiler-rt/test/lit.common.configured.in
Index: compiler-rt/test/lit.common.configured.in
===================================================================
--- compiler-rt/test/lit.common.configured.in
+++ compiler-rt/test/lit.common.configured.in
@@ -36,6 +36,7 @@
set_default("use_thinlto", False)
set_default("use_lto", config.use_thinlto)
set_default("use_newpm", False)
+set_default("use_interleaving", False)
set_default("android", @ANDROID_PYBOOL@)
set_default("android_ndk_version", @ANDROID_NDK_VERSION@)
set_default("android_serial", "@ANDROID_SERIAL_FOR_TESTING@")
Index: compiler-rt/test/cfi/vdtor.cpp
===================================================================
--- compiler-rt/test/cfi/vdtor.cpp
+++ compiler-rt/test/cfi/vdtor.cpp
@@ -14,7 +14,7 @@
// RUN: %run %t5 2>&1 | FileCheck --check-prefix=NCFI %s
// RUN: %clangxx_cfi_diag -o %t6 %s
-// RUN: %run %t6 2>&1 | FileCheck --check-prefix=CFI-DIAG %s
+// RUN: %interleave_diag %run %t6 2>&1 | FileCheck --check-prefix=CFI-DIAG %s
// Tests that the CFI enforcement also applies to virtual destructor calls made
// via 'delete'.
Index: compiler-rt/test/cfi/simple-fail.cpp
===================================================================
--- compiler-rt/test/cfi/simple-fail.cpp
+++ compiler-rt/test/cfi/simple-fail.cpp
@@ -47,12 +47,12 @@
// RUN: %expect_crash %run %t16 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi_diag -o %t17 %s
-// RUN: %run %t17 2>&1 | FileCheck --check-prefix=CFI-DIAG %s
+// RUN: %interleave_diag %run %t17 2>&1 | FileCheck --check-prefix=CFI-DIAG %s
// RUN: %clangxx -o %t18 %s
// RUN: %run %t18 2>&1 | FileCheck --check-prefix=NCFI %s
-// RUN: %clangxx_cfi -DCHECK_NO_SANITIZE_CFI -o %t19 %s
+// RUN: %clangxx_cfi_no_interleaving -DCHECK_NO_SANITIZE_CFI -o %t19 %s
// RUN: %run %t19 2>&1 | FileCheck --check-prefix=NCFI %s
// Tests that the CFI mechanism crashes the program when making a virtual call
@@ -95,6 +95,7 @@
// CFI-DIAG-NEXT: note: vtable is of type '{{(struct )?}}A'
((B *)a)->f(); // UB here
+ // INTERLEAVING-NCFI-NOT: {{^2$}}
// CFI-NOT: {{^2$}}
// NCFI: {{^2$}}
fprintf(stderr, "2\n");
Index: compiler-rt/test/cfi/multiple-inheritance.cpp
===================================================================
--- compiler-rt/test/cfi/multiple-inheritance.cpp
+++ compiler-rt/test/cfi/multiple-inheritance.cpp
@@ -19,8 +19,8 @@
// RUN: %run %t5 x 2>&1 | FileCheck --check-prefix=NCFI %s
// RUN: %clangxx_cfi_diag -o %t6 %s
-// RUN: %run %t6 2>&1 | FileCheck --check-prefix=CFI-DIAG2 %s
-// RUN: %run %t6 x 2>&1 | FileCheck --check-prefix=CFI-DIAG1 %s
+// RUN: %interleave_diag %run %t6 2>&1 | FileCheck --check-prefix=CFI-DIAG2 %s
+// RUN: %interleave_diag %run %t6 x 2>&1 | FileCheck --check-prefix=CFI-DIAG1 %s
// Tests that the CFI mechanism is sensitive to multiple inheritance and only
// permits calls via virtual tables for the correct base class.
Index: compiler-rt/test/cfi/mfcall.cpp
===================================================================
--- compiler-rt/test/cfi/mfcall.cpp
+++ compiler-rt/test/cfi/mfcall.cpp
@@ -1,4 +1,4 @@
-// UNSUPPORTED: windows-msvc
+// UNSUPPORTED: windows-msvc, interleaving
// RUN: %clangxx_cfi -o %t %s
// RUN: %expect_crash %run %t a
Index: compiler-rt/test/cfi/lit.site.cfg.in
===================================================================
--- compiler-rt/test/cfi/lit.site.cfg.in
+++ compiler-rt/test/cfi/lit.site.cfg.in
@@ -8,6 +8,7 @@
config.use_lto = True # CFI *requires* LTO.
config.use_thinlto = @CFI_TEST_USE_THINLTO@
config.use_newpm = @CFI_TEST_USE_NEWPM@
+config.use_interleaving = @CFI_TEST_USE_INTERLEAVING@
lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/test/lit.common.configured")
lit_config.load_config(config, "@CMAKE_CURRENT_SOURCE_DIR@/lit.cfg")
Index: compiler-rt/test/cfi/lit.cfg
===================================================================
--- compiler-rt/test/cfi/lit.cfg
+++ compiler-rt/test/cfi/lit.cfg
@@ -19,7 +19,10 @@
if config.cfi_lit_test_mode == "Devirt":
config.available_features.add('devirt')
clang_cfi += '-fwhole-program-vtables '
- config.substitutions.append((r"%expect_crash_unless_devirt ", ""))
+ if config.use_interleaving:
+ config.substitutions.append((r"%expect_crash_unless_devirt ", config.expect_crash))
+ else:
+ config.substitutions.append((r"%expect_crash_unless_devirt ", ""))
else:
config.substitutions.append((r"%expect_crash_unless_devirt ", config.expect_crash))
@@ -27,14 +30,27 @@
diag = '-fno-sanitize-trap=cfi -fsanitize-recover=cfi '
non_dso = '-fvisibility=hidden '
dso = '-fsanitize-cfi-cross-dso -fvisibility=default '
+ interleave = '-Xclang -vtable-interleaving -Wl,-plugin-opt,-vtable-interleaving -fno-sanitize=cfi-mfcall '
if config.android:
dso += '-include ' + config.test_source_root + '/cross-dso/util/cfi_stubs.h '
+
config.substitutions.append((r"%clang_cfi ", clang_cfi + non_dso))
- config.substitutions.append((r"%clangxx_cfi ", clang_cfi + cxx + non_dso))
config.substitutions.append((r"%clang_cfi_diag ", clang_cfi + non_dso + diag))
- config.substitutions.append((r"%clangxx_cfi_diag ", clang_cfi + cxx + non_dso + diag))
- config.substitutions.append((r"%clangxx_cfi_dso ", clang_cfi + cxx + dso))
- config.substitutions.append((r"%clangxx_cfi_dso_diag ", clang_cfi + cxx + dso + diag))
+
+ if config.use_interleaving:
+ config.available_features.add('interleaving')
+ config.substitutions.append((r"%clangxx_cfi ", clang_cfi + cxx + non_dso + interleave))
+ config.substitutions.append((r"%clangxx_cfi_diag ", clang_cfi + cxx + non_dso + diag + interleave))
+ config.substitutions.append((r"%interleave_diag ", "not "))
+ else:
+ config.substitutions.append((r"%clangxx_cfi ", clang_cfi + cxx + non_dso))
+ config.substitutions.append((r"%clangxx_cfi_dso ", clang_cfi + cxx + dso))
+ config.substitutions.append((r"%clangxx_cfi_dso_diag ", clang_cfi + cxx + dso + diag))
+ config.substitutions.append((r"%clangxx_cfi_diag ", clang_cfi + cxx + non_dso + diag))
+ config.substitutions.append((r"%interleave_diag ", ""))
+
+ config.substitutions.append((r"%clangxx_cfi_no_interleaving ", clang_cfi + cxx + non_dso))
+
config.substitutions.append((r"%debug_info_flags", ' '.join(config.debug_info_flags)))
else:
config.unsupported = True
@@ -44,3 +60,4 @@
if lit_config.params.get('check_supported', None) and config.unsupported:
raise BaseException("Tests unsupported")
+
Index: compiler-rt/test/cfi/icall/lit.local.cfg
===================================================================
--- compiler-rt/test/cfi/icall/lit.local.cfg
+++ compiler-rt/test/cfi/icall/lit.local.cfg
@@ -1,3 +1,6 @@
# The cfi-icall checker is only supported on x86 and x86_64 for now.
if config.root.host_arch not in ['x86', 'x86_64']:
config.unsupported = True
+
+if 'interleaving' in config.available_features:
+ config.unsupported = True
\ No newline at end of file
Index: compiler-rt/test/cfi/cross-dso/lit.local.cfg
===================================================================
--- compiler-rt/test/cfi/cross-dso/lit.local.cfg
+++ compiler-rt/test/cfi/cross-dso/lit.local.cfg
@@ -11,3 +11,6 @@
# Android O (API level 26) has support for cross-dso cfi in libdl.so.
if config.android and 'android-26' not in config.available_features:
config.unsupported = True
+
+if 'interleaving' in config.available_features:
+ config.unsupported = True
\ No newline at end of file
Index: compiler-rt/test/cfi/anon-namespace.cpp
===================================================================
--- compiler-rt/test/cfi/anon-namespace.cpp
+++ compiler-rt/test/cfi/anon-namespace.cpp
@@ -26,7 +26,7 @@
// RUN: %clangxx_cfi_diag -c -DTU1 -o %t1.o %s
// RUN: %clangxx_cfi_diag -c -DTU2 -o %t2.o %S/../cfi/anon-namespace.cpp
// RUN: %clangxx_cfi_diag -o %t6 %t1.o %t2.o
-// RUN: %run %t6 2>&1 | FileCheck --check-prefix=CFI-DIAG %s
+// RUN: %interleave_diag %run %t6 2>&1 | FileCheck --check-prefix=CFI-DIAG %s
// Tests that the CFI mechanism treats classes in the anonymous namespace in
// different translation units as having distinct identities. This is done by
Index: compiler-rt/test/cfi/CMakeLists.txt
===================================================================
--- compiler-rt/test/cfi/CMakeLists.txt
+++ compiler-rt/test/cfi/CMakeLists.txt
@@ -1,6 +1,6 @@
set(CFI_TESTSUITES)
-macro (add_cfi_test_suites lld thinlto newpm)
+macro (add_cfi_test_suites lld thinlto newpm interleaving)
set(suffix)
if (${lld})
set(suffix ${suffix}-lld)
@@ -11,11 +11,15 @@
if (${newpm})
set(suffix ${suffix}-newpm)
endif()
+ if (${interleaving})
+ set(suffix ${suffix}-interleaving)
+ endif()
set(suffix ${suffix}-${CFI_TEST_TARGET_ARCH})
set(CFI_TEST_USE_LLD ${lld})
set(CFI_TEST_USE_THINLTO ${thinlto})
set(CFI_TEST_USE_NEWPM ${newpm})
+ set(CFI_TEST_USE_INTERLEAVING ${interleaving})
set(CFI_LIT_TEST_MODE Standalone)
set(CFI_TEST_CONFIG_SUFFIX -standalone${suffix})
@@ -44,19 +48,19 @@
get_test_cc_for_arch(${arch} CFI_TEST_TARGET_CC CFI_TEST_TARGET_CFLAGS)
if (APPLE)
# FIXME: enable ThinLTO tests after fixing http://llvm.org/pr32741
- add_cfi_test_suites(False False False)
+ add_cfi_test_suites(False False False False)
elseif(WIN32)
- add_cfi_test_suites(True False False)
- add_cfi_test_suites(True True False)
+ add_cfi_test_suites(True False False False)
+ add_cfi_test_suites(True True False False)
else()
- add_cfi_test_suites(False False False)
- add_cfi_test_suites(False True False)
- add_cfi_test_suites(False False True)
- add_cfi_test_suites(False True True)
- if (COMPILER_RT_HAS_LLD AND NOT arch STREQUAL "i386")
- add_cfi_test_suites(True False False)
- add_cfi_test_suites(True True False)
- endif()
+ add_cfi_test_suites(False False False False)
+ add_cfi_test_suites(False True False False)
+ add_cfi_test_suites(False False True False)
+ add_cfi_test_suites(False True True False)
+ add_cfi_test_suites(True False False False)
+ add_cfi_test_suites(True True False False)
+ add_cfi_test_suites(True False False True)
+ add_cfi_test_suites(False False False True)
endif()
endforeach()
Index: clang/test/CodeGenCXX/virtual-base-cast.cpp
===================================================================
--- clang/test/CodeGenCXX/virtual-base-cast.cpp
+++ clang/test/CodeGenCXX/virtual-base-cast.cpp
@@ -13,7 +13,7 @@
A* a() { return x; }
// CHECK: @_Z1av() [[NUW:#[0-9]+]]
-// CHECK: [[VBASEOFFSETPTRA:%[a-zA-Z0-9\.]+]] = getelementptr i8, i8* {{.*}}, i64 -16
+// CHECK: [[VBASEOFFSETPTRA:%[a-zA-Z0-9\.]+]] = getelementptr inbounds i8, i8* {{.*}}, i64 -16
// CHECK: [[CASTVBASEOFFSETPTRA:%[a-zA-Z0-9\.]+]] = bitcast i8* [[VBASEOFFSETPTRA]] to i32*
// CHECK: load i32, i32* [[CASTVBASEOFFSETPTRA]]
// CHECK: }
@@ -29,7 +29,7 @@
B* b() { return x; }
// CHECK: @_Z1bv() [[NUW]]
-// CHECK: [[VBASEOFFSETPTRA:%[a-zA-Z0-9\.]+]] = getelementptr i8, i8* {{.*}}, i64 -20
+// CHECK: [[VBASEOFFSETPTRA:%[a-zA-Z0-9\.]+]] = getelementptr inbounds i8, i8* {{.*}}, i64 -20
// CHECK: [[CASTVBASEOFFSETPTRA:%[a-zA-Z0-9\.]+]] = bitcast i8* [[VBASEOFFSETPTRA]] to i32*
// CHECK: load i32, i32* [[CASTVBASEOFFSETPTRA]]
// CHECK: }
@@ -47,7 +47,7 @@
BB* c() { return x; }
// CHECK: @_Z1cv() [[NUW]]
-// CHECK: [[VBASEOFFSETPTRC:%[a-zA-Z0-9\.]+]] = getelementptr i8, i8* {{.*}}, i64 -24
+// CHECK: [[VBASEOFFSETPTRC:%[a-zA-Z0-9\.]+]] = getelementptr inbounds i8, i8* {{.*}}, i64 -24
// CHECK: [[CASTVBASEOFFSETPTRC:%[a-zA-Z0-9\.]+]] = bitcast i8* [[VBASEOFFSETPTRC]] to i32*
// CHECK: [[VBASEOFFSETC:%[a-zA-Z0-9\.]+]] = load i32, i32* [[CASTVBASEOFFSETPTRC]]
// CHECK: add i32 [[VBASEOFFSETC]], 8
Index: clang/test/CodeGenCXX/ubsan-vtable-checks.cpp
===================================================================
--- clang/test/CodeGenCXX/ubsan-vtable-checks.cpp
+++ clang/test/CodeGenCXX/ubsan-vtable-checks.cpp
@@ -25,7 +25,10 @@
// CHECK-NULL-NEXT: br i1 [[UBSAN_CMP_RES]], label %{{.*}}, label %{{.*}}
// CHECK-NULL: call void @__ubsan_handle_type_mismatch_v1_abort
// Second, we check that vtable is actually loaded once the type check is done.
- // CHECK-NULL: load {{.*}} (%struct.T*{{.*}})**, {{.*}} (%struct.T*{{.*}})***
+ // ITANIUM: [[R0:%.*]] = bitcast %struct.T* {{.*}} to i8**
+ // ITANIUM-NEXT: load i8*, i8** [[R0]]
+ // MSABI: [[R0:%.*]] = bitcast %struct.T* {{.*}} to i32 (%struct.T*)***
+ // MSABI-NEXT: load i32 (%struct.T*)**, i32 (%struct.T*)*** [[R0]]
return t->v();
}
@@ -37,7 +40,10 @@
// CHECK-VPTR: br i1 {{.*}} label %{{.*}}
// CHECK-VPTR: call void @__ubsan_handle_dynamic_type_cache_miss_abort
// Second, we check that vtable is actually loaded once the type check is done.
- // CHECK-VPTR: load {{.*}} (%struct.T*{{.*}})**, {{.*}} (%struct.T*{{.*}})***
+ // ITANIUM: [[vtable:%.*]] = bitcast %struct.T* {{.*}} to i8**
+ // ITANIUM-NEXT: load i8*, i8** [[vtable]]
+ // MSABI: [[R0:%.*]] = bitcast %struct.T* {{.*}} to i8* (%struct.T*, i32)***
+ // MSABI-NEXT: load i8* (%struct.T*, i32)**, i8* (%struct.T*, i32)*** [[R0]]
delete t;
}
Index: clang/test/CodeGenCXX/interleaving-virtual-calls.cpp
===================================================================
--- /dev/null
+++ clang/test/CodeGenCXX/interleaving-virtual-calls.cpp
@@ -0,0 +1,38 @@
+// This file tests if accesses to virtual function pointer entries are correctly generated when vtable interleaving is enabled.
+
+// RUN: %clang_cc1 -vtable-interleaving -flto -fvisibility hidden -fsanitize=cfi-vcall -emit-llvm %s -o - -triple x86_64-unknown-linux | FileCheck %s
+
+struct A {
+ int a;
+ virtual int foo1();
+ virtual int foo2();
+};
+struct B : A {
+ int b;
+ virtual int foo1();
+};
+struct C {
+ int c;
+ virtual int foo3();
+ virtual int foo4();
+};
+struct D : B, C {
+ virtual int foo2();
+ virtual int foo3();
+};
+
+// CHECK: @_Z5test1P1A
+int test1(A *a) {
+ // CHECK: [[R0:%.*]] = bitcast %struct.A* {{.*}} to i8**
+ // CHECK: [[VTABLE:%.*]] = load i8*, i8** [[R0]]
+ // CHECK: getelementptr inbounds i8, i8* [[VTABLE]], i32 ptrtoint ([0 x i32]* @"__A$0" to i32)
+ return a->foo1();
+}
+
+// CHECK: @_Z5test2P1C
+int test2(C *c) {
+ // CHECK: [[R0:%.*]] = bitcast %struct.C* {{.*}} to i8**
+ // CHECK: [[VTABLE:%.*]] = load i8*, i8** [[R0]]
+ // CHECK: getelementptr inbounds i8, i8* [[VTABLE]], i32 ptrtoint ([0 x i32]* @"__C$8" to i32)
+ return c->foo4();
+}
Index: clang/test/CodeGenCXX/interleaving-virtual-base.cpp
===================================================================
--- /dev/null
+++ clang/test/CodeGenCXX/interleaving-virtual-base.cpp
@@ -0,0 +1,72 @@
+// This file tests if accesses to vbase entries are correctly generated when vtable interleaving is enabled.
+// This test is based on virtual-base-cast.cpp.
+
+// RUN: %clang_cc1 -flto -fvisibility hidden -fsanitize=cfi-vcall -vtable-interleaving -emit-llvm %s -o - -triple i686-pc-linux-gnu | FileCheck %s
+
+struct A {
+ int a;
+ virtual int aa();
+};
+struct B {
+ int b;
+ virtual int bb();
+};
+struct C : virtual A, virtual B {
+ int c;
+ virtual int aa();
+ virtual int bb();
+};
+struct AA {
+ int a;
+ virtual int aa();
+};
+struct BB {
+ int b;
+ virtual int bb();
+};
+struct CC : AA, BB {
+ virtual int aa();
+ virtual int bb();
+ virtual int cc();
+};
+struct D : virtual C, virtual CC {
+ int e;
+};
+
+D *x;
+
+A *a() { return x; }
+// CHECK: @_Z1av() [[NUW:#[0-9]+]]
+// CHECK: [[VBASEOFFSETPTRA:%[a-zA-Z0-9\.]+]] = getelementptr inbounds i8, i8* {{.*}}, i64 ptrtoint ([0 x i32]* @"__D$-16" to i64)
+// CHECK: [[CASTVBASEOFFSETPTRA:%[a-zA-Z0-9\.]+]] = bitcast i8* [[VBASEOFFSETPTRA]] to i32*
+// CHECK: load i32, i32* [[CASTVBASEOFFSETPTRA]]
+// CHECK: }
+
+B *b() { return x; }
+// CHECK: @_Z1bv() [[NUW]]
+// CHECK: [[VBASEOFFSETPTRA:%[a-zA-Z0-9\.]+]] = getelementptr inbounds i8, i8* {{.*}}, i64 ptrtoint ([0 x i32]* @"__D$-20" to i64)
+// CHECK: [[CASTVBASEOFFSETPTRA:%[a-zA-Z0-9\.]+]] = bitcast i8* [[VBASEOFFSETPTRA]] to i32*
+// CHECK: load i32, i32* [[CASTVBASEOFFSETPTRA]]
+// CHECK: }
+
+BB *c() { return x; }
+// CHECK: @_Z1cv() [[NUW]]
+// CHECK: [[VBASEOFFSETPTRC:%[a-zA-Z0-9\.]+]] = getelementptr inbounds i8, i8* {{.*}}, i64 ptrtoint ([0 x i32]* @"__D$-24" to i64)
+// CHECK: [[CASTVBASEOFFSETPTRC:%[a-zA-Z0-9\.]+]] = bitcast i8* [[VBASEOFFSETPTRC]] to i32*
+// CHECK: [[VBASEOFFSETC:%[a-zA-Z0-9\.]+]] = load i32, i32* [[CASTVBASEOFFSETPTRC]]
+// CHECK: add i32 [[VBASEOFFSETC]], 8
+// CHECK: }
+
+// Put the vbptr at a non-zero offset inside a non-virtual base.
+struct E {
+ int e;
+};
+struct F : E, D {
+ int f;
+};
+
+F *y;
+
+BB *d() { return y; }
+
+// CHECK: attributes [[NUW]] = { noinline nounwind{{.*}} }
Index: clang/test/CodeGenCXX/interleaving-member-function-pointers.cpp
===================================================================
--- /dev/null
+++ clang/test/CodeGenCXX/interleaving-member-function-pointers.cpp
@@ -0,0 +1,180 @@
+// This file tests if accesses to vbase entries are correctly generated when vtable interleaving is enabled.
+// This test is based on member-function-pointers.cpp.
+
+// RUN: %clang_cc1 %s -flto -fvisibility hidden -fsanitize=cfi-vcall -vtable-interleaving -emit-llvm -o - -triple=x86_64-unknown-unknown | FileCheck -check-prefix CODE-LP64 %s
+// RUN: %clang_cc1 %s -flto -fvisibility hidden -fsanitize=cfi-vcall -vtable-interleaving -emit-llvm -o - -triple=i386-unknown-unknown | FileCheck -check-prefix CODE-LP32 %s
+// RUN: %clang_cc1 %s -flto -fvisibility hidden -fsanitize=cfi-vcall -vtable-interleaving -emit-llvm -o - -triple=x86_64-unknown-unknown | FileCheck -check-prefix GLOBAL-LP64 %s
+// RUN: %clang_cc1 %s -flto -fvisibility hidden -fsanitize=cfi-vcall -vtable-interleaving -emit-llvm -o - -triple=i386-unknown-unknown | FileCheck -check-prefix GLOBAL-LP32 %s
+// RUN: %clang_cc1 %s -flto -fvisibility hidden -fsanitize=cfi-vcall -vtable-interleaving -emit-llvm -o - -triple=armv7-unknown-unknown | FileCheck -check-prefix GLOBAL-ARM %s
+
+// PNaCl uses the same representation of method pointers as ARM.
+// RUN: %clang_cc1 %s -flto -fvisibility hidden -fsanitize=cfi-vcall -vtable-interleaving -emit-llvm -o - -triple=le32-unknown-nacl | FileCheck -check-prefix GLOBAL-ARM %s
+// MIPS uses the same representation of method pointers as ARM.
+// RUN: %clang_cc1 %s -flto -fvisibility hidden -fsanitize=cfi-vcall -vtable-interleaving -emit-llvm -o - -triple=mips-unknown-linux-gnu | FileCheck -check-prefix GLOBAL-ARM %s
+// WebAssembly uses the same representation of method pointers as ARM.
+// RUN: %clang_cc1 %s -flto -fvisibility hidden -fsanitize=cfi-vcall -vtable-interleaving -emit-llvm -o - -triple=wasm32-unknown-unknown | FileCheck -check-prefix GLOBAL-ARM %s
+
+namespace test1 {
+struct A {
+ int a;
+ void f();
+ virtual void vf1();
+ virtual void vf2();
+};
+struct B {
+ int b;
+ virtual void g();
+};
+struct C : B, A {};
+
+void (A::*pa)();
+void (A::*volatile vpa)();
+void (B::*pb)();
+void (C::*pc)();
+
+// GLOBAL-LP64: @_ZN5test13pa2E = hidden global { i64, i64 } { i64 ptrtoint (void (%"struct.test1::A"*)* @_ZN5test11A1fEv to i64), i64 0 }, align 8
+void (A::*pa2)() = &A::f;
+
+// GLOBAL-LP64: @_ZN5test13pa3E = hidden global { i64, i64 } { i64 add (i64 ptrtoint ([0 x i32]* @"__A$0" to i64), i64 1), i64 0 }, align 8
+// GLOBAL-LP32: @_ZN5test13pa3E = hidden global { i32, i32 } { i32 add (i32 ptrtoint ([0 x i32]* @"__A$0" to i32), i32 1), i32 0 }, align 4
+void (A::*pa3)() = &A::vf1;
+
+// GLOBAL-LP64: @_ZN5test13pa4E = hidden global { i64, i64 } { i64 add (i64 ptrtoint ([0 x i32]* @"__A$8" to i64), i64 1), i64 0 }, align 8
+// GLOBAL-LP32: @_ZN5test13pa4E = hidden global { i32, i32 } { i32 add (i32 ptrtoint ([0 x i32]* @"__A$4" to i32), i32 1), i32 0 }, align 4
+void (A::*pa4)() = &A::vf2;
+
+// GLOBAL-LP64: @_ZN5test13pc2E = hidden global { i64, i64 } { i64 ptrtoint (void (%"struct.test1::A"*)* @_ZN5test11A1fEv to i64), i64 16 }, align 8
+void (C::*pc2)() = &C::f;
+
+// GLOBAL-LP64: @_ZN5test13pc3E = hidden global { i64, i64 } { i64 add (i64 ptrtoint ([0 x i32]* @"__A$0" to i64), i64 1), i64 0 }, align 8
+void (A::*pc3)() = &A::vf1;
+
+void f() {
+ // CODE-LP64: store { i64, i64 } zeroinitializer, { i64, i64 }* @_ZN5test12paE
+ pa = 0;
+
+ // Is this okay? What are LLVM's volatile semantics for structs?
+ // CODE-LP64: store volatile { i64, i64 } zeroinitializer, { i64, i64 }* @_ZN5test13vpaE
+ vpa = 0;
+
+ // CODE-LP64: [[TMP:%.*]] = load { i64, i64 }, { i64, i64 }* @_ZN5test12paE, align 8
+ // CODE-LP64: [[TMPADJ:%.*]] = extractvalue { i64, i64 } [[TMP]], 1
+ // CODE-LP64: [[ADJ:%.*]] = add nsw i64 [[TMPADJ]], 16
+ // CODE-LP64: [[RES:%.*]] = insertvalue { i64, i64 } [[TMP]], i64 [[ADJ]], 1
+ // CODE-LP64: store { i64, i64 } [[RES]], { i64, i64 }* @_ZN5test12pcE, align 8
+ pc = pa;
+
+ // CODE-LP64: [[TMP:%.*]] = load { i64, i64 }, { i64, i64 }* @_ZN5test12pcE, align 8
+ // CODE-LP64: [[TMPADJ:%.*]] = extractvalue { i64, i64 } [[TMP]], 1
+ // CODE-LP64: [[ADJ:%.*]] = sub nsw i64 [[TMPADJ]], 16
+ // CODE-LP64: [[RES:%.*]] = insertvalue { i64, i64 } [[TMP]], i64 [[ADJ]], 1
+ // CODE-LP64: store { i64, i64 } [[RES]], { i64, i64 }* @_ZN5test12paE, align 8
+ pa = static_cast<void (A::*)()>(pc);
+}
+
+void f2() {
+ // CODE-LP64: store { i64, i64 } { i64 ptrtoint (void (%"struct.test1::A"*)* @_ZN5test11A1fEv to i64), i64 0 }
+ void (A::*pa2)() = &A::f;
+
+ // CODE-LP64: store { i64, i64 } { i64 add (i64 ptrtoint ([0 x i32]* @"__A$0" to i64), i64 1), i64 0 }
+ // CODE-LP32: store { i32, i32 } { i32 add (i32 ptrtoint ([0 x i32]* @"__A$0" to i32), i32 1), i32 0 }
+ void (A::*pa3)() = &A::vf1;
+
+ // CODE-LP64: store { i64, i64 } { i64 add (i64 ptrtoint ([0 x i32]* @"__A$8" to i64), i64 1), i64 0 }
+ // CODE-LP32: store { i32, i32 } { i32 add (i32 ptrtoint ([0 x i32]* @"__A$4" to i32), i32 1), i32 0 }
+ void (A::*pa4)() = &A::vf2;
+}
+
+void f3(A *a, A &ar) {
+ (a->*pa)();
+ (ar.*pa)();
+}
+
+bool f4() {
+ return pa;
+}
+} // namespace test1
+
+namespace test2 {
+struct A {
+ void foo();
+ virtual void vfoo();
+};
+struct B {
+ void foo();
+ virtual void vfoo();
+};
+struct C : A, B {
+ void foo();
+ virtual void vfoo();
+};
+
+// GLOBAL-ARM: @_ZN5test24ptr0E = hidden global {{.*}} { i32 ptrtoint ({{.*}}* @_ZN5test21A3fooEv to i32), i32 0 }
+// GLOBAL-ARM: @_ZN5test24ptr1E = hidden global {{.*}} { i32 ptrtoint ({{.*}}* @_ZN5test21B3fooEv to i32), i32 8 }
+// GLOBAL-ARM: @_ZN5test24ptr2E = hidden global {{.*}} { i32 ptrtoint ({{.*}}* @_ZN5test21C3fooEv to i32), i32 0 }
+// GLOBAL-ARM: @_ZN5test24ptr3E = hidden global {{.*}} { i32 ptrtoint ([0 x i32]* @"__A$0.1" to i32), i32 1 }
+// GLOBAL-ARM: @_ZN5test24ptr4E = hidden global {{.*}} { i32 ptrtoint ([0 x i32]* @"__B$0" to i32), i32 9 }
+// GLOBAL-ARM: @_ZN5test24ptr5E = hidden global {{.*}} { i32 ptrtoint ([0 x i32]* @"__C$0" to i32), i32 1 }
+void (C::*ptr0)() = &A::foo;
+void (C::*ptr1)() = &B::foo;
+void (C::*ptr2)() = &C::foo;
+void (C::*ptr3)() = &A::vfoo;
+void (C::*ptr4)() = &B::vfoo;
+void (C::*ptr5)() = &C::vfoo;
+} // namespace test2
+
+// rdar://problem/10815683 - Verify that we can emit reinterprets of
+// member pointers as constant initializers. For added trickiness,
+// we also add some non-trivial adjustments.
+namespace test3 {
+struct A {
+ int nonEmpty;
+ void foo();
+};
+struct B : public A {
+ virtual void requireNonZeroAdjustment();
+};
+struct C {
+ int nonEmpty;
+};
+struct D : public C {
+ virtual void requireNonZeroAdjustment();
+};
+
+// It's not that the offsets are doubled on ARM, it's that they're left-shifted by 1.
+
+// GLOBAL-LP64: @_ZN5test31aE = hidden global { i64, i64 } { i64 ptrtoint (void (%"struct.test3::A"*)* @_ZN5test31A3fooEv to i64), i64 0 }, align 8
+// GLOBAL-LP32: @_ZN5test31aE = hidden global { i32, i32 } { i32 ptrtoint (void (%"struct.test3::A"*)* @_ZN5test31A3fooEv to i32), i32 0 }, align 4
+// GLOBAL-ARM: @_ZN5test31aE = hidden global { i32, i32 } { i32 ptrtoint (void (%"struct.test3::A"*)* @_ZN5test31A3fooEv to i32), i32 0 }, align 4
+void (A::*a)() = &A::foo;
+
+// GLOBAL-LP64: @_ZN5test31bE = hidden global { i64, i64 } { i64 ptrtoint (void (%"struct.test3::A"*)* @_ZN5test31A3fooEv to i64), i64 8 }, align 8
+// GLOBAL-LP32: @_ZN5test31bE = hidden global { i32, i32 } { i32 ptrtoint (void (%"struct.test3::A"*)* @_ZN5test31A3fooEv to i32), i32 4 }, align 4
+// GLOBAL-ARM: @_ZN5test31bE = hidden global { i32, i32 } { i32 ptrtoint (void (%"struct.test3::A"*)* @_ZN5test31A3fooEv to i32), i32 8 }, align 4
+void (B::*b)() = (void (B::*)()) & A::foo;
+
+// GLOBAL-LP64: @_ZN5test31cE = hidden global { i64, i64 } { i64 ptrtoint (void (%"struct.test3::A"*)* @_ZN5test31A3fooEv to i64), i64 8 }, align 8
+// GLOBAL-LP32: @_ZN5test31cE = hidden global { i32, i32 } { i32 ptrtoint (void (%"struct.test3::A"*)* @_ZN5test31A3fooEv to i32), i32 4 }, align 4
+// GLOBAL-ARM: @_ZN5test31cE = hidden global { i32, i32 } { i32 ptrtoint (void (%"struct.test3::A"*)* @_ZN5test31A3fooEv to i32), i32 8 }, align 4
+void (C::*c)() = (void (C::*)())(void (B::*)()) & A::foo;
+
+// GLOBAL-LP64: @_ZN5test31dE = hidden global { i64, i64 } { i64 ptrtoint (void (%"struct.test3::A"*)* @_ZN5test31A3fooEv to i64), i64 16 }, align 8
+// GLOBAL-LP32: @_ZN5test31dE = hidden global { i32, i32 } { i32 ptrtoint (void (%"struct.test3::A"*)* @_ZN5test31A3fooEv to i32), i32 8 }, align 4
+// GLOBAL-ARM: @_ZN5test31dE = hidden global { i32, i32 } { i32 ptrtoint (void (%"struct.test3::A"*)* @_ZN5test31A3fooEv to i32), i32 16 }, align 4
+void (D::*d)() = (void (C::*)())(void (B::*)()) & A::foo;
+} // namespace test3
+
+namespace test4 {
+struct A {
+ virtual void a();
+};
+struct B : A {};
+struct C : B {
+ virtual void a();
+};
+void (C::*x)() = &C::a;
+
+// GLOBAL-LP64: @_ZN5test41xE = hidden global { i64, i64 } { i64 add (i64 ptrtoint ([0 x i32]* @"__C$0.2" to i64), i64 1), i64 0 }
+// GLOBAL-LP32: @_ZN5test41xE = hidden global { i32, i32 } { i32 add (i32 ptrtoint ([0 x i32]* @"__C$0.2" to i32), i32 1), i32 0 }
+// GLOBAL-ARM: @_ZN5test41xE = hidden global { i32, i32 } { i32 ptrtoint ([0 x i32]* @"__C$0.2" to i32), i32 1 }
+} // namespace test4
Index: clang/test/CodeGenCXX/devirtualize-virtual-function-calls-final.cpp
===================================================================
--- clang/test/CodeGenCXX/devirtualize-virtual-function-calls-final.cpp
+++ clang/test/CodeGenCXX/devirtualize-virtual-function-calls-final.cpp
@@ -89,6 +89,7 @@
// FIXME: It should be possible to devirtualize this case, but that is
// not implemented yet.
// CHECK: getelementptr
+ // CHECK-NEXT: bitcast
// CHECK-NEXT: %[[FUNC:.*]] = load
// CHECK-NEXT: call void %[[FUNC]]
static_cast<A*>(d)->f();
@@ -98,6 +99,7 @@
// FIXME: It should be possible to devirtualize this case, but that is
// not implemented yet.
// CHECK: getelementptr
+ // CHECK-NEXT: bitcast
// CHECK-NEXT: %[[FUNC:.*]] = load
// CHECK-NEXT: call i32 %[[FUNC]]
-static_cast<A&>(*d);
@@ -207,7 +209,8 @@
// CHECK: [[F_PTR_RA:%.+]] = bitcast
// CHECK: [[VTABLE:%.+]] = load {{.+}} [[F_PTR_RA]]
// CHECK: [[VFN:%.+]] = getelementptr inbounds {{.+}} [[VTABLE]], i{{[0-9]+}} 0
- // CHECK-NEXT: %[[FUNC:.*]] = load {{.+}} [[VFN]]
+ // CHECK-NEXT: [[VFP:%.*]] = bitcast {{.+}} [[VFN]]
+ // CHECK-NEXT: %[[FUNC:.*]] = load {{.+}} [[VFP]]
// CHECK-NEXT: = call {{.*}} %[[FUNC]]
return static_cast<RA*>(x)->f();
}
@@ -219,8 +222,9 @@
// CHECK: bitcast
// CHECK: [[F_PTR_RA:%.+]] = bitcast
// CHECK: [[VTABLE:%.+]] = load {{.+}} [[F_PTR_RA]]
- // CHECK: [[VFN:%.+]] = getelementptr inbounds {{.+}} [[VTABLE]], i{{[0-9]+}} 1
- // CHECK-NEXT: %[[FUNC:.*]] = load {{.+}} [[VFN]]
+ // CHECK: [[VFN:%.+]] = getelementptr inbounds {{.+}} [[VTABLE]], i{{[0-9]+}} 4
+ // CHECK-NEXT: [[VFP:%.*]] = bitcast {{.+}} [[VFN]]
+ // CHECK-NEXT: %[[FUNC:.*]] = load {{.+}} [[VFP]]
// CHECK-NEXT: = call {{.*}} %[[FUNC]]
return -static_cast<RA&>(*x);
}
Index: clang/test/CodeGenCXX/delete.cpp
===================================================================
--- clang/test/CodeGenCXX/delete.cpp
+++ clang/test/CodeGenCXX/delete.cpp
@@ -123,10 +123,11 @@
// CHECK-NEXT: [[ALLOCATED:%.*]] = getelementptr inbounds i8, i8* [[T0]], i64 [[OFFSET]]
// Load the complete-object destructor (not the deleting destructor)
// and call it.
- // CHECK-NEXT: [[T0:%.*]] = bitcast [[X:%.*]]* [[XP:%.*]] to void ([[X]]*)***
- // CHECK-NEXT: [[VTABLE:%.*]] = load void ([[X]]*)**, void ([[X]]*)*** [[T0]]
- // CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds void ([[X]]*)*, void ([[X]]*)** [[VTABLE]], i64 0
- // CHECK-NEXT: [[DTOR:%.*]] = load void ([[X]]*)*, void ([[X]]*)** [[T0]]
+ // CHECK-NEXT: [[T0:%.*]] = bitcast [[X:%.*]]* [[XP:%.*]] to i8**
+ // CHECK-NEXT: [[VTABLE:%.*]] = load i8*, i8** [[T0]]
+ // CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds i8, i8* [[VTABLE]], i32 0
+ // CHECK-NEXT: [[T1:%.*]] = bitcast i8* [[T0]] to void ([[X]]*)**
+ // CHECK-NEXT: [[DTOR:%.*]] = load void ([[X]]*)*, void ([[X]]*)** [[T1]]
// CHECK-NEXT: call void [[DTOR]]([[X]]* [[OBJ:%.*]])
// Call the global operator delete.
// CHECK-NEXT: call void @_ZdlPv(i8* [[ALLOCATED]]) [[NUW:#[0-9]+]]
Index: clang/test/CodeGenCXX/constructor-init.cpp
===================================================================
--- clang/test/CodeGenCXX/constructor-init.cpp
+++ clang/test/CodeGenCXX/constructor-init.cpp
@@ -96,8 +96,9 @@
// CHECK-LABEL: define void @_ZN10InitVTable1BC2Ev(%"struct.InitVTable::B"* %this) unnamed_addr
// CHECK: [[T0:%.*]] = bitcast [[B:%.*]]* [[THIS:%.*]] to i32 (...)***
// CHECK-NEXT: store i32 (...)** bitcast (i8** getelementptr inbounds ({ [3 x i8*] }, { [3 x i8*] }* @_ZTVN10InitVTable1BE, i32 0, inrange i32 0, i32 2) to i32 (...)**), i32 (...)*** [[T0]]
- // CHECK: [[VTBL:%.*]] = load i32 ([[B]]*)**, i32 ([[B]]*)*** {{%.*}}
- // CHECK-NEXT: [[FNP:%.*]] = getelementptr inbounds i32 ([[B]]*)*, i32 ([[B]]*)** [[VTBL]], i64 0
+ // CHECK: [[VTBL:%.*]] = load i8*, i8** {{%.*}}
+ // CHECK-NEXT: [[FNBP:%.*]] = getelementptr inbounds i8, i8* [[VTBL]], i32 0
+ // CHECK-NEXT: [[FNP:%.*]] = bitcast i8* [[FNBP]] to i32 ([[B]]*)**
// CHECK-NEXT: [[FN:%.*]] = load i32 ([[B]]*)*, i32 ([[B]]*)** [[FNP]]
// CHECK-NEXT: [[ARG:%.*]] = call i32 [[FN]]([[B]]* [[THIS]])
// CHECK-NEXT: call void @_ZN10InitVTable1AC2Ei({{.*}}* {{%.*}}, i32 [[ARG]])
Index: clang/test/CodeGenCXX/constructor-destructor-return-this.cpp
===================================================================
--- clang/test/CodeGenCXX/constructor-destructor-return-this.cpp
+++ clang/test/CodeGenCXX/constructor-destructor-return-this.cpp
@@ -131,7 +131,8 @@
// Verify that virtual calls to destructors are not marked with a 'returned'
// this parameter at the call site...
-// CHECKARM: [[VFN:%.*]] = getelementptr inbounds %class.E* (%class.E*)*, %class.E* (%class.E*)**
+// CHECKARM: [[VFPTR:%.*]] = getelementptr inbounds i8, i8*
+// CHECKARM: [[VFN:%.*]] = bitcast i8* [[VFPTR]] to %class.E* (%class.E*)**
// CHECKARM: [[THUNK:%.*]] = load %class.E* (%class.E*)*, %class.E* (%class.E*)** [[VFN]]
// CHECKARM: call %class.E* [[THUNK]](%class.E* %
Index: clang/test/CodeGenCXX/cfi-cross-dso.cpp
===================================================================
--- clang/test/CodeGenCXX/cfi-cross-dso.cpp
+++ clang/test/CodeGenCXX/cfi-cross-dso.cpp
@@ -28,8 +28,12 @@
// MS: @[[B_VTABLE:.*]] = private unnamed_addr constant { [2 x i8*] } {{.*}}@"??_R4B@?A0x{{[^@]*}}@@6B@"{{.*}}@"?f@B@?A0x{{[^@]*}}@@UEAAXXZ"
-// CHECK: %[[VT:.*]] = load void (%struct.A*)**, void (%struct.A*)***
-// CHECK: %[[VT2:.*]] = bitcast {{.*}}%[[VT]] to i8*, !nosanitize
+// CHECK: %[[R0:.*]] = load %struct.A*, %struct.A** %a.addr
+// ITANIUM: %[[R1:.*]] = bitcast %struct.A* %[[R0]] to i8**
+// ITANIUM: %[[VT2:.*]] = load i8*, i8** %[[R1]]
+// MS: %[[R1:.*]] = bitcast %struct.A* %[[R0]] to void (%struct.A*)***
+// MS: %[[VT:.*]] = load void (%struct.A*)**, void (%struct.A*)*** %[[R1]]
+// MS: %[[VT2:.*]] = bitcast {{.*}}%[[VT]] to i8*
// ITANIUM: %[[TEST:.*]] = call i1 @llvm.type.test(i8* %[[VT2]], metadata !"_ZTS1A"), !nosanitize
// MS: %[[TEST:.*]] = call i1 @llvm.type.test(i8* %[[VT2]], metadata !"?AUA@@"), !nosanitize
// CHECK: br i1 %[[TEST]], label %[[CONT:.*]], label %[[SLOW:.*]], {{.*}} !nosanitize
Index: clang/test/CodeGenCXX/arm.cpp
===================================================================
--- clang/test/CodeGenCXX/arm.cpp
+++ clang/test/CodeGenCXX/arm.cpp
@@ -278,11 +278,12 @@
// CHECK-NEXT: [[V:%.*]] = load [[A]]*, [[A]]** [[AVAR]], align 4
// CHECK-NEXT: [[ISNULL:%.*]] = icmp eq [[A]]* [[V]], null
// CHECK-NEXT: br i1 [[ISNULL]]
- // CHECK: [[T0:%.*]] = bitcast [[A]]* [[V]] to void ([[A]]*)***
- // CHECK-NEXT: [[T1:%.*]] = load void ([[A]]*)**, void ([[A]]*)*** [[T0]]
- // CHECK-NEXT: [[T2:%.*]] = getelementptr inbounds void ([[A]]*)*, void ([[A]]*)** [[T1]], i64 1
- // CHECK-NEXT: [[T3:%.*]] = load void ([[A]]*)*, void ([[A]]*)** [[T2]]
- // CHECK-NEXT: call void [[T3]]([[A]]* [[V]])
+ // CHECK: [[T0:%.*]] = bitcast [[A]]* [[V]] to i8**
+ // CHECK-NEXT: [[T1:%.*]] = load i8*, i8** [[T0]]
+ // CHECK-NEXT: [[T2:%.*]] = getelementptr inbounds i8, i8* [[T1]], i32 4
+ // CHECK: [[T3:%.*]] = bitcast i8* [[T2]] to void ([[A]]*)**
+ // CHECK-NEXT: [[T4:%.*]] = load void ([[A]]*)*, void ([[A]]*)** [[T3]]
+ // CHECK-NEXT: call void [[T4]]([[A]]* [[V]])
// CHECK-NEXT: br label
// CHECK: ret void
delete a;
Index: clang/test/CodeGenCXX/alignment.cpp
===================================================================
--- clang/test/CodeGenCXX/alignment.cpp
+++ clang/test/CodeGenCXX/alignment.cpp
@@ -226,7 +226,7 @@
// CHECK: [[B_P:%.*]] = load [[B:%.*]]*, [[B]]**
// CHECK: [[VPTR_P:%.*]] = bitcast [[B]]* [[B_P]] to i8**
// CHECK: [[VPTR:%.*]] = load i8*, i8** [[VPTR_P]], align 8
- // CHECK: [[T0:%.*]] = getelementptr i8, i8* [[VPTR]], i64 -24
+ // CHECK: [[T0:%.*]] = getelementptr inbounds i8, i8* [[VPTR]], i64 -24
// CHECK: [[OFFSET_P:%.*]] = bitcast i8* [[T0]] to i64*
// CHECK: [[OFFSET:%.*]] = load i64, i64* [[OFFSET_P]], align 8
// CHECK: [[T0:%.*]] = bitcast [[B]]* [[B_P]] to i8*
@@ -280,7 +280,7 @@
// CHECK: [[RESULT:%.*]] = alloca [[ARRAY]], align 64
// CHECK: [[VPTR_P:%.*]] = bitcast [[D]]* [[D_P]] to i8**
// CHECK: [[VPTR:%.*]] = load i8*, i8** [[VPTR_P]], align 16
- // CHECK: [[T0:%.*]] = getelementptr i8, i8* [[VPTR]], i64 -24
+ // CHECK: [[T0:%.*]] = getelementptr inbounds i8, i8* [[VPTR]], i64 -24
// CHECK: [[OFFSET_P:%.*]] = bitcast i8* [[T0]] to i64*
// CHECK: [[OFFSET:%.*]] = load i64, i64* [[OFFSET_P]], align 8
// CHECK: [[T0:%.*]] = bitcast [[D]]* [[D_P]] to i8*
Index: clang/test/CodeGen/tbaa-for-vptr.cpp
===================================================================
--- clang/test/CodeGen/tbaa-for-vptr.cpp
+++ clang/test/CodeGen/tbaa-for-vptr.cpp
@@ -1,10 +1,10 @@
-// RUN: %clang_cc1 -triple %itanium_abi_triple -emit-llvm -o - -fsanitize=thread %s | FileCheck %s --check-prefixes=CHECK,OLD-PATH
-// RUN: %clang_cc1 -triple %itanium_abi_triple -emit-llvm -o - -O1 %s | FileCheck %s --check-prefixes=CHECK,OLD-PATH
-// RUN: %clang_cc1 -triple %itanium_abi_triple -emit-llvm -o - -O1 -relaxed-aliasing -fsanitize=thread %s | FileCheck %s --check-prefixes=CHECK,OLD-PATH
+// RUN: %clang_cc1 -triple %itanium_abi_triple -emit-llvm -o - -fsanitize=thread %s | FileCheck %s --check-prefixes=CHECK,OLD-PATH,UNOPTIMIZED
+// RUN: %clang_cc1 -triple %itanium_abi_triple -emit-llvm -o - -O1 %s | FileCheck %s --check-prefixes=CHECK,OLD-PATH,OPTIMIZED
+// RUN: %clang_cc1 -triple %itanium_abi_triple -emit-llvm -o - -O1 -relaxed-aliasing -fsanitize=thread %s | FileCheck %s --check-prefixes=CHECK,OLD-PATH,OPTIMIZED
//
-// RUN: %clang_cc1 -triple %itanium_abi_triple -emit-llvm -new-struct-path-tbaa -o - -fsanitize=thread %s | FileCheck %s --check-prefixes=CHECK,NEW-PATH
-// RUN: %clang_cc1 -triple %itanium_abi_triple -emit-llvm -new-struct-path-tbaa -o - -O1 %s | FileCheck %s --check-prefixes=CHECK,NEW-PATH
-// RUN: %clang_cc1 -triple %itanium_abi_triple -emit-llvm -new-struct-path-tbaa -o - -O1 -relaxed-aliasing -fsanitize=thread %s | FileCheck %s --check-prefixes=CHECK,NEW-PATH
+// RUN: %clang_cc1 -triple %itanium_abi_triple -emit-llvm -new-struct-path-tbaa -o - -fsanitize=thread %s | FileCheck %s --check-prefixes=CHECK,NEW-PATH,UNOPTIMIZED
+// RUN: %clang_cc1 -triple %itanium_abi_triple -emit-llvm -new-struct-path-tbaa -o - -O1 %s | FileCheck %s --check-prefixes=CHECK,NEW-PATH,OPTIMIZED
+// RUN: %clang_cc1 -triple %itanium_abi_triple -emit-llvm -new-struct-path-tbaa -o - -O1 -relaxed-aliasing -fsanitize=thread %s | FileCheck %s --check-prefixes=CHECK,NEW-PATH,OPTIMIZED
//
// RUN: %clang_cc1 -triple %itanium_abi_triple -emit-llvm -o - %s | FileCheck %s --check-prefix=NOTBAA
// RUN: %clang_cc1 -triple %itanium_abi_triple -emit-llvm -o - -O2 -relaxed-aliasing %s | FileCheck %s --check-prefix=NOTBAA
@@ -27,7 +27,9 @@
}
// CHECK-LABEL: @_Z7CallFoo
-// CHECK: %{{.*}} = load i32 (%struct.A*)**, {{.*}} !tbaa ![[NUM:[0-9]+]]
+// UNOPTIMIZED: [[R0:%[^ ]*]] = bitcast %struct.A* {{.*}} to i8**
+// UNOPTIMIZED: %{{.*}} = load i8*, i8** [[R0]], {{.*}} !tbaa ![[NUM:[0-9]+]]
+// OPTIMIZED: %{{.*}} = load i32 (%struct.A*)**, {{.*}} !tbaa ![[NUM:[0-9]+]]
// CHECK: br i1
// CHECK: load i8*, {{.*}}, !tbaa ![[NUM]]
//
Index: clang/lib/Frontend/CompilerInvocation.cpp
===================================================================
--- clang/lib/Frontend/CompilerInvocation.cpp
+++ clang/lib/Frontend/CompilerInvocation.cpp
@@ -1333,6 +1333,8 @@
Opts.PassPlugins = Args.getAllArgValues(OPT_fpass_plugin_EQ);
+ Opts.VTableInterleaving = Args.hasArg(OPT_vtable_interleaving);
+
return Success;
}
Index: clang/lib/CodeGen/MicrosoftCXXABI.cpp
===================================================================
--- clang/lib/CodeGen/MicrosoftCXXABI.cpp
+++ clang/lib/CodeGen/MicrosoftCXXABI.cpp
@@ -1835,13 +1835,14 @@
llvm::Type *Ty,
SourceLocation Loc) {
CGBuilderTy &Builder = CGF.Builder;
-
- Ty = Ty->getPointerTo()->getPointerTo();
+ llvm::Type *VTablePtrTy = Ty->getPointerTo()->getPointerTo();
+ llvm::Type *FuncPtrTy = Ty->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, VTablePtrTy, MethodDecl->getParent());
MicrosoftVTableContext &VFTContext = CGM.getMicrosoftVTableContext();
MethodVFTableLocation ML = VFTContext.getMethodVFTableLocation(GD);
@@ -1860,9 +1861,13 @@
llvm::Value *VFunc;
if (CGF.ShouldEmitVTableTypeCheckedLoad(MethodDecl->getParent())) {
- VFunc = CGF.EmitVTableTypeCheckedLoad(
- getObjectWithVPtr(), VTable,
- ML.Index * CGM.getContext().getTargetInfo().getPointerWidth(0) / 8);
+ int64_t ByteOffset =
+ ML.Index * CGM.getContext().getTargetInfo().getPointerWidth(0) / 8;
+ llvm::Value *OffsetConstant =
+ llvm::ConstantInt::get(CGM.Int32Ty, ByteOffset);
+ llvm::Value *CastedVTable = Builder.CreateBitCast(VTable, CGM.Int8PtrTy);
+ VFunc = CGF.EmitVTableTypeCheckedLoad(getObjectWithVPtr(), CastedVTable,
+ FuncPtrTy, OffsetConstant);
} else {
if (CGM.getCodeGenOpts().PrepareForLTO)
CGF.EmitTypeMetadataCodeForVCall(getObjectWithVPtr(), VTable, Loc);
Index: clang/lib/CodeGen/ItaniumCXXABI.cpp
===================================================================
--- clang/lib/CodeGen/ItaniumCXXABI.cpp
+++ clang/lib/CodeGen/ItaniumCXXABI.cpp
@@ -24,14 +24,15 @@
#include "CodeGenFunction.h"
#include "CodeGenModule.h"
#include "TargetInfo.h"
-#include "clang/CodeGen/ConstantInitBuilder.h"
#include "clang/AST/Mangle.h"
-#include "clang/AST/Type.h"
#include "clang/AST/StmtCXX.h"
+#include "clang/AST/Type.h"
+#include "clang/CodeGen/ConstantInitBuilder.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/GlobalValue.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Intrinsics.h"
+#include "llvm/IR/Metadata.h"
#include "llvm/IR/Value.h"
#include "llvm/Support/ScopedPrinter.h"
@@ -42,6 +43,10 @@
class ItaniumCXXABI : public CodeGen::CGCXXABI {
/// VTables - All the vtables which have been defined.
llvm::DenseMap<const CXXRecordDecl *, llvm::GlobalVariable *> VTables;
+ /// Map each entry group to the corresponding offset global variable.
+ /// This map is needed for vtable interleaving.
+ llvm::DenseMap<std::pair<llvm::Metadata *, int64_t>, llvm::GlobalVariable *>
+ OffsetMap;
protected:
bool UseARMMethodPtrABI;
@@ -53,12 +58,10 @@
}
public:
- ItaniumCXXABI(CodeGen::CodeGenModule &CGM,
- bool UseARMMethodPtrABI = false,
- bool UseARMGuardVarABI = false) :
- CGCXXABI(CGM), UseARMMethodPtrABI(UseARMMethodPtrABI),
- UseARMGuardVarABI(UseARMGuardVarABI),
- Use32BitVTableOffsetABI(false) { }
+ ItaniumCXXABI(CodeGen::CodeGenModule &CGM, bool UseARMMethodPtrABI = false,
+ bool UseARMGuardVarABI = false)
+ : CGCXXABI(CGM), UseARMMethodPtrABI(UseARMMethodPtrABI),
+ UseARMGuardVarABI(UseARMGuardVarABI), Use32BitVTableOffsetABI(false) {}
bool classifyReturnType(CGFunctionInfo &FI) const override;
@@ -298,6 +301,16 @@
bool exportThunk() override { return true; }
+ // VirtuallyAdjustedType is used for vtable interleaving. It is the type
+ // of the vtable in which the vcall offset entry (if it is a this adjustment)
+ // or the virtual base entry (if it is a return adjustment) is defined. For a
+ // return adjustment, VirtuallyAdjustedType is the derived type; For a this
+ // adjustment, VirtuallyAdjustedType is the virtual base type.
+ llvm::Value *performTypeAdjustment(CodeGenFunction &CGF, Address InitialPtr,
+ int64_t NonVirtualAdjustment,
+ const CXXRecordDecl *VirtuallyAdjustedType,
+ int64_t VirtualAdjustment,
+ bool IsReturnAdjustment);
llvm::Value *performThisAdjustment(CodeGenFunction &CGF, Address This,
const ThisAdjustment &TA) override;
@@ -345,6 +358,16 @@
bool NeedsVTTParameter(GlobalDecl GD) override;
+ bool shouldInterleaveVTables(const CXXRecordDecl *RD);
+ llvm::Constant *getOffsetConstant(const CXXRecordDecl *RD, int64_t Offset,
+ llvm::Type *OffsetType,
+ bool AccessVCallOffset = false);
+ llvm::Value *getVTableEntryPointer(CodeGenFunction &CGF,
+ const CXXRecordDecl *RD,
+ bool AccessVCallOffset,
+ llvm::Value *VTable, int64_t Offset,
+ const llvm::Twine &Name = "");
+
/**************************** RTTI Uniqueness ******************************/
protected:
@@ -431,9 +454,9 @@
class ARMCXXABI : public ItaniumCXXABI {
public:
- ARMCXXABI(CodeGen::CodeGenModule &CGM) :
- ItaniumCXXABI(CGM, /* UseARMMethodPtrABI = */ true,
- /* UseARMGuardVarABI = */ true) {}
+ ARMCXXABI(CodeGen::CodeGenModule &CGM)
+ : ItaniumCXXABI(CGM, /* UseARMMethodPtrABI = */ true,
+ /* UseARMGuardVarABI = */ true) {}
bool HasThisReturn(GlobalDecl GD) const override {
return (isa<CXXConstructorDecl>(GD.getDecl()) || (
@@ -501,7 +524,8 @@
/* UseARMGuardVarABI = */ true);
case TargetCXXABI::GenericMIPS:
- return new ItaniumCXXABI(CGM, /* UseARMMethodPtrABI = */ true);
+ return new ItaniumCXXABI(CGM, /* UseARMMethodPtrABI = */ true,
+ /* UseARMGuardVarABI = */ false);
case TargetCXXABI::WebAssembly:
return new WebAssemblyCXXABI(CGM);
@@ -515,7 +539,8 @@
return new ItaniumCXXABI(CGM, /* UseARMMethodPtrABI = */ true,
/* UseARMGuardVarABI = */ false);
}
- return new ItaniumCXXABI(CGM);
+ return new ItaniumCXXABI(CGM, /* UseARMMethodPtrABI = */ false,
+ /* UseARMGuardVarABI = */ false);
case TargetCXXABI::Microsoft:
llvm_unreachable("Microsoft ABI is not Itanium-based");
@@ -630,7 +655,8 @@
llvm::Constant *CheckSourceLocation;
llvm::Constant *CheckTypeDesc;
bool ShouldEmitCFICheck = CGF.SanOpts.has(SanitizerKind::CFIMFCall) &&
- CGM.HasHiddenLTOVisibility(RD);
+ CGM.HasHiddenLTOVisibility(RD) &&
+ !CGM.getCodeGenOpts().VTableInterleaving;
if (ShouldEmitCFICheck) {
CodeGenFunction::SanitizerScope SanScope(&CGF);
@@ -900,6 +926,82 @@
return BuildMemberPointer(MD, CharUnits::Zero());
}
+/// We should only interleave vtables when the module has the hidden
+/// LTO visibility, cfi-vcall is enabled and VTableInterleaving
+/// is set.
+bool ItaniumCXXABI::shouldInterleaveVTables(const CXXRecordDecl *RD) {
+ return CGM.HasHiddenLTOVisibility(RD) &&
+ CGM.getLangOpts().Sanitize.has(SanitizerKind::CFIVCall) &&
+ CGM.getCodeGenOpts().VTableInterleaving;
+}
+
+/// When interleaving is disabled, this function simply returns
+/// a constant of the offset. Otherwise, it create or returns the
+/// offset placeholder of the specified type for the entry group identified
+/// by the pair (TypeId, Offset). An entry group is a set of vtable entries
+/// that must have the same offset from their corresponding address points.
+/// To interleave vtables, we replace offsets into vtables with offset
+/// placeholders, which will be replaced with real offsets once the interleaved
+/// layout is decided.
+llvm::Constant *ItaniumCXXABI::getOffsetConstant(const CXXRecordDecl *RD,
+ int64_t Offset,
+ llvm::Type *OffsetType,
+ bool IsVCallOffset) {
+ if (!shouldInterleaveVTables(RD))
+ return llvm::ConstantInt::get(OffsetType, Offset, true);
+
+ // AccessVCallOffset indicates whether we are getting the offset to a vcall
+ // offset or not. If it is true, we need to use the type id of the added vcall
+ // offset subtype of RD whose vtables are ones containing vcall offsets of RD.
+ llvm::Metadata *TypeId = IsVCallOffset
+ ? CGM.CreateMetadataIdentifierForVCallOffsetType(
+ QualType(RD->getTypeForDecl(), 0))
+ : CGM.CreateMetadataIdentifierForType(
+ QualType(RD->getTypeForDecl(), 0));
+ std::pair<llvm::Metadata *, int64_t> P = std::make_pair(TypeId, Offset);
+
+ // The offset global variable for the entry group.
+ llvm::GlobalVariable *OffsetGV;
+ if (OffsetMap.find(P) != OffsetMap.end())
+ OffsetGV = OffsetMap[P];
+ else {
+ // If we haven't seen this offset, create an offset global variable for it.
+ llvm::Type *GVType = llvm::ArrayType::get(CGM.Int32Ty, 0);
+ // The name of a offset global variable has the format "__[type
+ // id]$[offset]".
+ std::string Name =
+ "__" + RD->getNameAsString() + "$" + std::to_string(Offset);
+ OffsetGV = new llvm::GlobalVariable(
+ CGM.getModule(), GVType,
+ /*Constant=*/true, llvm::GlobalValue::InternalLinkage,
+ llvm::Constant::getNullValue(GVType), Name);
+
+ // Add offset.type metadata to the newly created offset global variable.
+ OffsetGV->addMetadata(
+ llvm::LLVMContext::MD_offset_type,
+ *llvm::MDTuple::get(
+ CGM.getLLVMContext(),
+ {TypeId, llvm::ConstantAsMetadata::get(
+ llvm::ConstantInt::get(CGM.Int64Ty, Offset))}));
+
+ OffsetMap[P] = OffsetGV;
+ }
+ return llvm::ConstantExpr::getPtrToInt(OffsetGV, OffsetType);
+}
+
+/// Return a pointer to the specified vtable entry. When vtable interleaving
+/// is enabled, the calculation of this pointer is based on a vtable offset
+/// placeholder, which will be replaced when the interleaved layout is decided.
+llvm::Value *ItaniumCXXABI::getVTableEntryPointer(
+ CodeGenFunction &CGF, const CXXRecordDecl *RD, bool AccessVCallOffset,
+ llvm::Value *VTablePtr, int64_t Offset, const llvm::Twine &Name) {
+ llvm::Constant *OffsetConstant =
+ getOffsetConstant(RD, Offset, CGM.Int64Ty, AccessVCallOffset);
+ // Use the placeholder to calculate the pointer to the entry.
+ return CGF.Builder.CreateInBoundsGEP(nullptr, VTablePtr, OffsetConstant,
+ Name);
+}
+
llvm::Constant *ItaniumCXXABI::BuildMemberPointer(const CXXMethodDecl *MD,
CharUnits ThisAdjustment) {
assert(MD->isInstance() && "Member function must not be static!");
@@ -915,6 +1017,7 @@
CharUnits PointerWidth =
Context.toCharUnitsFromBits(Context.getTargetInfo().getPointerWidth(0));
uint64_t VTableOffset = (Index * PointerWidth.getQuantity());
+ const CXXRecordDecl *RD = MD->getParent();
if (UseARMMethodPtrABI) {
// ARM C++ ABI 3.2.1:
@@ -923,7 +1026,7 @@
// least significant bit of adj then makes exactly the same
// discrimination as the least significant bit of ptr does for
// Itanium.
- MemPtr[0] = llvm::ConstantInt::get(CGM.PtrDiffTy, VTableOffset);
+ MemPtr[0] = getOffsetConstant(RD, (int64_t)VTableOffset, CGM.PtrDiffTy);
MemPtr[1] = llvm::ConstantInt::get(CGM.PtrDiffTy,
2 * ThisAdjustment.getQuantity() + 1);
} else {
@@ -931,7 +1034,13 @@
// For a virtual function, [the pointer field] is 1 plus the
// virtual table offset (in bytes) of the function,
// represented as a ptrdiff_t.
- MemPtr[0] = llvm::ConstantInt::get(CGM.PtrDiffTy, VTableOffset + 1);
+ if (shouldInterleaveVTables(RD)) {
+ llvm::Constant *Placeholder =
+ getOffsetConstant(RD, (int64_t)VTableOffset, CGM.PtrDiffTy);
+ llvm::Constant *ConstantOne = llvm::ConstantInt::get(CGM.PtrDiffTy, 1);
+ MemPtr[0] = llvm::ConstantExpr::getAdd(Placeholder, ConstantOne);
+ } else
+ MemPtr[0] = llvm::ConstantInt::get(CGM.PtrDiffTy, VTableOffset + 1);
MemPtr[1] = llvm::ConstantInt::get(CGM.PtrDiffTy,
ThisAdjustment.getQuantity());
}
@@ -1428,9 +1537,9 @@
CGM.getItaniumVTableContext().getVirtualBaseOffsetOffset(ClassDecl,
BaseClassDecl);
- llvm::Value *VBaseOffsetPtr =
- CGF.Builder.CreateConstGEP1_64(VTablePtr, VBaseOffsetOffset.getQuantity(),
- "vbase.offset.ptr");
+ llvm::Value *VBaseOffsetPtr = getVTableEntryPointer(
+ CGF, ClassDecl, false, VTablePtr, VBaseOffsetOffset.getQuantity(),
+ "vbase.offset.ptr");
VBaseOffsetPtr = CGF.Builder.CreateBitCast(VBaseOffsetPtr,
CGM.PtrDiffTy->getPointerTo());
@@ -1649,8 +1758,13 @@
llvm::ConstantInt::get(CGM.Int32Ty, AddressPoint.AddressPointIndex),
};
+ // The address point calculated by vtable_start + offset where vtable_start is
+ // the base address of the gep. When interleaving is enabled, we replace
+ // vtable_start with the new address point in the interleaved layout minus
+ // offset, which may not be an in bound address of the object.
+ bool InBounds = shouldInterleaveVTables(VTableClass) ? false : true;
return llvm::ConstantExpr::getGetElementPtr(VTable->getValueType(), VTable,
- Indices, /*InBounds=*/true,
+ Indices, /*InBounds=*/InBounds,
/*InRangeIndex=*/1);
}
@@ -1717,21 +1831,25 @@
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());
+ auto *RecordDecl = MethodDecl->getParent();
+ llvm::Value *VTable = CGF.GetVTablePtr(This, CGM.Int8PtrTy, RecordDecl);
uint64_t VTableIndex = CGM.getItaniumVTableContext().getMethodVTableIndex(GD);
+ int64_t ByteOffset =
+ VTableIndex * CGM.getContext().getTargetInfo().getPointerWidth(0) / 8;
+ llvm::Value *OffsetConstant =
+ getOffsetConstant(RecordDecl, ByteOffset, CGM.Int32Ty);
llvm::Value *VFunc;
- if (CGF.ShouldEmitVTableTypeCheckedLoad(MethodDecl->getParent())) {
- VFunc = CGF.EmitVTableTypeCheckedLoad(
- MethodDecl->getParent(), VTable,
- VTableIndex * CGM.getContext().getTargetInfo().getPointerWidth(0) / 8);
- } else {
- CGF.EmitTypeMetadataCodeForVCall(MethodDecl->getParent(), VTable, Loc);
-
+ if (CGF.ShouldEmitVTableTypeCheckedLoad(RecordDecl))
+ VFunc = CGF.EmitVTableTypeCheckedLoad(RecordDecl, VTable,
+ Ty->getPointerTo(), OffsetConstant);
+ else {
+ CGF.EmitTypeMetadataCodeForVCall(RecordDecl, VTable, Loc);
llvm::Value *VFuncPtr =
- CGF.Builder.CreateConstInBoundsGEP1_64(VTable, VTableIndex, "vfn");
+ CGF.Builder.CreateInBoundsGEP(nullptr, VTable, OffsetConstant, "vfn");
+ VFuncPtr =
+ CGF.Builder.CreateBitCast(VFuncPtr, Ty->getPointerTo()->getPointerTo());
auto *VFuncLoad =
CGF.Builder.CreateAlignedLoad(VFuncPtr, CGF.getPointerAlign());
@@ -1834,11 +1952,11 @@
return true;
}
-static llvm::Value *performTypeAdjustment(CodeGenFunction &CGF,
- Address InitialPtr,
- int64_t NonVirtualAdjustment,
- int64_t VirtualAdjustment,
- bool IsReturnAdjustment) {
+
+llvm::Value *ItaniumCXXABI::performTypeAdjustment(
+ CodeGenFunction &CGF, Address InitialPtr, int64_t NonVirtualAdjustment,
+ const CXXRecordDecl *VirtuallyAdjustedType, int64_t VirtualAdjustment,
+ bool IsReturnAdjustment) {
if (!NonVirtualAdjustment && !VirtualAdjustment)
return InitialPtr.getPointer();
@@ -1853,15 +1971,19 @@
// Perform the virtual adjustment if we have one.
llvm::Value *ResultPtr;
if (VirtualAdjustment) {
+ assert(VirtuallyAdjustedType != nullptr &&
+ "The type being virtually adjusted is not available");
+
llvm::Type *PtrDiffTy =
CGF.ConvertType(CGF.getContext().getPointerDiffType());
Address VTablePtrPtr = CGF.Builder.CreateElementBitCast(V, CGF.Int8PtrTy);
llvm::Value *VTablePtr = CGF.Builder.CreateLoad(VTablePtrPtr);
-
+ // If there is virtual adjustment and this is a this adjustment, we know
+ // that we read the vcall offset from the vtable.
llvm::Value *OffsetPtr =
- CGF.Builder.CreateConstInBoundsGEP1_64(VTablePtr, VirtualAdjustment);
-
+ getVTableEntryPointer(CGF, VirtuallyAdjustedType, !IsReturnAdjustment,
+ VTablePtr, VirtualAdjustment);
OffsetPtr = CGF.Builder.CreateBitCast(OffsetPtr, PtrDiffTy->getPointerTo());
// Load the adjustment offset from the vtable.
@@ -1889,6 +2011,7 @@
Address This,
const ThisAdjustment &TA) {
return performTypeAdjustment(CGF, This, TA.NonVirtual,
+ TA.VirtuallyAdjustedType,
TA.Virtual.Itanium.VCallOffsetOffset,
/*IsReturnAdjustment=*/false);
}
@@ -1897,6 +2020,7 @@
ItaniumCXXABI::performReturnAdjustment(CodeGenFunction &CGF, Address Ret,
const ReturnAdjustment &RA) {
return performTypeAdjustment(CGF, Ret, RA.NonVirtual,
+ RA.VirtuallyAdjustedType,
RA.Virtual.Itanium.VBaseOffsetOffset,
/*IsReturnAdjustment=*/true);
}
@@ -2656,7 +2780,7 @@
class ItaniumRTTIBuilder {
CodeGenModule &CGM; // Per-module state.
llvm::LLVMContext &VMContext;
- const ItaniumCXXABI &CXXABI; // Per-module state.
+ ItaniumCXXABI &CXXABI; // Per-module state.
/// Fields - The fields of the RTTI descriptor currently being built.
SmallVector<llvm::Constant *, 16> Fields;
@@ -2676,6 +2800,13 @@
/// inheritance, according to the Itanium C++ ABI, 2.9.5p6b.
void BuildSIClassTypeInfo(const CXXRecordDecl *RD);
+ /// GetOffsetFlag - Build a constant representing __offset_flags.
+ /// When vtable interleaving is enabled and Offset is an virtual base offset,
+ /// this constant will be constructed from the corresponding offset
+ /// placeholder.
+ llvm::Constant *GetOffsetFlag(const CXXRecordDecl *RD, int64_t Offset,
+ uint64_t LowerBits, llvm::Type *OffsetFlagsTy);
+
/// BuildVMIClassTypeInfo - Build an abi::__vmi_class_type_info, used for
/// classes with bases that do not satisfy the abi::__si_class_type_info
/// constraints, according ti the Itanium C++ ABI, 2.9.5p5c.
@@ -2694,7 +2825,7 @@
void BuildPointerToMemberTypeInfo(const MemberPointerType *Ty);
public:
- ItaniumRTTIBuilder(const ItaniumCXXABI &ABI)
+ ItaniumRTTIBuilder(ItaniumCXXABI &ABI)
: CGM(ABI.CGM), VMContext(CGM.getModule().getContext()), CXXABI(ABI) {}
// Pointer type info flags.
@@ -3052,6 +3183,11 @@
static const char * const VMIClassTypeInfo =
"_ZTVN10__cxxabiv121__vmi_class_type_infoE";
+ llvm::Type *VTableTy1 =
+ llvm::StructType::get(llvm::ArrayType::get(CGM.Int8PtrTy, 7));
+ llvm::Type *VTableTy2 =
+ llvm::StructType::get(llvm::ArrayType::get(CGM.Int8PtrTy, 10));
+ llvm::Type *CurVTableTy = nullptr;
const char *VTableName = nullptr;
switch (Ty->getTypeClass()) {
@@ -3084,6 +3220,7 @@
case Type::BlockPointer:
// abi::__fundamental_type_info.
VTableName = "_ZTVN10__cxxabiv123__fundamental_type_infoE";
+ CurVTableTy = VTableTy1;
break;
case Type::ConstantArray:
@@ -3091,17 +3228,20 @@
case Type::VariableArray:
// abi::__array_type_info.
VTableName = "_ZTVN10__cxxabiv117__array_type_infoE";
+ CurVTableTy = VTableTy1;
break;
case Type::FunctionNoProto:
case Type::FunctionProto:
// abi::__function_type_info.
VTableName = "_ZTVN10__cxxabiv120__function_type_infoE";
+ CurVTableTy = VTableTy1;
break;
case Type::Enum:
// abi::__enum_type_info.
VTableName = "_ZTVN10__cxxabiv116__enum_type_infoE";
+ CurVTableTy = VTableTy1;
break;
case Type::Record: {
@@ -3115,7 +3255,7 @@
} else {
VTableName = VMIClassTypeInfo;
}
-
+ CurVTableTy = VTableTy2;
break;
}
@@ -3126,6 +3266,7 @@
// Handle id and Class.
if (isa<BuiltinType>(Ty)) {
VTableName = ClassTypeInfo;
+ CurVTableTy = VTableTy2;
break;
}
@@ -3138,33 +3279,52 @@
} else {
VTableName = ClassTypeInfo;
}
+ CurVTableTy = VTableTy2;
break;
case Type::ObjCObjectPointer:
case Type::Pointer:
// abi::__pointer_type_info.
VTableName = "_ZTVN10__cxxabiv119__pointer_type_infoE";
+ CurVTableTy = VTableTy1;
break;
case Type::MemberPointer:
// abi::__pointer_to_member_type_info.
VTableName = "_ZTVN10__cxxabiv129__pointer_to_member_type_infoE";
+ CurVTableTy = VTableTy1;
break;
}
- llvm::Constant *VTable =
- CGM.getModule().getOrInsertGlobal(VTableName, CGM.Int8PtrTy);
- CGM.setDSOLocal(cast<llvm::GlobalValue>(VTable->stripPointerCasts()));
-
- llvm::Type *PtrDiffTy =
- CGM.getTypes().ConvertType(CGM.getContext().getPointerDiffType());
+ llvm::Constant *VTable;
+ // FIXME When interleaving is enabled, we assume that
+ // libc++abi is used and we use vtable types for typeinfo classes
+ // defined in libc++abi.
+ if (CGM.getCodeGenOpts().VTableInterleaving) {
+ VTable = CGM.getModule().getOrInsertGlobal(VTableName, CurVTableTy);
+ CGM.setDSOLocal(cast<llvm::GlobalValue>(VTable->stripPointerCasts()));
+ llvm::Value *Indices[] = {
+ llvm::ConstantInt::get(CGM.Int32Ty, 0),
+ llvm::ConstantInt::get(CGM.Int32Ty, 0),
+ llvm::ConstantInt::get(CGM.Int32Ty, 2),
+ };
+ VTable = llvm::ConstantExpr::getGetElementPtr(CurVTableTy, VTable, Indices,
+ /*InBounds=*/true,
+ /*InRangeIndex=*/1);
+ VTable = llvm::ConstantExpr::getBitCast(VTable, CGM.Int8PtrTy);
+ } else {
+ VTable = CGM.getModule().getOrInsertGlobal(VTableName, CGM.Int8PtrTy);
+ CGM.setDSOLocal(cast<llvm::GlobalValue>(VTable->stripPointerCasts()));
- // The vtable address point is 2.
- llvm::Constant *Two = llvm::ConstantInt::get(PtrDiffTy, 2);
- VTable =
- llvm::ConstantExpr::getInBoundsGetElementPtr(CGM.Int8PtrTy, VTable, Two);
- VTable = llvm::ConstantExpr::getBitCast(VTable, CGM.Int8PtrTy);
+ llvm::Type *PtrDiffTy =
+ CGM.getTypes().ConvertType(CGM.getContext().getPointerDiffType());
+ // The vtable address point is 2.
+ llvm::Constant *Two = llvm::ConstantInt::get(PtrDiffTy, 2);
+ VTable = llvm::ConstantExpr::getInBoundsGetElementPtr(CGM.Int8PtrTy, VTable,
+ Two);
+ VTable = llvm::ConstantExpr::getBitCast(VTable, CGM.Int8PtrTy);
+ }
Fields.push_back(VTable);
}
@@ -3536,6 +3696,28 @@
return Flags;
}
+llvm::Constant *ItaniumRTTIBuilder::GetOffsetFlag(const CXXRecordDecl *RD,
+ int64_t Offset,
+ uint64_t LowerBits,
+ llvm::Type *OffsetFlagsTy) {
+ // If interleaving is not enabled or Offset is not a virtual base offset,
+ // we don't need to create a offset placeholder for vtable interleaving.
+ if (!CXXABI.shouldInterleaveVTables(RD) || !(LowerBits & BCTI_Virtual)) {
+ uint64_t UpperBits = (uint64_t)Offset << 8;
+ uint64_t OffsetFlags = UpperBits | LowerBits;
+ return llvm::ConstantInt::get(OffsetFlagsTy, OffsetFlags);
+ }
+
+ llvm::Constant *OffsetConstant =
+ CXXABI.getOffsetConstant(RD, Offset, OffsetFlagsTy);
+ llvm::Constant *ShiftAmount = llvm::ConstantInt::get(OffsetFlagsTy, 8);
+ llvm::Constant *UpperBitsConstant =
+ llvm::ConstantExpr::getNSWShl(OffsetConstant, ShiftAmount);
+ llvm::Constant *LowerBitsConstant =
+ llvm::ConstantInt::get(OffsetFlagsTy, LowerBits);
+ return llvm::ConstantExpr::getOr(UpperBitsConstant, LowerBitsConstant);
+}
+
/// BuildVMIClassTypeInfo - Build an abi::__vmi_class_type_info, used for
/// classes with bases that do not satisfy the abi::__si_class_type_info
/// constraints, according ti the Itanium C++ ABI, 2.9.5p5c.
@@ -3595,8 +3777,6 @@
const CXXRecordDecl *BaseDecl =
cast<CXXRecordDecl>(Base.getType()->getAs<RecordType>()->getDecl());
- int64_t OffsetFlags = 0;
-
// All but the lower 8 bits of __offset_flags are a signed offset.
// For a non-virtual base, this is the offset in the object of the base
// subobject. For a virtual base, this is the offset in the virtual table of
@@ -3610,16 +3790,17 @@
Offset = Layout.getBaseClassOffset(BaseDecl);
};
- OffsetFlags = uint64_t(Offset.getQuantity()) << 8;
+ int64_t OffsetInt = Offset.getQuantity();
+ uint64_t Flags = 0;
// The low-order byte of __offset_flags contains flags, as given by the
// masks from the enumeration __offset_flags_masks.
if (Base.isVirtual())
- OffsetFlags |= BCTI_Virtual;
+ Flags |= BCTI_Virtual;
if (Base.getAccessSpecifier() == AS_public)
- OffsetFlags |= BCTI_Public;
+ Flags |= BCTI_Public;
- Fields.push_back(llvm::ConstantInt::get(OffsetFlagsLTy, OffsetFlags));
+ Fields.push_back(GetOffsetFlag(RD, OffsetInt, Flags, OffsetFlagsLTy));
}
}
Index: clang/lib/CodeGen/CodeGenModule.h
===================================================================
--- clang/lib/CodeGen/CodeGenModule.h
+++ clang/lib/CodeGen/CodeGenModule.h
@@ -538,6 +538,7 @@
MetadataTypeMap MetadataIdMap;
MetadataTypeMap VirtualMetadataIdMap;
MetadataTypeMap GeneralizedMetadataIdMap;
+ MetadataTypeMap VCallOffsetMetadataIdMap;
public:
CodeGenModule(ASTContext &C, const HeaderSearchOptions &headersearchopts,
@@ -1288,6 +1289,10 @@
/// unnamed MDNode (for internal identifiers).
llvm::Metadata *CreateMetadataIdentifierGeneralized(QualType T);
+ /// Create a metadata identifier that is intended to be used to group
+ /// T's vtables containing vcall offsets together for vtable interleaving.
+ llvm::Metadata *CreateMetadataIdentifierForVCallOffsetType(QualType T);
+
/// Create and attach type metadata to the given function.
void CreateFunctionTypeMetadataForIcall(const FunctionDecl *FD,
llvm::Function *F);
@@ -1297,7 +1302,8 @@
/// Create and attach type metadata for the given vtable.
void AddVTableTypeMetadata(llvm::GlobalVariable *VTable, CharUnits Offset,
- const CXXRecordDecl *RD);
+ const CXXRecordDecl *RD,
+ bool IsVCallOffsetType = false);
/// Return a vector of most-base classes for RD. This is used to implement
/// control flow integrity checks for member function pointers.
Index: clang/lib/CodeGen/CodeGenModule.cpp
===================================================================
--- clang/lib/CodeGen/CodeGenModule.cpp
+++ clang/lib/CodeGen/CodeGenModule.cpp
@@ -5448,10 +5448,16 @@
GeneralizedMetadataIdMap, ".generalized");
}
+llvm::Metadata *
+CodeGenModule::CreateMetadataIdentifierForVCallOffsetType(QualType T) {
+ return CreateMetadataIdentifierImpl(T, VCallOffsetMetadataIdMap,
+ ".vcall_offset");
+}
+
/// Returns whether this module needs the "all-vtables" type identifier.
bool CodeGenModule::NeedAllVtablesTypeId() const {
- // Returns true if at least one of vtable-based CFI checkers is enabled and
- // is not in the trapping mode.
+ // Returns true if vtable interleaving is disabled and at least one of
+ // vtable-based CFI checkers is enabled and is not in the trapping mode.
return ((LangOpts.Sanitize.has(SanitizerKind::CFIVCall) &&
!CodeGenOpts.SanitizeTrap.has(SanitizerKind::CFIVCall)) ||
(LangOpts.Sanitize.has(SanitizerKind::CFINVCall) &&
@@ -5464,9 +5470,13 @@
void CodeGenModule::AddVTableTypeMetadata(llvm::GlobalVariable *VTable,
CharUnits Offset,
- const CXXRecordDecl *RD) {
+ const CXXRecordDecl *RD,
+ bool IsVCallOffsetType) {
llvm::Metadata *MD =
- CreateMetadataIdentifierForType(QualType(RD->getTypeForDecl(), 0));
+ IsVCallOffsetType
+ ? CreateMetadataIdentifierForVCallOffsetType(
+ QualType(RD->getTypeForDecl(), 0))
+ : CreateMetadataIdentifierForType(QualType(RD->getTypeForDecl(), 0));
VTable->addTypeMetadata(Offset.getQuantity(), MD);
if (CodeGenOpts.SanitizeCfiCrossDso)
Index: clang/lib/CodeGen/CodeGenFunction.h
===================================================================
--- clang/lib/CodeGen/CodeGenFunction.h
+++ clang/lib/CodeGen/CodeGenFunction.h
@@ -1942,9 +1942,12 @@
/// true when both vcall CFI and whole-program-vtables are enabled.
bool ShouldEmitVTableTypeCheckedLoad(const CXXRecordDecl *RD);
- /// Emit a type checked load from the given vtable.
- llvm::Value *EmitVTableTypeCheckedLoad(const CXXRecordDecl *RD, llvm::Value *VTable,
- uint64_t VTableByteOffset);
+ /// Emit a type checked load from the given vtable. CastedVTable
+ /// is of int8ptr type and VTableByteOffset is a byte offset.
+ llvm::Value *EmitVTableTypeCheckedLoad(const CXXRecordDecl *RD,
+ llvm::Value *CastedVTable,
+ llvm::Type *EntryType,
+ llvm::Value *VTableByteOffset);
/// EnterDtorCleanups - Enter the cleanups necessary to complete the
/// given phase of destruction for a destructor. The end result
Index: clang/lib/CodeGen/CGVTables.cpp
===================================================================
--- clang/lib/CodeGen/CGVTables.cpp
+++ clang/lib/CodeGen/CGVTables.cpp
@@ -89,9 +89,8 @@
auto ClassDecl = ResultType->getPointeeType()->getAsCXXRecordDecl();
auto ClassAlign = CGF.CGM.getClassPointerAlignment(ClassDecl);
- ReturnValue = CGF.CGM.getCXXABI().performReturnAdjustment(CGF,
- Address(ReturnValue, ClassAlign),
- Thunk.Return);
+ ReturnValue = CGF.CGM.getCXXABI().performReturnAdjustment(
+ CGF, Address(ReturnValue, ClassAlign), Thunk.Return);
if (NullCheckValue) {
CGF.Builder.CreateBr(AdjustEnd);
@@ -287,9 +286,9 @@
// Adjust the 'this' pointer if necessary
llvm::Value *AdjustedThisPtr =
- Thunk ? CGM.getCXXABI().performThisAdjustment(
- *this, LoadCXXThisAddress(), Thunk->This)
- : LoadCXXThis();
+ Thunk ? CGM.getCXXABI().performThisAdjustment(*this, LoadCXXThisAddress(),
+ Thunk->This)
+ : LoadCXXThis();
if (CurFnInfo->usesInAlloca() || IsUnprototyped) {
// We don't handle return adjusting thunks, because they require us to call
@@ -1018,12 +1017,14 @@
CharUnits PointerWidth =
Context.toCharUnitsFromBits(Context.getTargetInfo().getPointerWidth(0));
- typedef std::pair<const CXXRecordDecl *, unsigned> AddressPoint;
+ typedef std::tuple<const CXXRecordDecl *, unsigned, bool> AddressPoint;
std::vector<AddressPoint> AddressPoints;
for (auto &&AP : VTLayout.getAddressPoints())
- AddressPoints.push_back(std::make_pair(
- AP.first.getBase(), VTLayout.getVTableOffset(AP.second.VTableIndex) +
- AP.second.AddressPointIndex));
+ AddressPoints.push_back(
+ std::make_tuple(AP.first.getBase(),
+ VTLayout.getVTableOffset(AP.second.VTableIndex) +
+ AP.second.AddressPointIndex,
+ AP.second.HasVCallOffsets));
// Sort the address points for determinism.
llvm::sort(AddressPoints, [this](const AddressPoint &AP1,
@@ -1034,13 +1035,13 @@
std::string S1;
llvm::raw_string_ostream O1(S1);
getCXXABI().getMangleContext().mangleTypeName(
- QualType(AP1.first->getTypeForDecl(), 0), O1);
+ QualType(std::get<0>(AP1)->getTypeForDecl(), 0), O1);
O1.flush();
std::string S2;
llvm::raw_string_ostream O2(S2);
getCXXABI().getMangleContext().mangleTypeName(
- QualType(AP2.first->getTypeForDecl(), 0), O2);
+ QualType(std::get<0>(AP2)->getTypeForDecl(), 0), O2);
O2.flush();
if (S1 < S2)
@@ -1048,13 +1049,29 @@
if (S1 != S2)
return false;
- return AP1.second < AP2.second;
+ return std::get<1>(AP1) < std::get<1>(AP2);
});
ArrayRef<VTableComponent> Comps = VTLayout.vtable_components();
for (auto AP : AddressPoints) {
// Create type metadata for the address point.
- AddVTableTypeMetadata(VTable, PointerWidth * AP.second, AP.first);
+ AddVTableTypeMetadata(VTable, PointerWidth * std::get<1>(AP),
+ std::get<0>(AP));
+
+ if (getCodeGenOpts().VTableInterleaving) {
+ // If the address point of some type T is in a vtable that contains vcall
+ // offsets for T, this address in the vtable is also an address point for
+ // the new type T.vcall_offset, which is intended to group all the vtables
+ // of T that contain vcall offsets together for interleaving.
+ if (std::get<2>(AP))
+ AddVTableTypeMetadata(VTable, PointerWidth * std::get<1>(AP),
+ std::get<0>(AP), true);
+
+ // When vtable interleaving is enabled, we don't emit type metadata for
+ // virtual member functions because the interleaving pass uses type
+ // metadata to determine address points of vtables.
+ continue;
+ }
// The class associated with each address point could also potentially be
// used for indirect calls via a member function pointer, so we need to
@@ -1066,7 +1083,7 @@
llvm::Metadata *MD = CreateMetadataIdentifierForVirtualMemPtrType(
Context.getMemberPointerType(
Comps[I].getFunctionDecl()->getType(),
- Context.getRecordType(AP.first).getTypePtr()));
+ Context.getRecordType(std::get<0>(AP)).getTypePtr()));
VTable->addTypeMetadata((PointerWidth * I).getQuantity(), MD);
}
}
Index: clang/lib/CodeGen/CGClass.cpp
===================================================================
--- clang/lib/CodeGen/CGClass.cpp
+++ clang/lib/CodeGen/CGClass.cpp
@@ -2785,7 +2785,8 @@
}
llvm::Value *CodeGenFunction::EmitVTableTypeCheckedLoad(
- const CXXRecordDecl *RD, llvm::Value *VTable, uint64_t VTableByteOffset) {
+ const CXXRecordDecl *RD, llvm::Value *CastedVTable, llvm::Type *ReturnType,
+ llvm::Value *VTableByteOffset) {
SanitizerScope SanScope(this);
EmitSanitizerStatReport(llvm::SanStat_CFI_VCall);
@@ -2794,19 +2795,16 @@
CGM.CreateMetadataIdentifierForType(QualType(RD->getTypeForDecl(), 0));
llvm::Value *TypeId = llvm::MetadataAsValue::get(CGM.getLLVMContext(), MD);
- llvm::Value *CastedVTable = Builder.CreateBitCast(VTable, Int8PtrTy);
- llvm::Value *CheckedLoad = Builder.CreateCall(
- CGM.getIntrinsic(llvm::Intrinsic::type_checked_load),
- {CastedVTable, llvm::ConstantInt::get(Int32Ty, VTableByteOffset),
- TypeId});
+ llvm::Value *CheckedLoad =
+ Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::type_checked_load),
+ {CastedVTable, VTableByteOffset, TypeId});
llvm::Value *CheckResult = Builder.CreateExtractValue(CheckedLoad, 1);
EmitCheck(std::make_pair(CheckResult, SanitizerKind::CFIVCall),
SanitizerHandler::CFICheckFail, nullptr, nullptr);
- return Builder.CreateBitCast(
- Builder.CreateExtractValue(CheckedLoad, 0),
- cast<llvm::PointerType>(VTable->getType())->getElementType());
+ return Builder.CreateBitCast(Builder.CreateExtractValue(CheckedLoad, 0),
+ ReturnType);
}
void CodeGenFunction::EmitForwardingCallToLambda(
Index: clang/lib/CodeGen/CGCXXABI.h
===================================================================
--- clang/lib/CodeGen/CGCXXABI.h
+++ clang/lib/CodeGen/CGCXXABI.h
@@ -444,8 +444,7 @@
virtual void setThunkLinkage(llvm::Function *Thunk, bool ForVTable,
GlobalDecl GD, bool ReturnAdjustment) = 0;
- virtual llvm::Value *performThisAdjustment(CodeGenFunction &CGF,
- Address This,
+ virtual llvm::Value *performThisAdjustment(CodeGenFunction &CGF, Address This,
const ThisAdjustment &TA) = 0;
virtual llvm::Value *performReturnAdjustment(CodeGenFunction &CGF,
Index: clang/lib/AST/VTableBuilder.cpp
===================================================================
--- clang/lib/AST/VTableBuilder.cpp
+++ clang/lib/AST/VTableBuilder.cpp
@@ -566,13 +566,20 @@
/// (Can be null when we're not building a vtable of the most derived class).
const FinalOverriders *Overriders;
+ /// VCallOffsetBases - The set of BaseSubobjects whose vtables contain vcall
+ /// offsets.
+ llvm::DenseSet<BaseSubobject> VCallOffsetBases;
+
/// AddVCallAndVBaseOffsets - Add vcall offsets and vbase offsets for the
/// given base subobject.
void AddVCallAndVBaseOffsets(BaseSubobject Base, bool BaseIsVirtual,
CharUnits RealBaseOffset);
/// AddVCallOffsets - Add vcall offsets for the given base subobject.
- void AddVCallOffsets(BaseSubobject Base, CharUnits VBaseOffset);
+ /// VBase is the virtual base whose vtable contains the vcall offsets that are
+ /// collected during this function.
+ void AddVCallOffsets(BaseSubobject Base, CharUnits VBaseOffset,
+ BaseSubobject VBase);
/// AddVBaseOffsets - Add vbase offsets for the given class.
void AddVBaseOffsets(const CXXRecordDecl *Base,
@@ -604,6 +611,11 @@
const VBaseOffsetOffsetsMapTy &getVBaseOffsetOffsets() const {
return VBaseOffsetOffsets;
}
+
+ /// isVCallOffsetBase - Check whether Base's vtable has vcall offsets.
+ bool isVCallOffsetBase(BaseSubobject Base) {
+ return VCallOffsetBases.find(Base) != VCallOffsetBases.end();
+ }
};
void
@@ -652,7 +664,7 @@
// We only want to add vcall offsets for virtual bases.
if (BaseIsVirtual)
- AddVCallOffsets(Base, RealBaseOffset);
+ AddVCallOffsets(Base, RealBaseOffset, Base);
}
CharUnits VCallAndVBaseOffsetBuilder::getCurrentOffsetOffset() const {
@@ -669,7 +681,8 @@
}
void VCallAndVBaseOffsetBuilder::AddVCallOffsets(BaseSubobject Base,
- CharUnits VBaseOffset) {
+ CharUnits VBaseOffset,
+ BaseSubobject VBase) {
const CXXRecordDecl *RD = Base.getBase();
const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD);
@@ -684,7 +697,7 @@
"Primary base should have a zero offset!");
AddVCallOffsets(BaseSubobject(PrimaryBase, Base.getBaseOffset()),
- VBaseOffset);
+ VBaseOffset, VBase);
}
// Add the vcall offsets.
@@ -695,6 +708,9 @@
CharUnits OffsetOffset = getCurrentOffsetOffset();
+ // Now we know that VBase at least has one vcall offset in its vtable.
+ VCallOffsetBases.insert(VBase);
+
// Don't add a vcall offset if we already have one for this member function
// signature.
if (!VCallOffsets.AddVCallOffset(MD, OffsetOffset))
@@ -729,8 +745,7 @@
CharUnits BaseOffset = Base.getBaseOffset() +
Layout.getBaseClassOffset(BaseDecl);
- AddVCallOffsets(BaseSubobject(BaseDecl, BaseOffset),
- VBaseOffset);
+ AddVCallOffsets(BaseSubobject(BaseDecl, BaseOffset), VBaseOffset, VBase);
}
}
@@ -1175,7 +1190,7 @@
ReturnAdjustment
ItaniumVTableBuilder::ComputeReturnAdjustment(BaseOffset Offset) {
- ReturnAdjustment Adjustment;
+ ReturnAdjustment Adjustment(Offset.DerivedClass);
if (!Offset.isEmpty()) {
if (Offset.VirtualBase) {
@@ -1262,7 +1277,7 @@
if (Offset.isEmpty())
return ThisAdjustment();
- ThisAdjustment Adjustment;
+ ThisAdjustment Adjustment(Offset.VirtualBase);
if (Offset.VirtualBase) {
// Get the vcall offset map for this virtual base.
@@ -1684,11 +1699,13 @@
// Add all address points.
while (true) {
- AddressPoints.insert(
- std::make_pair(BaseSubobject(RD, OffsetInLayoutClass),
- VTableLayout::AddressPointLocation{
- unsigned(VTableIndices.size() - 1),
- unsigned(AddressPoint - VTableIndex)}));
+ BaseSubobject CurBaseSubObj(RD, OffsetInLayoutClass);
+ bool HasVCallOffset = Builder.isVCallOffsetBase(CurBaseSubObj);
+ AddressPoints.insert(std::make_pair(
+ CurBaseSubObj,
+ VTableLayout::AddressPointLocation{unsigned(VTableIndices.size() - 1),
+ unsigned(AddressPoint - VTableIndex),
+ HasVCallOffset}));
const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD);
const CXXRecordDecl *PrimaryBase = Layout.getPrimaryBase();
Index: clang/include/clang/Driver/CC1Options.td
===================================================================
--- clang/include/clang/Driver/CC1Options.td
+++ clang/include/clang/Driver/CC1Options.td
@@ -375,6 +375,8 @@
Values<"a_key,b_key">;
def mbranch_target_enforce : Flag<["-"], "mbranch-target-enforce">;
def fno_dllexport_inlines : Flag<["-"], "fno-dllexport-inlines">;
+def vtable_interleaving : Flag<["-"], "vtable-interleaving">,
+ HelpText<"Enable VTable interleaving">;
//===----------------------------------------------------------------------===//
// Dependency Output Options
Index: clang/include/clang/Basic/CodeGenOptions.def
===================================================================
--- clang/include/clang/Basic/CodeGenOptions.def
+++ clang/include/clang/Basic/CodeGenOptions.def
@@ -364,6 +364,9 @@
/// Whether to emit unused static constants.
CODEGENOPT(KeepStaticConsts, 1, 0)
+/// Whether to interleave VTables.
+CODEGENOPT(VTableInterleaving, 1, 0)
+
#undef CODEGENOPT
#undef ENUM_CODEGENOPT
#undef VALUE_CODEGENOPT
Index: clang/include/clang/Basic/ABI.h
===================================================================
--- clang/include/clang/Basic/ABI.h
+++ clang/include/clang/Basic/ABI.h
@@ -37,6 +37,8 @@
Dtor_Comdat ///< The COMDAT used for dtors
};
+class CXXRecordDecl;
+
/// A return adjustment.
struct ReturnAdjustment {
/// The non-virtual adjustment from the derived object to its
@@ -81,7 +83,13 @@
}
} Virtual;
- ReturnAdjustment() : NonVirtual(0) {}
+ /// The type for which VirtualAdjustment is applied.
+ /// This field is only valid when the virtual return adjust is non-zero.
+ const CXXRecordDecl *VirtuallyAdjustedType;
+
+ ReturnAdjustment() : NonVirtual(0), VirtuallyAdjustedType(nullptr) {}
+ ReturnAdjustment(const CXXRecordDecl *Type)
+ : NonVirtual(0), VirtuallyAdjustedType(Type) {}
bool isEmpty() const { return !NonVirtual && Virtual.isEmpty(); }
@@ -149,7 +157,13 @@
}
} Virtual;
- ThisAdjustment() : NonVirtual(0) { }
+ /// The virtual base type for which VirtualAdjustment is applied.
+ /// This field is only valid when the virtual this adjustment is non-zero.
+ const CXXRecordDecl *VirtuallyAdjustedType;
+
+ ThisAdjustment() : NonVirtual(0), VirtuallyAdjustedType(nullptr) {}
+ ThisAdjustment(const CXXRecordDecl *Type)
+ : NonVirtual(0), VirtuallyAdjustedType(Type) {}
bool isEmpty() const { return !NonVirtual && Virtual.isEmpty(); }
Index: clang/include/clang/AST/VTableBuilder.h
===================================================================
--- clang/include/clang/AST/VTableBuilder.h
+++ clang/include/clang/AST/VTableBuilder.h
@@ -234,6 +234,9 @@
typedef std::pair<uint64_t, ThunkInfo> VTableThunkTy;
struct AddressPointLocation {
unsigned VTableIndex, AddressPointIndex;
+ // This flag indicates whether the vtable containing this address point
+ // has vcall offsets for the type associated with this address point.
+ bool HasVCallOffsets;
};
typedef llvm::DenseMap<BaseSubobject, AddressPointLocation>
AddressPointsMapTy;
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits