21

LLVM JITが通常のJITコンパイルなしとどのように関連しているかがわかりません。また、ドキュメントも適切ではありません。

たとえば、clangフロントエンドを使用するとします。

  1. ケース1:clang/llvmを使用してCファイルをネイティブにコンパイルします。私が理解しているこのフローは、gccフローのようなものです。x86実行可能ファイルを取得して実行します。
  2. ケース2:LLVMJITで実行されるある種のLLVMIRにコンパイルします。この場合、実行可能ファイルには、JITでIRを実行するためのLLVMランタイムが含まれていますか、それともどのように機能しますか?

これら2つの違いは何ですか?それらは正しいですか?LLVMフローにはJITと非JITの両方のサポートが含まれていますか?いつJITを使いたいのですか?Cのような言語ではまったく意味がありますか?

4

4 に答える 4

34

LLVMは、コンパイラーの構築に役立つライブラリーであることを理解する必要があります。Clangは、このライブラリの単なるフロントエンドです。

ClangはC/C++コードをLLVMIRに変換し、LLVMに渡します。LLVMはそれをネイティブコードにコンパイルします。

LLVMは、メモリ内に直接ネイティブコードを生成することもできます。これは、通常の関数として呼び出すことができます。したがって、ケース1と2は、LLVMの最適化とコード生成を共有します。

では、LLVMをJITコンパイラとしてどのように使用するのでしょうか。LLVM IR(メモリ内)を生成するアプリケーションを構築し、LLVMライブラリを使用してネイティブコード(メモリ内)を生成します。LLVMは、後で呼び出すことができるポインターを返します。クランは関係ありません。

ただし、clangを使用して一部のCコードをLLVM IRに変換し、これをJITコンテキストにロードして関数を使用することはできます。

実際の例:

JITコンパイラを使用して単純な言語を実装する方法を示すKaleidoscopeチュートリアルもあります。

于 2010-08-18T05:41:36.617 に答える
27

まず、LLVMバイトコード(LLVM IR)を取得します。

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

次に、LLVMJITを使用します。

lli test.bc

これでプログラムが実行されます。

次に、ネイティブになりたい場合は、LLVMバックエンドを使用します。

llc test.bc

アセンブリ出力から:

as test.S
于 2010-09-19T00:50:12.103 に答える
7

LLVMコミュニティのメールメッセージからJITされたコードをコンパイルして実行するための手順を実行しています。

[LLVMdev]MCJITと万華鏡のチュートリアル

ヘッダーファイル:

// 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との共有ライブラリ

于 2013-08-02T16:36:52.280 に答える
2

ほとんどのコンパイラには、フロントエンド、ある種のミドルコード/構造、およびバックエンドがあります。Cプログラムを使用してclangを使用し、実行できる非JIT x86プログラムになるようにコンパイルすると、フロントエンドからミドルエンド、バックエンドへと移行します。同じことがgccにも当てはまり、gccはフロントエンドからミドルシングとバックエンドに行きます。Gccsの真ん中のものは、LLVMのように広く開かれていて、使用可能ではありません。

llvmについて面白くて興味深いことの1つは、他の人や少なくともgccではできないことです。すべてのソースコードモジュールを取得し、それらをllvmsバイトコードにコンパイルし、1つの大きなバイトコードファイルにマージしてから、他のコンパイラで得られるファイルごとまたは関数ごとの最適化ではなく、全体を最適化します。llvmを使用すると、任意のレベルの部分的なものを取得して、プログラムの最適化をコンパイルできます。次に、そのバイトコードを取得し、llcを使用してターゲットアセンブラにエクスポートできます。私は通常埋め込みを行うので、それをラップする独自のスタートアップコードがありますが、理論的には、そのアセンブラーファイルを取得し、gccでコンパイルしてリンクし、実行できるはずです。gcc myfile.s-omyfile。llvmツールでこれを実行し、binutilsやgccを使用する必要がないようにする方法があると思いますが、時間をかけていません。

llvmは常にクロスコンパイラであるため、私はllvmが好きです。これは、gccとは異なり、ターゲットごとに新しいものをコンパイルして、ターゲットごとにニュアンスを処理する必要がないためです。私がJITを使用していることを私は知りません。それは、私がクロスコンパイラーおよびネイティブコンパイラーとして使用していると言っていることです。

したがって、最初のケースはフロント、ミドル、エンドであり、プロセスはソースから開始してバイナリを取得することで、プロセスが非表示になります。2番目のケースは、正面と中央を正しく理解し、中央を表すファイルで停止する場合です。次に、中間から終了(特定のターゲットプロセッサ)が実行時にちょうど間に合うように発生する可能性があります。ケース2の中間言語のリアルタイム実行であるバックエンドの違いは、ケース1のバックエンドとは異なる可能性があります。

于 2010-08-20T00:44:00.337 に答える