8013912: Nashorn needs to reuse temporary symbols
authorattila
Wed, 08 May 2013 15:51:36 +0200
changeset 17525 6582a7788183
parent 17524 703643aeb0d6
child 17526 3194676cb555
8013912: Nashorn needs to reuse temporary symbols Reviewed-by: jlaskey, lagergren
nashorn/src/jdk/nashorn/internal/codegen/Attr.java
nashorn/src/jdk/nashorn/internal/codegen/CompilationPhase.java
nashorn/src/jdk/nashorn/internal/codegen/Compiler.java
nashorn/src/jdk/nashorn/internal/codegen/CompilerConstants.java
nashorn/src/jdk/nashorn/internal/codegen/FinalizeTypes.java
nashorn/src/jdk/nashorn/internal/ir/AccessNode.java
nashorn/src/jdk/nashorn/internal/ir/BlockLexicalContext.java
nashorn/src/jdk/nashorn/internal/ir/CallNode.java
nashorn/src/jdk/nashorn/internal/ir/IdentNode.java
nashorn/src/jdk/nashorn/internal/ir/IndexNode.java
nashorn/src/jdk/nashorn/internal/ir/Node.java
nashorn/src/jdk/nashorn/internal/ir/RuntimeNode.java
nashorn/src/jdk/nashorn/internal/ir/Symbol.java
nashorn/src/jdk/nashorn/internal/ir/TemporarySymbols.java
nashorn/src/jdk/nashorn/internal/ir/TypeOverride.java
--- a/nashorn/src/jdk/nashorn/internal/codegen/Attr.java	Tue May 07 14:43:17 2013 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/Attr.java	Wed May 08 15:51:36 2013 +0200
@@ -33,7 +33,6 @@
 import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
 import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
 import static jdk.nashorn.internal.codegen.CompilerConstants.SWITCH_TAG_PREFIX;
-import static jdk.nashorn.internal.codegen.CompilerConstants.TEMP_PREFIX;
 import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
 import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
 import static jdk.nashorn.internal.ir.Symbol.IS_CONSTANT;
@@ -43,7 +42,6 @@
 import static jdk.nashorn.internal.ir.Symbol.IS_LET;
 import static jdk.nashorn.internal.ir.Symbol.IS_PARAM;
 import static jdk.nashorn.internal.ir.Symbol.IS_SCOPE;
-import static jdk.nashorn.internal.ir.Symbol.IS_TEMP;
 import static jdk.nashorn.internal.ir.Symbol.IS_THIS;
 import static jdk.nashorn.internal.ir.Symbol.IS_VAR;
 import static jdk.nashorn.internal.ir.Symbol.KINDMASK;
@@ -55,7 +53,6 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
-
 import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.ir.AccessNode;
 import jdk.nashorn.internal.ir.BinaryNode;
@@ -81,6 +78,7 @@
 import jdk.nashorn.internal.ir.Statement;
 import jdk.nashorn.internal.ir.SwitchNode;
 import jdk.nashorn.internal.ir.Symbol;
+import jdk.nashorn.internal.ir.TemporarySymbols;
 import jdk.nashorn.internal.ir.TernaryNode;
 import jdk.nashorn.internal.ir.TryNode;
 import jdk.nashorn.internal.ir.UnaryNode;
@@ -134,10 +132,13 @@
     private static final DebugLogger LOG   = new DebugLogger("attr");
     private static final boolean     DEBUG = LOG.isEnabled();
 
+    private final TemporarySymbols temporarySymbols;
+
     /**
      * Constructor.
      */
