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调用约定的问题:
保存寄存器($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函数返回方式错误
当前代码中,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
相关产品推荐
相关产品推荐

