https://github.com/Serosh-commits updated 
https://github.com/llvm/llvm-project/pull/194327

>From 07d3937ff15dff49c2e2cfcd56c2faed9c827faf Mon Sep 17 00:00:00 2001
From: LLVM Contributor <[email protected]>
Date: Sat, 23 May 2026 03:35:30 +0530
Subject: [PATCH] address reviewer feedback

---
 clang/include/clang/Basic/Builtins.td         |  60 +-
 clang/lib/AST/ByteCode/Interp.h               |  18 +-
 clang/lib/AST/ByteCode/InterpBuiltin.cpp      | 547 +++++++++++++++++-
 clang/lib/AST/ByteCode/InterpHelpers.h        |   8 +-
 clang/lib/AST/ExprConstant.cpp                | 362 +++++++++++-
 clang/test/CodeGen/builtins-fenv-access.c     |  76 +++
 clang/test/CodeGen/builtins-fenv-round.c      |  22 +
 .../test/SemaCXX/constexpr-cmath-builtins.cpp | 311 ++++++++++
 clang/test/SemaCXX/constexpr-fenv-access.cpp  |  13 +
 9 files changed, 1380 insertions(+), 37 deletions(-)
 create mode 100644 clang/test/CodeGen/builtins-fenv-access.c
 create mode 100644 clang/test/CodeGen/builtins-fenv-round.c
 create mode 100644 clang/test/SemaCXX/constexpr-cmath-builtins.cpp
 create mode 100644 clang/test/SemaCXX/constexpr-fenv-access.cpp

diff --git a/clang/include/clang/Basic/Builtins.td 
b/clang/include/clang/Basic/Builtins.td
index 40ec94ab75046..cc7bd98542634 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -138,7 +138,7 @@ def CbrtF128 : Builtin {
 
 def CeilF16F128 : Builtin, F16F128MathTemplate {
   let Spellings = ["__builtin_ceil"];
-  let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const];
+  let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const, Constexpr];
   let Prototype = "T(T)";
 }
 
@@ -192,19 +192,19 @@ def Expm1F128 : Builtin {
 
 def FdimF128 : Builtin {
   let Spellings = ["__builtin_fdimf128"];
-  let Attributes = [FunctionWithBuiltinPrefix, NoThrow, 
ConstIgnoringErrnoAndExceptions];
+  let Attributes = [FunctionWithBuiltinPrefix, NoThrow, 
ConstIgnoringErrnoAndExceptions, Constexpr];
   let Prototype = "__float128(__float128, __float128)";
 }
 
 def FloorF16F128 : Builtin, F16F128MathTemplate {
   let Spellings = ["__builtin_floor"];
-  let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const];
+  let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const, Constexpr];
   let Prototype = "T(T)";
 }
 
 def FmaF16F128 : Builtin, F16F128MathTemplate {
   let Spellings = ["__builtin_fma"];
-  let Attributes = [FunctionWithBuiltinPrefix, NoThrow, 
ConstIgnoringErrnoAndExceptions];
+  let Attributes = [FunctionWithBuiltinPrefix, NoThrow, 
ConstIgnoringErrnoAndExceptions, Constexpr];
   let Prototype = "T(T, T, T)";
 }
 
@@ -264,13 +264,13 @@ def FabsF128 : Builtin {
 
 def FmodF16F128 : F16F128MathTemplate, Builtin {
   let Spellings = ["__builtin_fmod"];
-  let Attributes = [FunctionWithBuiltinPrefix, NoThrow, 
ConstIgnoringErrnoAndExceptions];
+  let Attributes = [FunctionWithBuiltinPrefix, NoThrow, 
ConstIgnoringErrnoAndExceptions, Constexpr];
   let Prototype = "T(T, T)";
 }
 
 def FrexpF16F128 : F16F128MathTemplate, Builtin {
   let Spellings = ["__builtin_frexp"];
-  let Attributes = [FunctionWithBuiltinPrefix, NoThrow];
+  let Attributes = [FunctionWithBuiltinPrefix, NoThrow, 
ConstIgnoringErrnoAndExceptions, Constexpr];
   let Prototype = "T(T, int*)";
 }
 
@@ -300,13 +300,13 @@ def InfF16 : Builtin {
 
 def LdexpF16F128 : F16F128MathTemplate, Builtin {
   let Spellings = ["__builtin_ldexp"];
-  let Attributes = [FunctionWithBuiltinPrefix, NoThrow, 
ConstIgnoringErrnoAndExceptions];
+  let Attributes = [FunctionWithBuiltinPrefix, NoThrow, 
ConstIgnoringErrnoAndExceptions, Constexpr];
   let Prototype = "T(T, int)";
 }
 
 def ModfF128 : Builtin {
   let Spellings = ["__builtin_modff128"];
-  let Attributes = [FunctionWithBuiltinPrefix, NoThrow];
+  let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Constexpr];
   let Prototype = "__float128(__float128, __float128*)";
 }
 
@@ -352,7 +352,7 @@ def HypotF128 : Builtin {
 
 def ILogbF128 : Builtin {
   let Spellings = ["__builtin_ilogbf128"];
-  let Attributes = [FunctionWithBuiltinPrefix, NoThrow, 
ConstIgnoringErrnoAndExceptions];
+  let Attributes = [FunctionWithBuiltinPrefix, NoThrow, 
ConstIgnoringErrnoAndExceptions, Constexpr];
   let Prototype = "int(__float128)";
 }
 
@@ -370,7 +370,7 @@ def LLrintF128 : Builtin {
 
 def LLroundF128 : Builtin {
   let Spellings = ["__builtin_llroundf128"];
-  let Attributes = [FunctionWithBuiltinPrefix, NoThrow, 
ConstIgnoringErrnoAndExceptions];
+  let Attributes = [FunctionWithBuiltinPrefix, NoThrow, 
ConstIgnoringErrnoAndExceptions, Constexpr];
   let Prototype = "long long int(__float128)";
 }
 
@@ -412,7 +412,7 @@ def LrintF128 : Builtin {
 
 def LroundF128 : Builtin {
   let Spellings = ["__builtin_lroundf128"];
-  let Attributes = [FunctionWithBuiltinPrefix, NoThrow, 
ConstIgnoringErrnoAndExceptions];
+  let Attributes = [FunctionWithBuiltinPrefix, NoThrow, 
ConstIgnoringErrnoAndExceptions, Constexpr];
   let Prototype = "long int(__float128)";
 }
 
@@ -424,25 +424,25 @@ def NearbyintF128 : Builtin {
 
 def NextafterF128 : Builtin {
   let Spellings = ["__builtin_nextafterf128"];
-  let Attributes = [FunctionWithBuiltinPrefix, NoThrow, 
ConstIgnoringErrnoAndExceptions];
+  let Attributes = [FunctionWithBuiltinPrefix, NoThrow, 
ConstIgnoringErrnoAndExceptions, Constexpr];
   let Prototype = "__float128(__float128, __float128)";
 }
 
 def NexttowardF128 : Builtin {
   let Spellings = ["__builtin_nexttowardf128"];
-  let Attributes = [FunctionWithBuiltinPrefix, NoThrow, 
ConstIgnoringErrnoAndExceptions];
+  let Attributes = [FunctionWithBuiltinPrefix, NoThrow, 
ConstIgnoringErrnoAndExceptions, Constexpr];
   let Prototype = "__float128(__float128, __float128)";
 }
 
 def RemainderF128 : Builtin {
   let Spellings = ["__builtin_remainderf128"];
-  let Attributes = [FunctionWithBuiltinPrefix, NoThrow, 
ConstIgnoringErrnoAndExceptions];
+  let Attributes = [FunctionWithBuiltinPrefix, NoThrow, 
ConstIgnoringErrnoAndExceptions, Constexpr];
   let Prototype = "__float128(__float128, __float128)";
 }
 
 def RemquoF128 : Builtin {
   let Spellings = ["__builtin_remquof128"];
-  let Attributes = [FunctionWithBuiltinPrefix, NoThrow];
+  let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Constexpr];
   let Prototype = "__float128(__float128, __float128, int*)";
 }
 
@@ -454,27 +454,27 @@ def RintF16F128 : Builtin, F16F128MathTemplate {
 
 def RoundF16F128 : Builtin, F16F128MathTemplate {
   let Spellings = ["__builtin_round"];
-  let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const];
+  let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const, Constexpr];
   let Prototype = "T(T)";
 }
 
 def RoundevenF16F128 : Builtin, F16F128MathTemplate {
   let Spellings = ["__builtin_roundeven"];
-  let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const];
+  let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const, Constexpr];
   let Prototype = "T(T)";
 }
 
 def ScanlblnF128 : Builtin {
   let Spellings = ["__builtin_scalblnf128"];
   let Attributes = [FunctionWithBuiltinPrefix, NoThrow,
-                    ConstIgnoringErrnoAndExceptions];
+                    ConstIgnoringErrnoAndExceptions, Constexpr];
   let Prototype = "__float128(__float128, long int)";
 }
 
 def ScanlbnF128 : Builtin {
   let Spellings = ["__builtin_scalbnf128"];
   let Attributes = [FunctionWithBuiltinPrefix, NoThrow,
-                    ConstIgnoringErrnoAndExceptions];
+                    ConstIgnoringErrnoAndExceptions, Constexpr];
   let Prototype = "__float128(__float128, int)";
 }
 
@@ -522,7 +522,7 @@ def TgammaF128 : Builtin {
 
 def TruncF16F128 : Builtin, F16F128MathTemplate {
   let Spellings = ["__builtin_trunc"];
-  let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const];
+  let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const, Constexpr];
   let Prototype = "T(T)";
 }
 
