x86 寄存器:MBR/MDR 和指令寄存器
x86 registers: MBR/MDR and instruction registers
据我所知,IA-32 架构有十个 32 位寄存器和六个 16 位寄存器。
32位寄存器如下:
- 数据寄存器 - EAX、EBX、ECX、EDX
- 指针寄存器 - EIP、ESP、EBP
- 索引寄存器 - ESI、EDI
- 控制寄存器-EFLAG(EIP也归类为控制寄存器)
16位寄存器如下:
- 代码段:包含所有要执行的指令。
- 数据段:包含数据、常量和工作区。
- 堆栈段:它包含数据和return过程或子程序的地址。
- 额外段 (ES)。指向额外数据的指针。
- F 段 (FS)。指向更多额外数据的指针。
- G段(GS)。指向更多额外数据的指针。
但是,我找不到有关当前指令寄存器 (CIR) 或内存缓冲寄存器 (MBR)/内存数据寄存器 (MBR) 的任何信息。这些寄存器是否被称为其他东西?这些寄存器是 32 位的吗?
我假设它们是 32 位的,并且在此体系结构下最常用的指令长度不到 4 个字节。从观察来看,很多指令似乎都在4个字节以下,例如:
- 推动 EBP (55)
- MOV EBP, ESP (8B EC)
- LEA (8D 44 38 02)
对于较长的指令,CPU 将使用前缀代码和其他可选代码。较长的指令将需要一个以上的周期才能完成,具体取决于指令长度。
我说的寄存器长度是 32 位是否正确?在 IA-32 架构中还有其他我应该知道的寄存器吗?
不,您所说的寄存器是现代 x86 CPUs 中不作为物理寄存器存在的实现细节。
x86 没有指定您在玩具/教学 CPU 设计中找到的任何实施细节。 x86 手册仅指定架构可见的内容。
Intel 和 AMD 的优化手册对内部实现进行了一些详细介绍,这与您的建议完全不同。现代 x86 CPUs 将架构寄存器重命名为更大的物理寄存器文件,从而实现乱序执行,而不会因先写后写或先读后写数据危险而停止。 (有关乱序执行的基本介绍和实际 Haswell 内核的框图,请参阅 for more details about register renaming). See this answer。(请记住,物理芯片有多个内核)。
与简单或玩具微体系结构不同,几乎所有高性能 CPUs 都支持 miss under miss and/or hit under miss(多个未完成的缓存未命中,不会完全阻塞等待第一个的内存操作一个完成)
您可以构建一个具有单个 MBR/MDR 的简单 x86;如果原始 8086 和可能 386 微体系结构将类似的东西作为内部实现的一部分,我不会感到惊讶。
但是例如 Haswell 或 Skylake 核心每个周期可以执行 2 次加载和 1 次存储 from/to L1d 缓存(参见 How can cache be that fast?)。显然他们不能只有一个MBR。相反,Haswell 有 72 个加载缓冲区条目和 42 个存储缓冲区条目,它们一起是内存顺序缓冲区的一部分,它支持加载/存储的乱序执行,同时保持只有 StoreLoad 重新排序发生/对其他核心可见的错觉。
从 P5 Pentium 开始,,但在此之前只有 32 位访问是原子的。所以是的,如果 386/486 有 MDR,它可能是 32 位的。但即使是那些早期的 CPU 也可能在 CPU 和 RAM 之间有缓存。
我们知道 Haswell and later have a 256-bit path between L1d cache and execution units,即 32 字节 ,Skylake-AVX512 有 64 字节的 ZMM 路径 loads/stores。 AMD CPUs 将宽矢量操作分成 128 位块,因此它们的 load/store 缓冲区条目大概只有 16 字节宽。
英特尔 CPUs 至少将相邻存储合并到存储缓冲区内的同一缓存行,并且还有 10 个 LFB(行填充缓冲区)用于 L1d 和 L2 之间的待处理传输(或关闭-核心到 L3 或 DRAM)。
指令解码:x86 是变长的
x86是变长指令集;在前缀之后,最长的指令超过 32 位。即使对于 8086 也是如此。例如,add word [bx+disp16], imm16
是 6 个字节长。但是 8088 只有一个 4 字节的预取队列用于解码(相对于 8086 的 6 字节队列),因此它必须支持解码指令,而无需从内存中加载整个内容。 8088 / 8086 解码前缀一次 1 个周期,4 个字节的操作码 + modRM 绝对足以识别指令其余部分的长度,因此它可以解码它然后获取 disp16 and/or imm16 如果它们还没有被取走。现代 x86 可以有更长的指令,尤其是 SSSE3 / SSE4 需要许多强制性前缀作为操作码的一部分。
它也是一个 CISC ISA,因此在内部保留实际指令字节不是很有用;您不能像使用简单的 MIPS 那样直接将指令位用作内部控制信号。
在非流水线 CPU 中,是的,某处可能有一个物理 EIP 寄存器。对于现代 CPUs,每条指令都有一个与之关联的 EIP,但许多指令在 CPU 中同时运行。有序的流水线 CPU 可能会将 EIP 与每个阶段相关联,但无序的 CPU 必须在每条指令的基础上对其进行跟踪。 (实际上是每个 uop,因为复杂指令解码为超过 1 个内部 uop。)
现代 x86 以 16 或 32 字节为单位进行提取和解码,每个时钟周期最多解码 5 或 6 条指令,并将解码结果放入队列中,供前端乱序发送核心的一部分。
另请参阅 https://whosebug.com/tags/x86/info 中的 CPU-内部链接,尤其是 David Kanter 的文章和 Agner Fog 的微架构指南。
顺便说一句,您遗漏了 x86 的许多控制/调试寄存器。 CR0..4 对 386 启用保护模式、分页和各种其他内容至关重要。您 可以 在实模式下仅使用 GP 和段 regs 以及 EFLAGS 使用 CPU,但是如果包含非通用 regs,x86 有更多的体系结构寄存器OS 需要管理。
据我所知,IA-32 架构有十个 32 位寄存器和六个 16 位寄存器。
32位寄存器如下:
- 数据寄存器 - EAX、EBX、ECX、EDX
- 指针寄存器 - EIP、ESP、EBP
- 索引寄存器 - ESI、EDI
- 控制寄存器-EFLAG(EIP也归类为控制寄存器)
16位寄存器如下:
- 代码段:包含所有要执行的指令。
- 数据段:包含数据、常量和工作区。
- 堆栈段:它包含数据和return过程或子程序的地址。
- 额外段 (ES)。指向额外数据的指针。
- F 段 (FS)。指向更多额外数据的指针。
- G段(GS)。指向更多额外数据的指针。
但是,我找不到有关当前指令寄存器 (CIR) 或内存缓冲寄存器 (MBR)/内存数据寄存器 (MBR) 的任何信息。这些寄存器是否被称为其他东西?这些寄存器是 32 位的吗?
我假设它们是 32 位的,并且在此体系结构下最常用的指令长度不到 4 个字节。从观察来看,很多指令似乎都在4个字节以下,例如:
- 推动 EBP (55)
- MOV EBP, ESP (8B EC)
- LEA (8D 44 38 02)
对于较长的指令,CPU 将使用前缀代码和其他可选代码。较长的指令将需要一个以上的周期才能完成,具体取决于指令长度。
我说的寄存器长度是 32 位是否正确?在 IA-32 架构中还有其他我应该知道的寄存器吗?
不,您所说的寄存器是现代 x86 CPUs 中不作为物理寄存器存在的实现细节。
x86 没有指定您在玩具/教学 CPU 设计中找到的任何实施细节。 x86 手册仅指定架构可见的内容。
Intel 和 AMD 的优化手册对内部实现进行了一些详细介绍,这与您的建议完全不同。现代 x86 CPUs 将架构寄存器重命名为更大的物理寄存器文件,从而实现乱序执行,而不会因先写后写或先读后写数据危险而停止。 (有关乱序执行的基本介绍和实际 Haswell 内核的框图,请参阅
与简单或玩具微体系结构不同,几乎所有高性能 CPUs 都支持 miss under miss and/or hit under miss(多个未完成的缓存未命中,不会完全阻塞等待第一个的内存操作一个完成)
您可以构建一个具有单个 MBR/MDR 的简单 x86;如果原始 8086 和可能 386 微体系结构将类似的东西作为内部实现的一部分,我不会感到惊讶。
但是例如 Haswell 或 Skylake 核心每个周期可以执行 2 次加载和 1 次存储 from/to L1d 缓存(参见 How can cache be that fast?)。显然他们不能只有一个MBR。相反,Haswell 有 72 个加载缓冲区条目和 42 个存储缓冲区条目,它们一起是内存顺序缓冲区的一部分,它支持加载/存储的乱序执行,同时保持只有 StoreLoad 重新排序发生/对其他核心可见的错觉。
从 P5 Pentium 开始,
我们知道 Haswell and later have a 256-bit path between L1d cache and execution units,即 32 字节 ,Skylake-AVX512 有 64 字节的 ZMM 路径 loads/stores。 AMD CPUs 将宽矢量操作分成 128 位块,因此它们的 load/store 缓冲区条目大概只有 16 字节宽。
英特尔 CPUs 至少将相邻存储合并到存储缓冲区内的同一缓存行,并且还有 10 个 LFB(行填充缓冲区)用于 L1d 和 L2 之间的待处理传输(或关闭-核心到 L3 或 DRAM)。
指令解码:x86 是变长的
x86是变长指令集;在前缀之后,最长的指令超过 32 位。即使对于 8086 也是如此。例如,add word [bx+disp16], imm16
是 6 个字节长。但是 8088 只有一个 4 字节的预取队列用于解码(相对于 8086 的 6 字节队列),因此它必须支持解码指令,而无需从内存中加载整个内容。 8088 / 8086 解码前缀一次 1 个周期,4 个字节的操作码 + modRM 绝对足以识别指令其余部分的长度,因此它可以解码它然后获取 disp16 and/or imm16 如果它们还没有被取走。现代 x86 可以有更长的指令,尤其是 SSSE3 / SSE4 需要许多强制性前缀作为操作码的一部分。
它也是一个 CISC ISA,因此在内部保留实际指令字节不是很有用;您不能像使用简单的 MIPS 那样直接将指令位用作内部控制信号。
在非流水线 CPU 中,是的,某处可能有一个物理 EIP 寄存器。对于现代 CPUs,每条指令都有一个与之关联的 EIP,但许多指令在 CPU 中同时运行。有序的流水线 CPU 可能会将 EIP 与每个阶段相关联,但无序的 CPU 必须在每条指令的基础上对其进行跟踪。 (实际上是每个 uop,因为复杂指令解码为超过 1 个内部 uop。)
现代 x86 以 16 或 32 字节为单位进行提取和解码,每个时钟周期最多解码 5 或 6 条指令,并将解码结果放入队列中,供前端乱序发送核心的一部分。
另请参阅 https://whosebug.com/tags/x86/info 中的 CPU-内部链接,尤其是 David Kanter 的文章和 Agner Fog 的微架构指南。
顺便说一句,您遗漏了 x86 的许多控制/调试寄存器。 CR0..4 对 386 启用保护模式、分页和各种其他内容至关重要。您 可以 在实模式下仅使用 GP 和段 regs 以及 EFLAGS 使用 CPU,但是如果包含非通用 regs,x86 有更多的体系结构寄存器OS 需要管理。