因此,出于学习目的,我将 C 的一个子集编译为一个简单的堆栈 VM,我想知道如何最好地编译 switch 语句,例如
switch (e) {
case 0: { ... }
case 1: { ... }
...
case k: { ... }
}
我正在阅读的这本书提供了一种使用索引跳转编译它的简单方法,但书中描述的简单方案仅适用于连续的、升序的 case 值范围。
现在,我在第一遍使用符号标签,在第二遍中,我将把标签解析为实际的跳转目标,因为有了标签可以大大简化对堆栈指令的初始编译。我现在的想法是使用以下方案将书中的内容以任何顺序概括为任何 case 值序列。调用 case 值c1, c2, ..., cn
和相应的标签,然后假设 的值位于堆栈顶部,j1, j2, ..., jn
则生成以下指令序列:e
dup, loadc c1, eq, jumpnz j1,
dup, loadc c2, eq, jumpnz j2,
...
dup, loadc cn, eq, jumpnz jn,
pop, jump :default
j1: ...
j2: ...
...
jn: ...
default: ...
符号希望很清楚,但如果不是:dup
= 堆栈顶部的重复值,loadc c
= 将一个常量压入c
堆栈顶部,eq
= 比较堆栈上的顶部两个值并根据比较压入 0 或 1,jumpnz j
= 跳转到标签j
如果顶部堆栈值不为 0,则label:
= 在第二次编译过程中将解析为实际地址的东西。
所以我的问题是,编译 switch 语句的其他方法是什么?我的做法比连续范围的 case 值的索引跳转表要紧凑得多,但是当有很大的差距时效果很好。