x86指令编码如何选择操作码

7

当对x86-64进行指令cmpw %ax -5编码时,从英特尔指令集参考手册中选择两个操作码:

3D iw CMP AX, imm16 I Valid Valid Compare imm16 with AX.
83 /7 ib CMP r/m16, imm8 MI Valid Valid Compare imm8 with r/m16.

因此,将会有两个编码结果:

66 3d fb ff ; this for opcode 3d
66 83 f8 fb ; this for opcode 83

那么哪一个更好呢?

我尝试了以下一些在线反汇编器:

这两者都能将指令反汇编回原始代码。但为什么6683fb00可以工作,而663dfb不行呢?


2
不需要深入研究,一个指令似乎是将AX(一个16位寄存器)与一个16位值进行比较,而另一个则将另一个不同的(16位)寄存器与一个8位值进行比较。 - Neil
在第二种变体中,前缀不会改变长度。 - harold
@IraBaxter 从汇编的角度来看,应该选择哪个? - Steve
5
如果不必使用16位立即数,请避免在64位代码中使用长度更改前缀,因为这将产生相当大的惩罚。《英特尔优化手册》提出了一个规则,以避免像这样的LCP停顿:汇编/编译器编码规则21.(对性能影响,一般适用)应优先生成使用imm8或imm32值而不是imm16值的代码。 - Michael Petch
1
@Neil,如果他使用-5作为操作数,那就无所谓了。 - harold
显示剩余4条评论
1个回答

7
两种编码长度相同,因此这并不能帮助我们做出决定。
然而,正如@Michael Petch所评论的那样,在Intel CPU上,imm16编码会导致解码器出现LCP停顿。(因为没有66操作数大小前缀,它将是3D imm32,因此操作数大小前缀改变了指令其余部分的长度。这就是为什么它被称为Length-Changing-Prefix stall。据我所知,在使用32位立即数时,在16位代码中也会出现相同的停顿。) imm8编码在我所知道的任何微架构上都不会引起问题,因此应该优先考虑。请参阅Agner Fog's microarch.pdf以及标签wiki中的其他链接。

为了避免LCP停顿,使用更长的指令可能是值得的。(例如,如果您知道寄存器的上16位为零或符号扩展,则使用32位操作数大小可以避免LCP停顿。)

英特尔Snb系列CPU具有uop缓存,因此指令在执行之前不必总是重新解码。然而,uop缓存很小,所以这样做是值得的。

当然,如果您正在为AMD进行调优,则这不是一个因素。我忘记了Atom和Silvermont解码器是否也有LCP停顿。


回复: part2:

663d 是用于 cmp ax, imm16 的前缀+操作码。 663dfb 无法正常工作,因为它消耗了以下指令的第一个字节。当解码器看到 66 3D 时,它会从指令流中获取接下来的两个字节作为立即数。


1
这是一个很好的答案,额外提供了参考! - Steve

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