当前版本: 1.0
完成日期: 2009-04-17
作者: Jack Tan
Email: jiankemeng@gmail.com
版权声明: 转载请注明出处,商业用途请联系作者
先看一些基本的 C 语言判断循环结构在 ISA 层面上怎么被支持:
A. 判断
int test_if(int s) { int i; if(s > 0) i = s; else if(s < 0) i = -s; else i = s * 8; return i; }
000000000000000c <test_if>: c: 80 a2 20 00 cmp %o0, 0 ---> %o0 与 0 比较 10: 14 48 00 06 bg %icc, 28 <test_if+0x1c> ---> 大于 0 则跳转;annul 位为 0,则延迟槽中的指令总是被执行 14: 82 10 00 08 mov %o0, %g1 ---> 位于延迟槽,o0 值拷贝入 g1 18: 84 20 00 08 neg %o0, %g2 ---> o0 取反,对应于 -s 1c: 83 2a 20 03 sll %o0, 3, %g1 ---> o0 逻辑左移 3 位,其对应于 s*8 20: 80 a2 20 00 cmp %o0, 0 ---> 重新比较,据结果置 CCR[icc] 的相应位 24: 83 64 c0 02 movl %icc, %g2, %g1 ---> 小于 0 则将 g2 入 g1 28: 81 c3 e0 08 retl ---> 叶函数返回,不改变 CWP,等价于 jmpl %o7+8, %g0 2c: 91 38 60 00 sra %g1, 0, %o0 ---> 延迟槽,总是被执行
可以看到:
大于 0 时 g1 置 o0 就直接返回了;
小于等于 0 时,g1 置 s*8,g2 置 -s;
后条件拷贝,小于 0 则将 g2 入 g1 作为返回值 (-s),等于 0 就以 g1(s*8) 为返回值
注意到 test_if 是一个叶函数(其不调用其他函数),作为一个优化,其无需使用 save 指令去新获取一个独立的窗口,小心复用父函数的即可。
B. 循环
int test_cyc1(int c) { int sum = 0; do { sum += c; c--; } while(c > 0); return sum; }
0000000000000030 <test_cyc1>: 30: 84 10 20 00 clr %g2 34: 84 00 80 08 add %g2, %o0, %g2 ---> sum += c 38: 82 02 3f ff add %o0, -1, %g1 ---> g1 = o0 - 1 3c: 91 38 60 00 sra %g1, 0, %o0 ---> g1 拷贝入 o0 40: 80 a2 20 00 cmp %o0, 0 44: 34 4f ff fd bg,a %icc, 38 <test_cyc1+0x8> ---> o0 > 0 则跳转,annul 位为 1,则延迟槽中的指令只在跳转发生时才会被执行 48: 84 00 80 08 add %g2, %o0, %g2 ---> 延迟槽,跳转时被执行 4c: 81 c3 e0 08 retl ---> 叶函数返回,不改变 CWP,等价于 jmpl %o7+8, %g0 50: 91 38 a0 00 sra %g2, 0, %o0 ---> 置返回值
跳转类指令,附加 ‘,a’ 则是将 annul 位置 1
对条件跳转类指令,annul 位为 1,则CPU 在执行时只在跳转发生时才会执行延迟槽中的指令
int test_cyc2(int c) { int sum = 0; for(; c > 0; c--) sum += c; return sum; }
000000000000004c <test_cyc2>: 54: 80 a2 20 00 cmp %o0, 0 58: 04 40 00 08 ble,pn %icc, 78 <test_cyc2+0x24> ---> annul 位为 0,则延迟槽中的指令总是被执行 5c: 84 10 20 00 clr %g2 60: 84 00 80 08 add %g2, %o0, %g2 64: 82 02 3f ff add %o0, -1, %g1 68: 91 38 60 00 sra %g1, 0, %o0 6c: 80 a2 20 00 cmp %o0, 0 70: 34 4f ff fd bg,a %icc, 64 <test_cyc2+0x10> 74: 84 00 80 08 add %g2, %o0, %g2 78: 81 c3 e0 08 retl 7c: 91 38 a0 00 sra %g2, 0, %o0
跳转类指令,附加 ‘,pn’ 则是指令编码为静态转移预测为不发生;’,pt’ 则为静态转移预测为发生
1. 寄存器
SPARC 实现有条件寄存器 CCR (Condition Codes Register)
其长为 8 位,结构如下:
7 4 3 0
——————-
| xcc | icc |
——————-
xcc 用于存放 64 bit 计算结果的条件位
icc 用于存放 32 bit 计算结果的条件位
他们的结构一样: | N | Z | V | C |
四位,分别表示 Negative, Zero, Overflow, Carry (or borrow)
其中 Carry 位,只要计算结果小于 0,也要置位
2. 指令
Bicc —> branch on CCR[icc]
BPcc —> branch on CCR (including icc and xcc) with prediction
BPr —> branch on Interger Register with prediction
BPcc 类指令只是在 Bicc 类的基础上加了静态转移预测
Bicc 类有:
BA/BN — Branch Always/Never
BNE/BE/BG/BLE/BGE/BL/BGU/BLEU — Branch on !=/=/>/<=/>=/</>, Unsigned/<=, Unsigned
BCC/BCS — Branch on Carry Clear (>=, unsigned)/Carry Set (<, unsigned)
BPOS/BNEG — Branch on Positive/Negative
BVC/BVS — Branch on Overflow Clear/Set
其中 BA/BN 为可分为非条件转移,annul 位的语义有些不同:
annul = 0, 延迟槽中的指令总是被执行
annul = 1, 延迟槽中的指令总是不被执行
其它属条件转移类指令,annul 位的语义为:
annul = 0, 延迟槽中的指令总是被执行
annul = 1, 延迟槽中的指令只在跳转发生时才被执行
其亦带 ‘,a’ ‘,pt’ ‘,pn’ 后缀。不明确指定,汇编器默认使用 ‘,pt’