Index: clang/include/clang/Basic/LangOptions.h =================================================================== --- clang/include/clang/Basic/LangOptions.h +++ clang/include/clang/Basic/LangOptions.h @@ -21,6 +21,7 @@ #include "clang/Basic/Visibility.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Triple.h" +#include "llvm/IR/FPState.h" #include #include @@ -178,6 +179,29 @@ FEA_On }; + llvm::FPState::FPModelKind DefaultFPModel = llvm::FPState::FPM_Off; + llvm::FPState::FPModelKind getDefaultFPModel() const { + return DefaultFPModel; + } + void setDefaultFPModel(llvm::FPState::FPModelKind Value) { + DefaultFPModel = Value; + } + llvm::FPState::FPModelExceptKind DefaultFPModelExcept = + llvm::FPState::FPME_Off; + llvm::FPState::FPModelExceptKind getDefaultFPModelExcept() const { + return DefaultFPModelExcept; + } + void setDefaultFPModelExcept(llvm::FPState::FPModelExceptKind Value) { + DefaultFPModelExcept = Value; + } + llvm::FPState::FPSpeculationKind DefaultFPSpeculation = + llvm::FPState::FPS_Off; + llvm::FPState::FPSpeculationKind getDefaultFPSpeculation() const { + return DefaultFPSpeculation; + } + void setDefaultFPSpeculation(llvm::FPState::FPSpeculationKind Value) { + DefaultFPSpeculation = Value; + } public: /// Set of enabled sanitizers. Index: clang/include/clang/Driver/CC1Options.td =================================================================== --- clang/include/clang/Driver/CC1Options.td +++ clang/include/clang/Driver/CC1Options.td @@ -781,6 +781,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.cpp =================================================================== --- clang/lib/CodeGen/CodeGenFunction.cpp +++ clang/lib/CodeGen/CodeGenFunction.cpp @@ -90,6 +90,11 @@ FMF.setAllowReassoc(); } Builder.setFastMathFlags(FMF); + llvm::FPState fpState(Builder); + auto fpModel = getLangOpts().getDefaultFPModel(); + auto fpModelExcept = getLangOpts().getDefaultFPModelExcept(); + auto fpSpeculation = getLangOpts().getDefaultFPSpeculation(); + fpState.updateBuilder(fpModel, fpModelExcept, fpSpeculation); } CodeGenFunction::~CodeGenFunction() { Index: clang/lib/Driver/ToolChains/Clang.cpp =================================================================== --- clang/lib/Driver/ToolChains/Clang.cpp +++ clang/lib/Driver/ToolChains/Clang.cpp @@ -33,6 +33,7 @@ #include "clang/Driver/XRayArgs.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Config/llvm-config.h" +#include "llvm/IR/FPState.h" #include "llvm/Option/ArgList.h" #include "llvm/Support/CodeGen.h" #include "llvm/Support/Compression.h" @@ -2263,6 +2264,9 @@ bool TrappingMath = true; StringRef DenormalFPMath = ""; StringRef FPContract = ""; + llvm::FPState::FPModelKind FPModel = llvm::FPState::FPM_Off; + llvm::FPState::FPModelExceptKind FPModelExcept = llvm::FPState::FPME_Off; + llvm::FPState::FPSpeculationKind FPSpeculation = llvm::FPState::FPS_Off; if (const Arg *A = Args.getLastArg(options::OPT_flimited_precision_EQ)) { CmdArgs.push_back("-mlimit-float-precision"); @@ -2273,6 +2277,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 = llvm::FPState::FPM_Precise; + else if (Val == "strict") + FPModel = llvm::FPState::FPM_Strict; + else if (Val == "except") + FPModelExcept = llvm::FPState::FPME_Except; + else if (Val == "except-") + FPModelExcept = llvm::FPState::FPME_NoExcept; + else if (Val == "fast") + FPModel = llvm::FPState::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 = llvm::FPState::FPS_Fast; + else if (Val == "strict") + FPSpeculation = llvm::FPState::FPS_Strict; + else if (Val == "safe") + FPSpeculation = llvm::FPState::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; @@ -2366,6 +2403,56 @@ A->claim(); } + if ((FPModelExcept == llvm::FPState::FPME_Except) && + (FPModel == llvm::FPState::FPM_Fast)) + D.Diag(diag::err_drv_argument_not_allowed_with) << "fp-model=fast" + << "fp-model=except"; + + switch (FPModel) { + case llvm::FPState::FPM_Precise: + CmdArgs.push_back("-fp-model=precise"); + break; + case llvm::FPState::FPM_Strict: + CmdArgs.push_back("-fp-model=strict"); + break; + case llvm::FPState::FPM_Fast: + CmdArgs.push_back("-fp-model=fast"); + break; + case llvm::FPState::FPM_Off: + break; + default: + llvm_unreachable("Unrecognized FPModel"); + } + + switch (FPModelExcept) { + case llvm::FPState::FPME_Except: + CmdArgs.push_back("-fp-model-except"); + break; + case llvm::FPState::FPME_NoExcept: + CmdArgs.push_back("-no-fpmodel-except-"); + break; + case llvm::FPState::FPME_Off: + break; + default: + llvm_unreachable("Unrecognized FPModel"); + } + + switch (FPSpeculation) { + case llvm::FPState::FPS_Fast: + CmdArgs.push_back("-fp-speculation=fast"); + break; + case llvm::FPState::FPS_Strict: + CmdArgs.push_back("-fp-speculation=strict"); + break; + case llvm::FPState::FPS_Safe: + CmdArgs.push_back("-fp-speculation=safe"); + break; + case llvm::FPState::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 @@ -3043,6 +3043,59 @@ Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Val; } + llvm::FPState::FPModelKind FPM = llvm::FPState::FPM_Off; + if (Arg *A = Args.getLastArg(OPT_fp_model_EQ)) { + StringRef Val = A->getValue(); + if (Val == "precise") + FPM = llvm::FPState::FPM_Precise; + else if (Val == "strict") + FPM = llvm::FPState::FPM_Strict; + else if (Val == "fast") + FPM = llvm::FPState::FPM_Fast; + else + llvm_unreachable("invalid -fp-model setting"); + } + Opts.setDefaultFPModel(FPM); + + llvm::FPState::FPModelExceptKind FPME = llvm::FPState::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 = llvm::FPState::FPME_Except; + break; + case OPT_no_fp_model_except: + FPME = llvm::FPState::FPME_NoExcept; + break; + default: + llvm_unreachable("invalid -fp-model-except setting"); + } + Opts.setDefaultFPModelExcept(FPME); + + llvm::FPState::FPSpeculationKind FPS = llvm::FPState::FPS_Off; + if (Arg *A = Args.getLastArg(OPT_fp_speculation_EQ)) { + StringRef Val = A->getValue(); + if (Val == "fast") + FPS = llvm::FPState::FPS_Fast; + else if (Val == "strict") + FPS = llvm::FPState::FPS_Strict; + else if (Val == "safe") + FPS = llvm::FPState::FPS_Safe; + else + llvm_unreachable("invalid -fp-speculation setting"); + Opts.setDefaultFPSpeculation(FPS); + } + + if (FPM == llvm::FPState::FPM_Precise) + // This doesn't correspond to constrained fp, equivalent to -fp-contract=on + Opts.setDefaultFPContractMode(LangOptions::FPC_On); + else if (FPM == llvm::FPState::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); @@ -3408,6 +3461,15 @@ // FIXME: Should we really be calling this for an InputKind::Asm input? ParseLangArgs(LangOpts, Args, DashX, Res.getTargetOpts(), Res.getPreprocessorOpts(), Diags); + auto fpm = LangOpts.getDefaultFPModel(); + if (fpm == llvm::FPState::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/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 \ Index: llvm/include/llvm/IR/FPState.h =================================================================== --- /dev/null +++ llvm/include/llvm/IR/FPState.h @@ -0,0 +1,39 @@ +#ifndef LLVM_FPSTATE_H +#define LLVM_FPSTATE_H +namespace llvm { + +class MDNode; +class IRBuilderBase; +class FPState { +public: + enum FPModelKind { FPM_Off, FPM_Precise, FPM_Strict, FPM_Fast }; + + enum FPModelExceptKind { FPME_Off, FPME_Except, FPME_NoExcept }; + + enum FPSpeculationKind { FPS_Off, FPS_Fast, FPS_Strict, FPS_Safe }; + + enum ConstrainedExceptKind { CE_Off, CE_Strict, CE_Ignore, CE_MayTrap }; + enum ConstrainedRoundingKind { + CR_Off, + CR_Dynamic, + CR_ToNearest, + CR_Downward, + CR_Upward, + CR_ToZero + }; + +private: + IRBuilderBase &Builder; + FPModelKind FPM; + FPModelExceptKind FPME; + FPSpeculationKind FPS; + +public: + FPState(IRBuilderBase &B); + // function to update builder. + void updateBuilder(FPModelKind fpModel, FPModelExceptKind fpModelExcept, + FPSpeculationKind fpSpeculation); +}; +} // end namespace llvm + +#endif // LLVM_FPSTATE_H Index: llvm/lib/IR/CMakeLists.txt =================================================================== --- llvm/lib/IR/CMakeLists.txt +++ llvm/lib/IR/CMakeLists.txt @@ -22,6 +22,7 @@ DiagnosticInfo.cpp DiagnosticPrinter.cpp Dominators.cpp + FPState.cpp Function.cpp GVMaterializer.cpp Globals.cpp Index: llvm/lib/IR/FPState.cpp =================================================================== --- /dev/null +++ llvm/lib/IR/FPState.cpp @@ -0,0 +1,85 @@ +#include "llvm/IR/FPState.h" +#include "llvm/IR/IRBuilder.h" + +namespace llvm { +FPState::FPState(IRBuilderBase &B) + : Builder(B), FPM(FPM_Off), FPME(FPME_Off), FPS(FPS_Off) {} + +void FPState::updateBuilder(FPModelKind fpModel, + FPModelExceptKind fpModelExcept, + FPSpeculationKind fpSpeculation) { + // Save the new settings in the state variables. + FPM = fpModel; + FPME = fpModelExcept; + FPS = fpSpeculation; + + // Translate the compiler options into + // the 3 settings that are transmitted to the IR Builder + bool IsConstrainedRounding = false; + bool IsConstrainedExcept = false; + ConstrainedFPIntrinsic::RoundingMode ConstrainedRoundingMD; + ConstrainedFPIntrinsic::ExceptionBehavior ConstrainedExceptMD; + + switch (fpModel) { + case FPM_Off: + case FPM_Precise: + case FPM_Fast: + break; + case FPM_Strict: + IsConstrainedRounding = true; + ConstrainedRoundingMD = ConstrainedFPIntrinsic::rmDynamic; + IsConstrainedExcept = true; + ConstrainedExceptMD = ConstrainedFPIntrinsic::ebStrict; + break; + default: + llvm_unreachable("Unsupported FP Model"); + } + + switch (fpModelExcept) { + case FPME_Off: + break; + case FPME_Except: + IsConstrainedExcept = true; + ConstrainedExceptMD = ConstrainedFPIntrinsic::ebStrict; + break; + case FPME_NoExcept: + IsConstrainedExcept = true; + ConstrainedExceptMD = ConstrainedFPIntrinsic::ebIgnore; + break; + default: + llvm_unreachable("Unsupported FP Except Model"); + } + + switch (fpSpeculation) { + case FPS_Off: + break; + case FPS_Fast: + IsConstrainedExcept = true; + ConstrainedExceptMD = ConstrainedFPIntrinsic::ebIgnore; + break; + case FPS_Strict: + IsConstrainedExcept = true; + ConstrainedExceptMD = ConstrainedFPIntrinsic::ebStrict; + break; + case FPS_Safe: + IsConstrainedExcept = true; + ConstrainedExceptMD = 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 = ConstrainedFPIntrinsic::rmToNearest; + } + + Builder.setIsFPConstrained(IsConstrainedExcept || IsConstrainedRounding); + if (IsConstrainedExcept || IsConstrainedRounding) { + Builder.setDefaultConstrainedRounding(ConstrainedRoundingMD); + Builder.setDefaultConstrainedExcept(ConstrainedExceptMD); + } +} +} // end namespace llvm Index: llvm/unittests/IR/IRBuilderTest.cpp =================================================================== --- llvm/unittests/IR/IRBuilderTest.cpp +++ llvm/unittests/IR/IRBuilderTest.cpp @@ -10,6 +10,7 @@ #include "llvm/IR/BasicBlock.h" #include "llvm/IR/DIBuilder.h" #include "llvm/IR/DataLayout.h" +#include "llvm/IR/FPState.h" #include "llvm/IR/Function.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/LLVMContext.h" @@ -195,6 +196,68 @@ EXPECT_EQ(CII->getIntrinsicID(), Intrinsic::experimental_constrained_fadd); ASSERT_TRUE(CII->getExceptionBehavior() == ConstrainedFPIntrinsic::ebMayTrap); ASSERT_TRUE(CII->getRoundingMode() == ConstrainedFPIntrinsic::rmDownward); + // + // Use FPState to update the builder settings + FPState fpState(Builder); + + fpState.updateBuilder(FPState::FPM_Off, FPState::FPME_Off, FPState::FPS_Off); + V = Builder.CreateFAdd(V, V); + ASSERT_TRUE(!isa(V)); + + fpState.updateBuilder(FPState::FPM_Precise, FPState::FPME_Off, + FPState::FPS_Off); + V = Builder.CreateFAdd(V, V); + ASSERT_TRUE(!isa(V)); + + fpState.updateBuilder(FPState::FPM_Strict, FPState::FPME_Off, + FPState::FPS_Off); + V = Builder.CreateFAdd(V, V); + CII = cast(V); + ASSERT_TRUE(isa(V)); + ASSERT_TRUE(CII->getExceptionBehavior() == ConstrainedFPIntrinsic::ebStrict); + ASSERT_TRUE(CII->getRoundingMode() == ConstrainedFPIntrinsic::rmDynamic); + + fpState.updateBuilder(FPState::FPM_Off, FPState::FPME_Except, + FPState::FPS_Off); + V = Builder.CreateFAdd(V, V); + CII = cast(V); + ASSERT_TRUE(isa(V)); + ASSERT_TRUE(CII->getExceptionBehavior() == ConstrainedFPIntrinsic::ebStrict); + ASSERT_TRUE(CII->getRoundingMode() == ConstrainedFPIntrinsic::rmToNearest); + + fpState.updateBuilder(FPState::FPM_Off, FPState::FPME_NoExcept, + FPState::FPS_Off); + V = Builder.CreateFAdd(V, V); + CII = cast(V); + ASSERT_TRUE(isa(V)); + ASSERT_TRUE(CII->getExceptionBehavior() == ConstrainedFPIntrinsic::ebIgnore); + ASSERT_TRUE(CII->getRoundingMode() == ConstrainedFPIntrinsic::rmToNearest); + + fpState.updateBuilder(FPState::FPM_Fast, FPState::FPME_Off, FPState::FPS_Off); + V = Builder.CreateFAdd(V, V); + ASSERT_TRUE(!isa(V)); + + fpState.updateBuilder(FPState::FPM_Off, FPState::FPME_Off, FPState::FPS_Fast); + V = Builder.CreateFAdd(V, V); + CII = cast(V); + ASSERT_TRUE(isa(V)); + ASSERT_TRUE(CII->getExceptionBehavior() == ConstrainedFPIntrinsic::ebIgnore); + ASSERT_TRUE(CII->getRoundingMode() == ConstrainedFPIntrinsic::rmToNearest); + + fpState.updateBuilder(FPState::FPM_Off, FPState::FPME_Off, + FPState::FPS_Strict); + V = Builder.CreateFAdd(V, V); + CII = cast(V); + ASSERT_TRUE(isa(V)); + ASSERT_TRUE(CII->getExceptionBehavior() == ConstrainedFPIntrinsic::ebStrict); + ASSERT_TRUE(CII->getRoundingMode() == ConstrainedFPIntrinsic::rmToNearest); + + fpState.updateBuilder(FPState::FPM_Off, FPState::FPME_Off, FPState::FPS_Safe); + V = Builder.CreateFAdd(V, V); + CII = cast(V); + ASSERT_TRUE(isa(V)); + ASSERT_TRUE(CII->getExceptionBehavior() == ConstrainedFPIntrinsic::ebMayTrap); + ASSERT_TRUE(CII->getRoundingMode() == ConstrainedFPIntrinsic::rmToNearest); Builder.CreateRetVoid(); EXPECT_FALSE(verifyModule(*M));