sepavloff created this revision.
sepavloff added reviewers: rsmith, rjmccall, aaron.ballman, kpn, 
andrew.w.kaylor.
Herald added a project: All.
sepavloff requested review of this revision.
Herald added a project: clang.

This pragma is introduced by forthcoming C2x standard and can be used to
set particular rounding mode without need to call 'fesetmode' or accessing
control mode registers directly. Previously this pragma was implemented in
clang partially, only for the purpose of using in constant expressions and
making tests.

This change implements the pragma according to the standard draft. It sets
up dynamic rounding mode in the compound statement where the pragma acts.
This is inevitable for targets that set rounding mode by changing some
control register. Some targets however allow specifying rounding mode as a
field in instructions, in this case setting dynamic rounding mode is not
mandatory. However the standard draft requests that invocations of
functions shall be evaluated using dynamic rounding mode. So the dynamic
rounding mode is set even if actually later it is unused. Removal of
unneeded manipulation on dynamic rounding mode can be made by a separate
IR optimization.

The implementation uses intrinsic functions 'flt_rounds' and
'set_rounding' to save/restore dynamic rounding mode. It is not the best
way, because these functions need to extract rounding mode from the
content of some control register or pack into it. More efficient
implementation could save/restore entire value of the control register. It
requires functions 'fegetmode' and 'fesetmode' to be implemented as LLVM
intrinsic functions. When these functions will be implemented, the same
mechanism could be used in implementation of other similar fp pragmas.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D125625

Files:
  clang/include/clang/AST/Stmt.h
  clang/include/clang/Basic/DiagnosticParseKinds.td
  clang/lib/CodeGen/CGStmt.cpp
  clang/lib/CodeGen/CodeGenFunction.h
  clang/lib/Parse/ParsePragma.cpp
  clang/test/CodeGen/complex-strictfp.c
  clang/test/CodeGen/pragma-fenv_round.c
  clang/test/Parser/pragma-fenv_round.c

Index: clang/test/Parser/pragma-fenv_round.c
===================================================================
--- clang/test/Parser/pragma-fenv_round.c
+++ clang/test/Parser/pragma-fenv_round.c
@@ -6,6 +6,5 @@
   if (x)
     return y + 2;
   #pragma STDC FENV_ROUND FE_DOWNWARD // expected-error{{'#pragma STDC FENV_ROUND' can only appear at file scope or at the start of a compound statement}}
-                                      // expected-warning@-1{{pragma STDC FENV_ROUND is not supported}}
   return x + y;
 }
