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

Matt Arsenault via cfe-commits cfe-commits at lists.llvm.org
Fri Nov 3 01:13:54 PDT 2023


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

>From db9b84992dbd6d75dc5c23b11d63c195400d5bc1 Mon Sep 17 00:00:00 2001
From: Matt Arsenault <Matthew.Arsenault at 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       |   3 +-
 .../clang/Basic/DiagnosticSemaKinds.td        |   2 +-
 clang/include/clang/Basic/PragmaKinds.h       |   9 ++
 clang/include/clang/Sema/Sema.h               |   5 +-
 clang/lib/Parse/ParsePragma.cpp               |  52 ++++---
 clang/lib/Sema/SemaAttr.cpp                   |  18 ++-
 clang/test/CodeGen/fp-reciprocal-pragma.cpp   | 130 ++++++++++++++++++
 clang/test/Parser/pragma-fp-contract.c        |  15 ++
 clang/test/Parser/pragma-fp.cpp               |   4 +-
 .../test/Sema/eval-method-with-unsafe-math.c  |  32 +++++
 12 files changed, 259 insertions(+), 29 deletions(-)
 create mode 100644 clang/test/CodeGen/fp-reciprocal-pragma.cpp

diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 30e288f986782fd..090600275956be0 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -4617,6 +4617,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 ``-freciprocal-math`` flag or other fast-math options. 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 4696836b3a00caa..ad28acf61226136 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -218,6 +218,8 @@ Non-comprehensive list of changes in this release
   For scalable vectors, e.g., SVE or RISC-V V, the number of elements is not known at compile-time and is
   determined at runtime.
 
+* 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 de180344fcc5c74..2f3bef33f936883 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1569,12 +1569,13 @@ def note_pragma_loop_invalid_vectorize_option : Note<
   "vectorize_width(X, scalable) where X is an integer, or vectorize_width('fixed' or 'scalable')">;
 
 def err_pragma_fp_invalid_option : Error<
-  "%select{invalid|missing}0 option%select{ %1|}0; expected 'contract', 'reassociate' or 'exceptions'">;
+  "%select{invalid|missing}0 option%select{ %1|}0; expected 'contract', 'reassociate', 'reciprocal', or 'exceptions'">;
 def err_pragma_fp_invalid_argument : Error<
   "unexpected argument '%0' to '#pragma clang fp %1'; expected "
   "%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/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 474afc2fb99c1f0..d23d3dd8b592ec9 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -6755,7 +6755,7 @@ def warn_floatingpoint_eq : Warning<
 
 def err_setting_eval_method_used_in_unsafe_context : Error <
   "%select{'#pragma clang fp eval_method'|option 'ffp-eval-method'}0 cannot be used with "
-  "%select{option 'fapprox-func'|option 'mreassociate'|option 'freciprocal'|option 'ffp-eval-method'|'#pragma clang fp reassociate'}1">;
+  "%select{option 'fapprox-func'|option 'mreassociate'|option 'freciprocal'|option 'ffp-eval-method'|'#pragma clang fp reassociate'|'#pragma clang fp reciprocal'}1">;
 
 def warn_remainder_division_by_zero : Warning<
   "%select{remainder|division}0 by zero is undefined">,
diff --git a/clang/include/clang/Basic/PragmaKinds.h b/clang/include/clang/Basic/PragmaKinds.h
index 176bbc9ac7caaec..a4789ccde5ec3c1 100644
--- a/clang/include/clang/Basic/PragmaKinds.h
+++ b/clang/include/clang/Basic/PragmaKinds.h
@@ -34,6 +34,15 @@ enum PragmaFloatControlKind {
   PFC_Push,      // #pragma float_control(push)
   PFC_Pop        // #pragma float_control(pop)
 };
+
+enum PragmaFPKind {
+  PFK_Contract, // #pragma clang fp contract
+  PFK_Reassociate, // #pragma clang fp reassociate
+  PFK_Reciprocal,  // #pragma clang fp reciprocal
+  PFK_Exceptions, // #pragma clang fp exceptions
+  PFK_EvalMethod // #pragma clang fp eval_method
+};
+
 }
 
 #endif
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index daed24be0a86d11..8a6cb52736e6c0b 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -10998,7 +10998,10 @@ class Sema final {
 
   /// Called on well formed
   /// \#pragma clang fp reassociate
-  void ActOnPragmaFPReassociate(SourceLocation Loc, bool IsEnabled);
+  /// or
+  /// \#pragma clang fp reciprocal
+  void ActOnPragmaFPValueChangingOption(SourceLocation Loc, PragmaFPKind Kind,
+                                        bool IsEnabled);
 
   /// ActOnPragmaFenvAccess - Called on well formed
   /// \#pragma STDC FENV_ACCESS
diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp
index b3178aef64d72d7..f0d366aaa134eb3 100644
--- a/clang/lib/Parse/ParsePragma.cpp
+++ b/clang/lib/Parse/ParsePragma.cpp
@@ -3191,11 +3191,11 @@ 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 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;
 };
