Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
26 changes: 14 additions & 12 deletions src/coreclr/inc/corinfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -1434,14 +1434,14 @@ enum CORINFO_THIS_TRANSFORM

enum CORINFO_CALLINFO_FLAGS
{
CORINFO_CALLINFO_NONE = 0x0000,
CORINFO_CALLINFO_ALLOWINSTPARAM = 0x0001, // Can the compiler generate code to pass an instantiation parameters? Simple compilers should not use this flag
CORINFO_CALLINFO_CALLVIRT = 0x0002, // Is it a virtual call?
// UNUSED = 0x0004,
CORINFO_CALLINFO_DISALLOW_STUB = 0x0008, // Do not use a stub for this call, even if it is a virtual call.
CORINFO_CALLINFO_SECURITYCHECKS = 0x0010, // Perform security checks.
CORINFO_CALLINFO_LDFTN = 0x0020, // Resolving target of LDFTN
// UNUSED = 0x0040,
CORINFO_CALLINFO_NONE = 0x0000,
CORINFO_CALLINFO_ALLOWINSTPARAM = 0x0001, // Can the compiler generate code to pass an instantiation parameters? Simple compilers should not use this flag
CORINFO_CALLINFO_CALLVIRT = 0x0002, // Is it a virtual call?
CORINFO_CALLINFO_ALLOWASYNCVARIANT = 0x0004, // allow resolution to an async variant
CORINFO_CALLINFO_DISALLOW_STUB = 0x0008, // Do not use a stub for this call, even if it is a virtual call.
CORINFO_CALLINFO_SECURITYCHECKS = 0x0010, // Perform security checks.
CORINFO_CALLINFO_LDFTN = 0x0020, // Resolving target of LDFTN
// UNUSED = 0x0040,
};

enum CorInfoIsAccessAllowedResult
Expand Down Expand Up @@ -1484,10 +1484,6 @@ enum CorInfoTokenKind

// token comes from devirtualizing a method
CORINFO_TOKENKIND_DevirtualizedMethod = 0x800 | CORINFO_TOKENKIND_Method,

// 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 @@ -1560,6 +1556,12 @@ struct CORINFO_CALL_INFO
CORINFO_CONST_LOOKUP instParamLookup;

bool wrapperDelegateInvoke;

// If CORINFO_CALLINFO_ALLOWASYNCVARIANT was passed, this is the resolved
// async variant or NULL if no async variant was resolved.
// This is the async variant of the token's method and differs from hMethod
// of this class in cases of sharing, constrained resolution etc.
CORINFO_METHOD_HANDLE resolvedAsyncVariant;
Copy link
Member

@jkotas jkotas Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this a separate CORINFO_METHOD_HANDLE field and not just returned in hMethod?

The idea with getCallInfo is that it tells the JIT what to call exactly. I assume that if the VM tells the JIT to call async variant, the JIT is better to call it. Is that correct?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hMethod does return the async variant of the target method, and that's what the JIT calls, but that differs from the async variant of the token's method in cases of sharing or due to constrained calls. The JIT takes this CORINFO_METHOD_HANDLE and updates hMethod of the token with it since the token itself is passed back into the EE in various cases. That makes token match what was happening before when it was part of resolveToken (specifically so that the EE sees the same hMethod in that token).

I am not 100% sure what the EE sides end up using hMethod of the token for in those callbacks and whether this could be simplified or not.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose the primary one is that we call embedGenericHandle and pass it the token to compute the runtime lookup for an instantiation parameter, here:

instParam = impTokenToHandle(pResolvedToken, &runtimeLookup, true /*mustRestoreHandle*/);
if (instParam == nullptr)
{
assert(compDonotInline());
return TYP_UNDEF;
}

This works now because the JIT updates the token's hMethod with callInfo.resolvedAsyncVariant when it notices that the async variant was resolved.

Perhaps a cleaner way would be if getCallInfo directly returned the runtime lookup instead of this embed roundtrip. The current token vs callInfo.hMethod mismatch leads to other problems here, I guess this comment is referencing the same thing:

