diff --git a/src/coreclr/jit/codegen.h b/src/coreclr/jit/codegen.h index 8646d9191edcf1..663dd070caae42 100644 --- a/src/coreclr/jit/codegen.h +++ b/src/coreclr/jit/codegen.h @@ -1472,12 +1472,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX public: void instGen(instruction ins); -#if defined(TARGET_XARCH) void inst_JMP(emitJumpKind jmp, BasicBlock* tgtBlock, bool isRemovableJmpCandidate = false); -#else - void inst_JMP(emitJumpKind jmp, BasicBlock* tgtBlock); -#endif - void inst_SET(emitJumpKind condition, regNumber reg); void inst_RV(instruction ins, regNumber reg, var_types type, emitAttr size = EA_UNKNOWN); diff --git a/src/coreclr/jit/codegenarmarch.cpp b/src/coreclr/jit/codegenarmarch.cpp index f5de40823259e4..a706893053fd57 100644 --- a/src/coreclr/jit/codegenarmarch.cpp +++ b/src/coreclr/jit/codegenarmarch.cpp @@ -4288,18 +4288,6 @@ void CodeGen::inst_SETCC(GenCondition condition, var_types type, regNumber dstRe #endif } -//------------------------------------------------------------------------ -// inst_JMP: Generate a jump instruction. -// -void CodeGen::inst_JMP(emitJumpKind jmp, BasicBlock* tgtBlock) -{ -#if !FEATURE_FIXED_OUT_ARGS - assert((tgtBlock->bbTgtStkDepth * sizeof(int) == genStackLevel) || isFramePointerUsed()); -#endif // !FEATURE_FIXED_OUT_ARGS - - GetEmitter()->emitIns_J(emitter::emitJumpKindToIns(jmp), tgtBlock); -} - //------------------------------------------------------------------------ // genCodeForStoreBlk: Produce code for a GT_STORE_OBJ/GT_STORE_DYN_BLK/GT_STORE_BLK node. // diff --git a/src/coreclr/jit/codegenlinear.cpp b/src/coreclr/jit/codegenlinear.cpp index 6a44f1db324de1..b7272c5eb77ded 100644 --- a/src/coreclr/jit/codegenlinear.cpp +++ b/src/coreclr/jit/codegenlinear.cpp @@ -754,7 +754,7 @@ void CodeGen::genCodeForBBlist() case BBJ_ALWAYS: inst_JMP(EJ_jmp, block->bbJumpDest -#ifdef TARGET_AMD64 +#if defined(TARGET_AMD64) // AMD64 requires an instruction after a call instruction for unwinding // inside an EH region so if the last instruction generated was a call instruction // do not allow this jump to be marked for possible later removal. @@ -764,12 +764,9 @@ void CodeGen::genCodeForBBlist() , /* isRemovableJmpCandidate */ !GetEmitter()->emitIsLastInsCall() && !block->hasAlign() #else -#ifdef TARGET_XARCH , /* isRemovableJmpCandidate */ !block->hasAlign() -#endif - -#endif +#endif // defined(TARGET_AMD64) ); FALLTHROUGH; diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp index 039d4c11185225..6302e12440b3aa 100644 --- a/src/coreclr/jit/codegenxarch.cpp +++ b/src/coreclr/jit/codegenxarch.cpp @@ -1436,30 +1436,6 @@ void CodeGen::inst_SETCC(GenCondition condition, var_types type, regNumber dstRe } } -//------------------------------------------------------------------------ -// inst_JMP: Generate a jump instruction. -// -void CodeGen::inst_JMP(emitJumpKind jmp, BasicBlock* tgtBlock, bool isRemovableJmpCandidate) -{ -#if !FEATURE_FIXED_OUT_ARGS - // On the x86 we are pushing (and changing the stack level), but on x64 and other archs we have - // a fixed outgoing args area that we store into and we never change the stack level when calling methods. - // - // Thus only on x86 do we need to assert that the stack level at the target block matches the current stack level. - // - CLANG_FORMAT_COMMENT_ANCHOR; - -#ifdef UNIX_X86_ABI - // bbTgtStkDepth is a (pure) argument count (stack alignment padding should be excluded). - assert((tgtBlock->bbTgtStkDepth * sizeof(int) == (genStackLevel - curNestedAlignment)) || isFramePointerUsed()); -#else - assert((tgtBlock->bbTgtStkDepth * sizeof(int) == genStackLevel) || isFramePointerUsed()); -#endif -#endif // !FEATURE_FIXED_OUT_ARGS - - GetEmitter()->emitIns_J(emitter::emitJumpKindToIns(jmp), tgtBlock, 0, isRemovableJmpCandidate); -} - //------------------------------------------------------------------------ // genCodeForReturnTrap: Produce code for a GT_RETURNTRAP node. // diff --git a/src/coreclr/jit/emit.cpp b/src/coreclr/jit/emit.cpp index ebd4f2120585af..b6bcc4f398038e 100644 --- a/src/coreclr/jit/emit.cpp +++ b/src/coreclr/jit/emit.cpp @@ -1006,14 +1006,19 @@ insGroup* emitter::emitSavIG(bool emitAdd) assert(emitLastIns != nullptr); assert(emitCurIGfreeBase <= (BYTE*)emitLastIns); assert((BYTE*)emitLastIns < emitCurIGfreeBase + sz); - -#if defined(TARGET_XARCH) assert(emitLastIns != nullptr); - if (emitLastIns->idIns() == INS_jmp) + +#ifdef TARGET_XARCH + instruction jmpInstr = INS_jmp; +#else // TARGET_ARM64 + instruction jmpInstr = INS_b; +#endif + + if (emitLastIns->idIns() == jmpInstr) + { ig->igFlags |= IGF_HAS_REMOVABLE_JMP; } -#endif emitLastIns = (instrDesc*)((BYTE*)id + ((BYTE*)emitLastIns - (BYTE*)emitCurIGfreeBase)); } @@ -3821,7 +3826,6 @@ void emitter::emitDispIG(insGroup* ig, insGroup* igPrev, bool verbose) { instrDesc* id = (instrDesc*)ins; -#ifdef TARGET_XARCH if (emitJmpInstHasNoCode(id)) { // an instruction with no code prevents us being able to iterate to the @@ -3830,12 +3834,10 @@ void emitter::emitDispIG(insGroup* ig, insGroup* igPrev, bool verbose) assert(cnt == 1); break; } -#endif emitDispIns(id, false, true, false, ofs, nullptr, 0, ig); ins += emitSizeOfInsDsc(id); ofs += id->idCodeSize(); - } while (--cnt); printf("\n"); @@ -3897,12 +3899,7 @@ void emitter::emitDispJumpList() printf("IG%02u IN%04x %3s[%u] -> IG%02u %s\n", jmp->idjIG->igNum, jmp->idDebugOnlyInfo()->idNum, codeGen->genInsDisplayName(jmp), jmp->idCodeSize(), ((insGroup*)emitCodeGetCookie(jmp->idAddr()->iiaBBlabel))->igNum, -#if defined(TARGET_XARCH) - jmp->idjIsRemovableJmpCandidate ? " ; removal candidate" : "" -#else - "" -#endif - ); + jmp->idjIsRemovableJmpCandidate ? " ; removal candidate" : ""); jmpCount += 1; } printf(" total jump count: %u\n", jmpCount); @@ -4157,7 +4154,6 @@ void emitter::emitDispCommentForHandle(size_t handle, GenTreeFlags flag) // void emitter::emitRemoveJumpToNextInst() { -#ifdef TARGET_XARCH if (!emitContainsRemovableJmpCandidates) { return; @@ -4189,8 +4185,16 @@ void emitter::emitRemoveJumpToNextInst() insGroup* jmpGroup = jmp->idjIG; instrDescJmp* nextJmp = jmp->idjNext; - if (jmp->idInsFmt() == IF_LABEL && emitIsUncondJump(jmp) && jmp->idjIsRemovableJmpCandidate) + if ( +#ifdef TARGET_XARCH + jmp->idInsFmt() == IF_LABEL && +#endif + emitIsUncondJump(jmp) && jmp->idjIsRemovableJmpCandidate) { +#ifdef TARGET_ARMARCH + assert(jmp->idInsOpt() == INS_OPTS_JMP); +#endif + #if DEBUG assert((jmpGroup->igFlags & IGF_HAS_ALIGN) == 0); assert((jmpGroup->igNum > previousJumpIgNum) || (previousJumpIgNum == (UNATIVE_OFFSET)-1) || @@ -4261,7 +4265,11 @@ void emitter::emitRemoveJumpToNextInst() } UNATIVE_OFFSET codeSize = jmp->idCodeSize(); +#ifdef TARGET_XARCH jmp->idCodeSize(0); +#else + jmp->idInsOpt(INS_OPTS_NONE); +#endif jmpGroup->igSize -= (unsigned short)codeSize; jmpGroup->igFlags |= IGF_UPD_ISZ; @@ -4342,7 +4350,6 @@ void emitter::emitRemoveJumpToNextInst() JITDUMP("emitRemoveJumpToNextInst removed no unconditional jumps\n"); } #endif // DEBUG -#endif // TARGET_XARCH } /***************************************************************************** diff --git a/src/coreclr/jit/emit.h b/src/coreclr/jit/emit.h index fc90bd96d6909e..5078aac2359dc1 100644 --- a/src/coreclr/jit/emit.h +++ b/src/coreclr/jit/emit.h @@ -983,6 +983,12 @@ class emitter return (idIns() == INS_align) && (idInsOpt() == INS_OPTS_NONE); } + inline bool idIsEmptyJmp() const + { + return (idIns() == INS_b) && (((instrDescJmp*)this)->idjIsRemovableJmpCandidate) && + (idInsOpt() == INS_OPTS_NONE); + } + unsigned idCodeSize() const { int size = 4; @@ -1012,6 +1018,12 @@ class emitter size = 0; } break; + case IF_BI_0A: + if (idIsEmptyJmp()) + { + size = 0; + } + FALLTHROUGH; default: break; } @@ -1539,15 +1551,10 @@ class emitter // After emission, for forward jumps, this is the target offset -- in bytes from the // beginning of the function -- of the target instruction of the jump, used to // determine if this jump needs to be patched. - unsigned idjOffs : -#if defined(TARGET_XARCH) - 29; + unsigned idjOffs : 29; // indicates that the jump was added at the end of a BBJ_ALWAYS basic block and is // a candidate for being removed if it jumps to the next instruction unsigned idjIsRemovableJmpCandidate : 1; -#else - 30; -#endif unsigned idjShort : 1; // is the jump known to be a short one? unsigned idjKeepLong : 1; // should the jump be kept long? (used for hot to cold and cold to hot jumps) }; @@ -2137,6 +2144,63 @@ class emitter void emitInitIG(insGroup* ig); void emitInsertIGAfter(insGroup* insertAfterIG, insGroup* ig); +#if defined(TARGET_XARCH) + //------------------------------------------------------------------------ + // emitAlignInstHasNoCode: Returns true if the 'id' is an align instruction + // that was later removed and hence has codeSize==0. + // + // Arguments: + // id -- The instruction to check + // + inline bool emitAlignInstHasNoCode(instrDesc* id) + { + return (id->idIns() == INS_align) && (id->idCodeSize() == 0); + } + + //------------------------------------------------------------------------ + // emitJmpInstHasNoCode: Returns true if the 'id' is a jump instruction + // that was later removed and hence has codeSize==0. + // + // Arguments: + // id -- The instruction to check + // + inline bool emitJmpInstHasNoCode(instrDesc* id) + { + bool result = (id->idIns() == INS_jmp) && (id->idCodeSize() == 0); + + // A zero size jump instruction can only be the one that is marked + // as removable candidate. + assert(!result || ((instrDescJmp*)id)->idjIsRemovableJmpCandidate); + + return result; + } + + //------------------------------------------------------------------------ + // emitInstHasNoCode: Returns true if the 'id' is an instruction + // that was later removed and hence has codeSize==0. + // Currently it is one of `align` or `jmp`. + // + // Arguments: + // id -- The instruction to check + inline bool emitInstHasNoCode(instrDesc* id) + { + return emitAlignInstHasNoCode(id) || emitJmpInstHasNoCode(id); + } +#else + //------------------------------------------------------------------------ + // emitJmpInstHasNoCode: Returns true if the 'id' is a 'branch' instruction + // that was later removed + // + // Arguments: + // id -- The instruction to check + // + inline bool emitJmpInstHasNoCode(instrDesc* id) + { + return emitIsUncondJump(id) && (((instrDescJmp*)id)->idjIsRemovableJmpCandidate) && + (id->idInsOpt() == INS_OPTS_NONE); + } +#endif + void emitNewIG(); #if !defined(JIT32_GCENCODER) @@ -2144,12 +2208,6 @@ class emitter void emitEnableGC(); #endif // !defined(JIT32_GCENCODER) -#if defined(TARGET_XARCH) - static bool emitAlignInstHasNoCode(instrDesc* id); - static bool emitInstHasNoCode(instrDesc* id); - static bool emitJmpInstHasNoCode(instrDesc* id); -#endif - void emitGenIG(insGroup* ig); insGroup* emitSavIG(bool emitAdd = false); void emitNxtIG(bool extend = false); diff --git a/src/coreclr/jit/emitarm.cpp b/src/coreclr/jit/emitarm.cpp index ccf3ddff357099..ef79fec0d34606 100644 --- a/src/coreclr/jit/emitarm.cpp +++ b/src/coreclr/jit/emitarm.cpp @@ -4334,7 +4334,10 @@ 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, + int instrCount /* = 0 */, + bool isRemovableJmpCandidate /* = false */) { insFormat fmt = IF_NONE; @@ -4394,6 +4397,18 @@ void emitter::emitIns_J(instruction ins, BasicBlock* dst, int instrCount /* = 0 /* Assume the jump will be long */ id->idjShort = 0; + emitContainsRemovableJmpCandidates |= isRemovableJmpCandidate; + + if (isRemovableJmpCandidate) + { + id->idInsOpt(INS_OPTS_JMP); + id->idjIsRemovableJmpCandidate = 1; + } + else + { + id->idjIsRemovableJmpCandidate = 0; + } + if (dst != NULL) { id->idAddr()->iiaBBlabel = dst; @@ -6406,10 +6421,18 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) case IF_T2_N1: // T2_N .....i......iiii .iiiddddiiiiiiii R1 imm16 case IF_LARGEJMP: assert(id->idGCref() == GCT_NONE); - assert(id->idIsBound()); - dst = emitOutputLJ(ig, dst, id); - sz = sizeof(instrDescJmp); + if (!emitJmpInstHasNoCode(id)) + { + assert(id->idIsBound()); + dst = emitOutputLJ(ig, dst, id); + } + else + { + assert(((instrDescJmp*)id)->idjIsRemovableJmpCandidate); + } + + sz = sizeof(instrDescJmp); break; case IF_T1_D1: // T1_D1 .........mmmm... R1* @@ -6661,7 +6684,7 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) size_t expected = emitSizeOfInsDsc(id); assert(sz == expected); - if (emitComp->opts.disAsm || emitComp->verbose) + if ((emitComp->opts.disAsm || emitComp->verbose) && !emitJmpInstHasNoCode(id)) { emitDispIns(id, false, dspOffs, true, emitCurCodeOffs(odst), *dp, (dst - *dp), ig); } @@ -6700,7 +6723,7 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) /* All instructions are expected to generate code */ - assert(*dp != dst); + assert(*dp != dst || emitJmpInstHasNoCode(id)); *dp = dst; diff --git a/src/coreclr/jit/emitarm.h b/src/coreclr/jit/emitarm.h index 09133a1d88f02d..9dd2c175190dcb 100644 --- a/src/coreclr/jit/emitarm.h +++ b/src/coreclr/jit/emitarm.h @@ -192,12 +192,6 @@ inline static unsigned getBitWidth(emitAttr size) return (unsigned)size * BITS_PER_BYTE; } -/************************************************************************/ -/* Output target-independent instructions */ -/************************************************************************/ - -void emitIns_J(instruction ins, BasicBlock* dst, int instrCount = 0); - /************************************************************************/ /* The public entry points to output instructions */ /************************************************************************/ diff --git a/src/coreclr/jit/emitarm64.cpp b/src/coreclr/jit/emitarm64.cpp index 864ba862edddd7..db61935f5a4656 100644 --- a/src/coreclr/jit/emitarm64.cpp +++ b/src/coreclr/jit/emitarm64.cpp @@ -8402,7 +8402,10 @@ 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, + int instrCount /* = 0 */, + bool isRemovableJmpCandidate /* = false */) { insFormat fmt = IF_NONE; @@ -8454,6 +8457,16 @@ void emitter::emitIns_J(instruction ins, BasicBlock* dst, int instrCount) id->idIns(ins); id->idInsFmt(fmt); + emitContainsRemovableJmpCandidates |= isRemovableJmpCandidate; + if (isRemovableJmpCandidate) + { + id->idInsOpt(INS_OPTS_JMP); + id->idjIsRemovableJmpCandidate = 1; + } + else + { + id->idjIsRemovableJmpCandidate = 0; + } id->idjShort = idjShort; #ifdef DEBUG @@ -9939,7 +9952,7 @@ BYTE* emitter::emitOutputLJ(insGroup* ig, BYTE* dst, instrDesc* i) NYI_ARM64("Relocation Support for long address"); } - assert(insOptsNone(id->idInsOpt())); + assert(insOptsNone(id->idInsOpt()) || (isJump && id->idjIsRemovableJmpCandidate)); if (isJump) { @@ -10375,9 +10388,17 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) case IF_BI_0B: // BI_0B ......iiiiiiiiii iiiiiiiiiii..... simm19:00 case IF_LARGEJMP: assert(id->idGCref() == GCT_NONE); - assert(id->idIsBound()); - dst = emitOutputLJ(ig, dst, id); - sz = sizeof(instrDescJmp); + + if (!emitJmpInstHasNoCode(id)) + { + assert(id->idIsBound()); + dst = emitOutputLJ(ig, dst, id); + } + else + { + assert(((instrDescJmp*)id)->idjIsRemovableJmpCandidate); + } + sz = sizeof(instrDescJmp); break; case IF_BI_0C: // BI_0C ......iiiiiiiiii iiiiiiiiiiiiiiii simm26:00 @@ -11613,7 +11634,7 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) size_t expected = emitSizeOfInsDsc(id); assert(sz == expected); - if (emitComp->opts.disAsm || emitComp->verbose) + if ((emitComp->opts.disAsm || emitComp->verbose) && !emitJmpInstHasNoCode(id)) { emitDispIns(id, false, dspOffs, true, emitCurCodeOffs(odst), *dp, (dst - *dp), ig); } @@ -11637,7 +11658,7 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) /* All instructions are expected to generate code */ - assert(*dp != dst || id->idIsEmptyAlign()); + assert(*dp != dst || id->idIsEmptyAlign() || emitJmpInstHasNoCode(id)); *dp = dst; @@ -13955,7 +13976,14 @@ emitter::insExecutionCharacteristics emitter::getInsExecutionCharacteristics(ins // Branch Instructions // - case IF_BI_0A: // b, bl_local + case IF_BI_0A: // b, bl_local + if (emitJmpInstHasNoCode(id)) + { + result.insThroughput = PERFSCORE_THROUGHPUT_ZERO; + result.insLatency = PERFSCORE_LATENCY_ZERO; + break; + } + FALLTHROUGH; case IF_BI_0C: // bl, b_tail result.insThroughput = PERFSCORE_THROUGHPUT_1C; // but is Dual Issue result.insLatency = PERFSCORE_LATENCY_1C; diff --git a/src/coreclr/jit/emitarm64.h b/src/coreclr/jit/emitarm64.h index 8970c15b3c090c..0279b3360e75dd 100644 --- a/src/coreclr/jit/emitarm64.h +++ b/src/coreclr/jit/emitarm64.h @@ -705,12 +705,6 @@ inline static ssize_t computeRelPageAddr(size_t dstAddr, size_t srcAddr) return (dstAddr >> 12) - (srcAddr >> 12); } -/************************************************************************/ -/* Output target-independent instructions */ -/************************************************************************/ - -void emitIns_J(instruction ins, BasicBlock* dst, int instrCount = 0); - /************************************************************************/ /* The public entry points to output instructions */ /************************************************************************/ diff --git a/src/coreclr/jit/emitpub.h b/src/coreclr/jit/emitpub.h index 6fc2a787074172..307d3e63b26ab5 100644 --- a/src/coreclr/jit/emitpub.h +++ b/src/coreclr/jit/emitpub.h @@ -72,6 +72,12 @@ UNATIVE_OFFSET emitCodeOffset(void* blockPtr, unsigned codeOffs); const char* emitOffsetToLabel(unsigned offs); #endif // DEBUG +/************************************************************************/ +/* Output target-independent instructions */ +/************************************************************************/ + +void emitIns_J(instruction ins, BasicBlock* dst, int instrCount = 0, bool isRemovableJmpCandidate = false); + /************************************************************************/ /* Emit initialized data sections */ /************************************************************************/ diff --git a/src/coreclr/jit/emitxarch.cpp b/src/coreclr/jit/emitxarch.cpp index 707c8fc25f7c38..d1e86e2d3c3240 100644 --- a/src/coreclr/jit/emitxarch.cpp +++ b/src/coreclr/jit/emitxarch.cpp @@ -2059,49 +2059,6 @@ const emitJumpKind emitReverseJumpKinds[] = { return emitReverseJumpKinds[jumpKind]; } -//------------------------------------------------------------------------ -// emitAlignInstHasNoCode: Returns true if the 'id' is an align instruction -// that was later removed and hence has codeSize==0. -// -// Arguments: -// id -- The instruction to check -// -/* static */ bool emitter::emitAlignInstHasNoCode(instrDesc* id) -{ - return (id->idIns() == INS_align) && (id->idCodeSize() == 0); -} - -//------------------------------------------------------------------------ -// emitJmpInstHasNoCode: Returns true if the 'id' is a jump instruction -// that was later removed and hence has codeSize==0. -// -// Arguments: -// id -- The instruction to check -// -/* static */ bool emitter::emitJmpInstHasNoCode(instrDesc* id) -{ - bool result = (id->idIns() == INS_jmp) && (id->idCodeSize() == 0); - - // A zero size jump instruction can only be the one that is marked - // as removable candidate. - assert(!result || ((instrDescJmp*)id)->idjIsRemovableJmpCandidate); - - return result; -} - -//------------------------------------------------------------------------ -// emitInstHasNoCode: Returns true if the 'id' is an instruction -// that was later removed and hence has codeSize==0. -// Currently it is one of `align` or `jmp`. -// -// Arguments: -// id -- The instruction to check -// -/* static */ bool emitter::emitInstHasNoCode(instrDesc* id) -{ - return emitAlignInstHasNoCode(id) || emitJmpInstHasNoCode(id); -} - /***************************************************************************** * When encoding instructions that operate on byte registers * we have to ensure that we use a low register (EAX, EBX, ECX or EDX) @@ -9951,6 +9908,7 @@ void emitter::emitDispIns( { printf("L_M%03u_" FMT_BB, emitComp->compMethodID, id->idAddr()->iiaBBlabel->bbNum); } + break; case IF_METHOD: @@ -14724,7 +14682,8 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) assert((int)emitCurStackLvl >= 0); - // Only epilog "instructions", some pseudo-instrs and blocks that ends with a jump to the next block + // Only epilog "instructions", some pseudo-instrs and block ending jumps + // are allowed not to generate any code assert(*dp != dst || emitInstHasNoCode(id)); diff --git a/src/coreclr/jit/emitxarch.h b/src/coreclr/jit/emitxarch.h index 754632bb1c0896..2f218cfcb0dcdc 100644 --- a/src/coreclr/jit/emitxarch.h +++ b/src/coreclr/jit/emitxarch.h @@ -301,12 +301,6 @@ inline emitAttr emitDecodeScale(unsigned ensz) return emitter::emitSizeDecode[ensz]; } -/************************************************************************/ -/* Output target-independent instructions */ -/************************************************************************/ - -void emitIns_J(instruction ins, BasicBlock* dst, int instrCount = 0, bool isRemovableJmpCandidate = false); - /************************************************************************/ /* The public entry points to output instructions */ /************************************************************************/ diff --git a/src/coreclr/jit/instr.cpp b/src/coreclr/jit/instr.cpp index 9843482bbef6c6..7f467e110670de 100644 --- a/src/coreclr/jit/instr.cpp +++ b/src/coreclr/jit/instr.cpp @@ -256,6 +256,32 @@ bool CodeGenInterface::instIsFP(instruction ins) #endif } +/***************************************************************************** + * + * Generate a jump instruction. + */ + +void CodeGen::inst_JMP(emitJumpKind jmp, BasicBlock* tgtBlock, bool isRemovableJmpCandidate) +{ +#if !FEATURE_FIXED_OUT_ARGS + // On the x86 we are pushing (and changing the stack level), but on x64 and other archs we have + // a fixed outgoing args area that we store into and we never change the stack level when calling methods. + // + // Thus only on x86 do we need to assert that the stack level at the target block matches the current stack level. + // + CLANG_FORMAT_COMMENT_ANCHOR; + +#ifdef UNIX_X86_ABI + // bbTgtStkDepth is a (pure) argument count (stack alignment padding should be excluded). + assert((tgtBlock->bbTgtStkDepth * sizeof(int) == (genStackLevel - curNestedAlignment)) || isFramePointerUsed()); +#else + assert((tgtBlock->bbTgtStkDepth * sizeof(int) == genStackLevel) || isFramePointerUsed()); +#endif +#endif // !FEATURE_FIXED_OUT_ARGS + + GetEmitter()->emitIns_J(emitter::emitJumpKindToIns(jmp), tgtBlock, 0, isRemovableJmpCandidate); +} + /***************************************************************************** * * Generate a set instruction. diff --git a/src/coreclr/jit/instr.h b/src/coreclr/jit/instr.h index a01492d08b8a95..9577021a6ead09 100644 --- a/src/coreclr/jit/instr.h +++ b/src/coreclr/jit/instr.h @@ -172,7 +172,8 @@ enum insOpts: unsigned INS_OPTS_LSL, INS_OPTS_LSR, INS_OPTS_ASR, - INS_OPTS_ROR + INS_OPTS_ROR, + INS_OPTS_JMP }; enum insBarrier : unsigned { @@ -237,6 +238,7 @@ enum insOpts : unsigned #if FEATURE_LOOP_ALIGN , INS_OPTS_ALIGN // Align instruction #endif + , INS_OPTS_JMP // Jump instruction }; enum insCond : unsigned