diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.h b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.h index 57029c64ffd00..6adbb475cdbf8 100644 --- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.h +++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.h @@ -46,6 +46,14 @@ bool isIntegerIndexOrOpaqueType(Type type); /// Determines whether \p type is a valid floating-point type in EmitC. bool isSupportedFloatType(mlir::Type type); +/// Determines whether \p type is a valid floating-point or opaque type in +/// EmitC. +bool isFloatOrOpaqueType(mlir::Type type); + +/// Determines whether \p type is a valid integer or opaque type in +/// EmitC. +bool isIntegerOrOpaqueType(mlir::Type type); + /// Determines whether \p type is a emitc.size_t/ssize_t type. bool isPointerWideType(mlir::Type type); diff --git a/mlir/lib/Conversion/ArithToEmitC/ArithToEmitC.cpp b/mlir/lib/Conversion/ArithToEmitC/ArithToEmitC.cpp index 359d7b2279639..17bfe11a4429e 100644 --- a/mlir/lib/Conversion/ArithToEmitC/ArithToEmitC.cpp +++ b/mlir/lib/Conversion/ArithToEmitC/ArithToEmitC.cpp @@ -16,6 +16,7 @@ #include "mlir/Dialect/Arith/IR/Arith.h" #include "mlir/Dialect/EmitC/IR/EmitC.h" #include "mlir/Dialect/EmitC/Transforms/TypeConversions.h" +#include "mlir/IR/Attributes.h" #include "mlir/IR/BuiltinAttributes.h" #include "mlir/IR/BuiltinTypes.h" #include "mlir/Transforms/DialectConversion.h" @@ -39,8 +40,17 @@ class ArithConstantOpConversionPattern Type newTy = this->getTypeConverter()->convertType(arithConst.getType()); if (!newTy) return rewriter.notifyMatchFailure(arithConst, "type conversion failed"); + + std::optional opAttrib = + this->getTypeConverter()->convertTypeAttribute( + adaptor.getValue().getType(), adaptor.getValue()); + if (!opAttrib) { + return rewriter.notifyMatchFailure(arithConst, + "attribute conversion failed"); + } + rewriter.replaceOpWithNewOp(arithConst, newTy, - adaptor.getValue()); + opAttrib.value()); return success(); } }; @@ -67,6 +77,7 @@ Type adaptIntegralTypeSignedness(Type ty, bool needsUnsigned) { /// Insert a cast operation to type \p ty if \p val does not have this type. Value adaptValueType(Value val, ConversionPatternRewriter &rewriter, Type ty) { + assert(emitc::isSupportedEmitCType(val.getType())); return rewriter.createOrFold(val.getLoc(), ty, val); } @@ -78,7 +89,7 @@ class CmpFOpConversion : public OpConversionPattern { matchAndRewrite(arith::CmpFOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override { - if (!isa(adaptor.getRhs().getType())) { + if (!emitc::isFloatOrOpaqueType(adaptor.getRhs().getType())) { return rewriter.notifyMatchFailure(op.getLoc(), "cmpf currently only supported on " "floats, not tensors/vectors thereof"); @@ -273,7 +284,8 @@ class CmpIOpConversion : public OpConversionPattern { ConversionPatternRewriter &rewriter) const override { Type type = adaptor.getLhs().getType(); - if (!type || !(isa(type) || emitc::isPointerWideType(type))) { + if (!type || !(emitc::isIntegerOrOpaqueType(type) || + emitc::isPointerWideType(type))) { return rewriter.notifyMatchFailure( op, "expected integer or size_t/ssize_t/ptrdiff_t type"); } @@ -307,7 +319,7 @@ class NegFOpConversion : public OpConversionPattern { "negf currently only supports scalar types, not vectors or tensors"); } - if (!emitc::isSupportedFloatType(adaptedOpType)) { + if (!emitc::isFloatOrOpaqueType(adaptedOpType)) { return rewriter.notifyMatchFailure( op.getLoc(), "floating-point type is not supported by EmitC"); } @@ -328,7 +340,7 @@ class CastConversion : public OpConversionPattern { ConversionPatternRewriter &rewriter) const override { Type opReturnType = this->getTypeConverter()->convertType(op.getType()); - if (!opReturnType || !(isa(opReturnType) || + if (!opReturnType || !(emitc::isIntegerOrOpaqueType(opReturnType) || emitc::isPointerWideType(opReturnType))) return rewriter.notifyMatchFailure( op, "expected integer or size_t/ssize_t/ptrdiff_t result type"); @@ -339,7 +351,7 @@ class CastConversion : public OpConversionPattern { } Type operandType = adaptor.getIn().getType(); - if (!operandType || !(isa(operandType) || + if (!operandType || !(emitc::isIntegerOrOpaqueType(operandType) || emitc::isPointerWideType(operandType))) return rewriter.notifyMatchFailure( op, "expected integer or size_t/ssize_t/ptrdiff_t operand type"); @@ -433,7 +445,8 @@ class BinaryUIOpConversion final : public OpConversionPattern { if (!newRetTy) return rewriter.notifyMatchFailure(uiBinOp, "converting result type failed"); - if (!isa(newRetTy)) { + + if (!emitc::isIntegerOrOpaqueType(newRetTy)) { return rewriter.notifyMatchFailure(uiBinOp, "expected integer type"); } Type unsignedType = @@ -441,8 +454,8 @@ class BinaryUIOpConversion final : public OpConversionPattern { if (!unsignedType) return rewriter.notifyMatchFailure(uiBinOp, "converting result type failed"); - Value lhsAdapted = adaptValueType(uiBinOp.getLhs(), rewriter, unsignedType); - Value rhsAdapted = adaptValueType(uiBinOp.getRhs(), rewriter, unsignedType); + Value lhsAdapted = adaptValueType(adaptor.getLhs(), rewriter, unsignedType); + Value rhsAdapted = adaptValueType(adaptor.getRhs(), rewriter, unsignedType); auto newDivOp = rewriter.create(uiBinOp.getLoc(), unsignedType, @@ -463,7 +476,8 @@ class IntegerOpConversion final : public OpConversionPattern { ConversionPatternRewriter &rewriter) const override { Type type = this->getTypeConverter()->convertType(op.getType()); - if (!type || !(isa(type) || emitc::isPointerWideType(type))) { + if (!type || !(emitc::isIntegerOrOpaqueType(type) || + emitc::isPointerWideType(type))) { return rewriter.notifyMatchFailure( op, "expected integer or size_t/ssize_t/ptrdiff_t type"); } @@ -506,7 +520,7 @@ class BitwiseOpConversion : public OpConversionPattern { ConversionPatternRewriter &rewriter) const override { Type type = this->getTypeConverter()->convertType(op.getType()); - if (!isa_and_nonnull(type)) { + if (!type || !emitc::isIntegerOrOpaqueType(type)) { return rewriter.notifyMatchFailure( op, "expected integer type, vector/tensor support not yet implemented"); @@ -546,7 +560,9 @@ class ShiftOpConversion : public OpConversionPattern { ConversionPatternRewriter &rewriter) const override { Type type = this->getTypeConverter()->convertType(op.getType()); - if (!type || !(isa(type) || emitc::isPointerWideType(type))) { + bool retIsOpaque = isa_and_nonnull(type); + if (!type || (!retIsOpaque && !(isa(type) || + emitc::isPointerWideType(type)))) { return rewriter.notifyMatchFailure( op, "expected integer or size_t/ssize_t/ptrdiff_t type"); } @@ -572,21 +588,33 @@ class ShiftOpConversion : public OpConversionPattern { op.getLoc(), rhsType, "sizeof", ArrayRef{eight}); width = rewriter.create(op.getLoc(), rhsType, eight, sizeOfCall.getResult(0)); - } else { + } else if (!retIsOpaque) { width = rewriter.create( op.getLoc(), rhsType, rewriter.getIntegerAttr(rhsType, type.getIntOrFloatBitWidth())); + } else { + width = rewriter.create( + op.getLoc(), rhsType, + emitc::OpaqueAttr::get(rhsType.getContext(), + "opaque_shift_bitwidth")); } Value excessCheck = rewriter.create( op.getLoc(), rewriter.getI1Type(), emitc::CmpPredicate::lt, rhs, width); // Any concrete value is a valid refinement of poison. - Value poison = rewriter.create( - op.getLoc(), arithmeticType, - (isa(arithmeticType) - ? rewriter.getIntegerAttr(arithmeticType, 0) - : rewriter.getIndexAttr(0))); + Value poison; + if (retIsOpaque) { + poison = rewriter.create( + op.getLoc(), arithmeticType, + emitc::OpaqueAttr::get(rhsType.getContext(), "opaque_shift_poison")); + } else { + poison = rewriter.create( + op.getLoc(), arithmeticType, + (isa(arithmeticType) + ? rewriter.getIntegerAttr(arithmeticType, 0) + : rewriter.getIndexAttr(0))); + } emitc::ExpressionOp ternary = rewriter.create( op.getLoc(), arithmeticType, /*do_not_inline=*/false); @@ -655,7 +683,7 @@ class FtoICastOpConversion : public OpConversionPattern { ConversionPatternRewriter &rewriter) const override { Type operandType = adaptor.getIn().getType(); - if (!emitc::isSupportedFloatType(operandType)) + if (!emitc::isFloatOrOpaqueType(operandType)) return rewriter.notifyMatchFailure(castOp, "unsupported cast source type"); @@ -663,19 +691,23 @@ class FtoICastOpConversion : public OpConversionPattern { if (!dstType) return rewriter.notifyMatchFailure(castOp, "type conversion failed"); + Type actualResultType = dstType; + // Float-to-i1 casts are not supported: any value with 0 < value < 1 must be // truncated to 0, whereas a boolean conversion would return true. - if (!emitc::isSupportedIntegerType(dstType) || dstType.isInteger(1)) - return rewriter.notifyMatchFailure(castOp, - "unsupported cast destination type"); - - // Convert to unsigned if it's the "ui" variant - // Signless is interpreted as signed, so no need to cast for "si" - Type actualResultType = dstType; - if (isa(castOp)) { - actualResultType = - rewriter.getIntegerType(dstType.getIntOrFloatBitWidth(), - /*isSigned=*/false); + bool dstIsOpaque = isa(dstType); + if (!dstIsOpaque) { + if (!emitc::isSupportedIntegerType(dstType) || dstType.isInteger(1)) + return rewriter.notifyMatchFailure(castOp, + "unsupported cast destination type"); + + // Convert to unsigned if it's the "ui" variant + // Signless is interpreted as signed, so no need to cast for "si" + if (isa(castOp)) { + actualResultType = + rewriter.getIntegerType(dstType.getIntOrFloatBitWidth(), + /*isSigned=*/false); + } } Value result = rewriter.create( @@ -702,7 +734,9 @@ class ItoFCastOpConversion : public OpConversionPattern { ConversionPatternRewriter &rewriter) const override { // Vectors in particular are not supported Type operandType = adaptor.getIn().getType(); - if (!emitc::isSupportedIntegerType(operandType)) + bool opIsOpaque = isa(operandType); + + if (!(opIsOpaque || emitc::isSupportedIntegerType(operandType))) return rewriter.notifyMatchFailure(castOp, "unsupported cast source type"); @@ -710,14 +744,14 @@ class ItoFCastOpConversion : public OpConversionPattern { if (!dstType) return rewriter.notifyMatchFailure(castOp, "type conversion failed"); - if (!emitc::isSupportedFloatType(dstType)) + if (!emitc::isFloatOrOpaqueType(dstType)) return rewriter.notifyMatchFailure(castOp, "unsupported cast destination type"); // Convert to unsigned if it's the "ui" variant // Signless is interpreted as signed, so no need to cast for "si" Type actualOperandType = operandType; - if (isa(castOp)) { + if (!opIsOpaque && isa(castOp)) { actualOperandType = rewriter.getIntegerType(operandType.getIntOrFloatBitWidth(), /*isSigned=*/false); @@ -745,7 +779,7 @@ class FpCastOpConversion : public OpConversionPattern { ConversionPatternRewriter &rewriter) const override { // Vectors in particular are not supported. Type operandType = adaptor.getIn().getType(); - if (!emitc::isSupportedFloatType(operandType)) + if (!emitc::isFloatOrOpaqueType(operandType)) return rewriter.notifyMatchFailure(castOp, "unsupported cast source type"); if (auto roundingModeOp = @@ -759,7 +793,7 @@ class FpCastOpConversion : public OpConversionPattern { if (!dstType) return rewriter.notifyMatchFailure(castOp, "type conversion failed"); - if (!emitc::isSupportedFloatType(dstType)) + if (!emitc::isFloatOrOpaqueType(dstType)) return rewriter.notifyMatchFailure(castOp, "unsupported cast destination type"); diff --git a/mlir/lib/Conversion/ArithToEmitC/ArithToEmitCPass.cpp b/mlir/lib/Conversion/ArithToEmitC/ArithToEmitCPass.cpp index 45a088ed144f1..e64e4176d3baa 100644 --- a/mlir/lib/Conversion/ArithToEmitC/ArithToEmitCPass.cpp +++ b/mlir/lib/Conversion/ArithToEmitC/ArithToEmitCPass.cpp @@ -30,9 +30,42 @@ namespace { struct ConvertArithToEmitC : public impl::ConvertArithToEmitCBase { void runOnOperation() override; + + /// Applies conversion to opaque types for f80 and i80 types, both unsupported + /// in emitc. Used to test the pass with opaque types. + void populateOpaqueTypeConversions(TypeConverter &converter); }; } // namespace +void ConvertArithToEmitC::populateOpaqueTypeConversions( + TypeConverter &converter) { + converter.addConversion([](Type type) -> std::optional { + if (type.isF80()) + return emitc::OpaqueType::get(type.getContext(), "f80"); + if (type.isInteger() && type.getIntOrFloatBitWidth() == 80) + return emitc::OpaqueType::get(type.getContext(), "i80"); + return type; + }); + + converter.addTypeAttributeConversion( + [](Type type, + Attribute attrToConvert) -> TypeConverter::AttributeConversionResult { + if (auto floatAttr = llvm::dyn_cast(attrToConvert)) { + if (floatAttr.getType().isF80()) { + return emitc::OpaqueAttr::get(type.getContext(), "f80"); + } + return attrToConvert; + } + if (auto intAttr = llvm::dyn_cast(attrToConvert)) { + if (intAttr.getType().isInteger() && + intAttr.getType().getIntOrFloatBitWidth() == 80) { + return emitc::OpaqueAttr::get(type.getContext(), "i80"); + } + } + return attrToConvert; + }); +} + void ConvertArithToEmitC::runOnOperation() { ConversionTarget target(getContext()); @@ -42,8 +75,8 @@ void ConvertArithToEmitC::runOnOperation() { RewritePatternSet patterns(&getContext()); TypeConverter typeConverter; - typeConverter.addConversion([](Type type) { return type; }); + populateOpaqueTypeConversions(typeConverter); populateArithToEmitCPatterns(typeConverter, patterns); if (failed( diff --git a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp index b4d7482554fbc..c34752f92c794 100644 --- a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp +++ b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp @@ -132,6 +132,14 @@ bool mlir::emitc::isSupportedFloatType(Type type) { return false; } +bool mlir::emitc::isIntegerOrOpaqueType(Type type) { + return isa(type) || isSupportedIntegerType(type); +} + +bool mlir::emitc::isFloatOrOpaqueType(Type type) { + return isa(type) || isSupportedFloatType(type); +} + bool mlir::emitc::isPointerWideType(Type type) { return isa( type); diff --git a/mlir/test/Conversion/ArithToEmitC/arith-to-emitc-unsupported.mlir b/mlir/test/Conversion/ArithToEmitC/arith-to-emitc-unsupported.mlir index 9850f336b5ad6..a71de136e6d76 100644 --- a/mlir/test/Conversion/ArithToEmitC/arith-to-emitc-unsupported.mlir +++ b/mlir/test/Conversion/ArithToEmitC/arith-to-emitc-unsupported.mlir @@ -14,13 +14,6 @@ func.func @arith_cast_vector(%arg0: vector<5xf32>) -> vector<5xi32> { return %t: vector<5xi32> } -// ----- -func.func @arith_cast_f80(%arg0: f80) -> i32 { - // expected-error @+1 {{failed to legalize operation 'arith.fptosi'}} - %t = arith.fptosi %arg0 : f80 to i32 - return %t: i32 -} - // ----- func.func @arith_cast_f128(%arg0: f128) -> i32 { @@ -29,15 +22,6 @@ func.func @arith_cast_f128(%arg0: f128) -> i32 { return %t: i32 } - -// ----- - -func.func @arith_cast_to_f80(%arg0: i32) -> f80 { - // expected-error @+1 {{failed to legalize operation 'arith.sitofp'}} - %t = arith.sitofp %arg0 : i32 to f80 - return %t: f80 -} - // ----- func.func @arith_cast_to_f128(%arg0: i32) -> f128 { @@ -80,14 +64,6 @@ func.func @arith_cmpf_tensor(%arg0: tensor<5xf32>, %arg1: tensor<5xf32>) -> tens // ----- -func.func @arith_negf_f80(%arg0: f80) -> f80 { - // expected-error @+1 {{failed to legalize operation 'arith.negf'}} - %n = arith.negf %arg0 : f80 - return %n: f80 -} - -// ----- - func.func @arith_negf_tensor(%arg0: tensor<5xf32>) -> tensor<5xf32> { // expected-error @+1 {{failed to legalize operation 'arith.negf'}} %n = arith.negf %arg0 : tensor<5xf32> diff --git a/mlir/test/Conversion/ArithToEmitC/arith-to-emitc.mlir b/mlir/test/Conversion/ArithToEmitC/arith-to-emitc.mlir index cb1d092918f03..c1247cb97e440 100644 --- a/mlir/test/Conversion/ArithToEmitC/arith-to-emitc.mlir +++ b/mlir/test/Conversion/ArithToEmitC/arith-to-emitc.mlir @@ -771,3 +771,74 @@ func.func @arith_truncf(%arg0: f64) -> f16 { return %truncd1 : f16 } + +// ----- + +func.func @float_opaque_conversion(%arg0: f80, %arg1: f80) { + // CHECK-LABEL: float_opaque_conversion + // CHECK-SAME: (%[[Arg0:[^ ]*]]: f80, %[[Arg1:[^ ]*]]: f80) + + // CHECK-DAG: [[arg1_cast:[^ ]*]] = builtin.unrealized_conversion_cast %[[Arg1]] : f80 to !emitc.opaque<"f80"> + // CHECK-DAG: [[arg0_cast:[^ ]*]] = builtin.unrealized_conversion_cast %[[Arg0]] : f80 to !emitc.opaque<"f80"> + // CHECK: "emitc.constant"() <{value = #emitc.opaque<"f80">}> : () -> !emitc.opaque<"f80"> + %10 = arith.constant 0.0 : f80 + // CHECK: emitc.add [[arg0_cast]], [[arg1_cast]] : (!emitc.opaque<"f80">, !emitc.opaque<"f80">) -> !emitc.opaque<"f80"> + %2 = arith.addf %arg0, %arg1 : f80 + // CHECK: [[EQ:[^ ]*]] = emitc.cmp eq, [[arg0_cast]], [[arg1_cast]] : (!emitc.opaque<"f80">, !emitc.opaque<"f80">) -> i1 + // CHECK: [[NotNaNArg0:[^ ]*]] = emitc.cmp eq, [[arg0_cast]], [[arg0_cast]] : (!emitc.opaque<"f80">, !emitc.opaque<"f80">) -> i1 + // CHECK: [[NotNaNArg1:[^ ]*]] = emitc.cmp eq, [[arg1_cast]], [[arg1_cast]] : (!emitc.opaque<"f80">, !emitc.opaque<"f80">) -> i1 + // CHECK: [[Ordered:[^ ]*]] = emitc.logical_and [[NotNaNArg0]], [[NotNaNArg1]] : i1, i1 + // CHECK: emitc.logical_and [[Ordered]], [[EQ]] : i1, i1 + %11 = arith.cmpf oeq, %arg0, %arg1 : f80 + // CHECK: emitc.unary_minus [[arg0_cast]] : (!emitc.opaque<"f80">) -> !emitc.opaque<"f80"> + %12 = arith.negf %arg0 : f80 + // CHECK: [[V0:[^ ]*]] = emitc.cast [[arg0_cast]] : !emitc.opaque<"f80"> to ui32 + // CHECK: [[V1:[^ ]*]] = emitc.cast [[V0]] : ui32 to i32 + %7 = arith.fptoui %arg0 : f80 to i32 + // CHECK: emitc.cast [[V1]] : i32 to !emitc.opaque<"f80"> + %8 = arith.sitofp %7 : i32 to f80 + // CHECK: [[trunc:[^ ]*]] = emitc.cast [[arg0_cast]] : !emitc.opaque<"f80"> to f32 + %13 = arith.truncf %arg0 : f80 to f32 + // CHECK: emitc.cast [[trunc]] : f32 to !emitc.opaque<"f80"> + %15 = arith.extf %13 : f32 to f80 + return +} + +// ----- + +func.func @int_opaque_conversion(%arg0: i80, %arg1: i80, %arg2: i1) { + // CHECK-LABEL: int_opaque_conversion + // CHECK-SAME: (%[[Arg0:[^ ]*]]: i80, %[[Arg1:[^ ]*]]: i80, %[[Arg2:[^ ]*]]: i1) + + // CHECK-DAG: [[arg1_cast:[^ ]*]] = builtin.unrealized_conversion_cast %[[Arg1]] : i80 to !emitc.opaque<"i80"> + // CHECK-DAG: [[arg0_cast:[^ ]*]] = builtin.unrealized_conversion_cast %[[Arg0]] : i80 to !emitc.opaque<"i80"> + // CHECK: "emitc.constant"() <{value = #emitc.opaque<"i80">}> : () -> !emitc.opaque<"i80"> + %10 = arith.constant 0 : i80 + // CHECK: emitc.div [[arg0_cast]], [[arg1_cast]] : (!emitc.opaque<"i80">, !emitc.opaque<"i80">) -> !emitc.opaque<"i80"> + %3 = arith.divui %arg0, %arg1 : i80 + // CHECK: emitc.add [[arg0_cast]], [[arg1_cast]] : (!emitc.opaque<"i80">, !emitc.opaque<"i80">) -> !emitc.opaque<"i80"> + %2 = arith.addi %arg0, %arg1 : i80 + // CHECK: emitc.bitwise_and [[arg0_cast]], [[arg1_cast]] : (!emitc.opaque<"i80">, !emitc.opaque<"i80">) -> !emitc.opaque<"i80"> + %14 = arith.andi %arg0, %arg1 : i80 + // CHECK: [[Bitwidth:[^ ]*]] = "emitc.constant"() <{value = #emitc.opaque<"opaque_shift_bitwidth">}> : () -> !emitc.opaque<"i80"> + // CHECK: [[LT:[^ ]*]] = emitc.cmp lt, [[arg1_cast]], [[Bitwidth]] : (!emitc.opaque<"i80">, !emitc.opaque<"i80">) -> i1 + // CHECK: [[Poison:[^ ]*]] = "emitc.constant"() <{value = #emitc.opaque<"opaque_shift_poison">}> : () -> !emitc.opaque<"i80"> + // CHECK: [[Exp:[^ ]*]] = emitc.expression : !emitc.opaque<"i80"> { + // CHECK: [[LShift:[^ ]*]] = bitwise_left_shift [[arg0_cast]], [[arg1_cast]] : (!emitc.opaque<"i80">, !emitc.opaque<"i80">) -> !emitc.opaque<"i80"> + // CHECK: conditional [[LT]], [[LShift]], [[Poison]] : !emitc.opaque<"i80"> + // CHECK: yield {{.*}} : !emitc.opaque<"i80"> + // CHECK: } + %12 = arith.shli %arg0, %arg1 : i80 + // CHECK: emitc.cmp eq, [[arg0_cast]], [[arg1_cast]] : (!emitc.opaque<"i80">, !emitc.opaque<"i80">) -> i1 + %11 = arith.cmpi eq, %arg0, %arg1 : i80 + // CHECK: emitc.conditional %[[Arg2]], [[arg0_cast]], [[arg1_cast]] : !emitc.opaque<"i80"> + %13 = arith.select %arg2, %arg0, %arg1 : i80 + // CHECK: [[V0:[^ ]*]] = emitc.cast [[arg0_cast]] : !emitc.opaque<"i80"> to ui8 + // CHECK: emitc.cast [[V0]] : ui8 to i8 + %15 = arith.trunci %arg0 : i80 to i8 + // CHECK: [[V1:[^ ]*]] = emitc.cast [[arg0_cast]] : !emitc.opaque<"i80"> to f32 + %9 = arith.uitofp %arg0 : i80 to f32 + // CHECK: emitc.cast [[V1]] : f32 to !emitc.opaque<"i80"> + %6 = arith.fptosi %9 : f32 to i80 + return +}