2014-11-02 85 views
-1

我已經練習了很久以前的彙編程序,我想了解一個簡單的程序(我從C代碼生成彙編代碼),它添加了2個向量(實際上是2個數組)並將結果存儲在另一個向量中一個輸出數組)。我的目標是研究矢量化。爲此,我在i7內核處理器上使用Debian Wheezy下的gcc-4.9。瞭解簡單的設置程序

這裏C代碼片段(未矢量版本):

#include <stdio.h> 

#define SIZE 10000 

void test(double *a, double *b, double *c) 
{ 
    int i; 

    for (i = 0; i < SIZE; i++) 
    { 
    c[i] = a[i] + b[i]; 
    } 
} 

int main() 
{ 
int i; 
double tab1[SIZE]; 
double tab2[SIZE]; 
double tab3[SIZE]; 

for (i = 0; i < SIZE; i++) 
    { 
    tab1[i] = i; 
    tab2[i] = i; 
    tab3[i] = 0; 
    } 

test(tab1, tab2, tab3); 

for (i = 0; i < SIZE; i++) 
    printf(" tab3[%d] = %f\n", i, tab3[i]); 

return 0; 
} 

我生成與AT & T語法彙編代碼:

gcc -std=c99 -c main_no_vectorized.c -O3 -S -o main_no_vectorized.s 

這裏是彙編代碼:

.file "main_no_vectorized.c" 
    .section .text.unlikely,"ax",@progbits 
.LCOLDB0: 
    .text 
.LHOTB0: 
    .p2align 4,,15 
    .globl test 
    .type test, @function 
test: 
.LFB3: 
    .cfi_startproc 
    leaq 16(%rdx), %rax 
    leaq 16(%rsi), %rcx 
    cmpq %rax, %rsi 
    setae %r8b 
    cmpq %rcx, %rdx 
    setae %cl 
    orb %cl, %r8b 
    je .L7 
    cmpq %rax, %rdi 
    leaq 16(%rdi), %rax 
    setae %cl 
    cmpq %rax, %rdx 
    setae %al 
    orb %al, %cl 
    je .L7 
    testb $8, %dil 
    pushq %r12 
    .cfi_def_cfa_offset 16 
    .cfi_offset 12, -16 
    pushq %rbp 
    .cfi_def_cfa_offset 24 
    .cfi_offset 6, -24 
    pushq %rbx 
    .cfi_def_cfa_offset 32 
    .cfi_offset 3, -32 
    je .L8 
    movsd (%rdi), %xmm0 
    movl $9998, %ebp 
    movl $4999, %r9d 
    movl $9999, %r12d 
    movl $1, %r8d 
    movl $1, %ebx 
    addsd (%rsi), %xmm0 
    movsd %xmm0, (%rdx) 
.L3: 
    salq $3, %r8 
    xorl %eax, %eax 
    xorl %ecx, %ecx 
    leaq (%rdi,%r8), %r11 
    leaq (%rsi,%r8), %r10 
    addq %rdx, %r8 
    .p2align 4,,10 
    .p2align 3 
.L4: 
    movupd (%r10,%rax), %xmm0 
    addl $1, %ecx 
    addpd (%r11,%rax), %xmm0 
    movups %xmm0, (%r8,%rax) 
    addq $16, %rax 
    cmpl %r9d, %ecx 
    jb .L4 
    cmpl %ebp, %r12d 
    leal (%rbx,%rbp), %eax 
    je .L1 
    cltq 
    movsd (%rdi,%rax,8), %xmm0 
    addsd (%rsi,%rax,8), %xmm0 
    movsd %xmm0, (%rdx,%rax,8) 
.L1: 
    popq %rbx 
    .cfi_remember_state 
    .cfi_restore 3 
    .cfi_def_cfa_offset 24 
    popq %rbp 
    .cfi_restore 6 
    .cfi_def_cfa_offset 16 
    popq %r12 
    .cfi_restore 12 
    .cfi_def_cfa_offset 8 
    ret 
    .p2align 4,,10 
    .p2align 3 
.L8: 
    .cfi_restore_state 
    movl $10000, %ebp 
    movl $5000, %r9d 
    movl $10000, %r12d 
    xorl %r8d, %r8d 
    xorl %ebx, %ebx 
    jmp .L3 
.L7: 
    .cfi_def_cfa_offset 8 
    .cfi_restore 3 
    .cfi_restore 6 
    .cfi_restore 12 
    xorl %eax, %eax 
    .p2align 4,,10 
    .p2align 3 
.L2: 
    movsd (%rdi,%rax), %xmm0 
    addsd (%rsi,%rax), %xmm0 
    movsd %xmm0, (%rdx,%rax) 
    addq $8, %rax 
    cmpq $80000, %rax 
    jne .L2 
    rep ret 
    .cfi_endproc 
.LFE3: 
    .size test, .-test 
    .section .text.unlikely 
.LCOLDE0: 
    .text 
.LHOTE0: 
    .section .rodata.str1.1,"aMS",@progbits,1 
.LC3: 
    .string " tab3[%d] = %f\n" 
    .section .text.unlikely 
.LCOLDB4: 
    .section .text.startup,"ax",@progbits 
.LHOTB4: 
    .p2align 4,,15 
    .globl main 
    .type main, @function 
main: 
.LFB4: 
    .cfi_startproc 
    pushq %rbx 
    .cfi_def_cfa_offset 16 
    .cfi_offset 3, -16 
    xorl %eax, %eax 
    subq $240016, %rsp 
    .cfi_def_cfa_offset 240032 
    movdqa .LC2(%rip), %xmm3 
    leaq 32(%rsp), %rcx 
    leaq 80032(%rsp), %rdx 
    movdqa .LC1(%rip), %xmm1 
    .p2align 4,,10 
    .p2align 3 
