Wednesday, April 6, 2022

[SOLVED] GCC/Clang not optimising static global variable

Issue

GCC does not seem to be able to trace and optimize programs that read/write global variables in C/C++, even if they're static, which should allow it to guarantee that other compilation units won't change the variable.

When compiling the code

static int test = 0;

int abc() {
  test++;
  if (test > 100) \
    return 123;
  --test;
  return 1;
}

int main() {
  return abc();
}

with the flags -Os (to produce shorter and more readable assembly) and -fwhole-program or -flto using GCC version 11.2 I would expect this to be optimized to return 1, or the following assembly:

main:
        mov     eax, 1
        ret

This is in fact what is produced if test is a local variable. However, the following is produced instead:

main:
        mov     eax, DWORD PTR test[rip]
        mov     r8d, 1
        inc     eax
        cmp     eax, 100
        jle     .L1
        mov     DWORD PTR test[rip], eax
        mov     r8d, 123
.L1:
        mov     eax, r8d
        ret

Example: https://godbolt.org/z/xzrPjanjd

This happens with both GGC and Clang, and every other compiler I tried. I would expect modern compilers to be able to trace the flow of the program and remove the check. Is there something I'm not considering that may allow something external to the program to affect the variable, or is this just not implemented in any compilers yet?

Related: Why gcc isn't optimizing the global variable? but the answer given there mentions external functions and threads, neither of which apply here


Solution

I think you are asking a little bit too much for most of the compilers. While the compiler is probably allowed to optimize the static variable away according to the as-if rule in the standard, it is apparently not implemented in many compilers like you stated for GCC and Clang.

Two reasons I could think of are:

  • In your example obviously the link time optimization decided to inline the abc function, but did not optimize away the test variable. For that, an analysis of the read/write semantics of the test variable would be needed. This is very complex to do in a generic way. It might be possible in the simple case that you provided, but anything more complex would be really difficult.

  • The use case of such optimizations is rare. Global variables are most often used to represent some shared global state. I makes no sense to optimize that away. The effort for implementing such a feature in a compiler/linker would be large compared to the benefit for most programs.

Addition
Apparently GCC optimizes away the variable if you read-only access it. If you compile the following:

static int test = 0;

int abc() {
  int test_ = test;
  test_++;
  if (test_ > 100) \
    return 123;
  --test_;
  return 1;
}

int main() {
  return abc();
}

Where you read the variable once into a local variable and never write to it, it gets optimized away to:

main:
    mov     eax, 1
    ret

(See here for a demo)
However using such a local variable would defeat the whole point of having a global variable. If you never write to it, you might as well define a constant.



Answered By - Jakob Stark
Answer Checked By - Katrina (WPSolving Volunteer)