簡體   English   中英

遞歸 function 的裝配提前返回

[英]Assembly early return on a recursive function

This is more an academic exercise than anything else, but I'm looking to write a recursive function in assembly, that, if it receives and "interrupt signal" it returns to the main function, and not just the function that invoked it (which通常是相同的遞歸函數)。

對於這個測試,我正在做一個基本的倒計時並打印一個字符的數字(8...7...6...等)。 為了模擬“中斷”,我使用數字7 ,所以當 function 達到 7 時(如果它開始高於該值),它將返回1表示它被中斷,如果它沒有被中斷,它會倒計時為零。 到目前為止,這是我所擁有的:

.globl _start
_start:

    # countdown(9);
    mov $8, %rdi
    call countdown

    # return 0;
    mov %eax, %edi
    mov $60, %eax
    syscall

print:
    push %rbp
    mov %rsp, %rbp

    # write the value to a memory location
    pushq %rdi # now 16-byte aligned
    add $'0', -8(%rbp)
    movb $'\n', -7(%rbp)

    # do a write syscall
    mov $1, %rax        # linux syscall write
    mov $1, %rdi        # file descriptor: stdout=1
    lea -8(%rbp), %rsi  # memory location of string goes in rsi
    mov $2, %rdx        # length: 1 char + newline
    syscall

    # restore the stack
    pop %rdi
    pop %rbp
    ret;

countdown:
    # this is the handler to call the recursive function so it can
    # pass the address to jump back to in an interrupt as one of the
    # function parameters
    # (%rsp) currntly holds the return address, and let's pass that as the second argument
    mov %rdi, %rdi      # redundant, but for clarity
    mov (%rsp), %rsi    # return address to jump
    call countdown_recursive


countdown_recursive:

    # bool countdown(int n: n<10, return_address)

    # ...{
    push %rbp
    mov %rsp, %rbp

    # if (num<0) ... return
    cmp $0, %rdi
    jz end

    # imaginary interrupt on num=7
    cmp $7, %rdi
    jz fast_ret

    # else...printf("%d\n", num);
    push %rsi
    push %rdi
    call print
    pop %rdi
    pop %rsi

    # --num
    dec %rdi

    # countdown(num)
    call countdown_recursive

end:
    # ...}
    mov $0, %eax
    mov %rbp, %rsp
    pop %rbp
    ret

fast_ret:
    mov $1, %eax
    jmp *%rsi

以上看起來像一個有效的方法,將 memory 地址傳遞給 go 回到rsi嗎? function 對我來說寫起來非常棘手,但我認為主要是因為我對組裝非常陌生/原始。

除了返回到這個備用返回地址之外,您還需要恢復調用者的(保留調用的)寄存器,而不僅僅是您最近的父母的寄存器。 這包括RSP。

您基本上是在嘗試重新發明 C 的setjmp / longjmp ,它正是這樣做的,包括將堆棧指針重置回您調用setjmp的 scope 。 我認為 SO 的標簽中的一些問題是關於在 asm 中實現你自己的 setjmp / longjmp。

此外,為了提高效率,您可能希望使用自定義調用約定,其中返回地址指針(或實現上述內容后的 jmpbuf 指針)位於像 R15 這樣的調用保留寄存器中,因此您不必保存/在遞歸 function 的主體內圍繞打印調用恢復它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM