Skip to content

Commit a0668a4

Browse files
authored
[CIR] Upstream DynamicCastOp (#161734)
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.
1 parent f0ae4b3 commit a0668a4

File tree

7 files changed

+308
-0
lines changed

7 files changed

+308
-0
lines changed

clang/include/clang/CIR/Dialect/IR/CIRAttrs.td

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,63 @@ def CIR_VTableAttr : CIR_Attr<"VTable", "vtable", [TypedAttrInterface]> {
601601
}];
602602
}
603603

604+
//===----------------------------------------------------------------------===//
605+
// DynamicCastInfoAttr
606+
//===----------------------------------------------------------------------===//
607+
608+
def CIR_DynamicCastInfoAttr : CIR_Attr<"DynamicCastInfo", "dyn_cast_info"> {
609+
let summary = "ABI specific information about a dynamic cast";
610+
let description = [{
611+
Provide ABI specific information about a dynamic cast operation.
612+
613+
The `src_rtti` and the `dest_rtti` parameters give the RTTI of the source
614+
record type and the destination record type, respectively.
615+
616+
The `runtime_func` parameter gives the `__dynamic_cast` function which is
617+
provided by the runtime. The `bad_cast_func` parameter gives the
618+
`__cxa_bad_cast` function which is also provided by the runtime.
619+
620+
The `offset_hint` parameter gives the hint value that should be passed to
621+
the `__dynamic_cast` runtime function.
622+
}];
623+
624+
let parameters = (ins
625+
CIR_GlobalViewAttr:$src_rtti,
626+
CIR_GlobalViewAttr:$dest_rtti,
627+
"mlir::FlatSymbolRefAttr":$runtime_func,
628+
"mlir::FlatSymbolRefAttr":$bad_cast_func,
629+
CIR_IntAttr:$offset_hint
630+
);
631+
632+
let builders = [
633+
AttrBuilderWithInferredContext<(ins
634+
"GlobalViewAttr":$src_rtti,
635+
"GlobalViewAttr":$dest_rtti,
636+
"mlir::FlatSymbolRefAttr":$runtime_func,
637+
"mlir::FlatSymbolRefAttr":$bad_cast_func,
638+
"IntAttr":$offset_hint), [{
639+
return $_get(src_rtti.getContext(), src_rtti, dest_rtti, runtime_func,
640+
bad_cast_func, offset_hint);
641+
}]>,
642+
];
643+
644+
let genVerifyDecl = 1;
645+
let assemblyFormat = [{
646+
`<`
647+
struct(qualified($src_rtti),
648+
qualified($dest_rtti),
649+
$runtime_func,
650+
$bad_cast_func,
651+
qualified($offset_hint))
652+
`>`
653+
}];
654+
655+
let extraClassDeclaration = [{
656+
/// Get attribute alias name for this attribute.
657+
std::string getAlias() const;
658+
}];
659+
}
660+
604661
//===----------------------------------------------------------------------===//
605662
// TargetAddressSpaceAttr
606663
//===----------------------------------------------------------------------===//

clang/include/clang/CIR/Dialect/IR/CIROps.td

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,100 @@ def CIR_CastOp : CIR_Op<"cast", [
232232
}];
233233
}
234234

