1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
5 // Early Value Propagation
7 // This phase performs an SSA-based value propagation optimization that currently only applies to array
8 // lengths, runtime type handles, and explicit null checks. An SSA-based backwards tracking of local variables
9 // is performed at each point of interest, e.g., an array length reference site, a method table reference site, or
11 // The tracking continues until an interesting value is encountered. The value is then used to rewrite
12 // the source site or the value.
14 ///////////////////////////////////////////////////////////////////////////////////////
17 #include "ssabuilder.h"
19 bool Compiler::optDoEarlyPropForFunc()
21 bool propArrayLen = (optMethodFlags & OMF_HAS_NEWARRAY) && (optMethodFlags & OMF_HAS_ARRAYREF);
22 bool propGetType = (optMethodFlags & OMF_HAS_NEWOBJ) && (optMethodFlags & OMF_HAS_VTABLEREF);
23 bool propNullCheck = (optMethodFlags & OMF_HAS_NULLCHECK) != 0;
24 return propArrayLen || propGetType || propNullCheck;
27 bool Compiler::optDoEarlyPropForBlock(BasicBlock* block)
29 bool bbHasArrayRef = (block->bbFlags & BBF_HAS_IDX_LEN) != 0;
30 bool bbHasVtableRef = (block->bbFlags & BBF_HAS_VTABREF) != 0;
31 bool bbHasNullCheck = (block->bbFlags & BBF_HAS_NULLCHECK) != 0;
32 return bbHasArrayRef || bbHasVtableRef || bbHasNullCheck;
35 //--------------------------------------------------------------------
36 // gtIsVtableRef: Return true if the tree is a method table reference.
39 // tree - The input tree.
42 // Return true if the tree is a method table reference.
44 bool Compiler::gtIsVtableRef(GenTree* tree)
46 if (tree->OperGet() == GT_IND)
48 GenTree* addr = tree->AsIndir()->Addr();
50 if (addr->OperIsAddrMode())
52 GenTreeAddrMode* addrMode = addr->AsAddrMode();
54 return (!addrMode->HasIndex() && (addrMode->Base()->TypeGet() == TYP_REF));
61 //------------------------------------------------------------------------------
62 // getArrayLengthFromAllocation: Return the array length for an array allocation
66 // tree - The array allocation helper call.
69 // Return the array length node.
71 GenTree* Compiler::getArrayLengthFromAllocation(GenTree* tree)
73 assert(tree != nullptr);
75 if (tree->OperGet() == GT_CALL)
77 GenTreeCall* call = tree->AsCall();
79 if (call->gtCallType == CT_HELPER)
81 if (call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_NEWARR_1_DIRECT) ||
82 call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_NEWARR_1_R2R_DIRECT) ||
83 call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_NEWARR_1_OBJ) ||
84 call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_NEWARR_1_VC) ||
85 call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_NEWARR_1_ALIGN8))
87 // This is an array allocation site. Grab the array length node.
88 return gtArgEntryByArgNum(call, 1)->node;
96 //-----------------------------------------------------------------------------
97 // getObjectHandleNodeFromAllocation: Return the type handle for an object allocation
101 // tree - The object allocation helper call.
104 // Return the object type handle node.
106 GenTree* Compiler::getObjectHandleNodeFromAllocation(GenTree* tree)
108 assert(tree != nullptr);
110 if (tree->OperGet() == GT_CALL)
112 GenTreeCall* call = tree->AsCall();
114 if (call->gtCallType == CT_HELPER)
116 if (call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_NEWFAST) ||
117 call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_NEWSFAST) ||
118 call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_NEWSFAST_ALIGN8) ||
119 call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_NEWARR_1_DIRECT) ||
120 call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_NEWARR_1_R2R_DIRECT) ||
121 call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_NEWARR_1_OBJ) ||
122 call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_NEWARR_1_VC) ||
123 call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_NEWARR_1_ALIGN8))
125 // This is an object allocation site. Return the runtime type handle node.
126 fgArgTabEntry* argTabEntry = gtArgEntryByArgNum(call, 0);
127 return argTabEntry->node;
135 //------------------------------------------------------------------------------------------
136 // optEarlyProp: The entry point of the early value propagation.
139 // This phase performs an SSA-based value propagation, including
140 // 1. Array length propagation.
141 // 2. Runtime type handle propagation.
142 // 3. Null check folding.
144 // For array length propagation, a demand-driven SSA-based backwards tracking of constant
145 // array lengths is performed at each array length reference site which is in form of a
146 // GT_ARR_LENGTH node. When a GT_ARR_LENGTH node is seen, the array ref pointer which is
147 // the only child node of the GT_ARR_LENGTH is tracked. This is only done for array ref
148 // pointers that have valid SSA forms.The tracking is along SSA use-def chain and stops
149 // at the original array allocation site where we can grab the array length. The
150 // GT_ARR_LENGTH node will then be rewritten to a GT_CNS_INT node if the array length is
153 // Similarly, the same algorithm also applies to rewriting a method table (also known as
154 // vtable) reference site which is in form of GT_INDIR node. The base pointer, which is
155 // an object reference pointer, is treated in the same way as an array reference pointer.
157 // Null check folding tries to find GT_INDIR(obj + const) that GT_NULLCHECK(obj) can be folded into
158 /// and removed. Currently, the algorithm only matches GT_INDIR and GT_NULLCHECK in the same basic block.
160 void Compiler::optEarlyProp()
165 printf("*************** In optEarlyProp()\n");
169 assert(fgSsaPassesCompleted == 1);
171 if (!optDoEarlyPropForFunc())
176 for (BasicBlock* block = fgFirstBB; block != nullptr; block = block->bbNext)
178 if (!optDoEarlyPropForBlock(block))
185 for (GenTreeStmt* stmt = block->firstStmt(); stmt != nullptr;)
187 // Preserve the next link before the propagation and morph.
188 GenTreeStmt* next = stmt->gtNextStmt;
192 // Walk the stmt tree in linear order to rewrite any array length reference with a
193 // constant array length.
194 bool isRewritten = false;
195 for (GenTree* tree = stmt->gtStmt.gtStmtList; tree != nullptr; tree = tree->gtNext)
197 GenTree* rewrittenTree = optEarlyPropRewriteTree(tree);
198 if (rewrittenTree != nullptr)
200 gtUpdateSideEffects(stmt, rewrittenTree);
202 tree = rewrittenTree;
206 // Update the evaluation order and the statement info if the stmt has been rewritten.
220 JITDUMP("\nAfter optEarlyProp:\n");
221 fgDispBasicBlocks(/*dumpTrees*/ true);
226 //----------------------------------------------------------------
227 // optEarlyPropRewriteValue: Rewrite a tree to the actual value.
230 // tree - The input tree node to be rewritten.
233 // Return a new tree if the original tree was successfully rewritten.
234 // The containing tree links are updated.
236 GenTree* Compiler::optEarlyPropRewriteTree(GenTree* tree)
238 GenTree* objectRefPtr = nullptr;
239 optPropKind propKind = optPropKind::OPK_INVALID;
241 if (tree->OperGet() == GT_ARR_LENGTH)
243 objectRefPtr = tree->gtOp.gtOp1;
244 propKind = optPropKind::OPK_ARRAYLEN;
246 else if (tree->OperIsIndir())
248 // optFoldNullCheck takes care of updating statement info if a null check is removed.
249 optFoldNullCheck(tree);
251 if (gtIsVtableRef(tree))
253 // Don't propagate type handles that are used as null checks, which are usually in
255 // * stmtExpr void (top level)
257 // \--* lclVar ref V02 loc0
258 if (compCurStmt->gtStmt.gtStmtExpr == tree)
263 objectRefPtr = tree->AsIndir()->Addr();
264 propKind = optPropKind::OPK_OBJ_GETTYPE;
276 if (!objectRefPtr->OperIsScalarLocal() || fgExcludeFromSsa(objectRefPtr->AsLclVarCommon()->GetLclNum()))
282 unsigned lclNum = objectRefPtr->AsLclVarCommon()->GetLclNum();
283 unsigned ssaNum = objectRefPtr->AsLclVarCommon()->GetSsaNum();
284 GenTree* actualVal = optPropGetValue(lclNum, ssaNum, propKind);
286 if (actualVal != nullptr)
288 assert((propKind == optPropKind::OPK_ARRAYLEN) || (propKind == optPropKind::OPK_OBJ_GETTYPE));
289 assert(actualVal->IsCnsIntOrI());
291 assert(actualVal->GetNodeSize() == TREE_NODE_SZ_SMALL);
294 ssize_t actualConstVal = actualVal->AsIntCon()->IconValue();
296 if (propKind == optPropKind::OPK_ARRAYLEN)
298 if ((actualConstVal < 0) || (actualConstVal > INT32_MAX))
300 // Don't propagate array lengths that are beyond the maximum value of a GT_ARR_LENGTH or negative.
301 // node. CORINFO_HELP_NEWARR_1_OBJ helper call allows to take a long integer as the
302 // array length argument, but the type of GT_ARR_LENGTH is always INT32.
306 // When replacing GT_ARR_LENGTH nodes with constants we can end up with GT_ARR_BOUNDS_CHECK
307 // nodes that have constant operands and thus can be trivially proved to be useless. It's
308 // better to remove these range checks here, otherwise they'll pass through assertion prop
309 // (creating useless (c1 < c2)-like assertions) and reach RangeCheck where they are finally
310 // removed. Common patterns like new int[] { x, y, z } benefit from this.
312 if ((tree->gtNext != nullptr) && tree->gtNext->OperIs(GT_ARR_BOUNDS_CHECK))
314 GenTreeBoundsChk* check = tree->gtNext->AsBoundsChk();
316 if ((check->gtArrLen == tree) && check->gtIndex->IsCnsIntOrI())
318 ssize_t checkConstVal = check->gtIndex->AsIntCon()->IconValue();
319 if ((checkConstVal >= 0) && (checkConstVal < actualConstVal))
321 GenTree* comma = check->gtGetParent(nullptr);
322 if ((comma != nullptr) && comma->OperIs(GT_COMMA) && (comma->gtGetOp1() == check))
324 GenTree* next = check->gtNext;
325 optRemoveRangeCheck(comma, compCurStmt);
326 // Both `tree` and `check` have been removed from the statement.
327 // 'tree' was replaced with 'nop' or side effect list under 'comma'.
328 return comma->gtGetOp1();
338 printf("optEarlyProp Rewriting BB%02u\n", compCurBB->bbNum);
339 gtDispTree(compCurStmt);
344 GenTree* actualValClone = gtCloneExpr(actualVal);
346 if (actualValClone->gtType != tree->gtType)
348 assert(actualValClone->gtType == TYP_LONG);
349 assert(tree->gtType == TYP_INT);
350 assert((actualConstVal >= 0) && (actualConstVal <= INT32_MAX));
351 actualValClone->gtType = tree->gtType;
354 // Propagating a constant into an array index expression requires calling
355 // LabelIndex to update the FieldSeq annotations. EarlyProp may replace
356 // array length expressions with constants, so check if this is an array
357 // length operator that is part of an array index expression.
358 bool isIndexExpr = (tree->OperGet() == GT_ARR_LENGTH && ((tree->gtFlags & GTF_ARRLEN_ARR_IDX) != 0));
361 actualValClone->LabelIndex(this);
364 DecLclVarRefCountsVisitor::WalkTree(this, tree);
365 // acutalValClone has small tree node size, it is safe to use CopyFrom here.
366 tree->ReplaceWith(actualValClone, this);
367 IncLclVarRefCountsVisitor::WalkTree(this, tree);
373 gtDispTree(compCurStmt);
383 //-------------------------------------------------------------------------------------------
384 // optPropGetValue: Given an SSA object ref pointer, get the value needed based on valueKind.
387 // lclNum - The local var number of the ref pointer.
388 // ssaNum - The SSA var number of the ref pointer.
389 // valueKind - The kind of value of interest.
392 // Return the corresponding value based on valueKind.
394 GenTree* Compiler::optPropGetValue(unsigned lclNum, unsigned ssaNum, optPropKind valueKind)
396 return optPropGetValueRec(lclNum, ssaNum, valueKind, 0);
399 //-----------------------------------------------------------------------------------
400 // optPropGetValueRec: Given an SSA object ref pointer, get the value needed based on valueKind
401 // within a recursion bound.
404 // lclNum - The local var number of the array pointer.
405 // ssaNum - The SSA var number of the array pointer.
406 // valueKind - The kind of value of interest.
407 // walkDepth - Current recursive walking depth.
410 // Return the corresponding value based on valueKind.
412 GenTree* Compiler::optPropGetValueRec(unsigned lclNum, unsigned ssaNum, optPropKind valueKind, int walkDepth)
414 if (ssaNum == SsaConfig::RESERVED_SSA_NUM)
419 SSAName ssaName(lclNum, ssaNum);
420 GenTree* value = nullptr;
422 // Bound the recursion with a hard limit.
423 if (walkDepth > optEarlyPropRecurBound)
428 // Track along the use-def chain to get the array length
429 GenTree* treelhs = lvaTable[lclNum].GetPerSsaData(ssaNum)->m_defLoc.m_tree;
431 if (treelhs == nullptr)
433 // Incoming parameters or live-in variables don't have actual definition tree node
434 // for their FIRST_SSA_NUM. See SsaBuilder::RenameVariables.
435 assert(ssaNum == SsaConfig::FIRST_SSA_NUM);
440 GenTree* treeDefParent = treelhs->gtGetParent(&lhsPtr);
442 if (treeDefParent->OperGet() == GT_ASG)
444 assert(treelhs == treeDefParent->gtGetOp1());
445 GenTree* treeRhs = treeDefParent->gtGetOp2();
447 if (treeRhs->OperIsScalarLocal() && !fgExcludeFromSsa(treeRhs->AsLclVarCommon()->GetLclNum()))
449 // Recursively track the Rhs
450 unsigned rhsLclNum = treeRhs->AsLclVarCommon()->GetLclNum();
451 unsigned rhsSsaNum = treeRhs->AsLclVarCommon()->GetSsaNum();
453 value = optPropGetValueRec(rhsLclNum, rhsSsaNum, valueKind, walkDepth + 1);
457 if (valueKind == optPropKind::OPK_ARRAYLEN)
459 value = getArrayLengthFromAllocation(treeRhs);
460 if (value != nullptr)
462 if (!value->IsCnsIntOrI())
464 // Leave out non-constant-sized array
469 else if (valueKind == optPropKind::OPK_OBJ_GETTYPE)
471 value = getObjectHandleNodeFromAllocation(treeRhs);
472 if (value != nullptr)
474 if (!value->IsCnsIntOrI())
476 // Leave out non-constant-sized array
488 //----------------------------------------------------------------
489 // optFoldNullChecks: Try to find a GT_NULLCHECK node that can be folded into the GT_INDIR node.
492 // tree - The input GT_INDIR tree.
495 void Compiler::optFoldNullCheck(GenTree* tree)
498 // Check for a pattern like this:
509 // some trees in the same
511 // no unsafe side effects
517 // where the const is suitably small
518 // and transform it into
527 // some trees with no unsafe side effects here
533 if ((compCurBB->bbFlags & BBF_HAS_NULLCHECK) == 0)
538 assert(tree->OperIsIndir());
540 GenTree* const addr = tree->AsIndir()->Addr();
541 if (addr->OperGet() == GT_LCL_VAR)
543 // Check if we have the pattern above and find the nullcheck node if we do.
545 // Find the definition of the indirected local (x in the picture)
546 GenTreeLclVarCommon* const lclVarNode = addr->AsLclVarCommon();
548 const unsigned lclNum = lclVarNode->GetLclNum();
549 const unsigned ssaNum = lclVarNode->GetSsaNum();
551 if (ssaNum != SsaConfig::RESERVED_SSA_NUM)
553 DefLoc defLoc = lvaTable[lclNum].GetPerSsaData(ssaNum)->m_defLoc;
554 BasicBlock* defBlock = defLoc.m_blk;
556 if (compCurBB == defBlock)
558 GenTree* defTree = defLoc.m_tree;
559 GenTree* defParent = defTree->gtGetParent(nullptr);
561 if ((defParent->OperGet() == GT_ASG) && (defParent->gtNext == nullptr))
563 GenTree* defRHS = defParent->gtGetOp2();
564 if (defRHS->OperGet() == GT_COMMA)
566 if (defRHS->gtGetOp1()->OperGet() == GT_NULLCHECK)
568 GenTree* nullCheckTree = defRHS->gtGetOp1();
569 if (nullCheckTree->gtGetOp1()->OperGet() == GT_LCL_VAR)
571 // We found a candidate for 'y' in the picture
572 unsigned nullCheckLclNum = nullCheckTree->gtGetOp1()->AsLclVarCommon()->GetLclNum();
574 if (defRHS->gtGetOp2()->OperGet() == GT_ADD)
576 GenTree* additionNode = defRHS->gtGetOp2();
577 if ((additionNode->gtGetOp1()->OperGet() == GT_LCL_VAR) &&
578 (additionNode->gtGetOp1()->gtLclVarCommon.gtLclNum == nullCheckLclNum))
580 GenTree* offset = additionNode->gtGetOp2();
581 if (offset->IsCnsIntOrI())
583 if (!fgIsBigOffset(offset->gtIntConCommon.IconValue()))
585 // Walk from the use to the def in reverse execution order to see
586 // if any nodes have unsafe side effects.
587 GenTree* currentTree = lclVarNode->gtPrev;
588 bool isInsideTry = compCurBB->hasTryIndex();
589 bool canRemoveNullCheck = true;
590 const unsigned maxNodesWalked = 25;
591 unsigned nodesWalked = 0;
593 // First walk the nodes in the statement containing the indirection
594 // in reverse execution order starting with the indirection's
596 while (canRemoveNullCheck && (currentTree != nullptr))
598 if ((nodesWalked++ > maxNodesWalked) ||
599 !optCanMoveNullCheckPastTree(currentTree, isInsideTry))
601 canRemoveNullCheck = false;
605 currentTree = currentTree->gtPrev;
609 // Then walk the statement list in reverse execution order
610 // until we get to the statement containing the null check.
611 // We only need to check the side effects at the root of each statement.
612 GenTree* curStmt = compCurStmt->gtPrev;
613 currentTree = curStmt->gtStmt.gtStmtExpr;
614 while (canRemoveNullCheck && (currentTree != defParent))
616 if ((nodesWalked++ > maxNodesWalked) ||
617 !optCanMoveNullCheckPastTree(currentTree, isInsideTry))
619 canRemoveNullCheck = false;
623 curStmt = curStmt->gtStmt.gtPrevStmt;
624 assert(curStmt != nullptr);
625 currentTree = curStmt->gtStmt.gtStmtExpr;
629 if (canRemoveNullCheck)
631 // Remove the null check
632 nullCheckTree->gtFlags &= ~(GTF_EXCEPT | GTF_DONT_CSE);
634 // Set this flag to prevent reordering
635 nullCheckTree->gtFlags |= GTF_ORDER_SIDEEFF;
636 nullCheckTree->gtFlags |= GTF_IND_NONFAULTING;
638 defRHS->gtFlags &= ~(GTF_EXCEPT | GTF_DONT_CSE);
640 additionNode->gtFlags & (GTF_EXCEPT | GTF_DONT_CSE);
642 // Re-morph the statement.
643 fgMorphBlockStmt(compCurBB,
644 curStmt->AsStmt() DEBUGARG("optFoldNullCheck"));
659 //----------------------------------------------------------------
660 // optCanMoveNullCheckPastTree: Check if GT_NULLCHECK can be folded into a node that
661 // is after tree is execution order.
664 // tree - The input GT_INDIR tree.
665 // isInsideTry - True if tree is inside try, false otherwise
668 // True if GT_NULLCHECK can be folded into a node that is after tree is execution order,
671 bool Compiler::optCanMoveNullCheckPastTree(GenTree* tree, bool isInsideTry)
676 // We disallow calls, exception sources, and all assignments.
677 // Assignments to locals are disallowed inside try because
678 // they may be live in the handler.
679 if ((tree->gtFlags & GTF_SIDE_EFFECT) != 0)
686 // We disallow calls, exception sources, and assignments to
688 if (GTF_GLOBALLY_VISIBLE_SIDE_EFFECTS(tree->gtFlags))