SlideShare a Scribd company logo
编译器设计与实现
——Lcc原理剖析
华中科技大学计算机学院
张 德
2024/5/30
一、概述
1、编译器各阶段
2024/5/30
词法分析器
语法分析器
语义分析器
中间代码生成器
代码优化器
代码生成器
错误处理器
符号表管理器
源程序
目标程序
2、编译器各阶段的分组
 前端:依赖于语言并很大程度上独立于目
标机器。一般包括语法分析、词法分析、
符号表的建立、语义分析、中间代码生成
以及相关错误处理。
 后端:依赖于目标机器的阶段或某些阶段
的某些部分。一般来说,后端完成的任务
不依赖于源语言而只依赖于中间语言。主
要包括代码优化、代码生成以及相关的错
误处理和符号表操作。
2024/5/30
二、符号表
 符号表是编译器保存信息的中心库,编译
器的各部分通过符号表进行交互,并访问
符号表中的数据——符号。
 符号表把各种名字映射到符号集合。常量
、标识符和标号都是名字,不同名字有不
同的属性。
 符号管理不仅要处理符号本身,还管理符
号的作用域。
2024/5/30
1、符号的表示
struct symbol {
char *name; //名称
int scope; //作用域
Coordinate src; //在源程序中位置
Symbol up; //连接符号表中上一个符号
List uses; //可保存一个Coordinate列表,表示使用情况
int sclass; //扩展存储类型
<symbol flag> //符号标记
Type type; //如变量、函数、常量、结构或联合等信息
float ref; //被引用的粗略次数
union { //联合u为标号、结构、联合、枚举、常量、全局
<appendent info> //和静态变量提供附加信息
} u; //
Xsymbol x; //由后端处理,如为变量分配寄存器
<debugger extension>// 为调试器产生数据信息
}
2024/5/30
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、STATIC或EXTERN等
 首字母大写的类型表示全小写类型的指针,如Symbol。
2024/5/30
2、符号表的表示
extern Table constants;
extern Table externals;
extern Table globals;
extern Table identifiers;
extern Table labels;
extern Table types;
struct table {
int level; //同symbol中scope域
Table previous; //符号表链表,指向level-1的表
struct entry {
struct symbol sym;
struct entry *link;
} *buckets[256]; //这是一个哈希链数组,方便插入、查找
Symbol all; //指向当前及其外层所有符号列表的表头
};
2024/5/30
3、符号表举例
int x, y;
f(int x, int a){
int b;
y = x + a*b;
if (y < 5){
int a;
y = x + a*b;
}
}
2024/5/30
2024/5/30
3
0
4
5
6
0
0
0
0
0
a b x y
4、符号表的相关操作
 查找和建立标识符
Symbol install(const char * name, Table * tpp, int level, int arena);
Symbol lookup(const char *name, Table tp);
 标号:与标识符相似,但不涉及作用域
 常量:这些符号保存在constants表中
 产生变量:用于产生静态变量保存字符串等
2024/5/30
三、代码生成接口
 这一章内容定义了与目标机器无关的前端和
与目标机器相关的后端之间的接口。
 Lcc接口包括一些共享数据结构、18个函数和
包括36个操作符的语言。该语言用于将可执
行代码从源程序生成dag(有向无环图)。
 共享数据结构可供前后端共享,但某些域为
一端私有。symbol就是一个共享数据结构。
2024/5/30
1、类型度量
typedef struct metrics {
unsigned char size, align, outofline;
} Metrics;
size:类型的大小;
align:对齐字节数;
outofline:控制相关类型的常量的放置。为1时,不出现在dag中,存于
静态变量中。
Metrics charmetric;
Metrics shortmetric;
Metrics intmetric;
Metrics floatmetric;
Metrics doublemetric;
Metrics structmetric;
2024/5/30
2、接口记录
typedef struct interface {
<metrics>
<interface flags>
<interface functions>
Xinterface x;
} Interface;
lcc为每一种目标机器形成一个独有的接口实例。x
域是对interface的扩展,后段使用它存放与目标
及其相关的接口数据和函数,对后端私有。
2024/5/30
3、dag操作
 可执行代码用dag来描述。函数体是用dag
