promisivia / tiger-compiler-lab

A compiler for tiger language, lab for book "modern compiler implement in C"

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

image-20201201150729333

抽象语法(AST)

image-20201201152708246

image-20201201153142420

语义分析(Type checking)

阶段任务:

  • 将Identifier映射到它们的类型和存储位置(利用symbol table)
  • 将抽象语法转化成更简单、适合于生成机器代码的表示

代码结构:

image-20201201170631364

symbol

为了避免字符串比较,把每个字符串转化为一个symbol对象,它的所有出现都被映射成了同一个symbol

image-20201201154033135

  • S_table : 将 S_Symbols 映射到 bindings
  • S_SymbolS_name 在symbol 和 name间相互转换
  • S_empty 创建一个新的表
  • S_enter 插入绑定 $x\to b$, x是一个symbol指针
  • S_beginScope remembers the current state of the table
  • S_endScope restores the table to where it was at the most recent S_beginScope that has not already been ended

type

绑定的是什么?类型标识符关联Ty_ty

image-20201201155056429

  • 基本类型是intstring

  • 复合类型有recordarray

    • record记录每个域的名字和类型(Ty_fieldList
    • array记录数组类型

    它们都记录了变量的地址,因此两个看似相同的并不是同一种类型

  • nil属于任何类型

  • void表示没有返回值的表达式

  • name用来占位那些只知道名字不知道定义的类型,用Ty_Name(sym, NULL)来创造类型名是sym的类型,稍后填充ty域。

env

image-20201201190522424

image-20201201190715629

  • 对于值标识符我们需要知道它是变量(E_varEntry)或者函数( E_funEntry),我们用E_enventry 来记录这个信息。

    • 变量需要知道类型(Ty_ty ty)是什么

    • 函数需要知道:

      • 参数(Ty_tyList formals)
      • 返回类型( Ty_ty result)
      image-20201201160923952

      Ty_tyList 是一连串的 type)

      • Tr_level level
      image-20201201190544905
  • base_tenv: maps the symbol int to Ty_Int and string to Ty_String

  • base_venv: contains bindings for predefined functionsflush, ord, chr, size, and so on

semant

执行语义分析(类型检查),后续还会扩充功能:将表达式转化为中间代码

image-20201201162035605

代码的逻辑:调用语法分析器来生成 A_exp, 接着对这个表达式调用 SEM_transProg

image-20201201161524564

  • trans*分别转换 A_varA_expA_Dec 三种表达式

image-20201201161734227

  • 返回值是 expty,包含两部分:
    • Tr_exp exp: 转换为中间代码的表达式
    • Ty_ty ty:表达式的类型

语义分析(Activation Records)

image-20201201163445549

frame/x86frame

image-20201201163837336

  • F_frame:栈帧的抽象表示,有关形式参数和分配在栈帧中局部变量的信息。

    • F_accessList:the locations of all the formals
    • T_stm:instructions required to implement the “view shift”
    • int size: 需要分配的栈帧大小
    • Temp_label:and the label at which the function’s machine code is to begin
  • F_access:描述存放在栈中或寄存器中的形式参数和局部变量。

    image-20201201164831893
    • 可以由 InFrame()InReg() 构造 F_access 对象。
  • F_newFrame(Temp_label name, U_boolList formals):返回一个新栈帧。

    image-20201201164610709
  • F_formals():返回形式参数的位置(从被调用函数的角度来看)。

  • F_allocLocal(F_frame f, bool escape):分配一个新的变量。

    • 逃逸变量 InFrame
    • 非逃逸变量 InReg
// 参考头文件
/* frame.h */
typedef struct F_frame_ *F_frame;
typedef struct F_access_ *F_access;
typedef struct F_accessList_ *F_accessList;
struct F_accessList_ {F_access head; F_accessList tail;};
F_accessList F_AccessList(F_access head, F_accessList tail);

typedef struct F_frag_ *F_frag;
struct F_frag_ {
enum {F_stringFrag, F_procFrag} kind;
union {struct {Temp_label label; string str;} stringg;
struct {T_stm body; F_frame frame;} proc;
} u;
};
F_frag F_StringFrag(Temp_label label, string str);
F_frag F_ProcFrag(T_stm body, F_frame frame);

typedef struct F_fragList_ *F_fragList;
struct F_fragList_ {F_frag head; F_fragList tail;};
F_fragList F_FragList(F_frag head, F_fragList tail);

