Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,63 @@ 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 `src_rtti` and the `dest_rtti` parameters give the RTTI of the source
record type and the destination record type, respectively.

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 `offset_hint` parameter gives the hint value that should be passed to
the `__dynamic_cast` runtime function.
}];

let parameters = (ins
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":$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 = [{
`<`
struct(qualified($src_rtti),
qualified($dest_rtti),
$runtime_func,
$bad_cast_func,
qualified($offset_hint))
`>`
}];

let extraClassDeclaration = [{
/// Get attribute alias name for this attribute.
std::string getAlias() const;
}];
}

//===----------------------------------------------------------------------===//
// TargetAddressSpaceAttr
//===----------------------------------------------------------------------===//
Expand Down
94 changes: 94 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -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<
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
>
```
}];

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
Expand Down
8 changes: 8 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIRTypeConstraints.td
Original file line number Diff line number Diff line change
Expand Up @@ -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
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -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>;

//===----------------------------------------------------------------------===//
Expand Down
43 changes: 43 additions & 0 deletions clang/lib/CIR/Dialect/IR/CIRAttrs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
//===----------------------------------------------------------------------===//
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/CIR/Dialect/IR/CIRDialect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
};
Expand Down
59 changes: 59 additions & 0 deletions clang/test/CIR/IR/dynamic-cast.cir
Original file line number Diff line number Diff line change
@@ -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<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<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>
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: }
}
43 changes: 43 additions & 0 deletions clang/test/CIR/IR/invalid-dyn-cast.cir
Original file line number Diff line number Diff line change
@@ -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@+1 {{srcRtti must be an RTTI pointer}}
%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>
}
}

// -----

!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@+1 {{destRtti must be an RTTI pointer}}
%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>
}
}