[clang] [CIR] Upstream DynamicCastOp (PR #161734)

Andy Kaylor via cfe-commits cfe-commits at lists.llvm.org
Mon Oct 6 13:36:18 PDT 2025


https://github.com/andykaylor updated https://github.com/llvm/llvm-project/pull/161734

>From 03ac434cce6980004bd662480e72db5d4e24d9f6 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Wed, 1 Oct 2025 17:52:53 -0700
Subject: [PATCH 1/3] [CIR] Upstream DynamicCastOp

This adds the dialect handling for CIR_DynamicCastOp and
CIR_DynamicCastInfoAttr. Support for generating these operations from
source will be added in a later change.
---
 .../include/clang/CIR/Dialect/IR/CIRAttrs.td  | 53 +++++++++++
 clang/include/clang/CIR/Dialect/IR/CIROps.td  | 94 +++++++++++++++++++
 .../CIR/Dialect/IR/CIRTypeConstraints.td      |  8 ++
 clang/lib/CIR/Dialect/IR/CIRAttrs.cpp         | 43 +++++++++
 clang/lib/CIR/Dialect/IR/CIRDialect.cpp       |  4 +
 clang/test/CIR/IR/dynamic-cast.cir            | 59 ++++++++++++
 clang/test/CIR/IR/invalid-dyn-cast.cir        | 43 +++++++++
 7 files changed, 304 insertions(+)
 create mode 100644 clang/test/CIR/IR/dynamic-cast.cir
 create mode 100644 clang/test/CIR/IR/invalid-dyn-cast.cir

diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
index 7714750a53d44..0a757a0a68f3c 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
@@ -601,6 +601,59 @@ def CIR_VTableAttr : CIR_Attr<"VTable", "vtable", [TypedAttrInterface]> {
   }];
 }
 
+//===----------------------------------------------------------------------===//
+// DynamicCastInfoAttr
+//===----------------------------------------------------------------------===//
+
+def CIR_DynamicCastInfoAttr : CIR_Attr<"DynamicCastInfo", "dyn_cast_info"> {
+  let summary = "ABI specific information about a dynamic cast";
+  let description = [{
+    Provide ABI specific information about a dynamic cast operation.
+
+    The `srcRtti` and the `destRtti` parameters give the RTTI of the source
+    record type and the destination record type, respectively.
+
+    The `runtimeFunc` parameter gives the `__dynamic_cast` function which is
+    provided by the runtime. The `badCastFunc` parameter gives the
+    `__cxa_bad_cast` function which is also provided by the runtime.
+
+    The `offsetHint` parameter gives the hint value that should be passed to the
+    `__dynamic_cast` runtime function.
+  }];
+
+  let parameters = (ins
+    CIR_GlobalViewAttr:$srcRtti,
+    CIR_GlobalViewAttr:$destRtti,
+    "mlir::FlatSymbolRefAttr":$runtimeFunc,
+    "mlir::FlatSymbolRefAttr":$badCastFunc,
+    CIR_IntAttr:$offsetHint
+  );
+
+  let builders = [
+    AttrBuilderWithInferredContext<(ins "GlobalViewAttr":$srcRtti,
+                                        "GlobalViewAttr":$destRtti,
+                                        "mlir::FlatSymbolRefAttr":$runtimeFunc,
+                                        "mlir::FlatSymbolRefAttr":$badCastFunc,
+                                        "IntAttr":$offsetHint), [{
+      return $_get(srcRtti.getContext(), srcRtti, destRtti, runtimeFunc,
+                   badCastFunc, offsetHint);
+    }]>,
+  ];
+
+  let genVerifyDecl = 1;
+  let assemblyFormat = [{
+    `<`
+      qualified($srcRtti) `,` qualified($destRtti) `,`
+      $runtimeFunc `,` $badCastFunc `,` qualified($offsetHint)
+    `>`
+  }];
+
+  let extraClassDeclaration = [{
+    /// Get attribute alias name for this attribute.
+    std::string getAlias() const;
+  }];
+}
+
 //===----------------------------------------------------------------------===//
 // TargetAddressSpaceAttr
 //===----------------------------------------------------------------------===//
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index c81f64deff7bc..f2183bf509219 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -232,6 +232,100 @@ def CIR_CastOp : CIR_Op<"cast", [
   }];
 }
 
