From d067431ed81ab4a2068863474ab9a284cb9ccf93 Mon Sep 17 00:00:00 2001 From: Bruce Forstall Date: Thu, 9 Feb 2023 10:53:37 -0700 Subject: [PATCH] Introduce insGroup/instrDesc backwards navigation (#80840) * Introduce insGroup/instrDesc backwards navigation Currently, it is only possible to walk forwards in insGroup/instrDesc data: insGroup are in a single-linked list, and variable-sized instrDesc are packed into a memory array where function `emitSizeOfInsDsc()` can tell you the size of an instrDesc, and `emitAdvanceInstrDesc()` can advance an `instrDesc*` forward, but there is no facility for walking to the previous instrDesc. This change introduces a `igPrev` pointer so the insGroup list is doubly-linked. Also, each instrDesc is annotated with 4 or 5 bits of data indicating how large the previous instruction in the buffer is, so it is possible to find it by subtracting that size from a `instrDesc*`. Also, each insGroup maintains an `igLastIns` pointer to the last instruction in the group, to allow seeding the backwards walk. The instrDesc backwards walk data is taken from the "small constant" area, so some small constants get pushed to using larger instrDesc sizes. Walking backwards is done using `emitGetLastIns()` to find the last instruction in the function, and `emitPrevID()` to walk to the previous one. This information is currently only used in the JitDump. Basic measurement of the benchmarks SPMI collection showed about a 2.5% increase in emitter memory usage. * Fix one type * Put code under EMIT_BACKWARDS_NAVIGATION define * Disable backwards navigation --- src/coreclr/jit/emit.cpp | 236 ++++++++++++++++++++++++---- src/coreclr/jit/emit.h | 172 ++++++++++++++------ src/coreclr/jit/emitloongarch64.cpp | 2 +- 3 files changed, 331 insertions(+), 79 deletions(-) diff --git a/src/coreclr/jit/emit.cpp b/src/coreclr/jit/emit.cpp index 9acf7268e08a5f..33adf785ad2c30 100644 --- a/src/coreclr/jit/emit.cpp +++ b/src/coreclr/jit/emit.cpp @@ -218,6 +218,12 @@ unsigned emitter::emitTotalDescAlignCnt; void emitterStaticStats(FILE* fout) { + // The IG buffer size depends on whether we are storing a debug info pointer or not. For our purposes + // here, do not include that. + + const size_t igBuffSize = + (SC_IG_BUFFER_NUM_SMALL_DESCS * SMALL_IDSC_SIZE) + (SC_IG_BUFFER_NUM_LARGE_DESCS * sizeof(emitter::instrDesc)); + // insGroup members insGroup* igDummy = nullptr; @@ -226,6 +232,10 @@ void emitterStaticStats(FILE* fout) fprintf(fout, "insGroup:\n"); fprintf(fout, "Offset / size of igNext = %2zu / %2zu\n", offsetof(insGroup, igNext), sizeof(igDummy->igNext)); +#if EMIT_BACKWARDS_NAVIGATION + fprintf(fout, "Offset / size of igPrev = %2zu / %2zu\n", offsetof(insGroup, igPrev), + sizeof(igDummy->igPrev)); +#endif // EMIT_BACKWARDS_NAVIGATION #ifdef DEBUG fprintf(fout, "Offset / size of igSelf = %2zu / %2zu\n", offsetof(insGroup, igSelf), sizeof(igDummy->igSelf)); @@ -244,6 +254,10 @@ void emitterStaticStats(FILE* fout) sizeof(igDummy->igData)); fprintf(fout, "Offset / size of igPhData = %2zu / %2zu\n", offsetof(insGroup, igPhData), sizeof(igDummy->igPhData)); +#if EMIT_BACKWARDS_NAVIGATION + fprintf(fout, "Offset / size of igLastIns = %2zu / %2zu\n", offsetof(insGroup, igLastIns), + sizeof(igDummy->igLastIns)); +#endif // EMIT_BACKWARDS_NAVIGATION #if EMIT_TRACK_STACK_DEPTH fprintf(fout, "Offset / size of igStkLvl = %2zu / %2zu\n", offsetof(insGroup, igStkLvl), sizeof(igDummy->igStkLvl)); @@ -282,6 +296,9 @@ void emitterStaticStats(FILE* fout) // fprintf(fout, "Size of _idAddrUnion= %2zu\n", sizeof(((emitter::instrDesc*)0)->_idAddrUnion)); fprintf(fout, "Size of instrDescJmp = %2zu\n", sizeof(emitter::instrDescJmp)); +#if FEATURE_LOOP_ALIGN + fprintf(fout, "Size of instrDescAlign = %2zu\n", sizeof(emitter::instrDescAlign)); +#endif // FEATURE_LOOP_ALIGN #if !defined(TARGET_ARM64) fprintf(fout, "Size of instrDescLbl = %2zu\n", sizeof(emitter::instrDescLbl)); #endif // !defined(TARGET_ARM64) @@ -298,23 +315,23 @@ void emitterStaticStats(FILE* fout) #endif // TARGET_ARM fprintf(fout, "\n"); - fprintf(fout, "SC_IG_BUFFER_SIZE = %2zu\n", SC_IG_BUFFER_SIZE); - fprintf(fout, "SMALL_IDSC_SIZE per IG buffer = %2zu\n", SC_IG_BUFFER_SIZE / SMALL_IDSC_SIZE); - fprintf(fout, "instrDesc per IG buffer = %2zu\n", SC_IG_BUFFER_SIZE / sizeof(emitter::instrDesc)); - fprintf(fout, "instrDescJmp per IG buffer = %2zu\n", SC_IG_BUFFER_SIZE / sizeof(emitter::instrDescJmp)); + fprintf(fout, "igBuffSize = %2zu\n", igBuffSize); + fprintf(fout, "SMALL_IDSC_SIZE per IG buffer = %2zu\n", igBuffSize / SMALL_IDSC_SIZE); + fprintf(fout, "instrDesc per IG buffer = %2zu\n", igBuffSize / sizeof(emitter::instrDesc)); + fprintf(fout, "instrDescJmp per IG buffer = %2zu\n", igBuffSize / sizeof(emitter::instrDescJmp)); #if !defined(TARGET_ARM64) - fprintf(fout, "instrDescLbl per IG buffer = %2zu\n", SC_IG_BUFFER_SIZE / sizeof(emitter::instrDescLbl)); + fprintf(fout, "instrDescLbl per IG buffer = %2zu\n", igBuffSize / sizeof(emitter::instrDescLbl)); #endif // !defined(TARGET_ARM64) - fprintf(fout, "instrDescCns per IG buffer = %2zu\n", SC_IG_BUFFER_SIZE / sizeof(emitter::instrDescCns)); - fprintf(fout, "instrDescDsp per IG buffer = %2zu\n", SC_IG_BUFFER_SIZE / sizeof(emitter::instrDescDsp)); - fprintf(fout, "instrDescCnsDsp per IG buffer = %2zu\n", SC_IG_BUFFER_SIZE / sizeof(emitter::instrDescCnsDsp)); + fprintf(fout, "instrDescCns per IG buffer = %2zu\n", igBuffSize / sizeof(emitter::instrDescCns)); + fprintf(fout, "instrDescDsp per IG buffer = %2zu\n", igBuffSize / sizeof(emitter::instrDescDsp)); + fprintf(fout, "instrDescCnsDsp per IG buffer = %2zu\n", igBuffSize / sizeof(emitter::instrDescCnsDsp)); #ifdef TARGET_XARCH - fprintf(fout, "instrDescAmd per IG buffer = %2zu\n", SC_IG_BUFFER_SIZE / sizeof(emitter::instrDescAmd)); - fprintf(fout, "instrDescCnsAmd per IG buffer = %2zu\n", SC_IG_BUFFER_SIZE / sizeof(emitter::instrDescCnsAmd)); + fprintf(fout, "instrDescAmd per IG buffer = %2zu\n", igBuffSize / sizeof(emitter::instrDescAmd)); + fprintf(fout, "instrDescCnsAmd per IG buffer = %2zu\n", igBuffSize / sizeof(emitter::instrDescCnsAmd)); #endif // TARGET_XARCH - fprintf(fout, "instrDescCGCA per IG buffer = %2zu\n", SC_IG_BUFFER_SIZE / sizeof(emitter::instrDescCGCA)); + fprintf(fout, "instrDescCGCA per IG buffer = %2zu\n", igBuffSize / sizeof(emitter::instrDescCGCA)); #ifdef TARGET_ARM - fprintf(fout, "instrDescReloc per IG buffer = %2zu\n", SC_IG_BUFFER_SIZE / sizeof(emitter::instrDescReloc)); + fprintf(fout, "instrDescReloc per IG buffer = %2zu\n", igBuffSize / sizeof(emitter::instrDescReloc)); #endif // TARGET_ARM fprintf(fout, "\n"); @@ -715,6 +732,10 @@ void emitter::emitGenIG(insGroup* ig) } emitCurIGfreeNext = emitCurIGfreeBase; + +#if EMIT_BACKWARDS_NAVIGATION + emitLastInsFullSize = 0; +#endif // EMIT_BACKWARDS_NAVIGATION } /***************************************************************************** @@ -734,7 +755,7 @@ void emitter::emitNewIG() if (emitComp->verbose) { printf("Created:\n "); - emitDispIG(ig, nullptr, /* displayInstructions */ false, /* displayLocation */ false); + emitDispIG(ig, /* displayFunc */ false, /* displayInstructions */ false, /* displayLocation */ false); } #endif // DEBUG } @@ -908,7 +929,7 @@ insGroup* emitter::emitSavIG(bool emitAdd) if (emitComp->verbose) { printf("Saved:\n "); - emitDispIG(ig, nullptr, /* displayInstructions */ false, /* displayLocation */ false); + emitDispIG(ig, /* displayFunc */ false, /* displayInstructions */ false, /* displayLocation */ false); } else { @@ -1075,9 +1096,13 @@ insGroup* emitter::emitSavIG(bool emitAdd) emitLastIns = (instrDesc*)((BYTE*)id + ((BYTE*)emitLastIns - (BYTE*)emitCurIGfreeBase)); emitLastInsIG = ig; + +#if EMIT_BACKWARDS_NAVIGATION + ig->igLastIns = emitLastIns; +#endif // EMIT_BACKWARDS_NAVIGATION } - // Reset the buffer free pointers + // Reset the buffer free pointer. emitCurIGfreeNext = emitCurIGfreeBase; @@ -1232,6 +1257,11 @@ void emitter::emitBegFN(bool hasFramePtr ig->igNext = nullptr; +#if EMIT_BACKWARDS_NAVIGATION + emitLastInsFullSize = 0; + ig->igPrev = nullptr; +#endif + #ifdef DEBUG emitScratchSigInfo = nullptr; #endif // DEBUG @@ -1546,12 +1576,22 @@ void* emitter::emitAllocAnyInstr(size_t sz, emitAttr opsz) /* Grab the space for the instruction */ emitLastIns = id = (instrDesc*)(emitCurIGfreeNext + m_debugInfoSize); - emitLastInsIG = emitCurIG; - emitCurIGfreeNext += fullSize; + +#if EMIT_BACKWARDS_NAVIGATION + emitCurIG->igLastIns = id; +#endif // EMIT_BACKWARDS_NAVIGATION assert(sz >= sizeof(void*)); memset(id, 0, sz); +#if EMIT_BACKWARDS_NAVIGATION + id->idSetPrevSize(emitLastInsFullSize); + emitLastInsFullSize = (unsigned)fullSize; +#endif // EMIT_BACKWARDS_NAVIGATION + + emitLastInsIG = emitCurIG; + emitCurIGfreeNext += fullSize; + // These fields should have been zero-ed by the above assert(id->idReg1() == regNumber(0)); assert(id->idReg2() == regNumber(0)); @@ -1665,10 +1705,23 @@ void emitter::emitCheckIGList() { assert(emitPrologIG != nullptr); +#if EMIT_BACKWARDS_NAVIGATION + struct IGIDPair + { + insGroup* ig; + instrDesc* id; + }; + jitstd::list insList(emitComp->getAllocator(CMK_DebugOnly)); +#endif // EMIT_BACKWARDS_NAVIGATION + size_t currentOffset = 0; for (insGroup *currIG = emitIGlist, *prevIG = nullptr; currIG != nullptr; prevIG = currIG, currIG = currIG->igNext) { +#if EMIT_BACKWARDS_NAVIGATION + assert(prevIG == currIG->igPrev); +#endif // EMIT_BACKWARDS_NAVIGATION + if (currIG->igOffs != currentOffset) { printf("IG%02u has offset %08X, expected %08X\n", currIG->igNum, currIG->igOffs, currentOffset); @@ -1746,6 +1799,25 @@ void emitter::emitCheckIGList() // // change any semantics of the included instructions. // assert((currIG->igFlags & IGF_NOGCINTERRUPT) == (prevIG->igFlags & IGF_NOGCINTERRUPT)); } + +#if EMIT_BACKWARDS_NAVIGATION + // Check that the instrDesc "prev" pointers are all correct. + unsigned insCnt = currIG->igInsCnt; + if (insCnt > 0) + { + instrDesc* id = emitFirstInstrDesc(currIG->igData); + insList.push_front({currIG, id}); + assert(id->idPrevSize() == 0); + for (unsigned i = 0; i < insCnt - 1; i++) + { + size_t idSize = emitSizeOfInsDsc(id); + size_t idPrevSize = idSize + m_debugInfoSize; + emitAdvanceInstrDesc(&id, idSize); + insList.push_front({currIG, id}); + assert(id->idPrevSize() == idPrevSize); + } + } +#endif // EMIT_BACKWARDS_NAVIGATION } if (emitTotalCodeSize != 0 && emitTotalCodeSize != currentOffset) @@ -1753,6 +1825,28 @@ void emitter::emitCheckIGList() printf("Total code size is %08X, expected %08X\n", emitTotalCodeSize, currentOffset); assert(!"bad total code size"); } + +#if EMIT_BACKWARDS_NAVIGATION + + // Check that walking the instrDescs backwards using `emitPrevID` is correct. Compare the backwards + // walk against a list of IDs that was constructed above while walking forwards (but constructed in + // reverse order). + + insGroup* ig; + instrDesc* id; + if (emitGetLastIns(&ig, &id)) + { + jitstd::list::iterator nextPair = insList.begin(); + jitstd::list::iterator endPair = insList.end(); + do + { + assert(nextPair != endPair); + assert(ig == nextPair->ig); + assert(id == nextPair->id); + ++nextPair; + } while (emitPrevID(ig, id)); + } +#endif // EMIT_BACKWARDS_NAVIGATION } #endif // DEBUG @@ -3179,6 +3273,76 @@ void emitter::emitUnwindNopPadding(emitLocation* locFrom, Compiler* comp) #endif // TARGET_ARMARCH || TARGET_LOONGARCH64 +#if EMIT_BACKWARDS_NAVIGATION + +//------------------------------------------------------------------------ +// emitGetLastIns: Find the last instruction in the function and point ig/id at that instruction. +// +// Arguments: +// pig - Output. On exit, set *pig to the insGroup* containing the last instruction. +// pid - Output. On exit, set *pid to the instrDesc* of the last instruction. +// +// Returns: +// true if there are any instructions, false otherwise. If `false` is returned, `pig` and `pid` +// are untouched. +// +// Notes: +// If the last IG doesn't contain any instructions, walk backwards until finding an IG that does +// contain instructions. +// +bool emitter::emitGetLastIns(insGroup** pig, instrDesc** pid) +{ + for (insGroup* ig = emitIGlast; ig != nullptr; ig = ig->igPrev) + { + if (ig->igLastIns != nullptr) + { + *pig = ig; + *pid = (instrDesc*)ig->igLastIns; + return true; + } + } + return false; +} + +//------------------------------------------------------------------------ +// emitPrevID: Compute the previous instrDesc, either in this IG, or in a previous IG. 'id' +// will point to this instrDesc. 'ig' will also be updated. +// +// Arguments: +// ig - Current instruction group +// id - Current instrDesc +// +// Return Value: +// Returns true if there is an instruction, or false if we've iterated over all +// the instructions back to the beginning. +// +bool emitter::emitPrevID(insGroup*& ig, instrDesc*& id) +{ + unsigned idPrevSize = id->idPrevSize(); + if (idPrevSize != 0) + { + id = (instrDesc*)((BYTE*)id - idPrevSize); + return true; + } + + // No previous instructions in the group; walk previous IGs. + for (ig = ig->igPrev; ig != nullptr; ig = ig->igPrev) + { + if (ig->igLastIns != nullptr) + { + assert(ig->igInsCnt > 0); + id = (instrDesc*)ig->igLastIns; + return true; + } + + assert(ig->igInsCnt == 0); + } + + return false; +} + +#endif // EMIT_BACKWARDS_NAVIGATION + #if defined(TARGET_ARM) /***************************************************************************** @@ -3324,7 +3488,6 @@ void emitter::emitDispVarSet() // Return Value: // None // - void emitter::emitSetSecondRetRegGCType(instrDescCGCA* id, emitAttr secondRetSize) { if (EA_IS_GCREF(secondRetSize)) @@ -3736,7 +3899,7 @@ void emitter::emitDispIGflags(unsigned flags) } } -void emitter::emitDispIG(insGroup* ig, insGroup* igPrev, bool displayInstructions, bool displayLocation) +void emitter::emitDispIG(insGroup* ig, bool displayFunc, bool displayInstructions, bool displayLocation) { const int TEMP_BUFFER_LEN = 40; char buff[TEMP_BUFFER_LEN]; @@ -3749,7 +3912,7 @@ void emitter::emitDispIG(insGroup* ig, insGroup* igPrev, bool displayInstruction // distinct from the verbose on Compiler.) bool jitdump = emitComp->verbose; - if (jitdump && ((igPrev == nullptr) || (igPrev->igFuncIdx != ig->igFuncIdx))) + if (jitdump && displayFunc) { printf("func=%02u, ", ig->igFuncIdx); } @@ -3951,13 +4114,19 @@ void emitter::emitDispIG(insGroup* ig, insGroup* igPrev, bool displayInstruction void emitter::emitDispIGlist(bool displayInstructions) { - insGroup* ig; - insGroup* igPrev; - - for (igPrev = nullptr, ig = emitIGlist; ig; igPrev = ig, ig = ig->igNext) +#if EMIT_BACKWARDS_NAVIGATION + for (insGroup* ig = emitIGlist; ig != nullptr; ig = ig->igNext) + { + const bool displayFunc = (ig->igPrev == nullptr) || (ig->igPrev->igFuncIdx != ig->igFuncIdx); + emitDispIG(ig, displayFunc, displayInstructions); + } +#else // !EMIT_BACKWARDS_NAVIGATION + for (insGroup *ig = emitIGlist, *igPrev = nullptr; ig != nullptr; igPrev = ig, ig = ig->igNext) { - emitDispIG(ig, igPrev, displayInstructions); + const bool displayFunc = (igPrev == nullptr) || (igPrev->igFuncIdx != ig->igFuncIdx); + emitDispIG(ig, displayFunc, displayInstructions); } +#endif // !EMIT_BACKWARDS_NAVIGATION } void emitter::emitDispGCinfo() @@ -4380,9 +4549,9 @@ void emitter::emitRemoveJumpToNextInst() printf(" id: %u: ", id->idDebugOnlyInfo()->idNum); emitDispIns(id, false, true, false, 0, nullptr, 0, jmpGroup); printf("jump group:\n"); - emitDispIG(jmpGroup, nullptr, /* displayInstructions */ true); + emitDispIG(jmpGroup, /* displayFunc */ false, /* displayInstructions */ true); printf("target group:\n"); - emitDispIG(targetGroup, nullptr, /* displayInstructions */ false); + emitDispIG(targetGroup, /* displayFunc */ false, /* displayInstructions */ false); assert(jmp == id); } @@ -9171,6 +9340,10 @@ void emitter::emitInitIG(insGroup* ig) ig->igGCregs = RBM_NONE; ig->igInsCnt = 0; +#if EMIT_BACKWARDS_NAVIGATION + ig->igLastIns = nullptr; +#endif // EMIT_BACKWARDS_NAVIGATION + #if FEATURE_LOOP_ALIGN ig->igLoopBackEdge = nullptr; #endif @@ -9195,6 +9368,15 @@ void emitter::emitInsertIGAfter(insGroup* insertAfterIG, insGroup* ig) ig->igNext = insertAfterIG->igNext; insertAfterIG->igNext = ig; +#if EMIT_BACKWARDS_NAVIGATION + ig->igPrev = insertAfterIG; + + if (ig->igNext != nullptr) + { + ig->igNext->igPrev = ig; + } +#endif // EMIT_BACKWARDS_NAVIGATION + if (emitIGlast == insertAfterIG) { // If we are inserting at the end, then update the 'last' pointer diff --git a/src/coreclr/jit/emit.h b/src/coreclr/jit/emit.h index a8ff3e2cf693f0..17f4d64740877a 100644 --- a/src/coreclr/jit/emit.h +++ b/src/coreclr/jit/emit.h @@ -38,6 +38,8 @@ #define EMIT_INSTLIST_VERBOSE (emitComp->verbose) #endif +#define EMIT_BACKWARDS_NAVIGATION 0 // If 1, enable backwards navigation code for MIR (insGroup/instrDesc). + /*****************************************************************************/ #ifdef DEBUG @@ -252,6 +254,10 @@ struct insGroup { insGroup* igNext; +#if EMIT_BACKWARDS_NAVIGATION + insGroup* igPrev; +#endif + #ifdef DEBUG insGroup* igSelf; // for consistency checking #endif @@ -320,6 +326,12 @@ struct insGroup insPlaceholderGroupData* igPhData; // when igFlags & IGF_PLACEHOLDER }; +#if EMIT_BACKWARDS_NAVIGATION + // Last instruction in group, if any (nullptr if none); used for backwards navigation. + // (Should be type emitter::instrDesc*). + void* igLastIns; +#endif + #if EMIT_TRACK_STACK_DEPTH unsigned igStkLvl; // stack level on entry #endif @@ -335,6 +347,12 @@ struct insGroup insPlaceholderGroupData* igPhData; // when igFlags & IGF_PLACEHOLDER }; +#if EMIT_BACKWARDS_NAVIGATION + // Last instruction in group, if any (nullptr if none); used for backwards navigation. + // (Should be type emitter::instrDesc*). + void* igLastIns; +#endif + #if EMIT_TRACK_STACK_DEPTH unsigned igStkLvl; // stack level on entry #endif @@ -574,8 +592,6 @@ class emitter #endif // TARGET_XARCH - struct instrDesc; - struct instrDescDebugInfo { unsigned idNum; @@ -684,12 +700,6 @@ class emitter } #endif - void idSetRelocFlags(emitAttr attr) - { - _idCnsReloc = (EA_IS_CNS_RELOC(attr) ? 1 : 0); - _idDspReloc = (EA_IS_DSP_RELOC(attr) ? 1 : 0); - } - //////////////////////////////////////////////////////////////////////// // Space taken up to here: // x86: 17 bits @@ -729,7 +739,6 @@ class emitter // the live gcrefReg mask for the call instructions on x86/x64 // regNumber _idReg1 : REGNUM_BITS; // register num - regNumber _idReg2 : REGNUM_BITS; //////////////////////////////////////////////////////////////////////// @@ -738,7 +747,7 @@ class emitter // amd64: 38 bits // arm: 32 bits // arm64: 31 bits - CLANG_FORMAT_COMMENT_ANCHOR; + // loongarch64: 28 bits unsigned _idSmallDsc : 1; // is this a "small" descriptor? unsigned _idLargeCns : 1; // does a large constant follow? @@ -770,18 +779,6 @@ class emitter unsigned _idLclVar : 1; // access a local on stack unsigned _idLclFPBase : 1; // access a local on stack - SP based offset insOpts _idInsOpt : 3; // options for Load/Store instructions - -// For arm we have used 16 bits -#define ID_EXTRA_BITFIELD_BITS (16) - -#elif defined(TARGET_ARM64) -// For Arm64, we have used 17 bits from the second DWORD. -#define ID_EXTRA_BITFIELD_BITS (17) -#elif defined(TARGET_XARCH) || defined(TARGET_LOONGARCH64) - // For xarch and LoongArch64, we have used 14 bits from the second DWORD. -#define ID_EXTRA_BITFIELD_BITS (14) -#else -#error Unsupported or unset target architecture #endif //////////////////////////////////////////////////////////////////////// @@ -792,42 +789,95 @@ class emitter // arm64: 49 bits // loongarch64: 46 bits + // + // How many bits have been used beyond the first 32? + // Define ID_EXTRA_BITFIELD_BITS to that number. + // + CLANG_FORMAT_COMMENT_ANCHOR; + +#if defined(TARGET_ARM) +#define ID_EXTRA_BITFIELD_BITS (16) +#elif defined(TARGET_ARM64) +#define ID_EXTRA_BITFIELD_BITS (17) +#elif defined(TARGET_XARCH) || defined(TARGET_LOONGARCH64) +#define ID_EXTRA_BITFIELD_BITS (14) +#else +#error Unsupported or unset target architecture +#endif + unsigned _idCnsReloc : 1; // LargeCns is an RVA and needs reloc tag unsigned _idDspReloc : 1; // LargeDsp is an RVA and needs reloc tag #define ID_EXTRA_RELOC_BITS (2) +#if EMIT_BACKWARDS_NAVIGATION + + // "Pointer" to previous instrDesc in this group. If zero, there is + // no previous instrDesc. If non-zero, then _idScaledPrevOffset * 4 + // is the size in bytes of the previous instrDesc; subtract that from + // the current instrDesc* to reach the previous one. + // All instrDesc types are <= 56 bytes, but we also need m_debugInfoSize, + // which is pointer sized, so 5 bits are required on 64-bit and 4 bits + // on 32-bit. + CLANG_FORMAT_COMMENT_ANCHOR; + +#ifdef HOST_64BIT + unsigned _idScaledPrevOffset : 5; +#define ID_EXTRA_PREV_OFFSET_BITS (5) +#else + unsigned _idScaledPrevOffset : 4; +#define ID_EXTRA_PREV_OFFSET_BITS (4) +#endif + +#else // !EMIT_BACKWARDS_NAVIGATION +#define ID_EXTRA_PREV_OFFSET_BITS (0) +#endif // !EMIT_BACKWARDS_NAVIGATION + //////////////////////////////////////////////////////////////////////// - // Space taken up to here: - // x86: 48 bits - // amd64: 48 bits - // arm: 50 bits - // arm64: 51 bits - // loongarch64: 48 bits + // Space taken up to here (with/without prev offset, assuming host==target): + // x86: 52/48 bits + // amd64: 53/48 bits + // arm: 54/50 bits + // arm64: 56/51 bits + // loongarch64: 53/48 bits CLANG_FORMAT_COMMENT_ANCHOR; -#define ID_EXTRA_BITS (ID_EXTRA_RELOC_BITS + ID_EXTRA_BITFIELD_BITS) +#define ID_EXTRA_BITS (ID_EXTRA_RELOC_BITS + ID_EXTRA_BITFIELD_BITS + ID_EXTRA_PREV_OFFSET_BITS) /* Use whatever bits are left over for small constants */ #define ID_BIT_SMALL_CNS (32 - ID_EXTRA_BITS) + C_ASSERT(ID_BIT_SMALL_CNS > 0); #define ID_MIN_SMALL_CNS 0 #define ID_MAX_SMALL_CNS (int)((1 << ID_BIT_SMALL_CNS) - 1U) //////////////////////////////////////////////////////////////////////// - // Small constant size: - // x86: 16 bits - // amd64: 16 bits - // arm: 14 bits - // arm64: 13 bits + // Small constant size (with/without prev offset, assuming host==target): + // x86: 12/16 bits + // amd64: 11/16 bits + // arm: 10/14 bits + // arm64: 8/13 bits + // loongarch64: 11/16 bits unsigned _idSmallCns : ID_BIT_SMALL_CNS; //////////////////////////////////////////////////////////////////////// // Space taken up to here: 64 bits, all architectures, by design. //////////////////////////////////////////////////////////////////////// + + // + // This is the end of the 'small' instrDesc which is the same on all platforms + // + // If you add lots more fields that need to be cleared (such + // as various flags), you might need to update the body of + // emitter::emitAllocInstr() to clear them. + // + // SMALL_IDSC_SIZE is this size, in bytes. + // CLANG_FORMAT_COMMENT_ANCHOR; +#define SMALL_IDSC_SIZE 8 + public: instrDescDebugInfo* idDebugOnlyInfo() const { @@ -843,21 +893,6 @@ class emitter private: CLANG_FORMAT_COMMENT_ANCHOR; -// -// This is the end of the 'small' instrDesc which is the same on all platforms -// Sizes (includes one pointer): -// x86: 2 DWORDs, 96 bits -// amd64: 4 DWORDs, 128 bits -// arm: 3 DWORDs, 96 bits -// arm64: 4 DWORDs, 128 bits -// There should no padding or alignment issues on any platform or configuration. -// -// If you add lots more fields that need to be cleared (such -// as various flags), you might need to update the body of -// emitter::emitAllocInstr() to clear them. - -#define SMALL_IDSC_SIZE 8 - void checkSizes(); union idAddrUnion { @@ -1366,6 +1401,29 @@ class emitter return idIsDspReloc() || idIsCnsReloc(); } + void idSetRelocFlags(emitAttr attr) + { + _idCnsReloc = (EA_IS_CNS_RELOC(attr) ? 1 : 0); + _idDspReloc = (EA_IS_DSP_RELOC(attr) ? 1 : 0); + } + +#if EMIT_BACKWARDS_NAVIGATION + + // Return the stored size of the previous instrDesc in bytes, or zero if there + // is no previous instrDesc in this group. + unsigned idPrevSize() + { + return _idScaledPrevOffset * 4; + } + void idSetPrevSize(unsigned prevInstrDescSizeInBytes) + { + assert(prevInstrDescSizeInBytes % 4 == 0); + _idScaledPrevOffset = prevInstrDescSizeInBytes / 4; + assert(idPrevSize() == prevInstrDescSizeInBytes); + } + +#endif // EMIT_BACKWARDS_NAVIGATION + unsigned idSmallCns() const { return _idSmallCns; @@ -1784,7 +1842,7 @@ class emitter void emitDispIGflags(unsigned flags); void emitDispIG(insGroup* ig, - insGroup* igPrev = nullptr, + bool displayFunc = false, bool displayInstructions = false, bool displayLocation = true); void emitDispIGlist(bool displayInstructions = false); @@ -2013,7 +2071,10 @@ class emitter /* The logic that creates and keeps track of instruction groups */ /************************************************************************/ - // SC_IG_BUFFER_SIZE defines the size, in bytes, of the single, global instruction group buffer. + // The IG buffer size is the size, in bytes, of the single, global instruction group buffer. + // It is computed dynamically based on SC_IG_BUFFER_NUM_SMALL_DESCS, SC_IG_BUFFER_NUM_LARGE_DESCS, + // and whether a debug info pointer is being saved. + // // When a label is reached, or the buffer is filled, the precise amount of the buffer that was // used is copied to a newly allocated, precisely sized buffer, and the global buffer is reset // for use with the next set of instructions (see emitSavIG). If the buffer was filled before @@ -2226,6 +2287,10 @@ class emitter instrDesc* emitLastIns; insGroup* emitLastInsIG; +#if EMIT_BACKWARDS_NAVIGATION + unsigned emitLastInsFullSize; +#endif // EMIT_BACKWARDS_NAVIGATION + // Check if a peephole optimization involving emitLastIns is safe. // // We must have a non-null emitLastIns to consult. @@ -2286,6 +2351,11 @@ class emitter #endif // TARGET_ARMARCH || TARGET_LOONGARCH64 +#if EMIT_BACKWARDS_NAVIGATION + bool emitPrevID(insGroup*& ig, instrDesc*& id); + bool emitGetLastIns(insGroup** pig, instrDesc** pid); +#endif // EMIT_BACKWARDS_NAVIGATION + #ifdef TARGET_X86 void emitMarkStackLvl(unsigned stackLevel); #endif diff --git a/src/coreclr/jit/emitloongarch64.cpp b/src/coreclr/jit/emitloongarch64.cpp index 6840bd601ab755..574282bdf9a994 100644 --- a/src/coreclr/jit/emitloongarch64.cpp +++ b/src/coreclr/jit/emitloongarch64.cpp @@ -600,7 +600,7 @@ void emitter::emitIns(instruction ins) * the negtive `offs` is special for optimizing the large offset which >2047. * when offs >2047 we can't encode one instruction to load/store the data, * if there are several load/store at this case, you have to repeat the similar - * large offs with reduntant instructions and maybe eat up the `SC_IG_BUFFER_SIZE`. + * large offs with reduntant instructions and maybe eat up the `emitIGbuffSize`. * * Before optimizing the following instructions: * lu12i.w x0, 0x0