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)
