diff options
author | Lukas Rytz <lukas.rytz@gmail.com> | 2015-10-22 18:32:19 +0200 |
---|---|---|
committer | Lukas Rytz <lukas.rytz@gmail.com> | 2015-10-27 10:40:52 +0100 |
commit | 3a9581d32f9d3adb1dcb0b9c4bfeb9c86f0addcf (patch) | |
tree | 0128d61ec925cf385df4358bfcecb7d74ae09fcd /src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala | |
parent | 4321ea458ad1258f273ee22a4c6a7606ab054501 (diff) | |
download | scala-3a9581d32f9d3adb1dcb0b9c4bfeb9c86f0addcf.tar.gz scala-3a9581d32f9d3adb1dcb0b9c4bfeb9c86f0addcf.tar.bz2 scala-3a9581d32f9d3adb1dcb0b9c4bfeb9c86f0addcf.zip |
More efficient way to compute maxLocals / maxStack
In order to run an asm Analyzer, the maxLocals / maxStack values of
the method need to be computed. Asm doesn't provide an efficient
built-in for this purpose, but it computes these values while
serializing a class.
Previously, we used to serialize the method just to compute the max's,
which is inefficient. This commit implements a separate, efficient
traversal.
The computed values are also smaller, allowing to save space when
running an Analyzer: asm Analyzers only allocate a single stack slot
for long/double values, while the JVM allocates two. The maxStack
computed previously would always use two slots, which is not
necessary.
The new calculation was verified to be correct in the following way:
as a test, i left the old computation in place, ran the new one in
addition (in a special mode where the long/double values take two
slots) and asserted equality. Bootstrapping and test suite passed.
Diffstat (limited to 'src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala')
-rw-r--r-- | src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala | 34 |
1 files changed, 1 insertions, 33 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala index 38f3c51892..a80b3d0487 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala @@ -51,38 +51,6 @@ class LocalOpt[BT <: BTypes](val btypes: BT) { import backendUtils._ /** - * In order to run an Analyzer, the maxLocals / maxStack fields need to be available. The ASM - * framework only computes these values during bytecode generation. - * - * Since there's currently no better way, we run a bytecode generator on the method and extract - * the computed values. This required changes to the ASM codebase: - * - the [[MethodWriter]] class was made public - * - accessors for maxLocals / maxStack were added to the MethodWriter class - * - * We could probably make this faster (and allocate less memory) by hacking the ASM framework - * more: create a subclass of MethodWriter with a /dev/null byteVector. Another option would be - * to create a separate visitor for computing those values, duplicating the functionality from the - * MethodWriter. - * - * NOTE: the maxStack value computed by this method allocates two slots for long / double values, - * as required by the JVM spec. For running an Analyzer, one slot per long / double would be fine. - * See comment in `analysis` package object. - */ - def computeMaxLocalsMaxStack(method: MethodNode): Unit = { - if (!maxLocalsMaxStackComputed(method)) { - method.maxLocals = 0 - method.maxStack = 0 - val cw = new ClassWriter(ClassWriter.COMPUTE_MAXS) - val excs = method.exceptions.asScala.toArray - val mw = cw.visitMethod(method.access, method.name, method.desc, method.signature, excs).asInstanceOf[MethodWriter] - method.accept(mw) - method.maxLocals = mw.getMaxLocals - method.maxStack = mw.getMaxStack - maxLocalsMaxStackComputed += method - } - } - - /** * Remove unreachable code from a method. * * This implementation only removes instructions that are unreachable for an ASM analyzer / @@ -225,7 +193,7 @@ class LocalOpt[BT <: BTypes](val btypes: BT) { var i = 0 var liveLabels = Set.empty[LabelNode] var removedInstructions = Set.empty[AbstractInsnNode] - var maxLocals = Type.getArgumentsAndReturnSizes(method.desc) >> 2 - (if (BytecodeUtils.isStaticMethod(method)) 1 else 0) + var maxLocals = (Type.getArgumentsAndReturnSizes(method.desc) >> 2) - (if (BytecodeUtils.isStaticMethod(method)) 1 else 0) var maxStack = 0 val itr = method.instructions.iterator() while (itr.hasNext) { |