+//===----------------------------------------------------------------------===//
+// DynamicCastOp
+//===----------------------------------------------------------------------===//
+
+def CIR_DynamicCastKind : CIR_I32EnumAttr<
+  "DynamicCastKind", "dynamic cast kind", [
+    I32EnumAttrCase<"Ptr", 0, "ptr">,
+    I32EnumAttrCase<"Ref", 1, "ref">
+]>;
+
+def CIR_DynamicCastOp : CIR_Op<"dyn_cast"> {
+  let summary = "Perform dynamic cast on record pointers";
+  let description = [{
+    The `cir.dyn_cast` operation models part of the semantics of the
+    `dynamic_cast` operator in C++. It can be used to perform 3 kinds of casts
+    on record pointers:
+
+    - Down-cast, which casts a base class pointer to a derived class pointer;
+    - Side-cast, which casts a class pointer to a sibling class pointer;
+    - Cast-to-complete, which casts a class pointer to a void pointer.
+
+    The input of the operation must be a record pointer. The result of the
+    operation is either a record pointer or a void pointer.
+
+    The parameter `kind` specifies the semantics of this operation. If its value
+    is `ptr`, then the operation models dynamic casts on pointers. Otherwise, if
+    its value is `ref`, the operation models dynamic casts on references.
+    Specifically:
+
+    - When the input pointer is a null pointer value:
+      - If `kind` is `ref`, the operation will invoke undefined behavior. A
+        sanitizer check will be emitted if sanitizer is on.
+      - Otherwise, the operation will return a null pointer value as its result.
+    - When the runtime type check fails:
+      - If `kind` is `ref`, the operation will throw a `bad_cast` exception.
+      - Otherwise, the operation will return a null pointer value as its result.
+
+    The `info` argument gives detailed information about the requested dynamic
+    cast operation. It is an optional `#cir.dyn_cast_info` attribute that is
+    only present when the operation models a down-cast or a side-cast.
+
+    The `relative_layout` argument specifies whether the Itanium C++ ABI vtable
+    uses relative layout. It is only meaningful when the operation models a
+    cast-to-complete operation.
+
+    Examples:
+
+    ```mlir
+    %0 = cir.dyn_cast ptr %p : !cir.ptr<!rec_Base> -> !cir.ptr<!rec_Derived>
+    %1 = cir.dyn_cast ptr relative_layout %p : !cir.ptr<!rec_Base>
+              -> !cir.ptr<!rec_Derived>
+    %2 = cir.dyn_cast ref %r : !cir.ptr<!rec_Base> -> !cir.ptr<!rec_Derived>
+              #cir.dyn_cast_info<
+                #cir.global_view<@_ZTI4Base> : !cir.ptr<!u8i>,
+                #cir.global_view<@_ZTI7Derived> : !cir.ptr<!u8i>,
+                @__dynamic_cast,
+                @__cxa_bad_cast,
+                #cir.int<0> : !s64i
+              >
+    ```
+  }];
+
+  let arguments = (ins
+    CIR_DynamicCastKind:$kind,
+    CIR_PtrToRecordType:$src,
+    OptionalAttr<CIR_DynamicCastInfoAttr>:$info,
+    UnitAttr:$relative_layout
+  );
+
+  let results = (outs
+    CIR_PtrToAnyOf<[CIR_VoidType, CIR_RecordType]>:$result
+  );
+
+  let assemblyFormat = [{
+    $kind (`relative_layout` $relative_layout^)? $src
+    `:` qualified(type($src)) `->` qualified(type($result))
+    (qualified($info)^)? attr-dict
+  }];
+
+  let extraClassDeclaration = [{
+    /// Determine whether this operation models reference casting in C++.
+    bool isRefcast() {
+      return getKind() == ::cir::DynamicCastKind::Ref;
+    }
+
+    /// Determine whether this operation represents a dynamic cast to a void
+    /// pointer.
+    bool isCastToVoid() {
+      return getType().isVoidPtr();
+    }
+  }];
+
+  let hasLLVMLowering = false;
+}
 
 //===----------------------------------------------------------------------===//
 // PtrStrideOp
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRTypeConstraints.td b/clang/include/clang/CIR/Dialect/IR/CIRTypeConstraints.td
index da03a291a7690..9e8e1298308a4 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRTypeConstraints.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRTypeConstraints.td
@@ -171,6 +171,12 @@ def CIR_AnyComplexOrIntOrFloatType : AnyTypeOf<[
     let cppFunctionName = "isComplexOrIntegerOrFloatingPointType";
 }
 
+//===----------------------------------------------------------------------===//
+// Record Type predicates
+//===----------------------------------------------------------------------===//
+
+def CIR_AnyRecordType : CIR_TypeBase<"::cir::RecordType", "record type">;
+
 //===----------------------------------------------------------------------===//
 // Array Type predicates
 //===----------------------------------------------------------------------===//
@@ -228,6 +234,8 @@ def CIR_PtrToIntOrFloatType : CIR_PtrToType<CIR_AnyIntOrFloatType>;
 
 def CIR_PtrToComplexType : CIR_PtrToType<CIR_AnyComplexType>;
 
