Mikhail
Mikhail

Reputation: 4223

Compiling loops in Java

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

Answers (3)

Paŭlo Ebermann
Paŭlo Ebermann

Reputation: 74810

The straightforward way of implementing a for loop (from its JLS semantic) is this one:

  1. initialization
  2. condition - if false, go to 6.
  3. loop body
  4. incrementation
  5. goto 2
  6. end

That actually is just what the compiler generated in your case:

  1. initialization

    0:   iconst_0
    1:   istore_1
    
  2. condition - if false, go to 6.

    2:   iload_1
    3:   bipush  100
    5:   if_icmpge       14
    
  3. loop body (empty)

  4. incrementation

    8:   iinc    1, 1
    
  5. goto 2

    11:  goto    2
    
  6. 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

Michael Berry
Michael Berry

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

Alexei Kaigorodov
Alexei Kaigorodov

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

Related Questions