汇编中的尾递归

发布于 2024-10-30 13:20:14 字数 1716 浏览 3 评论 0原文

我正在尝试学习一点汇编。我使用 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

你是年少的欢喜 2024-11-06 13:20:14

是的,您应该将 call 替换为 jmp。一般来说,当您希望调用返回时在下一行继续执行时,您应该使用 call。在您的代码中,执行永远不会返回,因为 compare 只是一个循环,因此 jmp 是进入下一次迭代的正确方法。您拥有的两个通话报告实例也是如此。

至于你的第二个问题, jl 不会更改标志,因此在其之后调用 jg 没有问题。

Yes, you should replace the calls with jmps. In general, you should use call when you expect execution to resume on the following line when the call returns. In your code, execution never returns since compare is just a loop, so jmp is the correct way to go to the next iteration. The same is true for the two instances of call report you have.

As for your second question, jl doesn't change the flags, so there is no problem calling jg after it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文