问题描述
我试图理解为什么 mips-g++ 编译一个简单的除法子例程,它用 bnez v0,<jmp>
(bne
的伪指令)跳过实际的 div 指令。我的理解是,如果除数为零,则在这种情况下跳过 div 并捕获或中断是有意义的。如果除数不为零,为什么要分支?
我想了解我所缺少的东西。
子程序
void do_div(int &a,int &b,int &result){
result = a/b;
}
组装列表:
(编译器标志:-O0)
00000000 <_Z6do_divRiS_S_>:
0: 27bdfff8 addiu sp,sp,-8
4: afbe0004 sw s8,4(sp)
8: 03a0f025 move s8,sp
c: afc40008 sw a0,8(s8)
10: afc5000c sw a1,12(s8)
14: afc60010 sw a2,16(s8)
18: 8fc20008 lw v0,8(s8)
1c: 00000000 nop
20: 8c430000 lw v1,0(v0)
24: 8fc2000c lw v0,12(s8)
28: 00000000 nop
2c: 8c420000 lw v0,0(v0)
30: 00000000 nop
34: 14400002 bnez v0,40 <_Z6do_divRiS_S_+0x40>
38: 0062001a div zero,v1,v0
3c: 0007000d break 0x7
40: 00001010 mfhi v0
44: 00001812 mflo v1
48: 8fc20010 lw v0,16(s8)
4c: 00000000 nop
50: ac430000 sw v1,0(v0)
54: 00000000 nop
58: 03c0e825 move sp,s8
5c: 8fbe0004 lw s8,4(sp)
60: 27bd0008 addiu sp,8
64: 03e00008 jr ra
68: 00000000 nop
解决方法
正如@EOF 在评论中提到的,MIPS 有分支延迟槽的概念。
这是来自 BNE
的描述(强调我的):
如果GPR rs和GPR rt的内容不相等,则跳转到有效目标地址延迟槽中的指令执行后。
所以 DIV
总是被执行。您可能会想“好吧,如果 $v0
为 0,那不是问题吗?”。但是 DIV
的描述是这样的:
任何情况下都不会发生算术异常。
如果探地雷达 rt 中的除数为零,则算术结果值不可预测。
因此,在除数为零的情况下,您最终会得到不可预测的结果,无论如何我们都不会使用它,因为接下来要做的是使用 BREAK
指令触发断点异常。
来源:面向程序员的 MIPS32™ 架构第二卷:MIPS32™ 指令集