diff --git a/src/coreclr/jit/emit.cpp b/src/coreclr/jit/emit.cpp index 352d1b2acfcd7b..8f7091a9a037c6 100644 --- a/src/coreclr/jit/emit.cpp +++ b/src/coreclr/jit/emit.cpp @@ -417,6 +417,8 @@ void emitterStats(FILE* fout) #endif // TARGET_ARM fprintf(fout, "Total instrDescAlign: %8u (%5.2f%%)\n", emitter::emitTotalDescAlignCnt, 100.0 * emitter::emitTotalDescAlignCnt / emitter::emitTotalInsCnt); + fprintf(fout, "Total instrDescZeroSz: %8u (%5.2f%%)\n", emitter::emitTotalDescZeroSzCnt, + 100.0 * emitter::emitTotalDescZeroSzCnt / emitter::emitTotalInsCnt); fprintf(fout, "\n"); } @@ -665,6 +667,10 @@ void emitter::emitGenIG(insGroup* ig) emitCurIGinsCnt = 0; emitCurIGsize = 0; +#if defined(DEBUG) + emitCurIGZeroSzCnt = 0; +#endif // DEBUG + assert(emitCurIGjmpList == nullptr); #if FEATURE_LOOP_ALIGN @@ -793,14 +799,21 @@ insGroup* emitter::emitSavIG(bool emitAdd) emitCurCodeOffset += emitCurIGsize; assert(IsCodeAligned(emitCurCodeOffset)); +#if defined(DEBUG) || EMITTER_STATS + noway_assert((BYTE)emitCurIGZeroSzCnt == emitCurIGZeroSzCnt); + ig->igZeroSzCnt = (BYTE)emitCurIGZeroSzCnt; +#endif // DEBUG || EMITTER_STATS + #if EMITTER_STATS emitTotalIGicnt += emitCurIGinsCnt; + emitTotalIGZeroSzCnt += emitCurIGZeroSzCnt; emitTotalIGsize += sz; emitSizeMethod += sz; if (emitIGisInProlog(ig)) { emitCurPrologInsCnt += emitCurIGinsCnt; + emitCurPrologZeroSzCnt += emitCurIGZeroSzCnt; emitCurPrologIGSize += sz; // Keep track of the maximums. @@ -993,6 +1006,16 @@ insGroup* emitter::emitSavIG(bool emitAdd) assert(emitCurIGfreeBase <= (BYTE*)emitLastIns); assert((BYTE*)emitLastIns < emitCurIGfreeBase + sz); emitLastIns = (instrDesc*)((BYTE*)id + ((BYTE*)emitLastIns - (BYTE*)emitCurIGfreeBase)); + + if (emitLastEmittedIns != nullptr) + { + // Unlike with emitLastIns, we might be null if the group only contains + // elided instructions, in which case we'll only update in that scenario + + assert(emitCurIGfreeBase <= (BYTE*)emitLastEmittedIns); + assert((BYTE*)emitLastEmittedIns < emitCurIGfreeBase + sz); + emitLastEmittedIns = (instrDesc*)((BYTE*)id + ((BYTE*)emitLastEmittedIns - (BYTE*)emitCurIGfreeBase)); + } } // Reset the buffer free pointers @@ -1138,7 +1161,8 @@ void emitter::emitBegFN(bool hasFramePtr emitPrologIG = emitIGlist = emitIGlast = emitCurIG = ig = emitAllocIG(); - emitLastIns = nullptr; + emitLastIns = nullptr; + emitLastEmittedIns = nullptr; ig->igNext = nullptr; @@ -1197,6 +1221,15 @@ int emitter::instrDesc::idAddrUnion::iiaGetJitDataOffset() const // float emitter::insEvaluateExecutionCost(instrDesc* id) { + if (id->idIns() == INS_mov_eliminated) + { + // Elideable moves are specified to have a zero size, but are carried + // in emit so we can still do the relevant byref liveness update + + assert(id->idGCref() == GCT_BYREF); + return 0; + } + insExecutionCharacteristics result = getInsExecutionCharacteristics(id); float throughput = result.insThroughput; float latency = result.insLatency; @@ -1296,13 +1329,49 @@ void emitter::dispIns(instrDesc* id) assert(id->idDebugOnlyInfo()->idSize == sz); #endif // DEBUG +#if defined(DEBUG) || EMITTER_STATS + if (id->idCodeSize() == 0) + { + emitCurIGZeroSzCnt++; + +#if EMITTER_STATS + emitTotalDescZeroSzCnt++ +#endif // EMITTER_STATS + } +#endif // DEBUG || EMITTER_STATS + #if EMITTER_STATS emitIFcounts[id->idInsFmt()]++; -#endif +#endif // EMITTER_STATS } void emitter::appendToCurIG(instrDesc* id) { + assert(id == emitLastIns); + + if (emitLastIns->idIns() != INS_mov_eliminated) + { + // emitAllocAnyInstr sets emitLastIns and id + // to be the same. However, for the purposes + // of looking back we only want to consider + // certain "non-zero" size instructions and + // so we'll update the last emitted instruction + // when appending to the current IG. + + emitLastEmittedIns = emitLastIns; + } + else if (emitCurIGsize == 0) + { + // If we are part of a new instruction group + // then we need to null out the last instruction + // so that we aren't incorrectly tracking across + // block boundaries. + + // TOOD-CQ: We should also be able to track across + // extended instruction groups to allow more opts + + emitLastEmittedIns = nullptr; + } emitCurIGsize += id->idCodeSize(); } @@ -1416,7 +1485,7 @@ void* emitter::emitAllocAnyInstr(size_t sz, emitAttr opsz) #error "Undefined target for pseudorandom NOP insertion" #endif - emitCurIGsize += nopSize; + appendToCurIG(emitCurIGsize); emitNextNop = emitNextRandomNop(); } else @@ -4252,15 +4321,15 @@ void emitter::emitJumpDistBind() #ifdef TARGET_XARCH /* Done if this is not a variable-sized jump */ - if ((jmp->idIns() == INS_push) || (jmp->idIns() == INS_mov) || (jmp->idIns() == INS_call) || - (jmp->idIns() == INS_push_hide)) + if ((jmp->idIns() == INS_push) || (jmp->idIns() == INS_mov) || (jmp->idIns() == INS_mov_eliminated) || + (jmp->idIns() == INS_call) || (jmp->idIns() == INS_push_hide)) { continue; } #endif #ifdef TARGET_ARM - if ((jmp->idIns() == INS_push) || (jmp->idIns() == INS_mov) || (jmp->idIns() == INS_movt) || - (jmp->idIns() == INS_movw)) + if ((jmp->idIns() == INS_push) || (jmp->idIns() == INS_mov) || (jmp->idIns() == INS_mov_eliminated) || + (jmp->idIns() == INS_movt) || (jmp->idIns() == INS_movw)) { continue; } @@ -5974,6 +6043,9 @@ unsigned emitter::emitEndCodeGen(Compiler* comp, printf("\t\t\t\t\t\t;; bbWeight=%s PerfScore %.2f", refCntWtd2str(ig->igWeight), ig->igPerfScore); } *instrCount += ig->igInsCnt; + + // We don't want to include zero size instructions in the count, as they aren't impactful + *instrCount -= ig->igZeroSzCnt; #endif // DEBUG emitCurIG = nullptr; diff --git a/src/coreclr/jit/emit.h b/src/coreclr/jit/emit.h index 666201fc98bf00..e716d5ca7791d7 100644 --- a/src/coreclr/jit/emit.h +++ b/src/coreclr/jit/emit.h @@ -299,7 +299,11 @@ struct insGroup unsigned igStkLvl; // stack level on entry #endif regMaskSmall igGCregs; // set of registers with live GC refs - unsigned char igInsCnt; // # of instructions in this group + unsigned char igInsCnt; // # of instructions in this group + +#if defined(DEBUG) + unsigned char igZeroSzCnt; // # of zero size instructions in this group +#endif // DEBUG #else // REGMASK_BITS @@ -314,7 +318,11 @@ struct insGroup unsigned igStkLvl; // stack level on entry #endif - unsigned char igInsCnt; // # of instructions in this group + unsigned char igInsCnt; // # of instructions in this group + +#if defined(DEBUG) + unsigned char igZeroSzCnt; // # of zero size instructions in this group +#endif // DEBUG #endif // REGMASK_BITS @@ -920,7 +928,7 @@ class emitter break; } - return size; + return (idIns() != INS_mov_eliminated) ? size : 0; } #elif defined(TARGET_ARM) @@ -932,7 +940,7 @@ class emitter unsigned idCodeSize() const { unsigned result = (_idInsSize == ISZ_16BIT) ? 2 : (_idInsSize == ISZ_32BIT) ? 4 : 6; - return result; + return (idIns() != INS_mov_eliminated) ? result : 0; } insSize idInsSize() const { @@ -1793,6 +1801,10 @@ class emitter UNATIVE_OFFSET emitCurCodeOffset; // current code offset within group UNATIVE_OFFSET emitTotalCodeSize; // bytes of code in entire method +#if defined(DEBUG) || EMITTER_STATS + unsigned emitCurIGZeroSzCnt; // # of zero size instr's in buffer +#endif // DEBUG || EMITTER_STATS + insGroup* emitFirstColdIG; // first cold instruction group void emitSetFirstColdIGCookie(void* bbEmitCookie) @@ -1889,6 +1901,7 @@ class emitter } instrDesc* emitLastIns; + instrDesc* emitLastEmittedIns; #ifdef DEBUG void emitCheckIGoffsets(); @@ -2317,14 +2330,16 @@ class emitter static unsigned emitTotalInsCnt; - static unsigned emitCurPrologInsCnt; // current number of prolog instrDescs - static size_t emitCurPrologIGSize; // current size of prolog instrDescs - static unsigned emitMaxPrologInsCnt; // maximum number of prolog instrDescs - static size_t emitMaxPrologIGSize; // maximum size of prolog instrDescs + static unsigned emitCurPrologInsCnt; // current number of prolog instrDescs + static unsigned emitCurPrologZeroSzCnt; // current number of elided prolog instrDescs + static size_t emitCurPrologIGSize; // current size of prolog instrDescs + static unsigned emitMaxPrologInsCnt; // maximum number of prolog instrDescs + static size_t emitMaxPrologIGSize; // maximum size of prolog instrDescs static unsigned emitTotalIGcnt; // total number of insGroup allocated static unsigned emitTotalPhIGcnt; // total number of insPlaceholderGroupData allocated static unsigned emitTotalIGicnt; + static unsigned emitTotalIGZeroSzCnt; static size_t emitTotalIGsize; static unsigned emitTotalIGmcnt; // total method count static unsigned emitTotalIGExtend; // total number of 'emitExtend' (typically overflow) groups @@ -2359,6 +2374,7 @@ class emitter static unsigned emitSmallCns[SMALL_CNS_TSZ]; static unsigned emitLargeCnsCnt; static unsigned emitTotalDescAlignCnt; + static unsigned emitTotalDescZeroSzCnt; static unsigned emitIFcounts[IF_COUNT]; diff --git a/src/coreclr/jit/emitarm.cpp b/src/coreclr/jit/emitarm.cpp index 6953df5b49a7c0..dd96651ebb00a8 100644 --- a/src/coreclr/jit/emitarm.cpp +++ b/src/coreclr/jit/emitarm.cpp @@ -1230,6 +1230,7 @@ bool emitter::IsMovInstruction(instruction ins) switch (ins) { case INS_mov: + case INS_mov_eliminated: case INS_sxtb: case INS_sxth: case INS_uxtb: @@ -2065,8 +2066,15 @@ void emitter::emitIns_Mov(instruction ins, { if (canSkip && (dstReg == srcReg)) { - // These instructions have no side effect and can be skipped - return; + // These instructions might have a side effect in the form of a + // byref liveness update, so preserve them but emit nothing + + if (!EA_IS_BYREF(attr)) + { + return; + } + + ins = INS_mov_eliminated; } fmt = IF_T1_D0; sf = INS_FLAGS_NOT_SET; @@ -5767,6 +5775,18 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) VARSET_TP GCvars(VarSetOps::UninitVal()); + if (ins == INS_mov_eliminated) + { + // Elideable moves are specified to have a zero size, but are carried + // in emit so we can still do the relevant byref liveness update + + assert(id->idGCref() == GCT_BYREF); + assert(id->idCodeSize() == 0); + + sz = SMALL_IDSC_SIZE; + goto UPDATE_LIVENESS; + } + /* What instruction format have we got? */ switch (fmt) @@ -6587,6 +6607,7 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) break; } +UPDATE_LIVENESS: // Determine if any registers now hold GC refs, or whether a register that was overwritten held a GC ref. // We assume here that "id->idGCref()" is not GC_NONE only if the instruction described by "id" writes a // GC ref to register "id->idReg1()". (It may, apparently, also not be GC_NONE in other cases, such as @@ -6703,9 +6724,9 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) } #endif - /* All instructions are expected to generate code */ + /* All instructions, except eliminated moves, are expected to generate code */ - assert(*dp != dst); + assert((*dp != dst) || (ins == INS_mov_eliminated)); *dp = dst; @@ -7101,6 +7122,17 @@ void emitter::emitDispInsHex(instrDesc* id, BYTE* code, size_t sz) void emitter::emitDispInsHelp( instrDesc* id, bool isNew, bool doffs, bool asmfm, unsigned offset, BYTE* code, size_t sz, insGroup* ig) { + if (id->idIns() == INS_mov_eliminated) + { + // Elideable moves are specified to have a zero size, but are carried + // in emit so we can still do the relevant byref liveness update + + assert(id->idGCref() == GCT_BYREF); + assert(id->idCodeSize() == 0); + + return; + } + if (EMITVERBOSE) { unsigned idNum = id->idDebugOnlyInfo()->idNum; // Do not remove this! It is needed for VisualStudio diff --git a/src/coreclr/jit/emitarm64.cpp b/src/coreclr/jit/emitarm64.cpp index 37e19a219d265b..9733260aff1a68 100644 --- a/src/coreclr/jit/emitarm64.cpp +++ b/src/coreclr/jit/emitarm64.cpp @@ -4088,7 +4088,9 @@ void emitter::emitIns_Mov( if (IsRedundantMov(ins, size, dstReg, srcReg, canSkip)) { - // These instructions have no side effect and can be skipped + // TODO-ARM64: These instructions might have a side effect in the form of a + // byref liveness update, so we should preserve them but emit nothing + return; } @@ -4104,13 +4106,10 @@ void emitter::emitIns_Mov( return emitIns_R_R_I(INS_mov, size, dstReg, srcReg, 0); } } - else + else if (isVectorRegister(srcReg)) { - if (isVectorRegister(srcReg)) - { - assert(isGeneralRegister(dstReg)); - return emitIns_R_R_I(INS_mov, size, dstReg, srcReg, 0); - } + assert(isGeneralRegister(dstReg)); + return emitIns_R_R_I(INS_mov, size, dstReg, srcReg, 0); } // Is this a MOV to/from SP instruction? @@ -10348,6 +10347,17 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) assert(REG_NA == (int)REG_NA); + if (ins == INS_mov_eliminated) + { + // Elideable moves are specified to have a zero size, but are carried + // in emit so we can still do the relevant byref liveness update + + assert(id->idGCref() == GCT_BYREF); + assert(id->idCodeSize() == 0); + + goto UPDATE_LIVENESS; + } + /* What instruction format have we got? */ switch (fmt) @@ -11461,6 +11471,7 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) break; } +UPDATE_LIVENESS: // Determine if any registers now hold GC refs, or whether a register that was overwritten held a GC ref. // We assume here that "id->idGCref()" is not GC_NONE only if the instruction described by "id" writes a // GC ref to register "id->idReg1()". (It may, apparently, also not be GC_NONE in other cases, such as @@ -11576,9 +11587,9 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) } #endif - /* All instructions are expected to generate code */ + /* All instructions, except eliminated moves, are expected to generate code */ - assert(*dp != dst); + assert((*dp != dst) || (ins == INS_mov_eliminated)); *dp = dst; @@ -12188,6 +12199,17 @@ void emitter::emitDispInsHex(instrDesc* id, BYTE* code, size_t sz) void emitter::emitDispIns( instrDesc* id, bool isNew, bool doffs, bool asmfm, unsigned offset, BYTE* pCode, size_t sz, insGroup* ig) { + if (id->idIns() == INS_mov_eliminated) + { + // Elideable moves are specified to have a zero size, but are carried + // in emit so we can still do the relevant byref liveness update + + assert(id->idGCref() == GCT_BYREF); + assert(id->idCodeSize() == 0); + + return; + } + if (EMITVERBOSE) { unsigned idNum = @@ -15535,6 +15557,7 @@ bool emitter::IsMovInstruction(instruction ins) { case INS_fmov: case INS_mov: + case INS_mov_eliminated: case INS_sxtb: case INS_sxth: case INS_sxtw: @@ -15617,23 +15640,24 @@ bool emitter::IsRedundantMov(instruction ins, emitAttr size, regNumber dst, regN bool isFirstInstrInBlock = (emitCurIGinsCnt == 0) && ((emitCurIG->igFlags & IGF_EXTEND) == 0); if (!isFirstInstrInBlock && // Don't optimize if instruction is not the first instruction in IG. - (emitLastIns != nullptr) && - (emitLastIns->idIns() == INS_mov) && // Don't optimize if last instruction was not 'mov'. - (emitLastIns->idOpSize() == size)) // Don't optimize if operand size is different than previous instruction. + (emitLastEmittedIns != nullptr) && + (emitLastEmittedIns->idIns() == INS_mov) && // Don't optimize if last instruction was not 'mov'. + (emitLastEmittedIns->idOpSize() == size)) // Don't optimize if operand size is different than previous + // instruction. { // Check if we did same move in prev instruction except dst/src were switched. - regNumber prevDst = emitLastIns->idReg1(); - regNumber prevSrc = emitLastIns->idReg2(); - insFormat lastInsfmt = emitLastIns->idInsFmt(); + regNumber prevDst = emitLastEmittedIns->idReg1(); + regNumber prevSrc = emitLastEmittedIns->idReg2(); + insFormat lastInsfmt = emitLastEmittedIns->idInsFmt(); if ((prevDst == dst) && (prevSrc == src)) { - assert(emitLastIns->idOpSize() == size); + assert(emitLastEmittedIns->idOpSize() == size); JITDUMP("\n -- suppressing mov because previous instruction already moved from src to dst register.\n"); return true; } - // Sometimes emitLastIns can be a mov with single register e.g. "mov reg, #imm". So ensure to + // Sometimes emitLastEmittedIns can be a mov with single register e.g. "mov reg, #imm". So ensure to // optimize formats that does vector-to-vector or scalar-to-scalar register movs. bool isValidLastInsFormats = ((lastInsfmt == IF_DV_3C) || (lastInsfmt == IF_DR_2G) || (lastInsfmt == IF_DR_2E)); @@ -15696,16 +15720,17 @@ bool emitter::IsRedundantLdStr( { bool isFirstInstrInBlock = (emitCurIGinsCnt == 0) && ((emitCurIG->igFlags & IGF_EXTEND) == 0); - if (((ins != INS_ldr) && (ins != INS_str)) || (isFirstInstrInBlock) || (emitLastIns == nullptr)) + if (((ins != INS_ldr) && (ins != INS_str)) || (isFirstInstrInBlock) || (emitLastEmittedIns == nullptr)) { return false; } - regNumber prevReg1 = emitLastIns->idReg1(); - regNumber prevReg2 = emitLastIns->idReg2(); - insFormat lastInsfmt = emitLastIns->idInsFmt(); - emitAttr prevSize = emitLastIns->idOpSize(); - ssize_t prevImm = emitLastIns->idIsLargeCns() ? ((instrDescCns*)emitLastIns)->idcCnsVal : emitLastIns->idSmallCns(); + regNumber prevReg1 = emitLastEmittedIns->idReg1(); + regNumber prevReg2 = emitLastEmittedIns->idReg2(); + insFormat lastInsfmt = emitLastEmittedIns->idInsFmt(); + emitAttr prevSize = emitLastEmittedIns->idOpSize(); + ssize_t prevImm = emitLastEmittedIns->idIsLargeCns() ? ((instrDescCns*)emitLastEmittedIns)->idcCnsVal + : emitLastEmittedIns->idSmallCns(); // Only optimize if: // 1. "base" or "base plus immediate offset" addressing modes. @@ -15716,7 +15741,7 @@ bool emitter::IsRedundantLdStr( return false; } - if ((ins == INS_ldr) && (emitLastIns->idIns() == INS_str)) + if ((ins == INS_ldr) && (emitLastEmittedIns->idIns() == INS_str)) { // If reg1 is of size less than 8-bytes, then eliminating the 'ldr' // will not zero the upper bits of reg1. @@ -15737,7 +15762,7 @@ bool emitter::IsRedundantLdStr( return true; } } - else if ((ins == INS_str) && (emitLastIns->idIns() == INS_ldr)) + else if ((ins == INS_str) && (emitLastEmittedIns->idIns() == INS_ldr)) { // Make sure src and dst registers are not same. // ldr x0, [x0, #4] diff --git a/src/coreclr/jit/emitxarch.cpp b/src/coreclr/jit/emitxarch.cpp index 96b12839ad214f..284947219c4e5c 100644 --- a/src/coreclr/jit/emitxarch.cpp +++ b/src/coreclr/jit/emitxarch.cpp @@ -268,8 +268,14 @@ bool emitter::AreUpper32BitsZero(regNumber reg) return false; } - instrDesc* id = emitLastIns; - insFormat fmt = id->idInsFmt(); + instrDesc* id = emitLastEmittedIns; + + if (id == nullptr) + { + return false; + } + + insFormat fmt = id->idInsFmt(); // This isn't meant to be a comprehensive check. Just look for what // seems to be common. @@ -337,7 +343,13 @@ bool emitter::AreFlagsSetToZeroCmp(regNumber reg, emitAttr opSize, genTreeOps tr return false; } - instrDesc* id = emitLastIns; + instrDesc* id = emitLastEmittedIns; + + if (id == nullptr) + { + return false; + } + instruction lastIns = id->idIns(); insFormat fmt = id->idInsFmt(); @@ -1040,7 +1052,7 @@ unsigned emitter::emitOutputRexOrVexPrefixIfNeeded(instruction ins, BYTE* dst, c */ bool emitter::emitIsLastInsCall() { - if ((emitLastIns != nullptr) && (emitLastIns->idIns() == INS_call)) + if ((emitLastEmittedIns != nullptr) && (emitLastEmittedIns->idIns() == INS_call)) { return true; } @@ -1894,12 +1906,17 @@ const emitJumpKind emitReverseJumpKinds[] = { inline bool emitInstHasNoCode(instruction ins) { - if (ins == INS_align) + switch (ins) { - return true; - } + case INS_align: + return true; - return false; + case INS_mov_eliminated: + return true; + + default: + return false; + } } /***************************************************************************** @@ -2761,7 +2778,7 @@ void emitter::emitLoopAlign(unsigned short paddingBytes) /* Append this instruction to this IG's alignment list */ id->idaNext = emitCurIGAlignList; - emitCurIGsize += paddingBytes; + appendToCurIG(id); emitCurIGAlignList = id; } @@ -2814,7 +2831,7 @@ void emitter::emitIns_Nop(unsigned size) id->idCodeSize(size); dispIns(id); - emitCurIGsize += size; + appendToCurIG(id); } /***************************************************************************** @@ -2871,7 +2888,7 @@ void emitter::emitIns(instruction ins) id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); } // Add an instruction with no operands, but whose encoding depends on the size @@ -2898,7 +2915,7 @@ void emitter::emitIns(instruction ins, emitAttr attr) id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); } //------------------------------------------------------------------------ @@ -3152,7 +3169,7 @@ void emitter::spillIntArgRegsToShadowSlots() id->idReg1(argReg); sz = emitInsSizeAM(id, insCodeMR(INS_mov)); id->idCodeSize(sz); - emitCurIGsize += sz; + appendToCurIG(id); } } @@ -3203,7 +3220,7 @@ void emitter::emitInsLoadInd(instruction ins, emitAttr attr, regNumber dstReg, G UNATIVE_OFFSET sz = emitInsSizeAM(id, insCodeRM(ins)); id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); } //------------------------------------------------------------------------ @@ -3280,7 +3297,7 @@ void emitter::emitInsStoreInd(instruction ins, emitAttr attr, GenTreeStoreInd* m } dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); } //------------------------------------------------------------------------ @@ -3596,7 +3613,7 @@ regNumber emitter::emitInsBinary(instruction ins, emitAttr attr, GenTree* dst, G id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); return (memOp == src) ? dst->GetRegNum() : REG_NA; } @@ -3801,7 +3818,7 @@ void emitter::emitInsRMW(instruction ins, emitAttr attr, GenTreeStoreInd* storeI id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); } //------------------------------------------------------------------------ @@ -3852,7 +3869,7 @@ void emitter::emitInsRMW(instruction ins, emitAttr attr, GenTreeStoreInd* storeI id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); } /***************************************************************************** @@ -3948,7 +3965,7 @@ void emitter::emitIns_R(instruction ins, emitAttr attr, regNumber reg) id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); emitAdjustStackDepthPushPop(ins); } @@ -4086,7 +4103,7 @@ void emitter::emitIns_R_I(instruction ins, emitAttr attr, regNumber reg, ssize_t id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); if (reg == REG_ESP) { @@ -4142,7 +4159,7 @@ void emitter::emitIns_I(instruction ins, emitAttr attr, cnsval_ssize_t val) id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); emitAdjustStackDepthPushPop(ins); } @@ -4179,7 +4196,7 @@ void emitter::emitIns_IJ(emitAttr attr, regNumber reg, unsigned base) id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); } /***************************************************************************** @@ -4232,7 +4249,7 @@ void emitter::emitIns_C(instruction ins, emitAttr attr, CORINFO_FIELD_HANDLE fld id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); emitAdjustStackDepthPushPop(ins); } @@ -4248,6 +4265,7 @@ bool emitter::IsMovInstruction(instruction ins) switch (ins) { case INS_mov: + case INS_mov_eliminated: case INS_movapd: case INS_movaps: case INS_movd: @@ -4351,14 +4369,25 @@ void emitter::emitIns_Mov(instruction ins, emitAttr attr, regNumber dstReg, regN assert(size <= EA_32BYTE); noway_assert(emitVerifyEncodable(ins, size, dstReg, srcReg)); + UNATIVE_OFFSET sz = emitInsSizeRR(ins, dstReg, srcReg, attr); + if (canSkip && (dstReg == srcReg)) { switch (ins) { case INS_mov: { - // These instructions have no side effect and can be skipped - return; + // These instructions might have a side effect in the form of a + // byref liveness update, so preserve them but emit nothing + + if (!EA_IS_BYREF(attr)) + { + return; + } + + sz = 0; + ins = INS_mov_eliminated; + break; } case INS_movapd: @@ -4369,6 +4398,7 @@ void emitter::emitIns_Mov(instruction ins, emitAttr attr, regNumber dstReg, regN case INS_movups: { // These instructions have no side effect and can be skipped + assert(!EA_IS_BYREF(attr)); return; } @@ -4409,8 +4439,7 @@ void emitter::emitIns_Mov(instruction ins, emitAttr attr, regNumber dstReg, regN } } - UNATIVE_OFFSET sz = emitInsSizeRR(ins, dstReg, srcReg, attr); - insFormat fmt = emitInsModeFormat(ins, IF_RRD_RRD); + insFormat fmt = emitInsModeFormat(ins, IF_RRD_RRD); instrDesc* id = emitNewInstrSmall(attr); id->idIns(ins); @@ -4420,7 +4449,7 @@ void emitter::emitIns_Mov(instruction ins, emitAttr attr, regNumber dstReg, regN id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); } /***************************************************************************** @@ -4454,7 +4483,7 @@ void emitter::emitIns_R_R(instruction ins, emitAttr attr, regNumber reg1, regNum id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); } /***************************************************************************** @@ -4513,7 +4542,7 @@ void emitter::emitIns_R_R_I(instruction ins, emitAttr attr, regNumber reg1, regN id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); } void emitter::emitIns_AR(instruction ins, emitAttr attr, regNumber base, int offs) @@ -4532,7 +4561,7 @@ void emitter::emitIns_AR(instruction ins, emitAttr attr, regNumber base, int off id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); } //------------------------------------------------------------------------ @@ -4568,7 +4597,7 @@ void emitter::emitIns_AR_R_R( id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); } void emitter::emitIns_R_A(instruction ins, emitAttr attr, regNumber reg1, GenTreeIndir* indir) @@ -4585,7 +4614,7 @@ void emitter::emitIns_R_A(instruction ins, emitAttr attr, regNumber reg1, GenTre id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); } void emitter::emitIns_R_A_I(instruction ins, emitAttr attr, regNumber reg1, GenTreeIndir* indir, int ival) @@ -4605,7 +4634,7 @@ void emitter::emitIns_R_A_I(instruction ins, emitAttr attr, regNumber reg1, GenT id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); } void emitter::emitIns_R_AR_I(instruction ins, emitAttr attr, regNumber reg1, regNumber base, int offs, int ival) @@ -4626,7 +4655,7 @@ void emitter::emitIns_R_AR_I(instruction ins, emitAttr attr, regNumber reg1, reg id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); } void emitter::emitIns_R_C_I( @@ -4652,7 +4681,7 @@ void emitter::emitIns_R_C_I( id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); } void emitter::emitIns_R_S_I(instruction ins, emitAttr attr, regNumber reg1, int varx, int offs, int ival) @@ -4675,7 +4704,7 @@ void emitter::emitIns_R_S_I(instruction ins, emitAttr attr, regNumber reg1, int id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); } void emitter::emitIns_R_R_A(instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, GenTreeIndir* indir) @@ -4696,7 +4725,7 @@ void emitter::emitIns_R_R_A(instruction ins, emitAttr attr, regNumber reg1, regN id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); } void emitter::emitIns_R_R_AR(instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, regNumber base, int offs) @@ -4718,7 +4747,7 @@ void emitter::emitIns_R_R_AR(instruction ins, emitAttr attr, regNumber reg1, reg id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); } //------------------------------------------------------------------------ @@ -4786,7 +4815,7 @@ void emitter::emitIns_R_AR_R(instruction ins, id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); } void emitter::emitIns_R_R_C( @@ -4813,7 +4842,7 @@ void emitter::emitIns_R_R_C( id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); } /***************************************************************************** @@ -4837,7 +4866,7 @@ void emitter::emitIns_R_R_R(instruction ins, emitAttr attr, regNumber targetReg, id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); } void emitter::emitIns_R_R_S(instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, int varx, int offs) @@ -4861,7 +4890,7 @@ void emitter::emitIns_R_R_S(instruction ins, emitAttr attr, regNumber reg1, regN id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); } void emitter::emitIns_R_R_A_I( @@ -4883,7 +4912,7 @@ void emitter::emitIns_R_R_A_I( id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); } void emitter::emitIns_R_R_AR_I( @@ -4906,7 +4935,7 @@ void emitter::emitIns_R_R_AR_I( id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); } void emitter::emitIns_R_R_C_I( @@ -4933,7 +4962,7 @@ void emitter::emitIns_R_R_C_I( id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); } /********************************************************************************** @@ -4995,7 +5024,7 @@ void emitter::emitIns_R_R_R_I( id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); } void emitter::emitIns_R_R_S_I( @@ -5020,7 +5049,7 @@ void emitter::emitIns_R_R_S_I( id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); } //------------------------------------------------------------------------ @@ -5077,7 +5106,7 @@ void emitter::emitIns_R_R_A_R( id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); } //------------------------------------------------------------------------ @@ -5117,7 +5146,7 @@ void emitter::emitIns_R_R_AR_R( id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); } //------------------------------------------------------------------------ @@ -5167,7 +5196,7 @@ void emitter::emitIns_R_R_C_R(instruction ins, id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); } //------------------------------------------------------------------------ @@ -5206,7 +5235,7 @@ void emitter::emitIns_R_R_S_R( id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); } void emitter::emitIns_R_R_R_R( @@ -5229,7 +5258,7 @@ void emitter::emitIns_R_R_R_R( id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); } /***************************************************************************** @@ -5303,7 +5332,7 @@ void emitter::emitIns_R_C(instruction ins, emitAttr attr, regNumber reg, CORINFO id->idAddr()->iiaFieldHnd = fldHnd; dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); } /***************************************************************************** @@ -5374,7 +5403,7 @@ void emitter::emitIns_C_R(instruction ins, emitAttr attr, CORINFO_FIELD_HANDLE f id->idAddr()->iiaFieldHnd = fldHnd; dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); } /***************************************************************************** @@ -5422,7 +5451,7 @@ void emitter::emitIns_C_I(instruction ins, emitAttr attr, CORINFO_FIELD_HANDLE f id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); } void emitter::emitIns_J_S(instruction ins, emitAttr attr, BasicBlock* dst, int varx, int offs) @@ -5477,7 +5506,7 @@ void emitter::emitIns_J_S(instruction ins, emitAttr attr, BasicBlock* dst, int v id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); } /***************************************************************************** @@ -5534,7 +5563,7 @@ void emitter::emitIns_R_L(instruction ins, emitAttr attr, BasicBlock* dst, regNu id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); } /***************************************************************************** @@ -5595,7 +5624,7 @@ void emitter::emitIns_I_AR(instruction ins, emitAttr attr, int val, regNumber re id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); } void emitter::emitIns_I_AI(instruction ins, emitAttr attr, int val, ssize_t disp) @@ -5651,7 +5680,7 @@ void emitter::emitIns_I_AI(instruction ins, emitAttr attr, int val, ssize_t disp id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); } void emitter::emitIns_R_AR(instruction ins, emitAttr attr, regNumber reg, regNumber base, int disp) @@ -5681,7 +5710,7 @@ void emitter::emitIns_R_AI(instruction ins, emitAttr attr, regNumber ireg, ssize id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); } void emitter::emitIns_AR_R(instruction ins, emitAttr attr, regNumber reg, regNumber base, cnsval_ssize_t disp) @@ -5720,7 +5749,7 @@ void emitter::emitIns_S_R_I(instruction ins, emitAttr attr, int varNum, int offs id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); } void emitter::emitIns_A_R_I(instruction ins, emitAttr attr, GenTreeIndir* indir, regNumber reg, int imm) @@ -5736,7 +5765,7 @@ void emitter::emitIns_A_R_I(instruction ins, emitAttr attr, GenTreeIndir* indir, UNATIVE_OFFSET size = emitInsSizeAM(id, insCodeMR(ins), imm); id->idCodeSize(size); dispIns(id); - emitCurIGsize += size; + appendToCurIG(id); } void emitter::emitIns_AI_R(instruction ins, emitAttr attr, regNumber ireg, ssize_t disp) @@ -5771,7 +5800,7 @@ void emitter::emitIns_AI_R(instruction ins, emitAttr attr, regNumber ireg, ssize id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); emitAdjustStackDepthPushPop(ins); } @@ -5822,7 +5851,7 @@ void emitter::emitIns_I_ARR(instruction ins, emitAttr attr, int val, regNumber r id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); } void emitter::emitIns_R_ARR(instruction ins, emitAttr attr, regNumber reg, regNumber base, regNumber index, int disp) @@ -5883,7 +5912,7 @@ void emitter::emitIns_I_ARX( id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); } void emitter::emitIns_R_ARX( @@ -5918,7 +5947,7 @@ void emitter::emitIns_R_ARX( id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); } void emitter::emitIns_ARX_R( @@ -5955,7 +5984,7 @@ void emitter::emitIns_ARX_R( id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); emitAdjustStackDepthPushPop(ins); } @@ -6006,7 +6035,7 @@ void emitter::emitIns_I_AX(instruction ins, emitAttr attr, int val, regNumber re id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); } void emitter::emitIns_R_AX(instruction ins, emitAttr attr, regNumber ireg, regNumber reg, unsigned mul, int disp) @@ -6032,7 +6061,7 @@ void emitter::emitIns_R_AX(instruction ins, emitAttr attr, regNumber ireg, regNu id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); } void emitter::emitIns_AX_R(instruction ins, emitAttr attr, regNumber ireg, regNumber reg, unsigned mul, int disp) @@ -6067,7 +6096,7 @@ void emitter::emitIns_AX_R(instruction ins, emitAttr attr, regNumber ireg, regNu id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); emitAdjustStackDepthPushPop(ins); } @@ -6851,7 +6880,7 @@ void emitter::emitIns_S(instruction ins, emitAttr attr, int varx, int offs) id->idDebugOnlyInfo()->idVarRefOffs = emitVarRefOffs; #endif dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); emitAdjustStackDepthPushPop(ins); } @@ -6881,7 +6910,7 @@ void emitter::emitIns_S_R(instruction ins, emitAttr attr, regNumber ireg, int va id->idDebugOnlyInfo()->idVarRefOffs = emitVarRefOffs; #endif dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); } void emitter::emitIns_R_S(instruction ins, emitAttr attr, regNumber ireg, int varx, int offs) @@ -6904,7 +6933,7 @@ void emitter::emitIns_R_S(instruction ins, emitAttr attr, regNumber ireg, int va id->idDebugOnlyInfo()->idVarRefOffs = emitVarRefOffs; #endif dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); } void emitter::emitIns_S_I(instruction ins, emitAttr attr, int varx, int offs, int val) @@ -6947,7 +6976,7 @@ void emitter::emitIns_S_I(instruction ins, emitAttr attr, int varx, int offs, in id->idDebugOnlyInfo()->idVarRefOffs = emitVarRefOffs; #endif dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); } /***************************************************************************** @@ -7130,7 +7159,7 @@ void emitter::emitIns_J(instruction ins, BasicBlock* dst, int instrCount /* = 0 id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); emitAdjustStackDepthPushPop(ins); } @@ -7518,7 +7547,7 @@ void emitter::emitIns_Call(EmitCallType callType, id->idCodeSize(sz); dispIns(id); - emitCurIGsize += sz; + appendToCurIG(id); #if !FEATURE_FIXED_OUT_ARGS @@ -8407,6 +8436,17 @@ void emitter::emitDispIns( instruction ins = id->idIns(); + if (ins == INS_mov_eliminated) + { + // Elideable moves are specified to have a zero size, but are carried + // in emit so we can still do the relevant byref liveness update + + assert(id->idGCref() == GCT_BYREF); + assert(id->idCodeSize() == 0); + + return; + } + if (emitComp->verbose) { unsigned idNum = id->idDebugOnlyInfo()->idNum; @@ -11784,6 +11824,21 @@ BYTE* emitter::emitOutputRR(BYTE* dst, instrDesc* id) regNumber reg2 = id->idReg2(); emitAttr size = id->idOpSize(); + regNumber regFor012Bits = REG_NA; + regNumber regFor345Bits = REG_NA; + unsigned regCode = 0; + + if (ins == INS_mov_eliminated) + { + // Elideable moves are specified to have a zero size, but are carried + // in emit so we can still do the relevant byref liveness update + + assert(id->idGCref() == GCT_BYREF); + assert(id->idCodeSize() == 0); + + goto UPDATE_LIVENESS; + } + if (IsSSEOrAVXInstruction(ins)) { assert((ins != INS_movd) || (isFloatReg(reg1) != isFloatReg(reg2))); @@ -11903,8 +11958,9 @@ BYTE* emitter::emitOutputRR(BYTE* dst, instrDesc* id) } } - regNumber regFor012Bits = reg2; - regNumber regFor345Bits = REG_NA; + regFor012Bits = reg2; + regFor345Bits = REG_NA; + if (IsBMIInstruction(ins)) { regFor345Bits = getBmiRegNumber(ins); @@ -11922,7 +11978,7 @@ BYTE* emitter::emitOutputRR(BYTE* dst, instrDesc* id) } } - unsigned regCode = insEncodeReg345(ins, regFor345Bits, size, &code); + regCode = insEncodeReg345(ins, regFor345Bits, size, &code); regCode |= insEncodeReg012(ins, regFor012Bits, size, &code); if (TakesVexPrefix(ins)) @@ -11987,6 +12043,8 @@ BYTE* emitter::emitOutputRR(BYTE* dst, instrDesc* id) dst += emitOutputByte(dst, (0xC0 | regCode)); } +UPDATE_LIVENESS: + // Does this instruction operate on a GC ref value? if (id->idGCref()) { diff --git a/src/coreclr/jit/instrsarm.h b/src/coreclr/jit/instrsarm.h index 9356150d4b2e83..6ba160994692a2 100644 --- a/src/coreclr/jit/instrsarm.h +++ b/src/coreclr/jit/instrsarm.h @@ -146,20 +146,21 @@ INST6(ldrsh, "ldrsh", 0,LD, IF_EN6A, 0x5E00, BAD_CODE, 0xF9300000, // ldrsh Rt,[Rn+i12] T2_K1 111110011011nnnn ttttiiiiiiiiiiii F9B0 0000 imm(0-4095) // ldrsh Rt,[PC+i12] T2_K4 11111001U0111111 ttttiiiiiiiiiiii F93F 0000 imm(+-4095) -// enum name FP LD/ST Rd, Rm Rd,Rm Rd,i8 Rd,+i8<