Skip to content

Commit bbf0d19

Browse files
committed
Currently the control of the eval-method is mixed with fast-math.
FLT_EVAL_METHOD tells the user the precision at which, temporary results are evaluated but when fast-math is enabled, the numeric values are not guaranteed to match the source semantics, so the eval-method is meaningless. For example, the expression `x + y + z` has as source semantics `(x + y) + z`. FLT_EVAL_METHOD is telling the user at which precision `(x + y)` is evaluated. With fast-math enable the compiler can choose to evaluate the expression as `(y + z) + x`. The correct behavior is to set the FLT_EVAL_METHOD to `-1` to tell the user that the precision of the intermediate values is unknow. This patch is doing that. Differential Revision: https://reviews.llvm.org/D121122
1 parent 27e1931 commit bbf0d19

File tree

5 files changed

+182
-8
lines changed

5 files changed

+182
-8
lines changed

clang/include/clang/Lex/Preprocessor.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,11 @@ class Preprocessor {
192192
LangOptions::FPEvalMethodKind CurrentFPEvalMethod =
193193
LangOptions::FPEvalMethodKind::FEM_UnsetOnCommandLine;
194194

195+
// Keeps the value of the last evaluation method before a
196+
// `pragma float_control (precise,off) is applied.
197+
LangOptions::FPEvalMethodKind LastFPEvalMethod =
198+
LangOptions::FPEvalMethodKind::FEM_UnsetOnCommandLine;
199+
195200
// The most recent pragma location where the floating point evaluation
196201
// method was modified. This is used to determine whether the
197202
// 'pragma clang fp eval_method' was used whithin the current scope.
@@ -2078,6 +2083,14 @@ class Preprocessor {
20782083
return LastFPEvalPragmaLocation;
20792084
}
20802085

2086+
LangOptions::FPEvalMethodKind getLastFPEvalMethod() const {
2087+
return LastFPEvalMethod;
2088+
}
2089+
2090+
void setLastFPEvalMethod(LangOptions::FPEvalMethodKind Val) {
2091+
LastFPEvalMethod = Val;
2092+
}
2093+
20812094
void setCurrentFPEvalMethod(SourceLocation PragmaLoc,
20822095
LangOptions::FPEvalMethodKind Val) {
20832096
assert(Val != LangOptions::FEM_UnsetOnCommandLine &&

clang/lib/Lex/PPMacroExpansion.cpp

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1577,14 +1577,35 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) {
15771577
Tok.setKind(tok::string_literal);
15781578
} else if (II == Ident__FLT_EVAL_METHOD__) {
15791579
// __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);
1580+
if (getTUFPEvalMethod() ==
1581+
LangOptions::FPEvalMethodKind::FEM_Indeterminable) {
1582+
// This is possible if `AllowFPReassoc` or `AllowReciprocal` is enabled.
1583+
// These modes can be triggered via the command line option `-ffast-math`
1584+
// or via a `pragam float_control`.
1585+
// __FLT_EVAL_METHOD__ expands to -1.
1586+
// The `minus` operator is the next token we read from the stream.
1587+
auto Toks = std::make_unique<Token[]>(1);
1588+
OS << "-";
1589+
Tok.setKind(tok::minus);
1590+
// Push the token `1` to the stream.
1591+
Token NumberToken;
1592+
NumberToken.startToken();
1593+
NumberToken.setKind(tok::numeric_constant);
1594+
NumberToken.setLiteralData("1");
1595+
NumberToken.setLength(1);
1596+
Toks[0] = NumberToken;
1597+
EnterTokenStream(std::move(Toks), 1, /*DisableMacroExpansion*/ false,
1598+
/*IsReinject*/ false);
1599+
} else {
1600+
OS << getTUFPEvalMethod();
1601+
// __FLT_EVAL_METHOD__ expands to a simple numeric value.
1602+
Tok.setKind(tok::numeric_constant);
1603+
if (getLastFPEvalPragmaLocation().isValid()) {
1604+
// The program is ill-formed. The value of __FLT_EVAL_METHOD__ is
1605+
// altered by the pragma.
1606+
Diag(Tok, diag::err_illegal_use_of_flt_eval_macro);
1607+
Diag(getLastFPEvalPragmaLocation(), diag::note_pragma_entered_here);
1608+
}
15881609
}
15891610
} else if (II == Ident__COUNTER__) {
15901611
// __COUNTER__ expands to a simple numeric value.

clang/lib/Sema/Sema.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,12 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
257257
PP.setCurrentFPEvalMethod(SourceLocation(),
258258
getLangOpts().getFPEvalMethod());
259259
CurFPFeatures.setFPEvalMethod(PP.getCurrentFPEvalMethod());
260+
// When `-ffast-math` option is enabled, it triggers several driver math
261+
// options to be enabled. Among those, only one the following two modes
262+
// affect the eval-method: reciprocal or reassociate.
263+
if (getLangOpts().AllowFPReassoc || getLangOpts().AllowRecip)
264+
PP.setCurrentFPEvalMethod(SourceLocation(),
265+
LangOptions::FEM_Indeterminable);
260266
}
261267

262268
// Anchor Sema's type info to this TU.

clang/lib/Sema/SemaAttr.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,13 @@ void Sema::ActOnPragmaFloatControl(SourceLocation Loc,
508508
case PFC_Precise:
509509
NewFPFeatures.setFPPreciseEnabled(true);
510510
FpPragmaStack.Act(Loc, Action, StringRef(), NewFPFeatures);
511+
if (PP.getCurrentFPEvalMethod() ==
512+
LangOptions::FPEvalMethodKind::FEM_Indeterminable &&
513+
PP.getLastFPEvalPragmaLocation().isValid())
514+
// A preceding `pragma float_control(precise,off)` has changed
515+
// the value of the evaluation method.
516+
// Set it back to its old value.
517+
PP.setCurrentFPEvalMethod(SourceLocation(), PP.getLastFPEvalMethod());
511518
break;
512519
case PFC_NoPrecise:
513520
if (CurFPFeatures.getFPExceptionMode() == LangOptions::FPE_Strict)
@@ -517,6 +524,10 @@ void Sema::ActOnPragmaFloatControl(SourceLocation Loc,
517524
else
518525
NewFPFeatures.setFPPreciseEnabled(false);
519526
FpPragmaStack.Act(Loc, Action, StringRef(), NewFPFeatures);
527+
PP.setLastFPEvalMethod(PP.getCurrentFPEvalMethod());
528+
// `AllowFPReassoc` or `AllowReciprocal` option is enabled.
529+
PP.setCurrentFPEvalMethod(
530+
Loc, LangOptions::FPEvalMethodKind::FEM_Indeterminable);
520531
break;
521532
case PFC_Except:
522533
if (!isPreciseFPEnabled())
@@ -540,6 +551,12 @@ void Sema::ActOnPragmaFloatControl(SourceLocation Loc,
540551
}
541552
FpPragmaStack.Act(Loc, Action, StringRef(), NewFPFeatures);
542553
NewFPFeatures = FpPragmaStack.CurrentValue;
554+
if (CurFPFeatures.getAllowFPReassociate() ||
555+
CurFPFeatures.getAllowReciprocal())
556+
// Since we are popping the pragma, we don't want to be passing
557+
// a location here.
558+
PP.setCurrentFPEvalMethod(SourceLocation(),
559+
CurFPFeatures.getFPEvalMethod());
543560
break;
544561
}
545562
CurFPFeatures = NewFPFeatures.applyOverrides(getLangOpts());
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
// RUN: %clang_cc1 -fexperimental-strict-floating-point \
2+
// RUN: -triple x86_64-linux-gnu -emit-llvm -o - %s \
3+
// RUN: | FileCheck %s -check-prefixes=CHECK
4+
5+
// RUN: %clang_cc1 -triple i386--linux -emit-llvm -o - %s \
6+
// RUN: | FileCheck %s -check-prefixes=CHECK-EXT
7+
8+
// RUN: %clang_cc1 -fexperimental-strict-floating-point \
9+
// RUN: -mreassociate -freciprocal-math -ffp-contract=fast \
10+
// RUN: -ffast-math -triple x86_64-linux-gnu \
11+
// RUN: -emit-llvm -o - %s \
12+
// RUN: | FileCheck %s -check-prefixes=CHECK-FAST
13+
14+
// RUN: %clang_cc1 -triple i386--linux -mreassociate -freciprocal-math \
15+
// RUN: -ffp-contract=fast -ffast-math -emit-llvm -o - %s \
16+
// RUN: | FileCheck %s -check-prefixes=CHECK-FAST
17+
18+
float a = 1.0f, b = 2.0f, c = 3.0f;
19+
#pragma float_control(precise, off)
20+
float res2 = a + b + c;
21+
int val3 = __FLT_EVAL_METHOD__;
22+
#pragma float_control(precise, on)
23+
float res3 = a + b + c;
24+
int val4 = __FLT_EVAL_METHOD__;
25+
26+
// CHECK: @val3 = global i32 -1
27+
// CHECK: @val4 = global i32 0
28+
29+
// CHECK-EXT: @val3 = global i32 -1
30+
// CHECK-EXT: @val4 = global i32 2
31+
32+
// CHECK-FAST: @val3 = global i32 -1
33+
// CHECK-FAST: @val4 = global i32 -1
34+
35+
float res;
36+
int add(float a, float b, float c) {
37+
// CHECK: fadd float
38+
// CHECK: load float, float*
39+
// CHECK: fadd float
40+
// CHECK: store float
41+
// CHECK: ret i32 0
42+
res = a + b + c;
43+
return __FLT_EVAL_METHOD__;
44+
}
45+
46+
int add_precise(float a, float b, float c) {
47+
#pragma float_control(precise, on)
48+
// CHECK: fadd float
49+
// CHECK: load float, float*
50+
// CHECK: fadd float
51+
// CHECK: store float
52+
// CHECK: ret i32 0
53+
res = a + b + c;
54+
return __FLT_EVAL_METHOD__;
55+
}
56+
57+
#pragma float_control(push)
58+
#pragma float_control(precise, on)
59+
int add_precise_1(float a, float b, float c) {
60+
// CHECK: fadd float
61+
// CHECK: load float, float*
62+
// CHECK: fadd float
63+
// CHECK: store float
64+
// CHECK: ret i32 0
65+
res = a + b + c;
66+
return __FLT_EVAL_METHOD__;
67+
}
68+
#pragma float_control(pop)
69+
70+
int add_not_precise(float a, float b, float c) {
71+
// Fast-math is enabled with this pragma.
72+
#pragma float_control(precise, off)
73+
// CHECK: fadd fast float
74+
// CHECK: load float, float*
75+
// CHECK: fadd fast float
76+
// CHECK: float {{.*}}, float*
77+
// CHECK: ret i32 -1
78+
res = a + b + c;
79+
return __FLT_EVAL_METHOD__;
80+
}
81+
82+
#pragma float_control(push)
83+
// Fast-math is enabled with this pragma.
84+
#pragma float_control(precise, off)
85+
int add_not_precise_1(float a, float b, float c) {
86+
// CHECK: fadd fast float
87+
// CHECK: load float, float*
88+
// CHECK: fadd fast float
89+
// CHECK: float {{.*}}, float*
90+
// CHECK: ret i32 -1
91+
res = a + b + c;
92+
return __FLT_EVAL_METHOD__;
93+
}
94+
#pragma float_control(pop)
95+
96+
int getFPEvalMethod() {
97+
// CHECK: ret i32 0
98+
return __FLT_EVAL_METHOD__;
99+
}
100+
101+
float res1;
102+
int whatever(float a, float b, float c) {
103+
#pragma float_control(precise, off)
104+
// CHECK: load float, float*
105+
// CHECK: fadd fast float
106+
// CHECK: store float {{.*}}, float*
107+
// CHECK: store i32 -1
108+
// CHECK: store i32 0
109+
// CHECK: ret i32 -1
110+
res1 = a + b + c;
111+
int val1 = __FLT_EVAL_METHOD__;
112+
{
113+
#pragma float_control(precise, on)
114+
int val2 = __FLT_EVAL_METHOD__;
115+
}
116+
return __FLT_EVAL_METHOD__;
117+
}

0 commit comments

Comments
 (0)