sepavloff created this revision.
sepavloff added reviewers: anemet, aaron.ballman, kpn, hfinkel, rsmith, 
rjmccall.
Herald added a project: clang.

Pragma 'clang fp' is extended to support two new options, 'rounding' and
'exceptions'. They allow to specifying floating point environment more
precisely than '#pragma STDC FENV_ACCESS' does. Syntax of the pragma is
described in LanguageExtensions.rst.
The primary goal of this change is to facilitate development of support
for floating point operations in non-default or dynamic environment.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D65997

Files:
  clang/docs/LanguageExtensions.rst
  clang/include/clang/Basic/DiagnosticParseKinds.td
  clang/lib/Parse/ParsePragma.cpp
  clang/test/Parser/pragma-fp.cpp

Index: clang/test/Parser/pragma-fp.cpp
===================================================================
--- clang/test/Parser/pragma-fp.cpp
+++ 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@+1 {{missing option; expected contract}} */
+/* expected-error@+1 {{missing option; expected 'contract', 'rounding' or 'exceptions'}} */
 #pragma clang fp
   for (int i = 0; i < Length; i++) {
     List[i] = i;
   }
 }
 void test_1(int *List, int Length) {
-/* expected-error@+1 {{invalid option 'blah'; expected contract}} */
+/* expected-error@+1 {{invalid option 'blah'; expected 'contract', 'rounding' or 'exceptions'}} */
 #pragma clang fp blah
   for (int i = 0; i < Length; i++) {
     List[i] = i;
@@ -62,3 +62,37 @@
 #pragma clang fp contract(fast)
   }
 }
+
+
+void test_10(float *dest, float a, float b) {
+/* expected-error@+1 {{expected '('}} */
+#pragma clang fp rounding on
+  *dest = a + b;
+}
+
+void test_11(float *dest, float a, float b) {
+/* expected-error@+1 {{unexpected argument 'while' to '#pragma clang fp rounding'; expected 'tonearest', 'downward', 'upward', 'towardzero' or 'dynamic'}} */
+#pragma clang fp rounding(while)
+  *dest = a + b;
+}
+
+void test_12(float *dest, float a, float b) {
+#pragma clang fp rounding(downward)
+  *dest = a + b;
+}
+
+void test_13(float *dest, float a, float b) {
+#pragma clang fp contract(fast) rounding(downward)
+  *dest = a + b;
+}
+
+void test_14(float *dest, float a, float b) {
+/* expected-error@+1 {{unexpected argument 'on' to '#pragma clang fp exceptions'; expected 'ignore', 'maytrap' or 'strict'}} */
+#pragma clang fp exceptions(on)
+  *dest = a + b;
+}
+
+void test_15(float *dest, float a, float b) {
+#pragma clang fp exceptions(maytrap) contract(fast) rounding(downward)
+  *dest = a + b;
+}
Index: clang/lib/Parse/ParsePragma.cpp
===================================================================
--- clang/lib/Parse/ParsePragma.cpp
+++ clang/lib/Parse/ParsePragma.cpp
@@ -2654,11 +2654,11 @@
 namespace {
 /// Used as the annotation value for tok::annot_pragma_fp.
 struct TokFPAnnotValue {
-  enum FlagKinds { Contract };
-  enum FlagValues { On, Off, Fast };
+  enum FlagKinds { Contract, RoundingMode, Exceptions };
 
-  FlagKinds FlagKind;
-  FlagValues FlagValue;
+  llvm::Optional<LangOptions::FPContractModeKind> ContractValue;
+  llvm::Optional<LangOptions::FPRoundingModeKind> RoundingModeValue;
+  llvm::Optional<LangOptions::FPExceptionModeKind> ExceptionsValue;
 };
 } // end anonymous namespace
 
@@ -2667,14 +2667,17 @@
   // fp
   Token PragmaName = Tok;
   SmallVector<Token, 1> TokenList;
+  static const StringRef ExpectedOptionsText =
+      "'contract', 'rounding' or 'exceptions'";
 
   PP.Lex(Tok);
   if (Tok.isNot(tok::identifier)) {
     PP.Diag(Tok.getLocation(), diag::err_pragma_fp_invalid_option)
-        << /*MissingOption=*/true << "";
+        << /*MissingOption=*/true << "" << ExpectedOptionsText;
     return;
   }
 
