CoreRT: make CORINFO_HELP_READYTORUN_DELEGATE_CTOR optimization for verifiable sequen...
authorSergey Andreenko <seandree@microsoft.com>
Tue, 4 Apr 2017 23:20:29 +0000 (16:20 -0700)
committerGitHub <noreply@github.com>
Tue, 4 Apr 2017 23:20:29 +0000 (16:20 -0700)
delegate ctor optimization for CoreRT.

src/inc/corinfo.h
src/jit/_typeinfo.h
src/jit/compiler.h
src/jit/flowgraph.cpp
src/jit/importer.cpp

index f1ff1b680c4988e8b375d034ef896f1d63457ec9..cbc4464e1d0f00a23969db1307a0f1b5e36503dc 100644 (file)
@@ -1568,6 +1568,9 @@ enum CorInfoTokenKind
 
     // 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
index 795691ddc121d9d4556f3ebf439426ccaf6ec759..b024912dda72711f71202699bbb0655bde6a7f27 100755 (executable)
@@ -222,6 +222,9 @@ inline ti_types JITtype2tiType(CorInfoType type)
 // 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
@@ -289,7 +292,8 @@ private:
             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
@@ -300,8 +304,10 @@ private:
 
     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>
@@ -365,6 +371,16 @@ public:
         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;
@@ -444,6 +460,12 @@ public:
     // Operations
     /////////////////////////////////////////////////////////////////////////
 
+    void SetIsToken()
+    {
+        m_flags |= TI_FLAG_TOKEN;
+        assert(m_bits.token);
+    }
+
     void SetIsThisPtr()
     {
         m_flags |= TI_FLAG_THIS_PTR;
@@ -553,9 +575,19 @@ public:
     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:
@@ -616,7 +648,7 @@ public:
     // Returns whether this is a method desc
     BOOL IsMethod() const
     {
-        return (GetType() == TI_METHOD);
+        return GetType() == TI_METHOD;
     }
 
     BOOL IsStruct() const
@@ -720,6 +752,11 @@ public:
         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)
index 5e876aaa2ad5305f9ad23a895d451c41f1905228..1c01953f46feeaf35859b18719b6673723c4a017 100644 (file)
@@ -2880,6 +2880,7 @@ protected:
     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);
@@ -3384,6 +3385,8 @@ private:
     bool impIsImplicitTailCallCandidate(
         OPCODE curOpcode, const BYTE* codeAddrOfNextOpcode, const BYTE* codeEnd, int prefixFlags, bool isRecursive);
 
+    CORINFO_RESOLVED_TOKEN* impAllocateToken(CORINFO_RESOLVED_TOKEN token);
+
     /*
     XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
     XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
@@ -4729,7 +4732,9 @@ private:
     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);
index 534cb4047125354b4536d327ae9a0ae9f6602341..50d1cfa22d9715e4d1b9adefd3a44b9d7e2d2887 100644 (file)
@@ -7118,7 +7118,9 @@ bool Compiler::fgAddrCouldBeNull(GenTreePtr addr)
  *  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;
@@ -7182,8 +7184,37 @@ GenTreePtr Compiler::fgOptimizeDelegateConstructor(GenTreeCall* call, CORINFO_CO
 #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();
@@ -7191,8 +7222,8 @@ GenTreePtr Compiler::fgOptimizeDelegateConstructor(GenTreeCall* call, CORINFO_CO
 
             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);
index e4c740e3388d00a20e5265f1b4a48bea1dcf8618..11e1d470bbb893253afa5ae9cdd55aa74d1e27cf 100644 (file)
@@ -350,6 +350,12 @@ StackEntry& Compiler::impStackTop(unsigned n)
 
     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
@@ -6378,6 +6384,8 @@ var_types Compiler::impImportCall(OPCODE                  opcode,
     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.
@@ -7289,6 +7297,21 @@ var_types Compiler::impImportCall(OPCODE                  opcode,
         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
 
@@ -7369,7 +7392,7 @@ var_types Compiler::impImportCall(OPCODE                  opcode,
             {
                 // 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)
@@ -12272,7 +12295,8 @@ void Compiler::impImportBlockCode(BasicBlock* block)
                     return;
                 }
 
-                impPushOnStack(op1, typeInfo(resolvedToken.hMethod));
+                CORINFO_RESOLVED_TOKEN* heapToken = impAllocateToken(resolvedToken);
+                impPushOnStack(op1, typeInfo(heapToken));
 
                 break;
             }
@@ -12377,7 +12401,10 @@ void Compiler::impImportBlockCode(BasicBlock* block)
                     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;
             }
@@ -18696,3 +18723,18 @@ void Compiler::impDevirtualizeCall(GenTreeCall*            call,
     }
 #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;
+}