Unclear intrinsic behaviour in Java9

518 views Asked by At

Suppose I have this code (it really does not matter I think, but just in case here it is):

public class AtomicJDK9 {

     static AtomicInteger ai = new AtomicInteger(0);

     public static void main(String[] args) {
        int sum = 0;
        for (int i = 0; i < 30_000; ++i) {
           sum += atomicIncrement();
        }
        System.out.println(sum);
     }

     public static int atomicIncrement() {
         ai.getAndAdd(12);
         return ai.get();
     }
}

And here is how I am invoking it (using java-9):

 java -XX:+UnlockDiagnosticVMOptions 
      -XX:-TieredCompilation  
      -XX:+PrintIntrinsics  
      AtomicJDK9

What I am trying to find out is what methods were replaced by intrinsic code. The first one that is hit (inside Unsafe):

      @HotSpotIntrinsicCandidate
      public final int getAndAddInt(Object o, long offset, int delta) {
         int v;
         do {
            v = getIntVolatile(o, offset);
         } while (!weakCompareAndSwapIntVolatile(o, offset, v, v + delta));
         return v;
      }

And this method is indeed present in the output of the above invocation:

 @ 8   jdk.internal.misc.Unsafe::getAndAddInt (27 bytes)   (intrinsic)

But, the entire output is weird (for me that is):

 @ 8   jdk.internal.misc.Unsafe::getAndAddInt (27 bytes)   (intrinsic)
 @ 3   jdk.internal.misc.Unsafe::getIntVolatile (0 bytes)   (intrinsic)
 @ 18  jdk.internal.misc.Unsafe::weakCompareAndSwapIntVolatile (11 bytes)   (intrinsic)
 @ 7   jdk.internal.misc.Unsafe::compareAndSwapInt (0 bytes)   (intrinsic)
 @ 8   jdk.internal.misc.Unsafe::getAndAddInt (27 bytes)   (intrinsic)

Why is the getAndAddInt present twice in the output?

Also if getAndAddInt is indeed replaced by an intrinsic call, why is there a need to replace all other intrinsic methods down the call stackl they will not be used anymore. I do assume it's as simple as the stack of method calls is traversed from the bottom.

1

There are 1 answers

0
apangin On BEST ANSWER

In order to illustrate the compiler logic I ran JVM with the following arguments.

    -XX:-TieredCompilation -XX:CICompilerCount=1
    -XX:+UnlockDiagnosticVMOptions -XX:+PrintCompilation -XX:+PrintInlining 

And that's what it prints.

337   29   java.util.concurrent.atomic.AtomicInteger::getAndAdd (12 bytes)
              @ 8   jdk.internal.misc.Unsafe::getAndAddInt (27 bytes)   (intrinsic)
337   30   jdk.internal.misc.Unsafe::getAndAddInt (27 bytes)
              @ 3   jdk.internal.misc.Unsafe::getIntVolatile (0 bytes)   (intrinsic)
              @ 18   jdk.internal.misc.Unsafe::weakCompareAndSwapIntVolatile (11 bytes)   (intrinsic)
338   32   jdk.internal.misc.Unsafe::weakCompareAndSwapIntVolatile (11 bytes)
              @ 7   jdk.internal.misc.Unsafe::compareAndSwapInt (0 bytes)   (intrinsic)
339   33   AtomicJDK9::atomicIncrement (16 bytes)
              @ 5   java.util.concurrent.atomic.AtomicInteger::getAndAdd (12 bytes)   inline (hot)
                @ 8   jdk.internal.misc.Unsafe::getAndAddInt (27 bytes)   (intrinsic)
              @ 12   java.util.concurrent.atomic.AtomicInteger::get (5 bytes)   accessor
  • Methods are intrinsics only for compiler, but not for interpreter.
  • Every method starts in interpreter until it is considered hot.
  • AtomicInteger.getAndAdd is called not only from your code, but from common JDK code, too.
  • That is, AtomicInteger.getAndAdd reaches invocation threshold a little bit earlier than your AtomicJDK9.atomicIncrement. Then getAndAdd is submitted to compilation queue, and that's where the first intrinsic printout comes from.
  • HotSpot JVM compiles methods in background. While a method is being compiled, the execution continues in interpreter.
  • While AtomicInteger.getAndAdd is interpreted, Unsafe.getAndAddInt and Unsafe.weakCompareAndSwapIntVolatile methods also reach invocation threshold and begin compilation. The next 3 intrinsics are printed while compiling these Unsafe methods.
  • Finally, AtomicJDK9.atomicIncrement also reaches invocation threashold and begin compilation. The last intrinsic printout corresponds to your method.