【发布时间】:2021-03-25 03:49:59
【问题描述】:
我在汇编中编写递归函数时遇到了一些麻烦。我想在这里为 C 函数建模:
int power(int base, int exponent)
{
if (exponent==0)
return 1;
else {
/* return base * power(base, --exponent); -- in normal form, writing in long-form below*/
exponent--;
int tmp1 = power(base, exponent);
int tmp2 = base * tmp1;
return tmp2;
}
}
到目前为止,我在汇编中的内容如下:
power:
// int power(int base=rdi, int exponent=rsi)
push %rbp
mov %rsp, %rbp
// if (exponent==0) return 1
cmp $0, %rsi
jnz exponentiate
return_one:
mov $1, %eax
jmp cleanup
exponentiate:
dec %rsi
push %rax
call power
pop %rbx
imul %rbx, %rax
cleanup:
mov %rbp, %rsp
pop %rbp
ret
这会产生结果为零。我认为我的错误出在exponentiate 函数中,但我很难调试它。我知道我可以使用编译器资源管理器“找到答案”,但我想知道是否有人可以帮助指出我的代码中的错误。
我已经能够将其简化为以下,我什至不必为递归情况设置堆栈框架:
exponentiate:
dec %rsi
call power
imul %rdi, %rax
为什么允许这样做?我认为对于所有递归函数,您都需要设置自己的堆栈框架,但为什么在上述情况下不需要?
【问题讨论】:
-
你第一次做
push %rax,你期望%rax保持什么价值? -
@JosephSible-ReinstateMonica 我认为 1 如果这是
return_one的回报? -
你认为你的代码运行的顺序是什么?
-
Of course it is,你忽略了我所说的关于使用
-O1的所有内容,在这种情况下,它是一个非常干净的 asm,它只执行一次推送,没有其他堆栈空间。 godbolt.org/z/EKbcTf15a。事实证明,-O1不需要任何noinline技巧,仅此一项即可制作递归 asm 而无需将其转换为迭代。 How to remove "noise" from GCC/clang assembly output? -
re:堆栈对齐与否:是GCC sometimes reserves 16 bytes more than it needs for alignment。但这并不重要,它保留 一些 并使用它才是真正重要的。当您编写自己的函数时,您可以对堆栈框架进行自己的布局,并选择何时在寄存器中包含什么。使用
-fverbose-asm让它用 var 名称注释它的 asm。