+def CIR_PtrToRecordType : CIR_PtrToType<CIR_AnyRecordType>;
+
 def CIR_PtrToArray : CIR_PtrToType<CIR_AnyArrayType>;
 
 //===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp
index 3484c59df4ece..64ac97025e7c7 100644
--- a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp
@@ -472,6 +472,49 @@ LogicalResult cir::VTableAttr::verify(
   return success();
 }
 
+//===----------------------------------------------------------------------===//
+// DynamicCastInfoAtttr definitions
+//===----------------------------------------------------------------------===//
+
+std::string DynamicCastInfoAttr::getAlias() const {
+  // The alias looks like: `dyn_cast_info_<src>_<dest>`
+
+  std::string alias = "dyn_cast_info_";
+
+  alias.append(getSrcRtti().getSymbol().getValue());
+  alias.push_back('_');
+  alias.append(getDestRtti().getSymbol().getValue());
+
+  return alias;
+}
+
+LogicalResult DynamicCastInfoAttr::verify(
+    function_ref<InFlightDiagnostic()> emitError, cir::GlobalViewAttr srcRtti,
+    cir::GlobalViewAttr destRtti, mlir::FlatSymbolRefAttr runtimeFunc,
+    mlir::FlatSymbolRefAttr badCastFunc, cir::IntAttr offsetHint) {
+  auto isRttiPtr = [](mlir::Type ty) {
+    // RTTI pointers are !cir.ptr<!u8i>.
+
+    auto ptrTy = mlir::dyn_cast<cir::PointerType>(ty);
+    if (!ptrTy)
+      return false;
+
+    auto pointeeIntTy = mlir::dyn_cast<cir::IntType>(ptrTy.getPointee());
+    if (!pointeeIntTy)
+      return false;
+
+    return pointeeIntTy.isUnsigned() && pointeeIntTy.getWidth() == 8;
+  };
+
+  if (!isRttiPtr(srcRtti.getType()))
+    return emitError() << "srcRtti must be an RTTI pointer";
+
+  if (!isRttiPtr(destRtti.getType()))
+    return emitError() << "destRtti must be an RTTI pointer";
+
+  return success();
+}
+
 //===----------------------------------------------------------------------===//
 // CIR Dialect
 //===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index cdd4e3c96ca98..5f88590c48d30 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -71,6 +71,10 @@ struct CIROpAsmDialectInterface : public OpAsmDialectInterface {
       os << "bfi_" << bitfield.getName().str();
       return AliasResult::FinalAlias;
     }
+    if (auto dynCastInfoAttr = mlir::dyn_cast<cir::DynamicCastInfoAttr>(attr)) {
+      os << dynCastInfoAttr.getAlias();
+      return AliasResult::FinalAlias;
+    }
     return AliasResult::NoAlias;
   }
 };
