Skip to content

Commit ab78bb6

Browse files
committed
don't treat invokespecial as statically resolved
scala/scala-dev#143
1 parent 6ebf1fd commit ab78bb6

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
@@ -87,18 +87,18 @@ object BytecodeUtils {
8787

8888
def isLoadOrStore(instruction: AbstractInsnNode): Boolean = isLoad(instruction) || isStore(instruction)
8989

90-
def isNonVirtualCall(instruction: AbstractInsnNode): Boolean = {
91-
val op = instruction.getOpcode
92-
op == INVOKESPECIAL || op == INVOKESTATIC
90+
def isStaticCall(instruction: AbstractInsnNode): Boolean = {
91+
instruction.getOpcode == INVOKESTATIC
9392
}
9493

9594
def isVirtualCall(instruction: AbstractInsnNode): Boolean = {
9695
val op = instruction.getOpcode
97-
op == INVOKEVIRTUAL || op == INVOKEINTERFACE
96+
// invokespecial
97+
op == INVOKESPECIAL || op == INVOKEVIRTUAL || op == INVOKEINTERFACE
9898
}
9999

100100
def isCall(instruction: AbstractInsnNode): Boolean = {
101-
isNonVirtualCall(instruction) || isVirtualCall(instruction)
101+
isStaticCall(instruction) || isVirtualCall(instruction)
102102
}
103103

104104
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
@@ -156,7 +156,7 @@ abstract class CallGraph {
156156
(declarationClassNode, calleeSourceFilePath) <- byteCodeRepository.classNodeAndSourceFilePath(declarationClass): Either[OptimizerWarning, (ClassNode, Option[String])]
157157
} yield {
158158
val declarationClassBType = classBTypeFromClassNode(declarationClassNode)
159-
val info = analyzeCallsite(method, declarationClassBType, call, calleeSourceFilePath)
159+
val info = analyzeCallsite(method, declarationClassBType, call, calleeSourceFilePath, definingClass)
160160
import info._
161161
Callee(
162162
callee = method,
@@ -288,7 +288,7 @@ abstract class CallGraph {
288288
/**
289289
* Analyze a callsite and gather meta-data that can be used for inlining decisions.
290290
*/
291-
private def analyzeCallsite(calleeMethodNode: MethodNode, calleeDeclarationClassBType: ClassBType, call: MethodInsnNode, calleeSourceFilePath: Option[String]): CallsiteInfo = {
291+
private def analyzeCallsite(calleeMethodNode: MethodNode, calleeDeclarationClassBType: ClassBType, call: MethodInsnNode, calleeSourceFilePath: Option[String], callsiteClass: ClassBType): CallsiteInfo = {
292292
val methodSignature = calleeMethodNode.name + calleeMethodNode.desc
293293

294294
try {
@@ -297,12 +297,16 @@ abstract class CallGraph {
297297
// callee, we only check there for the methodInlineInfo, we should find it there.
298298
calleeDeclarationClassBType.info.orThrow.inlineInfo.methodInfos.get(methodSignature) match {
299299
case Some(methodInlineInfo) =>
300-
301300
val receiverType = classBTypeFromParsedClassfile(call.owner)
302-
// (1) A non-final method can be safe to inline if the receiver type is a final subclass. Example:
301+
// (1) Special case for trait super accessors. trait T { def f = 1 } generates a static
302+
// method t$ which calls `invokespecial T.f`. Even if `f` is not final, this call will
303+
// always resolve to `T.f`. This is a (very) special case. Otherwise, `invokespecial`
304+
// is only used for private methods, constructors and super calls.
305+
//
306+
// (2) A non-final method can be safe to inline if the receiver type is a final subclass. Example:
303307
// class A { @inline def f = 1 }; object B extends A; B.f // can be inlined
304308
//
305-
// TODO: (1) doesn't cover the following example:
309+
// TODO: (2) doesn't cover the following example:
306310
// trait TravLike { def map = ... }
307311
// sealed trait List extends TravLike { ... } // assume map is not overridden
308312
// final case class :: / final case object Nil
@@ -316,9 +320,10 @@ abstract class CallGraph {
316320
// TODO: type analysis can render more calls statically resolved. Example:
317321
// new A.f // can be inlined, the receiver type is known to be exactly A.
318322
val isStaticallyResolved: Boolean = {
319-
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
320-
methodInlineInfo.effectivelyFinal ||
321-
receiverType.info.orThrow.inlineInfo.isEffectivelyFinal // (1)
323+
isStaticCall(call) ||
324+
(call.getOpcode == Opcodes.INVOKESPECIAL && receiverType == callsiteClass) || // (1)
325+
methodInlineInfo.effectivelyFinal ||
326+
receiverType.info.orThrow.inlineInfo.isEffectivelyFinal // (2)
322327
}
323328

324329
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
@@ -57,7 +57,7 @@ abstract class InlinerHeuristics extends PerRunInit {
5757
var requests = Set.empty[InlineRequest]
5858
callGraph.callsites(methodNode).valuesIterator foreach {
5959
case callsite @ Callsite(_, _, _, Right(Callee(callee, _, _, _, _, _, _, callsiteWarning)), _, _, _, pos, _, _) =>
60-
inlineRequest(callsite, requests) match {
60+
inlineRequest(callsite) match {
6161
case Some(Right(req)) => requests += req
6262

6363
case Some(Left(w)) =>
@@ -134,7 +134,7 @@ abstract class InlinerHeuristics extends PerRunInit {
134134
* InlineRequest for the original callsite? new subclass of OptimizerWarning.
135135
* `Some(Right)` if the callsite should be and can be inlined
136136
*/
137-
def inlineRequest(callsite: Callsite, selectedRequestsForCallee: Set[InlineRequest]): Option[Either[OptimizerWarning, InlineRequest]] = {
137+
def inlineRequest(callsite: Callsite): Option[Either[OptimizerWarning, InlineRequest]] = {
138138
def requestIfCanInline(callsite: Callsite, reason: String): Option[Either[OptimizerWarning, InlineRequest]] = {
139139
val callee = callsite.callee.get
140140
if (!callee.safeToInline) {

0 commit comments

Comments
 (0)