8013912: Nashorn needs to reuse temporary symbols
Reviewed-by: jlaskey, lagergren
--- 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