代码之家  ›  专栏  ›  技术社区  ›  Jack

用JIT设计虚拟机

  •  4
  • Jack  · 技术社区  · 15 年前

    我正在开发一种为自己的虚拟机编译的脚本语言,这是一种简单的脚本语言,它具有处理类似 , 向量 , 漂浮物 等等……存储单元以这种方式表示:

    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 回复  |  直到 10 年前
        1
  •  7
  •   Steve Jessop    15 年前

    在编写JIT(“及时”)编译器之前,您至少应该考虑如何编写“提前一步”编译器。

    也就是说,给定一个包含虚拟机指令的程序,您将如何生成一个包含x86(或其他)指令的程序,该程序与原始程序相同?如何优化不同指令集和相同体系结构的不同版本的输出?您给出的示例操作码有一个非常复杂的实现,所以您只通过发出执行该任务的代码来实现“内联”哪些操作码,以及通过发出对某些共享代码的调用来实现哪些操作码?

    JIT必须能够做到这一点,而且它还必须在虚拟机运行时做出决定,决定它执行的代码是什么,何时执行,以及它如何表示虚拟机指令和本机指令的混合结果。

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

    另一种选择是编写一个非JIT编译器,以将VM指令(或原始脚本语言)转换为Java字节码,或LLVM,如杰夫·福斯特所说。然后让该字节码的工具链完成困难的、依赖于CPU的工作。

        2
  •  6
  •   Jeff Foster    15 年前

    虚拟机是一项需要考虑的大任务。你有没有考虑过把虚拟机建立在 LLVM ?

    LLVM将提供一个良好的基础,并且有很多 example projects 你可以用它来理解。

        3
  •  3
  •   Tomek Szpakowicz    15 年前

    SteveJessop有一个观点:JIT编译器比普通编译器要难得多。 而普通的编译器本身就很难实现。

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

    如果您的问题是这样的:

    我想创建一个光线跟踪程序,允许用户提供他们的着色程序等。 使用我自己的特定于域的语言。 没关系。 我已经定义了我的语言,实现了解释器,它工作得很好,也很正确。 但速度很慢:我如何才能以本机代码的形式执行它?

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

    • 将用户提供的过程转换为可以从程序调用的C函数。

    • 用适当的includes等将它们写出正常的C源文件。

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

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

    一些注释:

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

    • 不要放弃你的翻译。 把它作为语言的引用实现。

    推荐文章