SPARC 体系结构之判断循环

SPARC 体系结构之判断循环

当前版本: 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’

 

 

 

发表评论

1 + 9 = ?