If you look at generated code for the JMH benchmark like:
```
@Param({"1", "100", "10000", "1000000"})
int size;
int[] is;
@Setup
public void setup() {
is = new int[size];
for (int c = 0; c < size; c++) {
is[c] = c;
}
}
@Benchmark
public void test(Blackhole bh) {
for (int i = 0; i < is.length; i++) {
bh.consume(is[i]);
}
}
```
...then you would notice that the loop always re-reads `is`, `is.length`, does the range check, etc. -- all the things we would otherwise expect to be hoisted out of the loop.
This is because C2 blackholes are modeled as membars that pinch both control and memory slices (like you would expect from the opaque non-inlined call), therefore every iteration has to re-read the referenced memory contents and recompute everything dependent on those loads. This behavior is not new -- the old, non-compiler blackholes were doing the same thing, accidentally -- but it was drowned in blackhole overheads. Now, these effects are clearly visible.
We can try to do this a bit better: allow load optimizations to work across the blackholes, leaving only "prevent dead code elimination" part, as minimally required by blackhole semantics.