C++11: new builtin to allow constexpr to be applied to performance-critical functions

[Crossposted to both GCC and Clang dev lists]

Hi,

One issue facing library authors wanting to use C++11’s constexpr feature is that the same implementation must be provided for both the case of function invocation substitution and for execution at runtime. Due to the constraints on constexpr function definitions, this can force an implementation of a library function to be inefficient. To counteract this, I’d like to propose the addition of a builtin:

bool __builtin_constexpr_p()

This builtin would only be supported within constexpr function definitions. If the containing function is undergoing function invocation substitution, it returns true. Otherwise, it returns false. Hence we can implement library functions with a pattern like:

constexpr int constexpr_strncmp(const char *p, const char *q, size_t n) {
return !n ? 0 : *p != *q ? *p - *q : !*p ? 0 : constexpr_strncmp(p+1, q+1, n-1);
}

attribute((always_inline)) constexpr int my_strncmp(const char *p, const char *q, size_t n) {
return __builtin_constexpr_p() ? constexpr_strncmp(p, q, n) : strncmp(p, q, n);
}

Does this seem reasonable?

Thanks!
Richard

[Crossposted to both GCC and Clang dev lists]

Hi,

One issue facing library authors wanting to use C++11's constexpr feature
is that the same implementation must be provided for both the case of
function invocation substitution and for execution at runtime. Due to the
constraints on constexpr function definitions, this can force an
implementation of a library function to be inefficient. To counteract this,
I'd like to propose the addition of a builtin:

  bool __builtin_constexpr_p()

This builtin would only be supported within constexpr function
definitions. If the containing function is undergoing function invocation
substitution, it returns true. Otherwise, it returns false. Hence we can
implement library functions with a pattern like:

  constexpr int constexpr_strncmp(const char *p, const char *q, size_t n) {
    return !n ? 0 : *p != *q ? *p - *q : !*p ? 0 : constexpr_strncmp(p+1,
q+1, n-1);
  }
  __attribute__((always_inline)) constexpr int my_strncmp(const char *p,
const char *q, size_t n) {
    return __builtin_constexpr_p() ? constexpr_strncmp(p, q, n) :
strncmp(p, q, n);
  }

Does this seem reasonable?

Yes, especially the primary functionality. However, I have some concerns
about the interface. Let me hypothesize a different interface:

This stays the same...

constexpr int constexpr_strncmp(const char *p, const char *q, size_t n) {
  return !n ? 0 : *p != *q ? *p - *q : !*p ? 0 : constexpr_strncmp(p+1,
q+1, n-1);
}

But here we do something different on the actual declaration:

[[constexpr_alias(constexpr_strncmp)]]
int strncmp(const char *p, const char *q, size_t n);

When parsing the *declaration* of this function, we lookup the function
name passed to constexpr_alias. We must find a constexpr function with an
identical signature. Then, at function invocation substitution of strncmp,
we instead substitute the body of constexpr_strncmp.

This seems more direct (no redirection in the code), and it also provides a
specific advantage of allowing this to be easily added to an existing
declaration in a declaration-only header file without impacting or changing
the name of the runtime executed body or definition.

I really like this approach.

[Crossposted to both GCC and Clang dev lists]

Hi,

One issue facing library authors wanting to use C++11's constexpr feature is that the same implementation must be provided for both the case of function invocation substitution and for execution at runtime. Due to the constraints on constexpr function definitions, this can force an implementation of a library function to be inefficient. To counteract this, I'd like to propose the addition of a builtin:

  bool __builtin_constexpr_p()

This builtin would only be supported within constexpr function definitions. If the containing function is undergoing function invocation substitution, it returns true. Otherwise, it returns false. Hence we can implement library functions with a pattern like:

  constexpr int constexpr_strncmp(const char *p, const char *q, size_t n) {
    return !n ? 0 : *p != *q ? *p - *q : !*p ? 0 : constexpr_strncmp(p+1, q+1, n-1);
  }
  __attribute__((always_inline)) constexpr int my_strncmp(const char *p, const char *q, size_t n) {
    return __builtin_constexpr_p() ? constexpr_strncmp(p, q, n) : strncmp(p, q, n);
  }

