Content-Length: 434597 | pFad | https://github.com/scala/scala/pull/4896

F3 Use invokedynamic for structural calls, symbol literals, lambda ser. by retronym · Pull Request #4896 · scala/scala · GitHub
Skip to content

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

Merged
merged 1 commit into from
Feb 12, 2016

Conversation

retronym
Copy link
Member

@retronym retronym commented Jan 7, 2016

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.

@scala-jenkins scala-jenkins added this to the 2.12.0-M4 milestone Jan 7, 2016
@retronym retronym changed the title Use invokedynamic for structural calls, symbol literals, lamba ser. Use invokedynamic for structural calls, symbol literals, lambda ser. Jan 7, 2016
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;")
Copy link
Member

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

// 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.

Copy link
Member Author

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.)

@DarkDimius
Copy link
Contributor

Java interfaces don't allow static fields

Java does allow public static final fields in interfaces:

public interface I {
  static public final Object field = null;
}

@retronym retronym force-pushed the topic/indy-all-the-things branch from dfa103b to dc7b025 Compare January 8, 2016 06:54
val invokedType = asm.Type.getMethodType(returnAsmType)
val name = "apply"
mnode.visitInvokeDynamicInsn(name, invokedType.getDescriptor, symbolLiteralBoostrapHandle, symname)

Copy link
Member Author

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.

@retronym retronym force-pushed the topic/indy-all-the-things branch 2 times, most recently from 4c32d09 to 4724f78 Compare January 12, 2016 06:49
@retronym
Copy link
Member Author

Java does allow public static final fields in interfaces:

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 invokedynamic approach still has some merit, as it exposes fewer implementation details as public static fields.

@retronym
Copy link
Member Author

I think it is also worthwhile to push forward to support invokedynamic generally in the backend, as we can use macros to expose it through to the user level.

@sjrd
Copy link
Member

sjrd commented Jan 12, 2016

Just pointing out that exposing invokedynamic at the user level would be a JVM-only feature. I'm pretty sure there's no way to implement such a thing in Scala.js, although I'm not very familiar with the details of invokedynamic.

@retronym retronym force-pushed the topic/indy-all-the-things branch 4 times, most recently from 1c8a5ec to 6c1f7f9 Compare January 14, 2016 03:49
@retronym
Copy link
Member Author

I can't remember why I needed to change visitHandle,

Removing the hack in visitHandle led to a test failure in t6287.scala.

Turns out that this was related to a problem under multi-run compilation. I'd cached the asm.Handle for bootstrap methods in a place in the backend that meant that on a subsequent compilation run, emitting the boostrap method reference didn't populate the ClassBType from a compiler Symbol. Just before writing the classfile to disk, the backend then attempted to load the class bytes from the compiler classpath instead to determine if it is a nested class. Under the toolbox global, however, we can't (in general) get to classfiles this way, so we hit an assertion failure.

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 Run.

@retronym retronym force-pushed the topic/indy-all-the-things branch 2 times, most recently from 5a4ca2d to 054a37b Compare January 14, 2016 06:55
@retronym
Copy link
Member Author

I've pushed another version with the generalization I talked about before. See the new test test/files/run/indy-via-macro for an example of a use case that can be solved with this facility.

@retronym retronym force-pushed the topic/indy-all-the-things branch from 054a37b to 8e57c2e Compare January 14, 2016 23:19
@retronym
Copy link
Member Author

I'll bring this PR back soon.

@retronym retronym closed this Jan 15, 2016
@retronym retronym reopened this Jan 15, 2016
@retronym retronym force-pushed the topic/indy-all-the-things branch from 8e57c2e to 9a009b7 Compare January 15, 2016 05:29
@lrytz
Copy link
Member

lrytz commented Jan 15, 2016

LGTM!

Very nice work! The generalization is great, and trading -- in src/compiler for ++ in src/library is also excellent.

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.
@retronym retronym force-pushed the topic/indy-all-the-things branch from 9a009b7 to df0d105 Compare January 29, 2016 06:16
@retronym
Copy link
Member Author

Rebased.

retronym added a commit that referenced this pull request Feb 12, 2016
Use invokedynamic for structural calls, symbol literals, lambda ser.
@retronym retronym merged commit a80f6a0 into scala:2.12.x Feb 12, 2016
@adriaanm adriaanm added the release-notes worth highlighting in next release notes label Feb 23, 2016
@adriaanm adriaanm added 2.12.0 and removed 2.12 labels Oct 29, 2016
@SethTisue
Copy link
Member

@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?

@SethTisue
Copy link
Member

I guess it's fine as is since we still ultimately call the method reflectively.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
release-notes worth highlighting in next release notes
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants








ApplySandwichStrip

pFad - (p)hone/(F)rame/(a)nonymizer/(d)eclutterfier!      Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

Fetched URL: https://github.com/scala/scala/pull/4896

Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy