【发布时间】:2021-04-29 20:54:07
【问题描述】:
在 Igor Zhirkov 的书中Low-Level Programming 中有这个悬而未决的问题:
"尝试在不调用 print_uint、复制其代码或使用 jmp 的情况下重写 print_int。您将 只需要一条指令和仔细的代码放置。
阅读协同程序。”。
“print_int”和“print_uint”提供的代码:
print_uint:
mov rax, rdi
mov rdi, rsp
push 0
sub rsp, 16
dec rdi
mov r8, 10
.loop:
xor rdx, rdx
div r8
or dl, 0x30
dec rdi
mov [rdi], dl
test rax, rax
jnz .loop
call print_string
add rsp, 24
ret
print_int:
test rdi, rdi
jns print_uint
push rdi
mov rdi, '-'
call print_char
pop rdi
neg rdi
jmp print_uint
print_char:
push rdi
mov rdi, rsp
call print_string
pop rdi
ret
print_string:
push rdi
call string_length
pop rsi
mov rdx, rax
mov rax, 1
mov rdi, 1
syscall
ret
他所说的那条特殊的单一指令可能是什么?
【问题讨论】:
-
我看到了摆脱
jmp print_uint的方法,但是那个“一条指令”仍然是个谜 -
您是否应该将
print_int移动到print_uint之前,以便它可以落 进入其中(免费尾调)而不是使用jmp?也许他的意思是“更改一条指令”,如删除jmp print_uint? -
顺便说一句,
print_uint写得很好,和我在How do I print an integer in Assembly Level Programming without printf from the c library? 中所做的差不多。但是,如果这不是试图移植到非 Linux,您可以利用 RSP 下面的红色区域,如果您内联系统调用并计算长度,则可以省略sub rsp, 16,而不是调用print_string当你已经知道它在哪里时,让它搜索一个终止的0,因为你推了它。 -
@PeterCordes 谢谢。然而,此代码从未被测试为可能的最快版本,它不是以这种意图编写的。便携性也是没问题的。因为这个作业在本书的早期阶段,读者还不知道红色区域的概念,所以我没有使用它。
-
@IgorZhirkov:是的,与我在我的版本中留下
sub rsp的原因相同(为了人们将其移植到 32 位代码的利益)。如果您实际上是在优化速度,则可以使用乘法逆运算(例如 this code-review Q&A,这将比避免 sub/add 节省更多的周期。:P 另请参阅我的链接答案底部的其他链接,了解一些人们已经尝试过x /= 100并将其拆分以获得一些 ILP 和其他类似的东西。
标签: assembly x86-64 nasm micro-optimization