编译原理 之 代码生成【VIP专享】
编译原理实验四 代码生成

编译原理实验四代码生成输入:语法树。
输出:生成P代码存入codestr[codeindex]。
样例程序已经能生成P代码。
文法:stmt_seq -->statement ; stmt_seq | statementstatement-->decl_stmt | assign_stmtdecl_stmt-->type var_listtype-->int |floatvar_list-->id , var_list | idassign_stmt--> id := expexp-->exp + term | exp - term |termterm--> term * factor | term * factor | factorfactor-->id | num | ( exp )要求掌握理解程序设计方法。
#include<stdio.h>#include<ctype.h>typedef enum{MINUS,PLUS,TIMES,OVER,LPAREN,RPAREN,SEMI,ASSIGN,NUM,ID,INT,FLOAT,COMMA,DOLLAR} tokentype;/*记号*/typedef enum {stmtk,expk} nodekind;typedef enum {ifk,assignk,declk} stmtkind;typedef enum {opk,constk,idk} expkind;typedef enum {integer,real} exptype;typedef struct treenode{ struct treenode * child[3];struct treenode * sibling;nodekind nodek;exptype dtype ;union { stmtkind stmt; expkind exp;} kind;union { tokentype op;int val;char * name; } attr;} treenode;typedef struct bucket{char * name; exptype dtype; struct bucket * next;} bucket;bucket * hashtable[211];tokentype token[]={ID,ASSIGN,NUM,PLUS,NUM,TIMES,ID,SEMI,ID,ASSIGN,NUM,DOLLAR};char tokenstring[][30]={"ab",":=","12","+","5","*","x",";","xy",":=","34","$"}; int wordindex=0; /*以上两个数组的索引*/char codestr[30][30];int codeindex=0;treenode * t;treenode * decl();treenode * factor();treenode * term();treenode * exp();treenode * assign_stmt();treenode * stmt_seq();pretraverse(treenode *);expcode(treenode *);gencode(treenode *);main(){int i;t=stmt_seq();pretraverse(t);gencode(t);for(i=0; i<codeindex;i++)printf("%s\n",codestr[i]);}treenode * stmt_seq(){treenode * t;treenode * p;if(( token[wordindex]==INT)||(token[wordindex]==FLOAT)){t=decl(); }if(token[wordindex]==ID) t=assign_stmt();p=t;while( (token[wordindex]==SEMI)&& (token[wordindex]!=DOLLAR)) {treenode * q;wordindex++;q=assign_stmt();p->sibling=q;p=q;}return t;}treenode * assign_stmt(){treenode * t=(treenode *)malloc(sizeof(treenode));if(token[wordindex]==ID){t->nodek=stmtk;t->kind.stmt=assignk;t->=tokenstring[wordindex];wordindex++;}else {printf("error");exit(1);}if(token[wordindex]==ASSIGN)wordindex++;else {printf("error");exit(1);}t->child[0]=exp();t->child[1]=NULL;t->child[2]=NULL;t->sibling=NULL;return t;}treenode * exp(){treenode * t;t=term();while((token[wordindex]==PLUS)||(token[wordindex]==MINUS)) {treenode * p=(treenode *)malloc(sizeof(treenode));p->nodek=expk;p->kind.exp=opk;p->attr.op=token[wordindex];p->child[0]=t;t=p;wordindex++;t->child[1]=term();t->child[2]=NULL;t->sibling=NULL;}return t;}treenode * term(){treenode * t=factor();while((token[wordindex]==TIMES)||(token[wordindex]==OVER)) {treenode * p=(treenode *)malloc(sizeof(treenode));p->nodek=expk;p->kind.exp=opk;p->attr.op=token[wordindex];p->child[0]=t;t=p;wordindex++;t->child[1]=factor();t->child[2]=NULL;t->sibling=NULL;}return t;}treenode * factor(){treenode * t;switch(token[wordindex]){case LPAREN :wordindex++;t=exp();if(token[wordindex]==RPAREN)wordindex++;else {printf("error");exit(1);}break;case NUM :t=(treenode *)malloc(sizeof(treenode));t->nodek=expk;t->kind.exp=constk;t->attr.val=atoi(tokenstring[wordindex]);t->child[0]=NULL;t->child[1]=NULL;t->child[2]=NULL;t->sibling=NULL;wordindex++;break;case ID :t=(treenode *)malloc(sizeof(treenode));t->nodek=expk;t->kind.exp=idk;t->=tokenstring[wordindex];wordindex++;break;default:printf("error");}return t;}pretraverse(treenode * t){if (t!=NULL){ if (t->nodek==stmtk) printf("stmt-id:%s\n",t->);if (t->nodek==expk && t->kind.exp==idk)printf("exp-id:%s\n",t->);if (t->nodek==expk && t->kind.exp==opk) printf("exp-op:%d\n",t->attr.op);if (t->nodek==expk && t->kind.exp==constk)printf("exp-val:%d\n",t->attr.val);if(t->child[0]!=NULL) pretraverse(t->child[0]);if(t->child[1]!=NULL) pretraverse(t->child[1]);if(t->child[2]!=NULL) pretraverse(t->child[2]);if(t->sibling!=NULL) pretraverse(t->sibling);}}treenode * decl(){treenode * p,* q,* t1;treenode * t=(treenode *)malloc(sizeof(treenode));t->nodek=stmtk;t->kind.stmt=declk;t->=tokenstring[wordindex];t->child[1]=NULL;t->child[2]=NULL;wordindex++;if(token[wordindex]==ID){t1=(treenode *)malloc(sizeof(treenode));t->child[0]=t1;t1->nodek= expk;t1->kind.exp=idk;t1->=tokenstring[wordindex];t1->child[0]=NULL;t1->child[1]=NULL;t1->child[2]=NULL;t1->sibling=NULL;wordindex++;p=t1;while( token[wordindex]==COMMA){ wordindex++;q=(treenode *)malloc(sizeof(treenode));q->nodek= expk;q->kind.exp=idk;q->=tokenstring[wordindex];q->child[0]=NULL;q->child[1]=NULL;q->child[2]=NULL;q->sibling=NULL;p->sibling=q;wordindex++;p=q;}return t;}}expcode(treenode * t){char * s1, * s2;if(t->child[0]!=NULL) expcode(t->child[0]);if(t->child[1]!=NULL) expcode(t->child[1]);if(t->nodek==expk && t->kind.exp==opk){if (t->attr.op==PLUS) strcpy(codestr[codeindex++],"adi"); if (t->attr.op==MINUS) strcpy(codestr[codeindex++],"sbi"); if (t->attr.op==TIMES) strcpy(codestr[codeindex++],"mpi"); if (t->attr.op==OVER) strcpy(codestr[codeindex++],"ovi");}if(t->nodek==expk && t->kind.exp==idk)strcpy(codestr[codeindex++],strcat("lod ",t->)); if(t->nodek==expk && t->kind.exp==constk){strcpy(s1,"ldc ");itoa(t->attr.val, s2,10);strcat(s1,s2);strcpy(codestr[codeindex++],s1);}}gencode(treenode * t){char * s;if(t->nodek==stmtk && t->kind.stmt==assignk){ strcpy(s,"lda ");strcat(s,t->);strcpy(codestr[codeindex++],s);expcode(t->child[0]);strcpy(s,"sto");strcpy(codestr[codeindex++],s);if(t->sibling!=NULL) gencode(t->sibling);}}_。
编译原理课件-第9章代码生成

