RISC-Vには、浮動小数の比較命令命令として以下が定義されている。
funct7 | rs2 | rs1 | funct3 | rd | opcode | |
---|---|---|---|---|---|---|
FMIN.D | 0010101 | rs2 | rs1 | 000 | rd | 1010011 |
FMAX.D | 0010101 | rs2 | rs1 | 001 | rd | 1010011 |
FEQ.D | 1010001 | rs2 | rs1 | 010 | rd | 1010011 |
FLT.D | 1010001 | rs2 | rs1 | 001 | rd | 1010011 |
FLE.D | 1010001 | rs2 | rs1 | 000 | rd | 1010011 |
FMIN.S | 0010100 | rs2 | rs1 | 000 | rd | 1010011 |
FMAX.S | 0010100 | rs2 | rs1 | 001 | rd | 1010011 |
FEQ.S | 1010000 | rs2 | rs1 | 010 | rd | 1010011 |
FLT.S | 1010000 | rs2 | rs1 | 001 | rd | 1010011 |
FLE.S | 1010000 | rs2 | rs1 | 000 | rd | 1010011 |
これらの命令について、パタンを登録するだけだ。
llvm-myriscvx/lib/Target/MYRISCVX/MYRISCVXInstrInfoFD.td
// Arithmetic and logical instructions with 2 register operands. class FPCompDestR<bits<7> opcode, bits<3> funct3, bits<7>funct7, string instr_asm, SDPatternOperator OpNode, RegisterClass RC> : MYRISCVX_R<opcode, funct3, funct7, (outs GPR:$rd), (ins RC:$rs1, RC:$rs2), !strconcat(instr_asm, "\t$rd, $rs1, $rs2"), [(set GPR:$rd, (OpNode RC:$rs1, RC:$rs2))], IIAlu> { let isReMaterializable = 1; } def FMAX_S : ArithLogicR<0b1010011, 0b001, 0b0010100, "fmax.s", fmaxnum, FPR_S>; def FMIN_S : ArithLogicR<0b1010011, 0b000, 0b0010100, "fmin.s", fminnum, FPR_S>; def FEQ_S : FPCompDestR<0b1010011, 0b010, 0b1010000, "feq.s" , seteq, FPR_S>; def FLT_S : FPCompDestR<0b1010011, 0b001, 0b1010000, "flt.s" , setlt, FPR_S>; def FLE_S : FPCompDestR<0b1010011, 0b000, 0b1010000, "fle.s" , setle, FPR_S>; def FMAX_D : ArithLogicR<0b1010011, 0b001, 0b0010101, "fmax.s", fmaxnum, FPR_D>; def FMIN_D : ArithLogicR<0b1010011, 0b000, 0b0010101, "fmin.s", fminnum, FPR_D>; def FEQ_D : FPCompDestR<0b1010011, 0b010, 0b1010001, "feq.d" , seteq, FPR_D>; def FLT_D : FPCompDestR<0b1010011, 0b001, 0b1010001, "flt.d" , setlt, FPR_D>; def FLE_D : FPCompDestR<0b1010011, 0b000, 0b1010001, "fle.d" , setle, FPR_D>;
上記の10命令を登録した。また、比較命令のためのパタンとしてFPCompDestR
を定義した。
その名の通り、浮動小数点レジスタ通しの値を比較して、その結果を汎用レジスタに格納するパタンだ。
FPCompDestR
クラスでは、命令の定義と同時に命令生成のパタンも登録している。
この命令定義ではseteq, setlt, setle
を命令のパタンとして登録しているが、LLVMにはもう一つ比較のパタンが存在する。
setoeq, setolt, setole
だ。これらは浮動小数点命令特有の演算で、Ordered Opsと言われている。これらのパタンでも命令を生成できるように、パタンを追加する。
// For Single-Floating Point def : Pat<(setoeq FPR_S:$rs1, FPR_S:$rs2), (FEQ_S $rs1, $rs2)>; def : Pat<(setolt FPR_S:$rs1, FPR_S:$rs2), (FLT_S $rs1, $rs2)>; def : Pat<(setole FPR_S:$rs1, FPR_S:$rs2), (FLE_S $rs1, $rs2)>; // For greater / greater eq pattern. Reverse operand def : Pat<(setogt FPR_S:$rs1, FPR_S:$rs2), (FLE_S $rs2, $rs1)>; def : Pat<(setoge FPR_S:$rs1, FPR_S:$rs2), (FLT_S $rs2, $rs1)>; def : Pat<(setgt FPR_S:$rs1, FPR_S:$rs2), (FLE_S $rs2, $rs1)>; def : Pat<(setge FPR_S:$rs1, FPR_S:$rs2), (FLT_S $rs2, $rs1)>; // For Double-Floating Point def : Pat<(setoeq FPR_D:$rs1, FPR_D:$rs2), (FEQ_D $rs1, $rs2)>; def : Pat<(setolt FPR_D:$rs1, FPR_D:$rs2), (FLT_D $rs1, $rs2)>; def : Pat<(setole FPR_D:$rs1, FPR_D:$rs2), (FLE_D $rs1, $rs2)>; // For greater / greater eq pattern. Reverse operand def : Pat<(setogt FPR_D:$rs1, FPR_D:$rs2), (FLE_D $rs2, $rs1)>; def : Pat<(setoge FPR_D:$rs1, FPR_D:$rs2), (FLT_D $rs2, $rs1)>; def : Pat<(setgt FPR_D:$rs1, FPR_D:$rs2), (FLE_D $rs2, $rs1)>; def : Pat<(setge FPR_D:$rs1, FPR_D:$rs2), (FLT_D $rs2, $rs1)>;
さらに注意だ。setle, setlt, setole, setolt
だけでなく、その逆もある。setgt, setge, setogt, setoge
だ。
これらはle, lt
のオペランドを逆にすればよいので、これらのパタンも追加している。
また、命令のエイリアスとしてfge.s, fgt.s, fge.d, fgt.d
を追加した。これはアセンブラに対応させるための追加だ。
def : InstAlias<"fge.s $rd, $rs1, $rs2", (FLT_S GPR:$rd, FPR_S:$rs2, FPR_S:$rs1), 0>; def : InstAlias<"fgt.s $rd, $rs1, $rs2", (FLE_S GPR:$rd, FPR_S:$rs2, FPR_S:$rs1), 0>; def : InstAlias<"fge.d $rd, $rs1, $rs2", (FLT_D GPR:$rd, FPR_D:$rs2, FPR_D:$rs1), 0>; def : InstAlias<"fgt.d $rd, $rs1, $rs2", (FLE_D GPR:$rd, FPR_D:$rs2, FPR_D:$rs1), 0>;
ここまでで、LLVMをビルドしてテストを流してみる。以下のようなC言語のコードをテストする。
fp_cmp.cpp
int fp_lt_cmp(float a, float b) { return a < b; } int fp_le_cmp(float a, float b) { return a <= b; } int fp_gt_cmp(float a, float b) { return a > b; } int fp_ge_cmp(float a, float b) { return a >= b; } int dp_lt_cmp(double a, double b) { return a < b; } int dp_le_cmp(double a, double b) { return a <= b; } int dp_gt_cmp(double a, double b) { return a > b; } int dp_ge_cmp(double a, double b) { return a >= b; }
./bin/clang -O3 fp_cmp.cpp -emit-llvm ./bin/llc -filetype=asm fp_cmp.bc -mcpu=simple32 -march=myriscvx32 -o -
結果は以下のようになった。正しく命令が生成できていることが分かる。
_Z9fp_lt_cmpff: flt.s x10, f10, f11 ret _Z9fp_le_cmpff: fle.s x10, f10, f11 ret _Z9fp_gt_cmpff: fle.s x10, f11, f10 ret _Z9fp_ge_cmpff: flt.s x10, f11, f10 ret _Z9dp_lt_cmpdd: flt.d x10, f10, f11 ret _Z9dp_le_cmpdd: fle.d x10, f10, f11 ret _Z9dp_gt_cmpdd: fle.d x10, f11, f10 ret _Z9dp_ge_cmpdd: flt.d x10, f11, f10 ret