diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst --- a/clang/docs/LanguageExtensions.rst +++ b/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,31 @@ 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 the 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 any particular +rounding mode. This option is experimental; the compiler may ignore an explicit +rounding mode in some situations. + +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 `_. +This option is experimental; the compiler may ignore an explicit exception +behavior in some situations. + +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) =========================================================================== 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 @@ -1213,10 +1213,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', 'rounding' or 'exceptions'">; 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 %select{" + "'on', 'fast' or 'off'|" + "'tonearest', 'downward', 'upward', 'towardzero' or 'dynamic'|" + "'ignore', 'maytrap' or 'strict'}2" + >; def err_pragma_fp_scope : Error< "'#pragma clang fp' can only appear at file scope or at the start of a " "compound statement">; 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 @@ -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 ContractValue; + llvm::Optional RoundingModeValue; + llvm::Optional ExceptionsValue; }; } // end anonymous namespace @@ -2671,10 +2671,11 @@ PP.Lex(Tok); if (Tok.isNot(tok::identifier)) { PP.Diag(Tok.getLocation(), diag::err_pragma_fp_invalid_option) - << /*MissingOption=*/true << ""; + << /*MissingOption=*/true; return; } + auto *AnnotValue = new (PP.getPreprocessorAllocator()) TokFPAnnotValue; while (Tok.is(tok::identifier)) { IdentifierInfo *OptionInfo = Tok.getIdentifierInfo(); @@ -2682,6 +2683,8 @@ llvm::StringSwitch>( 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) @@ -2699,23 +2702,53 @@ 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() + << *FlagKind; return; } const IdentifierInfo *II = Tok.getIdentifierInfo(); - auto FlagValue = - llvm::StringSwitch>( - 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>( + 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() << *FlagKind; + return; + } + } else if (FlagKind == TokFPAnnotValue::RoundingMode) { + AnnotValue->RoundingModeValue = + llvm::StringSwitch>( + 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() << *FlagKind; + return; + } + } else if (FlagKind == TokFPAnnotValue::Exceptions) { + AnnotValue->ExceptionsValue = + llvm::StringSwitch>( + 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() << *FlagKind; + return; + } } PP.Lex(Tok); @@ -2725,17 +2758,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(AnnotValue)); - TokenList.push_back(FPTok); } if (Tok.isNot(tok::eod)) { @@ -2744,6 +2766,14 @@ return; } + Token FPTok; + FPTok.startToken(); + FPTok.setKind(tok::annot_pragma_fp); + FPTok.setLocation(PragmaName.getLocation()); + FPTok.setAnnotationEndLoc(PragmaName.getLocation()); + FPTok.setAnnotationValue(reinterpret_cast(AnnotValue)); + TokenList.push_back(FPTok); + auto TokenArray = llvm::make_unique(TokenList.size()); std::copy(TokenList.begin(), TokenList.end(), TokenArray.get()); @@ -2756,20 +2786,13 @@ auto *AnnotValue = reinterpret_cast(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(); } diff --git a/clang/test/Parser/pragma-fp.cpp b/clang/test/Parser/pragma-fp.cpp --- 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@+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; +}