From eb2627c4d282b5e4727e7e67fc7656409119cc96 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Tue, 5 Nov 2013 10:58:42 +0100 Subject: Don't try to eliminate tail calls in constructors. --- src/compiler/scala/tools/nsc/transform/TailCalls.scala | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/compiler/scala/tools/nsc/transform/TailCalls.scala b/src/compiler/scala/tools/nsc/transform/TailCalls.scala index b471d16ddd..cf38390e37 100644 --- a/src/compiler/scala/tools/nsc/transform/TailCalls.scala +++ b/src/compiler/scala/tools/nsc/transform/TailCalls.scala @@ -245,6 +245,11 @@ abstract class TailCalls extends Transform { else if (!receiverIsSame) failHere("it changes type of 'this' on a polymorphic recursive call") else rewriteTailCall(receiver) } + + def isEligible(tree: DefDef) = { + val sym = tree.symbol + !(sym.hasAccessorFlag || sym.isConstructor) + } tree match { case ValDef(_, _, _, _) => @@ -253,7 +258,7 @@ abstract class TailCalls extends Transform { super.transform(tree) - case dd @ DefDef(_, name, _, vparamss0, _, rhs0) if !dd.symbol.hasAccessorFlag => + case dd @ DefDef(_, name, _, vparamss0, _, rhs0) if isEligible(dd) => val newCtx = new DefDefTailContext(dd) if (newCtx.isMandatory && !(newCtx containsRecursiveCall rhs0)) unit.error(tree.pos, "@tailrec annotated method contains no recursive calls") -- cgit v1.2.3 From 67f7df27a8f4f97929140904eec237541d2b65ab Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Tue, 5 Nov 2013 10:53:15 +0100 Subject: Optimize tailcall elimination From the "if a tree falls" department: don't bother create a finely distinguished error messages about why the transform is inapplicable if the current context doesn't demand it. --- src/compiler/scala/tools/nsc/transform/TailCalls.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/compiler/scala/tools/nsc/transform/TailCalls.scala b/src/compiler/scala/tools/nsc/transform/TailCalls.scala index cf38390e37..05fa65eb9c 100644 --- a/src/compiler/scala/tools/nsc/transform/TailCalls.scala +++ b/src/compiler/scala/tools/nsc/transform/TailCalls.scala @@ -237,7 +237,8 @@ abstract class TailCalls extends Transform { if (!ctx.isEligible) fail("it is neither private nor final so can be overridden") else if (!isRecursiveCall) { - if (receiverIsSuper) failHere("it contains a recursive call targeting a supertype") + if (ctx.isMandatory && receiverIsSuper) // OPT expensive check, avoid unless we will actually report the error + failHere("it contains a recursive call targeting a supertype") else failHere(defaultReason) } else if (!matchesTypeArgs) failHere("it is called recursively with different type arguments") -- cgit v1.2.3 From c30cf0ee814337109941876b08323a42b440a724 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Sat, 2 Nov 2013 21:58:04 +0100 Subject: Optimization in TailCalls Only store the position and reason for a failure to tailcall transform a method if we ever need to report it, ie, if the method was annotated with @tailrec. Saves object hashing and map updates, profiling suggests that this reduces the tailcalls phase from about 2% of compilation time to about 1%. Also, clear the maps eagerly after each compilation unit, rather than letting them accumulate entries for the entire run. Working with smaller maps can't hurt. --- src/compiler/scala/tools/nsc/transform/TailCalls.scala | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/compiler/scala/tools/nsc/transform/TailCalls.scala b/src/compiler/scala/tools/nsc/transform/TailCalls.scala index 05fa65eb9c..5973c70583 100644 --- a/src/compiler/scala/tools/nsc/transform/TailCalls.scala +++ b/src/compiler/scala/tools/nsc/transform/TailCalls.scala @@ -128,6 +128,7 @@ abstract class TailCalls extends Transform { logResult(msg)(method.newValue(nme.THIS, pos, SYNTHETIC) setInfo currentClass.typeOfThis) } override def toString = s"${method.name} tparams=$tparams tailPos=$tailPos label=$label label info=${label.info}" + } object EmptyTailContext extends TailContext { @@ -185,6 +186,18 @@ abstract class TailCalls extends Transform { private def noTailContext() = new ClonedTailContext(ctx, tailPos = false) private def yesTailContext() = new ClonedTailContext(ctx, tailPos = true) + + override def transformUnit(unit: CompilationUnit): Unit = { + try { + super.transformUnit(unit) + } finally { + // OPT clear these after each compilation unit + failPositions.clear() + failReasons.clear() + accessed.clear() + } + } + /** Rewrite this tree to contain no tail recursive calls */ def transform(tree: Tree, nctx: TailContext): Tree = { val saved = ctx @@ -218,12 +231,12 @@ abstract class TailCalls extends Transform { */ def fail(reason: String) = { debuglog("Cannot rewrite recursive call at: " + fun.pos + " because: " + reason) - failReasons(ctx) = reason + if (ctx.isMandatory) failReasons(ctx) = reason treeCopy.Apply(tree, noTailTransform(target), transformArgs) } /* Position of failure is that of the tree being considered. */ def failHere(reason: String) = { - failPositions(ctx) = fun.pos + if (ctx.isMandatory) failPositions(ctx) = fun.pos fail(reason) } def rewriteTailCall(recv: Tree): Tree = { -- cgit v1.2.3