Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenCXXABI.h
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,13 @@ class CIRGenCXXABI {

virtual void emitBadCastCall(CIRGenFunction &CGF, mlir::Location loc) = 0;

virtual void emitBadTypeidCall(CIRGenFunction &cgf) = 0;
virtual bool shouldTypeidBeNullChecked(QualType srcRecordTy) = 0;

virtual mlir::Value emitTypeid(CIRGenFunction &cgf, mlir::Location loc,
QualType srcRecordTy, Address thisPtr,
mlir::Type stdTypeInfoPtrTy) = 0;

virtual mlir::Value
getVirtualBaseClassOffset(mlir::Location loc, CIRGenFunction &CGF,
Address This, const CXXRecordDecl *ClassDecl,
Expand Down
60 changes: 56 additions & 4 deletions clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1810,6 +1810,58 @@ mlir::Value CIRGenFunction::emitDynamicCast(Address ThisAddr,
destCirTy, isRefCast, ThisAddr);
}

static mlir::Value emitTypeidFromVTable(CIRGenFunction &cgf, mlir::Location loc,
const Expr *e,
mlir::Type stdTypeInfoPtrTy,
bool hasNullCheck) {
// Get the vtable pointer.
Address thisPtr = cgf.emitLValue(e).getAddress();

QualType srcRecordTy = e->getType();

// C++ [class.cdtor]p4:
// If the operand of typeid refers to the object under construction or
// destruction and the static type of the operand is neither the constructor
// or destructor's class nor one of its bases, the behavior is undefined.
cgf.emitTypeCheck(CIRGenFunction::TCK_DynamicOperation, e->getExprLoc(),
thisPtr.getPointer(), srcRecordTy);

// Whether we need an explicit null pointer check. For example, with the
// Microsoft ABI, if this is a call to __RTtypeid, the null pointer check and
// exception throw is inside the __RTtypeid(nullptr) call
if (hasNullCheck &&
cgf.CGM.getCXXABI().shouldTypeidBeNullChecked(srcRecordTy)) {
auto &builder = cgf.getBuilder();

// Create blocks for null and non-null paths
auto *currBlock = builder.getInsertionBlock();
auto *parentOp = currBlock->getParent()->getParentOp();
auto &region = parentOp->getRegion(0);

mlir::Block *badTypeidBlock = builder.createBlock(&region);
mlir::Block *endBlock = builder.createBlock(&region);

// Check if pointer is null
builder.setInsertionPointToEnd(currBlock);
mlir::Value nullPtr =
builder.getNullPtr(thisPtr.getPointer().getType(), loc);
mlir::Value isNull = builder.createCompare(loc, cir::CmpOpKind::eq,
thisPtr.getPointer(), nullPtr);

builder.create<cir::BrCondOp>(loc, isNull, badTypeidBlock, endBlock);

// Emit bad typeid path
builder.setInsertionPointToEnd(badTypeidBlock);
cgf.CGM.getCXXABI().emitBadTypeidCall(cgf);

// Continue on non-null path
builder.setInsertionPointToEnd(endBlock);
}

return cgf.CGM.getCXXABI().emitTypeid(cgf, loc, srcRecordTy, thisPtr,
stdTypeInfoPtrTy);
}

mlir::Value CIRGenFunction::emitCXXTypeidExpr(const CXXTypeidExpr *E) {
auto loc = getLoc(E->getSourceRange());

Expand Down Expand Up @@ -1843,10 +1895,10 @@ mlir::Value CIRGenFunction::emitCXXTypeidExpr(const CXXTypeidExpr *E) {
// If the operand is already the most derived object, no need to look up
// vtable.
if (E->isPotentiallyEvaluated() && !E->isMostDerived(getContext())) {
// This requires emitting code similar to dynamic_cast that looks up the
// type_info pointer from the vtable. Note that this path also needs to
// handle null checking when E->hasNullCheck() is true.
llvm_unreachable("NYI: typeid with polymorphic types (vtable lookup)");
// Polymorphic case: need runtime vtable lookup
mlir::Type typeInfoPtrTy = builder.getUInt8PtrTy();
return emitTypeidFromVTable(*this, loc, E->getExprOperand(), typeInfoPtrTy,
E->hasNullCheck());
}

// For non-polymorphic types, just return the static RTTI descriptor.
Expand Down
88 changes: 88 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,12 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI {

void emitBadCastCall(CIRGenFunction &CGF, mlir::Location loc) override;

void emitBadTypeidCall(CIRGenFunction &CGF) override;
bool shouldTypeidBeNullChecked(QualType SrcRecordTy) override;
mlir::Value emitTypeid(CIRGenFunction &CGF, mlir::Location loc,
QualType SrcRecordTy, Address ThisPtr,
mlir::Type StdTypeInfoPtrTy) override;

mlir::Value
getVirtualBaseClassOffset(mlir::Location loc, CIRGenFunction &CGF,
Address This, const CXXRecordDecl *ClassDecl,
Expand Down Expand Up @@ -2702,6 +2708,88 @@ void CIRGenItaniumCXXABI::emitBadCastCall(CIRGenFunction &CGF,
emitCallToBadCast(CGF, loc);
}

void CIRGenItaniumCXXABI::emitBadTypeidCall(CIRGenFunction &CGF) {
auto loc = CGF.getLoc(SourceLocation());
// TODO: When exception support is complete, emit throw std::bad_typeid
// For now, emit unreachable since calling typeid on null is UB
cir::UnreachableOp::create(CGF.getBuilder(), loc);
CGF.getBuilder().clearInsertionPoint();
}

bool CIRGenItaniumCXXABI::shouldTypeidBeNullChecked(QualType srcRecordTy) {
// Match CodeGen behavior: always check for null.
// TODO(cir): The Itanium ABI 2.9.5p3 states only pointer operands need
// null checking (references cannot be null), but CodeGen conservatively
// always returns true. We match this behavior for consistency.
// Consider optimizing to: return srcRecordTy->isPointerType();
return true;
}

mlir::Value CIRGenItaniumCXXABI::emitTypeid(CIRGenFunction &CGF,
mlir::Location loc,
QualType srcRecordTy,
Address thisPtr,
mlir::Type stdTypeInfoPtrTy) {
auto &builder = CGF.getBuilder();
auto *classDecl = srcRecordTy->castAsCXXRecordDecl();

// Get the vtable pointer from the object
mlir::Value vTable = CGF.getVTablePtr(loc, thisPtr, classDecl);

mlir::Value typeInfoPtr;

if (CGM.getItaniumVTableContext().isRelativeLayout()) {
// Relative layout: type_info offset is at vptr[-4] (4 bytes before vptr)
// Load the offset and add it to the vtable pointer
auto int32Ty = builder.getSInt32Ty();
auto int8PtrTy = builder.getUInt8PtrTy();

// Cast vtable to i8* for byte arithmetic
auto vTableBytes = builder.createBitcast(loc, vTable, int8PtrTy);

// Get address of the offset: vtable - 4
auto offsetAddrPtr = cir::PtrStrideOp::create(
builder, loc, builder.getPointerTo(int32Ty), vTableBytes,
builder.getConstInt(loc, builder.getSInt64Ty(), -4));

// Load the 32-bit offset
auto offsetAddr =
Address(offsetAddrPtr, int32Ty, CharUnits::fromQuantity(4));
auto offset = builder.createLoad(loc, offsetAddr);

// Sign-extend offset to pointer width
auto offset64 = cir::CastOp::create(builder, loc, builder.getSInt64Ty(),
cir::CastKind::integral, offset);

// Add offset to vtable pointer: vtable + offset
auto typeInfoBytes = cir::PtrStrideOp::create(builder, loc, int8PtrTy,
vTableBytes, offset64);

// Cast result to type_info pointer type
typeInfoPtr = builder.createBitcast(loc, typeInfoBytes, stdTypeInfoPtrTy);

} else {
// Absolute layout: type_info* is at vtable[-1]
// GEP to get address, then load

// Cast vtable pointer to a regular pointer type for ptr_stride
auto vTablePtr = builder.createBitcast(
loc, vTable, builder.getPointerTo(stdTypeInfoPtrTy));

// Get vtable[-1]
auto typeInfoAddrPtr = cir::PtrStrideOp::create(
builder, loc, builder.getPointerTo(stdTypeInfoPtrTy), vTablePtr,
builder.getConstInt(loc, builder.getSInt64Ty(), -1));

// Load the type_info pointer
auto typeInfoAddr =
Address(typeInfoAddrPtr, stdTypeInfoPtrTy, CGF.getPointerAlign());
typeInfoPtr = builder.createLoad(loc, typeInfoAddr);
}

return typeInfoPtr;
}

static CharUnits computeOffsetHint(ASTContext &astContext,
const CXXRecordDecl *Src,
const CXXRecordDecl *Dst) {
Expand Down
91 changes: 91 additions & 0 deletions clang/test/CIR/CodeGen/typeid-polymorphic.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.ll
// RUN: FileCheck --input-file=%t.ll %s --check-prefix=LLVM
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.og.ll
// RUN: FileCheck --input-file=%t.og.ll %s --check-prefix=OGCG

namespace std {
class type_info {
public:
virtual ~type_info();
};
}

// Basic polymorphic class hierarchy
struct Base {
virtual ~Base() {}
int x;
};

struct Derived : Base {
int y;
};

extern void use_typeinfo(const std::type_info*);

// Test 1: Basic polymorphic typeid - pointer
// CIR-LABEL: cir.func dso_local @_Z22test_polymorphic_basicP4Base
void test_polymorphic_basic(Base* ptr) {
// CIR: cir.vtable.get_vptr
// CIR: cir.load{{.*}}!cir.vptr
// CIR: cir.cast bitcast
// CIR: cir.ptr_stride
// CIR: cir.load

// LLVM-LABEL: @_Z22test_polymorphic_basicP4Base
// LLVM: load ptr, ptr %
// LLVM: getelementptr ptr, ptr %{{.*}}, i64 -1
// LLVM: load ptr, ptr %

// OGCG-LABEL: @_Z22test_polymorphic_basicP4Base
// OGCG: load ptr, ptr %
// OGCG: getelementptr inbounds ptr, ptr %{{.*}}, i64 -1
// OGCG: load ptr, ptr %
use_typeinfo(&typeid(*ptr));
}

// Test 2: Polymorphic typeid - reference (no null check)
// CIR-LABEL: cir.func dso_local @_Z14test_referenceR4Base
void test_reference(Base& ref) {
// CIR-NOT: cir.cmp(eq
// CIR: cir.vtable.get_vptr
// CIR: cir.ptr_stride

// LLVM-LABEL: @_Z14test_referenceR4Base
// LLVM: load ptr, ptr %
// LLVM: getelementptr ptr, ptr %{{.*}}, i64 -1

// OGCG-LABEL: @_Z14test_referenceR4Base
// OGCG: load ptr, ptr %
// OGCG: getelementptr inbounds ptr, ptr %{{.*}}, i64 -1
use_typeinfo(&typeid(ref));
}

// Test 3: Derived class pointer
// CIR-LABEL: cir.func dso_local @_Z20test_derived_pointerP7Derived
void test_derived_pointer(Derived* ptr) {
// CIR: cir.vtable.get_vptr
// CIR: cir.ptr_stride

// LLVM-LABEL: @_Z20test_derived_pointerP7Derived
// LLVM: getelementptr ptr, ptr %{{.*}}, i64 -1

// OGCG-LABEL: @_Z20test_derived_pointerP7Derived
// OGCG: getelementptr inbounds ptr, ptr %{{.*}}, i64 -1
use_typeinfo(&typeid(*ptr));
}

// Test 4: Const qualified pointer
// CIR-LABEL: cir.func dso_local @_Z14test_const_ptrPK4Base
void test_const_ptr(const Base* ptr) {
// CIR: cir.vtable.get_vptr
// CIR: cir.ptr_stride

// LLVM-LABEL: @_Z14test_const_ptrPK4Base
// LLVM: getelementptr ptr, ptr %{{.*}}, i64 -1

// OGCG-LABEL: @_Z14test_const_ptrPK4Base
// OGCG: getelementptr inbounds ptr, ptr %{{.*}}, i64 -1
use_typeinfo(&typeid(*ptr));
}