diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h --- a/clang/include/clang/AST/Stmt.h +++ b/clang/include/clang/AST/Stmt.h @@ -615,7 +615,7 @@ unsigned OperatorKind : 6; // Only meaningful for floating point types. - unsigned FPFeatures : 8; + unsigned FPFeatures : 14; }; class CXXRewrittenBinaryOperatorBitfields { diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1332,10 +1332,13 @@ "pipeline, pipeline_initiation_interval, vectorize_predicate, or distribute">; def err_pragma_fp_invalid_option : Error< - "%select{invalid|missing}0 option%select{ %1|}0; expected contract">; + "%select{invalid|missing}0 option%select{ %1|}0; expected contract or reassoc">; +def err_pragma_fp_contract_invalid_argument : Error< + "unexpected argument '%0' to '#pragma clang fp contract'; " + "expected 'on', 'fast' or 'off'">; def err_pragma_fp_invalid_argument : Error< "unexpected argument '%0' to '#pragma clang fp %1'; " - "expected 'on', 'fast' or 'off'">; + "expected 'on' or 'off'">; def err_pragma_invalid_keyword : Error< "invalid argument; expected 'enable'%select{|, 'full'}0%select{|, 'assume_safety'}1 or 'disable'">; diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h --- a/clang/include/clang/Basic/LangOptions.h +++ b/clang/include/clang/Basic/LangOptions.h @@ -174,22 +174,15 @@ Swift4_1, }; - enum FPContractModeKind { - // Form fused FP ops only where result will not be affected. - FPC_Off, + enum FPModeKind { + // Disable the floating point pragma + FPM_Off, - // Form fused FP ops according to FP_CONTRACT rules. - FPC_On, + // Enable the floating point pragma + FPM_On, - // Aggressively fuse FP ops (E.g. FMA). - FPC_Fast - }; - - // TODO: merge FEnvAccessModeKind and FPContractModeKind - enum FEnvAccessModeKind { - FEA_Off, - - FEA_On + // (Only for fp contract) Aggressively fuse FP ops (E.g. FMA). + FPM_Fast }; /// Alias for RoundingMode::NearestTiesToEven. @@ -380,7 +373,7 @@ public: FPOptions() - : fp_contract(LangOptions::FPC_Off), fenv_access(LangOptions::FEA_Off), + : fp_contract(LangOptions::FPM_Off), fenv_access(LangOptions::FPM_Off), rounding(LangOptions::FPR_ToNearest), exceptions(LangOptions::FPE_Ignore), allow_reassoc(0), no_nans(0), no_infs(0), no_signed_zeros(0), allow_reciprocal(0), approx_func(0) {} @@ -395,7 +388,7 @@ explicit FPOptions(const LangOptions &LangOpts) : fp_contract(LangOpts.getDefaultFPContractMode()), - fenv_access(LangOptions::FEA_Off), + fenv_access(LangOptions::FPM_Off), rounding(static_cast(LangOpts.getFPRoundingMode())), exceptions(LangOpts.getFPExceptionMode()), allow_reassoc(LangOpts.FastMath || LangOpts.AllowFPReassoc), @@ -420,30 +413,26 @@ bool requiresTrailingStorage(const LangOptions &LO); bool allowFPContractWithinStatement() const { - return fp_contract == LangOptions::FPC_On; + return fp_contract == LangOptions::FPM_On; } bool allowFPContractAcrossStatement() const { - return fp_contract == LangOptions::FPC_Fast; + return fp_contract == LangOptions::FPM_Fast; } void setAllowFPContractWithinStatement() { - fp_contract = LangOptions::FPC_On; + fp_contract = LangOptions::FPM_On; } void setAllowFPContractAcrossStatement() { - fp_contract = LangOptions::FPC_Fast; + fp_contract = LangOptions::FPM_Fast; } - void setDisallowFPContract() { fp_contract = LangOptions::FPC_Off; } + void setDisallowFPContract() { fp_contract = LangOptions::FPM_Off; } - bool allowFEnvAccess() const { - return fenv_access == LangOptions::FEA_On; - } + bool allowFEnvAccess() const { return fenv_access == LangOptions::FPM_On; } - void setAllowFEnvAccess() { - fenv_access = LangOptions::FEA_On; - } + void setAllowFEnvAccess() { fenv_access = LangOptions::FPM_On; } void setFPPreciseEnabled(bool Value) { if (Value) { @@ -457,7 +446,7 @@ } } - void setDisallowFEnvAccess() { fenv_access = LangOptions::FEA_Off; } + void setDisallowFEnvAccess() { fenv_access = LangOptions::FPM_Off; } RoundingMode getRoundingMode() const { return static_cast(rounding); @@ -507,8 +496,8 @@ /// Used with getAsOpaqueInt() to manage the float_control pragma stack. void getFromOpaqueInt(unsigned I) { - fp_contract = (static_cast(I & 3)); - fenv_access = (static_cast((I >> 2) & 1)); + fp_contract = (static_cast(I & 3)); + fenv_access = (static_cast((I >> 2) & 1)); rounding = static_cast(static_cast((I >> 3) & 7)); exceptions = (static_cast((I >> 6) & 3)); allow_reassoc = ((I >> 8) & 1); diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -268,7 +268,7 @@ LANGOPT(SinglePrecisionConstants , 1, 0, "treating double-precision floating point constants as single precision constants") LANGOPT(FastRelaxedMath , 1, 0, "OpenCL fast relaxed math") /// FP_CONTRACT mode (on/off/fast). -ENUM_LANGOPT(DefaultFPContractMode, FPContractModeKind, 2, FPC_Off, "FP contraction type") +ENUM_LANGOPT(DefaultFPContractMode, FPModeKind, 2, FPM_Off, "FP contraction type") ENUM_LANGOPT(FPRoundingMode, RoundingMode, 3, RoundingMode::NearestTiesToEven, "FP Rounding Mode type") ENUM_LANGOPT(FPExceptionMode, FPExceptionModeKind, 2, FPE_Ignore, "FP Exception Behavior Mode type") LANGOPT(NoBitFieldTypeAlign , 1, 0, "bit-field type alignment") diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -9617,12 +9617,15 @@ /// ActOnPragmaFPContract - Called on well formed /// \#pragma {STDC,OPENCL} FP_CONTRACT and /// \#pragma clang fp contract - void ActOnPragmaFPContract(LangOptions::FPContractModeKind FPC); + void ActOnPragmaFPContract(LangOptions::FPModeKind FPC); + + /// ActOnPragmaFPReassoc - Called on well formed + /// \#pragma clang fp reassoc + void ActOnPragmaFPReassoc(LangOptions::FPModeKind FPC); /// ActOnPragmaFenvAccess - Called on well formed /// \#pragma STDC FENV_ACCESS - void ActOnPragmaFEnvAccess(SourceLocation Loc, - LangOptions::FEnvAccessModeKind FPC); + void ActOnPragmaFEnvAccess(SourceLocation Loc, LangOptions::FPModeKind FPC); /// Called to set rounding mode for floating point operations. void setRoundingMode(llvm::RoundingMode); diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp @@ -450,15 +450,15 @@ // Set FP fusion mode. switch (LangOpts.getDefaultFPContractMode()) { - case LangOptions::FPC_Off: + case LangOptions::FPM_Off: // Preserve any contraction performed by the front-end. (Strict performs // splitting of the muladd intrinsic in the backend.) Options.AllowFPOpFusion = llvm::FPOpFusion::Standard; break; - case LangOptions::FPC_On: + case LangOptions::FPM_On: Options.AllowFPOpFusion = llvm::FPOpFusion::Standard; break; - case LangOptions::FPC_Fast: + case LangOptions::FPM_Fast: Options.AllowFPOpFusion = llvm::FPOpFusion::Fast; break; } diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -2318,7 +2318,7 @@ Opts.AltiVec = 0; Opts.ZVector = 0; Opts.setLaxVectorConversions(LangOptions::LaxVectorConversionKind::None); - Opts.setDefaultFPContractMode(LangOptions::FPC_On); + Opts.setDefaultFPContractMode(LangOptions::FPM_On); Opts.NativeHalfType = 1; Opts.NativeHalfArgsAndReturns = 1; Opts.OpenCLCPlusPlus = Opts.CPlusPlus; @@ -2338,7 +2338,7 @@ Opts.CUDA = IK.getLanguage() == Language::CUDA || Opts.HIP; if (Opts.CUDA) // Set default FP_CONTRACT to FAST. - Opts.setDefaultFPContractMode(LangOptions::FPC_Fast); + Opts.setDefaultFPContractMode(LangOptions::FPM_Fast); Opts.RenderScript = IK.getLanguage() == Language::RenderScript; if (Opts.RenderScript) { @@ -3204,11 +3204,11 @@ if (Arg *A = Args.getLastArg(OPT_ffp_contract)) { StringRef Val = A->getValue(); if (Val == "fast") - Opts.setDefaultFPContractMode(LangOptions::FPC_Fast); + Opts.setDefaultFPContractMode(LangOptions::FPM_Fast); else if (Val == "on") - Opts.setDefaultFPContractMode(LangOptions::FPC_On); + Opts.setDefaultFPContractMode(LangOptions::FPM_On); else if (Val == "off") - Opts.setDefaultFPContractMode(LangOptions::FPC_Off); + Opts.setDefaultFPContractMode(LangOptions::FPM_Off); else Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Val; } diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp --- a/clang/lib/Parse/ParsePragma.cpp +++ b/clang/lib/Parse/ParsePragma.cpp @@ -643,13 +643,13 @@ static_cast( reinterpret_cast(Tok.getAnnotationValue())); - LangOptions::FPContractModeKind FPC; + LangOptions::FPModeKind FPC; switch (OOS) { case tok::OOS_ON: - FPC = LangOptions::FPC_On; + FPC = LangOptions::FPM_On; break; case tok::OOS_OFF: - FPC = LangOptions::FPC_Off; + FPC = LangOptions::FPM_Off; break; case tok::OOS_DEFAULT: FPC = getLangOpts().getDefaultFPContractMode(); @@ -677,16 +677,16 @@ static_cast( reinterpret_cast(Tok.getAnnotationValue())); - LangOptions::FEnvAccessModeKind FPC; + LangOptions::FPModeKind FPC; switch (OOS) { case tok::OOS_ON: - FPC = LangOptions::FEA_On; + FPC = LangOptions::FPM_On; break; case tok::OOS_OFF: - FPC = LangOptions::FEA_Off; + FPC = LangOptions::FPM_Off; break; case tok::OOS_DEFAULT: // FIXME: Add this cli option when it makes sense. - FPC = LangOptions::FEA_Off; + FPC = LangOptions::FPM_Off; break; } @@ -2821,7 +2821,7 @@ namespace { /// Used as the annotation value for tok::annot_pragma_fp. struct TokFPAnnotValue { - enum FlagKinds { Contract }; + enum FlagKinds { Contract, Reassoc }; enum FlagValues { On, Off, Fast }; FlagKinds FlagKind; @@ -2849,6 +2849,7 @@ llvm::StringSwitch>( OptionInfo->getName()) .Case("contract", TokFPAnnotValue::Contract) + .Case("reassoc", TokFPAnnotValue::Reassoc) .Default(None); if (!FlagKind) { PP.Diag(Tok.getLocation(), diag::err_pragma_fp_invalid_option) @@ -2879,9 +2880,15 @@ .Case("fast", TokFPAnnotValue::Fast) .Default(llvm::None); - if (!FlagValue) { - PP.Diag(Tok.getLocation(), diag::err_pragma_fp_invalid_argument) - << PP.getSpelling(Tok) << OptionInfo->getName(); + if (FlagKind == TokFPAnnotValue::Reassoc) { + if (!FlagValue || FlagValue == TokFPAnnotValue::Fast) { + PP.Diag(Tok.getLocation(), diag::err_pragma_fp_invalid_argument) + << PP.getSpelling(Tok) << OptionInfo->getName(); + return; + } + } else if (!FlagValue) { + PP.Diag(Tok.getLocation(), diag::err_pragma_fp_contract_invalid_argument) + << PP.getSpelling(Tok); return; } PP.Lex(Tok); @@ -2895,7 +2902,7 @@ auto *AnnotValue = new (PP.getPreprocessorAllocator()) TokFPAnnotValue{*FlagKind, *FlagValue}; - // Generate the loop hint token. + // Generate the pragma fp annotation token. Token FPTok; FPTok.startToken(); FPTok.setKind(tok::annot_pragma_fp); @@ -2923,20 +2930,23 @@ auto *AnnotValue = reinterpret_cast(Tok.getAnnotationValue()); - LangOptions::FPContractModeKind FPC; + LangOptions::FPModeKind FPC; switch (AnnotValue->FlagValue) { case TokFPAnnotValue::On: - FPC = LangOptions::FPC_On; + FPC = LangOptions::FPM_On; break; case TokFPAnnotValue::Fast: - FPC = LangOptions::FPC_Fast; + FPC = LangOptions::FPM_Fast; break; case TokFPAnnotValue::Off: - FPC = LangOptions::FPC_Off; + FPC = LangOptions::FPM_Off; break; } - Actions.ActOnPragmaFPContract(FPC); + if (AnnotValue->FlagKind == TokFPAnnotValue::Reassoc) + Actions.ActOnPragmaFPReassoc(FPC); + else + Actions.ActOnPragmaFPContract(FPC); ConsumeAnnotationToken(); } diff --git a/clang/lib/Sema/SemaAttr.cpp b/clang/lib/Sema/SemaAttr.cpp --- a/clang/lib/Sema/SemaAttr.cpp +++ b/clang/lib/Sema/SemaAttr.cpp @@ -988,20 +988,33 @@ } } -void Sema::ActOnPragmaFPContract(LangOptions::FPContractModeKind FPC) { +void Sema::ActOnPragmaFPContract(LangOptions::FPModeKind FPC) { switch (FPC) { - case LangOptions::FPC_On: + case LangOptions::FPM_On: CurFPFeatures.setAllowFPContractWithinStatement(); break; - case LangOptions::FPC_Fast: + case LangOptions::FPM_Fast: CurFPFeatures.setAllowFPContractAcrossStatement(); break; - case LangOptions::FPC_Off: + case LangOptions::FPM_Off: CurFPFeatures.setDisallowFPContract(); break; } } +void Sema::ActOnPragmaFPReassoc(LangOptions::FPModeKind FPC) { + switch (FPC) { + default: + llvm_unreachable("invalid pragma fp reassoc kind"); + case LangOptions::FPM_On: + CurFPFeatures.setAllowAssociativeMath(true); + break; + case LangOptions::FPM_Off: + CurFPFeatures.setAllowAssociativeMath(false); + break; + } +} + void Sema::setRoundingMode(llvm::RoundingMode FPR) { CurFPFeatures.setRoundingMode(FPR); } @@ -1011,9 +1024,11 @@ } void Sema::ActOnPragmaFEnvAccess(SourceLocation Loc, - LangOptions::FEnvAccessModeKind FPC) { + LangOptions::FPModeKind FPC) { switch (FPC) { - case LangOptions::FEA_On: + default: + llvm_unreachable("invalid pragma fp fenv access kind"); + case LangOptions::FPM_On: // Verify Microsoft restriction: // You can't enable fenv_access unless precise semantics are enabled. // Precise semantics can be enabled either by the float_control @@ -1022,7 +1037,7 @@ Diag(Loc, diag::err_pragma_fenv_requires_precise); CurFPFeatures.setAllowFEnvAccess(); break; - case LangOptions::FEA_Off: + case LangOptions::FPM_Off: CurFPFeatures.setDisallowFEnvAccess(); break; } diff --git a/clang/test/CodeGen/fp-reassoc-pragma-fails.cpp b/clang/test/CodeGen/fp-reassoc-pragma-fails.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/fp-reassoc-pragma-fails.cpp @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s +float fp_reassoc_fail(float a, float b) { + // CHECK-LABEL: fp_reassoc_fail + // expected-error@+2{{'#pragma clang fp' can only appear at file scope or at the start of a compound statement}} + float c = a + b; +#pragma clang fp reassoc(on) + return c - b; +} + +float fp_reassoc_no_fast(float a, float b) { +// CHECK-LABEL: fp_reassoc_no_fast +// expected-error@+1{{unexpected argument 'fast' to '#pragma clang fp reassoc'; expected 'on' or 'off'}} +#pragma clang fp reassoc(fast) + return a - b; +} diff --git a/clang/test/CodeGen/fp-reassoc-pragma.cpp b/clang/test/CodeGen/fp-reassoc-pragma.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/fp-reassoc-pragma.cpp @@ -0,0 +1,90 @@ +// RUN: %clang_cc1 -O3 -triple %itanium_abi_triple -emit-llvm -o - %s | FileCheck %s +// Simple case +float fp_reassoc_simple(float a, float b, float c) { +// CHECK: _Z17fp_reassoc_simplefff +// CHECK: %[[M:.+]] = fmul reassoc float %a, %b +// CHECK-NEXT: fadd reassoc float %[[M]], %c +#pragma clang fp reassoc(on) + return a * b + c; +} + +// Reassoc pragma should only apply to its scope +float fp_reassoc_scoped(float a, float b, float c) { + // CHECK: _Z17fp_reassoc_scopedfff + // CHECK: %[[M:.+]] = fmul float %a, %b + // CHECK-NEXT: fadd float %[[M]], %c + { +#pragma clang fp reassoc(on) + } + return a * b + c; +} + +// Reassoc pragma should apply to templates as well +class Foo {}; +Foo operator+(Foo, Foo); +template +T template_reassoc(T a, T b, T c) { +#pragma clang fp reassoc(on) + return ((a + b) - c) + c; +} + +float fp_reassoc_template(float a, float b, float c) { + // CHECK: _Z19fp_reassoc_templatefff + // CHECK: %[[A1:.+]] = fadd reassoc float %a, %b + // CHECK-NEXT: %[[A2:.+]] = fsub reassoc float %[[A1]], %c + // CHECK-NEXT: fadd reassoc float %[[A2]], %c + return template_reassoc(a, b, c); +} + +// File Scoping should work across functions +#pragma clang fp reassoc(on) +float fp_file_scope_on(float a, float b, float c) { + // CHECK: _Z16fp_file_scope_onfff + // CHECK: %[[M1:.+]] = fmul reassoc float %a, %c + // CHECK-NEXT: %[[M2:.+]] = fmul reassoc float %b, %c + // CHECK-NEXT: fadd reassoc float %[[M1]], %[[M2]] + return (a * c) + (b * c); +} + +// Inner pragma has precedence +float fp_file_scope_stop(float a, float b, float c) { + // CHECK: _Z18fp_file_scope_stopfff + // CHECK: %[[A:.+]] = fadd reassoc float %a, %a + // CHECK: %[[M1:.+]] = fmul float %[[A]], %c + // CHECK-NEXT: %[[M2:.+]] = fmul float %b, %c + // CHECK-NEXT: fsub float %[[M1]], %[[M2]] + a = a + a; + { +#pragma clang fp reassoc(off) + return (a * c) - (b * c); + } +} + +#pragma clang fp reassoc(off) +float fp_reassoc_off(float a, float b, float c) { + // CHECK: _Z14fp_reassoc_offfff + // CHECK: %[[D1:.+]] = fdiv float %a, %c + // CHECK-NEXT: %[[D2:.+]] = fdiv float %b, %c + // CHECK-NEXT: fadd float %[[D1]], %[[D2]] + return (a / c) + (b / c); +} + +// Takes latest flag +float fp_reassoc_many(float a, float b, float c) { +// CHECK: _Z15fp_reassoc_manyfff +// CHECK: %[[D1:.+]] = fdiv reassoc float %a, %c +// CHECK-NEXT: %[[D2:.+]] = fdiv reassoc float %b, %c +// CHECK-NEXT: fadd reassoc float %[[D1]], %[[D2]] +#pragma clang fp reassoc(off) reassoc(on) + return (a / c) + (b / c); +} + +// Pragma does not propagate through called functions +float helper_func(float a, float b, float c) { return a + b + c; } +float fp_reassoc_call_helper(float a, float b, float c) { +// CHECK: _Z22fp_reassoc_call_helperfff +// CHECK: %[[S1:.+]] = fadd float %a, %b +// CHECK-NEXT: fadd float %[[S1]], %c +#pragma clang fp reassoc(on) + return helper_func(a, b, c); +}