3.6 lea 指令
lea(Load Effective Address,加载有效地址)是 x86-64 中一条独特且强大的指令。它计算内存引用的地址并将其存入目标寄存器,不访问内存。
基本语法
leaq 源操作数(S), 目的操作数(D) # D = &S其中源操作数使用 的寻址格式,但指令只计算地址,不读取内存。
lea 与 mov 的关键区别
# 假设 %rdi = 0x1000,内存[0x1000] = 0x42
movq (%rdi), %rax # rax = 内存[0x1000] = 0x42 (读取内存)
leaq (%rdi), %rax # rax = 0x1000 (只计算地址)| 特性 | lea | mov |
|---|---|---|
| 访问内存 | ❌ 不访问 | ✅ 访问(当源操作数是内存引用时) |
| 用途 | 地址计算 / 算术运算 | 数据传送 |
| 性能 | 快(仅 ALU 运算) | 可能慢(含访存延迟) |
核心用途
1. 计算指针地址
// C 语言示例
long *ptr = &array[i];# %rdi = &array, %rsi = i
leaq (%rdi, %rsi, 8), %rax # rax = array + i*8 = &array[i]2. 执行算术运算(无乘法指令)
leaq 可以高效完成形如 a + b × k + c 的运算,其中 k ∈ {1,2,4,8}。
# 实现 t = x + y*8 + 3
leaq 3(%rdi, %rsi, 8), %rax # rax = rdi + rsi*8 + 3
# 实现 t = x*4 + y*2
leaq (%rdi, %rdi, 4), %rax # rax = rdi + rdi*4 = rdi*5 (不是乘以 4!)
# 注意:需要组合使用
leaq (%rdi, %rdi, 2), %rax # rax = rdi * 3
addq %rdi, %rax # rax = rdi * 4 (或用左移 shlq $2, %rdi)3. 快速乘法(乘数为小的 2 的幂 ±1)
# 乘以 3
leaq (%rdi, %rdi, 2), %rax # rax = rdi * 3
# 乘以 5
leaq (%rdi, %rdi, 4), %rax # rax = rdi * 5
# 乘以 9
leaq (%rdi, %rdi, 8), %rax # rax = rdi * 9
# 乘以 7 (需要两步)
leaq (%rdi, %rdi, 8), %rax # rax = rdi * 9
subq %rdi, %rax # rax = rdi * 9 - rdi = rdi * 74. 寄存器间加法(不修改条件码)
# 实现 rax = rdi + rsi (与 addq 类似,但不设置条件码)
leaq (%rdi, %rsi), %rax # rax = rdi + rsi
# 对比 addq
addq %rsi, %rdi # rdi = rdi + rsi (会设置条件码)leaq 不修改 ,在某些场景下比 addq 更合适。
5. 实现 &(s->field)
struct S {
int a;
long b;
double c;
};
// 获取成员 b 的指针
long *pb = &s->b;# %rdi 指向 struct S
leaq 8(%rdi), %rax # rax = rdi + 8 = &(s->b)lea 的算术能力限制
leaq 能表达的运算受寻址格式限制:
| 运算 | lea 支持 | 示例 |
|---|---|---|
x + c | 是 | leaq c(%rdi), %rax |
x × k | 仅 k=1,2,4,8 | leaq (, %rdi, k), %rax |
x + y | 是 | leaq (%rdi, %rsi), %rax |
x + y×k | 仅 k=1,2,4,8 | leaq (%rdi, %rsi, k), %rax |
x + y×k + c | 仅 k=1,2,4,8 | leaq c(%rdi, %rsi, k), %rax |
x × 常数 (非 2 的幂) | 否 | 需组合指令 |
编译器使用 lea 的典型优化
// C 源代码
long calc(long a, long b, long c) {
return a * 3 + b * 5 + c * 7;
}# gcc -O2 生成的汇编
calc:
leaq (%rdi, %rdi, 2), %rax # rax = a * 3
leaq (%rsi, %rsi, 4), %rdx # rdx = b * 5
addq %rdx, %rax # rax = a*3 + b*5
leaq (%rcx, %rcx, 8), %rdx # rdx = c * 9
subq %rcx, %rdx # rdx = c*9 - c = c*8 ??? 不是 c*7
addq %rdx, %rax # rax = a*3 + b*5 + c*8 (编译器可能不同优化)
ret编译器会智能选择
lea+add/sub/shl的组合来优化整数乘法。
lea 的典型模式总结
# 结果 = 基址 + 变址×比例 + 偏移
# ↓ ↓ ↓
leaq offset(base, index, scale), dest| 模式 | 等价运算 |
|---|---|
leaq (%r1), %r2 | r2 = r1 |
leaq (%r1, %r2), %r3 | r3 = r1 + r2 |
leaq (, %r1, k), %r2 | r2 = r1 × k |
leaq (%r1, %r2, k), %r3 | r3 = r1 + r2 × k |
leaq k(%r1), %r2 | r2 = r1 + k |
leaq k(%r1, %r2, s), %r3 | r3 = r1 + r2 × s + k |
相关笔记
-
- lea 使用的寻址格式
-
- 与 lea 对比的算术指令
-
- 数组地址计算使用 lea
-
- lea 不修改条件码的特性
链接到
- 上一个知识点:3.5 栈操作
- 下一个知识点:3.7 算术运算指令