// If the target method is resolved via constrained static virtual dispatch
// And it requires an instParam, we do not have the generic dictionary infrastructure
// to load the correct generic context arg via EmbedGenericHandle.
// Instead, force the call to go down the CORINFO_CALL_CODE_POINTER code path
// which should have somewhat inferior performance. This should only actually happen in the case
// of shared generic code calling a shared generic implementation method, which should be rare.
//
// An alternative design would be to add a new generic dictionary entry kind to hold the MethodDesc
// of the constrained target instead, and use that in some circumstances; however, implementation of
// that design requires refactoring variuos parts of the JIT interface as well as
// TryResolveConstraintMethodApprox. In particular we would need to be abled to embed a constrained lookup
// via EmbedGenericHandle, as well as decide in TryResolveConstraintMethodApprox if the call can be made
// via a single use of CORINFO_CALL_CODE_POINTER, or would be better done with a CORINFO_CALL + embedded
// constrained generic handle, or if there is a case where we would want to use both a CORINFO_CALL and
// embedded constrained generic handle. Given the current expected high performance use case of this feature
// which is generic numerics which will always resolve to exact valuetypes, it is not expected that
// the complexity involved would be worth the risk. Other scenarios are not expected to be as performance
// sensitive.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about we introduce CORINFO_TOKENKIND_Async, then delete CORINFO_METHOD_HANDLE resolvedAsyncVariant;, and keep using hMethod, but also set the async tokenType on the EE side whenever we return the async variant.

That way we'll keep clearly marking the token as "async variant token". It is weird that the EE side would change tokenType, but also JIT side changing hMethod is not exactly clean.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would work but it will be a new contract for getCallInfo (now has another output, requires SPMI changes).

It's probably a bit better but I am leaning towards making it so that we do not need the token anymore for non-trivial things after getCallInfo has been called by making it return the runtime lookup. It seems it is the only way to make things self-consistent.

};

enum CORINFO_DEVIRTUALIZATION_DETAIL
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 = { /* 8f2ee94f-5111-4b7b-97bd-5c689d476385 */
0x8f2ee94f,
0x5111,
0x4b7b,
{0x97, 0xbd, 0x5c, 0x68, 0x9d, 0x47, 0x63, 0x85}
constexpr GUID JITEEVersionIdentifier = { /* 8edbe247-3d6f-43ca-81de-648d358d36f4 */
0x8edbe247,
0x3d6f,
0x43ca,
{0x81, 0xde, 0x64, 0x8d, 0x35, 0x8d, 0x36, 0xf4}
};

#endif // JIT_EE_VERSIONING_GUID_H
41 changes: 22 additions & 19 deletions src/coreclr/interpreter/compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4551,10 +4551,13 @@ void InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN* pConstrainedToken, bool re
}
else
{
unsigned flags = CORINFO_CALLINFO_ALLOWINSTPARAM | CORINFO_CALLINFO_SECURITYCHECKS | CORINFO_CALLINFO_DISALLOW_STUB;
const uint8_t* callIP = m_ip;
if (!newObj && m_methodInfo->args.isAsyncCall() && AsyncCallPeeps.FindAndApplyPeep(this))
{
resolvedCallToken = m_resolvedAsyncCallToken;
ResolveToken(token, CORINFO_TOKENKIND_Method, &resolvedCallToken);
continuationContextHandling = m_currentContinuationContextHandling;
flags |= CORINFO_CALLINFO_ALLOWASYNCVARIANT;
}
else
{
Expand All @@ -4570,11 +4573,23 @@ void InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN* pConstrainedToken, bool re
m_compHnd->getNewHelper(resolvedCallToken.hClass, &hasSideEffects);
}

CORINFO_CALLINFO_FLAGS flags = (CORINFO_CALLINFO_FLAGS)(CORINFO_CALLINFO_ALLOWINSTPARAM | CORINFO_CALLINFO_SECURITYCHECKS | CORINFO_CALLINFO_DISALLOW_STUB);
if (isVirtual)
flags = (CORINFO_CALLINFO_FLAGS)(flags | CORINFO_CALLINFO_CALLVIRT);
flags |= CORINFO_CALLINFO_CALLVIRT;

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

if (flags & CORINFO_CALLINFO_ALLOWASYNCVARIANT)
{
if (callInfo.resolvedAsyncVariant != NULL)
{
resolvedCallToken.hMethod = callInfo.resolvedAsyncVariant;
}
else
{
// Undo the IP change from the peep
m_ip = callIP + 5;
}
}

if (callInfo.sig.isVarArg())
{
Expand Down Expand Up @@ -6739,14 +6754,6 @@ int InterpCompiler::ApplyLdftnDelegateCtorPeep(const uint8_t* ip, OpcodePeepElem
return -1;
}

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);
return m_resolvedAsyncCallToken.hMethod != NULL;
}

