中间表示- 三地址码
使用三地址码的编译器结构
三地址码的基本思想
(1)给每个中间变量和计算结果命名,没有复合表达式
(2)只有最基本的控制流,没有各种控制结构(if、do、while、for等等),只有goto,call等
(3)所以三地址码可以看成是抽象的指令集,如通用的RISC
我们来看一个例子
a = 3 + 4 * 5;
if (x < y)
z = 6;
else
z = 7;
把上述程序翻译为三地址码后,程序如下:
x_1 = 4;
x_2 = 5;
x_3 = x_1 * x_2;
x_4 = 3;
x_5 = x_4 + x_3;
a = x_5;
Cjmp (x < y, L_1, L_2); // 条件跳转
L_1:
z = 6 ;
jmp L_3;
L_2:
z = 7;
jmp L_3;
L_3:
...
三地址码的定义
如何定义三地址码数据结构?
enum instr_kind {INSTR_CONST, INSTR_MOVE, ...};
struct Instr_t
{
enum instr_kind kind;
};
struct Instr_Add
{
enum instr_kind kind;
char *x;
char *y;
char *z;
};
struct Instr_Move
{
/* data */
};
如何生成三地址码?
以 C--生成三地址码 为例
要写如下几个递归函数来生成三地址码
Gen_P(P);Gen_F (F) ;Gen_T(T);Gen_S(S); Gen_E(E);
我们重点关注语句的代码生成,其余的可参考代码生成- 寄存器计算机_青衫客36的博客-CSDN博客
Gen_S(S s)
{
switch (s)
{
case x = e:
x1 = Gen_E(e); // 每个表达式的值要放在一个变量里
emit("x = x1");
break;
case printi(e):
x = Gen_E(e);
emit("printi(x)");
break;
case printb(e):
x = Gen_E(e);
emit("printb(x)");
break;
case x(e1, ..., en):
x1 = Gen_E(e1);
...;
xn = Gen_E(en);
emit("x(x1, ..., xn)");
break;
case return e:
x = Gen_E(e);
emit("return x");
break;
case if (e, s1, s2):
x = Gen_E(e);
emit("Cjmp(x, L1, L2)");
emit("Label L1:");
Gen_SList(s1);
emit("jmp L3");
emit("Label L2:");
Gen_SList(s2);
emit("jmp L3");
emit("Label L3:");
break;
case while (e, s):
emit("Label L1:");
x = Gen_E(e);
emit("Cjmp(x, L2, L3)");
emit("Label L2:");
Gen_SList(s);
emit("jmp L1");
emit("Label L3:");
break;
default:
break;
}
}
小结
(1)三地址码的优点:
- 所有的操作都是原子的,变量!没有复合结构
- 控制流结构被简化了,只有跳转
- 是抽象的机器代码,向后做代码生成更容易
(2)三地址码的不足:
- 程序的控制流信息是隐式的
- 可以做进一步的控制流分析