英特尔 x86 手册中是否存在像 direct/indirect 寻址模式这样的术语

Do terms like direct/indirect addressing mode actual exists in the Intel x86 manuals

为了提供一点背景知识,我想手动研究一下 x86 指令是如何 encoded/decoded 的。我遇到了 ModR/MSIB 字节,似乎理解 x86 寻址模式是理解指令编码方案的基础。

因此,我Google 搜索了 x86 寻址模式。搜索返回的大多数 blogs/videos 是 8086 处理器的寻址模式。通过其中一些,不同的寻址模式是寄存器、直接、间接、索引、基于,等等。但是博客在提到这些寻址模式时使用了不一致的名称。多个不同的来源使用多种不同的寻址模式。英特尔手册 here 中甚至没有提及不同的术语。例如,我在Intel手册中似乎找不到任何地方,一种称为Direct或Indirect的寻址方式。此外,ModRM 字节中的 Mod 位是一个 2 位字段,这让我想知道是否可以有 4 种以上的寻址模式。

我的问题是,像直接寻址模式、间接寻址模式这样的术语是否在 Intel 手册中不再使用,但被一般 public 使用。如果这些术语在技术上确实存在,我可以在手册中的何处找到对它们的引用。

大多数形式的 x86 寻址 modes 都没有真正的正式名称。除了 64 位 RIP 相对寻址外,它们都具有 [base + index*scale + disp8/disp32] 形式(或任何 1 或 2 个组件的子集)。请参阅 详细了解您可以对每个子集执行的操作。

英特尔在第 1 卷的第 3.7.5 节中正式使用这些名称作为寻址组件 modes(引用如下)。他们还使用 Register vs. Immediate vs. Memory 来对操作数进行分类,但通常不会对内存操作数的不同寻址形式 mode 大做文章。 (在 x86 机器代码中,操作数通常是 r/m,即它可以是 reg 或 mem,具体取决于 ModRM 字节中的 2 位“mod”字段,而另一个操作数是绝对是一个寄存器或绝对是一个立即数,正如操作码所暗示的那样。例如,参见 forms of add)

The Mod bits in the ModRM byte is a 2 bit field, which makes me wonder if more than 4 addressing modes are possible.