组成的序列或森林。每个dag都可以同过
gen函数传给后端。
 dag节点
struct node {
short op;
short count;
Symbol syms[3];
Node kids[2];
Node link;
Xnode x;
};
2024/5/30
3、dag操作
op域存放dag操作符。
dag操作符后缀表示操作数类型:
enum {
F=FLOAT,
I=INT,
U=UNSIGNED,
P=POINTER,
V=VOID,
B=STRUCT
};
如CNST,有变体CNSTI、CNSTU、CNSTP等。
CNST = 1<<4;
CNSTC=CNST+F;
CNSTI=CNST+I;
… …
2024/5/30
2024/5/30
2024/5/30
3、dag操作
举例: int i, *p; f() { i = *p++;}
2024/5/30
3 CNSTI
4
5 ASGNP
1 ADDRGP
p
7 INDIRI
6 ADDRGP
i
8 ASGNI
4 ADDP
2 INDIRP
4、接口标志
unsigned little_endian:1;
目标机器存储是低位优先还是高位优先
unsigned mulops_calls:1;
有硬件实现乘、除和求余,mulopes_calls应等于0
unsigned wants_callb:1;
通知前端产生CALLB节点以调用返回结构的函数
unsigned wants_argb:1;
通知前端节点产生ARGB节点以产生结构参数
unsigned left_to_right:1;
告诉前端按照从左到右的顺序计算和提交参数给后端
unsigned wants_dag:1;
告诉前端传递dag给后端
2024/5/30
5、函数
 前端将函数编译为私有数据结构。将函数
的任意部分传递给后端之前,前端必须先
对每个函数进行完整的分析。
 函数的处理:function函数包括前端过程
gencode遍历前端的私有数据结构,将dag的每个
森林传给后端过程gen。gen选择代码,在dag上添
加注释并将返回一个dag指针。gencode还可以调
用local宣告新的局不变量。前端过程emitcode再
次遍历,将gen返回的指针传递给emit函数发送代
码。
2024/5/30
6、上行调用
前段调用后端以执行代码生成和发送。后端调
用前端完成输出、分配存储空间、查询类型等功
能。上行调用即后端调用前端。
allocate分配空间,保证对齐方式符合机器多 数
类型
newnode分配新的dag节点
newconst符号表中创建新的常量
newtemp符号表中创建新的变量
… …
2024/5/30
四、词法分析
 词法分析器读入源程序,产生语言的基本
词法单元。
 例:*prt = 56;
2024/5/30
单词编码 附加值
‘*’
ID “prt”
‘=’
ICON “56”
1、输入
2024/5/30
buffer buffer+
MAXLINE buffer+
MAXLINE+MAXSIZE
n
cp limit
当limit-cp小于某一个固定值时,调用
fillbuf函数填充buffer
2、单词识别
 部分文法:
token:
keyword
identifier
constant
operator
punctuator
punctuator:
one of [ ] ( ) { } * , : = ; …
2024/5/30
定义:
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 “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")
xx(VOID, 8, 0, 0, 0, CHAR, "void")
xx(STRUCT, 9, 0, 0, 0, CHAR, "struct")
… …
2024/5/30
3、关键字的识别
可以通过查表完成,也可以通过硬编码方式识别。
例如,当起始小写字母为i时由gettok函数中switch语句的
case ‘i’处理。
2024/5/30
case 'i':
if (rcp[0] == 'f'&& !(map[rcp[1]]
&(DIGIT|LETTER))){
cp = rcp + 1;
return IF;
}
if (rcp[0] == 'n'&& rcp[1] == 't'
&& !(map[rcp[2]]&(DIGIT|LETTER))) {
cp = rcp + 2;
tsym = inttype->u.sym;
return INT;
}
goto id;
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': case '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;
}
2024/5/30
assert(cp == rcp);
token = (char *)rcp - 1;
while (map[*rcp]&(DIGIT|LETTER))
rcp++;
token = stringn(token, (char *)rcp - token);
tsym = lookup(token, identifiers);
cp = rcp;
return ID;
检查是否需要填充
buffer
5、其他
 另外还有:
 数字识别
 字符常量和字符串识别
