leonardchan updated this revision to Diff 196180.
leonardchan added a subscriber: mcgrathr.
leonardchan added a comment.
Herald added a subscriber: hiraditya.

Uploading my latest version of this patch and reporting some results. Will 
proceed with formatting and cleanup and ask for official reviews later.

___

After toying around with this for a bit, just enabling this flag on all c++ 
code in zircon shows about a 1% size increase in the final image (x64.zbi), but 
does its job of moving many vtables from .data.rel.ro to rodata. For each 
shared library that's included in the image, some of them decrease while most 
increase in size. These changes though are pretty small and rounded up align to 
page sizes (4kB), resulting in the overall 1% increase. This is mostly from 
extra instructions and extra PLT entries, through we have an idea for how we 
could reduce the PLT size.

We still get the benefits of moving vtables into rodata and having them shared 
between processes. Below is the section dump for one of our shared libraries 
(omitting the debug sections):

Without relative ABI

  Section Headers:
    [Nr] Name              Type            Address          Off    Size   ES 
Flg Lk Inf Al
    [ 0]                   NULL            0000000000000000 000000 000000 00    
  0   0  0
    [ 1] .note.gnu.build-id NOTE            0000000000000270 000270 000018 00   
A  0   0  4
    [ 2] .dynsym           DYNSYM          0000000000000288 000288 000540 18   
A  5   1  8
    [ 3] .gnu.hash         GNU_HASH        00000000000007c8 0007c8 000050 00   
A  2   0  8
    [ 4] .dynamic          DYNAMIC         0000000000000818 000818 000170 10   
A  5   0  8
    [ 5] .dynstr           STRTAB          0000000000000988 000988 000424 00   
A  0   0  1
    [ 6] .rela.dyn         RELA            0000000000000db0 000db0 0000a8 18   
A  2   0  8  # missing
    [ 7] .relr.dyn         00000013: <unknown> 0000000000000e58 000e58 000080 
08   A  0   0  8
    [ 8] .rela.plt         RELA            0000000000000ed8 000ed8 000468 18   
A  2  15  8
    [ 9] .rodata           PROGBITS        0000000000001340 001340 0025c8 00 
AMS  0   0 16
    [10] .eh_frame_hdr     PROGBITS        0000000000003908 003908 000a1c 00   
A  0   0  4
    [11] .eh_frame         PROGBITS        0000000000004328 004328 0032cc 00   
A  0   0  8
    [12] .text             PROGBITS        0000000000008000 008000 014c1b 00  
AX  0   0 16
    [13] .plt              PROGBITS        000000000001cc20 01cc20 000300 00  
AX  0   0 16
    [14] .data.rel.ro      PROGBITS        000000000001d000 01d000 001948 00  
WA  0   0 16
    [15] .got.plt          PROGBITS        000000000001e948 01e948 000190 00  
WA  0   0  8

With relative ABI

  Section Headers:
    [Nr] Name              Type            Address          Off    Size   ES 
Flg Lk Inf Al
    [ 0]                   NULL            0000000000000000 000000 000000 00    
  0   0  0
    [ 1] .note.gnu.build-id NOTE            0000000000000270 000270 000018 00   
A  0   0  4
    [ 2] .dynsym           DYNSYM          0000000000000288 000288 000540 18   
A  5   1  8
    [ 3] .gnu.hash         GNU_HASH        00000000000007c8 0007c8 000050 00   
A  2   0  8
    [ 4] .dynamic          DYNAMIC         0000000000000818 000818 000140 10   
A  5   0  8
    [ 5] .dynstr           STRTAB          0000000000000958 000958 000424 00   
A  0   0  1
    [ 6] .relr.dyn         00000013: <unknown> 0000000000000d80 000d80 000068 
08   A  0   0  8
    [ 7] .rela.plt         RELA            0000000000000de8 000de8 000480 18   
A  2  14  8
    [ 8] .rodata           PROGBITS        0000000000001270 001270 002990 00 
AMS  0   0 16
    [ 9] .eh_frame_hdr     PROGBITS        0000000000003c00 003c00 000a1c 00   
A  0   0  4
    [10] .eh_frame         PROGBITS        0000000000004620 004620 0032cc 00   
A  0   0  8
    [11] .text             PROGBITS        0000000000008000 008000 014ffb 00  
AX  0   0 16
    [12] .plt              PROGBITS        000000000001d000 01d000 000310 00  
AX  0   0 16
    [13] .data.rel.ro      PROGBITS        000000000001e000 01e000 0012d0 00  
WA  0   0 16
    [14] .got.plt          PROGBITS        000000000001f2d0 01f2d0 000198 00  
WA  0   0  8
    [15] .data             PROGBITS        0000000000020000 020000 000038 00  
WA  0   0 16
    [16] .bss              NOBITS          0000000000020038 020038 000100 00  
WA  0   0  8

We were also able to build libc++ with relative ABI and got ~2% decrease in 
size for the final shared library.

Other stuff in this patch:

- Remove any LTO related code
- Ensure `needsRelocation()` catches the relative offset function pointers


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/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/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/vtable-relative-abi.cpp
  llvm/lib/IR/Constants.cpp

Index: llvm/lib/IR/Constants.cpp
===================================================================
--- llvm/lib/IR/Constants.cpp
+++ llvm/lib/IR/Constants.cpp
@@ -495,6 +495,34 @@
   return false;
 }
 
+/// Returns true if this constant mathes the relative referencing for virtual
+/// functions introduced in the relative ABI. This is the difference between a
+/// function and its corresponding vtable entry.
+///
+/// i64 sub (
+///   i64 ptrtoint (i8* obj to i64),
+//    i64 ptrtoint (
+//      i32* getelementptr inbounds (%object_vtable, %object_vtable* @_ZTV12derived_virt, i32 0, i32 0, i32 2) to i64)
+static bool MatchesRelativeOffset(const Constant *C) {
+  const auto *CE = dyn_cast<ConstantExpr>(C);
+  if (!CE || CE->getOpcode() != Instruction::Sub)
+    return false;
+
+  const auto *LHS = dyn_cast<ConstantExpr>(CE->getOperand(0));
+  const auto *RHS = dyn_cast<ConstantExpr>(CE->getOperand(1));
+  if (!LHS || !RHS || LHS->getOpcode() != Instruction::PtrToInt ||
+      RHS->getOpcode() != Instruction::PtrToInt)
+    return false;
+
+  RHS = dyn_cast<ConstantExpr>(RHS->getOperand(0));
+  if (!RHS)
+    return false;
+
+  return (isa<GlobalValue>(LHS->getOperand(0)) &&
+          RHS->getOpcode() == Instruction::GetElementPtr &&
+          isa<GlobalValue>(RHS->getOperand(0)));
+}
+
 bool Constant::needsRelocation() const {
   if (isa<GlobalValue>(this))
     return true; // Global reference.
@@ -519,6 +547,10 @@
         return false;
     }
 
+  // This results in an R_PLT_PC reloc which can be computed at link time.
+  if (MatchesRelativeOffset(this))
+    return false;
+
   bool Result = false;
   for (unsigned i = 0, e = getNumOperands(); i != e; ++i)
     Result |= cast<Constant>(getOperand(i))->needsRelocation();
