[clang] [Clang] Add constexpr eval for cmath builtins (PR #194327)
via cfe-commits
cfe-commits at lists.llvm.org
Sat May 23 01:50:43 PDT 2026
https://github.com/Serosh-commits updated https://github.com/llvm/llvm-project/pull/194327
>From 724c9b3f3cf2484d8d9047958bd19b340f910e8f Mon Sep 17 00:00:00 2001
From: Serosh-commits <janmejayapanda400 at gmail.com>
Date: Sat, 23 May 2026 14:20:18 +0530
Subject: [PATCH] address reviewer feedback
---
clang/include/clang/Basic/Builtins.td | 60 +-
clang/lib/AST/ByteCode/Interp.h | 26 +-
clang/lib/AST/ByteCode/InterpBuiltin.cpp | 544 +++++++++++++++++-
clang/lib/AST/ByteCode/InterpHelpers.h | 9 +-
clang/lib/AST/ExprConstant.cpp | 362 +++++++++++-
clang/test/CodeGen/aix-builtin-mapping.c | 13 +-
clang/test/CodeGen/builtins-fenv-access.c | 76 +++
clang/test/CodeGen/builtins-fenv-round.c | 22 +
clang/test/CodeGen/logb_scalbn.c | 280 +++++++--
.../test/SemaCXX/constexpr-cmath-builtins.cpp | 311 ++++++++++
clang/test/SemaCXX/constexpr-fenv-access.cpp | 13 +
11 files changed, 1612 insertions(+), 104 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..3555570062e41 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,8 @@ 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 +452,8 @@ 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 +479,8 @@ 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 +782,8 @@ 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 +1105,11 @@ 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;
@@ -2828,8 +2830,8 @@ 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);
+ auto Status = 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..7e0b3aafc36da 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,358 @@ 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 +3477,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 +3512,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 +3602,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 +4961,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..4104ae1a7c5fa 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,10 @@ 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/aix-builtin-mapping.c b/clang/test/CodeGen/aix-builtin-mapping.c
index cc1cc1a44f32c..2c378f426da2c 100644
--- a/clang/test/CodeGen/aix-builtin-mapping.c
+++ b/clang/test/CodeGen/aix-builtin-mapping.c
@@ -6,17 +6,18 @@
// RUN: %clang_cc1 -triple powerpc-ibm-aix -mlong-double-64 -emit-llvm -o - %s | FileCheck -check-prefix=CHECK %s
// RUN: %clang_cc1 -triple powerpc64-ibm-aix -mlong-double-64 -emit-llvm -o - %s | FileCheck -check-prefix=CHECK %s
+long double arg = 1.0L;
int main()
{
int DummyInt;
long double DummyLongDouble;
long double returnValue;
- returnValue = __builtin_modfl(1.0L, &DummyLongDouble);
- returnValue = __builtin_frexpl(0.0L, &DummyInt);
- returnValue = __builtin_ldexpl(1.0L, 1);
+ returnValue = __builtin_modfl(arg, &DummyLongDouble);
+ returnValue = __builtin_frexpl(arg, &DummyInt);
+ returnValue = __builtin_ldexpl(arg, 1);
}
-// CHECK: %{{.+}} = call { double, double } @llvm.modf.f64(double 1.000000e+00)
-// CHECK: %{{.+}} = call { double, i32 } @llvm.frexp.f64.i32(double 0.000000e+00)
-// CHECK: %{{.+}} = call double @llvm.ldexp.f64.i32(double 1.000000e+00, i32 1)
+// CHECK: %{{.+}} = call { double, double } @llvm.modf.f64(double %{{.+}})
+// CHECK: %{{.+}} = call { double, i32 } @llvm.frexp.f64.i32(double %{{.+}})
+// CHECK: %{{.+}} = call double @llvm.ldexp.f64.i32(double %{{.+}}, i32 1)
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/CodeGen/logb_scalbn.c b/clang/test/CodeGen/logb_scalbn.c
index 3210bf5c9077b..4f4ffc4e23c1f 100644
--- a/clang/test/CodeGen/logb_scalbn.c
+++ b/clang/test/CodeGen/logb_scalbn.c
@@ -764,97 +764,177 @@ void test_logb_var(double a) {
// CHECK-NEXT: store float [[TMP0]], ptr [[D1_ASCAST]], align 4
// CHECK-NEXT: ret void
// DEFAULT-LABEL: define dso_local void @test_scalbnf(
-// DEFAULT-SAME: ) #[[ATTR0]] {
+// DEFAULT-SAME: float noundef [[A:%.*]], i32 noundef [[B:%.*]]) #[[ATTR0]] {
// DEFAULT-NEXT: [[ENTRY:.*:]]
+// DEFAULT-NEXT: [[A_ADDR:%.*]] = alloca float, align 4, addrspace(5)
+// DEFAULT-NEXT: [[B_ADDR:%.*]] = alloca i32, align 4, addrspace(5)
// DEFAULT-NEXT: [[D1:%.*]] = alloca float, align 4, addrspace(5)
+// DEFAULT-NEXT: [[A_ADDR_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[A_ADDR]] to ptr
+// DEFAULT-NEXT: [[B_ADDR_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[B_ADDR]] to ptr
// DEFAULT-NEXT: [[D1_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[D1]] to ptr
-// DEFAULT-NEXT: [[TMP0:%.*]] = call float @llvm.ldexp.f32.i32(float 1.670000e+01, i32 10)
-// DEFAULT-NEXT: store float [[TMP0]], ptr [[D1_ASCAST]], align 4
+// DEFAULT-NEXT: store float [[A]], ptr [[A_ADDR_ASCAST]], align 4
+// DEFAULT-NEXT: store i32 [[B]], ptr [[B_ADDR_ASCAST]], align 4
+// DEFAULT-NEXT: [[TMP0:%.*]] = load float, ptr [[A_ADDR_ASCAST]], align 4
+// DEFAULT-NEXT: [[TMP1:%.*]] = load i32, ptr [[B_ADDR_ASCAST]], align 4
+// DEFAULT-NEXT: [[TMP2:%.*]] = call float @llvm.ldexp.f32.i32(float [[TMP0]], i32 [[TMP1]])
+// DEFAULT-NEXT: store float [[TMP2]], ptr [[D1_ASCAST]], align 4
// DEFAULT-NEXT: ret void
//
// IGNORE-LABEL: define dso_local void @test_scalbnf(
-// IGNORE-SAME: ) #[[ATTR0]] {
+// IGNORE-SAME: float noundef [[A:%.*]], i32 noundef [[B:%.*]]) #[[ATTR0]] {
// IGNORE-NEXT: [[ENTRY:.*:]]
+// IGNORE-NEXT: [[A_ADDR:%.*]] = alloca float, align 4, addrspace(5)
+// IGNORE-NEXT: [[B_ADDR:%.*]] = alloca i32, align 4, addrspace(5)
// IGNORE-NEXT: [[D1:%.*]] = alloca float, align 4, addrspace(5)
+// IGNORE-NEXT: [[A_ADDR_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[A_ADDR]] to ptr
+// IGNORE-NEXT: [[B_ADDR_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[B_ADDR]] to ptr
// IGNORE-NEXT: [[D1_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[D1]] to ptr
-// IGNORE-NEXT: [[TMP0:%.*]] = call float @llvm.ldexp.f32.i32(float 1.670000e+01, i32 10)
-// IGNORE-NEXT: store float [[TMP0]], ptr [[D1_ASCAST]], align 4
+// IGNORE-NEXT: store float [[A]], ptr [[A_ADDR_ASCAST]], align 4
+// IGNORE-NEXT: store i32 [[B]], ptr [[B_ADDR_ASCAST]], align 4
+// IGNORE-NEXT: [[TMP0:%.*]] = load float, ptr [[A_ADDR_ASCAST]], align 4
+// IGNORE-NEXT: [[TMP1:%.*]] = load i32, ptr [[B_ADDR_ASCAST]], align 4
+// IGNORE-NEXT: [[TMP2:%.*]] = call float @llvm.ldexp.f32.i32(float [[TMP0]], i32 [[TMP1]])
+// IGNORE-NEXT: store float [[TMP2]], ptr [[D1_ASCAST]], align 4
// IGNORE-NEXT: ret void
//
// STRICT-LABEL: define dso_local void @test_scalbnf(
-// STRICT-SAME: ) #[[ATTR0]] {
+// STRICT-SAME: float noundef [[A:%.*]], i32 noundef [[B:%.*]]) #[[ATTR0]] {
// STRICT-NEXT: [[ENTRY:.*:]]
+// STRICT-NEXT: [[A_ADDR:%.*]] = alloca float, align 4, addrspace(5)
+// STRICT-NEXT: [[B_ADDR:%.*]] = alloca i32, align 4, addrspace(5)
// STRICT-NEXT: [[D1:%.*]] = alloca float, align 4, addrspace(5)
+// STRICT-NEXT: [[A_ADDR_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[A_ADDR]] to ptr
+// STRICT-NEXT: [[B_ADDR_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[B_ADDR]] to ptr
// STRICT-NEXT: [[D1_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[D1]] to ptr
-// STRICT-NEXT: [[TMP0:%.*]] = call float @llvm.ldexp.f32.i32(float 1.670000e+01, i32 10)
-// STRICT-NEXT: store float [[TMP0]], ptr [[D1_ASCAST]], align 4
+// STRICT-NEXT: store float [[A]], ptr [[A_ADDR_ASCAST]], align 4
+// STRICT-NEXT: store i32 [[B]], ptr [[B_ADDR_ASCAST]], align 4
+// STRICT-NEXT: [[TMP0:%.*]] = load float, ptr [[A_ADDR_ASCAST]], align 4
+// STRICT-NEXT: [[TMP1:%.*]] = load i32, ptr [[B_ADDR_ASCAST]], align 4
+// STRICT-NEXT: [[TMP2:%.*]] = call float @llvm.ldexp.f32.i32(float [[TMP0]], i32 [[TMP1]])
+// STRICT-NEXT: store float [[TMP2]], ptr [[D1_ASCAST]], align 4
// STRICT-NEXT: ret void
//
// MAYTRAP-LABEL: define dso_local void @test_scalbnf(
-// MAYTRAP-SAME: ) #[[ATTR0]] {
+// MAYTRAP-SAME: float noundef [[A:%.*]], i32 noundef [[B:%.*]]) #[[ATTR0]] {
// MAYTRAP-NEXT: [[ENTRY:.*:]]
+// MAYTRAP-NEXT: [[A_ADDR:%.*]] = alloca float, align 4, addrspace(5)
+// MAYTRAP-NEXT: [[B_ADDR:%.*]] = alloca i32, align 4, addrspace(5)
// MAYTRAP-NEXT: [[D1:%.*]] = alloca float, align 4, addrspace(5)
+// MAYTRAP-NEXT: [[A_ADDR_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[A_ADDR]] to ptr
+// MAYTRAP-NEXT: [[B_ADDR_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[B_ADDR]] to ptr
// MAYTRAP-NEXT: [[D1_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[D1]] to ptr
-// MAYTRAP-NEXT: [[TMP0:%.*]] = call float @llvm.ldexp.f32.i32(float 1.670000e+01, i32 10)
-// MAYTRAP-NEXT: store float [[TMP0]], ptr [[D1_ASCAST]], align 4
+// MAYTRAP-NEXT: store float [[A]], ptr [[A_ADDR_ASCAST]], align 4
+// MAYTRAP-NEXT: store i32 [[B]], ptr [[B_ADDR_ASCAST]], align 4
+// MAYTRAP-NEXT: [[TMP0:%.*]] = load float, ptr [[A_ADDR_ASCAST]], align 4
+// MAYTRAP-NEXT: [[TMP1:%.*]] = load i32, ptr [[B_ADDR_ASCAST]], align 4
+// MAYTRAP-NEXT: [[TMP2:%.*]] = call float @llvm.ldexp.f32.i32(float [[TMP0]], i32 [[TMP1]])
+// MAYTRAP-NEXT: store float [[TMP2]], ptr [[D1_ASCAST]], align 4
// MAYTRAP-NEXT: ret void
//
// ERRNO-LABEL: define dso_local void @test_scalbnf(
-// ERRNO-SAME: ) #[[ATTR0]] {
+// ERRNO-SAME: float noundef [[A:%.*]], i32 noundef [[B:%.*]]) #[[ATTR0]] {
// ERRNO-NEXT: [[ENTRY:.*:]]
+// ERRNO-NEXT: [[A_ADDR:%.*]] = alloca float, align 4, addrspace(5)
+// ERRNO-NEXT: [[B_ADDR:%.*]] = alloca i32, align 4, addrspace(5)
// ERRNO-NEXT: [[D1:%.*]] = alloca float, align 4, addrspace(5)
+// ERRNO-NEXT: [[A_ADDR_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[A_ADDR]] to ptr
+// ERRNO-NEXT: [[B_ADDR_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[B_ADDR]] to ptr
// ERRNO-NEXT: [[D1_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[D1]] to ptr
-// ERRNO-NEXT: [[CALL:%.*]] = call float @scalbnf(float noundef 1.670000e+01, i32 noundef 10) #[[ATTR2]]
+// ERRNO-NEXT: store float [[A]], ptr [[A_ADDR_ASCAST]], align 4
+// ERRNO-NEXT: store i32 [[B]], ptr [[B_ADDR_ASCAST]], align 4
+// ERRNO-NEXT: [[TMP0:%.*]] = load float, ptr [[A_ADDR_ASCAST]], align 4
+// ERRNO-NEXT: [[TMP1:%.*]] = load i32, ptr [[B_ADDR_ASCAST]], align 4
+// ERRNO-NEXT: [[CALL:%.*]] = call float @scalbnf(float noundef [[TMP0]], i32 noundef [[TMP1]]) #[[ATTR2]]
// ERRNO-NEXT: store float [[CALL]], ptr [[D1_ASCAST]], align 4
// ERRNO-NEXT: ret void
//
// AMDGCNSPIRV-DEFAULT-LABEL: define spir_func void @test_scalbnf(
-// AMDGCNSPIRV-DEFAULT-SAME: ) addrspace(4) #[[ATTR0]] {
+// AMDGCNSPIRV-DEFAULT-SAME: float noundef [[A:%.*]], i32 noundef [[B:%.*]]) addrspace(4) #[[ATTR0]] {
// AMDGCNSPIRV-DEFAULT-NEXT: [[ENTRY:.*:]]
+// AMDGCNSPIRV-DEFAULT-NEXT: [[A_ADDR:%.*]] = alloca float, align 4
+// AMDGCNSPIRV-DEFAULT-NEXT: [[B_ADDR:%.*]] = alloca i32, align 4
// AMDGCNSPIRV-DEFAULT-NEXT: [[D1:%.*]] = alloca float, align 4
+// AMDGCNSPIRV-DEFAULT-NEXT: [[A_ADDR_ASCAST:%.*]] = addrspacecast ptr [[A_ADDR]] to ptr addrspace(4)
+// AMDGCNSPIRV-DEFAULT-NEXT: [[B_ADDR_ASCAST:%.*]] = addrspacecast ptr [[B_ADDR]] to ptr addrspace(4)
// AMDGCNSPIRV-DEFAULT-NEXT: [[D1_ASCAST:%.*]] = addrspacecast ptr [[D1]] to ptr addrspace(4)
-// AMDGCNSPIRV-DEFAULT-NEXT: [[TMP0:%.*]] = call addrspace(4) float @llvm.ldexp.f32.i32(float 1.670000e+01, i32 10)
-// AMDGCNSPIRV-DEFAULT-NEXT: store float [[TMP0]], ptr addrspace(4) [[D1_ASCAST]], align 4
+// AMDGCNSPIRV-DEFAULT-NEXT: store float [[A]], ptr addrspace(4) [[A_ADDR_ASCAST]], align 4
+// AMDGCNSPIRV-DEFAULT-NEXT: store i32 [[B]], ptr addrspace(4) [[B_ADDR_ASCAST]], align 4
+// AMDGCNSPIRV-DEFAULT-NEXT: [[TMP0:%.*]] = load float, ptr addrspace(4) [[A_ADDR_ASCAST]], align 4
+// AMDGCNSPIRV-DEFAULT-NEXT: [[TMP1:%.*]] = load i32, ptr addrspace(4) [[B_ADDR_ASCAST]], align 4
+// AMDGCNSPIRV-DEFAULT-NEXT: [[TMP2:%.*]] = call addrspace(4) float @llvm.ldexp.f32.i32(float [[TMP0]], i32 [[TMP1]])
+// AMDGCNSPIRV-DEFAULT-NEXT: store float [[TMP2]], ptr addrspace(4) [[D1_ASCAST]], align 4
// AMDGCNSPIRV-DEFAULT-NEXT: ret void
//
// AMDGCNSPIRV-IGNORE-LABEL: define spir_func void @test_scalbnf(
-// AMDGCNSPIRV-IGNORE-SAME: ) addrspace(4) #[[ATTR0]] {
+// AMDGCNSPIRV-IGNORE-SAME: float noundef [[A:%.*]], i32 noundef [[B:%.*]]) addrspace(4) #[[ATTR0]] {
// AMDGCNSPIRV-IGNORE-NEXT: [[ENTRY:.*:]]
+// AMDGCNSPIRV-IGNORE-NEXT: [[A_ADDR:%.*]] = alloca float, align 4
+// AMDGCNSPIRV-IGNORE-NEXT: [[B_ADDR:%.*]] = alloca i32, align 4
// AMDGCNSPIRV-IGNORE-NEXT: [[D1:%.*]] = alloca float, align 4
+// AMDGCNSPIRV-IGNORE-NEXT: [[A_ADDR_ASCAST:%.*]] = addrspacecast ptr [[A_ADDR]] to ptr addrspace(4)
+// AMDGCNSPIRV-IGNORE-NEXT: [[B_ADDR_ASCAST:%.*]] = addrspacecast ptr [[B_ADDR]] to ptr addrspace(4)
// AMDGCNSPIRV-IGNORE-NEXT: [[D1_ASCAST:%.*]] = addrspacecast ptr [[D1]] to ptr addrspace(4)
-// AMDGCNSPIRV-IGNORE-NEXT: [[TMP0:%.*]] = call addrspace(4) float @llvm.ldexp.f32.i32(float 1.670000e+01, i32 10)
-// AMDGCNSPIRV-IGNORE-NEXT: store float [[TMP0]], ptr addrspace(4) [[D1_ASCAST]], align 4
+// AMDGCNSPIRV-IGNORE-NEXT: store float [[A]], ptr addrspace(4) [[A_ADDR_ASCAST]], align 4
+// AMDGCNSPIRV-IGNORE-NEXT: store i32 [[B]], ptr addrspace(4) [[B_ADDR_ASCAST]], align 4
+// AMDGCNSPIRV-IGNORE-NEXT: [[TMP0:%.*]] = load float, ptr addrspace(4) [[A_ADDR_ASCAST]], align 4
+// AMDGCNSPIRV-IGNORE-NEXT: [[TMP1:%.*]] = load i32, ptr addrspace(4) [[B_ADDR_ASCAST]], align 4
+// AMDGCNSPIRV-IGNORE-NEXT: [[TMP2:%.*]] = call addrspace(4) float @llvm.ldexp.f32.i32(float [[TMP0]], i32 [[TMP1]])
+// AMDGCNSPIRV-IGNORE-NEXT: store float [[TMP2]], ptr addrspace(4) [[D1_ASCAST]], align 4
// AMDGCNSPIRV-IGNORE-NEXT: ret void
//
// AMDGCNSPIRV-STRICT-LABEL: define spir_func void @test_scalbnf(
-// AMDGCNSPIRV-STRICT-SAME: ) addrspace(4) #[[ATTR0]] {
+// AMDGCNSPIRV-STRICT-SAME: float noundef [[A:%.*]], i32 noundef [[B:%.*]]) addrspace(4) #[[ATTR0]] {
// AMDGCNSPIRV-STRICT-NEXT: [[ENTRY:.*:]]
+// AMDGCNSPIRV-STRICT-NEXT: [[A_ADDR:%.*]] = alloca float, align 4
+// AMDGCNSPIRV-STRICT-NEXT: [[B_ADDR:%.*]] = alloca i32, align 4
// AMDGCNSPIRV-STRICT-NEXT: [[D1:%.*]] = alloca float, align 4
+// AMDGCNSPIRV-STRICT-NEXT: [[A_ADDR_ASCAST:%.*]] = addrspacecast ptr [[A_ADDR]] to ptr addrspace(4)
+// AMDGCNSPIRV-STRICT-NEXT: [[B_ADDR_ASCAST:%.*]] = addrspacecast ptr [[B_ADDR]] to ptr addrspace(4)
// AMDGCNSPIRV-STRICT-NEXT: [[D1_ASCAST:%.*]] = addrspacecast ptr [[D1]] to ptr addrspace(4)
-// AMDGCNSPIRV-STRICT-NEXT: [[TMP0:%.*]] = call addrspace(4) float @llvm.ldexp.f32.i32(float 1.670000e+01, i32 10)
-// AMDGCNSPIRV-STRICT-NEXT: store float [[TMP0]], ptr addrspace(4) [[D1_ASCAST]], align 4
+// AMDGCNSPIRV-STRICT-NEXT: store float [[A]], ptr addrspace(4) [[A_ADDR_ASCAST]], align 4
+// AMDGCNSPIRV-STRICT-NEXT: store i32 [[B]], ptr addrspace(4) [[B_ADDR_ASCAST]], align 4
+// AMDGCNSPIRV-STRICT-NEXT: [[TMP0:%.*]] = load float, ptr addrspace(4) [[A_ADDR_ASCAST]], align 4
+// AMDGCNSPIRV-STRICT-NEXT: [[TMP1:%.*]] = load i32, ptr addrspace(4) [[B_ADDR_ASCAST]], align 4
+// AMDGCNSPIRV-STRICT-NEXT: [[TMP2:%.*]] = call addrspace(4) float @llvm.ldexp.f32.i32(float [[TMP0]], i32 [[TMP1]])
+// AMDGCNSPIRV-STRICT-NEXT: store float [[TMP2]], ptr addrspace(4) [[D1_ASCAST]], align 4
// AMDGCNSPIRV-STRICT-NEXT: ret void
//
// AMDGCNSPIRV-MAYTRAP-LABEL: define spir_func void @test_scalbnf(
-// AMDGCNSPIRV-MAYTRAP-SAME: ) addrspace(4) #[[ATTR0]] {
+// AMDGCNSPIRV-MAYTRAP-SAME: float noundef [[A:%.*]], i32 noundef [[B:%.*]]) addrspace(4) #[[ATTR0]] {
// AMDGCNSPIRV-MAYTRAP-NEXT: [[ENTRY:.*:]]
+// AMDGCNSPIRV-MAYTRAP-NEXT: [[A_ADDR:%.*]] = alloca float, align 4
+// AMDGCNSPIRV-MAYTRAP-NEXT: [[B_ADDR:%.*]] = alloca i32, align 4
// AMDGCNSPIRV-MAYTRAP-NEXT: [[D1:%.*]] = alloca float, align 4
+// AMDGCNSPIRV-MAYTRAP-NEXT: [[A_ADDR_ASCAST:%.*]] = addrspacecast ptr [[A_ADDR]] to ptr addrspace(4)
+// AMDGCNSPIRV-MAYTRAP-NEXT: [[B_ADDR_ASCAST:%.*]] = addrspacecast ptr [[B_ADDR]] to ptr addrspace(4)
// AMDGCNSPIRV-MAYTRAP-NEXT: [[D1_ASCAST:%.*]] = addrspacecast ptr [[D1]] to ptr addrspace(4)
-// AMDGCNSPIRV-MAYTRAP-NEXT: [[TMP0:%.*]] = call addrspace(4) float @llvm.ldexp.f32.i32(float 1.670000e+01, i32 10)
-// AMDGCNSPIRV-MAYTRAP-NEXT: store float [[TMP0]], ptr addrspace(4) [[D1_ASCAST]], align 4
+// AMDGCNSPIRV-MAYTRAP-NEXT: store float [[A]], ptr addrspace(4) [[A_ADDR_ASCAST]], align 4
+// AMDGCNSPIRV-MAYTRAP-NEXT: store i32 [[B]], ptr addrspace(4) [[B_ADDR_ASCAST]], align 4
+// AMDGCNSPIRV-MAYTRAP-NEXT: [[TMP0:%.*]] = load float, ptr addrspace(4) [[A_ADDR_ASCAST]], align 4
+// AMDGCNSPIRV-MAYTRAP-NEXT: [[TMP1:%.*]] = load i32, ptr addrspace(4) [[B_ADDR_ASCAST]], align 4
+// AMDGCNSPIRV-MAYTRAP-NEXT: [[TMP2:%.*]] = call addrspace(4) float @llvm.ldexp.f32.i32(float [[TMP0]], i32 [[TMP1]])
+// AMDGCNSPIRV-MAYTRAP-NEXT: store float [[TMP2]], ptr addrspace(4) [[D1_ASCAST]], align 4
// AMDGCNSPIRV-MAYTRAP-NEXT: ret void
//
// AMDGCNSPIRV-ERRNO-LABEL: define spir_func void @test_scalbnf(
-// AMDGCNSPIRV-ERRNO-SAME: ) addrspace(4) #[[ATTR0]] {
+// AMDGCNSPIRV-ERRNO-SAME: float noundef [[A:%.*]], i32 noundef [[B:%.*]]) addrspace(4) #[[ATTR0]] {
// AMDGCNSPIRV-ERRNO-NEXT: [[ENTRY:.*:]]
+// AMDGCNSPIRV-ERRNO-NEXT: [[A_ADDR:%.*]] = alloca float, align 4
+// AMDGCNSPIRV-ERRNO-NEXT: [[B_ADDR:%.*]] = alloca i32, align 4
// AMDGCNSPIRV-ERRNO-NEXT: [[D1:%.*]] = alloca float, align 4
+// AMDGCNSPIRV-ERRNO-NEXT: [[A_ADDR_ASCAST:%.*]] = addrspacecast ptr [[A_ADDR]] to ptr addrspace(4)
+// AMDGCNSPIRV-ERRNO-NEXT: [[B_ADDR_ASCAST:%.*]] = addrspacecast ptr [[B_ADDR]] to ptr addrspace(4)
// AMDGCNSPIRV-ERRNO-NEXT: [[D1_ASCAST:%.*]] = addrspacecast ptr [[D1]] to ptr addrspace(4)
-// AMDGCNSPIRV-ERRNO-NEXT: [[CALL:%.*]] = call spir_func addrspace(4) float @scalbnf(float noundef 1.670000e+01, i32 noundef 10) #[[ATTR2]]
+// AMDGCNSPIRV-ERRNO-NEXT: store float [[A]], ptr addrspace(4) [[A_ADDR_ASCAST]], align 4
+// AMDGCNSPIRV-ERRNO-NEXT: store i32 [[B]], ptr addrspace(4) [[B_ADDR_ASCAST]], align 4
+// AMDGCNSPIRV-ERRNO-NEXT: [[TMP0:%.*]] = load float, ptr addrspace(4) [[A_ADDR_ASCAST]], align 4
+// AMDGCNSPIRV-ERRNO-NEXT: [[TMP1:%.*]] = load i32, ptr addrspace(4) [[B_ADDR_ASCAST]], align 4
+// AMDGCNSPIRV-ERRNO-NEXT: [[CALL:%.*]] = call spir_func addrspace(4) float @scalbnf(float noundef [[TMP0]], i32 noundef [[TMP1]]) #[[ATTR2]]
// AMDGCNSPIRV-ERRNO-NEXT: store float [[CALL]], ptr addrspace(4) [[D1_ASCAST]], align 4
// AMDGCNSPIRV-ERRNO-NEXT: ret void
//
-void test_scalbnf() {
- float D1 = __builtin_scalbnf(16.7f, 10);
+void test_scalbnf(float a, int b) {
+ float D1 = __builtin_scalbnf(a, b);
}
// CHECK-LABEL: define dso_local void @test_scalbnf_var1(
// CHECK-SAME: float noundef [[A:%.*]]) #[[ATTR0]] {
@@ -1345,97 +1425,177 @@ void test_scalbnf_var3(float a, int b) {
// CHECK-NEXT: store double [[TMP0]], ptr [[D1_ASCAST]], align 8
// CHECK-NEXT: ret void
// DEFAULT-LABEL: define dso_local void @test_scalbn(
-// DEFAULT-SAME: ) #[[ATTR0]] {
+// DEFAULT-SAME: double noundef [[A:%.*]], i32 noundef [[B:%.*]]) #[[ATTR0]] {
// DEFAULT-NEXT: [[ENTRY:.*:]]
+// DEFAULT-NEXT: [[A_ADDR:%.*]] = alloca double, align 8, addrspace(5)
+// DEFAULT-NEXT: [[B_ADDR:%.*]] = alloca i32, align 4, addrspace(5)
// DEFAULT-NEXT: [[D1:%.*]] = alloca double, align 8, addrspace(5)
+// DEFAULT-NEXT: [[A_ADDR_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[A_ADDR]] to ptr
+// DEFAULT-NEXT: [[B_ADDR_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[B_ADDR]] to ptr
// DEFAULT-NEXT: [[D1_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[D1]] to ptr
-// DEFAULT-NEXT: [[TMP0:%.*]] = call double @llvm.ldexp.f64.i32(double 1.720000e+01, i32 10)
-// DEFAULT-NEXT: store double [[TMP0]], ptr [[D1_ASCAST]], align 8
+// DEFAULT-NEXT: store double [[A]], ptr [[A_ADDR_ASCAST]], align 8
+// DEFAULT-NEXT: store i32 [[B]], ptr [[B_ADDR_ASCAST]], align 4
+// DEFAULT-NEXT: [[TMP0:%.*]] = load double, ptr [[A_ADDR_ASCAST]], align 8
+// DEFAULT-NEXT: [[TMP1:%.*]] = load i32, ptr [[B_ADDR_ASCAST]], align 4
+// DEFAULT-NEXT: [[TMP2:%.*]] = call double @llvm.ldexp.f64.i32(double [[TMP0]], i32 [[TMP1]])
+// DEFAULT-NEXT: store double [[TMP2]], ptr [[D1_ASCAST]], align 8
// DEFAULT-NEXT: ret void
//
// IGNORE-LABEL: define dso_local void @test_scalbn(
-// IGNORE-SAME: ) #[[ATTR0]] {
+// IGNORE-SAME: double noundef [[A:%.*]], i32 noundef [[B:%.*]]) #[[ATTR0]] {
// IGNORE-NEXT: [[ENTRY:.*:]]
+// IGNORE-NEXT: [[A_ADDR:%.*]] = alloca double, align 8, addrspace(5)
+// IGNORE-NEXT: [[B_ADDR:%.*]] = alloca i32, align 4, addrspace(5)
// IGNORE-NEXT: [[D1:%.*]] = alloca double, align 8, addrspace(5)
+// IGNORE-NEXT: [[A_ADDR_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[A_ADDR]] to ptr
+// IGNORE-NEXT: [[B_ADDR_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[B_ADDR]] to ptr
// IGNORE-NEXT: [[D1_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[D1]] to ptr
-// IGNORE-NEXT: [[TMP0:%.*]] = call double @llvm.ldexp.f64.i32(double 1.720000e+01, i32 10)
-// IGNORE-NEXT: store double [[TMP0]], ptr [[D1_ASCAST]], align 8
+// IGNORE-NEXT: store double [[A]], ptr [[A_ADDR_ASCAST]], align 8
+// IGNORE-NEXT: store i32 [[B]], ptr [[B_ADDR_ASCAST]], align 4
+// IGNORE-NEXT: [[TMP0:%.*]] = load double, ptr [[A_ADDR_ASCAST]], align 8
+// IGNORE-NEXT: [[TMP1:%.*]] = load i32, ptr [[B_ADDR_ASCAST]], align 4
+// IGNORE-NEXT: [[TMP2:%.*]] = call double @llvm.ldexp.f64.i32(double [[TMP0]], i32 [[TMP1]])
+// IGNORE-NEXT: store double [[TMP2]], ptr [[D1_ASCAST]], align 8
// IGNORE-NEXT: ret void
//
// STRICT-LABEL: define dso_local void @test_scalbn(
-// STRICT-SAME: ) #[[ATTR0]] {
+// STRICT-SAME: double noundef [[A:%.*]], i32 noundef [[B:%.*]]) #[[ATTR0]] {
// STRICT-NEXT: [[ENTRY:.*:]]
+// STRICT-NEXT: [[A_ADDR:%.*]] = alloca double, align 8, addrspace(5)
+// STRICT-NEXT: [[B_ADDR:%.*]] = alloca i32, align 4, addrspace(5)
// STRICT-NEXT: [[D1:%.*]] = alloca double, align 8, addrspace(5)
+// STRICT-NEXT: [[A_ADDR_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[A_ADDR]] to ptr
+// STRICT-NEXT: [[B_ADDR_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[B_ADDR]] to ptr
// STRICT-NEXT: [[D1_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[D1]] to ptr
-// STRICT-NEXT: [[TMP0:%.*]] = call double @llvm.ldexp.f64.i32(double 1.720000e+01, i32 10)
-// STRICT-NEXT: store double [[TMP0]], ptr [[D1_ASCAST]], align 8
+// STRICT-NEXT: store double [[A]], ptr [[A_ADDR_ASCAST]], align 8
+// STRICT-NEXT: store i32 [[B]], ptr [[B_ADDR_ASCAST]], align 4
+// STRICT-NEXT: [[TMP0:%.*]] = load double, ptr [[A_ADDR_ASCAST]], align 8
+// STRICT-NEXT: [[TMP1:%.*]] = load i32, ptr [[B_ADDR_ASCAST]], align 4
+// STRICT-NEXT: [[TMP2:%.*]] = call double @llvm.ldexp.f64.i32(double [[TMP0]], i32 [[TMP1]])
+// STRICT-NEXT: store double [[TMP2]], ptr [[D1_ASCAST]], align 8
// STRICT-NEXT: ret void
//
// MAYTRAP-LABEL: define dso_local void @test_scalbn(
-// MAYTRAP-SAME: ) #[[ATTR0]] {
+// MAYTRAP-SAME: double noundef [[A:%.*]], i32 noundef [[B:%.*]]) #[[ATTR0]] {
// MAYTRAP-NEXT: [[ENTRY:.*:]]
+// MAYTRAP-NEXT: [[A_ADDR:%.*]] = alloca double, align 8, addrspace(5)
+// MAYTRAP-NEXT: [[B_ADDR:%.*]] = alloca i32, align 4, addrspace(5)
// MAYTRAP-NEXT: [[D1:%.*]] = alloca double, align 8, addrspace(5)
+// MAYTRAP-NEXT: [[A_ADDR_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[A_ADDR]] to ptr
+// MAYTRAP-NEXT: [[B_ADDR_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[B_ADDR]] to ptr
// MAYTRAP-NEXT: [[D1_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[D1]] to ptr
-// MAYTRAP-NEXT: [[TMP0:%.*]] = call double @llvm.ldexp.f64.i32(double 1.720000e+01, i32 10)
-// MAYTRAP-NEXT: store double [[TMP0]], ptr [[D1_ASCAST]], align 8
+// MAYTRAP-NEXT: store double [[A]], ptr [[A_ADDR_ASCAST]], align 8
+// MAYTRAP-NEXT: store i32 [[B]], ptr [[B_ADDR_ASCAST]], align 4
+// MAYTRAP-NEXT: [[TMP0:%.*]] = load double, ptr [[A_ADDR_ASCAST]], align 8
+// MAYTRAP-NEXT: [[TMP1:%.*]] = load i32, ptr [[B_ADDR_ASCAST]], align 4
+// MAYTRAP-NEXT: [[TMP2:%.*]] = call double @llvm.ldexp.f64.i32(double [[TMP0]], i32 [[TMP1]])
+// MAYTRAP-NEXT: store double [[TMP2]], ptr [[D1_ASCAST]], align 8
// MAYTRAP-NEXT: ret void
//
// ERRNO-LABEL: define dso_local void @test_scalbn(
-// ERRNO-SAME: ) #[[ATTR0]] {
+// ERRNO-SAME: double noundef [[A:%.*]], i32 noundef [[B:%.*]]) #[[ATTR0]] {
// ERRNO-NEXT: [[ENTRY:.*:]]
+// ERRNO-NEXT: [[A_ADDR:%.*]] = alloca double, align 8, addrspace(5)
+// ERRNO-NEXT: [[B_ADDR:%.*]] = alloca i32, align 4, addrspace(5)
// ERRNO-NEXT: [[D1:%.*]] = alloca double, align 8, addrspace(5)
+// ERRNO-NEXT: [[A_ADDR_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[A_ADDR]] to ptr
+// ERRNO-NEXT: [[B_ADDR_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[B_ADDR]] to ptr
// ERRNO-NEXT: [[D1_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[D1]] to ptr
-// ERRNO-NEXT: [[CALL:%.*]] = call double @scalbn(double noundef 1.720000e+01, i32 noundef 10) #[[ATTR2]]
+// ERRNO-NEXT: store double [[A]], ptr [[A_ADDR_ASCAST]], align 8
+// ERRNO-NEXT: store i32 [[B]], ptr [[B_ADDR_ASCAST]], align 4
+// ERRNO-NEXT: [[TMP0:%.*]] = load double, ptr [[A_ADDR_ASCAST]], align 8
+// ERRNO-NEXT: [[TMP1:%.*]] = load i32, ptr [[B_ADDR_ASCAST]], align 4
+// ERRNO-NEXT: [[CALL:%.*]] = call double @scalbn(double noundef [[TMP0]], i32 noundef [[TMP1]]) #[[ATTR2]]
// ERRNO-NEXT: store double [[CALL]], ptr [[D1_ASCAST]], align 8
// ERRNO-NEXT: ret void
//
// AMDGCNSPIRV-DEFAULT-LABEL: define spir_func void @test_scalbn(
-// AMDGCNSPIRV-DEFAULT-SAME: ) addrspace(4) #[[ATTR0]] {
+// AMDGCNSPIRV-DEFAULT-SAME: double noundef [[A:%.*]], i32 noundef [[B:%.*]]) addrspace(4) #[[ATTR0]] {
// AMDGCNSPIRV-DEFAULT-NEXT: [[ENTRY:.*:]]
+// AMDGCNSPIRV-DEFAULT-NEXT: [[A_ADDR:%.*]] = alloca double, align 8
+// AMDGCNSPIRV-DEFAULT-NEXT: [[B_ADDR:%.*]] = alloca i32, align 4
// AMDGCNSPIRV-DEFAULT-NEXT: [[D1:%.*]] = alloca double, align 8
+// AMDGCNSPIRV-DEFAULT-NEXT: [[A_ADDR_ASCAST:%.*]] = addrspacecast ptr [[A_ADDR]] to ptr addrspace(4)
+// AMDGCNSPIRV-DEFAULT-NEXT: [[B_ADDR_ASCAST:%.*]] = addrspacecast ptr [[B_ADDR]] to ptr addrspace(4)
// AMDGCNSPIRV-DEFAULT-NEXT: [[D1_ASCAST:%.*]] = addrspacecast ptr [[D1]] to ptr addrspace(4)
-// AMDGCNSPIRV-DEFAULT-NEXT: [[TMP0:%.*]] = call addrspace(4) double @llvm.ldexp.f64.i32(double 1.720000e+01, i32 10)
-// AMDGCNSPIRV-DEFAULT-NEXT: store double [[TMP0]], ptr addrspace(4) [[D1_ASCAST]], align 8
+// AMDGCNSPIRV-DEFAULT-NEXT: store double [[A]], ptr addrspace(4) [[A_ADDR_ASCAST]], align 8
+// AMDGCNSPIRV-DEFAULT-NEXT: store i32 [[B]], ptr addrspace(4) [[B_ADDR_ASCAST]], align 4
+// AMDGCNSPIRV-DEFAULT-NEXT: [[TMP0:%.*]] = load double, ptr addrspace(4) [[A_ADDR_ASCAST]], align 8
+// AMDGCNSPIRV-DEFAULT-NEXT: [[TMP1:%.*]] = load i32, ptr addrspace(4) [[B_ADDR_ASCAST]], align 4
+// AMDGCNSPIRV-DEFAULT-NEXT: [[TMP2:%.*]] = call addrspace(4) double @llvm.ldexp.f64.i32(double [[TMP0]], i32 [[TMP1]])
+// AMDGCNSPIRV-DEFAULT-NEXT: store double [[TMP2]], ptr addrspace(4) [[D1_ASCAST]], align 8
// AMDGCNSPIRV-DEFAULT-NEXT: ret void
//
// AMDGCNSPIRV-IGNORE-LABEL: define spir_func void @test_scalbn(
-// AMDGCNSPIRV-IGNORE-SAME: ) addrspace(4) #[[ATTR0]] {
+// AMDGCNSPIRV-IGNORE-SAME: double noundef [[A:%.*]], i32 noundef [[B:%.*]]) addrspace(4) #[[ATTR0]] {
// AMDGCNSPIRV-IGNORE-NEXT: [[ENTRY:.*:]]
+// AMDGCNSPIRV-IGNORE-NEXT: [[A_ADDR:%.*]] = alloca double, align 8
+// AMDGCNSPIRV-IGNORE-NEXT: [[B_ADDR:%.*]] = alloca i32, align 4
// AMDGCNSPIRV-IGNORE-NEXT: [[D1:%.*]] = alloca double, align 8
+// AMDGCNSPIRV-IGNORE-NEXT: [[A_ADDR_ASCAST:%.*]] = addrspacecast ptr [[A_ADDR]] to ptr addrspace(4)
+// AMDGCNSPIRV-IGNORE-NEXT: [[B_ADDR_ASCAST:%.*]] = addrspacecast ptr [[B_ADDR]] to ptr addrspace(4)
// AMDGCNSPIRV-IGNORE-NEXT: [[D1_ASCAST:%.*]] = addrspacecast ptr [[D1]] to ptr addrspace(4)
-// AMDGCNSPIRV-IGNORE-NEXT: [[TMP0:%.*]] = call addrspace(4) double @llvm.ldexp.f64.i32(double 1.720000e+01, i32 10)
-// AMDGCNSPIRV-IGNORE-NEXT: store double [[TMP0]], ptr addrspace(4) [[D1_ASCAST]], align 8
+// AMDGCNSPIRV-IGNORE-NEXT: store double [[A]], ptr addrspace(4) [[A_ADDR_ASCAST]], align 8
+// AMDGCNSPIRV-IGNORE-NEXT: store i32 [[B]], ptr addrspace(4) [[B_ADDR_ASCAST]], align 4
+// AMDGCNSPIRV-IGNORE-NEXT: [[TMP0:%.*]] = load double, ptr addrspace(4) [[A_ADDR_ASCAST]], align 8
+// AMDGCNSPIRV-IGNORE-NEXT: [[TMP1:%.*]] = load i32, ptr addrspace(4) [[B_ADDR_ASCAST]], align 4
+// AMDGCNSPIRV-IGNORE-NEXT: [[TMP2:%.*]] = call addrspace(4) double @llvm.ldexp.f64.i32(double [[TMP0]], i32 [[TMP1]])
+// AMDGCNSPIRV-IGNORE-NEXT: store double [[TMP2]], ptr addrspace(4) [[D1_ASCAST]], align 8
// AMDGCNSPIRV-IGNORE-NEXT: ret void
//
// AMDGCNSPIRV-STRICT-LABEL: define spir_func void @test_scalbn(
-// AMDGCNSPIRV-STRICT-SAME: ) addrspace(4) #[[ATTR0]] {
+// AMDGCNSPIRV-STRICT-SAME: double noundef [[A:%.*]], i32 noundef [[B:%.*]]) addrspace(4) #[[ATTR0]] {
// AMDGCNSPIRV-STRICT-NEXT: [[ENTRY:.*:]]
+// AMDGCNSPIRV-STRICT-NEXT: [[A_ADDR:%.*]] = alloca double, align 8
+// AMDGCNSPIRV-STRICT-NEXT: [[B_ADDR:%.*]] = alloca i32, align 4
// AMDGCNSPIRV-STRICT-NEXT: [[D1:%.*]] = alloca double, align 8
+// AMDGCNSPIRV-STRICT-NEXT: [[A_ADDR_ASCAST:%.*]] = addrspacecast ptr [[A_ADDR]] to ptr addrspace(4)
+// AMDGCNSPIRV-STRICT-NEXT: [[B_ADDR_ASCAST:%.*]] = addrspacecast ptr [[B_ADDR]] to ptr addrspace(4)
// AMDGCNSPIRV-STRICT-NEXT: [[D1_ASCAST:%.*]] = addrspacecast ptr [[D1]] to ptr addrspace(4)
-// AMDGCNSPIRV-STRICT-NEXT: [[TMP0:%.*]] = call addrspace(4) double @llvm.ldexp.f64.i32(double 1.720000e+01, i32 10)
-// AMDGCNSPIRV-STRICT-NEXT: store double [[TMP0]], ptr addrspace(4) [[D1_ASCAST]], align 8
+// AMDGCNSPIRV-STRICT-NEXT: store double [[A]], ptr addrspace(4) [[A_ADDR_ASCAST]], align 8
+// AMDGCNSPIRV-STRICT-NEXT: store i32 [[B]], ptr addrspace(4) [[B_ADDR_ASCAST]], align 4
+// AMDGCNSPIRV-STRICT-NEXT: [[TMP0:%.*]] = load double, ptr addrspace(4) [[A_ADDR_ASCAST]], align 8
+// AMDGCNSPIRV-STRICT-NEXT: [[TMP1:%.*]] = load i32, ptr addrspace(4) [[B_ADDR_ASCAST]], align 4
+// AMDGCNSPIRV-STRICT-NEXT: [[TMP2:%.*]] = call addrspace(4) double @llvm.ldexp.f64.i32(double [[TMP0]], i32 [[TMP1]])
+// AMDGCNSPIRV-STRICT-NEXT: store double [[TMP2]], ptr addrspace(4) [[D1_ASCAST]], align 8
// AMDGCNSPIRV-STRICT-NEXT: ret void
//
// AMDGCNSPIRV-MAYTRAP-LABEL: define spir_func void @test_scalbn(
-// AMDGCNSPIRV-MAYTRAP-SAME: ) addrspace(4) #[[ATTR0]] {
+// AMDGCNSPIRV-MAYTRAP-SAME: double noundef [[A:%.*]], i32 noundef [[B:%.*]]) addrspace(4) #[[ATTR0]] {
// AMDGCNSPIRV-MAYTRAP-NEXT: [[ENTRY:.*:]]
+// AMDGCNSPIRV-MAYTRAP-NEXT: [[A_ADDR:%.*]] = alloca double, align 8
+// AMDGCNSPIRV-MAYTRAP-NEXT: [[B_ADDR:%.*]] = alloca i32, align 4
// AMDGCNSPIRV-MAYTRAP-NEXT: [[D1:%.*]] = alloca double, align 8
+// AMDGCNSPIRV-MAYTRAP-NEXT: [[A_ADDR_ASCAST:%.*]] = addrspacecast ptr [[A_ADDR]] to ptr addrspace(4)
+// AMDGCNSPIRV-MAYTRAP-NEXT: [[B_ADDR_ASCAST:%.*]] = addrspacecast ptr [[B_ADDR]] to ptr addrspace(4)
// AMDGCNSPIRV-MAYTRAP-NEXT: [[D1_ASCAST:%.*]] = addrspacecast ptr [[D1]] to ptr addrspace(4)
-// AMDGCNSPIRV-MAYTRAP-NEXT: [[TMP0:%.*]] = call addrspace(4) double @llvm.ldexp.f64.i32(double 1.720000e+01, i32 10)
-// AMDGCNSPIRV-MAYTRAP-NEXT: store double [[TMP0]], ptr addrspace(4) [[D1_ASCAST]], align 8
+// AMDGCNSPIRV-MAYTRAP-NEXT: store double [[A]], ptr addrspace(4) [[A_ADDR_ASCAST]], align 8
+// AMDGCNSPIRV-MAYTRAP-NEXT: store i32 [[B]], ptr addrspace(4) [[B_ADDR_ASCAST]], align 4
+// AMDGCNSPIRV-MAYTRAP-NEXT: [[TMP0:%.*]] = load double, ptr addrspace(4) [[A_ADDR_ASCAST]], align 8
+// AMDGCNSPIRV-MAYTRAP-NEXT: [[TMP1:%.*]] = load i32, ptr addrspace(4) [[B_ADDR_ASCAST]], align 4
+// AMDGCNSPIRV-MAYTRAP-NEXT: [[TMP2:%.*]] = call addrspace(4) double @llvm.ldexp.f64.i32(double [[TMP0]], i32 [[TMP1]])
+// AMDGCNSPIRV-MAYTRAP-NEXT: store double [[TMP2]], ptr addrspace(4) [[D1_ASCAST]], align 8
// AMDGCNSPIRV-MAYTRAP-NEXT: ret void
//
// AMDGCNSPIRV-ERRNO-LABEL: define spir_func void @test_scalbn(
-// AMDGCNSPIRV-ERRNO-SAME: ) addrspace(4) #[[ATTR0]] {
+// AMDGCNSPIRV-ERRNO-SAME: double noundef [[A:%.*]], i32 noundef [[B:%.*]]) addrspace(4) #[[ATTR0]] {
// AMDGCNSPIRV-ERRNO-NEXT: [[ENTRY:.*:]]
+// AMDGCNSPIRV-ERRNO-NEXT: [[A_ADDR:%.*]] = alloca double, align 8
+// AMDGCNSPIRV-ERRNO-NEXT: [[B_ADDR:%.*]] = alloca i32, align 4
// AMDGCNSPIRV-ERRNO-NEXT: [[D1:%.*]] = alloca double, align 8
+// AMDGCNSPIRV-ERRNO-NEXT: [[A_ADDR_ASCAST:%.*]] = addrspacecast ptr [[A_ADDR]] to ptr addrspace(4)
+// AMDGCNSPIRV-ERRNO-NEXT: [[B_ADDR_ASCAST:%.*]] = addrspacecast ptr [[B_ADDR]] to ptr addrspace(4)
// AMDGCNSPIRV-ERRNO-NEXT: [[D1_ASCAST:%.*]] = addrspacecast ptr [[D1]] to ptr addrspace(4)
-// AMDGCNSPIRV-ERRNO-NEXT: [[CALL:%.*]] = call spir_func addrspace(4) double @scalbn(double noundef 1.720000e+01, i32 noundef 10) #[[ATTR2]]
+// AMDGCNSPIRV-ERRNO-NEXT: store double [[A]], ptr addrspace(4) [[A_ADDR_ASCAST]], align 8
+// AMDGCNSPIRV-ERRNO-NEXT: store i32 [[B]], ptr addrspace(4) [[B_ADDR_ASCAST]], align 4
+// AMDGCNSPIRV-ERRNO-NEXT: [[TMP0:%.*]] = load double, ptr addrspace(4) [[A_ADDR_ASCAST]], align 8
+// AMDGCNSPIRV-ERRNO-NEXT: [[TMP1:%.*]] = load i32, ptr addrspace(4) [[B_ADDR_ASCAST]], align 4
+// AMDGCNSPIRV-ERRNO-NEXT: [[CALL:%.*]] = call spir_func addrspace(4) double @scalbn(double noundef [[TMP0]], i32 noundef [[TMP1]]) #[[ATTR2]]
// AMDGCNSPIRV-ERRNO-NEXT: store double [[CALL]], ptr addrspace(4) [[D1_ASCAST]], align 8
// AMDGCNSPIRV-ERRNO-NEXT: ret void
//
-void test_scalbn() {
- double D1 = __builtin_scalbn(17.2, 10);
+void test_scalbn(double a, int b) {
+ double D1 = __builtin_scalbn(a, b);
}
// CHECK-LABEL: define dso_local void @test_scalbn_var1(
// CHECK-SAME: double noundef [[A:%.*]]) #[[ATTR0]] {
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., "");
More information about the cfe-commits
mailing list