Skip to content

Commit

Permalink
JIT: enable devirtualization/inlining of other array interface methods (
Browse files Browse the repository at this point in the history
#109209)

The JIT recently enabled devirtualization of `GetEnumerator`, but for some cases
were inhibited from devirtualization because the runtime was returning an
instantiating stub instead of the actual method. This blocked inlining and
the JIT currently will not GDV unless it can also inline.

So for instance `ICollection<T>.Count` would not devirtualize.

We think we know enough to pass the right inst parameter (the exact method
desc) so enable this for the array case, at least for normal jitting.

For NAOT array devirtualization happens via normal paths thanks to `Array<T>` so
should already fpr these cases. For R2R we don't do array interface devirt (yet).

There was an existing field on `CORINFO_DEVIRTUALIZATION_INFO` to record the
need for an inst parameter, but it was unused and so I renamed it and use it
for this case.

Contributes to #108913.
  • Loading branch information
AndyAyersMS authored Oct 28, 2024
1 parent f0a8fa4 commit 87fea60
Show file tree
Hide file tree
Showing 23 changed files with 688 additions and 218 deletions.
19 changes: 17 additions & 2 deletions src/coreclr/inc/corinfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -1511,7 +1511,7 @@ struct CORINFO_DEVIRTUALIZATION_INFO
// - details on the computation done by the jit host
// - If pResolvedTokenDevirtualizedMethod is not set to NULL and targeting an R2R image
// use it as the parameter to getCallInfo
// - requiresInstMethodTableArg is set to TRUE if the devirtualized method requires a type handle arg.
// - isInstantiatingStub is set to TRUE if the devirtualized method is a generic method instantiating stub
// - wasArrayInterfaceDevirt is set TRUE for array interface method devirtualization
// (in which case the method handle and context will be a generic method)
//
Expand All @@ -1520,7 +1520,7 @@ struct CORINFO_DEVIRTUALIZATION_INFO
CORINFO_DEVIRTUALIZATION_DETAIL detail;
CORINFO_RESOLVED_TOKEN resolvedTokenDevirtualizedMethod;
CORINFO_RESOLVED_TOKEN resolvedTokenDevirtualizedUnboxedMethod;
bool requiresInstMethodTableArg;
bool isInstantiatingStub;
bool wasArrayInterfaceDevirt;
};

Expand Down Expand Up @@ -2111,6 +2111,14 @@ class ICorStaticInfo
bool* requiresInstMethodTableArg
) = 0;

// Get the wrapped entry point for an instantiating stub, if possible.
// Sets methodArg for method instantiations, classArg for class instantiations.
virtual CORINFO_METHOD_HANDLE getInstantiatedEntry(
CORINFO_METHOD_HANDLE ftn,
CORINFO_METHOD_HANDLE* methodArg,
CORINFO_CLASS_HANDLE* classArg
) = 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 Expand Up @@ -2292,6 +2300,13 @@ class ICorStaticInfo
unsigned index
) = 0;

// Return the type argument of the instantiated generic method,
// which is specified by the index
virtual CORINFO_CLASS_HANDLE getMethodInstantiationArgument(
CORINFO_METHOD_HANDLE ftn,
unsigned index
) = 0;

// Prints the name for a specified class including namespaces and enclosing
// classes.
// See printObjectDescription for documentation for the parameters.
Expand Down
9 changes: 9 additions & 0 deletions src/coreclr/inc/icorjitinfoimpl_generated.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,11 @@ CORINFO_METHOD_HANDLE getUnboxedEntry(
CORINFO_METHOD_HANDLE ftn,
bool* requiresInstMethodTableArg) override;

CORINFO_METHOD_HANDLE getInstantiatedEntry(
CORINFO_METHOD_HANDLE ftn,
CORINFO_METHOD_HANDLE* methodArg,
CORINFO_CLASS_HANDLE* classArg) override;

CORINFO_CLASS_HANDLE getDefaultComparerClass(
CORINFO_CLASS_HANDLE elemType) override;

Expand Down Expand Up @@ -184,6 +189,10 @@ CORINFO_CLASS_HANDLE getTypeInstantiationArgument(
CORINFO_CLASS_HANDLE cls,
unsigned index) override;

CORINFO_CLASS_HANDLE getMethodInstantiationArgument(
CORINFO_METHOD_HANDLE ftn,
unsigned index) override;

