变址寻址方式和隐含寻址方式
Indexed addressing mode and implied addressing mode
索引寻址模式通常用于访问数组,因为数组是连续存储的。我们有一个索引寄存器,它在每次迭代中递增,当添加到基地址时给出数组元素地址。
我不明白这种寻址方式的实际需要。为什么我们不能通过直接寻址来做到这一点?我们有基址,每次访问时只需要加1即可。为什么我们需要变址寻址方式,它有变址寄存器的开销?
我不确定隐含寻址模式的指令格式。假设我们有一条指令 INC AC。指令中是否指定了 AC 的地址,或者是否有一个特殊的操作码,意思是 'INC AC' 而我们不包括 AC(累加器)的地址?
I don't understand the actual need of this addressing mode. Why can't we do this with direct addressing?
可以; MIPS 只有一种寻址模式,编译器仍然可以很好地为其生成代码。但有时它必须使用额外的 shift + add
指令来计算地址(如果它不仅仅是循环遍历数组)。
寻址模式的重点是保存指令和保存寄存器,尤其是在像x86这样的2操作数指令集中,其中add eax, ecx
用结果覆盖eax
(eax += ecx
) ,不像 MIPS 或其他 3 指令 ISA,其中 addu $t2, $t1, $t0
做 t2 = t1 + t0
。在 x86 上,这将需要一个副本 (mov
) 和一个 add
。 (或者在那种特殊情况下,lea edx, [eax+ecx]
:x86 可以使用与内存操作数相同的指令编码进行复制和添加(和移位)。)
考虑一个直方图问题:您以不可预测的顺序生成数组索引,并且必须对数组进行索引。在 x86-64 上,add dword [rbx + rdi*4], 1
将使用单个 4 字节指令在内存中增加一个 32 位计数器,该指令解码为仅 2 微指令,以便前端在现代系统上发出乱序内核英特尔 CPU。 (http://agner.org/optimize/)。 (rbx
是基址寄存器,rdi
是缩放索引)。拥有 scaled 索引非常强大; x86 16 位寻址模式支持 2 个寄存器,但不支持缩放索引。
经典 MIPS 只有单独的移位和加法指令,尽管 MIPS32 确实添加了用于地址计算的缩放加法指令。那会在这里保存一条指令。作为加载-存储机器,加载和存储始终必须是单独的指令(与 x86 不同,其中添加解码为微融合加载+添加和存储。参见 )。
ARM 可能更适合 MIPS:它也是一个加载-存储 RISC 机器。但它确实有多种寻址模式可供选择,包括使用桶形移位器的缩放索引。因此,您无需为每个数组索引单独移位/添加,而是使用具有相同寻址模式的 LDR R0, [R1, R2, LSL #2]
、add r0, r0, #1
/ str
。
通常在遍历数组时,最好只在 x86 上递增指针。但它也是使用索引的一种选择,特别是对于使用相同索引的多个数组的循环,如 C[i] = A[i] + B[i]
。但是,索引寻址模式有时可以是 slightly less efficient in hardware,因此当编译器展开循环时,它通常应该使用指针,即使它必须分别递增所有 3 个指针而不是一个索引。
指令集设计的重点不仅是图灵完备,还在于启用高效代码,以更少的时钟周期完成更多工作and/or更小代码大小,或者让程序员选择实现这些目标中的任何一个。
计算机可编程的最低门槛非常低,例如参见各种 One instruction set computer 体系结构。 (None 实际实现,只是在纸上设计,以表明可以只用减法和分支指令(如果小于零)编写程序,并将内存操作数编码在指令中。
易于解码(尤其是并行解码)与紧凑之间需要权衡。 x86 很糟糕,因为它是作为一系列扩展而发展起来的,通常没有太多的计划来为未来的扩展留出空间。如果您对 ISA 设计决策感兴趣,请查看 Agner Fog 的博客,了解有关为高性能 CPU 设计 ISA 的有趣讨论,该 CPU 结合了 x86 的优点(一条指令完成大量工作,例如内存操作数作为ALU 指令)具有 RISC 的最佳特性(易于解码,大量寄存器):Proposal for an ideal extensible instruction set.
在如何使用指令字中的位方面也存在折衷,尤其是在像大多数 RISC 一样的固定指令宽度 ISA 中。不同的ISA做出了不同的选择。
- PowerPC 使用大量编码 space 来实现强大的位域指令,例如
rlwinm
(向左旋转并屏蔽 window 位)和大量操作码。 IDK 如果通常无法发音且难以记忆的助记符与此相关...
- ARM 使用高 4 位来根据条件代码预测执行任何指令。它为 the barrel shifter 使用更多位(第二个源操作数可以选择通过立即数或来自另一个寄存器的计数进行移位或循环)。
- MIPS的立即数比较大,基本很简单
x86 32/64 位寻址模式使用可变长度编码,当有索引时有一个额外的字节 SIB (scale/index/base) 字节,以及一个可选的 disp8 或 disp32 立即位移。 (例如 add esi, [rax + rdx + 12340]
需要 2 + 1 + 4 个字节进行编码,而 add esi, [rax]
需要 2 个字节。
x86 16 位寻址模式受到更多限制,并将除可选 disp8/disp16 位移之外的所有内容打包到 ModR/M 字节中。
Suppose we have a instruction INC AC. Is the address of AC specified in the instruction or is there a special opcode which means 'INC AC' and we don't include the address of AC (accumulator)?
是的,某些 ISA 中某些指令的机器代码格式包含隐式操作数。许多机器都有 push
/ pop
隐式使用特定寄存器作为堆栈指针的指令。例如,在 x86-64 的 push rax
中,RAX 是显式寄存器操作数(encoded in the low 3 bits of the one-byte opcode using the push r64
short form),而 RSP 是隐式操作数。
较旧的 8 位 CPU 通常具有 DECA 之类的指令(用于递减累加器 A)。也就是说,该寄存器有一个特定的操作码。这可能与在操作码字节中使用 DEC 指令指定哪个寄存器的某些位相同(就像 x86 在 x86-64 将 short INC/DEC encodings 重新用作 REX 前缀之前所做的那样:注意 "N.E" (不可编码) 在 dec r32
的 64 位模式列中)。但是如果没有正则模式那绝对可以认为是隐式操作数。
有时候,将事物归类到整齐的类别中会出错,所以不要太担心将位与操作码字节一起使用对于 x86 来说是隐式的还是显式的。这是一种花费更多操作码 space 来节省常用指令代码大小的方法,同时仍允许使用不同的寄存器。
有些 ISA 只按照约定使用某个寄存器作为堆栈指针,没有隐式使用。 MIPS就是这样。
ARM32(在 ARM 中,不是 Thumb 模式)也在 push/pop 中使用显式操作数。它的 push/pop 助记符只是 store-multiple decrement-before / load-multiple increment-after (LDMIA / STMDB) 的别名,用于实现全降序堆栈。请参阅 ARM's docs 的 LDM/STM,它解释了这一点,以及您可以使用这些指令的一般情况做什么,例如LDMDB 递减指针然后加载(在 POP 的相反方向)。
索引寻址模式通常用于访问数组,因为数组是连续存储的。我们有一个索引寄存器,它在每次迭代中递增,当添加到基地址时给出数组元素地址。 我不明白这种寻址方式的实际需要。为什么我们不能通过直接寻址来做到这一点?我们有基址,每次访问时只需要加1即可。为什么我们需要变址寻址方式,它有变址寄存器的开销?
我不确定隐含寻址模式的指令格式。假设我们有一条指令 INC AC。指令中是否指定了 AC 的地址,或者是否有一个特殊的操作码,意思是 'INC AC' 而我们不包括 AC(累加器)的地址?
I don't understand the actual need of this addressing mode. Why can't we do this with direct addressing?
可以; MIPS 只有一种寻址模式,编译器仍然可以很好地为其生成代码。但有时它必须使用额外的 shift + add
指令来计算地址(如果它不仅仅是循环遍历数组)。
寻址模式的重点是保存指令和保存寄存器,尤其是在像x86这样的2操作数指令集中,其中add eax, ecx
用结果覆盖eax
(eax += ecx
) ,不像 MIPS 或其他 3 指令 ISA,其中 addu $t2, $t1, $t0
做 t2 = t1 + t0
。在 x86 上,这将需要一个副本 (mov
) 和一个 add
。 (或者在那种特殊情况下,lea edx, [eax+ecx]
:x86 可以使用与内存操作数相同的指令编码进行复制和添加(和移位)。)
考虑一个直方图问题:您以不可预测的顺序生成数组索引,并且必须对数组进行索引。在 x86-64 上,add dword [rbx + rdi*4], 1
将使用单个 4 字节指令在内存中增加一个 32 位计数器,该指令解码为仅 2 微指令,以便前端在现代系统上发出乱序内核英特尔 CPU。 (http://agner.org/optimize/)。 (rbx
是基址寄存器,rdi
是缩放索引)。拥有 scaled 索引非常强大; x86 16 位寻址模式支持 2 个寄存器,但不支持缩放索引。
经典 MIPS 只有单独的移位和加法指令,尽管 MIPS32 确实添加了用于地址计算的缩放加法指令。那会在这里保存一条指令。作为加载-存储机器,加载和存储始终必须是单独的指令(与 x86 不同,其中添加解码为微融合加载+添加和存储。参见
ARM 可能更适合 MIPS:它也是一个加载-存储 RISC 机器。但它确实有多种寻址模式可供选择,包括使用桶形移位器的缩放索引。因此,您无需为每个数组索引单独移位/添加,而是使用具有相同寻址模式的 LDR R0, [R1, R2, LSL #2]
、add r0, r0, #1
/ str
。
通常在遍历数组时,最好只在 x86 上递增指针。但它也是使用索引的一种选择,特别是对于使用相同索引的多个数组的循环,如 C[i] = A[i] + B[i]
。但是,索引寻址模式有时可以是 slightly less efficient in hardware,因此当编译器展开循环时,它通常应该使用指针,即使它必须分别递增所有 3 个指针而不是一个索引。
指令集设计的重点不仅是图灵完备,还在于启用高效代码,以更少的时钟周期完成更多工作and/or更小代码大小,或者让程序员选择实现这些目标中的任何一个。
计算机可编程的最低门槛非常低,例如参见各种 One instruction set computer 体系结构。 (None 实际实现,只是在纸上设计,以表明可以只用减法和分支指令(如果小于零)编写程序,并将内存操作数编码在指令中。
易于解码(尤其是并行解码)与紧凑之间需要权衡。 x86 很糟糕,因为它是作为一系列扩展而发展起来的,通常没有太多的计划来为未来的扩展留出空间。如果您对 ISA 设计决策感兴趣,请查看 Agner Fog 的博客,了解有关为高性能 CPU 设计 ISA 的有趣讨论,该 CPU 结合了 x86 的优点(一条指令完成大量工作,例如内存操作数作为ALU 指令)具有 RISC 的最佳特性(易于解码,大量寄存器):Proposal for an ideal extensible instruction set.
在如何使用指令字中的位方面也存在折衷,尤其是在像大多数 RISC 一样的固定指令宽度 ISA 中。不同的ISA做出了不同的选择。
- PowerPC 使用大量编码 space 来实现强大的位域指令,例如
rlwinm
(向左旋转并屏蔽 window 位)和大量操作码。 IDK 如果通常无法发音且难以记忆的助记符与此相关... - ARM 使用高 4 位来根据条件代码预测执行任何指令。它为 the barrel shifter 使用更多位(第二个源操作数可以选择通过立即数或来自另一个寄存器的计数进行移位或循环)。
- MIPS的立即数比较大,基本很简单
x86 32/64 位寻址模式使用可变长度编码,当有索引时有一个额外的字节 SIB (scale/index/base) 字节,以及一个可选的 disp8 或 disp32 立即位移。 (例如 add esi, [rax + rdx + 12340]
需要 2 + 1 + 4 个字节进行编码,而 add esi, [rax]
需要 2 个字节。
x86 16 位寻址模式受到更多限制,并将除可选 disp8/disp16 位移之外的所有内容打包到 ModR/M 字节中。
Suppose we have a instruction INC AC. Is the address of AC specified in the instruction or is there a special opcode which means 'INC AC' and we don't include the address of AC (accumulator)?
是的,某些 ISA 中某些指令的机器代码格式包含隐式操作数。许多机器都有 push
/ pop
隐式使用特定寄存器作为堆栈指针的指令。例如,在 x86-64 的 push rax
中,RAX 是显式寄存器操作数(encoded in the low 3 bits of the one-byte opcode using the push r64
short form),而 RSP 是隐式操作数。
较旧的 8 位 CPU 通常具有 DECA 之类的指令(用于递减累加器 A)。也就是说,该寄存器有一个特定的操作码。这可能与在操作码字节中使用 DEC 指令指定哪个寄存器的某些位相同(就像 x86 在 x86-64 将 short INC/DEC encodings 重新用作 REX 前缀之前所做的那样:注意 "N.E" (不可编码) 在 dec r32
的 64 位模式列中)。但是如果没有正则模式那绝对可以认为是隐式操作数。
有时候,将事物归类到整齐的类别中会出错,所以不要太担心将位与操作码字节一起使用对于 x86 来说是隐式的还是显式的。这是一种花费更多操作码 space 来节省常用指令代码大小的方法,同时仍允许使用不同的寄存器。
有些 ISA 只按照约定使用某个寄存器作为堆栈指针,没有隐式使用。 MIPS就是这样。
ARM32(在 ARM 中,不是 Thumb 模式)也在 push/pop 中使用显式操作数。它的 push/pop 助记符只是 store-multiple decrement-before / load-multiple increment-after (LDMIA / STMDB) 的别名,用于实现全降序堆栈。请参阅 ARM's docs 的 LDM/STM,它解释了这一点,以及您可以使用这些指令的一般情况做什么,例如LDMDB 递减指针然后加载(在 POP 的相反方向)。