48

根据ARM IC。

在 ARM 状态下,PC 的值是当前指令的地址加上 8 个字节。

在拇指状态:

  • 对于 B、BL、CBNZ 和 CBZ 指令,PC 的值是当前指令的地址加上 4 个字节。
  • 对于所有其他使用标签的指令,PC 的值是当前指令的地址加上 4 个字节,结果的 bit[1] 清零以使其字对齐。

简单地说,PC寄存器的值指向下一条指令之后的指令。这是我不明白的。通常(特别是在 x86 上)程序计数器寄存器用于指向要执行的下一条指令的地址。

那么,这背后的前提是什么?条件执行,可能吗?

4

2 回答 2

74

这是一个令人讨厌的遗留抽象泄漏。

最初的 ARM 设计有一个 3 级流水线(获取-解码-执行)。为了简化设计,他们选择将 PC 读取为当前在取指地址线上的值,而不是 2 个周期前当前正在执行的指令的值。由于大多数 PC 相关地址是在链接时计算的,因此让汇编器/链接器补偿该 2 条指令偏移比设计所有逻辑来“纠正”PC 寄存器更容易。

当然,这一切都牢牢地放在“30 年前有意义的事情”堆上。现在想象一下,在今天的 15+ 阶段、多问题、无序流水线中,要在该寄存器中保持有意义的值需要什么,您可能会理解为什么现在很难找到一个认为将 PC 暴露为寄存器是个好主意。

不过,从好的方面来说,至少它不像延迟槽那么可怕。相反,与您想象的相反,有条件地执行每条指令实际上只是围绕该预取偏移量的另一种优化。当围绕条件代码进行分支时(或者仍然像疯子一样执行管道中剩下的任何内容),您可以完全避免非常短的分支,而不是总是不得不采取管道刷新延迟;管道保持忙碌,当标志不匹配*时,解码的指令可以作为 NOP 执行。同样,这些天我们有有效的分支预测器,它最终更像是一个障碍而不是帮助,但对于 1985 年来说它很酷。

* “...地球上 NOP 最多的指令集。”

于 2014-06-07T00:10:45.887 回答
2

确实如此...

下面是一个示例: C 程序:

int f,g,y;//global variables
int sum(int a, int b){
     return (a+b);
}
int main(void){
    f = 2;
    g = 3;
    y = sum(f, g);
    return y;
}

编译成汇编:

    00008390 <sum>:
int sum(int a, int b) {
return (a + b);
}
    8390: e0800001 add r0, r0, r1
    8394: e12fff1e bx lr
    00008398 <main>:
int f, g, y; // global variables
int sum(int a, int b);
int main(void) {
    8398: e92d4008 push {r3, lr}
f = 2;
    839c: e3a00002 mov r0, #2
    83a0: e59f301c ldr r3, [pc, #28] ; 83c4 <main+0x2c> 
    83a4: e5830000 str r0, [r3]
g = 3;
    83a8: e3a01003 mov r1, #3
    83ac: e59f3014 ldr r3, [pc, #20] ; 83c8 <main+0x30>
    83b0: e5831000 str r1, [r3]
y = sum(f,g);
    83b4: ebfffff5 bl 8390 <sum>
    83b8: e59f300c ldr r3, [pc, #12] ; 83cc <main+0x34>
    83bc: e5830000 str r0, [r3]
return y;
}
83c0: e8bd8008 pop {r3, pc}
83c4: 00010570 .word 0x00010570
83c8: 00010574 .word 0x00010574
83cc: 00010578 .word 0x00010578

见上面 LDR 的 PC 值——这里用于将变量 f,g,y 的地址加载到 r3。

    83a0: e59f301c ldr r3, [pc, #28];83c4 main+0x2c
    PC=0x83c4-28=0x83a8-0x1C = 0x83a8

PC 的值就是当前执行指令的下一条指令。由于ARM使用32位指令,但它使用字节地址,所以+ 8表示8字节,两条指令的长度。

所以附上了 ARM archi 的 5 阶段管道 linefetch、decode、execute、memory、writeback

ARM 的 5 级流水线

PC寄存器每个时钟加4,所以当指令冒泡执行时——当前指令,PC寄存器已经过去了2个时钟!现在是+ 8。这实际上意味着:PC指向“获取”指令,当前指令意味着“执行”指令,所以PC意味着下一个要执行的指令。

顺便说一句:图片来自哈里斯的数字设计和计算机体系结构 ARM 版的书

于 2018-04-10T10:41:45.930 回答