Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RISC-V] Flush-to-zero behavior for float-to-int conversion #94762

Merged
merged 14 commits into from
Dec 7, 2023
18 changes: 18 additions & 0 deletions src/coreclr/jit/codegenriscv64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3380,8 +3380,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);
}

Expand Down
87 changes: 87 additions & 0 deletions src/coreclr/jit/emitriscv64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
denis-paranichev marked this conversation as resolved.
Show resolved Hide resolved
{
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!");
Expand All @@ -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 */
denis-paranichev marked this conversation as resolved.
Show resolved Hide resolved
{
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;
denis-paranichev marked this conversation as resolved.
Show resolved Hide resolved
}
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.
Expand Down Expand Up @@ -3349,6 +3391,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");
Expand Down
9 changes: 9 additions & 0 deletions src/coreclr/jit/emitriscv64.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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);

Expand Down
6 changes: 6 additions & 0 deletions src/coreclr/jit/lsrariscv64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1269,6 +1269,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;
}

Expand Down
1 change: 1 addition & 0 deletions src/coreclr/jit/registerriscv64.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ REGDEF(T5, 30, 0x40000000, "t5" )
REGDEF(T6, 31, 0x80000000, "t6" )

REGALIAS(R8, FP)
REGALIAS(ZERO, R0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a common/official alias?

Copy link
Contributor Author

@denis-paranichev denis-paranichev Dec 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is official according to RISC-V specification.
https://riscv.org/wp-content/uploads/2015/01/riscv-calling.pdf


#define FBASE 32
#define FMASK(x) (1ULL << (FBASE+(x)))
Expand Down