Index: clang/test/CodeGen/pragma-fenv_round.c
===================================================================
--- /dev/null
+++ clang/test/CodeGen/pragma-fenv_round.c
@@ -0,0 +1,98 @@
+// RUN: %clang_cc1 -S -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s
+
+float func_1(float w, float x, float y, float z) {
+  #pragma STDC FENV_ROUND FE_TOWARDZERO
+  float result = x * y;
+  {
+    #pragma STDC FENV_ROUND FE_UPWARD
+    result += z;
+  }
+  return result - w;
+}
+
+// CHECK-LABEL: @func_1
+// CHECK:  call void @llvm.set.rounding(i32 0)
+// CHECK:  call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.ignore")
+// CHECK:  call void @llvm.set.rounding(i32 2)
+// CHECK:  call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.upward", metadata !"fpexcept.ignore")
+// CHECK:  call void @llvm.set.rounding(i32 0)
+// CHECK:  call float @llvm.experimental.constrained.fsub.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.ignore")
+// CHECK:  call void @llvm.set.rounding(i32 1)
+
+
+float func_2(float w, float x, float y, float z) {
+  #pragma STDC FENV_ROUND FE_TOWARDZERO
+  float result = x * y;
+  {
+    #pragma STDC FENV_ROUND FE_TOWARDZERO
+    result += z;
+  }
+  return result - w;
+}
+
+// CHECK-LABEL: @func_2
+// CHECK:  call void @llvm.set.rounding(i32 0)
+// CHECK:  call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.ignore")
+// CHECK:  call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.ignore")
+// CHECK:  call float @llvm.experimental.constrained.fsub.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.ignore")
+// CHECK:  call void @llvm.set.rounding(i32 1)
+
+
+float func_3(float w, float x, float y, float z) {
+  #pragma STDC FENV_ROUND FE_TOWARDZERO
+  float result = x * y;
+  {
+    #pragma STDC FENV_ROUND FE_DYNAMIC
+    result += z;
+  }
+  return result - w;
+}
+
+// CHECK-LABEL: @func_3
+// CHECK:  call void @llvm.set.rounding(i32 0)
+// CHECK:  call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.ignore")
+// CHECK:  call void @llvm.set.rounding(i32 1)
+// CHECK:  call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore")
+// CHECK:  call void @llvm.set.rounding(i32 0)
+// CHECK:  call float @llvm.experimental.constrained.fsub.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.ignore")
+// CHECK:  call void @llvm.set.rounding(i32 1)
+
+
+#pragma STDC FENV_ROUND FE_DOWNWARD
+float func_20(float x, float y) {
+  return x + y;
+}
+
+// CHECK-LABEL: func_20
+// CHECK:  call void @llvm.set.rounding(i32 3)
+// CHECK:  call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.downward", metadata !"fpexcept.ignore")
+// CHECK:  call void @llvm.set.rounding(i32 1)
+
+
+#pragma STDC FENV_ROUND FE_DOWNWARD
+float func_21(int x) {
+  return x;
+}
+
+// CHECK-LABEL: @func_21
+// CHECK:  call void @llvm.set.rounding(i32 3)
+// CHECK:  call float @llvm.experimental.constrained.sitofp.f32.i32({{.*}}, metadata !"round.downward", metadata !"fpexcept.ignore")
+// CHECK:  call void @llvm.set.rounding(i32 1)
+
+
+#pragma STDC FENV_ROUND FE_DOWNWARD
+float func_22(float w, float x, float y, float z) {
+  #pragma STDC FENV_ROUND FE_TOWARDZERO
+  float result = x * y;
+  {
+    result += z;
+  }
+  return result - w;
+}
+
+// CHECK-LABEL: @func_22
+// CHECK:  call void @llvm.set.rounding(i32 0)
+// CHECK:  call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.ignore")
+// CHECK:  call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.ignore")
+// CHECK:  call float @llvm.experimental.constrained.fsub.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.ignore")
+// CHECK:  call void @llvm.set.rounding(i32 1)
Index: clang/test/CodeGen/complex-strictfp.c
===================================================================
--- clang/test/CodeGen/complex-strictfp.c
+++ clang/test/CodeGen/complex-strictfp.c
@@ -5,8 +5,6 @@
 // Test that the constrained intrinsics are picking up the exception
 // metadata from the AST instead of the global default from the command line.
 // Include rounding metadata in the testing.
-// FIXME: All cases of "fpexcept.maytrap" in this test are wrong.
-// FIXME: All cases of "round.tonearest" in this test are wrong.
 
 #pragma float_control(except, on)
 #pragma STDC FENV_ROUND FE_UPWARD
@@ -17,16 +15,18 @@
 
 // CHECK-LABEL: @test3a(
 // CHECK-NEXT:  entry:
+// CHECK-NEXT:    call void @llvm.set.rounding(i32 2) #[[ATTR2:[0-9]+]]
 // CHECK-NEXT:    [[TMP0:%.*]] = load double, double* @D, align 8
 // CHECK-NEXT:    [[CF_REAL:%.*]] = load float, float* getelementptr inbounds ({ float, float }, { float, float }* @cf, i32 0, i32 0), align 4
 // CHECK-NEXT:    [[CF_IMAG:%.*]] = load float, float* getelementptr inbounds ({ float, float }, { float, float }* @cf, i32 0, i32 1), align 4
-// CHECK-NEXT:    [[CONV:%.*]] = call double @llvm.experimental.constrained.fpext.f64.f32(float [[CF_REAL]], metadata !"fpexcept.strict") #[[ATTR2:[0-9]+]]
+// CHECK-NEXT:    [[CONV:%.*]] = call double @llvm.experimental.constrained.fpext.f64.f32(float [[CF_REAL]], metadata !"fpexcept.strict") #[[ATTR2]]
 // CHECK-NEXT:    [[CONV1:%.*]] = call double @llvm.experimental.constrained.fpext.f64.f32(float [[CF_IMAG]], metadata !"fpexcept.strict") #[[ATTR2]]
 // CHECK-NEXT:    [[ADD_R:%.*]] = call double @llvm.experimental.constrained.fadd.f64(double [[CONV]], double [[TMP0]], metadata !"round.upward", metadata !"fpexcept.strict") #[[ATTR2]]
 // CHECK-NEXT:    [[CONV2:%.*]] = call float @llvm.experimental.constrained.fptrunc.f32.f64(double [[ADD_R]], metadata !"round.upward", metadata !"fpexcept.strict") #[[ATTR2]]
 // CHECK-NEXT:    [[CONV3:%.*]] = call float @llvm.experimental.constrained.fptrunc.f32.f64(double [[CONV1]], metadata !"round.upward", metadata !"fpexcept.strict") #[[ATTR2]]
 // CHECK-NEXT:    store float [[CONV2]], float* getelementptr inbounds ({ float, float }, { float, float }* @cf, i32 0, i32 0), align 4
 // CHECK-NEXT:    store float [[CONV3]], float* getelementptr inbounds ({ float, float }, { float, float }* @cf, i32 0, i32 1), align 4