Does this seem reasonable?

Yes, especially the primary functionality. However, I have some
concerns about the interface. Let me hypothesize a different
interface:

This stays the same...

constexpr int constexpr_strncmp(const char *p, const char *q, size_t n) {
  return !n ? 0 : *p != *q ? *p - *q : !*p ? 0 : constexpr_strncmp(p+1, q+1, n-1);
}

But here we do something different on the actual declaration:

[[constexpr_alias(constexpr_strncmp)]]
int strncmp(const char *p, const char *q, size_t n);

When parsing the *declaration* of this function, we lookup the
function name passed to constexpr_alias. We must find a constexpr
function with an identical signature. Then, at function invocation
substitution of strncmp, we instead substitute the body of
constexpr_strncmp.

This seems more direct (no redirection in the code), and it also
provides a specific advantage of allowing this to be easily added to
an existing declaration in a declaration-only header file without
impacting or changing the name of the runtime executed body or
definition.

-Chandler

PS: Sorry for the dup Clang folks, the GCC list doesn't like my mail client.

>
> [Crossposted to both GCC and Clang dev lists]
>
> Hi,
>
> One issue facing library authors wanting to use C++11's constexpr
> feature is that the same implementation must be provided for both the case
> of function invocation substitution and for execution at runtime. Due to the
> constraints on constexpr function definitions, this can force an
> implementation of a library function to be inefficient. To counteract this,
> I'd like to propose the addition of a builtin:
>
> bool __builtin_constexpr_p()
>
> This builtin would only be supported within constexpr function
> definitions. If the containing function is undergoing function invocation
> substitution, it returns true. Otherwise, it returns false. Hence we can
> implement library functions with a pattern like:
>
> constexpr int constexpr_strncmp(const char *p, const char *q, size_t
> n) {
> return !n ? 0 : *p != *q ? *p - *q : !*p ? 0 :
> constexpr_strncmp(p+1, q+1, n-1);
> }
> __attribute__((always_inline)) constexpr int my_strncmp(const char *p,
> const char *q, size_t n) {
> return __builtin_constexpr_p() ? constexpr_strncmp(p, q, n) :
> strncmp(p, q, n);
> }
>
> Does this seem reasonable?

Yes, especially the primary functionality. However, I have some
concerns about the interface. Let me hypothesize a different
interface:

This stays the same...
> constexpr int constexpr_strncmp(const char *p, const char *q, size_t n)
> {
> return !n ? 0 : *p != *q ? *p - *q : !*p ? 0 : constexpr_strncmp(p+1,
> q+1, n-1);
> }

But here we do something different on the actual declaration:
>
> [[constexpr_alias(constexpr_strncmp)]]
> int strncmp(const char *p, const char *q, size_t n);

When parsing the *declaration* of this function, we lookup the
function name passed to constexpr_alias. We must find a constexpr
function with an identical signature. Then, at function invocation
substitution of strncmp, we instead substitute the body of
constexpr_strncmp.

This seems more direct (no redirection in the code), and it also
provides a specific advantage of allowing this to be easily added to
an existing declaration in a declaration-only header file without
impacting or changing the name of the runtime executed body or
definition.

I really like this approach.

-Chandler

PS: Sorry for the dup Clang folks, the GCC list doesn't like my mail
client.

(Likewise)

I'd be very happy with this solution. I come across precisely the problem
raised by Richard on a very regular basis and have different workarounds
for both gcc and clang. I'd love to see something "standard" emerging!

For my side, I'd still like some way of declaring a function to be used
only in a constexpr environment, meaning that the compiler gives an error
up front when a function is then used in a non-constexpr environment. The
above proposal will provide a link-time error if the non-constexpr function
is not defined, which is half-way there. Perhaps using the "unavailable"
attribute in conjunction with "constexpr_alias" would be the compile-time
solution...

Cheers

Andy

I'd love to see this is in the pre-Bristol mailing with implementation experience to back it up. :slight_smile: Nice job Richard and Chandler!

Howard

What do you do for member functions? Name the member with or without
qualification? Can the constexpr version be private?

Jeffrey

While throwing things out there, why not just optionally allow constexpr functions to coexist with non-constexpr functions of the same name, like inline and non-inline? That would solve both the original problem and Andy's problem.

  // Primary declaration.
  int strcmp(const char *p, const char *q, size_t n);

  // This declaration will be used in any constexpr contexts.
  // Non-constexpr contexts will use the declaration above (which may be inline)
  constexpr int strcmp(const char *p, const char *q, size_t n) {
    return !n ? 0 : *p != *q ? *p - *q : !*p ? 0 : strcmp(p+1, q+1, n-1);
  }

This avoids both new builtins and having to come up with a manually-mangled name for the alternate implementation. But it is most definitely a language change.

And for Andy's problem, you'd then get...something like this?

  constexpr int foo(int n) {
    return n + 1;
  }

  int foo(int n) = delete;

...but I have not thought about the problems in implementing this.
Jordan

So, what you are basically proposing is that constexpr would allow differenciating between two overloads much like const, volatile, & and && do for class methods ?

It’s a rather bold change but I agree it could integrate nicely in C++ since we already have the concept of overload, thus the machinery to deal with it and the users being used to it.

– Matthieu

The way to solve this issue is:

  1) to make most of the restrictions on constexpr functions evaporate.

   2) resist the temptation of introducing a primitive to query the state of
       the optimizers.

-- Gaby

Or remove most of the restrictions on constexpr functions that were necessary
only to win approval for C++11. This case doesn't strike me as one of
those where you fight complexity with even greater complexity.

Allow loops and the like in constexpr functions and be done with it. See my
comments on the C++ Extension Working Group when these (and related)
issues where brought up.

-- Gaby

While throwing things out there, why not just optionally allow constexpr functions to coexist with non-constexpr functions of the same name, like inline and non-inline?

I think this is fundamentally the wrong approach. Constexpr functions
aren't a different kind of function, they're just functions for which
are sometimes required to be evaluated at translation time. Hence
overload resolution should not depend on whether a function is
constexpr. Also, this approach tends to require two definitions of
constexpr functions even where the definitions are the same (if one
calls a constexpr overload and the other calls a non-constexpr
overload). What we want is just a mechanism to make function
invocation substitution take a different path if it encounters a
function which it can't process (because the runtime form of that
function is somehow written in a way that it can't handle).

Or remove most of the restrictions on constexpr functions that were necessary
only to win approval for C++11. This case doesn't strike me as one of
those where you fight complexity with even greater complexity.

Allow loops and the like in constexpr functions and be done with it. See my
comments on the C++ Extension Working Group when these (and related)
issues where brought up.

Yes, I completely agree, but I don't think this solves the whole
problem. There are certain constructs which we are unlikely to *ever*
permit in constexpr functions, such as (as an extreme case) inline
assembly. Where possible, we should share an implementation between
compile time and runtime. This proposal is for the exceptional cases
(which, over time, should become fewer and fewer), and as a stopgap
measure while we work towards the right solution.

I am just back to College Station. We should coodinate for the post-Portland
mailing. If I can have your current draft by tomorrow morning, that
would be great.

-- Gaby

Just to clarify, I had assumed that if the constexpr function is the only visible declaration, it would be used as the runtime implementation as well, as it currently is in C++11. I guess in C++11 you can forward-declare a constexpr function without making it constexpr, though, so it's not backwards-compatible.

Jordan