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. |
4 | // |
5 | // Early Value Propagation |
6 | // |
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 |
10 | // an indirection. |
11 | // The tracking continues until an interesting value is encountered. The value is then used to rewrite |
12 | // the source site or the value. |
13 | // |
14 | /////////////////////////////////////////////////////////////////////////////////////// |
15 | |
16 | #include "jitpch.h" |
17 | #include "ssabuilder.h" |
18 | |
19 | bool Compiler::optDoEarlyPropForFunc() |
20 | { |
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; |
25 | } |
26 | |
27 | bool Compiler::optDoEarlyPropForBlock(BasicBlock* block) |
28 | { |
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; |
33 | } |
34 | |
35 | //-------------------------------------------------------------------- |
36 | // gtIsVtableRef: Return true if the tree is a method table reference. |
37 | // |
38 | // Arguments: |
39 | // tree - The input tree. |
40 | // |
41 | // Return Value: |
42 | // Return true if the tree is a method table reference. |
43 | |
44 | bool Compiler::gtIsVtableRef(GenTree* tree) |
45 | { |
46 | if (tree->OperGet() == GT_IND) |
47 | { |
48 | GenTree* addr = tree->AsIndir()->Addr(); |
49 | |
50 | if (addr->OperIsAddrMode()) |
51 | { |
52 | GenTreeAddrMode* addrMode = addr->AsAddrMode(); |
53 | |
54 | return (!addrMode->HasIndex() && (addrMode->Base()->TypeGet() == TYP_REF)); |
55 | } |
56 | } |
57 | |
58 | return false; |
59 | } |
60 | |
61 | //------------------------------------------------------------------------------ |
62 | // getArrayLengthFromAllocation: Return the array length for an array allocation |
63 | // helper call. |
64 | // |
65 | // Arguments: |
66 | // tree - The array allocation helper call. |
67 | // |
68 | // Return Value: |
69 | // Return the array length node. |
70 | |
71 | GenTree* Compiler::getArrayLengthFromAllocation(GenTree* tree) |
72 | { |
73 | assert(tree != nullptr); |
74 | |
75 | if (tree->OperGet() == GT_CALL) |
76 | { |
77 | GenTreeCall* call = tree->AsCall(); |
78 | |
79 | if (call->gtCallType == CT_HELPER) |
80 | { |
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)) |
86 | { |
87 | // This is an array allocation site. Grab the array length node. |
88 | return gtArgEntryByArgNum(call, 1)->node; |
89 | } |
90 | } |
91 | } |
92 | |
93 | return nullptr; |
94 | } |
95 | |
96 | //----------------------------------------------------------------------------- |
97 | // getObjectHandleNodeFromAllocation: Return the type handle for an object allocation |
98 | // helper call. |
99 | // |
100 | // Arguments: |
101 | // tree - The object allocation helper call. |
102 | // |
103 | // Return Value: |
104 | // Return the object type handle node. |
105 | |
106 | GenTree* Compiler::getObjectHandleNodeFromAllocation(GenTree* tree) |
107 | { |
108 | assert(tree != nullptr); |
109 | |
110 | if (tree->OperGet() == GT_CALL) |
111 | { |
112 | GenTreeCall* call = tree->AsCall(); |
113 | |
114 | if (call->gtCallType == CT_HELPER) |
115 | { |
116 | if (call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_NEWFAST) || |
117 | call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_NEWSFAST) || |
118 | call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_NEWSFAST_FINALIZE) || |
119 | call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_NEWSFAST_ALIGN8) || |
120 | call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_NEWSFAST_ALIGN8_VC) || |
121 | call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_NEWSFAST_ALIGN8_FINALIZE) || |
122 | call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_NEWARR_1_DIRECT) || |
123 | call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_NEWARR_1_R2R_DIRECT) || |
124 | call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_NEWARR_1_OBJ) || |
125 | call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_NEWARR_1_VC) || |
126 | call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_NEWARR_1_ALIGN8)) |
127 | { |
128 | // This is an object allocation site. Return the runtime type handle node. |
129 | fgArgTabEntry* argTabEntry = gtArgEntryByArgNum(call, 0); |
130 | return argTabEntry->node; |
131 | } |
132 | } |
133 | } |
134 | |
135 | return nullptr; |
136 | } |
137 | |
138 | //------------------------------------------------------------------------------------------ |
139 | // optEarlyProp: The entry point of the early value propagation. |
140 | // |
141 | // Notes: |
142 | // This phase performs an SSA-based value propagation, including |
143 | // 1. Array length propagation. |
144 | // 2. Runtime type handle propagation. |
145 | // 3. Null check folding. |
146 | // |
147 | // For array length propagation, a demand-driven SSA-based backwards tracking of constant |
148 | // array lengths is performed at each array length reference site which is in form of a |
149 | // GT_ARR_LENGTH node. When a GT_ARR_LENGTH node is seen, the array ref pointer which is |
150 | // the only child node of the GT_ARR_LENGTH is tracked. This is only done for array ref |
151 | // pointers that have valid SSA forms.The tracking is along SSA use-def chain and stops |
152 | // at the original array allocation site where we can grab the array length. The |
153 | // GT_ARR_LENGTH node will then be rewritten to a GT_CNS_INT node if the array length is |
154 | // constant. |
155 | // |
156 | // Similarly, the same algorithm also applies to rewriting a method table (also known as |
157 | // vtable) reference site which is in form of GT_INDIR node. The base pointer, which is |
158 | // an object reference pointer, is treated in the same way as an array reference pointer. |
159 | // |
160 | // Null check folding tries to find GT_INDIR(obj + const) that GT_NULLCHECK(obj) can be folded into |
161 | // and removed. Currently, the algorithm only matches GT_INDIR and GT_NULLCHECK in the same basic block. |
162 | |
163 | void Compiler::optEarlyProp() |
164 | { |
165 | #ifdef DEBUG |
166 | if (verbose) |
167 | { |
168 | printf("*************** In optEarlyProp()\n" ); |
169 | } |
170 | #endif |
171 | |
172 | assert(fgSsaPassesCompleted == 1); |
173 | |
174 | if (!optDoEarlyPropForFunc()) |
175 | { |
176 | return; |
177 | } |
178 | |
179 | for (BasicBlock* block = fgFirstBB; block != nullptr; block = block->bbNext) |
180 | { |
181 | if (!optDoEarlyPropForBlock(block)) |
182 | { |
183 | continue; |
184 | } |
185 | |
186 | compCurBB = block; |
187 | |
188 | for (GenTreeStmt* stmt = block->firstStmt(); stmt != nullptr;) |
189 | { |
190 | // Preserve the next link before the propagation and morph. |
191 | GenTreeStmt* next = stmt->gtNextStmt; |
192 | |
193 | compCurStmt = stmt; |
194 | |
195 | // Walk the stmt tree in linear order to rewrite any array length reference with a |
196 | // constant array length. |
197 | bool isRewritten = false; |
198 | for (GenTree* tree = stmt->gtStmt.gtStmtList; tree != nullptr; tree = tree->gtNext) |
199 | { |
200 | GenTree* rewrittenTree = optEarlyPropRewriteTree(tree); |
201 | if (rewrittenTree != nullptr) |
202 | { |
203 | gtUpdateSideEffects(stmt, rewrittenTree); |
204 | isRewritten = true; |
205 | tree = rewrittenTree; |
206 | } |
207 | } |
208 | |
209 | // Update the evaluation order and the statement info if the stmt has been rewritten. |
210 | if (isRewritten) |
211 | { |
212 | gtSetStmtInfo(stmt); |
213 | fgSetStmtSeq(stmt); |
214 | } |
215 | |
216 | stmt = next; |
217 | } |
218 | } |
219 | |
220 | #ifdef DEBUG |
221 | if (verbose) |
222 | { |
223 | JITDUMP("\nAfter optEarlyProp:\n" ); |
224 | fgDispBasicBlocks(/*dumpTrees*/ true); |
225 | } |
226 | #endif |
227 | } |
228 | |
229 | //---------------------------------------------------------------- |
230 | // optEarlyPropRewriteValue: Rewrite a tree to the actual value. |
231 | // |
232 | // Arguments: |
233 | // tree - The input tree node to be rewritten. |
234 | // |
235 | // Return Value: |
236 | // Return a new tree if the original tree was successfully rewritten. |
237 | // The containing tree links are updated. |
238 | // |
239 | GenTree* Compiler::optEarlyPropRewriteTree(GenTree* tree) |
240 | { |
241 | GenTree* objectRefPtr = nullptr; |
242 | optPropKind propKind = optPropKind::OPK_INVALID; |
243 | |
244 | if (tree->OperGet() == GT_ARR_LENGTH) |
245 | { |
246 | objectRefPtr = tree->gtOp.gtOp1; |
247 | propKind = optPropKind::OPK_ARRAYLEN; |
248 | } |
249 | else if (tree->OperIsIndir()) |
250 | { |
251 | // optFoldNullCheck takes care of updating statement info if a null check is removed. |
252 | optFoldNullCheck(tree); |
253 | |
254 | if (gtIsVtableRef(tree)) |
255 | { |
256 | // Don't propagate type handles that are used as null checks, which are usually in |
257 | // form of |
258 | // * stmtExpr void (top level) |
259 | // \--* indir int |
260 | // \--* lclVar ref V02 loc0 |
261 | if (compCurStmt->gtStmt.gtStmtExpr == tree) |
262 | { |
263 | return nullptr; |
264 | } |
265 | |
266 | objectRefPtr = tree->AsIndir()->Addr(); |
267 | propKind = optPropKind::OPK_OBJ_GETTYPE; |
268 | } |
269 | else |
270 | { |
271 | return nullptr; |
272 | } |
273 | } |
274 | else |
275 | { |
276 | return nullptr; |
277 | } |
278 | |
279 | if (!objectRefPtr->OperIsScalarLocal() || !lvaInSsa(objectRefPtr->AsLclVarCommon()->GetLclNum())) |
280 | |
281 | { |
282 | return nullptr; |
283 | } |
284 | |
285 | unsigned lclNum = objectRefPtr->AsLclVarCommon()->GetLclNum(); |
286 | unsigned ssaNum = objectRefPtr->AsLclVarCommon()->GetSsaNum(); |
287 | GenTree* actualVal = optPropGetValue(lclNum, ssaNum, propKind); |
288 | |
289 | if (actualVal != nullptr) |
290 | { |
291 | assert((propKind == optPropKind::OPK_ARRAYLEN) || (propKind == optPropKind::OPK_OBJ_GETTYPE)); |
292 | assert(actualVal->IsCnsIntOrI()); |
293 | #if SMALL_TREE_NODES |
294 | assert(actualVal->GetNodeSize() == TREE_NODE_SZ_SMALL); |
295 | #endif |
296 | |
297 | ssize_t actualConstVal = actualVal->AsIntCon()->IconValue(); |
298 | |
299 | if (propKind == optPropKind::OPK_ARRAYLEN) |
300 | { |
301 | if ((actualConstVal < 0) || (actualConstVal > INT32_MAX)) |
302 | { |
303 | // Don't propagate array lengths that are beyond the maximum value of a GT_ARR_LENGTH or negative. |
304 | // node. CORINFO_HELP_NEWARR_1_OBJ helper call allows to take a long integer as the |
305 | // array length argument, but the type of GT_ARR_LENGTH is always INT32. |
306 | return nullptr; |
307 | } |
308 | |
309 | // When replacing GT_ARR_LENGTH nodes with constants we can end up with GT_ARR_BOUNDS_CHECK |
310 | // nodes that have constant operands and thus can be trivially proved to be useless. It's |
311 | // better to remove these range checks here, otherwise they'll pass through assertion prop |
312 | // (creating useless (c1 < c2)-like assertions) and reach RangeCheck where they are finally |
313 | // removed. Common patterns like new int[] { x, y, z } benefit from this. |
314 | |
315 | if ((tree->gtNext != nullptr) && tree->gtNext->OperIs(GT_ARR_BOUNDS_CHECK)) |
316 | { |
317 | GenTreeBoundsChk* check = tree->gtNext->AsBoundsChk(); |
318 | |
319 | if ((check->gtArrLen == tree) && check->gtIndex->IsCnsIntOrI()) |
320 | { |
321 | ssize_t checkConstVal = check->gtIndex->AsIntCon()->IconValue(); |
322 | if ((checkConstVal >= 0) && (checkConstVal < actualConstVal)) |
323 | { |
324 | GenTree* comma = check->gtGetParent(nullptr); |
325 | if ((comma != nullptr) && comma->OperIs(GT_COMMA) && (comma->gtGetOp1() == check)) |
326 | { |
327 | GenTree* next = check->gtNext; |
328 | optRemoveRangeCheck(comma, compCurStmt); |
329 | // Both `tree` and `check` have been removed from the statement. |
330 | // 'tree' was replaced with 'nop' or side effect list under 'comma'. |
331 | return comma->gtGetOp1(); |
332 | } |
333 | } |
334 | } |
335 | } |
336 | } |
337 | |
338 | #ifdef DEBUG |
339 | if (verbose) |
340 | { |
341 | printf("optEarlyProp Rewriting " FMT_BB "\n" , compCurBB->bbNum); |
342 | gtDispTree(compCurStmt); |
343 | printf("\n" ); |
344 | } |
345 | #endif |
346 | |
347 | GenTree* actualValClone = gtCloneExpr(actualVal); |
348 | |
349 | if (actualValClone->gtType != tree->gtType) |
350 | { |
351 | assert(actualValClone->gtType == TYP_LONG); |
352 | assert(tree->gtType == TYP_INT); |
353 | assert((actualConstVal >= 0) && (actualConstVal <= INT32_MAX)); |
354 | actualValClone->gtType = tree->gtType; |
355 | } |
356 | |
357 | // Propagating a constant into an array index expression requires calling |
358 | // LabelIndex to update the FieldSeq annotations. EarlyProp may replace |
359 | // array length expressions with constants, so check if this is an array |
360 | // length operator that is part of an array index expression. |
361 | bool isIndexExpr = (tree->OperGet() == GT_ARR_LENGTH && ((tree->gtFlags & GTF_ARRLEN_ARR_IDX) != 0)); |
362 | if (isIndexExpr) |
363 | { |
364 | actualValClone->LabelIndex(this); |
365 | } |
366 | |
367 | // actualValClone has small tree node size, it is safe to use CopyFrom here. |
368 | tree->ReplaceWith(actualValClone, this); |
369 | |
370 | #ifdef DEBUG |
371 | if (verbose) |
372 | { |
373 | printf("to\n" ); |
374 | gtDispTree(compCurStmt); |
375 | printf("\n" ); |
376 | } |
377 | #endif |
378 | return tree; |
379 | } |
380 | |
381 | return nullptr; |
382 | } |
383 | |
384 | //------------------------------------------------------------------------------------------- |
385 | // optPropGetValue: Given an SSA object ref pointer, get the value needed based on valueKind. |
386 | // |
387 | // Arguments: |
388 | // lclNum - The local var number of the ref pointer. |
389 | // ssaNum - The SSA var number of the ref pointer. |
390 | // valueKind - The kind of value of interest. |
391 | // |
392 | // Return Value: |
393 | // Return the corresponding value based on valueKind. |
394 | |
395 | GenTree* Compiler::optPropGetValue(unsigned lclNum, unsigned ssaNum, optPropKind valueKind) |
396 | { |
397 | return optPropGetValueRec(lclNum, ssaNum, valueKind, 0); |
398 | } |
399 | |
400 | //----------------------------------------------------------------------------------- |
401 | // optPropGetValueRec: Given an SSA object ref pointer, get the value needed based on valueKind |
402 | // within a recursion bound. |
403 | // |
404 | // Arguments: |
405 | // lclNum - The local var number of the array pointer. |
406 | // ssaNum - The SSA var number of the array pointer. |
407 | // valueKind - The kind of value of interest. |
408 | // walkDepth - Current recursive walking depth. |
409 | // |
410 | // Return Value: |
411 | // Return the corresponding value based on valueKind. |
412 | |
413 | GenTree* Compiler::optPropGetValueRec(unsigned lclNum, unsigned ssaNum, optPropKind valueKind, int walkDepth) |
414 | { |
415 | if (ssaNum == SsaConfig::RESERVED_SSA_NUM) |
416 | { |
417 | return nullptr; |
418 | } |
419 | |
420 | SSAName ssaName(lclNum, ssaNum); |
421 | GenTree* value = nullptr; |
422 | |
423 | // Bound the recursion with a hard limit. |
424 | if (walkDepth > optEarlyPropRecurBound) |
425 | { |
426 | return nullptr; |
427 | } |
428 | |
429 | // Track along the use-def chain to get the array length |
430 | GenTree* treelhs = lvaTable[lclNum].GetPerSsaData(ssaNum)->m_defLoc.m_tree; |
431 | |
432 | if (treelhs == nullptr) |
433 | { |
434 | // Incoming parameters or live-in variables don't have actual definition tree node |
435 | // for their FIRST_SSA_NUM. See SsaBuilder::RenameVariables. |
436 | assert(ssaNum == SsaConfig::FIRST_SSA_NUM); |
437 | } |
438 | else |
439 | { |
440 | GenTree** lhsPtr; |
441 | GenTree* treeDefParent = treelhs->gtGetParent(&lhsPtr); |
442 | |
443 | if (treeDefParent->OperGet() == GT_ASG) |
444 | { |
445 | assert(treelhs == treeDefParent->gtGetOp1()); |
446 | GenTree* treeRhs = treeDefParent->gtGetOp2(); |
447 | |
448 | if (treeRhs->OperIsScalarLocal() && lvaInSsa(treeRhs->AsLclVarCommon()->GetLclNum())) |
449 | { |
450 | // Recursively track the Rhs |
451 | unsigned rhsLclNum = treeRhs->AsLclVarCommon()->GetLclNum(); |
452 | unsigned rhsSsaNum = treeRhs->AsLclVarCommon()->GetSsaNum(); |
453 | |
454 | value = optPropGetValueRec(rhsLclNum, rhsSsaNum, valueKind, walkDepth + 1); |
455 | } |
456 | else |
457 | { |
458 | if (valueKind == optPropKind::OPK_ARRAYLEN) |
459 | { |
460 | value = getArrayLengthFromAllocation(treeRhs); |
461 | if (value != nullptr) |
462 | { |
463 | if (!value->IsCnsIntOrI()) |
464 | { |
465 | // Leave out non-constant-sized array |
466 | value = nullptr; |
467 | } |
468 | } |
469 | } |
470 | else if (valueKind == optPropKind::OPK_OBJ_GETTYPE) |
471 | { |
472 | value = getObjectHandleNodeFromAllocation(treeRhs); |
473 | if (value != nullptr) |
474 | { |
475 | if (!value->IsCnsIntOrI()) |
476 | { |
477 | // Leave out non-constant-sized array |
478 | value = nullptr; |
479 | } |
480 | } |
481 | } |
482 | } |
483 | } |
484 | } |
485 | |
486 | return value; |
487 | } |
488 | |
489 | //---------------------------------------------------------------- |
490 | // optFoldNullChecks: Try to find a GT_NULLCHECK node that can be folded into the GT_INDIR node. |
491 | // |
492 | // Arguments: |
493 | // tree - The input GT_INDIR tree. |
494 | // |
495 | |
496 | void Compiler::optFoldNullCheck(GenTree* tree) |
497 | { |
498 | // |
499 | // Check for a pattern like this: |
500 | // |
501 | // = |
502 | // / \ |
503 | // x comma |
504 | // / \ |
505 | // nullcheck + |
506 | // | / \ |
507 | // y y const |
508 | // |
509 | // |
510 | // some trees in the same |
511 | // basic block with |
512 | // no unsafe side effects |
513 | // |
514 | // indir |
515 | // | |
516 | // x |
517 | // |
518 | // where the const is suitably small |
519 | // and transform it into |
520 | // |
521 | // = |
522 | // / \ |
523 | // x + |
524 | // / \ |
525 | // y const |
526 | // |
527 | // |
528 | // some trees with no unsafe side effects here |
529 | // |
530 | // indir |
531 | // | |
532 | // x |
533 | |
534 | if ((compCurBB->bbFlags & BBF_HAS_NULLCHECK) == 0) |
535 | { |
536 | return; |
537 | } |
538 | |
539 | assert(tree->OperIsIndir()); |
540 | |
541 | GenTree* const addr = tree->AsIndir()->Addr(); |
542 | if (addr->OperGet() == GT_LCL_VAR) |
543 | { |
544 | // Check if we have the pattern above and find the nullcheck node if we do. |
545 | |
546 | // Find the definition of the indirected local (x in the picture) |
547 | GenTreeLclVarCommon* const lclVarNode = addr->AsLclVarCommon(); |
548 | |
549 | const unsigned lclNum = lclVarNode->GetLclNum(); |
550 | const unsigned ssaNum = lclVarNode->GetSsaNum(); |
551 | |
552 | if (ssaNum != SsaConfig::RESERVED_SSA_NUM) |
553 | { |
554 | DefLoc defLoc = lvaTable[lclNum].GetPerSsaData(ssaNum)->m_defLoc; |
555 | BasicBlock* defBlock = defLoc.m_blk; |
556 | |
557 | if (compCurBB == defBlock) |
558 | { |
559 | GenTree* defTree = defLoc.m_tree; |
560 | GenTree* defParent = defTree->gtGetParent(nullptr); |
561 | |
562 | if ((defParent->OperGet() == GT_ASG) && (defParent->gtNext == nullptr)) |
563 | { |
564 | GenTree* defRHS = defParent->gtGetOp2(); |
565 | if (defRHS->OperGet() == GT_COMMA) |
566 | { |
567 | if (defRHS->gtGetOp1()->OperGet() == GT_NULLCHECK) |
568 | { |
569 | GenTree* nullCheckTree = defRHS->gtGetOp1(); |
570 | if (nullCheckTree->gtGetOp1()->OperGet() == GT_LCL_VAR) |
571 | { |
572 | // We found a candidate for 'y' in the picture |
573 | unsigned nullCheckLclNum = nullCheckTree->gtGetOp1()->AsLclVarCommon()->GetLclNum(); |
574 | |
575 | if (defRHS->gtGetOp2()->OperGet() == GT_ADD) |
576 | { |
577 | GenTree* additionNode = defRHS->gtGetOp2(); |
578 | if ((additionNode->gtGetOp1()->OperGet() == GT_LCL_VAR) && |
579 | (additionNode->gtGetOp1()->gtLclVarCommon.gtLclNum == nullCheckLclNum)) |
580 | { |
581 | GenTree* offset = additionNode->gtGetOp2(); |
582 | if (offset->IsCnsIntOrI()) |
583 | { |
584 | if (!fgIsBigOffset(offset->gtIntConCommon.IconValue())) |
585 | { |
586 | // Walk from the use to the def in reverse execution order to see |
587 | // if any nodes have unsafe side effects. |
588 | GenTree* currentTree = lclVarNode->gtPrev; |
589 | bool isInsideTry = compCurBB->hasTryIndex(); |
590 | bool canRemoveNullCheck = true; |
591 | const unsigned maxNodesWalked = 25; |
592 | unsigned nodesWalked = 0; |
593 | |
594 | // First walk the nodes in the statement containing the indirection |
595 | // in reverse execution order starting with the indirection's |
596 | // predecessor. |
597 | while (canRemoveNullCheck && (currentTree != nullptr)) |
598 | { |
599 | if ((nodesWalked++ > maxNodesWalked) || |
600 | !optCanMoveNullCheckPastTree(currentTree, isInsideTry)) |
601 | { |
602 | canRemoveNullCheck = false; |
603 | } |
604 | else |
605 | { |
606 | currentTree = currentTree->gtPrev; |
607 | } |
608 | } |
609 | |
610 | // Then walk the statement list in reverse execution order |
611 | // until we get to the statement containing the null check. |
612 | // We only need to check the side effects at the root of each statement. |
613 | GenTree* curStmt = compCurStmt->gtPrev; |
614 | currentTree = curStmt->gtStmt.gtStmtExpr; |
615 | while (canRemoveNullCheck && (currentTree != defParent)) |
616 | { |
617 | if ((nodesWalked++ > maxNodesWalked) || |
618 | !optCanMoveNullCheckPastTree(currentTree, isInsideTry)) |
619 | { |
620 | canRemoveNullCheck = false; |
621 | } |
622 | else |
623 | { |
624 | curStmt = curStmt->gtStmt.gtPrevStmt; |
625 | assert(curStmt != nullptr); |
626 | currentTree = curStmt->gtStmt.gtStmtExpr; |
627 | } |
628 | } |
629 | |
630 | if (canRemoveNullCheck) |
631 | { |
632 | // Remove the null check |
633 | nullCheckTree->gtFlags &= ~(GTF_EXCEPT | GTF_DONT_CSE); |
634 | |
635 | // Set this flag to prevent reordering |
636 | nullCheckTree->gtFlags |= GTF_ORDER_SIDEEFF; |
637 | nullCheckTree->gtFlags |= GTF_IND_NONFAULTING; |
638 | |
639 | defRHS->gtFlags &= ~(GTF_EXCEPT | GTF_DONT_CSE); |
640 | defRHS->gtFlags |= |
641 | additionNode->gtFlags & (GTF_EXCEPT | GTF_DONT_CSE); |
642 | |
643 | // Re-morph the statement. |
644 | fgMorphBlockStmt(compCurBB, |
645 | curStmt->AsStmt() DEBUGARG("optFoldNullCheck" )); |
646 | } |
647 | } |
648 | } |
649 | } |
650 | } |
651 | } |
652 | } |
653 | } |
654 | } |
655 | } |
656 | } |
657 | } |
658 | } |
659 | |
660 | //---------------------------------------------------------------- |
661 | // optCanMoveNullCheckPastTree: Check if GT_NULLCHECK can be folded into a node that |
662 | // is after tree is execution order. |
663 | // |
664 | // Arguments: |
665 | // tree - The input GT_INDIR tree. |
666 | // isInsideTry - True if tree is inside try, false otherwise |
667 | // |
668 | // Return Value: |
669 | // True if GT_NULLCHECK can be folded into a node that is after tree is execution order, |
670 | // false otherwise. |
671 | |
672 | bool Compiler::optCanMoveNullCheckPastTree(GenTree* tree, bool isInsideTry) |
673 | { |
674 | bool result = true; |
675 | if (isInsideTry) |
676 | { |
677 | // We disallow calls, exception sources, and all assignments. |
678 | // Assignments to locals are disallowed inside try because |
679 | // they may be live in the handler. |
680 | if ((tree->gtFlags & GTF_SIDE_EFFECT) != 0) |
681 | { |
682 | result = false; |
683 | } |
684 | } |
685 | else |
686 | { |
687 | // We disallow calls, exception sources, and assignments to |
688 | // global memory. |
689 | if (GTF_GLOBALLY_VISIBLE_SIDE_EFFECTS(tree->gtFlags)) |
690 | { |
691 | result = false; |
692 | } |
693 | } |
694 | return result; |
695 | } |
696 | |