[Mlir-commits] [mlir] [mlir][arith][RFC] Add new strict FP handling in Arith (PR #209287)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Mon Jul 13 12:55:17 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-mlir-math

@llvm/pr-subscribers-mlir-arith

Author: Andy Kaylor (andykaylor)

<details>
<summary>Changes</summary>

This is a proposed implementation of strict floating-point handling for Arith and Math dialects. If this direction is accepted, I will split the individual commits in this PR out for proper review. I have combined them here so that the entire design can be considered as a whole.

This is a follow-up to my prior [RFC on Discourse](https://discourse.llvm.org/t/rfc-mlir-new-representation-of-floating-point-constraints/91123) proposing a new MLIR representation of floating-point constraints. Based on the feedback I received in https://github.com/llvm/llvm-project/pull/205158, I am moving the floating-point environment attribute out to the CIR and Arith dialects and lowering these to existing operations in the LLVM dialect that continue to map directly to the current form of LLVM IR.

This PR shows the Arith and Math implementation. I have posted a separate [PR for the CIR dialect changes](https://github.com/llvm/llvm-project/pull/209275).

This is intended to replace the existing rounding-mode handling in the Arith dialect. I have left the old handling in place here but added comments that it should be considered deprecated. I am open to suggestions on how that should be handled.

The implementation here has one significant difference from the CIR implementation. In the CIR implementation, I am lowering operations with the Fenv attribute to general intrinsic calls to the constrained floating-point intrinsics. Here I have added specialized constrained operations to the LLVM dialect where they were missing and lower to those. I did this to be consistent with the existing Arith rounding-mode handling, though I think it may be more consistent with the LLVM dialect design philosophy to remove the specialized constrained operations there entirely and to lower to the generalized intrinsic calls as I have done in CIR.

Assisted-by: Cursor / claude-opus-4.8

---

Patch is 107.11 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/209287.diff


19 Files Affected:

- (modified) mlir/include/mlir/Conversion/ArithCommon/AttrToLLVMConverter.h (+51-9) 
- (modified) mlir/include/mlir/Dialect/Arith/IR/ArithBase.td (+147-1) 
- (modified) mlir/include/mlir/Dialect/Arith/IR/ArithOps.td (+81-8) 
- (modified) mlir/include/mlir/Dialect/Arith/IR/ArithOpsInterfaces.td (+83) 
- (modified) mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td (+78) 
- (modified) mlir/include/mlir/Dialect/Math/IR/MathOps.td (+245-25) 
- (modified) mlir/lib/Conversion/ArithCommon/AttrToLLVMConverter.cpp (+17) 
- (modified) mlir/lib/Conversion/ArithToLLVM/ArithToLLVM.cpp (+67-26) 
- (modified) mlir/lib/Conversion/MathToLLVM/MathToLLVM.cpp (+201-34) 
- (modified) mlir/lib/Dialect/Arith/IR/ArithCanonicalization.td (+26-14) 
- (modified) mlir/lib/Dialect/Arith/Transforms/ExpandOps.cpp (+14-10) 
- (modified) mlir/lib/Dialect/Math/IR/MathOps.cpp (+5) 
- (modified) mlir/test/Conversion/ArithToLLVM/arith-to-llvm.mlir (+82) 
- (modified) mlir/test/Conversion/MathToLLVM/math-to-llvm.mlir (+128) 
- (modified) mlir/test/Dialect/Arith/expand-ops.mlir (+13) 
- (modified) mlir/test/Dialect/Arith/invalid.mlir (+8) 
- (modified) mlir/test/Dialect/Arith/ops.mlir (+23) 
- (modified) mlir/test/Dialect/Math/ops.mlir (+20) 
- (modified) mlir/test/Target/LLVMIR/Import/intrinsic-unregistered.ll (+5-5) 


``````````diff
diff --git a/mlir/include/mlir/Conversion/ArithCommon/AttrToLLVMConverter.h b/mlir/include/mlir/Conversion/ArithCommon/AttrToLLVMConverter.h
index feb74c86e349f..7f4bb80cc4f07 100644
--- a/mlir/include/mlir/Conversion/ArithCommon/AttrToLLVMConverter.h
+++ b/mlir/include/mlir/Conversion/ArithCommon/AttrToLLVMConverter.h
@@ -45,6 +45,18 @@ convertArithRoundingModeAttrToLLVM(arith::RoundingModeAttr roundingModeAttr);
 LLVM::FPExceptionBehaviorAttr
 getLLVMDefaultFPExceptionBehavior(MLIRContext &context);
 
+/// Maps an arith floating-point exception mode and strict-exception flag, as
+/// described by an `#arith.fenv` attribute, to the corresponding LLVM
+/// constrained-intrinsic exception behavior:
+///
+///   masked            + strict_except=false -> fpexcept.ignore
+///   masked            + strict_except=true  -> fpexcept.strict
+///   unmasked|unknown  + strict_except=false -> fpexcept.maytrap
+///   unmasked|unknown  + strict_except=true  -> fpexcept.strict
+LLVM::FPExceptionBehavior
+convertArithFPExceptionBehaviorToLLVM(arith::FPExceptionMode exceptionMode,
+                                      bool strictExcept);
+
 // Attribute converter that populates a NamedAttrList by removing the fastmath
 // attribute from the source operation attributes, and replacing it with an
 // equivalent LLVM fastmath attribute.
@@ -145,25 +157,55 @@ class AttrConverterConstrainedFPToLLVM {
   AttrConverterConstrainedFPToLLVM(SourceOp srcOp) {
     // Copy the source attributes.
     convertedAttr = NamedAttrList{srcOp->getAttrs()};
+    MLIRContext *ctx = srcOp->getContext();
+
+    // The floating-point environment is described by the `#arith.fenv`
+    // attribute and, on operations that predate it, may also be described by
+    // the deprecated `roundingmode` attribute. Collect both (the verifier
+    // guarantees they are never set at the same time) and remove them from the
+    // attributes carried over to the target op.
+    arith::FenvAttr fenvAttr = srcOp.getFenvAttr();
+    convertedAttr.erase(srcOp.getFenvAttrName());
+
+    // Determine the rounding mode. The `fenv` attribute takes precedence; when
+    // neither source carries one, fall back to the default dynamic rounding
+    // mode.
+    [[maybe_unused]] arith::RoundingMode roundingMode =
+        arith::FenvAttr::getDefaultDynamicRoundingMode();
+    if constexpr (SourceOp::template hasTrait<
+                      arith::ArithRoundingModeInterface::Trait>()) {
+      auto roundingModeAttr = dyn_cast_if_present<arith::RoundingModeAttr>(
+          convertedAttr.erase(srcOp.getRoundingModeAttrName()));
+      if (!fenvAttr && roundingModeAttr)
+        roundingMode = roundingModeAttr.getValue();
+    }
+    if (fenvAttr)
+      roundingMode = fenvAttr.getDynamicRoundingModeOrDefault();
 
     if constexpr (TargetOp::template hasTrait<
                       LLVM::RoundingModeOpInterface::Trait>()) {
-      // Get the name of the rounding mode attribute.
-      StringRef arithAttrName = srcOp.getRoundingModeAttrName();
-      // Remove the source attribute.
-      auto arithAttr =
-          cast<arith::RoundingModeAttr>(convertedAttr.erase(arithAttrName));
-      // Set the target attribute.
       convertedAttr.set(TargetOp::getRoundingModeAttrName(),
-                        convertArithRoundingModeAttrToLLVM(arithAttr));
+                        LLVM::RoundingModeAttr::get(
+                            ctx, convertArithRoundingModeToLLVM(roundingMode)));
     }
     // Constrained intrinsics (llvm.intr.experimental.constrained.*) do not
     // support fastmath flags. Remove the arith fastmath attribute if present.
     if constexpr (SourceOp::template hasTrait<
                       arith::ArithFastMathInterface::Trait>())
       convertedAttr.erase(srcOp.getFastMathAttrName());
-    convertedAttr.set(TargetOp::getFPExceptionBehaviorAttrName(),
-                      getLLVMDefaultFPExceptionBehavior(*srcOp->getContext()));
+
+    // Determine the exception behavior from the `fenv` attribute, defaulting to
+    // `ignore` when no environment is specified (e.g. only a `roundingmode`
+    // attribute is present).
+    LLVM::FPExceptionBehavior exceptionBehavior =
+        LLVM::FPExceptionBehavior::Ignore;
+    if (fenvAttr)
+      exceptionBehavior = convertArithFPExceptionBehaviorToLLVM(
+          fenvAttr.getExceptionModeOrDefault(),
+          fenvAttr.getStrictExceptOrDefault());
+    convertedAttr.set(
+        TargetOp::getFPExceptionBehaviorAttrName(),
+        LLVM::FPExceptionBehaviorAttr::get(ctx, exceptionBehavior));
   }
 
   ArrayRef<NamedAttribute> getAttrs() const { return convertedAttr.getAttrs(); }
diff --git a/mlir/include/mlir/Dialect/Arith/IR/ArithBase.td b/mlir/include/mlir/Dialect/Arith/IR/ArithBase.td
index 985ae01008002..4132a4cfb3c55 100644
--- a/mlir/include/mlir/Dialect/Arith/IR/ArithBase.td
+++ b/mlir/include/mlir/Dialect/Arith/IR/ArithBase.td
@@ -186,12 +186,158 @@ def Arith_RTowardZero                // Round toward 0
     : I32EnumAttrCase<"toward_zero", 3>;
 def Arith_RToNearestTiesAwayFromZero // Round to nearest, ties away from zero
     : I32EnumAttrCase<"to_nearest_away", 4>;
+def Arith_RDynamic                   // Rounding mode unknown at compile time
+    : I32EnumAttrCase<"unknown", 7>;  // Mirrors `llvm::RoundingMode::Dynamic`.
 
 def Arith_RoundingModeAttr : I32EnumAttr<
     "RoundingMode", "Floating point rounding mode",
     [Arith_RToNearestTiesToEven, Arith_RDownward, Arith_RUpward,
-     Arith_RTowardZero, Arith_RToNearestTiesAwayFromZero]> {
+     Arith_RTowardZero, Arith_RToNearestTiesAwayFromZero, Arith_RDynamic]> {
   let cppNamespace = "::mlir::arith";
 }
 
+//===----------------------------------------------------------------------===//
+// Floating-point environment enums (used by the `#arith.fenv` attribute)
+//===----------------------------------------------------------------------===//
+
+def FPExceptionMode : I32EnumAttr<
+    "FPExceptionMode", "floating-point exception mode", [
+  I32EnumAttrCase<"Unknown", 0, "unknown">,
+  I32EnumAttrCase<"Masked", 1, "masked">,
+  I32EnumAttrCase<"Unmasked", 2, "unmasked">
+]> {
+  let description = [{
+    The known floating-point exception mode at the point the instruction is
+    executed. If the actual exception mode differs from this value, the
+    behavior is undefined.
+  }];
+  let cppNamespace = "::mlir::arith";
+  let genSpecializedAttr = 0;
+}
+
+//===----------------------------------------------------------------------===//
+// FenvAttr
+//===----------------------------------------------------------------------===//
+
+def Arith_FenvAttr : AttrDef<Arith_Dialect, "Fenv"> {
+  let mnemonic = "fenv";
+  let summary = "Describes floating-point environment constraints";
+  let description = [{
+    The `#arith.fenv` attribute describes constraints on the floating-point
+    handling of a floating-point operation. It can be attached to
+    floating-point operations to capture rounding and exception behavior. All
+    of its parameters are optional.
+
+    - `dynamic_rounding_mode`: the known dynamic rounding mode at the point the
+      instruction is executed. If the actual dynamic rounding mode when the
+      instruction is executed does not match the assumed value, the behavior is
+      undefined.
+    - `except_mode`: the known exception mode at the point the instruction is
+      executed. If the actual state of exception masking when the instruction is
+      executed does not match this value, the behavior is undefined.
+    - `strict_except`: if `false`, any case that would produce an FP exception
+      produces it non-deterministically instead (i.e. it may or may not occur).
+      This means the FP status is written non-deterministically, and, if
+      exceptions are unmasked, the instruction traps non-deterministically.
+
+    When this attribute is present on an operation, the operation is lowered to
+    the corresponding `llvm.experimental.constrained.*` intrinsic during
+    translation to LLVM IR.
+
+    The default setting for each parameter (`unknown` for
+    `dynamic_rounding_mode`, `masked` for exception_mode, and `false` for
+    `strict_except`) is canonically represented as an unset parameter.
+    Constructing an attribute whose parameters are all set to their defaults
+    therefore yields the same canonical, empty attribute as `#arith.fenv<>`.
+
+    Carrying the (possibly empty) attribute is *not* the same as having no
+    attribute at all. An operation that carries the attribute is lowered to the
+    corresponding constrained intrinsic operation in LLVM, even when every
+    parameter is at its default value. An operation with no `#arith.fenv`
+    attribute is lowered to the regular, unconstrained operation.
+
+    Examples:
+    ```mlir
+    fenv<dynamic_rounding_mode = to_nearest_even>
+    fenv<except_mode = unmasked>
+    fenv<strict_except = true>
+    fenv<dynamic_rounding_mode = upward, except_mode = unknown, strict_except = true>
+    ```
+  }];
+
+  let parameters = (ins
+    OptionalParameter<
+        "std::optional<::mlir::arith::RoundingMode>">:$dynamic_rounding_mode,
+    OptionalParameter<"std::optional<::mlir::arith::FPExceptionMode>">:$except_mode,
+    OptionalParameter<"mlir::BoolAttr">:$strict_except);
+
+  let assemblyFormat = [{
+    `<` struct($dynamic_rounding_mode, $except_mode, $strict_except) `>`
+  }];
+
+  // Replace the default builder so that constructing the attribute normalizes
+  // any parameter that is set to its default value to "unset". This keeps a
+  // single canonical representation for the default floating-point environment.
+  // Because `skipDefaultBuilders` suppresses the auto-synthesized const builder
+  // call, provide it explicitly. The `DefaultValuedAttr` form below uses a null
+  // attribute as its default, so this simply forwards its argument (a null
+  // attribute when the operation does not carry a `#arith.fenv` attribute).
+  let skipDefaultBuilders = 1;
+  let constBuilderCall = "$0";
+  let builders = [
+    AttrBuilder<(ins
+        CArg<"std::optional<::mlir::arith::RoundingMode>",
+             "std::nullopt">:$dynamicRoundingMode,
+        CArg<"std::optional<::mlir::arith::FPExceptionMode>",
+             "std::nullopt">:$exceptMode,
+        CArg<"mlir::BoolAttr", "{}">:$strictExcept), [{
+      if (dynamicRoundingMode == ::mlir::arith::RoundingMode::unknown)
+        dynamicRoundingMode = std::nullopt;
+      if (exceptMode == ::mlir::arith::FPExceptionMode::Masked)
+        exceptMode = std::nullopt;
+      if (strictExcept && !strictExcept.getValue())
+        strictExcept = {};
+      return Base::get($_ctxt, dynamicRoundingMode, exceptMode, strictExcept);
+    }]>
+  ];
+
+  let extraClassDeclaration = [{
+    /// The default dynamic rounding mode used when `dynamic_rounding_mode` is
+    /// unset.
+    static constexpr ::mlir::arith::RoundingMode
+    getDefaultDynamicRoundingMode() {
+      return ::mlir::arith::RoundingMode::unknown;
+    }
+
+    /// The default exception mode used when `except_mode` is unset.
+    static constexpr ::mlir::arith::FPExceptionMode getDefaultExceptionMode() {
+      return ::mlir::arith::FPExceptionMode::Masked;
+    }
+
+    /// Returns the effective dynamic rounding mode, substituting the default
+    /// when unset.
+    ::mlir::arith::RoundingMode getDynamicRoundingModeOrDefault() const {
+      return getDynamicRoundingMode().value_or(getDefaultDynamicRoundingMode());
+    }
+    /// Returns the effective exception mode, substituting the default when
+    /// unset.
+    ::mlir::arith::FPExceptionMode getExceptionModeOrDefault() const {
+      return getExceptMode().value_or(getDefaultExceptionMode());
+    }
+    /// Returns the effective `strict_except` flag.
+    bool getStrictExceptOrDefault() const {
+      ::mlir::BoolAttr attr = getStrictExcept();
+      return attr ? attr.getValue() : false;
+    }
+
+    /// Returns true if every parameter is at its default value, i.e. the
+    /// attribute describes the default floating-point environment and imposes
+    /// no constraints. Because the builder normalizes defaults to "unset", this
+    /// is equivalent to all parameters being unset.
+    bool isDefault() const {
+      return !getDynamicRoundingMode() && !getExceptMode() && !getStrictExcept();
+    }
+  }];
+}
+
 #endif // ARITH_BASE
diff --git a/mlir/include/mlir/Dialect/Arith/IR/ArithOps.td b/mlir/include/mlir/Dialect/Arith/IR/ArithOps.td
index 423948c8734af..c383e8d8cd1b9 100644
--- a/mlir/include/mlir/Dialect/Arith/IR/ArithOps.td
+++ b/mlir/include/mlir/Dialect/Arith/IR/ArithOps.td
@@ -93,6 +93,11 @@ class Arith_FloatBinaryOp<string mnemonic, list<Trait> traits = []> :
 
 // Base class for floating point binary operations with an optional rounding
 // mode.
+//
+// Deprecated: use `Arith_FloatBinaryOpWithFenv` instead. This class is retained
+// temporarily while floating-point operations migrate to the `#arith.fenv`
+// floating-point environment attribute, and will be removed once the migration
+// is complete.
 class Arith_FloatBinaryOpWithRoundingMode<string mnemonic,
                                           list<Trait> traits = []> :
     Arith_FloatBinaryOp<mnemonic,
@@ -128,6 +133,50 @@ class Arith_FloatBinaryOpWithRoundingMode<string mnemonic,
                           attr-dict `:` type($result) }];
 }
 
