https://github.com/xlauko updated https://github.com/llvm/llvm-project/pull/236704
>From 6e17cb3b41d9660d404524ce17478b9e82b424fd Mon Sep 17 00:00:00 2001 From: Henrich Lauko <[email protected]> Date: Thu, 29 Oct 2026 09:55:43 +0000 Subject: [PATCH 1/2] [CIR] Default the fenv attribute and elide it when empty Floating-point operations carried `fenv` as an optional attribute, so an attached `#cir.fenv<>` that set no field still printed as `fenv<>` and lowered to a constrained intrinsic. Make `fenv` a default-valued attribute whose default is the empty `#cir.fenv<>`: it does not constrain the operation and is not printed. Codegen always sets every field of the fenv it attaches, so its output is unchanged. Verifiers, folding, memory effects and lowering now use the new `getConstrainedFenv()` interface method, which returns null for the empty attribute. The custom builders that only passed an empty fenv are dropped; the generated ones default it. Assisted-by: Claude Code (Opus 5.5). --- .../include/clang/CIR/Dialect/IR/CIRAttrs.td | 25 +++++++ .../include/clang/CIR/Dialect/IR/CIRDialect.h | 2 +- clang/include/clang/CIR/Dialect/IR/CIROps.td | 67 ++++++------------- .../clang/CIR/Interfaces/CIROpInterfaces.td | 27 ++++---- clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 10 +-- .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 18 ++--- .../CIR/Analysis/alias-analysis-modref.cir | 4 +- clang/test/CIR/IR/fenv.cir | 21 +++--- clang/test/CIR/IR/invalid-cast.cir | 10 +-- clang/test/CIR/IR/invalid-cmp.cir | 6 +- clang/test/CIR/Lowering/fenv.cir | 8 ++- clang/utils/TableGen/CIRLoweringEmitter.cpp | 8 +-- 12 files changed, 106 insertions(+), 100 deletions(-) diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td index e04ac4bd9b8b84..9d2ec98042ce28 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td @@ -908,6 +908,10 @@ def CIR_FenvAttr : CIR_Attr<"Fenv", "fenv"> { `except_mode` defaults to `masked`, and an absent `strict_except` defaults to `false`. + The empty `#cir.fenv<>`, with no parameter set, does not constrain the + operation that carries it. It is the default value of the `fenv` + attribute of floating-point operations, which omit it when printing. + Example: ``` #cir.fenv<dynamic_rounding_mode = tonearest, except_mode = unknown, @@ -925,7 +929,20 @@ def CIR_FenvAttr : CIR_Attr<"Fenv", "fenv"> { `<` struct($dynamic_rounding_mode, $except_mode, $strict_except) `>` }]; + let builders = [ + AttrBuilder<(ins), [{ + return $_get($_ctxt, std::nullopt, std::nullopt, mlir::BoolAttr{}); + }]> + ]; + let extraClassDeclaration = [{ + /// Whether this is the empty attribute, which does not constrain the + /// operation that carries it. + bool isEmpty() const { + return !getDynamicRoundingMode() && !getExceptMode() && + !getStrictExcept(); + } + static constexpr cir::FPDynamicRoundingMode getDefaultDynamicRoundingMode() { return cir::FPDynamicRoundingMode::Unknown; @@ -950,9 +967,17 @@ def CIR_FenvAttr : CIR_Attr<"Fenv", "fenv"> { } }]; + // Used by `CIR_DefaultValuedFenvAttr`, whose default value is a full builder + // expression. + let constBuilderCall = "$0"; + let canHaveIllegalCXXABIType = 0; } +def CIR_DefaultValuedFenvAttr + : DefaultValuedAttr<CIR_FenvAttr, + "cir::FenvAttr::get($_builder.getContext())">; + //===----------------------------------------------------------------------===// // CIR_FlagParam //===----------------------------------------------------------------------===// diff --git a/clang/include/clang/CIR/Dialect/IR/CIRDialect.h b/clang/include/clang/CIR/Dialect/IR/CIRDialect.h index d9018e93628b69..6f695e6403e49c 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRDialect.h +++ b/clang/include/clang/CIR/Dialect/IR/CIRDialect.h @@ -67,7 +67,7 @@ class FenvOpTrait : public mlir::OpTrait::TraitBase<ConcreteType, FenvOpTrait> { void getEffects( llvm::SmallVectorImpl<mlir::MemoryEffects::EffectInstance> &effects) { - if (!getFenvOp().getFenvAttr()) + if (!getFenvOp().getConstrainedFenv()) return; effects.emplace_back(mlir::MemoryEffects::Read::get(), FloatingPointEnvironmentResource::get()); diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index bd762aae3e1d7e..fec2b11b8ffb68 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -75,10 +75,11 @@ include "mlir/Interfaces/SideEffectInterfaces.td" // `llvmOp` field from your CIR operation definition. // // The `constrainedLLVMIntrinsic` field enables a specialized variant of this -// generated lowering for floating-point operations that carry an optional -// `fenv` attribute. When it is set and the operation has an `fenv` attribute -// the operation is lowering to a call to the corresponding constrained fp -// intrinsic. If `constrainedLLVMIntrinsic` is set `llvmOp` must be set as well. +// generated lowering for floating-point operations that carry an `fenv` +// attribute. When it is set and the operation's `fenv` attribute is not the +// empty `#cir.fenv<>`, the operation is lowered to a call to the corresponding +// constrained fp intrinsic. If `constrainedLLVMIntrinsic` is set `llvmOp` must +// be set as well. // // `constrainedLLVMIntrinsicHasRoundingMode` indicates whether the constrained // intrinsic takes a rounding-mode metadata argument in addition to the @@ -344,12 +345,13 @@ def CIR_CastOp : CIR_Op<"cast", !listconcat([ ... %x = cir.cast array_to_ptrdecay %0 : !cir.ptr<!cir.array<!s32i x 10>> -> !cir.ptr<!s32i> - %y = cir.cast floating %f : !cir.double -> !cir.float fenv<> + %y = cir.cast floating %f : !cir.double -> !cir.float + fenv<except_mode = unknown, strict_except = true> ``` }]; let arguments = (ins CIR_CastKindAttr:$kind, CIR_AnyType:$src, - OptionalAttr<CIR_FenvAttr>:$fenv); + CIR_DefaultValuedFenvAttr:$fenv); let results = (outs CIR_AnyType:$result); let assemblyFormat = [{ @@ -2677,7 +2679,8 @@ def CIR_CmpOp : CIR_Op<"cmp", ``` %0 = cir.cmp gt %1, %2 : !s32i %1 = cir.cmp eq %a, %b : !cir.ptr<!u8i> - %2 = cir.cmp lt %x, %y : !cir.float fenv<> + %2 = cir.cmp lt %x, %y : !cir.float + fenv<except_mode = unknown, strict_except = true> ``` }]; @@ -2685,7 +2688,7 @@ def CIR_CmpOp : CIR_Op<"cmp", CIR_CmpOpKindAttr:$kind, CIR_ComparableType:$lhs, CIR_ComparableType:$rhs, - OptionalAttr<CIR_FenvAttr>:$fenv + CIR_DefaultValuedFenvAttr:$fenv ); let results = (outs CIR_BoolType:$result); @@ -3014,7 +3017,7 @@ class CIR_FPBinaryOp<string mnemonic, list<Trait> traits = [], !listconcat(CIR_FenvOpTraits, traits), CIR_DynamicMemoryEffects> { let arguments = !con(commonArgs, (ins - OptionalAttr<CIR_FenvAttr>:$fenv, + CIR_DefaultValuedFenvAttr:$fenv, OptionalAttr<CIR_FastMathFlagsAttr>:$fastmath_flags)); let assemblyFormat = [{ $lhs `,` $rhs `:` type($lhs) (`fenv` `` $fenv^)? prop-dict attr-dict @@ -3022,12 +3025,6 @@ class CIR_FPBinaryOp<string mnemonic, list<Trait> traits = [], let constrainedLLVMIntrinsic = mnemonic; - let builders = [ - OpBuilder<(ins "mlir::Value":$lhs, "mlir::Value":$rhs), [{ - build($_builder, $_state, lhs, rhs, cir::FenvAttr{}, - cir::FastMathFlagsAttr{}); - }]> - ]; } //===----------------------------------------------------------------------===// @@ -6545,7 +6542,8 @@ def CIR_VecCmpOp : CIR_Op<"vec.cmp", ``` %eq = cir.vec.cmp(eq, %vec_a, %vec_b) : !cir.vector<4 x !s32i>, !cir.vector<4 x !s32i> %lt = cir.vec.cmp(lt, %vec_a, %vec_b) : !cir.vector<4 x !s32i>, !cir.vector<4 x !s32i> - %gt = cir.vec.cmp(gt, %va, %vb) : !cir.vector<4 x !cir.float>, !cir.vector<4 x !s32i> fenv<> + %gt = cir.vec.cmp(gt, %va, %vb) : !cir.vector<4 x !cir.float>, !cir.vector<4 x !s32i> + fenv<except_mode = unknown, strict_except = true> ``` }]; @@ -6553,7 +6551,7 @@ def CIR_VecCmpOp : CIR_Op<"vec.cmp", CIR_CmpOpKindAttr:$kind, CIR_VectorType:$lhs, CIR_VectorType:$rhs, - OptionalAttr<CIR_FenvAttr>:$fenv + CIR_DefaultValuedFenvAttr:$fenv ); let results = (outs CIR_VectorType:$result); @@ -7446,7 +7444,7 @@ class CIR_ComplexFPBinOp<string mnemonic> let arguments = (ins CIR_ComplexOfFloatType:$lhs, CIR_ComplexOfFloatType:$rhs, - OptionalAttr<CIR_FenvAttr>:$fenv + CIR_DefaultValuedFenvAttr:$fenv ); let results = (outs CIR_ComplexOfFloatType:$result); @@ -8266,17 +8264,11 @@ class CIR_UnaryFPToFPBuiltinOp<string mnemonic, string llvmOpName> !listconcat([SameOperandsAndResultType], CIR_FenvOpTraits)> { let arguments = (ins CIR_AnyFloatOrVecOfFloatType:$src, - OptionalAttr<CIR_FenvAttr>:$fenv); + CIR_DefaultValuedFenvAttr:$fenv); let results = (outs CIR_AnyFloatOrVecOfFloatType:$result); let assemblyFormat = "$src `:` type($src) (`fenv` `` $fenv^)? attr-dict"; - let builders = [ - OpBuilder<(ins "mlir::Value":$src), [{ - build($_builder, $_state, src, cir::FenvAttr{}); - }]> - ]; - let llvmOp = llvmOpName; let constrainedLLVMIntrinsic = mnemonic; } @@ -8588,7 +8580,7 @@ class CIR_UnaryFPToIntBuiltinOp<string mnemonic, string llvmOpName> : CIR_Op<mnemonic, CIR_FenvOpTraits> { let arguments = (ins CIR_AnyFloatType:$src, - OptionalAttr<CIR_FenvAttr>:$fenv); + CIR_DefaultValuedFenvAttr:$fenv); let results = (outs CIR_IntType:$result); let summary = [{ @@ -8601,12 +8593,6 @@ class CIR_UnaryFPToIntBuiltinOp<string mnemonic, string llvmOpName> (`fenv` `` $fenv^)? attr-dict }]; - let builders = [ - OpBuilder<(ins "mlir::Type":$result, "mlir::Value":$src), [{ - build($_builder, $_state, result, src, cir::FenvAttr{}); - }]> - ]; - let llvmOp = llvmOpName; let constrainedLLVMIntrinsic = mnemonic; } @@ -8658,7 +8644,7 @@ class CIR_BinaryFPToFPBuiltinOp<string mnemonic, string llvmOpName> let arguments = (ins CIR_AnyFloatOrVecOfFloatType:$lhs, CIR_AnyFloatOrVecOfFloatType:$rhs, - OptionalAttr<CIR_FenvAttr>:$fenv + CIR_DefaultValuedFenvAttr:$fenv ); let results = (outs CIR_AnyFloatOrVecOfFloatType:$result); @@ -8668,12 +8654,6 @@ class CIR_BinaryFPToFPBuiltinOp<string mnemonic, string llvmOpName> (`fenv` `` $fenv^)? attr-dict }]; - let builders = [ - OpBuilder<(ins "mlir::Value":$lhs, "mlir::Value":$rhs), [{ - build($_builder, $_state, lhs, rhs, cir::FenvAttr{}); - }]> - ]; - let llvmOp = llvmOpName; } @@ -8810,7 +8790,7 @@ class CIR_TernaryFPToFPBuiltinOp<string mnemonic, string llvmOpName> CIR_AnyFloatOrVecOfFloatType:$a, CIR_AnyFloatOrVecOfFloatType:$b, CIR_AnyFloatOrVecOfFloatType:$c, - OptionalAttr<CIR_FenvAttr>:$fenv + CIR_DefaultValuedFenvAttr:$fenv ); let results = (outs CIR_AnyFloatOrVecOfFloatType:$result); @@ -8819,13 +8799,6 @@ class CIR_TernaryFPToFPBuiltinOp<string mnemonic, string llvmOpName> $a `,` $b `,` $c `:` type($a) (`fenv` `` $fenv^)? attr-dict }]; - let builders = [ - OpBuilder<(ins "mlir::Type":$result, "mlir::Value":$a, "mlir::Value":$b, - "mlir::Value":$c), [{ - build($_builder, $_state, result, a, b, c, cir::FenvAttr{}); - }]> - ]; - let llvmOp = llvmOpName; let constrainedLLVMIntrinsic = mnemonic; } diff --git a/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td b/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td index 1563fc5aa00da3..12aca794cc4b50 100644 --- a/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td +++ b/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td @@ -267,40 +267,39 @@ 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. + Interface for floating-point operations that carry a `#cir.fenv` + attribute. The attribute defaults to the empty `#cir.fenv<>`, which does + not constrain the operation. The interface 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), [{}], + "Return the `#cir.fenv` attribute if it constrains the operation, or " + "a null attribute if it is the empty `#cir.fenv<>`.", + "cir::FenvAttr", "getConstrainedFenv", (ins), [{}], /*defaultImplementation=*/[{ - return $_op.getFenvAttr(); + cir::FenvAttr fenv = $_op.getFenv(); + return fenv.isEmpty() ? cir::FenvAttr{} : fenv; }]>, InterfaceMethod< "Return the effective dynamic rounding mode.", "cir::FPDynamicRoundingMode", "getFenvDynamicRoundingMode", (ins), [{}], /*defaultImplementation=*/[{ - cir::FenvAttr fenv = $_op.getFenvAttr(); - return fenv ? fenv.getDynamicRoundingModeOrDefault() - : cir::FenvAttr::getDefaultDynamicRoundingMode(); + return $_op.getFenv().getDynamicRoundingModeOrDefault(); }]>, 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(); + return $_op.getFenv().getExceptionModeOrDefault(); }]>, InterfaceMethod< "Return the effective strict-exception flag.", "bool", "getFenvStrictExcept", (ins), [{}], /*defaultImplementation=*/[{ - cir::FenvAttr fenv = $_op.getFenvAttr(); - return fenv ? fenv.getStrictExceptOrDefault() : false; + return $_op.getFenv().getStrictExceptOrDefault(); }]>, ]; } diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index 15f5ccaef130b7..1b120e9442f2aa 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -953,7 +953,7 @@ LogicalResult cir::CastOp::verify() { mlir::Type srcType = getSrc().getType(); cir::CastKind kind = getKind(); - if (getFenvAttr() && !isFloatingPointCastKind(kind)) + if (getConstrainedFenv() && !isFloatingPointCastKind(kind)) return emitOpError() << "'fenv' is only valid for floating-point cast kinds"; @@ -4267,7 +4267,7 @@ OpFoldResult cir::VecExtractOp::fold(FoldAdaptor adaptor) { //===----------------------------------------------------------------------===// LogicalResult cir::CmpOp::verify() { - if (getFenvAttr() && !cir::isAnyFloatingPointType(getLhs().getType())) + if (getConstrainedFenv() && !cir::isAnyFloatingPointType(getLhs().getType())) return emitOpError() << "'fenv' is only valid for floating-point comparisons"; return success(); @@ -4278,19 +4278,19 @@ LogicalResult cir::CmpOp::verify() { //===----------------------------------------------------------------------===// LogicalResult cir::VecCmpOp::verify() { - if (getFenvAttr() && !cir::isFPOrVectorOfFPType(getLhs().getType())) + if (getConstrainedFenv() && !cir::isFPOrVectorOfFPType(getLhs().getType())) return emitOpError() << "'fenv' is only valid for floating-point comparisons"; return success(); } OpFoldResult cir::VecCmpOp::fold(FoldAdaptor adaptor) { - // Do not fold when fenv is present. + // Do not fold when the floating-point environment is constrained. // TODO(cir): This is overly conservative. We could fold comparisons as long // as we can prove that the operation wouldn't raise exceptions or // when the fenv attribute does not require strict exception // semantics. - if (getFenvAttr()) + if (getConstrainedFenv()) return {}; auto lhsVecAttr = diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index 39ecb6fa7c746c..1b0a66dad40f25 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -1991,7 +1991,7 @@ mlir::LogicalResult CIRToLLVMCastOpLowering::matchAndRewrite( }; bool isTrunc = getFloatWidth(srcTy) > getFloatWidth(dstTy); - if (cir::FenvAttr fenv = castOp.getFenvAttr()) { + if (cir::FenvAttr fenv = castOp.getConstrainedFenv()) { // fptrunc takes rounding mode + exception behavior; fpext takes only // exception behavior. return lowerToConstrainedFPIntrinsic( @@ -2072,7 +2072,7 @@ mlir::LogicalResult CIRToLLVMCastOpLowering::matchAndRewrite( bool isSigned = mlir::cast<cir::IntType>( elementTypeIfVectorOrMatrix(castOp.getSrc().getType())) .isSigned(); - if (cir::FenvAttr fenv = castOp.getFenvAttr()) { + if (cir::FenvAttr fenv = castOp.getConstrainedFenv()) { return lowerToConstrainedFPIntrinsic( castOp, llvmSrcVal, fenv, llvmDstTy, rewriter, isSigned ? "sitofp" : "uitofp", /*hasRoundingMode=*/true); @@ -2092,7 +2092,7 @@ mlir::LogicalResult CIRToLLVMCastOpLowering::matchAndRewrite( bool isSigned = mlir::cast<cir::IntType>(elementTypeIfVectorOrMatrix(castOp.getType())) .isSigned(); - if (cir::FenvAttr fenv = castOp.getFenvAttr()) { + if (cir::FenvAttr fenv = castOp.getConstrainedFenv()) { return lowerToConstrainedFPIntrinsic( castOp, llvmSrcVal, fenv, llvmDstTy, rewriter, isSigned ? "fptosi" : "fptoui", /*hasRoundingMode=*/false); @@ -2370,7 +2370,7 @@ mlir::LogicalResult CIRToLLVMFMaxNumOpLowering::matchAndRewrite( cir::FMaxNumOp op, OpAdaptor adaptor, mlir::ConversionPatternRewriter &rewriter) const { mlir::Type resTy = typeConverter->convertType(op.getType()); - if (cir::FenvAttr fenv = op.getFenvAttr()) + if (cir::FenvAttr fenv = op.getConstrainedFenv()) return lowerToConstrainedFPIntrinsic( op, adaptor.getOperands(), fenv, resTy, rewriter, "maxnum", /*hasRoundingMode=*/false, mlir::LLVM::FastmathFlags::nsz); @@ -2384,7 +2384,7 @@ mlir::LogicalResult CIRToLLVMFMinNumOpLowering::matchAndRewrite( cir::FMinNumOp op, OpAdaptor adaptor, mlir::ConversionPatternRewriter &rewriter) const { mlir::Type resTy = typeConverter->convertType(op.getType()); - if (cir::FenvAttr fenv = op.getFenvAttr()) + if (cir::FenvAttr fenv = op.getConstrainedFenv()) return lowerToConstrainedFPIntrinsic( op, adaptor.getOperands(), fenv, resTy, rewriter, "minnum", /*hasRoundingMode=*/false, mlir::LLVM::FastmathFlags::nsz); @@ -4019,7 +4019,7 @@ mlir::LogicalResult CIRToLLVMCmpOpLowering::matchAndRewrite( if (mlir::isa<cir::FPTypeInterface>(type)) { mlir::Type llvmResTy = getTypeConverter()->convertType(cmpOp.getType()); - if (cir::FenvAttr fenv = cmpOp.getFenvAttr()) { + if (cir::FenvAttr fenv = cmpOp.getConstrainedFenv()) { mlir::LLVM::CallIntrinsicOp call = createConstrainedFCmpCall( rewriter, cmpOp.getLoc(), adaptor.getLhs(), adaptor.getRhs(), cmpOp.getKind(), fenv, llvmResTy); @@ -5521,7 +5521,7 @@ mlir::LogicalResult CIRToLLVMVecCmpOpLowering::matchAndRewrite( convertCmpKindToICmpPredicate(op.getKind(), /*isSigned=*/false), adaptor.getLhs(), adaptor.getRhs()); } else if (mlir::isa<cir::FPTypeInterface>(elementType)) { - if (cir::FenvAttr fenv = op.getFenvAttr()) { + if (cir::FenvAttr fenv = op.getConstrainedFenv()) { llvm::ElementCount size = op.getLhs().getType().getSize(); auto i1VecTy = mlir::VectorType::get( size.getKnownMinValue(), rewriter.getI1Type(), {size.isScalable()}); @@ -5979,7 +5979,7 @@ mlir::LogicalResult CIRToLLVMComplexFAddOpLowering::matchAndRewrite( assert(!cir::MissingFeatures::fastMathFlags()); mlir::Value newReal; mlir::Value newImag; - if (cir::FenvAttr fenv = op.getFenvAttr()) { + if (cir::FenvAttr fenv = op.getFenv(); !fenv.isEmpty()) { newReal = createConstrainedFPIntrinsicCall( rewriter, loc, {lhsReal, rhsReal}, complexElemTy, fenv, /*constrainedMnemonic=*/"fadd", @@ -6032,7 +6032,7 @@ mlir::LogicalResult CIRToLLVMComplexFSubOpLowering::matchAndRewrite( assert(!cir::MissingFeatures::fastMathFlags()); mlir::Value newReal; mlir::Value newImag; - if (cir::FenvAttr fenv = op.getFenvAttr()) { + if (cir::FenvAttr fenv = op.getFenv(); !fenv.isEmpty()) { newReal = createConstrainedFPIntrinsicCall( rewriter, loc, {lhsReal, rhsReal}, complexElemTy, fenv, /*constrainedMnemonic=*/"fsub", diff --git a/clang/test/CIR/Analysis/alias-analysis-modref.cir b/clang/test/CIR/Analysis/alias-analysis-modref.cir index 781d3bd3baa60f..015fb1ee1600d9 100644 --- a/clang/test/CIR/Analysis/alias-analysis-modref.cir +++ b/clang/test/CIR/Analysis/alias-analysis-modref.cir @@ -39,7 +39,7 @@ cir.func @modref_load() { // ----- -// A cir.fadd with an `fenv` attribute has Read/Write effects on the +// A cir.fadd with a non-empty `fenv` attribute has Read/Write effects on the // non-addressable FloatingPointEnvironment resource and no associated // Value. Such effects can never alias a pointer-based location, so the // result is NoModRef regardless of the queried location. @@ -51,6 +51,6 @@ cir.func @modref_load() { !s32i = !cir.int<s, 32> cir.func @modref_fenv(%a: !cir.float, %b: !cir.float) { %x = cir.alloca "x" align(4) : !cir.ptr<!s32i> {test.ptr = "x"} - %0 = cir.fadd %a, %b : !cir.float fenv<> {test.ptr = "fadd_op"} + %0 = cir.fadd %a, %b : !cir.float fenv<strict_except = true> {test.ptr = "fadd_op"} cir.return } diff --git a/clang/test/CIR/IR/fenv.cir b/clang/test/CIR/IR/fenv.cir index 2c938c2c74c70b..4ca91d243a621b 100644 --- a/clang/test/CIR/IR/fenv.cir +++ b/clang/test/CIR/IR/fenv.cir @@ -13,9 +13,14 @@ cir.func @fadd_fenv(%a: !cir.float, %b: !cir.float) { // CHECK-NOT: fenv %1 = cir.fadd %a, %b : !cir.float - // Empty fenv attribute. - // CHECK: cir.fadd %{{.*}}, %{{.*}} : !cir.float fenv<> + // The empty fenv attribute is the default and is not printed. + // CHECK: cir.fadd %{{.*}}, %{{.*}} : !cir.float + // CHECK-NOT: fenv %2 = cir.fadd %a, %b : !cir.float fenv<> + + // Fields set to their default value are kept. + // CHECK: cir.fadd %{{.*}}, %{{.*}} : !cir.float fenv<dynamic_rounding_mode = unknown, except_mode = masked, strict_except = false> + %3 = cir.fadd %a, %b : !cir.float fenv<dynamic_rounding_mode = unknown, except_mode = masked, strict_except = false> cir.return } @@ -73,8 +78,8 @@ cir.func @unary_fp_to_int_builtin_fenv(%a: !cir.float) -> !s32i { %0 = cir.lround %a : !cir.float -> !s32i fenv<dynamic_rounding_mode = tonearest> // CHECK: cir.llround %{{.*}} : !cir.float -> !s32i fenv<except_mode = unmasked> %1 = cir.llround %a : !cir.float -> !s32i fenv<except_mode = unmasked> - // CHECK: cir.lrint %{{.*}} : !cir.float -> !s32i fenv<> - %2 = cir.lrint %a : !cir.float -> !s32i fenv<> + // CHECK: cir.lrint %{{.*}} : !cir.float -> !s32i fenv<except_mode = unknown> + %2 = cir.lrint %a : !cir.float -> !s32i fenv<except_mode = unknown> // CHECK: cir.llrint %{{.*}} : !cir.float -> !s32i // CHECK-NOT: fenv %3 = cir.llrint %a : !cir.float -> !s32i @@ -110,8 +115,8 @@ cir.func @ternary_fp_builtin_fenv(%a: !cir.float, %b: !cir.float, %c: !cir.float cir.func @cast_fenv(%a: !cir.float, %b: !cir.double, %i: !s32i) { // CHECK: cir.cast floating %{{.*}} : !cir.double -> !cir.float fenv<dynamic_rounding_mode = tonearest> %0 = cir.cast floating %b : !cir.double -> !cir.float fenv<dynamic_rounding_mode = tonearest> - // CHECK: cir.cast int_to_float %{{.*}} : !s32i -> !cir.float fenv<> - %1 = cir.cast int_to_float %i : !s32i -> !cir.float fenv<> + // CHECK: cir.cast int_to_float %{{.*}} : !s32i -> !cir.float fenv<except_mode = unknown> + %1 = cir.cast int_to_float %i : !s32i -> !cir.float fenv<except_mode = unknown> // CHECK: cir.cast float_to_int %{{.*}} : !cir.float -> !s32i fenv<except_mode = unmasked, strict_except = true> %2 = cir.cast float_to_int %a : !cir.float -> !s32i fenv<except_mode = unmasked, strict_except = true> // CHECK: cir.cast float_to_bool %{{.*}} : !cir.float -> !cir.bool @@ -126,8 +131,8 @@ cir.func @cmp_fenv(%a: !cir.float, %b: !cir.float, %vb: !cir.vector<4 x !cir.float>) { // CHECK: cir.cmp lt %{{.*}}, %{{.*}} : !cir.float fenv<except_mode = unmasked> %0 = cir.cmp lt %a, %b : !cir.float fenv<except_mode = unmasked> - // CHECK: cir.cmp eq %{{.*}}, %{{.*}} : !cir.float fenv<> - %1 = cir.cmp eq %a, %b : !cir.float fenv<> + // CHECK: cir.cmp eq %{{.*}}, %{{.*}} : !cir.float fenv<except_mode = unknown> + %1 = cir.cmp eq %a, %b : !cir.float fenv<except_mode = unknown> // CHECK: cir.vec.cmp(gt, %{{.*}}, %{{.*}}) : !cir.vector<4 x !cir.float>, !cir.vector<4 x !s32i> fenv<strict_except = true> %2 = cir.vec.cmp(gt, %va, %vb) : !cir.vector<4 x !cir.float>, !cir.vector<4 x !s32i> fenv<strict_except = true> diff --git a/clang/test/CIR/IR/invalid-cast.cir b/clang/test/CIR/IR/invalid-cast.cir index 67e3dbe68306e6..62cd36e30dcea0 100644 --- a/clang/test/CIR/IR/invalid-cast.cir +++ b/clang/test/CIR/IR/invalid-cast.cir @@ -45,7 +45,7 @@ module { 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<> + %0 = cir.cast integral %i : !s32i -> !s32i fenv<strict_except = true> cir.return } } @@ -57,7 +57,7 @@ module { 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<> + %0 = cir.cast int_to_bool %i : !s32i -> !cir.bool fenv<strict_except = true> cir.return } } @@ -69,7 +69,7 @@ module { 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<> + %0 = cir.cast ptr_to_int %p : !cir.ptr<!s32i> -> !s32i fenv<strict_except = true> cir.return } } @@ -81,7 +81,7 @@ module { 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<> + %0 = cir.cast bitcast %p : !cir.ptr<!s32i> -> !cir.ptr<!cir.float> fenv<strict_except = true> cir.return } } @@ -93,7 +93,7 @@ module { 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<> + %0 = cir.cast int_complex %c : !cir.complex<!s32i> -> !cir.complex<!s32i> fenv<strict_except = true> cir.return } } diff --git a/clang/test/CIR/IR/invalid-cmp.cir b/clang/test/CIR/IR/invalid-cmp.cir index 25e09700c2d72f..337becafc1b525 100644 --- a/clang/test/CIR/IR/invalid-cmp.cir +++ b/clang/test/CIR/IR/invalid-cmp.cir @@ -5,7 +5,7 @@ module { cir.func @fenv_on_int_cmp(%a: !s32i, %b: !s32i) { // expected-error@+1 {{'fenv' is only valid for floating-point comparisons}} - %0 = cir.cmp eq %a, %b : !s32i fenv<> + %0 = cir.cmp eq %a, %b : !s32i fenv<strict_except = true> cir.return } } @@ -17,7 +17,7 @@ module { module { cir.func @fenv_on_ptr_cmp(%a: !cir.ptr<!s32i>, %b: !cir.ptr<!s32i>) { // expected-error@+1 {{'fenv' is only valid for floating-point comparisons}} - %0 = cir.cmp ne %a, %b : !cir.ptr<!s32i> fenv<> + %0 = cir.cmp ne %a, %b : !cir.ptr<!s32i> fenv<strict_except = true> cir.return } } @@ -31,7 +31,7 @@ module { %b: !cir.vector<4 x !s32i>) { // expected-error@+1 {{'fenv' is only valid for floating-point comparisons}} %0 = cir.vec.cmp(eq, %a, %b) : !cir.vector<4 x !s32i>, - !cir.vector<4 x !s32i> fenv<> + !cir.vector<4 x !s32i> fenv<strict_except = true> cir.return } } diff --git a/clang/test/CIR/Lowering/fenv.cir b/clang/test/CIR/Lowering/fenv.cir index 27f3455c16ea37..e49daf6dda4924 100644 --- a/clang/test/CIR/Lowering/fenv.cir +++ b/clang/test/CIR/Lowering/fenv.cir @@ -14,10 +14,14 @@ module { // CHECK: llvm.fadd %{{.*}}, %{{.*}} : f32 %1 = cir.fadd %a, %b : !cir.float + // The empty fenv attribute does not constrain the operation. + // CHECK: llvm.fadd %{{.*}}, %{{.*}} : f32 + %2 = cir.fadd %a, %b : !cir.float fenv<> + // CHECK: %[[RM2:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"round.tonearest"> // CHECK: %[[EB2:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.ignore"> // CHECK: llvm.call_intrinsic "llvm.experimental.constrained.fadd"(%{{.*}}, %{{.*}}, %[[RM2]], %[[EB2]]) : (f32, f32, !llvm.metadata, !llvm.metadata) -> f32 - %2 = cir.fadd %a, %b : !cir.float fenv<> + %3 = cir.fadd %a, %b : !cir.float fenv<dynamic_rounding_mode = tonearest, except_mode = masked, strict_except = false> cir.return } @@ -255,7 +259,7 @@ module { // CHECK: llvm.fmul %{{.*}}, %{{.*}} fastmath<contract> : f32 %0 = cir.fmul %a, %b : !cir.float <fastmath_flags = [contract]> // CHECK: llvm.call_intrinsic "llvm.experimental.constrained.fadd"(%{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}) {fastmathFlags = #llvm.fastmath<contract>} : (f32, f32, !llvm.metadata, !llvm.metadata) -> f32 - %1 = cir.fadd %0, %b : !cir.float fenv<> <fastmath_flags = [contract]> + %1 = cir.fadd %0, %b : !cir.float fenv<except_mode = masked> <fastmath_flags = [contract]> cir.return } } diff --git a/clang/utils/TableGen/CIRLoweringEmitter.cpp b/clang/utils/TableGen/CIRLoweringEmitter.cpp index 46a8dd8bd6ba0d..00ee5f12d9942b 100644 --- a/clang/utils/TableGen/CIRLoweringEmitter.cpp +++ b/clang/utils/TableGen/CIRLoweringEmitter.cpp @@ -203,9 +203,9 @@ void GenerateLLVMLoweringPattern(llvm::StringRef OpName, if (!ConstrainedLLVMIntrinsic.empty()) { // Generate a matchAndRewrite body for a floating-point operation that - // carries an optional `fenv` attribute. The shared `lowerConstrainableFPOp` - // helper lowers to the plain `llvmOp` when no `fenv` attribute is present - // and to a call to the constrained floating-point intrinsic when one is. + // carries an `fenv` attribute. The shared `lowerConstrainableFPOp` helper + // lowers to the plain `llvmOp` when `fenv` is the empty `#cir.fenv<>` and + // to a call to the constrained floating-point intrinsic otherwise. assert(!LLVMOp.empty() && "constrainedLLVMIntrinsic requires llvmOp"); Code << " mlir::LogicalResult matchAndRewrite(cir::" << OpName @@ -213,7 +213,7 @@ void GenerateLLVMLoweringPattern(llvm::StringRef OpName, "const override {\n"; Code << " return lowerConstrainableFPOp<mlir::LLVM::" << LLVMOp << ">(\n"; - Code << " op, adaptor.getOperands(), op.getFenvAttr(),\n"; + Code << " op, adaptor.getOperands(), op.getConstrainedFenv(),\n"; Code << " " << (HasFastMathFlags ? "op.getFastmathFlagsAttr()" : "cir::FastMathFlagsAttr{}") >From da5b9d9d0696a44439fbca4cf01aa2dd76c46b6d Mon Sep 17 00:00:00 2001 From: Henrich Lauko <[email protected]> Date: Thu, 29 Oct 2026 10:18:51 +0000 Subject: [PATCH 2/2] [CIR] Drop comment on FenvAttr constBuilderCall Assisted-by: Claude Code (Opus 5.5). --- clang/include/clang/CIR/Dialect/IR/CIRAttrs.td | 2 -- 1 file changed, 2 deletions(-) diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td index 9d2ec98042ce28..b470ba5b0847a8 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td @@ -967,8 +967,6 @@ def CIR_FenvAttr : CIR_Attr<"Fenv", "fenv"> { } }]; - // Used by `CIR_DefaultValuedFenvAttr`, whose default value is a full builder - // expression. let constBuilderCall = "$0"; let canHaveIllegalCXXABIType = 0; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
