Say*_*ura 2 hardware cpu x86 assembly cpu-architecture
我读过,当CPU从内存中读取时,它会立即读取内存的字大小(例如4字节或8字节)。CPU 如何实现以下功能:
mov BYTE PTR [rbp-20], al
Run Code Online (Sandbox Code Playgroud)
它仅将一个字节的数据从 al 复制到堆栈。(假设数据总线宽度约为 64 位宽)如果有人可以提供有关如何在硬件级别实现它的信息,那就太好了。
而且,我们都知道,当CPU执行程序时,它有程序计数器或指令指针指向下一条指令的地址,控制单元将该指令取出到内存数据寄存器并稍后执行。比方说:
0: b8 00 00 00 00 mov eax,0x0
Run Code Online (Sandbox Code Playgroud)
是 5 字节代码长(在 x84 上)并且
0: 31 c0 xor eax,eax
Run Code Online (Sandbox Code Playgroud)
是 2 字节代码长,它们有不同的长度大小。
如果控制单元想要获取这些指令,那么:
像这样的指令怎么样:
0: 48 b8 5c 8f c2 f5 28 movabs rax,0x28f5c28f5c28f5c
7: 5c 8f 02
Run Code Online (Sandbox Code Playgroud)
超出了字大小,CPU 如何处理它们?
x86根本不是面向字的架构。指令是可变长度的,没有对齐。
“字长”在 x86 上不是一个有意义的术语;有些人可能用它来指代寄存器宽度,但取指/解码与整数寄存器无关。
实际上,在大多数现代 x86 CPU 上,从 L1 指令高速缓存中获取指令发生在对齐的 16 字节或 32 字节获取块中。稍后的流水线阶段会查找指令边界并并行解码最多 5 条指令(例如 Skylake)。请参阅David Kanter 的 Haswell 文章,了解显示从 L1i 缓存获取 16 字节指令的前端框图。
但请注意,现代 x86 CPU 还使用解码的 uop 缓存,因此它们不必处理难以解码的 x86 机器代码来处理非常频繁运行的代码(例如,在循环内,甚至是大循环内)。处理可变长度未对齐指令是旧 CPU 上的一个重要瓶颈。
请参阅现代 x86 硬件能否不将单个字节存储到内存中?有关缓存如何吸收存储到正常内存区域(MTRR 和/或 PAT 设置为 WB = 回写内存类型)的更多信息。
在现代 Intel CPU 上,将存储从存储缓冲区提交到 L1 数据缓存的逻辑可以处理任何宽度的任何存储,只要它完全包含在一个 64 字节缓存行中即可。
更面向字的非 x86 CPU(如 ARM)通常使用缓存字(4 或 8 字节)的读取-修改-写入来处理窄存储。请参阅是否有任何现代 CPU 的缓存字节存储实际上比字存储慢? 但现代 x86 CPU 确实花费了晶体管来使缓存字节存储或未对齐的更宽存储与缓存中对齐的 8 字节存储完全一样高效。
给定数据总线宽度为 64 位宽
现代 x86 在 CPU 中内置了内存控制器。DDR[1234] SDRAM 总线有 64 条数据线,但单个读或写命令会启动 8 次突发传输,传输 64字节数据。(并非巧合的是,64 字节是所有现有 x86 CPU 的缓存行大小。)
对于存储到不可缓存的内存区域(即,如果 CPU 配置为将该地址视为不可缓存,即使它由 DRAM 支持),则可以使用告诉 DRAM 的DQM 字节掩码信号进行单字节或其他窄存储存储器 8 个字节中的哪一个字节实际上是要存储来自该突发传输的。
(或者如果不支持(可能是这种情况),内存控制器可能必须读取旧内容并合并,然后存储整行。无论哪种方式,4 字节或 8 字节块都不是这里的重要单位. DDR突发传输可以缩短,但只能从64字节减少到32字节。我不认为8字节对齐写入在DRAM级别实际上非常特殊。它在x86 ISA中保证是“原子的”不过,即使在不可缓存的 MMIO 区域上也是如此。)
存储到不可缓存的 MMIO 区域将导致适当大小的 PCIe 事务(最多 64 字节)。
在CPU核心内部,数据缓存和执行单元之间的总线可以是32或64字节宽。(或者当前 AMD 上的 16 字节)。在 Haswell 及更高版本上,L1d 和 L2 缓存之间的缓存行传输也是通过 64 字节宽的总线完成的。