pcc created this revision.
pcc added reviewers: rsmith, rjmccall, rafael, mehdi_amini, aaron.ballman, 
majnemer, rnk.
pcc added a subscriber: cfe-commits.
Herald added a subscriber: mehdi_amini.

I'd like to propose this patch as an alternative to D17893 and D18199
that approaches the user interface to the relative C++ ABI feature from
a different angle. Instead of adding custom attributes and a whitelist,
we instead introduce a flag, -flto-relative-c++-abi-vtables, which enables
the relative ABI only for classes with hidden LTO visibility [0], which is
a concept we introduced for whole-program devirtualization that is based
on object file visibility. That concept also effectively controls where
the compiler is allowed to change the ABI arbitrarily. By using that very
similar concept, we can start off with a much simpler overall user interface,
at least for programs that are already using LTO.

It may seem strange to tie this feature to LTO visibility (and LTO), since
the feature of course does not intrinsically rely on LTO. However, I think
it seems reasonable for hidden LTO visibility to act as one possible source
of information for where the new ABI can be applied. At a later point we
can consider introducing other ways to enable the ABI (such as specific
attributes or whitelisting as proposed in D17893).

By requiring classes to have hidden visibility (a prerequisite for hidden
LTO visibility), we also avoid being blocked on the issues around non-hidden
visibility in ELF that were raised on the initial discussion thread for this
feature [1].

LTO also makes it a little easier to detect ABI mismatches in a platform
independent way using module flags. This patch introduces a module flag that
is used for that purpose.

[0] http://clang.llvm.org/docs/LTOVisibility.html
[1] http://lists.llvm.org/pipermail/llvm-dev/2016-February/096146.html

http://reviews.llvm.org/D20749

Files:
  docs/UsersManual.rst
  include/clang/AST/DeclCXX.h
  include/clang/Basic/DiagnosticSemaKinds.td
  include/clang/Basic/LangOptions.def
  include/clang/Driver/Options.td
  include/clang/Frontend/CodeGenOptions.def
  include/clang/Sema/Sema.h
  lib/AST/DeclCXX.cpp
  lib/CodeGen/CGClass.cpp
  lib/CodeGen/CGDebugInfo.cpp
  lib/CodeGen/CGVTables.cpp
  lib/CodeGen/CGVTables.h
  lib/CodeGen/CodeGenFunction.h
  lib/CodeGen/CodeGenModule.cpp
  lib/CodeGen/CodeGenModule.h
  lib/CodeGen/ItaniumCXXABI.cpp
  lib/CodeGen/MicrosoftCXXABI.cpp
  lib/Driver/Tools.cpp
  lib/Frontend/CompilerInvocation.cpp
  lib/Sema/SemaDeclCXX.cpp
  test/CodeGenCXX/bitsets.cpp
  test/CodeGenCXX/debug-info-virtual-fn-relative.cpp
  test/CodeGenCXX/vtable-relative-abi.cpp
  test/Driver/unstable-cxx-abi-vtables.cpp
  test/SemaCXX/unstable-cxx-abi.cpp

Index: test/SemaCXX/unstable-cxx-abi.cpp
===================================================================
--- /dev/null
+++ test/SemaCXX/unstable-cxx-abi.cpp
@@ -0,0 +1,35 @@
+// RUN: %clang_cc1 %s -w -std=c++11 -fsyntax-only -verify -flto-relative-c++-abi-vtables -fvisibility hidden
+
+struct __attribute__((visibility("default"))) platform { // expected-note 2{{base 'platform' uses the platform ABI}}
+  virtual void f();
+};
+struct relative { // expected-note 2{{base 'relative' uses the relative ABI}}
+  virtual void f();
+};
+
+struct mixed_bases : platform, relative {}; // expected-error {{inconsistent ABI for class 'mixed_bases'}}
+struct mixed_base_vbase : platform, virtual relative {}; // expected-error {{inconsistent ABI for class 'mixed_base_vbase'}}
+
+void f() {
+  struct relative2 {
+    virtual void f();
+  };
+  struct relative_derived : relative, relative2 {};
+}
+
+struct __attribute__((visibility("default"))) platform2 {
+  void mf() {
+    struct platform3 {
+      virtual void f();
+    };
+    struct platform_derived : platform, platform3 {};
+  }
+};
+
+struct __attribute__((visibility("default"))) non_dynamic {};
+
+struct relative_derived : non_dynamic, relative {};
+struct platform_derived : non_dynamic, platform {};
+
+struct relative_derived2 : relative_derived, relative {};
+struct platform_derived2 : platform_derived, platform {};
Index: test/Driver/unstable-cxx-abi-vtables.cpp
===================================================================
--- /dev/null
+++ test/Driver/unstable-cxx-abi-vtables.cpp
@@ -0,0 +1,5 @@
+// RUN: %clang -flto-relative-c++-abi-vtables -### %s 2>&1 | FileCheck -check-prefix=CLASSES %s
+// CLASSES: '-fwhole-program-vtables' only allowed with '-flto'
+//
+// 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: test/CodeGenCXX/vtable-relative-abi.cpp
===================================================================
--- /dev/null
+++ test/CodeGenCXX/vtable-relative-abi.cpp
@@ -0,0 +1,123 @@
+// RUN: %clang_cc1 %s -flto -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 -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 -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* 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 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 2) to i64)) to i32) }, align 8
+// CHECK-MS: @0 = private unnamed_addr constant { i8*, i32, i32 } { i8* bitcast (%rtti.CompleteObjectLocator* @"\01??_R4S@@6B@" to i8*), i32 trunc (i64 sub (i64 ptrtoint (void (%struct.S*)* @"\01?f1@S@@UEAAXXZ" to i64), i64 ptrtoint (i32* getelementptr inbounds ({ i8*, i32, i32 }, { i8*, i32, i32 }* @0, i32 0, i32 1) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%struct.S*)* @"\01?f2@S@@UEAAXXZ" to i64), i64 ptrtoint (i32* getelementptr inbounds ({ i8*, i32, i32 }, { i8*, i32, i32 }* @0, i32 0, i32 1) to i64)) to i32) }, comdat($"\01??_7S@@6B@")
+struct S {
+  S();
+  virtual void f1();
+  virtual void f2();
+};
+
+// CHECK-ITANIUM: @_ZTV1T = hidden unnamed_addr constant { 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 2) to i64)) to i32) }
+// CHECK-MS: @1 = private unnamed_addr constant { i8*, i32 } { i8* bitcast (%rtti.CompleteObjectLocator* @"\01??_R4T@@6B@" to i8*), i32 trunc (i64 sub (i64 ptrtoint (void (%struct.T*)* @"\01?g@T@@UEAAXXZ" to i64), i64 ptrtoint (i32* getelementptr inbounds ({ i8*, i32 }, { i8*, i32 }* @1, i32 0, i32 1) to i64)) to i32) }, comdat($"\01??_7T@@6B@")
+struct T {
+  T();
+  virtual void g();
+};
+
+// CHECK-ITANIUM: @_ZTV1U = hidden unnamed_addr constant { i8*, i8*, i32, i32, i8*, i8*, 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 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 2) to i64)) to 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 6) to i64)) to i32) }, align 8
+// CHECK-MS: @2 = private unnamed_addr constant { i8*, i32, i32 } { i8* bitcast (%rtti.CompleteObjectLocator* @"\01??_R4U@@6BS@@@" to i8*), i32 trunc (i64 sub (i64 ptrtoint (void (%struct.U*)* @"\01?f1@U@@UEAAXXZ" to i64), i64 ptrtoint (i32* getelementptr inbounds ({ i8*, i32, i32 }, { i8*, i32, i32 }* @2, i32 0, i32 1) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%struct.S*)* @"\01?f2@S@@UEAAXXZ" to i64), i64 ptrtoint (i32* getelementptr inbounds ({ i8*, i32, i32 }, { i8*, i32, i32 }* @2, i32 0, i32 1) to i64)) to i32) }, comdat($"\01??_7U@@6BS@@@")
+// CHECK-MS: @3 = private unnamed_addr constant { i8*, i32 } { i8* bitcast (%rtti.CompleteObjectLocator* @"\01??_R4U@@6BT@@@" to i8*), i32 trunc (i64 sub (i64 ptrtoint (void (%struct.T*)* @"\01?g@T@@UEAAXXZ" to i64), i64 ptrtoint (i32* getelementptr inbounds ({ i8*, i32 }, { i8*, i32 }* @3, i32 0, i32 1) to i64)) to i32) }, comdat($"\01??_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* 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 4) to i64)) to i32) }, comdat
+// CHECK-ITANIUM: @_ZTC2V38_2V2 = linkonce_odr hidden unnamed_addr constant { i8*, i8*, i8*, i8*, i32, 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* 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 8) to i64)) to i32) }, comdat
+struct V3 : V1, V2 {
+  V3();
+};
+
+V3::V3() {}
+
+// CHECK-ITANIUM: define hidden void @_Z5call1P1S
+// CHECK-MS: define void @"\01?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 @"\01?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* @"\01?getmfp1@@YAP8S@@EAAXXZXZ"()
+// CHECK-MS: ret i8* bitcast {{.*}} @"\01??_9S@@$BA@AA"
+// CHECK-MS: define linkonce_odr void @"\01??_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* @"\01?getmfp2@@YAP8S@@EAAXXZXZ"()
+// CHECK-MS: ret i8* bitcast {{.*}} @"\01??_9S@@$B7AA"
+// CHECK-MS: define linkonce_odr void @"\01??_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 = !{[[MF:![0-9]+]]}
+// CHECK-NOABI: [[MF]] = !{i32 1, !"lto-relative-c++-abi-vtables", i32 0}
+
+// CHECK: !llvm.module.flags = !{[[MF:![0-9]+]]}
+// CHECK: [[MF]] = !{i32 1, !"lto-relative-c++-abi-vtables", i32 1}
Index: test/CodeGenCXX/debug-info-virtual-fn-relative.cpp
===================================================================
--- /dev/null
+++ 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: test/CodeGenCXX/bitsets.cpp
===================================================================
--- test/CodeGenCXX/bitsets.cpp
+++ test/CodeGenCXX/bitsets.cpp
@@ -5,8 +5,10 @@
 // RUN: %clang_cc1 -flto -triple x86_64-pc-windows-msvc -fsanitize=cfi-vcall -fsanitize-trap=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CFI --check-prefix=MS --check-prefix=NDIAG %s
 
 // Tests for the whole-program-vtables feature:
-// RUN: %clang_cc1 -flto -triple x86_64-unknown-linux -fvisibility hidden -fwhole-program-vtables -emit-llvm -o - %s | FileCheck --check-prefix=VTABLE-OPT --check-prefix=ITANIUM %s
-// RUN: %clang_cc1 -flto -triple x86_64-pc-windows-msvc -fwhole-program-vtables -emit-llvm -o - %s | FileCheck --check-prefix=VTABLE-OPT --check-prefix=MS %s
+// RUN: %clang_cc1 -flto -triple x86_64-unknown-linux -fvisibility hidden -fwhole-program-vtables -emit-llvm -o - %s | FileCheck --check-prefix=VTABLE-OPT --check-prefix=ITANIUM --check-prefix=ITANIUM-STABLE %s
+// RUN: %clang_cc1 -flto -triple x86_64-pc-windows-msvc -fwhole-program-vtables -emit-llvm -o - %s | FileCheck --check-prefix=VTABLE-OPT --check-prefix=MS --check-prefix=MS-STABLE %s
+// RUN: %clang_cc1 -flto -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=ITANIUM --check-prefix=ITANIUM-UNSTABLE %s
+// RUN: %clang_cc1 -flto -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=MS --check-prefix=MS-UNSTABLE %s
 
 // MS: @[[VTA:[0-9]*]] {{.*}} comdat($"\01??_7A@@6B@")
 // MS: @[[VTB:[0-9]*]] {{.*}} comdat($"\01??_7B@@6B0@@")
@@ -171,41 +173,67 @@
 // ITANIUM-NDIAG: !llvm.bitsets = !{[[X:[^,]*(,[^,]*){14}]]}
 // ITANIUM-DIAG: !llvm.bitsets = !{[[X:[^,]*(,[^,]*){23}]]}
 
-// ITANIUM-DAG: !{!"_ZTS1A", [3 x i8*]* @_ZTV1A, i64 16}
+// ITANIUM-STABLE-DAG: !{!"_ZTS1A", [3 x i8*]* @_ZTV1A, i64 16}
 // ITANIUM-DIAG-DAG: !{!"all-vtables", [3 x i8*]* @_ZTV1A, i64 16}
-// ITANIUM-DAG: !{!"_ZTS1A", [7 x i8*]* @_ZTCN12_GLOBAL__N_11DE0_1B, i64 32}
+// ITANIUM-STABLE-DAG: !{!"_ZTS1A", [7 x i8*]* @_ZTCN12_GLOBAL__N_11DE0_1B, i64 32}
 // ITANIUM-DIAG-DAG: !{!"all-vtables", [7 x i8*]* @_ZTCN12_GLOBAL__N_11DE0_1B, i64 32}
-// ITANIUM-DAG: !{!"_ZTS1B", [7 x i8*]* @_ZTCN12_GLOBAL__N_11DE0_1B, i64 32}
-// ITANIUM-DAG: !{!"_ZTS1A", [9 x i8*]* @_ZTCN12_GLOBAL__N_11DE8_1C, i64 64}
+// ITANIUM-STABLE-DAG: !{!"_ZTS1B", [7 x i8*]* @_ZTCN12_GLOBAL__N_11DE0_1B, i64 32}
+// ITANIUM-STABLE-DAG: !{!"_ZTS1A", [9 x i8*]* @_ZTCN12_GLOBAL__N_11DE8_1C, i64 64}
 // ITANIUM-DIAG-DAG: !{!"all-vtables", [9 x i8*]* @_ZTCN12_GLOBAL__N_11DE8_1C, i64 64}
