From a202e2281854429535a8e43e8b28495a2a969019 Mon Sep 17 00:00:00 2001 From: SingleAccretion Date: Fri, 13 Mar 2026 01:08:47 +0300 Subject: [PATCH 1/8] Hide 'igNum' --- src/coreclr/jit/emit.cpp | 203 +++++++++++++++------------- src/coreclr/jit/emit.h | 32 +++-- src/coreclr/jit/emitarm64.cpp | 2 +- src/coreclr/jit/emitloongarch64.cpp | 11 +- src/coreclr/jit/emitxarch.cpp | 7 +- 5 files changed, 137 insertions(+), 118 deletions(-) diff --git a/src/coreclr/jit/emit.cpp b/src/coreclr/jit/emit.cpp index 3eafb5c5bc9344..24080ab0acf29a 100644 --- a/src/coreclr/jit/emit.cpp +++ b/src/coreclr/jit/emit.cpp @@ -116,10 +116,35 @@ void emitLocation::Print(LONG compMethodID) const { unsigned insNum = emitGetInsNumFromCodePos(codePos); unsigned insOfs = emitGetInsOfsFromCodePos(codePos); - printf("(G_M%03u_IG%02u,ins#%d,ofs#%d)", compMethodID, ig->igNum, insNum, insOfs); + printf("(G_M%03u_IG%02u,ins#%d,ofs#%d)", compMethodID, ig->GetDisplayId(), insNum, insOfs); } #endif // DEBUG +void insGroup::InitializeNum(unsigned num) +{ + igNum = num; +} + +unsigned insGroup::GetDisplayId() const +{ + return igNum; +} + +bool insGroup::IsBefore(const insGroup* ig) const +{ + return igNum < ig->igNum; +} + +bool insGroup::IsBeforeOrEqual(const insGroup* ig) const +{ + return !IsAfter(ig); +} + +bool insGroup::IsAfter(const insGroup* ig) const +{ + return ig->IsBefore(this); +} + /***************************************************************************** * * Return the name of an instruction format. @@ -788,9 +813,6 @@ void emitter::emitGenIG(insGroup* ig) { IMPL_LIMITATION("Too many arguments pushed on stack"); } - - // printf("Start IG #%02u [stk=%02u]\n", ig->igNum, emitCurStackLvl); - #endif if (emitNoGCIG) @@ -1223,9 +1245,9 @@ void emitter::emitBegFN(bool hasFramePtr emitIGbuffSize = 0; #if FEATURE_LOOP_ALIGN - emitLastAlignedIgNum = 0; - emitLastLoopStart = 0; - emitLastLoopEnd = 0; + emitLastAlignedIG = nullptr; + emitLastLoopStart = nullptr; + emitLastLoopEnd = nullptr; #endif /* Record stack frame info (the temp size is just an estimate) */ @@ -1824,7 +1846,7 @@ void emitter::emitCheckIGList() if (currIG->igOffs != currentOffset) { - printf("IG%02u has offset %08X, expected %08X\n", currIG->igNum, currIG->igOffs, currentOffset); + printf("IG%02u has offset %08X, expected %08X\n", currIG->GetDisplayId(), currIG->igOffs, currentOffset); assert(!"bad block offset"); } @@ -2900,7 +2922,7 @@ void* emitter::emitAddInlineLabel() // void emitter::emitPrintLabel(const insGroup* ig) const { - printf("G_M%03u_IG%02u", m_compiler->compMethodID, ig->igNum); + printf("G_M%03u_IG%02u", m_compiler->compMethodID, ig->GetDisplayId()); } //----------------------------------------------------------------------------- @@ -2918,7 +2940,7 @@ const char* emitter::emitLabelString(const insGroup* ig) const static char buf[4][TEMP_BUFFER_LEN]; const char* retbuf; - sprintf_s(buf[curBuf], TEMP_BUFFER_LEN, "G_M%03u_IG%02u", m_compiler->compMethodID, ig->igNum); + sprintf_s(buf[curBuf], TEMP_BUFFER_LEN, "G_M%03u_IG%02u", m_compiler->compMethodID, ig->GetDisplayId()); retbuf = buf[curBuf]; curBuf = (curBuf + 1) % 4; return retbuf; @@ -3005,7 +3027,7 @@ void emitter::emitSplit(emitLocation* startLoc, { #ifdef DEBUG if (EMITVERBOSE) - printf("emitSplit: can't split at IG%02u; we don't have a candidate to report\n", ig->igNum); + printf("emitSplit: can't split at IG%02u; we don't have a candidate to report\n", ig->GetDisplayId()); #endif return; } @@ -3016,7 +3038,7 @@ void emitter::emitSplit(emitLocation* startLoc, { #ifdef DEBUG if (EMITVERBOSE) - printf("emitSplit: can't split at IG%02u; we already reported it\n", igLastCandidate->igNum); + printf("emitSplit: can't split at IG%02u; we already reported it\n", igLastCandidate->GetDisplayId()); #endif return; } @@ -3029,7 +3051,7 @@ void emitter::emitSplit(emitLocation* startLoc, { #ifdef DEBUG if (EMITVERBOSE) - printf("emitSplit: can't split at IG%02u; zero-sized candidate\n", igLastCandidate->igNum); + printf("emitSplit: can't split at IG%02u; zero-sized candidate\n", igLastCandidate->GetDisplayId()); #endif return; } @@ -3040,7 +3062,7 @@ void emitter::emitSplit(emitLocation* startLoc, if (EMITVERBOSE) { printf("emitSplit: split at IG%02u is size %x, %s than requested maximum size of %x\n", - igLastCandidate->igNum, candidateSize, (candidateSize >= maxSplitSize) ? "larger" : "less", + igLastCandidate->GetDisplayId(), candidateSize, (candidateSize >= maxSplitSize) ? "larger" : "less", maxSplitSize); } #endif @@ -3089,7 +3111,7 @@ void emitter::emitSplit(emitLocation* startLoc, if ((igLastCandidate != nullptr) && (curSize == candidateSize)) { JITDUMP("emitSplit: can't split at last candidate IG%02u because it would create a zero-sized fragment\n", - igLastCandidate->igNum); + igLastCandidate->GetDisplayId()); } else { @@ -4060,7 +4082,7 @@ void emitter::emitDispIG(insGroup* ig, bool displayFunc, bool displayInstruction printf("%s placeholder, next placeholder=", pszType); if (igPh->igPhData->igPhNext) { - printf("IG%02u ", igPh->igPhData->igPhNext->igNum); + printf("IG%02u ", igPh->igPhData->igPhNext->GetDisplayId()); } else { @@ -4162,7 +4184,7 @@ void emitter::emitDispIG(insGroup* ig, bool displayFunc, bool displayInstruction #if FEATURE_LOOP_ALIGN if (ig->igLoopBackEdge != nullptr) { - printf("%sloop=IG%02u", separator, ig->igLoopBackEdge->igNum); + printf("%sloop=IG%02u", separator, ig->igLoopBackEdge->GetDisplayId()); separator = ", "; } #endif // FEATURE_LOOP_ALIGN @@ -4283,7 +4305,7 @@ void emitter::emitDispJumpList() unsigned int jmpCount = 0; for (instrDescJmp* jmp = emitJumpList; jmp != nullptr; jmp = jmp->idjNext) { - printf("IG%02u IN%04x %3s[%u]", jmp->idjIG->igNum, jmp->idDebugOnlyInfo()->idNum, + printf("IG%02u IN%04x %3s[%u]", jmp->idjIG->GetDisplayId(), jmp->idDebugOnlyInfo()->idNum, codeGen->genInsDisplayName(jmp), jmp->idCodeSize()); if (!jmp->idIsBound()) @@ -4304,7 +4326,7 @@ void emitter::emitDispJumpList() } else { - printf(" -> IG%02u", targetGroup->igNum); + printf(" -> IG%02u", targetGroup->GetDisplayId()); } } @@ -4426,7 +4448,7 @@ size_t emitter::emitIssue1Instr(insGroup* ig, instrDesc* id, BYTE** dp) #if FEATURE_LOOP_ALIGN // Should never over-estimate align instruction or any instruction before the last align instruction of a method - assert(id->idIns() != INS_align && emitCurIG->igNum > emitLastAlignedIgNum); + assert(id->idIns() != INS_align && ((emitLastAlignedIG == nullptr) || emitCurIG->IsAfter(emitLastAlignedIG))); #endif #if DEBUG_EMIT @@ -4642,8 +4664,8 @@ void emitter::emitRemoveJumpToNextInst() instrDescJmp* jmp = emitJumpList; instrDescJmp* previousJmp = nullptr; #if DEBUG - UNATIVE_OFFSET previousJumpIgNum = (UNATIVE_OFFSET)-1; - unsigned int previousJumpInsNum = -1; + insGroup* previousJumpIG = nullptr; + unsigned int previousJumpInsNum = -1; #endif // DEBUG while (jmp) @@ -4656,9 +4678,9 @@ void emitter::emitRemoveJumpToNextInst() #if DEBUG assert(jmp->idInsFmt() == IF_LABEL); assert(emitIsUncondJump(jmp)); - assert((jmpGroup->igNum > previousJumpIgNum) || (previousJumpIgNum == (UNATIVE_OFFSET)-1) || - ((jmpGroup->igNum == previousJumpIgNum) && (jmp->idDebugOnlyInfo()->idNum > previousJumpInsNum))); - previousJumpIgNum = jmpGroup->igNum; + assert((previousJumpIG == nullptr) || jmpGroup->IsAfter(previousJumpIG) || + ((jmpGroup == previousJumpIG) && (jmp->idDebugOnlyInfo()->idNum > previousJumpInsNum))); + previousJumpIG = jmpGroup; previousJumpInsNum = jmp->idDebugOnlyInfo()->idNum; #endif // DEBUG @@ -4700,7 +4722,7 @@ void emitter::emitRemoveJumpToNextInst() JITDUMP("IG%02u IN%04x is the last instruction in the group and jumps to the next instruction group " "IG%02u %s, removing.\n", - jmpGroup->igNum, jmp->idDebugOnlyInfo()->idNum, targetGroup->igNum, + jmpGroup->GetDisplayId(), jmp->idDebugOnlyInfo()->idNum, targetGroup->GetDisplayId(), emitLabelString(targetGroup)); #endif // DEBUG @@ -4757,24 +4779,24 @@ void emitter::emitRemoveJumpToNextInst() #if DEBUG if (targetGroup == nullptr) { - JITDUMP("IG%02u IN%04x jump target is not set!, keeping.\n", jmpGroup->igNum, + JITDUMP("IG%02u IN%04x jump target is not set!, keeping.\n", jmpGroup->GetDisplayId(), jmp->idDebugOnlyInfo()->idNum); } else if (jmpGroup->igNext != targetGroup) { - JITDUMP("IG%02u IN%04x does not jump to the next instruction group, keeping.\n", jmpGroup->igNum, - jmp->idDebugOnlyInfo()->idNum); + JITDUMP("IG%02u IN%04x does not jump to the next instruction group, keeping.\n", + jmpGroup->GetDisplayId(), jmp->idDebugOnlyInfo()->idNum); } else if ((jmpGroup->igFlags & IGF_HAS_REMOVABLE_JMP) == 0) { JITDUMP("IG%02u IN%04x containing instruction group is not marked with IGF_HAS_REMOVABLE_JMP, " "keeping.\n", - jmpGroup->igNum, jmp->idDebugOnlyInfo()->idNum); + jmpGroup->GetDisplayId(), jmp->idDebugOnlyInfo()->idNum); } else if (jmpGroup->endsWithAlignInstr()) { - JITDUMP("IG%02u IN%04x containing instruction group has alignment, keeping.\n", jmpGroup->igNum, - jmp->idDebugOnlyInfo()->idNum); + JITDUMP("IG%02u IN%04x containing instruction group has alignment, keeping.\n", + jmpGroup->GetDisplayId(), jmp->idDebugOnlyInfo()->idNum); } #endif // DEBUG } @@ -4789,9 +4811,9 @@ void emitter::emitRemoveJumpToNextInst() { insGroup* adjOffIG = jmpGroup->igNext; insGroup* adjOffUptoIG = nextJmp != nullptr ? nextJmp->idjIG : emitIGlast; - while ((adjOffIG != nullptr) && (adjOffIG->igNum <= adjOffUptoIG->igNum)) + while ((adjOffIG != nullptr) && adjOffIG->IsBeforeOrEqual(adjOffUptoIG)) { - JITDUMP("Adjusted offset of IG%02u from %04X to %04X\n", adjOffIG->igNum, adjOffIG->igOffs, + JITDUMP("Adjusted offset of IG%02u from %04X to %04X\n", adjOffIG->GetDisplayId(), adjOffIG->igOffs, (adjOffIG->igOffs - totalRemovedSize)); adjOffIG->igOffs -= totalRemovedSize; adjOffIG = adjOffIG->igNext; @@ -5062,8 +5084,7 @@ void emitter::emitJumpDistBind() assert(lastLJ == nullptr || lastIG != jmp->idjIG || lastLJ->idjOffs < jmp->idjOffs); lastLJ = (lastIG == jmp->idjIG) ? jmp : nullptr; - assert(lastIG == nullptr || lastIG->igNum <= jmp->idjIG->igNum || jmp->idjIG == prologIG || - emitNxtIGnum > unsigned(0xFFFF)); // igNum might overflow + assert(lastIG == nullptr || lastIG->IsBeforeOrEqual(jmp->idjIG) || jmp->idjIG == prologIG); lastIG = jmp->idjIG; #endif // DEBUG @@ -5092,8 +5113,8 @@ void emitter::emitJumpDistBind() #ifdef DEBUG if (EMITVERBOSE) { - printf("Adjusted offset of " FMT_BB " from %04X to %04X\n", lstIG->igNum, lstIG->igOffs, - lstIG->igOffs - adjIG); + printf("Adjusted offset of " FMT_BB " from %04X to %04X\n", lstIG->GetDisplayId(), + lstIG->igOffs, lstIG->igOffs - adjIG); } #endif // DEBUG lstIG->igOffs -= adjIG; @@ -5268,7 +5289,7 @@ void emitter::emitJumpDistBind() srcEncodingOffs = srcInstrOffs + ssz; // Encoding offset of relative offset for small branch #endif - if (jmpIG->igNum < tgtIG->igNum) + if (jmpIG->IsBefore(tgtIG)) { /* Forward jump */ @@ -5386,7 +5407,7 @@ void emitter::emitJumpDistBind() if (emitIsCmpJump(jmp)) { - if (jmpIG->igNum < tgtIG->igNum) + if (jmpIG->IsBefore(tgtIG)) { /* Forward jump */ @@ -5577,7 +5598,7 @@ void emitter::emitJumpDistBind() #ifdef DEBUG if (EMITVERBOSE) { - printf("Adjusted offset of " FMT_BB " from %04X to %04X\n", lstIG->igNum, lstIG->igOffs, + printf("Adjusted offset of " FMT_BB " from %04X to %04X\n", lstIG->GetDisplayId(), lstIG->igOffs, lstIG->igOffs - adjIG); } #endif // DEBUG @@ -5781,8 +5802,8 @@ void emitter::emitLongLoopAlign(unsigned alignmentBoundary DEBUG_ARG(bool isPlac // void emitter::emitConnectAlignInstrWithCurIG() { - JITDUMP("Mapping 'align' instruction in IG%02u to target IG%02u\n", emitAlignLastGroup->idaIG->igNum, - emitCurIG->igNum); + JITDUMP("Mapping 'align' instruction in IG%02u to target IG%02u\n", emitAlignLastGroup->idaIG->GetDisplayId(), + emitCurIG->GetDisplayId()); // Since we never align overlapping instructions, it is always guaranteed that // the emitAlignLastGroup points to the loop that is in process of getting aligned. @@ -5855,17 +5876,15 @@ bool emitter::emitEndsWithAlignInstr() // isAlignAdjusted - DEBUG only. Determine if adjustments are done to the align instructions or not. // During generating code, it is 'false' (because we haven't adjusted the size yet). // During outputting code, it is 'true'. -// containingIGNum - DEBUG only. IG number of IG that contains the current align instruction we are processing. -// loopHeadPredIGNum - DEBUG only. IG number of IG that precedes the IG that we are aligning with current align +// containingIG - DEBUG only. IG that contains the current align instruction we are processing. +// loopHeadPredIG - DEBUG only. IG that precedes the IG that we are aligning with current align // instruction. // // Returns: size of a loop in bytes. // -unsigned emitter::getLoopSize(insGroup* igLoopHeader, - unsigned maxLoopSize // - DEBUG_ARG(bool isAlignAdjusted) // - DEBUG_ARG(UNATIVE_OFFSET containingIGNum) // - DEBUG_ARG(UNATIVE_OFFSET loopHeadPredIGNum)) +unsigned emitter::getLoopSize(insGroup* igLoopHeader, + unsigned maxLoopSize DEBUGARG(bool isAlignAdjusted) DEBUGARG(insGroup* containingIG) + DEBUGARG(insGroup* loopHeadPredIG)) { unsigned loopSize = 0; @@ -5922,12 +5941,12 @@ unsigned emitter::getLoopSize(insGroup* igLoopHeader, { char buffer[5000]; int written = sprintf_s(buffer, 35, "Mismatch in align instruction.\n"); - written += sprintf_s(buffer + written, 100, "Containing IG: IG%02u\n", containingIGNum); - written += sprintf_s(buffer + written, 100, "loopHeadPredIG: IG%02u\n", loopHeadPredIGNum); - written += sprintf_s(buffer + written, 100, "loopHeadIG: IG%02u\n", igLoopHeader->igNum); - written += sprintf_s(buffer + written, 100, "igInLoop: IG%02u\n", igInLoop->igNum); + written += sprintf_s(buffer + written, 100, "Containing IG: IG%02u\n", containingIG->GetDisplayId()); + written += sprintf_s(buffer + written, 100, "loopHeadPredIG: IG%02u\n", loopHeadPredIG->GetDisplayId()); + written += sprintf_s(buffer + written, 100, "loopHeadIG: IG%02u\n", igLoopHeader->GetDisplayId()); + written += sprintf_s(buffer + written, 100, "igInLoop: IG%02u\n", igInLoop->GetDisplayId()); written += sprintf_s(buffer + written, 100, "igInLoop->igLoopBackEdge: IG%02u\n", - igInLoop->igLoopBackEdge->igNum); + igInLoop->igLoopBackEdge->GetDisplayId()); #if EMIT_BACKWARDS_NAVIGATION if (igInLoop->endsWithAlignInstr()) @@ -5936,7 +5955,7 @@ unsigned emitter::getLoopSize(insGroup* igLoopHeader, instrDescAlign* alignInstr = (instrDescAlign*)igInLoop->igLastIns; assert(alignInstr->idaIG == igInLoop); written += sprintf_s(buffer + written, 100, "igInLoop has align instruction for : IG%02u\n", - alignInstr->idaLoopHeadPredIG->igNext->igNum); + alignInstr->idaLoopHeadPredIG->igNext->GetDisplayId()); } #endif // EMIT_BACKWARDS_NAVIGATION @@ -5945,12 +5964,12 @@ unsigned emitter::getLoopSize(insGroup* igLoopHeader, for (igIter = igLoopHeader; (igIter != nullptr) && (igIter->igLoopBackEdge != igLoopHeader); igIter = igIter->igNext) { - written += sprintf_s(buffer + written, 100, "\tIG%02u\n", igIter->igNum); + written += sprintf_s(buffer + written, 100, "\tIG%02u\n", igIter->GetDisplayId()); } if (igIter == nullptr) { written += sprintf_s(buffer + written, 100, "Did not find IG with back edge to IG%02u\n", - igLoopHeader->igNum); + igLoopHeader->GetDisplayId()); } printf("\n\n%s", buffer); assert(false && !"Mismatch in align instruction"); @@ -5965,7 +5984,7 @@ unsigned emitter::getLoopSize(insGroup* igLoopHeader, // Find the alignInstr for igInLoop IG. for (; alignInstr != nullptr; alignInstr = alignInstr->idaNext) { - if (alignInstr->idaIG->igNum == igInLoop->igNum) + if (alignInstr->idaIG == igInLoop) { foundAlignInstr = true; break; @@ -6057,11 +6076,11 @@ bool emitter::emitSetLoopBackEdge(const BasicBlock* loopTopBlock) return false; } - if (dstIG->igNum > emitCurIG->igNum) + if (dstIG->IsAfter(emitCurIG)) { // Is this possible? - JITDUMP("ALIGN: found forward branch from IG%02u to IG%02u; not marking IG back edge.\n", emitCurIG->igNum, - dstIG->igNum); + JITDUMP("ALIGN: found forward branch from IG%02u to IG%02u; not marking IG back edge.\n", + emitCurIG->GetDisplayId(), dstIG->GetDisplayId()); return false; } @@ -6069,17 +6088,18 @@ bool emitter::emitSetLoopBackEdge(const BasicBlock* loopTopBlock) bool alignCurrentLoop = true; bool alignLastLoop = true; - unsigned currLoopStart = dstIG->igNum; - unsigned currLoopEnd = emitCurIG->igNum; + insGroup* currLoopStart = dstIG; + insGroup* currLoopEnd = emitCurIG; // Only mark back-edge if current loop starts after the last inner loop ended. - if (emitLastLoopEnd < currLoopStart) + if ((emitLastLoopEnd == nullptr) || emitLastLoopEnd->IsBefore(currLoopStart)) { assert(emitCurIG->igLoopBackEdge == nullptr); emitCurIG->igLoopBackEdge = dstIG; backEdgeSet = true; - JITDUMP("** IG%02u jumps back to IG%02u forming a loop.\n", currLoopEnd, currLoopStart); + JITDUMP("** IG%02u jumps back to IG%02u forming a loop.\n", currLoopEnd->GetDisplayId(), + currLoopStart->GetDisplayId()); emitLastLoopStart = currLoopStart; emitLastLoopEnd = currLoopEnd; @@ -6097,13 +6117,13 @@ bool emitter::emitSetLoopBackEdge(const BasicBlock* loopTopBlock) // |-----. // } - else if ((currLoopStart < emitLastLoopStart) && (emitLastLoopEnd < currLoopEnd)) + else if (currLoopStart->IsBefore(emitLastLoopStart) && emitLastLoopEnd->IsBefore(currLoopEnd)) { // if current loop completely encloses last loop, // then current loop should not be aligned. alignCurrentLoop = false; } - else if ((emitLastLoopStart < currLoopStart) && (currLoopEnd < emitLastLoopEnd)) + else if (emitLastLoopStart->IsBefore(currLoopStart) && currLoopEnd->IsBefore(emitLastLoopEnd)) { // if last loop completely encloses current loop, // then last loop should not be aligned. @@ -6137,12 +6157,13 @@ bool emitter::emitSetLoopBackEdge(const BasicBlock* loopTopBlock) markedCurrLoop = true; JITDUMP(";; Skip alignment for current loop IG%02u ~ IG%02u because it encloses an aligned loop " "IG%02u ~ IG%02u.\n", - currLoopStart, currLoopEnd, emitLastLoopStart, emitLastLoopEnd); + currLoopStart->GetDisplayId(), currLoopEnd->GetDisplayId(), emitLastLoopStart->GetDisplayId(), + emitLastLoopEnd->GetDisplayId()); } // Find the IG that has 'align' instruction to align the last loop // and clear the IGF_HAS_ALIGN flag. - if (!alignLastLoop && (loopHeadIG != nullptr) && (loopHeadIG->igNum == emitLastLoopStart)) + if (!alignLastLoop && (loopHeadIG != nullptr) && (loopHeadIG == emitLastLoopStart)) { assert(!markedLastLoop); assert(alignInstr->idaIG->endsWithAlignInstr() || alignInstr->idaIG->hadAlignInstr()); @@ -6153,7 +6174,8 @@ bool emitter::emitSetLoopBackEdge(const BasicBlock* loopTopBlock) markedLastLoop = true; JITDUMP(";; Skip alignment for aligned loop IG%02u ~ IG%02u because it encloses the current loop " "IG%02u ~ IG%02u.\n", - emitLastLoopStart, emitLastLoopEnd, currLoopStart, currLoopEnd); + emitLastLoopStart->GetDisplayId(), emitLastLoopEnd->GetDisplayId(), + currLoopStart->GetDisplayId(), currLoopEnd->GetDisplayId()); } if (markedLastLoop && markedCurrLoop) @@ -6209,8 +6231,8 @@ void emitter::emitLoopAlignAdjustments() insGroup* loopHeadIG = alignInstr->loopHeadIG(); insGroup* containingIG = alignInstr->idaIG; - JITDUMP(" Adjusting 'align' instruction in IG%02u that is targeted for IG%02u \n", containingIG->igNum, - loopHeadIG->igNum); + JITDUMP(" Adjusting 'align' instruction in IG%02u that is targeted for IG%02u \n", + containingIG->GetDisplayId(), loopHeadIG->GetDisplayId()); // Since we only adjust the padding up to the next align instruction which is behind the jump, we make sure // that we take into account all the alignBytes we removed until that point. Hence " - alignBytesRemoved" @@ -6225,9 +6247,8 @@ void emitter::emitLoopAlignAdjustments() unsigned actualPaddingNeeded = containingIG->endsWithAlignInstr() - ? emitCalculatePaddingForLoopAlignment(loopHeadIG, - loopIGOffset DEBUG_ARG(false) DEBUG_ARG(containingIG->igNum) - DEBUG_ARG(loopHeadPredIG->igNum)) + ? emitCalculatePaddingForLoopAlignment(loopHeadIG, loopIGOffset DEBUGARG(false) DEBUGARG(containingIG) + DEBUGARG(loopHeadPredIG)) : 0; assert(estimatedPaddingNeeded >= actualPaddingNeeded); @@ -6307,7 +6328,7 @@ void emitter::emitLoopAlignAdjustments() insGroup* adjOffIG = containingIG->igNext; instrDescAlign* nextAlign = emitAlignInNextIG(alignInstr); insGroup* adjOffUptoIG = nextAlign != nullptr ? nextAlign->idaIG : emitIGlast; - while ((adjOffIG != nullptr) && (adjOffIG->igNum <= adjOffUptoIG->igNum)) + while ((adjOffIG != nullptr) && adjOffIG->IsBeforeOrEqual(adjOffUptoIG)) { JITDUMP("Adjusted offset of %s from %04X to %04X\n", emitLabelString(adjOffIG), adjOffIG->igOffs, (adjOffIG->igOffs - alignBytesRemoved)); @@ -6320,9 +6341,9 @@ void emitter::emitLoopAlignAdjustments() if (actualPaddingNeeded > 0) { // Record the last loop IG that will be aligned. No overestimation - // adjustment will be done after emitLastAlignedIgNum. + // adjustment will be done after emitLastAlignedIG. JITDUMP("Recording last aligned IG: %s\n", emitLabelString(loopHeadPredIG)); - emitLastAlignedIgNum = loopHeadPredIG->igNum; + emitLastAlignedIG = loopHeadPredIG; } } @@ -6341,8 +6362,8 @@ void emitter::emitLoopAlignAdjustments() // isAlignAdjusted - Determine if adjustments are done to the align instructions or not. // During generating code, it is 'false' (because we haven't adjusted the size yet). // During outputting code, it is 'true'. -// containingIGNum - IG number of IG that contains the current align instruction we are processing. -// loopHeadPredIGNum - IG number of IG that preceds the IG that we are aligning with current align instruction. +// containingIG - IG that contains the current align instruction we are processing. +// loopHeadPredIG - IG that preceds the IG that we are aligning with current align instruction. // // Returns: Padding amount. // 0 means no padding is needed, either because loop is already aligned or it @@ -6367,9 +6388,9 @@ void emitter::emitLoopAlignAdjustments() // 3c. return paddingNeeded. // unsigned emitter::emitCalculatePaddingForLoopAlignment(insGroup* loopHeadIG, - size_t offset DEBUG_ARG(bool isAlignAdjusted) - DEBUG_ARG(UNATIVE_OFFSET containingIGNum) - DEBUG_ARG(UNATIVE_OFFSET loopHeadPredIGNum)) + size_t offset DEBUGARG(bool isAlignAdjusted) + DEBUGARG(insGroup* containingIG) + DEBUGARG(insGroup* loopHeadPredIG)) { unsigned alignmentBoundary = m_compiler->opts.compJitAlignLoopBoundary; @@ -6396,8 +6417,8 @@ unsigned emitter::emitCalculatePaddingForLoopAlignment(insGroup* loopHeadIG, maxLoopSize = m_compiler->opts.compJitAlignLoopMaxCodeSize; } - unsigned loopSize = getLoopSize(loopHeadIG, maxLoopSize DEBUG_ARG(isAlignAdjusted) DEBUG_ARG(containingIGNum) - DEBUG_ARG(loopHeadPredIGNum)); + unsigned loopSize = + getLoopSize(loopHeadIG, maxLoopSize DEBUGARG(isAlignAdjusted) DEBUGARG(containingIG) DEBUGARG(loopHeadPredIG)); // No padding if loop is big if (loopSize > maxLoopSize) @@ -7162,12 +7183,6 @@ unsigned emitter::emitEndCodeGen(Compiler* comp, #endif } - /* Are we overflowing? */ - if (ig->igNext && (ig->igNum + 1 != ig->igNext->igNum)) - { - NO_WAY("Too many instruction groups"); - } - instrDesc* id = emitFirstInstrDesc(ig->igData); #ifdef DEBUG @@ -9434,8 +9449,6 @@ UNATIVE_OFFSET emitter::emitCodeOffset(void* blockPtr, unsigned codePos) of = emitGetInsOfsFromCodePos(codePos); - // printf("[IG=%02u;ID=%03u;OF=%04X] <= %08X\n", ig->igNum, emitGetInsNumFromCodePos(codePos), of, codePos); - /* Make sure the offset estimate is accurate */ assert(of == emitFindOffset(ig, emitGetInsNumFromCodePos(codePos))); } @@ -9790,7 +9803,7 @@ void emitter::emitInitIG(insGroup* ig) { /* Assign the next available index to the instruction group */ - ig->igNum = emitNxtIGnum; + ig->InitializeNum(emitNxtIGnum); emitNxtIGnum++; diff --git a/src/coreclr/jit/emit.h b/src/coreclr/jit/emit.h index f674d75f0dd0c4..5f7a370af10317 100644 --- a/src/coreclr/jit/emit.h +++ b/src/coreclr/jit/emit.h @@ -280,7 +280,10 @@ struct insGroup size_t igDataSize; // size of instrDesc data pointed to by 'igData' #endif - UNATIVE_OFFSET igNum; // for ordering (and display) purposes +private: + unsigned igNum; // for ordering (and display) purposes + +public: UNATIVE_OFFSET igOffs; // offset of this group within method unsigned int igFuncIdx; // Which function/funclet does this belong to? (Index into Compiler::compFuncInfos array.) unsigned short igFlags; // see IGF_xxx below @@ -390,6 +393,11 @@ struct insGroup return (igFlags & IGF_REMOVED_ALIGN) != 0; } + void InitializeNum(unsigned num); + unsigned GetDisplayId() const; + bool IsBefore(const insGroup* ig) const; + bool IsBeforeOrEqual(const insGroup* ig) const; + bool IsAfter(const insGroup* ig) const; }; // end of struct insGroup // For AMD64 the maximum prolog/epilog size supported on the OS is 256 bytes @@ -2866,28 +2874,28 @@ class emitter void emitRemoveJumpToNextInst(); // try to remove unconditional jumps to the next instruction #if FEATURE_LOOP_ALIGN - instrDescAlign* emitCurIGAlignList; // list of align instructions in current IG - unsigned emitLastLoopStart; // Start IG of last inner loop - unsigned emitLastLoopEnd; // End IG of last inner loop - unsigned emitLastAlignedIgNum; // last IG that has align instruction - instrDescAlign* emitAlignList; // list of all align instructions in method - instrDescAlign* emitAlignLast; // last align instruction in method + instrDescAlign* emitCurIGAlignList; // list of align instructions in current IG + insGroup* emitLastLoopStart; // Start IG of last inner loop + insGroup* emitLastLoopEnd; // End IG of last inner loop + insGroup* emitLastAlignedIG; // last IG that has align instruction + instrDescAlign* emitAlignList; // list of all align instructions in method + instrDescAlign* emitAlignLast; // last align instruction in method // Points to the most recent added align instruction. If there are multiple align instructions like in arm64 or // non-adaptive alignment on xarch, this points to the first align instruction of the series of align instructions. instrDescAlign* emitAlignLastGroup; unsigned getLoopSize(insGroup* igLoopHeader, - unsigned maxLoopSize DEBUG_ARG(bool isAlignAdjusted) DEBUG_ARG(UNATIVE_OFFSET containingIGNum) - DEBUG_ARG(UNATIVE_OFFSET loopHeadPredIGNum)); // Get the smallest loop size + unsigned maxLoopSize DEBUGARG(bool isAlignAdjusted) DEBUGARG(insGroup* containingIG) + DEBUGARG(insGroup* loopHeadPredIG)); // Get the smallest loop size void emitLoopAlignment(DEBUG_ARG1(bool isPlacedBehindJmp)); bool emitEndsWithAlignInstr(); // Validate if newLabel is appropriate bool emitSetLoopBackEdge(const BasicBlock* loopTopBlock); void emitLoopAlignAdjustments(); // Predict if loop alignment is needed and make appropriate adjustments unsigned emitCalculatePaddingForLoopAlignment(insGroup* ig, - size_t offset DEBUG_ARG(bool isAlignAdjusted) - DEBUG_ARG(UNATIVE_OFFSET containingIGNum) - DEBUG_ARG(UNATIVE_OFFSET loopHeadPredIGNum)); + size_t offset DEBUGARG(bool isAlignAdjusted) + DEBUGARG(insGroup* containingIG) + DEBUGARG(insGroup* loopHeadPredIG)); void emitLoopAlign(unsigned paddingBytes, bool isFirstAlign DEBUG_ARG(bool isPlacedBehindJmp)); void emitLongLoopAlign(unsigned alignmentBoundary DEBUG_ARG(bool isPlacedBehindJmp)); diff --git a/src/coreclr/jit/emitarm64.cpp b/src/coreclr/jit/emitarm64.cpp index 2dd60855390f0f..483896dc10e3ab 100644 --- a/src/coreclr/jit/emitarm64.cpp +++ b/src/coreclr/jit/emitarm64.cpp @@ -14614,7 +14614,7 @@ void emitter::emitDispInsHelp( // targetIG is only set for 1st of the series of align instruction if ((alignInstrId->idaLoopHeadPredIG != nullptr) && (alignInstrId->loopHeadIG() != nullptr)) { - printf(" for IG%02u", alignInstrId->loopHeadIG()->igNum); + printf(" for IG%02u", alignInstrId->loopHeadIG()->GetDisplayId()); } printf("]"); } diff --git a/src/coreclr/jit/emitloongarch64.cpp b/src/coreclr/jit/emitloongarch64.cpp index 83d6425480678e..af215976403826 100644 --- a/src/coreclr/jit/emitloongarch64.cpp +++ b/src/coreclr/jit/emitloongarch64.cpp @@ -2837,8 +2837,7 @@ void emitter::emitJumpDistBind() 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 + assert(lastIG == nullptr || lastIG->IsBeforeOrEqual(jmp->idjIG) || jmp->idjIG == prologIG); lastIG = jmp->idjIG; #endif // DEBUG @@ -2867,8 +2866,8 @@ void emitter::emitJumpDistBind() #ifdef DEBUG if (EMITVERBOSE) { - printf("Adjusted offset of " FMT_BB " from %04X to %04X\n", lstIG->igNum, lstIG->igOffs, - lstIG->igOffs + adjIG); + printf("Adjusted offset of " FMT_BB " from %04X to %04X\n", lstIG->GetDisplayId(), + lstIG->igOffs, lstIG->igOffs + adjIG); } #endif // DEBUG lstIG->igOffs += adjIG; @@ -2952,7 +2951,7 @@ void emitter::emitJumpDistBind() srcEncodingOffs = srcInstrOffs + ssz; // Encoding offset of relative offset for small branch - if (jmpIG->igNum < tgtIG->igNum) + if (jmpIG->IsBefore(tgtIG)) { /* Forward jump */ @@ -3153,7 +3152,7 @@ void emitter::emitJumpDistBind() #ifdef DEBUG if (EMITVERBOSE) { - printf("Adjusted offset of " FMT_BB " from %04X to %04X\n", lstIG->igNum, lstIG->igOffs, + printf("Adjusted offset of " FMT_BB " from %04X to %04X\n", lstIG->GetDisplayId(), lstIG->igOffs, lstIG->igOffs + adjIG); } #endif // DEBUG diff --git a/src/coreclr/jit/emitxarch.cpp b/src/coreclr/jit/emitxarch.cpp index ff65e2d883a25f..600f6c1619625f 100644 --- a/src/coreclr/jit/emitxarch.cpp +++ b/src/coreclr/jit/emitxarch.cpp @@ -13983,7 +13983,7 @@ void emitter::emitDispIns( // targetIG is only set for 1st of the series of align instruction if ((alignInstrId->idaLoopHeadPredIG != nullptr) && (alignInstrId->loopHeadIG() != nullptr)) { - printf(" for IG%02u", alignInstrId->loopHeadIG()->igNum); + printf(" for IG%02u", alignInstrId->loopHeadIG()->GetDisplayId()); } printf("]"); } @@ -20013,9 +20013,8 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) #endif #if FEATURE_LOOP_ALIGN - // Only compensate over-estimated instructions if emitCurIG is before - // the last IG that needs alignment. - if (emitCurIG->igNum <= emitLastAlignedIgNum) + // Only compensate over-estimated instructions if emitCurIG is before the last IG that needs alignment. + if ((emitLastAlignedIG != nullptr) && emitCurIG->IsBeforeOrEqual(emitLastAlignedIG)) { int diff = id->idCodeSize() - ((UNATIVE_OFFSET)(dst - *dp)); assert(diff >= 0); From 37cdb45dbab16c0d76fba6449c16b57208c145b6 Mon Sep 17 00:00:00 2001 From: SingleAccretion Date: Fri, 13 Mar 2026 18:14:44 +0300 Subject: [PATCH 2/8] Support multiple intructions groups for code generated out-of-order --- src/coreclr/jit/emit.cpp | 142 ++++++++++++++++++++++++++++++++++----- src/coreclr/jit/emit.h | 2 + 2 files changed, 126 insertions(+), 18 deletions(-) diff --git a/src/coreclr/jit/emit.cpp b/src/coreclr/jit/emit.cpp index 24080ab0acf29a..4d7f0f0de7fdc7 100644 --- a/src/coreclr/jit/emit.cpp +++ b/src/coreclr/jit/emit.cpp @@ -71,20 +71,26 @@ int emitLocation::GetInsOffset() const return emitGetInsOfsFromCodePos(codePos); } -// Get the instruction offset in the current instruction group, which must be a funclet prolog group. +// Get the instruction offset in the current instruction region, which must be a funclet prolog. // This is used to find an instruction offset used in unwind data. -// TODO-AMD64-Bug?: We only support a single main function prolog group, but allow for multiple funclet prolog -// groups (not that we actually use that flexibility, since the funclet prolog will be small). How to -// handle that? UNATIVE_OFFSET emitLocation::GetFuncletPrologOffset(emitter* emit) const { assert(ig->igFuncIdx != 0); assert((ig->igFlags & IGF_FUNCLET_PROLOG) != 0); - assert(ig == emit->emitCurIG); + assert((ig->igFlags & IGF_OUT_OF_ORDER_HEAD) != 0); + assert(GetInsOffset() == 0); - return emit->emitCurIGsize; -} + unsigned offset = 0; + insGroup* lastIG = ig; + while (lastIG != emit->emitCurIG) + { + offset += lastIG->igSize; + lastIG = lastIG->igNext; + } + assert((lastIG->igFlags & IGF_FUNCLET_PROLOG) != 0); + return offset + emit->emitCurIGsize; +} //------------------------------------------------------------------------ // IsPreviousInsNum: Returns true if the emitter is on the next instruction // of the same group as this emitLocation. @@ -120,26 +126,133 @@ void emitLocation::Print(LONG compMethodID) const } #endif // DEBUG +//------------------------------------------------------------------------ +// InitializeNum: Initialize this IG's order/display number. +// +// Each subsequently allocated group should be assigned a monotonically +// increasing number. +// +// Arguments: +// num - The number +// void insGroup::InitializeNum(unsigned num) { igNum = num; } +//------------------------------------------------------------------------ +// GetDisplayId: Get the ID of this group used for display. +// +// Return Value: +// A unique ID for this group. +// unsigned insGroup::GetDisplayId() const { return igNum; } +//------------------------------------------------------------------------ +// IsBefore: Is this group before 'ig' in layout (IG list) order? +// +// Most groups are generated in-order, such that their 'numbers' reflect +// both the layout and IG linked list order. However, for prologs/epilogs, +// we can have extend groups that are generated after the IR-driven code, +// and this function accounts for that. +// +// Arguments: +// ig - The group to compare to +// +// Return Value: +// Whether 'this' is before 'ig'. +// bool insGroup::IsBefore(const insGroup* ig) const { - return igNum < ig->igNum; + assert(ig != nullptr); + + // All IGs are generated in order, except the extend groups that may hangs off prologs and epilogs. + // In turn, those groups themselves are generated in order within their respective regions. + unsigned positionOfThis; + if ((igFlags & IGF_OUT_OF_ORDER_MASK) != 0) + { + const insGroup* nextIG = igNext; + while (true) + { + if (nextIG == nullptr) + { + return false; + } + if (((nextIG->igFlags & IGF_OUT_OF_ORDER_MASK) == 0) || ((nextIG->igFlags & IGF_OUT_OF_ORDER_HEAD) != 0)) + { + positionOfThis = nextIG->igNum - 1; // Position equal to the in-order 'head' of the region. + break; + } + if (nextIG == ig) + { + return true; + } + + nextIG = nextIG->igNext; + } + } + else + { + positionOfThis = igNum; + } + + unsigned positionOfIG; + if ((ig->igFlags & IGF_OUT_OF_ORDER_MASK) != 0) + { + const insGroup* nextIG = ig->igNext; + while (true) + { + if (nextIG == nullptr) + { + return true; + } + if (((nextIG->igFlags & IGF_OUT_OF_ORDER_MASK) == 0) || ((nextIG->igFlags & IGF_OUT_OF_ORDER_HEAD) != 0)) + { + positionOfIG = nextIG->igNum - 1; + break; + } + if (nextIG == this) + { + return false; + } + + nextIG = nextIG->igNext; + } + } + else + { + positionOfIG = ig->igNum; + } + + return positionOfThis < positionOfIG; } +//------------------------------------------------------------------------ +// IsBefore: Is this group before 'ig' in layout order, or equal to it? +// +// Arguments: +// ig - The group to compare to +// +// Return Value: +// Whether 'this' is before 'ig' or is equal to it. +// bool insGroup::IsBeforeOrEqual(const insGroup* ig) const { return !IsAfter(ig); } +//------------------------------------------------------------------------ +// IsBefore: Is this group after 'ig' in layout order? +// +// Arguments: +// ig - The group to compare to +// +// Return Value: +// Whether 'this' is after 'ig'. +// bool insGroup::IsAfter(const insGroup* ig) const { return ig->IsBefore(this); @@ -1623,16 +1736,8 @@ void* emitter::emitAllocAnyInstr(size_t sz, emitAttr opsz) { #ifdef DEBUG // Under STRESS_EMITTER, put every instruction in its own instruction group. - // We can't do this for a prolog, epilog, funclet prolog, or funclet epilog, - // because those are generated out of order. We currently have a limitation - // where the jump shortening pass uses the instruction group number to determine - // if something is earlier or later in the code stream. This implies that - // these groups cannot be more than a single instruction group. Note that - // the prolog/epilog placeholder groups ARE generated in order, and are - // re-used. But generating additional groups would not work. if (m_compiler->compStressCompile(Compiler::STRESS_EMITTER, 1) && emitCurIGinsCnt && !emitIGisInProlog(emitCurIG) && - !emitIGisInEpilog(emitCurIG) && !emitCurIG->endsWithAlignInstr() && !emitIGisInFuncletProlog(emitCurIG) && - !emitIGisInFuncletEpilog(emitCurIG)) + !emitCurIG->endsWithAlignInstr()) { emitNxtIG(true); } @@ -2161,6 +2266,7 @@ void emitter::emitCreatePlaceholderIG(insGroupPlaceholderType igType, { igPh->igFlags |= IGF_FUNCLET_EPILOG; } + igPh->igFlags |= IGF_OUT_OF_ORDER_HEAD; /* Link it into the placeholder list */ @@ -2402,7 +2508,7 @@ void emitter::emitBegPrologEpilog(insGroup* igPh) m_compiler->funSetCurrentFunc(ig->igFuncIdx); /* Set the new IG as the place to generate code */ - + emitCurCodeOffset = ig->igOffs; emitGenIG(ig); #if EMIT_TRACK_STACK_DEPTH diff --git a/src/coreclr/jit/emit.h b/src/coreclr/jit/emit.h index 5f7a370af10317..1501d6022d99ad 100644 --- a/src/coreclr/jit/emit.h +++ b/src/coreclr/jit/emit.h @@ -314,6 +314,7 @@ struct insGroup #ifdef TARGET_ARM64 #define IGF_HAS_REMOVED_INSTR 0x1000 // this group has an instruction that was removed. #endif +#define IGF_OUT_OF_ORDER_HEAD 0x2000 // first group (generated in-order) of a region generated out-of-order // Mask of IGF_* flags that should be propagated to new blocks when they are created. // This allows prologs and epilogs to be any number of IGs, but still be @@ -323,6 +324,7 @@ struct insGroup #else // DEBUG #define IGF_PROPAGATE_MASK (IGF_EPILOG | IGF_FUNCLET_PROLOG) #endif // DEBUG +#define IGF_OUT_OF_ORDER_MASK (IGF_EPILOG | IGF_FUNCLET_PROLOG | IGF_FUNCLET_EPILOG) // Try to do better packing based on how large regMaskSmall is (8, 16, or 64 bits). From 83336f97a0bae28c73c243f45a8e82a12785a1cd Mon Sep 17 00:00:00 2001 From: SingleAccretion Date: Sat, 14 Mar 2026 14:43:52 +0300 Subject: [PATCH 3/8] Validation --- src/coreclr/jit/emit.cpp | 4 ++-- src/tests/Common/CLRTest.Jit.targets | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/emit.cpp b/src/coreclr/jit/emit.cpp index 4d7f0f0de7fdc7..429f73ec01c8a5 100644 --- a/src/coreclr/jit/emit.cpp +++ b/src/coreclr/jit/emit.cpp @@ -1736,8 +1736,8 @@ void* emitter::emitAllocAnyInstr(size_t sz, emitAttr opsz) { #ifdef DEBUG // Under STRESS_EMITTER, put every instruction in its own instruction group. - if (m_compiler->compStressCompile(Compiler::STRESS_EMITTER, 1) && emitCurIGinsCnt && !emitIGisInProlog(emitCurIG) && - !emitCurIG->endsWithAlignInstr()) + if ((m_compiler->compStressCompile(Compiler::STRESS_EMITTER, 1) || true) && emitCurIGinsCnt && + !emitIGisInProlog(emitCurIG) && !emitCurIG->endsWithAlignInstr()) { emitNxtIG(true); } diff --git a/src/tests/Common/CLRTest.Jit.targets b/src/tests/Common/CLRTest.Jit.targets index 729d588e5337eb..b7607732440036 100644 --- a/src/tests/Common/CLRTest.Jit.targets +++ b/src/tests/Common/CLRTest.Jit.targets @@ -112,6 +112,7 @@ IF DEFINED RunningIlasmRoundTrip ( false true + false true true From 66fe4a6888d6857b630ce7fb4d51387a622acb19 Mon Sep 17 00:00:00 2001 From: SingleAccretion Date: Wed, 18 Mar 2026 00:38:15 +0300 Subject: [PATCH 4/8] Fix weight diffs --- src/coreclr/jit/emit.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/coreclr/jit/emit.cpp b/src/coreclr/jit/emit.cpp index 429f73ec01c8a5..51418f39c27a5d 100644 --- a/src/coreclr/jit/emit.cpp +++ b/src/coreclr/jit/emit.cpp @@ -1595,8 +1595,6 @@ void emitter::perfScoreUnhandledInstruction(instrDesc* id, insExecutionCharacter pResult->insLatency = PERFSCORE_LATENCY_1C; } -#endif // defined(DEBUG) || defined(LATE_DISASM) - //---------------------------------------------------------------------------------------- // getCurrentBlockWeight: Return the block weight for the currently active block // @@ -1618,12 +1616,19 @@ weight_t emitter::getCurrentBlockWeight() { return m_compiler->compCurBB->getBBWeight(m_compiler); } - else // we have a null compCurBB + else if (emitCurIG != nullptr) + { + // Prolog or epilog case, use the weight of the head group or its extensions. + assert((emitCurIG->igFlags & IGF_OUT_OF_ORDER_MASK) != 0); + return emitCurIG->igWeight; + } + else { - // prolog or epilog case, so just use the standard weight + // This is the prolog case (when we're just initializing the IG list). return BB_UNITY_WEIGHT; } } +#endif // defined(DEBUG) || defined(LATE_DISASM) #if defined(TARGET_LOONGARCH64) void emitter::dispIns(instrDesc* id) From 8bd432c462126dcfcf1e931d2691ce1a7cd89345 Mon Sep 17 00:00:00 2001 From: SingleAccretion Date: Thu, 26 Mar 2026 16:44:08 +0300 Subject: [PATCH 5/8] Improve the test watcher --- src/native/watchdog/watchdog.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/native/watchdog/watchdog.cpp b/src/native/watchdog/watchdog.cpp index 2ab508a5fb53c8..ef59dbfe87afb2 100644 --- a/src/native/watchdog/watchdog.cpp +++ b/src/native/watchdog/watchdog.cpp @@ -41,7 +41,7 @@ int main(const int argc, const char *argv[]) const long timeout_mins = strtol(argv[1], nullptr, 10); int exit_code = run_timed_process(timeout_mins * 60000L, argc-2, &argv[2]); - printf("App Exit Code: %d\n", exit_code); + printf("Exit Code: %d\n", exit_code); return exit_code; } @@ -58,7 +58,6 @@ int run_timed_process(const long timeout_ms, const int proc_argc, const char *pr STARTUPINFOA startup_info; PROCESS_INFORMATION proc_info; - unsigned long exit_code; ZeroMemory(&startup_info, sizeof(startup_info)); startup_info.cb = sizeof(startup_info); @@ -72,7 +71,11 @@ int run_timed_process(const long timeout_ms, const int proc_argc, const char *pr return error_code; } - WaitForSingleObject(proc_info.hProcess, timeout_ms); + if (WaitForSingleObject(proc_info.hProcess, timeout_ms) == WAIT_TIMEOUT) + { + printf("Child process took too long. Timed out...\n"); + } + DWORD exit_code; GetExitCodeProcess(proc_info.hProcess, &exit_code); CloseHandle(proc_info.hProcess); From 9ca7727a6e1a040deb123b63f01754efafd5c188 Mon Sep 17 00:00:00 2001 From: SingleAccretion Date: Fri, 27 Mar 2026 23:40:10 +0300 Subject: [PATCH 6/8] Revert "Validation" This reverts commit 83336f97a0bae28c73c243f45a8e82a12785a1cd. --- src/coreclr/jit/emit.cpp | 4 ++-- src/tests/Common/CLRTest.Jit.targets | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/coreclr/jit/emit.cpp b/src/coreclr/jit/emit.cpp index 51418f39c27a5d..840eff0242d81c 100644 --- a/src/coreclr/jit/emit.cpp +++ b/src/coreclr/jit/emit.cpp @@ -1741,8 +1741,8 @@ void* emitter::emitAllocAnyInstr(size_t sz, emitAttr opsz) { #ifdef DEBUG // Under STRESS_EMITTER, put every instruction in its own instruction group. - if ((m_compiler->compStressCompile(Compiler::STRESS_EMITTER, 1) || true) && emitCurIGinsCnt && - !emitIGisInProlog(emitCurIG) && !emitCurIG->endsWithAlignInstr()) + if (m_compiler->compStressCompile(Compiler::STRESS_EMITTER, 1) && emitCurIGinsCnt && !emitIGisInProlog(emitCurIG) && + !emitCurIG->endsWithAlignInstr()) { emitNxtIG(true); } diff --git a/src/tests/Common/CLRTest.Jit.targets b/src/tests/Common/CLRTest.Jit.targets index b7607732440036..729d588e5337eb 100644 --- a/src/tests/Common/CLRTest.Jit.targets +++ b/src/tests/Common/CLRTest.Jit.targets @@ -112,7 +112,6 @@ IF DEFINED RunningIlasmRoundTrip ( false true - false true true From fb25de8f1fce170922d5e9fe81a5fe0f692404e7 Mon Sep 17 00:00:00 2001 From: SingleAccretion Date: Mon, 30 Mar 2026 00:04:26 +0300 Subject: [PATCH 7/8] Remove single-IG prolog restriction --- src/coreclr/jit/codegencommon.cpp | 2 +- src/coreclr/jit/emit.cpp | 146 +++++++++++++--------------- src/coreclr/jit/emit.h | 68 ++++++------- src/coreclr/jit/emitinl.h | 4 +- src/coreclr/jit/emitloongarch64.cpp | 14 +-- src/coreclr/jit/emitpub.h | 8 +- src/coreclr/jit/emitxarch.cpp | 3 +- src/coreclr/jit/unwind.cpp | 25 +---- 8 files changed, 117 insertions(+), 153 deletions(-) diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index 058316551fb377..487f7e9d2609d5 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -5550,7 +5550,7 @@ void CodeGen::genFnProlog() // For OSR we may have a zero-length prolog. That's not supported // when the method must report a generics context,/ so add a nop if so. // - if (m_compiler->opts.IsOSR() && (GetEmitter()->emitGetPrologOffsetEstimate() == 0) && + if (m_compiler->opts.IsOSR() && (GetEmitter()->emitGetCurrentCodeOffsetFrom(nullptr) == 0) && (m_compiler->lvaReportParamTypeArg() || m_compiler->lvaKeepAliveAndReportThis())) { JITDUMP("OSR: prolog was zero length and has generic context to report: adding nop to pad prolog.\n"); diff --git a/src/coreclr/jit/emit.cpp b/src/coreclr/jit/emit.cpp index 840eff0242d81c..2ca6e2f011810e 100644 --- a/src/coreclr/jit/emit.cpp +++ b/src/coreclr/jit/emit.cpp @@ -71,26 +71,6 @@ int emitLocation::GetInsOffset() const return emitGetInsOfsFromCodePos(codePos); } -// Get the instruction offset in the current instruction region, which must be a funclet prolog. -// This is used to find an instruction offset used in unwind data. -UNATIVE_OFFSET emitLocation::GetFuncletPrologOffset(emitter* emit) const -{ - assert(ig->igFuncIdx != 0); - assert((ig->igFlags & IGF_FUNCLET_PROLOG) != 0); - assert((ig->igFlags & IGF_OUT_OF_ORDER_HEAD) != 0); - assert(GetInsOffset() == 0); - - unsigned offset = 0; - insGroup* lastIG = ig; - while (lastIG != emit->emitCurIG) - { - offset += lastIG->igSize; - lastIG = lastIG->igNext; - } - assert((lastIG->igFlags & IGF_FUNCLET_PROLOG) != 0); - - return offset + emit->emitCurIGsize; -} //------------------------------------------------------------------------ // IsPreviousInsNum: Returns true if the emitter is on the next instruction // of the same group as this emitLocation. @@ -1284,7 +1264,7 @@ insGroup* emitter::emitSavIG(bool emitAdd) if (last != nullptr) { // Append the jump(s) from this IG to the global list - bool prologJump = (ig == emitPrologIG); + bool prologJump = emitIGisInProlog(ig); if ((emitJumpList == nullptr) || prologJump) { last->idjNext = emitJumpList; @@ -1371,6 +1351,8 @@ void emitter::emitBegFN(bool hasFramePtr emitChkAlign = chkAlign; #endif + emitPrologEndPos.Init(); + /* We have no epilogs yet */ emitEpilogSize = 0; @@ -1476,7 +1458,8 @@ void emitter::emitBegFN(bool hasFramePtr emitNxtIGnum = 1; - emitPrologIG = emitIGlist = emitIGlast = emitCurIG = ig = emitAllocIG(); + emitIGlist = emitIGlast = emitCurIG = ig = emitAllocIG(); + emitCurIG->igFlags |= (IGF_PROLOG | IGF_OUT_OF_ORDER_HEAD); emitLastIns = nullptr; emitLastInsIG = nullptr; @@ -1493,8 +1476,10 @@ void emitter::emitBegFN(bool hasFramePtr #endif /* Append another group, to start generating the method body */ - emitNewIG(); + + /* The group after the placeholder prolog group doesn't get the "propagate" flags */ + emitCurIG->igFlags &= ~IGF_PROPAGATE_MASK; } #ifdef PSEUDORANDOM_NOP_INSERTION @@ -1741,7 +1726,7 @@ void* emitter::emitAllocAnyInstr(size_t sz, emitAttr opsz) { #ifdef DEBUG // Under STRESS_EMITTER, put every instruction in its own instruction group. - if (m_compiler->compStressCompile(Compiler::STRESS_EMITTER, 1) && emitCurIGinsCnt && !emitIGisInProlog(emitCurIG) && + if (m_compiler->compStressCompile(Compiler::STRESS_EMITTER, 1) && (emitCurIGinsCnt != 0) && !emitCurIG->endsWithAlignInstr()) { emitNxtIG(true); @@ -1935,8 +1920,6 @@ void* emitter::emitAllocAnyInstr(size_t sz, emitAttr opsz) // void emitter::emitCheckIGList() { - assert(emitPrologIG != nullptr); - #if EMIT_BACKWARDS_NAVIGATION struct IGIDPair { @@ -1968,17 +1951,18 @@ void emitter::emitCheckIGList() assert((currIG->igFlags & IGF_EXTEND) == 0); // First IG must be the function prolog. - assert(currIG == emitPrologIG); + assert((currIG->igFlags & IGF_PROLOG) != 0); } - if (currIG == emitPrologIG) + if ((currIG->igFlags & IGF_PROLOG) != 0) { // If we're in the function prolog, we can't be in any other prolog or epilog. assert((currIG->igFlags & (IGF_FUNCLET_PROLOG | IGF_FUNCLET_EPILOG | IGF_EPILOG)) == 0); } // An IG can have at most one of the prolog and epilog flags set. - assert(genCountBits((unsigned)currIG->igFlags & (IGF_FUNCLET_PROLOG | IGF_FUNCLET_EPILOG | IGF_EPILOG)) <= 1); + assert(genCountBits((unsigned)currIG->igFlags & + (IGF_PROLOG | IGF_FUNCLET_PROLOG | IGF_FUNCLET_EPILOG | IGF_EPILOG)) <= 1); // An IG can't have both IGF_HAS_ALIGN and IGF_REMOVED_ALIGN. assert(genCountBits((unsigned)currIG->igFlags & (IGF_HAS_ALIGN | IGF_REMOVED_ALIGN)) <= 1); @@ -1994,16 +1978,13 @@ void emitter::emitCheckIGList() // not be EXTEND groups, and would there be a benefit to that? Since epilogs are NOGC // it would help eliminate NOGC EXTEND groups. // - // Note that function prologs must currently exist entirely within one IG and there is - // no flag to indicate a function prolog (the `emitPrologIG` variable points to the single - // unique prolog IG). - // // Thus, we can't have this assert: // assert((currIG->igFlags & (IGF_FUNCLET_PROLOG | IGF_FUNCLET_EPILOG | IGF_EPILOG)) == // (prevIG->igFlags & (IGF_FUNCLET_PROLOG | IGF_FUNCLET_EPILOG | IGF_EPILOG))); - // If this is a funclet prolog IG, then it can only extend another funclet prolog IG. - assert((currIG->igFlags & IGF_FUNCLET_PROLOG) == (prevIG->igFlags & IGF_FUNCLET_PROLOG)); + // If this is a prolog IG, then it can only extend another prolog IG. + assert((currIG->igFlags & (IGF_PROLOG | IGF_FUNCLET_PROLOG)) == + (prevIG->igFlags & (IGF_PROLOG | IGF_FUNCLET_PROLOG))); // If this is a function epilog IG, it can't extend a funclet prolog or funclet epilog IG. if (currIG->igFlags & IGF_EPILOG) @@ -2102,8 +2083,7 @@ void emitter::emitBegProlog() emitForceNewIG = false; /* Switch to the pre-allocated prolog IG */ - - emitGenIG(emitPrologIG); + emitGenIG(emitGetFirstPrologIG()); /* Nothing is live on entry to the prolog */ @@ -2116,21 +2096,6 @@ void emitter::emitBegProlog() emitPrevByrefRegs = RBM_NONE; } -/***************************************************************************** - * - * Return the code offset of the current location in the prolog. - */ - -unsigned emitter::emitGetPrologOffsetEstimate() -{ - /* For now only allow a single prolog ins group */ - - assert(emitPrologIG); - assert(emitPrologIG == emitCurIG); - - return emitCurIGsize; -} - /***************************************************************************** * * Mark the code offset of the current location as the end of the prolog, @@ -2140,13 +2105,7 @@ unsigned emitter::emitGetPrologOffsetEstimate() void emitter::emitMarkPrologEnd() { assert(m_compiler->compGeneratingProlog); - - /* For now only allow a single prolog ins group */ - - assert(emitPrologIG); - assert(emitPrologIG == emitCurIG); - - emitPrologEndPos = emitCurOffset(); + emitPrologEndPos.CaptureLocation(this); } /***************************************************************************** @@ -2163,7 +2122,7 @@ void emitter::emitEndProlog() /* Save the prolog IG if non-empty or if only one block */ - if (emitCurIGnonEmpty() || emitCurIG == emitPrologIG) + if (emitCurIGnonEmpty() || (emitCurIG == emitGetFirstPrologIG())) { emitSavIG(); } @@ -2340,7 +2299,6 @@ void emitter::emitCreatePlaceholderIG(insGroupPlaceholderType igType, emitForceStoreGCState = true; /* The group after the placeholder group doesn't get the "propagate" flags */ - emitCurIG->igFlags &= ~IGF_PROPAGATE_MASK; } @@ -2467,6 +2425,11 @@ void emitter::emitFinishPrologEpilogGeneration() * Common code for prolog / epilog beginning. Convert the placeholder group to actual code IG, * and set it as the current group. */ +insGroup* emitter::emitGetFirstPrologIG() const +{ + assert(emitIGisInProlog(emitIGlist)); + return emitIGlist; +} void emitter::emitBegPrologEpilog(insGroup* igPh) { @@ -4119,6 +4082,14 @@ void emitter::emitDispIGflags(unsigned flags) { printf(", byref"); } + if (flags & IGF_PROLOG) + { + printf(", prolog"); + } + if (flags & IGF_EPILOG) + { + printf(", epilog"); + } if (flags & IGF_FUNCLET_PROLOG) { printf(", funclet prolog"); @@ -4127,10 +4098,6 @@ void emitter::emitDispIGflags(unsigned flags) { printf(", funclet epilog"); } - if (flags & IGF_EPILOG) - { - printf(", epilog"); - } if (flags & IGF_NOGCINTERRUPT) { printf(", nogc"); @@ -4317,10 +4284,6 @@ void emitter::emitDispIG(insGroup* ig, bool displayFunc, bool displayInstruction { printf(" <-- Current IG"); } - if (ig == emitPrologIG) - { - printf(" <-- Prolog IG"); - } } printf("\n"); @@ -5002,9 +4965,6 @@ void emitter::emitJumpDistBind() UNATIVE_OFFSET adjIG; UNATIVE_OFFSET adjLJ; insGroup* lstIG; -#ifdef DEBUG - insGroup* prologIG = emitPrologIG; -#endif // DEBUG int jmp_iteration = 1; @@ -5195,7 +5155,7 @@ void emitter::emitJumpDistBind() assert(lastLJ == nullptr || lastIG != jmp->idjIG || lastLJ->idjOffs < jmp->idjOffs); lastLJ = (lastIG == jmp->idjIG) ? jmp : nullptr; - assert(lastIG == nullptr || lastIG->IsBeforeOrEqual(jmp->idjIG) || jmp->idjIG == prologIG); + assert(lastIG == nullptr || lastIG->IsBeforeOrEqual(jmp->idjIG) || emitIGisInProlog(jmp->idjIG)); lastIG = jmp->idjIG; #endif // DEBUG @@ -7817,7 +7777,7 @@ unsigned emitter::emitEndCodeGen(Compiler* comp, #endif // DEBUG // Assign the real prolog size - *prologSize = emitCodeOffset(emitPrologIG, emitPrologEndPos); + *prologSize = emitPrologEndPos.CodeOffset(this); /* Return the amount of code we've generated */ @@ -9567,6 +9527,39 @@ UNATIVE_OFFSET emitter::emitCodeOffset(void* blockPtr, unsigned codePos) return ig->igOffs + of; } +//------------------------------------------------------------------------ +// emitGetCurrentCodeOffsetFrom: Get current code offset relative to "ig". +// +// This is used to retrieve the current offset within a prolog being generated +// for unwind info. Thus, the offset we return from here can't later shrink, +// and overestimating the code size of prolog instructions is fatal. +// +// Arguments: +// ig - IG denoting the start of code, "nullptr" for main function prolog +// +// Return Value: +// Effectively " - ig->igOffs". +// +UNATIVE_OFFSET emitter::emitGetCurrentCodeOffsetFrom(insGroup* ig) +{ + if (ig == nullptr) + { + ig = emitGetFirstPrologIG(); + } + assert((ig->igFlags & IGF_OUT_OF_ORDER_HEAD) != 0); + + unsigned igKind = ig->igFlags & (IGF_PROLOG | IGF_FUNCLET_PROLOG); + unsigned offset = 0; + while (ig != emitCurIG) + { + offset += ig->igSize; + ig = ig->igNext; + } + assert((ig->igFlags & igKind) == igKind); + + return offset + emitCurIGsize; +} + /***************************************************************************** * * Record the fact that the given register now contains a live GC ref. @@ -9994,12 +9987,7 @@ void emitter::emitInsertIGAfter(insGroup* insertAfterIG, insGroup* ig) void emitter::emitNxtIG(bool extend) { - /* Right now we don't allow multi-IG prologs */ - - assert(emitCurIG != emitPrologIG); - /* First save the current group */ - emitSavIG(extend); /* Update the GC live sets for the group's start diff --git a/src/coreclr/jit/emit.h b/src/coreclr/jit/emit.h index 1501d6022d99ad..0ea660486f4607 100644 --- a/src/coreclr/jit/emit.h +++ b/src/coreclr/jit/emit.h @@ -213,8 +213,6 @@ class emitLocation return true; } - UNATIVE_OFFSET GetFuncletPrologOffset(emitter* emit) const; - bool IsPreviousInsNum(emitter* emit) const; #ifdef DEBUG @@ -293,38 +291,35 @@ struct insGroup insGroup* igLoopBackEdge; // "last" back-edge that branches back to an aligned loop head. #endif -#define IGF_GC_VARS 0x0001 // new set of live GC ref variables -#define IGF_BYREF_REGS 0x0002 // new set of live by-ref registers -#define IGF_FUNCLET_PROLOG 0x0004 // this group belongs to a funclet prolog -#define IGF_FUNCLET_EPILOG 0x0008 // this group belongs to a funclet epilog. -#define IGF_EPILOG 0x0010 // this group belongs to a main function epilog -#define IGF_NOGCINTERRUPT 0x0020 // this IG is in a no-interrupt region (prolog, epilog, etc.) -#define IGF_UPD_ISZ 0x0040 // some instruction sizes updated -#define IGF_PLACEHOLDER 0x0080 // this is a placeholder group, to be filled in later -#define IGF_EXTEND \ - 0x0100 // this block is conceptually an extension of the previous block - // and the emitter should continue to track GC info as if there was no new block. -#define IGF_HAS_ALIGN \ - 0x0200 // this group contains an alignment instruction(s) at the end to align either the next - // IG, or, if this IG contains with an unconditional branch, some subsequent IG. -#define IGF_REMOVED_ALIGN \ - 0x0400 // IG was marked as having an alignment instruction(s), but was later unmarked - // without updating the IG's size/offsets. -#define IGF_HAS_REMOVABLE_JMP 0x0800 // this group ends with an unconditional jump which is a candidate for removal +#define IGF_GC_VARS (1 << 0) // new set of live GC ref variables +#define IGF_BYREF_REGS (1 << 1) // new set of live by-ref registers +#define IGF_PROLOG (1 << 2) // this group belongs to a main function prolog +#define IGF_EPILOG (1 << 3) // this group belongs to a main function epilog +#define IGF_FUNCLET_PROLOG (1 << 4) // this group belongs to a funclet prolog +#define IGF_FUNCLET_EPILOG (1 << 5) // this group belongs to a funclet epilog. +#define IGF_NOGCINTERRUPT (1 << 6) // this IG is in a no-interrupt region (prolog, epilog, etc.) +#define IGF_UPD_ISZ (1 << 7) // some instruction sizes updated +#define IGF_PLACEHOLDER (1 << 8) // this is a placeholder group, to be filled in later +// This block is conceptually an extension of the previous block +// and the emitter should continue to track GC info as if there was no new block. +#define IGF_EXTEND (1 << 9) +// This group contains an alignment instruction(s) at the end to align either the next +// IG, or, if this IG contains with an unconditional branch, some subsequent IG. +#define IGF_HAS_ALIGN (1 << 10) +// IG was marked as having an alignment instruction(s), but was later unmarked +// without updating the IG's size/offsets. +#define IGF_REMOVED_ALIGN (1 << 11) +#define IGF_HAS_REMOVABLE_JMP (1 << 12) // this group ends with an unconditional jump which is a candidate for removal #ifdef TARGET_ARM64 -#define IGF_HAS_REMOVED_INSTR 0x1000 // this group has an instruction that was removed. +#define IGF_HAS_REMOVED_INSTR (1 << 13) // this group has an instruction that was removed. #endif -#define IGF_OUT_OF_ORDER_HEAD 0x2000 // first group (generated in-order) of a region generated out-of-order +#define IGF_OUT_OF_ORDER_HEAD (1 << 14) // first group (generated in-order) of a region generated out-of-order // Mask of IGF_* flags that should be propagated to new blocks when they are created. // This allows prologs and epilogs to be any number of IGs, but still be // automatically marked properly. -#ifdef DEBUG -#define IGF_PROPAGATE_MASK (IGF_EPILOG | IGF_FUNCLET_PROLOG | IGF_FUNCLET_EPILOG) -#else // DEBUG -#define IGF_PROPAGATE_MASK (IGF_EPILOG | IGF_FUNCLET_PROLOG) -#endif // DEBUG -#define IGF_OUT_OF_ORDER_MASK (IGF_EPILOG | IGF_FUNCLET_PROLOG | IGF_FUNCLET_EPILOG) +#define IGF_PROPAGATE_MASK (IGF_PROLOG | IGF_EPILOG | IGF_FUNCLET_PROLOG | IGF_FUNCLET_EPILOG) +#define IGF_OUT_OF_ORDER_MASK (IGF_PROLOG | IGF_EPILOG | IGF_FUNCLET_PROLOG | IGF_FUNCLET_EPILOG) // Try to do better packing based on how large regMaskSmall is (8, 16, or 64 bits). @@ -583,22 +578,22 @@ class emitter static emitAttr emitDecodeSize(emitter::opSize ensz); // Currently, we only allow one IG for the prolog - bool emitIGisInProlog(const insGroup* ig) + bool emitIGisInProlog(const insGroup* ig) const { - return ig == emitPrologIG; + return (ig != nullptr) && ((ig->igFlags & IGF_PROLOG) != 0); } - bool emitIGisInEpilog(const insGroup* ig) + bool emitIGisInEpilog(const insGroup* ig) const { return (ig != nullptr) && ((ig->igFlags & IGF_EPILOG) != 0); } - bool emitIGisInFuncletProlog(const insGroup* ig) + bool emitIGisInFuncletProlog(const insGroup* ig) const { return (ig != nullptr) && ((ig->igFlags & IGF_FUNCLET_PROLOG) != 0); } - bool emitIGisInFuncletEpilog(const insGroup* ig) + bool emitIGisInFuncletEpilog(const insGroup* ig) const { return (ig != nullptr) && ((ig->igFlags & IGF_FUNCLET_EPILOG) != 0); } @@ -2564,7 +2559,7 @@ class emitter /* Method prolog and epilog */ /************************************************************************/ - unsigned emitPrologEndPos; + emitLocation emitPrologEndPos; unsigned emitEpilogCnt; UNATIVE_OFFSET emitEpilogSize; @@ -2609,6 +2604,8 @@ class emitter #endif // JIT32_GCENCODER + insGroup* emitGetFirstPrologIG() const; + void emitBegPrologEpilog(insGroup* igPh); void emitEndPrologEpilog(); @@ -2865,9 +2862,6 @@ class emitter insGroup* emitIGlist; // first instruction group insGroup* emitIGlast; // last instruction group - insGroup* emitIGthis; // issued instruction group - - insGroup* emitPrologIG; // prolog instruction group instrDescJmp* emitJumpList; // list of local jumps in method instrDescJmp* emitJumpLast; // last of local jumps in method diff --git a/src/coreclr/jit/emitinl.h b/src/coreclr/jit/emitinl.h index 88f88713e58b39..ecd74685901e7b 100644 --- a/src/coreclr/jit/emitinl.h +++ b/src/coreclr/jit/emitinl.h @@ -611,9 +611,7 @@ bool emitter::emitGenNoGCLst(Callback& cb, bool skipMainPrologsAndEpilogs /* = f { if (skipMainPrologsAndEpilogs) { - if (ig == emitPrologIG) - continue; - if (ig->igFlags & IGF_EPILOG) + if ((ig->igFlags & (IGF_PROLOG | IGF_EPILOG)) != 0) continue; } if ((ig->igFlags & IGF_NOGCINTERRUPT) && ig->igSize > 0) diff --git a/src/coreclr/jit/emitloongarch64.cpp b/src/coreclr/jit/emitloongarch64.cpp index af215976403826..0913cd806e76d7 100644 --- a/src/coreclr/jit/emitloongarch64.cpp +++ b/src/coreclr/jit/emitloongarch64.cpp @@ -2780,9 +2780,6 @@ void emitter::emitJumpDistBind() UNATIVE_OFFSET adjIG; UNATIVE_OFFSET adjSJ; insGroup* lstIG; -#ifdef DEBUG - insGroup* prologIG = emitPrologIG; -#endif // DEBUG // NOTE: // bit0 of isLinkingEnd_LA: indicating whether updating the instrDescJmp's size with the type INS_OPTS_J; @@ -2837,7 +2834,7 @@ void emitter::emitJumpDistBind() assert(lastSJ == nullptr || lastIG != jmp->idjIG || lastSJ->idjOffs < (jmp->idjOffs + adjSJ)); lastSJ = (lastIG == jmp->idjIG) ? jmp : nullptr; - assert(lastIG == nullptr || lastIG->IsBeforeOrEqual(jmp->idjIG) || jmp->idjIG == prologIG); + assert(lastIG == nullptr || lastIG->IsBeforeOrEqual(jmp->idjIG) || emitIGisInProlog(jmp->idjIG)); lastIG = jmp->idjIG; #endif // DEBUG @@ -4052,7 +4049,8 @@ void emitter::emitDisInsName(code_t code, const BYTE* addr, instrDesc* id) { printf("%s, %s, 0x%lx\n", RegNames[regd], RegNames[regj], offs16); } - else if ((unsigned)(addr - emitCodeBlock) < emitPrologIG->igSize) // only for prolog + // only for prolog + else if (emitPrologEndPos.Valid() && ((unsigned)(addr - emitCodeBlock) < emitPrologEndPos.CodeOffset(this))) { if (offs16 < 0) { @@ -4073,7 +4071,8 @@ void emitter::emitDisInsName(code_t code, const BYTE* addr, instrDesc* id) { tmp = (((code >> 10) & 0xffff) | ((code & 0x1f) << 16)) << 11; tmp >>= 9; - if ((unsigned)(addr - emitCodeBlock) < emitPrologIG->igSize) // only for prolog + // only for prolog + if (emitPrologEndPos.Valid() && ((unsigned)(addr - emitCodeBlock) < emitPrologEndPos.CodeOffset(this))) { tmp >>= 2; if (tmp < 0) @@ -4102,7 +4101,8 @@ void emitter::emitDisInsName(code_t code, const BYTE* addr, instrDesc* id) methodName = m_compiler->eeGetMethodFullName((CORINFO_METHOD_HANDLE)id->idDebugOnlyInfo()->idMemCookie); printf("# %s\n", methodName); } - else if ((unsigned)(addr - emitCodeBlock) < emitPrologIG->igSize) // only for prolog + // only for prolog + else if (emitPrologEndPos.Valid() && ((unsigned)(addr - emitCodeBlock) < emitPrologEndPos.CodeOffset(this))) { tmp >>= 2; if (tmp < 0) diff --git a/src/coreclr/jit/emitpub.h b/src/coreclr/jit/emitpub.h index d57799eca1eec8..cda3839c43341a 100644 --- a/src/coreclr/jit/emitpub.h +++ b/src/coreclr/jit/emitpub.h @@ -43,10 +43,9 @@ unsigned emitGetEpilogCnt(); template bool emitGenNoGCLst(Callback& cb, bool skipMainPrologsAndEpilogs = false); -void emitBegProlog(); -unsigned emitGetPrologOffsetEstimate(); -void emitMarkPrologEnd(); -void emitEndProlog(); +void emitBegProlog(); +void emitMarkPrologEnd(); +void emitEndProlog(); void emitCreatePlaceholderIG(insGroupPlaceholderType igType, BasicBlock* igBB, @@ -67,6 +66,7 @@ unsigned emitCurOffset(); unsigned emitSpecifiedOffset(unsigned insCount, unsigned igSize); UNATIVE_OFFSET emitCodeOffset(void* blockPtr, unsigned codeOffs); +UNATIVE_OFFSET emitGetCurrentCodeOffsetFrom(insGroup* ig); #ifdef DEBUG const char* emitOffsetToLabel(unsigned offs); diff --git a/src/coreclr/jit/emitxarch.cpp b/src/coreclr/jit/emitxarch.cpp index 600f6c1619625f..e1dc9389252912 100644 --- a/src/coreclr/jit/emitxarch.cpp +++ b/src/coreclr/jit/emitxarch.cpp @@ -10895,8 +10895,7 @@ void emitter::emitIns_J(instruction ins, else { /* Only allow non-label jmps in prolog */ - assert(emitPrologIG); - assert(emitPrologIG == emitCurIG); + assert(emitIGisInProlog(emitCurIG)); assert(instrCount != 0); } diff --git a/src/coreclr/jit/unwind.cpp b/src/coreclr/jit/unwind.cpp index 2662479fd664b6..829865fc1a13bf 100644 --- a/src/coreclr/jit/unwind.cpp +++ b/src/coreclr/jit/unwind.cpp @@ -24,7 +24,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX // locations must remain correct after the prolog and epilogs are generated. // // For the prolog, instructions are put in the special, preallocated, prolog instruction group. -// We don't want to expose the emitPrologIG unnecessarily (locations are actually pointers to +// We don't want to expose the prolog IG unnecessarily (locations are actually pointers to // emitter instruction groups). Since we know the offset of the start of the function/funclet, // where the prolog is, will be zero, we use a nullptr start location to indicate that. // @@ -431,26 +431,11 @@ void Compiler::DumpCfiInfo(bool isHotCode, UNATIVE_OFFSET Compiler::unwindGetCurrentOffset(FuncInfoDsc* func) { assert(compGeneratingProlog); - UNATIVE_OFFSET offset; - if (func->funKind == FUNC_ROOT) - { - offset = GetEmitter()->emitGetPrologOffsetEstimate(); - } - else - { - if (TargetArchitecture::IsX64 || - (TargetOS::IsUnix && - (TargetArchitecture::IsArmArch || TargetArchitecture::IsX86 || TargetArchitecture::IsLoongArch64))) - { - assert(func->startLoc != nullptr); - offset = func->startLoc->GetFuncletPrologOffset(GetEmitter()); - } - else - { - offset = 0; // TODO ??? - } - } + emitLocation* loc = func->startLoc; + insGroup* ig = (loc != nullptr) ? loc->GetIG() : nullptr; + assert((ig == nullptr) || (loc->GetInsOffset() == 0)); + UNATIVE_OFFSET offset = GetEmitter()->emitGetCurrentCodeOffsetFrom(ig); return offset; } From 309ff866c0225860244755540ff3bb9f12ffbd6b Mon Sep 17 00:00:00 2001 From: SingleAccretion Date: Sat, 4 Apr 2026 22:21:10 +0300 Subject: [PATCH 8/8] Fix jumps encoded as "+/- " By deleting them. --- src/coreclr/jit/codegenarm.cpp | 4 +- src/coreclr/jit/codegenarm64.cpp | 15 +-- src/coreclr/jit/codegencommon.cpp | 3 + src/coreclr/jit/codegenriscv64.cpp | 5 +- src/coreclr/jit/codegenxarch.cpp | 7 +- src/coreclr/jit/emit.cpp | 155 ++++++++++++++++++----------- src/coreclr/jit/emit.h | 26 ++--- src/coreclr/jit/emitarm.cpp | 54 ++-------- src/coreclr/jit/emitarm.h | 2 +- src/coreclr/jit/emitarm64.cpp | 61 ++---------- src/coreclr/jit/emitarm64.h | 2 +- src/coreclr/jit/emitriscv64.cpp | 84 +--------------- src/coreclr/jit/emitriscv64.h | 5 - src/coreclr/jit/emitxarch.cpp | 73 ++------------ src/coreclr/jit/emitxarch.h | 2 +- 15 files changed, 165 insertions(+), 333 deletions(-) diff --git a/src/coreclr/jit/codegenarm.cpp b/src/coreclr/jit/codegenarm.cpp index 8aa01b18a363ae..5e40cc803be554 100644 --- a/src/coreclr/jit/codegenarm.cpp +++ b/src/coreclr/jit/codegenarm.cpp @@ -2627,9 +2627,11 @@ void CodeGen::genZeroInitFrameUsingBlockInit(int untrLclHi, int untrLclLo, regNu } else { + BasicBlock* loopHead = genCreateTempLabel(); + genDefineInlineTempLabel(loopHead); GetEmitter()->emitIns_R_I(INS_stm, EA_PTRSIZE, rAddr, stmImm); // zero stack slots GetEmitter()->emitIns_R_I(INS_sub, EA_PTRSIZE, rCnt, 1, INS_FLAGS_SET); - GetEmitter()->emitIns_J(INS_bhi, NULL, -3); + GetEmitter()->emitIns_ShortJ(INS_bhi, loopHead); uCntBytes %= REGSIZE_BYTES * 2; } diff --git a/src/coreclr/jit/codegenarm64.cpp b/src/coreclr/jit/codegenarm64.cpp index 432d8328aef230..77586e9b123afc 100644 --- a/src/coreclr/jit/codegenarm64.cpp +++ b/src/coreclr/jit/codegenarm64.cpp @@ -1920,10 +1920,12 @@ void CodeGen::genZeroInitFrameUsingBlockInit(int untrLclHi, int untrLclLo, regNu GetEmitter()->emitIns_R_R_I_I(INS_bfm, EA_PTRSIZE, addrReg, REG_ZR, 0, 5); // addrReg points at the beginning of a cache line. + BasicBlock* loopHead = genCreateTempLabel(); + genDefineInlineTempLabel(loopHead); GetEmitter()->emitIns_R(INS_dczva, EA_PTRSIZE, addrReg); GetEmitter()->emitIns_R_R_I(INS_add, EA_PTRSIZE, addrReg, addrReg, 64); GetEmitter()->emitIns_R_R(INS_cmp, EA_PTRSIZE, addrReg, endAddrReg); - GetEmitter()->emitIns_J(INS_blo, NULL, -4); + GetEmitter()->emitIns_ShortJ(INS_blo, loopHead); addrReg = endAddrReg; bytesToWrite = 64; @@ -1942,12 +1944,14 @@ void CodeGen::genZeroInitFrameUsingBlockInit(int untrLclHi, int untrLclLo, regNu instGen_Set_Reg_To_Imm(EA_PTRSIZE, countReg, bytesToWrite - 64); + BasicBlock* loopHead = genCreateTempLabel(); + genDefineInlineTempLabel(loopHead); GetEmitter()->emitIns_R_R_R_I(INS_stp, EA_16BYTE, zeroSimdReg, zeroSimdReg, addrReg, 32); GetEmitter()->emitIns_R_R_R_I(INS_stp, EA_16BYTE, zeroSimdReg, zeroSimdReg, addrReg, 64, INS_OPTS_PRE_INDEX); GetEmitter()->emitIns_R_R_I(INS_subs, EA_PTRSIZE, countReg, countReg, 64); - GetEmitter()->emitIns_J(INS_bge, NULL, -4); + GetEmitter()->emitIns_ShortJ(INS_bge, loopHead); bytesToWrite %= 64; } @@ -5760,13 +5764,12 @@ void CodeGen::genAllocLclFrame(unsigned frameSize, regNumber initReg, bool* pIni instGen_Set_Reg_To_Imm(EA_PTRSIZE, rOffset, -(ssize_t)pageSize); instGen_Set_Reg_To_Imm(EA_PTRSIZE, rLimit, -(ssize_t)frameSize); - // There's a "virtual" label here. But we can't create a label in the prolog, so we use the magic - // `emitIns_J` with a negative `instrCount` to branch back a specific number of instructions. - + BasicBlock* loopHead = genCreateTempLabel(); + genDefineInlineTempLabel(loopHead); GetEmitter()->emitIns_R_R_R(INS_ldr, EA_4BYTE, REG_ZR, REG_SPBASE, rOffset); GetEmitter()->emitIns_R_R_I(INS_sub, EA_PTRSIZE, rOffset, rOffset, pageSize); GetEmitter()->emitIns_R_R(INS_cmp, EA_PTRSIZE, rLimit, rOffset); // If equal, we need to probe again - GetEmitter()->emitIns_J(INS_bls, NULL, -4); + GetEmitter()->emitIns_ShortJ(INS_bls, loopHead); *pInitRegZeroed = false; // The initReg does not contain zero diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index 487f7e9d2609d5..a8c5cda17cf4ef 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -6013,6 +6013,7 @@ void CodeGen::genGeneratePrologsAndEpilogs() GetEmitter()->emitStartPrologEpilogGeneration(); + m_compiler->compCurBB = m_compiler->fgFirstBB; // Set the current BB for label creation. gcInfo.gcResetForBB(); genFnProlog(); @@ -6040,6 +6041,8 @@ void CodeGen::genGeneratePrologsAndEpilogs() GetEmitter()->emitFinishPrologEpilogGeneration(); + m_compiler->compCurBB = nullptr; + #ifdef DEBUG if (verbose) { diff --git a/src/coreclr/jit/codegenriscv64.cpp b/src/coreclr/jit/codegenriscv64.cpp index 148a42c53c98b1..bf2c440e458279 100644 --- a/src/coreclr/jit/codegenriscv64.cpp +++ b/src/coreclr/jit/codegenriscv64.cpp @@ -792,14 +792,15 @@ void CodeGen::genZeroInitFrameUsingBlockInit(int untrLclHi, int untrLclLo, regNu GetEmitter()->emitIns_R_R_R(INS_add, EA_PTRSIZE, rEndAddr, rEndAddr, rAddr); } - // TODO-RISCV64-RVC: Remove hardcoded branch offset here + BasicBlock* loopHead = genCreateTempLabel(); + genDefineInlineTempLabel(loopHead); GetEmitter()->emitIns_R_R_I(INS_sd, EA_PTRSIZE, REG_R0, rAddr, padding); GetEmitter()->emitIns_R_R_I(INS_sd, EA_PTRSIZE, REG_R0, rAddr, padding + REGSIZE_BYTES); GetEmitter()->emitIns_R_R_I(INS_sd, EA_PTRSIZE, REG_R0, rAddr, padding + 2 * REGSIZE_BYTES); GetEmitter()->emitIns_R_R_I(INS_sd, EA_PTRSIZE, REG_R0, rAddr, padding + 3 * REGSIZE_BYTES); GetEmitter()->emitIns_R_R_I(INS_addi, EA_PTRSIZE, rAddr, rAddr, 4 * REGSIZE_BYTES); - GetEmitter()->emitIns_R_R_I(INS_bltu, EA_PTRSIZE, rAddr, rEndAddr, -5 << 2); + GetEmitter()->emitIns_J_cond_la(INS_bltu, loopHead, rAddr, rEndAddr); uLclBytes -= uLoopBytes; uAddrCurr = 0; diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp index 36a120250c6cf3..d7dc3cc25ee7ea 100644 --- a/src/coreclr/jit/codegenxarch.cpp +++ b/src/coreclr/jit/codegenxarch.cpp @@ -1691,7 +1691,7 @@ void CodeGen::inst_JMP(emitJumpKind jmp, BasicBlock* tgtBlock, bool isRemovableJ #endif #endif // !FEATURE_FIXED_OUT_ARGS - GetEmitter()->emitIns_J(emitter::emitJumpKindToIns(jmp), tgtBlock, 0, isRemovableJmpCandidate); + GetEmitter()->emitIns_J(emitter::emitJumpKindToIns(jmp), tgtBlock, isRemovableJmpCandidate); } //------------------------------------------------------------------------ @@ -11281,7 +11281,10 @@ void CodeGen::genZeroInitFrameUsingBlockInit(int untrLclHi, int untrLclLo, regNu // Set loop counter emit->emitIns_R_I(INS_mov, EA_PTRSIZE, initReg, -(ssize_t)blkSize); + // Loop start + BasicBlock* loopHead = genCreateTempLabel(); + genDefineInlineTempLabel(loopHead); emit->emitIns_ARX_R(simdMov, EA_ATTR(XMM_REGSIZE_BYTES), zeroSIMDReg, frameReg, initReg, 1, alignedLclHi); emit->emitIns_ARX_R(simdMov, EA_ATTR(XMM_REGSIZE_BYTES), zeroSIMDReg, frameReg, initReg, 1, alignedLclHi + XMM_REGSIZE_BYTES); @@ -11290,7 +11293,7 @@ void CodeGen::genZeroInitFrameUsingBlockInit(int untrLclHi, int untrLclLo, regNu emit->emitIns_R_I(INS_add, EA_PTRSIZE, initReg, XMM_REGSIZE_BYTES * 3); // Loop until counter is 0 - emit->emitIns_J(INS_jne, nullptr, -5); + emit->emitIns_ShortJ(INS_jne, loopHead); // initReg will be zero at end of the loop *pInitRegZeroed = true; diff --git a/src/coreclr/jit/emit.cpp b/src/coreclr/jit/emit.cpp index 2ca6e2f011810e..e424798a371564 100644 --- a/src/coreclr/jit/emit.cpp +++ b/src/coreclr/jit/emit.cpp @@ -1238,16 +1238,14 @@ insGroup* emitter::emitSavIG(bool emitAdd) assert(last == nullptr || last->idjOffs > nj->idjOffs); - if (ig->igFlags & IGF_FUNCLET_PROLOG) + if ((ig->igFlags & IGF_OUT_OF_ORDER_MASK) != 0) { - // Our funclet prologs have short jumps, if the prolog would ever have - // long jumps, then we'd have to insert the list in sorted order than - // just append to the emitJumpList. - noway_assert(nj->idjShort); - if (nj->idjShort) - { - continue; - } + // Our out of order groups have short jumps. If we ever need the shortening capability + // for these jumps, we'll need to insert them at the appropriate place in emitJumpList. + nj->idjNext = emitFixedSizeJumpList; + emitFixedSizeJumpList = nj; + assert(nj->idjShort); + continue; } // Append the new jump to the list @@ -1264,8 +1262,7 @@ insGroup* emitter::emitSavIG(bool emitAdd) if (last != nullptr) { // Append the jump(s) from this IG to the global list - bool prologJump = emitIGisInProlog(ig); - if ((emitJumpList == nullptr) || prologJump) + if (emitJumpList == nullptr) { last->idjNext = emitJumpList; emitJumpList = list; @@ -1276,10 +1273,7 @@ insGroup* emitter::emitSavIG(bool emitAdd) emitJumpLast->idjNext = list; } - if (!prologJump || (emitJumpLast == nullptr)) - { - emitJumpLast = last; - } + emitJumpLast = last; } } @@ -1372,6 +1366,7 @@ void emitter::emitBegFN(bool hasFramePtr /* We don't have any jumps */ emitJumpList = emitJumpLast = nullptr; + emitFixedSizeJumpList = nullptr; emitCurIGjmpList = nullptr; emitFwdJumps = false; @@ -2465,14 +2460,15 @@ void emitter::emitBegPrologEpilog(insGroup* igPh) emitThisGCrefRegs = emitInitGCrefRegs = igPh->igPhData->igPhInitGCrefRegs; emitThisByrefRegs = emitInitByrefRegs = igPh->igPhData->igPhInitByrefRegs; + // Set the current BB for label creation. + m_compiler->compCurBB = igPh->igPhData->igPhBB; + igPh->igPhData = nullptr; /* Create a non-placeholder group pointer that we'll now use */ - insGroup* ig = igPh; /* Set the current function using the function index we stored */ - m_compiler->funSetCurrentFunc(ig->igFuncIdx); /* Set the new IG as the place to generate code */ @@ -3651,6 +3647,30 @@ void emitter::emitSetSecondRetRegGCType(instrDescCGCA* id, emitAttr secondRetSiz #endif // MULTIREG_HAS_SECOND_GC_RET #ifndef TARGET_WASM +//------------------------------------------------------------------------ +// emitIns_ShortJ: Emit a 'forced' short jump. +// +// Jumps in prologs are hardcoded to be short since we don't shorten +// them in binding. +// +// Arguments: +// ins - The jump instruction +// dst - The destination label (must already be bound to an IG) +// +void emitter::emitIns_ShortJ(instruction ins, BasicBlock* dst) +{ + assert((emitCurIG->igFlags & IGF_OUT_OF_ORDER_MASK) != 0); + + emitIns_J(ins, dst); + + // We currently have a limitation where all jumps in the prolog must be short. + // This is mostly because we the prolog can't change size in emission, as we + // currently hardcode offsets from it into the unwind info during IG building. + // We also don't insert the jumps into the jump list in layout order. + instrDescJmp* jumpId = static_cast(emitLastIns); + jumpId->idjKeepLong = false; + emitSetShortJump(jumpId); +} /***************************************************************************** * @@ -4952,6 +4972,15 @@ void emitter::emitJumpDistBind() } #endif + // For the fixed-size jumps, we only need to bind them. + for (instrDescJmp* jmp = emitFixedSizeJumpList; jmp != nullptr; jmp = jmp->idjNext) + { + if (!jmp->idIsBound()) + { + emitBindJump(jmp); + } + } + instrDescJmp* jmp; UNATIVE_OFFSET minShortExtra; // The smallest offset greater than that required for a jump to be converted @@ -5269,40 +5298,7 @@ void emitter::emitJumpDistBind() else { /* First time we've seen this label, convert its target */ - -#ifdef DEBUG - if (EMITVERBOSE) - { - printf("Binding: "); - emitDispIns(jmp, false, false, false); - printf("Binding L_M%03u_" FMT_BB, m_compiler->compMethodID, jmp->idAddr()->iiaBBlabel->bbNum); - } -#endif // DEBUG - - 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); - } - } -#endif // DEBUG - - assert(jmp->idAddr()->iiaBBlabel->HasFlag(BBF_HAS_LABEL)); - assert(tgtIG); - - /* Record the bound target */ - - jmp->idAddr()->iiaIGlabel = tgtIG; - jmp->idSetIsBound(); + tgtIG = emitBindJump(jmp); } // We should not be jumping/branching across funclets/functions @@ -5723,6 +5719,54 @@ void emitter::emitJumpDistBind() emitCheckIGList(); #endif // DEBUG } + +//------------------------------------------------------------------------ +// emitBindJump: 'Bind' a jump by assigning its target label field. +// +// Arguments: +// jmp - The jump instruction +// +// Return Value: +// The target IG "jmp" was bound to. +// +insGroup* emitter::emitBindJump(instrDescJmp* jmp) +{ + assert(!jmp->idIsBound()); + +#ifdef DEBUG + if (EMITVERBOSE) + { + printf("Binding: "); + emitDispIns(jmp, false, false, false); + printf("Binding L_M%03u_" FMT_BB, m_compiler->compMethodID, jmp->idAddr()->iiaBBlabel->bbNum); + } +#endif // DEBUG + + insGroup* 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); + } + } +#endif // DEBUG + + assert(jmp->idAddr()->iiaBBlabel->HasFlag(BBF_HAS_LABEL)); + assert(tgtIG != nullptr); + + /* Record the bound target */ + jmp->idAddr()->iiaIGlabel = tgtIG; + jmp->idSetIsBound(); + return tgtIG; +} #endif #if FEATURE_LOOP_ALIGN @@ -6653,13 +6697,6 @@ void emitter::emitCheckFuncletBranch(instrDesc* jmp, insGroup* jmpIG) } #endif - if (jmp->idAddr()->iiaHasInstrCount()) - { - // Too hard to figure out funclets from just an instruction count - // You're on your own! - return; - } - #ifdef TARGET_ARM64 // No interest if it's not jmp. if (emitIsLoadLabel(jmp) || emitIsLoadConstant(jmp)) @@ -7683,7 +7720,6 @@ unsigned emitter::emitEndCodeGen(Compiler* comp, // Presumably we could also just call "emitOutputLJ(NULL, adr, jmp)", like for long jumps? *(short int*)(adr + writeableOffset) -= (short)adj; #elif defined(TARGET_ARM64) - assert(!jmp->idAddr()->iiaHasInstrCount()); emitOutputLJ(NULL, adr, jmp); #elif defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) // For LoongArch64 and RiscV64 `emitFwdJumps` is always false. @@ -7700,7 +7736,6 @@ unsigned emitter::emitEndCodeGen(Compiler* comp, #if defined(TARGET_XARCH) *(int*)(adr + writeableOffset) -= adj; #elif defined(TARGET_ARMARCH) - assert(!jmp->idAddr()->iiaHasInstrCount()); emitOutputLJ(NULL, adr, jmp); #elif defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) // For LoongArch64 and RiscV64 `emitFwdJumps` is always false. diff --git a/src/coreclr/jit/emit.h b/src/coreclr/jit/emit.h index 0ea660486f4607..08e77576c0e242 100644 --- a/src/coreclr/jit/emit.h +++ b/src/coreclr/jit/emit.h @@ -1058,8 +1058,9 @@ class emitter int iiaGetJitDataOffset() const; // iiaEncodedInstrCount and its accessor functions are used to specify an instruction - // count for jumps, instead of using a label and multiple blocks. This is used in the - // prolog as well as for IF_LARGEJMP pseudo-branch instructions. + // count for jumps, instead of using a label and multiple blocks. This is only used + // for IF_LARGEJMP pseudo-branch instructions printing. + // TODO-Cleanup: remove this. int iiaEncodedInstrCount; bool iiaHasInstrCount() const @@ -2797,6 +2798,7 @@ class emitter insFormat emitMapFmtAtoM(insFormat fmt); void emitHandleMemOp(GenTreeIndir* indir, instrDesc* id, insFormat fmt, instruction ins); void spillIntArgRegsToShadowSlots(); + void emitIns_ShortJ(instruction ins, BasicBlock* dst); #ifdef TARGET_XARCH bool emitIsInstrWritingToReg(instrDesc* id, regNumber reg); @@ -2821,17 +2823,7 @@ class emitter // can hold at least one of the largest instruction descriptor forms), since we can always overflow // to subsequent instruction groups. // - // The only place where this fixed instruction group size is a problem is in the main function prolog, - // where we only support a single instruction group, and no extension groups. We should really fix that. - // Thus, the buffer size needs to be large enough to hold the maximum number of instructions that - // can possibly be generated into the prolog instruction group. That is difficult to statically determine. - // - // If we do generate an overflow prolog group, we will hit a NOWAY assert and fall back to MinOpts. - // This should reduce the number of instructions generated into the prolog. - // - // Note that OSR prologs require additional code not seen in normal prologs. - // - // Also, note that DEBUG and non-DEBUG builds have different instrDesc sizes, and there are multiple + // Note that DEBUG and non-DEBUG builds have different instrDesc sizes, and there are multiple // sizes of instruction descriptors, so the number of instructions that will fit in the largest // instruction group depends on the instruction mix as well as DEBUG/non-DEBUG build type. See the // EMITTER_STATS output for various statistics related to this. @@ -2863,9 +2855,11 @@ class emitter insGroup* emitIGlist; // first instruction group insGroup* emitIGlast; // last instruction group - instrDescJmp* emitJumpList; // list of local jumps in method - instrDescJmp* emitJumpLast; // last of local jumps in method - void emitJumpDistBind(); // Bind all the local jumps in method + instrDescJmp* emitJumpList; // list of local jumps in method + instrDescJmp* emitJumpLast; // last of local jumps in method + instrDescJmp* emitFixedSizeJumpList; // list of local jumps not eligible for shortening + void emitJumpDistBind(); // Bind all the local jumps in method + insGroup* emitBindJump(instrDescJmp* jmp); bool emitContainsRemovableJmpCandidates; void emitRemoveJumpToNextInst(); // try to remove unconditional jumps to the next instruction diff --git a/src/coreclr/jit/emitarm.cpp b/src/coreclr/jit/emitarm.cpp index 1f5698a0afb01e..e2ebec55fc6695 100644 --- a/src/coreclr/jit/emitarm.cpp +++ b/src/coreclr/jit/emitarm.cpp @@ -4316,8 +4316,6 @@ void emitter::emitSetMediumJump(instrDescJmp* id) /***************************************************************************** * * Add a jmp instruction. - * When dst is NULL, instrCount specifies number of instructions - * to jump: positive is forward, negative is backward. * Unconditional branches have two sizes: short and long. * Conditional branches have three sizes: short, medium, and long. A long * branch is a pseudo-instruction that represents two instructions: @@ -4325,20 +4323,12 @@ void emitter::emitSetMediumJump(instrDescJmp* id) * branch. Thus, we can handle branch offsets of imm24 instead of just imm20. */ -void emitter::emitIns_J(instruction ins, BasicBlock* dst, int instrCount /* = 0 */) +void emitter::emitIns_J(instruction ins, BasicBlock* dst) { - insFormat fmt = IF_NONE; - - if (dst != NULL) - { - assert(dst->HasFlag(BBF_HAS_LABEL)); - } - else - { - assert(instrCount != 0); - } + assert(dst->HasFlag(BBF_HAS_LABEL)); /* Figure out the encoding format of the instruction */ + insFormat fmt = IF_NONE; switch (ins) { case INS_b: @@ -4384,25 +4374,16 @@ void emitter::emitIns_J(instruction ins, BasicBlock* dst, int instrCount /* = 0 /* Assume the jump will be long */ - id->idjShort = 0; - if (dst != NULL) - { - id->idAddr()->iiaBBlabel = dst; - id->idjKeepLong = (ins == INS_bl) || m_compiler->fgInDifferentRegions(m_compiler->compCurBB, dst); + id->idjShort = 0; + id->idAddr()->iiaBBlabel = dst; + id->idjKeepLong = (ins == INS_bl) || m_compiler->fgInDifferentRegions(m_compiler->compCurBB, dst); #ifdef DEBUG - if (m_compiler->opts.compLongAddress) // Force long branches - id->idjKeepLong = 1; -#endif // DEBUG - } - else + if (m_compiler->opts.compLongAddress) // Force long branches { - id->idAddr()->iiaSetInstrCount(instrCount); - id->idjKeepLong = false; - /* This jump must be short */ - emitSetShortJump(id); - id->idSetIsBound(); + id->idjKeepLong = 1; } +#endif // DEBUG /* Record the jump's IG and offset within it */ @@ -5302,22 +5283,7 @@ BYTE* emitter::emitOutputLJ(insGroup* ig, BYTE* dst, instrDesc* i) /* Figure out the distance to the target */ srcOffs = emitCurCodeOffs(dst); - if (id->idAddr()->iiaHasInstrCount()) - { - assert(ig != NULL); - int instrCount = id->idAddr()->iiaGetInstrCount(); - unsigned insNum = emitFindInsNum(ig, id); - if (instrCount < 0) - { - // Backward branches using instruction count must be within the same instruction group. - assert(insNum + 1 >= (unsigned)(-instrCount)); - } - dstOffs = ig->igOffs + emitFindOffset(ig, (insNum + 1 + instrCount)); - } - else - { - dstOffs = id->idAddr()->iiaIGlabel->igOffs; - } + dstOffs = id->idAddr()->iiaIGlabel->igOffs; if (relAddr) { diff --git a/src/coreclr/jit/emitarm.h b/src/coreclr/jit/emitarm.h index 6e3eb5793c54aa..44fe1aac177d56 100644 --- a/src/coreclr/jit/emitarm.h +++ b/src/coreclr/jit/emitarm.h @@ -205,7 +205,7 @@ inline static unsigned getBitWidth(emitAttr size) /* 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 */ diff --git a/src/coreclr/jit/emitarm64.cpp b/src/coreclr/jit/emitarm64.cpp index 483896dc10e3ab..0873aa55f1a4c3 100644 --- a/src/coreclr/jit/emitarm64.cpp +++ b/src/coreclr/jit/emitarm64.cpp @@ -9111,21 +9111,12 @@ void emitter::emitIns_J_R_I(instruction ins, emitAttr attr, BasicBlock* dst, reg appendToCurIG(id); } -void emitter::emitIns_J(instruction ins, BasicBlock* dst, int instrCount) +void emitter::emitIns_J(instruction ins, BasicBlock* dst) { - insFormat fmt = IF_NONE; - - if (dst != nullptr) - { - assert(dst->HasFlag(BBF_HAS_LABEL)); - } - else - { - assert(instrCount != 0); - } + assert(dst->HasFlag(BBF_HAS_LABEL)); /* Figure out the encoding format of the instruction */ - + insFormat fmt = IF_NONE; switch (ins) { case INS_bl_local: @@ -9172,29 +9163,15 @@ void emitter::emitIns_J(instruction ins, BasicBlock* dst, int instrCount) } #endif // DEBUG - if (dst != nullptr) - { - id->idAddr()->iiaBBlabel = dst; - - // Skip unconditional jump that has a single form. - // The target needs to be relocated. - id->idjKeepLong = m_compiler->fgInDifferentRegions(m_compiler->compCurBB, dst); + id->idAddr()->iiaBBlabel = dst; + id->idjKeepLong = m_compiler->fgInDifferentRegions(m_compiler->compCurBB, dst); #ifdef DEBUG - if (m_compiler->opts.compLongAddress) // Force long branches - { - id->idjKeepLong = true; - } -#endif // DEBUG - } - else + if (m_compiler->opts.compLongAddress) // Force long branches { - id->idAddr()->iiaSetInstrCount(instrCount); - id->idjKeepLong = false; - /* This jump must be short */ - emitSetShortJump(id); - id->idSetIsBound(); + id->idjKeepLong = true; } +#endif // DEBUG /* Record the jump's IG and offset within it */ @@ -10540,26 +10517,8 @@ BYTE* emitter::emitOutputLJ(insGroup* ig, BYTE* dst, instrDesc* i) assert(loadLabel || isJump); - if (id->idAddr()->iiaHasInstrCount()) - { - assert(ig != NULL); - int instrCount = id->idAddr()->iiaGetInstrCount(); - unsigned insNum = emitFindInsNum(ig, id); - if (instrCount < 0) - { - // Backward branches using instruction count must be within the same instruction group. - assert(insNum + 1 >= (unsigned)(-instrCount)); - } - - dstOffs = ig->igOffs + emitFindOffset(ig, (insNum + 1 + instrCount)); - dstAddr = emitOffsetToPtr(dstOffs); - } - else - { - dstOffs = id->idAddr()->iiaIGlabel->igOffs; - dstAddr = emitOffsetToPtr(dstOffs); - } - + dstOffs = id->idAddr()->iiaIGlabel->igOffs; + dstAddr = emitOffsetToPtr(dstOffs); distVal = (ssize_t)(dstAddr - srcAddr); if (dstOffs <= srcOffs) diff --git a/src/coreclr/jit/emitarm64.h b/src/coreclr/jit/emitarm64.h index 49c636f2168e4b..e8194b28272abf 100644 --- a/src/coreclr/jit/emitarm64.h +++ b/src/coreclr/jit/emitarm64.h @@ -1441,7 +1441,7 @@ inline static ssize_t computeRelPageAddr(size_t dstAddr, size_t srcAddr) /* 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 */ diff --git a/src/coreclr/jit/emitriscv64.cpp b/src/coreclr/jit/emitriscv64.cpp index 4459866f2445b2..a0a0d4aecaf8f6 100644 --- a/src/coreclr/jit/emitriscv64.cpp +++ b/src/coreclr/jit/emitriscv64.cpp @@ -771,22 +771,6 @@ void emitter::emitIns_R_R_I( code |= (((imm >> 5) & 0x7f) << 25) | ((imm & 0x1f) << 7); // imm break; - case MajorOpcode::Branch: - assert(isGeneralRegister(reg1)); - assert(isGeneralRegister(reg2)); - assert(isValidSimm13(imm)); - assert(!(imm & 3)); - code |= reg1 << 15; - code |= reg2 << 20; - code |= ((imm >> 11) & 0x1) << 7; - code |= ((imm >> 1) & 0xf) << 8; - code |= ((imm >> 5) & 0x3f) << 25; - code |= ((imm >> 12) & 0x1) << 31; - // TODO-RISCV64: Move jump logic to emitIns_J - // TODO-RISC64-RVC: Remove this once all branches uses emitIns_J - id->idAddr()->iiaSetInstrCount(static_cast(imm / sizeof(code_t))); - break; - case MajorOpcode::System: assert(ins == INS_csrrs || ins == INS_csrrw || ins == INS_csrrc); assert(isGeneralRegisterOrR0(reg1)); @@ -2785,29 +2769,6 @@ unsigned emitter::emitOutput_JTypeInstr(BYTE* dst, instruction ins, regNumber rd return emitOutput_Instr(dst, insEncodeJTypeInstr(insCode, rd, imm21)); } -void emitter::emitOutputInstrJumpDistanceHelper(const insGroup* ig, - instrDescJmp* jmp, - UNATIVE_OFFSET& dstOffs, - const BYTE*& dstAddr) const -{ - if (jmp->idAddr()->iiaHasInstrCount()) - { - assert(ig != nullptr); - int instrCount = jmp->idAddr()->iiaGetInstrCount(); - unsigned insNum = emitFindInsNum(ig, jmp); - if (instrCount < 0) - { - // Backward branches using instruction count must be within the same instruction group. - assert(insNum + 1 >= static_cast(-instrCount)); - } - dstOffs = ig->igOffs + emitFindOffset(ig, insNum + 1 + instrCount); - dstAddr = emitOffsetToPtr(dstOffs); - return; - } - dstOffs = jmp->idAddr()->iiaIGlabel->igOffs; - dstAddr = emitOffsetToPtr(dstOffs); -} - /***************************************************************************** * * Calculates a current jump instruction distance @@ -2821,9 +2782,8 @@ ssize_t emitter::emitOutputInstrJumpDistance(const BYTE* src, const insGroup* ig assert(!jmp->idAddr()->iiaIsJitDataOffset()); // not used by riscv64 impl - UNATIVE_OFFSET dstOffs{}; - const BYTE* dstAddr = nullptr; - emitOutputInstrJumpDistanceHelper(ig, jmp, dstOffs, dstAddr); + const BYTE* dstOffs = jmp->idAddr()->iiaIGlabel->igOffs; + UNATIVE_OFFSET dstAddr = emitOffsetToPtr(dstOffs); ssize_t distVal = static_cast(dstAddr - srcAddr); @@ -3540,35 +3500,6 @@ bool emitter::emitDispBranchInstrType(unsigned opcode2, bool is_zero_reg, bool& return true; } -void emitter::emitDispBranchOffset(const instrDesc* id, const insGroup* ig, bool printOffsetPlaceholder) const -{ - if (printOffsetPlaceholder) - { - printf("pc+?? instructions"); - return; - } - - int instrCount = id->idAddr()->iiaGetInstrCount(); - if (ig == nullptr) - { - printf("pc%+d instructions", instrCount); - return; - } - unsigned insNum = emitFindInsNum(ig, id); - - if (ig->igInsCnt < insNum + 1 + instrCount) - { - // TODO-RISCV64-BUG: This should be a labeled offset but does not contain an iiaIGlabel - printf("pc%+d instructions", instrCount); - return; - } - - UNATIVE_OFFSET srcOffs = ig->igOffs + emitFindOffset(ig, insNum + 1); - UNATIVE_OFFSET dstOffs = ig->igOffs + emitFindOffset(ig, insNum + 1 + instrCount); - ssize_t relOffs = static_cast(emitOffsetToPtr(dstOffs) - emitOffsetToPtr(srcOffs)); - printf("pc%+d (%d instructions)", static_cast(relOffs), instrCount); -} - void emitter::emitDispBranchLabel(const instrDesc* id) const { if (id->idIsBound()) @@ -3596,16 +3527,7 @@ bool emitter::emitDispBranch(unsigned opcode2, printf("%s, ", RegNames[rs2]); } assert(id != nullptr); - if (id->idAddr()->iiaHasInstrCount()) - { - // Branch is jumping to some non-labeled offset - emitDispBranchOffset(id, ig, printOffsetPlaceholder); - } - else - { - // Branch is jumping to the labeled offset - emitDispBranchLabel(id); - } + emitDispBranchLabel(id); printf("\n"); return true; } diff --git a/src/coreclr/jit/emitriscv64.h b/src/coreclr/jit/emitriscv64.h index 904e7ecbf061ff..1a24593d669c4d 100644 --- a/src/coreclr/jit/emitriscv64.h +++ b/src/coreclr/jit/emitriscv64.h @@ -186,7 +186,6 @@ bool emitDispBranch(unsigned opcode2, const instrDesc* id, const insGroup* ig, bool printOffsetPlaceholder) const; -void emitDispBranchOffset(const instrDesc* id, const insGroup* ig, bool printOffsetPlaceholder) const; void emitDispBranchLabel(const instrDesc* id) const; bool emitDispBranchInstrType(unsigned opcode2, bool is_zero_reg, bool& print_second_reg) const; void emitDispIllegalInstruction(code_t instructionCode); @@ -208,10 +207,6 @@ void emitInsLoadStoreOp(instruction ins, emitAttr attr, regNumber dataReg, GenTr unsigned emitOutput_Instr(BYTE* dst, code_t code) const; ssize_t emitOutputInstrJumpDistance(const BYTE* src, const insGroup* ig, instrDescJmp* jmp); -void emitOutputInstrJumpDistanceHelper(const insGroup* ig, - instrDescJmp* jmp, - UNATIVE_OFFSET& dstOffs, - const BYTE*& dstAddr) const; // Method to do check if mov is redundant with respect to the last instruction. // If yes, the caller of this method can choose to omit current mov instruction. diff --git a/src/coreclr/jit/emitxarch.cpp b/src/coreclr/jit/emitxarch.cpp index e1dc9389252912..4a01a971981e02 100644 --- a/src/coreclr/jit/emitxarch.cpp +++ b/src/coreclr/jit/emitxarch.cpp @@ -10870,14 +10870,9 @@ void emitter::emitSetShortJump(instrDescJmp* id) /***************************************************************************** * * Add a jmp instruction. - * When dst is NULL, instrCount specifies number of instructions - * to jump: positive is forward, negative is backward. */ -void emitter::emitIns_J(instruction ins, - BasicBlock* dst, - int instrCount /* = 0 */, - bool isRemovableJmpCandidate /* = false */) +void emitter::emitIns_J(instruction ins, BasicBlock* dst, bool isRemovableJmpCandidate /* = false */) { #ifdef TARGET_AMD64 // Check emitter::emitLastIns before it is updated @@ -10887,18 +10882,7 @@ void emitter::emitIns_J(instruction ins, UNATIVE_OFFSET sz; instrDescJmp* id = emitNewInstrJmp(); - if (dst != nullptr) - { - assert(dst->HasFlag(BBF_HAS_LABEL)); - assert(instrCount == 0); - } - else - { - /* Only allow non-label jmps in prolog */ - assert(emitIGisInProlog(emitCurIG)); - assert(instrCount != 0); - } - + assert(dst->HasFlag(BBF_HAS_LABEL)); id->idIns(ins); id->idInsFmt(IF_LABEL); @@ -10926,24 +10910,12 @@ void emitter::emitIns_J(instruction ins, id->idjIsRemovableJmpCandidate = 0; } - id->idjShort = 0; - if (dst != nullptr) - { - /* Assume the jump will be long */ - id->idAddr()->iiaBBlabel = dst; - id->idjKeepLong = m_compiler->fgInDifferentRegions(m_compiler->compCurBB, dst); - } - else - { - id->idAddr()->iiaSetInstrCount(instrCount); - id->idjKeepLong = false; - /* This jump must be short */ - emitSetShortJump(id); - id->idSetIsBound(); - } + /* Assume the jump will be long */ + id->idjShort = 0; + id->idAddr()->iiaBBlabel = dst; + id->idjKeepLong = m_compiler->fgInDifferentRegions(m_compiler->compCurBB, dst); /* Record the jump's IG and offset within it */ - id->idjIG = emitCurIG; id->idjOffs = emitCurIGsize; @@ -13935,14 +13907,7 @@ void emitter::emitDispIns( if (id->idIsBound()) { - if (id->idAddr()->iiaHasInstrCount()) - { - printf("%3d instr", id->idAddr()->iiaGetInstrCount()); - } - else - { - emitPrintLabel(id->idAddr()->iiaIGlabel); - } + emitPrintLabel(id->idAddr()->iiaIGlabel); } else { @@ -17680,27 +17645,11 @@ BYTE* emitter::emitOutputLJ(insGroup* ig, BYTE* dst, instrDesc* i) srcOffs = emitCurCodeOffs(dst); srcAddr = emitOffsetToPtr(srcOffs); - if (id->idAddr()->iiaHasInstrCount()) + dstOffs = id->idAddr()->iiaIGlabel->igOffs; + dstAddr = emitOffsetToPtr(dstOffs); + if (!relAddr) { - assert(ig != nullptr); - int instrCount = id->idAddr()->iiaGetInstrCount(); - unsigned insNum = emitFindInsNum(ig, id); - if (instrCount < 0) - { - // Backward branches using instruction count must be within the same instruction group. - assert(insNum + 1 >= (unsigned)(-instrCount)); - } - dstOffs = ig->igOffs + emitFindOffset(ig, (insNum + 1 + instrCount)); - dstAddr = emitOffsetToPtr(dstOffs); - } - else - { - dstOffs = id->idAddr()->iiaIGlabel->igOffs; - dstAddr = emitOffsetToPtr(dstOffs); - if (!relAddr) - { - srcAddr = nullptr; - } + srcAddr = nullptr; } distVal = (ssize_t)(dstAddr - srcAddr); diff --git a/src/coreclr/jit/emitxarch.h b/src/coreclr/jit/emitxarch.h index 126d1a67a3593b..1173184a4c55a4 100644 --- a/src/coreclr/jit/emitxarch.h +++ b/src/coreclr/jit/emitxarch.h @@ -891,7 +891,7 @@ inline emitAttr emitDecodeScale(unsigned ensz) const /* Output target-independent instructions */ /************************************************************************/ -void emitIns_J(instruction ins, BasicBlock* dst, int instrCount = 0, bool isRemovableJmpCandidate = false); +void emitIns_J(instruction ins, BasicBlock* dst, bool isRemovableJmpCandidate = false); /************************************************************************/ /* The public entry points to output instructions */