Compile with option GCC -O2 generate another program

I heard that a C compiler with / without optimization can generate another program (compiling a program with optimization leads to its behavior differently), but I have never encountered such a case. Can anyone show a simple example of this?

+4
source share
9 answers

For gcc 4.4.4 this is different from -O0 and -O2

 void foo(int i) { foo(i+1); } main() { foo(0); } 

When optimizing these loops forever. Without optimization, a crash occurs (stack overflow!)

Other and more realistic options are usually time-dependent, vulnerable to variations in float accuracy or undefined behavior (uninitialized variables, heap / stack layout)

+5
source

If you look at the assembly generated by this code:

 int main () { int i = 1; while (i) ; return 0; } 

Unsigned -O2:

  .file "test.c" .text .globl main .type main, @function main: pushl %ebp movl %esp, %ebp subl $16, %esp movl $1, -4(%ebp) .L2: cmpl $0, -4(%ebp) jne .L2 movl $0, %eax leave ret .size main, .-main .ident "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3" .section .note.GNU-stack,"",@progbits 

With the -O2 flag:

  .file "test.c" .text .p2align 4,,15 .globl main .type main, @function main: pushl %ebp movl %esp, %ebp .L2: jmp .L2 .size main, .-main .ident "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3" .section .note.GNU-stack,"",@progbits 

With the -O2 flag, declaration i and the return value are omitted, and you only have a label that jumps to the same label to form an infinite loop.

Without the -O2 flag, you can clearly see the distribution of space i on the stack ( subl $16, %esp ) and initialization ( movl $1, -4(%ebp) ), as well as an estimate of the while condition ( cmpl $0, -4(%ebp) ) and the return value of the main function ( movl $0, %eax ).

+3
source

I saw this in programs that do a lot of math near the floating point accuracy limit. At the limit, arithmetic is not associative, so if the operations are performed in slightly different orders, you can get slightly different answers. In addition, if an 80-bit doubling floating point chip is used, but the results are stored in 64-bit variables with double precision, information may be lost, so the sequence of operations affects the results.

+2
source

Optimization uses assumptions about

  • the lack of smoothing of the pointer in some situations (which means that it can store information in registers without worrying about modification using another link).
  • instability of the overall memory location

Also, because of this, you may receive warnings, for example

  Type-punned pointers may break strict aliasing rules... (paraphrased) 

Warnings like these are designed to save you from headaches when your code develops subtle errors when compiling wit & optimization.

In general, in c and C ++

  • Be sure to know what you are doing.
  • never play it freely (don't press char ** directly on char *, etc.)
  • use const, volatile, throw (), obediently
  • trust the compiler provider (or developers) or create -O0

I'm sure I missed the epic, but you got a drift.

printed on my htc. Sorry typo or two

+2
source

The difference between optimization levels is usually associated with uninitialized variables. For instance:

 #include <stdio.h> int main() { int x; printf("%d\n", x); return 0; } 

When compiling with -O0 is 5895648 . When compiled with -O2 , every time I run it every time. e.g. -1077877612 .

The difference may be more subtle; Imagine you have the following code:

 int x; // uninitialized if (x % 10 == 8) printf("Go east\n"); else printf("Go west\n"); 

With -O0 this will output Go east and with -O2 , (usually) Go west .

+1
source

Examples of correct programs that have different results at different optimization levels can be found in error reporting reports and they will only work with certain versions of GCC.

But that would be easy to achieve by calling UB. However, it will no longer be the right program and may also generate different results with different versions of GCC (among other things, see mythology ).

+1
source

A rare case is when -O2 does not generate a different result, but does not use optimization.

 unsigned int fun ( unsigned int a ) { return(a+73); } 

Without optimization:

 fun: str fp, [sp, #-4]! .save {fp} .setfp fp, sp, #0 add fp, sp, #0 .pad #12 sub sp, sp, #12 str r0, [fp, #-8] ldr r3, [fp, #-8] add r3, r3, #73 mov r0, r3 add sp, fp, #0 ldmfd sp!, {fp} bx lr 

with optimization:

 fun: add r0, r0, #73 bx lr 

Even this function:

 void fun ( void ) { } 

Without optimization:

 fun: str fp, [sp, #-4]! .save {fp} .setfp fp, sp, #0 add fp, sp, #0 add sp, fp, #0 ldmfd sp!, {fp} bx lr 

With optimization:

 fun: bx lr 

If you declared everything mutable and created a need for a frame pointer, you can approach something where the same ones are not optimized and optimized. Similarly, if you compiled the debug version (not sure what the switch is), it will behave as if everything were mutable so that you can use the debugger to view variables in memory and in one step. which may also approach the same conclusion from the same input.

Also note that with or without optimization, it is expected that it will be different from the same source code from different compilers, and even different major versions of gcc produce different results. Trivial functions like the ones above usually give the same results when optimized by many compilers. But it can be expected that more complex functions with many other variables will give different results from compiler to compiler.

0
source

The following code displays Here i am when compiling without optimization, but nothing compiles with optimization.

The idea is that the function x() is listed as "clean" (without side effects), so the compiler can optimize it (my compiler is gcc 4.1.2 ).

 #include <stdio.h> int x() __attribute__ ((pure)); int x() { return printf("Here i am!\n"); } int main() { int y = x(); return 0; } 
0
source

One answer to this question may be as follows:

Each ANSI C compiler must support at least:

  • 31 parameters in function definition
  • 31 argument to function call
  • 509 characters in the source string
  • 32 levels of nested parentheses in an expression
  • The maximum value of long int cannot be less than 2,147,483,647 (i.e. long integers of at least 32 bits).

Source: C Expert Programming - Peter van den Linden

Perhaps the compiler supports 31 parameters in the function definition for -O0 and 35 for -O3, this is because there is no specification for this. Personally, I believe that this should be a design flaw and will greatly improve. But in a word: there are things in the compiler that are not limited by standards and can change in implementation, including optimization levels.

Hope this helps, as Mark Loeser said, you should be more specific in your question.

-1
source

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


All Articles