● example
例1 请写出C function definition. 三地址码。 int f ( int x, int y ) { return x + y + 1; }
解:This will translate into the following three-address code:
entry f t1 = x + y t2 = t1 + 1 return t2
8.9 A Survey of Code Optimizations Techniques 代码优化技术考察
8.10 Simple Optimizations for the TINY Code Generator TINY代 码生成器的简单优化
8.1 Intermediate Code and Data Structures for Code Generation 中间代码和用于代码生成的数据结构
t2=fact*x
label L1
fact=t2
halt
解:Quadruple implementation for the three-address code
(rd, x , _ , _ ) (gt, x, 0, t1 ) (if_f, t1, L1, _ ) (asn, 1,fact, _ ) (lab, L2, _ , _ ) (mul, fact, x, t2 ) (asn, t2, fact, _ )
8.6 Code Generation in Commercial Compilers: Two Case Studies 商用编译器 中的代码生成:两个案例研究
8.7 TM: A Simple Target Machine TM:简单的目标机器 8.8 Code Generation for the Tiny Language TINY语言的代码生成器
8编译原理之讲义代码生成

• RISC、CISC; • 可重定向代码、汇编语言
代码生成器设计中的问题
指令选择
•代码生成器将中间表示形式映射为目标
机代码
•映射的复杂性由下列因素决定:
• IR的层次
• 高:用代码模板翻译,但代码质量不高,需优 化
• 低:利用低层次细节生成更高效的代码
• 指令集体系结构本身的特性 • 期望的目标代码质量
方便记忆)
常量#constant56
例子
x=y-z
• LD R1, y
//R1=y
• LD R2, z
//R2=x
• SUB R1, R1, R2
//R1=R1-R2
• ST x, R1
//x=R1
b=a[i]
• LR R1, i
//R1=i
• MUL R1, R1, 8
//R1=R1*8
• LD R2, a(R1)
8.1代码生成器设计中的问题
设计目标:
• 生成代码的正确性(最重要) • 易于实现、测试和维护
输入 输出 指令选择 寄存器分配 计算顺序
代码生成器设计中的问题
输入
• 前端生成的源代码的IR(中间表示形式)及符号表信息 • 中间表示形式的选择
• 四元式、三元式、字节代码、堆栈机代码、后缀表示、抽象 语法树、DAG图、…
//R2=contents(a+contents(R1))
• ST b, R2
//b = R2
程序及指令代价
不同的目的有不同的度量
• 最短编译时间、目标程序大小、运行时间、能耗
不可判定一个目标程序是否最优 指令代价=指令固定代价(设为1)+运算分量寻
址模式代价,例:
编译原理chapter8 代码生成

