If you run a simple benchmark like this:
private String s;
private int idx;
@Setup
public void setup() {
idx = 13;
s = "Improve the scope of range check optimizations";
}
@Benchmark
public void test_charAt() {
trampoline_charAt();
}
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
public char trampoline_charAt() {
return s.charAt(idx);
}
...then you will see this generated code with the latest jdk9-dev:
mov %eax,-0x14000(%rsp)
push %rbp
sub $0x20,%rsp
mov 0xc(%rsi),%r11d ; get field $idx
mov 0x10(%rsi),%r10d ; get field $s
mov 0xc(%r12,%r10,8),%r9d ; get field $s.value
test %r11d,%r11d
jl 0x00007f6204b5cc19 ; range check 1, jump if ($idx < 0)
mov 0xc(%r12,%r9,8),%ebp ; get $s.value.arraylength
cmp %ebp,%r11d
jge 0x00007f6204b5cc31 ; range check 2, jump if ($idx >= arraylength)
cmp %ebp,%r11d ; <--- REDUNDANT COMPARE
jae 0x00007f6204b5cc03 ; jump if overflow?
lea (%r12,%r9,8),%r10 ; unpack $s.value array reference
movzwl 0x10(%r10,%r11,2),%eax ; get $s.value[$idx]
add $0x20,%rsp ; epilogue and return
pop %rbp
test %eax,0x15f1a3fe(%rip)
retq
As you can see, we do double compare on the same arguments, even though jumps are not affecting the flags, and there seem to be no incoming branches to the second cmp.