0

CPU如何区分指令和数据?

CPU 在执行代码时如何确定指令的长度(从 1 字节到最大 15 字节不等)?如果假设 cpu 不确定指令的长度,它可能会将数据作为指令的一部分。在这种情况下,可能会出现不希望的结果或 cpu 不执行该指令(如果不在操作码表中)。cpu如何判断,是数据还是指令?

4

1 回答 1

2

它不必预测。 从逻辑上讲,它一次解码一个字节,直到看到一条完整的指令。 (或用于 disp32 或 imm32 的 dword 块,或由较早字节暗示的指令的其他多字节部分。)指令的长度由前缀和操作码 + modrm + SIB 字节暗示。在查看了这些之后,CPU 可以确定需要获取多少指令字节。

但是一个真正的 CPU 只需要给出这样做的假象,并且可以查看后面的字节,只要它最终做正确的事情,如果它们不是应该执行的指令的一部分。

从物理上讲,在实际实现中,只要您最终做正确的事情,推测性地加载以后的字节就没有问题。

例如,L1 指令缓存使用 64 字节行,因此逻辑执行达到一个字节意味着整个 64 字节内存块将在 I-cache 中,即使它也在L1 D-cache 中,因为您执行了一些数据加载指令在同一行的其他字节上。

当然,从 L1I 缓存中获取也不是一次单个字节。在现代 x86 上,解码查看 32 或 16 字节的块以查找指令边界。例如,让我们看看 P6 系列,它没有 uop 缓存,因此它总是从 L1I 获取/解码。

来自Agner Fog 的 microarch PDF,在 PPro/PII/PIII 部分:

6.2 取指令

指令代码以对齐的 16 字节块从代码缓存中获取放入可以容纳两个 16 字节块的双缓冲区。双缓冲区的目的是使解码跨越 16 字节边界(即可被 16 整除的地址)的指令成为可能。代码以块的形式从双缓冲区传递到解码器,我将其称为 IFETCH 块(指令获取块)。IFETCH 块最长为 16 个字节。在大多数情况下,取指单元使每个 IFETCH 块从指令边界开始,而不是从 16 字节边界开始。然而,指令获取单元需要来自指令长度解码器的信息,以便知道指令边界在哪里。如果此信息不能及时获得,则它可能会在 16 字节边界处启动 IFETCH 块。这种并发症将在下面更详细地讨论。

然后,预解码器流水线阶段找到指令边界(假设它们都是有效指令),然后(根据分支预测单元的预测)将 3 条指令的机器代码并行发送到 3 个解码器。(Core2 扩大到 4 个解码器,Skylake 扩大到 5 个解码器,即使管道宽度保持在 4 微秒宽)。

如果某处有非法指令(或无条件指令jmp,或jcc恰好被采用的指令),那么稍后的“指令”将毫无意义,并在发现该事实后被丢弃。

https://www.realworldtech.com/nehalem/5/讨论了上一代 P6 系列微架构 Nehalem 中的解码阶段。但是 Agner Fog 的描述可能更有助于理解CPU 如何查看一堆字节,然后只使用逻辑上应该作为指令执行的字节

于 2020-02-20T13:47:58.503 回答