summaryrefslogtreecommitdiff
path: root/src/jit/optimizer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/jit/optimizer.cpp')
-rw-r--r--src/jit/optimizer.cpp82
1 files changed, 63 insertions, 19 deletions
diff --git a/src/jit/optimizer.cpp b/src/jit/optimizer.cpp
index 710dac540c..1e50e537e0 100644
--- a/src/jit/optimizer.cpp
+++ b/src/jit/optimizer.cpp
@@ -2838,6 +2838,11 @@ void Compiler::optUnrollLoops()
// to outermost order
for (unsigned lnum = optLoopCount - 1; lnum != ~0U; --lnum)
{
+ // This is necessary due to an apparent analysis limitation since
+ // optLoopCount must be strictly greater than 0 upon entry and lnum
+ // cannot wrap due to the loop termination condition.
+ PREFAST_ASSUME(lnum != 0U - 1);
+
BasicBlock* block;
BasicBlock* head;
BasicBlock* bottom;
@@ -6003,7 +6008,9 @@ void Compiler::optHoistLoopExprsForBlock(BasicBlock* blk, unsigned lnum, LoopHoi
{
GenTreePtr stmtTree = stmt->gtStmtExpr;
bool hoistable;
- (void)optHoistLoopExprsForTree(stmtTree, lnum, hoistCtxt, &firstBlockAndBeforeSideEffect, &hoistable);
+ bool cctorDependent;
+ (void)optHoistLoopExprsForTree(stmtTree, lnum, hoistCtxt, &firstBlockAndBeforeSideEffect, &hoistable,
+ &cctorDependent);
if (hoistable)
{
// we will try to hoist the top-level stmtTree
@@ -6109,43 +6116,87 @@ bool Compiler::optIsProfitableToHoistableTree(GenTreePtr tree, unsigned lnum)
//
// This function returns true if 'tree' is a loop invariant expression.
-// It also sets '*pHoistable' to true if 'tree' can be hoisted into a loop PreHeader block
+// It also sets '*pHoistable' to true if 'tree' can be hoisted into a loop PreHeader block,
+// and sets '*pCctorDependent' if 'tree' is a function of a static field that must not be
+// hoisted (even if '*pHoistable' is true) unless a preceding corresponding cctor init helper
+// call is also hoisted.
//
-bool Compiler::optHoistLoopExprsForTree(
- GenTreePtr tree, unsigned lnum, LoopHoistContext* hoistCtxt, bool* pFirstBlockAndBeforeSideEffect, bool* pHoistable)
+bool Compiler::optHoistLoopExprsForTree(GenTreePtr tree,
+ unsigned lnum,
+ LoopHoistContext* hoistCtxt,
+ bool* pFirstBlockAndBeforeSideEffect,
+ bool* pHoistable,
+ bool* pCctorDependent)
{
// First do the children.
// We must keep track of whether each child node was hoistable or not
//
unsigned nChildren = tree->NumChildren();
bool childrenHoistable[GenTree::MAX_CHILDREN];
+ bool childrenCctorDependent[GenTree::MAX_CHILDREN];
// Initialize the array elements for childrenHoistable[] to false
for (unsigned i = 0; i < nChildren; i++)
{
- childrenHoistable[i] = false;
+ childrenHoistable[i] = false;
+ childrenCctorDependent[i] = false;
}
+ // Initclass CLS_VARs and IconHandles are the base cases of cctor dependent trees.
+ // In the IconHandle case, it's of course the dereference, rather than the constant itself, that is
+ // truly dependent on the cctor. So a more precise approach would be to separately propagate
+ // isCctorDependent and isAddressWhoseDereferenceWouldBeCctorDependent, but we don't for simplicity/throughput;
+ // the constant itself would be considered non-hoistable anyway, since optIsCSEcandidate returns
+ // false for constants.
+ bool treeIsCctorDependent = ((tree->OperIs(GT_CLS_VAR) && ((tree->gtFlags & GTF_CLS_VAR_INITCLASS) != 0)) ||
+ (tree->OperIs(GT_CNS_INT) && ((tree->gtFlags & GTF_ICON_INITCLASS) != 0)));
bool treeIsInvariant = true;
for (unsigned childNum = 0; childNum < nChildren; childNum++)
{
if (!optHoistLoopExprsForTree(tree->GetChild(childNum), lnum, hoistCtxt, pFirstBlockAndBeforeSideEffect,
- &childrenHoistable[childNum]))
+ &childrenHoistable[childNum], &childrenCctorDependent[childNum]))
{
treeIsInvariant = false;
}
+
+ if (childrenCctorDependent[childNum])
+ {
+ // Normally, a parent of a cctor-dependent tree is also cctor-dependent.
+ treeIsCctorDependent = true;
+
+ // Check for the case where we can stop propagating cctor-dependent upwards.
+ if (tree->OperIs(GT_COMMA) && (childNum == 1))
+ {
+ GenTreePtr op1 = tree->gtGetOp1();
+ if (op1->OperIs(GT_CALL))
+ {
+ GenTreeCall* call = op1->AsCall();
+ if ((call->gtCallType == CT_HELPER) &&
+ s_helperCallProperties.MayRunCctor(eeGetHelperNum(call->gtCallMethHnd)))
+ {
+ // Hoisting the comma is ok because it would hoist the initialization along
+ // with the static field reference.
+ treeIsCctorDependent = false;
+ // Hoisting the static field without hoisting the initialization would be
+ // incorrect, make sure we consider the field (which we flagged as
+ // cctor-dependent) non-hoistable.
+ noway_assert(!childrenHoistable[childNum]);
+ }
+ }
+ }
+ }
}
- // If all the children of "tree" are hoistable, then "tree" itself can be hoisted
- //
- bool treeIsHoistable = treeIsInvariant;
+ // If all the children of "tree" are hoistable, then "tree" itself can be hoisted,
+ // unless it has a static var reference that can't be hoisted past its cctor call.
+ bool treeIsHoistable = treeIsInvariant && !treeIsCctorDependent;
// But we must see if anything else prevents "tree" from being hoisted.
//
if (treeIsInvariant)
{
// Tree must be a suitable CSE candidate for us to be able to hoist it.
- treeIsHoistable = optIsCSEcandidate(tree);
+ treeIsHoistable &= optIsCSEcandidate(tree);
// If it's a call, it must be a helper call, and be pure.
// Further, if it may run a cctor, it must be labeled as "Hoistable"
@@ -6184,14 +6235,6 @@ bool Compiler::optHoistLoopExprsForTree(
treeIsHoistable = false;
}
}
- // Currently we must give up on reads from static variables (even if we are in the first block).
- //
- if (tree->OperGet() == GT_CLS_VAR)
- {
- // TODO-CQ: test that fails if we hoist GT_CLS_VAR: JIT\Directed\Languages\ComponentPascal\pi_r.exe
- // method Main
- treeIsHoistable = false;
- }
}
// Is the value of the whole tree loop invariant?
@@ -6285,7 +6328,8 @@ bool Compiler::optHoistLoopExprsForTree(
}
}
- *pHoistable = treeIsHoistable;
+ *pHoistable = treeIsHoistable;
+ *pCctorDependent = treeIsCctorDependent;
return treeIsInvariant;
}