1

Say you have (for reasons that are not important here) the following code:

 int k = 0;
 ...  /* no change to k can happen here */
 if (k) { 
   do_something();
 }

Using the -O2 flag, GCC will not generate any code for it, recognizing that the if test is always false.

I'm wondering if this is a pretty common behaviour across compilers or it is something I should not rely on.

Does anybody knows?

Remo.D
  • 16,122
  • 6
  • 43
  • 74
  • 5
    How will you rely on it? – John Kugelman Jul 09 '19 at 12:54
  • 1
    There is no way I would count on *every* compiler to recognize this optimization, much less carry it out. – Scott Hunter Jul 09 '19 at 12:54
  • @JohnKugelman In the sense that I would rely on the optimizer to remove possibly "dead code". – Remo.D Jul 09 '19 at 12:56
  • @Remo.D Usually compilers at least issue a warning. – Vlad from Moscow Jul 09 '19 at 12:57
  • @ScottHunter Possibily not "every compiler", I agree. But what about "many compilers"? If there are standard optimization techniques aimed at this case, there is a good chance they'll be used. – Remo.D Jul 09 '19 at 12:59
  • Can you flag `k` as `const`? That'd make it near certain the `if` statement would be optimized out. – John Kugelman Jul 09 '19 at 13:01
  • If it isn't all compilers, you can't rely on it. – Scott Hunter Jul 09 '19 at 13:07
  • 4
    The real question is: Why do you care? This smells like an XY-problem. – klutt Jul 09 '19 at 13:21
  • Can you use `#if 0` instead ? All compilers (should) guarantee removing that. – Sander De Dycker Jul 09 '19 at 13:22
  • @SanderDeDycker Sure, that will make the preprocessore remove it. The compiler will not even see the code :) – Remo.D Jul 09 '19 at 14:02
  • @klutt Funny how everyone is suspicious about motivations :) I should have phrased it differently. Something like "optimiziation techniques and their implementation in C compilers" :) – Remo.D Jul 09 '19 at 14:05
  • Unless `k` is marked `volatile`, i would think that'd be a pretty obvious optimization. But if you do no optimization (eg -O0), i would hope that code would be generated from it. – yhyrcanus Jul 09 '19 at 14:15
  • 1
    @Remo.D : your question just feels odd. If you want to rely on non-inclusion of code, just `#if 0` it away. Otherwise, leave it up to the compiler. If you want to know if a specific compiler performs a certain type of optimization, compiler documentation and/or analyzing generated assembly should help. If you want to know if eliminating unreachable code is a common optimization, then I'd say yes, since it's relatively easy to do. – Sander De Dycker Jul 09 '19 at 14:16
  • 1
    @Remo.D It's very unlikely that this will affect performance in a significant way. After all, dead code does not run. So the only possible impact is an unnecessary branch. On the other hand, removing it will clean up the code which is a big plus. – klutt Jul 09 '19 at 14:26
  • 1
    @Remo.D Relying on the compiler removing dead code is practically pointless. Even in development regimes that require provision of evidence that dead code is never executed (e.g. high-criticality development) it is usually necessary to provide evidence that the *source* does not contain dead code. The compiler is only relevant in consideration of dead code if known to generate code that is never executed (e.g. exception handling code in circumstances where analysis has confirmed that no exception will be thrown) - and in most such cases, the compiler can be configured to not emit that code – Peter Jul 09 '19 at 15:04
  • 1
    I'm guessing that motivation is you can change the value of `k` at compile time to enable some extra checking, but don't like the idea of using `#ifdef` to do this? Template processing has forced compiler writers to up their game significantly for detecting unreachable code, so I would say you should expect it to work for compiled code, but remember, not all code is compiled! – Gem Taylor Jul 09 '19 at 15:16
  • Well, you should rely on it if you are making a program to rely on it (assume a program whose intention is to see if the compiler has eliminated that code, for example) – Luis Colorado Jul 10 '19 at 07:50

2 Answers2

2

There is no pretty common behaviour across compilers. But there is a way to explore how different compilers acts with specific part of code. Compiler explorer will help you to answer on every question about code generation, but of course you must be familiar with assembler language.

Ivan Kotov
  • 23
  • 3
2

Dead code elimination in this case is trivial to do for any modern optimizing compiler. I would definitely rely on it, given that optimizations are turned on and you are absolutely sure that the compiler can prove that the value is zero at the moment of check.

However, you should be aware that sometimes your code has more potential side effects than you think.


The first source of problems is calling non-inlined functions. Whenever you call a function which is not inlined (i.e. because its definition is located in another translation unit), compiler assumes that all global variables and the whole contents of the heap may change inside this call. Local variables are the lucky exception, because compiler knows that it is illegal to modify them indirectly... unless you save the address of a local variable somewhere. For instance, in this case dead code won't be eliminated:

int function_with_unpredictable_side_effects(const int &x);
void doit() {
  int k = 0;
  function_with_unpredictable_side_effects(k);
  if (k)
    printf("Never reached\n");
}

So compiler has to do some work and may fail even for local variables. By the way, I believe the problem which is solved in this case is called escape analysis.


The second source of problems is pointer aliasing: compiler has to take into account that all sort of pointers and references in your code may be equal, so changing something via one pointer may change the contents at the other one. Here is one example:

struct MyArray {
  int num;
  int arr[100];
};
void doit(int idx) {
  MyArray x;
  x.num = 0;
  x.arr[idx] = 7;
  if (x.num)
    printf("Never reached\n");
}

Visual C++ compiler does not eliminate the dead code, because it thinks that you may access x.num as x.arr[-1]. It may sound like an awful thing to do to you, but this compiler has been used in gamedev area for years, and such hacks are not uncommon there, so the compiler stays on the safe side. On the other hand, GCC removes the dead code. Maybe it is related to its exploitation of strict pointer aliasing rule.


P.S. The const keywork is never used by optimizer, it is only present in C/C++ language for programmers' convenience.

stgatilov
  • 5,333
  • 31
  • 54
  • that P.S. is not completely true, compiler can assume that a `const` number is actually a `const`, and use it as the literal it represents, in case you don't use the `&` operator with it, and this can be considered an optimisation. – Luis Colorado Jul 10 '19 at 07:54