x86 汇编助记符是否标准化?

Are x86 Assembly Mnemonic standarized?

x86 标准是否包含助记符,还是仅定义操作码?

如果不包括它们,是否有针对不同汇编程序的其他标准?

助记符不规范,不同的汇编器使用不同的助记符。一些例子:

  • AT&T 风格的汇编程序对所有助记符应用 bwlq 后缀以指示操作数的大小。 Intel 风格的汇编程序通常使用关键字 byteworddwordqword
  • 来表示这一点
  • AT&T 风格的汇编程序识别 cbtwcwtlcltqcqto,而 Intel 风格的汇编程序识别与 cbw 相同的指令, cwdcdqcqo
  • AT&T 风格的汇编程序识别 movz??movs??,其中 ?? 是英特尔风格的汇编程序调用 movzxmovsx 的两个大小后缀,和 movsxd
  • 一些 Intel 风格的汇编器只将 63 /r 识别为 movsxd 而其他人也将 movsx 识别为该指令的变体
  • Plan 9 风格的汇编程序(例如在 Go 中使用的)很奇怪并且在很多方面都有所不同,例如使用摩托罗拉风格的助记符进行条件跳转
  • 从历史上看,为 8086 的 NEC V20 克隆提供的 NEC 汇编程序带有几乎完全不同的助记符。例如,int 被称为 brk

不幸的是,并没有一个真正写在纸上的“x86 标准”来定义 CPU 成为 x86 必须满足的所有最低要求。

Intel 的文档非常接近 “x86 标准”,但在某些情况下,它提供的保证比现代 AMD CPUs 更可靠。例如英特尔保证 1/2/4/8 字节加载或存储 from/to 可缓​​存内存的原子性,任何对齐方式都不会跨越缓存行边界。但是 AMD 只保证不跨越 8 字节边界的可缓存 loads/stores。

引用 Intel 的手册,表明所有保证都是作为“Intel486 处理器(以及此后更新的处理器)”提供的保证。没有适用于 all x86 CPUs(或更重要的是所有 x86-64 CPUs)的基线。我认为 x86(包括 pre-x86-64)在实践中的实际共享基线是 1 个字节,因为 8088.

因此,想要在现代 x86-64 CPU 上 运行 的软件不能假定 8 字节 loads/stores 的原子性,除非它们实际上是对齐的。我认为我们都同意原子性保证是成为现代多核 x86 CPU 的重要组成部分。即使在单核上,未缓存 MMIO 访问的原子性也很重要;现代英特尔和 AMD 同意这一点,但英特尔再次仅根据“奔腾及更高版本的处理器”对其进行记录。隐含地“后来 Intel 处理器”。


也就是说,英特尔的文档确实为每个操作码定义了助记符,并注册了名称。 AMD 的文档在所有这些方面都与英特尔一致。

参见 Intel's x86 Software Development Manuals. HTML extracts of just the per-instruction manual entries (without the sections that explain the notation and instruction format) can be found at https://www.felixcloutier.com/x86/index.html and https://github.com/HJLebbink/asm-dude/wiki 的第 2 卷,其他各个地方的旧版本格式不同。


正如@fuz 所解释的,大多数 assemblers 选择遵循这个标准,但这不是必需的。 重要的部分是二进制兼容性,而不是 asm 源兼容性。

英特尔必须为指令分配名称,以便它可以在其其余手册中用英语谈论它们,而不是因为它们需要世界上每个人都使用相同的 asm 语法。


我不确定英特尔的手册是否完全定义了完整的 asm 语法(例如,如何在寻址模式中指示段覆盖前缀)。

在某些情况下,他们确实超越了描述哪个机器代码做了什么,例如在字符串指令 lods/stos/movs/cmps/scas(可能还有 ins/outs)中,您会在 Intel 的第 2 卷手册中找到类似这样的段落:

At the assembly-code level, two forms of this instruction are allowed: the “explicit-operands” form and the “no-operands” form. The explicit-operands form (specified with the MOVS mnemonic) allows the source and destination operands to be specified explicitly. Here, the source and destination operands should be symbols that indicate the size and location of the source value and the destination, respectively. This explicit-operands form is provided to allow documentation; however, note that the documentation provided by this form can be misleading. That is, the source and destination operand symbols must specify the correct type (size) of the operands (bytes, words, or doublewords), but they do not have to specify the correct location. The locations of the source and destination operands are always specified by the DS:(E)SI and ES:(E)DI registers, which must be loaded correctly before the move string instruction is executed.

(highlighting reproduced from (an HTML extract of) the original PDF)

一些“英特尔语法”assemblers 例如NASM 忽略这一点,只允许使用 movs 和大小作为助记符的一部分,比如 movsb。 NASM 也有指示段覆盖前缀的语法,如 fs lodsd 不需要操作数,因此这完全避免了使用指示错误内存操作数但仍然 assemble 的操作数的可能性.

(字符串指令只使用隐式内存操作数,不是ModR/M寻址方式。)

NASM: parser: instruction expected rep movs

Convert Instruction in assembly code lods and stos so NASM can compile


所以是的,Intel-syntax assembly, not to mention very different syntaxes like AT&T有多种口味。

AT&T 有意对某些指令使用不同的助记符,甚至将一些在 Intel 语法中共享助记符的操作码拆分为单独的助记符,例如 movzb 用于 movzx-with-a-byte-source , 和 movzw 为词源版本。 (通常也与大小后缀一起使用,例如 movzbl,但如果您愿意,可以从 32 位目标寄存器推断出 l。)

和 AT&T 语法 无意中 在与两个寄存器操作数 a syntax design bug we're stuck with 一起使用时将 fsubrfsub 交换。 (幸运的是,x87 作为一个整体已经过时了。)