2017-06-07 16 views
2

CでAVX行列 - ベクトル乗算関数を書いていましたが、必要な命令がGCCに実装されていないことが分かったので、いくつかのx86アセンブリを学ぶ絶好の機会でした。私は最初にMIPSアセンブリでルーチンを書いてから翻訳を試みました。私のコードは機能しません、私はsegfaultを取得し、私は理由を知りません。コード内の2つのjnzを削除すると機能しますが、なぜ影響を受けるのか分かりません。このタイプのジャンプは私が使用しているレジスタを破壊しますか?NASM行列 - ベクトル乗算

EDIT:mainの最初の2つの命令は、rdiを2に設定せず、むしろ後で問題を引き起こす0x1000000002に設定されているようです。 2がロードされないのはなぜですか?

EDIT2:入手しました。 @rkhbが指摘するように、rXXレジスタを使用すると、意図したより多くのデータがロードされます。私はレジスタを32ビットのレジスタに変更しました(適用可能な場合)。これはsegfaultの問題を解決しました。しかし今、プログラムは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

ありがとうございます。私はそれが何かを持っているかもしれないと思ったので、私はより面倒な変更を加えました。つまり、すべてのregをraxからeaxなどに変更しました。今は動作しますが、mainに戻った後、raxの値はゼロになります。 toは0になります。 –

+0

問題を解決した場合は、質問の編集ではなく、回答として投稿する必要があります。 –

答えて

0

データは4バイトとして宣言されているが、8バイトのレジスタ(raxなど)は、データを保持するために使用されます。これらのレジスタにデータをロードすると、下位4バイトのデータと上位4バイトのガーベジが表示されます。これを避けるには、8バイトのデータが宣言されるように(dqを使用して)宣言を変更するか、代わりに4バイトのレジスタを使用してください(eaxなど)。

この問題を解決すると、セグメンテーションフォルトの問題は解決しますが、プログラムは間違った結果を返します。結果はメモリアドレスcに格納され、その値はraxに保持されます。上記のコードでは、そのアドレスは4バイトずつ2回インクリメントされます。計算された値はc[0]c[1]に格納されますが、raxに返されるアドレスは実際には&c[2]です。したがって、mainに戻る前に、raxを8ずつ減らすか、一般に4*Nを減らす必要があります。

編集:GNUデバッガを使用してレジスタを検査するには、thisガイドを読んでください。