leonardchan updated this revision to Diff 188256.

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

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,48 @@
   }
 }
 
+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;
+  }
+
+  // This class's ABI is not inherited. Infer it from LTO visibility.
+  if (Record->hasHiddenLTOVisibility())
+    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 +6206,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);
 
@@ -3028,6 +3027,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
@@ -10893,6 +10893,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
@@ -320,6 +320,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
@@ -9529,4 +9529,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
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to