c-MIPS程序集:为什么main可以在不释放堆栈空间的情况下退出



我有一个大学练习的问题,我不明白。我们必须将C转换为汇编MIPS。总的来说,我必须为a[100]向量分配400个字节,但在解决方案中,我的教授并没有在函数末尾取消分配,为什么会发生这种情况?是否存在不需要释放移动堆栈指针的内存的情况?

这是C:中的代码

int idamax(int n, float * dx, int incx) {
float dmax;
int i, ix, itemp;
if (n < 1) return (-1);
if (n == 1) return (0);
if (incx != 1) {
ix = 1;
dmax = fabs(dx[0]);
ix = ix + incx;
for (i = 1; i < n; i++) {
if (dmax < fabs(dx[ix])) {
itemp = i;
dmax = fabs(dx[ix]);
}
ix = ix + incx;
}
} else {
itemp = 0;
dmax = fabs(dx[0]);
for (i = 1; i < n; i++) {
if (dmax < fabs(dx[i])) {
itemp = i;
dmax = fabs(dx[i]);
}
}
}
return (itemp);
}
int main() {
float a[100];
int l, k, n = 100, lda = 10;
for (k = 0; k < n; ++k) a[k] = (float)((k * k * k) % 100);
k = 4;
l = idamax(n - lda * k - k, &a[lda * k + k], 1) + k;
print_int(l);
exit;
}

主要组件代码:

main:
#______CALL_FRAME______
# 100 float: 400B
#______Totale 400B
addi $sp,$sp,-400
add $t9,$sp,$0 #&a
addi $t0, $0, 100 #n=100
addi $t1, $0, 10 #lda=10
#l in t2, k in t3
add $t3, $0, $0 #k=0
main_forini:
slt $t5,$t3,$t0 #k<?n
beq $t5,$0,main_forend
mult $t3, $t3 #k*k
mflo $t5
mult $t3, $t5
mflo $t5 #k*k*k
div $t5,$t0 #()%n
mfhi $t5
mtc1 $t5,$f0
cvt.s.w $f1,$f0 #(float)()
sll $t5,$t3,2 #k*4
add $t5,$t5,$t9 #&a[k]
swc1 $f1,0($t5) #a[k]=()
addi $t3, $t3, 1 #++k
j main_forini
main_forend:
addi $t3,$0,4 #k=4
mult $t1,$t3 #lda*k
mflo $t5
add $t5,$t5,$t3 #lda*k+k
sub $a0,$t0,$t5 #a0=n-lda*k-k
sll $t5,$t5,2
add $a1,$t5,$t9 #a1=&a[lda*k+k]
addi $a2,$0,1 #a2=1
jal idamax
addi $a0,$v0,4 #a0=l=retval+k
addi $v0,$0,1 #print_int
syscall
addi $v0,$0,10 #exit
syscall

main的执行永远不会到达函数的底部,因此永远不需要清理堆栈;exit()是一个"noreturn"函数。

如果main确实希望使用jr $ra而不是进行exit系统调用返回,则需要将堆栈指针与其他保留调用的寄存器一起还原。否则,您将违反main的调用者希望main遵循的调用约定。

(更新是因为您在使用MARS系统调用的问题中添加了asm:如果main是您代码的顶部,它可能不是一个函数:$ra不是输入时的有效返回地址,因此它无法返回。如果它不是函数,IMO就不要称它为main。(

当进程进行退出系统调用时,操作系统不关心用户空间堆栈指针指向哪里,因此main在退出前无需清理

(在"正常"的C实现中,exit()函数会编译成jal exit或简单的尾调用j exit。但你是为没有C库的MARS模拟器手动编译的,所以你可以内联系统调用,而不是调用包装器函数。(

还要注意,ISO Cexit(int)采用了一个arg,类似于MARSexit2(syscall/$v0=17(。事实上,您甚至没有将exit()作为函数调用,只是在C中编写了exit;,它将exit作为函数指针进行求值,而不调用它,也不使用该值执行任何操作。


通常,Cmain由CRT启动代码调用,例如,该代码可能运行C库init函数,并将argc和argv[]指针放在右侧寄存器中。因此,main通常不是操作系统的实际进程入口点,尤其是在托管实现中。(即编译后的C程序在操作系统下运行,而不是像独立程序那样成为自己的内核。(

如果你只是为MARS或SPIM模拟器或其他东西翻译它,那么除了你所写的,就没有C库或任何代码了,所以你正在写的是通常被称为_start的东西,而不是main

在C中,main是一个函数,但在MARS中,不能从顶级入口点jr $ra,因此入口点不是函数。因此,不要称之为main

在ISOC中,main递归地调用自己,或者其他函数调用main,都是合法的。只有当main真的是一个清理堆栈并正确返回的函数时,它才能工作。但这意味着它不可能也是需要进行exit系统调用的进程入口点。要用一个疯狂的递归main运行一个程序,该程序最终会执行一个Creturn语句(或从main的末尾掉下来(,main几乎必须编译成一个可以用jr $ra返回的真实函数。因此,它必须是jal main_start入口点到的函数。

这里有两个可能的答案。

第一个答案是main是程序的第一个也是最后一个函数。操作系统将在之后进行清理。

第二个答案是针对使用堆栈内存的其他函数。堆栈内存通常通过恢复调用函数的堆栈帧来释放(main没有堆栈帧,因此出现异常(。

相关内容

最新更新