Thu, 25 Sep 2014 15:53:47 +0200
8025435: Optimistic builtins support, implemented initial optimistic versions of push, pop, and charCodeAt
Reviewed-by: hannesw, attila, sundar
attila@963 | 1 | /* |
attila@963 | 2 | * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. |
attila@963 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
attila@963 | 4 | * |
attila@963 | 5 | * This code is free software; you can redistribute it and/or modify it |
attila@963 | 6 | * under the terms of the GNU General Public License version 2 only, as |
attila@963 | 7 | * published by the Free Software Foundation. Oracle designates this |
attila@963 | 8 | * particular file as subject to the "Classpath" exception as provided |
attila@963 | 9 | * by Oracle in the LICENSE file that accompanied this code. |
attila@963 | 10 | * |
attila@963 | 11 | * This code is distributed in the hope that it will be useful, but WITHOUT |
attila@963 | 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
attila@963 | 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
attila@963 | 14 | * version 2 for more details (a copy is included in the LICENSE file that |
attila@963 | 15 | * accompanied this code). |
attila@963 | 16 | * |
attila@963 | 17 | * You should have received a copy of the GNU General Public License version |
attila@963 | 18 | * 2 along with this work; if not, write to the Free Software Foundation, |
attila@963 | 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
attila@963 | 20 | * |
attila@963 | 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
attila@963 | 22 | * or visit www.oracle.com if you need additional information or have any |
attila@963 | 23 | * questions. |
attila@963 | 24 | */ |
attila@963 | 25 | |
lagergren@89 | 26 | package jdk.nashorn.internal.codegen; |
lagergren@89 | 27 | |
attila@963 | 28 | import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.BUILTINS_TRANSFORMED; |
attila@963 | 29 | import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.BYTECODE_GENERATED; |
attila@963 | 30 | import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.BYTECODE_INSTALLED; |
lagergren@89 | 31 | import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.CONSTANT_FOLDED; |
lagergren@89 | 32 | import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.INITIALIZED; |
attila@963 | 33 | import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.LOCAL_VARIABLE_TYPES_CALCULATED; |
lagergren@89 | 34 | import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.LOWERED; |
attila@963 | 35 | import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.OPTIMISTIC_TYPES_ASSIGNED; |
lagergren@137 | 36 | import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.PARSED; |
attila@963 | 37 | import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.SCOPE_DEPTHS_COMPUTED; |
lagergren@89 | 38 | import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.SPLIT; |
attila@963 | 39 | import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.SYMBOLS_ASSIGNED; |
attila@963 | 40 | import static jdk.nashorn.internal.runtime.logging.DebugLogger.quote; |
attila@963 | 41 | import java.io.PrintWriter; |
lagergren@277 | 42 | import java.util.ArrayList; |
lagergren@89 | 43 | import java.util.EnumSet; |
attila@963 | 44 | import java.util.HashMap; |
attila@963 | 45 | import java.util.LinkedHashMap; |
lagergren@277 | 46 | import java.util.List; |
attila@963 | 47 | import java.util.Map; |
attila@963 | 48 | import java.util.Map.Entry; |
lagergren@108 | 49 | import java.util.Set; |
attila@980 | 50 | import jdk.nashorn.internal.AssertsEnabled; |
attila@963 | 51 | import jdk.nashorn.internal.codegen.Compiler.CompilationPhases; |
lagergren@89 | 52 | import jdk.nashorn.internal.ir.FunctionNode; |
attila@416 | 53 | import jdk.nashorn.internal.ir.FunctionNode.CompilationState; |
lagergren@290 | 54 | import jdk.nashorn.internal.ir.LexicalContext; |
attila@963 | 55 | import jdk.nashorn.internal.ir.LiteralNode; |
attila@963 | 56 | import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; |
attila@963 | 57 | import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit; |
attila@416 | 58 | import jdk.nashorn.internal.ir.Node; |
attila@963 | 59 | import jdk.nashorn.internal.ir.SplitNode; |
lagergren@89 | 60 | import jdk.nashorn.internal.ir.debug.ASTWriter; |
lagergren@89 | 61 | import jdk.nashorn.internal.ir.debug.PrintVisitor; |
attila@144 | 62 | import jdk.nashorn.internal.ir.visitor.NodeVisitor; |
attila@963 | 63 | import jdk.nashorn.internal.runtime.CodeInstaller; |
attila@963 | 64 | import jdk.nashorn.internal.runtime.FunctionInitializer; |
attila@963 | 65 | import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData; |
sundar@118 | 66 | import jdk.nashorn.internal.runtime.ScriptEnvironment; |
attila@963 | 67 | import jdk.nashorn.internal.runtime.logging.DebugLogger; |
lagergren@89 | 68 | |
lagergren@89 | 69 | /** |
lagergren@211 | 70 | * A compilation phase is a step in the processes of turning a JavaScript |
lagergren@211 | 71 | * FunctionNode into bytecode. It has an optional return value. |
lagergren@89 | 72 | */ |
lagergren@89 | 73 | enum CompilationPhase { |
attila@963 | 74 | /** |
attila@963 | 75 | * Constant folding pass Simple constant folding that will make elementary |
attila@963 | 76 | * constructs go away |
attila@963 | 77 | */ |
attila@963 | 78 | CONSTANT_FOLDING_PHASE( |
attila@963 | 79 | EnumSet.of( |
attila@963 | 80 | INITIALIZED, |
attila@963 | 81 | PARSED)) { |
attila@963 | 82 | @Override |
attila@963 | 83 | FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { |
attila@963 | 84 | return (FunctionNode)fn.accept(new FoldConstants(compiler)); |
attila@963 | 85 | } |
lagergren@89 | 86 | |
attila@963 | 87 | @Override |
attila@963 | 88 | public String toString() { |
attila@963 | 89 | return "'Constant Folding'"; |
attila@963 | 90 | } |
attila@963 | 91 | }, |
attila@963 | 92 | |
attila@963 | 93 | /** |
attila@963 | 94 | * Lower (Control flow pass) Finalizes the control flow. Clones blocks for |
attila@963 | 95 | * finally constructs and similar things. Establishes termination criteria |
attila@963 | 96 | * for nodes Guarantee return instructions to method making sure control |
attila@963 | 97 | * flow cannot fall off the end. Replacing high level nodes with lower such |
attila@963 | 98 | * as runtime nodes where applicable. |
lagergren@89 | 99 | */ |
attila@963 | 100 | LOWERING_PHASE( |
attila@963 | 101 | EnumSet.of( |
attila@963 | 102 | INITIALIZED, |
attila@963 | 103 | PARSED, |
attila@963 | 104 | CONSTANT_FOLDED)) { |
lagergren@89 | 105 | @Override |
attila@963 | 106 | FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { |
attila@963 | 107 | return (FunctionNode)fn.accept(new Lower(compiler)); |
attila@963 | 108 | } |
lagergren@89 | 109 | |
attila@963 | 110 | @Override |
attila@963 | 111 | public String toString() { |
attila@963 | 112 | return "'Control Flow Lowering'"; |
attila@963 | 113 | } |
attila@963 | 114 | }, |
lagergren@211 | 115 | |
attila@963 | 116 | /** |
attila@963 | 117 | * Phase used only when doing optimistic code generation. It assigns all potentially |
attila@963 | 118 | * optimistic ops a program point so that an UnwarrantedException knows from where |
attila@963 | 119 | * a guess went wrong when creating the continuation to roll back this execution |
attila@963 | 120 | */ |
attila@963 | 121 | PROGRAM_POINT_PHASE( |
attila@963 | 122 | EnumSet.of( |
attila@963 | 123 | INITIALIZED, |
attila@963 | 124 | PARSED, |
attila@963 | 125 | CONSTANT_FOLDED, |
attila@963 | 126 | LOWERED)) { |
attila@963 | 127 | @Override |
attila@963 | 128 | FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { |
attila@963 | 129 | return (FunctionNode)fn.accept(new ProgramPoints()); |
attila@963 | 130 | } |
lagergren@89 | 131 | |
attila@963 | 132 | @Override |
attila@963 | 133 | public String toString() { |
attila@963 | 134 | return "'Program Point Calculation'"; |
attila@963 | 135 | } |
attila@963 | 136 | }, |
lagergren@108 | 137 | |
attila@963 | 138 | TRANSFORM_BUILTINS_PHASE( |
attila@963 | 139 | EnumSet.of( |
attila@963 | 140 | INITIALIZED, |
attila@963 | 141 | PARSED, |
attila@963 | 142 | CONSTANT_FOLDED, |
attila@963 | 143 | LOWERED)) { |
attila@963 | 144 | //we only do this if we have a param type map, otherwise this is not a specialized recompile |
attila@963 | 145 | @Override |
attila@963 | 146 | FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { |
attila@963 | 147 | final FunctionNode newFunctionNode = (FunctionNode)fn.accept(new ApplySpecialization(compiler)); |
attila@963 | 148 | return (FunctionNode)newFunctionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { |
attila@963 | 149 | @Override |
attila@963 | 150 | public Node leaveFunctionNode(final FunctionNode node) { |
attila@963 | 151 | return node.setState(lc, BUILTINS_TRANSFORMED); |
attila@963 | 152 | } |
attila@963 | 153 | }); |
attila@963 | 154 | } |
lagergren@211 | 155 | |
attila@963 | 156 | @Override |
attila@963 | 157 | public String toString() { |
attila@963 | 158 | return "'Builtin Replacement'"; |
attila@963 | 159 | } |
attila@963 | 160 | }, |
attila@963 | 161 | |
attila@963 | 162 | /** |
attila@963 | 163 | * Splitter Split the AST into several compile units based on a heuristic size calculation. |
attila@963 | 164 | * Split IR can lead to scope information being changed. |
attila@963 | 165 | */ |
attila@963 | 166 | SPLITTING_PHASE( |
attila@963 | 167 | EnumSet.of( |
attila@963 | 168 | INITIALIZED, |
attila@963 | 169 | PARSED, |
attila@963 | 170 | CONSTANT_FOLDED, |
attila@963 | 171 | LOWERED, |
attila@963 | 172 | BUILTINS_TRANSFORMED)) { |
attila@963 | 173 | @Override |
attila@963 | 174 | FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { |
attila@963 | 175 | final CompileUnit outermostCompileUnit = compiler.addCompileUnit(0L); |
attila@963 | 176 | |
attila@963 | 177 | FunctionNode newFunctionNode; |
attila@963 | 178 | |
attila@963 | 179 | //ensure elementTypes, postsets and presets exist for splitter and arraynodes |
attila@963 | 180 | newFunctionNode = (FunctionNode)fn.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { |
lagergren@108 | 181 | @Override |
attila@963 | 182 | public LiteralNode<?> leaveLiteralNode(final LiteralNode<?> literalNode) { |
attila@963 | 183 | return literalNode.initialize(lc); |
lagergren@89 | 184 | } |
lagergren@89 | 185 | }); |
lagergren@108 | 186 | |
attila@963 | 187 | newFunctionNode = new Splitter(compiler, newFunctionNode, outermostCompileUnit).split(newFunctionNode, true); |
lagergren@211 | 188 | |
attila@963 | 189 | assert newFunctionNode.getCompileUnit() == outermostCompileUnit : "fn=" + fn.getName() + ", fn.compileUnit (" + newFunctionNode.getCompileUnit() + ") != " + outermostCompileUnit; |
attila@963 | 190 | assert newFunctionNode.isStrict() == compiler.isStrict() : "functionNode.isStrict() != compiler.isStrict() for " + quote(newFunctionNode.getName()); |
attila@963 | 191 | |
attila@963 | 192 | return newFunctionNode; |
attila@963 | 193 | } |
attila@963 | 194 | |
attila@963 | 195 | @Override |
attila@963 | 196 | public String toString() { |
attila@963 | 197 | return "'Code Splitting'"; |
attila@963 | 198 | } |
attila@963 | 199 | }, |
attila@963 | 200 | |
attila@963 | 201 | SYMBOL_ASSIGNMENT_PHASE( |
attila@963 | 202 | EnumSet.of( |
attila@963 | 203 | INITIALIZED, |
attila@963 | 204 | PARSED, |
attila@963 | 205 | CONSTANT_FOLDED, |
attila@963 | 206 | LOWERED, |
attila@963 | 207 | BUILTINS_TRANSFORMED, |
attila@963 | 208 | SPLIT)) { |
attila@963 | 209 | @Override |
attila@963 | 210 | FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { |
attila@963 | 211 | return (FunctionNode)fn.accept(new AssignSymbols(compiler)); |
attila@963 | 212 | } |
attila@963 | 213 | |
attila@963 | 214 | @Override |
attila@963 | 215 | public String toString() { |
attila@963 | 216 | return "'Symbol Assignment'"; |
attila@963 | 217 | } |
attila@963 | 218 | }, |
attila@963 | 219 | |
attila@963 | 220 | SCOPE_DEPTH_COMPUTATION_PHASE( |
attila@963 | 221 | EnumSet.of( |
attila@963 | 222 | INITIALIZED, |
attila@963 | 223 | PARSED, |
attila@963 | 224 | CONSTANT_FOLDED, |
attila@963 | 225 | LOWERED, |
attila@963 | 226 | BUILTINS_TRANSFORMED, |
attila@963 | 227 | SPLIT, |
attila@963 | 228 | SYMBOLS_ASSIGNED)) { |
attila@963 | 229 | @Override |
attila@963 | 230 | FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { |
attila@963 | 231 | return (FunctionNode)fn.accept(new FindScopeDepths(compiler)); |
attila@963 | 232 | } |
attila@963 | 233 | |
attila@963 | 234 | @Override |
attila@963 | 235 | public String toString() { |
attila@963 | 236 | return "'Scope Depth Computation'"; |
attila@963 | 237 | } |
attila@963 | 238 | }, |
attila@963 | 239 | |
attila@963 | 240 | OPTIMISTIC_TYPE_ASSIGNMENT_PHASE( |
attila@963 | 241 | EnumSet.of( |
attila@963 | 242 | INITIALIZED, |
attila@963 | 243 | PARSED, |
attila@963 | 244 | CONSTANT_FOLDED, |
attila@963 | 245 | LOWERED, |
attila@963 | 246 | BUILTINS_TRANSFORMED, |
attila@963 | 247 | SPLIT, |
attila@963 | 248 | SYMBOLS_ASSIGNED, |
attila@963 | 249 | SCOPE_DEPTHS_COMPUTED)) { |
attila@963 | 250 | @Override |
attila@963 | 251 | FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { |
attila@963 | 252 | if (compiler.useOptimisticTypes()) { |
attila@963 | 253 | return (FunctionNode)fn.accept(new OptimisticTypesCalculator(compiler)); |
attila@963 | 254 | } |
attila@963 | 255 | return setStates(fn, OPTIMISTIC_TYPES_ASSIGNED); |
attila@963 | 256 | } |
attila@963 | 257 | |
attila@963 | 258 | @Override |
attila@963 | 259 | public String toString() { |
attila@963 | 260 | return "'Optimistic Type Assignment'"; |
attila@963 | 261 | } |
attila@963 | 262 | }, |
attila@963 | 263 | |
attila@963 | 264 | LOCAL_VARIABLE_TYPE_CALCULATION_PHASE( |
attila@963 | 265 | EnumSet.of( |
attila@963 | 266 | INITIALIZED, |
attila@963 | 267 | PARSED, |
attila@963 | 268 | CONSTANT_FOLDED, |
attila@963 | 269 | LOWERED, |
attila@963 | 270 | BUILTINS_TRANSFORMED, |
attila@963 | 271 | SPLIT, |
attila@963 | 272 | SYMBOLS_ASSIGNED, |
attila@963 | 273 | SCOPE_DEPTHS_COMPUTED, |
attila@963 | 274 | OPTIMISTIC_TYPES_ASSIGNED)) { |
attila@963 | 275 | @Override |
attila@963 | 276 | FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { |
attila@963 | 277 | final FunctionNode newFunctionNode = (FunctionNode)fn.accept(new LocalVariableTypesCalculator(compiler)); |
attila@963 | 278 | |
attila@963 | 279 | final ScriptEnvironment senv = compiler.getScriptEnvironment(); |
attila@963 | 280 | final PrintWriter err = senv.getErr(); |
attila@963 | 281 | |
attila@963 | 282 | //TODO separate phase for the debug printouts for abstraction and clarity |
attila@963 | 283 | if (senv._print_lower_ast || fn.getFlag(FunctionNode.IS_PRINT_LOWER_AST)) { |
attila@963 | 284 | err.println("Lower AST for: " + quote(newFunctionNode.getName())); |
attila@963 | 285 | err.println(new ASTWriter(newFunctionNode)); |
lagergren@108 | 286 | } |
lagergren@108 | 287 | |
attila@963 | 288 | if (senv._print_lower_parse || fn.getFlag(FunctionNode.IS_PRINT_LOWER_PARSE)) { |
attila@963 | 289 | err.println("Lower AST for: " + quote(newFunctionNode.getName())); |
attila@963 | 290 | err.println(new PrintVisitor(newFunctionNode)); |
attila@963 | 291 | } |
attila@963 | 292 | |
attila@963 | 293 | return newFunctionNode; |
attila@963 | 294 | } |
attila@963 | 295 | |
attila@963 | 296 | @Override |
attila@963 | 297 | public String toString() { |
attila@963 | 298 | return "'Local Variable Type Calculation'"; |
attila@963 | 299 | } |
attila@963 | 300 | }, |
attila@963 | 301 | |
lagergren@1003 | 302 | |
lagergren@1003 | 303 | /** |
attila@963 | 304 | * Reuse compile units, if they are already present. We are using the same compiler |
attila@963 | 305 | * to recompile stuff |
attila@963 | 306 | */ |
attila@963 | 307 | REUSE_COMPILE_UNITS_PHASE( |
attila@963 | 308 | EnumSet.of( |
attila@963 | 309 | INITIALIZED, |
attila@963 | 310 | PARSED, |
attila@963 | 311 | CONSTANT_FOLDED, |
attila@963 | 312 | LOWERED, |
attila@963 | 313 | BUILTINS_TRANSFORMED, |
attila@963 | 314 | SPLIT, |
attila@963 | 315 | SYMBOLS_ASSIGNED, |
attila@963 | 316 | SCOPE_DEPTHS_COMPUTED, |
attila@963 | 317 | OPTIMISTIC_TYPES_ASSIGNED, |
attila@963 | 318 | LOCAL_VARIABLE_TYPES_CALCULATED)) { |
attila@963 | 319 | @Override |
attila@963 | 320 | FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { |
attila@963 | 321 | assert phases.isRestOfCompilation() : "reuse compile units currently only used for Rest-Of methods"; |
attila@963 | 322 | |
attila@963 | 323 | final Map<CompileUnit, CompileUnit> map = new HashMap<>(); |
attila@963 | 324 | final Set<CompileUnit> newUnits = CompileUnit.createCompileUnitSet(); |
attila@963 | 325 | |
attila@963 | 326 | final DebugLogger log = compiler.getLogger(); |
attila@963 | 327 | |
attila@963 | 328 | log.fine("Clearing bytecode cache"); |
attila@963 | 329 | compiler.clearBytecode(); |
attila@963 | 330 | |
attila@963 | 331 | for (final CompileUnit oldUnit : compiler.getCompileUnits()) { |
attila@963 | 332 | assert map.get(oldUnit) == null; |
attila@963 | 333 | final StringBuilder sb = new StringBuilder(compiler.nextCompileUnitName()); |
attila@963 | 334 | if (phases.isRestOfCompilation()) { |
attila@963 | 335 | sb.append("$restOf"); |
attila@963 | 336 | } |
lagergren@1004 | 337 | //it's ok to not copy the initCount, methodCount and clinitCount here, as codegen is what |
lagergren@1004 | 338 | //fills those out anyway. Thus no need for a copy constructor |
attila@963 | 339 | final CompileUnit newUnit = compiler.createCompileUnit(sb.toString(), oldUnit.getWeight()); |
attila@963 | 340 | log.fine("Creating new compile unit ", oldUnit, " => ", newUnit); |
attila@963 | 341 | map.put(oldUnit, newUnit); |
attila@963 | 342 | assert newUnit != null; |
attila@963 | 343 | newUnits.add(newUnit); |
attila@963 | 344 | } |
attila@963 | 345 | |
attila@963 | 346 | log.fine("Replacing compile units in Compiler..."); |
attila@963 | 347 | compiler.replaceCompileUnits(newUnits); |
attila@963 | 348 | log.fine("Done"); |
attila@963 | 349 | |
attila@963 | 350 | //replace old compile units in function nodes, if any are assigned, |
attila@963 | 351 | //for example by running the splitter on this function node in a previous |
attila@963 | 352 | //partial code generation |
attila@963 | 353 | final FunctionNode newFunctionNode = (FunctionNode)fn.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { |
attila@144 | 354 | @Override |
attila@963 | 355 | public Node leaveFunctionNode(final FunctionNode node) { |
attila@963 | 356 | final CompileUnit oldUnit = node.getCompileUnit(); |
attila@963 | 357 | assert oldUnit != null : "no compile unit in function node"; |
attila@963 | 358 | |
attila@963 | 359 | final CompileUnit newUnit = map.get(oldUnit); |
attila@963 | 360 | assert newUnit != null : "old unit has no mapping to new unit " + oldUnit; |
attila@963 | 361 | |
attila@963 | 362 | log.fine("Replacing compile unit: ", oldUnit, " => ", newUnit, " in ", quote(node.getName())); |
attila@963 | 363 | return node.setCompileUnit(lc, newUnit).setState(lc, CompilationState.COMPILE_UNITS_REUSED); |
attila@963 | 364 | } |
attila@963 | 365 | |
attila@963 | 366 | @Override |
attila@963 | 367 | public Node leaveSplitNode(final SplitNode node) { |
attila@963 | 368 | final CompileUnit oldUnit = node.getCompileUnit(); |
attila@963 | 369 | assert oldUnit != null : "no compile unit in function node"; |
attila@963 | 370 | |
attila@963 | 371 | final CompileUnit newUnit = map.get(oldUnit); |
attila@963 | 372 | assert newUnit != null : "old unit has no mapping to new unit " + oldUnit; |
attila@963 | 373 | |
attila@963 | 374 | log.fine("Replacing compile unit: ", oldUnit, " => ", newUnit, " in ", quote(node.getName())); |
attila@963 | 375 | return node.setCompileUnit(lc, newUnit); |
attila@963 | 376 | } |
attila@963 | 377 | |
attila@963 | 378 | @Override |
attila@963 | 379 | public Node leaveLiteralNode(final LiteralNode<?> node) { |
attila@963 | 380 | if (node instanceof ArrayLiteralNode) { |
attila@963 | 381 | final ArrayLiteralNode aln = (ArrayLiteralNode)node; |
attila@963 | 382 | if (aln.getUnits() == null) { |
attila@963 | 383 | return node; |
attila@963 | 384 | } |
attila@963 | 385 | final List<ArrayUnit> newArrayUnits = new ArrayList<>(); |
attila@963 | 386 | for (final ArrayUnit au : aln.getUnits()) { |
attila@963 | 387 | final CompileUnit newUnit = map.get(au.getCompileUnit()); |
attila@963 | 388 | assert newUnit != null; |
attila@963 | 389 | newArrayUnits.add(new ArrayUnit(newUnit, au.getLo(), au.getHi())); |
attila@963 | 390 | } |
attila@963 | 391 | return aln.setUnits(lc, newArrayUnits); |
attila@144 | 392 | } |
attila@963 | 393 | return node; |
attila@963 | 394 | } |
lagergren@211 | 395 | |
attila@963 | 396 | @Override |
attila@963 | 397 | public Node leaveDefault(final Node node) { |
attila@963 | 398 | return node.ensureUniqueLabels(lc); |
attila@144 | 399 | } |
attila@144 | 400 | }); |
lagergren@211 | 401 | |
lagergren@211 | 402 | return newFunctionNode; |
lagergren@89 | 403 | } |
lagergren@89 | 404 | |
lagergren@89 | 405 | @Override |
lagergren@89 | 406 | public String toString() { |
attila@963 | 407 | return "'Reuse Compile Units'"; |
lagergren@89 | 408 | } |
lagergren@89 | 409 | }, |
lagergren@89 | 410 | |
attila@963 | 411 | /** |
lagergren@89 | 412 | * Bytecode generation: |
lagergren@89 | 413 | * |
lagergren@211 | 414 | * Generate the byte code class(es) resulting from the compiled FunctionNode |
lagergren@89 | 415 | */ |
attila@963 | 416 | BYTECODE_GENERATION_PHASE( |
attila@963 | 417 | EnumSet.of( |
attila@963 | 418 | INITIALIZED, |
attila@963 | 419 | PARSED, |
attila@963 | 420 | CONSTANT_FOLDED, |
attila@963 | 421 | LOWERED, |
attila@963 | 422 | BUILTINS_TRANSFORMED, |
attila@963 | 423 | SPLIT, |
attila@963 | 424 | SYMBOLS_ASSIGNED, |
attila@963 | 425 | SCOPE_DEPTHS_COMPUTED, |
attila@963 | 426 | OPTIMISTIC_TYPES_ASSIGNED, |
attila@963 | 427 | LOCAL_VARIABLE_TYPES_CALCULATED)) { |
attila@963 | 428 | |
lagergren@89 | 429 | @Override |
attila@963 | 430 | FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { |
attila@963 | 431 | final ScriptEnvironment senv = compiler.getScriptEnvironment(); |
attila@963 | 432 | |
lagergren@211 | 433 | FunctionNode newFunctionNode = fn; |
lagergren@89 | 434 | |
lagergren@1004 | 435 | //root class is special, as it is bootstrapped from createProgramFunction, thus it's skipped |
lagergren@1004 | 436 | //in CodeGeneration - the rest can be used as a working "is compile unit used" metric |
lagergren@1004 | 437 | fn.getCompileUnit().setUsed(); |
lagergren@1004 | 438 | |
attila@963 | 439 | compiler.getLogger().fine("Starting bytecode generation for ", quote(fn.getName()), " - restOf=", phases.isRestOfCompilation()); |
lagergren@1004 | 440 | |
attila@963 | 441 | final CodeGenerator codegen = new CodeGenerator(compiler, phases.isRestOfCompilation() ? compiler.getContinuationEntryPoints() : null); |
lagergren@1004 | 442 | |
lagergren@89 | 443 | try { |
attila@963 | 444 | // Explicitly set BYTECODE_GENERATED here; it can not be set in case of skipping codegen for :program |
attila@963 | 445 | // in the lazy + optimistic world. See CodeGenerator.skipFunction(). |
attila@963 | 446 | newFunctionNode = ((FunctionNode)newFunctionNode.accept(codegen)).setState(null, BYTECODE_GENERATED); |
lagergren@89 | 447 | codegen.generateScopeCalls(); |
lagergren@89 | 448 | } catch (final VerifyError e) { |
attila@963 | 449 | if (senv._verify_code || senv._print_code) { |
attila@963 | 450 | senv.getErr().println(e.getClass().getSimpleName() + ": " + e.getMessage()); |
attila@963 | 451 | if (senv._dump_on_error) { |
attila@963 | 452 | e.printStackTrace(senv.getErr()); |
lagergren@89 | 453 | } |
lagergren@89 | 454 | } else { |
lagergren@89 | 455 | throw e; |
lagergren@89 | 456 | } |
attila@963 | 457 | } catch (final Throwable e) { |
attila@963 | 458 | // Provide source file and line number being compiled when the assertion occurred |
attila@963 | 459 | throw new AssertionError("Failed generating bytecode for " + fn.getSourceName() + ":" + codegen.getLastLineNumber(), e); |
lagergren@89 | 460 | } |
lagergren@89 | 461 | |
lagergren@89 | 462 | for (final CompileUnit compileUnit : compiler.getCompileUnits()) { |
lagergren@1004 | 463 | final ClassEmitter classEmitter = compileUnit.getClassEmitter(); |
lagergren@1004 | 464 | classEmitter.end(); |
lagergren@1004 | 465 | |
lagergren@1003 | 466 | if (!compileUnit.isUsed()) { |
lagergren@1003 | 467 | compiler.getLogger().fine("Skipping unused compile unit ", compileUnit); |
lagergren@1003 | 468 | continue; |
lagergren@1003 | 469 | } |
lagergren@1003 | 470 | |
lagergren@89 | 471 | final byte[] bytecode = classEmitter.toByteArray(); |
lagergren@89 | 472 | assert bytecode != null; |
lagergren@89 | 473 | |
lagergren@89 | 474 | final String className = compileUnit.getUnitClassName(); |
lagergren@1004 | 475 | compiler.addClass(className, bytecode); //classes are only added to the bytecode map if compile unit is used |
lagergren@1004 | 476 | |
lagergren@1004 | 477 | CompileUnit.increaseEmitCount(); |
lagergren@89 | 478 | |
lagergren@211 | 479 | // should we verify the generated code? |
attila@963 | 480 | if (senv._verify_code) { |
sundar@118 | 481 | compiler.getCodeInstaller().verify(bytecode); |
lagergren@89 | 482 | } |
lagergren@89 | 483 | |
attila@963 | 484 | DumpBytecode.dumpBytecode(senv, compiler.getLogger(), bytecode, className); |
lagergren@89 | 485 | } |
lagergren@211 | 486 | |
lagergren@211 | 487 | return newFunctionNode; |
lagergren@89 | 488 | } |
lagergren@89 | 489 | |
lagergren@89 | 490 | @Override |
lagergren@89 | 491 | public String toString() { |
attila@963 | 492 | return "'Bytecode Generation'"; |
lagergren@89 | 493 | } |
attila@963 | 494 | }, |
lagergren@89 | 495 | |
attila@963 | 496 | INSTALL_PHASE( |
attila@963 | 497 | EnumSet.of( |
attila@963 | 498 | INITIALIZED, |
attila@963 | 499 | PARSED, |
attila@963 | 500 | CONSTANT_FOLDED, |
attila@963 | 501 | LOWERED, |
attila@963 | 502 | BUILTINS_TRANSFORMED, |
attila@963 | 503 | SPLIT, |
attila@963 | 504 | SYMBOLS_ASSIGNED, |
attila@963 | 505 | SCOPE_DEPTHS_COMPUTED, |
attila@963 | 506 | OPTIMISTIC_TYPES_ASSIGNED, |
attila@963 | 507 | LOCAL_VARIABLE_TYPES_CALCULATED, |
attila@963 | 508 | BYTECODE_GENERATED)) { |
attila@963 | 509 | |
attila@963 | 510 | @Override |
attila@963 | 511 | FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { |
attila@963 | 512 | final DebugLogger log = compiler.getLogger(); |
attila@963 | 513 | |
attila@963 | 514 | final Map<String, Class<?>> installedClasses = new LinkedHashMap<>(); |
attila@963 | 515 | |
attila@963 | 516 | boolean first = true; |
attila@963 | 517 | Class<?> rootClass = null; |
attila@963 | 518 | long length = 0L; |
attila@963 | 519 | |
attila@963 | 520 | final CodeInstaller<ScriptEnvironment> codeInstaller = compiler.getCodeInstaller(); |
attila@963 | 521 | final Map<String, byte[]> bytecode = compiler.getBytecode(); |
attila@963 | 522 | |
attila@963 | 523 | for (final Entry<String, byte[]> entry : bytecode.entrySet()) { |
attila@963 | 524 | final String className = entry.getKey(); |
attila@963 | 525 | //assert !first || className.equals(compiler.getFirstCompileUnit().getUnitClassName()) : "first=" + first + " className=" + className + " != " + compiler.getFirstCompileUnit().getUnitClassName(); |
attila@963 | 526 | final byte[] code = entry.getValue(); |
attila@963 | 527 | length += code.length; |
attila@963 | 528 | |
attila@963 | 529 | final Class<?> clazz = codeInstaller.install(className, code); |
attila@963 | 530 | if (first) { |
attila@963 | 531 | rootClass = clazz; |
attila@963 | 532 | first = false; |
attila@963 | 533 | } |
attila@963 | 534 | installedClasses.put(className, clazz); |
attila@963 | 535 | } |
attila@963 | 536 | |
attila@963 | 537 | if (rootClass == null) { |
attila@963 | 538 | throw new CompilationException("Internal compiler error: root class not found!"); |
attila@963 | 539 | } |
attila@963 | 540 | |
attila@963 | 541 | final Object[] constants = compiler.getConstantData().toArray(); |
attila@963 | 542 | codeInstaller.initialize(installedClasses.values(), compiler.getSource(), constants); |
attila@963 | 543 | |
attila@963 | 544 | // initialize transient fields on recompilable script function data |
attila@963 | 545 | for (final Object constant: constants) { |
attila@963 | 546 | if (constant instanceof RecompilableScriptFunctionData) { |
attila@963 | 547 | ((RecompilableScriptFunctionData)constant).initTransients(compiler.getSource(), codeInstaller); |
attila@963 | 548 | } |
attila@963 | 549 | } |
attila@963 | 550 | |
attila@963 | 551 | // initialize function in the compile units |
attila@963 | 552 | for (final CompileUnit unit : compiler.getCompileUnits()) { |
lagergren@1003 | 553 | if (!unit.isUsed()) { |
lagergren@1003 | 554 | continue; |
lagergren@1003 | 555 | } |
attila@963 | 556 | unit.setCode(installedClasses.get(unit.getUnitClassName())); |
attila@963 | 557 | } |
attila@963 | 558 | |
attila@963 | 559 | if (!compiler.isOnDemandCompilation()) { |
attila@963 | 560 | // Initialize functions |
attila@963 | 561 | final Map<Integer, FunctionInitializer> initializers = compiler.getFunctionInitializers(); |
attila@963 | 562 | if (initializers != null) { |
attila@963 | 563 | for (final Entry<Integer, FunctionInitializer> entry : initializers.entrySet()) { |
attila@963 | 564 | final FunctionInitializer initializer = entry.getValue(); |
attila@963 | 565 | initializer.setCode(installedClasses.get(initializer.getClassName())); |
attila@963 | 566 | compiler.getScriptFunctionData(entry.getKey()).initializeCode(initializer); |
attila@963 | 567 | } |
attila@963 | 568 | } |
attila@963 | 569 | } |
attila@963 | 570 | |
attila@963 | 571 | if (log.isEnabled()) { |
attila@963 | 572 | final StringBuilder sb = new StringBuilder(); |
attila@963 | 573 | |
attila@963 | 574 | sb.append("Installed class '"). |
attila@963 | 575 | append(rootClass.getSimpleName()). |
attila@963 | 576 | append('\''). |
attila@963 | 577 | append(" ["). |
attila@963 | 578 | append(rootClass.getName()). |
attila@963 | 579 | append(", size="). |
attila@963 | 580 | append(length). |
attila@963 | 581 | append(" bytes, "). |
attila@963 | 582 | append(compiler.getCompileUnits().size()). |
attila@963 | 583 | append(" compile unit(s)]"); |
attila@963 | 584 | |
attila@963 | 585 | log.fine(sb.toString()); |
attila@963 | 586 | } |
attila@963 | 587 | |
attila@963 | 588 | return setStates(fn.setRootClass(null, rootClass), BYTECODE_INSTALLED); |
attila@963 | 589 | } |
attila@963 | 590 | |
attila@963 | 591 | @Override |
attila@963 | 592 | public String toString() { |
attila@963 | 593 | return "'Class Installation'"; |
attila@963 | 594 | } |
attila@963 | 595 | |
attila@963 | 596 | }; |
attila@963 | 597 | |
attila@963 | 598 | /** pre conditions required for function node to which this transform is to be applied */ |
lagergren@89 | 599 | private final EnumSet<CompilationState> pre; |
attila@963 | 600 | |
attila@963 | 601 | /** start time of transform - used for timing, see {@link jdk.nashorn.internal.runtime.Timing} */ |
lagergren@89 | 602 | private long startTime; |
attila@963 | 603 | |
attila@963 | 604 | /** start time of transform - used for timing, see {@link jdk.nashorn.internal.runtime.Timing} */ |
lagergren@89 | 605 | private long endTime; |
attila@963 | 606 | |
attila@963 | 607 | /** boolean that is true upon transform completion */ |
lagergren@89 | 608 | private boolean isFinished; |
lagergren@89 | 609 | |
lagergren@89 | 610 | private CompilationPhase(final EnumSet<CompilationState> pre) { |
lagergren@137 | 611 | this.pre = pre; |
lagergren@89 | 612 | } |
lagergren@89 | 613 | |
attila@963 | 614 | private static FunctionNode setStates(final FunctionNode functionNode, final CompilationState state) { |
attila@980 | 615 | if (!AssertsEnabled.assertsEnabled()) { |
attila@980 | 616 | return functionNode; |
attila@980 | 617 | } |
attila@963 | 618 | return (FunctionNode)functionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { |
attila@963 | 619 | @Override |
attila@963 | 620 | public Node leaveFunctionNode(final FunctionNode fn) { |
attila@963 | 621 | return fn.setState(lc, state); |
attila@963 | 622 | } |
attila@963 | 623 | }); |
lagergren@89 | 624 | } |
lagergren@89 | 625 | |
attila@963 | 626 | /** |
attila@963 | 627 | * Start a compilation phase |
lagergren@1028 | 628 | * @param compiler the compiler to use |
attila@963 | 629 | * @param functionNode function to compile |
attila@963 | 630 | * @return function node |
attila@963 | 631 | */ |
attila@963 | 632 | protected FunctionNode begin(final Compiler compiler, final FunctionNode functionNode) { |
attila@963 | 633 | compiler.getLogger().indent(); |
attila@963 | 634 | |
attila@963 | 635 | assert pre != null; |
attila@963 | 636 | |
attila@980 | 637 | if (!functionNode.hasState(pre)) { |
attila@963 | 638 | final StringBuilder sb = new StringBuilder("Compilation phase "); |
attila@963 | 639 | sb.append(this). |
attila@963 | 640 | append(" is not applicable to "). |
attila@963 | 641 | append(quote(functionNode.getName())). |
attila@963 | 642 | append("\n\tFunctionNode state = "). |
attila@963 | 643 | append(functionNode.getState()). |
attila@963 | 644 | append("\n\tRequired state = "). |
attila@963 | 645 | append(this.pre); |
attila@963 | 646 | |
attila@963 | 647 | throw new CompilationException(sb.toString()); |
attila@963 | 648 | } |
attila@963 | 649 | |
attila@977 | 650 | startTime = System.nanoTime(); |
attila@963 | 651 | |
attila@963 | 652 | return functionNode; |
attila@963 | 653 | } |
attila@963 | 654 | |
attila@963 | 655 | /** |
attila@963 | 656 | * End a compilation phase |
attila@963 | 657 | * @param compiler the compiler |
attila@963 | 658 | * @param functionNode function node to compile |
attila@963 | 659 | * @return function node |
attila@963 | 660 | */ |
attila@963 | 661 | protected FunctionNode end(final Compiler compiler, final FunctionNode functionNode) { |
attila@963 | 662 | compiler.getLogger().unindent(); |
attila@977 | 663 | endTime = System.nanoTime(); |
attila@963 | 664 | compiler.getScriptEnvironment()._timing.accumulateTime(toString(), endTime - startTime); |
lagergren@89 | 665 | |
lagergren@89 | 666 | isFinished = true; |
lagergren@211 | 667 | return functionNode; |
lagergren@89 | 668 | } |
lagergren@89 | 669 | |
lagergren@89 | 670 | boolean isFinished() { |
lagergren@89 | 671 | return isFinished; |
lagergren@89 | 672 | } |
lagergren@89 | 673 | |
lagergren@89 | 674 | long getStartTime() { |
lagergren@89 | 675 | return startTime; |
lagergren@89 | 676 | } |
lagergren@89 | 677 | |
lagergren@89 | 678 | long getEndTime() { |
lagergren@89 | 679 | return endTime; |
lagergren@89 | 680 | } |
lagergren@89 | 681 | |
attila@963 | 682 | abstract FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode functionNode) throws CompilationException; |
lagergren@89 | 683 | |
attila@963 | 684 | /** |
attila@963 | 685 | * Apply a transform to a function node, returning the transfored function node. If the transform is not |
attila@963 | 686 | * applicable, an exception is thrown. Every transform requires the function to have a certain number of |
attila@963 | 687 | * states to operate. It can have more states set, but not fewer. The state list, i.e. the constructor |
attila@963 | 688 | * arguments to any of the CompilationPhase enum entries, is a set of REQUIRED states. |
attila@963 | 689 | * |
attila@963 | 690 | * @param compiler compiler |
attila@963 | 691 | * @param phases current complete pipeline of which this phase is one |
attila@963 | 692 | * @param functionNode function node to transform |
attila@963 | 693 | * |
attila@963 | 694 | * @return transformed function node |
attila@963 | 695 | * |
attila@963 | 696 | * @throws CompilationException if function node lacks the state required to run the transform on it |
attila@963 | 697 | */ |
attila@963 | 698 | final FunctionNode apply(final Compiler compiler, final CompilationPhases phases, final FunctionNode functionNode) throws CompilationException { |
attila@963 | 699 | assert phases.contains(this); |
attila@963 | 700 | |
attila@963 | 701 | return end(compiler, transform(compiler, phases, begin(compiler, functionNode))); |
lagergren@89 | 702 | } |
lagergren@89 | 703 | |
lagergren@89 | 704 | } |