为什么所有现代 x86 掩码都会将计数移至 CL 中的 5 个低位

No *_* QA 4 x86 assembly bit-shift cpu-architecture cpu-registers

我正在深入研究 x86 ASM 中的左移和右移操作,例如shl eax, cl

\n\n

来自 IA-32 英特尔架构软件开发人员\xe2\x80\x99s 手册 3

\n\n
\n

所有 IA-32\n 处理器(从 Intel 286 处理器开始)都会将移位计数屏蔽为 5 位,从而导致最大计数为 31。此屏蔽在所有操作模式\n 中完成(包括虚拟 8086模式)以减少指令的最大执行时间。

\n
\n\n

我试图理解这个逻辑背后的原因。也许它以这种方式工作是因为在硬件级别上很难使用 1 个周期实现寄存器中所有 32(或 64)位的移位?

\n\n

任何详细的解释都会有很大帮助!

\n

Chr*_*all 7

编辑以更正声明:80386,(令我惊讶的是)确实有一个桶形移位器。

\n
\n

很高兴听到 286 被描述为“现代”:-)

\n

8086 的运行时间SHL AX, CL为 8 个时钟 + 每位移位 4 个时钟。所以 if CL= 255 这是一条非常慢的指令!

\n

所以 286 帮了大家一个忙,通过掩码将计数限制在 0..31。将指令限制为最多 5 + 31 个时钟。对于 16 位寄存器来说,这是一个有趣的折衷方案。

\n

[我找到了《80186/80188 80C186/80C188硬件参考手册》(订单号270788-001),其中说这项创新首先出现在那里。 SHL等人运行了 5+n 个时钟(用于寄存器操作),与 286 相同。FWIW,186 还添加了 PUSHA/POPA、PUSH immed.、INS/OUTS、BOUND、ENTER/LEAVE、IMUL immed。和 SHL/ROL 等。我不知道为什么186看起来像是一个非人。]

\n

对于 386,他们保留相同的掩码,但这也适用于 32 位寄存器移位。我找到了一份《803​​86程序员参考手册》(订单号230985-001),其中给出了所有寄存器移位的时钟计数为3。《Intel 80386硬件参考手册》(订单号231732-002)第2.4节“执行单元”说执行单元包括:

\n
\n

\xe2\x80\xa2 数据单元包含 ALU、八个 32 位通用寄存器的文件和一个 64 位桶形移位器(在一个时钟内执行多个位移位)。

\n
\n

所以,我不知道为什么他们不将 32 位移位屏蔽为 0..63。在这一点上我只能提出荒谬的历史理论。

\n

我同意,遗憾的是没有一个 (GPR) 移位对于任何计数 >= 参数大小都返回零。这需要硬件检查底部 6/5 之外的任何位设置,并返回零。作为妥协,也许只是 Bit6/Bit5。

\n

[我还没有尝试过,但我怀疑使用PSLLQet al 是一项艰苦的工作——将计数和值打乱xmm,然后将结果再次打乱——与测试移位计数和屏蔽某些分支中的移位结果相比-自由时尚。]

\n

无论如何……这种行为的原因似乎已经成为历史。

\n

  • 有趣的事实:像 `pslld xmm0, 32` 或 `pslld xmm1, xmm0` 这样的 SIMD 移位 *确实* 使计数饱和而不是屏蔽它,因此您可以使用 MMX/SSE/AVX 移位移出所有位。 (2认同)
  • @NoNameQA:你不需要286模拟器;同样的规则也适用于现代 x86,“shl ax, 16”将寄存器归零。您可以在 64 位模式下的真实硬件上的调试器中单步执行该操作。[有多少种方法将寄存器设置为零?](/sf/answers/2287158751/)。我假设你的“x”应该是一个 16 位“int”。 (2认同)