Fri, 03 May 2013 15:33:54 +0200
8013477: Node.setSymbol needs to be copy on write - enable IR snapshots for recompilation based on callsite type specialization. [not enabled by default, hidden by a flag for now]
Reviewed-by: jlaskey, hannesw
jlaskey@3 | 1 | /* |
jlaskey@7 | 2 | * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. |
jlaskey@3 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
jlaskey@3 | 4 | * |
jlaskey@3 | 5 | * This code is free software; you can redistribute it and/or modify it |
jlaskey@3 | 6 | * under the terms of the GNU General Public License version 2 only, as |
jlaskey@3 | 7 | * published by the Free Software Foundation. Oracle designates this |
jlaskey@3 | 8 | * particular file as subject to the "Classpath" exception as provided |
jlaskey@3 | 9 | * by Oracle in the LICENSE file that accompanied this code. |
jlaskey@3 | 10 | * |
jlaskey@3 | 11 | * This code is distributed in the hope that it will be useful, but WITHOUT |
jlaskey@3 | 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
jlaskey@3 | 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
jlaskey@3 | 14 | * version 2 for more details (a copy is included in the LICENSE file that |
jlaskey@3 | 15 | * accompanied this code). |
jlaskey@3 | 16 | * |
jlaskey@3 | 17 | * You should have received a copy of the GNU General Public License version |
jlaskey@3 | 18 | * 2 along with this work; if not, write to the Free Software Foundation, |
jlaskey@3 | 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
jlaskey@3 | 20 | * |
jlaskey@3 | 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
jlaskey@3 | 22 | * or visit www.oracle.com if you need additional information or have any |
jlaskey@3 | 23 | * questions. |
jlaskey@3 | 24 | */ |
jlaskey@3 | 25 | |
jlaskey@3 | 26 | package jdk.nashorn.internal.ir; |
jlaskey@3 | 27 | |
jlaskey@3 | 28 | import java.io.PrintWriter; |
jlaskey@3 | 29 | import java.util.ArrayList; |
lagergren@211 | 30 | import java.util.Arrays; |
jlaskey@3 | 31 | import java.util.Collections; |
lagergren@57 | 32 | import java.util.Comparator; |
lagergren@211 | 33 | import java.util.LinkedHashMap; |
jlaskey@3 | 34 | import java.util.List; |
lagergren@211 | 35 | import java.util.Map; |
lagergren@96 | 36 | import jdk.nashorn.internal.codegen.Label; |
lagergren@211 | 37 | import jdk.nashorn.internal.ir.annotations.Immutable; |
jlaskey@3 | 38 | import jdk.nashorn.internal.ir.visitor.NodeVisitor; |
jlaskey@3 | 39 | import jdk.nashorn.internal.runtime.Source; |
jlaskey@3 | 40 | |
jlaskey@3 | 41 | /** |
jlaskey@3 | 42 | * IR representation for a list of statements and functions. All provides the |
jlaskey@3 | 43 | * basis for script body. |
jlaskey@3 | 44 | */ |
lagergren@211 | 45 | @Immutable |
lagergren@211 | 46 | public class Block extends BreakableNode implements Flags<Block> { |
jlaskey@3 | 47 | /** List of statements */ |
lagergren@211 | 48 | protected final List<Node> statements; |
jlaskey@3 | 49 | |
lagergren@211 | 50 | /** Symbol table - keys must be returned in the order they were put in. */ |
lagergren@211 | 51 | protected final Map<String, Symbol> symbols; |
jlaskey@3 | 52 | |
jlaskey@3 | 53 | /** Entry label. */ |
jlaskey@3 | 54 | protected final Label entryLabel; |
jlaskey@3 | 55 | |
lagergren@211 | 56 | /** Does the block/function need a new scope? */ |
lagergren@211 | 57 | protected final int flags; |
jlaskey@3 | 58 | |
lagergren@211 | 59 | /** Flag indicating that this block needs scope */ |
lagergren@211 | 60 | public static final int NEEDS_SCOPE = 1 << 0; |
lagergren@211 | 61 | |
lagergren@211 | 62 | /** |
lagergren@211 | 63 | * Flag indicating whether this block needs |
lagergren@211 | 64 | * self symbol assignment at the start. This is used only for |
lagergren@211 | 65 | * blocks that are the bodies of function nodes who refer to themselves |
lagergren@211 | 66 | * by name. It causes codegen to insert a var [fn_name] = __callee__ |
lagergren@211 | 67 | * at the start of the body |
lagergren@211 | 68 | */ |
lagergren@211 | 69 | public static final int NEEDS_SELF_SYMBOL = 1 << 1; |
lagergren@211 | 70 | |
lagergren@211 | 71 | /** |
lagergren@211 | 72 | * Is this block tagged as terminal based on its contents |
lagergren@211 | 73 | * (usually the last statement) |
lagergren@211 | 74 | */ |
lagergren@211 | 75 | public static final int IS_TERMINAL = 1 << 2; |
jlaskey@3 | 76 | |
jlaskey@3 | 77 | /** |
jlaskey@3 | 78 | * Constructor |
jlaskey@3 | 79 | * |
lagergren@211 | 80 | * @param source source code |
lagergren@211 | 81 | * @param token token |
lagergren@211 | 82 | * @param finish finish |
lagergren@211 | 83 | * @param statements statements |
jlaskey@3 | 84 | */ |
lagergren@211 | 85 | public Block(final Source source, final long token, final int finish, final Node... statements) { |
lagergren@211 | 86 | super(source, token, finish, new Label("block_break")); |
jlaskey@3 | 87 | |
lagergren@211 | 88 | this.statements = Arrays.asList(statements); |
lagergren@211 | 89 | this.symbols = new LinkedHashMap<>(); |
jlaskey@3 | 90 | this.entryLabel = new Label("block_entry"); |
lagergren@211 | 91 | this.flags = 0; |
jlaskey@3 | 92 | } |
jlaskey@3 | 93 | |
jlaskey@3 | 94 | /** |
lagergren@211 | 95 | * Constructor |
jlaskey@3 | 96 | * |
lagergren@211 | 97 | * @param source source code |
lagergren@211 | 98 | * @param token token |
lagergren@211 | 99 | * @param finish finish |
lagergren@211 | 100 | * @param statements statements |
jlaskey@3 | 101 | */ |
lagergren@211 | 102 | public Block(final Source source, final long token, final int finish, final List<Node> statements) { |
lagergren@211 | 103 | this(source, token, finish, statements.toArray(new Node[statements.size()])); |
lagergren@211 | 104 | } |
lagergren@211 | 105 | |
lagergren@247 | 106 | private Block(final Block block, final int finish, final List<Node> statements, final int flags, final Map<String, Symbol> symbols) { |
jlaskey@3 | 107 | super(block); |
lagergren@211 | 108 | this.statements = statements; |
lagergren@211 | 109 | this.flags = flags; |
lagergren@247 | 110 | this.symbols = new LinkedHashMap<>(symbols); //todo - symbols have no dependencies on any IR node and can as far as we understand it be shallow copied now |
lagergren@96 | 111 | this.entryLabel = new Label(block.entryLabel); |
lagergren@247 | 112 | this.finish = finish; |
lagergren@247 | 113 | } |
lagergren@247 | 114 | |
lagergren@247 | 115 | /** |
lagergren@247 | 116 | * Clear the symbols in a block |
lagergren@247 | 117 | * TODO: make this immutable |
lagergren@247 | 118 | */ |
lagergren@247 | 119 | public void clearSymbols() { |
lagergren@247 | 120 | symbols.clear(); |
jlaskey@3 | 121 | } |
jlaskey@3 | 122 | |
jlaskey@3 | 123 | @Override |
lagergren@211 | 124 | public Node ensureUniqueLabels(final LexicalContext lc) { |
lagergren@247 | 125 | return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags, symbols)); |
jlaskey@3 | 126 | } |
jlaskey@3 | 127 | |
jlaskey@3 | 128 | /** |
jlaskey@3 | 129 | * Assist in IR navigation. |
jlaskey@3 | 130 | * |
jlaskey@3 | 131 | * @param visitor IR navigating visitor. |
jlaskey@3 | 132 | * @return new or same node |
jlaskey@3 | 133 | */ |
jlaskey@3 | 134 | @Override |
lagergren@211 | 135 | public Node accept(final LexicalContext lc, final NodeVisitor visitor) { |
lagergren@211 | 136 | if (visitor.enterBlock(this)) { |
lagergren@211 | 137 | return visitor.leaveBlock(setStatements(lc, Node.accept(visitor, Node.class, statements))); |
jlaskey@3 | 138 | } |
jlaskey@3 | 139 | |
jlaskey@3 | 140 | return this; |
jlaskey@3 | 141 | } |
jlaskey@3 | 142 | |
jlaskey@3 | 143 | /** |
lagergren@137 | 144 | * Get an iterator for all the symbols defined in this block |
lagergren@137 | 145 | * @return symbol iterator |
lagergren@137 | 146 | */ |
lagergren@247 | 147 | public List<Symbol> getSymbols() { |
lagergren@247 | 148 | return Collections.unmodifiableList(new ArrayList<>(symbols.values())); |
lagergren@137 | 149 | } |
lagergren@137 | 150 | |
lagergren@137 | 151 | /** |
attila@144 | 152 | * Retrieves an existing symbol defined in the current block. |
attila@144 | 153 | * @param name the name of the symbol |
attila@144 | 154 | * @return an existing symbol with the specified name defined in the current block, or null if this block doesn't |
lagergren@247 | 155 | * define a symbol with this name.T |
jlaskey@3 | 156 | */ |
attila@144 | 157 | public Symbol getExistingSymbol(final String name) { |
attila@144 | 158 | return symbols.get(name); |
jlaskey@3 | 159 | } |
jlaskey@3 | 160 | |
jlaskey@3 | 161 | /** |
jlaskey@3 | 162 | * Test if this block represents a <tt>catch</tt> block in a <tt>try</tt> statement. |
jlaskey@3 | 163 | * This is used by the Splitter as catch blocks are not be subject to splitting. |
jlaskey@3 | 164 | * |
jlaskey@3 | 165 | * @return true if this block represents a catch block in a try statement. |
jlaskey@3 | 166 | */ |
jlaskey@3 | 167 | public boolean isCatchBlock() { |
jlaskey@3 | 168 | return statements.size() == 1 && statements.get(0) instanceof CatchNode; |
jlaskey@3 | 169 | } |
jlaskey@3 | 170 | |
jlaskey@3 | 171 | @Override |
jlaskey@3 | 172 | public void toString(final StringBuilder sb) { |
jlaskey@3 | 173 | for (final Node statement : statements) { |
jlaskey@3 | 174 | statement.toString(sb); |
jlaskey@3 | 175 | sb.append(';'); |
jlaskey@3 | 176 | } |
jlaskey@3 | 177 | } |
jlaskey@3 | 178 | |
jlaskey@3 | 179 | /** |
lagergren@57 | 180 | * Print symbols in block in alphabetical order, sorted on name |
lagergren@57 | 181 | * Used for debugging, see the --print-symbols flag |
jlaskey@3 | 182 | * |
jlaskey@3 | 183 | * @param stream print writer to output symbols to |
jlaskey@3 | 184 | * |
jlaskey@3 | 185 | * @return true if symbols were found |
jlaskey@3 | 186 | */ |
jlaskey@3 | 187 | public boolean printSymbols(final PrintWriter stream) { |
lagergren@57 | 188 | final List<Symbol> values = new ArrayList<>(symbols.values()); |
lagergren@57 | 189 | |
lagergren@57 | 190 | Collections.sort(values, new Comparator<Symbol>() { |
lagergren@57 | 191 | @Override |
lagergren@57 | 192 | public int compare(final Symbol s0, final Symbol s1) { |
lagergren@57 | 193 | return s0.getName().compareTo(s1.getName()); |
lagergren@57 | 194 | } |
lagergren@57 | 195 | }); |
jlaskey@3 | 196 | |
jlaskey@3 | 197 | for (final Symbol symbol : values) { |
jlaskey@3 | 198 | symbol.print(stream); |
jlaskey@3 | 199 | } |
jlaskey@3 | 200 | |
jlaskey@3 | 201 | return !values.isEmpty(); |
jlaskey@3 | 202 | } |
jlaskey@3 | 203 | |
jlaskey@3 | 204 | /** |
lagergren@211 | 205 | * Tag block as terminal or non terminal |
lagergren@211 | 206 | * @param lc lexical context |
lagergren@211 | 207 | * @param isTerminal is block terminal |
lagergren@211 | 208 | * @return same block, or new if flag changed |
jlaskey@3 | 209 | */ |
lagergren@211 | 210 | public Block setIsTerminal(final LexicalContext lc, final boolean isTerminal) { |
lagergren@211 | 211 | return isTerminal ? setFlag(lc, IS_TERMINAL) : clearFlag(lc, IS_TERMINAL); |
lagergren@211 | 212 | } |
lagergren@211 | 213 | |
lagergren@211 | 214 | @Override |
lagergren@211 | 215 | public boolean isTerminal() { |
lagergren@211 | 216 | return getFlag(IS_TERMINAL); |
jlaskey@3 | 217 | } |
jlaskey@3 | 218 | |
jlaskey@3 | 219 | /** |
jlaskey@3 | 220 | * Get the entry label for this block |
jlaskey@3 | 221 | * @return the entry label |
jlaskey@3 | 222 | */ |
jlaskey@3 | 223 | public Label getEntryLabel() { |
jlaskey@3 | 224 | return entryLabel; |
jlaskey@3 | 225 | } |
jlaskey@3 | 226 | |
jlaskey@3 | 227 | /** |
jlaskey@3 | 228 | * Get the list of statements in this block |
jlaskey@3 | 229 | * |
jlaskey@3 | 230 | * @return a list of statements |
jlaskey@3 | 231 | */ |
jlaskey@3 | 232 | public List<Node> getStatements() { |
jlaskey@3 | 233 | return Collections.unmodifiableList(statements); |
jlaskey@3 | 234 | } |
jlaskey@3 | 235 | |
jlaskey@3 | 236 | /** |
jlaskey@3 | 237 | * Reset the statement list for this block |
jlaskey@3 | 238 | * |
lagergren@211 | 239 | * @param lc lexical context |
lagergren@211 | 240 | * @param statements new statement list |
lagergren@211 | 241 | * @return new block if statements changed, identity of statements == block.statements |
jlaskey@3 | 242 | */ |
lagergren@211 | 243 | public Block setStatements(final LexicalContext lc, final List<Node> statements) { |
lagergren@211 | 244 | if (this.statements == statements) { |
lagergren@211 | 245 | return this; |
lagergren@211 | 246 | } |
lagergren@211 | 247 | int lastFinish = 0; |
lagergren@211 | 248 | if (!statements.isEmpty()) { |
lagergren@211 | 249 | lastFinish = statements.get(statements.size() - 1).getFinish(); |
lagergren@211 | 250 | } |
lagergren@247 | 251 | return Node.replaceInLexicalContext(lc, this, new Block(this, Math.max(finish, lastFinish), statements, flags, symbols)); |
jlaskey@3 | 252 | } |
jlaskey@3 | 253 | |
jlaskey@3 | 254 | /** |
jlaskey@3 | 255 | * Add or overwrite an existing symbol in the block |
jlaskey@3 | 256 | * |
lagergren@247 | 257 | * @param lc get lexical context |
jlaskey@3 | 258 | * @param symbol symbol |
jlaskey@3 | 259 | */ |
lagergren@247 | 260 | public void putSymbol(final LexicalContext lc, final Symbol symbol) { |
lagergren@247 | 261 | symbols.put(symbol.getName(), symbol); |
jlaskey@3 | 262 | } |
jlaskey@3 | 263 | |
jlaskey@3 | 264 | /** |
jlaskey@3 | 265 | * Check whether scope is necessary for this Block |
jlaskey@3 | 266 | * |
jlaskey@3 | 267 | * @return true if this function needs a scope |
jlaskey@3 | 268 | */ |
jlaskey@3 | 269 | public boolean needsScope() { |
lagergren@211 | 270 | return (flags & NEEDS_SCOPE) == NEEDS_SCOPE; |
lagergren@211 | 271 | } |
lagergren@211 | 272 | |
lagergren@211 | 273 | @Override |
lagergren@211 | 274 | public Block setFlags(final LexicalContext lc, int flags) { |
lagergren@211 | 275 | if (this.flags == flags) { |
lagergren@211 | 276 | return this; |
lagergren@211 | 277 | } |
lagergren@247 | 278 | return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags, symbols)); |
lagergren@211 | 279 | } |
lagergren@211 | 280 | |
lagergren@211 | 281 | @Override |
lagergren@211 | 282 | public Block clearFlag(final LexicalContext lc, int flag) { |
lagergren@211 | 283 | return setFlags(lc, flags & ~flag); |
lagergren@211 | 284 | } |
lagergren@211 | 285 | |
lagergren@211 | 286 | @Override |
lagergren@211 | 287 | public Block setFlag(final LexicalContext lc, int flag) { |
lagergren@211 | 288 | return setFlags(lc, flags | flag); |
lagergren@211 | 289 | } |
lagergren@211 | 290 | |
lagergren@211 | 291 | @Override |
lagergren@211 | 292 | public boolean getFlag(final int flag) { |
lagergren@211 | 293 | return (flags & flag) == flag; |
jlaskey@3 | 294 | } |
jlaskey@3 | 295 | |
jlaskey@3 | 296 | /** |
lagergren@24 | 297 | * Set the needs scope flag. |
lagergren@211 | 298 | * @param lc lexicalContext |
lagergren@211 | 299 | * @return new block if state changed, otherwise this |
jlaskey@3 | 300 | */ |
lagergren@211 | 301 | public Block setNeedsScope(final LexicalContext lc) { |
lagergren@211 | 302 | if (needsScope()) { |
lagergren@211 | 303 | return this; |
lagergren@211 | 304 | } |
lagergren@211 | 305 | |
lagergren@247 | 306 | return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags | NEEDS_SCOPE, symbols)); |
jlaskey@3 | 307 | } |
jlaskey@3 | 308 | |
attila@144 | 309 | /** |
lagergren@211 | 310 | * Computationally determine the next slot for this block, |
lagergren@211 | 311 | * indexed from 0. Use this as a relative base when computing |
lagergren@211 | 312 | * frames |
lagergren@211 | 313 | * @return next slot |
attila@144 | 314 | */ |
lagergren@211 | 315 | public int nextSlot() { |
lagergren@211 | 316 | int next = 0; |
lagergren@247 | 317 | for (final Symbol symbol : getSymbols()) { |
lagergren@247 | 318 | if (symbol.hasSlot()) { |
lagergren@247 | 319 | next += symbol.slotCount(); |
lagergren@247 | 320 | } |
lagergren@211 | 321 | } |
lagergren@211 | 322 | return next; |
attila@144 | 323 | } |
attila@144 | 324 | |
lagergren@211 | 325 | @Override |
lagergren@211 | 326 | protected boolean isBreakableWithoutLabel() { |
lagergren@211 | 327 | return false; |
attila@144 | 328 | } |
jlaskey@3 | 329 | } |