[clang] clang: Add pragma clang fp reciprocal (PR #68267)

via cfe-commits cfe-commits at lists.llvm.org
Wed Oct 4 15:22:08 PDT 2023


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

<details>
<summary>Changes</summary>

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.

---
Full diff: https://github.com/llvm/llvm-project/pull/68267.diff


8 Files Affected:

- (modified) clang/docs/LanguageExtensions.rst (+16) 
- (modified) clang/docs/ReleaseNotes.rst (+2) 
- (modified) clang/include/clang/Basic/DiagnosticParseKinds.td (+1) 
- (modified) clang/include/clang/Sema/Sema.h (+4) 
- (modified) clang/lib/Parse/ParsePragma.cpp (+20-9) 
- (modified) clang/lib/Sema/SemaAttr.cpp (+7) 
- (added) clang/test/CodeGen/fp-reciprocal-pragma.cpp (+130) 
- (modified) clang/test/Parser/pragma-fp-contract.c (+15) 


``````````diff
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 at +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 at +1{{unexpected argument 'fast' to '#pragma clang fp reciprocal'; expected 'on' or 'off'}}
+#pragma clang fp reciprocal(fast)
+  return a - b;
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/68267


More information about the cfe-commits mailing list