Index: clang/test/CodeGenCXX/vtable-relative-abi.cpp
===================================================================
--- /dev/null
+++ clang/test/CodeGenCXX/vtable-relative-abi.cpp
@@ -0,0 +1,117 @@
+// RUN: %clang_cc1 %s -triple x86_64-unknown-linux-gnu -frelative-c++-abi-vtables -emit-llvm -o - | FileCheck --check-prefix=CHECK --check-prefix=CHECK-ITANIUM %s
+// RUN: %clang_cc1 %s -triple x86_64-unknown-windows-msvc -frelative-c++-abi-vtables -emit-llvm -o - | FileCheck --check-prefix=CHECK --check-prefix=CHECK-MS %s
+
+// CHECK-ITANIUM: @_ZTV1S = 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 2) to i64)) to i32) } }, align 8
+// CHECK-MS: @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 } }* @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 } }* @0, i32 0, i32 0, i32 1) to i64)) to i32) } }, comdat($"??_7S@@6B@")
+
+struct S {
+  S();
+  virtual void f1();
+  virtual void f2();
+};
+
+// CHECK-ITANIUM: @_ZTV1T = 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: @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 } }* @1, i32 0, i32 0, i32 1) to i64)) to i32) } }, comdat($"??_7T@@6B@")
+struct T {
+  T();
+  virtual void g();
+};
+
+// CHECK-ITANIUM: @_ZTV1U = 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 2) 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: @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 } }* @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 } }* @2, i32 0, i32 0, i32 1) to i64)) to i32) } }, comdat($"??_7U@@6BS@@@")
+// CHECK-MS: @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 } }* @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 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 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 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 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 { 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 { 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 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)();
+}
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 -emit-llvm -debug-info-kind=limited %s -o - | FileCheck --check-prefix=CHECK-STABLE %s
+// RUN: %clang_cc1 -frelative-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
@@ -5888,6 +5888,13 @@
   }
 }
 
+void Sema::checkClassABI(CXXRecordDecl *Record) {
+  if (!getLangOpts().RelativeCXXABIVTables)
+    return;
+
+  Record->setIsRelativeCXXABI();
+}
+
 /// Determine whether a type is permitted to be passed or returned in
 /// registers, per C++ [class.temporary]p3.
 static bool canPassInRegisters(Sema &S, CXXRecordDecl *D,
@@ -6167,6 +6174,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
@@ -3040,6 +3040,8 @@
 
   Opts.CompleteMemberPointers = Args.hasArg(OPT_fcomplete_member_pointers);
   Opts.BuildingPCHWithObjectFile = Args.hasArg(OPT_building_pch_with_obj);
+
+  Opts.RelativeCXXABIVTables = Args.hasArg(OPT_frelative_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
@@ -5305,6 +5305,10 @@
     CmdArgs.push_back("-fwhole-program-vtables");
   }
 
+  // Add unstable C++ ABI flags.
+  if (Args.hasArg(options::OPT_frelative_cxx_abi_vtables))
+    CmdArgs.push_back("-frelative-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
@@ -1645,10 +1645,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);
   }
@@ -1774,7 +1771,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.
@@ -1807,6 +1804,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,
@@ -1834,14 +1832,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);
@@ -1867,9 +1863,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);
@@ -1990,10 +1985,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
@@ -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/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,26 @@
                                  const ThunkInfo &ThunkAdjustments,
                                  bool ForVTable);
 
-  void addVTableComponent(ConstantArrayBuilder &builder,
-                          const VTableLayout &layout, unsigned idx,
-                          llvm::Constant *rtti,
-                          unsigned &nextVTableThunkIndex);
+  /// 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,
+                                     unsigned lastAddrPoint);
+
+  /// 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 +132,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 lastAddrPoint) 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, lastAddrPoint)});
+
+  llvm::Constant *AddrPointInt =
+      llvm::ConstantExpr::getPtrToInt(gep, PtrDiffTy);
+  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, unsigned lastAddrPoint) {
+  auto &component = layout.getVTableComponent(componentIdx);
+  unsigned thisIndex = layout.getVTableOffset(vtableIdx);
+
+  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,98 @@
     }
 
     fnPtr = llvm::ConstantExpr::getBitCast(fnPtr, CGM.Int8PtrTy);
-    builder.add(fnPtr);
-    return;
+    if (RelativeABI && component.isFunctionPointerKind())
+      return makeRelative(fnPtr, VTable, vtableIdx, lastAddrPoint - 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) {
-  SmallVector<llvm::Type *, 4> tys;
+llvm::Type *CodeGenVTables::getVTableType(const CXXRecordDecl *RD,
+                                          const VTableLayout &layout) {
+  SmallVector<llvm::Type *, 4> types;
   for (unsigned i = 0, e = layout.getNumVTables(); i != e; ++i) {
-    tys.push_back(llvm::ArrayType::get(CGM.Int8PtrTy, layout.getVTableSize(i)));
+    if (!RD->isRelativeCXXABI()) {
+      types.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);
+      }
+      types.push_back(llvm::StructType::get(CGM.getLLVMContext(), innerTypes));
+    }
   }
 
