它不必预测。 从逻辑上讲,它一次解码一个字节,直到看到一条完整的指令。(或 disp32 或 imm32 的 dword 块,或由较早字节隐含的指令的其他多字节部分。)该指令由前缀和操作码 + modrm + SIB 字节隐含。看了这些之后,CPU 就知道它还需要获取多少指令字节。
但是真正的 CPU 只需要给出这样做的假象,并且 可以查看后面的字节,只要它最终会做正确的事情,如果它们不是应该执行的指令的一部分。
在实际实现中,只要您最终做对了,推测性地加载后面的字节就没有问题。
例如L1 指令缓存使用 64 字节行,因此逻辑执行达到一个字节意味着整个 64 字节内存块将在 I-cache 中,即使它 也 在 L1 D-cache 中,因为您对同一行中的其他字节执行了一些数据加载指令。
当然,从 L1I 缓存中获取也不是一次单个字节。在现代 x86 上,解码查看 32 或 16 字节的块以查找指令边界。例如让我们看看 P6 系列,它没有 uop 缓存,所以它总是从 L1I 获取/解码。
来自Agner Fog's microarch PDF,在 PPro/PII/PIII 部分:
6.2 指令获取
指令码以对齐的 16 字节块从代码缓存中获取,并转换为双精度
可以容纳两个 16 字节块的缓冲区。双缓冲的目的是为了
可以对跨越 16 字节边界的指令进行解码(即地址可整除)
16)。代码从双缓冲区传递到块中的解码器,我将
调用 IFETCH 块(指令提取块)。 IFETCH 块最长为 16 个字节。在
大多数情况下,取指单元使每个 IFETCH 块从一条指令开始
边界而不是 16 字节的边界。但是,取指令单元需要
来自指令长度解码器的信息,以便知道指令在哪里
边界是。如果此信息无法及时获得,则它可能会启动 IFETCH 块
以 16 字节为边界。这种并发症将在下面更详细地讨论。
预解码器流水线阶段然后找到指令边界(假设它们都是有效指令),然后(根据分支预测单元所做的预测)将 3 条指令的机器代码并行发送到 3 个解码器. (Core2 扩大到 4 个解码器,Skylake 扩大到 5 个解码器,尽管流水线宽度保持在 4 微秒宽)。
如果某处存在非法指令(或无条件的jmp,或恰好被采用的jcc),那么稍后的“指令”将毫无意义,并在发现该事实后被丢弃。
https://www.realworldtech.com/nehalem/5/ 谈论 Nehalem 中的解码阶段,这是上一代 P6 系列微架构。但 Agner Fog 的描述可能更有助于理解 CPU 如何查看一堆字节,然后只使用逻辑上应该作为指令执行的字节。