bool InterpCompiler::IsRuntimeAsyncCall(const uint8_t* ip, OpcodePeepElement* pattern, void** ppComputedInfo)
{
CORINFO_RESOLVED_TOKEN awaitResolvedToken;
Expand All @@ -6757,10 +6764,6 @@ bool InterpCompiler::IsRuntimeAsyncCall(const uint8_t* ip, OpcodePeepElement* pa
return false;
}

if (!ResolveAsyncCallToken(ip))
{
return false;
}
m_currentContinuationContextHandling = ContinuationContextHandling::ContinueOnCapturedContext;
return true;
}
Expand Down Expand Up @@ -6825,7 +6828,7 @@ bool InterpCompiler::IsRuntimeAsyncCallConfigureAwaitTask(const uint8_t* ip, Opc
return false;
}

return ResolveAsyncCallToken(ip);
return true;
}

bool InterpCompiler::IsRuntimeAsyncCallConfigureAwaitValueTaskExactStLoc(const uint8_t* ip, OpcodePeepElement* pattern, void** ppComputedInfo)
Expand Down Expand Up @@ -6895,7 +6898,7 @@ bool InterpCompiler::IsRuntimeAsyncCallConfigureAwaitValueTaskExactStLoc(const u
return false;
}

return ResolveAsyncCallToken(ip);
return true;
}

bool InterpCompiler::IsRuntimeAsyncCallConfigureAwaitValueTask(const uint8_t* ip, OpcodePeepElement* pattern, void** ppComputedInfo)
Expand Down Expand Up @@ -6928,7 +6931,7 @@ bool InterpCompiler::IsRuntimeAsyncCallConfigureAwaitValueTask(const uint8_t* ip
return false;
}

return ResolveAsyncCallToken(ip);
return true;
}

int InterpCompiler::ApplyConvRUnR4Peep(const uint8_t* ip, OpcodePeepElement* peep, void* computedInfo)
Expand Down
1 change: 0 additions & 1 deletion src/coreclr/interpreter/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -927,7 +927,6 @@ class InterpCompiler
bool IsLdftnDelegateCtorPeep(const uint8_t* ip, OpcodePeepElement* peep, void** outComputedInfo);
int ApplyLdftnDelegateCtorPeep(const uint8_t* ip, OpcodePeepElement* peep, void* computedInfo);

bool ResolveAsyncCallToken(const uint8_t* ip);
enum class ContinuationContextHandling : uint8_t
{
ContinueOnCapturedContext,
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);
}
49 changes: 24 additions & 25 deletions src/coreclr/jit/importer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8600,7 +8600,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 @@ -8639,8 +8639,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 @@ -8773,7 +8772,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 @@ -9000,6 +8999,14 @@ void Compiler::impImportBlockCode(BasicBlock* block)
// many other places. We unfortunately embed that knowledge here.
if (opcode != CEE_CALLI)
{
CORINFO_CALLINFO_FLAGS callInfoFlags =
CORINFO_CALLINFO_ALLOWINSTPARAM | CORINFO_CALLINFO_SECURITYCHECKS;

if (opcode == CEE_CALLVIRT)
{
callInfoFlags |= CORINFO_CALLINFO_CALLVIRT;
}

bool isAwait = false;
int configVal = -1; // -1 not configured, 0/1 configured to false/true
const BYTE* codeAddrAfterMatch = nullptr;
Expand All @@ -9021,44 +9028,36 @@ void Compiler::impImportBlockCode(BasicBlock* block)
{
prefixFlags |= PREFIX_TASK_AWAIT_CONTINUE_ON_CAPTURED_CONTEXT;
}

callInfoFlags |= CORINFO_CALLINFO_ALLOWASYNCVARIANT;
}
}

_impResolveToken(CORINFO_TOKENKIND_Method);

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

if (isAwait)
{
_impResolveToken(opcode == CEE_CALLVIRT ? CORINFO_TOKENKIND_AwaitVirtual
: CORINFO_TOKENKIND_Await);
if (resolvedToken.hMethod != nullptr)
if (callInfo.resolvedAsyncVariant != NO_METHOD_HANDLE)
{
// 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;

JITDUMP("Async variant provided by VM\n");
// Update the token as we may need it again for various runtime lookups
resolvedToken.hMethod = callInfo.resolvedAsyncVariant;
}
else
{
// 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.
// It can also happen generally if the VM does not think using the async entry point
// is worth it. Treat these as a regular call that is Awaited.
_impResolveToken(CORINFO_TOKENKIND_Method);
prefixFlags &= ~(PREFIX_IS_TASK_AWAIT | PREFIX_TASK_AWAIT_CONTINUE_ON_CAPTURED_CONTEXT);
isAwait = false;

JITDUMP("No async variant provided by VM, treating as regular call that is awaited\n");
}
}
else
{
_impResolveToken(CORINFO_TOKENKIND_Method);
}

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),
&callInfo);
}
else
{
Expand Down
5 changes: 0 additions & 5 deletions src/coreclr/jit/importercalls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6917,11 +6917,6 @@ void Compiler::impSetupAsyncCall(GenTreeCall* call, OPCODE opcode, unsigned pref
JITDUMP(" Continuation continues on thread pool\n");
}
}
else if (opcode == CEE_CALLI)
{
// Used for unboxing/instantiating stubs
JITDUMP("Call is an async calli\n");
}
Comment on lines -6920 to -6924
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated cleanup, this is no longer needed.

else
{
JITDUMP("Call is an async non-task await\n");
Expand Down
6 changes: 3 additions & 3 deletions src/coreclr/jit/morph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5358,14 +5358,14 @@ GenTree* Compiler::fgMorphTailCallViaHelpers(GenTreeCall* call, CORINFO_TAILCALL
{
assert(!call->tailCallInfo->GetSig()->hasTypeArg());

CORINFO_CALL_INFO callInfo;
unsigned flags = CORINFO_CALLINFO_LDFTN;
CORINFO_CALL_INFO callInfo;
CORINFO_CALLINFO_FLAGS flags = CORINFO_CALLINFO_LDFTN;
if (call->tailCallInfo->IsCallvirt())
{
flags |= CORINFO_CALLINFO_CALLVIRT;
}

eeGetCallInfo(call->tailCallInfo->GetToken(), nullptr, (CORINFO_CALLINFO_FLAGS)flags, &callInfo);
eeGetCallInfo(call->tailCallInfo->GetToken(), nullptr, flags, &callInfo);
target = getVirtMethodPointerTree(thisPtrStubArg, call->tailCallInfo->GetToken(), &callInfo);
}

Expand Down
46 changes: 4 additions & 42 deletions src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1763,7 +1763,7 @@ private static object ResolveTokenInScope(MethodILScope methodIL, object typeOrM
return result;
}

private object GetRuntimeDeterminedObjectForToken(ref CORINFO_RESOLVED_TOKEN pResolvedToken)
private object GetRuntimeDeterminedObjectForToken(ref CORINFO_RESOLVED_TOKEN pResolvedToken, bool isAsyncVariant = false)
{
// Since RyuJIT operates on canonical types (as opposed to runtime determined ones), but the
// dependency analysis operates on runtime determined ones, we convert the resolved token
Expand All @@ -1777,8 +1777,7 @@ private object GetRuntimeDeterminedObjectForToken(ref CORINFO_RESOLVED_TOKEN pRe
object result = GetRuntimeDeterminedObjectForToken(methodIL, typeOrMethodContext, pResolvedToken.token);
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 (isAsyncVariant && !((MethodDesc)result).IsAsyncVariant())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this part necessary?

Suggested change
if (isAsyncVariant && !((MethodDesc)result).IsAsyncVariant())
if (isAsyncVariant)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it is needed since the token may represent async variants directly for internal compiler-generated IL (like the thunks), so the result may already be the async variant for those cases. GetAsyncVariant asserts since they aren't task-returning in those cases.
(We won't hit that from the getCallInfo callers, but we can hit it from uses in other functions)

result = _compilation.TypeSystemContext.GetAsyncVariantMethod((MethodDesc)result);

return result;
Expand Down Expand Up @@ -1868,45 +1867,8 @@ 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)
{
// 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.
// return NULL, so that caller would re-resolve as a regular method call
bool allowAsyncVariant = method.GetTypicalMethodDefinition().Signature.ReturnsTaskOrValueTask();

// 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;
}

if (method != null)
{
pResolvedToken.hMethod = ObjectToHandle(method);
pResolvedToken.hClass = ObjectToHandle(method.OwningType);
}
else
{
pResolvedToken.hMethod = null;
pResolvedToken.hClass = null;
}
pResolvedToken.hMethod = ObjectToHandle(method);
pResolvedToken.hClass = ObjectToHandle(method.OwningType);
}
else
if (result is FieldDesc)
Expand Down
Loading
Loading