精品文档
11
MOV top, sp
ADD #caller.recordsize,top 返回序列包括两个部分: GOTO *0(SP)/*返回到调用过程*/ MOV sp, top SUB #caller.recordsize, SP 8.2.3 名字的运行地址 x:=0 静态分配区域的开始地址是static, x的相对地址12,则x的实际地 址应为static+12。 static [12]:=0
算法8.1 划分三地址程序形成基本块
输入:一个三地址语句序列。 输出:一个基本块表,其中每一条三地址 语句仅在一个块中。 方法:
ADD #caller. recordsize,SP/*将调用过程
的活动记录的长度加入到SP中*/ MOV #here+16,*SP/*存储返回地址*/ GOTO callee.code-area/*转移到被调用过
程的代码的第一条指令*/ 属性caller.recordsize代表一个活动记录的大小。
精品文档
12
如果静态区域是从地址100开始,则上述语 句的目标代码为: MOV #0,112
栈式分配,用一个display表存取非局部名
字,又假定该display表是存放在一些寄存器 中,并且x是局部于一个活动记录的变量,该 活动记录的display表指针在寄存器R3中,那 么,x:=0翻译成为如下三地址语句; t1:=12+R3 *t1:=0 其中t1中存放的是x的地址。
*/ action1 call p action2 halt /*p的代码 */ action3 return
S的活动记录 p的活动记录 返回地址 返回地址
arr
buf
i
n
j
精品文档
编译原理之中间代码生产、词法优化与代码生成

