diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h index 11ad61fdb8065..b4c24d7e4a8aa 100644 --- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h +++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h @@ -451,6 +451,11 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { return createBitcast(src, getPointerTo(newPointeeTy)); } + mlir::Value createPtrIsNull(mlir::Value ptr) { + mlir::Value nullPtr = getNullPtr(ptr.getType(), ptr.getLoc()); + return createCompare(ptr.getLoc(), cir::CmpOpKind::eq, ptr, nullPtr); + } + //===--------------------------------------------------------------------===// // Binary Operators //===--------------------------------------------------------------------===// @@ -644,6 +649,12 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { return getI64IntegerAttr(size.getQuantity()); } + // Creates constant nullptr for pointer type ty. + cir::ConstantOp getNullPtr(mlir::Type ty, mlir::Location loc) { + assert(!cir::MissingFeatures::targetCodeGenInfoGetNullPointer()); + return cir::ConstantOp::create(*this, loc, getConstPtrAttr(ty, 0)); + } + /// Create a loop condition. cir::ConditionOp createCondition(mlir::Value condition) { return cir::ConditionOp::create(*this, condition.getLoc(), condition); diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index ace20868532f0..df82ca138d4b1 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -85,6 +85,7 @@ struct MissingFeatures { static bool opFuncReadOnly() { return false; } static bool opFuncSection() { return false; } static bool opFuncWillReturn() { return false; } + static bool opFuncNoReturn() { return false; } static bool setLLVMFunctionFEnvAttributes() { return false; } static bool setFunctionAttributes() { return false; } @@ -256,6 +257,8 @@ struct MissingFeatures { static bool loopInfoStack() { return false; } static bool lowerAggregateLoadStore() { return false; } static bool lowerModeOptLevel() { return false; } + static bool loweringPrepareX86CXXABI() { return false; } + static bool loweringPrepareAArch64XXABI() { return false; } static bool maybeHandleStaticInExternC() { return false; } static bool mergeAllConstants() { return false; } static bool metaDataNode() { return false; } diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h index 25afe8b6b901d..a6f10e6bb9e9c 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h +++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h @@ -319,12 +319,6 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy { return cir::ConstantOp::create(*this, loc, cir::IntAttr::get(sInt64Ty, c)); } - // Creates constant nullptr for pointer type ty. - cir::ConstantOp getNullPtr(mlir::Type ty, mlir::Location loc) { - assert(!cir::MissingFeatures::targetCodeGenInfoGetNullPointer()); - return cir::ConstantOp::create(*this, loc, getConstPtrAttr(ty, 0)); - } - mlir::Value createNeg(mlir::Value value) { if (auto intTy = mlir::dyn_cast(value.getType())) { diff --git a/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt b/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt index df7a1a3e0acb5..3fc5b06b74e4d 100644 --- a/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt +++ b/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt @@ -4,6 +4,7 @@ add_clang_library(MLIRCIRTransforms FlattenCFG.cpp HoistAllocas.cpp LoweringPrepare.cpp + LoweringPrepareItaniumCXXABI.cpp GotoSolver.cpp DEPENDS diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp index 706e54f064aa6..dbff0b98fbe42 100644 --- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp +++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp @@ -6,6 +6,7 @@ // //===----------------------------------------------------------------------===// +#include "LoweringPrepareCXXABI.h" #include "PassDetail.h" #include "clang/AST/ASTContext.h" #include "clang/Basic/Module.h" @@ -62,6 +63,7 @@ struct LoweringPreparePass : public LoweringPrepareBase { void lowerComplexMulOp(cir::ComplexMulOp op); void lowerUnaryOp(cir::UnaryOp op); void lowerGlobalOp(cir::GlobalOp op); + void lowerDynamicCastOp(cir::DynamicCastOp op); void lowerArrayDtor(cir::ArrayDtor op); void lowerArrayCtor(cir::ArrayCtor op); @@ -91,6 +93,9 @@ struct LoweringPreparePass : public LoweringPrepareBase { clang::ASTContext *astCtx; + // Helper for lowering C++ ABI specific operations. + std::shared_ptr cxxABI; + /// Tracks current module. mlir::ModuleOp mlirModule; @@ -101,7 +106,24 @@ struct LoweringPreparePass : public LoweringPrepareBase { /// List of ctors and their priorities to be called before main() llvm::SmallVector, 4> globalCtorList; - void setASTContext(clang::ASTContext *c) { astCtx = c; } + void setASTContext(clang::ASTContext *c) { + astCtx = c; + switch (c->getCXXABIKind()) { + case clang::TargetCXXABI::GenericItanium: + // We'll need X86-specific support for handling vaargs lowering, but for + // now the Itanium ABI will work. + assert(!cir::MissingFeatures::loweringPrepareX86CXXABI()); + cxxABI.reset(cir::LoweringPrepareCXXABI::createItaniumABI()); + break; + case clang::TargetCXXABI::GenericAArch64: + case clang::TargetCXXABI::AppleARM64: + assert(!cir::MissingFeatures::loweringPrepareAArch64XXABI()); + cxxABI.reset(cir::LoweringPrepareCXXABI::createItaniumABI()); + break; + default: + llvm_unreachable("NYI"); + } + } }; } // namespace @@ -850,6 +872,17 @@ void LoweringPreparePass::buildCXXGlobalInitFunc() { cir::ReturnOp::create(builder, f.getLoc()); } +void LoweringPreparePass::lowerDynamicCastOp(DynamicCastOp op) { + CIRBaseBuilderTy builder(getContext()); + builder.setInsertionPointAfter(op); + + assert(astCtx && "AST context is not available during lowering prepare"); + auto loweredValue = cxxABI->lowerDynamicCast(builder, *astCtx, op); + + op.replaceAllUsesWith(loweredValue); + op.erase(); +} + static void lowerArrayDtorCtorIntoLoop(cir::CIRBaseBuilderTy &builder, clang::ASTContext *astCtx, mlir::Operation *op, mlir::Type eltTy, @@ -954,6 +987,8 @@ void LoweringPreparePass::runOnOp(mlir::Operation *op) { lowerComplexMulOp(complexMul); else if (auto glob = mlir::dyn_cast(op)) lowerGlobalOp(glob); + else if (auto dynamicCast = mlir::dyn_cast(op)) + lowerDynamicCastOp(dynamicCast); else if (auto unary = mlir::dyn_cast(op)) lowerUnaryOp(unary); } @@ -967,8 +1002,8 @@ void LoweringPreparePass::runOnOperation() { op->walk([&](mlir::Operation *op) { if (mlir::isa(op)) + cir::ComplexMulOp, cir::ComplexDivOp, cir::DynamicCastOp, + cir::GlobalOp, cir::UnaryOp>(op)) opsToTransform.push_back(op); }); diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepareCXXABI.h b/clang/lib/CIR/Dialect/Transforms/LoweringPrepareCXXABI.h new file mode 100644 index 0000000000000..2582c332d52a6 --- /dev/null +++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepareCXXABI.h @@ -0,0 +1,38 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file provides the LoweringPrepareCXXABI class, which is the base class +// for ABI specific functionalities that are required during LLVM lowering +// prepare. +// +//===----------------------------------------------------------------------===// + +#ifndef CIR_DIALECT_TRANSFORMS__LOWERINGPREPARECXXABI_H +#define CIR_DIALECT_TRANSFORMS__LOWERINGPREPARECXXABI_H + +#include "mlir/IR/Value.h" +#include "clang/AST/ASTContext.h" +#include "clang/CIR/Dialect/Builder/CIRBaseBuilder.h" +#include "clang/CIR/Dialect/IR/CIRDialect.h" + +namespace cir { + +class LoweringPrepareCXXABI { +public: + static LoweringPrepareCXXABI *createItaniumABI(); + + virtual ~LoweringPrepareCXXABI() {} + + virtual mlir::Value lowerDynamicCast(CIRBaseBuilderTy &builder, + clang::ASTContext &astCtx, + cir::DynamicCastOp op) = 0; +}; + +} // namespace cir + +#endif // CIR_DIALECT_TRANSFORMS__LOWERINGPREPARECXXABI_H diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepareItaniumCXXABI.cpp b/clang/lib/CIR/Dialect/Transforms/LoweringPrepareItaniumCXXABI.cpp new file mode 100644 index 0000000000000..7d3c711251b9f --- /dev/null +++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepareItaniumCXXABI.cpp @@ -0,0 +1,126 @@ +//===--------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with +// LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===--------------------------------------------------------------------===// +// +// This file provides Itanium C++ ABI specific code +// that is used during LLVMIR lowering prepare. +// +//===--------------------------------------------------------------------===// + +#include "LoweringPrepareCXXABI.h" +#include "mlir/IR/BuiltinAttributes.h" +#include "mlir/IR/Value.h" +#include "mlir/IR/ValueRange.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/CIR/Dialect/Builder/CIRBaseBuilder.h" +#include "clang/CIR/Dialect/IR/CIRAttrs.h" +#include "clang/CIR/Dialect/IR/CIRDataLayout.h" +#include "clang/CIR/Dialect/IR/CIRDialect.h" +#include "clang/CIR/MissingFeatures.h" + +class LoweringPrepareItaniumCXXABI : public cir::LoweringPrepareCXXABI { +public: + mlir::Value lowerDynamicCast(cir::CIRBaseBuilderTy &builder, + clang::ASTContext &astCtx, + cir::DynamicCastOp op) override; +}; + +cir::LoweringPrepareCXXABI *cir::LoweringPrepareCXXABI::createItaniumABI() { + return new LoweringPrepareItaniumCXXABI(); +} + +static void buildBadCastCall(cir::CIRBaseBuilderTy &builder, mlir::Location loc, + mlir::FlatSymbolRefAttr badCastFuncRef) { + builder.createCallOp(loc, badCastFuncRef, cir::VoidType(), + mlir::ValueRange{}); + // TODO(cir): Set the 'noreturn' attribute on the function. + assert(!cir::MissingFeatures::opFuncNoReturn()); + cir::UnreachableOp::create(builder, loc); + builder.clearInsertionPoint(); +} + +static mlir::Value +buildDynamicCastAfterNullCheck(cir::CIRBaseBuilderTy &builder, + cir::DynamicCastOp op) { + mlir::Location loc = op->getLoc(); + mlir::Value srcValue = op.getSrc(); + cir::DynamicCastInfoAttr castInfo = op.getInfo().value(); + + // TODO(cir): consider address space + assert(!cir::MissingFeatures::addressSpace()); + + mlir::Value srcPtr = builder.createBitcast(srcValue, builder.getVoidPtrTy()); + cir::ConstantOp srcRtti = builder.getConstant(loc, castInfo.getSrcRtti()); + cir::ConstantOp destRtti = builder.getConstant(loc, castInfo.getDestRtti()); + cir::ConstantOp offsetHint = + builder.getConstant(loc, castInfo.getOffsetHint()); + + mlir::FlatSymbolRefAttr dynCastFuncRef = castInfo.getRuntimeFunc(); + mlir::Value dynCastFuncArgs[4] = {srcPtr, srcRtti, destRtti, offsetHint}; + + mlir::Value castedPtr = + builder + .createCallOp(loc, dynCastFuncRef, builder.getVoidPtrTy(), + dynCastFuncArgs) + .getResult(); + + assert(mlir::isa(castedPtr.getType()) && + "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 (op.isRefCast()) { + // Emit a cir.if that checks the casted value. + mlir::Value castedValueIsNull = builder.createPtrIsNull(castedPtr); + builder.create( + loc, castedValueIsNull, false, [&](mlir::OpBuilder &, mlir::Location) { + buildBadCastCall(builder, loc, castInfo.getBadCastFunc()); + }); + } + + // Note that castedPtr is a void*. Cast it to a pointer to the destination + // type before return. + return builder.createBitcast(castedPtr, op.getType()); +} + +static mlir::Value +buildDynamicCastToVoidAfterNullCheck(cir::CIRBaseBuilderTy &builder, + clang::ASTContext &astCtx, + cir::DynamicCastOp op) { + llvm_unreachable("dynamic cast to void is NYI"); +} + +mlir::Value +LoweringPrepareItaniumCXXABI::lowerDynamicCast(cir::CIRBaseBuilderTy &builder, + clang::ASTContext &astCtx, + cir::DynamicCastOp op) { + mlir::Location loc = op->getLoc(); + mlir::Value srcValue = op.getSrc(); + + assert(!cir::MissingFeatures::emitTypeCheck()); + + if (op.isRefCast()) + return buildDynamicCastAfterNullCheck(builder, op); + + mlir::Value srcValueIsNotNull = builder.createPtrToBoolCast(srcValue); + return builder + .create( + loc, srcValueIsNotNull, + [&](mlir::OpBuilder &, mlir::Location) { + mlir::Value castedValue = + op.isCastToVoid() + ? buildDynamicCastToVoidAfterNullCheck(builder, astCtx, op) + : buildDynamicCastAfterNullCheck(builder, op); + builder.createYield(loc, castedValue); + }, + [&](mlir::OpBuilder &, mlir::Location) { + builder.createYield( + loc, builder.getNullPtr(op.getType(), loc).getResult()); + }) + .getResult(); +} diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index a1ecfc7a70909..26e0ba9b4e45a 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -1739,7 +1739,6 @@ mlir::LogicalResult CIRToLLVMGlobalOpLowering::matchAndRewrite( const mlir::LLVM::Linkage linkage = convertLinkage(op.getLinkage()); const StringRef symbol = op.getSymName(); SmallVector attributes; - mlir::SymbolRefAttr comdatAttr = getComdatAttr(op, rewriter); if (init.has_value()) { if (mlir::isa(init.value())) { @@ -1771,6 +1770,7 @@ mlir::LogicalResult CIRToLLVMGlobalOpLowering::matchAndRewrite( } // Rewrite op. + mlir::SymbolRefAttr comdatAttr = getComdatAttr(op, rewriter); auto newOp = rewriter.replaceOpWithNewOp( op, llvmType, isConst, linkage, symbol, init.value_or(mlir::Attribute()), alignment, addrSpace, isDsoLocal, isThreadLocal, comdatAttr, attributes); diff --git a/clang/test/CIR/CodeGen/dynamic-cast.cpp b/clang/test/CIR/CodeGen/dynamic-cast.cpp index e5244b220e76c..b4938402f0256 100644 --- a/clang/test/CIR/CodeGen/dynamic-cast.cpp +++ b/clang/test/CIR/CodeGen/dynamic-cast.cpp @@ -1,5 +1,11 @@ // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -fclangir -emit-cir -mmlir --mlir-print-ir-before=cir-lowering-prepare %s -o %t.cir 2> %t.before.log // RUN: FileCheck %s --input-file=%t.before.log -check-prefix=CIR-BEFORE +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -fclangir -emit-cir -mmlir --mlir-print-ir-after=cir-lowering-prepare %s -o %t.cir 2> %t.after.log +// RUN: FileCheck %s --input-file=%t.after.log -check-prefix=CIR-AFTER +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -fclangir -emit-llvm %s -o %t-cir.ll +// RUN: FileCheck %s --input-file=%t-cir.ll -check-prefix=LLVM +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -emit-llvm %s -o %t.ll +// RUN: FileCheck %s --input-file=%t.ll -check-prefix=OGCG struct Base { virtual ~Base(); @@ -19,6 +25,46 @@ Derived *ptr_cast(Base *b) { // CIR-BEFORE: %{{.+}} = cir.dyn_cast ptr %{{.+}} : !cir.ptr -> !cir.ptr #dyn_cast_info__ZTI4Base__ZTI7Derived // CIR-BEFORE: } +// CIR-AFTER: cir.func dso_local @_Z8ptr_castP4Base +// CIR-AFTER: %[[SRC:.*]] = cir.load{{.*}} %{{.+}} : !cir.ptr>, !cir.ptr +// CIR-AFTER-NEXT: %[[SRC_IS_NOT_NULL:.*]] = cir.cast ptr_to_bool %[[SRC]] : !cir.ptr -> !cir.bool +// CIR-AFTER-NEXT: %{{.+}} = cir.ternary(%[[SRC_IS_NOT_NULL]], true { +// CIR-AFTER-NEXT: %[[SRC_VOID_PTR:.*]] = cir.cast bitcast %[[SRC]] : !cir.ptr -> !cir.ptr +// CIR-AFTER-NEXT: %[[BASE_RTTI:.*]] = cir.const #cir.global_view<@_ZTI4Base> : !cir.ptr +// CIR-AFTER-NEXT: %[[DERIVED_RTTI:.*]] = cir.const #cir.global_view<@_ZTI7Derived> : !cir.ptr +// CIR-AFTER-NEXT: %[[HINT:.*]] = cir.const #cir.int<0> : !s64i +// CIR-AFTER-NEXT: %[[RT_CALL_RET:.*]] = cir.call @__dynamic_cast(%[[SRC_VOID_PTR]], %[[BASE_RTTI]], %[[DERIVED_RTTI]], %[[HINT]]) : (!cir.ptr, !cir.ptr, !cir.ptr, !s64i) -> !cir.ptr +// CIR-AFTER-NEXT: %[[CASTED:.*]] = cir.cast bitcast %[[RT_CALL_RET]] : !cir.ptr -> !cir.ptr +// CIR-AFTER-NEXT: cir.yield %[[CASTED]] : !cir.ptr +// CIR-AFTER-NEXT: }, false { +// CIR-AFTER-NEXT: %[[NULL_PTR:.*]] = cir.const #cir.ptr : !cir.ptr +// CIR-AFTER-NEXT: cir.yield %[[NULL_PTR]] : !cir.ptr +// CIR-AFTER-NEXT: }) : (!cir.bool) -> !cir.ptr +// CIR-AFTER: } + +// LLVM: define {{.*}} @_Z8ptr_castP4Base +// LLVM: %[[IS_NOT_NULL:.*]] = icmp ne ptr %[[PTR:.*]], null +// LLVM: br i1 %[[IS_NOT_NULL]], label %[[NOT_NULL:.*]], label %[[NULL:.*]] +// LLVM: [[NOT_NULL]]: +// LLVM: %[[RESULT:.*]] = call ptr @__dynamic_cast(ptr %[[PTR]], ptr @_ZTI4Base, ptr @_ZTI7Derived, i64 0) +// LLVM: br label %[[DONE:.*]] +// LLVM: [[NULL]]: +// LLVM: br label %[[DONE]] +// LLVM: [[DONE]]: +// LLVM: %[[RET:.*]] = phi ptr [ null, %[[NULL]] ], [ %[[RESULT]], %[[NOT_NULL]] ] + +// OGCG: define {{.*}} @_Z8ptr_castP4Base +// OGCG: %[[IS_NULL:.*]] = icmp eq ptr %[[PTR:.*]], null +// OGCG: br i1 %[[IS_NULL]], label %[[NULL:.*]], label %[[NOT_NULL:.*]] +// OGCG: [[NOT_NULL]]: +// OGCG: %[[RESULT:.*]] = call ptr @__dynamic_cast(ptr %[[PTR]], ptr @_ZTI4Base, ptr @_ZTI7Derived, i64 0) +// OGCG: br label %[[DONE:.*]] +// OGCG: [[NULL]]: +// OGCG: br label %[[DONE]] +// OGCG: [[DONE]]: +// OGCG: %[[RET:.*]] = phi ptr [ %[[RESULT]], %[[NOT_NULL]] ], [ null, %[[NULL]] ] + + Derived &ref_cast(Base &b) { return dynamic_cast(b); } @@ -26,3 +72,32 @@ Derived &ref_cast(Base &b) { // CIR-BEFORE: cir.func dso_local @_Z8ref_castR4Base // CIR-BEFORE: %{{.+}} = cir.dyn_cast ref %{{.+}} : !cir.ptr -> !cir.ptr #dyn_cast_info__ZTI4Base__ZTI7Derived // CIR-BEFORE: } + +// CIR-AFTER: cir.func dso_local @_Z8ref_castR4Base +// CIR-AFTER: %[[SRC_VOID_PTR:.*]] = cir.cast bitcast %{{.+}} : !cir.ptr -> !cir.ptr +// CIR-AFTER-NEXT: %[[SRC_RTTI:.*]] = cir.const #cir.global_view<@_ZTI4Base> : !cir.ptr +// CIR-AFTER-NEXT: %[[DEST_RTTI:.*]] = cir.const #cir.global_view<@_ZTI7Derived> : !cir.ptr +// CIR-AFTER-NEXT: %[[OFFSET_HINT:.*]] = cir.const #cir.int<0> : !s64i +// CIR-AFTER-NEXT: %[[CASTED_PTR:.*]] = cir.call @__dynamic_cast(%[[SRC_VOID_PTR]], %[[SRC_RTTI]], %[[DEST_RTTI]], %[[OFFSET_HINT]]) : (!cir.ptr, !cir.ptr, !cir.ptr, !s64i) -> !cir.ptr +// CIR-AFTER-NEXT: %[[NULL_PTR:.*]] = cir.const #cir.ptr : !cir.ptr +// CIR-AFTER-NEXT: %[[CASTED_PTR_IS_NULL:.*]] = cir.cmp(eq, %[[CASTED_PTR]], %[[NULL_PTR]]) : !cir.ptr, !cir.bool +// CIR-AFTER-NEXT: cir.if %[[CASTED_PTR_IS_NULL]] { +// CIR-AFTER-NEXT: cir.call @__cxa_bad_cast() : () -> () +// CIR-AFTER-NEXT: cir.unreachable +// CIR-AFTER-NEXT: } +// CIR-AFTER-NEXT: %{{.+}} = cir.cast bitcast %[[CASTED_PTR]] : !cir.ptr -> !cir.ptr +// CIR-AFTER: } + +// LLVM: define {{.*}} ptr @_Z8ref_castR4Base +// LLVM: %[[RESULT:.*]] = call ptr @__dynamic_cast(ptr %{{.*}}, ptr @_ZTI4Base, ptr @_ZTI7Derived, i64 0) +// LLVM: %[[IS_NULL:.*]] = icmp eq ptr %[[RESULT]], null +// LLVM: br i1 %[[IS_NULL]], label %[[BAD_CAST:.*]], label %[[DONE:.*]] +// LLVM: [[BAD_CAST]]: +// LLVM: call void @__cxa_bad_cast() + +// OGCG: define {{.*}} ptr @_Z8ref_castR4Base +// OGCG: %[[RESULT:.*]] = call ptr @__dynamic_cast(ptr %0, ptr @_ZTI4Base, ptr @_ZTI7Derived, i64 0) +// OGCG: %[[IS_NULL:.*]] = icmp eq ptr %[[RESULT]], null +// OGCG: br i1 %[[IS_NULL]], label %[[BAD_CAST:.*]], label %[[DONE:.*]] +// OGCG: [[BAD_CAST]]: +// OGCG: call void @__cxa_bad_cast()