.L21: 
    pshufd $238, %xmm1, %xmm0 
    cvtdq2pd %xmm1, %xmm2 
    paddd %xmm3, %xmm1 
    movaps %xmm2, 16(%rsp,%rax) 
    cvtdq2pd %xmm0, %xmm0 
    movaps %xmm2, 80016(%rsp,%rax) 
    movaps %xmm0, (%rcx,%rax) 
    movaps %xmm0, (%rdx,%rax) 
    addq $32, %rax 
    cmpq $80000, %rax 
    jne .L21 
    leaq 160016(%rsp), %rdi 
    movl $80000, %edx 
    xorl %esi, %esi 
    call memset 
    xorl %eax, %eax 
    .p2align 4,,10 
    .p2align 3 
.L22: 
    movapd 16(%rsp,%rax), %xmm0 
    addpd 80016(%rsp,%rax), %xmm0 
    movaps %xmm0, 160016(%rsp,%rax) 
    addq $16, %rax 
    cmpq $80000, %rax 
    jne .L22 
    xorl %ebx, %ebx 
    .p2align 4,,10 
    .p2align 3 
.L23: 
    movsd 160016(%rsp,%rbx,8), %xmm4 
    movl %ebx, %esi 
    movl $.LC3, %edi 
    movl $1, %eax 
    addq $1, %rbx 
    movapd %xmm4, %xmm0 
    movsd %xmm4, 8(%rsp) 
    call printf 
    cmpq $10000, %rbx 
    jne .L23 
    addq $240016, %rsp 
    .cfi_def_cfa_offset 16 
    xorl %eax, %eax 
    popq %rbx 
    .cfi_def_cfa_offset 8 
    ret 
    .cfi_endproc 
.LFE4: 
    .size main, .-main 
    .section .text.unlikely 
.LCOLDE4: 
    .section .text.startup 
.LHOTE4: 
    .section .rodata.cst16,"aM",@progbits,16 
    .align 16 
.LC1: 
    .long 0 
    .long 1 
    .long 2 
    .long 3 
    .align 16 
.LC2: 
    .long 4 
    .long 4 
    .long 4 
    .long 4 
    .ident "GCC: (Debian 4.9.1-16) 4.9.1" 
    .section .note.GNU-stack,"",@progbits 

您能否向我解釋上述彙編代碼與C相關的主要步驟代碼,特別是「測試」函數,主函數中的初始化循環和參數傳遞(即堆棧的推入和彈出指令)以及「a」和「b」數組的有效添加?

什麼對應於.L2,.L3,...段?與L2緩存,L3緩存有關係嗎?

對不起這些基礎知識的問題,但我從Intel x86_64彙編器開始。

感謝您的寶貴幫助

+0

有兩件事會給你理解發生了什麼問題。首先,當你轉儲彙編代碼時,如果有些陳述似乎沒有任何意義,那麼你會希望嘗試將優化減少到-O1(或根本沒有)。重度優化雖然適用於編譯器,但卻產生一些彙編程序輸出,有時只能由編寫優化例程的人識別。接下來,如果自從研究彙編(例如從32位移動到64位)之後已經有一段時間了,您將遇到系統調用和調用約定中的差異。 – 2014-11-02 05:43:53

回答

1

生成的彙編代碼非常複雜。它首先檢查數組a,b和c是否以一種會導致優化循環失敗的方式重疊。例如,如果你這樣做:

test(tab1, tab2, &tab1[1]); 

則重疊將被檢測並導致代碼跳轉到L7(直白實現)。順便說一下,L代表標籤,而標籤編號只是由編譯器生成的,沒有特別的意義。所以L1,L2,L3等只是用於代碼分支到各個地方的標籤。重疊檢查開始於.LFB3並結束於最後的je .L7

如果未檢測到重疊,則將使用優化的循環。這個優化的循環將嘗試一次添加兩個雙打而不是一個。優化循環所做的第一件事是確定數組a是否與16字節邊界(testb $8, %dil指令)對齊。如果是,它將跳轉到L8加載一組常量(例如r9 = 5000)。如果數組未對齊,則會落入並加載一組不同的常量(例如r9 = 4999),並處理第一個元素。這是因爲未對齊的情況下需要一次執行兩次4999次迭代,並在循環外分別處理第一個和最後一個未對齊的元素。對齊的情況下只會做5000次迭代。

無論哪種方式,代碼接下來到達L3。 L3和L4的代碼是優化的循環,它使用addpd指令一次添加兩個指令(L7上的非優化循環使用addsd一次添加一個)。在L4循環結束後,它會檢查是否需要處理最後一個元素(對於未對齊的情況)。然後它返回ret指令。

順便提一下,它有助於知道,當test被調用,ardibrsi,並crdx。這是64位的調用約定。因此,堆棧上沒有任何參數。如果您不太瞭解x86彙編,請專注於從L7開始的代碼。這是非優化的版本,你應該能夠指出這一部分,因爲我說你的三個參數是rdi,rsi和rdx。

0

.L2和如標籤,它們被用來指的下一條指令。如果您使用goto,它們幾乎完全像C中的標籤。標籤的主要用途是使用跳轉或分支來指定跳轉的位置。

例如,.L2標籤是在test()啓動for (i = 0; i < SIZE; i++)循環體的,它是由8個字節(一個double的大小)高達8 * 10000計數。循環中的最後一條指令是jne .L2,如果前面的比較結果不相等,則跳轉到.L2

您可能會發現this reference (PDF)對x64有幫助。