Skip to content

Commit 32b73bc

Browse files
committed
Add support for floating-point option ffp-eval-method and for
`pragma clang fp eval_method`. https://reviews.llvm.org/D109239
1 parent 7adb858 commit 32b73bc

40 files changed

+872
-72
lines changed

clang/docs/LanguageExtensions.rst

+32
Original file line numberDiff line numberDiff line change
@@ -3907,6 +3907,38 @@ A ``#pragma clang fp`` pragma may contain any number of options:
39073907
...
39083908
}
39093909
3910+
``#pragma clang fp eval_method`` allows floating-point behavior to be specified
3911+
for a section of the source code. This pragma can appear at file or namespace
3912+
scope, or at the start of a compound statement (excluding comments).
3913+
The pragma is active within the scope of the compound statement.
3914+
3915+
When ``pragma clang fp eval_method(source)`` is enabled, the section of code
3916+
governed by the pragma behaves as though the command-line option
3917+
``-ffp-eval-method=source`` is enabled. Rounds intermediate results to
3918+
source-defined precision.
3919+
3920+
When ``pragma clang fp eval_method(double)`` is enabled, the section of code
3921+
governed by the pragma behaves as though the command-line option
3922+
``-ffp-eval-method=double`` is enabled. Rounds intermediate results to
3923+
``double`` precision.
3924+
3925+
When ``pragma clang fp eval_method(extended)`` is enabled, the section of code
3926+
governed by the pragma behaves as though the command-line option
3927+
``-ffp-eval-method=extended`` is enabled. Rounds intermediate results to
3928+
target-dependent ``long double`` precision. In Win32 programming, for instance,
3929+
the long double data type maps to the double, 64-bit precision data type.
3930+
3931+
The full syntax this pragma supports is
3932+
``#pragma clang fp eval_method(source|double|extended)``.
3933+
3934+
.. code-block:: c++
3935+
3936+
for(...) {
3937+
// The compiler will use long double as the floating-point evaluation
3938+
// method.
3939+
#pragma clang fp eval_method(extended)
3940+
a = b[i] * c[i] + e;
3941+
}
39103942
39113943
The ``#pragma float_control`` pragma allows precise floating-point
39123944
semantics and floating-point exception behavior to be specified

clang/docs/UsersManual.rst

+27
Original file line numberDiff line numberDiff line change
@@ -1566,6 +1566,22 @@ Note that floating-point operations performed as part of constant initialization
15661566
* ``maytrap`` The compiler avoids transformations that may raise exceptions that would not have been raised by the original code. Constant folding performed by the compiler is exempt from this option.
15671567
* ``strict`` The compiler ensures that all transformations strictly preserve the floating point exception semantics of the original code.
15681568

1569+
.. option:: -ffp-eval-method=<value>
1570+
1571+
Specify the floating-point evaluation method for intermediate results within
1572+
a single expression of the code.
1573+
1574+
Valid values are: ``source``, ``double``, and ``extended``.
1575+
For 64-bit targets, the default value is ``source``. For 32-bit x86 targets
1576+
however, in the case of NETBSD 6.99.26 and under, the default value is
1577+
``double``; in the case of NETBSD greater than 6.99.26, with NoSSE, the
1578+
default value is ``extended``, with SSE the default value is ``source``.
1579+
Details:
1580+
1581+
* ``source`` The compiler uses the floating-point type declared in the source program as the evaluation method.
1582+
* ``double`` The compiler uses ``double`` as the floating-point evaluation method for all float expressions of type that is narrower than ``double``.
1583+
* ``extended`` The compiler uses ``long double`` as the floating-point evaluation method for all float expressions of type that is narrower than ``long double``.
1584+
15691585
.. option:: -f[no-]protect-parens:
15701586

15711587
This option pertains to floating-point types, complex types with
@@ -1587,6 +1603,17 @@ Note that floating-point operations performed as part of constant initialization
15871603
has no effect because the optimizer is prohibited from making unsafe
15881604
transformations.
15891605