diff --git a/clang/test/CIR/IR/dynamic-cast.cir b/clang/test/CIR/IR/dynamic-cast.cir
new file mode 100644
index 0000000000000..a7468f1e97211
--- /dev/null
+++ b/clang/test/CIR/IR/dynamic-cast.cir
@@ -0,0 +1,59 @@
+// RUN: cir-opt --verify-roundtrip %s | FileCheck %s
+
+!s64i = !cir.int<s, 64>
+!u8i = !cir.int<u, 8>
+!void = !cir.void
+
+!rec_Base = !cir.record<struct "Base" {!cir.vptr}>
+!rec_Derived = !cir.record<struct "Derived" {!rec_Base}>
+
+#dyn_cast_info__ZTI4Base__ZTI7Derived = #cir.dyn_cast_info<#cir.global_view<@_ZTI4Base> : !cir.ptr<!u8i>, #cir.global_view<@_ZTI7Derived> : !cir.ptr<!u8i>, @__dynamic_cast, @__cxa_bad_cast, #cir.int<0> : !s64i>
+
+// CHECK: #dyn_cast_info__ZTI4Base__ZTI7Derived = #cir.dyn_cast_info<#cir.global_view<@_ZTI4Base> : !cir.ptr<!u8i>, #cir.global_view<@_ZTI7Derived> : !cir.ptr<!u8i>, @__dynamic_cast, @__cxa_bad_cast, #cir.int<0> : !s64i>
+
+module {
+  cir.global "private" constant external @_ZTI4Base : !cir.ptr<!u8i>
+  cir.global "private" constant external @_ZTI7Derived : !cir.ptr<!u8i>
+  cir.func private @__dynamic_cast(!cir.ptr<!void>, !cir.ptr<!u8i>, !cir.ptr<!u8i>, !s64i) -> !cir.ptr<!void>
+  cir.func private @__cxa_bad_cast()
+
+  cir.func @test_ptr_cast(%arg0: !cir.ptr<!rec_Base>) -> !cir.ptr<!rec_Derived> {
+    %0 = cir.dyn_cast ptr %arg0 : !cir.ptr<!rec_Base> -> !cir.ptr<!rec_Derived> #dyn_cast_info__ZTI4Base__ZTI7Derived
+    cir.return %0 : !cir.ptr<!rec_Derived>
+  }
+
+  // CHECK:   cir.func @test_ptr_cast(%arg0: !cir.ptr<!rec_Base>) -> !cir.ptr<!rec_Derived> {
+  // CHECK:     %0 = cir.dyn_cast ptr %arg0 : !cir.ptr<!rec_Base> -> !cir.ptr<!rec_Derived> #dyn_cast_info__ZTI4Base__ZTI7Derived
+  // CHECK:     cir.return %0 : !cir.ptr<!rec_Derived>
+  // CHECK:   }
+
+  cir.func @test_ref_cast(%arg0: !cir.ptr<!rec_Base>) -> !cir.ptr<!rec_Derived> {
+    %0 = cir.dyn_cast ref %arg0 : !cir.ptr<!rec_Base> -> !cir.ptr<!rec_Derived> #dyn_cast_info__ZTI4Base__ZTI7Derived
+    cir.return %0 : !cir.ptr<!rec_Derived>
+  }
+
+  // CHECK:   cir.func @test_ref_cast(%arg0: !cir.ptr<!rec_Base>) -> !cir.ptr<!rec_Derived> {
+  // CHECK:     %0 = cir.dyn_cast ref %arg0 : !cir.ptr<!rec_Base> -> !cir.ptr<!rec_Derived> #dyn_cast_info__ZTI4Base__ZTI7Derived
+  // CHECK:     cir.return %0 : !cir.ptr<!rec_Derived>
+  // CHECK:   }
+
+  cir.func dso_local @test_cast_to_void(%arg0: !cir.ptr<!rec_Base>) -> !cir.ptr<!void> {
+   %0 = cir.dyn_cast ptr %arg0 : !cir.ptr<!rec_Base> -> !cir.ptr<!void>
+   cir.return %0 : !cir.ptr<!void>
+  }
+
+  // CHECK: cir.func dso_local @test_cast_to_void(%arg0: !cir.ptr<!rec_Base>) -> !cir.ptr<!void> {
+  // CHECK:     %0 = cir.dyn_cast ptr %arg0 : !cir.ptr<!rec_Base> -> !cir.ptr<!void>
+  // CHECK:     cir.return %0 : !cir.ptr<!void>
+  // CHECK:   }
+
+  cir.func dso_local @test_relative_layout_cast(%arg0: !cir.ptr<!rec_Base>) -> !cir.ptr<!void> {
+   %0 = cir.dyn_cast ptr relative_layout %arg0 : !cir.ptr<!rec_Base> -> !cir.ptr<!void>
+   cir.return %0 : !cir.ptr<!void>
+  }
+
+  // CHECK: cir.func dso_local @test_relative_layout_cast(%arg0: !cir.ptr<!rec_Base>) -> !cir.ptr<!void> {
+  // CHECK:     %0 = cir.dyn_cast ptr relative_layout %arg0 : !cir.ptr<!rec_Base> -> !cir.ptr<!void>
+  // CHECK:     cir.return %0 : !cir.ptr<!void>
+  // CHECK:   }
+}
diff --git a/clang/test/CIR/IR/invalid-dyn-cast.cir b/clang/test/CIR/IR/invalid-dyn-cast.cir
new file mode 100644
index 0000000000000..b6ac3ebcfef95
--- /dev/null
+++ b/clang/test/CIR/IR/invalid-dyn-cast.cir
@@ -0,0 +1,43 @@
+// RUN: cir-opt %s -verify-diagnostics -split-input-file
+
+!s64i = !cir.int<s, 64>
+!s8i = !cir.int<s, 8>
+!u32i = !cir.int<u, 32>
+!u8i = !cir.int<u, 8>
+!void = !cir.void
+
+!Base = !cir.record<struct "Base" {!cir.ptr<!cir.ptr<!cir.func<() -> !cir.int<u, 32>>>>}>
+!Derived = !cir.record<struct "Derived" {!cir.record<struct "Base" {!cir.ptr<!cir.ptr<!cir.func<() -> !cir.int<u, 32>>>>}>}>
+
+module {
+  cir.global "private" constant external @_ZTI4Base : !cir.ptr<!u32i>
+  cir.global "private" constant external @_ZTI7Derived : !cir.ptr<!u8i>
+  cir.func private @__dynamic_cast(!cir.ptr<!void>, !cir.ptr<!u8i>, !cir.ptr<!u8i>, !s64i) -> !cir.ptr<!void>
+  cir.func private @__cxa_bad_cast()
+  cir.func @test(%arg0 : !cir.ptr<!Base>) {
+    // expected-error at +1 {{srcRtti must be an RTTI pointer}}
+    %0 = cir.dyn_cast ptr %arg0 : !cir.ptr<!Base> -> !cir.ptr<!Derived> #cir.dyn_cast_info<#cir.global_view<@_ZTI4Base> : !cir.ptr<!u32i>, #cir.global_view<@_ZTI7Derived> : !cir.ptr<!u8i>, @__dynamic_cast, @__cxa_bad_cast, #cir.int<0> : !s64i>
+  }
+}
+
+// -----
+
+!s64i = !cir.int<s, 64>
+!s8i = !cir.int<s, 8>
+!u32i = !cir.int<u, 32>
+!u8i = !cir.int<u, 8>
+!void = !cir.void
+
+!Base = !cir.record<struct "Base" {!cir.ptr<!cir.ptr<!cir.func<() -> !cir.int<u, 32>>>>}>
+!Derived = !cir.record<struct "Derived" {!cir.record<struct "Base" {!cir.ptr<!cir.ptr<!cir.func<() -> !cir.int<u, 32>>>>}>}>
+
+module {
+  cir.global "private" constant external @_ZTI4Base : !cir.ptr<!u8i>
+  cir.global "private" constant external @_ZTI7Derived : !cir.ptr<!u32i>
+  cir.func private @__dynamic_cast(!cir.ptr<!void>, !cir.ptr<!u8i>, !cir.ptr<!u8i>, !s64i) -> !cir.ptr<!void>
+  cir.func private @__cxa_bad_cast()
+  cir.func @test(%arg0 : !cir.ptr<!Base>) {
+    // expected-error at +1 {{destRtti must be an RTTI pointer}}
+    %0 = cir.dyn_cast ptr %arg0 : !cir.ptr<!Base> -> !cir.ptr<!Derived> #cir.dyn_cast_info<#cir.global_view<@_ZTI4Base> : !cir.ptr<!u8i>, #cir.global_view<@_ZTI7Derived> : !cir.ptr<!u32i>, @__dynamic_cast, @__cxa_bad_cast, #cir.int<0> : !s64i>
+  }
+}

