Index: clang/include/clang/Basic/FPOptions.def =================================================================== --- clang/include/clang/Basic/FPOptions.def +++ clang/include/clang/Basic/FPOptions.def @@ -27,4 +27,5 @@ OPTION(FPEvalMethod, LangOptions::FPEvalMethodKind, 2, AllowApproxFunc) OPTION(Float16ExcessPrecision, LangOptions::ExcessPrecisionKind, 2, FPEvalMethod) OPTION(BFloat16ExcessPrecision, LangOptions::ExcessPrecisionKind, 2, FPEvalMethod) +OPTION(MathErrno, bool, 1, BFloat16ExcessPrecision) #undef OPTION Index: clang/include/clang/Basic/LangOptions.h =================================================================== --- clang/include/clang/Basic/LangOptions.h +++ clang/include/clang/Basic/LangOptions.h @@ -850,6 +850,7 @@ setNoSignedZeroOverride(!Value); setAllowReciprocalOverride(!Value); setAllowApproxFuncOverride(!Value); + setMathErrnoOverride(Value); if (Value) /* Precise mode implies fp_contract=on and disables ffast-math */ setAllowFPContractWithinStatement(); Index: clang/lib/CodeGen/CGBuiltin.cpp =================================================================== --- clang/lib/CodeGen/CGBuiltin.cpp +++ clang/lib/CodeGen/CGBuiltin.cpp @@ -2246,6 +2246,29 @@ const unsigned BuiltinIDIfNoAsmLabel = FD->hasAttr() ? 0 : BuiltinID; + // MSVC takes into account math-errno when compiling at -O2 level in + // the presence of a '#pragma float_control precise(on/off)'. + // ErrnoWithO2OrFastMath checks for that. + // When compiling with 'ffast-math' check if 'math-errno' is overriden, or + // if the '__attribute__((optnone)) is used. + // ErrnoWithO2OrFastMath and OptNoneWithFastMath check for that. + // In both of these cases don't emit intrinsics. + // At -O0 level, no intrinsic should be generated. + bool MathErrnoOverrride = false; + if (E->hasStoredFPFeatures()) { + FPOptionsOverride OP = E->getFPFeatures(); + if (OP.hasMathErrnoOverride()) + MathErrnoOverrride = OP.getMathErrnoOverride(); + } + bool ErrnoWithO2OrFastMath = + MathErrnoOverrride && (CGM.getCodeGenOpts().OptimizationLevel == 2 || + CGM.getLangOpts().FastMath); + bool OptNoneWithFastMath = + CurFuncDecl->hasAttr() && CGM.getLangOpts().FastMath; + bool EmitIntrinsic = + ((CGM.getCodeGenOpts().OptimizationLevel != 0) && + !OptNoneWithFastMath && !ErrnoWithO2OrFastMath); + // There are LLVM math intrinsics/instructions corresponding to math library // functions except the LLVM op will never set errno while the math library // might. Also, math builtins have the same semantics as their math library @@ -2257,9 +2280,11 @@ getContext().BuiltinInfo.isConstWithoutErrnoAndExceptions(BuiltinID); bool ConstWithoutExceptions = getContext().BuiltinInfo.isConstWithoutExceptions(BuiltinID); - if (FD->hasAttr() || - ((ConstWithoutErrnoAndExceptions || ConstWithoutExceptions) && - (!ConstWithoutErrnoAndExceptions || (!getLangOpts().MathErrno)))) { + + if ((FD->hasAttr() && EmitIntrinsic) || EmitIntrinsic || + ((ConstWithoutErrnoAndExceptions || ConstWithoutExceptions) && + (!ConstWithoutErrnoAndExceptions || + (!getLangOpts().MathErrno && EmitIntrinsic)))) { switch (BuiltinIDIfNoAsmLabel) { case Builtin::BIceil: case Builtin::BIceilf: Index: clang/lib/CodeGen/CGCall.cpp =================================================================== --- clang/lib/CodeGen/CGCall.cpp +++ clang/lib/CodeGen/CGCall.cpp @@ -2216,6 +2216,17 @@ return Mask; } +void CodeGenModule::AdjustMemoryAttribute(StringRef Name, + CGCalleeInfo CalleeInfo, + llvm::AttributeList &Attrs) { + if (Attrs.getMemoryEffects().getModRef() == llvm::ModRefInfo::NoModRef) { + Attrs = Attrs.removeFnAttribute(getLLVMContext(), llvm::Attribute::Memory); + llvm::Attribute MemoryAttr = llvm::Attribute::getWithMemoryEffects( + getLLVMContext(), llvm::MemoryEffects::writeOnly()); + Attrs = Attrs.addFnAttribute(getLLVMContext(), MemoryAttr); + } +} + /// Construct the IR attribute list of a function or call. /// /// When adding an attribute, please consider where it should be handled: @@ -5463,11 +5474,17 @@ /*AttrOnCallSite=*/true, /*IsThunk=*/false); - if (const FunctionDecl *FD = dyn_cast_or_null(CurFuncDecl)) + if (const FunctionDecl *FD = dyn_cast_or_null(CurFuncDecl)) { if (FD->hasAttr()) // All calls within a strictfp function are marked strictfp Attrs = Attrs.addFnAttribute(getLLVMContext(), llvm::Attribute::StrictFP); + // If -ffast-math is enabled and the function is guarded by an '__attribute__((optnone)) + // adjust the memory attribute so the BE emits the library call instead of the intrinsic. + if (FD->hasAttr() && getLangOpts().FastMath) + CGM.AdjustMemoryAttribute(CalleePtr->getName(), Callee.getAbstractInfo(), + Attrs); + } // Add call-site nomerge attribute if exists. if (InNoMergeAttributedStmt) Attrs = Attrs.addFnAttribute(getLLVMContext(), llvm::Attribute::NoMerge); Index: clang/lib/CodeGen/CodeGenModule.h =================================================================== --- clang/lib/CodeGen/CodeGenModule.h +++ clang/lib/CodeGen/CodeGenModule.h @@ -1254,6 +1254,12 @@ llvm::AttributeList &Attrs, unsigned &CallingConv, bool AttrOnCallSite, bool IsThunk); + /// Adjust Memory attribute to ensure that the BE gets the right attribute + // in order to generate the library call or the intrinsic for the function + // name 'Name'. + void AdjustMemoryAttribute(StringRef Name, + CGCalleeInfo CalleeInfo, llvm::AttributeList &Attrs); + /// Adds attributes to F according to our CodeGenOptions and LangOptions, as /// though we had emitted it ourselves. We remove any attributes on F that /// conflict with the attributes we add here. Index: clang/test/CodeGen/math-errno.c =================================================================== --- /dev/null +++ clang/test/CodeGen/math-errno.c @@ -0,0 +1,66 @@ +// -O2 +// RUN: %clang_cc1 -Wno-implicit-function-declaration \ +// RUN: -ffp-contract=on -fno-rounding-math -O2 -emit-llvm -o - %s \ +// RUN: | FileCheck %s + +// -ffast-math +// RUN: %clang_cc1 -Wno-implicit-function-declaration \ +// RUN: -menable-no-infs -menable-no-nans -fapprox-func \ +// RUN: -funsafe-math-optimizations -fno-signed-zeros -mreassociate \ +// RUN: -freciprocal-math -ffp-contract=fast -fno-rounding-math -ffast-math \ +// RUN: -ffinite-math-only -ffast-math -emit-llvm -o - %s \ +// RUN: | FileCheck %s -check-prefix=FAST + +// -O0 +// RUN: %clang_cc1 -Wno-implicit-function-declaration \ +// RUN: -ffp-contract=on -fno-rounding-math -O2 -emit-llvm -o - %s \ +// RUN: | FileCheck %s -check-prefix=NOOPT + +#pragma float_control(precise,on) +float f1(float x) { + return sqrtf(x); +} + +// CHECK-LABEL: define dso_local float @f1 +// CHECK: tail call float @sqrtf(float noundef {{.*}}) #[[ATTR4_O2:[0-9]+]] + +// FAST-LABEL: define dso_local nofpclass(nan inf) float @f1 +// FAST: call fast nofpclass(nan inf) float @sqrtf(float noundef nofpclass(nan inf) {{.*}}) #[[ATTR3_FAST:[0-9]+]] +// +// NOOPT-LABEL: define dso_local float @f1 +// NOOPT: tail call float @sqrtf(float noundef {{.*}}) #[[ATTR4_NOOPT:[0-9]+]] + + +#pragma float_control(precise,off) +float f2(float x) { + return sqrtf(x); +} + +// CHECK-LABEL: define dso_local float @f2 +// CHECK: tail call float @llvm.sqrt.f32(float {{.*}}) + +// FAST-LABEL: define dso_local nofpclass(nan inf) float @f2 +// FAST: call fast nofpclass(nan inf) float @sqrtf(float noundef nofpclass(nan inf) {{.*}}) #[[ATTR3_FAST]] + +// NOOPT-LABEL: define dso_local float @f2 +// NOOPT: tail call float @llvm.sqrt.f32(float {{.*}}) + + +__attribute__((optnone)) +float f3(float x) { + x = sqrtf(x); + return x; +} + +// CHECK-LABEL: define dso_local float @f3 +// CHECK: call float @llvm.sqrt.f32(float {{.*}}) + +// FAST-LABEL: define dso_local nofpclass(nan inf) float @f3 +// FAST: call fast nofpclass(nan inf) float @sqrtf(float noundef nofpclass(nan inf) {{.*}}) #[[ATTR4_FAST:[0-9]+]] + +// NOOPT-LABEL: define dso_local float @f3 +// NOOPT: call float @llvm.sqrt.f32(float {{.*}}) + +// CHECK: [[ATTR4_O2]] = { nounwind willreturn memory(none) } +// FAST: [[ATTR3_FAST]] = { nounwind willreturn memory(none) } +// NOOPT: [[ATTR4_NOOPT]] = { nounwind willreturn memory(none) }