前端对源程序进行进行分析并产生中间表示,后端则在此基础上生成目标代码。
前面几章介绍了编译器前端的主要内容,接下来会进入中间端和后端的部分。
中间端和后端的大致架构:
抽象语法树经过若干次中间表示和翻译,得到汇编代码。这是一个抽象层次逐渐降低的过程。
上世纪七八十年代时的做法是仅通过一次代码生成然后得到汇编,这样的做法由于抽象层次跨度太大,所以实现困难。
现代编译器的选择往往是经过多次生成而向目标代码逐渐靠近。
我们将研究两种不同的ISA上的代码生成技术
指令的语法
s -> push NUM
| load x // 栈操作
| store x // 访存
| add
| sub // 算数运算
| times
| div
指令的语义:push
NUM push 到栈上,NUM是立即数,不需要访存
push NUM:
top ++
stack[top] = NUM
指令的语义:load x
从内存读到栈上
load x:
top ++
stack[top] = x
指令的语义:store x
store x:
x = stack[top]
top --
指令的语义:add
add:
temp = stack[top - 1] + stack[top]
top -= 2
push temp
指令的语义:sub
sub:
temp = stack[top - 1] - stack[top]
top -= 2
push temp
指令的语义:times
times:
temp = stack[top - 1] * stack[top]
top -= 2
push temp
指令的语义:div
div:
temp = stack[top - 1] / stack[top]
top -= 2
push temp
示例
int x;
int y;
int z;
x = 10;
y = 5;
z = x + y;
y = z * x;
对应的指令为:
.int x
.int y
.int z
1: push 10
3: store x
4: push 5
5: store y
6: push x
7: push y
8: add
9: store z
10: load z
11: load x
12: times
13: store y
P -> D S
D -> T id; D
|
T -> int
| bool
S -> id = E
| printi(E)
| printb(E)
E -> n
| id
| true
| false
| E + E
| E && E
Gen_P(D S);
Gen_D(T id; D);
Gen_T(T);
Gen_S(S);
Gen_E(E);
指令的语法
s -> push NUM
| load x // 栈操作
| store x // 访存
| add
| sub // 算数运算
| times
| div
// 不变式(始终为真):表达式的值总在栈顶
// emit: 发送指令
Gen_E(E e)
switch(e)
case n: emit ("push n"); break;
case id: emit ("load id"); break;
case true: emit ("push 1"); break;
case false: emit ("push 0"); break;
case e1 + e2:
Gen_E(e1);
Gen_E(e2);
emit ("add");
break;
case e1 && e2:
Gen_E(e1);
Gen_E(e2);
emit ("and");
break;
// 不变式:栈的规模不变
Gen_S(S s)
switch (s)
case id=e: Gen_E(e);
emit("store id");
break
case printi(e): Gen_E(e);
emit("printi")
break
case printb(e): Gen_E(e);
emit("printb");
break;
Get_T(T t)
switch (t)
case int: emit(".int");
break;
case bool: emit(".bool");
break;
Get_D(T id; D)
Get_T(T); // -> .int / .bool
emit(" id");
Gen_D(D);
// 不变式:只生成 .int 类型
Gen_P(D S)
Gen_D(D);
Gen_S(S);
比如上述程序:
// 指令的语法
s -> movn n, r
| mov r1, r2 | 数据移动
| load [x], r |
| store r, [x] | 访存
| add r1, r2, r3 |
| sub r1, r2, r3 | 算术运算
| times r1, r2, r3 |
| div r1, r2, r3 |
Gen_E:
// 不变式:表达式的值在函数返回的寄存器中
R_t Gen_E(E e)
switch (e)
case n: r = fresh();
emit("movn n, r");
return r;
case id: r = fresh();
emit("mov id, r");
return r;
case true: r = fresh();
emit("movn 1, r");
return r;
case false: r = fresh();
emit("movn 0, r");
return r;
case e1+e2: r1 = Gen_E(e1);
r2 = Gen_E(e2);
r = fresh();
emit("add r1, r2, r3");
return r;
case e1&&e2: r1 = Gen_E(e1);
r2 = Gen_E(e2);
r = fresh();
emit("and r1, r2, r3");
return r; // 非短路
Gen_S:
Gen_S(S s)
switch (s)
case id=e: r = Gen_E(e);
emit("mov r, id");
break;
case printi(e): r = Gen_E(e);
emit("printi r");
break;
case printb(e): r = Gen_E(e);
emit("printb r");
break;
Gen_T:
// 不变式,只生成.int类型
Gen_T(T t)
switch (t)
case int: emit (". int");
break;
case bool: emit(". int");
break;
Gen_D:
// 不变式,只生成.int类型
Gen_D(T id; D)
Gen_T(T);
emit(" id");
Gen_D(D);
Gen_P:
// 不变式,只生成int类型
Gen_P(D S)
Gen_D(D);
Gen_S(S);
源代码生成了右边的汇编级代码
因篇幅问题不能全部显示,请点此查看更多更全内容