title: NASM matrix-vector multiplication
body: <p>I was writing an AVX matrix-vector multiplication function in C but realized an instruction I needed was not implemented in GCC, so I saw that as a golden opportunity to learn some x86 assembly. I first wrote a routine in MIPS assembly and then tried translating it. My code is not working, I get segfault and I have no idea why. If I remove the two <code>jnz</code> in the code, it works, but I don't understand why they would have any impact. Does this type of jump destroy registers that I'm using?</p>
  
  <p>EDIT: It appears that the first two instructions in <code>main</code> don't set <code>rdi</code> to 2, but rather to <code>0x1000000002</code>, which causes trouble later. Why isn't 2 loaded?</p>
  
  <p>EDIT2: Got it. As @rkhb pointed out, using rXX registers loads more data than I intended. I changed the registers to the 32-bit ones (where applicable), which solved the segfault problem. However, now the program printed 0,0. This was because the loop advances eax by 8 (in the below example), but doesn't subtract that amount before returning. Thus, the values are in <code>addr</code> and <code>addr+4</code>, but the pointer returned is <code>addr+8</code>.</p>
  
  <pre><code>; 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
  </code></pre>