+// CHECK-NEXT:    call void @llvm.set.rounding(i32 1) #[[ATTR2]]
 // CHECK-NEXT:    ret void
 //
 void test3a(void) {
@@ -35,6 +35,7 @@
 
 // CHECK-LABEL: @test3b(
 // CHECK-NEXT:  entry:
+// CHECK-NEXT:    call void @llvm.set.rounding(i32 2) #[[ATTR2]]
 // CHECK-NEXT:    [[CF_REAL:%.*]] = load float, float* getelementptr inbounds ({ float, float }, { float, float }* @cf, i32 0, i32 0), align 4
 // CHECK-NEXT:    [[CF_IMAG:%.*]] = load float, float* getelementptr inbounds ({ float, float }, { float, float }* @cf, i32 0, i32 1), align 4
 // CHECK-NEXT:    [[CONV:%.*]] = call double @llvm.experimental.constrained.fpext.f64.f32(float [[CF_REAL]], metadata !"fpexcept.strict") #[[ATTR2]]
@@ -42,6 +43,7 @@
 // CHECK-NEXT:    [[TMP0:%.*]] = load double, double* @D, align 8
 // CHECK-NEXT:    [[ADD_R:%.*]] = call double @llvm.experimental.constrained.fadd.f64(double [[TMP0]], double [[CONV]], metadata !"round.upward", metadata !"fpexcept.strict") #[[ATTR2]]
 // CHECK-NEXT:    store double [[ADD_R]], double* @D, align 8
+// CHECK-NEXT:    call void @llvm.set.rounding(i32 1) #[[ATTR2]]
 // CHECK-NEXT:    ret void
 //
 void test3b(void) {
@@ -50,6 +52,7 @@
 
 // CHECK-LABEL: @test3c(
 // CHECK-NEXT:  entry:
+// CHECK-NEXT:    call void @llvm.set.rounding(i32 2) #[[ATTR2]]
 // CHECK-NEXT:    [[G1_REAL:%.*]] = load double, double* getelementptr inbounds ({ double, double }, { double, double }* @g1, i32 0, i32 0), align 8
 // CHECK-NEXT:    [[G1_IMAG:%.*]] = load double, double* getelementptr inbounds ({ double, double }, { double, double }* @g1, i32 0, i32 1), align 8
 // CHECK-NEXT:    [[CF_REAL:%.*]] = load float, float* getelementptr inbounds ({ float, float }, { float, float }* @cf, i32 0, i32 0), align 4
@@ -63,6 +66,7 @@
 // CHECK-NEXT:    [[CONV3:%.*]] = call float @llvm.experimental.constrained.fptrunc.f32.f64(double [[TMP1]], metadata !"round.upward", metadata !"fpexcept.strict") #[[ATTR2]]
 // CHECK-NEXT:    store float [[CONV2]], float* getelementptr inbounds ({ float, float }, { float, float }* @cf, i32 0, i32 0), align 4
 // CHECK-NEXT:    store float [[CONV3]], float* getelementptr inbounds ({ float, float }, { float, float }* @cf, i32 0, i32 1), align 4
+// CHECK-NEXT:    call void @llvm.set.rounding(i32 1) #[[ATTR2]]
 // CHECK-NEXT:    ret void
 //
 void test3c(void) {
@@ -71,12 +75,14 @@
 
 // CHECK-LABEL: @test3d(
 // CHECK-NEXT:  entry:
+// CHECK-NEXT:    call void @llvm.set.rounding(i32 2) #[[ATTR2]]
 // CHECK-NEXT:    [[G1_REAL:%.*]] = load double, double* getelementptr inbounds ({ double, double }, { double, double }* @g1, i32 0, i32 0), align 8
 // CHECK-NEXT:    [[G1_IMAG:%.*]] = load double, double* getelementptr inbounds ({ double, double }, { double, double }* @g1, i32 0, i32 1), align 8
 // CHECK-NEXT:    [[TMP0:%.*]] = load double, double* @D, align 8
-// CHECK-NEXT:    [[ADD_R:%.*]] = call double @llvm.experimental.constrained.fadd.f64(double [[G1_REAL]], double [[TMP0]], metadata !"round.tonearest", metadata !"fpexcept.maytrap") #[[ATTR2]]
+// CHECK-NEXT:    [[ADD_R:%.*]] = call double @llvm.experimental.constrained.fadd.f64(double [[G1_REAL]], double [[TMP0]], metadata !"round.upward", metadata !"fpexcept.strict") #[[ATTR2]]
 // CHECK-NEXT:    store double [[ADD_R]], double* getelementptr inbounds ({ double, double }, { double, double }* @g1, i32 0, i32 0), align 8
 // CHECK-NEXT:    store double [[G1_IMAG]], double* getelementptr inbounds ({ double, double }, { double, double }* @g1, i32 0, i32 1), align 8
+// CHECK-NEXT:    call void @llvm.set.rounding(i32 1) #[[ATTR2]]
 // CHECK-NEXT:    ret void
 //
 void test3d(void) {
@@ -85,12 +91,14 @@
 
 // CHECK-LABEL: @test3e(
 // CHECK-NEXT:  entry:
+// CHECK-NEXT:    call void @llvm.set.rounding(i32 2) #[[ATTR2]]
 // CHECK-NEXT:    [[TMP0:%.*]] = load double, double* @D, align 8
 // CHECK-NEXT:    [[G1_REAL:%.*]] = load double, double* getelementptr inbounds ({ double, double }, { double, double }* @g1, i32 0, i32 0), align 8
 // CHECK-NEXT:    [[G1_IMAG:%.*]] = load double, double* getelementptr inbounds ({ double, double }, { double, double }* @g1, i32 0, i32 1), align 8
-// CHECK-NEXT:    [[ADD_R:%.*]] = call double @llvm.experimental.constrained.fadd.f64(double [[TMP0]], double [[G1_REAL]], metadata !"round.tonearest", metadata !"fpexcept.maytrap") #[[ATTR2]]
+// CHECK-NEXT:    [[ADD_R:%.*]] = call double @llvm.experimental.constrained.fadd.f64(double [[TMP0]], double [[G1_REAL]], metadata !"round.upward", metadata !"fpexcept.strict") #[[ATTR2]]
 // CHECK-NEXT:    store double [[ADD_R]], double* getelementptr inbounds ({ double, double }, { double, double }* @g1, i32 0, i32 0), align 8
 // CHECK-NEXT:    store double [[G1_IMAG]], double* getelementptr inbounds ({ double, double }, { double, double }* @g1, i32 0, i32 1), align 8
+// CHECK-NEXT:    call void @llvm.set.rounding(i32 1) #[[ATTR2]]
 // CHECK-NEXT:    ret void
 //
 void test3e(void) {
@@ -99,8 +107,10 @@
 
 // CHECK-LABEL: @t1(
 // CHECK-NEXT:  entry:
+// CHECK-NEXT:    call void @llvm.set.rounding(i32 2) #[[ATTR2]]
 // CHECK-NEXT:    [[CONV:%.*]] = call float @llvm.experimental.constrained.fptrunc.f32.f64(double 4.000000e+00, metadata !"round.upward", metadata !"fpexcept.strict") #[[ATTR2]]
 // CHECK-NEXT:    store float [[CONV]], float* getelementptr inbounds ({ float, float }, { float, float }* @cf, i32 0, i32 0), align 4
+// CHECK-NEXT:    call void @llvm.set.rounding(i32 1) #[[ATTR2]]
 // CHECK-NEXT:    ret void
 //
 void t1(void) {
@@ -109,8 +119,10 @@
 
 // CHECK-LABEL: @t2(
 // CHECK-NEXT:  entry:
+// CHECK-NEXT:    call void @llvm.set.rounding(i32 2) #[[ATTR2]]
 // CHECK-NEXT:    [[CONV:%.*]] = call float @llvm.experimental.constrained.fptrunc.f32.f64(double 4.000000e+00, metadata !"round.upward", metadata !"fpexcept.strict") #[[ATTR2]]
 // CHECK-NEXT:    store float [[CONV]], float* getelementptr inbounds ({ float, float }, { float, float }* @cf, i32 0, i32 1), align 4
+// CHECK-NEXT:    call void @llvm.set.rounding(i32 1) #[[ATTR2]]
 // CHECK-NEXT:    ret void
 //
 void t2(void) {
@@ -120,6 +132,7 @@
 // CHECK-LABEL: @t91(
 // CHECK-NEXT:  entry:
 // CHECK-NEXT:    [[C:%.*]] = alloca [0 x i8], align 1
+// CHECK-NEXT:    call void @llvm.set.rounding(i32 2) #[[ATTR2]]
 // CHECK-NEXT:    br i1 false, label [[COND_TRUE:%.*]], label [[COND_FALSE:%.*]]
 // CHECK:       cond.true:
 // CHECK-NEXT:    [[CONV:%.*]] = call double @llvm.experimental.constrained.fpext.f64.f32(float 2.000000e+00, metadata !"fpexcept.strict") #[[ATTR2]]
@@ -130,6 +143,7 @@
 // CHECK:       cond.end:
 // CHECK-NEXT:    [[COND_R:%.*]] = phi double [ [[CONV]], [[COND_TRUE]] ], [ [[CONV1]], [[COND_FALSE]] ]
 // CHECK-NEXT:    [[COND_I:%.*]] = phi double [ 0.000000e+00, [[COND_TRUE]] ], [ 0.000000e+00, [[COND_FALSE]] ]
+// CHECK-NEXT:    call void @llvm.set.rounding(i32 1) #[[ATTR2]]
 // CHECK-NEXT:    ret void
 //
 void t91(void) {
@@ -142,6 +156,7 @@
 // CHECK-LABEL: @t92(
 // CHECK-NEXT:  entry:
 // CHECK-NEXT:    [[C:%.*]] = alloca [0 x i8], align 1
+// CHECK-NEXT:    call void @llvm.set.rounding(i32 2) #[[ATTR2]]
 // CHECK-NEXT:    br i1 false, label [[COND_TRUE:%.*]], label [[COND_FALSE:%.*]]
 // CHECK:       cond.true:
 // CHECK-NEXT:    [[CONV:%.*]] = call double @llvm.experimental.constrained.fpext.f64.f32(float 2.000000e+00, metadata !"fpexcept.strict") #[[ATTR2]]
@@ -152,6 +167,7 @@
 // CHECK:       cond.end:
 // CHECK-NEXT:    [[COND_R:%.*]] = phi double [ [[CONV]], [[COND_TRUE]] ], [ [[CONV1]], [[COND_FALSE]] ]
 // CHECK-NEXT:    [[COND_I:%.*]] = phi double [ 0.000000e+00, [[COND_TRUE]] ], [ 0.000000e+00, [[COND_FALSE]] ]
+// CHECK-NEXT:    call void @llvm.set.rounding(i32 1) #[[ATTR2]]
 // CHECK-NEXT:    ret void
 //
 void t92(void) {
Index: clang/lib/Parse/ParsePragma.cpp
===================================================================
--- clang/lib/Parse/ParsePragma.cpp
+++ clang/lib/Parse/ParsePragma.cpp
@@ -3228,9 +3228,6 @@
     return;
   }
 
-  // Until the pragma is fully implemented, issue a warning.
-  PP.Diag(Tok.getLocation(), diag::warn_stdc_fenv_round_not_supported);
-
   MutableArrayRef<Token> Toks(PP.getPreprocessorAllocator().Allocate<Token>(1),
                               1);
   Toks[0].startToken();
Index: clang/lib/CodeGen/CodeGenFunction.h
===================================================================
--- clang/lib/CodeGen/CodeGenFunction.h
+++ clang/lib/CodeGen/CodeGenFunction.h
@@ -3021,6 +3021,10 @@
   /// Get the record field index as represented in debug info.
   unsigned getDebugInfoFIndex(const RecordDecl *Rec, unsigned FieldIndex);
 
+  /// Optionally emit code that sets required floating-point control modes (like
+  /// rounding direction). Also creates corresponding cleanup action if the code
+  /// is emitted.
+  void emitSetFPControlModes(const CompoundStmt &S);
 
   //===--------------------------------------------------------------------===//
   //                            Declaration Emission
Index: clang/lib/CodeGen/CGStmt.cpp
===================================================================
--- clang/lib/CodeGen/CGStmt.cpp
+++ clang/lib/CodeGen/CGStmt.cpp
@@ -452,6 +452,46 @@
   return true;
 }
 
+namespace {
+/// Cleanup action that restores floating-point control modes upon leaving
+/// a scope.
+struct FPControlModesCleanup final : EHScopeStack::Cleanup {
+  llvm::Value *PreviousModes;
+  FPControlModesCleanup(llvm::Value *M) : PreviousModes(M) {}
+  void Emit(CodeGenFunction &CGF, Flags flags) override {
+    // TODO: using 'setmode' must be more efficient.
+    CGF.Builder.CreateIntrinsic(llvm::Intrinsic::set_rounding, {},
+                                {PreviousModes});
+  }
+};
+} // namespace
+
+void CodeGenFunction::emitSetFPControlModes(const CompoundStmt &S) {
+  if (!S.hasStoredFPFeatures())
+    return;
+  FPOptionsOverride FPO = S.getStoredFPFeatures();
+  if (!FPO.hasRoundingModeOverride() ||
+      FPO.getRoundingModeOverride() == llvm::RoundingMode::Dynamic)
+    return;
+  FPOptions FP = FPO.applyOverrides(getLangOpts());
+  llvm::RoundingMode OldRM = CurFPFeatures.getRoundingMode();
+  llvm::RoundingMode NewRM = FP.getRoundingMode();
+  llvm::Value *PreviousRM = nullptr;
+  if (OldRM == llvm::RoundingMode::Dynamic) {
+    llvm::Function *FGetRound = CGM.getIntrinsic(llvm::Intrinsic::flt_rounds);
+    PreviousRM = Builder.CreateCall(FGetRound);
+  } else if (NewRM == llvm::RoundingMode::Dynamic || NewRM != OldRM) {
+    PreviousRM = llvm::ConstantInt::get(Int32Ty, static_cast<uint64_t>(OldRM));
+  } else {
+    return;
+  }
+  if (NewRM != llvm::RoundingMode::Dynamic)
+    Builder.CreateIntrinsic(
+        llvm::Intrinsic::set_rounding, {},
+        llvm::ConstantInt::get(Int32Ty, static_cast<uint64_t>(NewRM)));
+  EHStack.pushCleanup<FPControlModesCleanup>(NormalAndEHCleanup, PreviousRM);
+}
+
 /// EmitCompoundStmt - Emit a compound statement {..} node.  If GetLast is true,
 /// this captures the expression result of the last sub-statement and returns it
 /// (for use by the statement expression extension).
@@ -475,6 +515,14 @@
   assert((!GetLast || (GetLast && ExprResult)) &&
          "If GetLast is true then the CompoundStmt must have a StmtExprResult");
 
+  // Optionally set up the new FP environment, if the compound statement
+  // contains a pragma that modifies it.
+  emitSetFPControlModes(S);
+  CGFPOptionsRAII SavedFPFeatues(
+      *this, S.changesFPControlModes()
+                 ? S.getStoredFPFeatures().applyOverrides(CurFPFeatures)
+                 : CurFPFeatures);
+
   Address RetAlloca = Address::invalid();
 
   for (auto *CurStmt : S.body()) {
Index: clang/include/clang/Basic/DiagnosticParseKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticParseKinds.td
+++ clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1189,9 +1189,6 @@
 // The C standard 7.6.1p2 says "The [FENV_ACCESS] pragma shall occur either
 // outside external declarations or preceding all explicit declarations and
 // statements inside a compound statement.
-def warn_stdc_fenv_round_not_supported :
-   Warning<"pragma STDC FENV_ROUND is not supported">,
-   InGroup<UnknownPragmas>;
 def warn_stdc_unknown_rounding_mode : Warning<
   "invalid or unsupported rounding mode in '#pragma STDC FENV_ROUND' - ignored">,
   InGroup<IgnoredPragmas>;
Index: clang/include/clang/AST/Stmt.h
===================================================================
--- clang/include/clang/AST/Stmt.h
+++ clang/include/clang/AST/Stmt.h
@@ -1457,6 +1457,13 @@
     return *getTrailingObjects<FPOptionsOverride>();
   }
 
+  bool changesFPControlModes() const {
+    return hasStoredFPFeatures() &&
+           getStoredFPFeatures().hasRoundingModeOverride() &&
+           getStoredFPFeatures().getRoundingModeOverride() !=
+               llvm::RoundingMode::Dynamic;
+  }
+
   using body_iterator = Stmt **;
   using body_range = llvm::iterator_range<body_iterator>;
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to