自定义博客皮肤VIP专享

*博客头图:

格式为PNG、JPG,宽度*高度大于1920*100像素,不超过2MB,主视觉建议放在右侧,请参照线上博客头图

请上传大于1920*100像素的图片!

博客底图:

图片格式为PNG、JPG,不超过1MB,可上下左右平铺至整个背景

栏目图:

图片格式为PNG、JPG,图片宽度*高度为300*38像素,不超过0.5MB

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+
  • 博客(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 5482

原创 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 14531

原创 C编译器剖析PDF文档及UCC编译器162.3

为方便大家的阅读,我把博客从2015年1月初至5月初的发布几十篇文章整理成一份PDF文档,需要的朋友可前往http://download.csdn.net/detail/sheisc/8669715下载。      书中格式不规范之处,请多多见谅。例如,代码贴图的风格并不太统一,前几章的贴图是直接截取SourceInsight的界面,行号就是在源代码文件中的行号,这么做的初衷是为了便于能快速找

2015-05-06 21:46:09 4237

原创 C编译器剖析_尾声

尾声    总有曲终人散时,不知不觉我们已经完成了对UCC编译器的剖析,一路走来,最深的体会仍然是“纸上得来终觉浅,绝知此事要躬行”。按这个道理,理解UCC编译器的最好办法应是“直接阅读其源代码,思考UCC编译器在不同的执行点应处于怎样的状态,加入一些打印语句,输出相应的调试信息来验证自己的判断是否正确,如果发现Bug,就写一些测试程序来触发Bug,然后

2015-05-02 23:02:29 1636

原创 C编译器剖析_6.3.6 汇编代码生成_为“取地址”产生汇编指令

6.3.6  为“取地址”产生汇编指令    在这一小节中,我们来讨论一下以下两条中间指令的翻译:    (1)取地址指令              例如 ,表示取number的地址并保存到临时变量t0中    (2)对象清零指令            例如,表示把arr所占16字节的内存清零    我们先举一个例子来说明,对于图6.3.14第4行局部数组arr的初

2015-05-02 21:14:43 1787

原创 C编译器剖析_6.3.5 汇编代码生成_为类型转换产生汇编代码

6.3.5  为类型转换产生汇编代码    在这一小节中,我们来讨论一下整型和浮点型之间的类型转换。有些类型转换并不需要在汇编层次进行数据转换,例如int和unsigned  int之间的转换只是改变了表达式的类型,对数据本身并无影响,以下表达式“(unsigned int) a”对应的二进制数据为0xFFFFFFFF,而表达式“a”对应的二进制数据也为0xFFFFFFFF。但对相同内容的二进

2015-05-01 22:55:21 1877

原创 C编译器剖析_6.3.4 汇编代码生成_为函数调用与返回产生汇编代码

6.3.4        为函数调用与返回产生汇编代码    在这一小节中,我们来讨论一下如何为函数调用和函数返回生成汇编代码。函数调用对应的中间指令如下所示:         //中间指令的四元式:              让我们先熟悉一下C函数的调用约定CallingConvention,我们需要把参数从右向左入栈(即从argn到arg1依次入栈),不妨记这些参数所占用的总内

2015-05-01 00:33:54 1721

原创 C编译器剖析_6.3.3 汇编代码生成_为跳转指令产生汇编代码

6.3.3        为跳转指令产生汇编代码    在这一小节中,我们要为“有条件跳转”、“无条件跳转”和“间接跳转”产生相应的汇编指令。中间指令的四元式如下所示:            (1) 有条件跳转,例如“if (a                         ////////对应的汇编代码//////////            movl  a, %ea

2015-04-30 17:08:29 2123

原创 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 1292

原创 C编译器剖析_6.3.1 汇编代码生成_由中间指令产生汇编代码的主要流程

6.3.1  由中间指令产生汇编代码的主要流程    在这一小节,我们可把关注的焦点放在“如何把某条中间代码翻译成汇编代码”上。UCC编译器的中间代码是如下所示的四元式,包括运算符和3个操作数。             当然有些中间代码只需要用到opcode和DST就可以了,例如,无条件跳转指令“goto  BB2;”就不需要SRC1和SRC2。为了便于汇编代码的生成,UCC编译器在u

2015-04-29 17:54:19 3059

原创 C编译器剖析_6.2 汇编代码生成_寄存器的管理

在计算机中,CPU的速度比内存的速度快得多,编译器应尽量有效地利用寄存器资源,减少对内存的不必要访问,从而提高由编译器生成的汇编代码的运行速度。在中间代码生成阶段,UCC编译器用临时变量t来存放形如“t: a+b;”的公共子表达式的值;到了汇编代码生成时,UCC编译器会尽可能地把这些公共子表达式的值存放在寄存器,当需要再次重用时,就可以直接由相应的寄存器中得到。不过,CPU中寄存器的资源是很有限的

2015-04-26 22:04:09 1507

原创 C编译器剖析_6.1 汇编代码生成_简介

6.1 汇编代码生成简介    历经词法分析、语法分析、语义检查和中间代码生成阶段,我们终于来到了“目标代码生成阶段”,由于UCC编译器的目标代码即为32位x86汇编代码,因此我们就把本章称为“汇编代码生成”。UCC编译器中的大部分源代码都适用于Windows和Linux平台,但Windows平台上缺省的汇编器支持Intel风格的x86汇编代码,而Linux平台默认的汇编器则采用AT&T风格的

2015-04-25 19:24:12 2564

原创 C编译器剖析_5.4.2 中间代码生成及优化_基本块的合并

5.4.2  基本块的合并    我们在第5.4.1节时给出了由基本块构成的双向链表和控制流图,为阅读方便,我们这里再次给出“图5.1.4 基本块的静态结构和动态结构”。在这一小节中,我们试图把双向链表中相邻的基本块进行合并,当然这种合并需要满足一定条件,同时要保持程序的原有语义。在合并后,控制流图中的前驱与后继关系也要进行调整。我们需要改动的数据结构有图5.4.1中的双向链表和控制流图。需要

2015-04-24 17:05:20 3554

原创 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 1932

原创 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 1912

原创 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 1872

原创 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 1162

原创 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 1103

原创 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 1372

原创 C编译器剖析_5.2.3 中间代码生成及优化_通过“偏移”访问数组元素和结构体成员

第5.2.3节  通过“偏移”访问数组元素和结构体成员    在上一节小节,我们举例介绍了对“数组元素和结构体成员”的访问,我们采用的是“基地址+偏移”的模式来计算其内存单元的地址。对于数组元素arr2[i][2]来说,数组索引值i为变量,对应的地址要表达为“基地址+常量偏移+非常量偏移”;对于结构体成员dt.b来说,其地址可表达为“基地址+常量偏移”。下面,我们还是结合一个简单的例子来说明相

2015-04-15 20:31:18 1742

原创 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 1960

原创 C编译器剖析_5.2.1 中间代码生成及优化_布尔表达式的翻译

5.2  中间代码生成与优化_布尔表达式的翻译    我们仍然按照语法分析和语义检查时的思路,先讨论表达式的翻译,再处理语句。表达式从概念上来说,可分为算术表达式和布尔表达式,在一些编程语言(例如Java)中对这两者是有严格区分的,算术表达式的结果是整数或浮点数,而布尔表达式的结果是逻辑上的真或假。布尔是英国数学家,由于布尔较早进行了关于“与或非”逻辑运算的研究,为了纪念这位先驱,在Java中

2015-04-10 14:26:20 2736

原创 C编译器剖析_5.1 中间代码生成及优化_简介

本节对UCC编译器的中间代码生成及优化进行简介,给出基本块BasicBlock、三地址码、控制流图CFG的相应数据结构,介绍有条件跳转、无条件跳转和间接跳转等概念。

2015-04-08 19:41:24 4613

原创 C编译器剖析_4.4 语义检查_外部声明_临门一脚

4.4.5  对外部声明进行语义检查的临门一脚         在前面几小节的基础上,我们基本上已经把球从后场带到对方球门前了,就差临门一脚了。在这一节中,我们来分析一下对全局变量进行语义检查的函数CheckGlobalDeclaration,和对函数定义进行语义检查的函数CheckFunction。对全局变量进行检查的主要代码如图4.4.23所示,我们省略了一些细节。图4.4.23第7行的C

2015-03-26 20:09:14 1381

原创 C编译器剖析_4.4 语义检查_外部声明_内部连接和外部连接

本节介绍C语言的内部连接Interal Linkage和外部连接External Linkage。

2015-03-26 16:31:20 1189

原创 C编译器剖析_4.4 语义检查_外部声明_结构体和数组的初始化

本节讨论标量类型、数组、结构体和联合体的初始化。在C语言中,全局或静态变量的初始化应为常量;而局部变量没有此要求。

2015-03-26 11:00:16 1420

原创 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 1346

原创 C编译器剖析_4.4 语义检查_外部声明_类型结构的构建(1)

4.4.1  类型结构的构建(1)    int * arr1[5]和int (*arr2)[5]的类型结构

2015-03-17 19:55:11 1084

原创 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 1012

原创 C编译器剖析_4.2 语义检查_表达式的语义检查(7)_二元运算符_赋值运算_条件表达式

在前文对语义检查进行简介时,我们已初步介绍过用于对二元运算符表达式进行语义检查的函数CheckBinaryExpression,为了阅读方便,这里我们再次给出图4.2.2。在本小节中,我们准备对第1126至1144行中的各个函数进行讨论。图4.2.2 CheckBinaryExpression()    对于形如a+b的二元运算表达式,我们要通过在前面章节中介绍的函数Commo

2015-03-15 12:23:07 1218

原创 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 1144

原创 C编译器剖析_C类型系统_相容类型Compatible Type

在前文对函数调用进行语义检查时,我们用函数CanAssign()来判断“能否能把实参赋值给形参”,在函数CanAssign中,我们又用宏IsCompatiblePtr来判断两个指针是否相容。而如果指针变量T1 * ptr1和T2 * pt2相容,则意味着类型T1和T2是相容的。UCC编译器中ucl\type.c的函数IsCompatibleType()用于判断类型是否相容,与类型系统相关的数据结构

2015-03-10 20:28:12 1912

原创 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 1518

原创 C编译器剖析_4.2 语义检查_表达式的语义检查(4)_函数调用

4.2.4 函数调用的语义检查     在这一小节中,我们来讨论一下函数调用的语义检查,语法上,函数调用对应的表达式属于后缀表达式PostfixExpression,UCC编译器exprchk.c的函数CheckFunctionCall()完成了对函数调用的语义检查,如图4.2.18所示。在阅读这份代码时,需要对语法分析后为函数调用构造的语法树有较好认识,请先参照”图3.1.21后缀运算符对应

2015-03-06 15:15:15 1895

原创 C编译器剖析_C语言的变参函数

C语言的变参函数    UCC编译器中有不少地方使用了C语言的变参函数,这里我们专门用一小节来对C语言变参函数的实现原理进行分析。C标准库中的printf函数就是一个典型的变参函数,其接口如下所示,函数声明中的省略号…表明这是一个变参函数。         int printf(const char *format, ...);    下面我们举一个简单的例子来说明printf函数的调

2015-03-03 20:29:57 1936

原创 C编译器剖析_4.2 语义检查_表达式的语义检查(3)_字符串与标识符

4.2.3            在这一小节,我们先来分析一下基本表达式PrimaryExpression的语义检查,由C的标准文法,我们可以知道与PrimaryExpression相关的产生式如下所示,即加了一对小括号的表达式(Expression)在语法上也相当于标志符ID、常量CONST和字符串StringLiteral。primary-expression:

2015-03-03 11:29:26 1716

原创 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 1155

原创 C编译器剖析_4.2 语义检查_表达式的语义检查(1)

在这一节中,我们来分析一下exprchk.c中的代码。我们通过调用图4.2.1中的函数CheckExpression(),来对表达式所对应的语法树结点进行语义检查。虽然表达式结点对应的kind域皆为NK_Expression,但它们的运算符op域还是各有区别的,由此我们可以为不同的运算符表达式构造不同的语义检查函数,然后把这些函数放置在一张函数表ExprCheckes中,用运算符op作为下标,去调

2015-02-27 15:58:58 1474

原创 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 2150

原创 C编译器剖析_3.3 语法分析_C语言的外部声明(3)

在上一小节中,我们讨论了C标准文法中与声明Declaration有关的几个非终结符。在此基础上,接下来让我们看看ucl\decl.c中与“声明说明符DeclarationSpecifiers”和“声明符Declarator”相关的代码。函数ParseCommonHeader()分析了“声明说明符”和“声明符”,图3.3.10 ParseCommonHeader()    图3.

2015-02-10 15:07:36 1727

C编译器剖析PDF文档_UCC162.3源代码

包含C编译器剖析PDF文档,及修改少量BUG后的编译器源代码UCC162.3, 欢迎访问对应博客http://blog.csdn.net/sheisc

2015-05-06

C编译器源代码

C编译器的源代码,在这里,彻彻底底读懂C. 适用于x86上的Windows/Linux 平台.

2015-01-04

空空如也

TA创建的收藏夹 TA关注的收藏夹

TA关注的人

提示
确定要删除当前文章?
取消 删除