问题 现代x86中'指令前缀'的含义是什么


为了理解Bulldozer为什么不合格,我一直在关注Agner Fog的优秀微体系结构书籍,在第178页的推土机下它有这一段。

最多三个前缀的指令可以在一个时钟周期内解码。超过三个的指令会受到很大的惩罚   前缀。带有4-7前缀的指令需要额外14-15个时钟周期   解码。 8-11前缀的指令需要20-22个时钟周期   额外的,12-14前缀的指令需要27 - 28个时钟周期   额外。因此,不建议延长NOP指令   有三个以上的前缀。此规则的前缀计数包括   操作数大小,地址大小,段,重复,锁定,REX和XOP   前缀。三字节VEX前缀计为一个,而两个字节   VEX前缀不计算在内。转义码(0F,0F38,0F3A)不计算在内。

当我搜索前缀时,我的技术定义远远超出了我的能力。或者,建议他们每个指令限制为4,这与上述摘录相冲突。

因此,简单来说,有人可以解释他们是/做什么以及为什么你可能想要将多达14+用于指令而不是分解?


9732
2018-02-13 12:28


起源

非常好的问题。我想在这里阅读Peter Cordes和其他专家的答案。 - zx485
@ zx485:就像每个人都说的那样,通常你做长时间只会看到大量的前缀 NOP。一 NOP 除了代码大小的副作用和前端问题之外,无论长度如何都需要执行相同的时间。 (正如Agner Fog的指南所解释的那样)。你肯定不希望14个NOP在使用uop-cache的CPU上的uop缓存中浪费空间。除此之外,x32 ABI通常使用地址大小的前缀(因此base + index * scale寻址模式不会意外地超出32位地址范围)。所以 lock inc word [edi + r10d*4] 需要4:锁定op-sz addr-sz REX。 - Peter Cordes
IIRC,Atom和Silvermont具有类似的解码器限制,但作为SIMD指令编码一部分的前缀和转义字节计数。所以他们可以在SSSE3和后来的REX前缀指令上出现可怕的瓶颈。 - Peter Cordes


答案:


通常情况下,您可以根据需要使用尽可能多的指令和操作数来确定。汇编程序会自动发出一些前缀,而其他人则可以手动使用。

他们提到的情况是多字节的 NOP 传统上用于对齐填充,其中的想法是使用单个但适当长的指令来节省资源。显然,事实证明,使用更多的前缀只是为了保持单个指令可能比使用两个前缀更少的指令更糟糕。

此规则的前缀计数包括操作数大小,地址大小,段,重复,锁定,REX和XOP前缀。三字节VEX前缀计为一,而两字节VEX前缀不计。

例子:

  • 操作数大小:可以在32位和16位寄存器之间切换,例如 mov ax, [foo] 编码与...相同 mov eax, [foo] 但是带有前缀 66h
  • 地址大小:可以在32/16或64/32位地址大小之间切换,例如 mov [eax], foo 编码与...相同 mov [rax], foo 但是带有前缀 67h (在64位模式下)
  • 段:可以覆盖使用的段,例如 mov [fs:eax], foo 编码与...相同 mov [eax], foo 但是带有前缀 64h
  • repeat:用于字符串指令重复,例如 rep cmpsb 是编码相同的 cmpsb 但是带有前缀 f3h
  • 锁:与某些指令一起使用以使它们成为原子,例如: lock add [foo], 1 编码与...相同 add [foo], 1 但是带有前缀 f0h
  • REX.W:用于切换到64位操作数大小,例如 add rax, 1 编码与...相同 add eax, 1 但是带有前缀 48h
  • REX.R,B,X:用作modr / m字节的扩展以访问额外的寄存器,例如: add r8d, 1 是相同的 add eax, 1 但是带有前缀 41h
  • XOP,VEX:与向量指令子集一起使用

10
2018-02-13 12:50





“四个前缀”交易来自“前缀组”:

  1. 锁定/ REP / REPNE
  2. 段覆盖
  3. 操作数大小覆盖
  4. 地址大小覆盖

您可以重复前缀,但您不能(您可以,但行为未定义)使用多个前缀 不同 来自同一组的前缀。虽然这仅适用于第1组和第2组,但其他组中每组只有1件事。

就像是 66 66 66 66 66 66 66 66 90 是有效的(但可能很慢解码)。 2E 3E 00 00 (混合段覆盖)不是。

当必须执行字节时,堆栈前缀对于代码对齐非常有用,与填充不同 nop 它不会花费执行时间。一次使用太多可能会花费解码时间。


4
2018-02-13 13:03



问题断言: instructions with 12-14 prefixes take 27 - 28 clock cycles extra。你如何编码一个12-14前缀的OpCode,这些前缀不是多余的?我只是好奇一个例子。 - zx485
@ zx485你不能。 “4前缀”规则意味着最多只有4个前缀可以是非冗余的,不止是必须包含冗余前缀。并且在任何情况下只有11个不同的遗留前缀,因此即使使用无效组合,也不能有12个非冗余前缀(除非您添加新的REX或VEX,但它们具有不同的规则)。 - harold
感谢您的回答。当然,这将暗示任何(合理/非冗余)指令/具有多达三个前缀的OpCode在处理时不会超过1个周期。这是个好消息:-)应用所有四个前缀应该非常罕见 - 所以可以接受。 - zx485
@ zx485当然,在具有传统前缀的32位和64位代码中已经相当罕见,这使得它更不用担心了 - harold
很容易想象一些Linux x32代码使用 lock inc word [edi + r10d*4]。当gcc不确定高位寄存器中没有垃圾时,gcc经常使用x32的地址大小前缀。或者,当有效地址内的address-math在未截断为32b时会产生不同的结果。 Bulldozer的前端泡沫可能不是因为这个因素 lock。一个类似的指令与 fs 段覆盖(用于线程本地存储)可能更成问题,因为它可以使用4个前缀而不使用 lock,因而不是超慢。 - Peter Cordes