都是有gettok函数实现,实现方法相似。
词法分析器可以有象LEX这样的工具
实现。Lcc手工实现了词法分析器,体积
更小。
2024/5/30
五、语法分析
 根据语言的文法分析,以确认输入是否符
合语言规则,并建立输入源程序的内部表
示。
 Lcc采用递归下降的语法分析。
 语法分析以形式语言理论为基础,采取语
法制导翻译方法,处理程序中的错误。
2024/5/30
1、表达式
 表达式的表示: (a+b)+b*(a+b)
2024/5/30
ADD+I
ADDRG+P
a
MUL+I
ADD+I
INDIR+I
INDIR+I
ADD+I
ADDRG+P
b
ADDRG+P
b
INDIR+I
INDIR+I INDIR+I
ADDRG+P
a
ADDRG+P
b
 表达式的分析:
c语言的小部分表达式语法:
expr: 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(); factor()}
2024/5/30
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 == '=‘ || (prec[t] >= 6 && prec[t] <= 8)
|| (prec[t] >= 11 && prec[t] <= 13)) {
int op = t;
t = gettok();
if (oper[op] == ASGN)
p = asgntree(ASGN, p, value(expr1(0)));
else
<augmented assignment>
return p
}
2024/5/30
 条件表达式:
conditonal-expression:
binary-expression[? expression : conditional-expression]
static Tree expr2(void) {
Tree p = expr3(4);
if (t == '?') {
Tree l, r;
Coordinate pts[2];
if (Aflag > 1 && isfunc(p->type))
warning("%s used in a conditional expressionn",
funcname(p));
p = pointer(p);
t = gettok();
pts[0] = src;
l = pointer(expr(':'));
pts[1] = src;
r = pointer(expr2());
}
<other >
return p;
}
2024/5/30
 另有二元表达式、一元表达式、后缀表达
式和基本表达式。
 表达式分析多是用递归和大量switch语句实
现。
 在编译领域用一个分析函数代替n个函数处
理n级优先是非常流行的。
 关于表达式的分析还包括表达式语义的分
析,如类型检查转换、函数调用分析等各
种操作。
2024/5/30
2、语句分析
 代码的表示:表达式首先被编译为分析树然后转
化为dag。每个函数的dag在代码表中被串起来,
代码表表示了函数的代码。
code结构:
struct code {
enum { Blockbeg, Blockend, Local, Address, Defpoint,
Label, Start, Gen, Jump, Switch
} kind;
Code prev, next;
union {
<unions>
}u;
}
2024/5/30
 语句的识别:
void statement(int loop, Swtch swp, int lev) {
float ref = refinc;
if (Aflag >= 2 && lev == 15)
warning("more than 15 levels of nested statementsn");
switch (t) {
case IF: ifstmt(genlabel(2), loop, swp, lev + 1); break;
case WHILE: whilestmt(genlabel(3), swp, lev + 1); break;
case DO: dostmt(genlabel(3), swp, lev + 1); expect(';'); break;
… …
}
<check >
refinc = ref;
}
expect(‘;’)
break;
2024/5/30
 if语句的识别:
if expression == 0 goto L
statement1
goto L+1
L: statement2
L+1:
static void ifstmt(int lab, int loop, Swtch swp, int lev) {
t = gettok();
expect(‘(’); //判断if后的(
definept(NULL);
walk(conditional(‘)’), 0, lab); //包含listnode函数生成dag并加入
refinc /= 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);
}
2024/5/30
 在循环、switch、goto语句中都用到了标号
和跳转,标号使通过definelab函数定义的,
而跳转通过branch函数生成。
 除语句识别外,还有声明的识别。声明的
识别非常复杂,c语言中声明的形式很多,
处理时大量的相互递归调用。
 经过前端的分析后,将源程序转化为dag,
并添加进代码表。
2024/5/30
3、小结
六、中间代码生成
 编译器的后端通过function接口函数调用
gencode和emitcode来遍历代码表。
 walk和listnodes函数操作处理dag森林。
 newnode函数为节点分配内存并用它的参数
只来初始化节点的域。
 listnode还负责删除公共子表达式。
2024/5/30
1、构建节点
Node listnodes(Tree tp, int tlab, int flab) {
Node 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)) {
<listnodes cases>
}
tp->node = p;
return p;
}
2024/5/30
2、控制流
 最简单的一元和二元操作加入结点表,但
是并不会出现在根中。赋值等操作可以用
这种情况解决。 要改变控制流需要跳转。
case JUMP: {
l = listnodes(tp->kids[0], 0, 0);
list(newnode(JUMP+V, l, NULL, NULL));
reset();
} break;
2024/5/30
static void list(Node p) {
if (p && p->link == NULL) {
if (forest) {
p->link = forest->link;
forest->link = p;
} else
p->link = p;
forest = p;
}
}
2024/5/30
forest是一个循环链表,不为空则指向链表
最后一个节点,为空则将其初始化,link域
可以表示根结点。
case LT: { //LT代表大于转移,是接口dag标识符
l = listnodes(tp->kids[0], 0, 0);
r = listnodes(tp->kids[1], 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(newnode(op + opkind(l->op), l, r, findlabel(flab)));
}
if (forest && forest->syms[0])
forest->syms[0]->ref++; } break;
2024/5/30
2024/5/30
a[i]&&a[i]+b[i]>0&&a[i]+b[i]<10的森林
EQI
2
LEI
2
GEI
2
LABELV
2
INDIRI CNSTI
0
ADDI
ADDP
LSHI ADDRGP
a
INDIRI CNSTI
2
ADDRGP
i
INDIRI
ADDP
ADDRGP
b
CNSTI
10
七、代码生成器
 代码生成器:为编译前端提供接口函数,接口
函数用与目标机器相关的指令来实现无关的中间
代码。接口函数也为临时变量指派寄存器、固定
的函数单元或栈空间。
 Lcc将大部分与机器无关的函数重组到一个大的与
机器无关的程序中。
2024/5/30
例程名 功能
function 产生函数的头代码和尾代码,调用gencode
gencode 解释代码表,并将数传递给gen
gen 处理代码表中各个森林
rewrite
prelabel 修改树,以适应寄存器变量和特殊的目标机器
_label 用所有可能的实现标记树
reduce 选择代价最小的实现
prune 从树中提出某些自指令
linearize 输出排序指令
ralloc 分配寄存器
emitcode 解释代码表,为每个森林调用emit
emit 追溯指令列表
requate 删除寄存器到寄存器的复制
moveself 删除寄存器复制到自身的指令
emitasm 解释汇编模版,输出大多数指令
emit2 输出过复杂不适于模版的指令
2024/5/30
1、选择和发送指令
 Lcc的指令选择器时由程序lburg根据紧缩规
范自动生成的,lburg是代码生成器的生成
器。
 lburg接收紧缩规范并产生一个用c语言编写
的树分析程序,该程序为目标机器选择指
令。树分析程序接受中间代码的主题树,
并将它分割成与目标机器相对应的程序块
,成为数覆盖。
2024/5/30
模式:ADDI(reg,con)表示如果ADDI的第一个子节点能递归的匹配reg,
第二个子节点匹配con,那么该模式就在ADDI除匹配一棵树。
规则:addr:ADDI(reg,con)规定了非终结符addr与上述模式相匹配
规则:stmt: ASGNI(addr,reg)规定了ASGNI节点的每子节点递归的
与addr和reg匹配非终结符stmt就与该ASGNI匹配。
例: ASGNI(ADDP(INDIRP(ADDRLP(p)),CNSTI(4)),CNSTI(5))的覆盖
2024/5/30
ASGNI
ADDP
INDIRP CNSTI
4
ADDRLP
p
CNSTI
5
addr:ADDP(addr,con)
reg:INDIRP(addr)
addr:ADDRLP
con:CNSTI
reg:con
con:CNSTI
stmt:ASGNI(addr,reg)
2024/5/30
2024/5/30
2、发送器
 Lcc发送器(emitter)的作用是为目标机器输出代
