summaryrefslogtreecommitdiff
path: root/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/OptimizerCore.scala
diff options
context:
space:
mode:
Diffstat (limited to 'examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/OptimizerCore.scala')
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/OptimizerCore.scala3572
1 files changed, 0 insertions, 3572 deletions
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/OptimizerCore.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/OptimizerCore.scala
deleted file mode 100644
index 364038b..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/OptimizerCore.scala
+++ /dev/null
@@ -1,3572 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ __ ____ Scala.js tools **
-** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
-** /____/\___/_/ |_/____/_/ | |__/ /____/ **
-** |/____/ **
-\* */
-
-
-package scala.scalajs.tools.optimizer
-
-import scala.language.implicitConversions
-
-import scala.annotation.{switch, tailrec}
-
-import scala.collection.mutable
-
-import scala.util.control.{NonFatal, ControlThrowable, TailCalls}
-import scala.util.control.TailCalls.{done => _, _} // done is a too generic term
-
-import scala.scalajs.ir._
-import Definitions.{ObjectClass, isConstructorName, isReflProxyName}
-import Infos.OptimizerHints
-import Trees._
-import Types._
-
-import scala.scalajs.tools.sem.Semantics
-import scala.scalajs.tools.javascript.LongImpl
-import scala.scalajs.tools.logging._
-
-/** Optimizer core.
- * Designed to be "mixed in" [[IncOptimizer#MethodImpl#Optimizer]].
- * This is the core of the optimizer. It contains all the smart things the
- * optimizer does. To perform inlining, it relies on abstract protected
- * methods to identify the target of calls.
- */
-private[optimizer] abstract class OptimizerCore(semantics: Semantics) {
- import OptimizerCore._
-
- type MethodID <: AbstractMethodID
-
- val myself: MethodID
-
- /** Returns the body of a method. */
- protected def getMethodBody(method: MethodID): MethodDef
-
- /** Returns the list of possible targets for a dynamically linked call. */
- protected def dynamicCall(intfName: String,
- methodName: String): List[MethodID]
-
- /** Returns the target of a static call. */
- protected def staticCall(className: String,
- methodName: String): Option[MethodID]
-
- /** Returns the target of a trait impl call. */
- protected def traitImplCall(traitImplName: String,
- methodName: String): Option[MethodID]
-
- /** Returns the list of ancestors of a class or interface. */
- protected def getAncestorsOf(encodedName: String): List[String]
-
- /** Tests whether the given module class has an elidable accessor.
- * In other words, whether it is safe to discard a LoadModule of that
- * module class which is not used.
- */
- protected def hasElidableModuleAccessor(moduleClassName: String): Boolean
-
- /** Tests whether the given class is inlineable.
- * @return None if the class is not inlineable, Some(value) if it is, where
- * value is a RecordValue with the initial value of its fields.
- */
- protected def tryNewInlineableClass(className: String): Option[RecordValue]
-
- private val usedLocalNames = mutable.Set.empty[String]
- private val usedLabelNames = mutable.Set.empty[String]
- private var statesInUse: List[State[_]] = Nil
-
- private var disableOptimisticOptimizations: Boolean = false
- private var rollbacksCount: Int = 0
-
- private val attemptedInlining = mutable.ListBuffer.empty[MethodID]
-
- private var curTrampolineId = 0
-
- def optimize(thisType: Type, originalDef: MethodDef): (MethodDef, Infos.MethodInfo) = {
- try {
- val MethodDef(name, params, resultType, body) = originalDef
- val (newParams, newBody) = try {
- transformIsolatedBody(Some(myself), thisType, params, resultType, body)
- } catch {
- case _: TooManyRollbacksException =>
- usedLocalNames.clear()
- usedLabelNames.clear()
- statesInUse = Nil
- disableOptimisticOptimizations = true
- transformIsolatedBody(Some(myself), thisType, params, resultType, body)
- }
- val m = MethodDef(name, newParams, resultType, newBody)(None)(originalDef.pos)
- val info = recreateInfo(m)
- (m, info)
- } catch {
- case NonFatal(cause) =>
- throw new OptimizeException(myself, attemptedInlining.distinct.toList, cause)
- case e: Throwable =>
- // This is a fatal exception. Don't wrap, just output debug info error
- Console.err.println(exceptionMsg(myself, attemptedInlining.distinct.toList))
- throw e
- }
- }
-
- private def withState[A, B](state: State[A])(body: => B): B = {
- statesInUse ::= state
- try body
- finally statesInUse = statesInUse.tail
- }
-
- private def freshLocalName(base: String): String =
- freshNameGeneric(usedLocalNames, base)
-
- private def freshLabelName(base: String): String =
- freshNameGeneric(usedLabelNames, base)
-
- private val isReserved = isKeyword ++ Seq("arguments", "eval", "ScalaJS")
-
- private def freshNameGeneric(usedNames: mutable.Set[String], base: String): String = {
- val result = if (!usedNames.contains(base) && !isReserved(base)) {
- base
- } else {
- var i = 1
- while (usedNames.contains(base + "$" + i))
- i += 1
- base + "$" + i
- }
- usedNames += result
- result
- }
-
- private def tryOrRollback(body: CancelFun => TailRec[Tree])(
- fallbackFun: () => TailRec[Tree]): TailRec[Tree] = {
- if (disableOptimisticOptimizations) {
- fallbackFun()
- } else {
- val trampolineId = curTrampolineId
- val savedUsedLocalNames = usedLocalNames.toSet
- val savedUsedLabelNames = usedLabelNames.toSet
- val savedStates = statesInUse.map(_.makeBackup())
-
- body { () =>
- throw new RollbackException(trampolineId, savedUsedLocalNames,
- savedUsedLabelNames, savedStates, fallbackFun)
- }
- }
- }
-
- private def isSubclass(lhs: String, rhs: String): Boolean =
- getAncestorsOf(lhs).contains(rhs)
-
- private val isSubclassFun = isSubclass _
- private def isSubtype(lhs: Type, rhs: Type): Boolean =
- Types.isSubtype(lhs, rhs)(isSubclassFun)
-
- /** Transforms a statement.
- *
- * For valid expression trees, it is always the case that
- * {{{
- * transformStat(tree)
- * ===
- * pretransformExpr(tree)(finishTransformStat)
- * }}}
- */
- private def transformStat(tree: Tree)(implicit scope: Scope): Tree =
- transform(tree, isStat = true)
-
- /** Transforms an expression.
- *
- * It is always the case that
- * {{{
- * transformExpr(tree)
- * ===
- * pretransformExpr(tree)(finishTransformExpr)
- * }}}
- */
- private def transformExpr(tree: Tree)(implicit scope: Scope): Tree =
- transform(tree, isStat = false)
-
- /** Transforms a tree. */
- private def transform(tree: Tree, isStat: Boolean)(
- implicit scope: Scope): Tree = {
-
- @inline implicit def pos = tree.pos
- val result = tree match {
- // Definitions
-
- case VarDef(_, _, _, rhs) =>
- /* A local var that is last (or alone) in its block is not terribly
- * useful. Get rid of it.
- * (Non-last VarDefs in blocks are handled in transformBlock.)
- */
- transformStat(rhs)
-
- // Control flow constructs
-
- case tree: Block =>
- transformBlock(tree, isStat)
-
- case Labeled(ident @ Ident(label, _), tpe, body) =>
- trampoline {
- returnable(label, if (isStat) NoType else tpe, body, isStat,
- usePreTransform = false)(finishTransform(isStat))
- }
-
- case Assign(lhs, rhs) =>
- val cont = { (preTransLhs: PreTransform) =>
- resolveLocalDef(preTransLhs) match {
- case PreTransRecordTree(lhsTree, lhsOrigType, lhsCancelFun) =>
- val recordType = lhsTree.tpe.asInstanceOf[RecordType]
- pretransformNoLocalDef(rhs) {
- case PreTransRecordTree(rhsTree, rhsOrigType, rhsCancelFun) =>
- if (rhsTree.tpe != recordType || rhsOrigType != lhsOrigType)
- lhsCancelFun()
- TailCalls.done(Assign(lhsTree, rhsTree))
- case _ =>
- lhsCancelFun()
- }
- case PreTransTree(lhsTree, _) =>
- TailCalls.done(Assign(lhsTree, transformExpr(rhs)))
- }
- }
- trampoline {
- lhs match {
- case lhs: Select =>
- pretransformSelectCommon(lhs, isLhsOfAssign = true)(cont)
- case _ =>
- pretransformExpr(lhs)(cont)
- }
- }
-
- case Return(expr, optLabel) =>
- val optInfo = optLabel match {
- case Some(Ident(label, _)) =>
- Some(scope.env.labelInfos(label))
- case None =>
- scope.env.labelInfos.get("")
- }
- optInfo.fold[Tree] {
- Return(transformExpr(expr), None)
- } { info =>
- val newOptLabel = Some(Ident(info.newName, None))
- if (!info.acceptRecords) {
- val newExpr = transformExpr(expr)
- info.returnedTypes.value ::= (newExpr.tpe, RefinedType(newExpr.tpe))
- Return(newExpr, newOptLabel)
- } else trampoline {
- pretransformNoLocalDef(expr) { texpr =>
- texpr match {
- case PreTransRecordTree(newExpr, origType, cancelFun) =>
- info.returnedTypes.value ::= (newExpr.tpe, origType)
- TailCalls.done(Return(newExpr, newOptLabel))
- case PreTransTree(newExpr, tpe) =>
- info.returnedTypes.value ::= (newExpr.tpe, tpe)
- TailCalls.done(Return(newExpr, newOptLabel))
- }
- }
- }
- }
-
- case If(cond, thenp, elsep) =>
- val newCond = transformExpr(cond)
- newCond match {
- case BooleanLiteral(condValue) =>
- if (condValue) transform(thenp, isStat)
- else transform(elsep, isStat)
- case _ =>
- val newThenp = transform(thenp, isStat)
- val newElsep = transform(elsep, isStat)
- val refinedType =
- constrainedLub(newThenp.tpe, newElsep.tpe, tree.tpe)
- foldIf(newCond, newThenp, newElsep)(refinedType)
- }
-
- case While(cond, body, optLabel) =>
- val newCond = transformExpr(cond)
- newCond match {
- case BooleanLiteral(false) => Skip()
- case _ =>
- optLabel match {
- case None =>
- While(newCond, transformStat(body), None)
-
- case Some(labelIdent @ Ident(label, _)) =>
- val newLabel = freshLabelName(label)
- val info = new LabelInfo(newLabel, acceptRecords = false)
- While(newCond, {
- val bodyScope = scope.withEnv(
- scope.env.withLabelInfo(label, info))
- transformStat(body)(bodyScope)
- }, Some(Ident(newLabel, None)(labelIdent.pos)))
- }
- }
-
- case DoWhile(body, cond, None) =>
- val newBody = transformStat(body)
- val newCond = transformExpr(cond)
- newCond match {
- case BooleanLiteral(false) => newBody
- case _ => DoWhile(newBody, newCond, None)
- }
-
- case Try(block, errVar, EmptyTree, finalizer) =>
- val newBlock = transform(block, isStat)
- val newFinalizer = transformStat(finalizer)
- Try(newBlock, errVar, EmptyTree, newFinalizer)(newBlock.tpe)
-
- case Try(block, errVar @ Ident(name, originalName), handler, finalizer) =>
- val newBlock = transform(block, isStat)
-
- val newName = freshLocalName(name)
- val newOriginalName = originalName.orElse(Some(name))
- val localDef = LocalDef(RefinedType(AnyType), true,
- ReplaceWithVarRef(newName, newOriginalName, new SimpleState(true), None))
- val newHandler = {
- val handlerScope = scope.withEnv(scope.env.withLocalDef(name, localDef))
- transform(handler, isStat)(handlerScope)
- }
-
- val newFinalizer = transformStat(finalizer)
-
- val refinedType = constrainedLub(newBlock.tpe, newHandler.tpe, tree.tpe)
- Try(newBlock, Ident(newName, newOriginalName)(errVar.pos),
- newHandler, newFinalizer)(refinedType)
-
- case Throw(expr) =>
- Throw(transformExpr(expr))
-
- case Continue(optLabel) =>
- val newOptLabel = optLabel map { label =>
- Ident(scope.env.labelInfos(label.name).newName, None)(label.pos)
- }
- Continue(newOptLabel)
-
- case Match(selector, cases, default) =>
- val newSelector = transformExpr(selector)
- newSelector match {
- case newSelector: Literal =>
- val body = cases collectFirst {
- case (alts, body) if alts.exists(literal_===(_, newSelector)) => body
- } getOrElse default
- transform(body, isStat)
- case _ =>
- Match(newSelector,
- cases map (c => (c._1, transform(c._2, isStat))),
- transform(default, isStat))(tree.tpe)
- }
-
- // Scala expressions
-
- case New(cls, ctor, args) =>
- New(cls, ctor, args map transformExpr)
-
- case StoreModule(cls, value) =>
- StoreModule(cls, transformExpr(value))
-
- case tree: Select =>
- trampoline {
- pretransformSelectCommon(tree, isLhsOfAssign = false)(
- finishTransform(isStat = false))
- }
-
- case tree: Apply =>
- trampoline {
- pretransformApply(tree, isStat, usePreTransform = false)(
- finishTransform(isStat))
- }
-
- case tree: StaticApply =>
- trampoline {
- pretransformStaticApply(tree, isStat, usePreTransform = false)(
- finishTransform(isStat))
- }
-
- case tree: TraitImplApply =>
- trampoline {
- pretransformTraitImplApply(tree, isStat, usePreTransform = false)(
- finishTransform(isStat))
- }
-
- case tree @ UnaryOp(_, arg) =>
- if (isStat) transformStat(arg)
- else transformUnaryOp(tree)
-
- case tree @ BinaryOp(op, lhs, rhs) =>
- if (isStat) Block(transformStat(lhs), transformStat(rhs))
- else transformBinaryOp(tree)
-
- case NewArray(tpe, lengths) =>
- NewArray(tpe, lengths map transformExpr)
-
- case ArrayValue(tpe, elems) =>
- ArrayValue(tpe, elems map transformExpr)
-
- case ArrayLength(array) =>
- ArrayLength(transformExpr(array))
-
- case ArraySelect(array, index) =>
- ArraySelect(transformExpr(array), transformExpr(index))(tree.tpe)
-
- case RecordValue(tpe, elems) =>
- RecordValue(tpe, elems map transformExpr)
-
- case IsInstanceOf(expr, ClassType(ObjectClass)) =>
- transformExpr(BinaryOp(BinaryOp.!==, expr, Null()))
-
- case IsInstanceOf(expr, tpe) =>
- trampoline {
- pretransformExpr(expr) { texpr =>
- val result = {
- if (isSubtype(texpr.tpe.base, tpe)) {
- if (texpr.tpe.isNullable)
- BinaryOp(BinaryOp.!==, finishTransformExpr(texpr), Null())
- else
- Block(finishTransformStat(texpr), BooleanLiteral(true))
- } else {
- if (texpr.tpe.isExact)
- Block(finishTransformStat(texpr), BooleanLiteral(false))
- else
- IsInstanceOf(finishTransformExpr(texpr), tpe)
- }
- }
- TailCalls.done(result)
- }
- }
-
- case AsInstanceOf(expr, ClassType(ObjectClass)) =>
- transformExpr(expr)
-
- case AsInstanceOf(expr, cls) =>
- trampoline {
- pretransformExpr(tree)(finishTransform(isStat))
- }
-
- case Unbox(arg, charCode) =>
- trampoline {
- pretransformExpr(arg) { targ =>
- foldUnbox(targ, charCode)(finishTransform(isStat))
- }
- }
-
- case GetClass(expr) =>
- GetClass(transformExpr(expr))
-
- // JavaScript expressions
-
- case JSNew(ctor, args) =>
- JSNew(transformExpr(ctor), args map transformExpr)
-
- case JSDotSelect(qualifier, item) =>
- JSDotSelect(transformExpr(qualifier), item)
-
- case JSBracketSelect(qualifier, item) =>
- JSBracketSelect(transformExpr(qualifier), transformExpr(item))
-
- case tree: JSFunctionApply =>
- trampoline {
- pretransformJSFunctionApply(tree, isStat, usePreTransform = false)(
- finishTransform(isStat))
- }
-
- case JSDotMethodApply(receiver, method, args) =>
- JSDotMethodApply(transformExpr(receiver), method,
- args map transformExpr)
-
- case JSBracketMethodApply(receiver, method, args) =>
- JSBracketMethodApply(transformExpr(receiver), transformExpr(method),
- args map transformExpr)
-
- case JSDelete(JSDotSelect(obj, prop)) =>
- JSDelete(JSDotSelect(transformExpr(obj), prop))
-
- case JSDelete(JSBracketSelect(obj, prop)) =>
- JSDelete(JSBracketSelect(transformExpr(obj), transformExpr(prop)))
-
- case JSUnaryOp(op, lhs) =>
- JSUnaryOp(op, transformExpr(lhs))
-
- case JSBinaryOp(op, lhs, rhs) =>
- JSBinaryOp(op, transformExpr(lhs), transformExpr(rhs))
-
- case JSArrayConstr(items) =>
- JSArrayConstr(items map transformExpr)
-
- case JSObjectConstr(fields) =>
- JSObjectConstr(fields map {
- case (name, value) => (name, transformExpr(value))
- })
-
- // Atomic expressions
-
- case _:VarRef | _:This =>
- trampoline {
- pretransformExpr(tree)(finishTransform(isStat))
- }
-
- case Closure(captureParams, params, body, captureValues) =>
- transformClosureCommon(captureParams, params, body,
- captureValues.map(transformExpr))
-
- // Trees that need not be transformed
-
- case _:Skip | _:Debugger | _:LoadModule |
- _:JSEnvInfo | _:Literal | EmptyTree =>
- tree
- }
-
- if (isStat) keepOnlySideEffects(result)
- else result
- }
-
- private def transformClosureCommon(captureParams: List[ParamDef],
- params: List[ParamDef], body: Tree, newCaptureValues: List[Tree])(
- implicit pos: Position): Closure = {
-
- val (allNewParams, newBody) =
- transformIsolatedBody(None, AnyType, captureParams ++ params, AnyType, body)
- val (newCaptureParams, newParams) =
- allNewParams.splitAt(captureParams.size)
-
- Closure(newCaptureParams, newParams, newBody, newCaptureValues)
- }
-
- private def transformBlock(tree: Block, isStat: Boolean)(
- implicit scope: Scope): Tree = {
- def transformList(stats: List[Tree])(
- implicit scope: Scope): Tree = stats match {
- case last :: Nil =>
- transform(last, isStat)
-
- case (VarDef(Ident(name, originalName), vtpe, mutable, rhs)) :: rest =>
- trampoline {
- pretransformExpr(rhs) { trhs =>
- withBinding(Binding(name, originalName, vtpe, mutable, trhs)) {
- (restScope, cont1) =>
- val newRest = transformList(rest)(restScope)
- cont1(PreTransTree(newRest, RefinedType(newRest.tpe)))
- } (finishTransform(isStat))
- }
- }
-
- case stat :: rest =>
- val transformedStat = transformStat(stat)
- if (transformedStat.tpe == NothingType) transformedStat
- else Block(transformedStat, transformList(rest))(stat.pos)
-
- case Nil => // silence the exhaustivity warning in a sensible way
- Skip()(tree.pos)
- }
- transformList(tree.stats)(scope)
- }
-
- /** Pretransforms a list of trees as a list of [[PreTransform]]s.
- * This is a convenience method to use pretransformExpr on a list.
- */
- private def pretransformExprs(trees: List[Tree])(
- cont: List[PreTransform] => TailRec[Tree])(
- implicit scope: Scope): TailRec[Tree] = {
- trees match {
- case first :: rest =>
- pretransformExpr(first) { tfirst =>
- pretransformExprs(rest) { trest =>
- cont(tfirst :: trest)
- }
- }
-
- case Nil =>
- cont(Nil)
- }
- }
-
- /** Pretransforms two trees as a pair of [[PreTransform]]s.
- * This is a convenience method to use pretransformExpr on two trees.
- */
- private def pretransformExprs(tree1: Tree, tree2: Tree)(
- cont: (PreTransform, PreTransform) => TailRec[Tree])(
- implicit scope: Scope): TailRec[Tree] = {
- pretransformExpr(tree1) { ttree1 =>
- pretransformExpr(tree2) { ttree2 =>
- cont(ttree1, ttree2)
- }
- }
- }
-
- /** Pretransforms a tree and a list of trees as [[PreTransform]]s.
- * This is a convenience method to use pretransformExpr.
- */
- private def pretransformExprs(first: Tree, rest: List[Tree])(
- cont: (PreTransform, List[PreTransform]) => TailRec[Tree])(
- implicit scope: Scope): TailRec[Tree] = {
- pretransformExpr(first) { tfirst =>
- pretransformExprs(rest) { trest =>
- cont(tfirst, trest)
- }
- }
- }
-
- /** Pretransforms a tree to get a refined type while avoiding to force
- * things we might be able to optimize by folding and aliasing.
- */
- private def pretransformExpr(tree: Tree)(cont: PreTransCont)(
- implicit scope: Scope): TailRec[Tree] = tailcall {
- @inline implicit def pos = tree.pos
-
- tree match {
- case tree: Block =>
- pretransformBlock(tree)(cont)
-
- case VarRef(Ident(name, _), _) =>
- val localDef = scope.env.localDefs.getOrElse(name,
- sys.error(s"Cannot find local def '$name' at $pos\n" +
- s"While optimizing $myself\n" +
- s"Env is ${scope.env}\nInlining ${scope.implsBeingInlined}"))
- cont(PreTransLocalDef(localDef))
-
- case This() =>
- val localDef = scope.env.localDefs.getOrElse("this",
- sys.error(s"Found invalid 'this' at $pos\n" +
- s"While optimizing $myself\n" +
- s"Env is ${scope.env}\nInlining ${scope.implsBeingInlined}"))
- cont(PreTransLocalDef(localDef))
-
- case If(cond, thenp, elsep) =>
- val newCond = transformExpr(cond)
- newCond match {
- case BooleanLiteral(condValue) =>
- if (condValue) pretransformExpr(thenp)(cont)
- else pretransformExpr(elsep)(cont)
- case _ =>
- tryOrRollback { cancelFun =>
- pretransformNoLocalDef(thenp) { tthenp =>
- pretransformNoLocalDef(elsep) { telsep =>
- (tthenp, telsep) match {
- case (PreTransRecordTree(thenTree, thenOrigType, thenCancelFun),
- PreTransRecordTree(elseTree, elseOrigType, elseCancelFun)) =>
- val commonType =
- if (thenTree.tpe == elseTree.tpe &&
- thenOrigType == elseOrigType) thenTree.tpe
- else cancelFun()
- val refinedOrigType =
- constrainedLub(thenOrigType, elseOrigType, tree.tpe)
- cont(PreTransRecordTree(
- If(newCond, thenTree, elseTree)(commonType),
- refinedOrigType,
- cancelFun))
-
- case (PreTransRecordTree(thenTree, thenOrigType, thenCancelFun), _)
- if telsep.tpe.isNothingType =>
- cont(PreTransRecordTree(
- If(newCond, thenTree, finishTransformExpr(telsep))(thenTree.tpe),
- thenOrigType,
- thenCancelFun))
-
- case (_, PreTransRecordTree(elseTree, elseOrigType, elseCancelFun))
- if tthenp.tpe.isNothingType =>
- cont(PreTransRecordTree(
- If(newCond, finishTransformExpr(tthenp), elseTree)(elseTree.tpe),
- elseOrigType,
- elseCancelFun))
-
- case _ =>
- val newThenp = finishTransformExpr(tthenp)
- val newElsep = finishTransformExpr(telsep)
- val refinedType =
- constrainedLub(newThenp.tpe, newElsep.tpe, tree.tpe)
- cont(PreTransTree(
- foldIf(newCond, newThenp, newElsep)(refinedType)))
- }
- }
- }
- } { () =>
- val newThenp = transformExpr(thenp)
- val newElsep = transformExpr(elsep)
- val refinedType =
- constrainedLub(newThenp.tpe, newElsep.tpe, tree.tpe)
- cont(PreTransTree(
- foldIf(newCond, newThenp, newElsep)(refinedType)))
- }
- }
-
- case Match(selector, cases, default) =>
- val newSelector = transformExpr(selector)
- newSelector match {
- case newSelector: Literal =>
- val body = cases collectFirst {
- case (alts, body) if alts.exists(literal_===(_, newSelector)) => body
- } getOrElse default
- pretransformExpr(body)(cont)
- case _ =>
- cont(PreTransTree(Match(newSelector,
- cases map (c => (c._1, transformExpr(c._2))),
- transformExpr(default))(tree.tpe)))
- }
-
- case Labeled(ident @ Ident(label, _), tpe, body) =>
- returnable(label, tpe, body, isStat = false, usePreTransform = true)(cont)
-
- case New(cls @ ClassType(className), ctor, args) =>
- tryNewInlineableClass(className) match {
- case Some(initialValue) =>
- pretransformExprs(args) { targs =>
- tryOrRollback { cancelFun =>
- inlineClassConstructor(
- new AllocationSite(tree),
- cls, initialValue, ctor, targs, cancelFun)(cont)
- } { () =>
- cont(PreTransTree(
- New(cls, ctor, targs.map(finishTransformExpr)),
- RefinedType(cls, isExact = true, isNullable = false)))
- }
- }
- case None =>
- cont(PreTransTree(
- New(cls, ctor, args.map(transformExpr)),
- RefinedType(cls, isExact = true, isNullable = false)))
- }
-
- case tree: Select =>
- pretransformSelectCommon(tree, isLhsOfAssign = false)(cont)
-
- case tree: Apply =>
- pretransformApply(tree, isStat = false,
- usePreTransform = true)(cont)
-
- case tree: StaticApply =>
- pretransformStaticApply(tree, isStat = false,
- usePreTransform = true)(cont)
-
- case tree: TraitImplApply =>
- pretransformTraitImplApply(tree, isStat = false,
- usePreTransform = true)(cont)
-
- case tree: JSFunctionApply =>
- pretransformJSFunctionApply(tree, isStat = false,
- usePreTransform = true)(cont)
-
- case AsInstanceOf(expr, tpe) =>
- pretransformExpr(expr) { texpr =>
- tpe match {
- case ClassType(ObjectClass) =>
- cont(texpr)
- case _ =>
- if (isSubtype(texpr.tpe.base, tpe)) {
- cont(texpr)
- } else {
- cont(PreTransTree(
- AsInstanceOf(finishTransformExpr(texpr), tpe)))
- }
- }
- }
-
- case Closure(captureParams, params, body, captureValues) =>
- pretransformExprs(captureValues) { tcaptureValues =>
- tryOrRollback { cancelFun =>
- val captureBindings = for {
- (ParamDef(Ident(name, origName), tpe, mutable), value) <-
- captureParams zip tcaptureValues
- } yield {
- Binding(name, origName, tpe, mutable, value)
- }
- withNewLocalDefs(captureBindings) { (captureLocalDefs, cont1) =>
- val alreadyUsedState = new SimpleState[Boolean](false)
- withState(alreadyUsedState) {
- val replacement = TentativeClosureReplacement(
- captureParams, params, body, captureLocalDefs,
- alreadyUsedState, cancelFun)
- val localDef = LocalDef(
- RefinedType(AnyType, isExact = false, isNullable = false),
- mutable = false,
- replacement)
- cont1(PreTransLocalDef(localDef))
- }
- } (cont)
- } { () =>
- val newClosure = transformClosureCommon(captureParams, params, body,
- tcaptureValues.map(finishTransformExpr))
- cont(PreTransTree(
- newClosure,
- RefinedType(AnyType, isExact = false, isNullable = false)))
- }
- }
-
- case _ =>
- val result = transformExpr(tree)
- cont(PreTransTree(result, RefinedType(result.tpe)))
- }
- }
-
- private def pretransformBlock(tree: Block)(
- cont: PreTransCont)(
- implicit scope: Scope): TailRec[Tree] = {
- def pretransformList(stats: List[Tree])(
- cont: PreTransCont)(
- implicit scope: Scope): TailRec[Tree] = stats match {
- case last :: Nil =>
- pretransformExpr(last)(cont)
-
- case (VarDef(Ident(name, originalName), vtpe, mutable, rhs)) :: rest =>
- pretransformExpr(rhs) { trhs =>
- withBinding(Binding(name, originalName, vtpe, mutable, trhs)) {
- (restScope, cont1) =>
- pretransformList(rest)(cont1)(restScope)
- } (cont)
- }
-
- case stat :: rest =>
- implicit val pos = tree.pos
- val transformedStat = transformStat(stat)
- transformedStat match {
- case Skip() =>
- pretransformList(rest)(cont)
- case _ =>
- if (transformedStat.tpe == NothingType)
- cont(PreTransTree(transformedStat, RefinedType.Nothing))
- else {
- pretransformList(rest) { trest =>
- cont(PreTransBlock(transformedStat :: Nil, trest))
- }
- }
- }
-
- case Nil => // silence the exhaustivity warning in a sensible way
- TailCalls.done(Skip()(tree.pos))
- }
- pretransformList(tree.stats)(cont)(scope)
- }
-
- private def pretransformSelectCommon(tree: Select, isLhsOfAssign: Boolean)(
- cont: PreTransCont)(
- implicit scope: Scope): TailRec[Tree] = {
- val Select(qualifier, item, mutable) = tree
- pretransformExpr(qualifier) { preTransQual =>
- pretransformSelectCommon(tree.tpe, preTransQual, item, mutable,
- isLhsOfAssign)(cont)(scope, tree.pos)
- }
- }
-
- private def pretransformSelectCommon(expectedType: Type,
- preTransQual: PreTransform, item: Ident, mutable: Boolean,
- isLhsOfAssign: Boolean)(
- cont: PreTransCont)(
- implicit scope: Scope, pos: Position): TailRec[Tree] = {
- preTransQual match {
- case PreTransLocalDef(LocalDef(_, _,
- InlineClassBeingConstructedReplacement(fieldLocalDefs, cancelFun))) =>
- val fieldLocalDef = fieldLocalDefs(item.name)
- if (!isLhsOfAssign || fieldLocalDef.mutable) {
- cont(PreTransLocalDef(fieldLocalDef))
- } else {
- /* This is an assignment to an immutable field of a inlineable class
- * being constructed, but that does not appear at the "top-level" of
- * one of its constructors. We cannot handle those, so we cancel.
- * (Assignments at the top-level are normal initializations of these
- * fields, and are transformed as vals in inlineClassConstructor.)
- */
- cancelFun()
- }
- case PreTransLocalDef(LocalDef(_, _,
- InlineClassInstanceReplacement(_, fieldLocalDefs, cancelFun))) =>
- val fieldLocalDef = fieldLocalDefs(item.name)
- if (!isLhsOfAssign || fieldLocalDef.mutable) {
- cont(PreTransLocalDef(fieldLocalDef))
- } else {
- /* In an ideal world, this should not happen (assigning to an
- * immutable field of an already constructed object). However, since
- * we cannot IR-check that this does not happen (see #1021), this is
- * effectively allowed by the IR spec. We are therefore not allowed
- * to crash. We cancel instead. This will become an actual field
- * (rather than an optimized local val) which is not considered pure
- * (for that same reason).
- */
- cancelFun()
- }
- case _ =>
- resolveLocalDef(preTransQual) match {
- case PreTransRecordTree(newQual, origType, cancelFun) =>
- val recordType = newQual.tpe.asInstanceOf[RecordType]
- val field = recordType.findField(item.name)
- val sel = Select(newQual, item, mutable)(field.tpe)
- sel.tpe match {
- case _: RecordType =>
- cont(PreTransRecordTree(sel, RefinedType(expectedType), cancelFun))
- case _ =>
- cont(PreTransTree(sel, RefinedType(sel.tpe)))
- }
-
- case PreTransTree(newQual, _) =>
- cont(PreTransTree(Select(newQual, item, mutable)(expectedType),
- RefinedType(expectedType)))
- }
- }
- }
-
- /** Resolves any LocalDef in a [[PreTransform]]. */
- private def resolveLocalDef(preTrans: PreTransform): PreTransGenTree = {
- implicit val pos = preTrans.pos
- preTrans match {
- case PreTransBlock(stats, result) =>
- resolveLocalDef(result) match {
- case PreTransRecordTree(tree, tpe, cancelFun) =>
- PreTransRecordTree(Block(stats :+ tree), tpe, cancelFun)
- case PreTransTree(tree, tpe) =>
- PreTransTree(Block(stats :+ tree), tpe)
- }
-
- case PreTransLocalDef(localDef @ LocalDef(tpe, mutable, replacement)) =>
- replacement match {
- case ReplaceWithRecordVarRef(name, originalName,
- recordType, used, cancelFun) =>
- used.value = true
- PreTransRecordTree(
- VarRef(Ident(name, originalName), mutable)(recordType),
- tpe, cancelFun)
-
- case InlineClassInstanceReplacement(recordType, fieldLocalDefs, cancelFun) =>
- if (!isImmutableType(recordType))
- cancelFun()
- PreTransRecordTree(
- RecordValue(recordType, recordType.fields.map(
- f => fieldLocalDefs(f.name).newReplacement)),
- tpe, cancelFun)
-
- case _ =>
- PreTransTree(localDef.newReplacement, localDef.tpe)
- }
-
- case preTrans: PreTransGenTree =>
- preTrans
- }
- }
-
- /** Combines pretransformExpr and resolveLocalDef in one convenience method. */
- private def pretransformNoLocalDef(tree: Tree)(
- cont: PreTransGenTree => TailRec[Tree])(
- implicit scope: Scope): TailRec[Tree] = {
- pretransformExpr(tree) { ttree =>
- cont(resolveLocalDef(ttree))
- }
- }
-
- /** Finishes a pretransform, either a statement or an expression. */
- private def finishTransform(isStat: Boolean): PreTransCont = { preTrans =>
- TailCalls.done {
- if (isStat) finishTransformStat(preTrans)
- else finishTransformExpr(preTrans)
- }
- }
-
- /** Finishes an expression pretransform to get a normal [[Tree]].
- * This method (together with finishTransformStat) must not be called more
- * than once per pretransform and per translation.
- * By "per translation", we mean in an alternative path through
- * `tryOrRollback`. It could still be called several times as long as
- * it is once in the 'try' part and once in the 'fallback' part.
- */
- private def finishTransformExpr(preTrans: PreTransform): Tree = {
- implicit val pos = preTrans.pos
- preTrans match {
- case PreTransBlock(stats, result) =>
- Block(stats :+ finishTransformExpr(result))
- case PreTransLocalDef(localDef) =>
- localDef.newReplacement
- case PreTransRecordTree(_, _, cancelFun) =>
- cancelFun()
- case PreTransTree(tree, _) =>
- tree
- }
- }
-
- /** Finishes a statement pretransform to get a normal [[Tree]].
- * This method (together with finishTransformExpr) must not be called more
- * than once per pretransform and per translation.
- * By "per translation", we mean in an alternative path through
- * `tryOrRollback`. It could still be called several times as long as
- * it is once in the 'try' part and once in the 'fallback' part.
- */
- private def finishTransformStat(stat: PreTransform): Tree = stat match {
- case PreTransBlock(stats, result) =>
- Block(stats :+ finishTransformStat(result))(stat.pos)
- case PreTransLocalDef(_) =>
- Skip()(stat.pos)
- case PreTransRecordTree(tree, _, _) =>
- keepOnlySideEffects(tree)
- case PreTransTree(tree, _) =>
- keepOnlySideEffects(tree)
- }
-
- /** Keeps only the side effects of a Tree (overapproximation). */
- private def keepOnlySideEffects(stat: Tree): Tree = stat match {
- case _:VarRef | _:This | _:Literal =>
- Skip()(stat.pos)
- case Block(init :+ last) =>
- Block(init :+ keepOnlySideEffects(last))(stat.pos)
- case LoadModule(ClassType(moduleClassName)) =>
- if (hasElidableModuleAccessor(moduleClassName)) Skip()(stat.pos)
- else stat
- case Select(LoadModule(ClassType(moduleClassName)), _, _) =>
- if (hasElidableModuleAccessor(moduleClassName)) Skip()(stat.pos)
- else stat
- case Closure(_, _, _, captureValues) =>
- Block(captureValues.map(keepOnlySideEffects))(stat.pos)
- case UnaryOp(_, arg) =>
- keepOnlySideEffects(arg)
- case If(cond, thenp, elsep) =>
- (keepOnlySideEffects(thenp), keepOnlySideEffects(elsep)) match {
- case (Skip(), Skip()) => keepOnlySideEffects(cond)
- case (newThenp, newElsep) => If(cond, newThenp, newElsep)(NoType)(stat.pos)
- }
- case BinaryOp(_, lhs, rhs) =>
- Block(keepOnlySideEffects(lhs), keepOnlySideEffects(rhs))(stat.pos)
- case RecordValue(_, elems) =>
- Block(elems.map(keepOnlySideEffects))(stat.pos)
- case _ =>
- stat
- }
-
- private def pretransformApply(tree: Apply, isStat: Boolean,
- usePreTransform: Boolean)(
- cont: PreTransCont)(
- implicit scope: Scope): TailRec[Tree] = {
- val Apply(receiver, methodIdent @ Ident(methodName, _), args) = tree
- implicit val pos = tree.pos
-
- pretransformExpr(receiver) { treceiver =>
- def treeNotInlined0(transformedArgs: List[Tree]) =
- cont(PreTransTree(Apply(finishTransformExpr(treceiver), methodIdent,
- transformedArgs)(tree.tpe)(tree.pos), RefinedType(tree.tpe)))
-
- def treeNotInlined = treeNotInlined0(args.map(transformExpr))
-
- treceiver.tpe.base match {
- case NothingType =>
- cont(treceiver)
- case NullType =>
- cont(PreTransTree(Block(
- finishTransformStat(treceiver),
- CallHelper("throwNullPointerException")(NothingType))))
- case _ =>
- if (isReflProxyName(methodName)) {
- // Never inline reflective proxies
- treeNotInlined
- } else {
- val cls = boxedClassForType(treceiver.tpe.base)
- val impls =
- if (treceiver.tpe.isExact) staticCall(cls, methodName).toList
- else dynamicCall(cls, methodName)
- val allocationSite = treceiver.tpe.allocationSite
- if (impls.isEmpty || impls.exists(impl =>
- scope.implsBeingInlined((allocationSite, impl)))) {
- // isEmpty could happen, have to leave it as is for the TypeError
- treeNotInlined
- } else if (impls.size == 1) {
- val target = impls.head
- pretransformExprs(args) { targs =>
- val intrinsicCode = getIntrinsicCode(target)
- if (intrinsicCode >= 0) {
- callIntrinsic(intrinsicCode, Some(treceiver), targs,
- isStat, usePreTransform)(cont)
- } else if (target.inlineable || shouldInlineBecauseOfArgs(treceiver :: targs)) {
- inline(allocationSite, Some(treceiver), targs, target,
- isStat, usePreTransform)(cont)
- } else {
- treeNotInlined0(targs.map(finishTransformExpr))
- }
- }
- } else {
- if (impls.forall(_.isTraitImplForwarder)) {
- val reference = impls.head
- val TraitImplApply(ClassType(traitImpl), Ident(methodName, _), _) =
- getMethodBody(reference).body
- if (!impls.tail.forall(getMethodBody(_).body match {
- case TraitImplApply(ClassType(`traitImpl`),
- Ident(`methodName`, _), _) => true
- case _ => false
- })) {
- // Not all calling the same method in the same trait impl
- treeNotInlined
- } else {
- pretransformExprs(args) { targs =>
- inline(allocationSite, Some(treceiver), targs, reference,
- isStat, usePreTransform)(cont)
- }
- }
- } else {
- // TODO? Inline multiple non-trait-impl-forwarder with the exact same body?
- treeNotInlined
- }
- }
- }
- }
- }
- }
-
- private def boxedClassForType(tpe: Type): String = (tpe: @unchecked) match {
- case ClassType(cls) => cls
- case AnyType => Definitions.ObjectClass
- case UndefType => Definitions.BoxedUnitClass
- case BooleanType => Definitions.BoxedBooleanClass
- case IntType => Definitions.BoxedIntegerClass
- case LongType => Definitions.BoxedLongClass
- case FloatType => Definitions.BoxedFloatClass
- case DoubleType => Definitions.BoxedDoubleClass
- case StringType => Definitions.StringClass
- case ArrayType(_, _) => Definitions.ObjectClass
- }
-
- private def pretransformStaticApply(tree: StaticApply, isStat: Boolean,
- usePreTransform: Boolean)(
- cont: PreTransCont)(
- implicit scope: Scope): TailRec[Tree] = {
- val StaticApply(receiver, clsType @ ClassType(cls),
- methodIdent @ Ident(methodName, _), args) = tree
- implicit val pos = tree.pos
-
- def treeNotInlined0(transformedReceiver: Tree, transformedArgs: List[Tree]) =
- cont(PreTransTree(StaticApply(transformedReceiver, clsType,
- methodIdent, transformedArgs)(tree.tpe), RefinedType(tree.tpe)))
-
- def treeNotInlined =
- treeNotInlined0(transformExpr(receiver), args.map(transformExpr))
-
- if (isReflProxyName(methodName)) {
- // Never inline reflective proxies
- treeNotInlined
- } else {
- val optTarget = staticCall(cls, methodName)
- if (optTarget.isEmpty) {
- // just in case
- treeNotInlined
- } else {
- val target = optTarget.get
- pretransformExprs(receiver, args) { (treceiver, targs) =>
- val intrinsicCode = getIntrinsicCode(target)
- if (intrinsicCode >= 0) {
- callIntrinsic(intrinsicCode, Some(treceiver), targs,
- isStat, usePreTransform)(cont)
- } else {
- val shouldInline =
- target.inlineable || shouldInlineBecauseOfArgs(treceiver :: targs)
- val allocationSite = treceiver.tpe.allocationSite
- val beingInlined =
- scope.implsBeingInlined((allocationSite, target))
-
- if (shouldInline && !beingInlined) {
- inline(allocationSite, Some(treceiver), targs, target,
- isStat, usePreTransform)(cont)
- } else {
- treeNotInlined0(finishTransformExpr(treceiver),
- targs.map(finishTransformExpr))
- }
- }
- }
- }
- }
- }
-
- private def pretransformTraitImplApply(tree: TraitImplApply, isStat: Boolean,
- usePreTransform: Boolean)(
- cont: PreTransCont)(
- implicit scope: Scope): TailRec[Tree] = {
- val TraitImplApply(implType @ ClassType(impl),
- methodIdent @ Ident(methodName, _), args) = tree
- implicit val pos = tree.pos
-
- def treeNotInlined0(transformedArgs: List[Tree]) =
- cont(PreTransTree(TraitImplApply(implType, methodIdent,
- transformedArgs)(tree.tpe), RefinedType(tree.tpe)))
-
- def treeNotInlined = treeNotInlined0(args.map(transformExpr))
-
- val optTarget = traitImplCall(impl, methodName)
- if (optTarget.isEmpty) {
- // just in case
- treeNotInlined
- } else {
- val target = optTarget.get
- pretransformExprs(args) { targs =>
- val intrinsicCode = getIntrinsicCode(target)
- if (intrinsicCode >= 0) {
- callIntrinsic(intrinsicCode, None, targs,
- isStat, usePreTransform)(cont)
- } else {
- val shouldInline =
- target.inlineable || shouldInlineBecauseOfArgs(targs)
- val allocationSite = targs.headOption.flatMap(_.tpe.allocationSite)
- val beingInlined =
- scope.implsBeingInlined((allocationSite, target))
-
- if (shouldInline && !beingInlined) {
- inline(allocationSite, None, targs, target,
- isStat, usePreTransform)(cont)
- } else {
- treeNotInlined0(targs.map(finishTransformExpr))
- }
- }
- }
- }
- }
-
- private def pretransformJSFunctionApply(tree: JSFunctionApply,
- isStat: Boolean, usePreTransform: Boolean)(
- cont: PreTransCont)(
- implicit scope: Scope, pos: Position): TailRec[Tree] = {
- val JSFunctionApply(fun, args) = tree
- implicit val pos = tree.pos
-
- pretransformExpr(fun) { tfun =>
- tfun match {
- case PreTransLocalDef(LocalDef(_, false,
- closure @ TentativeClosureReplacement(
- captureParams, params, body, captureLocalDefs,
- alreadyUsed, cancelFun))) if !alreadyUsed.value =>
- alreadyUsed.value = true
- pretransformExprs(args) { targs =>
- inlineBody(
- Some(PreTransTree(Undefined())), // `this` is `undefined`
- captureParams ++ params, AnyType, body,
- captureLocalDefs.map(PreTransLocalDef(_)) ++ targs, isStat,
- usePreTransform)(cont)
- }
-
- case _ =>
- cont(PreTransTree(
- JSFunctionApply(finishTransformExpr(tfun), args.map(transformExpr))))
- }
- }
- }
-
- private def shouldInlineBecauseOfArgs(
- receiverAndArgs: List[PreTransform]): Boolean = {
- def isLikelyOptimizable(arg: PreTransform): Boolean = arg match {
- case PreTransBlock(_, result) =>
- isLikelyOptimizable(result)
-
- case PreTransLocalDef(localDef) =>
- localDef.replacement match {
- case TentativeClosureReplacement(_, _, _, _, _, _) => true
- case ReplaceWithRecordVarRef(_, _, _, _, _) => true
- case InlineClassBeingConstructedReplacement(_, _) => true
- case InlineClassInstanceReplacement(_, _, _) => true
- case _ => false
- }
-
- case PreTransRecordTree(_, _, _) =>
- true
-
- case _ =>
- arg.tpe.base match {
- case ClassType("s_Predef$$less$colon$less" | "s_Predef$$eq$colon$eq") =>
- true
- case _ =>
- false
- }
- }
- receiverAndArgs.exists(isLikelyOptimizable)
- }
-
- private def inline(allocationSite: Option[AllocationSite],
- optReceiver: Option[PreTransform],
- args: List[PreTransform], target: MethodID, isStat: Boolean,
- usePreTransform: Boolean)(
- cont: PreTransCont)(
- implicit scope: Scope, pos: Position): TailRec[Tree] = {
-
- attemptedInlining += target
-
- val MethodDef(_, formals, resultType, body) = getMethodBody(target)
-
- body match {
- case Skip() =>
- assert(isStat, "Found Skip() in expression position")
- cont(PreTransTree(
- Block((optReceiver ++: args).map(finishTransformStat)),
- RefinedType.NoRefinedType))
-
- case _: Literal =>
- cont(PreTransTree(
- Block((optReceiver ++: args).map(finishTransformStat) :+ body),
- RefinedType(body.tpe)))
-
- case This() if args.isEmpty =>
- assert(optReceiver.isDefined,
- "There was a This(), there should be a receiver")
- cont(optReceiver.get)
-
- case Select(This(), field, mutable) if formals.isEmpty =>
- assert(optReceiver.isDefined,
- "There was a This(), there should be a receiver")
- pretransformSelectCommon(body.tpe, optReceiver.get, field, mutable,
- isLhsOfAssign = false)(cont)
-
- case Assign(lhs @ Select(This(), field, mutable), VarRef(Ident(rhsName, _), _))
- if formals.size == 1 && formals.head.name.name == rhsName =>
- assert(isStat, "Found Assign in expression position")
- assert(optReceiver.isDefined,
- "There was a This(), there should be a receiver")
- pretransformSelectCommon(lhs.tpe, optReceiver.get, field, mutable,
- isLhsOfAssign = true) { preTransLhs =>
- // TODO Support assignment of record
- cont(PreTransTree(
- Assign(finishTransformExpr(preTransLhs),
- finishTransformExpr(args.head)),
- RefinedType.NoRefinedType))
- }
-
- case _ =>
- val targetID = (allocationSite, target)
- inlineBody(optReceiver, formals, resultType, body, args, isStat,
- usePreTransform)(cont)(scope.inlining(targetID), pos)
- }
- }
-
- private def inlineBody(optReceiver: Option[PreTransform],
- formals: List[ParamDef], resultType: Type, body: Tree,
- args: List[PreTransform], isStat: Boolean,
- usePreTransform: Boolean)(
- cont: PreTransCont)(
- implicit scope: Scope, pos: Position): TailRec[Tree] = tailcall {
-
- val optReceiverBinding = optReceiver map { receiver =>
- Binding("this", None, receiver.tpe.base, false, receiver)
- }
-
- val argsBindings = for {
- (ParamDef(Ident(name, originalName), tpe, mutable), arg) <- formals zip args
- } yield {
- Binding(name, originalName, tpe, mutable, arg)
- }
-
- withBindings(optReceiverBinding ++: argsBindings) { (bodyScope, cont1) =>
- returnable("", resultType, body, isStat, usePreTransform)(
- cont1)(bodyScope, pos)
- } (cont) (scope.withEnv(OptEnv.Empty))
- }
-
- private def callIntrinsic(code: Int, optTReceiver: Option[PreTransform],
- targs: List[PreTransform], isStat: Boolean, usePreTransform: Boolean)(
- cont: PreTransCont)(
- implicit pos: Position): TailRec[Tree] = {
-
- import Intrinsics._
-
- implicit def string2ident(s: String): Ident = Ident(s, None)
-
- lazy val newArgs = targs.map(finishTransformExpr)
-
- @inline def contTree(result: Tree) = cont(PreTransTree(result))
-
- @inline def StringClassType = ClassType(Definitions.StringClass)
-
- def asRTLong(arg: Tree): Tree =
- AsInstanceOf(arg, ClassType(LongImpl.RuntimeLongClass))
- def firstArgAsRTLong: Tree =
- asRTLong(newArgs.head)
-
- (code: @switch) match {
- // java.lang.System
-
- case ArrayCopy =>
- assert(isStat, "System.arraycopy must be used in statement position")
- contTree(CallHelper("systemArraycopy", newArgs)(NoType))
- case IdentityHashCode =>
- contTree(CallHelper("systemIdentityHashCode", newArgs)(IntType))
-
- // scala.scalajs.runtime package object
-
- case PropertiesOf =>
- contTree(CallHelper("propertiesOf", newArgs)(AnyType))
-
- // java.lang.Long
-
- case LongToString =>
- contTree(Apply(firstArgAsRTLong, "toString__T", Nil)(StringClassType))
- case LongCompare =>
- contTree(Apply(firstArgAsRTLong, "compareTo__sjsr_RuntimeLong__I",
- List(asRTLong(newArgs(1))))(IntType))
- case LongBitCount =>
- contTree(Apply(firstArgAsRTLong, LongImpl.bitCount, Nil)(IntType))
- case LongSignum =>
- contTree(Apply(firstArgAsRTLong, LongImpl.signum, Nil)(LongType))
- case LongLeading0s =>
- contTree(Apply(firstArgAsRTLong, LongImpl.numberOfLeadingZeros, Nil)(IntType))
- case LongTrailing0s =>
- contTree(Apply(firstArgAsRTLong, LongImpl.numberOfTrailingZeros, Nil)(IntType))
- case LongToBinStr =>
- contTree(Apply(firstArgAsRTLong, LongImpl.toBinaryString, Nil)(StringClassType))
- case LongToHexStr =>
- contTree(Apply(firstArgAsRTLong, LongImpl.toHexString, Nil)(StringClassType))
- case LongToOctalStr =>
- contTree(Apply(firstArgAsRTLong, LongImpl.toOctalString, Nil)(StringClassType))
-
- // TypedArray conversions
-
- case ByteArrayToInt8Array =>
- contTree(CallHelper("byteArray2TypedArray", newArgs)(AnyType))
- case ShortArrayToInt16Array =>
- contTree(CallHelper("shortArray2TypedArray", newArgs)(AnyType))
- case CharArrayToUint16Array =>
- contTree(CallHelper("charArray2TypedArray", newArgs)(AnyType))
- case IntArrayToInt32Array =>
- contTree(CallHelper("intArray2TypedArray", newArgs)(AnyType))
- case FloatArrayToFloat32Array =>
- contTree(CallHelper("floatArray2TypedArray", newArgs)(AnyType))
- case DoubleArrayToFloat64Array =>
- contTree(CallHelper("doubleArray2TypedArray", newArgs)(AnyType))
-
- case Int8ArrayToByteArray =>
- contTree(CallHelper("typedArray2ByteArray", newArgs)(AnyType))
- case Int16ArrayToShortArray =>
- contTree(CallHelper("typedArray2ShortArray", newArgs)(AnyType))
- case Uint16ArrayToCharArray =>
- contTree(CallHelper("typedArray2CharArray", newArgs)(AnyType))
- case Int32ArrayToIntArray =>
- contTree(CallHelper("typedArray2IntArray", newArgs)(AnyType))
- case Float32ArrayToFloatArray =>
- contTree(CallHelper("typedArray2FloatArray", newArgs)(AnyType))
- case Float64ArrayToDoubleArray =>
- contTree(CallHelper("typedArray2DoubleArray", newArgs)(AnyType))
- }
- }
-
- private def inlineClassConstructor(allocationSite: AllocationSite,
- cls: ClassType, initialValue: RecordValue,
- ctor: Ident, args: List[PreTransform], cancelFun: CancelFun)(
- cont: PreTransCont)(
- implicit scope: Scope, pos: Position): TailRec[Tree] = {
-
- val RecordValue(recordType, initialFieldValues) = initialValue
-
- pretransformExprs(initialFieldValues) { tinitialFieldValues =>
- val initialFieldBindings = for {
- (RecordType.Field(name, originalName, tpe, mutable), value) <-
- recordType.fields zip tinitialFieldValues
- } yield {
- Binding(name, originalName, tpe, mutable, value)
- }
-
- withNewLocalDefs(initialFieldBindings) { (initialFieldLocalDefList, cont1) =>
- val fieldNames = initialValue.tpe.fields.map(_.name)
- val initialFieldLocalDefs =
- Map(fieldNames zip initialFieldLocalDefList: _*)
-
- inlineClassConstructorBody(allocationSite, initialFieldLocalDefs,
- cls, cls, ctor, args, cancelFun) { (finalFieldLocalDefs, cont2) =>
- cont2(PreTransLocalDef(LocalDef(
- RefinedType(cls, isExact = true, isNullable = false,
- allocationSite = Some(allocationSite)),
- mutable = false,
- InlineClassInstanceReplacement(recordType, finalFieldLocalDefs, cancelFun))))
- } (cont1)
- } (cont)
- }
- }
-
- private def inlineClassConstructorBody(
- allocationSite: AllocationSite,
- inputFieldsLocalDefs: Map[String, LocalDef], cls: ClassType,
- ctorClass: ClassType, ctor: Ident, args: List[PreTransform],
- cancelFun: CancelFun)(
- buildInner: (Map[String, LocalDef], PreTransCont) => TailRec[Tree])(
- cont: PreTransCont)(
- implicit scope: Scope): TailRec[Tree] = tailcall {
-
- val target = staticCall(ctorClass.className, ctor.name).getOrElse(cancelFun())
- val targetID = (Some(allocationSite), target)
- if (scope.implsBeingInlined.contains(targetID))
- cancelFun()
-
- val MethodDef(_, formals, _, BlockOrAlone(stats, This())) =
- getMethodBody(target)
-
- val argsBindings = for {
- (ParamDef(Ident(name, originalName), tpe, mutable), arg) <- formals zip args
- } yield {
- Binding(name, originalName, tpe, mutable, arg)
- }
-
- withBindings(argsBindings) { (bodyScope, cont1) =>
- val thisLocalDef = LocalDef(
- RefinedType(cls, isExact = true, isNullable = false), false,
- InlineClassBeingConstructedReplacement(inputFieldsLocalDefs, cancelFun))
- val statsScope = bodyScope.inlining(targetID).withEnv(
- bodyScope.env.withLocalDef("this", thisLocalDef))
- inlineClassConstructorBodyList(allocationSite, thisLocalDef,
- inputFieldsLocalDefs, cls, stats, cancelFun)(
- buildInner)(cont1)(statsScope)
- } (cont) (scope.withEnv(OptEnv.Empty))
- }
-
- private def inlineClassConstructorBodyList(
- allocationSite: AllocationSite,
- thisLocalDef: LocalDef, inputFieldsLocalDefs: Map[String, LocalDef],
- cls: ClassType, stats: List[Tree], cancelFun: CancelFun)(
- buildInner: (Map[String, LocalDef], PreTransCont) => TailRec[Tree])(
- cont: PreTransCont)(
- implicit scope: Scope): TailRec[Tree] = {
- stats match {
- case This() :: rest =>
- inlineClassConstructorBodyList(allocationSite, thisLocalDef,
- inputFieldsLocalDefs, cls, rest, cancelFun)(buildInner)(cont)
-
- case Assign(s @ Select(ths: This,
- Ident(fieldName, fieldOrigName), false), value) :: rest =>
- pretransformExpr(value) { tvalue =>
- withNewLocalDef(Binding(fieldName, fieldOrigName, s.tpe, false,
- tvalue)) { (localDef, cont1) =>
- if (localDef.contains(thisLocalDef)) {
- /* Uh oh, there is a `val x = ...this...`. We can't keep it,
- * because this field will not be updated with `newThisLocalDef`.
- */
- cancelFun()
- }
- val newFieldsLocalDefs =
- inputFieldsLocalDefs.updated(fieldName, localDef)
- val newThisLocalDef = LocalDef(
- RefinedType(cls, isExact = true, isNullable = false), false,
- InlineClassBeingConstructedReplacement(newFieldsLocalDefs, cancelFun))
- val restScope = scope.withEnv(scope.env.withLocalDef(
- "this", newThisLocalDef))
- inlineClassConstructorBodyList(allocationSite,
- newThisLocalDef, newFieldsLocalDefs, cls, rest, cancelFun)(
- buildInner)(cont1)(restScope)
- } (cont)
- }
-
- /* if (cond)
- * throw e
- * else
- * this.outer = value
- *
- * becomes
- *
- * this.outer =
- * if (cond) throw e
- * else value
- *
- * Typical shape of initialization of outer pointer of inner classes.
- */
- case If(cond, th: Throw,
- Assign(Select(This(), _, false), value)) :: rest =>
- // work around a bug of the compiler (these should be @-bindings)
- val stat = stats.head.asInstanceOf[If]
- val ass = stat.elsep.asInstanceOf[Assign]
- val lhs = ass.lhs
- inlineClassConstructorBodyList(allocationSite, thisLocalDef,
- inputFieldsLocalDefs, cls,
- Assign(lhs, If(cond, th, value)(lhs.tpe)(stat.pos))(ass.pos) :: rest,
- cancelFun)(buildInner)(cont)
-
- case StaticApply(ths: This, superClass, superCtor, args) :: rest
- if isConstructorName(superCtor.name) =>
- pretransformExprs(args) { targs =>
- inlineClassConstructorBody(allocationSite, inputFieldsLocalDefs,
- cls, superClass, superCtor, targs,
- cancelFun) { (outputFieldsLocalDefs, cont1) =>
- val newThisLocalDef = LocalDef(
- RefinedType(cls, isExact = true, isNullable = false), false,
- InlineClassBeingConstructedReplacement(outputFieldsLocalDefs, cancelFun))
- val restScope = scope.withEnv(scope.env.withLocalDef(
- "this", newThisLocalDef))
- inlineClassConstructorBodyList(allocationSite,
- newThisLocalDef, outputFieldsLocalDefs,
- cls, rest, cancelFun)(buildInner)(cont1)(restScope)
- } (cont)
- }
-
- case VarDef(Ident(name, originalName), tpe, mutable, rhs) :: rest =>
- pretransformExpr(rhs) { trhs =>
- withBinding(Binding(name, originalName, tpe, mutable, trhs)) { (restScope, cont1) =>
- inlineClassConstructorBodyList(allocationSite,
- thisLocalDef, inputFieldsLocalDefs,
- cls, rest, cancelFun)(buildInner)(cont1)(restScope)
- } (cont)
- }
-
- case stat :: rest =>
- val transformedStat = transformStat(stat)
- transformedStat match {
- case Skip() =>
- inlineClassConstructorBodyList(allocationSite,
- thisLocalDef, inputFieldsLocalDefs,
- cls, rest, cancelFun)(buildInner)(cont)
- case _ =>
- if (transformedStat.tpe == NothingType)
- cont(PreTransTree(transformedStat, RefinedType.Nothing))
- else {
- inlineClassConstructorBodyList(allocationSite,
- thisLocalDef, inputFieldsLocalDefs,
- cls, rest, cancelFun) { (outputFieldsLocalDefs, cont1) =>
- buildInner(outputFieldsLocalDefs, { tinner =>
- cont1(PreTransBlock(transformedStat :: Nil, tinner))
- })
- }(cont)
- }
- }
-
- case Nil =>
- buildInner(inputFieldsLocalDefs, cont)
- }
- }
-
- private def foldIf(cond: Tree, thenp: Tree, elsep: Tree)(tpe: Type)(
- implicit pos: Position): Tree = {
- import BinaryOp._
-
- @inline def default = If(cond, thenp, elsep)(tpe)
- cond match {
- case BooleanLiteral(v) =>
- if (v) thenp
- else elsep
-
- case _ =>
- @inline def negCond = foldUnaryOp(UnaryOp.Boolean_!, cond)
- if (thenp.tpe == BooleanType && elsep.tpe == BooleanType) {
- (cond, thenp, elsep) match {
- case (_, BooleanLiteral(t), BooleanLiteral(e)) =>
- if (t == e) Block(keepOnlySideEffects(cond), thenp)
- else if (t) cond
- else negCond
-
- case (_, BooleanLiteral(false), _) =>
- foldIf(negCond, elsep, BooleanLiteral(false))(tpe) // canonical && form
- case (_, _, BooleanLiteral(true)) =>
- foldIf(negCond, BooleanLiteral(true), thenp)(tpe) // canonical || form
-
- /* if (lhs === null) rhs === null else lhs === rhs
- * -> lhs === rhs
- * This is the typical shape of a lhs == rhs test where
- * the equals() method has been inlined as a reference
- * equality test.
- */
- case (BinaryOp(BinaryOp.===, VarRef(lhsIdent, _), Null()),
- BinaryOp(BinaryOp.===, VarRef(rhsIdent, _), Null()),
- BinaryOp(BinaryOp.===, VarRef(lhsIdent2, _), VarRef(rhsIdent2, _)))
- if lhsIdent2 == lhsIdent && rhsIdent2 == rhsIdent =>
- elsep
-
- // Example: (x > y) || (x == y) -> (x >= y)
- case (BinaryOp(op1 @ (Num_== | Num_!= | Num_< | Num_<= | Num_> | Num_>=), l1, r1),
- BooleanLiteral(true),
- BinaryOp(op2 @ (Num_== | Num_!= | Num_< | Num_<= | Num_> | Num_>=), l2, r2))
- if ((l1.isInstanceOf[Literal] || l1.isInstanceOf[VarRef]) &&
- (r1.isInstanceOf[Literal] || r1.isInstanceOf[VarRef]) &&
- (l1 == l2 && r1 == r2)) =>
- val canBeEqual =
- ((op1 == Num_==) || (op1 == Num_<=) || (op1 == Num_>=)) ||
- ((op2 == Num_==) || (op2 == Num_<=) || (op2 == Num_>=))
- val canBeLessThan =
- ((op1 == Num_!=) || (op1 == Num_<) || (op1 == Num_<=)) ||
- ((op2 == Num_!=) || (op2 == Num_<) || (op2 == Num_<=))
- val canBeGreaterThan =
- ((op1 == Num_!=) || (op1 == Num_>) || (op1 == Num_>=)) ||
- ((op2 == Num_!=) || (op2 == Num_>) || (op2 == Num_>=))
-
- fold3WayComparison(canBeEqual, canBeLessThan, canBeGreaterThan, l1, r1)
-
- // Example: (x >= y) && (x <= y) -> (x == y)
- case (BinaryOp(op1 @ (Num_== | Num_!= | Num_< | Num_<= | Num_> | Num_>=), l1, r1),
- BinaryOp(op2 @ (Num_== | Num_!= | Num_< | Num_<= | Num_> | Num_>=), l2, r2),
- BooleanLiteral(false))
- if ((l1.isInstanceOf[Literal] || l1.isInstanceOf[VarRef]) &&
- (r1.isInstanceOf[Literal] || r1.isInstanceOf[VarRef]) &&
- (l1 == l2 && r1 == r2)) =>
- val canBeEqual =
- ((op1 == Num_==) || (op1 == Num_<=) || (op1 == Num_>=)) &&
- ((op2 == Num_==) || (op2 == Num_<=) || (op2 == Num_>=))
- val canBeLessThan =
- ((op1 == Num_!=) || (op1 == Num_<) || (op1 == Num_<=)) &&
- ((op2 == Num_!=) || (op2 == Num_<) || (op2 == Num_<=))
- val canBeGreaterThan =
- ((op1 == Num_!=) || (op1 == Num_>) || (op1 == Num_>=)) &&
- ((op2 == Num_!=) || (op2 == Num_>) || (op2 == Num_>=))
-
- fold3WayComparison(canBeEqual, canBeLessThan, canBeGreaterThan, l1, r1)
-
- case _ => default
- }
- } else {
- (thenp, elsep) match {
- case (Skip(), Skip()) => keepOnlySideEffects(cond)
- case (Skip(), _) => foldIf(negCond, elsep, thenp)(tpe)
-
- case _ => default
- }
- }
- }
- }
-
- private def transformUnaryOp(tree: UnaryOp)(implicit scope: Scope): Tree = {
- import UnaryOp._
-
- implicit val pos = tree.pos
- val UnaryOp(op, arg) = tree
-
- (op: @switch) match {
- case LongToInt =>
- trampoline {
- pretransformExpr(arg) { (targ) =>
- TailCalls.done {
- foldUnaryOp(op, finishTransformOptLongExpr(targ))
- }
- }
- }
-
- case _ =>
- foldUnaryOp(op, transformExpr(arg))
- }
- }
-
- private def transformBinaryOp(tree: BinaryOp)(implicit scope: Scope): Tree = {
- import BinaryOp._
-
- implicit val pos = tree.pos
- val BinaryOp(op, lhs, rhs) = tree
-
- (op: @switch) match {
- case === | !== =>
- trampoline {
- pretransformExprs(lhs, rhs) { (tlhs, trhs) =>
- TailCalls.done(foldReferenceEquality(tlhs, trhs, op == ===))
- }
- }
-
- case Long_== | Long_!= | Long_< | Long_<= | Long_> | Long_>= =>
- trampoline {
- pretransformExprs(lhs, rhs) { (tlhs, trhs) =>
- TailCalls.done {
- if (isLiteralOrOptimizableLong(tlhs) &&
- isLiteralOrOptimizableLong(trhs)) {
- foldBinaryOp(op, finishTransformOptLongExpr(tlhs),
- finishTransformOptLongExpr(trhs))
- } else {
- foldBinaryOp(op, finishTransformExpr(tlhs),
- finishTransformExpr(trhs))
- }
- }
- }
- }
-
- case _ =>
- foldBinaryOp(op, transformExpr(lhs), transformExpr(rhs))
- }
- }
-
- private def isLiteralOrOptimizableLong(texpr: PreTransform): Boolean = {
- texpr match {
- case PreTransTree(LongLiteral(_), _) =>
- true
- case PreTransLocalDef(LocalDef(_, _, replacement)) =>
- replacement match {
- case ReplaceWithVarRef(_, _, _, Some(_)) => true
- case ReplaceWithConstant(LongLiteral(_)) => true
- case _ => false
- }
- case _ =>
- false
- }
- }
-
- private def finishTransformOptLongExpr(targ: PreTransform): Tree = targ match {
- case PreTransLocalDef(LocalDef(tpe, false,
- ReplaceWithVarRef(_, _, _, Some(argValue)))) =>
- argValue()
- case _ =>
- finishTransformExpr(targ)
- }
-
- private def foldUnaryOp(op: UnaryOp.Code, arg: Tree)(
- implicit pos: Position): Tree = {
- import UnaryOp._
- @inline def default = UnaryOp(op, arg)
- (op: @switch) match {
- case Boolean_! =>
- arg match {
- case BooleanLiteral(v) => BooleanLiteral(!v)
- case UnaryOp(Boolean_!, x) => x
-
- case BinaryOp(innerOp, l, r) =>
- val newOp = (innerOp: @switch) match {
- case BinaryOp.=== => BinaryOp.!==
- case BinaryOp.!== => BinaryOp.===
-
- case BinaryOp.Num_== => BinaryOp.Num_!=
- case BinaryOp.Num_!= => BinaryOp.Num_==
- case BinaryOp.Num_< => BinaryOp.Num_>=
- case BinaryOp.Num_<= => BinaryOp.Num_>
- case BinaryOp.Num_> => BinaryOp.Num_<=
- case BinaryOp.Num_>= => BinaryOp.Num_<
-
- case BinaryOp.Long_== => BinaryOp.Long_!=
- case BinaryOp.Long_!= => BinaryOp.Long_==
- case BinaryOp.Long_< => BinaryOp.Long_>=
- case BinaryOp.Long_<= => BinaryOp.Long_>
- case BinaryOp.Long_> => BinaryOp.Long_<=
- case BinaryOp.Long_>= => BinaryOp.Long_<
-
- case BinaryOp.Boolean_== => BinaryOp.Boolean_!=
- case BinaryOp.Boolean_!= => BinaryOp.Boolean_==
-
- case _ => -1
- }
- if (newOp == -1) default
- else BinaryOp(newOp, l, r)
-
- case _ => default
- }
-
- case IntToLong =>
- arg match {
- case IntLiteral(v) => LongLiteral(v.toLong)
- case _ => default
- }
-
- case LongToInt =>
- arg match {
- case LongLiteral(v) => IntLiteral(v.toInt)
- case UnaryOp(IntToLong, x) => x
-
- case BinaryOp(BinaryOp.Long_+, x, y) =>
- foldBinaryOp(BinaryOp.Int_+,
- foldUnaryOp(LongToInt, x),
- foldUnaryOp(LongToInt, y))
- case BinaryOp(BinaryOp.Long_-, x, y) =>
- foldBinaryOp(BinaryOp.Int_-,
- foldUnaryOp(LongToInt, x),
- foldUnaryOp(LongToInt, y))
-
- case _ => default
- }
-
- case LongToDouble =>
- arg match {
- case LongLiteral(v) => DoubleLiteral(v.toDouble)
- case _ => default
- }
- case DoubleToInt =>
- arg match {
- case _ if arg.tpe == IntType => arg
- case NumberLiteral(v) => IntLiteral(v.toInt)
- case _ => default
- }
- case DoubleToFloat =>
- arg match {
- case _ if arg.tpe == FloatType => arg
- case NumberLiteral(v) => FloatLiteral(v.toFloat)
- case _ => default
- }
- case DoubleToLong =>
- arg match {
- case _ if arg.tpe == IntType => foldUnaryOp(IntToLong, arg)
- case NumberLiteral(v) => LongLiteral(v.toLong)
- case _ => default
- }
- case _ =>
- default
- }
- }
-
- /** Performs === for two literals.
- * The result is always known statically.
- */
- private def literal_===(lhs: Literal, rhs: Literal): Boolean = {
- (lhs, rhs) match {
- case (IntLiteral(l), IntLiteral(r)) => l == r
- case (FloatLiteral(l), FloatLiteral(r)) => l == r
- case (NumberLiteral(l), NumberLiteral(r)) => l == r
- case (LongLiteral(l), LongLiteral(r)) => l == r
- case (BooleanLiteral(l), BooleanLiteral(r)) => l == r
- case (StringLiteral(l), StringLiteral(r)) => l == r
- case (Undefined(), Undefined()) => true
- case (Null(), Null()) => true
- case _ => false
- }
- }
-
- private def foldBinaryOp(op: BinaryOp.Code, lhs: Tree, rhs: Tree)(
- implicit pos: Position): Tree = {
- import BinaryOp._
- @inline def default = BinaryOp(op, lhs, rhs)
- (op: @switch) match {
- case === | !== =>
- val positive = (op == ===)
- (lhs, rhs) match {
- case (lhs: Literal, rhs: Literal) =>
- BooleanLiteral(literal_===(lhs, rhs) == positive)
-
- case (_: Literal, _) => foldBinaryOp(op, rhs, lhs)
- case _ => default
- }
-
- case Int_+ =>
- (lhs, rhs) match {
- case (IntLiteral(l), IntLiteral(r)) => IntLiteral(l + r)
- case (_, IntLiteral(_)) => foldBinaryOp(Int_+, rhs, lhs)
- case (IntLiteral(0), _) => rhs
-
- case (IntLiteral(x),
- BinaryOp(innerOp @ (Int_+ | Int_-), IntLiteral(y), z)) =>
- foldBinaryOp(innerOp, IntLiteral(x+y), z)
-
- case _ => default
- }
-
- case Int_- =>
- (lhs, rhs) match {
- case (_, IntLiteral(r)) => foldBinaryOp(Int_+, lhs, IntLiteral(-r))
-
- case (IntLiteral(x), BinaryOp(Int_+, IntLiteral(y), z)) =>
- foldBinaryOp(Int_-, IntLiteral(x-y), z)
- case (IntLiteral(x), BinaryOp(Int_-, IntLiteral(y), z)) =>
- foldBinaryOp(Int_+, IntLiteral(x-y), z)
-
- case (_, BinaryOp(Int_-, IntLiteral(0), x)) =>
- foldBinaryOp(Int_+, lhs, x)
-
- case _ => default
- }
-
- case Int_* =>
- (lhs, rhs) match {
- case (IntLiteral(l), IntLiteral(r)) => IntLiteral(l * r)
- case (_, IntLiteral(_)) => foldBinaryOp(Int_*, rhs, lhs)
-
- case (IntLiteral(1), _) => rhs
- case (IntLiteral(-1), _) => foldBinaryOp(Int_-, IntLiteral(0), lhs)
-
- case _ => default
- }
-
- case Int_/ =>
- (lhs, rhs) match {
- case (IntLiteral(l), IntLiteral(r)) if r != 0 => IntLiteral(l / r)
-
- case (_, IntLiteral(1)) => lhs
- case (_, IntLiteral(-1)) => foldBinaryOp(Int_-, IntLiteral(0), lhs)
-
- case _ => default
- }
-
- case Int_% =>
- (lhs, rhs) match {
- case (IntLiteral(l), IntLiteral(r)) if r != 0 => IntLiteral(l % r)
- case (_, IntLiteral(1 | -1)) =>
- Block(keepOnlySideEffects(lhs), IntLiteral(0))
- case _ => default
- }
-
- case Int_| =>
- (lhs, rhs) match {
- case (IntLiteral(l), IntLiteral(r)) => IntLiteral(l | r)
- case (_, IntLiteral(_)) => foldBinaryOp(Int_|, rhs, lhs)
- case (IntLiteral(0), _) => rhs
-
- case (IntLiteral(x), BinaryOp(Int_|, IntLiteral(y), z)) =>
- foldBinaryOp(Int_|, IntLiteral(x | y), z)
-
- case _ => default
- }
-
- case Int_& =>
- (lhs, rhs) match {
- case (IntLiteral(l), IntLiteral(r)) => IntLiteral(l & r)
- case (_, IntLiteral(_)) => foldBinaryOp(Int_&, rhs, lhs)
- case (IntLiteral(-1), _) => rhs
-
- case (IntLiteral(x), BinaryOp(Int_&, IntLiteral(y), z)) =>
- foldBinaryOp(Int_&, IntLiteral(x & y), z)
-
- case _ => default
- }
-
- case Int_^ =>
- (lhs, rhs) match {
- case (IntLiteral(l), IntLiteral(r)) => IntLiteral(l ^ r)
- case (_, IntLiteral(_)) => foldBinaryOp(Int_^, rhs, lhs)
- case (IntLiteral(0), _) => rhs
-
- case (IntLiteral(x), BinaryOp(Int_^, IntLiteral(y), z)) =>
- foldBinaryOp(Int_^, IntLiteral(x ^ y), z)
-
- case _ => default
- }
-
- case Int_<< =>
- (lhs, rhs) match {
- case (IntLiteral(l), IntLiteral(r)) => IntLiteral(l << r)
- case (_, IntLiteral(x)) if x % 32 == 0 => lhs
- case _ => default
- }
-
- case Int_>>> =>
- (lhs, rhs) match {
- case (IntLiteral(l), IntLiteral(r)) => IntLiteral(l >>> r)
- case (_, IntLiteral(x)) if x % 32 == 0 => lhs
- case _ => default
- }
-
- case Int_>> =>
- (lhs, rhs) match {
- case (IntLiteral(l), IntLiteral(r)) => IntLiteral(l >> r)
- case (_, IntLiteral(x)) if x % 32 == 0 => lhs
- case _ => default
- }
-
- case Long_+ =>
- (lhs, rhs) match {
- case (LongLiteral(l), LongLiteral(r)) => LongLiteral(l + r)
- case (_, LongLiteral(_)) => foldBinaryOp(Long_+, rhs, lhs)
- case (LongLiteral(0), _) => rhs
-
- case (LongLiteral(x),
- BinaryOp(innerOp @ (Long_+ | Long_-), LongLiteral(y), z)) =>
- foldBinaryOp(innerOp, LongLiteral(x+y), z)
-
- case _ => default
- }
-
- case Long_- =>
- (lhs, rhs) match {
- case (_, LongLiteral(r)) => foldBinaryOp(Long_+, LongLiteral(-r), lhs)
-
- case (LongLiteral(x), BinaryOp(Long_+, LongLiteral(y), z)) =>
- foldBinaryOp(Long_-, LongLiteral(x-y), z)
- case (LongLiteral(x), BinaryOp(Long_-, LongLiteral(y), z)) =>
- foldBinaryOp(Long_+, LongLiteral(x-y), z)
-
- case (_, BinaryOp(BinaryOp.Long_-, LongLiteral(0L), x)) =>
- foldBinaryOp(Long_+, lhs, x)
-
- case _ => default
- }
-
- case Long_* =>
- (lhs, rhs) match {
- case (LongLiteral(l), LongLiteral(r)) => LongLiteral(l * r)
- case (_, LongLiteral(_)) => foldBinaryOp(Long_*, rhs, lhs)
-
- case (LongLiteral(1), _) => rhs
- case (LongLiteral(-1), _) => foldBinaryOp(Long_-, LongLiteral(0), lhs)
-
- case _ => default
- }
-
- case Long_/ =>
- (lhs, rhs) match {
- case (_, LongLiteral(0)) => default
- case (LongLiteral(l), LongLiteral(r)) => LongLiteral(l / r)
-
- case (_, LongLiteral(1)) => lhs
- case (_, LongLiteral(-1)) => foldBinaryOp(Long_-, LongLiteral(0), lhs)
-
- case (LongFromInt(x), LongFromInt(y: IntLiteral)) if y.value != -1 =>
- LongFromInt(foldBinaryOp(Int_/, x, y))
-
- case _ => default
- }
-
- case Long_% =>
- (lhs, rhs) match {
- case (_, LongLiteral(0)) => default
- case (LongLiteral(l), LongLiteral(r)) => LongLiteral(l % r)
-
- case (_, LongLiteral(1L | -1L)) =>
- Block(keepOnlySideEffects(lhs), LongLiteral(0L))
-
- case (LongFromInt(x), LongFromInt(y)) =>
- LongFromInt(foldBinaryOp(Int_%, x, y))
-
- case _ => default
- }
-
- case Long_| =>
- (lhs, rhs) match {
- case (LongLiteral(l), LongLiteral(r)) => LongLiteral(l | r)
- case (_, LongLiteral(_)) => foldBinaryOp(Long_|, rhs, lhs)
- case (LongLiteral(0), _) => rhs
-
- case (LongLiteral(x), BinaryOp(Long_|, LongLiteral(y), z)) =>
- foldBinaryOp(Long_|, LongLiteral(x | y), z)
-
- case _ => default
- }
-
- case Long_& =>
- (lhs, rhs) match {
- case (LongLiteral(l), LongLiteral(r)) => LongLiteral(l & r)
- case (_, LongLiteral(_)) => foldBinaryOp(Long_&, rhs, lhs)
- case (LongLiteral(-1), _) => rhs
-
- case (LongLiteral(x), BinaryOp(Long_&, LongLiteral(y), z)) =>
- foldBinaryOp(Long_&, LongLiteral(x & y), z)
-
- case _ => default
- }
-
- case Long_^ =>
- (lhs, rhs) match {
- case (LongLiteral(l), LongLiteral(r)) => LongLiteral(l ^ r)
- case (_, LongLiteral(_)) => foldBinaryOp(Long_^, rhs, lhs)
- case (LongLiteral(0), _) => rhs
-
- case (LongLiteral(x), BinaryOp(Long_^, LongLiteral(y), z)) =>
- foldBinaryOp(Long_^, LongLiteral(x ^ y), z)
-
- case _ => default
- }
-
- case Long_<< =>
- (lhs, rhs) match {
- case (LongLiteral(l), IntLiteral(r)) => LongLiteral(l << r)
- case (_, IntLiteral(x)) if x % 64 == 0 => lhs
- case _ => default
- }
-
- case Long_>>> =>
- (lhs, rhs) match {
- case (LongLiteral(l), IntLiteral(r)) => LongLiteral(l >>> r)
- case (_, IntLiteral(x)) if x % 64 == 0 => lhs
- case _ => default
- }
-
- case Long_>> =>
- (lhs, rhs) match {
- case (LongLiteral(l), IntLiteral(r)) => LongLiteral(l >> r)
- case (_, IntLiteral(x)) if x % 64 == 0 => lhs
- case _ => default
- }
-
- case Long_== | Long_!= =>
- val positive = (op == Long_==)
- (lhs, rhs) match {
- case (LongLiteral(l), LongLiteral(r)) =>
- BooleanLiteral((l == r) == positive)
-
- case (LongFromInt(x), LongFromInt(y)) =>
- foldBinaryOp(if (positive) === else !==, x, y)
- case (LongFromInt(x), LongLiteral(y)) =>
- assert(y > Int.MaxValue || y < Int.MinValue)
- Block(keepOnlySideEffects(x), BooleanLiteral(!positive))
-
- case (BinaryOp(Long_+, LongLiteral(x), y), LongLiteral(z)) =>
- foldBinaryOp(op, y, LongLiteral(z-x))
- case (BinaryOp(Long_-, LongLiteral(x), y), LongLiteral(z)) =>
- foldBinaryOp(op, y, LongLiteral(x-z))
-
- case (LongLiteral(_), _) => foldBinaryOp(op, rhs, lhs)
- case _ => default
- }
-
- case Long_< | Long_<= | Long_> | Long_>= =>
- def flippedOp = (op: @switch) match {
- case Long_< => Long_>
- case Long_<= => Long_>=
- case Long_> => Long_<
- case Long_>= => Long_<=
- }
-
- def intOp = (op: @switch) match {
- case Long_< => Num_<
- case Long_<= => Num_<=
- case Long_> => Num_>
- case Long_>= => Num_>=
- }
-
- (lhs, rhs) match {
- case (LongLiteral(l), LongLiteral(r)) =>
- val result = (op: @switch) match {
- case Long_< => l < r
- case Long_<= => l <= r
- case Long_> => l > r
- case Long_>= => l >= r
- }
- BooleanLiteral(result)
-
- case (_, LongLiteral(Long.MinValue)) =>
- if (op == Long_< || op == Long_>=)
- Block(keepOnlySideEffects(lhs), BooleanLiteral(op == Long_>=))
- else
- foldBinaryOp(if (op == Long_<=) Long_== else Long_!=, lhs, rhs)
-
- case (_, LongLiteral(Long.MaxValue)) =>
- if (op == Long_> || op == Long_<=)
- Block(keepOnlySideEffects(lhs), BooleanLiteral(op == Long_<=))
- else
- foldBinaryOp(if (op == Long_>=) Long_== else Long_!=, lhs, rhs)
-
- case (LongFromInt(x), LongFromInt(y)) =>
- foldBinaryOp(intOp, x, y)
- case (LongFromInt(x), LongLiteral(y)) =>
- assert(y > Int.MaxValue || y < Int.MinValue)
- val result =
- if (y > Int.MaxValue) op == Long_< || op == Long_<=
- else op == Long_> || op == Long_>=
- Block(keepOnlySideEffects(x), BooleanLiteral(result))
-
- /* x + y.toLong > z
- * -x on both sides
- * requires x + y.toLong not to overflow, and z - x likewise
- * y.toLong > z - x
- */
- case (BinaryOp(Long_+, LongLiteral(x), y @ LongFromInt(_)), LongLiteral(z))
- if canAddLongs(x, Int.MinValue) &&
- canAddLongs(x, Int.MaxValue) &&
- canSubtractLongs(z, x) =>
- foldBinaryOp(op, y, LongLiteral(z-x))
-
- /* x - y.toLong > z
- * -x on both sides
- * requires x - y.toLong not to overflow, and z - x likewise
- * -(y.toLong) > z - x
- */
- case (BinaryOp(Long_-, LongLiteral(x), y @ LongFromInt(_)), LongLiteral(z))
- if canSubtractLongs(x, Int.MinValue) &&
- canSubtractLongs(x, Int.MaxValue) &&
- canSubtractLongs(z, x) =>
- if (z-x != Long.MinValue) {
- // Since -(y.toLong) does not overflow, we can negate both sides
- foldBinaryOp(flippedOp, y, LongLiteral(-(z-x)))
- } else {
- /* -(y.toLong) > Long.MinValue
- * Depending on the operator, this is either always true or
- * always false.
- */
- val result = (op == Long_>) || (op == Long_>=)
- Block(keepOnlySideEffects(y), BooleanLiteral(result))
- }
-
- /* x.toLong + y.toLong > Int.MaxValue.toLong
- *
- * This is basically testing whether x+y overflows in positive.
- * If x <= 0 or y <= 0, this cannot happen -> false.
- * If x > 0 and y > 0, this can be detected with x+y < 0.
- * Therefore, we rewrite as:
- *
- * x > 0 && y > 0 && x+y < 0.
- *
- * This requires to evaluate x and y once.
- */
- case (BinaryOp(Long_+, LongFromInt(x), LongFromInt(y)),
- LongLiteral(Int.MaxValue)) =>
- trampoline {
- withNewLocalDefs(List(
- Binding("x", None, IntType, false, PreTransTree(x)),
- Binding("y", None, IntType, false, PreTransTree(y)))) {
- (tempsLocalDefs, cont) =>
- val List(tempXDef, tempYDef) = tempsLocalDefs
- val tempX = tempXDef.newReplacement
- val tempY = tempYDef.newReplacement
- cont(PreTransTree(
- AndThen(AndThen(
- BinaryOp(Num_>, tempX, IntLiteral(0)),
- BinaryOp(Num_>, tempY, IntLiteral(0))),
- BinaryOp(Num_<, BinaryOp(Int_+, tempX, tempY), IntLiteral(0)))))
- } (finishTransform(isStat = false))
- }
-
- case (LongLiteral(_), _) => foldBinaryOp(flippedOp, rhs, lhs)
- case _ => default
- }
-
- case Float_+ =>
- (lhs, rhs) match {
- case (FloatLiteral(l), FloatLiteral(r)) => FloatLiteral(l + r)
- case (FloatLiteral(0), _) => rhs
- case (_, FloatLiteral(_)) => foldBinaryOp(Float_+, rhs, lhs)
-
- case (FloatLiteral(x),
- BinaryOp(innerOp @ (Float_+ | Float_-), FloatLiteral(y), z)) =>
- foldBinaryOp(innerOp, FloatLiteral(x+y), z)
-
- case _ => default
- }
-
- case Float_- =>
- (lhs, rhs) match {
- case (_, FloatLiteral(r)) => foldBinaryOp(Float_+, lhs, FloatLiteral(-r))
-
- case (FloatLiteral(x), BinaryOp(Float_+, FloatLiteral(y), z)) =>
- foldBinaryOp(Float_-, FloatLiteral(x-y), z)
- case (FloatLiteral(x), BinaryOp(Float_-, FloatLiteral(y), z)) =>
- foldBinaryOp(Float_+, FloatLiteral(x-y), z)
-
- case (_, BinaryOp(BinaryOp.Float_-, FloatLiteral(0), x)) =>
- foldBinaryOp(Float_+, lhs, x)
-
- case _ => default
- }
-
- case Float_* =>
- (lhs, rhs) match {
- case (FloatLiteral(l), FloatLiteral(r)) => FloatLiteral(l * r)
- case (_, FloatLiteral(_)) => foldBinaryOp(Float_*, rhs, lhs)
-
- case (FloatLiteral(1), _) => rhs
- case (FloatLiteral(-1), _) => foldBinaryOp(Float_-, FloatLiteral(0), lhs)
-
- case _ => default
- }
-
- case Float_/ =>
- (lhs, rhs) match {
- case (FloatLiteral(l), FloatLiteral(r)) => FloatLiteral(l / r)
-
- case (_, FloatLiteral(1)) => lhs
- case (_, FloatLiteral(-1)) => foldBinaryOp(Float_-, FloatLiteral(0), lhs)
-
- case _ => default
- }
-
- case Float_% =>
- (lhs, rhs) match {
- case (FloatLiteral(l), FloatLiteral(r)) => FloatLiteral(l % r)
- case _ => default
- }
-
- case Double_+ =>
- (lhs, rhs) match {
- case (NumberLiteral(l), NumberLiteral(r)) => DoubleLiteral(l + r)
- case (NumberLiteral(0), _) => rhs
- case (_, NumberLiteral(_)) => foldBinaryOp(Double_+, rhs, lhs)
-
- case (NumberLiteral(x),
- BinaryOp(innerOp @ (Double_+ | Double_-), NumberLiteral(y), z)) =>
- foldBinaryOp(innerOp, DoubleLiteral(x+y), z)
-
- case _ => default
- }
-
- case Double_- =>
- (lhs, rhs) match {
- case (_, NumberLiteral(r)) => foldBinaryOp(Double_+, lhs, DoubleLiteral(-r))
-
- case (NumberLiteral(x), BinaryOp(Double_+, NumberLiteral(y), z)) =>
- foldBinaryOp(Double_-, DoubleLiteral(x-y), z)
- case (NumberLiteral(x), BinaryOp(Double_-, NumberLiteral(y), z)) =>
- foldBinaryOp(Double_+, DoubleLiteral(x-y), z)
-
- case (_, BinaryOp(BinaryOp.Double_-, NumberLiteral(0), x)) =>
- foldBinaryOp(Double_+, lhs, x)
-
- case _ => default
- }
-
- case Double_* =>
- (lhs, rhs) match {
- case (NumberLiteral(l), NumberLiteral(r)) => DoubleLiteral(l * r)
- case (_, NumberLiteral(_)) => foldBinaryOp(Double_*, rhs, lhs)
-
- case (NumberLiteral(1), _) => rhs
- case (NumberLiteral(-1), _) => foldBinaryOp(Double_-, DoubleLiteral(0), lhs)
-
- case _ => default
- }
-
- case Double_/ =>
- (lhs, rhs) match {
- case (NumberLiteral(l), NumberLiteral(r)) => DoubleLiteral(l / r)
-
- case (_, NumberLiteral(1)) => lhs
- case (_, NumberLiteral(-1)) => foldBinaryOp(Double_-, DoubleLiteral(0), lhs)
-
- case _ => default
- }
-
- case Double_% =>
- (lhs, rhs) match {
- case (NumberLiteral(l), NumberLiteral(r)) => DoubleLiteral(l % r)
- case _ => default
- }
-
- case Boolean_== | Boolean_!= =>
- val positive = (op == Boolean_==)
- (lhs, rhs) match {
- case (BooleanLiteral(l), _) =>
- if (l == positive) rhs
- else foldUnaryOp(UnaryOp.Boolean_!, rhs)
- case (_, BooleanLiteral(r)) =>
- if (r == positive) lhs
- else foldUnaryOp(UnaryOp.Boolean_!, lhs)
- case _ =>
- default
- }
-
- case Boolean_| =>
- (lhs, rhs) match {
- case (_, BooleanLiteral(false)) => lhs
- case (BooleanLiteral(false), _) => rhs
- case _ => default
- }
-
- case Boolean_& =>
- (lhs, rhs) match {
- case (_, BooleanLiteral(true)) => lhs
- case (BooleanLiteral(true), _) => rhs
- case _ => default
- }
-
- case Num_== | Num_!= =>
- val positive = (op == Num_==)
- (lhs, rhs) match {
- case (lhs: Literal, rhs: Literal) =>
- BooleanLiteral(literal_===(lhs, rhs) == positive)
-
- case (BinaryOp(Int_+, IntLiteral(x), y), IntLiteral(z)) =>
- foldBinaryOp(op, y, IntLiteral(z-x))
- case (BinaryOp(Int_-, IntLiteral(x), y), IntLiteral(z)) =>
- foldBinaryOp(op, y, IntLiteral(x-z))
-
- case (_: Literal, _) => foldBinaryOp(op, rhs, lhs)
- case _ => default
- }
-
- case Num_< | Num_<= | Num_> | Num_>= =>
- def flippedOp = (op: @switch) match {
- case Num_< => Num_>
- case Num_<= => Num_>=
- case Num_> => Num_<
- case Num_>= => Num_<=
- }
-
- if (lhs.tpe == IntType && rhs.tpe == IntType) {
- (lhs, rhs) match {
- case (IntLiteral(l), IntLiteral(r)) =>
- val result = (op: @switch) match {
- case Num_< => l < r
- case Num_<= => l <= r
- case Num_> => l > r
- case Num_>= => l >= r
- }
- BooleanLiteral(result)
-
- case (_, IntLiteral(Int.MinValue)) =>
- if (op == Num_< || op == Num_>=)
- Block(keepOnlySideEffects(lhs), BooleanLiteral(op == Num_>=))
- else
- foldBinaryOp(if (op == Num_<=) Num_== else Num_!=, lhs, rhs)
-
- case (_, IntLiteral(Int.MaxValue)) =>
- if (op == Num_> || op == Num_<=)
- Block(keepOnlySideEffects(lhs), BooleanLiteral(op == Num_<=))
- else
- foldBinaryOp(if (op == Num_>=) Num_== else Num_!=, lhs, rhs)
-
- case (IntLiteral(_), _) => foldBinaryOp(flippedOp, rhs, lhs)
- case _ => default
- }
- } else {
- (lhs, rhs) match {
- case (NumberLiteral(l), NumberLiteral(r)) =>
- val result = (op: @switch) match {
- case Num_< => l < r
- case Num_<= => l <= r
- case Num_> => l > r
- case Num_>= => l >= r
- }
- BooleanLiteral(result)
-
- case _ => default
- }
- }
-
- case _ =>
- default
- }
- }
-
- private def fold3WayComparison(canBeEqual: Boolean, canBeLessThan: Boolean,
- canBeGreaterThan: Boolean, lhs: Tree, rhs: Tree)(
- implicit pos: Position): Tree = {
- import BinaryOp._
- if (canBeEqual) {
- if (canBeLessThan) {
- if (canBeGreaterThan)
- Block(keepOnlySideEffects(lhs), keepOnlySideEffects(rhs), BooleanLiteral(true))
- else
- foldBinaryOp(Num_<=, lhs, rhs)
- } else {
- if (canBeGreaterThan)
- foldBinaryOp(Num_>=, lhs, rhs)
- else
- foldBinaryOp(Num_==, lhs, rhs)
- }
- } else {
- if (canBeLessThan) {
- if (canBeGreaterThan)
- foldBinaryOp(Num_!=, lhs, rhs)
- else
- foldBinaryOp(Num_<, lhs, rhs)
- } else {
- if (canBeGreaterThan)
- foldBinaryOp(Num_>, lhs, rhs)
- else
- Block(keepOnlySideEffects(lhs), keepOnlySideEffects(rhs), BooleanLiteral(false))
- }
- }
- }
-
- private def foldUnbox(arg: PreTransform, charCode: Char)(
- cont: PreTransCont): TailRec[Tree] = {
- (charCode: @switch) match {
- case 'Z' if arg.tpe.base == BooleanType => cont(arg)
- case 'I' if arg.tpe.base == IntType => cont(arg)
- case 'F' if arg.tpe.base == FloatType => cont(arg)
- case 'J' if arg.tpe.base == LongType => cont(arg)
- case 'D' if arg.tpe.base == DoubleType ||
- arg.tpe.base == IntType || arg.tpe.base == FloatType => cont(arg)
- case _ =>
- cont(PreTransTree(Unbox(finishTransformExpr(arg), charCode)(arg.pos)))
- }
- }
-
- private def foldReferenceEquality(tlhs: PreTransform, trhs: PreTransform,
- positive: Boolean = true)(implicit pos: Position): Tree = {
- (tlhs, trhs) match {
- case (_, PreTransTree(Null(), _)) if !tlhs.tpe.isNullable =>
- Block(
- finishTransformStat(tlhs),
- BooleanLiteral(!positive))
- case (PreTransTree(Null(), _), _) if !trhs.tpe.isNullable =>
- Block(
- finishTransformStat(trhs),
- BooleanLiteral(!positive))
- case _ =>
- foldBinaryOp(if (positive) BinaryOp.=== else BinaryOp.!==,
- finishTransformExpr(tlhs), finishTransformExpr(trhs))
- }
- }
-
- private def finishTransformCheckNull(preTrans: PreTransform)(
- implicit pos: Position): Tree = {
- if (preTrans.tpe.isNullable) {
- val transformed = finishTransformExpr(preTrans)
- CallHelper("checkNonNull", transformed)(transformed.tpe)
- } else {
- finishTransformExpr(preTrans)
- }
- }
-
- def transformIsolatedBody(optTarget: Option[MethodID],
- thisType: Type, params: List[ParamDef], resultType: Type,
- body: Tree): (List[ParamDef], Tree) = {
- val (paramLocalDefs, newParamDefs) = (for {
- p @ ParamDef(ident @ Ident(name, originalName), ptpe, mutable) <- params
- } yield {
- val newName = freshLocalName(name)
- val newOriginalName = originalName.orElse(Some(newName))
- val localDef = LocalDef(RefinedType(ptpe), mutable,
- ReplaceWithVarRef(newName, newOriginalName, new SimpleState(true), None))
- val newParamDef = ParamDef(
- Ident(newName, newOriginalName)(ident.pos), ptpe, mutable)(p.pos)
- ((name -> localDef), newParamDef)
- }).unzip
-
- val thisLocalDef =
- if (thisType == NoType) None
- else {
- Some("this" -> LocalDef(
- RefinedType(thisType, isExact = false, isNullable = false),
- false, ReplaceWithThis()))
- }
-
- val allLocalDefs = thisLocalDef ++: paramLocalDefs
-
- val scope0 = optTarget.fold(Scope.Empty)(
- target => Scope.Empty.inlining((None, target)))
- val scope = scope0.withEnv(OptEnv.Empty.withLocalDefs(allLocalDefs))
- val newBody =
- transform(body, resultType == NoType)(scope)
-
- (newParamDefs, newBody)
- }
-
- private def returnable(oldLabelName: String, resultType: Type,
- body: Tree, isStat: Boolean, usePreTransform: Boolean)(
- cont: PreTransCont)(
- implicit scope: Scope, pos: Position): TailRec[Tree] = tailcall {
- val newLabel = freshLabelName(
- if (oldLabelName.isEmpty) "inlinereturn" else oldLabelName)
-
- def doMakeTree(newBody: Tree, returnedTypes: List[Type]): Tree = {
- val refinedType =
- returnedTypes.reduce(constrainedLub(_, _, resultType))
- val returnCount = returnedTypes.size - 1
-
- tryOptimizePatternMatch(oldLabelName, refinedType,
- returnCount, newBody) getOrElse {
- Labeled(Ident(newLabel, None), refinedType, newBody)
- }
- }
-
- val info = new LabelInfo(newLabel, acceptRecords = usePreTransform)
- withState(info.returnedTypes) {
- val bodyScope = scope.withEnv(scope.env.withLabelInfo(oldLabelName, info))
-
- if (usePreTransform) {
- assert(!isStat, "Cannot use pretransform in statement position")
- tryOrRollback { cancelFun =>
- pretransformExpr(body) { tbody0 =>
- val returnedTypes0 = info.returnedTypes.value
- if (returnedTypes0.isEmpty) {
- // no return to that label, we can eliminate it
- cont(tbody0)
- } else {
- val tbody = resolveLocalDef(tbody0)
- val (newBody, returnedTypes) = tbody match {
- case PreTransRecordTree(bodyTree, origType, _) =>
- (bodyTree, (bodyTree.tpe, origType) :: returnedTypes0)
- case PreTransTree(bodyTree, tpe) =>
- (bodyTree, (bodyTree.tpe, tpe) :: returnedTypes0)
- }
- val (actualTypes, origTypes) = returnedTypes.unzip
- val refinedOrigType =
- origTypes.reduce(constrainedLub(_, _, resultType))
- actualTypes.collectFirst {
- case actualType: RecordType => actualType
- }.fold[TailRec[Tree]] {
- // None of the returned types are records
- cont(PreTransTree(
- doMakeTree(newBody, actualTypes), refinedOrigType))
- } { recordType =>
- if (actualTypes.exists(t => t != recordType && t != NothingType))
- cancelFun()
-
- val resultTree = doMakeTree(newBody, actualTypes)
-
- if (origTypes.exists(t => t != refinedOrigType && !t.isNothingType))
- cancelFun()
-
- cont(PreTransRecordTree(resultTree, refinedOrigType, cancelFun))
- }
- }
- } (bodyScope)
- } { () =>
- returnable(oldLabelName, resultType, body, isStat,
- usePreTransform = false)(cont)
- }
- } else {
- val newBody = transform(body, isStat)(bodyScope)
- val returnedTypes0 = info.returnedTypes.value.map(_._1)
- if (returnedTypes0.isEmpty) {
- // no return to that label, we can eliminate it
- cont(PreTransTree(newBody, RefinedType(newBody.tpe)))
- } else {
- val returnedTypes = newBody.tpe :: returnedTypes0
- val tree = doMakeTree(newBody, returnedTypes)
- cont(PreTransTree(tree, RefinedType(tree.tpe)))
- }
- }
- }
- }
-
- def tryOptimizePatternMatch(oldLabelName: String, refinedType: Type,
- returnCount: Int, newBody: Tree): Option[Tree] = {
- if (!oldLabelName.startsWith("matchEnd")) None
- else {
- newBody match {
- case Block(stats) =>
- @tailrec
- def createRevAlts(xs: List[Tree], acc: List[(Tree, Tree)]): List[(Tree, Tree)] = xs match {
- case If(cond, body, Skip()) :: xr =>
- createRevAlts(xr, (cond, body) :: acc)
- case remaining =>
- (EmptyTree, Block(remaining)(remaining.head.pos)) :: acc
- }
- val revAlts = createRevAlts(stats, Nil)
-
- if (revAlts.size == returnCount) {
- @tailrec
- def constructOptimized(revAlts: List[(Tree, Tree)], elsep: Tree): Option[Tree] = {
- revAlts match {
- case (cond, body) :: revAltsRest =>
- body match {
- case BlockOrAlone(prep,
- Return(result, Some(Ident(newLabel, _)))) =>
- val result1 =
- if (refinedType == NoType) keepOnlySideEffects(result)
- else result
- val prepAndResult = Block(prep :+ result1)(body.pos)
- if (cond == EmptyTree) {
- assert(elsep == EmptyTree)
- constructOptimized(revAltsRest, prepAndResult)
- } else {
- assert(elsep != EmptyTree)
- constructOptimized(revAltsRest,
- foldIf(cond, prepAndResult, elsep)(refinedType)(cond.pos))
- }
- case _ =>
- None
- }
- case Nil =>
- Some(elsep)
- }
- }
- constructOptimized(revAlts, EmptyTree)
- } else None
- case _ =>
- None
- }
- }
- }
-
- private def withBindings(bindings: List[Binding])(
- buildInner: (Scope, PreTransCont) => TailRec[Tree])(
- cont: PreTransCont)(
- implicit scope: Scope): TailRec[Tree] = {
- withNewLocalDefs(bindings) { (localDefs, cont1) =>
- val newMappings = for {
- (binding, localDef) <- bindings zip localDefs
- } yield {
- binding.name -> localDef
- }
- buildInner(scope.withEnv(scope.env.withLocalDefs(newMappings)), cont1)
- } (cont)
- }
-
- private def withBinding(binding: Binding)(
- buildInner: (Scope, PreTransCont) => TailRec[Tree])(
- cont: PreTransCont)(
- implicit scope: Scope): TailRec[Tree] = {
- withNewLocalDef(binding) { (localDef, cont1) =>
- buildInner(scope.withEnv(scope.env.withLocalDef(binding.name, localDef)),
- cont1)
- } (cont)
- }
-
- private def withNewLocalDefs(bindings: List[Binding])(
- buildInner: (List[LocalDef], PreTransCont) => TailRec[Tree])(
- cont: PreTransCont): TailRec[Tree] = {
- bindings match {
- case first :: rest =>
- withNewLocalDef(first) { (firstLocalDef, cont1) =>
- withNewLocalDefs(rest) { (restLocalDefs, cont2) =>
- buildInner(firstLocalDef :: restLocalDefs, cont2)
- } (cont1)
- } (cont)
-
- case Nil =>
- buildInner(Nil, cont)
- }
- }
-
- private def isImmutableType(tpe: Type): Boolean = tpe match {
- case RecordType(fields) =>
- fields.forall(f => !f.mutable && isImmutableType(f.tpe))
- case _ =>
- true
- }
-
- private def withNewLocalDef(binding: Binding)(
- buildInner: (LocalDef, PreTransCont) => TailRec[Tree])(
- cont: PreTransCont): TailRec[Tree] = tailcall {
- val Binding(name, originalName, declaredType, mutable, value) = binding
- implicit val pos = value.pos
-
- def withDedicatedVar(tpe: RefinedType): TailRec[Tree] = {
- val newName = freshLocalName(name)
- val newOriginalName = originalName.orElse(Some(name))
-
- val used = new SimpleState(false)
- withState(used) {
- def doBuildInner(localDef: LocalDef)(varDef: => VarDef)(
- cont: PreTransCont): TailRec[Tree] = {
- buildInner(localDef, { tinner =>
- if (used.value) {
- cont(PreTransBlock(varDef :: Nil, tinner))
- } else {
- tinner match {
- case PreTransLocalDef(`localDef`) =>
- cont(value)
- case _ if tinner.contains(localDef) =>
- cont(PreTransBlock(varDef :: Nil, tinner))
- case _ =>
- val rhsSideEffects = finishTransformStat(value)
- rhsSideEffects match {
- case Skip() =>
- cont(tinner)
- case _ =>
- if (rhsSideEffects.tpe == NothingType)
- cont(PreTransTree(rhsSideEffects, RefinedType.Nothing))
- else
- cont(PreTransBlock(rhsSideEffects :: Nil, tinner))
- }
- }
- }
- })
- }
-
- resolveLocalDef(value) match {
- case PreTransRecordTree(valueTree, valueTpe, cancelFun) =>
- val recordType = valueTree.tpe.asInstanceOf[RecordType]
- if (!isImmutableType(recordType))
- cancelFun()
- val localDef = LocalDef(valueTpe, mutable,
- ReplaceWithRecordVarRef(newName, newOriginalName, recordType,
- used, cancelFun))
- doBuildInner(localDef) {
- VarDef(Ident(newName, newOriginalName), recordType, mutable,
- valueTree)
- } (cont)
-
- case PreTransTree(valueTree, valueTpe) =>
- def doDoBuildInner(optValueTree: Option[() => Tree])(
- cont1: PreTransCont) = {
- val localDef = LocalDef(tpe, mutable, ReplaceWithVarRef(
- newName, newOriginalName, used, optValueTree))
- doBuildInner(localDef) {
- VarDef(Ident(newName, newOriginalName), tpe.base, mutable,
- optValueTree.fold(valueTree)(_()))
- } (cont1)
- }
- if (mutable) {
- doDoBuildInner(None)(cont)
- } else (valueTree match {
- case LongFromInt(arg) =>
- withNewLocalDef(
- Binding("x", None, IntType, false, PreTransTree(arg))) {
- (intLocalDef, cont1) =>
- doDoBuildInner(Some(
- () => LongFromInt(intLocalDef.newReplacement)))(
- cont1)
- } (cont)
-
- case BinaryOp(op @ (BinaryOp.Long_+ | BinaryOp.Long_-),
- LongFromInt(intLhs), LongFromInt(intRhs)) =>
- withNewLocalDefs(List(
- Binding("x", None, IntType, false, PreTransTree(intLhs)),
- Binding("y", None, IntType, false, PreTransTree(intRhs)))) {
- (intLocalDefs, cont1) =>
- val List(lhsLocalDef, rhsLocalDef) = intLocalDefs
- doDoBuildInner(Some(
- () => BinaryOp(op,
- LongFromInt(lhsLocalDef.newReplacement),
- LongFromInt(rhsLocalDef.newReplacement))))(
- cont1)
- } (cont)
-
- case _ =>
- doDoBuildInner(None)(cont)
- })
- }
- }
- }
-
- if (value.tpe.isNothingType) {
- cont(value)
- } else if (mutable) {
- withDedicatedVar(RefinedType(declaredType))
- } else {
- val refinedType = value.tpe
- value match {
- case PreTransBlock(stats, result) =>
- withNewLocalDef(binding.copy(value = result))(buildInner) { tresult =>
- cont(PreTransBlock(stats, tresult))
- }
-
- case PreTransLocalDef(localDef) if !localDef.mutable =>
- buildInner(localDef, cont)
-
- case PreTransTree(literal: Literal, _) =>
- buildInner(LocalDef(refinedType, false,
- ReplaceWithConstant(literal)), cont)
-
- case PreTransTree(VarRef(Ident(refName, refOriginalName), false), _) =>
- buildInner(LocalDef(refinedType, false,
- ReplaceWithVarRef(refName, refOriginalName,
- new SimpleState(true), None)), cont)
-
- case _ =>
- withDedicatedVar(refinedType)
- }
- }
- }
-
- /** Finds a type as precise as possible which is a supertype of lhs and rhs
- * but still a subtype of upperBound.
- * Requires that lhs and rhs be subtypes of upperBound, obviously.
- */
- private def constrainedLub(lhs: RefinedType, rhs: RefinedType,
- upperBound: Type): RefinedType = {
- if (upperBound == NoType) RefinedType(upperBound)
- else if (lhs == rhs) lhs
- else if (lhs.isNothingType) rhs
- else if (rhs.isNothingType) lhs
- else {
- RefinedType(constrainedLub(lhs.base, rhs.base, upperBound),
- false, lhs.isNullable || rhs.isNullable)
- }
- }
-
- /** Finds a type as precise as possible which is a supertype of lhs and rhs
- * but still a subtype of upperBound.
- * Requires that lhs and rhs be subtypes of upperBound, obviously.
- */
- private def constrainedLub(lhs: Type, rhs: Type, upperBound: Type): Type = {
- // TODO Improve this
- if (upperBound == NoType) upperBound
- else if (lhs == rhs) lhs
- else if (lhs == NothingType) rhs
- else if (rhs == NothingType) lhs
- else upperBound
- }
-
- /** Trampolines a pretransform */
- private def trampoline(tailrec: => TailRec[Tree]): Tree = {
- curTrampolineId += 1
-
- val myTrampolineId = curTrampolineId
-
- try {
- var rec = () => tailrec
-
- while (true) {
- try {
- return rec().result
- } catch {
- case e: RollbackException if e.trampolineId == myTrampolineId =>
- rollbacksCount += 1
- if (rollbacksCount > MaxRollbacksPerMethod)
- throw new TooManyRollbacksException
-
- usedLocalNames.clear()
- usedLocalNames ++= e.savedUsedLocalNames
- usedLabelNames.clear()
- usedLabelNames ++= e.savedUsedLabelNames
- for ((state, backup) <- statesInUse zip e.savedStates)
- state.asInstanceOf[State[Any]].restore(backup)
-
- rec = e.cont
- }
- }
-
- sys.error("Reached end of infinite loop")
- } finally {
- curTrampolineId -= 1
- }
- }
-}
-
-private[optimizer] object OptimizerCore {
-
- private final val MaxRollbacksPerMethod = 256
-
- private final class TooManyRollbacksException
- extends scala.util.control.ControlThrowable
-
- private val AnonFunctionClassPrefix = "sjsr_AnonFunction"
-
- private type CancelFun = () => Nothing
- private type PreTransCont = PreTransform => TailRec[Tree]
-
- private case class RefinedType private (base: Type, isExact: Boolean,
- isNullable: Boolean)(
- val allocationSite: Option[AllocationSite], dummy: Int = 0) {
-
- def isNothingType: Boolean = base == NothingType
- }
-
- private object RefinedType {
- def apply(base: Type, isExact: Boolean, isNullable: Boolean,
- allocationSite: Option[AllocationSite]): RefinedType =
- new RefinedType(base, isExact, isNullable)(allocationSite)
-
- def apply(base: Type, isExact: Boolean, isNullable: Boolean): RefinedType =
- RefinedType(base, isExact, isNullable, None)
-
- def apply(tpe: Type): RefinedType = tpe match {
- case BooleanType | IntType | FloatType | DoubleType | StringType |
- UndefType | NothingType | _:RecordType | NoType =>
- RefinedType(tpe, isExact = true, isNullable = false)
- case NullType =>
- RefinedType(tpe, isExact = true, isNullable = true)
- case _ =>
- RefinedType(tpe, isExact = false, isNullable = true)
- }
-
- val NoRefinedType = RefinedType(NoType)
- val Nothing = RefinedType(NothingType)
- }
-
- private class AllocationSite(private val node: Tree) {
- override def equals(that: Any): Boolean = that match {
- case that: AllocationSite => this.node eq that.node
- case _ => false
- }
-
- override def hashCode(): Int =
- System.identityHashCode(node)
-
- override def toString(): String =
- s"AllocationSite($node)"
- }
-
- private case class LocalDef(
- tpe: RefinedType,
- mutable: Boolean,
- replacement: LocalDefReplacement) {
-
- def newReplacement(implicit pos: Position): Tree = replacement match {
- case ReplaceWithVarRef(name, originalName, used, _) =>
- used.value = true
- VarRef(Ident(name, originalName), mutable)(tpe.base)
-
- case ReplaceWithRecordVarRef(_, _, _, _, cancelFun) =>
- cancelFun()
-
- case ReplaceWithThis() =>
- This()(tpe.base)
-
- case ReplaceWithConstant(value) =>
- value
-
- case TentativeClosureReplacement(_, _, _, _, _, cancelFun) =>
- cancelFun()
-
- case InlineClassBeingConstructedReplacement(_, cancelFun) =>
- cancelFun()
-
- case InlineClassInstanceReplacement(_, _, cancelFun) =>
- cancelFun()
- }
-
- def contains(that: LocalDef): Boolean = {
- (this eq that) || (replacement match {
- case TentativeClosureReplacement(_, _, _, captureLocalDefs, _, _) =>
- captureLocalDefs.exists(_.contains(that))
- case InlineClassInstanceReplacement(_, fieldLocalDefs, _) =>
- fieldLocalDefs.valuesIterator.exists(_.contains(that))
- case _ =>
- false
- })
- }
- }
-
- private sealed abstract class LocalDefReplacement
-
- private final case class ReplaceWithVarRef(name: String,
- originalName: Option[String],
- used: SimpleState[Boolean],
- longOpTree: Option[() => Tree]) extends LocalDefReplacement
-
- private final case class ReplaceWithRecordVarRef(name: String,
- originalName: Option[String],
- recordType: RecordType,
- used: SimpleState[Boolean],
- cancelFun: CancelFun) extends LocalDefReplacement
-
- private final case class ReplaceWithThis() extends LocalDefReplacement
-
- private final case class ReplaceWithConstant(
- value: Tree) extends LocalDefReplacement
-
- private final case class TentativeClosureReplacement(
- captureParams: List[ParamDef], params: List[ParamDef], body: Tree,
- captureValues: List[LocalDef],
- alreadyUsed: SimpleState[Boolean],
- cancelFun: CancelFun) extends LocalDefReplacement
-
- private final case class InlineClassBeingConstructedReplacement(
- fieldLocalDefs: Map[String, LocalDef],
- cancelFun: CancelFun) extends LocalDefReplacement
-
- private final case class InlineClassInstanceReplacement(
- recordType: RecordType,
- fieldLocalDefs: Map[String, LocalDef],
- cancelFun: CancelFun) extends LocalDefReplacement
-
- private final class LabelInfo(
- val newName: String,
- val acceptRecords: Boolean,
- /** (actualType, originalType), actualType can be a RecordType. */
- val returnedTypes: SimpleState[List[(Type, RefinedType)]] = new SimpleState(Nil))
-
- private class OptEnv(
- val localDefs: Map[String, LocalDef],
- val labelInfos: Map[String, LabelInfo]) {
-
- def withLocalDef(oldName: String, rep: LocalDef): OptEnv =
- new OptEnv(localDefs + (oldName -> rep), labelInfos)
-
- def withLocalDefs(reps: List[(String, LocalDef)]): OptEnv =
- new OptEnv(localDefs ++ reps, labelInfos)
-
- def withLabelInfo(oldName: String, info: LabelInfo): OptEnv =
- new OptEnv(localDefs, labelInfos + (oldName -> info))
-
- def withinFunction(paramLocalDefs: List[(String, LocalDef)]): OptEnv =
- new OptEnv(localDefs ++ paramLocalDefs, Map.empty)
-
- override def toString(): String = {
- "localDefs:"+localDefs.mkString("\n ", "\n ", "\n") +
- "labelInfos:"+labelInfos.mkString("\n ", "\n ", "")
- }
- }
-
- private object OptEnv {
- val Empty: OptEnv = new OptEnv(Map.empty, Map.empty)
- }
-
- private class Scope(val env: OptEnv,
- val implsBeingInlined: Set[(Option[AllocationSite], AbstractMethodID)]) {
- def withEnv(env: OptEnv): Scope =
- new Scope(env, implsBeingInlined)
-
- def inlining(impl: (Option[AllocationSite], AbstractMethodID)): Scope = {
- assert(!implsBeingInlined(impl), s"Circular inlining of $impl")
- new Scope(env, implsBeingInlined + impl)
- }
- }
-
- private object Scope {
- val Empty: Scope = new Scope(OptEnv.Empty, Set.empty)
- }
-
- /** The result of pretransformExpr().
- * It has a `tpe` as precisely refined as if a full transformExpr() had
- * been performed.
- * It is also not dependent on the environment anymore. In some sense, it
- * has "captured" its environment at definition site.
- */
- private sealed abstract class PreTransform {
- def pos: Position
- val tpe: RefinedType
-
- def contains(localDef: LocalDef): Boolean = this match {
- case PreTransBlock(_, result) =>
- result.contains(localDef)
- case PreTransLocalDef(thisLocalDef) =>
- thisLocalDef.contains(localDef)
- case _ =>
- false
- }
- }
-
- private final class PreTransBlock private (val stats: List[Tree],
- val result: PreTransLocalDef) extends PreTransform {
- def pos = result.pos
- val tpe = result.tpe
-
- assert(stats.nonEmpty)
-
- override def toString(): String =
- s"PreTransBlock($stats,$result)"
- }
-
- private object PreTransBlock {
- def apply(stats: List[Tree], result: PreTransform): PreTransform = {
- if (stats.isEmpty) result
- else {
- result match {
- case PreTransBlock(innerStats, innerResult) =>
- new PreTransBlock(stats ++ innerStats, innerResult)
- case result: PreTransLocalDef =>
- new PreTransBlock(stats, result)
- case PreTransRecordTree(tree, tpe, cancelFun) =>
- PreTransRecordTree(Block(stats :+ tree)(tree.pos), tpe, cancelFun)
- case PreTransTree(tree, tpe) =>
- PreTransTree(Block(stats :+ tree)(tree.pos), tpe)
- }
- }
- }
-
- def unapply(preTrans: PreTransBlock): Some[(List[Tree], PreTransLocalDef)] =
- Some(preTrans.stats, preTrans.result)
- }
-
- private sealed abstract class PreTransNoBlock extends PreTransform
-
- private final case class PreTransLocalDef(localDef: LocalDef)(
- implicit val pos: Position) extends PreTransNoBlock {
- val tpe: RefinedType = localDef.tpe
- }
-
- private sealed abstract class PreTransGenTree extends PreTransNoBlock
-
- private final case class PreTransRecordTree(tree: Tree,
- tpe: RefinedType, cancelFun: CancelFun) extends PreTransGenTree {
- def pos = tree.pos
-
- assert(tree.tpe.isInstanceOf[RecordType],
- s"Cannot create a PreTransRecordTree with non-record type ${tree.tpe}")
- }
-
- private final case class PreTransTree(tree: Tree,
- tpe: RefinedType) extends PreTransGenTree {
- def pos: Position = tree.pos
-
- assert(!tree.tpe.isInstanceOf[RecordType],
- s"Cannot create a Tree with record type ${tree.tpe}")
- }
-
- private object PreTransTree {
- def apply(tree: Tree): PreTransTree =
- PreTransTree(tree, RefinedType(tree.tpe))
- }
-
- private final case class Binding(name: String, originalName: Option[String],
- declaredType: Type, mutable: Boolean, value: PreTransform)
-
- private object NumberLiteral {
- def unapply(tree: Literal): Option[Double] = tree match {
- case DoubleLiteral(v) => Some(v)
- case IntLiteral(v) => Some(v.toDouble)
- case FloatLiteral(v) => Some(v.toDouble)
- case _ => None
- }
- }
-
- private object LongFromInt {
- def apply(x: Tree)(implicit pos: Position): Tree = x match {
- case IntLiteral(v) => LongLiteral(v)
- case _ => UnaryOp(UnaryOp.IntToLong, x)
- }
-
- def unapply(tree: Tree): Option[Tree] = tree match {
- case LongLiteral(v) if v.toInt == v => Some(IntLiteral(v.toInt)(tree.pos))
- case UnaryOp(UnaryOp.IntToLong, x) => Some(x)
- case _ => None
- }
- }
-
- private object AndThen {
- def apply(lhs: Tree, rhs: Tree)(implicit pos: Position): Tree =
- If(lhs, rhs, BooleanLiteral(false))(BooleanType)
- }
-
- /** Tests whether `x + y` is valid without falling out of range. */
- private def canAddLongs(x: Long, y: Long): Boolean =
- if (y >= 0) x+y >= x
- else x+y < x
-
- /** Tests whether `x - y` is valid without falling out of range. */
- private def canSubtractLongs(x: Long, y: Long): Boolean =
- if (y >= 0) x-y <= x
- else x-y > x
-
- /** Tests whether `-x` is valid without falling out of range. */
- private def canNegateLong(x: Long): Boolean =
- x != Long.MinValue
-
- private object Intrinsics {
- final val ArrayCopy = 1
- final val IdentityHashCode = ArrayCopy + 1
-
- final val PropertiesOf = IdentityHashCode + 1
-
- final val LongToString = PropertiesOf + 1
- final val LongCompare = LongToString + 1
- final val LongBitCount = LongCompare + 1
- final val LongSignum = LongBitCount + 1
- final val LongLeading0s = LongSignum + 1
- final val LongTrailing0s = LongLeading0s + 1
- final val LongToBinStr = LongTrailing0s + 1
- final val LongToHexStr = LongToBinStr + 1
- final val LongToOctalStr = LongToHexStr + 1
-
- final val ByteArrayToInt8Array = LongToOctalStr + 1
- final val ShortArrayToInt16Array = ByteArrayToInt8Array + 1
- final val CharArrayToUint16Array = ShortArrayToInt16Array + 1
- final val IntArrayToInt32Array = CharArrayToUint16Array + 1
- final val FloatArrayToFloat32Array = IntArrayToInt32Array + 1
- final val DoubleArrayToFloat64Array = FloatArrayToFloat32Array + 1
-
- final val Int8ArrayToByteArray = DoubleArrayToFloat64Array + 1
- final val Int16ArrayToShortArray = Int8ArrayToByteArray + 1
- final val Uint16ArrayToCharArray = Int16ArrayToShortArray + 1
- final val Int32ArrayToIntArray = Uint16ArrayToCharArray + 1
- final val Float32ArrayToFloatArray = Int32ArrayToIntArray + 1
- final val Float64ArrayToDoubleArray = Float32ArrayToFloatArray + 1
-
- val intrinsics: Map[String, Int] = Map(
- "jl_System$.arraycopy__O__I__O__I__I__V" -> ArrayCopy,
- "jl_System$.identityHashCode__O__I" -> IdentityHashCode,
-
- "sjsr_package$.propertiesOf__sjs_js_Any__sjs_js_Array" -> PropertiesOf,
-
- "jl_Long$.toString__J__T" -> LongToString,
- "jl_Long$.compare__J__J__I" -> LongCompare,
- "jl_Long$.bitCount__J__I" -> LongBitCount,
- "jl_Long$.signum__J__J" -> LongSignum,
- "jl_Long$.numberOfLeadingZeros__J__I" -> LongLeading0s,
- "jl_Long$.numberOfTrailingZeros__J__I" -> LongTrailing0s,
- "jl_long$.toBinaryString__J__T" -> LongToBinStr,
- "jl_Long$.toHexString__J__T" -> LongToHexStr,
- "jl_Long$.toOctalString__J__T" -> LongToOctalStr,
-
- "sjs_js_typedarray_package$.byteArray2Int8Array__AB__sjs_js_typedarray_Int8Array" -> ByteArrayToInt8Array,
- "sjs_js_typedarray_package$.shortArray2Int16Array__AS__sjs_js_typedarray_Int16Array" -> ShortArrayToInt16Array,
- "sjs_js_typedarray_package$.charArray2Uint16Array__AC__sjs_js_typedarray_Uint16Array" -> CharArrayToUint16Array,
- "sjs_js_typedarray_package$.intArray2Int32Array__AI__sjs_js_typedarray_Int32Array" -> IntArrayToInt32Array,
- "sjs_js_typedarray_package$.floatArray2Float32Array__AF__sjs_js_typedarray_Float32Array" -> FloatArrayToFloat32Array,
- "sjs_js_typedarray_package$.doubleArray2Float64Array__AD__sjs_js_typedarray_Float64Array" -> DoubleArrayToFloat64Array,
-
- "sjs_js_typedarray_package$.int8Array2ByteArray__sjs_js_typedarray_Int8Array__AB" -> Int8ArrayToByteArray,
- "sjs_js_typedarray_package$.int16Array2ShortArray__sjs_js_typedarray_Int16Array__AS" -> Int16ArrayToShortArray,
- "sjs_js_typedarray_package$.uint16Array2CharArray__sjs_js_typedarray_Uint16Array__AC" -> Uint16ArrayToCharArray,
- "sjs_js_typedarray_package$.int32Array2IntArray__sjs_js_typedarray_Int32Array__AI" -> Int32ArrayToIntArray,
- "sjs_js_typedarray_package$.float32Array2FloatArray__sjs_js_typedarray_Float32Array__AF" -> Float32ArrayToFloatArray,
- "sjs_js_typedarray_package$.float64Array2DoubleArray__sjs_js_typedarray_Float64Array__AD" -> Float64ArrayToDoubleArray
- ).withDefaultValue(-1)
- }
-
- private def getIntrinsicCode(target: AbstractMethodID): Int =
- Intrinsics.intrinsics(target.toString)
-
- private trait State[A] {
- def makeBackup(): A
- def restore(backup: A): Unit
- }
-
- private class SimpleState[A](var value: A) extends State[A] {
- def makeBackup(): A = value
- def restore(backup: A): Unit = value = backup
- }
-
- trait AbstractMethodID {
- def inlineable: Boolean
- def isTraitImplForwarder: Boolean
- }
-
- /** Parts of [[GenIncOptimizer#MethodImpl]] with decisions about optimizations. */
- abstract class MethodImpl {
- def encodedName: String
- def optimizerHints: OptimizerHints
- def originalDef: MethodDef
- def thisType: Type
-
- var inlineable: Boolean = false
- var isTraitImplForwarder: Boolean = false
-
- protected def updateInlineable(): Unit = {
- val MethodDef(Ident(methodName, _), params, _, body) = originalDef
-
- isTraitImplForwarder = body match {
- // Shape of forwarders to trait impls
- case TraitImplApply(impl, method, args) =>
- ((args.size == params.size + 1) &&
- (args.head.isInstanceOf[This]) &&
- (args.tail.zip(params).forall {
- case (VarRef(Ident(aname, _), _),
- ParamDef(Ident(pname, _), _, _)) => aname == pname
- case _ => false
- }))
-
- case _ => false
- }
-
- inlineable = optimizerHints.hasInlineAnnot || isTraitImplForwarder || {
- val MethodDef(_, params, _, body) = originalDef
- body match {
- case _:Skip | _:This | _:Literal => true
-
- // Shape of accessors
- case Select(This(), _, _) if params.isEmpty => true
- case Assign(Select(This(), _, _), VarRef(_, _))
- if params.size == 1 => true
-
- // Shape of trivial call-super constructors
- case Block(stats)
- if params.isEmpty && isConstructorName(encodedName) &&
- stats.forall(isTrivialConstructorStat) => true
-
- // Simple method
- case SimpleMethodBody() => true
-
- case _ => false
- }
- }
- }
- }
-
- private def isTrivialConstructorStat(stat: Tree): Boolean = stat match {
- case This() =>
- true
- case StaticApply(This(), _, _, Nil) =>
- true
- case TraitImplApply(_, Ident(methodName, _), This() :: Nil) =>
- methodName.contains("__$init$__")
- case _ =>
- false
- }
-
- private object SimpleMethodBody {
- @tailrec
- def unapply(body: Tree): Boolean = body match {
- case New(_, _, args) => areSimpleArgs(args)
- case Apply(receiver, _, args) => areSimpleArgs(receiver :: args)
- case StaticApply(receiver, _, _, args) => areSimpleArgs(receiver :: args)
- case TraitImplApply(_, _, args) => areSimpleArgs(args)
- case Select(qual, _, _) => isSimpleArg(qual)
- case IsInstanceOf(inner, _) => isSimpleArg(inner)
-
- case Block(List(inner, Undefined())) =>
- unapply(inner)
-
- case Unbox(inner, _) => unapply(inner)
- case AsInstanceOf(inner, _) => unapply(inner)
-
- case _ => isSimpleArg(body)
- }
-
- private def areSimpleArgs(args: List[Tree]): Boolean =
- args.forall(isSimpleArg)
-
- @tailrec
- private def isSimpleArg(arg: Tree): Boolean = arg match {
- case New(_, _, Nil) => true
- case Apply(receiver, _, Nil) => isTrivialArg(receiver)
- case StaticApply(receiver, _, _, Nil) => isTrivialArg(receiver)
- case TraitImplApply(_, _, Nil) => true
-
- case ArrayLength(array) => isTrivialArg(array)
- case ArraySelect(array, index) => isTrivialArg(array) && isTrivialArg(index)
-
- case Unbox(inner, _) => isSimpleArg(inner)
- case AsInstanceOf(inner, _) => isSimpleArg(inner)
-
- case _ =>
- isTrivialArg(arg)
- }
-
- private def isTrivialArg(arg: Tree): Boolean = arg match {
- case _:VarRef | _:This | _:Literal | _:LoadModule =>
- true
- case _ =>
- false
- }
- }
-
- private object BlockOrAlone {
- def unapply(tree: Tree): Some[(List[Tree], Tree)] = Some(tree match {
- case Block(init :+ last) => (init, last)
- case _ => (Nil, tree)
- })
- }
-
- /** Recreates precise [[Infos.MethodInfo]] from the optimized [[MethodDef]]. */
- private def recreateInfo(methodDef: MethodDef): Infos.MethodInfo = {
- new RecreateInfoTraverser().recreateInfo(methodDef)
- }
-
- private final class RecreateInfoTraverser extends Traversers.Traverser {
- import RecreateInfoTraverser._
-
- private val calledMethods = mutable.Map.empty[String, mutable.Set[String]]
- private val calledMethodsStatic = mutable.Map.empty[String, mutable.Set[String]]
- private val instantiatedClasses = mutable.Set.empty[String]
- private val accessedModules = mutable.Set.empty[String]
- private val accessedClassData = mutable.Set.empty[String]
-
- def recreateInfo(methodDef: MethodDef): Infos.MethodInfo = {
- traverse(methodDef.body)
- Infos.MethodInfo(
- encodedName = methodDef.name.name,
- calledMethods = calledMethods.toMap.mapValues(_.toList),
- calledMethodsStatic = calledMethodsStatic.toMap.mapValues(_.toList),
- instantiatedClasses = instantiatedClasses.toList,
- accessedModules = accessedModules.toList,
- accessedClassData = accessedClassData.toList)
- }
-
- private def addCalledMethod(container: String, methodName: String): Unit =
- calledMethods.getOrElseUpdate(container, mutable.Set.empty) += methodName
-
- private def addCalledMethodStatic(container: String, methodName: String): Unit =
- calledMethodsStatic.getOrElseUpdate(container, mutable.Set.empty) += methodName
-
- private def refTypeToClassData(tpe: ReferenceType): String = tpe match {
- case ClassType(cls) => cls
- case ArrayType(base, _) => base
- }
-
- def addAccessedClassData(encodedName: String): Unit = {
- if (!AlwaysPresentClassData.contains(encodedName))
- accessedClassData += encodedName
- }
-
- def addAccessedClassData(tpe: ReferenceType): Unit =
- addAccessedClassData(refTypeToClassData(tpe))
-
- override def traverse(tree: Tree): Unit = {
- tree match {
- case New(ClassType(cls), ctor, _) =>
- instantiatedClasses += cls
- addCalledMethodStatic(cls, ctor.name)
-
- case Apply(receiver, method, _) =>
- receiver.tpe match {
- case ClassType(cls) if !Definitions.HijackedClasses.contains(cls) =>
- addCalledMethod(cls, method.name)
- case AnyType =>
- addCalledMethod(Definitions.ObjectClass, method.name)
- case ArrayType(_, _) if method.name != "clone__O" =>
- /* clone__O is overridden in the pseudo Array classes and is
- * always kept anyway, because it is in scalajsenv.js.
- * Other methods delegate to Object, which we can model with
- * a static call to Object.method.
- */
- addCalledMethodStatic(Definitions.ObjectClass, method.name)
- case _ =>
- // Nothing to do
- }
-
- case StaticApply(_, ClassType(cls), method, _) =>
- addCalledMethodStatic(cls, method.name)
- case TraitImplApply(ClassType(impl), method, _) =>
- addCalledMethodStatic(impl, method.name)
-
- case LoadModule(ClassType(cls)) =>
- accessedModules += cls.stripSuffix("$")
-
- case NewArray(tpe, _) =>
- addAccessedClassData(tpe)
- case ArrayValue(tpe, _) =>
- addAccessedClassData(tpe)
- case IsInstanceOf(_, cls) =>
- addAccessedClassData(cls)
- case AsInstanceOf(_, cls) =>
- addAccessedClassData(cls)
- case ClassOf(cls) =>
- addAccessedClassData(cls)
-
- case _ =>
- }
- super.traverse(tree)
- }
- }
-
- private object RecreateInfoTraverser {
- /** Class data that are never eliminated by dce, so we don't need to
- * record them.
- */
- private val AlwaysPresentClassData = {
- import Definitions._
- Set("V", "Z", "C", "B", "S", "I", "J", "F", "D",
- ObjectClass, StringClass)
- }
- }
-
- private def exceptionMsg(myself: AbstractMethodID,
- attemptedInlining: List[AbstractMethodID]) = {
- val buf = new StringBuilder()
-
- buf.append("The Scala.js optimizer crashed while optimizing " + myself)
-
- buf.append("\nMethods attempted to inline:\n")
-
- for (m <- attemptedInlining) {
- buf.append("* ")
- buf.append(m)
- buf.append('\n')
- }
-
- buf.toString
- }
-
- private class RollbackException(val trampolineId: Int,
- val savedUsedLocalNames: Set[String],
- val savedUsedLabelNames: Set[String],
- val savedStates: List[Any],
- val cont: () => TailRec[Tree]) extends ControlThrowable
-
- class OptimizeException(val myself: AbstractMethodID,
- val attemptedInlining: List[AbstractMethodID], cause: Throwable
- ) extends Exception(exceptionMsg(myself, attemptedInlining), cause)
-
-}