版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、编译器设计与实现,Lcc原理剖析,华中科技大学计算机学院 张 德,2020/12/19,一、概述,1、编译器各阶段,2020/12/19,2、编译器各阶段的分组,前端:依赖于语言并很大程度上独立于目标机器。一般包括语法分析、词法分析、符号表的建立、语义分析、中间代码生成以及相关错误处理。 后端:依赖于目标机器的阶段或某些阶段的某些部分。一般来说,后端完成的任务不依赖于源语言而只依赖于中间语言。主要包括代码优化、代码生成以及相关的错误处理和符号表操作,2020/12/19,二、符号表,符号表是编译器保存信息的中心库,编译器的各部分通过符号表进行交互,并访问符号表中的数据符号。 符号表把各种名字映
2、射到符号集合。常量、标识符和标号都是名字,不同名字有不同的属性。 符号管理不仅要处理符号本身,还管理符号的作用域,2020/12/19,1、符号的表示,struct symbol char *name; /名称 int scope;/作用域 Coordinate src;/在源程序中位置 Symbol up;/连接符号表中上一个符号 List uses;/可保存一个Coordinate列表,表示使用情况 int sclass;/扩展存储类型 /符号标记 Type type;/如变量、函数、常量、结构或联合等信息 floatref;/被引用的粗略次数 union /联合u为标号、结构、联合、枚举
3、、常量、全局 /和静态变量提供附加信息 u;/ Xsymbol x;/由后端处理,如为变量分配寄存器 /为调试器产生数据信息,2020/12/19,1、符号的表示,scope域: enum CONSTANTS=1, LABELS, GLOBAL, PARAM, LOCAL ; 第k层中声明的局部变量其scope域等于LOCAL+k。 src域: typedef struct coord char *file; unsigned x, y; Coordinate; file指名包含该符号定义文件名,y和x表示出现的行号及行中位置。 sclass域:符号扩展类型 可以是AUTO、REGISTER、
4、STATIC或EXTERN等 首字母大写的类型表示全小写类型的指针,如Symbol,2020/12/19,2、符号表的表示,externTableconstants; externTableexternals; externTableglobals; externTableidentifiers; externTablelabels; externTabletypes; struct table intlevel; /同symbol中scope域 Table previous;/符号表链表,指向level-1的表 struct entry struct symbol sym; struct e
5、ntry *link; *buckets256;/这是一个哈希链数组,方便插入、查找 Symbol all;/指向当前及其外层所有符号列表的表头,2020/12/19,3、符号表举例,int x, y; f(int x, int a) int b; y = x + a*b; if (y 5) int a; y = x + a*b;,2020/12/19,2020/12/19,4、符号表的相关操作,查找和建立标识符 Symbol install(const char * name, Table * tpp, int level, int arena); Symbol lookup(const c
6、har *name, Table tp); 标号:与标识符相似,但不涉及作用域 常量:这些符号保存在constants表中 产生变量:用于产生静态变量保存字符串等,2020/12/19,三、代码生成接口,这一章内容定义了与目标机器无关的前端和与目标机器相关的后端之间的接口。 Lcc接口包括一些共享数据结构、18个函数和包括36个操作符的语言。该语言用于将可执行代码从源程序生成dag(有向无环图)。 共享数据结构可供前后端共享,但某些域为一端私有。symbol就是一个共享数据结构,2020/12/19,1、类型度量,typedef struct metrics unsigned char siz
7、e, align, outofline; Metrics; size:类型的大小; align:对齐字节数; outofline:控制相关类型的常量的放置。为1时,不出现在dag中,存于静态变量中。 Metrics charmetric; Metrics shortmetric; Metrics intmetric; Metrics floatmetric; Metrics doublemetric; Metrics structmetric,2020/12/19,2、接口记录,typedef struct interface Xinterfacex; Interface; lcc为每一种目标
8、机器形成一个独有的接口实例。x域是对interface的扩展,后段使用它存放与目标及其相关的接口数据和函数,对后端私有,2020/12/19,3、dag操作,可执行代码用dag来描述。函数体是用dag组成的序列或森林。每个dag都可以同过gen函数传给后端。 dag节点 struct node short op; short count; Symbol syms3; Node kids2; Node link; Xnode x;,2020/12/19,3、dag操作,op域存放dag操作符。 dag操作符后缀表示操作数类型: enum F=FLOAT, I=INT, U=UNSIGNED, P
9、=POINTER, V=VOID, B=STRUCT ; 如CNST,有变体CNSTI、CNSTU、CNSTP等。 CNST = 14; CNSTC=CNST+F; CNSTI=CNST+I;,2020/12/19,2020/12/19,2020/12/19,3、dag操作,举例: int i, *p; f() i = *p,2020/12/19,4、接口标志,unsigned little_endian:1; 目标机器存储是低位优先还是高位优先 unsigned mulops_calls:1; 有硬件实现乘、除和求余,mulopes_calls应等于0 unsigned wants_call
10、b:1; 通知前端产生CALLB节点以调用返回结构的函数 unsigned wants_argb:1; 通知前端节点产生ARGB节点以产生结构参数 unsigned left_to_right:1; 告诉前端按照从左到右的顺序计算和提交参数给后端 unsigned wants_dag:1; 告诉前端传递dag给后端,2020/12/19,5、函数,前端将函数编译为私有数据结构。将函数的任意部分传递给后端之前,前端必须先对每个函数进行完整的分析。 函数的处理:function函数包括前端过程gencode遍历前端的私有数据结构,将dag的每个森林传给后端过程gen。gen选择代码,在dag上添加
11、注释并将返回一个dag指针。gencode还可以调用local宣告新的局不变量。前端过程emitcode再次遍历,将gen返回的指针传递给emit函数发送代码,2020/12/19,6、上行调用,前段调用后端以执行代码生成和发送。后端调用前端完成输出、分配存储空间、查询类型等功能。上行调用即后端调用前端。 allocate分配空间,保证对齐方式符合机器多 数类型 newnode分配新的dag节点 newconst符号表中创建新的常量 newtemp符号表中创建新的变量,2020/12/19,四、词法分析,词法分析器读入源程序,产生语言的基本词法单元。 例:*prt 56,2020/12/19,
12、1、输入,2020/12/19,n,cp,limit,当limit-cp小于某一个固定值时,调用fillbuf函数填充buffer,2、单词识别,部分文法: token: keyword identifier constant operator punctuator punctuator: one of ( ) * , : = ;,2020/12/19,定义: ID 标识符 FCON 浮点常量 ICON 整型常量 SCON INCR DECR DEREF,emun #define xx(a,b,c,d,e,f,g) a=b, #define yy(a,b,c,d,e,f,g) #include
13、 “token.h” LAST token.h文件: yy(0, 0, 0, 0, 0, 0, 0) xx(FLOAT, 1, 0, 0, 0, CHAR, float) xx(DOUBLE, 2, 0, 0, 0, CHAR, double) xx(CHAR, 3, 0, 0, 0, CHAR, char) xx(SHORT, 4, 0, 0, 0, CHAR, short) xx(INT, 5, 0, 0, 0, CHAR, int) xx(UNSIGNED, 6, 0, 0, 0, CHAR, unsigned) xx(POINTER, 7, 0, 0, 0, 0, pointer) x
14、x(VOID, 8, 0, 0, 0, CHAR, void) xx(STRUCT, 9, 0, 0, 0, CHAR, struct),2020/12/19,3、关键字的识别,可以通过查表完成,也可以通过硬编码方式识别。 例如,当起始小写字母为i时由gettok函数中switch语句的case i处理,2020/12/19,case i: if (rcp0 = f,4、标识符识别,case h: case j: case k: case m: case n: case o: case p: case q: case x: case y: case z: case A: case B: cas
15、e C: case D: case E: case F: case G: case H: case I: case J: case K: case M: case N: case O: case P: case Q: case R: case S: case T: case U: case V: case W: case X: case Y: case Z: id: if (limit - rcp MAXLINE) cp = rcp - 1; fillbuf(); rcp = +cp;,2020/12/19,assert(cp = rcp); token = (char *)rcp - 1;
16、while (map*rcp,检查是否需要填充buffer,5、其他,另外还有: 数字识别 字符常量和字符串识别 都是有gettok函数实现,实现方法相似。 词法分析器可以有象LEX这样的工具实现。Lcc手工实现了词法分析器,体积更小,2020/12/19,五、语法分析,根据语言的文法分析,以确认输入是否符合语言规则,并建立输入源程序的内部表示。 Lcc采用递归下降的语法分析。 语法分析以形式语言理论为基础,采取语法制导翻译方法,处理程序中的错误,2020/12/19,1、表达式,表达式的表示: (a+b)+b*(a+b,2020/12/19,表达式的分析: c语言的小部分表达式语法: exp
17、r: term+term term: factor *factor factor: ID| ( expr ) T(expr) T(term+term) T(term)T(+term) term();T(+term) term();while(t = +) T(+term) term();while(t = +) T(+)T(term) term();while(t = +) t = gettok();T(term) term();while(t = +) t = gettok(); term() 同理得分析函数term是:factor();while(t = *) t = gettok();
18、factor(,2020/12/19,void factor() if(t=ID) t=gettok(); else if (t = () t=gettok(); expr(); expect() ;,c语言表达式分析 赋值表达式: assignment-expression: conditional-expression unary-expression assign-operator assignment-expression Tree expr1(int tok) static char stop = IF, ID, 0 ; Tree p = expr2(); if (t = = | (
19、prect = 6 else return p,2020/12/19,条件表达式: conditonal-expression: binary-expression? expression : conditional-expression static Tree expr2(void) Tree p = expr3(4); if (t = ?) Tree l, r; Coordinate pts2; if (Aflag 1,2020/12/19,另有二元表达式、一元表达式、后缀表达式和基本表达式。 表达式分析多是用递归和大量switch语句实现。 在编译领域用一个分析函数代替n个函数处理n级优
20、先是非常流行的。 关于表达式的分析还包括表达式语义的分析,如类型检查转换、函数调用分析等各种操作,2020/12/19,2、语句分析,代码的表示:表达式首先被编译为分析树然后转化为dag。每个函数的dag在代码表中被串起来,代码表表示了函数的代码。 code结构: struct code enum Blockbeg, Blockend, Local, Address, Defpoint, Label, Start, Gen, Jump, Switch kind; Code prev, next; union u;,2020/12/19,语句的识别: void statement(int loo
21、p, Swtch swp, int lev) float ref = refinc; if (Aflag = 2,2020/12/19,if语句的识别: if expression = 0 goto L statement1 goto L+1 L:statement2 L1: static void ifstmt(int lab, int loop, Swtch swp, int lev) t = gettok(); expect();/判断if后的( definept(NULL); walk(conditional(), 0, lab); /包含listnode函数生成dag并加入 refi
22、nc /= 2.0; /森林,把入口加入代码表.同时根 statement(loop, swp, lev);/据接过设置flab,tlab if (t = ELSE) branch(lab + 1); t = gettok(); definelab(lab); statement(loop, swp, lev); if (findlabel(lab + 1)-ref) definelab(lab + 1); else definelab(lab);,2020/12/19,在循环、switch、goto语句中都用到了标号 和跳转,标号使通过definelab函数定义的, 而跳转通过branch函
23、数生成。 除语句识别外,还有声明的识别。声明的识别非常复杂,c语言中声明的形式很多,处理时大量的相互递归调用。 经过前端的分析后,将源程序转化为dag,并添加进代码表,2020/12/19,3、小结,六、中间代码生成,编译器的后端通过function接口函数调用gencode和emitcode来遍历代码表。 walk和listnodes函数操作处理dag森林。 newnode函数为节点分配内存并用它的参数只来初始化节点的域。 listnode还负责删除公共子表达式,2020/12/19,1、构建节点,Node listnodes(Tree tp, int tlab, int flab) Nod
24、e p = NULL, l, r; int op; if (tp = NULL) return NULL; if (tp-node) /node标识listnode访问过的树 return tp-node; if (isarray(tp-type) op = tp-op + sizeop(voidptype-size); else op = tp-op + sizeop(tp-type-size); switch (generic(tp-op) tpnode p; return p;,2020/12/19,2、控制流,最简单的一元和二元操作加入结点表,但是并不会出现在根中。赋值等操作可以用这种
25、情况解决。 要改变控制流需要跳转。 case JUMP: l = listnodes(tp-kids0, 0, 0); list(newnode(JUMP+V, l, NULL, NULL); reset(); break,2020/12/19,static void list(Node p) if (p,2020/12/19,forest是一个循环链表,不为空则指向链表最后一个节点,为空则将其初始化,link域可以表示根结点,case LT: /LT代表大于转移,是接口dag标识符 l = listnodes(tp-kids0, 0, 0); r = listnodes(tp-kids1,
26、0, 0); if (tlab) list(newnode(generic(tp-op) + opkind(l-op), l, r, findlabel(tlab); else if (flab) switch (generic(tp-op) case EQ: op = NE; break;case NE: op = EQ; break; case GT: op = LE; break; case LT: op = GE; break; case GE: op = LT; break; case LE: op = GT; break; default: assert(0); list(newn
27、ode(op + opkind(l-op), l, r, findlabel(flab); if (forest,2020/12/19,2020/12/19,ai 标记与指令对应的模板,以区别子指令(如寻址指令): static char *_isinstruction,2020/12/19,lburg从1开始为规则编号,并通过返回规则号来报告匹配情况,这样当需要的时候就可以找到响应的模板。如果模板以一个换行符为结尾表示它是一条指令,否则就必然是某条指令的一部分,比如是一个操作数。 rc: reg %0 rc: con %0 reg: ADDI4(reg,mrc1) ?mov %c,%0nad
28、d %c,%1n 1 reg: ADDP4(reg,mrc1) ?mov %c,%0nadd %c,%1n 1,2020/12/19,emitasm对规则结构及汇编程序代码模板进行了解释。 emitasm递归调用自身,以处理地址计算之类的子指令。 emitasm的遍历从一个指令开始,当递归到达为该指令提供值的指令时结束。 emitasm由emit调用,emit确保emitasm以正确的顺序来处理这些指令,这样便可以处理指令间的顺序,2020/12/19,例如:发送器解释字符串“lw r%c,%1n” 先生成“lw r”,然后是目标寄存器的名字(通常是一个数字),接着是一个逗号。如果nts1中保
29、存了表示非终结符addr的整数,那么递归生成p-kids1作为一个addr。最后emitasm生成一个换行符,2020/12/19,3、寄存器的分配,从上节我们可以看出,代码发送器可以生成汇编代码,但是汇编代码中的寄存器是如何分配的? 寄存器分配包括两个内容: 分配:决定哪些值占用寄存器 指派:为每个值指派特定的寄存器,2020/12/19,2020/12/19,在后端选择指令并将子指令从树中分离出来,linearize采用前序遍历分离的树,并按照最后执行的顺序链接指令。gen再将每条指令传递给ralloc函数,ralloc一般首先调用putreg来释放不再被其子节点使用的寄存器,然后调用getreg函数为自身分配一个寄存器。对于临时变量,ralloc在首次赋值的时候为它分配一个寄存器,在最后一次使用的时候释放该机存器,2020/12/19,寄存器状态的跟踪 unsigned freemask2; unsigned usedmask2; 用掩码表示寄存器的状态 对于寄存器r: int n = r
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 护理质量考核方案及控制标准
- 2025年江西陶瓷工艺美术职业技术学院单招职业倾向性考试题库带答案解析
- 2025年武鸣县幼儿园教师招教考试备考题库附答案解析(夺冠)
- 2025年施秉县招教考试备考题库及答案解析(夺冠)
- 2025年雅江县招教考试备考题库附答案解析(必刷)
- 2025年江苏财会职业学院单招职业适应性考试题库带答案解析
- 2024年马山县招教考试备考题库及答案解析(必刷)
- 2025年重庆航天职业技术学院马克思主义基本原理概论期末考试模拟题附答案解析(夺冠)
- 2025年宣化科技职业学院单招综合素质考试题库附答案解析
- 2024年长江职业学院马克思主义基本原理概论期末考试题及答案解析(夺冠)
- 加工覆膜合同范例
- 湖北省荆州市八县2024-2025学年高一上学期期末联考英语试题(无答案)
- 《新疆工程勘察设计计费导则(工程勘察部分)》
- 字母认主协议书(2篇)
- 骨科研究生年终总结
- (完整)七年级生物上册思维导图
- HG20202-2014 脱脂工程施工及验收规范
- DL∕T 1573-2016 电力电缆分布式光纤测温系统技术规范
- 20G520-1-2钢吊车梁(6m-9m)2020年合订本
- 电梯维护保养规则(TSG T5002-2017)
- PLC控制的抢答器设计与仿真
评论
0/150
提交评论