编译原理简明教程(第2版)第9章
comp_Chapter9

重复信息的例子
如果用户忘了申明变量i(或者声明的时 候变量的拼写错误),而在程序中多出 使用i。 编译程序在每次碰到对i的使用的时候都 可能发现错误:变量i没有被定义。 这个错误信息将多次出现。而实际上这 个错误是唯一的。
语法错误提示信息(自顶向下)
在自顶向下的扫描过程中,如果扫描程 序碰到了非预期的符号输入,最简单的 提示方法就是:unexpected symbol: ‘?’ 自顶向下的分析过程中,扫描程序能够 十分清楚地知道当前正在按照什么规则 进行扫描,所以提示信息也可以给得更 加详细:在扫描???的时候碰到非预期的 符号??。
违反环境限制的错误:由于实现方面的问题,有些 编译器不接受语言的全集。同时语言本身也有限制。 比如:函数说明嵌套的深度,数组的最大层数。
错误复原
编写程序的过程中,出现错误是不可避免的。 编译程序在进行编译的时候,需要检查出程序 的错误。并且给出提示。 当编译程序碰到源程序中的错误的时候,应该 设法从错误中复原,并继续扫描程序以给出更 多的提示信息。更多的信息使得程序员能够更 加方便地修改程序。 可能复原是错误的。所以一般来讲,编译程序 给出的第一个错误是可靠的,而其他的错误信 息可能是不准确的。
错误复原的要点
株连信息的遏制
– 株连信息是指由于源程序中的一个错误而导 致编译程序向用户报告很多相关的出错信息。 而这些信息是不真实的。
重复信息的遏制
– 是指源程序中的一个错误而反映在源程序的 多处。
株连信息的例子
已经说明过程p(int a, int b);在使用的时 候写成了p(a.b)。 编译程序扫描的时候,首先发现“.”是 错误的,应为a不是结构类型的变量。然 后,发现b是错误的。最后发现p的参数 个数不同。 这些信息都是由于将‘,’误写做‘.’而 引起的。
编译原理lxy第9章

第16页
编译原理
C语言的程序结构 语言的程序结构 全局数据说明 Main ( ) { Main中的数据说明 中的数据说明 } void R ( ) { R中的数据说明 中的数据说明 } void R ( ) { R中的数据说明 中的数据说明 }
第17页
编译原理 C语言程序的存储组织 语言程序的存储组织
TOP R的活动记录 的活动记录
SP
Q的活动记录 的活动记录
Main的活动记录 的活动记录
主程序全局数据区
SP:总指向现行过程活动记录的起点,用于访问局部数据。 TOP:始终指向(已占用)栈顶单元。
第18页
编译原理 2、C的活动记录 、 的活动记录
C的活动记录有以下四个项目。 的活动记录有以下四个项目。 的活动记录有以下四个项目 1、连接数据(两项): 、 (两项): (1)老SP值,即前一活动记录的起始地址; 老 值 即前一活动记录的起始地址; (2)返回地址 (2)返回地址。 返回地址。 2、参数个数。 、 。 3、形式单元(存放实在参数的值或地址)。 、 (存放实在参数的值或地址)。 4、过程的局部变量、数组内情向量和临时工作单元。 、 。
编译原理
第九章 程序运行期间的存时存储空间的划分 活动记录概念 存储空间分配策略 1 1、静态存储分配 2、动态存储分配 简单栈式存储分配 嵌套过程语言的栈式分配 典型范例解析
第2页
编译原理 运行时存储空间的划分
编译程序为了使它编译后得到的目标程序能够运行, 编译程序为了使它编译后得到的目标程序能够运行, 要从操作系统中获得一块存储空间。 要从操作系统中获得一块存储空间。
有一些数据对象的大小在编译时也能确定,因此它们 有一些数据对象的大小在编译时也能确定, 也可以放在静态确定的区域。 也可以放在静态确定的区域。
编译原理第九章

