"or eax, eax"和"test eax, eax"的区别

9
< p >“or eax,eax”和“test eax,eax”的区别是什么?我看到不同的编译器在相同的比较中产生了两种不同的结果,就文档而言它们完全一样,所以我想知道为什么它们不都使用“test eax,eax”。考虑到“and eax,eax”会以与任何一个相同的方式设置标志,但我在freepascal,delphi或msVC ++中都没有看到它。

我在Delphi中编译了一些asm块并查看了汇编源代码,发现所有3种形式的操作码长度完全相同,我还检查了英特尔性能PDF,它说它们具有相同的延迟和吞吐量。

编辑:
问题特别涉及到“test eax,eax”,“or eax,eax”和“and eax,eax”之间的区别。对于寄存器、标志、操作码长度、延迟和吞吐量,所有3个都给出完全相同的结果。然而,为了测试是否为0,如果不为零或者如果带符号,一些编译器将使用“test eax,eax”,而一些编译器将使用“or eax,eax”,我想知道为什么他们不都使用“test eax,eax”,因为它使代码稍微更清晰一些。

编辑2:
作为参考,我在家里只有旧版的msvc ++和Delphi,在测试变量是否为零时,msvc ++使用“test eax,eax”,而Delphi使用“or eax,eax”。


根据寄存器名称,我猜测这是[x86]架构。如果不正确,请使用适当的处理器架构重新标记您的问题。有多个架构,它们各自具有不同的汇编语言。 - Damien_The_Unbeliever
你是对的,它是针对x86的,我没有把它放在那里,我的错。 - Marladu
我的链接重复的答案更详细地解释了为什么test更好,包括一些没有在任何地方提到的原因。现在甚至考虑使用or same,same的唯一原因是针对P6系列CPU,在这些CPU上,将寄存器与自身重写可以使其在乱序核心中保持“活动状态”,可能避免/减少后续指令读取相同寄存器时的寄存器读取停顿。但是,测试/ jcc的宏融合通常会超过这一点,除非实验显示特定热点受益。但也许正是这个原因,而不仅仅是惯性,是Delphi使用or的原因。 - Peter Cordes
andor更好(或者至少不会更差),因为它可以与Sandybridge系列上的jcc进行宏融合。但在大多数情况下,它仍然比更多的CPU上的test要差,并且仍然会在后续指令读取寄存器之前增加一个周期的延迟。如果您想避免P6上的寄存器读取停顿并仍然在SnB上获得宏融合,请使用and。如果您的代码可以在包括Sandybridge系列的CPU上运行,则or/jcc始终是次优的。 - Peter Cordes
3个回答

7
总的来说,testand之间唯一的区别是test <reg>, <reg>不会修改其操作数。本质上,test应用了一个and运算,丢弃结果中的非标志部分。如果操作数相同,结果将相同(or也是如此)。
由于诸如微操作融合之类的原因,test可能是更优秀的指令选择。因此,除非需要重复计算,否则通常优先选择testcmp/sub同样如此。
在Intel文档中搜索“融合(fusion)”,您可以找到详细信息。

1
我稍后会在主问题中添加更多文本,但问题是针对特定情况的:“test eax,eax”、“or eax,eax”、“and eax,eax”,这些情况对于寄存器、标志和操作码长度都给出完全相同的结果。 - Marladu
2
or eax, eax 并不会修改 eax 的值。 - Russell Borogove
哦,我明白了,你在谈论操作数相同的特定情况。抱歉,我应该注意到这一点。我会进行一些编辑。 - gsg
我猜这是最好的答案了。我仍然不明白为什么Delphi 2006使用“or eax,eax”而不是“test eax,eax”来测试变量是否等于0。真是个谜! - Marladu

3
确定在test eax,eax之后eax的内容与该指令之前的内容相同的电路比为or eax,eax达到这个结论所需的电路要简单得多。因此,test更好。
一些编译器可能在没有任何区别的情况下(在乱序执行之前)生成or,但现在某些乱序处理器会有所不同(而其他OOO处理器则非常复杂,可以将or eax,eax识别为真正等效于test eax,eax)。
我找不到支持一些现代处理器实际上能够推断or reg,reg不修改reg的参考资料,但这里有一个回答声称对于xchg reg,reg是这种情况。

0x90(使用reg = eax的xchg eax,reg操作码)是一个特殊情况:它被认为是真正的NOP,但是使用xchg r,r / m32操作码的相同指令的2字节编码,其中MODRM编码了eax,eax(或任何其他相同的,相同的)不是特殊的。在32位模式下,0x90也是xchg eax,eax,但在64位模式下,xchg eax,eax会将EAX零扩展为RAX,而0x90不会这样做:NOP有自己的指令参考手册条目。因此,在x86-64模式下汇编xchg eax,eax时,必须使用2字节编码。 - Peter Cordes

2

我们重申一下 @gsg 所说的,并且补充一些,TEST 指令对两个操作数进行按位逻辑比较(在内部执行按位 AND 运算但不存储结果),并根据该操作的结果设置处理器标志。OR 指令将源操作数与目的操作数进行逻辑 OR 运算,在目的地存储结果,并根据结果设置处理器标志。它们都以相同的方式影响处理器标志。因此,当操作数相同时,行为相同,标志没有区别。然而,当操作数不同时,它们的行为也会有所不同。您还可以使用and eax,eax测试零,其影响标志的方式相同。


你能想到一个eax的值,使得特定指令“test eax,eax”、“or eax,eax”、“and eax,eax”将产生不同的寄存器或标志吗? - Marladu
@Marladu orandtest根据操作结果完全相同影响标志位(我已经更正了我的答案)。因此,当操作数相同时(例如or eax,eaxtest eax,eaxand eax,eax),标志位结果是相同的。如果操作数不同,则testand将给出相同的标志位结果,因为它们都执行AND操作,但test不会影响操作数值。OR可能会根据不同的操作数值给出不同的标志位结果。 - lurker

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接