/* ____ ____ ____ ____ ______ *\ ** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** ** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** ** /_____/\____/\___/\____/____/ ** \* */ // $Id$ package scalac.transformer; import scalac.Global; import scalac.Phase; import scalac.PhaseDescriptor; import scalac.Unit; import scalac.ast.Tree; import scalac.ast.GenTransformer; import scalac.symtab.LabelSymbol; import scalac.symtab.Symbol; import scalac.symtab.Type; import scalac.util.Debug; /** * A Tail Call Transformer * * @author Erik Stenman * @version 1.0 * * What it does: * * Finds method calls in tail-position and replaces them with jumps. * A call is in a tail-position if it is the last instruction to be * executed in the body of a method. This is done by recursing over * the trees that may contain calls in tail-position (trees that can't * contain such calls are not transformed). * * Self-recursive calls in tail-position are replaced by jumps to a * label at the beginning of the method. As the JVM provides no way to * jump from a method to another one, non-recursive calls in * tail-position are not optimized. * * A method call is self-recursive if it calls the current method on * the current instance and the method is final (otherwise, it could * be a call to an overridden method in a subclass). Furthermore, If * the method has type parameters, the call must contain these * parameters as type arguments. * * If a method contains self-recursive calls, a label is added to at * the beginning of its body and the calls are replaced by jumps to * that label. */ public class TailCallPhase extends Phase { //######################################################################## // Public Constructors /** Initializes this instance. */ public TailCallPhase(Global global, PhaseDescriptor descriptor) { super(global, descriptor); } //######################################################################## // Public Methods /** Applies this phase to the given compilation units. */ public void apply(Unit[] units) { treeTransformer.apply(units); } //######################################################################## // Private Classes /** The tree transformer */ private final GenTransformer treeTransformer = new GenTransformer(global) { /** The current method */ private Symbol method; /** The current tail-call label */ private Symbol label; /** The expected type arguments of self-recursive calls */ private Type[] types; /** Transforms the given tree. */ public Tree transform(Tree tree) { switch (tree) { case DefDef(_, _, _, _, _, Tree rhs): assert method == null: Debug.show(method) + " -- " + tree; method = tree.symbol(); if (method.isMethodFinal()) { label = new LabelSymbol(method); types = Type.EMPTY_ARRAY; Type type = method.type(); switch (type) { case PolyType(Symbol[] tparams, Type result): types = Symbol.type(tparams); type = result; } label.setInfo(type.cloneType(method, label)); rhs = transform(rhs); if (label.isAccessed()) { rhs = gen.LabelDef(label, method.valueParams(), rhs); tree = gen.DefDef(method, rhs); } types = null; label = null; } method = null; return tree; case Block(Tree[] stats, Tree value): return gen.Block(tree.pos, stats, transform(value)); case If(Tree cond, Tree thenp, Tree elsep): Type type = tree.type(); thenp = transform(thenp); elsep = transform(elsep); return gen.If(tree.pos, cond, thenp, elsep, type); case Switch(Tree test, int[] tags, Tree[] bodies, Tree otherwise): Type type = tree.type(); bodies = transform(bodies); otherwise = transform(otherwise); return gen.Switch(tree.pos, test, tags, bodies,otherwise,type); case Apply(TypeApply(Tree fun, Tree[] targs), Tree[] vargs): if (!Type.isSameAs(Tree.typeOf(targs), types)) return tree; return transform(tree, fun, vargs); case Apply(Tree fun, Tree[] vargs): return transform(tree, fun, vargs); case ClassDef(_, _, _, _, _, _): case PackageDef(_, _): case LabelDef(_, _, _): case Return(_): return super.transform(tree); case Empty: case ValDef(_, _, _, _): case Assign(_, _): case New(_): case Super(_, _): case This(_): case Select(_, _): case Ident(_): case Literal(_): case TypeTerm(): return tree; default: throw Debug.abort("illegal case", tree); } } /** Transforms the given function call. */ private Tree transform(Tree tree, Tree fun, Tree[] vargs) { Symbol symbol = fun.symbol(); if (symbol != method) return tree; switch (fun) { case Select(Tree qual, _): if (!isReferenceToThis(qual, method.owner())) return tree; return gen.Apply(tree.pos, gen.Ident(qual.pos, label), vargs); case Ident(_): assert fun.symbol().isLabel(); return tree; default: throw Debug.abort("illegal case", fun); } } /** * Returns true if the tree represents the current instance of * given class. */ private boolean isReferenceToThis(Tree tree, Symbol clasz) { switch (tree) { case This(_): assert tree.symbol() == clasz: tree +" -- "+ Debug.show(clasz); return true; case Select(Tree qual, _): if (!clasz.isModuleClass()) return false; if (tree.symbol() != clasz.module()) return false; return isReferenceToThis(qual, clasz.owner()); case Ident(_): if (!clasz.isModuleClass()) return false; if (tree.symbol() != clasz.module()) return false; return true; default: return false; } } }; //######################################################################## }