Can different optimization levels lead to functionally different code?

前端 未结 13 1855
[愿得一人]
[愿得一人] 2020-12-13 12:39

I am curious about the liberties that a compiler has when optimizing. Let\'s limit this question to GCC and C/C++ (any version, any flavour of standard):

Is it possi

相关标签:
13条回答
  • 2020-12-13 13:13

    Floating point calculations are a ripe source for differences. Depending on how the individual operations are ordered, you can get more/less rounding errors.

    Less than safe multi-threaded code can also have different results depending on how memory accesses are optimized, but that's essentially a bug in your code anyhow.

    And as you mentioned, side effects in copy constructors can vanish when optimization levels change.

    0 讨论(0)
  • 2020-12-13 13:16

    The -fstrict-aliasing option can easily cause changes in behavior if you have two pointers to the same block of memory. This is supposed to be invalid but is actually quite common.

    0 讨论(0)
  • 2020-12-13 13:18

    For C, almost all operations are strictly defined in the abstract machine and optimizations are only allowed if the observable result is exactly that of that abstract machine. Exceptions of that rule that come to mind:

    • undefined behavior don't has to be consistent between different compiler runs or executions of the faulty code
    • floating point operations may cause different rounding
    • arguments to function calls can be evaluated in any order
    • expressions with volatile qualified type may or may not be evaluated just for their side effects
    • identical const qualified compound literals may or may be not folded into one static memory location
    0 讨论(0)
  • 2020-12-13 13:21

    OK, my flagrant play for the bounty, by providing a concrete example. I'll put together the bits from other people's answers and my comments.

    For the purpose of different behaviour at different optimizations levels, "optimization level A" shall denote gcc -O0 (I'm using version 4.3.4, but it doesn't matter much, I think any even vaguely recent version will show the difference I'm after), and "optimization level B" shall denote gcc -O0 -fno-elide-constructors.

    Code is simple:

    #include <iostream>
    
    struct Foo {
        ~Foo() { std::cout << "~Foo\n"; }
    };
    
    int main() {
        Foo f = Foo();
    }
    

    Output at optimization level A:

    ~Foo
    

    Output at optimization level B:

    ~Foo
    ~Foo
    

    The code is totally legal, but the output is implementation-dependent because of copy constructor elision, and in particular it's sensitive to gcc's optimization flag that disables copy ctor elision.

    Note that generally speaking, "optimization" refers to compiler transformations that can alter behavior that is undefined, unspecified or implementation-defined, but not behavior that is defined by the standard. So any example that satisfies your criteria necessarily is a program whose output is either unspecified or implementation-defined. In this case it's unspecified by the standard whether copy ctors are elided, I just happen to be lucky that GCC reliably elides them pretty much whenever allowed, but has an option to disable that.

    0 讨论(0)
  • 2020-12-13 13:23

    This C program invokes undefined behavior, but does display different results in different optimization levels:

    #include <stdio.h>
    /*
    $ for i in 0 1 2 3 4 
        do echo -n "$i: " && gcc -O$i x.c && ./a.out 
      done
    0: 5
    1: 5
    2: 5
    3: -1
    4: -1
    */
    
    void f(int a) {
      int b;
      printf("%d\n", (int)(&a-&b));
    }
    int main() {
     f(0);
     return 0;
    }
    
    0 讨论(0)
  • 2020-12-13 13:23

    Two different C programs:

    foo6.c

    void p2(void);
    
    int main() {
        p2();
        return 0;
    }
    

    bar6.c

    #include <stdio.h>
    
    char main;
    
    void p2() {
        printf("0x%x\n", main);
    }
    

    When both modules are compiled into one excecutable with optimization levels one and zero, they print out two different values. 0x48 for -O1 and 0x55 for -O0

    Screenshot of terminal

    Here is an example of it working in my environment

    0 讨论(0)
提交回复
热议问题