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