[clang] 4e53067 - [CIR] Add initial support for dynamic cast (#162337)

via cfe-commits cfe-commits at lists.llvm.org
Thu Oct 9 10:24:13 PDT 2025


Author: Andy Kaylor
Date: 2025-10-09T10:24:09-07:00
New Revision: 4e5306745a09b734000c200d02a54c853409a3b5

URL: https://github.com/llvm/llvm-project/commit/4e5306745a09b734000c200d02a54c853409a3b5
DIFF: https://github.com/llvm/llvm-project/commit/4e5306745a09b734000c200d02a54c853409a3b5.diff

LOG: [CIR] Add initial support for dynamic cast (#162337)

This adds support for dynamic cast handling and generating
`cir.dyn_cast` operations and `cir.dyn_cast_info` attributes.

This does not include support for lowering the dynamic cast to LLVM IR,
which will require changes to the LoweringPrepare pass that will be made
in a future change.

This also does not yet handle dynamic cast to void or exact dynamic
casts.

Added: 
    clang/test/CIR/CodeGen/dynamic-cast.cpp

Modified: 
    clang/include/clang/CIR/MissingFeatures.h
    clang/lib/CIR/CodeGen/CIRGenBuilder.h
    clang/lib/CIR/CodeGen/CIRGenCXXABI.h
    clang/lib/CIR/CodeGen/CIRGenExpr.cpp
    clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
    clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
    clang/lib/CIR/CodeGen/CIRGenFunction.h
    clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
    clang/lib/CIR/CodeGen/CIRGenModule.cpp
    clang/lib/CIR/CodeGen/CIRGenModule.h
    clang/lib/CIR/CodeGen/CIRGenVTables.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index c9ea08b96a817..ace20868532f0 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -79,9 +79,12 @@ struct MissingFeatures {
   static bool opFuncExtraAttrs() { return false; }
   static bool opFuncMaybeHandleStaticInExternC() { return false; }
   static bool opFuncMultipleReturnVals() { return false; }
+  static bool opFuncNoUnwind() { return false; }
   static bool opFuncOperandBundles() { return false; }
   static bool opFuncParameterAttributes() { return false; }
+  static bool opFuncReadOnly() { return false; }
   static bool opFuncSection() { return false; }
+  static bool opFuncWillReturn() { return false; }
   static bool setLLVMFunctionFEnvAttributes() { return false; }
   static bool setFunctionAttributes() { return false; }
 

diff  --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
index 58345b45c97bc..25afe8b6b901d 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h
+++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
@@ -122,6 +122,11 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
     return getPointerTo(cir::VPtrType::get(getContext()));
   }
 
+  cir::FuncType getFuncType(llvm::ArrayRef<mlir::Type> params, mlir::Type retTy,
+                            bool isVarArg = false) {
+    return cir::FuncType::get(params, retTy, isVarArg);
+  }
+
   /// Get a CIR record kind from a AST declaration tag.
   cir::RecordType::RecordKind getRecordKind(const clang::TagTypeKind kind) {
     switch (kind) {
@@ -372,6 +377,15 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
     return cir::BinOp::create(*this, loc, cir::BinOpKind::Div, lhs, rhs);
   }
 
+  mlir::Value createDynCast(mlir::Location loc, mlir::Value src,
+                            cir::PointerType destType, bool isRefCast,
+                            cir::DynamicCastInfoAttr info) {
+    auto castKind =
+        isRefCast ? cir::DynamicCastKind::Ref : cir::DynamicCastKind::Ptr;
+    return cir::DynamicCastOp::create(*this, loc, destType, castKind, src, info,
+                                      /*relative_layout=*/false);
+  }
+
   Address createBaseClassAddr(mlir::Location loc, Address addr,
                               mlir::Type destType, unsigned offset,
                               bool assumeNotNull) {

diff  --git a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h
index 26da7ce94e0ef..6030dd2365672 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h
+++ b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h
@@ -54,6 +54,12 @@ class CIRGenCXXABI {
                             Address thisAddr, const CXXRecordDecl *classDecl,
                             const CXXRecordDecl *baseClassDecl) = 0;
 
+  virtual mlir::Value emitDynamicCast(CIRGenFunction &cgf, mlir::Location loc,
+                                      QualType srcRecordTy,
+                                      QualType destRecordTy,
+                                      cir::PointerType destCIRTy,
+                                      bool isRefCast, Address src) = 0;
+
 public:
   /// Similar to AddedStructorArgs, but only notes the number of additional
   /// arguments.

diff  --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index be948908e18d5..f416571181153 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -1185,10 +1185,16 @@ LValue CIRGenFunction::emitCastLValue(const CastExpr *e) {
   case CK_BuiltinFnToFnPtr:
     llvm_unreachable("builtin functions are handled elsewhere");
 
+  case CK_Dynamic: {
+    LValue lv = emitLValue(e->getSubExpr());
+    Address v = lv.getAddress();
+    const auto *dce = cast<CXXDynamicCastExpr>(e);
+    return makeNaturalAlignAddrLValue(emitDynamicCast(v, dce), e->getType());
+  }
+
   // These are never l-values; just use the aggregate emission code.
   case CK_NonAtomicToAtomic:
   case CK_AtomicToNonAtomic:
-  case CK_Dynamic:
   case CK_ToUnion:
   case CK_BaseToDerived:
   case CK_AddressSpaceConversion:

diff  --git a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
index bbc6a49a5e798..97c0944fca336 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
@@ -722,3 +722,43 @@ void CIRGenFunction::emitDeleteCall(const FunctionDecl *deleteFD,
   // Emit the call to delete.
   emitNewDeleteCall(*this, deleteFD, deleteFTy, deleteArgs);
 }
+
+mlir::Value CIRGenFunction::emitDynamicCast(Address thisAddr,
+                                            const CXXDynamicCastExpr *dce) {
+  mlir::Location loc = getLoc(dce->getSourceRange());
+
+  cgm.emitExplicitCastExprType(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();
+  bool isRefCast = destTy->isReferenceType();
+
+  QualType srcRecordTy;
+  QualType destRecordTy;
+  if (isDynCastToVoid) {
+    srcRecordTy = srcTy->getPointeeType();
+    // No destRecordTy.
+  } else if (const PointerType *destPTy = destTy->getAs<PointerType>()) {
+    srcRecordTy = srcTy->castAs<PointerType>()->getPointeeType();
+    destRecordTy = destPTy->getPointeeType();
+  } else {
+    srcRecordTy = srcTy;
+    destRecordTy = destTy->castAs<ReferenceType>()->getPointeeType();
+  }
+
+  assert(srcRecordTy->isRecordType() && "source type must be a record type!");
+  assert(!cir::MissingFeatures::emitTypeCheck());
+
+  if (dce->isAlwaysNull()) {
+    cgm.errorNYI(dce->getSourceRange(), "emitDynamicCastToNull");
+    return {};
+  }
+
+  auto destCirTy = mlir::cast<cir::PointerType>(convertType(destTy));
+  return cgm.getCXXABI().emitDynamicCast(*this, loc, srcRecordTy, destRecordTy,
+                                         destCirTy, isRefCast, thisAddr);
+}

diff  --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
index 7edd83e81b04d..637f9ef65c88f 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -1916,6 +1916,11 @@ mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *ce) {
     return builder.createIntToPtr(middleVal, destCIRTy);
   }
 
+  case CK_Dynamic: {
+    Address v = cgf.emitPointerWithAlignment(subExpr);
+    const auto *dce = cast<CXXDynamicCastExpr>(ce);
+    return cgf.emitDynamicCast(v, dce);
+  }
   case CK_ArrayToPointerDecay:
     return cgf.emitArrayToPointerDecay(subExpr).getPointer();
 

diff  --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index db2adc282c17e..7a606eee4f5eb 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -1312,6 +1312,8 @@ class CIRGenFunction : public CIRGenTypeCache {
 
   mlir::LogicalResult emitDoStmt(const clang::DoStmt &s);
 
+  mlir::Value emitDynamicCast(Address thisAddr, const CXXDynamicCastExpr *dce);
+
   /// Emit an expression as an initializer for an object (variable, field, etc.)
   /// at the given location.  The expression is not necessarily the normal
   /// initializer for the object, and the address is not necessarily

diff  --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
index 5622a68f608a0..6689f885edd3b 100644
--- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
@@ -118,6 +118,16 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI {
                             Address thisAddr, const CXXRecordDecl *classDecl,
                             const CXXRecordDecl *baseClassDecl) override;
 
+  // The traditional clang CodeGen emits calls to `__dynamic_cast` directly into
+  // LLVM in the `emitDynamicCastCall` function. In CIR, `dynamic_cast`
+  // expressions are lowered to `cir.dyn_cast` ops instead of calls to runtime
+  // functions. So during CIRGen we don't need the `emitDynamicCastCall`
+  // function that clang CodeGen has.
+  mlir::Value emitDynamicCast(CIRGenFunction &cgf, mlir::Location loc,
+                              QualType srcRecordTy, QualType destRecordTy,
+                              cir::PointerType destCIRTy, bool isRefCast,
+                              Address src) override;
+
   /**************************** RTTI Uniqueness ******************************/
 protected:
   /// Returns true if the ABI requires RTTI type_info objects to be unique
@@ -1819,3 +1829,143 @@ mlir::Value CIRGenItaniumCXXABI::getVirtualBaseClassOffset(
   }
   return vbaseOffset;
 }
+
+static cir::FuncOp getBadCastFn(CIRGenFunction &cgf) {
+  // Prototype: void __cxa_bad_cast();
+
+  // TODO(cir): set the calling convention of the runtime function.
+  assert(!cir::MissingFeatures::opFuncCallingConv());
+
+  cir::FuncType fnTy =
+      cgf.getBuilder().getFuncType({}, cgf.getBuilder().getVoidTy());
+  return cgf.cgm.createRuntimeFunction(fnTy, "__cxa_bad_cast");
+}
+
+// TODO(cir): This could be shared with classic codegen.
+static CharUnits computeOffsetHint(ASTContext &astContext,
+                                   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 =
+          astContext.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 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::ptr
diff _t src2dst_offset);
+
+  mlir::Type voidPtrTy = cgf.getBuilder().getVoidPtrTy();
+  mlir::Type rttiPtrTy = cgf.getBuilder().getUInt8PtrTy();
+  mlir::Type ptrDiffTy = cgf.convertType(cgf.getContext().getPointerDiffType());
+
+  // TODO(cir): mark the function as nowind willreturn readonly.
+  assert(!cir::MissingFeatures::opFuncNoUnwind());
+  assert(!cir::MissingFeatures::opFuncWillReturn());
+  assert(!cir::MissingFeatures::opFuncReadOnly());
+
+  // TODO(cir): set the calling convention of the runtime function.
+  assert(!cir::MissingFeatures::opFuncCallingConv());
+
+  cir::FuncType FTy = cgf.getBuilder().getFuncType(
+      {voidPtrTy, rttiPtrTy, rttiPtrTy, ptrDiffTy}, voidPtrTy);
+  return cgf.cgm.createRuntimeFunction(FTy, "__dynamic_cast");
+}
+
+static cir::DynamicCastInfoAttr emitDynamicCastInfo(CIRGenFunction &cgf,
+                                                    mlir::Location loc,
+                                                    QualType srcRecordTy,
+                                                    QualType destRecordTy) {
+  auto srcRtti = mlir::cast<cir::GlobalViewAttr>(
+      cgf.cgm.getAddrOfRTTIDescriptor(loc, srcRecordTy));
+  auto destRtti = mlir::cast<cir::GlobalViewAttr>(
+      cgf.cgm.getAddrOfRTTIDescriptor(loc, destRecordTy));
+
+  cir::FuncOp runtimeFuncOp = getItaniumDynamicCastFn(cgf);
+  cir::FuncOp badCastFuncOp = getBadCastFn(cgf);
+  auto runtimeFuncRef = mlir::FlatSymbolRefAttr::get(runtimeFuncOp);
+  auto badCastFuncRef = mlir::FlatSymbolRefAttr::get(badCastFuncOp);
+
+  const CXXRecordDecl *srcDecl = srcRecordTy->getAsCXXRecordDecl();
+  const CXXRecordDecl *destDecl = destRecordTy->getAsCXXRecordDecl();
+  CharUnits offsetHint = computeOffsetHint(cgf.getContext(), srcDecl, destDecl);
+
+  mlir::Type ptr
diff Ty = cgf.convertType(cgf.getContext().getPointerDiffType());
+  auto offsetHintAttr = cir::IntAttr::get(ptr
diff Ty, offsetHint.getQuantity());
+
+  return cir::DynamicCastInfoAttr::get(srcRtti, destRtti, runtimeFuncRef,
+                                       badCastFuncRef, offsetHintAttr);
+}
+
+mlir::Value CIRGenItaniumCXXABI::emitDynamicCast(CIRGenFunction &cgf,
+                                                 mlir::Location loc,
+                                                 QualType srcRecordTy,
+                                                 QualType destRecordTy,
+                                                 cir::PointerType destCIRTy,
+                                                 bool isRefCast, Address src) {
+  bool isCastToVoid = destRecordTy.isNull();
+  assert((!isCastToVoid || !isRefCast) && "cannot cast to void reference");
+
+  if (isCastToVoid) {
+    cgm.errorNYI(loc, "emitDynamicCastToVoid");
+    return {};
+  }
+
+  // If the destination is effectively final, the cast succeeds if and only
+  // if the dynamic type of the pointer is exactly the destination type.
+  if (destRecordTy->getAsCXXRecordDecl()->isEffectivelyFinal() &&
+      cgf.cgm.getCodeGenOpts().OptimizationLevel > 0) {
+    cgm.errorNYI(loc, "emitExactDynamicCast");
+    return {};
+  }
+
+  cir::DynamicCastInfoAttr castInfo =
+      emitDynamicCastInfo(cgf, loc, srcRecordTy, destRecordTy);
+  return cgf.getBuilder().createDynCast(loc, src.getPointer(), destCIRTy,
+                                        isRefCast, castInfo);
+}

diff  --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index 910c8a9b8f98a..fe1ea5617b8cd 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -2079,6 +2079,29 @@ CIRGenModule::createCIRBuiltinFunction(mlir::Location loc, StringRef name,
   return fnOp;
 }
 
+cir::FuncOp CIRGenModule::createRuntimeFunction(cir::FuncType ty,
+                                                StringRef name, mlir::ArrayAttr,
+                                                [[maybe_unused]] bool isLocal,
+                                                bool assumeConvergent) {
+  if (assumeConvergent)
+    errorNYI("createRuntimeFunction: assumeConvergent");
+  if (isLocal)
+    errorNYI("createRuntimeFunction: local");
+
+  cir::FuncOp entry = getOrCreateCIRFunction(name, ty, GlobalDecl(),
+                                             /*forVtable=*/false);
+
+  if (entry) {
+    // TODO(cir): set the attributes of the function.
+    assert(!cir::MissingFeatures::setLLVMFunctionFEnvAttributes());
+    assert(!cir::MissingFeatures::opFuncCallingConv());
+    assert(!cir::MissingFeatures::opGlobalDLLImportExport());
+    entry.setDSOLocal(true);
+  }
+
+  return entry;
+}
+
 mlir::SymbolTable::Visibility
 CIRGenModule::getMLIRVisibility(cir::GlobalOp op) {
   // MLIR doesn't accept public symbols declarations (only

diff  --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h
index c6a6681021d47..f627bae9f87f9 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -480,6 +480,10 @@ class CIRGenModule : public CIRGenTypeCache {
                                        cir::FuncType ty,
                                        const clang::FunctionDecl *fd);
 
+  cir::FuncOp createRuntimeFunction(cir::FuncType ty, llvm::StringRef name,
+                                    mlir::ArrayAttr = {}, bool isLocal = false,
+                                    bool assumeConvergent = false);
+
   static constexpr const char *builtinCoroId = "__builtin_coro_id";
 
   /// Given a builtin id for a function like "__builtin_fabsf", return a

diff  --git a/clang/lib/CIR/CodeGen/CIRGenVTables.cpp b/clang/lib/CIR/CodeGen/CIRGenVTables.cpp
index 94d856b41b3ce..84f59773757b5 100644
--- a/clang/lib/CIR/CodeGen/CIRGenVTables.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenVTables.cpp
@@ -327,9 +327,40 @@ cir::GlobalLinkageKind CIRGenModule::getVTableLinkage(const CXXRecordDecl *rd) {
       llvm_unreachable("Should not have been asked to emit this");
     }
   }
+  // -fapple-kext mode does not support weak linkage, so we must use
+  // internal linkage.
+  if (astContext.getLangOpts().AppleKext)
+    return cir::GlobalLinkageKind::InternalLinkage;
+
+  auto discardableODRLinkage = cir::GlobalLinkageKind::LinkOnceODRLinkage;
+  auto nonDiscardableODRLinkage = cir::GlobalLinkageKind::WeakODRLinkage;
+  if (rd->hasAttr<DLLExportAttr>()) {
+    // Cannot discard exported vtables.
+    discardableODRLinkage = nonDiscardableODRLinkage;
+  } else if (rd->hasAttr<DLLImportAttr>()) {
+    // Imported vtables are available externally.
+    discardableODRLinkage = cir::GlobalLinkageKind::AvailableExternallyLinkage;
+    nonDiscardableODRLinkage =
+        cir::GlobalLinkageKind::AvailableExternallyLinkage;
+  }
+
+  switch (rd->getTemplateSpecializationKind()) {
+  case TSK_Undeclared:
+  case TSK_ExplicitSpecialization:
+  case TSK_ImplicitInstantiation:
+    return discardableODRLinkage;
+
+  case TSK_ExplicitInstantiationDeclaration: {
+    errorNYI(rd->getSourceRange(),
+             "getVTableLinkage: explicit instantiation declaration");
+    return cir::GlobalLinkageKind::ExternalLinkage;
+  }
+
+  case TSK_ExplicitInstantiationDefinition:
+    return nonDiscardableODRLinkage;
+  }
 
-  errorNYI(rd->getSourceRange(), "getVTableLinkage: no key function");
-  return cir::GlobalLinkageKind::ExternalLinkage;
+  llvm_unreachable("Invalid TemplateSpecializationKind!");
 }
 
 cir::GlobalOp CIRGenVTables::getAddrOfVTT(const CXXRecordDecl *rd) {

diff  --git a/clang/test/CIR/CodeGen/dynamic-cast.cpp b/clang/test/CIR/CodeGen/dynamic-cast.cpp
new file mode 100644
index 0000000000000..e5244b220e76c
--- /dev/null
+++ b/clang/test/CIR/CodeGen/dynamic-cast.cpp
@@ -0,0 +1,28 @@
+// 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
+
+struct Base {
+  virtual ~Base();
+};
+
+struct Derived : Base {};
+
+// CIR-BEFORE-DAG: !rec_Base = !cir.record
+// CIR-BEFORE-DAG: !rec_Derived = !cir.record
+// CIR-BEFORE-DAG: #dyn_cast_info__ZTI4Base__ZTI7Derived = #cir.dyn_cast_info<src_rtti = #cir.global_view<@_ZTI4Base> : !cir.ptr<!u8i>, dest_rtti = #cir.global_view<@_ZTI7Derived> : !cir.ptr<!u8i>, runtime_func = @__dynamic_cast, bad_cast_func = @__cxa_bad_cast, offset_hint = #cir.int<0> : !s64i>
+
+Derived *ptr_cast(Base *b) {
+  return dynamic_cast<Derived *>(b);
+}
+
+// CIR-BEFORE: cir.func dso_local @_Z8ptr_castP4Base
+// CIR-BEFORE:   %{{.+}} = cir.dyn_cast ptr %{{.+}} : !cir.ptr<!rec_Base> -> !cir.ptr<!rec_Derived> #dyn_cast_info__ZTI4Base__ZTI7Derived
+// CIR-BEFORE: }
+
+Derived &ref_cast(Base &b) {
+  return dynamic_cast<Derived &>(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: }


        


More information about the cfe-commits mailing list