编译原理复习总结及思维导图

Computer System 专栏收录该内容
4 篇文章 0 订阅

文章声明

在QQ群中无意中找到一篇《编译原理总结》的Word文件,感觉得很好,因此便将其整理出来并附上自己绘制的思维导图。由于文件里面没有作者信息,因此无法添附转载链接,若原作者看到并介意,还请联系删除。

第一部分 具体内容

第一章

(1) 程序设计语言
  • 机器语言: 由0、1代码构成,不需翻译就可直接执行其程序。
  • 汇编语言: 机器指令助记符(伪代码)形式,汇编后才可执行其程序。
  • 高级程序设计语言: 类自然语言和数学公式形式
(2) 基本术语
  • 源程序(Source Program):用源语言写的程序。源语言可以是汇编语言,也可以是高级程序设计语言。
  • 目标程序(Target Program):也称为“结果程序”,是源程序经翻译程序加工以后所生成的程序。目标程序可以用机器语言表示,也可以用汇编语言或其它中间语言表示。
  • 翻译程序(Translating Program):是指把一个源程序翻译成逻辑上等价的目标程序的程序。源程序为其输入,目标程序为其输出。
  • 汇编程序(Assembler):是指把一个汇编语言写的源程序转换成等价的机器语言表示的目标程序的翻译程序。
  • 编译程序(Compiler):若源程序是用高级程序设计语言所写,经翻译程序加工生成目标程序,则该翻译程序就称为“编译程序”,也可称为编译器。
  • 解释程序:是高级语言翻译程序的一种,他将源语言书写的源程序作为输入,解释一句后就提交计算机执行一句,并不形成目标程序,就像外语翻译中的“口译”一样,不产生全文的翻译文本。
  • 运行系统(Running System):目标程序执行时,需要有一些子程序(如一些连接装配程序及一些连接库等)配合进行工作,由这些子程序组成的一个子程序库称为运行系统。
  • 编译系统(Compiling System):编译程序和运行系统合称编译系统。
(3) 程序的翻译
  • 除机器语言程序外,用其它语言书写的程序都必须经过翻译才能被计算机识别。这一过程由翻译程序来完成。
  • 编译方式是一种分阶段进行的方式,包括翻译和运行两部分。
  • 前一阶段:翻译
  • 后一阶段:运行,由运行系统配合完成。
 图片不见了。。。
(4) 过程

1、词法分析阶段
这个阶段的任务是从左到右一个字符一个字符地读入源程序,对构成源程序的字符流进行扫描和分解,从而识别出一个个单词(也称单词符号或符号TOKEN)。
某源程序片断如下:
begin var sum, first, count: real; sum:=first+count10 end.
保留字 begin var real end
标识符 sum first count sum first count
界符 .
逗号, 逗号, 冒号: 分号; 加号+ 乘号
赋值号 := 整数10 10
2、语法分析阶段
是编译过程的第二个阶段。语法分析的任务是在词法分析的基础上将单词序列分解成各类语法短语,如“程序”,“语句”,“表达式”等等。一般这种语法短语,也称语法单位,或语法成分,或语法范畴。
语法分析所依据的是语言的语法规则,即描述程序结构的规则。通过语法分析确定整个输入串是否构成一个语法上正确的程序。
3、语义分析阶段
依据语言的语义规则,对语法分析得到的语法结构分析其含义以及应进行的运算,审查源程序中有无语义错误,为代码生成阶段收集类型信息。
4、中间代码生成
在进行了上述的语法分析和语义分析阶段的工作之后,有的编译程序将源程序转变成一种内部表示形式,这种内部表示形式叫做中间代码。
所谓“中间代码”是一种结构简单,含义明确的记号系统,这种记号系统可以设计为多种多样的形式。
重要的设计原则:一是容易生成;二是容易将它翻译成目标代码。
5、代码优化
任务:对前阶段产生的中间代码系列进行变换或改造。目的是使生成的目标代码更高效,即省时间省空间。例如上例四个四元式可优化为下面两个四元式。
6、目标代码生成
任务:将中间代码变换成特定机器上的绝对指令代码或可重定位的指令代码或汇编指令代码。它的工作与硬件系统结构和指令含义有关。
7、表格管理
编译过程中源程序的各种信息被保留在种种不同的表格里,编译各阶段的工作都涉及到构造、查找或更新有关的表格,因此需要有表格管理的工作;
8、出错处理
如果编译过程中发现源程序有错误,编译程度应报告错误的性质和错误发生的地点,并且将错误所造成的影响限制在尽可能小的范围内,使得源程序的其余部分能继续被编译下去,有些编译程序还能自动校正错误,这些工作称之为出错处理。

(5) 前端与后端

参考上面的图,目的是为了在多种源语言和多种目标语言的开发过程中,可以灵活搭配组合,消除重复开发的工作量,提高编译系统的开发效率。

(6) 遍

所谓遍,是对源程序或源程序的中间形式从头到尾扫视并完成规定任务的过程。
每一遍扫视可完成一个阶段或多个阶段的功能。
一遍的编译程序:以语法分析程序为核心 。
多遍扫描的优点

  • 可以减少内存容量的需求,分遍后,以遍为单位分别调用编译的各个程序,各遍程序可以相互覆盖。
  • 可使各遍的编译程序相互独立,结构清晰。
  • 能够进行充分优化,产生高质量的目标程序。
  • 可将编译程序分为前端和后端,有利于编译程序的移植。

多遍扫描的缺点
每遍都要读符号、送符号,增加了许多重复性的工作,降低编译效率。

(7) 程序设计语言范型(从支持的计算模式)
  • 强制(命令)式语言:是面向动作的,即一个计算过程看做是一系列动作,其动作是命令驱动,以语言形式表示。也称过程式语言,如C,FORTRAN等;
  • 函数式语言:注重程序表示的功能 也称应用式语言,如ML和LISP等;
  • 基于规则的语言:检查一定的使能条件,满足时执行动作 也称逻辑程序设计语言,如PROLOG.
  • 面向对象语言:提供抽象数据类型,支持封装性、继承性和多态性。 如C++和Java等。

第二章

(1) 符号和符号串

1、字母表:元素的有穷非空集合。
2、符号串:由字母表中的符号组成的任何有穷序列。
3、符号串的头尾,固有头和固有尾:如果z=xy是一符号串,那么x是z的头,y是z的尾,如果x是非空的,那么y是固有尾;同样如果y非空,那么x是固有头。
如:设z=abc,那么z的头是 ε \varepsilon ε,a, ab, abc, 除abc外,其它都是固有头;z的尾是 ε \varepsilon ε, c, bc, abc, z的固有尾是 ε \varepsilon ε, c, bc。
4、符号串的运算
(1)符号串的连接:设x和y是符号串,x和y的连接xy是把y的符号写在x的符号后得的符号串。
如:x=ST, y=abu, 则xy=STabu 显然有 ε \varepsilon εx=x ε \varepsilon ε=x。
(2)符号串的方幂:设x是符号串,把x自身连接n次得x的几次方幂xn。
如:设x=ab则 x 0 x^0 x0= ε \varepsilon ε x 1 x^1 x1=ab x 1 x^1 x1=abab x 3 x^3 x3=ababab
(3)符号串集合的乘积:设A和B为符号串集合,则A和B的乘积定义为AB={xy|x ∈ \in A且y ∈ \in B}
如:a={a, b}, B={00, 11} 则AB={a00, a11, b00, b11} 显然:{ ε \varepsilon ε}A=A{ ε \varepsilon ε}=A
(4)符号串集合的方幂:设A为符号串集,则A的n次方幂An定义为:An=AA……A=AAn-1=An-1A
(5)符号串集合的正闭包A+:A+=A1 U A2 U … U An U …
(6)符号串集合的闭包A*:A*=A0 U A+ = { ε \varepsilon ε} U A+
如:设有正字母表 ∑ \sum ={0,1} 则 ∑ \sum *= ∑ 0 \sum0 0 U ∑ 1 \sum1 1 U ∑ 2 \sum2 2 U … U ∑ n \sum n n U …={ ε \varepsilon ε, 0, 1, 00, 01, 10, 11, 000, 001,……}

(2)文法

1、文法G定义为四元组 V N V_N VN V T V_T VT,P,S)其中:
(1)VN 为非终结符号集
非终结符号表示一个语言短语(或语法成分、语法单位)。 如 程序、语句、表达式等。一般用大写字母或用〈 〉括起表示非终结符号。
(2)VT 为终结符号集
终结符号:组成语言的基本符号。是文法中不属于非终结符号集合的符号。一般用小写字母或不带〈 〉的符号表示。如程序设计语言的单词符号。
设V=VN U VT,称V为文法G的字母表。
(3)P 为产生式(也称规则)的集合
产生式的形式: α \alpha α β \beta β α \alpha α∷= β \beta β,其中 α \alpha α∈V+, β \beta β∈V*
(4)S 称作识别符号或开始符号,是一个非终结符号
一般表示此文法定义的最大语法短语,至少要在一条产生式中作为左部出现。
2、句型、句子的定义
设G[S]是一文法,如果符号串x是从识别符号推导出来的,即有S*x, 则称x是文法G[S]的句型。
若x仅由终结符号组成,即S ⇒ \Rightarrow *x, x ∈ \in VT ,则称x为G[S]的句子。
句型:在一棵树生长过程的任何时刻,所有那些端末结点自左至右的排列,就是一个句型。
语言的定义:文法G产生的语言记为L(G),它是文法G产生的全部句子的集合。
文法等价定义:若L(G1)=L(G2)则称文法G1和G2是等价的。

(3)文法的类型

0型文法(短语文法):定义0型语言,对应Turing机(图灵机);
1型文法(上下文有关文法):定义1型语言,对应线性限界自动机;箭头后面的要比前面的长或相等
2型文法(上下文无关文法):定义2型语言,对应非确定下推自动机;箭头前面的是非终结符,后面是串
3型文法(正规文法):定义3型语言,对应有限自动机。非终结符可以推出一个终结符或一个终结符和一个非终结符

 图片不见了。。。

最右推导也称为规范推导,所得句型称为规范句型
如果一个文法存在某个句型对应两棵不同的语法树,则说这个文法是二义的。或者说,若一个文法中存在某个句型,它有两个不同的最左(最右)推导,则这个文法是二义的。
上下文无关文法是否具有二义性是不可判定的
但有些特殊的2型文法[例如LL(1)、LR(0)、LR(1)等文法]是无二义性的。
一个文法兼有左递归和右递归是导致二义性的常见原因。
排除文法二义性通常有两种方法:
(1)在语义上加些限制
(2)重新构造一个无二义性的文法

(4) 句型的分析

句型的分析:就是识别一个符号串是否为某文法的句型。是某个推导的构造过程。
分析方法分两大类:自上而下分析法和自下而上分析法 推导与归约,最右推导是规范推导,逆过程为规范规约
S ⇒ ∗ α A δ ⇒ + α β δ S\Rightarrow *\alpha A \delta \Rightarrow + \alpha\beta\delta SαAδ+αβδ(由 A ⇒ + β A\Rightarrow +\beta A+β得)则称 β \beta β是句型 α β δ \alpha\beta\delta αβδ相对于非终结符A的短语。【间接推导】
S ⇒ ∗ α A δ ⇒ α β δ S\Rightarrow *\alpha A \delta \Rightarrow \alpha\beta\delta SαAδαβδ (由 A ⇒ β A\Rightarrow \beta Aβ得)则称 β \beta β是句型 α β δ \alpha\beta\delta αβδ相对于A→ β \beta β直接短语(也称简单短语)。【直接推导】
一个句型的最左直接短语称为该句型的句柄
一棵子树(至少要有父子两代)的所有端末结点自左至右排列起来形成相对于子树根的短语。若子树只有父子两代,则得到直接短语。

(5) 有关文法

(1)有害规则 文法中含形如U→U的产生式。
它对描述语言没有必要,且会引起文法的二义性。
(2)多余规则 文法中任何一个句子的推导都用不到的规则。
(3)无用规则 文法中含形如U→V的产生式,即单产生式。
为保证文法G的任一非终结符A在句子推导中出现,必须满足如下两个条件
(1)A必须在某句型中出现, α A δ \alpha A\delta αAδ
(2)必须能够从A推导出终结符号串t。
有关文法的化简和改造,包括以下几项工作
(1)无用符号和无用产生式的删除。
(2) ε \varepsilon ε-产生式的消除。
(3)单产生式的消除。
(4)左递归的消除。

第三章
(1)词法分析输出

单词符号(TOKEN) 是一个程序设计语言的基本语法符号。程序设计语言的单词符号一般可分成下列5种:
1.基本字,也称关键字,如PASCAL语言中的begin,end,if,while和var等。
2.标识符,用来表示各种名字,如常量名、变量名和过程名等。
3.常数,各种类型的常数,如25,3.1415,TRUE和"ABC"等。
4.运算符,如+,*,<= 等。
5.界符,如逗点,分号,括号等。
词法分析程序所输出的单词符号常常采用下二元式表示:(单词种别,单词自身的值)
可用整数码或助记符等表示。

(2)单词的描述工具

