Skip to content

Commit

Permalink
Enable inlining of shared generics code within same type (#38229)
Browse files Browse the repository at this point in the history
This change allows inlining of generic dictionary lookups when the caller and callee are the same exact type and instantiation.

Example:

```
class G<T>
{
    static bool M1() => typeof(T);

    object M2() => new T[1];

    bool M3() => Unsafe.SizeOf<T>();

    static string M()
    {
         // Assume that T is string
         // All M1, M2, M3 are inlined after this change, but were not inlined before this change
         M1(); M2(); M3();

         // Still not inlined - different instantiation, not the same exact type
         G<List<T>>.M1();

         // Still not inlined - different type.
         OtherG<T>.M();
    }
}
```
  • Loading branch information
jkotas authored Jun 27, 2020
1 parent 5e153a2 commit 31699de
Show file tree
Hide file tree
Showing 32 changed files with 292 additions and 308 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -393,8 +393,7 @@ CorInfoInitClassResult initClass(CORINFO_FIELD_HANDLE field, // Non-NULL - inqui
// field access NULL - inquire about cctor trigger in
// method prolog
CORINFO_METHOD_HANDLE method, // Method referencing the field or prolog
CORINFO_CONTEXT_HANDLE context, // Exact context of method
BOOL speculative = FALSE // TRUE means don't actually run it
CORINFO_CONTEXT_HANDLE context // Exact context of method
);

// This used to be called "loadClass". This records the fact
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -908,7 +908,6 @@ void MethodContext::repGetBoundaries(CORINFO_METHOD_HANDLE ftn,
void MethodContext::recInitClass(CORINFO_FIELD_HANDLE field,
CORINFO_METHOD_HANDLE method,
CORINFO_CONTEXT_HANDLE context,
BOOL speculative,
CorInfoInitClassResult result)
{
if (InitClass == nullptr)
Expand All @@ -920,20 +919,18 @@ void MethodContext::recInitClass(CORINFO_FIELD_HANDLE field,
key.field = (DWORDLONG)field;
key.method = (DWORDLONG)method;
key.context = (DWORDLONG)context;
key.speculative = (DWORD)speculative;

InitClass->Add(key, (DWORD)result);
DEBUG_REC(dmpInitClass(key, (DWORD)result));
}
void MethodContext::dmpInitClass(const Agnostic_InitClass& key, DWORD value)
{
printf("InitClass key fld-%016llX meth-%016llX con-%016llX spec-%u, value res-%u", key.field, key.method,
key.context, key.speculative, value);
printf("InitClass key fld-%016llX meth-%016llX con-%016llX, value res-%u", key.field, key.method,
key.context, value);
}
CorInfoInitClassResult MethodContext::repInitClass(CORINFO_FIELD_HANDLE field,
CORINFO_METHOD_HANDLE method,
CORINFO_CONTEXT_HANDLE context,
BOOL speculative)
CORINFO_CONTEXT_HANDLE context)
{
Agnostic_InitClass key;
ZeroMemory(&key, sizeof(Agnostic_InitClass)); // We use the input structs as a key and use memcmp to compare.. so we
Expand All @@ -942,7 +939,6 @@ CorInfoInitClassResult MethodContext::repInitClass(CORINFO_FIELD_HANDLE field,
key.field = (DWORDLONG)field;
key.method = (DWORDLONG)method;
key.context = (DWORDLONG)context;
key.speculative = (DWORD)speculative;

AssertCodeMsg(InitClass != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %016llX", (DWORDLONG)key.method);
AssertCodeMsg(InitClass->GetIndex(key) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)key.method);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ class MethodContext
DWORDLONG field;
DWORDLONG method;
DWORDLONG context;
DWORD speculative;
};
struct DLDL
{
Expand Down Expand Up @@ -631,13 +630,11 @@ class MethodContext
void recInitClass(CORINFO_FIELD_HANDLE field,
CORINFO_METHOD_HANDLE method,
CORINFO_CONTEXT_HANDLE context,
BOOL speculative,
CorInfoInitClassResult result);
void dmpInitClass(const Agnostic_InitClass& key, DWORD value);
CorInfoInitClassResult repInitClass(CORINFO_FIELD_HANDLE field,
CORINFO_METHOD_HANDLE method,
CORINFO_CONTEXT_HANDLE context,
BOOL speculative);
CORINFO_CONTEXT_HANDLE context);

void recGetMethodName(CORINFO_METHOD_HANDLE ftn, char* methodname, const char** moduleName);
void dmpGetMethodName(DLD key, DD value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -840,13 +840,12 @@ CorInfoInitClassResult interceptor_ICJI::initClass(
CORINFO_FIELD_HANDLE field, // Non-nullptr - inquire about cctor trigger before static field access
// nullptr - inquire about cctor trigger in method prolog
CORINFO_METHOD_HANDLE method, // Method referencing the field or prolog
CORINFO_CONTEXT_HANDLE context, // Exact context of method
BOOL speculative // TRUE means don't actually run it
CORINFO_CONTEXT_HANDLE context // Exact context of method
)
{
mc->cr->AddCall("initClass");
CorInfoInitClassResult temp = original_ICorJitInfo->initClass(field, method, context, speculative);
mc->recInitClass(field, method, context, speculative, temp);
CorInfoInitClassResult temp = original_ICorJitInfo->initClass(field, method, context);
mc->recInitClass(field, method, context, temp);
return temp;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -653,12 +653,11 @@ CorInfoInitClassResult interceptor_ICJI::initClass(
CORINFO_FIELD_HANDLE field, // Non-nullptr - inquire about cctor trigger before static field access
// nullptr - inquire about cctor trigger in method prolog
CORINFO_METHOD_HANDLE method, // Method referencing the field or prolog
CORINFO_CONTEXT_HANDLE context, // Exact context of method
BOOL speculative // TRUE means don't actually run it
CORINFO_CONTEXT_HANDLE context // Exact context of method
)
{
mcs->AddCall("initClass");
return original_ICorJitInfo->initClass(field, method, context, speculative);
return original_ICorJitInfo->initClass(field, method, context);
}

// This used to be called "loadClass". This records the fact
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -577,11 +577,10 @@ CorInfoInitClassResult interceptor_ICJI::initClass(
CORINFO_FIELD_HANDLE field, // Non-nullptr - inquire about cctor trigger before static field access
// nullptr - inquire about cctor trigger in method prolog
CORINFO_METHOD_HANDLE method, // Method referencing the field or prolog
CORINFO_CONTEXT_HANDLE context, // Exact context of method
BOOL speculative // TRUE means don't actually run it
CORINFO_CONTEXT_HANDLE context // Exact context of method
)
{
return original_ICorJitInfo->initClass(field, method, context, speculative);
return original_ICorJitInfo->initClass(field, method, context);
}

// This used to be called "loadClass". This records the fact
Expand Down
5 changes: 2 additions & 3 deletions src/coreclr/src/ToolBox/superpmi/superpmi/icorjitinfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -702,12 +702,11 @@ CorInfoInitClassResult MyICJI::initClass(CORINFO_FIELD_HANDLE field, // Non-null
// static field access nullptr - inquire about
// cctor trigger in method prolog
CORINFO_METHOD_HANDLE method, // Method referencing the field or prolog
CORINFO_CONTEXT_HANDLE context, // Exact context of method
BOOL speculative // TRUE means don't actually run it
CORINFO_CONTEXT_HANDLE context // Exact context of method
)
{
jitInstance->mc->cr->AddCall("initClass");
return jitInstance->mc->repInitClass(field, method, context, speculative);
return jitInstance->mc->repInitClass(field, method, context);
}

// This used to be called "loadClass". This records the fact
Expand Down
27 changes: 13 additions & 14 deletions src/coreclr/src/inc/corinfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -217,12 +217,12 @@ TODO: Talk about initializing strutures before use
#endif
#endif

SELECTANY const GUID JITEEVersionIdentifier = { /* 2ca8d539-5db9-4831-8f1b-ade425f036bd */
0x2ca8d539,
0x5db9,
0x4831,
{0x8f, 0x1b, 0xad, 0xe4, 0x25, 0xf0, 0x36, 0xbd}
};
SELECTANY const GUID JITEEVersionIdentifier = { /* 7af97117-55be-4c76-afb2-e26261cb140e */
0x7af97117,
0x55be,
0x4c76,
{ 0xaf, 0xb2, 0xe2, 0x62, 0x61, 0xcb, 0x14, 0x0e }
};

//////////////////////////////////////////////////////////////////////////////////////////////////////////
//
Expand Down Expand Up @@ -1038,9 +1038,8 @@ enum CorInfoInitClassResult
CORINFO_INITCLASS_NOT_REQUIRED = 0x00, // No class initialization required, but the class is not actually initialized yet
// (e.g. we are guaranteed to run the static constructor in method prolog)
CORINFO_INITCLASS_INITIALIZED = 0x01, // Class initialized
CORINFO_INITCLASS_SPECULATIVE = 0x02, // Class may be initialized speculatively
CORINFO_INITCLASS_USE_HELPER = 0x04, // The JIT must insert class initialization helper call.
CORINFO_INITCLASS_DONT_INLINE = 0x08, // The JIT should not inline the method requesting the class initialization. The class
CORINFO_INITCLASS_USE_HELPER = 0x02, // The JIT must insert class initialization helper call.
CORINFO_INITCLASS_DONT_INLINE = 0x04, // The JIT should not inline the method requesting the class initialization. The class
// initialization requires helper class now, but will not require initialization
// if the method is compiled standalone. Or the method cannot be inlined due to some
// requirement around class initialization such as shared generics.
Expand Down Expand Up @@ -1105,9 +1104,7 @@ typedef struct CORINFO_VarArgInfo * CORINFO_VARARGS_HANDLE;
// Generic tokens are resolved with respect to a context, which is usually the method
// being compiled. The CORINFO_CONTEXT_HANDLE indicates which exact instantiation
// (or the open instantiation) is being referred to.
// CORINFO_CONTEXT_HANDLE is more tightly scoped than CORINFO_MODULE_HANDLE. For cases
// where the exact instantiation does not matter, CORINFO_MODULE_HANDLE is used.
typedef CORINFO_METHOD_HANDLE CORINFO_CONTEXT_HANDLE;
typedef struct CORINFO_CONTEXT_STRUCT_* CORINFO_CONTEXT_HANDLE;

typedef struct CORINFO_DEPENDENCY_STRUCT_
{
Expand All @@ -1124,6 +1121,7 @@ enum CorInfoContextFlags
CORINFO_CONTEXTFLAGS_MASK = 0x01
};

#define METHOD_BEING_COMPILED_CONTEXT() ((CORINFO_CONTEXT_HANDLE)1)
#define MAKE_CLASSCONTEXT(c) (CORINFO_CONTEXT_HANDLE((size_t) (c) | CORINFO_CONTEXTFLAGS_CLASS))
#define MAKE_METHODCONTEXT(m) (CORINFO_CONTEXT_HANDLE((size_t) (m) | CORINFO_CONTEXTFLAGS_METHOD))

Expand Down Expand Up @@ -1254,6 +1252,7 @@ enum CORINFO_RUNTIME_LOOKUP_KIND
CORINFO_LOOKUP_THISOBJ,
CORINFO_LOOKUP_METHODPARAM,
CORINFO_LOOKUP_CLASSPARAM,
CORINFO_LOOKUP_NOT_SUPPORTED, // Returned for attempts to inline dictionary lookups
};

struct CORINFO_LOOKUP_KIND
Expand Down Expand Up @@ -2462,8 +2461,8 @@ class ICorStaticInfo
CORINFO_FIELD_HANDLE field, // Non-NULL - inquire about cctor trigger before static field access
// NULL - inquire about cctor trigger in method prolog
CORINFO_METHOD_HANDLE method, // Method referencing the field or prolog
CORINFO_CONTEXT_HANDLE context, // Exact context of method
BOOL speculative = FALSE // TRUE means don't actually run it
// NULL - method being compiled
CORINFO_CONTEXT_HANDLE context // Exact context of method
) = 0;

// This used to be called "loadClass". This records the fact
Expand Down
5 changes: 2 additions & 3 deletions src/coreclr/src/jit/ICorJitInfo_API_wrapper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -655,11 +655,10 @@ CorInfoInitClassResult WrapICorJitInfo::initClass(
CORINFO_FIELD_HANDLE field,

CORINFO_METHOD_HANDLE method,
CORINFO_CONTEXT_HANDLE context,
BOOL speculative)
CORINFO_CONTEXT_HANDLE context)
{
API_ENTER(initClass);
CorInfoInitClassResult temp = wrapHnd->initClass(field, method, context, speculative);
CorInfoInitClassResult temp = wrapHnd->initClass(field, method, context);
API_LEAVE(initClass);
return temp;
}
Expand Down
4 changes: 2 additions & 2 deletions src/coreclr/src/jit/compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4364,7 +4364,7 @@ void Compiler::compCompile(void** methodCodePtr, ULONG* methodCodeSize, JitFlags

// Insert call to class constructor as the first basic block if
// we were asked to do so.
if (info.compCompHnd->initClass(nullptr /* field */, info.compMethodHnd /* method */,
if (info.compCompHnd->initClass(nullptr /* field */, nullptr /* method */,
impTokenLookupContextHandle /* context */) &
CORINFO_INITCLASS_USE_HELPER)
{
Expand Down Expand Up @@ -5390,7 +5390,7 @@ int Compiler::compCompile(CORINFO_MODULE_HANDLE classPtr,
}
else
{
impTokenLookupContextHandle = MAKE_METHODCONTEXT(info.compMethodHnd);
impTokenLookupContextHandle = METHOD_BEING_COMPILED_CONTEXT();

info.compClassHnd = info.compCompHnd->getMethodClass(info.compMethodHnd);
info.compClassAttr = info.compCompHnd->getClassAttribs(info.compClassHnd);
Expand Down
3 changes: 2 additions & 1 deletion src/coreclr/src/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -3649,7 +3649,7 @@ class Compiler
};

bool impIsPrimitive(CorInfoType type);
bool impILConsumesAddr(const BYTE* codeAddr, CORINFO_METHOD_HANDLE fncHandle, CORINFO_MODULE_HANDLE scpHandle);
bool impILConsumesAddr(const BYTE* codeAddr);

void impResolveToken(const BYTE* addr, CORINFO_RESOLVED_TOKEN* pResolvedToken, CorInfoTokenKind kind);

Expand Down Expand Up @@ -7192,6 +7192,7 @@ class Compiler
var_types eeGetArgType(CORINFO_ARG_LIST_HANDLE list, CORINFO_SIG_INFO* sig);
var_types eeGetArgType(CORINFO_ARG_LIST_HANDLE list, CORINFO_SIG_INFO* sig, bool* isPinned);
CORINFO_CLASS_HANDLE eeGetArgClass(CORINFO_SIG_INFO* sig, CORINFO_ARG_LIST_HANDLE list);
CORINFO_CLASS_HANDLE eeGetClassFromContext(CORINFO_CONTEXT_HANDLE context);
unsigned eeGetArgSize(CORINFO_ARG_LIST_HANDLE list, CORINFO_SIG_INFO* sig);

// VOM info, method sigs
Expand Down
18 changes: 18 additions & 0 deletions src/coreclr/src/jit/ee_il_dll.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,24 @@ inline CORINFO_CLASS_HANDLE Compiler::eeGetArgClass(CORINFO_SIG_INFO* sig, CORIN
return argClass;
}

/*****************************************************************************/
inline CORINFO_CLASS_HANDLE Compiler::eeGetClassFromContext(CORINFO_CONTEXT_HANDLE context)
{
if (context == METHOD_BEING_COMPILED_CONTEXT())
{
return impInlineRoot()->info.compClassHnd;
}

if (((SIZE_T)context & CORINFO_CONTEXTFLAGS_MASK) == CORINFO_CONTEXTFLAGS_CLASS)
{
return CORINFO_CLASS_HANDLE((SIZE_T)context & ~CORINFO_CONTEXTFLAGS_MASK);
}
else
{
return info.compCompHnd->getMethodClass(CORINFO_METHOD_HANDLE((SIZE_T)context & ~CORINFO_CONTEXTFLAGS_MASK));
}
}

/*****************************************************************************
*
* Native Direct Optimizations
Expand Down
31 changes: 4 additions & 27 deletions src/coreclr/src/jit/flowgraph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4886,8 +4886,7 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed
const bool notLastInstr = (codeAddr < codeEndp - sz);
const bool notDebugCode = !opts.compDbgCode;

if (notStruct && notLastInstr && notDebugCode &&
impILConsumesAddr(codeAddr + sz, impTokenLookupContextHandle, info.compScopeHnd))
if (notStruct && notLastInstr && notDebugCode && impILConsumesAddr(codeAddr + sz))
{
// We can skip the addrtaken, as next IL instruction consumes
// the address.
Expand Down Expand Up @@ -23169,19 +23168,6 @@ void Compiler::fgInvokeInlineeCompiler(GenTreeCall* call, InlineResult* inlineRe
return;
}

if (inlineCandidateInfo->initClassResult & CORINFO_INITCLASS_SPECULATIVE)
{
// we defer the call to initClass() until inlining is completed in case it fails. If inlining succeeds,
// we will call initClass().
if (!(info.compCompHnd->initClass(nullptr /* field */, fncHandle /* method */,
inlineCandidateInfo->exactContextHnd /* context */) &
CORINFO_INITCLASS_INITIALIZED))
{
inlineResult->NoteFatal(InlineObservation::CALLEE_CLASS_INIT_FAILURE);
return;
}
}

// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// The inlining attempt cannot be failed starting from this point.
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Expand Down Expand Up @@ -23513,6 +23499,8 @@ void Compiler::fgInsertInlineeBlocks(InlineInfo* pInlineInfo)
compGSReorderStackLayout |= InlineeCompiler->compGSReorderStackLayout;
compHasBackwardJump |= InlineeCompiler->compHasBackwardJump;

lvaGenericsContextInUse |= InlineeCompiler->lvaGenericsContextInUse;

#ifdef FEATURE_SIMD
if (InlineeCompiler->usesSIMDTypes())
{
Expand Down Expand Up @@ -23865,18 +23853,7 @@ Statement* Compiler::fgInlinePrependStatements(InlineInfo* inlineInfo)

if (inlineInfo->inlineCandidateInfo->initClassResult & CORINFO_INITCLASS_USE_HELPER)
{
CORINFO_CONTEXT_HANDLE exactContext = inlineInfo->inlineCandidateInfo->exactContextHnd;
CORINFO_CLASS_HANDLE exactClass;

if (((SIZE_T)exactContext & CORINFO_CONTEXTFLAGS_MASK) == CORINFO_CONTEXTFLAGS_CLASS)
{
exactClass = CORINFO_CLASS_HANDLE((SIZE_T)exactContext & ~CORINFO_CONTEXTFLAGS_MASK);
}
else
{
exactClass = info.compCompHnd->getMethodClass(
CORINFO_METHOD_HANDLE((SIZE_T)exactContext & ~CORINFO_CONTEXTFLAGS_MASK));
}
CORINFO_CLASS_HANDLE exactClass = eeGetClassFromContext(inlineInfo->inlineCandidateInfo->exactContextHnd);

tree = fgGetSharedCCtor(exactClass);
newStmt = gtNewStmt(tree, callILOffset);
Expand Down
13 changes: 1 addition & 12 deletions src/coreclr/src/jit/gentree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17462,18 +17462,7 @@ CORINFO_CLASS_HANDLE Compiler::gtGetClassHandle(GenTree* tree, bool* pIsExact, b

if (context != nullptr)
{
CORINFO_CLASS_HANDLE exactClass = nullptr;

if (((size_t)context & CORINFO_CONTEXTFLAGS_MASK) == CORINFO_CONTEXTFLAGS_CLASS)
{
exactClass = (CORINFO_CLASS_HANDLE)((size_t)context & ~CORINFO_CONTEXTFLAGS_MASK);
}
else
{
CORINFO_METHOD_HANDLE exactMethod =
(CORINFO_METHOD_HANDLE)((size_t)context & ~CORINFO_CONTEXTFLAGS_MASK);
exactClass = info.compCompHnd->getMethodClass(exactMethod);
}
CORINFO_CLASS_HANDLE exactClass = eeGetClassFromContext(context);

// Grab the signature in this context.
CORINFO_SIG_INFO sig;
Expand Down
Loading

0 comments on commit 31699de

Please sign in to comment.