1 assembly x86-64 instruction-set cpu-architecture cpu-registers
假设我们使用的是 x86-64 机器,这意味着它的通用寄存器是 64 位长,它的数据总线一次可以处理 64 位,它的 ALU 可以处理最大 64 位数量(对吗?)。
有一个简单的指令,例如
MOV $5, %eax
Run Code Online (Sandbox Code Playgroud)
通过 64 位数据总线将 32 位数字移入 CPU 寄存器。
我已阅读以下内容:
An x86-64 instruction may be at most 15 bytes in length.
Run Code Online (Sandbox Code Playgroud)
我的问题是,如果数据总线最大为 64 位,这怎么可能?它如何处理 120 位的指令。CPU是否会在多个周期中获取它?
我的第二个问题是,是否有长度更大的特殊寄存器来存储所有 120 位?
指令获取是与数据加载/存储分开的数据路径。它不是使用64 位mov指令完成的。有专用逻辑处理可变长度未对齐 x86 指令的获取和解码。
一条指令可以跨越 4k 页边界,因此它的字节来自 2 个不连续的物理页!前端必须能够获取指令字节并将它们连接到缓冲区中。
即使 8086 也有一个小的指令预取缓冲区,尽管解码不一定需要它,因为在 8088 上它比最长的指令要小(不包括前缀)。
请参阅David Kanter 的 Sandybridge 文章,了解 Sandybridge(以及 Nehalem 和 Bulldozer)中的前端图。还有Agner Fog 的 microarch 指南。有关最新 AMD 前端的更多信息,请参阅https://en.wikichip.org/wiki/amd/microarchitectures/zen#Decode 。
在 P6 和 SnB 系列 Intel CPU 上,代码获取和预解码(以查找 insn 边界)发生在 16 字节块中,每个周期查找最多 6 条指令的长度,并且每个周期消耗最多 16 字节的 x86 机器代码。如果指令运行到块末尾,预解码器将保留这些字节直到下一个周期。Agner Fog 的 microarch pdf 有一些关于优化以避免预解码瓶颈的细节;x86解码很难。例如,在某些情况下,操作数大小前缀会更改指令其余部分的长度。例如,前缀是(5 字节)和( + 3 字节)66之间的唯一区别。在这种情况下,英特尔 CPU 中的预解码器会停止运行,需要额外的周期来处理它。(Alexis 的回答声称长度查找很容易。对于多年来积累的所有 ISA 扩展来说,这肯定不容易,例如,VEX 前缀是另一条指令的无效编码。而且它变得更加困难当你尝试并行执行多个指令时,因为你必须考虑第一个指令之后的所有指令的多个起点。较旧的 CPU 过去解码前缀的速度很慢,例如每个前缀需要一个额外的周期,甚至转义字节。但现代主流英特尔(不是低功耗)可以处理任意数量的前缀而不会受到任何影响。)add eax, imm32add ax, imm1666
一次最多向解码器馈送 4 个指令(或者使用宏融合时馈送到 5 或 6 个指令)。根据 uarch,这可以产生最多 7 个微操作 (uops)(Core2/Nehalem 上的 4-1-1-1 模式)、4 个(Skylake 之前的 SnB 系列)或 5 个(Skylake)。SKL 仍然只有 4 个解码器,但允许它们产生最多 5 个微指令,例如 2-1-1-1 等模式。
并行解码 x86 指令是一个瓶颈,现代 CPU(Intel 自 SnB 系列以来,AMD 自 Zen)缓存解码的微指令以简化代码的热门部分。Pentium 4 的跟踪缓存是该方向的早期实验,但效果不佳(并且它没有解码器吞吐量来在跟踪缓存未命中时保持可接受的性能)。
另请参阅90 年代早期的奔腾微处理器与当今的英特尔设计之间有什么关系?关于逆向计算,我的回答谈到了为什么 P4 是 CPU 架构的死胡同,以及 P6 系列(PPro / PIII)如何演变为英特尔当前的 Sandybridge 系列。
所有 x86-64 CPU 都足够新,具有宽内部数据路径的高性能,但 16 位和 32 位 CPU 具有相同的 15 字节最大长度(包括冗余前缀)。如果他们在查看操作码、modrm + 额外寻址模式字节和/或立即数之前单独解码这些指令,他们可能会使用至少足够大的缓冲区来容纳不包括前缀的指令。
除了原始的 8086 之外,其中一条指令的充满 REP 前缀的 64k 代码段是有效的。当时英特尔还没有对指令长度定义任何限制,并且 8086 解码的前缀与指令的其余部分分开。
还相关: