【问题标题】:LLVM jit and nativeLLVM jit 和本机
【发布时间】:2011-03-31 08:50:04
【问题描述】:

我不明白 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 这样的语言来说这是否有意义?

【问题讨论】:

  • 简单!它不关注二进制文件的性能,而是关注代码质量和编译速度,因此每个二进制文件都包含一个小存根来执行从 C/C++ 代码生成的字节码,就像 java 一样。

标签: clang llvm jit


【解决方案1】:

我正在采取步骤从 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

来源自

Shared libraries with GCC on Linux

【讨论】:

    【解决方案2】:

    首先,你得到 LLVM 字节码(LLVM IR):

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

    其次,你使用 LLVM JIT:

    lli test.bc
    

    运行程序。

    然后,如果你想获得原生,你可以使用 LLVM 后端:

    llc test.bc
    

    来自汇编输出:

    as test.S
    

    【讨论】:

      【解决方案3】:

      大多数编译器都有前端、某种中间代码/结构以及后端。当您使用 C 程序并使用 clang 并进行编译以最终得到一个可以运行的非 JIT x86 程序时,您仍然从前端到中间再到后端。 gcc 也是如此,gcc 从前端到中间件和后端。 Gccs 中间的东西不像 LLVM 那样开放和可用。

      现在,关于 llvm 的一件有趣/有趣的事情是,你不能与其他人一起做,或者至少是 gcc,你可以获取所有源代码模块,将它们编译为 llvms 字节码,将它们合并成一个大字节码文件,然后优化整个事情,而不是使用其他编译器获得的每个文件或每个函数的优化,使用 llvm 你可以获得任何级别的部分编译程序优化你喜欢。然后您可以获取该字节码并使用 llc 将其导出到目标汇编程序。我通常是嵌入式的,所以我有我自己的启动代码,但理论上你应该能够获取该汇编文件并使用 gcc 编译并链接它并运行它。 gcc myfile.s -o 我的文件。我想有一种方法可以让 llvm 工具来执行此操作,而不必使用 binutils 或 gcc,但我没有花时间。

      我喜欢 llvm 因为它总是一个交叉编译器,不像 gcc 你不必为每个目标编译一个新的并处理每个目标的细微差别。我不知道我对 JIT 有什么用就是我所说的我将它用作交叉编译器和本机编译器。

      因此,您的第一个案例是前面、中间、结尾,并且该过程对您隐藏,您从源代码开始并获得二进制文件,完成。第二种情况是,如果我正确理解了前面和中间,并以一些代表中间的文件停止。然后中间到结束(特定的目标处理器)可以在运行时及时发生。区别在于后端,案例二的中间语言的实时执行,很可能与案例一的后端不同。

      【讨论】:

      • gcc 和 icc 都有一个 lto 和 ipo 可以“优化整个事情”,所以它不是 llvm 的独特功能。此外,llvm 不仅仅是一个“交叉编译器”,它还是一个编译器,它支持单个二进制文件中的许多目标。它比简单的交叉编译器好一点,它是通用编译器——适用于任何目标。
      • 我喜欢“通用编译器”这个词。感谢您的更新,我不知道您可以让 gcc 做到这一点,有一天可以玩。
      【解决方案4】:

      您必须了解 LLVM 是一个帮助您构建编译器的库。 Clang 只是这个库的前端。

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

      LLVM 还能够直接在内存中生成本机代码,然后可以将其作为普通函数调用。所以案例1.和2.分享LLVM的优化和代码生成。

      那么如何使用 LLVM 作为 JIT 编译器呢?您构建了一个生成一些 LLVM IR(在内存中)的应用程序,然后使用 LLVM 库生成本机代码(仍在内存中)。 LLVM 将一个指针交还给您,您可以在之后调用该指针。不涉及叮当声。

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

      现实世界的例子:

      还有Kaleidoscope 教程展示了如何使用 JIT 编译器实现一种简单的语言。

      【讨论】:

      • 所以要将 LLVM 用作 JIT,您必须将其链接到您的应用程序中,对吗?是否有应用程序可以做到这一点?
      猜你喜欢
      • 2015-05-05
      • 1970-01-01
      • 2012-11-01
      • 2011-05-03
      • 2019-12-27
      • 1970-01-01
      • 1970-01-01
      • 2019-09-20
      • 2011-02-17
      相关资源
      最近更新 更多