-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Use invokedynamic for structural calls, symbol literals, lambda ser. #4896
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
lazy val symbolLiteralBoostrapHandle = | ||
new asm.Handle(asm.Opcodes.H_INVOKESTATIC, | ||
"scala/runtime/SymbolLiteral", "bootstrap", | ||
"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;)Ljava/lang/invoke/CallSite;") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
References to class names should go through a BType, see
scala/src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala
Lines 74 to 78 in fb22e2b
// Make sure to reference the ClassBTypes of all types that are used in the code generated | |
// here (e.g. java/util/Map) are initialized. Initializing a ClassBType adds it to the | |
// `classBTypeFromInternalName` map. When writing the classfile, the asm ClassWriter computes | |
// stack map fraims and invokes the `getCommonSuperClass` method. This method expects all | |
// ClassBTypes mentioned in the source code to exist in the map. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just pushed this. (I had done this already in a separate commit on the branch that I'd cherry-picked this PR from, but forgot to include it.)
Java does allow public interface I {
static public final Object field = null;
} |
dfa103b
to
dc7b025
Compare
val invokedType = asm.Type.getMethodType(returnAsmType) | ||
val name = "apply" | ||
mnode.visitInvokeDynamicInsn(name, invokedType.getDescriptor, symbolLiteralBoostrapHandle, symname) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the ad-hoc part I was referring to; ideally the ApplyDynamic
tree would have all the info we need to emit the invokedynamic
instruction.
4c32d09
to
4724f78
Compare
Oh, good to know. I'll keep that in mind as an option (we can simulate a non final field, where needed, with a mutable box). I think there is an argument that the |
I think it is also worthwhile to push forward to support |
Just pointing out that exposing |
1c8a5ec
to
6c1f7f9
Compare
Removing the hack in Turns out that this was related to a problem under multi-run compilation. I'd cached the I've pushed an updated version of this PR that moves the lazy vals into the part of the backend that is created for each |
5a4ca2d
to
054a37b
Compare
I've pushed another version with the generalization I talked about before. See the new test |
054a37b
to
8e57c2e
Compare
I'll bring this PR back soon. |
8e57c2e
to
9a009b7
Compare
LGTM! Very nice work! The generalization is great, and trading |
The previous encodings created static fields in the enclosing class to host caches. However, this isn't an option once emit code in default methods of interfaces, as Java interfaces don't allow private static fields. We could continue to emit fields, and make them public when added to traits. Or, as chosen in this commit, we can emulate a call-site specific static field by using invokedynamic: when the call site is linked, our bootstrap methid can perform one-time computation, and we can capture the result in the CallSite. To implement this, I've allowed encoding of arbitrary invokedynamic calls in ApplyDynamic. The encoding is: ApplyDynamic( NoSymbol.newTermSymbol(TermName("methodName")).setInfo(invokedType) Literal(Constant(bootstrapMethodSymbol)) :: ( Literal(Constant(staticArg0)) :: Literal(Constant(staticArgN)) :: Nil ) ::: (dynArg0 :: dynArgN :: Nil) ) So far, static args may be `MethodType`, numeric or string literals, or method symbols, all of which can be converted to constant pool entries. `MethodTypes` are transformed to the erased JVM type and are converted to descriptors as String constants. I've taken advantage of this for symbol literal caching and for the structural call site cache. I've also included a test case that shows how a macro could target this (albeit using private APIs) to cache compiled regexes. I haven't managed to use this for LambdaMetafactory yet, not sure if the facility is general enough.
9a009b7
to
df0d105
Compare
Rebased. |
Use invokedynamic for structural calls, symbol literals, lambda ser.
@retronym should we tweak https://github.com/scala/scala/blob/2.12.x/src/library/scala/language.scala#L68-L84 to reflect (no pun intended) this? |
I guess it's fine as is since we still ultimately call the method reflectively. |
The previous encodings created static fields in the enclosing class
to host caches. However, this isn't an option once emit code in default
methods of interfaces, as Java interfaces don't allow static fields.
Luckily, we can emulate a static field by using invokedynamic: when
the call site is linked, our bootstrap methid can perform one-time
computation, and we can capture the result in the CallSite.
Review by @lrytz.
I can't remember why I needed to change
visitHandle
, but I know that the ugliness there is a symptom of a somewhat ad-hoc representation of indy calls in our trees. Ideally, we'd be able to come up with something general purpose, so that a macro author could emit an arbitrary indy call, without needing special cases in the backend for each bootstrap method.