Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
239 changes: 229 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,256 @@
// 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 = taskReturningMethod.Signature;
TypeDesc returnType = sig.ReturnType;

MethodSignature sig = asyncMethod.Signature;
int numParams = (sig.IsStatic || sig.IsExplicitThis) ? sig.Length : sig.Length + 1;
for (int i = 0; i < numParams; i++)
codestream.EmitLdArg(i);
bool isValueTask = returnType.IsValueType;

codestream.Emit(ILOpcode.call, emitter.NewToken(asyncMethod));
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).

if (sig.ReturnType.IsVoid)
// TypeDesc executionAndSyncBlockStoreType = context.SystemModule.GetKnownType("System.Runtime.CompilerServices"u8, "ExecutionAndSyncBlockStore"u8);
// ILLocalVariable executionAndSyncBlockStoreLocal = emitter.NewLocal(executionAndSyncBlockStoreType);

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

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

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.IsStatic)
{
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));
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)
// .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)
.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.EmitStLoc(returnTaskLocal);
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"u8)
.GetKnownMethod("FromException"u8, null);
}
else
{
fromExceptionMd = context.SystemModule
.GetKnownType("System.Threading.Tasks"u8, "Task"u8)
.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)));
codestream.Emit(ILOpcode.endfinally);
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;

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

// For now, only catches exceptions of type Exception.
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);

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