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

Tue, 26 Aug 2014 15:52:55 +0200

author
attila
date
Tue, 26 Aug 2014 15:52:55 +0200
changeset 980
44b69fb3b031
parent 977
7cf80b2dc39b
child 1003
3f49db18721f
permissions
-rw-r--r--

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

mercurial