【问题标题】:NASM matrix-vector multiplicationNASM 矩阵向量乘法
【发布时间】:2017-11-08 04:40:51
【问题描述】:

我正在用 C 编写一个 AVX 矩阵向量乘法函数,但意识到我需要的指令没有在 GCC 中实现,所以我认为这是学习一些 x86 汇编的绝好机会。我首先在 MIPS 汇编中编写了一个例程,然后尝试翻译它。我的代码不起作用,我得到了段错误,我不知道为什么。如果我删除代码中的两个jnz,它可以工作,但我不明白为什么它们会产生任何影响。这种跳转会破坏我正在使用的寄存器吗?

编辑:似乎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

【问题讨论】:

  • mov rdi, [rdi] 加载 8 个字节。但是N: dd 2 只有 4 个字节大。将dd 更改为dq
  • 谢谢。我认为这可能与它有关,因此我进行了更繁琐的更改,即将所有 regs 从 rax 更改为 eax 等。现在它可以工作了,但是在返回 main 后,rax 中的值被归零,或者更确切地说,是什么 rax指向归零。
  • 如果您已经解决了问题,您应该将其作为答案发布,而不是作为对您问题的编辑。
  • 好的。我认为这真的没有必要,但好的,我会发布一个答案。

标签: assembly nasm x86-64


【解决方案1】:

数据被声明为 4 字节,但 8 字节寄存器(rax 等)用于保存数据。将数据加载到这些寄存器中时,您将在低 4 字节中存储数据,在高 4 字节中存储垃圾。为避免这种情况,要么更改声明以声明 8 字节数据(使用 dq),要么改用 4 字节寄存器(eax 等)。

修复它会解决分段错误的问题,但程序会返回错误的结果。结果将存储在内存地址c,其值保存在rax 中。在上面的代码中,该地址增加了 4 个字节两次;虽然计算值存储在c[0]c[1] 中,但rax 中返回的地址实际上是&c[2]。因此,在返回main 之前,您需要将rax 减8,或者一般情况下减4*N

编辑:您可以使用 GNU 调试器检查寄存器,阅读this 指南。

【讨论】:

    猜你喜欢
    • 2020-10-29
    • 1970-01-01
    • 1970-01-01
    • 2020-03-16
    • 2020-01-17
    • 2016-01-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多