Mon, 20 May 2013 16:38:38 +0200
8006069: Range analysis first iteration, runtime specializations
Reviewed-by: jlaskey, sundar
1.1 --- a/src/jdk/nashorn/internal/codegen/CompilationPhase.java Fri May 17 16:44:22 2013 -0300 1.2 +++ b/src/jdk/nashorn/internal/codegen/CompilationPhase.java Mon May 20 16:38:38 2013 +0200 1.3 @@ -11,13 +11,21 @@ 1.4 import java.io.File; 1.5 import java.io.FileOutputStream; 1.6 import java.io.IOException; 1.7 +import java.util.ArrayDeque; 1.8 +import java.util.ArrayList; 1.9 +import java.util.Deque; 1.10 import java.util.EnumSet; 1.11 import java.util.HashSet; 1.12 +import java.util.List; 1.13 import java.util.Set; 1.14 + 1.15 +import jdk.nashorn.internal.codegen.types.Range; 1.16 import jdk.nashorn.internal.codegen.types.Type; 1.17 import jdk.nashorn.internal.ir.Block; 1.18 import jdk.nashorn.internal.ir.CallNode; 1.19 import jdk.nashorn.internal.ir.FunctionNode; 1.20 +import jdk.nashorn.internal.ir.ReturnNode; 1.21 +import jdk.nashorn.internal.ir.Symbol; 1.22 import jdk.nashorn.internal.ir.FunctionNode.CompilationState; 1.23 import jdk.nashorn.internal.ir.LexicalContext; 1.24 import jdk.nashorn.internal.ir.Node; 1.25 @@ -174,7 +182,7 @@ 1.26 FunctionNode transform(final Compiler compiler, final FunctionNode fn) { 1.27 final TemporarySymbols ts = compiler.getTemporarySymbols(); 1.28 final FunctionNode newFunctionNode = (FunctionNode)enterAttr(fn, ts).accept(new Attr(ts)); 1.29 - if(compiler.getEnv()._print_mem_usage) { 1.30 + if (compiler.getEnv()._print_mem_usage) { 1.31 Compiler.LOG.info("Attr temporary symbol count: " + ts.getTotalSymbolCount()); 1.32 } 1.33 return newFunctionNode; 1.34 @@ -208,6 +216,89 @@ 1.35 }, 1.36 1.37 /* 1.38 + * Range analysis 1.39 + * Conservatively prove that certain variables can be narrower than 1.40 + * the most generic number type 1.41 + */ 1.42 + RANGE_ANALYSIS_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, ATTR)) { 1.43 + @Override 1.44 + FunctionNode transform(final Compiler compiler, final FunctionNode fn) { 1.45 + if (!compiler.getEnv()._range_analysis) { 1.46 + return fn; 1.47 + } 1.48 + 1.49 + FunctionNode newFunctionNode = (FunctionNode)fn.accept(new RangeAnalyzer()); 1.50 + final List<ReturnNode> returns = new ArrayList<>(); 1.51 + 1.52 + newFunctionNode = (FunctionNode)newFunctionNode.accept(new NodeVisitor() { 1.53 + private final Deque<ArrayList<ReturnNode>> returnStack = new ArrayDeque<>(); 1.54 + 1.55 + @Override 1.56 + public boolean enterFunctionNode(final FunctionNode functionNode) { 1.57 + returnStack.push(new ArrayList<ReturnNode>()); 1.58 + return true; 1.59 + } 1.60 + 1.61 + @Override 1.62 + public Node leaveFunctionNode(final FunctionNode functionNode) { 1.63 + Type returnType = Type.UNKNOWN; 1.64 + for (final ReturnNode ret : returnStack.pop()) { 1.65 + if (ret.getExpression() == null) { 1.66 + returnType = Type.OBJECT; 1.67 + break; 1.68 + } 1.69 + returnType = Type.widest(returnType, ret.getExpression().getType()); 1.70 + } 1.71 + return functionNode.setReturnType(getLexicalContext(), returnType); 1.72 + } 1.73 + 1.74 + @Override 1.75 + public Node leaveReturnNode(final ReturnNode returnNode) { 1.76 + final ReturnNode result = (ReturnNode)leaveDefault(returnNode); 1.77 + returns.add(result); 1.78 + return result; 1.79 + } 1.80 + 1.81 + @Override 1.82 + public Node leaveDefault(final Node node) { 1.83 + final Symbol symbol = node.getSymbol(); 1.84 + if (symbol != null) { 1.85 + final Range range = symbol.getRange(); 1.86 + final Type symbolType = symbol.getSymbolType(); 1.87 + if (!symbolType.isNumeric()) { 1.88 + return node; 1.89 + } 1.90 + final Type rangeType = range.getType(); 1.91 + if (!Type.areEquivalent(symbolType, rangeType) && Type.widest(symbolType, rangeType) == symbolType) { //we can narrow range 1.92 + RangeAnalyzer.LOG.info("[", getLexicalContext().getCurrentFunction().getName(), "] ", symbol, " can be ", range.getType(), " ", symbol.getRange()); 1.93 + return node.setSymbol(getLexicalContext(), symbol.setTypeOverrideShared(range.getType(), compiler.getTemporarySymbols())); 1.94 + } 1.95 + } 1.96 + return node; 1.97 + } 1.98 + }); 1.99 + 1.100 + Type returnType = Type.UNKNOWN; 1.101 + for (final ReturnNode node : returns) { 1.102 + if (node.getExpression() != null) { 1.103 + returnType = Type.widest(returnType, node.getExpression().getType()); 1.104 + } else { 1.105 + returnType = Type.OBJECT; 1.106 + break; 1.107 + } 1.108 + } 1.109 + 1.110 + return newFunctionNode.setReturnType(null, returnType); 1.111 + } 1.112 + 1.113 + @Override 1.114 + public String toString() { 1.115 + return "[Range Analysis]"; 1.116 + } 1.117 + }, 1.118 + 1.119 + 1.120 + /* 1.121 * Splitter Split the AST into several compile units based on a size 1.122 * heuristic Splitter needs attributed AST for weight calculations (e.g. is 1.123 * a + b a ScriptRuntime.ADD with call overhead or a dadd with much less). 1.124 @@ -218,7 +309,6 @@ 1.125 FunctionNode transform(final Compiler compiler, final FunctionNode fn) { 1.126 final CompileUnit outermostCompileUnit = compiler.addCompileUnit(compiler.firstCompileUnitName()); 1.127 1.128 -// assert fn.isProgram() ; 1.129 final FunctionNode newFunctionNode = new Splitter(compiler, fn, outermostCompileUnit).split(fn); 1.130 1.131 assert newFunctionNode.getCompileUnit() == outermostCompileUnit : "fn.compileUnit (" + newFunctionNode.getCompileUnit() + ") != " + outermostCompileUnit;
2.1 --- a/src/jdk/nashorn/internal/codegen/Compiler.java Fri May 17 16:44:22 2013 -0300 2.2 +++ b/src/jdk/nashorn/internal/codegen/Compiler.java Mon May 20 16:38:38 2013 +0200 2.3 @@ -99,7 +99,7 @@ 2.4 2.5 private boolean strict; 2.6 2.7 - private CodeInstaller<ScriptEnvironment> installer; 2.8 + private final CodeInstaller<ScriptEnvironment> installer; 2.9 2.10 private final TemporarySymbols temporarySymbols = new TemporarySymbols(); 2.11 2.12 @@ -219,6 +219,7 @@ 2.13 CompilationPhase.CONSTANT_FOLDING_PHASE, 2.14 CompilationPhase.LOWERING_PHASE, 2.15 CompilationPhase.ATTRIBUTION_PHASE, 2.16 + CompilationPhase.RANGE_ANALYSIS_PHASE, 2.17 CompilationPhase.SPLITTING_PHASE, 2.18 CompilationPhase.TYPE_FINALIZATION_PHASE, 2.19 CompilationPhase.BYTECODE_GENERATION_PHASE); 2.20 @@ -384,6 +385,8 @@ 2.21 if (info) { 2.22 final StringBuilder sb = new StringBuilder(); 2.23 sb.append("Compile job for '"). 2.24 + append(newFunctionNode.getSource()). 2.25 + append(':'). 2.26 append(newFunctionNode.getName()). 2.27 append("' finished"); 2.28 2.29 @@ -487,7 +490,7 @@ 2.30 } 2.31 2.32 if (sb != null) { 2.33 - LOG.info(sb); 2.34 + LOG.fine(sb); 2.35 } 2.36 2.37 return rootClass;
3.1 --- a/src/jdk/nashorn/internal/codegen/CompilerConstants.java Fri May 17 16:44:22 2013 -0300 3.2 +++ b/src/jdk/nashorn/internal/codegen/CompilerConstants.java Mon May 20 16:38:38 2013 +0200 3.3 @@ -262,7 +262,7 @@ 3.4 * @return the internal descriptor for this type 3.5 */ 3.6 public static String typeDescriptor(final Class<?> clazz) { 3.7 - return Type.getDescriptor(clazz); 3.8 + return Type.typeFor(clazz).getDescriptor(); 3.9 } 3.10 3.11 /**
4.1 --- a/src/jdk/nashorn/internal/codegen/MethodEmitter.java Fri May 17 16:44:22 2013 -0300 4.2 +++ b/src/jdk/nashorn/internal/codegen/MethodEmitter.java Mon May 20 16:38:38 2013 +0200 4.3 @@ -2081,7 +2081,9 @@ 4.4 * @param args debug information to print 4.5 */ 4.6 private void debug(final Object... args) { 4.7 - debug(30, args); 4.8 + if (DEBUG) { 4.9 + debug(30, args); 4.10 + } 4.11 } 4.12 4.13 /** 4.14 @@ -2091,7 +2093,9 @@ 4.15 * @param args debug information to print 4.16 */ 4.17 private void debug_label(final Object... args) { 4.18 - debug(26, args); 4.19 + if (DEBUG) { 4.20 + debug(22, args); 4.21 + } 4.22 } 4.23 4.24 private void debug(final int padConstant, final Object... args) { 4.25 @@ -2164,7 +2168,6 @@ 4.26 new Throwable().printStackTrace(LOG.getOutputStream()); 4.27 } 4.28 } 4.29 - 4.30 } 4.31 } 4.32
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 5.2 +++ b/src/jdk/nashorn/internal/codegen/RangeAnalyzer.java Mon May 20 16:38:38 2013 +0200 5.3 @@ -0,0 +1,474 @@ 5.4 +/* 5.5 + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 5.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5.7 + * 5.8 + * This code is free software; you can redistribute it and/or modify it 5.9 + * under the terms of the GNU General Public License version 2 only, as 5.10 + * published by the Free Software Foundation. Oracle designates this 5.11 + * particular file as subject to the "Classpath" exception as provided 5.12 + * by Oracle in the LICENSE file that accompanied this code. 5.13 + * 5.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 5.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 5.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 5.17 + * version 2 for more details (a copy is included in the LICENSE file that 5.18 + * accompanied this code). 5.19 + * 5.20 + * You should have received a copy of the GNU General Public License version 5.21 + * 2 along with this work; if not, write to the Free Software Foundation, 5.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 5.23 + * 5.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 5.25 + * or visit www.oracle.com if you need additional information or have any 5.26 + * questions. 5.27 + */ 5.28 + 5.29 +package jdk.nashorn.internal.codegen; 5.30 + 5.31 +import java.util.HashMap; 5.32 +import java.util.HashSet; 5.33 +import java.util.Map; 5.34 + 5.35 +import jdk.nashorn.internal.codegen.types.Range; 5.36 +import jdk.nashorn.internal.codegen.types.Type; 5.37 +import jdk.nashorn.internal.ir.Assignment; 5.38 +import jdk.nashorn.internal.ir.BinaryNode; 5.39 +import jdk.nashorn.internal.ir.ForNode; 5.40 +import jdk.nashorn.internal.ir.IdentNode; 5.41 +import jdk.nashorn.internal.ir.LiteralNode; 5.42 +import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; 5.43 +import jdk.nashorn.internal.ir.LoopNode; 5.44 +import jdk.nashorn.internal.ir.Node; 5.45 +import jdk.nashorn.internal.ir.RuntimeNode; 5.46 +import jdk.nashorn.internal.ir.Symbol; 5.47 +import jdk.nashorn.internal.ir.UnaryNode; 5.48 +import jdk.nashorn.internal.ir.VarNode; 5.49 +import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor; 5.50 +import jdk.nashorn.internal.ir.visitor.NodeVisitor; 5.51 +import jdk.nashorn.internal.parser.TokenType; 5.52 +import jdk.nashorn.internal.runtime.DebugLogger; 5.53 + 5.54 +/** 5.55 + * Range analysis and narrowing of type where it can be proven 5.56 + * that there is no spillover, e.g. 5.57 + * 5.58 + * function func(c) { 5.59 + * var v = c & 0xfff; 5.60 + * var w = c & 0xeee; 5.61 + * var x = v * w; 5.62 + * return x; 5.63 + * } 5.64 + * 5.65 + * Proves that the multiplication never exceeds 24 bits and can thus be an int 5.66 + */ 5.67 +final class RangeAnalyzer extends NodeOperatorVisitor { 5.68 + static final DebugLogger LOG = new DebugLogger("ranges"); 5.69 + 5.70 + private static final Range.Functionality RANGE = new Range.Functionality(LOG); 5.71 + 5.72 + private final Map<LoopNode, Symbol> loopCounters = new HashMap<>(); 5.73 + 5.74 + RangeAnalyzer() { 5.75 + } 5.76 + 5.77 + @Override 5.78 + public boolean enterForNode(final ForNode forNode) { 5.79 + //conservatively attempt to identify the loop counter. Null means that it wasn't 5.80 + //properly identified and that no optimizations can be made with it - its range is 5.81 + //simply unknown in that case, if it is assigned in the loop 5.82 + final Symbol counter = findLoopCounter(forNode); 5.83 + LOG.fine("Entering forNode " + forNode + " counter = " + counter); 5.84 + if (counter != null && !assignedInLoop(forNode, counter)) { 5.85 + loopCounters.put(forNode, counter); 5.86 + } 5.87 + return true; 5.88 + } 5.89 + 5.90 + //destination visited 5.91 + private Symbol setRange(final Node dest, final Range range) { 5.92 + if (range.isUnknown()) { 5.93 + return null; 5.94 + } 5.95 + 5.96 + final Symbol symbol = dest.getSymbol(); 5.97 + assert symbol != null : dest + " " + dest.getClass() + " has no symbol"; 5.98 + assert symbol.getRange() != null : symbol + " has no range"; 5.99 + final Range symRange = RANGE.join(symbol.getRange(), range); 5.100 + 5.101 + //anything assigned in the loop, not being the safe loop counter(s) invalidates its entire range 5.102 + if (getLexicalContext().inLoop() && !isLoopCounter(getLexicalContext().getCurrentLoop(), symbol)) { 5.103 + symbol.setRange(Range.createGenericRange()); 5.104 + return symbol; 5.105 + } 5.106 + 5.107 + if (!symRange.equals(symbol.getRange())) { 5.108 + LOG.fine("Modify range for " + dest + " " + symbol + " from " + symbol.getRange() + " to " + symRange + " (in node = " + dest + ")" ); 5.109 + symbol.setRange(symRange); 5.110 + } 5.111 + 5.112 + return null; 5.113 + } 5.114 + 5.115 + @Override 5.116 + public Node leaveADD(final BinaryNode node) { 5.117 + setRange(node, RANGE.add(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange())); 5.118 + return node; 5.119 + } 5.120 + 5.121 + @Override 5.122 + public Node leaveSUB(final BinaryNode node) { 5.123 + setRange(node, RANGE.sub(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange())); 5.124 + return node; 5.125 + } 5.126 + 5.127 + @Override 5.128 + public Node leaveMUL(final BinaryNode node) { 5.129 + setRange(node, RANGE.mul(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange())); 5.130 + return node; 5.131 + } 5.132 + 5.133 + @Override 5.134 + public Node leaveDIV(final BinaryNode node) { 5.135 + setRange(node, RANGE.div(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange())); 5.136 + return node; 5.137 + } 5.138 + 5.139 + @Override 5.140 + public Node leaveMOD(final BinaryNode node) { 5.141 + setRange(node, RANGE.mod(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange())); 5.142 + return node; 5.143 + } 5.144 + 5.145 + @Override 5.146 + public Node leaveBIT_AND(final BinaryNode node) { 5.147 + setRange(node, RANGE.and(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange())); 5.148 + return node; 5.149 + } 5.150 + 5.151 + @Override 5.152 + public Node leaveBIT_OR(final BinaryNode node) { 5.153 + setRange(node, RANGE.or(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange())); 5.154 + return node; 5.155 + } 5.156 + 5.157 + @Override 5.158 + public Node leaveBIT_XOR(final BinaryNode node) { 5.159 + setRange(node, RANGE.xor(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange())); 5.160 + return node; 5.161 + } 5.162 + 5.163 + @Override 5.164 + public Node leaveSAR(final BinaryNode node) { 5.165 + setRange(node, RANGE.sar(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange())); 5.166 + return node; 5.167 + } 5.168 + 5.169 + @Override 5.170 + public Node leaveSHL(final BinaryNode node) { 5.171 + setRange(node, RANGE.shl(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange())); 5.172 + return node; 5.173 + } 5.174 + 5.175 + @Override 5.176 + public Node leaveSHR(final BinaryNode node) { 5.177 + setRange(node, RANGE.shr(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange())); 5.178 + return node; 5.179 + } 5.180 + 5.181 + private Node leaveCmp(final BinaryNode node) { 5.182 + setRange(node, Range.createTypeRange(Type.BOOLEAN)); 5.183 + return node; 5.184 + } 5.185 + 5.186 + @Override 5.187 + public Node leaveEQ(final BinaryNode node) { 5.188 + return leaveCmp(node); 5.189 + } 5.190 + 5.191 + @Override 5.192 + public Node leaveEQ_STRICT(final BinaryNode node) { 5.193 + return leaveCmp(node); 5.194 + } 5.195 + 5.196 + @Override 5.197 + public Node leaveNE(final BinaryNode node) { 5.198 + return leaveCmp(node); 5.199 + } 5.200 + 5.201 + @Override 5.202 + public Node leaveNE_STRICT(final BinaryNode node) { 5.203 + return leaveCmp(node); 5.204 + } 5.205 + 5.206 + @Override 5.207 + public Node leaveLT(final BinaryNode node) { 5.208 + return leaveCmp(node); 5.209 + } 5.210 + 5.211 + @Override 5.212 + public Node leaveLE(final BinaryNode node) { 5.213 + return leaveCmp(node); 5.214 + } 5.215 + 5.216 + @Override 5.217 + public Node leaveGT(final BinaryNode node) { 5.218 + return leaveCmp(node); 5.219 + } 5.220 + 5.221 + @Override 5.222 + public Node leaveGE(final BinaryNode node) { 5.223 + return leaveCmp(node); 5.224 + } 5.225 + 5.226 + @Override 5.227 + public Node leaveASSIGN(final BinaryNode node) { 5.228 + Range range = node.rhs().getSymbol().getRange(); 5.229 + if (range.isUnknown()) { 5.230 + range = Range.createGenericRange(); 5.231 + } 5.232 + 5.233 + setRange(node.lhs(), range); 5.234 + setRange(node, range); 5.235 + 5.236 + return node; 5.237 + } 5.238 + 5.239 + private Node leaveSelfModifyingAssign(final BinaryNode node, final Range range) { 5.240 + setRange(node.lhs(), range); 5.241 + setRange(node, range); 5.242 + return node; 5.243 + } 5.244 + 5.245 + private Node leaveSelfModifyingAssign(final UnaryNode node, final Range range) { 5.246 + setRange(node.rhs(), range); 5.247 + setRange(node, range); 5.248 + return node; 5.249 + } 5.250 + 5.251 + @Override 5.252 + public Node leaveASSIGN_ADD(final BinaryNode node) { 5.253 + return leaveSelfModifyingAssign(node, RANGE.add(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange())); 5.254 + } 5.255 + 5.256 + @Override 5.257 + public Node leaveASSIGN_SUB(final BinaryNode node) { 5.258 + return leaveSelfModifyingAssign(node, RANGE.sub(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange())); 5.259 + } 5.260 + 5.261 + @Override 5.262 + public Node leaveASSIGN_MUL(final BinaryNode node) { 5.263 + return leaveSelfModifyingAssign(node, RANGE.mul(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange())); 5.264 + } 5.265 + 5.266 + @Override 5.267 + public Node leaveASSIGN_DIV(final BinaryNode node) { 5.268 + return leaveSelfModifyingAssign(node, Range.createTypeRange(Type.NUMBER)); 5.269 + } 5.270 + 5.271 + @Override 5.272 + public Node leaveASSIGN_MOD(final BinaryNode node) { 5.273 + return leaveSelfModifyingAssign(node, Range.createTypeRange(Type.NUMBER)); 5.274 + } 5.275 + 5.276 + @Override 5.277 + public Node leaveASSIGN_BIT_AND(final BinaryNode node) { 5.278 + return leaveSelfModifyingAssign(node, RANGE.and(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange())); 5.279 + } 5.280 + 5.281 + @Override 5.282 + public Node leaveASSIGN_BIT_OR(final BinaryNode node) { 5.283 + return leaveSelfModifyingAssign(node, RANGE.or(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange())); 5.284 + } 5.285 + 5.286 + @Override 5.287 + public Node leaveASSIGN_BIT_XOR(final BinaryNode node) { 5.288 + return leaveSelfModifyingAssign(node, RANGE.xor(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange())); 5.289 + } 5.290 + 5.291 + @Override 5.292 + public Node leaveASSIGN_SAR(final BinaryNode node) { 5.293 + return leaveSelfModifyingAssign(node, RANGE.sar(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange())); 5.294 + } 5.295 + 5.296 + @Override 5.297 + public Node leaveASSIGN_SHR(final BinaryNode node) { 5.298 + return leaveSelfModifyingAssign(node, RANGE.shr(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange())); 5.299 + } 5.300 + 5.301 + @Override 5.302 + public Node leaveASSIGN_SHL(final BinaryNode node) { 5.303 + return leaveSelfModifyingAssign(node, RANGE.shl(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange())); 5.304 + } 5.305 + 5.306 + @Override 5.307 + public Node leaveDECINC(final UnaryNode node) { 5.308 + switch (node.tokenType()) { 5.309 + case DECPREFIX: 5.310 + case DECPOSTFIX: 5.311 + return leaveSelfModifyingAssign(node, RANGE.sub(node.rhs().getSymbol().getRange(), Range.createRange(1))); 5.312 + case INCPREFIX: 5.313 + case INCPOSTFIX: 5.314 + return leaveSelfModifyingAssign(node, RANGE.add(node.rhs().getSymbol().getRange(), Range.createRange(1))); 5.315 + default: 5.316 + assert false; 5.317 + return node; 5.318 + } 5.319 + } 5.320 + 5.321 + @Override 5.322 + public Node leaveADD(final UnaryNode node) { 5.323 + Range range = node.rhs().getSymbol().getRange(); 5.324 + if (!range.getType().isNumeric()) { 5.325 + range = Range.createTypeRange(Type.NUMBER); 5.326 + } 5.327 + setRange(node, range); 5.328 + return node; 5.329 + } 5.330 + 5.331 + @Override 5.332 + public Node leaveBIT_NOT(final UnaryNode node) { 5.333 + setRange(node, Range.createTypeRange(Type.INT)); 5.334 + return node; 5.335 + } 5.336 + 5.337 + @Override 5.338 + public Node leaveNOT(final UnaryNode node) { 5.339 + setRange(node, Range.createTypeRange(Type.BOOLEAN)); 5.340 + return node; 5.341 + } 5.342 + 5.343 + @Override 5.344 + public Node leaveSUB(final UnaryNode node) { 5.345 + setRange(node, RANGE.neg(node.rhs().getSymbol().getRange())); 5.346 + return node; 5.347 + } 5.348 + 5.349 + @Override 5.350 + public Node leaveVarNode(final VarNode node) { 5.351 + if (node.isAssignment()) { 5.352 + Range range = node.getInit().getSymbol().getRange(); 5.353 + range = range.isUnknown() ? Range.createGenericRange() : range; 5.354 + 5.355 + setRange(node.getName(), range); 5.356 + setRange(node, range); 5.357 + } 5.358 + 5.359 + return node; 5.360 + } 5.361 + 5.362 + @SuppressWarnings("rawtypes") 5.363 + @Override 5.364 + public boolean enterLiteralNode(final LiteralNode node) { 5.365 + // ignore array literals 5.366 + return !(node instanceof ArrayLiteralNode); 5.367 + } 5.368 + 5.369 + @Override 5.370 + public Node leaveLiteralNode(@SuppressWarnings("rawtypes") final LiteralNode node) { 5.371 + if (node.getType().isInteger()) { 5.372 + setRange(node, Range.createRange(node.getInt32())); 5.373 + } else if (node.getType().isNumber()) { 5.374 + setRange(node, Range.createRange(node.getNumber())); 5.375 + } else if (node.getType().isLong()) { 5.376 + setRange(node, Range.createRange(node.getLong())); 5.377 + } else if (node.getType().isBoolean()) { 5.378 + setRange(node, Range.createTypeRange(Type.BOOLEAN)); 5.379 + } else { 5.380 + setRange(node, Range.createGenericRange()); 5.381 + } 5.382 + return node; 5.383 + } 5.384 + 5.385 + @Override 5.386 + public boolean enterRuntimeNode(final RuntimeNode node) { 5.387 + // a runtime node that cannot be specialized is no point entering 5.388 + return node.getRequest().canSpecialize(); 5.389 + } 5.390 + 5.391 + /** 5.392 + * Check whether a symbol is unsafely assigned in a loop - i.e. repeteadly assigned and 5.393 + * not being identified as the loop counter. That means we don't really know anything 5.394 + * about its range. 5.395 + * @param loopNode loop node 5.396 + * @param symbol symbol 5.397 + * @return true if assigned in loop 5.398 + */ 5.399 + // TODO - this currently checks for nodes only - needs to be augmented for while nodes 5.400 + // assignment analysis is also very conservative 5.401 + private static boolean assignedInLoop(final LoopNode loopNode, final Symbol symbol) { 5.402 + final HashSet<Node> skip = new HashSet<>(); 5.403 + final HashSet<Node> assignmentsInLoop = new HashSet<>(); 5.404 + 5.405 + loopNode.accept(new NodeVisitor() { 5.406 + private boolean assigns(final Node node, final Symbol s) { 5.407 + return node.isAssignment() && ((Assignment<?>)node).getAssignmentDest().getSymbol() == s; 5.408 + } 5.409 + 5.410 + @Override 5.411 + public boolean enterForNode(final ForNode forNode) { 5.412 + if (forNode.getInit() != null) { 5.413 + skip.add(forNode.getInit()); 5.414 + } 5.415 + if (forNode.getModify() != null) { 5.416 + skip.add(forNode.getModify()); 5.417 + } 5.418 + return true; 5.419 + } 5.420 + 5.421 + @Override 5.422 + public Node leaveDefault(final Node node) { 5.423 + //if this is an assignment to symbol 5.424 + if (!skip.contains(node) && assigns(node, symbol)) { 5.425 + assignmentsInLoop.add(node); 5.426 + } 5.427 + return node; 5.428 + } 5.429 + }); 5.430 + 5.431 + return !assignmentsInLoop.isEmpty(); 5.432 + } 5.433 + 5.434 + /** 5.435 + * Check for a loop counter. This is currently quite conservative, in that it only handles 5.436 + * x <= counter and x < counter. 5.437 + * 5.438 + * @param node loop node to check 5.439 + * @return 5.440 + */ 5.441 + private static Symbol findLoopCounter(final LoopNode node) { 5.442 + final Node test = node.getTest(); 5.443 + 5.444 + if (test != null && test.isComparison()) { 5.445 + final BinaryNode binaryNode = (BinaryNode)test; 5.446 + final Node lhs = binaryNode.lhs(); 5.447 + final Node rhs = binaryNode.rhs(); 5.448 + 5.449 + //detect ident cmp int_literal 5.450 + if (lhs instanceof IdentNode && rhs instanceof LiteralNode && ((LiteralNode<?>)rhs).getType().isInteger()) { 5.451 + final Symbol symbol = lhs.getSymbol(); 5.452 + final int margin = ((LiteralNode<?>)rhs).getInt32(); 5.453 + final TokenType op = test.tokenType(); 5.454 + 5.455 + switch (op) { 5.456 + case LT: 5.457 + case LE: 5.458 + symbol.setRange(RANGE.join(symbol.getRange(), Range.createRange(op == TokenType.LT ? margin - 1 : margin))); 5.459 + return symbol; 5.460 + case GT: 5.461 + case GE: 5.462 + //setRange(lhs, Range.createRange(op == TokenType.GT ? margin + 1 : margin)); 5.463 + //return symbol; 5.464 + default: 5.465 + break; 5.466 + } 5.467 + } 5.468 + } 5.469 + 5.470 + return null; 5.471 + } 5.472 + 5.473 + private boolean isLoopCounter(final LoopNode loopNode, final Symbol symbol) { 5.474 + //this only works if loop nodes aren't replaced by other ones during this transform, but they are not 5.475 + return loopCounters.get(loopNode) == symbol; 5.476 + } 5.477 +}
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 6.2 +++ b/src/jdk/nashorn/internal/codegen/types/Range.java Mon May 20 16:38:38 2013 +0200 6.3 @@ -0,0 +1,705 @@ 6.4 +/* 6.5 + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 6.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 6.7 + * 6.8 + * This code is free software; you can redistribute it and/or modify it 6.9 + * under the terms of the GNU General Public License version 2 only, as 6.10 + * published by the Free Software Foundation. Oracle designates this 6.11 + * particular file as subject to the "Classpath" exception as provided 6.12 + * by Oracle in the LICENSE file that accompanied this code. 6.13 + * 6.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 6.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 6.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 6.17 + * version 2 for more details (a copy is included in the LICENSE file that 6.18 + * accompanied this code). 6.19 + * 6.20 + * You should have received a copy of the GNU General Public License version 6.21 + * 2 along with this work; if not, write to the Free Software Foundation, 6.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 6.23 + * 6.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 6.25 + * or visit www.oracle.com if you need additional information or have any 6.26 + * questions. 6.27 + */ 6.28 + 6.29 +package jdk.nashorn.internal.codegen.types; 6.30 + 6.31 +import java.util.Arrays; 6.32 +import java.util.Collections; 6.33 +import java.util.List; 6.34 + 6.35 +import jdk.nashorn.internal.runtime.DebugLogger; 6.36 +import jdk.nashorn.internal.runtime.JSType; 6.37 + 6.38 +/** 6.39 + * Represents the value range of a symbol. 6.40 + */ 6.41 +public abstract class Range { 6.42 + 6.43 + private static final Range GENERIC_RANGE = new Range() { 6.44 + @Override 6.45 + public Type getType() { 6.46 + return Type.OBJECT; 6.47 + } 6.48 + }; 6.49 + 6.50 + private static final Range NUMBER_RANGE = new Range() { 6.51 + @Override 6.52 + public Type getType() { 6.53 + return Type.NUMBER; 6.54 + } 6.55 + }; 6.56 + 6.57 + private static final Range UNKNOWN_RANGE = new Range() { 6.58 + @Override 6.59 + public Type getType() { 6.60 + return Type.UNKNOWN; 6.61 + } 6.62 + 6.63 + @Override 6.64 + public boolean isUnknown() { 6.65 + return true; 6.66 + } 6.67 + }; 6.68 + 6.69 + private static class IntegerRange extends Range { 6.70 + private final long min; 6.71 + private final long max; 6.72 + private final Type type; 6.73 + 6.74 + private IntegerRange(final long min, final long max) { 6.75 + assert min <= max; 6.76 + this.min = min; 6.77 + this.max = max; 6.78 + this.type = typeFromRange(min, max); 6.79 + } 6.80 + 6.81 + private static Type typeFromRange(final long from, final long to) { 6.82 + if (from >= Integer.MIN_VALUE && to <= Integer.MAX_VALUE) { 6.83 + return Type.INT; 6.84 + } 6.85 + return Type.LONG; 6.86 + } 6.87 + 6.88 + @Override 6.89 + public Type getType() { 6.90 + return type; 6.91 + } 6.92 + 6.93 + public long getMin() { 6.94 + return min; 6.95 + } 6.96 + 6.97 + public long getMax() { 6.98 + return max; 6.99 + } 6.100 + 6.101 + @Override 6.102 + public boolean isIntegerConst() { 6.103 + return getMin() == getMax(); 6.104 + } 6.105 + 6.106 + private long getBitMask() { 6.107 + if (min == max) { 6.108 + return min; 6.109 + } 6.110 + 6.111 + if (min < 0) { 6.112 + return ~0L; 6.113 + } 6.114 + 6.115 + long mask = 1; 6.116 + while (mask < max) { 6.117 + mask = (mask << 1) | 1; 6.118 + } 6.119 + return mask; 6.120 + } 6.121 + 6.122 + @Override 6.123 + public boolean equals(final Object obj) { 6.124 + if (obj instanceof IntegerRange) { 6.125 + final IntegerRange other = (IntegerRange)obj; 6.126 + return this.type == other.type && this.min == other.min && this.max == other.max; 6.127 + } 6.128 + return false; 6.129 + } 6.130 + 6.131 + @Override 6.132 + public int hashCode() { 6.133 + return Long.hashCode(min) ^ Long.hashCode(max); 6.134 + } 6.135 + 6.136 + @Override 6.137 + public String toString() { 6.138 + return super.toString() + "[" + min +", " + max + "]"; 6.139 + } 6.140 + } 6.141 + 6.142 + /** 6.143 + * Get narrowest type for this range 6.144 + * @return type 6.145 + */ 6.146 + public abstract Type getType(); 6.147 + 6.148 + /** 6.149 + * Is this range unknown 6.150 + * @return true if unknown 6.151 + */ 6.152 + public boolean isUnknown() { 6.153 + return false; 6.154 + } 6.155 + 6.156 + /** 6.157 + * Check if an integer is enough to span this range 6.158 + * @return true if integer is enough 6.159 + */ 6.160 + public boolean isIntegerType() { 6.161 + return this instanceof IntegerRange; 6.162 + } 6.163 + 6.164 + /** 6.165 + * Check if an integer is enough to span this range 6.166 + * @return true if integer is enough 6.167 + */ 6.168 + public boolean isIntegerConst() { 6.169 + return false; 6.170 + } 6.171 + 6.172 + /** 6.173 + * Create an unknown range - this is most likely a singleton object 6.174 + * and it represents "we have no known range information" 6.175 + * @return the range 6.176 + */ 6.177 + public static Range createUnknownRange() { 6.178 + return UNKNOWN_RANGE; 6.179 + } 6.180 + 6.181 + /** 6.182 + * Create a constant range: [value, value] 6.183 + * @param value value 6.184 + * @return the range 6.185 + */ 6.186 + public static Range createRange(final int value) { 6.187 + return createIntegerRange(value, value); 6.188 + } 6.189 + 6.190 + /** 6.191 + * Create a constant range: [value, value] 6.192 + * @param value value 6.193 + * @return the range 6.194 + */ 6.195 + public static Range createRange(final long value) { 6.196 + return createIntegerRange(value, value); 6.197 + } 6.198 + 6.199 + /** 6.200 + * Create a constant range: [value, value] 6.201 + * @param value value 6.202 + * @return the range 6.203 + */ 6.204 + public static Range createRange(final double value) { 6.205 + if (isRepresentableAsLong(value)) { 6.206 + return createIntegerRange((long) value, (long) value); 6.207 + } 6.208 + return createNumberRange(); 6.209 + } 6.210 + 6.211 + /** 6.212 + * Create a constant range: [value, value] 6.213 + * @param value value 6.214 + * @return the range 6.215 + */ 6.216 + public static Range createRange(final Object value) { 6.217 + if (value instanceof Integer) { 6.218 + return createRange((int)value); 6.219 + } else if (value instanceof Long) { 6.220 + return createRange((long)value); 6.221 + } else if (value instanceof Double) { 6.222 + return createRange((double)value); 6.223 + } 6.224 + 6.225 + return createGenericRange(); 6.226 + } 6.227 + 6.228 + /** 6.229 + * Create a generic range - object symbol that carries no range 6.230 + * information 6.231 + * @return the range 6.232 + */ 6.233 + public static Range createGenericRange() { 6.234 + return GENERIC_RANGE; 6.235 + } 6.236 + 6.237 + /** 6.238 + * Create a number range - number symbol that carries no range 6.239 + * information 6.240 + * @return the range 6.241 + */ 6.242 + public static Range createNumberRange() { 6.243 + return NUMBER_RANGE; 6.244 + } 6.245 + 6.246 + /** 6.247 + * Create an integer range [min, max] 6.248 + * @param min minimum value, inclusive 6.249 + * @param max maximum value, inclusive 6.250 + * @return the range 6.251 + */ 6.252 + public static IntegerRange createIntegerRange(final long min, final long max) { 6.253 + return new IntegerRange(min, max); 6.254 + } 6.255 + 6.256 + /** 6.257 + * Create an integer range of maximum type width for the given type 6.258 + * @param type the type 6.259 + * @return the range 6.260 + */ 6.261 + public static IntegerRange createIntegerRange(final Type type) { 6.262 + assert type.isNumeric() && !type.isNumber(); 6.263 + final long min; 6.264 + final long max; 6.265 + if (type.isInteger()) { 6.266 + min = Integer.MIN_VALUE; 6.267 + max = Integer.MAX_VALUE; 6.268 + } else if (type.isLong()) { 6.269 + min = Long.MIN_VALUE; 6.270 + max = Long.MAX_VALUE; 6.271 + } else { 6.272 + throw new AssertionError(); //type incompatible with integer range 6.273 + } 6.274 + return new IntegerRange(min, max); 6.275 + } 6.276 + 6.277 + /** 6.278 + * Create an range of maximum type width for the given type 6.279 + * @param type the type 6.280 + * @return the range 6.281 + */ 6.282 + public static Range createTypeRange(final Type type) { 6.283 + if (type.isNumber()) { 6.284 + return createNumberRange(); 6.285 + } else if (type.isNumeric()) { 6.286 + return createIntegerRange(type); 6.287 + } else { 6.288 + return createGenericRange(); 6.289 + } 6.290 + } 6.291 + 6.292 + // check that add doesn't overflow 6.293 + private static boolean checkAdd(final long a, final long b) { 6.294 + final long result = a + b; 6.295 + return ((a ^ result) & (b ^ result)) >= 0; 6.296 + } 6.297 + 6.298 + // check that sub doesn't overflow 6.299 + private static boolean checkSub(final long a, final long b) { 6.300 + final long result = a - b; 6.301 + return ((a ^ result) & (b ^ result)) >= 0; 6.302 + } 6.303 + 6.304 + private static boolean checkMul(final long a, final long b) { 6.305 + // TODO correct overflow check 6.306 + return a >= Integer.MIN_VALUE && a <= Integer.MAX_VALUE && b >= Integer.MIN_VALUE && b <= Integer.MAX_VALUE; 6.307 + } 6.308 + 6.309 + /** 6.310 + * The range functionality class responsible for merging ranges and drawing 6.311 + * range conclusions from operations executed 6.312 + */ 6.313 + public static class Functionality { 6.314 + /** logger */ 6.315 + protected final DebugLogger log; 6.316 + 6.317 + /** 6.318 + * Constructor 6.319 + * @param log logger 6.320 + */ 6.321 + public Functionality(final DebugLogger log) { 6.322 + this.log = log; 6.323 + } 6.324 + 6.325 + /** 6.326 + * Join two ranges 6.327 + * @param a first range 6.328 + * @param b second range 6.329 + * @return the joined range 6.330 + */ 6.331 + public Range join(final Range a, final Range b) { 6.332 + if (a.equals(b)) { 6.333 + return a; 6.334 + } 6.335 + 6.336 + Type joinedType = a.getType(); 6.337 + if (a.getType() != b.getType()) { 6.338 + if (a.isUnknown()) { 6.339 + return b; 6.340 + } 6.341 + if (b.isUnknown()) { 6.342 + return a; 6.343 + } 6.344 + 6.345 + joinedType = Type.widest(a.getType(), b.getType()); 6.346 + } 6.347 + 6.348 + if (joinedType.isInteger() || joinedType.isLong()) { 6.349 + return createIntegerRange( 6.350 + Math.min(((IntegerRange) a).getMin(), ((IntegerRange) b).getMin()), 6.351 + Math.max(((IntegerRange) a).getMax(), ((IntegerRange) b).getMax())); 6.352 + } 6.353 + 6.354 + return createTypeRange(joinedType); 6.355 + } 6.356 + 6.357 + /** 6.358 + * Add operation 6.359 + * @param a range of first symbol to be added 6.360 + * @param b range of second symbol to be added 6.361 + * @return resulting range representing the value range after add 6.362 + */ 6.363 + public Range add(final Range a, final Range b) { 6.364 + if (a.isIntegerType() && b.isIntegerType()) { 6.365 + final IntegerRange lhs = (IntegerRange)a; 6.366 + final IntegerRange rhs = (IntegerRange)b; 6.367 + if (checkAdd(lhs.getMin(), rhs.getMin()) && checkAdd(lhs.getMax(), rhs.getMax())) { 6.368 + return createIntegerRange(lhs.getMin() + rhs.getMin(), lhs.getMax() + rhs.getMax()); 6.369 + } 6.370 + } 6.371 + 6.372 + if (a.getType().isNumeric() && b.getType().isNumeric()) { 6.373 + return createNumberRange(); 6.374 + } 6.375 + 6.376 + return createGenericRange(); 6.377 + } 6.378 + 6.379 + /** 6.380 + * Sub operation 6.381 + * @param a range of first symbol to be subtracted 6.382 + * @param b range of second symbol to be subtracted 6.383 + * @return resulting range representing the value range after subtraction 6.384 + */ 6.385 + public Range sub(final Range a, final Range b) { 6.386 + if (a.isIntegerType() && b.isIntegerType()) { 6.387 + final IntegerRange lhs = (IntegerRange)a; 6.388 + final IntegerRange rhs = (IntegerRange)b; 6.389 + if (checkSub(lhs.getMin(), rhs.getMax()) && checkSub(lhs.getMax(), rhs.getMin())) { 6.390 + return createIntegerRange(lhs.getMin() - rhs.getMax(), lhs.getMax() - rhs.getMin()); 6.391 + } 6.392 + } 6.393 + 6.394 + if (a.getType().isNumeric() && b.getType().isNumeric()) { 6.395 + return createNumberRange(); 6.396 + } 6.397 + 6.398 + return createGenericRange(); 6.399 + } 6.400 + 6.401 + /** 6.402 + * Mul operation 6.403 + * @param a range of first symbol to be multiplied 6.404 + * @param b range of second symbol to be multiplied 6.405 + * @return resulting range representing the value range after multiplication 6.406 + */ 6.407 + public Range mul(final Range a, final Range b) { 6.408 + if (a.isIntegerType() && b.isIntegerType()) { 6.409 + final IntegerRange lhs = (IntegerRange)a; 6.410 + final IntegerRange rhs = (IntegerRange)b; 6.411 + 6.412 + //ensure that nothing ever overflows or underflows 6.413 + if (checkMul(lhs.getMin(), rhs.getMin()) && 6.414 + checkMul(lhs.getMax(), rhs.getMax()) && 6.415 + checkMul(lhs.getMin(), rhs.getMax()) && 6.416 + checkMul(lhs.getMax(), rhs.getMin())) { 6.417 + 6.418 + final List<Long> results = 6.419 + Arrays.asList( 6.420 + lhs.getMin() * rhs.getMin(), 6.421 + lhs.getMin() * rhs.getMax(), 6.422 + lhs.getMax() * rhs.getMin(), 6.423 + lhs.getMax() * rhs.getMax()); 6.424 + return createIntegerRange(Collections.min(results), Collections.max(results)); 6.425 + } 6.426 + } 6.427 + 6.428 + if (a.getType().isNumeric() && b.getType().isNumeric()) { 6.429 + return createNumberRange(); 6.430 + } 6.431 + 6.432 + return createGenericRange(); 6.433 + } 6.434 + 6.435 + /** 6.436 + * Neg operation 6.437 + * @param a range of value symbol to be negated 6.438 + * @return resulting range representing the value range after neg 6.439 + */ 6.440 + public Range neg(final Range a) { 6.441 + if (a.isIntegerType()) { 6.442 + final IntegerRange rhs = (IntegerRange)a; 6.443 + if (rhs.getMin() != Long.MIN_VALUE && rhs.getMax() != Long.MIN_VALUE) { 6.444 + return createIntegerRange(-rhs.getMax(), -rhs.getMin()); 6.445 + } 6.446 + } 6.447 + 6.448 + if (a.getType().isNumeric()) { 6.449 + return createNumberRange(); 6.450 + } 6.451 + 6.452 + return createGenericRange(); 6.453 + } 6.454 + 6.455 + /** 6.456 + * Bitwise and operation 6.457 + * @param a range of first symbol to be and:ed 6.458 + * @param b range of second symbol to be and:ed 6.459 + * @return resulting range representing the value range after and 6.460 + */ 6.461 + public Range and(final Range a, final Range b) { 6.462 + if (a.isIntegerType() && b.isIntegerType()) { 6.463 + final int resultMask = (int) (((IntegerRange)a).getBitMask() & ((IntegerRange)b).getBitMask()); 6.464 + if (resultMask >= 0) { 6.465 + return createIntegerRange(0, resultMask); 6.466 + } 6.467 + } else if (a.isUnknown() && b.isIntegerType()) { 6.468 + final long operandMask = ((IntegerRange)b).getBitMask(); 6.469 + if (operandMask >= 0) { 6.470 + return createIntegerRange(0, operandMask); 6.471 + } 6.472 + } else if (a.isIntegerType() && b.isUnknown()) { 6.473 + final long operandMask = ((IntegerRange)a).getBitMask(); 6.474 + if (operandMask >= 0) { 6.475 + return createIntegerRange(0, operandMask); 6.476 + } 6.477 + } 6.478 + 6.479 + return createTypeRange(Type.INT); 6.480 + } 6.481 + 6.482 + /** 6.483 + * Bitwise or operation 6.484 + * @param a range of first symbol to be or:ed 6.485 + * @param b range of second symbol to be or:ed 6.486 + * @return resulting range representing the value range after or 6.487 + */ 6.488 + public Range or(final Range a, final Range b) { 6.489 + if (a.isIntegerType() && b.isIntegerType()) { 6.490 + final int resultMask = (int)(((IntegerRange)a).getBitMask() | ((IntegerRange)b).getBitMask()); 6.491 + if (resultMask >= 0) { 6.492 + return createIntegerRange(0, resultMask); 6.493 + } 6.494 + } 6.495 + 6.496 + return createTypeRange(Type.INT); 6.497 + } 6.498 + 6.499 + /** 6.500 + * Bitwise xor operation 6.501 + * @param a range of first symbol to be xor:ed 6.502 + * @param b range of second symbol to be xor:ed 6.503 + * @return resulting range representing the value range after and 6.504 + */ 6.505 + public Range xor(final Range a, final Range b) { 6.506 + if (a.isIntegerConst() && b.isIntegerConst()) { 6.507 + return createRange(((IntegerRange)a).getMin() ^ ((IntegerRange)b).getMin()); 6.508 + } 6.509 + 6.510 + if (a.isIntegerType() && b.isIntegerType()) { 6.511 + final int resultMask = (int)(((IntegerRange)a).getBitMask() | ((IntegerRange)b).getBitMask()); 6.512 + if (resultMask >= 0) { 6.513 + return createIntegerRange(0, createIntegerRange(0, resultMask).getBitMask()); 6.514 + } 6.515 + } 6.516 + return createTypeRange(Type.INT); 6.517 + } 6.518 + 6.519 + /** 6.520 + * Bitwise shl operation 6.521 + * @param a range of first symbol to be shl:ed 6.522 + * @param b range of second symbol to be shl:ed 6.523 + * @return resulting range representing the value range after shl 6.524 + */ 6.525 + public Range shl(final Range a, final Range b) { 6.526 + if (b.isIntegerType() && b.isIntegerConst()) { 6.527 + final IntegerRange left = (IntegerRange)(a.isIntegerType() ? a : createTypeRange(Type.INT)); 6.528 + final int shift = (int)((IntegerRange) b).getMin() & 0x1f; 6.529 + final int min = (int)left.getMin() << shift; 6.530 + final int max = (int)left.getMax() << shift; 6.531 + if (min >> shift == left.getMin() && max >> shift == left.getMax()) { 6.532 + return createIntegerRange(min, max); 6.533 + } 6.534 + } 6.535 + 6.536 + return createTypeRange(Type.INT); 6.537 + } 6.538 + 6.539 + /** 6.540 + * Bitwise shr operation 6.541 + * @param a range of first symbol to be shr:ed 6.542 + * @param b range of second symbol to be shr:ed 6.543 + * @return resulting range representing the value range after shr 6.544 + */ 6.545 + public Range shr(final Range a, final Range b) { 6.546 + if (b.isIntegerType() && b.isIntegerConst()) { 6.547 + final long shift = ((IntegerRange) b).getMin() & 0x1f; 6.548 + final IntegerRange left = (IntegerRange)(a.isIntegerType() ? a : createTypeRange(Type.INT)); 6.549 + if (left.getMin() >= 0) { 6.550 + long min = left.getMin() >>> shift; 6.551 + long max = left.getMax() >>> shift; 6.552 + return createIntegerRange(min, max); 6.553 + } else if (shift >= 1) { 6.554 + return createIntegerRange(0, JSType.MAX_UINT >>> shift); 6.555 + } 6.556 + } 6.557 + 6.558 + return createTypeRange(Type.INT); 6.559 + } 6.560 + 6.561 + /** 6.562 + * Bitwise sar operation 6.563 + * @param a range of first symbol to be sar:ed 6.564 + * @param b range of second symbol to be sar:ed 6.565 + * @return resulting range representing the value range after sar 6.566 + */ 6.567 + public Range sar(final Range a, final Range b) { 6.568 + if (b.isIntegerType() && b.isIntegerConst()) { 6.569 + final IntegerRange left = (IntegerRange)(a.isIntegerType() ? a : createTypeRange(Type.INT)); 6.570 + final long shift = ((IntegerRange) b).getMin() & 0x1f; 6.571 + final long min = left.getMin() >> shift; 6.572 + final long max = left.getMax() >> shift; 6.573 + return createIntegerRange(min, max); 6.574 + } 6.575 + 6.576 + return createTypeRange(Type.INT); 6.577 + } 6.578 + 6.579 + /** 6.580 + * Modulo operation 6.581 + * @param a range of first symbol to the mod operation 6.582 + * @param b range of second symbol to be mod operation 6.583 + * @return resulting range representing the value range after mod 6.584 + */ 6.585 + public Range mod(final Range a, final Range b) { 6.586 + if (a.isIntegerType() && b.isIntegerType()) { 6.587 + final IntegerRange rhs = (IntegerRange) b; 6.588 + if (rhs.getMin() > 0 || rhs.getMax() < 0) { // divisor range must not include 0 6.589 + final long absmax = Math.max(Math.abs(rhs.getMin()), Math.abs(rhs.getMax())) - 1; 6.590 + return createIntegerRange(rhs.getMin() > 0 ? 0 : -absmax, rhs.getMax() < 0 ? 0 : +absmax); 6.591 + } 6.592 + } 6.593 + return createTypeRange(Type.NUMBER); 6.594 + } 6.595 + 6.596 + /** 6.597 + * Division operation 6.598 + * @param a range of first symbol to the division 6.599 + * @param b range of second symbol to be division 6.600 + * @return resulting range representing the value range after division 6.601 + */ 6.602 + public Range div(final Range a, final Range b) { 6.603 + // TODO 6.604 + return createTypeRange(Type.NUMBER); 6.605 + } 6.606 + } 6.607 + 6.608 + /** 6.609 + * Simple trace functionality that will log range creation 6.610 + */ 6.611 + public static class TraceFunctionality extends Functionality { 6.612 + TraceFunctionality(final DebugLogger log) { 6.613 + super(log); 6.614 + } 6.615 + 6.616 + private Range trace(final Range result, final String operation, final Range... operands) { 6.617 + log.fine("range::" + operation + Arrays.toString(operands) + " => " + result); 6.618 + return result; 6.619 + } 6.620 + 6.621 + @Override 6.622 + public Range join(final Range a, final Range b) { 6.623 + final Range result = super.join(a, b); 6.624 + if (!a.equals(b)) { 6.625 + trace(result, "join", a, b); 6.626 + } 6.627 + return result; 6.628 + } 6.629 + 6.630 + @Override 6.631 + public Range add(final Range a, final Range b) { 6.632 + return trace(super.add(a, b), "add", a, b); 6.633 + } 6.634 + 6.635 + @Override 6.636 + public Range sub(final Range a, final Range b) { 6.637 + return trace(super.sub(a, b), "sub", a, b); 6.638 + } 6.639 + 6.640 + @Override 6.641 + public Range mul(final Range a, final Range b) { 6.642 + return trace(super.mul(a, b), "mul", a, b); 6.643 + } 6.644 + 6.645 + @Override 6.646 + public Range neg(final Range a) { 6.647 + return trace(super.neg(a), "neg", a); 6.648 + } 6.649 + 6.650 + @Override 6.651 + public Range and(final Range a, final Range b) { 6.652 + return trace(super.and(a, b), "and", a, b); 6.653 + } 6.654 + 6.655 + @Override 6.656 + public Range or(final Range a, final Range b) { 6.657 + return trace(super.or(a, b), "or", a, b); 6.658 + } 6.659 + 6.660 + @Override 6.661 + public Range xor(final Range a, final Range b) { 6.662 + return trace(super.xor(a, b), "xor", a, b); 6.663 + } 6.664 + 6.665 + @Override 6.666 + public Range shl(final Range a, final Range b) { 6.667 + return trace(super.shl(a, b), "shl", a, b); 6.668 + } 6.669 + 6.670 + @Override 6.671 + public Range shr(final Range a, final Range b) { 6.672 + return trace(super.shr(a, b), "shr", a, b); 6.673 + } 6.674 + 6.675 + @Override 6.676 + public Range sar(final Range a, final Range b) { 6.677 + return trace(super.sar(a, b), "sar", a, b); 6.678 + } 6.679 + 6.680 + @Override 6.681 + public Range mod(final Range a, final Range b) { 6.682 + return trace(super.mod(a, b), "mod", a, b); 6.683 + } 6.684 + 6.685 + @Override 6.686 + public Range div(final Range a, final Range b) { 6.687 + return trace(super.div(a, b), "div", a, b); 6.688 + } 6.689 + } 6.690 + 6.691 + @Override 6.692 + public String toString() { 6.693 + return String.valueOf(getType()); 6.694 + } 6.695 + 6.696 + @SuppressWarnings("unused") 6.697 + private static boolean isRepresentableAsInt(final double number) { 6.698 + return (int)number == number && !isNegativeZero(number); 6.699 + } 6.700 + 6.701 + private static boolean isRepresentableAsLong(final double number) { 6.702 + return (long)number == number && !isNegativeZero(number); 6.703 + } 6.704 + 6.705 + private static boolean isNegativeZero(final double number) { 6.706 + return Double.doubleToLongBits(number) == Double.doubleToLongBits(-0.0); 6.707 + } 6.708 +}
7.1 --- a/src/jdk/nashorn/internal/codegen/types/Type.java Fri May 17 16:44:22 2013 -0300 7.2 +++ b/src/jdk/nashorn/internal/codegen/types/Type.java Mon May 20 16:38:38 2013 +0200 7.3 @@ -106,23 +106,13 @@ 7.4 Type(final String name, final Class<?> clazz, final int weight, final int slots) { 7.5 this.name = name; 7.6 this.clazz = clazz; 7.7 - this.descriptor = Type.getDescriptor(clazz); 7.8 + this.descriptor = jdk.internal.org.objectweb.asm.Type.getDescriptor(clazz); 7.9 this.weight = weight; 7.10 assert weight >= MIN_WEIGHT && weight <= MAX_WEIGHT : "illegal type weight: " + weight; 7.11 this.slots = slots; 7.12 } 7.13 7.14 /** 7.15 - * Return an internal descriptor for a type 7.16 - * 7.17 - * @param type the type 7.18 - * @return descriptor string 7.19 - */ 7.20 - public static String getDescriptor(final Class<?> type) { 7.21 - return jdk.internal.org.objectweb.asm.Type.getDescriptor(type); 7.22 - } 7.23 - 7.24 - /** 7.25 * Get the weight of this type - use this e.g. for sorting method descriptors 7.26 * @return the weight 7.27 */
8.1 --- a/src/jdk/nashorn/internal/ir/BinaryNode.java Fri May 17 16:44:22 2013 -0300 8.2 +++ b/src/jdk/nashorn/internal/ir/BinaryNode.java Mon May 20 16:38:38 2013 +0200 8.3 @@ -59,6 +59,23 @@ 8.4 this.rhs = rhs; 8.5 } 8.6 8.7 + @Override 8.8 + public boolean isComparison() { 8.9 + switch (tokenType()) { 8.10 + case EQ: 8.11 + case EQ_STRICT: 8.12 + case NE: 8.13 + case NE_STRICT: 8.14 + case LE: 8.15 + case LT: 8.16 + case GE: 8.17 + case GT: 8.18 + return true; 8.19 + default: 8.20 + return false; 8.21 + } 8.22 + } 8.23 + 8.24 /** 8.25 * Return the widest possible type for this operation. This is used for compile time 8.26 * static type inference
9.1 --- a/src/jdk/nashorn/internal/ir/FunctionNode.java Fri May 17 16:44:22 2013 -0300 9.2 +++ b/src/jdk/nashorn/internal/ir/FunctionNode.java Mon May 20 16:38:38 2013 +0200 9.3 @@ -340,7 +340,7 @@ 9.4 * @return true if specialization is possible 9.5 */ 9.6 public boolean canSpecialize() { 9.7 - return getFlag(CAN_SPECIALIZE); 9.8 + return snapshot != null && getFlag(CAN_SPECIALIZE); 9.9 } 9.10 9.11 /**
10.1 --- a/src/jdk/nashorn/internal/ir/LexicalContext.java Fri May 17 16:44:22 2013 -0300 10.2 +++ b/src/jdk/nashorn/internal/ir/LexicalContext.java Mon May 20 16:38:38 2013 +0200 10.3 @@ -440,6 +440,23 @@ 10.4 } 10.5 10.6 /** 10.7 + * Check whether the lexical context is currently inside a loop 10.8 + * @return true if inside a loop 10.9 + */ 10.10 + public boolean inLoop() { 10.11 + return getCurrentLoop() != null; 10.12 + } 10.13 + 10.14 + /** 10.15 + * Returns the loop header of the current loop, or null if not inside a loop 10.16 + * @return loop header 10.17 + */ 10.18 + public LoopNode getCurrentLoop() { 10.19 + final Iterator<LoopNode> iter = new NodeIterator<>(LoopNode.class, getCurrentFunction()); 10.20 + return iter.hasNext() ? iter.next() : null; 10.21 + } 10.22 + 10.23 + /** 10.24 * Find the breakable node corresponding to this label. 10.25 * @param label label to search for, if null the closest breakable node will be returned unconditionally, e.g. a while loop with no label 10.26 * @return closest breakable node 10.27 @@ -461,8 +478,7 @@ 10.28 } 10.29 10.30 private LoopNode getContinueTo() { 10.31 - final Iterator<LoopNode> iter = new NodeIterator<>(LoopNode.class, getCurrentFunction()); 10.32 - return iter.hasNext() ? iter.next() : null; 10.33 + return getCurrentLoop(); 10.34 } 10.35 10.36 /**
11.1 --- a/src/jdk/nashorn/internal/ir/Node.java Fri May 17 16:44:22 2013 -0300 11.2 +++ b/src/jdk/nashorn/internal/ir/Node.java Mon May 20 16:38:38 2013 +0200 11.3 @@ -153,6 +153,14 @@ 11.4 } 11.5 11.6 /** 11.7 + * Returns true if this node represents a comparison operator 11.8 + * @return true if comparison 11.9 + */ 11.10 + public boolean isComparison() { 11.11 + return false; 11.12 + } 11.13 + 11.14 + /** 11.15 * For reference copies - ensure that labels in the copy node are unique 11.16 * using an appropriate copy constructor 11.17 * @param lc lexical context
12.1 --- a/src/jdk/nashorn/internal/ir/Symbol.java Fri May 17 16:44:22 2013 -0300 12.2 +++ b/src/jdk/nashorn/internal/ir/Symbol.java Mon May 20 16:38:38 2013 +0200 12.3 @@ -29,6 +29,8 @@ 12.4 import java.util.HashSet; 12.5 import java.util.Set; 12.6 import java.util.StringTokenizer; 12.7 + 12.8 +import jdk.nashorn.internal.codegen.types.Range; 12.9 import jdk.nashorn.internal.codegen.types.Type; 12.10 import jdk.nashorn.internal.runtime.Context; 12.11 import jdk.nashorn.internal.runtime.Debug; 12.12 @@ -89,6 +91,9 @@ 12.13 /** Number of times this symbol is used in code */ 12.14 private int useCount; 12.15 12.16 + /** Range for symbol */ 12.17 + private Range range; 12.18 + 12.19 /** Debugging option - dump info and stack trace when symbols with given names are manipulated */ 12.20 private static final Set<String> TRACE_SYMBOLS; 12.21 private static final Set<String> TRACE_SYMBOLS_STACKTRACE; 12.22 @@ -131,6 +136,7 @@ 12.23 this.type = type; 12.24 this.slot = slot; 12.25 this.fieldIndex = -1; 12.26 + this.range = Range.createUnknownRange(); 12.27 trace("CREATE SYMBOL"); 12.28 } 12.29 12.30 @@ -157,12 +163,13 @@ 12.31 12.32 private Symbol(final Symbol base, final String name, final int flags) { 12.33 this.flags = flags; 12.34 - this.name = name; 12.35 + this.name = name; 12.36 12.37 this.fieldIndex = base.fieldIndex; 12.38 - this.slot = base.slot; 12.39 - this.type = base.type; 12.40 - this.useCount = base.useCount; 12.41 + this.slot = base.slot; 12.42 + this.type = base.type; 12.43 + this.useCount = base.useCount; 12.44 + this.range = base.range; 12.45 } 12.46 12.47 private static String align(final String string, final int max) { 12.48 @@ -276,7 +283,7 @@ 12.49 12.50 @Override 12.51 public String toString() { 12.52 - final StringBuilder sb = new StringBuilder(); 12.53 + final StringBuilder sb = new StringBuilder(); 12.54 12.55 sb.append(name). 12.56 append(' '). 12.57 @@ -410,6 +417,22 @@ 12.58 } 12.59 12.60 /** 12.61 + * Get the range for this symbol 12.62 + * @return range for symbol 12.63 + */ 12.64 + public Range getRange() { 12.65 + return range; 12.66 + } 12.67 + 12.68 + /** 12.69 + * Set the range for this symbol 12.70 + * @param range range 12.71 + */ 12.72 + public void setRange(final Range range) { 12.73 + this.range = range; 12.74 + } 12.75 + 12.76 + /** 12.77 * Check if this symbol is a function parameter of known 12.78 * narrowest type 12.79 * @return true if parameter
13.1 --- a/src/jdk/nashorn/internal/runtime/CompiledFunction.java Fri May 17 16:44:22 2013 -0300 13.2 +++ b/src/jdk/nashorn/internal/runtime/CompiledFunction.java Mon May 20 16:38:38 2013 +0200 13.3 @@ -35,21 +35,27 @@ 13.4 */ 13.5 final class CompiledFunction implements Comparable<CompiledFunction> { 13.6 13.7 + /** The method type may be more specific than the invoker, if. e.g. 13.8 + * the invoker is guarded, and a guard with a generic object only 13.9 + * fallback, while the target is more specific, we still need the 13.10 + * more specific type for sorting */ 13.11 + private final MethodType type; 13.12 private final MethodHandle invoker; 13.13 private MethodHandle constructor; 13.14 13.15 - CompiledFunction(final MethodHandle invoker) { 13.16 - this(invoker, null); 13.17 + CompiledFunction(final MethodType type, final MethodHandle invoker) { 13.18 + this(type, invoker, null); 13.19 } 13.20 13.21 - CompiledFunction(final MethodHandle invoker, final MethodHandle constructor) { 13.22 - this.invoker = invoker; 13.23 - this.constructor = constructor; //isConstructor 13.24 + CompiledFunction(final MethodType type, final MethodHandle invoker, final MethodHandle constructor) { 13.25 + this.type = type; 13.26 + this.invoker = invoker; 13.27 + this.constructor = constructor; 13.28 } 13.29 13.30 @Override 13.31 public String toString() { 13.32 - return "<invoker=" + invoker + " ctor=" + constructor + ">"; 13.33 + return "<callSiteType= " + type + " invoker=" + invoker + " ctor=" + constructor + ">"; 13.34 } 13.35 13.36 MethodHandle getInvoker() { 13.37 @@ -69,7 +75,7 @@ 13.38 } 13.39 13.40 MethodType type() { 13.41 - return invoker.type(); 13.42 + return type; 13.43 } 13.44 13.45 @Override 13.46 @@ -103,8 +109,8 @@ 13.47 return weight() > o.weight(); 13.48 } 13.49 13.50 - boolean moreGenericThan(final MethodType type) { 13.51 - return weight() > weight(type); 13.52 + boolean moreGenericThan(final MethodType mt) { 13.53 + return weight() > weight(mt); 13.54 } 13.55 13.56 /** 13.57 @@ -112,15 +118,15 @@ 13.58 * It is compatible if the types are narrower than the invocation type so that 13.59 * a semantically equivalent linkage can be performed. 13.60 * 13.61 - * @param typesc 13.62 + * @param mt type to check against 13.63 * @return 13.64 */ 13.65 - boolean typeCompatible(final MethodType type) { 13.66 - final Class<?>[] wantedParams = type.parameterArray(); 13.67 + boolean typeCompatible(final MethodType mt) { 13.68 + final Class<?>[] wantedParams = mt.parameterArray(); 13.69 final Class<?>[] existingParams = type().parameterArray(); 13.70 13.71 //if we are not examining a varargs type, the number of parameters must be the same 13.72 - if (wantedParams.length != existingParams.length && !isVarArgsType(type)) { 13.73 + if (wantedParams.length != existingParams.length && !isVarArgsType(mt)) { 13.74 return false; 13.75 } 13.76
14.1 --- a/src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java Fri May 17 16:44:22 2013 -0300 14.2 +++ b/src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java Mon May 20 16:38:38 2013 +0200 14.3 @@ -78,9 +78,9 @@ 14.4 //only nasgen constructors: (boolean, self, args) are subject to binding a boolean newObj. isConstructor 14.5 //is too conservative a check. However, isConstructor(mh) always implies isConstructor param 14.6 assert isConstructor(); 14.7 - code.add(new CompiledFunction(MH.insertArguments(mh, 0, false), composeConstructor(MH.insertArguments(mh, 0, true), needsCallee))); //make sure callee state can be determined when we reach constructor 14.8 + code.add(new CompiledFunction(mh.type(), MH.insertArguments(mh, 0, false), composeConstructor(MH.insertArguments(mh, 0, true), needsCallee))); //make sure callee state can be determined when we reach constructor 14.9 } else { 14.10 - code.add(new CompiledFunction(mh)); 14.11 + code.add(new CompiledFunction(mh.type(), mh)); 14.12 } 14.13 } 14.14
15.1 --- a/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java Fri May 17 16:44:22 2013 -0300 15.2 +++ b/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java Mon May 20 16:38:38 2013 +0200 15.3 @@ -30,6 +30,8 @@ 15.4 import java.lang.invoke.MethodHandle; 15.5 import java.lang.invoke.MethodHandles; 15.6 import java.lang.invoke.MethodType; 15.7 +import java.util.ArrayList; 15.8 +import java.util.Arrays; 15.9 import java.util.LinkedList; 15.10 15.11 import jdk.nashorn.internal.codegen.Compiler; 15.12 @@ -49,9 +51,16 @@ 15.13 */ 15.14 public final class RecompilableScriptFunctionData extends ScriptFunctionData { 15.15 15.16 + /** FunctionNode with the code for this ScriptFunction */ 15.17 private FunctionNode functionNode; 15.18 - private final PropertyMap allocatorMap; 15.19 + 15.20 + /** Allocator map from makeMap() */ 15.21 + private final PropertyMap allocatorMap; 15.22 + 15.23 + /** Code installer used for all further recompilation/specialization of this ScriptFunction */ 15.24 private final CodeInstaller<ScriptEnvironment> installer; 15.25 + 15.26 + /** Name of class where allocator function resides */ 15.27 private final String allocatorClassName; 15.28 15.29 /** lazily generated allocator */ 15.30 @@ -60,6 +69,23 @@ 15.31 private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); 15.32 15.33 /** 15.34 + * Used for specialization based on runtime arguments. Whenever we specialize on 15.35 + * callsite parameter types at runtime, we need to use a parameter type guard to 15.36 + * ensure that the specialized version of the script function continues to be 15.37 + * applicable for a particular callsite * 15.38 + */ 15.39 + private static final MethodHandle PARAM_TYPE_GUARD = findOwnMH("paramTypeGuard", boolean.class, Type[].class, Object[].class); 15.40 + 15.41 + /** 15.42 + * It is usually a good gamble whever we detect a runtime callsite with a double 15.43 + * (or java.lang.Number instance) to specialize the parameter to an integer, if the 15.44 + * parameter in question can be represented as one. The double typically only exists 15.45 + * because the compiler doesn't know any better than "a number type" and conservatively 15.46 + * picks doubles when it can't prove that an integer addition wouldn't overflow 15.47 + */ 15.48 + private static final MethodHandle ENSURE_INT = findOwnMH("ensureInt", int.class, Object.class); 15.49 + 15.50 + /** 15.51 * Constructor - public as scripts use it 15.52 * 15.53 * @param functionNode functionNode that represents this function code 15.54 @@ -141,14 +167,6 @@ 15.55 return; // nothing to do, we have code, at least some. 15.56 } 15.57 15.58 - // check if function node is lazy, need to compile it. 15.59 - // note that currently function cloning is not working completely, which 15.60 - // means that the compiler will mutate the function node it has been given 15.61 - // once it has been compiled, it cannot be recompiled. This means that 15.62 - // lazy compilation works (not compiled yet) but e.g. specializations won't 15.63 - // until the copy-on-write changes for IR are in, making cloning meaningless. 15.64 - // therefore, currently method specialization is disabled. TODO 15.65 - 15.66 if (functionNode.isLazy()) { 15.67 Compiler.LOG.info("Trampoline hit: need to do lazy compilation of '", functionNode.getName(), "'"); 15.68 final Compiler compiler = new Compiler(installer); 15.69 @@ -156,38 +174,55 @@ 15.70 assert !functionNode.isLazy(); 15.71 compiler.install(functionNode); 15.72 15.73 - // we don't need to update any flags - varArgs and needsCallee are instrincic 15.74 - // in the function world we need to get a destination node from the compile instead 15.75 - // and replace it with our function node. TODO 15.76 + /* 15.77 + * We don't need to update any flags - varArgs and needsCallee are instrincic 15.78 + * in the function world we need to get a destination node from the compile instead 15.79 + * and replace it with our function node. TODO 15.80 + */ 15.81 } 15.82 15.83 - // we can't get here unless we have bytecode, either from eager compilation or from 15.84 - // running a lazy compile on the lines above 15.85 + /* 15.86 + * We can't get to this program point unless we have bytecode, either from 15.87 + * eager compilation or from running a lazy compile on the lines above 15.88 + */ 15.89 15.90 assert functionNode.hasState(CompilationState.EMITTED) : functionNode.getName() + " " + functionNode.getState() + " " + Debug.id(functionNode); 15.91 15.92 // code exists - look it up and add it into the automatically sorted invoker list 15.93 - addCode(functionNode, null, null); 15.94 + addCode(functionNode); 15.95 } 15.96 15.97 - private MethodHandle addCode(final FunctionNode fn, final MethodHandle guard, final MethodHandle fallback) { 15.98 - final MethodHandle target = 15.99 + private MethodHandle addCode(final FunctionNode fn) { 15.100 + return addCode(fn, null, null, null); 15.101 + } 15.102 + 15.103 + private MethodHandle addCode(final FunctionNode fn, final MethodType runtimeType, final MethodHandle guard, final MethodHandle fallback) { 15.104 + final MethodType targetType = new FunctionSignature(fn).getMethodType(); 15.105 + MethodHandle target = 15.106 MH.findStatic( 15.107 LOOKUP, 15.108 fn.getCompileUnit().getCode(), 15.109 fn.getName(), 15.110 - new FunctionSignature(fn). 15.111 - getMethodType()); 15.112 - MethodHandle mh = target; 15.113 - if (guard != null) { 15.114 - try { 15.115 - mh = MH.guardWithTest(MH.asCollector(guard, Object[].class, target.type().parameterCount()), MH.asType(target, fallback.type()), fallback); 15.116 - } catch (Throwable e) { 15.117 - e.printStackTrace(); 15.118 + targetType); 15.119 + 15.120 + /* 15.121 + * For any integer argument. a double that is representable as an integer is OK. 15.122 + * otherwise the guard would have failed. in that case introduce a filter that 15.123 + * casts the double to an integer, which we know will preserve all precision. 15.124 + */ 15.125 + for (int i = 0; i < targetType.parameterCount(); i++) { 15.126 + if (targetType.parameterType(i) == int.class) { 15.127 + //representable as int 15.128 + target = MH.filterArguments(target, i, ENSURE_INT); 15.129 } 15.130 } 15.131 15.132 - final CompiledFunction cf = new CompiledFunction(mh); 15.133 + MethodHandle mh = target; 15.134 + if (guard != null) { 15.135 + mh = MH.guardWithTest(MH.asCollector(guard, Object[].class, target.type().parameterCount()), MH.asType(target, fallback.type()), fallback); 15.136 + } 15.137 + 15.138 + final CompiledFunction cf = new CompiledFunction(runtimeType == null ? targetType : runtimeType, mh); 15.139 code.add(cf); 15.140 15.141 return cf.getInvoker(); 15.142 @@ -212,69 +247,162 @@ 15.143 return Type.OBJECT; 15.144 } 15.145 15.146 - @SuppressWarnings("unused") 15.147 - private static boolean paramTypeGuard(final Type[] compileTimeTypes, final Type[] runtimeTypes, Object... args) { 15.148 - //System.err.println("Param type guard " + Arrays.asList(args)); 15.149 + private static boolean canCoerce(final Object arg, final Type type) { 15.150 + Type argType = runtimeType(arg); 15.151 + if (Type.widest(argType, type) == type || arg == ScriptRuntime.UNDEFINED) { 15.152 + return true; 15.153 + } 15.154 + System.err.println(arg + " does not fit in "+ argType + " " + type + " " + arg.getClass()); 15.155 + new Throwable().printStackTrace(); 15.156 return false; 15.157 } 15.158 15.159 - private static final MethodHandle PARAM_TYPE_GUARD = findOwnMH("paramTypeGuard", boolean.class, Type[].class, Type[].class, Object[].class); 15.160 + @SuppressWarnings("unused") 15.161 + private static boolean paramTypeGuard(final Type[] paramTypes, final Object... args) { 15.162 + final int length = args.length; 15.163 + assert args.length >= paramTypes.length; 15.164 + 15.165 + //i==start, skip the this, callee params etc 15.166 + int start = args.length - paramTypes.length; 15.167 + for (int i = start; i < args.length; i++) { 15.168 + final Object arg = args[i]; 15.169 + if (!canCoerce(arg, paramTypes[i - start])) { 15.170 + return false; 15.171 + } 15.172 + } 15.173 + return true; 15.174 + } 15.175 + 15.176 + @SuppressWarnings("unused") 15.177 + private static int ensureInt(final Object arg) { 15.178 + if (arg instanceof Number) { 15.179 + return ((Number)arg).intValue(); 15.180 + } else if (arg instanceof Undefined) { 15.181 + return 0; 15.182 + } 15.183 + throw new AssertionError(arg); 15.184 + } 15.185 + 15.186 + /** 15.187 + * Given the runtime callsite args, compute a method type that is equivalent to what 15.188 + * was passed - this is typically a lot more specific that what the compiler has been 15.189 + * able to deduce 15.190 + * @param callSiteType callsite type for the compiled callsite target 15.191 + * @param args runtime arguments to the compiled callsite target 15.192 + * @return adjusted method type, narrowed as to conform to runtime callsite type instead 15.193 + */ 15.194 + private static MethodType runtimeType(final MethodType callSiteType, final Object[] args) { 15.195 + if (args == null) { 15.196 + //for example bound, or otherwise runtime arguments to callsite unavailable, then 15.197 + //do not change the type 15.198 + return callSiteType; 15.199 + } 15.200 + final Class<?>[] paramTypes = new Class<?>[callSiteType.parameterCount()]; 15.201 + final int start = args.length - callSiteType.parameterCount(); 15.202 + for (int i = start; i < args.length; i++) { 15.203 + paramTypes[i - start] = runtimeType(args[i]).getTypeClass(); 15.204 + } 15.205 + return MH.type(callSiteType.returnType(), paramTypes); 15.206 + } 15.207 + 15.208 + private static ArrayList<Type> runtimeType(final MethodType mt) { 15.209 + final ArrayList<Type> type = new ArrayList<>(); 15.210 + for (int i = 0; i < mt.parameterCount(); i++) { 15.211 + type.add(Type.typeFor(mt.parameterType(i))); 15.212 + } 15.213 + return type; 15.214 + } 15.215 15.216 @Override 15.217 MethodHandle getBestInvoker(final MethodType callSiteType, final Object[] args) { 15.218 - final MethodHandle mh = super.getBestInvoker(callSiteType, args); 15.219 + final MethodType runtimeType = runtimeType(callSiteType, args); 15.220 + assert runtimeType.parameterCount() == callSiteType.parameterCount(); 15.221 15.222 - if (!functionNode.canSpecialize() || !code.isLessSpecificThan(callSiteType)) { 15.223 + final MethodHandle mh = super.getBestInvoker(runtimeType, args); 15.224 + 15.225 + /* 15.226 + * Not all functions can be specialized, for example, if we deemed memory 15.227 + * footprint too large to store a parse snapshot, or if it is meaningless 15.228 + * to do so, such as e.g. for runScript 15.229 + */ 15.230 + if (!functionNode.canSpecialize()) { 15.231 return mh; 15.232 } 15.233 15.234 - final FunctionNode snapshot = functionNode.getSnapshot(); 15.235 - if (snapshot == null) { 15.236 + /* 15.237 + * Check if best invoker is equally specific or more specific than runtime 15.238 + * type. In that case, we don't need further specialization, but can use 15.239 + * whatever we have already. We know that it will match callSiteType, or it 15.240 + * would not have been returned from getBestInvoker 15.241 + */ 15.242 + if (!code.isLessSpecificThan(runtimeType)) { 15.243 return mh; 15.244 } 15.245 15.246 int i; 15.247 + final FunctionNode snapshot = functionNode.getSnapshot(); 15.248 + assert snapshot != null; 15.249 15.250 - //classes known at runtime 15.251 - final LinkedList<Type> runtimeArgs = new LinkedList<>(); 15.252 - for (i = args.length - 1; i >= args.length - snapshot.getParameters().size(); i--) { 15.253 - runtimeArgs.addLast(runtimeType(args[i])); 15.254 + /* 15.255 + * Create a list of the arg types that the compiler knows about 15.256 + * typically, the runtime args are a lot more specific, and we should aggressively 15.257 + * try to use those whenever possible 15.258 + * We WILL try to make an aggressive guess as possible, and add guards if needed. 15.259 + * For example, if the compiler can deduce that we have a number type, but the runtime 15.260 + * passes and int, we might still want to keep it an int, and the gamble to 15.261 + * check that whatever is passed is int representable usually pays off 15.262 + * If the compiler only knows that a parameter is an "Object", it is still worth 15.263 + * it to try to specialize it by looking at the runtime arg. 15.264 + */ 15.265 + final LinkedList<Type> compileTimeArgs = new LinkedList<>(); 15.266 + for (i = callSiteType.parameterCount() - 1; i >= 0 && compileTimeArgs.size() < snapshot.getParameters().size(); i--) { 15.267 + compileTimeArgs.addFirst(Type.typeFor(callSiteType.parameterType(i))); 15.268 } 15.269 15.270 - //classes known at compile time 15.271 - final LinkedList<Type> compileTimeArgs = new LinkedList<>(); 15.272 - for (i = callSiteType.parameterCount() - 1; i >= 0 && compileTimeArgs.size() < snapshot.getParameters().size(); i--) { 15.273 - compileTimeArgs.addLast(Type.typeFor(callSiteType.parameterType(i))); 15.274 + /* 15.275 + * The classes known at compile time are a safe to generate as primitives without parameter guards 15.276 + * But the classes known at runtime (if more specific than compile time types) are safe to generate as primitives 15.277 + * IFF there are parameter guards 15.278 + */ 15.279 + MethodHandle guard = null; 15.280 + final ArrayList<Type> runtimeParamTypes = runtimeType(runtimeType); 15.281 + while (runtimeParamTypes.size() > functionNode.getParameters().size()) { 15.282 + runtimeParamTypes.remove(0); 15.283 } 15.284 + for (i = 0; i < compileTimeArgs.size(); i++) { 15.285 + final Type rparam = Type.typeFor(runtimeType.parameterType(i)); 15.286 + final Type cparam = compileTimeArgs.get(i); 15.287 15.288 - //the classes known at compile time are a safe to generate as primitives without parameter guards 15.289 - //the classes known at runtime are safe to generate as primitives IFF there are parameter guards 15.290 - MethodHandle guard = null; 15.291 - for (i = 0; i < compileTimeArgs.size(); i++) { 15.292 - final Type runtimeType = runtimeArgs.get(i); 15.293 - final Type compileType = compileTimeArgs.get(i); 15.294 - 15.295 - if (compileType.isObject() && !runtimeType.isObject()) { 15.296 + if (cparam.isObject() && !rparam.isObject()) { 15.297 + //check that the runtime object is still coercible to the runtime type, because compiler can't prove it's always primitive 15.298 if (guard == null) { 15.299 - guard = PARAM_TYPE_GUARD; 15.300 - guard = MH.insertArguments(guard, 0, compileTimeArgs.toArray(new Type[compileTimeArgs.size()]), runtimeArgs.toArray(new Type[runtimeArgs.size()])); 15.301 + guard = MH.insertArguments(PARAM_TYPE_GUARD, 0, (Object)runtimeParamTypes.toArray(new Type[runtimeParamTypes.size()])); 15.302 } 15.303 } 15.304 } 15.305 15.306 - //System.err.println("Specialized " + name + " " + runtimeArgs + " known=" + compileTimeArgs); 15.307 + Compiler.LOG.info("Callsite specialized ", name, " runtimeType=", runtimeType, " parameters=", snapshot.getParameters(), " args=", Arrays.asList(args)); 15.308 15.309 assert snapshot != null; 15.310 assert snapshot != functionNode; 15.311 15.312 final Compiler compiler = new Compiler(installer); 15.313 - final FunctionNode compiledSnapshot = compiler.compile(snapshot.setHints(null, new Compiler.Hints(compileTimeArgs.toArray(new Type[compileTimeArgs.size()])))); 15.314 15.315 + final FunctionNode compiledSnapshot = compiler.compile( 15.316 + snapshot.setHints( 15.317 + null, 15.318 + new Compiler.Hints(runtimeParamTypes.toArray(new Type[runtimeParamTypes.size()])))); 15.319 + 15.320 + /* 15.321 + * No matter how narrow your types were, they can never be narrower than Attr during recompile made them. I.e. you 15.322 + * can put an int into the function here, if you see it as a runtime type, but if the function uses a multiplication 15.323 + * on it, it will still need to be a double. At least until we have overflow checks. Similarly, if an int is 15.324 + * passed but it is used as a string, it makes no sense to make the parameter narrower than Object. At least until 15.325 + * the "different types for one symbol in difference places" work is done 15.326 + */ 15.327 compiler.install(compiledSnapshot); 15.328 15.329 - final MethodHandle nmh = addCode(compiledSnapshot, guard, mh); 15.330 - 15.331 - return nmh; 15.332 + return addCode(compiledSnapshot, runtimeType, guard, mh); 15.333 } 15.334 15.335 private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
16.1 --- a/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java Fri May 17 16:44:22 2013 -0300 16.2 +++ b/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java Mon May 20 16:38:38 2013 +0200 16.3 @@ -54,7 +54,7 @@ 16.4 private final Namespace namespace; 16.5 16.6 /** Current Options object. */ 16.7 - private Options options; 16.8 + private final Options options; 16.9 16.10 /** Always allow functions as statements */ 16.11 public final boolean _anon_functions; 16.12 @@ -155,6 +155,9 @@ 16.13 /** print symbols and their contents for the script */ 16.14 public final boolean _print_symbols; 16.15 16.16 + /** range analysis for known types */ 16.17 + public final boolean _range_analysis; 16.18 + 16.19 /** is this environment in scripting mode? */ 16.20 public final boolean _scripting; 16.21 16.22 @@ -219,6 +222,7 @@ 16.23 _print_parse = options.getBoolean("print.parse"); 16.24 _print_lower_parse = options.getBoolean("print.lower.parse"); 16.25 _print_symbols = options.getBoolean("print.symbols"); 16.26 + _range_analysis = options.getBoolean("range.analysis"); 16.27 _scripting = options.getBoolean("scripting"); 16.28 _strict = options.getBoolean("strict"); 16.29 _version = options.getBoolean("version");
17.1 --- a/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java Fri May 17 16:44:22 2013 -0300 17.2 +++ b/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java Mon May 20 16:38:38 2013 +0200 17.3 @@ -91,12 +91,13 @@ 17.4 CompiledFunction bind(final CompiledFunction originalInv, final ScriptFunction fn, final Object self, final Object[] args) { 17.5 final MethodHandle boundInvoker = bindInvokeHandle(originalInv.getInvoker(), fn, self, args); 17.6 17.7 + //TODO the boundinvoker.type() could actually be more specific here 17.8 if (isConstructor()) { 17.9 ensureConstructor(originalInv); 17.10 - return new CompiledFunction(boundInvoker, bindConstructHandle(originalInv.getConstructor(), fn, args)); 17.11 + return new CompiledFunction(boundInvoker.type(), boundInvoker, bindConstructHandle(originalInv.getConstructor(), fn, args)); 17.12 } 17.13 17.14 - return new CompiledFunction(boundInvoker); 17.15 + return new CompiledFunction(boundInvoker.type(), boundInvoker); 17.16 } 17.17 17.18 /**
18.1 --- a/src/jdk/nashorn/internal/runtime/resources/Options.properties Fri May 17 16:44:22 2013 -0300 18.2 +++ b/src/jdk/nashorn/internal/runtime/resources/Options.properties Mon May 20 16:38:38 2013 +0200 18.3 @@ -277,6 +277,12 @@ 18.4 desc="Print the symbol table." \ 18.5 } 18.6 18.7 +nashorn.option.range.analysis = { \ 18.8 + name="--range-analysis", \ 18.9 + is_undocumented=true, \ 18.10 + desc="Do range analysis using known compile time types, and try to narrow number types" \ 18.11 +} 18.12 + 18.13 nashorn.option.D = { \ 18.14 name="-D", \ 18.15 desc="-Dname=value. Set a system property. This option can be repeated.", \
19.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 19.2 +++ b/test/script/basic/ranges_disabled.js Mon May 20 16:38:38 2013 +0200 19.3 @@ -0,0 +1,32 @@ 19.4 +/* 19.5 + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 19.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 19.7 + * 19.8 + * This code is free software; you can redistribute it and/or modify it 19.9 + * under the terms of the GNU General Public License version 2 only, as 19.10 + * published by the Free Software Foundation. 19.11 + * 19.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 19.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 19.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 19.15 + * version 2 for more details (a copy is included in the LICENSE file that 19.16 + * accompanied this code). 19.17 + * 19.18 + * You should have received a copy of the GNU General Public License version 19.19 + * 2 along with this work; if not, write to the Free Software Foundation, 19.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 19.21 + * 19.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 19.23 + * or visit www.oracle.com if you need additional information or have any 19.24 + * questions. 19.25 + */ 19.26 + 19.27 +/** 19.28 + * range analysis test. check that computation return values are correct 19.29 + * both with and without range analysis 19.30 + * 19.31 + * @test 19.32 + * @run 19.33 + */ 19.34 + 19.35 +load(__DIR__ + "ranges_payload.js");
20.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 20.2 +++ b/test/script/basic/ranges_disabled.js.EXPECTED Mon May 20 16:38:38 2013 +0200 20.3 @@ -0,0 +1,4 @@ 20.4 +289 20.5 +11094405 20.6 +4294967293 20.7 +-4722
21.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 21.2 +++ b/test/script/basic/ranges_enabled.js Mon May 20 16:38:38 2013 +0200 21.3 @@ -0,0 +1,33 @@ 21.4 +/* 21.5 + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 21.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 21.7 + * 21.8 + * This code is free software; you can redistribute it and/or modify it 21.9 + * under the terms of the GNU General Public License version 2 only, as 21.10 + * published by the Free Software Foundation. 21.11 + * 21.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 21.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 21.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 21.15 + * version 2 for more details (a copy is included in the LICENSE file that 21.16 + * accompanied this code). 21.17 + * 21.18 + * You should have received a copy of the GNU General Public License version 21.19 + * 2 along with this work; if not, write to the Free Software Foundation, 21.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 21.21 + * 21.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 21.23 + * or visit www.oracle.com if you need additional information or have any 21.24 + * questions. 21.25 + */ 21.26 + 21.27 +/** 21.28 + * range analysis test. check that computation return values are correct 21.29 + * both with and without range analysis 21.30 + * 21.31 + * @test 21.32 + * @option --range-analysis 21.33 + * @run 21.34 + */ 21.35 + 21.36 +load(__DIR__ + "ranges_payload.js");
22.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 22.2 +++ b/test/script/basic/ranges_enabled.js.EXPECTED Mon May 20 16:38:38 2013 +0200 22.3 @@ -0,0 +1,4 @@ 22.4 +289 22.5 +11094405 22.6 +4294967293 22.7 +-4722
23.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 23.2 +++ b/test/script/basic/ranges_payload.js Mon May 20 16:38:38 2013 +0200 23.3 @@ -0,0 +1,74 @@ 23.4 +/* 23.5 + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 23.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 23.7 + * 23.8 + * This code is free software; you can redistribute it and/or modify it 23.9 + * under the terms of the GNU General Public License version 2 only, as 23.10 + * published by the Free Software Foundation. 23.11 + * 23.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 23.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 23.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 23.15 + * version 2 for more details (a copy is included in the LICENSE file that 23.16 + * accompanied this code). 23.17 + * 23.18 + * You should have received a copy of the GNU General Public License version 23.19 + * 2 along with this work; if not, write to the Free Software Foundation, 23.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 23.21 + * 23.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 23.23 + * or visit www.oracle.com if you need additional information or have any 23.24 + * questions. 23.25 + */ 23.26 + 23.27 +/** 23.28 + * range analysis test. check that computation return values are correct 23.29 + * both with and without range analysis 23.30 + * 23.31 + * @subtest 23.32 + */ 23.33 + 23.34 +function f(c) { 23.35 + var v = c & 0xffff; 23.36 + var w = v & 0xfff; 23.37 + var x = v * w; 23.38 + return x; 23.39 +} 23.40 + 23.41 +function g() { 23.42 + var sum = 0; 23.43 + for (var x = 0; x < 4711; x++) { 23.44 + sum += x; 23.45 + } 23.46 + return sum; 23.47 +} 23.48 + 23.49 +function g2() { 23.50 + var sum = 0; 23.51 + //make sure we overflow 23.52 + var displacement = 0x7ffffffe; 23.53 + for (var x = displacement; x < (displacement + 2); x++) { 23.54 + sum += x; 23.55 + } 23.56 + return sum; 23.57 +} 23.58 + 23.59 +//mostly provide code coverage for all the range operations 23.60 +function h() { 23.61 + var sum = 0; 23.62 + sum += 4711; 23.63 + sum &= 0xffff; 23.64 + sum /= 2; 23.65 + sum *= 2; 23.66 + sum -= 4; 23.67 + sum |= 2; 23.68 + sum ^= 17; 23.69 + sum = sum % 10000; 23.70 + sum = -sum; 23.71 + return sum 23.72 +} 23.73 + 23.74 +print(f(17)); 23.75 +print(g()); 23.76 +print(g2()); 23.77 +print(h());