-    Attr() {
+    Attr(final TemporarySymbols temporarySymbols) {
+        this.temporarySymbols = temporarySymbols;
         this.localDefs   = new ArrayDeque<>();
         this.localUses   = new ArrayDeque<>();
         this.returnTypes = new ArrayDeque<>();
@@ -410,7 +411,7 @@
             final boolean anonymous = functionNode.isAnonymous();
             final String  name      = anonymous ? null : functionNode.getIdent().getName();
             if (anonymous || body.getExistingSymbol(name) != null) {
-                newFunctionNode = (FunctionNode)Attr.ensureSymbol(lc, body, FunctionNode.FUNCTION_TYPE, newFunctionNode);
+                newFunctionNode = (FunctionNode)ensureSymbol(lc, FunctionNode.FUNCTION_TYPE, newFunctionNode);
             } else {
                 assert name != null;
                 final Symbol self = body.getExistingSymbol(name);
@@ -421,7 +422,7 @@
 
         //unknown parameters are promoted to object type.
         newFunctionNode = finalizeParameters(newFunctionNode);
-        finalizeTypes(newFunctionNode);
+        newFunctionNode = finalizeTypes(newFunctionNode);
         for (final Symbol symbol : newFunctionNode.getDeclaredSymbols()) {
             if (symbol.getSymbolType().isUnknown()) {
                 symbol.setType(Type.OBJECT);
@@ -533,8 +534,7 @@
 
         setBlockScope(name, symbol);
 
-        if (symbol != null && !identNode.isInitializedHere()) {
-
+        if (!identNode.isInitializedHere()) {
             symbol.increaseUseCount();
         }
         addLocalUse(identNode.getName());
@@ -790,7 +790,7 @@
             // var x; with no init will be treated like a use of x by
             // leaveIdentNode unless we remove the name from the localdef list.
             removeLocalDef(name);
-            return newVarNode.setSymbol(lc, symbol);
+            return end(newVarNode.setSymbol(lc, symbol));
         }
 
         addLocalDef(name);
@@ -828,10 +828,10 @@
     @Override
     public Node leaveDECINC(final UnaryNode unaryNode) {
         // @see assignOffset
-        ensureAssignmentSlots(getLexicalContext().getCurrentFunction(), unaryNode.rhs());
+        final UnaryNode newUnaryNode = unaryNode.setRHS(ensureAssignmentSlots(unaryNode.rhs()));
         final Type type = arithType();
-        newType(unaryNode.rhs().getSymbol(), type);
-        return end(ensureSymbol(type, unaryNode));
+        newType(newUnaryNode.rhs().getSymbol(), type);
+        return end(ensureSymbol(type, newUnaryNode));
     }
 
     @Override
@@ -1384,9 +1384,7 @@
             final Symbol paramSymbol = defineSymbol(body, param.getName(), flags);
             assert paramSymbol != null;
 
-            if (paramSymbol != null) {
-                newType(paramSymbol, callSiteParamType == null ? Type.UNKNOWN : callSiteParamType);
-            }
+            newType(paramSymbol, callSiteParamType == null ? Type.UNKNOWN : callSiteParamType);
 
             LOG.info("Initialized param ", pos, "=", paramSymbol);
             pos++;
@@ -1521,19 +1519,25 @@
      *
      * see NASHORN-258
      *
-     * @param functionNode   the current function node (has to be passed as it changes in the visitor below)
      * @param assignmentDest the destination node of the assignment, e.g. lhs for binary nodes
      */
-    private static void ensureAssignmentSlots(final FunctionNode functionNode, final Node assignmentDest) {
-        assignmentDest.accept(new NodeVisitor() {
+    private Node ensureAssignmentSlots(final Node assignmentDest) {
+        final LexicalContext attrLexicalContext = getLexicalContext();
+        return assignmentDest.accept(new NodeVisitor() {
             @Override
             public Node leaveIndexNode(final IndexNode indexNode) {
                 assert indexNode.getSymbol().isTemp();
                 final Node index = indexNode.getIndex();
                 //only temps can be set as needing slots. the others will self resolve
                 //it is illegal to take a scope var and force it to be a slot, that breaks
-                if (index.getSymbol().isTemp() && !index.getSymbol().isConstant()) {
-                     index.getSymbol().setNeedsSlot(true);
+                Symbol indexSymbol = index.getSymbol();
+                if (indexSymbol.isTemp() && !indexSymbol.isConstant() && !indexSymbol.hasSlot()) {
+                    if(indexSymbol.isShared()) {
+                        indexSymbol = temporarySymbols.createUnshared(indexSymbol);
+                    }
+                    indexSymbol.setNeedsSlot(true);
+                    attrLexicalContext.getCurrentBlock().putSymbol(attrLexicalContext, indexSymbol);
+                    return indexNode.setIndex(index.setSymbol(attrLexicalContext, indexSymbol));
                 }
                 return indexNode;
             }
@@ -1558,22 +1562,30 @@
      *
      * @param functionNode
      */
-    private static void finalizeTypes(final FunctionNode functionNode) {
+    private FunctionNode finalizeTypes(final FunctionNode functionNode) {
         final Set<Node> changed = new HashSet<>();
+        FunctionNode currentFunctionNode = functionNode;
         do {
             changed.clear();
-            functionNode.accept(new NodeVisitor() {
+            final FunctionNode newFunctionNode = (FunctionNode)currentFunctionNode.accept(new NodeVisitor() {
 
-                private void widen(final Node node, final Type to) {
+                private Node widen(final Node node, final Type to) {
                     if (node instanceof LiteralNode) {
-                        return;
+                        return node;
                     }
                     Type from = node.getType();
                     if (!Type.areEquivalent(from, to) && Type.widest(from, to) == to) {
-                        LOG.fine("Had to post pass widen '", node, "' " + Debug.id(node), " from ", node.getType(), " to ", to);
-                        newType(node.getSymbol(), to);
-                        changed.add(node);
+                        LOG.fine("Had to post pass widen '", node, "' ", Debug.id(node), " from ", node.getType(), " to ", to);
+                        Symbol symbol = node.getSymbol();
+                        if(symbol.isShared() && symbol.wouldChangeType(to)) {
+                            symbol = temporarySymbols.getTypedTemporarySymbol(to);
+                        }
+                        newType(symbol, to);
+                        final Node newNode = node.setSymbol(getLexicalContext(), symbol);
+                        changed.add(newNode);
+                        return newNode;
                     }
+                    return node;
                 }
 
                 @Override
@@ -1599,20 +1611,23 @@
                 @Override
                 public Node leaveBinaryNode(final BinaryNode binaryNode) {
                     final Type widest = Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType());
+                    BinaryNode newBinaryNode = binaryNode;
                     switch (binaryNode.tokenType()) {
                     default:
                         if (!binaryNode.isAssignment() || binaryNode.isSelfModifying()) {
                             break;
                         }
-                        widen(binaryNode.lhs(), widest);
+                        newBinaryNode = newBinaryNode.setLHS(widen(newBinaryNode.lhs(), widest));
                     case ADD:
-                        widen(binaryNode, widest);
-                        break;
+                        newBinaryNode = (BinaryNode)widen(newBinaryNode, widest);
                     }
-                    return binaryNode;
+                    return newBinaryNode;
                 }
             });
+            getLexicalContext().replace(currentFunctionNode, newFunctionNode);
+            currentFunctionNode = newFunctionNode;
         } while (!changed.isEmpty());
+        return currentFunctionNode;
     }
 
     private Node leaveSelfModifyingAssignmentNode(final BinaryNode binaryNode) {
@@ -1626,14 +1641,12 @@
         newType(lhs.getSymbol(), destType); //may not narrow if dest is already wider than destType
 //        ensureSymbol(destType, binaryNode); //for OP= nodes, the node can carry a narrower types than its lhs rhs. This is perfectly fine
 
-        ensureAssignmentSlots(getLexicalContext().getCurrentFunction(), binaryNode);
-
-        return end(ensureSymbol(destType, binaryNode));
+        return end(ensureSymbol(destType, ensureAssignmentSlots(binaryNode)));
     }
 
     private Node ensureSymbol(final Type type, final Node node) {
         LOG.info("New TEMPORARY added to ", getLexicalContext().getCurrentFunction().getName(), " type=", type);
-        return Attr.ensureSymbol(getLexicalContext(), getLexicalContext().getCurrentBlock(), type, node);
+        return ensureSymbol(getLexicalContext(), type, node);
     }
 
     private Symbol newInternal(final String name, final Type type) {
@@ -1694,15 +1707,8 @@
         localUses.peek().add(name);
     }
 
-    static Node ensureSymbol(final LexicalContext lc, final Block block, final Type type, final Node node) {
-        Symbol symbol = node.getSymbol();
-        if (symbol != null) {
-            return node;
-        }
-        final String uname = lc.getCurrentFunction().uniqueName(TEMP_PREFIX.symbolName());
-        symbol = new Symbol(uname, IS_TEMP, type);
-        block.putSymbol(lc, symbol);
-        return node.setSymbol(lc, symbol);
+    private Node ensureSymbol(final LexicalContext lc, final Type type, final Node node) {
+        return temporarySymbols.ensureSymbol(lc, type, node);
     }
 
     /**
@@ -1771,6 +1777,10 @@
     }
 
     private Node end(final Node node, final boolean printNode) {
+        if(node instanceof Statement) {
+            // If we're done with a statement, all temporaries can be reused.
+            temporarySymbols.reuse();
+        }
         if (DEBUG) {
             final StringBuilder sb = new StringBuilder();
 
--- a/nashorn/src/jdk/nashorn/internal/codegen/CompilationPhase.java	Tue May 07 14:43:17 2013 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/CompilationPhase.java	Wed May 08 15:51:36 2013 +0200
@@ -21,6 +21,7 @@
 import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
 import jdk.nashorn.internal.ir.LexicalContext;
 import jdk.nashorn.internal.ir.Node;
+import jdk.nashorn.internal.ir.TemporarySymbols;
 import jdk.nashorn.internal.ir.debug.ASTWriter;
 import jdk.nashorn.internal.ir.debug.PrintVisitor;
 import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
@@ -171,7 +172,12 @@
     ATTRIBUTION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED)) {
         @Override
         FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
-            return (FunctionNode)enterAttr(fn).accept(new Attr());
+            final TemporarySymbols ts = compiler.getTemporarySymbols();
+            final FunctionNode newFunctionNode = (FunctionNode)enterAttr(fn, ts).accept(new Attr(ts));
+            if(compiler.getEnv()._print_mem_usage) {
+                Compiler.LOG.info("Attr temporary symbol count: " + ts.getTotalSymbolCount());
+            }
+            return newFunctionNode;
         }
 
         /**
@@ -179,14 +185,14 @@
          * and the function symbols to object
          * @param functionNode node where to start iterating
          */
-        private FunctionNode enterAttr(final FunctionNode functionNode) {
+        private FunctionNode enterAttr(final FunctionNode functionNode, final TemporarySymbols ts) {
             return (FunctionNode)functionNode.accept(new NodeVisitor() {
                 @Override
                 public Node leaveFunctionNode(final FunctionNode node) {
                     final LexicalContext lc = getLexicalContext();
                     if (node.isLazy()) {
                         FunctionNode newNode = node.setReturnType(getLexicalContext(), Type.OBJECT);
-                        return Attr.ensureSymbol(lc, lc.getCurrentBlock(), Type.OBJECT, newNode);
+                        return ts.ensureSymbol(lc, Type.OBJECT, newNode);
                     }
                     //node may have a reference here that needs to be nulled if it was referred to by
                     //its outer context, if it is lazy and not attributed
@@ -249,7 +255,7 @@
         FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
             final ScriptEnvironment env = compiler.getEnv();
 
-            final FunctionNode newFunctionNode = (FunctionNode)fn.accept(new FinalizeTypes());
+            final FunctionNode newFunctionNode = (FunctionNode)fn.accept(new FinalizeTypes(compiler.getTemporarySymbols()));
 
             if (env._print_lower_ast) {
                 env.getErr().println(new ASTWriter(newFunctionNode));
--- a/nashorn/src/jdk/nashorn/internal/codegen/Compiler.java	Tue May 07 14:43:17 2013 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/Compiler.java	Wed May 08 15:51:36 2013 +0200
@@ -36,6 +36,8 @@
 import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
 import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
 
+import jdk.nashorn.internal.ir.TemporarySymbols;
+
 import java.io.File;
 import java.lang.reflect.Field;
 import java.security.AccessController;
@@ -53,7 +55,6 @@
 import java.util.Map.Entry;
 import java.util.Set;
 import java.util.logging.Level;
-
 import jdk.internal.dynalink.support.NameCodec;
 import jdk.nashorn.internal.codegen.ClassEmitter.Flag;
 import jdk.nashorn.internal.codegen.types.Type;
@@ -100,6 +101,8 @@
 
     private CodeInstaller<ScriptEnvironment> installer;
 
+    private final TemporarySymbols temporarySymbols = new TemporarySymbols();
+
     /** logger for compiler, trampolines, splits and related code generation events
      *  that affect classes */
     public static final DebugLogger LOG = new DebugLogger("compiler");
@@ -394,7 +397,7 @@
         return newFunctionNode;
     }
 
-    private Class<?> install(final FunctionNode functionNode, final String className, final byte[] code) {
+    private Class<?> install(final String className, final byte[] code) {
         LOG.fine("Installing class ", className);
 
         final Class<?> clazz = installer.install(Compiler.binaryName(className), code);
@@ -436,7 +439,7 @@
 
         final String   rootClassName = firstCompileUnitName();
         final byte[]   rootByteCode  = bytecode.get(rootClassName);
-        final Class<?> rootClass     = install(functionNode, rootClassName, rootByteCode);
+        final Class<?> rootClass     = install(rootClassName, rootByteCode);
 
         int length = rootByteCode.length;
 
@@ -450,7 +453,7 @@
             final byte[] code = entry.getValue();
             length += code.length;
 
-            installedClasses.put(className, install(functionNode, className, code));
+            installedClasses.put(className, install(className, code));
         }
 
         for (final CompileUnit unit : compileUnits) {
@@ -508,6 +511,10 @@
         return installer;
     }
 
+    TemporarySymbols getTemporarySymbols() {
+        return temporarySymbols;
+    }
+
     void addClass(final String name, final byte[] code) {
         bytecode.put(name, code);
     }
--- a/nashorn/src/jdk/nashorn/internal/codegen/CompilerConstants.java	Tue May 07 14:43:17 2013 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/CompilerConstants.java	Wed May 08 15:51:36 2013 +0200
@@ -105,25 +105,25 @@
     ARGUMENTS("arguments", Object.class, 2),
 
     /** prefix for iterators for for (x in ...) */
-    ITERATOR_PREFIX(":iter"),
+    ITERATOR_PREFIX(":i"),
 
     /** prefix for tag variable used for switch evaluation */
-    SWITCH_TAG_PREFIX(":tag"),
+    SWITCH_TAG_PREFIX(":s"),
 
     /** prefix for all exceptions */
-    EXCEPTION_PREFIX(":exception"),
+    EXCEPTION_PREFIX(":e"),
 
     /** prefix for quick slots generated in Store */
-    QUICK_PREFIX(":quick"),
+    QUICK_PREFIX(":q"),
 
     /** prefix for temporary variables */
-    TEMP_PREFIX(":temp"),
+    TEMP_PREFIX(":t"),
 
     /** prefix for literals */
-    LITERAL_PREFIX(":lit"),
+    LITERAL_PREFIX(":l"),
 
     /** prefix for regexps */
-    REGEX_PREFIX(":regex"),
+    REGEX_PREFIX(":r"),
 
     /** "this" used in non-static Java methods; always in slot 0 */
     JAVA_THIS(null, 0),
--- a/nashorn/src/jdk/nashorn/internal/codegen/FinalizeTypes.java	Tue May 07 14:43:17 2013 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/FinalizeTypes.java	Wed May 08 15:51:36 2013 +0200
@@ -55,6 +55,7 @@
 import jdk.nashorn.internal.ir.RuntimeNode.Request;
 import jdk.nashorn.internal.ir.SwitchNode;
 import jdk.nashorn.internal.ir.Symbol;
+import jdk.nashorn.internal.ir.TemporarySymbols;
 import jdk.nashorn.internal.ir.TernaryNode;
 import jdk.nashorn.internal.ir.ThrowNode;
 import jdk.nashorn.internal.ir.TypeOverride;
@@ -87,7 +88,10 @@
 
     private static final DebugLogger LOG = new DebugLogger("finalize");
 
-    FinalizeTypes() {
+    private final TemporarySymbols temporarySymbols;
+
+    FinalizeTypes(final TemporarySymbols temporarySymbols) {
+        this.temporarySymbols = temporarySymbols;
     }
 
     @Override
@@ -227,21 +231,27 @@
         return leaveASSIGN(binaryNode);
     }
 
+    private boolean symbolIsInteger(Node node) {
+        final Symbol symbol = node.getSymbol();
+        assert symbol != null && symbol.getSymbolType().isInteger() : "int coercion expected: " + Debug.id(symbol) + " " + symbol + " " + getLexicalContext().getCurrentFunction().getSource();
+        return true;
+    }
+
     @Override
     public Node leaveBIT_AND(final BinaryNode binaryNode) {
-        assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isInteger() : "int coercion expected: " + binaryNode.getSymbol();
+        assert symbolIsInteger(binaryNode);
         return leaveBinary(binaryNode, Type.INT, Type.INT);
     }
 
     @Override
     public Node leaveBIT_OR(final BinaryNode binaryNode) {
-        assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isInteger() : "int coercion expected: " + binaryNode.getSymbol();
+        assert symbolIsInteger(binaryNode);
         return leaveBinary(binaryNode, Type.INT, Type.INT);
     }
 
     @Override
     public Node leaveBIT_XOR(final BinaryNode binaryNode) {
-        assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isInteger() : "int coercion expected: " + binaryNode.getSymbol();
+        assert symbolIsInteger(binaryNode);
         return leaveBinary(binaryNode, Type.INT, Type.INT);
     }
 
@@ -251,8 +261,7 @@
         final BinaryNode newBinaryNode = binaryNode.setRHS(discard(binaryNode.rhs()));
         // AccessSpecializer - the type of lhs, which is the remaining value of this node may have changed
         // in that case, update the node type as well
-        propagateType(newBinaryNode, newBinaryNode.lhs().getType());
-        return newBinaryNode;
+        return propagateType(newBinaryNode, newBinaryNode.lhs().getType());
     }
 
     @Override
@@ -261,8 +270,7 @@
         final BinaryNode newBinaryNode = binaryNode.setLHS(discard(binaryNode.lhs()));
         // AccessSpecializer - the type of rhs, which is the remaining value of this node may have changed
         // in that case, update the node type as well
-        propagateType(newBinaryNode, newBinaryNode.rhs().getType());
-        return newBinaryNode;
+        return propagateType(newBinaryNode, newBinaryNode.rhs().getType());
     }
 
     @Override
@@ -364,6 +372,7 @@
 
     @Override
     public Node leaveExecuteNode(final ExecuteNode executeNode) {
+        temporarySymbols.reuse();
         return executeNode.setExpression(discard(executeNode.getExpression()));
     }
 
@@ -489,8 +498,8 @@
 
     @Override
     public Node leaveVarNode(final VarNode varNode) {
-        final Node rhs = varNode.getInit();
-        if (rhs != null) {
+        final Node init = varNode.getInit();
+        if (init != null) {
             final SpecializedNode specialized = specialize(varNode);
             final VarNode specVarNode = (VarNode)specialized.node;
             Type destType = specialized.type;
@@ -498,8 +507,11 @@
                 destType = specVarNode.getType();
             }
             assert specVarNode.hasType() : specVarNode + " doesn't have a type";
-            return specVarNode.setInit(convert(rhs, destType));
+            final Node convertedInit = convert(init, destType);
+            temporarySymbols.reuse();
+            return specVarNode.setInit(convertedInit);
         }
+        temporarySymbols.reuse();
         return varNode;
     }
 
@@ -678,7 +690,7 @@
         }
     }
 
-    private static <T extends Node> SpecializedNode specialize(final Assignment<T> assignment) {
+    <T extends Node> SpecializedNode specialize(final Assignment<T> assignment) {
         final Node node = ((Node)assignment);
         final T lhs = assignment.getAssignmentDest();
         final Node rhs = assignment.getAssignmentSource();
@@ -700,9 +712,9 @@
         }
 
         final Node newNode = assignment.setAssignmentDest(setTypeOverride(lhs, to));
-        propagateType(newNode, to);
+        final Node typePropagatedNode = propagateType(newNode, to);
 
-        return new SpecializedNode(newNode, to);
+        return new SpecializedNode(typePropagatedNode, to);
     }
 
 
@@ -741,7 +753,7 @@
      * @param to      new type
      */
     @SuppressWarnings("unchecked")
-    private static <T extends Node> T setTypeOverride(final T node, final Type to) {
+    <T extends Node> T setTypeOverride(final T node, final Type to) {
         final Type from = node.getType();
         if (!node.getType().equals(to)) {
             LOG.info("Changing call override type for '", node, "' from ", node.getType(), " to ", to);
@@ -750,7 +762,7 @@
             }
         }
         LOG.info("Type override for lhs in '", node, "' => ", to);
-        return ((TypeOverride<T>)node).setType(to);
+        return ((TypeOverride<T>)node).setType(temporarySymbols, getLexicalContext(), to);
     }
 
     /**
@@ -808,7 +820,7 @@
         final LexicalContext lc = getLexicalContext();
         //This is the only place in this file that can create new temporaries
         //FinalizeTypes may not introduce ANY node that is not a conversion.
-        return Attr.ensureSymbol(lc, lc.getCurrentBlock(), to, resultNode);
+        return temporarySymbols.ensureSymbol(lc, to, resultNode);
     }
 
     private static Node discard(final Node node) {
@@ -836,12 +848,13 @@
      * @param node
      * @param to
      */
-    private static void propagateType(final Node node, final Type to) {
-        final Symbol symbol = node.getSymbol();
-        if (symbol.isTemp()) {
-            symbol.setTypeOverride(to);
+    private Node propagateType(final Node node, final Type to) {
+        Symbol symbol = node.getSymbol();
+        if (symbol.isTemp() && symbol.getSymbolType() != to) {
+            symbol = symbol.setTypeOverrideShared(to, temporarySymbols);
             LOG.info("Type override for temporary in '", node, "' => ", to);
         }
+        return node.setSymbol(getLexicalContext(), symbol);
     }
 
     /**
@@ -866,7 +879,7 @@
      * Whenever an explicit conversion is needed and the convertee is a literal, we can
      * just change the literal
      */
-    static class LiteralNodeConstantEvaluator extends FoldConstants.ConstantEvaluator<LiteralNode<?>> {
+    class LiteralNodeConstantEvaluator extends FoldConstants.ConstantEvaluator<LiteralNode<?>> {
         private final Type type;
 
         LiteralNodeConstantEvaluator(final LiteralNode<?> parent, final Type type) {
@@ -894,7 +907,7 @@
 
             if (literalNode != null) {
                 //inherit literal symbol for attr.
-                literalNode = (LiteralNode<?>)literalNode.setSymbol(null, parent.getSymbol());
+                literalNode = (LiteralNode<?>)literalNode.setSymbol(getLexicalContext(), parent.getSymbol());
             }
 
             return literalNode;
--- a/nashorn/src/jdk/nashorn/internal/ir/AccessNode.java	Tue May 07 14:43:17 2013 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/AccessNode.java	Wed May 08 15:51:36 2013 +0200
@@ -119,10 +119,10 @@
     }
 
     @Override
-    public AccessNode setType(final Type type) {
+    public AccessNode setType(final TemporarySymbols ts, final LexicalContext lc, final Type type) {
         logTypeChange(type);
-        getSymbol().setTypeOverride(type); //always a temp so this is fine.
-        return new AccessNode(this, base, property.setType(type), isFunction(), hasCallSiteType());
+        final AccessNode newAccessNode = (AccessNode)setSymbol(lc, getSymbol().setTypeOverrideShared(type, ts));
+        return new AccessNode(newAccessNode, base, property.setType(ts, lc, type), isFunction(), hasCallSiteType());
     }
 
     @Override
--- a/nashorn/src/jdk/nashorn/internal/ir/BlockLexicalContext.java	Tue May 07 14:43:17 2013 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/BlockLexicalContext.java	Wed May 08 15:51:36 2013 +0200
@@ -64,7 +64,6 @@
     }
 
     @Override
-    @SuppressWarnings("unchecked")
     public <T extends LexicalContextNode> T pop(final T node) {
         T expected = node;
         if (node instanceof Block) {
--- a/nashorn/src/jdk/nashorn/internal/ir/CallNode.java	Tue May 07 14:43:17 2013 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/CallNode.java	Wed May 08 15:51:36 2013 +0200
@@ -170,7 +170,7 @@
     }
 
     @Override
-    public CallNode setType(final Type type) {
+    public CallNode setType(final TemporarySymbols ts, final LexicalContext lc, final Type type) {
         if (this.type == type) {
             return this;
         }
@@ -200,7 +200,7 @@
                     setFunction(function.accept(visitor)).
                     setArgs(Node.accept(visitor, Node.class, args)).
                     setFlags(flags).
-                    setType(type).
+                    setType(null, lc, type).
                     setEvalArgs(evalArgs == null ?
                             null :
                             evalArgs.setCode(evalArgs.getCode().accept(visitor)).
--- a/nashorn/src/jdk/nashorn/internal/ir/IdentNode.java	Tue May 07 14:43:17 2013 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/IdentNode.java	Wed May 08 15:51:36 2013 +0200
@@ -61,7 +61,7 @@
      */
     public IdentNode(final long token, final int finish, final String name) {
         super(token, finish);
-        this.name = name;
+        this.name = name.intern();
         this.callSiteType = null;
         this.flags = 0;
     }
@@ -101,7 +101,7 @@
     }
 
     @Override
-    public IdentNode setType(final Type type) {
+    public IdentNode setType(final TemporarySymbols ts, final LexicalContext lc, final Type type) {
         // do NOT, repeat NOT touch the symbol here. it might be a local variable or whatever. This is the override if it isn't
         if (this.callSiteType == type) {
             return this;
--- a/nashorn/src/jdk/nashorn/internal/ir/IndexNode.java	Tue May 07 14:43:17 2013 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/IndexNode.java	Wed May 08 15:51:36 2013 +0200
@@ -106,6 +106,18 @@
         return index;
     }
 
+    /**
+     * Set the index expression for this node
+     * @param index new index expression
+     * @return a node equivalent to this one except for the requested change.
+     */
+    public IndexNode setIndex(Node index) {
+        if(this.index == index) {
+            return this;
+        }
+        return new IndexNode(this, base, index, isFunction(), hasCallSiteType());
+    }
+
     @Override
     public BaseNode setIsFunction() {
         if (isFunction()) {
@@ -115,10 +127,10 @@
     }
 
     @Override
-    public IndexNode setType(final Type type) {
+    public IndexNode setType(final TemporarySymbols ts, final LexicalContext lc, final Type type) {
         logTypeChange(type);
-        getSymbol().setTypeOverride(type); //always a temp so this is fine.
-        return new IndexNode(this, base, index, isFunction(), true);
+        final IndexNode newIndexNode = (IndexNode)setSymbol(lc, getSymbol().setTypeOverrideShared(type, ts));
+        return new IndexNode(newIndexNode, base, index, isFunction(), true);
     }
 
 }
--- a/nashorn/src/jdk/nashorn/internal/ir/Node.java	Tue May 07 14:43:17 2013 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/Node.java	Wed May 08 15:51:36 2013 +0200
@@ -27,7 +27,6 @@
 
 import java.util.ArrayList;
 import java.util.List;
-
 import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
 import jdk.nashorn.internal.parser.Token;
--- a/nashorn/src/jdk/nashorn/internal/ir/RuntimeNode.java	Tue May 07 14:43:17 2013 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/RuntimeNode.java	Wed May 08 15:51:36 2013 +0200
@@ -390,7 +390,7 @@
     }
 
     @Override
-    public RuntimeNode setType(final Type type) {
+    public RuntimeNode setType(final TemporarySymbols ts, final LexicalContext lc, final Type type) {
         if (this.callSiteType == type) {
             return this;
         }
--- a/nashorn/src/jdk/nashorn/internal/ir/Symbol.java	Tue May 07 14:43:17 2013 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/Symbol.java	Wed May 08 15:51:36 2013 +0200
@@ -29,7 +29,6 @@
 import java.util.HashSet;
 import java.util.Set;
 import java.util.StringTokenizer;
-
 import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.runtime.Context;
 import jdk.nashorn.internal.runtime.Debug;
@@ -69,6 +68,8 @@
     public static final int IS_FUNCTION_SELF = 1 << 10;
     /** Is this a specialized param? */
     public static final int IS_SPECIALIZED_PARAM = 1 << 11;
+    /** Is this symbol a shared temporary? */
+    public static final int IS_SHARED = 1 << 12;
 
     /** Null or name identifying symbol. */
     private final String name;
@@ -154,6 +155,16 @@
         this(name, flags, type, -1);
     }
 
+    private Symbol(final Symbol base, final String name, final int flags) {
+        this.flags = flags;
+        this.name = name;
+
+        this.fieldIndex = base.fieldIndex;
+        this.slot = base.slot;
+        this.type = base.type;
+        this.useCount = base.useCount;
+    }
+
     private static String align(final String string, final int max) {
         final StringBuilder sb = new StringBuilder();
         sb.append(string.substring(0, Math.min(string.length(), max)));
@@ -331,6 +342,24 @@
     }
 
     /**
+     * Returns true if this symbol is a temporary that is being shared across expressions.
+     * @return true if this symbol is a temporary that is being shared across expressions.
+     */
+    public boolean isShared() {
+        return (flags & IS_SHARED) == IS_SHARED;
+    }
+
+    /**
+     * Creates an unshared copy of a symbol. The symbol must be currently shared.
+     * @param newName the name for the new symbol.
+     * @return a new, unshared symbol.
+     */
+    public Symbol createUnshared(final String newName) {
+        assert isShared();
+        return new Symbol(this, newName, flags & ~IS_SHARED);
+    }
+
+    /**
      * Flag this symbol as scope as described in {@link Symbol#isScope()}
      */
     /**
@@ -339,10 +368,23 @@
      public void setIsScope() {
         if (!isScope()) {
             trace("SET IS SCOPE");
+            assert !isShared();
+            flags |= IS_SCOPE;
         }
-        flags |= IS_SCOPE;
     }
 
+     /**
+      * Mark this symbol as one being shared by multiple expressions. The symbol must be a temporary.
+      */
+     public void setIsShared() {
+         if(!isShared()) {
+             assert isTemp();
+             trace("SET IS SHARED");
+             flags |= IS_SHARED;
+         }
+     }
+
+
     /**
      * Check if this symbol is a variable
      * @return true if variable
@@ -397,7 +439,10 @@
      */
     public void setCanBeUndefined() {
         assert type.isObject() : type;
-        flags |= CAN_BE_UNDEFINED;
+        if(!canBeUndefined()) {
+            assert !isShared();
+            flags |= CAN_BE_UNDEFINED;
+        }
     }
 
     /**
@@ -405,7 +450,10 @@
      * @param type the primitive type it occurs with, currently unused but can be used for width guesses
      */
     public void setCanBePrimitive(final Type type) {
-        flags |= CAN_BE_PRIMITIVE;
+        if(!canBePrimitive()) {
+            assert !isShared();
+            flags |= CAN_BE_PRIMITIVE;
+        }
     }
 
     /**
@@ -445,7 +493,10 @@
      * Flag this symbol as a let
      */
     public void setIsLet() {
-        flags |= IS_LET;
+        if(!isLet()) {
+            assert !isShared();
+            flags |= IS_LET;
+        }
     }
 
     /**
@@ -474,7 +525,10 @@
      * @param fieldIndex field index - a positive integer
      */
     public void setFieldIndex(final int fieldIndex) {
-        this.fieldIndex = fieldIndex;
+        if(this.fieldIndex != fieldIndex) {
+            assert !isShared();
+            this.fieldIndex = fieldIndex;
+        }
     }
 
     /**
@@ -490,7 +544,10 @@
      * @param flags flags
      */
     public void setFlags(final int flags) {
-        this.flags = flags;
+        if(this.flags != flags) {
+            assert !isShared();
+            this.flags = flags;
+        }
     }
 
     /**
@@ -530,6 +587,7 @@
      */
     public void setSlot(final int slot) {
         if (slot != this.slot) {
+            assert !isShared();
             trace("SET SLOT " + slot);
             this.slot = slot;
         }
@@ -555,6 +613,15 @@
     }
 
     /**
+     * Returns true if calling {@link #setType(Type)} on this symbol would effectively change its type.
+     * @param newType the new type to test for
+     * @return true if setting this symbols type to a new value would effectively change its type.
+     */
+    public boolean wouldChangeType(final Type newType) {
+        return Type.widest(this.type, newType) != this.type;
+    }
+
+    /**
      * Only use this if you know about an existing type
      * constraint - otherwise a type can only be
      * widened
@@ -564,12 +631,33 @@
     public void setTypeOverride(final Type type) {
         final Type old = this.type;
         if (old != type) {
+            assert !isShared();
             trace("TYPE CHANGE: " + old + "=>" + type + " == " + type);
             this.type = type;
         }
     }
 
     /**
+     * Sets the type of the symbol to the specified type. If the type would be changed, but this symbol is a shared
+     * temporary, it will instead return a different temporary symbol of the requested type from the passed temporary
+     * symbols. That way, it never mutates the type of a shared temporary.
+     * @param type the new type for the symbol
+     * @param ts a holder of temporary symbols
+     * @return either this symbol, or a different symbol if this symbol is a shared temporary and it type would have to
+     * be changed.
+     */
+    public Symbol setTypeOverrideShared(final Type type, final TemporarySymbols ts) {
+        if(getSymbolType() != type) {
+            if(isShared()) {
+                assert !hasSlot();
+                return ts.getTypedTemporarySymbol(type);
+            }
+            setTypeOverride(type);
+        }
+        return this;
+    }
+
+    /**
      * From a lexical context, set this symbol as needing scope, which
      * will set flags for the defining block that will be written when
      * block is popped from the lexical context stack, used by codegen
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/ir/TemporarySymbols.java	Wed May 08 15:51:36 2013 +0200
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.nashorn.internal.ir;
+
+import static jdk.nashorn.internal.codegen.CompilerConstants.TEMP_PREFIX;
+import static jdk.nashorn.internal.ir.Symbol.IS_TEMP;
+
+import java.util.HashMap;
+import java.util.Map;
+import jdk.nashorn.internal.codegen.types.Type;
+
+/**
+ * Class that holds reusable temporary symbols by type.
+ *
+ */
+public class TemporarySymbols {
+    private static final String prefix = TEMP_PREFIX.symbolName() + "$";
+
+    private int totalSymbolCount;
+    private final Map<Type, TypedTemporarySymbols> temporarySymbolsByType = new HashMap<>();
+
+    /**
+     * Associates a temporary symbol of a given type with a node, if the node doesn't already have any symbol.
+     * @param lc the current lexical context
+     * @param type the type of the temporary symbol
+     * @param node the node
+     * @return the node that is guaranteed to have a symbol.
+     */
+    public Node ensureSymbol(final LexicalContext lc, final Type type, final Node node) {
+        final Symbol symbol = node.getSymbol();
+        if (symbol != null) {
+            return node;
+        }
+        return node.setSymbol(lc, getTypedTemporarySymbol(type));
+    }
+
+    /**
+     * Given a type, returns a temporary symbol of that type.
+     * @param type the required type of the symbol.
+     * @return a temporary symbol of the required type.
+     */
+    public Symbol getTypedTemporarySymbol(final Type type) {
+        return getTypedTemporarySymbols(type).getTemporarySymbol(type);
+    }
+
+    private TypedTemporarySymbols getTypedTemporarySymbols(final Type type) {
+        TypedTemporarySymbols temporarySymbols = temporarySymbolsByType.get(type);
+        if(temporarySymbols == null) {
+            temporarySymbols = new TypedTemporarySymbols();
+            temporarySymbolsByType.put(type, temporarySymbols);
+        }
+        return temporarySymbols;
+    }
+
+    /**
+     * This method is called to signal to this object that all the symbols it holds can be reused now.
+     */
+    public void reuse() {
+        for(TypedTemporarySymbols ts: temporarySymbolsByType.values()) {
+            ts.reuse();
+        }
+    }
+
+    /**
+     * Given a shared symbol, creates an unshared copy of it with a unique name.
+     * @param symbol the shared symbol
+     * @return the unshared, uniquely named copy of the symbol
+     */
+    public Symbol createUnshared(Symbol symbol) {
+        return symbol.createUnshared(getUniqueName());
+    }
+
+    private String getUniqueName() {
+        return prefix + (++totalSymbolCount);
+    }
+
+    /**
+     * Returns the total number of symbols this object created during its lifetime.
+     * @return the total number of symbols this object created during its lifetime.
+     */
+    public int getTotalSymbolCount() {
+        return totalSymbolCount;
+    }
+
+    private class TypedTemporarySymbols {
+        private Symbol[] symbols = new Symbol[16];
+        private int nextFreeSymbol = 0;
+        private int symbolCount = 0;
+
+        Symbol getTemporarySymbol(final Type type) {
+            while(nextFreeSymbol < symbolCount) {
+                final Symbol nextSymbol = symbols[nextFreeSymbol];
+                assert nextSymbol != null;
+                // If it has a slot, we can't reuse it.
+                if(!nextSymbol.hasSlot()) {
+                    final Type symbolType = nextSymbol.getSymbolType();
+                    if(symbolType == type) {
+                        assert nextSymbol.isTemp();
+                        assert !nextSymbol.isScope();
+                        // If types match, we can reuse it.
+                        nextSymbol.setIsShared();
+                        nextFreeSymbol++;
+                        return nextSymbol;
+                    }
+                    // If its type changed, but it doesn't have a slot then move it to its new home according to its
+                    // new type.
+                    getTypedTemporarySymbols(symbolType).addSymbol(nextSymbol);
+                }
+                // If we can move another symbol into its place, do that and repeat the analysis for this symbol.
+                --symbolCount;
+                if(symbolCount != nextFreeSymbol) {
+                    final Symbol lastFreeSymbol = symbols[symbolCount];
+                    symbols[nextFreeSymbol] = lastFreeSymbol;
+                }
+                symbols[symbolCount] = null;
+            }
+            return createNewSymbol(type);
+        }
+
+        private Symbol createNewSymbol(final Type type) {
+            ensureCapacity();
+            final Symbol symbol = symbols[nextFreeSymbol] = new Symbol(getUniqueName(), IS_TEMP, type);
+            nextFreeSymbol++;
+            symbolCount++;
+            return symbol;
+        }
+
+        private void addSymbol(Symbol symbol) {
+            ensureCapacity();
+            symbols[symbolCount++] = symbol;
+        }
+
+        void reuse() {
+            nextFreeSymbol = 0;
+        }
+
+        private void ensureCapacity() {
+            if(symbolCount == symbols.length) {
+                final Symbol[] newSymbols = new Symbol[symbolCount * 2];
+                System.arraycopy(symbols, 0, newSymbols, 0, symbolCount);
+                symbols = newSymbols;
+            }
+        }
+    }
+
+}
--- a/nashorn/src/jdk/nashorn/internal/ir/TypeOverride.java	Tue May 07 14:43:17 2013 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/TypeOverride.java	Wed May 08 15:51:36 2013 +0200
@@ -43,10 +43,12 @@
     /**
      * Set the override type
      *
-     * @param type  the type
+     * @param ts temporary symbols
+     * @param lc the current lexical context
+     * @param type the type
      * @return a node equivalent to this one except for the requested change.
      */
-    public T setType(final Type type);
+    public T setType(final TemporarySymbols ts, final LexicalContext lc, final Type type);
 
     /**
      * Returns true if this node can have a callsite override, e.g. all scope ident nodes