Skip to content

Commit d29aa30

Browse files
authored
[CIR] Upstream support for null and virtual method pointers (llvm#176522)
This upstreams support for pointer-to-member value representations of null and virtual member pointers and the lowering of virtual member pointers.
1 parent f77a812 commit d29aa30

12 files changed

Lines changed: 182 additions & 20 deletions

File tree

clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,10 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
184184
return cir::MethodAttr::get(ty, methodFuncSymbolRef);
185185
}
186186

187+
cir::MethodAttr getNullMethodAttr(cir::MethodType ty) {
188+
return cir::MethodAttr::get(ty);
189+
}
190+
187191
cir::BoolAttr getCIRBoolAttr(bool state) {
188192
return cir::BoolAttr::get(getContext(), state);
189193
}

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

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -511,18 +511,21 @@ def CIR_MethodAttr : CIR_Attr<"Method", "method", [TypedAttrInterface]> {
511511
If the member function is a non-virtual function, the `symbol` parameter
512512
gives the global symbol for the non-virtual member function.
513513

514-
Virtual function handling is not yet implemented.
514+
If the member function is a virtual function, the `vtable_offset` parameter
515+
gives the offset of the vtable entry corresponding to the virtual member
516+
function.
515517

516-
If `symbol` is not present, the attribute represents a null pointer
517-
constant.
518+
`symbol` and `vtable_offset` cannot be present at the same time. If both of
519+
`symbol` and `vtable_offset` are not present, the attribute represents a
520+
null pointer constant.
518521

519522
Examples:
520523
```
521524
// Non-virtual method
522525
%0 = cir.const #cir.method<@_ZN1S2m1Ei> :
523526
!cir.method<!cir.func<(!s32i)> in !rec_S>
524527

525-
// Virtual method (not yet implemented)
528+
// Virtual method
526529
%1 = cir.const #cir.method<vtable_offset = 8> :
527530
!cir.method<!cir.func<(!s32i)> in !rec_S>
528531

@@ -534,23 +537,35 @@ def CIR_MethodAttr : CIR_Attr<"Method", "method", [TypedAttrInterface]> {
534537
let parameters = (ins AttributeSelfTypeParameter<
535538
"", "cir::MethodType">:$type,
536539
OptionalParameter<
537-
"std::optional<mlir::FlatSymbolRefAttr>">:$symbol);
540+
"std::optional<mlir::FlatSymbolRefAttr>">:$symbol,
541+
OptionalParameter<
542+
"std::optional<uint64_t>">:$vtable_offset);
538543

539544
let builders = [
540545
AttrBuilderWithInferredContext<(ins "cir::MethodType":$type), [{
541-
return $_get(type.getContext(), type, std::nullopt);
546+
return $_get(type.getContext(), type, std::nullopt, std::nullopt);
542547
}]>,
543548
AttrBuilderWithInferredContext<(ins "cir::MethodType":$type,
544549
"mlir::FlatSymbolRefAttr":$symbol), [{
545-
return $_get(type.getContext(), type, symbol);
550+
return $_get(type.getContext(), type, symbol, std::nullopt);
551+
}]>,
552+
AttrBuilderWithInferredContext<(ins "cir::MethodType":$type,
553+
"uint64_t":$vtable_offset), [{
554+
return $_get(type.getContext(), type, std::nullopt, vtable_offset);
546555
}]>,
547556
];
548557

549558
let hasCustomAssemblyFormat = 1;
550559

560+
let genVerifyDecl = 1;
561+
551562
let extraClassDeclaration = [{
552563
bool isNull() const {
553-
return !getSymbol().has_value();
564+
return !getSymbol().has_value() && !getVtableOffset().has_value();
565+
}
566+
567+
bool isVirtual() const {
568+
return getVtableOffset().has_value();
554569
}
555570
}];
556571
}

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,6 @@ struct MissingFeatures {
349349
static bool useEHCleanupForArray() { return false; }
350350
static bool vaArgABILowering() { return false; }
351351
static bool vectorConstants() { return false; }
352-
static bool virtualMethodAttr() { return false; }
353352
static bool vlas() { return false; }
354353
static bool vtableInitialization() { return false; }
355354
static bool vtableEmitMetadata() { return false; }

clang/lib/CIR/CodeGen/CIRGenBuilder.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,10 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
380380
return cir::ConstantOp::create(*this, loc, getNullDataMemberAttr(ty));
381381
}
382382

383+
cir::ConstantOp getNullMethodPtr(cir::MethodType ty, mlir::Location loc) {
384+
return cir::ConstantOp::create(*this, loc, getNullMethodAttr(ty));
385+
}
386+
383387
// TODO: split this to createFPExt/createFPTrunc when we have dedicated cast
384388
// operations.
385389
mlir::Value createFloatingCast(mlir::Value v, mlir::Type destType) {

clang/lib/CIR/CodeGen/CIRGenCXXABI.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ class CIRGenCXXABI {
6161
cir::PointerType destCIRTy,
6262
bool isRefCast, Address src) = 0;
6363

64+
virtual cir::MethodAttr buildVirtualMethodAttr(cir::MethodType methodTy,
65+
const CXXMethodDecl *md) = 0;
66+
6467
public:
6568
/// Similar to AddedStructorArgs, but only notes the number of additional
6669
/// arguments.

clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2246,9 +2246,8 @@ mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *ce) {
22462246

22472247
const MemberPointerType *mpt = ce->getType()->getAs<MemberPointerType>();
22482248
if (mpt->isMemberFunctionPointerType()) {
2249-
cgf.cgm.errorNYI(subExpr->getSourceRange(),
2250-
"CK_NullToMemberPointer: member function pointer");
2251-
return {};
2249+
auto ty = mlir::cast<cir::MethodType>(cgf.convertType(destTy));
2250+
return builder.getNullMethodPtr(ty, cgf.getLoc(subExpr->getExprLoc()));
22522251
}
22532252

22542253
auto ty = mlir::cast<cir::DataMemberType>(cgf.convertType(destTy));

clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,9 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI {
156156
cir::PointerType destCIRTy, bool isRefCast,
157157
Address src) override;
158158

159+
cir::MethodAttr buildVirtualMethodAttr(cir::MethodType methodTy,
160+
const CXXMethodDecl *md) override;
161+
159162
Address initializeArrayCookie(CIRGenFunction &cgf, Address newPtr,
160163
mlir::Value numElements, const CXXNewExpr *e,
161164
QualType elementType) override;
@@ -2195,6 +2198,25 @@ mlir::Value CIRGenItaniumCXXABI::emitDynamicCast(CIRGenFunction &cgf,
21952198
isRefCast, castInfo);
21962199
}
21972200

2201+
cir::MethodAttr
2202+
CIRGenItaniumCXXABI::buildVirtualMethodAttr(cir::MethodType methodTy,
2203+
const CXXMethodDecl *md) {
2204+
assert(md->isVirtual() && "only deal with virtual member functions");
2205+
2206+
uint64_t index = cgm.getItaniumVTableContext().getMethodVTableIndex(md);
2207+
uint64_t vtableOffset;
2208+
if (cgm.getItaniumVTableContext().isRelativeLayout()) {
2209+
// Multiply by 4-byte relative offsets.
2210+
vtableOffset = index * 4;
2211+
} else {
2212+
const ASTContext &astContext = cgm.getASTContext();
2213+
CharUnits pointerWidth = astContext.toCharUnitsFromBits(
2214+
astContext.getTargetInfo().getPointerWidth(LangAS::Default));
2215+
vtableOffset = index * pointerWidth.getQuantity();
2216+
}
2217+
2218+
return cir::MethodAttr::get(methodTy, vtableOffset);
2219+
}
21982220
/// The Itanium ABI always places an offset to the complete object
21992221
/// at entry -2 in the vtable.
22002222
void CIRGenItaniumCXXABI::emitVirtualObjectDelete(

clang/lib/CIR/CodeGen/CIRGenModule.cpp

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1529,12 +1529,9 @@ mlir::Value CIRGenModule::emitMemberPointerConstant(const UnaryOperator *e) {
15291529
// A member function pointer.
15301530
if (const auto *methodDecl = dyn_cast<CXXMethodDecl>(decl)) {
15311531
auto ty = mlir::cast<cir::MethodType>(convertType(e->getType()));
1532-
if (methodDecl->isVirtual()) {
1533-
assert(!cir::MissingFeatures::virtualMethodAttr());
1534-
errorNYI(e->getSourceRange(),
1535-
"emitMemberPointerConstant: virtual method pointer");
1536-
return {};
1537-
}
1532+
if (methodDecl->isVirtual())
1533+
return cir::ConstantOp::create(
1534+
builder, loc, getCXXABI().buildVirtualMethodAttr(ty, methodDecl));
15381535

15391536
cir::FuncOp methodFuncOp = getAddrOfFunction(methodDecl);
15401537
return cir::ConstantOp::create(builder, loc,

clang/lib/CIR/Dialect/IR/CIRAttrs.cpp

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,18 @@ DataMemberAttr::verify(function_ref<InFlightDiagnostic()> emitError,
305305
// MethodAttr definitions
306306
//===----------------------------------------------------------------------===//
307307

308+
LogicalResult MethodAttr::verify(function_ref<InFlightDiagnostic()> emitError,
309+
cir::MethodType type,
310+
std::optional<FlatSymbolRefAttr> symbol,
311+
std::optional<uint64_t> vtable_offset) {
312+
if (symbol.has_value() && vtable_offset.has_value())
313+
return emitError()
314+
<< "at most one of symbol and vtable_offset can be present "
315+
"in #cir.method";
316+
317+
return success();
318+
}
319+
308320
Attribute MethodAttr::parse(AsmParser &parser, Type odsType) {
309321
auto ty = mlir::cast<cir::MethodType>(odsType);
310322

@@ -331,15 +343,30 @@ Attribute MethodAttr::parse(AsmParser &parser, Type odsType) {
331343
return get(ty, symbol);
332344
}
333345

334-
return {};
346+
// Parse a uint64 that represents the vtable offset.
347+
std::uint64_t vtableOffset = 0;
348+
if (parser.parseKeyword("vtable_offset"))
349+
return {};
350+
if (parser.parseEqual())
351+
return {};
352+
if (parser.parseInteger(vtableOffset))
353+
return {};
354+
355+
if (parser.parseGreater())
356+
return {};
357+
358+
return get(ty, vtableOffset);
335359
}
336360

337361
void MethodAttr::print(AsmPrinter &printer) const {
338362
auto symbol = getSymbol();
363+
auto vtableOffset = getVtableOffset();
339364

340365
printer << '<';
341366
if (symbol.has_value()) {
342367
printer << *symbol;
368+
} else if (vtableOffset.has_value()) {
369+
printer << "vtable_offset = " << *vtableOffset;
343370
} else {
344371
printer << "null";
345372
}

clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,29 @@ mlir::TypedAttr LowerItaniumCXXABI::lowerMethodConstant(
220220
loweredMethodTy, mlir::ArrayAttr::get(attr.getContext(), {zero, zero}));
221221
}
222222

223-
assert(!cir::MissingFeatures::virtualMethodAttr());
223+
if (attr.isVirtual()) {
224+
if (useARMMethodPtrABI) {
225+
// ARM C++ ABI 3.2.1:
226+
// This ABI specifies that adj contains twice the this
227+
// adjustment, plus 1 if the member function is virtual. The
228+
// least significant bit of adj then makes exactly the same
229+
// discrimination as the least significant bit of ptr does for
230+
// Itanium.
231+
llvm_unreachable("ARM method ptr abi NYI");
232+
}
233+
234+
// Itanium C++ ABI 2.3.2:
235+
//
236+
// In the standard representation, a member function pointer for a
237+
// virtual function is represented with ptr set to 1 plus the function's
238+
// v-table entry offset (in bytes), converted to a function pointer as if
239+
// by reinterpret_cast<fnptr_t>(uintfnptr_t(1 + offset)), where
240+
// uintfnptr_t is an unsigned integer of the same size as fnptr_t.
241+
auto ptr =
242+
cir::IntAttr::get(ptrdiffCIRTy, 1 + attr.getVtableOffset().value());
243+
return cir::ConstRecordAttr::get(
244+
loweredMethodTy, mlir::ArrayAttr::get(attr.getContext(), {ptr, zero}));
245+
}
224246

225247
// Itanium C++ ABI 2.3.2:
226248
//

0 commit comments

Comments
 (0)