[clang] Implementation of '#pragma STDC FENV_ROUND' (PR #89617)

Serge Pavlov via cfe-commits cfe-commits at lists.llvm.org
Mon May 13 05:02:36 PDT 2024


https://github.com/spavloff updated https://github.com/llvm/llvm-project/pull/89617

>From ec8838685fb7af618445c3ff1bae953778996c37 Mon Sep 17 00:00:00 2001
From: Serge Pavlov <sepavloff at gmail.com>
Date: Thu, 14 Apr 2022 18:00:14 +0700
Subject: [PATCH 1/3] Implementation of '#pragma STDC FENV_ROUND'

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. Targets that support static rounding mode encoded in
instructions can have more efficient implementation, it is not
implemented in this change.

The implementation uses intrinsic functions 'get_rounding' and
'set_rounding' to save/restore dynamic rounding mode. In some cases
using functions that operate entire set of control modes or even FP
environment may give more efficient implementation. This optimization is
not a part of this change.
---
 clang/include/clang/AST/Stmt.h                |   9 +
 .../clang/Basic/DiagnosticParseKinds.td       |   3 -
 clang/include/clang/Basic/LangOptions.h       |   6 +
 clang/lib/CodeGen/CGStmt.cpp                  |  56 ++++++
 clang/lib/CodeGen/CodeGenFunction.h           |   3 +
 clang/lib/Parse/ParsePragma.cpp               |   3 -
 clang/test/CodeGen/complex-strictfp.c         |  60 ++++---
 clang/test/CodeGen/math-errno.c               |   6 +-
 clang/test/CodeGen/pragma-fenv_access.c       |  45 ++++-
 clang/test/CodeGen/pragma-fenv_round.c        | 160 ++++++++++++++++++
 clang/test/Parser/pragma-fenv_round.c         |   1 -
 11 files changed, 315 insertions(+), 37 deletions(-)
 create mode 100644 clang/test/CodeGen/pragma-fenv_round.c

diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h
index 9cd7a364cd3f1..6eceecd93e59c 100644
--- a/clang/include/clang/AST/Stmt.h
+++ b/clang/include/clang/AST/Stmt.h
@@ -1658,6 +1658,15 @@ class CompoundStmt final
     return *getTrailingObjects<FPOptionsOverride>();
   }
 
+  /// Get FPOptions inside this statement. They may differ from the outer
+  /// options due to pragmas.
+  /// \param CurFPOptions FPOptions outside this statement.
+  FPOptions getNewFPOptions(FPOptions CurFPOptions) const {
+    return hasStoredFPFeatures()
+               ? getStoredFPFeatures().applyOverrides(CurFPOptions)
+               : CurFPOptions;
+  }
+
   using body_iterator = Stmt **;
   using body_range = llvm::iterator_range<body_iterator>;
 
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 44bc4e0e130de..c9b0c0e616c71 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1266,9 +1266,6 @@ def ext_stdc_pragma_ignored : ExtWarn<"unknown pragma in STDC namespace">,
 // 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>;
diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h
index e2a2aa71b880b..53c498f1c4017 100644
--- a/clang/include/clang/Basic/LangOptions.h
+++ b/clang/include/clang/Basic/LangOptions.h
@@ -846,6 +846,12 @@ class FPOptions {
            getAllowFEnvAccess();
   }
 
