https://github.com/andykaylor created 
https://github.com/llvm/llvm-project/pull/211144

This introduces a new CIR attribute that will be used to describe 
floating-point environment assumptions and restrictions, allowing for general 
modeling of floating-point environment access. A new interface is also 
introduced to simplify handling of default settings when the attribute or one 
of its optional components is not present.

This patch adds the attribute and interface to FPBinaryOp, 
BinaryFPToFPBuiltinOp, and UnaryFPToFPBuiltin. Support for generating 
operations with this attribute and lowering them to the LLVM dialect will be 
added in a future change.

Assisted-by: Cursor / claude-opus-4.8

>From 51e84bac7148f7354fe0b559d087d9fc500863fa Mon Sep 17 00:00:00 2001
From: Andy Kaylor <[email protected]>
Date: Wed, 10 Jun 2026 14:45:47 -0700
Subject: [PATCH] [CIR] Introduce fenv attribute for strict fp handling

This introduces a new CIR attribute that will be used to describe
floating-point environment assumptions and restrictions, allowing
for general modeling of floating-point environment access. A new
interface is also introduced to simplify handling of default
settings when the attribute or one of its optional components is
not present.

This patch adds the attribute and interface to FPBinaryOp,
BinaryFPToFPBuiltinOp, and UnaryFPToFPBuiltin. Support for generating
operations with this attribute and lowering them to the LLVM dialect will
be added in a future change.

Assisted-by: Cursor / claude-opus-4.8
---
 .../include/clang/CIR/Dialect/IR/CIRAttrs.td  | 101 +++++++++++++
 .../include/clang/CIR/Dialect/IR/CIRDialect.h |  39 +++++
 clang/include/clang/CIR/Dialect/IR/CIROps.td  |  70 +++++++--
 .../clang/CIR/Interfaces/CIROpInterfaces.h    |   1 +
 .../clang/CIR/Interfaces/CIROpInterfaces.td   |  41 ++++++
 clang/test/CIR/IR/fenv.cir                    |  68 +++++++++
 clang/unittests/CIR/CMakeLists.txt            |   1 +
 clang/unittests/CIR/FenvOpTest.cpp            | 136 ++++++++++++++++++
 8 files changed, 447 insertions(+), 10 deletions(-)
 create mode 100644 clang/test/CIR/IR/fenv.cir
 create mode 100644 clang/unittests/CIR/FenvOpTest.cpp

diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td 
b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
index 529e6e32c865e..f0c8464b8f105 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
@@ -797,6 +797,107 @@ def CIR_CmpThreeWayInfoAttr : CIR_Attr<"CmpThreeWayInfo", 
"cmp3way_info"> {
   let canHaveIllegalCXXABIType = 0;
 }
 
