В x86, почему у меня одна и та же команда два раза, с обратными операндами?

Я делаю несколько экспериментов с x86 asm, пытаясь увидеть, как общие конструкции языков собираются в сборку. В моем текущем эксперименте я пытаюсь увидеть, как ориентируются указатели языка C на регистро-косвенную адресацию. Я написал программу hello-world, похожую на указатель:

#include <stdio.h>

int
main (void)
{
    int value    = 5;
    int *int_val = &value;

    printf ("The value we have is %d\n", *int_val);
    return 0;
}

и скомпилировал его в следующий asm, используя: gcc -o pointer.s -fno-asynchronous-unwind-tables pointer.c: [1] [2]

        .file   "pointer.c"
        .section        .rodata
.LC0:
        .string "The value we have is %d\n"
        .text
        .globl  main
        .type   main, @function
main:
;------- function prologue
        pushq   %rbp
        movq    %rsp, %rbp
;---------------------------------
        subq    $32, %rsp
        movq    %fs:40, %rax
        movq    %rax, -8(%rbp)
        xorl    %eax, %eax
;----------------------------------
        movl    $5, -20(%rbp)   ; This is where the value 5 is stored in `value` (automatic allocation)
;----------------------------------
        leaq    -20(%rbp), %rax ;; (GUESS) If I have understood correctly, this is where the address of `value` is 
                                ;; extracted, and stored into %rax
;----------------------------------
        movq    %rax, -16(%rbp) ;; 
        movq    -16(%rbp), %rax ;; Why do I have two times the same instructions, with reversed operands???
;----------------------------------
        movl    (%rax), %eax
        movl    %eax, %esi
        movl    $.LC0, %edi
        movl    $0, %eax
        call    printf
;----------------------------------
        movl    $0, %eax
        movq    -8(%rbp), %rdx
        xorq    %fs:40, %rdx
        je      .L3
        call    __stack_chk_fail
.L3:
        leave
        ret
        .size   main, .-main
        .ident  "GCC: (Ubuntu 4.9.1-16ubuntu6) 4.9.1"
        .section        .note.GNU-stack,"",@progbits

Моя проблема в том, что я не понимаю, почему он содержит инструкцию movq два раза, с обратными операндами. Может кто-нибудь объяснить это мне?

[1]: Я хочу, чтобы мой код asm не был перемещен с помощью директив cfi, когда они мне вообще не нужны.

[2]: Моя среда Ubuntu 14.10, gcc 4.9.1 (изменена ubuntu) и Gnu assembler (GNU Binutils for Ubuntu) 2.24.90.20141014, настроена на таргетинг x86_64-linux-gnu

Ответ 1

Возможно, будет проще, если вы реорганизовываете свои блоки:

;----------------------------------
    leaq    -20(%rbp), %rax     ; &value
    movq    %rax, -16(%rbp)     ; int_val
;----------------------------------
    movq    -16(%rbp), %rax     ; int_val
    movl    (%rax), %eax        ; *int_val
    movl    %eax, %esi          ; printf-argument
    movl    $.LC0, %edi         ; printf-argument (format-string)
    movl    $0, %eax            ; no floating-point numbers
    call    printf
;----------------------------------

Первый блок выполняет int *int_val = &value;, второй блок выполняет printf .... Без оптимизации блоки независимы.

Ответ 2

Поскольку вы не делаете никакой оптимизации, gcc создает очень простой код, который выполняет каждый оператор в программе по одному, не глядя на какое-либо другое утверждение. Поэтому в вашем примере он сохраняет значение в переменной int_val, а затем самая следующая инструкция снова считывает эту переменную как часть следующего оператора. В обоих случаях используется %rax как временное значение удержания, так как это первый регистр, обычно используемый для вещей.