assembly x86莱亚指令执行不明确的操作[重复]

trnvg8h3  于 2023-05-18  发布在  其他
关注(0)|答案(2)|浏览(135)

此问题已在此处有答案

Using LEA on values that aren't addresses / pointers?(4个答案)
3天前关闭。
下面是C代码:

int baz(int a, int b)
{
    return a * 11;
}

它被编译为以下汇编指令集(带有-O2标志):

baz(int, int):
        lea     eax, [rdi+rdi*4]
        lea     eax, [rdi+rax*2]
        ret

lea指令计算第二个操作数(源操作数)的有效地址,并将其存储在第一个操作数中。对我来说,似乎第一条指令应该加载一个地址到EAX寄存器,但是,如果是这样的话,在第二条lea指令中将RAX乘以2就没有意义了,所以我推断这两条lea指令做的事情并不完全相同。
我想知道是否有人能解释一下这里到底发生了什么。

yduiuuwa

yduiuuwa1#

a的函数参数存储在rdi中。不需要从内存加载任何东西。
lea eax, [rdi+rdi*4]不计算任何内存位置的地址以从中检索数据。相反,编译器只是重新利用指令来做乘法。它存储a + a*4eax。我们把这个值称为t
然后,lea eax, [rdi+rax*2]有效地存储a + t*2eax
rax也是函数返回值的寄存器。
所以返回值将是a + t*2,即a + (a + a*4)*2,即a + a*5*2,即a*11

chhqkbe1

chhqkbe12#

Linux使用System V AMD64 ABI调用约定,该约定将第一个整数参数传递到寄存器RDI中,并将返回值传递到RAX中。这里EAX就足够了,因为它返回一个32位的值。第二个参数未使用。
LEA最初用于8086处理器上的地址计算,但也用于具有常数因子的整数运算,这里就是这种情况。在指令编码中使用SIB字节的标度值来编码常数因子。它可以是1、2、4或8。
因此,代码可以解释为

baz(RDI, RSI):            ; a, b
lea     eax, [rdi+rdi*4]  ; RAX = 1*a + 4*a   = 5*a
lea     eax, [rdi+rax*2]  ; RAX = 1*a + 2*RAX = 1*a + 2*(5*a)
ret                       ; return RAX/EAX = 11*a

RAX的上半部分(64位值)由第一个LEA自动清除,参见this SO question

相关问题