1606+
.. _FLT_EVAL_METHOD:
1607+
1608+
A note about ``__FLT_EVAL_METHOD__``
1609+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1610+
The macro ``__FLT_EVAL_METHOD__`` will expand to either the value set from the
1611+
command line option ``ffp-eval-method`` or to the value from the target info
1612+
setting. The ``__FLT_EVAL_METHOD__`` macro cannot expand to the correct
1613+
evaluation method in the presence of a ``#pragma`` which alters the evaluation
1614+
method. An error is issued if ``__FLT_EVAL_METHOD__`` is expanded inside a scope
1615+
modified by ``#pragma clang fp eval_method``.
1616+
15901617
.. _fp-constant-eval:
15911618

15921619
A note about Floating Point Constant Evaluation

clang/include/clang/Basic/DiagnosticLexKinds.td

+4
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,10 @@ def err_pragma_include_instead_system_reserved : Error<
321321
"header '%0' is an implementation detail; #include %select{'%2'|either '%2' "
322322
"or '%3'|one of %2}1 instead">;
323323

324+
def err_illegal_use_of_flt_eval_macro : Error<
325+
"'__FLT_EVAL_METHOD__' cannot be expanded inside a scope containing "
326+
"'#pragma clang fp eval_method'">;
327+
324328
def pp_poisoning_existing_macro : Warning<"poisoning existing macro">;
325329
def pp_out_of_date_dependency : Warning<
326330
"current file is older than dependency %0">;

clang/include/clang/Basic/DiagnosticParseKinds.td

+3
Original file line numberDiff line numberDiff line change
@@ -1267,6 +1267,9 @@ def err_pragma_attribute_namespace_on_attribute : Error<
12671267
def note_pragma_attribute_namespace_on_attribute : Note<
12681268
"omit the namespace to add attributes to the most-recently"
12691269
" pushed attribute group">;
1270+
def warn_no_support_for_eval_method_source_on_m32 : Warning<
1271+
"Setting the floating point evaluation method to `source` on a target"
1272+
" without SSE is not supported.">, InGroup<Pragmas>;
12701273

12711274
// OpenCL EXTENSION pragma (OpenCL 1.1 [9.1])
12721275
def warn_pragma_expected_colon : Warning<

clang/include/clang/Basic/FPOptions.def

+1
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,5 @@ OPTION(NoHonorInfs, bool, 1, NoHonorNaNs)
2323
OPTION(NoSignedZero, bool, 1, NoHonorInfs)
2424
OPTION(AllowReciprocal, bool, 1, NoSignedZero)
2525
OPTION(AllowApproxFunc, bool, 1, AllowReciprocal)
26+
OPTION(FPEvalMethod, LangOptions::FPEvalMethodKind, 2, AllowApproxFunc)
2627
#undef OPTION

clang/include/clang/Basic/LangOptions.def

+1
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,7 @@ BENIGN_ENUM_LANGOPT(DefaultFPContractMode, FPModeKind, 2, FPM_Off, "FP contracti
301301
COMPATIBLE_LANGOPT(ExpStrictFP, 1, false, "Enable experimental strict floating point")
302302
BENIGN_ENUM_LANGOPT(FPRoundingMode, RoundingMode, 3, RoundingMode::NearestTiesToEven, "FP Rounding Mode type")
303303
BENIGN_ENUM_LANGOPT(FPExceptionMode, FPExceptionModeKind, 2, FPE_Ignore, "FP Exception Behavior Mode type")
304+
BENIGN_ENUM_LANGOPT(FPEvalMethod, FPEvalMethodKind, 2, FEM_UnsetOnCommandLine, "FP type used for floating point arithmetic")
304305
LANGOPT(NoBitFieldTypeAlign , 1, 0, "bit-field type alignment")
305306
LANGOPT(HexagonQdsp6Compat , 1, 0, "hexagon-qdsp6 backward compatibility")
306307
LANGOPT(ObjCAutoRefCount , 1, 0, "Objective-C automated reference counting")

clang/include/clang/Basic/LangOptions.h

+18
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,24 @@ class LangOptions : public LangOptionsBase {
235235
FPE_Strict
236236
};
237237

238+
/// Possible float expression evaluation method choices.
239+
enum FPEvalMethodKind {
240+
/// The evaluation method cannot be determined or is inconsistent for this
241+
/// target.
242+
FEM_Indeterminable = -1,
243+
/// Use the declared type for fp arithmetic.
244+
FEM_Source = 0,
245+
/// Use the type double for fp arithmetic.
246+
FEM_Double = 1,
247+
/// Use extended type for fp arithmetic.
248+
FEM_Extended = 2,
249+
/// Used only for FE option processing; this is only used to indicate that
250+
/// the user did not specify an explicit evaluation method on the command
251+
/// line and so the target should be queried for its default evaluation
252+
/// method instead.
253+
FEM_UnsetOnCommandLine = 3
254+
};
255+
238256
/// Possible exception handling behavior.
239257
enum class ExceptionHandlingKind { None, SjLj, WinEH, DwarfCFI, Wasm };
240258

clang/include/clang/Basic/TargetInfo.h

+5-1
Original file line numberDiff line numberDiff line change
@@ -726,7 +726,11 @@ class TargetInfo : public virtual TransferrableTargetInfo,
726726
}
727727

