Index: clang/docs/UsersManual.rst =================================================================== --- clang/docs/UsersManual.rst +++ clang/docs/UsersManual.rst @@ -1296,6 +1296,30 @@ With the 'no-strict' option, Clang attempts to match the overflowing behavior of the target's native float-to-int conversion instructions. +.. option:: -fp-model=[values] + + Specify floating point behavior. + + Valid values are: ``precise``, ``strict``, ``fast``, ``except``, + and ``except-``. Note that -fp-model=except[-] can be combined with the + other three settings for this option. + + ``precise `` Disables optimizations that are not value-safe on + floating-point data, although FP contraction (FMA) is enabled. + * ``strict`` Enables precise and except, and disables contractions (FMA). + * ``fast`` Equivalent to -ffast-math + * ``except`` Honor strict floating-point exception semantics. + * ``except-`` Ignore floating-point exceptions. + +.. option:: -fp-speculation=[values] + + Specify the mode in which to speculate on floating-point operations. + + Valid values are: ``fast``, ``strict``, and ``safe``. + * ``fast`` The compiler will speculate on floating-point operations. + * ``strict`` The compiler will not speculate on floating-point operations. + * ``safe`` Disable speculation if there is a possibility that speculation may cause a floating-point exception. + .. option:: -fwhole-program-vtables Enable whole-program vtable optimizations, such as single-implementation Index: clang/include/clang/Basic/LangOptions.h =================================================================== --- clang/include/clang/Basic/LangOptions.h +++ clang/include/clang/Basic/LangOptions.h @@ -178,6 +178,28 @@ FEA_On }; + enum FPModelKind { + // -fp-model option is not specified + FPM_Off, + FPM_Precise, + FPM_Strict, + FPM_Fast + }; + + enum FPModelExceptKind { + // -fp-except option not specified + FPME_Off, + FPME_Except, + FPME_NoExcept + }; + + enum FPSpeculationKind { + // -fp-speculation option not specified + FPS_Off, + FPS_Fast, + FPS_Strict, + FPS_Safe + }; public: /// Set of enabled sanitizers. @@ -301,6 +323,43 @@ /// Return the OpenCL C or C++ version as a VersionTuple. VersionTuple getOpenCLVersionTuple() const; + + /// Floating point model options + class FPModelOptions { + public: + FPModelOptions() : FPM(LangOptions::FPM_Off), + FPME(LangOptions::FPME_Off), + FPS(LangOptions::FPS_Off) {} + + LangOptions::FPModelKind getFPModelSetting() const { + return FPM; + } + void setFPModelSetting(LangOptions::FPModelKind Value) {FPM = Value;} + + LangOptions::FPModelExceptKind getFPModelExceptSetting() const { + return FPME; + } + void setFPModelExceptSetting(LangOptions::FPModelExceptKind Value) { + FPME = Value; + } + + LangOptions::FPSpeculationKind getFPSpeculationSetting() const { + return FPS; + } + void setFPSpeculationSetting(LangOptions::FPSpeculationKind Value) { + FPS = Value; + } + + private: + LangOptions::FPModelKind FPM = LangOptions::FPM_Off; + LangOptions::FPModelExceptKind FPME = LangOptions::FPME_Off; + LangOptions::FPSpeculationKind FPS = LangOptions::FPS_Off; + }; + + FPModelOptions& getFPMOptions() { return fpm_options; } + FPModelOptions getFPMOptions() const { return fpm_options; } +private: + FPModelOptions fpm_options; }; /// Floating point control options Index: clang/include/clang/Driver/CC1Options.td =================================================================== --- clang/include/clang/Driver/CC1Options.td +++ clang/include/clang/Driver/CC1Options.td @@ -783,6 +783,10 @@ HelpText<"Use the native __fp16 type for arguments and returns (and skip ABI-specific lowering)">; def fallow_half_arguments_and_returns : Flag<["-"], "fallow-half-arguments-and-returns">, HelpText<"Allow function arguments and returns of type half">; +def fp_model_except : Flag<["-"], "fp-model-except">, + HelpText<"Controls the constrained exception setting of floating-point calculations.">; +def no_fp_model_except : Flag<["-"], "no-fp-model-except">, + HelpText<"Exceptions in floating-point calculations are ignored.">; def fdefault_calling_conv_EQ : Joined<["-"], "fdefault-calling-conv=">, HelpText<"Set default calling convention">, Values<"cdecl,fastcall,stdcall,vectorcall,regcall">; def finclude_default_header : Flag<["-"], "finclude-default-header">, Index: clang/include/clang/Driver/Options.td =================================================================== --- clang/include/clang/Driver/Options.td +++ clang/include/clang/Driver/Options.td @@ -910,6 +910,10 @@ def : Flag<["-"], "fno-extended-identifiers">, Group, Flags<[Unsupported]>; def fhosted : Flag<["-"], "fhosted">, Group; def fdenormal_fp_math_EQ : Joined<["-"], "fdenormal-fp-math=">, Group, Flags<[CC1Option]>; +def fp_model_EQ : Joined<["-"], "fp-model=">, Group, Flags<[CC1Option]>, + HelpText<"Controls the semantics of floating-point calculations.">; +def fp_speculation_EQ : Joined<["-"], "fp-speculation=">, Group, Flags<[CC1Option]>, + HelpText<"Specifies the mode in which to speculate on floating-point operations.">; def ffast_math : Flag<["-"], "ffast-math">, Group, Flags<[CC1Option]>, HelpText<"Allow aggressive, lossy floating-point optimizations">; def fno_fast_math : Flag<["-"], "fno-fast-math">, Group; Index: clang/lib/CodeGen/CodeGenFunction.h =================================================================== --- clang/lib/CodeGen/CodeGenFunction.h +++ clang/lib/CodeGen/CodeGenFunction.h @@ -4148,6 +4148,9 @@ /// point operation, expressed as the maximum relative error in ulp. void SetFPAccuracy(llvm::Value *Val, float Accuracy); + /// SetFPModel - Control floating point behavior via fp-model settings. + void SetFPModel(void); + private: llvm::MDNode *getRangeForLoadFromType(QualType Ty); void EmitReturnOfRValue(RValue RV, QualType Ty); Index: clang/lib/CodeGen/CodeGenFunction.cpp =================================================================== --- clang/lib/CodeGen/CodeGenFunction.cpp +++ clang/lib/CodeGen/CodeGenFunction.cpp @@ -33,6 +33,7 @@ #include "clang/Frontend/FrontendDiagnostic.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/Dominators.h" +#include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/Intrinsics.h" #include "llvm/IR/MDBuilder.h" #include "llvm/IR/Operator.h" @@ -90,6 +91,7 @@ FMF.setAllowReassoc(); } Builder.setFastMathFlags(FMF); + SetFPModel(); } CodeGenFunction::~CodeGenFunction() { @@ -105,6 +107,82 @@ CGM.getOpenMPRuntime().functionFinished(*this); } +void CodeGenFunction::SetFPModel(void) +{ + auto fpModel = getLangOpts().getFPMOptions().getFPModelSetting(); + auto fpModelExcept = getLangOpts().getFPMOptions().getFPModelExceptSetting(); + auto fpSpeculation = getLangOpts().getFPMOptions().getFPSpeculationSetting(); + + // Translate the compiler options into + // the 3 settings that are transmitted to the IR Builder + bool IsConstrainedRounding = false; + bool IsConstrainedExcept = false; + llvm::ConstrainedFPIntrinsic::RoundingMode ConstrainedRoundingMD; + llvm::ConstrainedFPIntrinsic::ExceptionBehavior ConstrainedExceptMD; + + switch (fpModel) { + case LangOptions::FPM_Off: + case LangOptions::FPM_Precise: + case LangOptions::FPM_Fast: + break; + case LangOptions::FPM_Strict: + IsConstrainedRounding = true; + ConstrainedRoundingMD = llvm::ConstrainedFPIntrinsic::rmDynamic; + IsConstrainedExcept = true; + ConstrainedExceptMD = llvm::ConstrainedFPIntrinsic::ebStrict; + break; + default: + llvm_unreachable("Unsupported FP Model"); + } + + switch (fpModelExcept) { + case LangOptions::FPME_Off: + break; + case LangOptions::FPME_Except: + IsConstrainedExcept = true; + ConstrainedExceptMD = llvm::ConstrainedFPIntrinsic::ebStrict; + break; + case LangOptions::FPME_NoExcept: + IsConstrainedExcept = true; + ConstrainedExceptMD = llvm::ConstrainedFPIntrinsic::ebIgnore; + break; + default: + llvm_unreachable("Unsupported FP Except Model"); + } + + switch (fpSpeculation) { + case LangOptions::FPS_Off: + break; + case LangOptions::FPS_Fast: + IsConstrainedExcept = true; + ConstrainedExceptMD = llvm::ConstrainedFPIntrinsic::ebIgnore; + break; + case LangOptions::FPS_Strict: + IsConstrainedExcept = true; + ConstrainedExceptMD = llvm::ConstrainedFPIntrinsic::ebStrict; + break; + case LangOptions::FPS_Safe: + IsConstrainedExcept = true; + ConstrainedExceptMD = llvm::ConstrainedFPIntrinsic::ebMayTrap; + break; + default: + llvm_unreachable("Unsupported FP Speculation"); + } + + if (IsConstrainedExcept && !IsConstrainedRounding) { + // If the rounding mode isn't set explicitly above, then use ebToNearest + // as the value when the constrained intrinsic is created + IsConstrainedRounding = true; + ConstrainedRoundingMD = llvm::ConstrainedFPIntrinsic::rmToNearest; + } + + if (IsConstrainedExcept || IsConstrainedRounding) { + Builder.setIsFPConstrained(true); + Builder.setDefaultConstrainedRounding(ConstrainedRoundingMD); + Builder.setDefaultConstrainedExcept(ConstrainedExceptMD); + } +} + CharUnits CodeGenFunction::getNaturalPointeeTypeAlignment(QualType T, LValueBaseInfo *BaseInfo, TBAAAccessInfo *TBAAInfo) { Index: clang/lib/Driver/ToolChains/Clang.cpp =================================================================== --- clang/lib/Driver/ToolChains/Clang.cpp +++ clang/lib/Driver/ToolChains/Clang.cpp @@ -2277,6 +2277,11 @@ bool TrappingMath = true; StringRef DenormalFPMath = ""; StringRef FPContract = ""; + LangOptions::FPModelKind FPModel = LangOptions::FPM_Off; + LangOptions::FPModelExceptKind FPModelExcept = + LangOptions::FPME_Off; + LangOptions::FPSpeculationKind FPSpeculation = + LangOptions::FPS_Off; if (const Arg *A = Args.getLastArg(options::OPT_flimited_precision_EQ)) { CmdArgs.push_back("-mlimit-float-precision"); @@ -2287,6 +2292,39 @@ switch (A->getOption().getID()) { // If this isn't an FP option skip the claim below default: continue; + // Options controlling floating point model and speculation + case options::OPT_fp_model_EQ: { + StringRef Val = A->getValue(); + if (Val == "precise") + FPModel = LangOptions::FPM_Precise; + else if (Val == "strict") + FPModel = LangOptions::FPM_Strict; + else if (Val == "except") + FPModelExcept = LangOptions::FPME_Except; + else if (Val == "except-") + FPModelExcept = LangOptions::FPME_NoExcept; + else if (Val == "fast") + FPModel = LangOptions::FPM_Fast; + else { + D.Diag(diag::err_drv_invalid_value) << "-fp-model" << Val; + continue; + } + break; + } + case options::OPT_fp_speculation_EQ: { + StringRef Val = A->getValue(); + if (Val == "fast") + FPSpeculation = LangOptions::FPS_Fast; + else if (Val == "strict") + FPSpeculation = LangOptions::FPS_Strict; + else if (Val == "safe") + FPSpeculation = LangOptions::FPS_Safe; + else { + D.Diag(diag::err_drv_invalid_value) << A->getAsString(Args) << Val; + continue; + } + break; + } // Options controlling individual features case options::OPT_fhonor_infinities: HonorINFs = true; break; @@ -2380,6 +2418,56 @@ A->claim(); } + if ((FPModelExcept == LangOptions::FPME_Except) && + (FPModel == LangOptions::FPM_Fast)) + D.Diag(diag::err_drv_argument_not_allowed_with) << "fp-model=fast" + << "fp-model=except"; + + switch (FPModel) { + case LangOptions::FPM_Precise: + CmdArgs.push_back("-fp-model=precise"); + break; + case LangOptions::FPM_Strict: + CmdArgs.push_back("-fp-model=strict"); + break; + case LangOptions::FPM_Fast: + CmdArgs.push_back("-fp-model=fast"); + break; + case LangOptions::FPM_Off: + break; + default: + llvm_unreachable("Unrecognized FPModel"); + } + + switch (FPModelExcept) { + case LangOptions::FPME_Except: + CmdArgs.push_back("-fp-model-except"); + break; + case LangOptions::FPME_NoExcept: + CmdArgs.push_back("-no-fpmodel-except-"); + break; + case LangOptions::FPME_Off: + break; + default: + llvm_unreachable("Unrecognized FPModel"); + } + + switch (FPSpeculation) { + case LangOptions::FPS_Fast: + CmdArgs.push_back("-fp-speculation=fast"); + break; + case LangOptions::FPS_Strict: + CmdArgs.push_back("-fp-speculation=strict"); + break; + case LangOptions::FPS_Safe: + CmdArgs.push_back("-fp-speculation=safe"); + break; + case LangOptions::FPS_Off: + break; + default: + llvm_unreachable("Unrecognized FPSpeculation"); + } + if (!HonorINFs) CmdArgs.push_back("-menable-no-infs"); Index: clang/lib/Frontend/CompilerInvocation.cpp =================================================================== --- clang/lib/Frontend/CompilerInvocation.cpp +++ clang/lib/Frontend/CompilerInvocation.cpp @@ -3039,6 +3039,59 @@ Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Val; } + LangOptions::FPModelKind FPM = LangOptions::FPM_Off; + if (Arg *A = Args.getLastArg(OPT_fp_model_EQ)) { + StringRef Val = A->getValue(); + if (Val == "precise") + FPM = LangOptions::FPM_Precise; + else if (Val == "strict") + FPM = LangOptions::FPM_Strict; + else if (Val == "fast") + FPM = LangOptions::FPM_Fast; + else + llvm_unreachable("invalid -fp-model setting"); + } + Opts.getFPMOptions().setFPModelSetting(FPM); + + LangOptions::FPModelExceptKind FPME = LangOptions::FPME_Off; + if (const Arg *A = + Args.getLastArg(OPT_fp_model_except, OPT_no_fp_model_except)) + switch (A->getOption().getID()) { + case OPT_fp_model_except: + FPME = LangOptions::FPME_Except; + break; + case OPT_no_fp_model_except: + FPME = LangOptions::FPME_NoExcept; + break; + default: + llvm_unreachable("invalid -fp-model-except setting"); + } + Opts.getFPMOptions().setFPModelExceptSetting(FPME); + + LangOptions::FPSpeculationKind FPS = LangOptions::FPS_Off; + if (Arg *A = Args.getLastArg(OPT_fp_speculation_EQ)) { + StringRef Val = A->getValue(); + if (Val == "fast") + FPS = LangOptions::FPS_Fast; + else if (Val == "strict") + FPS = LangOptions::FPS_Strict; + else if (Val == "safe") + FPS = LangOptions::FPS_Safe; + else + llvm_unreachable("invalid -fp-speculation setting"); + Opts.getFPMOptions().setFPSpeculationSetting(FPS); + } + + if (FPM == LangOptions::FPM_Precise) + // This doesn't correspond to constrained fp, equivalent to -fp-contract=on + Opts.setDefaultFPContractMode(LangOptions::FPC_On); + else if (FPM == LangOptions::FPM_Fast) { + // This doesn't correspond to constrained fp, equivalent to -ffast-math + Opts.FastMath = true; + Opts.FiniteMathOnly = true; + Opts.setDefaultFPContractMode(LangOptions::FPC_Fast); + } + Opts.RetainCommentsFromSystemHeaders = Args.hasArg(OPT_fretain_comments_from_system_headers); @@ -3404,6 +3457,15 @@ // FIXME: Should we really be calling this for an Language::Asm input? ParseLangArgs(LangOpts, Args, DashX, Res.getTargetOpts(), Res.getPreprocessorOpts(), Diags); + auto fpm = LangOpts.getFPMOptions().getFPModelSetting(); + if (fpm == LangOptions::FPM_Fast) { + auto CGOpts = Res.getCodeGenOpts(); + CGOpts.NoInfsFPMath = true; + CGOpts.UnsafeFPMath = true; + CGOpts.ReciprocalMath = true; + CGOpts.NoTrappingMath = true; + CGOpts.NoSignedZeros = true; + } if (Res.getFrontendOpts().ProgramAction == frontend::RewriteObjC) LangOpts.ObjCExceptions = 1; if (T.isOSDarwin() && DashX.isPreprocessed()) { Index: clang/test/CodeGen/fpconstrained.c =================================================================== --- /dev/null +++ clang/test/CodeGen/fpconstrained.c @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 -fp-model=strict -emit-llvm -o - %s | FileCheck %s -check-prefix=STRICT +// RUN: %clang_cc1 -fp-model=strict -fp-model-except -emit-llvm -o - %s | FileCheck %s -check-prefix=STRICTEXCEPT +// RUN: %clang_cc1 -fp-model=strict -no-fp-model-except -emit-llvm -o - %s | FileCheck %s -check-prefix=STRICTNOEXCEPT +// RUN: %clang_cc1 -fp-model-except -emit-llvm -o - %s | FileCheck %s -check-prefix=EXCEPT +// RUN: %clang_cc1 -no-fp-model-except -emit-llvm -o - %s | FileCheck %s -check-prefix=NOEXCEPT +// RUN: %clang_cc1 -fp-model=precise -emit-llvm -o - %s | FileCheck %s -check-prefix=PRECISE +// RUN: %clang_cc1 -fp-model=fast -emit-llvm -o - %s | FileCheck %s -check-prefix=FAST +// RUN: %clang_cc1 -fp-model=fast -fp-speculation=fast -emit-llvm -o - %s | FileCheck %s -check-prefix=NOEXCEPT +// RUN: %clang_cc1 -fp-model=fast -fp-speculation=strict -emit-llvm -o - %s | FileCheck %s -check-prefix=EXCEPT +// RUN: %clang_cc1 -fp-model=fast -fp-speculation=safe -emit-llvm -o - %s | FileCheck %s -check-prefix=MAYTRAP +float f0, f1, f2; + +void foo(void) { + // CHECK-LABEL: define {{.*}}void @foo() + + // MAYTRAP: llvm.experimental.constrained.fadd.f32(float %0, float %1, metadata !"round.tonearest", metadata !"fpexcept.maytrap") + // EXCEPT: llvm.experimental.constrained.fadd.f32(float %0, float %1, metadata !"round.tonearest", metadata !"fpexcept.strict") + // NOEXCEPT: llvm.experimental.constrained.fadd.f32(float %0, float %1, metadata !"round.tonearest", metadata !"fpexcept.ignore") + // STRICT: llvm.experimental.constrained.fadd.f32(float %0, float %1, metadata !"round.dynamic", metadata !"fpexcept.strict") + // STRICTEXCEPT: llvm.experimental.constrained.fadd.f32(float %0, float %1, metadata !"round.dynamic", metadata !"fpexcept.strict") + // STRICTNOEXCEPT: llvm.experimental.constrained.fadd.f32(float %0, float %1, metadata !"round.dynamic", metadata !"fpexcept.ignore") + // PRECISE: fadd float + // FAST: fadd fast + f0 = f1 + f2; + + // CHECK: ret +} Index: clang/test/Driver/clang_f_opts.c =================================================================== --- clang/test/Driver/clang_f_opts.c +++ clang/test/Driver/clang_f_opts.c @@ -217,6 +217,11 @@ // RUN: %clang -### -S -fexec-charset=iso-8859-1 -o /dev/null %s 2>&1 | FileCheck -check-prefix=CHECK-INVALID-INPUT-CHARSET %s // CHECK-INVALID-INPUT-CHARSET: error: invalid value 'iso-8859-1' in '-fexec-charset=iso-8859-1' +// RUN: %clang -### -S -fp-model=fast -fp-model=except -o /dev/null %s 2>&1 | FileCheck -check-prefix=CHECK-INVALID-FAST-EXCEPT %s +// RUN: %clang -### -S -fp-model=fast -fp-model=except -o /dev/null %s 2>&1 | FileCheck -check-prefix=CHECK-INVALID-FAST-EXCEPT %s +// CHECK-INVALID-FAST-EXCEPT: error: invalid argument 'fp-model=fast' not allowed with 'fp-model=except' +// + // Test that we don't error on these. // RUN: %clang -### -S -Werror \ // RUN: -falign-functions -falign-functions=2 -fno-align-functions \