私は最近LLVMを見てきましたが、非常に興味深いアーキテクチャであることがわかりました。ただし、チュートリアルと参考資料を調べても、文字列データ型を実装する方法の例が見つかりません。
整数、実数、その他の数値型、さらには配列、関数、構造体に関するドキュメントはたくさんありますが、文字列については知る限り何もありません。バックエンドに新しいデータ型を追加する必要がありますか? 組み込みのデータ型を使用する方法はありますか? 任意の洞察をいただければ幸いです。
私は最近LLVMを見てきましたが、非常に興味深いアーキテクチャであることがわかりました。ただし、チュートリアルと参考資料を調べても、文字列データ型を実装する方法の例が見つかりません。
整数、実数、その他の数値型、さらには配列、関数、構造体に関するドキュメントはたくさんありますが、文字列については知る限り何もありません。バックエンドに新しいデータ型を追加する必要がありますか? 組み込みのデータ型を使用する方法はありますか? 任意の洞察をいただければ幸いです。
文字列とは 文字の配列。
キャラクターとは?整数。
したがって、私は決して LLVM の専門家ではありませんが、たとえば、8 ビット文字セットを表現したい場合は、i8 (8 ビット整数) の配列、またはへのポインターを使用すると思います。 i8. 実際、単純な hello world C プログラムがあるとします。
#include <stdio.h>
int main() {
puts("Hello, world!");
return 0;
}
そして、llvm-gcc を使用してコンパイルし、生成された LLVM アセンブリをダンプします。
$ llvm-gcc -S -emit-llvm hello.c
$ cat hello.s
; ModuleID = 'hello.c'
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128"
target triple = "x86_64-linux-gnu"
@.str = internal constant [14 x i8] c"Hello, world!\00" ; <[14 x i8]*> [#uses=1]
define i32 @main() {
entry:
%retval = alloca i32 ; <i32*> [#uses=2]
%tmp = alloca i32 ; <i32*> [#uses=2]
%"alloca point" = bitcast i32 0 to i32 ; <i32> [#uses=0]
%tmp1 = getelementptr [14 x i8]* @.str, i32 0, i64 0 ; <i8*> [#uses=1]
%tmp2 = call i32 @puts( i8* %tmp1 ) nounwind ; <i32> [#uses=0]
store i32 0, i32* %tmp, align 4
%tmp3 = load i32* %tmp, align 4 ; <i32> [#uses=1]
store i32 %tmp3, i32* %retval, align 4
br label %return
return: ; preds = %entry
%retval4 = load i32* %retval ; <i32> [#uses=1]
ret i32 %retval4
}
declare i32 @puts(i8*)
ファイルの最後で宣言されている puts 関数への参照に注意してください。Cでは、 puts は
int puts(const char *s)
LLVMでは、
i32 @puts(i8*)
対応は明確でなければなりません。
余談ですが、最適化せずにコンパイルしたため、生成された LLVM は非常に冗長です。これらをオンにすると、不要な指示が消えます。
$ llvm-gcc -O2 -S -emit-llvm hello.c
$ cat hello.s
; ModuleID = 'hello.c'
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128"
target triple = "x86_64-linux-gnu"
@.str = internal constant [14 x i8] c"Hello, world!\00" ; <[14 x i8]*> [#uses=1]
define i32 @main() nounwind {
entry:
%tmp2 = tail call i32 @puts( i8* getelementptr ([14 x i8]* @.str, i32 0, i64 0) ) nounwind ; <i32> [#uses=0]
ret i32 0
}
declare i32 @puts(i8*)
[文字列とは何かを説明する他の回答をフォローアップするために、ここにいくつかの実装ヘルプがあります]
C インターフェイスを使用すると、必要な呼び出しは次のようになります。
LLVMValueRef llvmGenLocalStringVar(const char* data, int len)
{
LLVMValueRef glob = LLVMAddGlobal(mod, LLVMArrayType(LLVMInt8Type(), len), "string");
// set as internal linkage and constant
LLVMSetLinkage(glob, LLVMInternalLinkage);
LLVMSetGlobalConstant(glob, TRUE);
// Initialize with string:
LLVMSetInitializer(glob, LLVMConstString(data, len, TRUE));
return glob;
}
一般的な言語で文字列がどのように表現されるかを考えてみてください。
string
コンストラクタ、デストラクタ、およびコピー コンストラクタを持つ複雑なオブジェクトです。内側には、通常、基本的に C 文字列が入っています。LLVM の名前は一目瞭然です。まさに「低レベル」です。好きなように文字列を実装する必要があります。LLVM が誰かに特定の実装を強制するのはばかげています。