用Java编写的编译器:窥视孔优化器实现

用Java编写的编译器:窥视孔优化器实现,java,compiler-construction,compiler-optimization,Java,Compiler Construction,Compiler Optimization,我正在为Pascal的一个子集编写编译器。编译器为一台组装好的机器生成机器指令。我想为这种机器语言编写一个窥视孔优化器,但是我在替换一些更复杂的模式时遇到了困难 窥视孔优化器规范 我研究了几种编写窥视孔优化器的不同方法,并确定了一种后端方法: 每次生成机器指令时,编码器都会调用emit()函数 emit(指令currentInstr)检查窥视孔优化表: 如果当前指令与模式的尾部匹配: 检查以前发出的指令是否匹配 如果所有指令都与模式匹配,则应用优化,修改代码存储的结尾 如果未找到优化,则照

我正在为Pascal的一个子集编写编译器。编译器为一台组装好的机器生成机器指令。我想为这种机器语言编写一个窥视孔优化器,但是我在替换一些更复杂的模式时遇到了困难

窥视孔优化器规范 我研究了几种编写窥视孔优化器的不同方法,并确定了一种后端方法:

  • 每次生成机器指令时,编码器都会调用
    emit()
    函数
  • emit(指令currentInstr)
    检查窥视孔优化表:
    • 如果当前指令与模式的尾部匹配:
    • 检查以前发出的指令是否匹配
    • 如果所有指令都与模式匹配,则应用优化,修改代码存储的结尾
    • 如果未找到优化,则照常发出指令
当前的设计方法 这个方法很简单,我在实现上遇到了麻烦。在我的编译器中,机器指令存储在
指令
类中。我编写了一个
指令match
类,该类存储正则表达式,用于匹配机器指令的每个组件。如果模式匹配某些机器指令,则其
equals(instr指令)
方法返回
true

然而,我无法完全应用我的规则。首先,我觉得按照我目前的方法,我最终会得到一堆不必要的东西。考虑到一个完整的窥视孔优化列表可以包含大约400个模式,这将很快失去控制。此外,使用这种方法,我实际上无法获得更困难的替换(请参阅“我的问题”)

替代方法 我读过的一篇论文将以前的指令折叠成一个长字符串,使用正则表达式进行匹配和替换,并将字符串转换回机器指令。这对我来说似乎是个糟糕的方法,如果我错了,请纠正我

示例模式、模式语法
x:跳转x+1;x+1:跳转y-->x:跳转y
loadlx;负荷;添加-->加载x+y
加载d[r];STOREI(n)->STORE(n)d[r]
请注意,这些示例模式中的每一个都只是以下机器指令模板的可读表示:

op_code register n d
(n通常表示字数,d表示地址位移)。语法
x:
表示指令存储在代码存储中的地址
x

因此,当
LOADL
操作码为5时(
n
r
在此指令中未使用),指令
LOADL 17
相当于完整的机器指令
5 0 0 17

我的问题 因此,在这样的背景下,我的问题是:当我需要在替换中包含以前的部分指令作为变量时,如何有效地匹配和替换模式?例如,我可以简单地替换
loadl1的所有实例;使用递增机器指令添加
——我不需要前面指令的任何部分来执行此操作。但我不知道如何在替换模式中有效地使用第二个示例中的“x”和“y”值


编辑:我应该提到,
指令
类的每个字段都只是一个整数(这对于机器指令来说是正常的)。在模式表中使用“x”或“y”都是代表任何整数值的变量。

一种简单的方法是将窥视孔优化器实现为有限状态机

我们假设您有一个生成指令但不发出指令的原始代码生成器,以及一个将实际代码发送到对象流的发出例程

状态机捕获代码生成器生成的指令,并通过在状态之间转换来记住生成的0条或多条指令的序列。因此,一个状态隐式地记住一个(短)已生成但未发出的指令序列;它还必须记住它捕获的指令的关键参数,例如寄存器名、常量值和/或寻址模式以及抽象目标内存位置。特殊的启动状态会记住空指令串。在任何时候,您都需要能够发出未提交的指令(“刷新”);如果您一直这样做,您的窥视孔生成器将捕获下一条指令,然后将其发出,而不做任何有用的工作

