Tue, 08 Oct 2013 15:53:22 +0200
8026042: FoldConstants need to guard against ArrayLiteralNode
Reviewed-by: jlaskey, sundar
lagergren@57 | 1 | /* |
lagergren@57 | 2 | * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. |
lagergren@57 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
lagergren@57 | 4 | * |
lagergren@57 | 5 | * This code is free software; you can redistribute it and/or modify it |
lagergren@57 | 6 | * under the terms of the GNU General Public License version 2 only, as |
lagergren@57 | 7 | * published by the Free Software Foundation. Oracle designates this |
lagergren@57 | 8 | * particular file as subject to the "Classpath" exception as provided |
lagergren@57 | 9 | * by Oracle in the LICENSE file that accompanied this code. |
lagergren@57 | 10 | * |
lagergren@57 | 11 | * This code is distributed in the hope that it will be useful, but WITHOUT |
lagergren@57 | 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
lagergren@57 | 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
lagergren@57 | 14 | * version 2 for more details (a copy is included in the LICENSE file that |
lagergren@57 | 15 | * accompanied this code). |
lagergren@57 | 16 | * |
lagergren@57 | 17 | * You should have received a copy of the GNU General Public License version |
lagergren@57 | 18 | * 2 along with this work; if not, write to the Free Software Foundation, |
lagergren@57 | 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
lagergren@57 | 20 | * |
lagergren@57 | 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
lagergren@57 | 22 | * or visit www.oracle.com if you need additional information or have any |
lagergren@57 | 23 | * questions. |
lagergren@57 | 24 | */ |
lagergren@57 | 25 | |
lagergren@57 | 26 | package jdk.nashorn.internal.codegen; |
lagergren@57 | 27 | |
lagergren@57 | 28 | import jdk.nashorn.internal.codegen.types.Type; |
lagergren@57 | 29 | import jdk.nashorn.internal.ir.BinaryNode; |
lagergren@57 | 30 | import jdk.nashorn.internal.ir.Block; |
attila@430 | 31 | import jdk.nashorn.internal.ir.BlockStatement; |
lagergren@57 | 32 | import jdk.nashorn.internal.ir.EmptyNode; |
lagergren@137 | 33 | import jdk.nashorn.internal.ir.FunctionNode; |
attila@144 | 34 | import jdk.nashorn.internal.ir.FunctionNode.CompilationState; |
lagergren@57 | 35 | import jdk.nashorn.internal.ir.IfNode; |
lagergren@290 | 36 | import jdk.nashorn.internal.ir.LexicalContext; |
lagergren@57 | 37 | import jdk.nashorn.internal.ir.LiteralNode; |
lagergren@287 | 38 | import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; |
lagergren@57 | 39 | import jdk.nashorn.internal.ir.Node; |
lagergren@57 | 40 | import jdk.nashorn.internal.ir.TernaryNode; |
lagergren@57 | 41 | import jdk.nashorn.internal.ir.UnaryNode; |
lagergren@57 | 42 | import jdk.nashorn.internal.ir.visitor.NodeVisitor; |
lagergren@57 | 43 | import jdk.nashorn.internal.runtime.DebugLogger; |
lagergren@57 | 44 | import jdk.nashorn.internal.runtime.JSType; |
lagergren@57 | 45 | import jdk.nashorn.internal.runtime.ScriptRuntime; |
lagergren@57 | 46 | |
lagergren@57 | 47 | /** |
lagergren@57 | 48 | * Simple constant folding pass, executed before IR is starting to be lowered. |
lagergren@57 | 49 | */ |
lagergren@290 | 50 | final class FoldConstants extends NodeVisitor<LexicalContext> { |
lagergren@57 | 51 | |
lagergren@57 | 52 | private static final DebugLogger LOG = new DebugLogger("fold"); |
lagergren@57 | 53 | |
lagergren@89 | 54 | FoldConstants() { |
lagergren@290 | 55 | super(new LexicalContext()); |
lagergren@89 | 56 | } |
lagergren@89 | 57 | |
lagergren@57 | 58 | @Override |
attila@144 | 59 | public Node leaveUnaryNode(final UnaryNode unaryNode) { |
lagergren@57 | 60 | final LiteralNode<?> literalNode = new UnaryNodeConstantEvaluator(unaryNode).eval(); |
lagergren@57 | 61 | if (literalNode != null) { |
lagergren@211 | 62 | LOG.info("Unary constant folded ", unaryNode, " to ", literalNode); |
lagergren@57 | 63 | return literalNode; |
lagergren@57 | 64 | } |
lagergren@57 | 65 | return unaryNode; |
lagergren@57 | 66 | } |
lagergren@57 | 67 | |
lagergren@57 | 68 | @Override |
attila@144 | 69 | public Node leaveBinaryNode(final BinaryNode binaryNode) { |
lagergren@57 | 70 | final LiteralNode<?> literalNode = new BinaryNodeConstantEvaluator(binaryNode).eval(); |
lagergren@57 | 71 | if (literalNode != null) { |
lagergren@211 | 72 | LOG.info("Binary constant folded ", binaryNode, " to ", literalNode); |
lagergren@57 | 73 | return literalNode; |
lagergren@57 | 74 | } |
lagergren@57 | 75 | return binaryNode; |
lagergren@57 | 76 | } |
lagergren@57 | 77 | |
lagergren@57 | 78 | @Override |
lagergren@211 | 79 | public boolean enterFunctionNode(final FunctionNode functionNode) { |
lagergren@211 | 80 | return !functionNode.isLazy(); |
lagergren@137 | 81 | } |
lagergren@137 | 82 | |
lagergren@137 | 83 | @Override |
attila@144 | 84 | public Node leaveFunctionNode(final FunctionNode functionNode) { |
lagergren@290 | 85 | return functionNode.setState(lc, CompilationState.CONSTANT_FOLDED); |
lagergren@137 | 86 | } |
lagergren@137 | 87 | |
lagergren@137 | 88 | @Override |
attila@144 | 89 | public Node leaveIfNode(final IfNode ifNode) { |
lagergren@57 | 90 | final Node test = ifNode.getTest(); |
hannesw@595 | 91 | if (test instanceof LiteralNode.PrimitiveLiteralNode) { |
hannesw@595 | 92 | final Block shortCut = ((LiteralNode.PrimitiveLiteralNode<?>)test).isTrue() ? ifNode.getPass() : ifNode.getFail(); |
lagergren@57 | 93 | if (shortCut != null) { |
attila@430 | 94 | return new BlockStatement(ifNode.getLineNumber(), shortCut); |
lagergren@57 | 95 | } |
lagergren@57 | 96 | return new EmptyNode(ifNode); |
lagergren@57 | 97 | } |
lagergren@57 | 98 | return ifNode; |
lagergren@57 | 99 | } |
lagergren@57 | 100 | |
lagergren@57 | 101 | @Override |
attila@144 | 102 | public Node leaveTernaryNode(final TernaryNode ternaryNode) { |
attila@430 | 103 | final Node test = ternaryNode.getTest(); |
hannesw@595 | 104 | if (test instanceof LiteralNode.PrimitiveLiteralNode) { |
hannesw@595 | 105 | return ((LiteralNode.PrimitiveLiteralNode<?>)test).isTrue() ? ternaryNode.getTrueExpression() : ternaryNode.getFalseExpression(); |
lagergren@57 | 106 | } |
lagergren@57 | 107 | return ternaryNode; |
lagergren@57 | 108 | } |
lagergren@57 | 109 | |
lagergren@57 | 110 | /** |
lagergren@57 | 111 | * Helper class to evaluate constant expressions at compile time This is |
lagergren@57 | 112 | * also a simplifier used by BinaryNode visits, UnaryNode visits and |
lagergren@57 | 113 | * conversions. |
lagergren@57 | 114 | */ |
lagergren@57 | 115 | abstract static class ConstantEvaluator<T extends Node> { |
lagergren@57 | 116 | protected T parent; |
lagergren@57 | 117 | protected final long token; |
lagergren@57 | 118 | protected final int finish; |
lagergren@57 | 119 | |
lagergren@57 | 120 | protected ConstantEvaluator(final T parent) { |
lagergren@57 | 121 | this.parent = parent; |
lagergren@57 | 122 | this.token = parent.getToken(); |
lagergren@57 | 123 | this.finish = parent.getFinish(); |
lagergren@57 | 124 | } |
lagergren@57 | 125 | |
lagergren@57 | 126 | /** |
lagergren@57 | 127 | * Returns a literal node that replaces the given parent node, or null if replacement |
lagergren@57 | 128 | * is impossible |
lagergren@57 | 129 | * @return the literal node |
lagergren@57 | 130 | */ |
lagergren@57 | 131 | protected abstract LiteralNode<?> eval(); |
lagergren@57 | 132 | } |
lagergren@57 | 133 | |
lagergren@57 | 134 | private static class UnaryNodeConstantEvaluator extends ConstantEvaluator<UnaryNode> { |
lagergren@57 | 135 | UnaryNodeConstantEvaluator(final UnaryNode parent) { |
lagergren@57 | 136 | super(parent); |
lagergren@57 | 137 | } |
lagergren@57 | 138 | |
lagergren@57 | 139 | @Override |
lagergren@57 | 140 | protected LiteralNode<?> eval() { |
lagergren@57 | 141 | final Node rhsNode = parent.rhs(); |
lagergren@57 | 142 | |
lagergren@57 | 143 | if (!(rhsNode instanceof LiteralNode)) { |
lagergren@57 | 144 | return null; |
lagergren@57 | 145 | } |
lagergren@57 | 146 | |
lagergren@287 | 147 | if (rhsNode instanceof ArrayLiteralNode) { |
lagergren@287 | 148 | return null; |
lagergren@287 | 149 | } |
lagergren@287 | 150 | |
lagergren@57 | 151 | final LiteralNode<?> rhs = (LiteralNode<?>)rhsNode; |
lagergren@57 | 152 | final boolean rhsInteger = rhs.getType().isInteger(); |
lagergren@57 | 153 | |
lagergren@57 | 154 | LiteralNode<?> literalNode; |
lagergren@57 | 155 | |
lagergren@57 | 156 | switch (parent.tokenType()) { |
lagergren@57 | 157 | case ADD: |
lagergren@57 | 158 | if (rhsInteger) { |
lagergren@252 | 159 | literalNode = LiteralNode.newInstance(token, finish, rhs.getInt32()); |
lagergren@57 | 160 | } else { |
lagergren@252 | 161 | literalNode = LiteralNode.newInstance(token, finish, rhs.getNumber()); |
lagergren@57 | 162 | } |
lagergren@57 | 163 | break; |
lagergren@57 | 164 | case SUB: |
lagergren@57 | 165 | if (rhsInteger && rhs.getInt32() != 0) { // @see test/script/basic/minuszero.js |
lagergren@252 | 166 | literalNode = LiteralNode.newInstance(token, finish, -rhs.getInt32()); |
lagergren@57 | 167 | } else { |
lagergren@252 | 168 | literalNode = LiteralNode.newInstance(token, finish, -rhs.getNumber()); |
lagergren@57 | 169 | } |
lagergren@57 | 170 | break; |
lagergren@57 | 171 | case NOT: |
lagergren@252 | 172 | literalNode = LiteralNode.newInstance(token, finish, !rhs.getBoolean()); |
lagergren@57 | 173 | break; |
lagergren@57 | 174 | case BIT_NOT: |
lagergren@252 | 175 | literalNode = LiteralNode.newInstance(token, finish, ~rhs.getInt32()); |
lagergren@57 | 176 | break; |
lagergren@57 | 177 | default: |
lagergren@57 | 178 | return null; |
lagergren@57 | 179 | } |
lagergren@57 | 180 | |
lagergren@57 | 181 | return literalNode; |
lagergren@57 | 182 | } |
lagergren@57 | 183 | } |
lagergren@57 | 184 | |
lagergren@57 | 185 | //TODO add AND and OR with one constant parameter (bitwise) |
lagergren@57 | 186 | private static class BinaryNodeConstantEvaluator extends ConstantEvaluator<BinaryNode> { |
lagergren@57 | 187 | BinaryNodeConstantEvaluator(final BinaryNode parent) { |
lagergren@57 | 188 | super(parent); |
lagergren@57 | 189 | } |
lagergren@57 | 190 | |
lagergren@57 | 191 | @Override |
lagergren@57 | 192 | protected LiteralNode<?> eval() { |
lagergren@57 | 193 | LiteralNode<?> result; |
lagergren@57 | 194 | |
lagergren@57 | 195 | result = reduceTwoLiterals(); |
lagergren@57 | 196 | if (result != null) { |
lagergren@57 | 197 | return result; |
lagergren@57 | 198 | } |
lagergren@57 | 199 | |
lagergren@57 | 200 | result = reduceOneLiteral(); |
lagergren@57 | 201 | if (result != null) { |
lagergren@57 | 202 | return result; |
lagergren@57 | 203 | } |
lagergren@57 | 204 | |
lagergren@57 | 205 | return null; |
lagergren@57 | 206 | } |
lagergren@57 | 207 | |
lagergren@57 | 208 | @SuppressWarnings("static-method") |
lagergren@57 | 209 | private LiteralNode<?> reduceOneLiteral() { |
lagergren@57 | 210 | //TODO handle patterns like AND, OR, numeric ops that can be strength reduced but not replaced by a single literal node etc |
lagergren@57 | 211 | return null; |
lagergren@57 | 212 | } |
lagergren@57 | 213 | |
lagergren@57 | 214 | private LiteralNode<?> reduceTwoLiterals() { |
lagergren@57 | 215 | if (!(parent.lhs() instanceof LiteralNode && parent.rhs() instanceof LiteralNode)) { |
lagergren@57 | 216 | return null; |
lagergren@57 | 217 | } |
lagergren@57 | 218 | |
lagergren@57 | 219 | final LiteralNode<?> lhs = (LiteralNode<?>)parent.lhs(); |
lagergren@57 | 220 | final LiteralNode<?> rhs = (LiteralNode<?>)parent.rhs(); |
lagergren@57 | 221 | |
lagergren@287 | 222 | if (lhs instanceof ArrayLiteralNode || rhs instanceof ArrayLiteralNode) { |
lagergren@287 | 223 | return null; |
lagergren@287 | 224 | } |
lagergren@287 | 225 | |
lagergren@57 | 226 | final Type widest = Type.widest(lhs.getType(), rhs.getType()); |
lagergren@57 | 227 | |
lagergren@57 | 228 | boolean isInteger = widest.isInteger(); |
lagergren@57 | 229 | boolean isLong = widest.isLong(); |
lagergren@57 | 230 | |
lagergren@57 | 231 | double value; |
lagergren@57 | 232 | |
lagergren@57 | 233 | switch (parent.tokenType()) { |
lagergren@57 | 234 | case DIV: |
lagergren@57 | 235 | value = lhs.getNumber() / rhs.getNumber(); |
lagergren@57 | 236 | break; |
lagergren@57 | 237 | case ADD: |
lagergren@57 | 238 | if ((lhs.isString() || rhs.isNumeric()) && (rhs.isString() || rhs.isNumeric())) { |
lagergren@57 | 239 | Object res = ScriptRuntime.ADD(lhs.getObject(), rhs.getObject()); |
lagergren@57 | 240 | if (res instanceof Number) { |
lagergren@57 | 241 | value = ((Number)res).doubleValue(); |
lagergren@57 | 242 | break; |
lagergren@57 | 243 | } |
lagergren@57 | 244 | assert res instanceof CharSequence : res + " was not a CharSequence, it was a " + res.getClass(); |
lagergren@252 | 245 | return LiteralNode.newInstance(token, finish, res.toString()); |
lagergren@57 | 246 | } |
lagergren@57 | 247 | return null; |
lagergren@57 | 248 | case MUL: |
lagergren@57 | 249 | value = lhs.getNumber() * rhs.getNumber(); |
lagergren@57 | 250 | break; |
lagergren@57 | 251 | case MOD: |
lagergren@57 | 252 | value = lhs.getNumber() % rhs.getNumber(); |
lagergren@57 | 253 | break; |
lagergren@57 | 254 | case SUB: |
lagergren@57 | 255 | value = lhs.getNumber() - rhs.getNumber(); |
lagergren@57 | 256 | break; |
lagergren@57 | 257 | case SHR: |
lagergren@252 | 258 | return LiteralNode.newInstance(token, finish, (lhs.getInt32() >>> rhs.getInt32()) & JSType.MAX_UINT); |
lagergren@57 | 259 | case SAR: |
lagergren@252 | 260 | return LiteralNode.newInstance(token, finish, lhs.getInt32() >> rhs.getInt32()); |
lagergren@57 | 261 | case SHL: |
lagergren@252 | 262 | return LiteralNode.newInstance(token, finish, lhs.getInt32() << rhs.getInt32()); |
lagergren@57 | 263 | case BIT_XOR: |
lagergren@252 | 264 | return LiteralNode.newInstance(token, finish, lhs.getInt32() ^ rhs.getInt32()); |
lagergren@57 | 265 | case BIT_AND: |
lagergren@252 | 266 | return LiteralNode.newInstance(token, finish, lhs.getInt32() & rhs.getInt32()); |
lagergren@57 | 267 | case BIT_OR: |
lagergren@252 | 268 | return LiteralNode.newInstance(token, finish, lhs.getInt32() | rhs.getInt32()); |
lagergren@57 | 269 | case GE: |
lagergren@252 | 270 | return LiteralNode.newInstance(token, finish, ScriptRuntime.GE(lhs.getObject(), rhs.getObject())); |
lagergren@57 | 271 | case LE: |
lagergren@252 | 272 | return LiteralNode.newInstance(token, finish, ScriptRuntime.LE(lhs.getObject(), rhs.getObject())); |
lagergren@57 | 273 | case GT: |
lagergren@252 | 274 | return LiteralNode.newInstance(token, finish, ScriptRuntime.GT(lhs.getObject(), rhs.getObject())); |
lagergren@57 | 275 | case LT: |
lagergren@252 | 276 | return LiteralNode.newInstance(token, finish, ScriptRuntime.LT(lhs.getObject(), rhs.getObject())); |
lagergren@57 | 277 | case NE: |
lagergren@252 | 278 | return LiteralNode.newInstance(token, finish, ScriptRuntime.NE(lhs.getObject(), rhs.getObject())); |
lagergren@57 | 279 | case NE_STRICT: |
lagergren@252 | 280 | return LiteralNode.newInstance(token, finish, ScriptRuntime.NE_STRICT(lhs.getObject(), rhs.getObject())); |
lagergren@57 | 281 | case EQ: |
lagergren@252 | 282 | return LiteralNode.newInstance(token, finish, ScriptRuntime.EQ(lhs.getObject(), rhs.getObject())); |
lagergren@57 | 283 | case EQ_STRICT: |
lagergren@252 | 284 | return LiteralNode.newInstance(token, finish, ScriptRuntime.EQ_STRICT(lhs.getObject(), rhs.getObject())); |
lagergren@57 | 285 | default: |
lagergren@57 | 286 | return null; |
lagergren@57 | 287 | } |
lagergren@57 | 288 | |
lagergren@57 | 289 | isInteger &= value != 0.0 && JSType.isRepresentableAsInt(value); |
lagergren@57 | 290 | isLong &= value != 0.0 && JSType.isRepresentableAsLong(value); |
lagergren@57 | 291 | |
lagergren@57 | 292 | if (isInteger) { |
hannesw@285 | 293 | return LiteralNode.newInstance(token, finish, (int)value); |
lagergren@57 | 294 | } else if (isLong) { |
hannesw@285 | 295 | return LiteralNode.newInstance(token, finish, (long)value); |
lagergren@57 | 296 | } |
lagergren@57 | 297 | |
lagergren@252 | 298 | return LiteralNode.newInstance(token, finish, value); |
lagergren@57 | 299 | } |
lagergren@57 | 300 | } |
lagergren@57 | 301 | } |