[clang] [CIR] Add support for exact dynamic casts (PR #164007)
Sirui Mu via cfe-commits
cfe-commits at lists.llvm.org
Tue Oct 21 08:51:06 PDT 2025
================
@@ -1953,6 +1963,136 @@ static Address emitDynamicCastToVoid(CIRGenFunction &cgf, mlir::Location loc,
return Address{ptr, src.getAlignment()};
}
+static mlir::Value emitExactDynamicCast(CIRGenItaniumCXXABI &abi,
+ CIRGenFunction &cgf, mlir::Location loc,
+ QualType srcRecordTy,
+ QualType destRecordTy,
+ cir::PointerType destCIRTy,
+ bool isRefCast, Address src) {
+ // Find all the inheritance paths from SrcRecordTy to DestRecordTy.
+ const CXXRecordDecl *srcDecl = srcRecordTy->getAsCXXRecordDecl();
+ const CXXRecordDecl *destDecl = destRecordTy->getAsCXXRecordDecl();
+ CXXBasePaths paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true,
+ /*DetectVirtual=*/false);
+ (void)destDecl->isDerivedFrom(srcDecl, paths);
+
+ // Find an offset within `destDecl` where a `srcDecl` instance and its vptr
+ // might appear.
+ std::optional<CharUnits> offset;
+ for (const CXXBasePath &path : paths) {
+ // dynamic_cast only finds public inheritance paths.
+ if (path.Access != AS_public)
+ continue;
+
+ CharUnits pathOffset;
+ for (const CXXBasePathElement &pathElement : path) {
+ // Find the offset along this inheritance step.
+ const CXXRecordDecl *base =
+ pathElement.Base->getType()->getAsCXXRecordDecl();
+ if (pathElement.Base->isVirtual()) {
+ // For a virtual base class, we know that the derived class is exactly
+ // destDecl, so we can use the vbase offset from its layout.
+ const ASTRecordLayout &layout =
+ cgf.getContext().getASTRecordLayout(destDecl);
+ pathOffset = layout.getVBaseClassOffset(base);
+ } else {
+ const ASTRecordLayout &layout =
+ cgf.getContext().getASTRecordLayout(pathElement.Class);
+ pathOffset += layout.getBaseClassOffset(base);
+ }
+ }
+
+ if (!offset) {
+ offset = pathOffset;
+ } else if (offset != pathOffset) {
+ // base appears in at least two different places. Find the most-derived
+ // object and see if it's a DestDecl. Note that the most-derived object
+ // must be at least as aligned as this base class subobject, and must
+ // have a vptr at offset 0.
+ src = emitDynamicCastToVoid(cgf, loc, srcRecordTy, src);
+ srcDecl = destDecl;
+ offset = CharUnits::Zero();
+ break;
+ }
+ }
+
+ CIRGenBuilderTy &builder = cgf.getBuilder();
+
+ if (!offset) {
+ // If there are no public inheritance paths, the cast always fails.
+ mlir::Value nullPtrValue = builder.getNullPtr(destCIRTy, loc);
+ if (isRefCast) {
+ mlir::Region *currentRegion = builder.getBlock()->getParent();
+ emitCallToBadCast(cgf, loc);
+
+ // The call to bad_cast will terminate the block. Create a new block to
+ // hold any follow up code.
+ builder.createBlock(currentRegion, currentRegion->end());
+ }
+
+ return nullPtrValue;
+ }
+
+ // Compare the vptr against the expected vptr for the destination type at
+ // this offset. Note that we do not know what type src points to in the case
+ // where the derived class multiply inherits from the base class so we can't
+ // use getVTablePtr, so we load the vptr directly instead.
+
+ mlir::Value expectedVPtr =
+ abi.getVTableAddressPoint(BaseSubobject(srcDecl, *offset), destDecl);
+
+ // TODO(cir): handle address space here.
+ assert(!cir::MissingFeatures::addressSpace());
+ mlir::Type vptrTy = expectedVPtr.getType();
+ mlir::Type vptrPtrTy = builder.getPointerTo(vptrTy);
+ Address srcVPtrPtr(builder.createBitcast(src.getPointer(), vptrPtrTy),
+ src.getAlignment());
+ mlir::Value srcVPtr = builder.createLoad(loc, srcVPtrPtr);
+
+ // TODO(cir): decorate SrcVPtr with TBAA info.
+ assert(!cir::MissingFeatures::opTBAA());
+
+ mlir::Value success =
+ builder.createCompare(loc, cir::CmpOpKind::eq, srcVPtr, expectedVPtr);
+
+ auto emitCastResult = [&] {
+ if (offset->isZero())
+ return builder.createBitcast(src.getPointer(), destCIRTy);
+
+ // TODO(cir): handle address space here.
+ assert(!cir::MissingFeatures::addressSpace());
+ mlir::Type u8PtrTy = builder.getUInt8PtrTy();
+
+ mlir::Value strideToApply =
+ builder.getConstInt(loc, builder.getUInt64Ty(), offset->getQuantity());
+ mlir::Value srcU8Ptr = builder.createBitcast(src.getPointer(), u8PtrTy);
+ mlir::Value resultU8Ptr = cir::PtrStrideOp::create(builder, loc, u8PtrTy,
+ srcU8Ptr, strideToApply);
----------------
Lancern wrote:
Could we have a test that examines this path?
https://github.com/llvm/llvm-project/pull/164007
More information about the cfe-commits
mailing list