如何在AT&T汇编中实现幂运算?附代码验证与修正
AT&T汇编实现x^y的代码分析与正确方案
让我们一步步拆解你写的两段AT&T汇编代码,看看它们能不能正确计算x^y(已知%r14存x,%r15存y),然后给出靠谱的实现方案。
初始代码的问题
先看你写的初始代码:
.gobl functionWithItems functionWithItems: //Do some stuff that I want here. //I need to x ^ y jmp .power .power: imulq %r14, %r14 dec %r15 cmpq $-1, %r15 jne .power jmp .continueOtherPartsOfProgram .continueOtherPartsOfProgram: //Do some stuff after doing the power. r14 should now contain x ^ y. ret
这段代码完全没法正确计算幂运算,问题很多:
- 逻辑错误:每次直接在
%r14上做乘法,第一次运算后%r14就变成了x*x,之后每次都是用更新后的%r14自乘,这算出来的是3^(2^10)而不是3^10,完全偏离了幂运算的逻辑。 - 循环次数错误:循环终止条件是
%r15等于-1,初始y=10的话要循环11次,次数完全不对。 - 无边界处理:完全没考虑
y=0的情况,数学上x^0=1(x≠0),这里直接忽略了。
修正后代码的问题
再看修正后的代码:
.gobl functionWithItems functionWithItems: //Do some stuff that I want here. //I need to x ^ y cmpq $0, %r15 je .zeroValue movq %r14, %rbx cmpq $2, %r15 jge .numberTimes jmp .continueOtherPartsOfProgram .zeroValue: movq $1, %rbx jmp .continueOtherPartsOfProgram .numberTimes: dec %r15 jmp .power .power: imulq %r14, %rbx dec %r15 cmpq $0, %r15 jne .power jmp .continueOtherPartsOfProgram .continueOtherPartsOfProgram: //Do some stuff after doing the power. r14 should now contain x ^ y. ret
这段代码比初始版本进步了,但还是有几个关键问题:
- 结果未回存:你最后期望
%r14存结果,但代码把计算结果存在%rbx里,完全没把%rbx的值移回%r14,调用者最后看到的%r14还是原来的x值。 - 破坏调用约定:在x86-64的System V调用约定里,
%rbx是被调用者保存寄存器,你的函数修改了%rbx但没有先保存它的原始值,这会破坏调用者的上下文,导致程序其他部分出错。 - 循环逻辑冗余:
cmpq $2, %r15的判断有点多余,直接统一处理y>=1的情况会更简洁。 - 边界处理不完整:
y=0时设置%rbx=1,但同样没回存到%r14;另外没处理0^0这种未定义的边界情况(不过一般可以默认返回1)。
正确的AT&T汇编实现(普通循环版)
下面是符合调用约定、逻辑正确的实现,专门针对你的需求(结果存回%r14):
.gobl functionWithItems functionWithItems: # 保存被调用者保存寄存器%rbx,避免破坏调用者上下文 pushq %rbx # 处理y=0的情况:x^0=1(x≠0,0^0这里默认返回1) cmpq $0, %r15 je .handle_zero # 初始化结果为x(对应x^1) movq %r14, %rbx # 如果y=1,直接跳转到结束 cmpq $1, %r15 je .end_power # 循环次数:y-1次,因为已经初始化了x^1 dec %r15 .power_loop: imulq %r14, %rbx # rbx = rbx * r14 → x^n * x = x^(n+1) dec %r15 cmpq $0, %r15 jne .power_loop jmp .end_power .handle_zero: movq $1, %rbx # x^0=1 .end_power: # 将计算结果移回%r14,满足题目要求 movq %rbx, %r14 # 恢复之前保存的%rbx寄存器 popq %rbx .continueOtherPartsOfProgram: # 后续业务代码 ret
这段代码的核心要点:
- 遵循调用约定:先保存
%rbx,用完后恢复,保证调用者的寄存器状态不受影响。 - 完整边界处理:单独处理
y=0和y=1的情况,避免不必要的循环。 - 正确循环逻辑:初始化结果为
x,循环y-1次每次乘以x,最终得到x^y。 - 结果回存:把计算好的结果从
%rbx移回%r14,符合你的需求。
优化版:快速幂(二进制幂)算法
如果你的y值可能很大,普通循环的效率会很低,推荐用快速幂算法,它通过二进制分解y,把乘法次数从O(y)降到O(log₂y)。比如y=10(二进制1010),只需要4次乘法就能算出结果:
.gobl functionWithItems_fast functionWithItems_fast: # 保存被调用者保存寄存器%rbx,以及临时使用的%rcx pushq %rbx pushq %rcx # 处理y=0的情况 cmpq $0, %r15 je .fast_zero movq $1, %rbx # 结果初始化为1 movq %r14, %rcx # 把x存到%rcx,用于平方操作 movq %r15, %rdx # 用%rdx保存当前的y值 .fast_loop: # 如果当前y是奇数,将结果乘以当前的x testq $1, %rdx jz .skip_mult imulq %rcx, %rbx .skip_mult: # x平方(对应二进制位的权重提升) imulq %rcx, %rcx # y右移一位(相当于除以2) shrq $1, %rdx # y不为0则继续循环 jnz .fast_loop jmp .fast_end .fast_zero: movq $1, %rbx .fast_end: # 将结果移回%r14 movq %rbx, %r14 # 恢复寄存器 popq %rcx popq %rbx .continue_fast: # 后续业务代码 ret
内容的提问来源于stack exchange,提问作者Tribunal Homeland
相关产品推荐
相关产品推荐