-// ITANIUM-DAG: !{!"_ZTS1C", [9 x i8*]* @_ZTCN12_GLOBAL__N_11DE8_1C, i64 32}
-// ITANIUM-DAG: !{!"_ZTS1A", [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32}
+// ITANIUM-STABLE-DAG: !{!"_ZTS1C", [9 x i8*]* @_ZTCN12_GLOBAL__N_11DE8_1C, i64 32}
+// ITANIUM-STABLE-DAG: !{!"_ZTS1A", [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32}
 // ITANIUM-DIAG-DAG: !{!"all-vtables", [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32}
-// ITANIUM-DAG: !{!"_ZTS1B", [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32}
-// ITANIUM-DAG: !{!"_ZTS1C", [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 88}
+// ITANIUM-STABLE-DAG: !{!"_ZTS1B", [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32}
+// ITANIUM-STABLE-DAG: !{!"_ZTS1C", [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 88}
 // ITANIUM-DIAG-DAG: !{!"all-vtables", [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 88}
-// ITANIUM-DAG: !{![[DTYPE]], [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32}
-// ITANIUM-DAG: !{!"_ZTS1A", [7 x i8*]* @_ZTV1B, i64 32}
+// ITANIUM-STABLE-DAG: !{![[DTYPE]], [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32}
+// ITANIUM-STABLE-DAG: !{!"_ZTS1A", [7 x i8*]* @_ZTV1B, i64 32}
 // ITANIUM-DIAG-DAG: !{!"all-vtables", [7 x i8*]* @_ZTV1B, i64 32}
-// ITANIUM-DAG: !{!"_ZTS1B", [7 x i8*]* @_ZTV1B, i64 32}
-// ITANIUM-DAG: !{!"_ZTS1A", [5 x i8*]* @_ZTV1C, i64 32}
-// ITANIUM-DAG: !{!"_ZTS1C", [5 x i8*]* @_ZTV1C, i64 32}
-// ITANIUM-DAG: !{!"_ZTS1A", [3 x i8*]* @_ZTVZ3foovE2FA, i64 16}
-// ITANIUM-DAG: !{!{{[0-9]+}}, [3 x i8*]* @_ZTVZ3foovE2FA, i64 16}
-
-// MS-DAG: !{!"?AUA@@", [2 x i8*]* @[[VTA]], i64 8}
+// ITANIUM-STABLE-DAG: !{!"_ZTS1B", [7 x i8*]* @_ZTV1B, i64 32}
+// ITANIUM-STABLE-DAG: !{!"_ZTS1A", [5 x i8*]* @_ZTV1C, i64 32}
+// ITANIUM-STABLE-DAG: !{!"_ZTS1C", [5 x i8*]* @_ZTV1C, i64 32}
+// ITANIUM-STABLE-DAG: !{!"_ZTS1A", [3 x i8*]* @_ZTVZ3foovE2FA, i64 16}
+// ITANIUM-STABLE-DAG: !{!{{[0-9]+}}, [3 x i8*]* @_ZTVZ3foovE2FA, i64 16}
+
+// ITANIUM-UNSTABLE-DAG: !{!"_ZTS1A", { i8*, i8*, i32 }* @_ZTV1A, i64 16}
+// ITANIUM-UNSTABLE-DAG: !{!"_ZTS1A", { i8*, i8*, i8*, i8*, i32, i32, i32 }* @_ZTV1B, i64 32}
+// ITANIUM-UNSTABLE-DAG: !{!"_ZTS1B", { i8*, i8*, i8*, i8*, i32, i32, i32 }* @_ZTV1B, i64 32}
+// ITANIUM-UNSTABLE-DAG: !{!"_ZTS1A", { i8*, i8*, i8*, i8*, i32 }* @_ZTV1C, i64 32}
+// ITANIUM-UNSTABLE-DAG: !{!"_ZTS1C", { i8*, i8*, i8*, i8*, i32 }* @_ZTV1C, i64 32}
+// ITANIUM-UNSTABLE-DAG: !{!"_ZTS1A", { i8*, i8*, i8*, i8*, i32, i32, i32 }* @_ZTCN12_GLOBAL__N_11DE0_1B, i64 32}
+// ITANIUM-UNSTABLE-DAG: !{!"_ZTS1B", { i8*, i8*, i8*, i8*, i32, i32, i32 }* @_ZTCN12_GLOBAL__N_11DE0_1B, i64 32}
+// ITANIUM-UNSTABLE-DAG: !{!"_ZTS1A", { i8*, i8*, i8*, i8*, i32, i8*, i8*, i8*, i32 }* @_ZTCN12_GLOBAL__N_11DE8_1C, i64 64}
+// ITANIUM-UNSTABLE-DAG: !{!"_ZTS1C", { i8*, i8*, i8*, i8*, i32, i8*, i8*, i8*, i32 }* @_ZTCN12_GLOBAL__N_11DE8_1C, i64 32}
+// ITANIUM-UNSTABLE-DAG: !{!"_ZTS1A", { i8*, i8*, i8*, i8*, i32, i32, i32, i8*, i8*, i8*, i8*, i32 }* @_ZTVN12_GLOBAL__N_11DE, i64 32}
+// ITANIUM-UNSTABLE-DAG: !{!"_ZTS1B", { i8*, i8*, i8*, i8*, i32, i32, i32, i8*, i8*, i8*, i8*, i32 }* @_ZTVN12_GLOBAL__N_11DE, i64 32}
+// ITANIUM-UNSTABLE-DAG: !{!"_ZTS1C", { i8*, i8*, i8*, i8*, i32, i32, i32, i8*, i8*, i8*, i8*, i32 }* @_ZTVN12_GLOBAL__N_11DE, i64 80}
+// ITANIUM-UNSTABLE-DAG: !{![[DTYPE]], { i8*, i8*, i8*, i8*, i32, i32, i32, i8*, i8*, i8*, i8*, i32 }* @_ZTVN12_GLOBAL__N_11DE, i64 32}
+// ITANIUM-UNSTABLE-DAG: !{!"_ZTS1A", { i8*, i8*, i32 }* @_ZTVZ3foovE2FA, i64 16}
+// ITANIUM-UNSTABLE-DAG: !{!{{[0-9]+}}, { i8*, i8*, i32 }* @_ZTVZ3foovE2FA, i64 16}
+
+// MS-STABLE-DAG: !{!"?AUA@@", [2 x i8*]* @[[VTA]], i64 8}
 // MS-DIAG-DAG: !{!"all-vtables", [2 x i8*]* @[[VTA]], i64 8}
