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
Original file line number Diff line number Diff line change
Expand Up @@ -397,10 +397,10 @@ internal static unsafe void CopyConstruct<T>(T* dest, T* src) where T : unmanage
}
#endif

[Intrinsic]
[DebuggerHidden]
[DebuggerStepThrough]
internal static ref byte GetRawData(this object obj) =>
ref Unsafe.As<RawData>(obj).Data;
internal static ref byte GetRawData(this object obj) => ref obj.GetRawData();

[DebuggerHidden]
[DebuggerStepThrough]
Expand Down Expand Up @@ -703,21 +703,14 @@ internal static unsafe void CallDefaultConstructor(object* pObj, delegate*<objec
}
}
}
// Helper class to assist with unsafe pinning of arbitrary objects.
// It's used by VM code.
[NonVersionable] // This only applies to field layout
internal sealed class RawData
{
public byte Data;
}

// CLR arrays are laid out in memory as follows (multidimensional array bounds are optional):
// [ sync block || pMethodTable || num components || MD array bounds || array data .. ]
// ^ ^ ^ ^ returned reference
// | | \-- ref Unsafe.As<RawArrayData>(array).Data
// \-- array \-- ref Unsafe.As<RawData>(array).Data
// \-- array \-- ref array.GetRawData()
// The BaseSize of an array includes all the fields before the array data,
// including the sync block and method table. The reference to RawData.Data
// including the sync block and method table. The reference to GetRawData()
// points at the number of components, skipping over these two pointer-sized fields.
[NonVersionable] // This only applies to field layout
internal sealed class RawArrayData
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/inc/corinfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -2075,6 +2075,7 @@ struct CORINFO_FPSTRUCT_LOWERING
#define CORINFO_Array_MaxLength 0x7FFFFFC7
#define CORINFO_String_MaxLength 0x3FFFFFDF

#define OFFSETOF__CORINFO_Object__data SIZEOF__CORINFO_Object
#define OFFSETOF__CORINFO_Array__length SIZEOF__CORINFO_Object
#ifdef TARGET_64BIT
#define OFFSETOF__CORINFO_Array__data (OFFSETOF__CORINFO_Array__length + sizeof(uint32_t) /* length */ + sizeof(uint32_t) /* alignpad */)
Expand Down
53 changes: 34 additions & 19 deletions src/coreclr/interpreter/compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -773,7 +773,7 @@ int32_t InterpCompiler::CreateVarExplicit(InterpType interpType, CORINFO_CLASS_H
m_varsCapacity *= 2;
if (m_varsCapacity < 16)
m_varsCapacity = 16;

m_pVars = getAllocator(IMK_Var).allocateZeroed<InterpVar>(m_varsCapacity);
if (oldVars != NULL)
{
Expand All @@ -799,7 +799,7 @@ void InterpCompiler::EnsureStack(int additional)
m_stackCapacity *= 2;
if (m_stackCapacity < 4)
m_stackCapacity = 4;

m_pStackBase = new (getAllocator(IMK_StackInfo)) StackInfo[m_stackCapacity];
if (oldStackBase != NULL)
{
Expand Down Expand Up @@ -2068,23 +2068,23 @@ InterpMethod* InterpCompiler::FinalizeMethodData(void* baseAddressRW, void* base
uint32_t currentIntervalMapOffset = intervalMapsOffset;
const uint32_t asyncSuspendDataSectionEnd = asyncSuspendDataOffset + asyncSuspendDataSectionSize;
const uint32_t intervalMapsSectionEnd = intervalMapsOffset + intervalMapsSectionSize;

InterpByteCodeStart* pByteCodeStart = (InterpByteCodeStart*)rxBase;

for (int32_t i = 0; i < m_asyncSuspendDataItems.GetSize(); i++)
{
assert(currentAsyncOffset + sizeof(InterpAsyncSuspendData) <= asyncSuspendDataSectionEnd);

InterpAsyncSuspendData* srcData = m_asyncSuspendDataItems.Get(i);
InterpAsyncSuspendData* dstDataRW = (InterpAsyncSuspendData*)(rwBase + currentAsyncOffset);

// Copy the struct
memcpy(dstDataRW, srcData, sizeof(InterpAsyncSuspendData));

// Fix up the methodStartIP to point to the final bytecode start
dstDataRW->methodStartIP = pByteCodeStart;
dstDataRW->resumeInfo.DiagnosticIP += (TARGET_SIZE_T)pByteCodeStart;

// Fix up interval map pointers if they exist
// Note: The interval maps were allocated via AllocMethodData in the old model,
// we need to copy them to the new allocation and fix up the pointers
Expand All @@ -2097,14 +2097,14 @@ InterpMethod* InterpCompiler::FinalizeMethodData(void* baseAddressRW, void* base

uint32_t mapSize = (uint32_t)count * sizeof(InterpIntervalMapEntry);
assert(currentIntervalMapOffset + mapSize <= intervalMapsSectionEnd);

InterpIntervalMapEntry* dstMapRW = (InterpIntervalMapEntry*)(rwBase + currentIntervalMapOffset);
InterpIntervalMapEntry* dstMapRX = (InterpIntervalMapEntry*)(rxBase + currentIntervalMapOffset);
memcpy(dstMapRW, srcData->liveLocalsIntervals, mapSize);
dstDataRW->liveLocalsIntervals = dstMapRX;
currentIntervalMapOffset += mapSize;
}

if (srcData->zeroedLocalsIntervals != nullptr)
{
// Count entries
Expand All @@ -2114,7 +2114,7 @@ InterpMethod* InterpCompiler::FinalizeMethodData(void* baseAddressRW, void* base

uint32_t mapSize = (uint32_t)count * sizeof(InterpIntervalMapEntry);
assert(currentIntervalMapOffset + mapSize <= intervalMapsSectionEnd);

InterpIntervalMapEntry* dstMapRW = (InterpIntervalMapEntry*)(rwBase + currentIntervalMapOffset);
InterpIntervalMapEntry* dstMapRX = (InterpIntervalMapEntry*)(rxBase + currentIntervalMapOffset);
memcpy(dstMapRW, srcData->zeroedLocalsIntervals, mapSize);
Expand Down Expand Up @@ -2163,7 +2163,7 @@ InterpMethod* InterpCompiler::FinalizeMethodData(void* baseAddressRW, void* base
{
DataItemAsyncSuspendRef ref = m_dataItemAsyncSuspendRefs.Get(i);
// Calculate the final address of this async suspend data in the RX allocation
InterpAsyncSuspendData* finalAddr = (InterpAsyncSuspendData*)(rxBase + asyncSuspendDataOffset +
InterpAsyncSuspendData* finalAddr = (InterpAsyncSuspendData*)(rxBase + asyncSuspendDataOffset +
ref.asyncSuspendDataIndex * sizeof(InterpAsyncSuspendData));
pDataItemsRW[ref.dataItemIndex] = finalAddr;
}
Expand Down Expand Up @@ -3955,6 +3955,21 @@ bool InterpCompiler::EmitNamedIntrinsicCall(NamedIntrinsic ni, bool nonVirtualCa
return true;
}

case NI_System_Runtime_CompilerServices_RuntimeHelpers_GetRawData:
{
CHECK_STACK(1);
m_pStackPointer--;
int32_t objVar = m_pStackPointer[0].var;
AddIns(INTOP_NULLCHECK);
m_pLastNewIns->SetSVar(objVar);
AddIns(INTOP_ADD_P_IMM);
m_pLastNewIns->SetSVar(objVar);
m_pLastNewIns->data[0] = OFFSETOF__CORINFO_Object__data;
PushInterpType(InterpTypeByRef, NULL);
m_pLastNewIns->SetDVar(m_pStackPointer[-1].var);
return true;
}

case NI_System_Runtime_CompilerServices_RuntimeHelpers_SetNextCallGenericContext:
{
CHECK_STACK(1);
Expand Down Expand Up @@ -4587,7 +4602,7 @@ void InterpCompiler::EmitCalli(bool isTailCall, void* calliCookie, int callIFunc
{
if (m_compHnd->pInvokeMarshalingRequired(NULL, callSiteSig))
{
// If we remove this restriction, we should handle the track transitions scenario by forcing a
// If we remove this restriction, we should handle the track transitions scenario by forcing a
// p/invoke marshaling calli stub even when not needed.
BADCODE("PInvoke marshalling for calli is not supported in interpreted code");
}
Expand Down Expand Up @@ -6342,7 +6357,7 @@ void InterpCompiler::EmitSuspend(CorInfoType callRetType, ContinuationContextHan
}
InterpType interpType = m_pVars[var].interpType;
CORINFO_CLASS_HANDLE clsHnd = m_pVars[var].clsHnd;

int32_t alignUNUSED;
int32_t size = GetInterpTypeStackSize(clsHnd, interpType, &alignUNUSED);

Expand Down Expand Up @@ -6389,7 +6404,7 @@ void InterpCompiler::EmitSuspend(CorInfoType callRetType, ContinuationContextHan
SetSlotToTrue(objRefSlots, currentOffset + slotInfo.m_offsetBytes);
}
}

currentOffset += size;
}

Expand Down Expand Up @@ -6430,7 +6445,7 @@ void InterpCompiler::EmitSuspend(CorInfoType callRetType, ContinuationContextHan
suspendData->suspensionPointIndex = suspensionPointIndex;
CORINFO_ASYNC_INFO asyncInfo;
m_compHnd->getAsyncInfo(&asyncInfo);

GetDataForHelperFtn(CORINFO_HELP_ALLOC_CONTINUATION);
suspendData->continuationTypeHnd = continuationTypeHnd;
AllocateIntervalMapData_ForVars(&suspendData->liveLocalsIntervals, liveVars);
Expand Down Expand Up @@ -6568,13 +6583,13 @@ void InterpCompiler::EmitSuspend(CorInfoType callRetType, ContinuationContextHan

AddIns(handleContinuationOpcode);
int32_t suspendDataIndex = GetDataItemIndex(suspendData);

// Track this data item -> async suspend data reference for fixup during finalization
DataItemAsyncSuspendRef ref;
ref.dataItemIndex = suspendDataIndex;
ref.asyncSuspendDataIndex = m_asyncSuspendDataItems.GetSize() - 1; // suspendData was just added
m_dataItemAsyncSuspendRefs.Add(ref);

m_pLastNewIns->data[0] = suspendDataIndex;
m_pLastNewIns->data[1] = GetDataForHelperFtn(helperFuncForAllocatingContinuation);
PushInterpType(InterpTypeO, NULL);
Expand Down Expand Up @@ -6615,7 +6630,7 @@ void InterpCompiler::EmitSuspend(CorInfoType callRetType, ContinuationContextHan
// Add location to resume to. The implementation of this opcode will:
// - restore the data captured
// - If there is an exception, throw it
// - if there is a captured exec context, call the restoration function.
// - if there is a captured exec context, call the restoration function.
AddIns(INTOP_HANDLE_CONTINUATION_RESUME);
m_pLastNewIns->data[0] = suspendDataIndex;

Expand Down Expand Up @@ -10623,7 +10638,7 @@ void InterpCompiler::GenerateCode(CORINFO_METHOD_INFO* methodInfo)
// a normal call in this case.
bool isTailCall = !m_isAsyncVersionOfSyncMethod;
EmitCall(m_pConstrainedToken, readonly, isTailCall /* tailcall */, false /*newObj*/, false /*isCalli*/);
EmitRet(methodInfo); // The tail-call infrastructure in the interpreter is not 100% guaranteed to do a
EmitRet(methodInfo); // The tail-call infrastructure in the interpreter is not 100% guaranteed to do a
// tail-call, so inject the ret logic here to cover that case.
linkBBlocks = false;
break;
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/interpreter/intrinsics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ NamedIntrinsic GetNamedIntrinsic(COMP_HANDLE compHnd, CORINFO_METHOD_HANDLE comp
return NI_System_Runtime_CompilerServices_RuntimeHelpers_IsRuntimeAsync;
else if (!strcmp(methodName, "GetMethodTable"))
return NI_System_Runtime_CompilerServices_RuntimeHelpers_GetMethodTable;
else if (!strcmp(methodName, "GetRawData"))
return NI_System_Runtime_CompilerServices_RuntimeHelpers_GetRawData;
else if (!strcmp(methodName, "SetNextCallGenericContext"))
return NI_System_Runtime_CompilerServices_RuntimeHelpers_SetNextCallGenericContext;
else if (!strcmp(methodName, "SetNextCallAsyncContinuation"))
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/jit/fgprofile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2589,6 +2589,7 @@ PhaseStatus Compiler::fgPrepareToInstrumentMethod()
case NI_System_Runtime_CompilerServices_RuntimeHelpers_IsRuntimeAsync:
case NI_System_Runtime_CompilerServices_RuntimeHelpers_IsReferenceOrContainsReferences:
case NI_System_Runtime_CompilerServices_RuntimeHelpers_GetMethodTable:
case NI_System_Runtime_CompilerServices_RuntimeHelpers_GetRawData:
case NI_System_Runtime_CompilerServices_RuntimeHelpers_WriteBarrier:
case NI_System_Runtime_CompilerServices_RuntimeHelpers_SetNextCallGenericContext:
case NI_System_Runtime_CompilerServices_RuntimeHelpers_SetNextCallAsyncContinuation:
Expand Down
25 changes: 25 additions & 0 deletions src/coreclr/jit/importercalls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3945,6 +3945,27 @@ GenTree* Compiler::impIntrinsic(CORINFO_CLASS_HANDLE clsHnd,
break;
}

case NI_System_Runtime_CompilerServices_RuntimeHelpers_GetRawData:
{
GenTree* obj = impPopStack().val;
GenTree* objClone = nullptr;

if (fgAddrCouldBeNull(obj))
{
obj = impCloneExpr(obj, &objClone, CHECK_SPILL_ALL,
nullptr DEBUGARG("RuntimeHelpers.GetRawData obj"));
}

GenTree* offset = gtNewIconNode(OFFSETOF__CORINFO_Object__data, TYP_I_IMPL);
retNode = gtNewOperNode(GT_ADD, TYP_BYREF, obj, offset);

if (objClone != nullptr)
{
retNode = gtNewOperNode(GT_COMMA, TYP_BYREF, gtNewNullCheck(objClone), retNode);
}
break;
}

case NI_System_Runtime_InteropService_MemoryMarshal_GetArrayDataReference:
{
assert(sig->numArgs == 1);
Expand Down Expand Up @@ -12356,6 +12377,10 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method)
{
result = NI_System_Runtime_CompilerServices_RuntimeHelpers_GetMethodTable;
}
else if (strcmp(methodName, "GetRawData") == 0)
{
result = NI_System_Runtime_CompilerServices_RuntimeHelpers_GetRawData;
}
else if (strcmp(methodName, "SetNextCallGenericContext") == 0)
{
return NI_System_Runtime_CompilerServices_RuntimeHelpers_SetNextCallGenericContext;
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/jit/namedintrinsiclist.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ enum NamedIntrinsic : unsigned short
NI_System_Runtime_CompilerServices_RuntimeHelpers_IsRuntimeAsync,
NI_System_Runtime_CompilerServices_RuntimeHelpers_IsReferenceOrContainsReferences,
NI_System_Runtime_CompilerServices_RuntimeHelpers_GetMethodTable,
NI_System_Runtime_CompilerServices_RuntimeHelpers_GetRawData,
NI_System_Runtime_CompilerServices_RuntimeHelpers_WriteBarrier,
NI_System_Runtime_CompilerServices_RuntimeHelpers_SetNextCallGenericContext,
NI_System_Runtime_CompilerServices_RuntimeHelpers_SetNextCallAsyncContinuation,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,9 +177,6 @@ internal static bool IsBitwiseEquatable<T>()
return false;
}

internal static ref byte GetRawData(this object obj) =>
ref Unsafe.As<RawData>(obj).Data;
Comment thread
MichalPetryka marked this conversation as resolved.

internal static unsafe nuint GetRawObjectDataSize(this object obj)
{
MethodTable* pMT = GetMethodTable(obj);
Expand All @@ -199,6 +196,9 @@ internal static unsafe ushort GetElementSize(this Array array)
return array.GetMethodTable()->ComponentSize;
}

[Intrinsic]
internal static ref byte GetRawData(this object obj) => ref obj.GetRawData();

[Intrinsic]
internal static unsafe MethodTable* GetMethodTable(this object obj) => obj.GetMethodTable();

Expand Down Expand Up @@ -431,9 +431,9 @@ public static unsafe int SizeOf(RuntimeTypeHandle type)
// [ sync block || pMethodTable || num components || MD array bounds || array data .. ]
// ^ ^ ^ ^ returned reference
// | | \-- ref Unsafe.As<RawArrayData>(array).Data
// \-- array \-- ref Unsafe.As<RawData>(array).Data
// \-- array \-- ref array.GetRawData()
// The BaseSize of an array includes all the fields before the array data,
// including the sync block and method table. The reference to RawData.Data
// including the sync block and method table. The reference to GetRawData()
// points at the number of components, skipping over these two pointer-sized fields.
[StructLayout(LayoutKind.Sequential)]
internal class RawArrayData
Expand All @@ -444,10 +444,4 @@ internal class RawArrayData
#endif
public byte Data;
}

[StructLayout(LayoutKind.Sequential)]
internal class RawData
{
public byte Data;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -431,19 +431,19 @@ public override MethodIL EmitIL()
ILEmitter emit = new ILEmitter();
ILCodeStream codeStream = emit.NewCodeStream();

FieldDesc rawDataField = Context.SystemModule
.GetKnownType("System.Runtime.CompilerServices"u8, "RawData"u8)
.GetField("Data"u8);
MethodDesc rawDataMethod = Context.SystemModule
.GetKnownType("System.Runtime.CompilerServices"u8, "RuntimeHelpers"u8)
.GetKnownMethod("GetRawData"u8, null);

// Load ByRef to the field with the value of the boxed valuetype
codeStream.EmitLdArg(0);
codeStream.Emit(ILOpcode.ldflda, emit.NewToken(rawDataField));
codeStream.Emit(ILOpcode.call, emit.NewToken(rawDataMethod));

// Load the MethodTable of the boxed valuetype (this is the hidden generic context parameter expected
// by the (canonical) instance method, but normally not part of the signature in IL).
codeStream.EmitLdArg(0);
#if READYTORUN
codeStream.Emit(ILOpcode.ldflda, emit.NewToken(rawDataField));
codeStream.Emit(ILOpcode.call, emit.NewToken(rawDataMethod));
codeStream.EmitLdc(Context.Target.PointerSize);
codeStream.Emit(ILOpcode.sub);
codeStream.Emit(ILOpcode.ldind_i);
Expand Down Expand Up @@ -559,7 +559,7 @@ public override MethodIL EmitIL()

// unbox to get a pointer to the value type
codeStream.EmitLdArg(0);
codeStream.Emit(ILOpcode.ldflda, emit.NewToken(Context.SystemModule.GetKnownType("System.Runtime.CompilerServices"u8, "RawData"u8).GetField("Data"u8)));
codeStream.Emit(ILOpcode.call, emit.NewToken(Context.SystemModule.GetKnownType("System.Runtime.CompilerServices"u8, "RuntimeHelpers"u8).GetKnownMethod("GetRawData"u8, null)));

// Load rest of the arguments
for (int i = 0; i < _targetMethod.Signature.Length; i++)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ private MethodIL EmitILCommon(MethodDesc contextMethod)
var owningType = (MetadataType)_owningType.InstantiateAsOpen();

ILToken rawDataToken = owningType.IsValueType ? default :
emitter.NewToken(Context.SystemModule.GetKnownType("System.Runtime.CompilerServices"u8, "RawData"u8).GetKnownField("Data"u8));
emitter.NewToken(Context.SystemModule.GetKnownType("System.Runtime.CompilerServices"u8, "RuntimeHelpers"u8).GetKnownMethod("GetRawData"u8, null));

var switchStream = emitter.NewCodeStream();
var getFieldStream = emitter.NewCodeStream();
Expand Down Expand Up @@ -151,7 +151,7 @@ private MethodIL EmitILCommon(MethodDesc contextMethod)

// If this is a reference type, we subtract from the first field. Otherwise subtract from `ref this`.
if (!owningType.IsValueType)
getFieldStream.Emit(ILOpcode.ldflda, rawDataToken);
getFieldStream.Emit(ILOpcode.call, rawDataToken);

getFieldStream.Emit(ILOpcode.sub);

Expand Down
Loading
Loading