diff options
Diffstat (limited to 'examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/GenIncOptimizer.scala')
-rw-r--r-- | examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/GenIncOptimizer.scala | 921 |
1 files changed, 921 insertions, 0 deletions
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/GenIncOptimizer.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/GenIncOptimizer.scala new file mode 100644 index 0000000..47e1f87 --- /dev/null +++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/GenIncOptimizer.scala @@ -0,0 +1,921 @@ +/* __ *\ +** ________ ___ / / ___ __ ____ Scala.js tools ** +** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ ** +** /____/\___/_/ |_/____/_/ | |__/ /____/ ** +** |/____/ ** +\* */ + + +package scala.scalajs.tools.optimizer + +import language.higherKinds + +import scala.annotation.{switch, tailrec} + +import scala.collection.{GenMap, GenTraversableOnce, GenIterable, GenIterableLike} +import scala.collection.mutable + +import scala.scalajs.ir._ +import Definitions.isConstructorName +import Infos.OptimizerHints +import Trees._ +import Types._ + +import scala.scalajs.tools.sem._ + +import scala.scalajs.tools.javascript +import javascript.Trees.{Tree => JSTree} +import javascript.ScalaJSClassEmitter + +import scala.scalajs.tools.logging._ + +/** Incremental optimizer. + * An incremental optimizer consumes the reachability analysis produced by + * an [[Analyzer]], as well as trees for classes, trait impls, etc., and + * optimizes them in an incremental way. + * It maintains state between runs to do a minimal amount of work on every + * run, based on detecting what parts of the program must be re-optimized, + * and keeping optimized results from previous runs for the rest. + */ +abstract class GenIncOptimizer(semantics: Semantics) { + import GenIncOptimizer._ + + protected val CollOps: AbsCollOps + + private val classEmitter = new ScalaJSClassEmitter(semantics) + + private var logger: Logger = _ + + /** Are we in batch mode? I.e., are we running from scratch? + * Various parts of the algorithm can be skipped entirely when running in + * batch mode. + */ + private var batchMode: Boolean = false + + /** Should positions be considered when comparing tree hashes */ + private var considerPositions: Boolean = _ + + private var objectClass: Class = _ + private val classes = CollOps.emptyMap[String, Class] + private val traitImpls = CollOps.emptyParMap[String, TraitImpl] + + protected def getInterface(encodedName: String): InterfaceType + + /** Schedule a method for processing in the PROCESS PASS */ + protected def scheduleMethod(method: MethodImpl): Unit + + protected def newMethodImpl(owner: MethodContainer, + encodedName: String): MethodImpl + + def findTraitImpl(encodedName: String): TraitImpl = traitImpls(encodedName) + def findClass(encodedName: String): Class = classes(encodedName) + + def getTraitImpl(encodedName: String): Option[TraitImpl] = traitImpls.get(encodedName) + def getClass(encodedName: String): Option[Class] = classes.get(encodedName) + + type GetClassTreeIfChanged = + (String, Option[String]) => Option[(ClassDef, Option[String])] + + private def withLogger[A](logger: Logger)(body: => A): A = { + assert(this.logger == null) + this.logger = logger + try body + finally this.logger = null + } + + /** Update the incremental analyzer with a new run. */ + def update(analyzer: Analyzer, + getClassTreeIfChanged: GetClassTreeIfChanged, considerPositions: Boolean, + logger: Logger): Unit = withLogger(logger) { + + batchMode = objectClass == null + this.considerPositions = considerPositions + logger.debug(s"Optimizer batch mode: $batchMode") + + logTime(logger, "Incremental part of inc. optimizer") { + /* UPDATE PASS */ + updateAndTagEverything(analyzer, getClassTreeIfChanged) + } + + logTime(logger, "Optimizer part of inc. optimizer") { + /* PROCESS PASS */ + processAllTaggedMethods() + } + } + + /** Incremental part: update state and detect what needs to be re-optimized. + * UPDATE PASS ONLY. (This IS the update pass). + */ + private def updateAndTagEverything(analyzer: Analyzer, + getClassTreeIfChanged: GetClassTreeIfChanged): Unit = { + + val neededClasses = CollOps.emptyParMap[String, analyzer.ClassInfo] + val neededTraitImpls = CollOps.emptyParMap[String, analyzer.ClassInfo] + for { + classInfo <- analyzer.classInfos.values + if classInfo.isNeededAtAll + } { + if (classInfo.isClass && classInfo.isAnySubclassInstantiated) + CollOps.put(neededClasses, classInfo.encodedName, classInfo) + else if (classInfo.isImplClass) + CollOps.put(neededTraitImpls, classInfo.encodedName, classInfo) + } + + /* Remove deleted trait impls, and update existing trait impls. + * We don't even have to notify callers in case of additions or removals + * because callers have got to be invalidated by themselves. + * Only changed methods need to trigger notifications. + * + * Non-batch mode only. + */ + assert(!batchMode || traitImpls.isEmpty) + if (!batchMode) { + CollOps.retain(traitImpls) { (traitImplName, traitImpl) => + CollOps.remove(neededTraitImpls, traitImplName).fold { + /* Deleted trait impl. Mark all its methods as deleted, and remove it + * from known trait impls. + */ + traitImpl.methods.values.foreach(_.delete()) + + false + } { traitImplInfo => + /* Existing trait impl. Update it. */ + val (added, changed, removed) = + traitImpl.updateWith(traitImplInfo, getClassTreeIfChanged) + for (method <- changed) + traitImpl.myInterface.tagStaticCallersOf(method) + + true + } + } + } + + /* Add new trait impls. + * Easy, we don't have to notify anyone. + */ + for (traitImplInfo <- neededTraitImpls.values) { + val traitImpl = new TraitImpl(traitImplInfo.encodedName) + CollOps.put(traitImpls, traitImpl.encodedName, traitImpl) + traitImpl.updateWith(traitImplInfo, getClassTreeIfChanged) + } + + if (!batchMode) { + /* Class removals: + * * If a class is deleted or moved, delete its entire subtree (because + * all its descendants must also be deleted or moved). + * * If an existing class was instantiated but is no more, notify callers + * of its methods. + * + * Non-batch mode only. + */ + val objectClassStillExists = + objectClass.walkClassesForDeletions(neededClasses.get(_)) + assert(objectClassStillExists, "Uh oh, java.lang.Object was deleted!") + + /* Class changes: + * * Delete removed methods, update existing ones, add new ones + * * Update the list of ancestors + * * Class newly instantiated + * + * Non-batch mode only. + */ + objectClass.walkForChanges( + CollOps.remove(neededClasses, _).get, + getClassTreeIfChanged, + Set.empty) + } + + /* Class additions: + * * Add new classes (including those that have moved from elsewhere). + * In batch mode, we avoid doing notifications. + */ + + // Group children by (immediate) parent + val newChildrenByParent = CollOps.emptyAccMap[String, Analyzer#ClassInfo] + + for (classInfo <- neededClasses.values) { + val superInfo = classInfo.superClass + if (superInfo == null) { + assert(batchMode, "Trying to add java.lang.Object in incremental mode") + objectClass = new Class(None, classInfo.encodedName) + classes += classInfo.encodedName -> objectClass + objectClass.setupAfterCreation(classInfo, getClassTreeIfChanged) + } else { + CollOps.acc(newChildrenByParent, superInfo.encodedName, classInfo) + } + } + + val getNewChildren = + (name: String) => CollOps.getAcc(newChildrenByParent, name) + + // Walk the tree to add children + if (batchMode) { + objectClass.walkForAdditions(getNewChildren, getClassTreeIfChanged) + } else { + val existingParents = + CollOps.parFlatMapKeys(newChildrenByParent)(classes.get) + for (parent <- existingParents) + parent.walkForAdditions(getNewChildren, getClassTreeIfChanged) + } + + } + + /** Optimizer part: process all methods that need reoptimizing. + * PROCESS PASS ONLY. (This IS the process pass). + */ + protected def processAllTaggedMethods(): Unit + + protected def logProcessingMethods(count: Int): Unit = + logger.debug(s"Optimizing $count methods.") + + /** Base class for [[Class]] and [[TraitImpl]]. */ + abstract class MethodContainer(val encodedName: String) { + def thisType: Type + + val myInterface = getInterface(encodedName) + + val methods = mutable.Map.empty[String, MethodImpl] + + var lastVersion: Option[String] = None + + private def reachableMethodsOf(info: Analyzer#ClassInfo): Set[String] = { + (for { + methodInfo <- info.methodInfos.values + if methodInfo.isReachable && !methodInfo.isAbstract + } yield { + methodInfo.encodedName + }).toSet + } + + /** UPDATE PASS ONLY. Global concurrency safe but not on same instance */ + def updateWith(info: Analyzer#ClassInfo, + getClassTreeIfChanged: GetClassTreeIfChanged): (Set[String], Set[String], Set[String]) = { + myInterface.ancestors = info.ancestors.map(_.encodedName).toList + + val addedMethods = Set.newBuilder[String] + val changedMethods = Set.newBuilder[String] + val deletedMethods = Set.newBuilder[String] + + val reachableMethods = reachableMethodsOf(info) + val methodSetChanged = methods.keySet != reachableMethods + if (methodSetChanged) { + // Remove deleted methods + methods retain { (methodName, method) => + if (reachableMethods.contains(methodName)) { + true + } else { + deletedMethods += methodName + method.delete() + false + } + } + // Clear lastVersion if there are new methods + if (reachableMethods.exists(!methods.contains(_))) + lastVersion = None + } + for ((tree, version) <- getClassTreeIfChanged(encodedName, lastVersion)) { + lastVersion = version + this match { + case cls: Class => + cls.isModuleClass = tree.kind == ClassKind.ModuleClass + cls.fields = for (field @ VarDef(_, _, _, _) <- tree.defs) yield field + case _ => + } + tree.defs.foreach { + case methodDef: MethodDef if methodDef.name.isInstanceOf[Ident] && + reachableMethods.contains(methodDef.name.name) => + val methodName = methodDef.name.name + + val methodInfo = info.methodInfos(methodName) + methods.get(methodName).fold { + addedMethods += methodName + val method = newMethodImpl(this, methodName) + method.updateWith(methodInfo, methodDef) + methods(methodName) = method + method + } { method => + if (method.updateWith(methodInfo, methodDef)) + changedMethods += methodName + method + } + + case _ => // ignore + } + } + + (addedMethods.result(), changedMethods.result(), deletedMethods.result()) + } + } + + /** Class in the class hierarchy (not an interface). + * A class may be a module class. + * A class knows its superclass and the interfaces it implements. It also + * maintains a list of its direct subclasses, so that the instances of + * [[Class]] form a tree of the class hierarchy. + */ + class Class(val superClass: Option[Class], + _encodedName: String) extends MethodContainer(_encodedName) { + if (encodedName == Definitions.ObjectClass) { + assert(superClass.isEmpty) + assert(objectClass == null) + } else { + assert(superClass.isDefined) + } + + /** Parent chain from this to Object. */ + val parentChain: List[Class] = + this :: superClass.fold[List[Class]](Nil)(_.parentChain) + + /** Reverse parent chain from Object to this. */ + val reverseParentChain: List[Class] = + parentChain.reverse + + def thisType: Type = ClassType(encodedName) + + var interfaces: Set[InterfaceType] = Set.empty + var subclasses: CollOps.ParIterable[Class] = CollOps.emptyParIterable + var isInstantiated: Boolean = false + + var isModuleClass: Boolean = false + var hasElidableModuleAccessor: Boolean = false + + var fields: List[VarDef] = Nil + var isInlineable: Boolean = false + var tryNewInlineable: Option[RecordValue] = None + + override def toString(): String = + encodedName + + /** Walk the class hierarchy tree for deletions. + * This includes "deleting" classes that were previously instantiated but + * are no more. + * UPDATE PASS ONLY. Not concurrency safe on same instance. + */ + def walkClassesForDeletions( + getClassInfoIfNeeded: String => Option[Analyzer#ClassInfo]): Boolean = { + def sameSuperClass(info: Analyzer#ClassInfo): Boolean = + if (info.superClass == null) superClass.isEmpty + else superClass.exists(_.encodedName == info.superClass.encodedName) + + getClassInfoIfNeeded(encodedName) match { + case Some(classInfo) if sameSuperClass(classInfo) => + // Class still exists. Recurse. + subclasses = subclasses.filter( + _.walkClassesForDeletions(getClassInfoIfNeeded)) + if (isInstantiated && !classInfo.isInstantiated) + notInstantiatedAnymore() + true + case _ => + // Class does not exist or has been moved. Delete the entire subtree. + deleteSubtree() + false + } + } + + /** Delete this class and all its subclasses. UPDATE PASS ONLY. */ + def deleteSubtree(): Unit = { + delete() + for (subclass <- subclasses) + subclass.deleteSubtree() + } + + /** UPDATE PASS ONLY. */ + private def delete(): Unit = { + if (isInstantiated) + notInstantiatedAnymore() + for (method <- methods.values) + method.delete() + classes -= encodedName + /* Note: no need to tag methods that call *statically* one of the methods + * of the deleted classes, since they've got to be invalidated by + * themselves. + */ + } + + /** UPDATE PASS ONLY. */ + def notInstantiatedAnymore(): Unit = { + assert(isInstantiated) + isInstantiated = false + for (intf <- interfaces) { + intf.removeInstantiatedSubclass(this) + for (methodName <- allMethods().keys) + intf.tagDynamicCallersOf(methodName) + } + } + + /** UPDATE PASS ONLY. */ + def walkForChanges( + getClassInfo: String => Analyzer#ClassInfo, + getClassTreeIfChanged: GetClassTreeIfChanged, + parentMethodAttributeChanges: Set[String]): Unit = { + + val classInfo = getClassInfo(encodedName) + + val (addedMethods, changedMethods, deletedMethods) = + updateWith(classInfo, getClassTreeIfChanged) + + val oldInterfaces = interfaces + val newInterfaces = + classInfo.ancestors.map(info => getInterface(info.encodedName)).toSet + interfaces = newInterfaces + + val methodAttributeChanges = + (parentMethodAttributeChanges -- methods.keys ++ + addedMethods ++ changedMethods ++ deletedMethods) + + // Tag callers with dynamic calls + val wasInstantiated = isInstantiated + isInstantiated = classInfo.isInstantiated + assert(!(wasInstantiated && !isInstantiated), + "(wasInstantiated && !isInstantiated) should have been handled "+ + "during deletion phase") + + if (isInstantiated) { + if (wasInstantiated) { + val existingInterfaces = oldInterfaces.intersect(newInterfaces) + for { + intf <- existingInterfaces + methodName <- methodAttributeChanges + } { + intf.tagDynamicCallersOf(methodName) + } + if (newInterfaces.size != oldInterfaces.size || + newInterfaces.size != existingInterfaces.size) { + val allMethodNames = allMethods().keys + for { + intf <- oldInterfaces ++ newInterfaces -- existingInterfaces + methodName <- allMethodNames + } { + intf.tagDynamicCallersOf(methodName) + } + } + } else { + val allMethodNames = allMethods().keys + for (intf <- interfaces) { + intf.addInstantiatedSubclass(this) + for (methodName <- allMethodNames) + intf.tagDynamicCallersOf(methodName) + } + } + } + + // Tag callers with static calls + for (methodName <- methodAttributeChanges) + myInterface.tagStaticCallersOf(methodName) + + // Module class specifics + updateHasElidableModuleAccessor() + + // Inlineable class + if (updateIsInlineable(classInfo)) { + for (method <- methods.values; if isConstructorName(method.encodedName)) + myInterface.tagStaticCallersOf(method.encodedName) + } + + // Recurse in subclasses + for (cls <- subclasses) + cls.walkForChanges(getClassInfo, getClassTreeIfChanged, + methodAttributeChanges) + } + + /** UPDATE PASS ONLY. */ + def walkForAdditions( + getNewChildren: String => GenIterable[Analyzer#ClassInfo], + getClassTreeIfChanged: GetClassTreeIfChanged): Unit = { + + val subclassAcc = CollOps.prepAdd(subclasses) + + for (classInfo <- getNewChildren(encodedName)) { + val cls = new Class(Some(this), classInfo.encodedName) + CollOps.add(subclassAcc, cls) + classes += classInfo.encodedName -> cls + cls.setupAfterCreation(classInfo, getClassTreeIfChanged) + cls.walkForAdditions(getNewChildren, getClassTreeIfChanged) + } + + subclasses = CollOps.finishAdd(subclassAcc) + } + + /** UPDATE PASS ONLY. */ + def updateHasElidableModuleAccessor(): Unit = { + hasElidableModuleAccessor = + isAdHocElidableModuleAccessor(encodedName) || + (isModuleClass && lookupMethod("init___").exists(isElidableModuleConstructor)) + } + + /** UPDATE PASS ONLY. */ + def updateIsInlineable(classInfo: Analyzer#ClassInfo): Boolean = { + val oldTryNewInlineable = tryNewInlineable + isInlineable = classInfo.optimizerHints.hasInlineAnnot + if (!isInlineable) { + tryNewInlineable = None + } else { + val allFields = reverseParentChain.flatMap(_.fields) + val (fieldValues, fieldTypes) = (for { + VarDef(Ident(name, originalName), tpe, mutable, rhs) <- allFields + } yield { + (rhs, RecordType.Field(name, originalName, tpe, mutable)) + }).unzip + tryNewInlineable = Some( + RecordValue(RecordType(fieldTypes), fieldValues)(Position.NoPosition)) + } + tryNewInlineable != oldTryNewInlineable + } + + /** UPDATE PASS ONLY. */ + def setupAfterCreation(classInfo: Analyzer#ClassInfo, + getClassTreeIfChanged: GetClassTreeIfChanged): Unit = { + + updateWith(classInfo, getClassTreeIfChanged) + interfaces = + classInfo.ancestors.map(info => getInterface(info.encodedName)).toSet + + isInstantiated = classInfo.isInstantiated + + if (batchMode) { + if (isInstantiated) { + /* Only add the class to all its ancestor interfaces */ + for (intf <- interfaces) + intf.addInstantiatedSubclass(this) + } + } else { + val allMethodNames = allMethods().keys + + if (isInstantiated) { + /* Add the class to all its ancestor interfaces + notify all callers + * of any of the methods. + * TODO: be more selective on methods that are notified: it is not + * necessary to modify callers of methods defined in a parent class + * that already existed in the previous run. + */ + for (intf <- interfaces) { + intf.addInstantiatedSubclass(this) + for (methodName <- allMethodNames) + intf.tagDynamicCallersOf(methodName) + } + } + + /* Tag static callers because the class could have been *moved*, + * not just added. + */ + for (methodName <- allMethodNames) + myInterface.tagStaticCallersOf(methodName) + } + + updateHasElidableModuleAccessor() + updateIsInlineable(classInfo) + } + + /** UPDATE PASS ONLY. */ + private def isElidableModuleConstructor(impl: MethodImpl): Boolean = { + def isTriviallySideEffectFree(tree: Tree): Boolean = tree match { + case _:VarRef | _:Literal | _:This => true + case _ => false + } + def isElidableStat(tree: Tree): Boolean = tree match { + case Block(stats) => + stats.forall(isElidableStat) + case Assign(Select(This(), _, _), rhs) => + isTriviallySideEffectFree(rhs) + case TraitImplApply(ClassType(traitImpl), methodName, List(This())) => + traitImpls(traitImpl).methods(methodName.name).originalDef.body match { + case Skip() => true + case _ => false + } + case StaticApply(This(), ClassType(cls), methodName, args) => + Definitions.isConstructorName(methodName.name) && + args.forall(isTriviallySideEffectFree) && + impl.owner.asInstanceOf[Class].superClass.exists { superCls => + superCls.encodedName == cls && + superCls.lookupMethod(methodName.name).exists(isElidableModuleConstructor) + } + case StoreModule(_, _) => + true + case _ => + isTriviallySideEffectFree(tree) + } + isElidableStat(impl.originalDef.body) + } + + /** All the methods of this class, including inherited ones. + * It has () so we remember this is an expensive operation. + * UPDATE PASS ONLY. + */ + def allMethods(): scala.collection.Map[String, MethodImpl] = { + val result = mutable.Map.empty[String, MethodImpl] + for (parent <- reverseParentChain) + result ++= parent.methods + result + } + + /** BOTH PASSES. */ + @tailrec + final def lookupMethod(methodName: String): Option[MethodImpl] = { + methods.get(methodName) match { + case Some(impl) => Some(impl) + case none => + superClass match { + case Some(p) => p.lookupMethod(methodName) + case none => None + } + } + } + } + + /** Trait impl. */ + class TraitImpl(_encodedName: String) extends MethodContainer(_encodedName) { + def thisType: Type = NoType + } + + /** Thing from which a [[MethodImpl]] can unregister itself from. */ + trait Unregisterable { + /** UPDATE PASS ONLY. */ + def unregisterDependee(dependee: MethodImpl): Unit + } + + /** Type of a class or interface. + * Types are created on demand when a method is called on a given + * [[ClassType]]. + * + * Fully concurrency safe unless otherwise noted. + */ + abstract class InterfaceType(val encodedName: String) extends Unregisterable { + + override def toString(): String = + s"intf $encodedName" + + /** PROCESS PASS ONLY. Concurrency safe except with + * [[addInstantiatedSubclass]] and [[removeInstantiatedSubclass]] + */ + def instantiatedSubclasses: Iterable[Class] + + /** UPDATE PASS ONLY. Concurrency safe except with + * [[instantiatedSubclasses]] + */ + def addInstantiatedSubclass(x: Class): Unit + + /** UPDATE PASS ONLY. Concurrency safe except with + * [[instantiatedSubclasses]] + */ + def removeInstantiatedSubclass(x: Class): Unit + + /** PROCESS PASS ONLY. Concurrency safe except with [[ancestors_=]] */ + def ancestors: List[String] + + /** UPDATE PASS ONLY. Not concurrency safe. */ + def ancestors_=(v: List[String]): Unit + + /** PROCESS PASS ONLY. Concurrency safe except with [[ancestors_=]]. */ + def registerAskAncestors(asker: MethodImpl): Unit + + /** PROCESS PASS ONLY. */ + def registerDynamicCaller(methodName: String, caller: MethodImpl): Unit + + /** PROCESS PASS ONLY. */ + def registerStaticCaller(methodName: String, caller: MethodImpl): Unit + + /** UPDATE PASS ONLY. */ + def tagDynamicCallersOf(methodName: String): Unit + + /** UPDATE PASS ONLY. */ + def tagStaticCallersOf(methodName: String): Unit + } + + /** A method implementation. + * It must be concrete, and belong either to a [[Class]] or a [[TraitImpl]]. + * + * A single instance is **not** concurrency safe (unless otherwise noted in + * a method comment). However, the global state modifications are + * concurrency safe. + */ + abstract class MethodImpl(val owner: MethodContainer, + val encodedName: String) extends OptimizerCore.MethodImpl + with OptimizerCore.AbstractMethodID + with Unregisterable { + private[this] var _deleted: Boolean = false + + var optimizerHints: OptimizerHints = OptimizerHints.empty + var originalDef: MethodDef = _ + var desugaredDef: JSTree = _ + var preciseInfo: Infos.MethodInfo = _ + + def thisType: Type = owner.thisType + def deleted: Boolean = _deleted + + override def toString(): String = + s"$owner.$encodedName" + + /** PROCESS PASS ONLY. */ + def registerBodyAsker(asker: MethodImpl): Unit + + /** UPDATE PASS ONLY. */ + def tagBodyAskers(): Unit + + /** PROCESS PASS ONLY. */ + private def registerAskAncestors(intf: InterfaceType): Unit = { + intf.registerAskAncestors(this) + registeredTo(intf) + } + + /** PROCESS PASS ONLY. */ + private def registerDynamicCall(intf: InterfaceType, + methodName: String): Unit = { + intf.registerDynamicCaller(methodName, this) + registeredTo(intf) + } + + /** PROCESS PASS ONLY. */ + private def registerStaticCall(intf: InterfaceType, + methodName: String): Unit = { + intf.registerStaticCaller(methodName, this) + registeredTo(intf) + } + + /** PROCESS PASS ONLY. */ + def registerAskBody(target: MethodImpl): Unit = { + target.registerBodyAsker(this) + registeredTo(target) + } + + /** PROCESS PASS ONLY. */ + protected def registeredTo(intf: Unregisterable): Unit + + /** UPDATE PASS ONLY. */ + protected def unregisterFromEverywhere(): Unit + + /** Return true iff this is the first time this method is called since the + * last reset (via [[resetTag]]). + * UPDATE PASS ONLY. + */ + protected def protectTag(): Boolean + + /** PROCESS PASS ONLY. */ + protected def resetTag(): Unit + + /** Returns true if the method's attributes changed. + * Attributes are whether it is inlineable, and whether it is a trait + * impl forwarder. Basically this is what is declared in + * [[OptimizerCore.AbstractMethodID]]. + * In the process, tags all the body askers if the body changes. + * UPDATE PASS ONLY. Not concurrency safe on same instance. + */ + def updateWith(methodInfo: Analyzer#MethodInfo, + methodDef: MethodDef): Boolean = { + assert(!_deleted, "updateWith() called on a deleted method") + + val bodyChanged = { + originalDef == null || + (methodDef.hash zip originalDef.hash).forall { + case (h1, h2) => !Hashers.hashesEqual(h1, h2, considerPositions) + } + } + + if (bodyChanged) + tagBodyAskers() + + val hints = methodInfo.optimizerHints + val changed = hints != optimizerHints || bodyChanged + if (changed) { + val oldAttributes = (inlineable, isTraitImplForwarder) + + optimizerHints = hints + originalDef = methodDef + desugaredDef = null + preciseInfo = null + updateInlineable() + tag() + + val newAttributes = (inlineable, isTraitImplForwarder) + newAttributes != oldAttributes + } else { + false + } + } + + /** UPDATE PASS ONLY. Not concurrency safe on same instance. */ + def delete(): Unit = { + assert(!_deleted, "delete() called twice") + _deleted = true + if (protectTag()) + unregisterFromEverywhere() + } + + /** Concurrency safe with itself and [[delete]] on the same instance + * + * [[tag]] can be called concurrently with [[delete]] when methods in + * traits/classes are updated. + * + * UPDATE PASS ONLY. + */ + def tag(): Unit = if (protectTag()) { + scheduleMethod(this) + unregisterFromEverywhere() + } + + /** PROCESS PASS ONLY. */ + def process(): Unit = if (!_deleted) { + val (optimizedDef, info) = new Optimizer().optimize(thisType, originalDef) + desugaredDef = + if (owner.isInstanceOf[Class]) + classEmitter.genMethod(owner.encodedName, optimizedDef) + else + classEmitter.genTraitImplMethod(owner.encodedName, optimizedDef) + preciseInfo = info + resetTag() + } + + /** All methods are PROCESS PASS ONLY */ + private class Optimizer extends OptimizerCore(semantics) { + type MethodID = MethodImpl + + val myself: MethodImpl.this.type = MethodImpl.this + + protected def getMethodBody(method: MethodID): MethodDef = { + MethodImpl.this.registerAskBody(method) + method.originalDef + } + + protected def dynamicCall(intfName: String, + methodName: String): List[MethodID] = { + val intf = getInterface(intfName) + MethodImpl.this.registerDynamicCall(intf, methodName) + intf.instantiatedSubclasses.flatMap(_.lookupMethod(methodName)).toList + } + + protected def staticCall(className: String, + methodName: String): Option[MethodID] = { + val clazz = classes(className) + MethodImpl.this.registerStaticCall(clazz.myInterface, methodName) + clazz.lookupMethod(methodName) + } + + protected def traitImplCall(traitImplName: String, + methodName: String): Option[MethodID] = { + val traitImpl = traitImpls(traitImplName) + registerStaticCall(traitImpl.myInterface, methodName) + traitImpl.methods.get(methodName) + } + + protected def getAncestorsOf(intfName: String): List[String] = { + val intf = getInterface(intfName) + registerAskAncestors(intf) + intf.ancestors + } + + protected def hasElidableModuleAccessor(moduleClassName: String): Boolean = + classes(moduleClassName).hasElidableModuleAccessor + + protected def tryNewInlineableClass(className: String): Option[RecordValue] = + classes(className).tryNewInlineable + } + } + +} + +object GenIncOptimizer { + + private val isAdHocElidableModuleAccessor = + Set("s_Predef$") + + private[optimizer] def logTime[A](logger: Logger, + title: String)(body: => A): A = { + val startTime = System.nanoTime() + val result = body + val endTime = System.nanoTime() + val elapsedTime = endTime - startTime + logger.time(title, elapsedTime) + result + } + + private[optimizer] trait AbsCollOps { + type Map[K, V] <: mutable.Map[K, V] + type ParMap[K, V] <: GenMap[K, V] + type AccMap[K, V] + type ParIterable[V] <: GenIterableLike[V, ParIterable[V]] + type Addable[V] + + def emptyAccMap[K, V]: AccMap[K, V] + def emptyMap[K, V]: Map[K, V] + def emptyParMap[K, V]: ParMap[K, V] + def emptyParIterable[V]: ParIterable[V] + + // Operations on ParMap + def put[K, V](map: ParMap[K, V], k: K, v: V): Unit + def remove[K, V](map: ParMap[K, V], k: K): Option[V] + def retain[K, V](map: ParMap[K, V])(p: (K, V) => Boolean): Unit + + // Operations on AccMap + def acc[K, V](map: AccMap[K, V], k: K, v: V): Unit + def getAcc[K, V](map: AccMap[K, V], k: K): GenIterable[V] + def parFlatMapKeys[A, B](map: AccMap[A, _])( + f: A => GenTraversableOnce[B]): GenIterable[B] + + // Operations on ParIterable + def prepAdd[V](it: ParIterable[V]): Addable[V] + def add[V](addable: Addable[V], v: V): Unit + def finishAdd[V](addable: Addable[V]): ParIterable[V] + + } + +} |