Search code examples
ccompile-time-constant

Compile-time const and formal parameters


In the following example:

static inline void foo(const int varA)
{
  ...
  __some_builtin_function(varA);
  ...
}

int main()
{
  foo(10);
  return 0;
}

Is varA here considers as a compile-time constant?

Please note that I am working with C, not with C++.

Any link to the standard or such a reliable documentation describing compile-time constants and particularly their relation to the formal parameters would be really appreciated.


Solution

  • No, varA is not a compile time constant - it can certainly be different every time the function is called. Constants have a specific definition in the standard - some of the key details are touched on in this answer, or you can just read the standard for the official word.

    That said, what you may want to know is if the compiler will treat it as a constant, in cases where you call it with a constant value as in your example. The answer is "yes" for any decent compiler with optimization turned on. Call inlining and constant propagation are the magic that make this happen. The compiler will try to inline the call to foo and then substitute 10 for the argument, and will follow that recursively.

    Let's take a look at your example. I've slightly modified it to use return foo(10) in main so that the compiler doesn't optimize everything away entirely! I've also chosen gcc's __builtin_popcount as the unspecified function called by foo(). Check out this godbolt version of your program without optimization, compiled in gcc 6.2. The assembly looks like:

    foo(int):
            push    rbp
            mov     rbp, rsp
            mov     DWORD PTR [rbp-4], edi
            mov     eax, DWORD PTR [rbp-4]
            popcnt  eax, eax
            pop     rbp
            ret
    main:
            push    rbp
            mov     rbp, rsp
            mov     edi, 10
            call    foo(int)
            pop     rbp
            ret
    

    It's straightforward. Most of the foo() is just setting up the stack frame and (pointlessly) pushing edi (the varA argument) on to the stack.

    When we call foo() from main, we pass 10 as the argument. So clearly the fact that it's a constant hasn't helped.

    OK, let's compile this with a more realistic -O2 setting1. Here's what we get:

    main:
            mov     eax, 2
            ret
    

    That's it. The whole thing is just return 2, pretty much. So the compiler was definitely able to see that 10 is a constant value, and expand foo(10). Furthermore, it was able to evaluate foo(10) completely, calculating the popcount of 10 (0b1010 in binary) directly, without needing the popcount instruction at all, and just returning the answer 2.

    Also note that the compiler didn't even generate any code for foo() all. That's because it can see it is declared static inline2 so it can only be called from within this compilation unit, and that there are actually no callers that need the full function since the only call-site was inlined. So foo just disappears.

    So what the standard says about compile-time constants only helps in understanding what a compiler must do, and where certain expressions may be legally used, but it doesn't help much in understanding what a compiler will do in practice with optimization.

    The key here was that your method foo() is declared in the same compilation unit as its caller, so the compiler could inline and effectively optimize across the two functions. If it were in a separate compilation unit, this could not happen, unless you use some options such as link-time code generation.


    1As it turns out, pretty much any optimization setting here results in the same code, as the transformation is pretty trivial.

    2In fact, either of inline or static is enough to make the function local to the compilation unit. If you omit both, however, a body for foo() will be generated since it could be called from a separately compiled unit. With optimization, the body looks like:

    foo(int):
            xor     eax, eax
            popcnt  eax, edi
            ret