>From d849dc26f92a1028ac728f2a1af873ab6c11438c Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Fri, 3 Oct 2025 10:41:11 -0700
Subject: [PATCH 2/3] Apply review feedback

---
 clang/include/clang/CIR/Dialect/IR/CIRAttrs.td |  5 +++--
 clang/include/clang/CIR/Dialect/IR/CIROps.td   | 12 ++++++------
 clang/test/CIR/IR/dynamic-cast.cir             |  4 ++--
 clang/test/CIR/IR/invalid-dyn-cast.cir         |  4 ++--
 4 files changed, 13 insertions(+), 12 deletions(-)

diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
index 0a757a0a68f3c..1cd67ef794f69 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
@@ -643,8 +643,9 @@ def CIR_DynamicCastInfoAttr : CIR_Attr<"DynamicCastInfo", "dyn_cast_info"> {
   let genVerifyDecl = 1;
   let assemblyFormat = [{
     `<`
-      qualified($srcRtti) `,` qualified($destRtti) `,`
-      $runtimeFunc `,` $badCastFunc `,` qualified($offsetHint)
+      `srcRtti` `=` qualified($srcRtti) `,` `destRtti` `=` qualified($destRtti)
+      `,` `runtimeFunc` `=` $runtimeFunc `,` `badCastFunc` `=` $badCastFunc `,`
+      `offsetHint` `=` qualified($offsetHint)
     `>`
   }];
 
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index f2183bf509219..ce7019f9e21f7 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -285,11 +285,11 @@ def CIR_DynamicCastOp : CIR_Op<"dyn_cast"> {
               -> !cir.ptr<!rec_Derived>
     %2 = cir.dyn_cast ref %r : !cir.ptr<!rec_Base> -> !cir.ptr<!rec_Derived>
               #cir.dyn_cast_info<
-                #cir.global_view<@_ZTI4Base> : !cir.ptr<!u8i>,
-                #cir.global_view<@_ZTI7Derived> : !cir.ptr<!u8i>,
-                @__dynamic_cast,
-                @__cxa_bad_cast,
-                #cir.int<0> : !s64i
+                srcRtti = #cir.global_view<@_ZTI4Base> : !cir.ptr<!u8i>,
+                destRtti = #cir.global_view<@_ZTI7Derived> : !cir.ptr<!u8i>,
+                runtimeFunc = @__dynamic_cast,
+                badCastFunc = @__cxa_bad_cast,
+                offsetHint = #cir.int<0> : !s64i
               >
     ```
   }];
@@ -313,7 +313,7 @@ def CIR_DynamicCastOp : CIR_Op<"dyn_cast"> {
 
   let extraClassDeclaration = [{
     /// Determine whether this operation models reference casting in C++.
-    bool isRefcast() {
+    bool isRefCast() {
       return getKind() == ::cir::DynamicCastKind::Ref;
     }
 
diff --git a/clang/test/CIR/IR/dynamic-cast.cir b/clang/test/CIR/IR/dynamic-cast.cir
index a7468f1e97211..3d9ac2cbebe76 100644
--- a/clang/test/CIR/IR/dynamic-cast.cir
+++ b/clang/test/CIR/IR/dynamic-cast.cir
@@ -7,9 +7,9 @@
 !rec_Base = !cir.record<struct "Base" {!cir.vptr}>
 !rec_Derived = !cir.record<struct "Derived" {!rec_Base}>
 
-#dyn_cast_info__ZTI4Base__ZTI7Derived = #cir.dyn_cast_info<#cir.global_view<@_ZTI4Base> : !cir.ptr<!u8i>, #cir.global_view<@_ZTI7Derived> : !cir.ptr<!u8i>, @__dynamic_cast, @__cxa_bad_cast, #cir.int<0> : !s64i>
+#dyn_cast_info__ZTI4Base__ZTI7Derived = #cir.dyn_cast_info<srcRtti = #cir.global_view<@_ZTI4Base> : !cir.ptr<!u8i>, destRtti = #cir.global_view<@_ZTI7Derived> : !cir.ptr<!u8i>, runtimeFunc = @__dynamic_cast, badCastFunc = @__cxa_bad_cast, offsetHint = #cir.int<0> : !s64i>
 
-// CHECK: #dyn_cast_info__ZTI4Base__ZTI7Derived = #cir.dyn_cast_info<#cir.global_view<@_ZTI4Base> : !cir.ptr<!u8i>, #cir.global_view<@_ZTI7Derived> : !cir.ptr<!u8i>, @__dynamic_cast, @__cxa_bad_cast, #cir.int<0> : !s64i>
+// CHECK: #dyn_cast_info__ZTI4Base__ZTI7Derived = #cir.dyn_cast_info<srcRtti = #cir.global_view<@_ZTI4Base> : !cir.ptr<!u8i>, destRtti = #cir.global_view<@_ZTI7Derived> : !cir.ptr<!u8i>, runtimeFunc = @__dynamic_cast, badCastFunc = @__cxa_bad_cast, offsetHint = #cir.int<0> : !s64i>
 
 module {
   cir.global "private" constant external @_ZTI4Base : !cir.ptr<!u8i>
diff --git a/clang/test/CIR/IR/invalid-dyn-cast.cir b/clang/test/CIR/IR/invalid-dyn-cast.cir
index b6ac3ebcfef95..b5f0c928a634f 100644
--- a/clang/test/CIR/IR/invalid-dyn-cast.cir
+++ b/clang/test/CIR/IR/invalid-dyn-cast.cir
@@ -16,7 +16,7 @@ module {
   cir.func private @__cxa_bad_cast()
   cir.func @test(%arg0 : !cir.ptr<!Base>) {
     // expected-error at +1 {{srcRtti must be an RTTI pointer}}
-    %0 = cir.dyn_cast ptr %arg0 : !cir.ptr<!Base> -> !cir.ptr<!Derived> #cir.dyn_cast_info<#cir.global_view<@_ZTI4Base> : !cir.ptr<!u32i>, #cir.global_view<@_ZTI7Derived> : !cir.ptr<!u8i>, @__dynamic_cast, @__cxa_bad_cast, #cir.int<0> : !s64i>
+    %0 = cir.dyn_cast ptr %arg0 : !cir.ptr<!Base> -> !cir.ptr<!Derived> #cir.dyn_cast_info<srcRtti = #cir.global_view<@_ZTI4Base> : !cir.ptr<!u32i>, destRtti = #cir.global_view<@_ZTI7Derived> : !cir.ptr<!u8i>, runtimeFunc = @__dynamic_cast, badCastFunc = @__cxa_bad_cast, offsetHint = #cir.int<0> : !s64i>
   }
 }
 
@@ -38,6 +38,6 @@ module {
   cir.func private @__cxa_bad_cast()
   cir.func @test(%arg0 : !cir.ptr<!Base>) {
     // expected-error at +1 {{destRtti must be an RTTI pointer}}
-    %0 = cir.dyn_cast ptr %arg0 : !cir.ptr<!Base> -> !cir.ptr<!Derived> #cir.dyn_cast_info<#cir.global_view<@_ZTI4Base> : !cir.ptr<!u8i>, #cir.global_view<@_ZTI7Derived> : !cir.ptr<!u32i>, @__dynamic_cast, @__cxa_bad_cast, #cir.int<0> : !s64i>
+    %0 = cir.dyn_cast ptr %arg0 : !cir.ptr<!Base> -> !cir.ptr<!Derived> #cir.dyn_cast_info<srcRtti = #cir.global_view<@_ZTI4Base> : !cir.ptr<!u8i>, destRtti = #cir.global_view<@_ZTI7Derived> : !cir.ptr<!u32i>, runtimeFunc = @__dynamic_cast, badCastFunc = @__cxa_bad_cast, offsetHint = #cir.int<0> : !s64i>
   }
 }

>From 5bc63da62ba59eedada8fbf51d40da1c532b4210 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Mon, 6 Oct 2025 13:32:46 -0700
Subject: [PATCH 3/3] Apply more review feedback

---
 .../include/clang/CIR/Dialect/IR/CIRAttrs.td  | 42 ++++++++++---------
 clang/test/CIR/IR/dynamic-cast.cir            |  4 +-
 clang/test/CIR/IR/invalid-dyn-cast.cir        |  4 +-
 3 files changed, 26 insertions(+), 24 deletions(-)

diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
index 1cd67ef794f69..38dcf428b62de 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
@@ -610,42 +610,44 @@ def CIR_DynamicCastInfoAttr : CIR_Attr<"DynamicCastInfo", "dyn_cast_info"> {
   let description = [{
     Provide ABI specific information about a dynamic cast operation.
 
-    The `srcRtti` and the `destRtti` parameters give the RTTI of the source
+    The `src_rtti` and the `dest_rtti` parameters give the RTTI of the source
     record type and the destination record type, respectively.
 
-    The `runtimeFunc` parameter gives the `__dynamic_cast` function which is
-    provided by the runtime. The `badCastFunc` parameter gives the
+    The `runtime_func` parameter gives the `__dynamic_cast` function which is
+    provided by the runtime. The `bad_cast_func` parameter gives the
     `__cxa_bad_cast` function which is also provided by the runtime.
 
-    The `offsetHint` parameter gives the hint value that should be passed to the
-    `__dynamic_cast` runtime function.
+    The `offset_hint` parameter gives the hint value that should be passed to
+    the `__dynamic_cast` runtime function.
   }];
 
   let parameters = (ins
-    CIR_GlobalViewAttr:$srcRtti,
-    CIR_GlobalViewAttr:$destRtti,
-    "mlir::FlatSymbolRefAttr":$runtimeFunc,
-    "mlir::FlatSymbolRefAttr":$badCastFunc,
-    CIR_IntAttr:$offsetHint
+    CIR_GlobalViewAttr:$src_rtti,
+    CIR_GlobalViewAttr:$dest_rtti,
+    "mlir::FlatSymbolRefAttr":$runtime_func,
+    "mlir::FlatSymbolRefAttr":$bad_cast_func,
+    CIR_IntAttr:$offset_hint
   );
 
   let builders = [
-    AttrBuilderWithInferredContext<(ins "GlobalViewAttr":$srcRtti,
-                                        "GlobalViewAttr":$destRtti,
-                                        "mlir::FlatSymbolRefAttr":$runtimeFunc,
-                                        "mlir::FlatSymbolRefAttr":$badCastFunc,
-                                        "IntAttr":$offsetHint), [{
-      return $_get(srcRtti.getContext(), srcRtti, destRtti, runtimeFunc,
-                   badCastFunc, offsetHint);
+    AttrBuilderWithInferredContext<(ins "GlobalViewAttr":$src_rtti,
+                                        "GlobalViewAttr":$dest_rtti,
+                                        "mlir::FlatSymbolRefAttr":$runtime_func,
+                                        "mlir::FlatSymbolRefAttr":$bad_cast_func,
+                                        "IntAttr":$offset_hint), [{
+      return $_get(src_rtti.getContext(), src_rtti, dest_rtti, runtime_func,
+                   bad_cast_func, offset_hint);
     }]>,
   ];
 
   let genVerifyDecl = 1;
   let assemblyFormat = [{
     `<`
-      `srcRtti` `=` qualified($srcRtti) `,` `destRtti` `=` qualified($destRtti)
-      `,` `runtimeFunc` `=` $runtimeFunc `,` `badCastFunc` `=` $badCastFunc `,`
-      `offsetHint` `=` qualified($offsetHint)
+      struct(qualified($src_rtti),
+             qualified($dest_rtti),
+             $runtime_func,
+             $bad_cast_func,
+             qualified($offset_hint))
     `>`
   }];
 
diff --git a/clang/test/CIR/IR/dynamic-cast.cir b/clang/test/CIR/IR/dynamic-cast.cir
index 3d9ac2cbebe76..283f11e25b82b 100644
--- a/clang/test/CIR/IR/dynamic-cast.cir
+++ b/clang/test/CIR/IR/dynamic-cast.cir
@@ -7,9 +7,9 @@
 !rec_Base = !cir.record<struct "Base" {!cir.vptr}>
 !rec_Derived = !cir.record<struct "Derived" {!rec_Base}>
 
-#dyn_cast_info__ZTI4Base__ZTI7Derived = #cir.dyn_cast_info<srcRtti = #cir.global_view<@_ZTI4Base> : !cir.ptr<!u8i>, destRtti = #cir.global_view<@_ZTI7Derived> : !cir.ptr<!u8i>, runtimeFunc = @__dynamic_cast, badCastFunc = @__cxa_bad_cast, offsetHint = #cir.int<0> : !s64i>
+#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>
 
-// CHECK: #dyn_cast_info__ZTI4Base__ZTI7Derived = #cir.dyn_cast_info<srcRtti = #cir.global_view<@_ZTI4Base> : !cir.ptr<!u8i>, destRtti = #cir.global_view<@_ZTI7Derived> : !cir.ptr<!u8i>, runtimeFunc = @__dynamic_cast, badCastFunc = @__cxa_bad_cast, offsetHint = #cir.int<0> : !s64i>
+// CHECK: #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>
 
 module {
   cir.global "private" constant external @_ZTI4Base : !cir.ptr<!u8i>
diff --git a/clang/test/CIR/IR/invalid-dyn-cast.cir b/clang/test/CIR/IR/invalid-dyn-cast.cir
index b5f0c928a634f..65c7fc1e5594b 100644
--- a/clang/test/CIR/IR/invalid-dyn-cast.cir
+++ b/clang/test/CIR/IR/invalid-dyn-cast.cir
@@ -16,7 +16,7 @@ module {
   cir.func private @__cxa_bad_cast()
   cir.func @test(%arg0 : !cir.ptr<!Base>) {
     // expected-error at +1 {{srcRtti must be an RTTI pointer}}
-    %0 = cir.dyn_cast ptr %arg0 : !cir.ptr<!Base> -> !cir.ptr<!Derived> #cir.dyn_cast_info<srcRtti = #cir.global_view<@_ZTI4Base> : !cir.ptr<!u32i>, destRtti = #cir.global_view<@_ZTI7Derived> : !cir.ptr<!u8i>, runtimeFunc = @__dynamic_cast, badCastFunc = @__cxa_bad_cast, offsetHint = #cir.int<0> : !s64i>
+    %0 = cir.dyn_cast ptr %arg0 : !cir.ptr<!Base> -> !cir.ptr<!Derived> #cir.dyn_cast_info<src_rtti = #cir.global_view<@_ZTI4Base> : !cir.ptr<!u32i>, 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>
   }
 }
 
@@ -38,6 +38,6 @@ module {
   cir.func private @__cxa_bad_cast()
   cir.func @test(%arg0 : !cir.ptr<!Base>) {
     // expected-error at +1 {{destRtti must be an RTTI pointer}}
-    %0 = cir.dyn_cast ptr %arg0 : !cir.ptr<!Base> -> !cir.ptr<!Derived> #cir.dyn_cast_info<srcRtti = #cir.global_view<@_ZTI4Base> : !cir.ptr<!u8i>, destRtti = #cir.global_view<@_ZTI7Derived> : !cir.ptr<!u32i>, runtimeFunc = @__dynamic_cast, badCastFunc = @__cxa_bad_cast, offsetHint = #cir.int<0> : !s64i>
+    %0 = cir.dyn_cast ptr %arg0 : !cir.ptr<!Base> -> !cir.ptr<!Derived> #cir.dyn_cast_info<src_rtti = #cir.global_view<@_ZTI4Base> : !cir.ptr<!u8i>, dest_rtti = #cir.global_view<@_ZTI7Derived> : !cir.ptr<!u32i>, runtime_func = @__dynamic_cast, bad_cast_func = @__cxa_bad_cast, offset_hint = #cir.int<0> : !s64i>
   }
 }



More information about the cfe-commits mailing list