Skip to content

Commit cbb6519

Browse files
committed
[CIR] Implement constant expression member pointer casts
This patch implements support for member pointer casts in constant expressions, specifically handling CK_DerivedToBaseMemberPointer, CK_BaseToDerivedMemberPointer, and CK_ReinterpretMemberPointer cast kinds. The implementation closely follows CodeGen's approach in CGExprConstant.cpp and ItaniumCXXABI.cpp, adapted for CIR's attribute-based constant model. Implementation details: 1. Added CIRGenModule::GetNonVirtualBaseClassOffset() - Computes the offset between derived and base classes as a constant cir::IntAttr, mirroring CodeGen's GetNonVirtualBaseClassOffset() which returns llvm::Constant. 2. Added CIRGenCXXABI::getMemberPointerAdjustment() - Calculates the adjustment needed for member pointer conversions by determining the derived type based on cast direction and delegating to GetNonVirtualBaseClassOffset(). 3. Added CIRGenCXXABI::EmitMemberPointerConversion() - Base virtual method with NYI default, to be overridden by specific ABI implementations. 4. Implemented CIRGenItaniumCXXABI::EmitMemberPointerConversion() for the Itanium C++ ABI: - CK_ReinterpretMemberPointer: Returns source unchanged (no adjustment) - CK_DerivedToBaseMemberPointer/CK_BaseToDerivedMemberPointer: * For member data pointers: Adjusts the cir::DataMemberAttr index by the offset, matching LLVM's nsw add/sub on constant integers * Member function pointers: Marked NYI (requires struct {ptr, adj}) 5. Updated ConstExprEmitter::VisitCastExpr() to call the new infrastructure instead of llvm_unreachable(). Differences from CodeGen: - Uses CIR's attribute system (cir::IntAttr, cir::DataMemberAttr) instead of LLVM constants - Member data pointer adjustment directly modifies the DataMemberAttr index rather than operating on raw integer constants Test Plan: - Added clang/test/CIR/CodeGen/member-pointer-cast.cpp to verify CIR output - Updated clang/test/CIR/crashes/constexpr-cast.cpp to reflect progress (original NYI at line 1006 is fixed; now fails at member function pointer emission from APValue, which is a separate feature) The crash test case from TODO.md item #9 now progresses past the constant expression cast NYI to a different unimplemented feature (member function pointer emission), demonstrating that the core cast infrastructure works. Fixes original NYI at CIRGenExprConst.cpp:1006 ghstack-source-id: cf00c7d Pull-Request: #2017
1 parent 0272f1d commit cbb6519

File tree

8 files changed

+155
-6
lines changed

8 files changed

+155
-6
lines changed

clang/lib/CIR/CodeGen/CIRGenCXXABI.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,33 @@ mlir::TypedAttr CIRGenCXXABI::emitNullMemberPointer(clang::QualType T) {
9090
llvm_unreachable("NYI");
9191
}
9292