size_t printClassName(
CORINFO_CLASS_HANDLE cls,
char* buffer,
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 = { /* ac04f79d-8d06-4a15-9692-1b4f59265825 */
0xac04f79d,
0x8d06,
0x4a15,
{0x96, 0x92, 0x1b, 0x4f, 0x59, 0x26, 0x58, 0x25}
constexpr GUID JITEEVersionIdentifier = { /* d63b852f-8a9b-4306-89ff-7fe135eaaa5d */
0xd63b852f,
0x8a9b,
0x4306,
{0x89, 0xff, 0x7f, 0xe1, 0x35, 0xea, 0xaa, 0x5d}
};

//////////////////////////////////////////////////////////////////////////////////////////////////////////
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/jit/ICorJitInfo_names_generated.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ DEF_CLR_API(getMethodClass)
DEF_CLR_API(getMethodVTableOffset)
DEF_CLR_API(resolveVirtualMethod)
DEF_CLR_API(getUnboxedEntry)
DEF_CLR_API(getInstantiatedEntry)
DEF_CLR_API(getDefaultComparerClass)
DEF_CLR_API(getDefaultEqualityComparerClass)
DEF_CLR_API(getSZArrayHelperEnumeratorClass)
Expand All @@ -43,6 +44,7 @@ DEF_CLR_API(printObjectDescription)
DEF_CLR_API(asCorInfoType)
DEF_CLR_API(getClassNameFromMetadata)
DEF_CLR_API(getTypeInstantiationArgument)
DEF_CLR_API(getMethodInstantiationArgument)
DEF_CLR_API(printClassName)
DEF_CLR_API(isValueClass)
DEF_CLR_API(getClassAttribs)
Expand Down
21 changes: 21 additions & 0 deletions src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,17 @@ CORINFO_METHOD_HANDLE WrapICorJitInfo::getUnboxedEntry(
return temp;
}

CORINFO_METHOD_HANDLE WrapICorJitInfo::getInstantiatedEntry(
CORINFO_METHOD_HANDLE ftn,
CORINFO_METHOD_HANDLE* methodArg,
CORINFO_CLASS_HANDLE* classArg)
{
API_ENTER(getInstantiatedEntry);
CORINFO_METHOD_HANDLE temp = wrapHnd->getInstantiatedEntry(ftn, methodArg, classArg);
API_LEAVE(getInstantiatedEntry);
return temp;
}

CORINFO_CLASS_HANDLE WrapICorJitInfo::getDefaultComparerClass(
CORINFO_CLASS_HANDLE elemType)
{
Expand Down Expand Up @@ -395,6 +406,16 @@ CORINFO_CLASS_HANDLE WrapICorJitInfo::getTypeInstantiationArgument(
return temp;
}

CORINFO_CLASS_HANDLE WrapICorJitInfo::getMethodInstantiationArgument(
CORINFO_METHOD_HANDLE ftn,
unsigned index)
{
API_ENTER(getMethodInstantiationArgument);
CORINFO_CLASS_HANDLE temp = wrapHnd->getMethodInstantiationArgument(ftn, index);
API_LEAVE(getMethodInstantiationArgument);
return temp;
}

size_t WrapICorJitInfo::printClassName(
CORINFO_CLASS_HANDLE cls,
char* buffer,
Expand Down
8 changes: 8 additions & 0 deletions src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -3742,6 +3742,8 @@ class Compiler
CORINFO_CLASS_HANDLE gtGetArrayElementClassHandle(GenTree* array);
// Get a class handle from a helper call argument
CORINFO_CLASS_HANDLE gtGetHelperArgClassHandle(GenTree* array);
// Get a method handle from a helper call argument
CORINFO_METHOD_HANDLE gtGetHelperArgMethodHandle(GenTree* array);
// Get the class handle for a field
CORINFO_CLASS_HANDLE gtGetFieldClassHandle(CORINFO_FIELD_HANDLE fieldHnd, bool* pIsExact, bool* pIsNonNull);
// Check if this tree is a typeof()
Expand Down Expand Up @@ -7565,6 +7567,7 @@ class Compiler
unsigned classAttr,
unsigned likelihood,
bool arrayInterface,
bool instantiatingStub,
CORINFO_CONTEXT_HANDLE originalContextHandle);

int getGDVMaxTypeChecks()
Expand Down Expand Up @@ -8991,6 +8994,11 @@ class Compiler
return info.compCompHnd->getTypeInstantiationArgument(cls, index);
}

CORINFO_CLASS_HANDLE getMethodInstantiationArgument(CORINFO_METHOD_HANDLE ftn, unsigned index)
{
return info.compCompHnd->getMethodInstantiationArgument(ftn, index);
}

bool isNumericsNamespace(const char* ns)
{
return strcmp(ns, "System.Numerics") == 0;
Expand Down
46 changes: 46 additions & 0 deletions src/coreclr/jit/gentree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14238,6 +14238,52 @@ CORINFO_CLASS_HANDLE Compiler::gtGetHelperArgClassHandle(GenTree* tree)
return result;
}

//------------------------------------------------------------------------
// gtGetHelperArgMethodHandle: find the compile time method handle from
// a helper call argument tree
//
// Arguments:
// tree - tree that passes the handle to the helper
//
// Returns:
// The compile time method handle, if known.
//
CORINFO_METHOD_HANDLE Compiler::gtGetHelperArgMethodHandle(GenTree* tree)
{
CORINFO_METHOD_HANDLE result = NO_METHOD_HANDLE;

// The handle could be a literal constant
if ((tree->OperGet() == GT_CNS_INT) && (tree->TypeGet() == TYP_I_IMPL))
{
assert(tree->IsIconHandle(GTF_ICON_METHOD_HDL));
result = (CORINFO_METHOD_HANDLE)tree->AsIntCon()->gtCompileTimeHandle;
}
// Or the result of a runtime lookup
else if (tree->OperGet() == GT_RUNTIMELOOKUP)
{
result = tree->AsRuntimeLookup()->GetMethodHandle();
}
// Or something reached indirectly
else if (tree->gtOper == GT_IND)
{
// The handle indirs we are looking for will be marked as non-faulting.
// Certain others (eg from refanytype) may not be.
if (tree->gtFlags & GTF_IND_NONFAULTING)
{
GenTree* handleTreeInternal = tree->AsOp()->gtOp1;

if ((handleTreeInternal->OperGet() == GT_CNS_INT) && (handleTreeInternal->TypeGet() == TYP_I_IMPL))
{
// These handle constants should be method handles.
assert(handleTreeInternal->IsIconHandle(GTF_ICON_METHOD_HDL));
result = (CORINFO_METHOD_HANDLE)handleTreeInternal->AsIntCon()->gtCompileTimeHandle;
}
}
}

return result;
}

//------------------------------------------------------------------------
// gtFoldExprSpecial -- optimize binary ops with one constant operand
//
Expand Down
Loading

0 comments on commit 87fea60

Please sign in to comment.