diff options
author | Lukas Rytz <lukas.rytz@gmail.com> | 2015-08-28 13:18:00 +0200 |
---|---|---|
committer | Lukas Rytz <lukas.rytz@gmail.com> | 2015-08-28 13:19:41 +0200 |
commit | 2ae4d48ada4b1d17a0536d1df9c9d21c8cf5a951 (patch) | |
tree | 255882b5dd23f4b0ffb36a0b4b58dcaf12294bf0 | |
parent | 246f3eb69707db9ef8185e68408b3ddaba9ac17d (diff) | |
download | scala-2ae4d48ada4b1d17a0536d1df9c9d21c8cf5a951.tar.gz scala-2ae4d48ada4b1d17a0536d1df9c9d21c8cf5a951.tar.bz2 scala-2ae4d48ada4b1d17a0536d1df9c9d21c8cf5a951.zip |
Fix maxStack after inlining
The computed value was too large previously: inlining stores receiver
and argument values of the inlined method into locals.
A too large value doesn't cause any bugs, but data flow analyses would
allocate too large frames.
The test (InlinerTest.maxLocalsMaxStackAfterInline) didn't catch the
bug because it tested the methods of the wrong ClassNode. The
CodeGenTools.compileClasses method returns a ClassNode that is created
from the classfile byte array emitted by the compiler. This ClassNode
is a new object and unrelated to the ClassNode used in the compiler
backend. The test now takes the correct ClassNode from the hash map in
the backend's byteCodeRepository.
-rw-r--r-- | src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala | 6 | ||||
-rw-r--r-- | test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala | 4 |
2 files changed, 8 insertions, 2 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala index cebd5ea1e4..13a5060f07 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala @@ -316,8 +316,9 @@ class Inliner[BT <: BTypes](val btypes: BT) { // We just use an asm.Type here, no need to create the MethodBType. val calleAsmType = asm.Type.getMethodType(callee.desc) + val calleeParamTypes = calleAsmType.getArgumentTypes - for(argTp <- calleAsmType.getArgumentTypes) { + for(argTp <- calleeParamTypes) { val opc = argTp.getOpcode(ISTORE) // returns the correct xSTORE instruction for argTp argStores.insert(new VarInsnNode(opc, nextLocalIndex)) // "insert" is "prepend" - the last argument is on the top of the stack nextLocalIndex += argTp.getSize @@ -423,7 +424,8 @@ class Inliner[BT <: BTypes](val btypes: BT) { unreachableCodeEliminated -= callsiteMethod callsiteMethod.maxLocals += returnType.getSize + callee.maxLocals - callsiteMethod.maxStack = math.max(callsiteMethod.maxStack, callee.maxStack + callsiteStackHeight) + val numStoredArgs = calleeParamTypes.length + (if (isStaticMethod(callee)) 0 else 1) + callsiteMethod.maxStack = math.max(callsiteMethod.maxStack, callee.maxStack + callsiteStackHeight - numStoredArgs) None } 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 c6313a84d2..f085cc917b 100644 --- a/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala +++ b/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala @@ -72,6 +72,10 @@ class InlinerTest extends ClearAfterClass { def compile(scalaCode: String, javaCode: List[(String, String)] = Nil, allowMessage: StoreReporter#Info => Boolean = _ => false): List[ClassNode] = { InlinerTest.notPerRun.foreach(_.clear()) compileClasses(compiler)(scalaCode, javaCode, allowMessage) + // Use the class nodes stored in the byteCodeRepository. The ones returned by compileClasses are not the same, + // these are created new from the classfile byte array. They are completely separate instances which cannot + // be used to look up methods / callsites in the callGraph hash maps for example. + byteCodeRepository.compilingClasses.valuesIterator.toList.sortBy(_.name) } def checkCallsite(callsite: callGraph.Callsite, callee: MethodNode) = { |