编译原理 - Lecture 3
本笔记适用于
- UPENN CIS 3410/7000
- ShanghaiTech CS 131 2024+
- Havard CS 153
X86Lite
- X86 太复杂了,我们使用 X86 Lite
- 比起完整的 X86,它
- 只有 64 位有符号整数(没有其他位数,没有浮点数)
- 只有 20 个指令(真少啊,我在 TuringComplete 里面搭的 CPU 都有 20 个指令(逃))
- 作为一般计算的目标语言完全足够了
X86 示意图
于是我们来了解一下这个结构和基本汇编
(回来了,都回来了,我亲爱的~~CA课程~~图灵完备)
寄存器
16 x 64-bit reg
rax
general purpose accumulatorrbx
base register, pointer to datarcx
counter register for strings & loopsrdx
data register for I/Orsi
pointer register, string source registerrdi
pointer register, string destination registerrbp
base pointer, points to the stack framersp
stack pointer, points to the top of the stackr08
-r15
general purpose registers
以及一个特殊的寄存器
rip
a “virtual” register, points to the current instructionrip
is modified only indirectly via jumps and return, but it can be mentioned as a register elsewhere
操作数
Imm
Immediate, 64-bit literal signed integeLbl
Label, representing address, solved by the assembler/linker/loadeReg
Register, has contentsInd
Indirect Address,addr(Ind) = (Base + [Index * Scale]) + Disp
, contains- Base
- Index * Scale (Index 不能为
rsp
,是否和栈生长方向有关) - Disp: const
- 例如:
-4(%rsp) = rsp - 4
(%rax, %rcx, 4) = rax + 4 * rcx + 0
12(%rax, %rcx, 4) = rax + 4 * rcx + 12
内存模型
- 内存地址: \(2^{64}\) bytes -
0x00000000
~0xffffffff
- Quadword-aligned: 四字节对齐==(地址能被 8 整除)==
- 栈顶指针
rsp
指向栈顶(地址最低处),栈根据习俗生长方向从高到低
指令
mov
- 将 SRC(value: content / immediate) 复制到 DEST(location: reg/mem addr)
- SRC, DEST 都是 操作数(Operands)
- Example:
movq $4, %rax
// move the 64-bit immediate value 4 intorax
movq %rbx, %rax
// move the contents ofrbx
intorax
AT&T notation VS Intel notation
AT&T Notation:
INST src, dest
- 流行于 Unix / Mac
- Immediate:
$
- Register:
%
- Mnemonic suffix
q
quadword (4 words, 64-bit)l
long (2 words, 32-bit)w
word (16-bit)b
byte (8-bit)
Intel Notation
INST dest, src
- 常见于 Wintel
- 指令变体由寄存器名称决定
Arithmetic
negq DEST
负(二进制补码)addq SRC, DEST
加subq SRC, DEST
减imulq SRC, Reg
乘 (Reg
<-Reg
*SRC
,128-bit 截断)
Logic/Bit
notq DEST
andq SRC, DEST
orq SRC, DEST
xorq SRC, DEST
sarq Amt, DEST
(>>
,有符号)shlq Amt, DEST
(<<
,无符号)shrq Amt, DEST
(>>>
,无符号)
Memory
leaq Ind, DEST
DEST
<-addr(Ind)
向DEST
加载一个指针pushq SRC
=rsp -= 8
+Mem[rsp] = SRC
popq DEST
=DEST = Mem[rsp]
+rsp += 8
Conditional
标志位
- 标志位:记录了与最近的一次算术或逻辑操作结果有关的信息
- X86 指令会在执行时设置 标志(Flags),作为副作用
- Flags
OF
Overflow, 对于 64-bit reg 太大/太小时设置SF
Sign, 由结果的符号设置(0=正数,1=负数)ZF
Zero, 当结果为0时设置
- 据此可以定义出条件代码(以下完全指令为
CC SRC1, SRC2
,实际上是根据SRC1 - SRC2
的 Flag 判别)
条件代码 (CC
)
eq SRC1, SRC2
, equality =>ZF
neq SRC1, SRC2
, not equality =>!ZF
gt SRC1, SRC2
, greater than =>!le
<=>(SF == OF) && (!ZF)
lt SRC1, SRC2
, less than =>SF != OF
<=>(SF && !OF) || (!SF && OF)
细节:大到了溢出就是负数ge SRC1, SRC2
, greater or equal =>!lt
<=>(SF == OF)
le SRC1, SRC2
, less or equal =>(SF != OF) || ZF
由此定义出
comq SRC1, SRC2
, compute, 计算SRC2 - SRC1
并设置标志(不会存储结果)setbCC DEST
设置DEST
的最低字节为CC
结果(b(DEST) = if CC then 1 else 0
)jCC DEST
跳转(设置rip = if CC then SRC else fallthrough
)
所以一次成功的条件跳转 / 设置低位的方式需要两步,例如
Jump, Call, Return
jmp SRC
, jump 相当于rip = SRC
callq SRC
, call function, 区别就是会压栈和返回,相当于Push(rip)
+rip = SRC
,会导致rsp --
retq
, return, 就是从出栈复原(无返回值),相当于rip = Pop()
,会导致rsp ++
代码块,标签
- 标签(Labels)可以作为 jump 的目标(条件/调用)
- 标签由 Linker 和 Loader 翻译 - 指令位于“代码段”的堆中(也就是
PHYS_BASE
上的第一段,详情复习 OS 的内存结构) - X86 程序从指定的代码标签(通常是
main
)开始执行