我使用x86_64程序集中的单链接列表编写了一个堆栈实现。该堆栈支持常规的push / pop操作以及用于遍历每个元素的first / next。

我正在寻找常规反馈。

这是堆栈子例程:

; Stack Structure
;   Pointer Head
;   Pointer Current

; Stack Node
;   Pointer Next
;   Pointer Value

; input
;   void
; output
;   stack or 0 on error
StackCreate:
  mov rdi, 24
  call malloc
  test rax,rax
  jz StackCreate_end
  xor rdi, rdi
  mov [rax], rdi
  mov [rax+8], rdi
  ret
StackCreate_end:
  add rsp, 8
  ret

; input
;   stack
; output
;   void
StackDestroy:
  call free
  ret

; input
;   stack
;   value
; output
;   0 on error
StackPush:
  push rdi
  push rsi
  mov rdi, 16
  call malloc
  test rax,rax
  jz StackPush_end
  pop rsi
  pop rdi
  mov rdx, [rdi]
  mov [rax], rdx
  mov [rax+8], rsi
  mov [rdi], rax
  ret
StackPush_end:
  add rsp, 16
  ret

; input
;   stack
; output
;   value or 0 if empty
StackPop:
  mov rsi, rdi
  mov rdi, [rdi]
  test rdi, rdi
  jz StackPop_end
  mov rax, [rdi]
  mov [rsi], rax
  mov rax, [rdi+8]
  push rax
  call free
  pop rax
  ret
StackPop_end:
  xor rax, rax
  ret
  nop;e

; input
;   stack
; output
;   value or 0 if empty
StackFirst:
  mov rax, [rdi]
  test rax, rax
  jz StackFirst_end
  mov rsi, [rax]
  mov [rdi+8], rsi
  mov rax, [rax+8]
StackFirst_end:
  ret

; input
;   stack
; output
;   value or 0 if end
StackNext:
  mov rsi, [rdi+8]
  test rsi, rsi
  jz StackNext_end
  mov rax, [rsi]
  mov [rdi+8], rax
  mov rax, [rsi+8]
  ret
StackNext_end:
  xor rax, rax
  ret


这里是驱动程序:

[extern puts]
[extern malloc]
[extern free]

[SECTION .data]

arguments:
  db "Incorrect arguments.",10,"Expected: stackme <string1> <string2> ...",0

[SECTION .text]
BITS 64
global main

wrong_arguments:
  mov rdi, arguments
  call puts
  jmp main_end

main:
  cmp rdi, 1
  jle wrong_arguments

  lea r12, [rsi+8]
  lea r13, [rdi-1]

  call StackCreate
  test rax, rax
  jz main_end
  mov r14, rax

pushing_loop:
  test r13, r13
  jz pushing_done
  mov rdi, r14
  mov rsi, [r12]
  call StackPush
  add r12, 8
  dec r13
  jmp pushing_loop
pushing_done:

  mov rdi, r14
  call StackFirst
print_loop:
  test rax, rax
  jz print_done
  mov rdi, rax
  call puts
  mov rdi, r14
  call StackNext
  jmp print_loop
print_done:

stack_delete:
  mov rdi, r14
  call StackPop
  test rax, rax
  jnz stack_delete
stack_delete_done:
  mov rdi, r14
  call StackDestroy

main_end:
  xor eax, eax
  ret


组装/链接

>
nasm -f elf64 -l stackme.lst stackme.asm
gcc -o stackme stackme.o


#1 楼

该代码通常写得很好并且易于理解,但是我对它有一些意见可以帮助改进它。

消除StackCreate例程中的“幻数”

,第一个指令是mov rdi,24,但在这种情况下尚不清楚24表示什么。注释或命名常量或两者都将对此有所帮助。

添加更多有意义的注释

注释中的堆栈结构不是很清楚。我们可以推断出堆栈是由节点组成的,但是从代码或注释中很难分辨出来。

还要考虑堆栈代码的用户。当前代码中没有足够的信息来理解调用顺序,注册用法或返回值。所有这些内容在评论中都会很有用。如果您打算使用标准的ABI,则最好说出哪一个。

适当地使用rep ret

rep ret可能是gcc编译器时,gcc编译器会发出ret。跳跃的目标。这似乎很奇怪(而且确实如此!),但是原因是当ret不是分支目标时,AMD和Intel处理器上的分支预测逻辑都能更好地工作。因此,这意味着代码中的StackFirst_end标签实际上应该在rep之前有一个ret前缀。

适当地使用struc

因为您的堆栈使用两种结构,这会有所帮助使用NASM的struc/endstruc宏。这样既可以使代码在处理数据结构时更清楚,又可以消除我前面提到的许多“魔术数字”。

考虑重构StackFirstStackNext


StackFirstStackNext例程除了特定寄存器选择上的细微差别外,非常相似。可以将它们合并以消除一些代码。

减少使用mallocfree


mallocfree调用在计算上往往相对昂贵,尤其是相对于您已获得的汇编代码而言。因此,分配和管理一个块而不是为每次堆栈推调用malloc或用用户可以为速度而替换的某些其他内存分配方案替换对mallocfree的调用,可能更有意义。 br />
重新排列循环以避免无条件跳转

当前的pushing_loop如下所示:

    pushing_loop:
      test r13, r13
      jz pushing_done
      mov rdi, r14
      mov rsi, [r12]
      call StackPush
      add r12, 8
      dec r13
      jmp pushing_loop
    pushing_done:


但是,可以稍微重新排列以消除其中一种跳转:

      inc r13
      jmp push_test

    pushing_loop:
      mov rdi, r14
      mov rsi, [r12]
      call StackPush
      add r12, 8
    push_test:
      dec r13
      jnz pushing_loop


无条件跳转仅在循环的顶部进行一次,所有其他迭代仅在以下位置使用条件测试循环的底部。代码中还有其他一些地方可以应用。