[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