1

Is there a standard way to find out what the compiler does to constexpr functions?

(Side note: For debug, every constexpr function is deferred to runtime by default. Why is this sensible? Is there a way to influence this?)

For release it depends on the context. Obviously, for small test settings you can easily inspect the generated machine code, but this cannot be the way to go for a real project.

My current 'workaround' (VC++) is to break somewhere, go to my constexpr function and (try to) inspect the disassembly. If none is there, I conclude that it was all done at compile time. But it is not 100% reliable this way. (Optimization, etc.) Only the other way around is certain: If I do find disassembly (and can even break there), I know that it was NOT done at compile time.

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
old123987
  • 121
  • 6
  • 2
    Here's a better question: why do you need to? Let the compiler do its job and optimize where it sees fit. `constexpr` is not a tool of optimization; it's a tool to allow you to write code in places you could not do so previously. To write code that generates constant expressions in contexts which require constant expressions. – Nicol Bolas Aug 29 '18 at 13:52
  • Do you need it during the run-time, or is compile time failure fine? It's possible to assign to a `constexpr` variable and the compiler will error out if the function is evaluate in non-constexpr context. But I am not sure i that helps in your case. – luk32 Aug 29 '18 at 13:53
  • @NicolBolas: There is always a better question. – old123987 Aug 29 '18 at 13:57
  • I want to make sure that it is evaluated at compile-time. So, yes, it IS a performance optimization! – old123987 Aug 29 '18 at 13:57
  • 1
    Well, you can try to force it, see "*[constexpr does not work/apply inside function call](https://stackoverflow.com/questions/52004389/constexpr-does-not-work-apply-inside-function-call/52004692#52004692)*". But in the end, there isn't *quite* a way to force it, only make not doing so too complicated for most compilers. And aside from runtime and assessing the compiled cCode, there's no way to be sure. – Deduplicator Aug 29 '18 at 14:07
  • 2
    @Deduplicator I knew I saw it not-so-long-ago, but couldn't find it. Turns out it's same asker. I guess it makes my answer obsolete. – luk32 Aug 29 '18 at 14:11
  • 1
    @old123987: "*So, yes, it IS a performance optimization!*" Well, that's not what the feature is for. Just like `inline` doesn't mean that the compiler will inline the function, `constexpr` does not guarantee the compiler will only evaluate the function at compile-time. – Nicol Bolas Aug 29 '18 at 14:19
  • @Deduplicator: Which is the preferred way of "assessing"? That exacly is my question. – old123987 Aug 29 '18 at 14:41
  • 1
    It looks like there is a nearly- duplicate question by yourself [here](https://stackoverflow.com/questions/52004389). Please explain why are you not satisfied by the accepted answer. – n. m. could be an AI Aug 29 '18 at 16:26
  • @n.m.: in my opinion, it's not a duplicate. Same subject, but a very different question. The accepted answer there doesn't answer the question "How to tell if `constexpr` is evaluated at compile time (without manual inspection)?" at all. – geza Aug 29 '18 at 17:48
  • @geza it does (you can't, the as-if-rule always allows evaluation at runtime). – n. m. could be an AI Aug 29 '18 at 18:13
  • @n.m.: That's not an answer to this question. As-if rule doesn't matter here at all. For example, a compiler could have an option, which puts a warning, if it cannot evaluate an appropriately marked constexpr function compile-time. Just like there are compilers, which can warn, if an inline function won't be inlined. OP wants to solve a problem regarding constexpr functions. As OP cannot force compile-time evaluation easily (the other question), OP wants to know how to analyse easily the resulting compilation whether the compiler used compile-time evaluation (this question). – geza Aug 29 '18 at 18:23

2 Answers2

3

It's impossible. constexpr does not guarantee value inlining, you can see this manipulating optimization level here: https://godbolt.org/z/dAoiM-

Only since -O2 everything is inlined and the structure gets dissolved. Below that compiler happily uses runtime evaluation even for code used in constexpr context.

There are no standard language tools to inquire whether compiler applies particular optimization. It all boils down to the as-if rule. If the code behaves the same compiler can do anything to it. The only exception is mandatory RVO and other RVOs (they are allowed to changed observed behaviour.)

That being said. The constexpr is a useful hint. In the linked example if one removes constexpr specifiers even O3 (on recent clang and gcc) does not manage to remove the map.

It's worthwhile optimization-wise to write constexpr functions and data structure, making sure the compiler can optimize, though you cannot force it to.

You can force function to be evaluated in constexpr context, and you can also guard non-constexpr paths to throw, to prevent guaranteed run-time evaluation.

#include <iostream>
#include <vector>
using namespace std;

constexpr int f(int el) {
    return el > 0 ? el : throw "error";
}

int main() {
    // constexpr auto r = f(-1); // #1 compiler errors that throw is forbidden in  
                                 // constexpr, so it went into a non-constexpr path
                                 // and failed

    constexpr auto r = f(1);     // #2 fine - has to be interpreted in constexpr context
    cout << f(1) << '\n';        // #3 fine - can be interpreted in both contexts

    try {
        cout << f(-1) << '\n'; // # 4 // throws - i.e. runtime evaluation
    }
    catch (const char* e) {
        cout << e << '\n';
    }
    return 0;
}
luk32
  • 15,812
  • 38
  • 62
  • So how do you figure out, if #3 is REALLY evaluated at compile time. It could be, but it is still up to the compiler. – old123987 Aug 29 '18 at 14:11
  • 1
    You don't have compiler guarantees enforced on the compiler by the c++ standard except for mandatory copy elision, so it's always up to compiler. Wrap `f` assignment to a `constexpr` into lambda and return the value, that enforces `constexpr` path. But this is only `constexpr` context. FWIW compiler can throw in bogus NOPs and loops into your code to slow it down based on the "as-if" rule, so there is never a guarantee it emitted concrete assembly or used particular optimization and there are not language level tools to check it. – luk32 Aug 29 '18 at 14:16
  • @old123987 BTW it would be extremely weird if compiler had to be able to evaluate return value at a compile time, and didn't do so when it can. Even in case #2 it's up to the compiler to use the evaluated value or emit the code to calculate it. Binding the return value to a `constexpr` variable is to make sure that you as a programmer use a path that is possible to be evaluated at compile time, but it does not force the optimization. In #2 you know that if code compiles compiler *can* optimize it, not that it *will*. In #3 you don't know if it can. That's the only difference. – luk32 Aug 29 '18 at 14:28
  • Did you make your test with any optimizations? Because evidently, without optimization many compilers only assure feasibility, but still deferr to runtime. – Deduplicator Aug 29 '18 at 14:53
  • @Deduplicator Yes. [I did so on a slightly more complicated case, taken from SO.](https://godbolt.org/z/dAoiM-) It does agree with what you say. Only since `-O2` everything is inlined and the map gets dissolved. Though `constexpr` does not guarantee anything. On the other hand, if you remove `constexpr` specifiers, even on `O3` clang and gcc fail to optimize the map. So the `constexpr` does help the optimizers. It seems to be a useful hint rather than only semantic changer. (Some might argue that compiler would optimize things just as good regardless of `constexpr`, it's not the case.) – luk32 Aug 29 '18 at 15:23
  • @Deduplicator I reworded the answer and incorporated those conclusions into the answer. Hopefully it's more substantial and not just a reiteration of yours. – luk32 Aug 29 '18 at 15:55
1

As we now have the current C++20 standard, we can use consteval.

From the docs:

consteval - specifies that a function is an immediate function, that is, every call to the function must produce a compile-time constant. doc

This will fix the problem with constexpr.

Klaus
  • 24,205
  • 7
  • 58
  • 113