From 8de688d4103b22c0e65ada8fa231e4578ded1563 Mon Sep 17 00:00:00 2001 From: Adeel <3840695+am11@users.noreply.github.com> Date: Sat, 30 May 2026 00:43:22 +0300 Subject: [PATCH] Detect and use zicond in ifconversion on riscv64 --- src/coreclr/inc/clrconfigvalues.h | 1 + src/coreclr/inc/corinfoinstructionset.h | 6 ++ src/coreclr/inc/jiteeversionguid.h | 10 ++-- src/coreclr/inc/readytoruninstructionset.h | 1 + src/coreclr/jit/codegenriscv64.cpp | 59 +++++++++++++++++++ src/coreclr/jit/compiler.cpp | 5 ++ src/coreclr/jit/compiler.h | 2 + src/coreclr/jit/emitriscv64.cpp | 17 +++++- src/coreclr/jit/ifconversion.cpp | 34 ++++++++++- src/coreclr/jit/instrsriscv64.h | 5 ++ src/coreclr/jit/jitconfigvalues.h | 1 + src/coreclr/jit/lower.cpp | 4 +- src/coreclr/jit/lowerriscv64.cpp | 56 +++++++++++++++++- src/coreclr/jit/lsrariscv64.cpp | 13 ++++ .../Compiler/HardwareIntrinsicHelpers.cs | 4 ++ .../Runtime/ReadyToRunInstructionSet.cs | 1 + .../Runtime/ReadyToRunInstructionSetHelper.cs | 1 + .../JitInterface/CorInfoInstructionSet.cs | 7 +++ .../ThunkGenerator/InstructionSetDesc.txt | 2 + src/coreclr/vm/codeman.cpp | 5 ++ src/native/minipal/cpufeatures.c | 5 ++ src/native/minipal/cpufeatures.h | 1 + 22 files changed, 230 insertions(+), 10 deletions(-) diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h index 324e2b7241bed5..3313ae0303966c 100644 --- a/src/coreclr/inc/clrconfigvalues.h +++ b/src/coreclr/inc/clrconfigvalues.h @@ -714,6 +714,7 @@ RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableArm64SveSm4, W("EnableArm64Sv RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableRiscV64Zba, W("EnableRiscV64Zba"), 1, "Allows RiscV64 Zba hardware intrinsics to be disabled") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableRiscV64Zbb, W("EnableRiscV64Zbb"), 1, "Allows RiscV64 Zbb hardware intrinsics to be disabled") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableRiscV64Zbs, W("EnableRiscV64Zbs"), 1, "Allows RiscV64 Zbs hardware intrinsics to be disabled") +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableRiscV64Zicond, W("EnableRiscV64Zicond"), 1, "Allows RiscV64 Zicond hardware intrinsics to be disabled") #endif /// diff --git a/src/coreclr/inc/corinfoinstructionset.h b/src/coreclr/inc/corinfoinstructionset.h index a09fdf84086964..60e20e7c5e5c48 100644 --- a/src/coreclr/inc/corinfoinstructionset.h +++ b/src/coreclr/inc/corinfoinstructionset.h @@ -60,6 +60,7 @@ enum CORINFO_InstructionSet InstructionSet_Zba=2, InstructionSet_Zbb=3, InstructionSet_Zbs=4, + InstructionSet_Zicond=5, #endif // TARGET_RISCV64 #ifdef TARGET_AMD64 InstructionSet_X86Base=1, @@ -447,6 +448,8 @@ inline CORINFO_InstructionSetFlags EnsureInstructionSetFlagsAreValid(CORINFO_Ins resultflags.RemoveInstructionSet(InstructionSet_Zba); if (resultflags.HasInstructionSet(InstructionSet_Zbs) && !resultflags.HasInstructionSet(InstructionSet_RiscV64Base)) resultflags.RemoveInstructionSet(InstructionSet_Zbs); + if (resultflags.HasInstructionSet(InstructionSet_Zicond) && !resultflags.HasInstructionSet(InstructionSet_RiscV64Base)) + resultflags.RemoveInstructionSet(InstructionSet_Zicond); #endif // TARGET_RISCV64 #ifdef TARGET_AMD64 if (resultflags.HasInstructionSet(InstructionSet_X86Base) && !resultflags.HasInstructionSet(InstructionSet_X86Base_X64)) @@ -741,6 +744,8 @@ inline const char *InstructionSetToString(CORINFO_InstructionSet instructionSet) return "Zbb"; case InstructionSet_Zbs : return "Zbs"; + case InstructionSet_Zicond : + return "Zicond"; #endif // TARGET_RISCV64 #ifdef TARGET_AMD64 case InstructionSet_X86Base : @@ -942,6 +947,7 @@ inline CORINFO_InstructionSet InstructionSetFromR2RInstructionSet(ReadyToRunInst case READYTORUN_INSTRUCTION_Zba: return InstructionSet_Zba; case READYTORUN_INSTRUCTION_Zbb: return InstructionSet_Zbb; case READYTORUN_INSTRUCTION_Zbs: return InstructionSet_Zbs; + case READYTORUN_INSTRUCTION_Zicond: return InstructionSet_Zicond; #endif // TARGET_RISCV64 #ifdef TARGET_AMD64 case READYTORUN_INSTRUCTION_X86Base: return InstructionSet_X86Base; diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h index 3f1d8e566f18f4..e24e253cc52768 100644 --- a/src/coreclr/inc/jiteeversionguid.h +++ b/src/coreclr/inc/jiteeversionguid.h @@ -37,11 +37,11 @@ #include -constexpr GUID JITEEVersionIdentifier = { /* 31a04b06-915e-42a0-bbd2-c9c397677ae5 */ - 0x31a04b06, - 0x915e, - 0x42a0, - {0xbb, 0xd2, 0xc9, 0xc3, 0x97, 0x67, 0x7a, 0xe5} +constexpr GUID JITEEVersionIdentifier = { /* c7a85d30-48b8-4d2f-8767-89f520440ded */ + 0xc7a85d30, + 0x48b8, + 0x4d2f, + {0x87, 0x67, 0x89, 0xf5, 0x20, 0x44, 0x0d, 0xed} }; #endif // JIT_EE_VERSIONING_GUID_H diff --git a/src/coreclr/inc/readytoruninstructionset.h b/src/coreclr/inc/readytoruninstructionset.h index d6621204e21a75..f98acc6021ef9f 100644 --- a/src/coreclr/inc/readytoruninstructionset.h +++ b/src/coreclr/inc/readytoruninstructionset.h @@ -98,6 +98,7 @@ enum ReadyToRunInstructionSet READYTORUN_INSTRUCTION_SveAes=88, READYTORUN_INSTRUCTION_SveSha3=89, READYTORUN_INSTRUCTION_SveSm4=90, + READYTORUN_INSTRUCTION_Zicond=91, }; diff --git a/src/coreclr/jit/codegenriscv64.cpp b/src/coreclr/jit/codegenriscv64.cpp index b91240d58f3ebb..62da498abcd144 100644 --- a/src/coreclr/jit/codegenriscv64.cpp +++ b/src/coreclr/jit/codegenriscv64.cpp @@ -3150,6 +3150,61 @@ void CodeGen::genCodeForCompare(GenTreeOp* tree) genProduceReg(tree); } +//------------------------------------------------------------------------ +// genCodeForSelect: Produce branch-free code for a GT_SELECT node using Zicond. +// +// SELECT(cond, trueVal, falseVal) => +// czero.nez tmp, falseVal, cond ; tmp = (cond != 0) ? 0 : falseVal +// czero.eqz dst, trueVal, cond ; dst = (cond == 0) ? 0 : trueVal +// or dst, dst, tmp +// +// Degenerate cases (one operand is the zero register) collapse to a single czero. +// +// Arguments: +// tree - the GT_SELECT node +// +void CodeGen::genCodeForSelect(GenTreeOp* tree) +{ + assert(tree->OperIs(GT_SELECT)); + assert(m_compiler->compOpportunisticallyDependsOn(InstructionSet_Zicond)); + assert(varTypeIsIntegralOrI(tree)); + + GenTreeConditional* sel = tree->AsConditional(); + GenTree* cond = sel->gtCond; + GenTree* trueVal = sel->gtOp1; + GenTree* falseVal = sel->gtOp2; + + genConsumeRegs(cond); + genConsumeRegs(trueVal); + genConsumeRegs(falseVal); + + regNumber targetReg = tree->GetRegNum(); + regNumber condReg = cond->GetRegNum(); + regNumber trueReg = trueVal->isContained() ? REG_ZERO : trueVal->GetRegNum(); + regNumber falseReg = falseVal->isContained() ? REG_ZERO : falseVal->GetRegNum(); + + emitter* emit = GetEmitter(); + emitAttr attr = emitActualTypeSize(tree); + + if (falseReg == REG_ZERO) + { + emit->emitIns_R_R_R(INS_czero_eqz, attr, targetReg, trueReg, condReg); + } + else if (trueReg == REG_ZERO) + { + emit->emitIns_R_R_R(INS_czero_nez, attr, targetReg, falseReg, condReg); + } + else + { + regNumber tmpReg = internalRegisters.Extract(tree); + emit->emitIns_R_R_R(INS_czero_nez, attr, tmpReg, falseReg, condReg); + emit->emitIns_R_R_R(INS_czero_eqz, attr, targetReg, trueReg, condReg); + emit->emitIns_R_R_R(INS_or, attr, targetReg, targetReg, tmpReg); + } + + genProduceReg(tree); +} + //------------------------------------------------------------------------ // genCodeForJumpCompare: Generates code for jmpCompare statement. // @@ -3940,6 +3995,10 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode) genCodeForCompare(treeNode->AsOp()); break; + case GT_SELECT: + genCodeForSelect(treeNode->AsOp()); + break; + case GT_JCMP: genCodeForJumpCompare(treeNode->AsOpCC()); break; diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index d0b148f2506378..d8b3a889dc2d18 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -6215,6 +6215,11 @@ int Compiler::compCompileAfterInit(CORINFO_MODULE_HANDLE classPtr, { instructionSetFlags.AddInstructionSet(InstructionSet_Zbb); } + + if (JitConfig.EnableRiscV64Zicond() != 0) + { + instructionSetFlags.AddInstructionSet(InstructionSet_Zicond); + } #endif // These calls are important and explicitly ordered to ensure that the flags are correct in diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 5dd03596daf24f..d3e565375b230b 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -10698,6 +10698,7 @@ class Compiler } #endif // DEBUG +public: bool notifyInstructionSetUsage(CORINFO_InstructionSet isa, bool supported) const; // Answer the question: Is a particular ISA allowed to be used implicitly by optimizations? @@ -10741,6 +10742,7 @@ class Compiler return opts.compSupportsISA.HasInstructionSet(isa); } +private: #ifdef DEBUG //------------------------------------------------------------------------ // canUseEvexEncodingDebugOnly - Answer the question: Is Evex encoding supported on this target. diff --git a/src/coreclr/jit/emitriscv64.cpp b/src/coreclr/jit/emitriscv64.cpp index f1de046359846e..54be522aec6a24 100644 --- a/src/coreclr/jit/emitriscv64.cpp +++ b/src/coreclr/jit/emitriscv64.cpp @@ -850,7 +850,7 @@ void emitter::emitIns_R_R_R( (INS_fadd_d <= ins && ins <= INS_fmax_d) || (INS_feq_s <= ins && ins <= INS_fle_s) || (INS_feq_d <= ins && ins <= INS_fle_d) || (INS_lr_w <= ins && ins <= INS_amomaxu_d) || (INS_sh1add <= ins && ins <= INS_sh3add_uw) || (INS_rol <= ins && ins <= INS_maxu) || - (INS_bset <= ins && ins <= INS_binv)) + (INS_bset <= ins && ins <= INS_binv) || (INS_czero_eqz <= ins && ins <= INS_czero_nez)) { #ifdef DEBUG switch (ins) @@ -962,6 +962,9 @@ void emitter::emitIns_R_R_R( case INS_bclr: case INS_bext: case INS_binv: + + case INS_czero_eqz: + case INS_czero_nez: break; default: NYI_RISCV64("illegal ins within emitIns_R_R_R!"); @@ -4041,6 +4044,18 @@ void emitter::emitDispInsName( return emitDispIllegalInstruction(code); printf("binv %s, %s, %s\n", rd, rs1, rs2); return; + case 0b0000111: + switch (opcode3) + { + case 0b101: + printf("czero.eqz %s, %s, %s\n", rd, rs1, rs2); + return; + case 0b111: + printf("czero.nez %s, %s, %s\n", rd, rs1, rs2); + return; + default: + return emitDispIllegalInstruction(code); + } default: return emitDispIllegalInstruction(code); } diff --git a/src/coreclr/jit/ifconversion.cpp b/src/coreclr/jit/ifconversion.cpp index 64529c541b00d8..b950b007956a8e 100644 --- a/src/coreclr/jit/ifconversion.cpp +++ b/src/coreclr/jit/ifconversion.cpp @@ -639,8 +639,38 @@ bool OptIfConversionDsc::optIfConvert(int* pReachabilityBudget) #ifdef TARGET_RISCV64 if (select->OperIs(GT_SELECT)) { - JITDUMP("Skipping if-conversion that could not be optimized to ordinary operations\n"); - return true; + // Without Zicond, riscv64 has no native lowering for GT_SELECT - the + // branchy form is kept. With Zicond the SELECT is lowered to a + // czero.{eqz,nez}/or sequence, but only for integer-typed selects. + bool canCodegenSelect = m_compiler->compOpportunisticallyDependsOn(InstructionSet_Zicond) && + varTypeIsIntegralOrI(select->TypeGet()); + if (!canCodegenSelect) + { + JITDUMP("Skipping if-conversion that could not be optimized to ordinary operations\n"); + return true; + } + + // RISC-V has no flag register, so any branchless SELECT must first materialize + // the condition into a 0/1 register before the czero/or sequence (>=5 insns). + // When one arm is a no-op (read of an existing local) and the other is a small + // immediate, the branchy form fits in ~2 insns (branch + addi), so keep it. + GenTreeConditional* sel = select->AsConditional(); + GenTree* selTrue = sel->gtOp1; + GenTree* selFalse = sel->gtOp2; + + auto isLocalNoOp = [](GenTree* n) { + return n->OperIs(GT_LCL_VAR); + }; + auto isSmallImm = [](GenTree* n) { + return n->IsIntegralConst() && (n->AsIntConCommon()->IntegralValue() >= -2048) && + (n->AsIntConCommon()->IntegralValue() <= 2047); + }; + + if ((isLocalNoOp(selTrue) && isSmallImm(selFalse)) || (isSmallImm(selTrue) && isLocalNoOp(selFalse))) + { + JITDUMP("Skipping if-conversion: branchy form fits in ~2 insns; Zicond would need 5+\n"); + return true; + } } #endif diff --git a/src/coreclr/jit/instrsriscv64.h b/src/coreclr/jit/instrsriscv64.h index c15ab741ed3795..6fb67f4435674b 100644 --- a/src/coreclr/jit/instrsriscv64.h +++ b/src/coreclr/jit/instrsriscv64.h @@ -321,6 +321,11 @@ INST(bclri, "bclri", 0, 0x48001013) INST(bexti, "bexti", 0, 0x48005013) INST(binvi, "binvi", 0, 0x68001013) +// Zicond (RV32 + RV64) +//// R_R_R +INST(czero_eqz, "czero.eqz", 0, 0x0e005033) +INST(czero_nez, "czero.nez", 0, 0x0e007033) + // RVC INST(c_mv, "c.mv", 0, 0x00008002) INST(c_add, "c.add", 0, 0x00009002) diff --git a/src/coreclr/jit/jitconfigvalues.h b/src/coreclr/jit/jitconfigvalues.h index 6e36a55a67ae02..c48299fcdde5d5 100644 --- a/src/coreclr/jit/jitconfigvalues.h +++ b/src/coreclr/jit/jitconfigvalues.h @@ -442,6 +442,7 @@ RELEASE_CONFIG_INTEGER(EnableArm64SveSm4, "EnableArm64SveSm4", RELEASE_CONFIG_INTEGER(EnableRiscV64Zba, "EnableRiscV64Zba", 1) // Allows RiscV64 Zba hardware intrinsics to be disabled RELEASE_CONFIG_INTEGER(EnableRiscV64Zbb, "EnableRiscV64Zbb", 1) // Allows RiscV64 Zbb hardware intrinsics to be disabled RELEASE_CONFIG_INTEGER(EnableRiscV64Zbs, "EnableRiscV64Zbs", 1) // Allows RiscV64 Zbs hardware intrinsics to be disabled +RELEASE_CONFIG_INTEGER(EnableRiscV64Zicond, "EnableRiscV64Zicond", 1) // Allows RiscV64 Zicond hardware intrinsics to be disabled #endif RELEASE_CONFIG_INTEGER(EnableEmbeddedBroadcast, "EnableEmbeddedBroadcast", 1) // Allows embedded broadcasts to be disabled diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 5f136711aec316..51e026af6bab7f 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -4805,8 +4805,9 @@ GenTree* Lowering::LowerSelect(GenTreeConditional* select) // TODO-CQ: If we allowed multiple nodes to consume the same CPU flags then // we could do this on x86. We currently disable if-conversion for TYP_LONG // on 32-bit architectures because of this. - GenCondition selectCond; GenTreeOpCC* newSelect = nullptr; +#if !defined(TARGET_LOONGARCH64) && !defined(TARGET_RISCV64) + GenCondition selectCond; if (((select->gtFlags & GTF_SET_FLAGS) == 0) && TryLowerConditionToFlagsNode(select, cond, &selectCond)) { select->SetOper(GT_SELECTCC); @@ -4818,6 +4819,7 @@ GenTree* Lowering::LowerSelect(GenTreeConditional* select) JITDUMP("\n"); } else +#endif { ContainCheckSelect(select); } diff --git a/src/coreclr/jit/lowerriscv64.cpp b/src/coreclr/jit/lowerriscv64.cpp index d9164e5e18c7f8..9993b8a86a3e77 100644 --- a/src/coreclr/jit/lowerriscv64.cpp +++ b/src/coreclr/jit/lowerriscv64.cpp @@ -1283,7 +1283,61 @@ void Lowering::ContainCheckCompare(GenTreeOp* cmp) // void Lowering::ContainCheckSelect(GenTreeOp* node) { - noway_assert(!"GT_SELECT nodes are not supported on riscv64"); + assert(node->OperIs(GT_SELECT)); + // GT_SELECT is only produced/codegen'd on riscv64 when Zicond is available; integer-only. + assert(m_compiler->compOpportunisticallyDependsOn(InstructionSet_Zicond)); + assert(varTypeIsIntegralOrI(node)); + + GenTreeConditional* sel = node->AsConditional(); + + // czero.{eqz,nez} encode both polarities, so reversed relops (GE/LE and unsigned + // EQ/NE-with-zero) that would otherwise emit slt+xori can have the trailing xori + // dropped by swapping the SELECT arms and using the un-reversed compare directly. + GenTree* cond = sel->gtCond; + if (cond->OperIsCompare()) + { + GenTreeOp* relop = cond->AsOp(); + genTreeOps oper = relop->OperGet(); + // genCodeForCompare reverses GE/LE into LT/GT and emits a trailing xori; it + // also rewrites unsigned EQ(x,0)/NE(x,0) into LE/GT and reverses again. Both + // patterns end up materializing the negation as xori reg, reg, 1. + bool relopIsReversed = oper == GT_GE || oper == GT_LE; + if (!relopIsReversed && relop->OperIs(GT_EQ, GT_NE)) + { + GenTree* relopOp2 = relop->gtGetOp2(); + relopIsReversed = oper == GT_EQ && relopOp2->IsIntegralConst(0) && relopOp2->isContained(); + } + LIR::Use condUse; + if (relopIsReversed && BlockRange().TryGetUse(cond, &condUse) && (condUse.User() == sel)) + { + relop->SetOper(GenTree::ReverseRelop(oper)); + std::swap(sel->gtOp1, sel->gtOp2); + } + } + // Also fold an explicit XOR-with-1 negation produced by other lowerings. + else if (cond->OperIs(GT_XOR)) + { + GenTree* xorLhs = cond->AsOp()->gtGetOp1(); + GenTree* xorRhs = cond->AsOp()->gtGetOp2(); + LIR::Use condUse; + if (xorRhs->IsIntegralConst(1) && xorLhs->OperIsCompare() && BlockRange().TryGetUse(cond, &condUse) && + (condUse.User() == sel)) + { + sel->gtCond = xorLhs; + std::swap(sel->gtOp1, sel->gtOp2); + BlockRange().Remove(xorRhs); + BlockRange().Remove(cond); + } + } + + // czero.{eqz,nez} take a register source and a register condition, so the only + // contained form is an integral-zero operand that can be expressed via REG_ZERO. + GenTree* op1 = sel->gtOp1; + GenTree* op2 = sel->gtOp2; + if (op1->IsIntegralConst(0) && !op1->AsIntCon()->ImmedValNeedsReloc(m_compiler)) + MakeSrcContained(node, op1); + if (op2->IsIntegralConst(0) && !op2->AsIntCon()->ImmedValNeedsReloc(m_compiler)) + MakeSrcContained(node, op2); } //------------------------------------------------------------------------ diff --git a/src/coreclr/jit/lsrariscv64.cpp b/src/coreclr/jit/lsrariscv64.cpp index f2284323ce48a1..c0ba2d9eb01643 100644 --- a/src/coreclr/jit/lsrariscv64.cpp +++ b/src/coreclr/jit/lsrariscv64.cpp @@ -458,6 +458,19 @@ int LinearScan::BuildNode(GenTree* tree) srcCount = BuildCmp(tree); break; + case GT_SELECT: + { + GenTreeConditional* sel = tree->AsConditional(); + srcCount = BuildOperandUses(sel->gtCond); + srcCount += BuildOperandUses(sel->gtOp1); + srcCount += BuildOperandUses(sel->gtOp2); + buildInternalIntRegisterDefForNode(tree); + assert(dstCount == 1); + BuildDef(tree); + buildInternalRegisterUses(); + } + break; + case GT_CKFINITE: srcCount = 1; assert(dstCount == 1); diff --git a/src/coreclr/tools/Common/Compiler/HardwareIntrinsicHelpers.cs b/src/coreclr/tools/Common/Compiler/HardwareIntrinsicHelpers.cs index daea4e9d90811f..83624f21a02d92 100644 --- a/src/coreclr/tools/Common/Compiler/HardwareIntrinsicHelpers.cs +++ b/src/coreclr/tools/Common/Compiler/HardwareIntrinsicHelpers.cs @@ -324,6 +324,7 @@ private static class RiscV64IntrinsicConstants public const int Zba = (1 << 0); public const int Zbb = (1 << 1); public const int Zbs = (1 << 2); + public const int Zicond = (1 << 3); public static void AddToBuilder(InstructionSetSupportBuilder builder, int flags) { @@ -333,6 +334,8 @@ public static void AddToBuilder(InstructionSetSupportBuilder builder, int flags) builder.AddSupportedInstructionSet("zbb"); if ((flags & Zbs) != 0) builder.AddSupportedInstructionSet("zbs"); + if ((flags & Zicond) != 0) + builder.AddSupportedInstructionSet("zicond"); } public static int FromInstructionSet(InstructionSet instructionSet) @@ -346,6 +349,7 @@ public static int FromInstructionSet(InstructionSet instructionSet) InstructionSet.RiscV64_Zba => Zba, InstructionSet.RiscV64_Zbb => Zbb, InstructionSet.RiscV64_Zbs => Zbs, + InstructionSet.RiscV64_Zicond => Zicond, _ => throw new NotSupportedException(((InstructionSet_RiscV64)instructionSet).ToString()) }; diff --git a/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunInstructionSet.cs b/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunInstructionSet.cs index a56531e847e41c..2c179acef4d28c 100644 --- a/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunInstructionSet.cs +++ b/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunInstructionSet.cs @@ -101,5 +101,6 @@ public enum ReadyToRunInstructionSet SveAes = 88, SveSha3 = 89, SveSm4 = 90, + Zicond = 91, } } diff --git a/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunInstructionSetHelper.cs b/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunInstructionSetHelper.cs index be24be220c3719..8e20bd2f6289e8 100644 --- a/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunInstructionSetHelper.cs +++ b/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunInstructionSetHelper.cs @@ -73,6 +73,7 @@ public static class ReadyToRunInstructionSetHelper case InstructionSet.RiscV64_Zba: return ReadyToRunInstructionSet.Zba; case InstructionSet.RiscV64_Zbb: return ReadyToRunInstructionSet.Zbb; case InstructionSet.RiscV64_Zbs: return ReadyToRunInstructionSet.Zbs; + case InstructionSet.RiscV64_Zicond: return ReadyToRunInstructionSet.Zicond; default: throw new Exception("Unknown instruction set"); } diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoInstructionSet.cs b/src/coreclr/tools/Common/JitInterface/CorInfoInstructionSet.cs index fb21652b239f52..b53358e9a661b2 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoInstructionSet.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoInstructionSet.cs @@ -59,6 +59,7 @@ public enum InstructionSet RiscV64_Zba = InstructionSet_RiscV64.Zba, RiscV64_Zbb = InstructionSet_RiscV64.Zbb, RiscV64_Zbs = InstructionSet_RiscV64.Zbs, + RiscV64_Zicond = InstructionSet_RiscV64.Zicond, X64_X86Base = InstructionSet_X64.X86Base, X64_AVX = InstructionSet_X64.AVX, X64_AVX2 = InstructionSet_X64.AVX2, @@ -204,6 +205,7 @@ public enum InstructionSet_RiscV64 Zba = 2, Zbb = 3, Zbs = 4, + Zicond = 5, } public enum InstructionSet_X64 @@ -579,6 +581,8 @@ public static InstructionSetFlags ExpandInstructionSetByImplicationHelper(Target resultflags.AddInstructionSet(InstructionSet.RiscV64_RiscV64Base); if (resultflags.HasInstructionSet(InstructionSet.RiscV64_Zbs)) resultflags.AddInstructionSet(InstructionSet.RiscV64_RiscV64Base); + if (resultflags.HasInstructionSet(InstructionSet.RiscV64_Zicond)) + resultflags.AddInstructionSet(InstructionSet.RiscV64_RiscV64Base); break; case TargetArchitecture.X64: @@ -877,6 +881,8 @@ private static InstructionSetFlags ExpandInstructionSetByReverseImplicationHelpe resultflags.AddInstructionSet(InstructionSet.RiscV64_Zba); if (resultflags.HasInstructionSet(InstructionSet.RiscV64_RiscV64Base)) resultflags.AddInstructionSet(InstructionSet.RiscV64_Zbs); + if (resultflags.HasInstructionSet(InstructionSet.RiscV64_RiscV64Base)) + resultflags.AddInstructionSet(InstructionSet.RiscV64_Zicond); break; case TargetArchitecture.X64: @@ -1122,6 +1128,7 @@ public static IEnumerable ArchitectureToValidInstructionSets yield return new InstructionSetInfo("zba", "", InstructionSet.RiscV64_Zba, true); yield return new InstructionSetInfo("zbb", "", InstructionSet.RiscV64_Zbb, true); yield return new InstructionSetInfo("zbs", "", InstructionSet.RiscV64_Zbs, true); + yield return new InstructionSetInfo("zicond", "", InstructionSet.RiscV64_Zicond, true); break; case TargetArchitecture.X64: diff --git a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/InstructionSetDesc.txt b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/InstructionSetDesc.txt index 1391dba1965a87..5673b59c48da90 100644 --- a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/InstructionSetDesc.txt +++ b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/InstructionSetDesc.txt @@ -282,10 +282,12 @@ instructionset ,RiscV64 , ,RiscV64Base ,56 ,RiscV64Base ,base instructionset ,RiscV64 , ,Zba ,57 ,Zba ,zba instructionset ,RiscV64 , ,Zbb ,58 ,Zbb ,zbb instructionset ,RiscV64 , ,Zbs ,84 ,Zbs ,zbs +instructionset ,RiscV64 , ,Zicond ,91 ,Zicond ,zicond implication ,RiscV64 ,Zbb ,RiscV64Base implication ,RiscV64 ,Zba ,RiscV64Base implication ,RiscV64 ,Zbs ,RiscV64Base +implication ,RiscV64 ,Zicond ,RiscV64Base ; ,name and aliases ,archs ,lower baselines included by implication instructionsetgroup ,x86-64-v2 ,X64 X86 ,base diff --git a/src/coreclr/vm/codeman.cpp b/src/coreclr/vm/codeman.cpp index 033a2d62645e3e..e2c7a5f48e0e0a 100644 --- a/src/coreclr/vm/codeman.cpp +++ b/src/coreclr/vm/codeman.cpp @@ -1597,6 +1597,11 @@ void EEJitManager::SetCpuInfo() { CPUCompileFlags.Set(InstructionSet_Zbs); } + + if (((cpuFeatures & RiscV64IntrinsicConstants_Zicond) != 0) && CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_EnableRiscV64Zicond)) + { + CPUCompileFlags.Set(InstructionSet_Zicond); + } #endif // These calls are very important as it ensures the flags are consistent with any diff --git a/src/native/minipal/cpufeatures.c b/src/native/minipal/cpufeatures.c index 01d673f4ae4275..bb021ca639f970 100644 --- a/src/native/minipal/cpufeatures.c +++ b/src/native/minipal/cpufeatures.c @@ -750,6 +750,11 @@ int minipal_getcpufeatures(void) { result |= RiscV64IntrinsicConstants_Zbs; } + + if (pairs[0].value & RISCV_HWPROBE_EXT_ZICOND) + { + result |= RiscV64IntrinsicConstants_Zicond; + } } #endif // HAVE_HWPROBE_H diff --git a/src/native/minipal/cpufeatures.h b/src/native/minipal/cpufeatures.h index c9dd0fb7b20e43..abeeb745aa0bae 100644 --- a/src/native/minipal/cpufeatures.h +++ b/src/native/minipal/cpufeatures.h @@ -62,6 +62,7 @@ static_assert((1 << ARM64_ATOMICS_FEATURE_FLAG_BIT) == ARM64IntrinsicConstants_A #define RiscV64IntrinsicConstants_Zba (1 << 0) #define RiscV64IntrinsicConstants_Zbb (1 << 1) #define RiscV64IntrinsicConstants_Zbs (1 << 2) +#define RiscV64IntrinsicConstants_Zicond (1 << 3) #endif // HOST_RISCV64 #ifdef __cplusplus