为什么这个IA32汇编代码有三个leal指令?

时间:2014-03-19 16:12:49

标签: c assembly x86 intel

我编译了这个C函数:

int calc(int x, int y, int z) {
   return x + 3*y + 19*z;
}

我在calc.s中得到了这个,我正在注释正在发生的事情:

        .file   "calc.c"
        .text
.globl calc
        .type   calc, @function
calc:
        pushl   %ebp                    //Save paramaters
        movl    %esp, %ebp              //Move stack pointer into %ebp
        movl    12(%ebp), %eax          //Move y into %eax
        movl    16(%ebp), %ecx          //Move z into %ecx
        leal    (%eax,%eax,2), %eax     //%eax = 3*y
        addl    8(%ebp), %eax           //%eax = x+3y
        leal    (%ecx,%ecx,8), %edx     // ?
        leal    (%ecx,%edx,2), %edx     // ?
        addl    %edx, %eax              //%eax = (x+3*y)+(19*z)
        popl    %ebp                    //Pop the previous pointer
        ret
        .size   calc, .-calc
        .ident  "GCC: (Ubuntu 4.3.3-5ubuntu4) 4.3.3"
        .section        .note.GNU-stack,"",@progbits

我理解最后两个leal指令的所有内容。为什么你需要两个19 * z的leal指令,而3 * y是在一个指令中完成的。

3 个答案:

答案 0 :(得分:7)

leal是一种通过廉价的小常数进行乘法的方法,如果常数是2加1的幂。这个想法是没有偏移的leal相当于“Reg1 = Reg2 + Reg3 * Scale”。如果Reg2和Reg3恰好匹配,则表示“Reg1 = Reg2 *(Scale + 1)。

leal仅支持最多8的比例因子,因此要乘以19,您需要两个。

的影响
leal   (%eax,%eax,2), %eax

是:

eax = eax + eax*2

也就是说,乘以3。

后两个leal一起执行乘以19:

leal    (%ecx,%ecx,8), %edx     // edx = ecx+ecx*8
leal    (%ecx,%edx,2), %edx     // edx = ecx+edx*2 (but edx is already z*9)

答案 1 :(得分:4)

leal    (%ecx,%ecx,8), %edx # edx = ecx + 8*ecx = 9*ecx = 9 * z
leal    (%ecx,%edx,2), %edx
# edx = ecx + 2*edx = ecx + 2 * (ecx + 8*ecx) = z + 2 * 9 * z = 19 * z

这是lea指令使用add和bitshifts的原因,并且比使用mul进行整数乘法更快。 Lea受限于乘法因子1,2,4和8 - 因此有两条指令。

答案 2 :(得分:2)

lea用于双重目的是计算地址,但它也可用于具有某些约束的算术,如您在代码中观察到的那样。需要进行两次调用,因为lea的标量乘数为limited to 1, 2, 4 or 8,这意味着乘以19需要两次调用lea

  

[...]标量乘数分别限制为字节,字,双字或四字偏移的常数值1,2,4或8。这本身允许通用寄存器乘以常数值2,3,4,5,8和9,[...]

所以在你的情况下你有:

leal    (%ecx,%ecx,8), %edx     // edx = ecx + ecx*8 which is z*8 + z = z*9
leal    (%ecx,%edx,2), %edx     // edx = ecx + edx*2 which gives us (z*9)*2 + z
                                // for a total of 19z