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

Serge Pavlov via cfe-commits cfe-commits at lists.llvm.org
Mon Apr 22 09:14:36 PDT 2024


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

>From fc7aab600c25b39b2df039c0cbcf517719736311 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] 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               |   2 +-
 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, 313 insertions(+), 35 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 1b9c9231047717..0ee9c13df75e41 100644
--- a/clang/include/clang/AST/Stmt.h
+++ b/clang/include/clang/AST/Stmt.h
@@ -1663,6 +1663,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 bb9ca2a50cc06c..cbda8975717d2e 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1255,9 +1255,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 24b109e32cdd3e..ae8c7b2ead8ee2 100644
--- a/clang/include/clang/Basic/LangOptions.h
+++ b/clang/include/clang/Basic/LangOptions.h
@@ -834,6 +834,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 576fe2f7a2d46f..4fbc906afaeed5 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 c49e9fd00c8d3e..4964a1c6c0c5fb 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -3323,6 +3323,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 3979f75b6020db..56b15bda975a9a 100644
--- a/clang/lib/Parse/ParsePragma.cpp
+++ b/clang/lib/Parse/ParsePragma.cpp
@@ -3461,9 +3461,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 b0c84d5eebe725..e1f5405b921978 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 b5354e47e26b77..b8da81b585fa53 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]+]]
diff --git a/clang/test/CodeGen/pragma-fenv_access.c b/clang/test/CodeGen/pragma-fenv_access.c
index afca115ed08d1c..c079b9a76120d2 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 00000000000000..3c7485ea097064
--- /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 b2f6f7ed41a4a3..b8bc999d546f2e 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;
 }



More information about the cfe-commits mailing list