编译器 - 中间代码生成

  • 简述

    源代码可以直接翻译成它的目标机器码,那么为什么我们需要将源代码翻译成中间代码,然后再翻译成它的目标代码呢?让我们看看我们需要中间代码的原因。
    中间代码
    • 如果编译器将源语言翻译成其目标机器语言而没有生成中间代码的选项,那么对于每台新机器,都需要一个完整的本机编译器。
    • 中间代码通过保持所有编译器的分析部分相同,消除了对每台独特机器的新完整编译器的需求。
    • 编译器的第二部分,综合,根据目标机器而改变。
    • 通过在中间代码上应用代码优化技术,应用源代码修改来提高代码性能变得更容易。
  • 中间代表

    中间代码可以用多种方式表示,它们有自己的好处。
    • High Level IR- 高级中间代码表示非常接近源语言本身。它们可以很容易地从源代码生成,我们可以很容易地应用代码修改来提高性能。但是对于目标机器优化,它是不太优选的。
    • Low Level IR- 这个靠近目标机器,适合寄存器和内存分配,指令集选择等,有利于机器相关的优化。
    中间代码可以是特定于语言的(例如,Java 的字节代码)或独立于语言的(三地址代码)。
  • 三地址码

    中间代码生成器以带注释的语法树的形式从其前身阶段语义分析器接收输入。然后可以将该语法树转换为线性表示,例如后缀表示法。中间代码往往是与机器无关的代码。因此,代码生成器假设有无限数量的内存存储(寄存器)来生成代码。
    例如:
    
    a = b + c * d;
    
    中间代码生成器会尝试将此表达式划分为子表达式,然后生成相应的代码。
    
    r1 = c * d;
    r2 = b + r1;
    a = r2
    
    r 在目标程序中用作寄存器。
    三地址代码最多有三个地址位置来计算表达式。三地址代码可以用两种形式表示:四元组和三元组。

    四胞胎

    四元组表示中的每条指令分为四个字段:运算符、arg1、arg2 和结果。上面的示例以四重格式表示如下:
    参数1 参数2 结果
    * C d r1
    + b r1 r2
    + r2 r1 r3
    = r3 一个

    三元组

    三元组表示中的每条指令都有三个字段:op、arg1和arg2。各个子表达式的结果由表达式的位置表示。三元组表示与 DAG 和语法树的相似性。它们在表示表达式时等价于 DAG。
    参数1 参数2
    * C d
    + b (0)
    + (1) (0)
    = (2)
    三元组在优化时面临代码不可移动的问题,因为结果是定位的,改变表达式的顺序或位置可能会导致问题。

    间接三元组

    这种表示是对三元组表示的增强。它使用指针而不是位置来存储结果。这使优化器能够自由地重新定位子表达式以生成优化的代码。
  • 声明

    必须先声明变量或过程,然后才能使用它。声明涉及在内存中分配空间以及在符号表中输入类型和名称。程序的编码和设计可能会牢记目标机器结构,但可能并不总是能够准确地将源代码转换为其目标语言。
    将整个程序视为过程和子过程的集合,可以将所有名称声明为过程的本地名称。内存分配以连续的方式完成,名称按照它们在程序中声明的顺序分配给内存。我们使用偏移变量并将其设置为零 {offset = 0} 表示基地址。
    源编程语言和目标机器架构可能在名称存储方式上有所不同,因此使用相对寻址。虽然第一个名称从内存位置 0 {offset=0} 开始分配内存,但稍后声明的下一个名称应分配到第一个名称旁边的内存。
    Example:
    我们以 C 编程语言为例,其中一个整数变量分配了 2 个字节的内存,一个浮点变量分配了 4 个字节的内存。
    
    int a;
    float b;
    Allocation process:
    {offset = 0}
       int a;
       id.type = int
       id.width = 2
    offset = offset + id.width 
    {offset = 2}
       float b;
       id.type = float
       id.width = 4
       
    offset = offset + id.width 
    {offset = 6}
    
    要在符号表中输入此详细信息,可以使用过程enter 。该方法可能具有以下结构:
    
    enter(name, type, offset)
    
    此过程应在符号表中为变量name创建一个条目,将其类型设置为 type 并在其数据区域中设置相对地址偏移量。