+  /// Checks if the rounding mode is unknown at compile-time.
+  bool isRoundingModeDynamic() const {
+    return (getConstRoundingMode() == RoundingMode::Dynamic) &&
+           (getAllowFEnvAccess() || getRoundingMath());
+  }
+
   RoundingMode getRoundingMode() const {
     RoundingMode RM = getConstRoundingMode();
     if (RM == RoundingMode::Dynamic) {
diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index 576fe2f7a2d46..4fbc906afaeed 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -486,6 +486,56 @@ bool CodeGenFunction::EmitSimpleStmt(const Stmt *S,
   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 {
+    CGF.Builder.CreateIntrinsic(llvm::Intrinsic::set_rounding, {},
+                                {PreviousModes});
+  }
+};
+} // namespace
+
+void CodeGenFunction::emitSetFPControlModes(FPOptions NewFP) {
+  if (NewFP == CurFPFeatures)
+    return;
+
+  // For now only rounding mode is handled.
+
+  // If the new rounding mode is unknown in compile-time, it means that the
+  // compound statement contains `#pragma STDC FENV_ACCESS ON`. In this case all
+  // manipulations on FP environment, including setting and restoring control
+  // modes are made by the user.
+  if (NewFP.isRoundingModeDynamic())
+    return;
+
+  llvm::RoundingMode OldConstRM = CurFPFeatures.getConstRoundingMode();
+  llvm::RoundingMode NewConstRM = NewFP.getConstRoundingMode();
+  if (OldConstRM == NewConstRM)
+    return;
+
+  llvm::RoundingMode OldRM = CurFPFeatures.getRoundingMode();
+  if (OldRM == NewConstRM)
+    return;
+
+  llvm::Value *PreviousRM = nullptr;
+  if (CurFPFeatures.isRoundingModeDynamic()) {
+    llvm::Function *FGetRound = CGM.getIntrinsic(llvm::Intrinsic::get_rounding);
+    PreviousRM = Builder.CreateCall(FGetRound);
+  } else {
+    PreviousRM = llvm::ConstantInt::get(Int32Ty, static_cast<uint64_t>(OldRM));
+  }
+
+  llvm::RoundingMode NewRM = NewFP.getRoundingMode();
+  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).
@@ -509,6 +559,12 @@ CodeGenFunction::EmitCompoundStmtWithoutScope(const CompoundStmt &S,
   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.
+  FPOptions NewFP = S.getNewFPOptions(CurFPFeatures);
+  emitSetFPControlModes(NewFP);
+  CGFPOptionsRAII SavedFPFeatues(*this, NewFP);
+
   Address RetAlloca = Address::invalid();
 
   for (auto *CurStmt : S.body()) {
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index 6e7417fc7f52b..41ace99a47240 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -3324,6 +3324,9 @@ class CodeGenFunction : public CodeGenTypeCache {
   /// 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 and
+  /// creates corresponding cleanup action.
+  void emitSetFPControlModes(FPOptions NewFP);
 
   //===--------------------------------------------------------------------===//
   //                            Declaration Emission
diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp
index cd0fab5fe31d3..19043546057ba 100644
--- a/clang/lib/Parse/ParsePragma.cpp
+++ b/clang/lib/Parse/ParsePragma.cpp
@@ -3463,9 +3463,6 @@ void PragmaSTDC_FENV_ROUNDHandler::HandlePragma(Preprocessor &PP,
     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();
diff --git a/clang/test/CodeGen/complex-strictfp.c b/clang/test/CodeGen/complex-strictfp.c
index b0c84d5eebe72..e1f5405b92197 100644
--- a/clang/test/CodeGen/complex-strictfp.c
+++ b/clang/test/CodeGen/complex-strictfp.c
@@ -17,16 +17,18 @@ double D;
 
 // CHECK-LABEL: @test3a(
 // CHECK-NEXT:  entry:
+// CHECK-NEXT:    call void @llvm.set.rounding(i32 2) #[[ATTR3:[0-9]+]]
 // CHECK-NEXT:    [[TMP0:%.*]] = load double, ptr @D, align 8
 // CHECK-NEXT:    [[CF_REAL:%.*]] = load float, ptr @cf, align 4
 // CHECK-NEXT:    [[CF_IMAG:%.*]] = load float, ptr getelementptr inbounds ({ float, float }, ptr @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:    [[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:    [[CONV:%.*]] = call double @llvm.experimental.constrained.fpext.f64.f32(float [[CF_REAL]], metadata !"fpexcept.strict") #[[ATTR3]]
+// CHECK-NEXT:    [[CONV1:%.*]] = call double @llvm.experimental.constrained.fpext.f64.f32(float [[CF_IMAG]], metadata !"fpexcept.strict") #[[ATTR3]]
+// CHECK-NEXT:    [[ADD_R:%.*]] = call double @llvm.experimental.constrained.fadd.f64(double [[CONV]], double [[TMP0]], metadata !"round.upward", metadata !"fpexcept.strict") #[[ATTR3]]
+// CHECK-NEXT:    [[CONV2:%.*]] = call float @llvm.experimental.constrained.fptrunc.f32.f64(double [[ADD_R]], metadata !"round.upward", metadata !"fpexcept.strict") #[[ATTR3]]
+// CHECK-NEXT:    [[CONV3:%.*]] = call float @llvm.experimental.constrained.fptrunc.f32.f64(double [[CONV1]], metadata !"round.upward", metadata !"fpexcept.strict") #[[ATTR3]]
 // CHECK-NEXT:    store float [[CONV2]], ptr @cf, align 4
 // CHECK-NEXT:    store float [[CONV3]], ptr getelementptr inbounds ({ float, float }, ptr @cf, i32 0, i32 1), align 4
+// CHECK-NEXT:    call void @llvm.set.rounding(i32 1) #[[ATTR3]]
 // CHECK-NEXT:    ret void
 //
 void test3a(void) {
@@ -35,13 +37,15 @@ void test3a(void) {
 
 // CHECK-LABEL: @test3b(
 // CHECK-NEXT:  entry:
+// CHECK-NEXT:    call void @llvm.set.rounding(i32 2) #[[ATTR3]]
 // CHECK-NEXT:    [[CF_REAL:%.*]] = load float, ptr @cf, align 4
 // CHECK-NEXT:    [[CF_IMAG:%.*]] = load float, ptr getelementptr inbounds ({ float, float }, ptr @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]]
-// CHECK-NEXT:    [[CONV1:%.*]] = call double @llvm.experimental.constrained.fpext.f64.f32(float [[CF_IMAG]], metadata !"fpexcept.strict") #[[ATTR2]]
+// CHECK-NEXT:    [[CONV:%.*]] = call double @llvm.experimental.constrained.fpext.f64.f32(float [[CF_REAL]], metadata !"fpexcept.strict") #[[ATTR3]]
+// CHECK-NEXT:    [[CONV1:%.*]] = call double @llvm.experimental.constrained.fpext.f64.f32(float [[CF_IMAG]], metadata !"fpexcept.strict") #[[ATTR3]]
 // CHECK-NEXT:    [[TMP0:%.*]] = load double, ptr @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:    [[ADD_R:%.*]] = call double @llvm.experimental.constrained.fadd.f64(double [[TMP0]], double [[CONV]], metadata !"round.upward", metadata !"fpexcept.strict") #[[ATTR3]]
 // CHECK-NEXT:    store double [[ADD_R]], ptr @D, align 8
+// CHECK-NEXT:    call void @llvm.set.rounding(i32 1) #[[ATTR3]]
 // CHECK-NEXT:    ret void
 //
 void test3b(void) {
@@ -50,19 +54,21 @@ void test3b(void) {
 
 // CHECK-LABEL: @test3c(
 // CHECK-NEXT:  entry:
+// CHECK-NEXT:    call void @llvm.set.rounding(i32 2) #[[ATTR3]]
 // CHECK-NEXT:    [[G1_REAL:%.*]] = load double, ptr @g1, align 8
 // CHECK-NEXT:    [[G1_IMAG:%.*]] = load double, ptr getelementptr inbounds ({ double, double }, ptr @g1, i32 0, i32 1), align 8
 // CHECK-NEXT:    [[CF_REAL:%.*]] = load float, ptr @cf, align 4
 // CHECK-NEXT:    [[CF_IMAG:%.*]] = load float, ptr getelementptr inbounds ({ float, float }, ptr @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]]
-// CHECK-NEXT:    [[CONV1:%.*]] = call double @llvm.experimental.constrained.fpext.f64.f32(float [[CF_IMAG]], metadata !"fpexcept.strict") #[[ATTR2]]
-// CHECK-NEXT:    [[CALL:%.*]] = call { double, double } @__divdc3(double noundef [[CONV]], double noundef [[CONV1]], double noundef [[G1_REAL]], double noundef [[G1_IMAG]]) #[[ATTR3:[0-9]+]]
+// CHECK-NEXT:    [[CONV:%.*]] = call double @llvm.experimental.constrained.fpext.f64.f32(float [[CF_REAL]], metadata !"fpexcept.strict") #[[ATTR3]]
+// CHECK-NEXT:    [[CONV1:%.*]] = call double @llvm.experimental.constrained.fpext.f64.f32(float [[CF_IMAG]], metadata !"fpexcept.strict") #[[ATTR3]]
+// CHECK-NEXT:    [[CALL:%.*]] = call { double, double } @__divdc3(double noundef [[CONV]], double noundef [[CONV1]], double noundef [[G1_REAL]], double noundef [[G1_IMAG]]) #[[ATTR4:[0-9]+]]
 // CHECK-NEXT:    [[TMP0:%.*]] = extractvalue { double, double } [[CALL]], 0
 // CHECK-NEXT:    [[TMP1:%.*]] = extractvalue { double, double } [[CALL]], 1
-// CHECK-NEXT:    [[CONV2:%.*]] = call float @llvm.experimental.constrained.fptrunc.f32.f64(double [[TMP0]], metadata !"round.upward", metadata !"fpexcept.strict") #[[ATTR2]]
-// CHECK-NEXT:    [[CONV3:%.*]] = call float @llvm.experimental.constrained.fptrunc.f32.f64(double [[TMP1]], metadata !"round.upward", metadata !"fpexcept.strict") #[[ATTR2]]
+// CHECK-NEXT:    [[CONV2:%.*]] = call float @llvm.experimental.constrained.fptrunc.f32.f64(double [[TMP0]], metadata !"round.upward", metadata !"fpexcept.strict") #[[ATTR3]]
+// CHECK-NEXT:    [[CONV3:%.*]] = call float @llvm.experimental.constrained.fptrunc.f32.f64(double [[TMP1]], metadata !"round.upward", metadata !"fpexcept.strict") #[[ATTR3]]
 // CHECK-NEXT:    store float [[CONV2]], ptr @cf, align 4
 // CHECK-NEXT:    store float [[CONV3]], ptr getelementptr inbounds ({ float, float }, ptr @cf, i32 0, i32 1), align 4
+// CHECK-NEXT:    call void @llvm.set.rounding(i32 1) #[[ATTR3]]
 // CHECK-NEXT:    ret void
 //
 void test3c(void) {
@@ -71,12 +77,14 @@ void test3c(void) {
 
 // CHECK-LABEL: @test3d(
 // CHECK-NEXT:  entry:
+// CHECK-NEXT:    call void @llvm.set.rounding(i32 2) #[[ATTR3]]
 // CHECK-NEXT:    [[G1_REAL:%.*]] = load double, ptr @g1, align 8
 // CHECK-NEXT:    [[G1_IMAG:%.*]] = load double, ptr getelementptr inbounds ({ double, double }, ptr @g1, i32 0, i32 1), align 8
 // CHECK-NEXT:    [[TMP0:%.*]] = load double, ptr @D, align 8
-// 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:    [[ADD_R:%.*]] = call double @llvm.experimental.constrained.fadd.f64(double [[G1_REAL]], double [[TMP0]], metadata !"round.upward", metadata !"fpexcept.strict") #[[ATTR3]]
 // CHECK-NEXT:    store double [[ADD_R]], ptr @g1, align 8
 // CHECK-NEXT:    store double [[G1_IMAG]], ptr getelementptr inbounds ({ double, double }, ptr @g1, i32 0, i32 1), align 8
+// CHECK-NEXT:    call void @llvm.set.rounding(i32 1) #[[ATTR3]]
 // CHECK-NEXT:    ret void
 //
 void test3d(void) {
@@ -85,12 +93,14 @@ void test3d(void) {
 
 // CHECK-LABEL: @test3e(
 // CHECK-NEXT:  entry:
+// CHECK-NEXT:    call void @llvm.set.rounding(i32 2) #[[ATTR3]]
 // CHECK-NEXT:    [[TMP0:%.*]] = load double, ptr @D, align 8
 // CHECK-NEXT:    [[G1_REAL:%.*]] = load double, ptr @g1, align 8
 // CHECK-NEXT:    [[G1_IMAG:%.*]] = load double, ptr getelementptr inbounds ({ double, double }, ptr @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.upward", metadata !"fpexcept.strict") #[[ATTR2]]
+// CHECK-NEXT:    [[ADD_R:%.*]] = call double @llvm.experimental.constrained.fadd.f64(double [[TMP0]], double [[G1_REAL]], metadata !"round.upward", metadata !"fpexcept.strict") #[[ATTR3]]
 // CHECK-NEXT:    store double [[ADD_R]], ptr @g1, align 8
 // CHECK-NEXT:    store double [[G1_IMAG]], ptr getelementptr inbounds ({ double, double }, ptr @g1, i32 0, i32 1), align 8
+// CHECK-NEXT:    call void @llvm.set.rounding(i32 1) #[[ATTR3]]
 // CHECK-NEXT:    ret void
 //
 void test3e(void) {
@@ -99,8 +109,10 @@ void test3e(void) {
 
 // CHECK-LABEL: @t1(
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[CONV:%.*]] = call float @llvm.experimental.constrained.fptrunc.f32.f64(double 4.000000e+00, metadata !"round.upward", metadata !"fpexcept.strict") #[[ATTR2]]
+// CHECK-NEXT:    call void @llvm.set.rounding(i32 2) #[[ATTR3]]
+// CHECK-NEXT:    [[CONV:%.*]] = call float @llvm.experimental.constrained.fptrunc.f32.f64(double 4.000000e+00, metadata !"round.upward", metadata !"fpexcept.strict") #[[ATTR3]]
 // CHECK-NEXT:    store float [[CONV]], ptr @cf, align 4
+// CHECK-NEXT:    call void @llvm.set.rounding(i32 1) #[[ATTR3]]
 // CHECK-NEXT:    ret void
 //
 void t1(void) {
@@ -109,8 +121,10 @@ void t1(void) {
 
 // CHECK-LABEL: @t2(
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[CONV:%.*]] = call float @llvm.experimental.constrained.fptrunc.f32.f64(double 4.000000e+00, metadata !"round.upward", metadata !"fpexcept.strict") #[[ATTR2]]
+// CHECK-NEXT:    call void @llvm.set.rounding(i32 2) #[[ATTR3]]
+// CHECK-NEXT:    [[CONV:%.*]] = call float @llvm.experimental.constrained.fptrunc.f32.f64(double 4.000000e+00, metadata !"round.upward", metadata !"fpexcept.strict") #[[ATTR3]]
 // CHECK-NEXT:    store float [[CONV]], ptr getelementptr inbounds ({ float, float }, ptr @cf, i32 0, i32 1), align 4
+// CHECK-NEXT:    call void @llvm.set.rounding(i32 1) #[[ATTR3]]
 // CHECK-NEXT:    ret void
 //
 void t2(void) {
@@ -120,16 +134,18 @@ void t2(void) {
 // CHECK-LABEL: @t91(
 // CHECK-NEXT:  entry:
 // CHECK-NEXT:    [[C:%.*]] = alloca [0 x i8], align 1
+// CHECK-NEXT:    call void @llvm.set.rounding(i32 2) #[[ATTR3]]
 // 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]]
+// CHECK-NEXT:    [[CONV:%.*]] = call double @llvm.experimental.constrained.fpext.f64.f32(float 2.000000e+00, metadata !"fpexcept.strict") #[[ATTR3]]
 // CHECK-NEXT:    br label [[COND_END:%.*]]
 // CHECK:       cond.false:
-// CHECK-NEXT:    [[CONV1:%.*]] = call double @llvm.experimental.constrained.fpext.f64.f32(float 2.000000e+00, metadata !"fpexcept.strict") #[[ATTR2]]
+// CHECK-NEXT:    [[CONV1:%.*]] = call double @llvm.experimental.constrained.fpext.f64.f32(float 2.000000e+00, metadata !"fpexcept.strict") #[[ATTR3]]
 // CHECK-NEXT:    br label [[COND_END]]
 // 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) #[[ATTR3]]
 // CHECK-NEXT:    ret void
 //
 void t91(void) {
@@ -142,16 +158,18 @@ void t91(void) {
 // CHECK-LABEL: @t92(
 // CHECK-NEXT:  entry:
 // CHECK-NEXT:    [[C:%.*]] = alloca [0 x i8], align 1
+// CHECK-NEXT:    call void @llvm.set.rounding(i32 2) #[[ATTR3]]
 // 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]]
+// CHECK-NEXT:    [[CONV:%.*]] = call double @llvm.experimental.constrained.fpext.f64.f32(float 2.000000e+00, metadata !"fpexcept.strict") #[[ATTR3]]
 // CHECK-NEXT:    br label [[COND_END:%.*]]
 // CHECK:       cond.false:
-// CHECK-NEXT:    [[CONV1:%.*]] = call double @llvm.experimental.constrained.fpext.f64.f32(float 2.000000e+00, metadata !"fpexcept.strict") #[[ATTR2]]
+// CHECK-NEXT:    [[CONV1:%.*]] = call double @llvm.experimental.constrained.fpext.f64.f32(float 2.000000e+00, metadata !"fpexcept.strict") #[[ATTR3]]
 // CHECK-NEXT:    br label [[COND_END]]
 // 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) #[[ATTR3]]
 // CHECK-NEXT:    ret void
 //
 void t92(void) {
diff --git a/clang/test/CodeGen/math-errno.c b/clang/test/CodeGen/math-errno.c
index b5354e47e26b7..15340a11150c1 100644
--- a/clang/test/CodeGen/math-errno.c
+++ b/clang/test/CodeGen/math-errno.c
@@ -27,7 +27,7 @@ float f1(float x) {
 // CHECK: tail call float @sqrtf(float noundef {{.*}}) #[[ATTR4_O2:[0-9]+]]
 
 // FAST-LABEL: define {{.*}} nofpclass(nan inf) float @f1
-// FAST: call fast nofpclass(nan inf) float @sqrtf(float noundef nofpclass(nan inf) {{.*}}) #[[ATTR3_FAST:[0-9]+]]
+// FAST: call nofpclass(nan inf) float @sqrtf(float noundef nofpclass(nan inf) {{.*}}) #[[ATTR3_FAST:[0-9]+]]
 
 // NOOPT-LABEL: define {{.*}} float @f1
 // NOOPT: call float @sqrtf(float noundef {{.*}}) #[[ATTR4_NOOPT:[0-9]+]]
@@ -44,7 +44,7 @@ float f2(float x) {
 // FAST: call fast float @llvm.sqrt.f32(float {{.*}})
 
 // NOOPT-LABEL: define {{.*}} float @f2
-// NOOPT: call float @sqrtf(float {{.*}}) #[[ATTR4_NOOPT:[0-9]+]]
+// NOOPT: call fast float @sqrtf(float {{.*}}) #[[ATTR4_NOOPT:[0-9]+]]
 
 __attribute__((optnone))
 float f3(float x) {
@@ -56,7 +56,7 @@ float f3(float x) {
 // CHECK: call float @sqrtf(float noundef {{.*}})
 
 // FAST-LABEL: define {{.*}} nofpclass(nan inf) float @f3
-// FAST: call fast nofpclass(nan inf) float @sqrtf(float noundef nofpclass(nan inf) {{.*}}) #[[ATTR4_FAST:[0-9]+]]
+// FAST: call nofpclass(nan inf) float @sqrtf(float noundef nofpclass(nan inf) {{.*}}) #[[ATTR4_FAST:[0-9]+]]
 
 // NOOPT-LABEL: define {{.*}} float @f3
 // NOOPT:  call float @sqrtf(float noundef %0) #[[ATTR4_NOOPT:[0-9]+]]
diff --git a/clang/test/CodeGen/pragma-fenv_access.c b/clang/test/CodeGen/pragma-fenv_access.c
index afca115ed08d1..c079b9a76120d 100644
--- a/clang/test/CodeGen/pragma-fenv_access.c
+++ b/clang/test/CodeGen/pragma-fenv_access.c
@@ -1,9 +1,21 @@
-// RUN: %clang_cc1 -fexperimental-strict-floating-point -ffp-exception-behavior=strict -triple %itanium_abi_triple -emit-llvm %s -o - | FileCheck --check-prefixes=CHECK,STRICT %s
-// RUN: %clang_cc1 -fexperimental-strict-floating-point -frounding-math -ffp-exception-behavior=strict -triple %itanium_abi_triple -emit-llvm %s -o - | FileCheck --check-prefixes=CHECK,STRICT-RND %s
-// RUN: %clang_cc1 -fexperimental-strict-floating-point -ffp-exception-behavior=strict -triple %itanium_abi_triple -emit-llvm %s -o - -fms-extensions -DMS | FileCheck --check-prefixes=CHECK,STRICT %s
-// RUN: %clang_cc1 -fexperimental-strict-floating-point -frounding-math -ffp-exception-behavior=strict -triple %itanium_abi_triple -emit-llvm %s -o - -fms-extensions -DMS | FileCheck --check-prefixes=CHECK,STRICT-RND %s
-// RUN: %clang_cc1 -fexperimental-strict-floating-point -triple %itanium_abi_triple -emit-llvm %s -o - | FileCheck --check-prefixes=CHECK,DEFAULT %s
-// RUN: %clang_cc1 -fexperimental-strict-floating-point -frounding-math -triple %itanium_abi_triple -emit-llvm %s -o - | FileCheck --check-prefixes=CHECK,DEFAULT-RND %s
+// RUN: %clang_cc1 -fexperimental-strict-floating-point -ffp-exception-behavior=strict -triple %itanium_abi_triple -emit-llvm %s -o - \
+// RUN:   | FileCheck --check-prefixes=CHECK,STRICT,DEFM %s \
+// RUN:        --implicit-check-not "call void @llvm.set.rounding" --implicit-check-not "call i32 @llvm.get.rounding"
+// RUN: %clang_cc1 -fexperimental-strict-floating-point -frounding-math -ffp-exception-behavior=strict -triple %itanium_abi_triple -emit-llvm %s -o - \
+// RUN:   | FileCheck --check-prefixes=CHECK,STRICT-RND,RNDM %s \
+// RUN:        --implicit-check-not "call void @llvm.set.rounding" --implicit-check-not "call i32 @llvm.get.rounding"
+// RUN: %clang_cc1 -fexperimental-strict-floating-point -ffp-exception-behavior=strict -triple %itanium_abi_triple -emit-llvm %s -o - -fms-extensions -DMS \
+// RUN:   | FileCheck --check-prefixes=CHECK,STRICT,DEFM %s \
+// RUN:        --implicit-check-not "call void @llvm.set.rounding" --implicit-check-not "call i32 @llvm.get.rounding"
+// RUN: %clang_cc1 -fexperimental-strict-floating-point -frounding-math -ffp-exception-behavior=strict -triple %itanium_abi_triple -emit-llvm %s -o - -fms-extensions -DMS \
+// RUN:   | FileCheck --check-prefixes=CHECK,STRICT-RND,RNDM %s \
+// RUN:        --implicit-check-not "call void @llvm.set.rounding" --implicit-check-not "call i32 @llvm.get.rounding"
+// RUN: %clang_cc1 -fexperimental-strict-floating-point -triple %itanium_abi_triple -emit-llvm %s -o - \
+// RUN:   | FileCheck --check-prefixes=CHECK,DEFAULT,DEFM %s \
+// RUN:        --implicit-check-not "call void @llvm.set.rounding" --implicit-check-not "call i32 @llvm.get.rounding"
+// RUN: %clang_cc1 -fexperimental-strict-floating-point -frounding-math -triple %itanium_abi_triple -emit-llvm %s -o - \
+// RUN:   | FileCheck --check-prefixes=CHECK,DEFAULT-RND,RNDM %s \
+// RUN:        --implicit-check-not "call void @llvm.set.rounding" --implicit-check-not "call i32 @llvm.get.rounding"
 
 float func_00(float x, float y) {
   return x + y;
@@ -107,7 +119,11 @@ float func_08(float x, float y) {
   return x + y;
 }
 // CHECK-LABEL: @func_08
+// RNDM:   [[RM:%[0-9]+]] = call i32 @llvm.get.rounding()
+// CHECK:  call void @llvm.set.rounding(i32 2)
 // CHECK:  call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.upward", metadata !"fpexcept.strict")
+// RNDM:   call void @llvm.set.rounding(i32 [[RM]])
+// DEFM:   call void @llvm.set.rounding(i32 1)
 
 
 float func_09(float x, float y) {
@@ -116,7 +132,10 @@ float func_09(float x, float y) {
   return x + y;
 }
 // CHECK-LABEL: @func_09
+// RNDM:  [[RM:%[0-9]+]] = call i32 @llvm.get.rounding()
+// RNDM:  call void @llvm.set.rounding(i32 1)
 // CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict")
+// RNDM:  call void @llvm.set.rounding(i32 [[RM]])
 
 
 float func_10(float x, float y) {
@@ -126,7 +145,10 @@ float func_10(float x, float y) {
   return x + y;
 }
 // CHECK-LABEL: @func_10
+// RNDM:  [[RM:%[0-9]+]] = call i32 @llvm.get.rounding()
+// RNDM:  call void @llvm.set.rounding(i32 1)
 // CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore")
+// RNDM:  call void @llvm.set.rounding(i32 [[RM]])
 
 
 float func_11(float x, float y) {
@@ -136,8 +158,11 @@ float func_11(float x, float y) {
   return x + y;
 }
 // CHECK-LABEL: @func_11
+// RNDM:  [[RM:%[0-9]+]] = call i32 @llvm.get.rounding()
+// RNDM:  call void @llvm.set.rounding(i32 1)
 // STRICT: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore")
 // DEFAULT: fadd float
+// RNDM:  call void @llvm.set.rounding(i32 [[RM]])
 
 
 float func_12(float x, float y) {
@@ -156,7 +181,11 @@ float func_13(float x, float y) {
   return x + y;
 }
 // CHECK-LABEL: @func_13
+// RNDM:   [[RM:%[0-9]+]] = call i32 @llvm.get.rounding()
+// CHECK:  call void @llvm.set.rounding(i32 2)
 // CHECK:  call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.upward", metadata !"fpexcept.maytrap")
+// RNDM:   call void @llvm.set.rounding(i32 [[RM]])
+// DEFM:   call void @llvm.set.rounding(i32 1)
 
 
 float func_14(float x, float y, float z) {
@@ -184,10 +213,14 @@ float func_15(float x, float y, float z) {
   }
 }
 // CHECK-LABEL: @func_15
+// RNDM:    [[RM:%[0-9]+]] = call i32 @llvm.get.rounding()
+// CHECK:   call void @llvm.set.rounding(i32 0)
 // STRICT:  call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.strict")
 // STRICT:  call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.strict")
 // DEFAULT: call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.strict")
 // DEFAULT: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.ignore")
+// RNDM:    call void @llvm.set.rounding(i32 [[RM]])
+// DEFM:    call void @llvm.set.rounding(i32 1)
 
 
 float func_16(float x, float y) {
diff --git a/clang/test/CodeGen/pragma-fenv_round.c b/clang/test/CodeGen/pragma-fenv_round.c
new file mode 100644
index 0000000000000..3c7485ea09706
--- /dev/null
+++ b/clang/test/CodeGen/pragma-fenv_round.c
@@ -0,0 +1,160 @@
+// RUN: %clang_cc1 -S -triple x86_64-linux-gnu -emit-llvm %s -o - | \
+// RUN:   FileCheck %s --implicit-check-not "call void @llvm.set.rounding" --implicit-check-not "call i32 @llvm.get.rounding"
+
+float func_rz_ru(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_rz_ru
+// 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_rz_rz(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_rz_rz
+// 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_rne_rne(float w, float x, float y, float z) {
+  #pragma STDC FENV_ROUND FE_TONEAREST
+  float result = x * y;
+  {
+    #pragma STDC FENV_ROUND FE_TONEAREST
+    result += z;
+  }
+  return result - w;
+}
+
+// CHECK-LABEL: @func_rne_rne
+// CHECK:  fmul
+// CHECK:  fadd
+// CHECK:  fsub
+
+float func_rz_dyn_noacc(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_rz_dyn_noacc
+// 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)
+
+
+float func_rz_dyn(float w, float x, float y, float z) {
+  #pragma STDC FENV_ROUND FE_TOWARDZERO
+  float result = x * y;
+  {
+    #pragma STDC FENV_ROUND FE_DYNAMIC
+    #pragma STDC FENV_ACCESS ON
+    result += z;
+  }
+  return result - w;
+}
+
+// CHECK-LABEL: @func_rz_dyn
+// 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.dynamic", metadata !"fpexcept.strict")
+// CHECK:  call float @llvm.experimental.constrained.fsub.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.ignore")
+// CHECK:  call void @llvm.set.rounding(i32 1)
+
+
+float func_dyn_ru(float w, float x, float y, float z) {
+  #pragma STDC FENV_ROUND FE_DYNAMIC
+  #pragma STDC FENV_ACCESS ON
+  float result = x * y;
+  {
+    #pragma STDC FENV_ROUND FE_UPWARD
+    result += z;
+  }
+  return result - w;
+}
+
+// CHECK-LABEL: @func_dyn_ru
+// CHECK:  call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict")
+// CHECK:  [[RM:%[0-9]+]] = call i32 @llvm.get.rounding()
+// CHECK:  call void @llvm.set.rounding(i32 2)
+// CHECK:  call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.upward", metadata !"fpexcept.strict")
+// CHECK:  call void @llvm.set.rounding(i32 [[RM]])
+// CHECK:  call float @llvm.experimental.constrained.fsub.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict")
+
+
+float func_dyn_dyn(float w, float x, float y, float z) {
+  #pragma STDC FENV_ROUND FE_DYNAMIC
+  #pragma STDC FENV_ACCESS ON
+  #pragma clang fp exceptions(ignore)
+  float result = x * y;
+  {
+    #pragma STDC FENV_ROUND FE_DYNAMIC
+    result += z;
+  }
+  return result - w;
+}
+
+// CHECK-LABEL: @func_dyn_dyn
+// CHECK:  call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.ignore")
+// CHECK:  call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.ignore")
+// CHECK:  call float @llvm.experimental.constrained.fsub.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.ignore")
+
+
+#pragma STDC FENV_ROUND FE_DOWNWARD
+float func_glb_rd(float x, float y) {
+  return x + y;
+}
+
+// CHECK-LABEL: func_glb_rd
+// 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)
+
+float func_glb_rd2(float x, float y) {
+  return x - y;
+}
+
+// CHECK-LABEL: func_glb_rd2
+// CHECK:  call void @llvm.set.rounding(i32 3)
+// CHECK:  call float @llvm.experimental.constrained.fsub.f32({{.*}}, metadata !"round.downward", metadata !"fpexcept.ignore")
+// CHECK:  call void @llvm.set.rounding(i32 1)
+
+#pragma STDC FENV_ROUND FE_DOWNWARD
+float func_cvt_rd(int x) {
+  return x;
+}
+
+// CHECK-LABEL: @func_cvt_rd
+// 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)
diff --git a/clang/test/Parser/pragma-fenv_round.c b/clang/test/Parser/pragma-fenv_round.c
index b2f6f7ed41a4a..b8bc999d546f2 100644
--- a/clang/test/Parser/pragma-fenv_round.c
+++ b/clang/test/Parser/pragma-fenv_round.c
@@ -6,6 +6,5 @@ float func_01(int x, float y) {
   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 at -1{{pragma STDC FENV_ROUND is not supported}}
   return x + y;
 }

>From cd1a7c68417fb13e7fe14af215da0da156ce5a9a Mon Sep 17 00:00:00 2001
From: Serge Pavlov <sepavloff at gmail.com>
Date: Thu, 25 Apr 2024 18:35:06 +0700
Subject: [PATCH 2/3] Revome stale comment

---
 clang/include/clang/Basic/DiagnosticParseKinds.td | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index c9b0c0e616c71..d67a5f7f02eba 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1263,9 +1263,6 @@ def err_pragma_file_or_compound_scope : Error<
 // - #pragma stdc unknown
 def ext_stdc_pragma_ignored : ExtWarn<"unknown pragma in STDC namespace">,
    InGroup<UnknownPragmas>;
-// 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_unknown_rounding_mode : Warning<
   "invalid or unsupported rounding mode in '#pragma STDC FENV_ROUND' - ignored">,
   InGroup<IgnoredPragmas>;

>From cf6539667a9f3917cd85ff611ed813fb46304b2b Mon Sep 17 00:00:00 2001
From: Serge Pavlov <sepavloff at gmail.com>
Date: Sat, 11 May 2024 14:28:16 +0700
Subject: [PATCH 3/3] Support rounding mode switching for function calls

---
 clang/include/clang/Basic/TargetInfo.h |   8 ++
 clang/lib/Basic/Targets/RISCV.h        |   2 +
 clang/lib/CodeGen/CGCall.cpp           |  65 +++++++++++
 clang/lib/CodeGen/CGStmt.cpp           |  32 ++---
 clang/lib/CodeGen/CodeGenFunction.cpp  |   6 +
 clang/lib/CodeGen/CodeGenFunction.h    |  15 ++-
 clang/test/CodeGen/complex-strictfp.c  |   2 +
 clang/test/CodeGen/pragma-fenv_round.c | 155 +++++++++++++++++++++----
 8 files changed, 248 insertions(+), 37 deletions(-)

diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h
index 3ced2e7397a75..bb81188d76ae8 100644
--- a/clang/include/clang/Basic/TargetInfo.h
+++ b/clang/include/clang/Basic/TargetInfo.h
@@ -1232,6 +1232,14 @@ class TargetInfo : public TransferrableTargetInfo,
     return true;
   }
 
+  /// Returns true, if an operations that depends on rounding mode can be
+  /// implemented without changing FP environment. In this case the rounding
+  /// mode is encoded in the bits of implementing instruction.
+  virtual bool hasStaticRounding() const {
+    // Most supported targets require setting hardware register to use
+    // particular rounding mode.
+    return false;
+  }
   /// Returns the target triple of the primary target.
   const llvm::Triple &getTriple() const {
     return Triple;
diff --git a/clang/lib/Basic/Targets/RISCV.h b/clang/lib/Basic/Targets/RISCV.h
index d0e9cdc6da07b..e46982e71e00a 100644
--- a/clang/lib/Basic/Targets/RISCV.h
+++ b/clang/lib/Basic/Targets/RISCV.h
@@ -110,6 +110,8 @@ class RISCVTargetInfo : public TargetInfo {
 
   bool hasBFloat16Type() const override { return true; }
 
+  bool hasStaticRounding() const override { return true; }
+
   CallingConvCheckResult checkCallingConvention(CallingConv CC) const override;
 
   bool useFP16ConversionIntrinsics() const override {
diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index 69548902dc43b..6b8a02de4bc96 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -30,6 +30,7 @@
 #include "clang/CodeGen/CGFunctionInfo.h"
 #include "clang/CodeGen/SwiftCallingConv.h"
 #include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringSet.h"
 #include "llvm/Analysis/ValueTracking.h"
 #include "llvm/IR/Assumptions.h"
 #include "llvm/IR/AttributeMask.h"
@@ -5693,6 +5694,9 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
   AllocAlignAttrEmitter AllocAlignAttrEmitter(*this, TargetDecl, CallArgs);
   Attrs = AllocAlignAttrEmitter.TryEmitAsCallSiteAttribute(Attrs);
 
+  // Prepare execution environment.
+  setRoundingModeForCall(Callee);
+
   // Emit the actual call/invoke instruction.
   llvm::CallBase *CI;
   if (!InvokeDest) {
@@ -5846,6 +5850,9 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
   // lexical order, so deactivate it and run it manually here.
   CallArgs.freeArgumentMemory(*this);
 
+  // Restore execution environment.
+  restoreRoundingModeAfterCall();
+
   // Extract the return value.
   RValue Ret = [&] {
     switch (RetAI.getKind()) {
@@ -5980,6 +5987,64 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
   return Ret;
 }
 
+static bool endsWithRoundingModeSuffix(StringRef FuncName) {
+  size_t Underscore = FuncName.find_last_of("_");
+  if (Underscore == StringRef::npos || Underscore < 2)
+    return false;
+  StringRef Suffix = FuncName.substr(Underscore + 1);
+  static const StringRef RMSuffixes[] = {"rtz", "rte", "rtp", "rtn", "rhaz",
+                                         "rz",  "rn",  "ru",  "rd"};
+  for (auto RM : RMSuffixes) {
+    if (Suffix == RM)
+      return true;
+  }
+  return false;
+}
+
+bool CodeGenFunction::requiresDynamicRounding(const CGCallee &Callee) {
+  if (Callee.isOrdinary()) {
+    const Decl *CalleeDecl = Callee.getAbstractInfo().getCalleeDecl().getDecl();
+    if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(CalleeDecl)) {
+      IdentifierInfo *FuncNameII = FD->getDeclName().getAsIdentifierInfo();
+      if (FuncNameII) {
+        StringRef FuncName = FuncNameII->getName();
+        // If a reserved identifier ends with rounding mode suffix preceded by
+        // underscore, this function does not need the previous dynamic rounding
+        // mode to be set.
+        if (isReservedInAllContexts(
+                FuncNameII->isReserved(getContext().getLangOpts()))) {
+          if (endsWithRoundingModeSuffix(FuncName))
+            return false;
+        }
+      }
+    }
+  }
+  return true;
+}
+
+/// Sets dynamic rounding mode for the function called in the region where
+/// pragma FENV_ROUND is in effect.
+void CodeGenFunction::setRoundingModeForCall(const CGCallee &Callee) {
+  if (Target.hasStaticRounding() || Callee.isBuiltin() ||
+      !requiresDynamicRounding(Callee))
+    return;
+  if (!CurrentRoundingIsStatic || !DynamicRoundingMode)
+    return;
+  Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::set_rounding),
+                     DynamicRoundingMode);
+  CurrentRoundingIsStatic = false;
+}
+
+void CodeGenFunction::restoreRoundingModeAfterCall() {
+  if (Target.hasStaticRounding() || CurFPFeatures.isRoundingModeDynamic())
+    return;
+  if (CurrentRoundingIsStatic || !StaticRoundingMode)
+    return;
+  Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::set_rounding),
+                     StaticRoundingMode);
+  CurrentRoundingIsStatic = true;
+}
+
 CGCallee CGCallee::prepareConcreteCallee(CodeGenFunction &CGF) const {
   if (isVirtual()) {
     const CallExpr *CE = getVirtualCallExpr();
diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index 4fbc906afaeed..8dfdf3c21f7f5 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -499,12 +499,15 @@ struct FPControlModesCleanup final : EHScopeStack::Cleanup {
 };
 } // namespace
 
-void CodeGenFunction::emitSetFPControlModes(FPOptions NewFP) {
-  if (NewFP == CurFPFeatures)
+void CodeGenFunction::emitSetFPControlModes(FPOptions NewFP, FPOptions OldFP) {
+  if (NewFP == OldFP)
     return;
 
   // For now only rounding mode is handled.
 
+  if (Target.hasStaticRounding())
+    return;
+
   // If the new rounding mode is unknown in compile-time, it means that the
   // compound statement contains `#pragma STDC FENV_ACCESS ON`. In this case all
   // manipulations on FP environment, including setting and restoring control
@@ -512,28 +515,31 @@ void CodeGenFunction::emitSetFPControlModes(FPOptions NewFP) {
   if (NewFP.isRoundingModeDynamic())
     return;
 
-  llvm::RoundingMode OldConstRM = CurFPFeatures.getConstRoundingMode();
+  llvm::RoundingMode OldConstRM = OldFP.getConstRoundingMode();
   llvm::RoundingMode NewConstRM = NewFP.getConstRoundingMode();
   if (OldConstRM == NewConstRM)
     return;
 
-  llvm::RoundingMode OldRM = CurFPFeatures.getRoundingMode();
+  llvm::RoundingMode OldRM = OldFP.getRoundingMode();
   if (OldRM == NewConstRM)
     return;
 
-  llvm::Value *PreviousRM = nullptr;
-  if (CurFPFeatures.isRoundingModeDynamic()) {
+  if (OldFP.isRoundingModeDynamic()) {
     llvm::Function *FGetRound = CGM.getIntrinsic(llvm::Intrinsic::get_rounding);
-    PreviousRM = Builder.CreateCall(FGetRound);
+    DynamicRoundingMode = Builder.CreateCall(FGetRound);
   } else {
-    PreviousRM = llvm::ConstantInt::get(Int32Ty, static_cast<uint64_t>(OldRM));
+    DynamicRoundingMode =
+        llvm::ConstantInt::get(Int32Ty, static_cast<uint64_t>(OldRM));
   }
 
   llvm::RoundingMode NewRM = NewFP.getRoundingMode();
-  Builder.CreateIntrinsic(
-      llvm::Intrinsic::set_rounding, {},
-      llvm::ConstantInt::get(Int32Ty, static_cast<uint64_t>(NewRM)));
-  EHStack.pushCleanup<FPControlModesCleanup>(NormalAndEHCleanup, PreviousRM);
+  StaticRoundingMode =
+      llvm::ConstantInt::get(Int32Ty, static_cast<uint64_t>(NewRM));
+  Builder.CreateIntrinsic(llvm::Intrinsic::set_rounding, {},
+                          StaticRoundingMode);
+  EHStack.pushCleanup<FPControlModesCleanup>(NormalAndEHCleanup,
+                                             DynamicRoundingMode);
+  CurrentRoundingIsStatic = true;
 }
 
 /// EmitCompoundStmt - Emit a compound statement {..} node.  If GetLast is true,
@@ -562,8 +568,8 @@ CodeGenFunction::EmitCompoundStmtWithoutScope(const CompoundStmt &S,
   // Optionally set up the new FP environment, if the compound statement
   // contains a pragma that modifies it.
   FPOptions NewFP = S.getNewFPOptions(CurFPFeatures);
-  emitSetFPControlModes(NewFP);
   CGFPOptionsRAII SavedFPFeatues(*this, NewFP);
+  emitSetFPControlModes(NewFP, SavedFPFeatues.getOldFPOptions());
 
   Address RetAlloca = Address::invalid();
 
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index 87766a758311d..8ae6573ceacb3 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -147,6 +147,9 @@ CodeGenFunction::CGFPOptionsRAII::CGFPOptionsRAII(CodeGenFunction &CGF,
 void CodeGenFunction::CGFPOptionsRAII::ConstructorHelper(FPOptions FPFeatures) {
   OldFPFeatures = CGF.CurFPFeatures;
   CGF.CurFPFeatures = FPFeatures;
+  OldDynamicRM = CGF.DynamicRoundingMode;
+  OldStaticRM = CGF.StaticRoundingMode;
+  OldCurrentRoundingIsStatic = CGF.CurrentRoundingIsStatic;
 
   OldExcept = CGF.Builder.getDefaultConstrainedExcept();
   OldRounding = CGF.Builder.getDefaultConstrainedRounding();
@@ -191,6 +194,9 @@ void CodeGenFunction::CGFPOptionsRAII::ConstructorHelper(FPOptions FPFeatures) {
 
 CodeGenFunction::CGFPOptionsRAII::~CGFPOptionsRAII() {
   CGF.CurFPFeatures = OldFPFeatures;
+  CGF.DynamicRoundingMode = OldDynamicRM;
+  CGF.StaticRoundingMode = OldStaticRM;
+  CGF.CurrentRoundingIsStatic = OldCurrentRoundingIsStatic;
   CGF.Builder.setDefaultConstrainedExcept(OldExcept);
   CGF.Builder.setDefaultConstrainedRounding(OldRounding);
 }
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index 41ace99a47240..8489af7b3de55 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -817,15 +817,23 @@ class CodeGenFunction : public CodeGenTypeCache {
     CGFPOptionsRAII(CodeGenFunction &CGF, const Expr *E);
     ~CGFPOptionsRAII();
 
+    FPOptions getOldFPOptions() const { return OldFPFeatures; }
+
   private:
     void ConstructorHelper(FPOptions FPFeatures);
     CodeGenFunction &CGF;
     FPOptions OldFPFeatures;
+    llvm::Value *OldDynamicRM;
+    llvm::Value *OldStaticRM;
+    bool OldCurrentRoundingIsStatic;
     llvm::fp::ExceptionBehavior OldExcept;
     llvm::RoundingMode OldRounding;
     std::optional<CGBuilderTy::FastMathFlagGuard> FMFGuard;
   };
   FPOptions CurFPFeatures;
+  llvm::Value *DynamicRoundingMode = nullptr;
+  llvm::Value *StaticRoundingMode = nullptr;
+  bool CurrentRoundingIsStatic = false;
 
 public:
   /// ObjCEHValueStack - Stack of Objective-C exception values, used for
@@ -3326,7 +3334,12 @@ class CodeGenFunction : public CodeGenTypeCache {
 
   /// Optionally emit code that sets required floating-point control modes and
   /// creates corresponding cleanup action.
-  void emitSetFPControlModes(FPOptions NewFP);
+  void emitSetFPControlModes(FPOptions NewFP, FPOptions OldFP);
+
+  void setRoundingModeForCall(const CGCallee &Callee);
+  void restoreRoundingModeAfterCall();
+  bool requiresDynamicRounding(const CGCallee &Callee);
+
 
   //===--------------------------------------------------------------------===//
   //                            Declaration Emission
diff --git a/clang/test/CodeGen/complex-strictfp.c b/clang/test/CodeGen/complex-strictfp.c
index e1f5405b92197..6c0a9988feabc 100644
--- a/clang/test/CodeGen/complex-strictfp.c
+++ b/clang/test/CodeGen/complex-strictfp.c
@@ -61,7 +61,9 @@ void test3b(void) {
 // CHECK-NEXT:    [[CF_IMAG:%.*]] = load float, ptr getelementptr inbounds ({ float, float }, ptr @cf, i32 0, i32 1), align 4
 // CHECK-NEXT:    [[CONV:%.*]] = call double @llvm.experimental.constrained.fpext.f64.f32(float [[CF_REAL]], metadata !"fpexcept.strict") #[[ATTR3]]
 // CHECK-NEXT:    [[CONV1:%.*]] = call double @llvm.experimental.constrained.fpext.f64.f32(float [[CF_IMAG]], metadata !"fpexcept.strict") #[[ATTR3]]
+// CHECK-NEXT:    call void @llvm.set.rounding(i32 1) #[[ATTR3]]
 // CHECK-NEXT:    [[CALL:%.*]] = call { double, double } @__divdc3(double noundef [[CONV]], double noundef [[CONV1]], double noundef [[G1_REAL]], double noundef [[G1_IMAG]]) #[[ATTR4:[0-9]+]]
+// CHECK-NEXT:    call void @llvm.set.rounding(i32 2) #[[ATTR3]]
 // CHECK-NEXT:    [[TMP0:%.*]] = extractvalue { double, double } [[CALL]], 0
 // CHECK-NEXT:    [[TMP1:%.*]] = extractvalue { double, double } [[CALL]], 1
 // CHECK-NEXT:    [[CONV2:%.*]] = call float @llvm.experimental.constrained.fptrunc.f32.f64(double [[TMP0]], metadata !"round.upward", metadata !"fpexcept.strict") #[[ATTR3]]
diff --git a/clang/test/CodeGen/pragma-fenv_round.c b/clang/test/CodeGen/pragma-fenv_round.c
index 3c7485ea09706..64971ca00e761 100644
--- a/clang/test/CodeGen/pragma-fenv_round.c
+++ b/clang/test/CodeGen/pragma-fenv_round.c
@@ -1,5 +1,9 @@
-// RUN: %clang_cc1 -S -triple x86_64-linux-gnu -emit-llvm %s -o - | \
-// RUN:   FileCheck %s --implicit-check-not "call void @llvm.set.rounding" --implicit-check-not "call i32 @llvm.get.rounding"
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm %s -o - \
+// RUN:   | FileCheck --check-prefixes=CHECK,DYN %s \
+// RUN:        --implicit-check-not "call void @llvm.set.rounding" --implicit-check-not "call i32 @llvm.get.rounding"
+// RUN: %clang_cc1 -triple riscv32-linux-gnu -emit-llvm %s -o - \
+// RUN:   | FileCheck --check-prefixes=CHECK %s \
+// RUN:        --implicit-check-not "call void @llvm.set.rounding" --implicit-check-not "call i32 @llvm.get.rounding"
 
 float func_rz_ru(float w, float x, float y, float z) {
   #pragma STDC FENV_ROUND FE_TOWARDZERO
@@ -12,13 +16,13 @@ float func_rz_ru(float w, float x, float y, float z) {
 }
 
 // CHECK-LABEL: @func_rz_ru
-// CHECK:  call void @llvm.set.rounding(i32 0)
+// DYN:    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)
+// DYN:    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)
+// DYN:    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)
+// DYN:    call void @llvm.set.rounding(i32 1)
 
 
 float func_rz_rz(float w, float x, float y, float z) {
@@ -32,11 +36,11 @@ float func_rz_rz(float w, float x, float y, float z) {
 }
 
 // CHECK-LABEL: @func_rz_rz
-// CHECK:  call void @llvm.set.rounding(i32 0)
+// DYN:    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)
+// DYN:    call void @llvm.set.rounding(i32 1)
 
 float func_rne_rne(float w, float x, float y, float z) {
   #pragma STDC FENV_ROUND FE_TONEAREST
@@ -64,13 +68,13 @@ float func_rz_dyn_noacc(float w, float x, float y, float z) {
 }
 
 // CHECK-LABEL: @func_rz_dyn_noacc
-// CHECK:  call void @llvm.set.rounding(i32 0)
+// DYN:    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)
+// DYN:    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)
+// DYN:    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)
+// DYN:    call void @llvm.set.rounding(i32 1)
 
 
 float func_rz_dyn(float w, float x, float y, float z) {
@@ -85,11 +89,11 @@ float func_rz_dyn(float w, float x, float y, float z) {
 }
 
 // CHECK-LABEL: @func_rz_dyn
-// CHECK:  call void @llvm.set.rounding(i32 0)
+// DYN:    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.dynamic", metadata !"fpexcept.strict")
 // CHECK:  call float @llvm.experimental.constrained.fsub.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.ignore")
-// CHECK:  call void @llvm.set.rounding(i32 1)
+// DYN:    call void @llvm.set.rounding(i32 1)
 
 
 float func_dyn_ru(float w, float x, float y, float z) {
@@ -105,10 +109,10 @@ float func_dyn_ru(float w, float x, float y, float z) {
 
 // CHECK-LABEL: @func_dyn_ru
 // CHECK:  call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict")
-// CHECK:  [[RM:%[0-9]+]] = call i32 @llvm.get.rounding()
-// CHECK:  call void @llvm.set.rounding(i32 2)
+// DYN:    [[RM:%[0-9]+]] = call i32 @llvm.get.rounding()
+// DYN:    call void @llvm.set.rounding(i32 2)
 // CHECK:  call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.upward", metadata !"fpexcept.strict")
-// CHECK:  call void @llvm.set.rounding(i32 [[RM]])
+// DYN:    call void @llvm.set.rounding(i32 [[RM]])
 // CHECK:  call float @llvm.experimental.constrained.fsub.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict")
 
 
@@ -136,18 +140,18 @@ float func_glb_rd(float x, float y) {
 }
 
 // CHECK-LABEL: func_glb_rd
-// CHECK:  call void @llvm.set.rounding(i32 3)
+// DYN:    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)
+// DYN:    call void @llvm.set.rounding(i32 1)
 
 float func_glb_rd2(float x, float y) {
   return x - y;
 }
 
 // CHECK-LABEL: func_glb_rd2
-// CHECK:  call void @llvm.set.rounding(i32 3)
+// DYN:    call void @llvm.set.rounding(i32 3)
 // CHECK:  call float @llvm.experimental.constrained.fsub.f32({{.*}}, metadata !"round.downward", metadata !"fpexcept.ignore")
-// CHECK:  call void @llvm.set.rounding(i32 1)
+// DYN:    call void @llvm.set.rounding(i32 1)
 
 #pragma STDC FENV_ROUND FE_DOWNWARD
 float func_cvt_rd(int x) {
@@ -155,6 +159,111 @@ float func_cvt_rd(int x) {
 }
 
 // CHECK-LABEL: @func_cvt_rd
-// CHECK:  call void @llvm.set.rounding(i32 3)
+// DYN:    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)
+// DYN:    call void @llvm.set.rounding(i32 1)
+
+#pragma STDC FENV_ROUND FE_DYNAMIC
+
+void func_01(void);
+void __func_02_rtz(void);
+
+float func_call_01(float x, float y, float z) {
+  #pragma STDC FENV_ROUND FE_TOWARDZERO
+  float result = x * y;
+  func_01();
+  result += z;
+  return result;
+}
+
+// CHECK-LABEL: define {{.*}} float @func_call_01(
+// DYN:           call void @llvm.set.rounding(i32 0)
+// CHECK:         call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.ignore")
+// DYN:           call void @llvm.set.rounding(i32 1)
+// CHECK:         call void @func_01()
+// DYN:           call void @llvm.set.rounding(i32 0)
+// CHECK:         call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.ignore")
+// DYN:           call void @llvm.set.rounding(i32 1)
+
+float func_call_02(float x, float y) {
+  #pragma STDC FENV_ROUND FE_TOWARDZERO
+  float result = x * y;
+  __func_02_rtz();
+  return result;
+}
+
+// CHECK-LABEL: define {{.*}} float @func_call_02(
+// DYN:           call void @llvm.set.rounding(i32 0)
+// CHECK:         call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.ignore")
+// CHECK:         call void @__func_02_rtz()
+// DYN:           call void @llvm.set.rounding(i32 1)
+
+float func_call_03(float x, float y, float z) {
+  #pragma STDC FENV_ACCESS ON
+  #pragma STDC FENV_ROUND FE_TOWARDZERO
+  float result = x * y;
+  func_01();
+  result += z;
+  return result;
+}
+
+// CHECK-LABEL: define {{.*}} float @func_call_03(
+// DYN:           call void @llvm.set.rounding(i32 0)
+// CHECK:         call float @llvm.experimental.constrained.fmul.f32(float %0, float %1, metadata !"round.towardzero", metadata !"fpexcept.strict")
+// DYN:           call void @llvm.set.rounding(i32 1)
+// CHECK:         call void @func_01()
+// DYN:           call void @llvm.set.rounding(i32 0)
+// CHECK:         call float @llvm.experimental.constrained.fadd.f32(float %3, float %2, metadata !"round.towardzero", metadata !"fpexcept.strict")
+// DYN:           call void @llvm.set.rounding(i32 1)
+
+
+float func_call_04(float x, float y, float z) {
+  #pragma STDC FENV_ACCESS ON
+  #pragma STDC FENV_ROUND FE_DYNAMIC
+  float result = x * y;
+  func_01();
+  result += z;
+  return result;
+}
+
+// CHECK-LABEL: define {{.*}} float @func_call_04(
+// CHECK:         call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict")
+// CHECK:         call void @func_01()
+// CHECK:         call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict")
+
+float func_call_05(float x, float y, float z) {
+  #pragma STDC FENV_ACCESS ON
+  float result = x * y;
+  func_01();
+  result += z;
+  return result;
+}
+
+// CHECK-LABEL: define {{.*}} float @func_call_05(
+// CHECK:         call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict")
+// CHECK:         call void @func_01()
+// CHECK:         call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict")
+
+float func_call_06(float x, float y, float z) {
+  #pragma STDC FENV_ACCESS ON
+  #pragma STDC FENV_ROUND FE_DYNAMIC
+  float result = x * y;
+  {
+    #pragma STDC FENV_ROUND FE_TOWARDZERO
+    result += z;
+    func_01();
+  }
+  func_01();
+  return result;
+}
+
+// CHECK-LABEL: define {{.*}} float @func_call_06(
+// CHECK:         call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict")
+// DYN:           [[PREVRM:%[0-9]+]] = call i32 @llvm.get.rounding()
+// DYN:           call void @llvm.set.rounding(i32 0)
+// CHECK:         call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.strict")
+// DYN:           call void @llvm.set.rounding(i32 [[PREVRM]])
+// CHECK:         call void @func_01()
+// DYN:           call void @llvm.set.rounding(i32 0)
+// DYN:           call void @llvm.set.rounding(i32 [[PREVRM]])
+// CHECK:         call void @func_01()



More information about the cfe-commits mailing list