+// Base class for floating point binary operations that can carry an optional
+// floating-point environment (`#arith.fenv`) attribute. It extends
+// `Arith_FloatBinaryOpWithRoundingMode` with the `fenv` attribute and the
+// `FPEnvConstrainedOpInterface`, which provides a uniform API for querying the
+// floating-point environment constraints.
+class Arith_FloatBinaryOpWithFenv<string mnemonic, list<Trait> traits = []> :
+    Arith_FloatBinaryOpWithRoundingMode<mnemonic,
+      !listconcat([DeclareOpInterfaceMethods<FPEnvConstrainedOpInterface>],
+                  traits)> {
+  let arguments = (ins FloatLike:$lhs, FloatLike:$rhs,
+      DefaultValuedAttr<
+        Arith_FastMathAttr, "::mlir::arith::FastMathFlags::none">:$fastmath,
+      OptionalAttr<Arith_RoundingModeAttr>:$roundingmode,
+      OptionalAttr<Arith_FenvAttr>:$fenv);
+  // With two trailing optional attributes (`roundingmode` and `fenv`), the
+  // generated builders no longer provide C++ default arguments for them, so the
+  // convenience builders must pass both explicitly.
+  let builders = [
+    OpBuilder<(ins "Value":$lhs, "Value":$rhs,
+      CArg<"::mlir::arith::FastMathFlags",
+           "::mlir::arith::FastMathFlags::none">:$fastmath), [{
+      build($_builder, $_state, lhs, rhs, fastmath,
+            ::mlir::arith::RoundingModeAttr{}, ::mlir::arith::FenvAttr{});
+    }]>,
+    OpBuilder<(ins "Value":$lhs, "Value":$rhs,
+      "::mlir::arith::FastMathFlagsAttr":$fastmath), [{
+      build($_builder, $_state, lhs, rhs, fastmath,
+            ::mlir::arith::RoundingModeAttr{}, ::mlir::arith::FenvAttr{});
+    }]>,
+    OpBuilder<(ins "Type":$type, "Value":$lhs, "Value":$rhs,
+      CArg<"::mlir::arith::FastMathFlags",
+           "::mlir::arith::FastMathFlags::none">:$fastmath), [{
+      build($_builder, $_state, type, lhs, rhs,
+            ::mlir::arith::FastMathFlagsAttr::get(
+                $_builder.getContext(), fastmath),
+            ::mlir::arith::RoundingModeAttr{}, ::mlir::arith::FenvAttr{});
+    }]>,
+  ];
+  let assemblyFormat = [{ $lhs `,` $rhs ($roundingmode^)?
+                          (`fastmath` `` $fastmath^)?
+                          (`fenv` `` $fenv^)?
+                          attr-dict `:` type($result) }];
+}
+
 // Checks that tensor input and outputs have identical shapes. This is stricker
 // than the verification done in `SameOperandsAndResultShape` that allows for
 // tensor dimensions to be 'compatible' (e.g., dynamic dimensions being
