From e483fe26825501c175b701a662160dd0e54b035e Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Tue, 8 Mar 2016 21:20:28 +0100 Subject: Inline super calls, as they are statically resolved Ensures that mixin methods of `@inline` annotated concrete trait methods inline the trait method. Fixes https://github.com/scala/scala-dev/issues/86 --- src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala | 5 +++++ src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala | 7 ++++--- 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'src/compiler') diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala index 9a90c53d3e..f48f60a438 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala @@ -88,6 +88,11 @@ object BytecodeUtils { def isLoadOrStore(instruction: AbstractInsnNode): Boolean = isLoad(instruction) || isStore(instruction) + def isNonVirtualCall(instruction: AbstractInsnNode): Boolean = { + val op = instruction.getOpcode + op == INVOKESPECIAL || op == INVOKESTATIC + } + def isExecutable(instruction: AbstractInsnNode): Boolean = instruction.getOpcode >= 0 def isConstructor(methodNode: MethodNode): Boolean = { diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala index 6dd74bad84..3267b9b5df 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala @@ -132,7 +132,7 @@ class CallGraph[BT <: BTypes](val btypes: BT) { (declarationClassNode, source) <- byteCodeRepository.classNodeAndSource(declarationClass): Either[OptimizerWarning, (ClassNode, Source)] } yield { val declarationClassBType = classBTypeFromClassNode(declarationClassNode) - val CallsiteInfo(safeToInline, safeToRewrite, canInlineFromSource, annotatedInline, annotatedNoInline, samParamTypes, warning) = analyzeCallsite(method, declarationClassBType, call.owner, source) + val CallsiteInfo(safeToInline, safeToRewrite, canInlineFromSource, annotatedInline, annotatedNoInline, samParamTypes, warning) = analyzeCallsite(method, declarationClassBType, call, source) Callee( callee = method, calleeDeclarationClass = declarationClassBType, @@ -264,7 +264,7 @@ class CallGraph[BT <: BTypes](val btypes: BT) { /** * Analyze a callsite and gather meta-data that can be used for inlining decisions. */ - private def analyzeCallsite(calleeMethodNode: MethodNode, calleeDeclarationClassBType: ClassBType, receiverTypeInternalName: InternalName, calleeSource: Source): CallsiteInfo = { + private def analyzeCallsite(calleeMethodNode: MethodNode, calleeDeclarationClassBType: ClassBType, call: MethodInsnNode, calleeSource: Source): CallsiteInfo = { val methodSignature = calleeMethodNode.name + calleeMethodNode.desc try { @@ -277,7 +277,7 @@ class CallGraph[BT <: BTypes](val btypes: BT) { val isAbstract = BytecodeUtils.isAbstractMethod(calleeMethodNode) - val receiverType = classBTypeFromParsedClassfile(receiverTypeInternalName) + val receiverType = classBTypeFromParsedClassfile(call.owner) // (1) A non-final method can be safe to inline if the receiver type is a final subclass. Example: // class A { @inline def f = 1 }; object B extends A; B.f // can be inlined // @@ -295,6 +295,7 @@ class CallGraph[BT <: BTypes](val btypes: BT) { // TODO: type analysis can render more calls statically resolved. Example: // new A.f // can be inlined, the receiver type is known to be exactly A. val isStaticallyResolved: Boolean = { + isNonVirtualCall(call) || // SD-86: super calls (invokespecial) can be inlined methodInlineInfo.effectivelyFinal || receiverType.info.orThrow.inlineInfo.isEffectivelyFinal // (1) } -- cgit v1.2.3