FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

オリジナルLLVMバックエンド実装をまとめる(13. 配列・構造体のサポートの確認)

配列のサポートの確認

次に、整数型以外に配列のサポートの確認をする。実は、ここまでの実装で配列はサポートできるようになっている。以下のプログラムをコンパイルしてみる。

  • int_arary.cpp
int int_array()
{
  int array[4] = {100, 200, 300, 400};

  int a = array[0];
  int b = array[1];
  array[2] = 301;

  return 0;
}

配列としてarray b[4]を定義し、int_array()内ではbの要素にアクセスしている。

これをコンパイルしてみる。

./bin/clang int_array.cpp -c -emit-llvm --target=riscv32-unknown-elf -o int_arary.rv32.bc

いったんLLVM IRを確認しておく。Binary Codeをダンプしてみる。

@__const._Z9int_arrayv.array = private unnamed_addr constant [4 x i32] [i32 100, i32 200, i32 300, i32 400], align 4

; Function Attrs: noinline nounwind optnone
define dso_local i32 @_Z9int_arrayv() #0 {
entry:
  %array = alloca [4 x i32], align 4
  %a = alloca i32, align 4
  %b = alloca i32, align 4
  %0 = bitcast [4 x i32]* %array to i8*
  call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 4 %0, i8* align 4 bitcast ([4 x i32]* @__const._Z9int_arrayv.array to i8*), i32 16, i1 false)
  %arrayidx = getelementptr inbounds [4 x i32], [4 x i32]* %array, i32 0, i32 0
  %1 = load i32, i32* %arrayidx, align 4
  store i32 %1, i32* %a, align 4
  %arrayidx1 = getelementptr inbounds [4 x i32], [4 x i32]* %array, i32 0, i32 1
  %2 = load i32, i32* %arrayidx1, align 4
  store i32 %2, i32* %b, align 4
  %arrayidx2 = getelementptr inbounds [4 x i32], [4 x i32]* %array, i32 0, i32 2
  store i32 301, i32* %arrayidx2, align 4
  ret i32 0
}

長くてややこしいが、基本的に構造体と配列はオフセット付きのメモリアクセスであることは変わりないので、これまでの命令を組み合わせるだけで実現できそうだということが分かる。

命令を生成してみる。

./bin/llc -march=myriscvx64 -filetype=asm int_array.rv64.bc -o -
_Z9int_arrayv:                          # @_Z9int_arrayv

# %bb.0:                                # %entry
        addi    $x2, $x2, -24
        lui     $x10, %hi($__const._Z9int_arrayv.array)
        ori     $x10, $x10, %lo($__const._Z9int_arrayv.array)
        lwu     $x10, 0($x10)
        addi    $x11, $zero, 32
        sll     $x11, $x10, $x11
        or      $x10, $x11, $x10
        sd      $x10, 16($x2)
        sd      $x10, 8($x2)
        lw      $x10, 8($x2)
        sw      $x10, 4($x2)
        addi    $x10, $zero, 301
        sw      $x10, 16($x2)
        addi    $x2, $x2, 24
        ret
$func_end0:

構造体のサポートの確認

並列と同様、構造体も確認する。構造体も基本的にメモリへのオフセット付きのアクセスなので、これまでの命令を組み合わせるだけで実現できそうだ。

  • int_struct.cpp
struct Date
{
  short year;
  char month;
  char day;
  char hour;
  char minute;
  char second;
};

int int_struct()
{
  Date date1 = {2012, (char)11, (char)25, (char)9, (char)40, (char)15};
  char m = date1.month;
  char s = date1.second;

  date1.hour = (char)100;

  return 0;
}

構造体では、Date構造体を定義し、その実体date1を作っている。初期値を設定し、その構造体のメンバdate1.month, date1.secondにアクセスしている。また、構造体の要素date1.hourに対する書き込みを行った。

./bin/clang int_struct.cpp -c -emit-llvm --target=riscv32-unknown-elf -o int_struct.rv32.bc
./bin/llc -march=myriscvx32 -filetype=asm int_struct.rv32.bc -o -
_Z10int_structv:                        # @_Z10int_structv

# %bb.0:                                # %entry
        addi    $x2, $x2, -16
        lui     $x10, %hi($__const._Z10int_structv.date1)
        ori     $x10, $x10, %lo($__const._Z10int_structv.date1)
        lhu     $x10, 0($x10)
        slli    $x11, $x10, 16
        or      $x10, $x11, $x10
        addi    $x11, $x2, 8
        ori     $x12, $x11, 4
        sw      $x10, 0($x12)
        sw      $x10, 8($x2)
        ori     $x10, $x11, 2
        lb      $x10, 0($x10)
        sb      $x10, 4($x2)
        ori     $x10, $x11, 6
        lb      $x10, 0($x10)
        sb      $x10, 0($x2)
        addi    $x10, $zero, 100
        sb      $x10, 0($x12)
        addi    $x2, $x2, 16
        ret

このように、所望のメモリアクセス命令を生成することができた。構造体のサポートも上手く行っている。