Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
2f3873f
Switch to JIT intrinsics for generic context/async continuations
jakobbotsch Nov 19, 2025
39ab2c2
Wrong autocomplete
jakobbotsch Nov 19, 2025
4d58470
Reorder, add missing cases
jakobbotsch Nov 19, 2025
1a3b2ff
Delete unnecessary code
jakobbotsch Nov 19, 2025
654c802
Fix wrong numInArgs passed in instantiating stubs
jakobbotsch Nov 20, 2025
9e73487
Move to RuntimeHelpers
jakobbotsch Nov 20, 2025
1175236
Merge branch 'main' of github.com:dotnet/runtime into special-arg-int…
jakobbotsch Nov 20, 2025
92d5875
Use new helpers in ILC (without any other clean up)
jakobbotsch Nov 20, 2025
613a1b1
Use intrinsics from NAOT
jakobbotsch Nov 20, 2025
c68bfa4
Mark explicitly called async method as having async calling convention
jakobbotsch Nov 20, 2025
77e6c61
Merge branch 'main' of github.com:dotnet/runtime into special-arg-int…
jakobbotsch Nov 21, 2025
0709437
Not working interpreter implementation
jakobbotsch Nov 21, 2025
f23352f
Model generic context arg as part of calling convention of calli
jakobbotsch Nov 24, 2025
b5a5ed3
Revert change
jakobbotsch Nov 24, 2025
bf2ef9a
Add comment
jakobbotsch Nov 24, 2025
36b0223
Revert ilc changes for now
jakobbotsch Nov 24, 2025
3467bb2
Remove IMAGE_CEE_CS_CALLCONV_PARAMTYPE
jakobbotsch Nov 24, 2025
62362d5
Undo unnecessary diff
jakobbotsch Nov 24, 2025
2e7ac00
Return ASYNC_CONTINUATION lower handling for NativeAOT for now
jakobbotsch Nov 24, 2025
b9298b4
Fix comment
jakobbotsch Nov 24, 2025
41056e2
Fix comment again
jakobbotsch Nov 24, 2025
e65d750
Apply suggestions from code review
jakobbotsch Nov 24, 2025
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
6 changes: 3 additions & 3 deletions src/coreclr/inc/corhdr.h
Original file line number Diff line number Diff line change
Expand Up @@ -986,10 +986,10 @@ typedef enum CorCallingConvention


// The high bits of the calling convention convey additional info
IMAGE_CEE_CS_CALLCONV_MASK = 0x0f, // Calling convention is bottom 4 bits
IMAGE_CEE_CS_CALLCONV_HASTHIS = 0x20, // Top bit indicates a 'this' parameter
IMAGE_CEE_CS_CALLCONV_MASK = 0x0f, // Calling convention is bottom 4 bits
IMAGE_CEE_CS_CALLCONV_HASTHIS = 0x20, // Top bit indicates a 'this' parameter
IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS = 0x40, // This parameter is explicitly in the signature
IMAGE_CEE_CS_CALLCONV_GENERIC = 0x10, // Generic method sig with explicit number of type arguments (precedes ordinary parameter count)
IMAGE_CEE_CS_CALLCONV_GENERIC = 0x10, // Generic method sig with explicit number of type arguments (precedes ordinary parameter count)
// 0x80 is reserved for internal use
} CorCallingConvention;

