编译原理——中间代码生成

DREAMSJR 发布于 2024-08-07 612 次阅读


生成中间代码的目的

  • 便于优化
    • 让生成的目标代码效率更高
    • 优化不等同于对代码的精简和算法的优化
  • 便于移植
    • 编译前端:与目标机无关
    • 编译后端:与目标机相关

中间代码结构

逆波兰式(后缀表达式)

  • 变量次序与中缀式次序完全一致
  • 无括号
  • 运算符已按计算顺序排序

后缀表达式计算方式

遇数压栈,遇符计算

抽象语法树AGT和DAG

抽象语法树AGT

有向无环图DAG

三地址中间代码

  • 操作符的两个运算分量及其该操作的运算结果的抽象地址
  • 最常见的三地址中间代码有三元式和四元式两种

四元式操作符应包括以下几类:

  • 算数、逻辑、关系运算符
    • ADDI整型加,ADDF实型加,SUBI整型减,SUBF实型减,MULTI整型乘,MULTF实型乘,DIVI整型除,DIVF整型除,MOD,AND,OR,EQ,NE,GT,GE,LT,LE
  • I/O操作
    • (READI,-,-id)
    • (READF,-,-id)
    • (WRITE,-,-id)
  • 类型转换
    • (FLOAT,id_1,-,id_2)
  • 赋值
    • (ASSIG,id_1,-,id_2)
  • 地址加
    • (AADD,id_1,id_2,id_3)
  • 标号定义
    • (LABEL,-,-,label)
  • 条件/无条件转移
    • (JMP,-,-,label)
    • (JMP0,id,-,label) 条件假跳转
    • (JMP1,id,-,label) 条件真跳转
  • 过程调用
    • (ENTRY,Label,Size,Level) 子程序入口
      • Label 语句编号
      • Size 过程活动记录大小
      • Level 层数
    • (CALL,f,-,Result) 过程或函数调用
  • 传送参数参数传递中间代码
    • VARACT 地址
    • VALACT 变量
    • FUNACT 函数
    • PROACT 过程

语法制导方法

  • 语法制导技术在处理和规则相关联的任务中有着重要的应用
  • 语法制导就是在进行语法分析的同时要完成相应的语义动作,这些语义动作都是由一些程序组成的,要完成和用户的需求相关联的任务
  • 编译器对一个串进行语法检查的同时,可以按照语法分析的程序的结构对程序进行我们需要的操作,从而解决我们想要解决的问题
  • 制导方法的种类:依赖具体的语法分析方法,LL(1),LR(1)
  • 在进行LL(1)语法分析的过程中每遇到语义动作符,则调用相应的语义子程序来完成相应的语义处理。语义动作符可以加在产生式右部的任何位置。
  • 在进行LR(1)语法分析的过程中,每当按照某个产生式的右部进行规约时,就调用该产生式的右边的相应语义子程序来完成相应的语义处理。

LL(1)语法制导的实例

中间代码生成中的几个问题

  • 如果符号表一直保存到目标代码生成阶段,则在四元式中标识符的地址可用其在符号表中的地址表示。
  • 如果符号表不保存到目标代码生成阶段,则需要把目标代码生成阶段对名字进行地址分配所需要的相关信息放到中间代码中,这些信息包括标识符抽象地址(层数、偏移)以及直接/间接访问等信息,具体地说这些信息应放在四元式中的操作分量或运算结果的地址表示中。
  • 四元式中操作分量或运算结果的地址表示可以分为三大类
    • 标号类的语义信息是相应的标号值,包括过程/函数的入口标号
    • 数值类的语义信息就是该数据值
    • 地址类的语义信息由三部分组成
      • 层数
      • 偏移
      • 访问方式
        • 直接访问方式
        • 间接访问方式
          • 指针变量
          • 代表复杂变量地址的临时变量
  • 临时变量的地址表示:
    • 临时变量没有层数概念,因此对临时变量的层数可以取一个任意的负数
    • 在中间代码生成阶段尽管产生大量的临时变量,但经过优化后,只有少部分临时变量能保留下来,这样在中间代码生成阶段还不能确定临时变量的Offset值,因此临时变量的地址表示中的Offset暂时取为临时变量的编号。
  • 语义信息的提取与保存:

    • 四元式
    • 语义信息的两种表示方法
      • 指向对应符号表的指针
      • 把对应分量的语音信息放在此处

语义栈

在语法制导生成中间代码的过程中,要用到一个语义栈,主要用于存放运算分量和运算结果的语义信息。

该子程序主要用于表示式中间代码生成,其功能是产生一条四元式中间代码,以及类型检查工作。

哪些部分需要生成中间代码

表达式的中间代码生成

去左递归,去公共前缀

不用拘泥于形式,可以直接观察出中间代码

下标变量的中间代码生成

下标变量的地址计算

S为第i层数组变量元素空间大小

$S_{n-1}=D_ntimes S_nS_n=size(T)$

下标变量的四元式结构

一个[]三条中间代码,减乘除

赋值语句的中间代码

过程调用和函数调用的中间代码

  • 形参的种类
    • 值参
    • 变参(地址)
    • 函数(函数/过程指针)
  • 跳转的函数在哪
    • 实在函数:通过符号表去找
    • 形参函数:过程活动记录空间中,二元组(入口地址,display表)

GOTO语句和标号定位的中间代码

  • 标号声明格式:LABELL L[1],…,L[n]
  • 转向语句:goto L[i]
  • 标号定位:L[i]:S

标号的语义信息:所表示的代码地址,或是指其内部标号。

标号的处理原则:设立标号表,类似符号表,当进入一个局部化单位时,建立本层标号表,把本层的标号及其语义信息填入表中,当结束一个局部化单位时,删除本层符号表。

使用拉链法,头插法,将地址填到标号表的,如果有新的一条,就把四元式的最后一位置为标号表存的地址,把当前地址存到标号表中,如果最后发现了LABEL,就将定位与否设为已定位,并遍历拉链,回填地址。

条件语句的中间代码

While语句的中间代码

过程/函数声明的中间代码生成

此作者没有提供个人介绍
最后更新于 2024-08-07