diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -3182,7 +3182,7 @@ float f(float x, float y, float z) { // Enable floating point reassociation across statements - #pragma fp reassociate(on) + #pragma clang fp reassociate(on) float t = x + y; float v = t + z; } @@ -3211,6 +3211,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. + +``#pragma clang fp exceptions`` specifies floating point exception behavior. It +may take one the the values: ``ignore``, ``maytrap`` or ``strict``. Meaning of +these values is same as for `constrained floating point intrinsics `_. + +.. code-block:: c++ + + { + // Preserve floating point exceptions + #pragma clang fp exceptions(strict) + z = x + y; + if (fetestexcept(FE_OVERFLOW)) + ... + } + +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) reassociate(on) + ... + } + + The ``#pragma float_control`` pragma allows precise floating-point semantics and floating-point exception behavior to be specified for a section of the source code. This pragma can only appear at file scope or 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 @@ -1388,12 +1388,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' or 'reassociate'">; + "%select{invalid|missing}0 option%select{ %1|}0; expected 'contract', 'reassociate' or 'exceptions'">; def err_pragma_fp_invalid_argument : Error< - "unexpected argument '%0' to '#pragma clang fp %1'; " + "unexpected argument '%0' to '#pragma clang fp %1'; expected " "%select{" - "expected 'fast' or 'on' or 'off'|" - "expected 'on' or 'off'}2">; + "'fast' or 'on' or 'off'|" + "'on' or 'off'|" + "'ignore', 'maytrap' or 'strict'}2">; def err_pragma_invalid_keyword : Error< "invalid argument; expected 'enable'%select{|, 'full'}0%select{|, 'assume_safety'}1 or 'disable'">; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -9896,6 +9896,10 @@ /// \#pragma STDC FENV_ACCESS void ActOnPragmaFEnvAccess(SourceLocation Loc, bool IsEnabled); + /// Called on well formed '\#pragma clang fp' that has option 'exceptions'. + void ActOnPragmaFPExceptions(SourceLocation Loc, + LangOptions::FPExceptionModeKind); + /// Called to set constant rounding mode for floating point operations. void setRoundingMode(SourceLocation Loc, llvm::RoundingMode); 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 @@ -2858,11 +2858,12 @@ namespace { /// Used as the annotation value for tok::annot_pragma_fp. struct TokFPAnnotValue { - enum FlagKinds { Contract, Reassociate }; + enum FlagKinds { Contract, Reassociate, Exceptions }; enum FlagValues { On, Off, Fast }; - FlagKinds FlagKind; - FlagValues FlagValue; + llvm::Optional ContractValue; + llvm::Optional ReassociateValue; + llvm::Optional ExceptionsValue; }; } // end anonymous namespace @@ -2879,6 +2880,7 @@ return; } + auto *AnnotValue = new (PP.getPreprocessorAllocator()) TokFPAnnotValue; while (Tok.is(tok::identifier)) { IdentifierInfo *OptionInfo = Tok.getIdentifierInfo(); @@ -2887,6 +2889,7 @@ OptionInfo->getName()) .Case("contract", TokFPAnnotValue::Contract) .Case("reassociate", TokFPAnnotValue::Reassociate) + .Case("exceptions", TokFPAnnotValue::Exceptions) .Default(None); if (!FlagKind) { PP.Diag(Tok.getLocation(), diag::err_pragma_fp_invalid_option) @@ -2905,25 +2908,49 @@ if (Tok.isNot(tok::identifier)) { PP.Diag(Tok.getLocation(), diag::err_pragma_fp_invalid_argument) << PP.getSpelling(Tok) << OptionInfo->getName() - << (FlagKind == TokFPAnnotValue::Reassociate); + << static_cast(*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 || (FlagKind == TokFPAnnotValue::Reassociate && - FlagValue == TokFPAnnotValue::Fast)) { - PP.Diag(Tok.getLocation(), diag::err_pragma_fp_invalid_argument) - << PP.getSpelling(Tok) << OptionInfo->getName() - << (FlagKind == TokFPAnnotValue::Reassociate); - return; + if (FlagKind == TokFPAnnotValue::Contract) { + AnnotValue->ContractValue = + llvm::StringSwitch>( + II->getName()) + .Case("on", LangOptions::FPModeKind::FPM_On) + .Case("off", LangOptions::FPModeKind::FPM_Off) + .Case("fast", LangOptions::FPModeKind::FPM_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::Reassociate) { + AnnotValue->ReassociateValue = + llvm::StringSwitch>( + II->getName()) + .Case("on", LangOptions::FPModeKind::FPM_On) + .Case("off", LangOptions::FPModeKind::FPM_Off) + .Default(llvm::None); + if (!AnnotValue->ReassociateValue) { + 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::FPE_Ignore) + .Case("maytrap", LangOptions::FPE_MayTrap) + .Case("strict", LangOptions::FPE_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); @@ -2933,17 +2960,6 @@ return; } PP.Lex(Tok); - - auto *AnnotValue = new (PP.getPreprocessorAllocator()) - TokFPAnnotValue{*FlagKind, *FlagValue}; - // Generate the fp annotation 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)) { @@ -2952,6 +2968,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 = std::make_unique(TokenList.size()); std::copy(TokenList.begin(), TokenList.end(), TokenArray.get()); @@ -3019,24 +3043,16 @@ auto *AnnotValue = reinterpret_cast(Tok.getAnnotationValue()); - if (AnnotValue->FlagKind == TokFPAnnotValue::Reassociate) - Actions.ActOnPragmaFPReassociate( - Tok.getLocation(), AnnotValue->FlagValue == TokFPAnnotValue::On); - else { - LangOptions::FPModeKind FPC; - switch (AnnotValue->FlagValue) { - case TokFPAnnotValue::Off: - FPC = LangOptions::FPM_Off; - break; - case TokFPAnnotValue::On: - FPC = LangOptions::FPM_On; - break; - case TokFPAnnotValue::Fast: - FPC = LangOptions::FPM_Fast; - break; - } - Actions.ActOnPragmaFPContract(Tok.getLocation(), FPC); - } + if (AnnotValue->ReassociateValue) + Actions.ActOnPragmaFPReassociate(Tok.getLocation(), + *AnnotValue->ReassociateValue == + LangOptions::FPModeKind::FPM_On); + if (AnnotValue->ContractValue) + Actions.ActOnPragmaFPContract(Tok.getLocation(), + *AnnotValue->ContractValue); + if (AnnotValue->ExceptionsValue) + Actions.ActOnPragmaFPExceptions(Tok.getLocation(), + *AnnotValue->ExceptionsValue); ConsumeAnnotationToken(); } diff --git a/clang/lib/Sema/SemaAttr.cpp b/clang/lib/Sema/SemaAttr.cpp --- a/clang/lib/Sema/SemaAttr.cpp +++ b/clang/lib/Sema/SemaAttr.cpp @@ -1022,6 +1022,11 @@ CurFPFeatures = NewFPFeatures.applyOverrides(LO); } +void Sema::ActOnPragmaFPExceptions(SourceLocation Loc, + LangOptions::FPExceptionModeKind FPE) { + setExceptionMode(Loc, FPE); +} + void Sema::PushNamespaceVisibilityAttr(const VisibilityAttr *Attr, SourceLocation Loc) { // Visibility calculations will consider the namespace's visibility. diff --git a/clang/test/CodeGen/pragma-fp-exc.cpp b/clang/test/CodeGen/pragma-fp-exc.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/pragma-fp-exc.cpp @@ -0,0 +1,18 @@ +// RUN: %clang_cc1 -triple %itanium_abi_triple -emit-llvm -o - %s | FileCheck --check-prefix=CHECK-DEF %s +// RUN: %clang_cc1 -triple %itanium_abi_triple -ffp-exception-behavior=strict -emit-llvm -o - %s | FileCheck --check-prefix=CHECK-STRICT %s + +float func_01(float x, float y, float z) { + float res = x + y; + { +#pragma clang fp exceptions(maytrap) + res += z; + } + return res; +} +// CHECK-DEF-LABEL: @_Z7func_01fff +// CHECK-DEF: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// CHECK-DEF: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.maytrap") + +// CHECK-STRICT-LABEL: @_Z7func_01fff +// CHECK-STRICT: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// CHECK-STRICT: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.maytrap") 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' or 'reassociate'}} */ +/* expected-error@+1 {{missing option; expected 'contract', 'reassociate' 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' or 'reassociate'}} */ +/* expected-error@+1 {{invalid option 'blah'; expected 'contract', 'reassociate' or 'exceptions'}} */ #pragma clang fp blah for (int i = 0; i < Length; i++) { List[i] = i; @@ -62,3 +62,14 @@ #pragma clang fp contract(fast) } } + +void test_9(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_10(float *dest, float a, float b) { +#pragma clang fp exceptions(maytrap) contract(fast) reassociate(on) + *dest = a + b; +}