分配策略:如果两个临时变量名作用域不相交,则它们可以分配在
同一单元中。一个临时变量名自它第一次被定值(赋值)的地方起
直至它最后一次被引用的地方止,这区间的程序所能到达的全体四
元式构成了它的作用域。 令临时变量名都分配在局部数据区中,若某一单元已分配给某些
临时变量名,则把这些名字的作用域(它们必须互不相交信息记录 )作为此单元的分配信息记录下来。每当要对一个新临时变量名进 行分配时,首先求出此名的作用域,然后按序检查每个已分配单 元,一旦发现新求出的作用域与某个单元所记录的全部作用域均不 相交时就把这个单元分配给这个新名,同时把它的作用域也添加到 该单元的分配信息之中。如果新临时变量的作用域和所有已分配单 元的作用域均有冲突,则就分配给它一个新的单元,同时把新名的 作用域作为此单元的分配信息。
适于静态管理的语言必须满足的条件:
1. 数组的上下界必须是常数
2. 过程调用不允许递归
3. 不允许用户动态地建立数据实体
编译程序可以确定出现在程序中的数据实体的地址(一般相 对于各数据区基地址的偏移量)。由于过程调用不允许递归,数 据实体的存储地址就和过程相联系,过程调用的活动记录就可 以直接安排在目标代码之后,并把各数据项的存储地址填入到 相关的目标代码中,以便在运行时访问这些数据存储空间。这 里,不存在对存储区域的再利用。目标代码运行时不必进行运 行时的存储管理。
上一页
下一页
18
3.2 FORTRAN的局部数据区
FORTRAN的编译程序将每个程序段都定义对应的局部数据区,每个数据区都 有一个编号,在地址分配时,在符号表中,对每个数据名将登记上它是属于哪个 数据区的,以及在该区中的相对位置(DA、ADDR)。
编译原理简明教程第二版

编译原理简明教程第二版本文档是《编译原理简明教程第二版》的前言部分,旨在介绍本书的目的和背景,以及阐明本书适用的范围和读者群体。
编译原理是计算机科学中的重要课程,涉及将高级程序语言转换为计算机可执行的机器语言的技术。
编译原理的理解对于计算机科学专业的学生以及从事软件开发和系统设计的专业人士都是至关重要的。
本教程作为一本简明的编译原理入门教程,旨在为读者提供一个简单但全面的了解编译原理的框架。
无论您是计算机科学专业的学生还是从事软件开发或系统设计的专业人士,如果您对编译原理感兴趣或需要深入了解这一关键领域,本书都适合您阅读。
在本书中,我们将以简明和易懂的方式介绍编译原理的基本概念和核心理论,并提供一些实际的编译器实现例子,以帮助读者更好地理解和应用所学知识。
希望本教程能够为广大读者提供一份简明而实用的编译原理研究资料,并对您在编译原理的研究和实践中有所帮助。
祝您阅读愉快!本章将解释编译原理的概念和在计算机科学中的重要性。
同时,介绍本书将提供的基本概念和技术。
编译原理是计算机科学中的一个重要领域,它主要研究如何将一种语言(通常是高级语言)转化为另一种语言(通常是机器语言),以便计算机能够理解和执行。
编译原理在软件开发和优化中起着至关重要的作用。
本书的目标是向读者介绍编译原理的基本概念和技术,帮助读者理解编译原理的工作原理和应用。
通过阅读本书,读者将掌握词法分析、语法分析、语义分析、中间代码生成、代码优化和代码生成等编译原理中的关键概念和技术。
下一章将介绍编译原理的起源和发展,以及编译器的基本原理和结构。
让我们开始研究编译原理吧!本章将介绍编译器中的词法分析过程。
词法分析是编译器的第一阶段,其目的是将源代码分解成有意义的词素或词法单元。
本章将讨论词法单元的概念、正则表达式的使用和有限自动机的设计。
同时,我们还将探讨如何设计和实现词法分析器,以便将源代码转换为词法单元序列。
词法分析器在编译器中起着至关重要的作用。
编译原理简明教程第二版

编译原理简明教程第二版什么是编译原理?编译原理是计算机科学中的一门重要课程,它研究的是如何将高级程序设计语言(如C、Java)编写的程序转化为机器能够理解和执行的指令。
编译原理主要包括以下几个方面的内容:1.词法分析:将源代码转化为一系列的词法单元,例如标识符、关键字、运算符等。
2.语法分析:通过创建语法树,检查源代码是否符合语法规则。
3.语义分析:对语法树进行分析,检查程序中的语义错误。
4.优化技术:对源代码进行优化,提高程序的执行效率。
5.目标代码6.:将优化后的源代码转化为与目标机器相关的指令,7.可执行文件。
编译过程编译过程可以分为三个阶段:前端、优化和后端。
前端前端负责词法分析、语法分析和语义分析等工作。
具体过程如下:1.词法分析:将源代码划分为一个个词法单元。
这些词法单元是编程语言中的基本语义单位,例如标识符、关键字、运算符等。
词法分析的结果是一个词法单元序列。
2.语法分析:根据语法规则检查词法单元序列是否符合语法。
语法分析的结果是一个语法树。
3.语义分析:对语法树进行静态语义检查,确保程序的语义正确。
语义分析的结果是一个语义树。
优化优化阶段对语义树进行优化,提高程序的执行效率。
常见的优化技术包括常量折叠、循环展开和死代码删除等。
优化的目标是提高程序的执行速度、减少程序的内存占用和提高程序的可读性。
后端后端负责目标代码目标代码是针对具体的目标机器的,可以是汇编代码、机器码,或者是中间表示形式(如LLVM的字节码)。
目标代码的过程中,需要进行寄存器分配、指令选择和代码布局等工作。
编译器的实现编译器的实现可以基于手写的,也可以使用编译器工具(例如Flex和Bison)辅助完成。
手写实现编译器的优点是可以更好地理解和掌握编译原理的各个方面。
而使用编译器工具可以减少编写代码的工作量,提高编译器的开发效率。
无论是手写实现还是使用编译器工具,编译器的核心功能都是通过前端、优化和后端三个阶段来完成的。
编译原理(第2版)课后习题答案详解

第1 章引论第1 题解释下列术语:(1)编译程序(2)源程序(3)目标程序(4)编译程序的前端(5)后端(6)遍答案:(1)编译程序:如果源语言为高级语言,目标语言为某台计算机上的汇编语言或机器语言,则此翻译程序称为编译程序。
(2)源程序:源语言编写的程序称为源程序。
(3)目标程序:目标语言书写的程序称为目标程序。
(4)编译程序的前端:它由这样一些阶段组成:这些阶段的工作主要依赖于源语言而与目标机无关。
通常前端包括词法分析、语法分析、语义分析和中间代码生成这些阶段,某些优化工作也可在前端做,也包括与前端每个阶段相关的出错处理工作和符号表管理等工作。
(5)后端:指那些依赖于目标机而一般不依赖源语言,只与中间代码有关的那些阶段,即目标代码生成,以及相关出错处理和符号表操作。
(6)遍:是对源程序或其等价的中间语言程序从头到尾扫视并完成规定任务的过程。
第2 题一个典型的编译程序通常由哪些部分组成?各部分的主要功能是什么?并画出编译程序的总体结构图。
答案:一个典型的编译程序通常包含8 个组成部分,它们是词法分析程序、语法分析程序、语义分析程序、中间代码生成程序、中间代码优化程序、目标代码生成程序、表格管理程序和错误处理程序。
其各部分的主要功能简述如下。
词法分析程序:输人源程序,拼单词、检查单词和分析单词,输出单词的机内表达形式。
语法分析程序:检查源程序中存在的形式语法错误,输出错误处理信息。
语义分析程序:进行语义检查和分析语义信息,并把分析的结果保存到各类语义信息表中。
中间代码生成程序:按照语义规则,将语法分析程序分析出的语法单位转换成一定形式的中间语言代码,如三元式或四元式。
中间代码优化程序:为了产生高质量的目标代码,对中间代码进行等价变换处理。
目标代码生成程序:将优化后的中间代码程序转换成目标代码程序。
表格管理程序:负责建立、填写和查找等一系列表格工作。
表格的作用是记录源程序的各类信息和编译各阶段的进展情况,编译的每个阶段所需信息多数都从表格中读取,产生的中间结果都记录在相应的表格中。
08979编译原理第九章

