From de25e861249826658663a6d40f13b9b91d1146fc Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Tue, 29 Nov 2016 14:00:46 +0100 Subject: Don't exclude super calls to trait methods from inlining In 8020cd6, the inliner was changed to make sure trait methods bodies are not duplicated into the static super accessors, and from there into mixin forwarders. The check for mixin forwarders was too wide. In `def t = super.m`, where `m` is a trait method annotated `@inline`, we want to inline `m`. Note that `super.m` is translated to an `invokestatic T.m$`. The current check incorrectly identifies `t` as a mixin forwarder, and skip inlining. --- .../tools/nsc/backend/jvm/opt/InlinerHeuristics.scala | 12 ++++++++---- .../scala/tools/nsc/backend/jvm/opt/InlinerTest.scala | 15 +++++++++++++++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/InlinerHeuristics.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/InlinerHeuristics.scala index 929e8b5ca4..4744cb9ab1 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/opt/InlinerHeuristics.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/InlinerHeuristics.scala @@ -65,12 +65,13 @@ class InlinerHeuristics[BT <: BTypes](val bTypes: BT) { } private def isTraitStaticSuperAccessorName(s: String) = s.endsWith("$") + private def traitStaticSuperAccessorName(s: String) = s + "$" private def isTraitSuperAccessor(method: MethodNode, owner: ClassBType): Boolean = { owner.isInterface == Right(true) && BytecodeUtils.isStaticMethod(method) && isTraitStaticSuperAccessorName(method.name) } - private def findCall(method: MethodNode, such: MethodInsnNode => Boolean): Option[MethodInsnNode] = { + private def findSingleCall(method: MethodNode, such: MethodInsnNode => Boolean): Option[MethodInsnNode] = { @tailrec def noMoreInvoke(insn: AbstractInsnNode): Boolean = { insn == null || (!insn.isInstanceOf[MethodInsnNode] && noMoreInvoke(insn.getNext)) } @@ -87,12 +88,15 @@ class InlinerHeuristics[BT <: BTypes](val bTypes: BT) { find(method.instructions.getFirst) } private def superAccessorInvocation(method: MethodNode): Option[MethodInsnNode] = - findCall(method, mi => mi.itf && mi.getOpcode == Opcodes.INVOKESTATIC && isTraitStaticSuperAccessorName(mi.name)) + findSingleCall(method, mi => mi.itf && mi.getOpcode == Opcodes.INVOKESTATIC && isTraitStaticSuperAccessorName(mi.name)) private def isMixinForwarder(method: MethodNode, owner: ClassBType): Boolean = { owner.isInterface == Right(false) && !BytecodeUtils.isStaticMethod(method) && - superAccessorInvocation(method).nonEmpty + (superAccessorInvocation(method) match { + case Some(mi) => mi.name == traitStaticSuperAccessorName(method.name) + case _ => false + }) } private def isTraitSuperAccessorOrMixinForwarder(method: MethodNode, owner: ClassBType): Boolean = { @@ -138,7 +142,7 @@ class InlinerHeuristics[BT <: BTypes](val bTypes: BT) { if (isTraitSuperAccessor(callee.callee, callee.calleeDeclarationClass)) { // scala-dev#259: when inlining a trait super accessor, also inline the callsite to the default method val implName = callee.callee.name.dropRight(1) - findCall(callee.callee, mi => mi.itf && mi.getOpcode == Opcodes.INVOKESPECIAL && mi.name == implName) + findSingleCall(callee.callee, mi => mi.itf && mi.getOpcode == Opcodes.INVOKESPECIAL && mi.name == implName) } else { // scala-dev#259: when inlining a mixin forwarder, also inline the callsite to the static super accessor superAccessorInvocation(callee.callee) diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala index 90a938be35..7be88816d5 100644 --- a/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala +++ b/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala @@ -1738,4 +1738,19 @@ class InlinerTest extends BytecodeTesting { val List(a, c, t) = compile(code, allowMessage = _.msg contains warn) assertInvoke(getMethod(c, "t"), "T", "m$") } + + @Test + def sd259d(): Unit = { + val code = + """trait T { + | @inline final def m = 1 + |} + |class C extends T { + | def t = super.m // inline call to T.m$ here, we're not in the mixin forwarder C.m + |} + """.stripMargin + val List(c, t) = compileClasses(code) + assertNoInvoke(getMethod(c, "t")) + assertInvoke(getMethod(c, "m"), "T", "m$") + } } -- cgit v1.2.3