diff --git a/src/coreclr/jit/codegenriscv64.cpp b/src/coreclr/jit/codegenriscv64.cpp index db7abe9cf5e08a..c371e5aeebda00 100644 --- a/src/coreclr/jit/codegenriscv64.cpp +++ b/src/coreclr/jit/codegenriscv64.cpp @@ -4722,7 +4722,44 @@ void CodeGen::genEmitGSCookieCheck(bool pushReg) // void CodeGen::genIntrinsic(GenTreeIntrinsic* treeNode) { - NYI_RISCV64("genIntrinsic-----unimplemented/unused on RISCV64 yet----"); + GenTree* op1 = treeNode->gtGetOp1(); + GenTree* op2 = treeNode->gtGetOp2IfPresent(); + + emitAttr size = emitActualTypeSize(treeNode); + bool is4 = (size == 4); + + instruction instr = INS_invalid; + switch (treeNode->gtIntrinsicName) + { + case NI_System_Math_Abs: + instr = is4 ? INS_fsgnjx_s : INS_fsgnjx_d; + op2 = op1; // "fabs rd, rs" is a pseudo-instruction for "fsgnjx rd, rs, rs" + break; + case NI_System_Math_Sqrt: + instr = is4 ? INS_fsqrt_s : INS_fsqrt_d; + break; + case NI_System_Math_MinNumber: + instr = is4 ? INS_fmin_s : INS_fmin_d; + break; + case NI_System_Math_MaxNumber: + instr = is4 ? INS_fmax_s : INS_fmax_d; + break; + default: + NO_WAY("Unknown intrinsic"); + } + + genConsumeOperands(treeNode->AsOp()); + regNumber dest = treeNode->GetRegNum(); + regNumber src1 = op1->GetRegNum(); + if (op2 == nullptr) + { + GetEmitter()->emitIns_R_R(instr, size, dest, src1); + } + else + { + GetEmitter()->emitIns_R_R_R(instr, size, dest, src1, op2->GetRegNum()); + } + genProduceReg(treeNode); } //--------------------------------------------------------------------- diff --git a/src/coreclr/jit/emitriscv64.cpp b/src/coreclr/jit/emitriscv64.cpp index 0c5e32c9dbda59..37840c81186b8b 100644 --- a/src/coreclr/jit/emitriscv64.cpp +++ b/src/coreclr/jit/emitriscv64.cpp @@ -247,7 +247,7 @@ bool emitter::emitInsIsLoadOrStore(instruction ins) * Returns the specific encoding of the given CPU instruction. */ -inline emitter::code_t emitter::emitInsCode(instruction ins /*, insFormat fmt*/) const +inline emitter::code_t emitter::emitInsCode(instruction ins /*, insFormat fmt*/) { code_t code = BAD_CODE; @@ -688,7 +688,7 @@ void emitter::emitIns_R_R( if (INS_fcvt_d_w != ins && INS_fcvt_d_wu != ins) // fcvt.d.w[u] always produces an exact result code |= 0x7 << 12; // round according to frm status register } - else if (INS_fcvt_s_d == ins || INS_fcvt_d_s == ins) + else if (INS_fcvt_s_d == ins || INS_fcvt_d_s == ins || INS_fsqrt_s == ins || INS_fsqrt_d == ins) { assert(isFloatReg(reg1)); assert(isFloatReg(reg2)); @@ -865,7 +865,6 @@ void emitter::emitIns_R_R_R( case INS_fsub_s: case INS_fmul_s: case INS_fdiv_s: - case INS_fsqrt_s: case INS_fsgnj_s: case INS_fsgnjn_s: case INS_fsgnjx_s: @@ -880,7 +879,6 @@ void emitter::emitIns_R_R_R( case INS_fsub_d: case INS_fmul_d: case INS_fdiv_d: - case INS_fsqrt_d: case INS_fsgnj_d: case INS_fsgnjn_d: case INS_fsgnjx_d: @@ -925,7 +923,7 @@ void emitter::emitIns_R_R_R( code |= ((reg1 & 0x1f) << 7); code |= ((reg2 & 0x1f) << 15); code |= ((reg3 & 0x1f) << 20); - if ((INS_fadd_s <= ins && INS_fsqrt_s >= ins) || (INS_fadd_d <= ins && INS_fsqrt_d >= ins)) + if ((INS_fadd_s <= ins && INS_fdiv_s >= ins) || (INS_fadd_d <= ins && INS_fdiv_d >= ins)) { code |= 0x7 << 12; } @@ -4083,22 +4081,17 @@ void emitter::emitDispInsName( case 0x2C: // FSQRT.S printf("fsqrt.s %s, %s\n", fd, fs1); return; - case 0x10: // FSGNJ.S & FSGNJN.S & FSGNJX.S - if (opcode4 == 0) // FSGNJ.S + case 0x10: // FSGNJ.S & FSGNJN.S & FSGNJX.S + NYI_IF(opcode4 >= 3, "RISC-V illegal fsgnj.s variant"); + if (fs1 != fs2) { - printf("fsgnj.s %s, %s, %s\n", fd, fs1, fs2); + const char* variants[3] = {".s ", "n.s", "x.s"}; + printf("fsgnj%s %s, %s, %s\n", variants[opcode4], fd, fs1, fs2); } - else if (opcode4 == 1) // FSGNJN.S + else // pseudos { - printf("fsgnjn.s %s, %s, %s\n", fd, fs1, fs2); - } - else if (opcode4 == 2) // FSGNJX.S - { - printf("fsgnjx.s %s, %s, %s\n", fd, fs1, fs2); - } - else - { - NYI_RISCV64("illegal ins within emitDisInsName!"); + const char* names[3] = {"fmv.s ", "fneg.s", "fabs.s"}; + printf("%s %s, %s\n", names[opcode4], fd, fs1); } return; case 0x14: // FMIN.S & FMAX.S @@ -4186,7 +4179,6 @@ void emitter::emitDispInsName( { printf("fcvt.s.lu %s, %s\n", fd, xs1); } - else { NYI_RISCV64("illegal ins within emitDisInsName!"); @@ -4210,22 +4202,17 @@ void emitter::emitDispInsName( case 0x2d: // FSQRT.D printf("fsqrt.d %s, %s\n", fd, fs1); return; - case 0x11: // FSGNJ.D & FSGNJN.D & FSGNJX.D - if (opcode4 == 0) // FSGNJ.D + case 0x11: // FSGNJ.D & FSGNJN.D & FSGNJX.D + NYI_IF(opcode4 >= 3, "RISC-V illegal fsgnj.d variant"); + if (fs1 != fs2) { - printf("fsgnj.d %s, %s, %s\n", fd, fs1, fs2); + const char* variants[3] = {".d ", "n.d", "x.d"}; + printf("fsgnj%s %s, %s, %s\n", variants[opcode4], fd, fs1, fs2); } - else if (opcode4 == 1) // FSGNJN.D + else // pseudos { - printf("fsgnjn.d %s, %s, %s\n", fd, fs1, fs2); - } - else if (opcode4 == 2) // FSGNJX.D - { - printf("fsgnjx.d %s, %s, %s\n", fd, fs1, fs2); - } - else - { - NYI_RISCV64("illegal ins within emitDisInsName!"); + const char* names[3] = {"fmv.d ", "fneg.d", "fabs.d"}; + printf("%s %s, %s\n", names[opcode4], fd, fs1); } return; case 0x15: // FMIN.D & FMAX.D diff --git a/src/coreclr/jit/emitriscv64.h b/src/coreclr/jit/emitriscv64.h index bcc87538f3b18d..796c264c485950 100644 --- a/src/coreclr/jit/emitriscv64.h +++ b/src/coreclr/jit/emitriscv64.h @@ -70,7 +70,7 @@ bool emitDispBranchInstrType(unsigned opcode2, bool is_zero_reg, bool& print_sec void emitDispIllegalInstruction(code_t instructionCode); void emitDispImmediate(ssize_t imm, bool newLine = true, unsigned regBase = REG_ZERO); -emitter::code_t emitInsCode(instruction ins /*, insFormat fmt*/) const; +static emitter::code_t emitInsCode(instruction ins /*, insFormat fmt*/); // Generate code for a load or store operation and handle the case of contained GT_LEA op1 with [base + offset] void emitInsLoadStoreOp(instruction ins, emitAttr attr, regNumber dataReg, GenTreeIndir* indir); diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index cbc2c89f136ad3..7df9b4bff89f16 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -7907,7 +7907,28 @@ bool Compiler::IsTargetIntrinsic(NamedIntrinsic intrinsicName) default: return false; } -#elif defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) +#elif defined(TARGET_RISCV64) + switch (intrinsicName) + { + case NI_System_Math_Abs: + case NI_System_Math_Sqrt: + case NI_System_Math_MinNumber: + case NI_System_Math_MinMagnitudeNumber: + case NI_System_Math_MaxNumber: + case NI_System_Math_MaxMagnitudeNumber: + case NI_System_Math_Min: + case NI_System_Math_MinMagnitude: + case NI_System_Math_Max: + case NI_System_Math_MaxMagnitude: + case NI_System_Math_MultiplyAddEstimate: + case NI_System_Math_ReciprocalEstimate: + case NI_System_Math_ReciprocalSqrtEstimate: + return true; + + default: + return false; + } +#elif defined(TARGET_LOONGARCH64) switch (intrinsicName) { case NI_System_Math_Abs: @@ -7915,8 +7936,6 @@ bool Compiler::IsTargetIntrinsic(NamedIntrinsic intrinsicName) case NI_System_Math_ReciprocalSqrtEstimate: { // TODO-LoongArch64: support these standard intrinsics - // TODO-RISCV64: support these standard intrinsics - return false; } @@ -10177,6 +10196,48 @@ GenTree* Compiler::impMinMaxIntrinsic(CORINFO_METHOD_HANDLE method, } #endif // FEATURE_HW_INTRINSICS && TARGET_XARCH +#ifdef TARGET_RISCV64 + GenTree *op1Clone = nullptr, *op2Clone = nullptr; + + op2 = impPopStack().val; + if (!isNumber) + { + op2 = impCloneExpr(op2, &op2Clone, CHECK_SPILL_ALL, nullptr DEBUGARG("Clone op2 for Math.Min/Max non-Number")); + } + + op1 = impPopStack().val; + if (!isNumber) + { + op1 = impCloneExpr(op1, &op1Clone, CHECK_SPILL_ALL, nullptr DEBUGARG("Clone op1 for Math.Min/Max non-Number")); + } + + static const CORINFO_CONST_LOOKUP nullEntry = {IAT_VALUE}; + if (isMagnitude) + { + op1 = new (this, GT_INTRINSIC) GenTreeIntrinsic(callType, op1, NI_System_Math_Abs, nullptr R2RARG(nullEntry)); + op2 = new (this, GT_INTRINSIC) GenTreeIntrinsic(callType, op2, NI_System_Math_Abs, nullptr R2RARG(nullEntry)); + } + NamedIntrinsic name = isMax ? NI_System_Math_MaxNumber : NI_System_Math_MinNumber; + GenTree* minMax = new (this, GT_INTRINSIC) GenTreeIntrinsic(callType, op1, op2, name, nullptr R2RARG(nullEntry)); + + if (!isNumber) + { + GenTreeOp* isOp1Number = gtNewOperNode(GT_EQ, TYP_INT, op1Clone, gtCloneExpr(op1Clone)); + GenTreeOp* isOp2Number = gtNewOperNode(GT_EQ, TYP_INT, op2Clone, gtCloneExpr(op2Clone)); + GenTreeOp* isOkForMinMax = gtNewOperNode(GT_EQ, TYP_INT, isOp1Number, isOp2Number); + + GenTreeOp* nanPropagator = gtNewOperNode(GT_ADD, callType, gtCloneExpr(op1Clone), gtCloneExpr(op2Clone)); + + GenTreeQmark* qmark = gtNewQmarkNode(callType, isOkForMinMax, gtNewColonNode(callType, minMax, nanPropagator)); + // QMARK has to be a root node + unsigned tmp = lvaGrabTemp(true DEBUGARG("Temp for Qmark in Math.Min/Max non-Number")); + impStoreToTemp(tmp, qmark, CHECK_SPILL_NONE); + minMax = gtNewLclvNode(tmp, callType); + } + + return minMax; +#endif // TARGET_RISCV64 + // TODO-CQ: Returning this as an intrinsic blocks inlining and is undesirable // return impMathIntrinsic(method, sig, callType, intrinsicName, tailCall, isSpecial); diff --git a/src/coreclr/jit/lsrariscv64.cpp b/src/coreclr/jit/lsrariscv64.cpp index 2d67e5ce33e50b..186927b290290e 100644 --- a/src/coreclr/jit/lsrariscv64.cpp +++ b/src/coreclr/jit/lsrariscv64.cpp @@ -346,19 +346,23 @@ int LinearScan::BuildNode(GenTree* tree) case GT_INTRINSIC: { - noway_assert((tree->AsIntrinsic()->gtIntrinsicName == NI_System_Math_Abs) || - (tree->AsIntrinsic()->gtIntrinsicName == NI_System_Math_Ceiling) || - (tree->AsIntrinsic()->gtIntrinsicName == NI_System_Math_Floor) || - (tree->AsIntrinsic()->gtIntrinsicName == NI_System_Math_Round) || - (tree->AsIntrinsic()->gtIntrinsicName == NI_System_Math_Sqrt)); + NamedIntrinsic name = tree->AsIntrinsic()->gtIntrinsicName; + noway_assert((name == NI_System_Math_Abs) || (name == NI_System_Math_Sqrt) || + (name == NI_System_Math_MinNumber) || (name == NI_System_Math_MaxNumber)); // Both operand and its result must be of the same floating point type. GenTree* op1 = tree->gtGetOp1(); + GenTree* op2 = tree->gtGetOp2IfPresent(); assert(varTypeIsFloating(op1)); - assert(op1->TypeGet() == tree->TypeGet()); - + assert(op1->TypeIs(tree->TypeGet())); BuildUse(op1); srcCount = 1; + if (op2 != nullptr) + { + assert(op2->TypeIs(tree->TypeGet())); + BuildUse(op2); + srcCount++; + } assert(dstCount == 1); BuildDef(tree); } diff --git a/src/libraries/System.Private.CoreLib/src/System/Math.cs b/src/libraries/System.Private.CoreLib/src/System/Math.cs index 9337314de15b01..0c21426a16c155 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Math.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Math.cs @@ -1263,7 +1263,7 @@ public static double ReciprocalEstimate(double d) [Intrinsic] public static double ReciprocalSqrtEstimate(double d) { -#if MONO || TARGET_RISCV64 || TARGET_LOONGARCH64 +#if MONO || TARGET_LOONGARCH64 return 1.0 / Sqrt(d); #else return ReciprocalSqrtEstimate(d); diff --git a/src/libraries/System.Private.CoreLib/src/System/MathF.cs b/src/libraries/System.Private.CoreLib/src/System/MathF.cs index 2d1e6e4eda4758..834b4f5e9606ca 100644 --- a/src/libraries/System.Private.CoreLib/src/System/MathF.cs +++ b/src/libraries/System.Private.CoreLib/src/System/MathF.cs @@ -334,7 +334,7 @@ public static float ReciprocalEstimate(float x) [Intrinsic] public static float ReciprocalSqrtEstimate(float x) { -#if MONO || TARGET_RISCV64 || TARGET_LOONGARCH64 +#if MONO || TARGET_LOONGARCH64 return 1.0f / Sqrt(x); #else return ReciprocalSqrtEstimate(x);