Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
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
2 changes: 1 addition & 1 deletion src/coreclr/jit/async.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
// compiler - The compiler instance.
// handle - The method handle to look up the entrypoint for.
//
static void SetCallEntrypointForR2R(GenTreeCall* call, Compiler* compiler, CORINFO_METHOD_HANDLE handle)
void SetCallEntrypointForR2R(GenTreeCall* call, Compiler* compiler, CORINFO_METHOD_HANDLE handle)
{
#ifdef FEATURE_READYTORUN
if (!compiler->IsReadyToRun())
Expand Down
4 changes: 4 additions & 0 deletions src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -13649,6 +13649,10 @@ extern const BYTE genActualTypes[];
void dumpConvertedVarSet(Compiler* comp, VARSET_VALARG_TP vars);
#endif // DEBUG

// Defined in async.cpp. Sets the Ready-to-Run entrypoint on a JIT-synthesized async call so it is marked
// R2R-relative-indirect. A no-op when not compiling for Ready-to-Run.
void SetCallEntrypointForR2R(GenTreeCall* call, Compiler* compiler, CORINFO_METHOD_HANDLE handle);
Comment thread
jtschuster marked this conversation as resolved.

#include "compiler.hpp" // All the shared inline functions

/*****************************************************************************/
Expand Down
33 changes: 9 additions & 24 deletions src/coreclr/jit/importer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9121,28 +9121,6 @@ void Compiler::impImportBlockCode(BasicBlock* block)
(prefixFlags & PREFIX_CONSTRAINED) ? &constrainedResolvedToken : nullptr, flags,
&callInfo);

// TODO: crossgen2 cannot handle us removing this
if (isAwait && IsReadyToRun() && (callInfo.kind == CORINFO_CALL))
{
assert(callInfo.sig.isAsyncCall());
bool isSyncCallThunk;
info.compCompHnd->getAsyncOtherVariant(callInfo.hMethod, &isSyncCallThunk);
if (!isSyncCallThunk)
{
// The async variant that we got is a thunk. Switch
// back to the non-async task-returning call. There
// is no reason to go through the thunk.
_impResolveToken(CORINFO_TOKENKIND_Method);
prefixFlags &= ~(PREFIX_IS_TASK_AWAIT | PREFIX_TASK_AWAIT_CONTINUE_ON_CAPTURED_CONTEXT |
PREFIX_IS_ASYNC_VERSION_TAIL_AWAIT);
isAwait = false;

eeGetCallInfo(&resolvedToken,
(prefixFlags & PREFIX_CONSTRAINED) ? &constrainedResolvedToken : nullptr,
flags, &callInfo);
}
}

if (isAwait)
{
// If the synchronous call is a thunk then it means the async variant is not a thunk and we
Expand Down Expand Up @@ -11768,8 +11746,15 @@ bool Compiler::impWrapTopOfStackInAwait()

assert(awaitSig.isAsyncCall());

var_types callRetType = JITtype2varType(awaitSig.retType);
GenTreeCall* awaitCall = gtNewCallNode(CT_USER_FUNC, awaitMethod, callRetType);
var_types callRetType = JITtype2varType(awaitSig.retType);
GenTreeCall* awaitCall = gtNewCallNode(CT_USER_FUNC, awaitMethod, callRetType);

// The await-return call is synthesized here and never goes through impImportCall, so give it its
// Ready-to-Run entrypoint explicitly (as the other synthesized async calls do). Without this the call is
// not marked R2R-relative-indirect, so on arm64 fgMorphCall omits the indirection-cell (x11) argument the
// ReadyToRun DelayLoad helpers require, tripping a GetDataRva assert at runtime.
SetCallEntrypointForR2R(awaitCall, this, awaitMethod);

CORINFO_CLASS_HANDLE taskTypeHnd;
CorInfoType taskType = strip(info.compCompHnd->getArgType(&awaitSig, awaitSig.args, &taskTypeHnd));

Expand Down
13 changes: 12 additions & 1 deletion src/coreclr/tools/Common/Compiler/AsyncMethodVariant.cs
Original file line number Diff line number Diff line change
Expand Up @@ -150,14 +150,25 @@ public static bool IsReturnDroppingAsyncThunk(this MethodDesc method)
return method.GetTypicalMethodDefinition() is ReturnDroppingAsyncThunk;
}

/// <summary>
/// Returns true if the natural calling convention of the MethodDesc's definition requires adaptation to match
/// the calling convention of the MethodDesc itself. This is true when the MethodDesc's "async-ness" is
/// different than the "async-ness" defined in metadata (its definition's .IsAsync property), or for
/// return-dropping async thunks. This may necessitate a thunk to convert between the two calling conventions,
/// or a JIT transformation (see CORINFO_ASYNC_VERSION).
/// </summary>
public static bool IsAsyncThunk(this MethodDesc method)
{
return (method.IsAsyncVariant() ^ method.IsAsync) || method.IsReturnDroppingAsyncThunk();
}

/// <summary>
/// Returns true if the method body is a compiler-generated thunk created for runtime-async machinery.
/// This includes thunks that convert to or from async calling convention, as well as resumption stubs.
/// </summary>
public static bool IsCompilerGeneratedILBodyForAsync(this MethodDesc method)
{
return method.IsAsyncThunk() || method is AsyncResumptionStub;
return (method.IsAsyncThunk() && !method.SupportsAsyncVersionCodegen()) || method is AsyncResumptionStub;
}

public static bool RequiresSaveRestoreOfAsyncContexts(this MethodDesc method)
Expand Down
29 changes: 21 additions & 8 deletions src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -826,13 +826,11 @@ private bool Get_CORINFO_METHOD_INFO(MethodDesc method, MethodIL methodIL, CORIN
methodInfo->options |= CorInfoOptions.CORINFO_ASYNC_SAVE_CONTEXTS;
}

#if !READYTORUN
if (method.SupportsAsyncVersionCodegen())
{
// This is an async version and the IL belongs to the sync version.
methodInfo->options |= CorInfoOptions.CORINFO_ASYNC_VERSION;
}
#endif

methodInfo->regionKind = CorInfoRegionKind.CORINFO_REGION_NONE;
Get_CORINFO_SIG_INFO(method, sig: &methodInfo->args, methodIL);
Expand Down Expand Up @@ -1658,7 +1656,7 @@ static CORINFO_RESOLVED_TOKEN CreateResolvedTokenFromMethod(CorInfoImpl jitInter
return null;
}

variantIsThunk = method?.IsAsyncThunk() ?? false;
variantIsThunk = method.IsAsyncThunk();
return ObjectToHandle(method);
Comment thread
jtschuster marked this conversation as resolved.
}

Expand Down Expand Up @@ -3600,11 +3598,8 @@ private void getAsyncInfo(ref CORINFO_ASYNC_INFO pAsyncInfoOut)
instArg.constLookup.accessType = InfoAccessType.IAT_VALUE;
instArg.constLookup.addr = null;

#if READYTORUN
return null;
#else
MethodDesc caller = HandleToObject(callerHandle);
Debug.Assert(caller.IsAsyncVariant() && caller.IsAsyncThunk());
Debug.Assert(caller.SupportsAsyncVersionCodegen());

MethodDesc taskReturningMethod = caller.GetTargetOfAsyncVariant();
TypeDesc taskReturnType = taskReturningMethod.Signature.ReturnType;
Expand Down Expand Up @@ -3639,12 +3634,30 @@ private void getAsyncInfo(ref CORINFO_ASYNC_INFO pAsyncInfoOut)

if (result.RequiresInstArg())
{
#if READYTORUN
if (runtimeDeterminedResult.IsRuntimeDeterminedExactMethod)
{
// TODO-Async: the instantiation argument would have to be obtained through a runtime
// generic dictionary lookup, which is not yet emitted here, so defer to the runtime JIT.
throw new RequiresRuntimeJitException($"getAwaitReturnCall: runtime-determined exact instantiation requires runtime JIT ({runtimeDeterminedResult})");
Comment thread
jtschuster marked this conversation as resolved.
}

instArg.constLookup = CreateConstLookupToSymbol(
_compilation.SymbolNodeFactory.CreateReadyToRunHelper(
ReadyToRunHelperId.MethodDictionary,
new MethodWithToken(
runtimeDeterminedResult,
_compilation.NodeFactory.Resolver.GetModuleTokenForMethod(runtimeDeterminedResult, allowDynamicallyCreatedReference: true, throwIfNotFound: true),
constrainedType: null,
unboxing: false,
genericContextObject: caller)));
#else
// Runtime lookup is needed
ComputeLookup(caller != MethodBeingCompiled, runtimeDeterminedResult, ReadyToRunHelperId.MethodDictionary, caller, ref instArg);
#endif
}

return ObjectToHandle(result);
#endif
}

private CORINFO_CLASS_STRUCT_* getContinuationType(nuint dataSize, ref bool objRefs, nuint objRefsSize)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1125,7 +1125,6 @@ static void Validate(ReadyToRunReader reader)
/// to the underlying EcmaMethod.
/// </summary>
[Fact]
[ActiveIssue("https://github.com/dotnet/runtime/issues/129524")]
public void CompositeAsyncDevirtNonAsyncCallee()
{
// Compiled WITHOUT runtime-async so the awaited virtuals get synthesized async-variant thunks.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@
// WriterBase's virtuals are classic methods that get an "async variant" thunk when a runtime-async
// caller in another module awaits them. ConcreteWriter is sealed and doesn't override, and Holder
// exposes it base-typed, so the caller late-devirtualizes to the inherited base method.
using System.Runtime.CompilerServices;
using System.Threading.Tasks;

public class WriterBase
{
[MethodImpl(MethodImplOptions.NoInlining)]
public virtual ValueTask CompleteValueTaskAsync() => default;

[MethodImpl(MethodImplOptions.NoInlining)]
public virtual Task CompleteTaskAsync() => Task.CompletedTask;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilde
sb.Append(nameMangler.CompilationUnitPrefix);
sb.Append($@"ILBodyFixupSignature({_fixupKind.ToString()}): ");
sb.Append(nameMangler.GetMangledMethodName(ILMethod));
sb.Append(" for ");
sb.Append(nameMangler.GetMangledMethodName(_signatureMethod));
Comment thread
jtschuster marked this conversation as resolved.
}

public override int CompareToImpl(ISortableNode other, CompilerComparer comparer)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1023,6 +1023,14 @@ private void AddNecessaryAsyncReferences(MethodDesc method)
continuation.GetKnownField("State"u8),
continuation.GetKnownField("Flags"u8),
];
// The signature types for the TransparentAwaitWithResult overloads used by
// CorInfoImpl.getAwaitReturnCall (kept in sync with that method).
TypeDesc voidType = TypeSystemContext.GetWellKnownType(WellKnownType.Void);
TypeDesc taskType = TypeSystemContext.SystemModule.GetKnownType("System.Threading.Tasks"u8, "Task"u8);
TypeDesc valueTaskType = TypeSystemContext.SystemModule.GetKnownType("System.Threading.Tasks"u8, "ValueTask"u8);
MetadataType taskOfTType = TypeSystemContext.SystemModule.GetKnownType("System.Threading.Tasks"u8, "Task`1"u8);
MetadataType valueTaskOfTType = TypeSystemContext.SystemModule.GetKnownType("System.Threading.Tasks"u8, "ValueTask`1"u8);
TypeDesc methodVar = TypeSystemContext.GetSignatureVariable(0, method: true);
MethodDesc[] requiredMethods =
[
// For CorInfoImpl.getAsyncInfo
Expand All @@ -1038,6 +1046,13 @@ private void AddNecessaryAsyncReferences(MethodDesc method)
asyncHelpers.GetKnownMethod("AllocContinuation"u8, null),
asyncHelpers.GetKnownMethod("AllocContinuationClass"u8, null),
asyncHelpers.GetKnownMethod("AllocContinuationMethod"u8, null),

// For CorInfoImpl.getAwaitReturnCall. The JIT synthesizes calls to these overloads, so they
// have no IL token in the caller and their manifest tokens must be pre-seeded here.
asyncHelpers.GetKnownMethod("TransparentAwaitWithResult"u8, new MethodSignature(MethodSignatureFlags.Static, 0, voidType, [taskType])),
asyncHelpers.GetKnownMethod("TransparentAwaitWithResult"u8, new MethodSignature(MethodSignatureFlags.Static, 0, voidType, [valueTaskType])),
asyncHelpers.GetKnownMethod("TransparentAwaitWithResult"u8, new MethodSignature(MethodSignatureFlags.Static, 1, methodVar, [taskOfTType.MakeInstantiatedType(methodVar)])),
asyncHelpers.GetKnownMethod("TransparentAwaitWithResult"u8, new MethodSignature(MethodSignatureFlags.Static, 1, methodVar, [valueTaskOfTType.MakeInstantiatedType(methodVar)])),
];
var moduleForNewReferences = ((EcmaMethod)method.GetPrimaryMethodDesc().GetTypicalMethodDefinition()).Module;
_tokenManager.EnsureDefTokensAreAvailable([..requiredMethods, ..requiredTypes, ..requiredFields], moduleForNewReferences, true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -582,14 +582,6 @@ public static bool ShouldSkipCompilation(InstructionSetSupport instructionSetSup
return true;
}

// Currently crossgen2 does not support compiling async versions of synchronous Task-returning functions.
// We would compile a wrapper thunk but that comes with different perf characteristics and diagnostics
// that we do not want to deal with.
if (methodNeedingCode.SupportsAsyncVersionCodegen())
{
return true;
}

if (ShouldCodeNotBeCompiledIntoFinalImage(instructionSetSupport, methodNeedingCode))
{
return true;
Expand Down
Loading