程序设计语言中的单词(TOKEN)是基本语法符号。单词符号的语法可以用有效的工具加以描述。
正规式和它所表示的正规集的递归定义如下。设字母表为∑,辅助字母表∑ ={ |, ·, *, (, ) }
定义(正规式和它所表示的正规集):
设字母表为Σ,辅助字母表Σ`={Φ,ε,|,·,*,(, }。
② ε和Φ都是Σ上的正规式,它们所表示的正规集分别为{ε}和{ };
② 任何a∈Σ,a是Σ上的一个正规式,它所表示的正规集为{a};
③ 假定e1和e2都是Σ上的正规式,它们所表示的正规集分别为L(e1)和L(e2),那么,(e1), e1|e2, e1·e2, e1*也都是正规式,它们所表示的正规集分别为L(e1), L(e1)∪L(e2), L(e1)L(e2)和(L(e1))*。
④ 仅由有限次使用上述三步骤而定义的表达式才是Σ上的正规式,仅由这些正规式所表示的字集才是Σ上的正规集。

(3)有穷自动机

有穷自动机(也称有限自动机)作为一种识别装置,它能准确地识别正规集,即识别正规文法所定义的语言和正规式所表示的集合,引入有穷自动机这个理论,正是为词法分析程序的自动构造寻找特殊的方法和工具。
确定的有穷自动机(DFA)
定义:一确定的有穷自动机(DFA)M是一个五元组: M=(K,∑ ,f, S, Z)其中
1.K是一个有穷集,它的每个元素称为一个状态;
2.∑ 是一个有穷字母表,它的每个元素称为一个输入字符,所以也称为输入符号字母表;
3.f是转换函数,是在K×∑ →K上的映像,即,如f(ki,a)= kj, 就意味着,当前状态为ki,输入字符为a时,将转换到下一状态kj,我们把kj称为ki的一个后继状态;
4.S 是唯一的一个初态;
5.Z是一个终态集,终态也称可接受状态或结束状态。
DFA的作用
对于∑*中的任何字符串t,若存在一条从初态到某一终态结的道路,且这条路上所有弧的标记符连接成的字符串等于t,则称t可为DFA M所接受,若M的初态结同时又是终态结,则空字可为M所识别。
DFA M所能接受的字符串的全体记为L(M)。
不确定的有穷自动机(NFA)
定义:一个不确定的有穷自动机(NFA)M是一个五元组,M=(K,∑,f, S, Z)。
3.f是一个从K×∑*到K的幂集的映象
4.S属于K,是一个非空初态集
5.Z属于K,是一个终态集
区别
DFA:只有唯一初态。NFA:有初态集。
DFA是NFA的特例。
对于每个NFA M,存在一个DFA M’ ,使得L(M)=L(M’ )
对于任何两个有穷自动机M和M’ ,如果L(M)=L(M ’ ),则称M与M’ 是等价的。

第四章
(1)自顶向下语法分析

语法分析的作用是识别由词法分析给出的单词符号序列是否是给定文法的正确句子。
其中自顶向下分析,就是从文法的开始符号出发企图推导出与输入的单词串完全匹配的句子,若输入串是给定文法的句子,则必能推出,反之必然出错。
FIRST( α \alpha α={a| α ⇒ a β , a ∈ V T , α , β ∈ V ∗ \alpha \Rightarrow a \beta, a \in V_T, \alpha,\beta \in V^* αaβ,aVT,α,βV}
(1)求FIRST(x), x ∈ \in V
(a)若x ∈ \in VT,则FIRST(x)={x}
(b)若x是 ε \varepsilon ε,则FIRST(x)={ ε \varepsilon ε}
(c)若x ∈ \in VN,且x→y1y2…ym|…|z1z2…zn
则FIRST(x)= FIRST(y1y2…ym) ∪…∪FIRST(z1z2…zn)
(2)求FIRST(y1y2…ym), 其中 y1, y2, …, ymV。
(a)若y1 ∈ \in VT, 则FIRST(y1y2…ym)={y1}
(b)若y1 ∈ \in VN, ε ∉ \varepsilon \notin ε/ FIRST(y1),则FIRST(y1y2…ym)=FIRST(y1)
ε ∈ \varepsilon \in εFIRST(y1),则 FIRST(y1y2…ym)=(FIRST(y1)\{ ε \varepsilon ε})∪FIRST(y2y3…ym)
按上法求FIRST(y2y3…ym),类推下去。

对文法中每一 A ∈ V N A \in V_N AVN,计算FOLLOW(A)
(a)设S为文法的开始符号,则# ∈ \in FOLLOW(S)
(b)若有A→ α B β \alpha B\beta αBβ,则将FIRST( β \beta β)-{ ε \varepsilon ε}加入到FOLLOW(B)中,如果其中 β ⇒ ε \beta \Rightarrow \varepsilon βε ,则将FOLLOW(A)加入到FOLLOW(B)中。
计算SELECT集
定义:SELECT ( A→ α \alpha α ) =FIRST ( α \alpha α ),其中 α ⇒ ε \alpha \Rightarrow \varepsilon αε
α ⇒ ε \alpha \Rightarrow \varepsilon αε ,则SELECT ( A→ α \alpha α ) = (FIRST( α \alpha α ) - { ε \varepsilon ε}) ∪FOLLOW(A)
判定LL(1)文法
对每个非终结符A的两个不同产生式,A→ α \alpha α,A→ β \beta β,满足SELECTC(A→ α \alpha α)∩SELECT(A→ β \beta β )= ϕ \phi ϕ其中 α \alpha α β \beta β不能同时 ⇒ ε \Rightarrow \varepsilon ε

(2) 非LL(1)文法到LL(1)文法的等价变换

由LL(1)文法的定义可知,若文法中含有直接或间接左递归,或含有左公共因子,则该文法肯定不是LL(1)文法。
参考:http://jpkc.gdut.edu.cn/comp/cmpl4/4-3.htm
文法含左递归不便于使推导按从左往右的顺序匹配,甚至使分析发生死循环。
(a)消除直接左递归
一般情况下,直接左递归的形式为:
A→A α 1 \alpha1 α1 |A α 2 \alpha2 α2 |…|A α m \alpha m αm | β 1 \beta1 β1 | β 2 \beta2 β2 |…| β n \beta n βn
消除左递归后改写为:
A→ β 1 \beta1 β1 A’| β 2 \beta2 β2 A’|…| β n \beta n βn A’
A’→ α 1 \alpha1 α1 A’| α 2 \alpha2 α2A’ | … | α m \alpha m αmA’ | ε \varepsilon ε
(b)消除间接左递归
先通过产生式的替换,将间接左递归变为直接左递归,然后再消除直接左递归。

(3) 确定的自顶向下分析方法

1、基本思想
对每一非终结符构造一个过程,每个过程的功能是识别由该非终结符推出的串。
2、编写程序
IP:是输入串指示器,开始工作前IP指向串的第一个符号,每个程序工作完后,IP指向下一个未处理符号。
sym:表示IP所指符号。
ADVANCE:是过程,让IP指向下一个符号。
ERROR:是出错处理子程序。

(4) 预测分析方法

(1)预测分析表M 如下矩阵形式:矩阵M
•行标题用文法的非终结符表示。
•列标题用文法的终结符号和#表示。
•矩阵元素M[A, a]的内容是产生式A→ α \alpha α(或→ α \alpha α)表明当对A进行推导,面临输入符号a时,应采用候选 α \alpha α进行推导。
出错处理标志(即表中空白项)表明A不该面临输入符号a。
(2)符号栈
用于存放文法符号,栈顶为推导过程中句型尚未匹配部分的开头符号。
分析开始时,栈底先放一个#,然后放进文法开始符号,即S#
(3)预测分析总控程序
总是按栈顶符号x和当前输入符号行事。
对于任何(x,a),总控程序每次都执行下述三种可能动作之一:
(a)若x=a=‘#’,则宣布分析成功。
(b)若x=a ≠ \neq =‘#’,则把x从栈顶逐出,指针指向下一输入符号。
(c)若x是一个非终结符,则查看分析表M。
①如果M[A, a]中存放关于X的一个产生式,那么,首先把X顶出栈,然后把产生式右部符号串按反序一一推进栈。
②如果M[A, a]中存放“出错标志”,则调用出错处理程序ERROR。

第五章
(1)自底向上优先分析法

自底向上分析方法,也称移进—归约分析法,粗略地说它的实现思想是对输入符号串自左向右进行扫描,并将输入符逐个移入一个后进先出栈中,边移入边分析,一旦栈顶符号串形成某个句型的句柄时,(该句柄对应某产生式的右部),就用该产生式的左部非终结符代替相应右部的文法符号串,这称为一步归约。重复这一过程直到归约到栈中只剩文法的开始符号时则为分析成功,也就确认输入串是文法的句子。可以看出,移进一归约过程是自顶向下最右推导的逆过程。最右推导称为规范推导。自左向右的归约过程称为规范归约。
如何知道何时在栈顶符号串中已形成某句型的句柄,这是自底向上分析的关键。在自底向上分析方法中,本章主要介绍常用的算符优先分析法和LR类分析法。

(2) 算符优先分析法

算符文法的定义:设有一文法G,如果G中没有形如A→…BC…的产生式,其中B和C为非终结符,则称G为算符文法(Operater Grammar)也称OG文法。(文法中没有两个非终结符相临的情况,则称是算符文法)
性质1 在算符文法中任何句型都不包含两个相邻的非终结符。
性质2 如果Ab或b。A出现在算符文法的句型中;其中A ∈ \in VN,b ∈ \in VT,则中任何含b的短语必含有A。
算符优先文法的定义:设有一不含产生式的算符文法G,如果对任意两个终结符对a, b之间至多只有<、>和=三种关系的一种成立,则称G是一个算符优先文法。(Operator Precedence Grammar)即OPG文法。

(3) 算符优先关系表的构造

FIRSTVT(B)={b|B ⇒ \Rightarrow b…或B ⇒ \Rightarrow Cb…}
构造规则:
(1)若B→b…或B→Qb…,则b ∈ \in FIRSTVT(B)
(2)若B→Q…,则FIRSTVT(Q) ⊆ \subseteq FIRSTVT(B)
LASTVT(B)={a|B ⇒ \Rightarrow …a或B ⇒ \Rightarrow …aC}
构造规则
(1)若B→…a或B→…aQ,则a ∈ \in LASTVT(B)
(2)若B→…Q,则LASTVT(Q) ⊆ \subseteq LASTVT(B)
最左素短语:设有文法G[S],其句型的素短语是一个短语,它至少包含一个终结符,并且除自身外不包含其它素短语,最左边的素短语称最左素短语。

(4) 归约步骤

初始时栈底存#,输入指针指向输入串的首字符。
控制程序根据栈顶终结符a(若栈顶是非终结符,则次栈顶的终结符称为栈顶终结符)和输入指针所指的输入符b,查优先关系表M,可能有四种情况:
(1)M[a, b]为<或=时移进b,即将b进栈,输入指针指向下一输入符。
(2)M[a, b]为>时,则将栈顶含a的素短语按对应的产生式归约,素短语与产生式右部需终结符对应相同,非终结符位置应相同名称可不同。顶出栈中素短语,非终结符入栈。
(3)M[a, b]为空白,语法错,调用相应出错处理程序。
(4)a=b=# 时分析结束。

(5) 优先函数

关系法构造。 构造步骤:
(a)对每一终结符a(包括#),用fa,ga为结点名。
(b)若ai>aj或ai=aj,则从fai到gaj画一条箭弧。若ai<aj或ai=aj,则从gaj到fai画一条箭弧。
(c)给每个结点赋一个数,此数等于从该结点出发所能到达的结点(包括该结点自身在内)的个数。赋给结点f (ai)的数,就是函数f(ai)的值,赋给g (aj)的数,就是函数g(aj)的值。
(d)对构造出的优先函数,按优先关系矩阵检查一遍是否满足优先关系的条件,若不满足时,则在关系图中有回路说明不存在优先函数。
优先函数的优缺点
优点:
(1)节省存储空间;
(2)执行整数比较运算比查优先关系表方便。
缺点:
(1)有些优先关系表不存在优先函数。
(2)原先不存在优先关系的两个终结符变成可比较其函数值大小了,需加以克服。

第六章
(1) LR分析法

LR分析法是一类对上下文无关文法进行“自左向右的扫描和最左归约(即最右推导的逆过程)”分析的方法,是一种规范归约过程。LR(k)分析方法是1965年Knuth提出的,其中k表示向右查看输入符号串的个数。

 图片不见了。。。

(1)总控程序 所有LR分析器的总控程序都是相同的,共有四种动作:移进、归约、接受、出错。
(2)分析表 常见的有四种:
LR(0)分析表 适应文法范围小,是其它类型分析表构造的基础。
SLR(1)分析表 是LR(0)分析表的改进,适应文法范围强于LR(0)。
LR(1)分析表 分析能力强(指适应范围,查错速度),但状态太多。
LALR(1)分析表 是LR(1)分析表的改进,分析能力强于SLR而稍弱于LR(1),但状态少于LR(1)
(3)分析栈 包括文法符号栈和相应的状态栈。

(2) LR(0)分析

可归前缀
规范句型中,包括句柄及句柄以左的部分,称为可归前缀。
有些可归前缀的前缀是相同的,不仅仅属于某一个规范句型。我们把可归前缀的前缀称为活前缀
进行语法分析时,只要将待分析符号串的当前部分符号与 α \alpha αi[Pi]进行比较,便可知是否归约,以及应按哪条产生式归约。
为了得到所有可归前缀,可以对文法G构造一个有穷自动机,该有穷自动机能识别文法G的所有可归前缀。
构造识别可归前缀的有穷自动机
项目:文法的识别可归前缀的有穷自动机以文法的“项目”作为它的状态,所谓文法的项目,是在文法的每一条规则的右部添加一个圆点而形成。之所以这样构造项目,是受可归前缀的启发。圆点表示在识别可归前缀的过程中,对句柄(即某产生的右部)已识别过的部分。
项目分四类
(a)圆点在最右端的项目,形如A→ α \alpha α·,表示已从输入串看到能由一条产生式右部推导出的符号串,即已达一可归前缀末端,已识别出句柄可以归约,这种项目称为归约项目,相应状态称为归约状态。即为可归前缀识别态
(b)对形如S’→S·的项目,其中S是文法开始符号,称为接受项目,相应状态称为接受状态,表明可由S推导的输入串已全部识别完,整个分析过程成功结束。
(c)对于形如A→ α \alpha α·a β \beta β的项目,表明尚未识别一可归前缀,需将a移进符号栈,故称移进项目,相应状态为移进状态
(d)对于形如A→ α \alpha α·B β \beta β的项目,表明期待分析由B所推出的串归约到B,从而识别B。故称为待约项目,相应状态为待约状态
方法一:首先构造识别可归前缀的NFA,然后将NFA确定化,得DFA(部分)。
方法二:根据文法直接构造识别文法可归前缀的DFA。
①拓广文法
对文法G加一条产生式 S’→S 得 G’,目的是使开始状态唯一,接受状态易于识别。
②定义项目集I的闭包CLOSURE(I)
a) I的项目均属于CLOSURE( I );
b) 若A→ α \alpha α•B β \beta β属于CLOSURE(I),则每一形如B→• γ的项目也属于CLOSURE(I);
c) 重复b),直到CLOSURE(I)不再增大为止。
③定义状态转换函数GO(I,X)
其中I是项目集,X是文法符号。GO(I,X)=CLOSURE( J ),其中J={任何形如A→ α \alpha αx• β \beta β的项目|A→ α \alpha α•x β \beta β∈I},以上可以避免出现 ε \varepsilon ε 弧,避免从同状态射出相同标记弧。
④构造DFA
a) DFA的初态集:CLOSURE({S’→•S})
b) 对初态集或其它所构造的项目集应用转换函数,GO (I, X) =CLOSURE(J)求出新的项目集。
c) 重复b)直到不出现新的项目集为止。DFA中所有状态组成的集合也称为该文法的LR(0)项目集规范族。
构造算法
a) 若项目A→ α \alpha α·a β \beta β属于Ik且转换函数GO( I k I_k Ik, a)= I j I_j Ij,当a为终结符时则ACTION[k, a]为Sj,其动作含意为将终结符a移进符号栈,状态j进入状态栈,(即当状态k遇a时转向状态j)。
b) 若项目A→ α \alpha α·属于 I k I_k Ik,则对a为任何终结符或‘#’号,置ACTION[k,a]为“ r j r_j rj”,j为在文法G中某产生式A→ α \alpha α的序号。rj 动作的含义是把当前文法符号栈顶的符号串归约为A,且将栈指针从栈顶向下移动| α \alpha α|的长度(或符号栈中弹出| α \alpha α|个符号),非终结符A变为当前面临的符号。
c) 若GO(Ik, A)= Ij,则置GOTO[k, A]为 “j”,其中A为非终结符,表示当前状态为“k”时,遇文法符号A时状态应转向j,因此A 移入文法符号栈,j 移入状态栈。
d) 若项目S→S·属于Ik,则置ACTION[k, #]为“acc”,表示接受。
e) 凡不能用上述方法填入的分析表的元素,均应填上“报错标志”。为了表的清晰我们令在表中用空白表示错误标志。
LR(0)分析器的工作过程
(1)若ACTION [q i , a k ] = S j ,则将状态 S j , a k 进栈,三元式变化过程:
(2)若ACTION [ q i, a k ] = r j ,且第 j 条产生式为 A → β \beta β ,| β \beta β| = r ,则按 第 j 条产生式归约。
(3)若ACTION[qi, ak]=acc 则结束分析,输入串被接受。
(4)若ACTION[qi, ak]=ERROR 或表中为空白,表示出错,进行相应出错处理。

(3) 非LR(0)文法的判断

判断方法一:
考察识别文法可归前缀的DFA,若某个状态(即项目集)中既含移进项目又含归约项目;或含不只一个归约项目, 则会发生分析动作的冲突,可知该文法不是LR(0)的。
判断方法二:
若文法的LR(0)分析表中含多重定义,即表中某项动作不唯一,则该文法不是LR(0)文法。

(4) SLR(1)分析

用SLR(1)方法,对于当前状态中的归约项目,如A→ α \alpha α·,必须当前输入符号属于FOLLOW(A)时,才可做归约。有望解决LR(0)方法中的分析动作冲突问题。
SLR(1)分析表的构造
将LR(0)分析表构造算法中的 b)改为:
若项目A→ α \alpha α ·属于 I k I_k Ik,则对a为任何终结符或‘#’号,且满足a ∈ \in FOLLOW(A)时,置ACTION[k, a]为“rj”,j为在文法G中某产生式A→ α \alpha α的序号。

(5) 非SLR文法的判断

判断方法一:
对识别文法可归前缀DFA中任一状态下,FOLLOW(B1)…FOLLOW(Bn),两两不相交,否则,文法不是SLR的。只求需要归约Follow集
判断方法二:
若构造的SLR分析表含多重定义,则文法不是SLR的。

(6) LR(1)分析

若对某些文法,用SLR(1)方法仍解决不了分析动作的冲突问题,可采取以下措施:若某归约项目A→ α \alpha α · ∈ \in I ,当 I 为当前状态,面临当前输入符号a 时,只有a 是在 I 状态下A 的后继符号时 才用 A → α \alpha α 产生式归约,而不是对A的所有后继符号都可以归约。从而有望解决冲突。
A → α \alpha α·,Follow(A) 是向前搜索符,构造项目集是其他与LR(0)相似,只是加上向前搜索符
将LR(0)分析表构造算法中的 b) 中:若项目 A → α \alpha α · 属于Ik ,则对 a 为任何终结符或 ‘#’ 号,置ACTION[k, a]为 rj改为:若项目 [A→ α \alpha α·, a] 属于Ik ,则置 ACTION [k,a] = rj
LR(k)分析表
如果用 LR(1)方法仍不能解决冲突,则可再向前多搜索几个符号,这时的项目为 [A→ α \alpha α· β \beta β, a1 a2 … ak ] 称为 LR(k)项目,相应的分析表构造方法类似 LR(1)分析表的构造。

(7) LALR(1)分析

在LR(1)项目中,有很多状态中的项目除了向前搜索符号不同外,产生式部分是完全相同的,称这样的状态是同心的,为了克服LR(1)分析中状态太多的问题,可以将这些同心集合并。如果合并后得到的新状态没有冲突出现。则按新状态构造分析表。这就是LALR(1)分析法的基本思想。
LR(0) LR(1) SLR(1) 这三种文法很重要。

第七章
(1) 语义处理

语义分析:主要检查各种语法成分的语义是否符合语义规定,例如参与运算的表达式类型是否一致,赋值语句的赋值左部和右部类型的一致性,数组元素的维数与数组说明维数的一致性,每一维的下标是否越界,在相同作用域中名字是否被重复说明等。
目前尚无公认的、统一的形式化语义描述工具。但研究者已提出一些针对某些特殊语言的语义描述工具,例如:公理语义、指称语义、代数语义、操作语义等。
中间代码生成:对说明语句,通常将其中定义的名字及其属性信息,在符号表中进行登记;对于执行语句,生成语义上等价的中间语言代码。
属性文法:一个属性文法,包含一个上下文无关文法和一系列语义规则,这些语义规则附在文法的每个产生式上。
E→T1+T2 \qquad {T1·t=int AND T’2·t=int} \qquad E→T1 or T2 \qquad {T1·t=bool AND T2·t=bool}
T→num {T·t=int} \qquad T→true {T·t:=bool} \qquad T→false {T·t:=bool}
语法制导翻译:在语法分析过程中,随着分析的步步进展,根据每个产生式对应的语义子程序(或语义规则描述的语义动作)进行翻译的办法称作语法制导翻译。
语法制导翻译的具体实现途径:假定有一个LR语法分析器,现在把它的分析栈扩充,使得每个文法符号都跟有语义值,即把栈的结构看成下图所示那样。同时把LR分析器能力扩大,使它不仅执行语法分析任务,且能在用某个产生式进行归约的同时调用相应的语义子程序,完成有关的语义动作。每步工作后的语义值保存在扩充的分析栈里“语义值”栏中。

(2) 中间代码形式

常见的有逆波兰记号、三元式、四元式和树等。
1.逆波兰表示(也称后缀表示)逆波兰表示的特点:
(1)运算符紧跟在运算对象的后面出现,没有括号。
(2)运算对象出现的顺序(从左到右)和原有顺序(中缀)相同。
(3)运算符是按实际运算顺序(从左到右)出现的。
逆波兰表示的最大优点是易于计算机处理表达式,使用栈进行运算很方便。可将逆波兰表示形式扩充,用来描述程序设计语言中的其它语法成分。
2.三元式表示
三元式的形式为:(op,ARG1,ARG2)其中op为运算符,ARG1为运算对象1,ARG2为运算对象2。
三元式出现的先后顺序与表达式的运算顺序一致。
三元式的缺点是不便于优化,动一式,其它式子被牵动,使用间接三元式,可以克服这个缺点。
间接三元式:使用二个表(1)三元式:存放不相同的三元式(2)运算顺序表(也叫间接码表):按运算的先后顺序列出有关三元式在三元式表中的位置。
3.树形表示 三元式表示也可用相应的树形表示。
简单变量或常量的树就是该变量或常量本身,如果表示表达式e1和e2的树为T1和T2
4.四元式:表示形式(op; ARG1, ARG2, RESULT)四元式表示较利于代码优化。

(3) 简单赋值语句翻译

(1)id.name 语义变量,表示变量即标识符字符串。
(2)E. place 语义变量,表示存放E值的变量名在符号表中的入口位置或一整数码(临时变量)。
(3)Lookup(id. name)语义过程(函数),对id.name查符号表,若找到,则返回其在符号表中的位置,否则返回nil,体现先定义后使用的原则。
(4)GEN(op, ARG1, ARG2, RESULT)语义过程(函数),生成四元式,填进四元式表。
(5)newtemp 语义过程,生成一临时变量Ti。

图片不见了。。
(4) 布尔表达式的翻译

布尔表达式的翻译是指转换成最终能计算出该表达式的结果为真或假(1 或0)的四元式序列的过程。
Eg. a or b and not c 的四元式序列为:
(1) t1:=not c
(2) t2 :=b and t1
(3) t3 :=a or t2
(1) 控制语句的代码结构 真出口E.true 假出口E.false
(2) “回填”技术
控制语句中布尔表达式翻译成四元式序列时,有的转移地址不能在产生这些四元式的同时得知,需要在适当的时候回填这个地址。
(3) “拉链”技术
为了记录需要回填地址的四元式,常采用“拉链”技术。把需回填E.true 的四元式拉成一条链,称为“真”链;把需回填E.false的四元式拉成一条链,称为“假”链。

 图片不见了。。。
 图片不见了。。。
(5) 控制结构的翻译

for 循环语句的文法及其相应的语义动作

 图片不见了。。。
第八章 错误的诊查与校正

对于源程序,由于种种原因,往往含有或多或少的错误,因此,一个好的编译程序应具有较强的查错和改错能力。

1、语法错误

指程序结构、单词和拼写不符合语法要求的规则。都是在词法分析阶段和语法分析阶段发现的。
如 关键字拼写错误;某些语法成分未按语言的语法规则编写等。

2、语义错误

指程序不符合语义规则或超越具体计算机系统的限制。包括以下几种类型:

  • 说明错:对变量未说明就引用,某些量被重复说明,或不符合有关作用域的规定。
  • 类型不相容错:某些运算的操作数的类型不相容,形一实参在种属或类型上不相对应等。
  • 对某些值超越限制错:如对各类变量数值范围的限制;对数组维数、形参个数、循环嵌套数的限制。

另外,我们将在编译阶段就能发现的错误,称为静态错;到目标代码运行时才能发现的错误称为动态错,如溢出,动态数组的下标越界。

出错处理主要有两种处理方法:

1、校正法
试图对错误进行校正。
当编译程序发现错误时,给用户指出错误的性质、错误的位置,以及如何校正等方面的信息。
2、局部化法
当发现错误时,跳过错误所在的语法单位,继续往下分析。以便把错误限制在尽可能小的局部范围内。只需给用户报告出错误位置,出错性质即可。

一些语义错误的处理:

1、遏止株连错误
株连错误:由于第一次错误,而派生出后面若干额外错误。只要消除第一个错误,后面若干错误也就自动消失。
遏止方法:第一次发现是标识符引起错误时,输出出错信息,并用一个“正确”的标识符代替它,并把此新的标识符填进符号表,尽可能正确地填入各种属性且加上标记。
以后发现一个引起错误的标识符时,查看符号表中相应登记项,如果它已被标记,则不再打印出错信息。
2、遏止重复错误
重复错误:一种错误,发现n次,报n次错。
如:x未被说明,程序中出现n次,则n次报x未被说明。
遏止方法
设出错标识符为x
1、若x未经说明,则将x填入符号表,并填入鉴别出的属性。
2、若x已被说明,则查错误类表。
如果表中没有同类错误,则输出出错信息,并填进错误类表中。
如果表中有同类错误,则不输出错误信息。

第九章 符号表

符号表的作用

编译程序在词法分析到代码生成的各阶段,不断地积累和更新表中的信息,并按各自的需要从表中获取信息。符号表的功能归结为以下三个方面:
1、收集符号属性
在分析语言程序中标识符说明部分时,编译程序根据说明信息收集有关标识符的属性,并在符号表中建立符号的相应属性信息。如PL/0语言编译的符号表。
2、上下文语义的合法性检查的依据
通过符号中属性记录可检查标识符属性在上下文中的一致性和合法性。如:是否未说明就引用,说明与引用,其属性、类型是否一致。是否有重复定义。运算量间运算类型是否一致等。
3、作为目标代码生成阶段地址分配的依据
源程序中的变量在目标代码生成时需要确立其在存储分配的位置(主要是相对位置)。而地址分配主要根据变量的类型,在源程序中被说明的位置。
如在第几层分程序,是静态区还是动态区等,分配其在相应数据区中的相对地址,而这些地址分配的依据都是作为变量的语义信息被收集在该变量的符号表属性中。

符号表的内容:

1、符号名
源程序中一个标识符可以是一个变量名、常量名、函数名或过程名,登记在符号表中,通常把一个标识符在符号表中的位置(通常是一个整数)称之为该标识符的内部代码,从而取代该标识符。
2、符号的类型信息
符号的种类:如常量、变量、数组、标号、函数或过程等,符号的类型,如整型、实型、字符型、布尔型等。
数组:应包括维数、上下界、计算下标地址时涉及的常量等,放在数组信息向量表即内情向量表中,用于确定存储分配时所占空间,确定数组元素的位置。
过程或函数:应包括参数的个数、类型、排列次序等用来作调用过程的匹配处理和语义检查。
记录结构:应包括其成员的类型、个数、排列次序等信息。以便确定结构型变量应分配的空间及结构成员的位置。
3、符号变量的存储分配信息
存储类别:如全局量还是局部量,静态存储变量,还是动态存储变量等,作为存储分配的依据。
地址表示:简单变量或常量,一般是该量在数据区所占单元的绝对或相对地址。数组,是该数组在数据区中的首地址,过程或函数,是该过程或函数的分程序入口地址。
4、层次信息
对于分程序嵌套或过程嵌套结构型程序设计语言,还应包括每个标识符所属分程序(过程)的层次。

符号表的组织:

符号表的组织直接关系到语义功能的实现和语义处理的时空效率,关于符号表的组织可从符号表的总体组织和表项属性信息组织来分别讨论。
1、符号表的总体组织
第一种:按照属性完全相同的那些符号组织在一起。
第二种:把语言中的所有符号都组织在一张符号表中。
优点:总体管理集中单一。
缺点:若各表项相等,则增加了无用的属性空间,从而增加了空间开销。
若各表项不等长,则增加了对符号表管理的复杂度。
第三种:折中方式
根据符号属性相似程度分类组织成若干张表,使管 理复杂性及时空效率方面都取得折中的效果。
2、符号表项的排列
在编译程序的整个工作中,符号表被频繁地用来建立表项,找查表项,填充和引用表项的属性,因此表项的排列组织对该系统运行的效率起着十分重要的作用。
传统上采用三种构造方法:
(1)线性组织(按其出现顺序)
表项按它的符号被扫描到的先后顺序建立。
线性组织管理简单但运行效率低。适用于事先能确定符号个数且符号个数不大时。
(2)排序组织及二分法(abcd排列)
表项按其符号的字符代码串的值的大小排列。
关于表项的建立和查找通常采用二分法。
排序表的运行效率比线性表高,算法复杂性也高于线性表。
(3)散列组织
表项位置是由对表项的符号值(即字符代码串)进行某种函数操作(通常称为“杂凑”)所得到的函数值来确定的,这种函数通常被称为杂凑函数。
符号表的散列组织具有较高的运行效率,因而绝大多数编译程序中的符号表采用散列组织。

3、关键字域的组织:

在编译程序中,符号表的关键字域就是符号本身。也称名字域。
(1)等长关键字段
可设置关键字段为标识符的最大长度。由于程序中的标识符不会总是使用很长的拼写字,关键字段的这种组织方式在实际使用中会有很多空间是冗余的。
(2)关键字池的索引结构
符号表中关键字段是指针,指向该关键字在池中的位置,

 图片不见了。。。
4、其它域的组织

符号表属性域的组织,根据属性性质大致分成两类:
一类是符号表中符号的该属性值具有相同的类型且是等长的,则该属性域的类型结构就可用这个长度及类型来定义。
另一类属性值可能具有相同类型但长度不同,则该属性域的定义不能用简单的数据类型来定义。
(1)等长属性值域的组织
用于表中符号的该属性值具有相同的类型且是等长的。
①位向量表示:如表示符号的数据类型可以用3个bit位表示
②数值表示:表示符号的数据类型也可以用一个整型量来表示
③用指针链表示:有一些是表示符号之间关系的属性,可用指针或指针链来构造这些属性域。

 图片不见了。。。

(2)不等长属性值域组织
符号的某些属性值是不等长的

一个数组的内情向量属性可分成两种值,数组维数的个数及每一个维的元素个数。下标表示了所在维的元素个数。
数组符号在符号表项中可以设立一个指向内情向量空间的指针,而在内情向量空间记录关于该数组的维数个数和每一维的元素个数,下图表示了array1及array2两个数组在符号表中内情向量的组织。

 图片不见了。。。
5、下推链表组织

在程序语言的结构中,分程序的分层结构中允许定义同名标识符,则在每进入一个内层结构并发生重名标识符定义时,将此重名标识符链到链首,即原同名链下推。

 图片不见了。。。
6、符号表的管理:

包括表的初始化、符号的登录、符号的查找和有关分程度结构符号表层次管理。
1、符号表的初始化
(1)符号表的表长是渐增变化的情况
(2)符号表的表长是确定的情况
2、符号的登录
对于散列表,新符号的登录是通过杂凑算法决定登录表项的位置。
关于属性,多数可与符号名一起填入,有些根据扫描时所得信息,逐步用前述属性域组织方式(如等长、不等长、属性链)进行填入。

 图片不见了。。。

3、符号的查找
根据表的组织方法,可用顺序查找、折半查找或杂凑查找法查找。
4、符号表中分程序结构层次管理
名字作用范围:分程序中说明的名字只局部于此分程序,内层可以引用外层说明的名字(此名字必须在内层未被说明)。
通常对于具有分程序结构的语言用两种方式组织它们的符号表:
(1)对每个分程序建立一个独立的符号表
每当编译程序扫描到一个分程序结构时,为该分程序填写符号表。
而当扫描到该分程序结束时,则释放为该分程序所填的符号表内容。即动态建立和动态消亡。
符号的登录是中为该分程序所建立的符号表中进行,而符号的查找是首先在该分程序符号表中进行,若没有查找到,再根据分程序的层次结构,逐层向外地依次查找各层符号表。
特别要注意的是对于并列的分程序,其相应的符号表不会同时存在。
(2)单表结构
只用一张符号表
符号表中可设立一个属性域用来登录符号所在分程序层次,符号表相当于栈的形式。
每当编译程序扫描到一个分程序结构开始时,就在符号表栈顶登录该分程序中说明的符号及其属性,包括分程序层号。
而当编译程序扫描到一个分程序的结束时,就从栈顶退出为该分程序登录的符号及属性。

第十章 目标程序运行时的存储组织

运行时的存储区常常划分为:目标区、静态数据区、栈区和堆区。

 图片不见了。。。

在大部分现有编译程序中采用的数据空间使用和管理方法主要有两种,即静态存储分配和动态存储分配。
而动态存储分配中又分:栈式动态存储分配、堆式动态存储分配。

静态存储分配:

在编译时就安排好目标程序运行时的全部数据空间,并能确定每个数据项目的存储位置,存储空间的这种分配方法称为静态存储分配。
对语言的要求:
(1)不含可变体积数据,如动态数组。
(2)不含递归过程。
(3)数据名的性质完全确定,即不能运行时才确定。
如FORTRAN语言,就可用静态存储分配。

动态存储分配:

在目标代码运行时,动态地为源程序中的数据对象分配存储空间,则称为动态存储分配。
对不能满足静态存储分配的语言,则需用动态存储分配。但并非所有分配工作都放在运行时刻做,在编译阶段就要设计好存储组织形式,并反映在生成的目标代码中。
有两种动态存储方式:栈式和堆式

栈式动态存储分配:

将整个程序的数据空间设计为一个栈(Stack),每当调用一个过程时,它所需空间就分配在栈顶,每当过程工作结束时,就释放这部分空间。如PASCAL、ALGOL和C语言。

堆式动态存储分配:

所谓堆式动态存储分配,就是在存储空间里专门保留一片连续的存储块(称为堆Heap),在运行程序过程中,对于类似上述情况的语法成分,需要空间时,就由一个运行时刻存储管理程序从堆中分配一块区域给它,不再需要时,又可由此堆管理程序释放该区域,供以后重新分配使用。

栈式存储分配的实现

过程的活动记录(AR):是一段连续的存储区,用以存放过程的一次执行所需要的信息。

 图片不见了。。。

简单的栈式存储分配的实现

假设语言没有分程序结构,过程定义不允许嵌套,但允许过程的递归调用,如C语言。这样的语言可以直接采用栈式存储分配。
无嵌套定义的过程活动记录内容见下表(设该过程含可变数组)
TOP指向栈顶。SP指向现行活动记录起点,老SP指向调用现行过程的过程活动记录的起点。

嵌套过程语言的栈式实现

假设语言的过程定义允许嵌套,例如PASCAL语言。
由于过程定义是嵌套的,一个过程可以引用包围它的任一外层过程所定义的变量或数组,为了在活动记录中查找非局部量名字所对应的存储空间,必须设法跟踪每个外层过程的最新活动记录的位置。常用的跟踪方法有两种:
(1)建立静态链
静态链处指向直接外层过程的最新活动记录的地址,这就意味着在运行时栈上的数据区(活动记录)之间又增加一条链,这个链称为静态链,通过静态链可实现对过程中非局部变量名称的访问。
(2)建立显示表(display)
为了提高访问速度,引进一个指针数组——嵌套层次显示表(display),假定现进入的过程的层数为i,则它的display表含有i+1个单元,表中自顶向下每个单元依次存放着现行层、直接外层、……,直至最外层(0层,主程序层)等每一层过程的最新活动记录的基地址,为了便于组织存储区和处理,将display作为活动记录的一部分。
其中,全局display是一个指针,指向本过程直接外层过程最新活动记录中display表的首地址,可通过全局display来构建本过程的display表,即将全局display所指的display表拷贝过来,再加一项本过程活动记录的SP,就可得到本过程的display表,当要访问一个非局部量时,由此非局部量说明所在的静态层数在display表中找到该非局部量说明所在过程的最新活动记录起址,再加上该非局部量的相对地址就可实现访问。例 图10.16(P239) display表

 图片不见了。。。
第十一章 代码优化
1、代码优化:

是对代码进行等价变换,使得变换后的代码运行结果与变换前代码运行结果相同,而运行速度加大或占用存储空间少,或两者都有。

2、优化工作阶段:

对中间代码进行优化(不依赖于具体的机器)和对目标代码进行优化(依赖于个体的机器)

3、依据优化所涉及的程序范围,分为三个不同的级别:

(1)局部优化 指的是在只有一个入口,一个出口的基本程序块上进行的优化。
(2)循环优化 即对循环中的代码进行的优化。
(3)全局优化 是在整个程序范围内进行的优化。

4、优化技术简介

(1)删除多余运算(删除公共子表达式):优化后使运算时间减少。
多余运算:表达式E多次出现,每次出现值都不变,则除第一次出现外,都叫多余运算。
(2)代码外提:优化后减少了循环中代码总数。
将循环体中每次循环都执行的不变运算提到循环的前面。
(3)强度削弱:优化后提高了运算速度。
把强度大的(即执行时间较长的)运算换成强度小的运算,如把循环中的乘法运算用递归加法运算来替换。
(4)变换循环控制条件(删除基本归纳变量)
基本归纳变量:如果循环中对变量I只有唯一的形如I:=I±C的赋值,其中C为循环不变量,则称I为循环中的基本归纳变量。
同族归纳变量:如果循环中J的定值总可以化为形式:J:=C1*I±C2,其中I为基本归纳变量,C1和C2都是循环不变量,则称J为归纳变量,并称它与I同族。
删除基本归纳变量:一个基本归纳变量除用于自身的递归定值外,往往只在循环中用来计算其它归纳变量以及用来控制循环的进行。这时我们就可以用与循环控制条件中的基本归纳变量同族的某一归纳变量来替换,从而达到减少一个变量的目的,这往往伴随对归纳变量运算进行强度削弱时进行。

(5)合并已知量

编译时,对运算对象为已知量的运算,即可直接进行运算,不必等到运行时再运算。

(6)复写传播

称形为x:=y的赋值为复写。复写传播,若引用x时,x、y值均未变,则把引用x改为引用y。

(7)删除无用赋值

(1)对某变量A赋值后,该A值在程序中不被引用,则为无用赋值
(2)对某变量A赋值后,在该A值被引用前又对A重新赋值,则前面 对A的赋值无用。

局部优化:

将一程序(中间代码序列)划分为一系列其本块,在各基本块内分别进行优化,即为局部优化。
1、基本块
基本块是指程序中一顺序执行的语句序列,其中只有一个入口语句和一个出口语句。
(1)入口语句的三种定义:
①程序的第一条语句
②由转移语句转到的语句
③紧跟在条件转移语句后面的语句
(2)基本块的划分
①由一入口语句到下一入口语句(不包括此下一入口语句)之间的语句序列。
②由一入口语句到一转移语句(包括此转移语句)之间的语句序列。
③由一入口语句到一停语句(包括此停语句)之间的语句序列。

2、基本块的DAG图
一个基本块可由一个DAG图(无环路有向图)表示。
DAG图的特征
①叶结点用标识符(变量名)或常量作为惟一标记,当叶结点是标识符时,代表名字的初值,给它加下标。
②内部结点用运算符标记,它表示计算的值。
③各结点可能附加有一个或若干个标识符,附加于同一个结点上的若干个标识符有相同的值。

 图片不见了。。。

算法步骤
步骤1 初始DAG为空,无任何结点。
步骤2 依次对基本块的每一个四元式,执行步骤3—5。
步骤3:
如果node(B)没有定义,则建立标记为B的叶结点,并定义node(B)为这个结点编号nB,即建立结点。

 图片不见了。。。
如果对于2型四元式,node(C)没有定义,则建立叶结点。
 图片不见了。。。
如果node(B)和node(C)都是已知常量,则执行B op C(合并已知量),得到新常量P;为P建立一个新结点。
 图片不见了。。。
如果 node(B)或node(C)是处理当前四元式时建立的新结点,则删除它。 步骤4 对三种四元式分别处理如下: (1)对于0型,设node(B)值是n,转步骤5。 (2)对于1型,查看是否存在标记为op的结点,且它有唯一的子结点node(A),即形如:
 图片不见了。。。
如果不存在,则建立这样的结点。让n是找到或建立的结点,转步骤5。 (3)对于2型,查看是否存在标记为op的结点,且其左右子结点分别是node(B)与node(C)(找公共子表达式)即形如
 图片不见了。。。
如果不存在,则建立这样的结点。让n是找到或建立的结点,转步骤5。 步骤5: 如果node(A)无定义,则把A附加在结点n上并令node(A)=n,(可达到删除多余运算的目的)否则,先把A从node(A)结点上的附加标识符集中删除(注意,如果node(A)是叶结点,则其标记A不删除),把A附加到新结点n上并令node(A)=n。(可达到删除无用赋值的目的)转处理下一四元式。 基本块内主要可进行的优化 (1)删除多余运算(删除公共子表达式) (2)合并已知量 (3)删除无用赋值 (4)复写传播
循环优化

循环,粗略地说就是程序中那些反复执行的代码序列。为了找出程序中的循环,需要对程序的控制流程进行分析。
1、程序流图
(1)控制流程图:就是具有唯一首结点的有向图。简称为流图。
所谓首结点,就是从它开始到控制流程图中任何结点都有一条通路的结点。
(2)程序流图:即把一个程序表示为一个控制流程图,则称为程序流图。
程序流图中的结点就是基本块,首结点就是包含程序第一个语句的基本块。
构造有向边
①基本块j在程序中的位置紧跟在基本块i之后,并且基本块i的出口语句不是无条件转移语句或停语句。
②基本块i的出口语句是goto (S) 或 if…goto (S),并且(S)是基本块j的入口语句。
2、循环的性质及查找
(1)循环的性质
在程序流图中,我们称具有下列性质的结点序列为一个循环:
(1)它们是强连通的。即其中任意两个结点之间,必有一条通路,而且该通路上各结点都属于该结点序列。如果序列只包含一个结点,则必有一有向边从该结点引到其自身。
(2)它们中间有且只有一个是入口结点。所谓入口结点,是指序列中具有下述性质的结点:从序列外某结点,有一有向边引到它,或者它就是程序流图的首结点。
(2)循环的查找
回边 假设 a—>b是流图中的一条有向边,若b DOM a, 则称a—>b是流图中的一条回边。对于一已知流图,只要求出各结点必经结点集,就可求出流图中的所有回边。
对右图,有向边6—>6、7—>4、4—>2是回边。
循环 如果已知有向边a—>b是回边,则循环就是由结点b、a以及有通路到达a而该通路不经过b的所有结点组成。

 图片不见了。。。

3、循环优化的主要技术
(1)代码外提:入口结点、前置结点
(2)强度削弱
(3)变换循环控制条件(删除归纳变量)

全局优化是整个程序范围内的代码优化。

为了进行全局优化,需要在整个程序范围内,对程序中的所有变量的定值和引用间的关系进行分析,通常将这一工作称为数据流分析。通过在程序的控制流图中进行数据流分析,是实现全局优化的基础。

1、一些主要概念
(1)到达-定值
•变量A的定值是一个语句(四元式),它赋值或可能赋值给A,该语句的位置称作A的定值点。
(2)引用-定值链(ud链)
•假设在程序中某点u引用了变量A的定值,则把能到达u的A的所有定值点的全体,称为A在引用点u的引用—定值链。通常将到达—定值信息存储(一个定值表)作为引用—定值链较方便,称之为 ud链。
(3)活跃变量
•对程序中的某变量A和某点 p,若存在一条从p 开始的通路,其中引用了A在点p 的值,则称A 在点p 是活跃的。否则称A 在点p是死亡的。
• 活跃变量的分析对于删除无用赋值是很有意义的。
(4)定值-引用链(du链)
•若一个变量A在程序中某点p定值,则可计算该定值能到达的对A的所有引用点,这些引用点集合称为该定值点的定值—引用链,简称为 du链。
•du链信息可进一步用于强度削弱的优化中。
2、全局优化的主要技术
(1)删除全局多余运算(删除全局公共子表达式)
(2)复写传播。

第十二章 代码生成
代码生成:

是把经过语法分析或优化后的中间代码转换成特定目标机的机器语言或汇编语言,这样的转换程序称为代码生成程序。
代码生成程序的构造与输入的中间代码形式和目标机的指令系统及结构密切相关。

目标代码一般有3种形式:

(1) 能够立即执行的机器语言代码,所有地址均已定位;(绝对地址的机器语言代码)
(2) 待装配的机器语言模块,当需要执行时,由连接装入程序把它们和某些运行程序连接起来,转换成能执行的机器语言代码; (相对地址的机器语言代码)
(3) 汇编语言代码,尚需经过汇编程序汇编,转换成可执行的机器语言代码。

代码生成程序的输入

代码生成程序的输入由前端所产生的中间表示和符号表中的信息组成。
可供选择的中间表示有多种,主要包括:线性表示法(如后串表示)、三地址表示法(如四元式)、抽象机表示法(如栈式机器代码)和图形表示法(如语法树和DAG图)等。

指令选择

指令选择是指,寻找一个合适的目标机指令序列以实现给定的中间表示。
在指令的选择过程中,指令集的一致性和完整性是重要因素。如果目标机器不能支持指令集的所有类型,那么对每一种例外都需做特别的处理。指令速度和机器用语也是重要因素。如果不考虑目标程序的效率,那么指令选择可以直接做。
生成的代码的质量取决于它的执行速度和代码序列的长度。

指令选择的基本原则:
  • 减小产生代码的尺寸
  • 减小目标代码的执行时间
  • 降低目标代码的能耗
寄存器分配

寄存器分配工作是确定在程序的哪个点将哪些变量或中间变量的值放在寄存器中比较有益。
通常情况下,指令在寄存器中访问操作数的开销要比在内存中访问小,且许多指令不能直接访问内存。若需要处置的操作数在内存中,则需要显式地取入到寄存器中。由此可见,将经常使用的操作数保存在寄存器中是比较有利的。
寄存器的使用可以分成分配和指派两个阶段考虑:
寄存器分配期间,为程序的某一点选择驻留在寄存器中的一组变量;
在随后的寄存器指派阶段,挑出变量将要驻留的具体寄存器,即寄存器赋值。

寄存器分配原则:

生成某变量的目标对象值时,尽量让变量的值或计算结果保留在寄存器中直到寄存器不够分配为止。这样,访问变量时可减少对内存的存取次数,提高运行速度。
当到基本块出口时,将变量的值存放在内存中。因为一个基本块可能有多个后继或前驱结点,同一变量名在不同前驱结点的基本块内,出口前存放的寄存器可能不同或没有定值,所以应在出口前把寄存器的内容放在内存中,这样从基本块外入口的变量都在内存中。
在同一基本块内后边不再被引用的变量所占用的寄存器应尽早释放,以提高寄存器的利用率。

指令调度

指令调度是指确定程序指令的执行顺序。
计算执行的顺序会影响目标代码的有效性。
对具有流水线限制的体系结构,指令调度阶段是必需的。

 图片不见了。。。
若在MIPS 4KC上计算表达式(a+b)+c。可用右表中两个不同的指令序列。 它们的主要不同在于指令顺序和寄存器的赋值。可以看出,有些计算顺序要求存放中间结果的寄存器少,指令序列短,从而提高了目标代码的效率。 1.计算机模型 2.待用信息链表法 3.代码生成算法 ##### 代码生成程序的开发方法: 1、 解释性代码生成法 解释性代码生成方法是建立在一个代码生成专用语言,用这种语言以宏定义、子程序等形式描述代码生成过程。通过这些宏定义和子程序把中间语言解释为目标代码。这种方法使机器描述与代码生成算法结合在一起,与机器的联系直接反映在算法中。 算法局限性
  • 由于目标机的多样性、寻址方式、指令的差异等,给中间代码的设计带来困难;
  • 代码生成语言与机器密切相关,可移植性受限制;
  • 目标机的描述与代码生成算法混在一起,当描述改变时,势必引起算法的改变;
  • 需进行指令的选择、排序等低层次的繁琐工作,产生的目标代码质量依赖于设计者的经验能力。
  • 代码生成的视野有限,对协调上下文有关的优化较困难。

2、模式匹配代码生成法
模式匹配代码生成方法是,把对机器的描述与代码生成的算法分开。也就是建立一个代码生成用的机器描述语言,用以形式地描述目标机的资源、指令及其语义等有关信息。代码生成程序根据这些信息,自动地将中间语言程序翻译成目标机的汇编语言或机器代码。
方法局限性

  • 需通过形式描述的模式如实地反映机器的特性,这点不容易做到
  • 进行模式匹配时耗费时间很长
  • 目标代码的质量不理想

3、表驱动代码生成法
表驱动代码生成方法,是模仿从语法描述构造表和表驱动的一种语法分析方法。

  • 首先,把对目标机的形式化描述进行预加工转换成代码生成表
  • 然后,用表驱动的代码生成程序,来驱动代码生成表
  • 最后,把中间语言的内部表示翻译成目标机的汇编代码

方法优点
容易使用和修改,并且能较容易地为不同的计算机构造适合于它们自己的代码生成程序。
方法局限性
它所生成的目标代码的质量,依赖于机器描述的完善程度。
三种方法比较

  • 解释性代码生成法比较容易达到目标代码质量的要求,代码生成算法有效,但可移植性欠缺
  • 模式匹配代码生成法可达到较好的可移植性,但要生成高质量的目标代码以及高效率的算法,较困难
  • 表驱动代码生成法能达到很好的可移植性,它实际上是代码生成程序的生成程序,真正实现了代码生成自动化,但方法不够成熟
自动构造代码生成程序技术:

基于语法制导技术
此技术也称为Graham-Glanville方法。它利用类似于上下文无关文法的规则和相应的机器指令模板描述机器操作。当一条生成描述规则与一条波兰式中缀表示子字符串相匹配,且满足有关的语义限制时,将被匹配的部分用相应规则左边的符号替代,同时输出实例化后相应的指令模板。
Graham-Glanville 代码生成程序由中间语言变换、模式匹配器和指令生成3部分组成。
语义制导技术
此技术也称为属性文法或词缀文法方法。通过使用属性,将语义信息加入代码生成规则中。在代码生成过程中,属性除了传递值之外,还用于控制代码的生成、产生新的属性值以及产生副作用。控制部分通过断言来实现。一条规则是可应用的,当且仅当它与目标字符串语法上相匹配且满足所有的断言条件。

第十三章 编译程序的构造
编译程序的构造主要有三条途径:
  • 用某种程序语言书写编译程序
  • 通过LEX、YACC等工具进行自动构造
  • 通过现有的编译基础设施进行改造和组装
1. 编译程序的书写语言与T型图

一个编译程序涉及到三个方面的语言:即源语言、目标语言和编译程序的书写语言。为了描述方便,通常用T型图来表示。

 图片不见了。。。
2. 编译程序的自展技术

自展的思想是先用目标机的汇编语言或机器语言书写源语言的一个子集的编译程序,然后再用这个子集作为书写语言,实现源语言的编译程序。如果把这个过程根据情况分成若干步,像滚雪球一样直到生成预计源语言的编译程序为止,这样的实现方式称为自展技术。

结合T型图的原则是:
  • 下面的T型图的左右上角两个语言分别与上面左右两个T型图的底部语言相同【三个T型图】
  • 上面左右两个T型图的左右上角的语言必须分别相同
交叉编译程序

交叉编译就是在一个平台(主机平台)生成另一个平台(目标平台)上的可执行代码。
平台包括两个概念:体系结构和操作系统。同一体系结构可以运行不同的操作系统;同一个操作系统也可以在不同的体系结构上运行。

需要交叉编译程序的原因:
  • 目标平台上不允许或不能够安装所需要的编译程序;
  • 目标平台上资源匮乏,无法运行所需要的编译程序;
  • 目标平台还没有建立,没有操作系统。
编译基础设施

编译基础设施是编译程序的开发环境,它提供一系列开发编译程序的策略和工具,支持多种源语言、多目标机的编译技术,以便于人们在具有较高抽象层次的平台上进行编译程序开发和研究工作。

可重定向编译程序

普通的编译程序将源程序翻译成特定目标机的汇编代码或目标代码;可重定向编译程序能够根据不同目标机,生成相应的目标代码。在可重定向编译程序中,将与目标机相关的部分单独编写成不同的模块,根据不同的目标机调用不同的模块。

  • 可重定向编译程序不是针对特定机器的编译程序用户而言的可重定向。
  • 可重定向编译程序不是编译程序的生产器。
  • 有多个针对不同机型的可选后端。
支持可重定向编译的关键技术
  • 中间表示技术
  • 机器描述技术
  • 代码生成程序的构造技术
  • 目标机描述与目标代码生成的接口技术
常用的可定向编译程序

常用的可定向编译程序有:GCC和LCC。
两者的主要区别:

  • 在前端和后端数量上。
  • 在机器描述的能力上。
  • 在产生代码的质量上。
1.GCC的总体结构:

GCC编译系统由与源语言相关的前端、与源语言无关的后端和目标机描述三部分组成。

窥孔优化方法

是通过考查一小段目标指令(窥孔)并把这些指令替换为更短更快的一段指令,从而提高目标代码的质量。
所谓“孔”可以看成优化对象中的一个小的活动窗口,孔中的代码根据优化的需要可以连续也可不连续。
窥孔优化的一个特点是,优化后所产生的结果可能给后面的优化提供进一步的机会。

  • 冗余存取
  • 不可达代码
  • 控制流优化
  • 强度削弱
  • 删除无用操作

第二部分 思维导图

在这里插入图片描述

©️2021 CSDN 皮肤主题: 书香水墨 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值