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
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.
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.
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:
volatile
qualified
type may or may not be evaluated just
for their side effectsconst
qualified compound literals may or may be not folded into one static memory locationOK, 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.
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;
}
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