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.
7 // We take the following approach to range check analysis:
9 // Consider the following loop:
10 // for (int i = 0; i < a.len; ++i) {
14 // This would be represented as:
16 // / ______ a[i_1] = 0; BB2
19 // i_1 = phi(i_0, i_2); BB1 |
20 // i_1 < a.len -------------------+
23 // BB1 -> (i_1 < a.len) ? BB2 : BB3
27 // **Step 1. Walk the statements in the method checking if there is a bounds check.
28 // If there is a bounds check, ask the range of the index variable.
29 // In the above example i_1's range.
31 // **Step 2. Follow the defs and the dependency chain:
32 // i_1 is a local, so go to its definition which is i_1 = phi(i_0, i_2).
34 // Since rhs is a phi, we ask the range for i_0 and i_2 in the hopes of merging
35 // the resulting ranges for i_1.
37 // The range of i_0 follows immediately when going to its definition.
38 // Ask for the range of i_2, which leads to i_1 + 1.
39 // Ask for the range of i_1 and figure we are looping. Call the range of i_1 as
40 // "dependent" and quit looping further. The range of "1" is just <1, 1>.
42 // Now we have exhausted all the variables for which the range can be determined.
43 // The others are either "unknown" or "dependent."
45 // We also merge assertions from its pred block's edges for a phi argument otherwise
46 // from the block's assertionIn. This gives us an upper bound for i_1 as a.len.
48 // **Step 3. Check if an overflow occurs in the dependency chain (loop.)
49 // In the above case, we want to make sure there is no overflow in the definitions
50 // involving i_1 and i_2. Merge assertions from the block's edges whenever possible.
52 // **Step 4. Check if the dependency chain is monotonic.
54 // **Step 5. If monotonic is true, then perform a widening step, where we assume, the
55 // SSA variables that are "dependent" get their values from the definitions in the
56 // dependency loop and their initial values must be the definitions that are not in
57 // the dependency loop, in this case i_0's value which is 0.
63 static bool IntAddOverflows(int max1, int max2)
65 if (max1 > 0 && max2 > 0 && INT_MAX - max1 < max2)
69 if (max1 < 0 && max2 < 0 && max1 < INT_MIN - max2)
80 keUndef, // The limit is yet to be computed.
83 keDependent, // The limit is dependent on some other value.
84 keUnknown, // The limit could not be determined.
87 Limit() : type(keUndef)
91 Limit(LimitType type) : type(type)
95 Limit(LimitType type, int cns) : cns(cns), vn(ValueNumStore::NoVN), type(type)
97 assert(type == keConstant);
100 Limit(LimitType type, ValueNum vn, int cns) : cns(cns), vn(vn), type(type)
102 assert(type == keBinOpArray);
107 return type == keUndef;
111 return type == keDependent;
115 return type == keUnknown;
119 return type == keConstant;
127 return type == keBinOpArray;
129 bool AddConstant(int i)
136 if (IntAddOverflows(cns, i))
144 if (IntAddOverflows(cns, i))
153 // For these values of 'type', conservatively return false
160 bool Equals(Limit& l)
167 return l.type == type;
170 return l.type == type && l.vn == vn && l.cns == cns;
173 return l.type == type && l.cns == cns;
178 const char* ToString(CompAllocator* alloc)
181 char* buf = (char*)alloc->Alloc(size);
194 sprintf_s(buf, size, "VN%04X + %d", vn, cns);
198 sprintf_s(buf, size, "%d", cns);
209 // Range struct contains upper and lower limit.
215 Range(const Limit& limit) : uLimit(limit), lLimit(limit)
219 Range(const Limit& lLimit, const Limit& uLimit) : uLimit(uLimit), lLimit(lLimit)
234 char* ToString(CompAllocator* alloc)
237 char* buf = (char*)alloc->Alloc(size);
238 sprintf_s(buf, size, "<%s, %s>", lLimit.ToString(alloc), uLimit.ToString(alloc));
244 // Helpers for operations performed on ranges
247 // Given a constant limit in "l1", add it to l2 and mutate "l2".
248 static Limit AddConstantLimit(Limit& l1, Limit& l2)
250 assert(l1.IsConstant());
252 if (l.AddConstant(l1.GetConstant()))
258 return Limit(Limit::keUnknown);
262 // Given two ranges "r1" and "r2", perform an add operation on the
264 static Range Add(Range& r1, Range& r2)
266 Limit& r1lo = r1.LowerLimit();
267 Limit& r1hi = r1.UpperLimit();
268 Limit& r2lo = r2.LowerLimit();
269 Limit& r2hi = r2.UpperLimit();
271 Range result = Limit(Limit::keUnknown);
273 // Check lo ranges if they are dependent and not unknown.
274 if ((r1lo.IsDependent() && !r1lo.IsUnknown()) || (r2lo.IsDependent() && !r2lo.IsUnknown()))
276 result.lLimit = Limit(Limit::keDependent);
278 // Check hi ranges if they are dependent and not unknown.
279 if ((r1hi.IsDependent() && !r1hi.IsUnknown()) || (r2hi.IsDependent() && !r2hi.IsUnknown()))
281 result.uLimit = Limit(Limit::keDependent);
284 if (r1lo.IsConstant())
286 result.lLimit = AddConstantLimit(r1lo, r2lo);
288 if (r2lo.IsConstant())
290 result.lLimit = AddConstantLimit(r2lo, r1lo);
292 if (r1hi.IsConstant())
294 result.uLimit = AddConstantLimit(r1hi, r2hi);
296 if (r2hi.IsConstant())
298 result.uLimit = AddConstantLimit(r2hi, r1hi);
303 // Given two ranges "r1" and "r2", do a Phi merge. If "monotonic" is true,
304 // then ignore the dependent variables.
305 static Range Merge(Range& r1, Range& r2, bool monotonic)
307 Limit& r1lo = r1.LowerLimit();
308 Limit& r1hi = r1.UpperLimit();
309 Limit& r2lo = r2.LowerLimit();
310 Limit& r2hi = r2.UpperLimit();
312 // Take care of lo part.
313 Range result = Limit(Limit::keUnknown);
314 if (r1lo.IsUnknown() || r2lo.IsUnknown())
316 result.lLimit = Limit(Limit::keUnknown);
318 // Uninitialized, just copy.
319 else if (r1lo.IsUndef())
321 result.lLimit = r2lo;
323 else if (r1lo.IsDependent() || r2lo.IsDependent())
327 result.lLimit = r1lo.IsDependent() ? r2lo : r1lo;
331 result.lLimit = Limit(Limit::keDependent);
335 // Take care of hi part.
336 if (r1hi.IsUnknown() || r2hi.IsUnknown())
338 result.uLimit = Limit(Limit::keUnknown);
340 else if (r1hi.IsUndef())
342 result.uLimit = r2hi;
344 else if (r1hi.IsDependent() || r2hi.IsDependent())
348 result.uLimit = r1hi.IsDependent() ? r2hi : r1hi;
352 result.uLimit = Limit(Limit::keDependent);
356 if (r1lo.IsConstant() && r2lo.IsConstant())
358 result.lLimit = Limit(Limit::keConstant, min(r1lo.GetConstant(), r2lo.GetConstant()));
360 if (r1hi.IsConstant() && r2hi.IsConstant())
362 result.uLimit = Limit(Limit::keConstant, max(r1hi.GetConstant(), r2hi.GetConstant()));
364 if (r2hi.Equals(r1hi))
366 result.uLimit = r2hi;
368 if (r2lo.Equals(r1lo))
370 result.lLimit = r1lo;
372 // Widen Upper Limit => Max(k, (a.len + n)) yields (a.len + n),
373 // This is correct if k >= 0 and n >= k, since a.len always >= 0
374 // (a.len + n) could overflow, but the result (a.len + n) also
375 // preserves the overflow.
376 if (r1hi.IsConstant() && r1hi.GetConstant() >= 0 && r2hi.IsBinOpArray() &&
377 r2hi.GetConstant() >= r1hi.GetConstant())
379 result.uLimit = r2hi;
381 if (r2hi.IsConstant() && r2hi.GetConstant() >= 0 && r1hi.IsBinOpArray() &&
382 r1hi.GetConstant() >= r2hi.GetConstant())
384 result.uLimit = r1hi;
386 if (r1hi.IsBinOpArray() && r2hi.IsBinOpArray() && r1hi.vn == r2hi.vn)
388 result.uLimit = r1hi;
389 // Widen the upper bound if the other constant is greater.
390 if (r2hi.GetConstant() > r1hi.GetConstant())
392 result.uLimit = r2hi;
403 RangeCheck(Compiler* pCompiler);
405 typedef JitHashTable<GenTree*, JitPtrKeyFuncs<GenTree>, bool> OverflowMap;
406 typedef JitHashTable<GenTree*, JitPtrKeyFuncs<GenTree>, Range*> RangeMap;
407 typedef JitHashTable<GenTree*, JitPtrKeyFuncs<GenTree>, BasicBlock*> SearchPath;
410 // TODO-Cleanup: This code has been kept around just to ensure that the SSA data is still
411 // valid when RangeCheck runs. It should be removed at some point (and perhaps replaced
412 // by a proper SSA validity checker).
414 // Location information is used to map where the defs occur in the method.
419 GenTreeLclVarCommon* tree;
421 Location(BasicBlock* block, GenTree* stmt, GenTreeLclVarCommon* tree, GenTree* parent)
422 : block(block), stmt(stmt), tree(tree), parent(parent)
430 typedef JitHashTable<INT64, JitLargePrimitiveKeyFuncs<INT64>, Location*> VarToLocMap;
432 // Generate a hashcode unique for this ssa var.
433 UINT64 HashCode(unsigned lclNum, unsigned ssaNum);
435 // Add a location of the definition of ssa var to the location map.
436 // Requires "hash" to be computed using HashCode.
437 // Requires "location" to be the local definition.
438 void SetDef(UINT64 hash, Location* loc);
440 // Given a tree node that is a local, return the Location defining the local.
441 Location* GetDef(GenTreeLclVarCommon* lcl);
442 Location* GetDef(unsigned lclNum, unsigned ssaNum);
444 // Given a statement, check if it is a def and add its locations in a map.
445 void MapStmtDefs(const Location& loc);
447 // Given the CFG, check if it has defs and add their locations in a map.
448 void MapMethodDefs();
451 int GetArrLength(ValueNum vn);
453 // Check whether the computed range is within lower and upper bounds. This function
454 // assumes that the lower range is resolved and upper range is symbolic as in an
456 // TODO-CQ: This is not general enough.
457 bool BetweenBounds(Range& range, int lower, GenTree* upper);
459 // Entry point to optimize range checks in the block. Assumes value numbering
460 // and assertion prop phases are completed.
461 void OptimizeRangeChecks();
463 // Given a "tree" node, check if it contains array bounds check node and
464 // optimize to remove it, if possible. Requires "stmt" and "block" that
466 void OptimizeRangeCheck(BasicBlock* block, GenTree* stmt, GenTree* tree);
468 // Given the index expression try to find its range.
469 // The range of a variable depends on its rhs which in turn depends on its constituent variables.
470 // The "path" is the path taken in the search for the rhs' range and its constituents' range.
471 // If "monotonic" is true, the calculations are made more liberally assuming initial values
472 // at phi definitions.
473 Range GetRange(BasicBlock* block, GenTree* expr, bool monotonic DEBUGARG(int indent));
475 // Given the local variable, first find the definition of the local and find the range of the rhs.
476 // Helper for GetRange.
477 Range ComputeRangeForLocalDef(BasicBlock* block, GenTreeLclVarCommon* lcl, bool monotonic DEBUGARG(int indent));
479 // Compute the range, rather than retrieve a cached value. Helper for GetRange.
480 Range ComputeRange(BasicBlock* block, GenTree* expr, bool monotonic DEBUGARG(int indent));
482 // Compute the range for the op1 and op2 for the given binary operator.
483 Range ComputeRangeForBinOp(BasicBlock* block, GenTreeOp* binop, bool monotonic DEBUGARG(int indent));
485 // Merge assertions from AssertionProp's flags, for the corresponding "phiArg."
486 // Requires "pRange" to contain range that is computed partially.
487 void MergeAssertion(BasicBlock* block, GenTree* phiArg, Range* pRange DEBUGARG(int indent));
489 // Inspect the "assertions" and extract assertions about the given "phiArg" and
490 // refine the "pRange" value.
491 void MergeEdgeAssertions(GenTreeLclVarCommon* lcl, ASSERT_VALARG_TP assertions, Range* pRange);
493 // The maximum possible value of the given "limit." If such a value could not be determined
494 // return "false." For example: ARRLEN_MAX for array length.
495 bool GetLimitMax(Limit& limit, int* pMax);
497 // Does the addition of the two limits overflow?
498 bool AddOverflows(Limit& limit1, Limit& limit2);
500 // Does the binary operation between the operands overflow? Check recursively.
501 bool DoesBinOpOverflow(BasicBlock* block, GenTreeOp* binop);
503 // Does the phi operands involve an assignment that could overflow?
504 bool DoesPhiOverflow(BasicBlock* block, GenTree* expr);
506 // Find the def of the "expr" local and recurse on the arguments if any of them involve a
507 // calculation that overflows.
508 bool DoesVarDefOverflow(GenTreeLclVarCommon* lcl);
510 bool ComputeDoesOverflow(BasicBlock* block, GenTree* expr);
512 // Does the current "expr" which is a use involve a definition, that overflows.
513 bool DoesOverflow(BasicBlock* block, GenTree* tree);
515 // Widen the range by first checking if the induction variable is monotonic. Requires "pRange"
516 // to be partially computed.
517 void Widen(BasicBlock* block, GenTree* tree, Range* pRange);
519 // Is the binary operation increasing the value.
520 bool IsBinOpMonotonicallyIncreasing(GenTreeOp* binop);
522 // Given an "expr" trace its rhs and their definitions to check if all the assignments
523 // are monotonically increasing.
525 bool IsMonotonicallyIncreasing(GenTree* tree, bool rejectNegativeConst);
527 // We allocate a budget to avoid walking long UD chains. When traversing each link in the UD
528 // chain, we decrement the budget. When the budget hits 0, then no more range check optimization
529 // will be applied for the currently compiled method.
533 // Given a lclvar use, try to find the lclvar's defining assignment and its containing block.
534 GenTreeOp* GetSsaDefAsg(GenTreeLclVarCommon* lclUse, BasicBlock** asgBlock);
536 GenTreeBoundsChk* m_pCurBndsChk;
538 // Get the cached overflow values.
539 OverflowMap* GetOverflowMap();
540 OverflowMap* m_pOverflowMap;
542 // Get the cached range values.
543 RangeMap* GetRangeMap();
544 RangeMap* m_pRangeMap;
546 SearchPath* m_pSearchPath;
550 VarToLocMap* m_pDefTable;
553 Compiler* m_pCompiler;
554 CompAllocator m_alloc;
556 // The number of nodes for which range is computed throughout the current method.
557 // When this limit is zero, we have exhausted all the budget to walk the ud-chain.