Skip to content

Commit 513c52b

Browse files
tomeksowiam11
andauthored
[RISC-V] Simple math intrinsics (#113689)
* fabs & fsqrt * Min/MaxNumber * Min/MaxMagnitudeNumber * ReciprocalSqrtEstimate * Min/Max, Min/MaxMagnitude * Fix assert in lsrarisc64.cpp * Improve Min/Max non-Number in IL * name #endif * minor cleanup * Intrinsify LeadingZeroCount * TrailingZeroCount * PopCount * Build fix * Remove assertion from default case * fabs comment * CHECK_SPILL_ALL in mixn/max non-number operand clones * Revert bit count intrinsics * fix comment Co-authored-by: Adeel Mujahid <3840695+am11@users.noreply.github.com> --------- Co-authored-by: Adeel Mujahid <3840695+am11@users.noreply.github.com>
1 parent ed755bb commit 513c52b

File tree

7 files changed

+135
-46
lines changed

7 files changed

+135
-46
lines changed

src/coreclr/jit/codegenriscv64.cpp

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4722,7 +4722,44 @@ void CodeGen::genEmitGSCookieCheck(bool pushReg)
47224722
//
47234723
void CodeGen::genIntrinsic(GenTreeIntrinsic* treeNode)
47244724
{
4725-
NYI_RISCV64("genIntrinsic-----unimplemented/unused on RISCV64 yet----");
4725+
GenTree* op1 = treeNode->gtGetOp1();
4726+
GenTree* op2 = treeNode->gtGetOp2IfPresent();
4727+
4728+
emitAttr size = emitActualTypeSize(treeNode);
4729+
bool is4 = (size == 4);
4730+
4731+
instruction instr = INS_invalid;
4732+
switch (treeNode->gtIntrinsicName)
4733+
{
4734+
case NI_System_Math_Abs:
4735+
instr = is4 ? INS_fsgnjx_s : INS_fsgnjx_d;
4736+
op2 = op1; // "fabs rd, rs" is a pseudo-instruction for "fsgnjx rd, rs, rs"
4737+
break;
4738+
case NI_System_Math_Sqrt:
4739+
instr = is4 ? INS_fsqrt_s : INS_fsqrt_d;
4740+
break;
4741+
case NI_System_Math_MinNumber:
4742+
instr = is4 ? INS_fmin_s : INS_fmin_d;
4743+
break;
4744+
case NI_System_Math_MaxNumber:
4745+
instr = is4 ? INS_fmax_s : INS_fmax_d;
4746+
break;
4747+
default:
4748+
NO_WAY("Unknown intrinsic");
4749+
}
4750+
4751+
genConsumeOperands(treeNode->AsOp());
4752+
regNumber dest = treeNode->GetRegNum();
4753+
regNumber src1 = op1->GetRegNum();
4754+
if (op2 == nullptr)
4755+
{
4756+
GetEmitter()->emitIns_R_R(instr, size, dest, src1);
4757+
}
4758+
else
4759+
{
4760+
GetEmitter()->emitIns_R_R_R(instr, size, dest, src1, op2->GetRegNum());
4761+
}
4762+
genProduceReg(treeNode);
47264763
}
47274764

47284765
//---------------------------------------------------------------------

src/coreclr/jit/emitriscv64.cpp

Lines changed: 19 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ bool emitter::emitInsIsLoadOrStore(instruction ins)
247247
* Returns the specific encoding of the given CPU instruction.
248248
*/
249249

250-
inline emitter::code_t emitter::emitInsCode(instruction ins /*, insFormat fmt*/) const
250+
inline emitter::code_t emitter::emitInsCode(instruction ins /*, insFormat fmt*/)
251251
{
252252
code_t code = BAD_CODE;
253253

@@ -688,7 +688,7 @@ void emitter::emitIns_R_R(
688688
if (INS_fcvt_d_w != ins && INS_fcvt_d_wu != ins) // fcvt.d.w[u] always produces an exact result
689689
code |= 0x7 << 12; // round according to frm status register
690690
}
691-
else if (INS_fcvt_s_d == ins || INS_fcvt_d_s == ins)
691+
else if (INS_fcvt_s_d == ins || INS_fcvt_d_s == ins || INS_fsqrt_s == ins || INS_fsqrt_d == ins)
692692
{
693693
assert(isFloatReg(reg1));
694694
assert(isFloatReg(reg2));
@@ -865,7 +865,6 @@ void emitter::emitIns_R_R_R(
865865
case INS_fsub_s:
866866
case INS_fmul_s:
867867
case INS_fdiv_s:
868-
case INS_fsqrt_s:
869868
case INS_fsgnj_s:
870869
case INS_fsgnjn_s:
871870
case INS_fsgnjx_s:
@@ -880,7 +879,6 @@ void emitter::emitIns_R_R_R(
880879
case INS_fsub_d:
881880
case INS_fmul_d:
882881
case INS_fdiv_d:
883-
case INS_fsqrt_d:
884882
case INS_fsgnj_d:
885883
case INS_fsgnjn_d:
886884
case INS_fsgnjx_d:
@@ -925,7 +923,7 @@ void emitter::emitIns_R_R_R(
925923
code |= ((reg1 & 0x1f) << 7);
926924
code |= ((reg2 & 0x1f) << 15);
927925
code |= ((reg3 & 0x1f) << 20);
928-
if ((INS_fadd_s <= ins && INS_fsqrt_s >= ins) || (INS_fadd_d <= ins && INS_fsqrt_d >= ins))
926+
if ((INS_fadd_s <= ins && INS_fdiv_s >= ins) || (INS_fadd_d <= ins && INS_fdiv_d >= ins))
929927
{
930928
code |= 0x7 << 12;
931929
}
@@ -4319,22 +4317,17 @@ void emitter::emitDispInsName(
43194317
case 0x2C: // FSQRT.S
43204318
printf("fsqrt.s %s, %s\n", fd, fs1);
43214319
return;
4322-
case 0x10: // FSGNJ.S & FSGNJN.S & FSGNJX.S
4323-
if (opcode4 == 0) // FSGNJ.S
4320+
case 0x10: // FSGNJ.S & FSGNJN.S & FSGNJX.S
4321+
NYI_IF(opcode4 >= 3, "RISC-V illegal fsgnj.s variant");
4322+
if (fs1 != fs2)
43244323
{
4325-
printf("fsgnj.s %s, %s, %s\n", fd, fs1, fs2);
4324+
const char* variants[3] = {".s ", "n.s", "x.s"};
4325+
printf("fsgnj%s %s, %s, %s\n", variants[opcode4], fd, fs1, fs2);
43264326
}
4327-
else if (opcode4 == 1) // FSGNJN.S
4327+
else // pseudos
43284328
{
4329-
printf("fsgnjn.s %s, %s, %s\n", fd, fs1, fs2);
4330-
}
4331-
else if (opcode4 == 2) // FSGNJX.S
4332-
{
4333-
printf("fsgnjx.s %s, %s, %s\n", fd, fs1, fs2);
4334-
}
4335-
else
4336-
{
4337-
NYI_RISCV64("illegal ins within emitDisInsName!");
4329+
const char* names[3] = {"fmv.s ", "fneg.s", "fabs.s"};
4330+
printf("%s %s, %s\n", names[opcode4], fd, fs1);
43384331
}
43394332
return;
43404333
case 0x14: // FMIN.S & FMAX.S
@@ -4422,7 +4415,6 @@ void emitter::emitDispInsName(
44224415
{
44234416
printf("fcvt.s.lu %s, %s\n", fd, xs1);
44244417
}
4425-
44264418
else
44274419
{
44284420
NYI_RISCV64("illegal ins within emitDisInsName!");
@@ -4446,22 +4438,17 @@ void emitter::emitDispInsName(
44464438
case 0x2d: // FSQRT.D
44474439
printf("fsqrt.d %s, %s\n", fd, fs1);
44484440
return;
4449-
case 0x11: // FSGNJ.D & FSGNJN.D & FSGNJX.D
4450-
if (opcode4 == 0) // FSGNJ.D
4441+
case 0x11: // FSGNJ.D & FSGNJN.D & FSGNJX.D
4442+
NYI_IF(opcode4 >= 3, "RISC-V illegal fsgnj.d variant");
4443+
if (fs1 != fs2)
44514444
{
4452-
printf("fsgnj.d %s, %s, %s\n", fd, fs1, fs2);
4445+
const char* variants[3] = {".d ", "n.d", "x.d"};
4446+
printf("fsgnj%s %s, %s, %s\n", variants[opcode4], fd, fs1, fs2);
44534447
}
4454-
else if (opcode4 == 1) // FSGNJN.D
4448+
else // pseudos
44554449
{
4456-
printf("fsgnjn.d %s, %s, %s\n", fd, fs1, fs2);
4457-
}
4458-
else if (opcode4 == 2) // FSGNJX.D
4459-
{
4460-
printf("fsgnjx.d %s, %s, %s\n", fd, fs1, fs2);
4461-
}
4462-
else
4463-
{
4464-
NYI_RISCV64("illegal ins within emitDisInsName!");
4450+
const char* names[3] = {"fmv.d ", "fneg.d", "fabs.d"};
4451+
printf("%s %s, %s\n", names[opcode4], fd, fs1);
44654452
}
44664453
return;
44674454
case 0x15: // FMIN.D & FMAX.D

src/coreclr/jit/emitriscv64.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ bool emitDispBranchInstrType(unsigned opcode2, bool is_zero_reg, bool& print_sec
7070
void emitDispIllegalInstruction(code_t instructionCode);
7171
void emitDispImmediate(ssize_t imm, bool newLine = true, unsigned regBase = REG_ZERO);
7272

73-
emitter::code_t emitInsCode(instruction ins /*, insFormat fmt*/) const;
73+
static emitter::code_t emitInsCode(instruction ins /*, insFormat fmt*/);
7474

7575
// Generate code for a load or store operation and handle the case of contained GT_LEA op1 with [base + offset]
7676
void emitInsLoadStoreOp(instruction ins, emitAttr attr, regNumber dataReg, GenTreeIndir* indir);

src/coreclr/jit/importercalls.cpp

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7909,16 +7909,35 @@ bool Compiler::IsTargetIntrinsic(NamedIntrinsic intrinsicName)
79097909
default:
79107910
return false;
79117911
}
7912-
#elif defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
7912+
#elif defined(TARGET_RISCV64)
7913+
switch (intrinsicName)
7914+
{
7915+
case NI_System_Math_Abs:
7916+
case NI_System_Math_Sqrt:
7917+
case NI_System_Math_MinNumber:
7918+
case NI_System_Math_MinMagnitudeNumber:
7919+
case NI_System_Math_MaxNumber:
7920+
case NI_System_Math_MaxMagnitudeNumber:
7921+
case NI_System_Math_Min:
7922+
case NI_System_Math_MinMagnitude:
7923+
case NI_System_Math_Max:
7924+
case NI_System_Math_MaxMagnitude:
7925+
case NI_System_Math_MultiplyAddEstimate:
7926+
case NI_System_Math_ReciprocalEstimate:
7927+
case NI_System_Math_ReciprocalSqrtEstimate:
7928+
return true;
7929+
7930+
default:
7931+
return false;
7932+
}
7933+
#elif defined(TARGET_LOONGARCH64)
79137934
switch (intrinsicName)
79147935
{
79157936
case NI_System_Math_Abs:
79167937
case NI_System_Math_Sqrt:
79177938
case NI_System_Math_ReciprocalSqrtEstimate:
79187939
{
79197940
// TODO-LoongArch64: support these standard intrinsics
7920-
// TODO-RISCV64: support these standard intrinsics
7921-
79227941
return false;
79237942
}
79247943

@@ -10179,6 +10198,48 @@ GenTree* Compiler::impMinMaxIntrinsic(CORINFO_METHOD_HANDLE method,
1017910198
}
1018010199
#endif // FEATURE_HW_INTRINSICS && TARGET_XARCH
1018110200

10201+
#ifdef TARGET_RISCV64
10202+
GenTree *op1Clone = nullptr, *op2Clone = nullptr;
10203+
10204+
op2 = impPopStack().val;
10205+
if (!isNumber)
10206+
{
10207+
op2 = impCloneExpr(op2, &op2Clone, CHECK_SPILL_ALL, nullptr DEBUGARG("Clone op2 for Math.Min/Max non-Number"));
10208+
}
10209+
10210+
op1 = impPopStack().val;
10211+
if (!isNumber)
10212+
{
10213+
op1 = impCloneExpr(op1, &op1Clone, CHECK_SPILL_ALL, nullptr DEBUGARG("Clone op1 for Math.Min/Max non-Number"));
10214+
}
10215+
10216+
static const CORINFO_CONST_LOOKUP nullEntry = {IAT_VALUE};
10217+
if (isMagnitude)
10218+
{
10219+
op1 = new (this, GT_INTRINSIC) GenTreeIntrinsic(callType, op1, NI_System_Math_Abs, nullptr R2RARG(nullEntry));
10220+
op2 = new (this, GT_INTRINSIC) GenTreeIntrinsic(callType, op2, NI_System_Math_Abs, nullptr R2RARG(nullEntry));
10221+
}
10222+
NamedIntrinsic name = isMax ? NI_System_Math_MaxNumber : NI_System_Math_MinNumber;
10223+
GenTree* minMax = new (this, GT_INTRINSIC) GenTreeIntrinsic(callType, op1, op2, name, nullptr R2RARG(nullEntry));
10224+
10225+
if (!isNumber)
10226+
{
10227+
GenTreeOp* isOp1Number = gtNewOperNode(GT_EQ, TYP_INT, op1Clone, gtCloneExpr(op1Clone));
10228+
GenTreeOp* isOp2Number = gtNewOperNode(GT_EQ, TYP_INT, op2Clone, gtCloneExpr(op2Clone));
10229+
GenTreeOp* isOkForMinMax = gtNewOperNode(GT_EQ, TYP_INT, isOp1Number, isOp2Number);
10230+
10231+
GenTreeOp* nanPropagator = gtNewOperNode(GT_ADD, callType, gtCloneExpr(op1Clone), gtCloneExpr(op2Clone));
10232+
10233+
GenTreeQmark* qmark = gtNewQmarkNode(callType, isOkForMinMax, gtNewColonNode(callType, minMax, nanPropagator));
10234+
// QMARK has to be a root node
10235+
unsigned tmp = lvaGrabTemp(true DEBUGARG("Temp for Qmark in Math.Min/Max non-Number"));
10236+
impStoreToTemp(tmp, qmark, CHECK_SPILL_NONE);
10237+
minMax = gtNewLclvNode(tmp, callType);
10238+
}
10239+
10240+
return minMax;
10241+
#endif // TARGET_RISCV64
10242+
1018210243
// TODO-CQ: Returning this as an intrinsic blocks inlining and is undesirable
1018310244
// return impMathIntrinsic(method, sig, callType, intrinsicName, tailCall, isSpecial);
1018410245

src/coreclr/jit/lsrariscv64.cpp

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -346,19 +346,23 @@ int LinearScan::BuildNode(GenTree* tree)
346346

347347
case GT_INTRINSIC:
348348
{
349-
noway_assert((tree->AsIntrinsic()->gtIntrinsicName == NI_System_Math_Abs) ||
350-
(tree->AsIntrinsic()->gtIntrinsicName == NI_System_Math_Ceiling) ||
351-
(tree->AsIntrinsic()->gtIntrinsicName == NI_System_Math_Floor) ||
352-
(tree->AsIntrinsic()->gtIntrinsicName == NI_System_Math_Round) ||
353-
(tree->AsIntrinsic()->gtIntrinsicName == NI_System_Math_Sqrt));
349+
NamedIntrinsic name = tree->AsIntrinsic()->gtIntrinsicName;
350+
noway_assert((name == NI_System_Math_Abs) || (name == NI_System_Math_Sqrt) ||
351+
(name == NI_System_Math_MinNumber) || (name == NI_System_Math_MaxNumber));
354352

355353
// Both operand and its result must be of the same floating point type.
356354
GenTree* op1 = tree->gtGetOp1();
355+
GenTree* op2 = tree->gtGetOp2IfPresent();
357356
assert(varTypeIsFloating(op1));
358-
assert(op1->TypeGet() == tree->TypeGet());
359-
357+
assert(op1->TypeIs(tree->TypeGet()));
360358
BuildUse(op1);
361359
srcCount = 1;
360+
if (op2 != nullptr)
361+
{
362+
assert(op2->TypeIs(tree->TypeGet()));
363+
BuildUse(op2);
364+
srcCount++;
365+
}
362366
assert(dstCount == 1);
363367
BuildDef(tree);
364368
}

src/libraries/System.Private.CoreLib/src/System/Math.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1263,7 +1263,7 @@ public static double ReciprocalEstimate(double d)
12631263
[Intrinsic]
12641264
public static double ReciprocalSqrtEstimate(double d)
12651265
{
1266-
#if MONO || TARGET_RISCV64 || TARGET_LOONGARCH64
1266+
#if MONO || TARGET_LOONGARCH64
12671267
return 1.0 / Sqrt(d);
12681268
#else
12691269
return ReciprocalSqrtEstimate(d);

src/libraries/System.Private.CoreLib/src/System/MathF.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ public static float ReciprocalEstimate(float x)
334334
[Intrinsic]
335335
public static float ReciprocalSqrtEstimate(float x)
336336
{
337-
#if MONO || TARGET_RISCV64 || TARGET_LOONGARCH64
337+
#if MONO || TARGET_LOONGARCH64
338338
return 1.0f / Sqrt(x);
339339
#else
340340
return ReciprocalSqrtEstimate(x);

0 commit comments

Comments
 (0)