Jit: track ref types for inlinee locals and args
authorAndy Ayers <andya@microsoft.com>
Mon, 20 Mar 2017 18:12:05 +0000 (11:12 -0700)
committerAndy Ayers <andya@microsoft.com>
Thu, 23 Mar 2017 17:42:02 +0000 (10:42 -0700)
The jit may use new temporaries for inline locals and args. For temps
representing ref type locals and args, propagate the class handle from
the appropriate signature. This makes more types to be available for
devirtualization.

Also in addition to capturing arg ref types, cleanup `impInlineFetchArg`
by commoning out the table lookups and adding a header comment.

Commit migrated from https://github.com/dotnet/coreclr/commit/22668cc8a71140f8c8fa467e70d735cb0a00035e

src/coreclr/src/jit/importer.cpp

index f009c49..c39004e 100644 (file)
@@ -9410,6 +9410,8 @@ GenTreePtr Compiler::impCastClassOrIsInstToTree(GenTreePtr              op1,
 
     // Make QMark node a top level node by spilling it.
     unsigned tmp = lvaGrabTemp(true DEBUGARG("spilling QMark2"));
+    // TODO: Is it possible op1 has a better type?
+    lvaTable[tmp].lvClassHnd = pResolvedToken->hClass;
     impAssignTempGen(tmp, qmarkNull, (unsigned)CHECK_SPILL_NONE);
     return gtNewLclvNode(tmp, TYP_REF);
 #endif
@@ -17708,6 +17710,13 @@ unsigned Compiler::impInlineFetchLocal(unsigned lclNum DEBUGARG(const char* reas
         lvaTable[tmpNum].lvHasLdAddrOp = inlineeLocal.lclHasLdlocaOp;
         lvaTable[tmpNum].lvPinned      = inlineeLocal.lclIsPinned;
 
+        // Copy over class handle for ref types. Note this may be
+        // further improved if it is a shared type.
+        if (lclTyp == TYP_REF)
+        {
+            lvaTable[tmpNum].lvClassHnd = inlineeLocal.lclVerTypeInfo.GetClassHandleForObjRef();
+        }
+
         if (inlineeLocal.lclVerTypeInfo.IsStruct())
         {
             if (varTypeIsStruct(lclTyp))
@@ -17741,53 +17750,89 @@ unsigned Compiler::impInlineFetchLocal(unsigned lclNum DEBUGARG(const char* reas
     return tmpNum;
 }
 
-// A method used to return the GenTree (usually a GT_LCL_VAR) representing the arguments of the inlined method.
-// Only use this method for the arguments of the inlinee method.
-// !!! Do not use it for the locals of the inlinee method. !!!!
+//------------------------------------------------------------------------
+// impInlineFetchArg: return tree node for argument value in an inlinee
+//
+// Arguments:
+//    lclNum -- argument number in inlinee IL
+//    inlArgInfo -- argument info for inlinee
+//    lclVarInfo -- var info for inlinee
+//
+// Returns:
+//    Tree for the argument's value. Often an inlinee-scoped temp
+//    GT_LCL_VAR but can be other tree kinds, if the argument
+//    expression from the caller can be directly substituted into the
+//    inlinee body.
+//
+// Notes:
+//    Must be used only for arguments -- use impInlineFetchLocal for
+//    inlinee locals.
+//
+//    Direct substitution is performed when the formal argument cannot
+//    change value in the inlinee body (no starg or ldarga), and the
+//    actual argument expression's value cannot be changed if it is
+//    substituted it into the inlinee body.
+//
+//    Even if an inlinee-scoped temp is returned here, it may later be
+//    "bashed" to a caller-supplied tree when arguments are actually
+//    passed (see fgInlinePrependStatements). Bashing can happen if
+//    the argument ends up being single use and other conditions are
+//    met. So the contents of the tree returned here may not end up
+//    being the ones ultimately used for the argument.
+//
+//    This method will side effect inlArgInfo. It should only be called
+//    for actual uses of the argument in the inlinee.
 
 GenTreePtr Compiler::impInlineFetchArg(unsigned lclNum, InlArgInfo* inlArgInfo, InlLclVarInfo* lclVarInfo)
 {
-    /* Get the argument type */
-    var_types lclTyp = lclVarInfo[lclNum].lclTypeInfo;
+    // Cache the relevant arg and lcl info for this argument.
+    // We will modify argInfo but not lclVarInfo.
+    InlArgInfo&          argInfo          = inlArgInfo[lclNum];
+    const InlLclVarInfo& lclInfo          = lclVarInfo[lclNum];
+    const bool           argCanBeModified = argInfo.argHasLdargaOp || argInfo.argHasStargOp;
+    const var_types      lclTyp           = lclInfo.lclTypeInfo;
+    GenTreePtr           op1              = nullptr;
 
-    GenTreePtr op1 = nullptr;
-
-    // constant or address of local
-    if (inlArgInfo[lclNum].argIsInvariant && !inlArgInfo[lclNum].argHasLdargaOp && !inlArgInfo[lclNum].argHasStargOp)
+    if (argInfo.argIsInvariant && !argCanBeModified)
     {
-        /* Clone the constant. Note that we cannot directly use argNode
-        in the trees even if inlArgInfo[lclNum].argIsUsed==false as this
-        would introduce aliasing between inlArgInfo[].argNode and
-        impInlineExpr. Then gtFoldExpr() could change it, causing further
-        references to the argument working off of the bashed copy. */
-
-        op1 = gtCloneExpr(inlArgInfo[lclNum].argNode);
+        // Directly substitute constants or addresses of locals
+        //
+        // Clone the constant. Note that we cannot directly use
+        // argNode in the trees even if !argInfo.argIsUsed as this
+        // would introduce aliasing between inlArgInfo[].argNode and
+        // impInlineExpr. Then gtFoldExpr() could change it, causing
+        // further references to the argument working off of the
+        // bashed copy.
+        op1 = gtCloneExpr(argInfo.argNode);
         PREFIX_ASSUME(op1 != nullptr);
-        inlArgInfo[lclNum].argTmpNum = (unsigned)-1; // illegal temp
+        argInfo.argTmpNum = BAD_VAR_NUM;
     }
-    else if (inlArgInfo[lclNum].argIsLclVar && !inlArgInfo[lclNum].argHasLdargaOp && !inlArgInfo[lclNum].argHasStargOp)
+    else if (argInfo.argIsLclVar && !argCanBeModified)
     {
-        /* Argument is a local variable (of the caller)
-         * Can we re-use the passed argument node? */
-
-        op1                          = inlArgInfo[lclNum].argNode;
-        inlArgInfo[lclNum].argTmpNum = op1->gtLclVarCommon.gtLclNum;
+        // Directly substitute caller locals
+        //
+        // Use the caller-supplied node if this is the first use.
+        op1               = argInfo.argNode;
+        argInfo.argTmpNum = op1->gtLclVarCommon.gtLclNum;
 
-        if (inlArgInfo[lclNum].argIsUsed)
+        // Use an equivalent copy if this is the second or subsequent use.
+        if (argInfo.argIsUsed)
         {
             assert(op1->gtOper == GT_LCL_VAR);
             assert(lclNum == op1->gtLclVar.gtLclILoffs);
 
+            var_types newTyp = lclTyp;
+
             if (!lvaTable[op1->gtLclVarCommon.gtLclNum].lvNormalizeOnLoad())
             {
-                lclTyp = genActualType(lclTyp);
+                newTyp = genActualType(lclTyp);
             }
 
-            /* Create a new lcl var node - remember the argument lclNum */
-            op1 = gtNewLclvNode(op1->gtLclVarCommon.gtLclNum, lclTyp, op1->gtLclVar.gtLclILoffs);
+            // Create a new lcl var node - remember the argument lclNum
+            op1 = gtNewLclvNode(op1->gtLclVarCommon.gtLclNum, newTyp, op1->gtLclVar.gtLclILoffs);
         }
     }
-    else if (inlArgInfo[lclNum].argIsByRefToStructLocal && !inlArgInfo[lclNum].argHasStargOp)
+    else if (argInfo.argIsByRefToStructLocal && !argInfo.argHasStargOp)
     {
         /* Argument is a by-ref address to a struct, a normed struct, or its field.
            In these cases, don't spill the byref to a local, simply clone the tree and use it.
@@ -17806,59 +17851,65 @@ GenTreePtr Compiler::impInlineFetchArg(unsigned lclNum, InlArgInfo* inlArgInfo,
            then we change the argument tree (of "ldloca.s V_1") to TYP_I_IMPL to match the callee signature. We'll
            soon afterwards reject the inlining anyway, since the tree we return isn't a GT_LCL_VAR.
         */
-        assert(inlArgInfo[lclNum].argNode->TypeGet() == TYP_BYREF ||
-               inlArgInfo[lclNum].argNode->TypeGet() == TYP_I_IMPL);
-        op1 = gtCloneExpr(inlArgInfo[lclNum].argNode);
+        assert(argInfo.argNode->TypeGet() == TYP_BYREF || argInfo.argNode->TypeGet() == TYP_I_IMPL);
+        op1 = gtCloneExpr(argInfo.argNode);
     }
     else
     {
         /* Argument is a complex expression - it must be evaluated into a temp */
 
-        if (inlArgInfo[lclNum].argHasTmp)
+        if (argInfo.argHasTmp)
         {
-            assert(inlArgInfo[lclNum].argIsUsed);
-            assert(inlArgInfo[lclNum].argTmpNum < lvaCount);
+            assert(argInfo.argIsUsed);
+            assert(argInfo.argTmpNum < lvaCount);
 
             /* Create a new lcl var node - remember the argument lclNum */
-            op1 = gtNewLclvNode(inlArgInfo[lclNum].argTmpNum, genActualType(lclTyp));
+            op1 = gtNewLclvNode(argInfo.argTmpNum, genActualType(lclTyp));
 
             /* This is the second or later use of the this argument,
             so we have to use the temp (instead of the actual arg) */
-            inlArgInfo[lclNum].argBashTmpNode = nullptr;
+            argInfo.argBashTmpNode = nullptr;
         }
         else
         {
             /* First time use */
-            assert(inlArgInfo[lclNum].argIsUsed == false);
+            assert(!argInfo.argIsUsed);
 
             /* Reserve a temp for the expression.
             * Use a large size node as we may change it later */
 
-            unsigned tmpNum = lvaGrabTemp(true DEBUGARG("Inlining Arg"));
+            const unsigned tmpNum = lvaGrabTemp(true DEBUGARG("Inlining Arg"));
 
             lvaTable[tmpNum].lvType = lclTyp;
+
+            // Copy over class handle for ref types. Note this may be
+            // further improved if it is a shared type and we know the exact context.
+            if (lclTyp == TYP_REF)
+            {
+                lvaTable[tmpNum].lvClassHnd = lclInfo.lclVerTypeInfo.GetClassHandleForObjRef();
+            }
+
             assert(lvaTable[tmpNum].lvAddrExposed == 0);
-            if (inlArgInfo[lclNum].argHasLdargaOp)
+            if (argInfo.argHasLdargaOp)
             {
                 lvaTable[tmpNum].lvHasLdAddrOp = 1;
             }
 
-            if (lclVarInfo[lclNum].lclVerTypeInfo.IsStruct())
+            if (lclInfo.lclVerTypeInfo.IsStruct())
             {
                 if (varTypeIsStruct(lclTyp))
                 {
-                    lvaSetStruct(tmpNum, impInlineInfo->lclVarInfo[lclNum].lclVerTypeInfo.GetClassHandle(),
-                                 true /* unsafe value cls check */);
+                    lvaSetStruct(tmpNum, lclInfo.lclVerTypeInfo.GetClassHandle(), true /* unsafe value cls check */);
                 }
                 else
                 {
                     // This is a wrapped primitive.  Make sure the verstate knows that
-                    lvaTable[tmpNum].lvVerTypeInfo = impInlineInfo->lclVarInfo[lclNum].lclVerTypeInfo;
+                    lvaTable[tmpNum].lvVerTypeInfo = lclInfo.lclVerTypeInfo;
                 }
             }
 
-            inlArgInfo[lclNum].argHasTmp = true;
-            inlArgInfo[lclNum].argTmpNum = tmpNum;
+            argInfo.argHasTmp = true;
+            argInfo.argTmpNum = tmpNum;
 
             // If we require strict exception order, then arguments must
             // be evaluated in sequence before the body of the inlined method.
@@ -17869,7 +17920,7 @@ GenTreePtr Compiler::impInlineFetchArg(unsigned lclNum, InlArgInfo* inlArgInfo,
             // TODO-1stClassStructs: We currently do not reuse an existing lclVar
             // if it is a struct, because it requires some additional handling.
 
-            if (!varTypeIsStruct(lclTyp) && (!inlArgInfo[lclNum].argHasSideEff) && (!inlArgInfo[lclNum].argHasGlobRef))
+            if (!varTypeIsStruct(lclTyp) && !argInfo.argHasSideEff && !argInfo.argHasGlobRef)
             {
                 /* Get a *LARGE* LCL_VAR node */
                 op1 = gtNewLclLNode(tmpNum, genActualType(lclTyp), lclNum);
@@ -17878,21 +17929,20 @@ GenTreePtr Compiler::impInlineFetchArg(unsigned lclNum, InlArgInfo* inlArgInfo,
                 If there are no further uses of the arg, we may be
                 able to use the actual arg node instead of the temp.
                 If we do see any further uses, we will clear this. */
-                inlArgInfo[lclNum].argBashTmpNode = op1;
+                argInfo.argBashTmpNode = op1;
             }
             else
             {
                 /* Get a small LCL_VAR node */
                 op1 = gtNewLclvNode(tmpNum, genActualType(lclTyp));
                 /* No bashing of this argument */
-                inlArgInfo[lclNum].argBashTmpNode = nullptr;
+                argInfo.argBashTmpNode = nullptr;
             }
         }
     }
 
-    /* Mark the argument as used */
-
-    inlArgInfo[lclNum].argIsUsed = true;
+    // Mark this argument as used.
+    argInfo.argIsUsed = true;
 
     return op1;
 }