LLVM jit 和本机代码

22

我不理解LLVM JIT如何与常规的非JIT编译相关联,而且文档也不太好。

例如,假设我使用clang前端:

  1. 情况1:使用clang/llvm将C文件编译为本机代码。我理解这个流程类似于gcc流程-我得到我的x86可执行文件并运行它。
  2. 情况2:编译为某种在LLVM JIT上运行的LLVM IR。在这种情况下,可执行文件包含了LLVM运行时以在JIT上执行IR,或者它是如何工作的?

这两种方式有什么区别,它们是否正确? LLVM流程是否同时支持JIT和非JIT?我何时需要使用JIT-对于像C这样的语言,是否有意义使用JIT?


简单!它不是专注于二进制性能,而是专注于代码质量和编译速度,以便每个二进制文件都包含一个小存根来执行从C/C++代码生成的字节码,就像Java一样。 - user2284570
4个回答

36

你必须明白LLVM是一个帮助你构建编译器的库。Clang只是这个库的前端。

Clang将C/C++代码转换为LLVM IR并将其交给LLVM,后者将其编译为本地代码。

LLVM还能够直接在内存中生成本地代码,然后可以像普通函数一样调用。因此情况1和2共享LLVM的优化和代码生成。

那么如何将LLVM用作JIT编译器?您需要构建一个应用程序,它会生成一些LLVM IR(在内存中),然后使用LLVM库生成本地代码(仍在内存中)。LLVM会返回一个指针,您可以随后调用它。没有涉及clang。

但是,您可以使用clang将一些C代码转换为LLVM IR,并将其加载到JIT上下文中以使用这些函数。

现实世界的例子:

还有Kaleidoscope教程,展示了如何实现一个带有JIT编译器的简单语言。


那么,要将LLVM用作JIT,您确实必须将其链接到应用程序中,对吗?有这样做的应用程序吗? - zaharpopov

27

首先,您需要获取LLVM字节码(LLVM IR):

clang -emit-llvm -S -o test.bc test.c 

其次,您可以使用LLVM JIT:

lli test.bc

运行该程序。

然后,如果您想获得本地代码,则使用LLVM后端:

llc test.bc

从汇编输出来看:

as test.S

7
我正在采取步骤来编译和运行LLVM社区中邮件消息中的JIT代码。 [LLVMdev] MCJIT and Kaleidoscope Tutorial 头文件:
// foo.h
extern void foo(void);

和一个简单的foo()函数相关的功能:

//foo.c
#include <stdio.h>
void foo(void) {
    puts("Hello, I'm a shared library");
}

而主要函数是:

//main.c
#include <stdio.h>
#include "foo.h"
int main(void) {
    puts("This is a shared library test...");
    foo();
    return 0;
}

使用 foo.c 构建共享库:

gcc foo.c -shared -o libfoo.so -fPIC

生成 main.c 文件的 LLVM 位码:

clang -Wall -c -emit-llvm -O3 main.c -o main.bc

通过jit(和MCJIT)运行LLVM位码以获得所需的输出:

lli -load=./libfoo.so main.bc
lli -use-mcjit -load=./libfoo.so main.bc

你也可以将clang的输出导入到lli中:

clang -Wall -c -emit-llvm -O3 main.c -o - | lli -load=./libfoo.so 

输出

This is a shared library test...
Hello, I'm a shared library

来源获取自

在Linux上使用GCC共享库


2
大多数编译器都有前端、某些中间代码/结构和后端。当您使用clang编译C程序并进行编译,以便最终得到一个非JIT x86程序可以直接运行时,您仍然从前端到中间到后端。gcc也是如此,gcc从前端到一个中间层再到后端。gcc的中间层不像LLVM那样开放且可用。
现在,关于llvm有趣/有趣的一件事情是,您无法像其他编译器(或至少gcc)那样执行所有源代码模块,将它们编译为llvms字节码,将它们合并到一个大字节码文件中,然后对整个文件进行优化,而不是使用其他编译器每个文件或每个函数进行优化。LLVM可以获得任何级别的部分到完整程序优化。然后,您可以将该字节码导出到目标汇编语言,使用llc。我通常进行嵌入式开发,因此我会将自己的启动代码包装周围,但是理论上应该能够使用gcc编译和链接该汇编文件并运行它。gcc myfile.s -o myfile.我想有一种方法可以让llvm工具做到这一点,而不必使用binutils或gcc,但我还没有花时间去了解。
我喜欢llvm,因为它始终是交叉编译器,与gcc不同,您不必为每个目标编译一个新的编译器并处理每个目标的细微差别。我不知道我是否需要JIT事物,我的意思是我将其用作交叉编译器和本地编译器。
因此,您的第一个情况是前端、中间和结束,过程对您隐藏,您从源代码开始,得到二进制文件,完成。第二种情况是如果我理解正确,那么前面和中间会停止,并生成表示中间的某些文件。然后,中间到最终目标(特定的目标处理器)可能会在运行时即时发生。区别在于后端,第二种情况下的中间语言的实时执行可能与第一种情况下的后端有所不同。

4
GCC和ICC都有LTO和IPO,可以“优化整个东西”,因此这不是LLVM的独特功能。另外,LLVM不仅仅是一个“交叉编译器”,它是一个编译器,支持在单个二进制文件中为许多目标进行编译。它比简单的交叉编译器要好一些,它是通用编译器 - 可以用于任何目标。 - osgx
我喜欢那个术语“通用编译器”。谢谢更新,我不知道你可以让gcc做到这一点,有机会可以尝试一下。 - old_timer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接