From fc25939520b4bfe3bc397ca1c27bd9f878824458 Mon Sep 17 00:00:00 2001 From: Denis Paranichev <48580269+DenisParal@users.noreply.github.com> Date: Thu, 7 Dec 2023 14:15:59 +0300 Subject: [PATCH] [RISC-V] Flush-to-zero behavior for float-to-int conversion (#94762) * Implemented several RISC-V csr instructions and enabled flush-to-zero behavior for float-to-int conversion instruction * Apply comments * Replace branch solution with feq * Fixed typo * Fixed typo * Fixed csr instructions emitter * Apply comments * Apply jit-format * Apply comments * Temp register fix * Fixed dstSize * Wrong temporary register selection fix * Fixed typo --- src/coreclr/jit/codegenriscv64.cpp | 18 +++++++ src/coreclr/jit/emitriscv64.cpp | 87 ++++++++++++++++++++++++++++++ src/coreclr/jit/emitriscv64.h | 9 ++++ src/coreclr/jit/lsrariscv64.cpp | 6 +++ src/coreclr/jit/registerriscv64.h | 1 + 5 files changed, 121 insertions(+) diff --git a/src/coreclr/jit/codegenriscv64.cpp b/src/coreclr/jit/codegenriscv64.cpp index ed44bd7b023..18e065a21a4 100644 --- a/src/coreclr/jit/codegenriscv64.cpp +++ b/src/coreclr/jit/codegenriscv64.cpp @@ -3381,8 +3381,26 @@ void CodeGen::genFloatToIntCast(GenTree* treeNode) genConsumeOperands(treeNode->AsOp()); + regNumber tmpReg = treeNode->GetSingleTempReg(); + assert(tmpReg != treeNode->GetRegNum()); + assert(tmpReg != op1->GetRegNum()); + GetEmitter()->emitIns_R_R(ins, dstSize, treeNode->GetRegNum(), op1->GetRegNum()); + // This part emulates the "flush to zero" option because the RISC-V specification does not provide it. + instruction feq_ins = INS_feq_s; + if (srcType == TYP_DOUBLE) + { + feq_ins = INS_feq_d; + } + // Compare op1 with itself to get 0 if op1 is NaN and 1 for any other value + GetEmitter()->emitIns_R_R_R(feq_ins, dstSize, tmpReg, op1->GetRegNum(), op1->GetRegNum()); + // Get subtraction result of REG_ZERO (always 0) and feq result + // As a result we get 0 for NaN and -1 (all bits set) for any other value + GetEmitter()->emitIns_R_R_R(INS_sub, dstSize, tmpReg, REG_ZERO, tmpReg); + // and instruction with received mask produces 0 for NaN and preserves any other value + GetEmitter()->emitIns_R_R_R(INS_and, dstSize, treeNode->GetRegNum(), treeNode->GetRegNum(), tmpReg); + genProduceReg(treeNode); } diff --git a/src/coreclr/jit/emitriscv64.cpp b/src/coreclr/jit/emitriscv64.cpp index 4d2c4ea9cc9..77523664c2f 100644 --- a/src/coreclr/jit/emitriscv64.cpp +++ b/src/coreclr/jit/emitriscv64.cpp @@ -689,6 +689,15 @@ void emitter::emitIns_R_R_I( code |= ((imm >> 5) & 0x3f) << 25; code |= ((imm >> 12) & 0x1) << 31; } + else if (ins == INS_csrrs || ins == INS_csrrw || ins == INS_csrrc) + { + assert(isGeneralRegisterOrR0(reg1)); + assert(isGeneralRegisterOrR0(reg2)); + assert(isValidUimm12(imm)); + code |= reg1 << 7; + code |= reg2 << 15; + code |= imm << 20; + } else { NYI_RISCV64("illegal ins within emitIns_R_R_I!"); @@ -704,6 +713,39 @@ void emitter::emitIns_R_R_I( appendToCurIG(id); } +/***************************************************************************** + * + * Add an instruction referencing register and two constants. + */ + +void emitter::emitIns_R_I_I( + instruction ins, emitAttr attr, regNumber reg1, ssize_t imm1, ssize_t imm2, insOpts opt) /* = INS_OPTS_NONE */ +{ + code_t code = emitInsCode(ins); + + if (INS_csrrwi <= ins && ins <= INS_csrrci) + { + assert(isGeneralRegisterOrR0(reg1)); + assert(isValidUimm5(imm1)); + assert(isValidUimm12(imm2)); + code |= reg1 << 7; + code |= imm1 << 15; + code |= imm2 << 20; + } + else + { + NYI_RISCV64("illegal ins within emitIns_R_I_I!"); + } + instrDesc* id = emitNewInstr(attr); + + id->idIns(ins); + id->idReg1(reg1); + id->idAddr()->iiaSetInstrEncode(code); + id->idCodeSize(4); + + appendToCurIG(id); +} + /***************************************************************************** * * Add an instruction referencing three registers. @@ -3351,6 +3393,51 @@ void emitter::emitDisInsName(code_t code, const BYTE* addr, instrDesc* id) } case 0x73: { + unsigned int opcode2 = (code >> 12) & 0x7; + if (opcode2 != 0) + { + const char* rd = RegNames[(code >> 7) & 0x1f]; + int csrtype = (code >> 20); + if (opcode2 <= 0x3) + { + const char* rs1 = RegNames[(code >> 15) & 0x1f]; + switch (opcode2) + { + case 0x1: // CSRRW + printf("csrrw %s, %d, %s\n", rd, csrtype, rs1); + return; + case 0x2: // CSRRS + printf("csrrs %s, %d, %s\n", rd, csrtype, rs1); + return; + case 0x3: // CSRRC + printf("csrrc %s, %d, %s\n", rd, csrtype, rs1); + return; + default: + printf("RISCV64 illegal instruction: 0x%08X\n", code); + break; + } + } + else + { + unsigned imm5 = ((code >> 15) & 0x1f); + switch (opcode2) + { + case 0x5: // CSRRWI + printf("csrrwi %s, %d, %d\n", rd, csrtype, imm5); + return; + case 0x6: // CSRRSI + printf("csrrsi %s, %d, %d\n", rd, csrtype, imm5); + return; + case 0x7: // CSRRCI + printf("csrrci %s, %d, %d\n", rd, csrtype, imm5); + return; + default: + printf("RISCV64 illegal instruction: 0x%08X\n", code); + break; + } + } + } + if (code == emitInsCode(INS_ebreak)) { printf("ebreak\n"); diff --git a/src/coreclr/jit/emitriscv64.h b/src/coreclr/jit/emitriscv64.h index ea9149e2246..0f43e567575 100644 --- a/src/coreclr/jit/emitriscv64.h +++ b/src/coreclr/jit/emitriscv64.h @@ -106,6 +106,12 @@ static bool isValidUimm11(ssize_t value) return (0 == (value >> 11)); } +// Returns true if 'value' is a legal unsigned immediate 5 bit encoding. +static bool isValidUimm5(ssize_t value) +{ + return (0 == (value >> 5)); +} + // Returns true if 'value' is a legal signed immediate 20 bit encoding. static bool isValidSimm20(ssize_t value) { @@ -189,6 +195,9 @@ void emitIns_R_R(instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, void emitIns_R_R_I( instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, ssize_t imm, insOpts opt = INS_OPTS_NONE); +void emitIns_R_I_I( + instruction ins, emitAttr attr, regNumber reg1, ssize_t imm1, ssize_t imm2, insOpts opt = INS_OPTS_NONE); + void emitIns_R_R_R( instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, regNumber reg3, insOpts opt = INS_OPTS_NONE); diff --git a/src/coreclr/jit/lsrariscv64.cpp b/src/coreclr/jit/lsrariscv64.cpp index b9fad040cee..260d26cbfbf 100644 --- a/src/coreclr/jit/lsrariscv64.cpp +++ b/src/coreclr/jit/lsrariscv64.cpp @@ -1409,6 +1409,12 @@ int LinearScan::BuildCast(GenTreeCast* cast) int srcCount = BuildOperandUses(cast->CastOp()); BuildDef(cast); + if (varTypeIsFloating(cast->gtOp1) && !varTypeIsFloating(cast->TypeGet())) + { + buildInternalIntRegisterDefForNode(cast); + buildInternalRegisterUses(); + } + return srcCount; } diff --git a/src/coreclr/jit/registerriscv64.h b/src/coreclr/jit/registerriscv64.h index fea2e3cf5e1..fe6d3cf8ece 100644 --- a/src/coreclr/jit/registerriscv64.h +++ b/src/coreclr/jit/registerriscv64.h @@ -50,6 +50,7 @@ REGDEF(T5, 30, 0x40000000, "t5" ) REGDEF(T6, 31, 0x80000000, "t6" ) REGALIAS(R8, FP) +REGALIAS(ZERO, R0) #define FBASE 32 #define FMASK(x) (1ULL << (FBASE+(x))) -- 2.34.1