diff --git a/mlir/include/mlir/Dialect/Math/IR/MathOps.td b/mlir/include/mlir/Dialect/Math/IR/MathOps.td index fdb9ec09ae3e7..3f6d2d2e44783 100644 --- a/mlir/include/mlir/Dialect/Math/IR/MathOps.td +++ b/mlir/include/mlir/Dialect/Math/IR/MathOps.td @@ -135,6 +135,87 @@ def Math_AbsIOp : Math_IntegerUnaryOp<"absi"> { let hasFolder = 1; } +//===----------------------------------------------------------------------===// +// AcoshOp +//===----------------------------------------------------------------------===// + +def Math_AcoshOp : Math_FloatUnaryOp<"acosh">{ + let summary = "Hyperbolic arcus cosine of the given value"; + let description = [{ + Syntax: + + ``` + operation ::= ssa-id `=` `math.acosh` ssa-use `:` type + ``` + + The `acosh` operation computes the arcus cosine of a given value. It takes + one operand of floating point type (i.e., scalar, tensor or vector) and returns + one result of the same type. It has no standard attributes. + + Example: + + ```mlir + // Hyperbolic arcus cosine of scalar value. + %a = math.acosh %b : f64 + ``` + }]; + let hasFolder = 1; +} + +//===----------------------------------------------------------------------===// +// AsinOp +//===----------------------------------------------------------------------===// + +def Math_AsinOp : Math_FloatUnaryOp<"asin">{ + let summary = "arcus sine of the given value"; + let description = [{ + Syntax: + + ``` + operation ::= ssa-id `=` `math.asin` ssa-use `:` type + ``` + + The `asin` operation computes the arcus sine of a given value. It takes + one operand of floating point type (i.e., scalar, tensor or vector) and returns + one result of the same type. It has no standard attributes. + + Example: + + ```mlir + // Arcus sine of scalar value. + %a = math.asin %b : f64 + ``` + }]; + let hasFolder = 1; +} + +//===----------------------------------------------------------------------===// +// AsinhOp +//===----------------------------------------------------------------------===// + +def Math_AsinhOp : Math_FloatUnaryOp<"asinh">{ + let summary = "hyperbolic arcus sine of the given value"; + let description = [{ + Syntax: + + ``` + operation ::= ssa-id `=` `math.asinh` ssa-use `:` type + ``` + + The `asinh` operation computes the hyperbolic arcus sine of a given value. It takes + one operand of floating point type (i.e., scalar, tensor or vector) and returns + one result of the same type. It has no standard attributes. + + Example: + + ```mlir + // Hyperbolic arcus sine of scalar value. + %a = math.asinh %b : f64 + ``` + }]; + let hasFolder = 1; +} + //===----------------------------------------------------------------------===// // AtanOp //===----------------------------------------------------------------------===// @@ -156,6 +237,33 @@ def Math_AtanOp : Math_FloatUnaryOp<"atan">{ let hasFolder = 1; } +//===----------------------------------------------------------------------===// +// AtanhOp +//===----------------------------------------------------------------------===// + +def Math_AtanhOp : Math_FloatUnaryOp<"atanh">{ + let summary = "hyperbolic arcus tangent of the given value"; + let description = [{ + Syntax: + + ``` + operation ::= ssa-id `=` `math.atanh` ssa-use `:` type + ``` + + The `atanh` operation computes the hyperbolic arcus tangent of a given value. It takes + one operand of floating point type (i.e., scalar, tensor or vector) and returns + one result of the same type. It has no standard attributes. + + Example: + + ```mlir + // Hyperbolic arcus tangent of scalar value. + %a = math.atanh %b : f64 + ``` + }]; + let hasFolder = 1; +} + //===----------------------------------------------------------------------===// // Atan2Op //===----------------------------------------------------------------------===// diff --git a/mlir/lib/Conversion/MathToLibm/MathToLibm.cpp b/mlir/lib/Conversion/MathToLibm/MathToLibm.cpp index 80eec9b2df745..d1372576407f9 100644 --- a/mlir/lib/Conversion/MathToLibm/MathToLibm.cpp +++ b/mlir/lib/Conversion/MathToLibm/MathToLibm.cpp @@ -163,8 +163,12 @@ void mlir::populateMathToLibmConversionPatterns(RewritePatternSet &patterns) { MLIRContext *ctx = patterns.getContext(); populatePatternsForOp(patterns, ctx, "acosf", "acos"); + populatePatternsForOp(patterns, ctx, "acoshf", "acosh"); + populatePatternsForOp(patterns, ctx, "asinf", "asin"); + populatePatternsForOp(patterns, ctx, "asinhf", "asinh"); populatePatternsForOp(patterns, ctx, "atan2f", "atan2"); populatePatternsForOp(patterns, ctx, "atanf", "atan"); + populatePatternsForOp(patterns, ctx, "atanhf", "atanh"); populatePatternsForOp(patterns, ctx, "cbrtf", "cbrt"); populatePatternsForOp(patterns, ctx, "ceilf", "ceil"); populatePatternsForOp(patterns, ctx, "cosf", "cos"); diff --git a/mlir/lib/Dialect/Math/IR/MathOps.cpp b/mlir/lib/Dialect/Math/IR/MathOps.cpp index bac46996fce73..1690585e78c5d 100644 --- a/mlir/lib/Dialect/Math/IR/MathOps.cpp +++ b/mlir/lib/Dialect/Math/IR/MathOps.cpp @@ -59,6 +59,60 @@ OpFoldResult math::AcosOp::fold(FoldAdaptor adaptor) { }); } +//===----------------------------------------------------------------------===// +// AcoshOp folder +//===----------------------------------------------------------------------===// + +OpFoldResult math::AcoshOp::fold(FoldAdaptor adaptor) { + return constFoldUnaryOpConditional( + adaptor.getOperands(), [](const APFloat &a) -> std::optional { + switch (a.getSizeInBits(a.getSemantics())) { + case 64: + return APFloat(acosh(a.convertToDouble())); + case 32: + return APFloat(acoshf(a.convertToFloat())); + default: + return {}; + } + }); +} + +//===----------------------------------------------------------------------===// +// AsinOp folder +//===----------------------------------------------------------------------===// + +OpFoldResult math::AsinOp::fold(FoldAdaptor adaptor) { + return constFoldUnaryOpConditional( + adaptor.getOperands(), [](const APFloat &a) -> std::optional { + switch (a.getSizeInBits(a.getSemantics())) { + case 64: + return APFloat(asin(a.convertToDouble())); + case 32: + return APFloat(asinf(a.convertToFloat())); + default: + return {}; + } + }); +} + +//===----------------------------------------------------------------------===// +// AsinhOp folder +//===----------------------------------------------------------------------===// + +OpFoldResult math::AsinhOp::fold(FoldAdaptor adaptor) { + return constFoldUnaryOpConditional( + adaptor.getOperands(), [](const APFloat &a) -> std::optional { + switch (a.getSizeInBits(a.getSemantics())) { + case 64: + return APFloat(asinh(a.convertToDouble())); + case 32: + return APFloat(asinhf(a.convertToFloat())); + default: + return {}; + } + }); +} + //===----------------------------------------------------------------------===// // AtanOp folder //===----------------------------------------------------------------------===// @@ -77,6 +131,24 @@ OpFoldResult math::AtanOp::fold(FoldAdaptor adaptor) { }); } +//===----------------------------------------------------------------------===// +// AtanhOp folder +//===----------------------------------------------------------------------===// + +OpFoldResult math::AtanhOp::fold(FoldAdaptor adaptor) { + return constFoldUnaryOpConditional( + adaptor.getOperands(), [](const APFloat &a) -> std::optional { + switch (a.getSizeInBits(a.getSemantics())) { + case 64: + return APFloat(atanh(a.convertToDouble())); + case 32: + return APFloat(atanhf(a.convertToFloat())); + default: + return {}; + } + }); +} + //===----------------------------------------------------------------------===// // Atan2Op folder //===----------------------------------------------------------------------===// diff --git a/mlir/test/Conversion/MathToLibm/convert-to-libm.mlir b/mlir/test/Conversion/MathToLibm/convert-to-libm.mlir index bfe084b6ca0ab..ffc2939afe7ff 100644 --- a/mlir/test/Conversion/MathToLibm/convert-to-libm.mlir +++ b/mlir/test/Conversion/MathToLibm/convert-to-libm.mlir @@ -2,8 +2,16 @@ // CHECK-DAG: @acos(f64) -> f64 attributes {llvm.readnone} // CHECK-DAG: @acosf(f32) -> f32 attributes {llvm.readnone} +// CHECK-DAG: @acosh(f64) -> f64 attributes {llvm.readnone} +// CHECK-DAG: @acoshf(f32) -> f32 attributes {llvm.readnone} +// CHECK-DAG: @asin(f64) -> f64 attributes {llvm.readnone} +// CHECK-DAG: @asinf(f32) -> f32 attributes {llvm.readnone} +// CHECK-DAG: @asinh(f64) -> f64 attributes {llvm.readnone} +// CHECK-DAG: @asinhf(f32) -> f32 attributes {llvm.readnone} // CHECK-DAG: @atan(f64) -> f64 attributes {llvm.readnone} // CHECK-DAG: @atanf(f32) -> f32 attributes {llvm.readnone} +// CHECK-DAG: @atanh(f64) -> f64 attributes {llvm.readnone} +// CHECK-DAG: @atanhf(f32) -> f32 attributes {llvm.readnone} // CHECK-DAG: @erf(f64) -> f64 attributes {llvm.readnone} // CHECK-DAG: @erff(f32) -> f32 attributes {llvm.readnone} // CHECK-DAG: @expm1(f64) -> f64 attributes {llvm.readnone} @@ -70,6 +78,117 @@ func.func @acos_vec_caller(%float: vector<2xf32>, %double: vector<2xf64>) -> (ve return %float_result, %double_result : vector<2xf32>, vector<2xf64> } +// CHECK-LABEL: func @acosh_caller +// CHECK-SAME: %[[FLOAT:.*]]: f32 +// CHECK-SAME: %[[DOUBLE:.*]]: f64 +func.func @acosh_caller(%float: f32, %double: f64) -> (f32, f64) { + // CHECK-DAG: %[[FLOAT_RESULT:.*]] = call @acoshf(%[[FLOAT]]) : (f32) -> f32 + %float_result = math.acosh %float : f32 + // CHECK-DAG: %[[DOUBLE_RESULT:.*]] = call @acosh(%[[DOUBLE]]) : (f64) -> f64 + %double_result = math.acosh %double : f64 + // CHECK: return %[[FLOAT_RESULT]], %[[DOUBLE_RESULT]] + return %float_result, %double_result : f32, f64 +} + +// CHECK-LABEL: func @acosh_vec_caller( +// CHECK-SAME: %[[VAL_0:.*]]: vector<2xf32>, +// CHECK-SAME: %[[VAL_1:.*]]: vector<2xf64>) -> (vector<2xf32>, vector<2xf64>) { +// CHECK-DAG: %[[CVF:.*]] = arith.constant dense<0.000000e+00> : vector<2xf32> +// CHECK-DAG: %[[CVD:.*]] = arith.constant dense<0.000000e+00> : vector<2xf64> +// CHECK: %[[IN0_F32:.*]] = vector.extract %[[VAL_0]][0] : f32 from vector<2xf32> +// CHECK: %[[OUT0_F32:.*]] = call @acoshf(%[[IN0_F32]]) : (f32) -> f32 +// CHECK: %[[VAL_8:.*]] = vector.insert %[[OUT0_F32]], %[[CVF]] [0] : f32 into vector<2xf32> +// CHECK: %[[IN1_F32:.*]] = vector.extract %[[VAL_0]][1] : f32 from vector<2xf32> +// CHECK: %[[OUT1_F32:.*]] = call @acoshf(%[[IN1_F32]]) : (f32) -> f32 +// CHECK: %[[VAL_11:.*]] = vector.insert %[[OUT1_F32]], %[[VAL_8]] [1] : f32 into vector<2xf32> +// CHECK: %[[IN0_F64:.*]] = vector.extract %[[VAL_1]][0] : f64 from vector<2xf64> +// CHECK: %[[OUT0_F64:.*]] = call @acosh(%[[IN0_F64]]) : (f64) -> f64 +// CHECK: %[[VAL_14:.*]] = vector.insert %[[OUT0_F64]], %[[CVD]] [0] : f64 into vector<2xf64> +// CHECK: %[[IN1_F64:.*]] = vector.extract %[[VAL_1]][1] : f64 from vector<2xf64> +// CHECK: %[[OUT1_F64:.*]] = call @acosh(%[[IN1_F64]]) : (f64) -> f64 +// CHECK: %[[VAL_17:.*]] = vector.insert %[[OUT1_F64]], %[[VAL_14]] [1] : f64 into vector<2xf64> +// CHECK: return %[[VAL_11]], %[[VAL_17]] : vector<2xf32>, vector<2xf64> +// CHECK: } +func.func @acosh_vec_caller(%float: vector<2xf32>, %double: vector<2xf64>) -> (vector<2xf32>, vector<2xf64>) { + %float_result = math.acosh %float : vector<2xf32> + %double_result = math.acosh %double : vector<2xf64> + return %float_result, %double_result : vector<2xf32>, vector<2xf64> +} + +// CHECK-LABEL: func @asin_caller +// CHECK-SAME: %[[FLOAT:.*]]: f32 +// CHECK-SAME: %[[DOUBLE:.*]]: f64 +func.func @asin_caller(%float: f32, %double: f64) -> (f32, f64) { + // CHECK-DAG: %[[FLOAT_RESULT:.*]] = call @asinf(%[[FLOAT]]) : (f32) -> f32 + %float_result = math.asin %float : f32 + // CHECK-DAG: %[[DOUBLE_RESULT:.*]] = call @asin(%[[DOUBLE]]) : (f64) -> f64 + %double_result = math.asin %double : f64 + // CHECK: return %[[FLOAT_RESULT]], %[[DOUBLE_RESULT]] + return %float_result, %double_result : f32, f64 +} + +// CHECK-LABEL: func @asin_vec_caller( +// CHECK-SAME: %[[VAL_0:.*]]: vector<2xf32>, +// CHECK-SAME: %[[VAL_1:.*]]: vector<2xf64>) -> (vector<2xf32>, vector<2xf64>) { +// CHECK-DAG: %[[CVF:.*]] = arith.constant dense<0.000000e+00> : vector<2xf32> +// CHECK-DAG: %[[CVD:.*]] = arith.constant dense<0.000000e+00> : vector<2xf64> +// CHECK: %[[IN0_F32:.*]] = vector.extract %[[VAL_0]][0] : f32 from vector<2xf32> +// CHECK: %[[OUT0_F32:.*]] = call @asinf(%[[IN0_F32]]) : (f32) -> f32 +// CHECK: %[[VAL_8:.*]] = vector.insert %[[OUT0_F32]], %[[CVF]] [0] : f32 into vector<2xf32> +// CHECK: %[[IN1_F32:.*]] = vector.extract %[[VAL_0]][1] : f32 from vector<2xf32> +// CHECK: %[[OUT1_F32:.*]] = call @asinf(%[[IN1_F32]]) : (f32) -> f32 +// CHECK: %[[VAL_11:.*]] = vector.insert %[[OUT1_F32]], %[[VAL_8]] [1] : f32 into vector<2xf32> +// CHECK: %[[IN0_F64:.*]] = vector.extract %[[VAL_1]][0] : f64 from vector<2xf64> +// CHECK: %[[OUT0_F64:.*]] = call @asin(%[[IN0_F64]]) : (f64) -> f64 +// CHECK: %[[VAL_14:.*]] = vector.insert %[[OUT0_F64]], %[[CVD]] [0] : f64 into vector<2xf64> +// CHECK: %[[IN1_F64:.*]] = vector.extract %[[VAL_1]][1] : f64 from vector<2xf64> +// CHECK: %[[OUT1_F64:.*]] = call @asin(%[[IN1_F64]]) : (f64) -> f64 +// CHECK: %[[VAL_17:.*]] = vector.insert %[[OUT1_F64]], %[[VAL_14]] [1] : f64 into vector<2xf64> +// CHECK: return %[[VAL_11]], %[[VAL_17]] : vector<2xf32>, vector<2xf64> +// CHECK: } +func.func @asin_vec_caller(%float: vector<2xf32>, %double: vector<2xf64>) -> (vector<2xf32>, vector<2xf64>) { + %float_result = math.asin %float : vector<2xf32> + %double_result = math.asin %double : vector<2xf64> + return %float_result, %double_result : vector<2xf32>, vector<2xf64> +} + +// CHECK-LABEL: func @asinh_caller +// CHECK-SAME: %[[FLOAT:.*]]: f32 +// CHECK-SAME: %[[DOUBLE:.*]]: f64 +func.func @asinh_caller(%float: f32, %double: f64) -> (f32, f64) { + // CHECK-DAG: %[[FLOAT_RESULT:.*]] = call @asinhf(%[[FLOAT]]) : (f32) -> f32 + %float_result = math.asinh %float : f32 + // CHECK-DAG: %[[DOUBLE_RESULT:.*]] = call @asinh(%[[DOUBLE]]) : (f64) -> f64 + %double_result = math.asinh %double : f64 + // CHECK: return %[[FLOAT_RESULT]], %[[DOUBLE_RESULT]] + return %float_result, %double_result : f32, f64 +} + +// CHECK-LABEL: func @asinh_vec_caller( +// CHECK-SAME: %[[VAL_0:.*]]: vector<2xf32>, +// CHECK-SAME: %[[VAL_1:.*]]: vector<2xf64>) -> (vector<2xf32>, vector<2xf64>) { +// CHECK-DAG: %[[CVF:.*]] = arith.constant dense<0.000000e+00> : vector<2xf32> +// CHECK-DAG: %[[CVD:.*]] = arith.constant dense<0.000000e+00> : vector<2xf64> +// CHECK: %[[IN0_F32:.*]] = vector.extract %[[VAL_0]][0] : f32 from vector<2xf32> +// CHECK: %[[OUT0_F32:.*]] = call @asinhf(%[[IN0_F32]]) : (f32) -> f32 +// CHECK: %[[VAL_8:.*]] = vector.insert %[[OUT0_F32]], %[[CVF]] [0] : f32 into vector<2xf32> +// CHECK: %[[IN1_F32:.*]] = vector.extract %[[VAL_0]][1] : f32 from vector<2xf32> +// CHECK: %[[OUT1_F32:.*]] = call @asinhf(%[[IN1_F32]]) : (f32) -> f32 +// CHECK: %[[VAL_11:.*]] = vector.insert %[[OUT1_F32]], %[[VAL_8]] [1] : f32 into vector<2xf32> +// CHECK: %[[IN0_F64:.*]] = vector.extract %[[VAL_1]][0] : f64 from vector<2xf64> +// CHECK: %[[OUT0_F64:.*]] = call @asinh(%[[IN0_F64]]) : (f64) -> f64 +// CHECK: %[[VAL_14:.*]] = vector.insert %[[OUT0_F64]], %[[CVD]] [0] : f64 into vector<2xf64> +// CHECK: %[[IN1_F64:.*]] = vector.extract %[[VAL_1]][1] : f64 from vector<2xf64> +// CHECK: %[[OUT1_F64:.*]] = call @asinh(%[[IN1_F64]]) : (f64) -> f64 +// CHECK: %[[VAL_17:.*]] = vector.insert %[[OUT1_F64]], %[[VAL_14]] [1] : f64 into vector<2xf64> +// CHECK: return %[[VAL_11]], %[[VAL_17]] : vector<2xf32>, vector<2xf64> +// CHECK: } +func.func @asinh_vec_caller(%float: vector<2xf32>, %double: vector<2xf64>) -> (vector<2xf32>, vector<2xf64>) { + %float_result = math.asinh %float : vector<2xf32> + %double_result = math.asinh %double : vector<2xf64> + return %float_result, %double_result : vector<2xf32>, vector<2xf64> +} + // CHECK-LABEL: func @atan_caller // CHECK-SAME: %[[FLOAT:.*]]: f32 // CHECK-SAME: %[[DOUBLE:.*]]: f64 @@ -117,6 +236,53 @@ func.func @atan_vec_caller(%float: vector<2xf32>, %double: vector<2xf64>) -> (ve return %float_result, %double_result : vector<2xf32>, vector<2xf64> } +// CHECK-LABEL: func @atanh_caller +// CHECK-SAME: %[[FLOAT:.*]]: f32 +// CHECK-SAME: %[[DOUBLE:.*]]: f64 +// CHECK-SAME: %[[HALF:.*]]: f16 +// CHECK-SAME: %[[BFLOAT:.*]]: bf16 +func.func @atanh_caller(%float: f32, %double: f64, %half: f16, %bfloat: bf16) -> (f32, f64, f16, bf16) { + // CHECK: %[[FLOAT_RESULT:.*]] = call @atanhf(%[[FLOAT]]) : (f32) -> f32 + %float_result = math.atanh %float : f32 + // CHECK: %[[DOUBLE_RESULT:.*]] = call @atanh(%[[DOUBLE]]) : (f64) -> f64 + %double_result = math.atanh %double : f64 + // CHECK: %[[HALF_PROMOTED:.*]] = arith.extf %[[HALF]] : f16 to f32 + // CHECK: %[[HALF_CALL:.*]] = call @atanhf(%[[HALF_PROMOTED]]) : (f32) -> f32 + // CHECK: %[[HALF_RESULT:.*]] = arith.truncf %[[HALF_CALL]] : f32 to f16 + %half_result = math.atanh %half : f16 + // CHECK: %[[BFLOAT_PROMOTED:.*]] = arith.extf %[[BFLOAT]] : bf16 to f32 + // CHECK: %[[BFLOAT_CALL:.*]] = call @atanhf(%[[BFLOAT_PROMOTED]]) : (f32) -> f32 + // CHECK: %[[BFLOAT_RESULT:.*]] = arith.truncf %[[BFLOAT_CALL]] : f32 to bf16 + %bfloat_result = math.atanh %bfloat : bf16 + // CHECK: return %[[FLOAT_RESULT]], %[[DOUBLE_RESULT]], %[[HALF_RESULT]], %[[BFLOAT_RESULT]] + return %float_result, %double_result, %half_result, %bfloat_result : f32, f64, f16, bf16 +} + +// CHECK-LABEL: func @atanh_vec_caller( +// CHECK-SAME: %[[VAL_0:.*]]: vector<2xf32>, +// CHECK-SAME: %[[VAL_1:.*]]: vector<2xf64>) -> (vector<2xf32>, vector<2xf64>) { +// CHECK-DAG: %[[CVF:.*]] = arith.constant dense<0.000000e+00> : vector<2xf32> +// CHECK-DAG: %[[CVD:.*]] = arith.constant dense<0.000000e+00> : vector<2xf64> +// CHECK: %[[IN0_F32:.*]] = vector.extract %[[VAL_0]][0] : f32 from vector<2xf32> +// CHECK: %[[OUT0_F32:.*]] = call @atanhf(%[[IN0_F32]]) : (f32) -> f32 +// CHECK: %[[VAL_8:.*]] = vector.insert %[[OUT0_F32]], %[[CVF]] [0] : f32 into vector<2xf32> +// CHECK: %[[IN1_F32:.*]] = vector.extract %[[VAL_0]][1] : f32 from vector<2xf32> +// CHECK: %[[OUT1_F32:.*]] = call @atanhf(%[[IN1_F32]]) : (f32) -> f32 +// CHECK: %[[VAL_11:.*]] = vector.insert %[[OUT1_F32]], %[[VAL_8]] [1] : f32 into vector<2xf32> +// CHECK: %[[IN0_F64:.*]] = vector.extract %[[VAL_1]][0] : f64 from vector<2xf64> +// CHECK: %[[OUT0_F64:.*]] = call @atanh(%[[IN0_F64]]) : (f64) -> f64 +// CHECK: %[[VAL_14:.*]] = vector.insert %[[OUT0_F64]], %[[CVD]] [0] : f64 into vector<2xf64> +// CHECK: %[[IN1_F64:.*]] = vector.extract %[[VAL_1]][1] : f64 from vector<2xf64> +// CHECK: %[[OUT1_F64:.*]] = call @atanh(%[[IN1_F64]]) : (f64) -> f64 +// CHECK: %[[VAL_17:.*]] = vector.insert %[[OUT1_F64]], %[[VAL_14]] [1] : f64 into vector<2xf64> +// CHECK: return %[[VAL_11]], %[[VAL_17]] : vector<2xf32>, vector<2xf64> +// CHECK: } +func.func @atanh_vec_caller(%float: vector<2xf32>, %double: vector<2xf64>) -> (vector<2xf32>, vector<2xf64>) { + %float_result = math.atanh %float : vector<2xf32> + %double_result = math.atanh %double : vector<2xf64> + return %float_result, %double_result : vector<2xf32>, vector<2xf64> +} + // CHECK-LABEL: func @tanh_caller // CHECK-SAME: %[[FLOAT:.*]]: f32 // CHECK-SAME: %[[DOUBLE:.*]]: f64