Skip to content

Commit 3cfefd7

Browse files
Laity000lanza
authored andcommitted
[CIR][CIRGen] Support CodeGen for vbase constructors
1. Add new `cir.vtt.address_point` op for visiting the element of VTT to initialize the virtual pointer. 2. Implement `getVirtualBaseClassOffset` method which provides a virtual offset to adjust to actual virtual pointers in virtual base. 3. Follows the original clang CodeGen scheme for the implementation of most other parts. @bcardosolopes's note: this is cherry-picked from an older PR from Jing Zhang and slightly modified for updates: applied review, test, doc and operation syntax. It does not yet has LLVM lowering support, I'm going to make incremental changes on top of this. Any necessary CIR modifications to this design should follow up shortly too. Also, to make this work I also added more logic to `addImplicitStructorParam`s` and `buildThisParam`.
1 parent 92d38e0 commit 3cfefd7

15 files changed

+579
-101
lines changed

clang/include/clang/CIR/Dialect/IR/CIROps.td

+59
Original file line numberDiff line numberDiff line change
@@ -2399,6 +2399,65 @@ def VTableAddrPointOp : CIR_Op<"vtable.address_point",
23992399
let hasVerifier = 1;
24002400
}
24012401

2402+
//===----------------------------------------------------------------------===//
2403+
// VTTAddrPointOp
2404+
//===----------------------------------------------------------------------===//
2405+
2406+
def VTTAddrPointOp : CIR_Op<"vtt.address_point",
2407+
[Pure, DeclareOpInterfaceMethods<SymbolUserOpInterface>]> {
2408+
let summary = "Get the VTT address point";
2409+
let description = [{
2410+
The `vtt.address_point` operation retrieves an element from the VTT,
2411+
which is the address point of a C++ vtable. In virtual inheritance,
2412+
A set of internal `__vptr` for an object are initialized by this operation,
2413+
which assigns an element from the VTT. The initialization order is as follows:
2414+
2415+
The complete object constructors and destructors find the VTT,
2416+
via the mangled name of VTT global variable. They pass the address of
2417+
the subobject's sub-VTT entry in the VTT as a second parameter
2418+
when calling the base object constructors and destructors.
2419+
The base object constructors and destructors use the addresses passed to
2420+
initialize the primary virtual pointer and virtual pointers that point to
2421+
the classes which either have virtual bases or override virtual functions
2422+
with a virtual step.
2423+
2424+
The first parameter is either the mangled name of VTT global variable
2425+
or the address of the subobject's sub-VTT entry in the VTT.
2426+
The second parameter `offset` provides a virtual step to adjust to
2427+
the actual address point of the vtable.
2428+
2429+
The return type is always a `!cir.ptr<!cir.ptr<void>>`.
2430+
2431+
Example:
2432+
```mlir
2433+
cir.global linkonce_odr @_ZTV1B = ...
2434+
...
2435+
%3 = cir.base_class_addr(%1 : !cir.ptr<!ty_D> nonnull) [0] -> !cir.ptr<!ty_B>
2436+
%4 = cir.vtt.address_point @_ZTT1D, offset = 1 -> !cir.ptr<!cir.ptr<!void>>
2437+
cir.call @_ZN1BC2Ev(%3, %4)
2438+
```
2439+
Or:
2440+
```mlir
2441+
%7 = cir.vtt.address_point %3 : !cir.ptr<!cir.ptr<!void>>, offset = 1 -> !cir.ptr<!cir.ptr<!void>>
2442+
```
2443+
}];
2444+
2445+
let arguments = (ins OptionalAttr<FlatSymbolRefAttr>:$name,
2446+
Optional<CIR_AnyType>:$sym_addr,
2447+
I32Attr:$offset);
2448+
let results = (outs Res<CIR_PointerType, "", []>:$addr);
2449+
2450+
let assemblyFormat = [{
2451+
($name^)?
2452+
($sym_addr^ `:` type($sym_addr))?
2453+
`,`
2454+
`offset` `=` $offset
2455+
`->` qualified(type($addr)) attr-dict
2456+
}];
2457+
2458+
let hasVerifier = 1;
2459+
}
2460+
24022461
//===----------------------------------------------------------------------===//
24032462
// SetBitfieldOp
24042463
//===----------------------------------------------------------------------===//

clang/lib/CIR/CodeGen/CIRGenBuilder.h

+13
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "mlir/IR/BuiltinTypes.h"
3232
#include "mlir/IR/Location.h"
3333
#include "mlir/IR/Types.h"
34+
#include "mlir/IR/Value.h"
3435
#include "llvm/ADT/APSInt.h"
3536
#include "llvm/ADT/ArrayRef.h"
3637
#include "llvm/ADT/FloatingPointMode.h"
@@ -695,6 +696,18 @@ class CIRGenBuilderTy : public CIRBaseBuilderTy {
695696
return Address(baseAddr, ptrTy, addr.getAlignment());
696697
}
697698

699+
mlir::Value createVTTAddrPoint(mlir::Location loc, mlir::Type retTy,
700+
mlir::Value addr, uint64_t offset) {
701+
return create<mlir::cir::VTTAddrPointOp>(
702+
loc, retTy, mlir::FlatSymbolRefAttr{}, addr, offset);
703+
}
704+
705+
mlir::Value createVTTAddrPoint(mlir::Location loc, mlir::Type retTy,
706+
mlir::FlatSymbolRefAttr sym, uint64_t offset) {
707+
return create<mlir::cir::VTTAddrPointOp>(loc, retTy, sym, mlir::Value{},
708+
offset);
709+
}
710+
698711
// FIXME(cir): CIRGenBuilder class should have an attribute with a reference
699712
// to the module so that we don't have search for it or pass it around.
700713
// FIXME(cir): Track a list of globals, or at least the last one inserted, so

clang/lib/CIR/CodeGen/CIRGenCXXABI.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ void CIRGenCXXABI::buildThisParam(CIRGenFunction &CGF,
6666
isThisCompleteObject(CGF.CurGD)) {
6767
CGF.CXXABIThisAlignment = Layout.getAlignment();
6868
} else {
69-
llvm_unreachable("NYI");
69+
CGF.CXXABIThisAlignment = Layout.getNonVirtualAlignment();
7070
}
7171
}
7272

clang/lib/CIR/CodeGen/CIRGenCXXABI.h

+15-1
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,8 @@ class CIRGenCXXABI {
9595
clang::CXXCtorType Type, bool ForVirtualBase, bool Delegating) = 0;
9696

9797
/// Emit the ABI-specific prolog for the function
98-
virtual void buildInstanceFunctionProlog(CIRGenFunction &CGF) = 0;
98+
virtual void buildInstanceFunctionProlog(SourceLocation Loc,
99+
CIRGenFunction &CGF) = 0;
99100

100101
/// Get the type of the implicit "this" parameter used by a method. May return
101102
/// zero if no specific type is applicable, e.g. if the ABI expects the "this"
@@ -120,6 +121,14 @@ class CIRGenCXXABI {
120121
return CGF.CXXStructorImplicitParamDecl;
121122
}
122123

124+
mlir::Value getStructorImplicitParamValue(CIRGenFunction &CGF) {
125+
return CGF.CXXStructorImplicitParamValue;
126+
}
127+
128+
void setStructorImplicitParamValue(CIRGenFunction &CGF, mlir::Value val) {
129+
CGF.CXXStructorImplicitParamValue = val;
130+
}
131+
123132
/// Perform ABI-specific "this" argument adjustment required prior to
124133
/// a call of a virtual function.
125134
/// The "VirtualCall" argument is true iff the call itself is virtual.
@@ -319,6 +328,11 @@ class CIRGenCXXABI {
319328

320329
virtual void buildBadCastCall(CIRGenFunction &CGF, mlir::Location loc) = 0;
321330

331+
virtual mlir::Value
332+
getVirtualBaseClassOffset(mlir::Location loc, CIRGenFunction &CGF,
333+
Address This, const CXXRecordDecl *ClassDecl,
334+
const CXXRecordDecl *BaseClassDecl) = 0;
335+
322336
virtual mlir::Value buildDynamicCast(CIRGenFunction &CGF, mlir::Location Loc,
323337
QualType SrcRecordTy,
324338
QualType DestRecordTy,

clang/lib/CIR/CodeGen/CIRGenCall.cpp

+5-1
Original file line numberDiff line numberDiff line change
@@ -1186,14 +1186,18 @@ CIRGenTypes::arrangeCXXStructorDeclaration(GlobalDecl GD) {
11861186
if (auto *CD = dyn_cast<CXXConstructorDecl>(MD)) {
11871187
// A base class inheriting constructor doesn't get forwarded arguments
11881188
// needed to construct a virtual base (or base class thereof)
1189-
assert(!CD->getInheritedConstructor() && "Inheritance NYI");
1189+
if (auto Inherited = CD->getInheritedConstructor())
1190+
PassParams = inheritingCtorHasParams(Inherited, GD.getCtorType());
11901191
}
11911192

11921193
CanQual<FunctionProtoType> FTP = GetFormalType(MD);
11931194

11941195
if (PassParams)
11951196
appendParameterTypes(*this, argTypes, paramInfos, FTP);
11961197

1198+
CIRGenCXXABI::AddedStructorArgCounts AddedArgs =
1199+
TheCXXABI.buildStructorSignature(GD, argTypes);
1200+
(void)AddedArgs;
11971201
assert(paramInfos.empty() && "NYI");
11981202

11991203
assert(!MD->isVariadic() && "Variadic fns NYI");

clang/lib/CIR/CodeGen/CIRGenClass.cpp

+59-13
Original file line numberDiff line numberDiff line change
@@ -689,9 +689,8 @@ static Address ApplyNonVirtualAndVirtualOffset(
689689
CharUnits alignment;
690690
if (virtualOffset) {
691691
assert(nearestVBase && "virtual offset without vbase?");
692-
llvm_unreachable("NYI");
693-
// alignment = CGF.CGM.getVBaseAlignment(addr.getAlignment(),
694-
// derivedClass, nearestVBase);
692+
alignment = CGF.CGM.getVBaseAlignment(addr.getAlignment(), derivedClass,
693+
nearestVBase);
695694
} else {
696695
alignment = addr.getAlignment();
697696
}
@@ -714,7 +713,11 @@ void CIRGenFunction::initializeVTablePointer(mlir::Location loc,
714713
CharUnits NonVirtualOffset = CharUnits::Zero();
715714

716715
if (CGM.getCXXABI().isVirtualOffsetNeededForVTableField(*this, Vptr)) {
717-
llvm_unreachable("NYI");
716+
// We need to use the virtual base offset offset because the virtual base
717+
// might have a different offset in the most derived class.
718+
VirtualOffset = CGM.getCXXABI().getVirtualBaseClassOffset(
719+
loc, *this, LoadCXXThisAddress(), Vptr.VTableClass, Vptr.NearestVBase);
720+
NonVirtualOffset = Vptr.OffsetFromNearestVBase;
718721
} else {
719722
// We can just use the base offset in the complete class.
720723
NonVirtualOffset = Vptr.Base.getBaseOffset();
@@ -797,7 +800,16 @@ void CIRGenFunction::getVTablePointers(BaseSubobject Base,
797800
bool BaseDeclIsNonVirtualPrimaryBase;
798801

799802
if (I.isVirtual()) {
800-
llvm_unreachable("NYI");
803+
// Check if we've visited this virtual base before.
804+
if (!VBases.insert(BaseDecl).second)
805+
continue;
806+
807+
const ASTRecordLayout &Layout =
808+
getContext().getASTRecordLayout(VTableClass);
809+
810+
BaseOffset = Layout.getVBaseClassOffset(BaseDecl);
811+
BaseOffsetFromNearestVBase = CharUnits::Zero();
812+
BaseDeclIsNonVirtualPrimaryBase = false;
801813
} else {
802814
const ASTRecordLayout &Layout = getContext().getASTRecordLayout(RD);
803815

@@ -1449,18 +1461,34 @@ mlir::Value CIRGenFunction::GetVTTParameter(GlobalDecl GD, bool ForVirtualBase,
14491461
const CXXRecordDecl *RD = cast<CXXMethodDecl>(CurCodeDecl)->getParent();
14501462
const CXXRecordDecl *Base = cast<CXXMethodDecl>(GD.getDecl())->getParent();
14511463

1464+
uint64_t SubVTTIndex;
1465+
14521466
if (Delegating) {
14531467
llvm_unreachable("NYI");
14541468
} else if (RD == Base) {
14551469
llvm_unreachable("NYI");
14561470
} else {
1457-
llvm_unreachable("NYI");
1471+
const ASTRecordLayout &Layout = getContext().getASTRecordLayout(RD);
1472+
CharUnits BaseOffset = ForVirtualBase ? Layout.getVBaseClassOffset(Base)
1473+
: Layout.getBaseClassOffset(Base);
1474+
1475+
SubVTTIndex =
1476+
CGM.getVTables().getSubVTTIndex(RD, BaseSubobject(Base, BaseOffset));
1477+
assert(SubVTTIndex != 0 && "Sub-VTT index must be greater than zero!");
14581478
}
14591479

1480+
auto Loc = CGM.getLoc(RD->getBeginLoc());
14601481
if (CGM.getCXXABI().NeedsVTTParameter(CurGD)) {
1461-
llvm_unreachable("NYI");
1482+
// A VTT parameter was passed to the constructor, use it.
1483+
auto VTT = LoadCXXVTT();
1484+
return CGM.getBuilder().createVTTAddrPoint(Loc, VTT.getType(), VTT,
1485+
SubVTTIndex);
14621486
} else {
1463-
llvm_unreachable("NYI");
1487+
// We're the complete constructor, so get the VTT by name.
1488+
auto VTT = CGM.getVTables().getAddrOfVTT(RD);
1489+
return CGM.getBuilder().createVTTAddrPoint(
1490+
Loc, CGM.getBuilder().getPointerTo(CGM.VoidPtrTy),
1491+
mlir::FlatSymbolRefAttr::get(VTT.getSymNameAttr()), SubVTTIndex);
14641492
}
14651493
}
14661494

@@ -1645,8 +1673,25 @@ CIRGenModule::getDynamicOffsetAlignment(clang::CharUnits actualBaseAlign,
16451673
return std::min(actualBaseAlign, expectedTargetAlign);
16461674
}
16471675

1648-
/// Emit a loop to call a particular constructor for each of several members of
1649-
/// an array.
1676+
/// Return the best known alignment for a pointer to a virtual base,
1677+
/// given the alignment of a pointer to the derived class.
1678+
clang::CharUnits
1679+
CIRGenModule::getVBaseAlignment(CharUnits actualDerivedAlign,
1680+
const CXXRecordDecl *derivedClass,
1681+
const CXXRecordDecl *vbaseClass) {
1682+
// The basic idea here is that an underaligned derived pointer might
1683+
// indicate an underaligned base pointer.
1684+
1685+
assert(vbaseClass->isCompleteDefinition());
1686+
auto &baseLayout = getASTContext().getASTRecordLayout(vbaseClass);
1687+
CharUnits expectedVBaseAlign = baseLayout.getNonVirtualAlignment();
1688+
1689+
return getDynamicOffsetAlignment(actualDerivedAlign, derivedClass,
1690+
expectedVBaseAlign);
1691+
}
1692+
1693+
/// Emit a loop to call a particular constructor for each of several members
1694+
/// of an array.
16501695
///
16511696
/// \param ctor the constructor to call for each element
16521697
/// \param arrayType the type of the array to initialize
@@ -1663,8 +1708,8 @@ void CIRGenFunction::buildCXXAggrConstructorCall(
16631708
NewPointerIsChecked, zeroInitialize);
16641709
}
16651710

1666-
/// Emit a loop to call a particular constructor for each of several members of
1667-
/// an array.
1711+
/// Emit a loop to call a particular constructor for each of several members
1712+
/// of an array.
16681713
///
16691714
/// \param ctor the constructor to call for each element
16701715
/// \param numElements the number of elements in the array;
@@ -1749,7 +1794,8 @@ void CIRGenFunction::buildCXXAggrConstructorCall(
17491794
AggValueSlot::DoesNotOverlap, AggValueSlot::IsNotZeroed,
17501795
NewPointerIsChecked ? AggValueSlot::IsSanitizerChecked
17511796
: AggValueSlot::IsNotSanitizerChecked);
1752-
buildCXXConstructorCall(ctor, Ctor_Complete, /*ForVirtualBase=*/false,
1797+
buildCXXConstructorCall(ctor, Ctor_Complete,
1798+
/*ForVirtualBase=*/false,
17531799
/*Delegating=*/false, currAVS, E);
17541800
builder.create<mlir::cir::YieldOp>(loc);
17551801
});

clang/lib/CIR/CodeGen/CIRGenFunction.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -1301,7 +1301,7 @@ void CIRGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
13011301
}
13021302

13031303
if (D && isa<CXXMethodDecl>(D) && cast<CXXMethodDecl>(D)->isInstance()) {
1304-
CGM.getCXXABI().buildInstanceFunctionProlog(*this);
1304+
CGM.getCXXABI().buildInstanceFunctionProlog(Loc, *this);
13051305

13061306
const auto *MD = cast<CXXMethodDecl>(D);
13071307
if (MD->getParent()->isLambda() && MD->getOverloadedOperator() == OO_Call) {

clang/lib/CIR/CodeGen/CIRGenFunction.h

+8
Original file line numberDiff line numberDiff line change
@@ -1585,6 +1585,14 @@ class CIRGenFunction : public CIRGenTypeCache {
15851585
}
15861586
Address LoadCXXThisAddress();
15871587

1588+
/// Load the VTT parameter to base constructors/destructors have virtual
1589+
/// bases. FIXME: Every place that calls LoadCXXVTT is something that needs to
1590+
/// be abstracted properly.
1591+
mlir::Value LoadCXXVTT() {
1592+
assert(CXXStructorImplicitParamValue && "no VTT value for this function");
1593+
return CXXStructorImplicitParamValue;
1594+
}
1595+
15881596
/// Convert the given pointer to a complete class to the given direct base.
15891597
Address getAddressOfDirectBaseInCompleteClass(mlir::Location loc,
15901598
Address Value,

0 commit comments

Comments
 (0)