@@ -1089,7 +1138,7 @@ def Arith_FlushDenormalsOp : Arith_FloatUnaryOp<"flush_denormals"> {
 // AddFOp
 //===----------------------------------------------------------------------===//
 
-def Arith_AddFOp : Arith_FloatBinaryOpWithRoundingMode<"addf", [Commutative]> {
+def Arith_AddFOp : Arith_FloatBinaryOpWithFenv<"addf", [Commutative]> {
   let summary = "floating point addition operation";
   let description = [{
     The `addf` operation takes two operands and returns one result, each of
@@ -1120,7 +1169,7 @@ def Arith_AddFOp : Arith_FloatBinaryOpWithRoundingMode<"addf", [Commutative]> {
 // SubFOp
 //===----------------------------------------------------------------------===//
 
-def Arith_SubFOp : Arith_FloatBinaryOpWithRoundingMode<"subf"> {
+def Arith_SubFOp : Arith_FloatBinaryOpWithFenv<"subf"> {
   let summary = "floating point subtraction operation";
   let description = [{
     The `subf` operation takes two operands and returns one result, each of
@@ -1276,7 +1325,7 @@ def Arith_MinUIOp : Arith_TotalIntBinaryOp<"minui",
 // MulFOp
 //===----------------------------------------------------------------------===//
 
-def Arith_MulFOp : Arith_FloatBinaryOpWithRoundingMode<"mulf", [Commutative]> {
+def Arith_MulFOp : Arith_FloatBinaryOpWithFenv<"mulf", [Commutative]> {
   let summary = "floating point multiplication operation";
   let description = [{
     The `mulf` operation takes two operands and returns one result, each of
@@ -1308,7 +1357,7 @@ def Arith_MulFOp : Arith_FloatBinaryOpWithRoundingMode<"mulf", [Commutative]> {
 // DivFOp
 //===----------------------------------------------------------------------===//
 
-def Arith_DivFOp : Arith_FloatBinaryOpWithRoundingMode<"divf"> {
+def Arith_DivFOp : Arith_FloatBinaryOpWithFenv<"divf"> {
   let summary = "floating point division operation";
   let description = [{
     The `divf` operation takes two operands and returns one result, each of
@@ -1566,10 +1615,17 @@ def Arith_TruncFOp :
       [Pure, SameOperandsAndResultShape, SameInputOutputTensorDims,
        DeclareOpInterfaceMethods<ArithRoundingModeInterface>,
        DeclareOpInterfaceMethods<ArithFastMathInterface>,
+       DeclareOpInterfaceMethods<FPEnvConstrainedOpInterface>,
        DeclareOpInterfaceMethods<CastOpInterface>]>,
     Arguments<(ins FloatLike:$in,
+                   // Deprecated: represent the rounding mode through the `fenv`
+                   // attribute instead. This attribute is retained temporarily
+                   // while floating-point operations migrate to the
+                   // `#arith.fenv` floating-point environment attribute, and
+                   // will be removed once the migration is complete.
                    OptionalAttr<Arith_RoundingModeAttr>:$roundingmode,
-                   OptionalAttr<Arith_FastMathAttr>:$fastmath)>,
+                   OptionalAttr<Arith_FastMathAttr>:$fastmath,
+                   OptionalAttr<Arith_FenvAttr>:$fenv)>,
     Results<(outs FloatLike:$out)> {
   let summary = "cast from floating-point to narrower floating-point";
   let description = [{
@@ -1589,6 +1645,7 @@ def Arith_TruncFOp :
   let hasVerifier = 1;
   let assemblyFormat = [{ $in ($roundingmode^)?
                           (`fastmath` `` $fastmath^)?
+                          (`fenv` `` $fenv^)?
                           attr-dict `:` type($in) `to` type($out) }];
 }
 
@@ -1601,10 +1658,17 @@ def Arith_ConvertFOp :
       [Pure, SameOperandsAndResultShape, SameInputOutputTensorDims,
        DeclareOpInterfaceMethods<ArithRoundingModeInterface>,
        DeclareOpInterfaceMethods<ArithFastMathInterface>,
+       DeclareOpInterfaceMethods<FPEnvConstrainedOpInterface>,
        DeclareOpInterfaceMethods<CastOpInterface>]>,
     Arguments<(ins FloatLike:$in,
+                   // Deprecated: represent the rounding mode through the `fenv`
+                   // attribute instead. This attribute is retained temporarily
+                   // while floating-point operations migrate to the
+                   // `#arith.fenv` floating-point environment attribute, and
+                   // will be removed once the migration is complete.
                    OptionalAttr<Arith_RoundingModeAttr>:$roundingmode,
-                   OptionalAttr<Arith_FastMathAttr>:$fastmath)>,
+                   OptionalAttr<Arith_FastMathAttr>:$fastmath,
+                   OptionalAttr<Arith_FenvAttr>:$fenv)>,
     Results<(outs FloatLike:$out)> {
   let summary = "cast between floating-point types of the same bitwidth";
   let description = [{
@@ -1621,6 ...
[truncated]

``````````

</details>


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


More information about the Mlir-commits mailing list