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

C代码转MIPS:函数调用与循环实现的调用规范及.end指令问询

问题描述

我已经把以下C代码转换为MIPS代码,测试后输出与C代码一致,但不确定是否正确遵循了MIPS调用约定,同时不清楚翻译后的.end g指令位置是否正确,附上相关代码请求解答:

// TEST 1, comment out when running with TEST2
int sx[5] = {1, 2, 3, 4, 5};
int sy[5] = {5, 4, 3, 2, 1};

// TEST 2, uncomment when running with TEST2
//int sx[5] = {1, 2, 3, 4, 5};
//int sy[10] = {1, 0, 1, 0, 1, 0, 1, 0, 1, 0};

void g(int sa, int incx, int incy)
{
  int i, ix, iy;
  
  if (incx == 1 && incy == 1) {
    for (i=0; i<5; i++)
      sy[i] = sy[i] + sa*sx[i];
  }
  else {
    ix = 0, iy = 0;
    for (i=0; i<5; i++) {
      sy[iy] = sy[iy] + sa*sx[ix];
      ix = ix + incx;
      iy = iy + incy;
    }
  }
}

main()
{
  g(2, 1, 1); // TEST1
  //g(2, 1, 2); // TEST2
}

对应的MIPS代码:

.global main
    
    .data
## TEST 1
sx: .word 1, 2, 3, 4, 5
sy: .word 5, 4, 3, 2, 1
    
## TEST 2
#sx: .word 1, 2, 3, 4, 5
#sy: .word 1, 0, 1, 0, 1, 0, 1, 0, 1, 0
    
    .text
    .ent main
main:
    ## TEST 1
    li      $a0, 2  # int sa
    li      $a1, 1  # int incx
    li      $a2, 1  # int incy
    
    ## TEST 2
    #li     $a0, 2  # int sa
    #li     $a1, 1  # int incx
    #li     $a2, 2  # int incy
    
    jal     g       # function call to g

loop:
    j   loop
    nop
    .end main
    
    .ent g
    
## void g(int sa, int incx, int incy)
g:
    li      $s0, 0  # int i = 0
    li      $s1, 0  # int ix = 0
    li      $s2, 0  # int iy = 0
    
    ## if (incx == 1 && incy == 1) {
    # for (i=0; i<5; i++)
      # sy[i] = sy[i] + sa*sx[i];
      
    ## if either incx or incy are not equal to 1, branch to L2
    bne $a1, 1, L2
    nop
    bne $a2, 1, L2
    nop
## if-body
L1:
    slti    $t0, $s0, 5     # if $s0 (index i) is less than 5, set $t0 to 1
    beqz    $t0, loop       # if $t0 = 0 (meaning i >= 5), exit if-block
    nop 

### arithmetic code

    sw      $t7, 0($t4)     # loads $t7 into sy[i]
    addi    $s0, $s0, 1     # i++
    j       L1
    nop
## else-body
L2:
    slti    $t0, $s0, 5     # $s0 (index i) is less than 5, set $t0 to 1 
    beqz    $t0, loop       # if index i reaches max, exit to loop
    nop

#### arithmetic code

    sw      $t7, 0($t4)     # $t7 (arithmetic) stored into address for sy[iy]
    addi    $s0, $s0, 1     # i++
    j L2
    nop
    .end g
解答

一、MIPS调用约定问题

你的代码目前存在两处违反MIPS调用约定的问题:

  1. 保存寄存器($s0-$s2)未正确保存恢复
    $s0-$s7属于被调用者保存寄存器(callee-saved),函数g如果要使用这些寄存器,必须先把它们当前的值压入栈中保存,函数退出前再从栈中恢复。你现在直接修改$0-$s2,会破坏调用者(main函数)可能正在使用的这些寄存器的值,违反约定。
    正确做法:进入g函数后先执行栈操作:

    g:
        addi    $sp, $sp, -12  # 为3个$s寄存器分配栈空间
        sw      $s0, 0($sp)
        sw      $s1, 4($sp)
        sw      $s2, 8($sp)
        # 后续原有代码...
    

    在函数退出前(执行jr $ra之前)恢复:

    lw      $s0, 0($sp)
        lw      $s1, 4($sp)
        lw      $s2, 8($sp)
        addi    $sp, $sp, 12
        jr      $ra
    
  2. 函数返回方式错误
    当前代码中,L1和L2循环结束后跳转到main函数的loop标签,这完全不符合调用约定。g函数是void类型,执行完毕后应该通过jr $ra返回调用者(main),而不是跳转到调用者的内部标签。
    你需要把L1和L2中的beqz $t0, loop改成跳转到g函数内的返回代码段,比如新增一个exit_g标签:

    L1:
        slti    $t0, $s0, 5
        beqz    $t0, exit_g  # 循环结束跳转到exit_g
        nop
        # ...原有算术代码
    L2:
        slti    $t0, $s0, 5
        beqz    $t0, exit_g  # 循环结束跳转到exit_g
        nop
        # ...原有算术代码
    exit_g:
        # 恢复$s寄存器(如上面的代码)
        jr      $ra
    

二、.end g指令位置

.end g的位置是正确的,它需要和.ent g配对,放在g函数的最后一条指令之后,用于标记函数g的结束,当前代码中的位置符合要求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 15:10:26