- 博客(64)
- 资源 (2)
- 收藏
- 关注
原创 C编译器剖析_Github
欢迎前往GitHub下载和修改UCC编译器的相关代码,和配套的清晰版的PDF文档。UCC编译器遵循C89标准,代码在1万多行。只要努力,这是一个C程序员单枪匹马就可掌控的。在这里,可从C编译器实现的高度来重新认识C语言。https://github.com/sheisc/ucc162.3
2016-12-02 19:36:29
5669
1
原创 XV6操作系统的安装与使用
1.1 XV6操作系统的安装与使用 麻省理工大学的网站上http://pdos.csail.mit.edu/6.828/2011/xv6.html,已经对XV6的来龙去脉及如何下载做了较详细的介绍。这里我们以32位的Ubuntu12.04为例,简单介绍一下如何通过x86模拟器Bochs来运行XV6操作系统。 XV6操作系统是可以运行在多核结构的X86平台上,我
2015-06-27 22:07:50
14955
3
原创 C编译器剖析PDF文档及UCC编译器162.3
为方便大家的阅读,我把博客从2015年1月初至5月初的发布几十篇文章整理成一份PDF文档,需要的朋友可前往http://download.youkuaiyun.com/detail/sheisc/8669715下载。 书中格式不规范之处,请多多见谅。例如,代码贴图的风格并不太统一,前几章的贴图是直接截取SourceInsight的界面,行号就是在源代码文件中的行号,这么做的初衷是为了便于能快速找
2015-05-06 21:46:09
4356
原创 C编译器剖析_尾声
尾声 总有曲终人散时,不知不觉我们已经完成了对UCC编译器的剖析,一路走来,最深的体会仍然是“纸上得来终觉浅,绝知此事要躬行”。按这个道理,理解UCC编译器的最好办法应是“直接阅读其源代码,思考UCC编译器在不同的执行点应处于怎样的状态,加入一些打印语句,输出相应的调试信息来验证自己的判断是否正确,如果发现Bug,就写一些测试程序来触发Bug,然后
2015-05-02 23:02:29
1695
原创 C编译器剖析_6.3.6 汇编代码生成_为“取地址”产生汇编指令
6.3.6 为“取地址”产生汇编指令 在这一小节中,我们来讨论一下以下两条中间指令的翻译: (1)取地址指令 例如 ,表示取number的地址并保存到临时变量t0中 (2)对象清零指令 例如,表示把arr所占16字节的内存清零 我们先举一个例子来说明,对于图6.3.14第4行局部数组arr的初
2015-05-02 21:14:43
1858
原创 C编译器剖析_6.3.5 汇编代码生成_为类型转换产生汇编代码
6.3.5 为类型转换产生汇编代码 在这一小节中,我们来讨论一下整型和浮点型之间的类型转换。有些类型转换并不需要在汇编层次进行数据转换,例如int和unsigned int之间的转换只是改变了表达式的类型,对数据本身并无影响,以下表达式“(unsigned int) a”对应的二进制数据为0xFFFFFFFF,而表达式“a”对应的二进制数据也为0xFFFFFFFF。但对相同内容的二进
2015-05-01 22:55:21
1953
原创 C编译器剖析_6.3.4 汇编代码生成_为函数调用与返回产生汇编代码
6.3.4 为函数调用与返回产生汇编代码 在这一小节中,我们来讨论一下如何为函数调用和函数返回生成汇编代码。函数调用对应的中间指令如下所示: //中间指令的四元式: 让我们先熟悉一下C函数的调用约定CallingConvention,我们需要把参数从右向左入栈(即从argn到arg1依次入栈),不妨记这些参数所占用的总内
2015-05-01 00:33:54
1791
原创 C编译器剖析_6.3.3 汇编代码生成_为跳转指令产生汇编代码
6.3.3 为跳转指令产生汇编代码 在这一小节中,我们要为“有条件跳转”、“无条件跳转”和“间接跳转”产生相应的汇编指令。中间指令的四元式如下所示: (1) 有条件跳转,例如“if (a ////////对应的汇编代码////////// movl a, %ea
2015-04-30 17:08:29
2206
原创 C编译器剖析_6.3.2 汇编代码生成_为算术运算产生汇编代码
6.3.2 由EmitAssign函数产生算术运算的汇编代码 在这一小节中,我们要讨论的中间指令形如“t1: a+b;”或者“t2:&number”,这些指令用于进行一元或二元算术运算,并把运算结果保存在临时变量t1或者t2中。UCC中间指令的格式如下所示: // t1: a+b; // t2: &
2015-04-30 13:22:02
1345
原创 C编译器剖析_6.3.1 汇编代码生成_由中间指令产生汇编代码的主要流程
6.3.1 由中间指令产生汇编代码的主要流程 在这一小节,我们可把关注的焦点放在“如何把某条中间代码翻译成汇编代码”上。UCC编译器的中间代码是如下所示的四元式,包括运算符和3个操作数。 当然有些中间代码只需要用到opcode和DST就可以了,例如,无条件跳转指令“goto BB2;”就不需要SRC1和SRC2。为了便于汇编代码的生成,UCC编译器在u
2015-04-29 17:54:19
3222
原创 C编译器剖析_6.2 汇编代码生成_寄存器的管理
在计算机中,CPU的速度比内存的速度快得多,编译器应尽量有效地利用寄存器资源,减少对内存的不必要访问,从而提高由编译器生成的汇编代码的运行速度。在中间代码生成阶段,UCC编译器用临时变量t来存放形如“t: a+b;”的公共子表达式的值;到了汇编代码生成时,UCC编译器会尽可能地把这些公共子表达式的值存放在寄存器,当需要再次重用时,就可以直接由相应的寄存器中得到。不过,CPU中寄存器的资源是很有限的
2015-04-26 22:04:09
1575
原创 C编译器剖析_6.1 汇编代码生成_简介
6.1 汇编代码生成简介 历经词法分析、语法分析、语义检查和中间代码生成阶段,我们终于来到了“目标代码生成阶段”,由于UCC编译器的目标代码即为32位x86汇编代码,因此我们就把本章称为“汇编代码生成”。UCC编译器中的大部分源代码都适用于Windows和Linux平台,但Windows平台上缺省的汇编器支持Intel风格的x86汇编代码,而Linux平台默认的汇编器则采用AT&T风格的
2015-04-25 19:24:12
2657
原创 C编译器剖析_5.4.2 中间代码生成及优化_基本块的合并
5.4.2 基本块的合并 我们在第5.4.1节时给出了由基本块构成的双向链表和控制流图,为阅读方便,我们这里再次给出“图5.1.4 基本块的静态结构和动态结构”。在这一小节中,我们试图把双向链表中相邻的基本块进行合并,当然这种合并需要满足一定条件,同时要保持程序的原有语义。在合并后,控制流图中的前驱与后继关系也要进行调整。我们需要改动的数据结构有图5.4.1中的双向链表和控制流图。需要
2015-04-24 17:05:20
3639
原创 C编译器剖析_5.4.1 中间代码生成与优化_删除无用的临时变量和优化跳转目标
5.4.1 删除无用的临时变量和优化跳转目标 UCC编译器在优化方面做的工作不多,其中与优化有关的函数主要有以下几个:(1) Symbol Simplify(Type ty, int opcode, Symbol src1,Symbol src2);用于进行“代数恒等式”的简化,例如表达式“a(2) Symbol TryAddValue(Type ty,
2015-04-23 16:55:02
2014
原创 C编译器剖析_5.3.2 中间代码生成及优化_switch语句的翻译
5.3.2.Switch语句的翻译 在这一小节中,我们来讨论一下switch语句的翻译,switch语句的产生式如下所示。SwitchStatement: switch( expr ) statement 当C程序员编写出如下代码时,UCC编译器会在语义检查阶段进行报错“error:The break shall appear in
2015-04-22 17:04:42
2015
原创 C编译器剖析_5.3.1 中间代码生成及优化_If语句和复合语句的翻译
5.3.1 If语句和复合语句的翻译 我们先简单回顾一下对布尔表达式的翻译,我们通过调用TranslateBranch函数来产生跳转指令,从而实现布尔表达式的语义。在使用函数TranslateBranch(expr, bt, bn)时,有这么两个约定: (1) 当expr为真时,跳往bt基本块; (2) 紧随“函数TranslateBranch所生成的跳转指令”之
2015-04-20 15:48:35
1949
原创 C编译器剖析_5.2.6 中间代码生成及优化_一元表达式及其他表达式的翻译
5.2.6 一元表达式及其他表达式的翻译 在这一小节中,我们先来讨论一下一元表达式的翻译,我们先举个例子来说明一下。在以下C程序中,符号arr被声明为int[3][5]的数组类型,UCC编译器在语义检查后,为表达式“**arr=30;”构造的语法树为“(= ([]([] arr 0) 0) 30)”,在中间代码生成阶段,对应的中间代码为“arr[0] = 30;”。由这个例子可见,
2015-04-19 21:33:33
1211
原创 C编译器剖析_5.2.5 中间代码生成及优化_赋值表达式的翻译
5.2.5 赋值表达式的翻译 在这一小节中,我们来讨论一下赋值表达式的翻译,例如“a=a-b;”或者“a += b;”等。在图5.2.13的下方我们给出了表达式“a+=b;”在语义检查前后的语法树,右下侧的语法树相当于是表达式a = a’+b,但表达式中的a和a’对应同一个语法树结点。按C的语义,语句“a+=b;”中的a只能被求值一次,而不能求值两次。例如,若a结点对应一个函数调用(*f
2015-04-18 17:53:20
1168
原创 C编译器剖析_5.2.4 中间代码生成及优化_后缀表达式的翻译
5.2.4 后缀表达式的翻译 在前面的章节中,我们介绍了用于对数组元素和结构体成员进行访问的函数Offset,其接口如下所示,参数addr代表了基地址,参数voff代表可变偏移,而参数coff则代表常量偏移。 Symbol Offset(Type ty, Symbol addr,Symbol voff, int coff); 函数Offset的基本想法是产生
2015-04-17 00:01:20
1453
原创 C编译器剖析_5.2.3 中间代码生成及优化_通过“偏移”访问数组元素和结构体成员
第5.2.3节 通过“偏移”访问数组元素和结构体成员 在上一节小节,我们举例介绍了对“数组元素和结构体成员”的访问,我们采用的是“基地址+偏移”的模式来计算其内存单元的地址。对于数组元素arr2[i][2]来说,数组索引值i为变量,对应的地址要表达为“基地址+常量偏移+非常量偏移”;对于结构体成员dt.b来说,其地址可表达为“基地址+常量偏移”。下面,我们还是结合一个简单的例子来说明相
2015-04-15 20:31:18
1813
原创 C编译器剖析_5.2.2 中间代码生成及优化_再论符号symbol与公共子表达式
5.2.2 再论符号symbol与公共子表达式 在介绍算术表达式的翻译前,让我们简单重温一下第2.5节中的“图2.5.4 公共子表达式”及“图2.5.5 valueDef和valueUse”。为阅读方便,我们再次给出这两张图,更详细的说明请参见第2.5节。对于图2.5.4第2行的a+b,我们会由第7行的中间代码来对a+b进行求值,其结果存于临时变量t1中,之后在第3行中再次遇到表达
2015-04-12 19:23:04
2039
原创 C编译器剖析_5.2.1 中间代码生成及优化_布尔表达式的翻译
5.2 中间代码生成与优化_布尔表达式的翻译 我们仍然按照语法分析和语义检查时的思路,先讨论表达式的翻译,再处理语句。表达式从概念上来说,可分为算术表达式和布尔表达式,在一些编程语言(例如Java)中对这两者是有严格区分的,算术表达式的结果是整数或浮点数,而布尔表达式的结果是逻辑上的真或假。布尔是英国数学家,由于布尔较早进行了关于“与或非”逻辑运算的研究,为了纪念这位先驱,在Java中
2015-04-10 14:26:20
2835
原创 C编译器剖析_5.1 中间代码生成及优化_简介
本节对UCC编译器的中间代码生成及优化进行简介,给出基本块BasicBlock、三地址码、控制流图CFG的相应数据结构,介绍有条件跳转、无条件跳转和间接跳转等概念。
2015-04-08 19:41:24
4747
原创 C编译器剖析_4.4 语义检查_外部声明_临门一脚
4.4.5 对外部声明进行语义检查的临门一脚 在前面几小节的基础上,我们基本上已经把球从后场带到对方球门前了,就差临门一脚了。在这一节中,我们来分析一下对全局变量进行语义检查的函数CheckGlobalDeclaration,和对函数定义进行语义检查的函数CheckFunction。对全局变量进行检查的主要代码如图4.4.23所示,我们省略了一些细节。图4.4.23第7行的C
2015-03-26 20:09:14
1434
原创 C编译器剖析_4.4 语义检查_外部声明_内部连接和外部连接
本节介绍C语言的内部连接Interal Linkage和外部连接External Linkage。
2015-03-26 16:31:20
1259
原创 C编译器剖析_4.4 语义检查_外部声明_结构体和数组的初始化
本节讨论标量类型、数组、结构体和联合体的初始化。在C语言中,全局或静态变量的初始化应为常量;而局部变量没有此要求。
2015-03-26 11:00:16
1497
原创 C编译器剖析_4.4 语义检查_外部声明_类型结构的构建(2)
在这一小节中,我们将对形如第3章图3.3.17所示的结构体语法树进行语义检查,从而构建结构体的类型结构。 图3.3.17 ParseStructOrUnionSpecifier()构建的语法树 我们在第2章中给出了以下结构体struct Data对应的类型结构,如图2.4.4所示。为了阅读方便,我们重新给出这2幅图,由图示我们也能较清楚地预览本节的起点和终点。
2015-03-20 14:24:39
1408
原创 C编译器剖析_4.4 语义检查_外部声明_类型结构的构建(1)
4.4.1 类型结构的构建(1) int * arr1[5]和int (*arr2)[5]的类型结构
2015-03-17 19:55:11
1158
原创 C编译器剖析_4.3 语义检查_语句statement
4.3 对语句Statement的语义检查 在这一节中,我们来分析一下对语句Statement的语义检查,与其相关的代码在stmtchk.c中。图4.3.1第1至16行的数组StmtCheckers,给出了用于对各语句进行语义检查的函数表,第17行的CheckStatement(stmt)函数根据参数stmt的kind域来查这个函数表,由函数指针来调用相应的函数完成语义检查,如图第18行
2015-03-15 19:52:35
1090
原创 C编译器剖析_4.2 语义检查_表达式的语义检查(7)_二元运算符_赋值运算_条件表达式
在前文对语义检查进行简介时,我们已初步介绍过用于对二元运算符表达式进行语义检查的函数CheckBinaryExpression,为了阅读方便,这里我们再次给出图4.2.2。在本小节中,我们准备对第1126至1144行中的各个函数进行讨论。图4.2.2 CheckBinaryExpression() 对于形如a+b的二元运算表达式,我们要通过在前面章节中介绍的函数Commo
2015-03-15 12:23:07
1298
原创 C编译器剖析_4.2 语义检查_表达式的语义检查(6)_一元运算符表达式
在这一小节中,我们来讨论一元运算符表达式的语义检查,与其相关的代码如图4.2.35所示。对于“前加加”和“前减减”运算符而言,我们采取的策略跟处理“后加加”和“后减减”一样,都是将--a转换为a -= 1,而将++a转换为a += 1,所以图4.2.35第5行调用的函数,就是我们在讨论后缀表达式语义检查时介绍过的函数TransformIncrement()。对于形如+a或-a的表达式,我们需要检查
2015-03-14 17:42:46
1224
原创 C编译器剖析_C类型系统_相容类型Compatible Type
在前文对函数调用进行语义检查时,我们用函数CanAssign()来判断“能否能把实参赋值给形参”,在函数CanAssign中,我们又用宏IsCompatiblePtr来判断两个指针是否相容。而如果指针变量T1 * ptr1和T2 * pt2相容,则意味着类型T1和T2是相容的。UCC编译器中ucl\type.c的函数IsCompatibleType()用于判断类型是否相容,与类型系统相关的数据结构
2015-03-10 20:28:12
1999
原创 C编译器剖析_4.2 语义检查_表达式的语义检查(5)_结构体成员选择
4.2.5 成员选择运算符 在C语言中,结构体struct和联合体union被称为记录类型RecordType,在形如dt.a和ptr->a的后缀表达式中,运算符.和->被称为成员选择运算符。函数CheckMemberAccess()用于对这些表达式进行语义检查,与之相关的代码如图4.2.28所示。在表达式dt.a中,dt和a相当于是运算符.的两个操作数,dt对应的语法
2015-03-07 14:05:18
1615
原创 C编译器剖析_4.2 语义检查_表达式的语义检查(4)_函数调用
4.2.4 函数调用的语义检查 在这一小节中,我们来讨论一下函数调用的语义检查,语法上,函数调用对应的表达式属于后缀表达式PostfixExpression,UCC编译器exprchk.c的函数CheckFunctionCall()完成了对函数调用的语义检查,如图4.2.18所示。在阅读这份代码时,需要对语法分析后为函数调用构造的语法树有较好认识,请先参照”图3.1.21后缀运算符对应
2015-03-06 15:15:15
1972
原创 C编译器剖析_C语言的变参函数
C语言的变参函数 UCC编译器中有不少地方使用了C语言的变参函数,这里我们专门用一小节来对C语言变参函数的实现原理进行分析。C标准库中的printf函数就是一个典型的变参函数,其接口如下所示,函数声明中的省略号…表明这是一个变参函数。 int printf(const char *format, ...); 下面我们举一个简单的例子来说明printf函数的调
2015-03-03 20:29:57
2008
原创 C编译器剖析_4.2 语义检查_表达式的语义检查(3)_字符串与标识符
4.2.3 在这一小节,我们先来分析一下基本表达式PrimaryExpression的语义检查,由C的标准文法,我们可以知道与PrimaryExpression相关的产生式如下所示,即加了一对小括号的表达式(Expression)在语法上也相当于标志符ID、常量CONST和字符串StringLiteral。primary-expression:
2015-03-03 11:29:26
1871
原创 C编译器剖析_4.2 语义检查_表达式的语义检查(2)_数组索引
在这一小节中,我们准备分析一下exprchk.c中的函数CheckPostfixExpression,此函数用于对后缀表达式进行语义检查。后缀表达式所对应的语法树请参照第3章的图3.1.21,这里不再重复。我们先从数组索引入手,图4.2.4给出了hello.c经UCC编译后生成的语法树hello.ast,中间代码hello.uil及最终生成的汇编代码hello.s的主要部分。图4.
2015-02-28 23:55:24
1208
原创 C编译器剖析_4.2 语义检查_表达式的语义检查(1)
在这一节中,我们来分析一下exprchk.c中的代码。我们通过调用图4.2.1中的函数CheckExpression(),来对表达式所对应的语法树结点进行语义检查。虽然表达式结点对应的kind域皆为NK_Expression,但它们的运算符op域还是各有区别的,由此我们可以为不同的运算符表达式构造不同的语义检查函数,然后把这些函数放置在一张函数表ExprCheckes中,用运算符op作为下标,去调
2015-02-27 15:58:58
1552
原创 C编译器剖析_4.1 语义检查_语义检查简介
4.1 语义检查简介 在这一章中,我们需要在语法分析阶段建立的语法树的基础上,进行语义检查。UCC编译器中与此相关的代码主要在ucl\declchk.c,ucl\stmtchk.c和ucl\exprchk.c,分别用于对声明Declaration、语句Statement和表达式Expression进行语义检查SemanticsCheck。其中,最重要的工作就是建立C语言的类型系统,我们
2015-02-11 16:54:45
2240
原创 C编译器剖析_3.3 语法分析_C语言的外部声明(3)
在上一小节中,我们讨论了C标准文法中与声明Declaration有关的几个非终结符。在此基础上,接下来让我们看看ucl\decl.c中与“声明说明符DeclarationSpecifiers”和“声明符Declarator”相关的代码。函数ParseCommonHeader()分析了“声明说明符”和“声明符”,图3.3.10 ParseCommonHeader() 图3.
2015-02-10 15:07:36
1866
C编译器剖析PDF文档_UCC162.3源代码
2015-05-06
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人