C编译器剖析_尾声

尾声

总有曲终人散时,不知不觉我们已经完成了对UCC编译器的剖析,一路走来,最深的体会仍然是“纸上得来终觉浅,绝知此事要躬行”。按这个道理,理解UCC编译器的最好办法应是“直接阅读其源代码,思考UCC编译器在不同的执行点应处于怎样的状态,加入一些打印语句,输出相应的调试信息来验证自己的判断是否正确,如果发现Bug,就写一些测试程序来触发Bug,然后修改UCC编译器的源代码”。在遇到困惑时,或许拙作能带来一点点的帮助和提示,但拙作不能代替,也不应取代对UCC源代码的主动阅读。源代码分析的相关书籍很容易带来一个副作用,即读者在书籍的牵引下被动地看书,而忽视了对源代码本身的主动阅读。主动思考和被动阅读所带来的效果是完全不一样的。

LLVM和GCC的功能已经足够强大,也有足够庞大的程序员社区在支撑其不断发展和改进。而UCC几乎没有可能与这些编译器媲美,UCC编译器最大的优点在于“它的规模和复杂度都在一个C程序员单枪匹马就可以掌控的范围内”。只要我们愿意付出努力,我们一定可以较好地搞明白UCC编译器,从而更好地用好C语言这个简洁用力的利器。当然能基本读懂UCC编译器,与能像UCC原作者([email protected])那样写出UCC还是有一定差距的。UCC编译器最适合的读者可能是在校的大学生,有时间,有精力,且暂时还不用考虑“写代码养家糊口”,可以静下心来打好专业基本功。能有机会在一线参与编译器开发的程序员少之又少,但攻克UCC编译器这座山头的意义在于“它能让我们获得攻克其他山头的能力”,这可能就是所谓磨刀不误砍材功。当然,选择怎样的山头来练兵,源于我们自己的兴趣。从打基本功的角度出发,我们可以选择其他自己感兴趣的山头来攻克,比如麻省理工大学的XV6操作系统,而不一定要选择UCC编译器。XV6操作系统是UnixV6系统在X86平台上的重生,若时间允许,或许我们可以再一起进行“XV6操作系统剖析”。

每个人心中都有自己的偶像,当我们在谈论某某是我的偶像时,其潜台词往往是“我们希望自己能成为那样的人”。绝大部分人都无法达到自己偶像的高度,但偶像很大程度上会指引我们前进的方向。20世纪70年代诞生的C和Unix,影响了一代又一代的程序员,对IT整个行业也产生了深远的影响。阅读“UCC源代码和XV6源代码”,比“捧着编译原理和操作系统的教材”,应会更有趣得多,这也是我们向伟大的先行者Dennis
Ritchie和Ken Thompson致敬的最好办法,其实也是学习编译原理和操作系统的最有效方法之一。有了源代码级的感性认识后,再去补充理论知识,才会得到更好的升华。

CPU由于物理上的限制,其主频已不再无限提高了,摩尔定律在单CPU上已渐渐失效,现在和未来的方向应是多核、分布式和并行。在分布式或者并行的环境下,如何为程序员提供一个易用的编程环境,或许是未来若干年编译方向的研究热点。当然还有优化,毕竟我们始终在追求“没有最好,只有更好”。

限于水平和能力,书中不当之处敬请批评与指正。

[email protected]

时间: 2024-10-16 20:01:51

C编译器剖析_尾声的相关文章

C编译器剖析_1.5 结合C语言来学汇编_指针、数组和结构体

让我们再来看一份C代码,及其经UCC编译器编译后产生的主要汇编代码,如图1.33所示,其中包含了数组.指针和结构体. 图1.33 数组.指针和结构体 按照C的语义,图1.33第9行的C代码是对局部数组number的初始化,需要把number[0]初始化为2015,而数组中的其他元素皆被初始化为0.UCC编译器采取的翻译方法是:先调用memset函数来把数组number所占的内存空间清0,然后再把number[0]设为2015,如图1.33的第17至24行所示.C库函数memset的API如下所示

Oracle监听网络服务全面剖析_超越OCP精通Oracle视频教程培训06

课程目标 Oracle视频教程,风哥oracle教程培训学习内容包括,Oracle监听概念与常用配置文件,监听工具与服务测试,oracle监听静态注册与动态注册,配置oracle第二监听,oracle监听与客户端配置,Oracle监听日志配置与日常维护规范,Oracle监听安全与密码管理及防火墙,db_link与进程,如何跟踪问题深入分析等超越oracle认证的数据库教程 适用人群 IT相关从业人员.Oracle数据库技术人员.想加工资的.想升职的都可以. 课程简介 Oracle监听网络服务全面

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

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

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

6.1 汇编代码生成简介 历经词法分析.语法分析.语义检查和中间代码生成阶段,我们终于来到了"目标代码生成阶段",由于UCC编译器的目标代码即为32位x86汇编代码,因此我们就把本章称为"汇编代码生成".UCC编译器中的大部分源代码都适用于Windows和Linux平台,但Windows平台上缺省的汇编器支持Intel风格的x86汇编代码,而Linux平台默认的汇编器则采用AT&T风格的x86汇编代码,两者在汇编语法上有一些差别,为节省篇幅,我们主要针对Li

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

5.1  中间代码生成与优化_简介 在语法分析和语义检查阶段,我们始终在与语句Statement.表达式Expression和外部声明ExternalDeclaration这3个概念打交道.通过声明,我们最终建立起了相应的类型结构,并在符号表中保存了相关标识符的类型信息,到了中间代码生成阶段,我们就不再需要处理外部声明ExternalDeclaration了,只需要为语句和表达式生成相应的中间代码即可.文件ucl\tranexpr.c用于为表达式生成中间代码,文件名tranexpr是Transl

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

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

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

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

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

6.3.4        为函数调用与返回产生汇编代码 在这一小节中,我们来讨论一下如何为函数调用和函数返回生成汇编代码.函数调用对应的中间指令如下所示: //中间指令的四元式: < opcode, DST, SRC1, SRC2> <CALL, 用于接收返回值的变量retVal, 函数名func,  参数列表[arg1,arg2, -,argn]> 让我们先熟悉一下C函数的调用约定CallingConvention,我们需要把参数从右向左入栈(即从argn到arg1依次入栈),不

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

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