编译原理及实践-第一章

chapter-1 introduce

本章根据2021.9.7蹭课记忆记录

1.1为什么要用编译器

从源程序到可执行目标文件的过程为:

源程序------>编译器--------->目标程序

  • 在这个过程中,首先源程序由高级语言写的话,需要翻译成机器语言。如果源程序大的话,那么这个过程手写会很复杂。
    • 比如token,人工处理每次都要处理,编译器就帮助处理扫
  • 假如不要编译器,直接由hardware machine来支持就存在一些问题
    • 源程序的高级语言存在更新,也就是语言本身是更新的。这样每次更新都要带着目标程序下面更新。
    • hardware machine 也存在更新,所以一旦更新要迭代一下对源程序的支持是不现实的。
    • 因此需要有一个编译器,把源程序翻译成机器的指令。对源程序的scaning获得token,context-free grammers and parser(CFG)语法分析,Top-Down Parsing和Bottom-Up Paring 两种优化语法树,Semantic analysis语义分析(以上为编译的front-end)
    • 后面的是编译器的优化,比如何时展开,何时放入寄存器属于back-end。这部分的教学绝大部分处于研究生教学阶段。
  • 而且编译器需要的抽象程度比较高,抽象也是这门课要教导的一个核心思想。
  • 其中将语言分级,分级是为了定义标准,方便语义分析和语法分析。
  • 其中有限自动机理论是一个基础

1.2与编译器相关的程序

  1. 解释器(interpreter)
    1. 与编译器不同的地方在于,解释器是一个同步翻译。即来一句翻译一句。比如Javascript。编译器是整个源程序的翻译
  2. 汇编器(assembler)
    1. 预处理,编译,汇编,链接。汇编器将编译后的文件翻译成汇编文件,汇编是机器语言的助记符。
  3. 链接器(linker)
    1. 将多个相关的代码文件链接起来
  4. 加载器(loader)
    1. 将可重定位的文件加载入内存
  5. 预处理器(preprocessor)
    1. 进行头文件的展开/宏的替换/条件编译/去掉注释
  6. 编辑器(Edit)
    1. 比如IDE里面代码文本区
  7. 调试程序debugger
  8. 描述器(profiler)
    1. 执行中搜集目标程序行为统计的程序
  9. 项目管理程序(project manager)
    1. IDE里面的项目管理文件

1.3编译步骤

  1. 扫描程序(scanner)
    1. 编译器实际阅读源程序,扫描程序执行词法分析
      1. 比如 a[index] = 4 +2
      2. 扫描后的结果是 a,[ ,index , ] ,=,4,+,2这里是记录下来的都是token。但是比如说数字4和2,他们该抽象成数字符号。不然再随便来点数字都是无穷无尽的token。
      3. 因此以上的token抽象结果为标识符,左括号,标识符,右括号,赋值,数字,加号,数字
    2. [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YT8Je4YD-1637322735306)(C:\Users\颜\AppData\Roaming\Typora\typora-user-images\image-20210907170114973.png)]
  2. 语法分析程序(parser)
    1. 由于单纯的token无法确定token的排列问题,所以需要进行语义分析建立语法树
    2. [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PioIAmfl-1637322735309)(C:\Users\颜\AppData\Roaming\Typora\typora-user-images\image-20210907171208602.png)]
    3. 语法分析程序从扫描程序中获取记号形式的源代码,并完成定义程序结构的语法分析,这与自然语言中句子的语法分析类似。语法分析定义了程序的结构元素及其关系。通常将语法分析的结果表示为分析树(parse tree)或语法树(syntax tree) 【上图称为语法树】
    4. 附:编程语言的语法规则并不是胡乱取的,而是根据一个语言学家的语言分级规则来制定的,这样制定出来的规则方便编译器进行语法分析。
    5. 满足了这样一颗树的结构当前语法就是合法的。
    6. 分析树对显示程序的语法或程序元素很有帮助,但是对于表示结构不是很好。分析程序更趋向生成语法树。语法树是分析树中所含信息的浓缩。
    7. 语法树[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uHfbW0fp-1637322735311)(C:\Users\颜\AppData\Roaming\Typora\typora-user-images\image-20210907171910279.png)]
  3. 语义分析程序(semantic analyzer)
    1. 程序的语义就是它的“意思”,它与语法或结构不同。程序的语义确定程序的运行
    2. 例如
      1. a[index]=4+2
        1. a是一个整型数组,它带有来自整型子范围的下标;index是一个整型变量。接着语义分析程序将用所有的子表达式类型来标注语法树,并检查赋值是否使这些类型有意义了,如若没有,则声明一个类型匹配错误。
      2. int i; i=3.14;
        1. 这里i是整型,3.14是float常量,编译器默认进行隐式强制类型转换,但是编译器会报waring
    3. 有关语法树的语义分析结果可用以下注释了的树来表示
      1. ​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EeUTalcF-1637322735312)(C:\Users\颜\AppData\Roaming\Typora\typora-user-images\image-20210907172748490.png)]
  4. 源代码优化程序(source code optimizer)
    1. 编译器包括许多代码改进或者优化步骤。例如:
      1. 在csapp中学到的循环展开,累积变量提高并行量,利用cache的结构来重新组织代码,尝试用条件传送而非条件跳转。
      2. 上例中,表示4+2可由编译器计算先得到结果6(这种优化称为常量合并)。
      3. 其中涉及到三元式代码
    2. 编译器优化的部分并不是本科重点内容,绝大部分内容是在研究生的(当然针对的是非世界前top20学校)
  5. 代码生成器(code generator)
    1. 代码生成器得到中间代码(IR),并生成目标机器的代码。
  6. 目标代码优化程序(target code optimizer)
    1. 在这个阶段中,编译器尝试着改进由代码生成器生成的目标代码。这种改进包括编址模式以提高性能,将速度慢的指令更换成速度快的指令,以及删除多余的操作。例如:
      1. csapp中提高的将乘法指令改成移位指令的结合。

