src/jdk/nashorn/internal/codegen/FoldConstants.java

Tue, 08 Oct 2013 15:53:22 +0200

author
hannesw
date
Tue, 08 Oct 2013 15:53:22 +0200
changeset 595
c9921761903b
parent 430
2c007a8bb0e7
child 604
ec3094d9d5d5
permissions
-rw-r--r--

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 }

mercurial