–
–
中间表示:四元式 DAG图 控制流图 数据流方程
问题复杂度:往往是NP 完全或NP难
–
简化,小规模问题
…
优化算法
–
代码优化的基本过程
代码的描述 数据流分析(data-flow analysis) 控制流分析(control-flow analysis) 变换 (transformations)
(3)T1:=4*I (5)T3:=T2[T1] (6)T4:=T1 (8)T6:=T5[T4] (9)T7:=T3*T6 (10)P:=P+T7 (11)I:=I+1 (12)if I<=20 goto(3)
(3‘)T1:=T1+4
(12)if I<=20 goto(5)
(1)P:=0 (2)I:=1 (4)T2:=addr(A)-4 (7)T5:=addr(B)-4 (3)T1:=4*I
性能指标
–
–
–
目标
代码优化
优化策略:
– – –
权衡(trade off):意图、结果 简洁:KISS(Keep It Simple,Stupid) 分工:编译阶段、软件处理、硬件机制
优化的阶段:
front end (I.R)
中间代码优化
(source code)
用户
code generator
基本优化技术
常数合并(合并已知量) 常数传播 代数化简 强度削弱(削弱运算强度) 复写传播 删除多余运算 循环不变代பைடு நூலகம்外提 变换循环控制条件 删除无用赋值
编译原理课后习题答案ch9