码。发送器并不依赖于目标机器,由两个描述与
机器相关的数据的数组驱动。Lburg为每个BURM
生成一些c语言代码,用来声明并初始化这两个数
组。
 两个数组都是通过规则号索引。
 规则生成模板数组:static char *_template[];
 标记与指令对应的模板,以区别子指令(如寻址
指令): static char *_isinstruction[];
2024/5/30
lburg从1开始为规则编号,并通过返回
规则号来报告匹配情况,这样当需要的时
候就可以找到响应的模板。如果模板以一
个换行符为结尾表示它是一条指令,否则
就必然是某条指令的一部分,比如是一个
操作数。
rc: reg "%0"
rc: con "%0"
reg: ADDI4(reg,mrc1) "?mov %c,%0nadd %c,%1n" 1
reg: ADDP4(reg,mrc1) "?mov %c,%0nadd %c,%1n" 1
2024/5/30
 emitasm对规则结构及汇编程序代码模板进
行了解释。
 emitasm递归调用自身,以处理地址计算之
类的子指令。
 emitasm的遍历从一个指令开始,当递归到
达为该指令提供值的指令时结束。
 emitasm由emit调用,emit确保emitasm以
正确的顺序来处理这些指令,这样便可以
处理指令间的顺序。
2024/5/30
例如:发送器解释字符串“lw r%c,%1n”
先生成“lw r”,然后是目标寄存器的名字
(通常是一个数字),接着是一个逗号。
如果nts[1]中保存了表示非终结符addr的整
数,那么递归生成p->kids[1]作为一个addr
。最后emitasm生成一个换行符。
2024/5/30
3、寄存器的分配
 从上节我们可以看出,代码发送器可以生
成汇编代码,但是汇编代码中的寄存器是
如何分配的?
 寄存器分配包括两个内容:
 分配:决定哪些值占用寄存器
 指派:为每个值指派特定的寄存器
2024/5/30
2024/5/30
例程名 作用
linearize 为输出一棵指令树排序
ralloc 为一条指令释放和分配寄存器
putreg 释放一个忙寄存器
getreg 发现和分配一个寄存器
askreg 发现和分配一个空寄存器
askfixedreg 尝试分配一个制定的寄存器
spillee 标记一个最远使用寄存器溢出
spill 溢出一个或多个寄存器
spillr 溢出一个寄存器
genspill 产生代码溢出一个寄存器
genreload 产生代码重载一个被溢出的寄存器
reprune 当genreload更新x.kids后,更新kids
在后端选择指令并将子指令从树中分离
出来,linearize采用前序遍历分离的树,并
按照最后执行的顺序链接指令。gen再将每
条指令传递给ralloc函数,ralloc一般首先调
用putreg来释放不再被其子节点使用的寄存
器,然后调用getreg函数为自身分配一个寄
存器。对于临时变量,ralloc在首次赋值的
时候为它分配一个寄存器,在最后一次使
用的时候释放该机存器。
2024/5/30
 寄存器状态的跟踪
unsigned freemask[2];
unsigned usedmask[2];
用掩码表示寄存器的状态
对于寄存器r:
int n = r->set;
r->mask & ~freemask[n] 为0表示寄存器忙
2024/5/30
 寄存器分配:
寄存器分配器对森林进行三遍扫描:
 第一遍对所有使用临时变量的节点建立一个表。
该列表指名了临时变量节点的最后一次使用。
 第二遍对森林的扫描删去一些用于寄存器复制的
指令,把计算源寄存器的表达式重定向,使用目
的寄存器。
 最后一遍扫描为每个节点分配寄存器。
2024/5/30
 寄存器的溢出:
寄存器溢出是指寄存器分配器用完寄存
器时需要生成代码来空出一个忙寄存器,
将其值存储回存储器中,并将那些未被处
理的使用该寄存器的节点替换成存储器结
点。
存储器分配用完时最有选择是把最远使
用的寄存器存回存储器。这类似于操作系
统中的内存调度,原理相同。
2024/5/30
八、总结
 lcc是构造编译器的一种方法。在设计过程
