5_地址计算指令与其它-2

地址计算指令与其它-2

将 leal 指令用于计算 (实例一)

我们故意构造这么一个 arith 函数

int arith(int x, int y, int z)
{
    int t1 = x + y;
    int t2 = z + t1;
    int t3 = x + 4;
    int t4 = y * 48;
    int t5 = t3 + t4;
    int rval = t2 * t5;
    return rval;
}

这个函数就是为了显示一下它的功能, 做了一些很无聊的计算

我们用 gcc 将其编译为汇编代码 :

arith :
    ; Set up
    pushl %ebp
    movl %esp, %ebp

    ; Body
    movl 8(%ebp), %eax
    movl 12(%ebp), %edx
    leal (%edx, %eax), %ecx
    leal (%edx, %edx, 2), %edx
    sall $4, %edx
    addl 16(%ebp), %ecx
    leal 4(%edx, %eax), %eax
    imull %ecx, %eax

    ; Finish
    movl %ebp, %esp
    popl %ebp
    ret

函数栈内存如图

首先我们 mov 指令, 相当于把 x y 两个参数分别放到了 eax edx 里面去.

leal (%edx, %eax), %ecx 实际上这里我们用了 leal 指令完成了加法

再往下走, %ecx leal (%edx, %edx, 2), %edx 相当于完成了一个 3*edx, 这种做法要比用单独一条乘法指令快

sall $4, %edx 相当于我把算出来的结果给它左移了四位, 也就是相当于乘了 16, 所以就完成了 48*y 的操作

再往后我们再把这个 z 取出来, 取出来之后我们当然把 z 加上 ecx, 就是 z 加上 t1. 然后还是用 leal 把数据算出来. 实际上完成了 4 + t4 + x

最后还有个 t2 * t5, 因为这是两个变量相乘, 就没有一个常量, 这样你不得不用一条乘法指令

实例二

一开头就把 eax 通过 8(%ebp), 就是把 x 这个参数传进来

第二条指令就是把 y 取出来, 运算了个异或

再往下呢我们做了一个算数的右移 sarl 17 位, 因为 t1 是 int 有符号位, 所以它要做算术右移保证符号位

andl $8185, %eax 就是做了个 mask, 我们看 C 代码, mask 里面有个 1 的什么什么再减去 7, 肯定在编译的过程中就把这个常数给算出来了, 算出来之后就直接把这个常数编译到你的指令里面去了, $8185 就是这么来的. 编译器直接把它算出来, 然后直接用上了, 就变成这个样子.