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

Import isinst/castclass Nullable as underlying type #87241

Merged
merged 13 commits into from Jun 21, 2023
2 changes: 1 addition & 1 deletion src/coreclr/inc/corinfo.h
Expand Up @@ -2459,7 +2459,7 @@ class ICorStaticInfo

// returns the optimized "IsInstanceOf" or "ChkCast" helper
virtual CorInfoHelpFunc getCastingHelper(
CORINFO_RESOLVED_TOKEN * pResolvedToken,
CORINFO_CLASS_HANDLE clsHnd,
bool fThrowing
) = 0;

Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/inc/icorjitinfoimpl_generated.h
Expand Up @@ -277,7 +277,7 @@ CorInfoHelpFunc getNewArrHelper(
CORINFO_CLASS_HANDLE arrayCls) override;

CorInfoHelpFunc getCastingHelper(
CORINFO_RESOLVED_TOKEN* pResolvedToken,
CORINFO_CLASS_HANDLE clsHnd,
bool fThrowing) override;

CorInfoHelpFunc getSharedCCtorHelper(
Expand Down
10 changes: 5 additions & 5 deletions src/coreclr/inc/jiteeversionguid.h
Expand Up @@ -43,11 +43,11 @@ typedef const GUID *LPCGUID;
#define GUID_DEFINED
#endif // !GUID_DEFINED

constexpr GUID JITEEVersionIdentifier = { /* fda2f9dd-6b3e-4ecd-a7b8-79e5edf1f072 */
0xfda2f9dd,
0x6b3e,
0x4ecd,
{0xa7, 0xb8, 0x79, 0xe5, 0xed, 0xf1, 0xf0, 0x72}
constexpr GUID JITEEVersionIdentifier = { /* fb0ea359-ee8d-44f0-9418-a9b007d1935d */
0xfb0ea359,
0xee8d,
0x44f0,
{0x94, 0x18, 0xa9, 0xb0, 0x7, 0xd1, 0x93, 0x5d}
};

//////////////////////////////////////////////////////////////////////////////////////////////////////////
Expand Down
4 changes: 2 additions & 2 deletions src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp
Expand Up @@ -631,11 +631,11 @@ CorInfoHelpFunc WrapICorJitInfo::getNewArrHelper(
}

CorInfoHelpFunc WrapICorJitInfo::getCastingHelper(
CORINFO_RESOLVED_TOKEN* pResolvedToken,
CORINFO_CLASS_HANDLE clsHnd,
bool fThrowing)
{
API_ENTER(getCastingHelper);
CorInfoHelpFunc temp = wrapHnd->getCastingHelper(pResolvedToken, fThrowing);
CorInfoHelpFunc temp = wrapHnd->getCastingHelper(clsHnd, fThrowing);
API_LEAVE(getCastingHelper);
return temp;
}
Expand Down
24 changes: 17 additions & 7 deletions src/coreclr/jit/importer.cpp
Expand Up @@ -5422,9 +5422,19 @@ GenTree* Compiler::impCastClassOrIsInstToTree(
{
assert(op1->TypeGet() == TYP_REF);

// Import isinst Nullable<V> as isinst V
Copy link
Member

Choose a reason for hiding this comment

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

If all you want to do is to convert Nullable<T> into T for casing helpers, the best place to do it is resolveToken on the VM side. resolveToken on VM side does similar conversion for NewArr here: https://github.com/dotnet/runtime/blob/main/src/coreclr/vm/jitinterface.cpp#L1170. You can do similar conversion for tokens that come from isinst/castclass.

Copy link
Member

Choose a reason for hiding this comment

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

The conversion has to be also taken into account when embedding the handle (look for other places that check for CORINFO_TOKENKIND_Newarr in the VM). I think your current change is missing this part.

Copy link
Member Author

Choose a reason for hiding this comment

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

There's null check difference for castclass, so the nullable type should be visible to JIT if we want to do optimization for castclass.

Copy link
Member

Choose a reason for hiding this comment

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

There's null check difference for castclass

Could you please elaborate? Both isinst on null and castclass on null return null.

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 misunderstood. NRE for casting to value type is thrown by unbox.

CORINFO_CLASS_HANDLE hClass = info.compCompHnd->getTypeForBox(pResolvedToken->hClass);

if (hClass != pResolvedToken->hClass)
{
// Convert nullable to underlying type
assert(op2->OperIsConst());
op2 = gtNewIconEmbClsHndNode(hClass);
}

// Optimistically assume the jit should expand this as an inline test
bool shouldExpandInline = true;
bool isClassExact = impIsClassExact(pResolvedToken->hClass);
bool isClassExact = impIsClassExact(hClass);

// Profitability check.
//
Expand Down Expand Up @@ -5459,7 +5469,7 @@ GenTree* Compiler::impCastClassOrIsInstToTree(
// Pessimistically assume the jit cannot expand this as an inline test
bool canExpandInline = false;
bool partialExpand = false;
const CorInfoHelpFunc helper = info.compCompHnd->getCastingHelper(pResolvedToken, isCastClass);
const CorInfoHelpFunc helper = info.compCompHnd->getCastingHelper(hClass, isCastClass);

CORINFO_CLASS_HANDLE exactCls = NO_CLASS_HANDLE;

Expand All @@ -5472,7 +5482,7 @@ GenTree* Compiler::impCastClassOrIsInstToTree(
CORINFO_CLASS_HANDLE actualImplCls = NO_CLASS_HANDLE;
if (this->IsTargetAbi(CORINFO_NATIVEAOT_ABI) &&
((helper == CORINFO_HELP_ISINSTANCEOFINTERFACE) || (helper == CORINFO_HELP_CHKCASTINTERFACE)) &&
(info.compCompHnd->getExactClasses(pResolvedToken->hClass, 1, &actualImplCls) == 1) &&
(info.compCompHnd->getExactClasses(hClass, 1, &actualImplCls) == 1) &&
(actualImplCls != NO_CLASS_HANDLE) && impIsClassExact(actualImplCls))
{
// if an interface has a single implementation on NativeAOT where we won't load new types,
Expand All @@ -5490,7 +5500,7 @@ GenTree* Compiler::impCastClassOrIsInstToTree(
exactCls = actualImplCls;

JITDUMP("'%s' interface has a single implementation - '%s', using that to inline isinst/castclass.",
eeGetClassName(pResolvedToken->hClass), eeGetClassName(actualImplCls));
eeGetClassName(hClass), eeGetClassName(actualImplCls));
}
else if (isCastClass)
{
Expand All @@ -5500,7 +5510,7 @@ GenTree* Compiler::impCastClassOrIsInstToTree(
// For ChkCastAny we ignore cases where the class is known to be abstract or is an interface.
if (helper == CORINFO_HELP_CHKCASTANY)
{
const bool isAbstract = (info.compCompHnd->getClassAttribs(pResolvedToken->hClass) &
const bool isAbstract = (info.compCompHnd->getClassAttribs(hClass) &
(CORINFO_FLG_INTERFACE | CORINFO_FLG_ABSTRACT)) != 0;
canExpandInline = !isAbstract;
}
Expand Down Expand Up @@ -5543,7 +5553,7 @@ GenTree* Compiler::impCastClassOrIsInstToTree(
if ((likelyCls != NO_CLASS_HANDLE) &&
(likelyClass.likelihood > (UINT32)JitConfig.JitGuardedDevirtualizationChainLikelihood()))
{
if ((info.compCompHnd->compareTypesForCast(likelyCls, pResolvedToken->hClass) ==
if ((info.compCompHnd->compareTypesForCast(likelyCls, hClass) ==
TypeCompareState::Must))
{
bool isAbstract = (info.compCompHnd->getClassAttribs(likelyCls) &
Expand Down Expand Up @@ -5710,7 +5720,7 @@ GenTree* Compiler::impCastClassOrIsInstToTree(
assert(lclDsc->lvSingleDef == 0);
lclDsc->lvSingleDef = 1;
JITDUMP("Marked V%02u as a single def temp\n", tmp);
lvaSetClass(tmp, pResolvedToken->hClass);
Copy link
Member Author

Choose a reason for hiding this comment

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

Is there any observable effect for changing the return type of the expression?

lvaSetClass(tmp, hClass);
return gtNewLclvNode(tmp, TYP_REF);
}

Expand Down
Expand Up @@ -943,12 +943,12 @@ private static CorInfoHelpFunc _getNewArrHelper(IntPtr thisHandle, IntPtr* ppExc
}

[UnmanagedCallersOnly]
private static CorInfoHelpFunc _getCastingHelper(IntPtr thisHandle, IntPtr* ppException, CORINFO_RESOLVED_TOKEN* pResolvedToken, byte fThrowing)
private static CorInfoHelpFunc _getCastingHelper(IntPtr thisHandle, IntPtr* ppException, CORINFO_CLASS_STRUCT_* clsHnd, byte fThrowing)
{
var _this = GetThis(thisHandle);
try
{
return _this.getCastingHelper(ref *pResolvedToken, fThrowing != 0);
return _this.getCastingHelper(clsHnd, fThrowing != 0);
}
catch (Exception ex)
{
Expand Down Expand Up @@ -2722,7 +2722,7 @@ private static IntPtr GetUnmanagedCallbacks()
callbacks[60] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, byte*, byte, byte>)&_checkMethodModifier;
callbacks[61] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, CORINFO_METHOD_STRUCT_*, bool*, CorInfoHelpFunc>)&_getNewHelper;
callbacks[62] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CorInfoHelpFunc>)&_getNewArrHelper;
callbacks[63] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, byte, CorInfoHelpFunc>)&_getCastingHelper;
callbacks[63] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, byte, CorInfoHelpFunc>)&_getCastingHelper;
callbacks[64] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CorInfoHelpFunc>)&_getSharedCCtorHelper;
callbacks[65] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_CLASS_STRUCT_*>)&_getTypeForBox;
callbacks[66] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CorInfoHelpFunc>)&_getBoxHelper;
Expand Down
Expand Up @@ -222,7 +222,7 @@ FUNCTIONS
bool checkMethodModifier(CORINFO_METHOD_HANDLE hMethod, const char * modifier, bool fOptional)
CorInfoHelpFunc getNewHelper(CORINFO_RESOLVED_TOKEN* pResolvedToken, CORINFO_METHOD_HANDLE callerHandle, bool* pHasSideEffects)
CorInfoHelpFunc getNewArrHelper(CORINFO_CLASS_HANDLE arrayCls)
CorInfoHelpFunc getCastingHelper(CORINFO_RESOLVED_TOKEN* pResolvedToken, bool fThrowing)
CorInfoHelpFunc getCastingHelper(CORINFO_CLASS_HANDLE clsHnd, bool fThrowing)
CorInfoHelpFunc getSharedCCtorHelper(CORINFO_CLASS_HANDLE clsHnd)
CORINFO_CLASS_HANDLE getTypeForBox(CORINFO_CLASS_HANDLE cls)
CorInfoHelpFunc getBoxHelper(CORINFO_CLASS_HANDLE cls)
Expand Down
Expand Up @@ -1433,7 +1433,7 @@ private void PublishEmptyCode()
_methodCodeNode.InitializeColdFrameInfos(Array.Empty<FrameInfo>());
}

private CorInfoHelpFunc getCastingHelper(ref CORINFO_RESOLVED_TOKEN pResolvedToken, bool fThrowing)
private CorInfoHelpFunc getCastingHelper(CORINFO_CLASS_STRUCT_* clsHnd, bool fThrowing)
{
return fThrowing ? CorInfoHelpFunc.CORINFO_HELP_CHKCASTANY : CorInfoHelpFunc.CORINFO_HELP_ISINSTANCEOFANY;
}
Expand Down
Expand Up @@ -1033,9 +1033,9 @@ private ISymbolNode GetGenericLookupHelper(CORINFO_RUNTIME_LOOKUP_KIND runtimeLo
return _compilation.NodeFactory.ReadyToRunHelperFromDictionaryLookup(helperId, helperArgument, MethodBeingCompiled);
}

private CorInfoHelpFunc getCastingHelper(ref CORINFO_RESOLVED_TOKEN pResolvedToken, bool fThrowing)
private CorInfoHelpFunc getCastingHelper(CORINFO_CLASS_STRUCT_* clsHnd, bool fThrowing)
{
TypeDesc type = HandleToObject(pResolvedToken.hClass);
TypeDesc type = HandleToObject(clsHnd);

CorInfoHelpFunc helper;

Expand Down
6 changes: 3 additions & 3 deletions src/coreclr/tools/aot/jitinterface/jitinterface_generated.h
Expand Up @@ -74,7 +74,7 @@ struct JitInterfaceCallbacks
bool (* checkMethodModifier)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_METHOD_HANDLE hMethod, const char* modifier, bool fOptional);
CorInfoHelpFunc (* getNewHelper)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_RESOLVED_TOKEN* pResolvedToken, CORINFO_METHOD_HANDLE callerHandle, bool* pHasSideEffects);
CorInfoHelpFunc (* getNewArrHelper)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE arrayCls);
CorInfoHelpFunc (* getCastingHelper)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_RESOLVED_TOKEN* pResolvedToken, bool fThrowing);
CorInfoHelpFunc (* getCastingHelper)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE clsHnd, bool fThrowing);
CorInfoHelpFunc (* getSharedCCtorHelper)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE clsHnd);
CORINFO_CLASS_HANDLE (* getTypeForBox)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE cls);
CorInfoHelpFunc (* getBoxHelper)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE cls);
Expand Down Expand Up @@ -824,11 +824,11 @@ class JitInterfaceWrapper : public ICorJitInfo
}

