Bonky
Neither beliver nor reject anything, because any other person has rejected of believed it. Heaven has given you a mind for judging truth and error, Use it.
By Thomas Jefferson

编译原理 – 开始

professor: 王翠荣

contact: wangcr@neuq.edu.cn

office: 综合楼1430

编译器

编译器的执行结构

![](https://bonky-picture.oss-cn-beijing.aliyuncs.com/pic/2019-09-02-090134.png)phases of compiler

下面是一个具体的编译器执行的例子:

词法分析 Lexical Analysis/Scanning

识别源代码中的单词 token 。单词符号是由词法规则规定的。词法分析工具有Lex

对于上面的例子,词法分析器将按照以下步骤执行词法分析:

语法分析 Syntax Analysis/Parsing

构造出一个唯一的一颗语法树。语法分析工具有Yacc

语义分析 Semantic Analysis

负责检查程序上下文相关的属性,利用符号表检查语法树是否符合语言定义的规范。一般来说,语义分析包括以下内容:

  • 确定类型:确定标识符所关联的数据对象的数据类型。
  • 类型检查:按照语言的类型规则,对运算及运算分量进行类型检查,必要时做出相应类型转换。
  • 识别含义:根据程序设计语言的语义定义,确定各个构造部分组合后的含义,做出相应处理(生成中间代码或者目标代码)。
  • 静态语义检查:比如控制流检查,嵌套层数检查。

中间代码生成

常用的方法有下面三种:

逆波兰表示法: 逆波兰记法中,操作符置于操作数的后面。例如表达“三加四”时,写作“3 4 +”,而不是“3 + 4”。如果有多个操作符,操作符置于第二个操作数的后面,所以常规中缀记法的“3 – 4 + 5”在逆波兰记法中写作“3 4 – 5 +”:先3减去4,再加上5。使用逆波兰记法的一个好处是不需要使用括号。例如中缀记法中“3 – 4 * 5”与“(3 – 4)*5”不相同,但后缀记法中前者写做“3 4 5 * -”,无歧义地表示“3 (4 5 *) −”;后者写做“3 4 – 5 *”

三元式: (op,arg1,arg2)

四元式: (op,arg1,arg2,result)

代码优化

机器无关的代码优化步骤试图改进中间代码,以便生成更好的目标代码。“更好”通常意味着更快,但是也可能会有其他目标,如更短的或能耗更低的目标代码。

代码生成

代码生成器以源程序的中间表示形式作为输入,并把它映射到目标语言。如果目标语言是机器代码,那么就必须为程序使用的每个变量选择寄存器或内存位置。然后,中间指令被翻译成为能够完成相同任务的机器指令序列。代码生成的一个至关重要的方面是合理分配寄存器以存放变量的值。

符号表

记录源程序中使用的变量的名字,收集每个变量的各种属性信息,如类型、作用域、分配存储信息。

编译过程中的前端和后端以及趟(pass)

  • 词法分析、语法分析、语义分析,以及中间代码生成可以被组合在一起成为前端趟。一般来说不同高级语言都有着不同的前端。
  • 代码优化可以作为一个可选的趙。
  • 然后可以有一个为特定目标机生成代码的后端趟。一般来说对应不同的硬件平台。

前端趟与后端趟可以分离,组合出不同的编译器。(参考苹果 LLVMClang 的发展史)

2010112421324033

编译器和解释器区别

以下内容很多转自并翻译于:https://techdifferences.com/difference-between-compiler-and-interpreter.html

See the source image

Compiler Interpreter
执行 一次执行整个程序 一次执行一行代码
中间目标代码 产生中间目标代码 不产生中间目标代码
工作机制 先编译在执行 边编译边执行
效率 相对较快 相对较慢
内存需求 因为要产生中间代码,所以需要要更多内存 节省内存
报错 在编译完成后,同时显示所有错误 一次显示一个错误
勘误难度 相对简单
编程语言 C, C++, C#, Scala, typescript uses compiler. PHP, Perl, Python, Ruby uses an interpreter.

Java 的机制

图片 1

GCC

屏幕快照 2019-08-28 下午9.25.34

一次 gcc 编译流程的体验

源代码如下

屏幕快照 2019-08-28 下午9.27.27

第一步,生成预处理文件,大概类似下面这样,一个850多行。。然而看不懂

屏幕快照 2019-08-28 下午9.24.17

第二步,生成汇编程序,这个还是比较直观的,虽然有一些还是不理解

屏幕快照 2019-08-28 下午9.29.47

第三步,生成目标代码。发现一个有意思的问题,利用-o 选项生成的可以直接执行,我一步一步生成的无法直接执行,利用 chmod 给权限也无法执行 ==(已解决,原因是-o 选项后面跟的是目标的文件名,即便是以.o 结尾,但不过 helo.c 前没有选项,默认是直接完成整个编译,实际上现在这里的 helo.o为可执行文件)==

屏幕快照 2019-08-28 下午9.35.14

第四步,生成可执行文件

屏幕快照 2019-08-28 下午9.38.02

说说GCC,LLVM和Clang

CompilerOptions

Apple(包括中后期的NeXT) 一直使用GCC作为官方的编译器。GCC作为开源世界的编译器标准一直做得不错,但Apple对编译工具会提出更高的要求。

一方面,是Apple对Objective-C语言(甚至后来对C语言)新增很多特性,但GCC开发者并不买Apple的帐——不给实现,因此索性后来两者分成两条分支分别开发,这也造成Apple的编译器版本远落后于GCC的官方版本。另一方面,GCC的代码耦合度太高,不好独立,而且越是后期的版本,代码质量越差,但Apple想做的很多功能(比如更好的IDE支持)需要模块化的方式来调用GCC,但GCC一直不给做。甚至最近,《GCC运行环境豁免条款 (英文版)》从根本上限制了LLVM-GCC的开发。

所以,这种不和让Apple一直在寻找一个高效的、模块化的、协议更放松的开源替代品,于是Apple请来了编译器高材生Chris Lattner。他的硕士毕业论文提出了一套完整的在编译时、链接时、运行时甚至是在闲置时优化程序的编译思想,直接奠定了LLVM的基础。LLVM在他念博士时更加成熟,使用GCC作为前端来对用户程序进行语义分析产生IF(Intermidiate Format),然后LLVM使用分析结果完成代码优化和生成。这项研究让他在2005年毕业时,成为小有名气的编译器专家,他也因此早早地被Apple相中,成为其编译器项目的骨干(PS:Chris Lattner也是后来的Swift之父)。

img

Apple吸收Chris Lattner的目的要比改进GCC代码优化宏大得多——GCC系统庞大而笨重,而Apple大量使用的Objective-C在GCC中优先级很低。此外GCC作为一个纯粹的编译系统,与IDE配合得很差。加之许可证方面的要求,Apple无法使用LLVM 继续改进GCC的代码质量。于是,Apple决定从零开始写 C、C++、Objective-C语言的前端 Clang,完全替代掉GCC。在2009年的时候,Clang已经完全可以用于生产环境。

Clang相对于GCC速度快了数倍以上,然且内存占用只有GCC的十分之一,易于学习。但不过Clang支持的C++版本落后于GCC,而且GCC支持更多的平台,所以使用GCC还是Clang还需要根据情况而定。

Share

You may also like...

发表评论

电子邮件地址不会被公开。 必填项已用*标注