sepavloff created this revision.
sepavloff added reviewers: rsmith, rjmccall, mibintc, kpn.
Herald added a project: clang.
sepavloff requested review of this revision.

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`.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D87822

Files:
  clang/lib/AST/ExprConstant.cpp
  clang/lib/Sema/SemaAttr.cpp
  clang/test/AST/const-fpfeatures-diag.c
  clang/test/AST/const-fpfeatures.c

Index: clang/test/AST/const-fpfeatures.c
===================================================================
--- /dev/null
+++ clang/test/AST/const-fpfeatures.c
@@ -0,0 +1,22 @@
+// RUN: %clang_cc1 -S -emit-llvm -Wno-unknown-pragmas %s -o - | FileCheck %s
+
+// nextUp(1.F) == 0x1.000002p0F
+
+#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
+
+#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
Index: clang/test/AST/const-fpfeatures-diag.c
===================================================================
--- /dev/null
+++ 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}}
Index: clang/lib/Sema/SemaAttr.cpp
===================================================================
--- clang/lib/Sema/SemaAttr.cpp
+++ clang/lib/Sema/SemaAttr.cpp
@@ -981,7 +981,9 @@
 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();
Index: clang/lib/AST/ExprConstant.cpp
===================================================================
--- clang/lib/AST/ExprConstant.cpp
+++ clang/lib/AST/ExprConstant.cpp
@@ -2421,10 +2421,29 @@
 static bool HandleFloatToFloatCast(EvalInfo &Info, const Expr *E,
                                    QualType SrcType, QualType DestType,
                                    APFloat &Result) {
+  llvm::RoundingMode RM = llvm::RoundingMode::NearestTiesToEven;
+  if (auto Cast = dyn_cast<CastExpr>(E)) {
+    FPOptions FPFeatures = Cast->getFPFeaturesInEffect(Info.Ctx.getLangOpts());
+    RM = FPFeatures.getRoundingMode();
+  }
+  bool DynamicRM = RM == llvm::RoundingMode::Dynamic;
+  if (DynamicRM)
+    // If rounding mode is unknown at compile time, still try to execute the
+    // operation. If the result is exact, it does not depend on rounding mode.
+    RM = llvm::RoundingMode::NearestTiesToEven;
+
+  APFloat::opStatus St;
   APFloat Value = Result;
   bool ignored;
-  Result.convert(Info.Ctx.getFloatTypeSemantics(DestType),
-                 APFloat::rmNearestTiesToEven, &ignored);
+  St = Result.convert(Info.Ctx.getFloatTypeSemantics(DestType), RM, &ignored);
+
+  // FIXME: if:
+  // - evaluation triggered some 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;
 }
 
@@ -2647,31 +2666,51 @@
 }
 
 /// 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) {
+  FPOptions FPFeatures = E->getFPFeaturesInEffect(Info.Ctx.getLangOpts());
+  llvm::RoundingMode RM = FPFeatures.getRoundingMode();
+  bool DynamicRM = RM == llvm::RoundingMode::Dynamic;
+  if (DynamicRM)
+    // If rounding mode is unknown at compile time, still try to execute the
+    // operation. If the result is exact, it does not depend on rounding mode.
+    RM = llvm::RoundingMode::NearestTiesToEven;
+  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;
   }
 
+  if (DynamicRM && (St & APFloat::opInexact)) {
+    Info.FFDiag(E);
+    return false;
+  }
+
+  // FIXME: if:
+  // - evaluation triggered some 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.
+
   // [expr.pre]p4:
   //   If during the evaluation of an expression, the result is not
   //   mathematically defined [...], the behavior is undefined.
@@ -2763,7 +2802,7 @@
 }
 
 // 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 +4116,7 @@
 namespace {
 struct CompoundAssignSubobjectHandler {
   EvalInfo &Info;
-  const Expr *E;
+  const CompoundAssignOperator *E;
   QualType PromotedLHSType;
   BinaryOperatorKind Opcode;
   const APValue &RHS;
@@ -4197,10 +4236,12 @@
 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;
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to