在解释器循环(或其他有限状态机)中,有几种方法可以调度操作(或基于下一个输入跳转状态)。 C中显而易见的方法是在循环中使用switch语句。但是,在循环内使用开关存在性能问题。在最坏的情况下,可以将开关编码为分支序列,如果操作编码不连续(或者编译器不能推断它是连续的),则会发生这种情况。即使在编译器生成跳转表的乐观情况下,仍然会有一个冗余跳转回调度程序(我还没有看到编译器在循环中看到切换时内联调度)。此外,调度程序中通常还会进行冗余边界检查。
GCC团队通过引入Computed Gotos来解决这个问题。哪个语言扩展允许引用标签,以后可以使用标签指针上的goto
跳转到标签。这将生成理想的汇编代码:操作码索引的跳转表,在每次操作后都内联调度。
有没有办法,使用ISO C,生成类似的汇编代码?
最接近的我是将每个操作作为一个函数编写,将函数指针放在一个表中,并利用尾调用优化来生成跳转而不是调用。这个问题很明显:它依赖于可能存在或不存在的优化。如果没有优化,生成的代码将消耗堆栈并最终导致应用程序崩溃。
我尝试生成的理想汇编代码(GAS .intel_syntax,类似于NASM语法)
.intel_syntax noprefix
.section .rodata
optable:
.quad nop
.quad add
# more ops here
.section .text
## interpreter loop entry here
nop:
call nextinput # return value = table index in rax
jmp [optable + rax]
add:
# add stuff here
call nextinput
jmp [optable + rax]