+  auto *AnnotValue = new (PP.getPreprocessorAllocator()) TokFPAnnotValue;
   while (Tok.is(tok::identifier)) {
     IdentifierInfo *OptionInfo = Tok.getIdentifierInfo();
 
@@ -2682,13 +2685,28 @@
         llvm::StringSwitch<llvm::Optional<TokFPAnnotValue::FlagKinds>>(
             OptionInfo->getName())
             .Case("contract", TokFPAnnotValue::Contract)
+            .Case("rounding", TokFPAnnotValue::RoundingMode)
+            .Case("exceptions", TokFPAnnotValue::Exceptions)
             .Default(None);
     if (!FlagKind) {
       PP.Diag(Tok.getLocation(), diag::err_pragma_fp_invalid_option)
-          << /*MissingOption=*/false << OptionInfo;
+          << /*MissingOption=*/false << OptionInfo << ExpectedOptionsText;
       return;
     }
     PP.Lex(Tok);
+    StringRef ExpectedArgumentText;
+    switch (*FlagKind) {
+    case TokFPAnnotValue::Contract:
+      ExpectedArgumentText = "'on', 'fast' or 'off'";
+      break;
+    case TokFPAnnotValue::RoundingMode:
+      ExpectedArgumentText =
+          "'tonearest', 'downward', 'upward', 'towardzero' or 'dynamic'";
+      break;
+    case TokFPAnnotValue::Exceptions:
+      ExpectedArgumentText = "'ignore', 'maytrap' or 'strict'";
+      break;
+    }
 
     // Read '('
     if (Tok.isNot(tok::l_paren)) {
@@ -2699,23 +2717,56 @@
 
     if (Tok.isNot(tok::identifier)) {
       PP.Diag(Tok.getLocation(), diag::err_pragma_fp_invalid_argument)
-          << PP.getSpelling(Tok) << OptionInfo->getName();
+          << PP.getSpelling(Tok) << OptionInfo->getName()
+          << ExpectedArgumentText;
       return;
     }
     const IdentifierInfo *II = Tok.getIdentifierInfo();
 
-    auto FlagValue =
-        llvm::StringSwitch<llvm::Optional<TokFPAnnotValue::FlagValues>>(
-            II->getName())
-            .Case("on", TokFPAnnotValue::On)
-            .Case("off", TokFPAnnotValue::Off)
-            .Case("fast", TokFPAnnotValue::Fast)
-            .Default(llvm::None);
-
-    if (!FlagValue) {
-      PP.Diag(Tok.getLocation(), diag::err_pragma_fp_invalid_argument)
-          << PP.getSpelling(Tok) << OptionInfo->getName();
-      return;
+    if (FlagKind == TokFPAnnotValue::Contract) {
+      AnnotValue->ContractValue =
+          llvm::StringSwitch<llvm::Optional<LangOptions::FPContractModeKind>>(
+              II->getName())
+              .Case("on", LangOptions::FPContractModeKind::FPC_On)
+              .Case("off", LangOptions::FPContractModeKind::FPC_Off)
+              .Case("fast", LangOptions::FPContractModeKind::FPC_Fast)
+              .Default(llvm::None);
+      if (!AnnotValue->ContractValue) {
+        PP.Diag(Tok.getLocation(), diag::err_pragma_fp_invalid_argument)
+            << PP.getSpelling(Tok) << OptionInfo->getName()
+            << "'on', 'fast' or 'off'";
+        return;
+      }
+    } else if (FlagKind == TokFPAnnotValue::RoundingMode) {
+      AnnotValue->RoundingModeValue =
+          llvm::StringSwitch<llvm::Optional<LangOptions::FPRoundingModeKind>>(
+              II->getName())
+              .Case("tonearest", LangOptions::FPRoundingModeKind::ToNearest)
+              .Case("downward", LangOptions::FPRoundingModeKind::Downward)
+              .Case("upward", LangOptions::FPRoundingModeKind::Upward)
+              .Case("towardzero", LangOptions::FPRoundingModeKind::TowardZero)
+              .Case("dynamic", LangOptions::FPRoundingModeKind::Dynamic)
+              .Default(llvm::None);
+      if (!AnnotValue->RoundingModeValue) {
+        PP.Diag(Tok.getLocation(), diag::err_pragma_fp_invalid_argument)
+            << PP.getSpelling(Tok) << OptionInfo->getName()
+            << "'dynamic', 'tonearest', 'downward', 'upward' or 'towardzero'";
+        return;
+      }
+    } else if (FlagKind == TokFPAnnotValue::Exceptions) {
+      AnnotValue->ExceptionsValue =
+          llvm::StringSwitch<llvm::Optional<LangOptions::FPExceptionModeKind>>(
+              II->getName())
+              .Case("ignore", LangOptions::FPExceptionModeKind::Ignore)
+              .Case("maytrap", LangOptions::FPExceptionModeKind::MayTrap)
+              .Case("strict", LangOptions::FPExceptionModeKind::Strict)
+              .Default(llvm::None);
+      if (!AnnotValue->ExceptionsValue) {
+        PP.Diag(Tok.getLocation(), diag::err_pragma_fp_invalid_argument)
+            << PP.getSpelling(Tok) << OptionInfo->getName()
+            << "'ignore', 'maytrap' or 'strict'";
+        return;
+      }
     }
     PP.Lex(Tok);
 
@@ -2725,17 +2776,6 @@
       return;
     }
     PP.Lex(Tok);
-
-    auto *AnnotValue = new (PP.getPreprocessorAllocator())
-        TokFPAnnotValue{*FlagKind, *FlagValue};
-    // Generate the loop hint token.
-    Token FPTok;
-    FPTok.startToken();
-    FPTok.setKind(tok::annot_pragma_fp);
-    FPTok.setLocation(PragmaName.getLocation());
-    FPTok.setAnnotationEndLoc(PragmaName.getLocation());
-    FPTok.setAnnotationValue(reinterpret_cast<void *>(AnnotValue));
-    TokenList.push_back(FPTok);
   }
 
   if (Tok.isNot(tok::eod)) {
@@ -2744,6 +2784,14 @@
     return;
   }
 
+  Token FPTok;
+  FPTok.startToken();
+  FPTok.setKind(tok::annot_pragma_fp);
+  FPTok.setLocation(PragmaName.getLocation());
+  FPTok.setAnnotationEndLoc(PragmaName.getLocation());
+  FPTok.setAnnotationValue(reinterpret_cast<void *>(AnnotValue));
+  TokenList.push_back(FPTok);
+
   auto TokenArray = llvm::make_unique<Token[]>(TokenList.size());
   std::copy(TokenList.begin(), TokenList.end(), TokenArray.get());
 
@@ -2756,20 +2804,13 @@
   auto *AnnotValue =
       reinterpret_cast<TokFPAnnotValue *>(Tok.getAnnotationValue());
 
-  LangOptions::FPContractModeKind FPC;
-  switch (AnnotValue->FlagValue) {
-  case TokFPAnnotValue::On:
-    FPC = LangOptions::FPC_On;
-    break;
-  case TokFPAnnotValue::Fast:
-    FPC = LangOptions::FPC_Fast;
-    break;
-  case TokFPAnnotValue::Off:
-    FPC = LangOptions::FPC_Off;
-    break;
-  }
+  if (AnnotValue->ContractValue)
+    Actions.ActOnPragmaFPContract(*AnnotValue->ContractValue);
+  if (AnnotValue->RoundingModeValue)
+    Actions.setRoundingMode(*AnnotValue->RoundingModeValue);
+  if (AnnotValue->ExceptionsValue)
+    Actions.setExceptionMode(*AnnotValue->ExceptionsValue);
 
-  Actions.ActOnPragmaFPContract(FPC);
   ConsumeAnnotationToken();
 }
 
