4.3 程序的机器级代码表示与 CISC/RISC

x86 汇编的基础(寄存器、常用指令、条件码与跳转),选择与循环语句的机器级表示,过程调用与栈帧,以及 CISC 与 RISC 的比较。

考查要求 ★★★ 重点掌握 x86 寄存器mov/lea/cmp/jcc选择与循环的汇编栈帧与过程调用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, srcdst ← 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 中。单步观察下面的调用过程:

动画演示过程调用的机器级表示:栈帧的建立与撤销
高地址 ↑FFCHFF8HFF4HFF0HFECHFE8HFE4HFE0HFDCHesp →ebp = 1020H低地址 ↓(栈向这里增长)eax = ?
IA-32 汇编(Intel 格式)
1caller:
2 push ebp
3 mov ebp, esp
4 sub esp, 8 ; 为局部变量 a、b 分配空间
5 mov [ebp-4], 1 ; a = 1
6 mov [ebp-8], 2 ; b = 2
7 push [ebp-8] ; 参数 b 入栈(从右往左)
8 push [ebp-4] ; 参数 a 入栈
9 call add ; 返回地址入栈,转到 add
10 add esp, 8 ; 调用者清理参数
11 ...
12add:
13 push ebp ; 保存调用者的 ebp
14 mov ebp, esp ; 建立 add 的栈帧
15 mov eax, [ebp+8] ; eax = x
16 add eax, [ebp+12] ; eax = x + y
17 pop ebp ; 恢复调用者的 ebp
18 ret ; 弹出返回地址,返回
1/16进入 caller 时,esp 指向栈顶,ebp 指向上一个函数的栈帧底部。x86 的栈从高地址向低地址增长

CISC 与 RISC

CISC(复杂指令集)RISC(精简指令集)
指令系统复杂、庞大,指令数目多(一般 > 200)简单、精简,只选使用频率高的简单指令(一般 < 100)
指令字长不固定,格式多定长,格式少
可访存指令不加限制只有 Load / Store 指令可以访存
各种指令执行时间相差较大,大多数指令需多个时钟周期绝大多数在一个周期内完成
通用寄存器数量较少多
控制方式绝大多数为微程序控制绝大多数为组合逻辑(硬布线)控制
指令流水线可以通过一定方式实现必须实现
编译优化难以优化采用优化的编译程序,生成高效的代码
代表x86ARM、MIPS、RISC-V

即时练习

即时练习4 题
点击选项即可作答
  1. 1
    下列关于 RISC 的叙述中,错误的是( )。
  2. 2
    在 IA-32 中,某函数的开头为 push ebp; mov ebp, esp。执行这两条指令后,第一个参数通常位于( )。
  3. 3
    执行 cmp eax, ebx 后,若要在 eax 小于 ebx(均为无符号数)时跳转,应使用的指令是( )。
  4. 4
    指令 lea eax, [ebx + ecx*4 + 8] 的作用是( )。
学完了吗?

标记完成后,侧栏会显示进度。进度仅保存在本浏览器中。