[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