How do we plan to make constexpr <cmath> and <cstdlib>

There is a proposal that has been accepted as part of C++23

It is not implemented in clang/llvm yet
https://clang.llvm.org/cxx_status.html#cxx23

In C++20 only std::lerp is declared as constexpr. It has a realization right in libcxx/include/cmath.

The proposal is making a great amount of functions constexpr, and we need to find a strategy to make it into clang/llvm.

Let’s take std::fmax as an example. libcxx/include/cmath just includes these functions from another file.

using ::fmax _LIBCPP_USING_IF_EXISTS;
using ::fmaxf _LIBCPP_USING_IF_EXISTS;
using ::fmaxl _LIBCPP_USING_IF_EXISTS;

Seems like the included fmax is in libcxx/include/math.h, but it also includes the function from somewhere else (is the actual realization in libc?)

inline _LIBCPP_HIDE_FROM_ABI float       fmax(float __x, float __y) _NOEXCEPT             {return ::fmaxf(__x, __y);}

I suggest this strategy (please feel free to review it and give an advice):

  1. Make sure that there is the constexpr builtin for the function. As example, I made a constexpr __builtin_fmax.
  2. Make the function constexpr for C++23 with macro such as _LIBCPP_CONSTEXPR_CXX23
  3. If the function is constantly evaluated, use the constexpr builtin from pt1

So the code that looks as:

inline _LIBCPP_HIDE_FROM_ABI float fmax(float __x, float __y) _NOEXCEPT {return ::fmaxf(__x, __y);}

will look as:

inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_CXX23 float fmax(float __x, float __y) _NOEXCEPT {
  if consteval { // TODO: use some macro
    return __builtin_fmax(__x, __y); // evaluated in Clang
  } else {
    return ::fmaxf(__x, __y); // the actual realization (from libc???)
  }
}

Please feel free to suggest your alternative if you have one =)

1 Like

I’m actually currently working on changing all the functions defined in math.h to use the builtins unconditionally. libc++ only supports gcc and clang and both implement builtins for all the math.h functions. The only things in the library missing to make them constexpr is to declare them constexpr. Clang would have to make the C declarations constexpr, since libc++ doesn’t have any control over them and make the builtins constexpr. See ⚙ D134584 [libc++] Rewrite <cmath> for how it should look in the end. I’m splitting it up into multiple patches according to @ldionne’s comment.

working on changing all the functions defined in math.h to use the builtins unconditionally

Nice! So as I see, for example, whenever we call std::fmax, it ends calling __builtin_fmax:

inline _LIBCPP_HIDE_FROM_ABI float fmax(float __x, float __y) _NOEXCEPT { return __builtin_fmaxf(__x, __y); }

The only things in the library missing to make them constexpr is to declare them constexpr

It is only if the builtin has the constexpr realization. Not all builtins support being “constant evaluated” now.

For example I supported constexpr __builtin_fmax recently: rG0edff6faa266, therefore we now can declare fmax constexpr (after your patch), but every other function needs to be reviewed separately.

(A constexpr builtin is a function that is evaluated right in Clang frontend, as you can see in the patch)

godbolt - builtin fmax in clang trunk (compiles in constant evaluation)
godbolt - bulting fmax in clang 15.0 (doesn’t compile)

It is better to send a separate patch for every constexpr builtin, otherwise it is hard for reviewing. I tried to make 5 constexpr builtins, but abandoned this patch: ⚙ D134136 [Clang] Support constexpr for builtin fmax, fmin, ilogb, logb, scalbn.

As I see, the strategy could look like this:

  1. @philnik patches cmath/math.h so every function call actually calls a builtin.
  2. Meanwhile contributors make constexpr builtins for every function.
  3. Contributors declare functions in cmath/math.h as constexpr.

Right, these aren’t templates, so clang actually diagnoses when a functions isn’t constexpr. I guess we have to add some mechanism to conditionally make the functions constexpr until all supported compilers have full support. I think GCC already has most of them constexpr. Would it be possible to add a preprocessor check whether a builtin can be constant evaluated, similar to __has_builtin(...)? Maybe __is_builtin_constexpr(...)? That will probably come in handy for other things too.

Nice idea! With __is_builtin_constexpr (or __has_constexpr_builtin) the contributors will not need to do the 3rd step

  1. Contributors declare functions in cmath/math.h as constexpr.

because we could use a macro that conditionally declares the function as constexpr like _LIBCPP_CONSTEXPR_CXX23_IF_BUILTIN_CONSTEXPR(__builtin_fmax)

I’ll research how to add __is_builtin_constexpr (currently we don’t have a register for constexpr builtins).

Also with this preprocessor check we could add a test to see which builtins are not yet constexpr, but have to be constexpr to complete support for p0533r9.

I wish to express my happiness that the implementation presented does not depend on the host implementation of the function.

I submitted the patch with __has_constexpr_builtin macro support for reviewing: ⚙ D136036 [Clang] Add __has_constexpr_builtin support

I don’t think we need to have that discussion now, as most of the hard floating point functions were left out by WG21, but in the long term i noticed that, the folks writing llvm libc decided to make all their cmath function correctly rounded.

My hope is that we could reuse their work inside of clang to provide the constexpr implementation of trigonometrics etc builtins. what do you think?
It would make clang more consistent with gcc which already has this builtin constexpr, and making them correctly rounded is the only realistic solution as

  • behaving like the target is not really possible or realistic
  • behaving like the host (as Hubert points out) is also undesirable

Thanks! I should have named the topic as How do we plan to implement P0533R9 in Clang. I meant not the whole <cmath> and <cstdlib> but the part of these that are mentioned in the paper =)

Submitted a patch request that introduces a test that indicates progress on this paper (checks exactly same functions that are in the paper)
https://reviews.llvm.org/D136538#3877255

Now we have a whole workflow connecting ExprConstant.cpp, Builtins.def, __has_constexpr_builtin, the new test, and conditionally constexpr functions in libcxx after @philnik refactors cmath. :slightly_smiling_face: