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