Skip to content
Merged
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
67 changes: 67 additions & 0 deletions docs/design/datacontracts/ExecutionManager.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ public struct CodeBlockHandle
void GetMethodRegionInfo(CodeBlockHandle codeInfoHandle, out uint hotSize, out TargetPointer coldStart, out uint coldSize);
// Attempt to get the method desc of an entrypoint
TargetPointer NonVirtualEntry2MethodDesc(TargetCodePointer entrypoint);
// Map a method entry point to the code start that diagnostics report and that resolves through
// GetCodeBlockHandle. Other addresses are returned unchanged.
TargetCodePointer GetDiagnosticCodeStartFromEntryPoint(TargetCodePointer entryPoint);

// Gets the unwind info of the code block at the specified code pointer
TargetPointer GetUnwindInfo(CodeBlockHandle codeInfoHandle);
Expand Down Expand Up @@ -216,6 +219,10 @@ virtual-IP ranges bypass native thunk classification.
| `ExceptionLookupTableEntry` | *(type size)* | `uint32` | Size of an exception lookup table entry in bytes |
| `ExceptionLookupTableEntry` | `ExceptionInfoRVA` | `uint32` | RVA of the exception clause data |
| `ExceptionLookupTableEntry` | `MethodStartRVA` | `uint32` | RVA of the method start |
| `FunctionTableIndexRangeSection` | `MinFunctionTableIndex` | `uint32` | First runtime-global shared function-table index owned by the R2R module |
| `FunctionTableIndexRangeSection` | `Next` | `pointer` | Pointer to the next registered WASM R2R function-table range |
| `FunctionTableIndexRangeSection` | `NumRuntimeFunctions` | `uint32` | Number of consecutive RUNTIME_FUNCTION entries owned by the R2R module |
| `FunctionTableIndexRangeSection` | `R2RModule` | `pointer` | Pointer to the Module that owns this function-table range |
| `HashMap` | `Buckets` | `pointer` | Pointer to the buckets of a `HashMap` |
| `HostCodeHeap` | `BaseAddress` | `pointer` | Pointer to the base of the committed memory region |
| `HostCodeHeap` | `CurrentAddress` | `pointer` | Pointer to the last available committed byte in the region |
Expand All @@ -226,7 +233,12 @@ virtual-IP ranges bypass native thunk classification.
| `InterpreterRealCodeHeader` | `JitEHInfo` | `pointer` | Pointer to the `EE_ILEXCEPTION` containing exception clauses for interpreter code |
| `InterpreterRealCodeHeader` | `MethodDesc` | `pointer` | Pointer to the corresponding `MethodDesc` for interpreter code |
| `LoaderCodeHeap` | `LoaderHeap` | `pointer` | Offset of the embedded `ExplicitControlLoaderHeap` within the `LoaderCodeHeap` object; adding this to the object's base address yields the loader heap address |
| `MethodDesc` | `CodeData` | `pointer` | Pointer to per-method code data containing entry-point and code-versioning state |
| `MethodDesc` | `InterpreterCode` | `pointer` | Pointer to the method's `InterpByteCodeStart`, or the poison value 1 if the method will never be interpreted (only defined if `FEATURE_INTERPRETER` is enabled) |
| `MethodDescCodeData` | `TemporaryEntryPoint` | `CodePointer` | Temporary code entry point used before the method has a stable entry point |
| `Module` | `ReadyToRunInfo` | `pointer` | Pointer to the module's ReadyToRun information |
| `PortableEntryPoint` | `ActualCode` | `pointer` | Native code for the entrypoint; on WebAssembly R2R code this is a function-table index (only defined if `FeaturePortableEntrypoints` is enabled) |
| `PortableEntryPoint` | `Flags` | `int32` | Portable entrypoint flags; `0x4` means the interpreter entrypoint is preferred over `ActualCode` (only defined if `FeaturePortableEntrypoints` is enabled) |
| `PortableEntryPoint` | `MethodDesc` | `pointer` | Method desc of portable entrypoint (only defined if `FeaturePortableEntrypoints` is enabled) |
| `R2RExceptionClause` | *(type size)* | `uint32` | Size of a ReadyToRun exception clause in bytes |
| `R2RExceptionClause` | `ClassToken` | `uint32` | Union field: ClassToken or FilterOffset |
Expand Down Expand Up @@ -287,6 +299,7 @@ virtual-IP ranges bypass native thunk classification.
| --- | --- | --- |
| `EEJitManagerAddress` | `pointer` | Address of the global pointer to the EEJitManager instance (read a TargetPointer from this address to obtain the instance address) |
| `ExecutionManagerCodeRangeMapAddress` | `pointer` | Pointer to the global RangeSectionMap |
| `FunctionTableIndexRangeList` | `pointer` | Pointer to the head pointer of the registered WASM R2R function-table range list |
| `GCInfoVersion` | `uint32` | JITted code GCInfo version |
| `HashMapSlotsPerBucket` | `uint32` | Number of slots in each bucket of a `HashMap` |
| `HashMapValueMask` | `uint64` | Bitmask used when storing values in a `HashMap` |
Expand Down Expand Up @@ -491,6 +504,60 @@ TargetPointer IExecutionManager.NonVirtualEntry2MethodDesc(TargetCodePointer ent
```


`GetDiagnosticCodeStartFromEntryPoint` mirrors the native `GetDiagnosticCodeStartFromEntryPoint` (`src/coreclr/vm/precode.cpp`). Without portable entrypoints it maps an interpreter precode to its bytecode through `PrecodeStubs.GetInterpreterCodeFromInterpreterPrecodeIfPresent`. With portable entrypoints (WebAssembly), an entry point is a `PortableEntryPoint` rather than code: an interpreted method maps to its `InterpByteCodeStart`, and a native ReadyToRun method maps its function-table index to the synthetic virtual IP registered in the virtual-IP range list, so that the result resolves through `GetCodeBlockHandle`.

```csharp
// Constants from native code
const int PortableEntryPointPrefersInterpreterEntryPoint = 0x4; // PortableEntryPoint::kPrefersInterpreterEntryPoint
const ulong InterpreterCodePoison = 1; // INTERPRETER_CODE_POISON

TargetCodePointer IExecutionManager.GetDiagnosticCodeStartFromEntryPoint(TargetCodePointer entryPoint)
{
if (entryPoint == TargetCodePointer.Null)
return entryPoint;

if (!FeatureFlags.IsEnabled(RuntimeFeature.PortableEntrypoints))
return PrecodeStubs.GetInterpreterCodeFromInterpreterPrecodeIfPresent(entryPoint);

// On any read failure, entryPoint is returned unchanged.
// An address in a code range (including a Wasm R2R virtual IP) is already a code start.
if (/* range section found for entryPoint - see RangeSectionMap and the virtual-IP range list */)
return entryPoint;

Data.PortableEntryPoint pep = // read PortableEntryPoint at entryPoint
if (pep.MethodDesc == TargetPointer.Null)
return entryPoint;

Data.MethodDesc md = // read MethodDesc at pep.MethodDesc
if (md.InterpreterCode is TargetPointer interpreterCode && interpreterCode != TargetPointer.Null && interpreterCode != InterpreterCodePoison)
return new TargetCodePointer(interpreterCode);

// Native R2R portable entry points store a Wasm function-table index in ActualCode. As in native code,
// this applies only to the method's own portable entry point, which is currently its temporary entry point.
if (pep.ActualCode == TargetPointer.Null
|| (pep.Flags & PortableEntryPointPrefersInterpreterEntryPoint) != 0
|| md.CodeData == TargetPointer.Null
|| /* MethodDescCodeData.TemporaryEntryPoint at md.CodeData */ != entryPoint)
{
return entryPoint;
}

return TryGetWasmVirtualIPFromFunctionTableIndex((uint)pep.ActualCode, out ulong virtualIP)
? new TargetCodePointer(virtualIP)
: entryPoint;
}

// Mirrors ExecutionManager::GetWasmVirtualIPFromFunctionTableIndex. Also used by the WebAssembly stack walk.
bool TryGetWasmVirtualIPFromFunctionTableIndex(uint functionTableIndex, out ulong virtualIP)
{
// Walk the FunctionTableIndexRangeSection list headed by *FunctionTableIndexRangeList (with cycle
// detection) to the section where MinFunctionTableIndex <= functionTableIndex < MinFunctionTableIndex + NumRuntimeFunctions.
// Starting at RuntimeFunctions[functionTableIndex - MinFunctionTableIndex] of the section's
// R2RModule's ReadyToRunInfo, step back past funclet entries to the controlling function, then
// virtualIP = ReadyToRunInfo.MinVirtualIP + RuntimeFunction.BeginAddress.
}
```

The `CodeBlock` encapsulates the `MethodDesc` data from the target runtime together with the start of the jitted method

```csharp
Expand Down
1 change: 1 addition & 0 deletions docs/design/datacontracts/RuntimeTypeSystem.md
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,7 @@ static class RuntimeTypeSystem_1_Helpers
| `MethodDesc` | `Flags` | `uint16` | The method's flags |
| `MethodDesc` | `Flags3AndTokenRemainder` | `uint16` | More flags for the method, and the low bits of the method's token's RID |
| `MethodDesc` | `GCCoverageInfo` | `pointer` | The method's GCCover debug info, if supported |
| `MethodDesc` | `InterpreterCode` | `pointer` | Pointer to the method's `InterpByteCodeStart`, or the poison value 1 if the method will never be interpreted (only defined if `FEATURE_INTERPRETER` is enabled) |
| `MethodDesc` | `Slot` | `uint16` | The method's slot |
| `MethodDescChunk` | *(type size)* | `uint32` | Size of the data descriptor layout |
| `MethodDescChunk` | `Count` | `uint8` | The number of MethodDesc entries in this chunk, minus 1. |
Expand Down
8 changes: 8 additions & 0 deletions docs/design/datacontracts/data-descriptor-meanings.json
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,10 @@
"FramedMethodFrame.TransitionBlockPtr": "Pointer to Frame's TransitionBlock",
"FuncEvalFrame.DebuggerEvalPtr": "Pointer to the Frame's DebuggerEval object",
"FuncEvalFrame.ReturnAddress": "Return address of the frame",
"FunctionTableIndexRangeSection.MinFunctionTableIndex": "First runtime-global shared function-table index owned by the R2R module",
"FunctionTableIndexRangeSection.Next": "Pointer to the next registered WASM R2R function-table range",
"FunctionTableIndexRangeSection.NumRuntimeFunctions": "Number of consecutive RUNTIME_FUNCTION entries owned by the R2R module",
"FunctionTableIndexRangeSection.R2RModule": "Pointer to the Module that owns this function-table range",
"GCAllocContext.AllocBytes": "Number of bytes allocated on SOH by this context",
"GCAllocContext.AllocBytesLoh": "Number of bytes allocated not on SOH by this context",
"GCAllocContext.Limit": "Allocation limit pointer",
Expand Down Expand Up @@ -362,6 +366,7 @@
"MethodDesc.Flags": "The method's flags",
"MethodDesc.Flags3AndTokenRemainder": "More flags for the method, and the low bits of the method's token's RID",
"MethodDesc.GCCoverageInfo": "The method's GCCover debug info, if supported",
"MethodDesc.InterpreterCode": "Pointer to the method's `InterpByteCodeStart`, or the poison value 1 if the method will never be interpreted (only defined if `FEATURE_INTERPRETER` is enabled)",
"MethodDesc.Size": "Base size for mcIL classification",
"MethodDesc.Slot": "The method's slot",
"MethodDescChunk.Count": "The number of MethodDesc entries in this chunk, minus 1.",
Expand Down Expand Up @@ -457,6 +462,8 @@
"PInvokeMethodDesc.Size": "Base size for mcPInvoke classification",
"PlatformMetadata.CodePointerFlags": "fields describing the behavior of target code pointers",
"PlatformMetadata.PrecodeMachineDescriptor": "precode stub-related platform specific properties",
"PortableEntryPoint.ActualCode": "Native code for the entrypoint; on WebAssembly R2R code this is a function-table index (only defined if `FeaturePortableEntrypoints` is enabled)",
"PortableEntryPoint.Flags": "Portable entrypoint flags; `0x4` means the interpreter entrypoint is preferred over `ActualCode` (only defined if `FeaturePortableEntrypoints` is enabled)",
"PortableEntryPoint.MethodDesc": "Method desc of portable entrypoint (only defined if `FeaturePortableEntrypoints` is enabled)",
"PrecodeMachineDescriptor.FixupBytes": "Assembly code of a FixupStub",
"PrecodeMachineDescriptor.FixupIgnoredBytes": "Bytes to ignore when comparing FixupBytes to an actual block of memory in the target process.",
Expand Down Expand Up @@ -760,6 +767,7 @@
"FieldOffsetDynamicRVA": "Sentinel FieldDesc offset for an EnC-added RVA field whose enclosing type is not yet loaded",
"FieldOffsetNewEnc": "Sentinel offset value stored in FieldDesc::DWord2 for added fields whose storage has not yet been allocated.",
"FinalizerThread": "Pointer to the finalizer thread",
"FunctionTableIndexRangeList": "Pointer to the head pointer of the registered WASM R2R function-table range list",
"FreeObjectMethodTable": "A pointer to the address of a MethodTable used by the GC to indicate reclaimed memory",
"GCGlobalMechanisms": "Pointer to counters recording use of global GC mechanisms",
"GCHeapAllocAllocated": "Highest address allocated by Alloc (in workstation builds)",
Expand Down
4 changes: 2 additions & 2 deletions src/coreclr/debug/daccess/dacdbiimpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7715,11 +7715,11 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::EnumerateAsyncLocals(VMPTR_Method
{
return hr;
}
nativeCodeStartAddr = PCODEToPINSTR(requestedNativeCodeVersion.GetNativeCode());
nativeCodeStartAddr = PCODEToPINSTR(GetInterpreterCodeFromEntryPointIfPresent(requestedNativeCodeVersion.GetNativeCode()));
}
else
{
nativeCodeStartAddr = PCODEToPINSTR(pMethodDesc->GetNativeCode());
nativeCodeStartAddr = PCODEToPINSTR(GetInterpreterCodeFromEntryPointIfPresent(pMethodDesc->GetNativeCode()));
}

DebugInfoRequest request;
Expand Down
5 changes: 5 additions & 0 deletions src/coreclr/vm/datadescriptor/datadescriptor.inc
Original file line number Diff line number Diff line change
Expand Up @@ -782,6 +782,9 @@ CDAC_TYPE_FIELD(MethodDesc, T_UINT16, Flags, cdac_data<MethodDesc>::Flags)
CDAC_TYPE_FIELD(MethodDesc, T_UINT16, Flags3AndTokenRemainder, cdac_data<MethodDesc>::Flags3AndTokenRemainder)
CDAC_TYPE_FIELD(MethodDesc, T_UINT8, EntryPointFlags, cdac_data<MethodDesc>::EntryPointFlags)
CDAC_TYPE_FIELD(MethodDesc, T_POINTER, CodeData, cdac_data<MethodDesc>::CodeData)
#ifdef FEATURE_INTERPRETER
CDAC_TYPE_FIELD(MethodDesc, T_POINTER, InterpreterCode, cdac_data<MethodDesc>::InterpreterCode)
#endif // FEATURE_INTERPRETER
#ifdef HAVE_GCCOVER
CDAC_TYPE_FIELD(MethodDesc, T_POINTER, GCCoverageInfo, offsetof(MethodDesc, m_GcCover))
#endif // HAVE_GCCOVER
Expand Down Expand Up @@ -862,7 +865,9 @@ CDAC_TYPE_END(CodePointer)
#ifdef FEATURE_PORTABLE_ENTRYPOINTS
CDAC_TYPE_BEGIN(PortableEntryPoint)
CDAC_TYPE_INDETERMINATE(PortableEntryPoint)
CDAC_TYPE_FIELD(PortableEntryPoint, T_POINTER, ActualCode, cdac_data<PortableEntryPoint>::ActualCode)
CDAC_TYPE_FIELD(PortableEntryPoint, T_POINTER, MethodDesc, cdac_data<PortableEntryPoint>::MethodDesc)
CDAC_TYPE_FIELD(PortableEntryPoint, T_INT32, Flags, cdac_data<PortableEntryPoint>::Flags)
CDAC_TYPE_END(PortableEntryPoint)
#endif

Expand Down
4 changes: 4 additions & 0 deletions src/coreclr/vm/method.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1963,6 +1963,7 @@ class MethodDesc
WORD m_wFlags; // See MethodDescFlags
PTR_MethodDescCodeData m_codeData;
#ifdef FEATURE_INTERPRETER
// [cDAC] [ExecutionManager]: Contract depends on the value of INTERPRETER_CODE_POISON.
#define INTERPRETER_CODE_POISON 1
PTR_InterpByteCodeStart m_interpreterCode;
public:
Expand Down Expand Up @@ -2408,6 +2409,9 @@ template<> struct cdac_data<MethodDesc>
static constexpr size_t Flags3AndTokenRemainder = offsetof(MethodDesc, m_wFlags3AndTokenRemainder);
static constexpr size_t EntryPointFlags = offsetof(MethodDesc, m_bFlags4);
static constexpr size_t CodeData = offsetof(MethodDesc, m_codeData);
#ifdef FEATURE_INTERPRETER
static constexpr size_t InterpreterCode = offsetof(MethodDesc, m_interpreterCode);
#endif // FEATURE_INTERPRETER
};

