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
7 changes: 6 additions & 1 deletion src/coreclr/inc/corinfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -1485,7 +1485,6 @@ enum CorInfoTokenKind

// token comes from runtime async awaiting pattern
CORINFO_TOKENKIND_Await = 0x2000 | CORINFO_TOKENKIND_Method,
CORINFO_TOKENKIND_AwaitVirtual = 0x4000 | CORINFO_TOKENKIND_Method,
};

struct CORINFO_RESOLVED_TOKEN
Expand Down Expand Up @@ -2288,6 +2287,12 @@ class ICorStaticInfo
CORINFO_CLASS_HANDLE* classArg
) = 0;

// Get the other variant of an async method, if possible.
Comment thread
jakobbotsch marked this conversation as resolved.
virtual CORINFO_METHOD_HANDLE getAsyncOtherVariant(
CORINFO_METHOD_HANDLE ftn,
bool* variantIsThunk
) = 0;

// Given T, return the type of the default Comparer<T>.
// Returns null if the type can't be determined exactly.
virtual CORINFO_CLASS_HANDLE getDefaultComparerClass(
Expand Down
4 changes: 4 additions & 0 deletions src/coreclr/inc/icorjitinfoimpl_generated.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ CORINFO_METHOD_HANDLE getInstantiatedEntry(
CORINFO_METHOD_HANDLE* methodArg,
CORINFO_CLASS_HANDLE* classArg) override;

CORINFO_METHOD_HANDLE getAsyncOtherVariant(
CORINFO_METHOD_HANDLE ftn,
bool* variantIsThunk) override;

CORINFO_CLASS_HANDLE getDefaultComparerClass(
CORINFO_CLASS_HANDLE elemType) override;

Expand Down
10 changes: 5 additions & 5 deletions src/coreclr/inc/jiteeversionguid.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@

#include <minipal/guid.h>

constexpr GUID JITEEVersionIdentifier = { /* cd664ea4-e14e-4fd4-9cb8-e8fc1e1b5a0a */
0xcd664ea4,
0xe14e,
0x4fd4,
{0x9c, 0xb8, 0xe8, 0xfc, 0x1e, 0x1b, 0x5a, 0x0a}
constexpr GUID JITEEVersionIdentifier = { /* 22511e72-5ac8-4fc8-83ef-0b61688c68bb */
0x22511e72,
0x5ac8,
0x4fc8,
{0x83, 0xef, 0x0b, 0x61, 0x68, 0x8c, 0x68, 0xbb}
};

#endif // JIT_EE_VERSIONING_GUID_H
29 changes: 22 additions & 7 deletions src/coreclr/interpreter/compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4551,6 +4551,7 @@ void InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN* pConstrainedToken, bool re
}
else
{
const uint8_t* origIP = m_ip;
if (!newObj && m_methodInfo->args.isAsyncCall() && AsyncCallPeeps.FindAndApplyPeep(this))
{
resolvedCallToken = m_resolvedAsyncCallToken;
Expand All @@ -4576,14 +4577,30 @@ void InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN* pConstrainedToken, bool re

m_compHnd->getCallInfo(&resolvedCallToken, pConstrainedToken, m_methodInfo->ftn, flags, &callInfo);

if (callInfo.sig.isVarArg())
if (continuationContextHandling != ContinuationContextHandling::None && !callInfo.sig.isAsyncCall())
{
BADCODE("Vararg methods are not supported in interpreted code");
BADCODE("We're trying to emit an async call, but the async resolved context didn't find one");
}

if (continuationContextHandling != ContinuationContextHandling::None && !callInfo.sig.isAsyncCall())
if (continuationContextHandling != ContinuationContextHandling::None && callInfo.kind == CORINFO_CALL)
{
BADCODE("We're trying to emit an async call, but the async resolved context didn't find one");
bool isSyncCallThunk;
m_compHnd->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 create
// and go through the thunk.
ResolveToken(token, CORINFO_TOKENKIND_Method, &resolvedCallToken);
m_ip = origIP + 5;
Comment thread
jakobbotsch marked this conversation as resolved.
continuationContextHandling = ContinuationContextHandling::None;
m_compHnd->getCallInfo(&resolvedCallToken, pConstrainedToken, m_methodInfo->ftn, flags, &callInfo);
}
}

if (callInfo.sig.isVarArg())
Comment thread
jakobbotsch marked this conversation as resolved.
{
BADCODE("Vararg methods are not supported in interpreted code");
}

if (isJmp)
Expand Down Expand Up @@ -6741,9 +6758,7 @@ int InterpCompiler::ApplyLdftnDelegateCtorPeep(const uint8_t* ip, OpcodePeepElem

bool InterpCompiler::ResolveAsyncCallToken(const uint8_t* ip)
{
CorInfoTokenKind tokenKind =
ip[0] == CEE_CALL ? CORINFO_TOKENKIND_Await : CORINFO_TOKENKIND_AwaitVirtual;
ResolveToken(getU4LittleEndian(ip + 1), tokenKind, &m_resolvedAsyncCallToken);
ResolveToken(getU4LittleEndian(ip + 1), CORINFO_TOKENKIND_Await, &m_resolvedAsyncCallToken);
Comment thread
jakobbotsch marked this conversation as resolved.
return m_resolvedAsyncCallToken.hMethod != NULL;
}

Expand Down
1 change: 1 addition & 0 deletions src/coreclr/jit/ICorJitInfo_names_generated.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ DEF_CLR_API(getMethodVTableOffset)
DEF_CLR_API(resolveVirtualMethod)
DEF_CLR_API(getUnboxedEntry)
DEF_CLR_API(getInstantiatedEntry)
DEF_CLR_API(getAsyncOtherVariant)
DEF_CLR_API(getDefaultComparerClass)
DEF_CLR_API(getDefaultEqualityComparerClass)
DEF_CLR_API(getSZArrayHelperEnumeratorClass)
Expand Down
10 changes: 10 additions & 0 deletions src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,16 @@ CORINFO_METHOD_HANDLE WrapICorJitInfo::getInstantiatedEntry(
return temp;
}

CORINFO_METHOD_HANDLE WrapICorJitInfo::getAsyncOtherVariant(
CORINFO_METHOD_HANDLE ftn,
bool* variantIsThunk)
{
API_ENTER(getAsyncOtherVariant);
CORINFO_METHOD_HANDLE temp = wrapHnd->getAsyncOtherVariant(ftn, variantIsThunk);
API_LEAVE(getAsyncOtherVariant);
return temp;
}

CORINFO_CLASS_HANDLE WrapICorJitInfo::getDefaultComparerClass(
CORINFO_CLASS_HANDLE elemType)
{
Expand Down
9 changes: 7 additions & 2 deletions src/coreclr/jit/ee_il_dll.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,12 @@ inline var_types Compiler::TypeHandleToVarType(CorInfoType jitType, CORINFO_CLAS
return type;
}

inline CORINFO_CALLINFO_FLAGS combine(CORINFO_CALLINFO_FLAGS flag1, CORINFO_CALLINFO_FLAGS flag2)
constexpr CORINFO_CALLINFO_FLAGS operator|(CORINFO_CALLINFO_FLAGS a, CORINFO_CALLINFO_FLAGS b)
{
return (CORINFO_CALLINFO_FLAGS)(flag1 | flag2);
return (CORINFO_CALLINFO_FLAGS)((uint32_t)a | (uint32_t)b);
}

inline CORINFO_CALLINFO_FLAGS& operator|=(CORINFO_CALLINFO_FLAGS& a, CORINFO_CALLINFO_FLAGS b)
{
return a = (CORINFO_CALLINFO_FLAGS)((uint32_t)a | (uint32_t)b);
}
60 changes: 42 additions & 18 deletions src/coreclr/jit/importer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8556,7 +8556,7 @@ void Compiler::impImportBlockCode(BasicBlock* block)
JITDUMP(" %08X", resolvedToken.token);

eeGetCallInfo(&resolvedToken, (prefixFlags & PREFIX_CONSTRAINED) ? &constrainedResolvedToken : nullptr,
combine(CORINFO_CALLINFO_SECURITYCHECKS, CORINFO_CALLINFO_LDFTN), &callInfo);
CORINFO_CALLINFO_SECURITYCHECKS | CORINFO_CALLINFO_LDFTN, &callInfo);

// This check really only applies to intrinsic Array.Address methods
if (callInfo.sig.callConv & CORINFO_CALLCONV_PARAMTYPE)
Expand Down Expand Up @@ -8595,8 +8595,7 @@ void Compiler::impImportBlockCode(BasicBlock* block)
JITDUMP(" %08X", resolvedToken.token);

eeGetCallInfo(&resolvedToken, nullptr /* constraint typeRef */,
combine(combine(CORINFO_CALLINFO_SECURITYCHECKS, CORINFO_CALLINFO_LDFTN),
CORINFO_CALLINFO_CALLVIRT),
CORINFO_CALLINFO_SECURITYCHECKS | CORINFO_CALLINFO_LDFTN | CORINFO_CALLINFO_CALLVIRT,
&callInfo);

// This check really only applies to intrinsic Array.Address methods
Expand Down Expand Up @@ -8729,7 +8728,7 @@ void Compiler::impImportBlockCode(BasicBlock* block)
_impResolveToken(CORINFO_TOKENKIND_NewObj);

eeGetCallInfo(&resolvedToken, nullptr /* constraint typeRef*/,
combine(CORINFO_CALLINFO_SECURITYCHECKS, CORINFO_CALLINFO_ALLOWINSTPARAM), &callInfo);
CORINFO_CALLINFO_SECURITYCHECKS | CORINFO_CALLINFO_ALLOWINSTPARAM, &callInfo);

mflags = callInfo.methodFlags;

Expand Down Expand Up @@ -8982,16 +8981,8 @@ void Compiler::impImportBlockCode(BasicBlock* block)

if (isAwait)
{
_impResolveToken(opcode == CEE_CALLVIRT ? CORINFO_TOKENKIND_AwaitVirtual
: CORINFO_TOKENKIND_Await);
if (resolvedToken.hMethod != nullptr)
{
// There is a runtime async variant that is implicitly awaitable, just call that.
// skip the await pattern to the last token.
codeAddr = codeAddrAfterMatch;
opcodeOffs = awaitOffset;
}
else
_impResolveToken(CORINFO_TOKENKIND_Await);
if (resolvedToken.hMethod == nullptr)
{
// This can happen in cases when the Task-returning method is not a runtime Async
// function. For example "T M1<T>(T arg) => arg" when called with a Task argument.
Expand All @@ -9009,12 +9000,45 @@ void Compiler::impImportBlockCode(BasicBlock* block)
_impResolveToken(CORINFO_TOKENKIND_Method);
}

CORINFO_CALLINFO_FLAGS flags = CORINFO_CALLINFO_ALLOWINSTPARAM | CORINFO_CALLINFO_SECURITYCHECKS;
if (opcode == CEE_CALLVIRT)
{
flags |= CORINFO_CALLINFO_CALLVIRT;
}

eeGetCallInfo(&resolvedToken,
(prefixFlags & PREFIX_CONSTRAINED) ? &constrainedResolvedToken : nullptr,
// this is how impImportCall invokes getCallInfo
combine(combine(CORINFO_CALLINFO_ALLOWINSTPARAM, CORINFO_CALLINFO_SECURITYCHECKS),
(opcode == CEE_CALLVIRT) ? CORINFO_CALLINFO_CALLVIRT : CORINFO_CALLINFO_NONE),
(prefixFlags & PREFIX_CONSTRAINED) ? &constrainedResolvedToken : nullptr, flags,
&callInfo);

if (isAwait && (callInfo.kind == CORINFO_CALL))
{
assert(callInfo.sig.isAsyncCall());
bool isSyncCallThunk;
info.compCompHnd->getAsyncOtherVariant(callInfo.hMethod, &isSyncCallThunk);
Comment thread
jakobbotsch marked this conversation as resolved.
if (!isSyncCallThunk)
Comment thread
jakobbotsch marked this conversation as resolved.
{
// 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);
isAwait = false;

JITDUMP(
"Async variant provided by VM is a thunk, switching direct call to synchronous task-returning method\n");
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
// prefer to directly call it. Skip the await pattern to the last token.
Comment thread
jakobbotsch marked this conversation as resolved.
codeAddr = codeAddrAfterMatch;
opcodeOffs = awaitOffset;
}
}
else
{
Expand Down
11 changes: 11 additions & 0 deletions src/coreclr/jit/importercalls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6937,6 +6937,17 @@ void Compiler::impSetupAsyncCall(GenTreeCall* call, OPCODE opcode, unsigned pref
}

call->AsCall()->SetIsAsync(new (this, CMK_Async) AsyncCallInfo(asyncInfo));

#ifdef DEBUG
if (JitConfig.EnableExtraSuperPmiQueries() && (call->gtCallType == CT_USER_FUNC))
{
// Query the async variants (twice, to get both directions)
CORINFO_METHOD_HANDLE method = call->gtCallMethHnd;
bool variantIsThunk;
method = info.compCompHnd->getAsyncOtherVariant(method, &variantIsThunk);
method = info.compCompHnd->getAsyncOtherVariant(method, &variantIsThunk);
Comment thread
jakobbotsch marked this conversation as resolved.
}
#endif
}

//------------------------------------------------------------------------
Expand Down
39 changes: 23 additions & 16 deletions src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1558,6 +1558,27 @@ static CORINFO_RESOLVED_TOKEN CreateResolvedTokenFromMethod(CorInfoImpl jitInter
return null;
}

private CORINFO_METHOD_STRUCT_* getAsyncOtherVariant(CORINFO_METHOD_STRUCT_* ftn, ref bool variantIsThunk)
{
MethodDesc method = HandleToObject(ftn);
if (method.IsAsyncVariant())
Comment thread
jakobbotsch marked this conversation as resolved.
{
method = method.GetTargetOfAsyncVariant();
}
else if (method.Signature.ReturnsTaskOrValueTask())
{
method = method.GetAsyncVariant();
}
else
{
variantIsThunk = false;
return null;
}

variantIsThunk = method?.IsAsyncThunk() ?? false;
return ObjectToHandle(method);
}

private CORINFO_CLASS_STRUCT_* getDefaultComparerClass(CORINFO_CLASS_STRUCT_* elemType)
{
TypeDesc comparand = HandleToObject(elemType);
Expand Down Expand Up @@ -1778,7 +1799,7 @@ private object GetRuntimeDeterminedObjectForToken(ref CORINFO_RESOLVED_TOKEN pRe
if (pResolvedToken.tokenType == CorInfoTokenKind.CORINFO_TOKENKIND_Newarr)
result = ((TypeDesc)result).MakeArrayType();

if (pResolvedToken.tokenType is CorInfoTokenKind.CORINFO_TOKENKIND_Await or CorInfoTokenKind.CORINFO_TOKENKIND_AwaitVirtual)
if (pResolvedToken.tokenType is CorInfoTokenKind.CORINFO_TOKENKIND_Await)
result = _compilation.TypeSystemContext.GetAsyncVariantMethod((MethodDesc)result);

return result;
Expand Down Expand Up @@ -1868,7 +1889,7 @@ private void resolveToken(ref CORINFO_RESOLVED_TOKEN pResolvedToken)
_compilation.NodeFactory.MetadataManager.GetDependenciesDueToAccess(ref _additionalDependencies, _compilation.NodeFactory, (MethodIL)methodIL, method);
#endif

if (pResolvedToken.tokenType is CorInfoTokenKind.CORINFO_TOKENKIND_Await or CorInfoTokenKind.CORINFO_TOKENKIND_AwaitVirtual)
if (pResolvedToken.tokenType is CorInfoTokenKind.CORINFO_TOKENKIND_Await)
{
// in rare cases a method that returns Task is not actually TaskReturning (i.e. returns T).
// we cannot resolve to an Async variant in such case.
Expand All @@ -1878,20 +1899,6 @@ private void resolveToken(ref CORINFO_RESOLVED_TOKEN pResolvedToken)
// Don't get async variant of Delegate.Invoke method; the pointed to method is not an async variant either.
allowAsyncVariant = allowAsyncVariant && !method.OwningType.IsDelegate;

#if !READYTORUN
if (allowAsyncVariant)
{
bool isDirect = pResolvedToken.tokenType == CorInfoTokenKind.CORINFO_TOKENKIND_Await || method.IsCallEffectivelyDirect();
if (isDirect && !method.IsAsync)
{
// Async variant would be a thunk. Do not resolve direct calls
// to async thunks. That just creates and JITs unnecessary
// thunks, and the thunks are harder for the JIT to optimize.
allowAsyncVariant = false;
}
}
#endif

method = allowAsyncVariant
? _compilation.TypeSystemContext.GetAsyncVariantMethod(method)
: null;
Expand Down
17 changes: 17 additions & 0 deletions src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ static ICorJitInfoCallbacks()
s_callbacks.resolveVirtualMethod = &_resolveVirtualMethod;
s_callbacks.getUnboxedEntry = &_getUnboxedEntry;
s_callbacks.getInstantiatedEntry = &_getInstantiatedEntry;
s_callbacks.getAsyncOtherVariant = &_getAsyncOtherVariant;
s_callbacks.getDefaultComparerClass = &_getDefaultComparerClass;
s_callbacks.getDefaultEqualityComparerClass = &_getDefaultEqualityComparerClass;
s_callbacks.getSZArrayHelperEnumeratorClass = &_getSZArrayHelperEnumeratorClass;
Expand Down Expand Up @@ -220,6 +221,7 @@ static ICorJitInfoCallbacks()
public delegate* unmanaged<IntPtr, IntPtr*, CORINFO_DEVIRTUALIZATION_INFO*, byte> resolveVirtualMethod;
public delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, bool*, CORINFO_METHOD_STRUCT_*> getUnboxedEntry;
public delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, CORINFO_METHOD_STRUCT_**, CORINFO_CLASS_STRUCT_**, CORINFO_METHOD_STRUCT_*> getInstantiatedEntry;
public delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, bool*, CORINFO_METHOD_STRUCT_*> getAsyncOtherVariant;
public delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_CLASS_STRUCT_*> getDefaultComparerClass;
public delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_CLASS_STRUCT_*> getDefaultEqualityComparerClass;
public delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_CLASS_STRUCT_*> getSZArrayHelperEnumeratorClass;
Expand Down Expand Up @@ -665,6 +667,21 @@ private static byte _resolveVirtualMethod(IntPtr thisHandle, IntPtr* ppException
}
}

[UnmanagedCallersOnly]
private static CORINFO_METHOD_STRUCT_* _getAsyncOtherVariant(IntPtr thisHandle, IntPtr* ppException, CORINFO_METHOD_STRUCT_* ftn, bool* variantIsThunk)
{
var _this = GetThis(thisHandle);
try
{
return _this.getAsyncOtherVariant(ftn, ref *variantIsThunk);
Comment thread
jakobbotsch marked this conversation as resolved.
}
catch (Exception ex)
{
*ppException = _this.AllocException(ex);
return default;
}
}

[UnmanagedCallersOnly]
private static CORINFO_CLASS_STRUCT_* _getDefaultComparerClass(IntPtr thisHandle, IntPtr* ppException, CORINFO_CLASS_STRUCT_* elemType)
{
Expand Down
1 change: 0 additions & 1 deletion src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1476,7 +1476,6 @@ public enum CorInfoTokenKind

// token comes from runtime async awaiting pattern
CORINFO_TOKENKIND_Await = 0x2000 | CORINFO_TOKENKIND_Method,
CORINFO_TOKENKIND_AwaitVirtual = 0x4000 | CORINFO_TOKENKIND_Method,
};

// These are error codes returned by CompileMethod
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ FUNCTIONS
bool resolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO* info);
CORINFO_METHOD_HANDLE getUnboxedEntry(CORINFO_METHOD_HANDLE ftn, bool* requiresInstMethodTableArg);
CORINFO_METHOD_HANDLE getInstantiatedEntry(CORINFO_METHOD_HANDLE ftn, CORINFO_METHOD_HANDLE* methodArg, CORINFO_CLASS_HANDLE* classArg);
CORINFO_METHOD_HANDLE getAsyncOtherVariant(CORINFO_METHOD_HANDLE ftn, bool* variantIsThunk);
CORINFO_CLASS_HANDLE getDefaultComparerClass(CORINFO_CLASS_HANDLE elemType);
CORINFO_CLASS_HANDLE getDefaultEqualityComparerClass(CORINFO_CLASS_HANDLE elemType);
CORINFO_CLASS_HANDLE getSZArrayHelperEnumeratorClass(CORINFO_CLASS_HANDLE elemType);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -484,18 +484,6 @@ private void ImportCall(ILOpcode opcode, int token)
// Don't get async variant of Delegate.Invoke method; the pointed to method is not an async variant either.
allowAsyncVariant = allowAsyncVariant && !method.OwningType.IsDelegate;

if (allowAsyncVariant)
{
bool isDirect = opcode == ILOpcode.call || method.IsCallEffectivelyDirect();
if (isDirect && !method.IsAsync)
{
// Async variant would be a thunk. Do not resolve direct calls
// to async thunks. That just creates and JITs unnecessary
// thunks, and the thunks are harder for the JIT to optimize.
allowAsyncVariant = false;
}
}

if (allowAsyncVariant && MatchTaskAwaitPattern())
{
runtimeDeterminedMethod = _factory.TypeSystemContext.GetAsyncVariantMethod(runtimeDeterminedMethod);
Expand Down
Loading
Loading