为了做有用的工作,我们希望机器捕获尽可能长的序列。由于通常有许多种机器指令,实际上,你不能记住太多的一行,否则状态机将变得庞大。但对于最常见的机器指令(加载、添加、cmp、分支、存储),记住最后两三条指令是可行的。机器的大小实际上是由我们关心的最长窥视孔的长度决定的,但如果该长度是P,则整个机器不必是P

根据代码生成器生成的“下一条”指令,每个状态都有到下一个状态的转换。假设一个状态代表N条指令的捕获。 过渡选择包括:

  • 刷新此状态表示的最左边的0条或更多(称为k条)指令,并转换到下一个状态,表示N-k+1,表示机器指令I的额外捕获的指令
  • 刷新此状态表示的最左边的k指令,转换到状态 表示剩余的N-k指令和重新处理指令I
  • 完全刷新状态,并发出指令I。实际上你可以 在[仅在开始状态]上执行此操作
刷新k指令时,实际发出的是
 PUSHVAR x
 PUSHK i
 ADD
 POPVAR x
 MOVE x,k
 PUSHK i, PUSHK j, ADD ==> PUSHK i+j
 PUSHK i, POPVAR x ==> MOVE x,i 
 PEEPHOLESTATE (an enum symbol, initialized to EMPTY)
 FIRSTCONSTANT (an int)
 SECONDCONSTANT (an int)
GeneratePUSHK:
    switch (PEEPHOLESTATE) {
        EMPTY: PEEPHOLESTATE=PUSHK;
               FIRSTCONSTANT=K;
               break;
        PUSHK: PEEPHOLESTATE=PUSHKPUSHK;
               SECONDCONSTANT=K;
               break;
        PUSHKPUSHK:
        #IF consumeEmitLoadK // flush state, transition and consume generated instruction
               emit(PUSHK,FIRSTCONSTANT);
               FIRSTCONSTANT=SECONDCONSTANT;
               SECONDCONSTANT=K;
               PEEPHOLESTATE=PUSHKPUSHK;
               break;
        #ELSE // flush state, transition, and reprocess generated instruction
               emit(PUSHK,FIRSTCONSTANT);
               FIRSTCONSTANT=SECONDCONSTANT;
               PEEPHOLESTATE=PUSHK;
               goto GeneratePUSHK;  // Java can't do this, but other langauges can.
        #ENDIF
     }

  GenerateADD:
    switch (PEEPHOLESTATE) {
        EMPTY: emit(ADD);
               break;
        PUSHK: emit(PUSHK,FIRSTCONSTANT);
               emit(ADD);
               PEEPHOLESTATE=EMPTY;
               break;
        PUSHKPUSHK:
               PEEPHOLESTATE=PUSHK;
               FIRSTCONSTANT+=SECONDCONSTANT;
               break:
     }  

  GeneratePOPX:
    switch (PEEPHOLESTATE) {
        EMPTY: emit(POP,X);
               break;
        PUSHK: emit(MOV,X,FIRSTCONSTANT);
               PEEPHOLESTATE=EMPTY;
               break;
        PUSHKPUSHK:
               emit(MOV,X,SECONDCONSTANT);
               PEEPHOLESTATE=PUSHK;
               break:
     }

GeneratePUSHVARX:
    switch (PEEPHOLESTATE) {
        EMPTY: emit(PUSHVAR,X);
               break;
        PUSHK: emit(PUSHK,FIRSTCONSTANT);
               PEEPHOLESTATE=EMPTY;
               goto GeneratePUSHVARX;
        PUSHKPUSHK:
               PEEPHOLESTATE=PUSHK;
               emit(PUSHK,FIRSTCONSTANT);
               FIRSTCONSTANT=SECONDCONSTANT;
               goto GeneratePUSHVARX;
     }
  flush() {
    switch (PEEPHOLESTATE) {
        EMPTY: break;
        PUSHK: emit(PUSHK,FIRSTCONSTANT);
               break;
        PUSHKPUSHK:
               emit(PUSHK,FIRSTCONSTANT),
               emit(PUSHK,SECONDCONSTANT),
               break:
      }
      PEEPHOLESTATE=EMPTY;
      return; }
      PUSHK  1
      PUSHK  2
      ADD
      PUSHK  5
      POPVAR X
      POPVAR Y