【问题标题】:Problem with recursive factorial in x86_64 Assemblyx86_64 程序集中的递归阶乘问题
【发布时间】:2019-12-05 00:27:59
【问题描述】:

我是这种汇编语言的新手,我尝试自己编写以下代码。问题是我的代码无法正确计算数字的阶乘,并且始终在终端中显示 1 作为输出。我想知道它不起作用的原因。

.text

mystring1: .asciz "Assignment 4: recursion\nType any number to calculate the factorial of that number:\n"  # string for printing message
formatstr: .asciz "%ld"                   # format string for printing number
mystring2: .asciz "\n"                    # string for printing a new line

.global main  # make the main label visible  

main:

    pushq %rbp            # store the caller's base pointer
    movq %rsp, %rbp       # initialise the base pointer
    movq $0, %rax         # no vector registers in use for printf
    movq $mystring1, %rdi # load address of a string
    call printf           # call the printf subroutine
    call inout            # call the inout subroutine
    movq $0, %rax         # no vector registers in use for printf
    movq $mystring2, %rdi # load address of a string
    call printf
    jmp end

inout:

    pushq %rbp                  # push the base pointer
    movq %rsp, %rbp             # copy the stack pointer to rbp
    subq $16, %rsp              # reserve stack space for variable
    leaq -8(%rbp), %rsi         # load address of stack variable in rsi
    movq $formatstr, %rdi       # load first argument of scanf
    movq $0, %rax               # no vector registers in use for scanf
    call scanf                  # call scanf routine
    movq -8(%rbp), %rsi         # move the address of the variable to rsi
    call factorial
    movq $0, %rax               # no vector registers in use for printf
    movq $formatstr, %rdi       # move the address formatstring to rdi
    call printf                 # print the result
    movq %rbp, %rsp             # copy rbp to rsp
    popq %rbp                   # pop rbp from the stack
    ret                         # return from the subroutine

factorial:

    cmpq $1, %rsi
    jle factend
    pushq %rbx
    movq %rsi, %rbx
    subq $1, %rsi
    call factorial
    mulq %rbx
    popq %rbx
    ret

factend:

    movq $1, %rax
    ret

end:
    mov $0, %rdi # load program exit code
    call exit    # exit the program

我的代码的伪代码:

long rfact(long n)
{
     long result;
     if (n < = 1)
     {
        result = 1;
     }
     else
     {
        result = n * rfact(n - 1);
        return result;
     }
}

【问题讨论】:

  • 您没有使用mulq 的高半 RDX 结果。使用更高效的 2 操作数 imul %rbx, %rax。顺便说一句,x86-64 System V 调用约定传递 RDI 中的第一个整数/指针 arg。您已经为 args 到 printf 和 scanf 这样做了,所以您为自己的函数选择 RSI 很奇怪。
  • 坦率地说,我还有很多东西要学,我只是随机使用 RSI,但仍然感谢您提供的信息
  • 是的,这就是我教你的原因:P
  • 好吧,既然你问了,哦,如果你关心效率的话,那就太多了。 xor-zeroing,RIP-relative LEA,(谷歌搜索或搜索 SO)和mov $1, %eax zero-隐式扩展到 RAX;无需在 64 位操作数大小上浪费代码大小。 (地址相同;如果您正在针对位置相关的 Linux 可执行文件进行优化,请使用 mov $formatstr, %edi,而不是 %rdi,当您不需要 RIP 相关的 LEA 将静态地址放入寄存器中时。@987654330 @ 占用更多空间并没有任何好处,GAS 不会为您做这种优化,即使是数字常量。)
  • 但就正确性和良好的风格而言,你在这里做得很好。例如在调用者中使用堆栈空间作为 scanf 暂存空间。跨 factorial 保存/恢复 RBX 并使用它来保存本地变量是编译器会做的事情。哦,把你的字符串文字放在.section .rodata,代码放在.text。如果你好奇,把你的 C 放到 godbolt.org 中,看看它是如何用 -O1-O2 编译的。 ...现在我很好奇:godbolt.org/z/YkxAYS 表明 GCC -O1 所做的事情与您所做的非常接近。 clang -O1 仍然将简单的递归优化为循环。

标签: recursion assembly x86-64 att


【解决方案1】:

您在rax 中返回阶乘结果,但您的调用者假设它在rsi 中。在对factorial 的调用返回后,调用者应将结果从rax 移到需要的位置(在本例中为rsi)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-20
    • 2019-10-22
    • 1970-01-01
    • 2016-01-28
    • 1970-01-01
    • 2015-04-19
    相关资源
    最近更新 更多