From 511dad63c936bae1b4b809c01057903689ec50c7 Mon Sep 17 00:00:00 2001 From: Sirui Mu Date: Sun, 28 Jan 2024 13:40:01 +0800 Subject: [PATCH] [CIR][CodeGen] initial support for dynamic_cast This patch introduces CIR CodeGen support for dynamic_cast. As an initial step, this patch only adds support for cases where the destination type is not void*. Support for dynamic_cast to void* will be added in future patches. --- clang/lib/CIR/CodeGen/CIRGenBuilder.h | 4 + clang/lib/CIR/CodeGen/CIRGenCXXABI.h | 11 ++ clang/lib/CIR/CodeGen/CIRGenCall.cpp | 13 ++ clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 8 +- clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp | 85 +++++++++++ clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp | 7 +- clang/lib/CIR/CodeGen/CIRGenFunction.h | 8 + clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp | 144 ++++++++++++++++++ clang/lib/CIR/CodeGen/CIRGenModule.cpp | 11 ++ clang/lib/CIR/CodeGen/CIRGenModule.h | 10 ++ clang/test/CIR/CodeGen/dynamic-cast.cpp | 52 +++++++ 11 files changed, 349 insertions(+), 4 deletions(-) create mode 100644 clang/test/CIR/CodeGen/dynamic-cast.cpp diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h index e3f15227fb21..b71f878a444d 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h +++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h @@ -849,6 +849,10 @@ class CIRGenBuilderTy : public CIRBaseBuilderTy { alloca->moveAfter(*std::prev(allocas.end())); } } + + mlir::Value createPtrIsNull(mlir::Value ptr) { + return createNot(createPtrToBoolCast(ptr)); + } }; } // namespace cir diff --git a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h index a4e7808bf36b..b7dc7b66a4f3 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h +++ b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h @@ -299,6 +299,17 @@ class CIRGenCXXABI { virtual void buildRethrow(CIRGenFunction &CGF, bool isNoReturn) = 0; virtual void buildThrow(CIRGenFunction &CGF, const CXXThrowExpr *E) = 0; + + virtual void buildBadCastCall(CIRGenFunction &CGF, mlir::Location loc) = 0; + + virtual bool shouldDynamicCastCallBeNullChecked(bool SrcIsPtr, + QualType SrcRecordTy) = 0; + + virtual mlir::Value buildDynamicCastCall(CIRGenFunction &CGF, + mlir::Location Loc, Address Value, + QualType SrcRecordTy, + QualType DestTy, + QualType DestRecordTy) = 0; }; /// Creates and Itanium-family ABI diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp index fb0322a0f341..301d6e1ab05c 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp @@ -716,6 +716,19 @@ RValue CIRGenFunction::GetUndefRValue(QualType Ty) { return RValue::get(nullptr); } +mlir::Value CIRGenFunction::buildRuntimeCall(mlir::Location loc, + mlir::cir::FuncOp callee, + ArrayRef args) { + auto call = builder.create(loc, callee, args); + assert(call->getNumResults() <= 1 && + "runtime functions have at most 1 result"); + + if (call->getNumResults() == 0) + return nullptr; + + return call->getResult(0); +} + void CIRGenFunction::buildCallArg(CallArgList &args, const Expr *E, QualType type) { // TODO: Add the DisableDebugLocationUpdates helper diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index 950ac54d6f96..553bf50ee8c0 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -19,6 +19,7 @@ #include "CIRGenValue.h" #include "UnimplementedFeatureGuarding.h" +#include "clang/AST/ExprCXX.h" #include "clang/AST/GlobalDecl.h" #include "clang/Basic/Builtins.h" #include "clang/CIR/Dialect/IR/CIRDialect.h" @@ -1565,7 +1566,10 @@ LValue CIRGenFunction::buildCastLValue(const CastExpr *E) { assert(0 && "NYI"); case CK_Dynamic: { - assert(0 && "NYI"); + LValue LV = buildLValue(E->getSubExpr()); + Address V = LV.getAddress(); + const auto *DCE = cast(E); + return MakeNaturalAlignAddrLValue(buildDynamicCast(V, DCE), E->getType()); } case CK_ConstructorConversion: @@ -2172,7 +2176,6 @@ LValue CIRGenFunction::buildLValue(const Expr *E) { return buildPredefinedLValue(cast(E)); case Expr::CStyleCastExprClass: case Expr::CXXFunctionalCastExprClass: - case Expr::CXXDynamicCastExprClass: case Expr::CXXReinterpretCastExprClass: case Expr::CXXConstCastExprClass: case Expr::CXXAddrspaceCastExprClass: @@ -2181,6 +2184,7 @@ LValue CIRGenFunction::buildLValue(const Expr *E) { << E->getStmtClassName() << "'"; assert(0 && "Use buildCastLValue below, remove me when adding testcase"); case Expr::CXXStaticCastExprClass: + case Expr::CXXDynamicCastExprClass: case Expr::ImplicitCastExprClass: return buildCastLValue(cast(E)); case Expr::OpaqueValueExprClass: diff --git a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp index 3e318abdbcd1..90f15c1b1dfd 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp @@ -911,3 +911,88 @@ void CIRGenFunction::buildDeleteCall(const FunctionDecl *DeleteFD, llvm_unreachable("NYI"); // DestroyingDeleteTag->eraseFromParent(); } } + +static mlir::Value buildDynamicCastToNull(CIRGenFunction &CGF, + mlir::Location Loc, QualType DestTy) { + mlir::Type DestCIRTy = CGF.ConvertType(DestTy); + assert(DestCIRTy.isa() && + "result of dynamic_cast should be a ptr"); + + mlir::Value NullPtrValue = CGF.getBuilder().getNullPtr(DestCIRTy, Loc); + + if (!DestTy->isPointerType()) { + /// C++ [expr.dynamic.cast]p9: + /// A failed cast to reference type throws std::bad_cast + CGF.CGM.getCXXABI().buildBadCastCall(CGF, Loc); + } + + return NullPtrValue; +} + +mlir::Value CIRGenFunction::buildDynamicCast(Address ThisAddr, + const CXXDynamicCastExpr *DCE) { + auto loc = getLoc(DCE->getSourceRange()); + + CGM.buildExplicitCastExprType(DCE, this); + QualType destTy = DCE->getTypeAsWritten(); + QualType srcTy = DCE->getSubExpr()->getType(); + + // C++ [expr.dynamic.cast]p7: + // If T is "pointer to cv void," then the result is a pointer to the most + // derived object pointed to by v. + bool isDynCastToVoid = destTy->isVoidPointerType(); + QualType srcRecordTy; + QualType destRecordTy; + if (isDynCastToVoid) { + srcRecordTy = srcTy->getPointeeType(); + // No DestRecordTy. + } else if (const PointerType *DestPTy = destTy->getAs()) { + srcRecordTy = srcTy->castAs()->getPointeeType(); + destRecordTy = DestPTy->getPointeeType(); + } else { + srcRecordTy = srcTy; + destRecordTy = destTy->castAs()->getPointeeType(); + } + + buildTypeCheck(TCK_DynamicOperation, DCE->getExprLoc(), ThisAddr.getPointer(), + srcRecordTy); + + if (DCE->isAlwaysNull()) + return buildDynamicCastToNull(*this, loc, destTy); + + assert(srcRecordTy->isRecordType() && "source type must be a record type!"); + + // C++ [expr.dynamic.cast]p4: + // If the value of v is a null pointer value in the pointer case, the result + // is the null pointer value of type T. + bool shouldNullCheckSrcValue = + CGM.getCXXABI().shouldDynamicCastCallBeNullChecked(srcTy->isPointerType(), + srcRecordTy); + + auto buildDynamicCastAfterNullCheck = [&]() -> mlir::Value { + if (isDynCastToVoid) + llvm_unreachable("NYI"); + else { + assert(destRecordTy->isRecordType() && + "destination type must be a record type!"); + return CGM.getCXXABI().buildDynamicCastCall( + *this, loc, ThisAddr, srcRecordTy, destTy, destRecordTy); + } + }; + + if (!shouldNullCheckSrcValue) + return buildDynamicCastAfterNullCheck(); + + mlir::Value srcValueIsNull = builder.createPtrIsNull(ThisAddr.getPointer()); + return builder + .create( + loc, srcValueIsNull, + [&](mlir::OpBuilder &, mlir::Location) { + builder.createYield(loc, + buildDynamicCastToNull(*this, loc, destTy)); + }, + [&](mlir::OpBuilder &, mlir::Location) { + builder.createYield(loc, buildDynamicCastAfterNullCheck()); + }) + .getResult(); +} diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp index 0f85e0da58dd..99b500008631 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp @@ -1399,8 +1399,11 @@ mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *CE) { // the alignment. return CGF.buildPointerWithAlignment(CE).getPointer(); } - case CK_Dynamic: - llvm_unreachable("NYI"); + case CK_Dynamic: { + Address V = CGF.buildPointerWithAlignment(E); + const auto *DCE = cast(CE); + return CGF.buildDynamicCast(V, DCE); + } case CK_ArrayToPointerDecay: return CGF.buildArrayToPointerDecay(E).getPointer(); case CK_FunctionToPointerDecay: diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index aee30a35eb8c..e493f57d6804 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -354,6 +354,9 @@ class CIRGenFunction : public CIRGenTypeCache { TCK_MemberCall, /// Checking the 'this' pointer for a constructor call. TCK_ConstructorCall, + /// Checking the operand of a dynamic_cast or a typeid expression. Must be + /// null or an object within its lifetime. + TCK_DynamicOperation }; // Holds coroutine data if the current function is a coroutine. We use a @@ -630,6 +633,8 @@ class CIRGenFunction : public CIRGenTypeCache { QualType DeleteTy, mlir::Value NumElements = nullptr, CharUnits CookieSize = CharUnits()); + mlir::Value buildDynamicCast(Address ThisAddr, const CXXDynamicCastExpr *DCE); + mlir::Value createLoad(const clang::VarDecl *VD, const char *Name); mlir::Value buildScalarPrePostIncDec(const UnaryOperator *E, LValue LV, @@ -794,6 +799,9 @@ class CIRGenFunction : public CIRGenTypeCache { RValue buildCallExpr(const clang::CallExpr *E, ReturnValueSlot ReturnValue = ReturnValueSlot()); + mlir::Value buildRuntimeCall(mlir::Location loc, mlir::cir::FuncOp callee, + ArrayRef args = {}); + /// Create a check for a function parameter that may potentially be /// declared as non-null. void buildNonNullArgCheck(RValue RV, QualType ArgType, SourceLocation ArgLoc, diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp index 735998f0b20e..75bc4931f13e 100644 --- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp @@ -281,6 +281,18 @@ class CIRGenItaniumCXXABI : public cir::CIRGenCXXABI { return Args.size() - 1; } + void buildBadCastCall(CIRGenFunction &CGF, mlir::Location loc) override; + + bool shouldDynamicCastCallBeNullChecked(bool SrcIsPtr, + QualType SrcRecordTy) override { + return SrcIsPtr; + } + + mlir::Value buildDynamicCastCall(CIRGenFunction &CGF, mlir::Location Loc, + Address Value, QualType SrcRecordTy, + QualType DestTy, + QualType DestRecordTy) override; + /**************************** RTTI Uniqueness ******************************/ protected: /// Returns true if the ABI requires RTTI type_info objects to be unique @@ -2173,3 +2185,135 @@ void CIRGenItaniumCXXABI::buildThrow(CIRGenFunction &CGF, builder.create(CGF.getLoc(E->getSourceRange()), exceptionPtr, typeInfo.getSymbol(), dtor); } + +static mlir::cir::FuncOp getBadCastFn(CIRGenFunction &CGF) { + // Prototype: void __cxa_bad_cast(); + mlir::cir::FuncType FTy = + CGF.getBuilder().getFuncType({}, CGF.getBuilder().getVoidTy()); + return CGF.CGM.getOrCreateRuntimeFunction(FTy, "__cxa_bad_cast"); +} + +void CIRGenItaniumCXXABI::buildBadCastCall(CIRGenFunction &CGF, + mlir::Location loc) { + CGF.buildRuntimeCall(loc, getBadCastFn(CGF)); + // TODO(cir): mark the current insertion point as unreachable. +} + +static CharUnits computeOffsetHint(ASTContext &Context, + const CXXRecordDecl *Src, + const CXXRecordDecl *Dst) { + CXXBasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true, + /*DetectVirtual=*/false); + + // If Dst is not derived from Src we can skip the whole computation below and + // return that Src is not a public base of Dst. Record all inheritance paths. + if (!Dst->isDerivedFrom(Src, Paths)) + return CharUnits::fromQuantity(-2ULL); + + unsigned NumPublicPaths = 0; + CharUnits Offset; + + // Now walk all possible inheritance paths. + for (const CXXBasePath &Path : Paths) { + if (Path.Access != AS_public) // Ignore non-public inheritance. + continue; + + ++NumPublicPaths; + + for (const CXXBasePathElement &PathElement : Path) { + // If the path contains a virtual base class we can't give any hint. + // -1: no hint. + if (PathElement.Base->isVirtual()) + return CharUnits::fromQuantity(-1ULL); + + if (NumPublicPaths > 1) // Won't use offsets, skip computation. + continue; + + // Accumulate the base class offsets. + const ASTRecordLayout &L = Context.getASTRecordLayout(PathElement.Class); + Offset += L.getBaseClassOffset( + PathElement.Base->getType()->getAsCXXRecordDecl()); + } + } + + // -2: Src is not a public base of Dst. + if (NumPublicPaths == 0) + return CharUnits::fromQuantity(-2ULL); + + // -3: Src is a multiple public base type but never a virtual base type. + if (NumPublicPaths > 1) + return CharUnits::fromQuantity(-3ULL); + + // Otherwise, the Src type is a unique public nonvirtual base type of Dst. + // Return the offset of Src from the origin of Dst. + return Offset; +} + +static mlir::cir::FuncOp getItaniumDynamicCastFn(CIRGenFunction &CGF) { + // Prototype: + // void *__dynamic_cast(const void *sub, + // global_as const abi::__class_type_info *src, + // global_as const abi::__class_type_info *dst, + // std::ptrdiff_t src2dst_offset); + + mlir::Type VoidPtrTy = CGF.VoidPtrTy; + mlir::Type RTTIPtrTy = CGF.getBuilder().getUInt8PtrTy(); + mlir::Type PtrDiffTy = CGF.ConvertType(CGF.getContext().getPointerDiffType()); + + // TODO(cir): mark the function as nowind readonly. + + mlir::cir::FuncType FTy = CGF.getBuilder().getFuncType( + {VoidPtrTy, RTTIPtrTy, RTTIPtrTy, PtrDiffTy}, VoidPtrTy); + return CGF.CGM.getOrCreateRuntimeFunction(FTy, "__dynamic_cast"); +} + +mlir::Value CIRGenItaniumCXXABI::buildDynamicCastCall( + CIRGenFunction &CGF, mlir::Location Loc, Address Value, + QualType SrcRecordTy, QualType DestTy, QualType DestRecordTy) { + mlir::Type ptrdiffTy = CGF.ConvertType(CGF.getContext().getPointerDiffType()); + + mlir::Value srcRtti = CGF.getBuilder().getConstant( + Loc, + CGF.CGM.getAddrOfRTTIDescriptor(Loc, SrcRecordTy.getUnqualifiedType()) + .cast()); + mlir::Value destRtti = CGF.getBuilder().getConstant( + Loc, + CGF.CGM.getAddrOfRTTIDescriptor(Loc, DestRecordTy.getUnqualifiedType()) + .cast()); + + // Compute the offset hint. + const CXXRecordDecl *srcDecl = SrcRecordTy->getAsCXXRecordDecl(); + const CXXRecordDecl *destDecl = DestRecordTy->getAsCXXRecordDecl(); + mlir::Value offsetHint = CGF.getBuilder().getConstAPInt( + Loc, ptrdiffTy, + llvm::APSInt::get(computeOffsetHint(CGF.getContext(), srcDecl, destDecl) + .getQuantity())); + + // Emit the call to __dynamic_cast. + mlir::Value srcPtr = + CGF.getBuilder().createBitcast(Value.getPointer(), CGF.VoidPtrTy); + mlir::Value args[4] = {srcPtr, srcRtti, destRtti, offsetHint}; + mlir::Value castedPtr = + CGF.buildRuntimeCall(Loc, getItaniumDynamicCastFn(CGF), args); + + assert(castedPtr.getType().isa() && + "the return value of __dynamic_cast should be a ptr"); + + /// C++ [expr.dynamic.cast]p9: + /// A failed cast to reference type throws std::bad_cast + if (DestTy->isReferenceType()) { + // Emit a cir.if that checks the casted value. + mlir::Value castedValueIsNull = CGF.getBuilder().createPtrIsNull(castedPtr); + CGF.getBuilder().create( + Loc, castedValueIsNull, false, [&](mlir::OpBuilder &, mlir::Location) { + buildBadCastCall(CGF, Loc); + // TODO(cir): remove this once buildBadCastCall inserts unreachable + CGF.getBuilder().createYield(Loc); + }); + } + + // Note that castedPtr is a void*. Cast it to a pointer to the destination + // type before return. + mlir::Type destCIRTy = CGF.ConvertType(DestTy); + return CGF.getBuilder().createBitcast(castedPtr, destCIRTy); +} diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index 8888459e5cc2..ebaade4082c6 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -1977,6 +1977,17 @@ CIRGenModule::createCIRFunction(mlir::Location loc, StringRef name, return f; } +mlir::cir::FuncOp +CIRGenModule::getOrCreateRuntimeFunction(mlir::cir::FuncType Ty, + StringRef Name) { + auto entry = cast_if_present(getGlobalValue(Name)); + if (entry) + return entry; + + return createCIRFunction(mlir::UnknownLoc::get(builder.getContext()), Name, + Ty, nullptr); +} + bool isDefaultedMethod(const clang::FunctionDecl *FD) { if (FD->isDefaulted() && isa(FD) && (cast(FD)->isCopyAssignmentOperator() || diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h index 900210a7c24a..b200b210b888 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.h +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -490,6 +490,13 @@ class CIRGenModule : public CIRGenTypeCache { GetAddrOfGlobal(clang::GlobalDecl GD, ForDefinition_t IsForDefinition = NotForDefinition); + // Return whether RTTI information should be emitted for this target. + bool shouldEmitRTTI(bool ForEH = false) { + return (ForEH || getLangOpts().RTTI) && !getLangOpts().CUDAIsDevice && + !(getLangOpts().OpenMP && getLangOpts().OpenMPIsTargetDevice && + getTriple().isNVPTX()); + } + // C++ related functions. void buildDeclContext(const DeclContext *DC); @@ -606,6 +613,9 @@ class CIRGenModule : public CIRGenTypeCache { mlir::cir::FuncType Ty, const clang::FunctionDecl *FD); + mlir::cir::FuncOp getOrCreateRuntimeFunction(mlir::cir::FuncType Ty, + StringRef Name); + /// Emit type info if type of an expression is a variably modified /// type. Also emit proper debug info for cast types. void buildExplicitCastExprType(const ExplicitCastExpr *E, diff --git a/clang/test/CIR/CodeGen/dynamic-cast.cpp b/clang/test/CIR/CodeGen/dynamic-cast.cpp new file mode 100644 index 000000000000..196b8bac4250 --- /dev/null +++ b/clang/test/CIR/CodeGen/dynamic-cast.cpp @@ -0,0 +1,52 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -fclangir-enable -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s + +struct Base { + virtual ~Base(); +}; +// CHECK: !ty_22Base22 = !cir.struct + +struct Derived : Base {}; +// CHECK: !ty_22Derived22 = !cir.struct + +// CHECK: cir.func private @__dynamic_cast(!cir.ptr, !cir.ptr, !cir.ptr, !s64i) -> !cir.ptr + +Derived *ptr_cast(Base *b) { + return dynamic_cast(b); +} +// CHECK: cir.func @_Z8ptr_castP4Base +// CHECK: %[[#V1:]] = cir.load %{{.+}} : cir.ptr >, !cir.ptr +// CHECK-NEXT: %[[#V2:]] = cir.cast(ptr_to_bool, %[[#V1]] : !cir.ptr), !cir.bool +// CHECK-NEXT: %[[#V3:]] = cir.unary(not, %[[#V2]]) : !cir.bool, !cir.bool +// CHECK-NEXT: %{{.+}} = cir.ternary(%[[#V3]], true { +// CHECK-NEXT: %[[#V4:]] = cir.const(#cir.ptr : !cir.ptr) : !cir.ptr +// CHECK-NEXT: cir.yield %[[#V4]] : !cir.ptr +// CHECK-NEXT: }, false { +// CHECK-NEXT: %[[#V5:]] = cir.const(#cir.global_view<@_ZTI4Base> : !cir.ptr) : !cir.ptr +// CHECK-NEXT: %[[#V6:]] = cir.const(#cir.global_view<@_ZTI7Derived> : !cir.ptr) : !cir.ptr +// CHECK-NEXT: %[[#V7:]] = cir.const(#cir.int<0> : !s64i) : !s64i +// CHECK-NEXT: %[[#V8:]] = cir.cast(bitcast, %2 : !cir.ptr), !cir.ptr +// CHECK-NEXT: %[[#V9:]] = cir.call @__dynamic_cast(%[[#V8]], %[[#V5]], %[[#V6]], %[[#V7]]) : (!cir.ptr, !cir.ptr, !cir.ptr, !s64i) -> !cir.ptr +// CHECK-NEXT: %[[#V10:]] = cir.cast(bitcast, %[[#V9]] : !cir.ptr), !cir.ptr +// CHECK-NEXT: cir.yield %[[#V10]] : !cir.ptr +// CHECK-NEXT: }) : (!cir.bool) -> !cir.ptr + +// CHECK: cir.func private @__cxa_bad_cast() + +Derived &ref_cast(Base &b) { + return dynamic_cast(b); +} + +// CHECK: cir.func @_Z8ref_castR4Base +// CHECK: %[[#V11:]] = cir.load %{{.+}} : cir.ptr >, !cir.ptr +// CHECK-NEXT: %[[#V12:]] = cir.const(#cir.global_view<@_ZTI4Base> : !cir.ptr) : !cir.ptr +// CHECK-NEXT: %[[#V13:]] = cir.const(#cir.global_view<@_ZTI7Derived> : !cir.ptr) : !cir.ptr +// CHECK-NEXT: %[[#V14:]] = cir.const(#cir.int<0> : !s64i) : !s64i +// CHECK-NEXT: %[[#V15:]] = cir.cast(bitcast, %[[#V11]] : !cir.ptr), !cir.ptr +// CHECK-NEXT: %[[#V16:]] = cir.call @__dynamic_cast(%[[#V15]], %[[#V12]], %[[#V13]], %[[#V14]]) : (!cir.ptr, !cir.ptr, !cir.ptr, !s64i) -> !cir.ptr +// CHECK-NEXT: %[[#V17:]] = cir.cast(ptr_to_bool, %[[#V16]] : !cir.ptr), !cir.bool +// CHECK-NEXT: %[[#V18:]] = cir.unary(not, %[[#V17]]) : !cir.bool, !cir.bool +// CHECK-NEXT: cir.if %[[#V18]] { +// CHECK-NEXT: cir.call @__cxa_bad_cast() : () -> () +// CHECK-NEXT: } +// CHECK-NEXT: %{{.+}} = cir.cast(bitcast, %[[#V16]] : !cir.ptr), !cir.ptr