pcc updated this revision to Diff 50998.
pcc added a comment.
- Update test to match committed debug info change
http://reviews.llvm.org/D18199
Files:
docs/UsersManual.rst
lib/CodeGen/CGDebugInfo.cpp
lib/CodeGen/CGVTables.cpp
lib/CodeGen/CGVTables.h
lib/CodeGen/CodeGenFunction.h
lib/CodeGen/ItaniumCXXABI.cpp
lib/CodeGen/MicrosoftCXXABI.cpp
test/CodeGenCXX/debug-info-virtual-fn-relative.cpp
test/CodeGenCXX/vtable-relative-abi.cpp
Index: test/CodeGenCXX/vtable-relative-abi.cpp
===================================================================
--- /dev/null
+++ test/CodeGenCXX/vtable-relative-abi.cpp
@@ -0,0 +1,123 @@
+// RUN: %clang_cc1 %s -triple x86_64-unknown-linux-gnu -funstable-c++-abi-classes -emit-llvm -o - | FileCheck --check-prefix=CHECK --check-prefix=CHECK-ITANIUM %s
+// RUN: %clang_cc1 %s -triple x86_64-unknown-windows-msvc -funstable-c++-abi-classes -emit-llvm -o - | FileCheck --check-prefix=CHECK --check-prefix=CHECK-MS %s
+
+// CHECK-ITANIUM: @_ZTV1S = 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 = 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 = 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 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 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 void @_Z5call1P1S
+// CHECK-MS: define void @"\01?call1@@YAXPEAUS@@@Z"
+void call1(S *s) {
+ // CHECK: [[VOFFSETPTR:%.*]] = getelementptr inbounds i32, i32* [[VTABLE:%.*]], i64 0
+ // CHECK: [[VOFFSET:%.*]] = load i32, i32* [[VOFFSETPTR]], align 4
+ // CHECK: [[VTABLEI8:%.*]] = bitcast i32* [[VTABLE]] to i8*
+ // CHECK: [[VFN:%.*]] = getelementptr i8, i8* [[VTABLEI8]], i32 [[VOFFSET]]
+ // CHECK: [[VFNCASTED:%.*]] = bitcast i8* [[VFN]] to void (
+ // CHECK: call void [[VFNCASTED]](
+ s->f1();
+}
+
+// CHECK-ITANIUM: define void @_Z5call2P1S
+// CHECK-MS: define void @"\01?call2@@YAXPEAUS@@@Z"
+void call2(S *s) {
+ // CHECK: [[VOFFSETPTR:%.*]] = getelementptr inbounds i32, i32* [[VTABLE:%.*]], i64 1
+ // CHECK: [[VOFFSET:%.*]] = load i32, i32* [[VOFFSETPTR]], align 4
+ // CHECK: [[VTABLEI8:%.*]] = bitcast i32* [[VTABLE]] to i8*
+ // CHECK: [[VFN:%.*]] = getelementptr i8, i8* [[VTABLEI8]], i32 [[VOFFSET]]
+ // 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* @"\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: [[VOFFSETPTR:%.*]] = getelementptr inbounds i32, i32* [[VTABLE:%.*]], i64 0
+// CHECK-MS: [[VOFFSET:%.*]] = load i32, i32* [[VOFFSETPTR]], align 4
+// CHECK-MS: [[VTABLEI8:%.*]] = bitcast i32* [[VTABLE]] to i8*
+// CHECK-MS: [[VFN:%.*]] = getelementptr i8, i8* [[VTABLEI8]], i32 [[VOFFSET]]
+// CHECK-MS: [[VFNCASTED:%.*]] = bitcast i8* [[VFN]] to void (
+// CHECK-MS: musttail call void {{.*}} [[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* @"\01?getmfp2@@YAP8S@@EAAXXZXZ"()
+// CHECK-MS: ret i8* bitcast {{.*}} @"\01??_9S@@$B7AA"
+// CHECK-MS: define linkonce_odr void @"\01??_9S@@$B7AA"
+// CHECK-MS: [[VOFFSETPTR:%.*]] = getelementptr inbounds i32, i32* [[VTABLE:%.*]], i64 1
+// CHECK-MS: [[VOFFSET:%.*]] = load i32, i32* [[VOFFSETPTR]], align 4
+// CHECK-MS: [[VTABLEI8:%.*]] = bitcast i32* [[VTABLE]] to i8*
+// CHECK-MS: [[VFN:%.*]] = getelementptr i8, i8* [[VTABLEI8]], i32 [[VOFFSET]]
+// CHECK-MS: [[VFNCASTED:%.*]] = bitcast i8* [[VFN]] to void (
+// CHECK-MS: musttail call void {{.*}} [[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: [[VOFFSETPTR:%.*]] = getelementptr i8, i8* [[VTABLE:%.*]], i64 [[VTOFFSET]]
+ // CHECK-ITANIUM: [[VOFFSETI32PTR:%.*]] = bitcast i8* [[VOFFSETPTR]] to i32*
+ // CHECK-ITANIUM: [[VOFFSET:%.*]] = load i32, i32* [[VOFFSETI32PTR]], align 4
+ // CHECK-ITANIUM: [[VFN:%.*]] = getelementptr i8, i8* [[VTABLE]], i32 [[VOFFSET]]
+ // CHECK-ITANIUM: bitcast i8* [[VFN]] to void (
+ (s->*p)();
+}
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 -emit-llvm -debug-info-kind=limited %s -o - | FileCheck --check-prefix=CHECK-STABLE %s
+// RUN: %clang_cc1 -funstable-c++-abi-classes -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: lib/CodeGen/MicrosoftCXXABI.cpp
===================================================================
--- lib/CodeGen/MicrosoftCXXABI.cpp
+++ lib/CodeGen/MicrosoftCXXABI.cpp
@@ -1572,12 +1572,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);
}
@@ -1696,16 +1694,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.
@@ -1726,8 +1722,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(
@@ -1737,6 +1733,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,
@@ -1808,24 +1805,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.NeedVTableBitSets())
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(
@@ -1941,12 +1936,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
@@ -589,13 +589,24 @@
llvm::Value *VTableOffset = FnAsInt;
if (!UseARMMethodPtrABI)
VTableOffset = Builder.CreateSub(VTableOffset, ptrdiff_1);
- VTable = Builder.CreateGEP(VTable, VTableOffset);
+ llvm::Value *VTableSlotPtr = 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 (CGM.getVTables().shouldUseRelativeABI(RD)) {
+ VTableSlotPtr =
+ Builder.CreateBitCast(VTableSlotPtr, CGF.Int32Ty->getPointerTo());
+ llvm::Value *VirtualFnOffset =
+ Builder.CreateAlignedLoad(VTableSlotPtr, 4, "memptr.vfnoffset");
+ llvm::Value *VTableInt8Ptr = Builder.CreateBitCast(VTable, CGF.Int8PtrTy);
+ VirtualFn = Builder.CreateGEP(VTableInt8Ptr, VirtualFnOffset);
+ VirtualFn = Builder.CreateBitCast(VirtualFn, FTy->getPointerTo());
+ } else {
+ 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
@@ -803,7 +814,11 @@
const ASTContext &Context = getContext();
CharUnits PointerWidth =
Context.toCharUnitsFromBits(Context.getTargetInfo().getPointerWidth(0));
- uint64_t VTableOffset = (Index * PointerWidth.getQuantity());
+ uint64_t VTableOffset = Index;
+ if (CGM.getVTables().shouldUseRelativeABI(MD->getParent()))
+ VTableOffset *= 4;
+ else
+ VTableOffset *= PointerWidth.getQuantity();
if (UseARMMethodPtrABI) {
// ARM C++ ABI 3.2.1:
@@ -1465,10 +1480,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);
@@ -1576,12 +1591,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>())
@@ -1598,16 +1613,35 @@
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 *CodeGenFunction::GetVirtualFunctionFromVTable(const CXXRecordDecl *RD,
+ llvm::Value *VTable,
+ uint64_t VTableIndex,
+ llvm::Type *Ty) {
+ if (!CGM.getVTables().shouldUseRelativeABI(RD)) {
+ 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, Int32Ty->getPointerTo());
+ llvm::Value *VTableSlotPtr =
+ Builder.CreateConstInBoundsGEP1_64(VTable, VTableIndex, "vfn");
+ llvm::Value *VTableSlot = Builder.CreateAlignedLoad(VTableSlotPtr, 4);
+ llvm::Value *VTableInt8Ptr = Builder.CreateBitCast(VTable, Int8PtrTy);
+ llvm::Value *FnPtr = Builder.CreateGEP(VTableInt8Ptr, VTableSlot);
+ return Builder.CreateBitCast(FnPtr, Ty->getPointerTo());
}
llvm::Value *ItaniumCXXABI::EmitVirtualDestructorCall(
Index: lib/CodeGen/CodeGenFunction.h
===================================================================
--- lib/CodeGen/CodeGenFunction.h
+++ lib/CodeGen/CodeGenFunction.h
@@ -1383,6 +1383,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,14 @@
void GenerateClassData(const CXXRecordDecl *RD);
bool isVTableExternal(const CXXRecordDecl *RD);
+
+ /// Whether to use the relative vtable ABI for this record.
+ bool shouldUseRelativeABI(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
@@ -523,24 +523,73 @@
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) {
+bool CodeGenVTables::shouldUseRelativeABI(const CXXRecordDecl *RD) {
+ return RD->isUnstableCXXABI();
+}
+
+llvm::Type *CodeGenVTables::GetVTableType(const CXXRecordDecl *RD,
+ const VTableLayout &VTLayout) {
+ if (!shouldUseRelativeABI(RD))
+ 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 = shouldUseRelativeABI(RD);
+ 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()) {
@@ -594,7 +643,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.
@@ -612,7 +661,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 =
@@ -625,7 +674,7 @@
DeletedVirtualFn = llvm::ConstantExpr::getBitCast(DeletedVirtualFn,
CGM.Int8PtrTy);
}
- Init = DeletedVirtualFn;
+ Init = maybeMakeRelative(DeletedVirtualFn);
} else {
// Check if we should use a thunk.
if (NextVTableThunkIndex < NumVTableThunks &&
@@ -643,20 +692,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 *
@@ -683,8 +738,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
@@ -696,7 +750,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.
@@ -706,12 +760,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;
Index: lib/CodeGen/CGDebugInfo.cpp
===================================================================
--- lib/CodeGen/CGDebugInfo.cpp
+++ lib/CodeGen/CGDebugInfo.cpp
@@ -1150,7 +1150,7 @@
// Collect virtual method info.
llvm::DIType *ContainingType = nullptr;
unsigned Virtuality = 0;
- unsigned VIndex = 0;
+ unsigned VIndex = -1u;
if (Method->isVirtual()) {
if (Method->isPure())
@@ -1163,8 +1163,12 @@
// 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() &&
+ !CGM.getVTables().shouldUseRelativeABI(Method->getParent()))
VIndex = CGM.getItaniumVTableContext().getMethodVTableIndex(Method);
ContainingType = RecordTy;
}
Index: docs/UsersManual.rst
===================================================================
--- docs/UsersManual.rst
+++ docs/UsersManual.rst
@@ -1165,6 +1165,52 @@
// value of -fmax-type-align.
}
+**-funstable-c++-abi-classes**
+
+**-funstable-c++-abi-classes=PATH**
+
+ These flags control whether the compiler uses a more space-efficient
+ virtual table ABI, known as the relative ABI. The first flag enables the
+ ABI for all classes in the program, while the second flag enables the
+ ABI for classes in namespaces named in the file at ``PATH``.
+
+ 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.
+
+ The format of the namespace list file is a series of lines ending in
+ either ``*`` or ``**``. Preceding that is a namespace specifier delimited
+ by double-colons followed by ``::``, or the empty string to denote the
+ global namespace. ``#`` serves as a comment character. Each entry in the
+ list indicates that classes in that namespace, including nested classes,
+ classes defined in enclosed anonymous namespaces, and classes defined
+ within member functions of those classes, may use the relative ABI.
+
+ If the line ends in ``*`` this applies to the given namespace only,
+ while if the line ends in ``**`` it applies to the given namespace and
+ any enclosed namespaces.
+
+ It is not guaranteed that all such classes 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.
+
+ Example:
+
+ .. code-block:: console
+
+ # Enables the unstable ABI for classes in the global namespace.
+ *
+
+ # Enables the unstable ABI for classes in the base namespace, and
+ # any enclosed namespaces.
+ base::**
+
+ Note that it is recommended to use the ``[[clang::unstable_abi]]``
+ attribute instead of these flags wherever possible.
Profile Guided Optimization
---------------------------
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits