summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLukas Rytz <lukas.rytz@gmail.com>2015-08-28 13:18:00 +0200
committerLukas Rytz <lukas.rytz@gmail.com>2015-08-28 13:19:41 +0200
commit2ae4d48ada4b1d17a0536d1df9c9d21c8cf5a951 (patch)
tree255882b5dd23f4b0ffb36a0b4b58dcaf12294bf0
parent246f3eb69707db9ef8185e68408b3ddaba9ac17d (diff)
downloadscala-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.scala6
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala4
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) = {