From c0072a07fafb802eb7794e7cd11faca5e47566cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Thu, 18 Sep 2025 16:31:43 +0200 Subject: [PATCH 01/13] Use common implementation of emitJumpDistBind --- src/coreclr/jit/emit.cpp | 57 ++++++++++++++++++++++++--------- src/coreclr/jit/emitriscv64.cpp | 51 +++++++++++++++++++++++++---- src/coreclr/jit/emitriscv64.h | 22 +++++++++++++ src/coreclr/jit/targetriscv64.h | 3 ++ 4 files changed, 112 insertions(+), 21 deletions(-) diff --git a/src/coreclr/jit/emit.cpp b/src/coreclr/jit/emit.cpp index 88cf1c67337c61..bbae2273ed18ab 100644 --- a/src/coreclr/jit/emit.cpp +++ b/src/coreclr/jit/emit.cpp @@ -4848,7 +4848,7 @@ void emitter::emitRemoveJumpToNextInst() * LoongArch64 has an individual implementation for emitJumpDistBind(). */ -#if !defined(TARGET_LOONGARCH64) && !defined(TARGET_RISCV64) +#if !defined(TARGET_LOONGARCH64) void emitter::emitJumpDistBind() { #ifdef DEBUG @@ -4873,9 +4873,9 @@ void emitter::emitJumpDistBind() // to a small jump. If it is small enough, we will iterate in hopes of // converting those jumps we missed converting the first (or second...) time. -#if defined(TARGET_ARM) +#if defined(TARGET_ARM) || defined(TARGET_RISCV64) UNATIVE_OFFSET minMediumExtra; // Same as 'minShortExtra', but for medium-sized jumps. -#endif // TARGET_ARM +#endif // TARGET_ARM || TARGET_RISCV64 UNATIVE_OFFSET adjIG; UNATIVE_OFFSET adjLJ; @@ -4911,9 +4911,9 @@ void emitter::emitJumpDistBind() adjIG = 0; minShortExtra = (UNATIVE_OFFSET)-1; -#if defined(TARGET_ARM) +#if defined(TARGET_ARM) || defined(TARGET_RISCV64) minMediumExtra = (UNATIVE_OFFSET)-1; -#endif // TARGET_ARM +#endif // TARGET_ARM || TARGET_RISCV64 for (jmp = emitJumpList; jmp; jmp = jmp->idjNext) { @@ -4926,12 +4926,12 @@ void emitter::emitJumpDistBind() NATIVE_OFFSET nsd = 0; // small jump max. neg distance NATIVE_OFFSET psd = 0; // small jump max. pos distance -#if defined(TARGET_ARM) +#if defined(TARGET_ARM) || defined(TARGET_RISCV64) UNATIVE_OFFSET msz = 0; // medium jump size NATIVE_OFFSET nmd = 0; // medium jump max. neg distance NATIVE_OFFSET pmd = 0; // medium jump max. pos distance NATIVE_OFFSET mextra; // How far beyond the medium jump range is this jump offset? -#endif // TARGET_ARM +#endif // TARGET_ARM || TARGET_RISCV64 NATIVE_OFFSET extra; // How far beyond the short jump range is this jump offset? UNATIVE_OFFSET srcInstrOffs; // offset of the source instruction of the jump @@ -5039,6 +5039,31 @@ void emitter::emitJumpDistBind() } #endif // TARGET_ARM64 +#ifdef TARGET_RISCV64 + /* Figure out the smallest size we can end up with */ + + if (emitIsCmpJump(jmp)) + { + ssz = sizeof(code_t); + nsd = B_DIST_SMALL_MAX_NEG; + psd = B_DIST_SMALL_MAX_POS; + + msz = sizeof(code_t) * 2; + nmd = J_DIST_SMALL_MAX_NEG; + pmd = J_DIST_SMALL_MAX_POS; + } + else if (emitIsUncondJump(jmp)) + { + ssz = sizeof(code_t); + nsd = J_DIST_SMALL_MAX_NEG; + psd = J_DIST_SMALL_MAX_POS; + } + else + { + assert(!"Unknown jump instruction"); + } +#endif // TARGET_RISCV64 + /* Make sure the jumps are properly ordered */ #ifdef DEBUG @@ -5244,9 +5269,9 @@ void emitter::emitJumpDistBind() #if defined(TARGET_ARM) srcEncodingOffs = srcInstrOffs + 4; // For relative branches, ARM PC is always considered to be the instruction address + 4 -#elif defined(TARGET_ARM64) - srcEncodingOffs = - srcInstrOffs; // For relative branches, ARM64 PC is always considered to be the instruction address +#elif defined(TARGET_ARM64) || defined(TARGET_RISCV64) + srcEncodingOffs = srcInstrOffs; // For relative branches, ARM64 and RISC-V PC is always considered to be the + // instruction address #else srcEncodingOffs = srcInstrOffs + ssz; // Encoding offset of relative offset for small branch #endif @@ -5360,14 +5385,14 @@ void emitter::emitJumpDistBind() minShortExtra = (unsigned)extra; } -#if defined(TARGET_ARM) +#if defined(TARGET_ARM) || defined(TARGET_RISCV64) // If we're here, we couldn't convert to a small jump. // Handle conversion to medium-sized conditional jumps. // 'srcInstrOffs', 'srcEncodingOffs', 'dstOffs', 'jmpDist' have already been computed // and don't need to be recomputed. - if (emitIsCondJump(jmp)) + if (emitIsCmpJump(jmp)) { if (jmpIG->igNum < tgtIG->igNum) { @@ -5432,7 +5457,7 @@ void emitter::emitJumpDistBind() minMediumExtra = (unsigned)mextra; } -#endif // TARGET_ARM +#endif // TARGET_ARM || TARGET_RISCV64 /***************************************************************************** * We arrive here if the jump must stay long, at least for now. @@ -5475,13 +5500,15 @@ void emitter::emitJumpDistBind() // The size of IF_LARGEJMP/IF_LARGEADR/IF_LARGELDC are 8 or 12. // All other code size is 4. assert((sizeDif == 4) || (sizeDif == 8)); +#elif defined(TARGET_RISCV64) + assert((sizeDif == 4) || (sizeDif == 8)); #else #error Unsupported or unset target architecture #endif goto NEXT_JMP; -#if defined(TARGET_ARM) +#if defined(TARGET_ARM) || defined(TARGET_RISCV64) /*****************************************************************************/ /* Handle conversion to medium jump */ @@ -5508,7 +5535,7 @@ void emitter::emitJumpDistBind() goto NEXT_JMP; -#endif // TARGET_ARM +#endif // TARGET_ARM || TARGET_RISCV64 /*****************************************************************************/ diff --git a/src/coreclr/jit/emitriscv64.cpp b/src/coreclr/jit/emitriscv64.cpp index edb5d94ccee070..79aab052a73ecb 100644 --- a/src/coreclr/jit/emitriscv64.cpp +++ b/src/coreclr/jit/emitriscv64.cpp @@ -1293,8 +1293,43 @@ void emitter::emitIns_R_AI(instruction ins, */ void emitter::emitSetShortJump(instrDescJmp* id) { - // TODO-RISCV64: maybe delete it on future. - NYI_RISCV64("emitSetShortJump-----unimplemented/unused on RISCV64 yet----"); + if (id->idjKeepLong) + return; + + assert(emitIsCmpJump(id) || emitIsUncondJump(id)); + id->idCodeSize(sizeof(code_t)); // single 32-bit instruction + id->idjShort = true; + id->idInsOpt(emitIsUncondJump(id) ? INS_OPTS_J : INS_OPTS_J_cond); + +#if DEBUG_EMIT + if (id->idDebugOnlyInfo()->idNum == (unsigned)INTERESTING_JUMP_NUM || INTERESTING_JUMP_NUM == 0) + { + printf("[8] Converting jump %u to short\n", id->idDebugOnlyInfo()->idNum); + } +#endif // DEBUG_EMIT +} + +/***************************************************************************** + * + * Record that a jump instruction uses the medium encoding + * + */ +void emitter::emitSetMediumJump(instrDescJmp* id) +{ + if (id->idjKeepLong) + return; + +#if DEBUG_EMIT + if (id->idDebugOnlyInfo()->idNum == (unsigned)INTERESTING_JUMP_NUM || INTERESTING_JUMP_NUM == 0) + { + printf("[9] Converting jump %u to medium\n", id->idDebugOnlyInfo()->idNum); + } +#endif // DEBUG_EMIT + + assert(emitIsCmpJump(id)); + id->idCodeSize(2 * sizeof(code_t)); // two 32-bit instruction + id->idInsOpt(INS_OPTS_JALR); + id->idjShort = false; } /***************************************************************************** @@ -1366,8 +1401,6 @@ void emitter::emitIns_J(instruction ins, BasicBlock* dst, int instrCount) id->idReg1((regNumber)(instrCount & 0x1f)); id->idReg2((regNumber)((instrCount >> 5) & 0x1f)); - id->idInsOpt(INS_OPTS_J); - emitCounts_INS_OPTS_J++; id->idAddr()->iiaBBlabel = dst; if (emitComp->opts.compReloc) @@ -1396,7 +1429,9 @@ void emitter::emitIns_J(instruction ins, BasicBlock* dst, int instrCount) emitTotalIGjmps++; #endif - id->idCodeSize(4); + // TODO: estimate jump length, now we're starting from maximum size + id->idCodeSize((emitIsUncondJump(id) ? 2 : 3) * sizeof(code_t)); + id->idInsOpt(INS_OPTS_JALR); appendToCurIG(id); } @@ -1449,7 +1484,9 @@ void emitter::emitIns_J_cond_la(instruction ins, BasicBlock* dst, regNumber reg1 emitTotalIGjmps++; #endif - id->idCodeSize(4); + // TODO: estimate jump length, now we're starting from maximum size + id->idCodeSize(3 * sizeof(code_t)); + id->idInsOpt(INS_OPTS_JALR); appendToCurIG(id); } @@ -2081,6 +2118,7 @@ unsigned emitter::emitOutputCall(const insGroup* ig, BYTE* dst, instrDesc* id) return (unsigned)(dst - origDst); } +#if 0 void emitter::emitJumpDistBind() { #ifdef DEBUG @@ -2420,6 +2458,7 @@ void emitter::emitJumpDistBind() emitCheckIGList(); #endif // DEBUG } +#endif /***************************************************************************** * diff --git a/src/coreclr/jit/emitriscv64.h b/src/coreclr/jit/emitriscv64.h index 616802ed478eea..4fe9a3623da643 100644 --- a/src/coreclr/jit/emitriscv64.h +++ b/src/coreclr/jit/emitriscv64.h @@ -358,4 +358,26 @@ unsigned emitOutputCall(const insGroup* ig, BYTE* dst, instrDesc* id); unsigned get_curTotalCodeSize(); // bytes of code +//------------------------------------------------------------------------ +// emitIsCmpJump: checks if it's a compare and jump (branch) +// +// Arguments: +// jmp - the instruction to check +// +inline static bool emitIsCmpJump(instrDesc* jmp) +{ + return jmp->idInsIs(INS_beqz, INS_bnez, INS_bne, INS_beq, INS_blt, INS_bltu, INS_bge, INS_bgeu); +} + +//------------------------------------------------------------------------ +// emitIsUncondJump: checks if it's an unconditional jump +// +// Arguments: +// jmp - the instruction to check +// +inline static bool emitIsUncondJump(instrDesc* jmp) +{ + return jmp->idInsIs(INS_j, INS_jal); +} + #endif // TARGET_RISCV64 diff --git a/src/coreclr/jit/targetriscv64.h b/src/coreclr/jit/targetriscv64.h index 642cc8d3e5df00..844478f1d8cf1e 100644 --- a/src/coreclr/jit/targetriscv64.h +++ b/src/coreclr/jit/targetriscv64.h @@ -275,6 +275,9 @@ extern const regNumber fltArgRegs [MAX_FLOAT_REG_ARG]; extern const regMaskTP fltArgMasks[MAX_FLOAT_REG_ARG]; + #define J_DIST_SMALL_MAX_NEG (-(1 << 20)) + #define J_DIST_SMALL_MAX_POS (+(1 << 20) - 1) + #define B_DIST_SMALL_MAX_NEG (-4096) #define B_DIST_SMALL_MAX_POS (+4095) From 27e46f161eb79d2cf8bcb955394247a6caaf37f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Fri, 19 Sep 2025 11:54:05 +0200 Subject: [PATCH 02/13] remove risc-v specific impl --- src/coreclr/jit/emitriscv64.cpp | 342 -------------------------------- 1 file changed, 342 deletions(-) diff --git a/src/coreclr/jit/emitriscv64.cpp b/src/coreclr/jit/emitriscv64.cpp index 79aab052a73ecb..06f12a494b50ba 100644 --- a/src/coreclr/jit/emitriscv64.cpp +++ b/src/coreclr/jit/emitriscv64.cpp @@ -2118,348 +2118,6 @@ unsigned emitter::emitOutputCall(const insGroup* ig, BYTE* dst, instrDesc* id) return (unsigned)(dst - origDst); } -#if 0 -void emitter::emitJumpDistBind() -{ -#ifdef DEBUG - if (emitComp->verbose) - { - printf("*************** In emitJumpDistBind()\n"); - } - if (EMIT_INSTLIST_VERBOSE) - { - printf("\nInstruction list before the jump distance binding:\n\n"); - emitDispIGlist(true); - } -#endif - -#if DEBUG_EMIT - auto printJmpInfo = [this](const instrDescJmp* jmp, const insGroup* jmpIG, NATIVE_OFFSET extra, - UNATIVE_OFFSET srcInstrOffs, UNATIVE_OFFSET srcEncodingOffs, UNATIVE_OFFSET dstOffs, - NATIVE_OFFSET jmpDist, const char* direction) { - assert(jmp->idDebugOnlyInfo() != nullptr); - if (jmp->idDebugOnlyInfo()->idNum == (unsigned)INTERESTING_JUMP_NUM || INTERESTING_JUMP_NUM == 0) - { - const char* dirId = (strcmp(direction, "fwd") == 0) ? "[1]" : "[2]"; - if (INTERESTING_JUMP_NUM == 0) - { - printf("%s Jump %u:\n", dirId, jmp->idDebugOnlyInfo()->idNum); - } - printf("%s Jump block is at %08X\n", dirId, jmpIG->igOffs); - printf("%s Jump reloffset is %04X\n", dirId, jmp->idjOffs); - printf("%s Jump source is at %08X\n", dirId, srcEncodingOffs); - printf("%s Label block is at %08X\n", dirId, dstOffs); - printf("%s Jump dist. is %04X\n", dirId, jmpDist); - if (extra > 0) - { - printf("%s Dist excess [S] = %d \n", dirId, extra); - } - } - if (EMITVERBOSE) - { - printf("Estimate of %s jump [%08X/%03u]: %04X -> %04X = %04X\n", direction, dspPtr(jmp), - jmp->idDebugOnlyInfo()->idNum, srcInstrOffs, dstOffs, jmpDist); - } - }; -#endif - - instrDescJmp* jmp; - - UNATIVE_OFFSET adjIG; - UNATIVE_OFFSET adjSJ; - insGroup* lstIG; -#ifdef DEBUG - insGroup* prologIG = emitPrologIG; -#endif // DEBUG - - // NOTE: - // bit0 of isLinkingEnd: indicating whether updating the instrDescJmp's size with the type INS_OPTS_J; - // bit1 of isLinkingEnd: indicating not needed updating the size while emitTotalCodeSize <= 0xfff or had - // updated; - unsigned int isLinkingEnd = emitTotalCodeSize <= 0xfff ? 2 : 0; - - UNATIVE_OFFSET ssz = 0; // relative small jump's delay-slot. - // small jump max. neg distance - NATIVE_OFFSET nsd = B_DIST_SMALL_MAX_NEG; - // small jump max. pos distance - NATIVE_OFFSET maxPlaceholderSize = - emitCounts_INS_OPTS_J * (6 << 2); // the max placeholder sizeof(INS_OPTS_JALR) - sizeof(INS_OPTS_J) - NATIVE_OFFSET psd = B_DIST_SMALL_MAX_POS - maxPlaceholderSize; - - /*****************************************************************************/ - /* If the default small encoding is not enough, we start again here. */ - /*****************************************************************************/ - -AGAIN: - -#ifdef DEBUG - emitCheckIGList(); -#endif - -#ifdef DEBUG - insGroup* lastIG = nullptr; - instrDescJmp* lastSJ = nullptr; -#endif - - lstIG = nullptr; - adjSJ = 0; - adjIG = 0; - - for (jmp = emitJumpList; jmp; jmp = jmp->idjNext) - { - insGroup* jmpIG; - insGroup* tgtIG; - - UNATIVE_OFFSET jsz; // size of the jump instruction in bytes - - NATIVE_OFFSET extra; // How far beyond the short jump range is this jump offset? - UNATIVE_OFFSET srcInstrOffs; // offset of the source instruction of the jump - UNATIVE_OFFSET srcEncodingOffs; // offset of the source used by the instruction set to calculate the relative - // offset of the jump - UNATIVE_OFFSET dstOffs; - NATIVE_OFFSET jmpDist; // the relative jump distance, as it will be encoded - - /* Make sure the jumps are properly ordered */ - -#ifdef DEBUG - assert(lastSJ == nullptr || lastIG != jmp->idjIG || lastSJ->idjOffs < (jmp->idjOffs + adjSJ)); - lastSJ = (lastIG == jmp->idjIG) ? jmp : nullptr; - - assert(lastIG == nullptr || lastIG->igNum <= jmp->idjIG->igNum || jmp->idjIG == prologIG || - emitNxtIGnum > unsigned(0xFFFF)); // igNum might overflow - lastIG = jmp->idjIG; -#endif // DEBUG - - /* Get hold of the current jump size */ - - jsz = jmp->idCodeSize(); - - /* Get the group the jump is in */ - - jmpIG = jmp->idjIG; - - /* Are we in a group different from the previous jump? */ - - if (lstIG != jmpIG) - { - /* Were there any jumps before this one? */ - - if (lstIG) - { - /* Adjust the offsets of the intervening blocks */ - - do - { - lstIG = lstIG->igNext; - assert(lstIG); -#ifdef DEBUG - if (EMITVERBOSE) - { - printf("Adjusted offset of " FMT_BB " from %04X to %04X\n", lstIG->igNum, lstIG->igOffs, - lstIG->igOffs + adjIG); - } -#endif // DEBUG - lstIG->igOffs += adjIG; - assert(IsCodeAligned(lstIG->igOffs)); - } while (lstIG != jmpIG); - } - - /* We've got the first jump in a new group */ - adjSJ = 0; - lstIG = jmpIG; - } - - /* Apply any local size adjustment to the jump's relative offset */ - jmp->idjOffs += adjSJ; - - // If this is a jump via register, the instruction size does not change, so we are done. - - /* Have we bound this jump's target already? */ - - if (jmp->idIsBound()) - { - /* Does the jump already have the smallest size? */ - - if (jmp->idjShort) - { - // We should not be jumping/branching across funclets/functions - emitCheckFuncletBranch(jmp, jmpIG); - - continue; - } - - tgtIG = jmp->idAddr()->iiaIGlabel; - } - else - { - /* First time we've seen this label, convert its target */ - - tgtIG = (insGroup*)emitCodeGetCookie(jmp->idAddr()->iiaBBlabel); - -#ifdef DEBUG - if (EMITVERBOSE) - { - if (tgtIG) - { - printf(" to %s\n", emitLabelString(tgtIG)); - } - else - { - printf("-- ERROR, no emitter cookie for " FMT_BB "; it is probably missing BBF_HAS_LABEL.\n", - jmp->idAddr()->iiaBBlabel->bbNum); - } - } - assert(tgtIG); -#endif // DEBUG - - /* Record the bound target */ - - jmp->idAddr()->iiaIGlabel = tgtIG; - jmp->idSetIsBound(); - } - - // We should not be jumping/branching across funclets/functions - emitCheckFuncletBranch(jmp, jmpIG); - - /* - In the following distance calculations, if we're not actually - scheduling the code (i.e. reordering instructions), we can - use the actual offset of the jump (rather than the beg/end of - the instruction group) since the jump will not be moved around - and thus its offset is accurate. - - First we need to figure out whether this jump is a forward or - backward one; to do this we simply look at the ordinals of the - group that contains the jump and the target. - */ - - srcInstrOffs = jmpIG->igOffs + jmp->idjOffs; - - /* Note that the destination is always the beginning of an IG, so no need for an offset inside it */ - dstOffs = tgtIG->igOffs; - - srcEncodingOffs = srcInstrOffs + ssz; // Encoding offset of relative offset for small branch - - const char* direction = nullptr; - if (jmpIG->igNum < tgtIG->igNum) - { - /* Forward jump */ - direction = "fwd"; - - /* Adjust the target offset by the current delta. This is a worst-case estimate, as jumps between - here and the target could be shortened, causing the actual distance to shrink. - */ - dstOffs += adjIG; - - /* Compute the distance estimate */ - jmpDist = dstOffs - srcEncodingOffs; - - /* How much beyond the max. short distance does the jump go? */ - extra = jmpDist - psd; - } - else - { - /* Backward jump */ - direction = "bwd"; - - /* Compute the distance estimate */ - jmpDist = srcEncodingOffs - dstOffs; - - /* How much beyond the max. short distance does the jump go? */ - extra = jmpDist + nsd; - } - -#if DEBUG_EMIT - printJmpInfo(jmp, jmpIG, extra, srcInstrOffs, srcEncodingOffs, dstOffs, jmpDist, direction); -#endif // DEBUG_EMIT - - assert(jmpDist >= 0); - assert(!(jmpDist & 0x1)); - - if (!(isLinkingEnd & 0x2) && (extra > 0) && - (jmp->idInsOpt() == INS_OPTS_J || jmp->idInsOpt() == INS_OPTS_J_cond)) - { - // transform INS_OPTS_J/INS_OPTS_J_cond jump when jmpDist exceed the maximum short distance - instruction ins = jmp->idIns(); - assert((INS_jal <= ins) && (ins <= INS_bgeu)); - - if (ins > INS_jalr || (ins < INS_jalr && ins > INS_j)) // jal < beqz < bnez < jalr < - // beq/bne/blt/bltu/bge/bgeu - { - if (isValidSimm13(jmpDist + maxPlaceholderSize)) - { - continue; - } - // convert branch to opposite branch and jump - int insCount = isValidSimm21(jmpDist + maxPlaceholderSize) ? 1 /*jal*/ : 2 /*auipc+jalr*/; - extra = insCount * sizeof(code_t); - } - else if (ins == INS_jal || ins == INS_j) - { - if (isValidSimm21(jmpDist + maxPlaceholderSize)) - { - continue; - } - // convert jal to auipc+jalr - extra = sizeof(code_t); - } - else - { - unreached(); - } - - jmp->idInsOpt(INS_OPTS_JALR); - jmp->idCodeSize(jmp->idCodeSize() + extra); - jmpIG->igSize += (unsigned short)extra; // the placeholder sizeof(INS_OPTS_JALR) - sizeof(INS_OPTS_J). - adjSJ += (UNATIVE_OFFSET)extra; - adjIG += (UNATIVE_OFFSET)extra; - emitTotalCodeSize += (UNATIVE_OFFSET)extra; - jmpIG->igFlags |= IGF_UPD_ISZ; - isLinkingEnd |= 0x1; - } - } // end for each jump - - if ((isLinkingEnd & 0x3) < 0x2) - { - // indicating the instrDescJmp's size of the type INS_OPTS_J had updated - // after the first round and should iterate again to update. - isLinkingEnd = 0x2; - - // Adjust offsets of any remaining blocks. - for (; lstIG;) - { - lstIG = lstIG->igNext; - if (!lstIG) - { - break; - } -#ifdef DEBUG - if (EMITVERBOSE) - { - printf("Adjusted offset of " FMT_BB " from %04X to %04X\n", lstIG->igNum, lstIG->igOffs, - lstIG->igOffs + adjIG); - } -#endif // DEBUG - - lstIG->igOffs += adjIG; - - assert(IsCodeAligned(lstIG->igOffs)); - } - goto AGAIN; - } - -#ifdef DEBUG - if (EMIT_INSTLIST_VERBOSE) - { - printf("\nLabels list after the jump distance binding:\n\n"); - emitDispIGlist(false); - } - - emitCheckIGList(); -#endif // DEBUG -} -#endif - /***************************************************************************** * * Emit a 16/32-bit RISCV64 instruction From bdd7fbed5e88e6cf20409bbd32c693c83e8d3d40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Tue, 7 Oct 2025 15:00:56 +0200 Subject: [PATCH 03/13] Estimate jump distances --- src/coreclr/jit/emit.cpp | 2 +- src/coreclr/jit/emitriscv64.cpp | 73 ++++++++++++++++++++++++++++++++- 2 files changed, 72 insertions(+), 3 deletions(-) diff --git a/src/coreclr/jit/emit.cpp b/src/coreclr/jit/emit.cpp index bbae2273ed18ab..2e1a8791b482e4 100644 --- a/src/coreclr/jit/emit.cpp +++ b/src/coreclr/jit/emit.cpp @@ -5501,7 +5501,7 @@ void emitter::emitJumpDistBind() // All other code size is 4. assert((sizeDif == 4) || (sizeDif == 8)); #elif defined(TARGET_RISCV64) - assert((sizeDif == 4) || (sizeDif == 8)); + assert((sizeDif == 0) || (sizeDif == 4) || (sizeDif == 8)); #else #error Unsupported or unset target architecture #endif diff --git a/src/coreclr/jit/emitriscv64.cpp b/src/coreclr/jit/emitriscv64.cpp index 06f12a494b50ba..02a84eb084957e 100644 --- a/src/coreclr/jit/emitriscv64.cpp +++ b/src/coreclr/jit/emitriscv64.cpp @@ -1327,7 +1327,7 @@ void emitter::emitSetMediumJump(instrDescJmp* id) #endif // DEBUG_EMIT assert(emitIsCmpJump(id)); - id->idCodeSize(2 * sizeof(code_t)); // two 32-bit instruction + id->idCodeSize(2 * sizeof(code_t)); // two 32-bit instructions id->idInsOpt(INS_OPTS_JALR); id->idjShort = false; } @@ -1429,10 +1429,44 @@ void emitter::emitIns_J(instruction ins, BasicBlock* dst, int instrCount) emitTotalIGjmps++; #endif - // TODO: estimate jump length, now we're starting from maximum size id->idCodeSize((emitIsUncondJump(id) ? 2 : 3) * sizeof(code_t)); id->idInsOpt(INS_OPTS_JALR); + /* Figure out the max. size of the jump/call instruction */ + if (insGroup* tgt = (insGroup*)emitCodeGetCookie(dst); !id->idjKeepLong && (tgt != nullptr)) + { + /* This is a backward jump - figure out the distance */ + UNATIVE_OFFSET srcOffs = emitCurCodeOffset + emitCurIGsize; + + /* Compute the distance estimate */ + int jmpDist = srcOffs - tgt->igOffs; + assert(jmpDist >= 0); + + // TODO: separate, unconditional jumps only in emitIns_J, branches in emitIns_J_cond_la. + if (emitIsCmpJump(id)) + { + if (B_DIST_SMALL_MAX_NEG <= -jmpDist) + { + /* This jump surely will be short */ + emitSetShortJump(id); + } + else if (J_DIST_SMALL_MAX_NEG <= -jmpDist) + { + /* This jump surely will be medium */ + emitSetMediumJump(id); + } + } + else + { + assert(emitIsUncondJump(id)); + if (J_DIST_SMALL_MAX_NEG <= -jmpDist) + { + /* This jump surely will be short */ + emitSetShortJump(id); + } + } + } + appendToCurIG(id); } @@ -1488,6 +1522,41 @@ void emitter::emitIns_J_cond_la(instruction ins, BasicBlock* dst, regNumber reg1 id->idCodeSize(3 * sizeof(code_t)); id->idInsOpt(INS_OPTS_JALR); + /* Figure out the max. size of the jump/call instruction */ + if (insGroup* tgt = (insGroup*)emitCodeGetCookie(dst); !id->idjKeepLong && (tgt != nullptr)) + { + /* This is a backward jump - figure out the distance */ + UNATIVE_OFFSET srcOffs = emitCurCodeOffset + emitCurIGsize; + + /* Compute the distance estimate */ + int jmpDist = srcOffs - tgt->igOffs; + assert(jmpDist >= 0); + + // TODO: separate, unconditional jumps only in emitIns_J, branches in emitIns_J_cond_la. + if (emitIsCmpJump(id)) + { + if (B_DIST_SMALL_MAX_NEG <= -jmpDist) + { + /* This jump surely will be short */ + emitSetShortJump(id); + } + else if (J_DIST_SMALL_MAX_NEG <= -jmpDist) + { + /* This jump surely will be medium */ + emitSetMediumJump(id); + } + } + else + { + assert(emitIsUncondJump(id)); + if (J_DIST_SMALL_MAX_NEG <= -jmpDist) + { + /* This jump surely will be short */ + emitSetShortJump(id); + } + } + } + appendToCurIG(id); } From 26ec3216923ba34c61ede432165d333c8d015dc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Thu, 9 Oct 2025 11:57:09 +0200 Subject: [PATCH 04/13] Add jump distance estimates, separate emitIns_J and _J_cond_la --- src/coreclr/jit/codegenriscv64.cpp | 11 ++-- src/coreclr/jit/emit.cpp | 2 +- src/coreclr/jit/emitriscv64.cpp | 82 +++++------------------------- src/coreclr/jit/emitriscv64.h | 2 +- 4 files changed, 20 insertions(+), 77 deletions(-) diff --git a/src/coreclr/jit/codegenriscv64.cpp b/src/coreclr/jit/codegenriscv64.cpp index e581bc44d7029b..bc4ae81765955f 100644 --- a/src/coreclr/jit/codegenriscv64.cpp +++ b/src/coreclr/jit/codegenriscv64.cpp @@ -2351,7 +2351,7 @@ void CodeGen::genCodeForCmpXchg(GenTreeCmpXchg* treeNode) e->emitIns_R_R_R(is4 ? INS_lr_w : INS_lr_d, size, target, loc, REG_R0); // load original value e->emitIns_J_cond_la(INS_bne, fail, target, comparand); // fail if doesn’t match e->emitIns_R_R_R(is4 ? INS_sc_w : INS_sc_d, size, storeErr, loc, val); // try to update - e->emitIns_J(INS_bnez, retry, storeErr); // retry if update failed + e->emitIns_J_cond_la(INS_bnez, retry, storeErr); // retry if update failed genDefineTempLabel(fail); gcInfo.gcMarkRegSetNpt(locOp->gtGetRegMask()); @@ -3501,7 +3501,8 @@ void CodeGen::genCodeForJumpCompare(GenTreeOpCC* tree) assert(ins != INS_invalid); assert(regs != 0); - emit->emitIns_J(ins, compiler->compCurBB->GetTrueTarget(), regs); // 5-bits; + emit->emitIns_J_cond_la(ins, compiler->compCurBB->GetTrueTarget(), (regNumber)(regs & 0x1F), + (regNumber)((regs >> 5) & 0x1F)); // If we cannot fall into the false target, emit a jump to it BasicBlock* falseTarget = compiler->compCurBB->GetFalseTarget(); @@ -5540,7 +5541,7 @@ void CodeGen::genCodeForInitBlkLoop(GenTreeBlk* initBlkNode) // tempReg = tempReg - 8 GetEmitter()->emitIns_R_R_I(INS_addi, EA_PTRSIZE, tempReg, tempReg, -8); // if (tempReg != dstReg) goto loop; - GetEmitter()->emitIns_J(INS_bne, loop, (int)tempReg | ((int)dstReg << 5)); + GetEmitter()->emitIns_J_cond_la(INS_bne, loop, tempReg, dstReg); GetEmitter()->emitEnableGC(); gcInfo.gcMarkRegSetNpt(genRegMask(dstReg)); @@ -6487,10 +6488,8 @@ void CodeGen::genJumpToThrowHlpBlk_la( #endif // !FEATURE_FIXED_OUT_ARGS } - noway_assert(excpRaisingBlock != nullptr); - // Jump to the exception-throwing block on error. - emit->emitIns_J(ins, excpRaisingBlock, (int)reg1 | ((int)reg2 << 5)); // 5-bits; + emit->emitIns_J_cond_la(ins, excpRaisingBlock, reg1, reg2); } else { diff --git a/src/coreclr/jit/emit.cpp b/src/coreclr/jit/emit.cpp index 2e1a8791b482e4..594c43c9aa9801 100644 --- a/src/coreclr/jit/emit.cpp +++ b/src/coreclr/jit/emit.cpp @@ -4840,7 +4840,7 @@ void emitter::emitRemoveJumpToNextInst() /***************************************************************************** * Bind targets of relative jumps to choose the smallest possible encoding. * X86 and AMD64 have a small and large encoding. - * ARM has a small, medium, and large encoding. The large encoding is a pseudo-op + * ARM and RISC-V have a small, medium, and large encoding. The large encoding is a pseudo-op * to handle greater range than the conditional branch instructions can handle. * ARM64 has a small and large encoding for both conditional branch and loading label addresses. * The large encodings are pseudo-ops that represent a multiple instruction sequence, similar to ARM. (Currently diff --git a/src/coreclr/jit/emitriscv64.cpp b/src/coreclr/jit/emitriscv64.cpp index 02a84eb084957e..df6c242a54723f 100644 --- a/src/coreclr/jit/emitriscv64.cpp +++ b/src/coreclr/jit/emitriscv64.cpp @@ -1386,21 +1386,14 @@ void emitter::emitIns_J_R(instruction ins, emitAttr attr, BasicBlock* dst, regNu NYI_RISCV64("emitIns_J_R-----unimplemented/unused on RISCV64 yet----"); } -void emitter::emitIns_J(instruction ins, BasicBlock* dst, int instrCount) +void emitter::emitIns_J(instruction ins, BasicBlock* dst) { assert(dst != nullptr); - // - // INS_OPTS_J: placeholders. 1-ins: if the dst outof-range will be replaced by INS_OPTS_JALR. - // jal/j/jalr/bnez/beqz/beq/bne/blt/bge/bltu/bgeu dst - assert(dst->HasFlag(BBF_HAS_LABEL)); instrDescJmp* id = emitNewInstrJmp(); - assert((INS_jal <= ins) && (ins <= INS_bgeu)); id->idIns(ins); - id->idReg1((regNumber)(instrCount & 0x1f)); - id->idReg2((regNumber)((instrCount >> 5) & 0x1f)); - + assert(emitIsUncondJump(id)); id->idAddr()->iiaBBlabel = dst; if (emitComp->opts.compReloc) @@ -1429,7 +1422,8 @@ void emitter::emitIns_J(instruction ins, BasicBlock* dst, int instrCount) emitTotalIGjmps++; #endif - id->idCodeSize((emitIsUncondJump(id) ? 2 : 3) * sizeof(code_t)); + // Start from worst case (2 instructions): "auipc; jr" + id->idCodeSize(2 * sizeof(code_t)); id->idInsOpt(INS_OPTS_JALR); /* Figure out the max. size of the jump/call instruction */ @@ -1442,28 +1436,9 @@ void emitter::emitIns_J(instruction ins, BasicBlock* dst, int instrCount) int jmpDist = srcOffs - tgt->igOffs; assert(jmpDist >= 0); - // TODO: separate, unconditional jumps only in emitIns_J, branches in emitIns_J_cond_la. - if (emitIsCmpJump(id)) - { - if (B_DIST_SMALL_MAX_NEG <= -jmpDist) - { - /* This jump surely will be short */ - emitSetShortJump(id); - } - else if (J_DIST_SMALL_MAX_NEG <= -jmpDist) - { - /* This jump surely will be medium */ - emitSetMediumJump(id); - } - } - else + if (J_DIST_SMALL_MAX_NEG <= -jmpDist) { - assert(emitIsUncondJump(id)); - if (J_DIST_SMALL_MAX_NEG <= -jmpDist) - { - /* This jump surely will be short */ - emitSetShortJump(id); - } + emitSetShortJump(id); } } @@ -1472,32 +1447,16 @@ void emitter::emitIns_J(instruction ins, BasicBlock* dst, int instrCount) void emitter::emitIns_J_cond_la(instruction ins, BasicBlock* dst, regNumber reg1, regNumber reg2) { - // TODO-RISCV64: - // Now the emitIns_J_cond_la() is only the short condition branch. - // There is no long condition branch for RISCV64 so far. - // For RISCV64 , the long condition branch is like this: - // ---> branch_condition condition_target; //here is the condition branch, short branch is enough. - // ---> jump jump_target; (this supporting the long jump.) - // condition_target: - // ... - // ... - // jump_target: - // - // - // INS_OPTS_J_cond: placeholders. 1-ins. - // ins reg1, reg2, dst - assert(dst != nullptr); assert(dst->HasFlag(BBF_HAS_LABEL)); + assert((ins != INS_bnez && ins != INS_beqz) || (reg2 == REG_ZERO)); instrDescJmp* id = emitNewInstrJmp(); - id->idIns(ins); id->idReg1(reg1); id->idReg2(reg2); id->idjShort = false; - - id->idInsOpt(INS_OPTS_J_cond); + assert(emitIsCmpJump(id)); id->idAddr()->iiaBBlabel = dst; id->idjKeepLong = emitComp->fgInDifferentRegions(emitComp->compCurBB, dst); @@ -1518,7 +1477,7 @@ void emitter::emitIns_J_cond_la(instruction ins, BasicBlock* dst, regNumber reg1 emitTotalIGjmps++; #endif - // TODO: estimate jump length, now we're starting from maximum size + // Start from worst case (3 instructions): "branch (reversed); auipc; jr" id->idCodeSize(3 * sizeof(code_t)); id->idInsOpt(INS_OPTS_JALR); @@ -1532,28 +1491,13 @@ void emitter::emitIns_J_cond_la(instruction ins, BasicBlock* dst, regNumber reg1 int jmpDist = srcOffs - tgt->igOffs; assert(jmpDist >= 0); - // TODO: separate, unconditional jumps only in emitIns_J, branches in emitIns_J_cond_la. - if (emitIsCmpJump(id)) + if (B_DIST_SMALL_MAX_NEG <= -jmpDist) { - if (B_DIST_SMALL_MAX_NEG <= -jmpDist) - { - /* This jump surely will be short */ - emitSetShortJump(id); - } - else if (J_DIST_SMALL_MAX_NEG <= -jmpDist) - { - /* This jump surely will be medium */ - emitSetMediumJump(id); - } + emitSetShortJump(id); } - else + else if (J_DIST_SMALL_MAX_NEG <= -jmpDist) { - assert(emitIsUncondJump(id)); - if (J_DIST_SMALL_MAX_NEG <= -jmpDist) - { - /* This jump surely will be short */ - emitSetShortJump(id); - } + emitSetMediumJump(id); } } diff --git a/src/coreclr/jit/emitriscv64.h b/src/coreclr/jit/emitriscv64.h index 4fe9a3623da643..5dc8caab110ca4 100644 --- a/src/coreclr/jit/emitriscv64.h +++ b/src/coreclr/jit/emitriscv64.h @@ -288,7 +288,7 @@ inline static bool isFloatReg(regNumber reg) /* Output target-independent instructions */ /************************************************************************/ -void emitIns_J(instruction ins, BasicBlock* dst, int instrCount = 0); +void emitIns_J(instruction ins, BasicBlock* dst); /************************************************************************/ /* The public entry points to output instructions */ From c054836a6ab6b6194a7b158be5dd99e8ba9a3f03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Thu, 9 Oct 2025 13:47:21 +0200 Subject: [PATCH 05/13] Remove branches from emitOutputInstr_OptsJ --- src/coreclr/jit/emitriscv64.cpp | 30 +++--------------------------- 1 file changed, 3 insertions(+), 27 deletions(-) diff --git a/src/coreclr/jit/emitriscv64.cpp b/src/coreclr/jit/emitriscv64.cpp index df6c242a54723f..62c88d2348b25e 100644 --- a/src/coreclr/jit/emitriscv64.cpp +++ b/src/coreclr/jit/emitriscv64.cpp @@ -3022,36 +3022,12 @@ BYTE* emitter::emitOutputInstr_OptsJ(BYTE* dst, instrDesc* id, const insGroup* i { const ssize_t immediate = emitOutputInstrJumpDistance(dst, ig, static_cast(id)); assert((immediate & 0x01) == 0); + assert(emitIsUncondJump(id)); *ins = id->idIns(); - switch (*ins) - { - case INS_jal: - dst += emitOutput_JTypeInstr(dst, INS_jal, REG_RA, TrimSignedToImm21(immediate)); - break; - case INS_j: - dst += emitOutput_JTypeInstr(dst, INS_j, REG_ZERO, TrimSignedToImm21(immediate)); - break; - case INS_jalr: - dst += emitOutput_ITypeInstr(dst, INS_jalr, id->idReg1(), id->idReg2(), TrimSignedToImm12(immediate)); - break; - case INS_bnez: - case INS_beqz: - dst += emitOutput_BTypeInstr(dst, *ins, id->idReg1(), REG_ZERO, TrimSignedToImm13(immediate)); - break; - case INS_beq: - case INS_bne: - case INS_blt: - case INS_bge: - case INS_bltu: - case INS_bgeu: - dst += emitOutput_BTypeInstr(dst, *ins, id->idReg1(), id->idReg2(), TrimSignedToImm13(immediate)); - break; - default: - unreached(); - break; - } + regNumber linkReg = (*ins == INS_jal) ? REG_RA : REG_ZERO; + dst += emitOutput_JTypeInstr(dst, *ins, linkReg, TrimSignedToImm21(immediate)); return dst; } From 966fecd762cb331b3ceefdd10e35a88be57eb820 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Thu, 9 Oct 2025 16:21:48 +0200 Subject: [PATCH 06/13] Remove INS_OPTS_(J|J_cond) --- src/coreclr/jit/emitriscv64.cpp | 97 +++++++++++++-------------------- src/coreclr/jit/emitriscv64.h | 4 +- src/coreclr/jit/instr.h | 4 +- 3 files changed, 41 insertions(+), 64 deletions(-) diff --git a/src/coreclr/jit/emitriscv64.cpp b/src/coreclr/jit/emitriscv64.cpp index 62c88d2348b25e..f9d669695d344d 100644 --- a/src/coreclr/jit/emitriscv64.cpp +++ b/src/coreclr/jit/emitriscv64.cpp @@ -74,9 +74,7 @@ size_t emitter::emitSizeOfInsDsc(instrDesc* id) const switch (insOp) { - case INS_OPTS_JALR: - case INS_OPTS_J_cond: - case INS_OPTS_J: + case INS_OPTS_JUMP: return sizeof(instrDescJmp); case INS_OPTS_C: @@ -1299,7 +1297,6 @@ void emitter::emitSetShortJump(instrDescJmp* id) assert(emitIsCmpJump(id) || emitIsUncondJump(id)); id->idCodeSize(sizeof(code_t)); // single 32-bit instruction id->idjShort = true; - id->idInsOpt(emitIsUncondJump(id) ? INS_OPTS_J : INS_OPTS_J_cond); #if DEBUG_EMIT if (id->idDebugOnlyInfo()->idNum == (unsigned)INTERESTING_JUMP_NUM || INTERESTING_JUMP_NUM == 0) @@ -1328,7 +1325,6 @@ void emitter::emitSetMediumJump(instrDescJmp* id) assert(emitIsCmpJump(id)); id->idCodeSize(2 * sizeof(code_t)); // two 32-bit instructions - id->idInsOpt(INS_OPTS_JALR); id->idjShort = false; } @@ -1424,7 +1420,7 @@ void emitter::emitIns_J(instruction ins, BasicBlock* dst) // Start from worst case (2 instructions): "auipc; jr" id->idCodeSize(2 * sizeof(code_t)); - id->idInsOpt(INS_OPTS_JALR); + id->idInsOpt(INS_OPTS_JUMP); /* Figure out the max. size of the jump/call instruction */ if (insGroup* tgt = (insGroup*)emitCodeGetCookie(dst); !id->idjKeepLong && (tgt != nullptr)) @@ -1479,7 +1475,7 @@ void emitter::emitIns_J_cond_la(instruction ins, BasicBlock* dst, regNumber reg1 // Start from worst case (3 instructions): "branch (reversed); auipc; jr" id->idCodeSize(3 * sizeof(code_t)); - id->idInsOpt(INS_OPTS_JALR); + id->idInsOpt(INS_OPTS_JUMP); /* Figure out the max. size of the jump/call instruction */ if (insGroup* tgt = (insGroup*)emitCodeGetCookie(dst); !id->idjKeepLong && (tgt != nullptr)) @@ -2975,62 +2971,56 @@ BYTE* emitter::emitOutputInstr_OptsRl(BYTE* dst, instrDesc* id, instruction* ins return dst; } -BYTE* emitter::emitOutputInstr_OptsJalr(BYTE* dst, instrDescJmp* jmp, const insGroup* ig, instruction* ins) +BYTE* emitter::emitOutputInstr_OptsJump(BYTE* dst, instrDescJmp* jmp, const insGroup* ig, instruction* ins) { - const ssize_t immediate = emitOutputInstrJumpDistance(dst, ig, jmp) - 4; + ssize_t immediate = emitOutputInstrJumpDistance(dst, ig, jmp); assert((immediate & 0x01) == 0); + assert(emitIsUncondJump(jmp) || emitIsCmpJump(jmp)); *ins = jmp->idIns(); - if (jmp->idInsIs(INS_jal, INS_j)) // far jump + if (jmp->idjShort) { - assert(jmp->idCodeSize() == 2 * sizeof(code_t)); - assert(isValidSimm32(immediate)); - dst += emitOutput_UTypeInstr(dst, INS_auipc, REG_RA, UpperNBitsOfWordSignExtend<20>(immediate)); - dst += emitOutput_ITypeInstr(dst, INS_jalr, REG_RA, REG_RA, LowerNBitsOfWord<12>(immediate)); - } - else // opposite branch + jump - { - assert(jmp->idInsIs(INS_beqz, INS_bnez, INS_beq, INS_bne, INS_blt, INS_bltu, INS_bge, INS_bgeu)); - regNumber reg2 = jmp->idInsIs(INS_beqz, INS_bnez) ? REG_R0 : jmp->idReg2(); - dst += emitOutput_BTypeInstr_InvertComparation(dst, jmp->idIns(), jmp->idReg1(), reg2, jmp->idCodeSize()); - if (jmp->idCodeSize() == 2 * sizeof(code_t)) + assert(jmp->idCodeSize() == sizeof(code_t)); + if (emitIsUncondJump(jmp)) { - dst += emitOutput_JTypeInstr(dst, INS_jal, REG_ZERO, TrimSignedToImm21(immediate)); + regNumber linkReg = (*ins == INS_jal) ? REG_RA : REG_ZERO; + dst += emitOutput_JTypeInstr(dst, *ins, linkReg, TrimSignedToImm21(immediate)); } else { - assert(jmp->idCodeSize() == 3 * sizeof(code_t)); + dst += emitOutput_BTypeInstr(dst, *ins, jmp->idReg1(), jmp->idReg2(), TrimSignedToImm13(immediate)); + } + } + else // far jump + { + if (emitIsUncondJump(jmp)) + { + assert(jmp->idCodeSize() == 2 * sizeof(code_t)); assert(isValidSimm32(immediate)); dst += emitOutput_UTypeInstr(dst, INS_auipc, REG_RA, UpperNBitsOfWordSignExtend<20>(immediate)); - dst += emitOutput_ITypeInstr(dst, INS_jalr, REG_ZERO, REG_RA, LowerNBitsOfWord<12>(immediate)); + dst += emitOutput_ITypeInstr(dst, INS_jalr, REG_RA, REG_RA, LowerNBitsOfWord<12>(immediate)); + } + else // opposite branch + jump + { + assert(!jmp->idInsIs(INS_beqz, INS_bnez) || (jmp->idReg2() == REG_ZERO)); + dst += emitOutput_BTypeInstr_InvertComparation(dst, *ins, jmp->idReg1(), jmp->idReg2(), jmp->idCodeSize()); + immediate -= sizeof(code_t); + if (jmp->idCodeSize() == 2 * sizeof(code_t)) + { + dst += emitOutput_JTypeInstr(dst, INS_jal, REG_ZERO, TrimSignedToImm21(immediate)); + } + else + { + assert(jmp->idCodeSize() == 3 * sizeof(code_t)); + assert(isValidSimm32(immediate)); + dst += emitOutput_UTypeInstr(dst, INS_auipc, REG_RA, UpperNBitsOfWordSignExtend<20>(immediate)); + dst += emitOutput_ITypeInstr(dst, INS_jalr, REG_ZERO, REG_RA, LowerNBitsOfWord<12>(immediate)); + } } } return dst; } -BYTE* emitter::emitOutputInstr_OptsJCond(BYTE* dst, instrDesc* id, const insGroup* ig, instruction* ins) -{ - const ssize_t immediate = emitOutputInstrJumpDistance(dst, ig, static_cast(id)); - - *ins = id->idIns(); - - dst += emitOutput_BTypeInstr(dst, *ins, id->idReg1(), id->idReg2(), TrimSignedToImm13(immediate)); - return dst; -} - -BYTE* emitter::emitOutputInstr_OptsJ(BYTE* dst, instrDesc* id, const insGroup* ig, instruction* ins) -{ - const ssize_t immediate = emitOutputInstrJumpDistance(dst, ig, static_cast(id)); - assert((immediate & 0x01) == 0); - assert(emitIsUncondJump(id)); - - *ins = id->idIns(); - - regNumber linkReg = (*ins == INS_jal) ? REG_RA : REG_ZERO; - dst += emitOutput_JTypeInstr(dst, *ins, linkReg, TrimSignedToImm21(immediate)); - return dst; -} - BYTE* emitter::emitOutputInstr_OptsC(BYTE* dst, instrDesc* id, const insGroup* ig, size_t* size) { if (id->idIsLargeCall()) @@ -3126,17 +3116,8 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) dst = emitOutputInstr_OptsRl(dst, id, &ins); sz = sizeof(instrDesc); break; - case INS_OPTS_JALR: - dst = emitOutputInstr_OptsJalr(dst, static_cast(id), ig, &ins); - sz = sizeof(instrDescJmp); - break; - case INS_OPTS_J_cond: - dst = emitOutputInstr_OptsJCond(dst, id, ig, &ins); - sz = sizeof(instrDescJmp); - break; - case INS_OPTS_J: - // jal/j/jalr/bnez/beqz/beq/bne/blt/bge/bltu/bgeu dstRW-relative. - dst = emitOutputInstr_OptsJ(dst, id, ig, &ins); + case INS_OPTS_JUMP: + dst = emitOutputInstr_OptsJump(dst, static_cast(id), ig, &ins); sz = sizeof(instrDescJmp); break; case INS_OPTS_C: diff --git a/src/coreclr/jit/emitriscv64.h b/src/coreclr/jit/emitriscv64.h index 5dc8caab110ca4..de011784504b0d 100644 --- a/src/coreclr/jit/emitriscv64.h +++ b/src/coreclr/jit/emitriscv64.h @@ -138,9 +138,7 @@ unsigned emitOutput_JTypeInstr(BYTE* dst, instruction ins, regNumber rd, unsigne BYTE* emitOutputInstr_OptsReloc(BYTE* dst, const instrDesc* id, instruction* ins); BYTE* emitOutputInstr_OptsRc(BYTE* dst, const instrDesc* id, instruction* ins); BYTE* emitOutputInstr_OptsRl(BYTE* dst, instrDesc* id, instruction* ins); -BYTE* emitOutputInstr_OptsJalr(BYTE* dst, instrDescJmp* jmp, const insGroup* ig, instruction* ins); -BYTE* emitOutputInstr_OptsJCond(BYTE* dst, instrDesc* id, const insGroup* ig, instruction* ins); -BYTE* emitOutputInstr_OptsJ(BYTE* dst, instrDesc* id, const insGroup* ig, instruction* ins); +BYTE* emitOutputInstr_OptsJump(BYTE* dst, instrDescJmp* jmp, const insGroup* ig, instruction* ins); BYTE* emitOutputInstr_OptsC(BYTE* dst, instrDesc* id, const insGroup* ig, size_t* size); BYTE* emitOutputInstr_OptsI(BYTE* dst, instrDesc* id, instruction* ins); diff --git a/src/coreclr/jit/instr.h b/src/coreclr/jit/instr.h index 15de2fd08f77bd..197e5b8cc41f86 100644 --- a/src/coreclr/jit/instr.h +++ b/src/coreclr/jit/instr.h @@ -553,9 +553,7 @@ enum insOpts : unsigned INS_OPTS_RC, // see ::emitIns_R_C(). INS_OPTS_RL, // see ::emitIns_R_L(). - INS_OPTS_JALR, // see ::emitIns_J_R(). - INS_OPTS_J, // see ::emitIns_J(). - INS_OPTS_J_cond, // see ::emitIns_J_cond_la(). + INS_OPTS_JUMP, // see ::emitIns_J and ::emitIns_J_cond_la(). INS_OPTS_I, // see ::emitLoadImmediate(). INS_OPTS_C, // see ::emitIns_Call(). INS_OPTS_RELOC, // see ::emitIns_R_AI(). From d3325a5daa6dcfd5c363df75a2842e627ea6b69c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Thu, 9 Oct 2025 16:39:08 +0200 Subject: [PATCH 07/13] Put link register in jal instructions as reg1 --- src/coreclr/jit/emitriscv64.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/coreclr/jit/emitriscv64.cpp b/src/coreclr/jit/emitriscv64.cpp index f9d669695d344d..b3e6c2f8480816 100644 --- a/src/coreclr/jit/emitriscv64.cpp +++ b/src/coreclr/jit/emitriscv64.cpp @@ -1389,6 +1389,7 @@ void emitter::emitIns_J(instruction ins, BasicBlock* dst) instrDescJmp* id = emitNewInstrJmp(); id->idIns(ins); + id->idReg1((ins == INS_jal) ? REG_RA : REG_ZERO); // link register assert(emitIsUncondJump(id)); id->idAddr()->iiaBBlabel = dst; @@ -2983,8 +2984,7 @@ BYTE* emitter::emitOutputInstr_OptsJump(BYTE* dst, instrDescJmp* jmp, const insG assert(jmp->idCodeSize() == sizeof(code_t)); if (emitIsUncondJump(jmp)) { - regNumber linkReg = (*ins == INS_jal) ? REG_RA : REG_ZERO; - dst += emitOutput_JTypeInstr(dst, *ins, linkReg, TrimSignedToImm21(immediate)); + dst += emitOutput_JTypeInstr(dst, *ins, jmp->idReg1(), TrimSignedToImm21(immediate)); } else { @@ -2997,8 +2997,10 @@ BYTE* emitter::emitOutputInstr_OptsJump(BYTE* dst, instrDescJmp* jmp, const insG { assert(jmp->idCodeSize() == 2 * sizeof(code_t)); assert(isValidSimm32(immediate)); - dst += emitOutput_UTypeInstr(dst, INS_auipc, REG_RA, UpperNBitsOfWordSignExtend<20>(immediate)); - dst += emitOutput_ITypeInstr(dst, INS_jalr, REG_RA, REG_RA, LowerNBitsOfWord<12>(immediate)); + regNumber linkReg = jmp->idReg1(); + regNumber tempReg = (linkReg == REG_ZERO) ? codeGen->rsGetRsvdReg() : linkReg; + dst += emitOutput_UTypeInstr(dst, INS_auipc, tempReg, UpperNBitsOfWordSignExtend<20>(immediate)); + dst += emitOutput_ITypeInstr(dst, INS_jalr, linkReg, tempReg, LowerNBitsOfWord<12>(immediate)); } else // opposite branch + jump { @@ -3013,8 +3015,9 @@ BYTE* emitter::emitOutputInstr_OptsJump(BYTE* dst, instrDescJmp* jmp, const insG { assert(jmp->idCodeSize() == 3 * sizeof(code_t)); assert(isValidSimm32(immediate)); - dst += emitOutput_UTypeInstr(dst, INS_auipc, REG_RA, UpperNBitsOfWordSignExtend<20>(immediate)); - dst += emitOutput_ITypeInstr(dst, INS_jalr, REG_ZERO, REG_RA, LowerNBitsOfWord<12>(immediate)); + regNumber tempReg = codeGen->rsGetRsvdReg(); + dst += emitOutput_UTypeInstr(dst, INS_auipc, tempReg, UpperNBitsOfWordSignExtend<20>(immediate)); + dst += emitOutput_ITypeInstr(dst, INS_jalr, REG_ZERO, tempReg, LowerNBitsOfWord<12>(immediate)); } } } From 53126d04de0f8037b496a9bf22acf0c6a6bfc4f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Thu, 9 Oct 2025 16:43:26 +0200 Subject: [PATCH 08/13] Remove emitIns_J_R --- src/coreclr/jit/emitriscv64.cpp | 6 +----- src/coreclr/jit/emitriscv64.h | 2 -- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/coreclr/jit/emitriscv64.cpp b/src/coreclr/jit/emitriscv64.cpp index b3e6c2f8480816..fef809c4f031ec 100644 --- a/src/coreclr/jit/emitriscv64.cpp +++ b/src/coreclr/jit/emitriscv64.cpp @@ -1377,11 +1377,6 @@ void emitter::emitIns_R_L(instruction ins, emitAttr attr, BasicBlock* dst, regNu appendToCurIG(id); } -void emitter::emitIns_J_R(instruction ins, emitAttr attr, BasicBlock* dst, regNumber reg) -{ - NYI_RISCV64("emitIns_J_R-----unimplemented/unused on RISCV64 yet----"); -} - void emitter::emitIns_J(instruction ins, BasicBlock* dst) { assert(dst != nullptr); @@ -1446,6 +1441,7 @@ void emitter::emitIns_J_cond_la(instruction ins, BasicBlock* dst, regNumber reg1 { assert(dst != nullptr); assert(dst->HasFlag(BBF_HAS_LABEL)); + assert((ins != INS_bnez && ins != INS_beqz) || (reg2 == REG_ZERO)); instrDescJmp* id = emitNewInstrJmp(); diff --git a/src/coreclr/jit/emitriscv64.h b/src/coreclr/jit/emitriscv64.h index de011784504b0d..b4440a8603bcf8 100644 --- a/src/coreclr/jit/emitriscv64.h +++ b/src/coreclr/jit/emitriscv64.h @@ -343,8 +343,6 @@ void emitIns_R_C(instruction ins, emitAttr attr, regNumber destReg, regNumber ad void emitIns_R_L(instruction ins, emitAttr attr, BasicBlock* dst, regNumber reg); -void emitIns_J_R(instruction ins, emitAttr attr, BasicBlock* dst, regNumber reg); - void emitIns_R_AR(instruction ins, emitAttr attr, regNumber ireg, regNumber reg, int offs); void emitIns_R_AI(instruction ins, From 5d2976af5a4e4ac152cfae626929f1f9cdd552d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Thu, 9 Oct 2025 17:09:40 +0200 Subject: [PATCH 09/13] Merge emitIns_J and emitIns_J_cond_la --- src/coreclr/jit/emitriscv64.cpp | 94 ++++++++++----------------------- src/coreclr/jit/emitriscv64.h | 24 ++++++--- 2 files changed, 44 insertions(+), 74 deletions(-) diff --git a/src/coreclr/jit/emitriscv64.cpp b/src/coreclr/jit/emitriscv64.cpp index fef809c4f031ec..31ca08235d3554 100644 --- a/src/coreclr/jit/emitriscv64.cpp +++ b/src/coreclr/jit/emitriscv64.cpp @@ -1379,77 +1379,28 @@ void emitter::emitIns_R_L(instruction ins, emitAttr attr, BasicBlock* dst, regNu void emitter::emitIns_J(instruction ins, BasicBlock* dst) { - assert(dst != nullptr); - assert(dst->HasFlag(BBF_HAS_LABEL)); - - instrDescJmp* id = emitNewInstrJmp(); - id->idIns(ins); - id->idReg1((ins == INS_jal) ? REG_RA : REG_ZERO); // link register - assert(emitIsUncondJump(id)); - id->idAddr()->iiaBBlabel = dst; - - if (emitComp->opts.compReloc) - { - id->idSetIsDspReloc(); - } - - id->idjShort = false; - - // TODO-RISCV64: maybe deleted this. - id->idjKeepLong = emitComp->fgInDifferentRegions(emitComp->compCurBB, dst); -#ifdef DEBUG - if (emitComp->opts.compLongAddress) // Force long branches - id->idjKeepLong = 1; -#endif // DEBUG - - /* Record the jump's IG and offset within it */ - id->idjIG = emitCurIG; - id->idjOffs = emitCurIGsize; - - /* Append this jump to this IG's jump list */ - id->idjNext = emitCurIGjmpList; - emitCurIGjmpList = id; - -#if EMITTER_STATS - emitTotalIGjmps++; -#endif - - // Start from worst case (2 instructions): "auipc; jr" - id->idCodeSize(2 * sizeof(code_t)); - id->idInsOpt(INS_OPTS_JUMP); - - /* Figure out the max. size of the jump/call instruction */ - if (insGroup* tgt = (insGroup*)emitCodeGetCookie(dst); !id->idjKeepLong && (tgt != nullptr)) - { - /* This is a backward jump - figure out the distance */ - UNATIVE_OFFSET srcOffs = emitCurCodeOffset + emitCurIGsize; - - /* Compute the distance estimate */ - int jmpDist = srcOffs - tgt->igOffs; - assert(jmpDist >= 0); - - if (J_DIST_SMALL_MAX_NEG <= -jmpDist) - { - emitSetShortJump(id); - } - } - - appendToCurIG(id); + assert(emitIsUncondJump(ins)); + regNumber linkReg = (ins == INS_jal) ? REG_RA : REG_ZERO; + emitIns_Jump(ins, dst, linkReg, REG_ZERO); } void emitter::emitIns_J_cond_la(instruction ins, BasicBlock* dst, regNumber reg1, regNumber reg2) +{ + assert(emitIsCmpJump(ins)); + assert((ins != INS_bnez && ins != INS_beqz) || (reg2 == REG_ZERO)); + emitIns_Jump(ins, dst, reg1, reg2); +} + +void emitter::emitIns_Jump(instruction ins, BasicBlock* dst, regNumber reg1, regNumber reg2) { assert(dst != nullptr); assert(dst->HasFlag(BBF_HAS_LABEL)); - assert((ins != INS_bnez && ins != INS_beqz) || (reg2 == REG_ZERO)); - instrDescJmp* id = emitNewInstrJmp(); id->idIns(ins); id->idReg1(reg1); id->idReg2(reg2); - id->idjShort = false; - assert(emitIsCmpJump(id)); + id->idjShort = false; id->idAddr()->iiaBBlabel = dst; id->idjKeepLong = emitComp->fgInDifferentRegions(emitComp->compCurBB, dst); @@ -1470,8 +1421,8 @@ void emitter::emitIns_J_cond_la(instruction ins, BasicBlock* dst, regNumber reg1 emitTotalIGjmps++; #endif - // Start from worst case (3 instructions): "branch (reversed); auipc; jr" - id->idCodeSize(3 * sizeof(code_t)); + // Start from the worst case: "[branch (reversed);] auipc; jr" + id->idCodeSize((emitIsCmpJump(id) ? 3 : 2) * sizeof(code_t)); id->idInsOpt(INS_OPTS_JUMP); /* Figure out the max. size of the jump/call instruction */ @@ -1484,13 +1435,24 @@ void emitter::emitIns_J_cond_la(instruction ins, BasicBlock* dst, regNumber reg1 int jmpDist = srcOffs - tgt->igOffs; assert(jmpDist >= 0); - if (B_DIST_SMALL_MAX_NEG <= -jmpDist) + if (emitIsCmpJump(id)) { - emitSetShortJump(id); + if (B_DIST_SMALL_MAX_NEG <= -jmpDist) + { + emitSetShortJump(id); + } + else if (J_DIST_SMALL_MAX_NEG <= -jmpDist - sizeof(code_t)) // the PC will be taken after the reversed + // branch + { + emitSetMediumJump(id); + } } - else if (J_DIST_SMALL_MAX_NEG <= -jmpDist) + else { - emitSetMediumJump(id); + if (J_DIST_SMALL_MAX_NEG <= -jmpDist) + { + emitSetShortJump(id); + } } } diff --git a/src/coreclr/jit/emitriscv64.h b/src/coreclr/jit/emitriscv64.h index b4440a8603bcf8..a66de99b999940 100644 --- a/src/coreclr/jit/emitriscv64.h +++ b/src/coreclr/jit/emitriscv64.h @@ -30,6 +30,7 @@ const char* emitVectorRegName(regNumber reg); #endif // DEBUG void emitIns_J_cond_la(instruction ins, BasicBlock* dst, regNumber reg1 = REG_R0, regNumber reg2 = REG_R0); +void emitIns_J(instruction ins, BasicBlock* dst); void emitLoadImmediate(emitAttr attr, regNumber reg, ssize_t imm); @@ -62,6 +63,8 @@ bool emitInsIsLoad(instruction ins); bool emitInsIsStore(instruction ins); bool emitInsIsLoadOrStore(instruction ins); +void emitIns_Jump(instruction ins, BasicBlock* dst, regNumber reg1 = REG_ZERO, regNumber reg2 = REG_ZERO); + // RVC emitters bool tryEmitCompressedIns_R_R_R( instruction ins, emitAttr attr, regNumber rd, regNumber rs1, regNumber rs2, insOpts opt); @@ -282,12 +285,6 @@ inline static bool isFloatReg(regNumber reg) return (reg >= REG_FP_FIRST && reg <= REG_FP_LAST); } -/************************************************************************/ -/* Output target-independent instructions */ -/************************************************************************/ - -void emitIns_J(instruction ins, BasicBlock* dst); - /************************************************************************/ /* The public entry points to output instructions */ /************************************************************************/ @@ -362,7 +359,13 @@ unsigned get_curTotalCodeSize(); // bytes of code // inline static bool emitIsCmpJump(instrDesc* jmp) { - return jmp->idInsIs(INS_beqz, INS_bnez, INS_bne, INS_beq, INS_blt, INS_bltu, INS_bge, INS_bgeu); + return emitIsCmpJump(jmp->idIns()); +} + +inline static bool emitIsCmpJump(instruction ins) +{ + return (ins == INS_beqz) || (ins == INS_bnez) || (ins == INS_bne) || (ins == INS_beq) || (ins == INS_blt) || + (ins == INS_bltu) || (ins == INS_bge) || (ins == INS_bgeu); } //------------------------------------------------------------------------ @@ -373,7 +376,12 @@ inline static bool emitIsCmpJump(instrDesc* jmp) // inline static bool emitIsUncondJump(instrDesc* jmp) { - return jmp->idInsIs(INS_j, INS_jal); + return emitIsUncondJump(jmp->idIns()); +} + +inline static bool emitIsUncondJump(instruction ins) +{ + return (ins == INS_j) || (ins == INS_jal); } #endif // TARGET_RISCV64 From 465540913f34367fdff487ffc5611002608e8220 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Fri, 10 Oct 2025 11:46:12 +0200 Subject: [PATCH 10/13] todo --- src/coreclr/jit/emit.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/coreclr/jit/emit.cpp b/src/coreclr/jit/emit.cpp index 594c43c9aa9801..99382755c1b2ec 100644 --- a/src/coreclr/jit/emit.cpp +++ b/src/coreclr/jit/emit.cpp @@ -5042,6 +5042,7 @@ void emitter::emitJumpDistBind() #ifdef TARGET_RISCV64 /* Figure out the smallest size we can end up with */ + // TODO-RISC64-RVC: add compressed branches and jumps if (emitIsCmpJump(jmp)) { ssz = sizeof(code_t); From 594f8349caf13c794a97a1830b69f346fe0f9af5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Mon, 13 Oct 2025 17:02:59 +0200 Subject: [PATCH 11/13] Move bounds of far branch --- src/coreclr/jit/emit.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/emit.cpp b/src/coreclr/jit/emit.cpp index 99382755c1b2ec..cb861db9b30387 100644 --- a/src/coreclr/jit/emit.cpp +++ b/src/coreclr/jit/emit.cpp @@ -5049,9 +5049,11 @@ void emitter::emitJumpDistBind() nsd = B_DIST_SMALL_MAX_NEG; psd = B_DIST_SMALL_MAX_POS; + // 2 instructions: "reverse cmp-and-branch; jal offset;" + // Move bounds to the right by 'ssz' to account for the reversed branch instruction size. msz = sizeof(code_t) * 2; - nmd = J_DIST_SMALL_MAX_NEG; - pmd = J_DIST_SMALL_MAX_POS; + nmd = J_DIST_SMALL_MAX_NEG + ssz; + pmd = J_DIST_SMALL_MAX_POS + ssz; } else if (emitIsUncondJump(jmp)) { From 573ca76d85680c5c12e1a2c79e76f63455502a4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Wed, 22 Oct 2025 15:26:09 +0200 Subject: [PATCH 12/13] jit code conv, remove unnecessary diff --- src/coreclr/jit/emitriscv64.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/coreclr/jit/emitriscv64.cpp b/src/coreclr/jit/emitriscv64.cpp index 3852d5178414bb..831e709e997c06 100644 --- a/src/coreclr/jit/emitriscv64.cpp +++ b/src/coreclr/jit/emitriscv64.cpp @@ -1400,7 +1400,10 @@ void emitter::emitIns_Jump(instruction ins, BasicBlock* dst, regNumber reg1, reg id->idIns(ins); id->idReg1(reg1); id->idReg2(reg2); - id->idjShort = false; + // Start from the worst case: "[branch (reversed);] auipc; jalr" + id->idjShort = false; + id->idCodeSize((emitIsCmpJump(id) ? 3 : 2) * sizeof(code_t)); + id->idInsOpt(INS_OPTS_JUMP); id->idAddr()->iiaBBlabel = dst; id->idjKeepLong = emitComp->fgInDifferentRegions(emitComp->compCurBB, dst); @@ -1421,12 +1424,9 @@ void emitter::emitIns_Jump(instruction ins, BasicBlock* dst, regNumber reg1, reg emitTotalIGjmps++; #endif - // Start from the worst case: "[branch (reversed);] auipc; jalr" - id->idCodeSize((emitIsCmpJump(id) ? 3 : 2) * sizeof(code_t)); - id->idInsOpt(INS_OPTS_JUMP); - /* Figure out the max. size of the jump/call instruction */ - if (insGroup* tgt = (insGroup*)emitCodeGetCookie(dst); !id->idjKeepLong && (tgt != nullptr)) + insGroup* tgt = (insGroup*)emitCodeGetCookie(dst); + if (!id->idjKeepLong && (tgt != nullptr)) { /* This is a backward jump - figure out the distance */ UNATIVE_OFFSET srcOffs = emitCurCodeOffset + emitCurIGsize; From 9d7b040bced32d75d8f931f4534ddd5d2263d778 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Wed, 22 Oct 2025 15:31:26 +0200 Subject: [PATCH 13/13] remove emitCounts_INS_OPTS_J --- src/coreclr/jit/emit.cpp | 2 +- src/coreclr/jit/emit.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/coreclr/jit/emit.cpp b/src/coreclr/jit/emit.cpp index cb861db9b30387..2aa394f5560bed 100644 --- a/src/coreclr/jit/emit.cpp +++ b/src/coreclr/jit/emit.cpp @@ -1306,7 +1306,7 @@ void emitter::emitBegFN(bool hasFramePtr emitFirstColdIG = nullptr; emitTotalCodeSize = 0; -#if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) +#if defined(TARGET_LOONGARCH64) emitCounts_INS_OPTS_J = 0; #endif diff --git a/src/coreclr/jit/emit.h b/src/coreclr/jit/emit.h index 398671531bfaa7..0b14c76ad2e969 100644 --- a/src/coreclr/jit/emit.h +++ b/src/coreclr/jit/emit.h @@ -2636,9 +2636,9 @@ class emitter #endif // defined(TARGET_X86) #endif // !defined(HOST_64BIT) -#if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) +#if defined(TARGET_LOONGARCH64) unsigned int emitCounts_INS_OPTS_J; -#endif // TARGET_LOONGARCH64 || TARGET_RISCV64 +#endif // TARGET_LOONGARCH64 instrDesc* emitFirstInstrDesc(BYTE* idData) const; void emitAdvanceInstrDesc(instrDesc** id, size_t idSize) const;