[Mlir-commits] [mlir] [mlir][arith] Add `arith.fptofp` op (PR #188041)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Mon Mar 23 06:45:30 PDT 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-mlir

Author: Matthias Springer (matthias-springer)

<details>
<summary>Changes</summary>

There are multiple FP types with the same bitwidth. Neither `extf` nor `truncf` can be used in that case. Add a new `arith.fptofp` op that can be used in such cases. The op is modeled after `arith.truncf`. Also add a lowering to LLVM.

Assisted-by: claude-4.6-opus-high


---
Full diff: https://github.com/llvm/llvm-project/pull/188041.diff


7 Files Affected:

- (modified) mlir/include/mlir/Dialect/Arith/IR/ArithOps.td (+34) 
- (modified) mlir/lib/Conversion/ArithToLLVM/ArithToLLVM.cpp (+70) 
- (modified) mlir/lib/Dialect/Arith/IR/ArithOps.cpp (+44) 
- (modified) mlir/test/Conversion/ArithToLLVM/arith-to-llvm.mlir (+116) 
- (modified) mlir/test/Dialect/Arith/canonicalize.mlir (+11) 
- (modified) mlir/test/Dialect/Arith/invalid.mlir (+40) 
- (modified) mlir/test/Dialect/Arith/ops.mlir (+80) 


``````````diff
diff --git a/mlir/include/mlir/Dialect/Arith/IR/ArithOps.td b/mlir/include/mlir/Dialect/Arith/IR/ArithOps.td
index 4b830c05bf585..06ff3f9eeac44 100644
--- a/mlir/include/mlir/Dialect/Arith/IR/ArithOps.td
+++ b/mlir/include/mlir/Dialect/Arith/IR/ArithOps.td
@@ -1425,6 +1425,40 @@ def Arith_TruncFOp :
                           attr-dict `:` type($in) `to` type($out) }];
 }
 
+//===----------------------------------------------------------------------===//
+// FPToFPOp
+//===----------------------------------------------------------------------===//
+
+def Arith_FPToFPOp :
+    Arith_Op<"fptofp",
+      [Pure, SameOperandsAndResultShape, SameInputOutputTensorDims,
+       DeclareOpInterfaceMethods<ArithRoundingModeInterface>,
+       DeclareOpInterfaceMethods<ArithFastMathInterface>,
+       DeclareOpInterfaceMethods<CastOpInterface>]>,
+    Arguments<(ins FloatLike:$in,
+                   OptionalAttr<Arith_RoundingModeAttr>:$roundingmode,
+                   OptionalAttr<Arith_FastMathAttr>:$fastmath)>,
+    Results<(outs FloatLike:$out)> {
+  let summary = "cast between floating-point types";
+  let description = [{
+    Cast a floating-point value to a different floating-point type. Unlike
+    `arith.extf` and `arith.truncf`, this operation supports arbitrary
+    conversions between floating-point types, including conversions between
+    types of the same bitwidth but different semantics (e.g., f16 to bf16).
+
+    The source and destination element types must be different. If the value
+    cannot be exactly represented, it is rounded using the provided rounding
+    mode or the default one if no rounding mode is provided. When operating
+    on vectors, casts elementwise.
+  }];
+
+  let hasFolder = 1;
+  let hasVerifier = 1;
+  let assemblyFormat = [{ $in ($roundingmode^)?
+                          (`fastmath` `` $fastmath^)?
+                          attr-dict `:` type($in) `to` type($out) }];
+}
+
 //===----------------------------------------------------------------------===//
 // Scaling TruncFOp
 //===----------------------------------------------------------------------===//
diff --git a/mlir/lib/Conversion/ArithToLLVM/ArithToLLVM.cpp b/mlir/lib/Conversion/ArithToLLVM/ArithToLLVM.cpp
index e7f561e8a4d67..a6fe481304c06 100644
--- a/mlir/lib/Conversion/ArithToLLVM/ArithToLLVM.cpp
+++ b/mlir/lib/Conversion/ArithToLLVM/ArithToLLVM.cpp
@@ -262,6 +262,75 @@ struct CmpFOpLowering : public ConvertOpToLLVMPattern<arith::CmpFOp> {
                   ConversionPatternRewriter &rewriter) const override;
 };
 
