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@+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@+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@+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@+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@+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) { _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
