热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

在程序集中编写JIT编译器-WritingaJITcompilerinassembly

IvewrittenavirtualmachineinCwhichhasdecentperformanceforanon-JITVM,butIwanttolea

I've written a virtual machine in C which has decent performance for a non-JIT VM, but I want to learn something new, and improve performance. My current implementation simply uses a switch to translate from VM bytecode to instructions, which is compiled to a jump table. Like I said, decent performance for what it is, but I've hit a barrier that can only be overcome with a JIT compiler.

我用C语言编写了一个虚拟机,它对于非jit VM有很好的性能,但是我想学习一些新的东西,并提高性能。我当前的实现只是使用一个开关将VM字节码转换为指令,指令被编译为跳转表。就像我说的,这是一种不错的性能,但是我遇到了一个障碍,只能用JIT编译器来克服。

I've already asked a similar question not long ago about self-modifying code, but I came to realize that I wasn't asking the right question.

不久前我已经问过一个类似的关于自我修改代码的问题,但我开始意识到我问的不是正确的问题。

So my goal is to write a JIT compiler for this C virtual machine, and I want to do it in x86 assembly. (I'm using NASM as my assembler) I'm not quite sure how to go about doing this. I'm comfortable with assembly, and I've looked over some self-modifying code examples, but I haven't come to figure out how to do code generation just yet.

因此,我的目标是为这个C虚拟机编写一个JIT编译器,我想在x86汇编语言中实现它。(我使用NASM作为我的汇编程序)我不太确定该怎么做。我对汇编很熟悉,我已经看过一些自我修改的代码示例,但是我还没有找到如何生成代码的方法。

My main block so far is copying instructions to an executable piece of memory, with my arguments. I'm aware that I can label a certain line in NASM, and copy the entire line from that address with the static arguments, but that's not very dynamic, and doesn't work for a JIT compiler. I need to be able to interpret the instruction from bytecode, copy it to executable memory, interpret the first argument, copy it to memory, then interpret the second argument, and copy it to memory.

到目前为止,我的主要模块是使用我的参数将指令复制到可执行内存中。我知道,我可以在NASM中标记某一行,并使用静态参数从该地址复制整行,但这不是动态的,对JIT编译器不起作用。我需要能够解释来自字节码的指令,将它复制到可执行内存中,解释第一个参数,将它复制到内存中,然后解释第二个参数,并将其复制到内存中。

I've been informed about several libraries that would make this task easier, such as GNU lightning, and even LLVM. However, I'd like to write this by hand first, to understand how it works, before using external resources.

我被告知有几个库可以使这个任务更容易,比如GNU lightning,甚至LLVM。但是,我想先用手来写,在使用外部资源之前先了解它是如何工作的。

Are there any resources or examples this community could provide to help me get started on this task? A simple example showing two or three instructions like "add" and "mov" being used to generate executable code, with arguments, dynamically, in memory, would do wonders.

这个社区有什么资源或例子可以帮助我开始这项任务吗?一个简单的示例显示使用“添加”和“mov”之类的指令来生成可执行代码,并在内存中动态地使用参数,这将带来奇迹。

2 个解决方案

#1


18  

I wouldn't recommend writing a JIT in assembly at all. There are good arguments for writing the most frequently executed bits of the interpreter in assembly. For an example of how this looks like see this comment from Mike Pall, the author of LuaJIT.

我完全不建议在程序集中编写JIT。在汇编语言中编写最频繁执行的解释器位是有很好的理由的。要了解这种情况的示例,请参见《LuaJIT》的作者Mike Pall的评论。

As for the JIT, there are many different levels with varying complexity:

至于JIT,有很多不同的层次,复杂程度不同:

  1. Compile a basic block (a sequence of non-branching instructions) by simply copying the interpreter's code. For example, the implementations of a few (register-based) bytecode instructions might look like this:

    通过简单地复制解释器的代码,编译一个基本块(非分支指令序列)。例如,一些(基于寄存器的)字节码指令的实现可能是这样的:

    ; ebp points to virtual register 0 on the stack
    instr_ADD:
        
        mov eax, [ebp + ecx * 4]  ; load first operand from stack
        add eax, [ebp + edx * 4]  ; add second operand from stack
        mov [ebp + ebx * 4], eax  ; write back result
        
    instr_SUB:
        ... ; similar
    

    So, given the instruction sequence ADD R3, R1, R2, SUB R3, R3, R4 a simple JIT could copy the relevant parts of the interpreters implementation into a new machine code chunk:

    因此,给定指令序列加上R3、R1、R2、R3、R3、R4,一个简单的JIT就可以将解释器实现的相关部分复制到一个新的机器代码块中:

        mov ecx, 1
        mov edx, 2
        mov ebx, 3
        mov eax, [ebp + ecx * 4]  ; load first operand from stack
        add eax, [ebp + edx * 4]  ; add second operand from stack
        mov [ebp + ebx * 4], eax  ; write back result
        mov ecx, 3
        mov edx, 4
        mov ebx, 3
        mov eax, [ebp + ecx * 4]  ; load first operand from stack
        sub eax, [ebp + edx * 4]  ; add second operand from stack
        mov [ebp + ebx * 4], eax  ; write back result
    

    This simply copies the relevant code, so we need to initialise the registers used accordingly. A better solution would be to translate this directly into machine instructions mov eax, [ebp + 4], but now you already have to manually encode the requested instructions.

    这只是复制相关的代码,因此我们需要初始化相应使用的寄存器。更好的解决方案是直接将其转换为机器指令mov eax [ebp + 4],但是现在您已经需要手动编码所请求的指令。

    This technique removes the overheads of interpretation, but otherwise does not improve efficiency much. If the code is executed for only one or two times, then it may not worth it to first translate it to machine code (which requires flushing at least parts of the I-cache).

    这种技术消除了解释的开销,但没有提高效率。如果代码只执行一到两次,那么首先将其转换为机器码可能不值得(这需要至少刷新I-cache的一部分)。

  2. While some JITs use the above technique instead of an interpreter, they then employ a more complicated optimisation mechanism for frequently executed code. This involves translating the executed bytecode into an intermediate representation (IR) on which additional optimisations are performed.

    虽然有些JITs使用上述技术而不是解释器,但是它们会为频繁执行的代码使用更复杂的优化机制。这涉及到将已执行的字节码转换为中间表示(IR),在其中执行额外的优化。

    Depending on the source language and the type of JIT, this can be very complex (which is why many JITs delegate this task to LLVM). A method-based JIT needs to deal with joining control-flow graphs, so they use SSA form and run various analyses on that (e.g., Hotspot).

    根据源语言和JIT的类型,这可能非常复杂(这就是为什么许多JITs将这个任务委托给LLVM)。基于方法的JIT需要处理连接控制流图,因此他们使用SSA形式并对其进行各种分析(例如,Hotspot)。

    A tracing JIT (like LuaJIT 2) only compiles straight line code which makes many things easier to implement, but you have to be very careful how you pick traces and how you link multiple traces together efficiently. Gal and Franz describe one method in this paper (PDF). For another method see the LuaJIT source code. Both JITs are written in C (or perhaps C++).

    跟踪JIT(如LuaJIT 2)只编译直线代码,这使许多事情更容易实现,但是您必须非常小心地选择跟踪,以及如何高效地将多个跟踪链接在一起。Gal和Franz在本文中描述了一种方法。有关另一个方法,请参阅LuaJIT源代码。两个JITs都是用C(或者可能是c++)编写的。

#2


7  

I suggest you look at the project http://code.google.com/p/asmjit/. By using the framework it provides, you can save a lot of energy. If you want write all things by hand, just read the source and rewrite it yourself, I think it's not very hard.

我建议您查看项目http://code.google.com/p/asmjit/。通过使用它提供的框架,您可以节省很多能源。如果你想手工写所有的东西,只要阅读源代码并自己重写,我认为这并不难。


推荐阅读
  • 本文介绍了Swing组件的用法,重点讲解了图标接口的定义和创建方法。图标接口用来将图标与各种组件相关联,可以是简单的绘画或使用磁盘上的GIF格式图像。文章详细介绍了图标接口的属性和绘制方法,并给出了一个菱形图标的实现示例。该示例可以配置图标的尺寸、颜色和填充状态。 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • 开发笔记:Java是如何读取和写入浏览器Cookies的
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了Java是如何读取和写入浏览器Cookies的相关的知识,希望对你有一定的参考价值。首先我 ... [详细]
  • 在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板
    本文介绍了在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板的方法和步骤,包括将ResourceDictionary添加到页面中以及在ResourceDictionary中实现模板的构建。通过本文的阅读,读者可以了解到在Xamarin XAML语言中构建控件模板的具体操作步骤和语法形式。 ... [详细]
  • Java中包装类的设计原因以及操作方法
    本文主要介绍了Java中设计包装类的原因以及操作方法。在Java中,除了对象类型,还有八大基本类型,为了将基本类型转换成对象,Java引入了包装类。文章通过介绍包装类的定义和实现,解答了为什么需要包装类的问题,并提供了简单易用的操作方法。通过本文的学习,读者可以更好地理解和应用Java中的包装类。 ... [详细]
  • CEPH LIO iSCSI Gateway及其使用参考文档
    本文介绍了CEPH LIO iSCSI Gateway以及使用该网关的参考文档,包括Ceph Block Device、CEPH ISCSI GATEWAY、USING AN ISCSI GATEWAY等。同时提供了多个参考链接,详细介绍了CEPH LIO iSCSI Gateway的配置和使用方法。 ... [详细]
  • 纠正网上的错误:自定义一个类叫java.lang.System/String的方法
    本文纠正了网上关于自定义一个类叫java.lang.System/String的错误答案,并详细解释了为什么这种方法是错误的。作者指出,虽然双亲委托机制确实可以阻止自定义的System类被加载,但通过自定义一个特殊的类加载器,可以绕过双亲委托机制,达到自定义System类的目的。作者呼吁读者对网上的内容持怀疑态度,并带着问题来阅读文章。 ... [详细]
  • 合并列值-合并为一列问题需求:createtabletab(Aint,Bint,Cint)inserttabselect1,2,3unionallsel ... [详细]
  • ***byte(字节)根据长度转成kb(千字节)和mb(兆字节)**parambytes*return*publicstaticStringbytes2kb(longbytes){ ... [详细]
  • Servlet多用户登录时HttpSession会话信息覆盖问题的解决方案
    本文讨论了在Servlet多用户登录时可能出现的HttpSession会话信息覆盖问题,并提供了解决方案。通过分析JSESSIONID的作用机制和编码方式,我们可以得出每个HttpSession对象都是通过客户端发送的唯一JSESSIONID来识别的,因此无需担心会话信息被覆盖的问题。需要注意的是,本文讨论的是多个客户端级别上的多用户登录,而非同一个浏览器级别上的多用户登录。 ... [详细]
  • 微信官方授权及获取OpenId的方法,服务器通过SpringBoot实现
    主要步骤:前端获取到code(wx.login),传入服务器服务器通过参数AppID和AppSecret访问官方接口,获取到OpenId ... [详细]
  • ShiftLeft:将静态防护与运行时防护结合的持续性安全防护解决方案
    ShiftLeft公司是一家致力于将应用的静态防护和运行时防护与应用开发自动化工作流相结合以提升软件开发生命周期中的安全性的公司。传统的安全防护方式存在误报率高、人工成本高、耗时长等问题,而ShiftLeft提供的持续性安全防护解决方案能够解决这些问题。通过将下一代静态代码分析与应用开发自动化工作流中涉及的安全工具相结合,ShiftLeft帮助企业实现DevSecOps的安全部分,提供高效、准确的安全能力。 ... [详细]
  • SpringBoot简单日志配置
     在生产环境中,只打印error级别的错误,在测试环境中,可以调成debugapplication.properties文件##默认使用logbacklogging.level.r ... [详细]
  • OpenMap教程4 – 图层概述
    本文介绍了OpenMap教程4中关于地图图层的内容,包括将ShapeLayer添加到MapBean中的方法,OpenMap支持的图层类型以及使用BufferedLayer创建图像的MapBean。此外,还介绍了Layer背景标志的作用和OMGraphicHandlerLayer的基础层类。 ... [详细]
author-avatar
mobiledu2502912277
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有