中对数以百计的技术策略进行了选择,这
些策略很多都是可行的方法。
 lcc中运用了许多编程技巧,使的很多方法
得以巧妙的实现,减小了代码的体积。
 lcc中的各个部分还可单独拿出做其他的应
用,如语法分析可用于处理电子数据表,
lburg可处理各种树的模式匹配问题。
2024/5/30
1、数据结构
 由于lcc的共享数据结构不多,因此可以很
好的处理前端与代码生成之间的数据结构共
享。
 这样也有不足之处:比较其它简单的设计方
法,这些结构更为复杂。
 有些人认为是c语言导致这种复杂性,用定
义单独结构的方法可以减少这些复杂程度。
例如用面向对象语言将结构分割。
2024/5/30
2、接口
 lcc剔除了许多冗余部分并做了简单性
假定,一次是的代码很紧凑,但是这
些假定限制了接口在其他语言和机器
上的应用能力。
 lcc接口假定符号和无符号整数以及长
整数都具有相同的长度。假定所有指
针表示相同。
2024/5/30
3、语法和语义分析
 lcc的语法和语义分析穿插进行。
 lcc采用一遍扫描的策略,与AST相比
她的开销更小,速度更快。
2024/5/30
4、代码生成和优化
 代码生成需要综合平衡各种因素。功能强
大的优化器可以产生更好的代码。但是他
的速度太慢了。
 就每棵树来说,lcc的指令选择是最佳的。
但相邻数的代码边界处就差一些。lcc可以
在最后使用窥孔优化解决。
 lcc的寄存分配器比较原始,目前可以采用
图的着色方法分配能力更为出色,但这样
做会使代码多出很多。
2024/5/30

More Related Content

PPT
第1章 概论
PPT
Slide08 807007748
PDF
Clojure and FP
PPT
第9章 符号表
PDF
GCC_Porting_on_MiniSystem
PDF
Lambda演算与邱奇编码
PDF
Jvm分享20101228
PDF
Java program in_action
第1章 概论
Slide08 807007748
Clojure and FP
第9章 符号表
GCC_Porting_on_MiniSystem
Lambda演算与邱奇编码
Jvm分享20101228
Java program in_action

Similar to LCC Retarget C Compiler Introduction in Chinese (20)

PDF
Java program in_action_20110727-撒迦
PPT
第4章 自顶向下的语法分析
PPT
PPT
第三章 栈和队列
PPT
第2章 文法和语言
PPT
第3章算法与控制语句
PDF
竞赛中C++语言拾遗
PPT
第7章 语法制导翻译和中间代码生成
PPT
第6章 自底向上的lr分析法
PPT
第6章 自底向上的lr分析法
PPT
第三章 栈和队列(新)
PDF
使用Dsl改善软件设计
PDF
C语言学习100例实例程序
PPTX
Hcsm lect-20120913
PPTX
Chapter 3 basic syntax and operator
ODP
Ihome inaction 篇外篇之fp介绍
PPT
第1章概述
PDF
Learning python in the motion picture industry by will zhou
DOCX
系統程式 -- 第 8 章 編譯器
PDF
Clojure简介与应用
Java program in_action_20110727-撒迦
第4章 自顶向下的语法分析
第三章 栈和队列
第2章 文法和语言
第3章算法与控制语句
竞赛中C++语言拾遗
第7章 语法制导翻译和中间代码生成
第6章 自底向上的lr分析法
第6章 自底向上的lr分析法
第三章 栈和队列(新)
使用Dsl改善软件设计
C语言学习100例实例程序
Hcsm lect-20120913
Chapter 3 basic syntax and operator
Ihome inaction 篇外篇之fp介绍
第1章概述
Learning python in the motion picture industry by will zhou
系統程式 -- 第 8 章 編譯器
Clojure简介与应用
Ad

LCC Retarget C Compiler Introduction in Chinese