JIT: add some devirtualization info to the inline context (#20395)
authorAndy Ayers <andya@microsoft.com>
Sat, 13 Oct 2018 01:30:10 +0000 (18:30 -0700)
committerGitHub <noreply@github.com>
Sat, 13 Oct 2018 01:30:10 +0000 (18:30 -0700)
Allows the jit to remember which calls were devirtualized and which
of those were then optimized to use an unboxed entry point. This info
is then dumped out as part of the inline tree.

Also remove some of the clutter from the COMPlus_JitPrintInlinedMethods
output stream -- we don't need to see both the in-stream results and
the final results, and we don't really need to know about the budget.
This information is still dumped for COMPlus_JitDump.

src/jit/flowgraph.cpp
src/jit/gentree.h
src/jit/importer.cpp
src/jit/inline.cpp
src/jit/inline.h

index f8fe15d..003ed3c 100644 (file)
@@ -21878,8 +21878,9 @@ void Compiler::fgInline()
 
     if (verbose || fgPrintInlinedMethods)
     {
-        printf("**************** Inline Tree\n");
-        m_inlineStrategy->Dump();
+        JITDUMP("**************** Inline Tree");
+        printf("\n");
+        m_inlineStrategy->Dump(verbose);
     }
 
 #endif // DEBUG
@@ -22573,7 +22574,7 @@ void Compiler::fgInvokeInlineeCompiler(GenTreeCall* call, InlineResult* inlineRe
 
 #ifdef DEBUG
 
-    if (verbose || fgPrintInlinedMethods)
+    if (verbose)
     {
         printf("Successfully inlined %s (%d IL bytes) (depth %d) [%s]\n", eeGetMethodFullName(fncHandle),
                inlineCandidateInfo->methInfo.ILCodeSize, inlineDepth, inlineResult->ReasonString());
index ebeae48..bd30143 100644 (file)
@@ -3537,6 +3537,8 @@ struct GenTreeCall final : public GenTree
                                                     // stubs, because executable code cannot be generated at runtime.
 #define GTF_CALL_M_HELPER_SPECIAL_DCE    0x00020000 // GT_CALL -- this helper call can be removed if it is part of a comma and
                                                     // the comma result is unused.
+#define GTF_CALL_M_DEVIRTUALIZED         0x00040000 // GT_CALL -- this call was devirtualized
+#define GTF_CALL_M_UNBOXED               0x00080000 // GT_CALL -- this call was optimized to use the unboxed entry point
 
     // clang-format on
 
@@ -3743,6 +3745,16 @@ struct GenTreeCall final : public GenTree
         gtCallMoreFlags |= GTF_CALL_M_FAT_POINTER_CHECK;
     }
 
+    bool IsDevirtualized() const
+    {
+        return (gtCallMoreFlags & GTF_CALL_M_DEVIRTUALIZED) != 0;
+    }
+
+    bool IsUnboxed() const
+    {
+        return (gtCallMoreFlags & GTF_CALL_M_UNBOXED) != 0;
+    }
+
     unsigned gtCallMoreFlags; // in addition to gtFlags
 
     unsigned char gtCallType : 3;   // value from the gtCallTypes enumeration
index a3a82e8..68f602b 100644 (file)
@@ -19631,6 +19631,7 @@ void Compiler::impDevirtualizeCall(GenTreeCall*            call,
     call->gtFlags &= ~GTF_CALL_VIRT_STUB;
     call->gtCallMethHnd = derivedMethod;
     call->gtCallType    = CT_USER_FUNC;
+    call->gtCallMoreFlags |= GTF_CALL_M_DEVIRTUALIZED;
 
     // Virtual calls include an implicit null check, which we may
     // now need to make explicit.
@@ -19695,6 +19696,7 @@ void Compiler::impDevirtualizeCall(GenTreeCall*            call,
                         // Pass the local var as this and the type handle as a new arg
                         JITDUMP("Success! invoking unboxed entry point on local copy, and passing method table arg\n");
                         call->gtCallObjp = localCopyThis;
+                        call->gtCallMoreFlags |= GTF_CALL_M_UNBOXED;
 
                         // Prepend for R2L arg passing or empty L2R passing
                         if ((Target::g_tgtArgOrder == Target::ARG_ORDER_R2L) || (call->gtCallArgs == nullptr))
@@ -19742,7 +19744,8 @@ void Compiler::impDevirtualizeCall(GenTreeCall*            call,
                     JITDUMP("Success! invoking unboxed entry point on local copy\n");
                     call->gtCallObjp    = localCopyThis;
                     call->gtCallMethHnd = unboxedEntryMethod;
-                    derivedMethod       = unboxedEntryMethod;
+                    call->gtCallMoreFlags |= GTF_CALL_M_UNBOXED;
+                    derivedMethod = unboxedEntryMethod;
                 }
                 else
                 {
index 36a8802..a106476 100644 (file)
@@ -336,6 +336,8 @@ InlineContext::InlineContext(InlineStrategy* strategy)
     , m_Observation(InlineObservation::CALLEE_UNUSED_INITIAL)
     , m_CodeSizeEstimate(0)
     , m_Success(true)
+    , m_Devirtualized(false)
+    , m_Unboxed(false)
 #if defined(DEBUG) || defined(INLINE_DATA)
     , m_Policy(nullptr)
     , m_Callee(nullptr)
@@ -392,19 +394,21 @@ void InlineContext::Dump(unsigned indent)
     else
     {
         // Inline attempt.
-        const char* inlineReason = InlGetObservationString(m_Observation);
-        const char* inlineResult = m_Success ? "" : "FAILED: ";
+        const char* inlineReason  = InlGetObservationString(m_Observation);
+        const char* inlineResult  = m_Success ? "" : "FAILED: ";
+        const char* devirtualized = m_Devirtualized ? " devirt" : "";
+        const char* unboxed       = m_Unboxed ? " unboxed" : "";
 
         if (m_Offset == BAD_IL_OFFSET)
         {
-            printf("%*s[%u IL=???? TR=%06u %08X] [%s%s] %s\n", indent, "", m_Ordinal, m_TreeID, calleeToken,
-                   inlineResult, inlineReason, calleeName);
+            printf("%*s[%u IL=???? TR=%06u %08X] [%s%s%s%s] %s\n", indent, "", m_Ordinal, m_TreeID, calleeToken,
+                   inlineResult, inlineReason, devirtualized, unboxed, calleeName);
         }
         else
         {
             IL_OFFSET offset = jitGetILoffs(m_Offset);
-            printf("%*s[%u IL=%04d TR=%06u %08X] [%s%s] %s\n", indent, "", m_Ordinal, offset, m_TreeID, calleeToken,
-                   inlineResult, inlineReason, calleeName);
+            printf("%*s[%u IL=%04d TR=%06u %08X] [%s%s%s%s] %s\n", indent, "", m_Ordinal, offset, m_TreeID, calleeToken,
+                   inlineResult, inlineReason, devirtualized, unboxed, calleeName);
         }
     }
 
@@ -1172,7 +1176,6 @@ InlineContext* InlineStrategy::NewRoot()
 // and link it into the context tree
 //
 // Arguments:
-//    stmt       - statement containing call being inlined
 //    inlineInfo - information about this inline
 //
 // Return Value:
@@ -1186,6 +1189,7 @@ InlineContext* InlineStrategy::NewSuccess(InlineInfo* inlineInfo)
     BYTE*          calleeIL      = inlineInfo->inlineCandidateInfo->methInfo.ILCode;
     unsigned       calleeILSize  = inlineInfo->inlineCandidateInfo->methInfo.ILCodeSize;
     InlineContext* parentContext = stmt->gtInlineContext;
+    GenTreeCall*   originalCall  = inlineInfo->inlineResult->GetCall();
 
     noway_assert(parentContext != nullptr);
 
@@ -1194,12 +1198,14 @@ InlineContext* InlineStrategy::NewSuccess(InlineInfo* inlineInfo)
     calleeContext->m_Parent = parentContext;
     // Push on front here will put siblings in reverse lexical
     // order which we undo in the dumper
-    calleeContext->m_Sibling     = parentContext->m_Child;
-    parentContext->m_Child       = calleeContext;
-    calleeContext->m_Child       = nullptr;
-    calleeContext->m_Offset      = stmt->AsStmt()->gtStmtILoffsx;
-    calleeContext->m_Observation = inlineInfo->inlineResult->GetObservation();
-    calleeContext->m_Success     = true;
+    calleeContext->m_Sibling       = parentContext->m_Child;
+    parentContext->m_Child         = calleeContext;
+    calleeContext->m_Child         = nullptr;
+    calleeContext->m_Offset        = stmt->gtStmtILoffsx;
+    calleeContext->m_Observation   = inlineInfo->inlineResult->GetObservation();
+    calleeContext->m_Success       = true;
+    calleeContext->m_Devirtualized = originalCall->IsDevirtualized();
+    calleeContext->m_Unboxed       = originalCall->IsUnboxed();
 
 #if defined(DEBUG) || defined(INLINE_DATA)
 
@@ -1211,13 +1217,13 @@ InlineContext* InlineStrategy::NewSuccess(InlineInfo* inlineInfo)
     // +1 here since we set this before calling NoteOutcome.
     calleeContext->m_Ordinal = m_InlineCount + 1;
     // Update offset with more accurate info
-    calleeContext->m_Offset = inlineInfo->inlineResult->GetCall()->gtRawILOffset;
+    calleeContext->m_Offset = originalCall->gtRawILOffset;
 
 #endif // defined(DEBUG) || defined(INLINE_DATA)
 
 #if defined(DEBUG)
 
-    calleeContext->m_TreeID = inlineInfo->inlineResult->GetCall()->gtTreeID;
+    calleeContext->m_TreeID = originalCall->gtTreeID;
 
 #endif // defined(DEBUG)
 
@@ -1237,8 +1243,7 @@ InlineContext* InlineStrategy::NewSuccess(InlineInfo* inlineInfo)
 //    inlineResult - inlineResult for the attempt
 //
 // Return Value:
-//    A new InlineContext for diagnostic purposes, or nullptr if
-//    the desired context could not be created.
+//    A new InlineContext for diagnostic purposes
 
 InlineContext* InlineStrategy::NewFailure(GenTreeStmt* stmt, InlineResult* inlineResult)
 {
@@ -1247,31 +1252,34 @@ InlineContext* InlineStrategy::NewFailure(GenTreeStmt* stmt, InlineResult* inlin
     InlineContext* parentContext = stmt->gtInlineContext;
     assert(parentContext != nullptr);
     InlineContext* failedContext = new (m_Compiler, CMK_Inlining) InlineContext(this);
+    GenTreeCall*   originalCall  = inlineResult->GetCall();
 
     // Pushing the new context on the front of the parent child list
     // will put siblings in reverse lexical order which we undo in the
     // dumper.
-    failedContext->m_Parent      = parentContext;
-    failedContext->m_Sibling     = parentContext->m_Child;
-    parentContext->m_Child       = failedContext;
-    failedContext->m_Child       = nullptr;
-    failedContext->m_Offset      = stmt->gtStmtILoffsx;
-    failedContext->m_Observation = inlineResult->GetObservation();
-    failedContext->m_Callee      = inlineResult->GetCallee();
-    failedContext->m_Success     = false;
+    failedContext->m_Parent        = parentContext;
+    failedContext->m_Sibling       = parentContext->m_Child;
+    parentContext->m_Child         = failedContext;
+    failedContext->m_Child         = nullptr;
+    failedContext->m_Offset        = stmt->gtStmtILoffsx;
+    failedContext->m_Observation   = inlineResult->GetObservation();
+    failedContext->m_Callee        = inlineResult->GetCallee();
+    failedContext->m_Success       = false;
+    failedContext->m_Devirtualized = originalCall->IsDevirtualized();
+    failedContext->m_Unboxed       = originalCall->IsUnboxed();
 
     assert(InlIsValidObservation(failedContext->m_Observation));
 
 #if defined(DEBUG) || defined(INLINE_DATA)
 
     // Update offset with more accurate info
-    failedContext->m_Offset = inlineResult->GetCall()->gtRawILOffset;
+    failedContext->m_Offset = originalCall->gtRawILOffset;
 
 #endif // #if defined(DEBUG) || defined(INLINE_DATA)
 
 #if defined(DEBUG)
 
-    failedContext->m_TreeID = inlineResult->GetCall()->gtTreeID;
+    failedContext->m_TreeID = originalCall->gtTreeID;
 
 #endif // defined(DEBUG)
 
@@ -1282,11 +1290,19 @@ InlineContext* InlineStrategy::NewFailure(GenTreeStmt* stmt, InlineResult* inlin
 
 //------------------------------------------------------------------------
 // Dump: dump description of inline behavior
+//
+// Arguments:
+//   showBudget - also dump final budget values
 
-void InlineStrategy::Dump()
+void InlineStrategy::Dump(bool showBudget)
 {
     m_RootContext->Dump();
 
+    if (!showBudget)
+    {
+        return;
+    }
+
     printf("Budget: initialTime=%d, finalTime=%d, initialBudget=%d, currentBudget=%d\n", m_InitialTimeEstimate,
            m_CurrentTimeEstimate, m_InitialTimeBudget, m_CurrentTimeBudget);
 
index cedf36d..0503b07 100644 (file)
@@ -677,20 +677,32 @@ public:
         return m_Parent == nullptr;
     }
 
+    bool IsDevirtualized() const
+    {
+        return m_Devirtualized;
+    }
+
+    bool IsUnboxed() const
+    {
+        return m_Unboxed;
+    }
+
 private:
     InlineContext(InlineStrategy* strategy);
 
 private:
-    InlineStrategy*   m_InlineStrategy;   // overall strategy
-    InlineContext*    m_Parent;           // logical caller (parent)
-    InlineContext*    m_Child;            // first child
-    InlineContext*    m_Sibling;          // next child of the parent
-    BYTE*             m_Code;             // address of IL buffer for the method
-    unsigned          m_ILSize;           // size of IL buffer for the method
-    IL_OFFSETX        m_Offset;           // call site location within parent
-    InlineObservation m_Observation;      // what lead to this inline
-    int               m_CodeSizeEstimate; // in bytes * 10
-    bool              m_Success;          // true if this was a successful inline
+    InlineStrategy*   m_InlineStrategy;    // overall strategy
+    InlineContext*    m_Parent;            // logical caller (parent)
+    InlineContext*    m_Child;             // first child
+    InlineContext*    m_Sibling;           // next child of the parent
+    BYTE*             m_Code;              // address of IL buffer for the method
+    unsigned          m_ILSize;            // size of IL buffer for the method
+    IL_OFFSETX        m_Offset;            // call site location within parent
+    InlineObservation m_Observation;       // what lead to this inline
+    int               m_CodeSizeEstimate;  // in bytes * 10
+    bool              m_Success : 1;       // true if this was a successful inline
+    bool              m_Devirtualized : 1; // true if this was a devirtualized call
+    bool              m_Unboxed : 1;       // true if this call now invokes the unboxed entry
 
 #if defined(DEBUG) || defined(INLINE_DATA)
 
@@ -817,7 +829,7 @@ public:
 #if defined(DEBUG) || defined(INLINE_DATA)
 
     // Dump textual description of inlines done so far.
-    void Dump();
+    void Dump(bool showBudget);
 
     // Dump data-format description of inlines done so far.
     void DumpData();