235+
//===----------------------------------------------------------------------===//
236+
// DynamicCastOp
237+
//===----------------------------------------------------------------------===//
238+
239+
def CIR_DynamicCastKind : CIR_I32EnumAttr<
240+
"DynamicCastKind", "dynamic cast kind", [
241+
I32EnumAttrCase<"Ptr", 0, "ptr">,
242+
I32EnumAttrCase<"Ref", 1, "ref">
243+
]>;
244+
245+
def CIR_DynamicCastOp : CIR_Op<"dyn_cast"> {
246+
let summary = "Perform dynamic cast on record pointers";
247+
let description = [{
248+
The `cir.dyn_cast` operation models part of the semantics of the
249+
`dynamic_cast` operator in C++. It can be used to perform 3 kinds of casts
250+
on record pointers:
251+
252+
- Down-cast, which casts a base class pointer to a derived class pointer;
253+
- Side-cast, which casts a class pointer to a sibling class pointer;
254+
- Cast-to-complete, which casts a class pointer to a void pointer.
255+
256+
The input of the operation must be a record pointer. The result of the
257+
operation is either a record pointer or a void pointer.
258+
259+
The parameter `kind` specifies the semantics of this operation. If its value
260+
is `ptr`, then the operation models dynamic casts on pointers. Otherwise, if
261+
its value is `ref`, the operation models dynamic casts on references.
262+
Specifically:
263+
264+
- When the input pointer is a null pointer value:
265+
- If `kind` is `ref`, the operation will invoke undefined behavior. A
266+
sanitizer check will be emitted if sanitizer is on.
267+
- Otherwise, the operation will return a null pointer value as its result.
268+
- When the runtime type check fails:
269+
- If `kind` is `ref`, the operation will throw a `bad_cast` exception.
270+
- Otherwise, the operation will return a null pointer value as its result.
271+
272+
The `info` argument gives detailed information about the requested dynamic
273+
cast operation. It is an optional `#cir.dyn_cast_info` attribute that is
274+
only present when the operation models a down-cast or a side-cast.
275+
276+
The `relative_layout` argument specifies whether the Itanium C++ ABI vtable
277+
uses relative layout. It is only meaningful when the operation models a
278+
cast-to-complete operation.
279+
280+
Examples:
281+
282+
```mlir
283+
%0 = cir.dyn_cast ptr %p : !cir.ptr<!rec_Base> -> !cir.ptr<!rec_Derived>
284+
%1 = cir.dyn_cast ptr relative_layout %p : !cir.ptr<!rec_Base>
285+
-> !cir.ptr<!rec_Derived>
286+
%2 = cir.dyn_cast ref %r : !cir.ptr<!rec_Base> -> !cir.ptr<!rec_Derived>
287+
#cir.dyn_cast_info<
288+
srcRtti = #cir.global_view<@_ZTI4Base> : !cir.ptr<!u8i>,
289+
destRtti = #cir.global_view<@_ZTI7Derived> : !cir.ptr<!u8i>,
290+
runtimeFunc = @__dynamic_cast,
291+
badCastFunc = @__cxa_bad_cast,
292+
offsetHint = #cir.int<0> : !s64i
293+
>
294+
```
295+
}];
296+
297+
let arguments = (ins
298+
CIR_DynamicCastKind:$kind,
299+
CIR_PtrToRecordType:$src,
300+
OptionalAttr<CIR_DynamicCastInfoAttr>:$info,
301+
UnitAttr:$relative_layout
302+
);
303+
304+
let results = (outs
305+
CIR_PtrToAnyOf<[CIR_VoidType, CIR_RecordType]>:$result
306+
);
307+
308+
let assemblyFormat = [{
309+
$kind (`relative_layout` $relative_layout^)? $src
310+
`:` qualified(type($src)) `->` qualified(type($result))
311+
(qualified($info)^)? attr-dict
312+
}];
313+
314+
let extraClassDeclaration = [{
315+
/// Determine whether this operation models reference casting in C++.
316+
bool isRefCast() {
317+
return getKind() == ::cir::DynamicCastKind::Ref;
318+
}
319+
320+
/// Determine whether this operation represents a dynamic cast to a void
321+
/// pointer.
322+
bool isCastToVoid() {
323+
return getType().isVoidPtr();
324+
}
325+
}];
326+
327+
let hasLLVMLowering = false;
328+
}
235329

236330
//===----------------------------------------------------------------------===//
237331
// PtrStrideOp

clang/include/clang/CIR/Dialect/IR/CIRTypeConstraints.td

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,12 @@ def CIR_AnyComplexOrIntOrBoolOrFloatType
177177
let cppFunctionName = "isComplexOrIntegerOrBoolOrFloatingPointType";
178178
}
179179

180+
//===----------------------------------------------------------------------===//
181+
// Record Type predicates
182+
//===----------------------------------------------------------------------===//
183+
184+
def CIR_AnyRecordType : CIR_TypeBase<"::cir::RecordType", "record type">;
185+
180186
//===----------------------------------------------------------------------===//
181187
// Array Type predicates
182188
//===----------------------------------------------------------------------===//
@@ -234,6 +240,8 @@ def CIR_PtrToIntOrFloatType : CIR_PtrToType<CIR_AnyIntOrFloatType>;
234240

235241
def CIR_PtrToComplexType : CIR_PtrToType<CIR_AnyComplexType>;
236242

243+
def CIR_PtrToRecordType : CIR_PtrToType<CIR_AnyRecordType>;
244+
237245
def CIR_PtrToArray : CIR_PtrToType<CIR_AnyArrayType>;
238246

239247
//===----------------------------------------------------------------------===//

clang/lib/CIR/Dialect/IR/CIRAttrs.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,49 @@ LogicalResult cir::VTableAttr::verify(
472472
return success();
473473
}
474474

