我正在尝试将 Hoopl 引入一些编译器并遇到了一些问题:为 Hoopl 创建一个图形会使节点按照引入的标签的顺序出现。
例如:
(define (test) (if (eq? (random) 1 ) 2 (if (eq? (random) 2 ) 3 0) ) )
“编译”为
L25: call-direct random -> _tmp7_6
branch L27
L26: return RETVAL
L27: iconst 1 _tmp8_7
branch L28
L28: call-direct eq? _tmp7_6, _tmp8_7 -> _tmp4_8
branch L29
L29: cond-branch _tmp4_8 L30 L31
L30: iconst 2 RETVAL
branch L26
L31: call-direct random -> _tmp12_10
branch L32
L32: iconst 2 _tmp13_11
branch L33
L33: call-direct eq? _tmp12_10, _tmp13_11 -> _tmp9_12
branch L34
L34: cond-branch _tmp9_12 L36 L37
L35: assign RETVAL _tmp6_15
branch L26
L36: iconst 3 _tmp6_15
branch L35
L37: iconst 0 _tmp6_15
branch L35
由于从 AST 构建递归图的顺序,指令的顺序(按 showGraph 的顺序)很奇怪。为了生成代码,我需要以更自然的方式重新排序块,比如将返回 RETVAL 放置到函数的末尾,像这样合并块
branch Lx:
Lx: ...
进入一个街区,依此类推。似乎我需要类似的东西:
block1 = get block
Ln = get_last jump
block2 = find block Ln
if (some conditions)
remove block2
replace blick1 (merge block1 block2)
我对如何用 Hoopl 执行此操作感到非常困惑。当然,我可能会转储所有节点,然后在 Hoopl 框架之外执行转换,但我认为这是个坏主意。
有人可以给我胶水吗?我没有找到任何有用的例子。在 Lambdachine 项目中执行了类似的操作,但似乎太复杂了。
还有一个问题。是否有必要将所有 Call 指令设为非本地指令?考虑到 Call 的实现不会改变任何局部变量并且总是将控制权转移到块的下一条指令,这有什么意义呢?如果调用指令定义为
data Insn e x where
Call :: [Expr] -> Expr -> Label :: Insn O C -- last instruction of the block
这导致图表看起来更加奇怪。所以我用
-- what the difference with any other primitive, like "add a b -> c"
Call :: [Expr] -> Expr -> Label :: Insn O O
可能是我错了吗?