1.4编译器中的主要数据结构

编译器的阶段使用的算法与支持这些阶段的数据结构之间的交互式非常强大的。

  1. 记号(token)
    1. 当scanner程序将字符收集到记号中的时候,通常以符号表示这个记号。也就是说,作为一个枚举数据类型的值来表示源程序的记号集。
  2. 语法树(syntax tree)
    1. 如果分析程序确实生成了语法树,它的构造通常为基于指针的标准结构,在进行分析时动态分配该结构,则整棵树可作为一个指向根节点的单个变量保存。
    2. 结构中的每一个节点都是一个记录,它的域表示由分析程序和之后的语义分析程序收集的信息。例如,一个表达式的数据 类型可作为表达式的语法树节点中的域。
    3. 有时为了节省空间,这些域也是动态分配或存放在诸如符号表的其他数据结构中,这样就可以有选择地进行分配和释放。
  3. 符号表(symbol table)
    1. 这个数据结构中的信息与标识符有关:函数、变量、常量以及数据类型。
    2. 符号表几乎与编译器的所有阶段交互:扫描程序、分析程序或将标识符输入到表格中的语义分析程序;语义分析程序将增加数据类型和其他信息;优化阶段和代码生成阶段也将利用由符号表提供的信息选 出恰当的代码。
    3. 因为对符号表的访问如此频繁,所以插入、删除和访问操作都必须比常规操作更有效。尽管可以使用各种树的结构,但杂凑表却是达到这一要求的标准数据结构。有时在一 个列表或栈中可使用若干个表格。
    4. 这个符号表应该就是csapp中链接那一章节的符号表。
  4. 常数表(literal table)
    1. 常数表的功能是存放在程序中用到的常量和字符串,因此快速插入和查找在常数表中也十分重要。但是,在其中却无需删除,这是因为它的数据全程应用于程序而且常量或字符串在该表中只出现一次。
    2. 通过允许重复使用常量和字符串,常数表对于缩小程序在存储器中的大小显 得非常重要。在代码生成器中也需要常数表来构造用于常数和在目标代码文件中输入数据定义的符号地址。
    3. 我想可以理解成静态区,公共代码段,即链接中提到的存储已经初始化全局变量或者全局变量的区域。
  5. 中间代码(intermediate code)
    1. 根据中间代码的类型(例如三元式代码和P -代码)和优化的类型,该代码可以是文本串 的数组、临时文本文件或是结构的连接列表。对于进行复杂优化的编译器,应特别注意选择允许简单重组的表示。
    2. 暂时不懂
  6. 临时文件(temporary file)
    1. 计算机过去一直未能在编译器时将整个程序保留在存储器中。
    2. 这一问题已经通过使用临时文件来保存翻译时中间步骤的结果或通过“匆忙地”编译(也就是只保留源程序早期部分的足 够信息用以处理翻译)解决了。
    3. 存储器的限制现在也只是一个小问题了,现在可以将整个编译单元放在存储器之中,特别是在可以分别编译的语言中时。但是偶尔还是会发现需要在某些运行步骤中生成中间文件。其中典型的是代码生成时需要反填( b a c k p a t c h)地址。
    4. 目前不懂

1.5编译器结构中的其他问题

  1. 分析和综合
    1. 对于前面的语法分析和语义分析,用到的是分析的思想。后面生成翻译代码的时候用到的综合的思想
  2. 前端和后端
    1. 大致可以在“中间代码”的步骤划分前后端
  3. pass
    1. 编译器发现,生成代码之前多次处理整个源程序很方便。这些重复就是pass。
    2. 比如有1次pass完成和3次pass完成,两者完全是不同的技术
  4. 语言定义和编译器
    1. 程序设计语言的词法和语法结构通常用形式的术语指定,并使用正则表达式和上下文无关法
  5. 编译器的选项和界面

猜你喜欢

转载自blog.csdn.net/zstuyyyyccccbbbb/article/details/121429825
今日推荐