https://github.com/vinay-deshmukh updated https://github.com/llvm/llvm-project/pull/233522
>From ab2f1629f54fada8a92c2eafa8015f607f0f83b2 Mon Sep 17 00:00:00 2001 From: Vinay Deshmukh <[email protected]> Date: Mon, 19 Oct 2026 19:09:10 +0100 Subject: [PATCH 1/4] initial implementation --- clang/include/clang/Basic/Builtins.td | 3 +- clang/lib/AST/ByteCode/InterpBuiltin.cpp | 17 ++++++++++ clang/lib/AST/ExprConstant.cpp | 36 +++++++++++++++------ clang/test/Sema/constant-builtins-trunc.cpp | 28 ++++++++++++++++ 4 files changed, 73 insertions(+), 11 deletions(-) create mode 100644 clang/test/Sema/constant-builtins-trunc.cpp diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td index 344a712ddc585..010d9a9451320 100644 --- a/clang/include/clang/Basic/Builtins.td +++ b/clang/include/clang/Basic/Builtins.td @@ -526,7 +526,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)"; } @@ -4501,6 +4501,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/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp index a539fb26abc08..e0471b0de31d3 100644 --- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp +++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp @@ -4756,6 +4756,23 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call, case Builtin::BI__builtin_fminimum_numf128: return interp__builtin_fmin(S, OpPC, Frame, /*IsNumBuiltin=*/true); + case Builtin::BI__builtin_truncf: + case Builtin::BI__builtin_trunc: + case Builtin::BI__builtin_truncl: + case Builtin::BI__builtin_truncf16: + case Builtin::BI__builtin_truncf128: { + + // FIXME: implement after ExprConstant.cpp + // APFloat RHS(0.); + // Result = RHS; + // return true; + + llvm::errs() << "interp: pending vinay"; + // llvm::report_fatal_error( + // "TODO: implement constexpr evaluation for __builtin_signbit"); + return false; + } + case Builtin::BI__builtin_fmax: case Builtin::BI__builtin_fmaxf: case Builtin::BI__builtin_fmaxl: diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index a3385d3b7318f..d4361f0df3e3b 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -757,11 +757,7 @@ namespace { /// std::allocator<T>::allocate). const Expr *AllocExpr = nullptr; - enum Kind { - New, - ArrayNew, - StdAllocator - }; + enum Kind { New, ArrayNew, StdAllocator }; /// Get the kind of the allocation. This must match between allocation /// and deallocation. @@ -1959,7 +1955,7 @@ static bool IsGlobalLValue(APValue::LValueBase B) { if (!B) return true; - if (const ValueDecl *D = B.dyn_cast<const ValueDecl*>()) { + if (const ValueDecl *D = B.dyn_cast<const ValueDecl *>()) { // ... the address of an object with static storage duration, if (const VarDecl *VD = dyn_cast<VarDecl>(D)) return VD->hasGlobalStorage(); @@ -1974,7 +1970,7 @@ static bool IsGlobalLValue(APValue::LValueBase B) { if (B.is<TypeInfoLValue>() || B.is<DynamicAllocLValue>()) return true; - const Expr *E = B.get<const Expr*>(); + const Expr *E = B.get<const Expr *>(); switch (E->getStmtClass()) { default: return false; @@ -3512,9 +3508,11 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E, // have been value-dependent too), so diagnose that. assert(!VD->mightBeUsableInConstantExpressions(Info.Ctx)); if (!Info.checkingPotentialConstantExpression()) { - Info.FFDiag(E, Info.getLangOpts().CPlusPlus11 - ? diag::note_constexpr_ltor_non_constexpr - : diag::note_constexpr_ltor_non_integral, 1) + Info.FFDiag(E, + Info.getLangOpts().CPlusPlus11 + ? diag::note_constexpr_ltor_non_constexpr + : diag::note_constexpr_ltor_non_integral, + 1) << VD << VD->getType(); NoteLValueLocation(Info, Base); } @@ -20511,6 +20509,24 @@ bool FloatExprEvaluator::VisitCallExpr(const CallExpr *E) { return true; } + case Builtin::BI__builtin_truncf: + case Builtin::BI__builtin_trunc: + case Builtin::BI__builtin_truncl: + case Builtin::BI__builtin_truncf16: + case Builtin::BI__builtin_truncf128: { + // const Expr * arg0 = E->getArg(0); + + // FIXME: use 0 for now, will implement later + APFloat RHS(0.); + Result = RHS; + return true; + + // llvm::errs() << "exprconstant: pending vinay"; + // llvm::report_fatal_error( + // "TODO: implement constexpr evaluation for __builtin_signbit"); + // return false; + } + case Builtin::BI__builtin_fmax: case Builtin::BI__builtin_fmaxf: case Builtin::BI__builtin_fmaxl: diff --git a/clang/test/Sema/constant-builtins-trunc.cpp b/clang/test/Sema/constant-builtins-trunc.cpp new file mode 100644 index 0000000000000..6537b47ac430f --- /dev/null +++ b/clang/test/Sema/constant-builtins-trunc.cpp @@ -0,0 +1,28 @@ +// RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify %s + +// expected-no-diagnostics + + +// TODO: +// R-U-N: +auto x = " // %clang_cc1 -std=c++17 -fsyntax-only -verify -fexperimental-new-constant-interpreter %s"; + +// trunc +// truncf +// truncl + +// ASSERT_NOT_CONSTEXPR_CXX23(std::trunc(1.0f) == 1.0f); +// ASSERT_NOT_CONSTEXPR_CXX23(std::trunc(1.0) == 1.0); +// ASSERT_NOT_CONSTEXPR_CXX23(std::trunc(1.0L) == 1.0L); +// ASSERT_NOT_CONSTEXPR_CXX23(std::truncf(1.0f) == 1.0f); +// ASSERT_NOT_CONSTEXPR_CXX23(std::truncl(1.0L) == 1.0L); + + +// std::trunc(1.0f) +// std::trunc(1.0) +// std::trunc(1.0L) +// std::truncf(1.0f) +// std::truncl(1.0L) + +constexpr float f = __builtin_trunc(+2.7f); +static_assert(f == 0.0); // FIXME: implement it later \ No newline at end of file >From e218e2d1027e6bce2a627f149d4d2f30ef26e0dc Mon Sep 17 00:00:00 2001 From: Vinay Deshmukh <[email protected]> Date: Mon, 19 Oct 2026 21:37:53 +0100 Subject: [PATCH 2/4] implement ExprConstant approach --- clang/lib/AST/ExprConstant.cpp | 15 +++--- clang/test/Sema/constant-builtins-trunc.cpp | 58 +++++++++++++++++++-- 2 files changed, 61 insertions(+), 12 deletions(-) diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index d4361f0df3e3b..2ddf010a64272 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -56,6 +56,7 @@ #include "clang/Basic/TargetBuiltins.h" #include "clang/Basic/TargetInfo.h" #include "llvm/ADT/APFixedPoint.h" +#include "llvm/ADT/FloatingPointMode.h" #include "llvm/ADT/Sequence.h" #include "llvm/ADT/SmallBitVector.h" #include "llvm/ADT/StringExtras.h" @@ -20514,17 +20515,13 @@ bool FloatExprEvaluator::VisitCallExpr(const CallExpr *E) { case Builtin::BI__builtin_truncl: case Builtin::BI__builtin_truncf16: case Builtin::BI__builtin_truncf128: { - // const Expr * arg0 = E->getArg(0); + APFloat Arg0(0.); + if (!EvaluateFloat(E->getArg(0), Arg0, Info)) + return false; - // FIXME: use 0 for now, will implement later - APFloat RHS(0.); - Result = RHS; + Arg0.roundToIntegral(llvm::RoundingMode::TowardZero); + Result = Arg0; return true; - - // llvm::errs() << "exprconstant: pending vinay"; - // llvm::report_fatal_error( - // "TODO: implement constexpr evaluation for __builtin_signbit"); - // return false; } case Builtin::BI__builtin_fmax: diff --git a/clang/test/Sema/constant-builtins-trunc.cpp b/clang/test/Sema/constant-builtins-trunc.cpp index 6537b47ac430f..fc0b9e311a949 100644 --- a/clang/test/Sema/constant-builtins-trunc.cpp +++ b/clang/test/Sema/constant-builtins-trunc.cpp @@ -1,6 +1,7 @@ // RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify %s -// expected-no-diagnostics +// FIXME +auto y = "// expected-no-diagnostics"; // TODO: @@ -24,5 +25,56 @@ auto x = " // %clang_cc1 -std=c++17 -fsyntax-only -verify -fexperimental-new-co // std::truncf(1.0f) // std::truncl(1.0L) -constexpr float f = __builtin_trunc(+2.7f); -static_assert(f == 0.0); // FIXME: implement it later \ No newline at end of file + +constexpr double NaN = __builtin_nan(""); +constexpr double Inf = __builtin_inf(); +constexpr double NegInf = -__builtin_inf(); + +static_assert(__builtin_trunc(+2.7f) == 2.0); +static_assert(__builtin_trunc(+2.7) == 2.0); +static_assert(__builtin_trunc(+2.7L) == 2.0L); +static_assert(__builtin_truncf(+2.7f) == 2.0f); +static_assert(__builtin_truncl(+2.7L) == 2.0L); +static_assert(__builtin_truncf16(+2.7f) == 2.0f); +static_assert(__builtin_truncf128(+2.7f) == 2.0f); + +#define TRUNC_TEST_POSITIVE(T, FUNC) \ + static_assert(FUNC(T(2.7)) == T(2.0)); \ + static_assert(FUNC(T(2.0)) == T(2.0)); \ + static_assert(FUNC(T(0.0)) == T(0.0)); + +#define TRUNC_TEST_NEGATIVE(T, FUNC) \ + static_assert(FUNC(T(-2.7)) == T(-2.0)); \ + static_assert(FUNC(T(-2.0)) == T(-2.0)); \ + static_assert(FUNC(T(-0.0)) == T(-0.0)); + +#define TRUNC_TEST_SPECIAL(T, FUNC) \ + static_assert(FUNC(T(Inf)) == T(Inf)); \ + static_assert(FUNC(T(NegInf)) == T(NegInf)); \ + static_assert(__builtin_isnan(FUNC(NaN))); + +// FIXME: add NaN handling here as well + +#define LIST_TRUNC_TESTS(T, FUNC) \ + TRUNC_TEST_POSITIVE(T, FUNC) \ + TRUNC_TEST_NEGATIVE(T, FUNC) \ + TRUNC_TEST_SPECIAL(T, FUNC) + + +LIST_TRUNC_TESTS(float, __builtin_trunc); +LIST_TRUNC_TESTS(double, __builtin_trunc); +LIST_TRUNC_TESTS((long double), __builtin_trunc); +LIST_TRUNC_TESTS(__fp16, __builtin_truncf16) +#ifdef __FLOAT128__ +LIST_TRUNC_TESTS(__float128, __builtin_truncf128) +#endif + +static_assert(__builtin_isnan(__builtin_trunc(NaN))); +static_assert(__builtin_trunc(Inf) == Inf); +static_assert(__builtin_trunc(NegInf) == NegInf); +static_assert(__builtin_trunc(0.0) == 0.0); +static_assert(__builtin_trunc(-0.0) == -0.0); + +// FIXME: remove, kept here temporarily +// expected-error@+1 {{static assertion failed}} +static_assert(0 == 1); >From 8d6018f6edc2cf067f544e6e83fc48f8f0da5054 Mon Sep 17 00:00:00 2001 From: Vinay Deshmukh <[email protected]> Date: Mon, 19 Oct 2026 22:09:17 +0100 Subject: [PATCH 3/4] InterpBuiltin.cpp --- clang/lib/AST/ByteCode/InterpBuiltin.cpp | 27 ++++++++++++--------- clang/test/Sema/constant-builtins-trunc.cpp | 8 +----- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp index e0471b0de31d3..f13d86f968dff 100644 --- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp +++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "../ExprConstShared.h" #include "Boolean.h" +#include "ByteCode/Floating.h" #include "Char.h" #include "EvalEmitter.h" #include "InterpBuiltinBitCast.h" @@ -529,6 +530,18 @@ static bool interp__builtin_fmin(InterpState &S, CodePtr OpPC, return true; } +static bool interp__builtin_trunc(InterpState &S, CodePtr OpPC, + const InterpFrame *Frame) { + const Floating &Arg = S.Stk.pop<Floating>(); + Floating Result = S.allocFloat(Arg.getSemantics()); + APFloat tmp = Arg.getAPFloat(); + tmp.roundToIntegral(llvm::RoundingMode::TowardZero); + + Result.copy(tmp); + S.Stk.push<Floating>(Result); + return true; +} + static bool interp__builtin_fmax(InterpState &S, CodePtr OpPC, const InterpFrame *Frame, bool IsNumBuiltin) { const Floating &RHS = S.Stk.pop<Floating>(); @@ -4760,18 +4773,8 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call, case Builtin::BI__builtin_trunc: case Builtin::BI__builtin_truncl: case Builtin::BI__builtin_truncf16: - case Builtin::BI__builtin_truncf128: { - - // FIXME: implement after ExprConstant.cpp - // APFloat RHS(0.); - // Result = RHS; - // return true; - - llvm::errs() << "interp: pending vinay"; - // llvm::report_fatal_error( - // "TODO: implement constexpr evaluation for __builtin_signbit"); - return false; - } + case Builtin::BI__builtin_truncf128: + return interp__builtin_trunc(S, OpPC, Frame); case Builtin::BI__builtin_fmax: case Builtin::BI__builtin_fmaxf: diff --git a/clang/test/Sema/constant-builtins-trunc.cpp b/clang/test/Sema/constant-builtins-trunc.cpp index fc0b9e311a949..d4f3e730e9b5c 100644 --- a/clang/test/Sema/constant-builtins-trunc.cpp +++ b/clang/test/Sema/constant-builtins-trunc.cpp @@ -1,13 +1,9 @@ // RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify %s +// RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify -fexperimental-new-constant-interpreter %s // FIXME auto y = "// expected-no-diagnostics"; - -// TODO: -// R-U-N: -auto x = " // %clang_cc1 -std=c++17 -fsyntax-only -verify -fexperimental-new-constant-interpreter %s"; - // trunc // truncf // truncl @@ -53,8 +49,6 @@ static_assert(__builtin_truncf128(+2.7f) == 2.0f); static_assert(FUNC(T(NegInf)) == T(NegInf)); \ static_assert(__builtin_isnan(FUNC(NaN))); -// FIXME: add NaN handling here as well - #define LIST_TRUNC_TESTS(T, FUNC) \ TRUNC_TEST_POSITIVE(T, FUNC) \ TRUNC_TEST_NEGATIVE(T, FUNC) \ >From 3768c35aee4d3f6d6bc34915e8b1d7ba5f27ad44 Mon Sep 17 00:00:00 2001 From: Vinay Deshmukh <[email protected]> Date: Tue, 20 Oct 2026 08:55:28 +0100 Subject: [PATCH 4/4] mark constexpr to see CI run --- libcxx/include/__math/rounding_functions.h | 11 +++++++---- .../numerics/c.math/constexpr-cxx23-clang.pass.cpp | 10 +++++----- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/libcxx/include/__math/rounding_functions.h b/libcxx/include/__math/rounding_functions.h index aadeb395fa2eb..188a517d4cc25 100644 --- a/libcxx/include/__math/rounding_functions.h +++ b/libcxx/include/__math/rounding_functions.h @@ -222,19 +222,22 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0> // trunc -[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI float trunc(float __x) _NOEXCEPT { return __builtin_trunc(__x); } +[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI float trunc(float __x) _NOEXCEPT { + return __builtin_trunc(__x); +} template <class = int> -[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI double trunc(double __x) _NOEXCEPT { +[[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI double trunc(double __x) _NOEXCEPT { return __builtin_trunc(__x); } -[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI long double trunc(long double __x) _NOEXCEPT { +[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI long double +trunc(long double __x) _NOEXCEPT { return __builtin_truncl(__x); } template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0> -[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI double trunc(_A1 __x) _NOEXCEPT { +[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI double trunc(_A1 __x) _NOEXCEPT { return __builtin_trunc((double)__x); } diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp index beca4907284ad..6560a70aafc7a 100644 --- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp +++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp @@ -137,11 +137,11 @@ int main(int, char**) { ASSERT_NOT_CONSTEXPR_CXX23(std::llroundf(1.0f) == 1LL); ASSERT_NOT_CONSTEXPR_CXX23(std::llroundl(1.0L) == 1LL); - ASSERT_NOT_CONSTEXPR_CXX23(std::trunc(1.0f) == 1.0f); - ASSERT_NOT_CONSTEXPR_CXX23(std::trunc(1.0) == 1.0); - ASSERT_NOT_CONSTEXPR_CXX23(std::trunc(1.0L) == 1.0L); - ASSERT_NOT_CONSTEXPR_CXX23(std::truncf(1.0f) == 1.0f); - ASSERT_NOT_CONSTEXPR_CXX23(std::truncl(1.0L) == 1.0L); + ASSERT_CONSTEXPR_CXX23(std::trunc(1.0f) == 1.0f); + ASSERT_CONSTEXPR_CXX23(std::trunc(1.0) == 1.0); + ASSERT_CONSTEXPR_CXX23(std::trunc(1.0L) == 1.0L); + ASSERT_CONSTEXPR_CXX23(std::truncf(1.0f) == 1.0f); + ASSERT_CONSTEXPR_CXX23(std::truncl(1.0L) == 1.0L); ASSERT_NOT_CONSTEXPR_CXX23(std::fmod(1.5f, 1.0f) == 0.5f); ASSERT_NOT_CONSTEXPR_CXX23(std::fmod(1.5, 1.0) == 0.5); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
