Reputation: 4223
As I can see from JVM specification this code:
void spin() {
int i;
for (i = 0; i < 100; i++) {
; // Loop body is empty
}
}
should be compiled into:
0 iconst_0
1 istore_1
2 goto 8
5 iinc 1 1
8 iload_1
9 bipush 100
11 if_icmplt 5
14 return
where condition check if_icmplt
is after the loop body, but when I compile it myself and view with javap, I see:
0: iconst_0
1: istore_1
2: iload_1
3: bipush 100
5: if_icmpge 14
8: iinc 1, 1
11: goto 2
14: return
and loop condition is before the cycle body. Why does this happen?
Placing condition after body prevents us from doing goto after every cycle and looks logical to me. So why is OracleJDK doing another way?
Upvotes: 2
Views: 1000
Reputation: 74810
The straightforward way of implementing a for loop (from its JLS semantic) is this one:
That actually is just what the compiler generated in your case:
initialization
0: iconst_0
1: istore_1
condition - if false, go to 6.
2: iload_1
3: bipush 100
5: if_icmpge 14
loop body (empty)
incrementation
8: iinc 1, 1
goto 2
11: goto 2
end
14: return
The version in the JVM spec is another way to implement it, which would also be acceptable. As said by the answerers, the JIT compiler of a normal VM will look at this again and optimize it anyways (if it is relevant enough), so slight optimizations on the bytecode level would only matter for JVMs which don't have a JIT and interpret the bytecode instruction by instruction. And even then any optimizations would be specific to the actual machine.
Upvotes: 2
Reputation: 72359
It's the same number of instructions either way, and as pointed out already the spec doesn't tie the compiler to this particular bytecode:
A compiler might compile spin to...
The compiler can pretty much choose to compile it to whatever bytecode it wants, as long as the end effect is the same.
Placing condition after body prevents us from doing goto after every cycle and looks logical to me. So why is OracleJDK doing another way?
Probably impossible to say for definite unless the guy that wrote that bit of the compiler swings by - however I'd imagine your theory could be correct in that it seems possible it's to do with aiding later JIT optimisation. My only hunch (which may be incorrect) is that it could be to do with the positioning of the goto
command - there may be some more potential for better code inlining if the first 6 instructions are taken as a logical block, with no gotos, as they are in the bytecode that the compiler actually produces. Of course, since that particular goto
can only ever jump to within that block there's no logical difference, and the JIT could still inline it with exactly the same effect. These days I'd imagine it's clever enough to work that out, but it may not have always been, in which case the produced code provides a good work around. Of course, when (if) the JIT was made clever enough, there was no need to change the code, so it stuck.
An elaborate theory, and perhaps completely wrong - but if it is a functional difference, that's my best guess!
The other possibility is that it's just how that part of the compiler has been written, is completely coincidental, and there's no set reason for it at all (as in the compiler devs probably weren't trying to make the code turn out exactly as it did in the example.)
Upvotes: 1
Reputation: 13535
It is not for better JIT optimizations - for JIT, these code snippets are equivalent. It is because there is no sense to make optimizations in javac, since JIT optimizations are more powerful anyway.
Upvotes: 2