#ifndef DACCESS_COMPILE
Expand Down
3 changes: 3 additions & 0 deletions src/coreclr/vm/precode_portable.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class PortableEntryPoint final
MethodDesc* _pMD;
Volatile<void*> _pInterpreterData;

// [cDAC] [ExecutionManager]: Contract depends on the value of kPrefersInterpreterEntryPoint.
enum PortableEntryPointFlag
{
kNone = 0,
Expand Down Expand Up @@ -257,7 +258,9 @@ static_assert(offsetof(ClosedStaticRetBufPortableEntryPoint, _entryPoint) == TAR
template<>
struct cdac_data<PortableEntryPoint>
{
static constexpr size_t ActualCode = offsetof(PortableEntryPoint, _pActualCode);
static constexpr size_t MethodDesc = offsetof(PortableEntryPoint, _pMD);
static constexpr size_t Flags = offsetof(PortableEntryPoint, _flags);

static_assert(offsetof(PortableEntryPoint, _pActualCode) == 0, "CLR ABI requires _pActualCode to be at offset 0 of PortableEntryPoint");
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,11 @@ public interface IExecutionManager : IContract
TargetPointer GetFuncletStartAddress(CodeBlockHandle codeInfoHandle) => throw new NotImplementedException();
void GetMethodRegionInfo(CodeBlockHandle codeInfoHandle, out uint hotSize, out TargetPointer coldStart, out uint coldSize) => throw new NotImplementedException();
TargetPointer NonVirtualEntry2MethodDesc(TargetCodePointer entrypoint) => throw new NotImplementedException();
// Map a method entry point to the code start that diagnostics report and that resolves through
// GetCodeBlockHandle: interpreter bytecode for interpreter precodes and interpreted portable entry
// points, or the synthetic virtual IP for Wasm ReadyToRun portable entry points. Other addresses
// are returned unchanged. Mirrors GetDiagnosticCodeStartFromEntryPoint in native code (precode.cpp).
TargetCodePointer GetDiagnosticCodeStartFromEntryPoint(TargetCodePointer entryPoint) => throw new NotImplementedException();
bool IsFunclet(CodeBlockHandle codeInfoHandle) => throw new NotImplementedException();
bool IsFilterFunclet(CodeBlockHandle codeInfoHandle) => throw new NotImplementedException();
TargetPointer GetUnwindInfo(CodeBlockHandle codeInfoHandle) => throw new NotImplementedException();
Expand Down
Loading
Loading