diff --git a/src/coreclr/interpreter/compiler.cpp b/src/coreclr/interpreter/compiler.cpp index 6f10b84a6ea6f2..4766bc9d6f508d 100644 --- a/src/coreclr/interpreter/compiler.cpp +++ b/src/coreclr/interpreter/compiler.cpp @@ -935,8 +935,15 @@ int32_t* InterpCompiler::EmitCodeIns(int32_t *ip, InterpInst *ins, TArraydata[0]))->resumeInfo.DiagnosticIP = (size_t)startIp - (size_t)m_pMethodCode; + InterpAsyncSuspendData* pSuspendData = (InterpAsyncSuspendData*)GetDataItemAtIndex(ins->data[0]); + // Store as native offset from the start of the method (including the InterpByteCodeStart header) + // so it matches OffsetMapping.nativeOffset and AsyncSuspensionPoint.DiagnosticNativeOffset. + pSuspendData->resumeInfo.DiagnosticIP = sizeof(InterpByteCodeStart) + ((size_t)startIp - (size_t)m_pMethodCode); + + // SUSPEND occupies 3 int32_t slots; the matching RESUME starts immediately after. + size_t resumeOffset = sizeof(InterpByteCodeStart) + ((size_t)(startIp - m_pMethodCode) + 3) * sizeof(int32_t); + assert(pSuspendData->suspensionPointIndex == m_suspensionPointIPOffsets.GetSize()); + m_suspensionPointIPOffsets.Add((int32_t)resumeOffset); } if (opcode == INTOP_SWITCH) @@ -1261,6 +1268,40 @@ void InterpCompiler::EmitCode() } m_compHnd->setBoundaries(m_methodInfo->ftn, m_ILToNativeMapSize, m_pILToNativeMap); m_pILToNativeMap = NULL; // Ownership transferred to the VM + + ReportAsyncDebugInfo(); +} + +void InterpCompiler::ReportAsyncDebugInfo() +{ + int32_t numSuspensionPoints = m_asyncDebugSuspensionPoints.GetSize(); + if (numSuspensionPoints == 0) + return; + + for (int32_t i = 0; i < numSuspensionPoints; i++) + { + m_asyncDebugSuspensionPoints.GetUnderlyingArray()[i].DiagnosticNativeOffset = + (uint32_t)m_asyncSuspendDataItems.Get(i)->resumeInfo.DiagnosticIP; + } + + ICorDebugInfo::AsyncInfo asyncInfo; + asyncInfo.NumSuspensionPoints = (uint32_t)numSuspensionPoints; + + size_t spBytes = (size_t)numSuspensionPoints * sizeof(ICorDebugInfo::AsyncSuspensionPoint); + ICorDebugInfo::AsyncSuspensionPoint* hostSuspensionPoints = + (ICorDebugInfo::AsyncSuspensionPoint*)m_compHnd->allocateArray(spBytes); + memcpy(hostSuspensionPoints, m_asyncDebugSuspensionPoints.GetUnderlyingArray(), spBytes); + + int32_t numVars = m_asyncDebugContinuationVars.GetSize(); + ICorDebugInfo::AsyncContinuationVarInfo* hostVars = NULL; + if (numVars > 0) + { + size_t vBytes = (size_t)numVars * sizeof(ICorDebugInfo::AsyncContinuationVarInfo); + hostVars = (ICorDebugInfo::AsyncContinuationVarInfo*)m_compHnd->allocateArray(vBytes); + memcpy(hostVars, m_asyncDebugContinuationVars.GetUnderlyingArray(), vBytes); + } + + m_compHnd->reportAsyncDebugInfo(&asyncInfo, hostSuspensionPoints, hostVars, (uint32_t)numVars); } #ifdef FEATURE_INTERPRETER @@ -1909,6 +1950,12 @@ void InterpCompiler::PrepareInterpMethod() count++; // Include the terminator entry m_methodDataBuilder.AllocateIntervalMap(count); } + + if (m_asyncSuspendDataItems.GetSize() > 0) + { + m_methodDataBuilder.AllocateInSection(InterpMethodDataSection::IntervalMaps, + (uint32_t)m_asyncSuspendDataItems.GetSize() * (uint32_t)sizeof(int32_t)); + } } int32_t* InterpCompiler::GetCode(int32_t *pCodeSize) @@ -2028,10 +2075,35 @@ InterpMethod* InterpCompiler::FinalizeMethodData(void* baseAddressRW, void* base dstDataRW->zeroedLocalsIntervals = dstMapRX; currentIntervalMapOffset += mapSize; } - + currentAsyncOffset += sizeof(InterpAsyncSuspendData); } + { + int32_t numSuspensionPoints = m_asyncSuspendDataItems.GetSize(); + InterpMethod* pInterpMethodRW = (InterpMethod*)(rwBase + interpMethodOffset); + if (numSuspensionPoints > 0) + { + uint32_t tableSize = (uint32_t)numSuspensionPoints * (uint32_t)sizeof(int32_t); + assert(currentIntervalMapOffset + tableSize <= intervalMapsSectionEnd); + + int32_t* tableRW = (int32_t*)(rwBase + currentIntervalMapOffset); + int32_t* tableRX = (int32_t*)(rxBase + currentIntervalMapOffset); + for (int32_t i = 0; i < numSuspensionPoints; i++) + { + tableRW[i] = m_suspensionPointIPOffsets.Get(i); + } + pInterpMethodRW->numSuspensionPoints = numSuspensionPoints; + pInterpMethodRW->suspensionPointIPOffsets = tableRX; + currentIntervalMapOffset += tableSize; + } + else + { + pInterpMethodRW->numSuspensionPoints = 0; + pInterpMethodRW->suspensionPointIPOffsets = NULL; + } + } + assert(currentAsyncOffset <= asyncSuspendDataSectionEnd); assert(currentIntervalMapOffset <= intervalMapsSectionEnd); @@ -2089,6 +2161,9 @@ InterpCompiler::InterpCompiler(COMP_HANDLE compHnd, , m_leavesTable(GetMemPoolAllocator(IMK_EHClause)) , m_dataItems(GetMemPoolAllocator(IMK_DataItem)) , m_asyncSuspendDataItems(GetMemPoolAllocator(IMK_DataItem)) + , m_suspensionPointIPOffsets(GetMemPoolAllocator(IMK_DataItem)) + , m_asyncDebugSuspensionPoints(GetMemPoolAllocator(IMK_DataItem)) + , m_asyncDebugContinuationVars(GetMemPoolAllocator(IMK_DataItem)) , m_dataItemAsyncSuspendRefs(GetMemPoolAllocator(IMK_DataItem)) , m_initLocals(false) , m_unmanagedCallersOnly(false) @@ -5783,6 +5858,7 @@ void InterpCompiler::EmitSuspend(const CORINFO_CALL_INFO &callInfo, Continuation TArray liveVars(GetMemPoolAllocator(IMK_AsyncSuspend)); TArray varsToZero(GetMemPoolAllocator(IMK_AsyncSuspend)); + int32_t debugVarsStartCount = m_asyncDebugContinuationVars.GetSize(); // Step 2: Handle live stack vars (excluding return value) int32_t stackDepth = (int32_t)(m_pStackPointer - m_pStackBase); @@ -5935,6 +6011,14 @@ void InterpCompiler::EmitSuspend(const CORINFO_CALL_INFO &callInfo, Continuation INTERP_DUMP("Allocate var %d at data offset %d (size %d) (offsetFromStartOfReturnValue = %d)\n", var, currentOffset, size, currentOffset - returnValueDataStartOffset); + if (var >= 0 && var < m_numILVars && var != returnValueVar) + { + ICorDebugInfo::AsyncContinuationVarInfo varInf; + varInf.VarNumber = (uint32_t)var; + varInf.Offset = (uint32_t)(currentOffset + OFFSETOF__CORINFO_Continuation__data); + m_asyncDebugContinuationVars.Add(varInf); + } + if (interpType == InterpTypeO) { // Mark as GC reference @@ -5990,7 +6074,9 @@ void InterpCompiler::EmitSuspend(const CORINFO_CALL_INFO &callInfo, Continuation } InterpAsyncSuspendData* suspendData = (InterpAsyncSuspendData*)AllocMethodData(sizeof(InterpAsyncSuspendData)); + int32_t suspensionPointIndex = m_asyncSuspendDataItems.GetSize(); m_asyncSuspendDataItems.Add(suspendData); + suspendData->suspensionPointIndex = suspensionPointIndex; CORINFO_ASYNC_INFO asyncInfo; m_compHnd->getAsyncInfo(&asyncInfo); @@ -6015,6 +6101,15 @@ void InterpCompiler::EmitSuspend(const CORINFO_CALL_INFO &callInfo, Continuation suspendData->resumeInfo.DiagnosticIP = (size_t)NULL; suspendData->methodStartIP = 0; // This is filled in by logic later in emission once we know the final address of the method suspendData->continuationArgOffset = m_pVars[m_continuationArgIndex].offset; + + { + ICorDebugInfo::AsyncSuspensionPoint sp; + sp.DiagnosticNativeOffset = (uint32_t)suspendData->resumeInfo.DiagnosticIP; + sp.NumContinuationVars = (uint32_t)(m_asyncDebugContinuationVars.GetSize() - debugVarsStartCount); + m_asyncDebugSuspensionPoints.Add(sp); + assert(m_asyncDebugSuspensionPoints.GetSize() - 1 == suspensionPointIndex); + } + suspendData->asyncMethodReturnType = NULL; switch (m_methodInfo->args.retType) { diff --git a/src/coreclr/interpreter/compiler.h b/src/coreclr/interpreter/compiler.h index 485c55503ea977..e1a09403484de6 100644 --- a/src/coreclr/interpreter/compiler.h +++ b/src/coreclr/interpreter/compiler.h @@ -707,6 +707,10 @@ class InterpCompiler TArray m_asyncSuspendDataItems; + TArray m_suspensionPointIPOffsets; + TArray m_asyncDebugSuspensionPoints; + TArray m_asyncDebugContinuationVars; + // Tracks which data items contain pointers to async suspend data // First = data item index, Second = index into m_asyncSuspendDataItems struct DataItemAsyncSuspendRef @@ -1045,6 +1049,7 @@ class InterpCompiler void AllocOffsets(); int32_t ComputeCodeSize(); void EmitCode(); + void ReportAsyncDebugInfo(); int32_t* EmitBBCode(int32_t *ip, InterpBasicBlock *bb, TArray *relocs); int32_t* EmitCodeIns(int32_t *ip, InterpInst *pIns, TArray *relocs); void PatchRelocations(TArray *relocs); diff --git a/src/coreclr/interpreter/inc/interpretershared.h b/src/coreclr/interpreter/inc/interpretershared.h index c696140b97b073..7119131c255c24 100644 --- a/src/coreclr/interpreter/inc/interpretershared.h +++ b/src/coreclr/interpreter/inc/interpretershared.h @@ -48,6 +48,11 @@ struct InterpMethod bool publishSecretStubParam; int32_t codeSize; // size in int32_t slots + // Maps Continuation.State (suspension-point index) to the byte offset of the matching + // INTOP_HANDLE_CONTINUATION_RESUME opcode from InterpByteCodeStart. + int32_t numSuspensionPoints; + int32_t* suspensionPointIPOffsets; + #ifdef INTERPRETER_COMPILER_INTERNAL InterpMethod( CORINFO_METHOD_HANDLE methodHnd, int32_t argsSize, int32_t allocaSize, @@ -66,6 +71,8 @@ struct InterpMethod this->unmanagedCallersOnly = unmanagedCallersOnly; this->publishSecretStubParam = publishSecretStubParam; this->codeSize = codeSize; + this->numSuspensionPoints = 0; + this->suspensionPointIPOffsets = NULL; pCallStub = NULL; } #endif @@ -218,6 +225,9 @@ struct InterpAsyncSuspendData COMPILER_SHARED_TYPE(CORINFO_METHOD_HANDLE, DPTR(MethodDesc), captureSyncContextMethod); COMPILER_SHARED_TYPE(CORINFO_METHOD_HANDLE, DPTR(MethodDesc), restoreExecutionContextMethod); COMPILER_SHARED_TYPE(CORINFO_METHOD_HANDLE, DPTR(MethodDesc), restoreContextsOnSuspensionMethod); + + // Written into Continuation.State at suspend; matches the JIT encoding. + int32_t suspensionPointIndex; }; #endif diff --git a/src/coreclr/vm/interpexec.cpp b/src/coreclr/vm/interpexec.cpp index 7fe0a4b3f00955..824267be2ff0e7 100644 --- a/src/coreclr/vm/interpexec.cpp +++ b/src/coreclr/vm/interpexec.cpp @@ -4528,7 +4528,7 @@ do \ } } - continuation->SetState((int32_t)((uint8_t*)ip - (uint8_t*)pFrame->startIp)); + continuation->SetState(pAsyncSuspendData->suspensionPointIndex); _ASSERTE(pAsyncSuspendData->methodStartIP != 0); continuation->SetResumeInfo(&pAsyncSuspendData->resumeInfo); pInterpreterFrame->SetContinuation(continuation); @@ -4591,9 +4591,11 @@ do \ _ASSERTE(pInterpreterFrame->GetContinuation() == NULL); if (continuation != NULL) { - // A continuation is present, begin the restoration process + // State is the suspension-point index; map it to the resume IP. int32_t state = continuation->GetState(); - ip = (int32_t*)((uint8_t*)pFrame->startIp + state); + _ASSERTE(state >= 0 && state < pMethod->numSuspensionPoints); + _ASSERTE(pMethod->suspensionPointIPOffsets != NULL); + ip = (int32_t*)((uint8_t*)pFrame->startIp + pMethod->suspensionPointIPOffsets[state]); // Now we have an IP to where we should resume execution. This should be an INTOP_HANDLE_CONTINUATION_RESUME opcode // And before it should be an INTOP_HANDLE_CONTINUATION_SUSPEND opcode