delegate ctor optimization for CoreRT.
// token comes from CEE_NEWOBJ
CORINFO_TOKENKIND_NewObj = 0x200 | CORINFO_TOKENKIND_Method,
+
+ // token comes from CEE_LDVIRTFTN
+ CORINFO_TOKENKIND_Ldvirtftn = 0x400 | CORINFO_TOKENKIND_Method,
};
struct CORINFO_RESOLVED_TOKEN
// since conversions between them are not verifiable.
#define TI_FLAG_NATIVE_INT 0x00000200
+// This item contains resolved token. It is used for ctor delegate optimization.
+#define TI_FLAG_TOKEN 0x00000400
+
// This item contains the 'this' pointer (used for tracking)
#define TI_FLAG_THIS_PTR 0x00001000
unsigned byref : 1; // used
unsigned byref_readonly : 1; // used
unsigned nativeInt : 1; // used
- unsigned : 2; // unused
+ unsigned token : 1; // used
+ unsigned : 1; // unused
unsigned thisPtr : 1; // used
unsigned thisPermHome : 1; // used
unsigned generic_type_var : 1; // used
union {
CORINFO_CLASS_HANDLE m_cls;
- // Valid only for type TI_METHOD
+ // Valid only for type TI_METHOD without IsToken
CORINFO_METHOD_HANDLE m_method;
+ // Valid only for TI_TOKEN with IsToken
+ CORINFO_RESOLVED_TOKEN* m_token;
};
template <typename T>
m_method = method;
}
+ typeInfo(CORINFO_RESOLVED_TOKEN* token)
+ {
+ assert(token != nullptr);
+ assert(token->hMethod != nullptr);
+ assert(!isInvalidHandle(token->hMethod));
+ m_flags = TI_METHOD;
+ SetIsToken();
+ m_token = token;
+ }
+
#ifdef DEBUG
#if VERBOSE_VERIFY
void Dump() const;
// Operations
/////////////////////////////////////////////////////////////////////////
+ void SetIsToken()
+ {
+ m_flags |= TI_FLAG_TOKEN;
+ assert(m_bits.token);
+ }
+
void SetIsThisPtr()
{
m_flags |= TI_FLAG_THIS_PTR;
CORINFO_METHOD_HANDLE GetMethod() const
{
assert(GetType() == TI_METHOD);
+ if (IsToken())
+ {
+ return m_token->hMethod;
+ }
return m_method;
}
+ CORINFO_RESOLVED_TOKEN* GetToken() const
+ {
+ assert(IsToken());
+ return m_token;
+ }
+
// Get this item's type
// If primitive, returns the primitive type (TI_*)
// If not primitive, returns:
// Returns whether this is a method desc
BOOL IsMethod() const
{
- return (GetType() == TI_METHOD);
+ return GetType() == TI_METHOD;
}
BOOL IsStruct() const
return (m_flags & TI_FLAG_UNINIT_OBJREF);
}
+ BOOL IsToken() const
+ {
+ return IsMethod() && ((m_flags & TI_FLAG_TOKEN) != 0);
+ }
+
private:
// used to make functions that return typeinfo efficient.
typeInfo(DWORD flags, CORINFO_CLASS_HANDLE cls)
StackEntry impPopStack(CORINFO_CLASS_HANDLE& structTypeRet);
GenTreePtr impPopStack(typeInfo& ti);
StackEntry& impStackTop(unsigned n = 0);
+ unsigned impStackHeight();
void impSaveStackState(SavedStack* savePtr, bool copy);
void impRestoreStackState(SavedStack* savePtr);
bool impIsImplicitTailCallCandidate(
OPCODE curOpcode, const BYTE* codeAddrOfNextOpcode, const BYTE* codeEnd, int prefixFlags, bool isRecursive);
+ CORINFO_RESOLVED_TOKEN* impAllocateToken(CORINFO_RESOLVED_TOKEN token);
+
/*
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
void fgNoteNonInlineCandidate(GenTreeStmt* stmt, GenTreeCall* call);
static fgWalkPreFn fgFindNonInlineCandidate;
#endif
- GenTreePtr fgOptimizeDelegateConstructor(GenTreeCall* call, CORINFO_CONTEXT_HANDLE* ExactContextHnd);
+ GenTreePtr fgOptimizeDelegateConstructor(GenTreeCall* call,
+ CORINFO_CONTEXT_HANDLE* ExactContextHnd,
+ CORINFO_RESOLVED_TOKEN* ldftnToken);
GenTreePtr fgMorphLeaf(GenTreePtr tree);
void fgAssignSetVarDef(GenTreePtr tree);
GenTreePtr fgMorphOneAsgBlockOp(GenTreePtr tree);
* Optimize the call to the delegate constructor.
*/
-GenTreePtr Compiler::fgOptimizeDelegateConstructor(GenTreeCall* call, CORINFO_CONTEXT_HANDLE* ExactContextHnd)
+GenTreePtr Compiler::fgOptimizeDelegateConstructor(GenTreeCall* call,
+ CORINFO_CONTEXT_HANDLE* ExactContextHnd,
+ CORINFO_RESOLVED_TOKEN* ldftnToken)
{
noway_assert(call->gtCallType == CT_USER_FUNC);
CORINFO_METHOD_HANDLE methHnd = call->gtCallMethHnd;
#ifdef FEATURE_READYTORUN_COMPILER
if (opts.IsReadyToRun())
{
+ if (IsTargetAbi(CORINFO_CORERT_ABI))
+ {
+ if (ldftnToken != nullptr)
+ {
+ GenTreePtr thisPointer = call->gtCallObjp;
+ GenTreePtr targetObjPointers = call->gtCallArgs->Current();
+ GenTreeArgList* helperArgs = nullptr;
+ CORINFO_LOOKUP pLookup;
+ CORINFO_CONST_LOOKUP entryPoint;
+ info.compCompHnd->getReadyToRunDelegateCtorHelper(ldftnToken, clsHnd, &pLookup);
+ if (!pLookup.lookupKind.needsRuntimeLookup)
+ {
+ helperArgs = gtNewArgList(thisPointer, targetObjPointers);
+ entryPoint = pLookup.constLookup;
+ }
+ else
+ {
+ assert(oper != GT_FTN_ADDR);
+ CORINFO_CONST_LOOKUP genericLookup;
+ info.compCompHnd->getReadyToRunHelper(ldftnToken, &pLookup.lookupKind,
+ CORINFO_HELP_READYTORUN_GENERIC_HANDLE, &genericLookup);
+ GenTreePtr ctxTree = getRuntimeContextTree(pLookup.lookupKind.runtimeLookupKind);
+ helperArgs = gtNewArgList(thisPointer, targetObjPointers, ctxTree);
+ entryPoint = genericLookup;
+ }
+ call = gtNewHelperCallNode(CORINFO_HELP_READYTORUN_DELEGATE_CTOR, TYP_VOID, GTF_EXCEPT, helperArgs);
+ call->setEntryPoint(entryPoint);
+ }
+ }
// ReadyToRun has this optimization for a non-virtual function pointers only for now.
- if (oper == GT_FTN_ADDR)
+ else if (oper == GT_FTN_ADDR)
{
GenTreePtr thisPointer = call->gtCallObjp;
GenTreePtr targetObjPointers = call->gtCallArgs->Current();
call = gtNewHelperCallNode(CORINFO_HELP_READYTORUN_DELEGATE_CTOR, TYP_VOID, GTF_EXCEPT, helperArgs);
- CORINFO_RESOLVED_TOKEN* ldftnToken = targetMethod->gtFptrVal.gtLdftnResolvedToken;
- CORINFO_LOOKUP entryPoint;
+ assert(ldftnToken == targetMethod->gtFptrVal.gtLdftnResolvedToken);
+ CORINFO_LOOKUP entryPoint;
info.compCompHnd->getReadyToRunDelegateCtorHelper(ldftnToken, clsHnd, &entryPoint);
assert(!entryPoint.lookupKind.needsRuntimeLookup);
call->setEntryPoint(entryPoint.constLookup);
return verCurrentState.esStack[verCurrentState.esStackDepth - n - 1];
}
+
+unsigned Compiler::impStackHeight()
+{
+ return verCurrentState.esStackDepth;
+}
+
/*****************************************************************************
* Some of the trees are spilled specially. While unspilling them, or
* making a copy, these need to be handled specially. The function
int tailCall = prefixFlags & PREFIX_TAILCALL;
bool readonlyCall = (prefixFlags & PREFIX_READONLY) != 0;
+ CORINFO_RESOLVED_TOKEN* ldftnToken = nullptr;
+
// Synchronized methods need to call CORINFO_HELP_MON_EXIT at the end. We could
// do that before tailcalls, but that is probably not the intended
// semantic. So just disallow tailcalls from synchronized methods.
exactContextHnd = nullptr;
}
+ if ((opcode == CEE_NEWOBJ) && ((clsFlags & CORINFO_FLG_DELEGATE) != 0))
+ {
+ // Only verifiable cases are supported.
+ // dup; ldvirtftn; newobj; or ldftn; newobj.
+ // IL test could contain unverifiable sequence, in this case optimization should not be done.
+ if (impStackHeight() > 0)
+ {
+ typeInfo delegateTypeInfo = impStackTop().seTypeInfo;
+ if (delegateTypeInfo.IsToken())
+ {
+ ldftnToken = delegateTypeInfo.GetToken();
+ }
+ }
+ }
+
//-------------------------------------------------------------------------
// The main group of arguments
{
// New inliner morph it in impImportCall.
// This will allow us to inline the call to the delegate constructor.
- call = fgOptimizeDelegateConstructor(call->AsCall(), &exactContextHnd);
+ call = fgOptimizeDelegateConstructor(call->AsCall(), &exactContextHnd, ldftnToken);
}
if (!bIntrinsicImported)
return;
}
- impPushOnStack(op1, typeInfo(resolvedToken.hMethod));
+ CORINFO_RESOLVED_TOKEN* heapToken = impAllocateToken(resolvedToken);
+ impPushOnStack(op1, typeInfo(heapToken));
break;
}
return;
}
- impPushOnStack(fptr, typeInfo(resolvedToken.hMethod));
+ CORINFO_RESOLVED_TOKEN* heapToken = impAllocateToken(resolvedToken);
+ assert(heapToken->tokenType == CORINFO_TOKENKIND_Method);
+ heapToken->tokenType = CORINFO_TOKENKIND_Ldvirtftn;
+ impPushOnStack(fptr, typeInfo(heapToken));
break;
}
}
#endif // defined(DEBUG)
}
+
+//------------------------------------------------------------------------
+// impAllocateToken: create CORINFO_RESOLVED_TOKEN into jit-allocated memory and init it.
+//
+// Arguments:
+// token - init value for the allocated token.
+//
+// Return Value:
+// pointer to token into jit-allocated memory.
+CORINFO_RESOLVED_TOKEN* Compiler::impAllocateToken(CORINFO_RESOLVED_TOKEN token)
+{
+ CORINFO_RESOLVED_TOKEN* memory = (CORINFO_RESOLVED_TOKEN*)compGetMem(sizeof(token));
+ *memory = token;
+ return memory;
+}