汇编-在偏移量为5的寄存器/数组中移动

时间:2020-03-24 05:54:09

标签: assembly x86-64 addressing-mode yasm

快速提问。该代码将无法编译:

mov eax, dword [rbx+rsi*5]

我不希望这样,因为mov和multiplication是两个不同的CPU操作。可以实现的唯一原因是通过移位。

但是,它确实可以编译:

mov eax, dword [lst+rsi*5]

“ lst”是变量数组。当在上下文中使用时,它也会产生输出(因此代码可以编译并运行)。为什么这样做有效呢?

yasm -Worphan-labels -g dwarf2 -f elf64 NAME.asm -l NAME.lst

1 个答案:

答案 0 :(得分:0)

x86寻址模式必须符合格式[base + idx*scale + disp0/8/32]。 (或相对于RIP。)

*scale实际上被编码为2位移位计数,因此它可以是1、2、4或8。请参见A couple of questions about [base + index*scale + disp]和{{3 }}

这里发生的是您的汇编器分解了[lst + rsi*5]
为您[lst + rsi + rsi*4]
。(或其他1 + (1<<0..3)形式的比例因子)
(其中lst是一个4字节(32位)的绝对地址,它的符号扩展为64位。是的,它在Referencing the contents of a memory location. (x86 addressing modes)中有效;静态代码+数据在低2GiB中虚拟地址空间,因此可以正常工作。)

但是,如果您已经具有基址寄存器,则无法拆分它,而仍然具有可编码的寻址模式。 [rbx + rsi + rsi*4]是不可能的。

类似地,NASM和YASM允许您编写类似vaddps xmm0, [rbp]而不是vaddps xmm0, xmm0, [rbp+0]的东西(即使RBP作为基址寄存器是Linux non-PIE executables。也可以省略第一个源操作数相同的情况)作为目的地)。例如,写[rbp + rax]而不是[rbp + rax*1]-寻址模式每个基址或索引最多只能有1个。

当您的代码表示的操作明确且可编码时,汇编器有时会具有便捷的功能,以使源代码看起来与机器代码/您从反汇编中获得的代码有所不同。


mov和乘法是两种不同的CPU操作

寻址模式包括加法和移位,即使shladd也是单独的指令。那不是原因另外,imul ecx, [lst + rsi + rsi*4], 12345是有效的指令。带有内存源或目标操作数的类似移位或加法也是如此。

但是,是的,x86寻址模式不能对任意乘法进行编码,而只能对2位移位计数进行编码。


遍历任意步幅/元素大小的数组:

通常,您会在寄存器中获得一个指针,并在循环内将其递增

add  rsi, 5*4      ; 5*4 = 20 as an assemble time constant expression
add  eax, [rsi]

基本上,这是缩放的强度降低,从而使乘法或移位变为加法。这意味着您可以使用效率更高的简单非索引寻址模式(代码大小,并避免在Sandybridge系列上分层)。