Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
240 changes: 230 additions & 10 deletions src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs
Original file line number Diff line number Diff line change
@@ -1,37 +1,257 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using Internal.TypeSystem;

namespace Internal.IL.Stubs
{
public static class AsyncThunkILEmitter
{
// The emitted code matches method EmitTaskReturningThunk in CoreCLR VM.
public static MethodIL EmitTaskReturningThunk(MethodDesc taskReturningMethod, MethodDesc asyncMethod)
{
TypeSystemContext context = taskReturningMethod.Context;

var emitter = new ILEmitter();
var codestream = emitter.NewCodeStream();

// TODO: match EmitTaskReturningThunk in CoreCLR VM

MethodSignature sig = asyncMethod.Signature;
Comment thread
eduardo-vp marked this conversation as resolved.
Outdated
int numParams = (sig.IsStatic || sig.IsExplicitThis) ? sig.Length : sig.Length + 1;
for (int i = 0; i < numParams; i++)
codestream.EmitLdArg(i);
TypeDesc returnType = sig.ReturnType;

MetadataType md = taskReturningMethod.Signature.ReturnType as MetadataType;
ReadOnlySpan<byte> name = md.Name;
bool isValueTask = name.SequenceEqual("ValueTask"u8) || name.SequenceEqual("ValueTask`1"u8);
Comment thread
eduardo-vp marked this conversation as resolved.
Outdated

TypeDesc logicalReturnType = null;
ILLocalVariable logicalResultLocal = 0;
if (returnType.HasInstantiation)
{
// The return type is either Task<T> or ValueTask<T>, exactly one generic argument
logicalReturnType = returnType.Instantiation[0];
logicalResultLocal = emitter.NewLocal(logicalReturnType);
}

ILLocalVariable returnTaskLocal = emitter.NewLocal(returnType);

// TODO: Fix this (ExecutionAndSyncBlockStore is not available in Native AOT).

// TypeDesc executionAndSyncBlockStoreType = context.SystemModule.GetKnownType("System.Runtime.CompilerServices"u8, "ExecutionAndSyncBlockStore"u8);
// ILLocalVariable executionAndSyncBlockStoreLocal = emitter.NewLocal(executionAndSyncBlockStoreType);
Comment thread
eduardo-vp marked this conversation as resolved.
Outdated

ILCodeLabel returnTaskLabel = emitter.NewCodeLabel();
ILCodeLabel suspendedLabel = emitter.NewCodeLabel();
ILCodeLabel finishedLabel = emitter.NewCodeLabel();

codestream.Emit(ILOpcode.call, emitter.NewToken(asyncMethod));
// codestream.EmitLdLoca(executionAndSyncBlockStoreLocal);
// codestream.Emit(ILOpcode.call, emitter.NewToken(executionAndSyncBlockStoreType.GetKnownMethod("Push"u8, null)));

if (sig.ReturnType.IsVoid)
ILExceptionRegionBuilder tryFinallyRegion = emitter.NewFinallyRegion();
{
codestream.Emit(ILOpcode.call, emitter.NewToken(context.SystemModule.GetKnownType("System.Threading.Tasks"u8, "Task"u8).GetKnownMethod("get_CompletedTask"u8, null)));
codestream.BeginTry(tryFinallyRegion);
codestream.Emit(ILOpcode.nop);
ILExceptionRegionBuilder tryCatchRegion = emitter.NewCatchRegion();
{
codestream.BeginTry(tryCatchRegion);

int localArg = 0;
if (sig.IsExplicitThis)
Comment thread
eduardo-vp marked this conversation as resolved.
Outdated
{
codestream.EmitLdArg(localArg++);
}

for (int iArg = 0; iArg < sig.Length; iArg++)
{
codestream.EmitLdArg(localArg++);
}

if (asyncMethod.OwningType.HasInstantiation)
{
var inst = new TypeDesc[asyncMethod.OwningType.Instantiation.Length];
for (int i = 0; i < inst.Length; i++)
{
inst[i] = context.GetSignatureVariable(i, false);
}

var instantiatedType = context.GetInstantiatedType((MetadataType)asyncMethod.OwningType, new Instantiation(inst));
Comment thread
eduardo-vp marked this conversation as resolved.
Outdated
asyncMethod = context.GetMethodForInstantiatedType(asyncMethod, instantiatedType);
}

if (asyncMethod.HasInstantiation)
{
var inst = new TypeDesc[asyncMethod.Instantiation.Length];
for (int i = 0; i < inst.Length; i++)
{
inst[i] = context.GetSignatureVariable(i, true);
}
asyncMethod = asyncMethod.MakeInstantiatedMethod(new Instantiation(inst));
}

codestream.Emit(ILOpcode.call, emitter.NewToken(asyncMethod));

if (logicalReturnType != null)
{
codestream.EmitStLoc(logicalResultLocal);
}

// TODO: Fix this (AsyncCallContinuation is not available in Native AOT).

//MethodDesc asyncCallContinuationMd = context.SystemModule
// .GetKnownType("System.StubHelpers"u8, "StubHelpers"u8)
Comment thread
eduardo-vp marked this conversation as resolved.
Outdated
// .GetKnownMethod("AsyncCallContinuation"u8, null);

//codestream.Emit(ILOpcode.call, emitter.NewToken(asyncCallContinuationMd));

codestream.Emit(ILOpcode.brfalse, finishedLabel);
codestream.Emit(ILOpcode.leave, suspendedLabel);
codestream.EmitLabel(finishedLabel);

if (logicalReturnType != null)
{
codestream.EmitLdLoc(logicalResultLocal);

MethodDesc fromResultMethod;
if (isValueTask)
{
fromResultMethod = context.SystemModule
.GetKnownType("System.Threading.Tasks"u8, "ValueTask`1"u8)
Comment thread
eduardo-vp marked this conversation as resolved.
Outdated
.GetKnownMethod("FromResult"u8, null)
.MakeInstantiatedMethod(new Instantiation(logicalReturnType));
}
else
{
fromResultMethod = context.SystemModule
.GetKnownType("System.Threading.Tasks"u8, "Task"u8)
.GetKnownMethod("FromResult"u8, null)
.MakeInstantiatedMethod(new Instantiation(logicalReturnType));
}

codestream.Emit(ILOpcode.call, emitter.NewToken(fromResultMethod));
}
else
{
MethodDesc getCompletedTaskMethod;
if (isValueTask)
{
getCompletedTaskMethod = context.SystemModule
.GetKnownType("System.Threading.Tasks"u8, "ValueTask"u8)
.GetKnownMethod("get_CompletedTask"u8, null);
}
else
{
getCompletedTaskMethod = context.SystemModule
.GetKnownType("System.Threading.Tasks"u8, "Task"u8)
.GetKnownMethod("get_CompletedTask"u8, null);
}
codestream.Emit(ILOpcode.call, emitter.NewToken(getCompletedTaskMethod));
}

codestream.EmitLdLoc(returnTaskLocal);
Comment thread
eduardo-vp marked this conversation as resolved.
Outdated
codestream.Emit(ILOpcode.leave, returnTaskLabel);

codestream.EndTry(tryCatchRegion);
}
// Catch
{
codestream.BeginHandler(tryCatchRegion);

MethodDesc fromExceptionMd;
if (logicalReturnType != null)
{
// Generate: returnType.FromException<T>(Exception)
if (isValueTask)
{
fromExceptionMd = context.SystemModule
.GetKnownType("System.Threading.Tasks"u8, "ValueTask`1"u8)
Comment thread
eduardo-vp marked this conversation as resolved.
Outdated
.GetKnownMethod("FromException"u8, null);
}
else
{
fromExceptionMd = context.SystemModule
.GetKnownType("System.Threading.Tasks"u8, "Task`1"u8)
Comment thread
eduardo-vp marked this conversation as resolved.
Outdated
.GetKnownMethod("FromException"u8, null);
}
fromExceptionMd = fromExceptionMd.MakeInstantiatedMethod(new Instantiation(logicalReturnType));
}
else
{
// Generate: returnType.FromException(Exception)
if (isValueTask)
{
fromExceptionMd = context.SystemModule
.GetKnownType("System.Threading.Tasks"u8, "ValueTask"u8)
.GetKnownMethod("FromException"u8, null);
}
else
{
fromExceptionMd = context.SystemModule
.GetKnownType("System.Threading.Tasks"u8, "Task"u8)
.GetKnownMethod("FromException"u8, null);
}
}

codestream.Emit(ILOpcode.call, emitter.NewToken(fromExceptionMd));
codestream.EmitStLoc(returnTaskLocal);
codestream.Emit(ILOpcode.leave, returnTaskLabel);
codestream.EndHandler(tryCatchRegion);
}

codestream.EmitLabel(suspendedLabel);

// TODO: Fix this (Finalize returning thunks are not available in Native AOT).

//MethodDesc finalizeTaskReturningThunkMd;

//if (logicalReturnType != null)
//{
// if (isValueTask)
// {
// finalizeTaskReturningThunkMd = context.SystemModule
// .GetKnownType("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8)
// .GetKnownMethod("FinalizeValueTaskReturningThunk`1"u8, null);
// }
// else
// {
// finalizeTaskReturningThunkMd = context.SystemModule
// .GetKnownType("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8)
// .GetKnownMethod("FinalizeTaskReturningThunk`1"u8, null);
// }
// finalizeTaskReturningThunkMd = finalizeTaskReturningThunkMd.MakeInstantiatedMethod(new Instantiation(logicalReturnType));
//}
//else
//{
// if (isValueTask)
// {
// finalizeTaskReturningThunkMd = context.SystemModule
// .GetKnownType("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8)
// .GetKnownMethod("FinalizeValueTaskReturningThunk"u8, null);
// }
// else
// {
// finalizeTaskReturningThunkMd = context.SystemModule
// .GetKnownType("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8)
// .GetKnownMethod("FinalizeTaskReturningThunk"u8, null);
// }
//}
//codestream.Emit(ILOpcode.call, emitter.NewToken(finalizeTaskReturningThunkMd));
codestream.EmitStLoc(returnTaskLocal);
codestream.Emit(ILOpcode.leave, returnTaskLabel);

codestream.EndTry(tryFinallyRegion);
}
else
//
{
codestream.Emit(ILOpcode.call, emitter.NewToken(context.SystemModule.GetKnownType("System.Threading.Tasks"u8, "Task"u8).GetKnownMethod("FromResult"u8, null).MakeInstantiatedMethod(sig.ReturnType)));
codestream.BeginHandler(tryFinallyRegion);

// TODO: Fix this (ExecutionAndSyncBlockStore is not available in Native AOT).

// codestream.EmitLdLoca(executionAndSyncBlockStoreLocal);
// codestream.Emit(ILOpcode.call, emitter.NewToken(executionAndSyncBlockStoreType.GetKnownMethod("Pop"u8, null)));
Comment thread
eduardo-vp marked this conversation as resolved.
Outdated
codestream.EndHandler(tryFinallyRegion);
}

codestream.EmitLabel(returnTaskLabel);
codestream.EmitLdLoc(returnTaskLocal);
codestream.Emit(ILOpcode.ret);

return emitter.Link(taskReturningMethod);
Expand Down
28 changes: 26 additions & 2 deletions src/coreclr/tools/Common/TypeSystem/IL/Stubs/ILEmitter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -669,6 +669,7 @@ public class ILEmitter
private ArrayBuilder<ILCodeStream> _codeStreams;
private ArrayBuilder<LocalVariableDefinition> _locals;
private ArrayBuilder<object> _tokens;
private ArrayBuilder<ILExceptionRegionBuilder> _catchRegions;
private ArrayBuilder<ILExceptionRegionBuilder> _finallyRegions;
Comment thread
eduardo-vp marked this conversation as resolved.
Outdated

public ILEmitter()
Expand Down Expand Up @@ -727,6 +728,14 @@ public ILCodeLabel NewCodeLabel()
return newLabel;
}

// For now, only catches exceptions of type Exception.
Comment thread
eduardo-vp marked this conversation as resolved.
Outdated
public ILExceptionRegionBuilder NewCatchRegion()
{
var region = new ILExceptionRegionBuilder();
_catchRegions.Add(region);
return region;
}

public ILExceptionRegionBuilder NewFinallyRegion()
{
var region = new ILExceptionRegionBuilder();
Expand Down Expand Up @@ -782,18 +791,33 @@ public MethodIL Link(MethodDesc owningMethod)

ILExceptionRegion[] exceptionRegions = null;

int numberOfExceptionRegions = _finallyRegions.Count;
int numberOfExceptionRegions = _catchRegions.Count + _finallyRegions.Count;
if (numberOfExceptionRegions > 0)
{
exceptionRegions = new ILExceptionRegion[numberOfExceptionRegions];

TypeDesc exceptionType = owningMethod.Context.SystemModule.GetKnownType("System"u8, "Exception"u8);
Comment thread
eduardo-vp marked this conversation as resolved.
Outdated

int exceptionTypeToken = (int)NewToken(exceptionType);

for (int i = 0; i < _catchRegions.Count; i++)
{
ILExceptionRegionBuilder region = _catchRegions[i];

Debug.Assert(region.IsDefined);

exceptionRegions[i] = new ILExceptionRegion(ILExceptionRegionKind.Catch,
region.TryOffset, region.TryLength, region.HandlerOffset, region.HandlerLength,
classToken: exceptionTypeToken, filterOffset: 0);
}

for (int i = 0; i < _finallyRegions.Count; i++)
{
ILExceptionRegionBuilder region = _finallyRegions[i];

Debug.Assert(region.IsDefined);

exceptionRegions[i] = new ILExceptionRegion(ILExceptionRegionKind.Finally,
exceptionRegions[_catchRegions.Count + i] = new ILExceptionRegion(ILExceptionRegionKind.Finally,
region.TryOffset, region.TryLength, region.HandlerOffset, region.HandlerLength,
classToken: 0, filterOffset: 0);
}
Expand Down
5 changes: 3 additions & 2 deletions src/coreclr/vm/asyncthunks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ bool MethodDesc::TryGenerateAsyncThunk(DynamicResolver** resolver, COR_ILMETHOD_
return true;
}

// provided an async method, emits a Task-returning wrapper.
// Provided an async method, emits a Task-returning wrapper.
// The emitted code matches method EmitTaskReturningThunk in the Managed Type System.
void MethodDesc::EmitTaskReturningThunk(MethodDesc* pAsyncOtherVariant, MetaSig& thunkMsig, ILStubLinker* pSL)
{
_ASSERTE(!pAsyncOtherVariant->IsAsyncThunkMethod());
Expand Down Expand Up @@ -132,7 +133,7 @@ void MethodDesc::EmitTaskReturningThunk(MethodDesc* pAsyncOtherVariant, MetaSig&
}

int token;
_ASSERTE(!pAsyncOtherVariant->IsWrapperStub());
_ASSERTE(!pAsyncOtherVariant->IsWrapperStub()); // Would it be better to move the assertion to the top of this method?
if (pAsyncOtherVariant->HasClassOrMethodInstantiation())
{
// For generic code emit generic signatures.
Expand Down
Loading