-  return llvm::StructType::get(CGM.getLLVMContext(), tys);
+  return llvm::StructType::get(CGM.getLLVMContext(), types);
 }
 
-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);
+
+    // We want to point to the first function in the vtable. This would
+    // normally be the 3rd component under Itanium ABI (2nd for Microsoft),
+    // but not in the case of virtual inheritance. For both of these ABIs, the
+    // first virtual function comes after the RTTI component, so we can just
+    // check if the previous component was RTTI.
+    //
+    // FIXME: Need a better way of identifying address points that works with
+    // the Itanium and MS ABIs.
+    unsigned lastAddrPoint = 0;
+
+    if (!RelativeABI) {
+      // Construct a normal array of pointers.
+      auto vtableElem = components.beginArray(CGM.Int8PtrTy);
+      for (unsigned componentIdx = thisIndex; componentIdx != nextIndex;
+           ++componentIdx) {
+        if (componentIdx > 0 && VTLayout.getVTableComponent(componentIdx-1).isRTTIKind())
+          lastAddrPoint = componentIdx;
+        vtableElem.add(getVTableComponent(VTLayout, componentIdx, vtableIdx, RTTI,
+                                          nextVTableThunkIndex, RelativeABI, VTable,
+                                          lastAddrPoint));
+      }
+      vtableElem.finishAndAddTo(components);
+    } else {
+      // Instead use an i32 to indicate an offset.
+      SmallVector<llvm::Constant *, 64> Inits;
+      for (unsigned componentIdx = thisIndex; componentIdx != nextIndex;
+           ++componentIdx) {
+        if (componentIdx > 0 && VTLayout.getVTableComponent(componentIdx-1).isRTTIKind())
+          lastAddrPoint = componentIdx;
+        Inits.push_back(getVTableComponent(VTLayout, componentIdx, vtableIdx,
+                                           RTTI, nextVTableThunkIndex,
+                                           RelativeABI, VTable, lastAddrPoint));
+      }
+      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 +836,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 +859,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.
@@ -1053,8 +1140,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
@@ -2581,6 +2581,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.
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());
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -10938,6 +10938,9 @@
   // (including field initializers) is fully parsed.
   SmallVector<CXXRecordDecl*, 4> DelayedDllExportClasses;
 
+  /// Determine if this class can use the relative vtable ABI.
+  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,10 @@
   "fno-fine-grained-bitfield-accesses">, Group<f_clang_Group>, Flags<[CC1Option]>,
   HelpText<"Use large-integer access for consecutive bitfield runs.">;
 
+def frelative_cxx_abi_vtables : Flag<["-"], "frelative-c++-abi-vtables">,
+  Group<f_Group>, Flags<[CC1Option]>,
+  HelpText<"Use the unstable C++ class ABI for classes with virtual tables">;
+
 def flat__namespace : Flag<["-"], "flat_namespace">;
 def flax_vector_conversions : Flag<["-"], "flax-vector-conversions">, Group<f_Group>;
 def flimited_precision_EQ : Joined<["-"], "flimited-precision=">, Group<f_Group>;
Index: clang/include/clang/Basic/LangOptions.def
===================================================================
--- clang/include/clang/Basic/LangOptions.def
+++ clang/include/clang/Basic/LangOptions.def
@@ -323,6 +323,10 @@
 
 LANGOPT(RegisterStaticDestructors, 1, 1, "Register C++ static destructors")
 
+LANGOPT(RelativeCXXABIVTables, 1, 0,
+        "Whether to use clang's relative C++ ABI "
+        "for classes with vtables")
+
 #undef LANGOPT
 #undef COMPATIBLE_LANGOPT
 #undef BENIGN_LANGOPT
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.
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to