📜 ⬆️ ⬇️

Const and optimization in C

Today , / r / C_Programming was asked about the effect of const in C on optimization. I have heard many times the variants of this question during the last twenty years. Personally, I blame the entire naming const .


Consider this program:


 void foo(const int *); int bar(void) { int x = 0; int y = 0; for (int i = 0; i < 10; i++) { foo(&x); y += x; // this load not optimized out } return y; } 

The foo function accepts a pointer to const, which promises on behalf of foo that the value of x will not be changed. It may seem that the compiler can assume that x always zero, and therefore also y .


However, if we look at the assembler code generated by several different compilers, we will see that x loaded at each iteration of the loop. This is what gcc 4.9.2 s produced with -O3, with my comments:


 bar: push rbp push rbx xor ebp, ebp ; y = 0 mov ebx, 0xa ;    i sub rsp, 0x18 ; allocate x mov dword [rsp+0xc], 0 ; x = 0 .L0: lea rdi, [rsp+0xc] ;  &x call foo add ebp, dword [rsp+0xc] ; y += x ( ?) sub ebx, 1 jne .L0 add rsp, 0x18 ; deallocate x mov eax, ebp ;  y pop rbx pop rbp ret 

clang 3.5 (with -fno-unroll-loops) gave roughly the same thing, only ebp and ebx were swapped, and the calculation of &x removed from the loop in r14 .


Are both compilers unable to use this useful information? Unless if foo changes x , it will not be undefined behavior? Oddly enough, the answer is no. In this situation , this will be the absolutely correct definition of foo .


 void foo(const int *readonly_x) { int *x = (int *)readonly_x; // cast away const (*x)++; } 

It is important to remember that const does not mean constant . Take note of the fact that this is the wrong name. This is not a tool for optimization. It is needed to inform the programmer - and not the compiler - as a tool to help catch a certain class of errors at compile time. I like it when it is used in the API because it tells how the function will use its arguments, or how the caller should handle the returned pointers. Usually it is not strict enough to change the behavior of the compiler.


Despite what I said, sometimes the compiler can use const for optimization. In the C99 specification, in §6.7.3¶5, there is one sentence about this:


         const   lvalue   const,  . 


The original x was without a const modifier, so this rule did not apply. And there is no rule against casting to a non- const type in order to change an object that is not itself a const . This means that the foo behavior above is not an undefined behavior for this call . Note that the foo uncertainty depends on how it was called.


With one change in bar I can make this rule applicable, allowing the optimizer to work.


  const int x = 0; 

The compiler can now assume that changing x in foo is an undefined behavior, and therefore never happens . Anyway, basically so optimizer C talks about your programs. The compiler can assume that x never changes, allowing it to optimize both loading in each iteration, and y .


 bar: push rbx mov ebx, 0xa ;   i sub rsp, 0x10 ; allocate x mov dword [rsp+0xc], 0 ; x = 0 .L0: lea rdi, [rsp+0xc] ;  &x call foo sub ebx, 1 jne .L0 add rsp, 0x10 ; deallocate x xor eax, eax ;  0 pop rbx ret 

The load disappears, y disappears, and the function always returns zero.


Curiously, the specification allows the compiler to go even further. It can place x somewhere out of the stack, even in read-only memory. For example, he can produce such a transformation:


 static int __x = 0; int bar(void) { for (int i = 0; i < 10; i++) foo(&__x); return 0; } 

Or on x86-64 ( -fPIC, small memory model ), where it turns out to get rid of a few more instructions:


 section .rodata x: dd 0 section .text bar: push rbx mov ebx, 0xa ;   i .L0: lea rdi, [rel x] ;  &x call foo sub ebx, 1 jne .L0 xor eax, eax ;  0 pop rbx ret 

Neither clang nor gcc go so far, apparently because it is more dangerous for poorly written code.


Even with a special rule on const rule, use const for yourself and your fellow programmers. Let the optimizer decide for itself what is constant and what is not.


')

Source: https://habr.com/ru/post/307266/


All Articles