MIPS递归阶乘代码$ra调用问题:无法返回至branch分支输出结果
问题:MIPS递归阶乘无法返回至输出分支
编写MIPS递归阶乘汇编代码时,输入factorial(1)递归执行符合预期,但进入end:分支后,返回地址无法回到branch:分支后的输出逻辑,导致计算结果无法输出。项目要求不能修改现有代码格式,需要明确$ra寄存器的存储/调用方式来解决这个问题。
相关代码:
.data prompt1: .asciiz "\nPlease enter an integer: \n" negativePrompt: .asciiz "\nYour input must be greater than or equal to 0 in order to function.\n" factorialIs: .asciiz "\nThe factorial of your input number is: \n" .globl main .text main: subu $sp, $sp, 32 # push memory for 8 values sw $ra, 20($sp) # save return address sw $fp, 16($sp) # save old frame pointer addiu $fp, $sp, 28 # set up frame pointer branch: li $v0, 4 # load macro for print_str la $a0, prompt1 # pass argument to string syscall # print string prompt1 li $v0, 5 # load macro for read_int syscall # get N value from user move $a0, $v0 # store N as arg blt $a0, 0, negBranch # if N is negative, handles case jal factorial # calls factorial function li $v0, 4 # macro for print string la $a0, factorialIs # print string syscall li $v0, 1 # macro for print int move $a0, $v0 # print output int in v0 syscall lw $ra, 20($sp) # restore return address of caller lw $fp, 16($sp) # restore frame pointer addiu $sp, $sp, 32 # pop stack jr $ra # return to caller #exit: # "would you like to calculate again?" # li $v0, 10 # load macro to exit # syscall # execute exit negBranch: li $v0, 4 # load macro for print_str la $a0, negativePrompt # pass argument to string syscall # print string prompt1 li $t0, 0 # initialize input value to 0 j branch # ask user to input new number factorial: subu $sp, $sp, 32 # pushes memory for variables sw $ra, 12($sp) # stores return address of recursion sw $fp, 8($sp) # save frame pointer addiu $fp, $sp, 28 # set up frame pointer sw $a0, 0($fp) # stores n in frame pointer lw $v0, 0($fp) bgtz $v0, zeroBranch # if N is greater than 0, recurse li $v0, 1 # return 1 jr end zeroBranch: lw $v1, 0($fp) # load n value subu $v0, $v1, 1 # v0 = n - 1 move $a0, $v0 # a0 = v0 = n - 1 jal factorial # recursive function call lw $v1, 0($fp) # load original n value to v1 mul $v0, $v0, $v1 # v0 = n * fact(n-1) end: lw $ra, 12($sp) # loads return address of main call ## this ^ line does not access the address from "jal factorial" lw $fp, 16($sp) # loads initial n value addiu $sp, $sp, 32 # collapses stack frame jr $ra # return to main then exit
错误分析
- 帧指针恢复偏移错误:在
factorial函数中,sw $fp, 8($sp)将旧帧指针存在栈的8偏移位置,但end:分支里用lw $fp, 16($sp)读取,偏移量不匹配,导致帧指针恢复错误,进而破坏栈结构,让返回地址的读取逻辑失效。 - 结果寄存器被覆盖:
jal factorial执行后,阶乘结果存在$v0中,但后续执行li $v0, 4(打印字符串的系统调用编号)直接覆盖了$v0,导致最终打印的不是阶乘结果。 - 返回地址逻辑误解:
lw $ra, 12($sp)本身是正确的(对应factorial中sw $ra,12($sp)的存储位置),但因为帧指针恢复错误,才导致返回地址无法正确回到branch:分支后的输出逻辑。
修复方案(不修改现有代码格式,仅调整错误指令)
- 修正帧指针恢复偏移:将
end:分支中的lw $fp, 16($sp)改为lw $fp, 8($sp),对应factorial函数中存储$fp的位置。 - 保护阶乘结果寄存器:在
main中调用jal factorial后,先将结果存入临时寄存器,避免被后续系统调用覆盖:jal factorial # calls factorial function move $t0, $v0 # 临时存储阶乘结果,防止被后续syscall覆盖 li $v0, 4 # macro for print string la $a0, factorialIs # print string syscall li $v0, 1 # macro for print int move $a0, $t0 # 打印存储的阶乘结果 syscall - 可选:修复main的返回逻辑:
main中的jr $ra在首次执行时,$ra的值是操作系统入口的不确定地址,若要实现重复计算,可将jr $ra改为j branch,或者启用注释的exit分支执行退出。
修改后,递归返回时$ra会正确指向jal factorial的下一行,执行输出逻辑,成功打印阶乘结果。
内容的提问来源于stack exchange,提问作者kaili
相关产品推荐
相关产品推荐

