2017-06-07 237 views
2

我正在用C編寫一個AVX矩陣向量乘法函數,但是實現了我需要的指令並未在GCC中實現,所以我將它看作是學習某些x86程序集的絕好機會。我首先在MIPS程序集中編寫了一個例程,然後嘗試翻譯它。我的代碼不工作,我得到段錯誤,我不知道爲什麼。如果我在代碼中刪除了兩個jnz,它可以工作,但我不明白他們爲什麼會產生任何影響。這種類型的跳轉銷燬是否註冊了我正在使用的?NASM矩陣向量乘法

編輯:看起來main中的前兩條指令並沒有將rdi設置爲2,而是將其設置爲0x1000000002,這會在以後造成麻煩。爲什麼不裝2?

EDIT2:明白了。正如@rkhb指出的那樣,使用rXX寄存器加載的數據比我預期的要多。我將寄存器更改爲32位(如果適用),這解決了段錯誤問題。但是,現在該程序打印0,0。這是因爲循環將eax提前8(在下面的例子中),但是在返回之前不會減去該數量。因此,值爲addraddr+4,但返回的指針爲addr+8

; nasm -felf64 filename.asm 
; gcc filename.o 

    global main 
    extern printf 

    section .data 
N: dd 2   ; dimension 
a: dd 1, 2, 3, 4  ; matrix 
b: dd 1, 2   ; vector 
format: db "%d", 10, 0 

    section .bss 
c: resb 8   ; reserve 8B 

    section .text 
main: 
    ; set up arguments 
    lea rdi, [N] ; fix: change regs to edi, etc 
    mov rdi, [rdi] 
    lea rsi, [a] 
    lea rdx, [b] 
    lea rcx, [c] 

    call matvec  ; c = a*b 

    ; print results 
    mov rsi, [rax] 
    mov rdi, format 
    push rax 
    mov rax, 0 
    call printf  ; print c[0], should be 5 
    pop rax 
    add rax, 4 
    mov rsi, [rax] 
    mov rdi, format 
    mov rax, 0 
    call printf  ; print c[1], should be 11 

    ret 

; rdi = N, rsi = int*, rdx = int*, rcx = int* 
matvec: 
    mov rax, rcx ; rax = c 
    mov R14, rdi ; r14 = N 
    mov R15, R14 
    shl R15, 2  ; r15 = 4*N 
    xor R8, R8  ; i = 0 
    xor R9, R9  ; j = 0 
    xor R10, R10 ; sum = 0 

loop: 
    mov R11, [rsi] ; r11 = *a 
    mov R12, [rdx] ; r12 = *b 
    imul R11, R12 ; r11 *= r12 
    add R10, R11 ; r10 += r11 
    add rsi, 4  ; a++ 
    add rdx, 4  ; b++ 
    add R9, 1  ; j++ 
    cmp R14, R9 
    jnz loop  ; loop while r14-r9 = N-j != 0 

    mov [rax], R10 ; *c = sum 
    xor R10, R10 ; sum = 0 
    xor R9, R9  ; j = 0 on every i loop 
    sub rdx, R15 ; b -= 4*N 
    add rax, 4  ; c++ 
    add R8, 1  ; i++ 
    cmp R14, R8 
    jnz loop  ; loop while r14-r8 = N-i != 0 

    sub rax, R15 ; fix: subtract 4*N from return pointer 
    ret 
+1

'mov rdi,[rdi]'加載8個字節。但'N:dd 2'只有4個字節大。將'dd'改爲'dq'。 – rkhb

+0

謝謝。我認爲這可能與它有關,所以我做了更麻煩的更改,即將所有regs從rax更改爲eax等。現在它可以工作,但在返回main之後,rax中的值被清零,或者更確切地說,rax指向爲零。 –

+0

如果你已經解決了這個問題,你應該把它作爲答案發布,而不是作爲你的問題的編輯。 –

回答

0

的數據被聲明爲4個字節,但8字節寄存器(rax等)被用於保持所述數據。當將數據加載到這些寄存器中時,您將使用低4字節的數據和高4字節的垃圾。爲避免出現這種情況,請更改聲明以便聲明8字節數據(使用dq),或使用4字節寄存器(eax等)。

修復將處理分段錯誤的問題,但程序將返回錯誤的結果。結果意欲存儲在內存地址c,其值保存在rax中。在上面的代碼中,該地址會增加4個字節兩次;而計算值存儲在c[0]c[1]中,rax返回的地址實際上是&c[2]。因此,在返回main之前,您需要將rax減1或減去4*N

編輯:你可以使用GNU調試器檢查寄存器,閱讀this指南。