盛威网()专业的计算机学习网站
2
《编译原理》课后习题答案第九章
附加题
问题 1: 利用 Pascal 的作用域规则,试确定在下面的 Pascal 程序中的名字 a 和 b 的每一次出现 所应用的说明。 program m ( input, output ) ; procedure n ( u, v, x, y : integer ) ; var m : record m, n : interger end ; n : record n, m : interger end ; begin with m do begin m := u ; n:= v end ; with n do begin m := x ; n := y end ; writeln ( m.m, m.n, n.m, n.n ) end ; begin m ( 1, 2, 3, 4 ) end. 答案: 图中用蓝色数字下标相应标明。 program m1 ( input, output ) ; procedure n1( u, v, x, y : integer ) ; var m2 : record m3, n2 : interger end ; n3 : record n4, m4 : interger end ; begin with m2 do begin m3 := u ; n2 := v end ; with n3 do begin m4 := x ; n4 := y end ; writeln ( m2.m3, m2.n2, n3.m4, n3.n4 ) end ; begin m1 ( 1, 2, 3, 4 ) end. 问题 2: 当一个过程作为参数被传递时,我们假定有以下三种与此相联系的环境可以考虑,下 面的 Pascal 程序是用来说明这一问题的。 一种是词法环境 (lexical environment) , 如此这样的一个过程的环境是由这一过程定义 之处的各标识符的联编所构成; 一种是传递环境(passing environment) ,是由这一过程作为参数被传递之处的各标识
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
AC OP d=>AC
即累加器AC中的内容与d中的内容进行某种运算,结果送 到累加器AC中;其内存容量足够大;提供了21条符号汇 编指令。
9.2.2 虚拟机的汇编指令
虚拟机的汇编指令如表9.1所示。
9.2.2 虚拟机的汇编指令
1.填地址指令
设有一维数组a: array[m..n]of integer; 其元素有a[m], a[m+1], … , a[i], … , a[n],一共需要n-m+1个存储单元: <a[m]>,<a[m+1]>,…,<a[i]>,…,<a[n]> 一般有存储单元: <a[i]>=<a[m]>+i-m
如果把寄存器分配给某些变量,则该变量在定值前每引用一 次,将可少访问一次内存,从而可节省执行代价1;如果 某变量在基本块中被定值,且出基本块后还要被引用,则 把寄存器固定分配给该变量,可省去把它保存到内存单元 的操作,从而节省执行代价2。
9.1.4 运行时的存储管理
通常所见到的计算机都是冯· 诺依曼体系结构的,其特征是变 例如:假定一个字包含4字节,每个字符占1字节,每个整型 量——存储字,即用变量来仿效存储字,变量名实际上是 量占4字节,则对于字符型量的存储位置可以任意,然而, 存储字的名字。 对于整型变量的存储区域之开始地址必须是0,4,8, 对于编译程序来说,必须对源程序中出现的变量与常量分配 16,…,否则会引起错误。 运行时刻的存储空间,这一工作由先行端的分析和代码生 成程序共同完成。另从符号表的信息可以确定一个名字 (标识符)所代表数据对象在过程数据区中的相对地址。为 了存储分配的正确实现,除了必须考虑标识符的作用域问 题外,还必须考虑字边界对齐问题,即对于字节编址的计 算机,必须注意对于不同类型的量所分配存储区域的起始 地址都必须符合边界要求。
9.2 一个计算机模型——虚拟机
9.2.1 虚拟机
9.2.2 虚拟机的汇编指令
9.2.1 虚拟机
虚拟机不是一台实际的机器,而是便于讨论的一台假想和 抽象的计算机。假设这台虚拟机有如下特性:
该虚拟机是一台地址单累加器的计算机,用“AC”表示该 累加器;设OP为操作码,d为地址,则指令: 0P d 表示
9.1.2 目标代码
本章采用汇编语言代码作为目标代码,但不针对某种特定的 目标机指令系统或汇编语言来生成目标代码,而是假设有 一台计算机,其指令系统等均按某种需要而设定,为教学 目的往往采取这种虚拟机目标代码形式。 下面就以一种虚拟机指令系统来讨论目标代码的生成。
9.1.3 寄存器分配
通常,计算机存储之间不直接打交道,而是通过寄存嚣。寄 存器可以用来保存中间计算结果,而且运算对象在寄存器 寄存器的分配策略与目标机的资源密切相关。有些机器中的 中的指令一般比运算对象在内存的指令短些,执行速度也 寄存器分为变址器和数据寄存器,还有些机器的寄存器可 快些,因此,充分利用寄存器对生成高质量目标代码尤其 以通用。 重要。寄存器的分配也自然成为目标代码生成中的主要问 按用途不同,寄存器可分为作为变址器使用、专供操作系统 题。 使用、用于目标代码中存放引用次数最多的变量三类。对 于前两类寄存器的分配方法比较直观,则这里主要讨论第 三类寄存器的分配方法。
假设指令编号从100开始,于是汇编指令编写的程序为 100 CLA <10> 101 ADD <<a[0]>> 102 STA l03 103 CLA 104 STO X
这里的第102条填地址指令“STA 103”填了第103条指令的地址部分,相当于@(Ac)=>103的地址部分, 因此第103条指令的地址部分为空。执行第103条指令时,隐含地址为<a[10]>,即CLA<a[10]>, 再执行第104条指令时,则有a[10] =>X,最终实现了X: = a[10]。 如果对X:=a[10]语句不采用填地址指令,其程序为 100 CLA <10> 101 ADD <<a[0]>> 102 STO M 103 ICA M 104 STO X 其中M为临时工作单元变量,用来存放<a[10]>,第103条指令使用间接取的方式,实现了X: = a[10]。
由于<a[0]>=<a[m]-m,所以有<a[m]>=<a[0]>+m。从而对于一维数组的存储单 元,有公式:
<a[i]>=<a[0]>+i 其中<a[0]>称为数组的假头,<a[m]>称为数组的真头。
9.2.2 虚拟机的汇编指令
例如,有赋值语句: X:=a[10] 设数组a为单块连续存放方式存放,<a[0]>为假头,则<a[10]>=<a[0]>+10。
9.1.3 寄存器分配
对于目标机器,如果有多个寄存器可供目标程序运行时使用, 在编译时应采用合理的方法分配这些寄存器。把运算数据 根据访问内存数来定义每条指令的执行代价,则对于以下的 存放在寄存器中,将会减少访问内存的次数,从而可以提 一些操作有其相应的执行代价: 高执行速度。 操作码 操作数l 操作数2 分配中不是把寄存器平均分配给各个变量使用,而是从可用 执行代价 的寄存器中分出几个,固定分配给几个简单变量使用。按 OP 寄存器 寄存器 l 照什么标准来分配呢?为此引入一个术语:指令的执行代 价,并规定访问内存一次的代价为1。 寄存器 内存单元 2 寄存器 寄存器间接地址 2
9.2.2 虚拟机的汇编指令
2.按真转编写程序
9.2.2 虚拟机的汇编指令
3.按假转编写程序
9.3 从中间代码生成目标代码
9.3.1 从逆波兰表示生成目标代码
9.3.2 从四元式序列生成目标代码
9.3.1 从逆波兰表生成目标代码
从表达式的逆波兰表示生成相应的目标代码的算法可给出 如下。 首先设立一个运算分量栈(栈),用来存放暂时不能处理的 运算分量的名(即工作单元地址)以及中间运算结果的名 (即存放中间结果的工作单元地址或累加器AC)。 其具体算法步骤如下。 从左到右扫描所给定的逆波兰表达式中的每个符号: 若扫描到运算分量,则将其下推入运算分量栈。 若扫描到运算符,按该运算符是几目运算,把运算分 量栈中相应个数的栈顶元素取出,生成该运算相应的目 标代码,此后栈上退去相应个数的运算分量,运算结果 存放在“AC”中,把“AC”标志入运算分量栈。 如此继续,直到整个逆波兰表达式处理完毕为止。
《编译原理简明教程》
普通高等教育“十二五”规划计算机教材
---太原理工大学 ---计算机科学与技术学院 ---冯秀芳、崔冬华、段富等
目 录
•第一章 引言 •第二章 形式语言理论基础 •第三章 自动机理论基础 •第四章 词法分析 •第五章 语法分析—自顶向下分析方法 •第六章 语法分析—自底向上分析方法 •第七章 语义分析及中间代码的生成 •第八章 代码优化 •第九章 目标代码的生成 •第十章 符号表 •第十一章 目标程序运行时的存储组织与分配 •第十二章 出错处理 •第十三章 编译程序自动生成工具简介 •第十四章 面向对象语言的编译 •第十五章 并行编译技术
9.3.1 从逆波兰表生成目标代码
所生成的目标代码为 例如,对于逆波兰表达式: CLA a ab*cd+e/MPY b 具体生成目标代码处理过程如表9.2所示。 M STO CLA c ADD d DIV e SUB M
9.3.1 从逆波兰表生成目标代码
所生成的目标代码为 又如,对于逆波兰表达式: SGN a aQbc*-d/ STO M 具体生成目标代码的处理过程如表9.3所示。b CLA MPY c ISU M DIV d
达式进行语法语义分析。 ② 检查栈有无元素。若没有,则当前运算符入栈,然后转回到步骤①检查下一 个符号;否则检查运算符的优先级。若当前的比栈顶的大,则当前运算符入 为了处理简单起见,规定被处理的简单表达式的前后都有 栈,然后转回步骤①,否则转到步骤③。
③ 检查当前符号和栈顶元素。若当前符号是“)”,而栈顶元素是“(”,则“(”上 退栈并转回第一步;若当前是“#”,栈项也是“#”,则处理完毕,算法结束。 #<简单表达式># 若不是上述两种情况则转到步骤④。
9.1.2 目标代码
代码生成程序的输出的形式一般有以下三种形式: ① 能够立即执行的机器语言代码,所有地址均已定位。即 具有绝对地址的机器语言代码。 ② 待装配的机器语言模块。当需要执行时,由连接装配程 这种形式(即已定位的机器语言代码作为输出)的好处是,它 序把它们和某些运行程序连接起来,装配成可以执行的机 可以放在内存固定的地方,可以立即执行,这样对于小的 器语言代码。即可浮动的机器语言代码。 程序可以迅速编译和执行。但由于不可重定位,其灵活性 比较差。在编译过程中,通常要把整个源程序一起编译。 ③这种形式是可浮动的机器语言代码,又称相对目标代码, 汇编语言形式的代码。需要经过汇编程序汇编转换成可 而不能独立地完成源程序各程序块的编译,即使是供源程 允许子程序分别编译,在具体执行前必须确定代码运行时 执行的机器语言代码。 序调用的子程序也必须同时进行编译。 在存储器中的位置,即给代码定位,从而形成可执行代码。 这种形式比前两种更具有灵活性。它的主要优点是可以产生 这种代码比较灵活,可以分别编译以及从目标模块中调用 符号指令和利用宏机制来帮助生成代码,目前不少编译程 先前已编译好的其他程序模块。常用的编译程序大多采用 序采用这种代码形式。 这种可浮动的代码形式。
9.1.1 目标代码生成程序的输入、输出
编译程序的最终输出是目标代码,这在编译程序的代码生成 阶段完成,也可在语义分析阶段生成。一般地,代码生成 部分称为代码生成程序。代码生成程序的功能是为源程序 生成与之等价的目标机器代码。也就是说,其输入是由先 行端产生的源程序的中间表示,输出是目标代码。