编译原理之中间代码⽣产、词法优化与代码⽣成
中间代码⽣成
在把⼀个源程序翻译成⽬标代码的过程中,⼀个编译器可能构造出⼀个或多个中间表⽰。
这些中间表⽰可以有多种形式。
语法树是⼀种中间表⽰形式,它们通常在语法分析和语义分析中使⽤。
在源程序的语法分析和语义分析完成之后,很多编译器⽣成⼀个明确的低级的或类机器语⾔的中间表⽰。
我们可以把这个表⽰看作是某个抽象机器的程序。
该中间表⽰应该具有两个重要的性质:它应该易于⽣成,且能够被轻松地翻译为⽬标机器上的语⾔。
代码优化
机器⽆关的代码优化步骤试图改进中间代码,以便⽣成更好的⽬标代码。
“更好”通常意味着更快,但是也可能会有其他⽬标,如更短的或能耗更低的⽬标代码
代码⽣成
代码⽣成器以源程序的中间表⽰形式作为输⼊,并把它映射到⽬标语⾔。
如果⽬标语⾔是机器代码,那么就必须为程序使⽤的每个变量选择寄存器或内存位置。
然后,中间指令被翻译成为能够完成相同任务的机器指令序列。
代码⽣成的⼀个⾄关重要的⽅⾯是合理分配寄存器以存放变量的值。
(这⾥我觉得还存疑因为书本上下下段还说了⼀句话,上⾯对代码⽣成的讨论忽略了对源程序中的标识⾏进⾏存储分配的重要问题。
) 不过总之,运⾏时刻的存储组织⽅法依赖于被编译的语⾔。
编译器在中间代码⽣成或代码⽣成阶段做出有关存储分配的决定。
编译原理课件-中间代码生成

