目标代码的生成第8章

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

8.3.1 从逆波兰表生成目标代码
例如,对于逆波兰表达式:
所生成的目标代码为
ab*cd+e/-
CLA a MPY b
具体生成目标代码处理过程如表8.2所示ST。O M
CLA c
ADD d
DIV e
SUB M
8.3.1 从逆波兰表生成目标代码
又如,对于逆波兰表达式:
所生成的目标代码为
aQbc*-d/
SGN a STO M
具体生成目标代码的处理过程如表8.3所CL示A 。b
MPY c
ISU M
DIV d
8.3.1 从逆波兰表生成目标代码
① 从上左面到右首扫先描把简简单单表达表式达中式的改每造个符成号中。间若语扫言描到的运逆算波分兰量形时就式下,推然入栈, 若扫描后到由运逆算波符兰(包表括达括号式和生“成#”目)时标就代转码到②。,实如际此中一也直扫常描把下两去步,合直到整 个表达为式一扫步描,结根束为据止运。算符优先数的大小关系,直接对简单表
各种代码的形式
• 中间代码: 后缀式,三地址代码,四元式 • 符号表中的项:名字,类型,嵌套深度,偏移
量 • 目标代码:绝对机器代码,可重定位代码,汇

代码生成器的输出必须是正确和高质量的 产生最优化代码的问题是不可判定的
目标代码的生成
目标代码生成是编译程序的最后一个工作阶段。它取先行阶段所Leabharlann Baidu 生的源程序的中间语言或优化后的中间语言表示作为输入,产生等价的 目标代码作为输出,如下图8.1所示。
退栈并转#<回简第单一表步达;若式当>#前是“#”,栈项也是“#”,则处理完毕,算法结束。
若不是上述两种情况则转到步骤④。
④ 应根并的据把目与栈标顶“运代运#算”码算视分,符为量然的优后栈性先同质(栈时(数单退)为目,栈0或相和的双栈应运目。算算运运法算符算符如。结)下果从同存。栈时放顶引在取进“一运A个C”或算中两,符个并栈分把(量“栈生A)C成”标相志
如果对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]。
8.2.2 虚拟机的汇编指令
2.按真转编写程序
8.2.2 虚拟机的汇编指令
3.按假转编写程序
8.3 从中间代码生成目标代码
8.3.1 从逆波兰表示生成目标代码 8.3.2 从四元式序列生成目标代码
8.3.1 从逆波兰表生成目标代码
从表达式的逆波兰表示生成相应的目标代码的算法可给出如下。 首先设立一个运算分量栈(栈),用来存放暂时不能处理的运算 分量的名(即工作单元地址)以及中间运算结果的名(即存放中间结 果的工作单元地址或累加器AC)。 其具体算法步骤如下。 从左到右扫描所给定的逆波兰表达式中的每个符号: 若扫描到运算分量,则将其下推入运算分量栈。 若扫描到运算符,按该运算符是几目运算,把运算分量栈 中相应个数的栈顶元素取出,生成该运算相应的目标代码,此后 栈上退去相应个数的运算分量,运算结果存放在“AC”中,把“AC” 标志入运算分量栈。 如此继续,直到整个逆波兰表达式处理完毕为止。
8.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 由于<a[0]>=<a[m]-m,所以有<a[m]>=<a[0]>+m。从而对于一维数组的存储单
对于目标机器,如果有多个寄存器可供目标程序运行时使用,在编
译时根应据采访用问合内理存的数方来法定分义配每这条些指寄令存的器执。行把代运价算,数则据对存于放以在下寄的存一器中些,操
将作会 有减其少相访应问的内执存行的代次价数: ,从而可以提高执行速度。
分配操中作不码是把寄存器操平作均数分l 配给各个操变作量数使2用,而是从可执用行的代寄价存
下推入栈,最后转回步骤②重复处理当前运算符。另外,在形成目标代码时 应注意“AC”有无被别的量占用,并进行相应处理。
生符先号前指已令编和译利好用的宏其机他制程来序帮模助块生。成常代用码的,编目译前程不序少大编多译采程用 序采这用种这可种浮代动码的形代式码。形式。
8.1.2 目标代码
本章采用汇编语言代码作为目标代码,但不针对某种特 定的目标机指令系统或汇编语言来生成目标代码,而是假设 有一台计算机,其指令系统等均按某种需要而设定,为教学 目的往往采取这种虚拟机目标代码形式。
② 检查达栈有式无进元行素语。法若没语有义,分则析当。前运算符入栈,然后转回到步骤①检查下一 个栈为,符了然号;后处否转理回则简步检单骤查起①运算,见符否,则的规转优定到先被步级。骤处③若理。当的前简的单比栈表顶达的式大的,前则当后前都运有算符入 特殊符号“#”,即呈下列形式:
③ 检查当前符号和栈顶元素。若当前符号是“)”,而栈顶元素是“(”,则“(”上
运行时的存储管理
通常所见到的计算机都是冯·诺依曼体系结构的,其特征是 变量——存储字,即用变量来仿效存储字,变量名实际上是 存储字的名字。
对于编译程序来说,必须对源程序中出现的变量与常量分 配运行时刻的存储空间,这一工作由先行端的分析和代码生 成程序共同完成。另从符号表的信息可以确定一个名字(标识 符)所代表数据对象在过程数据区中的相对地址。为了存储分 配的正确实现,除了必须考虑标识符的作用域问题外,还必 须考虑字边界对齐问题,即对于字节编址的计算机,必须注 意对于不同类型的量所分配存储区域的起始地址都必须符合 边界要求。
寄存器的分配策略与目标机的资源密切相关。有些机器中的寄存器 分为变址器和数据寄存器,还有些机器的寄存器可以通用。
按用途不同,寄存器可分为作为变址器使用、专供操作系统使用、 用于目标代码中存放引用次数最多的变量三类。对于前两类寄存器的分 配方法比较直观,则这里主要讨论第三类寄存器的分配方法。
寄存器分配
③这允在比而序执这汇种许存调不较行种编形子储用能差的形语式程器的独。机式言是序中子立器比在形可分的程语前地编式浮别位序言两完译的动编置也代种成过代的译,必码更源程码机,即须。具程中。器在给同有序,需语具代时灵各通要言体码进活程常经代执定行性序要过码行位编。块把汇,前,译它的整编又必从。的编个程称须而主译源序相确形要程,汇对定成优序即编目代可点一使转标码执是起是换代运行可编供成码行代以译源可,时码产程。。 这种代码比较灵活,可以分别编译以及从目标模块中调用
这里的第102条填地址指令“STA 103”填了第103条指令的地址部分,相当于@(Ac)=>103的地址部分, 因此第103条指令的地址部分为空。执行第103条指令时,隐含地址为<a[10]>,即CLA<a[10]>, 再执行第104条指令时,则有a[10] =>X,最终实现了X: = a[10]。
图8.1 目标代码生成过程
目标代码生成程序中的有关问题
对于一个好的代码生成程序,要求其使所生成的目标 代码尽可能地短,并能较充分地发挥目标计算机可用资源的 效率。如充分利用计算机的寄存器或变址器,以节省访问内 存的时间,尽可能使用执行速度较快的指令,存储管理合理, 等等。
熟悉目标机器和它的指令系统是设计一个好的代码生 成程序的先决条件,但在代码生成的一般性讨论中,不能对 目标机器细节描述到足够详细的程度,以便对一个完整的语 言产生好的代码。本章不以某一台具体的计算机做背景,只 是针对一个假想的计算机模型——虚拟机给出生成目标代码 的算法。
该虚拟机是一台地址单累加器的计算机,用“AC”表示该 累加器;设OP为操作码,d为地址,则指令: 0P d
表示 AC OP d=>AC
即累加器AC中的内容与d中的内容进行某种运算,结果送 到累加器AC中;其内存容量足够大;提供了21条符号汇 编指令。
8.2.2 虚拟机的汇编指令
虚拟机的汇编指令如表8.1所示。
器中分出几O个P ,固定分配寄给存几器个简单变量寄使存用器。按照什么标准来l分配呢? 为此引入一个术语:指令寄的存执器行代价,并规内定存访单问元内存一次的代价2为1。
寄存器
寄存器间接地址
2
寄存器
内存间接地址
3
寄存器分配
基于以上原因,分配中尽可能把变量值保留在寄存器中,当一个寄 存器的值不再需要时,该寄存器可以收回留作他用,但这只有对程序做 了全面的数据流分析后才能确定,这种分析要做大量的额外工作不太实 际,在寄存器的分配中常常以基本块为单位进行。
目标代码生成程序的输入、输出
编译程序的最终输出是目标代码,这在编译程序的代码 生成阶段完成,也可在语义分析阶段生成。一般地,代码生 成部分称为代码生成程序。代码生成程序的功能是为源程序 生成与之等价的目标机器代码。也就是说,其输入是由先行 端产生的源程序的中间表示,输出是目标代码。
假定在代码生成前,编译的先行端已扫描、分析和翻译 源程序,成为足够详细的中间表示,这样,中间语言中名字 的值可以表示为目标机器能够直接操作的量(位、整数、实 数、指针等),还假定必要的类型检查与插入类型转换等已 完成,不仅语法上正确,语义也是正确的,这样代码生成阶 段可以认为它的输入是正确的。
在一个基本块内,按照中间语言代码的顺序,逐个基本块产生目标 代码。生成的目标代码应尽可能将运算的结果存放在寄存器中,直到该 寄存器必须用来存放别的变量或基本块结束为止。这样可以减少基本块 访问内存的次数,从而提高运行速度。
如果把寄存器分配给某些变量,则该变量在定值前每引用一次,将 可少访问一次内存,从而可节省执行代价1;如果某变量在基本块中被定 值,且出基本块后还要被引用,则把寄存器固定分配给该变量,可省去 把它保存到内存单元的操作,从而节省执行代价2。
例如:假定一个字包含4字节,每个字符占1字节,每个整 型量占4字节,则对于字符型量的存储位置可以任意,然而, 对于整型变量的存储区域之开始地址必须是0,4,8,16,…, 否则会引起错误。
8.2 一个计算机模型——虚拟机
8.2.1 虚拟机 8.2.2 虚拟机的汇编指令
8.2.1 虚拟机
虚拟机不是一台实际的机器,而是便于讨论的一台假想和 抽象的计算机。假设这台虚拟机有如下特性:
元,有公式: <a[i]>=<a[0]>+i 其中<a[0]>称为数组的假头,<a[m]>称为数组的真头。
8.2.2 虚拟机的汇编指令
例如,有赋值语句:
X:=a[10]
设数组a为单块连续存放方式存放,<a[0]>为假头,则<a[10]>=<a[0]>+10。
假设指令编号从100开始,于是汇编指令编写的程序为 100 CLA <10> 101 ADD <<a[0]>> 102 STA l03 103 CLA 104 STO X
下面就以一种虚拟机指令系统来讨论目标代码的生成。
寄存器分配
通常,计算机存储之间不直接打交道,而是通过寄存器。寄存器可 以用来保存中间计算结果,而且运算对象在寄存器中的指令一般比运算 对象在内存的指令短些,执行速度也快些,因此,充分利用寄存器对生 成高质量目标代码尤其重要。寄存器的分配也自然成为目标代码生成中 的主要问题。
目标代码
代码生成程序的输出的形式一般有以下三种形式:
① 能够立即执行的机器语言代码,所有地址均已定位。即 具有绝对地址的机器语言代码。
②这种待形装式配(的即机已器定语位言的模机块器。语当言需代要码执作行为时输,出由)的连好接处装是配,程它 序可把以它放们在和内某存些固运定行的程地序方连,接可起以来立,即装执配行成,可这以样执对行于的小机的 器程语序言可代以码迅。速即编可译浮和动执的行机。器但语由言于代不码可。重定位,其灵活性
第八章
目标代码生成
主要内容
目标代码生成的任务 设计目标代码生成器需要考虑的主要问题 简单的代码生成器 Sample语言目标代码生成器的设计
教学要求
重点掌握
代码生成要考虑的主要问题 寄存器的分配 基本块的代码生成以及从DAG生成代码
8.1 概述
代码生成所 处的位置
代码生成器的作用
相关文档
最新文档