Repository: dotnet/runtime
File: src/coreclr/jit/assertionprop.cpp (C++)

Code before:
                //  arr[cond ? 10 : 5] = 0;  // arr is at least 6 elements long
                //  arr[2] = 0;              // arr must be at least 3 elements long
                //
                // or even:
                //
                //  arr[cond ? 10 : 5] = 0; // arr is at least 6 elements long
                //  arr[cond ? 1 : 2] = 0;  // arr must be at least 3 elements long
                //
                auto tryGetMaxOrMinConst = [this](ValueNum vn, bool getMin, int* index) -> bool {
                    *index = getMin ? INT_MAX : INT_MIN;
                    return vnStore->VNVisitReachingVNs(vn,
                                                       [this, &index, &getMin](ValueNum vn) -> ValueNumStore::VNVisit {
                        int cns = 0;
                        if (vnStore->IsVNIntegralConstant(vn, &cns))
                        {
                            *index = getMin ? min(*index, cns) : max(*index, cns);
                            return ValueNumStore::VNVisit::Continue;
                        }
                        return ValueNumStore::VNVisit::Abort;
                    }) == ValueNumStore::VNVisit::Continue;
                };

                int index1;
                int index2;
                if (tryGetMaxOrMinConst(curAssertion->op1.bnd.vnIdx, /*min*/ true, &index1) &&
                    tryGetMaxOrMinConst(vnCurIdx, /*max*/ false, &index2))
                {
                    // It can always be considered as redundant with any previous higher constant value
                    //       a[K1] followed by a[K2], with K2 >= 0 and K1 >= K2
                    if (index2 >= 0 && index1 >= index2)
                    {
                        return dropBoundsCheck(INDEBUG("a[K1] followed by a[K2], with K2 >= 0 and K1 >= K2"));
                    }
                }
            }
            // Extend this to remove additional redundant bounds checks:
            // i.e.  a[i+1] followed by a[i]  by using the VN(i+1) >= VN(i)
            //       a[i]   followed by a[j]  when j is known to be >= i
            //       a[i]   followed by a[5]  when i is known to be >= 5
        }
    }

    return nullptr;
}

/*****************************************************************************
 *
 *  Called when we have a successfully performed an assertion prop. We have
 *  the newTree in hand. This method will replace the existing tree in the
 *  stmt with the newTree.
 *

Review comment (suggestion):
Probably a rare, but if both the index and array are Phis you could check pairwise:

Eg code like
```C#
int[] a = null;
int x = 0;
if (p)
{ 
   a = new int[10];
   x = 8;
}
else
{
   a = new int[5];
   x = 3;
}

  = a[x];
```
The bounds check is redundant, even though `max(x) = 10` and `min(a.Length) = 5`

I suppose we don't have a proper walker for this.

Code after:
                //  arr[cond ? 10 : 5] = 0;  // arr is at least 6 elements long
                //  arr[2] = 0;              // arr must be at least 3 elements long
                //
                // or even:
                //
                //  arr[cond ? 10 : 5] = 0; // arr is at least 6 elements long
                //  arr[cond ? 1 : 2] = 0;  // arr must be at least 3 elements long
                //
                auto tryGetMaxOrMinConst = [this](ValueNum vn, bool getMin, int* index) -> bool {
                    *index = getMin ? INT_MAX : INT_MIN;
                    return vnStore->VNVisitReachingVNs(vn,
                                                       [this, index, getMin](ValueNum vn) -> ValueNumStore::VNVisit {
                        int cns = 0;
                        if (vnStore->IsVNIntegralConstant(vn, &cns))
                        {
                            *index = getMin ? min(*index, cns) : max(*index, cns);
                            return ValueNumStore::VNVisit::Continue;
                        }
                        return ValueNumStore::VNVisit::Abort;
                    }) == ValueNumStore::VNVisit::Continue;
                };

                int index1;
                int index2;
                if (tryGetMaxOrMinConst(curAssertion->op1.bnd.vnIdx, /*min*/ true, &index1) &&
                    tryGetMaxOrMinConst(vnCurIdx, /*max*/ false, &index2))
                {
                    // It can always be considered as redundant with any previous higher constant value
                    //       a[K1] followed by a[K2], with K2 >= 0 and K1 >= K2
                    if (index2 >= 0 && index1 >= index2)
                    {
                        return dropBoundsCheck(INDEBUG("a[K1] followed by a[K2], with K2 >= 0 and K1 >= K2"));
                    }
                }
            }
            // Extend this to remove additional redundant bounds checks:
            // i.e.  a[i+1] followed by a[i]  by using the VN(i+1) >= VN(i)
            //       a[i]   followed by a[j]  when j is known to be >= i
            //       a[i]   followed by a[5]  when i is known to be >= 5
        }
    }

    return nullptr;
}

/*****************************************************************************
 *
 *  Called when we have a successfully performed an assertion prop. We have
 *  the newTree in hand. This method will replace the existing tree in the
 *  stmt with the newTree.
 *