+/// Lower arith.fptofp to the appropriate LLVM op(s).
+///
+/// - If src is wider than dst: llvm.fptrunc
+/// - If src is narrower than dst: llvm.fpext
+/// - bf16 <-> f16: llvm.fpext to f32, then llvm.fptrunc to dst.
+/// - Other FP types: not supported by the LLVM dialect.
+struct FPToFPOpLowering : public ConvertOpToLLVMPattern<arith::FPToFPOp> {
+  using ConvertOpToLLVMPattern::ConvertOpToLLVMPattern;
+
+  LogicalResult
+  matchAndRewrite(arith::FPToFPOp op, OpAdaptor adaptor,
+                  ConversionPatternRewriter &rewriter) const override {
+    if (LLVM::detail::opHasUnsupportedFloatingPointTypes(op,
+                                                         *getTypeConverter()))
+      return rewriter.notifyMatchFailure(op, "unsupported floating point type");
+
+    auto srcFloat = cast<FloatType>(getElementTypeOrSelf(op.getIn().getType()));
+    auto dstFloat = cast<FloatType>(getElementTypeOrSelf(op.getType()));
+
+    Type convertedType = getTypeConverter()->convertType(op.getType());
+    if (!convertedType)
+      return rewriter.notifyMatchFailure(op, "failed to convert result type");
+
+    Value input = adaptor.getIn();
+    Location loc = op.getLoc();
+    Type operandType = input.getType();
+
+    if (!isa<LLVM::LLVMArrayType>(operandType)) {
+      rewriter.replaceOp(op, emitConversion(rewriter, loc, input, convertedType,
+                                            srcFloat, dstFloat));
+      return success();
+    }
+
+    if (!isa<VectorType>(op.getType()))
+      return rewriter.notifyMatchFailure(op, "expected vector result type");
+
+    return LLVM::detail::handleMultidimensionalVectors(
+        op.getOperation(), adaptor.getOperands(), *getTypeConverter(),
+        [&](Type llvm1DVectorTy, ValueRange operands) -> Value {
+          return emitConversion(rewriter, loc, operands.front(), llvm1DVectorTy,
+                                srcFloat, dstFloat);
+        },
+        rewriter);
+  }
+
+private:
+  static Value emitConversion(ConversionPatternRewriter &rewriter, Location loc,
+                              Value input, Type targetType, FloatType srcFloat,
+                              FloatType dstFloat) {
+    unsigned srcWidth = srcFloat.getWidth();
+    unsigned dstWidth = dstFloat.getWidth();
+    if (srcWidth > dstWidth)
+      return LLVM::FPTruncOp::create(rewriter, loc, targetType, input);
+    if (srcWidth < dstWidth)
+      return LLVM::FPExtOp::create(rewriter, loc, targetType, input);
+
+    // Same width, different semantics: bf16 <-> f16
+    assert((srcFloat.isBF16() && dstFloat.isF16() ||
+            srcFloat.isF16() && dstFloat.isBF16()) &&
+           "only bf16 <-> f16 conversions are supported");
+    Type f32Scalar = Float32Type::get(rewriter.getContext());
+    Type f32Ty = f32Scalar;
+    if (auto vecTy = dyn_cast<VectorType>(targetType))
+      f32Ty = VectorType::get(vecTy.getShape(), f32Scalar);
+    Value ext = LLVM::FPExtOp::create(rewriter, loc, f32Ty, input);
+    return LLVM::FPTruncOp::create(rewriter, loc, targetType, ext);
+  }
+};
+
 struct SelectOpOneToNLowering : public ConvertOpToLLVMPattern<arith::SelectOp> {
   using ConvertOpToLLVMPattern::ConvertOpToLLVMPattern;
   using Adaptor = ConvertOpToLLVMPattern<arith::SelectOp>::OneToNOpAdaptor;
@@ -642,6 +711,7 @@ void mlir::arith::populateArithToLLVMConversionPatterns(
     ExtFOpLowering,
     ExtSIOpLowering,
     ExtUIOpLowering,
+    FPToFPOpLowering,
     FPToSIOpLowering,
     FPToUIOpLowering,
     IndexCastOpSILowering,
diff --git a/mlir/lib/Dialect/Arith/IR/ArithOps.cpp b/mlir/lib/Dialect/Arith/IR/ArithOps.cpp
index 155edc5070a9d..774eeb03536be 100644
--- a/mlir/lib/Dialect/Arith/IR/ArithOps.cpp
+++ b/mlir/lib/Dialect/Arith/IR/ArithOps.cpp
@@ -1699,6 +1699,50 @@ LogicalResult arith::TruncFOp::verify() {
   return verifyTruncateOp<FloatType>(*this);
 }
 
+//===----------------------------------------------------------------------===//
+// FPToFPOp
+//===----------------------------------------------------------------------===//
+
+OpFoldResult arith::FPToFPOp::fold(FoldAdaptor adaptor) {
+  auto resElemType = cast<FloatType>(getElementTypeOrSelf(getType()));
+  const llvm::fltSemantics &targetSemantics = resElemType.getFloatSemantics();
+  return constFoldCastOp<FloatAttr, FloatAttr>(
+      adaptor.getOperands(), getType(),
+      [this, &targetSemantics](const APFloat &a, bool &castStatus) {
+        RoundingMode roundingMode =
+            getRoundingmode().value_or(RoundingMode::to_nearest_even);
+        llvm::RoundingMode llvmRoundingMode =
+            convertArithRoundingModeToLLVMIR(roundingMode);
+        FailureOr<APFloat> result =
+            convertFloatValue(a, targetSemantics, llvmRoundingMode);
+        if (failed(result)) {
+          castStatus = false;
+          return a;
+        }
+        return *result;
+      });
+}
+
+bool arith::FPToFPOp::areCastCompatible(TypeRange inputs, TypeRange outputs) {
+  if (!areValidCastInputsAndOutputs(inputs, outputs))
+    return false;
+  auto srcType = getTypeIfLike<FloatType>(inputs.front());
+  auto dstType = getTypeIfLike<FloatType>(outputs.front());
+  if (!srcType || !dstType)
+    return false;
+  return srcType != dstType;
+}
+
+LogicalResult arith::FPToFPOp::verify() {
+  Type srcType = getElementTypeOrSelf(getIn().getType());
+  Type dstType = getElementTypeOrSelf(getType());
+  if (srcType == dstType)
+    return emitError("result element type ")
+           << dstType << " must be different from operand element type "
+           << srcType;
+  return success();
+}
+
 //===----------------------------------------------------------------------===//
 // ScalingTruncFOp
 //===----------------------------------------------------------------------===//
diff --git a/mlir/test/Conversion/ArithToLLVM/arith-to-llvm.mlir b/mlir/test/Conversion/ArithToLLVM/arith-to-llvm.mlir
index 47069906fa110..5c43639442338 100644
--- a/mlir/test/Conversion/ArithToLLVM/arith-to-llvm.mlir
+++ b/mlir/test/Conversion/ArithToLLVM/arith-to-llvm.mlir
@@ -838,3 +838,119 @@ func.func @supported_fp_type(%arg0: f32, %arg1: vector<4xf32>, %arg2: vector<4x8
   %3 = arith.cmpf oeq, %arg0, %arg3 : f32
   return
 }
+
+// -----
+
+// CHECK-LABEL: func @fptofp_ext(
+// CHECK-SAME: %[[ARG0:.*]]: f16, %[[ARG1:.*]]: f32)
+func.func @fptofp_ext(%arg0 : f16, %arg1 : f32) {
+// CHECK-NEXT: = llvm.fpext %[[ARG0]] : f16 to f32
+  %0 = arith.fptofp %arg0 : f16 to f32
+// CHECK-NEXT: = llvm.fpext %[[ARG0]] : f16 to f64
+  %1 = arith.fptofp %arg0 : f16 to f64
+// CHECK-NEXT: = llvm.fpext %[[ARG1]] : f32 to f64
+  %2 = arith.fptofp %arg1 : f32 to f64
+  return
+}
+
+// -----
+
+// CHECK-LABEL: func @fptofp_trunc(
+// CHECK-SAME: %[[ARG0:.*]]: f32, %[[ARG1:.*]]: f64)
+func.func @fptofp_trunc(%arg0 : f32, %arg1 : f64) {
+// CHECK-NEXT: = llvm.fptrunc %[[ARG0]] : f32 to f16
+  %0 = arith.fptofp %arg0 : f32 to f16
+// CHECK-NEXT: = llvm.fptrunc %[[ARG1]] : f64 to f16
+  %1 = arith.fptofp %arg1 : f64 to f16
+// CHECK-NEXT: = llvm.fptrunc %[[ARG1]] : f64 to f32
+  %2 = arith.fptofp %arg1 : f64 to f32
+  return
+}
+
+// -----
+
+// bf16 <-> f16: same width, different semantics. Lowered via f32.
+// CHECK-LABEL: func @fptofp_same_width(
+// CHECK-SAME: %[[ARG0:.*]]: bf16, %[[ARG1:.*]]: f16)
+func.func @fptofp_same_width(%arg0 : bf16, %arg1 : f16) {
+// CHECK-NEXT: %[[EXT0:.*]] = llvm.fpext %[[ARG0]] : bf16 to f32
+// CHECK-NEXT: = llvm.fptrunc %[[EXT0]] : f32 to f16
+  %0 = arith.fptofp %arg0 : bf16 to f16
+// CHECK-NEXT: %[[EXT1:.*]] = llvm.fpext %[[ARG1]] : f16 to f32
+// CHECK-NEXT: = llvm.fptrunc %[[EXT1]] : f32 to bf16
+  %1 = arith.fptofp %arg1 : f16 to bf16
+  return
+}
+
+// -----
+
+// CHECK-LABEL: func @fptofp_ext_vector(
+// CHECK-SAME: %[[ARG0:.*]]: vector<2xf16>, %[[ARG1:.*]]: vector<2xf32>)
+func.func @fptofp_ext_vector(%arg0 : vector<2xf16>, %arg1 : vector<2xf32>) {
+// CHECK-NEXT: = llvm.fpext %[[ARG0]] : vector<2xf16> to vector<2xf32>
+  %0 = arith.fptofp %arg0 : vector<2xf16> to vector<2xf32>
+// CHECK-NEXT: = llvm.fpext %[[ARG0]] : vector<2xf16> to vector<2xf64>
+  %1 = arith.fptofp %arg0 : vector<2xf16> to vector<2xf64>
+// CHECK-NEXT: = llvm.fpext %[[ARG1]] : vector<2xf32> to vector<2xf64>
+  %2 = arith.fptofp %arg1 : vector<2xf32> to vector<2xf64>
+  return
+}
+
+// -----
+
+// CHECK-LABEL: func @fptofp_trunc_vector(
+// CHECK-SAME: %[[ARG0:.*]]: vector<2xf32>, %[[ARG1:.*]]: vector<2xf64>)
+func.func @fptofp_trunc_vector(%arg0 : vector<2xf32>, %arg1 : vector<2xf64>) {
+// CHECK-NEXT: = llvm.fptrunc %[[ARG0]] : vector<2xf32> to vector<2xf16>
+  %0 = arith.fptofp %arg0 : vector<2xf32> to vector<2xf16>
+// CHECK-NEXT: = llvm.fptrunc %[[ARG1]] : vector<2xf64> to vector<2xf16>
+  %1 = arith.fptofp %arg1 : vector<2xf64> to vector<2xf16>
+// CHECK-NEXT: = llvm.fptrunc %[[ARG1]] : vector<2xf64> to vector<2xf32>
+  %2 = arith.fptofp %arg1 : vector<2xf64> to vector<2xf32>
+  return
+}
+
+// -----
+
+// CHECK-LABEL: func @fptofp_same_width_vector(
+// CHECK-SAME: %[[ARG0:.*]]: vector<2xbf16>, %[[ARG1:.*]]: vector<2xf16>)
+func.func @fptofp_same_width_vector(%arg0 : vector<2xbf16>, %arg1 : vector<2xf16>) {
+// CHECK-NEXT: %[[EXT0:.*]] = llvm.fpext %[[ARG0]] : vector<2xbf16> to vector<2xf32>
+// CHECK-NEXT: = llvm.fptrunc %[[EXT0]] : vector<2xf32> to vector<2xf16>
+  %0 = arith.fptofp %arg0 : vector<2xbf16> to vector<2xf16>
+// CHECK-NEXT: %[[EXT1:.*]] = llvm.fpext %[[ARG1]] : vector<2xf16> to vector<2xf32>
+// CHECK-NEXT: = llvm.fptrunc %[[EXT1]] : vector<2xf32> to vector<2xbf16>
+  %1 = arith.fptofp %arg1 : vector<2xf16> to vector<2xbf16>
+  return
+}
+
+// -----
+
+// Multi-dimensional vectors are unrolled.
+// CHECK-LABEL: @fptofp_ext_multidim_vector
+func.func @fptofp_ext_multidim_vector(%arg0 : vector<2x3xf16>) {
+// CHECK-COUNT-2: llvm.fpext %{{.*}} : vector<3xf16> to vector<3xf32>
+  %0 = arith.fptofp %arg0 : vector<2x3xf16> to vector<2x3xf32>
+  return
+}
+
+// -----
+
+// CHECK-LABEL: @fptofp_trunc_multidim_vector
+func.func @fptofp_trunc_multidim_vector(%arg0 : vector<2x3xf64>) {
+// CHECK-COUNT-2: llvm.fptrunc %{{.*}} : vector<3xf64> to vector<3xf32>
+  %0 = arith.fptofp %arg0 : vector<2x3xf64> to vector<2x3xf32>
+  return
+}
+
+// -----
+
+// CHECK-LABEL: @fptofp_same_width_multidim_vector
+func.func @fptofp_same_width_multidim_vector(%arg0 : vector<2x3xbf16>) {
+// CHECK: llvm.fpext %{{.*}} : vector<3xbf16> to vector<3xf32>
+// CHECK: llvm.fptrunc %{{.*}} : vector<3xf32> to vector<3xf16>
+// CHECK: llvm.fpext %{{.*}} : vector<3xbf16> to vector<3xf32>
+// CHECK: llvm.fptrunc %{{.*}} : vector<3xf32> to vector<3xf16>
+  %0 = arith.fptofp %arg0 : vector<2x3xbf16> to vector<2x3xf16>
+  return
+}
diff --git a/mlir/test/Dialect/Arith/canonicalize.mlir b/mlir/test/Dialect/Arith/canonicalize.mlir
index 643e4e076e7c6..5d3bc34636669 100644
--- a/mlir/test/Dialect/Arith/canonicalize.mlir
+++ b/mlir/test/Dialect/Arith/canonicalize.mlir
@@ -3553,3 +3553,14 @@ func.func @truncf_neg_inf_to_finite_only_no_fold() -> f4E2M1FN {
   return %result : f4E2M1FN
 }
 
+// -----
+
+// CHECK-LABEL: @fptofp_fold_f8
+//       CHECK:   %[[C:.*]] = arith.constant 2.000000e+00 : f8E5M2
+//       CHECK:   return %[[C]]
+func.func @fptofp_fold_f8() -> f8E5M2 {
+  %c = arith.constant 2.0 : f8E4M3FN
+  %result = arith.fptofp %c : f8E4M3FN to f8E5M2
+  return %result : f8E5M2
+}
+
diff --git a/mlir/test/Dialect/Arith/invalid.mlir b/mlir/test/Dialect/Arith/invalid.mlir
index 0ea614e0d4b97..722d36d6448be 100644
--- a/mlir/test/Dialect/Arith/invalid.mlir
+++ b/mlir/test/Dialect/Arith/invalid.mlir
@@ -1016,3 +1016,43 @@ func.func @index_castui_i0(%a: i0) -> index {
   %0 = arith.index_castui %a : i0 to index
   return %0 : index
 }
+
+// -----
+
+func.func @fptofp_same_type(%arg0 : f32) {
+  // expected-error @+1 {{are cast incompatible}}
+  %0 = arith.fptofp %arg0 : f32 to f32
+  return
+}
+
+// -----
+
+func.func @fptofp_same_type_vec(%arg0 : vector<2xf16>) {
+  // expected-error @+1 {{are cast incompatible}}
+  %0 = arith.fptofp %arg0 : vector<2xf16> to vector<2xf16>
+  return
+}
+
+// -----
+
+func.func @fptofp_shape_mismatch(%arg0 : vector<2xf16>) {
+  // expected-error @+1 {{op requires the same shape for all operands and results}}
+  %0 = arith.fptofp %arg0 : vector<2xf16> to vector<3xf32>
+  return
+}
+
+// -----
+
+func.func @fptofp_int_input(%arg0 : i32) {
+  // expected-error @+1 {{op operand #0 must be floating-point-like, but got 'i32'}}
+  %0 = arith.fptofp %arg0 : i32 to f32
+  return
+}
+
+// -----
+
+func.func @fptofp_int_output(%arg0 : f32) {
+  // expected-error @+1 {{op result #0 must be floating-point-like, but got 'i32'}}
+  %0 = arith.fptofp %arg0 : f32 to i32
+  return
+}
diff --git a/mlir/test/Dialect/Arith/ops.mlir b/mlir/test/Dialect/Arith/ops.mlir
index 9765db69d6dd5..97a359357819f 100644
--- a/mlir/test/Dialect/Arith/ops.mlir
+++ b/mlir/test/Dialect/Arith/ops.mlir
@@ -1228,3 +1228,83 @@ func.func @intflags_func(%arg0: i64, %arg1: i64) {
   %4 = arith.trunci %arg0 overflow<nsw, nuw> : i64 to i32
   return
 }
+
+// CHECK-LABEL: func @test_fptofp(
+// CHECK-SAME: %[[ARG0:.*]]: f16
+func.func @test_fptofp(%arg0 : f16) -> f32 {
+  // CHECK: arith.fptofp %[[ARG0]] : f16 to f32
+  %0 = arith.fptofp %arg0 : f16 to f32
+  return %0 : f32
+}
+
+// CHECK-LABEL: func @test_fptofp_bf16_to_f16(
+// CHECK-SAME: %[[ARG0:.*]]: bf16
+func.func @test_fptofp_bf16_to_f16(%arg0 : bf16) -> f16 {
+  // CHECK: arith.fptofp %[[ARG0]] : bf16 to f16
+  %0 = arith.fptofp %arg0 : bf16 to f16
+  return %0 : f16
+}
+
+// CHECK-LABEL: func @test_fptofp_vector(
+// CHECK-SAME: %[[ARG0:.*]]: vector<8xf16>
+func.func @test_fptofp_vector(%arg0 : vector<8xf16>) -> vector<8xf32> {
+  // CHECK: arith.fptofp %[[ARG0]] : vector<8xf16> to vector<8xf32>
+  %0 = arith.fptofp %arg0 : vector<8xf16> to vector<8xf32>
+  return %0 : vector<8xf32>
+}
+
+// CHECK-LABEL: func @test_fptofp_scalable_vector(
+// CHECK-SAME: %[[ARG0:.*]]: vector<[8]xf16>
+func.func @test_fptofp_scalable_vector(%arg0 : vector<[8]xf16>) -> vector<[8]xf32> {
+  // CHECK: arith.fptofp %[[ARG0]] : vector<[8]xf16> to vector<[8]xf32>
+  %0 = arith.fptofp %arg0 : vector<[8]xf16> to vector<[8]xf32>
+  return %0 : vector<[8]xf32>
+}
+
+// CHECK-LABEL: func @test_fptofp_tensor(
+// CHECK-SAME: %[[ARG0:.*]]: tensor<8x8xf32>
+func.func @test_fptofp_tensor(%arg0 : tensor<8x8xf32>) -> tensor<8x8xf64> {
+  // CHECK: arith.fptofp %[[ARG0]] : tensor<8x8xf32> to tensor<8x8xf64>
+  %0 = arith.fptofp %arg0 : tensor<8x8xf32> to tensor<8x8xf64>
+  return %0 : tensor<8x8xf64>
+}
+
+// CHECK-LABEL: func @test_fptofp_tensor_encoding(
+// CHECK-SAME: %[[ARG0:.*]]: tensor<8x8xf32, "foo">
+func.func @test_fptofp_tensor_encoding(%arg0 : tensor<8x8xf32, "foo">) -> tensor<8x8xf64, "foo"> {
+  // CHECK: arith.fptofp %[[ARG0]] : tensor<8x8xf32, "foo"> to tensor<8x8xf64, "foo">
+  %0 = arith.fptofp %arg0 : tensor<8x8xf32, "foo"> to tensor<8x8xf64, "foo">
+  return %0 : tensor<8x8xf64, "foo">
+}
+
+// CHECK-LABEL: func @test_fptofp_rounding_mode(
+// CHECK-SAME: %[[ARG0:.*]]: f64
+func.func @test_fptofp_rounding_mode(%arg0 : f64) -> (f32, f32, f32, f32, f32) {
+  // CHECK: arith.fptofp %[[ARG0]] to_nearest_even : f64 to f32
+  %0 = arith.fptofp %arg0 to_nearest_even : f64 to f32
+  // CHECK: arith.fptofp %[[ARG0]] downward : f64 to f32
+  %1 = arith.fptofp %arg0 downward : f64 to f32
+  // CHECK: arith.fptofp %[[ARG0]] upward : f64 to f32
+  %2 = arith.fptofp %arg0 upward : f64 to f32
+  // CHECK: arith.fptofp %[[ARG0]] toward_zero : f64 to f32
+  %3 = arith.fptofp %arg0 toward_zero : f64 to f32
+  // CHECK: arith.fptofp %[[ARG0]] to_nearest_away : f64 to f32
+  %4 = arith.fptofp %arg0 to_nearest_away : f64 to f32
+  return %0, %1, %2, %3, %4 : f32, f32, f32, f32, f32
+}
+
+// CHECK-LABEL: func @test_fptofp_fastmath(
+// CHECK-SAME: %[[ARG0:.*]]: f32
+func.func @test_fptofp_fastmath(%arg0 : f32) -> f64 {
+  // CHECK: arith.fptofp %[[ARG0]] fastmath<nnan> : f32 to f64
+  %0 = arith.fptofp %arg0 fastmath<nnan> : f32 to f64
+  return %0 : f64
+}
+
+// CHECK-LABEL: func @test_fptofp_rounding_mode_and_fastmath(
+// CHECK-SAME: %[[ARG0:.*]]: f64
+func.func @test_fptofp_rounding_mode_and_fastmath(%arg0 : f64) -> f32 {
+  // CHECK: arith.fptofp %[[ARG0]] to_nearest_even fastmath<nnan> : f64 to f32
+  %0 = arith.fptofp %arg0 to_nearest_even fastmath<nnan> : f64 to f32
+  return %0 : f32
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/188041


More information about the Mlir-commits mailing list