(3) E→not E1 { E.true:= E 1.false; E.Codebegin:= E 1.codebegin; E.false:= E 1.true
(翻譯不是最優)
語句 if a<b or c<d and e<f then S1 else S2 的四元式
(1) if a<b goto (7) //轉移至(E.true )
(2) goto (3)
(3) if c<d goto (5)
(4) goto (p+1)
//轉移至(E.false)
(5) if e<f goto (7) (6) goto (p+1) (7)( S1的四元式
不同層次的中間代碼
源語言
中間代碼
(高級語言) (高級)
中間代碼 (中級)
中間代碼 (低級)
float a[10][20]; a[i][j+2];
t1 = a[i, j+2]
t1 = j + 2 t2 = i * 20 t3 = t1 + t2 t4 = 4 * t3 t5 = addr a t6 = t5 + t4 t7 = *t6
語義描述使用的變數和過程:
E.true : “真”鏈, E.false : “假”鏈
E.codebegin : E 的第一個四元式
Nextstat: 下一四元式地址
2021-2022学年编译原理之目标代码生成(2)

⑴(> a(y) b(y) t1(y)) ①LD R,a ②GT R,b
⑵(if t1(n) _ _ ) ③FJ R,? ⑨
RDL SEM t1
③
⑶(+ a(y) b(y) t2(y)) ④LD R,a ⑤ADD R,b t2
⑷(* t2(n) c(y) x(y)) ⑥MUL R,c
x
⑸(el _ _ _ ) ⑦ST R,x ⑧JMP_,? 15
【习题9.5】简要叙述代码生成器(控制器)的过程; 【习题9.6】已知下列语句:
if(a+b<c) x=(a+b)/(c-d)+(a+b); ※ 试分别解答:
⑴ 写出优化的四元式序列; ⑵ 标记变量的活跃信息; ⑶ 描述单寄存器R下的目标代码生成过程。
※ 条件语句的四元式结构:
设 条件语句: if(E)S1 ; else S2 ;
⑹(+ t4(n)t5(n) x(y))
注
…
需要随时观察寄存器状态 !
①LD R,a ②ADD R,b
③ST R,t1 ④LD R,c ⑤SUB R,d
⑥MUL R,t1 ⑦ ST R,t3 ⑧LD R,a ⑨SUB R,t3 ⑩ST R,t
11 LD R,t1 12 DIV R,2 13 ADD R,t4
译
⑹(* a(y) b(y) t3(y))
过
⑺(- 5 t3(n) x(y))
程
⑻(ie _ _ _ ) 注 需要及…时处理跳转地址返填 !
①LD R,a ②GT R,b
待返 填1
③FJ R,⑨? ④LD R,a ⑤ADD R,b
⑥MUL R,c ⑦ST R,x ⑧JMP_,1?5 ⑨ LD R,a ⑩MUL R,b
编译原理与代码生成

编译原理与代码生成在计算机科学领域中,编译原理是一门研究编程语言如何被转换成可执行代码的学科。
它涉及到编译器的设计和开发,其中一个关键环节就是代码生成。
代码生成是编译过程中的最后一步,它将中间表示形式(如抽象语法树或中间代码)转化为机器代码或目标代码,以便计算机能够直接执行。
代码生成是编译过程中至关重要的一环,其质量直接影响到最终生成的可执行程序的效率和性能。
一个好的代码生成器应该能够生成高效、优化的机器代码,并且具备可移植性。
为了实现这一目标,代码生成器通常需要考虑以下几个方面:1. 寄存器分配:寄存器是现代计算机体系结构中重要的资源之一。
在代码生成阶段,寄存器的分配是一个关键问题。
代码生成器需要决定哪些变量应该存储在寄存器中,哪些应该存储在内存中,并生成相应的指令来进行寄存器的分配和管理。
2. 内存分配:除了寄存器分配外,代码生成器还需要考虑如何进行内存分配。
它需要决定哪些变量应该存储在堆栈上,哪些应该存储在静态数据区,以及如何有效地进行内存的分配和释放。
3. 代码优化:代码生成器还需要考虑一系列的代码优化技术,以提高生成代码的质量和效率。
这些技术包括常见的优化方法,如常量折叠、公共子表达式消除、死代码删除等。
通过应用这些优化技术,代码生成器可以生成更紧凑、更高效的机器代码。
4. 目标代码生成:最后,代码生成器需要将中间表示形式翻译成目标机器能够执行的机器代码。
这一过程中,代码生成器需要根据目标机器的指令集架构和特性来生成相应的机器代码。
同时,为了保证生成的代码的可移植性,代码生成器还需要考虑各种编译器选项和标准。
综上所述,编译原理中的代码生成是编译过程中不可或缺的一部分。
通过合理的寄存器分配、内存分配、代码优化和目标代码生成,代码生成器可以生成高质量、高效率的机器代码。
这对于计算机科学学习者和编译器开发者来说,都是一个重要的课题。
只有深入理解编译原理和代码生成的原理和技术,才能够设计和实现出优秀的编译器和程序。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
1. 代码生成程序的输入
• 中间代码. 线性表示法(后缀 式)、三地址(四元式)等.
• 符号表中的信息. 代码生成根据 符号表中的信息来决定中间代码 中的名字所指示的数据对象的运 行时地址.
• 有时需要作类型检查.
2. 指令选择
✓ 指令选择--寻找一个合适的目标机 指令序列以实现给定的中间表示.
ADD Rj,Ri 其开销为1,结果在Ri中. ② b存放在Ri中,而c在一个存储单元里,并假
定b不再活跃。 ADD c,Ri 其开销为2 ③ 或者 MOV c,Rj ADD Rj,Ri 其开销为3
4. 指令调度•Biblioteka 指令调度——确定程序指令的执行顺序
• 对具有流水线限制的体系结构,这个阶段 是必须的。如:RISC体系结构一个通用的 流水线限制为:从内存中取入寄存器中的 值在随后的某几个周期中是不能用的。在 这几个周期期间,调出不依赖于该取入值 的指令来执行是很重要的。
代码生成器
目标程序目标程序
符号表
图12.1 代码生成器在编译系统中的位置
代码生成器的输入 ➢中间代码 ➢符号表中的信息
目标代码一般有三种形式:
1. 能够立即执行的机器语言代码,所 有地址均已定位,通常位于固定的 存储区域中,编译后可直接运行.
2. 待装配的机器语言模块。当需要执 行时,由连接装入程序把它们和某
✓ 如果目标机不能支持指令集的所有 类型,那么对每一种例外要做特别 处理.
✓ 多数CPU的指令集合具有冗余性, 也即,同一计算可用两个或多个不 同的指令序列完成。指令选择器选 择其中之一以产生最好的代码.
例:“加1”的两种代码生成方 式
如:中间代码 a:=a+1: 实现1:INC a
实现2:LD R0,a ADD R0, #1 ST R0,a
第十二章 代码生成
• 代码生成概述 • 简单代码生成程序
➢一个假想的计算机模型 ➢简单的代码生成算法
• 代码生成器的自动生成技术
12.1 代码生成概述
• 将经过语法分析或优化后的中间代码, 转 换成特定目标机器的机器语言或汇编语言, 这样的转换程序称为代码生成程序.
• 目标代码与具体的计算机体系结构、指令 格式、字长、寄存器的个数和种类等,并 和指令的语义、操作系统、存储管理相关, 增大了代码生成程序的复杂性.
指令选择的基本原则
• 减小产生代码的尺寸 • 减小目标代码的执行时间 • 降低目标代码的能耗
3. 寄存器分配
• 通常情况下,指令在寄存器中访问操 作数的开销要比在内存中访问小。
• 许多指令不能直接访问内存。如果操 作数在内存中,需要显式地取入到寄 存器中。
• 将经常使用的操作数保存在寄存器中 是比较有利的。
些运行程序连接起来,转换成能执 行的机器语言代码.
3. 汇编语言代码。尚需经过汇编程序
汇编,转换成可执行的机器语言代
码.
返回
12.1.2 代码生成要考虑的基本问题
✓目标代码与具体的目标机结构、 指令系统、操作系统有关.
✓代码生成要考虑存储管理、寄存 器分配等方面的问题.
✓代码生成要使生成的目标代码较 短
• 必须找一个指令(与被取值无关)在取指 令之后立即执行,如果找不到相应的指令, 这些周期就会被浪费。
指令选择,寄存器分配,指令调度间的关系
• 在代码生成过程中,这三者的关系非常 密切。在进行寄存器分配和指令调度之 时,假定指令选择已经完成;
• 若先进行调度,寄存器趋向于过度分配; 若先进行寄存器分配,对于给定的寄存 器分配,可供调度的指令可能太少,可 能不包含任何好的调度。
寄存器分配原则
① 生成某变量的目标代码时,尽量让变 量的值或计算结果保留在寄存器中直 到寄存器不够分配为止。这样,访问 变量值时可减少对内存的存取次数, 以提高运行速度;
② 在同一基本块内后边不再被引用的变 量所占用的寄存器应尽早释放,以提 高寄存器的利用率;
寄存器分配原则
③ 当到基本块出口时,将变量的值存 放在内存中,因为一个基本块可能 有多个后继结点或多个前驱结点, 同一变量名在不同前驱结点的基本 块内出口前存放的R可能不同,或没 有定值,所以应在出口前把寄存器 的内容放在内存中,这样从基本块 外入口的变量值都在内存中.
• 一个简单的代码生成程序的构造.
• 目标代码的执行效率很大程度依赖寄存器 的使用.
12.1.1 代码生成器(程序)在编译系统中的位置
将经过语法分析或优化后的中间代码,转 换成特定机器的目标代码.完成代码生成这一 过程的程序称为代码生成器,如图所示.
源源程程序序
编译前端
中间 代码
代码优化
中间 代码
• 选择最优的寄存器分配是困难的,这是 NP完全问题(多项式条件下不可求解)
12.2 一个简单的代码生成器
• 介绍一个简单的代码生成程序 • 以四元式的中间代码作为输入,将
其转换成给定的M计算机的目标 代码
• 讨论一个基本块内如何充分利用 寄存器以提高目标代码的效率
• 给出寄存器分配的一般算法
12.2.1 一个假想的计算机模型
3. 寄存器分配
• 寄存器是比较稀少的资源,计算机程 序所需要的寄存器要比可用的寄存器 多。
• 寄存器分配负责确定在程序的哪个点 将哪些值放在寄存器中比较有益。
寄存器分配可以分成分配和指派两个 阶段来考虑
① 在寄存器分配期间,为程序的某一 点选择驻留在寄存器中的一组变量;
② 在随后的寄存器指派阶段,挑出变 量将要驻留的具体寄存器。
• 设计一个好的代码生成器,必须 熟习目标机器和它的指令系统
• 假定一种计算机由n个通用寄存 器R0, R1, …, Rn-1
• R既可作累加器也可作变址器 • op表示运算符,M表示内存单元 • C表示常量,*表示间址方式存取
机器指令类型
类型
指令形式 意义(op是二目运算符)
寄存器分配与寄存器赋值
• 寄存器分配 确定在程序的某个点将哪些值放在寄 存器中
• 寄存器赋值 确定分配有寄存器的值应该在哪个寄 存器中。由于一些目标机可能具有不 同类型的寄存器,因此,对寄存器使 用的一致性方面也存在着一定的约束。
例如,a:=b+c
① Ri中存放了b的值,而Rj中存放了c的值, 且b在此语句之后不再活跃。