@@ -3986,6 +3986,7 @@ def Fmod : FPMathTemplate, LibBuiltin<"math.h"> {
   let Attributes = [NoThrow, ConstIgnoringErrnoAndExceptions];
   let Prototype = "T(T, T)";
   let AddBuiltinPrefixedAlias = 1;
+  let OnlyBuiltinPrefixedAliasIsConstexpr = 1;
 }
 
 def Frexp : FPMathTemplate, LibBuiltin<"math.h"> {
@@ -3993,6 +3994,7 @@ def Frexp : FPMathTemplate, LibBuiltin<"math.h"> {
   let Attributes = [NoThrow];
   let Prototype = "T(T, int*)";
   let AddBuiltinPrefixedAlias = 1;
+  let OnlyBuiltinPrefixedAliasIsConstexpr = 1;
 }
 
 def Sincos : FPMathTemplate, GNULibBuiltin<"math.h"> {
@@ -4013,6 +4015,7 @@ def Ldexp : FPMathTemplate, LibBuiltin<"math.h"> {
   let Attributes = [NoThrow, ConstIgnoringErrnoAndExceptions];
   let Prototype = "T(T, int)";
   let AddBuiltinPrefixedAlias = 1;
+  let OnlyBuiltinPrefixedAliasIsConstexpr = 1;
 }
 
 def Modf : FPMathTemplate, LibBuiltin<"math.h"> {
@@ -4020,6 +4023,7 @@ def Modf : FPMathTemplate, LibBuiltin<"math.h"> {
   let Attributes = [NoThrow];
   let Prototype = "T(T, T*)";
   let AddBuiltinPrefixedAlias = 1;
+  let OnlyBuiltinPrefixedAliasIsConstexpr = 1;
 }
 
 def Nan : FPMathTemplate, LibBuiltin<"math.h"> {
@@ -4091,6 +4095,7 @@ def Ceil : FPMathTemplate, LibBuiltin<"math.h"> {
   let Attributes = [NoThrow, Const];
   let Prototype = "T(T)";
   let AddBuiltinPrefixedAlias = 1;
+  let OnlyBuiltinPrefixedAliasIsConstexpr = 1;
 }
 
 def Cos : FPMathTemplate, LibBuiltin<"math.h"> {
@@ -4155,6 +4160,7 @@ def Fdim : FPMathTemplate, LibBuiltin<"math.h"> {
   let Attributes = [NoThrow, ConstIgnoringErrnoAndExceptions];
   let Prototype = "T(T, T)";
   let AddBuiltinPrefixedAlias = 1;
+  let OnlyBuiltinPrefixedAliasIsConstexpr = 1;
 }
 
 def Floor : FPMathTemplate, LibBuiltin<"math.h"> {
@@ -4162,6 +4168,7 @@ def Floor : FPMathTemplate, LibBuiltin<"math.h"> {
   let Attributes = [NoThrow, Const];
   let Prototype = "T(T)";
   let AddBuiltinPrefixedAlias = 1;
+  let OnlyBuiltinPrefixedAliasIsConstexpr = 1;
 }
 
 def Fma : FPMathTemplate, LibBuiltin<"math.h"> {
@@ -4169,6 +4176,7 @@ def Fma : FPMathTemplate, LibBuiltin<"math.h"> {
   let Attributes = [NoThrow, ConstIgnoringErrnoAndExceptions];
   let Prototype = "T(T, T, T)";
   let AddBuiltinPrefixedAlias = 1;
+  let OnlyBuiltinPrefixedAliasIsConstexpr = 1;
 }
 
 def Fmax : FPMathTemplate, LibBuiltin<"math.h"> {
@@ -4215,6 +4223,7 @@ def Ilogb : FPMathTemplate, LibBuiltin<"math.h"> {
   let Attributes = [NoThrow, ConstIgnoringErrnoAndExceptions];
   let Prototype = "int(T)";
   let AddBuiltinPrefixedAlias = 1;
+  let OnlyBuiltinPrefixedAliasIsConstexpr = 1;
 }
 
 def Lgamma : FPMathTemplate, LibBuiltin<"math.h"> {
@@ -4236,6 +4245,7 @@ def Llround : FPMathTemplate, LibBuiltin<"math.h"> {
   let Attributes = [NoThrow, ConstIgnoringErrnoAndExceptions];
   let Prototype = "long long int(T)";
   let AddBuiltinPrefixedAlias = 1;
+  let OnlyBuiltinPrefixedAliasIsConstexpr = 1;
 }
 
 def Log : FPMathTemplate, LibBuiltin<"math.h"> {
@@ -4285,6 +4295,7 @@ def Lround : FPMathTemplate, LibBuiltin<"math.h"> {
   let Attributes = [NoThrow, ConstIgnoringErrnoAndExceptions];
   let Prototype = "long int(T)";
   let AddBuiltinPrefixedAlias = 1;
+  let OnlyBuiltinPrefixedAliasIsConstexpr = 1;
 }
 
 def Nearbyint : FPMathTemplate, LibBuiltin<"math.h"> {
@@ -4299,6 +4310,7 @@ def Nextafter : FPMathTemplate, LibBuiltin<"math.h"> {
   let Attributes = [NoThrow, ConstIgnoringErrnoAndExceptions];
   let Prototype = "T(T, T)";
   let AddBuiltinPrefixedAlias = 1;
+  let OnlyBuiltinPrefixedAliasIsConstexpr = 1;
 }
 
 def Nexttoward : FPMathTemplate, LibBuiltin<"math.h"> {
@@ -4306,6 +4318,7 @@ def Nexttoward : FPMathTemplate, LibBuiltin<"math.h"> {
   let Attributes = [NoThrow, ConstIgnoringErrnoAndExceptions];
   let Prototype = "T(T, long double)";
   let AddBuiltinPrefixedAlias = 1;
+  let OnlyBuiltinPrefixedAliasIsConstexpr = 1;
 }
 
 def Remainder : FPMathTemplate, LibBuiltin<"math.h"> {
@@ -4313,6 +4326,7 @@ def Remainder : FPMathTemplate, LibBuiltin<"math.h"> {
   let Attributes = [NoThrow, ConstIgnoringErrnoAndExceptions];
   let Prototype = "T(T, T)";
   let AddBuiltinPrefixedAlias = 1;
+  let OnlyBuiltinPrefixedAliasIsConstexpr = 1;
 }
 
 def Remquo : FPMathTemplate, LibBuiltin<"math.h"> {
@@ -4320,6 +4334,7 @@ def Remquo : FPMathTemplate, LibBuiltin<"math.h"> {
   let Attributes = [NoThrow];
   let Prototype = "T(T, T, int*)";
   let AddBuiltinPrefixedAlias = 1;
+  let OnlyBuiltinPrefixedAliasIsConstexpr = 1;
 }
 
 def Rint : FPMathTemplate, LibBuiltin<"math.h"> {
@@ -4334,6 +4349,7 @@ def Round : FPMathTemplate, LibBuiltin<"math.h"> {
   let Attributes = [NoThrow, Const];
   let Prototype = "T(T)";
   let AddBuiltinPrefixedAlias = 1;
+  let OnlyBuiltinPrefixedAliasIsConstexpr = 1;
 }
 
 def RoundEven : FPMathTemplate, LibBuiltin<"math.h"> {
@@ -4341,6 +4357,7 @@ def RoundEven : FPMathTemplate, LibBuiltin<"math.h"> {
   let Attributes = [NoThrow, Const];
   let Prototype = "T(T)";
   let AddBuiltinPrefixedAlias = 1;
+  let OnlyBuiltinPrefixedAliasIsConstexpr = 1;
 }
 
 def Scalbln : FPMathTemplate, LibBuiltin<"math.h"> {
@@ -4348,6 +4365,7 @@ def Scalbln : FPMathTemplate, LibBuiltin<"math.h"> {
   let Attributes = [NoThrow, ConstIgnoringErrnoAndExceptions];
   let Prototype = "T(T, long int)";
   let AddBuiltinPrefixedAlias = 1;
+  let OnlyBuiltinPrefixedAliasIsConstexpr = 1;
 }
 
 def Scalbn : FPMathTemplate, LibBuiltin<"math.h"> {
@@ -4355,6 +4373,7 @@ def Scalbn : FPMathTemplate, LibBuiltin<"math.h"> {
   let Attributes = [NoThrow, ConstIgnoringErrnoAndExceptions];
   let Prototype = "T(T, int)";
   let AddBuiltinPrefixedAlias = 1;
+  let OnlyBuiltinPrefixedAliasIsConstexpr = 1;
 }
 
 def Sin : FPMathTemplate, LibBuiltin<"math.h"> {
@@ -4404,6 +4423,7 @@ def Trunc : FPMathTemplate, LibBuiltin<"math.h"> {
   let Attributes = [NoThrow, Const];
   let Prototype = "T(T)";
   let AddBuiltinPrefixedAlias = 1;
+  let OnlyBuiltinPrefixedAliasIsConstexpr = 1;
 }
 
 def Cabs : FPMathTemplate, LibBuiltin<"complex.h"> {
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index 235f1c471f471..3d4930d491ed3 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -88,10 +88,6 @@ bool DiagnoseUninitialized(InterpState &S, CodePtr OpPC, 
bool Extern,
 bool CheckGlobalLoad(InterpState &S, CodePtr OpPC, const Block *B);
 bool CheckLocalLoad(InterpState &S, CodePtr OpPC, const Block *B);
 
-/// Checks if a value can be stored in a block.
-bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
-                bool WillBeActivated = false);
-
 /// Checks if a value can be initialized.
 bool CheckInit(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
 
@@ -401,7 +397,7 @@ inline bool Addf(InterpState &S, CodePtr OpPC, uint32_t 
FPOI) {
 
   FPOptions FPO = FPOptions::getFromOpaqueInt(FPOI);
   Floating Result = S.allocFloat(LHS.getSemantics());
-  auto Status = Floating::add(LHS, RHS, getRoundingMode(FPO), &Result);
+  auto Status = Floating::add(LHS, RHS, getRoundingMode(FPO, 
S.inConstantContext()), &Result);
   S.Stk.push<Floating>(Result);
   return CheckFloatResult(S, OpPC, Result, Status, FPO);
 }
@@ -455,7 +451,7 @@ inline bool Subf(InterpState &S, CodePtr OpPC, uint32_t 
FPOI) {
 
   FPOptions FPO = FPOptions::getFromOpaqueInt(FPOI);
   Floating Result = S.allocFloat(LHS.getSemantics());
-  auto Status = Floating::sub(LHS, RHS, getRoundingMode(FPO), &Result);
+  auto Status = Floating::sub(LHS, RHS, getRoundingMode(FPO, 
S.inConstantContext()), &Result);
   S.Stk.push<Floating>(Result);
   return CheckFloatResult(S, OpPC, Result, Status, FPO);
 }
@@ -481,7 +477,7 @@ inline bool Mulf(InterpState &S, CodePtr OpPC, uint32_t 
FPOI) {
   FPOptions FPO = FPOptions::getFromOpaqueInt(FPOI);
   Floating Result = S.allocFloat(LHS.getSemantics());
 
-  auto Status = Floating::mul(LHS, RHS, getRoundingMode(FPO), &Result);
+  auto Status = Floating::mul(LHS, RHS, getRoundingMode(FPO, 
S.inConstantContext()), &Result);
 
   S.Stk.push<Floating>(Result);
   return CheckFloatResult(S, OpPC, Result, Status, FPO);
@@ -783,7 +779,7 @@ inline bool Divf(InterpState &S, CodePtr OpPC, uint32_t 
FPOI) {
   FPOptions FPO = FPOptions::getFromOpaqueInt(FPOI);
 
   Floating Result = S.allocFloat(LHS.getSemantics());
-  auto Status = Floating::div(LHS, RHS, getRoundingMode(FPO), &Result);
+  auto Status = Floating::div(LHS, RHS, getRoundingMode(FPO, 
S.inConstantContext()), &Result);
 
   S.Stk.push<Floating>(Result);
   return CheckFloatResult(S, OpPC, Result, Status, FPO);
@@ -1105,9 +1101,9 @@ bool IncDecFloatHelper(InterpState &S, CodePtr OpPC, 
const Pointer &Ptr,
   FPOptions FPO = FPOptions::getFromOpaqueInt(FPOI);
   llvm::APFloat::opStatus Status;
   if constexpr (Op == IncDecOp::Inc)
-    Status = Floating::increment(Value, getRoundingMode(FPO), &Result);
+    Status = Floating::increment(Value, getRoundingMode(FPO, 
S.inConstantContext()), &Result);
   else
-    Status = Floating::decrement(Value, getRoundingMode(FPO), &Result);
+    Status = Floating::decrement(Value, getRoundingMode(FPO, 
S.inConstantContext()), &Result);
 
   Ptr.deref<Floating>() = Result;
 
@@ -2829,7 +2825,7 @@ bool CastIntegralFloating(InterpState &S, CodePtr OpPC,
   FPOptions FPO = FPOptions::getFromOpaqueInt(FPOI);
   Floating Result = S.allocFloat(*Sem);
   auto Status =
-      Floating::fromIntegral(FromAP, *Sem, getRoundingMode(FPO), &Result);
+      Floating::fromIntegral(FromAP, *Sem, getRoundingMode(FPO, 
S.inConstantContext()), &Result);
   S.Stk.push<Floating>(Result);
 
   return CheckFloatResult(S, OpPC, Result, Status, FPO);
diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp 
b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index 3e9ce902427eb..47e6f2227c020 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -199,6 +199,79 @@ static llvm::APSInt convertBoolVectorToInt(const Pointer 
&Val) {
   return Result;
 }
 
+static bool CheckFloatResult(InterpState &S, CodePtr OpPC, const CallExpr 
*Call,
+                             const APFloat &Result, APFloat::opStatus Status) {
+  FPOptions FPO = Call->getFPFeaturesInEffect(S.getLangOpts());
+
+  if (!S.inConstantContext()) {
+    if ((Status & APFloat::opInexact) &&
+        FPO.getRoundingMode() == llvm::RoundingMode::Dynamic) {
+      const SourceInfo &E = S.Current->getSource(OpPC);
+      S.FFDiag(E, diag::note_constexpr_dynamic_rounding);
+      return false;
+    }
+
+    if ((Status != APFloat::opOK) &&
+        (FPO.getRoundingMode() == llvm::RoundingMode::Dynamic ||
+         FPO.getExceptionMode() != LangOptions::FPE_Ignore ||
+         FPO.getAllowFEnvAccess())) {
+      const SourceInfo &E = S.Current->getSource(OpPC);
+      S.FFDiag(E, diag::note_constexpr_float_arithmetic_strict);
+      return false;
+    }
+  }
+
+  // If any of the following exceptions were raised, the operation is not a
+  // constant expression.
+  if (Status & (APFloat::opInvalidOp | APFloat::opOverflow |
+                APFloat::opUnderflow | APFloat::opDivByZero)) {
+    if (!S.checkingPotentialConstantExpression()) {
+      const SourceInfo &Loc = S.Current->getSource(OpPC);
+      S.CCEDiag(Loc, diag::note_constexpr_float_arithmetic) << Result.isNaN();
+    }
+    return false;
+  }
+
+  return true;
+}
+
+static bool CheckBuiltinStore(InterpState &S, CodePtr OpPC,
+                              const Pointer &Ptr) {
+  if (!CheckLive(S, OpPC, Ptr, AK_Assign))
+    return false;
+  if (!Ptr.isBlockPointer())
+    return Invalid(S, OpPC);
+  return CheckStore(S, OpPC, Ptr);
+}
+
+static APFloat::opStatus getScalbnStatus(const APFloat &Arg,
+                                         const APFloat &Result, int Exp) {
+  // APFloat::scalbn returns only the scaled value, so derive the status needed
+  // by strict floating-point evaluation.
+  if (!Arg.isFinite() || Arg.isZero())
+    return APFloat::opOK;
+
+  if (Result.isInfinity())
+    return static_cast<APFloat::opStatus>(APFloat::opOverflow |
+                                          APFloat::opInexact);
+  if (Result.isZero())
+    return static_cast<APFloat::opStatus>(APFloat::opUnderflow |
+                                          APFloat::opInexact);
+  if (Exp == std::numeric_limits<int>::min())
+    return static_cast<APFloat::opStatus>(APFloat::opUnderflow |
+                                          APFloat::opInexact);
+
+  APFloat Inverse = scalbn(Result, -Exp, APFloat::rmNearestTiesToEven);
+  if (Inverse.compare(Arg) == APFloat::cmpEqual)
+    return APFloat::opOK;
+
+  if (Result.isDenormal())
+    return static_cast<APFloat::opStatus>(APFloat::opUnderflow |
+                                          APFloat::opInexact);
+  return static_cast<APFloat::opStatus>(APFloat::opOverflow |
+                                        APFloat::opInexact);
+}
+
 // Strict double -> float conversion used for X86 PD2PS/cvtsd2ss intrinsics.
 // Reject NaN/Inf/Subnormal inputs and any lossy/inexact conversions.
 static bool convertDoubleToFloatStrict(APFloat Src, Floating &Dst,
@@ -717,6 +790,361 @@ static inline Floating abs(InterpState &S, const Floating 
&In) {
   return Output;
 }
 
+static bool interp__builtin_roundToIntegral(InterpState &S, CodePtr OpPC,
+                                            const InterpFrame *Frame,
+                                            const CallExpr *Call,
+                                            unsigned BuiltinOp) {
+  const Floating &Val = S.Stk.pop<Floating>();
+  Floating Result = S.allocFloat(Val.getSemantics());
+  APFloat F = Val.getAPFloat();
+
+  llvm::RoundingMode RM;
+  switch (BuiltinOp) {
+  case Builtin::BI__builtin_ceil:
+  case Builtin::BI__builtin_ceilf:
+  case Builtin::BI__builtin_ceill:
+  case Builtin::BI__builtin_ceilf16:
+  case Builtin::BI__builtin_ceilf128:
+    RM = llvm::RoundingMode::TowardPositive;
+    break;
+  case Builtin::BI__builtin_floor:
+  case Builtin::BI__builtin_floorf:
+  case Builtin::BI__builtin_floorl:
+  case Builtin::BI__builtin_floorf16:
+  case Builtin::BI__builtin_floorf128:
+    RM = llvm::RoundingMode::TowardNegative;
+    break;
+  case Builtin::BI__builtin_trunc:
+  case Builtin::BI__builtin_truncf:
+  case Builtin::BI__builtin_truncl:
+  case Builtin::BI__builtin_truncf16:
+  case Builtin::BI__builtin_truncf128:
+    RM = llvm::RoundingMode::TowardZero;
+    break;
+  case Builtin::BI__builtin_round:
+  case Builtin::BI__builtin_roundf:
+  case Builtin::BI__builtin_roundl:
+  case Builtin::BI__builtin_roundf16:
+  case Builtin::BI__builtin_roundf128:
+    RM = llvm::RoundingMode::NearestTiesToAway;
+    break;
+  case Builtin::BI__builtin_roundeven:
+  case Builtin::BI__builtin_roundevenf:
+  case Builtin::BI__builtin_roundevenl:
+  case Builtin::BI__builtin_roundevenf16:
+  case Builtin::BI__builtin_roundevenf128:
+    RM = llvm::RoundingMode::NearestTiesToEven;
+    break;
+  default:
+    llvm_unreachable("invalid builtin ID");
+  }
+
+  // roundToIntegral handles special values (NaN, INF) per IEEE 754.
+  APFloat::opStatus Status = F.roundToIntegral(RM);
+  if (!CheckFloatResult(S, OpPC, Call, F, Status))
+    return false;
+
+  Result.copy(F);
+  S.Stk.push<Floating>(Result);
+  return true;
+}
+
+static bool interp__builtin_fdim(InterpState &S, CodePtr OpPC,
+                                 const InterpFrame *Frame,
+                                 const CallExpr *Call) {
+  const Floating &RHS = S.Stk.pop<Floating>();
+  const Floating &LHS = S.Stk.pop<Floating>();
+  APFloat L = LHS.getAPFloat();
+  APFloat R = RHS.getAPFloat();
+  APFloat Result(L.getSemantics());
+
+  if (L.isNaN()) {
+    Result = L;
+  } else if (R.isNaN()) {
+    Result = R;
+  } else if (L.compare(R) == APFloat::cmpGreaterThan) {
+    llvm::RoundingMode RM =
+        getRoundingMode(Call->getFPFeaturesInEffect(S.getLangOpts()),
+                        S.inConstantContext());
+    APFloat::opStatus Status = L.subtract(R, RM);
+    if (!CheckFloatResult(S, OpPC, Call, L, Status))
+      return false;
+    Result = L;
+  } else {
+    Result = APFloat::getZero(L.getSemantics());
+  }
+
+  Floating F = S.allocFloat(Result.getSemantics());
+  F.copy(Result);
+  S.Stk.push<Floating>(F);
+  return true;
+}
+
+static bool interp__builtin_fma(InterpState &S, CodePtr OpPC,
+                                const InterpFrame *Frame,
+                                const CallExpr *Call) {
+  const Floating &Z = S.Stk.pop<Floating>();
+  const Floating &Y = S.Stk.pop<Floating>();
+  const Floating &X = S.Stk.pop<Floating>();
+  APFloat Result = X.getAPFloat();
+
+  llvm::RoundingMode RM =
+      getRoundingMode(Call->getFPFeaturesInEffect(S.getLangOpts()),
+                      S.inConstantContext());
+
+  // fusedMultiplyAdd handles special values (NaN, INF) per IEEE 754.
+  APFloat::opStatus Status =
+      Result.fusedMultiplyAdd(Y.getAPFloat(), Z.getAPFloat(), RM);
+  if (!CheckFloatResult(S, OpPC, Call, Result, Status))
+    return false;
+
+  Floating F = S.allocFloat(Result.getSemantics());
+  F.copy(Result);
+  S.Stk.push<Floating>(F);
+  return true;
+}
+
+static bool interp__builtin_frexp(InterpState &S, CodePtr OpPC,
+                                  const InterpFrame *Frame,
+                                  const CallExpr *Call) {
+  const Pointer &Ptr = S.Stk.pop<Pointer>();
+  const Floating &Val = S.Stk.pop<Floating>();
+
+  int Exp = 0;
+  // frexp handles special values (NaN, INF) per IEEE 754.
+  APFloat F = frexp(Val.getAPFloat(), Exp, APFloat::rmNearestTiesToEven);
+
+  if (!CheckBuiltinStore(S, OpPC, Ptr))
+    return false;
+
+  QualType ExpType = Call->getArg(1)->getType()->getPointeeType();
+  PrimType ExpT = *S.getContext().classify(ExpType);
+  assignIntegral(S, Ptr, ExpT, APSInt::get(Exp));
+  Ptr.initialize();
+
+  Floating Result = S.allocFloat(F.getSemantics());
+  Result.copy(F);
+  S.Stk.push<Floating>(Result);
+  return true;
+}
+
+static bool interp__builtin_modf(InterpState &S, CodePtr OpPC,
+                                 const InterpFrame *Frame,
+                                 const CallExpr *Call) {
+  const Pointer &Ptr = S.Stk.pop<Pointer>();
+  const Floating &Val = S.Stk.pop<Floating>();
+  const APFloat &F = Val.getAPFloat();
+
+  APFloat Integral = F;
+  Integral.roundToIntegral(APFloat::rmTowardZero);
+
+  if (!CheckBuiltinStore(S, OpPC, Ptr))
+    return false;
+
+  Floating I = S.allocFloat(Integral.getSemantics());
+  I.copy(Integral);
+  Ptr.deref<Floating>() = I;
+  Ptr.initialize();
+
+  if (F.isInfinity()) {
+    Floating Fraction = S.allocFloat(F.getSemantics());
+    Fraction.copy(APFloat::getZero(F.getSemantics(), F.isNegative()));
+    S.Stk.push<Floating>(Fraction);
+    return true;
+  }
+
+  APFloat Fraction = F;
+  if (!Fraction.isZero())
+    Fraction.subtract(Integral, APFloat::rmNearestTiesToEven);
+
+  Floating Result = S.allocFloat(Fraction.getSemantics());
+  Result.copy(Fraction);
+  S.Stk.push<Floating>(Result);
+  return true;
+}
+
+static bool interp__builtin_fmod(InterpState &S, CodePtr OpPC,
+                                 const InterpFrame *Frame, const CallExpr 
*Call,
+                                 unsigned BuiltinOp) {
+  const Floating &RHS = S.Stk.pop<Floating>();
+  const Floating &LHS = S.Stk.pop<Floating>();
+  const APFloat &L = LHS.getAPFloat();
+  const APFloat &R = RHS.getAPFloat();
+  APFloat ResF = L;
+
+  // mod and remainder handle special values (NaN, INF) per IEEE 754.
+  APFloat::opStatus Status;
+  if (BuiltinOp == Builtin::BI__builtin_remainder ||
+      BuiltinOp == Builtin::BI__builtin_remainderf ||
+      BuiltinOp == Builtin::BI__builtin_remainderl ||
+      BuiltinOp == Builtin::BI__builtin_remainderf128)
+    Status = ResF.remainder(R);
+  else
+    Status = ResF.mod(R);
+
+  if (!CheckFloatResult(S, OpPC, Call, ResF, Status))
+    return false;
+
+  Floating F = S.allocFloat(ResF.getSemantics());
+  F.copy(ResF);
+  S.Stk.push<Floating>(F);
+  return true;
+}
+
+static bool interp__builtin_nextafter(InterpState &S, CodePtr OpPC,
+                                      const InterpFrame *Frame,
+                                      const CallExpr *Call) {
+  const Floating &RHS = S.Stk.pop<Floating>();
+  const Floating &LHS = S.Stk.pop<Floating>();
+  const APFloat &L = LHS.getAPFloat();
+  const APFloat &R = RHS.getAPFloat();
+
+  if (L.isNaN()) {
+    S.Stk.push<Floating>(LHS);
+    return true;
+  }
+
+  // nexttoward(x, y) takes y as long double, so if y is NaN we must
+  // convert it to x's semantics before returning.
+  if (R.isNaN()) {
+    bool LoseInfo = false;
+    APFloat NaN = R;
+    NaN.convert(L.getSemantics(), APFloat::rmNearestTiesToEven, &LoseInfo);
+    Floating Result = S.allocFloat(NaN.getSemantics());
+    Result.copy(NaN);
+    S.Stk.push<Floating>(Result);
+    return true;
+  }
+
+  APFloat LCopy = L;
+  bool LoseInfo = false;
+  LCopy.convert(R.getSemantics(), APFloat::rmNearestTiesToEven, &LoseInfo);
+  APFloat::cmpResult Res = LCopy.compare(R);
+
+  APFloat Next = L;
+  bool Stepped = false;
+  if (Res == APFloat::cmpEqual && L.isZero() && R.isZero() &&
+      L.isNegative() != R.isNegative())
+    Next = APFloat::getZero(L.getSemantics(), R.isNegative());
+  else if (Res != APFloat::cmpEqual) {
+    Next.next(Res == APFloat::cmpGreaterThan);
+    Stepped = true;
+  }
+
+  APFloat::opStatus Status = APFloat::opOK;
+  if (Stepped && Next.isInfinity())
+    Status = static_cast<APFloat::opStatus>(APFloat::opOverflow |
+                                            APFloat::opInexact);
+  else if (Stepped && (Next.isDenormal() || Next.isZero()))
+    Status = static_cast<APFloat::opStatus>(APFloat::opUnderflow |
+                                            APFloat::opInexact);
+
+  if (!CheckFloatResult(S, OpPC, Call, Next, Status))
+    return false;
+
+  Floating Result = S.allocFloat(Next.getSemantics());
+  Result.copy(Next);
+  S.Stk.push<Floating>(Result);
+  return true;
+}
+
+static bool interp__builtin_scalbn(InterpState &S, CodePtr OpPC,
+                                   const InterpFrame *Frame,
+                                   const CallExpr *Call) {
+  PrimType ExpT = *S.getContext().classify(Call->getArg(1)->getType());
+  APSInt Exp;
+  if (!popToAPSInt(S.Stk, ExpT, Exp))
+    return false;
+  const Floating &Val = S.Stk.pop<Floating>();
+
+  llvm::RoundingMode RM =
+      getRoundingMode(Call->getFPFeaturesInEffect(S.getLangOpts()),
+                      S.inConstantContext());
+
+  APFloat ValF = Val.getAPFloat();
+  int ExpVal = (int)Exp.getExtValue();
+  APFloat Scaled = scalbn(ValF, ExpVal, RM);
+  APFloat::opStatus Status = getScalbnStatus(ValF, Scaled, ExpVal);
+  if (!CheckFloatResult(S, OpPC, Call, Scaled, Status))
+    return false;
+
+  Floating Result = S.allocFloat(Val.getSemantics());
+  Result.copy(Scaled);
+  S.Stk.push<Floating>(Result);
+  return true;
+}
+
+static bool interp__builtin_ilogb(InterpState &S, CodePtr OpPC,
+                                  const InterpFrame *Frame,
+                                  const CallExpr *Call) {
+  const Floating &Val = S.Stk.pop<Floating>();
+  pushInteger(S, ilogb(Val.getAPFloat()), Call->getType());
+  return true;
+}
+
+static bool interp__builtin_remquo(InterpState &S, CodePtr OpPC,
+                                   const InterpFrame *Frame,
+                                   const CallExpr *Call) {
+  const Pointer &Ptr = S.Stk.pop<Pointer>();
+  const Floating &RHS = S.Stk.pop<Floating>();
+  const Floating &LHS = S.Stk.pop<Floating>();
+
+  APFloat Q = LHS.getAPFloat();
+  Q.divide(RHS.getAPFloat(), APFloat::rmNearestTiesToEven);
+  Q.roundToIntegral(APFloat::rmNearestTiesToEven);
+
+  if (Ptr.isDummy())
+    return false;
+
+  QualType QuoType = Call->getArg(2)->getType()->getPointeeType();
+  APSInt QuoInt(S.getASTContext().getTypeSize(QuoType), /*IsUnsigned=*/false);
+  bool IsExact = false;
+  APFloat::opStatus ConvSt =
+      Q.convertToInteger(QuoInt, APFloat::rmTowardZero, &IsExact);
+  if (ConvSt & APFloat::opInvalidOp)
+    QuoInt = 0;
+
+  PrimType QuoT = *S.getContext().classify(QuoType);
+  assignIntegral(S, Ptr, QuoT, QuoInt);
+  Ptr.initialize();
+
+  APFloat R = LHS.getAPFloat();
+  // remainder handles special values (NaN, INF) per IEEE 754.
+  APFloat::opStatus Status = R.remainder(RHS.getAPFloat());
+  if (!CheckFloatResult(S, OpPC, Call, R, Status))
+    return false;
+
+  Floating Result = S.allocFloat(R.getSemantics());
+  Result.copy(R);
+  S.Stk.push<Floating>(Result);
+  return true;
+}
+
+static bool interp__builtin_lround(InterpState &S, CodePtr OpPC,
+                                   const InterpFrame *Frame,
+                                   const CallExpr *Call) {
+  const Floating &Val = S.Stk.pop<Floating>();
+  APFloat F = Val.getAPFloat();
+
+  llvm::RoundingMode RM = llvm::RoundingMode::NearestTiesToAway;
+
+  // roundToIntegral handles special values (NaN, INF) per IEEE 754.
+  F.roundToIntegral(RM);
+
+  APSInt IntVal(S.getASTContext().getTypeSize(Call->getType()),
+                Call->getType()->isUnsignedIntegerOrEnumerationType());
+  bool IsExact = false;
+  APFloat::opStatus Status = F.convertToInteger(IntVal, RM, &IsExact);
+
+  if (Status & APFloat::opInvalidOp) {
+    auto Loc = S.Current->getSource(OpPC);
+    S.CCEDiag(Loc, diag::note_constexpr_float_arithmetic) << F.isNaN();
+  }
+
+  pushInteger(S, IntVal, Call->getType());
+  return true;
+}
+
 // The C standard says "fabs raises no floating-point exceptions,
 // even if x is a signaling NaN. The returned value is independent of
 // the current rounding direction mode."  Therefore constant folding can
@@ -3052,7 +3480,7 @@ static bool interp_builtin_horizontal_fp_binop(
   const Pointer &LHS = S.Stk.pop<Pointer>();
   const Pointer &Dst = S.Stk.peek<Pointer>();
   FPOptions FPO = Call->getFPFeaturesInEffect(S.Ctx.getLangOpts());
-  llvm::RoundingMode RM = getRoundingMode(FPO);
+  llvm::RoundingMode RM = getRoundingMode(FPO, S.inConstantContext());
   const auto *VT = Call->getArg(0)->getType()->castAs<VectorType>();
 
   unsigned NumElts = VT->getNumElements();
@@ -3087,7 +3515,7 @@ static bool interp__builtin_ia32_addsub(InterpState &S, 
CodePtr OpPC,
   const Pointer &LHS = S.Stk.pop<Pointer>();
   const Pointer &Dst = S.Stk.peek<Pointer>();
   FPOptions FPO = Call->getFPFeaturesInEffect(S.Ctx.getLangOpts());
-  llvm::RoundingMode RM = getRoundingMode(FPO);
+  llvm::RoundingMode RM = getRoundingMode(FPO, S.inConstantContext());
   const auto *VT = Call->getArg(0)->getType()->castAs<VectorType>();
   unsigned NumElems = VT->getNumElements();
 
@@ -3177,7 +3605,7 @@ static bool interp__builtin_elementwise_triop_fp(
   assert(Call->getNumArgs() == 3);
 
   FPOptions FPO = Call->getFPFeaturesInEffect(S.Ctx.getLangOpts());
-  llvm::RoundingMode RM = getRoundingMode(FPO);
+  llvm::RoundingMode RM = getRoundingMode(FPO, S.inConstantContext());
   QualType Arg1Type = Call->getArg(0)->getType();
   QualType Arg2Type = Call->getArg(1)->getType();
   QualType Arg3Type = Call->getArg(2)->getType();
@@ -4536,6 +4964,119 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, 
const CallExpr *Call,
   case Builtin::BI__builtin_issubnormal:
     return interp__builtin_issubnormal(S, OpPC, Frame, Call);
 
+  case Builtin::BI__builtin_lround:
+  case Builtin::BI__builtin_lroundf:
+  case Builtin::BI__builtin_lroundl:
+  case Builtin::BI__builtin_lroundf128:
+  case Builtin::BI__builtin_llround:
+  case Builtin::BI__builtin_llroundf:
+  case Builtin::BI__builtin_llroundl:
+  case Builtin::BI__builtin_llroundf128:
+    return interp__builtin_lround(S, OpPC, Frame, Call);
+
+  case Builtin::BI__builtin_ceil:
+  case Builtin::BI__builtin_ceilf:
+  case Builtin::BI__builtin_ceill:
+  case Builtin::BI__builtin_ceilf16:
+  case Builtin::BI__builtin_ceilf128:
+  case Builtin::BI__builtin_floor:
+  case Builtin::BI__builtin_floorf:
+  case Builtin::BI__builtin_floorl:
+  case Builtin::BI__builtin_floorf16:
+  case Builtin::BI__builtin_floorf128:
+  case Builtin::BI__builtin_trunc:
+  case Builtin::BI__builtin_truncf:
+  case Builtin::BI__builtin_truncl:
+  case Builtin::BI__builtin_truncf16:
+  case Builtin::BI__builtin_truncf128:
+  case Builtin::BI__builtin_roundeven:
+  case Builtin::BI__builtin_roundevenf:
+  case Builtin::BI__builtin_roundevenl:
+  case Builtin::BI__builtin_roundevenf16:
+  case Builtin::BI__builtin_roundevenf128:
+    return interp__builtin_roundToIntegral(S, OpPC, Frame, Call, BuiltinID);
+
+  case Builtin::BI__builtin_fdim:
+  case Builtin::BI__builtin_fdimf:
+  case Builtin::BI__builtin_fdiml:
+  case Builtin::BI__builtin_fdimf128:
+    return interp__builtin_fdim(S, OpPC, Frame, Call);
+
+  case Builtin::BI__builtin_frexp:
+  case Builtin::BI__builtin_frexpf:
+  case Builtin::BI__builtin_frexpl:
+  case Builtin::BI__builtin_frexpf16:
+  case Builtin::BI__builtin_frexpf128:
+    return interp__builtin_frexp(S, OpPC, Frame, Call);
+
+  case Builtin::BI__builtin_modf:
+  case Builtin::BI__builtin_modff:
+  case Builtin::BI__builtin_modfl:
+  case Builtin::BI__builtin_modff128:
+    return interp__builtin_modf(S, OpPC, Frame, Call);
+
+  case Builtin::BI__builtin_fma:
+  case Builtin::BI__builtin_fmaf:
+  case Builtin::BI__builtin_fmal:
+  case Builtin::BI__builtin_fmaf16:
+  case Builtin::BI__builtin_fmaf128:
+    return interp__builtin_fma(S, OpPC, Frame, Call);
+
+  case Builtin::BI__builtin_fmod:
+  case Builtin::BI__builtin_fmodf:
+  case Builtin::BI__builtin_fmodl:
+  case Builtin::BI__builtin_fmodf16:
+  case Builtin::BI__builtin_fmodf128:
+  case Builtin::BI__builtin_remainder:
+  case Builtin::BI__builtin_remainderf:
+  case Builtin::BI__builtin_remainderl:
+  case Builtin::BI__builtin_remainderf128:
+    return interp__builtin_fmod(S, OpPC, Frame, Call, BuiltinID);
+
+  case Builtin::BI__builtin_nextafter:
+  case Builtin::BI__builtin_nextafterf:
+  case Builtin::BI__builtin_nextafterl:
+  case Builtin::BI__builtin_nextafterf128:
+  case Builtin::BI__builtin_nexttoward:
+  case Builtin::BI__builtin_nexttowardf:
+  case Builtin::BI__builtin_nexttowardl:
+  case Builtin::BI__builtin_nexttowardf128:
+    return interp__builtin_nextafter(S, OpPC, Frame, Call);
+
+  case Builtin::BI__builtin_scalbn:
+  case Builtin::BI__builtin_scalbnf:
+  case Builtin::BI__builtin_scalbnl:
+  case Builtin::BI__builtin_scalbnf128:
+  case Builtin::BI__builtin_scalbln:
+  case Builtin::BI__builtin_scalblnf:
+  case Builtin::BI__builtin_scalblnl:
+  case Builtin::BI__builtin_scalblnf128:
+  case Builtin::BI__builtin_ldexp:
+  case Builtin::BI__builtin_ldexpf:
+  case Builtin::BI__builtin_ldexpl:
+  case Builtin::BI__builtin_ldexpf16:
+  case Builtin::BI__builtin_ldexpf128:
+    return interp__builtin_scalbn(S, OpPC, Frame, Call);
+
+  case Builtin::BI__builtin_ilogb:
+  case Builtin::BI__builtin_ilogbf:
+  case Builtin::BI__builtin_ilogbl:
+  case Builtin::BI__builtin_ilogbf128:
+    return interp__builtin_ilogb(S, OpPC, Frame, Call);
+
+  case Builtin::BI__builtin_remquo:
+  case Builtin::BI__builtin_remquof:
+  case Builtin::BI__builtin_remquol:
+  case Builtin::BI__builtin_remquof128:
+    return interp__builtin_remquo(S, OpPC, Frame, Call);
+
+  case Builtin::BI__builtin_round:
+  case Builtin::BI__builtin_roundf:
+  case Builtin::BI__builtin_roundl:
+  case Builtin::BI__builtin_roundf16:
+  case Builtin::BI__builtin_roundf128:
+    return interp__builtin_roundToIntegral(S, OpPC, Frame, Call, BuiltinID);
+
   case Builtin::BI__builtin_iszero:
     return interp__builtin_iszero(S, OpPC, Frame, Call);
 
diff --git a/clang/lib/AST/ByteCode/InterpHelpers.h 
b/clang/lib/AST/ByteCode/InterpHelpers.h
index 905bf1b43bfab..aaf8c5a26eec5 100644
--- a/clang/lib/AST/ByteCode/InterpHelpers.h
+++ b/clang/lib/AST/ByteCode/InterpHelpers.h
@@ -57,6 +57,10 @@ bool CheckMutable(InterpState &S, CodePtr OpPC, const 
Pointer &Ptr);
 bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
                AccessKinds AK = AK_Read);
 
+/// Checks if a value can be stored in a block.
+bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
+                bool WillBeActivated = false);
+
 /// Diagnose mismatched new[]/delete or new/delete[] pairs.
 bool CheckNewDeleteForms(InterpState &S, CodePtr OpPC,
                          DynamicAllocator::Form AllocForm,
@@ -87,7 +91,9 @@ inline bool CheckArraySize(InterpState &S, CodePtr OpPC, 
uint64_t NumElems) {
   return true;
 }
 
-static inline llvm::RoundingMode getRoundingMode(FPOptions FPO) {
+static inline llvm::RoundingMode getRoundingMode(FPOptions FPO, bool 
InConstantContext) {
+  if (!InConstantContext && !FPO.getAllowFEnvAccess())
+    return llvm::RoundingMode::NearestTiesToEven;
   auto RM = FPO.getRoundingMode();
   if (RM == llvm::RoundingMode::Dynamic)
     return llvm::RoundingMode::NearestTiesToEven;
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 38aa5798cfeb9..19156aac6d663 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -2660,8 +2660,10 @@ static bool HandleFloatToIntCast(EvalInfo &Info, const 
Expr *E,
 /// expression. If the result is exact, it does not depend on rounding mode.
 /// So return "tonearest" mode instead of "dynamic".
 static llvm::RoundingMode getActiveRoundingMode(EvalInfo &Info, const Expr *E) 
{
-  llvm::RoundingMode RM =
-      E->getFPFeaturesInEffect(Info.getLangOpts()).getRoundingMode();
+  FPOptions FPO = E->getFPFeaturesInEffect(Info.getLangOpts());
+  if (!Info.InConstantContext && !FPO.getAllowFEnvAccess())
+    return llvm::RoundingMode::NearestTiesToEven;
+  llvm::RoundingMode RM = FPO.getRoundingMode();
   if (RM == llvm::RoundingMode::Dynamic)
     RM = llvm::RoundingMode::NearestTiesToEven;
   return RM;
@@ -2708,6 +2710,34 @@ static bool checkFloatingPointResult(EvalInfo &Info, 
const Expr *E,
   return true;
 }
 
+static APFloat::opStatus getScalbnStatus(const APFloat &Arg,
+                                         const APFloat &Result, int Exp) {
+  // APFloat::scalbn returns only the scaled value, so derive the status needed
+  // by strict floating-point evaluation.
+  if (!Arg.isFinite() || Arg.isZero())
+    return APFloat::opOK;
+
+  if (Result.isInfinity())
+    return static_cast<APFloat::opStatus>(APFloat::opOverflow |
+                                          APFloat::opInexact);
+  if (Result.isZero())
+    return static_cast<APFloat::opStatus>(APFloat::opUnderflow |
+                                          APFloat::opInexact);
+  if (Exp == std::numeric_limits<int>::min())
+    return static_cast<APFloat::opStatus>(APFloat::opUnderflow |
+                                          APFloat::opInexact);
+
+  APFloat Inverse = scalbn(Result, -Exp, APFloat::rmNearestTiesToEven);
+  if (Inverse.compare(Arg) == APFloat::cmpEqual)
+    return APFloat::opOK;
+
+  if (Result.isDenormal())
+    return static_cast<APFloat::opStatus>(APFloat::opUnderflow |
+                                          APFloat::opInexact);
+  return static_cast<APFloat::opStatus>(APFloat::opOverflow |
+                                        APFloat::opInexact);
+}
+
 static bool HandleFloatToFloatCast(EvalInfo &Info, const Expr *E,
                                    QualType SrcType, QualType DestType,
                                    APFloat &Result) {
@@ -16396,6 +16426,34 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const 
CallExpr *E,
   };
 
   switch (BuiltinOp) {
+  case Builtin::BI__builtin_lround:
+  case Builtin::BI__builtin_lroundf:
+  case Builtin::BI__builtin_lroundl:
+  case Builtin::BI__builtin_lroundf128:
+  case Builtin::BI__builtin_llround:
+  case Builtin::BI__builtin_llroundf:
+  case Builtin::BI__builtin_llroundl:
+  case Builtin::BI__builtin_llroundf128: {
+    APFloat FloatVal(0.0);
+    if (!EvaluateFloat(E->getArg(0), FloatVal, Info))
+      return false;
+
+    llvm::RoundingMode RM = llvm::RoundingMode::NearestTiesToAway;
+
+    FloatVal.roundToIntegral(RM);
+
+    APSInt IntVal(Info.Ctx.getTypeSize(E->getType()),
+                  E->getType()->isUnsignedIntegerOrEnumerationType());
+    bool IsExact = false;
+    APFloat::opStatus Status = FloatVal.convertToInteger(IntVal, RM, &IsExact);
+
+    if (Status & APFloat::opInvalidOp)
+      Info.CCEDiag(E, diag::note_constexpr_float_arithmetic)
+          << (FloatVal.isNaN() ? 1 : 0);
+
+    return Success(IntVal, E);
+  }
+
   default:
     return false;
 
@@ -16865,6 +16923,22 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const 
CallExpr *E,
     return Success(Val.popcount() % 2, E);
   }
 
+  case Builtin::BI__builtin_ilogb:
+  case Builtin::BI__builtin_ilogbf:
+  case Builtin::BI__builtin_ilogbl:
+  case Builtin::BI__builtin_ilogbf128: {
+    APFloat FloatVal(0.0);
+    if (!EvaluateFloat(E->getArg(0), FloatVal, Info))
+      return false;
+
+    if (FloatVal.isZero() || FloatVal.isNaN() || FloatVal.isInfinity()) {
+      if (!checkFloatingPointResult(Info, E, APFloat::opInvalidOp))
+        return false;
+    }
+
+    return Success(ilogb(FloatVal), E);
+  }
+
   case Builtin::BI__builtin_abs:
   case Builtin::BI__builtin_labs:
   case Builtin::BI__builtin_llabs: {
@@ -19946,6 +20020,7 @@ bool FloatExprEvaluator::VisitCallExpr(const CallExpr 
*E) {
     if (!EvaluateFloat(E->getArg(0), Result, Info) ||
         !EvaluateFloat(E->getArg(1), RHS, Info))
       return false;
+    // maxnum handle special values (NaN, INF) per IEEE 754.
     Result = maxnum(Result, RHS);
     return true;
   }
@@ -19959,6 +20034,7 @@ bool FloatExprEvaluator::VisitCallExpr(const CallExpr 
*E) {
     if (!EvaluateFloat(E->getArg(0), Result, Info) ||
         !EvaluateFloat(E->getArg(1), RHS, Info))
       return false;
+    // minnum handle special values (NaN, INF) per IEEE 754.
     Result = minnum(Result, RHS);
     return true;
   }
@@ -19972,6 +20048,7 @@ bool FloatExprEvaluator::VisitCallExpr(const CallExpr 
*E) {
     if (!EvaluateFloat(E->getArg(0), Result, Info) ||
         !EvaluateFloat(E->getArg(1), RHS, Info))
       return false;
+    // maximumnum handle special values (NaN, INF) per IEEE 754.
     Result = maximumnum(Result, RHS);
     return true;
   }
@@ -19985,10 +20062,291 @@ bool FloatExprEvaluator::VisitCallExpr(const 
CallExpr *E) {
     if (!EvaluateFloat(E->getArg(0), Result, Info) ||
         !EvaluateFloat(E->getArg(1), RHS, Info))
       return false;
+    // minimumnum handles special values (NaN, INF) per IEEE 754.
     Result = minimumnum(Result, RHS);
     return true;
   }
 
+  case Builtin::BI__builtin_round:
+  case Builtin::BI__builtin_roundf:
+  case Builtin::BI__builtin_roundl:
+  case Builtin::BI__builtin_roundf16:
+  case Builtin::BI__builtin_roundf128:
+  case Builtin::BI__builtin_ceil:
+  case Builtin::BI__builtin_ceilf:
+  case Builtin::BI__builtin_ceill:
+  case Builtin::BI__builtin_ceilf16:
+  case Builtin::BI__builtin_ceilf128:
+  case Builtin::BI__builtin_floor:
+  case Builtin::BI__builtin_floorf:
+  case Builtin::BI__builtin_floorl:
+  case Builtin::BI__builtin_floorf16:
+  case Builtin::BI__builtin_floorf128:
+  case Builtin::BI__builtin_trunc:
+  case Builtin::BI__builtin_truncf:
+  case Builtin::BI__builtin_truncl:
+  case Builtin::BI__builtin_truncf16:
+  case Builtin::BI__builtin_truncf128:
+  case Builtin::BI__builtin_roundeven:
+  case Builtin::BI__builtin_roundevenf:
+  case Builtin::BI__builtin_roundevenl:
+  case Builtin::BI__builtin_roundevenf16:
+  case Builtin::BI__builtin_roundevenf128: {
+    if (!EvaluateFloat(E->getArg(0), Result, Info))
+      return false;
+    llvm::RoundingMode RM;
+    switch (E->getBuiltinCallee()) {
+
+    case Builtin::BI__builtin_round:
+    case Builtin::BI__builtin_roundf:
+    case Builtin::BI__builtin_roundl:
+    case Builtin::BI__builtin_roundf16:
+    case Builtin::BI__builtin_roundf128:
+      RM = llvm::RoundingMode::NearestTiesToAway;
+      break;
+    case Builtin::BI__builtin_ceil:
+    case Builtin::BI__builtin_ceilf:
+    case Builtin::BI__builtin_ceill:
+    case Builtin::BI__builtin_ceilf16:
+    case Builtin::BI__builtin_ceilf128:
+      RM = llvm::RoundingMode::TowardPositive;
+      break;
+    case Builtin::BI__builtin_floor:
+    case Builtin::BI__builtin_floorf:
+    case Builtin::BI__builtin_floorl:
+    case Builtin::BI__builtin_floorf16:
+    case Builtin::BI__builtin_floorf128:
+      RM = llvm::RoundingMode::TowardNegative;
+      break;
+    case Builtin::BI__builtin_roundeven:
+    case Builtin::BI__builtin_roundevenf:
+    case Builtin::BI__builtin_roundevenl:
+    case Builtin::BI__builtin_roundevenf16:
+    case Builtin::BI__builtin_roundevenf128:
+      RM = llvm::RoundingMode::NearestTiesToEven;
+      break;
+    default:
+      RM = llvm::RoundingMode::TowardZero;
+      break;
+    }
+    // roundToIntegral handles special values (NaN, INF) per IEEE 754.
+    APFloat::opStatus St = Result.roundToIntegral(RM);
+    return checkFloatingPointResult(Info, E, St);
+  }
+
+  case Builtin::BI__builtin_fdim:
+  case Builtin::BI__builtin_fdimf:
+  case Builtin::BI__builtin_fdiml:
+  case Builtin::BI__builtin_fdimf128: {
+    APFloat RHS(0.);
+    if (!EvaluateFloat(E->getArg(0), Result, Info) ||
+        !EvaluateFloat(E->getArg(1), RHS, Info))
+      return false;
+    if (Result.compare(RHS) == APFloat::cmpGreaterThan) {
+      llvm::RoundingMode RM = getActiveRoundingMode(getEvalInfo(), E);
+      APFloat::opStatus St = Result.subtract(RHS, RM);
+      return checkFloatingPointResult(Info, E, St);
+    } else if (Result.isNaN()) {
+      // Result is already the NaN with its payload preserved.
+    } else if (RHS.isNaN()) {
+      Result = RHS;
+    } else {
+      Result = APFloat::getZero(Result.getSemantics());
+    }
+    return true;
+  }
+
+  case Builtin::BI__builtin_fma:
+  case Builtin::BI__builtin_fmaf:
+  case Builtin::BI__builtin_fmal:
+  case Builtin::BI__builtin_fmaf16:
+  case Builtin::BI__builtin_fmaf128: {
+    APFloat RHS(0.), Third(0.);
+    if (!EvaluateFloat(E->getArg(0), Result, Info) ||
+        !EvaluateFloat(E->getArg(1), RHS, Info) ||
+        !EvaluateFloat(E->getArg(2), Third, Info))
+      return false;
+
+    llvm::RoundingMode RM = getActiveRoundingMode(getEvalInfo(), E);
+    // fusedMultiplyAdd handles special values (NaN, INF) per IEEE 754.
+    APFloat::opStatus St = Result.fusedMultiplyAdd(RHS, Third, RM);
+    return checkFloatingPointResult(Info, E, St);
+  }
+
+  case Builtin::BI__builtin_fmod:
+  case Builtin::BI__builtin_fmodf:
+  case Builtin::BI__builtin_fmodl:
+  case Builtin::BI__builtin_fmodf16:
+  case Builtin::BI__builtin_fmodf128: {
+    APFloat RHS(0.);
+    if (!EvaluateFloat(E->getArg(0), Result, Info) ||
+        !EvaluateFloat(E->getArg(1), RHS, Info))
+      return false;
+    // mod handles special values (NaN, INF) per IEEE 754.
+    APFloat::opStatus St = Result.mod(RHS);
+    return checkFloatingPointResult(Info, E, St);
+  }
+
+  case Builtin::BI__builtin_remainder:
+  case Builtin::BI__builtin_remainderf:
+  case Builtin::BI__builtin_remainderl:
+  case Builtin::BI__builtin_remainderf128: {
+    APFloat RHS(0.);
+    if (!EvaluateFloat(E->getArg(0), Result, Info) ||
+        !EvaluateFloat(E->getArg(1), RHS, Info))
+      return false;
+    // remainder handles special values (NaN, INF) per IEEE 754.
+    APFloat::opStatus St = Result.remainder(RHS);
+    return checkFloatingPointResult(Info, E, St);
+  }
+
+  case Builtin::BI__builtin_nextafter:
+  case Builtin::BI__builtin_nextafterf:
+  case Builtin::BI__builtin_nextafterl:
+  case Builtin::BI__builtin_nextafterf128:
+  case Builtin::BI__builtin_nexttoward:
+  case Builtin::BI__builtin_nexttowardf:
+  case Builtin::BI__builtin_nexttowardl:
+  case Builtin::BI__builtin_nexttowardf128: {
+    APFloat RHS(0.);
+    if (!EvaluateFloat(E->getArg(0), Result, Info) ||
+        !EvaluateFloat(E->getArg(1), RHS, Info))
+      return false;
+
+    if (Result.isNaN())
+      return true;
+
+    if (RHS.isNaN()) {
+      bool LoseInfo = false;
+      Result = RHS;
+      Result.convert(Info.Ctx.getFloatTypeSemantics(E->getType()),
+                     APFloat::rmNearestTiesToEven, &LoseInfo);
+      return true;
+    }
+
+    APFloat ResultCopy = Result;
+    bool LoseInfo = false;
+    ResultCopy.convert(RHS.getSemantics(), APFloat::rmNearestTiesToEven,
+                       &LoseInfo);
+    APFloat::cmpResult Res = ResultCopy.compare(RHS);
+
+    if (Res == APFloat::cmpEqual) {
+      if (Result.isZero() && RHS.isZero() &&
+          Result.isNegative() != RHS.isNegative())
+        Result = APFloat::getZero(Result.getSemantics(), RHS.isNegative());
+      return true;
+    }
+
+    Result.next(Res == APFloat::cmpGreaterThan);
+    return true;
+  }
+
+  case Builtin::BI__builtin_scalbn:
+  case Builtin::BI__builtin_scalbnf:
+  case Builtin::BI__builtin_scalbnl:
+  case Builtin::BI__builtin_scalbnf128:
+  case Builtin::BI__builtin_scalbln:
+  case Builtin::BI__builtin_scalblnf:
+  case Builtin::BI__builtin_scalblnl:
+  case Builtin::BI__builtin_scalblnf128:
+  case Builtin::BI__builtin_ldexp:
+  case Builtin::BI__builtin_ldexpf:
+  case Builtin::BI__builtin_ldexpl:
+  case Builtin::BI__builtin_ldexpf16:
+  case Builtin::BI__builtin_ldexpf128: {
+    APSInt Exp;
+    if (!EvaluateFloat(E->getArg(0), Result, Info) ||
+        !EvaluateInteger(E->getArg(1), Exp, Info))
+      return false;
+
+    llvm::RoundingMode RM = getActiveRoundingMode(getEvalInfo(), E);
+    APFloat Arg = Result;
+    int ExpVal = (int)Exp.getExtValue();
+    Result = scalbn(Result, ExpVal, RM);
+    APFloat::opStatus St = getScalbnStatus(Arg, Result, ExpVal);
+    return checkFloatingPointResult(Info, E, St);
+  }
+
+  case Builtin::BI__builtin_frexp:
+  case Builtin::BI__builtin_frexpf:
+  case Builtin::BI__builtin_frexpl:
+  case Builtin::BI__builtin_frexpf16:
+  case Builtin::BI__builtin_frexpf128: {
+    LValue ExpLVal;
+    if (!EvaluateFloat(E->getArg(0), Result, Info) ||
+        !EvaluatePointer(E->getArg(1), ExpLVal, Info))
+      return false;
+
+    int Exp = 0;
+    // frexp handles special values (NaN, INF) per IEEE 754.
+    Result = frexp(Result, Exp, APFloat::rmNearestTiesToEven);
+
+    QualType PointeeType = E->getArg(1)->getType()->getPointeeType();
+    APValue APV{APSInt(Info.Ctx.getTypeSize(PointeeType), false)};
+    APV.getInt() = Exp;
+    if (!handleAssignment(Info, E, ExpLVal, PointeeType, APV))
+      return false;
+    return true;
+  }
+
+  case Builtin::BI__builtin_modf:
+  case Builtin::BI__builtin_modff:
+  case Builtin::BI__builtin_modfl:
+  case Builtin::BI__builtin_modff128: {
+    LValue IptrLVal;
+    if (!EvaluateFloat(E->getArg(0), Result, Info) ||
+        !EvaluatePointer(E->getArg(1), IptrLVal, Info))
+      return false;
+
+    APFloat Integral = Result;
+    Integral.roundToIntegral(APFloat::rmTowardZero);
+
+    QualType PointeeType = E->getArg(1)->getType()->getPointeeType();
+    APValue APV{Integral};
+    if (!handleAssignment(Info, E, IptrLVal, PointeeType, APV))
+      return false;
+
+    if (Result.isInfinity())
+      Result = APFloat::getZero(Result.getSemantics(), Result.isNegative());
+    else if (!Result.isZero())
+      Result.subtract(Integral, APFloat::rmNearestTiesToEven);
+    return true;
+  }
+
+  case Builtin::BI__builtin_remquo:
+  case Builtin::BI__builtin_remquof:
+  case Builtin::BI__builtin_remquol:
+  case Builtin::BI__builtin_remquof128: {
+    APFloat RHS(0.);
+    LValue QuoLVal;
+    if (!EvaluateFloat(E->getArg(0), Result, Info) ||
+        !EvaluateFloat(E->getArg(1), RHS, Info) ||
+        !EvaluatePointer(E->getArg(2), QuoLVal, Info))
+      return false;
+
+    APFloat Q = Result;
+    Q.divide(RHS, APFloat::rmNearestTiesToEven);
+    Q.roundToIntegral(APFloat::rmNearestTiesToEven);
+
+    APSInt QuoInt(
+        Info.Ctx.getTypeSize(E->getArg(2)->getType()->getPointeeType()), 
false);
+    bool IsExact = false;
+
+    APFloat::opStatus ConvSt =
+        Q.convertToInteger(QuoInt, APFloat::rmTowardZero, &IsExact);
+    if (ConvSt & APFloat::opInvalidOp)
+      QuoInt = 0;
+
+    APValue APV{QuoInt};
+    if (!handleAssignment(Info, E, QuoLVal,
+                          E->getArg(2)->getType()->getPointeeType(), APV))
+      return false;
+
+    // remainder handles special values (NaN, INF) per IEEE 754.
+    APFloat::opStatus St = Result.remainder(RHS);
+    return checkFloatingPointResult(Info, E, St);
+  }
+
   case Builtin::BI__builtin_elementwise_fma: {
     if (!E->getArg(0)->isPRValue() || !E->getArg(1)->isPRValue() ||
         !E->getArg(2)->isPRValue()) {
diff --git a/clang/test/CodeGen/builtins-fenv-access.c 
b/clang/test/CodeGen/builtins-fenv-access.c
new file mode 100644
index 0000000000000..2edadeb8c75c4
--- /dev/null
+++ b/clang/test/CodeGen/builtins-fenv-access.c
@@ -0,0 +1,76 @@
+// RUN: %clang_cc1 -fexperimental-strict-floating-point 
-ffp-exception-behavior=strict -triple x86_64-linux-gnu -emit-llvm %s -o - | 
FileCheck %s
+// RUN: %clang_cc1 -fexperimental-strict-floating-point 
-ffp-exception-behavior=strict -triple x86_64-linux-gnu 
-fexperimental-new-constant-interpreter -emit-llvm %s -o - | FileCheck %s
+
+#pragma STDC FENV_ACCESS ON
+
+// CHECK-LABEL: define{{.*}} double @test_ceil_inexact(
+// CHECK: call double @llvm.experimental.constrained.ceil.f64(
+double test_ceil_inexact(void) { return __builtin_ceil(2.1); }
+
+// CHECK-LABEL: define{{.*}} double @test_ceil_exact(
+// CHECK: ret double 2.000000e+00
+double test_ceil_exact(void) { return __builtin_ceil(2.0); }
+
+// CHECK-LABEL: define{{.*}} double @test_floor_inexact(
+// CHECK: call double @llvm.experimental.constrained.floor.f64(
+double test_floor_inexact(void) { return __builtin_floor(2.1); }
+
+// CHECK-LABEL: define{{.*}} double @test_floor_exact(
+// CHECK: ret double 2.000000e+00
+double test_floor_exact(void) { return __builtin_floor(2.0); }
+
+// CHECK-LABEL: define{{.*}} double @test_trunc_inexact(
+// CHECK: call double @llvm.experimental.constrained.trunc.f64(
+double test_trunc_inexact(void) { return __builtin_trunc(2.1); }
+
+// CHECK-LABEL: define{{.*}} double @test_trunc_exact(
+// CHECK: ret double 2.000000e+00
+double test_trunc_exact(void) { return __builtin_trunc(2.0); }
+
+// CHECK-LABEL: define{{.*}} double @test_round_inexact(
+// CHECK: call double @llvm.experimental.constrained.round.f64(
+double test_round_inexact(void) { return __builtin_round(2.1); }
+
+// CHECK-LABEL: define{{.*}} double @test_round_exact(
+// CHECK: ret double 2.000000e+00
+double test_round_exact(void) { return __builtin_round(2.0); }
+
+// CHECK-LABEL: define{{.*}} double @test_roundeven_inexact(
+// CHECK: call double @llvm.experimental.constrained.roundeven.f64(
+double test_roundeven_inexact(void) { return __builtin_roundeven(2.1); }
+
+// CHECK-LABEL: define{{.*}} double @test_roundeven_exact(
+// CHECK: ret double 2.000000e+00
+double test_roundeven_exact(void) { return __builtin_roundeven(2.0); }
+
+// CHECK-LABEL: define{{.*}} double @test_fdim_inexact(
+// CHECK: call double @fdim(
+double test_fdim_inexact(void) { return __builtin_fdim(3.0, 0.1); }
+
+// CHECK-LABEL: define{{.*}} double @test_fdim_exact(
+// CHECK: ret double 2.000000e+00
+double test_fdim_exact(void) { return __builtin_fdim(3.0, 1.0); }
+
+// CHECK-LABEL: define{{.*}} double @test_fma_inexact(
+// CHECK: call double @llvm.experimental.constrained.fma.f64(
+double test_fma_inexact(void) { return __builtin_fma(3.0, 0.1, 0.01); }
+
+// CHECK-LABEL: define{{.*}} double @test_fma_exact(
+// CHECK: ret double 1.000000e+01
+double test_fma_exact(void) { return __builtin_fma(2.0, 3.0, 4.0); }
+
+// CHECK-LABEL: define{{.*}} double @test_scalbn_inexact(
+// CHECK: call double @scalbn(
+double test_scalbn_inexact(void) { return __builtin_scalbn(__DBL_MAX__, 1); }
+
+// CHECK-LABEL: define{{.*}} double @test_scalbn_exact(
+// CHECK: ret double 4.000000e+00
+double test_scalbn_exact(void) { return __builtin_scalbn(2.0, 1); }
+
+// CHECK-LABEL: define{{.*}} double @test_ldexp_inexact(
+// CHECK: call double @llvm.experimental.constrained.ldexp.f64.i32(
+double test_ldexp_inexact(void) { return __builtin_ldexp(__DBL_MAX__, 1); }
+
+// CHECK-LABEL: define{{.*}} double @test_ldexp_exact(
+// CHECK: ret double 4.000000e+00
+double test_ldexp_exact(void) { return __builtin_ldexp(2.0, 1); }
diff --git a/clang/test/CodeGen/builtins-fenv-round.c 
b/clang/test/CodeGen/builtins-fenv-round.c
new file mode 100644
index 0000000000000..054c6eb7d019d
--- /dev/null
+++ b/clang/test/CodeGen/builtins-fenv-round.c
@@ -0,0 +1,22 @@
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm %s -o - 
-Wno-unknown-pragmas | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-linux-gnu 
-fexperimental-new-constant-interpreter -emit-llvm %s -o - -Wno-unknown-pragmas 
| FileCheck %s
+
+#pragma STDC FENV_ROUND FE_TONEARESTFROMZERO
+
+// CHECK-LABEL: define{{.*}} double @test_fdim(
+// CHECK: ret double 1.000000e+00
+double test_fdim(void) {
+  return __builtin_fdim(__DBL_EPSILON__ / 2., -1.);
+}
+
+// CHECK-LABEL: define{{.*}} double @test_fma(
+// CHECK: ret double 1.000000e+00
+double test_fma(void) {
+  return __builtin_fma(0.5, __DBL_EPSILON__, 1.0);
+}
+
+// CHECK-LABEL: define{{.*}} double @test_scalbn(
+// CHECK: ret double 1.000000e+00
+double test_scalbn(void) {
+  return __builtin_scalbn(1.0 + __DBL_EPSILON__ / 2., 0);
+}
diff --git a/clang/test/SemaCXX/constexpr-cmath-builtins.cpp 
b/clang/test/SemaCXX/constexpr-cmath-builtins.cpp
new file mode 100644
index 0000000000000..a316d9e63c4d3
--- /dev/null
+++ b/clang/test/SemaCXX/constexpr-cmath-builtins.cpp
@@ -0,0 +1,311 @@
+// RUN: %clang_cc1 -fexperimental-new-constant-interpreter 
-verify=expected,new -std=c++20 %s
+// RUN: %clang_cc1 -verify=expected -std=c++20 %s
+
+constexpr bool isNegativeZero(double x) {
+  return x == 0.0 && __builtin_copysign(1.0, x) < 0.0;
+}
+constexpr bool isPositiveZero(double x) {
+  return x == 0.0 && __builtin_copysign(1.0, x) > 0.0;
+}
+
+// round tests
+static_assert(__builtin_round(1.1) == 1.0);
+static_assert(__builtin_round(1.5) == 2.0);
+static_assert(__builtin_round(1.9) == 2.0);
+static_assert(__builtin_round(-1.5) == -2.0);
+static_assert(__builtin_roundf16(1.5f16) == 2.0f16);
+static_assert(__builtin_roundf128(1.5) == 2.0);
+static_assert(__builtin_isnan(__builtin_round(__builtin_nan(""))));
+static_assert(__builtin_isinf(__builtin_round(__builtin_inf())));
+
+// lround tests
+static_assert(__builtin_lround(1.1) == 1);
+static_assert(__builtin_lround(1.5) == 2);
+static_assert(__builtin_lround(-1.5) == -2);
+static_assert(__builtin_lroundf128(1.5) == 2);
+
+// llround tests
+static_assert(__builtin_llround(1.1) == 1LL);
+static_assert(__builtin_llround(1.5) == 2LL);
+static_assert(__builtin_llround(-1.5) == -2LL);
+static_assert(__builtin_llroundf128(1.5) == 2LL);
+
+// ceil tests
+static_assert(__builtin_ceil(1.1) == 2.0);
+static_assert(__builtin_ceil(-1.1) == -1.0);
+static_assert(__builtin_ceilf(1.1f) == 2.0f);
+static_assert(__builtin_ceilf16(1.1f16) == 2.0f16);
+static_assert(__builtin_ceilf128(1.1) == 2.0);
+static_assert(__builtin_isnan(__builtin_ceil(__builtin_nan(""))));
+static_assert(__builtin_isinf(__builtin_ceil(__builtin_inf())));
+
+// floor tests
+static_assert(__builtin_floor(1.1) == 1.0);
+static_assert(__builtin_floor(-1.1) == -2.0);
+static_assert(__builtin_floorf(1.1f) == 1.0f);
+static_assert(__builtin_floorf16(1.1f16) == 1.0f16);
+static_assert(__builtin_floorf128(1.1) == 1.0);
+static_assert(__builtin_isnan(__builtin_floor(__builtin_nan(""))));
+static_assert(__builtin_isinf(__builtin_floor(__builtin_inf())));
+
+// trunc tests
+static_assert(__builtin_trunc(1.1) == 1.0);
+static_assert(__builtin_trunc(-1.1) == -1.0);
+static_assert(__builtin_truncf(1.1f) == 1.0f);
+static_assert(__builtin_truncf16(1.1f16) == 1.0f16);
+static_assert(__builtin_truncf128(1.1) == 1.0);
+static_assert(__builtin_isnan(__builtin_trunc(__builtin_nan(""))));
+static_assert(__builtin_isinf(__builtin_trunc(__builtin_inf())));
+
+// roundeven tests
+static_assert(__builtin_roundeven(1.5) == 2.0);
+static_assert(__builtin_roundeven(2.5) == 2.0);
+static_assert(__builtin_roundeven(-1.5) == -2.0);
+static_assert(__builtin_roundeven(-2.5) == -2.0);
+static_assert(__builtin_roundevenf(1.5f) == 2.0f);
+static_assert(__builtin_roundevenl(1.5l) == 2.0l);
+static_assert(__builtin_roundevenf128(1.5) == 2.0);
+static_assert(__builtin_roundeven(0.5) == 0.0);
+static_assert(isNegativeZero(__builtin_roundeven(-0.5)));
+static_assert(__builtin_isnan(__builtin_roundeven(__builtin_nan(""))));
+static_assert(__builtin_isinf(__builtin_roundeven(__builtin_inf())));
+
+// fdim tests
+static_assert(__builtin_fdim(3.0, 1.0) == 2.0);
+static_assert(__builtin_fdim(1.0, 3.0) == 0.0);
+static_assert(__builtin_fdimf(3.0f, 1.0f) == 2.0f);
+static_assert(__builtin_fdimf128(3.0, 1.0) == 2.0);
+static_assert(__builtin_isnan(__builtin_fdim(__builtin_nan(""), 1.0)));
+static_assert(__builtin_isnan(__builtin_fdim(1.0, __builtin_nan(""))));
+static_assert(__builtin_isinf(__builtin_fdim(__builtin_inf(), 0.0)));
+static_assert(__builtin_fdim(__builtin_inf(), __builtin_inf()) == 0.0);
+
+// fma tests
+static_assert(__builtin_fma(2.0, 3.0, 4.0) == 10.0);
+static_assert(__builtin_fmaf(2.0f, 3.0f, 4.0f) == 10.0f);
+static_assert(__builtin_fmaf16(2.0f16, 3.0f16, 4.0f16) == 10.0f16);
+static_assert(__builtin_fmaf128(2.0, 3.0, 4.0) == 10.0);
+static_assert(__builtin_isnan(__builtin_fma(__builtin_nan(""), 2.0, 3.0)));
+static_assert(__builtin_isnan(__builtin_fma(1.0, __builtin_nan(""), 3.0)));
+static_assert(__builtin_isnan(__builtin_fma(1.0, 2.0, __builtin_nan(""))));
+static_assert(__builtin_isinf(__builtin_fma(__builtin_inf(), 2.0, 3.0)));
+
+// fmod tests
+static_assert(__builtin_fmod(5.5, 3.0) == 2.5);
+static_assert(__builtin_fmodf(5.5f, 3.0f) == 2.5f);
+static_assert(__builtin_fmodf16(5.5f16, 3.0f16) == 2.5f16);
+static_assert(__builtin_fmodf128(5.5, 3.0) == 2.5);
+static_assert(__builtin_isnan(__builtin_fmod(__builtin_nan(""), 2.0)));
+static_assert(__builtin_isnan(__builtin_fmod(2.0, __builtin_nan(""))));
+
+// remainder tests
+static_assert(__builtin_remainder(5.5, 3.0) == -0.5);
+static_assert(__builtin_remainderf(5.5f, 3.0f) == -0.5f);
+static_assert(__builtin_remainderf128(5.5, 3.0) == -0.5);
+static_assert(__builtin_isnan(__builtin_remainder(__builtin_nan(""), 2.0)));
+static_assert(__builtin_isnan(__builtin_remainder(2.0, __builtin_nan(""))));
+
+// nextafter tests
+static_assert(__builtin_nextafter(1.0, 2.0) > 1.0);
+static_assert(__builtin_nextafter(1.0, 0.0) < 1.0);
+static_assert(__builtin_nextafter(1.0, 1.0) == 1.0);
+static_assert(__builtin_nextafter(0.0, 1.0) > 0.0); // new-error {{static 
assertion expression is not an integral constant expression}} \
+                                                     // new-note {{floating 
point arithmetic produces an infinity}}
+static_assert(__builtin_nextafter(0.0, -1.0) < 0.0); // new-error {{static 
assertion expression is not an integral constant expression}} \
+                                                     // new-note {{floating 
point arithmetic produces an infinity}}
+static_assert(isPositiveZero(__builtin_nextafter(-0.0, 0.0)));
+static_assert(__builtin_nextafterf128(1.0, 2.0) > 1.0);
+static_assert(__builtin_isnan(__builtin_nextafter(__builtin_nan(""), 2.0)));
+static_assert(__builtin_isnan(__builtin_nextafter(2.0, __builtin_nan(""))));
+static_assert(__builtin_isinf(__builtin_nextafter(__builtin_inf(), 
__builtin_inf())));
+
+// nexttoward tests
+static_assert(__builtin_nexttoward(1.0, 2.0L) > 1.0);
+static_assert(__builtin_nexttoward(1.0, 1.0L) == 1.0);
+static_assert(__builtin_nexttowardf128(1.0, 2.0L) > 1.0);
+static_assert(__builtin_isnan(__builtin_nexttoward(__builtin_nan(""), 2.0L)));
+static_assert(__builtin_isnan(__builtin_nexttoward(2.0, __builtin_nan(""))));
+
+// scalbn tests
+static_assert(__builtin_scalbn(1.0, 2) == 4.0);
+static_assert(__builtin_scalbnf(1.0f, -1) == 0.5f);
+static_assert(__builtin_scalbnf128(1.0, 2) == 4.0);
+static_assert(__builtin_scalbn(0.0, 2) == 0.0);
+static_assert(__builtin_scalbn(1.0, 0) == 1.0);
+static_assert(__builtin_isnan(__builtin_scalbn(__builtin_nan(""), 2)));
+static_assert(__builtin_isinf(__builtin_scalbn(__builtin_inf(), 2)));
+
+// scalbln tests
+static_assert(__builtin_scalbln(1.0, 2L) == 4.0);
+static_assert(__builtin_scalblnf128(1.0, 2L) == 4.0);
+static_assert(__builtin_isnan(__builtin_scalbln(__builtin_nan(""), 2L)));
+static_assert(__builtin_isinf(__builtin_scalbln(__builtin_inf(), 2L)));
+
+// ldexp tests
+static_assert(__builtin_ldexp(1.0, 3) == 8.0);
+static_assert(__builtin_ldexpf16(1.0f16, 3) == 8.0f16);
+static_assert(__builtin_ldexpf128(1.0, 3) == 8.0);
+static_assert(__builtin_isnan(__builtin_ldexp(__builtin_nan(""), 2)));
+static_assert(__builtin_isinf(__builtin_ldexp(__builtin_inf(), 2)));
+
+// ilogb tests
+static_assert(__builtin_ilogb(1.0) == 0);
+static_assert(__builtin_ilogb(2.0) == 1);
+static_assert(__builtin_ilogb(0.5) == -1);
+static_assert(__builtin_ilogbf(8.0f) == 3);
+static_assert(__builtin_ilogbf128(8.0) == 3);
+static_assert(__builtin_ilogb(0.) == (-__INT_MAX__));
+static_assert(__builtin_ilogb(__builtin_nan("")) == (-__INT_MAX__ - 1));
+static_assert(__builtin_ilogb(__builtin_inf()) == __INT_MAX__);
+
+// remquo tests
+constexpr double test_remquo(double x, double y) {
+  int quo = 0;
+  double rem = __builtin_remquo(x, y, &quo);
+  return rem;
+}
+static_assert(test_remquo(10.0, 3.0) == 1.0);
+
+constexpr int test_remquo_quo(double x, double y) {
+  int quo = 0;
+  __builtin_remquo(x, y, &quo);
+  return quo;
+}
+static_assert(test_remquo_quo(10.0, 3.0) == 3);
+static_assert(test_remquo_quo(10.0, -3.0) == -3);
+
+// remquo NaN cases (per C standard / cppreference):
+// - x or y is NaN
+// - x is +/-inf
+// - y is +/-0
+static_assert(__builtin_isnan(test_remquo(__builtin_nan(""), 2.0)));
+static_assert(__builtin_isnan(test_remquo(2.0, __builtin_nan(""))));
+static_assert(__builtin_isnan(test_remquo(__builtin_nan(""), 
__builtin_nan(""))));
+
+// frexp tests
+constexpr double test_frexp_val(double x) {
+  int exp;
+  return __builtin_frexp(x, &exp);
+}
+static_assert(test_frexp_val(8.0) == 0.5);
+
+constexpr int test_frexp_exp(double x) {
+  int exp;
+  __builtin_frexp(x, &exp);
+  return exp;
+}
+static_assert(test_frexp_exp(8.0) == 4);
+
+// frexp special cases: +/- 0
+static_assert(test_frexp_val(0.0) == 0.0);
+static_assert(isNegativeZero(test_frexp_val(-0.0)));
+static_assert(test_frexp_exp(0.0) == 0);
+static_assert(test_frexp_exp(-0.0) == 0);
+// NaN and Inf: LLVM does not specify the exponent value for these cases.
+static_assert(__builtin_isnan(test_frexp_val(__builtin_nan(""))));
+static_assert(__builtin_isinf(test_frexp_val(__builtin_inf())));
+constexpr int frexp_nan_exp = test_frexp_exp(__builtin_nan(""));
+constexpr int frexp_inf_exp = test_frexp_exp(__builtin_inf());
+constexpr int frexp_neg_inf_exp = test_frexp_exp(-__builtin_inf());
+static_assert(test_frexp_val(0.5) == 0.5);
+static_assert(test_frexp_exp(0.5) == 0);
+static_assert(test_frexp_val(1.0) == 0.5);
+static_assert(test_frexp_exp(1.0) == 1);
+
+// modf tests
+constexpr double test_modf_val(double x) {
+  double iptr;
+  return __builtin_modf(x, &iptr);
+}
+static_assert(test_modf_val(1.5) == 0.5);
+static_assert(test_modf_val(-1.5) == -0.5);
+static_assert(isNegativeZero(test_modf_val(-0.0)));
+
+constexpr double test_modf_iptr(double x) {
+  double iptr;
+  __builtin_modf(x, &iptr);
+  return iptr;
+}
+static_assert(test_modf_iptr(1.5) == 1.0);
+static_assert(test_modf_iptr(-1.5) == -1.0);
+
+// modf special values
+static_assert(test_modf_val(__builtin_inf()) == 0.0);
+static_assert(__builtin_isinf(test_modf_iptr(__builtin_inf())));
+
+
+namespace LRoundDiagnostic {
+  constexpr int i = __builtin_lround(1e30); // expected-error {{constexpr 
variable 'i' must be initialized by a constant expression}} \
+                                            // expected-note {{floating point 
arithmetic produces an infinity}}
+}
+
+// NaN payload preservation tests.
+template <typename T> struct Bytes {
+  unsigned char bytes[sizeof(T)];
+  bool operator==(const Bytes &) const = default;
+};
+template <typename T> constexpr bool bytesEqual(T a, T b) {
+  return __builtin_bit_cast(Bytes<T>, a) == __builtin_bit_cast(Bytes<T>, b);
+}
+
+constexpr double nanWithNonZeroPayload = __builtin_nan("0x1337");
+
+// fdim NaN payload preservation
+static_assert(bytesEqual(nanWithNonZeroPayload,
+                         __builtin_fdim(nanWithNonZeroPayload, 0.)));
+static_assert(bytesEqual(nanWithNonZeroPayload,
+                         __builtin_fdim(0., nanWithNonZeroPayload)));
+
+// round NaN payload preservation
+static_assert(bytesEqual(nanWithNonZeroPayload,
+                         __builtin_round(nanWithNonZeroPayload)));
+
+// ceil NaN payload preservation
+static_assert(bytesEqual(nanWithNonZeroPayload,
+                         __builtin_ceil(nanWithNonZeroPayload)));
+
+// floor NaN payload preservation
+static_assert(bytesEqual(nanWithNonZeroPayload,
+                         __builtin_floor(nanWithNonZeroPayload)));
+
+// trunc NaN payload preservation
+static_assert(bytesEqual(nanWithNonZeroPayload,
+                         __builtin_trunc(nanWithNonZeroPayload)));
+
+// roundeven NaN payload preservation
+static_assert(bytesEqual(nanWithNonZeroPayload,
+                         __builtin_roundeven(nanWithNonZeroPayload)));
+
+// fmod NaN payload preservation
+static_assert(bytesEqual(nanWithNonZeroPayload,
+                         __builtin_fmod(nanWithNonZeroPayload, 2.0)));
+static_assert(bytesEqual(nanWithNonZeroPayload,
+                         __builtin_fmod(2.0, nanWithNonZeroPayload)));
+
+// remainder NaN payload preservation
+static_assert(bytesEqual(nanWithNonZeroPayload,
+                         __builtin_remainder(nanWithNonZeroPayload, 2.0)));
+static_assert(bytesEqual(nanWithNonZeroPayload,
+                         __builtin_remainder(2.0, nanWithNonZeroPayload)));
+
+// scalbn NaN payload preservation
+static_assert(bytesEqual(nanWithNonZeroPayload,
+                         __builtin_scalbn(nanWithNonZeroPayload, 2)));
+
+// ldexp NaN payload preservation
+static_assert(bytesEqual(nanWithNonZeroPayload,
+                         __builtin_ldexp(nanWithNonZeroPayload, 2)));
+
+// modf NaN payload preservation
+constexpr double test_modf_nan() {
+  double iptr;
+  return __builtin_modf(nanWithNonZeroPayload, &iptr);
+}
+static_assert(bytesEqual(nanWithNonZeroPayload, test_modf_nan()));
+
+// frexp NaN payload preservation
+constexpr double test_frexp_nan() {
+  int exp;
+  return __builtin_frexp(nanWithNonZeroPayload, &exp);
+}
+static_assert(bytesEqual(nanWithNonZeroPayload, test_frexp_nan()));
diff --git a/clang/test/SemaCXX/constexpr-fenv-access.cpp 
b/clang/test/SemaCXX/constexpr-fenv-access.cpp
new file mode 100644
index 0000000000000..a41a6eacafbd4
--- /dev/null
+++ b/clang/test/SemaCXX/constexpr-fenv-access.cpp
@@ -0,0 +1,13 @@
+// RUN: %clang_cc1 -triple x86_64-linux -verify -Wno-unknown-pragmas %s
+// RUN: %clang_cc1 -triple x86_64-linux -verify 
-fexperimental-new-constant-interpreter -Wno-unknown-pragmas %s
+// expected-no-diagnostics
+
+#pragma STDC FENV_ROUND FE_TONEARESTFROMZERO
+static_assert(__builtin_fdim(__DBL_EPSILON__ / 2., -1.) == 1. + 
__DBL_EPSILON__, "");
+static_assert(__builtin_fma(0.5, __DBL_EPSILON__, 1.0) == 1. + 
__DBL_EPSILON__, "");
+static_assert(__builtin_scalbn(1.0 + __DBL_EPSILON__ / 2., 0) == 1. + 
__DBL_EPSILON__, "");
+
+#pragma STDC FENV_ROUND FE_TONEAREST
+static_assert(__builtin_fdim(__DBL_EPSILON__ / 2., -1.) == 1., "");
+static_assert(__builtin_fma(0.5, __DBL_EPSILON__, 1.0) == 1., "");
+static_assert(__builtin_scalbn(1.0 + __DBL_EPSILON__ / 2., 0) == 1., "");

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to