Temp_map F_tempMap;
Temp_tempList F_registers(void);
string F_getlabel(F_frame frame);
T_exp F_Exp(F_access acc, T_exp framePtr);
F_access F_allocLocal(F_frame f, bool escape); //(see p. 139)
F_accessList F_formals(F_frame f); //(p. 137)
Temp_label F_name(F_frame f); ///(p. 136)
extern const int F_wordSize; //(p. 159)
Temp_temp F_FP(void); //(p. 159)
Temp_temp F_SP(void);
Temp_temp F_ZERO(void);
Temp_temp F_RA(void);
Temp_temp F_RV(void); //(p. 172)
F_frame F_newFrame(Temp_label name, U_boolList formals); //(p. 136)
T_exp F_externalCall(string s, T_expList args); //(p. 168)
F_frag F_string (Temp_label lab, string str); //(p. 269)
F_frag F_newProcFrag(T_stm body, F_frame frame);
T_stm F_procEntryExit1(F_frame frame, T_stm stm); //(p. 267)
AS_instrList F_procEntryExit2(AS_instrList body); //(p. 215)
AS_proc F_procEntryExit3(F_frame frame, AS_instrList body);

//c
T_stm F_procEntryExit1(F_frame frame, T_stm stm) {
	return stm;
}

static Temp_tempList returnSink = NULL;
AS_instrList F_procEntryExit2(AS_instrList body) {
    if (!returnSink) returnSink = Temp_TempList(ZERO, Temp_TempList(RA,
    Temp_TempList(SP, calleeSaves)));
    return AS_splice(body, AS_InstrList(AS_Oper("", NULL, returnSink, NULL), NULL));
}

AS_proc F_procEntryExit3(F_frame frame, AS_instrList body) {
    char buf[100];
    sprintf(buf,"PROCEDURE %s\n", S_name(frame->name));
    return AS_Proc(String(buf), body, "END\n");
}

translation helper