475+
//===----------------------------------------------------------------------===//
476+
// DynamicCastInfoAtttr definitions
477+
//===----------------------------------------------------------------------===//
478+
479+
std::string DynamicCastInfoAttr::getAlias() const {
480+
// The alias looks like: `dyn_cast_info_<src>_<dest>`
481+
482+
std::string alias = "dyn_cast_info_";
483+
484+
alias.append(getSrcRtti().getSymbol().getValue());
485+
alias.push_back('_');
486+
alias.append(getDestRtti().getSymbol().getValue());
487+
488+
return alias;
489+
}
490+
491+
LogicalResult DynamicCastInfoAttr::verify(
492+
function_ref<InFlightDiagnostic()> emitError, cir::GlobalViewAttr srcRtti,
493+
cir::GlobalViewAttr destRtti, mlir::FlatSymbolRefAttr runtimeFunc,
494+
mlir::FlatSymbolRefAttr badCastFunc, cir::IntAttr offsetHint) {
495+
auto isRttiPtr = [](mlir::Type ty) {
496+
// RTTI pointers are !cir.ptr<!u8i>.
497+
498+
auto ptrTy = mlir::dyn_cast<cir::PointerType>(ty);
499+
if (!ptrTy)
500+
return false;
501+
502+
auto pointeeIntTy = mlir::dyn_cast<cir::IntType>(ptrTy.getPointee());
503+
if (!pointeeIntTy)
504+
return false;
505+
506+
return pointeeIntTy.isUnsigned() && pointeeIntTy.getWidth() == 8;
507+
};
508+
509+
if (!isRttiPtr(srcRtti.getType()))
510+
return emitError() << "srcRtti must be an RTTI pointer";
511+
512+
if (!isRttiPtr(destRtti.getType()))
513+
return emitError() << "destRtti must be an RTTI pointer";
514+
515+
return success();
516+
}
517+
475518
//===----------------------------------------------------------------------===//
476519
// CIR Dialect
477520
//===----------------------------------------------------------------------===//

clang/lib/CIR/Dialect/IR/CIRDialect.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ struct CIROpAsmDialectInterface : public OpAsmDialectInterface {
7171
os << "bfi_" << bitfield.getName().str();
7272
return AliasResult::FinalAlias;
7373
}
74+
if (auto dynCastInfoAttr = mlir::dyn_cast<cir::DynamicCastInfoAttr>(attr)) {
75+
os << dynCastInfoAttr.getAlias();
76+
return AliasResult::FinalAlias;
77+
}
7478
return AliasResult::NoAlias;
7579
}
7680
};

clang/test/CIR/IR/dynamic-cast.cir

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// RUN: cir-opt --verify-roundtrip %s | FileCheck %s
2+
3+
!s64i = !cir.int<s, 64>
4+
!u8i = !cir.int<u, 8>
5+
!void = !cir.void
6+
7+
!rec_Base = !cir.record<struct "Base" {!cir.vptr}>
8+
!rec_Derived = !cir.record<struct "Derived" {!rec_Base}>
9+
10+
#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>
11+
12+
// 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>
13+
14+
module {
15+
cir.global "private" constant external @_ZTI4Base : !cir.ptr<!u8i>
16+
cir.global "private" constant external @_ZTI7Derived : !cir.ptr<!u8i>
17+
cir.func private @__dynamic_cast(!cir.ptr<!void>, !cir.ptr<!u8i>, !cir.ptr<!u8i>, !s64i) -> !cir.ptr<!void>
18+
cir.func private @__cxa_bad_cast()
19+
20+
cir.func @test_ptr_cast(%arg0: !cir.ptr<!rec_Base>) -> !cir.ptr<!rec_Derived> {
21+
%0 = cir.dyn_cast ptr %arg0 : !cir.ptr<!rec_Base> -> !cir.ptr<!rec_Derived> #dyn_cast_info__ZTI4Base__ZTI7Derived
22+
cir.return %0 : !cir.ptr<!rec_Derived>
23+
}
24+
25+
// CHECK: cir.func @test_ptr_cast(%arg0: !cir.ptr<!rec_Base>) -> !cir.ptr<!rec_Derived> {
26+
// CHECK: %0 = cir.dyn_cast ptr %arg0 : !cir.ptr<!rec_Base> -> !cir.ptr<!rec_Derived> #dyn_cast_info__ZTI4Base__ZTI7Derived
27+
// CHECK: cir.return %0 : !cir.ptr<!rec_Derived>
28+
// CHECK: }
29+
30+
cir.func @test_ref_cast(%arg0: !cir.ptr<!rec_Base>) -> !cir.ptr<!rec_Derived> {
31+
%0 = cir.dyn_cast ref %arg0 : !cir.ptr<!rec_Base> -> !cir.ptr<!rec_Derived> #dyn_cast_info__ZTI4Base__ZTI7Derived
32+
cir.return %0 : !cir.ptr<!rec_Derived>
33+
}
34+
35+
// CHECK: cir.func @test_ref_cast(%arg0: !cir.ptr<!rec_Base>) -> !cir.ptr<!rec_Derived> {
36+
// CHECK: %0 = cir.dyn_cast ref %arg0 : !cir.ptr<!rec_Base> -> !cir.ptr<!rec_Derived> #dyn_cast_info__ZTI4Base__ZTI7Derived
37+
// CHECK: cir.return %0 : !cir.ptr<!rec_Derived>
38+
// CHECK: }
39+
40+
cir.func dso_local @test_cast_to_void(%arg0: !cir.ptr<!rec_Base>) -> !cir.ptr<!void> {
41+
%0 = cir.dyn_cast ptr %arg0 : !cir.ptr<!rec_Base> -> !cir.ptr<!void>
42+
cir.return %0 : !cir.ptr<!void>
43+
}
44+
45+
// CHECK: cir.func dso_local @test_cast_to_void(%arg0: !cir.ptr<!rec_Base>) -> !cir.ptr<!void> {
46+
// CHECK: %0 = cir.dyn_cast ptr %arg0 : !cir.ptr<!rec_Base> -> !cir.ptr<!void>
47+
// CHECK: cir.return %0 : !cir.ptr<!void>
48+
// CHECK: }
49+
50+
cir.func dso_local @test_relative_layout_cast(%arg0: !cir.ptr<!rec_Base>) -> !cir.ptr<!void> {
51+
%0 = cir.dyn_cast ptr relative_layout %arg0 : !cir.ptr<!rec_Base> -> !cir.ptr<!void>
52+
cir.return %0 : !cir.ptr<!void>
53+
}
54+
55+
// CHECK: cir.func dso_local @test_relative_layout_cast(%arg0: !cir.ptr<!rec_Base>) -> !cir.ptr<!void> {
56+
// CHECK: %0 = cir.dyn_cast ptr relative_layout %arg0 : !cir.ptr<!rec_Base> -> !cir.ptr<!void>
57+
// CHECK: cir.return %0 : !cir.ptr<!void>
58+
// CHECK: }
59+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// RUN: cir-opt %s -verify-diagnostics -split-input-file
2+
3+
!s64i = !cir.int<s, 64>
4+
!s8i = !cir.int<s, 8>
5+
!u32i = !cir.int<u, 32>
6+
!u8i = !cir.int<u, 8>
7+
!void = !cir.void
8+
9+
!Base = !cir.record<struct "Base" {!cir.ptr<!cir.ptr<!cir.func<() -> !cir.int<u, 32>>>>}>
10+
!Derived = !cir.record<struct "Derived" {!cir.record<struct "Base" {!cir.ptr<!cir.ptr<!cir.func<() -> !cir.int<u, 32>>>>}>}>
11+
12+
module {
13+
cir.global "private" constant external @_ZTI4Base : !cir.ptr<!u32i>
14+
cir.global "private" constant external @_ZTI7Derived : !cir.ptr<!u8i>
15+
cir.func private @__dynamic_cast(!cir.ptr<!void>, !cir.ptr<!u8i>, !cir.ptr<!u8i>, !s64i) -> !cir.ptr<!void>
16+
cir.func private @__cxa_bad_cast()
17+
cir.func @test(%arg0 : !cir.ptr<!Base>) {
18+
// expected-error@+1 {{srcRtti must be an RTTI pointer}}
19+
%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>
20+
}
21+
}
22+
23+
// -----
24+
25+
!s64i = !cir.int<s, 64>
26+
!s8i = !cir.int<s, 8>
27+
!u32i = !cir.int<u, 32>
28+
!u8i = !cir.int<u, 8>
29+
!void = !cir.void
30+
31+
!Base = !cir.record<struct "Base" {!cir.ptr<!cir.ptr<!cir.func<() -> !cir.int<u, 32>>>>}>
32+
!Derived = !cir.record<struct "Derived" {!cir.record<struct "Base" {!cir.ptr<!cir.ptr<!cir.func<() -> !cir.int<u, 32>>>>}>}>
33+
34+
module {
35+
cir.global "private" constant external @_ZTI4Base : !cir.ptr<!u8i>
36+
cir.global "private" constant external @_ZTI7Derived : !cir.ptr<!u32i>
37+
cir.func private @__dynamic_cast(!cir.ptr<!void>, !cir.ptr<!u8i>, !cir.ptr<!u8i>, !s64i) -> !cir.ptr<!void>
38+
cir.func private @__cxa_bad_cast()
39+
cir.func @test(%arg0 : !cir.ptr<!Base>) {
40+
// expected-error@+1 {{destRtti must be an RTTI pointer}}
41+
%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>
42+
}
43+
}

0 commit comments

Comments
 (0)