【问题标题】:What do 'instruction prefixes' mean in modern x86现代 x86 中的“指令前缀”是什么意思
【发布时间】:2016-05-24 14:51:30
【问题描述】:

为了了解为什么 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 岁以上的人添加到一条指令上而不是分解它?

【问题讨论】:

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

标签: assembly x86 prefix


【解决方案1】:

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

  1. lock/rep/repne
  2. 段覆盖
  3. 操作数大小覆盖
  4. 地址大小覆盖

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

66 66 66 66 66 66 66 66 90 之类的东西是有效的(但解码速度可能很慢)。 2E 3E 00 00(混合段覆盖)不是。

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

【讨论】:

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

通常,您可以根据需要使用任意数量,由预期的指令和操作数决定。汇编器会自动发出一些前缀,而另一些则需要您手动使用。

他们提到的情况是多字节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:与某些指令一起使用以使它们成为原子,例如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, 1add eax, 1 相同,但前缀为 41h
  • XOP、VEX:与向量指令子集一起使用

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-03
    • 1970-01-01
    • 1970-01-01
    • 2013-06-22
    相关资源
    最近更新 更多