Add option 'exceptions' to pragma clang fp

Pragma 'clang fp' is extended to support a new option, 'exceptions'. It
allows to specify floating point exception behavior more flexibly.

Differential Revision: https://reviews.llvm.org/D89849
This commit is contained in:
Serge Pavlov 2020-10-21 00:56:39 +07:00
parent 27f647d117
commit 6021cbea4d
7 changed files with 134 additions and 54 deletions

View File

@ -3182,7 +3182,7 @@ The pragma can take two values: ``on`` and ``off``.
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 @@ The pragma can also be used with ``off`` which turns FP contraction off for a
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 <http://llvm.org/docs/LangRef.html#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

View File

@ -1388,12 +1388,13 @@ def err_pragma_loop_invalid_option : Error<
"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'">;

View File

@ -9896,6 +9896,10 @@ public:
/// \#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);

View File

@ -2858,11 +2858,12 @@ void PragmaOptimizeHandler::HandlePragma(Preprocessor &PP,
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<LangOptions::FPModeKind> ContractValue;
llvm::Optional<LangOptions::FPModeKind> ReassociateValue;
llvm::Optional<LangOptions::FPExceptionModeKind> ExceptionsValue;
};
} // end anonymous namespace
@ -2879,6 +2880,7 @@ void PragmaFPHandler::HandlePragma(Preprocessor &PP,
return;
}
auto *AnnotValue = new (PP.getPreprocessorAllocator()) TokFPAnnotValue;
while (Tok.is(tok::identifier)) {
IdentifierInfo *OptionInfo = Tok.getIdentifierInfo();
@ -2887,6 +2889,7 @@ void PragmaFPHandler::HandlePragma(Preprocessor &PP,
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 @@ void PragmaFPHandler::HandlePragma(Preprocessor &PP,
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<int>(*FlagKind);
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 || (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<llvm::Optional<LangOptions::FPModeKind>>(
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<llvm::Optional<LangOptions::FPModeKind>>(
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<llvm::Optional<LangOptions::FPExceptionModeKind>>(
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 @@ void PragmaFPHandler::HandlePragma(Preprocessor &PP,
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<void *>(AnnotValue));
TokenList.push_back(FPTok);
}
if (Tok.isNot(tok::eod)) {
@ -2952,6 +2968,14 @@ void PragmaFPHandler::HandlePragma(Preprocessor &PP,
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 = std::make_unique<Token[]>(TokenList.size());
std::copy(TokenList.begin(), TokenList.end(), TokenArray.get());
@ -3019,24 +3043,16 @@ void Parser::HandlePragmaFP() {
auto *AnnotValue =
reinterpret_cast<TokFPAnnotValue *>(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();
}

View File

@ -1022,6 +1022,11 @@ void Sema::ActOnPragmaFEnvAccess(SourceLocation Loc, bool IsEnabled) {
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.

View File

@ -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")

View File

@ -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 @@ void test_8(int *List, int Length) {
#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;
}