frame模块还需要包括机器相关的定义(7.2.2简单变量)

  • 接口

    image-20201204231650809
    • F_FP: 返回当前的fp

    • F_Exp:将一个F_access转换为一个Tree表达式(T_exp

      image-20201204232243336

      ​ const k是offset

      运行时是在translate中被调用的

      F_Exp(acc, T_Temp(F_FP()))

      传递 T_Temp(F_FP() 的原因是,必须要获得当前access所在的frame pointer,如果access是一个Reg,就简单的忽略fp

  • 调用运行时系统的函数 image-20201205141807398

    • Tiger没有对存储器进行管理的机制,所以需要调用外部c或汇编编写的函数

    • 实现:

      T_exp F_externalCall(string s, T_expList args) { 
          return T_Call(T_Name(Temp_namedlabel(s)), args); 
      }

      F_externalCall 包裹是因为可能涉及一些额外的处理,例如下划线

    • 调用:

      image-20201205142824838

fragment part

(7.3.3 fragment)

image-20201205132712877

image-20201205132842664

escape

image-20201201165655315

  • Esc_findEscape(A_exp exp)
    • 遍历语法树(和类型检查相似),判断每一个变量
    • Esc_findEscape需要在semantic分析之前
    • 每当发现变量或函数参数声明时,如A_VarDec(name=symbol("a"), escape=r, ...), 调用EscapeEntry
  • EscapeEntry(d, &(x->escape)): 引入一个新的表,在d深度绑定一个symbol。将x的escape设为false。在大于d的深度使用时,设为true

temp

​ 语义分析阶段还不知道变量具体要保存在哪里。所以用temp表示暂时保存在寄存器中的这些变量。用label表示其准确地址还需要确定。

image-20201201170429557

  • Temp_temp:临时变量,局部变量的抽象名,暂时保存在寄存器中的值。
  • Temp_label:标号,静态存储器地址的抽象名,机器语言中的位置。
  • Temp_newtemp() returns a new temporary from an infinite set of temps.
  • Temp_newlabel() returns a new label from an infinite set of labels.
  • Temp_namedlabel(string) returns a new label whose assembly-language name is string

image-20201215193550240

translate

Translate 模块为 semant 模块管理着局部变量和静态函数嵌套。

typedef struct Tr_access_ *Tr_access;
struct Tr_access_ {
	Tr_level level;
	F_access access;
};

typedef struct Tr_level_ *Tr_level;
struct Tr_level_ {
	F_frame frame;
	Tr_level parent;
    Tr_accessList formals; // frame翻译过来的
};

typedef struct Tr_accessList_ *Tr_accessList;
struct Tr_accessList_ {
	Tr_access head;
	Tr_accessList tail;	
};
  • Tr_access:比 F_access 多知道一个与静态链相关的信息 (Tr_level)
  • Tr_level:比 F_frame 多知道在第几层(父节点 Tr_level parent
Tr_accessList Tr_AccessList(Tr_access head, Tr_accessList tail);
Tr_level Tr_outermost(void); // 返回相同的值,代表包含main函数的层次
Tr_level Tr_newLevel(Tr_level parent, Temp_label name, U_boolList formals);
Tr_accessList Tr_formals(Tr_level level);
Tr_access Tr_allocLocal(Tr_level level, bool escape);
  • Tr_newLevel():创建新的嵌套层,调用 F_newFrame()
  • Tr_formals(): 调用F_formals(), 并转化为 Tr_access
  • Tr_allocLocal():在指定的层次中创建变量,调用 F_allocLocal()

管理静态链、追踪层次信息

image-20201201164027897

翻译中间代码

中间表示中的个体成分应该表示很简单的事,以方便用机器指令来表示。

tree

IR的数据结构T_exp和T_stm

typedef struct T_stm_ *T_stm;
typedef struct T_exp_ *T_exp;

translate

结构和辅助

  • 创建一种抽象类型(Tr_exp)模拟三种表达式:Ex, Nx, Cx

    • ExT_exp
    • NxT_stm
    • Cx:一个结构体
    typedef struct Tr_exp_ *Tr_exp;
    struct Tr_exp_ {
        enum {Tr_ex, Tr_nx, Tr_cx} kind;
        union {T_exp ex; T_stm nx; struct Cx cx; } u;
    };
    
    typedef struct Tr_expList_ *Tr_expList;
    struct Tr_expList_ {
        Tr_exp head;
        Tr_expList tail;
    };
    
    struct Cx {
    	patchList trues; patchList falses; 
    	T_stm stm; //真实的语句,比如 T_Cjump
    };
    • patchList :
     typedef struct patchList_ *patchList;
     struct patchList_ {Temp_label *head; patchList tail;};
     static patchList PatchList(Temp_label *head, patchList tail);
    • doPatch(patchList tList, Temp_label label) : 利用 label 回填标号回填表 patchList 中所有代填的标号。

      • joinPatch(patchList first, patchList second):
  • 还需要三种表达式之间相互转换:

    static T_exp unEx(Tr_exp e);
    static T_stm unNx(Tr_exp e);
    static struct Cx unCx(Tr_exp e);

    unEx 接受一个 Tr_exp 类型的参数,将其转化为 T_exp 类型

    unNx, unCx 同理,需要注意的是 unCx 应该拒绝来自 Nx 的转换

实际翻译

Translate模块还需要实现所有MEM节点的管理,这样Semant模块才不需要依赖中间语言Tree的表示(7.2.2简单变量)

  • 简单变量(7.2.2)

    Tr_Exp Tr_simpleVar(Tr_Access, Tr_Level);

    Semant模块中的逻辑为

    1. 在venv中查询到对应S_symbol的Tr_Access
    2. 当前的level就是x所在函数的level
    3. 传递x的access和所在函数的level给Tr_simpleVar
    Tr_exp Tr_simpleVar(Tr_access access, Tr_level level) {
      T_exp fp = T_Temp(F_FP()); // 当前的fp
      while (level && level != access->level) {
        F_access sl = F_formals(level->frame)->head;
        fp = F_Exp(sl, fp); // 不停地把fp替换成新的IR tree(MEM(fp,offset))
        level = level->parent;
      }
      return Tr_Ex(F_Exp(access->access, fp));
    }
  • 条件表达式

  • 字符串(7.2.10)

    • 引入fragment(见frame模块)

      image-20201205132943648
    • 对于每个字符串文字常数 lit,Translate 模块做两个操作:

      • 生成一个新的标号 lab,并返回这个标号的树中间表示 T_Name(lab)
        Temp_label l = Temp_newlabel();
        return Tr_Ex(T_Name(l));
    • 将片段 F_String(lab, lit) 放到一个全局表(fragList)中

      F_frag f = F_StringFrag(l, buf);
      fragList = F_FragList(f, fragList);
    • 注意:buf不只是字符,还需要告诉编译器长度,具体实现可以是: img
  • record和array的创建(7.2.11)

    • array
    image-20201205141725636
    • record

      Temp_temp r = Temp_newtemp();
      T_exp ex = T_Eseq(T_Seq(T_Move(T_Temp(r), 
      F_externalCall("malloc", T_ExpList(T_Const(n * F_wordSize), NULL))), 
      moveFields(r, n, fields)), T_Temp(r));
      return Tr_Ex(ex);
  • loops(7.2.12/13)

    • while

      image-20201205143418102
    • break: jump to done(label)

      Tr_exp Tr_break(Temp_label done) {
          return Tr_Nx(T_Jump(T_Name(done), Temp_LabelList(done, NULL)));
      }
    • for

      image-20201205144100951

基本块和轨迹

将任意一棵树转换成由 T_stm 组成的表的过程分为三步:

  1. 将树重写为不含 SEQ 和 ESEQ 节点的规范树:Linearize
  2. 将规范树分组组合成其内不含转移和标号的基本块集合:BasicBlocks
  3. 对基本块排序并形成一组轨迹,轨迹中每一个 CJUMP 之后都直接跟随它的 false 标号:traceSchedule

canon

image-20201216001309635

Linearize

T_stmList C_linearize(T_stm stm){
    return linear(do_stm(stm), NULL);
}
static T_stm do_stm(T_stm stm); //得到一个SEQ集中在前面的tree
static T_stm reorder(expRefList rlist); //重写,将ESEQ和CALL放到最前面

采用规则 $$ ESEQ(s1, ESEQ(s2, e)) = ESEQ(SEQ(s1,s2), e)\ BINOP(op, ESEQ(s, e1), e2) = ESEQ(s, BINOP(op, e1, e2))\ MEM(ESEQ(s, e1)) = ESEQ(s, MEM(e1))\ JUMP(ESEQ(s, e1)) = SEQ(s, JUMP(e1))\ CJUMP(op, ESEQ(s, e1), e2,l1,l2) = SEQ(s, CJUMP(op, e1, e2,l1,l2))\ BINOP(op, e1, ESEQ(s, e2)) = ESEQ(MOVE(TEMP t, e1),ESEQ(s, BINOP(op, TEMP t, e2)))\ CJUMP(op, e1, ESEQ(s, e2),l1,l2) = SEQ(MOVE(TEMP t, e1),SEQ(s, CJUMP(op, TEMP t, e2,l1,l2)))\ BINOP(op, e1, ESEQ(s, e2)) = ESEQ(s, BINOP(op, e1, e2))\ CJUMP(op, e1, ESEQ(s, e2),l1,l2) = SEQ(s, CJUMP(op, e1, e2,l1,l2)) $$ 采用规则$CALL(fun, args) → ESEQ(MOVE(TEMP\ t, CALL(fun, args)), TEMP\ t)$把CALL放到最前面

采用规则 $SEQ(SEQ(a, b), c) = SEQ(a,seq(b, c))$, 线性化(C_linearize)得到一个$SEQ(s1, SEQ(s2,..., SEQ(s_{n−1},s_n) . . .))$

BasicBlocks

traceSchedule

Basic的顺序可以任意选择,采用如下的算法:

image-20201216002617510

指令选择

将IR tree翻译成机器指令序列

assem

image-20201216153647621

image-20201216153436962image-20201216153520558

codgen

image-20201216150402605

  • 参数:

    • F_frame f:需要将帧指针替换为栈指针,为了知道frame label,frame size是在分配完成之后知道的,由F_procEntryExit3添加
    • T_stmList: 将简化后的IR tree传入
  • 在main中调用F_procEntryExit2在结尾添加活跃的寄存器

  • 具体工作:

    static AS_instrList iList=NULL, last=NULL;
    AS_instrList F_codegen(F_frame f, T_stmList stmList) {
      AS_instrList list;
      // 使用Maximal Munch算法实现IR树到Assem汇编指令的在转换
      for (T_stmList sl = stmList; sl; sl = sl->tail)
        munchStm(sl->head);
      // 返回汇编指令链表(AS_instrList)
      list = iList; iList = last = NULL;
      return list;
    }
  • 实现munchStm

    image-20201216153540466
    static void munchStm(T_stm s) {
      switch ( s )
        case EXP(CALL(e,args)):
     		Temp_temp r = munchExp(e);
            Temp_tempList l = munchArgs(0,args);
            emit(AS_Oper("CALL ‘s0\n", calldefs, L(r,l), NULL)); //将AS_Oper()这个instru加入全局iList中
    }

活跃分析

flowgraph

image-20201210171204624

  • FG_def(n): 指令n定义的变量列表
  • FG_use(n): 指令n使用过的变量列表
  • FG_isMove(n):判断是否是move指令,后续可以决定是否可以删除

liveness

image-20201210162806905

  • Live_graph:
    • G_graph graph: 冲突图
    • Live_moveList: 节点偶对组成的表

About

A compiler for tiger language, lab for book "modern compiler implement in C"


Languages

Language:C 92.0%Language:Yacc 3.7%Language:Lex 2.8%Language:C++ 1.6%