[clang] 5e3f403 - [CIR] Add fenv attribute to cast operation (#212899)

via cfe-commits cfe-commits at lists.llvm.org
Thu Jul 30 09:19:35 PDT 2026


Author: Andy Kaylor
Date: 2026-07-30T09:19:30-07:00
New Revision: 5e3f4038530f54fa80ef2e2e648dcb399d279b70

URL: https://github.com/llvm/llvm-project/commit/5e3f4038530f54fa80ef2e2e648dcb399d279b70
DIFF: https://github.com/llvm/llvm-project/commit/5e3f4038530f54fa80ef2e2e648dcb399d279b70.diff

LOG: [CIR] Add fenv attribute to cast operation (#212899)

This adds the optional #cir.fenv attribute to the cir.cast operation and
updates the verifier to enforce the attribute being present only when
the cast involves floating-point values.

Assisted-by: Cursor / various models

Added: 
    

Modified: 
    clang/include/clang/CIR/Dialect/IR/CIROps.td
    clang/lib/CIR/Dialect/IR/CIRDialect.cpp
    clang/test/CIR/IR/fenv.cir
    clang/test/CIR/IR/invalid-cast.cir
    clang/unittests/CIR/FenvOpTest.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 35b0c30ba0725..40c3542e32172 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -122,6 +122,25 @@ class HasAtMostOneOfAttrs<list<string> names> : PredOpTrait<
   HasAtMostOneOfAttrsPred<!foreach(name, names, "$" # name)>
 >;
 
+//===----------------------------------------------------------------------===//
+// Floating-Point Environment Traits
+//===----------------------------------------------------------------------===//
+
+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;
+
 //===----------------------------------------------------------------------===//
 // CastOp
 //===----------------------------------------------------------------------===//
@@ -201,9 +220,9 @@ def CIR_CastKind : CIR_I32EnumAttr<"CastKind", "cast kind", [
   I32EnumAttrCase<"bool_to_float", 1000>,
 ]>;
 
-def CIR_CastOp : CIR_Op<"cast", [
-  Pure, DeclareOpInterfaceMethods<PromotableOpInterface>
-]> {
+def CIR_CastOp : CIR_Op<"cast", !listconcat([
+  DeclareOpInterfaceMethods<PromotableOpInterface>
+], CIR_FenvOpTraits)> {
   let summary = "Conversion between values of 
diff erent types";
   let description = [{
     Apply the usual C/C++ conversion rules between values. This operation models
@@ -239,6 +258,13 @@ def CIR_CastOp : CIR_Op<"cast", [
 
     - `bool_to_float`
 
+    The optional `fenv` attribute describes constraints on the floating-point
+    handling of the operation. It is only valid for floating-point cast kinds:
+    `floating`, `int_to_float`, `float_to_int`, `float_to_bool`,
+    `bool_to_float`, `float_to_complex`, `float_complex_to_real`,
+    `float_complex_to_bool`, `float_complex`, `float_complex_to_int_complex`,
+    and `int_complex_to_float_complex`.
+
     Example:
 
     ```
@@ -246,16 +272,27 @@ def CIR_CastOp : CIR_Op<"cast", [
     ...
     %x = cir.cast array_to_ptrdecay %0 
        : !cir.ptr<!cir.array<i32 x 10>> -> !cir.ptr<i32>
+    %y = cir.cast floating %f : !cir.double -> !cir.float {
+      fenv = #cir.fenv<>
+    }
     ```
   }];
 
-  let arguments = (ins CIR_CastKind:$kind, CIR_AnyType:$src);
+  let arguments = (ins CIR_CastKind:$kind, CIR_AnyType:$src,
+                       OptionalAttr<CIR_FenvAttr>:$fenv);
   let results = (outs CIR_AnyType:$result);
 
   let assemblyFormat = [{
     $kind $src `:` type($src) `->` type($result) attr-dict
   }];
 
+  let builders = [
+    OpBuilder<(ins "mlir::Type":$result, "cir::CastKind":$kind,
+                   "mlir::Value":$src), [{
+      build($_builder, $_state, result, kind, src, cir::FenvAttr{});
+    }]>
+  ];
+
   // The input and output types should match the cast kind.
   let hasVerifier = 1;
   let hasFolder = 1;
@@ -2783,21 +2820,6 @@ 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.
 //

diff  --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index b30172431cbfc..c54957a84768a 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -642,21 +642,44 @@ OpFoldResult cir::ConstantOp::fold(FoldAdaptor /*adaptor*/) {
 // CastOp
 //===----------------------------------------------------------------------===//
 
+static bool isFloatingPointCastKind(cir::CastKind kind) {
+  switch (kind) {
+  case cir::CastKind::floating:
+  case cir::CastKind::int_to_float:
+  case cir::CastKind::float_to_int:
+  case cir::CastKind::float_to_bool:
+  case cir::CastKind::bool_to_float:
+  case cir::CastKind::float_to_complex:
+  case cir::CastKind::float_complex_to_real:
+  case cir::CastKind::float_complex_to_bool:
+  case cir::CastKind::float_complex:
+  case cir::CastKind::float_complex_to_int_complex:
+  case cir::CastKind::int_complex_to_float_complex:
+    return true;
+  default:
+    return false;
+  }
+}
+
 LogicalResult cir::CastOp::verify() {
   mlir::Type resType = getType();
   mlir::Type srcType = getSrc().getType();
+  cir::CastKind kind = getKind();
+
+  if (getFenvAttr() && !isFloatingPointCastKind(kind))
+    return emitOpError()
+           << "'fenv' is only valid for floating-point cast kinds";
 
   // Verify address space casts for pointer types. given that
   // casts for within a 
diff erent address space are illegal.
   auto srcPtrTy = mlir::dyn_cast<cir::PointerType>(srcType);
   auto resPtrTy = mlir::dyn_cast<cir::PointerType>(resType);
-  if (srcPtrTy && resPtrTy && (getKind() != cir::CastKind::address_space))
+  if (srcPtrTy && resPtrTy && (kind != cir::CastKind::address_space))
     if (srcPtrTy.getAddrSpace() != resPtrTy.getAddrSpace()) {
       return emitOpError() << "result type address space does not match the "
                               "address space of the operand";
     }
 
-  cir::CastKind kind = getKind();
   auto srcVTy = mlir::dyn_cast<cir::VectorType>(srcType);
   auto resVTy = mlir::dyn_cast<cir::VectorType>(resType);
   if (srcVTy && resVTy) {
@@ -674,7 +697,7 @@ LogicalResult cir::CastOp::verify() {
     resType = resVTy.getElementType();
   }
 
-  switch (getKind()) {
+  switch (kind) {
   case cir::CastKind::int_to_bool: {
     if (!mlir::isa<cir::BoolType>(resType))
       return emitOpError() << "requires !cir.bool type for result";

diff  --git a/clang/test/CIR/IR/fenv.cir b/clang/test/CIR/IR/fenv.cir
index d6817484b4feb..6047ccf1af3c7 100644
--- a/clang/test/CIR/IR/fenv.cir
+++ b/clang/test/CIR/IR/fenv.cir
@@ -90,3 +90,17 @@ cir.func @ternary_fp_builtin_fenv(%a: !cir.float, %b: !cir.float, %c: !cir.float
   %1 = cir.fma %a, %b, %c : !cir.float
   cir.return
 }
+
+// CHECK-LABEL: cir.func @cast_fenv
+cir.func @cast_fenv(%a: !cir.float, %b: !cir.double, %i: !s32i) {
+  // CHECK: cir.cast floating %{{.*}} : !cir.double -> !cir.float {fenv = #cir.fenv<dynamic_rounding_mode = tonearest>}
+  %0 = cir.cast floating %b : !cir.double -> !cir.float {fenv = #cir.fenv<dynamic_rounding_mode = tonearest>}
+  // CHECK: cir.cast int_to_float %{{.*}} : !s32i -> !cir.float {fenv = #cir.fenv<>}
+  %1 = cir.cast int_to_float %i : !s32i -> !cir.float {fenv = #cir.fenv<>}
+  // CHECK: cir.cast float_to_int %{{.*}} : !cir.float -> !s32i {fenv = #cir.fenv<except_mode = unmasked, strict_except = true>}
+  %2 = cir.cast float_to_int %a : !cir.float -> !s32i {fenv = #cir.fenv<except_mode = unmasked, strict_except = true>}
+  // CHECK: cir.cast float_to_bool %{{.*}} : !cir.float -> !cir.bool
+  // CHECK-NOT: fenv
+  %3 = cir.cast float_to_bool %a : !cir.float -> !cir.bool
+  cir.return
+}

diff  --git a/clang/test/CIR/IR/invalid-cast.cir b/clang/test/CIR/IR/invalid-cast.cir
index cbe4354641497..4ba8025106a39 100644
--- a/clang/test/CIR/IR/invalid-cast.cir
+++ b/clang/test/CIR/IR/invalid-cast.cir
@@ -37,3 +37,63 @@ module {
     cir.return
   }
 }
+
+// -----
+
+!s32i = !cir.int<s, 32>
+
+module {
+  cir.func @fenv_on_integral(%i: !s32i) {
+    // expected-error at +1 {{'fenv' is only valid for floating-point cast kinds}}
+    %0 = cir.cast integral %i : !s32i -> !s32i {fenv = #cir.fenv<>}
+    cir.return
+  }
+}
+
+// -----
+
+!s32i = !cir.int<s, 32>
+
+module {
+  cir.func @fenv_on_int_to_bool(%i: !s32i) {
+    // expected-error at +1 {{'fenv' is only valid for floating-point cast kinds}}
+    %0 = cir.cast int_to_bool %i : !s32i -> !cir.bool {fenv = #cir.fenv<>}
+    cir.return
+  }
+}
+
+// -----
+
+!s32i = !cir.int<s, 32>
+
+module {
+  cir.func @fenv_on_ptr_to_int(%p: !cir.ptr<!s32i>) {
+    // expected-error at +1 {{'fenv' is only valid for floating-point cast kinds}}
+    %0 = cir.cast ptr_to_int %p : !cir.ptr<!s32i> -> !s32i {fenv = #cir.fenv<>}
+    cir.return
+  }
+}
+
+// -----
+
+!s32i = !cir.int<s, 32>
+
+module {
+  cir.func @fenv_on_bitcast(%p: !cir.ptr<!s32i>) {
+    // expected-error at +1 {{'fenv' is only valid for floating-point cast kinds}}
+    %0 = cir.cast bitcast %p : !cir.ptr<!s32i> -> !cir.ptr<!cir.float> {fenv = #cir.fenv<>}
+    cir.return
+  }
+}
+
+// -----
+
+!s32i = !cir.int<s, 32>
+
+module {
+  cir.func @fenv_on_int_complex(%c: !cir.complex<!s32i>) {
+    // expected-error at +1 {{'fenv' is only valid for floating-point cast kinds}}
+    %0 = cir.cast int_complex %c : !cir.complex<!s32i> -> !cir.complex<!s32i> {fenv = #cir.fenv<>}
+    cir.return
+  }
+}

diff  --git a/clang/unittests/CIR/FenvOpTest.cpp b/clang/unittests/CIR/FenvOpTest.cpp
index 2588203971c7c..293c94563cbf7 100644
--- a/clang/unittests/CIR/FenvOpTest.cpp
+++ b/clang/unittests/CIR/FenvOpTest.cpp
@@ -73,6 +73,8 @@ TEST_F(CIRFenvOpTest, MemoryEffects) {
       %5 = cir.fma %a, %b, %c : !cir.float {fenv = #cir.fenv<>}
       %6 = cir.lround %a : !cir.float -> !s32i
       %7 = cir.lround %a : !cir.float -> !s32i {fenv = #cir.fenv<>}
+      %8 = cir.cast floating %a : !cir.float -> !cir.double
+      %9 = cir.cast floating %a : !cir.float -> !cir.double {fenv = #cir.fenv<>}
       cir.return
     }
   )CIR");
@@ -106,6 +108,13 @@ TEST_F(CIRFenvOpTest, MemoryEffects) {
   EXPECT_TRUE(isMemoryEffectFree(lroundOps[0]));
   expectFenvReadAndWrite(lroundOps[1]);
   EXPECT_FALSE(isMemoryEffectFree(lroundOps[1]));
+
+  SmallVector<cir::CastOp> castOps = findOps<cir::CastOp>(*module);
+  ASSERT_EQ(castOps.size(), 2u);
+  EXPECT_TRUE(getEffects(castOps[0]).empty());
+  EXPECT_TRUE(isMemoryEffectFree(castOps[0]));
+  expectFenvReadAndWrite(castOps[1]);
+  EXPECT_FALSE(isMemoryEffectFree(castOps[1]));
 }
 
 TEST_F(CIRFenvOpTest, Speculatability) {


        


More information about the cfe-commits mailing list