728728
/// Return the value for the C99 FLT_EVAL_METHOD macro.
729-
virtual unsigned getFloatEvalMethod() const { return 0; }
729+
virtual LangOptions::FPEvalMethodKind getFPEvalMethod() const {
730+
return LangOptions::FPEvalMethodKind::FEM_Source;
731+
}
732+
733+
virtual bool supportSourceEvalMethod() const { return true; }
730734

731735
// getLargeArrayMinWidth/Align - Return the minimum array size that is
732736
// 'large' and its alignment.

clang/include/clang/Driver/Options.td

+5
Original file line numberDiff line numberDiff line change
@@ -1495,6 +1495,11 @@ def : Flag<["-"], "fextended-identifiers">, Group<clang_ignored_f_Group>;
14951495
def : Flag<["-"], "fno-extended-identifiers">, Group<f_Group>, Flags<[Unsupported]>;
14961496
def fhosted : Flag<["-"], "fhosted">, Group<f_Group>;
14971497
def fdenormal_fp_math_EQ : Joined<["-"], "fdenormal-fp-math=">, Group<f_Group>, Flags<[CC1Option]>;
1498+
def ffp_eval_method_EQ : Joined<["-"], "ffp-eval-method=">, Group<f_Group>, Flags<[CC1Option]>,
1499+
HelpText<"Specifies the evaluation method to use for floating-point arithmetic.">,
1500+
Values<"source,double,extended">, NormalizedValuesScope<"LangOptions">,
1501+
NormalizedValues<["FEM_Source", "FEM_Double", "FEM_Extended"]>,
1502+
MarshallingInfoEnum<LangOpts<"FPEvalMethod">, "FEM_UnsetOnCommandLine">;
14981503
def ffp_model_EQ : Joined<["-"], "ffp-model=">, Group<f_Group>, Flags<[NoXarchOption]>,
14991504
HelpText<"Controls the semantics of floating-point calculations.">;
15001505
def ffp_exception_behavior_EQ : Joined<["-"], "ffp-exception-behavior=">, Group<f_Group>, Flags<[CC1Option]>,

clang/include/clang/Lex/Preprocessor.h

+41
Original file line numberDiff line numberDiff line change
@@ -179,12 +179,27 @@ class Preprocessor {
179179
IdentifierInfo *Ident__is_target_vendor; // __is_target_vendor
180180
IdentifierInfo *Ident__is_target_os; // __is_target_os
181181
IdentifierInfo *Ident__is_target_environment; // __is_target_environment
182+
IdentifierInfo *Ident__FLT_EVAL_METHOD__; // __FLT_EVAL_METHOD
182183

183184
// Weak, only valid (and set) while InMacroArgs is true.
184185
Token* ArgMacro;
185186

186187
SourceLocation DATELoc, TIMELoc;
187188

189+
// FEM_UnsetOnCommandLine means that an explicit evaluation method was
190+
// not specified on the command line. The target is queried to set the
191+
// default evaluation method.
192+
LangOptions::FPEvalMethodKind CurrentFPEvalMethod =
193+
LangOptions::FPEvalMethodKind::FEM_UnsetOnCommandLine;
194+
195+
// The most recent pragma location where the floating point evaluation
196+
// method was modified. This is used to determine whether the
197+
// 'pragma clang fp eval_method' was used whithin the current scope.
198+
SourceLocation LastFPEvalPragmaLocation;
199+
200+
LangOptions::FPEvalMethodKind TUFPEvalMethod =
201+
LangOptions::FPEvalMethodKind::FEM_UnsetOnCommandLine;
202+
188203
// Next __COUNTER__ value, starts at 0.
189204
unsigned CounterValue = 0;
190205

@@ -2048,6 +2063,32 @@ class Preprocessor {
20482063
unsigned getCounterValue() const { return CounterValue; }
20492064
void setCounterValue(unsigned V) { CounterValue = V; }
20502065

2066+
LangOptions::FPEvalMethodKind getCurrentFPEvalMethod() const {
2067+
assert(CurrentFPEvalMethod != LangOptions::FEM_UnsetOnCommandLine &&
2068+
"FPEvalMethod should be set either from command line or from the "
2069+
"target info");
2070+
return CurrentFPEvalMethod;
2071+
}
2072+
2073+
LangOptions::FPEvalMethodKind getTUFPEvalMethod() const {
2074+
return TUFPEvalMethod;
2075+
}
2076+
2077+
SourceLocation getLastFPEvalPragmaLocation() const {
2078+
return LastFPEvalPragmaLocation;
2079+
}
2080+
2081+
void setCurrentFPEvalMethod(SourceLocation PragmaLoc,
2082+
LangOptions::FPEvalMethodKind Val) {
2083+
assert(Val != LangOptions::FEM_UnsetOnCommandLine &&
2084+
"FPEvalMethod should never be set to FEM_UnsetOnCommandLine");
2085+
// This is the location of the '#pragma float_control" where the
2086+
// execution state is modifed.
2087+
LastFPEvalPragmaLocation = PragmaLoc;
2088+
CurrentFPEvalMethod = Val;
2089+
TUFPEvalMethod = Val;
2090+
}
2091+
20512092
/// Retrieves the module that we're currently building, if any.
20522093
Module *getCurrentModule();
20532094

clang/include/clang/Parse/Parser.h

+1
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ class Parser : public CodeCompletionHandler {
184184
std::unique_ptr<PragmaHandler> PCSectionHandler;
185185
std::unique_ptr<PragmaHandler> MSCommentHandler;
186186
std::unique_ptr<PragmaHandler> MSDetectMismatchHandler;
187+
std::unique_ptr<PragmaHandler> FPEvalMethodHandler;
187188
std::unique_ptr<PragmaHandler> FloatControlHandler;
188189
std::unique_ptr<PragmaHandler> MSPointersToMembers;
189190
std::unique_ptr<PragmaHandler> MSVtorDisp;

clang/include/clang/Sema/Sema.h

+7-7
Original file line numberDiff line numberDiff line change
@@ -1541,19 +1541,16 @@ class Sema final {
15411541
/// statements.
15421542
class FPFeaturesStateRAII {
15431543
public:
1544-
FPFeaturesStateRAII(Sema &S) : S(S), OldFPFeaturesState(S.CurFPFeatures) {
1545-
OldOverrides = S.FpPragmaStack.CurrentValue;
1546-
}
1547-
~FPFeaturesStateRAII() {
1548-
S.CurFPFeatures = OldFPFeaturesState;
1549-
S.FpPragmaStack.CurrentValue = OldOverrides;
1550-
}
1544+
FPFeaturesStateRAII(Sema &S);
1545+
~FPFeaturesStateRAII();
15511546
FPOptionsOverride getOverrides() { return OldOverrides; }
15521547

15531548
private:
15541549
Sema& S;
15551550
FPOptions OldFPFeaturesState;
15561551
FPOptionsOverride OldOverrides;
1552+
LangOptions::FPEvalMethodKind OldEvalMethod;
1553+
SourceLocation OldFPPragmaLocation;
15571554
};
15581555

15591556
void addImplicitTypedef(StringRef Name, QualType T);
@@ -10131,6 +10128,9 @@ class Sema final {
1013110128
!CurFPFeatures.getAllowApproxFunc();
1013210129
}
1013310130

10131+
void ActOnPragmaFPEvalMethod(SourceLocation Loc,
10132+
LangOptions::FPEvalMethodKind Value);
10133+
1013410134
/// ActOnPragmaFloatControl - Call on well-formed \#pragma float_control
1013510135
void ActOnPragmaFloatControl(SourceLocation Loc, PragmaMsStackAction Action,
1013610136
PragmaFloatControlKind Value);

clang/lib/Basic/Targets/OSTargets.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -749,7 +749,9 @@ class AIXTargetInfo : public OSTargetInfo<Target> {
749749
}
750750

751751
// AIX sets FLT_EVAL_METHOD to be 1.
752-
unsigned getFloatEvalMethod() const override { return 1; }
752+
LangOptions::FPEvalMethodKind getFPEvalMethod() const override {
753+
return LangOptions::FPEvalMethodKind::FEM_Double;
754+
}
753755

754756
bool defaultsToAIXPowerAlignment() const override { return true; }
755757
};

clang/lib/Basic/Targets/X86.h

+9-5
Original file line numberDiff line numberDiff line change
@@ -168,11 +168,15 @@ class LLVM_LIBRARY_VISIBILITY X86TargetInfo : public TargetInfo {
168168
return LongDoubleFormat == &llvm::APFloat::IEEEquad() ? "g" : "e";
169169
}
170170

171-
unsigned getFloatEvalMethod() const override {
171+
LangOptions::FPEvalMethodKind getFPEvalMethod() const override {
172172
// X87 evaluates with 80 bits "long double" precision.
173-
return SSELevel == NoSSE ? 2 : 0;
173+
return SSELevel == NoSSE ? LangOptions::FPEvalMethodKind::FEM_Extended
174+
: LangOptions::FPEvalMethodKind::FEM_Source;
174175
}
175176

177+
// EvalMethod `source` is not supported for targets with `NoSSE` feature.
178+
bool supportSourceEvalMethod() const override { return SSELevel > NoSSE; }
179+
176180
ArrayRef<const char *> getGCCRegNames() const override;
177181

178182
ArrayRef<TargetInfo::GCCRegAlias> getGCCRegAliases() const override {
@@ -471,13 +475,13 @@ class LLVM_LIBRARY_VISIBILITY NetBSDI386TargetInfo
471475
NetBSDI386TargetInfo(const llvm::Triple &Triple, const TargetOptions &Opts)
472476
: NetBSDTargetInfo<X86_32TargetInfo>(Triple, Opts) {}
473477

474-
unsigned getFloatEvalMethod() const override {
478+
LangOptions::FPEvalMethodKind getFPEvalMethod() const override {
475479
VersionTuple OsVersion = getTriple().getOSVersion();
476480
// New NetBSD uses the default rounding mode.
477481
if (OsVersion >= VersionTuple(6, 99, 26) || OsVersion.getMajor() == 0)
478-
return X86_32TargetInfo::getFloatEvalMethod();
482+
return X86_32TargetInfo::getFPEvalMethod();
479483
// NetBSD before 6.99.26 defaults to "double" rounding.
480-
return 1;
484+
return LangOptions::FPEvalMethodKind::FEM_Double;
481485
}
482486
};
483487

clang/lib/Driver/ToolChains/Clang.cpp

+17
Original file line numberDiff line numberDiff line change
@@ -2726,6 +2726,8 @@ static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D,
27262726
StringRef FPModel = "";
27272727
// -ffp-exception-behavior options: strict, maytrap, ignore
27282728
StringRef FPExceptionBehavior = "";
2729+
// -ffp-eval-method options: double, extended, source
2730+
StringRef FPEvalMethod = "";
27292731
const llvm::DenormalMode DefaultDenormalFPMath =
27302732
TC.getDefaultDenormalModeForType(Args, JA);
27312733
const llvm::DenormalMode DefaultDenormalFP32Math =
@@ -2921,6 +2923,18 @@ static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D,
29212923
break;
29222924
}
29232925

2926+
// Validate and pass through -ffp-eval-method option.
2927+
case options::OPT_ffp_eval_method_EQ: {
2928+
StringRef Val = A->getValue();
2929+
if (Val.equals("double") || Val.equals("extended") ||
2930+
Val.equals("source"))
2931+
FPEvalMethod = Val;
2932+
else
2933+
D.Diag(diag::err_drv_unsupported_option_argument)
2934+
<< A->getOption().getName() << Val;
2935+
break;
2936+
}
2937+
29242938
case options::OPT_ffinite_math_only:
29252939
HonorINFs = false;
29262940
HonorNaNs = false;
@@ -3076,6 +3090,9 @@ static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D,
30763090
CmdArgs.push_back(Args.MakeArgString("-ffp-exception-behavior=" +
30773091
FPExceptionBehavior));
30783092

3093+
if (!FPEvalMethod.empty())
3094+
CmdArgs.push_back(Args.MakeArgString("-ffp-eval-method=" + FPEvalMethod));
3095+
30793096
ParseMRecip(D, Args, CmdArgs);
30803097

30813098
// -ffast-math enables the __FAST_MATH__ preprocessor macro, but check for the

clang/lib/Frontend/InitPreprocessor.cpp

-1
Original file line numberDiff line numberDiff line change
@@ -1136,7 +1136,6 @@ static void InitializePredefinedMacros(const TargetInfo &TI,
11361136
}
11371137

11381138
// Macros to control C99 numerics and <float.h>
1139-
Builder.defineMacro("__FLT_EVAL_METHOD__", Twine(TI.getFloatEvalMethod()));
11401139
Builder.defineMacro("__FLT_RADIX__", "2");
11411140
Builder.defineMacro("__DECIMAL_DIG__", "__LDBL_DECIMAL_DIG__");
11421141

clang/lib/Lex/PPMacroExpansion.cpp

+12
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,7 @@ void Preprocessor::RegisterBuiltinMacros() {
342342
Ident__TIME__ = RegisterBuiltinMacro(*this, "__TIME__");
343343
Ident__COUNTER__ = RegisterBuiltinMacro(*this, "__COUNTER__");
344344
Ident_Pragma = RegisterBuiltinMacro(*this, "_Pragma");
345+
Ident__FLT_EVAL_METHOD__ = RegisterBuiltinMacro(*this, "__FLT_EVAL_METHOD__");
345346

346347
// C++ Standing Document Extensions.
347348
if (getLangOpts().CPlusPlus)
@@ -1574,6 +1575,17 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) {
15741575
// Surround the string with " and strip the trailing newline.
15751576
OS << '"' << StringRef(Result).drop_back() << '"';
15761577
Tok.setKind(tok::string_literal);
1578+
} else if (II == Ident__FLT_EVAL_METHOD__) {
1579+
// __FLT_EVAL_METHOD__ is set to the default value.
1580+
OS << getTUFPEvalMethod();
1581+
// __FLT_EVAL_METHOD__ expands to a simple numeric value.
1582+
Tok.setKind(tok::numeric_constant);
1583+
if (getLastFPEvalPragmaLocation().isValid()) {
1584+
// The program is ill-formed. The value of __FLT_EVAL_METHOD__ is altered
1585+
// by the pragma.
1586+
Diag(Tok, diag::err_illegal_use_of_flt_eval_macro);
1587+
Diag(getLastFPEvalPragmaLocation(), diag::note_pragma_entered_here);
1588+
}
15771589
} else if (II == Ident__COUNTER__) {
15781590
// __COUNTER__ expands to a simple numeric value.
15791591
OS << CounterValue++;

0 commit comments

Comments
 (0)