Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement reabstraction in CoreCLR for Static Virtual Methods #88711

Merged
merged 10 commits into from
Jul 14, 2023
Merged
1 change: 1 addition & 0 deletions src/coreclr/inc/corinfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -633,6 +633,7 @@ enum CorInfoHelpFunc
CORINFO_HELP_THROW_PLATFORM_NOT_SUPPORTED, // throw PlatformNotSupportedException
CORINFO_HELP_THROW_TYPE_NOT_SUPPORTED, // throw TypeNotSupportedException
CORINFO_HELP_THROW_AMBIGUOUS_RESOLUTION_EXCEPTION, // throw AmbiguousResolutionException for failed static virtual method resolution
CORINFO_HELP_THROW_ENTRYPOINT_NOT_FOUND_EXCEPTION, // throw EntryPointNotFoundException for failed static virtual method resolution

CORINFO_HELP_JIT_PINVOKE_BEGIN, // Transition to preemptive mode before a P/Invoke, frame is the first argument
CORINFO_HELP_JIT_PINVOKE_END, // Transition to cooperative mode after a P/Invoke, frame is the first argument
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 @@ -43,11 +43,11 @@ typedef const GUID *LPCGUID;
#define GUID_DEFINED
#endif // !GUID_DEFINED

constexpr GUID JITEEVersionIdentifier = { /* 02e334af-4e6e-4a68-9feb-308d3d2661bc */
0x2e334af,
0x4e6e,
0x4a68,
{0x9f, 0xeb, 0x30, 0x8d, 0x3d, 0x26, 0x61, 0xbc}
constexpr GUID JITEEVersionIdentifier = { /* cef79bc8-29bf-4f7b-9d05-9fc06832098c */
0xcef79bc8,
0x29bf,
0x4f7b,
{0x9d, 0x05, 0x9f, 0xc0, 0x68, 0x32, 0x09, 0x8c}
};

//////////////////////////////////////////////////////////////////////////////////////////////////////////
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/inc/jithelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,7 @@
JITHELPER(CORINFO_HELP_THROW_PLATFORM_NOT_SUPPORTED, JIT_ThrowPlatformNotSupportedException, CORINFO_HELP_SIG_REG_ONLY)
JITHELPER(CORINFO_HELP_THROW_TYPE_NOT_SUPPORTED, JIT_ThrowTypeNotSupportedException, CORINFO_HELP_SIG_REG_ONLY)
JITHELPER(CORINFO_HELP_THROW_AMBIGUOUS_RESOLUTION_EXCEPTION, JIT_ThrowAmbiguousResolutionException, CORINFO_HELP_SIG_REG_ONLY)
JITHELPER(CORINFO_HELP_THROW_ENTRYPOINT_NOT_FOUND_EXCEPTION, JIT_ThrowEntryPointNotFoundException, CORINFO_HELP_SIG_REG_ONLY)

JITHELPER(CORINFO_HELP_JIT_PINVOKE_BEGIN, JIT_PInvokeBegin, CORINFO_HELP_SIG_REG_ONLY)
JITHELPER(CORINFO_HELP_JIT_PINVOKE_END, JIT_PInvokeEnd, CORINFO_HELP_SIG_REG_ONLY)
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,7 @@ which is the right helper to use to allocate an object of a given type. */
CORINFO_HELP_THROW_PLATFORM_NOT_SUPPORTED, // throw PlatformNotSupportedException
CORINFO_HELP_THROW_TYPE_NOT_SUPPORTED, // throw TypeNotSupportedException
CORINFO_HELP_THROW_AMBIGUOUS_RESOLUTION_EXCEPTION, // throw AmbiguousResolutionException for failed static virtual method resolution
CORINFO_HELP_THROW_ENTRYPOINT_NOT_FOUND_EXCEPTION, // throw EntryPointNotFoundException for failed static virtual method resolution

CORINFO_HELP_JIT_PINVOKE_BEGIN, // Transition to preemptive mode before a P/Invoke, frame is the first argument
CORINFO_HELP_JIT_PINVOKE_END, // Transition to cooperative mode after a P/Invoke, frame is the first argument
Expand Down
3 changes: 3 additions & 0 deletions src/coreclr/vm/dllimport.h
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ enum ILStubTypes
ILSTUB_WRAPPERDELEGATE_INVOKE = 0x80000007,
ILSTUB_TAILCALL_STOREARGS = 0x80000008,
ILSTUB_TAILCALL_CALLTARGET = 0x80000009,
ILSTUB_STATIC_VIRTUAL_DISPATCH_STUB = 0x8000000A,
};

