# 3.5.1 加载有效地址

加载有效地址(load effective address)指令 leaq 实际上是 movq 指令的变形。它的指令形式是从内存读数据到寄存器,但实际上它根本就没有引用内存。它的第一个操作数看上去是一个内存引用,但该指令并不是从指定的位置读入数据,而是将有效地址写入到目的操作数。在图 3-10 中我们用 C 语言的地址操作符 &S 说明这种计算。这条指令可以为后面的内存引用产生指针。

另外,它还可以简洁地描述普通的算术操作。例如,如果寄存器 %rdx 的值为 x,那么指令

leaq 7(%rdx,%rdx,4),%rax

将设置寄存器 %rax 的值为 5x + 7。编译器经常发现 leaq 的一些灵活用法,根本就与有效地址计算无关。目的操作数必须是一个寄存器。

为了说明 leaq 在编译出的代码中的使用,看看下面这个 C 程序:

long scale(long x, long y, long z) {
    long t = x + 4 * y + 12 * z;
    return t;
}

编译时,该函数的算术运算以三条 leaq 指令实现,就像右边注释说明的那样:

long scale(long x, long y, long z)
x in %rdi, y in %rsi, z in %rdx

scale:
    leaq (%rdi,%rsi,4), %rax    x + 4*y
    leaq (%rdx,%rdx,2), %rdx    z + 2*z = 3*z
    leaq (%rax,%rdx,4), %rax    (x+4*y) + 4*(3*z) = x + 4*y + 12*z
    ret

leaq 指令能执行加法和有限形式的乘法,在编译如上简单的算术表达式时,是很有用处的。

练习题 3.6 假设寄存器 %rax 的值为 x,%rcx 的值为 y。填写下表,指明下面每条汇编代码指令存储在寄存器 %rdx 中的值:

表达式 结果
leaq 6(%rax),%rdx
leaq (%rax,%rcx),%rdx
leaq (%rax,%rcx,4),%rdx
leaq 7(%rax,%rax,8),%rdx
leaq 0xA(,%rcx,4),%rdx
leaq 9(%rax,%rcx,2),%rdx

练习题 3.7 考虑下面的代码,我们省略了被计算的表达式:

long scale2(long x, long y, long z) {
    long t = ____________________;
    return t;
}

用 GCC 编译实际的函数得到如下的汇编代码:

long scale2(long x, long y, long z)
x in %rdi, y in %rsi, z in %rdx

scale2:
    leaq (%rdi,%rdi,4), %rax
    leaq (%rax,%rsi,2), %rax
    leaq (%rax,%rdx,8), %rax
    ret

填写出 C 代码中缺失的表达式。