Compiler construction JIT编译实际上是如何在运行时执行机器代码的?

Compiler construction JIT编译实际上是如何在运行时执行机器代码的?,compiler-construction,compilation,llvm,jit,machine-code,Compiler Construction,Compilation,Llvm,Jit,Machine Code,在阅读了以下参考资料后,我理解了JIT编译的工作原理。但是,我仍然想知道它在运行时是如何执行机器代码的 我在操作系统或编译器优化方面没有深厚的背景,也没有直接对机器代码做过任何事情,但我开始探索它。我已经开始在汇编中玩了,看看像NASM这样的东西如何将汇编代码编译成可执行的机器代码,然后从命令行调用它,比如./my executable 但是JIT编译器在运行时是如何做到这一点的呢?它像是将流式机器代码导入stdin或其他什么东西,还是它是如何工作的?如果您可以提供一个示例或一些伪代码,说明某些

在阅读了以下参考资料后,我理解了JIT编译的工作原理。但是,我仍然想知道它在运行时是如何执行机器代码的

我在操作系统或编译器优化方面没有深厚的背景,也没有直接对机器代码做过任何事情,但我开始探索它。我已经开始在汇编中玩了,看看像NASM这样的东西如何将汇编代码编译成可执行的机器代码,然后从命令行调用它,比如./my executable


但是JIT编译器在运行时是如何做到这一点的呢?它像是将流式机器代码导入stdin或其他什么东西,还是它是如何工作的?如果您可以提供一个示例或一些伪代码,说明某些程序集或某些东西是如何按照这些思路(虽然不像C那么高级别)来演示基本流程的,那也将是令人惊讶的。

当执行代码时,这一切归结为将代码加载到内存的已知部分,通过直接寄存器设置或jmp指令或类似方式,将程序计数器设置为代码的开头。因此,JIT编译器将要做的是在内存的已知部分构建机器代码,然后从那里执行。

您提到过您使用汇编,因此您对汇编的工作原理有了一些了解,很好。假设您编写了在地址0x75612d39处分配缓冲区ex:的代码。然后,代码将程序集操作保存到该缓冲区以从堆栈中弹出一个数字,程序集调用打印函数以打印该数字,然后程序集返回。然后将数字3推到堆栈上,并调用/跳转到地址0x75612d39。处理器将按照指示打印您的号码,然后再次返回代码,然后继续。在汇编级别,它实际上非常简单

我不知道任何真正的汇编语言,但这里有一个由我知道的字节码拼凑而成的示例。此机器有2个字节指针,字符串%s位于地址6a,函数printf位于地址1388

此函数的程序集如下所示:

OP Params OpName     Description
13 82 6a  PushString 82 means string, 6a is the address of "%s"
                     So this function pushes a pointer to "%s" on the stack.
13 83 00  PushInt    83 means integer, 00 means the one on the top of the stack.
                     So this function gets the integer at the top of the stack,
                     And pushes it on the stack again
17 13 88 Call        1388 is printf, so this calls the printf function
03 02    Pop         This pops the two things we pushed back off the stack
02       Return      This returns to the calling code.

因此,当JITTER读取void myfuncint a{printf%s,a;}时,它会在地址0x75612d39处为此函数分配内存,并将这些字节存储在该内存中:13 82 6a 13 83 00 17 88 03 02。然后,要调用该函数,它只需跳转/调用地址0x75612d39处的函数

我将尝试在@MooingDuck答案上提供更多信息。 让我们举一个Hello World代码的例子。
namespace Hello
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello, world!");
        }
    }
}
等效的汇编代码类似于:

    mov     edx,len                             ;message length
    mov     ecx,msg                             ;message to write
    mov     ebx,1                               ;file descriptor (stdout)
    mov     eax,4                               ;system call number (sys_write)
    int     0x80                                ;call kernel

    mov     eax,1                               ;system call number (sys_exit)
    int     0x80                                ;call kernel


msg     db  'Hello, world!',0xa                 ;our dear string
len     equ $ - msg                             ;length of our dear string
此代码取自

这些指令中的每一条,以及数据本身,都可以用数字表示。现在,我可以把这些数字放在一个缓冲区中,告诉CPU到达缓冲区在内存中的位置并开始执行代码。对吧?

别那么快

正如您在中所看到的,在将内存映射为可执行文件之前,它不会工作。现在您可以将is转换为函数,并调用此内存。它将运行

总而言之,据我所知,抖动的工作原理大致如下:

阅读IL 对其进行编译,即确定哪些操作码将执行此任务 为它们分配内存并将它们映射为可执行代码 通过cast或其他方式将此内存作为函数调用
抖动只生成机器代码,不执行。这是处理器的工作。你能不能展示一个基本的例子,也许是一个汇编代码片段,只是更新了原来的问题?我开始了解您所描述的内容,但还不了解程序集的外观,以及处理器在其中的作用。@LancePollard:我已经很久没有处理过任何程序集了,即使这样,我们也不太可能知道相同的程序集。我输入了一个我熟悉的字节码样本,只是好奇它是什么字节码?它是为学习/教学目的开发的吗?字节码是我公司未命名的专有代码,用于本地化。我真的不能给出任何细节。
    mov     edx,len                             ;message length
    mov     ecx,msg                             ;message to write
    mov     ebx,1                               ;file descriptor (stdout)
    mov     eax,4                               ;system call number (sys_write)
    int     0x80                                ;call kernel

    mov     eax,1                               ;system call number (sys_exit)
    int     0x80                                ;call kernel


msg     db  'Hello, world!',0xa                 ;our dear string
len     equ $ - msg                             ;length of our dear string