LLVMにはすでにRISC-Vのバックエンドサポートが追加されている。しかし、勉強のために独自のRISC-V実装をLLVMに追加している。
第7章は、int型以外の各種変数型に対応させる。
diff --git a/lib/Target/MYRISCVX/MYRISCVXInstrInfo.td b/lib/Target/MYRISCVX/MYRISCVXInstrInfo.td index 41f009b0d11..b7da82ae9f7 100644 --- a/lib/Target/MYRISCVX/MYRISCVXInstrInfo.td +++ b/lib/Target/MYRISCVX/MYRISCVXInstrInfo.td @@ -127,6 +133,12 @@ def store_a : AlignedStore<store>; // Instructions specific format //===----------------------------------------------------------------------===// +class EffectiveAddress<string instr_asm, RegisterClass RC, Operand Mem> : + FI<0b0000011, 0b010, (outs RC:$ra), (ins Mem:$addr), + instr_asm, [(set RC:$ra, addr:$addr)], IIAlu> { +} + +
EffectiveAddressクラスを定義している。これは良く分からないがaddi命令に対応させるようにしておけばよいのかな?
以下のプログラムでテストする。これはプログラムとしては意味のないもの、というか動かないだろうけど、とりあえずポインタの動きを見るためには良さそう。
/// start int test_local_pointer() { int b = 3; int* p = &b; return *p; }
./bin/clang -c -target mips-unknown-linux-gnu ../lbdex/input/ch7_1_localpointer.cpp -emit-llvm ./bin/llc -march=myriscvx32 -filetype=asm ch7_1_localpointer.bc -o -
addi x10, x0, 3 sw x10, 4(x2) addi x10, x2, 4 sw x10, 0(x2) lw x10, 0(x2) lw x10, 0(x10)
3という実体を作ってから、メモリに格納してから、アドレスを生成してまた実体をメモリからロードする。 一応動作としては合っているようだ。
次に、以下のコードをコンパイルしてアセンブリを生成できるようにする。charとshortが混在している環境だ。
/// start struct Date { short year; char month; char day; char hour; char minute; char second; }; unsigned int b[4] = {'a', 'b', 'c', '\0'}; int test_char() { unsigned char a = b[1]; char c = (char)b[1]; Date date1 = {2012, (char)11, (char)25, (char)9, (char)40, (char)15}; char m = date1.month; char s = date1.second; return 0; }
Tutorialの通りに、単純に以下のコードを追加すれば動作するのかと思った。
lib/Target/MYRISCVX/MYRISCVXInstrInfo.td
defm LB : LoadM32<0b0000011, 0b000, "lb" , sextloadi8_a >; defm LH : LoadM32<0b0000011, 0b001, "lh" , sextloadi16_a>; defm LW : LoadM32<0b0000011, 0b010, "lw" , load_a >; defm LBU : LoadM32<0b0000011, 0b100, "lbu", zextloadi8_a >; defm LHU : LoadM32<0b0000011, 0b101, "lhu", zextloadi16_a>; defm SB : StoreM32<0b0100011, 0b000, "sb", truncstorei8_a >; defm SH : StoreM32<0b0100011, 0b001, "sh", truncstorei16_a>; defm SW : StoreM32<0b0100011, 0b010, "sw", store_a >;
LLVMをビルドしてアセンブリをはいてみると、どうもうまくいかずにエラーとなってしまった。
LLVM ERROR: Cannot select: t45: i32,ch = load<(dereferenceable load 1 from %ir.second, align 2), anyext from i8> t44, t36, undef:i32 t36: i32 = or FrameIndex:i32<2>, Constant:i32<6> t12: i32 = FrameIndex<2> t28: i32 = Constant<6> t5: i32 = undef
これらのエラーは、MIPSと比較したり、charとshortを全部intに変えてみたりして確認してみるとわかりやすい気がする。 原因を絞り込んだ結果、charとshortのメモリアクセス命令を定義しただけではだめ、ちゃんとIRから推論されるように追加する必要があった。
lib/Target/MYRISCVX/MYRISCVXInstrInfo.td
@@ -413,3 +414,9 @@ class WrapperPat<SDNode node, Instruction ORiOp, RegisterClass RC>: (ORiOp RC:$gp, node:$in)>; def : WrapperPat<tglobaladdr, ORI, GPR>; + +def : Pat<(i32 (extloadi1 addr:$src)), (LBU addr:$src)>; +def : Pat<(i32 (extloadi8 addr:$src)), (LBU addr:$src)>; +def : Pat<(i32 (extloadi16 addr:$src)), (LHU addr:$src)>; +// peepholes +def : Pat<(store (i32 0), addr:$dst), (SW ZERO, addr:$dst)>;
これにより、上記のC言語のコードは正しく出力されるようになった。
./bin/clang -c -target mips-unknown-linux-gnu ../lbdex/input/ch7_1_char_in_struct.cpp -emit-llvm ./bin/llc -march=mips -relocation-model=pic -filetype=asm ch7_1_char_in_struct.bc -o -
# %bb.0: # %entry addi x2, x2, -24 lui x10, %got_hi(b) add x10, x10, x3 lw x10, %got_lo(b)(x10) addi x10, x10, 4 lw x11, 0(x10) sb x11, 20(x2) lw x10, 0(x10) sb x10, 16(x2) lw x10, %got($_ZZ9test_charvE5date1)(x3) ori x10, x10, %lo($_ZZ9test_charvE5date1) addi x11, x10, 4 lhu x11, 0(x11) addi x12, x10, 6 lhu x12, 0(x12) slli x12, x12, 16 or x11, x12, x11 addi x12, x2, 8 ori x13, x12, 4 sw x11, 0(x13) lhu x11, 0(x10) addi x10, x10, 2 lhu x10, 0(x10) slli x10, x10, 16 or x10, x10, x11 sw x10, 8(x2) ori x10, x12, 2 lbu x10, 0(x10) sb x10, 4(x2) ori x10, x12, 6 lbu x10, 0(x10) sb x10, 0(x2) addi x10, x0, 0 addi x2, x2, 24 ret