93+
/// Returns the adjustment, in bytes, required for the given
94+
/// member-pointer operation. Returns null if no adjustment is
95+
/// required.
96+
mlir::Attribute
97+
CIRGenCXXABI::getMemberPointerAdjustment(const CastExpr *castExpr) {
98+
assert(castExpr->getCastKind() == CK_DerivedToBaseMemberPointer ||
99+
castExpr->getCastKind() == CK_BaseToDerivedMemberPointer);
100+
101+
QualType derivedType;
102+
if (castExpr->getCastKind() == CK_DerivedToBaseMemberPointer)
103+
derivedType = castExpr->getSubExpr()->getType();
104+
else
105+
derivedType = castExpr->getType();
106+
107+
const CXXRecordDecl *derivedClass =
108+
derivedType->castAs<MemberPointerType>()->getMostRecentCXXRecordDecl();
109+
110+
return CGM.getNonVirtualBaseClassOffsetAsAttr(
111+
derivedClass, castExpr->path_begin(), castExpr->path_end());
112+
}
113+
114+
mlir::Attribute
115+
CIRGenCXXABI::emitMemberPointerConversion(const CastExpr *castExpr,
116+
mlir::Attribute src) {
117+
llvm_unreachable("NYI");
118+
}
119+
93120
CharUnits CIRGenCXXABI::getArrayCookieSize(const CXXNewExpr *E) {
94121
if (!requiresArrayCookie(E))
95122
return CharUnits::Zero();

clang/lib/CIR/CodeGen/CIRGenCXXABI.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,16 @@ class CIRGenCXXABI {
330330
/// Create a null member pointer of the given type.
331331
virtual mlir::TypedAttr emitNullMemberPointer(clang::QualType T);
332332

333+
/// Returns the adjustment, in bytes, required for the given
334+
/// member-pointer operation. Returns null if no adjustment is
335+
/// required. Does not handle virtual conversions.
336+
mlir::Attribute getMemberPointerAdjustment(const CastExpr *castExpr);
337+
338+
/// Perform a derived-to-base, base-to-derived, or bitcast member pointer
339+
/// conversion on a constant value.
340+
virtual mlir::Attribute emitMemberPointerConversion(const CastExpr *castExpr,
341+
mlir::Attribute src);
342+
333343
/// Gets the offsets of all the virtual base pointers in a given class.
334344
virtual std::vector<CharUnits> getVBPtrOffsets(const CXXRecordDecl *RD);
335345

clang/lib/CIR/CodeGen/CIRGenExprConst.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1003,7 +1003,10 @@ class ConstExprEmitter
10031003
case CK_ReinterpretMemberPointer:
10041004
case CK_DerivedToBaseMemberPointer:
10051005
case CK_BaseToDerivedMemberPointer: {
1006-
llvm_unreachable("not implemented");
1006+
auto src = Emitter.tryEmitPrivate(subExpr, subExpr->getType());
1007+
if (!src)
1008+
return nullptr;
1009+
return CGM.getCXXABI().emitMemberPointerConversion(E, src);
10071010
}
10081011

10091012
// These will never be supported.

clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI {
124124
bool classifyReturnType(CIRGenFunctionInfo &FI) const override;
125125
bool isZeroInitializable(const MemberPointerType *MPT) override;
126126
mlir::TypedAttr emitNullMemberPointer(clang::QualType T) override;
127+
mlir::Attribute emitMemberPointerConversion(const CastExpr *castExpr,
128+
mlir::Attribute src) override;
127129

128130
AddedStructorArgCounts
129131
buildStructorSignature(GlobalDecl GD,
@@ -3186,6 +3188,59 @@ mlir::TypedAttr CIRGenItaniumCXXABI::emitNullMemberPointer(clang::QualType T) {
31863188
llvm_unreachable("NYI");
31873189
}
31883190

3191+
mlir::Attribute
3192+
CIRGenItaniumCXXABI::emitMemberPointerConversion(const CastExpr *castExpr,
3193+
mlir::Attribute src) {
3194+
assert(castExpr->getCastKind() == CK_DerivedToBaseMemberPointer ||
3195+
castExpr->getCastKind() == CK_BaseToDerivedMemberPointer ||
3196+
castExpr->getCastKind() == CK_ReinterpretMemberPointer);
3197+
3198+
// Under Itanium, reinterprets don't require any additional processing.
3199+
if (castExpr->getCastKind() == CK_ReinterpretMemberPointer)
3200+
return src;
3201+
3202+
// If the adjustment is trivial, we don't need to do anything.
3203+
mlir::Attribute adj = getMemberPointerAdjustment(castExpr);
3204+
if (!adj)
3205+
return src;
3206+
3207+
bool isDerivedToBase =
3208+
(castExpr->getCastKind() == CK_DerivedToBaseMemberPointer);
3209+
3210+
const MemberPointerType *destTy =
3211+
castExpr->getType()->castAs<MemberPointerType>();
3212+
3213+
// For member data pointers, this is just a matter of adding the
3214+
// offset if the source is non-null.
3215+
if (destTy->isMemberDataPointer()) {
3216+
auto dataMemberAttr = cast<cir::DataMemberAttr>(src);
3217+
auto adjIntAttr = cast<cir::IntAttr>(adj);
3218+
3219+
// null maps to null.
3220+
if (dataMemberAttr.isNullPtr())
3221+
return src;
3222+
3223+
// Get the current member index.
3224+
auto memberIndex = dataMemberAttr.getMemberIndex().value();
3225+
3226+
// Apply the adjustment.
3227+
int64_t newIndex;
3228+
if (isDerivedToBase)
3229+
newIndex = memberIndex - adjIntAttr.getSInt();
3230+
else
3231+
newIndex = memberIndex + adjIntAttr.getSInt();
3232+
3233+
// Create a new data member attribute with the adjusted index.
3234+
auto resultType =
3235+
cast<cir::DataMemberType>(CGM.convertType(castExpr->getType()));
3236+
return cir::DataMemberAttr::get(resultType, newIndex);
3237+
}
3238+
3239+
// For member function pointers, the adjustment is more complex.
3240+
// They are represented as a struct with {ptr, adj}.
3241+
llvm_unreachable("Member function pointer conversions NYI");
3242+
}
3243+
31893244
/// The Itanium ABI always places an offset to the complete object
31903245
/// at entry -2 in the vtable.
31913246
void CIRGenItaniumCXXABI::emitVirtualObjectDelete(

clang/lib/CIR/CodeGen/CIRGenModule.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4277,6 +4277,22 @@ CharUnits CIRGenModule::computeNonVirtualBaseClassOffset(
42774277
return offset;
42784278
}
42794279

4280+
mlir::Attribute CIRGenModule::getNonVirtualBaseClassOffsetAsAttr(
4281+
const CXXRecordDecl *classDecl, CastExpr::path_const_iterator pathBegin,
4282+
CastExpr::path_const_iterator pathEnd) {
4283+
assert(pathBegin != pathEnd && "Base path should not be empty!");
4284+
4285+
CharUnits offset =
4286+
computeNonVirtualBaseClassOffset(classDecl, pathBegin, pathEnd);
4287+
if (offset.isZero())
4288+
return nullptr;
4289+
4290+
mlir::Type ptrDiffTy =
4291+
getTypes().convertType(getASTContext().getPointerDiffType());
4292+
4293+
return cir::IntAttr::get(ptrDiffTy, offset.getQuantity());
4294+
}
4295+
42804296
void CIRGenModule::Error(SourceLocation loc, StringRef message) {
42814297
unsigned diagID = getDiags().getCustomDiagID(DiagnosticsEngine::Error, "%0");
42824298
getDiags().Report(astContext.getFullLoc(loc), diagID) << message;

clang/lib/CIR/CodeGen/CIRGenModule.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,13 @@ class CIRGenModule : public CIRGenTypeCache {
405405
CastExpr::path_const_iterator pathBegin,
406406
CastExpr::path_const_iterator pathEnd);
407407

408+
/// Returns the offset from a derived class to a class as a constant
409+
/// attribute. Returns null if the offset is 0.
410+
mlir::Attribute
411+
getNonVirtualBaseClassOffsetAsAttr(const CXXRecordDecl *classDecl,
412+
CastExpr::path_const_iterator pathBegin,
413+
CastExpr::path_const_iterator pathEnd);
414+
408415
/// Get the CIR attributes and calling convention to use for a particular
409416
/// function type.
410417
///
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
2+
// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
3+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.ll
4+
// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM
5+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ogcg.ll
6+
// RUN: FileCheck --input-file=%t.ogcg.ll %s -check-prefix=LLVM
7+
8+
// Test base-to-derived member data pointer conversion
9+
10+
class Base {
11+
public:
12+
int baseField1;
13+
int baseField2;
14+
};
15+
16+
class Derived : public Base {
17+
public:
18+
int derivedField;
19+
};
20+
21+
// Test 1: Base-to-derived conversion with zero offset (baseField1 at field index 0)
22+
// CIR: cir.global external @ptrZeroOffset = #cir.data_member<0>
23+
// LLVM: @ptrZeroOffset = global i64 0
24+
int Derived::*ptrZeroOffset = static_cast<int Derived::*>(&Base::baseField1);
25+
26+
// Test 2: Base-to-derived conversion with non-zero offset (baseField2 at field index 1)
27+
// CIR: cir.global external @ptrNonZeroOffset = #cir.data_member<1>
28+
// LLVM: @ptrNonZeroOffset = global i64 4
29+
int Derived::*ptrNonZeroOffset = static_cast<int Derived::*>(&Base::baseField2);
30+
31+
// Test 3: Reinterpret cast (should preserve original value)
32+
// CIR: cir.global external @ptrReinterpret = #cir.data_member<0>
33+
// LLVM: @ptrReinterpret = global i64 0
34+
int Derived::*ptrReinterpret = reinterpret_cast<int Derived::*>(&Base::baseField1);

clang/test/CIR/crashes/constexpr-cast.cpp

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
22
// XFAIL: *
33
//
4-
// Constant expression NYI
5-
// Location: CIRGenExprConst.cpp:1006
6-
//
7-
// Original failure: exprconst_1006 from LLVM build
8-
// Reduced from /tmp/HexagonAttributeParser-40f1ed.cpp
4+
// Member function pointer emission NYI
5+
// Location: CIRGenExprConst.cpp:2045
96

107
class a {
118
public:

0 commit comments

Comments
 (0)