Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/coreclr/debug/daccess/dacdbiimpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1255,6 +1255,10 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetNativeCodeInfo(VMPTR_Assembly
if (pCodeInfo->m_rgCodeRegions[kHot].pAddress != (CORDB_ADDRESS)NULL)
{
pCodeInfo->isInstantiatedGeneric = pMethodDesc->HasClassOrMethodInstantiation();
{
EECodeInfo codeInfo(PINSTRToPCODE((TADDR)pCodeInfo->m_rgCodeRegions[kHot].pAddress));
pCodeInfo->isInterpreted = codeInfo.IsInterpretedCode();
}
LookupEnCVersions(pModule,
pCodeInfo->vmNativeCodeMethodDescToken,
functionToken,
Expand Down Expand Up @@ -1335,6 +1339,7 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetNativeCodeInfoForAddr(CORDB_AD
vmMethodDesc.SetHostPtr(codeInfo.GetMethodDesc());
MethodDesc* pMethodDesc = vmMethodDesc.GetDacPtr();
pCodeInfo->isInstantiatedGeneric = pMethodDesc->HasClassOrMethodInstantiation();
pCodeInfo->isInterpreted = codeInfo.IsInterpretedCode();
pCodeInfo->vmNativeCodeMethodDescToken = vmMethodDesc;

SIZE_T unusedLatestEncVersion;
Expand Down
110 changes: 107 additions & 3 deletions src/coreclr/debug/di/module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,38 @@
#include "pedecoder.h"
#include "memorystreams.h"

#ifdef FEATURE_INTERPRETER
#include "intopsshared.h"

static const uint8_t s_interpOpLenTableForDI[] =
{
#define OPDEF(a,b,c,d,e,f) c,
#include "intops.def"
#undef OPDEF
};

static int GetInterpreterCallOpcodeLengthInSlots(int32_t opcode)
{
switch (opcode)
{
case INTOP_CALL:
case INTOP_CALL_NULLCHECK:
case INTOP_CALL_TAIL:
case INTOP_CALLDELEGATE:
case INTOP_CALLDELEGATE_TAIL:
case INTOP_CALLI:
case INTOP_CALLI_TAIL:
case INTOP_CALLVIRT:
case INTOP_CALLVIRT_TAIL:
case INTOP_CALL_PINVOKE:
_ASSERTE(opcode >= 0 && (size_t)opcode < ARRAY_SIZE(s_interpOpLenTableForDI));
return s_interpOpLenTableForDI[opcode];
default:
return -1;
}
}
#endif // FEATURE_INTERPRETER

//---------------------------------------------------------------------------------------
// Initialize a new CordbModule around a Module in the target.
//
Expand Down Expand Up @@ -3905,14 +3937,22 @@ HRESULT CordbVariableHome::GetOffset(LONG *pOffset)
//-----------------------------------------------------------------------------
CordbNativeCode::CordbNativeCode(CordbFunction * pFunction,
const NativeCodeFunctionData * pJitData,
BOOL fIsInstantiatedGeneric)
BOOL fIsInstantiatedGeneric,
BOOL fIsInterpreted)
: CordbCode(pFunction, (UINT_PTR)pJitData->m_rgCodeRegions[kHot].pAddress, pJitData->encVersion, FALSE),
m_vmNativeCodeMethodDescToken(pJitData->vmNativeCodeMethodDescToken),
m_fCodeAvailable(TRUE),
m_fIsInstantiatedGeneric(fIsInstantiatedGeneric != FALSE)
#ifdef FEATURE_INTERPRETER
, m_fIsInterpreted(fIsInterpreted != FALSE)
#endif
{
_ASSERTE(GetVersion() >= CorDB_DEFAULT_ENC_FUNCTION_VERSION);

#ifndef FEATURE_INTERPRETER
(void)fIsInterpreted;
#endif

for (CodeBlobRegion region = kHot; region < MAX_REGIONS; ++region)
{
m_rgCodeRegions[region] = pJitData->m_rgCodeRegions[region];
Expand Down Expand Up @@ -4356,6 +4396,19 @@ HRESULT CordbNativeCode::EnumerateVariableHomes(ICorDebugVariableHomeEnum **ppEn

int CordbNativeCode::GetCallInstructionLength(BYTE *ip, ULONG32 count)
{
#ifdef FEATURE_INTERPRETER
if (IsInterpreted())
{
if (count < sizeof(int32_t))
return -1;
int32_t interpOpcode;
memcpy(&interpOpcode, ip, sizeof(int32_t));
int interpSlots = GetInterpreterCallOpcodeLengthInSlots(interpOpcode);
if (interpSlots <= 0)
return -1;
return interpSlots * (int)sizeof(int32_t);
}
#endif // FEATURE_INTERPRETER
#if defined(TARGET_ARM) || defined(TARGET_RISCV64)
if (Is32BitInstruction(*(WORD*)ip))
return 4;
Expand Down Expand Up @@ -5028,7 +5081,9 @@ HRESULT CordbNativeCode::GetReturnValueLiveOffsetImpl(Instantiation *currentInst
// Get the length of the call instruction.
int offset = GetCallInstructionLength(nativeBuffer, fetched);
if (offset == -1)
return E_UNEXPECTED; // Could not decode instruction, this should never happen.
{
return E_UNEXPECTED; // Could not decode instruction, this should never happen.
}

pOffsets[found] = pMap->nativeStartOffset + offset;
}
Expand All @@ -5051,6 +5106,55 @@ HRESULT CordbNativeCode::GetReturnValueLiveOffsetImpl(Instantiation *currentInst
return S_OK;
}

#ifdef FEATURE_INTERPRETER
HRESULT CordbNativeCode::GetInterpreterCallDvarOffset(ULONG32 ILoffset, ULONG32 postCallNativeOffset, int32_t* pDvarOffset)
{
if (pDvarOffset == NULL)
return E_INVALIDARG;

HRESULT hr = S_OK;
SequencePoints *pSP = GetSequencePoints();
DebuggerILToNativeMap *pMap = pSP->GetCallsiteMapAddr();

for (ULONG32 i = 0; i < pSP->GetCallsiteEntryCount() && pMap; ++i, pMap++)
{
if (pMap->ilOffset != ILoffset ||
(pMap->source & ICorDebugInfo::CALL_INSTRUCTION) != ICorDebugInfo::CALL_INSTRUCTION)
{
continue;
}

// slot 0 = opcode, slot 1 = dvar byte offset
BYTE nativeBuffer[2 * sizeof(int32_t)];
ULONG32 fetched = 0;
hr = GetCode(pMap->nativeStartOffset,
pMap->nativeStartOffset + ARRAY_SIZE(nativeBuffer),
ARRAY_SIZE(nativeBuffer),
nativeBuffer,
&fetched);
if (FAILED(hr) || fetched < ARRAY_SIZE(nativeBuffer))
continue;
Comment thread
kotlarmilos marked this conversation as resolved.

int32_t interpOpcode;
memcpy(&interpOpcode, nativeBuffer, sizeof(int32_t));
int interpSlots = GetInterpreterCallOpcodeLengthInSlots(interpOpcode);
if (interpSlots <= 0)
return E_UNEXPECTED;

ULONG32 expectedPostCall = pMap->nativeStartOffset + interpSlots * (ULONG32)sizeof(int32_t);
if (expectedPostCall != postCallNativeOffset)
continue;

int32_t dvarOffset;
memcpy(&dvarOffset, nativeBuffer + sizeof(int32_t), sizeof(int32_t));
*pDvarOffset = dvarOffset;
return S_OK;
}

return E_UNEXPECTED;
}
#endif // FEATURE_INTERPRETER

//-----------------------------------------------------------------------------
// Creates a CordbNativeCode (if it's not already created) and adds it to the
// hash table of CordbNativeCode instances belonging to this module.
Expand Down Expand Up @@ -5105,7 +5209,7 @@ CordbNativeCode * CordbModule::LookupOrCreateNativeCode(mdMethodDef methodToken,
pFunction->InitParentClassOfFunction();

// First, create a new CordbNativeCode instance--we'll need this to make the CordbJITInfo instance
pNativeCode = new (nothrow)CordbNativeCode(pFunction, &codeInfo, codeInfo.isInstantiatedGeneric != 0);
pNativeCode = new (nothrow)CordbNativeCode(pFunction, &codeInfo, codeInfo.isInstantiatedGeneric != 0, codeInfo.isInterpreted != 0);
_ASSERTE(pNativeCode != NULL);

m_nativeCodeTable.AddBaseOrThrow(pNativeCode);
Expand Down
24 changes: 20 additions & 4 deletions src/coreclr/debug/di/rspriv.h
Original file line number Diff line number Diff line change
Expand Up @@ -5771,7 +5771,8 @@ class CordbNativeCode : public CordbCode,
public:
CordbNativeCode(CordbFunction * pFunction,
const NativeCodeFunctionData * pJitData,
BOOL fIsInstantiatedGeneric);
BOOL fIsInstantiatedGeneric,
BOOL fIsInterpreted);
#ifdef _DEBUG
const char * DbgGetName() { return "CordbNativeCode"; };
#endif // _DEBUG
Expand Down Expand Up @@ -5842,6 +5843,11 @@ class CordbNativeCode : public CordbCode,
// Worker function for GetReturnValueLiveOffset.
HRESULT GetReturnValueLiveOffsetImpl(Instantiation *currentInstantiation, ULONG32 ILoffset, ULONG32 bufferSize, ULONG32 *pFetched, ULONG32 *pOffsets);

#ifdef FEATURE_INTERPRETER
// Returns the FP-relative offset of an interpreter call's destination var.
HRESULT GetInterpreterCallDvarOffset(ULONG32 ILoffset, ULONG32 postCallNativeOffset, int32_t* pDvarOffset);
#endif

// get total size of the code including both hot and cold regions
ULONG32 GetSize();

Expand Down Expand Up @@ -5876,6 +5882,13 @@ class CordbNativeCode : public CordbCode,
return m_fIsInstantiatedGeneric != 0;
}

#ifdef FEATURE_INTERPRETER
BOOL IsInterpreted()
{
return m_fIsInterpreted != 0;
}
#endif

// Determine whether we have initialized the native variable and
// sequence point offsets
BOOL IsNativeCodeValid ()
Expand Down Expand Up @@ -5929,6 +5942,9 @@ class CordbNativeCode : public CordbCode,
bool m_fCodeAvailable; // true iff the code has been jitted but not pitched

bool m_fIsInstantiatedGeneric; // true iff this is an instantiated generic
#ifdef FEATURE_INTERPRETER
bool m_fIsInterpreted; // true iff compiled by the CoreCLR interpreter
#endif

// information in the following two classes tracks native offsets and is initialized on demand.

Expand Down Expand Up @@ -6764,11 +6780,11 @@ typedef std::function<HRESULT(DWORD index, ICorDebugValue** ppValue)> ValueGette
class CordbValueEnum : public CordbBase, public ICorDebugValueEnum
{
public:
CordbValueEnum(CordbProcess* pProcess,
UINT maxCount,
CordbValueEnum(CordbProcess* pProcess,
UINT maxCount,
ValueGetter valueGetter,
NeuterList* pNeuterList);

virtual ~CordbValueEnum();
virtual void Neuter();

Expand Down
18 changes: 18 additions & 0 deletions src/coreclr/debug/di/rsthread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8847,6 +8847,24 @@ HRESULT CordbJITILFrame::GetReturnValueForILOffsetImpl(ULONG32 ILoffset, ICorDeb
CordbType *pType = 0;
IfFailRet(BuildInstantiationForCallsite(GetModule(), types, inst, &m_genericArgs, targetClass, genericSig));
IfFailRet(CordbType::SigToType(GetModule(), &methodSig, &inst, &pType));

#ifdef FEATURE_INTERPRETER
if (pCode->IsInterpreted())
{
// Interpreter return values live in an FP-relative dvar, not a register.
int32_t dvarOffset = 0;
IfFailRet(pCode->GetInterpreterCallDvarOffset(ILoffset, currentOffset, &dvarOffset));
ICorDebugInfo::NativeVarInfo synthetic = {};
synthetic.startOffset = currentOffset;
synthetic.endOffset = currentOffset;
synthetic.varNumber = 0;
synthetic.loc.vlType = ICorDebugInfo::VLT_STK;
synthetic.loc.vlStk.vlsBaseReg = ICorDebugInfo::REGNUM_FP;
synthetic.loc.vlStk.vlsOffset = dvarOffset;
return GetNativeVariable(pType, &synthetic, ppReturnValue);
Comment thread
kotlarmilos marked this conversation as resolved.
}
#endif // FEATURE_INTERPRETER

return GetReturnValueForType(pType, ppReturnValue);
}

Expand Down
3 changes: 3 additions & 0 deletions src/coreclr/debug/inc/dacdbistructures.h
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,9 @@ class MSLAYOUT NativeCodeFunctionData
// indicates whether the function is a generic function, or a method inside a generic class (or both).
BOOL isInstantiatedGeneric;

// indicates whether the function is compiled by the CoreCLR interpreter rather than the JIT.
BOOL isInterpreted;

// MethodDesc for the function
VMPTR_MethodDesc vmNativeCodeMethodDescToken;

Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/debug/inc/dacdbistructures.inl
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,7 @@ NativeCodeFunctionData::NativeCodeFunctionData(DebuggerIPCE_JITFuncData * source

// copy the other function information
isInstantiatedGeneric = source->isInstantiatedGeneric;
isInterpreted = FALSE;
vmNativeCodeMethodDescToken = source->vmNativeCodeMethodDescToken;
encVersion = source->enCVersion;
}
Expand All @@ -587,6 +588,7 @@ inline
void NativeCodeFunctionData::Clear()
{
isInstantiatedGeneric = FALSE;
isInterpreted = FALSE;
encVersion = CorDB_DEFAULT_ENC_FUNCTION_VERSION;
for (CodeBlobRegion region = kHot; region < MAX_REGIONS; ++region)
{
Expand Down
35 changes: 26 additions & 9 deletions src/coreclr/interpreter/compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,11 @@ InterpInst* InterpCompiler::NewIns(int opcode, int dataLen)
// This is the first instruction we are emitting for this IL offset and the stack is empty, which
// implies the IL stack is empty too.
ins->flags |= INTERP_INST_FLAG_EMPTY_IL_STACK;
m_isFirstInstForEmptyILStack = false;
// Skip emit-nop instructions so the flag falls through to the first real IR.
if (!InterpOpIsEmitNop(opcode))
{
m_isFirstInstForEmptyILStack = false;
}
}
m_pLastNewIns = ins;
return ins;
Expand Down Expand Up @@ -1054,9 +1058,8 @@ int32_t* InterpCompiler::EmitCodeIns(int32_t *ip, InterpInst *ins, TArray<Reloc*
if (ilOffset < (uint32_t)m_ILCodeSizeFromILHeader)
{
uint32_t nativeOffset = ConvertOffset(ins->nativeOffset);
// Only emit mapping entries at IL offsets where the evaluation stack is empty
if ((ins->flags & INTERP_INST_FLAG_EMPTY_IL_STACK) &&
((m_ILToNativeMapSize == 0) || (m_pILToNativeMap[m_ILToNativeMapSize - 1].ilOffset != ilOffset)))
// Emit one entry per IL offset, tagging STACK_EMPTY only when the IL stack was empty.
if ((m_ILToNativeMapSize == 0) || (m_pILToNativeMap[m_ILToNativeMapSize - 1].ilOffset != ilOffset))
Comment thread
kotlarmilos marked this conversation as resolved.
{
// This code assumes that instructions for the same IL offset are emitted in a single run without
// any other IL offsets in between and that they don't repeat again after the run ends.
Expand All @@ -1072,13 +1075,23 @@ int32_t* InterpCompiler::EmitCodeIns(int32_t *ip, InterpInst *ins, TArray<Reloc*
m_pNativeMapIndexToILOffset[ilOffset] = m_ILToNativeMapSize;
#endif // DEBUG

// Since we can have at most one entry per IL offset,
// this map cannot possibly use more entries than the size of the IL code
assert(m_ILToNativeMapSize < m_ILCodeSize);
assert(m_ILToNativeMapSize < m_ILToNativeMapCapacity);

m_pILToNativeMap[m_ILToNativeMapSize].ilOffset = ilOffset;
m_pILToNativeMap[m_ILToNativeMapSize].nativeOffset = nativeOffset;
m_pILToNativeMap[m_ILToNativeMapSize].source = (ins->flags & INTERP_INST_FLAG_EMPTY_IL_STACK)
? ICorDebugInfo::STACK_EMPTY
: ICorDebugInfo::SOURCE_TYPE_INVALID;
m_ILToNativeMapSize++;
}

if (ins->flags & INTERP_INST_FLAG_DBG_CALL_INSTRUCTION)
{
assert(m_ILToNativeMapSize < m_ILToNativeMapCapacity);

m_pILToNativeMap[m_ILToNativeMapSize].ilOffset = ilOffset;
m_pILToNativeMap[m_ILToNativeMapSize].nativeOffset = nativeOffset;
m_pILToNativeMap[m_ILToNativeMapSize].source = ICorDebugInfo::STACK_EMPTY;
m_pILToNativeMap[m_ILToNativeMapSize].source = ICorDebugInfo::CALL_INSTRUCTION;
m_ILToNativeMapSize++;
}
}
Expand Down Expand Up @@ -1178,7 +1191,9 @@ void InterpCompiler::EmitCode()

// This will eventually be freed by the VM, using freeArray.
// If we fail before handing them to the VM, there is logic in the InterpCompiler destructor to free it.
m_pILToNativeMap = (ICorDebugInfo::OffsetMapping*)m_compHnd->allocateArray(m_ILCodeSize * sizeof(ICorDebugInfo::OffsetMapping));
// Each IL offset may produce up to two entries (STACK_EMPTY plus CALL_INSTRUCTION).
m_ILToNativeMapCapacity = m_ILCodeSize * 2;
m_pILToNativeMap = (ICorDebugInfo::OffsetMapping*)m_compHnd->allocateArray(m_ILToNativeMapCapacity * sizeof(ICorDebugInfo::OffsetMapping));

// For each BB, compute the number of EH clauses that overlap with it.
for (unsigned int i = 0; i < getEHcount(m_methodInfo); i++)
Expand Down Expand Up @@ -5564,6 +5579,8 @@ void InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN* pConstrainedToken, bool re
m_pLastNewIns->SetSVar(CALL_ARGS_SVAR);

m_pLastNewIns->flags |= INTERP_INST_FLAG_CALL;
if (!tailcall && !newObj)
m_pLastNewIns->flags |= INTERP_INST_FLAG_DBG_CALL_INSTRUCTION;
m_pLastNewIns->info.pCallInfo = new (getAllocator(IMK_CallInfo)) InterpCallInfo();
m_pLastNewIns->info.pCallInfo->pCallArgs = callArgs;

Expand Down
5 changes: 4 additions & 1 deletion src/coreclr/interpreter/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,9 @@ enum InterpInstFlags
// Flag used internally by the var offset allocator
INTERP_INST_FLAG_ACTIVE_CALL = 0x02,
// The IL stack is empty at this instruction
INTERP_INST_FLAG_EMPTY_IL_STACK = 0x04
INTERP_INST_FLAG_EMPTY_IL_STACK = 0x04,
// Marks a user IL call site reportable to ICorDebugCode3::GetReturnValueLiveOffset
INTERP_INST_FLAG_DBG_CALL_INSTRUCTION = 0x08
};

struct InterpInst
Expand Down Expand Up @@ -856,6 +858,7 @@ class InterpCompiler
int32_t* m_pNativeMapIndexToILOffset = NULL;
#endif
int32_t m_ILToNativeMapSize = 0;
int32_t m_ILToNativeMapCapacity = 0;

InterpBasicBlock* AllocBB(int32_t ilOffset);
InterpBasicBlock* GetBB(int32_t ilOffset);
Expand Down
Loading