索引寻址模式和隐式寻址模式



索引寻址模式通常用于访问数组,因为数组是连续存储的。我们有一个索引寄存器,它在每次迭代中都会递增,当添加到基址时,它会给出数组元素地址。 我不明白这种寻址模式的实际需求。为什么我们不能用直接寻址来做到这一点?我们有基址,每次访问时都可以添加 1。为什么我们需要索引寻址模式,该模式具有索引寄存器的开销?

我不确定隐含寻址模式的指令格式。假设我们有一个指令INC AC。指令中是否指定了AC的地址,或者是否有表示"INC AC"的特殊操作码,而我们不包括AC(累加器)的地址?

我不明白这种寻址模式的实际需求。 为什么我们不能通过直接寻址来做到这一点?

您可以;MIPS只有一种寻址模式,编译器仍然可以为其生成代码。 但有时它必须使用额外的 shift +add指令来计算地址(如果它不仅仅是循环遍历数组)。

寻址模式的要点是保存指令和保存寄存器,特别是在像x86这样的2操作数指令集中,其中add eax, ecx用结果(eax += ecx)覆盖eax,不像MIPS或其他3指令ISA,其中addu $t2, $t1, $t0t2 = t1 + t0。 在x86上,这将需要一个副本(mov)和一个add。 (或者在这种特殊情况下,lea edx, [eax+ecx]:x86 可以使用与内存操作数相同的指令编码进行复制和添加(和移位)。

考虑一个直方图问题:您以不可预测的顺序生成数组索引,并且必须为数组编制索引。 在 x86-64 上,add dword [rbx + rdi*4], 1将使用单个 4 字节指令递增内存中的 32 位计数器,该指令仅解码为 2 uops,以便前端发出到现代英特尔 CPU 上的无序内核中。 (http://agner.org/optimize/)。 (rbx是基本寄存器,rdi是缩放索引)。 拥有缩放索引非常强大;x86 16 位寻址模式支持 2 个寄存器,但不支持缩放索引。

经典MIPS只有单独的移位和添加指令,尽管MIPS32确实为地址计算添加了缩放添加指令。 这将在这里节省一条指令。 作为负载存储机器,加载和存储始终必须是单独的指令(与 x86 不同,在 x86 上,添加解码为微融合加载+添加和存储。 请参阅 INC 指令与添加 1:这重要吗?

对于MIPS来说,ARM可能是一个更好的比较:它也是一个负载存储RISC机器。 但它确实有多种寻址模式可供选择,包括使用桶形移位器的缩放索引。 因此,您不需要为每个数组索引单独移位/添加,而是使用具有相同寻址模式的LDR R0, [R1, R2, LSL #2]add r0, r0, #1/str

通常,在循环遍历数组时,最好只在 x86 上增加指针。 但它也是使用索引的一种选择,特别是对于使用同一索引的多个数组的循环,例如C[i] = A[i] + B[i]。 但是,索引寻址模式有时在硬件中的效率略低,因此当编译器展开循环时,它通常应该使用指针,即使它必须单独增加所有 3 个指针而不是一个索引。


指令集设计的意义不仅仅是图灵完备,而是实现高效的代码,以更少的时钟周期和/或更小的代码大小完成更多的工作,或者让程序员选择瞄准这些目标中的任何一个。

计算机可编程的最低阈值极低,例如参见各种单指令集计算机体系结构。 (没有一个是真正实现的,只是在纸上设计,以表明可以只用减法和分支(如果小于零)指令编写程序,并在指令中编码内存操作数。

在易于解码(尤其是并行解码)与紧凑之间有一个权衡。 x86 很糟糕,因为它演变为一系列扩展,通常没有太多计划为未来的扩展留出空间。 如果您对ISA设计决策感兴趣,请查看Agner Fog的博客,了解有关为高性能CPU设计ISA的有趣讨论,该ISA结合了x86的优点(大量工作与一条指令,例如作为ALU指令一部分的内存操作数)与RISC的最佳功能(易于解码,大量寄存器): 建议理想的可扩展指令集。

在如何使用指令字中的位也有权衡,尤其是在像大多数RISC一样的固定指令宽度ISA中。 不同的《国际审计准则》作出了不同的选择。

  • PowerPC 将大量编码空间用于强大的位域指令,如rlwinm(向左旋转并屏蔽位窗口)和大量操作码。 IDK 如果通常无法发音和难以记住的助记符与此有关......
  • ARM 使用高 4 位来预测执行基于条件代码的任何指令。 它为桶形移位器使用更多位(第二个源操作数可以选择通过立即或来自另一个寄存器的计数来移动或旋转)。
  • MIPS具有相对较大的直接操作数,并且基本上很简单。

x86 32/64 位寻址模式使用可变长度编码,当有索引时,具有额外的字节 SIB(刻度/索引/基数)字节,以及可选的 disp8 或 disp32 立即置换。 (例如add esi, [rax + rdx + 12340]编码需要 2 + 1 + 4 字节,而add esi, [rax]则需要 2 个字节。

x86 16 位寻址模式受到更多限制,并且将除可选 disp8/disp16 位移之外的所有内容打包到 ModR/M 字节中。


假设我们有一个指令INC AC。指令中是否指定了AC的地址,或者是否有表示"INC AC"的特殊操作码,而我们不包括AC(累加器)的地址?

是的,某些 ISA 中某些指令的机器代码格式包括隐式操作数。 许多机器都有push/pop指令,这些指令隐式使用特定的寄存器作为堆栈指针。 例如,在x86-64的push rax中,RAX是一个显式寄存器操作数(使用push r64缩写形式在单字节操作码的低3位中编码),而RSP是一个隐式操作数。

较旧的 8 位 CPU 通常具有像 DECA 这样的指令(递减累加器,A)。 即该寄存器有一个特定的操作码。 这可能与在操作码字节中具有一些位指定哪个寄存器的 DEC 指令相同(就像 x86-64 将短 INC/DEC 编码重新用作 REX 前缀之前 x86 所做的那样:请注意 64 位模式列中的"N.E"(不可编码)dec r32)。 但是,如果没有常规模式,那么它绝对可以被视为隐式操作数。

有时,将内容放入整洁的类别会分解,因此不要太担心使用带有操作码字节的位对于 x86 是隐式还是显式。 这是一种花费更多操作码空间来节省常用指令的代码大小,同时仍允许与不同的寄存器一起使用的方法。

按照惯例,某些 ISA 仅使用某个寄存器作为堆栈指针,没有隐式使用。 MIPS就是这样。

ARM32(在 ARM 模式下,而不是拇指模式下)也在 push/pop 中使用显式操作数。 它的推送/弹出助记符只是存储多个递减之前/加载倍增后 (LDMIA/STMDB) 的别名,以实现全降序堆栈。 请参阅 ARM 的 LDM/STM 文档,其中对此进行了解释,以及您可以使用这些指令的一般情况做什么,例如 LDMDB 递减指针然后加载(与 POP 相反的方向)。

最新更新