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

Mon, 06 Oct 2014 10:43:57 +0200

author
lagergren
date
Mon, 06 Oct 2014 10:43:57 +0200
changeset 1031
9b24fc6da691
parent 1029
70597fd25c61
child 1036
8a99ee1fb375
permissions
-rw-r--r--

8059231: Verify that octane raytrace now works with optimistic types turned off. Add better logging for optimistic types in the compiler.
Reviewed-by: attila, hannesw, sundar

     1 /*
     2  * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     8  * particular file as subject to the "Classpath" exception as provided
     9  * by Oracle in the LICENSE file that accompanied this code.
    10  *
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    14  * version 2 for more details (a copy is included in the LICENSE file that
    15  * accompanied this code).
    16  *
    17  * You should have received a copy of the GNU General Public License version
    18  * 2 along with this work; if not, write to the Free Software Foundation,
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    20  *
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    22  * or visit www.oracle.com if you need additional information or have any
    23  * questions.
    24  */
    26 package jdk.nashorn.internal.codegen;
    28 import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
    29 import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
    30 import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
    31 import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
    32 import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
    33 import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
    34 import static jdk.nashorn.internal.runtime.logging.DebugLogger.quote;
    35 import java.io.File;
    36 import java.lang.invoke.MethodType;
    37 import java.util.Arrays;
    38 import java.util.Collections;
    39 import java.util.Comparator;
    40 import java.util.HashMap;
    41 import java.util.Iterator;
    42 import java.util.LinkedHashMap;
    43 import java.util.LinkedList;
    44 import java.util.List;
    45 import java.util.Map;
    46 import java.util.Set;
    47 import java.util.TreeMap;
    48 import java.util.concurrent.atomic.AtomicInteger;
    49 import java.util.function.Consumer;
    50 import java.util.logging.Level;
    51 import jdk.internal.dynalink.support.NameCodec;
    52 import jdk.nashorn.internal.codegen.types.Type;
    53 import jdk.nashorn.internal.ir.Expression;
    54 import jdk.nashorn.internal.ir.FunctionNode;
    55 import jdk.nashorn.internal.ir.Optimistic;
    56 import jdk.nashorn.internal.ir.debug.ClassHistogramElement;
    57 import jdk.nashorn.internal.ir.debug.ObjectSizeCalculator;
    58 import jdk.nashorn.internal.runtime.CodeInstaller;
    59 import jdk.nashorn.internal.runtime.Context;
    60 import jdk.nashorn.internal.runtime.ErrorManager;
    61 import jdk.nashorn.internal.runtime.FunctionInitializer;
    62 import jdk.nashorn.internal.runtime.ParserException;
    63 import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
    64 import jdk.nashorn.internal.runtime.ScriptEnvironment;
    65 import jdk.nashorn.internal.runtime.ScriptObject;
    66 import jdk.nashorn.internal.runtime.ScriptRuntime;
    67 import jdk.nashorn.internal.runtime.Source;
    68 import jdk.nashorn.internal.runtime.logging.DebugLogger;
    69 import jdk.nashorn.internal.runtime.logging.Loggable;
    70 import jdk.nashorn.internal.runtime.logging.Logger;
    72 /**
    73  * Responsible for converting JavaScripts to java byte code. Main entry
    74  * point for code generator. The compiler may also install classes given some
    75  * predefined Code installation policy, given to it at construction time.
    76  * @see CodeInstaller
    77  */
    78 @Logger(name="compiler")
    79 public final class Compiler implements Loggable {
    81     /** Name of the scripts package */
    82     public static final String SCRIPTS_PACKAGE = "jdk/nashorn/internal/scripts";
    84     /** Name of the objects package */
    85     public static final String OBJECTS_PACKAGE = "jdk/nashorn/internal/objects";
    87     private final ScriptEnvironment env;
    89     private final Source source;
    91     private final String sourceName;
    93     private final ErrorManager errors;
    95     private final boolean optimistic;
    97     private final Map<String, byte[]> bytecode;
    99     private final Set<CompileUnit> compileUnits;
   101     private final ConstantData constantData;
   103     private final CodeInstaller<ScriptEnvironment> installer;
   105     /** logger for compiler, trampolines, splits and related code generation events
   106      *  that affect classes */
   107     private final DebugLogger log;
   109     private final Context context;
   111     private final TypeMap types;
   113     // Runtime scope in effect at the time of the compilation. Used to evaluate types of expressions and prevent overly
   114     // optimistic assumptions (which will lead to unnecessary deoptimizing recompilations).
   115     private final TypeEvaluator typeEvaluator;
   117     private final boolean strict;
   119     private final boolean onDemand;
   121     /**
   122      * If this is a recompilation, this is how we pass in the invalidations, e.g. programPoint=17, Type == int means
   123      * that using whatever was at program point 17 as an int failed.
   124      */
   125     private final Map<Integer, Type> invalidatedProgramPoints;
   127     /**
   128      * Descriptor of the location where we write the type information after compilation.
   129      */
   130     private final Object typeInformationFile;
   132     /**
   133      * Compile unit name of first compile unit - this prefix will be used for all
   134      * classes that a compilation generates.
   135      */
   136     private final String firstCompileUnitName;
   138     /**
   139      * Contains the program point that should be used as the continuation entry point, as well as all previous
   140      * continuation entry points executed as part of a single logical invocation of the function. In practical terms, if
   141      * we execute a rest-of method from the program point 17, but then we hit deoptimization again during it at program
   142      * point 42, and execute a rest-of method from the program point 42, and then we hit deoptimization again at program
   143      * point 57 and are compiling a rest-of method for it, the values in the array will be [57, 42, 17]. This is only
   144      * set when compiling a rest-of method. If this method is a rest-of for a non-rest-of method, the array will have
   145      * one element. If it is a rest-of for a rest-of, the array will have two elements, and so on.
   146      */
   147     private final int[] continuationEntryPoints;
   149     /**
   150      * ScriptFunction data for what is being compile, where applicable.
   151      * TODO: make this immutable, propagate it through the CompilationPhases
   152      */
   153     private RecompilableScriptFunctionData compiledFunction;
   155     /**
   156      * Most compile unit names are longer than the default StringBuilder buffer,
   157      * worth startup performance when massive class generation is going on to increase
   158      * this
   159      */
   160     private static final int COMPILE_UNIT_NAME_BUFFER_SIZE = 32;
   162     /**
   163      * Compilation phases that a compilation goes through
   164      */
   165     public static class CompilationPhases implements Iterable<CompilationPhase> {
   167         /** Singleton that describes a standard eager compilation - this includes code installation */
   168         public final static CompilationPhases COMPILE_ALL = new CompilationPhases(
   169                 "Compile all",
   170                 new CompilationPhase[] {
   171                         CompilationPhase.CONSTANT_FOLDING_PHASE,
   172                         CompilationPhase.LOWERING_PHASE,
   173                         CompilationPhase.PROGRAM_POINT_PHASE,
   174                         CompilationPhase.TRANSFORM_BUILTINS_PHASE,
   175                         CompilationPhase.SPLITTING_PHASE,
   176                         CompilationPhase.SYMBOL_ASSIGNMENT_PHASE,
   177                         CompilationPhase.SCOPE_DEPTH_COMPUTATION_PHASE,
   178                         CompilationPhase.OPTIMISTIC_TYPE_ASSIGNMENT_PHASE,
   179                         CompilationPhase.LOCAL_VARIABLE_TYPE_CALCULATION_PHASE,
   180                         CompilationPhase.BYTECODE_GENERATION_PHASE,
   181                         CompilationPhase.INSTALL_PHASE
   182                 });
   184         /** Compile all for a rest of method */
   185         public final static CompilationPhases COMPILE_ALL_RESTOF =
   186                 COMPILE_ALL.setDescription("Compile all, rest of").addAfter(CompilationPhase.LOCAL_VARIABLE_TYPE_CALCULATION_PHASE, CompilationPhase.REUSE_COMPILE_UNITS_PHASE);
   188         /** Singleton that describes a standard eager compilation, but no installation, for example used by --compile-only */
   189         public final static CompilationPhases COMPILE_ALL_NO_INSTALL =
   190                 COMPILE_ALL.
   191                 removeLast().
   192                 setDescription("Compile without install");
   194         /** Singleton that describes compilation up to the CodeGenerator, but not actually generating code */
   195         public final static CompilationPhases COMPILE_UPTO_BYTECODE =
   196                 COMPILE_ALL.
   197                 removeLast().
   198                 removeLast().
   199                 setDescription("Compile upto bytecode");
   201         /**
   202          * Singleton that describes back end of method generation, given that we have generated the normal
   203          * method up to CodeGenerator as in {@link CompilationPhases#COMPILE_UPTO_BYTECODE}
   204          */
   205         public final static CompilationPhases COMPILE_FROM_BYTECODE = new CompilationPhases(
   206                 "Generate bytecode and install",
   207                 new CompilationPhase[] {
   208                         CompilationPhase.BYTECODE_GENERATION_PHASE,
   209                         CompilationPhase.INSTALL_PHASE
   210                 });
   212         /**
   213          * Singleton that describes restOf method generation, given that we have generated the normal
   214          * method up to CodeGenerator as in {@link CompilationPhases#COMPILE_UPTO_BYTECODE}
   215          */
   216         public final static CompilationPhases COMPILE_FROM_BYTECODE_RESTOF =
   217                 COMPILE_FROM_BYTECODE.
   218                 addFirst(CompilationPhase.REUSE_COMPILE_UNITS_PHASE).
   219                 setDescription("Generate bytecode and install - RestOf method");
   221         private final List<CompilationPhase> phases;
   223         private final String desc;
   225         private CompilationPhases(final String desc, final CompilationPhase... phases) {
   226             this.desc = desc;
   228             final List<CompilationPhase> newPhases = new LinkedList<>();
   229             newPhases.addAll(Arrays.asList(phases));
   230             this.phases = Collections.unmodifiableList(newPhases);
   231         }
   233         @Override
   234         public String toString() {
   235             return "'" + desc + "' " + phases.toString();
   236         }
   238         private CompilationPhases setDescription(final String desc) {
   239             return new CompilationPhases(desc, phases.toArray(new CompilationPhase[phases.size()]));
   240         }
   242         private CompilationPhases removeLast() {
   243             final LinkedList<CompilationPhase> list = new LinkedList<>(phases);
   244             list.removeLast();
   245             return new CompilationPhases(desc, list.toArray(new CompilationPhase[list.size()]));
   246         }
   248         private CompilationPhases addFirst(final CompilationPhase phase) {
   249             if (phases.contains(phase)) {
   250                 return this;
   251             }
   252             final LinkedList<CompilationPhase> list = new LinkedList<>(phases);
   253             list.addFirst(phase);
   254             return new CompilationPhases(desc, list.toArray(new CompilationPhase[list.size()]));
   255         }
   257         @SuppressWarnings("unused") //TODO I'll use this soon
   258         private CompilationPhases replace(final CompilationPhase phase, final CompilationPhase newPhase) {
   259             final LinkedList<CompilationPhase> list = new LinkedList<>();
   260             for (final CompilationPhase p : phases) {
   261                 list.add(p == phase ? newPhase : p);
   262             }
   263             return new CompilationPhases(desc, list.toArray(new CompilationPhase[list.size()]));
   264         }
   266         private CompilationPhases addAfter(final CompilationPhase phase, final CompilationPhase newPhase) {
   267             final LinkedList<CompilationPhase> list = new LinkedList<>();
   268             for (final CompilationPhase p : phases) {
   269                 list.add(p);
   270                 if (p == phase) {
   271                     list.add(newPhase);
   272                 }
   273             }
   274             return new CompilationPhases(desc, list.toArray(new CompilationPhase[list.size()]));
   275         }
   277         boolean contains(final CompilationPhase phase) {
   278             return phases.contains(phase);
   279         }
   281         @Override
   282         public Iterator<CompilationPhase> iterator() {
   283             return phases.iterator();
   284         }
   286         boolean isRestOfCompilation() {
   287             return this == COMPILE_ALL_RESTOF || this == COMPILE_FROM_BYTECODE_RESTOF;
   288         }
   290         String getDesc() {
   291             return desc;
   292         }
   294         String toString(final String prefix) {
   295             final StringBuilder sb = new StringBuilder();
   296             for (final CompilationPhase phase : phases) {
   297                 sb.append(prefix).append(phase).append('\n');
   298             }
   299             return sb.toString();
   300         }
   301     }
   303     /**
   304      * This array contains names that need to be reserved at the start
   305      * of a compile, to avoid conflict with variable names later introduced.
   306      * See {@link CompilerConstants} for special names used for structures
   307      * during a compile.
   308      */
   309     private static String[] RESERVED_NAMES = {
   310         SCOPE.symbolName(),
   311         THIS.symbolName(),
   312         RETURN.symbolName(),
   313         CALLEE.symbolName(),
   314         VARARGS.symbolName(),
   315         ARGUMENTS.symbolName()
   316     };
   318     // per instance
   319     private final int compilationId = COMPILATION_ID.getAndIncrement();
   321     // per instance
   322     private final AtomicInteger nextCompileUnitId = new AtomicInteger(0);
   324     private static final AtomicInteger COMPILATION_ID = new AtomicInteger(0);
   326     /**
   327      * Constructor
   328      *
   329      * @param context   context
   330      * @param env       script environment
   331      * @param installer code installer
   332      * @param source    source to compile
   333      * @param errors    error manager
   334      * @param isStrict  is this a strict compilation
   335      */
   336     public Compiler(
   337             final Context context,
   338             final ScriptEnvironment env,
   339             final CodeInstaller<ScriptEnvironment> installer,
   340             final Source source,
   341             final ErrorManager errors,
   342             final boolean isStrict) {
   343         this(context, env, installer, source, errors, isStrict, false, null, null, null, null, null, null);
   344     }
   346     /**
   347      * Constructor
   348      *
   349      * @param context                  context
   350      * @param env                      script environment
   351      * @param installer                code installer
   352      * @param source                   source to compile
   353      * @param errors                   error manager
   354      * @param isStrict                 is this a strict compilation
   355      * @param isOnDemand               is this an on demand compilation
   356      * @param compiledFunction         compiled function, if any
   357      * @param types                    parameter and return value type information, if any is known
   358      * @param invalidatedProgramPoints invalidated program points for recompilation
   359      * @param typeInformationFile      descriptor of the location where type information is persisted
   360      * @param continuationEntryPoints  continuation entry points for restof method
   361      * @param runtimeScope             runtime scope for recompilation type lookup in {@code TypeEvaluator}
   362      */
   363     public Compiler(
   364             final Context context,
   365             final ScriptEnvironment env,
   366             final CodeInstaller<ScriptEnvironment> installer,
   367             final Source source,
   368             final ErrorManager errors,
   369             final boolean isStrict,
   370             final boolean isOnDemand,
   371             final RecompilableScriptFunctionData compiledFunction,
   372             final TypeMap types,
   373             final Map<Integer, Type> invalidatedProgramPoints,
   374             final Object typeInformationFile,
   375             final int[] continuationEntryPoints,
   376             final ScriptObject runtimeScope) {
   377         this.context                  = context;
   378         this.env                      = env;
   379         this.installer                = installer;
   380         this.constantData             = new ConstantData();
   381         this.compileUnits             = CompileUnit.createCompileUnitSet();
   382         this.bytecode                 = new LinkedHashMap<>();
   383         this.log                      = initLogger(context);
   384         this.source                   = source;
   385         this.errors                   = errors;
   386         this.sourceName               = FunctionNode.getSourceName(source);
   387         this.onDemand                 = isOnDemand;
   388         this.compiledFunction         = compiledFunction;
   389         this.types                    = types;
   390         this.invalidatedProgramPoints = invalidatedProgramPoints == null ? new HashMap<Integer, Type>() : invalidatedProgramPoints;
   391         this.typeInformationFile      = typeInformationFile;
   392         this.continuationEntryPoints  = continuationEntryPoints == null ? null: continuationEntryPoints.clone();
   393         this.typeEvaluator            = new TypeEvaluator(this, runtimeScope);
   394         this.firstCompileUnitName     = firstCompileUnitName();
   395         this.strict                   = isStrict;
   397         this.optimistic = env._optimistic_types;
   398     }
   400     private static String safeSourceName(final ScriptEnvironment env, final CodeInstaller<ScriptEnvironment> installer, final Source source) {
   401         String baseName = new File(source.getName()).getName();
   403         final int index = baseName.lastIndexOf(".js");
   404         if (index != -1) {
   405             baseName = baseName.substring(0, index);
   406         }
   408         baseName = baseName.replace('.', '_').replace('-', '_');
   409         if (!env._loader_per_compile) {
   410             baseName = baseName + installer.getUniqueScriptId();
   411         }
   413         final String mangled = NameCodec.encode(baseName);
   414         return mangled != null ? mangled : baseName;
   415     }
   417     private String firstCompileUnitName() {
   418         final StringBuilder sb = new StringBuilder(SCRIPTS_PACKAGE).
   419                 append('/').
   420                 append(CompilerConstants.DEFAULT_SCRIPT_NAME.symbolName()).
   421                 append('$');
   423         if (isOnDemandCompilation()) {
   424             sb.append(RecompilableScriptFunctionData.RECOMPILATION_PREFIX);
   425         }
   427         if (compilationId > 0) {
   428             sb.append(compilationId).append('$');
   429         }
   431         if (types != null && compiledFunction.getFunctionNodeId() > 0) {
   432             sb.append(compiledFunction.getFunctionNodeId());
   433             final Type[] paramTypes = types.getParameterTypes(compiledFunction.getFunctionNodeId());
   434             for (final Type t : paramTypes) {
   435                 sb.append(Type.getShortSignatureDescriptor(t));
   436             }
   437             sb.append('$');
   438         }
   440         sb.append(Compiler.safeSourceName(env, installer, source));
   442         return sb.toString();
   443     }
   445     void declareLocalSymbol(final String symbolName) {
   446         typeEvaluator.declareLocalSymbol(symbolName);
   447     }
   449     void setData(final RecompilableScriptFunctionData data) {
   450         assert this.compiledFunction == null : data;
   451         this.compiledFunction = data;
   452     }
   454     @Override
   455     public DebugLogger getLogger() {
   456         return log;
   457     }
   459     @Override
   460     public DebugLogger initLogger(final Context ctxt) {
   461         final boolean optimisticTypes = ctxt.getEnv()._optimistic_types;
   462         return ctxt.getLogger(this.getClass(), new Consumer<DebugLogger>() {
   463             @Override
   464             public void accept(final DebugLogger newLogger) {
   465                 if (!Compiler.this.getScriptEnvironment()._lazy_compilation) {
   466                     newLogger.warning("WARNING: Running with lazy compilation switched off. This is not a default setting.");
   467                 }
   468                 newLogger.warning("Optimistic types are ", optimisticTypes ? "ENABLED." : "DISABLED.");
   469             }
   470         });
   471     }
   473     ScriptEnvironment getScriptEnvironment() {
   474         return env;
   475     }
   477     boolean isOnDemandCompilation() {
   478         return onDemand;
   479     }
   481     boolean useOptimisticTypes() {
   482         return optimistic;
   483     }
   485     Context getContext() {
   486         return context;
   487     }
   489     Type getOptimisticType(final Optimistic node) {
   490         return typeEvaluator.getOptimisticType(node);
   491     }
   493     /**
   494      * Returns true if the expression can be safely evaluated, and its value is an object known to always use
   495      * String as the type of its property names retrieved through
   496      * {@link ScriptRuntime#toPropertyIterator(Object)}. It is used to avoid optimistic assumptions about its
   497      * property name types.
   498      * @param expr the expression to test
   499      * @return true if the expression can be safely evaluated, and its value is an object known to always use
   500      * String as the type of its property iterators.
   501      */
   502     boolean hasStringPropertyIterator(final Expression expr) {
   503         return typeEvaluator.hasStringPropertyIterator(expr);
   504     }
   506     void addInvalidatedProgramPoint(final int programPoint, final Type type) {
   507         invalidatedProgramPoints.put(programPoint, type);
   508     }
   511     /**
   512      * Returns a copy of this compiler's current mapping of invalidated optimistic program points to their types. The
   513      * copy is not live with regard to changes in state in this compiler instance, and is mutable.
   514      * @return a copy of this compiler's current mapping of invalidated optimistic program points to their types.
   515      */
   516     public Map<Integer, Type> getInvalidatedProgramPoints() {
   517         return invalidatedProgramPoints == null ? null : new TreeMap<>(invalidatedProgramPoints);
   518     }
   520     TypeMap getTypeMap() {
   521         return types;
   522     }
   524     MethodType getCallSiteType(final FunctionNode fn) {
   525         if (types == null || !isOnDemandCompilation()) {
   526             return null;
   527         }
   528         return types.getCallSiteType(fn);
   529     }
   531     Type getParamType(final FunctionNode fn, final int pos) {
   532         return types == null ? null : types.get(fn, pos);
   533     }
   535     /**
   536      * Do a compilation job
   537      *
   538      * @param functionNode function node to compile
   539      * @param phases phases of compilation transforms to apply to function
   541      * @return transformed function
   542      *
   543      * @throws CompilationException if error occurs during compilation
   544      */
   545     public FunctionNode compile(final FunctionNode functionNode, final CompilationPhases phases) throws CompilationException {
   546         if (log.isEnabled()) {
   547             log.info("Starting compile job for ", DebugLogger.quote(functionNode.getName()), " phases=", quote(phases.getDesc()));
   548             log.indent();
   549         }
   551         final String name = DebugLogger.quote(functionNode.getName());
   553         FunctionNode newFunctionNode = functionNode;
   555         for (final String reservedName : RESERVED_NAMES) {
   556             newFunctionNode.uniqueName(reservedName);
   557         }
   559         final boolean info = log.levelFinerThanOrEqual(Level.INFO);
   561         final DebugLogger timeLogger = env.isTimingEnabled() ? env._timing.getLogger() : null;
   563         long time = 0L;
   565         for (final CompilationPhase phase : phases) {
   566             log.fine(phase, " starting for ", name);
   568             try {
   569                 newFunctionNode = phase.apply(this, phases, newFunctionNode);
   570             } catch (final ParserException error) {
   571                 errors.error(error);
   572                 if (env._dump_on_error) {
   573                     error.printStackTrace(env.getErr());
   574                 }
   575                 return null;
   576             }
   578             log.fine(phase, " done for function ", quote(name));
   580             if (env._print_mem_usage) {
   581                 printMemoryUsage(functionNode, phase.toString());
   582             }
   584             time += (env.isTimingEnabled() ? phase.getEndTime() - phase.getStartTime() : 0L);
   585         }
   587         if (typeInformationFile != null && !phases.isRestOfCompilation()) {
   588             OptimisticTypesPersistence.store(typeInformationFile, invalidatedProgramPoints);
   589         }
   591         log.unindent();
   593         if (info) {
   594             final StringBuilder sb = new StringBuilder("Finished compile job for ");
   595             sb.append(newFunctionNode.getSource()).
   596                 append(':').
   597                 append(quote(newFunctionNode.getName()));
   599             if (time > 0L && timeLogger != null) {
   600                 assert env.isTimingEnabled();
   601                 sb.append(" in ").append(time).append(" ms");
   602             }
   603             log.info(sb);
   604         }
   606         return newFunctionNode;
   607     }
   609     Source getSource() {
   610         return source;
   611     }
   613     Map<String, byte[]> getBytecode() {
   614         return Collections.unmodifiableMap(bytecode);
   615     }
   617     /**
   618      * Reset bytecode cache for compiler reuse.
   619      */
   620     void clearBytecode() {
   621         bytecode.clear();
   622     }
   624     CompileUnit getFirstCompileUnit() {
   625         assert !compileUnits.isEmpty();
   626         return compileUnits.iterator().next();
   627     }
   629     Set<CompileUnit> getCompileUnits() {
   630         return compileUnits;
   631     }
   633     ConstantData getConstantData() {
   634         return constantData;
   635     }
   637     CodeInstaller<ScriptEnvironment> getCodeInstaller() {
   638         return installer;
   639     }
   641     void addClass(final String name, final byte[] code) {
   642         bytecode.put(name, code);
   643     }
   645     String nextCompileUnitName() {
   646         final StringBuilder sb = new StringBuilder(COMPILE_UNIT_NAME_BUFFER_SIZE);
   647         sb.append(firstCompileUnitName);
   648         final int cuid = nextCompileUnitId.getAndIncrement();
   649         if (cuid > 0) {
   650             sb.append("$cu").append(cuid);
   651         }
   653         return sb.toString();
   654     }
   656     Map<Integer, FunctionInitializer> functionInitializers;
   658     void addFunctionInitializer(final RecompilableScriptFunctionData functionData, final FunctionNode functionNode) {
   659         if (functionInitializers == null) {
   660             functionInitializers = new HashMap<>();
   661         }
   662         if (!functionInitializers.containsKey(functionData)) {
   663             functionInitializers.put(functionData.getFunctionNodeId(), new FunctionInitializer(functionNode));
   664         }
   665     }
   667     Map<Integer, FunctionInitializer> getFunctionInitializers() {
   668         return functionInitializers;
   669     }
   671     /**
   672      * Persist current compilation with the given {@code cacheKey}.
   673      * @param cacheKey cache key
   674      * @param functionNode function node
   675      */
   676     public void persistClassInfo(final String cacheKey, final FunctionNode functionNode) {
   677         if (cacheKey != null && env._persistent_cache) {
   678             Map<Integer, FunctionInitializer> initializers;
   679             // If this is an on-demand compilation create a function initializer for the function being compiled.
   680             // Otherwise use function initializer map generated by codegen.
   681             if (functionInitializers == null) {
   682                 initializers = new HashMap<>();
   683                 final FunctionInitializer initializer = new FunctionInitializer(functionNode, getInvalidatedProgramPoints());
   684                 initializers.put(functionNode.getId(), initializer);
   685             } else {
   686                 initializers = functionInitializers;
   687             }
   688             final String mainClassName = getFirstCompileUnit().getUnitClassName();
   689             installer.storeScript(cacheKey, source, mainClassName, bytecode, initializers, constantData.toArray(), compilationId);
   690         }
   691     }
   693     /**
   694      * Make sure the next compilation id is greater than {@code value}.
   695      * @param value compilation id value
   696      */
   697     public static void updateCompilationId(final int value) {
   698         if (value >= COMPILATION_ID.get()) {
   699             COMPILATION_ID.set(value + 1);
   700         }
   701     }
   703     CompileUnit addCompileUnit(final long initialWeight) {
   704         final CompileUnit compileUnit = createCompileUnit(initialWeight);
   705         compileUnits.add(compileUnit);
   706         log.fine("Added compile unit ", compileUnit);
   707         return compileUnit;
   708     }
   710     CompileUnit createCompileUnit(final String unitClassName, final long initialWeight) {
   711         final ClassEmitter classEmitter = new ClassEmitter(context, sourceName, unitClassName, isStrict());
   712         final CompileUnit  compileUnit  = new CompileUnit(unitClassName, classEmitter, initialWeight);
   713         classEmitter.begin();
   715         return compileUnit;
   716     }
   718     private CompileUnit createCompileUnit(final long initialWeight) {
   719         return createCompileUnit(nextCompileUnitName(), initialWeight);
   720     }
   722     boolean isStrict() {
   723         return strict;
   724     }
   726     void replaceCompileUnits(final Set<CompileUnit> newUnits) {
   727         compileUnits.clear();
   728         compileUnits.addAll(newUnits);
   729     }
   731     CompileUnit findUnit(final long weight) {
   732         for (final CompileUnit unit : compileUnits) {
   733             if (unit.canHold(weight)) {
   734                 unit.addWeight(weight);
   735                 return unit;
   736             }
   737         }
   739         return addCompileUnit(weight);
   740     }
   742     /**
   743      * Convert a package/class name to a binary name.
   744      *
   745      * @param name Package/class name.
   746      * @return Binary name.
   747      */
   748     public static String binaryName(final String name) {
   749         return name.replace('/', '.');
   750     }
   752     RecompilableScriptFunctionData getScriptFunctionData(final int functionId) {
   753         return compiledFunction == null ? null : compiledFunction.getScriptFunctionData(functionId);
   754     }
   756     boolean isGlobalSymbol(final FunctionNode fn, final String name) {
   757         return getScriptFunctionData(fn.getId()).isGlobalSymbol(fn, name);
   758     }
   760     int[] getContinuationEntryPoints() {
   761         return continuationEntryPoints;
   762     }
   764     Type getInvalidatedProgramPointType(final int programPoint) {
   765         return invalidatedProgramPoints.get(programPoint);
   766     }
   768     private void printMemoryUsage(final FunctionNode functionNode, final String phaseName) {
   769         if (!log.isEnabled()) {
   770             return;
   771         }
   773         log.info(phaseName, "finished. Doing IR size calculation...");
   775         final ObjectSizeCalculator osc = new ObjectSizeCalculator(ObjectSizeCalculator.getEffectiveMemoryLayoutSpecification());
   776         osc.calculateObjectSize(functionNode);
   778         final List<ClassHistogramElement> list      = osc.getClassHistogram();
   779         final StringBuilder               sb        = new StringBuilder();
   780         final long                        totalSize = osc.calculateObjectSize(functionNode);
   782         sb.append(phaseName).
   783             append(" Total size = ").
   784             append(totalSize / 1024 / 1024).
   785             append("MB");
   786         log.info(sb);
   788         Collections.sort(list, new Comparator<ClassHistogramElement>() {
   789             @Override
   790             public int compare(final ClassHistogramElement o1, final ClassHistogramElement o2) {
   791                 final long diff = o1.getBytes() - o2.getBytes();
   792                 if (diff < 0) {
   793                     return 1;
   794                 } else if (diff > 0) {
   795                     return -1;
   796                 } else {
   797                     return 0;
   798                 }
   799             }
   800         });
   801         for (final ClassHistogramElement e : list) {
   802             final String line = String.format("    %-48s %10d bytes (%8d instances)", e.getClazz(), e.getBytes(), e.getInstances());
   803             log.info(line);
   804             if (e.getBytes() < totalSize / 200) {
   805                 log.info("    ...");
   806                 break; // never mind, so little memory anyway
   807             }
   808         }
   809     }
   810 }

mercurial