代码生成- 寄存器计算机

寄存器计算机是目前最流行的机器体系结构之一。

  • 效率很高
  • 机器体系结构规整

机器基于寄存器架构:

  • 典型的有16、32或更多个寄存器,所有操作都在寄存器中进行
  • 访存都通过load/store进行,内存不能直接运算

寄存器计算机Reg的结构

  • 内存,存放溢出的变量(溢出是指寄存器放不下的变量)
  • 寄存器,进行运算的空间,假设有无限多个
  • 执行引擎,指令的执行

代码生成- 寄存器计算机_第1张图片

寄存器计算机的指令集(ISA)

指令的语法

s ->  movn n, r                将一个立即数赋值给一个寄存器(数据移动)

  ->  mov r1, r2                将一个寄存器的值赋值给另一个寄存器(数据移动)

  ->  load [x], r                 把内存中x的值读入到寄存器r中(访存)

  ->  store r, [x]                把一个数据从寄存器中写入到内存中(访存)

  ->  add r1, r2, r3           r3 = r1 + r2(算术运算)

  ->  sub r1, r2, r3           r3 = r1 - r2(算术运算)

  ->  times r1, r2, r3        r3 = r1 * r2(算术运算)

  ->  div r1, r2, r3            r3 = r1 / r2(算术运算)

注:[x]表示这样一个x是在内存当中的

变量的寄存器分配伪指令

Reg机器只支持一种数据类型int,并且给变量x分配寄存器的伪指令是

.int x

在代码生成阶段,假设Reg机器上有无限多个寄存器。

  • 因此每个声明变量和临时变量都会占用一个(虚拟)寄存器
  • 把虚拟寄存器分配到物理寄存器的过程称为寄存器分配

递归下降代码生成算法

从C--到Reg

代码生成- 寄存器计算机_第2张图片

注意:在栈式计算机中,每一个函数都是void类型,而此处寄存器计算机Gen_E(E)的返回值类型为R_t(寄存器类型)。

表达式的代码生成

不变式:表达式的值在函数返回的寄存器中

R_t Gen_E(E e)
{
    switch (e)
    {
    case n:
        r = fresh();    // 返回唯一的寄存器编号
        emit("movn n, r");
        return r;

    case id:
        r = fresh();
        emit("mov id, r");
        return r;

    case true:
        r = fresh();
        emit("movn 1, r");
        return r;
    
    case false:
        r = fresh();
        emit("movn 0, r");
        return r;

    case e1 + e2:
        r1 = Gen_E(e1);
        r2 = Gen_E(e2);
        r3 = fresh();
        emit("add r1, r2, r3");
        return r3;

    case e1 && e2:
        r1 = Gen_E(e1);
        r2 = Gen_E(e2);
        r3 = fresh();
        emit("and r1, r2, r3");
        return r3;   // 非短路

    default:
        break;
    }
}

语句的代码生成

Gen_S(S s)
{
    switch (s)
    {
    case id = e:
        r = Gen_E(e);
        emit("mov r, id");  // 假设此处id也在寄存器中
        break;
    
    case printi(e):
        r = Gen_E(e);
        emit("printi r");
        break;

    case printb(e):
        r = Gen_E(e);
        emit("printb r");
        break;

    default:
        break;
    }
}

类型的代码生成

不变式:只生成.int 类型

Gen_T(T t)
{
    switch (t)
    {
    case int:
        emit(".int");
        break;
    
    case bool:
        emit(".int");
        break;

    default:
        break;
    }
}

变量声明的代码生成

不变式:只生成.int 类型

Gen_D(T id; D)
{
    Gen_T(T);
    emit(" id");
    Gen_D(D);
}

程序的代码生成

不变式:只生成.int 类型

Gen_P(D S)
{
    Gen_D(D);
    Gen_S(S);
}

代码生成- 寄存器计算机_第3张图片

tip:将x = 1 + 2 + 3 + 4 画成抽象语法树,就可以理解对应产生的9条汇编级指令了。

如何运行生成的代码?

1、写一个虚拟机(解释器),这个虚拟机上就有无限个寄存器,我们可以用单链表存放寄存器,这个单链表可以无限长。

2、在真实的物理机器上运行:需进行寄存器分配

我们有无限多个虚拟寄存器(r1, ..., rn),但一台物理机上只有k1-km个寄存器,那么我们就需要把这些寄存器虚拟的映射到实际的物理机器上,如果还不够用的话,那么就需要有一些虚拟寄存器放到内存里(前面提到的“溢出”)

代码生成- 寄存器计算机_第4张图片

 

你可能感兴趣的:(编译原理,编译原理,开发语言,C)