From 708868227d43b3f0c0a6037b27c15faa9b2110e3 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Wed, 23 Sep 2015 13:39:22 +0200 Subject: Rename the Analyzers backend component to BackendUtils Until now, there was no good place to hold various utility functions that are used acrosss the backend / optimizer. --- .../tools/nsc/backend/jvm/BCodeBodyBuilder.scala | 3 +- .../scala/tools/nsc/backend/jvm/BTypes.scala | 6 +-- .../tools/nsc/backend/jvm/BTypesFromSymbols.scala | 6 +-- .../tools/nsc/backend/jvm/analysis/Analyzers.scala | 48 --------------------- .../nsc/backend/jvm/analysis/BackendUtils.scala | 50 ++++++++++++++++++++++ .../tools/nsc/backend/jvm/opt/CallGraph.scala | 2 +- .../nsc/backend/jvm/opt/ClosureOptimizer.scala | 2 +- .../scala/tools/nsc/backend/jvm/opt/Inliner.scala | 2 +- .../scala/tools/nsc/backend/jvm/opt/LocalOpt.scala | 2 +- .../jvm/analysis/NullnessAnalyzerTest.scala | 2 +- .../jvm/analysis/ProdConsAnalyzerTest.scala | 2 +- .../tools/nsc/backend/jvm/opt/InlinerTest.scala | 2 +- 12 files changed, 65 insertions(+), 62 deletions(-) delete mode 100644 src/compiler/scala/tools/nsc/backend/jvm/analysis/Analyzers.scala create mode 100644 src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala index e5eb0b79d5..98e8fae51a 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala @@ -11,6 +11,7 @@ package jvm import scala.annotation.switch import scala.reflect.internal.Flags +import java.lang.invoke.LambdaMetafactory import scala.tools.asm import GenBCode._ @@ -1303,7 +1304,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { val samName = sam.name.toString val samMethodType = asmMethodType(sam).toASMType - val flags = 3 // TODO 2.12.x Replace with LambdaMetafactory.FLAG_SERIALIZABLE | LambdaMetafactory.FLAG_MARKERS + val flags = LambdaMetafactory.FLAG_SERIALIZABLE | LambdaMetafactory.FLAG_MARKERS val ScalaSerializable = classBTypeFromSymbol(definitions.SerializableClass).toASMType bc.jmethod.visitInvokeDynamicInsn(samName, invokedType, lambdaMetaFactoryBootstrapHandle, diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala index aff2d2d8c9..518c808488 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala @@ -14,7 +14,7 @@ import asm.Opcodes import scala.tools.asm.tree._ import scala.tools.nsc.backend.jvm.BTypes.{InlineInfo, MethodInlineInfo} import scala.tools.nsc.backend.jvm.BackendReporting._ -import scala.tools.nsc.backend.jvm.analysis.Analyzers +import scala.tools.nsc.backend.jvm.analysis.BackendUtils import scala.tools.nsc.backend.jvm.opt._ import scala.collection.convert.decorateAsScala._ import scala.tools.nsc.settings.ScalaSettings @@ -30,6 +30,8 @@ import scala.tools.nsc.settings.ScalaSettings abstract class BTypes { import BTypes.InternalName + val backendUtils: BackendUtils[this.type] + // Some core BTypes are required here, in class BType, where no Global instance is available. // The Global is only available in the subclass BTypesFromSymbols. We cannot depend on the actual // implementation (CoreBTypesProxy) here because it has members that refer to global.Symbol. @@ -51,8 +53,6 @@ abstract class BTypes { val callGraph: CallGraph[this.type] - val analyzers: Analyzers[this.type] - val backendReporting: BackendReporting // Allows to define per-run caches here and in the CallGraph component, which don't have a global diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala index df34326bbf..79b5e6a2fb 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala @@ -7,7 +7,7 @@ package scala.tools.nsc package backend.jvm import scala.tools.asm -import scala.tools.nsc.backend.jvm.analysis.Analyzers +import scala.tools.nsc.backend.jvm.analysis.BackendUtils import scala.tools.nsc.backend.jvm.opt._ import scala.tools.nsc.backend.jvm.BTypes._ import BackendReporting._ @@ -33,6 +33,8 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes { val bCodeAsmCommon: BCodeAsmCommon[global.type] = new BCodeAsmCommon(global) import bCodeAsmCommon._ + val backendUtils: BackendUtils[this.type] = new BackendUtils(this) + // Why the proxy, see documentation of class [[CoreBTypes]]. val coreBTypes = new CoreBTypesProxy[this.type](this) import coreBTypes._ @@ -49,8 +51,6 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes { val callGraph: CallGraph[this.type] = new CallGraph(this) - val analyzers: Analyzers[this.type] = new Analyzers(this) - val backendReporting: BackendReporting = new BackendReportingImpl(global) final def initializeCoreBTypes(): Unit = { diff --git a/src/compiler/scala/tools/nsc/backend/jvm/analysis/Analyzers.scala b/src/compiler/scala/tools/nsc/backend/jvm/analysis/Analyzers.scala deleted file mode 100644 index bb5c6e3820..0000000000 --- a/src/compiler/scala/tools/nsc/backend/jvm/analysis/Analyzers.scala +++ /dev/null @@ -1,48 +0,0 @@ -package scala.tools.nsc -package backend.jvm -package analysis - -import scala.tools.asm.tree.{AbstractInsnNode, MethodNode} -import scala.tools.asm.tree.analysis.{Frame, BasicInterpreter, Analyzer, Value} -import scala.tools.nsc.backend.jvm.BTypes._ -import scala.tools.nsc.backend.jvm.opt.BytecodeUtils._ - -/** - * This component hosts tools for running ASM analyzers that require access to a `BTypes` instance. - * In particular, the AsmAnalyzer class runs `computeMaxLocalsMaxStack` on the methodNode to be - * analyzed. This method in turn lives inside the BTypes assembly because it queries the per-run - * cache `maxLocalsMaxStackComputed` defined in there. - */ -class Analyzers[BT <: BTypes](val btypes: BT) { - import btypes._ - - /** - * A wrapper to make ASM's Analyzer a bit easier to use. - */ - class AsmAnalyzer[V <: Value](methodNode: MethodNode, classInternalName: InternalName, val analyzer: Analyzer[V] = new Analyzer(new BasicInterpreter)) { - localOpt.computeMaxLocalsMaxStack(methodNode) - analyzer.analyze(classInternalName, methodNode) - def frameAt(instruction: AbstractInsnNode): Frame[V] = analyzer.frameAt(instruction, methodNode) - } - - /** - * See the doc comment on package object `analysis` for a discussion on performance. - */ - object AsmAnalyzer { - // jvm limit is 65535 for both number of instructions and number of locals - - private def size(method: MethodNode) = method.instructions.size.toLong * method.maxLocals * method.maxLocals - - // with the limits below, analysis should not take more than one second - - private val nullnessSizeLimit = 5000l * 600l * 600l // 5000 insns, 600 locals - private val basicValueSizeLimit = 9000l * 1000l * 1000l - private val sourceValueSizeLimit = 8000l * 950l * 950l - - def sizeOKForNullness(method: MethodNode): Boolean = size(method) < nullnessSizeLimit - def sizeOKForBasicValue(method: MethodNode): Boolean = size(method) < basicValueSizeLimit - def sizeOKForSourceValue(method: MethodNode): Boolean = size(method) < sourceValueSizeLimit - } - - class ProdConsAnalyzer(val methodNode: MethodNode, classInternalName: InternalName) extends AsmAnalyzer(methodNode, classInternalName, new Analyzer(new InitialProducerSourceInterpreter)) with ProdConsAnalyzerImpl -} diff --git a/src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala b/src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala new file mode 100644 index 0000000000..f4badd8383 --- /dev/null +++ b/src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala @@ -0,0 +1,50 @@ +package scala.tools.nsc +package backend.jvm +package analysis + +import scala.tools.asm.tree.{AbstractInsnNode, MethodNode} +import scala.tools.asm.tree.analysis.{Frame, BasicInterpreter, Analyzer, Value} +import scala.tools.nsc.backend.jvm.BTypes._ +import scala.tools.nsc.backend.jvm.opt.BytecodeUtils._ + +/** + * This component hosts tools and utilities used in the backend that require access to a `BTypes` + * instance. + * + * One example is the AsmAnalyzer class, which runs `computeMaxLocalsMaxStack` on the methodNode to + * be analyzed. This method in turn lives inside the BTypes assembly because it queries the per-run + * cache `maxLocalsMaxStackComputed` defined in there. + */ +class BackendUtils[BT <: BTypes](val btypes: BT) { + import btypes._ + + /** + * A wrapper to make ASM's Analyzer a bit easier to use. + */ + class AsmAnalyzer[V <: Value](methodNode: MethodNode, classInternalName: InternalName, val analyzer: Analyzer[V] = new Analyzer(new BasicInterpreter)) { + localOpt.computeMaxLocalsMaxStack(methodNode) + analyzer.analyze(classInternalName, methodNode) + def frameAt(instruction: AbstractInsnNode): Frame[V] = analyzer.frameAt(instruction, methodNode) + } + + /** + * See the doc comment on package object `analysis` for a discussion on performance. + */ + object AsmAnalyzer { + // jvm limit is 65535 for both number of instructions and number of locals + + private def size(method: MethodNode) = method.instructions.size.toLong * method.maxLocals * method.maxLocals + + // with the limits below, analysis should not take more than one second + + private val nullnessSizeLimit = 5000l * 600l * 600l // 5000 insns, 600 locals + private val basicValueSizeLimit = 9000l * 1000l * 1000l + private val sourceValueSizeLimit = 8000l * 950l * 950l + + def sizeOKForNullness(method: MethodNode): Boolean = size(method) < nullnessSizeLimit + def sizeOKForBasicValue(method: MethodNode): Boolean = size(method) < basicValueSizeLimit + def sizeOKForSourceValue(method: MethodNode): Boolean = size(method) < sourceValueSizeLimit + } + + class ProdConsAnalyzer(val methodNode: MethodNode, classInternalName: InternalName) extends AsmAnalyzer(methodNode, classInternalName, new Analyzer(new InitialProducerSourceInterpreter)) with ProdConsAnalyzerImpl +} diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala index b9788c3f56..32eaf07080 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala @@ -21,7 +21,7 @@ import BytecodeUtils._ class CallGraph[BT <: BTypes](val btypes: BT) { import btypes._ - import analyzers._ + import backendUtils._ /** * The call graph contains the callsites in the program being compiled. diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/ClosureOptimizer.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/ClosureOptimizer.scala index fb7dd16909..a7c4c27a97 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/opt/ClosureOptimizer.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/ClosureOptimizer.scala @@ -23,7 +23,7 @@ import scala.collection.convert.decorateAsScala._ class ClosureOptimizer[BT <: BTypes](val btypes: BT) { import btypes._ import callGraph._ - import analyzers._ + import backendUtils._ /** * If a closure is allocated and invoked within the same method, re-write the invocation to the 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 baa747492f..a2ee4f8ad4 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala @@ -24,7 +24,7 @@ class Inliner[BT <: BTypes](val btypes: BT) { import btypes._ import callGraph._ import inlinerHeuristics._ - import analyzers._ + import backendUtils._ def eliminateUnreachableCodeAndUpdateCallGraph(methodNode: MethodNode, definingClass: InternalName): Unit = { localOpt.minimalRemoveUnreachableCode(methodNode, definingClass) foreach { 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 1e7b46012e..38f3c51892 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala @@ -48,7 +48,7 @@ import scala.tools.nsc.backend.jvm.opt.BytecodeUtils._ class LocalOpt[BT <: BTypes](val btypes: BT) { import LocalOptImpls._ import btypes._ - import analyzers._ + import backendUtils._ /** * In order to run an Analyzer, the maxLocals / maxStack fields need to be available. The ASM diff --git a/test/junit/scala/tools/nsc/backend/jvm/analysis/NullnessAnalyzerTest.scala b/test/junit/scala/tools/nsc/backend/jvm/analysis/NullnessAnalyzerTest.scala index f78d450db1..a7d1dc168a 100644 --- a/test/junit/scala/tools/nsc/backend/jvm/analysis/NullnessAnalyzerTest.scala +++ b/test/junit/scala/tools/nsc/backend/jvm/analysis/NullnessAnalyzerTest.scala @@ -31,7 +31,7 @@ object NullnessAnalyzerTest extends ClearAfterClass.Clearable { class NullnessAnalyzerTest extends ClearAfterClass { ClearAfterClass.stateToClear = NullnessAnalyzerTest val noOptCompiler = NullnessAnalyzerTest.noOptCompiler - import noOptCompiler.genBCode.bTypes.analyzers._ + import noOptCompiler.genBCode.bTypes.backendUtils._ def newNullnessAnalyzer(methodNode: MethodNode, classInternalName: InternalName = "C") = new AsmAnalyzer(methodNode, classInternalName, new NullnessAnalyzer) diff --git a/test/junit/scala/tools/nsc/backend/jvm/analysis/ProdConsAnalyzerTest.scala b/test/junit/scala/tools/nsc/backend/jvm/analysis/ProdConsAnalyzerTest.scala index 155e0a6017..4835eb3cdf 100644 --- a/test/junit/scala/tools/nsc/backend/jvm/analysis/ProdConsAnalyzerTest.scala +++ b/test/junit/scala/tools/nsc/backend/jvm/analysis/ProdConsAnalyzerTest.scala @@ -26,7 +26,7 @@ object ProdConsAnalyzerTest extends ClearAfterClass.Clearable { class ProdConsAnalyzerTest extends ClearAfterClass { ClearAfterClass.stateToClear = ProdConsAnalyzerTest val noOptCompiler = ProdConsAnalyzerTest.noOptCompiler - import noOptCompiler.genBCode.bTypes.analyzers._ + import noOptCompiler.genBCode.bTypes.backendUtils._ def prodToString(producer: AbstractInsnNode) = producer match { case p: InitialProducer => p.toString 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 8429a583b5..1108a37266 100644 --- a/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala +++ b/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala @@ -68,7 +68,7 @@ class InlinerTest extends ClearAfterClass { val compiler = InlinerTest.compiler import compiler.genBCode.bTypes._ - import compiler.genBCode.bTypes.analyzers._ + import compiler.genBCode.bTypes.backendUtils._ def compile(scalaCode: String, javaCode: List[(String, String)] = Nil, allowMessage: StoreReporter#Info => Boolean = _ => false): List[ClassNode] = { InlinerTest.notPerRun.foreach(_.clear()) -- cgit v1.2.3