-// MS-DAG: !{!"?AUB@@", [3 x i8*]* @[[VTB]], i64 8}
+// MS-STABLE-DAG: !{!"?AUB@@", [3 x i8*]* @[[VTB]], i64 8}
 // MS-DIAG-DAG: !{!"all-vtables", [3 x i8*]* @[[VTB]], i64 8}
-// MS-DAG: !{!"?AUA@@", [2 x i8*]* @[[VTAinB]], i64 8}
+// MS-STABLE-DAG: !{!"?AUA@@", [2 x i8*]* @[[VTAinB]], i64 8}
 // MS-DIAG-DAG: !{!"all-vtables", [2 x i8*]* @[[VTAinB]], i64 8}
-// MS-DAG: !{!"?AUA@@", [2 x i8*]* @[[VTAinC]], i64 8}
+// MS-STABLE-DAG: !{!"?AUA@@", [2 x i8*]* @[[VTAinC]], i64 8}
 // MS-DIAG-DAG: !{!"all-vtables", [2 x i8*]* @[[VTAinC]], i64 8}
-// MS-DAG: !{!"?AUB@@", [3 x i8*]* @[[VTBinD]], i64 8}
+// MS-STABLE-DAG: !{!"?AUB@@", [3 x i8*]* @[[VTBinD]], i64 8}
 // MS-DIAG-DAG: !{!"all-vtables", [3 x i8*]* @[[VTBinD]], i64 8}
-// MS-DAG: !{![[DTYPE]], [3 x i8*]* @[[VTBinD]], i64 8}
-// MS-DAG: !{!"?AUA@@", [2 x i8*]* @[[VTAinBinD]], i64 8}
+// MS-STABLE-DAG: !{![[DTYPE]], [3 x i8*]* @[[VTBinD]], i64 8}
+// MS-STABLE-DAG: !{!"?AUA@@", [2 x i8*]* @[[VTAinBinD]], i64 8}
 // MS-DIAG-DAG: !{!"all-vtables", [2 x i8*]* @[[VTAinBinD]], i64 8}
-// MS-DAG: !{!"?AUA@@", [2 x i8*]* @[[VTFA]], i64 8}
+// MS-STABLE-DAG: !{!"?AUA@@", [2 x i8*]* @[[VTFA]], i64 8}
 // MS-DIAG-DAG: !{!"all-vtables", [2 x i8*]* @[[VTFA]], i64 8}
-// MS-DAG: !{!{{[0-9]+}}, [2 x i8*]* @[[VTFA]], i64 8}
+// MS-STABLE-DAG: !{!{{[0-9]+}}, [2 x i8*]* @[[VTFA]], i64 8}
+
+// MS-UNSTABLE-DAG: !{!"?AUA@@", { i8*, i32 }* @[[VTA]], i64 8}
+// MS-UNSTABLE-DAG: !{!"?AUB@@", { i8*, i32, i32 }* @[[VTB]], i64 8}
+// MS-UNSTABLE-DAG: !{!"?AUA@@", { i8*, i32 }* @[[VTAinB]], i64 8}
+// MS-UNSTABLE-DAG: !{!"?AUA@@", { i8*, i32 }* @[[VTAinC]], i64 8}
+// MS-UNSTABLE-DAG: !{!"?AUB@@", { i8*, i32, i32 }* @[[VTBinD]], i64 8}
+// MS-UNSTABLE-DAG: !{![[DTYPE]], { i8*, i32, i32 }* @[[VTBinD]], i64 8}
+// MS-UNSTABLE-DAG: !{!"?AUA@@", { i8*, i32 }* @[[VTAinBinD]], i64 8}
+// MS-UNSTABLE-DAG: !{!"?AUA@@", { i8*, i32 }* @[[VTFA]], i64 8}
+// MS-UNSTABLE-DAG: !{!{{[0-9]+}}, { i8*, i32 }* @[[VTFA]], i64 8}
Index: lib/Sema/SemaDeclCXX.cpp
===================================================================
--- lib/Sema/SemaDeclCXX.cpp
+++ lib/Sema/SemaDeclCXX.cpp
@@ -4922,6 +4922,60 @@
   }
 }
 
+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) {
+      if (Base->isRelativeCXXABI() != InheritedABIIsRelative) {
+        Diag(Record->getLocation(), diag::err_abi_mismatch) << Record;
+        CXXRecordDecl *Platform =
+            InheritedABIIsRelative ? Base : InheritedABIFrom;
+        CXXRecordDecl *Relative =
+            InheritedABIIsRelative ? InheritedABIFrom : Base;
+        Diag(Platform->getLocation(), diag::note_abi_relative_base)
+            << Platform << /*Relative=*/false;
+        Diag(Relative->getLocation(), diag::note_abi_relative_base)
+            << Relative << /*Relative=*/true;
+      }
+    } else {
+      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();
+}
+
 /// \brief Perform semantic checks on a class definition that has been
 /// completing, introducing implicitly-declared members, checking for
 /// abstract types, etc.
@@ -5075,6 +5129,8 @@
   DeclareInheritingConstructors(Record);
 
   checkClassLevelDLLAttribute(Record);
+
+  checkClassABI(Record);
 }
 
 /// Look up the special member function that would be called by a special
Index: lib/Frontend/CompilerInvocation.cpp
===================================================================
--- lib/Frontend/CompilerInvocation.cpp
+++ lib/Frontend/CompilerInvocation.cpp
@@ -487,7 +487,6 @@
   Opts.DebugColumnInfo = Args.hasArg(OPT_dwarf_column_info);
   Opts.EmitCodeView = Args.hasArg(OPT_gcodeview);
   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.DebugTypeExtRefs = Args.hasArg(OPT_dwarf_ext_refs);
   Opts.DebugExplicitImport = Triple.isPS4CPU(); 
@@ -2049,6 +2048,9 @@
   Opts.SanitizeAddressFieldPadding =
       getLastArgIntValue(Args, OPT_fsanitize_address_field_padding, 0, Diags);
   Opts.SanitizerBlacklistFiles = Args.getAllArgValues(OPT_fsanitize_blacklist);
+
+  Opts.LTOVisibilityPublicStd = Args.hasArg(OPT_flto_visibility_public_std);
+  Opts.LTORelativeCXXABIVTables = Args.hasArg(OPT_flto_relative_cxx_abi_vtables);
 }
 
 static void ParsePreprocessorArgs(PreprocessorOptions &Opts, ArgList &Args,
Index: lib/Driver/Tools.cpp
===================================================================
--- lib/Driver/Tools.cpp
+++ lib/Driver/Tools.cpp
@@ -5851,6 +5851,15 @@
     CmdArgs.push_back("-fwhole-program-vtables");
   }
 
