分支预测如何与指令指针交互

3
我理解,在处理器流水线的开始处,指令指针(指向下一条要执行的指令的地址)在取指之后会被分支预测器更新,以便在下一个周期中获取新的地址。
然而,如果在流水线的早期修改了指令指针,那么这是否会影响当前处于执行阶段的指令,因为这些指令可能依赖于旧的指令指针值?例如,在执行call时,当前的EIP需要被推入堆栈,但是当指令指针在分支预测期间更新时,这是否会受到影响?

1
在许多流水线架构中,程序计数器是虚假的,软件可以看到的那个有正确的值。逻辑使用了许多其他指令指针地址来执行真正的重活,包括一个或多个分支预测计算、实际指向内存获取的指针等。Arm是一个简单的架构,程序计数器提前两条指令已经不再是这样了,管道更深,具有预测功能。然而,我们仍然有一个R15,它给出了指令集设计的结果。 - old_timer
一个可用的(伪)寄存器像EIP一样,将具有正确的指令集数值,独立于用于实际获取的任何锁存或组合地址。 - old_timer
1个回答

10

你好像假定整个CPU核只使用一个物理EIP寄存器。

这是不可行的,因为任何可能引发异常的指令都需要知道自己的地址。或者当外部中断到达时,CPU可以决定在任何指令之后服务中断,使其成为体系结构上的EIP。在长模式(x86-64)下,还有RIP相对寻址模式,因此call不是唯一需要当前程序计数器作为数据的指令。

简单的流水线CPU可能每个流水线阶段都有一个EIP。

现代超标量乱序x86将每个正在执行的指令(或者也许是每个uop)关联一个EIP(或RIP);但多uop指令的所有uop都与彼此相关联,因此指令不能部分退役。

与体系结构上的其他状态(例如EFLAGS、EAX等)不同,该值在解码后就已经静态地确定。实际上,即使早于立即数值;指令边界在预解码阶段(或在L1i缓存中标记),以便将多个指令并行输入多个解码器。

早期的取指/解码阶段可能只跟踪16字节或32字节取指块的地址,但在解码之后,我猜想内部uop表示中有一个地址字段。对于非跳转指令,它可能只是相对于前一个指令的小偏移量(为了节省空间),因此如果需要,可以计算出来,但我们已经深入到实现细节中了。乱序执行维护了指令按程序顺序运行的“幻象”,它们确实按顺序发射和退出(进入/离开核心的乱序执行部分)。
相关: x86寄存器:MBR/MDR和指令寄存器 基于Toy CPU的看法犯了类似的错误假设。也没有保存机器代码字节的“当前指令”寄存器。在那里查看更多关于乱序执行/流水线CPU的链接。
分支预测必须在块被解码之前工作。即,假设我们刚刚获取了地址为abc的块,我们需要预测下一个要获取的块。也就是说,预测必须预测在并行解码的16字节指令块中跳转的存在。
相关: 为什么英特尔在这些年中改变了静态分支预测机制?

现代超标量乱序x86处理器每个指令都有一个EIP(或RIP)相关联”这句话不是有点误导吗?我认为当执行uop时,EIP就像任何其他输入寄存器一样 - 即使用EIP的微架构值。” - Margaret Bloom
1
@MargaretBloom:不完全是这样;它不会被存储在寄存器文件中,因为在解码时对于每个指令都是静态已知的,并且不能成为输出。控制依赖与数据依赖的处理方式不同。我已经重新表述了那句话,因为它不太像我想要表达的意思。 - Peter Cordes
1
大多数指令实际上并不需要知道自己的IP地址,至少在高效的方式下是如此。因此,完全有可能地址不会与每个指令一起存储,而只会在发生中断或异常时以某种不一定快速的方式进行追溯计算。然而,像call或rip-relative等直接使用IP的指令在解码时仍会填充它。正如你所指出的,这已经涉及到具体的实现细节,我只是在猜测。 - BeeOnRope
1
顺便说一句,我认为取指/解码单元几乎必须为预测计算“精确”的地址,而不使用像32字节这样更大的粒度,因为(a)从解码开始,您已经需要知道正确的偏移量以便可以正确解码指令,并且因为您需要形成正确的连续指令流;(b)预测器本身需要实际地址以便进行下一个预测,因为可能在一个块内有多个分支。所以(a)意味着粗略预测的窗口很小,而(b)则可能为零。 - BeeOnRope
1
有可能预测器并不像我在(b)中所建议的那样行事——也就是说,他们的“最紧密”的循环只是在块的水平上进行预测,然后再细化目标以供解码器使用。这意味着他们会被同一块中具有不同目标和模式的多个分支所欺骗,这可以从软件中进行测试。 - BeeOnRope
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接