add some array bounds check optimizations

This does some data flow analysis to eliminate some low hanging fruit on redundant array bounds checks. For example,
```
void test(int[] a) {
    for (size_t i = 0; i < 10; ++i)
        a[i] = a[i] + 1;
}
```
now only gets one bounds check inside the loop rather than 2.