+//===----------------------------------------------------------------------===//
+// FenvAttr
+//===----------------------------------------------------------------------===//
+
+def CIR_FPDynamicRoundingMode : CIR_I32EnumAttr<
+    "FPDynamicRoundingMode", "floating-point dynamic rounding mode", [
+  I32EnumAttrCase<"ToNearest", 0, "tonearest">,
+  I32EnumAttrCase<"Downward", 1, "downward">,
+  I32EnumAttrCase<"Upward", 2, "upward">,
+  I32EnumAttrCase<"UpwardZero", 3, "upwardzero">,
+  I32EnumAttrCase<"ToNearestAway", 4, "tonearestaway">,
+  I32EnumAttrCase<"Unknown", 7, "unknown">
+]> {
+  let description = [{
+    The known dynamic rounding mode at the point the instruction is executed.
+    If the actual dynamic rounding mode differs from this value, the behavior
+    is undefined.
+  }];
+  let genSpecializedAttr = 0;
+}
+
+def CIR_FPExceptionMode : CIR_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 genSpecializedAttr = 0;
+}
+
+def CIR_FenvAttr : CIR_Attr<"Fenv", "fenv"> {
+  let summary = "Describes floating-point environment constraints";
+  let description = [{
+    The `#cir.fenv` attribute describes constraints on the floating-point
+    handling of a floating-point operation. It is 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. Otherwise the behavior is undefined.
+    - `except_mode`: the known exception mode at the point the instruction is
+      executed. Otherwise 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.
+
+    An absent `dynamic_rounding_mode` defaults to `unknown`, an absent
+    `except_mode` defaults to `masked`, and an absent `strict_except` defaults
+    to `false`.
+
+    Example:
+    ```
+    #cir.fenv<dynamic_rounding_mode = tonearest, except_mode = unknown,
+              strict_except = true>
+    ```
+  }];
+
+  let parameters = (ins
+    OptionalParameter<
+        "std::optional<cir::FPDynamicRoundingMode>">:$dynamic_rounding_mode,
+    OptionalParameter<"std::optional<cir::FPExceptionMode>">:$except_mode,
+    OptionalParameter<"mlir::BoolAttr">:$strict_except);
+
+  let assemblyFormat = [{
+    `<` struct($dynamic_rounding_mode, $except_mode, $strict_except) `>`
+  }];
+
+  let extraClassDeclaration = [{
+    static constexpr cir::FPDynamicRoundingMode
+    getDefaultDynamicRoundingMode() {
+      return cir::FPDynamicRoundingMode::Unknown;
+    }
+
+    static constexpr cir::FPExceptionMode getDefaultExceptionMode() {
+      return cir::FPExceptionMode::Masked;
+    }
+
+    cir::FPDynamicRoundingMode getDynamicRoundingModeOrDefault() const {
+      return getDynamicRoundingMode().value_or(
+          getDefaultDynamicRoundingMode());
+    }
+
+    cir::FPExceptionMode getExceptionModeOrDefault() const {
+      return getExceptMode().value_or(getDefaultExceptionMode());
+    }
+
+    bool getStrictExceptOrDefault() const {
+      mlir::BoolAttr strictExcept = getStrictExcept();
+      return strictExcept && strictExcept.getValue();
+    }
+  }];
+
+  let canHaveIllegalCXXABIType = 0;
+}
+
 
//===----------------------------------------------------------------------===//
 // GlobalViewAttr
 
