为什么仅在使用单班时设置溢出标志?

Why is the Overflow-Flag only set when single shifts are used?

在 x86 英特尔参考手册中说:

"The overflow flag is set only if the single-shift forms of the instruction are used. [...]"

但是当我遇到以下情况时:

xor eax, eax
mov al, 0b11000000
shl al, 2
;content of al: 00000000

这里答案的高位与进位结果不一样,即cf = 1,没有设置溢出标志

我不明白为什么这是正确的行为。为什么只在使用单班时设置溢出标志?

OF=undefined for shift counts other than 1;实践中的结果取决于您的 CPU。请参阅下面我关于如何在我的英特尔 CPU.

上设置的理论

这个设计决定有些道理,让硬件稍微简单一些。

正确检测 2 的补码溢出需要检查移出的所有位是否与新的 MSB 匹配。这不同于像现在使用 CF 那样只检查移出的最后一位,因此需要像使用原始 8086 那样的 one-at-a-time 移位器的一些内部状态。

斯蒂芬莫尔斯也许就是这样 (8086 ISA 的架构师)在为 8086 做出设计选择时正在思考。他的书 8086 Primer 是 available for free on his web site,并确认 (pg96) 8086 对于 variable-count 操作码具有未定义的 OF . (对于 8086,显然它包括 shl al, cl 和 CL=1,这与英特尔目前的文档不同。)关于移位指令及其用途的部分(pg64-66)没有提到 OF,只有 CF。

必须检查所有移出的位也可能使桶形移位器更昂贵,但 Morse 不太可能考虑到这一点。

IDK 为什么莫尔斯没有将 OF 定义为总是以某种特定方式设置,也许是根据 CF 与当前 MSB 不匹配,这可能没有用,但对于 1 的计数仍然有意义。 ALU 已经需要为 CF 移出最后一位。也许那是因为 8086 没有在 variable-count 操作码中为 OF 定义任何东西,即使计数恰好是 1.


请注意,对于某些计数大于 1 的情况,某些 CPU 在实践中会产生 OF=1。例如,我的 i7-6700k Skylake 使用 0x7f << 2documentation 表示

OF flag is affected only for 1-bit shifts (see “Description” above); otherwise, it is undefined.

Undefined 不是 affected 的反义词;那将是“不受影响的”。它总是设置为某个值,他们只是没有记录 CPU 如何选择 0 与 1。

实际上未修改会强制读取旧的 FLAGS 值并与现代 CPU 上的 0 以外的立即轮班计数合并,例如变量计数,以防它为 0,所以这很好没有那样指定。 (shl reg, cl 在 Sandybridge-family 上是 3 微指令,因为在 CL&31 == 0 的情况下需要保持 FLAGS 不变。因此,这将是一种不需要的数据依赖性,不像现在,除非计数为 0,否则轮班写入所有标志。


我用这个 NASM 程序测试了我的 CPU

_start:
    mov cl, 7
    mov dl, 0x7f       ; GDB   set $dl = 0xc0  or whatever after this
.loop:
    mov eax, edx
    shl al, cl
    dec cl             ; set a breakpoint here to look at EFLAGS after every continue
    jnz .loop
;; fall off the end; I'm only single-stepping this in GDB anyway

assemble + link 使用 nasm+ld,使用 GDB 使用 运行 并使用 layout reg / layout next 进入静态可执行文件。使用 startisi.

我的 Skylake CPU 为 shl al,cl 设置了 OF=1,AL=0x7f CL=2(或 1 或任何 non-zero 计数)。或者对于 AL=0x80。但永远不要将其设置为 AL=0x3 或 AL=0xc0 (0b1100_0000)

我目前对解释该行为的猜测是 OF 被设置为好像它是一个偏移 1,
即如果 输入 位的 OF = (input[MSB] != input[MSB-1])

这是有道理的;在纸质规范需要特定结果的情况下,它会给出正确的结果,而且实施起来很便宜。 (OF 输出仍然必须来自不同的位,具体取决于 operand-size。)

当然,来自其他供应商的其他微体系结构可能有所不同。 pure-software 仍然符合 on-paper 规范的 x86 模拟器也是如此。