From 75c5b6e2634a141786c172a77c70064e7d01a172 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Wed, 27 Oct 2021 14:02:29 +0200 Subject: [PATCH 1/6] Refactor IL_OFFSETX into DebugInfo Remove IL_OFFSETX in favor of a DebugInfo structure. Previously we were packing extra information into the upper bits of IL_OFFSETX, which are now separate bit fields on a new ILLocation structure. DebugInfo contains an ILLocation and also an inline context, which will be used in the future when tracking debug info inside of inlinees. Another problem with IL_OFFSETX was that there were several sentinel values used to describe prologs, epilogs and no mappings. However these were only used in code-gen, so refactor codegen to track this separately instead of having to muddle it into IL_OFFSETX. This makes it clearer what we can expect from IL offsets during JIT. This change is no-diff and PIN also shows that TP is not negatively affected; in fact, there seems to be a small TP gain, maybe because we don't have to handle sentinel values anymore. --- src/coreclr/inc/cordebuginfo.h | 2 +- src/coreclr/jit/codegen.h | 17 +- src/coreclr/jit/codegenarm.cpp | 4 +- src/coreclr/jit/codegenarm64.cpp | 2 +- src/coreclr/jit/codegenarmarch.cpp | 18 +- src/coreclr/jit/codegencommon.cpp | 247 +++++--------------- src/coreclr/jit/codegenlinear.cpp | 20 +- src/coreclr/jit/codegenxarch.cpp | 22 +- src/coreclr/jit/compiler.cpp | 123 +--------- src/coreclr/jit/compiler.h | 111 ++++----- src/coreclr/jit/compiler.hpp | 11 +- src/coreclr/jit/ee_il_dll.cpp | 65 ++++-- src/coreclr/jit/emit.cpp | 4 +- src/coreclr/jit/emitarm.cpp | 6 +- src/coreclr/jit/emitarm.h | 2 +- src/coreclr/jit/emitarm64.cpp | 6 +- src/coreclr/jit/emitarm64.h | 2 +- src/coreclr/jit/emitxarch.cpp | 6 +- src/coreclr/jit/emitxarch.h | 2 +- src/coreclr/jit/fgbasic.cpp | 8 +- src/coreclr/jit/fginline.cpp | 24 +- src/coreclr/jit/fgopt.cpp | 2 +- src/coreclr/jit/fgprofile.cpp | 67 +++--- src/coreclr/jit/fgstmt.cpp | 14 +- src/coreclr/jit/flowgraph.cpp | 2 +- src/coreclr/jit/gentree.cpp | 56 +++-- src/coreclr/jit/gentree.h | 139 +++++++++-- src/coreclr/jit/importer.cpp | 186 +++++++-------- src/coreclr/jit/indirectcalltransformer.cpp | 14 +- src/coreclr/jit/inline.cpp | 27 +-- src/coreclr/jit/inline.h | 10 +- src/coreclr/jit/inlinepolicy.cpp | 4 +- src/coreclr/jit/inlinepolicy.h | 4 +- src/coreclr/jit/jit.h | 34 +-- src/coreclr/jit/morph.cpp | 44 ++-- src/coreclr/jit/optimizer.cpp | 2 +- src/coreclr/jit/rationalize.cpp | 7 +- 37 files changed, 592 insertions(+), 722 deletions(-) diff --git a/src/coreclr/inc/cordebuginfo.h b/src/coreclr/inc/cordebuginfo.h index 4de783aae9845..e2683d36ddcd7 100644 --- a/src/coreclr/inc/cordebuginfo.h +++ b/src/coreclr/inc/cordebuginfo.h @@ -52,7 +52,7 @@ class ICorDebugInfo struct OffsetMapping { uint32_t nativeOffset; - uint32_t ilOffset; + uint32_t ilOffset; // IL offset or one of the special values in MappingTypes SourceTypes source; // The debugger needs this so that // we don't put Edit and Continue breakpoints where // the stack isn't empty. We can put regular breakpoints diff --git a/src/coreclr/jit/codegen.h b/src/coreclr/jit/codegen.h index 0fa215587eba2..74420b2883260 100644 --- a/src/coreclr/jit/codegen.h +++ b/src/coreclr/jit/codegen.h @@ -434,10 +434,10 @@ class CodeGen final : public CodeGenInterface CORINFO_METHOD_HANDLE methHnd, INDEBUG_LDISASM_COMMA(CORINFO_SIG_INFO* sigInfo) void* addr - X86_ARG(int argSize), + X86_ARG(int argSize), emitAttr retSize MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize), - IL_OFFSETX ilOffset, + const DebugInfo& di, regNumber base, bool isJump); // clang-format on @@ -447,10 +447,10 @@ class CodeGen final : public CodeGenInterface CORINFO_METHOD_HANDLE methHnd, INDEBUG_LDISASM_COMMA(CORINFO_SIG_INFO* sigInfo) GenTreeIndir* indir - X86_ARG(int argSize), + X86_ARG(int argSize), emitAttr retSize MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize), - IL_OFFSETX ilOffset, + const DebugInfo& di, bool isJump); // clang-format on @@ -552,15 +552,16 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */ #ifdef DEBUG - void genIPmappingDisp(unsigned mappingNum, Compiler::IPmappingDsc* ipMapping); + void genIPmappingDisp(unsigned mappingNum, IPmappingDsc* ipMapping); void genIPmappingListDisp(); #endif // DEBUG - void genIPmappingAdd(IL_OFFSETX offset, bool isLabel); - void genIPmappingAddToFront(IL_OFFSETX offset); + IPmappingDsc* genCreateIPMapping(IPmappingDscKind kind, const DebugInfo& di, bool isLabel); + void genIPmappingAdd(IPmappingDscKind kind, const DebugInfo& di, bool isLabel); + void genIPmappingAddToFront(IPmappingDscKind kind, const DebugInfo& di, bool isLabel); void genIPmappingGen(); - void genEnsureCodeEmitted(IL_OFFSETX offsx); + void genEnsureCodeEmitted(const DebugInfo& di); //------------------------------------------------------------------------- // scope info for the variables diff --git a/src/coreclr/jit/codegenarm.cpp b/src/coreclr/jit/codegenarm.cpp index 23beb66352978..12977ae1acee5 100644 --- a/src/coreclr/jit/codegenarm.cpp +++ b/src/coreclr/jit/codegenarm.cpp @@ -1630,7 +1630,7 @@ void CodeGen::genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize, INDEBUG_LDISASM_COMMA(nullptr) NULL, // addr argSize, retSize, gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur, - BAD_IL_OFFSET, // ilOffset + DebugInfo(), callTargetReg, // ireg REG_NA, 0, 0, // xreg, xmul, disp false // isJump @@ -1640,7 +1640,7 @@ void CodeGen::genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize, { GetEmitter()->emitIns_Call(emitter::EC_FUNC_TOKEN, compiler->eeFindHelper(helper), INDEBUG_LDISASM_COMMA(nullptr) addr, argSize, retSize, gcInfo.gcVarPtrSetCur, - gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur, BAD_IL_OFFSET, REG_NA, REG_NA, 0, + gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur, DebugInfo(), REG_NA, REG_NA, 0, 0, /* ilOffset, ireg, xreg, xmul, disp */ false /* isJump */ ); diff --git a/src/coreclr/jit/codegenarm64.cpp b/src/coreclr/jit/codegenarm64.cpp index 8f4db06485bb9..8229ed8539104 100644 --- a/src/coreclr/jit/codegenarm64.cpp +++ b/src/coreclr/jit/codegenarm64.cpp @@ -3841,7 +3841,7 @@ void CodeGen::genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize, GetEmitter()->emitIns_Call(callType, compiler->eeFindHelper(helper), INDEBUG_LDISASM_COMMA(nullptr) addr, argSize, retSize, EA_UNKNOWN, gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, - gcInfo.gcRegByrefSetCur, BAD_IL_OFFSET, /* IL offset */ + gcInfo.gcRegByrefSetCur, DebugInfo(), callTarget, /* ireg */ REG_NA, 0, 0, /* xreg, xmul, disp */ false /* isJump */ diff --git a/src/coreclr/jit/codegenarmarch.cpp b/src/coreclr/jit/codegenarmarch.cpp index 4f6f7890f50d9..547754cfc56c9 100644 --- a/src/coreclr/jit/codegenarmarch.cpp +++ b/src/coreclr/jit/codegenarmarch.cpp @@ -2462,16 +2462,14 @@ void CodeGen::genCallInstruction(GenTreeCall* call) } } - // We need to propagate the IL offset information to the call instruction, so we can emit + DebugInfo di; + // We need to propagate the debug information to the call instruction, so we can emit // an IL to native mapping record for the call, to support managed return value debugging. // We don't want tail call helper calls that were converted from normal calls to get a record, // so we skip this hash table lookup logic in that case. - - IL_OFFSETX ilOffset = BAD_IL_OFFSET; - - if (compiler->opts.compDbgInfo && compiler->genCallSite2ILOffsetMap != nullptr && !call->IsTailCall()) + if (compiler->opts.compDbgInfo && compiler->genCallSite2DebugInfoMap != nullptr && !call->IsTailCall()) { - (void)compiler->genCallSite2ILOffsetMap->Lookup(call, &ilOffset); + (void)compiler->genCallSite2DebugInfoMap->Lookup(call, &di); } CORINFO_SIG_INFO* sigInfo = nullptr; @@ -2511,7 +2509,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call) nullptr, // addr retSize MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(secondRetSize), - ilOffset, + di, target->GetRegNum(), call->IsFastTailCall()); // clang-format on @@ -2551,7 +2549,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call) nullptr, // addr retSize MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(secondRetSize), - ilOffset, + di, targetAddrReg, call->IsFastTailCall()); // clang-format on @@ -2599,7 +2597,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call) INDEBUG_LDISASM_COMMA(sigInfo) NULL, retSize, - ilOffset, + di, tmpReg, call->IsFastTailCall()); // clang-format on @@ -2614,7 +2612,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call) addr, retSize MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(secondRetSize), - ilOffset, + di, REG_NA, call->IsFastTailCall()); // clang-format on diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index 8c788286f292d..e333cdb32f260 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -125,7 +125,7 @@ CodeGen::CodeGen(Compiler* theCompiler) : CodeGenInterface(theCompiler) // Initialize the IP-mapping logic. compiler->genIPmappingList = nullptr; compiler->genIPmappingLast = nullptr; - compiler->genCallSite2ILOffsetMap = nullptr; + compiler->genCallSite2DebugInfoMap = nullptr; /* Assume that we not fully interruptible */ @@ -1798,7 +1798,7 @@ void CodeGen::genExitCode(BasicBlock* block) that this is ok */ // For non-optimized debuggable code, there is only one epilog. - genIPmappingAdd((IL_OFFSETX)ICorDebugInfo::EPILOG, true); + genIPmappingAdd(IPmappingDscKind::Epilog, DebugInfo(), true); bool jmpEpilog = ((block->bbFlags & BBF_HAS_JMP) != 0); if (compiler->getNeedsGSSecurityCookie()) @@ -6978,7 +6978,7 @@ void CodeGen::genFnProlog() // Do this so we can put the prolog instruction group ahead of // other instruction groups - genIPmappingAddToFront((IL_OFFSETX)ICorDebugInfo::PROLOG); + genIPmappingAddToFront(IPmappingDscKind::Prolog, DebugInfo(), true); #ifdef DEBUG if (compiler->opts.dspCode) @@ -8068,7 +8068,7 @@ void CodeGen::genFnEpilog(BasicBlock* block) gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur, - BAD_IL_OFFSET, // IL offset + DebugInfo(), indCallReg, // ireg REG_NA, // xreg 0, // xmul @@ -8469,7 +8469,8 @@ void CodeGen::genFnEpilog(BasicBlock* block) gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur, - BAD_IL_OFFSET, indCallReg, REG_NA, 0, 0, /* iloffset, ireg, xreg, xmul, disp */ + DebugInfo(), + indCallReg, REG_NA, 0, 0, /* ireg, xreg, xmul, disp */ true /* isJump */ ); // clang-format on @@ -10401,32 +10402,38 @@ const char* CodeGen::siStackVarName(size_t offs, size_t size, unsigned reg, unsi * Display a IPmappingDsc. Pass -1 as mappingNum to not display a mapping number. */ -void CodeGen::genIPmappingDisp(unsigned mappingNum, Compiler::IPmappingDsc* ipMapping) +void CodeGen::genIPmappingDisp(unsigned mappingNum, IPmappingDsc* ipMapping) { if (mappingNum != unsigned(-1)) { printf("%d: ", mappingNum); } - IL_OFFSETX offsx = ipMapping->ipmdILoffsx; - - if (offsx == BAD_IL_OFFSET) - { - printf("???"); - } - else + switch (ipMapping->ipmdKind) { - Compiler::eeDispILOffs(jitGetILoffsAny(offsx)); - - if (jitIsStackEmpty(offsx)) + case IPmappingDscKind::Prolog: + printf("PROLOG"); + break; + case IPmappingDscKind::Epilog: + printf("EPILOG"); + break; + case IPmappingDscKind::NoMapping: + printf("NO_MAP"); + break; + case IPmappingDscKind::Normal: + const ILLocation& loc = ipMapping->ipmdLoc; + Compiler::eeDispILOffs(loc.GetOffset()); + if (loc.IsStackEmpty()) { printf(" STACK_EMPTY"); } - if (jitIsCallInstruction(offsx)) + if (loc.IsCall()) { printf(" CALL_INSTRUCTION"); } + + break; } printf(" "); @@ -10445,7 +10452,7 @@ void CodeGen::genIPmappingDisp(unsigned mappingNum, Compiler::IPmappingDsc* ipMa void CodeGen::genIPmappingListDisp() { unsigned mappingNum = 0; - Compiler::IPmappingDsc* ipMapping; + IPmappingDsc* ipMapping; for (ipMapping = compiler->genIPmappingList; ipMapping != nullptr; ipMapping = ipMapping->ipmdNext) { @@ -10463,34 +10470,34 @@ void CodeGen::genIPmappingListDisp() * Record the instr offset as being at the current code gen position. */ -void CodeGen::genIPmappingAdd(IL_OFFSETX offsx, bool isLabel) +void CodeGen::genIPmappingAdd(IPmappingDscKind kind, const DebugInfo& di, bool isLabel) { if (!compiler->opts.compDbgInfo) { return; } - assert(offsx != BAD_IL_OFFSET); + assert((kind == IPmappingDscKind::Normal) == (di.GetLocation().IsValid())); - switch ((int)offsx) // Need the cast since offs is unsigned and the case statements are comparing to signed. + switch (kind) { - case ICorDebugInfo::PROLOG: - case ICorDebugInfo::EPILOG: + case IPmappingDscKind::Prolog: + case IPmappingDscKind::Epilog: break; default: - if (offsx != (IL_OFFSETX)ICorDebugInfo::NO_MAPPING) + if (kind == IPmappingDscKind::Normal) { - noway_assert(jitGetILoffs(offsx) <= compiler->info.compILCodeSize); + noway_assert(di.GetLocation().GetOffset() <= compiler->info.compILCodeSize); } - // Ignore this one if it's the same IL offset as the last one we saw. + // Ignore this one if it's the same IL location as the last one we saw. // Note that we'll let through two identical IL offsets if the flag bits // differ, or two identical "special" mappings (e.g., PROLOG). - if ((compiler->genIPmappingLast != nullptr) && (offsx == compiler->genIPmappingLast->ipmdILoffsx)) + if ((compiler->genIPmappingLast != nullptr) && (kind == compiler->genIPmappingLast->ipmdKind) && (di.GetLocation() == compiler->genIPmappingLast->ipmdLoc)) { - JITDUMP("genIPmappingAdd: ignoring duplicate IL offset 0x%x\n", offsx); + JITDUMP("genIPmappingAdd: ignoring duplicate IL offset 0x%x\n", di.GetLocation().GetOffset()); return; } break; @@ -10498,12 +10505,15 @@ void CodeGen::genIPmappingAdd(IL_OFFSETX offsx, bool isLabel) /* Create a mapping entry and append it to the list */ - Compiler::IPmappingDsc* addMapping = compiler->getAllocator(CMK_DebugInfo).allocate(1); + IPmappingDsc* addMapping = compiler->getAllocator(CMK_DebugInfo).allocate(1); addMapping->ipmdNativeLoc.CaptureLocation(GetEmitter()); - addMapping->ipmdILoffsx = offsx; + addMapping->ipmdKind = kind; + addMapping->ipmdLoc = di.GetLocation(); addMapping->ipmdIsLabel = isLabel; addMapping->ipmdNext = nullptr; + assert((kind == IPmappingDscKind::Normal) == (addMapping->ipmdLoc.IsValid())); + if (compiler->genIPmappingList != nullptr) { assert(compiler->genIPmappingLast != nullptr); @@ -10531,37 +10541,23 @@ void CodeGen::genIPmappingAdd(IL_OFFSETX offsx, bool isLabel) * * Prepend an IPmappingDsc struct to the list that we're maintaining * for the debugger. - * Record the instr offset as being at the current code gen position. */ -void CodeGen::genIPmappingAddToFront(IL_OFFSETX offsx) +void CodeGen::genIPmappingAddToFront(IPmappingDscKind kind, const DebugInfo& di, bool isLabel) { if (!compiler->opts.compDbgInfo) { return; } - assert(offsx != BAD_IL_OFFSET); - assert(compiler->compGeneratingProlog); // We only ever do this during prolog generation. - - switch ((int)offsx) // Need the cast since offs is unsigned and the case statements are comparing to signed. - { - case ICorDebugInfo::NO_MAPPING: - case ICorDebugInfo::PROLOG: - case ICorDebugInfo::EPILOG: - break; - - default: - noway_assert(jitGetILoffs(offsx) <= compiler->info.compILCodeSize); - break; - } + noway_assert((kind != IPmappingDscKind::Normal) || (di.GetLocation().IsValid() && (di.GetLocation().GetOffset() <= compiler->info.compILCodeSize))); /* Create a mapping entry and prepend it to the list */ - Compiler::IPmappingDsc* addMapping = compiler->getAllocator(CMK_DebugInfo).allocate(1); + IPmappingDsc* addMapping = compiler->getAllocator(CMK_DebugInfo).allocate(1); addMapping->ipmdNativeLoc.CaptureLocation(GetEmitter()); - addMapping->ipmdILoffsx = offsx; - addMapping->ipmdIsLabel = true; - addMapping->ipmdNext = nullptr; + addMapping->ipmdKind = kind; + addMapping->ipmdLoc = di.GetLocation(); + addMapping->ipmdIsLabel = isLabel; addMapping->ipmdNext = compiler->genIPmappingList; compiler->genIPmappingList = addMapping; @@ -10582,130 +10578,14 @@ void CodeGen::genIPmappingAddToFront(IL_OFFSETX offsx) /*****************************************************************************/ -C_ASSERT(IL_OFFSETX(ICorDebugInfo::NO_MAPPING) != IL_OFFSETX(BAD_IL_OFFSET)); -C_ASSERT(IL_OFFSETX(ICorDebugInfo::PROLOG) != IL_OFFSETX(BAD_IL_OFFSET)); -C_ASSERT(IL_OFFSETX(ICorDebugInfo::EPILOG) != IL_OFFSETX(BAD_IL_OFFSET)); - -C_ASSERT(IL_OFFSETX(BAD_IL_OFFSET) > MAX_IL_OFFSET); -C_ASSERT(IL_OFFSETX(ICorDebugInfo::NO_MAPPING) > MAX_IL_OFFSET); -C_ASSERT(IL_OFFSETX(ICorDebugInfo::PROLOG) > MAX_IL_OFFSET); -C_ASSERT(IL_OFFSETX(ICorDebugInfo::EPILOG) > MAX_IL_OFFSET); - -//------------------------------------------------------------------------ -// jitGetILoffs: Returns the IL offset portion of the IL_OFFSETX type. -// Asserts if any ICorDebugInfo distinguished value (like ICorDebugInfo::NO_MAPPING) -// is seen; these are unexpected here. Also asserts if passed BAD_IL_OFFSET. -// -// Arguments: -// offsx - the IL_OFFSETX value with the IL offset to extract. -// -// Return Value: -// The IL offset. - -IL_OFFSET jitGetILoffs(IL_OFFSETX offsx) -{ - assert(offsx != BAD_IL_OFFSET); - - switch ((int)offsx) // Need the cast since offs is unsigned and the case statements are comparing to signed. - { - case ICorDebugInfo::NO_MAPPING: - case ICorDebugInfo::PROLOG: - case ICorDebugInfo::EPILOG: - unreached(); - - default: - return IL_OFFSET(offsx & ~IL_OFFSETX_BITS); - } -} - -//------------------------------------------------------------------------ -// jitGetILoffsAny: Similar to jitGetILoffs(), but passes through ICorDebugInfo -// distinguished values. Asserts if passed BAD_IL_OFFSET. -// -// Arguments: -// offsx - the IL_OFFSETX value with the IL offset to extract. -// -// Return Value: -// The IL offset. - -IL_OFFSET jitGetILoffsAny(IL_OFFSETX offsx) -{ - assert(offsx != BAD_IL_OFFSET); - - switch ((int)offsx) // Need the cast since offs is unsigned and the case statements are comparing to signed. - { - case ICorDebugInfo::NO_MAPPING: - case ICorDebugInfo::PROLOG: - case ICorDebugInfo::EPILOG: - return IL_OFFSET(offsx); - - default: - return IL_OFFSET(offsx & ~IL_OFFSETX_BITS); - } -} - -//------------------------------------------------------------------------ -// jitIsStackEmpty: Does the IL offset have the stack empty bit set? -// Asserts if passed BAD_IL_OFFSET. -// -// Arguments: -// offsx - the IL_OFFSETX value to check -// -// Return Value: -// 'true' if the stack empty bit is set; 'false' otherwise. - -bool jitIsStackEmpty(IL_OFFSETX offsx) -{ - assert(offsx != BAD_IL_OFFSET); - - switch ((int)offsx) // Need the cast since offs is unsigned and the case statements are comparing to signed. - { - case ICorDebugInfo::NO_MAPPING: - case ICorDebugInfo::PROLOG: - case ICorDebugInfo::EPILOG: - return true; - - default: - return (offsx & IL_OFFSETX_STKBIT) == 0; - } -} - -//------------------------------------------------------------------------ -// jitIsCallInstruction: Does the IL offset have the call instruction bit set? -// Asserts if passed BAD_IL_OFFSET. -// -// Arguments: -// offsx - the IL_OFFSETX value to check -// -// Return Value: -// 'true' if the call instruction bit is set; 'false' otherwise. - -bool jitIsCallInstruction(IL_OFFSETX offsx) -{ - assert(offsx != BAD_IL_OFFSET); - - switch ((int)offsx) // Need the cast since offs is unsigned and the case statements are comparing to signed. - { - case ICorDebugInfo::NO_MAPPING: - case ICorDebugInfo::PROLOG: - case ICorDebugInfo::EPILOG: - return false; - - default: - return (offsx & IL_OFFSETX_CALLINSTRUCTIONBIT) != 0; - } -} - -/*****************************************************************************/ - -void CodeGen::genEnsureCodeEmitted(IL_OFFSETX offsx) +void CodeGen::genEnsureCodeEmitted(const DebugInfo& di) { if (!compiler->opts.compDbgCode) { return; } - if (offsx == BAD_IL_OFFSET) + if (!di.GetLocation().IsValid()) { return; } @@ -10717,7 +10597,7 @@ void CodeGen::genEnsureCodeEmitted(IL_OFFSETX offsx) return; } - if (compiler->genIPmappingLast->ipmdILoffsx != offsx) + if (compiler->genIPmappingLast->ipmdLoc != di.GetLocation()) { return; } @@ -10756,8 +10636,8 @@ void CodeGen::genIPmappingGen() return; } - Compiler::IPmappingDsc* tmpMapping; - Compiler::IPmappingDsc* prevMapping; + IPmappingDsc* tmpMapping; + IPmappingDsc* prevMapping; unsigned mappingCnt; UNATIVE_OFFSET lastNativeOfs; @@ -10769,12 +10649,10 @@ void CodeGen::genIPmappingGen() for (prevMapping = nullptr, tmpMapping = compiler->genIPmappingList; tmpMapping != nullptr; tmpMapping = tmpMapping->ipmdNext) { - IL_OFFSETX srcIP = tmpMapping->ipmdILoffsx; - // Managed RetVal - since new sequence points are emitted to identify IL calls, // make sure that those are not filtered and do not interfere with filtering of // other sequence points. - if (jitIsCallInstruction(srcIP)) + if (tmpMapping->ipmdKind == IPmappingDscKind::Normal && tmpMapping->ipmdLoc.IsCall()) { mappingCnt++; continue; @@ -10797,19 +10675,19 @@ void CodeGen::genIPmappingGen() */ PREFIX_ASSUME(prevMapping != nullptr); // We would exit before if this was true - if (prevMapping->ipmdILoffsx == (IL_OFFSETX)ICorDebugInfo::NO_MAPPING) + if (prevMapping->ipmdKind == IPmappingDscKind::NoMapping) { // If the previous entry was NO_MAPPING, ignore it prevMapping->ipmdNativeLoc.Init(); prevMapping = tmpMapping; } - else if (srcIP == (IL_OFFSETX)ICorDebugInfo::NO_MAPPING) + else if (tmpMapping->ipmdKind == IPmappingDscKind::NoMapping) { // If the current entry is NO_MAPPING, ignore it // Leave prevMapping unchanged as tmpMapping is no longer valid tmpMapping->ipmdNativeLoc.Init(); } - else if (srcIP == (IL_OFFSETX)ICorDebugInfo::EPILOG || srcIP == 0) + else if ((tmpMapping->ipmdKind == IPmappingDscKind::Epilog) || (tmpMapping->ipmdKind == IPmappingDscKind::Normal && tmpMapping->ipmdLoc.GetOffset() == 0)) { // counting for special cases: see below mappingCnt++; @@ -10856,18 +10734,17 @@ void CodeGen::genIPmappingGen() } UNATIVE_OFFSET nextNativeOfs = tmpMapping->ipmdNativeLoc.CodeOffset(GetEmitter()); - IL_OFFSETX srcIP = tmpMapping->ipmdILoffsx; - if (jitIsCallInstruction(srcIP)) + if (tmpMapping->ipmdKind == IPmappingDscKind::Normal && tmpMapping->ipmdLoc.IsCall()) { - compiler->eeSetLIinfo(mappingCnt++, nextNativeOfs, jitGetILoffs(srcIP), jitIsStackEmpty(srcIP), true); + compiler->eeSetLIinfo(mappingCnt++, nextNativeOfs, IPmappingDscKind::Normal, tmpMapping->ipmdLoc); } else if (nextNativeOfs != lastNativeOfs) { - compiler->eeSetLIinfo(mappingCnt++, nextNativeOfs, jitGetILoffsAny(srcIP), jitIsStackEmpty(srcIP), false); + compiler->eeSetLIinfo(mappingCnt++, nextNativeOfs, tmpMapping->ipmdKind, tmpMapping->ipmdLoc); lastNativeOfs = nextNativeOfs; } - else if (srcIP == (IL_OFFSETX)ICorDebugInfo::EPILOG || srcIP == 0) + else if (tmpMapping->ipmdKind == IPmappingDscKind::Epilog || (tmpMapping->ipmdKind == IPmappingDscKind::Normal && tmpMapping->ipmdLoc.GetOffset() == 0)) { // For the special case of an IL instruction with no body // followed by the epilog (say ret void immediately preceding @@ -10875,7 +10752,7 @@ void CodeGen::genIPmappingGen() // at the (empty) ret statement if the user tries to put a // breakpoint there, and then have the option of seeing the // epilog or not based on SetUnmappedStopMask for the stepper. - compiler->eeSetLIinfo(mappingCnt++, nextNativeOfs, jitGetILoffsAny(srcIP), jitIsStackEmpty(srcIP), false); + compiler->eeSetLIinfo(mappingCnt++, nextNativeOfs, tmpMapping->ipmdKind, tmpMapping->ipmdLoc); } } @@ -10899,9 +10776,9 @@ void CodeGen::genIPmappingGen() if ((block->bbRefs > 1) && (stmt != nullptr)) { bool found = false; - if (stmt->GetILOffsetX() != BAD_IL_OFFSET) + if (stmt->GetDebugInfo() != BAD_IL_OFFSET) { - IL_OFFSET ilOffs = jitGetILoffs(stmt->GetILOffsetX()); + IL_OFFSET ilOffs = jitGetILoffs(stmt->GetDebugInfo()); for (unsigned i = 0; i < eeBoundariesCount; ++i) { if (eeBoundaries[i].ilOffset == ilOffs) diff --git a/src/coreclr/jit/codegenlinear.cpp b/src/coreclr/jit/codegenlinear.cpp index e0f48954c65cd..c95fba37af4f0 100644 --- a/src/coreclr/jit/codegenlinear.cpp +++ b/src/coreclr/jit/codegenlinear.cpp @@ -376,7 +376,7 @@ void CodeGen::genCodeForBBlist() !compiler->fgBBisScratch(block)) // If the block is the distinguished first scratch block, then no need to // emit a NO_MAPPING entry, immediately after the prolog. { - genIPmappingAdd((IL_OFFSETX)ICorDebugInfo::NO_MAPPING, true); + genIPmappingAdd(IPmappingDscKind::NoMapping, DebugInfo(), true); } bool firstMapping = true; @@ -425,16 +425,16 @@ void CodeGen::genCodeForBBlist() } #endif // DEBUG - IL_OFFSETX currentILOffset = BAD_IL_OFFSET; + DebugInfo currentDI; for (GenTree* node : LIR::AsRange(block)) { // Do we have a new IL offset? if (node->OperGet() == GT_IL_OFFSET) { GenTreeILOffset* ilOffset = node->AsILOffset(); - genEnsureCodeEmitted(currentILOffset); - currentILOffset = ilOffset->gtStmtILoffsx; - genIPmappingAdd(currentILOffset, firstMapping); + genEnsureCodeEmitted(currentDI); + currentDI = ilOffset->gtStmtDI; + genIPmappingAdd(IPmappingDscKind::Normal, currentDI, firstMapping); firstMapping = false; #ifdef DEBUG assert(ilOffset->gtStmtLastILoffs <= compiler->info.compILCodeSize || @@ -529,7 +529,7 @@ void CodeGen::genCodeForBBlist() // // This can lead to problems when debugging the generated code. To prevent these issues, make sure // we've generated code for the last IL offset we saw in the block. - genEnsureCodeEmitted(currentILOffset); + genEnsureCodeEmitted(currentDI); /* Is this the last block, and are there any open scopes left ? */ @@ -2302,7 +2302,7 @@ void CodeGen::genEmitCall(int callType, X86_ARG(int argSize), emitAttr retSize MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize), - IL_OFFSETX ilOffset, + const DebugInfo& di, regNumber base, bool isJump) { @@ -2324,7 +2324,7 @@ void CodeGen::genEmitCall(int callType, gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur, - ilOffset, base, REG_NA, 0, 0, isJump); + di, base, REG_NA, 0, 0, isJump); } // clang-format on @@ -2340,7 +2340,7 @@ void CodeGen::genEmitCallIndir(int callType, X86_ARG(int argSize), emitAttr retSize MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize), - IL_OFFSETX ilOffset, + const DebugInfo& di, bool isJump) { #if !defined(TARGET_X86) @@ -2365,7 +2365,7 @@ void CodeGen::genEmitCallIndir(int callType, gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur, - ilOffset, + di, iReg, xReg, indir->Scale(), diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp index 128de7c9fcd5b..5c8e0bdd5c2ae 100644 --- a/src/coreclr/jit/codegenxarch.cpp +++ b/src/coreclr/jit/codegenxarch.cpp @@ -5506,11 +5506,11 @@ void CodeGen::genCallInstruction(GenTreeCall* call X86_ARG(target_ssize_t stackA // We don't want tail call helper calls that were converted from normal calls to get a record, // so we skip this hash table lookup logic in that case. - IL_OFFSETX ilOffset = BAD_IL_OFFSET; + DebugInfo di; - if (compiler->opts.compDbgInfo && compiler->genCallSite2ILOffsetMap != nullptr && !call->IsTailCall()) + if (compiler->opts.compDbgInfo && compiler->genCallSite2DebugInfoMap != nullptr && !call->IsTailCall()) { - (void)compiler->genCallSite2ILOffsetMap->Lookup(call, &ilOffset); + (void)compiler->genCallSite2DebugInfoMap->Lookup(call, &di); } CORINFO_SIG_INFO* sigInfo = nullptr; @@ -5562,7 +5562,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call X86_ARG(target_ssize_t stackA gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur, - ilOffset, REG_VIRTUAL_STUB_TARGET, REG_NA, 1, 0); + di, REG_VIRTUAL_STUB_TARGET, REG_NA, 1, 0); // clang-format on } else @@ -5583,7 +5583,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call X86_ARG(target_ssize_t stackA X86_ARG(argSizeForEmitter), retSize MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(secondRetSize), - ilOffset, + di, REG_NA, call->IsFastTailCall()); // clang-format on @@ -5605,7 +5605,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call X86_ARG(target_ssize_t stackA X86_ARG(argSizeForEmitter), retSize MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(secondRetSize), - ilOffset, + di, call->IsFastTailCall()); // clang-format on } @@ -5631,7 +5631,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call X86_ARG(target_ssize_t stackA X86_ARG(argSizeForEmitter), retSize MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(secondRetSize), - ilOffset, + di, target->GetRegNum(), call->IsFastTailCall()); // clang-format on @@ -5661,7 +5661,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call X86_ARG(target_ssize_t stackA gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur, - ilOffset, indirCellReg, REG_NA, 0, 0, + di, indirCellReg, REG_NA, 0, 0, call->IsFastTailCall()); // clang-format on } @@ -5678,7 +5678,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call X86_ARG(target_ssize_t stackA X86_ARG(argSizeForEmitter), retSize MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(secondRetSize), - ilOffset, + di, REG_NA, call->IsFastTailCall()); // clang-format on @@ -5718,7 +5718,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call X86_ARG(target_ssize_t stackA X86_ARG(argSizeForEmitter), retSize MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(secondRetSize), - ilOffset, + di, REG_NA, call->IsFastTailCall()); // clang-format on @@ -8408,7 +8408,7 @@ void CodeGen::genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize, gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur, - BAD_IL_OFFSET, // IL offset + DebugInfo(), callTarget, // ireg REG_NA, 0, 0, // xreg, xmul, disp false // isJump diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index a26a003a455fb..7fa9179f53d2c 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -174,128 +174,6 @@ void Compiler::JitLogEE(unsigned level, const char* fmt, ...) va_end(args); } -void Compiler::compDspSrcLinesByLineNum(unsigned line, bool seek) -{ - if (!jitSrcFilePtr) - { - return; - } - - if (jitCurSrcLine == line) - { - return; - } - - if (jitCurSrcLine > line) - { - if (!seek) - { - return; - } - - if (fseek(jitSrcFilePtr, 0, SEEK_SET) != 0) - { - printf("Compiler::compDspSrcLinesByLineNum: fseek returned an error.\n"); - } - jitCurSrcLine = 0; - } - - if (!seek) - { - printf(";\n"); - } - - do - { - char temp[128]; - size_t llen; - - if (!fgets(temp, sizeof(temp), jitSrcFilePtr)) - { - return; - } - - if (seek) - { - continue; - } - - llen = strlen(temp); - if (llen && temp[llen - 1] == '\n') - { - temp[llen - 1] = 0; - } - - printf("; %s\n", temp); - } while (++jitCurSrcLine < line); - - if (!seek) - { - printf(";\n"); - } -} - -/*****************************************************************************/ - -void Compiler::compDspSrcLinesByNativeIP(UNATIVE_OFFSET curIP) -{ - static IPmappingDsc* nextMappingDsc; - static unsigned lastLine; - - if (!opts.dspLines) - { - return; - } - - if (curIP == 0) - { - if (genIPmappingList) - { - nextMappingDsc = genIPmappingList; - lastLine = jitGetILoffs(nextMappingDsc->ipmdILoffsx); - - unsigned firstLine = jitGetILoffs(nextMappingDsc->ipmdILoffsx); - - unsigned earlierLine = (firstLine < 5) ? 0 : firstLine - 5; - - compDspSrcLinesByLineNum(earlierLine, true); // display previous 5 lines - compDspSrcLinesByLineNum(firstLine, false); - } - else - { - nextMappingDsc = nullptr; - } - - return; - } - - if (nextMappingDsc) - { - UNATIVE_OFFSET offset = nextMappingDsc->ipmdNativeLoc.CodeOffset(GetEmitter()); - - if (offset <= curIP) - { - IL_OFFSET nextOffs = jitGetILoffs(nextMappingDsc->ipmdILoffsx); - - if (lastLine < nextOffs) - { - compDspSrcLinesByLineNum(nextOffs); - } - else - { - // This offset corresponds to a previous line. Rewind to that line - - compDspSrcLinesByLineNum(nextOffs - 2, true); - compDspSrcLinesByLineNum(nextOffs); - } - - lastLine = nextOffs; - nextMappingDsc = nextMappingDsc->ipmdNext; - } - } -} - -/*****************************************************************************/ #endif // DEBUG /*****************************************************************************/ @@ -2744,6 +2622,7 @@ void Compiler::compInitOptions(JitFlags* jitFlags) verboseSsa = verbose && shouldUseVerboseSsa(); asciiTrees = shouldDumpASCIITrees(); opts.dspDiffable = compIsForInlining() ? impInlineInfo->InlinerCompiler->opts.dspDiffable : false; + #endif opts.altJit = false; diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index befb4a6f60b7b..55183e5a24b13 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -2515,6 +2515,26 @@ inline LoopFlags& operator&=(LoopFlags& a, LoopFlags b) return a = (LoopFlags)((unsigned short)a & (unsigned short)b); } +// The following holds information about instr offsets in terms of generated code. + +enum class IPmappingDscKind +{ + Prolog, // The mapping represents the start of a prolog. + Epilog, // The mapping represents the start of an epilog. + NoMapping, // This does not map to any IL offset. + Normal, // The mapping maps to an IL offset. +}; + +struct IPmappingDsc +{ + IPmappingDsc* ipmdNext; // next line# record + emitLocation ipmdNativeLoc; // the emitter location of the native code corresponding to the IL offset + IPmappingDscKind ipmdKind; // The kind of mapping + ILLocation ipmdLoc; // The location for normal mappings + bool ipmdIsLabel; // Can this code be a branch label? +}; + + /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX @@ -2987,7 +3007,8 @@ class Compiler */ // Functions to create nodes - Statement* gtNewStmt(GenTree* expr = nullptr, IL_OFFSETX offset = BAD_IL_OFFSET); + Statement* gtNewStmt(GenTree* expr = nullptr); + Statement* gtNewStmt(GenTree* expr, const DebugInfo& di); // For unary opers. GenTree* gtNewOperNode(genTreeOps oper, var_types type, GenTree* op1, bool doSimplifications = TRUE); @@ -3071,12 +3092,12 @@ class Compiler CORINFO_METHOD_HANDLE handle, var_types type, GenTreeCall::Use* args, - IL_OFFSETX ilOffset = BAD_IL_OFFSET); + const DebugInfo& di = DebugInfo()); GenTreeCall* gtNewIndCallNode(GenTree* addr, var_types type, GenTreeCall::Use* args, - IL_OFFSETX ilOffset = BAD_IL_OFFSET); + const DebugInfo& di = DebugInfo()); GenTreeCall* gtNewHelperCallNode(unsigned helper, var_types type, GenTreeCall::Use* args = nullptr); @@ -3084,8 +3105,8 @@ class Compiler GenTree* ctxTree, void* compileTimeHandle); - GenTreeLclVar* gtNewLclvNode(unsigned lnum, var_types type DEBUGARG(IL_OFFSETX ILoffs = BAD_IL_OFFSET)); - GenTreeLclVar* gtNewLclLNode(unsigned lnum, var_types type DEBUGARG(IL_OFFSETX ILoffs = BAD_IL_OFFSET)); + GenTreeLclVar* gtNewLclvNode(unsigned lnum, var_types type DEBUGARG(IL_OFFSET offs = BAD_IL_OFFSET)); + GenTreeLclVar* gtNewLclLNode(unsigned lnum, var_types type DEBUGARG(IL_OFFSET offs = BAD_IL_OFFSET)); GenTreeLclVar* gtNewLclVarAddrNode(unsigned lclNum, var_types type = TYP_I_IMPL); GenTreeLclFld* gtNewLclFldAddrNode(unsigned lclNum, @@ -3331,7 +3352,7 @@ class Compiler GenTree* gtNewTempAssign(unsigned tmp, GenTree* val, Statement** pAfterStmt = nullptr, - IL_OFFSETX ilOffset = BAD_IL_OFFSET, + const DebugInfo& di = DebugInfo(), BasicBlock* block = nullptr); GenTree* gtNewRefCOMfield(GenTree* objPtr, @@ -3387,7 +3408,7 @@ class Compiler Statement* gtCloneStmt(Statement* stmt) { GenTree* exprClone = gtCloneExpr(stmt->GetRootNode()); - return gtNewStmt(exprClone, stmt->GetILOffsetX()); + return gtNewStmt(exprClone, stmt->GetDebugInfo()); } // Internal helper for cloning a call @@ -4251,7 +4272,7 @@ class Compiler CORINFO_CONTEXT_HANDLE* exactContextHandle, bool isLateDevirtualization, bool isExplicitTailCall, - IL_OFFSETX ilOffset = BAD_IL_OFFSET); + IL_OFFSET ilOffset = BAD_IL_OFFSET); //========================================================================= // PROTECTED @@ -4298,7 +4319,7 @@ class Compiler bool impCanPInvokeInlineCallSite(BasicBlock* block); void impCheckForPInvokeCall( GenTreeCall* call, CORINFO_METHOD_HANDLE methHnd, CORINFO_SIG_INFO* sig, unsigned mflags, BasicBlock* block); - GenTreeCall* impImportIndirectCall(CORINFO_SIG_INFO* sig, IL_OFFSETX ilOffset = BAD_IL_OFFSET); + GenTreeCall* impImportIndirectCall(CORINFO_SIG_INFO* sig, const DebugInfo& di = DebugInfo()); void impPopArgsForUnmanagedCall(GenTree* call, CORINFO_SIG_INFO* sig); void impInsertHelperCall(CORINFO_HELPER_DESC* helperCall); @@ -4462,20 +4483,20 @@ class Compiler void impAppendStmt(Statement* stmt, unsigned chkLevel); void impAppendStmt(Statement* stmt); void impInsertStmtBefore(Statement* stmt, Statement* stmtBefore); - Statement* impAppendTree(GenTree* tree, unsigned chkLevel, IL_OFFSETX offset); - void impInsertTreeBefore(GenTree* tree, IL_OFFSETX offset, Statement* stmtBefore); + Statement* impAppendTree(GenTree* tree, unsigned chkLevel, const DebugInfo& di); + void impInsertTreeBefore(GenTree* tree, const DebugInfo& di, Statement* stmtBefore); void impAssignTempGen(unsigned tmp, GenTree* val, unsigned curLevel, Statement** pAfterStmt = nullptr, - IL_OFFSETX ilOffset = BAD_IL_OFFSET, + const DebugInfo& di = DebugInfo(), BasicBlock* block = nullptr); void impAssignTempGen(unsigned tmpNum, GenTree* val, CORINFO_CLASS_HANDLE structHnd, unsigned curLevel, Statement** pAfterStmt = nullptr, - IL_OFFSETX ilOffset = BAD_IL_OFFSET, + const DebugInfo& di = DebugInfo(), BasicBlock* block = nullptr); Statement* impExtractLastStmt(); @@ -4489,14 +4510,14 @@ class Compiler CORINFO_CLASS_HANDLE structHnd, unsigned curLevel, Statement** pAfterStmt = nullptr, - IL_OFFSETX ilOffset = BAD_IL_OFFSET, + const DebugInfo& di = DebugInfo(), BasicBlock* block = nullptr); GenTree* impAssignStructPtr(GenTree* dest, GenTree* src, CORINFO_CLASS_HANDLE structHnd, unsigned curLevel, Statement** pAfterStmt = nullptr, - IL_OFFSETX ilOffset = BAD_IL_OFFSET, + const DebugInfo& di = DebugInfo(), BasicBlock* block = nullptr); GenTree* impGetStructAddr(GenTree* structVal, CORINFO_CLASS_HANDLE structHnd, unsigned curLevel, bool willDeref); @@ -4573,15 +4594,17 @@ class Compiler void impNoteLastILoffs(); #endif - /* IL offset of the stmt currently being imported. It gets set to - BAD_IL_OFFSET after it has been set in the appended trees. Then it gets - updated at IL offsets for which we have to report mapping info. - It also includes flag bits, so use jitGetILoffs() - to get the actual IL offset value. + /* Debug info of the stmt currently being imported. It gets set to empty information + (DebugInfo()) after it has been set in the appended trees. Then it gets updated at + IL instructions for which we have to report mapping info. */ + DebugInfo impCurStmtDI; - IL_OFFSETX impCurStmtOffs; void impCurStmtOffsSet(IL_OFFSET offs); + /* + * Get current debug info with stack info and call instruction info incoporated + */ + DebugInfo impCurDebugInfo(IL_OFFSET offs, bool callInstruction = false); void impNoteBranchOffs(); @@ -4603,11 +4626,6 @@ class Compiler GenTreeCall::Use* impPopReverseCallArgs(unsigned count, CORINFO_SIG_INFO* sig, unsigned skipReverseCount = 0); - /* - * Get current IL offset with stack-empty info incoporated - */ - IL_OFFSETX impCurILOffset(IL_OFFSET offs, bool callInstruction = false); - //---------------- Spilling the importer stack ---------------------------- // The maximum number of bytes of IL processed without clean stack state. @@ -5210,10 +5228,10 @@ class Compiler BasicBlock* fgSplitBlockAfterNode(BasicBlock* curr, GenTree* node); // for LIR BasicBlock* fgSplitEdge(BasicBlock* curr, BasicBlock* succ); - Statement* fgNewStmtFromTree(GenTree* tree, BasicBlock* block, IL_OFFSETX offs); + Statement* fgNewStmtFromTree(GenTree* tree, BasicBlock* block, const DebugInfo& di); Statement* fgNewStmtFromTree(GenTree* tree); Statement* fgNewStmtFromTree(GenTree* tree, BasicBlock* block); - Statement* fgNewStmtFromTree(GenTree* tree, IL_OFFSETX offs); + Statement* fgNewStmtFromTree(GenTree* tree, const DebugInfo& di); GenTree* fgGetTopLevelQmark(GenTree* expr, GenTree** ppDst = nullptr); void fgExpandQmarkForCastInstOf(BasicBlock* block, Statement* stmt); @@ -6335,7 +6353,7 @@ class Compiler fgArgTabEntry* argTabEntry, unsigned lclParamNum, BasicBlock* block, - IL_OFFSETX callILOffset, + const DebugInfo& callDI, Statement* tmpAssignmentInsertionPoint, Statement* paramAssignmentInsertionPoint); GenTree* fgMorphCall(GenTreeCall* call); @@ -7363,7 +7381,7 @@ class Compiler } void considerGuardedDevirtualization(GenTreeCall* call, - IL_OFFSETX iloffset, + IL_OFFSET ilOffset, bool isInterface, CORINFO_METHOD_HANDLE baseMethod, CORINFO_CLASS_HANDLE baseClass, @@ -8157,19 +8175,15 @@ class Compiler unsigned eeBoundariesCount; - struct boundariesDsc - { - UNATIVE_OFFSET nativeIP; - IL_OFFSET ilOffset; - unsigned sourceReason; - } * eeBoundaries; // Boundaries to report to EE + ICorDebugInfo::OffsetMapping* eeBoundaries; // Boundaries to report to the EE void eeSetLIcount(unsigned count); - void eeSetLIinfo(unsigned which, UNATIVE_OFFSET offs, unsigned srcIP, bool stkEmpty, bool callInstruction); + void eeSetLIinfo(unsigned which, UNATIVE_OFFSET offs, IPmappingDscKind kind, const ILLocation& loc); void eeSetLIdone(); #ifdef DEBUG static void eeDispILOffs(IL_OFFSET offs); - static void eeDispLineInfo(const boundariesDsc* line); + static void eeDispSourceMappingOffs(uint32_t offs); + static void eeDispLineInfo(const ICorDebugInfo::OffsetMapping* line); void eeDispLineInfos(); #endif // DEBUG @@ -8285,34 +8299,24 @@ class Compiler public: CodeGenInterface* codeGen; - // The following holds information about instr offsets in terms of generated code. - - struct IPmappingDsc - { - IPmappingDsc* ipmdNext; // next line# record - emitLocation ipmdNativeLoc; // the emitter location of the native code corresponding to the IL offset - IL_OFFSETX ipmdILoffsx; // the instr offset - bool ipmdIsLabel; // Can this code be a branch label? - }; - // Record the instr offset mapping to the generated code IPmappingDsc* genIPmappingList; IPmappingDsc* genIPmappingLast; // Managed RetVal - A side hash table meant to record the mapping from a - // GT_CALL node to its IL offset. This info is used to emit sequence points + // GT_CALL node to its debug info. This info is used to emit sequence points // that can be used by debugger to determine the native offset at which the // managed RetVal will be available. // - // In fact we can store IL offset in a GT_CALL node. This was ruled out in - // favor of a side table for two reasons: 1) We need IL offset for only those + // In fact we can store debug info in a GT_CALL node. This was ruled out in + // favor of a side table for two reasons: 1) We need debug info for only those // GT_CALL nodes (created during importation) that correspond to an IL call and // whose return type is other than TYP_VOID. 2) GT_CALL node is a frequently used // structure and IL offset is needed only when generating debuggable code. Therefore // it is desirable to avoid memory size penalty in retail scenarios. - typedef JitHashTable, IL_OFFSETX> CallSiteILOffsetTable; - CallSiteILOffsetTable* genCallSite2ILOffsetMap; + typedef JitHashTable, DebugInfo> CallSiteDebugInfoTable; + CallSiteDebugInfoTable* genCallSite2DebugInfoMap; unsigned genReturnLocal; // Local number for the return value when applicable. BasicBlock* genReturnBB; // jumped to when not optimizing for speed. @@ -10453,6 +10457,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX VarName compVarName(regNumber reg, bool isFloatReg = false); const char* compRegVarName(regNumber reg, bool displayVar = false, bool isFloatReg = false); const char* compRegNameForSize(regNumber reg, size_t size); + const char* compFPregVarName(unsigned fpReg, bool displayVar = false); void compDspSrcLinesByNativeIP(UNATIVE_OFFSET curIP); void compDspSrcLinesByLineNum(unsigned line, bool seek = false); #endif // DEBUG diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index 0c446f2020d0b..7fec5719abc36 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -822,9 +822,16 @@ inline GenTree::GenTree(genTreeOps oper, var_types type DEBUGARG(bool largeNode) /*****************************************************************************/ -inline Statement* Compiler::gtNewStmt(GenTree* expr, IL_OFFSETX offset) +inline Statement* Compiler::gtNewStmt(GenTree* expr) { - Statement* stmt = new (this->getAllocator(CMK_ASTNode)) Statement(expr, offset DEBUGARG(compStatementID++)); + Statement* stmt = new (this->getAllocator(CMK_ASTNode)) Statement(expr DEBUGARG(compStatementID++)); + return stmt; +} + +inline Statement* Compiler::gtNewStmt(GenTree* expr, const DebugInfo& di) +{ + Statement* stmt = gtNewStmt(expr); + stmt->SetDebugInfo(di); return stmt; } diff --git a/src/coreclr/jit/ee_il_dll.cpp b/src/coreclr/jit/ee_il_dll.cpp index 07ad7dfc90b9c..0a7f226a1cc12 100644 --- a/src/coreclr/jit/ee_il_dll.cpp +++ b/src/coreclr/jit/ee_il_dll.cpp @@ -960,7 +960,7 @@ void Compiler::eeSetLIcount(unsigned count) eeBoundariesCount = count; if (eeBoundariesCount) { - eeBoundaries = (boundariesDsc*)info.compCompHnd->allocateArray(eeBoundariesCount * sizeof(eeBoundaries[0])); + eeBoundaries = (ICorDebugInfo::OffsetMapping*)info.compCompHnd->allocateArray(eeBoundariesCount * sizeof(eeBoundaries[0])); } else { @@ -969,18 +969,39 @@ void Compiler::eeSetLIcount(unsigned count) } void Compiler::eeSetLIinfo( - unsigned which, UNATIVE_OFFSET nativeOffset, IL_OFFSET ilOffset, bool stkEmpty, bool callInstruction) + unsigned which, UNATIVE_OFFSET nativeOffset, IPmappingDscKind kind, const ILLocation& loc) { assert(opts.compDbgInfo); - assert(eeBoundariesCount > 0); + assert(eeBoundariesCount > 0 && eeBoundaries != nullptr); assert(which < eeBoundariesCount); - if (eeBoundaries != nullptr) - { - eeBoundaries[which].nativeIP = nativeOffset; - eeBoundaries[which].ilOffset = ilOffset; - eeBoundaries[which].sourceReason = stkEmpty ? ICorDebugInfo::STACK_EMPTY : 0; - eeBoundaries[which].sourceReason |= callInstruction ? ICorDebugInfo::CALL_INSTRUCTION : 0; + eeBoundaries[which].nativeOffset = nativeOffset; + eeBoundaries[which].source = (ICorDebugInfo::SourceTypes)0; + + switch (kind) + { + int source; + + case IPmappingDscKind::Normal: + eeBoundaries[which].ilOffset = loc.GetOffset(); + source = loc.IsStackEmpty() ? ICorDebugInfo::STACK_EMPTY : 0; + source |= loc.IsCall() ? ICorDebugInfo::CALL_INSTRUCTION : 0; + eeBoundaries[which].source = (ICorDebugInfo::SourceTypes)source; + break; + case IPmappingDscKind::Prolog: + eeBoundaries[which].ilOffset = ICorDebugInfo::PROLOG; + eeBoundaries[which].source = ICorDebugInfo::STACK_EMPTY; + break; + case IPmappingDscKind::Epilog: + eeBoundaries[which].ilOffset = ICorDebugInfo::EPILOG; + eeBoundaries[which].source = ICorDebugInfo::STACK_EMPTY; + break; + case IPmappingDscKind::NoMapping: + eeBoundaries[which].ilOffset = ICorDebugInfo::NO_MAPPING; + eeBoundaries[which].source = ICorDebugInfo::STACK_EMPTY; + break; + default: + unreached(); } } @@ -1005,8 +1026,13 @@ void Compiler::eeSetLIdone() #if defined(DEBUG) -/* static */ void Compiler::eeDispILOffs(IL_OFFSET offs) +{ + printf("0x%04X", offs); +} + +/* static */ +void Compiler::eeDispSourceMappingOffs(uint32_t offs) { const char* specialOffs[] = {"EPILOG", "PROLOG", "NO_MAP"}; @@ -1022,33 +1048,34 @@ void Compiler::eeDispILOffs(IL_OFFSET offs) printf("%s", specialOffs[specialOffsNum]); break; default: - printf("0x%04X", offs); + eeDispILOffs(offs); + break; } } /* static */ -void Compiler::eeDispLineInfo(const boundariesDsc* line) +void Compiler::eeDispLineInfo(const ICorDebugInfo::OffsetMapping* line) { printf("IL offs "); - eeDispILOffs(line->ilOffset); + eeDispSourceMappingOffs(line->ilOffset); - printf(" : 0x%08X", line->nativeIP); - if (line->sourceReason != 0) + printf(" : 0x%08X", line->nativeOffset); + if (line->source != 0) { // It seems like it should probably never be zero since ICorDebugInfo::SOURCE_TYPE_INVALID is zero. // However, the JIT has always generated this and printed "stack non-empty". printf(" ( "); - if ((line->sourceReason & ICorDebugInfo::STACK_EMPTY) != 0) + if ((line->source & ICorDebugInfo::STACK_EMPTY) != 0) { printf("STACK_EMPTY "); } - if ((line->sourceReason & ICorDebugInfo::CALL_INSTRUCTION) != 0) + if ((line->source & ICorDebugInfo::CALL_INSTRUCTION) != 0) { printf("CALL_INSTRUCTION "); } - if ((line->sourceReason & ICorDebugInfo::CALL_SITE) != 0) + if ((line->source & ICorDebugInfo::CALL_SITE) != 0) { printf("CALL_SITE "); } @@ -1057,7 +1084,7 @@ void Compiler::eeDispLineInfo(const boundariesDsc* line) printf("\n"); // We don't expect to see any other bits. - assert((line->sourceReason & ~(ICorDebugInfo::STACK_EMPTY | ICorDebugInfo::CALL_INSTRUCTION)) == 0); + assert((line->source & ~(ICorDebugInfo::STACK_EMPTY | ICorDebugInfo::CALL_INSTRUCTION)) == 0); } void Compiler::eeDispLineInfos() diff --git a/src/coreclr/jit/emit.cpp b/src/coreclr/jit/emit.cpp index 9975cd5041945..54d57dd22b2f7 100644 --- a/src/coreclr/jit/emit.cpp +++ b/src/coreclr/jit/emit.cpp @@ -1815,11 +1815,11 @@ void emitter::emitCreatePlaceholderIG(insGroupPlaceholderType igType, { if (igType == IGPT_FUNCLET_PROLOG) { - codeGen->genIPmappingAdd((IL_OFFSETX)ICorDebugInfo::PROLOG, true); + codeGen->genIPmappingAdd(IPmappingDscKind::Prolog, DebugInfo(), true); } else if (igType == IGPT_FUNCLET_EPILOG) { - codeGen->genIPmappingAdd((IL_OFFSETX)ICorDebugInfo::EPILOG, true); + codeGen->genIPmappingAdd(IPmappingDscKind::Epilog, DebugInfo(), true); } } #endif // FEATURE_EH_FUNCLETS diff --git a/src/coreclr/jit/emitarm.cpp b/src/coreclr/jit/emitarm.cpp index 7ed8421074192..fff80213d9c96 100644 --- a/src/coreclr/jit/emitarm.cpp +++ b/src/coreclr/jit/emitarm.cpp @@ -4678,7 +4678,7 @@ void emitter::emitIns_Call(EmitCallType callType, VARSET_VALARG_TP ptrVars, regMaskTP gcrefRegs, regMaskTP byrefRegs, - IL_OFFSETX ilOffset /* = BAD_IL_OFFSET */, + const DebugInfo& di /* = DebugInfo() */, regNumber ireg /* = REG_NA */, regNumber xreg /* = REG_NA */, unsigned xmul /* = 0 */, @@ -4719,9 +4719,9 @@ void emitter::emitIns_Call(EmitCallType callType, #endif /* Managed RetVal: emit sequence point for the call */ - if (emitComp->opts.compDbgInfo && ilOffset != BAD_IL_OFFSET) + if (emitComp->opts.compDbgInfo && di.GetLocation().IsValid()) { - codeGen->genIPmappingAdd(ilOffset, false); + codeGen->genIPmappingAdd(IPmappingDscKind::Normal, di, false); } /* diff --git a/src/coreclr/jit/emitarm.h b/src/coreclr/jit/emitarm.h index 744b8676c45b2..723bdcbd8c4bd 100644 --- a/src/coreclr/jit/emitarm.h +++ b/src/coreclr/jit/emitarm.h @@ -328,7 +328,7 @@ void emitIns_Call(EmitCallType callType, VARSET_VALARG_TP ptrVars, regMaskTP gcrefRegs, regMaskTP byrefRegs, - IL_OFFSETX ilOffset = BAD_IL_OFFSET, + const DebugInfo& di = DebugInfo(), regNumber ireg = REG_NA, regNumber xreg = REG_NA, unsigned xmul = 0, diff --git a/src/coreclr/jit/emitarm64.cpp b/src/coreclr/jit/emitarm64.cpp index 414d1eaffa0d1..6dc8a1fd533a5 100644 --- a/src/coreclr/jit/emitarm64.cpp +++ b/src/coreclr/jit/emitarm64.cpp @@ -8520,7 +8520,7 @@ void emitter::emitIns_Call(EmitCallType callType, VARSET_VALARG_TP ptrVars, regMaskTP gcrefRegs, regMaskTP byrefRegs, - IL_OFFSETX ilOffset /* = BAD_IL_OFFSET */, + const DebugInfo& di /* = DebugInfo() */, regNumber ireg /* = REG_NA */, regNumber xreg /* = REG_NA */, unsigned xmul /* = 0 */, @@ -8561,9 +8561,9 @@ void emitter::emitIns_Call(EmitCallType callType, #endif /* Managed RetVal: emit sequence point for the call */ - if (emitComp->opts.compDbgInfo && ilOffset != BAD_IL_OFFSET) + if (emitComp->opts.compDbgInfo && di.GetLocation().IsValid()) { - codeGen->genIPmappingAdd(ilOffset, false); + codeGen->genIPmappingAdd(IPmappingDscKind::Normal, di, false); } /* diff --git a/src/coreclr/jit/emitarm64.h b/src/coreclr/jit/emitarm64.h index 77a71a9005157..ab06c5e25c700 100644 --- a/src/coreclr/jit/emitarm64.h +++ b/src/coreclr/jit/emitarm64.h @@ -858,7 +858,7 @@ void emitIns_Call(EmitCallType callType, VARSET_VALARG_TP ptrVars, regMaskTP gcrefRegs, regMaskTP byrefRegs, - IL_OFFSETX ilOffset, + const DebugInfo& di, regNumber ireg, regNumber xreg, unsigned xmul, diff --git a/src/coreclr/jit/emitxarch.cpp b/src/coreclr/jit/emitxarch.cpp index e07984760e018..35196032eccb8 100644 --- a/src/coreclr/jit/emitxarch.cpp +++ b/src/coreclr/jit/emitxarch.cpp @@ -7536,7 +7536,7 @@ void emitter::emitIns_Call(EmitCallType callType, VARSET_VALARG_TP ptrVars, regMaskTP gcrefRegs, regMaskTP byrefRegs, - IL_OFFSETX ilOffset, + const DebugInfo& di, regNumber ireg, regNumber xreg, unsigned xmul, @@ -7577,9 +7577,9 @@ void emitter::emitIns_Call(EmitCallType callType, #endif /* Managed RetVal: emit sequence point for the call */ - if (emitComp->opts.compDbgInfo && ilOffset != BAD_IL_OFFSET) + if (emitComp->opts.compDbgInfo && di.GetLocation().IsValid()) { - codeGen->genIPmappingAdd(ilOffset, false); + codeGen->genIPmappingAdd(IPmappingDscKind::Normal, di, false); } /* diff --git a/src/coreclr/jit/emitxarch.h b/src/coreclr/jit/emitxarch.h index 8217d088417cc..39458eade16cf 100644 --- a/src/coreclr/jit/emitxarch.h +++ b/src/coreclr/jit/emitxarch.h @@ -539,7 +539,7 @@ void emitIns_Call(EmitCallType callType, VARSET_VALARG_TP ptrVars, regMaskTP gcrefRegs, regMaskTP byrefRegs, - IL_OFFSETX ilOffset = BAD_IL_OFFSET, + const DebugInfo& di = DebugInfo(), regNumber ireg = REG_NA, regNumber xreg = REG_NA, unsigned xmul = 0, diff --git a/src/coreclr/jit/fgbasic.cpp b/src/coreclr/jit/fgbasic.cpp index da643cfd6d4db..7bd812b46a6c9 100644 --- a/src/coreclr/jit/fgbasic.cpp +++ b/src/coreclr/jit/fgbasic.cpp @@ -4055,9 +4055,9 @@ IL_OFFSET Compiler::fgFindBlockILOffset(BasicBlock* block) for (Statement* const stmt : block->Statements()) { - if (stmt->GetILOffsetX() != BAD_IL_OFFSET) + if (stmt->GetDebugInfo().GetLocation().IsValid()) { - return jitGetILoffs(stmt->GetILOffsetX()); + return stmt->GetDebugInfo().GetLocation().GetOffset(); } } @@ -4228,9 +4228,9 @@ BasicBlock* Compiler::fgSplitBlockAfterNode(BasicBlock* curr, GenTree* node) if ((*riter)->gtOper == GT_IL_OFFSET) { GenTreeILOffset* ilOffset = (*riter)->AsILOffset(); - if (ilOffset->gtStmtILoffsx != BAD_IL_OFFSET) + if (ilOffset->gtStmtDI.GetLocation().IsValid()) { - splitPointILOffset = jitGetILoffs(ilOffset->gtStmtILoffsx); + splitPointILOffset = ilOffset->gtStmtDI.GetLocation().GetOffset(); break; } } diff --git a/src/coreclr/jit/fginline.cpp b/src/coreclr/jit/fginline.cpp index 9b2e4080a62a2..8214a2251ef8a 100644 --- a/src/coreclr/jit/fginline.cpp +++ b/src/coreclr/jit/fginline.cpp @@ -1281,9 +1281,9 @@ void Compiler::fgInsertInlineeBlocks(InlineInfo* pInlineInfo) block->copyEHRegion(iciBlock); block->bbFlags |= iciBlock->bbFlags & BBF_BACKWARD_JUMP; - if (iciStmt->GetILOffsetX() != BAD_IL_OFFSET) + if (iciStmt->GetDebugInfo().GetLocation().IsValid()) { - block->bbCodeOffs = jitGetILoffs(iciStmt->GetILOffsetX()); + block->bbCodeOffs = iciStmt->GetDebugInfo().GetLocation().GetOffset(); block->bbCodeOffsEnd = block->bbCodeOffs + 1; // TODO: is code size of 1 some magic number for inlining? } else @@ -1471,7 +1471,7 @@ Statement* Compiler::fgInlinePrependStatements(InlineInfo* inlineInfo) { BasicBlock* block = inlineInfo->iciBlock; Statement* callStmt = inlineInfo->iciStmt; - IL_OFFSETX callILOffset = callStmt->GetILOffsetX(); + const DebugInfo& callDI = DebugInfo(nullptr, callStmt->GetDebugInfo().GetLocation()); Statement* postStmt = callStmt->GetNextStmt(); Statement* afterStmt = callStmt; // afterStmt is the place where the new statements should be inserted after. Statement* newStmt = nullptr; @@ -1589,7 +1589,7 @@ Statement* Compiler::fgInlinePrependStatements(InlineInfo* inlineInfo) // argTmpNum here since in-linee compiler instance // would have iterated over these and marked them // accordingly. - impAssignTempGen(tmpNum, argNode, structHnd, (unsigned)CHECK_SPILL_NONE, &afterStmt, callILOffset, + impAssignTempGen(tmpNum, argNode, structHnd, (unsigned)CHECK_SPILL_NONE, &afterStmt, callDI, block); // We used to refine the temp type here based on @@ -1639,7 +1639,7 @@ Statement* Compiler::fgInlinePrependStatements(InlineInfo* inlineInfo) // Don't put GT_OBJ node under a GT_COMMA. // Codegen can't deal with it. // Just hang the address here in case there are side-effect. - newStmt = gtNewStmt(gtUnusedValNode(argNode->AsOp()->gtOp1), callILOffset); + newStmt = gtNewStmt(gtUnusedValNode(argNode->AsOp()->gtOp1), callDI); } else { @@ -1715,7 +1715,7 @@ Statement* Compiler::fgInlinePrependStatements(InlineInfo* inlineInfo) // just append the arg node as an unused value. if (newStmt == nullptr) { - newStmt = gtNewStmt(gtUnusedValNode(argNode), callILOffset); + newStmt = gtNewStmt(gtUnusedValNode(argNode), callDI); } fgInsertStmtAfter(block, afterStmt, newStmt); @@ -1751,7 +1751,7 @@ Statement* Compiler::fgInlinePrependStatements(InlineInfo* inlineInfo) CORINFO_CLASS_HANDLE exactClass = eeGetClassFromContext(inlineInfo->inlineCandidateInfo->exactContextHnd); tree = fgGetSharedCCtor(exactClass); - newStmt = gtNewStmt(tree, callILOffset); + newStmt = gtNewStmt(tree, callDI); fgInsertStmtAfter(block, afterStmt, newStmt); afterStmt = newStmt; } @@ -1759,7 +1759,7 @@ Statement* Compiler::fgInlinePrependStatements(InlineInfo* inlineInfo) // Insert the nullcheck statement now. if (nullcheck) { - newStmt = gtNewStmt(nullcheck, callILOffset); + newStmt = gtNewStmt(nullcheck, callDI); fgInsertStmtAfter(block, afterStmt, newStmt); afterStmt = newStmt; } @@ -1812,7 +1812,7 @@ Statement* Compiler::fgInlinePrependStatements(InlineInfo* inlineInfo) // Unsafe value cls check is not needed here since in-linee compiler instance would have // iterated over locals and marked accordingly. impAssignTempGen(tmpNum, gtNewZeroConNode(genActualType(lclTyp)), NO_CLASS_HANDLE, - (unsigned)CHECK_SPILL_NONE, &afterStmt, callILOffset, block); + (unsigned)CHECK_SPILL_NONE, &afterStmt, callDI, block); } else { @@ -1821,7 +1821,7 @@ Statement* Compiler::fgInlinePrependStatements(InlineInfo* inlineInfo) false, // isVolatile false); // not copyBlock - newStmt = gtNewStmt(tree, callILOffset); + newStmt = gtNewStmt(tree, callDI); fgInsertStmtAfter(block, afterStmt, newStmt); afterStmt = newStmt; } @@ -1881,7 +1881,7 @@ void Compiler::fgInlineAppendStatements(InlineInfo* inlineInfo, BasicBlock* bloc JITDUMP("fgInlineAppendStatements: nulling out gc ref inlinee locals.\n"); Statement* callStmt = inlineInfo->iciStmt; - IL_OFFSETX callILOffset = callStmt->GetILOffsetX(); + const DebugInfo& callDI = callStmt->GetDebugInfo(); CORINFO_METHOD_INFO* InlineeMethodInfo = InlineeCompiler->info.compMethodInfo; const unsigned lclCnt = InlineeMethodInfo->locals.numArgs; InlLclVarInfo* lclVarInfo = inlineInfo->lclVarInfo; @@ -1930,7 +1930,7 @@ void Compiler::fgInlineAppendStatements(InlineInfo* inlineInfo, BasicBlock* bloc // Assign null to the local. GenTree* nullExpr = gtNewTempAssign(tmpNum, gtNewZeroConNode(lclTyp)); - Statement* nullStmt = gtNewStmt(nullExpr, callILOffset); + Statement* nullStmt = gtNewStmt(nullExpr, callDI); if (stmtAfter == nullptr) { diff --git a/src/coreclr/jit/fgopt.cpp b/src/coreclr/jit/fgopt.cpp index 59f326b0fc13a..c0c4158b7800d 100644 --- a/src/coreclr/jit/fgopt.cpp +++ b/src/coreclr/jit/fgopt.cpp @@ -3953,7 +3953,7 @@ bool Compiler::fgOptimizeSwitchJumps() // GenTree* const dominantCaseCompare = gtNewOperNode(GT_EQ, TYP_INT, switchValue, gtNewIconNode(dominantCase)); GenTree* const jmpTree = gtNewOperNode(GT_JTRUE, TYP_VOID, dominantCaseCompare); - Statement* const jmpStmt = fgNewStmtFromTree(jmpTree, switchStmt->GetILOffsetX()); + Statement* const jmpStmt = fgNewStmtFromTree(jmpTree, switchStmt->GetDebugInfo()); fgInsertStmtAtEnd(block, jmpStmt); // Reattach switch value to the switch. This may introduce a comma diff --git a/src/coreclr/jit/fgprofile.cpp b/src/coreclr/jit/fgprofile.cpp index d301166966fbf..b9e50cb5cfa03 100644 --- a/src/coreclr/jit/fgprofile.cpp +++ b/src/coreclr/jit/fgprofile.cpp @@ -902,6 +902,26 @@ void Compiler::WalkSpanningTree(SpanningTreeVisitor* visitor) } } +// Map a block into its schema key we will use for storing basic blocks. +// +static int32_t EfficientEdgeCountBlockToKey(BasicBlock* block) +{ + static const int IS_INTERNAL_BLOCK = (int32_t)0x80000000; + int32_t key = (int32_t)block->bbCodeOffs; + // We may see empty BBJ_NONE BBF_INTERNAL blocks that were added + // by fgNormalizeEH. + // + // We'll use their bbNum in place of IL offset, and set + // a high bit as a "flag" + // + if ((block->bbFlags & BBF_INTERNAL) == BBF_INTERNAL) + { + key = block->bbNum | IS_INTERNAL_BLOCK; + } + + return key; +} + //------------------------------------------------------------------------ // EfficientEdgeCountInstrumentor: instrumentor that adds a counter to // selective edges. @@ -1086,35 +1106,19 @@ void EfficientEdgeCountInstrumentor::BuildSchemaElements(BasicBlock* block, Sche assert(probe->schemaIndex == -1); probe->schemaIndex = (int)schema.size(); - // Assign the current block's IL offset into the profile data. - // Use the "other" field to hold the target block IL offset. - // - int32_t sourceOffset = (int32_t)block->bbCodeOffs; - int32_t targetOffset = (int32_t)target->bbCodeOffs; - - // We may see empty BBJ_NONE BBF_INTERNAL blocks that were added - // by fgNormalizeEH. - // - // We'll use their bbNum in place of IL offset, and set - // a high bit as a "flag" + // Normally we use the the offset of the block in the schema, but for certain + // blocks we do not have any information we can use and need to use internal BB numbers. // - if ((block->bbFlags & BBF_INTERNAL) == BBF_INTERNAL) - { - sourceOffset = block->bbNum | IL_OFFSETX_CALLINSTRUCTIONBIT; - } - - if ((target->bbFlags & BBF_INTERNAL) == BBF_INTERNAL) - { - targetOffset = target->bbNum | IL_OFFSETX_CALLINSTRUCTIONBIT; - } + int32_t sourceKey = EfficientEdgeCountBlockToKey(block); + int32_t targetKey = EfficientEdgeCountBlockToKey(target); ICorJitInfo::PgoInstrumentationSchema schemaElem; schemaElem.Count = 1; - schemaElem.Other = targetOffset; + schemaElem.Other = targetKey; schemaElem.InstrumentationKind = JitConfig.JitCollect64BitCounts() ? ICorJitInfo::PgoInstrumentationKind::EdgeLongCount : ICorJitInfo::PgoInstrumentationKind::EdgeIntCount; - schemaElem.ILOffset = sourceOffset; + schemaElem.ILOffset = sourceKey; schemaElem.Offset = 0; schema.push_back(schemaElem); @@ -1287,7 +1291,7 @@ class BuildClassProbeSchemaGen schemaElem.InstrumentationKind = JitConfig.JitCollect64BitCounts() ? ICorJitInfo::PgoInstrumentationKind::TypeHandleHistogramLongCount : ICorJitInfo::PgoInstrumentationKind::TypeHandleHistogramIntCount; - schemaElem.ILOffset = jitGetILoffs(call->gtClassProfileCandidateInfo->ilOffset); + schemaElem.ILOffset = (int32_t)call->gtClassProfileCandidateInfo->ilOffset; schemaElem.Offset = 0; m_schema.push_back(schemaElem); @@ -1988,19 +1992,6 @@ class EfficientEdgeCountReconstructor : public SpanningTreeVisitor unsigned m_unknownEdges; unsigned m_zeroEdges; - // Map a block into its schema key. - // - static int32_t BlockToKey(BasicBlock* block) - { - int32_t key = (int32_t)block->bbCodeOffs; - if ((block->bbFlags & BBF_INTERNAL) == BBF_INTERNAL) - { - key = block->bbNum | IL_OFFSETX_CALLINSTRUCTIONBIT; - } - - return key; - } - // Map correlating block keys to blocks. // typedef JitHashTable, BasicBlock*> KeyToBlockMap; @@ -2018,7 +2009,7 @@ class EfficientEdgeCountReconstructor : public SpanningTreeVisitor } EdgeKey(BasicBlock* sourceBlock, BasicBlock* targetBlock) - : m_sourceKey(BlockToKey(sourceBlock)), m_targetKey(BlockToKey(targetBlock)) + : m_sourceKey(EfficientEdgeCountBlockToKey(sourceBlock)), m_targetKey(EfficientEdgeCountBlockToKey(targetBlock)) { } @@ -2241,7 +2232,7 @@ void EfficientEdgeCountReconstructor::Prepare() // for (BasicBlock* const block : m_comp->Blocks()) { - m_keyToBlockMap.Set(BlockToKey(block), block); + m_keyToBlockMap.Set(EfficientEdgeCountBlockToKey(block), block); BlockInfo* const info = new (m_allocator) BlockInfo(); SetBlockInfo(block, info); diff --git a/src/coreclr/jit/fgstmt.cpp b/src/coreclr/jit/fgstmt.cpp index 67da44726f994..41537d29db94a 100644 --- a/src/coreclr/jit/fgstmt.cpp +++ b/src/coreclr/jit/fgstmt.cpp @@ -381,9 +381,9 @@ Statement* Compiler::fgInsertStmtListAfter(BasicBlock* block, Statement* stmtAft * * Create a new statement from tree and wire the links up. */ -Statement* Compiler::fgNewStmtFromTree(GenTree* tree, BasicBlock* block, IL_OFFSETX offs) +Statement* Compiler::fgNewStmtFromTree(GenTree* tree, BasicBlock* block, const DebugInfo& di) { - Statement* stmt = gtNewStmt(tree, offs); + Statement* stmt = gtNewStmt(tree, di); if (fgStmtListThreaded) { @@ -403,17 +403,17 @@ Statement* Compiler::fgNewStmtFromTree(GenTree* tree, BasicBlock* block, IL_OFFS Statement* Compiler::fgNewStmtFromTree(GenTree* tree) { - return fgNewStmtFromTree(tree, nullptr, BAD_IL_OFFSET); + return fgNewStmtFromTree(tree, nullptr, DebugInfo()); } Statement* Compiler::fgNewStmtFromTree(GenTree* tree, BasicBlock* block) { - return fgNewStmtFromTree(tree, block, BAD_IL_OFFSET); + return fgNewStmtFromTree(tree, block, DebugInfo()); } -Statement* Compiler::fgNewStmtFromTree(GenTree* tree, IL_OFFSETX offs) +Statement* Compiler::fgNewStmtFromTree(GenTree* tree, const DebugInfo& di) { - return fgNewStmtFromTree(tree, nullptr, offs); + return fgNewStmtFromTree(tree, nullptr, di); } //------------------------------------------------------------------------ @@ -457,7 +457,7 @@ void Compiler::fgRemoveStmt(BasicBlock* block, Statement* stmt DEBUGARG(bool isU } #endif // DEBUG - if (opts.compDbgCode && stmt->GetPrevStmt() != stmt && stmt->GetILOffsetX() != BAD_IL_OFFSET) + if (opts.compDbgCode && stmt->GetPrevStmt() != stmt && stmt->GetDebugInfo().GetLocation().IsValid()) { /* TODO: For debuggable code, should we remove significant statement boundaries. Or should we leave a GT_NO_OP in its place? */ diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index 5742d8659ac12..b33351cd391b9 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -278,7 +278,7 @@ BasicBlock* Compiler::fgCreateGCPoll(GCPollType pollType, BasicBlock* block) if (nextStmt != nullptr) { // Is it possible for gtNextStmt to be NULL? - newStmt->SetILOffsetX(nextStmt->GetILOffsetX()); + newStmt->SetDebugInfo(nextStmt->GetDebugInfo()); } } diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 90093f9e7bba6..aec484e049f98 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -380,6 +380,25 @@ bool GenTree::IsNodeProperlySized() const } #endif +bool DebugInfo::GetParent(DebugInfo* par) const +{ + if (m_inlineContext->IsRoot()) + return false; + + *par = DebugInfo(m_inlineContext->GetParent(), m_inlineContext->GetLocation()); + return true; +} + +DebugInfo DebugInfo::GetRoot() const +{ + DebugInfo result = *this; + while (result.GetParent(&result)) + { + } + + return result; +} + //------------------------------------------------------------------------ // ReplaceWith: replace this with the src node. The source must be an isolated node // and cannot be used after the replacement. @@ -6349,13 +6368,13 @@ GenTree* Compiler::gtNewSIMDVectorZero(var_types simdType, CorInfoType simdBaseJ } #endif // FEATURE_SIMD -GenTreeCall* Compiler::gtNewIndCallNode(GenTree* addr, var_types type, GenTreeCall::Use* args, IL_OFFSETX ilOffset) +GenTreeCall* Compiler::gtNewIndCallNode(GenTree* addr, var_types type, GenTreeCall::Use* args, const DebugInfo& di) { - return gtNewCallNode(CT_INDIRECT, (CORINFO_METHOD_HANDLE)addr, type, args, ilOffset); + return gtNewCallNode(CT_INDIRECT, (CORINFO_METHOD_HANDLE)addr, type, args, di); } GenTreeCall* Compiler::gtNewCallNode( - gtCallTypes callType, CORINFO_METHOD_HANDLE callHnd, var_types type, GenTreeCall::Use* args, IL_OFFSETX ilOffset) + gtCallTypes callType, CORINFO_METHOD_HANDLE callHnd, var_types type, GenTreeCall::Use* args, const DebugInfo& di) { GenTreeCall* node = new (this, GT_CALL) GenTreeCall(genActualType(type)); @@ -6404,7 +6423,7 @@ GenTreeCall* Compiler::gtNewCallNode( // Spec: Managed Retval sequence points needs to be generated while generating debug info for debuggable code. // // Implementation note: if not generating MRV info genCallSite2ILOffsetMap will be NULL and - // codegen will pass BAD_IL_OFFSET as IL offset of a call node to emitter, which will cause emitter + // codegen will pass DebugInfo() to emitter, which will cause emitter // not to emit IP mapping entry. if (opts.compDbgCode && opts.compDbgInfo) { @@ -6418,14 +6437,14 @@ GenTreeCall* Compiler::gtNewCallNode( // // b) (Opt) Add new sequence points only if requested by debugger through // a new boundary type - ICorDebugInfo::BoundaryTypes - if (genCallSite2ILOffsetMap == nullptr) + if (genCallSite2DebugInfoMap == nullptr) { - genCallSite2ILOffsetMap = new (getAllocator()) CallSiteILOffsetTable(getAllocator()); + genCallSite2DebugInfoMap = new (getAllocator()) CallSiteDebugInfoTable(getAllocator()); } // Make sure that there are no duplicate entries for a given call node - assert(!genCallSite2ILOffsetMap->Lookup(node)); - genCallSite2ILOffsetMap->Set(node, ilOffset); + assert(!genCallSite2DebugInfoMap->Lookup(node)); + genCallSite2DebugInfoMap->Set(node, di); } // Initialize gtOtherRegs @@ -6446,7 +6465,7 @@ GenTreeCall* Compiler::gtNewCallNode( return node; } -GenTreeLclVar* Compiler::gtNewLclvNode(unsigned lnum, var_types type DEBUGARG(IL_OFFSETX ILoffs)) +GenTreeLclVar* Compiler::gtNewLclvNode(unsigned lnum, var_types type DEBUGARG(IL_OFFSET offs)) { assert(type != TYP_VOID); // We need to ensure that all struct values are normalized. @@ -6466,7 +6485,7 @@ GenTreeLclVar* Compiler::gtNewLclvNode(unsigned lnum, var_types type DEBUGARG(IL assert((type == varDsc->lvType) || simd12ToSimd16Widening || (lvaIsImplicitByRefLocal(lnum) && fgGlobalMorph && (varDsc->lvType == TYP_BYREF))); } - GenTreeLclVar* node = new (this, GT_LCL_VAR) GenTreeLclVar(GT_LCL_VAR, type, lnum DEBUGARG(ILoffs)); + GenTreeLclVar* node = new (this, GT_LCL_VAR) GenTreeLclVar(GT_LCL_VAR, type, lnum DEBUGARG(offs)); /* Cannot have this assert because the inliner uses this function * to add temporaries */ @@ -6476,7 +6495,7 @@ GenTreeLclVar* Compiler::gtNewLclvNode(unsigned lnum, var_types type DEBUGARG(IL return node; } -GenTreeLclVar* Compiler::gtNewLclLNode(unsigned lnum, var_types type DEBUGARG(IL_OFFSETX ILoffs)) +GenTreeLclVar* Compiler::gtNewLclLNode(unsigned lnum, var_types type DEBUGARG(IL_OFFSET offs)) { // We need to ensure that all struct values are normalized. // It might be nice to assert this in general, but we have assignments of int to long. @@ -6491,7 +6510,7 @@ GenTreeLclVar* Compiler::gtNewLclLNode(unsigned lnum, var_types type DEBUGARG(IL // This local variable node may later get transformed into a large node assert(GenTree::s_gtNodeSizes[LargeOpOpcode()] > GenTree::s_gtNodeSizes[GT_LCL_VAR]); GenTreeLclVar* node = - new (this, LargeOpOpcode()) GenTreeLclVar(GT_LCL_VAR, type, lnum DEBUGARG(ILoffs) DEBUGARG(/*largeNode*/ true)); + new (this, LargeOpOpcode()) GenTreeLclVar(GT_LCL_VAR, type, lnum DEBUGARG(offs) DEBUGARG(/*largeNode*/ true)); return node; } @@ -11477,13 +11496,13 @@ void Compiler::gtDispLeaf(GenTree* tree, IndentStack* indentStack) case GT_IL_OFFSET: printf(" IL offset: "); - if (tree->AsILOffset()->gtStmtILoffsx == BAD_IL_OFFSET) + if (!tree->AsILOffset()->gtStmtDI.GetLocation().IsValid()) { printf("???"); } else { - printf("0x%x", jitGetILoffs(tree->AsILOffset()->gtStmtILoffsx)); + printf("0x%x", tree->AsILOffset()->gtStmtDI.GetLocation().GetOffset()); } break; @@ -12344,15 +12363,14 @@ void Compiler::gtDispStmt(Statement* stmt, const char* msg /* = nullptr */) printf("%s ", msg); } printStmtID(stmt); - IL_OFFSETX firstILOffsx = stmt->GetILOffsetX(); printf(" (IL "); - if (firstILOffsx == BAD_IL_OFFSET) + if (!stmt->GetDebugInfo().GetLocation().IsValid()) { printf(" ???"); } else { - printf("0x%03X", jitGetILoffs(firstILOffsx)); + printf("0x%03X", stmt->GetDebugInfo().GetLocation().GetOffset()); } printf("..."); @@ -15249,7 +15267,7 @@ GenTree* Compiler::gtFoldExprConst(GenTree* tree) // May set compFloatingPointUsed. GenTree* Compiler::gtNewTempAssign( - unsigned tmp, GenTree* val, Statement** pAfterStmt, IL_OFFSETX ilOffset, BasicBlock* block) + unsigned tmp, GenTree* val, Statement** pAfterStmt, const DebugInfo& di, BasicBlock* block) { // Self-assignment is a nop. if (val->OperGet() == GT_LCL_VAR && val->AsLclVarCommon()->GetLclNum() == tmp) @@ -15391,7 +15409,7 @@ GenTree* Compiler::gtNewTempAssign( } dest->gtFlags |= GTF_DONT_CSE; valx->gtFlags |= GTF_DONT_CSE; - asg = impAssignStruct(dest, val, valStructHnd, (unsigned)CHECK_SPILL_NONE, pAfterStmt, ilOffset, block); + asg = impAssignStruct(dest, val, valStructHnd, (unsigned)CHECK_SPILL_NONE, pAfterStmt, di, block); } else { diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index 091c2464e6950..45839c5b51c5f 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -6134,16 +6134,122 @@ struct GenTreeRetExpr : public GenTree class InlineContext; +// Represents information about the location of an IL instruction. +class ILLocation +{ +public: + ILLocation() + : m_offset(BAD_IL_OFFSET) + , m_isStackEmpty(false) + , m_isCall(false) + { + } + + ILLocation(IL_OFFSET offset, bool isStackEmpty, bool isCall) + : m_offset(offset) + , m_isStackEmpty(isStackEmpty) + , m_isCall(isCall) + { + } + + IL_OFFSET GetOffset() const + { + return m_offset; + } + + // Is this source location at a stack empty point? We need to be able to + // report this information back to the debugger since we only allow EnC + // transitions at stack empty points. + bool IsStackEmpty() const + { + return m_isStackEmpty; + } + + bool IsCall() const + { + return m_isCall; + } + + bool IsValid() const + { + return m_offset != BAD_IL_OFFSET; + } + + inline bool operator==(const ILLocation& other) const + { + return (m_offset == other.m_offset) && (m_isStackEmpty == other.m_isStackEmpty) && (m_isCall == other.m_isCall); + } + + inline bool operator!=(const ILLocation& other) const + { + return !(*this == other); + } + +private: + IL_OFFSET m_offset; + bool m_isStackEmpty : 1; + bool m_isCall : 1; +}; + +// Represents debug information about a statement. +class DebugInfo +{ +public: + DebugInfo() + : m_inlineContext(nullptr) + { + } + + DebugInfo(InlineContext* inlineContext, ILLocation loc) + : m_inlineContext(inlineContext) + , m_location(loc) + { + } + + InlineContext* GetInlineContext() const + { + return m_inlineContext; + } + + ILLocation GetLocation() const + { + return m_location; + } + + // Retrieve information about the location that inlined this statement. + bool GetParent(DebugInfo* par) const; + + // Get debug info in the root. If this debug info is in the root, then + // returns *this. Otherwise returns information of the call in the root that + // eventually produced this statement through inlines. + DebugInfo GetRoot() const; + + inline bool operator==(const DebugInfo& other) const + { + return (m_inlineContext == other.m_inlineContext) && (m_location == other.m_location); + } + + inline bool operator!=(const DebugInfo& other) const + { + return !(*this == other); + } + +private: + InlineContext* m_inlineContext; + ILLocation m_location; +}; + +// In LIR there are no longer statements so debug information is inserted linearly using these nodes. struct GenTreeILOffset : public GenTree { - IL_OFFSETX gtStmtILoffsx; // instr offset (if available) + DebugInfo gtStmtDI; // debug info #ifdef DEBUG IL_OFFSET gtStmtLastILoffs; // instr offset at end of stmt #endif - GenTreeILOffset(IL_OFFSETX offset DEBUGARG(IL_OFFSET lastOffset = BAD_IL_OFFSET)) + GenTreeILOffset(const DebugInfo& di DEBUGARG(IL_OFFSET lastOffset = BAD_IL_OFFSET)) : GenTree(GT_IL_OFFSET, TYP_VOID) - , gtStmtILoffsx(offset) + , gtStmtDI(di) #ifdef DEBUG , gtStmtLastILoffs(lastOffset) #endif @@ -6216,13 +6322,11 @@ class GenTreeList struct Statement { public: - Statement(GenTree* expr, IL_OFFSETX offset DEBUGARG(unsigned stmtID)) + Statement(GenTree* expr DEBUGARG(unsigned stmtID)) : m_rootNode(expr) , m_treeList(nullptr) , m_next(nullptr) , m_prev(nullptr) - , m_inlineContext(nullptr) - , m_ILOffsetX(offset) #ifdef DEBUG , m_lastILOffset(BAD_IL_OFFSET) , m_stmtID(stmtID) @@ -6265,24 +6369,27 @@ struct Statement return GenTreeList(GetTreeList()); } - InlineContext* GetInlineContext() const + const DebugInfo& GetDebugInfo() const { - return m_inlineContext; + return m_debugInfo; } - void SetInlineContext(InlineContext* inlineContext) + void SetDebugInfo(const DebugInfo& di) { - m_inlineContext = inlineContext; + m_debugInfo = di; } - IL_OFFSETX GetILOffsetX() const + // During inlining we require the inline context to be maintained. We use + // the storage in the debug info for it. After inlining is over we no + // longer require inline contexts for statements without debug information. + InlineContext* GetInlineContext() { - return m_ILOffsetX; + return m_debugInfo.GetInlineContext(); } - void SetILOffsetX(IL_OFFSETX offsetX) + void SetInlineContext(InlineContext* inlineContext) { - m_ILOffsetX = offsetX; + m_debugInfo = DebugInfo(inlineContext, m_debugInfo.GetLocation()); } #ifdef DEBUG @@ -6363,9 +6470,7 @@ struct Statement Statement* m_next; Statement* m_prev; - InlineContext* m_inlineContext; // The inline context for this statement. - - IL_OFFSETX m_ILOffsetX; // The instr offset (if available). + DebugInfo m_debugInfo; // Debug info for the statement, including inline context #ifdef DEBUG IL_OFFSET m_lastILOffset; // The instr offset at the end of this statement. diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index d1c955fddb321..1ff9605b8d176 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -615,10 +615,10 @@ inline void Compiler::impAppendStmt(Statement* stmt, unsigned chkLevel) impMarkContiguousSIMDFieldAssignments(stmt); #endif - /* Once we set impCurStmtOffs in an appended tree, we are ready to - report the following offsets. So reset impCurStmtOffs */ + /* Once we set impCurStmtDI in an appended tree, we are ready to + report the following offsets. So reset impCurStmtDI */ - if (impLastStmt->GetILOffsetX() == impCurStmtOffs) + if (impLastStmt->GetDebugInfo().GetLocation() == impCurStmtDI.GetLocation()) { impCurStmtOffsSet(BAD_IL_OFFSET); } @@ -713,13 +713,13 @@ inline void Compiler::impInsertStmtBefore(Statement* stmt, Statement* stmtBefore * Return the newly created statement. */ -Statement* Compiler::impAppendTree(GenTree* tree, unsigned chkLevel, IL_OFFSETX offset) +Statement* Compiler::impAppendTree(GenTree* tree, unsigned chkLevel, const DebugInfo& di) { assert(tree); /* Allocate an 'expression statement' node */ - Statement* stmt = gtNewStmt(tree, offset); + Statement* stmt = gtNewStmt(tree, di); /* Append the statement to the current block's stmt list */ @@ -733,11 +733,11 @@ Statement* Compiler::impAppendTree(GenTree* tree, unsigned chkLevel, IL_OFFSETX * Insert the given expression tree before "stmtBefore" */ -void Compiler::impInsertTreeBefore(GenTree* tree, IL_OFFSETX offset, Statement* stmtBefore) +void Compiler::impInsertTreeBefore(GenTree* tree, const DebugInfo& di, Statement* stmtBefore) { /* Allocate an 'expression statement' node */ - Statement* stmt = gtNewStmt(tree, offset); + Statement* stmt = gtNewStmt(tree, di); /* Append the statement to the current block's stmt list */ @@ -754,7 +754,7 @@ void Compiler::impAssignTempGen(unsigned tmp, GenTree* val, unsigned curLevel, Statement** pAfterStmt, /* = NULL */ - IL_OFFSETX ilOffset, /* = BAD_IL_OFFSET */ + const DebugInfo& di, /* = DebugInfo() */ BasicBlock* block /* = NULL */ ) { @@ -764,13 +764,13 @@ void Compiler::impAssignTempGen(unsigned tmp, { if (pAfterStmt) { - Statement* asgStmt = gtNewStmt(asg, ilOffset); + Statement* asgStmt = gtNewStmt(asg, di); fgInsertStmtAfter(block, *pAfterStmt, asgStmt); *pAfterStmt = asgStmt; } else { - impAppendTree(asg, curLevel, impCurStmtOffs); + impAppendTree(asg, curLevel, impCurStmtDI); } } } @@ -784,7 +784,7 @@ void Compiler::impAssignTempGen(unsigned tmpNum, CORINFO_CLASS_HANDLE structType, unsigned curLevel, Statement** pAfterStmt, /* = NULL */ - IL_OFFSETX ilOffset, /* = BAD_IL_OFFSET */ + const DebugInfo& di, /* = DebugInfo() */ BasicBlock* block /* = NULL */ ) { @@ -813,7 +813,7 @@ void Compiler::impAssignTempGen(unsigned tmpNum, // calls that may not actually be required - e.g. if we only access a field of a struct. GenTree* dst = gtNewLclvNode(tmpNum, varType); - asg = impAssignStruct(dst, val, structType, curLevel, pAfterStmt, ilOffset, block); + asg = impAssignStruct(dst, val, structType, curLevel, pAfterStmt, di, block); } else { @@ -824,13 +824,13 @@ void Compiler::impAssignTempGen(unsigned tmpNum, { if (pAfterStmt) { - Statement* asgStmt = gtNewStmt(asg, ilOffset); + Statement* asgStmt = gtNewStmt(asg, di); fgInsertStmtAfter(block, *pAfterStmt, asgStmt); *pAfterStmt = asgStmt; } else { - impAppendTree(asg, curLevel, impCurStmtOffs); + impAppendTree(asg, curLevel, impCurStmtDI); } } } @@ -1194,15 +1194,16 @@ GenTree* Compiler::impAssignStruct(GenTree* dest, CORINFO_CLASS_HANDLE structHnd, unsigned curLevel, Statement** pAfterStmt, /* = nullptr */ - IL_OFFSETX ilOffset, /* = BAD_IL_OFFSET */ + const DebugInfo& di, /* = DebugInfo() */ BasicBlock* block /* = nullptr */ ) { assert(varTypeIsStruct(dest)); - if (ilOffset == BAD_IL_OFFSET) + DebugInfo usedDI = di; + if (!usedDI.GetLocation().IsValid()) { - ilOffset = impCurStmtOffs; + usedDI = impCurStmtDI; } while (dest->gtOper == GT_COMMA) @@ -1213,13 +1214,13 @@ GenTree* Compiler::impAssignStruct(GenTree* dest, // Append all the op1 of GT_COMMA trees before we evaluate op2 of the GT_COMMA tree. if (pAfterStmt) { - Statement* newStmt = gtNewStmt(dest->AsOp()->gtOp1, ilOffset); + Statement* newStmt = gtNewStmt(dest->AsOp()->gtOp1, usedDI); fgInsertStmtAfter(block, *pAfterStmt, newStmt); *pAfterStmt = newStmt; } else { - impAppendTree(dest->AsOp()->gtOp1, curLevel, ilOffset); // do the side effect + impAppendTree(dest->AsOp()->gtOp1, curLevel, usedDI); // do the side effect } // set dest to the second thing @@ -1249,7 +1250,7 @@ GenTree* Compiler::impAssignStruct(GenTree* dest, destAddr = gtNewOperNode(GT_ADDR, TYP_BYREF, dest); } - return (impAssignStructPtr(destAddr, src, structHnd, curLevel, pAfterStmt, ilOffset, block)); + return (impAssignStructPtr(destAddr, src, structHnd, curLevel, pAfterStmt, usedDI, block)); } //------------------------------------------------------------------------ @@ -1261,7 +1262,7 @@ GenTree* Compiler::impAssignStruct(GenTree* dest, // structHnd - handle representing the struct type // curLevel - stack level for which a spill may be being done // pAfterStmt - statement to insert any additional statements after -// ilOffset - il offset for new statements +// di - debug info for new statements // block - block to insert any additional statements in // // Return Value: @@ -1275,16 +1276,17 @@ GenTree* Compiler::impAssignStructPtr(GenTree* destAddr, CORINFO_CLASS_HANDLE structHnd, unsigned curLevel, Statement** pAfterStmt, /* = NULL */ - IL_OFFSETX ilOffset, /* = BAD_IL_OFFSET */ + const DebugInfo& di, /* = DebugInfo() */ BasicBlock* block /* = NULL */ ) { GenTree* dest = nullptr; GenTreeFlags destFlags = GTF_EMPTY; - if (ilOffset == BAD_IL_OFFSET) + DebugInfo usedDI = di; + if (!usedDI.GetLocation().IsValid()) { - ilOffset = impCurStmtOffs; + usedDI = impCurStmtDI; } assert(src->OperIs(GT_LCL_VAR, GT_LCL_FLD, GT_FIELD, GT_IND, GT_OBJ, GT_CALL, GT_MKREFANY, GT_RET_EXPR, GT_COMMA) || @@ -1501,13 +1503,13 @@ GenTree* Compiler::impAssignStructPtr(GenTree* destAddr, GenTree* asg = gtNewAssignNode(ptrSlot, src->AsOp()->gtOp1); if (pAfterStmt) { - Statement* newStmt = gtNewStmt(asg, ilOffset); + Statement* newStmt = gtNewStmt(asg, usedDI); fgInsertStmtAfter(block, *pAfterStmt, newStmt); *pAfterStmt = newStmt; } else { - impAppendTree(asg, curLevel, ilOffset); + impAppendTree(asg, curLevel, usedDI); } // return the assign of the type value, to be appended @@ -1520,14 +1522,14 @@ GenTree* Compiler::impAssignStructPtr(GenTree* destAddr, if (pAfterStmt) { // Insert op1 after '*pAfterStmt' - Statement* newStmt = gtNewStmt(src->AsOp()->gtOp1, ilOffset); + Statement* newStmt = gtNewStmt(src->AsOp()->gtOp1, usedDI); fgInsertStmtAfter(block, *pAfterStmt, newStmt); *pAfterStmt = newStmt; } else if (impLastStmt != nullptr) { // Do the side-effect as a separate statement. - impAppendTree(src->AsOp()->gtOp1, curLevel, ilOffset); + impAppendTree(src->AsOp()->gtOp1, curLevel, usedDI); } else { @@ -1535,12 +1537,12 @@ GenTree* Compiler::impAssignStructPtr(GenTree* destAddr, // in the importer where we can append the side effect. // Instead, we're going to sink the assignment below the COMMA. src->AsOp()->gtOp2 = - impAssignStructPtr(destAddr, src->AsOp()->gtOp2, structHnd, curLevel, pAfterStmt, ilOffset, block); + impAssignStructPtr(destAddr, src->AsOp()->gtOp2, structHnd, curLevel, pAfterStmt, usedDI, block); return src; } // Evaluate the second thing using recursion. - return impAssignStructPtr(destAddr, src->AsOp()->gtOp2, structHnd, curLevel, pAfterStmt, ilOffset, block); + return impAssignStructPtr(destAddr, src->AsOp()->gtOp2, structHnd, curLevel, pAfterStmt, usedDI, block); } else if (src->IsLocal()) { @@ -1689,7 +1691,7 @@ GenTree* Compiler::impGetStructAddr(GenTree* structVal, beforeStmt = oldLastStmt->GetNextStmt(); } - impInsertTreeBefore(structVal->AsOp()->gtOp1, impCurStmtOffs, beforeStmt); + impInsertTreeBefore(structVal->AsOp()->gtOp1, impCurStmtDI, beforeStmt); structVal->AsOp()->gtOp1 = gtNewNothingNode(); } @@ -2321,7 +2323,7 @@ GenTree* Compiler::impRuntimeLookupToTree(CORINFO_RESOLVED_TOKEN* pResolvedToken impSpillSideEffects(true, CHECK_SPILL_ALL DEBUGARG("bubbling QMark0")); unsigned slotLclNum = lvaGrabTemp(true DEBUGARG("impRuntimeLookup test")); - impAssignTempGen(slotLclNum, slotPtrTree, NO_CLASS_HANDLE, (unsigned)CHECK_SPILL_ALL, nullptr, impCurStmtOffs); + impAssignTempGen(slotLclNum, slotPtrTree, NO_CLASS_HANDLE, (unsigned)CHECK_SPILL_ALL, nullptr, impCurStmtDI); GenTree* slot = gtNewLclvNode(slotLclNum, TYP_I_IMPL); // downcast the pointer to a TYP_INT on 64-bit targets @@ -2341,7 +2343,7 @@ GenTree* Compiler::impRuntimeLookupToTree(CORINFO_RESOLVED_TOKEN* pResolvedToken GenTree* asg = gtNewAssignNode(slot, indir); GenTreeColon* colon = new (this, GT_COLON) GenTreeColon(TYP_VOID, gtNewNothingNode(), asg); GenTreeQmark* qmark = gtNewQmarkNode(TYP_VOID, relop, colon); - impAppendTree(qmark, (unsigned)CHECK_SPILL_NONE, impCurStmtOffs); + impAppendTree(qmark, (unsigned)CHECK_SPILL_NONE, impCurStmtDI); return gtNewLclvNode(slotLclNum, TYP_I_IMPL); } @@ -2822,8 +2824,8 @@ BasicBlock* Compiler::impPushCatchArgOnStack(BasicBlock* hndBlk, CORINFO_CLASS_H { // Report the debug info. impImportBlockCode won't treat the actual handler as exception block and thus // won't do it for us. - impCurStmtOffs = newBlk->bbCodeOffs | IL_OFFSETX_STKBIT; - argStmt = gtNewStmt(argAsg, impCurStmtOffs); + impCurStmtDI = DebugInfo(impCurStmtDI.GetInlineContext(), ILLocation(newBlk->bbCodeOffs, false, false)); + argStmt = gtNewStmt(argAsg, impCurStmtDI); } else { @@ -2872,7 +2874,7 @@ GenTree* Compiler::impCloneExpr(GenTree* tree, // specialized type (e.g. a SIMD type). So we will get the type from // the lclVar AFTER calling impAssignTempGen(). - impAssignTempGen(temp, tree, structHnd, curLevel, pAfterStmt, impCurStmtOffs); + impAssignTempGen(temp, tree, structHnd, curLevel, pAfterStmt, impCurStmtDI); var_types type = genActualType(lvaTable[temp].TypeGet()); *pClone = gtNewLclvNode(temp, type); @@ -2889,31 +2891,28 @@ inline void Compiler::impCurStmtOffsSet(IL_OFFSET offs) if (compIsForInlining()) { Statement* callStmt = impInlineInfo->iciStmt; - impCurStmtOffs = callStmt->GetILOffsetX(); + impCurStmtDI = callStmt->GetDebugInfo(); } else { - assert(offs == BAD_IL_OFFSET || (offs & IL_OFFSETX_BITS) == 0); - IL_OFFSETX stkBit = (verCurrentState.esStackDepth > 0) ? IL_OFFSETX_STKBIT : 0; - impCurStmtOffs = offs | stkBit; + bool isStackEmpty = verCurrentState.esStackDepth <= 0; + impCurStmtDI = DebugInfo(impCurStmtDI.GetInlineContext(), ILLocation(offs, isStackEmpty, false)); } } /***************************************************************************** * Returns current IL offset with stack-empty and call-instruction info incorporated */ -inline IL_OFFSETX Compiler::impCurILOffset(IL_OFFSET offs, bool callInstruction) +inline DebugInfo Compiler::impCurDebugInfo(IL_OFFSET offs, bool callInstruction) { if (compIsForInlining()) { - return BAD_IL_OFFSET; + return DebugInfo(); } else { - assert(offs == BAD_IL_OFFSET || (offs & IL_OFFSETX_BITS) == 0); - IL_OFFSETX stkBit = (verCurrentState.esStackDepth > 0) ? IL_OFFSETX_STKBIT : 0; - IL_OFFSETX callInstructionBit = callInstruction ? IL_OFFSETX_CALLINSTRUCTIONBIT : 0; - return offs | stkBit | callInstructionBit; + bool isStackEmpty = verCurrentState.esStackDepth <= 0; + return DebugInfo(impCurStmtDI.GetInlineContext(), ILLocation(offs, isStackEmpty, callInstruction)); } } @@ -2978,7 +2977,7 @@ void Compiler::impNoteBranchOffs() { if (opts.compDbgCode) { - impAppendTree(gtNewNothingNode(), (unsigned)CHECK_SPILL_NONE, impCurStmtOffs); + impAppendTree(gtNewNothingNode(), (unsigned)CHECK_SPILL_NONE, impCurStmtDI); } } @@ -5646,7 +5645,7 @@ void Compiler::verConvertBBToThrowVerificationException(BasicBlock* block DEBUGA GenTree* op1 = gtNewHelperCallNode(CORINFO_HELP_VERIFICATION, TYP_VOID, gtNewCallArgs(gtNewIconNode(block->bbCodeOffs))); // verCurrentState.esStackDepth = 0; - impAppendTree(op1, (unsigned)CHECK_SPILL_NONE, impCurStmtOffs); + impAppendTree(op1, (unsigned)CHECK_SPILL_NONE, impCurStmtDI); // The inliner is not able to handle methods that require throw block, so // make sure this methods never gets inlined. @@ -7056,7 +7055,7 @@ void Compiler::impImportAndPushBox(CORINFO_RESOLVED_TOKEN* pResolvedToken) // Assign the boxed object to the box temp. // GenTree* asg = gtNewTempAssign(impBoxTemp, op1); - Statement* asgStmt = impAppendTree(asg, (unsigned)CHECK_SPILL_NONE, impCurStmtOffs); + Statement* asgStmt = impAppendTree(asg, (unsigned)CHECK_SPILL_NONE, impCurStmtDI); // If the exprToBox is a call that returns its value via a ret buf arg, // move the assignment statement(s) before the call (which must be a top level tree). @@ -7178,7 +7177,7 @@ void Compiler::impImportAndPushBox(CORINFO_RESOLVED_TOKEN* pResolvedToken) impSpillSideEffects(true, (unsigned)CHECK_SPILL_ALL DEBUGARG("impImportAndPushBox")); // Set up this copy as a second assignment. - Statement* copyStmt = impAppendTree(op1, (unsigned)CHECK_SPILL_NONE, impCurStmtOffs); + Statement* copyStmt = impAppendTree(op1, (unsigned)CHECK_SPILL_NONE, impCurStmtDI); op1 = gtNewLclvNode(impBoxTemp, TYP_REF); @@ -7676,7 +7675,7 @@ void Compiler::impCheckForPInvokeCall( } } -GenTreeCall* Compiler::impImportIndirectCall(CORINFO_SIG_INFO* sig, IL_OFFSETX ilOffset) +GenTreeCall* Compiler::impImportIndirectCall(CORINFO_SIG_INFO* sig, const DebugInfo& di) { var_types callRetTyp = JITtype2varType(sig->retType); @@ -7714,7 +7713,7 @@ GenTreeCall* Compiler::impImportIndirectCall(CORINFO_SIG_INFO* sig, IL_OFFSETX i /* Create the call node */ - GenTreeCall* call = gtNewIndCallNode(fptr, callRetTyp, nullptr, ilOffset); + GenTreeCall* call = gtNewIndCallNode(fptr, callRetTyp, nullptr, di); call->gtFlags |= GTF_EXCEPT | (fptr->gtFlags & GTF_GLOB_EFFECT); #ifdef UNIX_X86_ABI @@ -8267,7 +8266,7 @@ void Compiler::impInsertHelperCall(CORINFO_HELPER_DESC* helperInfo) * Also, consider sticking this in the first basic block. */ GenTree* callout = gtNewHelperCallNode(helperInfo->helperNum, TYP_VOID, args); - impAppendTree(callout, (unsigned)CHECK_SPILL_NONE, impCurStmtOffs); + impAppendTree(callout, (unsigned)CHECK_SPILL_NONE, impCurStmtDI); } //------------------------------------------------------------------------ @@ -8483,7 +8482,7 @@ var_types Compiler::impImportCall(OPCODE opcode, { assert(opcode == CEE_CALL || opcode == CEE_CALLVIRT || opcode == CEE_NEWOBJ || opcode == CEE_CALLI); - IL_OFFSETX ilOffset = impCurILOffset(rawILOffset, true); + DebugInfo di = impCurDebugInfo(rawILOffset, true); var_types callRetTyp = TYP_COUNT; CORINFO_SIG_INFO* sig = nullptr; CORINFO_METHOD_HANDLE methHnd = nullptr; @@ -8566,7 +8565,7 @@ var_types Compiler::impImportCall(OPCODE opcode, callRetTyp = JITtype2varType(calliSig.retType); - call = impImportIndirectCall(&calliSig, ilOffset); + call = impImportIndirectCall(&calliSig, di); // We don't know the target method, so we have to infer the flags, or // assume the worst-case. @@ -8791,7 +8790,7 @@ var_types Compiler::impImportCall(OPCODE opcode, else { // The stub address is known at compile time - call = gtNewCallNode(CT_USER_FUNC, callInfo->hMethod, callRetTyp, nullptr, ilOffset); + call = gtNewCallNode(CT_USER_FUNC, callInfo->hMethod, callRetTyp, nullptr, di); call->AsCall()->gtStubCallStubAddr = callInfo->stubLookup.constLookup.addr; call->gtFlags |= GTF_CALL_VIRT_STUB; assert(callInfo->stubLookup.constLookup.accessType != IAT_PPVALUE && @@ -8821,7 +8820,7 @@ var_types Compiler::impImportCall(OPCODE opcode, { assert(!(mflags & CORINFO_FLG_STATIC)); // can't call a static method assert(!(clsFlags & CORINFO_FLG_VALUECLASS)); - call = gtNewCallNode(CT_USER_FUNC, callInfo->hMethod, callRetTyp, nullptr, ilOffset); + call = gtNewCallNode(CT_USER_FUNC, callInfo->hMethod, callRetTyp, nullptr, di); call->gtFlags |= GTF_CALL_VIRT_VTABLE; // Should we expand virtual call targets early for this method? @@ -8871,7 +8870,7 @@ var_types Compiler::impImportCall(OPCODE opcode, // Create the actual call node - call = gtNewIndCallNode(fptr, callRetTyp, args, ilOffset); + call = gtNewIndCallNode(fptr, callRetTyp, args, di); call->AsCall()->gtCallThisArg = gtNewCallArgs(thisPtrCopy); call->gtFlags |= GTF_EXCEPT | (fptr->gtFlags & GTF_GLOB_EFFECT); @@ -8898,7 +8897,7 @@ var_types Compiler::impImportCall(OPCODE opcode, case CORINFO_CALL: { // This is for a non-virtual, non-interface etc. call - call = gtNewCallNode(CT_USER_FUNC, callInfo->hMethod, callRetTyp, nullptr, ilOffset); + call = gtNewCallNode(CT_USER_FUNC, callInfo->hMethod, callRetTyp, nullptr, di); // We remove the nullcheck for the GetType call intrinsic. // TODO-CQ: JIT64 does not introduce the null check for many more helper calls @@ -8943,7 +8942,7 @@ var_types Compiler::impImportCall(OPCODE opcode, impAssignTempGen(lclNum, fptr, (unsigned)CHECK_SPILL_ALL); fptr = gtNewLclvNode(lclNum, TYP_I_IMPL); - call = gtNewIndCallNode(fptr, callRetTyp, nullptr, ilOffset); + call = gtNewIndCallNode(fptr, callRetTyp, nullptr, di); call->gtFlags |= GTF_EXCEPT | (fptr->gtFlags & GTF_GLOB_EFFECT); if (callInfo->nullInstanceCheck) { @@ -9384,6 +9383,7 @@ var_types Compiler::impImportCall(OPCODE opcode, const bool isLateDevirtualization = false; impDevirtualizeCall(call->AsCall(), pResolvedToken, &callInfo->hMethod, &callInfo->methodFlags, &callInfo->contextHandle, &exactContextHnd, isLateDevirtualization, isExplicitTailCall, + // Take care to pass raw IL offset here as the 'debug info' might be different for inlinees. rawILOffset); } @@ -9436,7 +9436,7 @@ var_types Compiler::impImportCall(OPCODE opcode, } // append the call node. - impAppendTree(call, (unsigned)CHECK_SPILL_ALL, impCurStmtOffs); + impAppendTree(call, (unsigned)CHECK_SPILL_ALL, impCurStmtDI); // Now push the value of the 'new onto the stack @@ -9702,11 +9702,11 @@ var_types Compiler::impImportCall(OPCODE opcode, { // we actually did push something, so don't spill the thing we just pushed. assert(verCurrentState.esStackDepth > 0); - impAppendTree(call, verCurrentState.esStackDepth - 1, impCurStmtOffs); + impAppendTree(call, verCurrentState.esStackDepth - 1, impCurStmtDI); } else { - impAppendTree(call, (unsigned)CHECK_SPILL_ALL, impCurStmtOffs); + impAppendTree(call, (unsigned)CHECK_SPILL_ALL, impCurStmtDI); } } else @@ -9774,7 +9774,7 @@ var_types Compiler::impImportCall(OPCODE opcode, assert(!isFatPointerCandidate); // We should not try to inline calli. // Make the call its own tree (spill the stack if needed). - impAppendTree(call, (unsigned)CHECK_SPILL_ALL, impCurStmtOffs); + impAppendTree(call, (unsigned)CHECK_SPILL_ALL, impCurStmtDI); // TODO: Still using the widened type. GenTree* retExpr = gtNewInlineCandidateReturnExpr(call, genActualType(callRetTyp), compCurBB->bbFlags); @@ -10299,7 +10299,7 @@ void Compiler::impImportLeave(BasicBlock* block) callBlock->bbJumpKind = BBJ_CALLFINALLY; // convert the BBJ_LEAVE to BBJ_CALLFINALLY if (endCatches) - impAppendTree(endCatches, (unsigned)CHECK_SPILL_NONE, impCurStmtOffs); + impAppendTree(endCatches, (unsigned)CHECK_SPILL_NONE, impCurStmtDI); #ifdef DEBUG if (verbose) @@ -10386,7 +10386,7 @@ void Compiler::impImportLeave(BasicBlock* block) block->bbJumpKind = BBJ_ALWAYS; // convert the BBJ_LEAVE to a BBJ_ALWAYS if (endCatches) - impAppendTree(endCatches, (unsigned)CHECK_SPILL_NONE, impCurStmtOffs); + impAppendTree(endCatches, (unsigned)CHECK_SPILL_NONE, impCurStmtDI); #ifdef DEBUG if (verbose) @@ -11725,17 +11725,17 @@ void Compiler::impImportBlockCode(BasicBlock* block) impSpillStackEnsure(true); } - // Has impCurStmtOffs been reported in any tree? + // Has impCurStmtDI been reported in any tree? - if (impCurStmtOffs != BAD_IL_OFFSET && opts.compDbgCode) + if (impCurStmtDI.GetLocation().IsValid() && opts.compDbgCode) { GenTree* placeHolder = new (this, GT_NO_OP) GenTree(GT_NO_OP, TYP_VOID); - impAppendTree(placeHolder, (unsigned)CHECK_SPILL_NONE, impCurStmtOffs); + impAppendTree(placeHolder, (unsigned)CHECK_SPILL_NONE, impCurStmtDI); - assert(impCurStmtOffs == BAD_IL_OFFSET); + assert(!impCurStmtDI.GetLocation().IsValid()); } - if (impCurStmtOffs == BAD_IL_OFFSET) + if (!impCurStmtDI.GetLocation().IsValid()) { /* Make sure that nxtStmtIndex is in sync with opcodeOffs. If opcodeOffs has gone past nxtStmtIndex, catch up */ @@ -11776,7 +11776,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) { /* At stack-empty locations, we have already added the tree to the stmt list with the last offset. We just need to update - impCurStmtOffs + impCurStmtDI */ impCurStmtOffsSet(opcodeOffs); @@ -11807,8 +11807,8 @@ void Compiler::impImportBlockCode(BasicBlock* block) impCurStmtOffsSet(opcodeOffs); } - assert(impCurStmtOffs == BAD_IL_OFFSET || nxtStmtOffs == BAD_IL_OFFSET || - jitGetILoffs(impCurStmtOffs) <= nxtStmtOffs); + assert(!impCurStmtDI.GetLocation().IsValid() || nxtStmtOffs == BAD_IL_OFFSET || + impCurStmtDI.GetLocation().GetOffset() <= nxtStmtOffs); } } @@ -11937,14 +11937,14 @@ void Compiler::impImportBlockCode(BasicBlock* block) } /* Append 'op1' to the list of statements */ - impAppendTree(op1, (unsigned)CHECK_SPILL_ALL, impCurStmtOffs); + impAppendTree(op1, (unsigned)CHECK_SPILL_ALL, impCurStmtDI); goto DONE_APPEND; APPEND: /* Append 'op1' to the list of statements */ - impAppendTree(op1, (unsigned)CHECK_SPILL_NONE, impCurStmtOffs); + impAppendTree(op1, (unsigned)CHECK_SPILL_NONE, impCurStmtDI); goto DONE_APPEND; DONE_APPEND: @@ -13464,7 +13464,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) /* GT_JTRUE is handled specially for non-empty stacks. See 'addStmt' in impImportBlock(block). For correct line numbers, spill stack. */ - if (opts.compDbgCode && impCurStmtOffs != BAD_IL_OFFSET) + if (opts.compDbgCode && impCurStmtDI.GetLocation().IsValid()) { impSpillStackEnsure(true); } @@ -13634,13 +13634,13 @@ void Compiler::impImportBlockCode(BasicBlock* block) { impSpillSideEffects(false, (unsigned)CHECK_SPILL_ALL DEBUGARG( "Branch to next Optimization, op1 side effect")); - impAppendTree(gtUnusedValNode(op1), (unsigned)CHECK_SPILL_NONE, impCurStmtOffs); + impAppendTree(gtUnusedValNode(op1), (unsigned)CHECK_SPILL_NONE, impCurStmtDI); } if (op2->gtFlags & GTF_GLOB_EFFECT) { impSpillSideEffects(false, (unsigned)CHECK_SPILL_ALL DEBUGARG( "Branch to next Optimization, op2 side effect")); - impAppendTree(gtUnusedValNode(op2), (unsigned)CHECK_SPILL_NONE, impCurStmtOffs); + impAppendTree(gtUnusedValNode(op2), (unsigned)CHECK_SPILL_NONE, impCurStmtDI); } #ifdef DEBUG @@ -14525,7 +14525,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) if (op1->gtFlags & GTF_SIDE_EFFECT) { op1 = gtUnusedValNode(op1); - impAppendTree(op1, (unsigned)CHECK_SPILL_ALL, impCurStmtOffs); + impAppendTree(op1, (unsigned)CHECK_SPILL_ALL, impCurStmtDI); } goto DO_LDFTN; } @@ -14535,7 +14535,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) if (op1->gtFlags & GTF_SIDE_EFFECT) { op1 = gtUnusedValNode(op1); - impAppendTree(op1, (unsigned)CHECK_SPILL_ALL, impCurStmtOffs); + impAppendTree(op1, (unsigned)CHECK_SPILL_ALL, impCurStmtDI); } goto DO_LDFTN; } @@ -14787,7 +14787,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) gtNewIconNode(0), // Value false, // isVolatile false); // not copyBlock - impAppendTree(newObjThisPtr, (unsigned)CHECK_SPILL_NONE, impCurStmtOffs); + impAppendTree(newObjThisPtr, (unsigned)CHECK_SPILL_NONE, impCurStmtDI); } else { @@ -15192,7 +15192,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) if (obj->gtFlags & GTF_SIDE_EFFECT) { obj = gtUnusedValNode(obj); - impAppendTree(obj, (unsigned)CHECK_SPILL_ALL, impCurStmtOffs); + impAppendTree(obj, (unsigned)CHECK_SPILL_ALL, impCurStmtDI); } obj = nullptr; } @@ -15524,7 +15524,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) if (obj->gtFlags & GTF_SIDE_EFFECT) { obj = gtUnusedValNode(obj); - impAppendTree(obj, (unsigned)CHECK_SPILL_ALL, impCurStmtOffs); + impAppendTree(obj, (unsigned)CHECK_SPILL_ALL, impCurStmtDI); } obj = nullptr; } @@ -16128,7 +16128,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) // The pointer may have side-effects if (op1->AsOp()->gtOp1->gtFlags & GTF_SIDE_EFFECT) { - impAppendTree(op1->AsOp()->gtOp1, (unsigned)CHECK_SPILL_ALL, impCurStmtOffs); + impAppendTree(op1->AsOp()->gtOp1, (unsigned)CHECK_SPILL_ALL, impCurStmtDI); #ifdef DEBUG impNoteLastILoffs(); #endif @@ -16356,7 +16356,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) // may be other trees on the evaluation stack that side-effect the // sources of the UNBOX operation we must spill the stack. - impAppendTree(op1, (unsigned)CHECK_SPILL_ALL, impCurStmtOffs); + impAppendTree(op1, (unsigned)CHECK_SPILL_ALL, impCurStmtDI); // Create the address-expression to reference past the object header // to the beginning of the value-type. Today this means adjusting @@ -17725,10 +17725,10 @@ bool Compiler::impReturnInstruction(int prefixFlags, OPCODE& opcode) else if (info.compRetBuffArg != BAD_VAR_NUM) { // Assign value to return buff (first param) - GenTree* retBuffAddr = gtNewLclvNode(info.compRetBuffArg, TYP_BYREF DEBUGARG(impCurStmtOffs)); + GenTree* retBuffAddr = gtNewLclvNode(info.compRetBuffArg, TYP_BYREF DEBUGARG(impCurStmtDI.GetLocation().GetOffset())); op2 = impAssignStructPtr(retBuffAddr, op2, retClsHnd, (unsigned)CHECK_SPILL_ALL); - impAppendTree(op2, (unsigned)CHECK_SPILL_NONE, impCurStmtOffs); + impAppendTree(op2, (unsigned)CHECK_SPILL_NONE, impCurStmtDI); // There are cases where the address of the implicit RetBuf should be returned explicitly (in RAX). CLANG_FORMAT_COMMENT_ANCHOR; @@ -17800,7 +17800,7 @@ bool Compiler::impReturnInstruction(int prefixFlags, OPCODE& opcode) } } - impAppendTree(op1, (unsigned)CHECK_SPILL_NONE, impCurStmtOffs); + impAppendTree(op1, (unsigned)CHECK_SPILL_NONE, impCurStmtDI); #ifdef DEBUG // Remember at which BC offset the tree was finished impNoteLastILoffs(); @@ -21233,7 +21233,7 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call, CORINFO_CONTEXT_HANDLE* pExactContextHandle, bool isLateDevirtualization, bool isExplicitTailCall, - IL_OFFSETX ilOffset) + IL_OFFSET ilOffset) { assert(call != nullptr); assert(method != nullptr); @@ -21263,7 +21263,7 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call, // Record some info needed for the class profiling probe. // - pInfo->ilOffset = ilOffset; + pInfo->ilOffset = ilOffset; pInfo->probeIndex = info.compClassProbeCount++; pInfo->stubAddr = call->gtStubCallStubAddr; @@ -22123,7 +22123,7 @@ void Compiler::addFatPointerCandidate(GenTreeCall* call) // Arguments: // // call - potential guarded devirtualization candidate -// ilOffset - IL offset of the call instruction +// ilOffset - IL ofset of the call instruction // isInterface - true if this is an interface call // baseMethod - target method of the call // baseClass - class that introduced the target method @@ -22137,7 +22137,7 @@ void Compiler::addFatPointerCandidate(GenTreeCall* call) // void Compiler::considerGuardedDevirtualization( GenTreeCall* call, - IL_OFFSETX ilOffset, + IL_OFFSET ilOffset, bool isInterface, CORINFO_METHOD_HANDLE baseMethod, CORINFO_CLASS_HANDLE baseClass, diff --git a/src/coreclr/jit/indirectcalltransformer.cpp b/src/coreclr/jit/indirectcalltransformer.cpp index feb7418fbb869..c3ce3151192d5 100644 --- a/src/coreclr/jit/indirectcalltransformer.cpp +++ b/src/coreclr/jit/indirectcalltransformer.cpp @@ -364,7 +364,7 @@ class IndirectCallTransformer GenTree* zero = new (compiler, GT_CNS_INT) GenTreeIntCon(TYP_I_IMPL, 0); GenTree* fatPointerCmp = compiler->gtNewOperNode(GT_NE, TYP_INT, fatPointerAnd, zero); GenTree* jmpTree = compiler->gtNewOperNode(GT_JTRUE, TYP_VOID, fatPointerCmp); - Statement* jmpStmt = compiler->fgNewStmtFromTree(jmpTree, stmt->GetILOffsetX()); + Statement* jmpStmt = compiler->fgNewStmtFromTree(jmpTree, stmt->GetDebugInfo()); compiler->fgInsertStmtAtEnd(checkBlock, jmpStmt); } @@ -595,7 +595,7 @@ class IndirectCallTransformer const unsigned thisTempNum = compiler->lvaGrabTemp(true DEBUGARG("guarded devirt this temp")); // lvaSetClass(thisTempNum, ...); GenTree* asgTree = compiler->gtNewTempAssign(thisTempNum, thisTree); - Statement* asgStmt = compiler->fgNewStmtFromTree(asgTree, stmt->GetILOffsetX()); + Statement* asgStmt = compiler->fgNewStmtFromTree(asgTree, stmt->GetDebugInfo()); compiler->fgInsertStmtAtEnd(checkBlock, asgStmt); thisTree = compiler->gtNewLclvNode(thisTempNum, TYP_REF); @@ -624,7 +624,7 @@ class IndirectCallTransformer // GenTree* methodTableCompare = compiler->gtNewOperNode(GT_NE, TYP_INT, targetMethodTable, methodTable); GenTree* jmpTree = compiler->gtNewOperNode(GT_JTRUE, TYP_VOID, methodTableCompare); - Statement* jmpStmt = compiler->fgNewStmtFromTree(jmpTree, stmt->GetILOffsetX()); + Statement* jmpStmt = compiler->fgNewStmtFromTree(jmpTree, stmt->GetDebugInfo()); compiler->fgInsertStmtAtEnd(checkBlock, jmpStmt); } @@ -1170,13 +1170,13 @@ class IndirectCallTransformer assert(sizeCheck->OperIs(GT_LE)); GenTree* sizeJmpTree = compiler->gtNewOperNode(GT_JTRUE, TYP_VOID, sizeCheck); - Statement* sizeJmpStmt = compiler->fgNewStmtFromTree(sizeJmpTree, stmt->GetILOffsetX()); + Statement* sizeJmpStmt = compiler->fgNewStmtFromTree(sizeJmpTree, stmt->GetDebugInfo()); compiler->fgInsertStmtAtEnd(checkBlock, sizeJmpStmt); checkBlock2 = CreateAndInsertBasicBlock(BBJ_COND, checkBlock); assert(nullCheck->OperIs(GT_EQ)); GenTree* nullJmpTree = compiler->gtNewOperNode(GT_JTRUE, TYP_VOID, nullCheck); - Statement* nullJmpStmt = compiler->fgNewStmtFromTree(nullJmpTree, stmt->GetILOffsetX()); + Statement* nullJmpStmt = compiler->fgNewStmtFromTree(nullJmpTree, stmt->GetDebugInfo()); compiler->fgInsertStmtAtEnd(checkBlock2, nullJmpStmt); } @@ -1195,7 +1195,7 @@ class IndirectCallTransformer origCall->gtCallArgs = argsIter.GetUse(); GenTree* asg = compiler->gtNewTempAssign(resultLclNum, resultHandle); - Statement* asgStmt = compiler->gtNewStmt(asg, stmt->GetILOffsetX()); + Statement* asgStmt = compiler->gtNewStmt(asg, stmt->GetDebugInfo()); compiler->fgInsertStmtAtEnd(thenBlock, asgStmt); } @@ -1206,7 +1206,7 @@ class IndirectCallTransformer { elseBlock = CreateAndInsertBasicBlock(BBJ_NONE, thenBlock); GenTree* asg = compiler->gtNewTempAssign(resultLclNum, origCall); - Statement* asgStmt = compiler->gtNewStmt(asg, stmt->GetILOffsetX()); + Statement* asgStmt = compiler->gtNewStmt(asg, stmt->GetDebugInfo()); compiler->fgInsertStmtAtEnd(elseBlock, asgStmt); } diff --git a/src/coreclr/jit/inline.cpp b/src/coreclr/jit/inline.cpp index fea132190a56b..bdfa52d43bee4 100644 --- a/src/coreclr/jit/inline.cpp +++ b/src/coreclr/jit/inline.cpp @@ -332,7 +332,6 @@ InlineContext::InlineContext(InlineStrategy* strategy) , m_Code(nullptr) , m_ILSize(0) , m_ImportedILSize(0) - , m_Offset(BAD_IL_OFFSET) , m_Observation(InlineObservation::CALLEE_UNUSED_INITIAL) , m_CodeSizeEstimate(0) , m_Success(true) @@ -403,14 +402,14 @@ void InlineContext::Dump(unsigned indent) const char* guarded = m_Guarded ? " guarded" : ""; const char* unboxed = m_Unboxed ? " unboxed" : ""; - if (m_Offset == BAD_IL_OFFSET) + if (!m_Location.IsValid()) { printf("%*s[%u IL=???? TR=%06u %08X] [%s%s: %s%s%s%s] %s\n", indent, "", m_Ordinal, m_TreeID, calleeToken, inlineResult, inlineTarget, inlineReason, guarded, devirtualized, unboxed, calleeName); } else { - IL_OFFSET offset = jitGetILoffs(m_Offset); + IL_OFFSET offset = m_Location.GetOffset(); printf("%*s[%u IL=%04d TR=%06u %08X] [%s%s: %s%s%s%s] %s\n", indent, "", m_Ordinal, offset, m_TreeID, calleeToken, inlineResult, inlineTarget, inlineReason, guarded, devirtualized, unboxed, calleeName); } @@ -537,11 +536,7 @@ void InlineContext::DumpXml(FILE* file, unsigned indent) buf[sizeof(buf) - 1] = 0; EscapeNameForXml(buf); - int offset = -1; - if (m_Offset != BAD_IL_OFFSET) - { - offset = (int)jitGetILoffs(m_Offset); - } + int offset = m_Location.IsValid() ? m_Location.GetOffset() : -1; fprintf(file, "%*s<%s>\n", indent, "", inlineType); fprintf(file, "%*s%08x\n", indent + 2, "", calleeToken); @@ -636,13 +631,13 @@ InlineResult::InlineResult(Compiler* compiler, GenTreeCall* call, Statement* stm // Pass along some optional information to the policy. if (stmt != nullptr) { - m_InlineContext = stmt->GetInlineContext(); + m_InlineContext = stmt->GetDebugInfo().GetInlineContext(); m_Policy->NoteContext(m_InlineContext); #if defined(DEBUG) || defined(INLINE_DATA) m_Policy->NoteOffset(call->gtRawILOffset); #else - m_Policy->NoteOffset(stmt->GetILOffsetX()); + m_Policy->NoteOffset(stmt->GetDebugInfo().GetLocation().GetOffset()); #endif // defined(DEBUG) || defined(INLINE_DATA) } @@ -1254,7 +1249,7 @@ InlineContext* InlineStrategy::NewSuccess(InlineInfo* inlineInfo) Statement* stmt = inlineInfo->iciStmt; BYTE* calleeIL = inlineInfo->inlineCandidateInfo->methInfo.ILCode; unsigned calleeILSize = inlineInfo->inlineCandidateInfo->methInfo.ILCodeSize; - InlineContext* parentContext = stmt->GetInlineContext(); + InlineContext* parentContext = stmt->GetDebugInfo().GetInlineContext(); GenTreeCall* originalCall = inlineInfo->inlineResult->GetCall(); noway_assert(parentContext != nullptr); @@ -1267,7 +1262,7 @@ InlineContext* InlineStrategy::NewSuccess(InlineInfo* inlineInfo) calleeContext->m_Sibling = parentContext->m_Child; parentContext->m_Child = calleeContext; calleeContext->m_Child = nullptr; - calleeContext->m_Offset = stmt->GetILOffsetX(); + calleeContext->m_Location = stmt->GetDebugInfo().GetLocation(); calleeContext->m_Observation = inlineInfo->inlineResult->GetObservation(); calleeContext->m_Success = true; calleeContext->m_Devirtualized = originalCall->IsDevirtualized(); @@ -1285,7 +1280,7 @@ InlineContext* InlineStrategy::NewSuccess(InlineInfo* inlineInfo) // +1 here since we set this before calling NoteOutcome. calleeContext->m_Ordinal = m_InlineCount + 1; // Update offset with more accurate info - calleeContext->m_Offset = originalCall->gtRawILOffset; + calleeContext->m_Location = ILLocation(originalCall->gtRawILOffset, false, false); #endif // defined(DEBUG) || defined(INLINE_DATA) @@ -1317,7 +1312,7 @@ InlineContext* InlineStrategy::NewFailure(Statement* stmt, InlineResult* inlineR { // Check for a parent context first. We should now have a parent // context for all statements. - InlineContext* parentContext = stmt->GetInlineContext(); + InlineContext* parentContext = stmt->GetDebugInfo().GetInlineContext(); assert(parentContext != nullptr); InlineContext* failedContext = new (m_Compiler, CMK_Inlining) InlineContext(this); GenTreeCall* originalCall = inlineResult->GetCall(); @@ -1329,7 +1324,7 @@ InlineContext* InlineStrategy::NewFailure(Statement* stmt, InlineResult* inlineR failedContext->m_Sibling = parentContext->m_Child; parentContext->m_Child = failedContext; failedContext->m_Child = nullptr; - failedContext->m_Offset = stmt->GetILOffsetX(); + failedContext->m_Location = stmt->GetDebugInfo().GetLocation(); failedContext->m_Observation = inlineResult->GetObservation(); failedContext->m_Callee = inlineResult->GetCallee(); failedContext->m_Success = false; @@ -1342,7 +1337,7 @@ InlineContext* InlineStrategy::NewFailure(Statement* stmt, InlineResult* inlineR #if defined(DEBUG) || defined(INLINE_DATA) // Update offset with more accurate info - failedContext->m_Offset = originalCall->gtRawILOffset; + failedContext->m_Location = ILLocation(originalCall->gtRawILOffset, false, false); #endif // #if defined(DEBUG) || defined(INLINE_DATA) diff --git a/src/coreclr/jit/inline.h b/src/coreclr/jit/inline.h index 6a39b2d9cb5d0..a140f2f39fd98 100644 --- a/src/coreclr/jit/inline.h +++ b/src/coreclr/jit/inline.h @@ -233,7 +233,7 @@ class InlinePolicy { (void)context; } - virtual void NoteOffset(IL_OFFSETX offset) + virtual void NoteOffset(IL_OFFSET offset) { (void)offset; } @@ -747,10 +747,10 @@ class InlineContext return m_CodeSizeEstimate; } - // Get the offset of the call site - IL_OFFSETX GetOffset() const + // Get the loation of the call site within the parent + ILLocation GetLocation() const { - return m_Offset; + return m_Location; } // True if this is the root context @@ -790,7 +790,7 @@ class InlineContext const BYTE* m_Code; // address of IL buffer for the method unsigned m_ILSize; // size of IL buffer for the method unsigned m_ImportedILSize; // estimated size of imported IL - IL_OFFSETX m_Offset; // call site location within parent + ILLocation m_Location; // call site location within parent InlineObservation m_Observation; // what lead to this inline int m_CodeSizeEstimate; // in bytes * 10 bool m_Success : 1; // true if this was a successful inline diff --git a/src/coreclr/jit/inlinepolicy.cpp b/src/coreclr/jit/inlinepolicy.cpp index c69453b3565fb..1ec47378872d5 100644 --- a/src/coreclr/jit/inlinepolicy.cpp +++ b/src/coreclr/jit/inlinepolicy.cpp @@ -3383,7 +3383,7 @@ bool ReplayPolicy::FindContext(InlineContext* context) // Token and Hash we're looking for. mdMethodDef contextToken = m_RootCompiler->info.compCompHnd->getMethodDefFromMethod(context->GetCallee()); unsigned contextHash = m_RootCompiler->compMethodHash(context->GetCallee()); - unsigned contextOffset = (unsigned)context->GetOffset(); + unsigned contextOffset = (unsigned)context->GetLocation().GetOffset(); return FindInline(contextToken, contextHash, contextOffset); } @@ -3573,7 +3573,7 @@ bool ReplayPolicy::FindInline(CORINFO_METHOD_HANDLE callee) int offset = -1; if (m_Offset != BAD_IL_OFFSET) { - offset = (int)jitGetILoffs(m_Offset); + offset = m_Offset; } unsigned calleeOffset = (unsigned)offset; diff --git a/src/coreclr/jit/inlinepolicy.h b/src/coreclr/jit/inlinepolicy.h index e604fd57a36ed..f5f200e9f5bd7 100644 --- a/src/coreclr/jit/inlinepolicy.h +++ b/src/coreclr/jit/inlinepolicy.h @@ -516,7 +516,7 @@ class ReplayPolicy : public DiscretionaryPolicy m_InlineContext = context; } - void NoteOffset(IL_OFFSETX offset) override + void NoteOffset(IL_OFFSET offset) override { m_Offset = offset; } @@ -542,7 +542,7 @@ class ReplayPolicy : public DiscretionaryPolicy static FILE* s_ReplayFile; static CritSecObject s_XmlReaderLock; InlineContext* m_InlineContext; - IL_OFFSETX m_Offset; + IL_OFFSET m_Offset; bool m_WasForceInline; }; diff --git a/src/coreclr/jit/jit.h b/src/coreclr/jit/jit.h index c84bb0b529e4a..d8cb5cabfb065 100644 --- a/src/coreclr/jit/jit.h +++ b/src/coreclr/jit/jit.h @@ -288,41 +288,9 @@ const CORINFO_CLASS_HANDLE NO_CLASS_HANDLE = (CORINFO_CLASS_HANDLE) nullptr; /*****************************************************************************/ -// We define two IL offset types, as follows: -// -// IL_OFFSET: either a distinguished value, or an IL offset. -// IL_OFFSETX: either a distinguished value, or the top two bits are a flags, and the remaining bottom -// bits are a IL offset. -// -// In both cases, the set of legal distinguished values is: -// BAD_IL_OFFSET -- A unique illegal IL offset number. Note that it must be different from -// the ICorDebugInfo values, below, and must also not be a legal IL offset. -// ICorDebugInfo::NO_MAPPING -- The IL offset corresponds to no source code (such as EH step blocks). -// ICorDebugInfo::PROLOG -- The IL offset indicates a prolog -// ICorDebugInfo::EPILOG -- The IL offset indicates an epilog -// -// The IL offset must be in the range [0 .. 0x3fffffff]. This is because we steal -// the top two bits in IL_OFFSETX for flags, but we want the maximum range to be the same -// for both types. The IL value can't be larger than the maximum IL offset of the function -// being compiled. -// -// Blocks and statements never store one of the ICorDebugInfo values, even for IL_OFFSETX types. These are -// only stored in the IPmappingDsc struct, ipmdILoffsx field. - typedef unsigned IL_OFFSET; -const IL_OFFSET BAD_IL_OFFSET = 0x80000000; -const IL_OFFSET MAX_IL_OFFSET = 0x3fffffff; - -typedef unsigned IL_OFFSETX; // IL_OFFSET with stack-empty or call-instruction bit -const IL_OFFSETX IL_OFFSETX_STKBIT = 0x80000000; // Note: this bit is set when the stack is NOT empty! -const IL_OFFSETX IL_OFFSETX_CALLINSTRUCTIONBIT = 0x40000000; // Set when the IL offset is for a call instruction. -const IL_OFFSETX IL_OFFSETX_BITS = IL_OFFSETX_STKBIT | IL_OFFSETX_CALLINSTRUCTIONBIT; - -IL_OFFSET jitGetILoffs(IL_OFFSETX offsx); -IL_OFFSET jitGetILoffsAny(IL_OFFSETX offsx); -bool jitIsStackEmpty(IL_OFFSETX offsx); -bool jitIsCallInstruction(IL_OFFSETX offsx); +const IL_OFFSET BAD_IL_OFFSET = 0xffffffff; const unsigned BAD_VAR_NUM = UINT_MAX; diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 83946626f25d9..07426bda1ff13 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -8066,7 +8066,7 @@ GenTree* Compiler::fgCreateCallDispatcherAndGetResult(GenTreeCall* orig CORINFO_METHOD_HANDLE dispatcherHnd) { GenTreeCall* callDispatcherNode = - gtNewCallNode(CT_USER_FUNC, dispatcherHnd, TYP_VOID, nullptr, fgMorphStmt->GetILOffsetX()); + gtNewCallNode(CT_USER_FUNC, dispatcherHnd, TYP_VOID, nullptr, fgMorphStmt->GetDebugInfo()); // The dispatcher has signature // void DispatchTailCalls(void* callersRetAddrSlot, void* callTarget, void* retValue) @@ -8680,13 +8680,13 @@ void Compiler::fgMorphRecursiveFastTailCallIntoLoop(BasicBlock* block, GenTreeCa // Transform recursive tail call into a loop. Statement* earlyArgInsertionPoint = lastStmt; - IL_OFFSETX callILOffset = lastStmt->GetILOffsetX(); + const DebugInfo& callDI = lastStmt->GetDebugInfo(); // Hoist arg setup statement for the 'this' argument. GenTreeCall::Use* thisArg = recursiveTailCall->gtCallThisArg; if ((thisArg != nullptr) && !thisArg->GetNode()->IsNothingNode() && !thisArg->GetNode()->IsArgPlaceHolderNode()) { - Statement* thisArgStmt = gtNewStmt(thisArg->GetNode(), callILOffset); + Statement* thisArgStmt = gtNewStmt(thisArg->GetNode(), callDI); fgInsertStmtBefore(block, earlyArgInsertionPoint, thisArgStmt); } @@ -8744,7 +8744,7 @@ void Compiler::fgMorphRecursiveFastTailCallIntoLoop(BasicBlock* block, GenTreeCa if ((earlyArg->gtFlags & GTF_LATE_ARG) != 0) { // This is a setup node so we need to hoist it. - Statement* earlyArgStmt = gtNewStmt(earlyArg, callILOffset); + Statement* earlyArgStmt = gtNewStmt(earlyArg, callDI); fgInsertStmtBefore(block, earlyArgInsertionPoint, earlyArgStmt); } else @@ -8758,7 +8758,7 @@ void Compiler::fgMorphRecursiveFastTailCallIntoLoop(BasicBlock* block, GenTreeCa fgAssignRecursiveCallArgToCallerParam(earlyArg, curArgTabEntry, fgGetArgTabEntryParameterLclNum(recursiveTailCall, curArgTabEntry), - block, callILOffset, tmpAssignmentInsertionPoint, + block, callDI, tmpAssignmentInsertionPoint, paramAssignmentInsertionPoint); if ((tmpAssignmentInsertionPoint == lastStmt) && (paramAssignStmt != nullptr)) { @@ -8785,7 +8785,7 @@ void Compiler::fgMorphRecursiveFastTailCallIntoLoop(BasicBlock* block, GenTreeCa fgAssignRecursiveCallArgToCallerParam(lateArg, curArgTabEntry, fgGetArgTabEntryParameterLclNum(recursiveTailCall, curArgTabEntry), - block, callILOffset, tmpAssignmentInsertionPoint, + block, callDI, tmpAssignmentInsertionPoint, paramAssignmentInsertionPoint); if ((tmpAssignmentInsertionPoint == lastStmt) && (paramAssignStmt != nullptr)) @@ -8805,7 +8805,7 @@ void Compiler::fgMorphRecursiveFastTailCallIntoLoop(BasicBlock* block, GenTreeCa var_types thisType = lvaTable[info.compThisArg].TypeGet(); GenTree* arg0 = gtNewLclvNode(lvaArg0Var, thisType); GenTree* arg0Assignment = gtNewAssignNode(arg0, gtNewLclvNode(info.compThisArg, thisType)); - Statement* arg0AssignmentStmt = gtNewStmt(arg0Assignment, callILOffset); + Statement* arg0AssignmentStmt = gtNewStmt(arg0Assignment, callDI); fgInsertStmtBefore(block, paramAssignmentInsertionPoint, arg0AssignmentStmt); } @@ -8847,7 +8847,7 @@ void Compiler::fgMorphRecursiveFastTailCallIntoLoop(BasicBlock* block, GenTreeCa GenTree* zero = gtNewZeroConNode(genActualType(lclType)); init = gtNewAssignNode(lcl, zero); } - Statement* initStmt = gtNewStmt(init, callILOffset); + Statement* initStmt = gtNewStmt(init, callDI); fgInsertStmtBefore(block, lastStmt, initStmt); } } @@ -8901,7 +8901,7 @@ Statement* Compiler::fgAssignRecursiveCallArgToCallerParam(GenTree* arg, fgArgTabEntry* argTabEntry, unsigned lclParamNum, BasicBlock* block, - IL_OFFSETX callILOffset, + const DebugInfo& callDI, Statement* tmpAssignmentInsertionPoint, Statement* paramAssignmentInsertionPoint) { @@ -8953,7 +8953,7 @@ Statement* Compiler::fgAssignRecursiveCallArgToCallerParam(GenTree* arg, GenTree* tempSrc = arg; GenTree* tempDest = gtNewLclvNode(tmpNum, tempSrc->gtType); GenTree* tmpAssignNode = gtNewAssignNode(tempDest, tempSrc); - Statement* tmpAssignStmt = gtNewStmt(tmpAssignNode, callILOffset); + Statement* tmpAssignStmt = gtNewStmt(tmpAssignNode, callDI); fgInsertStmtBefore(block, tmpAssignmentInsertionPoint, tmpAssignStmt); argInTemp = gtNewLclvNode(tmpNum, tempSrc->gtType); } @@ -8963,7 +8963,7 @@ Statement* Compiler::fgAssignRecursiveCallArgToCallerParam(GenTree* arg, assert(paramDsc->lvIsParam); GenTree* paramDest = gtNewLclvNode(lclParamNum, paramDsc->lvType); GenTree* paramAssignNode = gtNewAssignNode(paramDest, argInTemp); - paramAssignStmt = gtNewStmt(paramAssignNode, callILOffset); + paramAssignStmt = gtNewStmt(paramAssignNode, callDI); fgInsertStmtBefore(block, paramAssignmentInsertionPoint, paramAssignStmt); } @@ -9015,7 +9015,7 @@ GenTree* Compiler::fgMorphCall(GenTreeCall* call) assg = fgMorphTree(assg); // Create the assignment statement and insert it before the current statement. - Statement* assgStmt = gtNewStmt(assg, compCurStmt->GetILOffsetX()); + Statement* assgStmt = gtNewStmt(assg, compCurStmt->GetDebugInfo()); fgInsertStmtBefore(compCurBB, compCurStmt, assgStmt); // Return the temp. @@ -15979,8 +15979,8 @@ void Compiler::fgMergeBlockReturn(BasicBlock* block) noway_assert(ret->gtGetOp1() != nullptr); Statement* pAfterStatement = lastStmt; - IL_OFFSETX offset = lastStmt->GetILOffsetX(); - GenTree* tree = gtNewTempAssign(genReturnLocal, ret->gtGetOp1(), &pAfterStatement, offset, block); + const DebugInfo& di = lastStmt->GetDebugInfo(); + GenTree* tree = gtNewTempAssign(genReturnLocal, ret->gtGetOp1(), &pAfterStatement, di, block); if (tree->OperIsCopyBlkOp()) { tree = fgMorphCopyBlock(tree); @@ -15998,7 +15998,7 @@ void Compiler::fgMergeBlockReturn(BasicBlock* block) { // gtNewTempAssign inserted additional statements after last fgRemoveStmt(block, lastStmt); - Statement* newStmt = gtNewStmt(tree, offset); + Statement* newStmt = gtNewStmt(tree, di); fgInsertStmtAfter(block, pAfterStatement, newStmt); lastStmt = newStmt; } @@ -16456,23 +16456,23 @@ void Compiler::fgExpandQmarkForCastInstOf(BasicBlock* block, Statement* stmt) // Append cond1 as JTRUE to cond1Block GenTree* jmpTree = gtNewOperNode(GT_JTRUE, TYP_VOID, condExpr); - Statement* jmpStmt = fgNewStmtFromTree(jmpTree, stmt->GetILOffsetX()); + Statement* jmpStmt = fgNewStmtFromTree(jmpTree, stmt->GetDebugInfo()); fgInsertStmtAtEnd(cond1Block, jmpStmt); // Append cond2 as JTRUE to cond2Block jmpTree = gtNewOperNode(GT_JTRUE, TYP_VOID, cond2Expr); - jmpStmt = fgNewStmtFromTree(jmpTree, stmt->GetILOffsetX()); + jmpStmt = fgNewStmtFromTree(jmpTree, stmt->GetDebugInfo()); fgInsertStmtAtEnd(cond2Block, jmpStmt); // AsgBlock should get tmp = op1 assignment. trueExpr = gtNewTempAssign(dst->AsLclVarCommon()->GetLclNum(), trueExpr); - Statement* trueStmt = fgNewStmtFromTree(trueExpr, stmt->GetILOffsetX()); + Statement* trueStmt = fgNewStmtFromTree(trueExpr, stmt->GetDebugInfo()); fgInsertStmtAtEnd(asgBlock, trueStmt); // Since we are adding helper in the JTRUE false path, reverse the cond2 and add the helper. gtReverseCond(cond2Expr); GenTree* helperExpr = gtNewTempAssign(dst->AsLclVarCommon()->GetLclNum(), true2Expr); - Statement* helperStmt = fgNewStmtFromTree(helperExpr, stmt->GetILOffsetX()); + Statement* helperStmt = fgNewStmtFromTree(helperExpr, stmt->GetDebugInfo()); fgInsertStmtAtEnd(helperBlock, helperStmt); // Finally remove the nested qmark stmt. @@ -16674,7 +16674,7 @@ void Compiler::fgExpandQmarkStmt(BasicBlock* block, Statement* stmt) } GenTree* jmpTree = gtNewOperNode(GT_JTRUE, TYP_VOID, qmark->gtGetOp1()); - Statement* jmpStmt = fgNewStmtFromTree(jmpTree, stmt->GetILOffsetX()); + Statement* jmpStmt = fgNewStmtFromTree(jmpTree, stmt->GetDebugInfo()); fgInsertStmtAtEnd(condBlock, jmpStmt); // Remove the original qmark statement. @@ -16700,7 +16700,7 @@ void Compiler::fgExpandQmarkStmt(BasicBlock* block, Statement* stmt) { trueExpr = gtNewTempAssign(lclNum, trueExpr); } - Statement* trueStmt = fgNewStmtFromTree(trueExpr, stmt->GetILOffsetX()); + Statement* trueStmt = fgNewStmtFromTree(trueExpr, stmt->GetDebugInfo()); fgInsertStmtAtEnd(thenBlock, trueStmt); } @@ -16711,7 +16711,7 @@ void Compiler::fgExpandQmarkStmt(BasicBlock* block, Statement* stmt) { falseExpr = gtNewTempAssign(lclNum, falseExpr); } - Statement* falseStmt = fgNewStmtFromTree(falseExpr, stmt->GetILOffsetX()); + Statement* falseStmt = fgNewStmtFromTree(falseExpr, stmt->GetDebugInfo()); fgInsertStmtAtEnd(elseBlock, falseStmt); } diff --git a/src/coreclr/jit/optimizer.cpp b/src/coreclr/jit/optimizer.cpp index 10f14b2b53cf7..b179dcc96ca55 100644 --- a/src/coreclr/jit/optimizer.cpp +++ b/src/coreclr/jit/optimizer.cpp @@ -4399,7 +4399,7 @@ bool Compiler::optInvertWhileLoop(BasicBlock* block) if (opts.compDbgInfo) { - clonedStmt->SetILOffsetX(stmt->GetILOffsetX()); + clonedStmt->SetDebugInfo(stmt->GetDebugInfo()); } clonedStmt->SetCompilerAdded(); diff --git a/src/coreclr/jit/rationalize.cpp b/src/coreclr/jit/rationalize.cpp index 9887153d54d47..08c29ae03b769 100644 --- a/src/coreclr/jit/rationalize.cpp +++ b/src/coreclr/jit/rationalize.cpp @@ -957,13 +957,12 @@ PhaseStatus Rationalizer::DoPhase() { BlockRange().InsertAtEnd(LIR::Range(statement->GetTreeList(), statement->GetRootNode())); - // If this statement has correct offset information, change it into an IL offset + // If this statement has correct debug information, change it into a debug info // node and insert it into the LIR. - if (statement->GetILOffsetX() != BAD_IL_OFFSET) + if (statement->GetDebugInfo().GetLocation().IsValid()) { - assert(!statement->IsPhiDefnStmt()); GenTreeILOffset* ilOffset = new (comp, GT_IL_OFFSET) - GenTreeILOffset(statement->GetILOffsetX() DEBUGARG(statement->GetLastILOffset())); + GenTreeILOffset(statement->GetDebugInfo() DEBUGARG(statement->GetLastILOffset())); BlockRange().InsertBefore(statement->GetTreeList(), ilOffset); } From 86265794c26ed410705ab47c493cfb67b455adee Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Thu, 4 Nov 2021 16:02:01 +0100 Subject: [PATCH 2/6] Track debug information in statements from inlinees Add support for tracking debug information in statements coming from inlinees. Changes: * Turn on compDbgInfo in inlinees. We use the implicit boundaries from the inline root, but we do not use any explicit boundaries. That is, we do not query the EE for explicit boundaries for the inlinee. * Create InlineContexts eagerly and use them during import. All DebugInfo created in the JIT is in a "consistent" state, meaning that we never see an IL location set without a corresponding inline context. This was difficult before as InlineContexts would be created quite late, after the importer for the inlinee had run. We now create it eagerly and attach it to debug info during importation. Later, when we figure out whether an inline succeeded or not, we mark it as succeeded or failed. * Stop carrying InlineContext around unconditionally in Statement. The inline context is now only part of the debug info, which may not be set. Inlining needs the inline context to create new inline contexts and to check for recursive inlines. Previously it retrieved it from the inline statement, but due to the above change we now have to get it from somewhere else. To do this we now keep it unconditionally together with InlineCandidateInfo so that we can retrieve it later. * Validate all created debug information when associated with a statement. This is done by creating a bitvector containing IL locations that mark the beginning of IL instructions, and validating that all IL offsets point to these when Statement::SetDebugInfo is called. * While we track debug info in statements from inlinees, the runtime side is still not hooked up. Currently we track the information until we get to rationalize, where we normalize all debug info back to the root inserted as GT_IL_OFFSET nodes. The change is free of any diffs due to this normalization. We also track IL offsets as part of basic blocks: these are also normalized to be in the root. --- src/coreclr/jit/codegencommon.cpp | 8 +- src/coreclr/jit/compiler.cpp | 10 +- src/coreclr/jit/compiler.h | 29 +-- src/coreclr/jit/ee_il_dll.cpp | 12 +- src/coreclr/jit/emitxarch.cpp | 2 +- src/coreclr/jit/fgbasic.cpp | 19 +- src/coreclr/jit/fginline.cpp | 61 +++---- src/coreclr/jit/fgstmt.cpp | 19 +- src/coreclr/jit/gentree.cpp | 154 ++++++++++++++-- src/coreclr/jit/gentree.h | 50 ++++-- src/coreclr/jit/importer.cpp | 78 ++++---- src/coreclr/jit/indirectcalltransformer.cpp | 6 +- src/coreclr/jit/inline.cpp | 190 +++++++++----------- src/coreclr/jit/inline.h | 49 +++-- src/coreclr/jit/morph.cpp | 52 ++++-- src/coreclr/jit/rationalize.cpp | 12 +- 16 files changed, 463 insertions(+), 288 deletions(-) diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index e333cdb32f260..7c775203f75ee 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -10477,7 +10477,7 @@ void CodeGen::genIPmappingAdd(IPmappingDscKind kind, const DebugInfo& di, bool i return; } - assert((kind == IPmappingDscKind::Normal) == (di.GetLocation().IsValid())); + assert((kind == IPmappingDscKind::Normal) == di.IsValid()); switch (kind) { @@ -10512,7 +10512,7 @@ void CodeGen::genIPmappingAdd(IPmappingDscKind kind, const DebugInfo& di, bool i addMapping->ipmdIsLabel = isLabel; addMapping->ipmdNext = nullptr; - assert((kind == IPmappingDscKind::Normal) == (addMapping->ipmdLoc.IsValid())); + assert((kind == IPmappingDscKind::Normal) == addMapping->ipmdLoc.IsValid()); if (compiler->genIPmappingList != nullptr) { @@ -10549,7 +10549,7 @@ void CodeGen::genIPmappingAddToFront(IPmappingDscKind kind, const DebugInfo& di, return; } - noway_assert((kind != IPmappingDscKind::Normal) || (di.GetLocation().IsValid() && (di.GetLocation().GetOffset() <= compiler->info.compILCodeSize))); + noway_assert((kind != IPmappingDscKind::Normal) || (di.IsValid() && (di.GetLocation().GetOffset() <= compiler->info.compILCodeSize))); /* Create a mapping entry and prepend it to the list */ @@ -10585,7 +10585,7 @@ void CodeGen::genEnsureCodeEmitted(const DebugInfo& di) return; } - if (!di.GetLocation().IsValid()) + if (!di.IsValid()) { return; } diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index 7fa9179f53d2c..e6dd9a8e4f1df 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -2494,7 +2494,6 @@ void Compiler::compInitOptions(JitFlags* jitFlags) assert(!jitFlags->IsSet(JitFlags::JIT_FLAG_BBINSTR)); assert(!jitFlags->IsSet(JitFlags::JIT_FLAG_PROF_ENTERLEAVE)); assert(!jitFlags->IsSet(JitFlags::JIT_FLAG_DEBUG_EnC)); - assert(!jitFlags->IsSet(JitFlags::JIT_FLAG_DEBUG_INFO)); assert(!jitFlags->IsSet(JitFlags::JIT_FLAG_REVERSE_PINVOKE)); assert(!jitFlags->IsSet(JitFlags::JIT_FLAG_TRACK_TRANSITIONS)); } @@ -3619,8 +3618,6 @@ bool Compiler::compPromoteFewerStructs(unsigned lclNum) void Compiler::compInitDebuggingInfo() { - assert(!compIsForInlining()); - #ifdef DEBUG if (verbose) { @@ -6087,6 +6084,7 @@ int Compiler::compCompileHelper(CORINFO_MODULE_HANDLE classPtr, #endif info.compFlags = impInlineInfo->inlineCandidateInfo->methAttr; + compInlineContext = impInlineInfo->inlineContext; } else { @@ -6094,6 +6092,7 @@ int Compiler::compCompileHelper(CORINFO_MODULE_HANDLE classPtr, #ifdef PSEUDORANDOM_NOP_INSERTION info.compChecksum = getMethodBodyChecksum((char*)methodInfo->ILCode, methodInfo->ILCodeSize); #endif + compInlineContext = m_inlineStrategy->GetRootContext(); } compSwitchedToOptimized = false; @@ -6231,10 +6230,7 @@ int Compiler::compCompileHelper(CORINFO_MODULE_HANDLE classPtr, lvaInitTypeRef(); - if (!compIsForInlining()) - { - compInitDebuggingInfo(); - } + compInitDebuggingInfo(); #ifdef DEBUG if (compIsForInlining()) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 55183e5a24b13..091a29704315f 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -3601,6 +3601,7 @@ class Compiler void gtDispLclVar(unsigned lclNum, bool padForBiggestDisp = true); void gtDispLclVarStructType(unsigned lclNum); void gtDispClassLayout(ClassLayout* layout, var_types type); + void gtDispILLocation(const ILLocation& loc); void gtDispStmt(Statement* stmt, const char* msg = nullptr); void gtDispBlockStmts(BasicBlock* block); void gtGetArgMsg(GenTreeCall* call, GenTree* arg, unsigned argNum, char* bufp, unsigned bufLength); @@ -4176,9 +4177,11 @@ class Compiler unsigned lvaPSPSym; // variable representing the PSPSym #endif - InlineInfo* impInlineInfo; + InlineInfo* impInlineInfo; // Only present for inlinees InlineStrategy* m_inlineStrategy; + InlineContext* compInlineContext; // Always present + // The Compiler* that is the root of the inlining tree of which "this" is a member. Compiler* impInlineRoot(); @@ -4594,17 +4597,15 @@ class Compiler void impNoteLastILoffs(); #endif - /* Debug info of the stmt currently being imported. It gets set to empty information - (DebugInfo()) after it has been set in the appended trees. Then it gets updated at - IL instructions for which we have to report mapping info. - */ + // Debug info of current statement being imported. It gets set to contain + // no IL location (!impCurStmtDI.GetLocation().IsValid) after it has been + // set in the appended trees. Then it gets updated at IL instructions for + // which we have to report mapping info. + // It will always contain the current inline context. DebugInfo impCurStmtDI; + DebugInfo impCreateDIWithCurrentStackInfo(IL_OFFSET offs, bool isCall); void impCurStmtOffsSet(IL_OFFSET offs); - /* - * Get current debug info with stack info and call instruction info incoporated - */ - DebugInfo impCurDebugInfo(IL_OFFSET offs, bool callInstruction = false); void impNoteBranchOffs(); @@ -6162,10 +6163,10 @@ class Compiler #endif public: - Statement* fgNewStmtAtBeg(BasicBlock* block, GenTree* tree, IL_OFFSETX offs = BAD_IL_OFFSET); + Statement* fgNewStmtAtBeg(BasicBlock* block, GenTree* tree, const DebugInfo& di = DebugInfo()); void fgInsertStmtAtEnd(BasicBlock* block, Statement* stmt); - Statement* fgNewStmtAtEnd(BasicBlock* block, GenTree* tree, IL_OFFSETX offs = BAD_IL_OFFSET); - Statement* fgNewStmtNearEnd(BasicBlock* block, GenTree* tree, IL_OFFSETX offs = BAD_IL_OFFSET); + Statement* fgNewStmtAtEnd(BasicBlock* block, GenTree* tree, const DebugInfo& di = DebugInfo()); + Statement* fgNewStmtNearEnd(BasicBlock* block, GenTree* tree, const DebugInfo& di = DebugInfo()); private: void fgInsertStmtNearEnd(BasicBlock* block, Statement* stmt); @@ -6359,7 +6360,7 @@ class Compiler GenTree* fgMorphCall(GenTreeCall* call); GenTree* fgExpandVirtualVtableCallTarget(GenTreeCall* call); void fgMorphCallInline(GenTreeCall* call, InlineResult* result); - void fgMorphCallInlineHelper(GenTreeCall* call, InlineResult* result); + void fgMorphCallInlineHelper(GenTreeCall* call, InlineResult* result, InlineContext** createdContext); #if DEBUG void fgNoteNonInlineCandidate(Statement* stmt, GenTreeCall* call); static fgWalkPreFn fgFindNonInlineCandidate; @@ -6485,7 +6486,7 @@ class Compiler unsigned fgBigOffsetMorphingTemps[TYP_COUNT]; unsigned fgCheckInlineDepthAndRecursion(InlineInfo* inlineInfo); - void fgInvokeInlineeCompiler(GenTreeCall* call, InlineResult* result); + void fgInvokeInlineeCompiler(GenTreeCall* call, InlineResult* result, InlineContext** createdContext); void fgInsertInlineeBlocks(InlineInfo* pInlineInfo); Statement* fgInlinePrependStatements(InlineInfo* inlineInfo); void fgInlineAppendStatements(InlineInfo* inlineInfo, BasicBlock* block, Statement* stmt); diff --git a/src/coreclr/jit/ee_il_dll.cpp b/src/coreclr/jit/ee_il_dll.cpp index 0a7f226a1cc12..c862821406a89 100644 --- a/src/coreclr/jit/ee_il_dll.cpp +++ b/src/coreclr/jit/ee_il_dll.cpp @@ -608,7 +608,17 @@ void Compiler::eeGetStmtOffsets() uint32_t* offsets; ICorDebugInfo::BoundaryTypes offsetsImplicit; - info.compCompHnd->getBoundaries(info.compMethodHnd, &offsetsCount, &offsets, &offsetsImplicit); + if (compIsForInlining()) + { + // We do not get explicit boundaries for inlinees, only implicit ones. + offsetsImplicit = impInlineRoot()->info.compStmtOffsetsImplicit; + offsetsCount = 0; + offsets = nullptr; + } + else + { + info.compCompHnd->getBoundaries(info.compMethodHnd, &offsetsCount, &offsets, &offsetsImplicit); + } /* Set the implicit boundaries */ diff --git a/src/coreclr/jit/emitxarch.cpp b/src/coreclr/jit/emitxarch.cpp index 35196032eccb8..7f88c5049acba 100644 --- a/src/coreclr/jit/emitxarch.cpp +++ b/src/coreclr/jit/emitxarch.cpp @@ -7577,7 +7577,7 @@ void emitter::emitIns_Call(EmitCallType callType, #endif /* Managed RetVal: emit sequence point for the call */ - if (emitComp->opts.compDbgInfo && di.GetLocation().IsValid()) + if (emitComp->opts.compDbgInfo && di.IsValid()) { codeGen->genIPmappingAdd(IPmappingDscKind::Normal, di, false); } diff --git a/src/coreclr/jit/fgbasic.cpp b/src/coreclr/jit/fgbasic.cpp index 7bd812b46a6c9..00e5592fccabb 100644 --- a/src/coreclr/jit/fgbasic.cpp +++ b/src/coreclr/jit/fgbasic.cpp @@ -895,6 +895,11 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed bool preciseScan = makeInlineObservations && compInlineResult->GetPolicy()->RequiresPreciseScan(); const bool resolveTokens = preciseScan && (isPreJit || isTier1); + // Track offsets where IL instructions begin in DEBUG builds. Used to + // validate debug info generated by the JIT. + assert(codeSize == compInlineContext->GetILSize()); + INDEBUG(FixedBitVect* ilInstsSet = FixedBitVect::bitVectInit(codeSize, this)); + if (makeInlineObservations) { // Set default values for profile (to avoid NoteFailed in CALLEE_IL_CODE_SIZE's handler) @@ -946,6 +951,9 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed { prevOpcode = opcode; opcode = (OPCODE)getU1LittleEndian(codeAddr); + + INDEBUG(ilInstsSet->bitVectSet((UINT)(codeAddr - codeBegp))); + codeAddr += sizeof(__int8); if (!handled && preciseScan) @@ -2092,6 +2100,8 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed " at offset %04X", (IL_OFFSET)(codeAddr - codeBegp)); } + INDEBUG(compInlineContext->SetILInstsSet(ilInstsSet)); + if (makeInlineObservations) { compInlineResult->Note(InlineObservation::CALLEE_END_OPCODE_SCAN); @@ -4055,9 +4065,11 @@ IL_OFFSET Compiler::fgFindBlockILOffset(BasicBlock* block) for (Statement* const stmt : block->Statements()) { - if (stmt->GetDebugInfo().GetLocation().IsValid()) + // Blocks always contain IL offsets in the root. + DebugInfo di = stmt->GetDebugInfo().GetRoot(); + if (di.IsValid()) { - return stmt->GetDebugInfo().GetLocation().GetOffset(); + return di.GetLocation().GetOffset(); } } @@ -4228,8 +4240,9 @@ BasicBlock* Compiler::fgSplitBlockAfterNode(BasicBlock* curr, GenTree* node) if ((*riter)->gtOper == GT_IL_OFFSET) { GenTreeILOffset* ilOffset = (*riter)->AsILOffset(); - if (ilOffset->gtStmtDI.GetLocation().IsValid()) + if (ilOffset->gtStmtDI.IsValid()) { + assert(ilOffset->gtStmtDI.GetInlineContext()->IsRoot()); splitPointILOffset = ilOffset->gtStmtDI.GetLocation().GetOffset(); break; } diff --git a/src/coreclr/jit/fginline.cpp b/src/coreclr/jit/fginline.cpp index 8214a2251ef8a..21b44e1d60717 100644 --- a/src/coreclr/jit/fginline.cpp +++ b/src/coreclr/jit/fginline.cpp @@ -29,11 +29,12 @@ unsigned Compiler::fgCheckInlineDepthAndRecursion(InlineInfo* inlineInfo) { BYTE* candidateCode = inlineInfo->inlineCandidateInfo->methInfo.ILCode; - InlineContext* inlineContext = inlineInfo->iciStmt->GetInlineContext(); + InlineContext* inlineContext = inlineInfo->inlineCandidateInfo->inlinersContext; InlineResult* inlineResult = inlineInfo->inlineResult; // There should be a context for all candidates. assert(inlineContext != nullptr); + int depth = 0; for (; inlineContext != nullptr; inlineContext = inlineContext->GetParent()) @@ -101,17 +102,6 @@ PhaseStatus Compiler::fgInline() noway_assert(fgFirstBB != nullptr); - // Set the root inline context on all statements - InlineContext* rootContext = m_inlineStrategy->GetRootContext(); - - for (BasicBlock* const block : Blocks()) - { - for (Statement* const stmt : block->Statements()) - { - stmt->SetInlineContext(rootContext); - } - } - BasicBlock* block = fgFirstBB; bool madeChanges = false; @@ -123,7 +113,7 @@ PhaseStatus Compiler::fgInline() for (Statement* const stmt : block->Statements()) { -#ifdef DEBUG +#if defined(DEBUG) || defined(INLINE_DATA) // In debug builds we want the inline tree to show all failed // inlines. Some inlines may fail very early and never make it to // candidate stage. So scan the tree looking for those early failures. @@ -234,7 +224,7 @@ PhaseStatus Compiler::fgInline() return madeChanges ? PhaseStatus::MODIFIED_EVERYTHING : PhaseStatus::MODIFIED_NOTHING; } -#ifdef DEBUG +#if defined(DEBUG) || defined(INLINE_DATA) //------------------------------------------------------------------------ // fgFindNonInlineCandidate: tree walk helper to ensure that a tree node @@ -283,7 +273,7 @@ void Compiler::fgNoteNonInlineCandidate(Statement* stmt, GenTreeCall* call) return; } - InlineResult inlineResult(this, call, nullptr, "fgNotInlineCandidate"); + InlineResult inlineResult(this, call, nullptr, "fgNoteNonInlineCandidate"); InlineObservation currentObservation = InlineObservation::CALLSITE_NOT_CANDIDATE; // Try and recover the reason left behind when the jit decided @@ -301,8 +291,7 @@ void Compiler::fgNoteNonInlineCandidate(Statement* stmt, GenTreeCall* call) if (call->gtCallType == CT_USER_FUNC) { - // Create InlineContext for the failure - m_inlineStrategy->NewFailure(stmt, &inlineResult); + m_inlineStrategy->NewContext(call->gtInlineContext, stmt, call)->SetFailed(&inlineResult); } } @@ -863,7 +852,7 @@ Compiler::fgWalkResult Compiler::fgDebugCheckInlineCandidates(GenTree** pTree, f #endif // DEBUG -void Compiler::fgInvokeInlineeCompiler(GenTreeCall* call, InlineResult* inlineResult) +void Compiler::fgInvokeInlineeCompiler(GenTreeCall* call, InlineResult* inlineResult, InlineContext** createdContext) { noway_assert(call->gtOper == GT_CALL); noway_assert((call->gtFlags & GTF_CALL_INLINE_CANDIDATE) != 0); @@ -945,6 +934,11 @@ void Compiler::fgInvokeInlineeCompiler(GenTreeCall* call, InlineResult* inlineRe { pParam->inlineInfo->InlineRoot = pParam->pThis->impInlineInfo->InlineRoot; } + + // The inline context is part of debug info and must be created + // before we start creating statements; we lazily create it as + // late as possible, which is here. + pParam->inlineInfo->inlineContext = pParam->inlineInfo->InlineRoot->m_inlineStrategy->NewContext(pParam->inlineInfo->inlineCandidateInfo->inlinersContext, pParam->inlineInfo->iciStmt, pParam->inlineInfo->iciCall); pParam->inlineInfo->argCnt = pParam->inlineCandidateInfo->methInfo.args.totalILArgs(); pParam->inlineInfo->tokenLookupContextHandle = pParam->inlineCandidateInfo->exactContextHnd; @@ -960,7 +954,6 @@ void Compiler::fgInvokeInlineeCompiler(GenTreeCall* call, InlineResult* inlineRe compileFlagsForInlinee.Clear(JitFlags::JIT_FLAG_BBINSTR); compileFlagsForInlinee.Clear(JitFlags::JIT_FLAG_PROF_ENTERLEAVE); compileFlagsForInlinee.Clear(JitFlags::JIT_FLAG_DEBUG_EnC); - compileFlagsForInlinee.Clear(JitFlags::JIT_FLAG_DEBUG_INFO); compileFlagsForInlinee.Clear(JitFlags::JIT_FLAG_REVERSE_PINVOKE); compileFlagsForInlinee.Clear(JitFlags::JIT_FLAG_TRACK_TRANSITIONS); @@ -1011,6 +1004,8 @@ void Compiler::fgInvokeInlineeCompiler(GenTreeCall* call, InlineResult* inlineRe } } + *createdContext = inlineInfo.inlineContext; + if (inlineResult->IsFailure()) { return; @@ -1117,16 +1112,8 @@ void Compiler::fgInsertInlineeBlocks(InlineInfo* pInlineInfo) #endif // DEBUG - // Create a new inline context and mark the inlined statements with it - InlineContext* calleeContext = m_inlineStrategy->NewSuccess(pInlineInfo); - - for (BasicBlock* const block : InlineeCompiler->Blocks()) - { - for (Statement* const stmt : block->Statements()) - { - stmt->SetInlineContext(calleeContext); - } - } + // Mark success. + pInlineInfo->inlineContext->SetSucceeded(pInlineInfo); // Prepend statements Statement* stmtAfter = fgInlinePrependStatements(pInlineInfo); @@ -1281,9 +1268,10 @@ void Compiler::fgInsertInlineeBlocks(InlineInfo* pInlineInfo) block->copyEHRegion(iciBlock); block->bbFlags |= iciBlock->bbFlags & BBF_BACKWARD_JUMP; - if (iciStmt->GetDebugInfo().GetLocation().IsValid()) + DebugInfo di = iciStmt->GetDebugInfo().GetRoot(); + if (di.IsValid()) { - block->bbCodeOffs = iciStmt->GetDebugInfo().GetLocation().GetOffset(); + block->bbCodeOffs = di.GetLocation().GetOffset(); block->bbCodeOffsEnd = block->bbCodeOffs + 1; // TODO: is code size of 1 some magic number for inlining? } else @@ -1471,7 +1459,7 @@ Statement* Compiler::fgInlinePrependStatements(InlineInfo* inlineInfo) { BasicBlock* block = inlineInfo->iciBlock; Statement* callStmt = inlineInfo->iciStmt; - const DebugInfo& callDI = DebugInfo(nullptr, callStmt->GetDebugInfo().GetLocation()); + const DebugInfo& callDI = callStmt->GetDebugInfo(); Statement* postStmt = callStmt->GetNextStmt(); Statement* afterStmt = callStmt; // afterStmt is the place where the new statements should be inserted after. Statement* newStmt = nullptr; @@ -1836,15 +1824,6 @@ Statement* Compiler::fgInlinePrependStatements(InlineInfo* inlineInfo) } } - // Update any newly added statements with the appropriate context. - InlineContext* context = callStmt->GetInlineContext(); - assert(context != nullptr); - for (Statement* addedStmt = callStmt->GetNextStmt(); addedStmt != postStmt; addedStmt = addedStmt->GetNextStmt()) - { - assert(addedStmt->GetInlineContext() == nullptr); - addedStmt->SetInlineContext(context); - } - return afterStmt; } diff --git a/src/coreclr/jit/fgstmt.cpp b/src/coreclr/jit/fgstmt.cpp index 41537d29db94a..24140b3fc52b9 100644 --- a/src/coreclr/jit/fgstmt.cpp +++ b/src/coreclr/jit/fgstmt.cpp @@ -101,14 +101,14 @@ void Compiler::fgInsertStmtAtBeg(BasicBlock* block, Statement* stmt) // Arguments: // block - the block into which 'tree' will be inserted; // tree - the tree to be inserted. -// offs - the offset to use for the statement +// di - the debug info to use for the new statement. // // Return Value: // The new created statement with `tree` inserted into `block`. // -Statement* Compiler::fgNewStmtAtBeg(BasicBlock* block, GenTree* tree, IL_OFFSETX offs) +Statement* Compiler::fgNewStmtAtBeg(BasicBlock* block, GenTree* tree, const DebugInfo& di) { - Statement* stmt = gtNewStmt(tree, offs); + Statement* stmt = gtNewStmt(tree, di); fgInsertStmtAtBeg(block, stmt); return stmt; } @@ -154,7 +154,7 @@ void Compiler::fgInsertStmtAtEnd(BasicBlock* block, Statement* stmt) // Arguments: // block - the block into which 'stmt' will be inserted; // tree - the tree to be inserted. -// offs - the offset to use for the statement +// di - the debug info to use for the new statement. // // Return Value: // The new created statement with `tree` inserted into `block`. @@ -162,9 +162,9 @@ void Compiler::fgInsertStmtAtEnd(BasicBlock* block, Statement* stmt) // Note: // If the block can be a conditional block, use fgNewStmtNearEnd. // -Statement* Compiler::fgNewStmtAtEnd(BasicBlock* block, GenTree* tree, IL_OFFSETX offs) +Statement* Compiler::fgNewStmtAtEnd(BasicBlock* block, GenTree* tree, const DebugInfo& di) { - Statement* stmt = gtNewStmt(tree, offs); + Statement* stmt = gtNewStmt(tree, di); fgInsertStmtAtEnd(block, stmt); return stmt; } @@ -243,13 +243,14 @@ void Compiler::fgInsertStmtNearEnd(BasicBlock* block, Statement* stmt) // Arguments: // block - the block into which 'stmt' will be inserted; // tree - the tree to be inserted. +// di - the debug info to use for the new statement. // // Return Value: // The new created statement with `tree` inserted into `block`. // -Statement* Compiler::fgNewStmtNearEnd(BasicBlock* block, GenTree* tree, IL_OFFSETX offs) +Statement* Compiler::fgNewStmtNearEnd(BasicBlock* block, GenTree* tree, const DebugInfo& di) { - Statement* stmt = gtNewStmt(tree, offs); + Statement* stmt = gtNewStmt(tree, di); fgInsertStmtNearEnd(block, stmt); return stmt; } @@ -457,7 +458,7 @@ void Compiler::fgRemoveStmt(BasicBlock* block, Statement* stmt DEBUGARG(bool isU } #endif // DEBUG - if (opts.compDbgCode && stmt->GetPrevStmt() != stmt && stmt->GetDebugInfo().GetLocation().IsValid()) + if (opts.compDbgCode && stmt->GetPrevStmt() != stmt && stmt->GetDebugInfo().IsValid()) { /* TODO: For debuggable code, should we remove significant statement boundaries. Or should we leave a GT_NO_OP in its place? */ diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index aec484e049f98..576dda1d00757 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -380,15 +380,132 @@ bool GenTree::IsNodeProperlySized() const } #endif +#ifdef DEBUG +//------------------------------------------------------------------------ +// Dump: Print a textual representation of this ILLocation. +// +// Notes: +// For invalid ILLocations, we print '???'. +// Otherwise the offset and flags are printed in the format 0xabc[EC]. +// +void ILLocation::Dump() const +{ + if (!IsValid()) + { + printf("???"); + } + else + { + printf("0x%03X[", GetOffset()); + printf("%c", IsStackEmpty() ? 'E' : '-'); + printf("%c", IsCall() ? 'C' : '-'); + printf("]"); + } +} + +//------------------------------------------------------------------------ +// Dump: Print a textual representation of this DebugInfo. +// +// Parameters: +// recurse - print the full path back to the root, separated by arrows. +// +// Notes: +// The DebugInfo is printed in the format +// +// INL02 @ 0xabc[EC] +// +// Before '@' is the ordinal of the inline context, then comes the IL +// offset, and then comes the IL location flags (stack Empty, isCall). +// +// If 'recurse' is specified then dump the full DebugInfo path to the +// root in the format +// +// INL02 @ 0xabc[EC] <- INL01 @ 0x123[EC] <- ... <- INLRT @ 0x456[EC] +// +// with the left most entry being the inner most inlined statement. +void DebugInfo::Dump(bool recurse) const +{ + InlineContext* context = GetInlineContext(); + if (context != nullptr) + { + if (context->IsRoot()) + { + printf("INLRT @ "); + } + else if (context->GetOrdinal() != 0) + { + printf(FMT_INL_CTX " @ ", context->GetOrdinal()); + } + } + + GetLocation().Dump(); + + DebugInfo par; + if (recurse && GetParent(&par)) + { + printf(" <- "); + par.Dump(recurse); + } +} + +//------------------------------------------------------------------------ +// Validate: Validate this DebugInfo instance. +// +// Notes: +// This validates that if there is DebugInfo, then it looks sane by checking +// that the IL location correctly points to the beginning of an IL instruction. +// +void DebugInfo::Validate() const +{ + DebugInfo di = *this; + do + { + if (!di.IsValid()) + continue; + + bool isValidOffs = di.GetLocation().GetOffset() < di.GetInlineContext()->GetILSize(); + if (isValidOffs) + { + bool isValidStart = di.GetInlineContext()->GetILInstsSet()->bitVectTest(di.GetLocation().GetOffset()); + assert(isValidStart && "Detected invalid debug info: IL offset does not refer to the start of an IL instruction"); + } + else + { + assert(!"Detected invalid debug info: IL offset is out of range"); + } + + } while (di.GetParent(&di)); +} +#endif + +//------------------------------------------------------------------------ +// GetParent: Get debug info for the parent statement that inlined the +// statement for this debug info. +// +// Return Value: +// True if the current debug info is valid and has a parent; otherwise false. +// On false return, the 'par' parameter is unaffected. +// bool DebugInfo::GetParent(DebugInfo* par) const { - if (m_inlineContext->IsRoot()) + if ((m_inlineContext == nullptr) || m_inlineContext->IsRoot()) return false; *par = DebugInfo(m_inlineContext->GetParent(), m_inlineContext->GetLocation()); return true; } +//------------------------------------------------------------------------ +// GetRoot: Get debug info for the statement in the root function that +// eventually led to this debug info through inlines. +// +// Return Value: +// If this DebugInfo instance is valid, returns a DebugInfo instance +// representing the call in the root function that eventually inlined the +// statement this DebugInfo describes. +// +// If this DebugInfo instance is invalid, returns an invalid DebugInfo instance. +// DebugInfo DebugInfo::GetRoot() const { DebugInfo result = *this; @@ -6418,6 +6535,7 @@ GenTreeCall* Compiler::gtNewCallNode( // These get updated after call node is built. node->gtInlineObservation = InlineObservation::CALLEE_UNUSED_INITIAL; node->gtRawILOffset = BAD_IL_OFFSET; + node->gtInlineContext = compInlineContext; #endif // Spec: Managed Retval sequence points needs to be generated while generating debug info for debuggable code. @@ -6425,7 +6543,7 @@ GenTreeCall* Compiler::gtNewCallNode( // Implementation note: if not generating MRV info genCallSite2ILOffsetMap will be NULL and // codegen will pass DebugInfo() to emitter, which will cause emitter // not to emit IP mapping entry. - if (opts.compDbgCode && opts.compDbgInfo) + if (opts.compDbgCode && opts.compDbgInfo && di.IsValid()) { // Managed Retval - IL offset of the call. This offset is used to emit a // CALL_INSTRUCTION type sequence point while emitting corresponding native call. @@ -8313,7 +8431,8 @@ GenTreeCall* Compiler::gtCloneExprCallHelper(GenTreeCall* tree, #if defined(DEBUG) || defined(INLINE_DATA) copy->gtInlineObservation = tree->gtInlineObservation; - copy->gtRawILOffset = tree->AsCall()->gtRawILOffset; + copy->gtRawILOffset = tree->gtRawILOffset; + copy->gtInlineContext = tree->gtInlineContext; #endif copy->CopyOtherRegFlags(tree); @@ -11496,7 +11615,7 @@ void Compiler::gtDispLeaf(GenTree* tree, IndentStack* indentStack) case GT_IL_OFFSET: printf(" IL offset: "); - if (!tree->AsILOffset()->gtStmtDI.GetLocation().IsValid()) + if (!tree->AsILOffset()->gtStmtDI.IsValid()) { printf("???"); } @@ -12363,27 +12482,40 @@ void Compiler::gtDispStmt(Statement* stmt, const char* msg /* = nullptr */) printf("%s ", msg); } printStmtID(stmt); - printf(" (IL "); - if (!stmt->GetDebugInfo().GetLocation().IsValid()) + printf(" ( "); + const DebugInfo& di = stmt->GetDebugInfo(); + // For statements in the root we display just the location without the + // inline context info. + if (di.GetInlineContext() == nullptr || di.GetInlineContext()->IsRoot()) { - printf(" ???"); + di.GetLocation().Dump(); } else { - printf("0x%03X", stmt->GetDebugInfo().GetLocation().GetOffset()); + stmt->GetDebugInfo().Dump(false); } - printf("..."); + printf(" ... "); IL_OFFSET lastILOffs = stmt->GetLastILOffset(); if (lastILOffs == BAD_IL_OFFSET) { - printf(" ???"); + printf("???"); } else { printf("0x%03X", lastILOffs); } - printf(")\n"); + + printf(" )"); + + DebugInfo par; + if (stmt->GetDebugInfo().GetParent(&par)) + { + printf(" <- "); + par.Dump(true); + } + + printf("\n"); } gtDispTree(stmt->GetRootNode()); } diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index 45839c5b51c5f..40b3d003c5716 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -4866,6 +4866,11 @@ struct GenTreeCall final : public GenTree // IL offset of the call wrt its parent method. IL_OFFSET gtRawILOffset; + + // In DEBUG we report even non inline candidates in the inline tree in + // fgNoteNonInlineCandidate. We need to keep around the inline context for + // this as normally it's part of the candidate info. + class InlineContext* gtInlineContext; #endif // defined(DEBUG) || defined(INLINE_DATA) bool IsHelperCall() const @@ -6185,6 +6190,11 @@ class ILLocation return !(*this == other); } +#ifdef DEBUG + // Dump textual representation of this ILLocation to jitstdout. + void Dump() const; +#endif + private: IL_OFFSET m_offset; bool m_isStackEmpty : 1; @@ -6217,13 +6227,33 @@ class DebugInfo } // Retrieve information about the location that inlined this statement. + // Note that there can be associated parent information even when IsValid + // below returns false. bool GetParent(DebugInfo* par) const; // Get debug info in the root. If this debug info is in the root, then - // returns *this. Otherwise returns information of the call in the root that - // eventually produced this statement through inlines. + // returns *this. Otherwise returns information of the call in the root + // that eventually produced this statement through inlines. DebugInfo GetRoot() const; +#ifdef DEBUG + void Validate() const; +#else + void Validate() const { } +#endif + +#ifdef DEBUG + // Dump textual representation of this DebugInfo to jitstdout. + void Dump(bool recurse) const; +#endif + + // Check if this debug info has both a valid inline context and valid + // location. + bool IsValid() const + { + return m_inlineContext != nullptr && m_location.IsValid(); + } + inline bool operator==(const DebugInfo& other) const { return (m_inlineContext == other.m_inlineContext) && (m_location == other.m_location); @@ -6377,19 +6407,7 @@ struct Statement void SetDebugInfo(const DebugInfo& di) { m_debugInfo = di; - } - - // During inlining we require the inline context to be maintained. We use - // the storage in the debug info for it. After inlining is over we no - // longer require inline contexts for statements without debug information. - InlineContext* GetInlineContext() - { - return m_debugInfo.GetInlineContext(); - } - - void SetInlineContext(InlineContext* inlineContext) - { - m_debugInfo = DebugInfo(inlineContext, m_debugInfo.GetLocation()); + di.Validate(); } #ifdef DEBUG @@ -6470,7 +6488,7 @@ struct Statement Statement* m_next; Statement* m_prev; - DebugInfo m_debugInfo; // Debug info for the statement, including inline context + DebugInfo m_debugInfo; #ifdef DEBUG IL_OFFSET m_lastILOffset; // The instr offset at the end of this statement. diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 1ff9605b8d176..4bdc519914ac4 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -615,10 +615,12 @@ inline void Compiler::impAppendStmt(Statement* stmt, unsigned chkLevel) impMarkContiguousSIMDFieldAssignments(stmt); #endif - /* Once we set impCurStmtDI in an appended tree, we are ready to - report the following offsets. So reset impCurStmtDI */ + // Once we set the current offset as debug info in an appended tree, we are + // ready to report the following offsets. Note that we need to compare + // offsets here instead of debug info, since we do not set the "is call" + // debug in impCurStmtDI. - if (impLastStmt->GetDebugInfo().GetLocation() == impCurStmtDI.GetLocation()) + if (impLastStmt->GetDebugInfo().GetLocation().GetOffset() == impCurStmtDI.GetLocation().GetOffset()) { impCurStmtOffsSet(BAD_IL_OFFSET); } @@ -1201,7 +1203,7 @@ GenTree* Compiler::impAssignStruct(GenTree* dest, assert(varTypeIsStruct(dest)); DebugInfo usedDI = di; - if (!usedDI.GetLocation().IsValid()) + if (!usedDI.IsValid()) { usedDI = impCurStmtDI; } @@ -1284,7 +1286,7 @@ GenTree* Compiler::impAssignStructPtr(GenTree* destAddr, GenTreeFlags destFlags = GTF_EMPTY; DebugInfo usedDI = di; - if (!usedDI.GetLocation().IsValid()) + if (!usedDI.IsValid()) { usedDI = impCurStmtDI; } @@ -2824,7 +2826,10 @@ BasicBlock* Compiler::impPushCatchArgOnStack(BasicBlock* hndBlk, CORINFO_CLASS_H { // Report the debug info. impImportBlockCode won't treat the actual handler as exception block and thus // won't do it for us. - impCurStmtDI = DebugInfo(impCurStmtDI.GetInlineContext(), ILLocation(newBlk->bbCodeOffs, false, false)); + // TODO-DEBUGINFO: Previous code always set stack as non-empty + // here. Can we not just use impCurStmtOffsSet? Are we out of sync + // here with the stack? + impCurStmtDI = DebugInfo(compInlineContext, ILLocation(newBlk->bbCodeOffs, false, false)); argStmt = gtNewStmt(argAsg, impCurStmtDI); } else @@ -2881,38 +2886,28 @@ GenTree* Compiler::impCloneExpr(GenTree* tree, return gtNewLclvNode(temp, type); } -/***************************************************************************** - * Remember the IL offset (including stack-empty info) for the trees we will - * generate now. - */ - -inline void Compiler::impCurStmtOffsSet(IL_OFFSET offs) +DebugInfo Compiler::impCreateDIWithCurrentStackInfo(IL_OFFSET offs, bool isCall) { - if (compIsForInlining()) - { - Statement* callStmt = impInlineInfo->iciStmt; - impCurStmtDI = callStmt->GetDebugInfo(); - } - else - { - bool isStackEmpty = verCurrentState.esStackDepth <= 0; - impCurStmtDI = DebugInfo(impCurStmtDI.GetInlineContext(), ILLocation(offs, isStackEmpty, false)); - } + assert(offs != BAD_IL_OFFSET); + + bool isStackEmpty = verCurrentState.esStackDepth <= 0; + return DebugInfo(compInlineContext, ILLocation(offs, isStackEmpty, isCall)); } /***************************************************************************** - * Returns current IL offset with stack-empty and call-instruction info incorporated + * Remember the IL offset (including stack-empty info) for the trees we will + * generate next. */ -inline DebugInfo Compiler::impCurDebugInfo(IL_OFFSET offs, bool callInstruction) + +inline void Compiler::impCurStmtOffsSet(IL_OFFSET offs) { - if (compIsForInlining()) + if (offs == BAD_IL_OFFSET) { - return DebugInfo(); + impCurStmtDI = DebugInfo(compInlineContext, ILLocation()); } else { - bool isStackEmpty = verCurrentState.esStackDepth <= 0; - return DebugInfo(impCurStmtDI.GetInlineContext(), ILLocation(offs, isStackEmpty, callInstruction)); + impCurStmtDI = impCreateDIWithCurrentStackInfo(offs, false); } } @@ -2998,11 +2993,6 @@ unsigned Compiler::impInitBlockLineInfo() impCurStmtOffsSet(BAD_IL_OFFSET); - if (compIsForInlining()) - { - return ~0; - } - IL_OFFSET blockOffs = compCurBB->bbCodeOffs; if ((verCurrentState.esStackDepth == 0) && (info.compStmtOffsetsImplicit & ICorDebugInfo::STACK_EMPTY_BOUNDARIES)) @@ -8453,7 +8443,7 @@ bool Compiler::impIsImplicitTailCallCandidate( // newObjThis - tree for this pointer or uninitalized newobj temp (or nullptr) // prefixFlags - IL prefix flags for the call // callInfo - EE supplied info for the call -// rawILOffset - IL offset of the opcode +// rawILOffset - IL offset of the opcode, used for guarded devirtualization. // // Returns: // Type of the call's return value. @@ -8482,7 +8472,11 @@ var_types Compiler::impImportCall(OPCODE opcode, { assert(opcode == CEE_CALL || opcode == CEE_CALLVIRT || opcode == CEE_NEWOBJ || opcode == CEE_CALLI); - DebugInfo di = impCurDebugInfo(rawILOffset, true); + // The current statement DI may not refer to the exact call, but for calls + // we wish to be able to attach the exact IL instruction to get "return + // value" support in the debugger, so create one with the exact IL offset. + DebugInfo di = impCreateDIWithCurrentStackInfo(rawILOffset, true); + var_types callRetTyp = TYP_COUNT; CORINFO_SIG_INFO* sig = nullptr; CORINFO_METHOD_HANDLE methHnd = nullptr; @@ -11725,17 +11719,17 @@ void Compiler::impImportBlockCode(BasicBlock* block) impSpillStackEnsure(true); } - // Has impCurStmtDI been reported in any tree? + // Have we reported debug info for any tree? - if (impCurStmtDI.GetLocation().IsValid() && opts.compDbgCode) + if (impCurStmtDI.IsValid() && opts.compDbgCode) { GenTree* placeHolder = new (this, GT_NO_OP) GenTree(GT_NO_OP, TYP_VOID); impAppendTree(placeHolder, (unsigned)CHECK_SPILL_NONE, impCurStmtDI); - assert(!impCurStmtDI.GetLocation().IsValid()); + assert(!impCurStmtDI.IsValid()); } - if (!impCurStmtDI.GetLocation().IsValid()) + if (!impCurStmtDI.IsValid()) { /* Make sure that nxtStmtIndex is in sync with opcodeOffs. If opcodeOffs has gone past nxtStmtIndex, catch up */ @@ -11807,8 +11801,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) impCurStmtOffsSet(opcodeOffs); } - assert(!impCurStmtDI.GetLocation().IsValid() || nxtStmtOffs == BAD_IL_OFFSET || - impCurStmtDI.GetLocation().GetOffset() <= nxtStmtOffs); + assert(!impCurStmtDI.IsValid() || (nxtStmtOffs == BAD_IL_OFFSET) || (impCurStmtDI.GetLocation().GetOffset() <= nxtStmtOffs)); } } @@ -13464,7 +13457,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) /* GT_JTRUE is handled specially for non-empty stacks. See 'addStmt' in impImportBlock(block). For correct line numbers, spill stack. */ - if (opts.compDbgCode && impCurStmtDI.GetLocation().IsValid()) + if (opts.compDbgCode && impCurStmtDI.IsValid()) { impSpillStackEnsure(true); } @@ -19766,6 +19759,7 @@ void Compiler::impCheckCanInline(GenTreeCall* call, pInfo->initClassResult = initClassResult; pInfo->fncRetType = fncRetType; pInfo->exactContextNeedsRuntimeLookup = false; + pInfo->inlinersContext = pParam->pThis->compInlineContext; // Note exactContextNeedsRuntimeLookup is reset later on, // over in impMarkInlineCandidate. diff --git a/src/coreclr/jit/indirectcalltransformer.cpp b/src/coreclr/jit/indirectcalltransformer.cpp index c3ce3151192d5..c192275564794 100644 --- a/src/coreclr/jit/indirectcalltransformer.cpp +++ b/src/coreclr/jit/indirectcalltransformer.cpp @@ -781,14 +781,14 @@ class IndirectCallTransformer } else { - compiler->fgNewStmtAtEnd(thenBlock, call, stmt->GetILOffsetX()); + compiler->fgNewStmtAtEnd(thenBlock, call, stmt->GetDebugInfo()); } } else { // Add the call. // - compiler->fgNewStmtAtEnd(thenBlock, call, stmt->GetILOffsetX()); + compiler->fgNewStmtAtEnd(thenBlock, call, stmt->GetDebugInfo()); // Re-establish this call as an inline candidate. // @@ -831,7 +831,7 @@ class IndirectCallTransformer elseBlock = CreateAndInsertBasicBlock(BBJ_NONE, thenBlock); elseBlock->bbFlags |= currBlock->bbFlags & BBF_SPLIT_GAINED; GenTreeCall* call = origCall; - Statement* newStmt = compiler->gtNewStmt(call, stmt->GetILOffsetX()); + Statement* newStmt = compiler->gtNewStmt(call, stmt->GetDebugInfo()); call->gtFlags &= ~GTF_CALL_INLINE_CANDIDATE; call->SetIsGuarded(); diff --git a/src/coreclr/jit/inline.cpp b/src/coreclr/jit/inline.cpp index bdfa52d43bee4..5530cabc183c8 100644 --- a/src/coreclr/jit/inline.cpp +++ b/src/coreclr/jit/inline.cpp @@ -343,7 +343,11 @@ InlineContext::InlineContext(InlineStrategy* strategy) , m_Callee(nullptr) , m_TreeID(0) , m_Ordinal(0) + , m_ActualCallOffset(BAD_IL_OFFSET) #endif // defined(DEBUG) || defined(INLINE_DATA) +#ifdef DEBUG + , m_ILInstsSet(nullptr) +#endif { // Empty } @@ -402,15 +406,25 @@ void InlineContext::Dump(unsigned indent) const char* guarded = m_Guarded ? " guarded" : ""; const char* unboxed = m_Unboxed ? " unboxed" : ""; - if (!m_Location.IsValid()) + IL_OFFSET offs = BAD_IL_OFFSET; + +#if defined(DEBUG) || defined(INLINE_DATA) + offs = m_ActualCallOffset; +#endif + + if (offs == BAD_IL_OFFSET && m_Location.IsValid()) { - printf("%*s[%u IL=???? TR=%06u %08X] [%s%s: %s%s%s%s] %s\n", indent, "", m_Ordinal, m_TreeID, calleeToken, + offs = m_Location.GetOffset(); + } + + if (offs == BAD_IL_OFFSET) + { + printf("%*s[" FMT_INL_CTX " IL=???? TR=%06u %08X] [%s%s: %s%s%s%s] %s\n", indent, "", GetOrdinal(), m_TreeID, calleeToken, inlineResult, inlineTarget, inlineReason, guarded, devirtualized, unboxed, calleeName); } else { - IL_OFFSET offset = m_Location.GetOffset(); - printf("%*s[%u IL=%04d TR=%06u %08X] [%s%s: %s%s%s%s] %s\n", indent, "", m_Ordinal, offset, m_TreeID, + printf("%*s[" FMT_INL_CTX " IL=%04d TR=%06u %08X] [%s%s: %s%s%s%s] %s\n", indent, "", GetOrdinal(), offs, m_TreeID, calleeToken, inlineResult, inlineTarget, inlineReason, guarded, devirtualized, unboxed, calleeName); } } @@ -454,7 +468,7 @@ void InlineContext::DumpData(unsigned indent) else if (m_Success) { const char* inlineReason = InlGetObservationString(m_Observation); - printf("%*s%u,\"%s\",\"%s\",", indent, "", m_Ordinal, inlineReason, calleeName); + printf("%*s%u,\"%s\",\"%s\",", indent, "", GetOrdinal(), inlineReason, calleeName); m_Policy->DumpData(jitstdout); printf("\n"); } @@ -1232,126 +1246,90 @@ InlineContext* InlineStrategy::NewRoot() return rootContext; } -//------------------------------------------------------------------------ -// NewSuccess: construct an InlineContext for a successful inline -// and link it into the context tree -// -// Arguments: -// inlineInfo - information about this inline -// -// Return Value: -// A new InlineContext for statements brought into the method by -// this inline. - -InlineContext* InlineStrategy::NewSuccess(InlineInfo* inlineInfo) +InlineContext* InlineStrategy::NewContext(InlineContext* parentContext, Statement* stmt, GenTreeCall* call) { - InlineContext* calleeContext = new (m_Compiler, CMK_Inlining) InlineContext(this); - Statement* stmt = inlineInfo->iciStmt; - BYTE* calleeIL = inlineInfo->inlineCandidateInfo->methInfo.ILCode; - unsigned calleeILSize = inlineInfo->inlineCandidateInfo->methInfo.ILCodeSize; - InlineContext* parentContext = stmt->GetDebugInfo().GetInlineContext(); - GenTreeCall* originalCall = inlineInfo->inlineResult->GetCall(); - - noway_assert(parentContext != nullptr); - - calleeContext->m_Code = calleeIL; - calleeContext->m_ILSize = calleeILSize; - calleeContext->m_Parent = parentContext; - // Push on front here will put siblings in reverse lexical - // order which we undo in the dumper - calleeContext->m_Sibling = parentContext->m_Child; - parentContext->m_Child = calleeContext; - calleeContext->m_Child = nullptr; - calleeContext->m_Location = stmt->GetDebugInfo().GetLocation(); - calleeContext->m_Observation = inlineInfo->inlineResult->GetObservation(); - calleeContext->m_Success = true; - calleeContext->m_Devirtualized = originalCall->IsDevirtualized(); - calleeContext->m_Guarded = originalCall->IsGuarded(); - calleeContext->m_Unboxed = originalCall->IsUnboxed(); - calleeContext->m_ImportedILSize = inlineInfo->inlineResult->GetImportedILSize(); - -#if defined(DEBUG) || defined(INLINE_DATA) - - InlinePolicy* policy = inlineInfo->inlineResult->GetPolicy(); + InlineContext* context = new (m_Compiler, CMK_Inlining) InlineContext(this); - calleeContext->m_Policy = policy; - calleeContext->m_CodeSizeEstimate = policy->CodeSizeEstimate(); - calleeContext->m_Callee = inlineInfo->fncHandle; - // +1 here since we set this before calling NoteOutcome. - calleeContext->m_Ordinal = m_InlineCount + 1; - // Update offset with more accurate info - calleeContext->m_Location = ILLocation(originalCall->gtRawILOffset, false, false); + context->m_InlineStrategy = this; + context->m_Parent = parentContext; + context->m_Sibling = parentContext->m_Child; + parentContext->m_Child = context; -#endif // defined(DEBUG) || defined(INLINE_DATA) - -#if defined(DEBUG) + // In debug builds we record inline contexts in all produced calls to be + // able to show all failed inlines in the inline tree, even non-candidates. + // These should always match the parent context we are seeing here. + assert(parentContext == call->gtInlineContext); - calleeContext->m_TreeID = originalCall->gtTreeID; - -#endif // defined(DEBUG) + if (call->IsInlineCandidate()) + { + InlineCandidateInfo* info = call->gtInlineCandidateInfo; + context->m_Code = info->methInfo.ILCode; + context->m_ILSize = info->methInfo.ILCodeSize; - NoteOutcome(calleeContext); +#ifdef DEBUG + // All inline candidates should get their own statements that have + // appropriate debug info (or no debug info). + InlineContext* diInlineContext = stmt->GetDebugInfo().GetInlineContext(); + assert(diInlineContext == nullptr || diInlineContext == parentContext); +#endif + } - return calleeContext; -} + // TODO-DEBUGINFO: Currently, to keep the same behavior as before, we use + // the location of the statement containing the call being inlined. This is + // not always the exact IL offset of the call instruction, consider e.g. + // ldarg.0 + // call + // which becomes a single statement where the IL location points to the + // ldarg instruction. For SPGO purposes we should consider always storing + // the exact offset of the call instruction which will be more precise. We + // may consider storing the statement itself as well. + context->m_Location = stmt->GetDebugInfo().GetLocation(); + context->m_Devirtualized = call->IsDevirtualized(); + context->m_Guarded = call->IsGuarded(); + context->m_Unboxed = call->IsUnboxed(); #if defined(DEBUG) || defined(INLINE_DATA) + context->m_TreeID = call->gtTreeID; + context->m_Callee = call->gtCallType == CT_INDIRECT ? nullptr : call->gtCallMethHnd; + context->m_ActualCallOffset = call->gtRawILOffset; +#endif -//------------------------------------------------------------------------ -// NewFailure: construct an InlineContext for a failing inline -// and link it into the context tree -// -// Arguments: -// stmt - statement containing the attempted inline -// inlineResult - inlineResult for the attempt -// -// Return Value: -// A new InlineContext for diagnostic purposes + return context; +} -InlineContext* InlineStrategy::NewFailure(Statement* stmt, InlineResult* inlineResult) +void InlineContext::SetSucceeded(const InlineInfo* info) { - // Check for a parent context first. We should now have a parent - // context for all statements. - InlineContext* parentContext = stmt->GetDebugInfo().GetInlineContext(); - assert(parentContext != nullptr); - InlineContext* failedContext = new (m_Compiler, CMK_Inlining) InlineContext(this); - GenTreeCall* originalCall = inlineResult->GetCall(); - - // Pushing the new context on the front of the parent child list - // will put siblings in reverse lexical order which we undo in the - // dumper. - failedContext->m_Parent = parentContext; - failedContext->m_Sibling = parentContext->m_Child; - parentContext->m_Child = failedContext; - failedContext->m_Child = nullptr; - failedContext->m_Location = stmt->GetDebugInfo().GetLocation(); - failedContext->m_Observation = inlineResult->GetObservation(); - failedContext->m_Callee = inlineResult->GetCallee(); - failedContext->m_Success = false; - failedContext->m_Devirtualized = originalCall->IsDevirtualized(); - failedContext->m_Guarded = originalCall->IsGuarded(); - failedContext->m_Unboxed = originalCall->IsUnboxed(); - - assert(InlIsValidObservation(failedContext->m_Observation)); + assert(InlIsValidObservation(info->inlineResult->GetObservation())); + m_Observation = info->inlineResult->GetObservation(); + m_ImportedILSize = info->inlineResult->GetImportedILSize(); + m_Success = true; #if defined(DEBUG) || defined(INLINE_DATA) + m_Policy = info->inlineResult->GetPolicy(); + m_CodeSizeEstimate = m_Policy->CodeSizeEstimate(); + m_Ordinal = m_InlineStrategy->m_InlineCount + 1; +#endif - // Update offset with more accurate info - failedContext->m_Location = ILLocation(originalCall->gtRawILOffset, false, false); - -#endif // #if defined(DEBUG) || defined(INLINE_DATA) - -#if defined(DEBUG) - - failedContext->m_TreeID = originalCall->gtTreeID; + m_InlineStrategy->NoteOutcome(this); +} -#endif // defined(DEBUG) +void InlineContext::SetFailed(const InlineResult* result) +{ + assert(InlIsValidObservation(result->GetObservation())); + m_Observation = result->GetObservation(); + m_ImportedILSize = result->GetImportedILSize(); + m_Success = false; - NoteOutcome(failedContext); +#if defined(DEBUG) || defined(INLINE_DATA) + m_Policy = result->GetPolicy(); + m_CodeSizeEstimate = m_Policy->CodeSizeEstimate(); +#endif - return failedContext; + m_InlineStrategy->NoteOutcome(this); } +#if defined(DEBUG) || defined(INLINE_DATA) + //------------------------------------------------------------------------ // Dump: dump description of inline behavior // diff --git a/src/coreclr/jit/inline.h b/src/coreclr/jit/inline.h index a140f2f39fd98..f61327da2bb2f 100644 --- a/src/coreclr/jit/inline.h +++ b/src/coreclr/jit/inline.h @@ -589,6 +589,7 @@ struct InlineCandidateInfo : public GuardedDevirtualizationCandidateInfo CorInfoInitClassResult initClassResult; var_types fncRetType; bool exactContextNeedsRuntimeLookup; + InlineContext* inlinersContext; }; // InlArgInfo describes inline candidate argument properties. @@ -635,6 +636,7 @@ struct InlineInfo CORINFO_METHOD_HANDLE fncHandle; InlineCandidateInfo* inlineCandidateInfo; + InlineContext* inlineContext; InlineResult* inlineResult; @@ -686,6 +688,8 @@ struct InlineInfo // This makes it possible to detect recursion and to keep track of the // depth of each inline attempt. +#define FMT_INL_CTX "INL%02u" + class InlineContext { // InlineContexts are created by InlineStrategies @@ -709,6 +713,11 @@ class InlineContext return m_Callee; } + unsigned GetOrdinal() const + { + return m_Ordinal; + } + #endif // defined(DEBUG) || defined(INLINE_DATA) // Get the parent context for this context. @@ -779,6 +788,21 @@ class InlineContext return m_ImportedILSize; } + void SetSucceeded(const InlineInfo* info); + void SetFailed(const InlineResult* result); + +#ifdef DEBUG + FixedBitVect* GetILInstsSet() const + { + return m_ILInstsSet; + } + + void SetILInstsSet(FixedBitVect* set) + { + m_ILInstsSet = set; + } +#endif + private: InlineContext(InlineStrategy* strategy); @@ -790,8 +814,8 @@ class InlineContext const BYTE* m_Code; // address of IL buffer for the method unsigned m_ILSize; // size of IL buffer for the method unsigned m_ImportedILSize; // estimated size of imported IL - ILLocation m_Location; // call site location within parent - InlineObservation m_Observation; // what lead to this inline + ILLocation m_Location; // inlining statement location within parent + InlineObservation m_Observation; // what lead to this inline success or failure int m_CodeSizeEstimate; // in bytes * 10 bool m_Success : 1; // true if this was a successful inline bool m_Devirtualized : 1; // true if this was a devirtualized call @@ -800,12 +824,17 @@ class InlineContext #if defined(DEBUG) || defined(INLINE_DATA) - InlinePolicy* m_Policy; // policy that evaluated this inline - CORINFO_METHOD_HANDLE m_Callee; // handle to the method - unsigned m_TreeID; // ID of the GenTreeCall - unsigned m_Ordinal; // Ordinal number of this inline + InlinePolicy* m_Policy; // policy that evaluated this inline + CORINFO_METHOD_HANDLE m_Callee; // handle to the method + unsigned m_TreeID; // ID of the GenTreeCall in the parent + unsigned m_Ordinal; // Ordinal number of this inline + IL_OFFSET m_ActualCallOffset; // IL offset of actual call instruction leading to the inline #endif // defined(DEBUG) || defined(INLINE_DATA) + +#ifdef DEBUG + FixedBitVect* m_ILInstsSet; // Set of offsets where instructions begin +#endif }; // The InlineStrategy holds the per-method persistent inline state. @@ -814,16 +843,14 @@ class InlineContext class InlineStrategy { + friend class InlineContext; public: // Construct a new inline strategy. InlineStrategy(Compiler* compiler); - // Create context for a successful inline. - InlineContext* NewSuccess(InlineInfo* inlineInfo); - - // Create context for a failing inline. - InlineContext* NewFailure(Statement* stmt, InlineResult* inlineResult); + // Create context for the specified inline candidate contained in the specified statement. + InlineContext* NewContext(InlineContext* parentContext, Statement* stmt, GenTreeCall* call); // Compiler associated with this strategy Compiler* GetCompiler() const diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 07426bda1ff13..da1d3065f9e03 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -6364,8 +6364,9 @@ void Compiler::fgMorphCallInline(GenTreeCall* call, InlineResult* inlineResult) // Is this call an inline candidate? if (call->IsInlineCandidate()) { + InlineContext* createdContext = nullptr; // Attempt the inline - fgMorphCallInlineHelper(call, inlineResult); + fgMorphCallInlineHelper(call, inlineResult, &createdContext); // We should have made up our minds one way or another.... assert(inlineResult->IsDecided()); @@ -6374,13 +6375,20 @@ void Compiler::fgMorphCallInline(GenTreeCall* call, InlineResult* inlineResult) if (inlineResult->IsFailure()) { + if (createdContext != nullptr) + { + // We created a context before we got to the failure, so mark + // it as failed in the tree. + createdContext->SetFailed(inlineResult); + } + else + { #ifdef DEBUG - - // Before we do any cleanup, create a failing InlineContext to - // capture details of the inlining attempt. - m_inlineStrategy->NewFailure(fgMorphStmt, inlineResult); - + // In debug we always put all inline attempts into the inline tree. + InlineContext* ctx = m_inlineStrategy->NewContext(call->gtInlineCandidateInfo->inlinersContext, fgMorphStmt, call); + ctx->SetFailed(inlineResult); #endif + } inliningFailed = true; @@ -6416,14 +6424,28 @@ void Compiler::fgMorphCallInline(GenTreeCall* call, InlineResult* inlineResult) } } -/***************************************************************************** - * Helper to attempt to inline a call - * Sets success/failure in inline result - * If success, modifies current method's IR with inlinee's IR - * If failed, undoes any speculative modifications to current method - */ - -void Compiler::fgMorphCallInlineHelper(GenTreeCall* call, InlineResult* result) +//------------------------------------------------------------------------------ +// fgMorphCallInlineHelper: Helper to attempt to inline a call +// +// Arguments: +// call - call expression to inline, inline candidate +// result - result to set to success or failure +// createdContext - The context that was created if the inline attempt got to the inliner. +// +// Notes: +// Attempts to inline the call. +// +// If successful, callee's IR is inserted in place of the call, and +// is marked with an InlineContext. +// +// If unsuccessful, the transformations done in anticipation of a +// possible inline are undone, and the candidate flag on the call +// is cleared. +// +// If a context was created because we got to the importer then it is output by this function. +// If the inline succeeded, this context will already be marked as successful. If it failed and +// a context is returned, then it will not have been marked as success or failed. +void Compiler::fgMorphCallInlineHelper(GenTreeCall* call, InlineResult* result, InlineContext** createdContext) { // Don't expect any surprises here. assert(result->IsCandidate()); @@ -6483,7 +6505,7 @@ void Compiler::fgMorphCallInlineHelper(GenTreeCall* call, InlineResult* result) // Invoke the compiler to inline the call. // - fgInvokeInlineeCompiler(call, result); + fgInvokeInlineeCompiler(call, result, createdContext); if (result->IsFailure()) { diff --git a/src/coreclr/jit/rationalize.cpp b/src/coreclr/jit/rationalize.cpp index 08c29ae03b769..35599b95c8e96 100644 --- a/src/coreclr/jit/rationalize.cpp +++ b/src/coreclr/jit/rationalize.cpp @@ -957,12 +957,16 @@ PhaseStatus Rationalizer::DoPhase() { BlockRange().InsertAtEnd(LIR::Range(statement->GetTreeList(), statement->GetRootNode())); - // If this statement has correct debug information, change it into a debug info - // node and insert it into the LIR. - if (statement->GetDebugInfo().GetLocation().IsValid()) + // If this statement has correct debug information, change it + // into a debug info node and insert it into the LIR. Currently + // we do not support describing IL offsets in inlinees in the + // emitter, so we normalize all debug info to be in the inline + // root here. + DebugInfo di = statement->GetDebugInfo().GetRoot(); + if (di.IsValid()) { GenTreeILOffset* ilOffset = new (comp, GT_IL_OFFSET) - GenTreeILOffset(statement->GetDebugInfo() DEBUGARG(statement->GetLastILOffset())); + GenTreeILOffset(di DEBUGARG(statement->GetLastILOffset())); BlockRange().InsertBefore(statement->GetTreeList(), ilOffset); } From de5e24b7fef97a3e800b489a1febc504b6e18d0b Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Thu, 4 Nov 2021 20:54:01 +0100 Subject: [PATCH 3/6] Run jit-format --- src/coreclr/jit/codegenarm.cpp | 3 +- src/coreclr/jit/codegenarm64.cpp | 7 ++- src/coreclr/jit/codegencommon.cpp | 76 ++++++++++++++++--------------- src/coreclr/jit/compiler.cpp | 2 +- src/coreclr/jit/compiler.h | 47 ++++++++++--------- src/coreclr/jit/ee_il_dll.cpp | 52 ++++++++++----------- src/coreclr/jit/emitarm.cpp | 2 +- src/coreclr/jit/emitarm.h | 12 ++--- src/coreclr/jit/emitarm64.cpp | 2 +- src/coreclr/jit/fginline.cpp | 26 ++++++----- src/coreclr/jit/fgprofile.cpp | 5 +- src/coreclr/jit/gentree.cpp | 9 ++-- src/coreclr/jit/gentree.h | 26 ++++------- src/coreclr/jit/importer.cpp | 37 ++++++++------- src/coreclr/jit/inline.cpp | 40 ++++++++-------- src/coreclr/jit/inline.h | 4 +- src/coreclr/jit/morph.cpp | 25 +++++----- src/coreclr/jit/rationalize.cpp | 4 +- 18 files changed, 192 insertions(+), 187 deletions(-) diff --git a/src/coreclr/jit/codegenarm.cpp b/src/coreclr/jit/codegenarm.cpp index 12977ae1acee5..b77255549db4d 100644 --- a/src/coreclr/jit/codegenarm.cpp +++ b/src/coreclr/jit/codegenarm.cpp @@ -1629,8 +1629,7 @@ void CodeGen::genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize, GetEmitter()->emitIns_Call(emitter::EC_INDIR_R, compiler->eeFindHelper(helper), INDEBUG_LDISASM_COMMA(nullptr) NULL, // addr argSize, retSize, gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, - gcInfo.gcRegByrefSetCur, - DebugInfo(), + gcInfo.gcRegByrefSetCur, DebugInfo(), callTargetReg, // ireg REG_NA, 0, 0, // xreg, xmul, disp false // isJump diff --git a/src/coreclr/jit/codegenarm64.cpp b/src/coreclr/jit/codegenarm64.cpp index 8229ed8539104..8fad31c95b59e 100644 --- a/src/coreclr/jit/codegenarm64.cpp +++ b/src/coreclr/jit/codegenarm64.cpp @@ -3841,10 +3841,9 @@ void CodeGen::genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize, GetEmitter()->emitIns_Call(callType, compiler->eeFindHelper(helper), INDEBUG_LDISASM_COMMA(nullptr) addr, argSize, retSize, EA_UNKNOWN, gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, - gcInfo.gcRegByrefSetCur, DebugInfo(), - callTarget, /* ireg */ - REG_NA, 0, 0, /* xreg, xmul, disp */ - false /* isJump */ + gcInfo.gcRegByrefSetCur, DebugInfo(), callTarget, /* ireg */ + REG_NA, 0, 0, /* xreg, xmul, disp */ + false /* isJump */ ); regMaskTP killMask = compiler->compHelperCallKillSet((CorInfoHelpFunc)helper); diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index 7c775203f75ee..d56c5ddd4ab44 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -123,8 +123,8 @@ CodeGen::CodeGen(Compiler* theCompiler) : CodeGenInterface(theCompiler) #endif // TARGET_AMD64 // Initialize the IP-mapping logic. - compiler->genIPmappingList = nullptr; - compiler->genIPmappingLast = nullptr; + compiler->genIPmappingList = nullptr; + compiler->genIPmappingLast = nullptr; compiler->genCallSite2DebugInfoMap = nullptr; /* Assume that we not fully interruptible */ @@ -10411,29 +10411,29 @@ void CodeGen::genIPmappingDisp(unsigned mappingNum, IPmappingDsc* ipMapping) switch (ipMapping->ipmdKind) { - case IPmappingDscKind::Prolog: - printf("PROLOG"); - break; - case IPmappingDscKind::Epilog: - printf("EPILOG"); - break; - case IPmappingDscKind::NoMapping: - printf("NO_MAP"); - break; - case IPmappingDscKind::Normal: - const ILLocation& loc = ipMapping->ipmdLoc; - Compiler::eeDispILOffs(loc.GetOffset()); - if (loc.IsStackEmpty()) - { - printf(" STACK_EMPTY"); - } + case IPmappingDscKind::Prolog: + printf("PROLOG"); + break; + case IPmappingDscKind::Epilog: + printf("EPILOG"); + break; + case IPmappingDscKind::NoMapping: + printf("NO_MAP"); + break; + case IPmappingDscKind::Normal: + const ILLocation& loc = ipMapping->ipmdLoc; + Compiler::eeDispILOffs(loc.GetOffset()); + if (loc.IsStackEmpty()) + { + printf(" STACK_EMPTY"); + } - if (loc.IsCall()) - { - printf(" CALL_INSTRUCTION"); - } + if (loc.IsCall()) + { + printf(" CALL_INSTRUCTION"); + } - break; + break; } printf(" "); @@ -10451,7 +10451,7 @@ void CodeGen::genIPmappingDisp(unsigned mappingNum, IPmappingDsc* ipMapping) void CodeGen::genIPmappingListDisp() { - unsigned mappingNum = 0; + unsigned mappingNum = 0; IPmappingDsc* ipMapping; for (ipMapping = compiler->genIPmappingList; ipMapping != nullptr; ipMapping = ipMapping->ipmdNext) @@ -10495,7 +10495,8 @@ void CodeGen::genIPmappingAdd(IPmappingDscKind kind, const DebugInfo& di, bool i // Ignore this one if it's the same IL location as the last one we saw. // Note that we'll let through two identical IL offsets if the flag bits // differ, or two identical "special" mappings (e.g., PROLOG). - if ((compiler->genIPmappingLast != nullptr) && (kind == compiler->genIPmappingLast->ipmdKind) && (di.GetLocation() == compiler->genIPmappingLast->ipmdLoc)) + if ((compiler->genIPmappingLast != nullptr) && (kind == compiler->genIPmappingLast->ipmdKind) && + (di.GetLocation() == compiler->genIPmappingLast->ipmdLoc)) { JITDUMP("genIPmappingAdd: ignoring duplicate IL offset 0x%x\n", di.GetLocation().GetOffset()); return; @@ -10507,8 +10508,8 @@ void CodeGen::genIPmappingAdd(IPmappingDscKind kind, const DebugInfo& di, bool i IPmappingDsc* addMapping = compiler->getAllocator(CMK_DebugInfo).allocate(1); addMapping->ipmdNativeLoc.CaptureLocation(GetEmitter()); - addMapping->ipmdKind = kind; - addMapping->ipmdLoc = di.GetLocation(); + addMapping->ipmdKind = kind; + addMapping->ipmdLoc = di.GetLocation(); addMapping->ipmdIsLabel = isLabel; addMapping->ipmdNext = nullptr; @@ -10549,14 +10550,15 @@ void CodeGen::genIPmappingAddToFront(IPmappingDscKind kind, const DebugInfo& di, return; } - noway_assert((kind != IPmappingDscKind::Normal) || (di.IsValid() && (di.GetLocation().GetOffset() <= compiler->info.compILCodeSize))); + noway_assert((kind != IPmappingDscKind::Normal) || + (di.IsValid() && (di.GetLocation().GetOffset() <= compiler->info.compILCodeSize))); /* Create a mapping entry and prepend it to the list */ IPmappingDsc* addMapping = compiler->getAllocator(CMK_DebugInfo).allocate(1); addMapping->ipmdNativeLoc.CaptureLocation(GetEmitter()); - addMapping->ipmdKind = kind; - addMapping->ipmdLoc = di.GetLocation(); + addMapping->ipmdKind = kind; + addMapping->ipmdLoc = di.GetLocation(); addMapping->ipmdIsLabel = isLabel; addMapping->ipmdNext = compiler->genIPmappingList; @@ -10636,10 +10638,10 @@ void CodeGen::genIPmappingGen() return; } - IPmappingDsc* tmpMapping; - IPmappingDsc* prevMapping; - unsigned mappingCnt; - UNATIVE_OFFSET lastNativeOfs; + IPmappingDsc* tmpMapping; + IPmappingDsc* prevMapping; + unsigned mappingCnt; + UNATIVE_OFFSET lastNativeOfs; /* First count the number of distinct mapping records */ @@ -10687,7 +10689,8 @@ void CodeGen::genIPmappingGen() // Leave prevMapping unchanged as tmpMapping is no longer valid tmpMapping->ipmdNativeLoc.Init(); } - else if ((tmpMapping->ipmdKind == IPmappingDscKind::Epilog) || (tmpMapping->ipmdKind == IPmappingDscKind::Normal && tmpMapping->ipmdLoc.GetOffset() == 0)) + else if ((tmpMapping->ipmdKind == IPmappingDscKind::Epilog) || + (tmpMapping->ipmdKind == IPmappingDscKind::Normal && tmpMapping->ipmdLoc.GetOffset() == 0)) { // counting for special cases: see below mappingCnt++; @@ -10744,7 +10747,8 @@ void CodeGen::genIPmappingGen() compiler->eeSetLIinfo(mappingCnt++, nextNativeOfs, tmpMapping->ipmdKind, tmpMapping->ipmdLoc); lastNativeOfs = nextNativeOfs; } - else if (tmpMapping->ipmdKind == IPmappingDscKind::Epilog || (tmpMapping->ipmdKind == IPmappingDscKind::Normal && tmpMapping->ipmdLoc.GetOffset() == 0)) + else if (tmpMapping->ipmdKind == IPmappingDscKind::Epilog || + (tmpMapping->ipmdKind == IPmappingDscKind::Normal && tmpMapping->ipmdLoc.GetOffset() == 0)) { // For the special case of an IL instruction with no body // followed by the epilog (say ret void immediately preceding diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index e6dd9a8e4f1df..1047b9eaf5b76 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -6083,7 +6083,7 @@ int Compiler::compCompileHelper(CORINFO_MODULE_HANDLE classPtr, assert((methAttr_Old & (~flagsToIgnore)) == (methAttr_New & (~flagsToIgnore))); #endif - info.compFlags = impInlineInfo->inlineCandidateInfo->methAttr; + info.compFlags = impInlineInfo->inlineCandidateInfo->methAttr; compInlineContext = impInlineInfo->inlineContext; } else diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 091a29704315f..2e9af6769b24c 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -2534,7 +2534,6 @@ struct IPmappingDsc bool ipmdIsLabel; // Can this code be a branch label? }; - /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX @@ -3092,12 +3091,12 @@ class Compiler CORINFO_METHOD_HANDLE handle, var_types type, GenTreeCall::Use* args, - const DebugInfo& di = DebugInfo()); + const DebugInfo& di = DebugInfo()); GenTreeCall* gtNewIndCallNode(GenTree* addr, var_types type, GenTreeCall::Use* args, - const DebugInfo& di = DebugInfo()); + const DebugInfo& di = DebugInfo()); GenTreeCall* gtNewHelperCallNode(unsigned helper, var_types type, GenTreeCall::Use* args = nullptr); @@ -3349,11 +3348,11 @@ class Compiler GenTreeOp* gtNewAssignNode(GenTree* dst, GenTree* src); - GenTree* gtNewTempAssign(unsigned tmp, - GenTree* val, - Statement** pAfterStmt = nullptr, - const DebugInfo& di = DebugInfo(), - BasicBlock* block = nullptr); + GenTree* gtNewTempAssign(unsigned tmp, + GenTree* val, + Statement** pAfterStmt = nullptr, + const DebugInfo& di = DebugInfo(), + BasicBlock* block = nullptr); GenTree* gtNewRefCOMfield(GenTree* objPtr, CORINFO_RESOLVED_TOKEN* pResolvedToken, @@ -4488,18 +4487,18 @@ class Compiler void impInsertStmtBefore(Statement* stmt, Statement* stmtBefore); Statement* impAppendTree(GenTree* tree, unsigned chkLevel, const DebugInfo& di); void impInsertTreeBefore(GenTree* tree, const DebugInfo& di, Statement* stmtBefore); - void impAssignTempGen(unsigned tmp, - GenTree* val, - unsigned curLevel, - Statement** pAfterStmt = nullptr, - const DebugInfo& di = DebugInfo(), - BasicBlock* block = nullptr); + void impAssignTempGen(unsigned tmp, + GenTree* val, + unsigned curLevel, + Statement** pAfterStmt = nullptr, + const DebugInfo& di = DebugInfo(), + BasicBlock* block = nullptr); void impAssignTempGen(unsigned tmpNum, GenTree* val, CORINFO_CLASS_HANDLE structHnd, unsigned curLevel, Statement** pAfterStmt = nullptr, - const DebugInfo& di = DebugInfo(), + const DebugInfo& di = DebugInfo(), BasicBlock* block = nullptr); Statement* impExtractLastStmt(); @@ -4513,14 +4512,14 @@ class Compiler CORINFO_CLASS_HANDLE structHnd, unsigned curLevel, Statement** pAfterStmt = nullptr, - const DebugInfo& di = DebugInfo(), + const DebugInfo& di = DebugInfo(), BasicBlock* block = nullptr); GenTree* impAssignStructPtr(GenTree* dest, GenTree* src, CORINFO_CLASS_HANDLE structHnd, unsigned curLevel, Statement** pAfterStmt = nullptr, - const DebugInfo& di = DebugInfo(), + const DebugInfo& di = DebugInfo(), BasicBlock* block = nullptr); GenTree* impGetStructAddr(GenTree* structVal, CORINFO_CLASS_HANDLE structHnd, unsigned curLevel, bool willDeref); @@ -6350,13 +6349,13 @@ class Compiler GenTree* fgGetStubAddrArg(GenTreeCall* call); unsigned fgGetArgTabEntryParameterLclNum(GenTreeCall* call, fgArgTabEntry* argTabEntry); void fgMorphRecursiveFastTailCallIntoLoop(BasicBlock* block, GenTreeCall* recursiveTailCall); - Statement* fgAssignRecursiveCallArgToCallerParam(GenTree* arg, - fgArgTabEntry* argTabEntry, - unsigned lclParamNum, - BasicBlock* block, + Statement* fgAssignRecursiveCallArgToCallerParam(GenTree* arg, + fgArgTabEntry* argTabEntry, + unsigned lclParamNum, + BasicBlock* block, const DebugInfo& callDI, - Statement* tmpAssignmentInsertionPoint, - Statement* paramAssignmentInsertionPoint); + Statement* tmpAssignmentInsertionPoint, + Statement* paramAssignmentInsertionPoint); GenTree* fgMorphCall(GenTreeCall* call); GenTree* fgExpandVirtualVtableCallTarget(GenTreeCall* call); void fgMorphCallInline(GenTreeCall* call, InlineResult* result); @@ -7382,7 +7381,7 @@ class Compiler } void considerGuardedDevirtualization(GenTreeCall* call, - IL_OFFSET ilOffset, + IL_OFFSET ilOffset, bool isInterface, CORINFO_METHOD_HANDLE baseMethod, CORINFO_CLASS_HANDLE baseClass, diff --git a/src/coreclr/jit/ee_il_dll.cpp b/src/coreclr/jit/ee_il_dll.cpp index c862821406a89..ebf1ea2945195 100644 --- a/src/coreclr/jit/ee_il_dll.cpp +++ b/src/coreclr/jit/ee_il_dll.cpp @@ -612,8 +612,8 @@ void Compiler::eeGetStmtOffsets() { // We do not get explicit boundaries for inlinees, only implicit ones. offsetsImplicit = impInlineRoot()->info.compStmtOffsetsImplicit; - offsetsCount = 0; - offsets = nullptr; + offsetsCount = 0; + offsets = nullptr; } else { @@ -970,7 +970,8 @@ void Compiler::eeSetLIcount(unsigned count) eeBoundariesCount = count; if (eeBoundariesCount) { - eeBoundaries = (ICorDebugInfo::OffsetMapping*)info.compCompHnd->allocateArray(eeBoundariesCount * sizeof(eeBoundaries[0])); + eeBoundaries = + (ICorDebugInfo::OffsetMapping*)info.compCompHnd->allocateArray(eeBoundariesCount * sizeof(eeBoundaries[0])); } else { @@ -978,40 +979,39 @@ void Compiler::eeSetLIcount(unsigned count) } } -void Compiler::eeSetLIinfo( - unsigned which, UNATIVE_OFFSET nativeOffset, IPmappingDscKind kind, const ILLocation& loc) +void Compiler::eeSetLIinfo(unsigned which, UNATIVE_OFFSET nativeOffset, IPmappingDscKind kind, const ILLocation& loc) { assert(opts.compDbgInfo); assert(eeBoundariesCount > 0 && eeBoundaries != nullptr); assert(which < eeBoundariesCount); eeBoundaries[which].nativeOffset = nativeOffset; - eeBoundaries[which].source = (ICorDebugInfo::SourceTypes)0; + eeBoundaries[which].source = (ICorDebugInfo::SourceTypes)0; switch (kind) { int source; - case IPmappingDscKind::Normal: - eeBoundaries[which].ilOffset = loc.GetOffset(); - source = loc.IsStackEmpty() ? ICorDebugInfo::STACK_EMPTY : 0; - source |= loc.IsCall() ? ICorDebugInfo::CALL_INSTRUCTION : 0; - eeBoundaries[which].source = (ICorDebugInfo::SourceTypes)source; - break; - case IPmappingDscKind::Prolog: - eeBoundaries[which].ilOffset = ICorDebugInfo::PROLOG; - eeBoundaries[which].source = ICorDebugInfo::STACK_EMPTY; - break; - case IPmappingDscKind::Epilog: - eeBoundaries[which].ilOffset = ICorDebugInfo::EPILOG; - eeBoundaries[which].source = ICorDebugInfo::STACK_EMPTY; - break; - case IPmappingDscKind::NoMapping: - eeBoundaries[which].ilOffset = ICorDebugInfo::NO_MAPPING; - eeBoundaries[which].source = ICorDebugInfo::STACK_EMPTY; - break; - default: - unreached(); + case IPmappingDscKind::Normal: + eeBoundaries[which].ilOffset = loc.GetOffset(); + source = loc.IsStackEmpty() ? ICorDebugInfo::STACK_EMPTY : 0; + source |= loc.IsCall() ? ICorDebugInfo::CALL_INSTRUCTION : 0; + eeBoundaries[which].source = (ICorDebugInfo::SourceTypes)source; + break; + case IPmappingDscKind::Prolog: + eeBoundaries[which].ilOffset = ICorDebugInfo::PROLOG; + eeBoundaries[which].source = ICorDebugInfo::STACK_EMPTY; + break; + case IPmappingDscKind::Epilog: + eeBoundaries[which].ilOffset = ICorDebugInfo::EPILOG; + eeBoundaries[which].source = ICorDebugInfo::STACK_EMPTY; + break; + case IPmappingDscKind::NoMapping: + eeBoundaries[which].ilOffset = ICorDebugInfo::NO_MAPPING; + eeBoundaries[which].source = ICorDebugInfo::STACK_EMPTY; + break; + default: + unreached(); } } diff --git a/src/coreclr/jit/emitarm.cpp b/src/coreclr/jit/emitarm.cpp index fff80213d9c96..70580cb134552 100644 --- a/src/coreclr/jit/emitarm.cpp +++ b/src/coreclr/jit/emitarm.cpp @@ -4678,7 +4678,7 @@ void emitter::emitIns_Call(EmitCallType callType, VARSET_VALARG_TP ptrVars, regMaskTP gcrefRegs, regMaskTP byrefRegs, - const DebugInfo& di /* = DebugInfo() */, + const DebugInfo& di /* = DebugInfo() */, regNumber ireg /* = REG_NA */, regNumber xreg /* = REG_NA */, unsigned xmul /* = 0 */, diff --git a/src/coreclr/jit/emitarm.h b/src/coreclr/jit/emitarm.h index 723bdcbd8c4bd..c93c15dff8ea9 100644 --- a/src/coreclr/jit/emitarm.h +++ b/src/coreclr/jit/emitarm.h @@ -328,12 +328,12 @@ void emitIns_Call(EmitCallType callType, VARSET_VALARG_TP ptrVars, regMaskTP gcrefRegs, regMaskTP byrefRegs, - const DebugInfo& di = DebugInfo(), - regNumber ireg = REG_NA, - regNumber xreg = REG_NA, - unsigned xmul = 0, - ssize_t disp = 0, - bool isJump = false); + const DebugInfo& di = DebugInfo(), + regNumber ireg = REG_NA, + regNumber xreg = REG_NA, + unsigned xmul = 0, + ssize_t disp = 0, + bool isJump = false); /***************************************************************************** * diff --git a/src/coreclr/jit/emitarm64.cpp b/src/coreclr/jit/emitarm64.cpp index 6dc8a1fd533a5..ddf47e8cbe831 100644 --- a/src/coreclr/jit/emitarm64.cpp +++ b/src/coreclr/jit/emitarm64.cpp @@ -8520,7 +8520,7 @@ void emitter::emitIns_Call(EmitCallType callType, VARSET_VALARG_TP ptrVars, regMaskTP gcrefRegs, regMaskTP byrefRegs, - const DebugInfo& di /* = DebugInfo() */, + const DebugInfo& di /* = DebugInfo() */, regNumber ireg /* = REG_NA */, regNumber xreg /* = REG_NA */, unsigned xmul /* = 0 */, diff --git a/src/coreclr/jit/fginline.cpp b/src/coreclr/jit/fginline.cpp index 21b44e1d60717..c55052eae0bee 100644 --- a/src/coreclr/jit/fginline.cpp +++ b/src/coreclr/jit/fginline.cpp @@ -938,7 +938,10 @@ void Compiler::fgInvokeInlineeCompiler(GenTreeCall* call, InlineResult* inlineRe // The inline context is part of debug info and must be created // before we start creating statements; we lazily create it as // late as possible, which is here. - pParam->inlineInfo->inlineContext = pParam->inlineInfo->InlineRoot->m_inlineStrategy->NewContext(pParam->inlineInfo->inlineCandidateInfo->inlinersContext, pParam->inlineInfo->iciStmt, pParam->inlineInfo->iciCall); + pParam->inlineInfo->inlineContext = + pParam->inlineInfo->InlineRoot->m_inlineStrategy + ->NewContext(pParam->inlineInfo->inlineCandidateInfo->inlinersContext, + pParam->inlineInfo->iciStmt, pParam->inlineInfo->iciCall); pParam->inlineInfo->argCnt = pParam->inlineCandidateInfo->methInfo.args.totalILArgs(); pParam->inlineInfo->tokenLookupContextHandle = pParam->inlineCandidateInfo->exactContextHnd; @@ -1271,7 +1274,7 @@ void Compiler::fgInsertInlineeBlocks(InlineInfo* pInlineInfo) DebugInfo di = iciStmt->GetDebugInfo().GetRoot(); if (di.IsValid()) { - block->bbCodeOffs = di.GetLocation().GetOffset(); + block->bbCodeOffs = di.GetLocation().GetOffset(); block->bbCodeOffsEnd = block->bbCodeOffs + 1; // TODO: is code size of 1 some magic number for inlining? } else @@ -1457,13 +1460,13 @@ void Compiler::fgInsertInlineeBlocks(InlineInfo* pInlineInfo) Statement* Compiler::fgInlinePrependStatements(InlineInfo* inlineInfo) { - BasicBlock* block = inlineInfo->iciBlock; - Statement* callStmt = inlineInfo->iciStmt; - const DebugInfo& callDI = callStmt->GetDebugInfo(); - Statement* postStmt = callStmt->GetNextStmt(); - Statement* afterStmt = callStmt; // afterStmt is the place where the new statements should be inserted after. - Statement* newStmt = nullptr; - GenTreeCall* call = inlineInfo->iciCall->AsCall(); + BasicBlock* block = inlineInfo->iciBlock; + Statement* callStmt = inlineInfo->iciStmt; + const DebugInfo& callDI = callStmt->GetDebugInfo(); + Statement* postStmt = callStmt->GetNextStmt(); + Statement* afterStmt = callStmt; // afterStmt is the place where the new statements should be inserted after. + Statement* newStmt = nullptr; + GenTreeCall* call = inlineInfo->iciCall->AsCall(); noway_assert(call->gtOper == GT_CALL); @@ -1577,8 +1580,7 @@ Statement* Compiler::fgInlinePrependStatements(InlineInfo* inlineInfo) // argTmpNum here since in-linee compiler instance // would have iterated over these and marked them // accordingly. - impAssignTempGen(tmpNum, argNode, structHnd, (unsigned)CHECK_SPILL_NONE, &afterStmt, callDI, - block); + impAssignTempGen(tmpNum, argNode, structHnd, (unsigned)CHECK_SPILL_NONE, &afterStmt, callDI, block); // We used to refine the temp type here based on // the actual arg, but we now do this up front, when @@ -1860,7 +1862,7 @@ void Compiler::fgInlineAppendStatements(InlineInfo* inlineInfo, BasicBlock* bloc JITDUMP("fgInlineAppendStatements: nulling out gc ref inlinee locals.\n"); Statement* callStmt = inlineInfo->iciStmt; - const DebugInfo& callDI = callStmt->GetDebugInfo(); + const DebugInfo& callDI = callStmt->GetDebugInfo(); CORINFO_METHOD_INFO* InlineeMethodInfo = InlineeCompiler->info.compMethodInfo; const unsigned lclCnt = InlineeMethodInfo->locals.numArgs; InlLclVarInfo* lclVarInfo = inlineInfo->lclVarInfo; diff --git a/src/coreclr/jit/fgprofile.cpp b/src/coreclr/jit/fgprofile.cpp index b9e50cb5cfa03..cc08d29bcb537 100644 --- a/src/coreclr/jit/fgprofile.cpp +++ b/src/coreclr/jit/fgprofile.cpp @@ -907,7 +907,7 @@ void Compiler::WalkSpanningTree(SpanningTreeVisitor* visitor) static int32_t EfficientEdgeCountBlockToKey(BasicBlock* block) { static const int IS_INTERNAL_BLOCK = (int32_t)0x80000000; - int32_t key = (int32_t)block->bbCodeOffs; + int32_t key = (int32_t)block->bbCodeOffs; // We may see empty BBJ_NONE BBF_INTERNAL blocks that were added // by fgNormalizeEH. // @@ -2009,7 +2009,8 @@ class EfficientEdgeCountReconstructor : public SpanningTreeVisitor } EdgeKey(BasicBlock* sourceBlock, BasicBlock* targetBlock) - : m_sourceKey(EfficientEdgeCountBlockToKey(sourceBlock)), m_targetKey(EfficientEdgeCountBlockToKey(targetBlock)) + : m_sourceKey(EfficientEdgeCountBlockToKey(sourceBlock)) + , m_targetKey(EfficientEdgeCountBlockToKey(targetBlock)) { } diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 576dda1d00757..e1ebfe8bed96e 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -467,7 +467,8 @@ void DebugInfo::Validate() const if (isValidOffs) { bool isValidStart = di.GetInlineContext()->GetILInstsSet()->bitVectTest(di.GetLocation().GetOffset()); - assert(isValidStart && "Detected invalid debug info: IL offset does not refer to the start of an IL instruction"); + assert(isValidStart && + "Detected invalid debug info: IL offset does not refer to the start of an IL instruction"); } else { @@ -503,7 +504,7 @@ bool DebugInfo::GetParent(DebugInfo* par) const // If this DebugInfo instance is valid, returns a DebugInfo instance // representing the call in the root function that eventually inlined the // statement this DebugInfo describes. -// +// // If this DebugInfo instance is invalid, returns an invalid DebugInfo instance. // DebugInfo DebugInfo::GetRoot() const @@ -6535,7 +6536,7 @@ GenTreeCall* Compiler::gtNewCallNode( // These get updated after call node is built. node->gtInlineObservation = InlineObservation::CALLEE_UNUSED_INITIAL; node->gtRawILOffset = BAD_IL_OFFSET; - node->gtInlineContext = compInlineContext; + node->gtInlineContext = compInlineContext; #endif // Spec: Managed Retval sequence points needs to be generated while generating debug info for debuggable code. @@ -8432,7 +8433,7 @@ GenTreeCall* Compiler::gtCloneExprCallHelper(GenTreeCall* tree, #if defined(DEBUG) || defined(INLINE_DATA) copy->gtInlineObservation = tree->gtInlineObservation; copy->gtRawILOffset = tree->gtRawILOffset; - copy->gtInlineContext = tree->gtInlineContext; + copy->gtInlineContext = tree->gtInlineContext; #endif copy->CopyOtherRegFlags(tree); diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index 40b3d003c5716..0f74a349b6b33 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -6143,17 +6143,12 @@ class InlineContext; class ILLocation { public: - ILLocation() - : m_offset(BAD_IL_OFFSET) - , m_isStackEmpty(false) - , m_isCall(false) + ILLocation() : m_offset(BAD_IL_OFFSET), m_isStackEmpty(false), m_isCall(false) { } ILLocation(IL_OFFSET offset, bool isStackEmpty, bool isCall) - : m_offset(offset) - , m_isStackEmpty(isStackEmpty) - , m_isCall(isCall) + : m_offset(offset), m_isStackEmpty(isStackEmpty), m_isCall(isCall) { } @@ -6197,22 +6192,19 @@ class ILLocation private: IL_OFFSET m_offset; - bool m_isStackEmpty : 1; - bool m_isCall : 1; + bool m_isStackEmpty : 1; + bool m_isCall : 1; }; // Represents debug information about a statement. class DebugInfo { public: - DebugInfo() - : m_inlineContext(nullptr) + DebugInfo() : m_inlineContext(nullptr) { } - DebugInfo(InlineContext* inlineContext, ILLocation loc) - : m_inlineContext(inlineContext) - , m_location(loc) + DebugInfo(InlineContext* inlineContext, ILLocation loc) : m_inlineContext(inlineContext), m_location(loc) { } @@ -6239,7 +6231,9 @@ class DebugInfo #ifdef DEBUG void Validate() const; #else - void Validate() const { } + void Validate() const + { + } #endif #ifdef DEBUG @@ -6266,7 +6260,7 @@ class DebugInfo private: InlineContext* m_inlineContext; - ILLocation m_location; + ILLocation m_location; }; // In LIR there are no longer statements so debug information is inserted linearly using these nodes. diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 4bdc519914ac4..a3c308bffa5b5 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -752,12 +752,12 @@ void Compiler::impInsertTreeBefore(GenTree* tree, const DebugInfo& di, Statement * curLevel is the stack level for which the spill to the temp is being done. */ -void Compiler::impAssignTempGen(unsigned tmp, - GenTree* val, - unsigned curLevel, - Statement** pAfterStmt, /* = NULL */ - const DebugInfo& di, /* = DebugInfo() */ - BasicBlock* block /* = NULL */ +void Compiler::impAssignTempGen(unsigned tmp, + GenTree* val, + unsigned curLevel, + Statement** pAfterStmt, /* = NULL */ + const DebugInfo& di, /* = DebugInfo() */ + BasicBlock* block /* = NULL */ ) { GenTree* asg = gtNewTempAssign(tmp, val); @@ -786,7 +786,7 @@ void Compiler::impAssignTempGen(unsigned tmpNum, CORINFO_CLASS_HANDLE structType, unsigned curLevel, Statement** pAfterStmt, /* = NULL */ - const DebugInfo& di, /* = DebugInfo() */ + const DebugInfo& di, /* = DebugInfo() */ BasicBlock* block /* = NULL */ ) { @@ -1196,7 +1196,7 @@ GenTree* Compiler::impAssignStruct(GenTree* dest, CORINFO_CLASS_HANDLE structHnd, unsigned curLevel, Statement** pAfterStmt, /* = nullptr */ - const DebugInfo& di, /* = DebugInfo() */ + const DebugInfo& di, /* = DebugInfo() */ BasicBlock* block /* = nullptr */ ) { @@ -1278,7 +1278,7 @@ GenTree* Compiler::impAssignStructPtr(GenTree* destAddr, CORINFO_CLASS_HANDLE structHnd, unsigned curLevel, Statement** pAfterStmt, /* = NULL */ - const DebugInfo& di, /* = DebugInfo() */ + const DebugInfo& di, /* = DebugInfo() */ BasicBlock* block /* = NULL */ ) { @@ -2830,7 +2830,7 @@ BasicBlock* Compiler::impPushCatchArgOnStack(BasicBlock* hndBlk, CORINFO_CLASS_H // here. Can we not just use impCurStmtOffsSet? Are we out of sync // here with the stack? impCurStmtDI = DebugInfo(compInlineContext, ILLocation(newBlk->bbCodeOffs, false, false)); - argStmt = gtNewStmt(argAsg, impCurStmtDI); + argStmt = gtNewStmt(argAsg, impCurStmtDI); } else { @@ -9377,7 +9377,8 @@ var_types Compiler::impImportCall(OPCODE opcode, const bool isLateDevirtualization = false; impDevirtualizeCall(call->AsCall(), pResolvedToken, &callInfo->hMethod, &callInfo->methodFlags, &callInfo->contextHandle, &exactContextHnd, isLateDevirtualization, isExplicitTailCall, - // Take care to pass raw IL offset here as the 'debug info' might be different for inlinees. + // Take care to pass raw IL offset here as the 'debug info' might be different for + // inlinees. rawILOffset); } @@ -11801,7 +11802,8 @@ void Compiler::impImportBlockCode(BasicBlock* block) impCurStmtOffsSet(opcodeOffs); } - assert(!impCurStmtDI.IsValid() || (nxtStmtOffs == BAD_IL_OFFSET) || (impCurStmtDI.GetLocation().GetOffset() <= nxtStmtOffs)); + assert(!impCurStmtDI.IsValid() || (nxtStmtOffs == BAD_IL_OFFSET) || + (impCurStmtDI.GetLocation().GetOffset() <= nxtStmtOffs)); } } @@ -17718,7 +17720,8 @@ bool Compiler::impReturnInstruction(int prefixFlags, OPCODE& opcode) else if (info.compRetBuffArg != BAD_VAR_NUM) { // Assign value to return buff (first param) - GenTree* retBuffAddr = gtNewLclvNode(info.compRetBuffArg, TYP_BYREF DEBUGARG(impCurStmtDI.GetLocation().GetOffset())); + GenTree* retBuffAddr = + gtNewLclvNode(info.compRetBuffArg, TYP_BYREF DEBUGARG(impCurStmtDI.GetLocation().GetOffset())); op2 = impAssignStructPtr(retBuffAddr, op2, retClsHnd, (unsigned)CHECK_SPILL_ALL); impAppendTree(op2, (unsigned)CHECK_SPILL_NONE, impCurStmtDI); @@ -19759,7 +19762,7 @@ void Compiler::impCheckCanInline(GenTreeCall* call, pInfo->initClassResult = initClassResult; pInfo->fncRetType = fncRetType; pInfo->exactContextNeedsRuntimeLookup = false; - pInfo->inlinersContext = pParam->pThis->compInlineContext; + pInfo->inlinersContext = pParam->pThis->compInlineContext; // Note exactContextNeedsRuntimeLookup is reset later on, // over in impMarkInlineCandidate. @@ -21227,7 +21230,7 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call, CORINFO_CONTEXT_HANDLE* pExactContextHandle, bool isLateDevirtualization, bool isExplicitTailCall, - IL_OFFSET ilOffset) + IL_OFFSET ilOffset) { assert(call != nullptr); assert(method != nullptr); @@ -21257,7 +21260,7 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call, // Record some info needed for the class profiling probe. // - pInfo->ilOffset = ilOffset; + pInfo->ilOffset = ilOffset; pInfo->probeIndex = info.compClassProbeCount++; pInfo->stubAddr = call->gtStubCallStubAddr; @@ -22131,7 +22134,7 @@ void Compiler::addFatPointerCandidate(GenTreeCall* call) // void Compiler::considerGuardedDevirtualization( GenTreeCall* call, - IL_OFFSET ilOffset, + IL_OFFSET ilOffset, bool isInterface, CORINFO_METHOD_HANDLE baseMethod, CORINFO_CLASS_HANDLE baseClass, diff --git a/src/coreclr/jit/inline.cpp b/src/coreclr/jit/inline.cpp index 5530cabc183c8..d3f7d37221b2c 100644 --- a/src/coreclr/jit/inline.cpp +++ b/src/coreclr/jit/inline.cpp @@ -419,13 +419,15 @@ void InlineContext::Dump(unsigned indent) if (offs == BAD_IL_OFFSET) { - printf("%*s[" FMT_INL_CTX " IL=???? TR=%06u %08X] [%s%s: %s%s%s%s] %s\n", indent, "", GetOrdinal(), m_TreeID, calleeToken, - inlineResult, inlineTarget, inlineReason, guarded, devirtualized, unboxed, calleeName); + printf("%*s[" FMT_INL_CTX " IL=???? TR=%06u %08X] [%s%s: %s%s%s%s] %s\n", indent, "", GetOrdinal(), + m_TreeID, calleeToken, inlineResult, inlineTarget, inlineReason, guarded, devirtualized, unboxed, + calleeName); } else { - printf("%*s[" FMT_INL_CTX " IL=%04d TR=%06u %08X] [%s%s: %s%s%s%s] %s\n", indent, "", GetOrdinal(), offs, m_TreeID, - calleeToken, inlineResult, inlineTarget, inlineReason, guarded, devirtualized, unboxed, calleeName); + printf("%*s[" FMT_INL_CTX " IL=%04d TR=%06u %08X] [%s%s: %s%s%s%s] %s\n", indent, "", GetOrdinal(), offs, + m_TreeID, calleeToken, inlineResult, inlineTarget, inlineReason, guarded, devirtualized, unboxed, + calleeName); } } @@ -1251,9 +1253,9 @@ InlineContext* InlineStrategy::NewContext(InlineContext* parentContext, Statemen InlineContext* context = new (m_Compiler, CMK_Inlining) InlineContext(this); context->m_InlineStrategy = this; - context->m_Parent = parentContext; - context->m_Sibling = parentContext->m_Child; - parentContext->m_Child = context; + context->m_Parent = parentContext; + context->m_Sibling = parentContext->m_Child; + parentContext->m_Child = context; // In debug builds we record inline contexts in all produced calls to be // able to show all failed inlines in the inline tree, even non-candidates. @@ -1263,8 +1265,8 @@ InlineContext* InlineStrategy::NewContext(InlineContext* parentContext, Statemen if (call->IsInlineCandidate()) { InlineCandidateInfo* info = call->gtInlineCandidateInfo; - context->m_Code = info->methInfo.ILCode; - context->m_ILSize = info->methInfo.ILCodeSize; + context->m_Code = info->methInfo.ILCode; + context->m_ILSize = info->methInfo.ILCodeSize; #ifdef DEBUG // All inline candidates should get their own statements that have @@ -1283,14 +1285,14 @@ InlineContext* InlineStrategy::NewContext(InlineContext* parentContext, Statemen // ldarg instruction. For SPGO purposes we should consider always storing // the exact offset of the call instruction which will be more precise. We // may consider storing the statement itself as well. - context->m_Location = stmt->GetDebugInfo().GetLocation(); + context->m_Location = stmt->GetDebugInfo().GetLocation(); context->m_Devirtualized = call->IsDevirtualized(); context->m_Guarded = call->IsGuarded(); context->m_Unboxed = call->IsUnboxed(); #if defined(DEBUG) || defined(INLINE_DATA) - context->m_TreeID = call->gtTreeID; - context->m_Callee = call->gtCallType == CT_INDIRECT ? nullptr : call->gtCallMethHnd; + context->m_TreeID = call->gtTreeID; + context->m_Callee = call->gtCallType == CT_INDIRECT ? nullptr : call->gtCallMethHnd; context->m_ActualCallOffset = call->gtRawILOffset; #endif @@ -1300,14 +1302,14 @@ InlineContext* InlineStrategy::NewContext(InlineContext* parentContext, Statemen void InlineContext::SetSucceeded(const InlineInfo* info) { assert(InlIsValidObservation(info->inlineResult->GetObservation())); - m_Observation = info->inlineResult->GetObservation(); + m_Observation = info->inlineResult->GetObservation(); m_ImportedILSize = info->inlineResult->GetImportedILSize(); - m_Success = true; + m_Success = true; #if defined(DEBUG) || defined(INLINE_DATA) - m_Policy = info->inlineResult->GetPolicy(); + m_Policy = info->inlineResult->GetPolicy(); m_CodeSizeEstimate = m_Policy->CodeSizeEstimate(); - m_Ordinal = m_InlineStrategy->m_InlineCount + 1; + m_Ordinal = m_InlineStrategy->m_InlineCount + 1; #endif m_InlineStrategy->NoteOutcome(this); @@ -1316,12 +1318,12 @@ void InlineContext::SetSucceeded(const InlineInfo* info) void InlineContext::SetFailed(const InlineResult* result) { assert(InlIsValidObservation(result->GetObservation())); - m_Observation = result->GetObservation(); + m_Observation = result->GetObservation(); m_ImportedILSize = result->GetImportedILSize(); - m_Success = false; + m_Success = false; #if defined(DEBUG) || defined(INLINE_DATA) - m_Policy = result->GetPolicy(); + m_Policy = result->GetPolicy(); m_CodeSizeEstimate = m_Policy->CodeSizeEstimate(); #endif diff --git a/src/coreclr/jit/inline.h b/src/coreclr/jit/inline.h index f61327da2bb2f..dd0c76efaf51e 100644 --- a/src/coreclr/jit/inline.h +++ b/src/coreclr/jit/inline.h @@ -589,7 +589,7 @@ struct InlineCandidateInfo : public GuardedDevirtualizationCandidateInfo CorInfoInitClassResult initClassResult; var_types fncRetType; bool exactContextNeedsRuntimeLookup; - InlineContext* inlinersContext; + InlineContext* inlinersContext; }; // InlArgInfo describes inline candidate argument properties. @@ -833,7 +833,7 @@ class InlineContext #endif // defined(DEBUG) || defined(INLINE_DATA) #ifdef DEBUG - FixedBitVect* m_ILInstsSet; // Set of offsets where instructions begin + FixedBitVect* m_ILInstsSet; // Set of offsets where instructions begin #endif }; diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index da1d3065f9e03..307c42e64944f 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -6385,7 +6385,8 @@ void Compiler::fgMorphCallInline(GenTreeCall* call, InlineResult* inlineResult) { #ifdef DEBUG // In debug we always put all inline attempts into the inline tree. - InlineContext* ctx = m_inlineStrategy->NewContext(call->gtInlineCandidateInfo->inlinersContext, fgMorphStmt, call); + InlineContext* ctx = + m_inlineStrategy->NewContext(call->gtInlineCandidateInfo->inlinersContext, fgMorphStmt, call); ctx->SetFailed(inlineResult); #endif } @@ -8701,8 +8702,8 @@ void Compiler::fgMorphRecursiveFastTailCallIntoLoop(BasicBlock* block, GenTreeCa // Transform recursive tail call into a loop. - Statement* earlyArgInsertionPoint = lastStmt; - const DebugInfo& callDI = lastStmt->GetDebugInfo(); + Statement* earlyArgInsertionPoint = lastStmt; + const DebugInfo& callDI = lastStmt->GetDebugInfo(); // Hoist arg setup statement for the 'this' argument. GenTreeCall::Use* thisArg = recursiveTailCall->gtCallThisArg; @@ -8919,13 +8920,13 @@ void Compiler::fgMorphRecursiveFastTailCallIntoLoop(BasicBlock* block, GenTreeCa // Return Value: // parameter assignment statement if one was inserted; nullptr otherwise. -Statement* Compiler::fgAssignRecursiveCallArgToCallerParam(GenTree* arg, - fgArgTabEntry* argTabEntry, - unsigned lclParamNum, - BasicBlock* block, +Statement* Compiler::fgAssignRecursiveCallArgToCallerParam(GenTree* arg, + fgArgTabEntry* argTabEntry, + unsigned lclParamNum, + BasicBlock* block, const DebugInfo& callDI, - Statement* tmpAssignmentInsertionPoint, - Statement* paramAssignmentInsertionPoint) + Statement* tmpAssignmentInsertionPoint, + Statement* paramAssignmentInsertionPoint) { // Call arguments should be assigned to temps first and then the temps should be assigned to parameters because // some argument trees may reference parameters directly. @@ -16000,9 +16001,9 @@ void Compiler::fgMergeBlockReturn(BasicBlock* block) noway_assert(ret->OperGet() == GT_RETURN); noway_assert(ret->gtGetOp1() != nullptr); - Statement* pAfterStatement = lastStmt; - const DebugInfo& di = lastStmt->GetDebugInfo(); - GenTree* tree = gtNewTempAssign(genReturnLocal, ret->gtGetOp1(), &pAfterStatement, di, block); + Statement* pAfterStatement = lastStmt; + const DebugInfo& di = lastStmt->GetDebugInfo(); + GenTree* tree = gtNewTempAssign(genReturnLocal, ret->gtGetOp1(), &pAfterStatement, di, block); if (tree->OperIsCopyBlkOp()) { tree = fgMorphCopyBlock(tree); diff --git a/src/coreclr/jit/rationalize.cpp b/src/coreclr/jit/rationalize.cpp index 35599b95c8e96..13e9705236c13 100644 --- a/src/coreclr/jit/rationalize.cpp +++ b/src/coreclr/jit/rationalize.cpp @@ -965,8 +965,8 @@ PhaseStatus Rationalizer::DoPhase() DebugInfo di = statement->GetDebugInfo().GetRoot(); if (di.IsValid()) { - GenTreeILOffset* ilOffset = new (comp, GT_IL_OFFSET) - GenTreeILOffset(di DEBUGARG(statement->GetLastILOffset())); + GenTreeILOffset* ilOffset = + new (comp, GT_IL_OFFSET) GenTreeILOffset(di DEBUGARG(statement->GetLastILOffset())); BlockRange().InsertBefore(statement->GetTreeList(), ilOffset); } From fe1088e0fd48abc1f97d2e6ba52d010fa8914bd0 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Tue, 9 Nov 2021 12:49:09 +0100 Subject: [PATCH 4/6] Address feedback --- src/coreclr/jit/CMakeLists.txt | 2 + src/coreclr/jit/codegencommon.cpp | 8 +- src/coreclr/jit/compiler.h | 1 + src/coreclr/jit/debuginfo.cpp | 145 ++++++++++++++++++++++++++++++ src/coreclr/jit/debuginfo.h | 135 ++++++++++++++++++++++++++++ src/coreclr/jit/gentree.cpp | 137 ---------------------------- src/coreclr/jit/gentree.h | 127 +------------------------- src/coreclr/jit/importer.cpp | 32 +++++-- 8 files changed, 315 insertions(+), 272 deletions(-) create mode 100644 src/coreclr/jit/debuginfo.cpp create mode 100644 src/coreclr/jit/debuginfo.h diff --git a/src/coreclr/jit/CMakeLists.txt b/src/coreclr/jit/CMakeLists.txt index e468b3a59c7c7..e3dbfc5993b3d 100644 --- a/src/coreclr/jit/CMakeLists.txt +++ b/src/coreclr/jit/CMakeLists.txt @@ -80,6 +80,7 @@ set( JIT_SOURCES codegenlinear.cpp compiler.cpp copyprop.cpp + debuginfo.cpp disasm.cpp earlyprop.cpp ee_il_dll.cpp @@ -178,6 +179,7 @@ if (CLR_CMAKE_TARGET_WIN32) compmemkind.h compphases.h dataflow.h + debuginfo.h decomposelongs.h disasm.h emit.h diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index d56c5ddd4ab44..a33b4b94a5994 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -10780,12 +10780,12 @@ void CodeGen::genIPmappingGen() if ((block->bbRefs > 1) && (stmt != nullptr)) { bool found = false; - if (stmt->GetDebugInfo() != BAD_IL_OFFSET) + DebugInfo rootInfo = stmt->GetDebugInfo().GetRoot(); + if (rootInfo.IsValid()) { - IL_OFFSET ilOffs = jitGetILoffs(stmt->GetDebugInfo()); - for (unsigned i = 0; i < eeBoundariesCount; ++i) + for (unsigned i = 0; i < compiler->eeBoundariesCount; ++i) { - if (eeBoundaries[i].ilOffset == ilOffs) + if (compiler->eeBoundaries[i].ilOffset == rootInfo.GetLocation().GetOffset()) { found = true; break; diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 2e9af6769b24c..35b8a891e89c3 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -27,6 +27,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #include "jitstd.h" #include "jithashtable.h" #include "gentree.h" +#include "debuginfo.h" #include "lir.h" #include "block.h" #include "inline.h" diff --git a/src/coreclr/jit/debuginfo.cpp b/src/coreclr/jit/debuginfo.cpp new file mode 100644 index 0000000000000..ed5edeb113da3 --- /dev/null +++ b/src/coreclr/jit/debuginfo.cpp @@ -0,0 +1,145 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "jitpch.h" +#include "debuginfo.h" + +#ifdef DEBUG +//------------------------------------------------------------------------ +// Dump: Print a textual representation of this ILLocation. +// +// Notes: +// For invalid ILLocations, we print '???'. +// Otherwise the offset and flags are printed in the format 0xabc[EC]. +// +void ILLocation::Dump() const +{ + if (!IsValid()) + { + printf("???"); + } + else + { + printf("0x%03X[", GetOffset()); + printf("%c", IsStackEmpty() ? 'E' : '-'); + printf("%c", IsCall() ? 'C' : '-'); + printf("]"); + } +} + +//------------------------------------------------------------------------ +// Dump: Print a textual representation of this DebugInfo. +// +// Parameters: +// recurse - print the full path back to the root, separated by arrows. +// +// Notes: +// The DebugInfo is printed in the format +// +// INL02 @ 0xabc[EC] +// +// Before '@' is the ordinal of the inline context, then comes the IL +// offset, and then comes the IL location flags (stack Empty, isCall). +// +// If 'recurse' is specified then dump the full DebugInfo path to the +// root in the format +// +// INL02 @ 0xabc[EC] <- INL01 @ 0x123[EC] <- ... <- INLRT @ 0x456[EC] +// +// with the left most entry being the inner most inlined statement. +void DebugInfo::Dump(bool recurse) const +{ + InlineContext* context = GetInlineContext(); + if (context != nullptr) + { + if (context->IsRoot()) + { + printf("INLRT @ "); + } + else if (context->GetOrdinal() != 0) + { + printf(FMT_INL_CTX " @ ", context->GetOrdinal()); + } + } + + GetLocation().Dump(); + + DebugInfo par; + if (recurse && GetParent(&par)) + { + printf(" <- "); + par.Dump(recurse); + } +} + +//------------------------------------------------------------------------ +// Validate: Validate this DebugInfo instance. +// +// Notes: +// This validates that if there is DebugInfo, then it looks sane by checking +// that the IL location correctly points to the beginning of an IL instruction. +// +void DebugInfo::Validate() const +{ + DebugInfo di = *this; + do + { + if (!di.IsValid()) + continue; + + bool isValidOffs = di.GetLocation().GetOffset() < di.GetInlineContext()->GetILSize(); + if (isValidOffs) + { + bool isValidStart = di.GetInlineContext()->GetILInstsSet()->bitVectTest(di.GetLocation().GetOffset()); + assert(isValidStart && + "Detected invalid debug info: IL offset does not refer to the start of an IL instruction"); + } + else + { + assert(!"Detected invalid debug info: IL offset is out of range"); + } + + } while (di.GetParent(&di)); +} +#endif + +//------------------------------------------------------------------------ +// GetParent: Get debug info for the parent statement that inlined the +// statement for this debug info. +// +// Parameters: +// parent [out] - Debug info for the location that inlined this statement. +// +// Return Value: +// True if the current debug info is valid and has a parent; otherwise false. +// On false return, the 'parent' parameter is unaffected. +// +bool DebugInfo::GetParent(DebugInfo* parent) const +{ + if ((m_inlineContext == nullptr) || m_inlineContext->IsRoot()) + return false; + + *parent = DebugInfo(m_inlineContext->GetParent(), m_inlineContext->GetLocation()); + return true; +} + +//------------------------------------------------------------------------ +// GetRoot: Get debug info for the statement in the root function that +// eventually led to this debug info through inlines. +// +// Return Value: +// If this DebugInfo instance is valid, returns a DebugInfo instance +// representing the call in the root function that eventually inlined the +// statement this DebugInfo describes. +// +// If this DebugInfo instance is invalid, returns an invalid DebugInfo instance. +// +DebugInfo DebugInfo::GetRoot() const +{ + DebugInfo result = *this; + while (result.GetParent(&result)) + { + } + + return result; +} diff --git a/src/coreclr/jit/debuginfo.h b/src/coreclr/jit/debuginfo.h new file mode 100644 index 0000000000000..304b258dc12af --- /dev/null +++ b/src/coreclr/jit/debuginfo.h @@ -0,0 +1,135 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#ifndef _DEBUGINFO_H_ +#define _DEBUGINFO_H_ + +#include "jit.h" + +class InlineContext; + +// Represents information about the location of an IL instruction. +class ILLocation +{ +public: + ILLocation() : m_offset(BAD_IL_OFFSET), m_isStackEmpty(false), m_isCall(false) + { + } + + ILLocation(IL_OFFSET offset, bool isStackEmpty, bool isCall) + : m_offset(offset), m_isStackEmpty(isStackEmpty), m_isCall(isCall) + { + } + + IL_OFFSET GetOffset() const + { + return m_offset; + } + + // Is this source location at a stack empty point? We need to be able to + // report this information back to the debugger since we only allow EnC + // transitions at stack empty points. + bool IsStackEmpty() const + { + return m_isStackEmpty; + } + + bool IsCall() const + { + return m_isCall; + } + + bool IsValid() const + { + return m_offset != BAD_IL_OFFSET; + } + + inline bool operator==(const ILLocation& other) const + { + return (m_offset == other.m_offset) && (m_isStackEmpty == other.m_isStackEmpty) && (m_isCall == other.m_isCall); + } + + inline bool operator!=(const ILLocation& other) const + { + return !(*this == other); + } + +#ifdef DEBUG + // Dump textual representation of this ILLocation to jitstdout. + void Dump() const; +#endif + +private: + IL_OFFSET m_offset; + bool m_isStackEmpty : 1; + bool m_isCall : 1; +}; + +// Represents debug information about a statement. +class DebugInfo +{ +public: + DebugInfo() : m_inlineContext(nullptr) + { + } + + DebugInfo(InlineContext* inlineContext, ILLocation loc) : m_inlineContext(inlineContext), m_location(loc) + { + } + + InlineContext* GetInlineContext() const + { + return m_inlineContext; + } + + ILLocation GetLocation() const + { + return m_location; + } + + // Retrieve information about the location that inlined this statement. + // Note that there can be associated parent information even when IsValid + // below returns false. + bool GetParent(DebugInfo* parent) const; + + // Get debug info in the root. If this debug info is in the root, then + // returns *this. Otherwise returns information of the call in the root + // that eventually produced this statement through inlines. + DebugInfo GetRoot() const; + +#ifdef DEBUG + void Validate() const; +#else + void Validate() const + { + } +#endif + +#ifdef DEBUG + // Dump textual representation of this DebugInfo to jitstdout. + void Dump(bool recurse) const; +#endif + + // Check if this debug info has both a valid inline context and valid + // location. + bool IsValid() const + { + return m_inlineContext != nullptr && m_location.IsValid(); + } + + inline bool operator==(const DebugInfo& other) const + { + return (m_inlineContext == other.m_inlineContext) && (m_location == other.m_location); + } + + inline bool operator!=(const DebugInfo& other) const + { + return !(*this == other); + } + +private: + InlineContext* m_inlineContext; + ILLocation m_location; +}; + +#endif diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index e1ebfe8bed96e..9d939d5ca6a33 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -380,143 +380,6 @@ bool GenTree::IsNodeProperlySized() const } #endif -#ifdef DEBUG -//------------------------------------------------------------------------ -// Dump: Print a textual representation of this ILLocation. -// -// Notes: -// For invalid ILLocations, we print '???'. -// Otherwise the offset and flags are printed in the format 0xabc[EC]. -// -void ILLocation::Dump() const -{ - if (!IsValid()) - { - printf("???"); - } - else - { - printf("0x%03X[", GetOffset()); - printf("%c", IsStackEmpty() ? 'E' : '-'); - printf("%c", IsCall() ? 'C' : '-'); - printf("]"); - } -} - -//------------------------------------------------------------------------ -// Dump: Print a textual representation of this DebugInfo. -// -// Parameters: -// recurse - print the full path back to the root, separated by arrows. -// -// Notes: -// The DebugInfo is printed in the format -// -// INL02 @ 0xabc[EC] -// -// Before '@' is the ordinal of the inline context, then comes the IL -// offset, and then comes the IL location flags (stack Empty, isCall). -// -// If 'recurse' is specified then dump the full DebugInfo path to the -// root in the format -// -// INL02 @ 0xabc[EC] <- INL01 @ 0x123[EC] <- ... <- INLRT @ 0x456[EC] -// -// with the left most entry being the inner most inlined statement. -void DebugInfo::Dump(bool recurse) const -{ - InlineContext* context = GetInlineContext(); - if (context != nullptr) - { - if (context->IsRoot()) - { - printf("INLRT @ "); - } - else if (context->GetOrdinal() != 0) - { - printf(FMT_INL_CTX " @ ", context->GetOrdinal()); - } - } - - GetLocation().Dump(); - - DebugInfo par; - if (recurse && GetParent(&par)) - { - printf(" <- "); - par.Dump(recurse); - } -} - -//------------------------------------------------------------------------ -// Validate: Validate this DebugInfo instance. -// -// Notes: -// This validates that if there is DebugInfo, then it looks sane by checking -// that the IL location correctly points to the beginning of an IL instruction. -// -void DebugInfo::Validate() const -{ - DebugInfo di = *this; - do - { - if (!di.IsValid()) - continue; - - bool isValidOffs = di.GetLocation().GetOffset() < di.GetInlineContext()->GetILSize(); - if (isValidOffs) - { - bool isValidStart = di.GetInlineContext()->GetILInstsSet()->bitVectTest(di.GetLocation().GetOffset()); - assert(isValidStart && - "Detected invalid debug info: IL offset does not refer to the start of an IL instruction"); - } - else - { - assert(!"Detected invalid debug info: IL offset is out of range"); - } - - } while (di.GetParent(&di)); -} -#endif - -//------------------------------------------------------------------------ -// GetParent: Get debug info for the parent statement that inlined the -// statement for this debug info. -// -// Return Value: -// True if the current debug info is valid and has a parent; otherwise false. -// On false return, the 'par' parameter is unaffected. -// -bool DebugInfo::GetParent(DebugInfo* par) const -{ - if ((m_inlineContext == nullptr) || m_inlineContext->IsRoot()) - return false; - - *par = DebugInfo(m_inlineContext->GetParent(), m_inlineContext->GetLocation()); - return true; -} - -//------------------------------------------------------------------------ -// GetRoot: Get debug info for the statement in the root function that -// eventually led to this debug info through inlines. -// -// Return Value: -// If this DebugInfo instance is valid, returns a DebugInfo instance -// representing the call in the root function that eventually inlined the -// statement this DebugInfo describes. -// -// If this DebugInfo instance is invalid, returns an invalid DebugInfo instance. -// -DebugInfo DebugInfo::GetRoot() const -{ - DebugInfo result = *this; - while (result.GetParent(&result)) - { - } - - return result; -} - //------------------------------------------------------------------------ // ReplaceWith: replace this with the src node. The source must be an isolated node // and cannot be used after the replacement. diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index 0f74a349b6b33..ec3d178a95115 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -28,6 +28,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #include "simd.h" #include "namedintrinsiclist.h" #include "layout.h" +#include "debuginfo.h" // Debugging GenTree is much easier if we add a magic virtual function to make the debugger able to figure out what type // it's got. This is enabled by default in DEBUG. To enable it in RET builds (temporarily!), you need to change the @@ -6137,132 +6138,6 @@ struct GenTreeRetExpr : public GenTree #endif }; -class InlineContext; - -// Represents information about the location of an IL instruction. -class ILLocation -{ -public: - ILLocation() : m_offset(BAD_IL_OFFSET), m_isStackEmpty(false), m_isCall(false) - { - } - - ILLocation(IL_OFFSET offset, bool isStackEmpty, bool isCall) - : m_offset(offset), m_isStackEmpty(isStackEmpty), m_isCall(isCall) - { - } - - IL_OFFSET GetOffset() const - { - return m_offset; - } - - // Is this source location at a stack empty point? We need to be able to - // report this information back to the debugger since we only allow EnC - // transitions at stack empty points. - bool IsStackEmpty() const - { - return m_isStackEmpty; - } - - bool IsCall() const - { - return m_isCall; - } - - bool IsValid() const - { - return m_offset != BAD_IL_OFFSET; - } - - inline bool operator==(const ILLocation& other) const - { - return (m_offset == other.m_offset) && (m_isStackEmpty == other.m_isStackEmpty) && (m_isCall == other.m_isCall); - } - - inline bool operator!=(const ILLocation& other) const - { - return !(*this == other); - } - -#ifdef DEBUG - // Dump textual representation of this ILLocation to jitstdout. - void Dump() const; -#endif - -private: - IL_OFFSET m_offset; - bool m_isStackEmpty : 1; - bool m_isCall : 1; -}; - -// Represents debug information about a statement. -class DebugInfo -{ -public: - DebugInfo() : m_inlineContext(nullptr) - { - } - - DebugInfo(InlineContext* inlineContext, ILLocation loc) : m_inlineContext(inlineContext), m_location(loc) - { - } - - InlineContext* GetInlineContext() const - { - return m_inlineContext; - } - - ILLocation GetLocation() const - { - return m_location; - } - - // Retrieve information about the location that inlined this statement. - // Note that there can be associated parent information even when IsValid - // below returns false. - bool GetParent(DebugInfo* par) const; - - // Get debug info in the root. If this debug info is in the root, then - // returns *this. Otherwise returns information of the call in the root - // that eventually produced this statement through inlines. - DebugInfo GetRoot() const; - -#ifdef DEBUG - void Validate() const; -#else - void Validate() const - { - } -#endif - -#ifdef DEBUG - // Dump textual representation of this DebugInfo to jitstdout. - void Dump(bool recurse) const; -#endif - - // Check if this debug info has both a valid inline context and valid - // location. - bool IsValid() const - { - return m_inlineContext != nullptr && m_location.IsValid(); - } - - inline bool operator==(const DebugInfo& other) const - { - return (m_inlineContext == other.m_inlineContext) && (m_location == other.m_location); - } - - inline bool operator!=(const DebugInfo& other) const - { - return !(*this == other); - } - -private: - InlineContext* m_inlineContext; - ILLocation m_location; -}; - // In LIR there are no longer statements so debug information is inserted linearly using these nodes. struct GenTreeILOffset : public GenTree { diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index a3c308bffa5b5..900c232b89587 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -2886,6 +2886,19 @@ GenTree* Compiler::impCloneExpr(GenTree* tree, return gtNewLclvNode(temp, type); } + +//------------------------------------------------------------------------ +// impCreateDIWithCurrentStackInfo: Create a DebugInfo instance with the +// specified IL offset and 'is call' bit, using the current stack to determine +// whether to set the 'stack empty' bit. +// +// Arguments: +// offs - the IL offset for the DebugInfo +// isCall - whether the created DebugInfo should have the IsCall bit set +// +// Return Value: +// The DebugInfo instance. +// DebugInfo Compiler::impCreateDIWithCurrentStackInfo(IL_OFFSET offs, bool isCall) { assert(offs != BAD_IL_OFFSET); @@ -2894,11 +2907,20 @@ DebugInfo Compiler::impCreateDIWithCurrentStackInfo(IL_OFFSET offs, bool isCall) return DebugInfo(compInlineContext, ILLocation(offs, isStackEmpty, isCall)); } -/***************************************************************************** - * Remember the IL offset (including stack-empty info) for the trees we will - * generate next. - */ - +//------------------------------------------------------------------------ +// impCurStmtOffsSet: Set the "current debug info" to attach to statements that +// we are generating next. +// +// Arguments: +// offs - the IL offset +// +// Remarks: +// This function will be called in the main IL processing loop when it is +// determined that we have reached a location in the IL stream for which we +// want to report debug information. This is the main way we determine which +// statements to report debug info for to the EE: for other statements, they +// will have no debug information attached. +// inline void Compiler::impCurStmtOffsSet(IL_OFFSET offs) { if (offs == BAD_IL_OFFSET) From 19c1174d0cbc92b6172c371b3d33cd7c723e3801 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Tue, 9 Nov 2021 12:57:40 +0100 Subject: [PATCH 5/6] Fix bad merge --- src/coreclr/jit/inline.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/inline.cpp b/src/coreclr/jit/inline.cpp index a0c3f7dee892b..7cfebb6ad40ad 100644 --- a/src/coreclr/jit/inline.cpp +++ b/src/coreclr/jit/inline.cpp @@ -430,13 +430,13 @@ void InlineContext::Dump(bool verbose, unsigned indent) { if (offs == BAD_IL_OFFSET) { - printf("%*s[%u IL=???? TR=%06u %08X] [%s%s: %s%s%s%s] %s\n", indent, "", m_Ordinal, m_TreeID, + printf("%*s[" FMT_INL_CTX " IL=???? TR=%06u %08X] [%s%s: %s%s%s%s] %s\n", indent, "", m_Ordinal, m_TreeID, calleeToken, inlineResult, inlineTarget, inlineReason, guarded, devirtualized, unboxed, calleeName); } else { - printf("%*s[%u IL=%04d TR=%06u %08X] [%s%s: %s%s%s%s] %s\n", indent, "", m_Ordinal, + printf("%*s[" FMT_INL_CTX " IL=%04d TR=%06u %08X] [%s%s: %s%s%s%s] %s\n", indent, "", m_Ordinal, offs, m_TreeID, calleeToken, inlineResult, inlineTarget, inlineReason, guarded, devirtualized, unboxed, calleeName); } From 652b8a1a1cf4aa2f938f44d05b0082a3f8177abb Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Tue, 9 Nov 2021 13:08:54 +0100 Subject: [PATCH 6/6] Run jit-format --- src/coreclr/jit/importer.cpp | 1 - src/coreclr/jit/inline.cpp | 10 +++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index b7b2fc6d38719..78177c7057a6d 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -2886,7 +2886,6 @@ GenTree* Compiler::impCloneExpr(GenTree* tree, return gtNewLclvNode(temp, type); } - //------------------------------------------------------------------------ // impCreateDIWithCurrentStackInfo: Create a DebugInfo instance with the // specified IL offset and 'is call' bit, using the current stack to determine diff --git a/src/coreclr/jit/inline.cpp b/src/coreclr/jit/inline.cpp index 7cfebb6ad40ad..546eb2aa376f9 100644 --- a/src/coreclr/jit/inline.cpp +++ b/src/coreclr/jit/inline.cpp @@ -430,15 +430,15 @@ void InlineContext::Dump(bool verbose, unsigned indent) { if (offs == BAD_IL_OFFSET) { - printf("%*s[" FMT_INL_CTX " IL=???? TR=%06u %08X] [%s%s: %s%s%s%s] %s\n", indent, "", m_Ordinal, m_TreeID, - calleeToken, inlineResult, inlineTarget, inlineReason, guarded, devirtualized, unboxed, + printf("%*s[" FMT_INL_CTX " IL=???? TR=%06u %08X] [%s%s: %s%s%s%s] %s\n", indent, "", m_Ordinal, + m_TreeID, calleeToken, inlineResult, inlineTarget, inlineReason, guarded, devirtualized, unboxed, calleeName); } else { - printf("%*s[" FMT_INL_CTX " IL=%04d TR=%06u %08X] [%s%s: %s%s%s%s] %s\n", indent, "", m_Ordinal, - offs, m_TreeID, calleeToken, inlineResult, inlineTarget, inlineReason, guarded, - devirtualized, unboxed, calleeName); + printf("%*s[" FMT_INL_CTX " IL=%04d TR=%06u %08X] [%s%s: %s%s%s%s] %s\n", indent, "", m_Ordinal, offs, + m_TreeID, calleeToken, inlineResult, inlineTarget, inlineReason, guarded, devirtualized, unboxed, + calleeName); } } else