程序的机器级表示
3.1历史观点
8086—〉80286—〉i386—〉i486—〉Pentium—〉PentiumPro—〉Pentium—〉Pentium—〉Pentium4—〉Pentium4e—〉Core 2 Duo —〉Core i7
3.2程序编码
1.gcc -01 –o p p1.c p2.c 使用第一级优化
2.程序计数器(%eip)指示将要执行的下一条指令在存储器中的地址。
3.寄存器文件
4.-S:C语言编译器产生的汇编代码
例:gcc -01 –S code.c 会产生一个汇编文件code.c
3.3数据格式
char |
字节 |
b |
1 |
short |
字 |
w |
2 |
int |
双字 |
1 |
4 |
long int |
双字 |
1 |
4 |
long long int |
— |
— |
4 |
char* |
双字 |
1 |
4 |
float |
单精度 |
s |
4 |
Double |
双精度 |
l |
8 |
long double |
扩展精度 |
t |
10/12 |
3.4访问信息
1.操作数指示符类型:立即数、寄存器、寄存器
2.数据传送指令
指令 |
效果 |
描述 |
MOV S,D |
S<-D |
传送 |
movb movw movl |
传送字节 传送字 传送双字 |
|
MOVS S,D |
D<-符号扩展(S) |
传送符号扩展的字节 |
MOVZ S,D |
D<-零扩展(S) |
传送零扩展的字节 |
3.5算术和逻辑操作(20135315韩玉琪的博客)
1.加载有效地址:leal实际上是movl的变形,为存储器引用产生指针
2.一元操作和二院操作:1)++,- -;2)+=
- 一元操作
- - INC 加1
- - DEC 减1
- - NEG 取负
- NOT 取补
- 只有一个操作数,既是源又是目的,可以是一个寄存器,或者存储器位置。
- 二元操作
- - ADD 加
- - SUB 减
- - IMUL 乘
- - XOR 异或
- - OR 或
- AND 与
- 第一个操作数可以是立即数、寄存器或者存储器位置
- 第二个操作数既是源也是又是目的。可以是寄存器或者存储器位置,但是不能同时是存储器位置。
- 注意操作的顺序:
第二个操作数 操作符 第一个操作数
3.移位操作:>>,<<
- 先给出移位量,第二项给出要移位的数值。
- - SAL 左移
- - SHL 左移(等同于SAL)
- - SAR 算术右移
- SHR 逻辑右移
- 源操作数(移位量):立即数或者放在单字节寄存器元素%cl中。
- 目的操作数:一个寄存器或是一个存储器位置。
4.特殊算术操作
- 乘法
- 乘积截断
- imull 双操作数
- 从两个32位操作数产生一个32位的乘积。
- 乘积不截断
- mull 无符号数乘法
- imull 有符号数乘法
- - 要求一个参数必须在寄存器%eax中,另一个作为指令的源操作数给出。
- 乘积的高32位在%edx中,低32位在%eax中。
- 除法
- 有符号除法
- idivl 操作数
- - 将DX:AX中的64位数作为被除数,操作数中为除数
- 结果:商在AX中,余数在DX中。
- 无符号除法
- divl指令
- 通常会事先设定寄存器%edx为0.
3.6控制
1.条件码:
CF:进位标志 ZF:零标志 SF:符号标志 OF:溢出标志
2.访问条件码
- SET指令:执行比较指令,根据计算t=a-b的结果设置条件码
3.跳转指令及其编码:jmp *%eax
- 无条件跳转
- 直接跳转:跳转目标是作为指令的一部分编码的。
- 间接跳转:跳转目标是从寄存器或存储器位置中读出的。
4.条件传送指令(参考资料20135202闫佳歆博客)
- 将条件表达式和语句从c语言翻译成机器语言,最常用的方式就是结合有条件和无条件跳转。
- if-else 的汇编结构
- 通用形式模板
- if(test-expr)
- then-statement
- else
- else-statement
(注:test-expr 整数表达式[假/真])
- 汇编实现形式
- t = test-expr;
- if (!t)
- goto false;
- then-statement
- goto done;
- false:
- else-statement
done:
5.switch语句
3.7过程
1.栈帧结构:机器用栈帧来传递过程参数、存储返回信息、保存寄存器用于以后的回复以及本地存储。为单个过程分配的那部分栈称为栈帧
2.帧指针:%ebp,栈指针:%%esp
3.转移控制
call Label 过程调用
call *Operand 过程调用
leave 为返回准备栈
ret 从过程调用中返回
4.寄存器使用惯例
1).%eax、%edx、%ecx 调用者保存
2).%ebx、%esi、%edi 被调用者保存
5.递归过程:递归调用一个函数本身与调用其他函数是一样的。相互调用更为复杂
问题:
1.比较指令cmp和减法指令sub有何不同?
sub d,s 是d-s,结果送回d中,即送回目的操作数中。
cmp d,s 也是d-s,但结果不送回目的操作数中,是利用减法进行两个数值的比较。
作业
- main.c:
- 汇编代码:
- 用vi查看编译器指令:
- 删除gcc产生代码中以"."开头的编译器指令后:
- 分析:
- main函数保存%ebp,并设置新的帧指针。
pushl %ebp movl %esp,%ebp
- 分配4字节的栈空间
subl $4,%esp
- 设置 arg1=8
movl $8,(%esp)
- call调用fh
- fh被调用,初始化帧指针,分配栈空间。
- 将(%esp)中的8给 %eax,即存入栈中
movl %eax,(%esp)
- call调用gh
- gh被调用,初始化栈指针,分配栈空间
- 将 %eax 与立即数 3 相加
add $3,%eax
- 在gh结束前弹栈
popl %ebp
- ret返回fh中call的调用位置
- fh也结束,return返回main中call调用的位置
- main继续 %eax 加1的操作
addl $1,%eax
- leave为返回准备栈,相当于%ebp出栈,最后ret结束。