Constexpr evaluation and compiler optimization level - c++

see the following snippet:
struct config {
int x;
constexpr int multiply() const {
return x*3;
}
};
constexpr config c = {.x = 1};
int main() {
int x = c.multiply();
return x;
}
If I compile this with clang and -O0 I get a function call to multiply even though the object c and the function are marked constexpr. If I compile it with -O1 everything gets optimized as expected. Gcc on the other hand generates no call to multiply.
If I change the main to:
int main() {
constexpr auto y = c.multiply();
int x = y;
return x;
}
If I compile this with clang and -O0 I get not function call and the value 3 directly as stack variable. The -O1 result is the same as above.
So my question is: Does the constexpr evaluation depend on the compiler level? I would expect that in the example 1 the call to multiply would be constexpr and performed compile time. (like gcc does)
BR,
go2sh
See https://godbolt.org/z/WvPE5W77h

The Standard just requires that a call to constexpr is evaluated at compile time if the arguments are constexpr and the result must be constexpr due to context. Basically just forcing more restrictions on the author of the function, thus allowing it to be used in constexpr contexts.
Meaning y in second snippet forces evaluation at compile time. On the other hand, x in the first is an ordinary run-time call.
But the as-if rule applies here - as long as the observable effects of the program remain the same, the compiler can generate any instructions it wants. It can evaluate any function, even non-constexpr ones if it can do so - happens in practice often with constants propagation.
Yes, in general, higher optimization levels will inline more code and push more evaluation to the compile time. But "looking at the assembly" is not an observable effect in the sense above so there are no guarantees. You can use inline to give a hint for inlining the function instead of calling it (constexpr is inline by default but for other reasons...) but the compilers can ignore them.
Of course, the compiler can evaluate all constexpr functions with constexpr args at compile time, that is why they exist, why clang does not do so with -O0, I do not know.
If you need guaranteed compile-time evaluation, use consteval instead.

Related

Compiler Behavior differences with const_cast and apparent MSVC bug with constexpr evaluation

I've been experimenting with different forms of UB in order to better understand the current compiler inadequacies using constexpr evaluations to detect UB.
Note: There is normally no compiler requirement to emit diagnostics when UB occurs. However, this is not the case when executing compile time code such as consteval functions as shown here. Even there, there is an exception for library code where compilers are not required to emit diagnostics for UB. The question here does not use library code and compilers should emit diagnostics when encountering UB executing code at compile time.
I ran across an odd situation where modifying the underlying memory of an const int in automatic storage (aka stack) results in no warnings with CLANG, GCC, and MSVC but when evaluated in a constexpr function CLANG and GCC elicit the expected error while MSVC gives no error.
The function being evaluated casts a const int to a non const through it's address to attempt to change it's value from 1 to 2. Afterwards, the const value is added to that returned from a function called with a const int& that simply returns the argument value.
This is all UB, of course. However, all three compilers happily compile and run when not in a constexpr function. While UB, they are not required to emit diagnostics. And they interpret the result as the original const value when the variable is used directly but as the modified value when accessed through a reference.
But when running in compile time code, GCC and CLANG identify the UB as, for example:
modification of object of const-qualified type 'const int' is not allowed in a constant expression
MSVC produces no error or warning.
Since this uses no library code in the constexpr evaluation, I believe MSVC is in error not reporting the UB. Is this correct?
Compiler Explorer
#include <iostream>
// un-comment out the next line to evaluate at runtime
// #define consteval
consteval int ret_arg(const int& v) { return v; }
consteval int f()
{
const int i{ 1 };
*const_cast<int*>(&i) = 2;
return i + ret_arg(i);
}
int main()
{
std::cout << f() << "\n";
}
Run time UB w/o compiler errors for all compilers can be seen by commenting the "#define consteval" line at the top.

C++ constexpr function always evaluated at compile time?

If I want to have a function that behaves the same as a macro, that is, compute the value at compile time, can I use a constexpr function?
For example, can I replace the Foo macro by the foo function and still have a compile time evaluated result in all of the following cases:
#define FOO(x) (x + 2)
constexpr int foo(int x) {
return x + 2;
}
void doSomething(int a) { ... }
int main() {
int res1 = foo(3);
doSomething(foo(4));
const int res2 = foo(5);
return 0;
}
With C++20, consteval might be your friend here:
consteval int foo(int x) {
return x + 2;
}
int main() {
constexpr int r = foo(2);
}
By themselves, constexpr functions are not required to be evaluated at compile time. However, you can force them to be evaluated by assigning the return value to a constexpr variable:
doSomething(foo(4)); // foo(4) not guaranteed to be evaluated at compile time
constexpr auto result = foo(4); // foo(4) _is_ guaranteed to be evaluated at compile time
doSomething(result):
Also, a note about your question on macros. A macro definition has nothing to do with compile time evaluation. It's more akin to an always inline function.
It's impossible to detect if something was evaluated at runtime or compile-time using only the C++ itself, so the as-if rule allows the compiler to do whatever it wants.
Nothing stops a compiler from performing the addition in your macro at runtime, and nothing stops it from calculating even a non-constexpr function at compile-time (as long as it doesn't perform any IO, etc). It all depends on optimization settings and the sanity of the compiler.
Normally, in unoptimized builds, constexpr functions are executed at runtime unless the return value is used in a context that requires a compile-time constant. This includes initializing a constexpr variable from it, and your const int res2 implicitly becomes constexpr because its initializer is constexpr, so foo(5) should be called at compile-time.
In optimized builds, you can expect the compiler to do as much as possible at compile-time. (A function doesn't even have constexpr, as long as the function body is visible in the current translation unit, or if link-time optimizations are enabled.)

Constexpr functions not called at compile-time if result is ignored

I was investigating some rather weird code-coverage results of constexpr functions (compile-time calls can't get instrumented by the code-coverage tool I use), and noticed that some constexpr functions were getting called as runtime functions, if the results of the function call were not stored.
It seems that, for constexpr functions or methods, if you store the results of the call (either in a runtime variable [emphasis!!!] or a constexpr variable), the call is a compile-time call (as long as the parameters are compile-time). If you ignore the results, the call is a runtime call. This has nothing to do with my code-coverage tool; the behavior seems to be repeatable in the simple example below.
You could argue that since constexpr functions cannot have side-effects, it doesn't matter what the compiler does if you don't return / use a result. I'd think that for efficiency, the compiler would still make whatever it can be constexpr, but that's neither here nor there... What I'm wondering is if this is even defined behavior.
Is this a portable way to guarantee your constexpr functions will be invoked as runtime??? There aren't a ton of uses for that, but one use is: if you want to "take credit for tests that were called on constexpr functions" in code coverage, by just calling the same function at the end of your unit test, and ignoring the result, so that they get instrumented.
There are other ways to force a function to get called as runtime, I know, I'm mostly curious about what's going on here. It was very unexpected when I first saw it. Unless this is portable, I'll probably just call my constexpr methods through a runtime object (which seems to do the trick even for static methods), or through a runtime function pointer.
See example below. Live demo: https://onlinegdb.com/rJao0RNGP
// Modified from https://stackoverflow.com/a/40410624/12854372
extern bool no_symbol;
struct ContextIsConstexpr {
size_t s;
constexpr ContextIsConstexpr() : s(1) {}
constexpr void operator()() {
auto ignore = s ? 1 : no_symbol;
}
};
constexpr bool tryIgnoringResult()
{
ContextIsConstexpr()();
return true;
}
constexpr void thereIsNoResult() {
ContextIsConstexpr()();
}
int main()
{
constexpr auto result1 = tryIgnoringResult(); // OK, compile-time
auto result2 = tryIgnoringResult(); // OK, compile-time
// tryIgnoringResult(); // Unexpected, runtime!
// thereIsNoResult(); // Unexpected, runtime!
}
It might be confusing, but constexpr functions should be called at compile time only in constexpr contexts (assignation to constexpr variable, use for array size or template parameter, ...).
In regular context, functions are called at runtime. Optimizer might resolve that function at compile time (as for any other functions following the as-if rule). constexpr is indeed a good hint for optimization to happen.
You could argue that since constexpr functions cannot have side-effects
They can have side effect, see following valid example:
constexpr int f(int i)
{
if (i == 0) return 0;
std::cout << i << std::endl;
return i;
}
int main()
{
[[maybe_unused]] constexpr int zero = f(0); // Compile time
f(42); // runtime
}
Demo

constexpr constructor gives a different result when evaluated at compile time by GCC

The constructor uses a function taking a reference and returning by value while repeatedly modifying a data member:
constexpr int vv(int x) {return x;}
constexpr int & rr(int & x) {return x;}
constexpr int rv(int & x) {return x;}
constexpr struct S {
int x {0};
template<typename F> constexpr S(F f) {x = f(x) + 1; x = f(x) + 1;}
} s(rv); // s.x is 1 if function rv is used, 2 otherwise.
static_assert(s.x == 2, "");
It is only the function rv which gives the unexpected result when used in the constructor. If vv or rr is passed instead then s.x is 2 as expected.
I noticed the behavior on GCC 5.4.1 ARM and it appears to be the same in all versions of GCC that support C++14. Clang gives the expected result of 2 in all cases. Neither GCC nor Clang give any warnings with Wall and Wextra enabled.
Is this example valid C++14 and a bug in GCC? I read the list of restrictions on constant expressions in the standard and see nothing obvious it violates but I'm not sure I understand all the technical details.
Your code is certainly intended to be well-formed. I believe GCC performs a revised form of memoization; back in C++11, objects could not be modified in constant expressions, hence it was perfectly fine to cache function results. In fact, it's quite apparent from a few Clang bug reports that GCC did exactly that, see e.g. here:
Clang's constexpr implementation does not perform caching. In C++14,
it's not even clear whether caching will be possible, so it seems like
a waste of time to invest in it now.
They obviously had to introduce some additional analysis in C++14, and they made a mistake: they assumed that any constexpr object's subobject cannot be modified during its lifetime. That clearly doesn't hold during the period of construction of the enclosing object. If we use a temporary instead of x, the code compiles. If we put the whole thing into a constexpr function and remove s's constexpr specifier, it works.
Funnily enough, rr's and vv's results are also cached, but the same reference being returned works fine, and call by value avoids that problem altogether :)
Reported as 79520.

Is constexpr a "hint" (like inline) or "a binding request" to the compiler?

Is constexpr an indicator for the compiler or does it mandate a behaviour ?
The example at hand is the following :
template<typename T>
std::size_t constexpr getID() { return typeid(T).hash_code(); }
hash_code is a runtime constant, yet this snippet would compile even though a compile time evaluation is requested with constexpr. Only after the return value is used where a compile time constant is expected, would we get noticed that this is not usable as a constexpr function.
So is constexpr a "hint" (much like the inline keyword) or "a binding request" to the compiler ?
Is constexpr a “hint” (like inline) or “a binding request” to the compiler?
It is neither. Forget about when it is evaluated. Everything (with a few minor exceptions, notably involving volatile) is evaluated whenever the compiler deems it necessary to produce the behaviour of the C++ abstract machine. There isn't much else to say about when things are evaluated.
The compiler is free to produce code that evaluates what would be constant expressions at runtime if that doesn't produce a different behaviour. It is free to produce code that evaluates things not marked constexpr at compile-time if it has the smarts.
If not about compile-time vs runtime, what is constexpr about, then?
constexpr allows things to be treated as constant expressions. Anything marked constexpr must have the possibility of producing a constant expression in some way.
In the case of functions, they can be able to produce constant expressions with some arguments but not others. But as long as there is some set of arguments that can result in a constant expression, a function can be marked constexpr. If such a set of arguments is used in a function call, that expression is a constant expression. Does that mean it is evaluated at compile-time? See above. It's evaluated when the compiler deems appropriate. The only thing it means is that you can use it in a context requiring a constant expression.
For variables, either they are constant expressions or not. They have no arguments, so if constexpr they always have to be initialised with constant expressions.
TL;DR: constexpr is about tagging things as being usable in constant expressions, not about deciding when to evaluate them.
With that out of the way, it appears your function template is ill-formed. There is no set of arguments that could result in a constant expression. The standard doesn't require a diagnostic for this, though.
From the C++11 Wiki page:
If a constexpr function or constructor is called with arguments which
aren't constant expressions, the call behaves as if the function were
not constexpr, and the resulting value is not a constant expression.
Likewise, if the expression in the return statement of a constexpr
function does not evaluate to a constant expression for a particular
invocation, the result is not a constant expression.
The constexpr specifier thus expresses the possibility to evaluate something at compile time and is subject to some restrictions when used.
For your particular snippet it seems to me that the C++11 constraint:
exactly one return statement that contains only literal values,
constexpr variables and functions
is not fulfilled, as hash_code is defined to be:
size_t hash_code() const;
In this case the standard draft n3242 says:
For a constexpr function, if no function argument values exist such
that the function invocation substitution would produce a constant
expression (5.19), the program is ill-formed; no diagnostic required.
I believe your example fits here.
constexpr functions can be used to evaluate compile time constants. So it is possible to use it like:
constexpr int func(int a) { return a+2; }
char x[func(10)];
If func is called during runtime, the compiler can evaluate this expression before if possible. But that is not a must but normally done if the input is also const.
It is also important to have constexpr constructors. This is the only chance to get non POD classes constexpr objects.
class Point
{
private:
int x;
int y;
public:
constexpr Point( int _x, int _y) : x(_x), y(_y) {}
constexpr int GetX() const { return x; }
};
constexpr Point p{1,2};
int main()
{
char i[p.GetX()];
return 0;
}
The complete answer to your question has two aspects:
A constexpr function can only be evaluated at compile-time when all
arguments can be evaluated at compile-time. It can still be used as
a normal function which is evaluated at runtime.
An constexpr variable must be initialized with a value evaluated at compile-time.
The compiler has to raise an error if it cannot do this.
You could assign the hash code to a constexpr variable and then get a compiler output:
#include <typeinfo>
#include <array>
template<typename T>
std::size_t constexpr getID() {
return []() {constexpr size_t id = typeid(T).hash_code(); return id;}(); }
int main() {
// both statement generate compiler errors
//std::array<int, typeid(int).hash_code()> a;
//constexpr size_t y = typeid(int).hash_code();
size_t x = getID<int>();
}