Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does the compiler optimize references to constant variables?

When it comes to the C and C++ languages, does the compiler optimize references to constant variables so that the program automatically knows what values are being referred to, instead of having to peek at the memory locations of the constant variables? When it comes to arrays, does it depend on whether the index value to point at in the array is a constant at compile time?

For instance, take a look at this code:

int main(void) {
    1:  char tesst[3] = {'1', '3', '7'};
    2:  char erm = tesst[1];
}

Does the compiler "change" line 2 to "char erm = '3'" at compile time?

like image 446
Måns Nilsson Avatar asked Dec 19 '16 14:12

Måns Nilsson


People also ask

What does compiler optimization do?

In computing, an optimizing compiler is a compiler that tries to minimize or maximize some attributes of an executable computer program. Common requirements are to minimize a program's execution time, memory footprint, storage size, and power consumption (the last three being popular for portable computers).

Does const get optimized?

const can't be optimized because there may be other mutable references to the same memory object. But for immutable references, there cannot be. It is possible to cast away const and immutable in D, but these are only allowed in system code, presumably where the programmer actually does know what he's doing.

Does const help the compiler?

Only const on an object definition actually makes it immutable. The main point of using const is not to assist the compiler in optimizations but to protect yourself from mistakes.

How does the C++ compiler optimize?

The C/C++ compiler compiles each source file separately and produces the corresponding object file. This means the compiler can only apply optimizations on a single source file rather than on the whole program. However, some important optimizations can be performed only by looking at the whole program.


2 Answers

I personally would expect the posted code to turn into "nothing", since neither variable is actually used, and thus can be removed.

But yes, modern compilers (gcc, clang, msvc, etc) should be able to replace that reference to the alternative with it's constant value [as long as the compiler can be reasonably sure that the content of tesst isn't being changed - if you pass tesst into a function, even if its as a const reference, and the compiler doesn't actually know the function is NOT changing that, it will assume that it does and load the value].

Compiling this using clang -O1 opts.c -S:

#include <stdio.h>

int main()
{
    char tesst[3] = {'1', '3', '7'};
    char erm = tesst[1];

    printf("%d\n", erm);
}

produces:

...

main:
    pushq   %rax
.Ltmp0:
    movl    $.L.str, %edi
    movl    $51, %esi
    xorl    %eax, %eax
    callq   printf
    xorl    %eax, %eax
    popq    %rcx
    retq

 ...

So, the same as printf("%d\n", '3');.

[I'm using C rather than C++ because it would be about 50 lines of assembler if I used cout, as everything gets inlined]

I expect gcc and msvc to make a similar optimisation (tested gcc -O1 -S and it gives exactly the same code, aside from some symbol names are subtly different)

And to illustrate that "it may not do it if you call a function":

#include <stdio.h>

extern void blah(const char* x);

int main()
{
    char tesst[3] = {'1', '3', '7'};
    blah(tesst);
    char erm = tesst[1];

    printf("%d\n", erm);
}


main:                                   # @main
    pushq   %rax
    movb    $55, 6(%rsp)
    movw    $13105, 4(%rsp)         # imm = 0x3331
    leaq    4(%rsp), %rdi
    callq   blah
    movsbl  5(%rsp), %esi
    movl    $.L.str, %edi
    xorl    %eax, %eax
    callq   printf
    xorl    %eax, %eax
    popq    %rcx
    retq

Now, it fetches the value from inside tesst.

like image 153
Mats Petersson Avatar answered Oct 21 '22 06:10

Mats Petersson


It mostly depends on the level of optimization and which compiler you are using.

With maximum optimizations, the compiler will indeed probably just replace your whole code with char erm = '3';. GCC -O3 does this anyway.

But then of course it depends on what you do with that variable. The compiler might not even allocate the variable, but just use the raw number in the operation where the variable occurs.

like image 32
Lundin Avatar answered Oct 21 '22 08:10

Lundin