4.3 程序的机器级代码表示与 CISC/RISC
x86 汇编的基础(寄存器、常用指令、条件码与跳转),选择与循环语句的机器级表示,过程调用与栈帧,以及 CISC 与 RISC 的比较。
x86 汇编基础
寄存器
IA-32 有 8 个 32 位通用寄存器:EAX、EBX、ECX、EDX、ESI、EDI、EBP、ESP。其中 ESP 是栈指针(指向栈顶),EBP 是基址指针(指向当前栈帧底部)。EAX、EBX、ECX、EDX 的低 16 位可写作 AX 等,AX 又可分为 AH(高 8 位)与 AL(低 8 位)。另有指令指针 EIP(即 PC)和标志寄存器 EFLAGS(含 OF、SF、ZF、CF 等)。
常用指令(Intel 格式:目的操作数在前)
| 指令 | 含义 |
|---|---|
mov dst, src | dst ← src;两个操作数不能同时为内存地址 |
add / sub / inc / dec / imul / idiv | 算术运算,结果影响标志位 |
and / or / xor / not / shl / sar / shr | 逻辑运算与移位 |
lea dst, [addr] | 把地址(而不是内容)送入 dst,常用于计算 x + k*y |
cmp a, b / test a, b | 计算 a − b / a & b 并设置标志位,不保存结果 |
jmp label | 无条件跳转 |
je/jne/jg/jl/jge/jle | 有符号数的条件跳转(根据 ZF、SF、OF) |
ja/jb/jae/jbe | 无符号数的条件跳转(根据 CF、ZF) |
push / pop | 入栈(esp −= 4 后写入)/ 出栈(读出后 esp += 4) |
call / ret | 调用(压入返回地址并跳转)/ 返回(弹出返回地址到 EIP) |
寻址方式的表示
[base + index*scale + disp]:例如 mov eax, [ebx + esi*4 + 8] 取出地址 ebx + esi×4 + 8 处的 4 字节。访问 int a[i] 时,常写作 [基址 + i*4]。
选择与循环语句
int max(int x, int y) { // mov eax, [ebp+8] ; eax = x
if (x > y) return x; // cmp eax, [ebp+12] ; 比较 x 与 y
else return y; // jle .L2 ; x <= y 则跳转
} // jmp .L3 ; 返回 x
// .L2: mov eax, [ebp+12] ; eax = y
// .L3: ...
for / while 循环通常被翻译为”条件判断 + 循环体 + 跳回判断”的结构:
int sum = 0; // mov ecx, 0 ; i = 0
for (int i = 0; i < n; i++) // mov eax, 0 ; sum = 0
sum += a[i]; // .L1: cmp ecx, [n]
// jge .L2 ; i >= n 退出
// add eax, [a + ecx*4] ; sum += a[i]
// inc ecx ; i++
// jmp .L1
// .L2:
过程调用的机器级表示
过程调用时,每个过程都有自己的栈帧,由 EBP 和 ESP 界定。IA-32 中参数通过栈传递(从右往左压栈),返回值放在 EAX 中。单步观察下面的调用过程:
动画演示过程调用的机器级表示:栈帧的建立与撤销
IA-32 汇编(Intel 格式)
1caller:2 push ebp3 mov ebp, esp4 sub esp, 8 ; 为局部变量 a、b 分配空间5 mov [ebp-4], 1 ; a = 16 mov [ebp-8], 2 ; b = 27 push [ebp-8] ; 参数 b 入栈(从右往左)8 push [ebp-4] ; 参数 a 入栈9 call add ; 返回地址入栈,转到 add10 add esp, 8 ; 调用者清理参数11 ...12add:13 push ebp ; 保存调用者的 ebp14 mov ebp, esp ; 建立 add 的栈帧15 mov eax, [ebp+8] ; eax = x16 add eax, [ebp+12] ; eax = x + y17 pop ebp ; 恢复调用者的 ebp18 ret ; 弹出返回地址,返回
1/16进入 caller 时,esp 指向栈顶,ebp 指向上一个函数的栈帧底部。x86 的栈从高地址向低地址增长
CISC 与 RISC
| CISC(复杂指令集) | RISC(精简指令集) | |
|---|---|---|
| 指令系统 | 复杂、庞大,指令数目多(一般 > 200) | 简单、精简,只选使用频率高的简单指令(一般 < 100) |
| 指令字长 | 不固定,格式多 | 定长,格式少 |
| 可访存指令 | 不加限制 | 只有 Load / Store 指令可以访存 |
| 各种指令执行时间 | 相差较大,大多数指令需多个时钟周期 | 绝大多数在一个周期内完成 |
| 通用寄存器数量 | 较少 | 多 |
| 控制方式 | 绝大多数为微程序控制 | 绝大多数为组合逻辑(硬布线)控制 |
| 指令流水线 | 可以通过一定方式实现 | 必须实现 |
| 编译优化 | 难以优化 | 采用优化的编译程序,生成高效的代码 |
| 代表 | x86 | ARM、MIPS、RISC-V |
即时练习
即时练习4 题
点击选项即可作答
- 1下列关于 RISC 的叙述中,错误的是( )。
- 2在 IA-32 中,某函数的开头为 push ebp; mov ebp, esp。执行这两条指令后,第一个参数通常位于( )。
- 3执行 cmp eax, ebx 后,若要在 eax 小于 ebx(均为无符号数)时跳转,应使用的指令是( )。
- 4指令 lea eax, [ebx + ecx*4 + 8] 的作用是( )。
学完了吗?
标记完成后,侧栏会显示进度。进度仅保存在本浏览器中。