Index: clang/include/clang/Basic/DiagnosticParseKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticParseKinds.td
+++ clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1213,10 +1213,9 @@
   "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 %2">;
 def err_pragma_fp_invalid_argument : Error<
-  "unexpected argument '%0' to '#pragma clang fp %1'; "
-  "expected 'on', 'fast' or 'off'">;
+  "unexpected argument '%0' to '#pragma clang fp %1'; expected %2">;
 def err_pragma_fp_scope : Error<
   "'#pragma clang fp' can only appear at file scope or at the start of a "
   "compound statement">;
Index: clang/docs/LanguageExtensions.rst
===================================================================
--- clang/docs/LanguageExtensions.rst
+++ clang/docs/LanguageExtensions.rst
@@ -3117,10 +3117,12 @@
 compound statement, the pragma is active within the scope of the compound
 statement.
 
-Currently, only FP contraction can be controlled with the pragma. ``#pragma
-clang fp contract`` specifies whether the compiler should contract a multiply
-and an addition (or subtraction) into a fused FMA operation when supported by
-the target.
+Currently, only FP contraction, rounding mode and exception behavior can be
+controlled with the pragma.
+
+The option ``contract`` specifies whether the compiler should contract a
+multiply and an addition (or subtraction) into a fused FMA operation when
+supported by the target.
 
 The pragma can take three values: ``on``, ``fast`` and ``off``.  The ``on``
 option is identical to using ``#pragma STDC FP_CONTRACT(ON)`` and it allows
@@ -3141,6 +3143,30 @@
 section of the code. This can be useful when fast contraction is otherwise
 enabled for the translation unit with the ``-ffp-contract=fast`` flag.
 
+The option ``rounding`` specifies rounding mode applied to floating point
+operations. It may take one of the values: ``tonearest``, ``downward``,
+``upward``, ``towardzero`` and ``dynamic``. The first four values represent
+corresponding IEEE rounding rules, ``tonearest`` being the default mode. The
+value ``dynamic`` informs the compiler that it must not assume particular
+rounding mode. This option is experimental, compiler does not guarantee to
+process it in all cases.
+
+The option ``exceptions`` specify floating point exception behavior. It may
+take one the the values: ``ignore``, ``maytrap`` or ``strict``. Default value
+is ``ignore``. Meaning of these values is same as for `constrained floating
+point intrinsics <http://llvm.org/docs/LangRef.html#id2115>`_. This option is
+experimental, compiler does not guarantee to process it in all cases.
+
+A ``#pragma clang fp`` pragma may contain any number of options:
+
+.. code-block:: c++
+
+  void func(float *dest, float a, float b) {
+    #pragma clang fp exceptions(maytrap) contract(fast) rounding(downward)
+    *dest = a + b;
+  }
+
+
 Specifying an attribute for multiple declarations (#pragma clang attribute)
 ===========================================================================
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to