//===----------------------------------------------------------------------===//
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRDialect.h 
b/clang/include/clang/CIR/Dialect/IR/CIRDialect.h
index 970a6984a5b05..c6f6c80206bfe 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRDialect.h
+++ b/clang/include/clang/CIR/Dialect/IR/CIRDialect.h
@@ -42,6 +42,45 @@ using BuilderOpStateCallbackRef = llvm::function_ref<void(
 namespace cir {
 void buildTerminatedBody(mlir::OpBuilder &builder, mlir::Location loc);
 
+/// The process floating-point environment, including its rounding mode and
+/// exception state.
+struct FloatingPointEnvironmentResource
+    : public mlir::SideEffects::Resource::Base<
+          FloatingPointEnvironmentResource> {
+  mlir::StringRef getName() const final { return "FloatingPointEnvironment"; }
+  bool isAddressable() const final { return false; }
+};
+
+template <typename ConcreteType>
+class FenvOpTrait : public mlir::OpTrait::TraitBase<ConcreteType, FenvOpTrait> 
{
+public:
+  mlir::Speculation::Speculatability getSpeculatability() {
+    // Masked exceptions cannot trap. When strict_except is false, exception
+    // side effects are non-deterministic, so speculation is still safe.
+    FPEnvConstrainedOpInterface fenvOp = getFenvOp();
+    if (fenvOp.getFenvExceptionMode() == cir::FPExceptionMode::Masked &&
+        !fenvOp.getFenvStrictExcept())
+      return mlir::Speculation::Speculatable;
+
+    return mlir::Speculation::NotSpeculatable;
+  }
+
+  void getEffects(
+      llvm::SmallVectorImpl<mlir::MemoryEffects::EffectInstance> &effects) {
+    if (!getFenvOp().getFenvAttr())
+      return;
+    effects.emplace_back(mlir::MemoryEffects::Read::get(),
+                         FloatingPointEnvironmentResource::get());
+    effects.emplace_back(mlir::MemoryEffects::Write::get(),
+                         FloatingPointEnvironmentResource::get());
+  }
+
+private:
+  FPEnvConstrainedOpInterface getFenvOp() {
+    return mlir::cast<FPEnvConstrainedOpInterface>(this->getOperation());
+  }
+};
+
 /// Look up the RecordLayoutAttr for a named record in the module's
 /// cir.record_layouts dictionary.  Asserts if the entry is missing.
 RecordLayoutAttr getRecordLayout(mlir::ModuleOp module, mlir::StringAttr name);
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td 
b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 0f034a4f1f7fa..6dbcf160e1564 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -1882,7 +1882,9 @@ class CIR_UnaryOp<string mnemonic, Type type, list<Trait> 
traits = []>
         DeclareOpInterfaceMethods<CIR_UnaryOpInterface>
       ], traits)>
 {
-  let arguments = (ins type:$input);
+  dag commonArgs = (ins type:$input);
+
+  let arguments = commonArgs;
 
   let results = (outs type:$result);
 
@@ -2557,17 +2559,21 @@ def CIR_MulOverflowOp : 
CIR_BinOpOverflow<"mul.overflow", [Commutative]> {
 
 // Base class for all CIR binary arithmetic/logic operations.
 // `type` constrains the lhs/rhs/result type.
-class CIR_BinaryOp<string mnemonic, Type type, list<Trait> traits = []>
-    : CIR_Op<mnemonic, !listconcat([
-        NoMemoryEffect, SameOperandsAndResultType,
+class CIR_BinaryOp<string mnemonic, Type type, list<Trait> traits = [],
+                   bit hasNoMemoryEffect = 1>
+    : CIR_Op<mnemonic, !listconcat(
+      !if(hasNoMemoryEffect, [NoMemoryEffect], []), [
+        SameOperandsAndResultType,
         DeclareOpInterfaceMethods<CIR_BinaryOpInterface>
       ], traits)>
 {
-  let arguments = (ins
+  dag commonArgs = (ins
     type:$lhs,
     type:$rhs
   );
 
+  let arguments = commonArgs;
+
   let results = (outs type:$result);
 
   let assemblyFormat = [{
@@ -2741,11 +2747,38 @@ def CIR_RemOp : CIR_BinaryOp<"rem", 
CIR_AnyIntOrVecOfIntType> {
 // Floating-Point Binary Arithmetic Operations
 
//===----------------------------------------------------------------------===//
 
+def CIR_FenvOpTrait : NativeOpTrait<"FenvOpTrait"> {
+  let cppNamespace = "::cir";
+}
+
+defvar CIR_FenvOpTraits = [
+  CIR_FPEnvConstrainedOpInterface,
+  ConditionallySpeculatable,
+  MemoryEffectsOpInterface,
+  CIR_FenvOpTrait
+];
+
+// Passed as CIR_BinaryOp's hasNoMemoryEffect: fenv ops compute effects
+// dynamically via FenvOpTrait, so they must not inherit NoMemoryEffect.
+defvar CIR_DynamicMemoryEffects = 0;
+
 // Base class for floating-point binary arithmetic operations. The lhs, rhs,
 // and result must all be the same floating-point scalar or vector type.
+//
+// The optional `fenv` attribute describes constraints on the floating-point
+// handling of the operation.
 class CIR_FPBinaryOp<string mnemonic, list<Trait> traits = []>
     : CIR_BinaryOp<mnemonic, CIR_AnyFloatOrVecOfFloatType,
-                   !listconcat([Pure], traits)>;
+                   !listconcat(CIR_FenvOpTraits, traits),
+                   CIR_DynamicMemoryEffects> {
+  let arguments = !con(commonArgs, (ins OptionalAttr<CIR_FenvAttr>:$fenv));
+
+  let builders = [
+    OpBuilder<(ins "mlir::Value":$lhs, "mlir::Value":$rhs), [{
+      build($_builder, $_state, lhs, rhs, cir::FenvAttr{});
+    }]>
+  ];
+}
 
 
//===----------------------------------------------------------------------===//
 // FAddOp
@@ -6899,13 +6932,21 @@ def CIR_PtrDiffOp : CIR_Op<"ptr_diff", [Pure, 
SameTypeOperands]> {
 
//===----------------------------------------------------------------------===//
 
 class CIR_UnaryFPToFPBuiltinOp<string mnemonic, string llvmOpName>
-    : CIR_Op<mnemonic, [Pure, SameOperandsAndResultType]>
+    : CIR_Op<mnemonic,
+             !listconcat([SameOperandsAndResultType], CIR_FenvOpTraits)>
 {
-  let arguments = (ins CIR_AnyFloatOrVecOfFloatType:$src);
+  let arguments = (ins CIR_AnyFloatOrVecOfFloatType:$src,
+                       OptionalAttr<CIR_FenvAttr>:$fenv);
   let results = (outs CIR_AnyFloatOrVecOfFloatType:$result);
 
   let assemblyFormat = "$src `:` type($src) attr-dict";
 
+  let builders = [
+    OpBuilder<(ins "mlir::Value":$src), [{
+      build($_builder, $_state, src, cir::FenvAttr{});
+    }]>
+  ];
+
   let llvmOp = llvmOpName;
 }
 
@@ -7210,14 +7251,16 @@ def CIR_LlrintOp : CIR_UnaryFPToIntBuiltinOp<"llrint", 
"LlrintOp"> {
 }
 
 class CIR_BinaryFPToFPBuiltinOp<string mnemonic, string llvmOpName>
-    : CIR_Op<mnemonic, [Pure, SameOperandsAndResultType]> {
+    : CIR_Op<mnemonic,
+             !listconcat([SameOperandsAndResultType], CIR_FenvOpTraits)> {
   let summary = [{
     libc builtin equivalent ignoring floating-point exceptions and errno.
   }];
 
   let arguments = (ins
     CIR_AnyFloatOrVecOfFloatType:$lhs,
-    CIR_AnyFloatOrVecOfFloatType:$rhs
+    CIR_AnyFloatOrVecOfFloatType:$rhs,
+    OptionalAttr<CIR_FenvAttr>:$fenv
   );
 
   let results = (outs  CIR_AnyFloatOrVecOfFloatType:$result);
@@ -7226,6 +7269,13 @@ class CIR_BinaryFPToFPBuiltinOp<string mnemonic, string 
llvmOpName>
     $lhs `,` $rhs `:` qualified(type($lhs)) attr-dict
   }];
 
+  let builders = [
+    OpBuilder<(ins "mlir::Type":$result, "mlir::Value":$lhs,
+                   "mlir::Value":$rhs), [{
+      build($_builder, $_state, result, lhs, rhs, cir::FenvAttr{});
+    }]>
+  ];
+
   let llvmOp = llvmOpName;
 }
 
diff --git a/clang/include/clang/CIR/Interfaces/CIROpInterfaces.h 
b/clang/include/clang/CIR/Interfaces/CIROpInterfaces.h
index cb7488d3aee36..830b64f488feb 100644
--- a/clang/include/clang/CIR/Interfaces/CIROpInterfaces.h
+++ b/clang/include/clang/CIR/Interfaces/CIROpInterfaces.h
@@ -21,6 +21,7 @@
 #include "clang/AST/Attr.h"
 #include "clang/AST/DeclTemplate.h"
 #include "clang/AST/Mangle.h"
+#include "clang/CIR/Dialect/IR/CIRAttrs.h"
 #include "clang/CIR/Dialect/IR/CIROpsEnums.h"
 
 /// Include the generated interface declarations.
diff --git a/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td 
b/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td
index fb256c4a26c2e..0b07b35e2921c 100644
--- a/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td
+++ b/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td
@@ -211,6 +211,47 @@ let cppNamespace = "::cir" in {
     }];
   }
 
+  def CIR_FPEnvConstrainedOpInterface
+      : OpInterface<"FPEnvConstrainedOpInterface"> {
+    let description = [{
+      Interface for floating-point operations that carry an optional
+      `#cir.fenv` attribute. It provides effective values for each field,
+      substituting the floating-point environment defaults for absent fields.
+    }];
+
+    let methods = [
+      InterfaceMethod<
+        "Return the `#cir.fenv` attribute, or a null attribute if absent.",
+        "cir::FenvAttr", "getFenvAttr", (ins), [{}],
+        /*defaultImplementation=*/[{
+          return $_op.getFenvAttr();
+        }]>,
+      InterfaceMethod<
+        "Return the effective dynamic rounding mode.",
+        "cir::FPDynamicRoundingMode", "getFenvDynamicRoundingMode", (ins),
+        [{}], /*defaultImplementation=*/[{
+          cir::FenvAttr fenv = $_op.getFenvAttr();
+          return fenv ? fenv.getDynamicRoundingModeOrDefault()
+                      : cir::FenvAttr::getDefaultDynamicRoundingMode();
+        }]>,
+      InterfaceMethod<
+        "Return the effective floating-point exception mode.",
+        "cir::FPExceptionMode", "getFenvExceptionMode", (ins), [{}],
+        /*defaultImplementation=*/[{
+          cir::FenvAttr fenv = $_op.getFenvAttr();
+          return fenv ? fenv.getExceptionModeOrDefault()
+                      : cir::FenvAttr::getDefaultExceptionMode();
+        }]>,
+      InterfaceMethod<
+        "Return the effective strict-exception flag.",
+        "bool", "getFenvStrictExcept", (ins), [{}],
+        /*defaultImplementation=*/[{
+          cir::FenvAttr fenv = $_op.getFenvAttr();
+          return fenv ? fenv.getStrictExceptOrDefault() : false;
+        }]>,
+    ];
+  }
+
   def CIR_BinaryOpInterface : OpInterface<"BinaryOpInterface"> {
     let description = [{
       Common interface for CIR binary arithmetic and logic operations.
diff --git a/clang/test/CIR/IR/fenv.cir b/clang/test/CIR/IR/fenv.cir
new file mode 100644
index 0000000000000..fad82abdb43dc
--- /dev/null
+++ b/clang/test/CIR/IR/fenv.cir
@@ -0,0 +1,68 @@
+// RUN: cir-opt %s --verify-roundtrip | FileCheck %s
+
+!s32i = !cir.int<s, 32>
+
+// CHECK-LABEL: cir.func @fadd_fenv
+cir.func @fadd_fenv(%a: !cir.float, %b: !cir.float) {
+  // All fields present.
+  // CHECK: cir.fadd %{{.*}}, %{{.*}} : !cir.float {fenv = 
#cir.fenv<dynamic_rounding_mode = tonearest, except_mode = unknown, 
strict_except = true>}
+  %0 = cir.fadd %a, %b : !cir.float {fenv = #cir.fenv<dynamic_rounding_mode = 
tonearest, except_mode = unknown, strict_except = true>}
+
+  // No fenv attribute at all.
+  // CHECK: cir.fadd %{{.*}}, %{{.*}} : !cir.float
+  // CHECK-NOT: fenv
+  %1 = cir.fadd %a, %b : !cir.float
+
+  // Empty fenv attribute.
+  // CHECK: cir.fadd %{{.*}}, %{{.*}} : !cir.float {fenv = #cir.fenv<>}
+  %2 = cir.fadd %a, %b : !cir.float {fenv = #cir.fenv<>}
+  cir.return
+}
+
+// Roundtrip each binary floating-point op with a fenv attribute.
+// CHECK-LABEL: cir.func @fp_binops_fenv
+cir.func @fp_binops_fenv(%a: !cir.double, %b: !cir.double) {
+  // CHECK: cir.fadd %{{.*}}, %{{.*}} : !cir.double {fenv = 
#cir.fenv<dynamic_rounding_mode = tonearest>}
+  %0 = cir.fadd %a, %b : !cir.double {fenv = #cir.fenv<dynamic_rounding_mode = 
tonearest>}
+  // CHECK: cir.fsub %{{.*}}, %{{.*}} : !cir.double {fenv = 
#cir.fenv<dynamic_rounding_mode = downward>}
+  %1 = cir.fsub %a, %b : !cir.double {fenv = #cir.fenv<dynamic_rounding_mode = 
downward>}
+  // CHECK: cir.fmul %{{.*}}, %{{.*}} : !cir.double {fenv = 
#cir.fenv<dynamic_rounding_mode = upward>}
+  %2 = cir.fmul %a, %b : !cir.double {fenv = #cir.fenv<dynamic_rounding_mode = 
upward>}
+  // CHECK: cir.fdiv %{{.*}}, %{{.*}} : !cir.double {fenv = 
#cir.fenv<dynamic_rounding_mode = upwardzero>}
+  %3 = cir.fdiv %a, %b : !cir.double {fenv = #cir.fenv<dynamic_rounding_mode = 
upwardzero>}
+  // CHECK: cir.frem %{{.*}}, %{{.*}} : !cir.double {fenv = 
#cir.fenv<dynamic_rounding_mode = tonearestaway>}
+  %4 = cir.frem %a, %b : !cir.double {fenv = #cir.fenv<dynamic_rounding_mode = 
tonearestaway>}
+  cir.return
+}
+
+// CHECK-LABEL: cir.func @fneg_fenv
+cir.func @fneg_fenv(%a: !cir.float, %v: !cir.vector<4 x !cir.float>) {
+  // CHECK: cir.fneg %{{.*}} : !cir.float {fenv = #cir.fenv<except_mode = 
masked>}
+  %0 = cir.fneg %a : !cir.float {fenv = #cir.fenv<except_mode = masked>}
+  // CHECK: cir.fneg %{{.*}} : !cir.vector<4 x !cir.float> {fenv = 
#cir.fenv<except_mode = unmasked, strict_except = false>}
+  %1 = cir.fneg %v : !cir.vector<4 x !cir.float> {fenv = #cir.fenv<except_mode 
= unmasked, strict_except = false>}
+  cir.return
+}
+
+// CHECK-LABEL: cir.func @unary_fp_builtin_fenv
+cir.func @unary_fp_builtin_fenv(%a: !cir.float) {
+  // CHECK: cir.sqrt %{{.*}} : !cir.float {fenv = 
#cir.fenv<dynamic_rounding_mode = unknown>}
+  %0 = cir.sqrt %a : !cir.float {fenv = #cir.fenv<dynamic_rounding_mode = 
unknown>}
+  // CHECK: cir.cos %{{.*}} : !cir.float {fenv = 
#cir.fenv<dynamic_rounding_mode = downward>}
+  %1 = cir.cos %a : !cir.float {fenv = #cir.fenv<dynamic_rounding_mode = 
downward>}
+  // CHECK: cir.floor %{{.*}} : !cir.float {fenv = 
#cir.fenv<dynamic_rounding_mode = upwardzero>}
+  %2 = cir.floor %a : !cir.float {fenv = #cir.fenv<dynamic_rounding_mode = 
upwardzero>}
+  cir.return
+}
+
+// CHECK-LABEL: cir.func @binary_fp_builtin_fenv
+cir.func @binary_fp_builtin_fenv(%a: !cir.float, %b: !cir.float) {
+  // CHECK: cir.copysign %{{.*}}, %{{.*}} : !cir.float {fenv = 
#cir.fenv<strict_except = true>}
+  %0 = cir.copysign %a, %b : !cir.float {fenv = #cir.fenv<strict_except = 
true>}
+  // CHECK: cir.pow %{{.*}}, %{{.*}} : !cir.float {fenv = 
#cir.fenv<dynamic_rounding_mode = tonearest, strict_except = true>}
+  %1 = cir.pow %a, %b : !cir.float {fenv = #cir.fenv<dynamic_rounding_mode = 
tonearest, strict_except = true>}
+  // CHECK: cir.atan2 %{{.*}}, %{{.*}} : !cir.float
+  // CHECK-NOT: fenv
+  %2 = cir.atan2 %a, %b : !cir.float
+  cir.return
+}
diff --git a/clang/unittests/CIR/CMakeLists.txt 
b/clang/unittests/CIR/CMakeLists.txt
index 93adf199486a9..fb1eea661b065 100644
--- a/clang/unittests/CIR/CMakeLists.txt
+++ b/clang/unittests/CIR/CMakeLists.txt
@@ -6,6 +6,7 @@ include_directories(${MLIR_TABLEGEN_OUTPUT_DIR})
 add_distinct_clang_unittest(CIRUnitTests
   CallOpTest.cpp
   ControlFlowTest.cpp
+  FenvOpTest.cpp
   GetFloatingPointTypeTest.cpp
   IntTypeABIAlignTest.cpp
   PointerLikeTest.cpp
diff --git a/clang/unittests/CIR/FenvOpTest.cpp 
b/clang/unittests/CIR/FenvOpTest.cpp
new file mode 100644
index 0000000000000..6a3a89b264e14
--- /dev/null
+++ b/clang/unittests/CIR/FenvOpTest.cpp
@@ -0,0 +1,136 @@
+//===- FenvOpTest.cpp - Unit tests for CIR fenv operations 
----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "mlir/IR/BuiltinOps.h"
+#include "mlir/IR/MLIRContext.h"
+#include "mlir/IR/OwningOpRef.h"
+#include "mlir/Interfaces/SideEffectInterfaces.h"
+#include "mlir/Parser/Parser.h"
+#include "clang/CIR/Dialect/IR/CIRDialect.h"
+
+#include <gtest/gtest.h>
+
+using namespace mlir;
+
+namespace {
+
+class CIRFenvOpTest : public ::testing::Test {
+protected:
+  CIRFenvOpTest() { context.loadDialect<cir::CIRDialect>(); }
+
+  OwningOpRef<ModuleOp> parse(StringRef ir) {
+    OwningOpRef<ModuleOp> module = parseSourceString<ModuleOp>(ir, &context);
+    EXPECT_TRUE(module) << "failed to parse IR";
+    return module;
+  }
+
+  template <typename OpTy> SmallVector<OpTy> findOps(ModuleOp module) {
+    SmallVector<OpTy> ops;
+    module.walk([&](OpTy op) { ops.push_back(op); });
+    return ops;
+  }
+
+  static SmallVector<MemoryEffects::EffectInstance> getEffects(Operation *op) {
+    MemoryEffectOpInterface effectsOp = cast<MemoryEffectOpInterface>(op);
+    SmallVector<MemoryEffects::EffectInstance> effects;
+    effectsOp.getEffects(effects);
+    return effects;
+  }
+
+  static void expectFenvReadAndWrite(Operation *op) {
+    SmallVector<MemoryEffects::EffectInstance> effects = getEffects(op);
+    ASSERT_EQ(effects.size(), 2u);
+
+    unsigned reads = 0;
+    unsigned writes = 0;
+    for (const MemoryEffects::EffectInstance &effect : effects) {
+      EXPECT_EQ(effect.getResource(),
+                cir::FloatingPointEnvironmentResource::get());
+      reads += isa<MemoryEffects::Read>(effect.getEffect());
+      writes += isa<MemoryEffects::Write>(effect.getEffect());
+    }
+    EXPECT_EQ(reads, 1u);
+    EXPECT_EQ(writes, 1u);
+  }
+
+  MLIRContext context;
+};
+
+TEST_F(CIRFenvOpTest, MemoryEffects) {
+  OwningOpRef<ModuleOp> module = parse(R"CIR(
+    cir.func @f(%a: !cir.float, %b: !cir.float) {
+      %0 = cir.fadd %a, %b : !cir.float
+      %1 = cir.fadd %a, %b : !cir.float {fenv = #cir.fenv<>}
+      %2 = cir.sqrt %a : !cir.float {fenv = #cir.fenv<>}
+      %3 = cir.pow %a, %b : !cir.float {fenv = #cir.fenv<>}
+      cir.return
+    }
+  )CIR");
+  ASSERT_TRUE(module);
+
+  SmallVector<cir::FAddOp> faddOps = findOps<cir::FAddOp>(*module);
+  ASSERT_EQ(faddOps.size(), 2u);
+  EXPECT_TRUE(getEffects(faddOps[0]).empty());
+  EXPECT_TRUE(isMemoryEffectFree(faddOps[0]));
+  expectFenvReadAndWrite(faddOps[1]);
+  EXPECT_FALSE(isMemoryEffectFree(faddOps[1]));
+
+  SmallVector<cir::SqrtOp> sqrtOps = findOps<cir::SqrtOp>(*module);
+  ASSERT_EQ(sqrtOps.size(), 1u);
+  expectFenvReadAndWrite(sqrtOps[0]);
+
+  SmallVector<cir::PowOp> powOps = findOps<cir::PowOp>(*module);
+  ASSERT_EQ(powOps.size(), 1u);
+  expectFenvReadAndWrite(powOps[0]);
+}
+
+TEST_F(CIRFenvOpTest, Speculatability) {
+  OwningOpRef<ModuleOp> module = parse(R"CIR(
+    cir.func @f(%a: !cir.float, %b: !cir.float) {
+      %0 = cir.fadd %a, %b : !cir.float
+      %1 = cir.fadd %a, %b : !cir.float {fenv = #cir.fenv<>}
+      %2 = cir.fadd %a, %b : !cir.float {
+        fenv = #cir.fenv<except_mode = masked>
+      }
+      %3 = cir.fadd %a, %b : !cir.float {
+        fenv = #cir.fenv<strict_except = false>
+      }
+      %4 = cir.fadd %a, %b : !cir.float {
+        fenv = #cir.fenv<except_mode = masked, strict_except = true>
+      }
+      %5 = cir.fadd %a, %b : !cir.float {
+        fenv = #cir.fenv<except_mode = unmasked, strict_except = false>
+      }
+      %6 = cir.fadd %a, %b : !cir.float {
+        fenv = #cir.fenv<except_mode = unknown, strict_except = false>
+      }
+      cir.return
+    }
+  )CIR");
+  ASSERT_TRUE(module);
+
+  SmallVector<cir::FAddOp> ops = findOps<cir::FAddOp>(*module);
+  ASSERT_EQ(ops.size(), 7u);
+
+  // Missing fenv fields use the defaults: masked exceptions and non-strict
+  // exception behavior.
+  for (unsigned i = 0; i != 4; ++i)
+    EXPECT_TRUE(isSpeculatable(ops[i])) << "operation " << i;
+
+  for (unsigned i = 4; i != ops.size(); ++i)
+    EXPECT_FALSE(isSpeculatable(ops[i])) << "operation " << i;
+
+  cir::FPEnvConstrainedOpInterface fenvOp =
+      cast<cir::FPEnvConstrainedOpInterface>(ops[1].getOperation());
+  EXPECT_EQ(fenvOp.getFenvDynamicRoundingMode(),
+            cir::FPDynamicRoundingMode::Unknown);
+  EXPECT_EQ(fenvOp.getFenvExceptionMode(), cir::FPExceptionMode::Masked);
+  EXPECT_FALSE(fenvOp.getFenvStrictExcept());
+}
+
+} // namespace

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to