Skip to content

Commit

Permalink
Inline calls to static trait super accessors if trait method is trivial
Browse files Browse the repository at this point in the history
Generally we don't inline static trait super accessors because they
contain an `invokespecial`, which has different semantics if inlined
into another classfile. However, if the target trait method itself is
a forwarder or trivial, we should inline the static super accessor.
The `invokespecial` will be inlined in the next round.
  • Loading branch information
lrytz committed Jun 28, 2019
1 parent 51f979d commit 2d66bd0
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,21 @@ abstract class InlinerHeuristics extends PerRunInit {
// inlined in turn (chosen by the same heuristic), or the code is rolled back. but we don't inline them just because
// they are forwarders.
val isTraitSuperAccessor = backendUtils.isTraitSuperAccessor(callee.callee, callee.calleeDeclarationClass)
if (isTraitSuperAccessor) null
if (isTraitSuperAccessor) {
// inline static trait super accessors if the corresponding trait method is a forwarder or trivial (scala-dev#618)
{
val css = callGraph.callsites(callee.callee)
if (css.sizeIs == 1) css.head._2 else null
} match {
case null => null
case traitMethodCallsite =>
val tmCallee = traitMethodCallsite.callee.get
val traitMethodForwarderKind = backendUtils.looksLikeForwarderOrFactoryOrTrivial(
tmCallee.callee, tmCallee.calleeDeclarationClass.internalName, allowPrivateCalls = false)
if (traitMethodForwarderKind > 0) GenericForwarder
else null
}
}
else {
val forwarderKind = backendUtils.looksLikeForwarderOrFactoryOrTrivial(callee.callee, callee.calleeDeclarationClass.internalName, allowPrivateCalls = false)
if (forwarderKind < 0)
Expand Down
29 changes: 27 additions & 2 deletions test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1642,8 +1642,8 @@ class InlinerTest extends BytecodeTesting {
assertInvoke(getMethod(c, "t5"), "T", "m3a") // could not inline
assertNoInvoke(getMethod(c, "t6")) // both forwarders inlined, closure eliminated

assertInvoke(getMethod(c, "t7"), "T", "m1a$")
assertInvoke(getMethod(c, "t8"), "T", "m1b$")
assertNoInvoke(getMethod(c, "t7"))
assertNoInvoke(getMethod(c, "t8"))

assertNoInvoke(getMethod(c, "t9"))
assertNoInvoke(getMethod(c, "t10"))
Expand Down Expand Up @@ -2208,4 +2208,29 @@ class InlinerTest extends BytecodeTesting {
}
assertEquals(List("A", "$anonfun$f$1"), args.head)
}

@Test
def sd618(): Unit = {
val code =
"""trait T {
| final def m1 = 1 // trivial
| final def m2 = p // forwarder
| @noinline def p = 42
|}
|
|object TT extends T // gets mixin forwarders m1 / m2 which call the static T.m1$ / T.m2$
|
|class C {
| def t1a(t: T) = t.m1 // inlined, so we get 1
| def t1b = TT.m1 // mixin forwarder is inlined, static forwarder then as well because the final method is trivial
| def t2a(t: T) = t.m2 // inlined, so we get T.p
| def t2b = TT.m2 // mixin forwarder is inlined, static forwarder then as well because the final method is forwarder
|}
""".stripMargin
val c :: _ = compileClasses(code)
assertNoInvoke(getMethod(c, "t1a"))
assertNoInvoke(getMethod(c, "t1b"))
assertInvoke(getMethod(c, "t2a"), "T", "p")
assertInvoke(getMethod(c, "t2b"), "T", "p")
}
}

0 comments on commit 2d66bd0

Please sign in to comment.