単子葉類プログラマーのメモ

プログラミング関連の自分用メモだけど他の人の役に立つかもしれないので公開しておく感じのブログ

LLVM言語 学習メモ(6) - 構造体、配列

LLVMの構造体、配列について。

型については以前書いたが、その使い方についてはあまり書いていなかったので改めてまとめておく。

LLVMのバージョンは10.0.0

目次

構造体

例:構造体の定義

%Foo = type {i32, i32}

構造体の識別子は関数の外で、%から始まる名前で定義する。 なぜか@で始まる名前ではない。また、関数やグローバル変数と違って、使用箇所より上で定義しないとlliコマンドやllcコマンドの実行時にエラーになる。

この例ではi32型のメンバ変数を2つもつ構造体を定義している。 構造体の型についての説明はここを参照

例:構造体変数の定義

; Foo型のグローバル変数
@global_foo = global %Foo {i32 111, i32 222}

define i32 @main() {
    ; Foo型のローカル変数
    %ptr_foo_1 = alloca %Foo

普通の変数定義と同じ方法で定義できる。

グローバル変数の初期値として構造体リテラル{i32 111, i32 222}を指定している。 構造体リテラルの説明はここを参照

例:代入

    ; 0初期化リテラルを代入
    store %Foo zeroinitializer, %Foo* %ptr_foo_1

    ; 構造体リテラルを代入
    store %Foo {i32 1, i32 2}, %Foo* %ptr_foo_2

zeroinitializerを代入すると、すべてのメンバに0を代入できる。

{i32 1, i32 2}のような構造体リテラルを代入することもできる。

例:一部のメンバにのみ値を代入

    %ptr_foo_2_a = getelementptr %Foo, %Foo* %ptr_foo_2, i32 0, i32 1
    store i32 3, i32* %ptr_foo_2_a

一部のメンバを読み書きする場合、getelementptrでメンバ変数のポインタを取得してから、storeloadを行う。

getelementptr %Foo, %Foo* %ptr_foo_2, i32 0, i32 1の最後の値が、メンバ変数の順序に相当する。0が先頭、1が2個目になる。 getelementptrについての説明はここを参照

例:まとめ&関数の引数指定等

declare i32 @printf(i8*, ...)
@str = constant [10 x i8] c"{%d, %d}\0A\00"

; 構造体を定義
%Foo = type {i32, i32}

; 構造体のグローバル変数
@global_foo = global %Foo {i32 111, i32 222}

define i32 @main() {
    ; Foo型のローカル変数を確保
    %ptr_foo_1 = alloca %Foo
    %ptr_foo_2 = alloca %Foo

    ; 0初期化リテラルを代入
    store %Foo zeroinitializer, %Foo* %ptr_foo_1

    ; 構造体リテラルを代入
    store %Foo {i32 1, i32 2}, %Foo* %ptr_foo_2

    ; 一部のメンバにのみ値を代入
    %ptr_foo_2_a = getelementptr %Foo, %Foo* %ptr_foo_2, i32 0, i32 1
    store i32 3, i32* %ptr_foo_2_a

    ; 構造体の内容を表示する関数を実行
    call void @printFoo(%Foo* %ptr_foo_1)
    call void @printFoo(%Foo* %ptr_foo_2)
    call void @printFoo(%Foo* @global_foo)

    ret i32 0
}

define void @printFoo(%Foo* %arg1) {
    ; 構造体の1個目のメンバと2個目のメンバの値を取得
    %ptr.foo.0 = getelementptr %Foo, %Foo* %arg1, i32 0, i32 0
    %foo.0 = load i32, i32* %ptr.foo.0
    %ptr.foo.1 = getelementptr %Foo, %Foo* %arg1, i32 0, i32 1
    %foo.1 = load i32, i32* %ptr.foo.1

    ; 構造体の内容を表示
    %ptr_str = getelementptr [10 x i8], [10 x i8]* @str, i32 0, i32 0;
    call i32 (i8*, ...) @printf(i8* %ptr_str, i32 %foo.0, i32 %foo.1)

    ret void
}

実行結果

{0, 0}
{1, 3}
{111, 222}

配列

例:配列変数の定義

; グローバル変数
@global_array = global [3 x i32] [i32 1, i32 2, i32 3]

define i32 @main() {
    ; ローカル変数を確保
    %ptr_array_1 = alloca [3 x i32]

ここでは例としてi32型の長さ3の配列を定義している。

グローバル変数の場合は初期値として配列リテラル[i32 1, i32 2, i32 3]を指定している。

例:代入

    ; 0初期化リテラルを代入
    store [3 x i32] zeroinitializer, [3 x i32]* %ptr_array_1

    ; リテラルを代入
    store [3 x i32] [i32 11, i32 22, i32 33], [3 x i32]* %ptr_array_2

構造体の場合と同様。

例:一部の要素にのみ値を代入

    %ptr_array_2_1 = getelementptr [3 x i32], [3 x i32]* %ptr_array_2, i32 0, i32 1
    store i32 222, i32* %ptr_array_2_1

getelementptr [3 x i32], [3 x i32]* %ptr_array_2, i32 0, i32 1の最後の値が配列の添え字に相当する。

例:まとめ&関数の引数指定等

declare i32 @printf(i8*, ...)
@str = constant [14 x i8] c"[%d, %d, %d]\0A\00"

; グローバル変数
@global_array = global [3 x i32] [i32 1, i32 2, i32 3]

define i32 @main() {
    ; ローカル変数を確保
    %ptr_array_1 = alloca [3 x i32]
    %ptr_array_2 = alloca [3 x i32]

    ; 0初期化リテラルを代入
    store [3 x i32] zeroinitializer, [3 x i32]* %ptr_array_1

    ; リテラルを代入
    store [3 x i32] [i32 11, i32 22, i32 33], [3 x i32]* %ptr_array_2

    ; 一部の要素にのみ値を代入
    %ptr_array_2_1 = getelementptr [3 x i32], [3 x i32]* %ptr_array_2, i32 0, i32 1
    store i32 222, i32* %ptr_array_2_1

    ; 構造体の内容を表示する関数を実行
    call void @printArray([3 x i32]* %ptr_array_1)
    call void @printArray([3 x i32]* %ptr_array_2)
    call void @printArray([3 x i32]* @global_array)

    ret i32 0
}

define void @printArray([3 x i32]* %arg1) {
    ; 構造体の各メンバの値を取得
    %ptr.array.0 = getelementptr [3 x i32], [3 x i32]* %arg1, i32 0, i32 0
    %array.0 = load i32, i32* %ptr.array.0
    %ptr.array.1 = getelementptr [3 x i32], [3 x i32]* %arg1, i32 0, i32 1
    %array.1 = load i32, i32* %ptr.array.1
    %ptr.array.2 = getelementptr [3 x i32], [3 x i32]* %arg1, i32 0, i32 2
    %array.2 = load i32, i32* %ptr.array.2

    ; 構造体の内容を表示
    %ptr_str = getelementptr [14 x i8], [14 x i8]* @str, i32 0, i32 0;
    call i32 (i8*, ...) @printf(i8* %ptr_str, i32 %array.0, i32 %array.1, i32 %array.2)

    ret void
}

実行結果

[0, 0, 0]
[11, 222, 33]
[1, 2, 3]