+  // Add unstable C++ ABI flags.
+  if (Args.hasArg(options::OPT_flto_relative_cxx_abi_vtables)) {
+    if (!D.isUsingLTO())
+      D.Diag(diag::err_drv_argument_only_allowed_with)
+          << "-fwhole-program-vtables"
+          << "-flto";
+    CmdArgs.push_back("-flto-relative-c++-abi-vtables");
+  }
+
   // Finally add the compile command to the compilation.
   if (Args.hasArg(options::OPT__SLASH_fallback) &&
       Output.getType() == types::TY_Object &&
Index: lib/CodeGen/MicrosoftCXXABI.cpp
===================================================================
--- lib/CodeGen/MicrosoftCXXABI.cpp
+++ lib/CodeGen/MicrosoftCXXABI.cpp
@@ -1571,12 +1571,10 @@
                [](const VTableComponent &VTC) { return VTC.isRTTIKind(); }))
       RTTI = getMSCompleteObjectLocator(RD, Info);
 
-    llvm::Constant *Init = CGVT.CreateVTableInitializer(
-        RD, VTLayout.vtable_component_begin(),
-        VTLayout.getNumVTableComponents(), VTLayout.vtable_thunk_begin(),
-        VTLayout.getNumVTableThunks(), RTTI);
-
-    VTable->setInitializer(Init);
+    CGVT.SetVTableInitializer(VTable, RD, VTLayout.vtable_component_begin(),
+                              VTLayout.getNumVTableComponents(),
+                              VTLayout.vtable_thunk_begin(),
+                              VTLayout.getNumVTableThunks(), RTTI);
 
     emitVTableBitSetEntries(Info, RD, VTable);
   }
@@ -1695,16 +1693,14 @@
     return VTable;
   }
 
-  uint64_t NumVTableSlots =
-      VTContext.getVFTableLayout(RD, VFPtr->FullOffsetInMDC)
-          .getNumVTableComponents();
+  const VTableLayout &VTLayout =
+      VTContext.getVFTableLayout(RD, VFPtr->FullOffsetInMDC);
   llvm::GlobalValue::LinkageTypes VTableLinkage =
       VTableAliasIsRequred ? llvm::GlobalValue::PrivateLinkage : VFTableLinkage;
 
   StringRef VTableName = VTableAliasIsRequred ? StringRef() : VFTableName.str();
 
-  llvm::ArrayType *VTableType =
-      llvm::ArrayType::get(CGM.Int8PtrTy, NumVTableSlots);
+  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.
@@ -1725,8 +1721,8 @@
   // importing it.  We never reference the RTTI data directly so there is no
   // need to make room for it.
   if (VTableAliasIsRequred) {
-    llvm::Value *GEPIndices[] = {llvm::ConstantInt::get(CGM.IntTy, 0),
-                                 llvm::ConstantInt::get(CGM.IntTy, 1)};
+    llvm::Value *GEPIndices[] = {llvm::ConstantInt::get(CGM.Int32Ty, 0),
+                                 llvm::ConstantInt::get(CGM.Int32Ty, 1)};
     // Create a GEP which points just after the first entry in the VFTable,
     // this should be the location of the first virtual method.
     llvm::Constant *VTableGEP = llvm::ConstantExpr::getInBoundsGetElementPtr(
@@ -1736,6 +1732,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,
@@ -1807,24 +1804,22 @@
                                                         llvm::Type *Ty,
                                                         SourceLocation Loc) {
   GD = GD.getCanonicalDecl();
-  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::MethodVFTableLocation ML =
       CGM.getMicrosoftVTableContext().getMethodVFTableLocation(GD);
   if (CGM.getCodeGenOpts().PrepareForLTO)
     CGF.EmitBitSetCodeForVCall(getClassAtVTableLocation(getContext(), GD, ML),
                                VTable, Loc);
 
-  llvm::Value *VFuncPtr =
-      Builder.CreateConstInBoundsGEP1_64(VTable, ML.Index, "vfn");
-  return Builder.CreateAlignedLoad(VFuncPtr, CGF.getPointerAlign());
+  return CGF.GetVirtualFunctionFromVTable(MethodDecl->getParent(), VTable,
+                                          ML.Index, Ty);
 }
 
 llvm::Value *MicrosoftCXXABI::EmitVirtualDestructorCall(
@@ -1940,12 +1935,11 @@
   // Load the vfptr and then callee from the vftable.  The callee should have
   // adjusted 'this' so that the vfptr is at offset zero.
   llvm::Value *VTable = CGF.GetVTablePtr(
-      getThisAddress(CGF), ThunkTy->getPointerTo()->getPointerTo(), MD->getParent());
+      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), Callee);
 
Index: lib/CodeGen/ItaniumCXXABI.cpp
===================================================================
--- lib/CodeGen/ItaniumCXXABI.cpp
+++ lib/CodeGen/ItaniumCXXABI.cpp
@@ -578,17 +578,26 @@
   llvm::Value *VTable =
     CGF.GetVTablePtr(Address(This, VTablePtrAlign), VTableTy, RD);
 
-  // Apply the offset.
   llvm::Value *VTableOffset = FnAsInt;
   if (!UseARMMethodPtrABI)
     VTableOffset = Builder.CreateSub(VTableOffset, ptrdiff_1);
-  VTable = Builder.CreateGEP(VTable, VTableOffset);
 
   // Load the virtual function to call.
-  VTable = Builder.CreateBitCast(VTable, FTy->getPointerTo()->getPointerTo());
-  llvm::Value *VirtualFn =
-    Builder.CreateAlignedLoad(VTable, CGF.getPointerAlign(),
-                              "memptr.virtualfn");
+  llvm::Value *VirtualFn;
+  if (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.CreateGEP(VTable, VTableOffset);
+    VTableSlotPtr = Builder.CreateBitCast(VTableSlotPtr,
+                                          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
@@ -796,7 +805,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:
@@ -1458,10 +1471,10 @@
       CGM.GetAddrOfRTTIDescriptor(CGM.getContext().getTagDeclType(RD));
 
   // Create and set the initializer.
-  llvm::Constant *Init = CGVT.CreateVTableInitializer(
-      RD, VTLayout.vtable_component_begin(), VTLayout.getNumVTableComponents(),
-      VTLayout.vtable_thunk_begin(), VTLayout.getNumVTableThunks(), RTTI);
-  VTable->setInitializer(Init);
+  CGVT.SetVTableInitializer(VTable, RD, VTLayout.vtable_component_begin(),
+                            VTLayout.getNumVTableComponents(),
+                            VTLayout.vtable_thunk_begin(),
+                            VTLayout.getNumVTableThunks(), RTTI);
 
   // Set the correct linkage.
   VTable->setLinkage(Linkage);
@@ -1569,12 +1582,12 @@
   llvm::raw_svector_ostream Out(Name);
   getMangleContext().mangleCXXVTable(RD, Out);
 
-  ItaniumVTableContext &VTContext = CGM.getItaniumVTableContext();
-  llvm::ArrayType *ArrayType = llvm::ArrayType::get(
-      CGM.Int8PtrTy, VTContext.getVTableLayout(RD).getNumVTableComponents());
+  const VTableLayout &VTLayout =
+      CGM.getItaniumVTableContext().getVTableLayout(RD);
+  llvm::Type *VTableType = CGM.getVTables().GetVTableType(RD, VTLayout);
 
   VTable = CGM.CreateOrReplaceCXXRuntimeVariable(
-      Name, ArrayType, llvm::GlobalValue::ExternalLinkage);
+      Name, VTableType, llvm::GlobalValue::ExternalLinkage);
   VTable->setUnnamedAddr(true);
 
   if (RD->hasAttr<DLLImportAttr>())
@@ -1591,16 +1604,15 @@
                                                       llvm::Type *Ty,
                                                       SourceLocation Loc) {
   GD = GD.getCanonicalDecl();
-  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());
 
   CGF.EmitBitSetCodeForVCall(MethodDecl->getParent(), VTable, Loc);
 
   uint64_t VTableIndex = CGM.getItaniumVTableContext().getMethodVTableIndex(GD);
-  llvm::Value *VFuncPtr =
-      CGF.Builder.CreateConstInBoundsGEP1_64(VTable, VTableIndex, "vfn");
-  return CGF.Builder.CreateAlignedLoad(VFuncPtr, CGF.getPointerAlign());
+  return CGF.GetVirtualFunctionFromVTable(MethodDecl->getParent(), VTable,
+                                          VTableIndex, Ty);
 }
 
 llvm::Value *ItaniumCXXABI::EmitVirtualDestructorCall(
Index: lib/CodeGen/CodeGenModule.h
===================================================================
--- lib/CodeGen/CodeGenModule.h
+++ lib/CodeGen/CodeGenModule.h
@@ -1113,11 +1113,6 @@
   void EmitOMPDeclareReduction(const OMPDeclareReductionDecl *D,
                                CodeGenFunction *CGF = nullptr);
 
-  /// Returns whether the given record has hidden LTO visibility and therefore
-  /// may participate in (single-module) CFI and whole-program vtable
-  /// optimization.
-  bool HasHiddenLTOVisibility(const CXXRecordDecl *RD);
-
   /// Emit bit set entries for the given vtable using the given layout if
   /// vptr CFI is enabled.
   void EmitVTableBitSetEntries(llvm::GlobalVariable *VTable,
Index: lib/CodeGen/CodeGenModule.cpp
===================================================================
--- lib/CodeGen/CodeGenModule.cpp
+++ lib/CodeGen/CodeGenModule.cpp
@@ -478,6 +478,12 @@
                               LangOpts.CUDADeviceFlushDenormalsToZero ? 1 : 0);
   }
 
+  if (CodeGenOpts.PrepareForLTO) {
+    getModule().addModuleFlag(llvm::Module::Error,
+                              "lto-relative-c++-abi-vtables",
+                              getLangOpts().LTORelativeCXXABIVTables);
+  }
+
   if (uint32_t PLevel = Context.getLangOpts().PICLevel) {
     assert(PLevel < 3 && "Invalid PIC Level");
     getModule().setPICLevel(static_cast<llvm::PICLevel::Level>(PLevel));
Index: lib/CodeGen/CodeGenFunction.h
===================================================================
--- lib/CodeGen/CodeGenFunction.h
+++ lib/CodeGen/CodeGenFunction.h
@@ -1392,6 +1392,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: lib/CodeGen/CGVTables.h
===================================================================
--- lib/CodeGen/CGVTables.h
+++ lib/CodeGen/CGVTables.h
@@ -57,14 +57,16 @@
   void maybeEmitThunkForVTable(GlobalDecl GD, const ThunkInfo &Thunk);
 
 public:
-  /// CreateVTableInitializer - Create a vtable initializer for the given record
-  /// decl.
+  /// SetVTableInitializer - Set VTable's initializer to the vtable initializer
+  /// for the given record decl.
   /// \param Components - The vtable components; this is really an array of
   /// VTableComponents.
-  llvm::Constant *CreateVTableInitializer(
-      const CXXRecordDecl *RD, const VTableComponent *Components,
-      unsigned NumComponents, const VTableLayout::VTableThunkTy *VTableThunks,
-      unsigned NumVTableThunks, llvm::Constant *RTTI);
+  void SetVTableInitializer(llvm::GlobalVariable *VTable,
+                            const CXXRecordDecl *RD,
+                            const VTableComponent *Components,
+                            unsigned NumComponents,
+                            const VTableLayout::VTableThunkTy *VTableThunks,
+                            unsigned NumVTableThunks, llvm::Constant *RTTI);
 
   CodeGenVTables(CodeGenModule &CGM);
 
@@ -112,6 +114,11 @@
   void GenerateClassData(const CXXRecordDecl *RD);
 
   bool isVTableExternal(const CXXRecordDecl *RD);
+
+  /// Returns the type of a vtable with the given layout. Normally an array of
+  /// pointers, but may be a struct under the relative vtable ABI.
+  llvm::Type *GetVTableType(const CXXRecordDecl *RD,
+                            const VTableLayout &VTLayout);
 };
 
 } // end namespace CodeGen
Index: lib/CodeGen/CGVTables.cpp
===================================================================
--- lib/CodeGen/CGVTables.cpp
+++ lib/CodeGen/CGVTables.cpp
@@ -521,24 +521,69 @@
     emitThunk(GD, Thunk, /*ForVTable=*/false);
 }
 
