汇编中的尾递归
我正在尝试学习一点汇编。我使用 NASM 而不是 AT&T 语法。
这是我的第一个程序,它比较两个命令行参数并输出最小的一个(如果它们相等,则优先选择第一个)。
我想我做错了。我注意到堆栈随着每次“调用”比较而增长,因此我可以通过尾递归优化来使其更好。但是,我不知道如何正确地做到这一点。我是否应该将此处所有出现的“call”替换为其他内容,例如 jmp? 我读到,跳跃的方式还取决于您如何与 linux 内核和/或 libc 有关“函数”对象的链接(我不与 libc 链接,因此没有主“函数”),这让我感到困惑,所以我我想我来这里是为了寻求简单的简短建议。
另外,我遇到的另一个问题是,在“jg”后立即执行“jl”是多么愚蠢,如果“jl”跳转实际上更改了标志内容,因此“jg”也跳转,这可能会导致不必要的行为。是否存在双向“原子”跳跃?
section .data
usage: db 'Please provide two strings',10
usageLen: equ $-usage
bigger: db 'It is bigger!',10
biggerLen: equ $-bigger
smaller: db 'It is smaller!',10
smallerLen: equ $-smaller
section .text
global _start
_start:
pop ebx ; argc
cmp ebx, 3
jne usage_exit
pop eax ; argv[0]
pop eax
pop ebx
call compare ;
usage_exit:
mov eax, 4 ; sys_write
mov ebx, 1 ; int fd = stdout
mov ecx, usage
mov edx, usageLen
int 80h ; call kernel
mov eax, 1 ; sys_exit
mov ebx, 0 ; int status = 0
int 80h ; call kernel
report:
mov ecx, eax
mov edx, ebx
mov eax, 4 ; sys_write
mov ebx, 1 ; int fd = stdout
int 80h ; call kernel
mov eax, 1 ; sys_exit
mov ebx, 0 ; int status = 0
int 80h ; call kernel (exit)
compare:
push eax
push ebx
mov eax, [eax]
mov ebx, [ebx]
test ax, ax
jz is_smaller
test bx, bx
jz is_bigger
cmp ax, bx
jl is_smaller
jg is_bigger
; if the same, try next positions
pop ebx
pop eax
inc eax
inc ebx
call compare
is_smaller:
mov eax, smaller
mov ebx, smallerLen
call report
is_bigger:
mov eax, bigger
mov ebx, biggerLen
call report
I'm trying to learn assembly a bit. I use NASM instead of AT&T syntax.
This is my first program, it compares the two command line arguments and outputs which one is the smallest (favouring the first in case they're equal).
I think I'm doing this wrong. I noticed that the stack grows with each "call" of compare, so I could probably make this better with tail recursion optimization. However, I don't know how to do it correctly. Should I replace all occurrences of 'call' here with something else, like jmp?
I read that the manner of jumping also depends on how you link with the linux kernel and/or libc regarding 'function' objects (I do not link with libc and so do not have a main 'function'), which confuses me so I thought I'd come here for simple short advice.
Also, another question I had is how foolish it is to do "jl" immediately followed by "jg", which might cause unwanted behaviour if the "jl" jump actually changed the flag contents so "jg" also jumps. Is there a two-way 'atomic' jump?
section .data
usage: db 'Please provide two strings',10
usageLen: equ $-usage
bigger: db 'It is bigger!',10
biggerLen: equ $-bigger
smaller: db 'It is smaller!',10
smallerLen: equ $-smaller
section .text
global _start
_start:
pop ebx ; argc
cmp ebx, 3
jne usage_exit
pop eax ; argv[0]
pop eax
pop ebx
call compare ;
usage_exit:
mov eax, 4 ; sys_write
mov ebx, 1 ; int fd = stdout
mov ecx, usage
mov edx, usageLen
int 80h ; call kernel
mov eax, 1 ; sys_exit
mov ebx, 0 ; int status = 0
int 80h ; call kernel
report:
mov ecx, eax
mov edx, ebx
mov eax, 4 ; sys_write
mov ebx, 1 ; int fd = stdout
int 80h ; call kernel
mov eax, 1 ; sys_exit
mov ebx, 0 ; int status = 0
int 80h ; call kernel (exit)
compare:
push eax
push ebx
mov eax, [eax]
mov ebx, [ebx]
test ax, ax
jz is_smaller
test bx, bx
jz is_bigger
cmp ax, bx
jl is_smaller
jg is_bigger
; if the same, try next positions
pop ebx
pop eax
inc eax
inc ebx
call compare
is_smaller:
mov eax, smaller
mov ebx, smallerLen
call report
is_bigger:
mov eax, bigger
mov ebx, biggerLen
call report
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
是的,您应该将
call
替换为jmp
。一般来说,当您希望调用返回时在下一行继续执行时,您应该使用call
。在您的代码中,执行永远不会返回,因为compare
只是一个循环,因此jmp
是进入下一次迭代的正确方法。您拥有的两个通话报告
实例也是如此。至于你的第二个问题,
jl
不会更改标志,因此在其之后调用jg
没有问题。Yes, you should replace the
call
s withjmp
s. In general, you should usecall
when you expect execution to resume on the following line when the call returns. In your code, execution never returns sincecompare
is just a loop, sojmp
is the correct way to go to the next iteration. The same is true for the two instances ofcall report
you have.As for your second question,
jl
doesn't change the flags, so there is no problem callingjg
after it.