Mod 选择 Register vs. Memory with disp0/8/32。

  • mod 没有位移的 [rbp] 意味着有一个没有底座的 disp32。 (这就是你在反汇编中看到 [rbp+0] 的原因:[rbp] 的最佳编码是 base=rbp,disp8 为 0。(请注意 [rbp] 在它是帧时没有用指针。)
  • base=rsp 的 ModR/M 编码意味着有一个 SIB 字节。
  • 索引=RSP 的 SIB 编码意味着没有索引。 (根据前面的规则,这使得编码 [rsp] 而不是不太有用的 [rsp+rsp] 成为可能。)

在用英语撰写有关汇编语言的文章时,使用含义明显的术语是很自然的,包括您提到的一些术语。例如,Intel's optimization manual 表示(我强调):

2.3.2.4 Micro-op Queue and the Loop Stream Detector (LSD)

... (micro-fused uops with indexed addressing modes are un-laminated in the IDQ on SnB)

... For code that is dominated by indexed addressing (as often happens with array processing), recoding algorithms to use base (or base+displacement) addressing can sometimes improve performance by keeping the load plus operation and store instructions fused.

索引寻址 modes 包括使用 idx*scale 的任何组合,无论它是与基本 reg 还是与 disp32,或两者兼而有之。 (idx 单独是不可编码的;[rax*1] 实际上编码为 disp32+idx*1disp32=0。)在某些时候他们说“任何带有索引的寻址 mode " 或类似的,否则可能不清楚它们的确切含义。当然,testing with performance counters可以验证解释。

但他们并没有过分地为事物编造名字。当没有明显的英语短语时,他们可以粘贴在某物上,他们写道(仍在 Sandybridge 部分):

The common load latency is five cycles. When using a simple addressing mode, base plus offset that is smaller than 2048, the load latency can be four cycles.

在table 2-19中,它们有两列,一列用于Base + Offset > 2048;Base + Index [+ Offset],另一个用于 Base + Offset < 2048,延迟低 1 个周期(256b AVX 加载除外)。 (有趣的是,[rdi+8] 的延迟比 [rdi-8] 低 1c。)

(从技术上讲,他们可能应该说“位移”,因为整个寻址 mode 计算(有效地址)是 x86 术语中 seg:off 逻辑地址的偏移量,这添加到段基址时形成线性地址。但是“偏移量”也用于描述非 x86 通用术语中寻址 modes 的立即常量部分。幸运的是,x86 分段不是您通常必须考虑的事情大约这些天。)


在第 1 卷手册中,英特尔确实使用了您描述的一些术语。他们将仅具有位移分量的寻址 mode 描述为“直接”(某种),将 [reg] 描述为“间接”,因为在谈论指令集和指令集时确实会使用这些术语解决 mod 他们支持的问题。

vol.1 3.7.5 Specifying an Offset

The following addressing modes suggest uses for common combinations of address components.

  • Displacement ⎯ A displacement alone represents a direct (uncomputed) offset to the operand. Because the displacement is encoded in the instruction, this form of an address is sometimes called an absolute or static address. It is commonly used to access a statically allocated scalar operand.

  • Base ⎯ A base alone represents an indirect offset to the operand. ...

  • (Index ∗ Scale) + Displacement ⎯ This address mode offers an efficient way to index into a static array...

  • Base + Index + Displacement ...

  • Base + (Index ∗ Scale) + Displacement ⎯ Using all the addressing components together allows efficient indexing of a two-dimensional array when the elements of the array are 2, 4, or 8 bytes in size.

但是如您所见,它们不会为更复杂的形式命名。

不过,他们确实区分了立即操作数、寄存器操作数和内存操作数。 (3.7 操作数寻址)。不过,它们通常很少或根本没有区分使用寄存器编码的 r/m32 操作数与必须是寄存器的其他操作数。


分支指令术语

直接与间接也适用于分支机构。这有点像谈论寻址 mode 以到达下一个 运行 的代码字节。

6.3.7 Branch Functions in 64-Bit Mode

...

Address sizes affect the size of RCX used for JCXZ and LOOP; they also impact the address calculation for memory indirect branches. Such addresses are 64 bits by default; but they can be overridden to 32 bits by an address size prefix.

内存间接是 jmp [rax],其中 RIP 的最终值来自内存,而不是像 jmp rax 这样设置 RIP=RAX 的寄存器间接分支。 x86 没有用于数据 loads/stores 的内存间接寻址 mode;采用分支后的代码获取在术语中引入了额外的间接级别。 (某种程度上,由于在将新地址加载到其中后,RIP 被代码提取取消引用)。

第 2 卷 manual entry for jmp 确实讨论了间接与相对或绝对跳跃。 (虽然注意 x86 没有绝对直接 near 跳转(如果你不能使用 relative,将地址放在寄存器和 jmp reg 中);唯一的绝对直接跳转很慢"far" jmp ptr16:16jmp ptr16:32 带有一个直接指针作为机器代码的一部分。)

在描述近间接跳转时,jmp r/m32(或 64),他们说“在 GP reg 或内存中间接指定的绝对偏移量”。 (此处 seg:off 意义上的“偏移量”将用作 cs:eip 或 cs:rip 的一部分以获取代码。)。

分段使 x86 寻址的讨论变得更加复杂,尤其是在比较可以显式包含段的特殊寻址 modes 与不包含段的特殊寻址时。


命名寻址modes 被高估了

记住 x86 寻址 modes 可以根据一般情况的子集执行的操作要容易得多,而不是分别使用诸如 Indexed、Based 或其他名称来记住所有不同的可能性。

您在像 https://www.tutorialspoint.com/microprocessor/microprocessor_8086_addressing_modes.htm or http://www.geeksforgeeks.org/addressing-modes/ 这样的教程中看到了那种对寻址 modes 进行分类的重要内容。后者甚至有一个测验,要求您将 C 语句与一些寻址-mode 名称相匹配。

使用不太灵活的 16 位寻址 modes,您可以尝试命名它们的数量很少,Based 与 Indexed 确实为您提供了不同的寄存器选择。但是当你在编程时,你真正需要记住的是它是你对 [bx|bp] + [di|si] + disp0/8/16 的任何子集的选择。这就是 di/si(dst/src 索引)以及 bx/bp 的名字来源。


这样的术语在比较不同 ISA 的功能时很有用。例如,Wikipedia says像 PDP-8 这样的旧 ISA 大量使用内存间接,因为它们的寄存器很少,而且寄存器只有 8 位寻址范围。

维基百科还说:

Note that there is no generally accepted way of naming the various addressing modes.

modes 的命名没有意义。如果你正在写东西,请确保你的意思很清楚,而不依赖于某些术语的特定技术含义。例如如果你说“索引寻址 mode”,请确保 reader 从上下文中知道你是否包含 base+index*scale

我想知道命名 modes 的某些愿望是否起源于早于 8086 的 8 位微处理器。您可能想在 https://retrocomputing.stackexchange.com/ 上询问这个问题。我不太了解 8 位 CPU 上可用的寻址 modes,主要是固定的一字节指令。