[clang] [CIR] Add lowering support for dynamic cast (PR #162715)
Andy Kaylor via cfe-commits
cfe-commits at lists.llvm.org
Thu Oct 9 11:45:06 PDT 2025
https://github.com/andykaylor created https://github.com/llvm/llvm-project/pull/162715
This adds support for lowering cir.dyn_cast operations to a form that can be lowered to LLVM IR.
>From d21577c4b466e35ed62d745bff33f31624d6b43f Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Wed, 1 Oct 2025 17:07:13 -0700
Subject: [PATCH] [CIR] Add lowering support for dynamic cast
This adds support for lowering cir.dyn_cast operations to a form that
can be lowered to LLVM IR.
---
.../CIR/Dialect/Builder/CIRBaseBuilder.h | 11 ++
clang/include/clang/CIR/MissingFeatures.h | 3 +
clang/lib/CIR/CodeGen/CIRGenBuilder.h | 6 -
.../lib/CIR/Dialect/Transforms/CMakeLists.txt | 1 +
.../Dialect/Transforms/LoweringPrepare.cpp | 41 +++++-
.../Transforms/LoweringPrepareCXXABI.h | 38 ++++++
.../LoweringPrepareItaniumCXXABI.cpp | 126 ++++++++++++++++++
.../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 2 +-
clang/test/CIR/CodeGen/dynamic-cast.cpp | 75 +++++++++++
9 files changed, 293 insertions(+), 10 deletions(-)
create mode 100644 clang/lib/CIR/Dialect/Transforms/LoweringPrepareCXXABI.h
create mode 100644 clang/lib/CIR/Dialect/Transforms/LoweringPrepareItaniumCXXABI.cpp
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<cir::IntType>(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<LoweringPreparePass> {
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<LoweringPreparePass> {
clang::ASTContext *astCtx;
+ // Helper for lowering C++ ABI specific operations.
+ std::shared_ptr<cir::LoweringPrepareCXXABI> cxxABI;
+
/// Tracks current module.
mlir::ModuleOp mlirModule;
@@ -101,7 +106,24 @@ struct LoweringPreparePass : public LoweringPrepareBase<LoweringPreparePass> {
/// List of ctors and their priorities to be called before main()
llvm::SmallVector<std::pair<std::string, uint32_t>, 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<cir::GlobalOp>(op))
lowerGlobalOp(glob);
+ else if (auto dynamicCast = mlir::dyn_cast<cir::DynamicCastOp>(op))
+ lowerDynamicCastOp(dynamicCast);
else if (auto unary = mlir::dyn_cast<cir::UnaryOp>(op))
lowerUnaryOp(unary);
}
@@ -967,8 +1002,8 @@ void LoweringPreparePass::runOnOperation() {
op->walk([&](mlir::Operation *op) {
if (mlir::isa<cir::ArrayCtor, cir::ArrayDtor, cir::CastOp,
- cir::ComplexMulOp, cir::ComplexDivOp, cir::GlobalOp,
- cir::UnaryOp>(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<cir::PointerType>(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<cir::IfOp>(
+ 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<cir::TernaryOp>(
+ 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<mlir::NamedAttribute> attributes;
- mlir::SymbolRefAttr comdatAttr = getComdatAttr(op, rewriter);
if (init.has_value()) {
if (mlir::isa<cir::FPAttr, cir::IntAttr, cir::BoolAttr>(init.value())) {
@@ -1771,6 +1770,7 @@ mlir::LogicalResult CIRToLLVMGlobalOpLowering::matchAndRewrite(
}
// Rewrite op.
+ mlir::SymbolRefAttr comdatAttr = getComdatAttr(op, rewriter);
auto newOp = rewriter.replaceOpWithNewOp<mlir::LLVM::GlobalOp>(
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<!rec_Base> -> !cir.ptr<!rec_Derived> #dyn_cast_info__ZTI4Base__ZTI7Derived
// CIR-BEFORE: }
+// CIR-AFTER: cir.func dso_local @_Z8ptr_castP4Base
+// CIR-AFTER: %[[SRC:.*]] = cir.load{{.*}} %{{.+}} : !cir.ptr<!cir.ptr<!rec_Base>>, !cir.ptr<!rec_Base>
+// CIR-AFTER-NEXT: %[[SRC_IS_NOT_NULL:.*]] = cir.cast ptr_to_bool %[[SRC]] : !cir.ptr<!rec_Base> -> !cir.bool
+// CIR-AFTER-NEXT: %{{.+}} = cir.ternary(%[[SRC_IS_NOT_NULL]], true {
+// CIR-AFTER-NEXT: %[[SRC_VOID_PTR:.*]] = cir.cast bitcast %[[SRC]] : !cir.ptr<!rec_Base> -> !cir.ptr<!void>
+// CIR-AFTER-NEXT: %[[BASE_RTTI:.*]] = cir.const #cir.global_view<@_ZTI4Base> : !cir.ptr<!u8i>
+// CIR-AFTER-NEXT: %[[DERIVED_RTTI:.*]] = cir.const #cir.global_view<@_ZTI7Derived> : !cir.ptr<!u8i>
+// 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<!void>, !cir.ptr<!u8i>, !cir.ptr<!u8i>, !s64i) -> !cir.ptr<!void>
+// CIR-AFTER-NEXT: %[[CASTED:.*]] = cir.cast bitcast %[[RT_CALL_RET]] : !cir.ptr<!void> -> !cir.ptr<!rec_Derived>
+// CIR-AFTER-NEXT: cir.yield %[[CASTED]] : !cir.ptr<!rec_Derived>
+// CIR-AFTER-NEXT: }, false {
+// CIR-AFTER-NEXT: %[[NULL_PTR:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!rec_Derived>
+// CIR-AFTER-NEXT: cir.yield %[[NULL_PTR]] : !cir.ptr<!rec_Derived>
+// CIR-AFTER-NEXT: }) : (!cir.bool) -> !cir.ptr<!rec_Derived>
+// 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<Derived &>(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<!rec_Base> -> !cir.ptr<!rec_Derived> #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<!rec_Base> -> !cir.ptr<!void>
+// CIR-AFTER-NEXT: %[[SRC_RTTI:.*]] = cir.const #cir.global_view<@_ZTI4Base> : !cir.ptr<!u8i>
+// CIR-AFTER-NEXT: %[[DEST_RTTI:.*]] = cir.const #cir.global_view<@_ZTI7Derived> : !cir.ptr<!u8i>
+// 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<!void>, !cir.ptr<!u8i>, !cir.ptr<!u8i>, !s64i) -> !cir.ptr<!void>
+// CIR-AFTER-NEXT: %[[NULL_PTR:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!void>
+// CIR-AFTER-NEXT: %[[CASTED_PTR_IS_NULL:.*]] = cir.cmp(eq, %[[CASTED_PTR]], %[[NULL_PTR]]) : !cir.ptr<!void>, !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<!void> -> !cir.ptr<!rec_Derived>
+// 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()
More information about the cfe-commits
mailing list