Skip to content

Commit 4074112

Browse files
htyulanza
authored andcommitted
[CIR][Codegen] VTT support for virtual class inheritance (#252)
This patch brings up the basic support for C++ virtual inheritance. VTT (virtual table table) now can be laid out as expected for simple program with single virtual inheritance. RTTI support is on the way. This patch does not include LLVM lowering support.
1 parent 952e4cb commit 4074112

File tree

8 files changed

+215
-32
lines changed

8 files changed

+215
-32
lines changed

clang/lib/CIR/CodeGen/CIRGenCXXABI.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,11 @@ class CIRGenCXXABI {
188188
/// Emits the VTable definitions required for the given record type.
189189
virtual void emitVTableDefinitions(CIRGenVTables &CGVT,
190190
const CXXRecordDecl *RD) = 0;
191+
192+
/// Emit any tables needed to implement virtual inheritance. For Itanium,
193+
/// this emits virtual table tables.
194+
virtual void emitVirtualInheritanceTables(const CXXRecordDecl *RD) = 0;
195+
191196
virtual mlir::Attribute getAddrOfRTTIDescriptor(mlir::Location loc,
192197
QualType Ty) = 0;
193198
virtual CatchTypeInfo
@@ -280,6 +285,12 @@ class CIRGenCXXABI {
280285
// directly or require access through a thread wrapper function.
281286
virtual bool usesThreadWrapperFunction(const VarDecl *VD) const = 0;
282287

288+
/// Emit the code to initialize hidden members required to handle virtual
289+
/// inheritance, if needed by the ABI.
290+
virtual void
291+
initializeHiddenVirtualInheritanceMembers(CIRGenFunction &CGF,
292+
const CXXRecordDecl *RD) {}
293+
283294
/// Emit a single constructor/destructor with the gien type from a C++
284295
/// constructor Decl.
285296
virtual void buildCXXStructor(clang::GlobalDecl GD) = 0;

clang/lib/CIR/CodeGen/CIRGenClass.cpp

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,8 @@ bool CIRGenFunction::IsConstructorDelegationValid(
4040
// };
4141
// ...although even this example could in principle be emitted as a delegation
4242
// since the address of the parameter doesn't escape.
43-
if (Ctor->getParent()->getNumVBases()) {
44-
llvm_unreachable("NYI");
45-
}
43+
if (Ctor->getParent()->getNumVBases())
44+
return false;
4645

4746
// We also disable the optimization for variadic functions because it's
4847
// impossible to "re-pass" varargs.
@@ -236,7 +235,7 @@ static void buildMemberInitializer(CIRGenFunction &CGF,
236235
if (CGF.CurGD.getCtorType() == Ctor_Base)
237236
LHS = CGF.MakeNaturalAlignPointeeAddrLValue(ThisPtr, RecordTy);
238237
else
239-
llvm_unreachable("NYI");
238+
LHS = CGF.MakeNaturalAlignAddrLValue(ThisPtr, RecordTy);
240239

241240
buildLValueForAnyFieldInitialization(CGF, MemberInit, LHS);
242241

@@ -524,9 +523,10 @@ Address CIRGenFunction::getAddressOfDirectBaseInCompleteClass(
524523
// TODO: for complete types, this should be possible with a GEP.
525524
Address V = This;
526525
if (!Offset.isZero()) {
527-
// TODO(cir): probably create a new operation to account for
528-
// down casting when the offset isn't zero.
529-
llvm_unreachable("NYI");
526+
mlir::Value OffsetVal = builder.getSInt32(Offset.getQuantity(), loc);
527+
mlir::Value VBaseThisPtr = builder.create<mlir::cir::PtrStrideOp>(
528+
loc, This.getPointer().getType(), This.getPointer(), OffsetVal);
529+
V = Address(VBaseThisPtr, CXXABIThisAlignment);
530530
}
531531
V = builder.createElementBitCast(loc, V, ConvertType(Base));
532532
return V;
@@ -605,7 +605,11 @@ void CIRGenFunction::buildCtorPrologue(const CXXConstructorDecl *CD,
605605
for (; B != E && (*B)->isBaseInitializer() && (*B)->isBaseVirtual(); B++) {
606606
if (!ConstructVBases)
607607
continue;
608-
llvm_unreachable("NYI");
608+
if (CGM.getCodeGenOpts().StrictVTablePointers &&
609+
CGM.getCodeGenOpts().OptimizationLevel > 0 &&
610+
isInitializerOfDynamicClass(*B))
611+
llvm_unreachable("NYI");
612+
buildBaseInitializer(getLoc(CD->getBeginLoc()), *this, ClassDecl, *B);
609613
}
610614

611615
if (BaseCtorContinueBB) {
@@ -699,7 +703,7 @@ void CIRGenFunction::initializeVTablePointers(mlir::Location loc,
699703
initializeVTablePointer(loc, Vptr);
700704

701705
if (RD->getNumVBases())
702-
llvm_unreachable("NYI");
706+
CGM.getCXXABI().initializeHiddenVirtualInheritanceMembers(*this, RD);
703707
}
704708

705709
CIRGenFunction::VPtrsVector

clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -356,8 +356,10 @@ void CIRGenFunction::buildCXXConstructExpr(const CXXConstructExpr *E,
356356
break;
357357
case CXXConstructionKind::Delegating:
358358
llvm_unreachable("NYI");
359+
break;
359360
case CXXConstructionKind::VirtualBase:
360-
llvm_unreachable("NYI");
361+
ForVirtualBase = true;
362+
[[fallthrough]];
361363
case CXXConstructionKind::NonVirtualBase:
362364
Type = Ctor_Base;
363365
break;

clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ class CIRGenItaniumCXXABI : public cir::CIRGenCXXABI {
197197
const CXXRecordDecl *NearestVBase) override;
198198
void emitVTableDefinitions(CIRGenVTables &CGVT,
199199
const CXXRecordDecl *RD) override;
200+
void emitVirtualInheritanceTables(const CXXRecordDecl *RD) override;
200201
mlir::Attribute getAddrOfRTTIDescriptor(mlir::Location loc,
201202
QualType Ty) override;
202203
bool useThunkForDtorVariant(const CXXDestructorDecl *Dtor,
@@ -821,10 +822,10 @@ class CIRGenItaniumRTTIBuilder {
821822
/// to the Itanium C++ ABI, 2.9.5p6b.
822823
void BuildSIClassTypeInfo(mlir::Location loc, const CXXRecordDecl *RD);
823824

824-
// /// Build an abi::__vmi_class_type_info, used for
825-
// /// classes with bases that do not satisfy the abi::__si_class_type_info
826-
// /// constraints, according ti the Itanium C++ ABI, 2.9.5p5c.
827-
// void BuildVMIClassTypeInfo(const CXXRecordDecl *RD);
825+
/// Build an abi::__vmi_class_type_info, used for
826+
/// classes with bases that do not satisfy the abi::__si_class_type_info
827+
/// constraints, according ti the Itanium C++ ABI, 2.9.5p5c.
828+
void BuildVMIClassTypeInfo(const CXXRecordDecl *RD);
828829

829830
// /// Build an abi::__pointer_type_info struct, used
830831
// /// for pointer types.
@@ -1436,6 +1437,10 @@ void CIRGenItaniumRTTIBuilder::BuildSIClassTypeInfo(mlir::Location loc,
14361437
Fields.push_back(BaseTypeInfo);
14371438
}
14381439

1440+
void CIRGenItaniumRTTIBuilder::BuildVMIClassTypeInfo(const CXXRecordDecl *RD) {
1441+
// TODO: Implement this function.
1442+
}
1443+
14391444
mlir::Attribute
14401445
CIRGenItaniumRTTIBuilder::GetAddrOfExternalRTTIDescriptor(mlir::Location loc,
14411446
QualType Ty) {
@@ -1560,8 +1565,7 @@ mlir::Attribute CIRGenItaniumRTTIBuilder::BuildTypeInfo(
15601565
if (CanUseSingleInheritance(RD)) {
15611566
BuildSIClassTypeInfo(loc, RD);
15621567
} else {
1563-
llvm_unreachable("NYI");
1564-
// BuildVMIClassTypeInfo(RD);
1568+
BuildVMIClassTypeInfo(RD);
15651569
}
15661570

15671571
break;
@@ -1730,6 +1734,13 @@ void CIRGenItaniumCXXABI::emitVTableDefinitions(CIRGenVTables &CGVT,
17301734
llvm_unreachable("NYI");
17311735
}
17321736

1737+
void CIRGenItaniumCXXABI::emitVirtualInheritanceTables(
1738+
const CXXRecordDecl *RD) {
1739+
CIRGenVTables &VTables = CGM.getVTables();
1740+
auto VTT = VTables.getAddrOfVTT(RD);
1741+
VTables.buildVTTDefinition(VTT, CGM.getVTableLinkage(RD), RD);
1742+
}
1743+
17331744
/// What sort of uniqueness rules should we use for the RTTI for the
17341745
/// given type?
17351746
CIRGenItaniumCXXABI::RTTIUniquenessKind
@@ -1842,4 +1853,4 @@ void CIRGenItaniumCXXABI::buildThrow(CIRGenFunction &CGF,
18421853
// Now throw the exception.
18431854
builder.create<mlir::cir::ThrowOp>(CGF.getLoc(E->getSourceRange()),
18441855
exceptionPtr, typeInfo.getSymbol(), dtor);
1845-
}
1856+
}

clang/lib/CIR/CodeGen/CIRGenVTables.cpp

Lines changed: 119 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,17 @@
1313
#include "CIRGenCXXABI.h"
1414
#include "CIRGenFunction.h"
1515
#include "CIRGenModule.h"
16+
#include "mlir/IR/Attributes.h"
1617
#include "clang/AST/Attr.h"
1718
#include "clang/AST/CXXInheritance.h"
1819
#include "clang/AST/RecordLayout.h"
20+
#include "clang/AST/VTTBuilder.h"
1921
#include "clang/Basic/CodeGenOptions.h"
2022
#include "clang/CIR/Dialect/IR/CIRAttrs.h"
2123
#include "clang/CIR/Dialect/IR/CIRTypes.h"
2224
#include "clang/CodeGen/CGFunctionInfo.h"
2325
#include "clang/CodeGen/ConstantInitBuilder.h"
26+
#include "llvm/Support/ErrorHandling.h"
2427
#include "llvm/Support/Format.h"
2528
#include "llvm/Transforms/Utils/Cloning.h"
2629
#include <algorithm>
@@ -151,17 +154,17 @@ void CIRGenVTables::GenerateClassData(const CXXRecordDecl *RD) {
151154
assert(!UnimplementedFeature::generateDebugInfo());
152155

153156
if (RD->getNumVBases())
154-
llvm_unreachable("NYI");
157+
CGM.getCXXABI().emitVirtualInheritanceTables(RD);
155158

156159
CGM.getCXXABI().emitVTableDefinitions(*this, RD);
157160
}
158161

159162
static void AddPointerLayoutOffset(CIRGenModule &CGM,
160163
ConstantArrayBuilder &builder,
161164
CharUnits offset) {
162-
assert(offset.getQuantity() == 0 && "NYI");
163-
builder.add(mlir::cir::ConstPtrAttr::get(
164-
CGM.getBuilder().getContext(), CGM.getBuilder().getUInt8PtrTy(), 0));
165+
builder.add(mlir::cir::ConstPtrAttr::get(CGM.getBuilder().getContext(),
166+
CGM.getBuilder().getUInt8PtrTy(),
167+
offset.getQuantity()));
165168
}
166169

167170
static void AddRelativeLayoutOffset(CIRGenModule &CGM,
@@ -415,6 +418,118 @@ CIRGenModule::getVTableLinkage(const CXXRecordDecl *RD) {
415418
llvm_unreachable("Invalid TemplateSpecializationKind!");
416419
}
417420

421+
mlir::cir::GlobalOp
422+
getAddrOfVTTVTable(CIRGenVTables &CGVT, CIRGenModule &CGM,
423+
const CXXRecordDecl *MostDerivedClass,
424+
const VTTVTable &vtable,
425+
mlir::cir::GlobalLinkageKind linkage,
426+
VTableLayout::AddressPointsMapTy &addressPoints) {
427+
if (vtable.getBase() == MostDerivedClass) {
428+
assert(vtable.getBaseOffset().isZero() &&
429+
"Most derived class vtable must have a zero offset!");
430+
// This is a regular vtable.
431+
return CGM.getCXXABI().getAddrOfVTable(MostDerivedClass, CharUnits());
432+
}
433+
434+
llvm_unreachable("generateConstructionVTable NYI");
435+
}
436+
437+
mlir::cir::GlobalOp CIRGenVTables::getAddrOfVTT(const CXXRecordDecl *RD)
438+
{
439+
assert(RD->getNumVBases() && "Only classes with virtual bases need a VTT");
440+
441+
SmallString<256> OutName;
442+
llvm::raw_svector_ostream Out(OutName);
443+
cast<ItaniumMangleContext>(CGM.getCXXABI().getMangleContext())
444+
.mangleCXXVTT(RD, Out);
445+
StringRef Name = OutName.str();
446+
447+
// This will also defer the definition of the VTT.
448+
(void)CGM.getCXXABI().getAddrOfVTable(RD, CharUnits());
449+
450+
VTTBuilder Builder(CGM.getASTContext(), RD, /*GenerateDefinition=*/false);
451+
452+
auto ArrayType = mlir::cir::ArrayType::get(CGM.getBuilder().getContext(),
453+
CGM.getBuilder().getUInt8PtrTy(),
454+
Builder.getVTTComponents().size());
455+
auto Align =
456+
CGM.getDataLayout().getABITypeAlign(CGM.getBuilder().getUInt8PtrTy());
457+
auto VTT = CGM.createOrReplaceCXXRuntimeVariable(
458+
CGM.getLoc(RD->getSourceRange()), Name, ArrayType,
459+
mlir::cir::GlobalLinkageKind::ExternalLinkage,
460+
CharUnits::fromQuantity(Align));
461+
CGM.setGVProperties(VTT, RD);
462+
return VTT;
463+
}
464+
465+
/// Emit the definition of the given vtable.
466+
void CIRGenVTables::buildVTTDefinition(mlir::cir::GlobalOp VTT,
467+
mlir::cir::GlobalLinkageKind Linkage,
468+
const CXXRecordDecl *RD) {
469+
VTTBuilder Builder(CGM.getASTContext(), RD, /*GenerateDefinition=*/true);
470+
471+
auto ArrayType = mlir::cir::ArrayType::get(CGM.getBuilder().getContext(),
472+
CGM.getBuilder().getUInt8PtrTy(),
473+
Builder.getVTTComponents().size());
474+
475+
SmallVector<mlir::cir::GlobalOp, 8> VTables;
476+
SmallVector<VTableAddressPointsMapTy, 8> VTableAddressPoints;
477+
for (const VTTVTable *i = Builder.getVTTVTables().begin(),
478+
*e = Builder.getVTTVTables().end();
479+
i != e; ++i) {
480+
VTableAddressPoints.push_back(VTableAddressPointsMapTy());
481+
VTables.push_back(getAddrOfVTTVTable(*this, CGM, RD, *i, Linkage,
482+
VTableAddressPoints.back()));
483+
}
484+
485+
SmallVector<mlir::Attribute, 8> VTTComponents;
486+
for (const VTTComponent *i = Builder.getVTTComponents().begin(),
487+
*e = Builder.getVTTComponents().end();
488+
i != e; ++i) {
489+
const VTTVTable &VTTVT = Builder.getVTTVTables()[i->VTableIndex];
490+
mlir::cir::GlobalOp VTable = VTables[i->VTableIndex];
491+
VTableLayout::AddressPointLocation AddressPoint;
492+
if (VTTVT.getBase() == RD) {
493+
// Just get the address point for the regular vtable.
494+
AddressPoint =
495+
getItaniumVTableContext().getVTableLayout(RD).getAddressPoint(
496+
i->VTableBase);
497+
} else {
498+
AddressPoint = VTableAddressPoints[i->VTableIndex].lookup(i->VTableBase);
499+
assert(AddressPoint.AddressPointIndex != 0 &&
500+
"Did not find ctor vtable address point!");
501+
}
502+
503+
mlir::Attribute Idxs[3] = {
504+
CGM.getBuilder().getI32IntegerAttr(0),
505+
CGM.getBuilder().getI32IntegerAttr(AddressPoint.VTableIndex),
506+
CGM.getBuilder().getI32IntegerAttr(AddressPoint.AddressPointIndex),
507+
};
508+
509+
auto Init = mlir::cir::GlobalViewAttr::get(
510+
CGM.getBuilder().getUInt8PtrTy(),
511+
mlir::FlatSymbolRefAttr::get(VTable.getSymNameAttr()),
512+
mlir::ArrayAttr::get(CGM.getBuilder().getContext(), Idxs));
513+
514+
VTTComponents.push_back(Init);
515+
}
516+
517+
auto Init = CGM.getBuilder().getConstArray(
518+
mlir::ArrayAttr::get(CGM.getBuilder().getContext(), VTTComponents),
519+
ArrayType);
520+
521+
VTT.setInitialValueAttr(Init);
522+
523+
// Set the correct linkage.
524+
VTT.setLinkage(Linkage);
525+
mlir::SymbolTable::setSymbolVisibility(VTT,
526+
CIRGenModule::getMLIRVisibility(VTT));
527+
528+
if (CGM.supportsCOMDAT() && VTT.isWeakForLinker()) {
529+
assert(!UnimplementedFeature::setComdat());
530+
}
531+
}
532+
418533
void CIRGenVTables::buildThunks(GlobalDecl GD) {
419534
const CXXMethodDecl *MD =
420535
cast<CXXMethodDecl>(GD.getDecl())->getCanonicalDecl();

clang/lib/CIR/CodeGen/CIRGenVTables.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -138,13 +138,13 @@ class CIRGenVTables {
138138
// llvm::GlobalVariable::LinkageTypes Linkage,
139139
// VTableAddressPointsMapTy &AddressPoints);
140140

141-
// /// GetAddrOfVTT - Get the address of the VTT for the given record decl.
142-
// llvm::GlobalVariable *GetAddrOfVTT(const CXXRecordDecl *RD);
141+
/// Get the address of the VTT for the given record decl.
142+
mlir::cir::GlobalOp getAddrOfVTT(const CXXRecordDecl *RD);
143143

144-
// /// EmitVTTDefinition - Emit the definition of the given vtable.
145-
// void EmitVTTDefinition(llvm::GlobalVariable *VTT,
146-
// llvm::GlobalVariable::LinkageTypes Linkage,
147-
// const CXXRecordDecl *RD);
144+
/// Emit the definition of the given vtable.
145+
void buildVTTDefinition(mlir::cir::GlobalOp VTT,
146+
mlir::cir::GlobalLinkageKind Linkage,
147+
const CXXRecordDecl *RD);
148148

149149
/// Emit the associated thunks for the given global decl.
150150
void buildThunks(GlobalDecl GD);

clang/lib/CIR/CodeGen/CIRRecordLayoutBuilder.cpp

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -410,15 +410,37 @@ void CIRRecordLowering::accumulateVBases() {
410410
const CXXRecordDecl *BaseDecl = Base.getType()->getAsCXXRecordDecl();
411411
if (BaseDecl->isEmpty())
412412
continue;
413-
llvm_unreachable("NYI");
413+
// If the vbase is a primary virtual base of some base, then it doesn't
414+
// get its own storage location but instead lives inside of that base.
415+
if (astContext.isNearlyEmpty(BaseDecl) &&
416+
!hasOwnStorage(cxxRecordDecl, BaseDecl))
417+
continue;
418+
ScissorOffset = std::min(ScissorOffset,
419+
astRecordLayout.getVBaseClassOffset(BaseDecl));
414420
}
415421
members.push_back(MemberInfo(ScissorOffset, MemberInfo::InfoKind::Scissor,
416422
mlir::Type{}, cxxRecordDecl));
417423
for (const auto &Base : cxxRecordDecl->vbases()) {
418424
const CXXRecordDecl *BaseDecl = Base.getType()->getAsCXXRecordDecl();
419425
if (BaseDecl->isEmpty())
420426
continue;
421-
llvm_unreachable("NYI");
427+
CharUnits Offset = astRecordLayout.getVBaseClassOffset(BaseDecl);
428+
// If the vbase is a primary virtual base of some base, then it doesn't
429+
// get its own storage location but instead lives inside of that base.
430+
if (isOverlappingVBaseABI() && astContext.isNearlyEmpty(BaseDecl) &&
431+
!hasOwnStorage(cxxRecordDecl, BaseDecl)) {
432+
members.push_back(
433+
MemberInfo(Offset, MemberInfo::InfoKind::VBase, nullptr, BaseDecl));
434+
continue;
435+
}
436+
// If we've got a vtordisp, add it as a storage type.
437+
if (astRecordLayout.getVBaseOffsetsMap()
438+
.find(BaseDecl)
439+
->second.hasVtorDisp())
440+
members.push_back(
441+
StorageInfo(Offset - CharUnits::fromQuantity(4), getUIntNType(32)));
442+
members.push_back(MemberInfo(Offset, MemberInfo::InfoKind::VBase,
443+
getStorageType(BaseDecl), BaseDecl));
422444
}
423445
}
424446

@@ -449,8 +471,7 @@ void CIRRecordLowering::fillOutputFields() {
449471
} else if (member.kind == MemberInfo::InfoKind::Base) {
450472
nonVirtualBases[member.cxxRecordDecl] = fieldTypes.size() - 1;
451473
} else if (member.kind == MemberInfo::InfoKind::VBase) {
452-
llvm_unreachable("NYI");
453-
// virtualBases[member.cxxRecordDecl] = fieldTypes.size() - 1;
474+
virtualBases[member.cxxRecordDecl] = fieldTypes.size() - 1;
454475
}
455476
}
456477
}
@@ -657,4 +678,4 @@ CIRDataLayout::CIRDataLayout(mlir::ModuleOp modOp) : layout{modOp} {
657678
llvm_unreachable("unknown endianess");
658679
}
659680
}
660-
}
681+
}

0 commit comments

Comments
 (0)