From 8d3be4dc79cb4679fc4994c32b21e10847e5518f Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Fri, 22 Jan 2016 22:33:29 +0100 Subject: Harden methods to recognize method invocations to optimize The previous methods to identify method invocations that can be optimized, such as `isPredefAutoBox`, were String-based. Now we obtain class and method signatures from symbols through the BTypes infrastructure. We also piggy-back on specialization's type transformer to create all specialized subclasses of Tuple1/Tuple2. We'll do the same in the future for FunctionN, but the current JFunctionN are written in Java and specialized artisanally. --- src/compiler/scala/tools/nsc/Global.scala | 2 +- .../tools/nsc/backend/jvm/BCodeBodyBuilder.scala | 4 +- .../scala/tools/nsc/backend/jvm/BTypes.scala | 2 +- .../tools/nsc/backend/jvm/BTypesFromSymbols.scala | 8 +- .../scala/tools/nsc/backend/jvm/CoreBTypes.scala | 273 ++++++++++++++------ .../nsc/backend/jvm/analysis/BackendUtils.scala | 138 +++++++++- .../jvm/analysis/InstructionStackEffect.scala | 1 - .../backend/jvm/analysis/NullnessAnalyzer.scala | 6 +- .../tools/nsc/backend/jvm/opt/BytecodeUtils.scala | 285 --------------------- .../tools/nsc/backend/jvm/opt/CallGraph.scala | 2 +- .../nsc/backend/jvm/opt/ClosureOptimizer.scala | 20 +- .../scala/tools/nsc/backend/jvm/opt/LocalOpt.scala | 2 +- .../scala/tools/nsc/transform/LambdaLift.scala | 5 - .../scala/reflect/internal/Definitions.scala | 4 + .../scala/reflect/runtime/JavaUniverseForce.scala | 1 + .../jvm/analysis/NullnessAnalyzerTest.scala | 2 +- 16 files changed, 350 insertions(+), 405 deletions(-) diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 422e2080f0..c6124e7177 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -1353,7 +1353,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) val inlineExceptionHandlersPhase = phaseNamed("inlinehandlers") val closelimPhase = phaseNamed("closelim") val dcePhase = phaseNamed("dce") - // val jvmPhase = phaseNamed("jvm") + val jvmPhase = phaseNamed("jvm") def runIsAt(ph: Phase) = globalPhase.id == ph.id def runIsAtOptimiz = { diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala index adaf870c46..58c1d0e497 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala @@ -643,7 +643,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { case Apply(fun @ _, List(expr)) if currentRun.runDefinitions.isBox(fun.symbol) => val nativeKind = tpeTK(expr) genLoad(expr, nativeKind) - val MethodNameAndType(mname, methodType) = asmBoxTo(nativeKind) + val MethodNameAndType(mname, methodType) = srBoxesRuntimeBoxToMethods(nativeKind) bc.invokestatic(srBoxesRunTimeRef.internalName, mname, methodType.descriptor, app.pos) generatedType = boxResultType(fun.symbol) // was typeToBType(fun.symbol.tpe.resultType) @@ -651,7 +651,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { genLoad(expr) val boxType = unboxResultType(fun.symbol) // was typeToBType(fun.symbol.owner.linkedClassOfClass.tpe) generatedType = boxType - val MethodNameAndType(mname, methodType) = asmUnboxTo(boxType) + val MethodNameAndType(mname, methodType) = srBoxesRuntimeUnboxToMethods(boxType) bc.invokestatic(srBoxesRunTimeRef.internalName, mname, methodType.descriptor, app.pos) case app @ Apply(fun, args) => diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala index ef3fab7617..f1b9e8ea94 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala @@ -1113,7 +1113,7 @@ abstract class BTypes { */ /** - * Just a named pair, used in CoreBTypes.asmBoxTo/asmUnboxTo. + * Just a named pair, used in CoreBTypes.srBoxesRuntimeBoxToMethods/srBoxesRuntimeUnboxToMethods. */ final case class MethodNameAndType(name: String, methodType: MethodBType) diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala index a2bf8e5725..35b84cc4a9 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala @@ -109,7 +109,7 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes { assert(classSym != NoSymbol, "Cannot create ClassBType from NoSymbol") assert(classSym.isClass, s"Cannot create ClassBType from non-class symbol $classSym") assertClassNotArrayNotPrimitive(classSym) - assert(!primitiveTypeMap.contains(classSym) || isCompilingPrimitive, s"Cannot create ClassBType for primitive class symbol $classSym") + assert(!primitiveTypeToBType.contains(classSym) || isCompilingPrimitive, s"Cannot create ClassBType for primitive class symbol $classSym") if (classSym == NothingClass) srNothingRef else if (classSym == NullClass) srNullRef else { @@ -155,7 +155,7 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes { def primitiveOrClassToBType(sym: Symbol): BType = { assertClassNotArray(sym) assert(!sym.isImplClass, sym) - primitiveTypeMap.getOrElse(sym, classBTypeFromSymbol(sym)) + primitiveTypeToBType.getOrElse(sym, classBTypeFromSymbol(sym)) } /** @@ -217,7 +217,7 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes { def assertClassNotArrayNotPrimitive(sym: Symbol): Unit = { assertClassNotArray(sym) - assert(!primitiveTypeMap.contains(sym) || isCompilingPrimitive, sym) + assert(!primitiveTypeToBType.contains(sym) || isCompilingPrimitive, sym) } private def setClassInfo(classSym: Symbol, classBType: ClassBType): ClassBType = { @@ -240,7 +240,7 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes { superClassSym == ObjectClass else // A ClassBType for a primitive class (scala.Boolean et al) is only created when compiling these classes. - ((superClassSym != NoSymbol) && !superClassSym.isInterface) || (isCompilingPrimitive && primitiveTypeMap.contains(classSym)), + ((superClassSym != NoSymbol) && !superClassSym.isInterface) || (isCompilingPrimitive && primitiveTypeToBType.contains(classSym)), s"Bad superClass for $classSym: $superClassSym" ) val superClass = if (superClassSym == NoSymbol) None diff --git a/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala index 0317e08d9e..0eaf509133 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala @@ -1,7 +1,7 @@ package scala.tools.nsc package backend.jvm -import scala.annotation.switch +import scala.tools.nsc.backend.jvm.BTypes.InternalName /** * Core BTypes and some other definitions. The initialization of these definitions requires access @@ -29,14 +29,14 @@ import scala.annotation.switch class CoreBTypes[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes: BTFS) { import bTypes._ import global._ - import rootMirror.{requiredClass, requiredModule, getClassIfDefined} + import rootMirror.{requiredClass, requiredModule, getRequiredClass, getClassIfDefined} import definitions._ /** * Maps primitive types to their corresponding PrimitiveBType. The map is defined lexically above * the first use of `classBTypeFromSymbol` because that method looks at the map. */ - lazy val primitiveTypeMap: Map[Symbol, PrimitiveBType] = Map( + lazy val primitiveTypeToBType: Map[Symbol, PrimitiveBType] = Map( UnitClass -> UNIT, BooleanClass -> BOOL, CharClass -> CHAR, @@ -45,34 +45,22 @@ class CoreBTypes[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes: BTFS) { IntClass -> INT, LongClass -> LONG, FloatClass -> FLOAT, - DoubleClass -> DOUBLE - ) - - private lazy val BOXED_UNIT : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.Void]) - private lazy val BOXED_BOOLEAN : ClassBType = classBTypeFromSymbol(BoxedBooleanClass) - private lazy val BOXED_BYTE : ClassBType = classBTypeFromSymbol(BoxedByteClass) - private lazy val BOXED_SHORT : ClassBType = classBTypeFromSymbol(BoxedShortClass) - private lazy val BOXED_CHAR : ClassBType = classBTypeFromSymbol(BoxedCharacterClass) - private lazy val BOXED_INT : ClassBType = classBTypeFromSymbol(BoxedIntClass) - private lazy val BOXED_LONG : ClassBType = classBTypeFromSymbol(BoxedLongClass) - private lazy val BOXED_FLOAT : ClassBType = classBTypeFromSymbol(BoxedFloatClass) - private lazy val BOXED_DOUBLE : ClassBType = classBTypeFromSymbol(BoxedDoubleClass) + DoubleClass -> DOUBLE) /** * Map from primitive types to their boxed class type. Useful when pushing class literals onto the * operand stack (ldc instruction taking a class literal), see genConstant. */ lazy val boxedClassOfPrimitive: Map[PrimitiveBType, ClassBType] = Map( - UNIT -> BOXED_UNIT, - BOOL -> BOXED_BOOLEAN, - BYTE -> BOXED_BYTE, - SHORT -> BOXED_SHORT, - CHAR -> BOXED_CHAR, - INT -> BOXED_INT, - LONG -> BOXED_LONG, - FLOAT -> BOXED_FLOAT, - DOUBLE -> BOXED_DOUBLE - ) + UNIT -> classBTypeFromSymbol(requiredClass[java.lang.Void]), + BOOL -> classBTypeFromSymbol(BoxedBooleanClass), + BYTE -> classBTypeFromSymbol(BoxedByteClass), + SHORT -> classBTypeFromSymbol(BoxedShortClass), + CHAR -> classBTypeFromSymbol(BoxedCharacterClass), + INT -> classBTypeFromSymbol(BoxedIntClass), + LONG -> classBTypeFromSymbol(BoxedLongClass), + FLOAT -> classBTypeFromSymbol(BoxedFloatClass), + DOUBLE -> classBTypeFromSymbol(BoxedDoubleClass)) lazy val boxedClasses: Set[ClassBType] = boxedClassOfPrimitive.values.toSet @@ -82,7 +70,7 @@ class CoreBTypes[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes: BTFS) { */ lazy val boxResultType: Map[Symbol, ClassBType] = { for ((valueClassSym, boxMethodSym) <- currentRun.runDefinitions.boxMethod) - yield boxMethodSym -> boxedClassOfPrimitive(primitiveTypeMap(valueClassSym)) + yield boxMethodSym -> boxedClassOfPrimitive(primitiveTypeToBType(valueClassSym)) } /** @@ -90,7 +78,7 @@ class CoreBTypes[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes: BTFS) { * For example, the method symbol for `Byte.unbox()`) is mapped to the PrimitiveBType BYTE. */ lazy val unboxResultType: Map[Symbol, PrimitiveBType] = { for ((valueClassSym, unboxMethodSym) <- currentRun.runDefinitions.unboxMethod) - yield unboxMethodSym -> primitiveTypeMap(valueClassSym) + yield unboxMethodSym -> primitiveTypeToBType(valueClassSym) } /* @@ -106,6 +94,7 @@ class CoreBTypes[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes: BTFS) { lazy val ObjectRef : ClassBType = classBTypeFromSymbol(ObjectClass) lazy val StringRef : ClassBType = classBTypeFromSymbol(StringClass) + lazy val PredefRef : ClassBType = classBTypeFromSymbol(PredefModule.moduleClass) lazy val jlStringBuilderRef : ClassBType = classBTypeFromSymbol(StringBuilderClass) lazy val jlThrowableRef : ClassBType = classBTypeFromSymbol(ThrowableClass) lazy val jlCloneableRef : ClassBType = classBTypeFromSymbol(JavaCloneableClass) // java/lang/Cloneable @@ -116,56 +105,135 @@ class CoreBTypes[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes: BTFS) { lazy val sbScalaBeanInfoRef : ClassBType = classBTypeFromSymbol(requiredClass[scala.beans.ScalaBeanInfo]) lazy val jliSerializedLambdaRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.invoke.SerializedLambda]) lazy val jliMethodHandlesRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.invoke.MethodHandles]) - lazy val jliMethodHandlesLookupRef : ClassBType = classBTypeFromSymbol(exitingPickler(rootMirror.getRequiredClass("java.lang.invoke.MethodHandles.Lookup"))) // didn't find a reliable non-stringly-typed way that works for inner classes in the backend + lazy val jliMethodHandlesLookupRef : ClassBType = classBTypeFromSymbol(exitingPickler(getRequiredClass("java.lang.invoke.MethodHandles.Lookup"))) // didn't find a reliable non-stringly-typed way that works for inner classes in the backend lazy val srLambdaDeserializerRef : ClassBType = classBTypeFromSymbol(requiredModule[scala.runtime.LambdaDeserializer.type].moduleClass) lazy val srBoxesRunTimeRef : ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.BoxesRunTime]) lazy val srBoxedUnitRef : ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.BoxedUnit]) - lazy val hashMethodSym: Symbol = getMember(ScalaRunTimeModule, nme.hash_) + private def methodNameAndType(cls: Symbol, name: Name, static: Boolean = false, filterOverload: Symbol => Boolean = _ => true): MethodNameAndType = { + val holder = if (static) cls.companionModule.moduleClass else cls + val method = holder.info.member(name).suchThat(filterOverload) + assert(!method.isOverloaded, method) + MethodNameAndType(name.toString, methodBTypeFromSymbol(method)) + } - // TODO @lry avoiding going through through missingHook for every line in the REPL: https://github.com/scala/scala/commit/8d962ed4ddd310cc784121c426a2e3f56a112540 - lazy val AndroidParcelableInterface : Symbol = getClassIfDefined("android.os.Parcelable") - lazy val AndroidCreatorClass : Symbol = getClassIfDefined("android.os.Parcelable$Creator") + private def srBoxesRuntimeMethods(getName: (String, String) => String): Map[BType, MethodNameAndType] = { + ScalaValueClassesNoUnit.map(primitive => { + val bType = primitiveTypeToBType(primitive) + val name = newTermName(getName(primitive.name.toString, boxedClass(primitive).name.toString)) + (bType, methodNameAndType(BoxesRunTimeClass, name)) + })(collection.breakOut) + } - lazy val BeanInfoAttr: Symbol = requiredClass[scala.beans.BeanInfo] + // Z -> MethodNameAndType(boxToBoolean,(Z)Ljava/lang/Boolean;) + lazy val srBoxesRuntimeBoxToMethods: Map[BType, MethodNameAndType] = srBoxesRuntimeMethods((primitive, boxed) => "boxTo" + boxed) - /* The Object => String overload. */ - lazy val String_valueOf: Symbol = { - getMember(StringModule, nme.valueOf) filter (sym => sym.info.paramTypes match { - case List(pt) => pt.typeSymbol == ObjectClass - case _ => false - }) + // Z -> MethodNameAndType(unboxToBoolean,(Ljava/lang/Object;)Z) + lazy val srBoxesRuntimeUnboxToMethods: Map[BType, MethodNameAndType] = srBoxesRuntimeMethods((primitive, boxed) => "unboxTo" + primitive) + + def singleParamOfClass(cls: Symbol) = (s: Symbol) => s.paramss match { + case List(List(param)) => param.info.typeSymbol == cls + case _ => false } - /** - * Methods in scala.runtime.BoxesRuntime - */ - lazy val asmBoxTo : Map[BType, MethodNameAndType] = Map( - BOOL -> MethodNameAndType("boxToBoolean", MethodBType(List(BOOL), BOXED_BOOLEAN)), - BYTE -> MethodNameAndType("boxToByte", MethodBType(List(BYTE), BOXED_BYTE)), - CHAR -> MethodNameAndType("boxToCharacter", MethodBType(List(CHAR), BOXED_CHAR)), - SHORT -> MethodNameAndType("boxToShort", MethodBType(List(SHORT), BOXED_SHORT)), - INT -> MethodNameAndType("boxToInteger", MethodBType(List(INT), BOXED_INT)), - LONG -> MethodNameAndType("boxToLong", MethodBType(List(LONG), BOXED_LONG)), - FLOAT -> MethodNameAndType("boxToFloat", MethodBType(List(FLOAT), BOXED_FLOAT)), - DOUBLE -> MethodNameAndType("boxToDouble", MethodBType(List(DOUBLE), BOXED_DOUBLE)) - ) - - lazy val asmUnboxTo: Map[BType, MethodNameAndType] = Map( - BOOL -> MethodNameAndType("unboxToBoolean", MethodBType(List(ObjectRef), BOOL)), - BYTE -> MethodNameAndType("unboxToByte", MethodBType(List(ObjectRef), BYTE)), - CHAR -> MethodNameAndType("unboxToChar", MethodBType(List(ObjectRef), CHAR)), - SHORT -> MethodNameAndType("unboxToShort", MethodBType(List(ObjectRef), SHORT)), - INT -> MethodNameAndType("unboxToInt", MethodBType(List(ObjectRef), INT)), - LONG -> MethodNameAndType("unboxToLong", MethodBType(List(ObjectRef), LONG)), - FLOAT -> MethodNameAndType("unboxToFloat", MethodBType(List(ObjectRef), FLOAT)), - DOUBLE -> MethodNameAndType("unboxToDouble", MethodBType(List(ObjectRef), DOUBLE)) - ) + // java/lang/Boolean -> MethodNameAndType(valueOf,(Z)Ljava/lang/Boolean;) + lazy val javaBoxMethods: Map[InternalName, MethodNameAndType] = { + ScalaValueClassesNoUnit.map(primitive => { + val boxed = boxedClass(primitive) + val method = methodNameAndType(boxed, newTermName("valueOf"), static = true, filterOverload = singleParamOfClass(primitive)) + (classBTypeFromSymbol(boxed).internalName, method) + })(collection.breakOut) + } + + // java/lang/Boolean -> MethodNameAndType(booleanValue,()Z) + lazy val javaUnboxMethods: Map[InternalName, MethodNameAndType] = { + ScalaValueClassesNoUnit.map(primitive => { + val boxed = boxedClass(primitive) + val name = primitive.name.toString.toLowerCase + "Value" + (classBTypeFromSymbol(boxed).internalName, methodNameAndType(boxed, newTermName(name))) + })(collection.breakOut) + } + + private def predefBoxingMethods(getName: (String, String) => String): Map[String, MethodBType] = { + ScalaValueClassesNoUnit.map(primitive => { + val boxed = boxedClass(primitive) + val name = getName(primitive.name.toString, boxed.name.toString) + (name, methodNameAndType(PredefModule.moduleClass, newTermName(name)).methodType) + })(collection.breakOut) + } + + // boolean2Boolean -> (Z)Ljava/lang/Boolean; + lazy val predefAutoBoxMethods: Map[String, MethodBType] = predefBoxingMethods((primitive, boxed) => primitive.toLowerCase + "2" + boxed) + + // Boolean2boolean -> (Ljava/lang/Boolean;)Z + lazy val predefAutoUnboxMethods: Map[String, MethodBType] = predefBoxingMethods((primitive, boxed) => boxed + "2" + primitive.toLowerCase) + + private def staticRefMethods(name: Name): Map[InternalName, MethodNameAndType] = { + allRefClasses.map(refClass => + (classBTypeFromSymbol(refClass).internalName, methodNameAndType(refClass, name, static = true)))(collection.breakOut) + } + + // scala/runtime/BooleanRef -> MethodNameAndType(create,(Z)Lscala/runtime/BooleanRef;) + lazy val srRefCreateMethods: Map[InternalName, MethodNameAndType] = staticRefMethods(nme.create) + + // scala/runtime/BooleanRef -> MethodNameAndType(zero,()Lscala/runtime/BooleanRef;) + lazy val srRefZeroMethods: Map[InternalName, MethodNameAndType] = staticRefMethods(nme.zero) + + // java/lang/Boolean -> MethodNameAndType(,(Z)V) + lazy val primitiveBoxConstructors: Map[InternalName, MethodNameAndType] = { + ScalaValueClassesNoUnit.map(primitive => { + val boxed = boxedClass(primitive) + (classBTypeFromSymbol(boxed).internalName, methodNameAndType(boxed, nme.CONSTRUCTOR, filterOverload = singleParamOfClass(primitive))) + })(collection.breakOut) + } + + private def nonOverloadedConstructors(classes: Iterable[Symbol]): Map[InternalName, MethodNameAndType] = { + classes.map(cls => (classBTypeFromSymbol(cls).internalName, methodNameAndType(cls, nme.CONSTRUCTOR)))(collection.breakOut) + } + + // scala/runtime/BooleanRef -> MethodNameAndType(,(Z)V) + lazy val srRefConstructors: Map[InternalName, MethodNameAndType] = nonOverloadedConstructors(allRefClasses) + + private def specializedSubclasses(cls: Symbol): List[Symbol] = { + exitingSpecialize(cls.info) // the `transformInfo` method of specialization adds specialized subclasses to the `specializedClass` map + specializeTypes.specializedClass.collect({ + case ((`cls`, _), specCls) => specCls + }).toList + } + + // scala/Tuple3 -> MethodNameAndType(,(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V) + // scala/Tuple2$mcZC$sp -> MethodNameAndType(,(ZC)V) + lazy val tupleClassConstructors: Map[InternalName, MethodNameAndType] = { + val tupleClassSymbols = TupleClass.seq ++ specializedSubclasses(TupleClass(1)) ++ specializedSubclasses(TupleClass(2)) + nonOverloadedConstructors(tupleClassSymbols) + } + + // enumeration of specialized classes is temporary, while we still use the java-defined JFunctionN. + // once we switch to ordinary FunctionN, we can use specializedSubclasses just like for tuples. + private def functionClasses(base: String): Set[Symbol] = { + def primitives = Iterator("B", "S", "I", "J", "C", "F", "D", "Z", "V") + def ijfd = Iterator("I", "J", "F", "D") + def ijfdzv = Iterator("I", "J", "F", "D", "Z", "V") + def ijd = Iterator("I", "J", "D") + val classNames = Set.empty[String] ++ { + (0 to 22).map(base + _) + } ++ { + primitives.map(base + "0$mc" + _ + "$sp") // Function0 + } ++ { + // return type specializations appear first in the name string (alphabetical sorting) + for (r <- ijfdzv; a <- ijfd) yield base + "1$mc" + r + a + "$sp" // Function1 + } ++ { + for (r <- ijfdzv; a <- ijd; b <- ijd) yield base + "2$mc" + r + a + b + "$sp" // Function2 + } + classNames map getRequiredClass + } + + lazy val srJFunctionRefs: Set[InternalName] = functionClasses("scala.runtime.java8.JFunction").map(classBTypeFromSymbol(_).internalName) lazy val typeOfArrayOp: Map[Int, BType] = { import scalaPrimitives._ Map( - (List(ZARRAY_LENGTH, ZARRAY_GET, ZARRAY_SET) map (_ -> BOOL)) ++ + (List(ZARRAY_LENGTH, ZARRAY_GET, ZARRAY_SET) map (_ -> BOOL)) ++ (List(BARRAY_LENGTH, BARRAY_GET, BARRAY_SET) map (_ -> BYTE)) ++ (List(SARRAY_LENGTH, SARRAY_GET, SARRAY_SET) map (_ -> SHORT)) ++ (List(CARRAY_LENGTH, CARRAY_GET, CARRAY_SET) map (_ -> CHAR)) ++ @@ -176,6 +244,22 @@ class CoreBTypes[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes: BTFS) { (List(OARRAY_LENGTH, OARRAY_GET, OARRAY_SET) map (_ -> ObjectRef)) : _* ) } + + lazy val hashMethodSym: Symbol = getMember(ScalaRunTimeModule, nme.hash_) + + // TODO @lry avoiding going through through missingHook for every line in the REPL: https://github.com/scala/scala/commit/8d962ed4ddd310cc784121c426a2e3f56a112540 + lazy val AndroidParcelableInterface : Symbol = getClassIfDefined("android.os.Parcelable") + lazy val AndroidCreatorClass : Symbol = getClassIfDefined("android.os.Parcelable$Creator") + + lazy val BeanInfoAttr: Symbol = requiredClass[scala.beans.BeanInfo] + + /* The Object => String overload. */ + lazy val String_valueOf: Symbol = { + getMember(StringModule, nme.valueOf) filter (sym => sym.info.paramTypes match { + case List(pt) => pt.typeSymbol == ObjectClass + case _ => false + }) + } } /** @@ -191,10 +275,14 @@ trait CoreBTypesProxyGlobalIndependent[BTS <: BTypes] { import bTypes._ def boxedClasses: Set[ClassBType] + def boxedClassOfPrimitive: Map[PrimitiveBType, ClassBType] - def ObjectRef : ClassBType def srNothingRef : ClassBType def srNullRef : ClassBType + + def ObjectRef : ClassBType + def StringRef : ClassBType + def PredefRef : ClassBType def jlCloneableRef : ClassBType def jiSerializableRef : ClassBType def juHashMapRef : ClassBType @@ -206,8 +294,23 @@ trait CoreBTypesProxyGlobalIndependent[BTS <: BTypes] { def srBoxesRunTimeRef : ClassBType def srBoxedUnitRef : ClassBType - def asmBoxTo : Map[BType, MethodNameAndType] - def asmUnboxTo: Map[BType, MethodNameAndType] + def srBoxesRuntimeBoxToMethods : Map[BType, MethodNameAndType] + def srBoxesRuntimeUnboxToMethods : Map[BType, MethodNameAndType] + + def javaBoxMethods : Map[InternalName, MethodNameAndType] + def javaUnboxMethods : Map[InternalName, MethodNameAndType] + + def predefAutoBoxMethods : Map[String, MethodBType] + def predefAutoUnboxMethods : Map[String, MethodBType] + + def srRefCreateMethods : Map[InternalName, MethodNameAndType] + def srRefZeroMethods : Map[InternalName, MethodNameAndType] + + def primitiveBoxConstructors : Map[InternalName, MethodNameAndType] + def srRefConstructors : Map[InternalName, MethodNameAndType] + def tupleClassConstructors : Map[InternalName, MethodNameAndType] + + def srJFunctionRefs: Set[InternalName] } /** @@ -222,20 +325,21 @@ final class CoreBTypesProxy[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes: _coreBTypes = coreBTypes.asInstanceOf[CoreBTypes[bTypes.type]] } - def primitiveTypeMap: Map[Symbol, PrimitiveBType] = _coreBTypes.primitiveTypeMap + def primitiveTypeToBType: Map[Symbol, PrimitiveBType] = _coreBTypes.primitiveTypeToBType def boxedClasses: Set[ClassBType] = _coreBTypes.boxedClasses - def boxedClassOfPrimitive: Map[PrimitiveBType, ClassBType] = _coreBTypes.boxedClassOfPrimitive def boxResultType: Map[Symbol, ClassBType] = _coreBTypes.boxResultType def unboxResultType: Map[Symbol, PrimitiveBType] = _coreBTypes.unboxResultType + def srNothingRef : ClassBType = _coreBTypes.srNothingRef + def srNullRef : ClassBType = _coreBTypes.srNullRef + def ObjectRef : ClassBType = _coreBTypes.ObjectRef def StringRef : ClassBType = _coreBTypes.StringRef + def PredefRef : ClassBType = _coreBTypes.PredefRef def jlStringBuilderRef : ClassBType = _coreBTypes.jlStringBuilderRef - def srNothingRef : ClassBType = _coreBTypes.srNothingRef - def srNullRef : ClassBType = _coreBTypes.srNullRef def jlThrowableRef : ClassBType = _coreBTypes.jlThrowableRef def jlCloneableRef : ClassBType = _coreBTypes.jlCloneableRef def jiSerializableRef : ClassBType = _coreBTypes.jiSerializableRef @@ -250,6 +354,28 @@ final class CoreBTypesProxy[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes: def srBoxesRunTimeRef : ClassBType = _coreBTypes.srBoxesRunTimeRef def srBoxedUnitRef : ClassBType = _coreBTypes.srBoxedUnitRef + def srBoxesRuntimeBoxToMethods : Map[BType, MethodNameAndType] = _coreBTypes.srBoxesRuntimeBoxToMethods + def srBoxesRuntimeUnboxToMethods : Map[BType, MethodNameAndType] = _coreBTypes.srBoxesRuntimeUnboxToMethods + + def javaBoxMethods : Map[InternalName, MethodNameAndType] = _coreBTypes.javaBoxMethods + def javaUnboxMethods : Map[InternalName, MethodNameAndType] = _coreBTypes.javaUnboxMethods + + def predefAutoBoxMethods : Map[String, MethodBType] = _coreBTypes.predefAutoBoxMethods + def predefAutoUnboxMethods : Map[String, MethodBType] = _coreBTypes.predefAutoUnboxMethods + + def srRefCreateMethods : Map[InternalName, MethodNameAndType] = _coreBTypes.srRefCreateMethods + def srRefZeroMethods : Map[InternalName, MethodNameAndType] = _coreBTypes.srRefZeroMethods + + def primitiveBoxConstructors : Map[InternalName, MethodNameAndType] = _coreBTypes.primitiveBoxConstructors + def srRefConstructors : Map[InternalName, MethodNameAndType] = _coreBTypes.srRefConstructors + def tupleClassConstructors : Map[InternalName, MethodNameAndType] = _coreBTypes.tupleClassConstructors + + def srJFunctionRefs: Set[InternalName] = _coreBTypes.srJFunctionRefs + + def typeOfArrayOp: Map[Int, BType] = _coreBTypes.typeOfArrayOp + + // Some symbols. These references should probably be moved to Definitions. + def hashMethodSym: Symbol = _coreBTypes.hashMethodSym def AndroidParcelableInterface : Symbol = _coreBTypes.AndroidParcelableInterface @@ -258,9 +384,4 @@ final class CoreBTypesProxy[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes: def BeanInfoAttr: Symbol = _coreBTypes.BeanInfoAttr def String_valueOf: Symbol = _coreBTypes.String_valueOf - - def asmBoxTo : Map[BType, MethodNameAndType] = _coreBTypes.asmBoxTo - def asmUnboxTo: Map[BType, MethodNameAndType] = _coreBTypes.asmUnboxTo - - def typeOfArrayOp: Map[Int, BType] = _coreBTypes.typeOfArrayOp } diff --git a/src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala b/src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala index baf82032f7..e8630c65d9 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala @@ -3,9 +3,11 @@ package backend.jvm package analysis import scala.annotation.switch -import scala.tools.asm.{Opcodes, Handle, Type, Label} +import scala.tools.asm.{Handle, Type, Label} +import scala.tools.asm.Opcodes._ import scala.tools.asm.tree._ import scala.tools.asm.tree.analysis.{Frame, BasicInterpreter, Analyzer, Value} +import GenBCode._ import scala.tools.nsc.backend.jvm.BTypes._ import scala.tools.nsc.backend.jvm.opt.BytecodeUtils import scala.tools.nsc.backend.jvm.opt.BytecodeUtils._ @@ -24,6 +26,7 @@ import scala.collection.convert.decorateAsScala._ */ class BackendUtils[BT <: BTypes](val btypes: BT) { import btypes._ + import btypes.coreBTypes._ import callGraph.ClosureInstantiation /** @@ -73,8 +76,6 @@ class BackendUtils[BT <: BTypes](val btypes: BT) { */ def addLambdaDeserialize(classNode: ClassNode): Unit = { val cw = classNode - import scala.tools.asm.Opcodes._ - import btypes.coreBTypes._ // Make sure to reference the ClassBTypes of all types that are used in the code generated // here (e.g. java/util/Map) are initialized. Initializing a ClassBType adds it to the @@ -145,14 +146,137 @@ class BackendUtils[BT <: BTypes](val btypes: BT) { (result, map, hasSerializableClosureInstantiation) } - def getBoxedUnit: FieldInsnNode = new FieldInsnNode(Opcodes.GETSTATIC, coreBTypes.srBoxedUnitRef.internalName, "UNIT", coreBTypes.srBoxedUnitRef.descriptor) + def getBoxedUnit: FieldInsnNode = new FieldInsnNode(GETSTATIC, srBoxedUnitRef.internalName, "UNIT", srBoxedUnitRef.descriptor) private val anonfunAdaptedName = """.*\$anonfun\$\d+\$adapted""".r def hasAdaptedImplMethod(closureInit: ClosureInstantiation): Boolean = { - BytecodeUtils.isrJFunctionType(Type.getReturnType(closureInit.lambdaMetaFactoryCall.indy.desc).getInternalName) && + isrJFunctionType(Type.getReturnType(closureInit.lambdaMetaFactoryCall.indy.desc).getInternalName) && anonfunAdaptedName.pattern.matcher(closureInit.lambdaMetaFactoryCall.implMethod.getName).matches } + private def primitiveAsmTypeToBType(primitiveType: Type): PrimitiveBType = (primitiveType.getSort: @switch) match { + case Type.BOOLEAN => BOOL + case Type.BYTE => BYTE + case Type.CHAR => CHAR + case Type.SHORT => SHORT + case Type.INT => INT + case Type.LONG => LONG + case Type.FLOAT => FLOAT + case Type.DOUBLE => DOUBLE + case _ => null + } + + def isScalaBox(insn: MethodInsnNode): Boolean = { + insn.owner == srBoxesRunTimeRef.internalName && { + val args = Type.getArgumentTypes(insn.desc) + args.length == 1 && (srBoxesRuntimeBoxToMethods.get(primitiveAsmTypeToBType(args(0))) match { + case Some(MethodNameAndType(name, tp)) => name == insn.name && tp.descriptor == insn.desc + case _ => false + }) + } + } + + def getScalaBox(primitiveType: Type): MethodInsnNode = { + val bType = primitiveAsmTypeToBType(primitiveType) + val MethodNameAndType(name, methodBType) = srBoxesRuntimeBoxToMethods(bType) + new MethodInsnNode(INVOKESTATIC, srBoxesRunTimeRef.internalName, name, methodBType.descriptor, /*itf =*/ false) + } + + def isScalaUnbox(insn: MethodInsnNode): Boolean = { + insn.owner == srBoxesRunTimeRef.internalName && (srBoxesRuntimeUnboxToMethods.get(primitiveAsmTypeToBType(Type.getReturnType(insn.desc))) match { + case Some(MethodNameAndType(name, tp)) => name == insn.name && tp.descriptor == insn.desc + case _ => false + }) + } + + def getScalaUnbox(primitiveType: Type): MethodInsnNode = { + val bType = primitiveAsmTypeToBType(primitiveType) + val MethodNameAndType(name, methodBType) = srBoxesRuntimeUnboxToMethods(bType) + new MethodInsnNode(INVOKESTATIC, srBoxesRunTimeRef.internalName, name, methodBType.descriptor, /*itf =*/ false) + } + + private def calleeInMap(insn: MethodInsnNode, map: Map[InternalName, MethodNameAndType]): Boolean = map.get(insn.owner) match { + case Some(MethodNameAndType(name, tp)) => insn.name == name && insn.desc == tp.descriptor + case _ => false + } + + def isJavaBox(insn: MethodInsnNode): Boolean = calleeInMap(insn, javaBoxMethods) + def isJavaUnbox(insn: MethodInsnNode): Boolean = calleeInMap(insn, javaUnboxMethods) + + def isPredefAutoBox(insn: MethodInsnNode): Boolean = { + insn.owner == PredefRef.internalName && (predefAutoBoxMethods.get(insn.name) match { + case Some(tp) => insn.desc == tp.descriptor + case _ => false + }) + } + + def isPredefAutoUnbox(insn: MethodInsnNode): Boolean = { + insn.owner == PredefRef.internalName && (predefAutoUnboxMethods.get(insn.name) match { + case Some(tp) => insn.desc == tp.descriptor + case _ => false + }) + } + + def isRefCreate(insn: MethodInsnNode): Boolean = calleeInMap(insn, srRefCreateMethods) + def isRefZero(insn: MethodInsnNode): Boolean = calleeInMap(insn, srRefZeroMethods) + + def runtimeRefClassBoxedType(refClass: InternalName): Type = Type.getArgumentTypes(srRefCreateMethods(refClass).methodType.descriptor)(0) + + def isSideEffectFreeCall(insn: MethodInsnNode): Boolean = { + isScalaBox(insn) || isScalaUnbox(insn) || + isJavaBox(insn) || // not java unbox, it may NPE + isSideEffectFreeConstructorCall(insn) + } + + def isNonNullMethodInvocation(mi: MethodInsnNode): Boolean = { + isJavaBox(mi) || isScalaBox(mi) || isPredefAutoBox(mi) || isRefCreate(mi) || isRefZero(mi) + } + + def isModuleLoad(insn: AbstractInsnNode, moduleName: InternalName): Boolean = insn match { + case fi: FieldInsnNode => fi.getOpcode == GETSTATIC && fi.owner == moduleName && fi.name == "MODULE$" && fi.desc == ("L" + moduleName + ";") + case _ => false + } + + def isPredefLoad(insn: AbstractInsnNode) = isModuleLoad(insn, PredefRef.internalName) + + def isPrimitiveBoxConstructor(insn: MethodInsnNode): Boolean = calleeInMap(insn, primitiveBoxConstructors) + def isRuntimeRefConstructor(insn: MethodInsnNode): Boolean = calleeInMap(insn, srRefConstructors) + def isTupleConstructor(insn: MethodInsnNode): Boolean = calleeInMap(insn, tupleClassConstructors) + + // unused objects created by these constructors are eliminated by pushPop + private lazy val sideEffectFreeConstructors: Set[(String, String)] = { + val ownerDesc = (p: (InternalName, MethodNameAndType)) => (p._1, p._2.methodType.descriptor) + primitiveBoxConstructors.map(ownerDesc).toSet ++ + srRefConstructors.map(ownerDesc) ++ + tupleClassConstructors.map(ownerDesc) ++ Set( + (ObjectRef.internalName, MethodBType(Nil, UNIT).descriptor), + (StringRef.internalName, MethodBType(Nil, UNIT).descriptor), + (StringRef.internalName, MethodBType(List(StringRef), UNIT).descriptor), + (StringRef.internalName, MethodBType(List(ArrayBType(CHAR)), UNIT).descriptor)) + } + + def isSideEffectFreeConstructorCall(insn: MethodInsnNode): Boolean = { + insn.name == INSTANCE_CONSTRUCTOR_NAME && sideEffectFreeConstructors((insn.owner, insn.desc)) + } + + private lazy val classesOfSideEffectFreeConstructors = sideEffectFreeConstructors.map(_._1) + + def isNewForSideEffectFreeConstructor(insn: AbstractInsnNode) = { + insn.getOpcode == NEW && { + val ti = insn.asInstanceOf[TypeInsnNode] + classesOfSideEffectFreeConstructors.contains(ti.desc) + } + } + + def isBoxedUnit(insn: AbstractInsnNode) = { + insn.getOpcode == GETSTATIC && { + val fi = insn.asInstanceOf[FieldInsnNode] + fi.owner == srBoxedUnitRef.internalName && fi.name == "UNIT" && fi.desc == srBoxedUnitRef.descriptor + } + } + + def isrJFunctionType(internalName: InternalName): Boolean = srJFunctionRefs(internalName) + /** * Visit the class node and collect all referenced nested classes. */ @@ -287,15 +411,13 @@ class BackendUtils[BT <: BTypes](val btypes: BT) { * Analyzer: its implementation also skips over unreachable code in the same way. */ def computeMaxLocalsMaxStack(method: MethodNode): Unit = { - import Opcodes._ - if (isAbstractMethod(method) || isNativeMethod(method)) { method.maxLocals = 0 method.maxStack = 0 } else if (!maxLocalsMaxStackComputed(method)) { val size = method.instructions.size - var maxLocals = (Type.getArgumentsAndReturnSizes(method.desc) >> 2) - (if (isStaticMethod(method)) 1 else 0) + var maxLocals = parametersSize(method) var maxStack = 0 // queue of instruction indices where analysis should start diff --git a/src/compiler/scala/tools/nsc/backend/jvm/analysis/InstructionStackEffect.scala b/src/compiler/scala/tools/nsc/backend/jvm/analysis/InstructionStackEffect.scala index 2181f00850..dd19ad594f 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/analysis/InstructionStackEffect.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/analysis/InstructionStackEffect.scala @@ -335,5 +335,4 @@ object InstructionStackEffect { IFNONNULL => t(1, 0) } } - } diff --git a/src/compiler/scala/tools/nsc/backend/jvm/analysis/NullnessAnalyzer.scala b/src/compiler/scala/tools/nsc/backend/jvm/analysis/NullnessAnalyzer.scala index 1078d0085e..30e73f8ac2 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/analysis/NullnessAnalyzer.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/analysis/NullnessAnalyzer.scala @@ -63,7 +63,7 @@ object NullnessValue { def unknown(insn: AbstractInsnNode) = if (BytecodeUtils.instructionResultSize(insn) == 2) UnknownValue2 else UnknownValue1 } -final class NullnessInterpreter extends Interpreter[NullnessValue](Opcodes.ASM5) { +final class NullnessInterpreter(bTypes: BTypes) extends Interpreter[NullnessValue](Opcodes.ASM5) { def newValue(tp: Type): NullnessValue = { // ASM loves giving semantics to null. The behavior here is the same as in SourceInterpreter, // which is provided by the framework. @@ -114,7 +114,7 @@ final class NullnessInterpreter extends Interpreter[NullnessValue](Opcodes.ASM5) def ternaryOperation(insn: AbstractInsnNode, value1: NullnessValue, value2: NullnessValue, value3: NullnessValue): NullnessValue = UnknownValue1 def naryOperation(insn: AbstractInsnNode, values: util.List[_ <: NullnessValue]): NullnessValue = insn match { - case mi: MethodInsnNode if isNonNullMethodInvocation(mi) => + case mi: MethodInsnNode if bTypes.backendUtils.isNonNullMethodInvocation(mi) => NotNullValue case _ => @@ -197,7 +197,7 @@ class NullnessFrame(nLocals: Int, nStack: Int) extends AliasingFrame[NullnessVal * This class is required to override the `newFrame` methods, which makes makes sure the analyzer * uses NullnessFrames. */ -class NullnessAnalyzer extends Analyzer[NullnessValue](new NullnessInterpreter) { +class NullnessAnalyzer(bTypes: BTypes) extends Analyzer[NullnessValue](new NullnessInterpreter(bTypes)) { override def newFrame(nLocals: Int, nStack: Int): NullnessFrame = new NullnessFrame(nLocals, nStack) override def newFrame(src: Frame[_ <: NullnessValue]): NullnessFrame = new NullnessFrame(src) } diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala index f83167eabf..ff36f36589 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala @@ -348,291 +348,6 @@ object BytecodeUtils { } } - def isSideEffectFreeCall(insn: MethodInsnNode): Boolean = { - isScalaBox(insn) || isScalaUnbox(insn) || - isJavaBox(insn) || // not java unbox, it may NPE - isSideEffectFreeConstructorCall(insn) - } - - def isNonNullMethodInvocation(mi: MethodInsnNode): Boolean = { - isJavaBox(mi) || isScalaBox(mi) || isPredefAutoBox(mi) || isRefCreate(mi) || isRefZero(mi) - } - - private val srBoxesRunTimeName = "scala/runtime/BoxesRunTime" - - private val boxToMethods = Map( - Type.BOOLEAN -> ("boxToBoolean", "(Z)Ljava/lang/Boolean;"), - Type.BYTE -> ("boxToByte", "(B)Ljava/lang/Byte;"), - Type.CHAR -> ("boxToCharacter", "(C)Ljava/lang/Character;"), - Type.SHORT -> ("boxToShort", "(S)Ljava/lang/Short;"), - Type.INT -> ("boxToInteger", "(I)Ljava/lang/Integer;"), - Type.LONG -> ("boxToLong", "(J)Ljava/lang/Long;"), - Type.FLOAT -> ("boxToFloat", "(F)Ljava/lang/Float;"), - Type.DOUBLE -> ("boxToDouble", "(D)Ljava/lang/Double;")) - - def isScalaBox(insn: MethodInsnNode): Boolean = { - insn.owner == srBoxesRunTimeName && { - val args = Type.getArgumentTypes(insn.desc) - args.length == 1 && (boxToMethods.get(args(0).getSort) match { - case Some((name, desc)) => name == insn.name && desc == insn.desc - case _ => false - }) - } - } - - def getScalaBox(primitiveType: Type): MethodInsnNode = { - val (method, desc) = boxToMethods(primitiveType.getSort) - new MethodInsnNode(INVOKESTATIC, srBoxesRunTimeName, method, desc, /*itf =*/ false) - } - - private val unboxToMethods = Map( - Type.BOOLEAN -> ("unboxToBoolean", "(Ljava/lang/Object;)Z"), - Type.BYTE -> ("unboxToByte", "(Ljava/lang/Object;)B"), - Type.CHAR -> ("unboxToChar", "(Ljava/lang/Object;)C"), - Type.SHORT -> ("unboxToShort", "(Ljava/lang/Object;)S"), - Type.INT -> ("unboxToInt", "(Ljava/lang/Object;)I"), - Type.LONG -> ("unboxToLong", "(Ljava/lang/Object;)J"), - Type.FLOAT -> ("unboxToFloat", "(Ljava/lang/Object;)F"), - Type.DOUBLE -> ("unboxToDouble", "(Ljava/lang/Object;)D")) - - def isScalaUnbox(insn: MethodInsnNode): Boolean = { - insn.owner == srBoxesRunTimeName && (unboxToMethods.get(Type.getReturnType(insn.desc).getSort) match { - case Some((name, desc)) => name == insn.name && desc == insn.desc - case _ => false - }) - } - - def getScalaUnbox(primitiveType: Type): MethodInsnNode = { - val (method, desc) = unboxToMethods(primitiveType.getSort) - new MethodInsnNode(INVOKESTATIC, srBoxesRunTimeName, method, desc, /*itf =*/ false) - } - - def isJavaBox(insn: MethodInsnNode): Boolean = { - insn.name == "valueOf" && { - val args = Type.getArgumentTypes(insn.desc) - args.length == 1 && ((args(0).getSort: @switch) match { - case Type.BOOLEAN => insn.owner == "java/lang/Boolean" && insn.desc == "(Z)Ljava/lang/Boolean;" - case Type.BYTE => insn.owner == "java/lang/Byte" && insn.desc == "(B)Ljava/lang/Byte;" - case Type.CHAR => insn.owner == "java/lang/Character" && insn.desc == "(C)Ljava/lang/Character;" - case Type.SHORT => insn.owner == "java/lang/Short" && insn.desc == "(S)Ljava/lang/Short;" - case Type.INT => insn.owner == "java/lang/Integer" && insn.desc == "(I)Ljava/lang/Integer;" - case Type.LONG => insn.owner == "java/lang/Long" && insn.desc == "(J)Ljava/lang/Long;" - case Type.FLOAT => insn.owner == "java/lang/Float" && insn.desc == "(F)Ljava/lang/Float;" - case Type.DOUBLE => insn.owner == "java/lang/Double" && insn.desc == "(D)Ljava/lang/Double;" - case _ => false - }) - } - } - - def isJavaUnbox(insn: MethodInsnNode): Boolean = { - insn.desc.startsWith("()") && { - (Type.getReturnType(insn.desc).getSort: @switch) match { - case Type.BOOLEAN => insn.owner == "java/lang/Boolean" && insn.name == "booleanValue" - case Type.BYTE => insn.owner == "java/lang/Byte" && insn.name == "byteValue" - case Type.CHAR => insn.owner == "java/lang/Character" && insn.name == "charValue" - case Type.SHORT => insn.owner == "java/lang/Short" && insn.name == "shortValue" - case Type.INT => insn.owner == "java/lang/Integer" && insn.name == "intValue" - case Type.LONG => insn.owner == "java/lang/Long" && insn.name == "longValue" - case Type.FLOAT => insn.owner == "java/lang/Float" && insn.name == "floatValue" - case Type.DOUBLE => insn.owner == "java/lang/Double" && insn.name == "doubleValue" - case _ => false - } - } - } - - def isPredefAutoBox(insn: MethodInsnNode): Boolean = { - insn.owner == "scala/Predef$" && { - val args = Type.getArgumentTypes(insn.desc) - args.length == 1 && ((args(0).getSort: @switch) match { - case Type.BOOLEAN => insn.name == "boolean2Boolean" && insn.desc == "(Z)Ljava/lang/Boolean;" - case Type.BYTE => insn.name == "byte2Byte" && insn.desc == "(B)Ljava/lang/Byte;" - case Type.CHAR => insn.name == "char2Character" && insn.desc == "(C)Ljava/lang/Character;" - case Type.SHORT => insn.name == "short2Short" && insn.desc == "(S)Ljava/lang/Short;" - case Type.INT => insn.name == "int2Integer" && insn.desc == "(I)Ljava/lang/Integer;" - case Type.LONG => insn.name == "long2Long" && insn.desc == "(J)Ljava/lang/Long;" - case Type.FLOAT => insn.name == "float2Float" && insn.desc == "(F)Ljava/lang/Float;" - case Type.DOUBLE => insn.name == "double2Double" && insn.desc == "(D)Ljava/lang/Double;" - case _ => false - }) - } - } - - def isPredefAutoUnbox(insn: MethodInsnNode): Boolean = { - insn.owner == "scala/Predef$" && { - (Type.getReturnType(insn.desc).getSort: @switch) match { - case Type.BOOLEAN => insn.name == "Boolean2boolean" && insn.desc == "(Ljava/lang/Boolean;)Z" - case Type.BYTE => insn.name == "Byte2byte" && insn.desc == "(Ljava/lang/Byte;)B" - case Type.CHAR => insn.name == "Character2char" && insn.desc == "(Ljava/lang/Character;)C" - case Type.SHORT => insn.name == "Short2short" && insn.desc == "(Ljava/lang/Short;)S" - case Type.INT => insn.name == "Integer2int" && insn.desc == "(Ljava/lang/Integer;)I" - case Type.LONG => insn.name == "Long2long" && insn.desc == "(Ljava/lang/Long;)J" - case Type.FLOAT => insn.name == "Float2float" && insn.desc == "(Ljava/lang/Float;)F" - case Type.DOUBLE => insn.name == "Double2double" && insn.desc == "(Ljava/lang/Double;)D" - case _ => false - } - } - } - - def isRefCreate(insn: MethodInsnNode): Boolean = { - insn.name == "create" && { - val args = Type.getArgumentTypes(insn.desc) - args.length == 1 && ((args(0).getSort: @switch) match { - case Type.BOOLEAN => insn.owner == "scala/runtime/BooleanRef" && insn.desc == "(Z)Lscala/runtime/BooleanRef;" || insn.owner == "scala/runtime/VolatileBooleanRef" && insn.desc == "(Z)Lscala/runtime/VolatileBooleanRef;" - case Type.BYTE => insn.owner == "scala/runtime/ByteRef" && insn.desc == "(B)Lscala/runtime/ByteRef;" || insn.owner == "scala/runtime/VolatileByteRef" && insn.desc == "(B)Lscala/runtime/VolatileByteRef;" - case Type.CHAR => insn.owner == "scala/runtime/CharRef" && insn.desc == "(C)Lscala/runtime/CharRef;" || insn.owner == "scala/runtime/VolatileCharRef" && insn.desc == "(C)Lscala/runtime/VolatileCharRef;" - case Type.SHORT => insn.owner == "scala/runtime/ShortRef" && insn.desc == "(S)Lscala/runtime/ShortRef;" || insn.owner == "scala/runtime/VolatileShortRef" && insn.desc == "(S)Lscala/runtime/VolatileShortRef;" - case Type.INT => insn.owner == "scala/runtime/IntRef" && insn.desc == "(I)Lscala/runtime/IntRef;" || insn.owner == "scala/runtime/VolatileIntRef" && insn.desc == "(I)Lscala/runtime/VolatileIntRef;" - case Type.LONG => insn.owner == "scala/runtime/LongRef" && insn.desc == "(J)Lscala/runtime/LongRef;" || insn.owner == "scala/runtime/VolatileLongRef" && insn.desc == "(J)Lscala/runtime/VolatileLongRef;" - case Type.FLOAT => insn.owner == "scala/runtime/FloatRef" && insn.desc == "(F)Lscala/runtime/FloatRef;" || insn.owner == "scala/runtime/VolatileFloatRef" && insn.desc == "(F)Lscala/runtime/VolatileFloatRef;" - case Type.DOUBLE => insn.owner == "scala/runtime/DoubleRef" && insn.desc == "(D)Lscala/runtime/DoubleRef;" || insn.owner == "scala/runtime/VolatileDoubleRef" && insn.desc == "(D)Lscala/runtime/VolatileDoubleRef;" - case Type.OBJECT => insn.owner == "scala/runtime/ObjectRef" && insn.desc == "(Ljava/lang/Object;)Lscala/runtime/ObjectRef;" || insn.owner == "scala/runtime/VolatileObjectRef" && insn.desc == "(Ljava/lang/Object;)Lscala/runtime/VolatileObjectRef;" - case _ => false - }) - } - } - - private val jlObjectType = Type.getType("Ljava/lang/Object;") - - private val runtimeRefClassesAndTypes = Map( - ("scala/runtime/BooleanRef", Type.BOOLEAN_TYPE), - ("scala/runtime/ByteRef", Type.BYTE_TYPE), - ("scala/runtime/CharRef", Type.CHAR_TYPE), - ("scala/runtime/ShortRef", Type.SHORT_TYPE), - ("scala/runtime/IntRef", Type.INT_TYPE), - ("scala/runtime/LongRef", Type.LONG_TYPE), - ("scala/runtime/FloatRef", Type.FLOAT_TYPE), - ("scala/runtime/DoubleRef", Type.DOUBLE_TYPE), - ("scala/runtime/ObjectRef", jlObjectType), - ("scala/runtime/VolatileBooleanRef", Type.BOOLEAN_TYPE), - ("scala/runtime/VolatileByteRef", Type.BYTE_TYPE), - ("scala/runtime/VolatileCharRef", Type.CHAR_TYPE), - ("scala/runtime/VolatileShortRef", Type.SHORT_TYPE), - ("scala/runtime/VolatileIntRef", Type.INT_TYPE), - ("scala/runtime/VolatileLongRef", Type.LONG_TYPE), - ("scala/runtime/VolatileFloatRef", Type.FLOAT_TYPE), - ("scala/runtime/VolatileDoubleRef", Type.DOUBLE_TYPE), - ("scala/runtime/VolatileObjectRef", jlObjectType)) - - def isRefZero(insn: MethodInsnNode): Boolean = { - insn.name == "zero" && runtimeRefClassesAndTypes.contains(insn.owner) && insn.desc == "()L" + insn.owner + ";" - } - - def runtimeRefClassBoxedType(refClass: InternalName): Type = runtimeRefClassesAndTypes(refClass) - - def isModuleLoad(insn: AbstractInsnNode, moduleName: InternalName): Boolean = insn match { - case fi: FieldInsnNode => fi.getOpcode == GETSTATIC && fi.owner == moduleName && fi.name == "MODULE$" && fi.desc == ("L" + moduleName + ";") - case _ => false - } - - def isPredefLoad(insn: AbstractInsnNode) = isModuleLoad(insn, "scala/Predef$") - - private val primitiveBoxConstructors = Set( - "java/lang/Boolean(Z)V", - "java/lang/Byte(B)V", - "java/lang/Character(C)V", - "java/lang/Short(S)V", - "java/lang/Integer(I)V", - "java/lang/Long(J)V", - "java/lang/Float(F)V", - "java/lang/Double(D)V") - - def isPrimitiveBoxConstructor(insn: MethodInsnNode): Boolean = { - insn.name == INSTANCE_CONSTRUCTOR_NAME && primitiveBoxConstructors(insn.owner + insn.desc) - } - - private val runtimeRefConstructors = Set( - "scala/runtime/ObjectRef(Ljava/lang/Object;)V", - "scala/runtime/BooleanRef(Z)V", - "scala/runtime/ByteRef(B)V", - "scala/runtime/CharRef(C)V", - "scala/runtime/ShortRef(S)V", - "scala/runtime/IntRef(I)V", - "scala/runtime/LongRef(J)V", - "scala/runtime/FloatRef(F)V", - "scala/runtime/DoubleRef(D)V", - - "scala/runtime/VolatileObjectRef(Ljava/lang/Object;)V", - "scala/runtime/VolatileBooleanRef(Z)V", - "scala/runtime/VolatileByteRef(B)V", - "scala/runtime/VolatileCharRef(C)V", - "scala/runtime/VolatileShortRef(S)V", - "scala/runtime/VolatileIntRef(I)V", - "scala/runtime/VolatileLongRef(J)V", - "scala/runtime/VolatileFloatRef(F)V", - "scala/runtime/VolatileDoubleRef(D)V") - - def isRuntimeRefConstructor(insn: MethodInsnNode): Boolean = { - insn.name == INSTANCE_CONSTRUCTOR_NAME && runtimeRefConstructors(insn.owner + insn.desc) - } - - private val tupleConstructors = Set.empty[String] ++ { - (1 to 22).map(n => "scala/Tuple" + n + "(" + ("Ljava/lang/Object;" * n) + ")V") - } ++ { - Iterator("I", "J", "D").map(t => "scala/Tuple1$mc" + t + "$sp(" + t + ")V") - } ++ { - def tuple2Specs = Iterator("I", "J", "D", "C", "Z") - for (a <- tuple2Specs; b <- tuple2Specs) yield "scala/Tuple2$mc" + a + b + "$sp(" + a + b + ")V" - } - - def isTupleConstructor(insn: MethodInsnNode): Boolean = { - insn.name == INSTANCE_CONSTRUCTOR_NAME && tupleConstructors(insn.owner + insn.desc) - } - - // unused objects created by these constructors are eliminated by pushPop - private val sideEffectFreeConstructors = primitiveBoxConstructors ++ - runtimeRefConstructors ++ - tupleConstructors ++ Set( - "java/lang/Object()V", - "java/lang/String()V", - "java/lang/String(Ljava/lang/String;)V", - "java/lang/String([C)V" - ) - - def isSideEffectFreeConstructorCall(insn: MethodInsnNode): Boolean = { - insn.name == INSTANCE_CONSTRUCTOR_NAME && sideEffectFreeConstructors(insn.owner + insn.desc) - } - - private val classesForSideEffectFreeConstructors = sideEffectFreeConstructors.map(s => s.substring(0, s.indexOf('('))) - - // we only eliminate `NEW C` if the class C has a constructor that we consider side-effect free. - // removing a `NEW` eliminates a potential NoClassDefFoundError, so we only do it for core classes. - def isNewForSideEffectFreeConstructor(insn: AbstractInsnNode) = { - insn.getOpcode == NEW && { - val ti = insn.asInstanceOf[TypeInsnNode] - classesForSideEffectFreeConstructors(ti.desc) - } - } - - def isBoxedUnit(insn: AbstractInsnNode) = { - insn.getOpcode == GETSTATIC && { - val fi = insn.asInstanceOf[FieldInsnNode] - fi.owner == "scala/runtime/BoxedUnit" && fi.name == "UNIT" && fi.desc == "Lscala/runtime/BoxedUnit;" - } - } - - private def buildFunctionTypes(base: String): Set[InternalName] = { - def primitives = Iterator("B", "S", "I", "J", "C", "F", "D", "Z", "V") - def ijfd = Iterator("I", "J", "F", "D") - def ijfdzv = Iterator("I", "J", "F", "D", "Z", "V") - def ijd = Iterator("I", "J", "D") - Set.empty[String] ++ { - (0 to 22).map(base + _) - } ++ { - primitives.map(base + "0$mc" + _ + "$sp") // Function0 - } ++ { - // return type specializations appear first in the name string (alphabetical sorting) - for (r <- ijfdzv; a <- ijfd) yield base + "1$mc" + r + a + "$sp" // Function1 - } ++ { - for (r <- ijfdzv; a <- ijd; b <- ijd) yield base + "2$mc" + r + a + b + "$sp" // Function2 - } - } - - private val srJFunctionTypes: Set[InternalName] = buildFunctionTypes("scala/runtime/java8/JFunction") - def isrJFunctionType(internalName: InternalName): Boolean = srJFunctionTypes(internalName) - - private val sFunctionTypes: Set[InternalName] = buildFunctionTypes("scala/Function") - def isScalaFunctionType(internalName: InternalName): Boolean = sFunctionTypes(internalName) - implicit class AnalyzerExtensions[V <: Value](val analyzer: Analyzer[V]) extends AnyVal { def frameAt(instruction: AbstractInsnNode, methodNode: MethodNode): Frame[V] = analyzer.getFrames()(methodNode.instructions.indexOf(instruction)) } 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 64677ddcc0..863bb2d10a 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala @@ -103,7 +103,7 @@ class CallGraph[BT <: BTypes](val btypes: BT) { val analyzer = { if (compilerSettings.YoptNullnessTracking && AsmAnalyzer.sizeOKForNullness(methodNode)) { - Some(new AsmAnalyzer(methodNode, definingClass.internalName, new NullnessAnalyzer)) + Some(new AsmAnalyzer(methodNode, definingClass.internalName, new NullnessAnalyzer(btypes))) } else if (AsmAnalyzer.sizeOKForBasicValue(methodNode)) { Some(new AsmAnalyzer(methodNode, definingClass.internalName)) } else None 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 35bc82d098..f98a08a6a9 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/opt/ClosureOptimizer.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/ClosureOptimizer.scala @@ -254,18 +254,6 @@ class ClosureOptimizer[BT <: BTypes](val btypes: BT) { Type.VOID <= sort && sort <= Type.DOUBLE } - private def unboxOp(primitiveType: Type): MethodInsnNode = { - val bType = bTypeForDescriptorOrInternalNameFromClassfile(primitiveType.getDescriptor) - val MethodNameAndType(name, methodBType) = asmUnboxTo(bType) - new MethodInsnNode(INVOKESTATIC, srBoxesRunTimeRef.internalName, name, methodBType.descriptor, /*itf =*/ false) - } - - private def boxOp(primitiveType: Type): MethodInsnNode = { - val bType = bTypeForDescriptorOrInternalNameFromClassfile(primitiveType.getDescriptor) - val MethodNameAndType(name, methodBType) = asmBoxTo(bType) - new MethodInsnNode(INVOKESTATIC, srBoxesRunTimeRef.internalName, name, methodBType.descriptor, /*itf =*/ false) - } - /** * The argument types of the lambda body method may differ in two ways from the argument types of * the closure member method that is invoked (and replaced by a call to the body). @@ -291,9 +279,9 @@ class ClosureOptimizer[BT <: BTypes](val btypes: BT) { if (invokeArgTypes(i) == implMethodArgTypes(i)) { res(i) = None } else if (isPrimitiveType(implMethodArgTypes(i)) && invokeArgTypes(i).getDescriptor == ObjectRef.descriptor) { - res(i) = Some(unboxOp(implMethodArgTypes(i))) + res(i) = Some(getScalaUnbox(implMethodArgTypes(i))) } else if (isPrimitiveType(invokeArgTypes(i)) && implMethodArgTypes(i).getDescriptor == ObjectRef.descriptor) { - res(i) = Some(boxOp(invokeArgTypes(i))) + res(i) = Some(getScalaBox(invokeArgTypes(i))) } else { assert(!isPrimitiveType(invokeArgTypes(i)), invokeArgTypes(i)) assert(!isPrimitiveType(implMethodArgTypes(i)), implMethodArgTypes(i)) @@ -354,12 +342,12 @@ class ClosureOptimizer[BT <: BTypes](val btypes: BT) { if (isPrimitiveType(invocationReturnType) && bodyReturnType.getDescriptor == ObjectRef.descriptor) { val op = if (invocationReturnType.getSort == Type.VOID) getPop(1) - else unboxOp(invocationReturnType) + else getScalaUnbox(invocationReturnType) ownerMethod.instructions.insertBefore(invocation, op) } else if (isPrimitiveType(bodyReturnType) && invocationReturnType.getDescriptor == ObjectRef.descriptor) { val op = if (bodyReturnType.getSort == Type.VOID) getBoxedUnit - else boxOp(bodyReturnType) + else getScalaBox(bodyReturnType) ownerMethod.instructions.insertBefore(invocation, op) } else { // see comment of that method 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 8db922a635..cd4d5be197 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala @@ -364,7 +364,7 @@ class LocalOpt[BT <: BTypes](val btypes: BT) { */ def nullnessOptimizations(method: MethodNode, ownerClassName: InternalName): Boolean = { AsmAnalyzer.sizeOKForNullness(method) && { - lazy val nullnessAnalyzer = new AsmAnalyzer(method, ownerClassName, new NullnessAnalyzer) + lazy val nullnessAnalyzer = new AsmAnalyzer(method, ownerClassName, new NullnessAnalyzer(btypes)) // When running nullness optimizations the method may still have unreachable code. Analyzer // frames of unreachable instructions are `null`. diff --git a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala index d1be1558b9..43f698d7ba 100644 --- a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala +++ b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala @@ -31,11 +31,6 @@ abstract class LambdaLift extends InfoTransform { } } - /** scala.runtime.*Ref classes */ - private lazy val allRefClasses: Set[Symbol] = { - refClass.values.toSet ++ volatileRefClass.values.toSet ++ Set(VolatileObjectRefClass, ObjectRefClass) - } - /** Each scala.runtime.*Ref class has a static method `create(value)` that simply instantiates the Ref to carry that value. */ private lazy val refCreateMethod: Map[Symbol, Symbol] = { mapFrom(allRefClasses.toList)(x => getMemberMethod(x.companionModule, nme.create)) diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index 5ce5c39145..f2da7614a3 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -93,6 +93,10 @@ trait Definitions extends api.StandardDefinitions { lazy val refClass = classesMap(x => getRequiredClass("scala.runtime." + x + "Ref")) lazy val volatileRefClass = classesMap(x => getRequiredClass("scala.runtime.Volatile" + x + "Ref")) + lazy val allRefClasses: Set[Symbol] = { + refClass.values.toSet ++ volatileRefClass.values.toSet ++ Set(VolatileObjectRefClass, ObjectRefClass) + } + def isNumericSubClass(sub: Symbol, sup: Symbol) = ( (numericWeight contains sub) && (numericWeight contains sup) diff --git a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala index 0132fff17c..46e4371cfa 100644 --- a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala +++ b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala @@ -424,6 +424,7 @@ trait JavaUniverseForce { self: runtime.JavaUniverse => definitions.boxedClass definitions.refClass definitions.volatileRefClass + definitions.allRefClasses definitions.UnitClass definitions.ByteClass definitions.ShortClass 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 a7d1dc168a..daff6fc223 100644 --- a/test/junit/scala/tools/nsc/backend/jvm/analysis/NullnessAnalyzerTest.scala +++ b/test/junit/scala/tools/nsc/backend/jvm/analysis/NullnessAnalyzerTest.scala @@ -33,7 +33,7 @@ class NullnessAnalyzerTest extends ClearAfterClass { val noOptCompiler = NullnessAnalyzerTest.noOptCompiler import noOptCompiler.genBCode.bTypes.backendUtils._ - def newNullnessAnalyzer(methodNode: MethodNode, classInternalName: InternalName = "C") = new AsmAnalyzer(methodNode, classInternalName, new NullnessAnalyzer) + def newNullnessAnalyzer(methodNode: MethodNode, classInternalName: InternalName = "C") = new AsmAnalyzer(methodNode, classInternalName, new NullnessAnalyzer(noOptCompiler.genBCode.bTypes)) def testNullness(analyzer: AsmAnalyzer[NullnessValue], method: MethodNode, query: String, index: Int, nullness: NullnessValue): Unit = { for (i <- findInstr(method, query)) { -- cgit v1.2.3