我熟悉这种形式的内存引用:
XXX ptr [base + index * size + displacement]
其中XXX是一些大小(字节/字/dword/等),base
和index
都是寄存器,size
是2的小幂,displacement
是有符号值。
AMD64引入了RIP相对寻址。据我了解,我应该能够使用rip
作为基本寄存器。但是,当我尝试使用 clang-900.0.39.2 执行此操作时:
mov r8b, byte ptr [rip + rdi * 1 + Lsomething]
我得到:
错误:基+索引表达式无效
mov r8b, byte ptr [rip + rdi * 1 + Lsomething]
使用rip
作为基本寄存器时,是否无法使用索引寄存器?我是否必须使用lea
来计算rip + Lsomething
然后偏移到该?
不,[RIP + rel32]
是唯一涉及 RIP 的寻址模式。另请参阅引用内存位置的内容。(x86 寻址模式)。
如果要最大程度地提高为静态数组编制索引的效率,则需要创建与位置相关的代码,以便在正常寻址模式下将表地址用作 32 位绝对disp32
。 在 Linux 中,在位置相关的可执行文件中允许这样做,但不允许共享库(必须是 PIC)。 看到 x86-64 Linux 中不再允许使用 32 位绝对地址?了解如何在默认情况下配置 GCC 以制作 PIE 时使用-fno-pie -no-pie
。
否则,对于与位置无关的数组索引,lea rsi, [rip + Lsomething]
并使用指针增量或[rsi + rdi*1 + constant]
寻址模式,或任何其他可行的替代方法。 (非索引寻址模式有时会在英特尔 CPU 上节省 uop,因此指针增量可能很好,尤其是在展开时,因此每个指针的add
可以收回成本,而不是对多个数组使用相同的索引。
在任意寻址模式下,它不是"RIP 作为基本寄存器";机器代码编码中没有空间。 x86-64 有 16 个可能的基本寄存器,用 ModR/M 或 SIB 字节中的 3 位 + 可选 REX 前缀中的 1 位编码。 要使RIP可用作具有任意寻址模式的基础,则需要消除其他寄存器,并在32位和64位模式之间的有效地址解码中产生很多差异。
x86-32 有 2 种冗余的编码方式[0x123456]
,即 no-base + disp32:有或没有 SIB 字节,因为 SIB 有无碱和无索引的编码。 请参阅 http://wiki.osdev.org/X86-64_Instruction_Encoding#32.2F64-bit_addressing 以获取漂亮的表格,或查看英特尔的手册。
无索引SIB编码使得编码[esp]
而不是[esp+esp]
成为可能,因为表示base=RSP的ModR/M编码是表示"有一个SIB"的转义码。 他们本可以设计它,这样你就可以使用esp
作为esp
以外的基础的索引,但没有人愿意首先使用esp
作为索引。 有趣的事实:无基数(带disp32
)编码使用本来是没有位移的[ebp]
,这就是为什么[ebp]
实际上被编码为[ebp + disp8=0]
。 在 x86-64 模式下,这也适用于 R13。
x86-64将较短的(无SIB)编码重新用于[disp32]
[RIP + disp32]
,即[RIP + rel32]
。
32位绝对地址([disp32]
)仍然可以用更长的SIB编码进行编码。 (这是 NASM 默认执行的操作,如果您不使用default rel
.) 甚至没有[RIP + disp8]
(例如,用于加载附近的代码地址)。 在 ModR/M 字节的 Mod 和 M 字段中只有一个位模式,用于编码 RIP 相对地址。