#ifdef FEATURE_COMINTEROP
Expand All @@ -214,6 +215,8 @@ inline bool SF_IsForNumParamBytes (DWORD dwStubFlags) { LIMITED_METHOD_CONT
inline bool SF_IsStructMarshalStub (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return (dwStubFlags < NDIRECTSTUB_FL_INVALID && 0 != (dwStubFlags & NDIRECTSTUB_FL_STRUCT_MARSHAL)); }
inline bool SF_IsCheckPendingException (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return (dwStubFlags < NDIRECTSTUB_FL_INVALID && 0 != (dwStubFlags & NDIRECTSTUB_FL_CHECK_PENDING_EXCEPTION)); }

inline bool SF_IsVirtualStaticMethodDispatchStub(DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return dwStubFlags == ILSTUB_STATIC_VIRTUAL_DISPATCH_STUB; }

#ifdef FEATURE_ARRAYSTUB_AS_IL
inline bool SF_IsArrayOpStub (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return ((dwStubFlags == ILSTUB_ARRAYOP_GET) ||
(dwStubFlags == ILSTUB_ARRAYOP_SET) ||
Expand Down
102 changes: 98 additions & 4 deletions src/coreclr/vm/genericdict.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include "typectxt.h"
#include "virtualcallstub.h"
#include "sigbuilder.h"
#include "dllimport.h"

#ifndef DACCESS_COMPILE

Expand Down Expand Up @@ -599,6 +600,70 @@ Dictionary* Dictionary::GetTypeDictionaryWithSizeCheck(MethodTable* pMT, ULONG s
RETURN pDictionary;
}

struct StaticVirtualDispatchHashBlob : public ILStubHashBlobBase
{
MethodDesc *pExactInterfaceMethod;
MethodTable *pTargetMT;
};

PCODE CreateStubForStaticVirtualDispatch(MethodTable* pTargetMT, MethodTable* pInterfaceMT, MethodDesc *pInterfaceMD)
{
GCX_PREEMP();

Module* pLoaderModule = ClassLoader::ComputeLoaderModule(pTargetMT, 0, pInterfaceMD->GetMethodInstantiation());

MethodDesc *pExactMD = MethodDesc::FindOrCreateAssociatedMethodDesc(
pInterfaceMD,
pInterfaceMT,
FALSE, // forceBoxedEntryPoint
pInterfaceMD->GetMethodInstantiation(), // methodInst
FALSE, // allowInstParam
TRUE); // forceRemotableMethod

StaticVirtualDispatchHashBlob hashBlob;
memset(&hashBlob, 0, sizeof(hashBlob));
hashBlob.pExactInterfaceMethod = pExactMD;
hashBlob.pTargetMT = pTargetMT;
hashBlob.m_cbSizeOfBlob = sizeof(hashBlob);
ILStubHashBlob *pHashBlob = (ILStubHashBlob*)&hashBlob;

MethodDesc *pStubMD = pLoaderModule->GetILStubCache()->LookupStubMethodDesc(pHashBlob);
if (pStubMD == NULL)
{
SigTypeContext context(pExactMD);
ILStubLinker sl(pExactMD->GetModule(), pExactMD->GetSignature(), &context, pExactMD, ILSTUB_LINKER_FLAG_NONE);
MetaSig sig(pInterfaceMD);

ILCodeStream *pCode = sl.NewCodeStream(ILStubLinker::kDispatch);

UINT paramCount = 0;
BOOL fReturnVal = !sig.IsReturnTypeVoid();
while(paramCount < sig.NumFixedArgs())
pCode->EmitLDARG(paramCount++);

pCode->EmitCONSTRAINED(pCode->GetToken(pTargetMT));
pCode->EmitCALL(pCode->GetToken(pInterfaceMD), sig.NumFixedArgs(), fReturnVal);
pCode->EmitRET();

PCCOR_SIGNATURE pSig;
DWORD cbSig;

pInterfaceMD->GetSig(&pSig,&cbSig);

pStubMD = ILStubCache::CreateAndLinkNewILStubMethodDesc(pLoaderModule->GetLoaderAllocator(),
pLoaderModule->GetILStubCache()->GetOrCreateStubMethodTable(pLoaderModule),
ILSTUB_STATIC_VIRTUAL_DISPATCH_STUB,
pInterfaceMD->GetModule(),
pSig, cbSig,
&context,
&sl);

pStubMD = pLoaderModule->GetILStubCache()->InsertStubMethodDesc(pStubMD, pHashBlob);
}

return JitILStub(pStubMD);
}

//---------------------------------------------------------------------------------------
//
DictionaryEntry
Expand Down Expand Up @@ -1068,11 +1133,40 @@ Dictionary::PopulateEntry(
}
_ASSERTE(!constraintType.IsNull());

MethodDesc *pResolvedMD = constraintType.GetMethodTable()->TryResolveConstraintMethodApprox(ownerType, pMethod);
MethodDesc *pResolvedMD;

// All such calls should be resolvable. If not then for now just throw an error.
_ASSERTE(pResolvedMD);
INDEBUG(if (!pResolvedMD) constraintType.GetMethodTable()->TryResolveConstraintMethodApprox(ownerType, pMethod);)
if (pMethod->IsStatic())
{
// Virtual Static Method resolution
_ASSERTE(!ownerType.IsTypeDesc());
_ASSERTE(ownerType.IsInterface());
BOOL uniqueResolution;
pResolvedMD = constraintType.GetMethodTable()->ResolveVirtualStaticMethod(
ownerType.GetMethodTable(),
pMethod,
/* allowNullResult */ TRUE,
/* verifyImplemented */ FALSE,
/* allowVariantMatches */ TRUE,
&uniqueResolution);

// If we couldn't get an exact result, fall back to using a stub to make the exact function call
// This will trigger the logic in the JIT which can handle AmbiguousImplementationException and
// EntryPointNotFoundException at exactly the right time
if (!uniqueResolution || pResolvedMD == NULL || pResolvedMD->IsAbstract())
{
_ASSERTE(pResolvedMD == NULL || pResolvedMD->IsStatic());
result = (CORINFO_GENERIC_HANDLE)CreateStubForStaticVirtualDispatch(constraintType.GetMethodTable(), ownerType.GetMethodTable(), pMethod);
break;
}
}
else
{
pResolvedMD = constraintType.GetMethodTable()->TryResolveConstraintMethodApprox(ownerType, pMethod);

// All such calls should be resolvable. If not then for now just throw an error.
_ASSERTE(pResolvedMD);
INDEBUG(if (!pResolvedMD) constraintType.GetMethodTable()->TryResolveConstraintMethodApprox(ownerType, pMethod);)
}
if (!pResolvedMD)
COMPlusThrowHR(COR_E_BADIMAGEFORMAT);

Expand Down
45 changes: 45 additions & 0 deletions src/coreclr/vm/ilstubcache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ namespace
case DynamicMethodDesc::StubWrapperDelegate: return "IL_STUB_WrapperDelegate_Invoke";
case DynamicMethodDesc::StubTailCallStoreArgs: return "IL_STUB_StoreTailCallArgs";
case DynamicMethodDesc::StubTailCallCallTarget: return "IL_STUB_CallTailCallTarget";
case DynamicMethodDesc::StubVirtualStaticMethodDispatch: return "IL_STUB_bVirtualStaticMethodDispatch";
Copy link
Member

Choose a reason for hiding this comment

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

Nit - is the lowercase 'b' after IL_STUB_ intentional or just a typo?

default:
UNREACHABLE_MSG("Unknown stub type");
}
Expand Down Expand Up @@ -319,6 +320,11 @@ MethodDesc* ILStubCache::CreateNewMethodDesc(LoaderHeap* pCreationHeap, MethodTa
}
}

if (SF_IsVirtualStaticMethodDispatchStub(dwStubFlags))
{
pMD->SetILStubType(DynamicMethodDesc::StubVirtualStaticMethodDispatch);
}

// if we made it this far, we can set a more descriptive stub name
#ifdef FEATURE_ARRAYSTUB_AS_IL
if (SF_IsArrayOpStub(dwStubFlags))
Expand Down Expand Up @@ -397,6 +403,45 @@ MethodTable* ILStubCache::GetOrCreateStubMethodTable(Module* pModule)
RETURN m_pStubMT;
}


MethodDesc* ILStubCache::LookupStubMethodDesc(ILStubHashBlob* pHashBlob)
{
CrstHolder ch(&m_crst);

// Try to find the stub
const ILStubCacheEntry* phe = m_hashMap.LookupPtr(pHashBlob);
if (phe)
{
return phe->m_pMethodDesc;
}

return NULL;
}

MethodDesc* ILStubCache::InsertStubMethodDesc(MethodDesc *pMD, ILStubHashBlob* pHashBlob)
{
size_t cbSizeOfBlob = pHashBlob->m_cbSizeOfBlob;

CrstHolder ch(&m_crst);

const ILStubCacheEntry* phe = m_hashMap.LookupPtr(pHashBlob);
if (phe == NULL)
{
AllocMemHolder<ILStubHashBlob> pBlobHolder( m_heap->AllocMem(S_SIZE_T(cbSizeOfBlob)) );
ILStubHashBlob* pBlob = pBlobHolder;
_ASSERTE(pHashBlob->m_cbSizeOfBlob == cbSizeOfBlob);
memcpy(pBlob, pHashBlob, cbSizeOfBlob);

m_hashMap.Add(ILStubCacheEntry{ pMD, pBlob });
pBlobHolder.SuppressRelease();

return pMD;
}
else
{
return phe->m_pMethodDesc;
}
}
#endif // DACCESS_COMPILE