-llvm::Constant *CodeGenVTables::CreateVTableInitializer(
-    const CXXRecordDecl *RD, const VTableComponent *Components,
-    unsigned NumComponents, const VTableLayout::VTableThunkTy *VTableThunks,
-    unsigned NumVTableThunks, llvm::Constant *RTTI) {
+llvm::Type *CodeGenVTables::GetVTableType(const CXXRecordDecl *RD,
+                                          const VTableLayout &VTLayout) {
+  if (!RD->isRelativeCXXABI())
+    return llvm::ArrayType::get(CGM.Int8PtrTy,
+                                VTLayout.getNumVTableComponents());
+
+  std::vector<llvm::Type *> Types;
+  for (auto &Comp : VTLayout.vtable_components()) {
+    if (Comp.isFunctionPointerKind())
+      Types.push_back(CGM.Int32Ty);
+    else
+      Types.push_back(CGM.Int8PtrTy);
+  }
+  return llvm::StructType::get(CGM.getLLVMContext(), Types);
+}
+
+void CodeGenVTables::SetVTableInitializer(
+    llvm::GlobalVariable *VTable, const CXXRecordDecl *RD,
+    const VTableComponent *Components, unsigned NumComponents,
+    const VTableLayout::VTableThunkTy *VTableThunks, unsigned NumVTableThunks,
+    llvm::Constant *RTTI) {
   SmallVector<llvm::Constant *, 64> Inits;
 
   llvm::Type *Int8PtrTy = CGM.Int8PtrTy;
+  llvm::Type *Int32Ty = CGM.Int32Ty;
   
   llvm::Type *PtrDiffTy = 
     CGM.getTypes().ConvertType(CGM.getContext().getPointerDiffType());
 
   unsigned NextVTableThunkIndex = 0;
 
   llvm::Constant *PureVirtualFn = nullptr, *DeletedVirtualFn = nullptr;
 
+  bool RelativeABI = RD->isRelativeCXXABI();
+  llvm::Type *VTableTy = VTable->getValueType();
+  llvm::Type *FunctionPtrTy = RelativeABI ? Int32Ty : Int8PtrTy;
+  llvm::Constant *AddrPointInt;
+
+  auto maybeMakeRelative = [&](llvm::Constant *C) {
+    if (!RelativeABI)
+      return C;
+    return llvm::ConstantExpr::getIntegerCast(
+        llvm::ConstantExpr::getSub(
+            llvm::ConstantExpr::getPtrToInt(C, PtrDiffTy), AddrPointInt),
+        Int32Ty, /*isSigned=*/true);
+  };
+
   for (unsigned I = 0; I != NumComponents; ++I) {
     VTableComponent Component = Components[I];
 
+    if (RelativeABI && Component.isFunctionPointerKind() &&
+        (I == 0 || !Components[I - 1].isFunctionPointerKind())) {
+      // FIXME: Need a better way of identifying address points that works with
+      // the Itanium and MS ABIs.
+      AddrPointInt = llvm::ConstantExpr::getPtrToInt(
+          llvm::ConstantExpr::getGetElementPtr(
+              VTableTy, VTable,
+              llvm::ArrayRef<llvm::Constant *>{
+                  llvm::ConstantInt::get(Int32Ty, 0),
+                  llvm::ConstantInt::get(Int32Ty, I)}),
+          PtrDiffTy);
+    }
+
     llvm::Constant *Init = nullptr;
 
     switch (Component.getKind()) {
@@ -592,7 +637,7 @@
                                  : (MD->hasAttr<CUDAHostAttr>() ||
                                     !MD->hasAttr<CUDADeviceAttr>());
         if (!CanEmitMethod) {
-          Init = llvm::ConstantExpr::getNullValue(Int8PtrTy);
+          Init = llvm::ConstantExpr::getNullValue(FunctionPtrTy);
           break;
         }
         // Method is acceptable, continue processing as usual.
@@ -610,7 +655,7 @@
           PureVirtualFn = llvm::ConstantExpr::getBitCast(PureVirtualFn,
                                                          CGM.Int8PtrTy);
         }
-        Init = PureVirtualFn;
+        Init = maybeMakeRelative(PureVirtualFn);
       } else if (cast<CXXMethodDecl>(GD.getDecl())->isDeleted()) {
         if (!DeletedVirtualFn) {
           llvm::FunctionType *Ty =
@@ -623,7 +668,7 @@
           DeletedVirtualFn = llvm::ConstantExpr::getBitCast(DeletedVirtualFn,
                                                          CGM.Int8PtrTy);
         }
-        Init = DeletedVirtualFn;
+        Init = maybeMakeRelative(DeletedVirtualFn);
       } else {
         // Check if we should use a thunk.
         if (NextVTableThunkIndex < NumVTableThunks &&
@@ -641,20 +686,26 @@
         }
 
         Init = llvm::ConstantExpr::getBitCast(Init, Int8PtrTy);
+        Init = maybeMakeRelative(Init);
       }
       break;
     }
 
     case VTableComponent::CK_UnusedFunctionPointer:
-      Init = llvm::ConstantExpr::getNullValue(Int8PtrTy);
+      Init = llvm::ConstantExpr::getNullValue(FunctionPtrTy);
       break;
     };
     
     Inits.push_back(Init);
   }
-  
-  llvm::ArrayType *ArrayType = llvm::ArrayType::get(Int8PtrTy, NumComponents);
-  return llvm::ConstantArray::get(ArrayType, Inits);
+
+  if (RelativeABI) {
+    VTable->setInitializer(
+        llvm::ConstantStruct::get(cast<llvm::StructType>(VTableTy), Inits));
+  } else {
+    VTable->setInitializer(
+        llvm::ConstantArray::get(cast<llvm::ArrayType>(VTableTy), Inits));
+  }
 }
 
 llvm::GlobalVariable *
@@ -681,8 +732,7 @@
                            Base.getBase(), Out);
   StringRef Name = OutName.str();
 
-  llvm::ArrayType *ArrayType = 
-    llvm::ArrayType::get(CGM.Int8PtrTy, VTLayout->getNumVTableComponents());
+  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
@@ -694,7 +744,7 @@
 
   // Create the variable that will hold the construction vtable.
   llvm::GlobalVariable *VTable = 
-    CGM.CreateOrReplaceCXXRuntimeVariable(Name, ArrayType, Linkage);
+    CGM.CreateOrReplaceCXXRuntimeVariable(Name, VTType, Linkage);
   CGM.setGlobalVisibility(VTable, RD);
 
   // V-tables are always unnamed_addr.
@@ -704,12 +754,11 @@
       CGM.getContext().getTagDeclType(Base.getBase()));
 
   // Create and set the initializer.
-  llvm::Constant *Init = CreateVTableInitializer(
-      Base.getBase(), VTLayout->vtable_component_begin(),
+  SetVTableInitializer(
+      VTable, Base.getBase(), VTLayout->vtable_component_begin(),
       VTLayout->getNumVTableComponents(), VTLayout->vtable_thunk_begin(),
       VTLayout->getNumVTableThunks(), RTTI);
-  VTable->setInitializer(Init);
-  
+
   CGM.EmitVTableBitSetEntries(VTable, *VTLayout.get());
 
   return VTable;
@@ -900,40 +949,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::EmitVTableBitSetEntries(llvm::GlobalVariable *VTable,
                                             const VTableLayout &VTLayout) {
   if (!getCodeGenOpts().PrepareForLTO)
@@ -976,8 +991,18 @@
 
   llvm::NamedMDNode *BitsetsMD =
       getModule().getOrInsertNamedMetadata("llvm.bitsets");
-  for (auto BitsetEntry : BitsetEntries)
-    CreateVTableBitSetEntry(BitsetsMD, VTable,
-                            PointerWidth * BitsetEntry.second,
-                            BitsetEntry.first);
+
+  const llvm::StructLayout *SL = nullptr;
+  if (auto *ST = dyn_cast<llvm::StructType>(VTable->getValueType()))
+    SL = getDataLayout().getStructLayout(ST);
+
+  for (auto BitsetEntry : BitsetEntries) {
+    CharUnits ByteOffset;
+    if (SL)
+      ByteOffset =
+          CharUnits::fromQuantity(SL->getElementOffset(BitsetEntry.second));
+    else
+      ByteOffset = PointerWidth * BitsetEntry.second;
+    CreateVTableBitSetEntry(BitsetsMD, VTable, ByteOffset, BitsetEntry.first);
+  }
 }
Index: lib/CodeGen/CGDebugInfo.cpp
===================================================================
--- lib/CodeGen/CGDebugInfo.cpp
+++ lib/CodeGen/CGDebugInfo.cpp
@@ -1159,9 +1159,16 @@
     // FIXME: Add proper support for debug info for virtual calls in
     // the Microsoft ABI, where we may use multiple vptrs to make a vftable
     // lookup if we have multiple or virtual inheritance.
+    // FIXME: Add support for debug info for virtual calls in the relative
+    // ABI, where the virtual function address needs to be calculated from
+    // the virtual table address.
     if (!isa<CXXDestructorDecl>(Method) &&
-        !CGM.getTarget().getCXXABI().isMicrosoft())
+        !CGM.getTarget().getCXXABI().isMicrosoft() &&
+        !Method->getParent()->isRelativeCXXABI())
       VIndex = CGM.getItaniumVTableContext().getMethodVTableIndex(Method);
