使用JIT设计虚拟机

时间:2009-11-28 12:17:22

标签: c++ vm-implementation

我正在开发一种脚本语言,可以编译自己的虚拟机,这是一个简单的虚拟机,可以处理某些类型的数据,如 points vectors 浮动等等......存储单元以这种方式表示:

struct memory_cell
{
    u32 id;
    u8 type;

    union
    {
        u8 b; /* boolean */
        double f; /* float */
        struct { double x, y, z; } v; /* vector */
        struct { double r, g, b; } c; /* color */
        struct { double r, g, b; } cw; /* color weight */
        struct { double x, y, z; } p; /* point variable */
        struct { u16 length; memory_cell **cells; } l; /* list variable */
    };  
};

指令是通用的,能够处理许多不同的操作数。例如

ADD dest, src1, src2

可以使用浮点数,向量,点,颜色根据操作数设置正确的目标类型。

主执行周期只检查指令的操作码(这是一个包含联合的结构来定义任何类型的指令)并执行它。我使用了一种简化的方法,其中我没有寄存器,只有大量的存储单元。

我想知道JIT是否可以帮助我获得最佳表现以及如何实现它。

正如我所说,到目前为止所达到的最佳实施是这样的:

 void VirtualMachine::executeInstruction(instr i)
 {
     u8 opcode = (i.opcode[0] & (u8)0xFC) >> 2;

     if (opcode >= 1 && opcode <= 17) /* RTL instruction */
     {
        memory_cell *dest;
        memory_cell *src1;
        memory_cell *src2;

        /* fetching destination */
        switch (i.opcode[0] & 0x03)
        {
            /* skip fetching for optimization */
            case 0: { break; }
            case MEM_CELL: { dest = memory[stack_pointer+i.rtl.dest.cell]; break; }
            case ARRAY_VAL: { dest = memory[stack_pointer+i.rtl.dest.cell]->l.cells[i.rtl.dest.index]; break; }
            case ARRAY_CELL: { dest = memory[stack_pointer+i.rtl.dest.cell]->l.cells[(int)i.rtl.dest.value]; break; }
        }

     /* omitted code */

     switch (opcode)
     {
         case ADD:
         {
             if (src1->type == M_VECTOR && src2->type == M_VECTOR)
             {
                 dest->type = M_VECTOR;
                 dest->v.x = src1->v.x + src2->v.x;
                 dest->v.y = src1->v.y + src2->v.y;
                 dest->v.z = src1->v.z + src2->v.z;
              }

      /* omitted code */

尝试jit编译是否容易/方便?但我真的不知道从哪里开始,这就是为什么我会提出一些建议。

除此之外,在开发它时我还应该考虑其他任何建议吗?

这个虚拟机应该足够快,可以为光线跟踪器计算着色器,但我还没有做过任何基准测试。

3 个答案:

答案 0 :(得分:7)

在编写JIT(“Just-in-time”)编译器之前,至少应该考虑如何编写“Way-ahead-time-time”编译器。

也就是说,如果程序包含VM的指令,您将如何生成由x86(或其他)指令组成的程序,该程序与原始程序相同?您将如何优化不同指令集的输出以及相同架构的不同版本?您给出的示例操作码具有相当复杂的实现,因此您可以通过发出执行该作业的代码来实现“内联”操作,并通过调用某些共享代码来实现哪些操作码?

JIT必须能够做到这一点,并且它还必须在VM运行时做出决策,执行它所执行的代码,何时执行,以及它如何表示VM指令和本机的混合结果指令。

如果你还不是集会骑师,那么我不建议你写一个JIT。这并不是说“不要永远”,但你应该在开始认真之前成为一名集会骑师。

另一种方法是编写非JIT编译器,将VM指令(或原始脚本语言)转换为Java字节码或LLVM,正如Jeff Foster所说。然后让该字节码的工具链完成与CPU相关的困难工作。

答案 1 :(得分:6)

VM是一项需要考虑的重大任务。您是否考虑过基于LLVM之类的虚拟机?

LLVM将提供一个良好的基础,您可以使用大量example projects来理解。

答案 2 :(得分:3)

Steve Jessop有一点:JIT编译器比普通编译器更难。 普通编译器本身很难。

但是,阅读问题的最后部分,我想知道你是否真的想要一个JIT编译器。

如果你的问题是这样的话:

  

我想创建一个光线跟踪程序,允许用户提供着色器程序等。   使用我自己的域特定语言。   没关系。   我已经定义了语言,实现了解释器,并且它运行良好且正确。   但它很慢:我如何将其作为本机代码执行?

然后我以前做的是类似的情况:

  • 将用户提供的程序翻译成可以从程序中调用的C函数。

  • 使用正确的#include等将它们写入普通的C源文件。

  • 使用普通的C编译器将它们编译为.dll(或* in。* nix)。

  • 在程序中动态加载.dll,找出函数指针并使用它们 在光线跟踪器中代替解释版本。

一些注意事项:

  • 在某些环境中,可能无法访问:无法访问C编译器或系统策略 禁止你加载你自己的DLL。 所以在尝试之前先检查一下。

  • 不要丢弃您的口译员。 将其作为您语言的参考实现。