为啥 INC 和 DEC 指令*不*影响进位标志 (CF)?
Posted
技术标签:
【中文标题】为啥 INC 和 DEC 指令*不*影响进位标志 (CF)?【英文标题】:Why do the INC and DEC instructions *not* affect the Carry Flag (CF)?为什么 INC 和 DEC 指令*不*影响进位标志 (CF)? 【发布时间】:2012-11-06 06:33:52 【问题描述】:为什么x86指令INC
(递增)和DEC
(递减)不影响FLAGSREGISTER中的CF
(进位标志)?
【问题讨论】:
@AndreasBrinck 这个问题不是关于“为什么?”,那里的答案也没有回答设计选择的问题。 这不是完全重复的。另一个问题询问如何使用 INC/DEC 并简单地观察进位未更新。这个问题询问为什么它没有更新。我发现有几个人愿意将问题作为一个完全重复的问题来结束,而事实并非如此,这表明“版主”正在开枪。恕我直言,这对 SO 没有帮助。 半相关:x86 Assembly: INC and DEC instruction and overflow flag 展示了一些如何更新 OF 而不是 CF 的示例。这就是这个问题在某一时刻被错误地链接到的非重复项。 【参考方案1】:要了解为什么您可能需要记住当前具有 32 位和 64 位值的“x86”CPU 开始使用的 8 位机器更加有限,可以追溯到 Intel 8008。(我早在 1973 年就在这个世界上编码,我还记得(呃)它!)。
在那个世界里,寄存器既珍贵又小。您需要INC
/DEC
用于各种目的,最常见的是循环控制。许多循环涉及执行“多精度算术”(例如,16 位或更多!)通过让INC
/DEC
设置零标志(Z
),您可以使用它们很好地控制循环;通过坚持循环控制指令不更改进位标志 (CF
),进位在循环迭代中得以保留,您可以实现多精度运算,而无需编写大量代码来记住进位状态。
一旦你习惯了丑陋的指令集,这效果很好。
在具有更大字数的更现代的机器上,你不需要这么多,所以INC
和DEC
在语义上可能等同于ADD
...,1 等等。这实际上是我的当我需要携带套装时使用:-
大多数情况下,我现在远离INC
和DEC
,因为它们会进行部分条件代码更新,这可能会导致管道中出现有趣的停顿,而ADD
/SUB
不会。所以在不重要的地方(大多数地方),我使用ADD
/SUB
来避免摊位。我只在保持代码小问题时才使用INC
/DEC
,例如,在缓存行中,一两条指令的大小会产生足够的差异。这可能是毫无意义的 nano[字面意思!]-优化,但我的编码习惯相当老派。
我的解释告诉我们为什么 INC
/DEC
设置零标志 (Z
)。对于INC
/DEC
设置符号(和奇偶校验标志)的原因,我没有特别令人信服的解释。
编辑 2016 年 4 月:似乎在现代 x86 上可以更好地处理失速问题。见INC instruction vs ADD 1: Does it matter?
【讨论】:
设置 SF/OF 和 ZF 让您dec / jge
从 n 循环到 0,而不是 n 循环到 1。(即当 dec
产生 -1
而不是0
.) 这有时很有用。
更新:Skylake 更好:它完全独立于其他 FLAGS (SPAZO) 处理 CF;需要两者输入的少数指令将它们作为2个单独的输入读取,因此根本不需要标志合并。 (但是cmova
和cmovbe
仍然是 2 uop,因为总共有 4 个输入,这与其他只需要 3 个的 cmov 指令不同。jcc 和 setcc 即使它们需要 2 个单独的标志输入,也始终是 1 uop)。请参阅@Bee 在What is a Partial Flag Stall? 上的回答【参考方案2】:
当你有 zero 标志由 inc/dec 设置时为什么 sign 的问题最好用问题来解决:你宁愿没有选项 一个 ?
a) for (n=7;n>=0;n--) // translates to `dec + jns`
b) for (n=8;n>0;n--) // translates to `dec + jnz`
正如Ira Baxter 已经澄清的那样,进位标志用于很多算法——不仅是多精度算术,还用于单色/cga/EGA 时代的位图处理: 这会将 80 像素宽的行向右移动一个像素...
mov cx, 10
begin: lodsb
rcr al,1 // this is rotate though carry:
stosb // for the algorithm to work, carry must not be destroyed
LOOP begin //
但是:为什么是平价?
我相信答案是为什么不。该指令集来自 70 年代后期,当时晶体管稀缺。拒绝为某些特定指令计算奇偶校验标志没有任何意义,只会增加 CPU 的复杂性。
【讨论】:
“为什么不进行奇偶校验”?我愿意打赌,在程序员编写的数百万条 INC/DEC 指令中,只有一小部分实际使用了奇偶校验位结果。在指令中包含(有效)无用的副作用只是意味着更多的文档,更多这样的讨论,以及未来实现中更多的晶体管向后兼容愚蠢的结果。给架构师的建议:如果副作用很少有用,请不要这样做。【参考方案3】:inc 和 dec 指令通常用于维护迭代或循环计数。使用 32 位,迭代次数可高达 4,294,967,295。这个数字对于大多数应用来说已经足够大了。如果我们需要比这更大的计数怎么办?我们必须使用 add 而不是 inc 吗?这就引出了第二个也是主要原因。
进位标志检测到的条件也可以用零标志检测。为什么?因为 inc 和 dec 仅将数字更改 1。例如,假设 ECX 寄存器已达到其最大值 4,294,967,295 (FFFFFFFFH)。如果我们再执行
inc ECX
我们通常期望进位标志设置为 1。但是,我们可以通过注意 ECX = 0 来检测这种情况,这会设置零标志。因此,设置进位标志对于这些指令来说确实是多余的。
【讨论】:
inc
是,但dec
不是。对于dec
,ZF==0 检测迭代 before 进位(从 0 到 2^32-1 的环绕)。我在on another answer that also made this argument 发表了同样的评论。【参考方案4】:
因为不需要影响。检查零标志就足够了。 因此,在 inc 和 dec 指令之后,进位标志保持不变,在某些情况下这很有用。
【讨论】:
inc
是这样:进位从 0xFFFF... 到 0,所以 ZF 和 CF 相等。但是dec
从0
变为0xFFFF...
,所以ZF 设置在减量before 进位上。我认为真正的原因是sub reg,1
在您需要 CF 时可用,而通常您不需要 CF 结果。因此,保留 CF 可以将其与 ADC、移位、RCL 和其他在 CF 中保留某些内容的东西混合使用。 (当然,这个设计决定是针对 8086 做出的,早在部分寄存器停顿作为未来问题出现之前。当时loop
是有效的,因此无标志循环很容易。)以上是关于为啥 INC 和 DEC 指令*不*影响进位标志 (CF)?的主要内容,如果未能解决你的问题,请参考以下文章