# HG changeset patch # User attila # Date 1368021096 -7200 # Node ID d28180d97c6196c2c22f207909e719002d0f20c2 # Parent fb1d7ea3e1b66578c284a3a69aa1f28c4a749993 8013912: Nashorn needs to reuse temporary symbols Reviewed-by: jlaskey, lagergren diff -r fb1d7ea3e1b6 -r d28180d97c61 src/jdk/nashorn/internal/codegen/Attr.java --- a/src/jdk/nashorn/internal/codegen/Attr.java Tue May 07 14:43:17 2013 +0200 +++ b/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 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(); diff -r fb1d7ea3e1b6 -r d28180d97c61 src/jdk/nashorn/internal/codegen/CompilationPhase.java --- a/src/jdk/nashorn/internal/codegen/CompilationPhase.java Tue May 07 14:43:17 2013 +0200 +++ b/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)); diff -r fb1d7ea3e1b6 -r d28180d97c61 src/jdk/nashorn/internal/codegen/Compiler.java --- a/src/jdk/nashorn/internal/codegen/Compiler.java Tue May 07 14:43:17 2013 +0200 +++ b/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 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); } diff -r fb1d7ea3e1b6 -r d28180d97c61 src/jdk/nashorn/internal/codegen/CompilerConstants.java --- a/src/jdk/nashorn/internal/codegen/CompilerConstants.java Tue May 07 14:43:17 2013 +0200 +++ b/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), diff -r fb1d7ea3e1b6 -r d28180d97c61 src/jdk/nashorn/internal/codegen/FinalizeTypes.java --- a/src/jdk/nashorn/internal/codegen/FinalizeTypes.java Tue May 07 14:43:17 2013 +0200 +++ b/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 SpecializedNode specialize(final Assignment assignment) { + SpecializedNode specialize(final Assignment 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 setTypeOverride(final T node, final Type to) { + 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)node).setType(to); + return ((TypeOverride)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> { + class LiteralNodeConstantEvaluator extends FoldConstants.ConstantEvaluator> { 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; diff -r fb1d7ea3e1b6 -r d28180d97c61 src/jdk/nashorn/internal/ir/AccessNode.java --- a/src/jdk/nashorn/internal/ir/AccessNode.java Tue May 07 14:43:17 2013 +0200 +++ b/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 diff -r fb1d7ea3e1b6 -r d28180d97c61 src/jdk/nashorn/internal/ir/BlockLexicalContext.java --- a/src/jdk/nashorn/internal/ir/BlockLexicalContext.java Tue May 07 14:43:17 2013 +0200 +++ b/src/jdk/nashorn/internal/ir/BlockLexicalContext.java Wed May 08 15:51:36 2013 +0200 @@ -64,7 +64,6 @@ } @Override - @SuppressWarnings("unchecked") public T pop(final T node) { T expected = node; if (node instanceof Block) { diff -r fb1d7ea3e1b6 -r d28180d97c61 src/jdk/nashorn/internal/ir/CallNode.java --- a/src/jdk/nashorn/internal/ir/CallNode.java Tue May 07 14:43:17 2013 +0200 +++ b/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)). diff -r fb1d7ea3e1b6 -r d28180d97c61 src/jdk/nashorn/internal/ir/IdentNode.java --- a/src/jdk/nashorn/internal/ir/IdentNode.java Tue May 07 14:43:17 2013 +0200 +++ b/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; diff -r fb1d7ea3e1b6 -r d28180d97c61 src/jdk/nashorn/internal/ir/IndexNode.java --- a/src/jdk/nashorn/internal/ir/IndexNode.java Tue May 07 14:43:17 2013 +0200 +++ b/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); } } diff -r fb1d7ea3e1b6 -r d28180d97c61 src/jdk/nashorn/internal/ir/Node.java --- a/src/jdk/nashorn/internal/ir/Node.java Tue May 07 14:43:17 2013 +0200 +++ b/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; diff -r fb1d7ea3e1b6 -r d28180d97c61 src/jdk/nashorn/internal/ir/RuntimeNode.java --- a/src/jdk/nashorn/internal/ir/RuntimeNode.java Tue May 07 14:43:17 2013 +0200 +++ b/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; } diff -r fb1d7ea3e1b6 -r d28180d97c61 src/jdk/nashorn/internal/ir/Symbol.java --- a/src/jdk/nashorn/internal/ir/Symbol.java Tue May 07 14:43:17 2013 +0200 +++ b/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 diff -r fb1d7ea3e1b6 -r d28180d97c61 src/jdk/nashorn/internal/ir/TemporarySymbols.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/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 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; + } + } + } + +} diff -r fb1d7ea3e1b6 -r d28180d97c61 src/jdk/nashorn/internal/ir/TypeOverride.java --- a/src/jdk/nashorn/internal/ir/TypeOverride.java Tue May 07 14:43:17 2013 +0200 +++ b/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