快速提问。该代码将无法编译:
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
答案 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操作
寻址模式做包括加法和移位,即使shl
和add
也是单独的指令。那不是原因另外,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系列上分层)。