Search code examples
cassemblyx86decompiling

Why does GCC move variables to a temporary location before assigning them?


When looking at some decompiled C code I saw this:

movl    -0xc(%rbp), %esi
movl    %esi, -0x8(%rbp)

This corresponds to this C code:

x = y;

This got me thinking: how come gcc moves y to %esi and then move %esi to x instead of just moving y to x directly?


This is the entire C and decompiled code, if it matters:

C

int main(void) {
    int x, y, z;

    while(1) {
        x = 0;
        y = 1;
        do {
            printf("%d\n", x);

            z = x + y;
            x = y;
            y = z;
        } while(x < 255);
    }
}

Decompiled

pushq    %rbp
movq     %rsp, %rbp
subq     $0x20, %rsp
movl     $0x0, -0x4(%rbp)

movl     $0x0, -0x8(%rbp) ; x = 0
movl     $0x1, -0xc(%rbp) ; y = 1

; printf
leaq     0x56(%rip), %rdi
movl     -0x8(%rbp), %esi
movb     $0x0, %al
callq    0x100000f78

; z = x + y
movl     -0x8(%rbp), %esi  ; x -> esi
addl     -0xc(%rbp), %esi  ; y + esi
movl     %esi, -0x10(%rbp) ; z = esi

; x = y
movl     -0xc(%rbp), %esi
movl     %esi, -0x8(%rbp)

; y = z
movl     -0x10(%rbp), %esi
movl     %esi, -0xc(%rbp)

movl     %eax, -0x14(%rbp) ; not sure... I believe printf return value?
cmpl     $0xff, -0x8(%rbp) ; x < 255
jl       0x100000f3d ; do...while(x < 255)
jmp      0x100000f2f ; while(1)

Solution

  • Most x86 instructions (other than some specialized instructions such as movsb) can only access one memory location. Therefore a move from memory to memory requires going through a register with two mov instructions.

    The mov instruction can be used in the following ways:

    mov mem, reg
    mov reg, mem
    mov reg, reg
    mov reg, imm
    mov mem, imm
    

    There is no mov mem, mem.

    Note that if you had compiled with optimizations, the variables would be placed in registers so this wouldn't be an issue.