Expand Down
121 changes: 73 additions & 48 deletions src/coreclr/interpreter/compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1733,6 +1733,8 @@ InterpCompiler::InterpCompiler(COMP_HANDLE compHnd,
: m_stackmapsByClass(FreeInterpreterStackMap)
, m_pInitLocalsIns(nullptr)
, m_hiddenArgumentVar(-1)
, m_nextCallGenericContextVar(-1)
, m_nextCallAsyncContinuationVar(-1)
, m_leavesTable(this)
, m_dataItems(this)
, m_globalVarsWithRefsStackTop(0)
Expand Down Expand Up @@ -3250,6 +3252,22 @@ bool InterpCompiler::EmitNamedIntrinsicCall(NamedIntrinsic ni, bool nonVirtualCa
return true;
}

case NI_System_Runtime_CompilerServices_RuntimeHelpers_SetNextCallGenericContext:
{
CHECK_STACK(1);
m_pStackPointer--;
m_nextCallGenericContextVar = m_pStackPointer[0].var;
return true;
}

case NI_System_Runtime_CompilerServices_RuntimeHelpers_SetNextCallAsyncContinuation:
{
CHECK_STACK(1);
m_pStackPointer--;
m_nextCallAsyncContinuationVar = m_pStackPointer[0].var;
return true;
}

case NI_System_Threading_Interlocked_CompareExchange:
{
CHECK_STACK(3);
Expand Down Expand Up @@ -4260,62 +4278,69 @@ void InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN* pConstrainedToken, bool re
{
int contextParamVar = -1;

// Instantiated generic method
CORINFO_CONTEXT_HANDLE exactContextHnd = callInfo.contextHandle;
if (((SIZE_T)exactContextHnd & CORINFO_CONTEXTFLAGS_MASK) == CORINFO_CONTEXTFLAGS_METHOD)
if (m_nextCallGenericContextVar >= 0)
{
assert(exactContextHnd != METHOD_BEING_COMPILED_CONTEXT());

CORINFO_METHOD_HANDLE exactMethodHandle =
(CORINFO_METHOD_HANDLE)((SIZE_T)exactContextHnd & ~CORINFO_CONTEXTFLAGS_MASK);
DeclarePointerIsMethod(exactMethodHandle);

if (!callInfo.exactContextNeedsRuntimeLookup)
{
PushStackType(StackTypeI, NULL);
m_pStackPointer--;
contextParamVar = m_pStackPointer[0].var;
AddIns(INTOP_LDPTR);
m_pLastNewIns->SetDVar(contextParamVar);
m_pLastNewIns->data[0] = GetDataItemIndex((void*)exactMethodHandle);
}
else
{
contextParamVar = EmitGenericHandle(&resolvedCallToken, GenericHandleEmbedOptions::VarOnly).var;
}
contextParamVar = m_nextCallGenericContextVar;
m_nextCallGenericContextVar = -1;
}

// otherwise must be an instance method in a generic struct,
// a static method in a generic type, or a runtime-generated array method
else
{
assert(((SIZE_T)exactContextHnd & CORINFO_CONTEXTFLAGS_MASK) == CORINFO_CONTEXTFLAGS_CLASS);
CORINFO_CLASS_HANDLE exactClassHandle = getClassFromContext(exactContextHnd);
DeclarePointerIsClass(exactClassHandle);

if ((callInfo.classFlags & CORINFO_FLG_ARRAY) && readonly)
// Instantiated generic method
CORINFO_CONTEXT_HANDLE exactContextHnd = callInfo.contextHandle;
if (((SIZE_T)exactContextHnd & CORINFO_CONTEXTFLAGS_MASK) == CORINFO_CONTEXTFLAGS_METHOD)
{
PushStackType(StackTypeI, NULL);
m_pStackPointer--;
contextParamVar = m_pStackPointer[0].var;
// We indicate "readonly" to the Address operation by using a null
// instParam.
AddIns(INTOP_LDPTR);
m_pLastNewIns->SetDVar(contextParamVar);
m_pLastNewIns->data[0] = GetDataItemIndex(NULL);
}
else if (!callInfo.exactContextNeedsRuntimeLookup)
{
PushStackType(StackTypeI, NULL);
m_pStackPointer--;
contextParamVar = m_pStackPointer[0].var;
AddIns(INTOP_LDPTR);
m_pLastNewIns->SetDVar(contextParamVar);
m_pLastNewIns->data[0] = GetDataItemIndex((void*)exactClassHandle);
assert(exactContextHnd != METHOD_BEING_COMPILED_CONTEXT());

CORINFO_METHOD_HANDLE exactMethodHandle =
(CORINFO_METHOD_HANDLE)((SIZE_T)exactContextHnd & ~CORINFO_CONTEXTFLAGS_MASK);
DeclarePointerIsMethod(exactMethodHandle);

if (!callInfo.exactContextNeedsRuntimeLookup)
{
PushStackType(StackTypeI, NULL);
m_pStackPointer--;
contextParamVar = m_pStackPointer[0].var;
AddIns(INTOP_LDPTR);
m_pLastNewIns->SetDVar(contextParamVar);
m_pLastNewIns->data[0] = GetDataItemIndex((void*)exactMethodHandle);
}
else
{
contextParamVar = EmitGenericHandle(&resolvedCallToken, GenericHandleEmbedOptions::VarOnly).var;
}
}
// otherwise must be an instance method in a generic struct,
// a static method in a generic type, or a runtime-generated array method
else
{
contextParamVar = EmitGenericHandle(&resolvedCallToken, GenericHandleEmbedOptions::VarOnly | GenericHandleEmbedOptions::EmbedParent).var;
assert(((SIZE_T)exactContextHnd & CORINFO_CONTEXTFLAGS_MASK) == CORINFO_CONTEXTFLAGS_CLASS);
CORINFO_CLASS_HANDLE exactClassHandle = getClassFromContext(exactContextHnd);
DeclarePointerIsClass(exactClassHandle);

if ((callInfo.classFlags & CORINFO_FLG_ARRAY) && readonly)
{
PushStackType(StackTypeI, NULL);
m_pStackPointer--;
contextParamVar = m_pStackPointer[0].var;
// We indicate "readonly" to the Address operation by using a null
// instParam.
AddIns(INTOP_LDPTR);
m_pLastNewIns->SetDVar(contextParamVar);
m_pLastNewIns->data[0] = GetDataItemIndex(NULL);
}
else if (!callInfo.exactContextNeedsRuntimeLookup)
{
PushStackType(StackTypeI, NULL);
m_pStackPointer--;
contextParamVar = m_pStackPointer[0].var;
AddIns(INTOP_LDPTR);
m_pLastNewIns->SetDVar(contextParamVar);
m_pLastNewIns->data[0] = GetDataItemIndex((void*)exactClassHandle);
}
else
{
contextParamVar = EmitGenericHandle(&resolvedCallToken, GenericHandleEmbedOptions::VarOnly | GenericHandleEmbedOptions::EmbedParent).var;
}
}
}
callArgs[extraParamArgLocation] = contextParamVar;
Expand Down
5 changes: 5 additions & 0 deletions src/coreclr/interpreter/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,11 @@ class InterpCompiler
// populates the var at method entry
int32_t m_hiddenArgumentVar;

// If RuntimeHelpers.SetNextCallGenericContext or SetNextCallAsyncContinuation were used
// then these contain the value that should be passed as those arguments.
int32_t m_nextCallGenericContextVar;
int32_t m_nextCallAsyncContinuationVar;

// Table of mappings of leave instructions to the first finally call island the leave
// needs to execute.
TArray<LeavesTableEntry, MemPoolAllocator> m_leavesTable;
Expand Down
4 changes: 4 additions & 0 deletions src/coreclr/interpreter/intrinsics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ NamedIntrinsic GetNamedIntrinsic(COMP_HANDLE compHnd, CORINFO_METHOD_HANDLE comp
return NI_System_Runtime_CompilerServices_RuntimeHelpers_IsReferenceOrContainsReferences;
else if (!strcmp(methodName, "GetMethodTable"))
return NI_System_Runtime_CompilerServices_RuntimeHelpers_GetMethodTable;
else if (!strcmp(methodName, "SetNextCallGenericContext"))
return NI_System_Runtime_CompilerServices_RuntimeHelpers_SetNextCallGenericContext;
else if (!strcmp(methodName, "SetNextCallAsyncContinuation"))
return NI_System_Runtime_CompilerServices_RuntimeHelpers_SetNextCallAsyncContinuation;
}
}
else if (!strcmp(namespaceName, "System.Runtime.InteropServices"))
Expand Down
4 changes: 4 additions & 0 deletions src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -3993,6 +3993,10 @@ class Compiler

bool lvaInlineeReturnSpillTempFreshlyCreated = false; // True if the temp was freshly created for the inlinee return

unsigned lvaNextCallGenericContext = BAD_VAR_NUM; // Local number of argument passed as WellKnownArg::InstParam to next call

unsigned lvaNextCallAsyncContinuation = BAD_VAR_NUM; // Local number of argument passed as WellKnownArg::AsyncContinuation to next call

#if FEATURE_FIXED_OUT_ARGS
unsigned lvaOutgoingArgSpaceVar = BAD_VAR_NUM; // var that represents outgoing argument space
PhasedVar<unsigned> lvaOutgoingArgSpaceSize; // size of fixed outgoing argument space
Expand Down
Loading
Loading