InlineRefactor: rename inlExpLst to InlineContext
authorAndy Ayers <andya@microsoft.com>
Thu, 18 Feb 2016 23:28:08 +0000 (15:28 -0800)
committerAndy Ayers <andya@microsoft.com>
Fri, 19 Feb 2016 03:30:33 +0000 (19:30 -0800)
Rename inlExpLst to InlineContext, and move the code for it into
the inlining files. Expand the context to capture the observation
leading to success. Defer generating the method names until the
context is dumped to avoid unnecessary debug/check allocations.

Make sure observation field on GenTreeCall is properly initialized
and copied, and give it the right type via an opaque forward
declaration.

src/jit/block.cpp
src/jit/block.h
src/jit/compiler.h
src/jit/flowgraph.cpp
src/jit/gentree.cpp
src/jit/gentree.h
src/jit/inline.cpp
src/jit/inline.h
src/jit/morph.cpp

index d241cf6..30211d6 100644 (file)
@@ -636,69 +636,4 @@ unsigned PtrKeyFuncs<BasicBlock>::GetHashCode(const BasicBlock* ptr)
     return ptr->bbNum;
 }
 
-//------------------------------------------------------------------------
-// inlExpLst: default constructor for an inlExpList
-//
-// Notes: use for the root instance. We set ixlCode to nullptr here
-// (rather than the IL buffer address of the root method) to preserve
-// existing behavior, which is to allow one recursive inline.
-
-inlExpLst::inlExpLst() : ixlParent(nullptr), ixlChild(nullptr), 
-                         ixlSibling(nullptr), ilOffset(BAD_IL_OFFSET), 
-                         ixlCode(nullptr)
-{
-#ifdef DEBUG
-   methodName = nullptr;
-   depth = 0;
-#endif
-}
-
-
-#ifdef DEBUG
-
-//------------------------------------------------------------------------
-// Dump: Dump an inlExpLst entry and all descendants to stdout
-//
-// Arguments:
-//    indent: indentation level for this node
-
-void inlExpLst::Dump(int indent)
-{
-    // Handle fact that siblings are in reverse order.
-    if (ixlSibling != nullptr) 
-    {
-        ixlSibling->Dump(indent);
-    }
-    
-    // Dump this node
-    if (ixlParent == nullptr)
-    {
-        // root
-        printf("Inlines into %s\n", methodName);
-    }
-    else 
-    {
-        for (int i = 0; i < indent; i++) 
-        { 
-            printf(" ");
-        }
-        
-        if (ilOffset == BAD_IL_OFFSET) 
-        {
-            printf("[IL=?] %s\n", methodName);
-        }
-        else 
-        {
-            IL_OFFSET offset = jitGetILoffs(ilOffset);
-            printf("[IL=%d] %s\n", offset, methodName);
-        }
-    }
-    
-    // Recurse to first child
-    if (ixlChild != nullptr) 
-    {
-        ixlChild->Dump(indent + 2);
-    }
-}
-#endif
 
index f12017a..34f9b5f 100644 (file)
@@ -147,37 +147,6 @@ struct EntryState
     StackEntry*     esStack;                    // ptr to  stack
 };
 
-
-//-----------------------------------------------------------------------------
-//
-//  The following keeps track of the currently expanded inline functions.
-//  Any method currently on the list should not be inlined since that
-//  implies that it's being called recursively.
-//  We track the IL code body so we don't get confused by generics.
-//
-
-struct inlExpLst
-{
-   // Default constructor, suitable for root instance
-   inlExpLst();
-
-   inlExpLst*      ixlParent;    // logical caller (parent)
-   inlExpLst*      ixlChild;     // first child
-   inlExpLst*      ixlSibling;   // next child of the parent
-   IL_OFFSETX      ilOffset;     // call site location within parent
-   BYTE*           ixlCode;      // address of IL buffer for the method
-
-#ifdef DEBUG
-   const char *    methodName;
-   unsigned        depth;
-
-   // Dump this entry and all descendants
-   void Dump(int indent = 0);
-#endif
-};
-
-typedef inlExpLst* inlExpPtr;
-
 // This encapsulates the "exception handling" successors of a block.  That is,
 // if a basic block BB1 occurs in a try block, we consider the first basic block
 // BB2 of the corresponding handler to be an "EH successor" of BB1.  Because we
