From 55489131b28dc60280df728b408ff684164c1f1c Mon Sep 17 00:00:00 2001 From: Andy Kaylor Date: Thu, 9 Oct 2025 16:33:38 -0700 Subject: [PATCH] [CIR] Handle dynamic cast to null This adds support for handling dynamic casts that are known at compile time to always result in a null pointer. For pointer casts, this emits a null pointer value. For reference casts, it calls the __bad_cast function. --- clang/lib/CIR/CodeGen/CIRGenCXXABI.h | 2 ++ clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp | 26 +++++++++++--- clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp | 7 ++++ clang/test/CIR/CodeGen/dynamic-cast-exact.cpp | 34 +++++++++++++++++++ 4 files changed, 65 insertions(+), 4 deletions(-) diff --git a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h index c78f9b0ea8a26..d3c7dac04dc67 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h +++ b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h @@ -124,6 +124,8 @@ class CIRGenCXXABI { virtual void emitRethrow(CIRGenFunction &cgf, bool isNoReturn) = 0; virtual void emitThrow(CIRGenFunction &cgf, const CXXThrowExpr *e) = 0; + virtual void emitBadCastCall(CIRGenFunction &cgf, mlir::Location loc) = 0; + virtual mlir::Attribute getAddrOfRTTIDescriptor(mlir::Location loc, QualType ty) = 0; diff --git a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp index fe9e21064263b..a3cdf192bf9b8 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp @@ -801,6 +801,26 @@ void CIRGenFunction::emitDeleteCall(const FunctionDecl *deleteFD, emitNewDeleteCall(*this, deleteFD, deleteFTy, deleteArgs); } +static mlir::Value emitDynamicCastToNull(CIRGenFunction &cgf, + mlir::Location loc, QualType destTy) { + mlir::Type destCIRTy = cgf.convertType(destTy); + assert(mlir::isa(destCIRTy) && + "result of dynamic_cast should be a ptr"); + + if (!destTy->isPointerType()) { + mlir::Region *currentRegion = cgf.getBuilder().getBlock()->getParent(); + /// C++ [expr.dynamic.cast]p9: + /// A failed cast to reference type throws std::bad_cast + cgf.cgm.getCXXABI().emitBadCastCall(cgf, loc); + + // The call to bad_cast will terminate the current block. Create a new block + // to hold any follow up code. + cgf.getBuilder().createBlock(currentRegion, currentRegion->end()); + } + + return cgf.getBuilder().getNullPtr(destCIRTy, loc); +} + mlir::Value CIRGenFunction::emitDynamicCast(Address thisAddr, const CXXDynamicCastExpr *dce) { mlir::Location loc = getLoc(dce->getSourceRange()); @@ -831,10 +851,8 @@ mlir::Value CIRGenFunction::emitDynamicCast(Address thisAddr, assert(srcRecordTy->isRecordType() && "source type must be a record type!"); assert(!cir::MissingFeatures::emitTypeCheck()); - if (dce->isAlwaysNull()) { - cgm.errorNYI(dce->getSourceRange(), "emitDynamicCastToNull"); - return {}; - } + if (dce->isAlwaysNull()) + return emitDynamicCastToNull(*this, loc, destTy); auto destCirTy = mlir::cast(convertType(destTy)); return cgm.getCXXABI().emitDynamicCast(*this, loc, srcRecordTy, destRecordTy, diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp index f7c4d181cba66..2dce0b16e3043 100644 --- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp @@ -120,6 +120,8 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI { return true; } + void emitBadCastCall(CIRGenFunction &cgf, mlir::Location loc) override; + mlir::Value getVirtualBaseClassOffset(mlir::Location loc, CIRGenFunction &cgf, Address thisAddr, const CXXRecordDecl *classDecl, @@ -1883,6 +1885,11 @@ static void emitCallToBadCast(CIRGenFunction &cgf, mlir::Location loc) { cgf.getBuilder().clearInsertionPoint(); } +void CIRGenItaniumCXXABI::emitBadCastCall(CIRGenFunction &cgf, + mlir::Location loc) { + emitCallToBadCast(cgf, loc); +} + // TODO(cir): This could be shared with classic codegen. static CharUnits computeOffsetHint(ASTContext &astContext, const CXXRecordDecl *src, diff --git a/clang/test/CIR/CodeGen/dynamic-cast-exact.cpp b/clang/test/CIR/CodeGen/dynamic-cast-exact.cpp index e3b853321ac7d..9ddb68fbf7355 100644 --- a/clang/test/CIR/CodeGen/dynamic-cast-exact.cpp +++ b/clang/test/CIR/CodeGen/dynamic-cast-exact.cpp @@ -172,3 +172,37 @@ B *offset_cast(A *a) { // OGCG-NEXT: br label %[[LABEL_END]] // OGCG: [[LABEL_END]]: // OGCG-NEXT: phi ptr [ %[[RESULT]], %[[LABEL_NOTNULL]] ], [ null, %[[LABEL_NULL]] ] + +Derived *ptr_cast_always_fail(Base2 *ptr) { + return dynamic_cast(ptr); + } + +// CIR: cir.func {{.*}} @_Z20ptr_cast_always_failP5Base2 +// CIR: %{{.+}} = cir.load{{.*}} %{{.+}} : !cir.ptr>, !cir.ptr +// CIR-NEXT: %[[RESULT:.*]] = cir.const #cir.ptr : !cir.ptr +// CIR-NEXT: cir.store %[[RESULT]], %{{.*}} : !cir.ptr, !cir.ptr> + +// LLVM: define {{.*}} ptr @_Z20ptr_cast_always_failP5Base2 +// LLVM-NEXT: ret ptr null + +// OGCG: define {{.*}} ptr @_Z20ptr_cast_always_failP5Base2 +// OGCG-NEXT: entry: +// OGCG-NEXT: ret ptr null + +Derived &ref_cast_always_fail(Base2 &ref) { + return dynamic_cast(ref); +} + +// CIR: cir.func {{.*}} @_Z20ref_cast_always_failR5Base2 +// CIR: %{{.+}} = cir.load{{.*}} %{{.+}} : !cir.ptr>, !cir.ptr +// CIR-NEXT: cir.call @__cxa_bad_cast() : () -> () +// CIR-NEXT: cir.unreachable + +// LLVM: define {{.*}} ptr @_Z20ref_cast_always_failR5Base2 +// LLVM-NEXT: tail call void @__cxa_bad_cast() +// LLVM-NEXT: unreachable + +// OGCG: define {{.*}} ptr @_Z20ref_cast_always_failR5Base2 +// OGCG-NEXT: entry: +// OGCG-NEXT: tail call void @__cxa_bad_cast() +// OGCG-NEXT: unreachable