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

Thu, 25 Sep 2014 15:53:47 +0200

author
lagergren
date
Thu, 25 Sep 2014 15:53:47 +0200
changeset 1028
d79265f2fa92
parent 1004
698280da463a
child 1064
03c06c337d9d
permissions
-rw-r--r--

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 }

mercurial