我正试图为一个带有指令集的自行设计的CPU编写一个编译器。CPU具有3个寄存器、2个输入寄存器(B和C)和一个输出寄存器(D)。例如,当执行ADD指令时,计算B和C的和并将其存储在D中。
我试图用访问者设计模式编写编译器:我有一堆语言树类,如"IfStatement“、”加法“、"Integer”和一个访问者“编译器”。访问者将查看树的每个节点,并将字节码附加到字节码列表的末尾。我不知道如何干净地处理寄存器重写:当计算表达式时
2*(7+3)生成的字节码是
PUTb 2
PUTb 7
PUTc 3
ADD
MOVE D C
MUL如您所见,2被7覆盖。
(7+3)*2或者它可以存储临时结果是RAM,使用其他一些指令,这对于更复杂的表达式来说当然是必要的,比如
(7-5)*(8+3)是否有一种干净的/面向对象的方法来处理这个问题?这里的访客模式不合适吗?我需要看一些先进的技术,如注册着色吗?编译器将用Java编写,但我认为这并不重要。
发布于 2016-01-31 15:59:48
您将需要一个溢出区域,可能是堆栈,或者是一些内存块。
您还需要在优化之前纠正生成的代码,因为优化损坏的代码基本上是不可能的。
因此,给定一个堆栈,您应该有类似于:PUT b,2; Push b; Put b,7; Put c,3; ADD; Pop b; MUL和如果使用内存的话,PUT b,2; Store b @1; Put b,7; Put c,3; ADD; Load b @1; MUL。接下来,为了获得优化,您可以通过根据简单的权重更改操作数的顺序,获得平衡树的单独传递。
您可以创建一个数据结构来跟踪寄存器的内容(您可以假设语句开始时为空)。每次生成指令时,都会更新该数据结构以标记其中的内容(例如,哪个树节点和/或什么操作数、i.e.variable或常量或溢出临时变量)。
在生成操作之前,要确保其源在需要的寄存器中,如果没有,则从溢出区域、变量区域、常量区域/直接区或另一个寄存器执行负载。
(在设计上,您不应该需要在此时生成除某种负载之外的其他任何东西,例如,如果要生成MUL,就不必生成一个ADD,因为应该已经在适当的访问遍历中处理了这一点。)
但是--在执行任何操作数负载(这将覆盖寄存器)之前,如果有任何跟踪,您将生成一个存储到溢出区域。对目标/输出寄存器(例如MUL)也做同样的操作。并在整个过程中更新跟踪结构,以便生成的下一条指令将具有寄存器的状态&溢出温度,截止到代码中的当前点。
https://softwareengineering.stackexchange.com/questions/308904
复制相似问题