LLVM IRでポインタのサポートをする。ポインタをサポートするためには、該当するシンボルに対するアドレスを計算するというIRを追加する必要がある。これを、EffectiveAddress
というクラスで追加する。
llvm-myriscvx/lib/Target/MYRISCVX/MYRISCVXInstrInfo.td
def mem_ea : Operand<iPTR> { let PrintMethod = "printMemOperandEA"; let MIOperandInfo = (ops GPR, simm12); let EncoderMethod = "getMemEncoding"; }
class EffectiveAddress<string instr_asm, RegisterClass RC, Operand Mem> : MYRISCVX_I<0b0010011, 0b000, (outs RC:$ra), (ins Mem:$addr), instr_asm, [(set RC:$ra, addr:$addr)], IIAlu>;
このEffectiveAddress
は、実際にはアドレス計算するためのaddi
命令を生成している。addi
命令により、スタックポインタからのオフセットで、アドレスを算出している。
def LEA_ADDI : EffectiveAddress<"addi\t$ra, $addr", GPR, mem_ea> { let isCodeGenOnly = 1; }
例えば、以下のようなコードをコンパイルしてみる。
int test_local_pointer() { int b = 3; int* p = &b; return *p; }
./bin/clang -target mips-unknown-linux-gnu -c ../lbdex/input/ch7_1_localpointer.cpp -emit-llvm ./bin/llc -march=myriscvx32 -mcpu=simple32 -relocation-model=pic -filetype=asm ch7_1_localpointer.bc -o -
# %bb.0: # %entry addi x2, x2, -8 addi x10, zero, 3 sw x10, 4(x2) addi x10, x2, 4 sw x10, 0(x2) lw x10, 0(x2) lw x10, 0(x10) addi x2, x2, 8 ret x1
読み解いていくと、まずはx10
に3を格納する。それをスタックポインタ(x2
)の2番目のスロットに格納する(4(x2)
)。
addi x10, zero, 3 sw x10, 4(x2)
次のaddi
でx10
にb
のポインタ&b
を格納する。これには、さきほどのEffectiveAddress
で作成したaddi
命令を使用し、スタックの位置からのオフセットを計算して算出する。そして、そのポインタの値(=x10
)をスタックポインタ(x2
)の1番目のスロットに格納する。
addi x10, x2, 4 sw x10, 0(x2)
最後に、ポインタの値を返すために、ポインタ経由でロード命令を2回発行して、x10
レジスタに格納する。
lw x10, 0(x2) lw x10, 0(x10)