You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

递归Collatz函数转MIPS汇编遇异常,求正确实现方案

递归Collatz函数的MIPS汇编实现问题排查与修复

问题背景

练习将C语言递归Collatz函数转换为MIPS汇编,已编写驱动代码,但自行实现的collatz汇编函数输出异常,实际输出为大整数,与预期不符。

原C代码

uint32_t collatz(uint32_t n, int d) {
  /*   printf("%d\n", n);*/
  if (n != 1) {
    if (n % 2)
      return collatz(3 * n + 1, d + 1);
    else {
      return collatz(n / 2, d + 1);
    }
  }

  return d;
}

驱动代码(原版本)

.data

arrow: .asciiz " -> "

.text

main:
    li      $sp,        0x7ffffffc      # initialize $sp

# PROLOGUE
    subu    $sp,        $sp,        8   # expand stack by 8 bytes
    sw      $ra,        8($sp)          # push $ra (ret addr, 4 bytes)
    sw      $fp,        4($sp)          # push $fp (4 bytes)
    addu    $fp,        $sp,        8   # set $fp to saved $ra

    subu    $sp,        $sp,        12  # save s0 and s1 on stack before using them
    sw      $s0,        12($sp)         # push $s0
    sw      $s1,        8($sp)          # push $s1
    sw      $s2,        4($sp)          # push $s2

    la      $s0,        xarr            # load address to s0

main_for:
    lw      $s1,        ($s0)           # use s1 for xarr[i] value
    li      $s2,        0               # use s2 for initial depth (steps)
    beqz    $s1,        main_end        # if xarr[i] == 0, stop.

# save args on stack rightmost one first
    subu    $sp,        $sp,        8   # save args on stack
    sw      $s2,        8($sp)          # save depth
    sw      $s1,        4($sp)          # save xarr[i]

    li      $v0,        1
    move    $a0,        $s1             # print_int(xarr[i])
    syscall 

    li      $v0,        4               # print " -> "
    la      $a0,        arrow
    syscall 

    jal     collatz                     # result = collatz(xarr[i])

    move    $a0,        $v0             # print_int(result)
    li      $v0,        1
    syscall 

    li      $a0,        10              # print_char('\n')
    li      $v0,        11
    syscall 

    addu    $s0,        $s0,        4   # make s0 point to the next element

    lw      $s2,        8($sp)          # save depth
    lw      $s1,        4($sp)          # save xarr[i]
    addu    $sp,        $sp,        8   # save args on stack
    j       main_for

main_end:
    lw      $s0,        12($sp)         # restore $s0
    lw      $s1,        8($sp)          # restore $s1
    lw      $s2,        4($sp)          # restore $s2

# EPILOGUE
    move    $sp,        $fp             # restore $sp
    lw      $ra,        ($fp)           # restore saved $ra
    lw      $fp,        -4($sp)         # restore saved $fp
    jr      $ra                         # return to kernel

错误的collatz汇编实现

# collatz function in MIPS Assembly
# Assumes n is in $a0 and d is in $a1
# Returns the result in $v0

    .text
    .globl collatz
collatz:
    addi    $sp, $sp, -12    # Allocate stack space for local variables and return address
    sw      $ra, 8($sp)      # Save return address
    sw      $a0, 4($sp)      # Save n
    sw      $a1, 0($sp)      # Save d

    # Check if n is 1 (base case)
    li      $t0, 1
    beq     $a0, $t0, base_case

    # Check if n is even or odd
    andi    $t1, $a0, 1      # t1 = n % 2
    beqz    $t1, even_case

    # Odd case: 3n + 1
    li      $t2, 3
    mul     $t2, $a0, $t2    # t2 = 3 * n
    addi    $t2, $t2, 1      # t2 = 3 * n + 1

    j       recursive_call

even_case:
    # Even case: n / 2
    srl     $t2, $a0, 1      # t2 = n / 2

