llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clangir Author: Andy Kaylor (andykaylor) <details> <summary>Changes</summary> 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. --- Full diff: https://github.com/llvm/llvm-project/pull/161734.diff 7 Files Affected: - (modified) clang/include/clang/CIR/Dialect/IR/CIRAttrs.td (+53) - (modified) clang/include/clang/CIR/Dialect/IR/CIROps.td (+94) - (modified) clang/include/clang/CIR/Dialect/IR/CIRTypeConstraints.td (+8) - (modified) clang/lib/CIR/Dialect/IR/CIRAttrs.cpp (+43) - (modified) clang/lib/CIR/Dialect/IR/CIRDialect.cpp (+4) - (added) clang/test/CIR/IR/dynamic-cast.cir (+59) - (added) clang/test/CIR/IR/invalid-dyn-cast.cir (+43) ``````````diff diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td index f8358de9a1eb9..7aa0363abd689 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; + }]; +} + //===----------------------------------------------------------------------===// // ConstComplexAttr //===----------------------------------------------------------------------===// diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 0a78492aa9a86..3f4fec37a0967 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 95faad6746955..f95c70b5ae892 100644 --- a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp @@ -462,6 +462,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 6b5cc808e9a29..cda43c98c5f01 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@+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@+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> + } +} `````````` </details> https://github.com/llvm/llvm-project/pull/161734 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
