在线指令集手册将涵盖所有内容。以及编译器。
unsigned int fun ( unsigned int a, unsigned int b, unsigned int c )
{
unsigned int r;
r = 0;
if(a==5)
{
r=6;
}
return(r);
}
mips-elf-gcc -O2 -c -fno-delayed-branch so.c -o so.o
mips-elf-objdump -d so.o
so.o: file format elf32-bigmips
Disassembly of section .text:
00000000 <fun>:
0: 24020005 li $2,5
4: 10820004 beq $4,$2,18 <fun+0x18>
8: 00000000 nop
c: 00001025 move $2,$0
10: 03e00008 jr $31
14: 00000000 nop
18: 24020006 li $2,6
1c: 03e00008 jr $31
20: 00000000 nop
等于和不等于很容易,bne,beq...
在这种情况下,编译器通常会生成与此相反的内容
return reg = 0;
compare input reg with 5
if NOT equal then branch to skip
return reg = 6
skip:
return
但并非总是如此,gcc 会这样做
0: 24020005 li $2,5
4: 10820004 beq $4,$2,18 <fun+0x18>
8: 00000000 nop
r2 是返回寄存器,现在用 5 作为临时寄存器加载它,以便进行比较。将输入 reg (r4) 与 5 进行比较,如果相等则跳转到地址 18。如果不相等则继续。
如果不相等则
c: 00001025 move $2,$0
10: 03e00008 jr $31
14: 00000000 nop
将 0 放入返回寄存器 r2 并返回。这条路径相当于
r = 0;
return(r);
如果等于5则
18: 24020006 li $2,6
1c: 03e00008 jr $31
20: 00000000 nop
将 6 放入 r2 并返回
对于这个示例 C 代码,编译器对解决方案非常暴力,除了不会产生更多分支以具有单个退出点(非典型)。
unsigned int fun ( unsigned int a, unsigned int b )
{
unsigned int r;
r = a;
switch(b)
{
case 5: r+=3; break;
case 7: r^=3; break;
}
return(r);
}
有时你会看到工具生成一个跳转表,这取决于架构和其他因素,在这种情况下不会发生,但如果你有 switch(b&3) 然后四种可能的情况,我尝试了 gcc没有做跳表。
但真正的开关是什么?在这种情况下,在所有情况下,它只不过是 if(括号中的参数,在这种情况下只是 b 本身) b 是 5 否则 b 是 7 。 switch 是一个 if-then-else 树,通常编译器会费心去实现它。
00000000 <fun>:
0: 24030005 li $3,5
4: 00801025 move $2,$4
8: 10a30009 beq $5,$3,30 <fun+0x30>
c: 00000000 nop
10: 24030007 li $3,7
14: 14a30004 bne $5,$3,28 <fun+0x28>
18: 00000000 nop
1c: 38820003 xori $2,$4,0x3
20: 03e00008 jr $31
24: 00000000 nop
28: 03e00008 jr $31
2c: 00000000 nop
30: 24820003 addiu $2,$4,3
34: 03e00008 jr $31
38: 00000000 nop
r = a
4: 00801025 move $2,$4
如果 b == 5 那么
0: 24030005 li $3,5
8: 10a30009 beq $5,$3,30 <fun+0x30>
c: 00000000 nop
r+=3 并返回
30: 24820003 addiu $2,$4,3
34: 03e00008 jr $31
38: 00000000 nop
(否则)如果 b==7
10: 24030007 li $3,7
14: 14a30004 bne $5,$3,28 <fun+0x28>
18: 00000000 nop
r ^=3 并返回
1c: 38820003 xori $2,$4,0x3
20: 03e00008 jr $31
24: 00000000 nop
else return (r 设置为前面)
28: 03e00008 jr $31
2c: 00000000 nop
所以它是一棵简单的 if-then-else 树,其中有一个非蛮力无序的东西,r = a 被放置在 if b == 5 混合在一起的中间。
unsigned int fun ( unsigned int a )
{
unsigned int r;
r = 0;
if(a<5)
{
r = 3;
}
return(r);
}
00000000 <fun>:
0: 2c840005 sltiu $4,$4,5
4: 14800004 bnez $4,18 <fun+0x18>
8: 00000000 nop
c: 00001025 move $2,$0
10: 03e00008 jr $31
14: 00000000 nop
18: 24020003 li $2,3
1c: 03e00008 jr $31
20: 00000000 nop
因为 mips 是如何工作的,或者至少是我为之编译的 mips
如果小于无符号则设置
0: 2c840005 sltiu $4,$4,5
由于 C 的编写方式,我们实际上可以丢弃带有 if 小于问题的 a 变量。因此,如果 r4 (a) 小于 5,则“设置”r4(非零)
所以现在小于变成了一个相等或不相等的问题
if(r4 is not equal to zero) 结合上面的意思是如果a小于5那么
4: 14800004 bnez $4,18 <fun+0x18>
8: 00000000 nop
返回 3
18: 24020003 li $2,3
1c: 03e00008 jr $31
20: 00000000 nop
否则返回0
c: 00001025 move $2,$0
10: 03e00008 jr $31
14: 00000000 nop
Mips 不像大多数其他指令集那样喜欢/使用标志。大多数其他人会有一个比较指令,它执行减法并设置一堆标志,但不将减法存储到寄存器中,然后后续指令将使用这些标志。 mips 使用无标志的理念,因此进行比较并在同一指令中对其进行操作。以另一种架构为例:
00000000 <fun>:
0: e3500004 cmp r0, #4
4: 93a00003 movls r0, #3
8: 83a00000 movhi r0, #0
c: e12fff1e bx lr
将 a 与 4 进行比较,如果小于或相同,则 r = 3 如果更高的 r = 0 并返回。这在某种程度上也是非常不典型的,因为此处显示的指令集 (arm) 允许按指令条件执行,而不仅仅是分支是有条件的,而 mov 和 add 则不是。
而且这个比较典型
00000000 <_fun>:
0: 0a00 clr r0
2: 2d97 0002 0004 cmp 2(sp), $4
8: 8301 blos c <_fun+0xc>
a: 0087 rts pc
c: 15c0 0003 mov $3, r0
10: 0087 rts pc
这几乎是一对一的蛮力
r = 0
0: 0a00 clr r0
比较a(传入堆栈)和4
2: 2d97 0002 0004 cmp 2(sp), $4
如果更低或相同,则分支到地址 c:
8: 8301 blos c <_fun+0xc>
如果 5 或更高,则返回(r = 0)
a: 0087 rts pc
如果小于5(小于等于4)则返回3
c: 15c0 0003 mov $3, r0
10: 0087 rts pc
(目标在右边 mov $3,r0 表示 r0 = 3。$ 表示常量)