recursive_call:
    # Prepare arguments for recursive call
    lw      $a0, 4($sp)      # Restore n (冗余操作)
    lw      $a1, 0($sp)      # Restore d
    addi    $a1, $a1, 1      # Increment d

    move    $a0, $t2         # Update n
    jal     collatz          # Recursive call

    # Returning from recursive call
    j       end_function

base_case:
    # Base case: n is 1, return d
    lw      $v0, 0($sp)      # Load d into return value register $v0

end_function:
    lw      $ra, 8($sp)      # Restore return address
    addi    $sp, $sp, 12     # Deallocate stack space
    jr      $ra              # Return to caller

实际异常输出

2 -> 2147476133
4 -> 2147476262
6 -> 2147476391
8 -> 2147476520
10 -> 2147476649

问题分析

  1. 参数传递错误:驱动代码中调用collatz前,未将参数放入MIPS约定的$a0(n)和$a1(d)寄存器,而是错误地将参数压入栈中,导致collatz函数读取到垃圾值,输出异常大整数。
  2. 冗余代码:collatz函数的recursive_call部分,先恢复原n到$a0,再立即用新n覆盖,属于冗余操作,虽不影响逻辑但可优化。

修正后的代码

修正后的驱动代码(关键部分修改)

移除不必要的参数压栈操作,调用前将参数放入约定寄存器:

main_for:
    lw      $s1,        ($s0)           # use s1 for xarr[i] value
    li      $s2,        0               # use s2 for initial depth (steps)
    beqz    $s1,        main_end        # if xarr[i] == 0, stop.

    li      $v0,        1
    move    $a0,        $s1             # print_int(xarr[i])
    syscall 

    li      $v0,        4               # print " -> "
    la      $a0,        arrow
    syscall 

    # 传递参数到寄存器,符合MIPS调用约定
    move    $a0,        $s1             # n = xarr[i]
    move    $a1,        $s2             # d = 0
    jal     collatz                     # result = collatz(n, d)

    move    $a0,        $v0             # print_int(result)
    li      $v0,        1
    syscall 

    li      $a0,        10              # print_char('\n')
    li      $v0,        11
    syscall 

    addu    $s0,        $s0,        4   # make s0 point to the next element
    j       main_for

修正后的collatz汇编函数

移除冗余操作,简化递归调用参数准备:

# collatz function in MIPS Assembly
# n in $a0, d in $a1, returns result in $v0
    .text
    .globl collatz
collatz:
    addi    $sp, $sp, -12    # Allocate stack space for ra, n, d
    sw      $ra, 8($sp)      # Save return address
    sw      $a0, 4($sp)      # Save current n
    sw      $a1, 0($sp)      # Save current d

    # Base case: n == 1, return d
    li      $t0, 1
    beq     $a0, $t0, base_case

    # Check if n is even (n & 1 == 0)
    andi    $t1, $a0, 1
    beqz    $t1, even_case

    # Odd case: compute 3*n +1
    li      $t2, 3
    mul     $t2, $a0, $t2
    addi    $t2, $t2, 1
    j       prepare_recurse

even_case:
    # Even case: compute n/2 (logical shift right)
    srl     $t2, $a0, 1

prepare_recurse:
    # Prepare recursive call arguments
    move    $a0, $t2         # New n is t2
    addi    $a1, $a1, 1      # d +=1
    jal     collatz          # Recursive call, result in $v0
    j       cleanup

base_case:
    lw      $v0, 0($sp)      # Return d as result

cleanup:
    lw      $ra, 8($sp)      # Restore return address
    addi    $sp, $sp, 12     # Deallocate stack
    jr      $ra              # Return to caller

验证结果

修正后运行,输出符合预期:

2 -> 1
4 -> 2
6 -> 8
8 -> 3
10 -> 6

内容的提问来源于stack exchange,提问作者HelpNeeded

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 21:29:54