@@ -3219,12 +3219,13 @@ void PragmaFPHandler::HandlePragma(Preprocessor &PP,
     IdentifierInfo *OptionInfo = Tok.getIdentifierInfo();
 
     auto FlagKind =
-        llvm::StringSwitch<std::optional<TokFPAnnotValue::FlagKinds>>(
+        llvm::StringSwitch<std::optional<PragmaFPKind>>(
             OptionInfo->getName())
-            .Case("contract", TokFPAnnotValue::Contract)
-            .Case("reassociate", TokFPAnnotValue::Reassociate)
-            .Case("exceptions", TokFPAnnotValue::Exceptions)
-            .Case("eval_method", TokFPAnnotValue::EvalMethod)
+            .Case("contract", PFK_Contract)
+            .Case("reassociate", PFK_Reassociate)
+            .Case("exceptions", PFK_Exceptions)
+            .Case("eval_method", PFK_EvalMethod)
+            .Case("reciprocal", PFK_Reciprocal)
             .Default(std::nullopt);
     if (!FlagKind) {
       PP.Diag(Tok.getLocation(), diag::err_pragma_fp_invalid_option)
@@ -3240,7 +3241,7 @@ void PragmaFPHandler::HandlePragma(Preprocessor &PP,
     }
     PP.Lex(Tok);
     bool isEvalMethodDouble =
-        Tok.is(tok::kw_double) && FlagKind == TokFPAnnotValue::EvalMethod;
+        Tok.is(tok::kw_double) && FlagKind == PFK_EvalMethod;
 
     // Don't diagnose if we have an eval_metod pragma with "double" kind.
     if (Tok.isNot(tok::identifier) && !isEvalMethodDouble) {
@@ -3251,7 +3252,7 @@ void PragmaFPHandler::HandlePragma(Preprocessor &PP,
     }
     const IdentifierInfo *II = Tok.getIdentifierInfo();
 
-    if (FlagKind == TokFPAnnotValue::Contract) {
+    if (FlagKind == PFK_Contract) {
       AnnotValue->ContractValue =
           llvm::StringSwitch<std::optional<LangOptions::FPModeKind>>(
               II->getName())
@@ -3264,19 +3265,22 @@ 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 == PFK_Reassociate ||
+               FlagKind == PFK_Reciprocal) {
+      auto &Value = FlagKind == PFK_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;
       }
-    } else if (FlagKind == TokFPAnnotValue::Exceptions) {
+    } else if (FlagKind == PFK_Exceptions) {
       AnnotValue->ExceptionsValue =
           llvm::StringSwitch<std::optional<LangOptions::FPExceptionModeKind>>(
               II->getName())
@@ -3289,7 +3293,7 @@ void PragmaFPHandler::HandlePragma(Preprocessor &PP,
             << PP.getSpelling(Tok) << OptionInfo->getName() << *FlagKind;
         return;
       }
-    } else if (FlagKind == TokFPAnnotValue::EvalMethod) {
+    } else if (FlagKind == PFK_EvalMethod) {
       AnnotValue->EvalMethodValue =
           llvm::StringSwitch<std::optional<LangOptions::FPEvalMethodKind>>(
               II->getName())
@@ -3395,9 +3399,15 @@ void Parser::HandlePragmaFP() {
       reinterpret_cast<TokFPAnnotValue *>(Tok.getAnnotationValue());
 
   if (AnnotValue->ReassociateValue)
-    Actions.ActOnPragmaFPReassociate(Tok.getLocation(),
-                                     *AnnotValue->ReassociateValue ==
-                                         LangOptions::FPModeKind::FPM_On);
+    Actions.ActOnPragmaFPValueChangingOption(
+        Tok.getLocation(), PFK_Reassociate,
+        *AnnotValue->ReassociateValue == LangOptions::FPModeKind::FPM_On);
+
+  if (AnnotValue->ReciprocalValue)
+    Actions.ActOnPragmaFPValueChangingOption(
+        Tok.getLocation(), PFK_Reciprocal,
+        *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..79271c872627310 100644
--- a/clang/lib/Sema/SemaAttr.cpp
+++ b/clang/lib/Sema/SemaAttr.cpp
@@ -1285,7 +1285,8 @@ void Sema::ActOnPragmaFPContract(SourceLocation Loc,
   CurFPFeatures = NewFPFeatures.applyOverrides(getLangOpts());
 }
 
-void Sema::ActOnPragmaFPReassociate(SourceLocation Loc, bool IsEnabled) {
+void Sema::ActOnPragmaFPValueChangingOption(SourceLocation Loc,
+                                            PragmaFPKind Kind, bool IsEnabled) {
   if (IsEnabled) {
     // For value unsafe context, combining this pragma with eval method
     // setting is not recommended. See comment in function FixupInvocation#506.
@@ -1301,10 +1302,21 @@ void Sema::ActOnPragmaFPReassociate(SourceLocation Loc, bool IsEnabled) {
       Reason = 0;
     if (Reason != -1)
       Diag(Loc, diag::err_setting_eval_method_used_in_unsafe_context)
-          << Reason << 4;
+          << Reason << (Kind == PFK_Reassociate ? 4 : 5);
   }
+
   FPOptionsOverride NewFPFeatures = CurFPFeatureOverrides();
-  NewFPFeatures.setAllowFPReassociateOverride(IsEnabled);
+  switch (Kind) {
+  case PFK_Reassociate:
+    NewFPFeatures.setAllowFPReassociateOverride(IsEnabled);
+    break;
+  case PFK_Reciprocal:
+    NewFPFeatures.setAllowReciprocalOverride(IsEnabled);
+    break;
+  default:
+    llvm_unreachable("unhandled value changing pragma fp");
+  }
+
   FpPragmaStack.Act(Loc, PSK_Set, StringRef(), NewFPFeatures);
   CurFPFeatures = NewFPFeatures.applyOverrides(getLangOpts());
 }
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;
+}
diff --git a/clang/test/Parser/pragma-fp.cpp b/clang/test/Parser/pragma-fp.cpp
index 0377a1b259f465c..737aee03f4bb015 100644
--- a/clang/test/Parser/pragma-fp.cpp
+++ b/clang/test/Parser/pragma-fp.cpp
@@ -1,14 +1,14 @@
 // RUN: %clang_cc1 -std=c++11 -verify %s
 
 void test_0(int *List, int Length) {
-/* expected-error at +1 {{missing option; expected 'contract', 'reassociate' or 'exceptions'}} */
+/* expected-error at +1 {{missing option; expected 'contract', 'reassociate', 'reciprocal', or 'exceptions'}} */
 #pragma clang fp
   for (int i = 0; i < Length; i++) {
     List[i] = i;
   }
 }
 void test_1(int *List, int Length) {
-/* expected-error at +1 {{invalid option 'blah'; expected 'contract', 'reassociate' or 'exceptions'}} */
+/* expected-error at +1 {{invalid option 'blah'; expected 'contract', 'reassociate', 'reciprocal', or 'exceptions'}} */
 #pragma clang fp blah
   for (int i = 0; i < Length; i++) {
     List[i] = i;
diff --git a/clang/test/Sema/eval-method-with-unsafe-math.c b/clang/test/Sema/eval-method-with-unsafe-math.c
index b68dc6e7505d379..eb61e1d9f3e6350 100644
--- a/clang/test/Sema/eval-method-with-unsafe-math.c
+++ b/clang/test/Sema/eval-method-with-unsafe-math.c
@@ -54,3 +54,35 @@ float f4(float a, float b, float c) {
 #pragma clang fp reassociate(on)
   return (a * c) - (b * c);
 }
+
+float f4_reciprocal(float a, float b, float c) {
+#pragma clang fp eval_method(double)
+  // CHECK-FUNC: '#pragma clang fp eval_method' cannot be used with option 'fapprox-func'
+  // CHECK-ASSOC: '#pragma clang fp eval_method' cannot be used with option 'mreassociate'
+  // CHECK-RECPR: '#pragma clang fp eval_method' cannot be used with option 'freciprocal'
+  // CHECK-PRGM: '#pragma clang fp eval_method' cannot be used with '#pragma clang fp reciprocal'
+#pragma clang fp reciprocal(on)
+  return (a * c) / (b * c);
+}
+
+float f4_reciprocal_reassociate(float a, float b, float c) {
+#pragma clang fp eval_method(double)
+  // CHECK-FUNC: '#pragma clang fp eval_method' cannot be used with option 'fapprox-func'
+  // CHECK-ASSOC: '#pragma clang fp eval_method' cannot be used with option 'mreassociate'
+  // CHECK-RECPR: '#pragma clang fp eval_method' cannot be used with option 'freciprocal'
+  // CHECK-PRGM: '#pragma clang fp eval_method' cannot be used with '#pragma clang fp reassociate'
+  // CHECK-PRGM: '#pragma clang fp eval_method' cannot be used with '#pragma clang fp reciprocal'
+#pragma clang fp reciprocal(on) reassociate(on)
+  return (a * c) / (b * c);
+}
+
+float f2_reciprocal(float a, float b, float c) {
+  // CHECK-FFP-OPT: option 'ffp-eval-method' cannot be used with '#pragma clang fp reciprocal'
+#pragma clang fp reciprocal(on)
+  return (a + b) / c;
+}
+
+float f3_reciprocal(float a, float b, float c) {
+#pragma clang fp reciprocal(off)
+  return (a - b) / c;
+}



More information about the cfe-commits mailing list