virtual CorInfoHelpFunc getCastingHelper(
CORINFO_RESOLVED_TOKEN* pResolvedToken,
CORINFO_CLASS_HANDLE clsHnd,
bool fThrowing)
{
CorInfoExceptionClass* pException = nullptr;
CorInfoHelpFunc temp = _callbacks->getCastingHelper(_thisHandle, &pException, pResolvedToken, fThrowing);
CorInfoHelpFunc temp = _callbacks->getCastingHelper(_thisHandle, &pException, clsHnd, fThrowing);
if (pException != nullptr) throw pException;
return temp;
}
Expand Down
8 changes: 4 additions & 4 deletions src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp
Expand Up @@ -3995,14 +3995,14 @@ CorInfoIsAccessAllowedResult MethodContext::repCanAccessClass(CORINFO_RESOLVED_T
return temp;
}

void MethodContext::recGetCastingHelper(CORINFO_RESOLVED_TOKEN* pResolvedToken, bool fThrowing, CorInfoHelpFunc result)
void MethodContext::recGetCastingHelper(CORINFO_CLASS_HANDLE clsHnd, bool fThrowing, CorInfoHelpFunc result)
{
if (GetCastingHelper == nullptr)
GetCastingHelper = new LightWeightMap<Agnostic_GetCastingHelper, DWORD>();

Agnostic_GetCastingHelper key;
ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
key.hClass = CastHandle(pResolvedToken->hClass);
key.hClass = CastHandle(clsHnd);
key.fThrowing = (DWORD)fThrowing;

DWORD value = (DWORD)result;
Expand All @@ -4013,11 +4013,11 @@ void MethodContext::dmpGetCastingHelper(const Agnostic_GetCastingHelper& key, DW
{
printf("GetCastingHelper key cls-%016" PRIX64 ", thw-%u, value res-%u", key.hClass, key.fThrowing, value);
}
CorInfoHelpFunc MethodContext::repGetCastingHelper(CORINFO_RESOLVED_TOKEN* pResolvedToken, bool fThrowing)
CorInfoHelpFunc MethodContext::repGetCastingHelper(CORINFO_CLASS_HANDLE clsHnd, bool fThrowing)
{
Agnostic_GetCastingHelper key;
ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
key.hClass = CastHandle(pResolvedToken->hClass);
key.hClass = CastHandle(clsHnd);
key.fThrowing = (DWORD)fThrowing;

DWORD value = LookupByKeyOrMiss(GetCastingHelper, key, ": key %016" PRIX64 "", key.hClass);
Expand Down
4 changes: 2 additions & 2 deletions src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h
Expand Up @@ -526,9 +526,9 @@ class MethodContext
CORINFO_METHOD_HANDLE callerHandle,
CORINFO_HELPER_DESC* pAccessHelper);

void recGetCastingHelper(CORINFO_RESOLVED_TOKEN* pResolvedToken, bool fThrowing, CorInfoHelpFunc result);
void recGetCastingHelper(CORINFO_CLASS_HANDLE clsHnd, bool fThrowing, CorInfoHelpFunc result);
void dmpGetCastingHelper(const Agnostic_GetCastingHelper& key, DWORD value);
CorInfoHelpFunc repGetCastingHelper(CORINFO_RESOLVED_TOKEN* pResolvedToken, bool fThrowing);
CorInfoHelpFunc repGetCastingHelper(CORINFO_CLASS_HANDLE clsHnd, bool fThrowing);

void recEmbedModuleHandle(CORINFO_MODULE_HANDLE handle, void** ppIndirection, CORINFO_MODULE_HANDLE result);
void dmpEmbedModuleHandle(DWORDLONG key, DLDL value);
Expand Down
Expand Up @@ -711,11 +711,11 @@ CorInfoHelpFunc interceptor_ICJI::getNewArrHelper(CORINFO_CLASS_HANDLE arrayCls)
}

// returns the optimized "IsInstanceOf" or "ChkCast" helper
CorInfoHelpFunc interceptor_ICJI::getCastingHelper(CORINFO_RESOLVED_TOKEN* pResolvedToken, bool fThrowing)
CorInfoHelpFunc interceptor_ICJI::getCastingHelper(CORINFO_CLASS_HANDLE clsHnd, bool fThrowing)
{
mc->cr->AddCall("getCastingHelper");
CorInfoHelpFunc temp = original_ICorJitInfo->getCastingHelper(pResolvedToken, fThrowing);
mc->recGetCastingHelper(pResolvedToken, fThrowing, temp);
CorInfoHelpFunc temp = original_ICorJitInfo->getCastingHelper(clsHnd, fThrowing);
mc->recGetCastingHelper(clsHnd, fThrowing, temp);
return temp;
}

Expand Down
Expand Up @@ -520,11 +520,11 @@ CorInfoHelpFunc interceptor_ICJI::getNewArrHelper(
}

CorInfoHelpFunc interceptor_ICJI::getCastingHelper(
CORINFO_RESOLVED_TOKEN* pResolvedToken,
CORINFO_CLASS_HANDLE clsHnd,
bool fThrowing)
{
mcs->AddCall("getCastingHelper");
return original_ICorJitInfo->getCastingHelper(pResolvedToken, fThrowing);
return original_ICorJitInfo->getCastingHelper(clsHnd, fThrowing);
}

CorInfoHelpFunc interceptor_ICJI::getSharedCCtorHelper(
Expand Down
Expand Up @@ -457,10 +457,10 @@ CorInfoHelpFunc interceptor_ICJI::getNewArrHelper(
}

CorInfoHelpFunc interceptor_ICJI::getCastingHelper(
CORINFO_RESOLVED_TOKEN* pResolvedToken,
CORINFO_CLASS_HANDLE clsHnd,
bool fThrowing)
{
return original_ICorJitInfo->getCastingHelper(pResolvedToken, fThrowing);
return original_ICorJitInfo->getCastingHelper(clsHnd, fThrowing);
}

CorInfoHelpFunc interceptor_ICJI::getSharedCCtorHelper(
Expand Down
4 changes: 2 additions & 2 deletions src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp
Expand Up @@ -598,10 +598,10 @@ CorInfoHelpFunc MyICJI::getNewArrHelper(CORINFO_CLASS_HANDLE arrayCls)
}

// returns the optimized "IsInstanceOf" or "ChkCast" helper
CorInfoHelpFunc MyICJI::getCastingHelper(CORINFO_RESOLVED_TOKEN* pResolvedToken, bool fThrowing)
CorInfoHelpFunc MyICJI::getCastingHelper(CORINFO_CLASS_HANDLE clsHnd, bool fThrowing)
{
jitInstance->mc->cr->AddCall("getCastingHelper");
return jitInstance->mc->repGetCastingHelper(pResolvedToken, fThrowing);
return jitInstance->mc->repGetCastingHelper(clsHnd, fThrowing);
}

// returns helper to trigger static constructor
Expand Down
6 changes: 3 additions & 3 deletions src/coreclr/vm/jitinterface.cpp
Expand Up @@ -5914,7 +5914,7 @@ CorInfoHelpFunc CEEInfo::getNewArrHelperStatic(TypeHandle clsHnd)
}

/***********************************************************************/
CorInfoHelpFunc CEEInfo::getCastingHelper(CORINFO_RESOLVED_TOKEN * pResolvedToken, bool fThrowing)
CorInfoHelpFunc CEEInfo::getCastingHelper(CORINFO_CLASS_HANDLE clsHnd, bool fThrowing)
{
CONTRACTL {
THROWS;
Expand All @@ -5927,9 +5927,9 @@ CorInfoHelpFunc CEEInfo::getCastingHelper(CORINFO_RESOLVED_TOKEN * pResolvedToke
JIT_TO_EE_TRANSITION();

bool fClassMustBeRestored;
result = getCastingHelperStatic(TypeHandle(pResolvedToken->hClass), fThrowing, &fClassMustBeRestored);
result = getCastingHelperStatic(TypeHandle(clsHnd), fThrowing, &fClassMustBeRestored);
if (fClassMustBeRestored)
classMustBeLoadedBeforeCodeIsRun(pResolvedToken->hClass);
classMustBeLoadedBeforeCodeIsRun(clsHnd);

EE_TO_JIT_TRANSITION();

Expand Down