# 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 代码中缺失的表达式。