index 069c7d0..5b56d98 100644 (file)
@@ -4637,9 +4637,9 @@ private:
 
     unsigned            fgBigOffsetMorphingTemps[TYP_COUNT];
 
-    static bool         fgIsUnboundedInlineRecursion(inlExpPtr expLst,
-                                                     BYTE *    ilCode,
-                                                     DWORD*    depth);
+    static bool         fgIsUnboundedInlineRecursion(InlineContext* context,
+                                                     BYTE*          ilCode,
+                                                     DWORD*         depth);
 
     void                fgInvokeInlineeCompiler(GenTreeCall*   call, InlineResult* result);
     void                fgInsertInlineeBlocks (InlineInfo * pInlineInfo);
index 12a5d1c..001349a 100644 (file)
@@ -21317,7 +21317,7 @@ void Compiler::fgRemoveContainedEmbeddedStatements(GenTreePtr tree, GenTreeStmt*
  * Also return the depth.
  */
 
-bool          Compiler::fgIsUnboundedInlineRecursion(inlExpPtr               expLst,
+bool          Compiler::fgIsUnboundedInlineRecursion(InlineContext*          inlineContext,
                                                      BYTE*                   ilCode,
                                                      DWORD*                  finalDepth)
 {
@@ -21325,11 +21325,11 @@ bool          Compiler::fgIsUnboundedInlineRecursion(inlExpPtr               exp
     DWORD depth = 0;
     bool result = false;
 
-    for (; expLst != nullptr; expLst = expLst->ixlParent)
+    for (; inlineContext != nullptr; inlineContext = inlineContext->inlParent)
     {
         // Hard limit just to catch pathological cases
         depth++;
-        if  ((expLst->ixlCode == ilCode) || (depth > MAX_INLINING_RECURSION_DEPTH))
+        if  ((inlineContext->inlCode == ilCode) || (depth > MAX_INLINING_RECURSION_DEPTH))
         {
            result = true;
            break;
@@ -21360,10 +21360,10 @@ void                Compiler::fgInline()
     noway_assert(block != nullptr);
 
     // Set the root inline context on all statements
-    inlExpLst* expDsc = new (this, CMK_Inlining) inlExpLst();
+    InlineContext* rootContext = new (this, CMK_Inlining) InlineContext;
 
 #if defined(DEBUG)
-    expDsc->methodName = info.compFullName;
+    rootContext->inlCallee = info.compMethodHnd;
 #endif
 
     for (; block != nullptr; block = block->bbNext)
@@ -21372,7 +21372,7 @@ void                Compiler::fgInline()
              stmt;
              stmt = stmt->gtNextStmt)
         {
-           stmt->gtInlineExpList = expDsc;
+            stmt->gtInlineContext = rootContext;
         }
     }
 
@@ -21462,7 +21462,7 @@ void                Compiler::fgInline()
     if  (verbose || (fgInlinedCount > 0 && fgPrintInlinedMethods))
     {
        printf("**************** Inline Tree\n");
-       expDsc->Dump();
+       rootContext->Dump(this);
     }
 
 #endif // DEBUG
@@ -21761,10 +21761,11 @@ void       Compiler::fgInvokeInlineeCompiler(GenTreeCall*  call,
     // Store the link to inlineCandidateInfo into inlineInfo
     inlineInfo.inlineCandidateInfo = inlineCandidateInfo;
 
-    DWORD inlineDepth = 0;
-    BYTE * candidateIL = inlineCandidateInfo->methInfo.ILCode;
-    inlExpPtr expList = inlineInfo.iciStmt->gtStmt.gtInlineExpList;
-    if (fgIsUnboundedInlineRecursion(expList, candidateIL, &inlineDepth))
+    DWORD          inlineDepth = 0;
+    BYTE*          candidateIL = inlineCandidateInfo->methInfo.ILCode;
+    InlineContext* inlineContext = inlineInfo.iciStmt->gtStmt.gtInlineContext;
+
+    if (fgIsUnboundedInlineRecursion(inlineContext, candidateIL, &inlineDepth))
     {
 #ifdef DEBUG
         if (verbose)
@@ -21990,23 +21991,23 @@ void Compiler::fgInsertInlineeBlocks(InlineInfo* pInlineInfo)
 #endif // defined(DEBUG) || MEASURE_INLINING
 
     //
-    // Obtain an inlExpLst struct and update the gtInlineExpList field in the inlinee's statements
+    // Obtain the current InlineContext and link in a new child for the callee
     //
-    inlExpLst* expDsc = new (this, CMK_Inlining) inlExpLst;
-    inlExpLst *parentLst = iciStmt->gtStmt.gtInlineExpList;
-    noway_assert(parentLst != nullptr);
-    BYTE *parentIL = pInlineInfo->inlineCandidateInfo->methInfo.ILCode;
-    expDsc->ixlCode = parentIL;
-    expDsc->ixlParent = parentLst;
+    InlineContext* calleeContext = new (this, CMK_Inlining) InlineContext;
+    InlineContext* parentContext = iciStmt->gtStmt.gtInlineContext;
+    noway_assert(parentContext != nullptr);
+    BYTE* calleeIL = pInlineInfo->inlineCandidateInfo->methInfo.ILCode;
+    calleeContext->inlCode = calleeIL;
+    calleeContext->inlParent = parentContext;
     // Push on front here will put siblings in reverse lexical
     // order which we undo in the dumper
-    expDsc->ixlSibling = parentLst->ixlChild;
-    parentLst->ixlChild = expDsc;
-    expDsc->ixlChild = nullptr;
-    expDsc->ilOffset = iciStmt->AsStmt()->gtStmtILoffsx;
+    calleeContext->inlSibling = parentContext->inlChild;
+    parentContext->inlChild = calleeContext;
+    calleeContext->inlChild = nullptr;
+    calleeContext->inlOffset = iciStmt->AsStmt()->gtStmtILoffsx;
+    calleeContext->inlObservation = pInlineInfo->inlineResult->getObservation();
 #ifdef DEBUG
-    expDsc->methodName = eeGetMethodFullName(pInlineInfo->fncHandle);
-    expDsc->depth = parentLst->depth + 1;
+    calleeContext->inlCallee = pInlineInfo->fncHandle;
 #endif
 
     for (block = InlineeCompiler->fgFirstBB;
@@ -22017,7 +22018,7 @@ void Compiler::fgInsertInlineeBlocks(InlineInfo* pInlineInfo)
              stmt;
              stmt = stmt->gtNextStmt)
         {
-            stmt->gtInlineExpList = expDsc;
+            stmt->gtInlineContext = calleeContext;
         }
     }
 
index 3ff8f08..a44760d 100644 (file)
@@ -5129,6 +5129,10 @@ GenTreeCall*          Compiler::gtNewCallNode(gtCallTypes     callType,
     node->gtCall.gtEntryPoint.addr = nullptr;
 #endif
 
+#ifdef DEBUG
+    node->gtCall.gtInlineObservation = InlineObservation::CALLEE_UNUSED_INITIAL;
+#endif
+
 #ifdef DEBUGGING_SUPPORT
     // Spec: Managed Retval sequence points needs to be generated while generating debug info for debuggable code.
     //
@@ -6312,6 +6316,10 @@ GenTreePtr          Compiler::gtCloneExpr(GenTree * tree,
         copy->gtCall.gtEntryPoint = tree->gtCall.gtEntryPoint;
 #endif
 
+#ifdef DEBUG
+        copy->gtCall.gtInlineObservation = tree->gtCall.gtInlineObservation;
+#endif
+
         break;
 
     case GT_FIELD:
index 3c3a203..4e785ac 100644 (file)
@@ -2336,6 +2336,7 @@ struct GenTreeColon: public GenTreeOp
 
 /* gtCall   -- method call      (GT_CALL) */
 typedef class fgArgInfo *  fgArgInfoPtr;
+enum class InlineObservation;
 
 struct GenTreeCall final : public GenTree
 {
@@ -2510,7 +2511,7 @@ struct GenTreeCall final : public GenTree
 #ifdef DEBUG
     // For non-inline candidates, track the first observation
     // that blocks candidacy.
-    unsigned gtInlineObservation;
+    InlineObservation gtInlineObservation;
 #endif
 
     GenTreeCall(var_types type) : 
@@ -3175,15 +3176,13 @@ struct GenTreeRetExpr: public GenTree
 
 /* gtStmt   -- 'statement expr' (GT_STMT) */
 
+struct InlineContext;
+
 struct GenTreeStmt: public GenTree
 {
-    GenTreePtr      gtStmtExpr;     // root of the expression tree
-    GenTreePtr      gtStmtList;     // first node (for forward walks)
-
-    inlExpPtr       gtInlineExpList; // The inline expansion list of this statement.
-                                     // This is a list of CORINFO_METHOD_HANDLEs 
-                                     // that shows the history of inline expansion 
-                                     // which leads to this statement. 
+    GenTreePtr      gtStmtExpr;      // root of the expression tree
+    GenTreePtr      gtStmtList;      // first node (for forward walks)
+    InlineContext*  gtInlineContext; // The inline context for this statement.
   
 #if defined(DEBUGGING_SUPPORT) || defined(DEBUG)
     IL_OFFSETX      gtStmtILoffsx;   // instr offset (if available)
@@ -3253,7 +3252,7 @@ struct GenTreeStmt: public GenTree
         : GenTree(GT_STMT, TYP_VOID)
         , gtStmtExpr(expr)
         , gtStmtList(nullptr)
-        , gtInlineExpList(nullptr)
+        , gtInlineContext(nullptr)
 #if defined(DEBUGGING_SUPPORT) || defined(DEBUG)
         , gtStmtILoffsx(offset)
 #endif
index e642964..7988f62 100644 (file)
@@ -152,6 +152,83 @@ const char* inlGetImpactString(InlineObservation obs)
     }
 }
 
+
+//------------------------------------------------------------------------
+// InlieContext: default constructor
+//
+// Notes: use for the root instance. We set inlCode to nullptr here
+// (rather than the IL buffer address of the root method) to preserve
+// existing behavior, which is to allow one recursive inline.
+
+InlineContext::InlineContext()
+    : inlParent(nullptr)
+    , inlChild(nullptr)
+    , inlSibling(nullptr)
+    , inlOffset(BAD_IL_OFFSET)
+    , inlCode(nullptr)
+    , inlObservation(InlineObservation::CALLEE_UNUSED_INITIAL)
+#ifdef DEBUG
+    , inlCallee(nullptr)
+#endif
+{
+    // Empty
+}
+
+#ifdef DEBUG
+
+//------------------------------------------------------------------------
+// Dump: Dump an InlineContext entry and all descendants to stdout
+//
+// Arguments:
+//    compiler - compiler instance doing inlining
+//    indent   - indentation level for this node
+
+void InlineContext::Dump(Compiler* compiler, int indent)
+{
+    // Handle fact that siblings are in reverse order.
+    if (inlSibling != nullptr)
+    {
+        inlSibling->Dump(compiler, indent);
+    }
+
+    const char* calleeName = compiler->eeGetMethodFullName(inlCallee);
+
+    // Dump this node
+    if (inlParent == nullptr)
+    {
+        // Root method
+        printf("Inlines into %s\n", calleeName);
+    }
+    else
+    {
+        // Successful inline
+        const char* inlineReason = inlGetDescriptionString(inlObservation);
+
+        for (int i = 0; i < indent; i++)
+        {
+            printf(" ");
+        }
+
+        if (inlOffset == BAD_IL_OFFSET)
+        {
+            printf("[IL=????] [%s] %s\n", inlineReason, calleeName);
+        }
+        else
+        {
+            IL_OFFSET offset = jitGetILoffs(inlOffset);
+            printf("[IL=%04d] [%s] %s\n", offset, inlineReason, calleeName);
+        }
+    }
+
+    // Recurse to first child
+    if (inlChild != nullptr)
+    {
+        inlChild->Dump(compiler, indent + 2);
+    }
+}
+
+#endif // DEBUG
+
 //------------------------------------------------------------------------
 // InlineResult: Construct an InlineResult to evaluate a particular call
 // for inlining.
@@ -184,7 +261,7 @@ InlineResult::InlineResult(Compiler*    compiler,
 }
 
 //------------------------------------------------------------------------
-// InlineResult: Construct an InlineResult to evaluate a particular 
+// InlineResult: Construct an InlineResult to evaluate a particular
 // method as a possible inline candidate.
 //
 // Notes:
@@ -257,7 +334,7 @@ void InlineResult::report()
         // compiler should have revoked candidacy on the call by now
         assert((inlCall->gtFlags & GTF_CALL_INLINE_CANDIDATE) == 0);
 
-        inlCall->gtInlineObservation = static_cast<unsigned>(inlObservation);
+        inlCall->gtInlineObservation = inlObservation;
     }
 
 #endif // DEBUG
index 202b5ca..9f72edb 100644 (file)
@@ -13,6 +13,7 @@
 // InlineTarget -- enum, target of a particular observation
 // InlineImpact -- enum, impact of a particular observation
 // InlineObservation -- enum, facts observed when considering an inline
+// InlineContext -- class, remembers what inlines happened
 // InlineResult -- class, accumulates observations and makes a decision
 // InlineCandidateInfo -- struct, detailed information needed for inlining
 // InlArgInfo -- struct, information about a candidate's argument
@@ -116,6 +117,42 @@ InlineTarget inlGetTarget(InlineObservation obs);
 
 InlineImpact inlGetImpact(InlineObservation obs);
 
+// InlineContext tracks the inline history in a method.
+//
+// Notes:
+//
+// InlineContexts form a tree with the root method as the root and
+// inlines as children. Nested inlines are represented as granchildren
+// and so on.
+//
+// Leaves in the tree represent successful inlines of leaf methods.
+// In DEBUG builds we also keep track of failed inline attempts.
+//
+// During inlining, all statements in the IR refer back to the
+// InlineContext that is responsible for those statements existing.
+// This makes it possible to detect recursion and to keep track of the
+// depth of each inline attempt.
+
+struct InlineContext
+{
+    // Default constructor, suitable for root instance
+    InlineContext();
+
+    InlineContext*        inlParent;      // logical caller (parent)
+    InlineContext*        inlChild;       // first child
+    InlineContext*        inlSibling;     // next child of the parent
+    IL_OFFSETX            inlOffset;      // call site location within parent
+    BYTE*                 inlCode;        // address of IL buffer for the method
+    InlineObservation     inlObservation; // what lead to this inline
+
+#ifdef DEBUG
+    CORINFO_METHOD_HANDLE inlCallee;     // handle to the method
+
+   // Dump this entry and all descendants
+   void Dump(Compiler* compiler, int indent = 0);
+#endif
+};
+
 // InlineResult summarizes what is known about the viability of a
 // particular inline candiate.
 
@@ -318,6 +355,12 @@ public:
         report();
     }
 
+    // The observation leading to this particular result
+    InlineObservation getObservation() const
+    {
+        return inlObservation;
+    }
+
     // The reason for this particular result
     const char * reasonString() const
     {
index 86c579c..ab26617 100644 (file)
@@ -5696,7 +5696,8 @@ void Compiler::fgMorphCallInlineHelper(GenTreeCall* call, InlineResult* result)
 
         // Try and recover the reason left behind when the jit decided
         // this call was not a candidate.
-        InlineObservation priorObservation = static_cast<InlineObservation>(call->gtInlineObservation);
+        InlineObservation priorObservation = call->gtInlineObservation;
+
         if (inlIsValidObservation(priorObservation))
         {
             currentObservation = priorObservation;