summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/jit/flowgraph.cpp6
-rw-r--r--src/jit/gentree.h30
-rw-r--r--src/jit/importer.cpp83
-rw-r--r--src/jit/inline.h1
4 files changed, 100 insertions, 20 deletions
diff --git a/src/jit/flowgraph.cpp b/src/jit/flowgraph.cpp
index bb222c42dd..4f9c23b53f 100644
--- a/src/jit/flowgraph.cpp
+++ b/src/jit/flowgraph.cpp
@@ -23229,11 +23229,7 @@ GenTreePtr Compiler::fgInlinePrependStatements(InlineInfo* inlineInfo)
//
// Chase through any GT_RET_EXPRs to find the actual argument
// expression.
- GenTree* actualArgNode = argNode;
- while (actualArgNode->gtOper == GT_RET_EXPR)
- {
- actualArgNode = actualArgNode->gtRetExpr.gtInlineCandidate;
- }
+ GenTree* actualArgNode = argNode->gtRetExprVal();
// For case (1)
//
diff --git a/src/jit/gentree.h b/src/jit/gentree.h
index 09ce220871..9082945e6a 100644
--- a/src/jit/gentree.h
+++ b/src/jit/gentree.h
@@ -1813,6 +1813,9 @@ public:
inline GenTreePtr gtEffectiveVal(bool commaOnly = false);
+ // Tunnel through any GT_RET_EXPRs
+ inline GenTree* gtRetExprVal();
+
// Return the child of this node if it is a GT_RELOAD or GT_COPY; otherwise simply return the node itself
inline GenTree* gtSkipReloadOrCopy();
@@ -6130,6 +6133,33 @@ inline GenTreePtr GenTree::gtEffectiveVal(bool commaOnly)
}
}
+//-------------------------------------------------------------------------
+// gtRetExprVal - walk back through GT_RET_EXPRs
+//
+// Returns:
+// tree representing return value from a successful inline,
+// or original call for failed or yet to be determined inline.
+//
+// Notes:
+// Multi-level inlines can form chains of GT_RET_EXPRs.
+// This method walks back to the root of the chain.
+
+inline GenTree* GenTree::gtRetExprVal()
+{
+ GenTree* retExprVal = this;
+ for (;;)
+ {
+ if (retExprVal->gtOper == GT_RET_EXPR)
+ {
+ retExprVal = retExprVal->gtRetExpr.gtInlineCandidate;
+ }
+ else
+ {
+ return retExprVal;
+ }
+ }
+}
+
inline GenTree* GenTree::gtSkipReloadOrCopy()
{
// There can be only one reload or copy (we can't have a reload/copy of a reload/copy)
diff --git a/src/jit/importer.cpp b/src/jit/importer.cpp
index e1c96cd5be..d45de4bbe0 100644
--- a/src/jit/importer.cpp
+++ b/src/jit/importer.cpp
@@ -17919,8 +17919,24 @@ void Compiler::impCheckCanInline(GenTreePtr call,
}
}
+//------------------------------------------------------------------------
+// impInlineRecordArgInfo: record information about an inline candidate argument
+//
+// Arguments:
+// pInlineInfo - inline info for the inline candidate
+// curArgVal - tree for the caller actual argument value
+// argNum - logical index of this argument
+// inlineResult - result of ongoing inline evaluation
+//
+// Notes:
+//
+// Checks for various inline blocking conditions and makes notes in
+// the inline info arg table about the properties of the actual. These
+// properties are used later by impFetchArg to determine how best to
+// pass the argument into the inlinee.
+
void Compiler::impInlineRecordArgInfo(InlineInfo* pInlineInfo,
- GenTreePtr curArgVal,
+ GenTree* curArgVal,
unsigned argNum,
InlineResult* inlineResult)
{
@@ -17972,9 +17988,18 @@ void Compiler::impInlineRecordArgInfo(InlineInfo* pInlineInfo,
}
}
+ // If the arg is a local that is address-taken, we can't safely
+ // directly substitute it into the inlinee.
+ //
+ // Previously we'd accomplish this by setting "argHasLdargaOp" but
+ // that has a stronger meaning: that the arg value can change in
+ // the method body. Using that flag prevents type propagation,
+ // which is safe in this case.
+ //
+ // Instead mark the arg as having a caller local ref.
if (!inlCurArgInfo->argIsInvariant && gtHasLocalsWithAddrOp(curArgVal))
{
- inlCurArgInfo->argHasLdargaOp = true;
+ inlCurArgInfo->argHasCallerLocalRef = true;
}
#ifdef DEBUG
@@ -18000,6 +18025,10 @@ void Compiler::impInlineRecordArgInfo(InlineInfo* pInlineInfo,
{
printf(" has global refs");
}
+ if (inlCurArgInfo->argHasCallerLocalRef)
+ {
+ printf(" has caller local ref");
+ }
if (inlCurArgInfo->argHasSideEff)
{
printf(" has side effects");
@@ -18024,9 +18053,32 @@ void Compiler::impInlineRecordArgInfo(InlineInfo* pInlineInfo,
#endif
}
-/*****************************************************************************
- *
- */
+//------------------------------------------------------------------------
+// impInlineInitVars: setup inline information for inlinee args and locals
+//
+// Arguments:
+// pInlineInfo - inline info for the inline candidate
+//
+// Notes:
+// This method primarily adds caller-supplied info to the inlArgInfo
+// and sets up the lclVarInfo table.
+//
+// For args, the inlArgInfo records properties of the actual argument
+// including the tree node that produces the arg value. This node is
+// usually the tree node present at the call, but may also differ in
+// various ways:
+// - when the call arg is a GT_RET_EXPR, we search back through the ret
+// expr chain for the actual node. Note this will either be the original
+// call (which will be a failed inline by this point), or the return
+// expression from some set of inlines.
+// - when argument type casting is needed the necessary casts are added
+// around the argument node.
+// - if an argment can be simplified by folding then the node here is the
+// folded value.
+//
+// The method may make observations that lead to marking this candidate as
+// a failed inline. If this happens the initialization is abandoned immediately
+// to try and reduce the jit time cost for a failed inline.
void Compiler::impInlineInitVars(InlineInfo* pInlineInfo)
{
@@ -18056,8 +18108,8 @@ void Compiler::impInlineInitVars(InlineInfo* pInlineInfo)
if (thisArg)
{
inlArgInfo[0].argIsThis = true;
-
- impInlineRecordArgInfo(pInlineInfo, thisArg, argCnt, inlineResult);
+ GenTree* actualThisArg = thisArg->gtRetExprVal();
+ impInlineRecordArgInfo(pInlineInfo, actualThisArg, argCnt, inlineResult);
if (inlineResult->IsFailure())
{
@@ -18093,9 +18145,9 @@ void Compiler::impInlineInitVars(InlineInfo* pInlineInfo)
}
assert(argTmp->gtOper == GT_LIST);
- GenTreePtr argVal = argTmp->gtOp.gtOp1;
-
- impInlineRecordArgInfo(pInlineInfo, argVal, argCnt, inlineResult);
+ GenTree* arg = argTmp->gtOp.gtOp1;
+ GenTree* actualArg = arg->gtRetExprVal();
+ impInlineRecordArgInfo(pInlineInfo, actualArg, argCnt, inlineResult);
if (inlineResult->IsFailure())
{
@@ -18537,9 +18589,9 @@ GenTreePtr Compiler::impInlineFetchArg(unsigned lclNum, InlArgInfo* inlArgInfo,
PREFIX_ASSUME(op1 != nullptr);
argInfo.argTmpNum = BAD_VAR_NUM;
}
- else if (argInfo.argIsLclVar && !argCanBeModified)
+ else if (argInfo.argIsLclVar && !argCanBeModified && !argInfo.argHasCallerLocalRef)
{
- // Directly substitute caller locals
+ // Directly substitute unaliased caller locals for args that cannot be modified
//
// Use the caller-supplied node if this is the first use.
op1 = argInfo.argNode;
@@ -18628,7 +18680,7 @@ GenTreePtr Compiler::impInlineFetchArg(unsigned lclNum, InlArgInfo* inlArgInfo,
}
else
{
- // Arg might be modified, use the delcared type of
+ // Arg might be modified, use the declared type of
// the argument.
lvaSetClass(tmpNum, lclInfo.lclVerTypeInfo.GetClassHandleForObjRef());
}
@@ -18659,13 +18711,14 @@ GenTreePtr Compiler::impInlineFetchArg(unsigned lclNum, InlArgInfo* inlArgInfo,
// If we require strict exception order, then arguments must
// be evaluated in sequence before the body of the inlined method.
// So we need to evaluate them to a temp.
- // Also, if arguments have global references, we need to
+ // Also, if arguments have global or local references, we need to
// evaluate them to a temp before the inlined body as the
// inlined body may be modifying the global ref.
// TODO-1stClassStructs: We currently do not reuse an existing lclVar
// if it is a struct, because it requires some additional handling.
- if (!varTypeIsStruct(lclTyp) && !argInfo.argHasSideEff && !argInfo.argHasGlobRef)
+ if (!varTypeIsStruct(lclTyp) && !argInfo.argHasSideEff && !argInfo.argHasGlobRef &&
+ !argInfo.argHasCallerLocalRef)
{
/* Get a *LARGE* LCL_VAR node */
op1 = gtNewLclLNode(tmpNum, genActualType(lclTyp), lclNum);
diff --git a/src/jit/inline.h b/src/jit/inline.h
index 757efec6a5..fb1d7a2e7d 100644
--- a/src/jit/inline.h
+++ b/src/jit/inline.h
@@ -515,6 +515,7 @@ struct InlArgInfo
unsigned argIsThis : 1; // the argument is the 'this' pointer
unsigned argHasSideEff : 1; // the argument has side effects
unsigned argHasGlobRef : 1; // the argument has a global ref
+ unsigned argHasCallerLocalRef : 1; // the argument value depends on an aliased caller local
unsigned argHasTmp : 1; // the argument will be evaluated to a temp
unsigned argHasLdargaOp : 1; // Is there LDARGA(s) operation on this argument?
unsigned argHasStargOp : 1; // Is there STARG(s) operation on this argument?