编译此C代码后:
int main(void)
{
unsigned int i = 5;
switch(i)
{
case 1: i = i + 3; break;
case 5: i = i<<3; break;
case 10: i = i>>2;
}
}
我有本大会MIPS:
.file 1 "SwitchCase.c"
.text
.align 2
.globl main
.ent main
main:
.frame $fp,16,$31 # vars= 8, regs= 1/0, args= 0, gp= 0
.mask 0x40000000,-8
.fmask 0x00000000,0
addiu $sp,$sp,-16
sw $fp,8($sp)
move $fp,$sp
li $2,5 # 0x5
sw $2,0($fp)
lw $2,0($fp)
sw $2,4($fp)
li $2,5 # 0x5
lw $3,4($fp)
beq $3,$2,$L4
lw $3,4($fp)
sltu $2,$3,6
beq $2,$0,$L6
li $2,1 # 0x1
lw $3,4($fp)
beq $3,$2,$L3
j $L2
$L6:
li $2,10 # 0xa
lw $3,4($fp)
beq $3,$2,$L5
j $L2
$L3:
lw $2,0($fp)
addiu $2,$2,3
sw $2,0($fp)
j $L2
$L4:
lw $2,0($fp)
sll $2,$2,3
sw $2,0($fp)
j $L2
$L5:
lw $2,0($fp)
srl $2,$2,2
sw $2,0($fp)
$L2:
move $sp,$fp
lw $fp,8($sp)
addiu $sp,$sp,16
j $31
.end main
我真的不明白这个汇编MIPS,特别是带有 $ sp,$ fp 寄存器的指令(这个代码的寄存器的目的),有人可以帮我解释一下这个吗? / p>
答案 0 :(得分:2)
可以在Wikipedia中找到相关信息。 $sp
代表堆栈指针,$fp
代表帧指针。
您的main()
在堆栈上为其变量创建了一个框架。帧长16个字节。
addiu $sp,$sp,-16 # reserve 16 B on stack
sw $fp,8($sp) # store previous frame pointer onto stack
move $fp,$sp # set frame pointer for main()
# ...
lw $2,0($fp) # load a value from $fp+0 to register 2
sw $2,4($fp) # save a value to $fp+4 from register 2
# ...
move $sp,$fp # reverting the first 3 instructions now
lw $fp,8($sp)
addiu $sp,$sp,16
@ Dolda2000已经解释为什么这样做了。
答案 1 :(得分:2)
从评论中判断实际问题:'“我认为只是通过子程序我们必须使用$ sp,$ fp,但我不在这里使用任何子程序(程序),为什么这个代码有$ sp,$ fp寄存器吗?“
首先,main
仍需要一个自己的堆栈框来保存局部变量(以及临时的非命名值),这就是为什么它需要一个有效的帧指针($fp
)
如果你想知道它为什么会保存前一个帧指针,那是因为甚至main
被其他函数调用(在某些CRT库中通常称为_start
),这需要有它的框架保留。
main是否依次调用其他子例程是无关紧要的,因为这并不能解除它对堆栈帧的需求,如上所述。
此外,它通常被指定为平台的ABI的一部分,所有功能都应该设置适当的帧,无论它们是否真正需要它们。这是因为堆栈上的正确框架链通常对调试器和其他分析工具很有用,之后经常会发现在需要时缺少它们会很麻烦(例如对崩溃的程序进行事后调试) 。但是,如果您真的想要,可以通过使用某些特定于编译器的标志-fomit-frame-pointer
来告诉大多数编译器违反ABI而忽略它们。