diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h index fbbf54314191c..45cb18a50eeee 100644 --- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h +++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h @@ -11,6 +11,7 @@ #include "clang/AST/CharUnits.h" #include "clang/Basic/AddressSpaces.h" +#include "clang/Basic/LangOptions.h" #include "clang/CIR/Dialect/IR/CIRAttrs.h" #include "clang/CIR/Dialect/IR/CIRDialect.h" #include "clang/CIR/Dialect/IR/CIRTypes.h" @@ -68,6 +69,12 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { : mlir::OpBuilder(&mlirContext) {} CIRBaseBuilderTy(mlir::OpBuilder &builder) : mlir::OpBuilder(builder) {} + bool isFPConstrained = false; + clang::LangOptions::FPExceptionModeKind defaultConstrainedExcept = + clang::LangOptions::FPE_Ignore; + llvm::RoundingMode defaultConstrainedRounding = + llvm::RoundingMode::NearestTiesToEven; + mlir::Value getConstAPInt(mlir::Location loc, mlir::Type typ, const llvm::APInt &val) { return cir::ConstantOp::create(*this, loc, cir::IntAttr::get(typ, val)); @@ -203,6 +210,96 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { cir::BoolAttr getTrueAttr() { return getCIRBoolAttr(true); } cir::BoolAttr getFalseAttr() { return getCIRBoolAttr(false); } + // + // Floating point specific helpers + // ------------------------------- + // + + /// Enable/Disable use of constrained floating point math. When enabled the + /// CreateF() calls instead create constrained floating point intrinsic + /// calls. Fast math flags are unaffected by this setting. + void setIsFPConstrained(bool isCon) { isFPConstrained = isCon; } + + /// Query for the use of constrained floating point math + bool getIsFPConstrained() const { return isFPConstrained; } + + /// Set the exception handling to be used with constrained floating point + void setDefaultConstrainedExcept( + clang::LangOptions::FPExceptionModeKind newExcept) { + defaultConstrainedExcept = newExcept; + } + + /// Get the exception handling used with constrained floating point + clang::LangOptions::FPExceptionModeKind getDefaultConstrainedExcept() const { + return defaultConstrainedExcept; + } + + /// Set the rounding mode handling to be used with constrained floating point + void setDefaultConstrainedRounding(llvm::RoundingMode newRounding) { + defaultConstrainedRounding = newRounding; + } + + /// Get the rounding mode handling used with constrained floating point + llvm::RoundingMode getDefaultConstrainedRounding() const { + return defaultConstrainedRounding; + } + + /// Build the `#cir.fenv` attribute describing the constrained floating-point + /// environment currently in effect. This is attached to floating-point + /// operations that support it to capture the rounding and exception + /// behavior. Returns a null attribute when constrained floating-point is not + /// enabled, in which case no attribute should be attached. + cir::FenvAttr getConstrainedFPAttr() { + if (!isFPConstrained) + return {}; + + cir::FPDynamicRoundingMode roundingMode; + switch (defaultConstrainedRounding) { + case llvm::RoundingMode::NearestTiesToEven: + roundingMode = cir::FPDynamicRoundingMode::ToNearest; + break; + case llvm::RoundingMode::TowardNegative: + roundingMode = cir::FPDynamicRoundingMode::Downward; + break; + case llvm::RoundingMode::TowardPositive: + roundingMode = cir::FPDynamicRoundingMode::Upward; + break; + case llvm::RoundingMode::TowardZero: + roundingMode = cir::FPDynamicRoundingMode::UpwardZero; + break; + case llvm::RoundingMode::NearestTiesToAway: + roundingMode = cir::FPDynamicRoundingMode::ToNearestAway; + break; + case llvm::RoundingMode::Dynamic: + roundingMode = cir::FPDynamicRoundingMode::Unknown; + break; + default: + llvm_unreachable("unexpected constrained rounding mode"); + } + + cir::FPExceptionMode exceptMode; + bool strictExcept; + switch (defaultConstrainedExcept) { + case clang::LangOptions::FPE_Ignore: + exceptMode = cir::FPExceptionMode::Masked; + strictExcept = false; + break; + case clang::LangOptions::FPE_MayTrap: + exceptMode = cir::FPExceptionMode::Unknown; + strictExcept = false; + break; + case clang::LangOptions::FPE_Strict: + exceptMode = cir::FPExceptionMode::Unknown; + strictExcept = true; + break; + default: + llvm_unreachable("unexpected constrained exception mode"); + } + + return cir::FenvAttr::get(getContext(), roundingMode, exceptMode, + mlir::BoolAttr::get(getContext(), strictExcept)); + } + mlir::Value createComplexCreate(mlir::Location loc, mlir::Value real, mlir::Value imag) { auto resultComplexTy = cir::ComplexType::get(real.getType()); @@ -744,45 +841,41 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { mlir::Value createFAdd(mlir::Location loc, mlir::Value lhs, mlir::Value rhs) { assert(!cir::MissingFeatures::metaDataNode()); - assert(!cir::MissingFeatures::fpConstraints()); assert(!cir::MissingFeatures::fastMathFlags()); - return cir::FAddOp::create(*this, loc, lhs, rhs); + return cir::FAddOp::create(*this, loc, lhs, rhs, getConstrainedFPAttr()); } mlir::Value createFSub(mlir::Location loc, mlir::Value lhs, mlir::Value rhs) { assert(!cir::MissingFeatures::metaDataNode()); - assert(!cir::MissingFeatures::fpConstraints()); assert(!cir::MissingFeatures::fastMathFlags()); - return cir::FSubOp::create(*this, loc, lhs, rhs); + return cir::FSubOp::create(*this, loc, lhs, rhs, getConstrainedFPAttr()); } mlir::Value createFMul(mlir::Location loc, mlir::Value lhs, mlir::Value rhs) { assert(!cir::MissingFeatures::metaDataNode()); - assert(!cir::MissingFeatures::fpConstraints()); assert(!cir::MissingFeatures::fastMathFlags()); - return cir::FMulOp::create(*this, loc, lhs, rhs); + return cir::FMulOp::create(*this, loc, lhs, rhs, getConstrainedFPAttr()); } mlir::Value createFDiv(mlir::Location loc, mlir::Value lhs, mlir::Value rhs) { assert(!cir::MissingFeatures::metaDataNode()); - assert(!cir::MissingFeatures::fpConstraints()); assert(!cir::MissingFeatures::fastMathFlags()); - return cir::FDivOp::create(*this, loc, lhs, rhs); + return cir::FDivOp::create(*this, loc, lhs, rhs, getConstrainedFPAttr()); } mlir::Value createFRem(mlir::Location loc, mlir::Value lhs, mlir::Value rhs) { assert(!cir::MissingFeatures::metaDataNode()); - assert(!cir::MissingFeatures::fpConstraints()); assert(!cir::MissingFeatures::fastMathFlags()); - return cir::FRemOp::create(*this, loc, lhs, rhs); + return cir::FRemOp::create(*this, loc, lhs, rhs, getConstrainedFPAttr()); } mlir::Value createFNeg(mlir::Location loc, mlir::Value operand) { assert(cir::isFPOrVectorOfFPType(operand.getType()) && "expected floating-point or vector-of-float type"); assert(!cir::MissingFeatures::metaDataNode()); - assert(!cir::MissingFeatures::fpConstraints()); assert(!cir::MissingFeatures::fastMathFlags()); + // fneg does not raise FP exceptions or depend on the rounding mode, so it + // never carries an fenv attribute. return cir::FNegOp::create(*this, loc, operand); } @@ -796,7 +889,10 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { cir::CmpOp createCompare(mlir::Location loc, cir::CmpOpKind kind, mlir::Value lhs, mlir::Value rhs) { - return cir::CmpOp::create(*this, loc, kind, lhs, rhs); + cir::FenvAttr fenv; + if (cir::isAnyFloatingPointType(lhs.getType())) + fenv = getConstrainedFPAttr(); + return cir::CmpOp::create(*this, loc, kind, lhs, rhs, fenv); } cir::VecCmpOp createVecCompare(mlir::Location loc, cir::CmpOpKind kind, @@ -806,7 +902,11 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { getSIntNTy(getCIRIntOrFloatBitWidth(vecCast.getElementType())); VectorType integralVecTy = cir::VectorType::get(integralTy, vecCast.getSize()); - return cir::VecCmpOp::create(*this, loc, integralVecTy, kind, lhs, rhs); + cir::FenvAttr fenv; + if (cir::isFPOrVectorOfFPType(lhs.getType())) + fenv = getConstrainedFPAttr(); + return cir::VecCmpOp::create(*this, loc, integralVecTy, kind, lhs, rhs, + fenv); } mlir::Value createIsNaN(mlir::Location loc, mlir::Value operand) { diff --git a/clang/include/clang/CIR/Dialect/IR/CIRDialect.td b/clang/include/clang/CIR/Dialect/IR/CIRDialect.td index d340a9061310f..de90fed45f170 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRDialect.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRDialect.td @@ -44,6 +44,7 @@ def CIR_Dialect : Dialect { static llvm::StringRef getReturnsTwiceAttrName() { return "returns_twice"; } static llvm::StringRef getColdAttrName() { return "cold"; } static llvm::StringRef getHotAttrName() { return "hot"; } + static llvm::StringRef getStrictFPAttrName() { return "strictfp"; } static llvm::StringRef getNoDuplicatesAttrName() { return "noduplicate"; } static llvm::StringRef getConvergentAttrName() { return "convergent"; } static llvm::StringRef getNoUnwindAttrName() { return "nounwind"; } diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 591b5dc9acf1c..ba8011e03f638 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -7619,6 +7619,7 @@ class CIR_TernaryFPToFPBuiltinOp ]; let llvmOp = llvmOpName; + let constrainedLLVMIntrinsic = mnemonic; } def CIR_FMAOp : CIR_TernaryFPToFPBuiltinOp<"fma", "FMAOp"> { diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h index 074233050bee7..c906e65a132c2 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h +++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h @@ -30,9 +30,6 @@ namespace clang::CIRGen { class CIRGenBuilderTy : public cir::CIRBaseBuilderTy { const CIRGenTypeCache &typeCache; - bool isFPConstrained = false; - llvm::fp::ExceptionBehavior defaultConstrainedExcept = llvm::fp::ebStrict; - llvm::RoundingMode defaultConstrainedRounding = llvm::RoundingMode::Dynamic; llvm::StringMap recordNames; llvm::StringMap globalsVersioning; @@ -120,43 +117,6 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy { return baseName + "." + std::to_string(recordNames[baseName]++); } - // - // Floating point specific helpers - // ------------------------------- - // - - /// Enable/Disable use of constrained floating point math. When enabled the - /// CreateF() calls instead create constrained floating point intrinsic - /// calls. Fast math flags are unaffected by this setting. - void setIsFPConstrained(bool isCon) { isFPConstrained = isCon; } - - /// Query for the use of constrained floating point math - bool getIsFPConstrained() const { return isFPConstrained; } - - /// Set the exception handling to be used with constrained floating point - void setDefaultConstrainedExcept(llvm::fp::ExceptionBehavior newExcept) { - assert(llvm::convertExceptionBehaviorToStr(newExcept) && - "Garbage strict exception behavior!"); - defaultConstrainedExcept = newExcept; - } - - /// Get the exception handling used with constrained floating point - llvm::fp::ExceptionBehavior getDefaultConstrainedExcept() const { - return defaultConstrainedExcept; - } - - /// Set the rounding mode handling to be used with constrained floating point - void setDefaultConstrainedRounding(llvm::RoundingMode newRounding) { - assert(llvm::convertRoundingModeToStr(newRounding) && - "Garbage strict rounding mode!"); - defaultConstrainedRounding = newRounding; - } - - /// Get the rounding mode handling used with constrained floating point - llvm::RoundingMode getDefaultConstrainedRounding() const { - return defaultConstrainedRounding; - } - cir::LongDoubleType getLongDoubleTy(const llvm::fltSemantics &format) const { if (&format == &llvm::APFloat::IEEEdouble()) return cir::LongDoubleType::get(getContext(), typeCache.doubleTy); @@ -497,10 +457,9 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy { // TODO: split this to createFPExt/createFPTrunc when we have dedicated cast // operations. mlir::Value createFloatingCast(mlir::Value v, mlir::Type destType) { - assert(!cir::MissingFeatures::fpConstraints()); - return cir::CastOp::create(*this, v.getLoc(), destType, - cir::CastKind::floating, v); + cir::CastKind::floating, v, + getConstrainedFPAttr()); } mlir::Value createDynCast(mlir::Location loc, mlir::Value src, diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp index 874f5188ae009..beb3082f48537 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp @@ -356,10 +356,9 @@ static RValue emitUnaryMaybeConstrainedFPBuiltin(CIRGenFunction &cgf, mlir::Value arg = cgf.emitScalarExpr(e.getArg(0)); CIRGenFunction::CIRGenFPOptionsRAII FPOptsRAII(cgf, &e); - assert(!cir::MissingFeatures::fpConstraints()); - auto call = - Operation::create(cgf.getBuilder(), arg.getLoc(), arg.getType(), arg); + auto call = Operation::create(cgf.getBuilder(), arg.getLoc(), arg.getType(), + arg, cgf.getBuilder().getConstrainedFPAttr()); return RValue::get(call->getResult(0)); } @@ -377,9 +376,10 @@ static RValue emitUnaryMaybeConstrainedFPToIntBuiltin(CIRGenFunction &cgf, mlir::Type resultType = cgf.convertType(e.getType()); mlir::Value src = cgf.emitScalarExpr(e.getArg(0)); - assert(!cir::MissingFeatures::fpConstraints()); + CIRGenFunction::CIRGenFPOptionsRAII FPOptsRAII(cgf, &e); - auto call = Op::create(cgf.getBuilder(), src.getLoc(), resultType, src); + auto call = Op::create(cgf.getBuilder(), src.getLoc(), resultType, src, + cgf.getBuilder().getConstrainedFPAttr()); return RValue::get(call->getResult(0)); } @@ -398,6 +398,8 @@ static RValue emitBinaryFPBuiltin(CIRGenFunction &cgf, const CallExpr &e) { template static RValue emitTernaryMaybeConstrainedFPBuiltin(CIRGenFunction &cgf, const CallExpr &e) { + CIRGenFunction::CIRGenFPOptionsRAII FPOptsRAII(cgf, &e); + mlir::Value arg0 = cgf.emitScalarExpr(e.getArg(0)); mlir::Value arg1 = cgf.emitScalarExpr(e.getArg(1)); mlir::Value arg2 = cgf.emitScalarExpr(e.getArg(2)); @@ -405,9 +407,8 @@ static RValue emitTernaryMaybeConstrainedFPBuiltin(CIRGenFunction &cgf, mlir::Location loc = cgf.getLoc(e.getExprLoc()); mlir::Type ty = cgf.convertType(e.getType()); - assert(!cir::MissingFeatures::fpConstraints()); - - auto call = Op::create(cgf.getBuilder(), loc, ty, arg0, arg1, arg2); + auto call = Op::create(cgf.getBuilder(), loc, ty, arg0, arg1, arg2, + cgf.getBuilder().getConstrainedFPAttr()); return RValue::get(call->getResult(0)); } @@ -417,12 +418,13 @@ static mlir::Value emitBinaryMaybeConstrainedFPBuiltin(CIRGenFunction &cgf, mlir::Value arg0 = cgf.emitScalarExpr(e.getArg(0)); mlir::Value arg1 = cgf.emitScalarExpr(e.getArg(1)); + CIRGenFunction::CIRGenFPOptionsRAII FPOptsRAII(cgf, &e); + mlir::Location loc = cgf.getLoc(e.getExprLoc()); mlir::Type ty = cgf.convertType(e.getType()); - assert(!cir::MissingFeatures::fpConstraints()); - - auto call = Op::create(cgf.getBuilder(), loc, ty, arg0, arg1); + auto call = Op::create(cgf.getBuilder(), loc, ty, arg0, arg1, + cgf.getBuilder().getConstrainedFPAttr()); return call->getResult(0); } @@ -1587,7 +1589,6 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID, case Builtin::BI__builtin_isnan: { CIRGenFunction::CIRGenFPOptionsRAII FPOptsRAII(*this, e); mlir::Value v = emitScalarExpr(e->getArg(0)); - assert(!cir::MissingFeatures::fpConstraints()); mlir::Location loc = getLoc(e->getBeginLoc()); return RValue::get(builder.createBoolToInt( builder.createIsFPClass(loc, v, cir::FPClassTest::Nan), @@ -1606,7 +1607,6 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID, case Builtin::BI__builtin_isinf: { CIRGenFunction::CIRGenFPOptionsRAII FPOptsRAII(*this, e); mlir::Value v = emitScalarExpr(e->getArg(0)); - assert(!cir::MissingFeatures::fpConstraints()); mlir::Location loc = getLoc(e->getBeginLoc()); return RValue::get(builder.createBoolToInt( builder.createIsFPClass(loc, v, cir::FPClassTest::Infinity), @@ -1619,9 +1619,7 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID, case Builtin::BIfinitel: case Builtin::BI__finitel: case Builtin::BI__builtin_isfinite: { - CIRGenFunction::CIRGenFPOptionsRAII FPOptsRAII(*this, e); mlir::Value v = emitScalarExpr(e->getArg(0)); - assert(!cir::MissingFeatures::fpConstraints()); mlir::Location loc = getLoc(e->getBeginLoc()); return RValue::get(builder.createBoolToInt( builder.createIsFPClass(loc, v, cir::FPClassTest::Finite), @@ -1629,7 +1627,6 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID, } case Builtin::BI__builtin_isnormal: { - CIRGenFunction::CIRGenFPOptionsRAII FPOptsRAII(*this, e); mlir::Value v = emitScalarExpr(e->getArg(0)); mlir::Location loc = getLoc(e->getBeginLoc()); return RValue::get(builder.createBoolToInt( @@ -1638,7 +1635,6 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID, } case Builtin::BI__builtin_issubnormal: { - CIRGenFunction::CIRGenFPOptionsRAII FPOptsRAII(*this, e); mlir::Value v = emitScalarExpr(e->getArg(0)); mlir::Location loc = getLoc(e->getBeginLoc()); return RValue::get(builder.createBoolToInt( @@ -1647,7 +1643,6 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID, } case Builtin::BI__builtin_iszero: { - CIRGenFunction::CIRGenFPOptionsRAII FPOptsRAII(*this, e); mlir::Value v = emitScalarExpr(e->getArg(0)); mlir::Location loc = getLoc(e->getBeginLoc()); return RValue::get(builder.createBoolToInt( @@ -1663,7 +1658,6 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID, mlir::Value v = emitScalarExpr(e->getArg(0)); uint64_t test = result.Val.getInt().getLimitedValue(); mlir::Location loc = getLoc(e->getBeginLoc()); - // return RValue::get(builder.createBoolToInt( builder.createIsFPClass(loc, v, cir::FPClassTest(test)), convertType(e->getType()))); diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltinX86.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltinX86.cpp index 9c98f4bc55daa..9228367fdd44f 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuiltinX86.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenBuiltinX86.cpp @@ -58,11 +58,10 @@ static mlir::Value emitVectorFCmp(CIRGenFunction &cgf, const CallExpr &expr, llvm::SmallVector &ops, cir::CmpOpKind pred, bool shouldInvert) { CIRGenFunction::CIRGenFPOptionsRAII FPOptsRAII(cgf, &expr); - // TODO(cir): Add isSignaling boolean once emitConstrainedFPCall implemented - assert(!cir::MissingFeatures::emitConstrainedFPCall()); clang::CIRGen::CIRGenBuilderTy &builder = cgf.getBuilder(); mlir::Value cmp = builder.createVecCompare(cgf.getLoc(expr.getExprLoc()), pred, ops[0], ops[1]); + // TODO(cir): Add dedicated predicates to avoid the need to invert this. mlir::Value bitCast = builder.createBitcast( shouldInvert ? builder.createNot(cmp) : cmp, ops[0].getType()); return bitCast; diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp index 7ed01ecdf27ff..8d660a0a2c721 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp @@ -522,32 +522,40 @@ class ScalarExprEmitter : public StmtVisitor { std::optional castKind; + // Start with a null fenv attr. If this is a floating point cast, we will + // get the attribute from the builder. + cir::FenvAttr fenvAttr; + if (mlir::isa(srcTy)) { if (opts.treatBooleanAsSigned) cgf.getCIRGenModule().errorNYI("signed bool"); - if (cgf.getBuilder().isInt(dstTy)) + if (cgf.getBuilder().isInt(dstTy)) { castKind = cir::CastKind::bool_to_int; - else if (mlir::isa(dstTy)) + } else if (mlir::isa(dstTy)) { + fenvAttr = cgf.getBuilder().getConstrainedFPAttr(); castKind = cir::CastKind::bool_to_float; - else + } else { llvm_unreachable("Internal error: Cast to unexpected type"); + } } else if (cgf.getBuilder().isInt(srcTy)) { - if (cgf.getBuilder().isInt(dstTy)) + if (cgf.getBuilder().isInt(dstTy)) { castKind = cir::CastKind::integral; - else if (mlir::isa(dstTy)) + } else if (mlir::isa(dstTy)) { + fenvAttr = cgf.getBuilder().getConstrainedFPAttr(); castKind = cir::CastKind::int_to_float; - else if (mlir::isa(dstTy)) + } else if (mlir::isa(dstTy)) { castKind = cir::CastKind::int_to_bool; - else + } else { llvm_unreachable("Internal error: Cast to unexpected type"); + } } else if (mlir::isa(srcTy)) { + fenvAttr = cgf.getBuilder().getConstrainedFPAttr(); if (cgf.getBuilder().isInt(dstTy)) { // If we can't recognize overflow as undefined behavior, assume that // overflow saturates. This protects against normal optimizations if we // are compiling with non-standard FP semantics. if (!cgf.cgm.getCodeGenOpts().StrictFloatCastOverflow) cgf.getCIRGenModule().errorNYI("strict float cast overflow"); - assert(!cir::MissingFeatures::fpConstraints()); castKind = cir::CastKind::float_to_int; } else if (mlir::isa(dstTy)) { // TODO: split this to createFPExt/createFPTrunc @@ -563,7 +571,7 @@ class ScalarExprEmitter : public StmtVisitor { assert(castKind.has_value() && "Internal error: CastKind not set."); return builder.createOrFold(src.getLoc(), fullDstTy, *castKind, - src); + src, fenvAttr); } mlir::Value @@ -1059,8 +1067,7 @@ class ScalarExprEmitter : public StmtVisitor { result.compType = vecType->getElementType(); result.opcode = e->getOpcode(); result.loc = e->getSourceRange(); - // TODO(cir): Result.FPFeatures - CIRGenFunction::CIRGenFPOptionsRAII FPOptsRAII(cgf, e); + result.fpFeatures = e->getFPFeaturesInEffect(cgf.getLangOpts()); result.e = e; return result; } @@ -2192,6 +2199,7 @@ mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *ce) { Expr *subExpr = ce->getSubExpr(); QualType destTy = ce->getType(); CastKind kind = ce->getCastKind(); + CIRGenFunction::CIRGenFPOptionsRAII FPOptsRAII(cgf, ce); // These cases are generally not written to ignore the result of evaluating // their sub-expressions, so we clear this now. diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp index 16e66b143d232..66e7b6d5061df 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -30,7 +30,8 @@ namespace clang::CIRGen { CIRGenFunction::CIRGenFunction(CIRGenModule &cgm, CIRGenBuilderTy &builder, bool suppressNewContext) - : CIRGenTypeCache(cgm), cgm{cgm}, builder(builder) { + : CIRGenTypeCache(cgm), cgm{cgm}, builder(builder), + curFPFeatures(cgm.getLangOpts()) { ehStack.setCGF(this); } @@ -484,6 +485,24 @@ void CIRGenFunction::startFunction(GlobalDecl gd, QualType returnType, const auto *fd = dyn_cast_or_null(d); curFuncDecl = (d ? d->getNonClosureContext() : nullptr); + // This is an artifact of the legacy handling of constrained floating-point + // modes. The rounding mode and exception behavior tracked in + // clang::LangOptions don't correspond directly to the representation we + // use in CIR, but the CIR settings can be derived from them. We track the + // default state using the legacy settings because it keeps the tracking in + // sync with classic codegen. + llvm::RoundingMode rm = getLangOpts().getDefaultRoundingMode(); + LangOptions::FPExceptionModeKind eb = getLangOpts().getDefaultExceptionMode(); + builder.setDefaultConstrainedRounding(rm); + builder.setDefaultConstrainedExcept(eb); + builder.setIsFPConstrained(false); + if ((fd && (fd->UsesFPIntrin() || fd->hasAttr())) || + (!fd && (eb != LangOptions::FPExceptionModeKind::FPE_Ignore || + rm != llvm::RoundingMode::NearestTiesToEven))) { + builder.setIsFPConstrained(true); + fn->setAttr(cir::CIRDialect::getStrictFPAttrName(), + mlir::UnitAttr::get(fn.getContext())); + } prologueCleanupDepth = ehStack.stable_begin(); mlir::Block *entryBB = &fn.getBlocks().front(); @@ -721,10 +740,6 @@ cir::FuncOp CIRGenFunction::generateCode(clang::GlobalDecl gd, cir::FuncOp fn, // Emit the standard function prologue. startFunction(gd, retTy, fn, funcType, args, loc, bodyRange.getBegin()); - if (funcDecl->UsesFPIntrin() || funcDecl->hasAttr()) { - cgm.errorNYI(loc, "STDC FENV_ACCESS"); - return fn; - } // Save parameters for coroutine function. if (body && isa_and_nonnull(body)) @@ -963,23 +978,6 @@ LValue CIRGenFunction::makeNaturalAlignAddrLValue(mlir::Value val, return makeAddrLValue(addr, ty, baseInfo); } -// Map the LangOption for exception behavior into the corresponding enum in -// the IR. -static llvm::fp::ExceptionBehavior -toConstrainedExceptMd(LangOptions::FPExceptionModeKind kind) { - switch (kind) { - case LangOptions::FPE_Ignore: - return llvm::fp::ebIgnore; - case LangOptions::FPE_MayTrap: - return llvm::fp::ebMayTrap; - case LangOptions::FPE_Strict: - return llvm::fp::ebStrict; - case LangOptions::FPE_Default: - llvm_unreachable("expected explicitly initialized exception behavior"); - } - llvm_unreachable("unsupported FP exception behavior"); -} - clang::QualType CIRGenFunction::buildFunctionArgList(clang::GlobalDecl gd, FunctionArgList &args) { const auto *fd = cast(gd.getDecl()); @@ -1389,13 +1387,12 @@ void CIRGenFunction::CIRGenFPOptionsRAII::ConstructorHelper( // TODO(cir): create guard to restore fast math configurations. assert(!cir::MissingFeatures::fastMathGuard()); - [[maybe_unused]] llvm::RoundingMode newRoundingBehavior = - fpFeatures.getRoundingMode(); - // TODO(cir): override rounding behaviour once FM configs are guarded. - [[maybe_unused]] llvm::fp::ExceptionBehavior newExceptionBehavior = - toConstrainedExceptMd(static_cast( - fpFeatures.getExceptionMode())); - // TODO(cir): override exception behaviour once FM configs are guarded. + llvm::RoundingMode newRoundingMode = fpFeatures.getRoundingMode(); + LangOptions::FPExceptionModeKind newExceptionBehavior = + fpFeatures.getExceptionMode(); + + cgf.builder.setDefaultConstrainedRounding(newRoundingMode); + cgf.builder.setDefaultConstrainedExcept(newExceptionBehavior); // TODO(cir): override FP flags once FM configs are guarded. assert(!cir::MissingFeatures::fastMathFlags()); @@ -1403,8 +1400,8 @@ void CIRGenFunction::CIRGenFPOptionsRAII::ConstructorHelper( assert((cgf.curFuncDecl == nullptr || cgf.builder.getIsFPConstrained() || isa(cgf.curFuncDecl) || isa(cgf.curFuncDecl) || - (newExceptionBehavior == llvm::fp::ebIgnore && - newRoundingBehavior == llvm::RoundingMode::NearestTiesToEven)) && + (newExceptionBehavior == LangOptions::FPE_Ignore && + newRoundingMode == llvm::RoundingMode::NearestTiesToEven)) && "FPConstrained should be enabled on entire function"); // TODO(cir): mark CIR function with fast math attributes. diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index 4ba3ee59f49b0..521672206f78f 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -63,6 +63,33 @@ class CIRGenFunction : public CIRGenTypeCache { /// is where the next operations will be introduced. CIRGenBuilderTy &builder; + /// Saves the builder's constrained floating-point configuration on + /// construction and restores it on destruction. The builder is shared + /// across all functions in the module, so its constrained-FP state must be + /// scoped to each function's emission. + /// + /// Note that the similarly named CIRGenFunction::CIRGenFPOptionsRAII + /// intentionally avoids restoring the "isFPConstrained" state because that + /// state is function-wide, but this object does restore the state so that + /// any CIRGenFunction instance created while we are in the process of + /// emitting another function cannot corrupt the prior functions state. + struct ConstrainedFPRAII { + CIRGenBuilderTy &builder; + bool savedIsFPConstrained; + LangOptions::FPExceptionModeKind savedExcept; + llvm::RoundingMode savedRounding; + + explicit ConstrainedFPRAII(CIRGenBuilderTy &builder) + : builder(builder), savedIsFPConstrained(builder.getIsFPConstrained()), + savedExcept(builder.getDefaultConstrainedExcept()), + savedRounding(builder.getDefaultConstrainedRounding()) {} + ~ConstrainedFPRAII() { + builder.setIsFPConstrained(savedIsFPConstrained); + builder.setDefaultConstrainedExcept(savedExcept); + builder.setDefaultConstrainedRounding(savedRounding); + } + } constrainedFPState{builder}; + public: /// The GlobalDecl for the current function being compiled or the global /// variable currently being initialized. @@ -243,7 +270,7 @@ class CIRGenFunction : public CIRGenTypeCache { void ConstructorHelper(clang::FPOptions FPFeatures); CIRGenFunction &cgf; clang::FPOptions oldFPFeatures; - llvm::fp::ExceptionBehavior oldExcept; + LangOptions::FPExceptionModeKind oldExcept; llvm::RoundingMode oldRounding; }; clang::FPOptions curFPFeatures; diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index 3660c86378a9c..e9836b9010197 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -509,6 +509,9 @@ static llvm::StringRef getConstrainedRoundingMetadata(cir::FenvAttr fenv) { } static llvm::StringRef getConstrainedExceptMetadata(cir::FenvAttr fenv) { + std::optional exceptMode = fenv.getExceptMode(); + if (exceptMode && *exceptMode == cir::FPExceptionMode::Masked) + return "fpexcept.ignore"; mlir::BoolAttr strictExcept = fenv.getStrictExcept(); if (!strictExcept) return "fpexcept.ignore"; @@ -2559,6 +2562,7 @@ void CIRToLLVMFuncOpLowering::lowerFuncAttributes( attr.getName() == func.getInlineKindAttrName() || attr.getName() == func.getSideEffectAttrName() || attr.getName() == CIRDialect::getNoReturnAttrName() || + attr.getName() == CIRDialect::getStrictFPAttrName() || attr.getName() == func.getAnnotationsAttrName() || (filterArgAndResAttrs && (attr.getName() == func.getArgAttrsAttrName() || @@ -2684,6 +2688,14 @@ mlir::LogicalResult CIRToLLVMFuncOpLowering::matchAndRewrite( if (op->hasAttr(CIRDialect::getNoReturnAttrName())) fn.setNoreturn(true); + // The LLVM dialect's LLVMFuncOp has no dedicated field for the `strictfp` + // function attribute, so route it through the `passthrough` array. The MLIR + // LLVM IR translator forwards `passthrough` entries to LLVM IR as function + // attributes. + if (op->hasAttr(CIRDialect::getStrictFPAttrName())) + fn.setPassthroughAttr(rewriter.getArrayAttr( + {rewriter.getStringAttr(CIRDialect::getStrictFPAttrName())})); + if (std::optional inlineKind = op.getInlineKind()) { fn.setNoInline(*inlineKind == cir::InlineKind::NoInline); fn.setInlineHint(*inlineKind == cir::InlineKind::InlineHint); diff --git a/clang/test/CIR/CodeGen/pragma-fenv_access.c b/clang/test/CIR/CodeGen/pragma-fenv_access.c new file mode 100644 index 0000000000000..cc2eaebdc085e --- /dev/null +++ b/clang/test/CIR/CodeGen/pragma-fenv_access.c @@ -0,0 +1,1716 @@ +// --- STRICT: -ffp-exception-behavior=strict --- +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir \ +// RUN: -fexperimental-strict-floating-point -ffp-exception-behavior=strict \ +// RUN: -emit-cir %s -o %t-strict.cir +// RUN: FileCheck --check-prefixes=CIR,CIR-STRICT --input-file=%t-strict.cir %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir \ +// RUN: -fexperimental-strict-floating-point -ffp-exception-behavior=strict \ +// RUN: -emit-llvm %s -o %t-strict-cir.ll +// RUN: FileCheck --check-prefixes=LLVM,LLVM-STRICT \ +// RUN: --input-file=%t-strict-cir.ll %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu \ +// RUN: -fexperimental-strict-floating-point -ffp-exception-behavior=strict \ +// RUN: -emit-llvm %s -o %t-strict.ll +// RUN: FileCheck --check-prefixes=LLVM,LLVM-STRICT \ +// RUN: --input-file=%t-strict.ll %s + +// --- STRICT-RND: -frounding-math -ffp-exception-behavior=strict --- +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir \ +// RUN: -fexperimental-strict-floating-point -frounding-math \ +// RUN: -ffp-exception-behavior=strict -emit-cir %s -o %t-strict-rnd.cir +// RUN: FileCheck --check-prefixes=CIR,CIR-STRICT-RND --input-file=%t-strict-rnd.cir %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir \ +// RUN: -fexperimental-strict-floating-point -frounding-math \ +// RUN: -ffp-exception-behavior=strict -emit-llvm %s -o %t-strict-rnd-cir.ll +// RUN: FileCheck --check-prefixes=LLVM,LLVM-STRICT-RND \ +// RUN: --input-file=%t-strict-rnd-cir.ll %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu \ +// RUN: -fexperimental-strict-floating-point -frounding-math \ +// RUN: -ffp-exception-behavior=strict -emit-llvm %s -o %t-strict-rnd.ll +// RUN: FileCheck --check-prefixes=LLVM,LLVM-STRICT-RND \ +// RUN: --input-file=%t-strict-rnd.ll %s + +// --- DEFAULT: pragma-only constrained FP --- +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir \ +// RUN: -fexperimental-strict-floating-point -emit-cir %s -o %t-default.cir +// RUN: FileCheck --check-prefixes=CIR,CIR-DEFAULT --input-file=%t-default.cir %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir \ +// RUN: -fexperimental-strict-floating-point -emit-llvm %s -o %t-default-cir.ll +// RUN: FileCheck --check-prefixes=LLVM,LLVM-DEFAULT \ +// RUN: --input-file=%t-default-cir.ll %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu \ +// RUN: -fexperimental-strict-floating-point -emit-llvm %s -o %t-default.ll +// RUN: FileCheck --check-prefixes=LLVM,LLVM-DEFAULT \ +// RUN: --input-file=%t-default.ll %s + +// --- DEFAULT-RND: -frounding-math, pragma-only exceptions --- +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir \ +// RUN: -fexperimental-strict-floating-point -frounding-math \ +// RUN: -emit-cir %s -o %t-default-rnd.cir +// RUN: FileCheck --check-prefixes=CIR,CIR-DEFAULT-RND --input-file=%t-default-rnd.cir %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir \ +// RUN: -fexperimental-strict-floating-point -frounding-math \ +// RUN: -emit-llvm %s -o %t-default-rnd-cir.ll +// RUN: FileCheck --check-prefixes=LLVM,LLVM-DEFAULT-RND \ +// RUN: --input-file=%t-default-rnd-cir.ll %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu \ +// RUN: -fexperimental-strict-floating-point -frounding-math \ +// RUN: -emit-llvm %s -o %t-default-rnd.ll +// RUN: FileCheck --check-prefixes=LLVM,LLVM-DEFAULT-RND \ +// RUN: --input-file=%t-default-rnd.ll %s + +void test_cmdline_defaults(float x, float y, float z, int i, double d) { +// CIR-LABEL: cir.func {{.*}}@test_cmdline_defaults( +// CIR-STRICT-SAME: strictfp +// CIR-STRICT-RND-SAME: strictfp +// CIR-DEFAULT-RND-SAME: strictfp +// CIR-DEFAULT-NOT: strictfp +// LLVM-LABEL: define {{.*}}@test_cmdline_defaults( +// LLVM-STRICT-SAME: #[[$STRICT_ATTR:[0-9]+]] +// LLVM-STRICT-RND-SAME: #[[$STRICT_ATTR:[0-9]+]] +// LLVM-DEFAULT-RND-SAME: #[[$STRICT_ATTR:[0-9]+]] + float sf; + double sd; + int si; + long sl; + _Bool sb; + sf = x + y; +// CIR-STRICT: cir.fadd {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.fadd {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.fadd {{.*}} : !cir.float loc +// CIR-DEFAULT-RND: cir.fadd {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM-STRICT: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") +// LLVM-DEFAULT: fadd float +// LLVM-DEFAULT-RND: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.ignore") + sf = x - y; +// CIR-STRICT: cir.fsub {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.fsub {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.fsub {{.*}} : !cir.float loc +// CIR-DEFAULT-RND: cir.fsub {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM-STRICT: call float @llvm.experimental.constrained.fsub.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.fsub.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") +// LLVM-DEFAULT: fsub float +// LLVM-DEFAULT-RND: call float @llvm.experimental.constrained.fsub.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.ignore") + sf = x * y; +// CIR-STRICT: cir.fmul {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.fmul {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.fmul {{.*}} : !cir.float loc +// CIR-DEFAULT-RND: cir.fmul {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM-STRICT: call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") +// LLVM-DEFAULT: fmul float +// LLVM-DEFAULT-RND: call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.ignore") + sf = x / y; +// CIR-STRICT: cir.fdiv {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.fdiv {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.fdiv {{.*}} : !cir.float loc +// CIR-DEFAULT-RND: cir.fdiv {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM-STRICT: call float @llvm.experimental.constrained.fdiv.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.fdiv.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") +// LLVM-DEFAULT: fdiv float +// LLVM-DEFAULT-RND: call float @llvm.experimental.constrained.fdiv.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.ignore") + sf = -x; +// CIR: cir.fneg {{.*}} : !cir.float loc +// LLVM: fneg float + sb = x < y; +// CIR-STRICT: cir.cmp lt {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.cmp lt {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.cmp lt {{.*}} : !cir.float +// CIR-DEFAULT-RND: cir.cmp lt {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM-STRICT: call i1 @llvm.experimental.constrained.fcmps.f32({{.*}}, metadata !"olt", metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call i1 @llvm.experimental.constrained.fcmps.f32({{.*}}, metadata !"olt", metadata !"fpexcept.strict") +// LLVM-DEFAULT: fcmp olt float +// LLVM-DEFAULT-RND: call i1 @llvm.experimental.constrained.fcmps.f32({{.*}}, metadata !"olt", metadata !"fpexcept.ignore") + sf = (float)i; +// CIR-STRICT: cir.cast int_to_float {{.*}} : !s32i -> !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.cast int_to_float {{.*}} : !s32i -> !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.cast int_to_float {{.*}} : !s32i -> !cir.float +// CIR-DEFAULT-RND: cir.cast int_to_float {{.*}} : !s32i -> !cir.float {fenv = #cir.fenv} +// LLVM-STRICT: call float @llvm.experimental.constrained.sitofp.f32.i32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.sitofp.f32.i32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") +// LLVM-DEFAULT: sitofp i32 {{.*}} to float +// LLVM-DEFAULT-RND: call float @llvm.experimental.constrained.sitofp.f32.i32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.ignore") + si = (int)x; +// CIR-STRICT: cir.cast float_to_int {{.*}} : !cir.float -> !s32i {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.cast float_to_int {{.*}} : !cir.float -> !s32i {fenv = #cir.fenv} +// CIR-DEFAULT: cir.cast float_to_int {{.*}} : !cir.float -> !s32i +// CIR-DEFAULT-RND: cir.cast float_to_int {{.*}} : !cir.float -> !s32i {fenv = #cir.fenv} +// LLVM-STRICT: call i32 @llvm.experimental.constrained.fptosi.i32.f32({{.*}}, metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call i32 @llvm.experimental.constrained.fptosi.i32.f32({{.*}}, metadata !"fpexcept.strict") +// LLVM-DEFAULT: fptosi float {{.*}} to i32 +// LLVM-DEFAULT-RND: call i32 @llvm.experimental.constrained.fptosi.i32.f32({{.*}}, metadata !"fpexcept.ignore") + sd = (double)x; +// CIR-STRICT: cir.cast floating {{.*}} : !cir.float -> !cir.double {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.cast floating {{.*}} : !cir.float -> !cir.double {fenv = #cir.fenv} +// CIR-DEFAULT: cir.cast floating {{.*}} : !cir.float -> !cir.double +// CIR-DEFAULT-RND: cir.cast floating {{.*}} : !cir.float -> !cir.double {fenv = #cir.fenv} +// LLVM-STRICT: call double @llvm.experimental.constrained.fpext.f64.f32({{.*}}, metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call double @llvm.experimental.constrained.fpext.f64.f32({{.*}}, metadata !"fpexcept.strict") +// LLVM-DEFAULT: fpext float {{.*}} to double +// LLVM-DEFAULT-RND: call double @llvm.experimental.constrained.fpext.f64.f32({{.*}}, metadata !"fpexcept.ignore") + sf = (float)d; +// CIR-STRICT: cir.cast floating {{.*}} : !cir.double -> !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.cast floating {{.*}} : !cir.double -> !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.cast floating {{.*}} : !cir.double -> !cir.float +// CIR-DEFAULT-RND: cir.cast floating {{.*}} : !cir.double -> !cir.float {fenv = #cir.fenv} +// LLVM-STRICT: call float @llvm.experimental.constrained.fptrunc.f32.f64({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.fptrunc.f32.f64({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") +// LLVM-DEFAULT: fptrunc double {{.*}} to float +// LLVM-DEFAULT-RND: call float @llvm.experimental.constrained.fptrunc.f32.f64({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.ignore") + sf = __builtin_sqrtf(x); +// CIR-STRICT: cir.sqrt {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.sqrt {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.sqrt {{.*}} : !cir.float loc +// CIR-DEFAULT-RND: cir.sqrt {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM-STRICT: call float @llvm.experimental.constrained.sqrt.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.sqrt.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") +// LLVM-DEFAULT: call float @llvm.sqrt.f32 +// LLVM-DEFAULT-RND: call float @llvm.experimental.constrained.sqrt.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.ignore") + sf = __builtin_powf(x, y); +// CIR-STRICT: cir.pow {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.pow {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.pow {{.*}} : !cir.float loc +// CIR-DEFAULT-RND: cir.pow {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM-STRICT: call float @llvm.experimental.constrained.pow.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.pow.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") +// LLVM-DEFAULT: call float @llvm.pow.f32 +// LLVM-DEFAULT-RND: call float @llvm.experimental.constrained.pow.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.ignore") + sf = __builtin_fmaf(x, y, z); +// CIR-STRICT: cir.fma {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.fma {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.fma {{.*}} : !cir.float loc +// CIR-DEFAULT-RND: cir.fma {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM-STRICT: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") +// LLVM-DEFAULT: call float @llvm.fma.f32 +// LLVM-DEFAULT-RND: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.ignore") + sl = __builtin_lroundf(x); +// CIR-STRICT: cir.lround {{.*}} : !cir.float -> !s64i {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.lround {{.*}} : !cir.float -> !s64i {fenv = #cir.fenv} +// CIR-DEFAULT: cir.lround {{.*}} : !cir.float -> !s64i +// CIR-DEFAULT-RND: cir.lround {{.*}} : !cir.float -> !s64i {fenv = #cir.fenv} +// LLVM-STRICT: call i64 @llvm.experimental.constrained.lround.i64.f32({{.*}}, metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call i64 @llvm.experimental.constrained.lround.i64.f32({{.*}}, metadata !"fpexcept.strict") +// LLVM-DEFAULT: call i64 @llvm.lround.i64.f32 +// LLVM-DEFAULT-RND: call i64 @llvm.experimental.constrained.lround.i64.f32({{.*}}, metadata !"fpexcept.ignore") + sf = __builtin_fmodf(x, y); +// CIR-STRICT: cir.fmod {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.fmod {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.fmod {{.*}} : !cir.float loc +// CIR-DEFAULT-RND: cir.fmod {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM-STRICT: call float @llvm.experimental.constrained.frem.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.frem.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") +// LLVM-DEFAULT: frem float +// LLVM-DEFAULT-RND: call float @llvm.experimental.constrained.frem.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.ignore") +} + +#pragma STDC FENV_ACCESS ON + +void test_global_fenv_access_on(float x, float y, float z, int i, double d) { +// CIR-LABEL: cir.func {{.*}}@test_global_fenv_access_on( +// LLVM-LABEL: define {{.*}}@test_global_fenv_access_on( + float sf; + double sd; + int si; + long sl; + _Bool sb; + sf = x + y; +// CIR: cir.fadd {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + sf = x - y; +// CIR: cir.fsub {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fsub.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + sf = x * y; +// CIR: cir.fmul {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + sf = x / y; +// CIR: cir.fdiv {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fdiv.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + sf = -x; +// CIR: cir.fneg {{.*}} : !cir.float loc +// LLVM: fneg float + sb = x < y; +// CIR: cir.cmp lt {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call i1 @llvm.experimental.constrained.fcmps.f32({{.*}}, metadata !"olt", metadata !"fpexcept.strict") + sf = (float)i; +// CIR: cir.cast int_to_float {{.*}} : !s32i -> !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.sitofp.f32.i32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + si = (int)x; +// CIR: cir.cast float_to_int {{.*}} : !cir.float -> !s32i {fenv = #cir.fenv} +// LLVM: call i32 @llvm.experimental.constrained.fptosi.i32.f32({{.*}}, metadata !"fpexcept.strict") + sd = (double)x; +// CIR: cir.cast floating {{.*}} : !cir.float -> !cir.double {fenv = #cir.fenv} +// LLVM: call double @llvm.experimental.constrained.fpext.f64.f32({{.*}}, metadata !"fpexcept.strict") + sf = (float)d; +// CIR: cir.cast floating {{.*}} : !cir.double -> !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fptrunc.f32.f64({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + sf = __builtin_sqrtf(x); +// CIR: cir.sqrt {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.sqrt.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + sf = __builtin_powf(x, y); +// CIR: cir.pow {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.pow.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + sf = __builtin_fmaf(x, y, z); +// CIR: cir.fma {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM-STRICT: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") +// LLVM-DEFAULT: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") +// LLVM-DEFAULT-RND: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + sl = __builtin_lroundf(x); +// CIR: cir.lround {{.*}} : !cir.float -> !s64i {fenv = #cir.fenv} +// LLVM: call i64 @llvm.experimental.constrained.lround.i64.f32({{.*}}, metadata !"fpexcept.strict") + sf = __builtin_fmodf(x, y); +// CIR: cir.fmod {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.frem.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") +} + +void test_except_off_fenv_access_off(float x, float y, float z, int i, double d) { +#pragma float_control(except, off) +#pragma STDC FENV_ACCESS OFF +// CIR-LABEL: cir.func {{.*}}@test_except_off_fenv_access_off( +// LLVM-LABEL: define {{.*}}@test_except_off_fenv_access_off( + float sf; + double sd; + int si; + long sl; + _Bool sb; + sf = x + y; +// CIR: cir.fadd {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") + sf = x - y; +// CIR: cir.fsub {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fsub.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") + sf = x * y; +// CIR: cir.fmul {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") + sf = x / y; +// CIR: cir.fdiv {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fdiv.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") + sf = -x; +// CIR: cir.fneg {{.*}} : !cir.float loc +// LLVM: fneg float + sb = x < y; +// CIR: cir.cmp lt {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call i1 @llvm.experimental.constrained.fcmps.f32({{.*}}, metadata !"olt", metadata !"fpexcept.ignore") + sf = (float)i; +// CIR: cir.cast int_to_float {{.*}} : !s32i -> !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.sitofp.f32.i32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") + si = (int)x; +// CIR: cir.cast float_to_int {{.*}} : !cir.float -> !s32i {fenv = #cir.fenv} +// LLVM: call i32 @llvm.experimental.constrained.fptosi.i32.f32({{.*}}, metadata !"fpexcept.ignore") + sd = (double)x; +// CIR: cir.cast floating {{.*}} : !cir.float -> !cir.double {fenv = #cir.fenv} +// LLVM: call double @llvm.experimental.constrained.fpext.f64.f32({{.*}}, metadata !"fpexcept.ignore") + sf = (float)d; +// CIR: cir.cast floating {{.*}} : !cir.double -> !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fptrunc.f32.f64({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") + sf = __builtin_sqrtf(x); +// CIR: cir.sqrt {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.sqrt.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") + sf = __builtin_powf(x, y); +// CIR: cir.pow {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.pow.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") + sf = __builtin_fmaf(x, y, z); +// CIR: cir.fma {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM-STRICT: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-DEFAULT: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-DEFAULT-RND: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") + sl = __builtin_lroundf(x); +// CIR: cir.lround {{.*}} : !cir.float -> !s64i {fenv = #cir.fenv} +// LLVM: call i64 @llvm.experimental.constrained.lround.i64.f32({{.*}}, metadata !"fpexcept.ignore") + sf = __builtin_fmodf(x, y); +// CIR: cir.fmod {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.frem.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +} + +void test_fenv_access_on_after_local_off(float x, float y, float z, int i, double d) { +// CIR-LABEL: cir.func {{.*}}@test_fenv_access_on_after_local_off( +// LLVM-LABEL: define {{.*}}@test_fenv_access_on_after_local_off( + float sf; + double sd; + int si; + long sl; + _Bool sb; + sf = x + y; +// CIR: cir.fadd {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + sf = x - y; +// CIR: cir.fsub {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fsub.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + sf = x * y; +// CIR: cir.fmul {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + sf = x / y; +// CIR: cir.fdiv {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fdiv.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + sf = -x; +// CIR: cir.fneg {{.*}} : !cir.float loc +// LLVM: fneg float + sb = x < y; +// CIR: cir.cmp lt {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call i1 @llvm.experimental.constrained.fcmps.f32({{.*}}, metadata !"olt", metadata !"fpexcept.strict") + sf = (float)i; +// CIR: cir.cast int_to_float {{.*}} : !s32i -> !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.sitofp.f32.i32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + si = (int)x; +// CIR: cir.cast float_to_int {{.*}} : !cir.float -> !s32i {fenv = #cir.fenv} +// LLVM: call i32 @llvm.experimental.constrained.fptosi.i32.f32({{.*}}, metadata !"fpexcept.strict") + sd = (double)x; +// CIR: cir.cast floating {{.*}} : !cir.float -> !cir.double {fenv = #cir.fenv} +// LLVM: call double @llvm.experimental.constrained.fpext.f64.f32({{.*}}, metadata !"fpexcept.strict") + sf = (float)d; +// CIR: cir.cast floating {{.*}} : !cir.double -> !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fptrunc.f32.f64({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + sf = __builtin_sqrtf(x); +// CIR: cir.sqrt {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.sqrt.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + sf = __builtin_powf(x, y); +// CIR: cir.pow {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.pow.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + sf = __builtin_fmaf(x, y, z); +// CIR: cir.fma {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM-STRICT: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") +// LLVM-DEFAULT: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") +// LLVM-DEFAULT-RND: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + sl = __builtin_lroundf(x); +// CIR: cir.lround {{.*}} : !cir.float -> !s64i {fenv = #cir.fenv} +// LLVM: call i64 @llvm.experimental.constrained.lround.i64.f32({{.*}}, metadata !"fpexcept.strict") + sf = __builtin_fmodf(x, y); +// CIR: cir.fmod {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.frem.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") +} + +#pragma STDC FENV_ACCESS OFF + +void test_float_control_except_off(float x, float y, float z, int i, double d) { +#pragma float_control(except, off) +// CIR-LABEL: cir.func {{.*}}@test_float_control_except_off( +// LLVM-LABEL: define {{.*}}@test_float_control_except_off( + float sf; + double sd; + int si; + long sl; + _Bool sb; + sf = x + y; +// CIR-STRICT: cir.fadd {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.fadd {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.fadd {{.*}} : !cir.float loc +// CIR-DEFAULT-RND: cir.fadd {{.*}} : !cir.float loc +// LLVM-STRICT: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-DEFAULT: fadd float +// LLVM-DEFAULT-RND: fadd float + sf = x - y; +// CIR-STRICT: cir.fsub {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.fsub {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.fsub {{.*}} : !cir.float loc +// CIR-DEFAULT-RND: cir.fsub {{.*}} : !cir.float loc +// LLVM-STRICT: call float @llvm.experimental.constrained.fsub.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.fsub.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-DEFAULT: fsub float +// LLVM-DEFAULT-RND: fsub float + sf = x * y; +// CIR-STRICT: cir.fmul {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.fmul {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.fmul {{.*}} : !cir.float loc +// CIR-DEFAULT-RND: cir.fmul {{.*}} : !cir.float loc +// LLVM-STRICT: call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-DEFAULT: fmul float +// LLVM-DEFAULT-RND: fmul float + sf = x / y; +// CIR-STRICT: cir.fdiv {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.fdiv {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.fdiv {{.*}} : !cir.float loc +// CIR-DEFAULT-RND: cir.fdiv {{.*}} : !cir.float loc +// LLVM-STRICT: call float @llvm.experimental.constrained.fdiv.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.fdiv.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-DEFAULT: fdiv float +// LLVM-DEFAULT-RND: fdiv float + sf = -x; +// CIR: cir.fneg {{.*}} : !cir.float loc +// LLVM: fneg float + sb = x < y; +// CIR-STRICT: cir.cmp lt {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.cmp lt {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.cmp lt {{.*}} : !cir.float +// CIR-DEFAULT-RND: cir.cmp lt {{.*}} : !cir.float +// LLVM-STRICT: call i1 @llvm.experimental.constrained.fcmps.f32({{.*}}, metadata !"olt", metadata !"fpexcept.ignore") +// LLVM-STRICT-RND: call i1 @llvm.experimental.constrained.fcmps.f32({{.*}}, metadata !"olt", metadata !"fpexcept.ignore") +// LLVM-DEFAULT: fcmp olt float +// LLVM-DEFAULT-RND: fcmp olt float + sf = (float)i; +// CIR-STRICT: cir.cast int_to_float {{.*}} : !s32i -> !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.cast int_to_float {{.*}} : !s32i -> !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.cast int_to_float {{.*}} : !s32i -> !cir.float +// CIR-DEFAULT-RND: cir.cast int_to_float {{.*}} : !s32i -> !cir.float +// LLVM-STRICT: call float @llvm.experimental.constrained.sitofp.f32.i32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.sitofp.f32.i32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-DEFAULT: sitofp i32 {{.*}} to float +// LLVM-DEFAULT-RND: sitofp i32 {{.*}} to float + si = (int)x; +// CIR-STRICT: cir.cast float_to_int {{.*}} : !cir.float -> !s32i {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.cast float_to_int {{.*}} : !cir.float -> !s32i {fenv = #cir.fenv} +// CIR-DEFAULT: cir.cast float_to_int {{.*}} : !cir.float -> !s32i +// CIR-DEFAULT-RND: cir.cast float_to_int {{.*}} : !cir.float -> !s32i +// LLVM-STRICT: call i32 @llvm.experimental.constrained.fptosi.i32.f32({{.*}}, metadata !"fpexcept.ignore") +// LLVM-STRICT-RND: call i32 @llvm.experimental.constrained.fptosi.i32.f32({{.*}}, metadata !"fpexcept.ignore") +// LLVM-DEFAULT: fptosi float {{.*}} to i32 +// LLVM-DEFAULT-RND: fptosi float {{.*}} to i32 + sd = (double)x; +// CIR-STRICT: cir.cast floating {{.*}} : !cir.float -> !cir.double {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.cast floating {{.*}} : !cir.float -> !cir.double {fenv = #cir.fenv} +// CIR-DEFAULT: cir.cast floating {{.*}} : !cir.float -> !cir.double +// CIR-DEFAULT-RND: cir.cast floating {{.*}} : !cir.float -> !cir.double +// LLVM-STRICT: call double @llvm.experimental.constrained.fpext.f64.f32({{.*}}, metadata !"fpexcept.ignore") +// LLVM-STRICT-RND: call double @llvm.experimental.constrained.fpext.f64.f32({{.*}}, metadata !"fpexcept.ignore") +// LLVM-DEFAULT: fpext float {{.*}} to double +// LLVM-DEFAULT-RND: fpext float {{.*}} to double + sf = (float)d; +// CIR-STRICT: cir.cast floating {{.*}} : !cir.double -> !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.cast floating {{.*}} : !cir.double -> !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.cast floating {{.*}} : !cir.double -> !cir.float +// CIR-DEFAULT-RND: cir.cast floating {{.*}} : !cir.double -> !cir.float +// LLVM-STRICT: call float @llvm.experimental.constrained.fptrunc.f32.f64({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.fptrunc.f32.f64({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-DEFAULT: fptrunc double {{.*}} to float +// LLVM-DEFAULT-RND: fptrunc double {{.*}} to float + sf = __builtin_sqrtf(x); +// CIR-STRICT: cir.sqrt {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.sqrt {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.sqrt {{.*}} : !cir.float loc +// CIR-DEFAULT-RND: cir.sqrt {{.*}} : !cir.float loc +// LLVM-STRICT: call float @llvm.experimental.constrained.sqrt.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.sqrt.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-DEFAULT: call float @llvm.sqrt.f32 +// LLVM-DEFAULT-RND: call float @llvm.sqrt.f32 + sf = __builtin_powf(x, y); +// CIR-STRICT: cir.pow {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.pow {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.pow {{.*}} : !cir.float loc +// CIR-DEFAULT-RND: cir.pow {{.*}} : !cir.float loc +// LLVM-STRICT: call float @llvm.experimental.constrained.pow.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.pow.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-DEFAULT: call float @llvm.pow.f32 +// LLVM-DEFAULT-RND: call float @llvm.pow.f32 + sf = __builtin_fmaf(x, y, z); +// CIR-STRICT: cir.fma {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.fma {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.fma {{.*}} : !cir.float loc +// CIR-DEFAULT-RND: cir.fma {{.*}} : !cir.float loc +// LLVM-STRICT: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-DEFAULT: call float @llvm.fma.f32 +// LLVM-DEFAULT-RND: call float @llvm.fma.f32 + sl = __builtin_lroundf(x); +// CIR-STRICT: cir.lround {{.*}} : !cir.float -> !s64i {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.lround {{.*}} : !cir.float -> !s64i {fenv = #cir.fenv} +// CIR-DEFAULT: cir.lround {{.*}} : !cir.float -> !s64i +// CIR-DEFAULT-RND: cir.lround {{.*}} : !cir.float -> !s64i +// LLVM-STRICT: call i64 @llvm.experimental.constrained.lround.i64.f32({{.*}}, metadata !"fpexcept.ignore") +// LLVM-STRICT-RND: call i64 @llvm.experimental.constrained.lround.i64.f32({{.*}}, metadata !"fpexcept.ignore") +// LLVM-DEFAULT: call i64 @llvm.lround.i64.f32 +// LLVM-DEFAULT-RND: call i64 @llvm.lround.i64.f32 + sf = __builtin_fmodf(x, y); +// CIR-STRICT: cir.fmod {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.fmod {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.fmod {{.*}} : !cir.float loc +// CIR-DEFAULT-RND: cir.fmod {{.*}} : !cir.float loc +// LLVM-STRICT: call float @llvm.experimental.constrained.frem.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.frem.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-DEFAULT: frem float +// LLVM-DEFAULT-RND: frem float +} + +void test_float_control_except_on(float x, float y, float z, int i, double d) { +#pragma float_control(except, on) +// CIR-LABEL: cir.func {{.*}}@test_float_control_except_on( +// LLVM-LABEL: define {{.*}}@test_float_control_except_on( + float sf; + double sd; + int si; + long sl; + _Bool sb; + sf = x + y; +// CIR: cir.fadd {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") + sf = x - y; +// CIR: cir.fsub {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fsub.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") + sf = x * y; +// CIR: cir.fmul {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") + sf = x / y; +// CIR: cir.fdiv {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fdiv.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") + sf = -x; +// CIR: cir.fneg {{.*}} : !cir.float loc +// LLVM: fneg float + sb = x < y; +// CIR: cir.cmp lt {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call i1 @llvm.experimental.constrained.fcmps.f32({{.*}}, metadata !"olt", metadata !"fpexcept.strict") + sf = (float)i; +// CIR: cir.cast int_to_float {{.*}} : !s32i -> !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.sitofp.f32.i32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") + si = (int)x; +// CIR: cir.cast float_to_int {{.*}} : !cir.float -> !s32i {fenv = #cir.fenv} +// LLVM: call i32 @llvm.experimental.constrained.fptosi.i32.f32({{.*}}, metadata !"fpexcept.strict") + sd = (double)x; +// CIR: cir.cast floating {{.*}} : !cir.float -> !cir.double {fenv = #cir.fenv} +// LLVM: call double @llvm.experimental.constrained.fpext.f64.f32({{.*}}, metadata !"fpexcept.strict") + sf = (float)d; +// CIR: cir.cast floating {{.*}} : !cir.double -> !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fptrunc.f32.f64({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") + sf = __builtin_sqrtf(x); +// CIR: cir.sqrt {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.sqrt.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") + sf = __builtin_powf(x, y); +// CIR: cir.pow {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.pow.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") + sf = __builtin_fmaf(x, y, z); +// CIR: cir.fma {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM-STRICT: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-DEFAULT: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-DEFAULT-RND: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") + sl = __builtin_lroundf(x); +// CIR: cir.lround {{.*}} : !cir.float -> !s64i {fenv = #cir.fenv} +// LLVM: call i64 @llvm.experimental.constrained.lround.i64.f32({{.*}}, metadata !"fpexcept.strict") + sf = __builtin_fmodf(x, y); +// CIR: cir.fmod {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.frem.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +} + +void test_local_fenv_access_on(float x, float y, float z, int i, double d) { +#pragma STDC FENV_ACCESS ON +// CIR-LABEL: cir.func {{.*}}@test_local_fenv_access_on( +// LLVM-LABEL: define {{.*}}@test_local_fenv_access_on( + float sf; + double sd; + int si; + long sl; + _Bool sb; + sf = x + y; +// CIR: cir.fadd {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + sf = x - y; +// CIR: cir.fsub {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fsub.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + sf = x * y; +// CIR: cir.fmul {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + sf = x / y; +// CIR: cir.fdiv {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fdiv.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + sf = -x; +// CIR: cir.fneg {{.*}} : !cir.float loc +// LLVM: fneg float + sb = x < y; +// CIR: cir.cmp lt {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call i1 @llvm.experimental.constrained.fcmps.f32({{.*}}, metadata !"olt", metadata !"fpexcept.strict") + sf = (float)i; +// CIR: cir.cast int_to_float {{.*}} : !s32i -> !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.sitofp.f32.i32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + si = (int)x; +// CIR: cir.cast float_to_int {{.*}} : !cir.float -> !s32i {fenv = #cir.fenv} +// LLVM: call i32 @llvm.experimental.constrained.fptosi.i32.f32({{.*}}, metadata !"fpexcept.strict") + sd = (double)x; +// CIR: cir.cast floating {{.*}} : !cir.float -> !cir.double {fenv = #cir.fenv} +// LLVM: call double @llvm.experimental.constrained.fpext.f64.f32({{.*}}, metadata !"fpexcept.strict") + sf = (float)d; +// CIR: cir.cast floating {{.*}} : !cir.double -> !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fptrunc.f32.f64({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + sf = __builtin_sqrtf(x); +// CIR: cir.sqrt {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.sqrt.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + sf = __builtin_powf(x, y); +// CIR: cir.pow {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.pow.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + sf = __builtin_fmaf(x, y, z); +// CIR: cir.fma {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM-STRICT: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") +// LLVM-DEFAULT: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") +// LLVM-DEFAULT-RND: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + sl = __builtin_lroundf(x); +// CIR: cir.lround {{.*}} : !cir.float -> !s64i {fenv = #cir.fenv} +// LLVM: call i64 @llvm.experimental.constrained.lround.i64.f32({{.*}}, metadata !"fpexcept.strict") + sf = __builtin_fmodf(x, y); +// CIR: cir.fmod {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.frem.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") +} + +void test_float_control_except_off_after_on(float x, float y, float z, int i, double d) { +#pragma float_control(except, off) +// CIR-LABEL: cir.func {{.*}}@test_float_control_except_off_after_on( +// LLVM-LABEL: define {{.*}}@test_float_control_except_off_after_on( + float sf; + double sd; + int si; + long sl; + _Bool sb; + sf = x + y; +// CIR-STRICT: cir.fadd {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.fadd {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.fadd {{.*}} : !cir.float loc +// CIR-DEFAULT-RND: cir.fadd {{.*}} : !cir.float loc +// LLVM-STRICT: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-DEFAULT: fadd float +// LLVM-DEFAULT-RND: fadd float + sf = x - y; +// CIR-STRICT: cir.fsub {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.fsub {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.fsub {{.*}} : !cir.float loc +// CIR-DEFAULT-RND: cir.fsub {{.*}} : !cir.float loc +// LLVM-STRICT: call float @llvm.experimental.constrained.fsub.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.fsub.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-DEFAULT: fsub float +// LLVM-DEFAULT-RND: fsub float + sf = x * y; +// CIR-STRICT: cir.fmul {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.fmul {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.fmul {{.*}} : !cir.float loc +// CIR-DEFAULT-RND: cir.fmul {{.*}} : !cir.float loc +// LLVM-STRICT: call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-DEFAULT: fmul float +// LLVM-DEFAULT-RND: fmul float + sf = x / y; +// CIR-STRICT: cir.fdiv {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.fdiv {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.fdiv {{.*}} : !cir.float loc +// CIR-DEFAULT-RND: cir.fdiv {{.*}} : !cir.float loc +// LLVM-STRICT: call float @llvm.experimental.constrained.fdiv.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.fdiv.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-DEFAULT: fdiv float +// LLVM-DEFAULT-RND: fdiv float + sf = -x; +// CIR: cir.fneg {{.*}} : !cir.float loc +// LLVM: fneg float + sb = x < y; +// CIR-STRICT: cir.cmp lt {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.cmp lt {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.cmp lt {{.*}} : !cir.float +// CIR-DEFAULT-RND: cir.cmp lt {{.*}} : !cir.float +// LLVM-STRICT: call i1 @llvm.experimental.constrained.fcmps.f32({{.*}}, metadata !"olt", metadata !"fpexcept.ignore") +// LLVM-STRICT-RND: call i1 @llvm.experimental.constrained.fcmps.f32({{.*}}, metadata !"olt", metadata !"fpexcept.ignore") +// LLVM-DEFAULT: fcmp olt float +// LLVM-DEFAULT-RND: fcmp olt float + sf = (float)i; +// CIR-STRICT: cir.cast int_to_float {{.*}} : !s32i -> !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.cast int_to_float {{.*}} : !s32i -> !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.cast int_to_float {{.*}} : !s32i -> !cir.float +// CIR-DEFAULT-RND: cir.cast int_to_float {{.*}} : !s32i -> !cir.float +// LLVM-STRICT: call float @llvm.experimental.constrained.sitofp.f32.i32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.sitofp.f32.i32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-DEFAULT: sitofp i32 {{.*}} to float +// LLVM-DEFAULT-RND: sitofp i32 {{.*}} to float + si = (int)x; +// CIR-STRICT: cir.cast float_to_int {{.*}} : !cir.float -> !s32i {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.cast float_to_int {{.*}} : !cir.float -> !s32i {fenv = #cir.fenv} +// CIR-DEFAULT: cir.cast float_to_int {{.*}} : !cir.float -> !s32i +// CIR-DEFAULT-RND: cir.cast float_to_int {{.*}} : !cir.float -> !s32i +// LLVM-STRICT: call i32 @llvm.experimental.constrained.fptosi.i32.f32({{.*}}, metadata !"fpexcept.ignore") +// LLVM-STRICT-RND: call i32 @llvm.experimental.constrained.fptosi.i32.f32({{.*}}, metadata !"fpexcept.ignore") +// LLVM-DEFAULT: fptosi float {{.*}} to i32 +// LLVM-DEFAULT-RND: fptosi float {{.*}} to i32 + sd = (double)x; +// CIR-STRICT: cir.cast floating {{.*}} : !cir.float -> !cir.double {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.cast floating {{.*}} : !cir.float -> !cir.double {fenv = #cir.fenv} +// CIR-DEFAULT: cir.cast floating {{.*}} : !cir.float -> !cir.double +// CIR-DEFAULT-RND: cir.cast floating {{.*}} : !cir.float -> !cir.double +// LLVM-STRICT: call double @llvm.experimental.constrained.fpext.f64.f32({{.*}}, metadata !"fpexcept.ignore") +// LLVM-STRICT-RND: call double @llvm.experimental.constrained.fpext.f64.f32({{.*}}, metadata !"fpexcept.ignore") +// LLVM-DEFAULT: fpext float {{.*}} to double +// LLVM-DEFAULT-RND: fpext float {{.*}} to double + sf = (float)d; +// CIR-STRICT: cir.cast floating {{.*}} : !cir.double -> !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.cast floating {{.*}} : !cir.double -> !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.cast floating {{.*}} : !cir.double -> !cir.float +// CIR-DEFAULT-RND: cir.cast floating {{.*}} : !cir.double -> !cir.float +// LLVM-STRICT: call float @llvm.experimental.constrained.fptrunc.f32.f64({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.fptrunc.f32.f64({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-DEFAULT: fptrunc double {{.*}} to float +// LLVM-DEFAULT-RND: fptrunc double {{.*}} to float + sf = __builtin_sqrtf(x); +// CIR-STRICT: cir.sqrt {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.sqrt {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.sqrt {{.*}} : !cir.float loc +// CIR-DEFAULT-RND: cir.sqrt {{.*}} : !cir.float loc +// LLVM-STRICT: call float @llvm.experimental.constrained.sqrt.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.sqrt.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-DEFAULT: call float @llvm.sqrt.f32 +// LLVM-DEFAULT-RND: call float @llvm.sqrt.f32 + sf = __builtin_powf(x, y); +// CIR-STRICT: cir.pow {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.pow {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.pow {{.*}} : !cir.float loc +// CIR-DEFAULT-RND: cir.pow {{.*}} : !cir.float loc +// LLVM-STRICT: call float @llvm.experimental.constrained.pow.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.pow.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-DEFAULT: call float @llvm.pow.f32 +// LLVM-DEFAULT-RND: call float @llvm.pow.f32 + sf = __builtin_fmaf(x, y, z); +// CIR-STRICT: cir.fma {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.fma {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.fma {{.*}} : !cir.float loc +// CIR-DEFAULT-RND: cir.fma {{.*}} : !cir.float loc +// LLVM-STRICT: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-DEFAULT: call float @llvm.fma.f32 +// LLVM-DEFAULT-RND: call float @llvm.fma.f32 + sl = __builtin_lroundf(x); +// CIR-STRICT: cir.lround {{.*}} : !cir.float -> !s64i {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.lround {{.*}} : !cir.float -> !s64i {fenv = #cir.fenv} +// CIR-DEFAULT: cir.lround {{.*}} : !cir.float -> !s64i +// CIR-DEFAULT-RND: cir.lround {{.*}} : !cir.float -> !s64i +// LLVM-STRICT: call i64 @llvm.experimental.constrained.lround.i64.f32({{.*}}, metadata !"fpexcept.ignore") +// LLVM-STRICT-RND: call i64 @llvm.experimental.constrained.lround.i64.f32({{.*}}, metadata !"fpexcept.ignore") +// LLVM-DEFAULT: call i64 @llvm.lround.i64.f32 +// LLVM-DEFAULT-RND: call i64 @llvm.lround.i64.f32 + sf = __builtin_fmodf(x, y); +// CIR-STRICT: cir.fmod {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.fmod {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.fmod {{.*}} : !cir.float loc +// CIR-DEFAULT-RND: cir.fmod {{.*}} : !cir.float loc +// LLVM-STRICT: call float @llvm.experimental.constrained.frem.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.frem.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-DEFAULT: frem float +// LLVM-DEFAULT-RND: frem float +} + +void test_scoped_fenv_access_on(float x, float y) { +// CIR-LABEL: cir.func {{.*}}@test_scoped_fenv_access_on( +// LLVM-LABEL: define {{.*}}@test_scoped_fenv_access_on( + float sf; + if (x) { +#pragma STDC FENV_ACCESS ON + sf = x + y; +// CIR: cir.fadd {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + } +} + +void test_fenv_round_upward(float x, float y, float z, int i, double d) { +#pragma STDC FENV_ROUND FE_UPWARD +#pragma STDC FENV_ACCESS ON +// CIR-LABEL: cir.func {{.*}}@test_fenv_round_upward( +// LLVM-LABEL: define {{.*}}@test_fenv_round_upward( + float sf; + double sd; + int si; + long sl; + _Bool sb; + sf = x + y; +// CIR: cir.fadd {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.upward", metadata !"fpexcept.strict") + sf = x - y; +// CIR: cir.fsub {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fsub.f32({{.*}}, metadata !"round.upward", metadata !"fpexcept.strict") + sf = x * y; +// CIR: cir.fmul {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.upward", metadata !"fpexcept.strict") + sf = x / y; +// CIR: cir.fdiv {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fdiv.f32({{.*}}, metadata !"round.upward", metadata !"fpexcept.strict") + sf = -x; +// CIR: cir.fneg {{.*}} : !cir.float loc +// LLVM: fneg float + sb = x < y; +// CIR: cir.cmp lt {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call i1 @llvm.experimental.constrained.fcmps.f32({{.*}}, metadata !"olt", metadata !"fpexcept.strict") + sf = (float)i; +// CIR: cir.cast int_to_float {{.*}} : !s32i -> !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.sitofp.f32.i32({{.*}}, metadata !"round.upward", metadata !"fpexcept.strict") + si = (int)x; +// CIR: cir.cast float_to_int {{.*}} : !cir.float -> !s32i {fenv = #cir.fenv} +// LLVM: call i32 @llvm.experimental.constrained.fptosi.i32.f32({{.*}}, metadata !"fpexcept.strict") + sd = (double)x; +// CIR: cir.cast floating {{.*}} : !cir.float -> !cir.double {fenv = #cir.fenv} +// LLVM: call double @llvm.experimental.constrained.fpext.f64.f32({{.*}}, metadata !"fpexcept.strict") + sf = (float)d; +// CIR: cir.cast floating {{.*}} : !cir.double -> !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fptrunc.f32.f64({{.*}}, metadata !"round.upward", metadata !"fpexcept.strict") + sf = __builtin_sqrtf(x); +// CIR: cir.sqrt {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.sqrt.f32({{.*}}, metadata !"round.upward", metadata !"fpexcept.strict") + sf = __builtin_powf(x, y); +// CIR: cir.pow {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.pow.f32({{.*}}, metadata !"round.upward", metadata !"fpexcept.strict") + sf = __builtin_fmaf(x, y, z); +// CIR: cir.fma {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM-STRICT: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.upward", metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.upward", metadata !"fpexcept.strict") +// LLVM-DEFAULT: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.upward", metadata !"fpexcept.strict") +// LLVM-DEFAULT-RND: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.upward", metadata !"fpexcept.strict") + sl = __builtin_lroundf(x); +// CIR: cir.lround {{.*}} : !cir.float -> !s64i {fenv = #cir.fenv} +// LLVM: call i64 @llvm.experimental.constrained.lround.i64.f32({{.*}}, metadata !"fpexcept.strict") + sf = __builtin_fmodf(x, y); +// CIR: cir.fmod {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.frem.f32({{.*}}, metadata !"round.upward", metadata !"fpexcept.strict") +} + +void test_fenv_round_tonearest(float x, float y, float z, int i, double d) { +#pragma STDC FENV_ROUND FE_TONEAREST +#pragma STDC FENV_ACCESS ON +// CIR-LABEL: cir.func {{.*}}@test_fenv_round_tonearest( +// LLVM-LABEL: define {{.*}}@test_fenv_round_tonearest( + float sf; + double sd; + int si; + long sl; + _Bool sb; + sf = x + y; +// CIR: cir.fadd {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") + sf = x - y; +// CIR: cir.fsub {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fsub.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") + sf = x * y; +// CIR: cir.fmul {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") + sf = x / y; +// CIR: cir.fdiv {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fdiv.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") + sf = -x; +// CIR: cir.fneg {{.*}} : !cir.float loc +// LLVM: fneg float + sb = x < y; +// CIR: cir.cmp lt {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call i1 @llvm.experimental.constrained.fcmps.f32({{.*}}, metadata !"olt", metadata !"fpexcept.strict") + sf = (float)i; +// CIR: cir.cast int_to_float {{.*}} : !s32i -> !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.sitofp.f32.i32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") + si = (int)x; +// CIR: cir.cast float_to_int {{.*}} : !cir.float -> !s32i {fenv = #cir.fenv} +// LLVM: call i32 @llvm.experimental.constrained.fptosi.i32.f32({{.*}}, metadata !"fpexcept.strict") + sd = (double)x; +// CIR: cir.cast floating {{.*}} : !cir.float -> !cir.double {fenv = #cir.fenv} +// LLVM: call double @llvm.experimental.constrained.fpext.f64.f32({{.*}}, metadata !"fpexcept.strict") + sf = (float)d; +// CIR: cir.cast floating {{.*}} : !cir.double -> !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fptrunc.f32.f64({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") + sf = __builtin_sqrtf(x); +// CIR: cir.sqrt {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.sqrt.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") + sf = __builtin_powf(x, y); +// CIR: cir.pow {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.pow.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") + sf = __builtin_fmaf(x, y, z); +// CIR: cir.fma {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM-STRICT: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-DEFAULT: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-DEFAULT-RND: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") + sl = __builtin_lroundf(x); +// CIR: cir.lround {{.*}} : !cir.float -> !s64i {fenv = #cir.fenv} +// LLVM: call i64 @llvm.experimental.constrained.lround.i64.f32({{.*}}, metadata !"fpexcept.strict") + sf = __builtin_fmodf(x, y); +// CIR: cir.fmod {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.frem.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +} + +void test_fenv_round_tonearest_except_ignore(float x, float y, float z, int i, double d) { +#pragma STDC FENV_ROUND FE_TONEAREST +#pragma clang fp exceptions(ignore) +#pragma STDC FENV_ACCESS ON +// CIR-LABEL: cir.func {{.*}}@test_fenv_round_tonearest_except_ignore( +// LLVM-LABEL: define {{.*}}@test_fenv_round_tonearest_except_ignore( + float sf; + double sd; + int si; + long sl; + _Bool sb; + sf = x + y; +// CIR: cir.fadd {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") + sf = x - y; +// CIR: cir.fsub {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fsub.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") + sf = x * y; +// CIR: cir.fmul {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") + sf = x / y; +// CIR: cir.fdiv {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fdiv.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") + sf = -x; +// CIR: cir.fneg {{.*}} : !cir.float loc +// LLVM: fneg float + sb = x < y; +// CIR: cir.cmp lt {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call i1 @llvm.experimental.constrained.fcmps.f32({{.*}}, metadata !"olt", metadata !"fpexcept.ignore") + sf = (float)i; +// CIR: cir.cast int_to_float {{.*}} : !s32i -> !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.sitofp.f32.i32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") + si = (int)x; +// CIR: cir.cast float_to_int {{.*}} : !cir.float -> !s32i {fenv = #cir.fenv} +// LLVM: call i32 @llvm.experimental.constrained.fptosi.i32.f32({{.*}}, metadata !"fpexcept.ignore") + sd = (double)x; +// CIR: cir.cast floating {{.*}} : !cir.float -> !cir.double {fenv = #cir.fenv} +// LLVM: call double @llvm.experimental.constrained.fpext.f64.f32({{.*}}, metadata !"fpexcept.ignore") + sf = (float)d; +// CIR: cir.cast floating {{.*}} : !cir.double -> !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fptrunc.f32.f64({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") + sf = __builtin_sqrtf(x); +// CIR: cir.sqrt {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.sqrt.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") + sf = __builtin_powf(x, y); +// CIR: cir.pow {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.pow.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") + sf = __builtin_fmaf(x, y, z); +// CIR: cir.fma {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM-STRICT: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-DEFAULT: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-DEFAULT-RND: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") + sl = __builtin_lroundf(x); +// CIR: cir.lround {{.*}} : !cir.float -> !s64i {fenv = #cir.fenv} +// LLVM: call i64 @llvm.experimental.constrained.lround.i64.f32({{.*}}, metadata !"fpexcept.ignore") + sf = __builtin_fmodf(x, y); +// CIR: cir.fmod {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.frem.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +} + +void test_fenv_round_tonearest_except_ignore_fenv_off(float x, float y, float z, int i, double d) { +#pragma STDC FENV_ROUND FE_TONEAREST +#pragma clang fp exceptions(ignore) +#pragma STDC FENV_ACCESS OFF +// CIR-LABEL: cir.func {{.*}}@test_fenv_round_tonearest_except_ignore_fenv_off( +// LLVM-LABEL: define {{.*}}@test_fenv_round_tonearest_except_ignore_fenv_off( + float sf; + double sd; + int si; + long sl; + _Bool sb; + sf = x + y; +// CIR-STRICT: cir.fadd {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.fadd {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.fadd {{.*}} : !cir.float loc +// CIR-DEFAULT-RND: cir.fadd {{.*}} : !cir.float loc +// LLVM-STRICT: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-DEFAULT: fadd float +// LLVM-DEFAULT-RND: fadd float + sf = x - y; +// CIR-STRICT: cir.fsub {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.fsub {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.fsub {{.*}} : !cir.float loc +// CIR-DEFAULT-RND: cir.fsub {{.*}} : !cir.float loc +// LLVM-STRICT: call float @llvm.experimental.constrained.fsub.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.fsub.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-DEFAULT: fsub float +// LLVM-DEFAULT-RND: fsub float + sf = x * y; +// CIR-STRICT: cir.fmul {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.fmul {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.fmul {{.*}} : !cir.float loc +// CIR-DEFAULT-RND: cir.fmul {{.*}} : !cir.float loc +// LLVM-STRICT: call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-DEFAULT: fmul float +// LLVM-DEFAULT-RND: fmul float + sf = x / y; +// CIR-STRICT: cir.fdiv {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.fdiv {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.fdiv {{.*}} : !cir.float loc +// CIR-DEFAULT-RND: cir.fdiv {{.*}} : !cir.float loc +// LLVM-STRICT: call float @llvm.experimental.constrained.fdiv.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.fdiv.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-DEFAULT: fdiv float +// LLVM-DEFAULT-RND: fdiv float + sf = -x; +// CIR: cir.fneg {{.*}} : !cir.float loc +// LLVM: fneg float + sb = x < y; +// CIR-STRICT: cir.cmp lt {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.cmp lt {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.cmp lt {{.*}} : !cir.float +// CIR-DEFAULT-RND: cir.cmp lt {{.*}} : !cir.float +// LLVM-STRICT: call i1 @llvm.experimental.constrained.fcmps.f32({{.*}}, metadata !"olt", metadata !"fpexcept.ignore") +// LLVM-STRICT-RND: call i1 @llvm.experimental.constrained.fcmps.f32({{.*}}, metadata !"olt", metadata !"fpexcept.ignore") +// LLVM-DEFAULT: fcmp olt float +// LLVM-DEFAULT-RND: fcmp olt float + sf = (float)i; +// CIR-STRICT: cir.cast int_to_float {{.*}} : !s32i -> !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.cast int_to_float {{.*}} : !s32i -> !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.cast int_to_float {{.*}} : !s32i -> !cir.float +// CIR-DEFAULT-RND: cir.cast int_to_float {{.*}} : !s32i -> !cir.float +// LLVM-STRICT: call float @llvm.experimental.constrained.sitofp.f32.i32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.sitofp.f32.i32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-DEFAULT: sitofp i32 {{.*}} to float +// LLVM-DEFAULT-RND: sitofp i32 {{.*}} to float + si = (int)x; +// CIR-STRICT: cir.cast float_to_int {{.*}} : !cir.float -> !s32i {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.cast float_to_int {{.*}} : !cir.float -> !s32i {fenv = #cir.fenv} +// CIR-DEFAULT: cir.cast float_to_int {{.*}} : !cir.float -> !s32i +// CIR-DEFAULT-RND: cir.cast float_to_int {{.*}} : !cir.float -> !s32i +// LLVM-STRICT: call i32 @llvm.experimental.constrained.fptosi.i32.f32({{.*}}, metadata !"fpexcept.ignore") +// LLVM-STRICT-RND: call i32 @llvm.experimental.constrained.fptosi.i32.f32({{.*}}, metadata !"fpexcept.ignore") +// LLVM-DEFAULT: fptosi float {{.*}} to i32 +// LLVM-DEFAULT-RND: fptosi float {{.*}} to i32 + sd = (double)x; +// CIR-STRICT: cir.cast floating {{.*}} : !cir.float -> !cir.double {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.cast floating {{.*}} : !cir.float -> !cir.double {fenv = #cir.fenv} +// CIR-DEFAULT: cir.cast floating {{.*}} : !cir.float -> !cir.double +// CIR-DEFAULT-RND: cir.cast floating {{.*}} : !cir.float -> !cir.double +// LLVM-STRICT: call double @llvm.experimental.constrained.fpext.f64.f32({{.*}}, metadata !"fpexcept.ignore") +// LLVM-STRICT-RND: call double @llvm.experimental.constrained.fpext.f64.f32({{.*}}, metadata !"fpexcept.ignore") +// LLVM-DEFAULT: fpext float {{.*}} to double +// LLVM-DEFAULT-RND: fpext float {{.*}} to double + sf = (float)d; +// CIR-STRICT: cir.cast floating {{.*}} : !cir.double -> !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.cast floating {{.*}} : !cir.double -> !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.cast floating {{.*}} : !cir.double -> !cir.float +// CIR-DEFAULT-RND: cir.cast floating {{.*}} : !cir.double -> !cir.float +// LLVM-STRICT: call float @llvm.experimental.constrained.fptrunc.f32.f64({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.fptrunc.f32.f64({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-DEFAULT: fptrunc double {{.*}} to float +// LLVM-DEFAULT-RND: fptrunc double {{.*}} to float + sf = __builtin_sqrtf(x); +// CIR-STRICT: cir.sqrt {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.sqrt {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.sqrt {{.*}} : !cir.float loc +// CIR-DEFAULT-RND: cir.sqrt {{.*}} : !cir.float loc +// LLVM-STRICT: call float @llvm.experimental.constrained.sqrt.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.sqrt.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-DEFAULT: call float @llvm.sqrt.f32 +// LLVM-DEFAULT-RND: call float @llvm.sqrt.f32 + sf = __builtin_powf(x, y); +// CIR-STRICT: cir.pow {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.pow {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.pow {{.*}} : !cir.float loc +// CIR-DEFAULT-RND: cir.pow {{.*}} : !cir.float loc +// LLVM-STRICT: call float @llvm.experimental.constrained.pow.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.pow.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-DEFAULT: call float @llvm.pow.f32 +// LLVM-DEFAULT-RND: call float @llvm.pow.f32 + sf = __builtin_fmaf(x, y, z); +// CIR-STRICT: cir.fma {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.fma {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.fma {{.*}} : !cir.float loc +// CIR-DEFAULT-RND: cir.fma {{.*}} : !cir.float loc +// LLVM-STRICT: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-DEFAULT: call float @llvm.fma.f32 +// LLVM-DEFAULT-RND: call float @llvm.fma.f32 + sl = __builtin_lroundf(x); +// CIR-STRICT: cir.lround {{.*}} : !cir.float -> !s64i {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.lround {{.*}} : !cir.float -> !s64i {fenv = #cir.fenv} +// CIR-DEFAULT: cir.lround {{.*}} : !cir.float -> !s64i +// CIR-DEFAULT-RND: cir.lround {{.*}} : !cir.float -> !s64i +// LLVM-STRICT: call i64 @llvm.experimental.constrained.lround.i64.f32({{.*}}, metadata !"fpexcept.ignore") +// LLVM-STRICT-RND: call i64 @llvm.experimental.constrained.lround.i64.f32({{.*}}, metadata !"fpexcept.ignore") +// LLVM-DEFAULT: call i64 @llvm.lround.i64.f32 +// LLVM-DEFAULT-RND: call i64 @llvm.lround.i64.f32 + sf = __builtin_fmodf(x, y); +// CIR-STRICT: cir.fmod {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.fmod {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.fmod {{.*}} : !cir.float loc +// CIR-DEFAULT-RND: cir.fmod {{.*}} : !cir.float loc +// LLVM-STRICT: call float @llvm.experimental.constrained.frem.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.frem.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-DEFAULT: frem float +// LLVM-DEFAULT-RND: frem float +} + +void test_fp_exceptions_maytrap(float x, float y, float z, int i, double d) { +#pragma clang fp exceptions(maytrap) +#pragma STDC FENV_ACCESS ON +// CIR-LABEL: cir.func {{.*}}@test_fp_exceptions_maytrap( +// LLVM-LABEL: define {{.*}}@test_fp_exceptions_maytrap( + float sf; + double sd; + int si; + long sl; + _Bool sb; + sf = x + y; +// CIR: cir.fadd {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.maytrap") + sf = x - y; +// CIR: cir.fsub {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fsub.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.maytrap") + sf = x * y; +// CIR: cir.fmul {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.maytrap") + sf = x / y; +// CIR: cir.fdiv {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fdiv.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.maytrap") + sf = -x; +// CIR: cir.fneg {{.*}} : !cir.float loc +// LLVM: fneg float + sb = x < y; +// CIR: cir.cmp lt {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call i1 @llvm.experimental.constrained.fcmps.f32({{.*}}, metadata !"olt", metadata !"fpexcept.maytrap") + sf = (float)i; +// CIR: cir.cast int_to_float {{.*}} : !s32i -> !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.sitofp.f32.i32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.maytrap") + si = (int)x; +// CIR: cir.cast float_to_int {{.*}} : !cir.float -> !s32i {fenv = #cir.fenv} +// LLVM: call i32 @llvm.experimental.constrained.fptosi.i32.f32({{.*}}, metadata !"fpexcept.maytrap") + sd = (double)x; +// CIR: cir.cast floating {{.*}} : !cir.float -> !cir.double {fenv = #cir.fenv} +// LLVM: call double @llvm.experimental.constrained.fpext.f64.f32({{.*}}, metadata !"fpexcept.maytrap") + sf = (float)d; +// CIR: cir.cast floating {{.*}} : !cir.double -> !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fptrunc.f32.f64({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.maytrap") + sf = __builtin_sqrtf(x); +// CIR: cir.sqrt {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.sqrt.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.maytrap") + sf = __builtin_powf(x, y); +// CIR: cir.pow {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.pow.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.maytrap") + sf = __builtin_fmaf(x, y, z); +// CIR: cir.fma {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM-STRICT: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.maytrap") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.maytrap") +// LLVM-DEFAULT: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.maytrap") +// LLVM-DEFAULT-RND: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.maytrap") + sl = __builtin_lroundf(x); +// CIR: cir.lround {{.*}} : !cir.float -> !s64i {fenv = #cir.fenv} +// LLVM: call i64 @llvm.experimental.constrained.lround.i64.f32({{.*}}, metadata !"fpexcept.maytrap") + sf = __builtin_fmodf(x, y); +// CIR: cir.fmod {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.frem.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.maytrap") +} + +void test_fp_exceptions_maytrap_round_upward(float x, float y, float z, int i, double d) { +#pragma clang fp exceptions(maytrap) +#pragma STDC FENV_ROUND FE_UPWARD +#pragma STDC FENV_ACCESS ON +// CIR-LABEL: cir.func {{.*}}@test_fp_exceptions_maytrap_round_upward( +// LLVM-LABEL: define {{.*}}@test_fp_exceptions_maytrap_round_upward( + float sf; + double sd; + int si; + long sl; + _Bool sb; + sf = x + y; +// CIR: cir.fadd {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.upward", metadata !"fpexcept.maytrap") + sf = x - y; +// CIR: cir.fsub {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fsub.f32({{.*}}, metadata !"round.upward", metadata !"fpexcept.maytrap") + sf = x * y; +// CIR: cir.fmul {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.upward", metadata !"fpexcept.maytrap") + sf = x / y; +// CIR: cir.fdiv {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fdiv.f32({{.*}}, metadata !"round.upward", metadata !"fpexcept.maytrap") + sf = -x; +// CIR: cir.fneg {{.*}} : !cir.float loc +// LLVM: fneg float + sb = x < y; +// CIR: cir.cmp lt {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call i1 @llvm.experimental.constrained.fcmps.f32({{.*}}, metadata !"olt", metadata !"fpexcept.maytrap") + sf = (float)i; +// CIR: cir.cast int_to_float {{.*}} : !s32i -> !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.sitofp.f32.i32({{.*}}, metadata !"round.upward", metadata !"fpexcept.maytrap") + si = (int)x; +// CIR: cir.cast float_to_int {{.*}} : !cir.float -> !s32i {fenv = #cir.fenv} +// LLVM: call i32 @llvm.experimental.constrained.fptosi.i32.f32({{.*}}, metadata !"fpexcept.maytrap") + sd = (double)x; +// CIR: cir.cast floating {{.*}} : !cir.float -> !cir.double {fenv = #cir.fenv} +// LLVM: call double @llvm.experimental.constrained.fpext.f64.f32({{.*}}, metadata !"fpexcept.maytrap") + sf = (float)d; +// CIR: cir.cast floating {{.*}} : !cir.double -> !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fptrunc.f32.f64({{.*}}, metadata !"round.upward", metadata !"fpexcept.maytrap") + sf = __builtin_sqrtf(x); +// CIR: cir.sqrt {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.sqrt.f32({{.*}}, metadata !"round.upward", metadata !"fpexcept.maytrap") + sf = __builtin_powf(x, y); +// CIR: cir.pow {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.pow.f32({{.*}}, metadata !"round.upward", metadata !"fpexcept.maytrap") + sf = __builtin_fmaf(x, y, z); +// CIR: cir.fma {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM-STRICT: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.upward", metadata !"fpexcept.maytrap") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.upward", metadata !"fpexcept.maytrap") +// LLVM-DEFAULT: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.upward", metadata !"fpexcept.maytrap") +// LLVM-DEFAULT-RND: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.upward", metadata !"fpexcept.maytrap") + sl = __builtin_lroundf(x); +// CIR: cir.lround {{.*}} : !cir.float -> !s64i {fenv = #cir.fenv} +// LLVM: call i64 @llvm.experimental.constrained.lround.i64.f32({{.*}}, metadata !"fpexcept.maytrap") + sf = __builtin_fmodf(x, y); +// CIR: cir.fmod {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.frem.f32({{.*}}, metadata !"round.upward", metadata !"fpexcept.maytrap") +} + +void test_nested_fenv_access_off(float x, float y) { +#pragma STDC FENV_ACCESS ON +// CIR-LABEL: cir.func {{.*}}@test_nested_fenv_access_off( +// LLVM-LABEL: define {{.*}}@test_nested_fenv_access_off( + float sf; + sf = x + y; +// CIR: cir.fadd {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + { +#pragma STDC FENV_ACCESS OFF + sf = x + y; +// CIR-STRICT: cir.fadd {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.fadd {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.fadd {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT-RND: cir.fadd {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM-STRICT: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-DEFAULT: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-DEFAULT-RND: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") + } +} + +void test_fenv_round_towardzero_nested_fenv_off(float x, float y) { +#pragma STDC FENV_ROUND FE_TOWARDZERO +#pragma STDC FENV_ACCESS ON +// CIR-LABEL: cir.func {{.*}}@test_fenv_round_towardzero_nested_fenv_off( +// LLVM-LABEL: define {{.*}}@test_fenv_round_towardzero_nested_fenv_off( + float sf; + sf = x + y; +// CIR: cir.fadd {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.strict") + { +#pragma STDC FENV_ACCESS OFF + sf = x + y; +// CIR-STRICT: cir.fadd {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.fadd {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.fadd {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT-RND: cir.fadd {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM-STRICT: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.strict") +// LLVM-DEFAULT: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.ignore") +// LLVM-DEFAULT-RND: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.ignore") + } +} + +void test_nested_fenv_round_then_fenv_access(float x, float y) { +// CIR-LABEL: cir.func {{.*}}@test_nested_fenv_round_then_fenv_access( +// LLVM-LABEL: define {{.*}}@test_nested_fenv_round_then_fenv_access( + float sf; + sf = x + y; +// CIR-STRICT: cir.fadd {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.fadd {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.fadd {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT-RND: cir.fadd {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM-STRICT: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-DEFAULT: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-DEFAULT-RND: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") + { +#pragma STDC FENV_ROUND FE_TONEAREST +#pragma STDC FENV_ACCESS ON + sf = x + y; +// CIR: cir.fadd {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") + } + { +#pragma STDC FENV_ACCESS ON + sf = x + y; +// CIR: cir.fadd {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + } +} + +void test_fenv_round_dynamic_with_fenv_access(float x, float y, float z, int i, double d) { +#pragma STDC FENV_ROUND FE_DYNAMIC +#pragma STDC FENV_ACCESS ON +// CIR-LABEL: cir.func {{.*}}@test_fenv_round_dynamic_with_fenv_access( +// LLVM-LABEL: define {{.*}}@test_fenv_round_dynamic_with_fenv_access( + float sf; + double sd; + int si; + long sl; + _Bool sb; + sf = x + y; +// CIR: cir.fadd {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + sf = x - y; +// CIR: cir.fsub {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fsub.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + sf = x * y; +// CIR: cir.fmul {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + sf = x / y; +// CIR: cir.fdiv {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fdiv.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + sf = -x; +// CIR: cir.fneg {{.*}} : !cir.float loc +// LLVM: fneg float + sb = x < y; +// CIR: cir.cmp lt {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call i1 @llvm.experimental.constrained.fcmps.f32({{.*}}, metadata !"olt", metadata !"fpexcept.strict") + sf = (float)i; +// CIR: cir.cast int_to_float {{.*}} : !s32i -> !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.sitofp.f32.i32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + si = (int)x; +// CIR: cir.cast float_to_int {{.*}} : !cir.float -> !s32i {fenv = #cir.fenv} +// LLVM: call i32 @llvm.experimental.constrained.fptosi.i32.f32({{.*}}, metadata !"fpexcept.strict") + sd = (double)x; +// CIR: cir.cast floating {{.*}} : !cir.float -> !cir.double {fenv = #cir.fenv} +// LLVM: call double @llvm.experimental.constrained.fpext.f64.f32({{.*}}, metadata !"fpexcept.strict") + sf = (float)d; +// CIR: cir.cast floating {{.*}} : !cir.double -> !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fptrunc.f32.f64({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + sf = __builtin_sqrtf(x); +// CIR: cir.sqrt {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.sqrt.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + sf = __builtin_powf(x, y); +// CIR: cir.pow {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.pow.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + sf = __builtin_fmaf(x, y, z); +// CIR: cir.fma {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM-STRICT: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") +// LLVM-DEFAULT: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") +// LLVM-DEFAULT-RND: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + sl = __builtin_lroundf(x); +// CIR: cir.lround {{.*}} : !cir.float -> !s64i {fenv = #cir.fenv} +// LLVM: call i64 @llvm.experimental.constrained.lround.i64.f32({{.*}}, metadata !"fpexcept.strict") + sf = __builtin_fmodf(x, y); +// CIR: cir.fmod {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.frem.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") +} + +void test_fenv_round_dynamic_without_fenv_access(float x, float y, float z, int i, double d) { +#pragma STDC FENV_ROUND FE_DYNAMIC +// CIR-LABEL: cir.func {{.*}}@test_fenv_round_dynamic_without_fenv_access( +// LLVM-LABEL: define {{.*}}@test_fenv_round_dynamic_without_fenv_access( + float sf; + double sd; + int si; + long sl; + _Bool sb; + sf = x + y; +// CIR-STRICT: cir.fadd {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.fadd {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.fadd {{.*}} : !cir.float loc +// CIR-DEFAULT-RND: cir.fadd {{.*}} : !cir.float loc +// LLVM-STRICT: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-DEFAULT: fadd float +// LLVM-DEFAULT-RND: fadd float + sf = x - y; +// CIR-STRICT: cir.fsub {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.fsub {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.fsub {{.*}} : !cir.float loc +// CIR-DEFAULT-RND: cir.fsub {{.*}} : !cir.float loc +// LLVM-STRICT: call float @llvm.experimental.constrained.fsub.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.fsub.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-DEFAULT: fsub float +// LLVM-DEFAULT-RND: fsub float + sf = x * y; +// CIR-STRICT: cir.fmul {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.fmul {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.fmul {{.*}} : !cir.float loc +// CIR-DEFAULT-RND: cir.fmul {{.*}} : !cir.float loc +// LLVM-STRICT: call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-DEFAULT: fmul float +// LLVM-DEFAULT-RND: fmul float + sf = x / y; +// CIR-STRICT: cir.fdiv {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.fdiv {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.fdiv {{.*}} : !cir.float loc +// CIR-DEFAULT-RND: cir.fdiv {{.*}} : !cir.float loc +// LLVM-STRICT: call float @llvm.experimental.constrained.fdiv.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.fdiv.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-DEFAULT: fdiv float +// LLVM-DEFAULT-RND: fdiv float + sf = -x; +// CIR: cir.fneg {{.*}} : !cir.float loc +// LLVM: fneg float + sb = x < y; +// CIR-STRICT: cir.cmp lt {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.cmp lt {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.cmp lt {{.*}} : !cir.float +// CIR-DEFAULT-RND: cir.cmp lt {{.*}} : !cir.float +// LLVM-STRICT: call i1 @llvm.experimental.constrained.fcmps.f32({{.*}}, metadata !"olt", metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call i1 @llvm.experimental.constrained.fcmps.f32({{.*}}, metadata !"olt", metadata !"fpexcept.strict") +// LLVM-DEFAULT: fcmp olt float +// LLVM-DEFAULT-RND: fcmp olt float + sf = (float)i; +// CIR-STRICT: cir.cast int_to_float {{.*}} : !s32i -> !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.cast int_to_float {{.*}} : !s32i -> !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.cast int_to_float {{.*}} : !s32i -> !cir.float +// CIR-DEFAULT-RND: cir.cast int_to_float {{.*}} : !s32i -> !cir.float +// LLVM-STRICT: call float @llvm.experimental.constrained.sitofp.f32.i32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.sitofp.f32.i32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-DEFAULT: sitofp i32 {{.*}} to float +// LLVM-DEFAULT-RND: sitofp i32 {{.*}} to float + si = (int)x; +// CIR-STRICT: cir.cast float_to_int {{.*}} : !cir.float -> !s32i {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.cast float_to_int {{.*}} : !cir.float -> !s32i {fenv = #cir.fenv} +// CIR-DEFAULT: cir.cast float_to_int {{.*}} : !cir.float -> !s32i +// CIR-DEFAULT-RND: cir.cast float_to_int {{.*}} : !cir.float -> !s32i +// LLVM-STRICT: call i32 @llvm.experimental.constrained.fptosi.i32.f32({{.*}}, metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call i32 @llvm.experimental.constrained.fptosi.i32.f32({{.*}}, metadata !"fpexcept.strict") +// LLVM-DEFAULT: fptosi float {{.*}} to i32 +// LLVM-DEFAULT-RND: fptosi float {{.*}} to i32 + sd = (double)x; +// CIR-STRICT: cir.cast floating {{.*}} : !cir.float -> !cir.double {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.cast floating {{.*}} : !cir.float -> !cir.double {fenv = #cir.fenv} +// CIR-DEFAULT: cir.cast floating {{.*}} : !cir.float -> !cir.double +// CIR-DEFAULT-RND: cir.cast floating {{.*}} : !cir.float -> !cir.double +// LLVM-STRICT: call double @llvm.experimental.constrained.fpext.f64.f32({{.*}}, metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call double @llvm.experimental.constrained.fpext.f64.f32({{.*}}, metadata !"fpexcept.strict") +// LLVM-DEFAULT: fpext float {{.*}} to double +// LLVM-DEFAULT-RND: fpext float {{.*}} to double + sf = (float)d; +// CIR-STRICT: cir.cast floating {{.*}} : !cir.double -> !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.cast floating {{.*}} : !cir.double -> !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.cast floating {{.*}} : !cir.double -> !cir.float +// CIR-DEFAULT-RND: cir.cast floating {{.*}} : !cir.double -> !cir.float +// LLVM-STRICT: call float @llvm.experimental.constrained.fptrunc.f32.f64({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.fptrunc.f32.f64({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-DEFAULT: fptrunc double {{.*}} to float +// LLVM-DEFAULT-RND: fptrunc double {{.*}} to float + sf = __builtin_sqrtf(x); +// CIR-STRICT: cir.sqrt {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.sqrt {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.sqrt {{.*}} : !cir.float loc +// CIR-DEFAULT-RND: cir.sqrt {{.*}} : !cir.float loc +// LLVM-STRICT: call float @llvm.experimental.constrained.sqrt.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.sqrt.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-DEFAULT: call float @llvm.sqrt.f32 +// LLVM-DEFAULT-RND: call float @llvm.sqrt.f32 + sf = __builtin_powf(x, y); +// CIR-STRICT: cir.pow {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.pow {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.pow {{.*}} : !cir.float loc +// CIR-DEFAULT-RND: cir.pow {{.*}} : !cir.float loc +// LLVM-STRICT: call float @llvm.experimental.constrained.pow.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.pow.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-DEFAULT: call float @llvm.pow.f32 +// LLVM-DEFAULT-RND: call float @llvm.pow.f32 + sf = __builtin_fmaf(x, y, z); +// CIR-STRICT: cir.fma {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.fma {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.fma {{.*}} : !cir.float loc +// CIR-DEFAULT-RND: cir.fma {{.*}} : !cir.float loc +// LLVM-STRICT: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-DEFAULT: call float @llvm.fma.f32 +// LLVM-DEFAULT-RND: call float @llvm.fma.f32 + sl = __builtin_lroundf(x); +// CIR-STRICT: cir.lround {{.*}} : !cir.float -> !s64i {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.lround {{.*}} : !cir.float -> !s64i {fenv = #cir.fenv} +// CIR-DEFAULT: cir.lround {{.*}} : !cir.float -> !s64i +// CIR-DEFAULT-RND: cir.lround {{.*}} : !cir.float -> !s64i +// LLVM-STRICT: call i64 @llvm.experimental.constrained.lround.i64.f32({{.*}}, metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call i64 @llvm.experimental.constrained.lround.i64.f32({{.*}}, metadata !"fpexcept.strict") +// LLVM-DEFAULT: call i64 @llvm.lround.i64.f32 +// LLVM-DEFAULT-RND: call i64 @llvm.lround.i64.f32 + sf = __builtin_fmodf(x, y); +// CIR-STRICT: cir.fmod {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.fmod {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.fmod {{.*}} : !cir.float loc +// CIR-DEFAULT-RND: cir.fmod {{.*}} : !cir.float loc +// LLVM-STRICT: call float @llvm.experimental.constrained.frem.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.frem.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-DEFAULT: frem float +// LLVM-DEFAULT-RND: frem float +} + +#pragma STDC FENV_ACCESS ON +void test_file_scope_fenv_access_on(float x, float y, float z, int i, double d) { +// CIR-LABEL: cir.func {{.*}}@test_file_scope_fenv_access_on( +// LLVM-LABEL: define {{.*}}@test_file_scope_fenv_access_on( + float sf; + double sd; + int si; + long sl; + _Bool sb; + sf = x + y; +// CIR: cir.fadd {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + sf = x - y; +// CIR: cir.fsub {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fsub.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + sf = x * y; +// CIR: cir.fmul {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + sf = x / y; +// CIR: cir.fdiv {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fdiv.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + sf = -x; +// CIR: cir.fneg {{.*}} : !cir.float loc +// LLVM: fneg float + sb = x < y; +// CIR: cir.cmp lt {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call i1 @llvm.experimental.constrained.fcmps.f32({{.*}}, metadata !"olt", metadata !"fpexcept.strict") + sf = (float)i; +// CIR: cir.cast int_to_float {{.*}} : !s32i -> !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.sitofp.f32.i32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + si = (int)x; +// CIR: cir.cast float_to_int {{.*}} : !cir.float -> !s32i {fenv = #cir.fenv} +// LLVM: call i32 @llvm.experimental.constrained.fptosi.i32.f32({{.*}}, metadata !"fpexcept.strict") + sd = (double)x; +// CIR: cir.cast floating {{.*}} : !cir.float -> !cir.double {fenv = #cir.fenv} +// LLVM: call double @llvm.experimental.constrained.fpext.f64.f32({{.*}}, metadata !"fpexcept.strict") + sf = (float)d; +// CIR: cir.cast floating {{.*}} : !cir.double -> !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.fptrunc.f32.f64({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + sf = __builtin_sqrtf(x); +// CIR: cir.sqrt {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.sqrt.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + sf = __builtin_powf(x, y); +// CIR: cir.pow {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.pow.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + sf = __builtin_fmaf(x, y, z); +// CIR: cir.fma {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM-STRICT: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") +// LLVM-DEFAULT: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") +// LLVM-DEFAULT-RND: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + sl = __builtin_lroundf(x); +// CIR: cir.lround {{.*}} : !cir.float -> !s64i {fenv = #cir.fenv} +// LLVM: call i64 @llvm.experimental.constrained.lround.i64.f32({{.*}}, metadata !"fpexcept.strict") + sf = __builtin_fmodf(x, y); +// CIR: cir.fmod {{.*}} : !cir.float {fenv = #cir.fenv} +// LLVM: call float @llvm.experimental.constrained.frem.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") +} + +#pragma STDC FENV_ACCESS OFF +void test_file_scope_fenv_access_off(float x, float y, float z, int i, double d) { +// CIR-LABEL: cir.func {{.*}}@test_file_scope_fenv_access_off( +// LLVM-LABEL: define {{.*}}@test_file_scope_fenv_access_off( + float sf; + double sd; + int si; + long sl; + _Bool sb; + sf = x + y; +// CIR-STRICT: cir.fadd {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.fadd {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.fadd {{.*}} : !cir.float loc +// CIR-DEFAULT-RND: cir.fadd {{.*}} : !cir.float loc +// LLVM-STRICT: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-DEFAULT: fadd float +// LLVM-DEFAULT-RND: fadd float + sf = x - y; +// CIR-STRICT: cir.fsub {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.fsub {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.fsub {{.*}} : !cir.float loc +// CIR-DEFAULT-RND: cir.fsub {{.*}} : !cir.float loc +// LLVM-STRICT: call float @llvm.experimental.constrained.fsub.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.fsub.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-DEFAULT: fsub float +// LLVM-DEFAULT-RND: fsub float + sf = x * y; +// CIR-STRICT: cir.fmul {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.fmul {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.fmul {{.*}} : !cir.float loc +// CIR-DEFAULT-RND: cir.fmul {{.*}} : !cir.float loc +// LLVM-STRICT: call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-DEFAULT: fmul float +// LLVM-DEFAULT-RND: fmul float + sf = x / y; +// CIR-STRICT: cir.fdiv {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.fdiv {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.fdiv {{.*}} : !cir.float loc +// CIR-DEFAULT-RND: cir.fdiv {{.*}} : !cir.float loc +// LLVM-STRICT: call float @llvm.experimental.constrained.fdiv.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.fdiv.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-DEFAULT: fdiv float +// LLVM-DEFAULT-RND: fdiv float + sf = -x; +// CIR: cir.fneg {{.*}} : !cir.float loc +// LLVM: fneg float + sb = x < y; +// CIR-STRICT: cir.cmp lt {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.cmp lt {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.cmp lt {{.*}} : !cir.float +// CIR-DEFAULT-RND: cir.cmp lt {{.*}} : !cir.float +// LLVM-STRICT: call i1 @llvm.experimental.constrained.fcmps.f32({{.*}}, metadata !"olt", metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call i1 @llvm.experimental.constrained.fcmps.f32({{.*}}, metadata !"olt", metadata !"fpexcept.strict") +// LLVM-DEFAULT: fcmp olt float +// LLVM-DEFAULT-RND: fcmp olt float + sf = (float)i; +// CIR-STRICT: cir.cast int_to_float {{.*}} : !s32i -> !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.cast int_to_float {{.*}} : !s32i -> !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.cast int_to_float {{.*}} : !s32i -> !cir.float +// CIR-DEFAULT-RND: cir.cast int_to_float {{.*}} : !s32i -> !cir.float +// LLVM-STRICT: call float @llvm.experimental.constrained.sitofp.f32.i32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.sitofp.f32.i32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-DEFAULT: sitofp i32 {{.*}} to float +// LLVM-DEFAULT-RND: sitofp i32 {{.*}} to float + si = (int)x; +// CIR-STRICT: cir.cast float_to_int {{.*}} : !cir.float -> !s32i {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.cast float_to_int {{.*}} : !cir.float -> !s32i {fenv = #cir.fenv} +// CIR-DEFAULT: cir.cast float_to_int {{.*}} : !cir.float -> !s32i +// CIR-DEFAULT-RND: cir.cast float_to_int {{.*}} : !cir.float -> !s32i +// LLVM-STRICT: call i32 @llvm.experimental.constrained.fptosi.i32.f32({{.*}}, metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call i32 @llvm.experimental.constrained.fptosi.i32.f32({{.*}}, metadata !"fpexcept.strict") +// LLVM-DEFAULT: fptosi float {{.*}} to i32 +// LLVM-DEFAULT-RND: fptosi float {{.*}} to i32 + sd = (double)x; +// CIR-STRICT: cir.cast floating {{.*}} : !cir.float -> !cir.double {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.cast floating {{.*}} : !cir.float -> !cir.double {fenv = #cir.fenv} +// CIR-DEFAULT: cir.cast floating {{.*}} : !cir.float -> !cir.double +// CIR-DEFAULT-RND: cir.cast floating {{.*}} : !cir.float -> !cir.double +// LLVM-STRICT: call double @llvm.experimental.constrained.fpext.f64.f32({{.*}}, metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call double @llvm.experimental.constrained.fpext.f64.f32({{.*}}, metadata !"fpexcept.strict") +// LLVM-DEFAULT: fpext float {{.*}} to double +// LLVM-DEFAULT-RND: fpext float {{.*}} to double + sf = (float)d; +// CIR-STRICT: cir.cast floating {{.*}} : !cir.double -> !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.cast floating {{.*}} : !cir.double -> !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.cast floating {{.*}} : !cir.double -> !cir.float +// CIR-DEFAULT-RND: cir.cast floating {{.*}} : !cir.double -> !cir.float +// LLVM-STRICT: call float @llvm.experimental.constrained.fptrunc.f32.f64({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.fptrunc.f32.f64({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-DEFAULT: fptrunc double {{.*}} to float +// LLVM-DEFAULT-RND: fptrunc double {{.*}} to float + sf = __builtin_sqrtf(x); +// CIR-STRICT: cir.sqrt {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.sqrt {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.sqrt {{.*}} : !cir.float loc +// CIR-DEFAULT-RND: cir.sqrt {{.*}} : !cir.float loc +// LLVM-STRICT: call float @llvm.experimental.constrained.sqrt.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.sqrt.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-DEFAULT: call float @llvm.sqrt.f32 +// LLVM-DEFAULT-RND: call float @llvm.sqrt.f32 + sf = __builtin_powf(x, y); +// CIR-STRICT: cir.pow {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.pow {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.pow {{.*}} : !cir.float loc +// CIR-DEFAULT-RND: cir.pow {{.*}} : !cir.float loc +// LLVM-STRICT: call float @llvm.experimental.constrained.pow.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.pow.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-DEFAULT: call float @llvm.pow.f32 +// LLVM-DEFAULT-RND: call float @llvm.pow.f32 + sf = __builtin_fmaf(x, y, z); +// CIR-STRICT: cir.fma {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.fma {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.fma {{.*}} : !cir.float loc +// CIR-DEFAULT-RND: cir.fma {{.*}} : !cir.float loc +// LLVM-STRICT: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-DEFAULT: call float @llvm.fma.f32 +// LLVM-DEFAULT-RND: call float @llvm.fma.f32 + sl = __builtin_lroundf(x); +// CIR-STRICT: cir.lround {{.*}} : !cir.float -> !s64i {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.lround {{.*}} : !cir.float -> !s64i {fenv = #cir.fenv} +// CIR-DEFAULT: cir.lround {{.*}} : !cir.float -> !s64i +// CIR-DEFAULT-RND: cir.lround {{.*}} : !cir.float -> !s64i +// LLVM-STRICT: call i64 @llvm.experimental.constrained.lround.i64.f32({{.*}}, metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call i64 @llvm.experimental.constrained.lround.i64.f32({{.*}}, metadata !"fpexcept.strict") +// LLVM-DEFAULT: call i64 @llvm.lround.i64.f32 +// LLVM-DEFAULT-RND: call i64 @llvm.lround.i64.f32 + sf = __builtin_fmodf(x, y); +// CIR-STRICT: cir.fmod {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-STRICT-RND: cir.fmod {{.*}} : !cir.float {fenv = #cir.fenv} +// CIR-DEFAULT: cir.fmod {{.*}} : !cir.float loc +// CIR-DEFAULT-RND: cir.fmod {{.*}} : !cir.float loc +// LLVM-STRICT: call float @llvm.experimental.constrained.frem.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.frem.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-DEFAULT: frem float +// LLVM-DEFAULT-RND: frem float +} + +// LLVM-STRICT: attributes #[[$STRICT_ATTR]] = { {{.*}}strictfp{{.*}} } +// LLVM-STRICT-RND: attributes #[[$STRICT_ATTR]] = { {{.*}}strictfp{{.*}} } +// LLVM-DEFAULT-RND: attributes #[[$STRICT_ATTR]] = { {{.*}}strictfp{{.*}} } diff --git a/clang/test/CIR/Lowering/fenv.cir b/clang/test/CIR/Lowering/fenv.cir index 14f334a09d11c..282d51d2e73a7 100644 --- a/clang/test/CIR/Lowering/fenv.cir +++ b/clang/test/CIR/Lowering/fenv.cir @@ -54,7 +54,10 @@ module { %1 = cir.fadd %a, %b : !cir.float {fenv = #cir.fenv} // CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.ignore"> // CHECK: llvm.call_intrinsic "llvm.experimental.constrained.fadd" - %2 = cir.fadd %a, %b : !cir.float {fenv = #cir.fenv} + %2 = cir.fadd %a, %b : !cir.float {fenv = #cir.fenv} + // CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.ignore"> + // CHECK: llvm.call_intrinsic "llvm.experimental.constrained.fadd" + %3 = cir.fadd %a, %b : !cir.float {fenv = #cir.fenv} cir.return } @@ -114,6 +117,16 @@ module { cir.return } + // CHECK-LABEL: llvm.func @ternary_fp_builtins + cir.func @ternary_fp_builtins(%a: !cir.double, %b: !cir.double, + %c: !cir.double) { + // CHECK: llvm.call_intrinsic "llvm.experimental.constrained.fma"(%{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}) : (f64, f64, f64, !llvm.metadata, !llvm.metadata) -> f64 + %0 = cir.fma %a, %b, %c : !cir.double {fenv = #cir.fenv} + // CHECK: llvm.intr.fma(%{{.*}}, %{{.*}}, %{{.*}}) : (f64, f64, f64) -> f64 + %1 = cir.fma %a, %b, %c : !cir.double + cir.return + } + // CHECK-LABEL: llvm.func @minmax_num cir.func @minmax_num(%a: !cir.double, %b: !cir.double) { // CHECK: llvm.call_intrinsic "llvm.experimental.constrained.maxnum"(%{{.*}}, %{{.*}}, %{{.*}}) {fastmathFlags = #llvm.fastmath} : (f64, f64, !llvm.metadata) -> f64 diff --git a/clang/test/CIR/Lowering/strictfp.cir b/clang/test/CIR/Lowering/strictfp.cir new file mode 100644 index 0000000000000..66caa04ca9a9c --- /dev/null +++ b/clang/test/CIR/Lowering/strictfp.cir @@ -0,0 +1,28 @@ +// RUN: cir-opt %s -cir-to-llvm -o - | FileCheck %s -check-prefix=MLIR +// RUN: cir-translate %s -cir-to-llvmir --disable-cc-lowering | FileCheck %s -check-prefix=LLVM + +// Functions tagged with the `strictfp` CIR function attribute should be +// lowered to `llvm.func` operations whose `passthrough` array contains +// `"strictfp"`, and then translated to LLVM IR functions carrying the +// `strictfp` attribute. + +module { + cir.func @strict_fn() attributes {strictfp} { + cir.return + } + + cir.func @plain_fn() { + cir.return + } +} + +// MLIR-LABEL: llvm.func @strict_fn() +// MLIR-SAME: passthrough = ["strictfp"] +// MLIR-LABEL: llvm.func @plain_fn() +// MLIR-NOT: passthrough +// MLIR-NOT: strictfp + +// LLVM: define void @strict_fn() #[[STRICT:[0-9]+]] +// LLVM-LABEL: define void @plain_fn() +// LLVM-NOT: #[[STRICT]] +// LLVM: attributes #[[STRICT]] = {{.*}}strictfp