From a1645f1835c81f8a1d5b1f1796ea070c417de0bf Mon Sep 17 00:00:00 2001 From: Nathan Lanza Date: Sat, 22 Nov 2025 20:13:18 -0800 Subject: [PATCH] Update [ghstack-poisoned] --- clang/lib/CIR/CodeGen/CIRGenCXXABI.h | 7 ++ clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp | 60 +++++++++++- clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp | 88 ++++++++++++++++++ clang/test/CIR/CodeGen/typeid-polymorphic.cpp | 91 +++++++++++++++++++ 4 files changed, 242 insertions(+), 4 deletions(-) create mode 100644 clang/test/CIR/CodeGen/typeid-polymorphic.cpp diff --git a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h index 05dbcc4cd80c..8aa576ca782a 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h +++ b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h @@ -366,6 +366,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, diff --git a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp index 2852036106e1..c0cc262db493 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp @@ -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 ®ion = parentOp->getRegion(0); + + mlir::Block *badTypeidBlock = builder.createBlock(®ion); + mlir::Block *endBlock = builder.createBlock(®ion); + + // 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(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()); @@ -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. diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp index a393e8bec03d..df51a2bdf623 100644 --- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp @@ -333,6 +333,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, @@ -2485,6 +2491,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) { diff --git a/clang/test/CIR/CodeGen/typeid-polymorphic.cpp b/clang/test/CIR/CodeGen/typeid-polymorphic.cpp new file mode 100644 index 000000000000..fefde3e1837d --- /dev/null +++ b/clang/test/CIR/CodeGen/typeid-polymorphic.cpp @@ -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)); +}