https://github.com/arsenm created https://github.com/llvm/llvm-project/pull/68267
Just follow allow with the reassociate pragma. This allows locally setting the arcp fast math flag. Previously you could only access this through the global -freciprocal-math. >From 082efa2687b0b6a250bbdbe60040ca502e4ad8a1 Mon Sep 17 00:00:00 2001 From: Matt Arsenault <matthew.arsena...@amd.com> Date: Thu, 31 Aug 2023 17:33:35 -0400 Subject: [PATCH] clang: Add pragma clang fp reciprocal Just follow allow with the reassociate pragma. This allows locally setting the arcp fast math flag. Previously you could only access this through the global -freciprocal-math. --- clang/docs/LanguageExtensions.rst | 16 +++ clang/docs/ReleaseNotes.rst | 2 + .../clang/Basic/DiagnosticParseKinds.td | 1 + clang/include/clang/Sema/Sema.h | 4 + clang/lib/Parse/ParsePragma.cpp | 29 ++-- clang/lib/Sema/SemaAttr.cpp | 7 + clang/test/CodeGen/fp-reciprocal-pragma.cpp | 130 ++++++++++++++++++ clang/test/Parser/pragma-fp-contract.c | 15 ++ 8 files changed, 195 insertions(+), 9 deletions(-) create mode 100644 clang/test/CodeGen/fp-reciprocal-pragma.cpp diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index b9466b5a0bc2087..49678240cb55f07 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -4609,6 +4609,22 @@ The pragma can take two values: ``on`` and ``off``. float v = t + z; } +``#pragma clang fp reciprocal`` allows control over using reciprocal +approximations in floating point expressions. When enabled, this +pragma allows the expression ``x / y`` to be approximated as ``x * +(1.0 / y)``. This pragma can be used to disable reciprocal +approximation when it is otherwise enabled for the translation unit +with the ``-fallow-reciprocal`` flag. The pragma can take two values: +``on`` and ``off``. + +.. code-block:: c++ + + float f(float x, float y) + { + // Enable floating point reciprocal approximation + #pragma clang fp reciprocal(on) + return x / y; + } ``#pragma clang fp contract`` specifies whether the compiler should contract a multiply and an addition (or subtraction) into a fused FMA diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 6f410c48bd1ffe9..d412e2334c4bbc3 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -144,6 +144,8 @@ C23 Feature Support Non-comprehensive list of changes in this release ------------------------------------------------- +* Added ``#pragma clang fp reciprocal``. + New Compiler Flags ------------------ diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index 178761bdcf4d5e3..27385487b55bce0 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1566,6 +1566,7 @@ def err_pragma_fp_invalid_argument : Error< "%select{" "'fast' or 'on' or 'off'|" "'on' or 'off'|" + "'on' or 'off'|" "'ignore', 'maytrap' or 'strict'|" "'source', 'double' or 'extended'}2">; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index bb05c45391b5473..49b4c836c7451ab 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -10950,6 +10950,10 @@ class Sema final { /// \#pragma clang fp reassociate void ActOnPragmaFPReassociate(SourceLocation Loc, bool IsEnabled); + /// Called on well formed + /// \#pragma clang fp reciprocal + void ActOnPragmaFPReciprocal(SourceLocation Loc, bool IsEnabled); + /// ActOnPragmaFenvAccess - Called on well formed /// \#pragma STDC FENV_ACCESS void ActOnPragmaFEnvAccess(SourceLocation Loc, bool IsEnabled); diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp index b3178aef64d72d7..d5ebb9d994a819a 100644 --- a/clang/lib/Parse/ParsePragma.cpp +++ b/clang/lib/Parse/ParsePragma.cpp @@ -3191,11 +3191,12 @@ void PragmaOptimizeHandler::HandlePragma(Preprocessor &PP, namespace { /// Used as the annotation value for tok::annot_pragma_fp. struct TokFPAnnotValue { - enum FlagKinds { Contract, Reassociate, Exceptions, EvalMethod }; + enum FlagKinds { Contract, Reassociate, Reciprocal, Exceptions, EvalMethod }; enum FlagValues { On, Off, Fast }; std::optional<LangOptions::FPModeKind> ContractValue; std::optional<LangOptions::FPModeKind> ReassociateValue; + std::optional<LangOptions::FPModeKind> ReciprocalValue; std::optional<LangOptions::FPExceptionModeKind> ExceptionsValue; std::optional<LangOptions::FPEvalMethodKind> EvalMethodValue; }; @@ -3225,6 +3226,7 @@ void PragmaFPHandler::HandlePragma(Preprocessor &PP, .Case("reassociate", TokFPAnnotValue::Reassociate) .Case("exceptions", TokFPAnnotValue::Exceptions) .Case("eval_method", TokFPAnnotValue::EvalMethod) + .Case("reciprocal", TokFPAnnotValue::Reciprocal) .Default(std::nullopt); if (!FlagKind) { PP.Diag(Tok.getLocation(), diag::err_pragma_fp_invalid_option) @@ -3264,14 +3266,17 @@ void PragmaFPHandler::HandlePragma(Preprocessor &PP, << PP.getSpelling(Tok) << OptionInfo->getName() << *FlagKind; return; } - } else if (FlagKind == TokFPAnnotValue::Reassociate) { - AnnotValue->ReassociateValue = - llvm::StringSwitch<std::optional<LangOptions::FPModeKind>>( - II->getName()) - .Case("on", LangOptions::FPModeKind::FPM_On) - .Case("off", LangOptions::FPModeKind::FPM_Off) - .Default(std::nullopt); - if (!AnnotValue->ReassociateValue) { + } else if (FlagKind == TokFPAnnotValue::Reassociate || + FlagKind == TokFPAnnotValue::Reciprocal) { + auto &Value = FlagKind == TokFPAnnotValue::Reassociate + ? AnnotValue->ReassociateValue + : AnnotValue->ReciprocalValue; + Value = llvm::StringSwitch<std::optional<LangOptions::FPModeKind>>( + II->getName()) + .Case("on", LangOptions::FPModeKind::FPM_On) + .Case("off", LangOptions::FPModeKind::FPM_Off) + .Default(std::nullopt); + if (!Value) { PP.Diag(Tok.getLocation(), diag::err_pragma_fp_invalid_argument) << PP.getSpelling(Tok) << OptionInfo->getName() << *FlagKind; return; @@ -3398,6 +3403,12 @@ void Parser::HandlePragmaFP() { Actions.ActOnPragmaFPReassociate(Tok.getLocation(), *AnnotValue->ReassociateValue == LangOptions::FPModeKind::FPM_On); + + if (AnnotValue->ReciprocalValue) + Actions.ActOnPragmaFPReciprocal(Tok.getLocation(), + *AnnotValue->ReciprocalValue == + LangOptions::FPModeKind::FPM_On); + if (AnnotValue->ContractValue) Actions.ActOnPragmaFPContract(Tok.getLocation(), *AnnotValue->ContractValue); diff --git a/clang/lib/Sema/SemaAttr.cpp b/clang/lib/Sema/SemaAttr.cpp index 6dadf01ead4441b..a7eb25e72a63547 100644 --- a/clang/lib/Sema/SemaAttr.cpp +++ b/clang/lib/Sema/SemaAttr.cpp @@ -1309,6 +1309,13 @@ void Sema::ActOnPragmaFPReassociate(SourceLocation Loc, bool IsEnabled) { CurFPFeatures = NewFPFeatures.applyOverrides(getLangOpts()); } +void Sema::ActOnPragmaFPReciprocal(SourceLocation Loc, bool IsEnabled) { + FPOptionsOverride NewFPFeatures = CurFPFeatureOverrides(); + NewFPFeatures.setAllowReciprocalOverride(IsEnabled); + FpPragmaStack.Act(Loc, PSK_Set, StringRef(), NewFPFeatures); + CurFPFeatures = NewFPFeatures.applyOverrides(getLangOpts()); +} + void Sema::ActOnPragmaFEnvRound(SourceLocation Loc, llvm::RoundingMode FPR) { FPOptionsOverride NewFPFeatures = CurFPFeatureOverrides(); NewFPFeatures.setConstRoundingModeOverride(FPR); diff --git a/clang/test/CodeGen/fp-reciprocal-pragma.cpp b/clang/test/CodeGen/fp-reciprocal-pragma.cpp new file mode 100644 index 000000000000000..db93550301bf23c --- /dev/null +++ b/clang/test/CodeGen/fp-reciprocal-pragma.cpp @@ -0,0 +1,130 @@ +// RUN: %clang_cc1 -O3 -triple %itanium_abi_triple -emit-llvm -o - %s | FileCheck -check-prefixes=CHECK,DEFAULT %s +// RUN: %clang_cc1 -O3 -triple %itanium_abi_triple -freciprocal-math -emit-llvm -o - %s | FileCheck -check-prefixes=CHECK,FLAG %s + +float base(float a, float b, float c) { +// CHECK-LABEL: _Z4basefff +// FLAG: %[[A:.+]] = fdiv arcp float %b, %c +// FLAG: %[[M:.+]] = fdiv arcp float %[[A]], %b +// FLAG-NEXT: fadd arcp float %[[M]], %c + +// DEFAULT: %[[A:.+]] = fdiv float %b, %c +// DEFAULT: %[[M:.+]] = fdiv float %[[A]], %b +// DEFAULT-NEXT: fadd float %[[M]], %c + a = b / c; + return a / b + c; +} + +// Simple case +float fp_recip_simple(float a, float b, float c) { +// CHECK-LABEL: _Z15fp_recip_simplefff +// CHECK: %[[A:.+]] = fdiv arcp float %b, %c +// CHECK: %[[M:.+]] = fdiv arcp float %[[A]], %b +// CHECK-NEXT: fadd arcp float %[[M]], %c +#pragma clang fp reciprocal(on) + a = b / c; + return a / b + c; +} + +// Test interaction with -freciprocal-math +float fp_recip_disable(float a, float b, float c) { +// CHECK-LABEL: _Z16fp_recip_disablefff +// CHECK: %[[A:.+]] = fdiv float %b, %c +// CHECK: %[[M:.+]] = fdiv float %[[A]], %b +// CHECK-NEXT: fadd float %[[M]], %c +#pragma clang fp reciprocal(off) + a = b / c; + return a / b + c; +} + +float fp_recip_with_reassoc_simple(float a, float b, float c) { +// CHECK-LABEL: _Z28fp_recip_with_reassoc_simplefff +// CHECK: %[[A:.+]] = fmul reassoc arcp float %b, %c +// CHECK: %[[M:.+]] = fdiv reassoc arcp float %b, %[[A]] +// CHECK-NEXT: fadd reassoc arcp float %[[M]], %c +#pragma clang fp reciprocal(on) reassociate(on) + a = b / c; + return a / b + c; +} + +// arcp pragma should only apply to its scope +float fp_recip_scoped(float a, float b, float c) { + // CHECK-LABEL: _Z15fp_recip_scopedfff + // DEFAULT: %[[M:.+]] = fdiv float %a, %b + // DEFAULT-NEXT: fadd float %[[M]], %c + // FLAG: %[[M:.+]] = fdiv arcp float %a, %b + // FLAG-NEXT: fadd arcp float %[[M]], %c + { +#pragma clang fp reciprocal(on) + } + return a / b + c; +} + +// arcp pragma should apply to templates as well +class Foo {}; +Foo operator+(Foo, Foo); +template <typename T> +T template_recip(T a, T b, T c) { +#pragma clang fp reciprocal(on) + return ((a / b) - c) + c; +} + +float fp_recip_template(float a, float b, float c) { + // CHECK-LABEL: _Z17fp_recip_templatefff + // CHECK: %[[A1:.+]] = fdiv arcp float %a, %b + // CHECK-NEXT: %[[A2:.+]] = fsub arcp float %[[A1]], %c + // CHECK-NEXT: fadd arcp float %[[A2]], %c + return template_recip<float>(a, b, c); +} + +// File Scoping should work across functions +#pragma clang fp reciprocal(on) +float fp_file_scope_on(float a, float b, float c) { + // CHECK-LABEL: _Z16fp_file_scope_onfff + // CHECK: %[[M1:.+]] = fdiv arcp float %a, %c + // CHECK-NEXT: %[[M2:.+]] = fdiv arcp float %b, %c + // CHECK-NEXT: fadd arcp float %[[M1]], %[[M2]] + return (a / c) + (b / c); +} + +// Inner pragma has precedence +float fp_file_scope_stop(float a, float b, float c) { + // CHECK-LABEL: _Z18fp_file_scope_stopfff + // CHECK: %[[A:.+]] = fdiv arcp float %a, %a + // CHECK: %[[M1:.+]] = fdiv float %[[A]], %c + // CHECK-NEXT: %[[M2:.+]] = fdiv float %b, %c + // CHECK-NEXT: fsub float %[[M1]], %[[M2]] + a = a / a; + { +#pragma clang fp reciprocal(off) + return (a / c) - (b / c); + } +} + +#pragma clang fp reciprocal(off) +float fp_recip_off(float a, float b, float c) { + // CHECK-LABEL: _Z12fp_recip_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_recip_many(float a, float b, float c) { +// CHECK-LABEL: _Z13fp_recip_manyfff +// CHECK: %[[D1:.+]] = fdiv arcp float %a, %c +// CHECK-NEXT: %[[D2:.+]] = fdiv arcp float %b, %c +// CHECK-NEXT: fadd arcp float %[[D1]], %[[D2]] +#pragma clang fp reciprocal(off) reciprocal(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_recip_call_helper(float a, float b, float c) { +// CHECK-LABEL: _Z20fp_recip_call_helperfff +// CHECK: %[[S1:.+]] = fadd float %a, %b +// CHECK-NEXT: fadd float %[[S1]], %c +#pragma clang fp reciprocal(on) + return helper_func(a, b, c); +} diff --git a/clang/test/Parser/pragma-fp-contract.c b/clang/test/Parser/pragma-fp-contract.c index 3230a23792af3fc..788fffc00d70d17 100644 --- a/clang/test/Parser/pragma-fp-contract.c +++ b/clang/test/Parser/pragma-fp-contract.c @@ -38,3 +38,18 @@ float fp_reassoc_no_fast(float a, float b) { #pragma clang fp reassociate(fast) return a - b; } + +float fp_recip_fail(float a, float b) { + // CHECK-LABEL: fp_recip_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 reciprocal(off) + return c - b; +} + +float fp_recip_no_fast(float a, float b) { +// CHECK-LABEL: fp_recip_no_fast +// expected-error@+1{{unexpected argument 'fast' to '#pragma clang fp reciprocal'; expected 'on' or 'off'}} +#pragma clang fp reciprocal(fast) + return a - b; +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits