Skip to content

Commit

Permalink
Add support for #pragma clang fp reassociate(on|off)
Browse files Browse the repository at this point in the history
Reviewers: rjmccall, erichkeane, sepavloff

Differential Revision: https://reviews.llvm.org/D78827
  • Loading branch information
Melanie Blower committed May 6, 2020
1 parent f5f83cf commit c355bec
Show file tree
Hide file tree
Showing 12 changed files with 220 additions and 91 deletions.
32 changes: 26 additions & 6 deletions clang/docs/LanguageExtensions.rst
Expand Up @@ -3173,16 +3173,36 @@ at the start of a compound statement (excluding comments). When using within a
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, the following settings can be controlled with this pragma:
``#pragma clang fp reassociate`` allows control over the reassociation
of floating point expressions. When enabled, this pragma allows the expression
``x + (y + z)`` to be reassociated as ``(x + y) + z``.
Reassociation can also occur across multiple statements.
This pragma can be used to disable reassociation when it is otherwise
enabled for the translation unit with the ``-fassociative-math`` flag.
The pragma can take two values: ``on`` and ``off``.
.. code-block:: c++
float f(float x, float y, float z)
{
// Enable floating point reassociation across statements
#pragma fp reassociate(on)
float t = x + y;
float v = t + z;
}
``#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.
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
fusion as specified the language standard. The ``fast`` option allows fusiong
fusion as specified the language standard. The ``fast`` option allows fusion
in cases when the language standard does not make this possible (e.g. across
statements in C)
statements in C).
.. code-block:: c++
Expand Down
6 changes: 4 additions & 2 deletions clang/include/clang/Basic/DiagnosticParseKinds.td
Expand Up @@ -1332,10 +1332,12 @@ 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">;
"%select{invalid|missing}0 option%select{ %1|}0; expected 'contract' or 'reassociate'">;
def err_pragma_fp_invalid_argument : Error<
"unexpected argument '%0' to '#pragma clang fp %1'; "
"expected 'on', 'fast' or 'off'">;
"%select{"
"expected 'fast' or 'on' or 'off'|"
"expected 'on' or 'off'}2">;

def err_pragma_invalid_keyword : Error<
"invalid argument; expected 'enable'%select{|, 'full'}0%select{|, 'assume_safety'}1 or 'disable'">;
Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/Basic/LangOptions.def
Expand Up @@ -268,7 +268,7 @@ BENIGN_LANGOPT(SpellChecking , 1, 1, "spell-checking")
LANGOPT(SinglePrecisionConstants , 1, 0, "treating double-precision floating point constants as single precision constants")
LANGOPT(FastRelaxedMath , 1, 0, "OpenCL fast relaxed math")
/// FP_CONTRACT mode (on/off/fast).
ENUM_LANGOPT(DefaultFPContractMode, FPContractModeKind, 2, FPC_Off, "FP contraction type")
ENUM_LANGOPT(DefaultFPContractMode, FPModeKind, 2, FPM_Off, "FP contraction type")
ENUM_LANGOPT(FPRoundingMode, RoundingMode, 3, RoundingMode::NearestTiesToEven, "FP Rounding Mode type")
ENUM_LANGOPT(FPExceptionMode, FPExceptionModeKind, 2, FPE_Ignore, "FP Exception Behavior Mode type")
LANGOPT(NoBitFieldTypeAlign , 1, 0, "bit-field type alignment")
Expand Down
50 changes: 20 additions & 30 deletions clang/include/clang/Basic/LangOptions.h
Expand Up @@ -174,22 +174,15 @@ class LangOptions : public LangOptionsBase {
Swift4_1,
};

enum FPContractModeKind {
// Form fused FP ops only where result will not be affected.
FPC_Off,
enum FPModeKind {
// Disable the floating point pragma
FPM_Off,

// Form fused FP ops according to FP_CONTRACT rules.
FPC_On,
// Enable the floating point pragma
FPM_On,

// Aggressively fuse FP ops (E.g. FMA).
FPC_Fast
};

// TODO: merge FEnvAccessModeKind and FPContractModeKind
enum FEnvAccessModeKind {
FEA_Off,

FEA_On
FPM_Fast
};

/// Alias for RoundingMode::NearestTiesToEven.
Expand Down Expand Up @@ -378,7 +371,7 @@ class FPOptions {

public:
FPOptions()
: fp_contract(LangOptions::FPC_Off), fenv_access(LangOptions::FEA_Off),
: fp_contract(LangOptions::FPM_Off), fenv_access(LangOptions::FPM_Off),
rounding(LangOptions::FPR_ToNearest),
exceptions(LangOptions::FPE_Ignore), allow_reassoc(0), no_nans(0),
no_infs(0), no_signed_zeros(0), allow_reciprocal(0), approx_func(0) {}
Expand All @@ -388,7 +381,7 @@ class FPOptions {

explicit FPOptions(const LangOptions &LangOpts)
: fp_contract(LangOpts.getDefaultFPContractMode()),
fenv_access(LangOptions::FEA_Off),
fenv_access(LangOptions::FPM_Off),
rounding(static_cast<unsigned>(LangOpts.getFPRoundingMode())),
exceptions(LangOpts.getFPExceptionMode()),
allow_reassoc(LangOpts.FastMath || LangOpts.AllowFPReassoc),
Expand All @@ -413,30 +406,26 @@ class FPOptions {
bool requiresTrailingStorage(const LangOptions &LO);

bool allowFPContractWithinStatement() const {
return fp_contract == LangOptions::FPC_On;
return fp_contract == LangOptions::FPM_On;
}

bool allowFPContractAcrossStatement() const {
return fp_contract == LangOptions::FPC_Fast;
return fp_contract == LangOptions::FPM_Fast;
}

void setAllowFPContractWithinStatement() {
fp_contract = LangOptions::FPC_On;
fp_contract = LangOptions::FPM_On;
}

void setAllowFPContractAcrossStatement() {
fp_contract = LangOptions::FPC_Fast;
fp_contract = LangOptions::FPM_Fast;
}

void setDisallowFPContract() { fp_contract = LangOptions::FPC_Off; }
void setDisallowFPContract() { fp_contract = LangOptions::FPM_Off; }

bool allowFEnvAccess() const {
return fenv_access == LangOptions::FEA_On;
}
bool allowFEnvAccess() const { return fenv_access == LangOptions::FPM_On; }

void setAllowFEnvAccess() {
fenv_access = LangOptions::FEA_On;
}
void setAllowFEnvAccess() { fenv_access = LangOptions::FPM_On; }

void setFPPreciseEnabled(bool Value) {
if (Value) {
Expand All @@ -450,7 +439,7 @@ class FPOptions {
}
}

void setDisallowFEnvAccess() { fenv_access = LangOptions::FEA_Off; }
void setDisallowFEnvAccess() { fenv_access = LangOptions::FPM_Off; }

RoundingMode getRoundingMode() const {
return static_cast<RoundingMode>(rounding);
Expand Down Expand Up @@ -500,8 +489,8 @@ class FPOptions {

/// Used with getAsOpaqueInt() to manage the float_control pragma stack.
void getFromOpaqueInt(unsigned I) {
fp_contract = (static_cast<LangOptions::FPContractModeKind>(I & 3));
fenv_access = (static_cast<LangOptions::FEnvAccessModeKind>((I >> 2) & 1));
fp_contract = (static_cast<LangOptions::FPModeKind>(I & 3));
fenv_access = ((I >> 2) & 1);
rounding = static_cast<unsigned>(static_cast<RoundingMode>((I >> 3) & 7));
exceptions = (static_cast<LangOptions::FPExceptionModeKind>((I >> 6) & 3));
allow_reassoc = ((I >> 8) & 1);
Expand All @@ -520,7 +509,8 @@ class FPOptions {
unsigned fenv_access : 1;
unsigned rounding : 3;
unsigned exceptions : 2;
/// Allow reassociation transformations for floating-point instructions.
/// Allow reassociation transformations for floating-point instructions
/// across multiple statements.
unsigned allow_reassoc : 1;
/// No NaNs - Allow optimizations to assume the arguments and result
/// are not NaN. If an argument is a nan, or the result would be a nan,
Expand Down
9 changes: 6 additions & 3 deletions clang/include/clang/Sema/Sema.h
Expand Up @@ -9617,12 +9617,15 @@ class Sema final {
/// ActOnPragmaFPContract - Called on well formed
/// \#pragma {STDC,OPENCL} FP_CONTRACT and
/// \#pragma clang fp contract
void ActOnPragmaFPContract(LangOptions::FPContractModeKind FPC);
void ActOnPragmaFPContract(LangOptions::FPModeKind FPC);

/// Called on well formed
/// \#pragma clang fp reassociate
void ActOnPragmaFPReassociate(bool IsEnabled);

/// ActOnPragmaFenvAccess - Called on well formed
/// \#pragma STDC FENV_ACCESS
void ActOnPragmaFEnvAccess(SourceLocation Loc,
LangOptions::FEnvAccessModeKind FPC);
void ActOnPragmaFEnvAccess(SourceLocation Loc, bool IsEnabled);

/// Called to set rounding mode for floating point operations.
void setRoundingMode(llvm::RoundingMode);
Expand Down
6 changes: 3 additions & 3 deletions clang/lib/CodeGen/BackendUtil.cpp
Expand Up @@ -450,15 +450,15 @@ static void initTargetOptions(llvm::TargetOptions &Options,

// Set FP fusion mode.
switch (LangOpts.getDefaultFPContractMode()) {
case LangOptions::FPC_Off:
case LangOptions::FPM_Off:
// Preserve any contraction performed by the front-end. (Strict performs
// splitting of the muladd intrinsic in the backend.)
Options.AllowFPOpFusion = llvm::FPOpFusion::Standard;
break;
case LangOptions::FPC_On:
case LangOptions::FPM_On:
Options.AllowFPOpFusion = llvm::FPOpFusion::Standard;
break;
case LangOptions::FPC_Fast:
case LangOptions::FPM_Fast:
Options.AllowFPOpFusion = llvm::FPOpFusion::Fast;
break;
}
Expand Down
10 changes: 5 additions & 5 deletions clang/lib/Frontend/CompilerInvocation.cpp
Expand Up @@ -2318,7 +2318,7 @@ void CompilerInvocation::setLangDefaults(LangOptions &Opts, InputKind IK,
Opts.AltiVec = 0;
Opts.ZVector = 0;
Opts.setLaxVectorConversions(LangOptions::LaxVectorConversionKind::None);
Opts.setDefaultFPContractMode(LangOptions::FPC_On);
Opts.setDefaultFPContractMode(LangOptions::FPM_On);
Opts.NativeHalfType = 1;
Opts.NativeHalfArgsAndReturns = 1;
Opts.OpenCLCPlusPlus = Opts.CPlusPlus;
Expand All @@ -2338,7 +2338,7 @@ void CompilerInvocation::setLangDefaults(LangOptions &Opts, InputKind IK,
Opts.CUDA = IK.getLanguage() == Language::CUDA || Opts.HIP;
if (Opts.CUDA)
// Set default FP_CONTRACT to FAST.
Opts.setDefaultFPContractMode(LangOptions::FPC_Fast);
Opts.setDefaultFPContractMode(LangOptions::FPM_Fast);

Opts.RenderScript = IK.getLanguage() == Language::RenderScript;
if (Opts.RenderScript) {
Expand Down Expand Up @@ -3200,11 +3200,11 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK,
if (Arg *A = Args.getLastArg(OPT_ffp_contract)) {
StringRef Val = A->getValue();
if (Val == "fast")
Opts.setDefaultFPContractMode(LangOptions::FPC_Fast);
Opts.setDefaultFPContractMode(LangOptions::FPM_Fast);
else if (Val == "on")
Opts.setDefaultFPContractMode(LangOptions::FPC_On);
Opts.setDefaultFPContractMode(LangOptions::FPM_On);
else if (Val == "off")
Opts.setDefaultFPContractMode(LangOptions::FPC_Off);
Opts.setDefaultFPContractMode(LangOptions::FPM_Off);
else
Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Val;
}
Expand Down
58 changes: 33 additions & 25 deletions clang/lib/Parse/ParsePragma.cpp
Expand Up @@ -640,13 +640,13 @@ void Parser::HandlePragmaFPContract() {
static_cast<tok::OnOffSwitch>(
reinterpret_cast<uintptr_t>(Tok.getAnnotationValue()));

LangOptions::FPContractModeKind FPC;
LangOptions::FPModeKind FPC;
switch (OOS) {
case tok::OOS_ON:
FPC = LangOptions::FPC_On;
FPC = LangOptions::FPM_On;
break;
case tok::OOS_OFF:
FPC = LangOptions::FPC_Off;
FPC = LangOptions::FPM_Off;
break;
case tok::OOS_DEFAULT:
FPC = getLangOpts().getDefaultFPContractMode();
Expand Down Expand Up @@ -679,21 +679,21 @@ void Parser::HandlePragmaFEnvAccess() {
static_cast<tok::OnOffSwitch>(
reinterpret_cast<uintptr_t>(Tok.getAnnotationValue()));

LangOptions::FEnvAccessModeKind FPC;
bool IsEnabled;
switch (OOS) {
case tok::OOS_ON:
FPC = LangOptions::FEA_On;
IsEnabled = true;
break;
case tok::OOS_OFF:
FPC = LangOptions::FEA_Off;
IsEnabled = false;
break;
case tok::OOS_DEFAULT: // FIXME: Add this cli option when it makes sense.
FPC = LangOptions::FEA_Off;
IsEnabled = false;
break;
}

SourceLocation PragmaLoc = ConsumeAnnotationToken();
Actions.ActOnPragmaFEnvAccess(PragmaLoc, FPC);
Actions.ActOnPragmaFEnvAccess(PragmaLoc, IsEnabled);
}


Expand Down Expand Up @@ -2825,7 +2825,7 @@ void PragmaOptimizeHandler::HandlePragma(Preprocessor &PP,
namespace {
/// Used as the annotation value for tok::annot_pragma_fp.
struct TokFPAnnotValue {
enum FlagKinds { Contract };
enum FlagKinds { Contract, Reassociate };
enum FlagValues { On, Off, Fast };

FlagKinds FlagKind;
Expand Down Expand Up @@ -2853,6 +2853,7 @@ void PragmaFPHandler::HandlePragma(Preprocessor &PP,
llvm::StringSwitch<llvm::Optional<TokFPAnnotValue::FlagKinds>>(
OptionInfo->getName())
.Case("contract", TokFPAnnotValue::Contract)
.Case("reassociate", TokFPAnnotValue::Reassociate)
.Default(None);
if (!FlagKind) {
PP.Diag(Tok.getLocation(), diag::err_pragma_fp_invalid_option)
Expand All @@ -2870,7 +2871,8 @@ 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();
<< PP.getSpelling(Tok) << OptionInfo->getName()
<< (FlagKind == TokFPAnnotValue::Reassociate);
return;
}
const IdentifierInfo *II = Tok.getIdentifierInfo();
Expand All @@ -2883,9 +2885,11 @@ void PragmaFPHandler::HandlePragma(Preprocessor &PP,
.Case("fast", TokFPAnnotValue::Fast)
.Default(llvm::None);

if (!FlagValue) {
if (!FlagValue || (FlagKind == TokFPAnnotValue::Reassociate &&
FlagValue == TokFPAnnotValue::Fast)) {
PP.Diag(Tok.getLocation(), diag::err_pragma_fp_invalid_argument)
<< PP.getSpelling(Tok) << OptionInfo->getName();
<< PP.getSpelling(Tok) << OptionInfo->getName()
<< (FlagKind == TokFPAnnotValue::Reassociate);
return;
}
PP.Lex(Tok);
Expand Down Expand Up @@ -2927,20 +2931,24 @@ void Parser::HandlePragmaFP() {
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->FlagKind == TokFPAnnotValue::Reassociate)
Actions.ActOnPragmaFPReassociate(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(FPC);
}

Actions.ActOnPragmaFPContract(FPC);
ConsumeAnnotationToken();
}

Expand Down

0 comments on commit c355bec

Please sign in to comment.