Description
We had to (#194) make the module field non-final to prepare for JDK9/Classfile 53 verifier, which enforces the (long standing) rule that static final field assignments must occur in <clinit>
(not <init>
).
However, this prevents the JIT from eliding the access of this field in calling code. For example, in scala.math.max(1, 2)
, the JIT can't rule out that scala.math.package$.MODULE$
hasn't been nulled out, so it must be checked.
For some special cases, like above, I've managed to restore finality of that field in scala/scala#6523. But important modules don't qualify for the optimization, e.g Predef
and Nil
.
The JDK has an internal annotation, @Stable
, that would tell the JIT that this field is 'effectively final', ie it is assign once. That's not available for our purposes. It can be simulated with invokedynamic, which might be worth trying, but we need to ensure it is really zero-overhead across different JDK versions.
Another idea came to mind yesterday.
Given:
trait T {
println("T")
}
class C {
println("C")
}
object O extends C with T {
println("body")
}
We currently emit:
public final class O$ extends C implements T {
public static LO$; MODULE$
public static <clinit>()V
NEW O$
INVOKESPECIAL O$.<init> ()V
RETURN
private <init>()V
ALOAD 0
INVOKESPECIAL C.<init> ()V
ALOAD 0
PUTSTATIC O$.MODULE$ : LO$;
ALOAD 0
INVOKESTATIC T.$init$ (LT;)V (itf)
GETSTATIC scala/Predef$.MODULE$ : Lscala/Predef$;
LDC "body"
INVOKEVIRTUAL scala/Predef$.println (Ljava/lang/Object;)V
RETURN
}
But why not:
public final class O$ extends C implements T {
public static final LO$; MODULE$
public static <clinit>()V
NEW O$
DUP
INVOKESPECIAL O$.<init> ()V
DUP
PUTSTATIC O$.MODULE$ : LO$;
INVOKESPECIAL O$.$init$
RETURN
private <init>()V
ALOAD 0
INVOKESPECIAL C.<init> ()V
private $init$()V
ALOAD 0
INVOKESTATIC T.$init$ (LT;)V (itf)
GETSTATIC scala/Predef$.MODULE$ : Lscala/Predef$;
LDC "body"
INVOKEVIRTUAL scala/Predef$.println (Ljava/lang/Object;)V
}
ie, split the constructor up just after the super-class constructor call and move the rest into a private method that would be called after <clinit>
assigns the module field.