Skip to content

Commit 4095a12

Browse files
committed
don't treat invokespecial as statically resolved
scala/scala-dev#143
1 parent 2fc371c commit 4095a12

File tree

3 files changed

+20
-15
lines changed

3 files changed

+20
-15
lines changed

src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -94,18 +94,18 @@ object BytecodeUtils {
9494

9595
def isLoadOrStore(instruction: AbstractInsnNode): Boolean = isLoad(instruction) || isStore(instruction)
9696

97-
def isNonVirtualCall(instruction: AbstractInsnNode): Boolean = {
98-
val op = instruction.getOpcode
99-
op == INVOKESPECIAL || op == INVOKESTATIC
97+
def isStaticCall(instruction: AbstractInsnNode): Boolean = {
98+
instruction.getOpcode == INVOKESTATIC
10099
}
101100

102101
def isVirtualCall(instruction: AbstractInsnNode): Boolean = {
103102
val op = instruction.getOpcode
104-
op == INVOKEVIRTUAL || op == INVOKEINTERFACE
103+
// invokespecial
104+
op == INVOKESPECIAL || op == INVOKEVIRTUAL || op == INVOKEINTERFACE
105105
}
106106

107107
def isCall(instruction: AbstractInsnNode): Boolean = {
108-
isNonVirtualCall(instruction) || isVirtualCall(instruction)
108+
isStaticCall(instruction) || isVirtualCall(instruction)
109109
}
110110

111111
def isExecutable(instruction: AbstractInsnNode): Boolean = instruction.getOpcode >= 0

src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ abstract class CallGraph {
163163
(declarationClassNode, calleeSourceFilePath) <- byteCodeRepository.classNodeAndSourceFilePath(declarationClass): Either[OptimizerWarning, (ClassNode, Option[String])]
164164
} yield {
165165
val declarationClassBType = classBTypeFromClassNode(declarationClassNode)
166-
val info = analyzeCallsite(method, declarationClassBType, call, calleeSourceFilePath)
166+
val info = analyzeCallsite(method, declarationClassBType, call, calleeSourceFilePath, definingClass)
167167
import info._
168168
Callee(
169169
callee = method,
@@ -295,7 +295,7 @@ abstract class CallGraph {
295295
/**
296296
* Analyze a callsite and gather meta-data that can be used for inlining decisions.
297297
*/
298-
private def analyzeCallsite(calleeMethodNode: MethodNode, calleeDeclarationClassBType: ClassBType, call: MethodInsnNode, calleeSourceFilePath: Option[String]): CallsiteInfo = {
298+
private def analyzeCallsite(calleeMethodNode: MethodNode, calleeDeclarationClassBType: ClassBType, call: MethodInsnNode, calleeSourceFilePath: Option[String], callsiteClass: ClassBType): CallsiteInfo = {
299299
val methodSignature = calleeMethodNode.name + calleeMethodNode.desc
300300

301301
try {
@@ -304,12 +304,16 @@ abstract class CallGraph {
304304
// callee, we only check there for the methodInlineInfo, we should find it there.
305305
calleeDeclarationClassBType.info.orThrow.inlineInfo.methodInfos.get(methodSignature) match {
306306
case Some(methodInlineInfo) =>
307-
308307
val receiverType = classBTypeFromParsedClassfile(call.owner)
309-
// (1) A non-final method can be safe to inline if the receiver type is a final subclass. Example:
308+
// (1) Special case for trait super accessors. trait T { def f = 1 } generates a static
309+
// method t$ which calls `invokespecial T.f`. Even if `f` is not final, this call will
310+
// always resolve to `T.f`. This is a (very) special case. Otherwise, `invokespecial`
311+
// is only used for private methods, constructors and super calls.
312+
//
313+
// (2) A non-final method can be safe to inline if the receiver type is a final subclass. Example:
310314
// class A { @inline def f = 1 }; object B extends A; B.f // can be inlined
311315
//
312-
// TODO: (1) doesn't cover the following example:
316+
// TODO: (2) doesn't cover the following example:
313317
// trait TravLike { def map = ... }
314318
// sealed trait List extends TravLike { ... } // assume map is not overridden
315319
// final case class :: / final case object Nil
@@ -323,9 +327,10 @@ abstract class CallGraph {
323327
// TODO: type analysis can render more calls statically resolved. Example:
324328
// new A.f // can be inlined, the receiver type is known to be exactly A.
325329
val isStaticallyResolved: Boolean = {
326-
isNonVirtualCall(call) || // scala/scala-dev#86: super calls (invokespecial) can be inlined -- TODO: check if that's still needed, and if it's correct: scala-dev#143
327-
methodInlineInfo.effectivelyFinal ||
328-
receiverType.info.orThrow.inlineInfo.isEffectivelyFinal // (1)
330+
isStaticCall(call) ||
331+
(call.getOpcode == Opcodes.INVOKESPECIAL && receiverType == callsiteClass) || // (1)
332+
methodInlineInfo.effectivelyFinal ||
333+
receiverType.info.orThrow.inlineInfo.isEffectivelyFinal // (2)
329334
}
330335

331336
val warning = calleeDeclarationClassBType.info.orThrow.inlineInfo.warning.map(

src/compiler/scala/tools/nsc/backend/jvm/opt/InlinerHeuristics.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ abstract class InlinerHeuristics extends PerRunInit {
6464
var requests = Set.empty[InlineRequest]
6565
callGraph.callsites(methodNode).valuesIterator foreach {
6666
case callsite @ Callsite(_, _, _, Right(Callee(callee, _, _, _, _, _, _, callsiteWarning)), _, _, _, pos, _, _) =>
67-
inlineRequest(callsite, requests) match {
67+
inlineRequest(callsite) match {
6868
case Some(Right(req)) => requests += req
6969

7070
case Some(Left(w)) =>
@@ -141,7 +141,7 @@ abstract class InlinerHeuristics extends PerRunInit {
141141
* InlineRequest for the original callsite? new subclass of OptimizerWarning.
142142
* `Some(Right)` if the callsite should be and can be inlined
143143
*/
144-
def inlineRequest(callsite: Callsite, selectedRequestsForCallee: Set[InlineRequest]): Option[Either[OptimizerWarning, InlineRequest]] = {
144+
def inlineRequest(callsite: Callsite): Option[Either[OptimizerWarning, InlineRequest]] = {
145145
def requestIfCanInline(callsite: Callsite, reason: String): Option[Either[OptimizerWarning, InlineRequest]] = {
146146
val callee = callsite.callee.get
147147
if (!callee.safeToInline) {

0 commit comments

Comments
 (0)