diff --git a/.github/prompts/add-new-jit-ee-api.prompt.md b/.github/prompts/add-new-jit-ee-api.prompt.md index ce3f56b937f6f5..81c5de02328e65 100644 --- a/.github/prompts/add-new-jit-ee-api.prompt.md +++ b/.github/prompts/add-new-jit-ee-api.prompt.md @@ -1,23 +1,23 @@ --- mode: 'agent' -tools: ['githubRepo', 'codebase', 'terminalLastCommand'] +tools: ['fetch', 'codebase', 'runCommands', 'usages', 'search', 'think'] description: 'Add a new API to the JIT-VM (aka JIT-EE) interface in the codebase.' --- #### 1 — Goal -Implement **one** new JIT-VM (also known as JIT-EE) API and all supporting glue. +Implement **one** new JIT-VM (also known as JIT-EE) API and all supporting glue. The JIT-VM interface defines the APIs through which the JIT compiler communicates with the runtime (VM). #### 2 — Prerequisites for the model * You have full repo access -* You may run scripts (e.g., `.sh` or `.bat`) +* You may run scripts (e.g., `.sh` or `.bat`) * Ask **clarifying questions** before the first code change if anything (signature, types, platform constraints) is unclear. #### 3 — Required user inputs -Ask the user for a C-like signature of the new API if it's not provided. +Ask the user for a C-like signature of the new API if it's not provided. Suggest `/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt` file as a reference. Example: ``` @@ -83,8 +83,8 @@ Use the correct directory for the script to run. +} ``` -6. Now implement the most complex part - SuperPMI. SuperPMI acts as a (de)serializer for JIT-VM queries in order -to then replay them without the actual VM to speed up jit-diffs and other scenarios. All parameters and return +6. Now implement the most complex part - SuperPMI. SuperPMI acts as a (de)serializer for JIT-VM queries in order +to then replay them without the actual VM to speed up jit-diffs and other scenarios. All parameters and return values recorded/restored using special primitve types and helpers. We need to update the following files: * `/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h`: @@ -96,7 +96,7 @@ Go through each of them one by one. * `/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h`: Define two `Agnostic_*` types for input arguments and another one for output parameters (return value, output arguments). - Do not create them if one of the generics ones can be re-used such as `DLD`, `DD`, `DLDL`, etc. Use `DWORD*` + Do not create them if one of the generics ones can be re-used such as `DLD`, `DD`, `DLDL`, etc. Use `DWORD*` like types for integers. Inspect the whole file to see how other APIs are defined. * `/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h`: @@ -126,7 +126,7 @@ Now add a new element to `enum mcPackets` enum in the same file. Example: ``` * `/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp`: -Add the implementation of the 3 methods to `methodcontext.cpp` at the end of it. +Add the implementation of the 3 methods to `methodcontext.cpp` at the end of it. Consider other similar methods in the file for reference. Do not change implementations of other methods in the file. Example: ```diff diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs index a7ab5415d25d97..291fd8802d3016 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs @@ -4,6 +4,7 @@ using System.Buffers.Binary; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Numerics; using System.Reflection; using System.Runtime.InteropServices; using System.Runtime.Serialization; @@ -47,83 +48,76 @@ public void Pop() } [Flags] - internal enum CorInfoContinuationFlags + // Keep in sync with CORINFO_CONTINUATION_FLAGS + internal enum ContinuationFlags { - // Whether or not the continuation expects the result to be boxed and - // placed in the GCData array at index 0. Not set if the callee is void. - CORINFO_CONTINUATION_RESULT_IN_GCDATA = 1, - // If this bit is set the continuation resumes inside a try block and thus - // if an exception is being propagated, needs to be resumed. The exception - // should be placed at index 0 or 1 depending on whether the continuation - // also expects a result. - CORINFO_CONTINUATION_NEEDS_EXCEPTION = 2, - // If this bit is set the continuation has the IL offset that inspired the - // OSR method saved in the beginning of 'Data', or -1 if the continuation - // belongs to a tier 0 method. - CORINFO_CONTINUATION_OSR_IL_OFFSET_IN_DATA = 4, + // Note: the following 'Has' members determine the members present at + // the beginning of the continuation's data chunk. Each field is + // pointer sized when present, apart from the result that has variable + // size. + + // Whether or not the continuation starts with an OSR IL offset. + HasOsrILOffset = 1, + // If this bit is set the continuation resumes inside a try block and + // thus if an exception is being propagated, needs to be resumed. + HasException = 2, + // If this bit is set the continuation has space for a continuation + // context. + HasContinuationContext = 4, + // If this bit is set the continuation has space to store a result + // returned by the callee. + HasResult = 8, // If this bit is set the continuation should continue on the thread // pool. - CORINFO_CONTINUATION_CONTINUE_ON_THREAD_POOL = 8, - // If this bit is set the continuation has a SynchronizationContext - // that we should continue on. - CORINFO_CONTINUATION_CONTINUE_ON_CAPTURED_SYNCHRONIZATION_CONTEXT = 16, - // If this bit is set the continuation has a TaskScheduler - // that we should continue on. - CORINFO_CONTINUATION_CONTINUE_ON_CAPTURED_TASK_SCHEDULER = 32, + ContinueOnThreadPool = 16, + // If this bit is set the continuation context is a + // SynchronizationContext that we should continue on. + ContinueOnCapturedSynchronizationContext = 32, + // If this bit is set the continuation context is a TaskScheduler that + // we should continue on. + ContinueOnCapturedTaskScheduler = 64, } - internal sealed unsafe class Continuation +#pragma warning disable CA1852 // "Type can be sealed" -- no it cannot because the runtime constructs subtypes dynamically + internal unsafe class Continuation { public Continuation? Next; - public delegate* Resume; - public uint State; - public CorInfoContinuationFlags Flags; - - // Data and GCData contain the state of the continuation. - // Note: The JIT is ultimately responsible for laying out these arrays. - // However, other parts of the system depend on the layout to - // know where to locate or place various pieces of data: - // - // 1. Resumption stubs need to know where to place the return value - // inside the next continuation. If the return value has GC references - // then it is boxed and placed at GCData[0]; otherwise, it is placed - // inside Data at offset 0 if - // CORINFO_CONTINUATION_OSR_IL_OFFSET_IN_DATA is NOT set and otherwise - // at offset 4. - // - // 2. Likewise, Finalize[Value]TaskReturningThunk needs to know from - // where to extract the return value. - // - // 3. The dispatcher needs to know where to place the exception inside - // the next continuation with a handler. Continuations with handlers - // have CORINFO_CONTINUATION_NEEDS_EXCEPTION set. The exception is - // placed at GCData[0] if CORINFO_CONTINUATION_RESULT_IN_GCDATA is NOT - // set, and otherwise at GCData[1]. - // - public byte[]? Data; - public object?[]? GCData; - - public object GetContinuationContext() + public delegate* Resume; + public ContinuationFlags Flags; + public int State; + +#if TARGET_64BIT + private const int PointerSize = 8; +#else + private const int PointerSize = 4; +#endif + + private const int DataOffset = PointerSize /* Next */ + PointerSize /* Resume */ + 8 /* Flags + State */; + + public unsafe object GetContinuationContext() { - int index = 0; - if ((Flags & CorInfoContinuationFlags.CORINFO_CONTINUATION_RESULT_IN_GCDATA) != 0) - index++; - if ((Flags & CorInfoContinuationFlags.CORINFO_CONTINUATION_NEEDS_EXCEPTION) != 0) - index++; - Debug.Assert(GCData != null && GCData.Length > index); - object? continuationContext = GCData[index]; - Debug.Assert(continuationContext != null); - return continuationContext; + Debug.Assert((Flags & ContinuationFlags.HasContinuationContext) != 0); + uint contIndex = (uint)BitOperations.PopCount((uint)Flags & ((uint)ContinuationFlags.HasContinuationContext - 1)); + ref byte data = ref RuntimeHelpers.GetRawData(this); + return Unsafe.As(ref Unsafe.Add(ref data, DataOffset + contIndex * PointerSize)); } public void SetException(Exception ex) { - int index = 0; - if ((Flags & CorInfoContinuationFlags.CORINFO_CONTINUATION_RESULT_IN_GCDATA) != 0) - index++; + Debug.Assert((Flags & ContinuationFlags.HasException) != 0); + uint contIndex = (uint)BitOperations.PopCount((uint)Flags & ((uint)ContinuationFlags.HasException - 1)); + ref byte data = ref RuntimeHelpers.GetRawData(this); + Unsafe.As(ref Unsafe.Add(ref data, DataOffset + contIndex * PointerSize)) = ex; + } + + public ref byte GetResultStorageOrNull() + { + if ((Flags & ContinuationFlags.HasResult) == 0) + return ref Unsafe.NullRef(); - Debug.Assert(GCData != null && GCData.Length > index); - GCData[index] = ex; + uint contIndex = (uint)BitOperations.PopCount((uint)Flags & ((uint)ContinuationFlags.HasResult - 1)); + ref byte data = ref RuntimeHelpers.GetRawData(this); + return ref Unsafe.Add(ref data, DataOffset + contIndex * PointerSize); } } @@ -148,63 +142,35 @@ private struct RuntimeAsyncAwaitState [ThreadStatic] private static RuntimeAsyncAwaitState t_runtimeAsyncAwaitState; - private static Continuation AllocContinuation(Continuation prevContinuation, nuint numGCRefs, nuint dataSize) + private static unsafe Continuation AllocContinuation(Continuation prevContinuation, MethodTable* contMT) { - Continuation newContinuation = new Continuation { Data = new byte[dataSize], GCData = new object[numGCRefs] }; + Continuation newContinuation = (Continuation)RuntimeTypeHandle.InternalAllocNoChecks(contMT); prevContinuation.Next = newContinuation; return newContinuation; } - private static unsafe Continuation AllocContinuationMethod(Continuation prevContinuation, nuint numGCRefs, nuint dataSize, MethodDesc* method) + private static unsafe Continuation AllocContinuationMethod(Continuation prevContinuation, MethodTable* contMT, int keepAliveOffset, MethodDesc* method) { LoaderAllocator loaderAllocator = RuntimeMethodHandle.GetLoaderAllocator(new RuntimeMethodHandleInternal((IntPtr)method)); - object?[] gcData; - if (loaderAllocator != null) - { - gcData = new object[numGCRefs + 1]; - gcData[numGCRefs] = loaderAllocator; - } - else - { - gcData = new object[numGCRefs]; - } - - Continuation newContinuation = new Continuation { Data = new byte[dataSize], GCData = gcData }; + Continuation newContinuation = (Continuation)RuntimeTypeHandle.InternalAllocNoChecks(contMT); + Unsafe.As(ref Unsafe.Add(ref RuntimeHelpers.GetRawData(newContinuation), keepAliveOffset)) = loaderAllocator; prevContinuation.Next = newContinuation; return newContinuation; } - private static unsafe Continuation AllocContinuationClass(Continuation prevContinuation, nuint numGCRefs, nuint dataSize, MethodTable* methodTable) + private static unsafe Continuation AllocContinuationClass(Continuation prevContinuation, MethodTable* contMT, int keepAliveOffset, MethodTable* methodTable) { IntPtr loaderAllocatorHandle = methodTable->GetLoaderAllocatorHandle(); - object?[] gcData; + + Continuation newContinuation = (Continuation)RuntimeTypeHandle.InternalAllocNoChecks(contMT); + prevContinuation.Next = newContinuation; if (loaderAllocatorHandle != IntPtr.Zero) { - gcData = new object[numGCRefs + 1]; - gcData[numGCRefs] = GCHandle.FromIntPtr(loaderAllocatorHandle).Target; - } - else - { - gcData = new object[numGCRefs]; + Unsafe.As(ref Unsafe.Add(ref RuntimeHelpers.GetRawData(newContinuation), keepAliveOffset)) = GCHandle.FromIntPtr(loaderAllocatorHandle).Target; } - - Continuation newContinuation = new Continuation { Data = new byte[dataSize], GCData = gcData }; - prevContinuation.Next = newContinuation; return newContinuation; } - // Used to box the return value before storing into caller's continuation - // if the value is an object-containing struct. - // We are allocating a box directly instead of relying on regular boxing because we want - // to store structs without changing layout, including nullables. - private static unsafe object AllocContinuationResultBox(void* ptr) - { - MethodTable* pMT = (MethodTable*)ptr; - Debug.Assert(pMT->IsValueType); - // We need no type/cctor checks since we will be storing an instance that already exists. - return RuntimeTypeHandle.InternalAllocNoChecks((MethodTable*)pMT); - } - [BypassReadyToRun] [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.Async)] [RequiresPreviewFeatures] @@ -224,8 +190,9 @@ private interface IRuntimeAsyncTaskOps static abstract Action GetContinuationAction(T task); static abstract Continuation GetContinuationState(T task); static abstract void SetContinuationState(T task, Continuation value); - static abstract bool SetCompleted(T task, Continuation continuation); + static abstract bool SetCompleted(T task); static abstract void PostToSyncContext(T task, SynchronizationContext syncCtx); + static abstract ref byte GetResultStorage(T task); } /// @@ -274,39 +241,23 @@ void ITaskCompletionAction.Invoke(Task completingTask) private struct Ops : IRuntimeAsyncTaskOps> { public static Action GetContinuationAction(RuntimeAsyncTask task) => (Action)task.m_action!; - public static void MoveNext(RuntimeAsyncTask task) => task.MoveNext(); public static Continuation GetContinuationState(RuntimeAsyncTask task) => (Continuation)task.m_stateObject!; public static void SetContinuationState(RuntimeAsyncTask task, Continuation value) { task.m_stateObject = value; } - public static bool SetCompleted(RuntimeAsyncTask task, Continuation continuation) + public static bool SetCompleted(RuntimeAsyncTask task) { - T result; - if (RuntimeHelpers.IsReferenceOrContainsReferences()) - { - if (typeof(T).IsValueType) - { - result = Unsafe.As(ref continuation.GCData![0]!.GetRawData()); - } - else - { - result = Unsafe.As(ref continuation.GCData![0]!); - } - } - else - { - result = Unsafe.As(ref continuation.Data![0]); - } - - return task.TrySetResult(result); + return task.TrySetResult(task.m_result); } public static void PostToSyncContext(RuntimeAsyncTask task, SynchronizationContext syncContext) { syncContext.Post(s_postCallback, task); } + + public static ref byte GetResultStorage(RuntimeAsyncTask task) => ref Unsafe.As(ref task.m_result); } } @@ -356,14 +307,13 @@ void ITaskCompletionAction.Invoke(Task completingTask) private struct Ops : IRuntimeAsyncTaskOps { public static Action GetContinuationAction(RuntimeAsyncTask task) => (Action)task.m_action!; - public static void MoveNext(RuntimeAsyncTask task) => task.MoveNext(); public static Continuation GetContinuationState(RuntimeAsyncTask task) => (Continuation)task.m_stateObject!; public static void SetContinuationState(RuntimeAsyncTask task, Continuation value) { task.m_stateObject = value; } - public static bool SetCompleted(RuntimeAsyncTask task, Continuation continuation) + public static bool SetCompleted(RuntimeAsyncTask task) { return task.TrySetResult(); } @@ -372,6 +322,8 @@ public static void PostToSyncContext(RuntimeAsyncTask task, SynchronizationConte { syncContext.Post(s_postCallback, task); } + + public static ref byte GetResultStorage(RuntimeAsyncTask task) => ref Unsafe.NullRef(); } } @@ -381,13 +333,15 @@ public static unsafe void DispatchContinuations(T task) where T : Task, { ExecutionAndSyncBlockStore contexts = default; contexts.Push(); - Continuation continuation = TOps.GetContinuationState(task); + Continuation? continuation = TOps.GetContinuationState(task); while (true) { + Debug.Assert(continuation != null); try { - Continuation? newContinuation = continuation.Resume(continuation); + ref byte resultLoc = ref continuation.Next != null ? ref continuation.Next.GetResultStorageOrNull() : ref TOps.GetResultStorage(task); + Continuation? newContinuation = continuation.Resume(continuation, ref resultLoc); if (newContinuation != null) { @@ -397,13 +351,13 @@ public static unsafe void DispatchContinuations(T task) where T : Task, return; } - Debug.Assert(continuation.Next != null); continuation = continuation.Next; } catch (Exception ex) { - Continuation nextContinuation = UnwindToPossibleHandler(continuation); - if (nextContinuation.Resume == null) + Debug.Assert(continuation != null); + Continuation? nextContinuation = UnwindToPossibleHandler(continuation); + if (nextContinuation == null) { // Tail of AsyncTaskMethodBuilderT.SetException bool successfullySet = ex is OperationCanceledException oce ? @@ -425,9 +379,9 @@ public static unsafe void DispatchContinuations(T task) where T : Task, continuation = nextContinuation; } - if (continuation.Resume == null) + if (continuation == null) { - bool successfullySet = TOps.SetCompleted(task, continuation); + bool successfullySet = TOps.SetCompleted(task); contexts.Pop(); @@ -447,14 +401,18 @@ public static unsafe void DispatchContinuations(T task) where T : Task, } } - private static Continuation UnwindToPossibleHandler(Continuation continuation) + private static Continuation? UnwindToPossibleHandler(Continuation continuation) { while (true) { - Debug.Assert(continuation.Next != null); - continuation = continuation.Next; - if ((continuation.Flags & CorInfoContinuationFlags.CORINFO_CONTINUATION_NEEDS_EXCEPTION) != 0) - return continuation; + Continuation? nextContinuation = continuation.Next; + if (nextContinuation == null) + return null; + + if ((nextContinuation.Flags & ContinuationFlags.HasException) != 0) + return nextContinuation; + + continuation = nextContinuation; } } @@ -475,10 +433,10 @@ public static void HandleSuspended(T task) where T : Task, ITaskComplet // Head continuation should be the result of async call to AwaitAwaiter or UnsafeAwaitAwaiter. // These never have special continuation handling. - const CorInfoContinuationFlags continueFlags = - CorInfoContinuationFlags.CORINFO_CONTINUATION_CONTINUE_ON_CAPTURED_SYNCHRONIZATION_CONTEXT | - CorInfoContinuationFlags.CORINFO_CONTINUATION_CONTINUE_ON_THREAD_POOL | - CorInfoContinuationFlags.CORINFO_CONTINUATION_CONTINUE_ON_CAPTURED_TASK_SCHEDULER; + const ContinuationFlags continueFlags = + ContinuationFlags.ContinueOnCapturedSynchronizationContext | + ContinuationFlags.ContinueOnThreadPool | + ContinuationFlags.ContinueOnCapturedTaskScheduler; Debug.Assert((headContinuation.Flags & continueFlags) == 0); TOps.SetContinuationState(task, headContinuation); @@ -513,7 +471,7 @@ public static void HandleSuspended(T task) where T : Task, ITaskComplet private static bool QueueContinuationFollowUpActionIfNecessary(T task, Continuation continuation) where T : Task where TOps : IRuntimeAsyncTaskOps { - if ((continuation.Flags & CorInfoContinuationFlags.CORINFO_CONTINUATION_CONTINUE_ON_THREAD_POOL) != 0) + if ((continuation.Flags & ContinuationFlags.ContinueOnThreadPool) != 0) { SynchronizationContext? ctx = Thread.CurrentThreadAssumedInitialized._synchronizationContext; if (ctx == null || ctx.GetType() == typeof(SynchronizationContext)) @@ -531,7 +489,7 @@ private static bool QueueContinuationFollowUpActionIfNecessary(T task, return true; } - if ((continuation.Flags & CorInfoContinuationFlags.CORINFO_CONTINUATION_CONTINUE_ON_CAPTURED_SYNCHRONIZATION_CONTEXT) != 0) + if ((continuation.Flags & ContinuationFlags.ContinueOnCapturedSynchronizationContext) != 0) { object continuationContext = continuation.GetContinuationContext(); Debug.Assert(continuationContext is SynchronizationContext { }); @@ -557,7 +515,7 @@ private static bool QueueContinuationFollowUpActionIfNecessary(T task, return true; } - if ((continuation.Flags & CorInfoContinuationFlags.CORINFO_CONTINUATION_CONTINUE_ON_CAPTURED_TASK_SCHEDULER) != 0) + if ((continuation.Flags & ContinuationFlags.ContinueOnCapturedTaskScheduler) != 0) { object continuationContext = continuation.GetContinuationContext(); Debug.Assert(continuationContext is TaskScheduler { }); @@ -575,58 +533,34 @@ private static bool QueueContinuationFollowUpActionIfNecessary(T task, } } - // Change return type to ThunkTask -- no benefit since this is used for Task returning thunks only + // Change return type to RuntimeAsyncTask -- no benefit since this is used for Task returning thunks only #pragma warning disable CA1859 // When a Task-returning thunk gets a continuation result // it calls here to make a Task that awaits on the current async state. - private static Task FinalizeTaskReturningThunk(Continuation continuation) + private static Task FinalizeTaskReturningThunk() { - Continuation finalContinuation = new Continuation(); - - // Note that the exact location the return value is placed is tied - // into getAsyncResumptionStub in the VM, so do not change this - // without also changing that code (and the JIT). - if (RuntimeHelpers.IsReferenceOrContainsReferences()) - { - finalContinuation.Flags = CorInfoContinuationFlags.CORINFO_CONTINUATION_RESULT_IN_GCDATA | CorInfoContinuationFlags.CORINFO_CONTINUATION_NEEDS_EXCEPTION; - finalContinuation.GCData = new object[1]; - } - else - { - finalContinuation.Flags = CorInfoContinuationFlags.CORINFO_CONTINUATION_NEEDS_EXCEPTION; - finalContinuation.Data = new byte[Unsafe.SizeOf()]; - } - - continuation.Next = finalContinuation; - RuntimeAsyncTask result = new(); result.HandleSuspended(); return result; } - private static Task FinalizeTaskReturningThunk(Continuation continuation) + private static Task FinalizeTaskReturningThunk() { - Continuation finalContinuation = new Continuation - { - Flags = CorInfoContinuationFlags.CORINFO_CONTINUATION_NEEDS_EXCEPTION, - }; - continuation.Next = finalContinuation; - RuntimeAsyncTask result = new(); result.HandleSuspended(); return result; } - private static ValueTask FinalizeValueTaskReturningThunk(Continuation continuation) + private static ValueTask FinalizeValueTaskReturningThunk() { // We only come to these methods in the expensive case (already // suspended), so ValueTask optimization here is not relevant. - return new ValueTask(FinalizeTaskReturningThunk(continuation)); + return new ValueTask(FinalizeTaskReturningThunk()); } - private static ValueTask FinalizeValueTaskReturningThunk(Continuation continuation) + private static ValueTask FinalizeValueTaskReturningThunk() { - return new ValueTask(FinalizeTaskReturningThunk(continuation)); + return new ValueTask(FinalizeTaskReturningThunk()); } private static Task TaskFromException(Exception ex) @@ -705,11 +639,11 @@ private static void RestoreContexts(bool suspended, ExecutionContext? previousEx } } - private static void CaptureContinuationContext(SynchronizationContext syncCtx, ref object context, ref CorInfoContinuationFlags flags) + private static void CaptureContinuationContext(SynchronizationContext syncCtx, ref object context, ref ContinuationFlags flags) { if (syncCtx != null && syncCtx.GetType() != typeof(SynchronizationContext)) { - flags |= CorInfoContinuationFlags.CORINFO_CONTINUATION_CONTINUE_ON_CAPTURED_SYNCHRONIZATION_CONTEXT; + flags |= ContinuationFlags.ContinueOnCapturedSynchronizationContext; context = syncCtx; return; } @@ -717,12 +651,12 @@ private static void CaptureContinuationContext(SynchronizationContext syncCtx, r TaskScheduler? sched = TaskScheduler.InternalCurrent; if (sched != null && sched != TaskScheduler.Default) { - flags |= CorInfoContinuationFlags.CORINFO_CONTINUATION_CONTINUE_ON_CAPTURED_TASK_SCHEDULER; + flags |= ContinuationFlags.ContinueOnCapturedTaskScheduler; context = sched; return; } - flags |= CorInfoContinuationFlags.CORINFO_CONTINUATION_CONTINUE_ON_THREAD_POOL; + flags |= ContinuationFlags.ContinueOnThreadPool; } internal static T CompletedTaskResult(Task task) diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index f1a60023132a8a..bdedfa685b37e8 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -1698,29 +1698,34 @@ struct CORINFO_EE_INFO CORINFO_OS osType; }; +// Keep in sync with ContinuationFlags enum in BCL sources enum CorInfoContinuationFlags { - // Whether or not the continuation expects the result to be boxed and - // placed in the GCData array at index 0. Not set if the callee is void. - CORINFO_CONTINUATION_RESULT_IN_GCDATA = 1, - // If this bit is set the continuation resumes inside a try block and thus - // if an exception is being propagated, needs to be resumed. The exception - // should be placed at index 0 or 1 depending on whether the continuation - // also expects a result. - CORINFO_CONTINUATION_NEEDS_EXCEPTION = 2, - // If this bit is set the continuation has the IL offset that inspired the - // OSR method saved in the beginning of 'Data', or -1 if the continuation - // belongs to a tier 0 method. - CORINFO_CONTINUATION_OSR_IL_OFFSET_IN_DATA = 4, + // Note: the following 'Has' members determine the members present at + // the beginning of the continuation's data chunk. Each field is + // pointer sized when present, apart from the result that has variable + // size. + + // Whether or not the continuation starts with an OSR IL offset. + CORINFO_CONTINUATION_HAS_OSR_ILOFFSET = 1, + // If this bit is set the continuation resumes inside a try block and + // thus if an exception is being propagated, needs to be resumed. + CORINFO_CONTINUATION_HAS_EXCEPTION = 2, + // If this bit is set the continuation has space for a continuation + // context. + CORINFO_CONTINUATION_HAS_CONTINUATION_CONTEXT = 4, + // If this bit is set the continuation has space to store a result + // returned by the callee. + CORINFO_CONTINUATION_HAS_RESULT = 8, // If this bit is set the continuation should continue on the thread // pool. - CORINFO_CONTINUATION_CONTINUE_ON_THREAD_POOL = 8, - // If this bit is set the continuation has a SynchronizationContext - // that we should continue on. - CORINFO_CONTINUATION_CONTINUE_ON_CAPTURED_SYNCHRONIZATION_CONTEXT = 16, - // If this bit is set the continuation has a TaskScheduler - // that we should continue on. - CORINFO_CONTINUATION_CONTINUE_ON_CAPTURED_TASK_SCHEDULER = 32, + CORINFO_CONTINUATION_CONTINUE_ON_THREAD_POOL = 16, + // If this bit is set the continuation context is a + // SynchronizationContext that we should continue on. + CORINFO_CONTINUATION_CONTINUE_ON_CAPTURED_SYNCHRONIZATION_CONTEXT = 32, + // If this bit is set the continuation context is a TaskScheduler that + // we should continue on. + CORINFO_CONTINUATION_CONTINUE_ON_CAPTURED_TASK_SCHEDULER = 64, }; struct CORINFO_ASYNC_INFO @@ -1735,13 +1740,6 @@ struct CORINFO_ASYNC_INFO CORINFO_FIELD_HANDLE continuationStateFldHnd; // 'Flags' field CORINFO_FIELD_HANDLE continuationFlagsFldHnd; - // 'Data' field - CORINFO_FIELD_HANDLE continuationDataFldHnd; - // 'GCData' field - CORINFO_FIELD_HANDLE continuationGCDataFldHnd; - // Whether or not the continuation needs to be allocated through the - // helper that also takes a method handle - bool continuationsNeedMethodHandle; // Method handle for AsyncHelpers.CaptureExecutionContext CORINFO_METHOD_HANDLE captureExecutionContextMethHnd; // Method handle for AsyncHelpers.RestoreExecutionContext @@ -1960,6 +1958,8 @@ struct CORINFO_FPSTRUCT_LOWERING #define OFFSETOF__CORINFO_Span__reference 0 #define OFFSETOF__CORINFO_Span__length TARGET_POINTER_SIZE +#define OFFSETOF__CORINFO_Continuation__data (SIZEOF__CORINFO_Object + TARGET_POINTER_SIZE /* Next */ + TARGET_POINTER_SIZE /* Resume */ + 8 /* Flags + State */) + /* data to optimize delegate construction */ struct DelegateCtorArgs @@ -3342,6 +3342,12 @@ class ICorDynamicInfo : public ICorStaticInfo virtual CORINFO_METHOD_HANDLE getAsyncResumptionStub() = 0; + virtual CORINFO_CLASS_HANDLE getContinuationType( + size_t dataSize, + bool* objRefs, + size_t objRefsSize + ) = 0; + // Optionally, convert calli to regular method call. This is for PInvoke argument marshalling. virtual bool convertPInvokeCalliToCall( CORINFO_RESOLVED_TOKEN * pResolvedToken, diff --git a/src/coreclr/inc/dacvars.h b/src/coreclr/inc/dacvars.h index 151335e6daa60b..0be37f0098163a 100644 --- a/src/coreclr/inc/dacvars.h +++ b/src/coreclr/inc/dacvars.h @@ -175,6 +175,8 @@ DEFINE_DACVAR(UNKNOWN_POINTER_TYPE, dac__g_TypedReferenceMT, ::g_TypedReferenceM DEFINE_DACVAR(UNKNOWN_POINTER_TYPE, dac__g_pWeakReferenceClass, ::g_pWeakReferenceClass) DEFINE_DACVAR(UNKNOWN_POINTER_TYPE, dac__g_pWeakReferenceOfTClass, ::g_pWeakReferenceOfTClass) +DEFINE_DACVAR_VOLATILE(UNKNOWN_POINTER_TYPE, dac__g_pContinuationClassIfSubTypeCreated, ::g_pContinuationClassIfSubTypeCreated) + #ifdef FEATURE_COMINTEROP DEFINE_DACVAR(UNKNOWN_POINTER_TYPE, dac__g_pBaseCOMObject, ::g_pBaseCOMObject) #endif diff --git a/src/coreclr/inc/icorjitinfoimpl_generated.h b/src/coreclr/inc/icorjitinfoimpl_generated.h index 17e84757200845..0dc4f5fa089732 100644 --- a/src/coreclr/inc/icorjitinfoimpl_generated.h +++ b/src/coreclr/inc/icorjitinfoimpl_generated.h @@ -655,6 +655,11 @@ bool getTailCallHelpers( CORINFO_GET_TAILCALL_HELPERS_FLAGS flags, CORINFO_TAILCALL_HELPERS* pResult) override; +CORINFO_CLASS_HANDLE getContinuationType( + size_t dataSize, + bool* objRefs, + size_t objRefsSize) override; + CORINFO_METHOD_HANDLE getAsyncResumptionStub() override; bool convertPInvokeCalliToCall( diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h index e1fda2eeb3cbb0..2b4ee632d71684 100644 --- a/src/coreclr/inc/jiteeversionguid.h +++ b/src/coreclr/inc/jiteeversionguid.h @@ -37,11 +37,11 @@ #include -constexpr GUID JITEEVersionIdentifier = { /* 3d2bdd20-eced-4a07-b9fb-227ce7f55fcd */ - 0x3d2bdd20, - 0xeced, - 0x4a07, - {0xb9, 0xfb, 0x22, 0x7c, 0xe7, 0xf5, 0x5f, 0xcd} +constexpr GUID JITEEVersionIdentifier = { /* 68e93e9d-dd28-49ca-9ebc-a01a54532bb3 */ + 0x68e93e9d, + 0xdd28, + 0x49ca, + {0x9e, 0xbc, 0xa0, 0x1a, 0x54, 0x53, 0x2b, 0xb3} }; #endif // JIT_EE_VERSIONING_GUID_H diff --git a/src/coreclr/jit/ICorJitInfo_names_generated.h b/src/coreclr/jit/ICorJitInfo_names_generated.h index 1e455eb98bc74e..e58987625b7d9e 100644 --- a/src/coreclr/jit/ICorJitInfo_names_generated.h +++ b/src/coreclr/jit/ICorJitInfo_names_generated.h @@ -159,6 +159,7 @@ DEF_CLR_API(getFieldThreadLocalStoreID) DEF_CLR_API(GetDelegateCtor) DEF_CLR_API(MethodCompileComplete) DEF_CLR_API(getTailCallHelpers) +DEF_CLR_API(getContinuationType) DEF_CLR_API(getAsyncResumptionStub) DEF_CLR_API(convertPInvokeCalliToCall) DEF_CLR_API(notifyInstructionSetUsage) diff --git a/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp b/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp index f4fe7019066774..b1014f476f739e 100644 --- a/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp +++ b/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp @@ -1536,6 +1536,17 @@ bool WrapICorJitInfo::getTailCallHelpers( return temp; } +CORINFO_CLASS_HANDLE WrapICorJitInfo::getContinuationType( + size_t dataSize, + bool* objRefs, + size_t objRefsSize) +{ + API_ENTER(getContinuationType); + CORINFO_CLASS_HANDLE temp = wrapHnd->getContinuationType(dataSize, objRefs, objRefsSize); + API_LEAVE(getContinuationType); + return temp; +} + CORINFO_METHOD_HANDLE WrapICorJitInfo::getAsyncResumptionStub() { API_ENTER(getAsyncResumptionStub); diff --git a/src/coreclr/jit/async.cpp b/src/coreclr/jit/async.cpp index ec92f9b23ca6a7..4d43458c89e569 100644 --- a/src/coreclr/jit/async.cpp +++ b/src/coreclr/jit/async.cpp @@ -802,7 +802,7 @@ void AsyncTransformation::Transform( CreateLiveSetForSuspension(block, call, defs, life, liveLocals); - ContinuationLayout layout = LayOutContinuation(block, call, liveLocals); + ContinuationLayout layout = LayOutContinuation(block, call, ContinuationNeedsKeepAlive(life), liveLocals); ClearSuspendedIndicator(block, call); @@ -931,22 +931,99 @@ void AsyncTransformation::LiftLIREdges(BasicBlock* block, } } +//------------------------------------------------------------------------ +// AsyncTransformation::ContinuationNeedsKeepAlive: +// Check whether we need to allocate a "KeepAlive" field in the continuation. +// +// Parameters: +// life - Live locals +// +// Returns: +// True if we need to keep a LoaderAllocator for generic context or +// collectible method alive. +// +bool AsyncTransformation::ContinuationNeedsKeepAlive(AsyncLiveness& life) +{ + const unsigned GENERICS_CTXT_FROM = CORINFO_GENERICS_CTXT_FROM_METHODDESC | CORINFO_GENERICS_CTXT_FROM_METHODTABLE; + if (((m_comp->info.compMethodInfo->options & GENERICS_CTXT_FROM) != 0) && life.IsLive(m_comp->info.compTypeCtxtArg)) + { + return true; + } + + return false; +} + +class GCPointerBitMapBuilder +{ + bool* m_objRefs; + size_t m_size; + +public: + GCPointerBitMapBuilder(bool* objRefs, size_t size) + : m_objRefs(objRefs) + , m_size(size) + { + } + + INDEBUG(unsigned NumObjRefs = 0); + + void Set(unsigned offset) + { + assert((offset % TARGET_POINTER_SIZE) == 0); + assert(offset < m_size); + unsigned slot = offset / TARGET_POINTER_SIZE; + + // We do not expect to set the same offset multiple times + assert(!m_objRefs[slot]); + m_objRefs[slot] = true; + INDEBUG(NumObjRefs++); + } + + void SetIfNotMax(unsigned offset) + { + if (offset != UINT_MAX) + { + Set(offset); + } + } + + void SetType(unsigned offset, var_types type, ClassLayout* layout) + { + if (type == TYP_REF) + { + Set(offset); + } + else if (type == TYP_STRUCT) + { + for (unsigned slot = 0; slot < layout->GetSlotCount(); slot++) + { + if (layout->GetGCPtrType(slot) == TYP_REF) + { + Set(offset + slot * TARGET_POINTER_SIZE); + } + } + } + } +}; + //------------------------------------------------------------------------ // AsyncTransformation::LayOutContinuation: // Create the layout of the GC pointer and data arrays in the continuation // object. // // Parameters: -// block - The block containing the async call -// call - The async call -// liveLocals - [in, out] Information about each live local. Size/alignment -// information is read and offset/index information is written. +// block - The block containing the async call +// call - The async call +// needsKeepAlive - Whether the layout needs a "keep alive" field allocated +// liveLocals - [in, out] Information about each live local. Size/alignment +// information is read and offset/index information is written. // // Returns: // Layout information. // ContinuationLayout AsyncTransformation::LayOutContinuation(BasicBlock* block, GenTreeCall* call, + bool needsKeepAlive, jitstd::vector& liveLocals) { ContinuationLayout layout(liveLocals); @@ -962,154 +1039,203 @@ ContinuationLayout AsyncTransformation::LayOutContinuation(BasicBlock* if (layout->IsCustomLayout()) { - inf.Alignment = 1; - inf.DataSize = layout->GetSize(); - inf.GCDataCount = layout->GetGCPtrCount(); + inf.Alignment = 1; + inf.Size = layout->GetSize(); } else { inf.Alignment = m_comp->info.compCompHnd->getClassAlignmentRequirement(layout->GetClassHandle()); - if ((layout->GetGCPtrCount() * TARGET_POINTER_SIZE) == layout->GetSize()) - { - inf.DataSize = 0; - } - else - { - inf.DataSize = layout->GetSize(); - } - - inf.GCDataCount = layout->GetGCPtrCount(); + inf.Size = layout->GetSize(); } } else if (dsc->TypeIs(TYP_REF)) { - inf.Alignment = TARGET_POINTER_SIZE; - inf.DataSize = 0; - inf.GCDataCount = 1; + inf.Alignment = TARGET_POINTER_SIZE; + inf.Size = TARGET_POINTER_SIZE; } else { assert(!dsc->TypeIs(TYP_BYREF)); - inf.Alignment = genTypeAlignments[dsc->TypeGet()]; - inf.DataSize = genTypeSize(dsc); - inf.GCDataCount = 0; + inf.Alignment = genTypeAlignments[dsc->TypeGet()]; + inf.Size = genTypeSize(dsc); } } - jitstd::sort(liveLocals.begin(), liveLocals.end(), [](const LiveLocalInfo& lhs, const LiveLocalInfo& rhs) { - if (lhs.Alignment == rhs.Alignment) + jitstd::sort(liveLocals.begin(), liveLocals.end(), [=](const LiveLocalInfo& lhs, const LiveLocalInfo& rhs) { + bool lhsIsRef = m_comp->lvaGetDesc(lhs.LclNum)->TypeIs(TYP_REF); + bool rhsIsRef = m_comp->lvaGetDesc(rhs.LclNum)->TypeIs(TYP_REF); + + // Keep object refs first to improve sharability of continuation types. + if (lhsIsRef != rhsIsRef) { - // Prefer lowest local num first for same alignment. - return lhs.LclNum < rhs.LclNum; + return lhsIsRef; } - // Otherwise prefer highest alignment first. - return lhs.Alignment > rhs.Alignment; - }); + // Otherwise prefer higher alignment first. + if (lhs.HeapAlignment() != rhs.HeapAlignment()) + { + return lhs.HeapAlignment() > rhs.HeapAlignment(); + } - // For OSR, we store the IL offset that inspired the OSR method at the - // beginning of the data (-1 in the tier0 version): - if (m_comp->doesMethodHavePatchpoints() || m_comp->opts.IsOSR()) - { - JITDUMP(" Method %s; keeping IL offset that inspired OSR method at the beginning of non-GC data\n", - m_comp->doesMethodHavePatchpoints() ? "has patchpoints" : "is an OSR method"); - layout.DataSize += sizeof(int); - } + // Prefer lowest local num first for tiebreaker. + return lhs.LclNum < rhs.LclNum; + }); if (call->gtReturnType == TYP_STRUCT) { layout.ReturnStructLayout = m_comp->typGetObjLayout(call->gtRetClsHnd); layout.ReturnSize = layout.ReturnStructLayout->GetSize(); - layout.ReturnInGCData = layout.ReturnStructLayout->HasGCPtr(); + layout.ReturnAlignment = m_comp->info.compCompHnd->getClassAlignmentRequirement(call->gtRetClsHnd); } else { - layout.ReturnSize = genTypeSize(call->gtReturnType); - layout.ReturnInGCData = varTypeIsGC(call->gtReturnType); + layout.ReturnSize = genTypeSize(call->gtReturnType); + layout.ReturnAlignment = layout.ReturnSize; } assert((layout.ReturnSize > 0) == (call->gtReturnType != TYP_VOID)); - // The return value is always stored: - // 1. At index 0 in GCData if it is a TYP_REF or a struct with GC references - // 2. At index 0 in Data, for non OSR methods without GC ref returns - // 3. At index 4 in Data for OSR methods without GC ref returns. The - // continuation flags indicates this scenario with a flag. - if (layout.ReturnInGCData) + auto allocLayout = [&layout](unsigned align, unsigned size) { + layout.Size = roundUp(layout.Size, align); + unsigned offset = layout.Size; + layout.Size += size; + return offset; + }; + + // For OSR, we store the IL offset that inspired the OSR method at the + // beginning of the data (and store -1 in the tier0 version). This must be + // at the beginning because the tier0 and OSR versions need to agree on + // this. + if (m_comp->doesMethodHavePatchpoints() || m_comp->opts.IsOSR()) { - layout.GCRefsCount++; + JITDUMP(" Method %s; keeping IL offset that inspired OSR method at the beginning of non-GC data\n", + m_comp->doesMethodHavePatchpoints() ? "has patchpoints" : "is an OSR method"); + // Must be pointer sized for compatibility with Continuation methods that access fields + layout.OSRILOffset = allocLayout(TARGET_POINTER_SIZE, TARGET_POINTER_SIZE); } - else if (layout.ReturnSize > 0) + + if (block->hasTryIndex()) { - layout.ReturnValDataOffset = layout.DataSize; - layout.DataSize += layout.ReturnSize; + layout.ExceptionOffset = allocLayout(TARGET_POINTER_SIZE, TARGET_POINTER_SIZE); + JITDUMP(" " FMT_BB " is in try region %u; exception will be at offset %u\n", block->bbNum, + block->getTryIndex(), layout.ExceptionOffset); + } + + if (call->GetAsyncInfo().ContinuationContextHandling == ContinuationContextHandling::ContinueOnCapturedContext) + { + layout.ContinuationContextOffset = allocLayout(TARGET_POINTER_SIZE, TARGET_POINTER_SIZE); + JITDUMP(" Continuation continues on captured context; context will be at offset %u\n", + layout.ContinuationContextOffset); } -#ifdef DEBUG if (layout.ReturnSize > 0) { - JITDUMP(" Will store return of type %s, size %u in", + layout.ReturnValOffset = allocLayout(layout.ReturnHeapAlignment(), layout.ReturnSize); + + JITDUMP(" Will store return of type %s, size %u at offset %u\n", call->gtReturnType == TYP_STRUCT ? layout.ReturnStructLayout->GetClassName() : varTypeName(call->gtReturnType), - layout.ReturnSize); - - if (layout.ReturnInGCData) - { - JITDUMP(" GC data\n"); - } - else - { - JITDUMP(" non-GC data at offset %u\n", layout.ReturnValDataOffset); - } + layout.ReturnSize, layout.ReturnValOffset); } -#endif - if (block->hasTryIndex()) + if (needsKeepAlive) { - layout.ExceptionGCDataIndex = layout.GCRefsCount++; - JITDUMP(" " FMT_BB " is in try region %u; exception will be at GC@+%02u in GC data\n", block->bbNum, - block->getTryIndex(), layout.ExceptionGCDataIndex); + layout.KeepAliveOffset = allocLayout(TARGET_POINTER_SIZE, TARGET_POINTER_SIZE); + JITDUMP(" Continuation needs keep alive object; will be at offset %u\n", layout.KeepAliveOffset); } - if (call->GetAsyncInfo().ContinuationContextHandling == ContinuationContextHandling::ContinueOnCapturedContext) + if (call->GetAsyncInfo().ExecutionContextHandling == ExecutionContextHandling::AsyncSaveAndRestore) { - layout.ContinuationContextGCDataIndex = layout.GCRefsCount++; - JITDUMP(" Continuation continues on captured context; context will be at GC@+%02u in GC data\n", - layout.ContinuationContextGCDataIndex); + layout.ExecContextOffset = allocLayout(TARGET_POINTER_SIZE, TARGET_POINTER_SIZE); + JITDUMP(" Call has async-only save and restore of ExecutionContext; ExecutionContext will be at offset %u\n", + layout.ExecContextOffset); } - if (call->GetAsyncInfo().ExecutionContextHandling == ExecutionContextHandling::AsyncSaveAndRestore) + for (LiveLocalInfo& inf : liveLocals) { - layout.ExecContextGCDataIndex = layout.GCRefsCount++; - JITDUMP( - " Call has async-only save and restore of ExecutionContext; ExecutionContext will be at GC@+%02u in GC data\n", - layout.ExecContextGCDataIndex); + inf.Offset = allocLayout(inf.HeapAlignment(), inf.Size); } - for (LiveLocalInfo& inf : liveLocals) + layout.Size = roundUp(layout.Size, TARGET_POINTER_SIZE); + +#ifdef DEBUG + if (m_comp->verbose) { - layout.DataSize = roundUp(layout.DataSize, inf.Alignment); + printf(" Continuation layout (%u bytes):\n", layout.Size); + for (LiveLocalInfo& inf : liveLocals) + { + printf(" +%03u V%02u: %u bytes\n", inf.Offset, inf.LclNum, inf.Size); + } + } +#endif - inf.DataOffset = layout.DataSize; - inf.GCDataIndex = layout.GCRefsCount; + // Now create continuation type. First create bitmap of object refs. + bool* objRefs = + layout.Size < TARGET_POINTER_SIZE ? nullptr : new (m_comp, CMK_Async) bool[layout.Size / TARGET_POINTER_SIZE]{}; + + GCPointerBitMapBuilder bitmapBuilder(objRefs, layout.Size); + bitmapBuilder.SetIfNotMax(layout.ExceptionOffset); + bitmapBuilder.SetIfNotMax(layout.ContinuationContextOffset); + bitmapBuilder.SetIfNotMax(layout.KeepAliveOffset); + bitmapBuilder.SetIfNotMax(layout.ExecContextOffset); + + if (layout.ReturnSize > 0) + { + bitmapBuilder.SetType(layout.ReturnValOffset, call->gtReturnType, layout.ReturnStructLayout); + } - layout.DataSize += inf.DataSize; - layout.GCRefsCount += inf.GCDataCount; + for (LiveLocalInfo& inf : liveLocals) + { + LclVarDsc* dsc = m_comp->lvaGetDesc(inf.LclNum); + var_types storedType; + ClassLayout* layout; + if (dsc->TypeIs(TYP_STRUCT) || dsc->IsImplicitByRef()) + { + storedType = TYP_STRUCT; + layout = dsc->GetLayout(); + } + else + { + storedType = dsc->TypeGet(); + layout = NULL; + } + bitmapBuilder.SetType(inf.Offset, storedType, layout); } #ifdef DEBUG if (m_comp->verbose) { - printf(" Continuation layout (%u bytes, %u GC pointers):\n", layout.DataSize, layout.GCRefsCount); - for (LiveLocalInfo& inf : liveLocals) + printf("Getting continuation layout size = %u, numGCRefs = %u\n", layout.Size, bitmapBuilder.NumObjRefs); + bool* start = objRefs; + bool* endOfObjRefs = objRefs + layout.Size / TARGET_POINTER_SIZE; + while (start < endOfObjRefs) { - printf(" +%03u (GC@+%02u) V%02u: %u bytes, %u GC pointers\n", inf.DataOffset, inf.GCDataIndex, - inf.LclNum, inf.DataSize, inf.GCDataCount); + while (start < endOfObjRefs && !*start) + start++; + + if (start >= endOfObjRefs) + break; + + bool* end = start; + while (end < endOfObjRefs && *end) + end++; + + printf(" [%3u..%3u) obj refs\n", (start - objRefs) * TARGET_POINTER_SIZE, + (end - objRefs) * TARGET_POINTER_SIZE); + start = end; } } #endif + layout.ClassHnd = + m_comp->info.compCompHnd->getContinuationType(layout.Size, objRefs, layout.Size / TARGET_POINTER_SIZE); + +#ifdef DEBUG + char buffer[256]; + JITDUMP(" Result = %s\n", m_comp->eeGetClassName(layout.ClassHnd, buffer, ArrLen(buffer))); +#endif + return layout; } @@ -1287,8 +1413,7 @@ BasicBlock* AsyncTransformation::CreateSuspension( // Allocate continuation GenTree* returnedContinuation = m_comp->gtNewLclvNode(m_returnedContinuationVar, TYP_REF); - GenTreeCall* allocContinuation = - CreateAllocContinuationCall(life, returnedContinuation, layout.GCRefsCount, layout.DataSize); + GenTreeCall* allocContinuation = CreateAllocContinuationCall(life, returnedContinuation, layout); m_comp->compCurBB = suspendBB; m_comp->fgMorphTree(allocContinuation); @@ -1315,12 +1440,14 @@ BasicBlock* AsyncTransformation::CreateSuspension( // Fill in 'flags' const AsyncCallInfo& callInfo = call->GetAsyncInfo(); unsigned continuationFlags = 0; - if (layout.ReturnInGCData) - continuationFlags |= CORINFO_CONTINUATION_RESULT_IN_GCDATA; - if (block->hasTryIndex()) - continuationFlags |= CORINFO_CONTINUATION_NEEDS_EXCEPTION; - if (m_comp->doesMethodHavePatchpoints() || m_comp->opts.IsOSR()) - continuationFlags |= CORINFO_CONTINUATION_OSR_IL_OFFSET_IN_DATA; + if (layout.OSRILOffset != UINT_MAX) + continuationFlags |= CORINFO_CONTINUATION_HAS_OSR_ILOFFSET; + if (layout.ExceptionOffset != UINT_MAX) + continuationFlags |= CORINFO_CONTINUATION_HAS_EXCEPTION; + if (layout.ContinuationContextOffset != UINT_MAX) + continuationFlags |= CORINFO_CONTINUATION_HAS_CONTINUATION_CONTEXT; + if (layout.ReturnValOffset != UINT_MAX) + continuationFlags |= CORINFO_CONTINUATION_HAS_RESULT; if (callInfo.ContinuationContextHandling == ContinuationContextHandling::ContinueOnThreadPool) continuationFlags |= CORINFO_CONTINUATION_CONTINUE_ON_THREAD_POOL; @@ -1330,14 +1457,9 @@ BasicBlock* AsyncTransformation::CreateSuspension( GenTree* storeFlags = StoreAtOffset(newContinuation, flagsOffset, flagsNode, TYP_INT); LIR::AsRange(suspendBB).InsertAtEnd(LIR::SeqTree(m_comp, storeFlags)); - if (layout.GCRefsCount > 0) - { - FillInGCPointersOnSuspension(call, layout, suspendBB); - } - - if (layout.DataSize > 0) + if (layout.Size > 0) { - FillInDataOnSuspension(layout.Locals, suspendBB); + FillInDataOnSuspension(call, layout, suspendBB); } if (suspendBB->KindIs(BBJ_RETURN)) @@ -1357,155 +1479,113 @@ BasicBlock* AsyncTransformation::CreateSuspension( // Parameters: // life - Liveness information about live locals // prevContinuation - IR node that has the value of the previous continuation object -// gcRefsCount - Number of GC refs to allocate in the continuation object -// dataSize - Number of bytes to allocate in the continuation object +// layout - Layout information // // Returns: // IR node representing the allocation. // -GenTreeCall* AsyncTransformation::CreateAllocContinuationCall(AsyncLiveness& life, - GenTree* prevContinuation, - unsigned gcRefsCount, - unsigned dataSize) +GenTreeCall* AsyncTransformation::CreateAllocContinuationCall(AsyncLiveness& life, + GenTree* prevContinuation, + const ContinuationLayout& layout) { - GenTree* gcRefsCountNode = m_comp->gtNewIconNode((ssize_t)gcRefsCount, TYP_I_IMPL); - GenTree* dataSizeNode = m_comp->gtNewIconNode((ssize_t)dataSize, TYP_I_IMPL); + GenTree* contClassHndNode = m_comp->gtNewIconEmbClsHndNode(layout.ClassHnd); // If VM requests that we report the method handle, or if we have a shared generic context method handle // that is live here, then we need to call a different helper to keep the loader alive. - GenTree* methodHandleArg = nullptr; - GenTree* classHandleArg = nullptr; if (((m_comp->info.compMethodInfo->options & CORINFO_GENERICS_CTXT_FROM_METHODDESC) != 0) && life.IsLive(m_comp->info.compTypeCtxtArg)) { - methodHandleArg = m_comp->gtNewLclvNode(m_comp->info.compTypeCtxtArg, TYP_I_IMPL); + assert(layout.KeepAliveOffset != UINT_MAX); + GenTree* methodHandleArg = m_comp->gtNewLclvNode(m_comp->info.compTypeCtxtArg, TYP_I_IMPL); + // Offset passed to function is relative to instance data. + int keepAliveOffset = (OFFSETOF__CORINFO_Continuation__data - SIZEOF__CORINFO_Object) + layout.KeepAliveOffset; + GenTree* keepAliveOffsetNode = m_comp->gtNewIconNode(keepAliveOffset); + return m_comp->gtNewHelperCallNode(CORINFO_HELP_ALLOC_CONTINUATION_METHOD, TYP_REF, prevContinuation, + contClassHndNode, keepAliveOffsetNode, methodHandleArg); } else if (((m_comp->info.compMethodInfo->options & CORINFO_GENERICS_CTXT_FROM_METHODTABLE) != 0) && life.IsLive(m_comp->info.compTypeCtxtArg)) { - classHandleArg = m_comp->gtNewLclvNode(m_comp->info.compTypeCtxtArg, TYP_I_IMPL); - } - else if (m_asyncInfo->continuationsNeedMethodHandle) - { - methodHandleArg = m_comp->gtNewIconEmbMethHndNode(m_comp->info.compMethodHnd); - } - - if (methodHandleArg != nullptr) - { - return m_comp->gtNewHelperCallNode(CORINFO_HELP_ALLOC_CONTINUATION_METHOD, TYP_REF, prevContinuation, - gcRefsCountNode, dataSizeNode, methodHandleArg); - } - - if (classHandleArg != nullptr) - { + assert(layout.KeepAliveOffset != UINT_MAX); + GenTree* classHandleArg = m_comp->gtNewLclvNode(m_comp->info.compTypeCtxtArg, TYP_I_IMPL); + int keepAliveOffset = (OFFSETOF__CORINFO_Continuation__data - SIZEOF__CORINFO_Object) + layout.KeepAliveOffset; + GenTree* keepAliveOffsetNode = m_comp->gtNewIconNode(keepAliveOffset); return m_comp->gtNewHelperCallNode(CORINFO_HELP_ALLOC_CONTINUATION_CLASS, TYP_REF, prevContinuation, - gcRefsCountNode, dataSizeNode, classHandleArg); + contClassHndNode, keepAliveOffsetNode, classHandleArg); } - return m_comp->gtNewHelperCallNode(CORINFO_HELP_ALLOC_CONTINUATION, TYP_REF, prevContinuation, gcRefsCountNode, - dataSizeNode); + return m_comp->gtNewHelperCallNode(CORINFO_HELP_ALLOC_CONTINUATION, TYP_REF, prevContinuation, contClassHndNode); } //------------------------------------------------------------------------ -// AsyncTransformation::FillInGCPointersOnSuspension: -// Create IR that fills the GC pointers of the continuation object. -// This also nulls out the GC pointers in the locals if the local has data -// parts that need to be stored. +// AsyncTransformation::FillInDataOnSuspension: +// Create IR that fills the data array of the continuation object. // // Parameters: -// call - The async call that is being transformed -// layout - Layout information +// call - The async call +// layout - Information about the continuation layout // suspendBB - Basic block to add IR to. // -void AsyncTransformation::FillInGCPointersOnSuspension(GenTreeCall* call, - const ContinuationLayout& layout, - BasicBlock* suspendBB) +void AsyncTransformation::FillInDataOnSuspension(GenTreeCall* call, + const ContinuationLayout& layout, + BasicBlock* suspendBB) { - unsigned objectArrLclNum = GetGCDataArrayVar(); + if (m_comp->doesMethodHavePatchpoints() || m_comp->opts.IsOSR()) + { + GenTree* ilOffsetToStore; + if (m_comp->doesMethodHavePatchpoints()) + ilOffsetToStore = m_comp->gtNewIconNode(-1); + else + ilOffsetToStore = m_comp->gtNewIconNode((int)m_comp->info.compILEntry); - GenTree* newContinuation = m_comp->gtNewLclvNode(m_newContinuationVar, TYP_REF); - unsigned gcDataOffset = m_comp->info.compCompHnd->getFieldOffset(m_asyncInfo->continuationGCDataFldHnd); - GenTree* gcDataInd = LoadFromOffset(newContinuation, gcDataOffset, TYP_REF); - GenTree* storeAllocedObjectArr = m_comp->gtNewStoreLclVarNode(objectArrLclNum, gcDataInd); - LIR::AsRange(suspendBB).InsertAtEnd(LIR::SeqTree(m_comp, storeAllocedObjectArr)); + // OSR IL offset needs to be at offset 0 because OSR and tier0 methods + // need to agree on that. + assert(layout.OSRILOffset == 0); + GenTree* newContinuation = m_comp->gtNewLclvNode(m_newContinuationVar, TYP_REF); + unsigned offset = OFFSETOF__CORINFO_Continuation__data; + GenTree* storePatchpointOffset = StoreAtOffset(newContinuation, offset, ilOffsetToStore, TYP_INT); + LIR::AsRange(suspendBB).InsertAtEnd(LIR::SeqTree(m_comp, storePatchpointOffset)); + } + // Fill in data for (const LiveLocalInfo& inf : layout.Locals) { - if (inf.GCDataCount <= 0) - { - continue; - } + assert(inf.Size > 0); LclVarDsc* dsc = m_comp->lvaGetDesc(inf.LclNum); - if (dsc->TypeIs(TYP_REF)) + + GenTree* newContinuation = m_comp->gtNewLclvNode(m_newContinuationVar, TYP_REF); + unsigned offset = OFFSETOF__CORINFO_Continuation__data + inf.Offset; + + GenTree* value; + if (dsc->IsImplicitByRef()) { - GenTree* value = m_comp->gtNewLclvNode(inf.LclNum, TYP_REF); - GenTree* objectArr = m_comp->gtNewLclvNode(objectArrLclNum, TYP_REF); - GenTree* store = - StoreAtOffset(objectArr, OFFSETOF__CORINFO_Array__data + (inf.GCDataIndex * TARGET_POINTER_SIZE), value, - TYP_REF); - LIR::AsRange(suspendBB).InsertAtEnd(LIR::SeqTree(m_comp, store)); + GenTree* baseAddr = m_comp->gtNewLclvNode(inf.LclNum, dsc->TypeGet()); + value = m_comp->gtNewLoadValueNode(dsc->GetLayout(), baseAddr, GTF_IND_NONFAULTING); } else { - assert(dsc->TypeIs(TYP_STRUCT) || dsc->IsImplicitByRef()); - ClassLayout* layout = dsc->GetLayout(); - unsigned numSlots = layout->GetSlotCount(); - unsigned gcRefIndex = 0; - for (unsigned i = 0; i < numSlots; i++) - { - var_types gcPtrType = layout->GetGCPtrType(i); - assert((gcPtrType == TYP_I_IMPL) || (gcPtrType == TYP_REF)); - if (gcPtrType != TYP_REF) - { - continue; - } - - GenTree* value; - if (dsc->IsImplicitByRef()) - { - GenTree* baseAddr = m_comp->gtNewLclvNode(inf.LclNum, dsc->TypeGet()); - value = LoadFromOffset(baseAddr, i * TARGET_POINTER_SIZE, TYP_REF); - } - else - { - value = m_comp->gtNewLclFldNode(inf.LclNum, TYP_REF, i * TARGET_POINTER_SIZE); - } - - GenTree* objectArr = m_comp->gtNewLclvNode(objectArrLclNum, TYP_REF); - unsigned offset = - OFFSETOF__CORINFO_Array__data + ((inf.GCDataIndex + gcRefIndex) * TARGET_POINTER_SIZE); - GenTree* store = StoreAtOffset(objectArr, offset, value, TYP_REF); - LIR::AsRange(suspendBB).InsertAtEnd(LIR::SeqTree(m_comp, store)); - - gcRefIndex++; - - if (inf.DataSize > 0) - { - // Null out the GC field in preparation of storing the rest. - GenTree* null = m_comp->gtNewNull(); - - if (dsc->IsImplicitByRef()) - { - GenTree* baseAddr = m_comp->gtNewLclvNode(inf.LclNum, dsc->TypeGet()); - store = StoreAtOffset(baseAddr, i * TARGET_POINTER_SIZE, null, TYP_REF); - } - else - { - store = m_comp->gtNewStoreLclFldNode(inf.LclNum, TYP_REF, i * TARGET_POINTER_SIZE, null); - } + value = m_comp->gtNewLclVarNode(inf.LclNum); + } - LIR::AsRange(suspendBB).InsertAtEnd(LIR::SeqTree(m_comp, store)); - } - } + GenTreeFlags indirFlags = + GTF_IND_NONFAULTING | (inf.HeapAlignment() < inf.Alignment ? GTF_IND_UNALIGNED : GTF_EMPTY); - if (!dsc->IsImplicitByRef()) - { - m_comp->lvaSetVarDoNotEnregister(inf.LclNum DEBUGARG(DoNotEnregisterReason::LocalField)); - } + GenTree* store; + if (dsc->TypeIs(TYP_STRUCT) || dsc->IsImplicitByRef()) + { + GenTree* cns = m_comp->gtNewIconNode((ssize_t)offset, TYP_I_IMPL); + GenTree* addr = m_comp->gtNewOperNode(GT_ADD, TYP_BYREF, newContinuation, cns); + store = m_comp->gtNewStoreValueNode(dsc->GetLayout(), addr, value, indirFlags); } + else + { + store = StoreAtOffset(newContinuation, offset, value, dsc->TypeGet(), indirFlags); + } + + LIR::AsRange(suspendBB).InsertAtEnd(LIR::SeqTree(m_comp, store)); } - if (layout.ContinuationContextGCDataIndex != UINT_MAX) + if (layout.ContinuationContextOffset != UINT_MAX) { const AsyncCallInfo& callInfo = call->GetAsyncInfo(); assert(callInfo.SaveAndRestoreSynchronizationContextField); @@ -1515,7 +1595,7 @@ void AsyncTransformation::FillInGCPointersOnSuspension(GenTreeCall* // Insert call // AsyncHelpers.CaptureContinuationContext( // syncContextFromBeforeCall, - // ref newContinuation.GCData[ContinuationContextGCDataIndex], + // ref newContinuation.ContinuationContext, // ref newContinuation.Flags). GenTree* syncContextPlaceholder = m_comp->gtNewNull(); GenTree* contextElementPlaceholder = m_comp->gtNewZeroConNode(TYP_BYREF); @@ -1545,10 +1625,10 @@ void AsyncTransformation::FillInGCPointersOnSuspension(GenTreeCall* gotUse = LIR::AsRange(suspendBB).TryGetUse(contextElementPlaceholder, &use); assert(gotUse); - GenTree* objectArr = m_comp->gtNewLclvNode(objectArrLclNum, TYP_REF); - unsigned offset = OFFSETOF__CORINFO_Array__data + (layout.ContinuationContextGCDataIndex * TARGET_POINTER_SIZE); - GenTree* contextElementOffset = - m_comp->gtNewOperNode(GT_ADD, TYP_BYREF, objectArr, m_comp->gtNewIconNode((ssize_t)offset, TYP_I_IMPL)); + GenTree* newContinuation = m_comp->gtNewLclvNode(m_newContinuationVar, TYP_REF); + unsigned offset = OFFSETOF__CORINFO_Continuation__data + layout.ContinuationContextOffset; + GenTree* contextElementOffset = m_comp->gtNewOperNode(GT_ADD, TYP_BYREF, newContinuation, + m_comp->gtNewIconNode((ssize_t)offset, TYP_I_IMPL)); LIR::AsRange(suspendBB).InsertBefore(contextElementPlaceholder, LIR::SeqTree(m_comp, contextElementOffset)); use.ReplaceWith(contextElementOffset); @@ -1568,7 +1648,7 @@ void AsyncTransformation::FillInGCPointersOnSuspension(GenTreeCall* LIR::AsRange(suspendBB).Remove(flagsPlaceholder); } - if (layout.ExecContextGCDataIndex != UINT_MAX) + if (layout.ExecContextOffset != UINT_MAX) { GenTreeCall* captureExecContext = m_comp->gtNewCallNode(CT_USER_FUNC, m_asyncInfo->captureExecutionContextMethHnd, TYP_REF); @@ -1576,86 +1656,9 @@ void AsyncTransformation::FillInGCPointersOnSuspension(GenTreeCall* m_comp->compCurBB = suspendBB; m_comp->fgMorphTree(captureExecContext); - GenTree* objectArr = m_comp->gtNewLclvNode(objectArrLclNum, TYP_REF); - GenTree* store = - StoreAtOffset(objectArr, - OFFSETOF__CORINFO_Array__data + (layout.ExecContextGCDataIndex * TARGET_POINTER_SIZE), - captureExecContext, TYP_REF); - LIR::AsRange(suspendBB).InsertAtEnd(LIR::SeqTree(m_comp, store)); - } -} - -//------------------------------------------------------------------------ -// AsyncTransformation::FillInDataOnSuspension: -// Create IR that fills the data array of the continuation object. -// -// Parameters: -// liveLocals - Information about each live local. -// suspendBB - Basic block to add IR to. -// -void AsyncTransformation::FillInDataOnSuspension(const jitstd::vector& liveLocals, BasicBlock* suspendBB) -{ - unsigned byteArrLclNum = GetDataArrayVar(); - - GenTree* newContinuation = m_comp->gtNewLclvNode(m_newContinuationVar, TYP_REF); - unsigned dataOffset = m_comp->info.compCompHnd->getFieldOffset(m_asyncInfo->continuationDataFldHnd); - GenTree* dataInd = LoadFromOffset(newContinuation, dataOffset, TYP_REF); - GenTree* storeAllocedByteArr = m_comp->gtNewStoreLclVarNode(byteArrLclNum, dataInd); - LIR::AsRange(suspendBB).InsertAtEnd(LIR::SeqTree(m_comp, storeAllocedByteArr)); - - if (m_comp->doesMethodHavePatchpoints() || m_comp->opts.IsOSR()) - { - GenTree* ilOffsetToStore; - if (m_comp->doesMethodHavePatchpoints()) - ilOffsetToStore = m_comp->gtNewIconNode(-1); - else - ilOffsetToStore = m_comp->gtNewIconNode((int)m_comp->info.compILEntry); - - GenTree* byteArr = m_comp->gtNewLclvNode(byteArrLclNum, TYP_REF); - unsigned offset = OFFSETOF__CORINFO_Array__data; - GenTree* storePatchpointOffset = StoreAtOffset(byteArr, offset, ilOffsetToStore, TYP_INT); - LIR::AsRange(suspendBB).InsertAtEnd(LIR::SeqTree(m_comp, storePatchpointOffset)); - } - - // Fill in data - for (const LiveLocalInfo& inf : liveLocals) - { - if (inf.DataSize <= 0) - { - continue; - } - - LclVarDsc* dsc = m_comp->lvaGetDesc(inf.LclNum); - - GenTree* byteArr = m_comp->gtNewLclvNode(byteArrLclNum, TYP_REF); - unsigned offset = OFFSETOF__CORINFO_Array__data + inf.DataOffset; - - GenTree* value; - if (dsc->IsImplicitByRef()) - { - GenTree* baseAddr = m_comp->gtNewLclvNode(inf.LclNum, dsc->TypeGet()); - value = m_comp->gtNewLoadValueNode(dsc->GetLayout(), baseAddr, GTF_IND_NONFAULTING); - } - else - { - value = m_comp->gtNewLclVarNode(inf.LclNum); - } - - GenTree* store; - if (dsc->TypeIs(TYP_STRUCT) || dsc->IsImplicitByRef()) - { - GenTree* cns = m_comp->gtNewIconNode((ssize_t)offset, TYP_I_IMPL); - GenTree* addr = m_comp->gtNewOperNode(GT_ADD, TYP_BYREF, byteArr, cns); - // This is to heap, but all GC refs are nulled out already, so we can skip the write barrier. - // TODO-CQ: Backend does not care about GTF_IND_TGT_NOT_HEAP for STORE_BLK. - store = - m_comp->gtNewStoreValueNode(dsc->GetLayout(), addr, value, GTF_IND_NONFAULTING | GTF_IND_TGT_NOT_HEAP); - } - else - { - store = StoreAtOffset(byteArr, offset, value, dsc->TypeGet()); - } - + GenTree* newContinuation = m_comp->gtNewLclvNode(m_newContinuationVar, TYP_REF); + unsigned offset = OFFSETOF__CORINFO_Continuation__data + layout.ExecContextOffset; + GenTree* store = StoreAtOffset(newContinuation, offset, captureExecContext, TYP_REF); LIR::AsRange(suspendBB).InsertAtEnd(LIR::SeqTree(m_comp, store)); } } @@ -1745,48 +1748,21 @@ BasicBlock* AsyncTransformation::CreateResumption(BasicBlock* bloc SetSuspendedIndicator(resumeBB, block, call); - // We need to restore data before we restore GC pointers, since restoring - // the data may also write the GC pointer fields with nulls. - unsigned resumeByteArrLclNum = BAD_VAR_NUM; - if (layout.DataSize > 0) + if (layout.Size > 0) { - resumeByteArrLclNum = GetDataArrayVar(); - - GenTree* newContinuation = m_comp->gtNewLclvNode(m_comp->lvaAsyncContinuationArg, TYP_REF); - unsigned dataOffset = m_comp->info.compCompHnd->getFieldOffset(m_asyncInfo->continuationDataFldHnd); - GenTree* dataInd = LoadFromOffset(newContinuation, dataOffset, TYP_REF); - GenTree* storeAllocedByteArr = m_comp->gtNewStoreLclVarNode(resumeByteArrLclNum, dataInd); - - LIR::AsRange(resumeBB).InsertAtEnd(LIR::SeqTree(m_comp, storeAllocedByteArr)); - - RestoreFromDataOnResumption(resumeByteArrLclNum, layout.Locals, resumeBB); + RestoreFromDataOnResumption(layout, resumeBB); } - unsigned resumeObjectArrLclNum = BAD_VAR_NUM; - BasicBlock* storeResultBB = resumeBB; + BasicBlock* storeResultBB = resumeBB; - if (layout.GCRefsCount > 0) + if (layout.ExceptionOffset != UINT_MAX) { - resumeObjectArrLclNum = GetGCDataArrayVar(); - - GenTree* newContinuation = m_comp->gtNewLclvNode(m_comp->lvaAsyncContinuationArg, TYP_REF); - unsigned gcDataOffset = m_comp->info.compCompHnd->getFieldOffset(m_asyncInfo->continuationGCDataFldHnd); - GenTree* gcDataInd = LoadFromOffset(newContinuation, gcDataOffset, TYP_REF); - GenTree* storeAllocedObjectArr = m_comp->gtNewStoreLclVarNode(resumeObjectArrLclNum, gcDataInd); - LIR::AsRange(resumeBB).InsertAtEnd(LIR::SeqTree(m_comp, storeAllocedObjectArr)); - - RestoreFromGCPointersOnResumption(resumeObjectArrLclNum, layout, resumeBB); - - if (layout.ExceptionGCDataIndex != UINT_MAX) - { - storeResultBB = RethrowExceptionOnResumption(block, resumeObjectArrLclNum, layout, resumeBB); - } + storeResultBB = RethrowExceptionOnResumption(block, layout, resumeBB); } if ((layout.ReturnSize > 0) && (callDefInfo.DefinitionNode != nullptr)) { - CopyReturnValueOnResumption(call, callDefInfo, resumeByteArrLclNum, resumeObjectArrLclNum, layout, - storeResultBB); + CopyReturnValueOnResumption(call, callDefInfo, layout, storeResultBB); } return resumeBB; @@ -1798,70 +1774,12 @@ BasicBlock* AsyncTransformation::CreateResumption(BasicBlock* bloc // object. // // Parameters: -// resumeByteArrLclNum - Local that has the continuation object's data array -// liveLocals - Information about each live local. -// resumeBB - Basic block to append IR to +// layout - Information about the continuation layout +// resumeBB - Basic block to append IR to // -void AsyncTransformation::RestoreFromDataOnResumption(unsigned resumeByteArrLclNum, - const jitstd::vector& liveLocals, - BasicBlock* resumeBB) +void AsyncTransformation::RestoreFromDataOnResumption(const ContinuationLayout& layout, BasicBlock* resumeBB) { - // Copy data - for (const LiveLocalInfo& inf : liveLocals) - { - if (inf.DataSize <= 0) - { - continue; - } - - LclVarDsc* dsc = m_comp->lvaGetDesc(inf.LclNum); - - GenTree* byteArr = m_comp->gtNewLclvNode(resumeByteArrLclNum, TYP_REF); - unsigned offset = OFFSETOF__CORINFO_Array__data + inf.DataOffset; - GenTree* cns = m_comp->gtNewIconNode((ssize_t)offset, TYP_I_IMPL); - GenTree* addr = m_comp->gtNewOperNode(GT_ADD, TYP_BYREF, byteArr, cns); - - GenTree* value; - if (dsc->TypeIs(TYP_STRUCT) || dsc->IsImplicitByRef()) - { - value = m_comp->gtNewLoadValueNode(dsc->GetLayout(), addr, GTF_IND_NONFAULTING); - } - else - { - value = m_comp->gtNewIndir(dsc->TypeGet(), addr, GTF_IND_NONFAULTING); - } - - GenTree* store; - if (dsc->IsImplicitByRef()) - { - GenTree* baseAddr = m_comp->gtNewLclvNode(inf.LclNum, dsc->TypeGet()); - store = m_comp->gtNewStoreValueNode(dsc->GetLayout(), baseAddr, value, - GTF_IND_NONFAULTING | GTF_IND_TGT_NOT_HEAP); - } - else - { - store = m_comp->gtNewStoreLclVarNode(inf.LclNum, value); - } - - LIR::AsRange(resumeBB).InsertAtEnd(LIR::SeqTree(m_comp, store)); - } -} - -//------------------------------------------------------------------------ -// AsyncTransformation::RestoreFromGCPointersOnResumption: -// Create IR that restores locals from the GC pointers array of the -// continuation object. -// -// Parameters: -// resumeObjectArrLclNum - Local that has the continuation object's GC pointers array -// liveLocals - Information about each live local. -// resumeBB - Basic block to append IR to -// -void AsyncTransformation::RestoreFromGCPointersOnResumption(unsigned resumeObjectArrLclNum, - const ContinuationLayout& layout, - BasicBlock* resumeBB) -{ - if (layout.ExecContextGCDataIndex != BAD_VAR_NUM) + if (layout.ExecContextOffset != BAD_VAR_NUM) { GenTree* valuePlaceholder = m_comp->gtNewZeroConNode(TYP_REF); GenTreeCall* restoreCall = @@ -1877,10 +1795,9 @@ void AsyncTransformation::RestoreFromGCPointersOnResumption(unsigned bool gotUse = LIR::AsRange(resumeBB).TryGetUse(valuePlaceholder, &valueUse); assert(gotUse); - GenTree* objectArr = m_comp->gtNewLclvNode(resumeObjectArrLclNum, TYP_REF); - unsigned execContextOffset = - OFFSETOF__CORINFO_Array__data + (layout.ExecContextGCDataIndex * TARGET_POINTER_SIZE); - GenTree* execContextValue = LoadFromOffset(objectArr, execContextOffset, TYP_REF); + GenTree* continuation = m_comp->gtNewLclvNode(m_comp->lvaAsyncContinuationArg, TYP_REF); + unsigned execContextOffset = OFFSETOF__CORINFO_Continuation__data + layout.ExecContextOffset; + GenTree* execContextValue = LoadFromOffset(continuation, execContextOffset, TYP_REF); LIR::AsRange(resumeBB).InsertBefore(valuePlaceholder, LIR::SeqTree(m_comp, execContextValue)); valueUse.ReplaceWith(execContextValue); @@ -1888,60 +1805,41 @@ void AsyncTransformation::RestoreFromGCPointersOnResumption(unsigned LIR::AsRange(resumeBB).Remove(valuePlaceholder); } + // Copy data for (const LiveLocalInfo& inf : layout.Locals) { - if (inf.GCDataCount <= 0) + LclVarDsc* dsc = m_comp->lvaGetDesc(inf.LclNum); + + GenTree* continuation = m_comp->gtNewLclvNode(m_comp->lvaAsyncContinuationArg, TYP_REF); + unsigned offset = OFFSETOF__CORINFO_Continuation__data + inf.Offset; + GenTree* cns = m_comp->gtNewIconNode((ssize_t)offset, TYP_I_IMPL); + GenTree* addr = m_comp->gtNewOperNode(GT_ADD, TYP_BYREF, continuation, cns); + + GenTreeFlags indirFlags = + GTF_IND_NONFAULTING | (inf.HeapAlignment() < inf.Alignment ? GTF_IND_UNALIGNED : GTF_EMPTY); + GenTree* value; + if (dsc->TypeIs(TYP_STRUCT) || dsc->IsImplicitByRef()) { - continue; + value = m_comp->gtNewLoadValueNode(dsc->GetLayout(), addr, indirFlags); } - - LclVarDsc* dsc = m_comp->lvaGetDesc(inf.LclNum); - if (dsc->TypeIs(TYP_REF)) + else { - GenTree* objectArr = m_comp->gtNewLclvNode(resumeObjectArrLclNum, TYP_REF); - unsigned offset = OFFSETOF__CORINFO_Array__data + (inf.GCDataIndex * TARGET_POINTER_SIZE); - GenTree* value = LoadFromOffset(objectArr, offset, TYP_REF); - GenTree* store = m_comp->gtNewStoreLclVarNode(inf.LclNum, value); + value = m_comp->gtNewIndir(dsc->TypeGet(), addr, indirFlags); + } - LIR::AsRange(resumeBB).InsertAtEnd(LIR::SeqTree(m_comp, store)); + GenTree* store; + if (dsc->IsImplicitByRef()) + { + GenTree* baseAddr = m_comp->gtNewLclvNode(inf.LclNum, dsc->TypeGet()); + store = m_comp->gtNewStoreValueNode(dsc->GetLayout(), baseAddr, value, + GTF_IND_NONFAULTING | GTF_IND_TGT_NOT_HEAP); } else { - assert(dsc->TypeIs(TYP_STRUCT) || dsc->IsImplicitByRef()); - ClassLayout* layout = dsc->GetLayout(); - unsigned numSlots = layout->GetSlotCount(); - unsigned gcRefIndex = 0; - for (unsigned i = 0; i < numSlots; i++) - { - var_types gcPtrType = layout->GetGCPtrType(i); - assert((gcPtrType == TYP_I_IMPL) || (gcPtrType == TYP_REF)); - if (gcPtrType != TYP_REF) - { - continue; - } - - GenTree* objectArr = m_comp->gtNewLclvNode(resumeObjectArrLclNum, TYP_REF); - unsigned offset = - OFFSETOF__CORINFO_Array__data + ((inf.GCDataIndex + gcRefIndex) * TARGET_POINTER_SIZE); - GenTree* value = LoadFromOffset(objectArr, offset, TYP_REF); - GenTree* store; - if (dsc->IsImplicitByRef()) - { - GenTree* baseAddr = m_comp->gtNewLclvNode(inf.LclNum, dsc->TypeGet()); - store = StoreAtOffset(baseAddr, i * TARGET_POINTER_SIZE, value, TYP_REF); - // Implicit byref args are never on heap - store->gtFlags |= GTF_IND_TGT_NOT_HEAP; - } - else - { - store = m_comp->gtNewStoreLclFldNode(inf.LclNum, TYP_REF, i * TARGET_POINTER_SIZE, value); - } - - LIR::AsRange(resumeBB).InsertAtEnd(LIR::SeqTree(m_comp, store)); - - gcRefIndex++; - } + store = m_comp->gtNewStoreLclVarNode(inf.LclNum, value); } + + LIR::AsRange(resumeBB).InsertAtEnd(LIR::SeqTree(m_comp, store)); } } @@ -1952,7 +1850,6 @@ void AsyncTransformation::RestoreFromGCPointersOnResumption(unsigned // // Parameters: // block - The block containing the async call -// resumeObjectArrLclNum - Local that has the continuation object's GC pointers array // layout - Layout information for the continuation object // resumeBB - Basic block to append IR to // @@ -1962,7 +1859,6 @@ void AsyncTransformation::RestoreFromGCPointersOnResumption(unsigned // rethrow. // BasicBlock* AsyncTransformation::RethrowExceptionOnResumption(BasicBlock* block, - unsigned resumeObjectArrLclNum, const ContinuationLayout& layout, BasicBlock* resumeBB) { @@ -1996,9 +1892,9 @@ BasicBlock* AsyncTransformation::RethrowExceptionOnResumption(BasicBlock* // Check if we have an exception. unsigned exceptionLclNum = GetExceptionVar(); - GenTree* objectArr = m_comp->gtNewLclvNode(resumeObjectArrLclNum, TYP_REF); - unsigned exceptionOffset = OFFSETOF__CORINFO_Array__data + layout.ExceptionGCDataIndex * TARGET_POINTER_SIZE; - GenTree* exceptionInd = LoadFromOffset(objectArr, exceptionOffset, TYP_REF); + GenTree* continuation = m_comp->gtNewLclvNode(m_comp->lvaAsyncContinuationArg, TYP_REF); + unsigned exceptionOffset = OFFSETOF__CORINFO_Continuation__data + layout.ExceptionOffset; + GenTree* exceptionInd = LoadFromOffset(continuation, exceptionOffset, TYP_REF); GenTree* storeException = m_comp->gtNewStoreLclVarNode(exceptionLclNum, exceptionInd); LIR::AsRange(resumeBB).InsertAtEnd(LIR::SeqTree(m_comp, storeException)); @@ -2032,50 +1928,23 @@ BasicBlock* AsyncTransformation::RethrowExceptionOnResumption(BasicBlock* // call - The async call // callDefInfo - Information about the async call's definition // block - The block containing the async call -// resumeByteArrLclNum - Local that has the continuation object's data array -// resumeObjectArrLclNum - Local that has the continuation object's GC pointers array // layout - Layout information for the continuation object // storeResultBB - Basic block to append IR to // void AsyncTransformation::CopyReturnValueOnResumption(GenTreeCall* call, const CallDefinitionInfo& callDefInfo, - unsigned resumeByteArrLclNum, - unsigned resumeObjectArrLclNum, const ContinuationLayout& layout, BasicBlock* storeResultBB) { - GenTree* resultBase; - unsigned resultOffset; - GenTreeFlags resultIndirFlags = GTF_IND_NONFAULTING; - if (layout.ReturnInGCData) - { - assert(resumeObjectArrLclNum != BAD_VAR_NUM); - resultBase = m_comp->gtNewLclvNode(resumeObjectArrLclNum, TYP_REF); - - if (call->gtReturnType == TYP_STRUCT) - { - // Boxed struct. - resultBase = LoadFromOffset(resultBase, OFFSETOF__CORINFO_Array__data, TYP_REF); - resultOffset = TARGET_POINTER_SIZE; // Offset of data inside box - } - else - { - assert(call->gtReturnType == TYP_REF); - resultOffset = OFFSETOF__CORINFO_Array__data; - } - } - else - { - assert(resumeByteArrLclNum != BAD_VAR_NUM); - resultBase = m_comp->gtNewLclvNode(resumeByteArrLclNum, TYP_REF); - resultOffset = OFFSETOF__CORINFO_Array__data + layout.ReturnValDataOffset; - if (layout.ReturnValDataOffset != 0) - resultIndirFlags = GTF_IND_UNALIGNED; - } + GenTree* resultBase = m_comp->gtNewLclvNode(m_comp->lvaAsyncContinuationArg, TYP_REF); + unsigned resultOffset = OFFSETOF__CORINFO_Continuation__data + layout.ReturnValOffset; assert(callDefInfo.DefinitionNode != nullptr); LclVarDsc* resultLcl = m_comp->lvaGetDesc(callDefInfo.DefinitionNode); + GenTreeFlags indirFlags = + GTF_IND_NONFAULTING | (layout.ReturnHeapAlignment() < layout.ReturnAlignment ? GTF_IND_UNALIGNED : GTF_EMPTY); + // TODO-TP: We can use liveness to avoid generating a lot of this IR. if (call->gtReturnType == TYP_STRUCT) { @@ -2083,7 +1952,7 @@ void AsyncTransformation::CopyReturnValueOnResumption(GenTreeCall* { GenTree* resultOffsetNode = m_comp->gtNewIconNode((ssize_t)resultOffset, TYP_I_IMPL); GenTree* resultAddr = m_comp->gtNewOperNode(GT_ADD, TYP_BYREF, resultBase, resultOffsetNode); - GenTree* resultData = m_comp->gtNewLoadValueNode(layout.ReturnStructLayout, resultAddr, resultIndirFlags); + GenTree* resultData = m_comp->gtNewLoadValueNode(layout.ReturnStructLayout, resultAddr, indirFlags); GenTree* storeResult; if ((callDefInfo.DefinitionNode->GetLclOffs() == 0) && ClassLayout::AreCompatible(resultLcl->GetLayout(), layout.ReturnStructLayout)) @@ -2122,7 +1991,7 @@ void AsyncTransformation::CopyReturnValueOnResumption(GenTreeCall* LclVarDsc* fieldDsc = m_comp->lvaGetDesc(fieldLclNum); unsigned fldOffset = resultOffset + fieldDsc->lvFldOffset; - GenTree* value = LoadFromOffset(resultBase, fldOffset, fieldDsc->TypeGet(), resultIndirFlags); + GenTree* value = LoadFromOffset(resultBase, fldOffset, fieldDsc->TypeGet(), indirFlags); GenTree* store = m_comp->gtNewStoreLclVarNode(fieldLclNum, value); LIR::AsRange(storeResultBB).InsertAtEnd(LIR::SeqTree(m_comp, store)); @@ -2135,7 +2004,7 @@ void AsyncTransformation::CopyReturnValueOnResumption(GenTreeCall* } else { - GenTree* value = LoadFromOffset(resultBase, resultOffset, call->gtReturnType, resultIndirFlags); + GenTree* value = LoadFromOffset(resultBase, resultOffset, call->gtReturnType, indirFlags); GenTree* storeResult; if (callDefInfo.DefinitionNode->OperIs(GT_STORE_LCL_VAR)) @@ -2188,63 +2057,22 @@ GenTreeIndir* AsyncTransformation::LoadFromOffset(GenTree* base, // offset - Offset to add on top of the base address // value - Value to store // storeType - Type of store +// indirFlags - Indirection flags // // Returns: // IR node of the store. // -GenTreeStoreInd* AsyncTransformation::StoreAtOffset(GenTree* base, unsigned offset, GenTree* value, var_types storeType) +GenTreeStoreInd* AsyncTransformation::StoreAtOffset( + GenTree* base, unsigned offset, GenTree* value, var_types storeType, GenTreeFlags indirFlags) { assert(base->TypeIs(TYP_REF, TYP_BYREF, TYP_I_IMPL)); GenTree* cns = m_comp->gtNewIconNode((ssize_t)offset, TYP_I_IMPL); var_types addrType = base->TypeIs(TYP_I_IMPL) ? TYP_I_IMPL : TYP_BYREF; GenTree* addr = m_comp->gtNewOperNode(GT_ADD, addrType, base, cns); - GenTreeStoreInd* store = m_comp->gtNewStoreIndNode(storeType, addr, value, GTF_IND_NONFAULTING); + GenTreeStoreInd* store = m_comp->gtNewStoreIndNode(storeType, addr, value, indirFlags); return store; } -//------------------------------------------------------------------------ -// AsyncTransformation::GetDataArrayVar: -// Create a new local to hold the data array of the continuation object. This -// local can be validly used for the entire suspension point; the returned -// local may be used by multiple suspension points. -// -// Returns: -// Local number. -// -unsigned AsyncTransformation::GetDataArrayVar() -{ - // Create separate locals unless we have many locals in the method for live - // range splitting purposes. This helps LSRA to avoid create additional - // callee saves that harm the prolog/epilog. - if ((m_dataArrayVar == BAD_VAR_NUM) || !m_comp->lvaHaveManyLocals()) - { - m_dataArrayVar = m_comp->lvaGrabTemp(false DEBUGARG("byte[] for continuation")); - m_comp->lvaGetDesc(m_dataArrayVar)->lvType = TYP_REF; - } - - return m_dataArrayVar; -} - -//------------------------------------------------------------------------ -// AsyncTransformation::GetGCDataArrayVar: -// Create a new local to hold the GC pointers array of the continuation -// object. This local can be validly used for the entire suspension point; -// the returned local may be used by multiple suspension points. -// -// Returns: -// Local number. -// -unsigned AsyncTransformation::GetGCDataArrayVar() -{ - if ((m_gcDataArrayVar == BAD_VAR_NUM) || !m_comp->lvaHaveManyLocals()) - { - m_gcDataArrayVar = m_comp->lvaGrabTemp(false DEBUGARG("object[] for continuation")); - m_comp->lvaGetDesc(m_gcDataArrayVar)->lvType = TYP_REF; - } - - return m_gcDataArrayVar; -} - //------------------------------------------------------------------------ // AsyncTransformation::GetResultBaseVar: // Create a new local to hold the base address of the incoming result from @@ -2477,10 +2305,8 @@ void AsyncTransformation::CreateResumptionSwitch() // We need to dispatch to the OSR version if the IL offset is non-negative. continuationArg = m_comp->gtNewLclvNode(m_comp->lvaAsyncContinuationArg, TYP_REF); - unsigned offsetOfData = m_comp->info.compCompHnd->getFieldOffset(m_asyncInfo->continuationDataFldHnd); - GenTree* dataArr = LoadFromOffset(continuationArg, offsetOfData, TYP_REF); - unsigned offsetOfIlOffset = OFFSETOF__CORINFO_Array__data; - GenTree* ilOffset = LoadFromOffset(dataArr, offsetOfIlOffset, TYP_INT); + unsigned offsetOfIlOffset = OFFSETOF__CORINFO_Continuation__data; + GenTree* ilOffset = LoadFromOffset(continuationArg, offsetOfIlOffset, TYP_INT); unsigned ilOffsetLclNum = m_comp->lvaGrabTemp(false DEBUGARG("IL offset for tier0 OSR method")); m_comp->lvaGetDesc(ilOffsetLclNum)->lvType = TYP_INT; GenTree* storeIlOffset = m_comp->gtNewStoreLclVarNode(ilOffsetLclNum, ilOffset); @@ -2530,10 +2356,8 @@ void AsyncTransformation::CreateResumptionSwitch() JITDUMP(" Created " FMT_BB " to check for Tier-0 continuations\n", checkILOffsetBB->bbNum); continuationArg = m_comp->gtNewLclvNode(m_comp->lvaAsyncContinuationArg, TYP_REF); - unsigned offsetOfData = m_comp->info.compCompHnd->getFieldOffset(m_asyncInfo->continuationDataFldHnd); - GenTree* dataArr = LoadFromOffset(continuationArg, offsetOfData, TYP_REF); - unsigned offsetOfIlOffset = OFFSETOF__CORINFO_Array__data; - GenTree* ilOffset = LoadFromOffset(dataArr, offsetOfIlOffset, TYP_INT); + unsigned offsetOfIlOffset = OFFSETOF__CORINFO_Continuation__data; + GenTree* ilOffset = LoadFromOffset(continuationArg, offsetOfIlOffset, TYP_INT); GenTree* zero = m_comp->gtNewIconNode(0); GenTree* ltZero = m_comp->gtNewOperNode(GT_LT, TYP_INT, ilOffset, zero); GenTree* jtrue = m_comp->gtNewOperNode(GT_JTRUE, TYP_VOID, ltZero); diff --git a/src/coreclr/jit/async.h b/src/coreclr/jit/async.h index e30aaf760e6395..6546ba02b7afde 100644 --- a/src/coreclr/jit/async.h +++ b/src/coreclr/jit/async.h @@ -5,34 +5,44 @@ struct LiveLocalInfo { unsigned LclNum; unsigned Alignment; - unsigned DataOffset; - unsigned DataSize; - unsigned GCDataIndex; - unsigned GCDataCount; + unsigned Offset; + unsigned Size; explicit LiveLocalInfo(unsigned lclNum) : LclNum(lclNum) { } + + unsigned HeapAlignment() const + { + return std::min(Alignment, (unsigned)TARGET_POINTER_SIZE); + } }; struct ContinuationLayout { - unsigned DataSize = 0; - unsigned GCRefsCount = 0; - ClassLayout* ReturnStructLayout = nullptr; - unsigned ReturnSize = 0; - bool ReturnInGCData = false; - unsigned ReturnValDataOffset = UINT_MAX; - unsigned ExceptionGCDataIndex = UINT_MAX; - unsigned ExecContextGCDataIndex = UINT_MAX; - unsigned ContinuationContextGCDataIndex = UINT_MAX; + unsigned Size = 0; + unsigned OSRILOffset = UINT_MAX; + unsigned ExceptionOffset = UINT_MAX; + unsigned ContinuationContextOffset = UINT_MAX; + unsigned KeepAliveOffset = UINT_MAX; + ClassLayout* ReturnStructLayout = nullptr; + unsigned ReturnAlignment = 0; + unsigned ReturnSize = 0; + unsigned ReturnValOffset = UINT_MAX; + unsigned ExecContextOffset = UINT_MAX; const jitstd::vector& Locals; + CORINFO_CLASS_HANDLE ClassHnd = NO_CLASS_HANDLE; explicit ContinuationLayout(const jitstd::vector& locals) : Locals(locals) { } + + unsigned ReturnHeapAlignment() const + { + return std::min(ReturnAlignment, (unsigned)TARGET_POINTER_SIZE); + } }; struct CallDefinitionInfo @@ -80,8 +90,11 @@ class AsyncTransformation const jitstd::vector& defs, jitstd::vector& liveLocals); + bool ContinuationNeedsKeepAlive(class AsyncLiveness& life); + ContinuationLayout LayOutContinuation(BasicBlock* block, GenTreeCall* call, + bool needsKeepAlive, jitstd::vector& liveLocals); void ClearSuspendedIndicator(BasicBlock* block, GenTreeCall* call); @@ -90,39 +103,27 @@ class AsyncTransformation BasicBlock* CreateSuspension( BasicBlock* block, GenTreeCall* call, unsigned stateNum, AsyncLiveness& life, const ContinuationLayout& layout); - GenTreeCall* CreateAllocContinuationCall(AsyncLiveness& life, - GenTree* prevContinuation, - unsigned gcRefsCount, - unsigned int dataSize); - void FillInGCPointersOnSuspension(GenTreeCall* call, const ContinuationLayout& layout, BasicBlock* suspendBB); - void FillInDataOnSuspension(const jitstd::vector& liveLocals, BasicBlock* suspendBB); - void CreateCheckAndSuspendAfterCall(BasicBlock* block, - GenTreeCall* call, - const CallDefinitionInfo& callDefInfo, - AsyncLiveness& life, - BasicBlock* suspendBB, - BasicBlock** remainder); - BasicBlock* CreateResumption(BasicBlock* block, - BasicBlock* remainder, - GenTreeCall* call, - const CallDefinitionInfo& callDefInfo, - unsigned stateNum, - const ContinuationLayout& layout); - void SetSuspendedIndicator(BasicBlock* block, BasicBlock* callBlock, GenTreeCall* call); - void RestoreFromDataOnResumption(unsigned resumeByteArrLclNum, - const jitstd::vector& liveLocals, - BasicBlock* resumeBB); - void RestoreFromGCPointersOnResumption(unsigned resumeObjectArrLclNum, - const ContinuationLayout& layout, - BasicBlock* resumeBB); - BasicBlock* RethrowExceptionOnResumption(BasicBlock* block, - unsigned resumeObjectArrLclNum, - const ContinuationLayout& layout, - BasicBlock* resumeBB); + GenTreeCall* CreateAllocContinuationCall(AsyncLiveness& life, + GenTree* prevContinuation, + const ContinuationLayout& layout); + void FillInDataOnSuspension(GenTreeCall* call, const ContinuationLayout& layout, BasicBlock* suspendBB); + void CreateCheckAndSuspendAfterCall(BasicBlock* block, + GenTreeCall* call, + const CallDefinitionInfo& callDefInfo, + AsyncLiveness& life, + BasicBlock* suspendBB, + BasicBlock** remainder); + BasicBlock* CreateResumption(BasicBlock* block, + BasicBlock* remainder, + GenTreeCall* call, + const CallDefinitionInfo& callDefInfo, + unsigned stateNum, + const ContinuationLayout& layout); + void SetSuspendedIndicator(BasicBlock* block, BasicBlock* callBlock, GenTreeCall* call); + void RestoreFromDataOnResumption(const ContinuationLayout& layout, BasicBlock* resumeBB); + BasicBlock* RethrowExceptionOnResumption(BasicBlock* block, const ContinuationLayout& layout, BasicBlock* resumeBB); void CopyReturnValueOnResumption(GenTreeCall* call, const CallDefinitionInfo& callDefInfo, - unsigned resumeByteArrLclNum, - unsigned resumeObjectArrLclNum, const ContinuationLayout& layout, BasicBlock* storeResultBB); @@ -130,10 +131,12 @@ class AsyncTransformation unsigned offset, var_types type, GenTreeFlags indirFlags = GTF_IND_NONFAULTING); - GenTreeStoreInd* StoreAtOffset(GenTree* base, unsigned offset, GenTree* value, var_types storeType); + GenTreeStoreInd* StoreAtOffset(GenTree* base, + unsigned offset, + GenTree* value, + var_types storeType, + GenTreeFlags indirFlags = GTF_IND_NONFAULTING); - unsigned GetDataArrayVar(); - unsigned GetGCDataArrayVar(); unsigned GetResultBaseVar(); unsigned GetExceptionVar(); diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 7538a26ef002ff..9f1900b1ba3496 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -3361,6 +3361,12 @@ private void getAsyncInfo(ref CORINFO_ASYNC_INFO pAsyncInfoOut) throw new NotImplementedException(); } + private CORINFO_CLASS_STRUCT_* getContinuationType(nuint dataSize, ref bool objRefs, nuint objRefsSize) + { + Debug.Assert(objRefsSize == (dataSize + (nuint)(PointerSize - 1)) / (nuint)PointerSize); + throw new NotImplementedException("getContinuationType"); + } + private mdToken getMethodDefFromMethod(CORINFO_METHOD_STRUCT_* hMethod) { MethodDesc method = HandleToObject(hMethod); diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs index 902a001c505a9f..b0a1781661517d 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs @@ -2297,6 +2297,21 @@ private static byte _getTailCallHelpers(IntPtr thisHandle, IntPtr* ppException, } } + [UnmanagedCallersOnly] + private static CORINFO_CLASS_STRUCT_* _getContinuationType(IntPtr thisHandle, IntPtr* ppException, nuint dataSize, bool* objRefs, nuint objRefsSize) + { + var _this = GetThis(thisHandle); + try + { + return _this.getContinuationType(dataSize, ref *objRefs, objRefsSize); + } + catch (Exception ex) + { + *ppException = _this.AllocException(ex); + return default; + } + } + [UnmanagedCallersOnly] private static CORINFO_METHOD_STRUCT_* _getAsyncResumptionStub(IntPtr thisHandle, IntPtr* ppException) { @@ -2606,7 +2621,7 @@ private static uint _getJitFlags(IntPtr thisHandle, IntPtr* ppException, CORJIT_ private static IntPtr GetUnmanagedCallbacks() { - void** callbacks = (void**)NativeMemory.Alloc((nuint)(sizeof(void*) * 176)); + void** callbacks = (void**)NativeMemory.Alloc((nuint)(sizeof(void*) * 177)); callbacks[0] = (delegate* unmanaged)&_isIntrinsic; callbacks[1] = (delegate* unmanaged)&_notifyMethodInfoUsage; @@ -2763,27 +2778,28 @@ private static IntPtr GetUnmanagedCallbacks() callbacks[152] = (delegate* unmanaged)&_GetDelegateCtor; callbacks[153] = (delegate* unmanaged)&_MethodCompileComplete; callbacks[154] = (delegate* unmanaged)&_getTailCallHelpers; - callbacks[155] = (delegate* unmanaged)&_getAsyncResumptionStub; - callbacks[156] = (delegate* unmanaged)&_convertPInvokeCalliToCall; - callbacks[157] = (delegate* unmanaged)&_notifyInstructionSetUsage; - callbacks[158] = (delegate* unmanaged)&_updateEntryPointForTailCall; - callbacks[159] = (delegate* unmanaged)&_allocMem; - callbacks[160] = (delegate* unmanaged)&_reserveUnwindInfo; - callbacks[161] = (delegate* unmanaged)&_allocUnwindInfo; - callbacks[162] = (delegate* unmanaged)&_allocGCInfo; - callbacks[163] = (delegate* unmanaged)&_setEHcount; - callbacks[164] = (delegate* unmanaged)&_setEHinfo; - callbacks[165] = (delegate* unmanaged)&_logMsg; - callbacks[166] = (delegate* unmanaged)&_doAssert; - callbacks[167] = (delegate* unmanaged)&_reportFatalError; - callbacks[168] = (delegate* unmanaged)&_getPgoInstrumentationResults; - callbacks[169] = (delegate* unmanaged)&_allocPgoInstrumentationBySchema; - callbacks[170] = (delegate* unmanaged)&_recordCallSite; - callbacks[171] = (delegate* unmanaged)&_recordRelocation; - callbacks[172] = (delegate* unmanaged)&_getRelocTypeHint; - callbacks[173] = (delegate* unmanaged)&_getExpectedTargetArchitecture; - callbacks[174] = (delegate* unmanaged)&_getJitFlags; - callbacks[175] = (delegate* unmanaged)&_getSpecialCopyHelper; + callbacks[155] = (delegate* unmanaged)&_getContinuationType; + callbacks[156] = (delegate* unmanaged)&_getAsyncResumptionStub; + callbacks[157] = (delegate* unmanaged)&_convertPInvokeCalliToCall; + callbacks[158] = (delegate* unmanaged)&_notifyInstructionSetUsage; + callbacks[159] = (delegate* unmanaged)&_updateEntryPointForTailCall; + callbacks[160] = (delegate* unmanaged)&_allocMem; + callbacks[161] = (delegate* unmanaged)&_reserveUnwindInfo; + callbacks[162] = (delegate* unmanaged)&_allocUnwindInfo; + callbacks[163] = (delegate* unmanaged)&_allocGCInfo; + callbacks[164] = (delegate* unmanaged)&_setEHcount; + callbacks[165] = (delegate* unmanaged)&_setEHinfo; + callbacks[166] = (delegate* unmanaged)&_logMsg; + callbacks[167] = (delegate* unmanaged)&_doAssert; + callbacks[168] = (delegate* unmanaged)&_reportFatalError; + callbacks[169] = (delegate* unmanaged)&_getPgoInstrumentationResults; + callbacks[170] = (delegate* unmanaged)&_allocPgoInstrumentationBySchema; + callbacks[171] = (delegate* unmanaged)&_recordCallSite; + callbacks[172] = (delegate* unmanaged)&_recordRelocation; + callbacks[173] = (delegate* unmanaged)&_getRelocTypeHint; + callbacks[174] = (delegate* unmanaged)&_getExpectedTargetArchitecture; + callbacks[175] = (delegate* unmanaged)&_getJitFlags; + callbacks[176] = (delegate* unmanaged)&_getSpecialCopyHelper; return (IntPtr)callbacks; } diff --git a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt index cbcfd25f32b359..2a06ed81d01bd5 100644 --- a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt +++ b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt @@ -321,6 +321,7 @@ FUNCTIONS CORINFO_METHOD_HANDLE GetDelegateCtor(CORINFO_METHOD_HANDLE methHnd, CORINFO_CLASS_HANDLE clsHnd, CORINFO_METHOD_HANDLE targetMethodHnd, DelegateCtorArgs * pCtorData); void MethodCompileComplete(CORINFO_METHOD_HANDLE methHnd); bool getTailCallHelpers(CORINFO_RESOLVED_TOKEN* callToken, CORINFO_SIG_INFO* sig, CORINFO_GET_TAILCALL_HELPERS_FLAGS flags, CORINFO_TAILCALL_HELPERS* pResult); + CORINFO_CLASS_HANDLE getContinuationType(size_t dataSize, bool* objRefs, size_t objRefsSize); CORINFO_METHOD_HANDLE getAsyncResumptionStub(); bool convertPInvokeCalliToCall(CORINFO_RESOLVED_TOKEN * pResolvedToken, bool mustConvert); bool notifyInstructionSetUsage(CORINFO_InstructionSet instructionSet,bool supportEnabled); diff --git a/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h b/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h index 186d8ce372117e..cf8ea9deb5b2ff 100644 --- a/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h +++ b/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h @@ -166,6 +166,7 @@ struct JitInterfaceCallbacks CORINFO_METHOD_HANDLE (* GetDelegateCtor)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_METHOD_HANDLE methHnd, CORINFO_CLASS_HANDLE clsHnd, CORINFO_METHOD_HANDLE targetMethodHnd, DelegateCtorArgs* pCtorData); void (* MethodCompileComplete)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_METHOD_HANDLE methHnd); bool (* getTailCallHelpers)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_RESOLVED_TOKEN* callToken, CORINFO_SIG_INFO* sig, CORINFO_GET_TAILCALL_HELPERS_FLAGS flags, CORINFO_TAILCALL_HELPERS* pResult); + CORINFO_CLASS_HANDLE (* getContinuationType)(void * thisHandle, CorInfoExceptionClass** ppException, size_t dataSize, bool* objRefs, size_t objRefsSize); CORINFO_METHOD_HANDLE (* getAsyncResumptionStub)(void * thisHandle, CorInfoExceptionClass** ppException); bool (* convertPInvokeCalliToCall)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_RESOLVED_TOKEN* pResolvedToken, bool mustConvert); bool (* notifyInstructionSetUsage)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_InstructionSet instructionSet, bool supportEnabled); @@ -1714,6 +1715,17 @@ class JitInterfaceWrapper : public ICorJitInfo return temp; } + virtual CORINFO_CLASS_HANDLE getContinuationType( + size_t dataSize, + bool* objRefs, + size_t objRefsSize) +{ + CorInfoExceptionClass* pException = nullptr; + CORINFO_CLASS_HANDLE temp = _callbacks->getContinuationType(_thisHandle, &pException, dataSize, objRefs, objRefsSize); + if (pException != nullptr) throw pException; + return temp; +} + virtual CORINFO_METHOD_HANDLE getAsyncResumptionStub() { CorInfoExceptionClass* pException = nullptr; diff --git a/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h b/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h index 01ec01427fa862..d45676ab5a8784 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h @@ -200,9 +200,6 @@ struct Agnostic_CORINFO_ASYNC_INFO DWORDLONG continuationResumeFldHnd; DWORDLONG continuationStateFldHnd; DWORDLONG continuationFlagsFldHnd; - DWORDLONG continuationDataFldHnd; - DWORDLONG continuationGCDataFldHnd; - DWORD continuationsNeedMethodHandle; DWORDLONG captureExecutionContextMethHnd; DWORDLONG restoreExecutionContextMethHnd; DWORDLONG captureContinuationContextMethHnd; @@ -662,6 +659,13 @@ struct Agnostic_GetFpStructLowering DWORD numLoweredElements; }; +struct Agnostic_GetContinuationTypeIn +{ + DWORDLONG dataSize; + DWORD objRefs; + DWORD objRefsSize; +}; + struct Agnostic_ResolveVirtualMethodKey { DWORDLONG virtualMethod; diff --git a/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h b/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h index cc0878ee3d7d66..a52578a288bc15 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h @@ -130,6 +130,7 @@ LWM(GetSwiftLowering, DWORDLONG, Agnostic_GetSwiftLowering) LWM(GetFpStructLowering, DWORDLONG, Agnostic_GetFpStructLowering) LWM(GetTailCallHelpers, Agnostic_GetTailCallHelpers, Agnostic_CORINFO_TAILCALL_HELPERS) LWM(GetAsyncResumptionStub, DWORD, DWORDLONG) +LWM(GetContinuationType, Agnostic_GetContinuationTypeIn, DWORDLONG) LWM(UpdateEntryPointForTailCall, Agnostic_CORINFO_CONST_LOOKUP, Agnostic_CORINFO_CONST_LOOKUP) LWM(GetSpecialCopyHelper, DWORDLONG, DWORDLONG) LWM(GetThreadTLSIndex, DWORD, DLD) diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp index ac87130dba1785..7dad7bac1fc57b 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp @@ -4457,9 +4457,6 @@ void MethodContext::recGetAsyncInfo(const CORINFO_ASYNC_INFO* pAsyncInfo) value.continuationResumeFldHnd = CastHandle(pAsyncInfo->continuationResumeFldHnd); value.continuationStateFldHnd = CastHandle(pAsyncInfo->continuationStateFldHnd); value.continuationFlagsFldHnd = CastHandle(pAsyncInfo->continuationFlagsFldHnd); - value.continuationDataFldHnd = CastHandle(pAsyncInfo->continuationDataFldHnd); - value.continuationGCDataFldHnd = CastHandle(pAsyncInfo->continuationGCDataFldHnd); - value.continuationsNeedMethodHandle = pAsyncInfo->continuationsNeedMethodHandle ? 1 : 0; value.captureExecutionContextMethHnd = CastHandle(pAsyncInfo->captureExecutionContextMethHnd); value.restoreExecutionContextMethHnd = CastHandle(pAsyncInfo->restoreExecutionContextMethHnd); value.captureContinuationContextMethHnd = CastHandle(pAsyncInfo->captureContinuationContextMethHnd); @@ -4472,10 +4469,9 @@ void MethodContext::recGetAsyncInfo(const CORINFO_ASYNC_INFO* pAsyncInfo) void MethodContext::dmpGetAsyncInfo(DWORD key, const Agnostic_CORINFO_ASYNC_INFO& value) { printf("GetAsyncInfo key %u value contClsHnd-%016" PRIX64 " contNextFldHnd-%016" PRIX64 " contResumeFldHnd-%016" PRIX64 - " contStateFldHnd-%016" PRIX64 " contFlagsFldHnd-%016" PRIX64 " contDataFldHnd-%016" PRIX64 " contGCDataFldHnd-%016" PRIX64 " contsNeedMethodHandle-%d", + " contStateFldHnd-%016" PRIX64 " contFlagsFldHnd-%016" PRIX64, key, value.continuationClsHnd, value.continuationNextFldHnd, value.continuationResumeFldHnd, - value.continuationStateFldHnd, value.continuationFlagsFldHnd, value.continuationDataFldHnd, - value.continuationGCDataFldHnd, value.continuationsNeedMethodHandle); + value.continuationStateFldHnd, value.continuationFlagsFldHnd); } void MethodContext::repGetAsyncInfo(CORINFO_ASYNC_INFO* pAsyncInfoOut) { @@ -4485,9 +4481,6 @@ void MethodContext::repGetAsyncInfo(CORINFO_ASYNC_INFO* pAsyncInfoOut) pAsyncInfoOut->continuationResumeFldHnd = (CORINFO_FIELD_HANDLE)value.continuationResumeFldHnd; pAsyncInfoOut->continuationStateFldHnd = (CORINFO_FIELD_HANDLE)value.continuationStateFldHnd; pAsyncInfoOut->continuationFlagsFldHnd = (CORINFO_FIELD_HANDLE)value.continuationFlagsFldHnd; - pAsyncInfoOut->continuationDataFldHnd = (CORINFO_FIELD_HANDLE)value.continuationDataFldHnd; - pAsyncInfoOut->continuationGCDataFldHnd = (CORINFO_FIELD_HANDLE)value.continuationGCDataFldHnd; - pAsyncInfoOut->continuationsNeedMethodHandle = value.continuationsNeedMethodHandle != 0; pAsyncInfoOut->captureExecutionContextMethHnd = (CORINFO_METHOD_HANDLE)value.captureExecutionContextMethHnd; pAsyncInfoOut->restoreExecutionContextMethHnd = (CORINFO_METHOD_HANDLE)value.restoreExecutionContextMethHnd; pAsyncInfoOut->captureContinuationContextMethHnd = (CORINFO_METHOD_HANDLE)value.captureContinuationContextMethHnd; @@ -6946,6 +6939,56 @@ CORINFO_METHOD_HANDLE MethodContext::repGetAsyncResumptionStub() return (CORINFO_METHOD_HANDLE)hnd; } +void MethodContext::recGetContinuationType(size_t dataSize, + bool* objRefs, + size_t objRefsSize, + CORINFO_CLASS_HANDLE result) +{ + if (GetContinuationType == nullptr) + { + GetContinuationType = new LightWeightMap(); + } + + Agnostic_GetContinuationTypeIn key; + ZeroMemory(&key, sizeof(key)); + key.dataSize = (DWORDLONG)dataSize; + key.objRefs = GetContinuationType->AddBuffer((unsigned char*)objRefs, (unsigned)objRefsSize); + key.objRefsSize = (DWORD)objRefsSize; + + DWORDLONG value = CastHandle(result); + + GetContinuationType->Add(key, value); + DEBUG_REC(dmpGetContinuationType(key, value)); +} + +void MethodContext::dmpGetContinuationType(const Agnostic_GetContinuationTypeIn& key, DWORDLONG value) +{ + printf("GetContinuationType dataSize-%016" PRIX64 ", objRefs-%u, handle-%016" PRIX64, + key.dataSize, key.objRefs, value); +} + +CORINFO_CLASS_HANDLE MethodContext::repGetContinuationType(size_t dataSize, + bool* objRefs, + size_t objRefsSize) +{ + AssertMapExistsNoMessage(GetContinuationType); + + int objRefsBuffer = GetContinuationType->Contains((unsigned char*)objRefs, (unsigned)objRefsSize); + if (objRefsBuffer == -1) + LogException(EXCEPTIONCODE_MC, "SuperPMI assertion failed (missing obj refs buffer)", ""); + + Agnostic_GetContinuationTypeIn key; + ZeroMemory(&key, sizeof(key)); + key.dataSize = (DWORDLONG)dataSize; + key.objRefs = objRefsBuffer; + key.objRefsSize = (DWORD)objRefsSize; + + DWORDLONG value = LookupByKeyOrMiss(GetContinuationType, key, ": dataSize %016" PRIX64 ", objRefs %u", key.dataSize, key.objRefs); + DEBUG_REP(dmpGetContinuationType(key, value)); + + return (CORINFO_CLASS_HANDLE)value; +} + void MethodContext::recUpdateEntryPointForTailCall( const CORINFO_CONST_LOOKUP& origEntryPoint, const CORINFO_CONST_LOOKUP& newEntryPoint) diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h index d9958e6c2988ed..c5d7f82923841e 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h @@ -867,6 +867,10 @@ class MethodContext void dmpGetAsyncResumptionStub(DWORD key, DWORDLONG handle); CORINFO_METHOD_HANDLE repGetAsyncResumptionStub(); + void recGetContinuationType(size_t dataSize, bool* objRefs, size_t objRefsSize, CORINFO_CLASS_HANDLE result); + void dmpGetContinuationType(const Agnostic_GetContinuationTypeIn& key, DWORDLONG value); + CORINFO_CLASS_HANDLE repGetContinuationType(size_t dataSize, bool* objRefs, size_t objRefsSize); + void recUpdateEntryPointForTailCall(const CORINFO_CONST_LOOKUP& origEntryPoint, const CORINFO_CONST_LOOKUP& newEntryPoint); void dmpUpdateEntryPointForTailCall(const Agnostic_CORINFO_CONST_LOOKUP& origEntryPoint, const Agnostic_CORINFO_CONST_LOOKUP& newEntryPoint); void repUpdateEntryPointForTailCall(CORINFO_CONST_LOOKUP* entryPoint); @@ -1208,6 +1212,7 @@ enum mcPackets Packet_GetAsyncResumptionStub = 231, Packet_GetCookieForInterpreterCalliSig = 232, Packet_GetHelperFtn = 233, + Packet_GetContinuationType = 234, }; void SetDebugDumpVariables(); diff --git a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp index c214cc7c1005b6..e8250d6a45c483 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp @@ -1782,6 +1782,14 @@ CORINFO_METHOD_HANDLE interceptor_ICJI::getAsyncResumptionStub() return stub; } +CORINFO_CLASS_HANDLE interceptor_ICJI::getContinuationType(size_t dataSize, bool* objRefs, size_t objRefsSize) +{ + mc->cr->AddCall("getContinuationType"); + CORINFO_CLASS_HANDLE result = original_ICorJitInfo->getContinuationType(dataSize, objRefs, objRefsSize); + mc->recGetContinuationType(dataSize, objRefs, objRefsSize, result); + return result; +} + void interceptor_ICJI::updateEntryPointForTailCall(CORINFO_CONST_LOOKUP* entryPoint) { mc->cr->AddCall("updateEntryPointForTailCall"); diff --git a/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp b/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp index db7a78298949fe..e78124fc9642fa 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp @@ -1266,6 +1266,15 @@ bool interceptor_ICJI::getTailCallHelpers( return original_ICorJitInfo->getTailCallHelpers(callToken, sig, flags, pResult); } +CORINFO_CLASS_HANDLE interceptor_ICJI::getContinuationType( + size_t dataSize, + bool* objRefs, + size_t objRefsSize) +{ + mcs->AddCall("getContinuationType"); + return original_ICorJitInfo->getContinuationType(dataSize, objRefs, objRefsSize); +} + CORINFO_METHOD_HANDLE interceptor_ICJI::getAsyncResumptionStub() { mcs->AddCall("getAsyncResumptionStub"); diff --git a/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp b/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp index 5f5df1a3bff589..7937d140a0d275 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp @@ -1111,6 +1111,14 @@ bool interceptor_ICJI::getTailCallHelpers( return original_ICorJitInfo->getTailCallHelpers(callToken, sig, flags, pResult); } +CORINFO_CLASS_HANDLE interceptor_ICJI::getContinuationType( + size_t dataSize, + bool* objRefs, + size_t objRefsSize) +{ + return original_ICorJitInfo->getContinuationType(dataSize, objRefs, objRefsSize); +} + CORINFO_METHOD_HANDLE interceptor_ICJI::getAsyncResumptionStub() { return original_ICorJitInfo->getAsyncResumptionStub(); diff --git a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp index 6c9f758d6b9425..b022efbc7c600b 100644 --- a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp @@ -1508,6 +1508,13 @@ CORINFO_METHOD_HANDLE MyICJI::getAsyncResumptionStub() return jitInstance->mc->repGetAsyncResumptionStub();; } +CORINFO_CLASS_HANDLE MyICJI::getContinuationType(size_t dataSize, bool* objRefs, size_t objRefsSize) +{ + jitInstance->mc->cr->AddCall("getContinuationType"); + CORINFO_CLASS_HANDLE result = jitInstance->mc->repGetContinuationType(dataSize, objRefs, objRefsSize); + return result; +} + bool MyICJI::convertPInvokeCalliToCall(CORINFO_RESOLVED_TOKEN* pResolvedToken, bool fMustConvert) { jitInstance->mc->cr->AddCall("convertPInvokeCalliToCall"); diff --git a/src/coreclr/vm/CMakeLists.txt b/src/coreclr/vm/CMakeLists.txt index f078c15b2a1b3c..93d191d335c911 100644 --- a/src/coreclr/vm/CMakeLists.txt +++ b/src/coreclr/vm/CMakeLists.txt @@ -52,6 +52,7 @@ set(VM_SOURCES_DAC_AND_WKS_COMMON array.cpp assembly.cpp assemblybinder.cpp + asynccontinuations.cpp binder.cpp castcache.cpp callcounting.cpp @@ -149,6 +150,7 @@ set(VM_HEADERS_DAC_AND_WKS_COMMON array.h assembly.hpp assemblybinder.h + asynccontinuations.h binder.h castcache.h callcounting.h diff --git a/src/coreclr/vm/asynccontinuations.cpp b/src/coreclr/vm/asynccontinuations.cpp new file mode 100644 index 00000000000000..c9f954015d76c6 --- /dev/null +++ b/src/coreclr/vm/asynccontinuations.cpp @@ -0,0 +1,405 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// =========================================================================== +// File: asynccontinuations.cpp +// + +// =========================================================================== +// This file contains the manager for creating new dynamic async continuation types. +// =========================================================================== +// + +#include "common.h" +#include "asynccontinuations.h" + +#ifndef DACCESS_COMPILE + +AsyncContinuationsManager::AsyncContinuationsManager(LoaderAllocator* allocator) + : m_allocator(allocator) +{ + LIMITED_METHOD_CONTRACT; + + m_layoutsLock.Init(CrstLeafLock); + LockOwner lock = {&m_layoutsLock, IsOwnerOfCrst}; + m_layouts.Init(16, &lock, m_allocator->GetLowFrequencyHeap()); +} + +void AsyncContinuationsManager::NotifyUnloadingClasses() +{ + if (!CORProfilerTrackClasses()) + { + return; + } + + EEHashTableIteration iter; + m_layouts.IterateStart(&iter); + while (m_layouts.IterateNext(&iter)) + { + MethodTable* pMT = (MethodTable*)m_layouts.IterateGetValue(&iter); + ClassLoader::NotifyUnload(pMT, true); + ClassLoader::NotifyUnload(pMT, false); + } +} + +static EEClass* volatile g_singletonContinuationEEClass; + +EEClass* AsyncContinuationsManager::GetOrCreateSingletonSubContinuationEEClass() +{ + if (g_singletonContinuationEEClass != NULL) + return g_singletonContinuationEEClass; + + return CreateSingletonSubContinuationEEClass(); +} + +EEClass* AsyncContinuationsManager::CreateSingletonSubContinuationEEClass() +{ + AllocMemTracker amTracker; + + Module* spc = SystemDomain::SystemModule(); + LoaderAllocator* allocator = SystemDomain::GetGlobalLoaderAllocator(); + + EEClass* pClass = EEClass::CreateMinimalClass(allocator->GetHighFrequencyHeap(), &amTracker); + + MethodTable* pMT = CreateNewContinuationMethodTable(0, NULL, pClass, allocator, spc, &amTracker); + + pClass->SetMethodTable(pMT); + +#ifdef _DEBUG + pClass->SetDebugClassName("Continuation"); + pMT->SetDebugClassName("Continuation"); +#endif + + if (InterlockedCompareExchangeT(&g_singletonContinuationEEClass, pClass, NULL) == NULL) + { + amTracker.SuppressRelease(); + } + + return g_singletonContinuationEEClass; +} + +template +static bool EnumerateRunsOfObjRefs(unsigned size, const bool* objRefs, TFunc func) +{ + const bool* objRefsEnd = objRefs + (size + (TARGET_POINTER_SIZE - 1)) / TARGET_POINTER_SIZE; + const bool* start = objRefs; + while (start < objRefsEnd) + { + while (start < objRefsEnd && !*start) + start++; + + if (start >= objRefsEnd) + return true; + + const bool* end = start; + while (end < objRefsEnd && *end) + end++; + + if (!func((start - objRefs) * TARGET_POINTER_SIZE, end - start)) + return false; + + start = end; + } + + return true; +} + +MethodTable* AsyncContinuationsManager::CreateNewContinuationMethodTable( + unsigned dataSize, + const bool* objRefs, + EEClass* pClass, + LoaderAllocator* allocator, + Module* loaderModule, + AllocMemTracker* pamTracker) +{ + STANDARD_VM_CONTRACT; + + MethodTable* pParentClass = CoreLibBinder::GetClass(CLASS__CONTINUATION); + + if (g_pContinuationClassIfSubTypeCreated.Load() == NULL) + { + g_pContinuationClassIfSubTypeCreated.Store(pParentClass); + } + + DWORD numVirtuals = pParentClass->GetNumVirtuals(); + + size_t cbMT = sizeof(MethodTable); + cbMT += MethodTable::GetNumVtableIndirections(numVirtuals) * sizeof(MethodTable::VTableIndir_t); + + unsigned numObjRefRuns = 0; + EnumerateRunsOfObjRefs(dataSize, objRefs, [&](size_t start, size_t count) + { + numObjRefRuns++; + return true; + }); + + unsigned numParentPointerSeries = 0; + if (pParentClass->ContainsGCPointers()) + numParentPointerSeries = static_cast(CGCDesc::GetCGCDescFromMT(pParentClass)->GetNumSeries()); + + unsigned numPointerSeries = numParentPointerSeries + numObjRefRuns; + + size_t cbGC = numPointerSeries == 0 ? 0 : CGCDesc::ComputeSize(numPointerSeries); + + BYTE* pMemory = (BYTE*)pamTracker->Track(allocator->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(cbGC) + S_SIZE_T(cbMT))); + + unsigned startOfDataInInstance = AlignUp(pParentClass->GetNumInstanceFieldBytes(), TARGET_POINTER_SIZE); + unsigned startOfDataInObject = OBJECT_SIZE + startOfDataInInstance; + + MethodTable* pMT = (MethodTable*)(pMemory + cbGC); + pMT->AllocateAuxiliaryData(allocator, loaderModule, pamTracker, MethodTableStaticsFlags::None); + pMT->SetParentMethodTable(pParentClass); + pMT->SetLoaderAllocator(allocator); + pMT->SetModule(pParentClass->GetModule()); + pMT->SetNumVirtuals(static_cast(numVirtuals)); + pMT->SetClass(pClass); + pMT->SetBaseSize(OBJECT_BASESIZE + startOfDataInInstance + dataSize); + + if (numPointerSeries > 0) + { + pMT->SetContainsGCPointers(); + CGCDesc::Init(pMT, numPointerSeries); + CGCDescSeries* pSeries = CGCDesc::GetCGCDescFromMT(pMT)->GetLowestSeries(); + + // Write GC runs. In memory they must be descending by offset, so we + // enumerate forwards but write them from the end. + unsigned curIndex = numPointerSeries; + CGCDescSeries* pParentSeries = CGCDesc::GetCGCDescFromMT(pParentClass)->GetLowestSeries(); + for (unsigned i = numParentPointerSeries; i--;) + { + curIndex--; + pSeries[curIndex].SetSeriesSize((pParentSeries[i].GetSeriesSize() + pParentClass->GetBaseSize()) - pMT->GetBaseSize()); + pSeries[curIndex].SetSeriesOffset(pParentSeries[i].GetSeriesOffset()); + } + + auto writeSeries = [&](size_t start, size_t count) { + curIndex--; + pSeries[curIndex].SetSeriesSize((count * TARGET_POINTER_SIZE) - pMT->GetBaseSize()); + pSeries[curIndex].SetSeriesOffset(startOfDataInObject + start); + return true; + }; + EnumerateRunsOfObjRefs(dataSize, objRefs, writeSeries); + + _ASSERTE(curIndex == 0); + } + + pMT->SetClassInited(); + INDEBUG(pMT->GetAuxiliaryDataForWrite()->SetIsPublished()); + + return pMT; +} + +MethodTable* AsyncContinuationsManager::CreateNewContinuationMethodTable( + unsigned dataSize, + const bool* objRefs, + MethodDesc* asyncMethod, + AllocMemTracker* pamTracker) +{ + MethodTable* pMT = CreateNewContinuationMethodTable( + dataSize, + objRefs, + GetOrCreateSingletonSubContinuationEEClass(), + m_allocator, + asyncMethod->GetLoaderModule(), + pamTracker); + +#ifdef DEBUG + StackSString debugName; + PrintContinuationName( + pMT, + [&](LPCSTR str, LPCWSTR wstr) { debugName.AppendUTF8(str); }, + [&](unsigned num) { debugName.AppendPrintf("%u", num); }); + const char* debugNameUTF8 = debugName.GetUTF8(); + size_t len = strlen(debugNameUTF8) + 1; + char* name = (char*)pamTracker->Track(m_allocator->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(len))); + strcpy_s(name, len, debugNameUTF8); + pMT->SetDebugClassName(name); +#endif + + return pMT; +} + +MethodTable* AsyncContinuationsManager::LookupOrCreateContinuationMethodTable(unsigned dataSize, const bool* objRefs, MethodDesc* asyncMethod) +{ + STANDARD_VM_CONTRACT; + + ContinuationLayoutKeyData keyData(dataSize, objRefs); + { + CrstHolder lock(&m_layoutsLock); + MethodTable* lookupResult; + if (m_layouts.GetValue(ContinuationLayoutKey(&keyData), (HashDatum*)&lookupResult)) + { + return lookupResult; + } + } + +#ifdef FEATURE_EVENT_TRACE + UINT32 typeLoad = ETW::TypeSystemLog::TypeLoadBegin(); +#endif + + AllocMemTracker amTracker; + MethodTable* pNewMT = CreateNewContinuationMethodTable(dataSize, objRefs, asyncMethod, &amTracker); + MethodTable* pReturnedMT = pNewMT; + { + CrstHolder lock(&m_layoutsLock); + MethodTable* lookupResult; + if (m_layouts.GetValue(ContinuationLayoutKey(&keyData), (HashDatum*)&lookupResult)) + { + pReturnedMT = lookupResult; + } + else + { + m_layouts.InsertValue(ContinuationLayoutKey(pNewMT), pNewMT); + } + } + + if (pReturnedMT == pNewMT) + { + amTracker.SuppressRelease(); + + ClassLoader::NotifyLoad(TypeHandle(pNewMT)); + } + +#ifdef FEATURE_EVENT_TRACE + if (ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context, TypeLoadStop)) + { + ETW::TypeSystemLog::TypeLoadEnd(typeLoad, TypeHandle(pReturnedMT), CLASS_LOADED); + } +#endif + + return pReturnedMT; +} + +ContinuationLayoutKey::ContinuationLayoutKey(MethodTable* pMT) + : Data(reinterpret_cast(pMT) | 1) +{ +} + +ContinuationLayoutKey::ContinuationLayoutKey(ContinuationLayoutKeyData* pData) + : Data(reinterpret_cast(pData)) +{ +} + +EEHashEntry_t* ContinuationLayoutKeyHashTableHelper::AllocateEntry(ContinuationLayoutKey key, BOOL bDeepCopy, AllocationHeap heap) +{ + CONTRACTL + { + WRAPPER(THROWS); + WRAPPER(GC_NOTRIGGER); + INJECT_FAULT(return FALSE;); + } + CONTRACTL_END + + EEHashEntry_t* pEntry = (EEHashEntry_t*)new (nothrow) BYTE[SIZEOF_EEHASH_ENTRY + sizeof(ContinuationLayoutKey)]; + if (pEntry == NULL) + return NULL; + memcpy(pEntry->Key, &key, sizeof(ContinuationLayoutKey)); + return pEntry; +} + +void ContinuationLayoutKeyHashTableHelper::DeleteEntry(EEHashEntry_t *pEntry, AllocationHeap heap) +{ + LIMITED_METHOD_CONTRACT; + + delete [] (BYTE*) pEntry; +} + +static CGCDescSeries* NextSeriesInData(CGCDescSeries* curSeries, CGCDescSeries* lowestSeries) +{ + while (curSeries >= lowestSeries && curSeries->GetSeriesOffset() < OFFSETOF__CORINFO_Continuation__data) + { + curSeries--; + } + + return curSeries; +} + +BOOL ContinuationLayoutKeyHashTableHelper::CompareKeys(EEHashEntry_t *pEntry, ContinuationLayoutKey key) +{ + LIMITED_METHOD_CONTRACT; + ContinuationLayoutKey storedKey; + memcpy(&storedKey, pEntry->Key, sizeof(ContinuationLayoutKey)); + + _ASSERTE((storedKey.Data & 1) != 0); // Always stores a MethodTable* as key + _ASSERTE((key.Data & 1) == 0); // Always uses ContinuationLayoutKeyData* as lookup key + + MethodTable* left = reinterpret_cast(storedKey.Data ^ 1); + ContinuationLayoutKeyData* right = reinterpret_cast(key.Data); + if (left->GetBaseSize() != (OBJHEADER_SIZE + OFFSETOF__CORINFO_Continuation__data + right->DataSize)) + return FALSE; + + // Now compare GC pointer series. + CGCDesc* gc = CGCDesc::GetCGCDescFromMT(left); + CGCDescSeries* curSeries = gc->GetHighestSeries(); + CGCDescSeries* lowestSeries = gc->GetLowestSeries(); + + // Skip ahead to first series inside the data region. + curSeries = NextSeriesInData(curSeries, lowestSeries); + + // Now verify that the series in the data match the bitmap. + auto compare = [=, &curSeries](size_t offset, size_t count) + { + if (curSeries < lowestSeries) + return false; + + if (OFFSETOF__CORINFO_Continuation__data + offset != curSeries->GetSeriesOffset()) + return false; + + if (count * TARGET_POINTER_SIZE != curSeries->GetSeriesSize() + left->GetBaseSize()) + return false; + + curSeries--; + return true; + }; + + if (!EnumerateRunsOfObjRefs(right->DataSize, right->ObjRefs, compare)) + { + return false; + } + + // Finally verify that we ran out of GC pointers simultaneously. + return curSeries + 1 == lowestSeries; +} + +DWORD ContinuationLayoutKeyHashTableHelper::Hash(ContinuationLayoutKey key) +{ + DWORD dwHash = 5381; + + if ((key.Data & 1) != 0) + { + MethodTable* pMT = reinterpret_cast(key.Data ^ 1); + CGCDesc* gc = CGCDesc::GetCGCDescFromMT(pMT); + CGCDescSeries* curSeries = gc->GetHighestSeries(); + CGCDescSeries* lowestSeries = gc->GetLowestSeries(); + + // Skip ahead to first series inside the data region. + curSeries = NextSeriesInData(curSeries, lowestSeries); + + dwHash = ((dwHash << 5) + dwHash) ^ (pMT->GetBaseSize() - (OBJHEADER_SIZE + OFFSETOF__CORINFO_Continuation__data)); + while (curSeries >= lowestSeries) + { + dwHash = ((dwHash << 5) + dwHash) ^ (unsigned)(curSeries->GetSeriesOffset() - OFFSETOF__CORINFO_Continuation__data); + dwHash = ((dwHash << 5) + dwHash) ^ (unsigned)((curSeries->GetSeriesSize() + pMT->GetBaseSize()) / TARGET_POINTER_SIZE); + curSeries--; + } + } + else + { + ContinuationLayoutKeyData* keyData = reinterpret_cast(key.Data); + + dwHash = ((dwHash << 5) + dwHash) ^ keyData->DataSize; + EnumerateRunsOfObjRefs(keyData->DataSize, keyData->ObjRefs, [&dwHash](size_t offset, size_t count){ + dwHash = ((dwHash << 5) + dwHash) ^ (unsigned)offset; + dwHash = ((dwHash << 5) + dwHash) ^ (unsigned)count; + return true; + }); + } + + return dwHash; +} + +void ContinuationLayoutKeyHashTableHelper::ReplaceKey(EEHashEntry_t *pEntry, ContinuationLayoutKey newKey) +{ + memcpy(pEntry->Key, &newKey, sizeof(ContinuationLayoutKey)); +} + +#endif diff --git a/src/coreclr/vm/asynccontinuations.h b/src/coreclr/vm/asynccontinuations.h new file mode 100644 index 00000000000000..ad577fb4fa00b3 --- /dev/null +++ b/src/coreclr/vm/asynccontinuations.h @@ -0,0 +1,87 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// =========================================================================== +// File: asynccontinuations.h +// +// =========================================================================== + +#ifndef ASYNCCONTINUATIONS_H +#define ASYNCCONTINUATIONS_H + +struct ContinuationLayoutKeyData +{ + unsigned DataSize; + const bool* ObjRefs; + + ContinuationLayoutKeyData(unsigned dataSize, const bool* objRefs) + : DataSize(dataSize) + , ObjRefs(objRefs) + { + } +}; + +struct ContinuationLayoutKey +{ + uintptr_t Data; + ContinuationLayoutKey() : Data(0) + { + } + ContinuationLayoutKey(MethodTable* pMT); + ContinuationLayoutKey(ContinuationLayoutKeyData* pData); +}; + +struct ContinuationLayoutKeyHashTableHelper +{ + static EEHashEntry_t * AllocateEntry(ContinuationLayoutKey key, BOOL bDeepCopy, AllocationHeap heap); + static void DeleteEntry(EEHashEntry_t *pEntry, AllocationHeap heap); + static BOOL CompareKeys(EEHashEntry_t *pEntry, ContinuationLayoutKey key); + static DWORD Hash(ContinuationLayoutKey key); + static void ReplaceKey(EEHashEntry_t *pEntry, ContinuationLayoutKey newKey); +}; + +typedef EEHashTable ContinuationLayoutHashTable; + +class AsyncContinuationsManager +{ + LoaderAllocator* m_allocator; + CrstExplicitInit m_layoutsLock; + ContinuationLayoutHashTable m_layouts; + + MethodTable* CreateNewContinuationMethodTable(unsigned dataSize, const bool* objRefs, MethodDesc* asyncMethod, AllocMemTracker* pamTracker); + + static MethodTable* CreateNewContinuationMethodTable(unsigned dataSize, const bool* objRefs, EEClass* eeClass, LoaderAllocator* allocator, Module* loaderModule, AllocMemTracker* pamTracker); + static PTR_EEClass GetOrCreateSingletonSubContinuationEEClass(); + static PTR_EEClass CreateSingletonSubContinuationEEClass(); +public: + AsyncContinuationsManager(LoaderAllocator* allocator); + MethodTable* LookupOrCreateContinuationMethodTable(unsigned dataSize, const bool* objRefs, MethodDesc* asyncMethod); + void NotifyUnloadingClasses(); + + template + static void PrintContinuationName(MethodTable* pMT, AppendString append, AppendNum appendNum); +}; + +typedef DPTR(AsyncContinuationsManager) PTR_AsyncContinuationsManager; + +template +void AsyncContinuationsManager::PrintContinuationName(MethodTable* pMT, AppendString append, AppendNum appendNum) +{ + append("Continuation_", W("Continuation_")); + appendNum(pMT->GetBaseSize() - (OBJHEADER_SIZE + OFFSETOF__CORINFO_Continuation__data)); + CGCDesc* desc = CGCDesc::GetCGCDescFromMT(pMT); + CGCDescSeries* lowestSeries = desc->GetLowestSeries(); + for (CGCDescSeries* curSeries = desc->GetHighestSeries(); curSeries >= lowestSeries; curSeries--) + { + if (curSeries->GetSeriesOffset() < OFFSETOF__CORINFO_Continuation__data) + { + continue; + } + + append("_", W("_")); + appendNum((unsigned)(curSeries->GetSeriesOffset() - OFFSETOF__CORINFO_Continuation__data)); + append("_", W("_")); + appendNum((unsigned)((curSeries->GetSeriesSize() + pMT->GetBaseSize()) / TARGET_POINTER_SIZE)); + } +} + +#endif diff --git a/src/coreclr/vm/asyncthunks.cpp b/src/coreclr/vm/asyncthunks.cpp index cf4e290bc6e7c4..fcb0c06d059936 100644 --- a/src/coreclr/vm/asyncthunks.cpp +++ b/src/coreclr/vm/asyncthunks.cpp @@ -69,16 +69,14 @@ void MethodDesc::EmitTaskReturningThunk(MethodDesc* pAsyncOtherVariant, MetaSig& // store.Push(); // try // { - // Continuation cont; // try // { // T result = Inner(args); // // call an intrisic to see if the call above produced a continuation - // cont = StubHelpers.AsyncCallContinuation(); - // if (cont == null) + // if (StubHelpers.AsyncCallContinuation() == null) // return Task.FromResult(result); // - // return FinalizeTaskReturningThunk(cont); + // return FinalizeTaskReturningThunk(); // } // catch (Exception ex) // { @@ -92,8 +90,6 @@ void MethodDesc::EmitTaskReturningThunk(MethodDesc* pAsyncOtherVariant, MetaSig& ILCodeStream* pCode = pSL->NewCodeStream(ILStubLinker::kDispatch); - unsigned continuationLocal = pCode->NewLocal(LocalDesc(CoreLibBinder::GetClass(CLASS__CONTINUATION))); - TypeHandle thTaskRet = thunkMsig.GetRetTypeHandleThrowing(); bool isValueTask = thTaskRet.GetMethodTable()->IsValueType(); @@ -195,8 +191,6 @@ void MethodDesc::EmitTaskReturningThunk(MethodDesc* pAsyncOtherVariant, MetaSig& if (logicalResultLocal != UINT_MAX) pCode->EmitSTLOC(logicalResultLocal); pCode->EmitCALL(METHOD__STUBHELPERS__ASYNC_CALL_CONTINUATION, 0, 1); - pCode->EmitSTLOC(continuationLocal); - pCode->EmitLDLOC(continuationLocal); pCode->EmitBRFALSE(finishedLabel); pCode->EmitLEAVE(suspendedLabel); @@ -283,8 +277,7 @@ void MethodDesc::EmitTaskReturningThunk(MethodDesc* pAsyncOtherVariant, MetaSig& md = CoreLibBinder::GetMethod(METHOD__ASYNC_HELPERS__FINALIZE_TASK_RETURNING_THUNK); finalizeTaskReturningThunkToken = pCode->GetToken(md); } - pCode->EmitLDLOC(continuationLocal); - pCode->EmitCALL(finalizeTaskReturningThunkToken, 1, 1); + pCode->EmitCALL(finalizeTaskReturningThunkToken, 0, 1); pCode->EmitSTLOC(returnTaskLocal); pCode->EmitLEAVE(returnTaskLabel); diff --git a/src/coreclr/vm/clsload.cpp b/src/coreclr/vm/clsload.cpp index 72ca527baa01c4..eedfec3d11848e 100644 --- a/src/coreclr/vm/clsload.cpp +++ b/src/coreclr/vm/clsload.cpp @@ -2879,7 +2879,7 @@ void ClassLoader::NotifyLoad(TypeHandle typeHnd) } #endif //PROFILING_SUPPORTED - if (pMT->IsTypicalTypeDefinition()) + if (pMT->IsTypicalTypeDefinition() && !IsNilToken(pMT->GetCl())) { LOG((LF_CLASSLOADER, LL_INFO100, "Successfully loaded class %s\n", pMT->GetDebugClassName())); diff --git a/src/coreclr/vm/clsload.hpp b/src/coreclr/vm/clsload.hpp index c50dd4a5fc7987..9d98e76c46200b 100644 --- a/src/coreclr/vm/clsload.hpp +++ b/src/coreclr/vm/clsload.hpp @@ -915,12 +915,14 @@ class ClassLoader // Publish the type in the loader's tables static TypeHandle PublishType(const TypeKey *pTypeKey, TypeHandle typeHnd); +public: // Notify profiler and debugger that a type load has completed // Also update perf counters static void NotifyLoad(TypeHandle typeHnd); // Notify profiler that a MethodTable is being unloaded static void NotifyUnload(MethodTable* pMT, bool unloadStarted); +private: // Phase CLASS_LOAD_EXACTPARENTS of class loading // Load exact parents and interfaces and dependent structures (generics dictionary, vtable fixes) static void LoadExactParents(MethodTable* pMT); diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index bfc6cc586ca38c..7227608a0dba17 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -718,12 +718,11 @@ DEFINE_CLASS(ASYNC_HELPERS, CompilerServices, AsyncHelpers) DEFINE_METHOD(ASYNC_HELPERS, ALLOC_CONTINUATION, AllocContinuation, NoSig) DEFINE_METHOD(ASYNC_HELPERS, ALLOC_CONTINUATION_METHOD, AllocContinuationMethod, NoSig) DEFINE_METHOD(ASYNC_HELPERS, ALLOC_CONTINUATION_CLASS, AllocContinuationClass, NoSig) -DEFINE_METHOD(ASYNC_HELPERS, ALLOC_CONTINUATION_RESULT_BOX, AllocContinuationResultBox, SM_VoidPtr_RetObj) -DEFINE_METHOD(ASYNC_HELPERS, FINALIZE_TASK_RETURNING_THUNK, FinalizeTaskReturningThunk, SM_Continuation_RetTask) -DEFINE_METHOD(ASYNC_HELPERS, FINALIZE_TASK_RETURNING_THUNK_1, FinalizeTaskReturningThunk, GM_Continuation_RetTaskOfT) -DEFINE_METHOD(ASYNC_HELPERS, FINALIZE_VALUETASK_RETURNING_THUNK, FinalizeValueTaskReturningThunk, SM_Continuation_RetValueTask) -DEFINE_METHOD(ASYNC_HELPERS, FINALIZE_VALUETASK_RETURNING_THUNK_1, FinalizeValueTaskReturningThunk, GM_Continuation_RetValueTaskOfT) +DEFINE_METHOD(ASYNC_HELPERS, FINALIZE_TASK_RETURNING_THUNK, FinalizeTaskReturningThunk, SM_RetTask) +DEFINE_METHOD(ASYNC_HELPERS, FINALIZE_TASK_RETURNING_THUNK_1, FinalizeTaskReturningThunk, GM_RetTaskOfT) +DEFINE_METHOD(ASYNC_HELPERS, FINALIZE_VALUETASK_RETURNING_THUNK, FinalizeValueTaskReturningThunk, SM_RetValueTask) +DEFINE_METHOD(ASYNC_HELPERS, FINALIZE_VALUETASK_RETURNING_THUNK_1, FinalizeValueTaskReturningThunk, GM_RetValueTaskOfT) DEFINE_METHOD(ASYNC_HELPERS, TASK_FROM_EXCEPTION, TaskFromException, SM_Exception_RetTask) DEFINE_METHOD(ASYNC_HELPERS, TASK_FROM_EXCEPTION_1, TaskFromException, GM_Exception_RetTaskOfT) @@ -860,8 +859,6 @@ DEFINE_FIELD(CONTINUATION, NEXT, Next) DEFINE_FIELD(CONTINUATION, RESUME, Resume) DEFINE_FIELD(CONTINUATION, STATE, State) DEFINE_FIELD(CONTINUATION, FLAGS, Flags) -DEFINE_FIELD(CONTINUATION, DATA, Data) -DEFINE_FIELD(CONTINUATION, GCDATA, GCData) DEFINE_CLASS(RUNTIME_WRAPPED_EXCEPTION, CompilerServices, RuntimeWrappedException) DEFINE_METHOD(RUNTIME_WRAPPED_EXCEPTION, OBJ_CTOR, .ctor, IM_Obj_RetVoid) diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index f0e6abfd342a03..8bb8e1967ed874 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -3417,7 +3417,6 @@ size_t CEEInfo::printClassName(CORINFO_CLASS_HANDLE cls, char* buffer, size_t bu size_t requiredBufferSize = 0; TypeHandle th(cls); - IMDInternalImport* pImport = th.GetMethodTable()->GetMDImport(); auto append = [buffer, bufferSize, &bytesWritten, &requiredBufferSize](const char* str) { @@ -3440,6 +3439,13 @@ size_t CEEInfo::printClassName(CORINFO_CLASS_HANDLE cls, char* buffer, size_t bu requiredBufferSize += strLen; }; + auto appendNum = [&](unsigned num) + { + char str[16]; + sprintf_s(str, ARRAY_SIZE(str), "%u", num); + append(str); + }; + // Subset of TypeString that does just what we need while staying in UTF8 // and avoiding expensive copies. This function is called a lot in checked // builds. @@ -3451,10 +3457,22 @@ size_t CEEInfo::printClassName(CORINFO_CLASS_HANDLE cls, char* buffer, size_t bu mdTypeDef td = th.GetCl(); if (IsNilToken(td)) { - append("(dynamicClass)"); + if (th.IsContinuation()) + { + AsyncContinuationsManager::PrintContinuationName( + th.AsMethodTable(), + [&](LPCSTR str, LPCWSTR wstr) { append(str); }, + appendNum); + } + else + { + append("(dynamicClass)"); + } } else { + IMDInternalImport* pImport = th.GetMethodTable()->GetMDImport(); + DWORD attr; IfFailThrow(pImport->GetTypeDefProps(td, &attr, NULL)); @@ -10242,9 +10260,6 @@ void CEEInfo::getAsyncInfo(CORINFO_ASYNC_INFO* pAsyncInfoOut) pAsyncInfoOut->continuationResumeFldHnd = CORINFO_FIELD_HANDLE(CoreLibBinder::GetField(FIELD__CONTINUATION__RESUME)); pAsyncInfoOut->continuationStateFldHnd = CORINFO_FIELD_HANDLE(CoreLibBinder::GetField(FIELD__CONTINUATION__STATE)); pAsyncInfoOut->continuationFlagsFldHnd = CORINFO_FIELD_HANDLE(CoreLibBinder::GetField(FIELD__CONTINUATION__FLAGS)); - pAsyncInfoOut->continuationDataFldHnd = CORINFO_FIELD_HANDLE(CoreLibBinder::GetField(FIELD__CONTINUATION__DATA)); - pAsyncInfoOut->continuationGCDataFldHnd = CORINFO_FIELD_HANDLE(CoreLibBinder::GetField(FIELD__CONTINUATION__GCDATA)); - pAsyncInfoOut->continuationsNeedMethodHandle = m_pMethodBeingCompiled->GetLoaderAllocator()->CanUnload(); pAsyncInfoOut->captureExecutionContextMethHnd = CORINFO_METHOD_HANDLE(CoreLibBinder::GetMethod(METHOD__ASYNC_HELPERS__CAPTURE_EXECUTION_CONTEXT)); pAsyncInfoOut->restoreExecutionContextMethHnd = CORINFO_METHOD_HANDLE(CoreLibBinder::GetMethod(METHOD__ASYNC_HELPERS__RESTORE_EXECUTION_CONTEXT)); pAsyncInfoOut->captureContinuationContextMethHnd = CORINFO_METHOD_HANDLE(CoreLibBinder::GetMethod(METHOD__ASYNC_HELPERS__CAPTURE_CONTINUATION_CONTEXT)); @@ -10254,6 +10269,36 @@ void CEEInfo::getAsyncInfo(CORINFO_ASYNC_INFO* pAsyncInfoOut) EE_TO_JIT_TRANSITION(); } +CORINFO_CLASS_HANDLE CEEInfo::getContinuationType( + size_t dataSize, + bool* objRefs, + size_t objRefsSize) +{ + CONTRACTL { + THROWS; + GC_TRIGGERS; + MODE_PREEMPTIVE; + } CONTRACTL_END; + + CORINFO_CLASS_HANDLE result = NULL; + + JIT_TO_EE_TRANSITION(); + + _ASSERTE(objRefsSize == (dataSize + (TARGET_POINTER_SIZE - 1)) / TARGET_POINTER_SIZE); + LoaderAllocator* allocator = m_pMethodBeingCompiled->GetLoaderAllocator(); + AsyncContinuationsManager* asyncConts = allocator->GetAsyncContinuationsManager(); + result = (CORINFO_CLASS_HANDLE)asyncConts->LookupOrCreateContinuationMethodTable((unsigned)dataSize, objRefs, m_pMethodBeingCompiled); + +#ifdef DEBUG + CORINFO_CLASS_HANDLE result2 = (CORINFO_CLASS_HANDLE)asyncConts->LookupOrCreateContinuationMethodTable((unsigned)dataSize, objRefs, m_pMethodBeingCompiled); + _ASSERTE(result2 == result); +#endif + + EE_TO_JIT_TRANSITION(); + + return result; +} + // Return details about EE internal data structures uint32_t CEEInfo::getThreadTLSIndex(void **ppIndirection) { @@ -14495,9 +14540,11 @@ static Signature BuildResumptionStubSignature(LoaderAllocator* alloc, AllocMemTr { SigBuilder sigBuilder; sigBuilder.AppendByte(IMAGE_CEE_CS_CALLCONV_DEFAULT); - sigBuilder.AppendData(1); // 1 argument + sigBuilder.AppendData(2); // 2 arguments sigBuilder.AppendElementType(ELEMENT_TYPE_OBJECT); // return type sigBuilder.AppendElementType(ELEMENT_TYPE_OBJECT); // continuation + sigBuilder.AppendElementType(ELEMENT_TYPE_BYREF); // result location + sigBuilder.AppendElementType(ELEMENT_TYPE_U1); return AllocateSignature(alloc, sigBuilder, pamTracker); } @@ -14701,93 +14748,9 @@ CORINFO_METHOD_HANDLE CEEJitInfo::getAsyncResumptionStub() pCode->EmitLDLOC(newContinuationLoc); pCode->EmitBRTRUE(doneResult); - // Load 'next' of current continuation - pCode->EmitLDARG(0); - pCode->EmitLDFLD(FIELD__CONTINUATION__NEXT); - - // Result is placed in GCData[0] if it has GC references (potentially boxing it). - bool isOrContainsGCPointers = false; - if (CorTypeInfo::IsObjRef(resultTypeHnd.GetInternalCorElementType()) || (resultTypeHnd.IsValueType() && resultTypeHnd.AsMethodTable()->ContainsGCPointers())) - { - // Load 'gcdata' of next continuation - pCode->EmitLDFLD(FIELD__CONTINUATION__GCDATA); - - // Now we have the GC array. At the first index is the result. - pCode->EmitLDC(0); - - // NOTE: that we are not using regular boxing (in EmitBOX sense) and allocate our own box instances via a helper. - // There are two reasons: - // - resultTypeHnd may be a nullable type and have different layout in boxed/unboxed forms. - // We do not want to deal with that. - // - resultTypeHnd may contain __Canon fields. Regular boxing would not allow that, but this box is used for a very - // specific internal purpose where we only require that the GC layout of the box matches the data - // that we store in it, thus we want to allow __Canon. - if (resultTypeHnd.IsValueType()) - { - // make a box and dup the ref - MethodDesc* md = CoreLibBinder::GetMethod(METHOD__ASYNC_HELPERS__ALLOC_CONTINUATION_RESULT_BOX); - pCode->EmitLDC((DWORD_PTR)resultTypeHnd.AsMethodTable()); - pCode->EmitCALL(pCode->GetToken(md), 1, 1); - pCode->EmitDUP(); - // dst is the offset of the first field in the box - pCode->EmitLDFLDA(FIELD__RAW_DATA__DATA); - // load the result - pCode->EmitLDLOC(resultLoc); - // store into the box - pCode->EmitSTOBJ(pCode->GetToken(resultTypeHnd)); - } - else - { - // load the result - pCode->EmitLDLOC(resultLoc); - } - - // Store the result. - pCode->EmitSTELEM_REF(); - } - else - { - // Otherwise it goes into Data, either at offset 0 or 4 depending - // on CORINFO_CONTINUATION_OSR_IL_OFFSET_IN_DATA. - ILCodeLabel* hasOsrILOffset = pCode->NewCodeLabel(); - - unsigned nextContinuationLcl = pCode->NewLocal(LocalDesc(continuationTypeHnd)); - pCode->EmitSTLOC(nextContinuationLcl); - - // Load 'flags' of next continuation - pCode->EmitLDLOC(nextContinuationLcl); - pCode->EmitLDFLD(FIELD__CONTINUATION__FLAGS); - pCode->EmitLDC(CORINFO_CONTINUATION_OSR_IL_OFFSET_IN_DATA); - pCode->EmitAND(); - pCode->EmitBRTRUE(hasOsrILOffset); - - // Load 'data' of next continuation - pCode->EmitLDLOC(nextContinuationLcl); - pCode->EmitLDFLD(FIELD__CONTINUATION__DATA); - // Load address of array at index 0 - pCode->EmitLDC(0); - pCode->EmitLDELEMA(pCode->GetToken(CoreLibBinder::GetClass(CLASS__BYTE))); - - // Store at index 0. - pCode->EmitLDLOC(resultLoc); - pCode->EmitSTOBJ(pCode->GetToken(resultTypeHnd)); - - pCode->EmitBR(doneResult); - - pCode->EmitLabel(hasOsrILOffset); - - // Load 'data' of next continuation - pCode->EmitLDLOC(nextContinuationLcl); - pCode->EmitLDFLD(FIELD__CONTINUATION__DATA); - // Load address of array at index 4 - pCode->EmitLDC(4); - pCode->EmitLDELEMA(pCode->GetToken(CoreLibBinder::GetClass(CLASS__BYTE))); - - // Store at index 4. - pCode->EmitLDLOC(resultLoc); - pCode->EmitUNALIGNED(1); - pCode->EmitSTOBJ(pCode->GetToken(resultTypeHnd)); - } + pCode->EmitLDARG(1); // resultLoc + pCode->EmitLDLOC(resultLoc); + pCode->EmitSTOBJ(pCode->GetToken(resultTypeHnd)); pCode->EmitLabel(doneResult); } diff --git a/src/coreclr/vm/loaderallocator.cpp b/src/coreclr/vm/loaderallocator.cpp index f33bd9de06e62e..e8aceec349e07b 100644 --- a/src/coreclr/vm/loaderallocator.cpp +++ b/src/coreclr/vm/loaderallocator.cpp @@ -63,6 +63,8 @@ LoaderAllocator::LoaderAllocator(bool collectible) : m_onStackReplacementManager = NULL; #endif + m_asyncContinuationsManager = NULL; + m_fGCPressure = false; m_fTerminated = false; m_fUnloaded = false; @@ -1410,6 +1412,14 @@ void LoaderAllocator::Terminate() } #endif + if (m_asyncContinuationsManager != NULL) + { + m_asyncContinuationsManager->NotifyUnloadingClasses(); + + delete m_asyncContinuationsManager; + m_asyncContinuationsManager = NULL; + } + // In collectible types we merge the low frequency and high frequency heaps // So don't destroy them twice. if ((m_pLowFrequencyHeap != NULL) && (m_pLowFrequencyHeap != m_pHighFrequencyHeap)) @@ -2294,6 +2304,31 @@ PTR_OnStackReplacementManager LoaderAllocator::GetOnStackReplacementManager() #endif // FEATURE_ON_STACK_REPLACEMENT #ifndef DACCESS_COMPILE +PTR_AsyncContinuationsManager LoaderAllocator::GetAsyncContinuationsManager() +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + INJECT_FAULT(COMPlusThrowOM();); + } + CONTRACTL_END; + + if (m_asyncContinuationsManager == NULL) + { + AsyncContinuationsManager* newManager = new AsyncContinuationsManager(this); + + if (InterlockedCompareExchangeT(&m_asyncContinuationsManager, newManager, NULL) != NULL) + { + // some thread swooped in and set the field + delete newManager; + } + } + _ASSERTE(m_asyncContinuationsManager != NULL); + return m_asyncContinuationsManager; +} + void LoaderAllocator::AllocateBytesForStaticVariables(DynamicStaticsInfo* pStaticsInfo, uint32_t cbMem, bool isClassInitedByUpdatingStaticPointer) { CONTRACTL diff --git a/src/coreclr/vm/loaderallocator.hpp b/src/coreclr/vm/loaderallocator.hpp index eaf327588f9a99..52e54d98d42a88 100644 --- a/src/coreclr/vm/loaderallocator.hpp +++ b/src/coreclr/vm/loaderallocator.hpp @@ -19,6 +19,7 @@ class FuncPtrStubs; #include "qcall.h" #include "ilstubcache.h" +#include "asynccontinuations.h" #include "callcounting.h" #include "methoddescbackpatchinfo.h" #include "crossloaderallocatorhash.h" @@ -484,6 +485,8 @@ class LoaderAllocator PTR_OnStackReplacementManager m_onStackReplacementManager; #endif + PTR_AsyncContinuationsManager m_asyncContinuationsManager; + #ifndef DACCESS_COMPILE public: @@ -894,6 +897,8 @@ class LoaderAllocator PTR_OnStackReplacementManager GetOnStackReplacementManager(); #endif // FEATURE_ON_STACK_REPLACEMENT + PTR_AsyncContinuationsManager GetAsyncContinuationsManager(); + #ifndef DACCESS_COMPILE public: virtual void RegisterDependentHandleToNativeObjectForCleanup(LADependentHandleToNativeObject *dependentHandle) {}; diff --git a/src/coreclr/vm/metasig.h b/src/coreclr/vm/metasig.h index 94157031ee6489..44e9be70a90a17 100644 --- a/src/coreclr/vm/metasig.h +++ b/src/coreclr/vm/metasig.h @@ -619,10 +619,8 @@ DEFINE_METASIG(SM(PtrByte_RetVoid, P(b), v)) DEFINE_METASIG_T(SM(RetContinuation, , C(CONTINUATION))) DEFINE_METASIG(GM(T_RetVoid, IMAGE_CEE_CS_CALLCONV_DEFAULT, 1, M(0), v)) -DEFINE_METASIG_T(SM(Continuation_RetTask, C(CONTINUATION), C(TASK))) -DEFINE_METASIG_T(GM(Continuation_RetTaskOfT, IMAGE_CEE_CS_CALLCONV_DEFAULT, 1, C(CONTINUATION), GI(C(TASK_1), 1, M(0)))) -DEFINE_METASIG_T(SM(Continuation_RetValueTask, C(CONTINUATION), g(VALUETASK))) -DEFINE_METASIG_T(GM(Continuation_RetValueTaskOfT, IMAGE_CEE_CS_CALLCONV_DEFAULT, 1, C(CONTINUATION), GI(g(VALUETASK_1), 1, M(0)))) +DEFINE_METASIG_T(GM(RetTaskOfT, IMAGE_CEE_CS_CALLCONV_DEFAULT, 1, , GI(C(TASK_1), 1, M(0)))) +DEFINE_METASIG_T(GM(RetValueTaskOfT, IMAGE_CEE_CS_CALLCONV_DEFAULT, 1, , GI(g(VALUETASK_1), 1, M(0)))) // Undefine macros in case we include the file again in the compilation unit diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index 56f704997fa332..fa8114b1bf7e32 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -375,7 +375,7 @@ BOOL MethodTable::ValidateWithPossibleAV() } // generic instantiation check - return (HasInstantiation() || IsArray()) && (pEEClassFromMethodTable->GetClassWithPossibleAV() == pEEClass); + return (HasInstantiation() || IsArray() || IsContinuation()) && (pEEClassFromMethodTable->GetClassWithPossibleAV() == pEEClass); } @@ -421,7 +421,7 @@ WORD MethodTable::GetNumMethods() PTR_MethodTable MethodTable::GetTypicalMethodTable() { LIMITED_METHOD_DAC_CONTRACT; - if (IsArray()) + if (IsArray() || IsContinuation()) return (PTR_MethodTable)this; PTR_MethodTable methodTableMaybe = GetModule()->LookupTypeDef(GetCl()).AsMethodTable(); @@ -1341,6 +1341,7 @@ BOOL MethodTable::CanCastToClass(MethodTable *pTargetMT, TypeHandlePairList *pVi PRECONDITION(CheckPointer(pTargetMT)); PRECONDITION(!pTargetMT->IsArray()); PRECONDITION(!pTargetMT->IsInterface()); + PRECONDITION(!pTargetMT->IsContinuation()); } CONTRACTL_END @@ -6245,7 +6246,7 @@ BOOL MethodTable::SanityCheck() if (GetNumGenericArgs() != 0) return (pCanonMT->GetClass() == pClass); else - return (pCanonMT == this) || IsArray(); + return (pCanonMT == this) || IsArray() || IsContinuation(); } @@ -7491,7 +7492,7 @@ CHECK MethodTable::CheckInstanceActivated() { WRAPPER_NO_CONTRACT; - if (IsArray()) + if (IsArray() || IsContinuation()) CHECK_OK; diff --git a/src/coreclr/vm/methodtable.h b/src/coreclr/vm/methodtable.h index e161f01e0855a2..4a77cb157dae66 100644 --- a/src/coreclr/vm/methodtable.h +++ b/src/coreclr/vm/methodtable.h @@ -939,6 +939,7 @@ class MethodTable PTR_Module GetModule() { LIMITED_METHOD_CONTRACT; + _ASSERTE(!IsContinuation()); return m_pModule; } @@ -2788,6 +2789,8 @@ class MethodTable SetFlag(enum_flag_Category_Nullable); } + inline BOOL IsContinuation(); + // The following methods are only valid for the method tables for array types. CorElementType GetArrayElementType() { diff --git a/src/coreclr/vm/methodtable.inl b/src/coreclr/vm/methodtable.inl index 6bdf3aa40a0bc2..d24f788bbad5bc 100644 --- a/src/coreclr/vm/methodtable.inl +++ b/src/coreclr/vm/methodtable.inl @@ -299,6 +299,14 @@ inline BOOL MethodTable::IsValueType() return GetFlag(enum_flag_Category_ValueType_Mask) == enum_flag_Category_ValueType; } +inline BOOL MethodTable::IsContinuation() +{ + LIMITED_METHOD_DAC_CONTRACT; + + PTR_MethodTable contClass = g_pContinuationClassIfSubTypeCreated; + return contClass != NULL && m_pParentMethodTable == contClass; +} + //========================================================================================== inline DWORD MethodTable::GetRank() { diff --git a/src/coreclr/vm/typehandle.cpp b/src/coreclr/vm/typehandle.cpp index 05b80e1fd662fa..3726307d1d9d45 100644 --- a/src/coreclr/vm/typehandle.cpp +++ b/src/coreclr/vm/typehandle.cpp @@ -91,6 +91,13 @@ BOOL TypeHandle::IsString() const return !IsTypeDesc() && AsMethodTable()->IsString(); } +BOOL TypeHandle::IsContinuation() const +{ + LIMITED_METHOD_CONTRACT; + + return !IsTypeDesc() && AsMethodTable()->IsContinuation(); +} + BOOL TypeHandle::IsGenericVariable() const { LIMITED_METHOD_DAC_CONTRACT; @@ -318,6 +325,20 @@ bool TypeHandle::IsManagedClassObjectPinned() const void TypeHandle::AllocateManagedClassObject(RUNTIMETYPEHANDLE* pDest) { + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + } + CONTRACTL_END + + if (IsContinuation()) + { + COMPlusThrow(kNotSupportedException, W("NotSupported_Continuation")); + return; + } + REFLECTCLASSBASEREF refClass = NULL; PTR_LoaderAllocator allocator = GetLoaderAllocator(); diff --git a/src/coreclr/vm/typehandle.h b/src/coreclr/vm/typehandle.h index c4fb31c7324be4..92af9c513a7641 100644 --- a/src/coreclr/vm/typehandle.h +++ b/src/coreclr/vm/typehandle.h @@ -440,6 +440,9 @@ class TypeHandle // String BOOL IsString() const; + // Continuation sub types + BOOL IsContinuation() const; + // True if this type *is* a formal generic type parameter or any component of it is a formal generic type parameter BOOL ContainsGenericVariables(BOOL methodOnly=FALSE) const; diff --git a/src/coreclr/vm/typestring.cpp b/src/coreclr/vm/typestring.cpp index 4321492601e3f7..2c22666561a7ea 100644 --- a/src/coreclr/vm/typestring.cpp +++ b/src/coreclr/vm/typestring.cpp @@ -834,14 +834,25 @@ void TypeString::AppendType(TypeNameBuilder& tnb, TypeHandle ty, Instantiation t else { // Get the TypeDef token and attributes - IMDInternalImport *pImport = ty.GetMethodTable()->GetMDImport(); mdTypeDef td = ty.GetCl(); - if (IsNilToken(td)) { - // This type does not exist in metadata. Simply append "dynamicClass". - tnb.AddName(W("(dynamicClass)")); + if (IsNilToken(td)) + { + if (ty.IsContinuation()) + { + AsyncContinuationsManager::PrintContinuationName( + ty.AsMethodTable(), + [&](LPCSTR str, LPCWSTR wstr) { tnb.Append(wstr); }, + [&](unsigned num) { tnb.AppendNum(num); }); + } + else + { + // This type does not exist in metadata. Simply append "dynamicClass". + tnb.AddName(W("(dynamicClass)")); + } } else { + IMDInternalImport *pImport = ty.GetMethodTable()->GetMDImport(); #ifdef _DEBUG if (format & FormatDebug) { diff --git a/src/coreclr/vm/typestring.h b/src/coreclr/vm/typestring.h index c8706144c6ebe0..4bfe0b9692483e 100644 --- a/src/coreclr/vm/typestring.h +++ b/src/coreclr/vm/typestring.h @@ -102,6 +102,7 @@ class TypeNameBuilder void Append(WCHAR c) { WRAPPER_NO_CONTRACT; m_pStr->Append(c); } void Append(LPCUTF8 pStr) { WRAPPER_NO_CONTRACT; m_pStr->AppendUTF8(pStr); } void Append(UTF8 c) { WRAPPER_NO_CONTRACT; m_pStr->AppendUTF8(c); } + void AppendNum(unsigned num) { WRAPPER_NO_CONTRACT; m_pStr->AppendPrintf("%u", num); } SString* GetString() { WRAPPER_NO_CONTRACT; return m_pStr; } private: diff --git a/src/coreclr/vm/vars.cpp b/src/coreclr/vm/vars.cpp index e5d1004ad97997..c5546edcb2c487 100644 --- a/src/coreclr/vm/vars.cpp +++ b/src/coreclr/vm/vars.cpp @@ -67,6 +67,12 @@ GPTR_IMPL(MethodTable, g_TypedReferenceMT); GPTR_IMPL(MethodTable, g_pWeakReferenceClass); GPTR_IMPL(MethodTable, g_pWeakReferenceOfTClass); +#ifdef DACCESS_COMPILE +GPTR_IMPL(MethodTable, g_pContinuationClassIfSubTypeCreated); +#else +GVAL_IMPL(Volatile, g_pContinuationClassIfSubTypeCreated); +#endif + #ifdef FEATURE_COMINTEROP GPTR_IMPL(MethodTable, g_pBaseCOMObject); #endif diff --git a/src/coreclr/vm/vars.hpp b/src/coreclr/vm/vars.hpp index 4ef7f2fbfcd0a6..08988b342e784b 100644 --- a/src/coreclr/vm/vars.hpp +++ b/src/coreclr/vm/vars.hpp @@ -345,6 +345,12 @@ GPTR_DECL(MethodTable, g_TypedReferenceMT); GPTR_DECL(MethodTable, g_pWeakReferenceClass); GPTR_DECL(MethodTable, g_pWeakReferenceOfTClass); +#ifdef DACCESS_COMPILE +GPTR_DECL(MethodTable, g_pContinuationClassIfSubTypeCreated); +#else +GVAL_DECL(Volatile, g_pContinuationClassIfSubTypeCreated); +#endif + #ifdef FEATURE_COMINTEROP GPTR_DECL(MethodTable, g_pBaseCOMObject); #endif diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index 34977b2e22b910..7404f6124e5b16 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -4058,6 +4058,9 @@ C++/CLI activation has been disabled via a feature switch. See https://aka.ms/dotnet-illink/nativehost for more information. + + Object.GetType() is not supported for continuations + Queue empty. diff --git a/src/tests/async/Directory.Build.props b/src/tests/async/Directory.Build.props index a1f7d48cf60a7d..f505092384b1b1 100644 --- a/src/tests/async/Directory.Build.props +++ b/src/tests/async/Directory.Build.props @@ -5,7 +5,7 @@ true - $(NoWarn);xUnit1013;CS1998 + $(NoWarn);xUnit1013;CS1998;SYSLIB5007 false $(Features);runtime-async=on