//
Expand Down
6 changes: 6 additions & 0 deletions src/coreclr/vm/ilstubcache.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@ class ILStubCache final

MethodTable* GetOrCreateStubMethodTable(Module* pLoaderModule);

MethodDesc* LookupStubMethodDesc(ILStubHashBlob* pHashBlob);

// Insert a stub MethodDesc into the cache
// If one is already present at a matching hash blob, return the already present one, otherwise, return pMD
MethodDesc* InsertStubMethodDesc(MethodDesc* pMD, ILStubHashBlob* pHashBlob);

private: // static
static MethodDesc* CreateNewMethodDesc(
LoaderHeap* pCreationHeap,
Expand Down
32 changes: 32 additions & 0 deletions src/coreclr/vm/jithelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4428,6 +4428,38 @@ HCIMPL3(void, JIT_ThrowAmbiguousResolutionException,
}
HCIMPLEND

/*********************************************************************/
HCIMPL3(void, JIT_ThrowEntryPointNotFoundException,
MethodDesc *method,
MethodTable *interfaceType,
MethodTable *targetType)
{
FCALL_CONTRACT;

HELPER_METHOD_FRAME_BEGIN_0(); // Set up a frame

SString strMethodName;
SString strInterfaceName;
SString strTargetClassName;
SString assemblyName;

targetType->GetAssembly()->GetDisplayName(assemblyName);
TypeString::AppendMethod(strMethodName, method, method->GetMethodInstantiation());
TypeString::AppendType(strInterfaceName, TypeHandle(interfaceType));
TypeString::AppendType(strTargetClassName, targetType);

COMPlusThrow(
kEntryPointNotFoundException,
IDS_CLASSLOAD_METHOD_NOT_IMPLEMENTED,
strMethodName,
strInterfaceName,
strTargetClassName,
assemblyName);

HELPER_METHOD_FRAME_END(); // Set up a frame
}
HCIMPLEND

/*********************************************************************/
HCIMPL0(void, JIT_Overflow)
{
Expand Down
30 changes: 24 additions & 6 deletions src/coreclr/vm/jitinterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5068,6 +5068,7 @@ void CEEInfo::getCallInfo(

BOOL fResolvedConstraint = FALSE;
BOOL fForceUseRuntimeLookup = FALSE;
BOOL fAbstractSVM = FALSE;

MethodDesc * pMDAfterConstraintResolution = pMD;
if (constrainedType.IsNull())
Expand Down Expand Up @@ -5148,11 +5149,21 @@ void CEEInfo::getCallInfo(
#ifdef FEATURE_DEFAULT_INTERFACES
else if (directMethod && pMD->IsStatic())
{
// Default interface implementation of static virtual method
pMDAfterConstraintResolution = directMethod;
fResolvedConstraint = TRUE;
pResult->thisTransform = CORINFO_NO_THIS_TRANSFORM;
exactType = directMethod->GetMethodTable();
if (directMethod->IsAbstract())
{
// This is the result when we call a SVM which is abstract, or re-abstracted
directMethod = NULL;
pResult->thisTransform = CORINFO_NO_THIS_TRANSFORM;
fAbstractSVM = true;
}
else
{
// Default interface implementation of static virtual method
pMDAfterConstraintResolution = directMethod;
fResolvedConstraint = TRUE;
pResult->thisTransform = CORINFO_NO_THIS_TRANSFORM;
exactType = directMethod->GetMethodTable();
}
}
#endif
else if (constrainedType.IsValueType())
Expand Down Expand Up @@ -5649,7 +5660,14 @@ void CEEInfo::getCallInfo(
// shared generics is covered by the ConstrainedMethodEntrySlot dictionary entry.
pResult->kind = CORINFO_CALL;
pResult->accessAllowed = CORINFO_ACCESS_ILLEGAL;
pResult->callsiteCalloutHelper.helperNum = CORINFO_HELP_THROW_AMBIGUOUS_RESOLUTION_EXCEPTION;
if (fAbstractSVM)
{
pResult->callsiteCalloutHelper.helperNum = CORINFO_HELP_THROW_ENTRYPOINT_NOT_FOUND_EXCEPTION;
}
else
{
pResult->callsiteCalloutHelper.helperNum = CORINFO_HELP_THROW_AMBIGUOUS_RESOLUTION_EXCEPTION;
}
pResult->callsiteCalloutHelper.numArgs = 3;
pResult->callsiteCalloutHelper.args[0].methodHandle = (CORINFO_METHOD_HANDLE)pMD;
pResult->callsiteCalloutHelper.args[0].argType = CORINFO_HELPER_ARG_TYPE_Method;
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/vm/method.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2458,6 +2458,8 @@ class DynamicMethodDesc : public StoredSigMethodDesc
StubTailCallStoreArgs,
StubTailCallCallTarget,

StubVirtualStaticMethodDispatch,

StubLast
};

Expand Down
5 changes: 5 additions & 0 deletions src/coreclr/vm/stubgen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1237,6 +1237,11 @@ void ILCodeStream::EmitCLT_UN()
WRAPPER_NO_CONTRACT;
Emit(CEE_CLT_UN, -1, 0);
}
void ILCodeStream::EmitCONSTRAINED(int token)
{
WRAPPER_NO_CONTRACT;
Emit(CEE_CONSTRAINED, 0, token);
}
void ILCodeStream::EmitCONV_I()
{
WRAPPER_NO_CONTRACT;
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/vm/stubgen.h
Original file line number Diff line number Diff line change
Expand Up @@ -720,6 +720,7 @@ class ILCodeStream
void EmitCGT_UN ();
void EmitCLT ();
void EmitCLT_UN ();
void EmitCONSTRAINED(int token);
void EmitCONV_I ();
void EmitCONV_I1 ();
void EmitCONV_I2 ();
Expand Down
Loading
Loading