diff options
Diffstat (limited to 'src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala')
-rw-r--r-- | src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala | 174 |
1 files changed, 102 insertions, 72 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala index 0c26e01322..3e3229d2c3 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala @@ -7,15 +7,18 @@ package scala.tools.nsc package backend.jvm import scala.annotation.switch +import scala.collection.{concurrent, mutable} import scala.collection.concurrent.TrieMap import scala.reflect.internal.util.Position import scala.tools.asm import asm.Opcodes -import scala.tools.asm.tree.{MethodNode, MethodInsnNode, InnerClassNode, ClassNode} +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.BackendUtils import scala.tools.nsc.backend.jvm.opt._ -import scala.collection.convert.decorateAsScala._ +import scala.collection.JavaConverters._ +import scala.collection.mutable.ListBuffer import scala.tools.nsc.settings.ScalaSettings /** @@ -29,6 +32,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. @@ -38,12 +43,14 @@ abstract class BTypes { /** * Tools for parsing classfiles, used by the inliner. */ - val byteCodeRepository: ByteCodeRepository + val byteCodeRepository: ByteCodeRepository[this.type] val localOpt: LocalOpt[this.type] val inliner: Inliner[this.type] + val inlinerHeuristics: InlinerHeuristics[this.type] + val closureOptimizer: ClosureOptimizer[this.type] val callGraph: CallGraph[this.type] @@ -56,7 +63,6 @@ abstract class BTypes { // Allows access to the compiler settings for backend components that don't have a global in scope def compilerSettings: ScalaSettings - /** * A map from internal names to ClassBTypes. Every ClassBType is added to this map on its * construction. @@ -68,19 +74,27 @@ abstract class BTypes { * Concurrent because stack map frames are computed when in the class writer, which might run * on multiple classes concurrently. */ - val classBTypeFromInternalName: collection.concurrent.Map[InternalName, ClassBType] = recordPerRunCache(TrieMap.empty) + val classBTypeFromInternalName: concurrent.Map[InternalName, ClassBType] = recordPerRunCache(TrieMap.empty) /** * Store the position of every MethodInsnNode during code generation. This allows each callsite * in the call graph to remember its source position, which is required for inliner warnings. */ - val callsitePositions: collection.concurrent.Map[MethodInsnNode, Position] = recordPerRunCache(TrieMap.empty) + val callsitePositions: concurrent.Map[MethodInsnNode, Position] = recordPerRunCache(TrieMap.empty) + + /** + * Stores callsite instructions of invocations annotated `f(): @inline/noinline`. + * Instructions are added during code generation (BCodeBodyBuilder). The maps are then queried + * when building the CallGraph, every Callsite object has an annotated(No)Inline field. + */ + val inlineAnnotatedCallsites: mutable.Set[MethodInsnNode] = recordPerRunCache(mutable.Set.empty) + val noInlineAnnotatedCallsites: mutable.Set[MethodInsnNode] = recordPerRunCache(mutable.Set.empty) /** * Contains the internal names of all classes that are defined in Java source files of the current * compilation run (mixed compilation). Used for more detailed error reporting. */ - val javaDefinedClasses: collection.mutable.Set[InternalName] = recordPerRunCache(collection.mutable.Set.empty) + val javaDefinedClasses: mutable.Set[InternalName] = recordPerRunCache(mutable.Set.empty) /** * Cache, contains methods whose unreachable instructions are eliminated. @@ -92,12 +106,47 @@ abstract class BTypes { * This cache allows running dead code elimination whenever an analyzer is used. If the method * is already optimized, DCE can return early. */ - val unreachableCodeEliminated: collection.mutable.Set[MethodNode] = recordPerRunCache(collection.mutable.Set.empty) + val unreachableCodeEliminated: mutable.Set[MethodNode] = recordPerRunCache(mutable.Set.empty) + + /** + * Cache of methods which have correct `maxLocals` / `maxStack` values assigned. This allows + * invoking `computeMaxLocalsMaxStack` whenever running an analyzer but performing the actual + * computation only when necessary. + */ + val maxLocalsMaxStackComputed: mutable.Set[MethodNode] = recordPerRunCache(mutable.Set.empty) + + /** + * Classes with indyLambda closure instantiations where the SAM type is serializable (e.g. Scala's + * FunctionN) need a `$deserializeLambda$` method. This map contains classes for which such a + * method has been generated. It is used during ordinary code generation, as well as during + * inlining: when inlining an indyLambda instruction into a class, we need to make sure the class + * has the method. + */ + val indyLambdaImplMethods: mutable.AnyRefMap[InternalName, mutable.LinkedHashSet[asm.Handle]] = recordPerRunCache(mutable.AnyRefMap()) + def addIndyLambdaImplMethod(hostClass: InternalName, handle: Seq[asm.Handle]): Seq[asm.Handle] = { + if (handle.isEmpty) Nil else { + val set = indyLambdaImplMethods.getOrElseUpdate(hostClass, mutable.LinkedHashSet()) + val added = handle.filterNot(set) + set ++= handle + added + } + } + def removeIndyLambdaImplMethod(hostClass: InternalName, handle: Seq[asm.Handle]): Unit = { + if (handle.nonEmpty) + indyLambdaImplMethods.getOrElseUpdate(hostClass, mutable.LinkedHashSet()) --= handle + } + + def getIndyLambdaImplMethods(hostClass: InternalName): Iterable[asm.Handle] = { + indyLambdaImplMethods.getOrNull(hostClass) match { + case null => Nil + case xs => xs + } + } /** * Obtain the BType for a type descriptor or internal name. For class descriptors, the ClassBType * is constructed by parsing the corresponding classfile. - * + * * Some JVM operations use either a full descriptor or only an internal name. Example: * ANEWARRAY java/lang/String // a new array of strings (internal name for the String class) * ANEWARRAY [Ljava/lang/String; // a new array of array of string (full descriptor for the String class) @@ -128,7 +177,7 @@ abstract class BTypes { val res = ClassBType(internalName) byteCodeRepository.classNode(internalName) match { case Left(msg) => res.info = Left(NoClassBTypeInfoMissingBytecode(msg)); res - case Right(c) => setClassInfoFromParsedClassfile(c, res) + case Right(c) => setClassInfoFromClassNode(c, res) } }) } @@ -138,21 +187,19 @@ abstract class BTypes { */ def classBTypeFromClassNode(classNode: ClassNode): ClassBType = { classBTypeFromInternalName.getOrElse(classNode.name, { - setClassInfoFromParsedClassfile(classNode, ClassBType(classNode.name)) + setClassInfoFromClassNode(classNode, ClassBType(classNode.name)) }) } - private def setClassInfoFromParsedClassfile(classNode: ClassNode, classBType: ClassBType): ClassBType = { + private def setClassInfoFromClassNode(classNode: ClassNode, classBType: ClassBType): ClassBType = { val superClass = classNode.superName match { case null => - assert(classNode.name == ObjectReference.internalName, s"class with missing super type: ${classNode.name}") + assert(classNode.name == ObjectRef.internalName, s"class with missing super type: ${classNode.name}") None case superName => Some(classBTypeFromParsedClassfile(superName)) } - val interfaces: List[ClassBType] = classNode.interfaces.asScala.map(classBTypeFromParsedClassfile)(collection.breakOut) - val flags = classNode.access /** @@ -197,6 +244,8 @@ abstract class BTypes { val inlineInfo = inlineInfoFromClassfile(classNode) + val interfaces: List[ClassBType] = classNode.interfaces.asScala.map(classBTypeFromParsedClassfile)(collection.breakOut) + classBType.info = Right(ClassInfo(superClass, interfaces, flags, nestedClasses, nestedInfo, inlineInfo)) classBType } @@ -226,22 +275,21 @@ abstract class BTypes { val methodInfos = classNode.methods.asScala.map(methodNode => { val info = MethodInlineInfo( effectivelyFinal = BytecodeUtils.isFinalMethod(methodNode), - traitMethodWithStaticImplementation = false, annotatedInline = false, annotatedNoInline = false) (methodNode.name + methodNode.desc, info) }).toMap InlineInfo( - traitImplClassSelfType = None, isEffectivelyFinal = BytecodeUtils.isFinalClass(classNode), + sam = inlinerHeuristics.javaSam(classNode.name), methodInfos = methodInfos, warning) } // The InlineInfo is built from the classfile (not from the symbol) for all classes that are NOT - // being compiled. For those classes, the info is only needed if the inliner is enabled, othewise + // being compiled. For those classes, the info is only needed if the inliner is enabled, otherwise // we can save the memory. - if (!compilerSettings.YoptInlinerEnabled) BTypes.EmptyInlineInfo + if (!compilerSettings.optInlinerEnabled) BTypes.EmptyInlineInfo else fromClassfileAttribute getOrElse fromClassfileWithoutAttribute } @@ -291,8 +339,8 @@ abstract class BTypes { final def isNonVoidPrimitiveType = isPrimitive && this != UNIT - final def isNullType = this == RT_NULL - final def isNothingType = this == RT_NOTHING + final def isNullType = this == srNullRef + final def isNothingType = this == srNothingRef final def isBoxed = this.isClass && boxedClasses(this.asClassBType) @@ -315,7 +363,7 @@ abstract class BTypes { this match { case ArrayBType(component) => - if (other == ObjectReference || other == jlCloneableReference || other == jioSerializableReference) true + if (other == ObjectRef || other == jlCloneableRef || other == jiSerializableRef) true else other match { case ArrayBType(otherComponent) => component.conformsTo(otherComponent).orThrow case _ => false @@ -324,7 +372,7 @@ abstract class BTypes { case classType: ClassBType => if (isBoxed) { if (other.isBoxed) this == other - else if (other == ObjectReference) true + else if (other == ObjectRef) true else other match { case otherClassType: ClassBType => classType.isSubtypeOf(otherClassType).orThrow // e.g., java/lang/Double conforms to java/lang/Number case _ => false @@ -367,7 +415,7 @@ abstract class BTypes { assert(other.isRef, s"Cannot compute maxType: $this, $other") // Approximate `lub`. The common type of two references is always ObjectReference. - ObjectReference + ObjectRef case _: MethodBType => assertionError(s"unexpected method type when computing maxType: $this") @@ -554,6 +602,8 @@ abstract class BTypes { * Terminology * ----------- * + * Diagram here: https://blogs.oracle.com/darcy/entry/nested_inner_member_and_top + * * - Nested class (JLS 8): class whose declaration occurs within the body of another class * * - Top-level class (JLS 8): non-nested class @@ -603,7 +653,7 @@ abstract class BTypes { * Fields in the InnerClass entries: * - inner class: the (nested) class C we are talking about * - outer class: the class of which C is a member. Has to be null for non-members, i.e. for - * local and anonymous classes. NOTE: this co-incides with the presence of an + * local and anonymous classes. NOTE: this coincides with the presence of an * EnclosingMethod attribute (see below) * - inner name: A string with the simple name of the inner class. Null for anonymous classes. * - flags: access property flags, details in JVMS, table in 4.7.6. Static flag: see @@ -652,7 +702,7 @@ abstract class BTypes { * local and anonymous classes, no matter if there is an enclosing method or not. Accordingly, the * "class" field (see below) must be always defined, while the "method" field may be null. * - * NOTE: When an EnclosingMethod attribute is requried (local and anonymous classes), the "outer" + * NOTE: When an EnclosingMethod attribute is required (local and anonymous classes), the "outer" * field in the InnerClass table must be null. * * Fields: @@ -760,26 +810,17 @@ abstract class BTypes { * } * * - * Traits Members - * -------------- - * - * Some trait methods don't exist in the generated interface, but only in the implementation class - * (private methods in traits for example). Since EnclosingMethod expresses a source-level property, - * but the source-level enclosing method doesn't exist in the classfile, we the enclosing method - * is null (the enclosing class is still emitted). - * See BCodeAsmCommon.considerAsTopLevelImplementationArtifact - * + * Specialized Classes, Delambdafy:method closure classes + * ------------------------------------------------------ * - * Implementation Classes, Specialized Classes, Delambdafy:method closure classes - * ------------------------------------------------------------------------------ - * - * Trait implementation classes and specialized classes are always considered top-level. Again, - * the InnerClass / EnclosingMethod attributes describe a source-level properties. The impl - * classes are compilation artifacts. + * Specialized classes are always considered top-level, as the InnerClass / EnclosingMethod + * attributes describe a source-level properties. * * The same is true for delambdafy:method closure classes. These classes are generated at * top-level in the delambdafy phase, no special support is required in the backend. * + * See also BCodeHelpers.considerAsTopLevelImplementationArtifact. + * * * Mirror Classes * -------------- @@ -837,7 +878,7 @@ abstract class BTypes { // best-effort verification. also we don't report an error if the info is a Left. def ifInit(c: ClassBType)(p: ClassBType => Boolean): Boolean = c._info == null || c.info.isLeft || p(c) - def isJLO(t: ClassBType) = t.internalName == ObjectReference.internalName + def isJLO(t: ClassBType) = t.internalName == ObjectRef.internalName assert(!ClassBType.isInternalPhantomType(internalName), s"Cannot create ClassBType for phantom type $this") @@ -900,7 +941,7 @@ abstract class BTypes { // the static flag in the InnerClass table has a special meaning, see InnerClass comment i.flags & ~Opcodes.ACC_STATIC, if (isStaticNestedClass) Opcodes.ACC_STATIC else 0 - ) & BCodeAsmCommon.INNER_CLASSES_FLAGS + ) & BCodeHelpers.INNER_CLASSES_FLAGS ) }) @@ -917,7 +958,7 @@ abstract class BTypes { def isSubtypeOf(other: ClassBType): Either[NoClassBTypeInfo, Boolean] = try { if (this == other) return Right(true) if (isInterface.orThrow) { - if (other == ObjectReference) return Right(true) // interfaces conform to Object + if (other == ObjectRef) return Right(true) // interfaces conform to Object if (!other.isInterface.orThrow) return Right(false) // this is an interface, the other is some class other than object. interfaces cannot extend classes, so the result is false. // else: this and other are both interfaces. continue to (*) } else { @@ -950,13 +991,13 @@ abstract class BTypes { // exercised by test/files/run/t4761.scala if (other.isSubtypeOf(this).orThrow) this else if (this.isSubtypeOf(other).orThrow) other - else ObjectReference + else ObjectRef case (true, false) => - if (other.isSubtypeOf(this).orThrow) this else ObjectReference + if (other.isSubtypeOf(this).orThrow) this else ObjectRef case (false, true) => - if (this.isSubtypeOf(other).orThrow) other else ObjectReference + if (this.isSubtypeOf(other).orThrow) other else ObjectRef case _ => // TODO @lry I don't really understand the reasoning here. @@ -1081,7 +1122,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) @@ -1103,24 +1144,14 @@ object BTypes { /** * Metadata about a ClassBType, used by the inliner. * - * More information may be added in the future to enable more elaborate inlinine heuristics. - * - * @param traitImplClassSelfType `Some(tp)` if this InlineInfo describes a trait, and the `self` - * parameter type of the methods in the implementation class is not - * the trait itself. Example: - * trait T { self: U => def f = 1 } - * Generates something like: - * class T$class { static def f(self: U) = 1 } - * - * In order to inline a trat method call, the INVOKEINTERFACE is - * rewritten to an INVOKESTATIC of the impl class, so we need the - * self type (U) to get the right signature. - * - * `None` if the self type is the interface type, or if this - * InlineInfo does not describe a trait. + * More information may be added in the future to enable more elaborate inline heuristics. + * Note that this class should contain information that can only be obtained from the ClassSymbol. + * Information that can be computed from the ClassNode should be added to the call graph instead. * * @param isEffectivelyFinal True if the class cannot have subclasses: final classes, module - * classes, trait impl classes. + * classes. + * + * @param sam If this class is a SAM type, the SAM's "$name$descriptor". * * @param methodInfos The [[MethodInlineInfo]]s for the methods declared in this class. * The map is indexed by the string s"$name$descriptor" (to @@ -1130,29 +1161,28 @@ object BTypes { * InlineInfo, for example if some classfile could not be found on * the classpath. This warning can be reported later by the inliner. */ - final case class InlineInfo(traitImplClassSelfType: Option[InternalName], - isEffectivelyFinal: Boolean, + final case class InlineInfo(isEffectivelyFinal: Boolean, + sam: Option[String], methodInfos: Map[String, MethodInlineInfo], warning: Option[ClassInlineInfoWarning]) - val EmptyInlineInfo = InlineInfo(None, false, Map.empty, None) + val EmptyInlineInfo = InlineInfo(false, None, Map.empty, None) /** * Metadata about a method, used by the inliner. * * @param effectivelyFinal True if the method cannot be overridden (in Scala) - * @param traitMethodWithStaticImplementation True if the method is an interface method method of - * a trait method and has a static counterpart in the - * implementation class. * @param annotatedInline True if the method is annotated `@inline` * @param annotatedNoInline True if the method is annotated `@noinline` */ final case class MethodInlineInfo(effectivelyFinal: Boolean, - traitMethodWithStaticImplementation: Boolean, annotatedInline: Boolean, annotatedNoInline: Boolean) // no static way (without symbol table instance) to get to nme.ScalaATTR / ScalaSignatureATTR val ScalaAttributeName = "Scala" val ScalaSigAttributeName = "ScalaSig" -}
\ No newline at end of file + + // when inlining, local variable names of the callee are prefixed with the name of the callee method + val InlinedLocalVariablePrefixMaxLenght = 128 +} |