[clang] 6314f41 - [FPEnv] Evaluate constant expressions under non-default rounding modes
Serge Pavlov via cfe-commits
cfe-commits at lists.llvm.org
Sat Sep 26 04:57:07 PDT 2020
Author: Serge Pavlov
Date: 2020-09-26T17:59:39+07:00
New Revision: 6314f412a83d61e293e3696d4afe1797a4001ac4
URL: https://github.com/llvm/llvm-project/commit/6314f412a83d61e293e3696d4afe1797a4001ac4
DIFF: https://github.com/llvm/llvm-project/commit/6314f412a83d61e293e3696d4afe1797a4001ac4.diff
LOG: [FPEnv] Evaluate constant expressions under non-default rounding modes
The change implements evaluation of constant floating point expressions
under non-default rounding modes. The main objective was to support
evaluation of global variable initializers, where constant rounding mode
may be specified by `#pragma STDC FENV_ROUND`.
Differential Revision: https://reviews.llvm.org/D87822
Added:
clang/test/AST/const-fpfeatures-diag.c
clang/test/AST/const-fpfeatures.c
clang/test/AST/const-fpfeatures.cpp
Modified:
clang/include/clang/AST/Expr.h
clang/include/clang/Basic/DiagnosticASTKinds.td
clang/lib/AST/Expr.cpp
clang/lib/AST/ExprConstant.cpp
clang/lib/AST/TextNodeDumper.cpp
clang/lib/Sema/SemaAttr.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h
index 1ea454514b2f..c14143c3f336 100644
--- a/clang/include/clang/AST/Expr.h
+++ b/clang/include/clang/AST/Expr.h
@@ -413,6 +413,10 @@ class Expr : public ValueStmt {
return ClassifyImpl(Ctx, &Loc);
}
+ /// Returns the set of floating point options that apply to this expression.
+ /// Only meaningful for operations on floating point values.
+ FPOptions getFPFeaturesInEffect(const LangOptions &LO) const;
+
/// getValueKindForType - Given a formal return or parameter type,
/// give its value kind.
static ExprValueKind getValueKindForType(QualType T) {
diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td
index 6a9ff309e49c..231d597fe36a 100644
--- a/clang/include/clang/Basic/DiagnosticASTKinds.td
+++ b/clang/include/clang/Basic/DiagnosticASTKinds.td
@@ -72,6 +72,8 @@ def note_constexpr_array_index : Note<"cannot refer to element %0 of "
"in a constant expression">;
def note_constexpr_float_arithmetic : Note<
"floating point arithmetic produces %select{an infinity|a NaN}0">;
+def note_constexpr_dynamic_rounding : Note<
+ "cannot evaluate this expression if rounding mode is dynamic">;
def note_constexpr_pointer_subtraction_not_same_array : Note<
"subtracted pointers are not elements of the same array">;
def note_constexpr_pointer_subtraction_zero_size : Note<
diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp
index b664224aa732..7fc09edddf98 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -3577,6 +3577,18 @@ bool Expr::HasSideEffects(const ASTContext &Ctx,
return false;
}
+FPOptions Expr::getFPFeaturesInEffect(const LangOptions &LO) const {
+ if (auto Call = dyn_cast<CallExpr>(this))
+ return Call->getFPFeaturesInEffect(LO);
+ if (auto UO = dyn_cast<UnaryOperator>(this))
+ return UO->getFPFeaturesInEffect(LO);
+ if (auto BO = dyn_cast<BinaryOperator>(this))
+ return BO->getFPFeaturesInEffect(LO);
+ if (auto Cast = dyn_cast<CastExpr>(this))
+ return Cast->getFPFeaturesInEffect(LO);
+ return FPOptions::defaultWithoutTrailingStorage(LO);
+}
+
namespace {
/// Look for a call to a non-trivial function within an expression.
class NonTrivialCallFinder : public ConstEvaluatedExprVisitor<NonTrivialCallFinder>
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index e8f132dd4803..9bc4afd0b9f8 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -2418,14 +2418,60 @@ static bool HandleFloatToIntCast(EvalInfo &Info, const Expr *E,
return true;
}
+/// Get rounding mode used for evaluation of the specified expression.
+/// \param[out] DynamicRM Is set to true is the requested rounding mode is
+/// dynamic.
+/// If rounding mode is unknown at compile time, still try to evaluate the
+/// expression. If the result is exact, it does not depend on rounding mode.
+/// So return "tonearest" mode instead of "dynamic".
+static llvm::RoundingMode getActiveRoundingMode(EvalInfo &Info, const Expr *E,
+ bool &DynamicRM) {
+ llvm::RoundingMode RM =
+ E->getFPFeaturesInEffect(Info.Ctx.getLangOpts()).getRoundingMode();
+ DynamicRM = (RM == llvm::RoundingMode::Dynamic);
+ if (DynamicRM)
+ RM = llvm::RoundingMode::NearestTiesToEven;
+ return RM;
+}
+
+/// Check if the given evaluation result is allowed for constant evaluation.
+static bool checkFloatingPointResult(EvalInfo &Info, const Expr *E,
+ APFloat::opStatus St) {
+ FPOptions FPO = E->getFPFeaturesInEffect(Info.Ctx.getLangOpts());
+ if ((St & APFloat::opInexact) &&
+ FPO.getRoundingMode() == llvm::RoundingMode::Dynamic) {
+ // Inexact result means that it depends on rounding mode. If the requested
+ // mode is dynamic, the evaluation cannot be made in compile time.
+ Info.FFDiag(E, diag::note_constexpr_dynamic_rounding);
+ return false;
+ }
+
+ if (St & APFloat::opStatus::opInvalidOp) {
+ // There is no usefully definable result.
+ Info.FFDiag(E);
+ return false;
+ }
+
+ // FIXME: if:
+ // - evaluation triggered other FP exception, and
+ // - exception mode is not "ignore", and
+ // - the expression being evaluated is not a part of global variable
+ // initializer,
+ // the evaluation probably need to be rejected.
+ return true;
+}
+
static bool HandleFloatToFloatCast(EvalInfo &Info, const Expr *E,
QualType SrcType, QualType DestType,
APFloat &Result) {
+ assert(isa<CastExpr>(E) || isa<CompoundAssignOperator>(E));
+ bool DynamicRM;
+ llvm::RoundingMode RM = getActiveRoundingMode(Info, E, DynamicRM);
+ APFloat::opStatus St;
APFloat Value = Result;
bool ignored;
- Result.convert(Info.Ctx.getFloatTypeSemantics(DestType),
- APFloat::rmNearestTiesToEven, &ignored);
- return true;
+ St = Result.convert(Info.Ctx.getFloatTypeSemantics(DestType), RM, &ignored);
+ return checkFloatingPointResult(Info, E, St);
}
static APSInt HandleIntToIntCast(EvalInfo &Info, const Expr *E,
@@ -2647,28 +2693,31 @@ static bool handleIntIntBinOp(EvalInfo &Info, const Expr *E, const APSInt &LHS,
}
/// Perform the given binary floating-point operation, in-place, on LHS.
-static bool handleFloatFloatBinOp(EvalInfo &Info, const Expr *E,
+static bool handleFloatFloatBinOp(EvalInfo &Info, const BinaryOperator *E,
APFloat &LHS, BinaryOperatorKind Opcode,
const APFloat &RHS) {
+ bool DynamicRM;
+ llvm::RoundingMode RM = getActiveRoundingMode(Info, E, DynamicRM);
+ APFloat::opStatus St;
switch (Opcode) {
default:
Info.FFDiag(E);
return false;
case BO_Mul:
- LHS.multiply(RHS, APFloat::rmNearestTiesToEven);
+ St = LHS.multiply(RHS, RM);
break;
case BO_Add:
- LHS.add(RHS, APFloat::rmNearestTiesToEven);
+ St = LHS.add(RHS, RM);
break;
case BO_Sub:
- LHS.subtract(RHS, APFloat::rmNearestTiesToEven);
+ St = LHS.subtract(RHS, RM);
break;
case BO_Div:
// [expr.mul]p4:
// If the second operand of / or % is zero the behavior is undefined.
if (RHS.isZero())
Info.CCEDiag(E, diag::note_expr_divide_by_zero);
- LHS.divide(RHS, APFloat::rmNearestTiesToEven);
+ St = LHS.divide(RHS, RM);
break;
}
@@ -2680,7 +2729,8 @@ static bool handleFloatFloatBinOp(EvalInfo &Info, const Expr *E,
Info.CCEDiag(E, diag::note_constexpr_float_arithmetic) << LHS.isNaN();
return Info.noteUndefinedBehavior();
}
- return true;
+
+ return checkFloatingPointResult(Info, E, St);
}
static bool handleLogicalOpForVector(const APInt &LHSValue,
@@ -2763,7 +2813,7 @@ static bool handleCompareOpForVector(const APValue &LHSValue,
}
// Perform binary operations for vector types, in place on the LHS.
-static bool handleVectorVectorBinOp(EvalInfo &Info, const Expr *E,
+static bool handleVectorVectorBinOp(EvalInfo &Info, const BinaryOperator *E,
BinaryOperatorKind Opcode,
APValue &LHSValue,
const APValue &RHSValue) {
@@ -4077,7 +4127,7 @@ static bool handleAssignment(EvalInfo &Info, const Expr *E, const LValue &LVal,
namespace {
struct CompoundAssignSubobjectHandler {
EvalInfo &Info;
- const Expr *E;
+ const CompoundAssignOperator *E;
QualType PromotedLHSType;
BinaryOperatorKind Opcode;
const APValue &RHS;
@@ -4197,10 +4247,12 @@ struct CompoundAssignSubobjectHandler {
const AccessKinds CompoundAssignSubobjectHandler::AccessKind;
/// Perform a compound assignment of LVal <op>= RVal.
-static bool handleCompoundAssignment(
- EvalInfo &Info, const Expr *E,
- const LValue &LVal, QualType LValType, QualType PromotedLValType,
- BinaryOperatorKind Opcode, const APValue &RVal) {
+static bool handleCompoundAssignment(EvalInfo &Info,
+ const CompoundAssignOperator *E,
+ const LValue &LVal, QualType LValType,
+ QualType PromotedLValType,
+ BinaryOperatorKind Opcode,
+ const APValue &RVal) {
if (LVal.Designator.Invalid)
return false;
diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp
index acbc0434931d..e3132752546f 100644
--- a/clang/lib/AST/TextNodeDumper.cpp
+++ b/clang/lib/AST/TextNodeDumper.cpp
@@ -1105,6 +1105,8 @@ void TextNodeDumper::VisitCompoundAssignOperator(
dumpBareType(Node->getComputationLHSType());
OS << " ComputeResultTy=";
dumpBareType(Node->getComputationResultType());
+ if (Node->hasStoredFPFeatures())
+ printFPOptions(Node->getStoredFPFeatures());
}
void TextNodeDumper::VisitAddrLabelExpr(const AddrLabelExpr *Node) {
diff --git a/clang/lib/Sema/SemaAttr.cpp b/clang/lib/Sema/SemaAttr.cpp
index bd5fc586b6af..180c7b21eecc 100644
--- a/clang/lib/Sema/SemaAttr.cpp
+++ b/clang/lib/Sema/SemaAttr.cpp
@@ -981,7 +981,9 @@ void Sema::ActOnPragmaFPReassociate(SourceLocation Loc, bool IsEnabled) {
void Sema::setRoundingMode(SourceLocation Loc, llvm::RoundingMode FPR) {
// C2x: 7.6.2p3 If the FE_DYNAMIC mode is specified and FENV_ACCESS is "off",
// the translator may assume that the default rounding mode is in effect.
- if (FPR == llvm::RoundingMode::Dynamic && !CurFPFeatures.getAllowFEnvAccess())
+ if (FPR == llvm::RoundingMode::Dynamic &&
+ !CurFPFeatures.getAllowFEnvAccess() &&
+ CurFPFeatures.getFPExceptionMode() == LangOptions::FPE_Ignore)
FPR = llvm::RoundingMode::NearestTiesToEven;
FPOptionsOverride NewFPFeatures = CurFPFeatureOverrides();
diff --git a/clang/test/AST/const-fpfeatures-diag.c b/clang/test/AST/const-fpfeatures-diag.c
new file mode 100644
index 000000000000..c957459c05a5
--- /dev/null
+++ b/clang/test/AST/const-fpfeatures-diag.c
@@ -0,0 +1,8 @@
+// RUN: %clang_cc1 -verify -ffp-exception-behavior=strict -Wno-unknown-pragmas %s
+
+#pragma STDC FENV_ROUND FE_DYNAMIC
+
+// nextUp(1.F) == 0x1.000002p0F
+
+float F1 = 0x1.000000p0F + 0x0.000002p0F;
+float F2 = 0x1.000000p0F + 0x0.000001p0F; // expected-error{{initializer element is not a compile-time constant}}
diff --git a/clang/test/AST/const-fpfeatures.c b/clang/test/AST/const-fpfeatures.c
new file mode 100644
index 000000000000..bf512ff70658
--- /dev/null
+++ b/clang/test/AST/const-fpfeatures.c
@@ -0,0 +1,31 @@
+// RUN: %clang_cc1 -S -emit-llvm -Wno-unknown-pragmas %s -o - | FileCheck %s
+
+// nextUp(1.F) == 0x1.000002p0F
+
+const double _Complex C0 = 0x1.000001p0 + 0x1.000001p0I;
+
+#pragma STDC FENV_ROUND FE_UPWARD
+
+float F1u = 1.0F + 0x0.000002p0F;
+float F2u = 1.0F + 0x0.000001p0F;
+float F3u = 0x1.000001p0;
+// CHECK: @F1u = {{.*}} float 0x3FF0000020000000
+// CHECK: @F2u = {{.*}} float 0x3FF0000020000000
+// CHECK: @F3u = {{.*}} float 0x3FF0000020000000
+
+float _Complex C1u = C0;
+// CHECK: @C1u = {{.*}} { float, float } { float 0x3FF0000020000000, float 0x3FF0000020000000 }
+
+
+#pragma STDC FENV_ROUND FE_DOWNWARD
+
+float F1d = 1.0F + 0x0.000002p0F;
+float F2d = 1.0F + 0x0.000001p0F;
+float F3d = 0x1.000001p0;
+
+// CHECK: @F1d = {{.*}} float 0x3FF0000020000000
+// CHECK: @F2d = {{.*}} float 1.000000e+00
+// CHECK: @F3d = {{.*}} float 1.000000e+00
+
+float _Complex C1d = C0;
+// CHECK: @C1d = {{.*}} { float, float } { float 1.000000e+00, float 1.000000e+00 }
diff --git a/clang/test/AST/const-fpfeatures.cpp b/clang/test/AST/const-fpfeatures.cpp
new file mode 100644
index 000000000000..9c807b34625f
--- /dev/null
+++ b/clang/test/AST/const-fpfeatures.cpp
@@ -0,0 +1,81 @@
+// RUN: %clang_cc1 -S -emit-llvm -triple i386-linux -std=c++2a -Wno-unknown-pragmas %s -o - | FileCheck %s
+
+// nextUp(1.F) == 0x1.000002p0F
+
+constexpr float add_round_down(float x, float y) {
+ #pragma STDC FENV_ROUND FE_DOWNWARD
+ float res = x;
+ res += y;
+ return res;
+}
+
+constexpr float add_round_up(float x, float y) {
+ #pragma STDC FENV_ROUND FE_UPWARD
+ float res = x;
+ res += y;
+ return res;
+}
+
+float V1 = add_round_down(1.0F, 0x0.000001p0F);
+float V2 = add_round_up(1.0F, 0x0.000001p0F);
+// CHECK: @V1 = {{.*}} float 1.000000e+00
+// CHECK: @V2 = {{.*}} float 0x3FF0000020000000
+
+constexpr float add_cast_round_down(float x, double y) {
+ #pragma STDC FENV_ROUND FE_DOWNWARD
+ float res = x;
+ res += y;
+ return res;
+}
+
+constexpr float add_cast_round_up(float x, double y) {
+ #pragma STDC FENV_ROUND FE_UPWARD
+ float res = x;
+ res += y;
+ return res;
+}
+
+float V3 = add_cast_round_down(1.0F, 0x0.000001p0F);
+float V4 = add_cast_round_up(1.0F, 0x0.000001p0F);
+
+// CHECK: @V3 = {{.*}} float 1.000000e+00
+// CHECK: @V4 = {{.*}} float 0x3FF0000020000000
+
+// The next three variables use the same function as initializer, only rounding
+// modes
diff er.
+
+float V5 = []() -> float {
+ return [](float x, float y)->float {
+ #pragma STDC FENV_ROUND FE_UPWARD
+ return x + y;
+ }([](float x, float y) -> float {
+ #pragma STDC FENV_ROUND FE_UPWARD
+ return x + y;
+ }(1.0F, 0x0.000001p0F),
+ 0x0.000001p0F);
+}();
+// CHECK: @V5 = {{.*}} float 0x3FF0000040000000
+
+float V6 = []() -> float {
+ return [](float x, float y)->float {
+ #pragma STDC FENV_ROUND FE_DOWNWARD
+ return x + y;
+ }([](float x, float y) -> float {
+ #pragma STDC FENV_ROUND FE_UPWARD
+ return x + y;
+ }(1.0F, 0x0.000001p0F),
+ 0x0.000001p0F);
+}();
+// CHECK: @V6 = {{.*}} float 0x3FF0000020000000
+
+float V7 = []() -> float {
+ return [](float x, float y)->float {
+ #pragma STDC FENV_ROUND FE_DOWNWARD
+ return x + y;
+ }([](float x, float y) -> float {
+ #pragma STDC FENV_ROUND FE_DOWNWARD
+ return x + y;
+ }(1.0F, 0x0.000001p0F),
+ 0x0.000001p0F);
+}();
+// CHECK: @V7 = {{.*}} float 1.000000e+00
More information about the cfe-commits
mailing list