assembly - 为什么仅在使用单类时才设置溢出标志?

标签 assembly x86 cpu-architecture bit-shift eflags

在 x86 intel 引用手册中,它说:

"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。请参阅下文,了解我在 Intel CPU 上如何设置它的理论。


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

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

这也许就是斯蒂芬·莫尔斯所说的 (8086 ISA 的架构师)在为 8086 做出设计选择时正在思考。他的书《8086 Primer》是 available for free on his web site ,并确认(第 96 页)8086 的可变计数操作码未定义 OF。 (对于 8086,显然包括 CL=1 的 shl al, cl,与 Intel 目前的文档不同。)有关移位指令及其用途的部分(第 64-66 页)没有提到 OF,只提到 CF。

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

不知道为什么 Morse 没有将 OF 定义为始终以某种特定方式设置,也许根据 CF 与当前 MSB 不匹配,这可能没有用,但对于 1 的计数仍然有意义。 ALU 已经需要为 CF 移出最后一位。也许这是因为 8086 没有在可变计数操作码中为 OF 定义任何内容,即使计数恰好为 1。


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

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

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

实际上,未经修改将强制读取除 0 之外的立即移位计数并与旧的 FLAGS 值合并。在现代 CPU 上,就像变量计数一样,如果它是 0,所以最好不以这种方式指定。 (shl reg, cl 在 Sandybridge 系列上是 3 uops,因为在 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

使用 nasm+ld 汇编+链接到静态可执行文件,使用 GDB 运行并使用 layout reg/layout next 。使用startisi .

我的 Skylake CPU 确实为 shl al,cl 设置了 OF=1 AL=0x7f CL=2(或 1 或任何非零计数)。或者 AL=0x80。但切勿将其设置为任何计数的 AL=0x3 或 AL=0xc0 (0b1100_0000)

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

这是有道理的;在纸张规范需要特定结果的情况下,它给出了正确的结果,并且实现起来很便宜。 (OF 输出仍然必须来自不同的位,具体取决于操作数大小。)

当然,其他供应商的其他微架构可能有所不同。纯软件 x86 模拟器也同样符合纸上规范。

关于assembly - 为什么仅在使用单类时才设置溢出标志?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71771188/

相关文章:

当程序从终端运行时,clock_gettime 需要更长的时间来执行

c - 使用 LDT(本地描述符表)

c - 解释一下这个程序中的esp-ebp

x86 - 为什么中断描述符表 (IDT) 中的偏移位分为两个字段?

assembly - 如果使用同一个寄存器分别作为输入和输出,两条指令能否在同一个周期内执行?

linux - 将目标文件转换为另一种体系结构

assembly - 为什么 movaps 会导致段错误?

assembly - 在gas宏中自动生成xmm寄存器名称?

c++ - SSE跨平台指令集

c - 没有gdt的idt使用grub