How does GCC optimize out an unused variable incremented inside a loop?

后端 未结 2 1419
清酒与你
清酒与你 2020-12-08 13:12

I wrote this simple C program:

int main() {
    int i;
    int count = 0;
    for(i = 0; i < 2000000000; i++){
              


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

    Compilers have a few tools at their disposal to make code more efficient or more "efficient":

    1. If the result of a computation is never used, the code that performs the computation can be omitted (if the computation acted upon volatile values, those values must still be read but the results of the read may be ignored). If the results of the computations that fed it weren't used, the code that performs those can be omitted as well. If such omission makes the code for both paths on a conditional branch identical, the condition may be regarded as unused and omitted. This will have no effect on the behaviors (other than execution time) of any program that doesn't make out-of-bounds memory accesses or invoke what Annex L would call "Critical Undefined Behaviors".

    2. If the compiler determines that the machine code that computes a value can only produce results in a certain range, it may omit any conditional tests whose outcome could be predicted on that basis. As above, this will not affect behaviors other than execution time unless code invokes "Critical Undefined Behaviors".

    3. If the compiler determines that certain inputs would invoke any form of Undefined Behavior with the code as written, the Standard would allow the compiler to omit any code which would only be relevant when such inputs are received, even if the natural behavior of the execution platform given such inputs would have been benign and the compiler's rewrite would make it dangerous.

    Good compilers do #1 and #2. For some reason, however, #3 has become fashionable.

    0 讨论(0)
  • 2020-12-08 14:00

    The compiler is even smarter than that. :)

    In fact, it realizes that you aren't using the result of the loop. So it took out the entire loop completely!

    This is called Dead Code Elimination.

    A better test is to print the result:

    #include <stdio.h>
    int main(void) {
        int i; int count = 0;
        for(i = 0; i < 2000000000; i++){
            count = count + 1;
        }
    
        //  Print result to prevent Dead Code Elimination
        printf("%d\n", count);
    }
    

    EDIT : I've added the required #include <stdio.h>; the MSVC assembly listing corresponds to a version without the #include, but it should be the same.


    I don't have GCC in front of me at the moment, since I'm booted into Windows. But here's the disassembly of the version with the printf() on MSVC:

    EDIT : I had the wrong assembly output. Here's the correct one.

    ; 57   : int main(){
    
    $LN8:
        sub rsp, 40                 ; 00000028H
    
    ; 58   : 
    ; 59   : 
    ; 60   :     int i; int count = 0;
    ; 61   :     for(i = 0; i < 2000000000; i++){
    ; 62   :         count = count + 1;
    ; 63   :     }
    ; 64   : 
    ; 65   :     //  Print result to prevent Dead Code Elimination
    ; 66   :     printf("%d\n",count);
    
        lea rcx, OFFSET FLAT:??_C@_03PMGGPEJJ@?$CFd?6?$AA@
        mov edx, 2000000000             ; 77359400H
        call    QWORD PTR __imp_printf
    
    ; 67   : 
    ; 68   : 
    ; 69   : 
    ; 70   :
    ; 71   :     return 0;
    
        xor eax, eax
    
    ; 72   : }
    
        add rsp, 40                 ; 00000028H
        ret 0
    

    So yes, Visual Studio does this optimization. I'd assume GCC probably does too.

    And yes, GCC performs a similar optimization. Here's an assembly listing for the same program with gcc -S -O2 test.c (gcc 4.5.2, Ubuntu 11.10, x86):

            .file   "test.c"
            .section        .rodata.str1.1,"aMS",@progbits,1
    .LC0:
            .string "%d\n"
            .text
            .p2align 4,,15
    .globl main
            .type   main, @function
    main:
            pushl   %ebp
            movl    %esp, %ebp
            andl    $-16, %esp
            subl    $16, %esp
            movl    $2000000000, 8(%esp)
            movl    $.LC0, 4(%esp)
            movl    $1, (%esp)
            call    __printf_chk
            leave
            ret
            .size   main, .-main
            .ident  "GCC: (Ubuntu/Linaro 4.5.2-8ubuntu4) 4.5.2"
            .section        .note.GNU-stack,"",@progbits
    
    0 讨论(0)
提交回复
热议问题