+    else
+      // -1u indicates that the vtable index is non-representable.
+      VIndex = -1u;
     ContainingType = RecordTy;
   }
 
Index: lib/CodeGen/CGClass.cpp
===================================================================
--- lib/CodeGen/CGClass.cpp
+++ lib/CodeGen/CGClass.cpp
@@ -2450,6 +2450,24 @@
   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.
@@ -2489,7 +2507,7 @@
                                              llvm::Value *VTable,
                                              SourceLocation Loc) {
   if (CGM.getCodeGenOpts().WholeProgramVTables &&
-      CGM.HasHiddenLTOVisibility(RD)) {
+      RD->hasHiddenLTOVisibility()) {
     llvm::Metadata *MD =
         CGM.CreateMetadataIdentifierForType(QualType(RD->getTypeForDecl(), 0));
     llvm::Value *BitSetName =
@@ -2566,7 +2584,7 @@
                                          CFITypeCheckKind TCK,
                                          SourceLocation Loc) {
   if (!CGM.getCodeGenOpts().SanitizeCfiCrossDso &&
-      !CGM.HasHiddenLTOVisibility(RD))
+      !RD->hasHiddenLTOVisibility())
     return;
 
   std::string TypeName = RD->getQualifiedNameAsString();
Index: lib/AST/DeclCXX.cpp
===================================================================
--- lib/AST/DeclCXX.cpp
+++ lib/AST/DeclCXX.cpp
@@ -20,6 +20,7 @@
 #include "clang/AST/ExprCXX.h"
 #include "clang/AST/TypeLoc.h"
 #include "clang/Basic/IdentifierTable.h"
+#include "clang/Basic/TargetInfo.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallPtrSet.h"
 using namespace clang;
@@ -71,8 +72,8 @@
       ImplicitCopyAssignmentHasConstParam(true),
       HasDeclaredCopyConstructorWithConstParam(false),
       HasDeclaredCopyAssignmentWithConstParam(false), IsLambda(false),
-      IsParsingBaseSpecifiers(false), NumBases(0), NumVBases(0), Bases(),
-      VBases(), Definition(D), FirstFriend() {}
+      IsParsingBaseSpecifiers(false), IsRelativeCXXABI(false), NumBases(0),
+      NumVBases(0), Bases(), VBases(), Definition(D), FirstFriend() {}
 
 CXXBaseSpecifier *CXXRecordDecl::DefinitionData::getBasesSlowCase() const {
   return Bases.get(Definition->getASTContext().getExternalSource());
@@ -1443,6 +1444,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 CXXMethodDecl::anchor() { }
 
 bool CXXMethodDecl::isStatic() const {
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -9482,6 +9482,12 @@
   // Emitting members of dllexported classes is delayed until the class
   // (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);
 };
 
 /// \brief RAII object that enters a new expression evaluation context.
Index: include/clang/Frontend/CodeGenOptions.def
===================================================================
--- include/clang/Frontend/CodeGenOptions.def
+++ include/clang/Frontend/CodeGenOptions.def
@@ -190,10 +190,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: include/clang/Driver/Options.td
===================================================================
--- include/clang/Driver/Options.td
+++ include/clang/Driver/Options.td
@@ -756,6 +756,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 fthinlto_index_EQ : Joined<["-"], "fthinlto-index=">,
   Flags<[CC1Option]>, Group<f_Group>,
   HelpText<"Perform ThinLTO importing using provided function summary index">;
Index: include/clang/Basic/LangOptions.def
===================================================================
--- include/clang/Basic/LangOptions.def
+++ include/clang/Basic/LangOptions.def
@@ -254,6 +254,14 @@
                                            "field padding (0: none, 1:least "
                                            "aggressive, 2: more aggressive)")
 
+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: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -8525,4 +8525,9 @@
   "'%0' qualifier on omitted return type %1 has no effect">,
   InGroup<IgnoredQualifiers>;
 
+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: include/clang/AST/DeclCXX.h
===================================================================
--- include/clang/AST/DeclCXX.h
+++ include/clang/AST/DeclCXX.h
@@ -457,6 +457,9 @@
     /// \brief Whether we are currently parsing base specifiers.
     unsigned IsParsingBaseSpecifiers : 1;
 
+    /// \brief Whether the class uses the relative C++ vtable ABI.
+    unsigned IsRelativeCXXABI : 1;
+
     /// \brief The number of base class specifiers in Bases.
     unsigned NumBases;
 
@@ -703,6 +706,11 @@
     return data().IsParsingBaseSpecifiers;
   }
 
+  void setIsRelativeCXXABI() { data().IsRelativeCXXABI = true; }
+  bool isRelativeCXXABI() const {
+    return data().IsRelativeCXXABI;
+  }
+
   /// \brief Sets the base classes of this struct or class.
   void setBases(CXXBaseSpecifier const * const *Bases, unsigned NumBases);
 
@@ -1715,6 +1723,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;
+
   static bool classof(const Decl *D) { return classofKind(D->getKind()); }
   static bool classofKind(Kind K) {
     return K >= firstCXXRecord && K <= lastCXXRecord;
Index: docs/UsersManual.rst
===================================================================
--- docs/UsersManual.rst
+++ docs/UsersManual.rst
@@ -1171,6 +1171,23 @@
         // value of -fmax-type-align.
       }
 
+**-flto-relative-c++-abi-vtables**
+
+   This flag controls whether the compiler uses a more space-efficient virtual
+   table ABI, known as the relative ABI. It enables the relative ABI for all
+   classes with :doc:`hidden LTO visibility <LTOVisibility>`. Requires `-flto`.
+
+   This ABI is not guaranteed to be stable between versions of Clang. This
+   means that it is an ODR violation to define a class with the relative
+   ABI in two translation units compiled with different versions of
+   Clang. Specifically, mixing different head revisions or major releases
+   is not allowed, but mixing different point releases is fine.
+
+   It is not guaranteed that all classes with hidden LTO visibility will
+   use the relative ABI. For example, if a base class uses the platform ABI
+   and declares virtual functions, that will force any derived classes to
+   use the platform ABI. The compiler will diagnose any cases where a class
+   derives from multiple incompatible base classes.
 
 Profile Guided Optimization
 ---------------------------
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to