001    /*
002     *  This file is part of the Jikes RVM project (http://jikesrvm.org).
003     *
004     *  This file is licensed to You under the Eclipse Public License (EPL);
005     *  You may not use this file except in compliance with the License. You
006     *  may obtain a copy of the License at
007     *
008     *      http://www.opensource.org/licenses/eclipse-1.0.php
009     *
010     *  See the COPYRIGHT.txt file distributed with this work for information
011     *  regarding copyright ownership.
012     */
013    package org.jikesrvm.compilers.baseline;
014    
015    import org.jikesrvm.ArchitectureSpecific.Assembler;
016    import org.jikesrvm.ArchitectureSpecific.CodeArray;
017    import org.jikesrvm.ArchitectureSpecific.BaselineCompilerImpl;
018    import org.jikesrvm.ArchitectureSpecific.MachineCode;
019    import org.jikesrvm.VM;
020    import org.jikesrvm.classloader.NormalMethod;
021    import org.jikesrvm.compilers.common.CompiledMethod;
022    import org.jikesrvm.compilers.common.CompiledMethods;
023    import org.jikesrvm.osr.BytecodeTraverser;
024    import org.jikesrvm.runtime.Time;
025    import org.vmmagic.unboxed.Offset;
026    
027    /**
028     * Baseline compiler - platform independent code.
029     * Platform dependent versions extend this class and define
030     * the host of abstract methods defined by TemplateCompilerFramework to complete
031     * the implementation of a baseline compiler for a particular target,
032     */
033    public abstract class BaselineCompiler extends TemplateCompilerFramework {
034    
035      private static long gcMapNanos;
036      private static long osrSetupNanos;
037      private static long codeGenNanos;
038      private static long encodingNanos;
039    
040      /**
041       * Options used during base compiler execution
042       */
043      public static BaselineOptions options;
044    
045      /**
046       * Next edge counter entry to allocate
047       */
048      protected int edgeCounterIdx;
049    
050      protected final Offset getEdgeCounterOffset() {
051        return Offset.fromIntZeroExtend(method.getId() << LOG_BYTES_IN_ADDRESS);
052      }
053    
054      protected final int getEdgeCounterIndex() {
055        return method.getId();
056      }
057    
058      /**
059       * The types that locals can take.
060       * There are two types of locals. First the parameters of the method, they only have one type
061       * Second, the other locals, numbers get reused when stack shrinks and grows again.
062       * Therefore, these can have more than one type assigned.
063       * The compiler can use this information to assign registers to locals
064       * See the BaselineCompilerImpl constructor.
065       */
066      protected final byte[] localTypes;
067    
068      /**
069       * Construct a BaselineCompilerImpl
070       */
071      protected BaselineCompiler(BaselineCompiledMethod cm) {
072        super(cm);
073        shouldPrint =
074            (!VM.runningTool &&
075             (options.PRINT_MACHINECODE) &&
076             (!options.hasMETHOD_TO_PRINT() || options.fuzzyMatchMETHOD_TO_PRINT(method.toString())));
077        if (!VM.runningTool && options.PRINT_METHOD) printMethodMessage();
078        if (shouldPrint && VM.runningVM && !fullyBootedVM) {
079          shouldPrint = false;
080          if (options.PRINT_METHOD) {
081            VM.sysWriteln("\ttoo early in VM.boot() to print machine code");
082          }
083        }
084        asm = new Assembler(bcodes.length(), shouldPrint, (BaselineCompilerImpl) this);
085        localTypes = new byte[method.getLocalWords()];
086      }
087    
088      /**
089       * Clear out crud from bootimage writing
090       */
091      public static void initOptions() {
092        options = new BaselineOptions();
093      }
094    
095      /**
096       * Now that VM is fully booted, enable options
097       * such as PRINT_MACHINE_CODE that require a fully booted VM.
098       */
099      public static void fullyBootedVM() {
100        // If the user has requested machine code dumps, then force a test
101        // of method to print option so extra classes needed to process
102        // matching will be loaded and compiled upfront. Thus avoiding getting
103        // stuck looping by just asking if we have a match in the middle of
104        // compilation. Pick an obscure string for the check.
105        if (options.hasMETHOD_TO_PRINT() && options.fuzzyMatchMETHOD_TO_PRINT("???")) {
106          VM.sysWrite("??? is not a sensible string to specify for method name");
107        }
108        fullyBootedVM = true;
109      }
110    
111      /**
112       * Process a command line argument
113       * @param prefix
114       * @param arg     Command line argument with prefix stripped off
115       */
116      public static void processCommandLineArg(String prefix, String arg) {
117        if (!options.processAsOption(prefix, arg)) {
118          VM.sysWrite("BaselineCompiler: Unrecognized argument \"" + arg + "\"\n");
119          VM.sysExit(VM.EXIT_STATUS_BOGUS_COMMAND_LINE_ARG);
120        }
121      }
122    
123      /**
124       * Generate a report of time spent in various phases of the baseline compiler.
125       * <p> NB: This method may be called in a context where classloading and/or
126       * GC cannot be allowed.
127       * Therefore we must use primitive sysWrites for output and avoid string
128       * appends and other allocations.
129       *
130       * @param explain Should an explanation of the metrics be generated?
131       */
132      public static void generateBaselineCompilerSubsystemReport(boolean explain) {
133        if (!VM.MeasureCompilationPhases) return;
134    
135        VM.sysWriteln("\n\t\tBaseline Compiler SubSystem");
136        VM.sysWriteln("\tPhase\t\t\t    Time");
137        VM.sysWriteln("\t\t\t\t(ms)    (%ofTotal)");
138    
139        double gcMapTime = Time.nanosToMillis(gcMapNanos);
140        double osrSetupTime = Time.nanosToMillis(osrSetupNanos);
141        double codeGenTime = Time.nanosToMillis(codeGenNanos);
142        double encodingTime = Time.nanosToMillis(encodingNanos);
143        double total = gcMapTime + osrSetupTime + codeGenTime + encodingTime;
144    
145        VM.sysWrite("\tCompute GC Maps\t\t", gcMapTime);
146        VM.sysWriteln("\t", 100 * gcMapTime / total);
147    
148        if (osrSetupTime > 0) {
149          VM.sysWrite("\tOSR setup \t\t", osrSetupTime);
150          VM.sysWriteln("\t", 100 * osrSetupTime / total);
151        }
152    
153        VM.sysWrite("\tCode generation\t\t", codeGenTime);
154        VM.sysWriteln("\t", 100 * codeGenTime / total);
155    
156        VM.sysWrite("\tEncode GC/MC maps\t", encodingTime);
157        VM.sysWriteln("\t", 100 * encodingTime / total);
158    
159        VM.sysWriteln("\tTOTAL\t\t\t", total);
160      }
161    
162      /**
163       * Compile the given method with the baseline compiler.
164       *
165       * @param method the NormalMethod to compile.
166       * @return the generated CompiledMethod for said NormalMethod.
167       */
168      public static CompiledMethod compile(NormalMethod method) {
169        if (VM.VerifyAssertions) VM._assert(!method.getDeclaringClass().hasSaveVolatileAnnotation(), "Baseline compiler doesn't implement SaveVolatile");
170    
171        BaselineCompiledMethod cm =
172            (BaselineCompiledMethod) CompiledMethods.createCompiledMethod(method, CompiledMethod.BASELINE);
173        cm.compile();
174        return cm;
175      }
176    
177      protected abstract void initializeCompiler();
178    
179      /**
180       * Top level driver for baseline compilation of a method.
181       */
182      protected void compile() {
183        if (shouldPrint) printStartHeader(method);
184    
185        // Phase 1: GC map computation
186        long start = 0;
187        ReferenceMaps refMaps;
188        try {
189          if (VM.MeasureCompilationPhases) {
190            start = Time.nanoTime();
191          }
192          refMaps = new ReferenceMaps((BaselineCompiledMethod) compiledMethod, stackHeights, localTypes);
193        } finally {
194          if (VM.MeasureCompilationPhases) {
195            long end = Time.nanoTime();
196            gcMapNanos += end - start;
197          }
198        }
199    
200        /* reference map and stackheights were computed using original bytecodes
201         * and possibly new operand words
202         * recompute the stack height, but keep the operand words of the code
203         * generation consistent with reference map
204         * TODO: revisit this code as part of OSR redesign
205         */
206        // Phase 2: OSR setup\
207        boolean edge_counters = options.PROFILE_EDGE_COUNTERS;
208        try {
209          if (VM.MeasureCompilationPhases) {
210            start = Time.nanoTime();
211          }
212          if (VM.BuildForAdaptiveSystem && method.isForOsrSpecialization()) {
213            options.PROFILE_EDGE_COUNTERS = false;
214            // we already allocated enough space for stackHeights, shift it back first
215            System.arraycopy(stackHeights,
216                             0,
217                             stackHeights,
218                             method.getOsrPrologueLength(),
219                             method.getBytecodeLength());   // NB: getBytecodeLength returns back the length of original bytecodes
220    
221            // compute stack height for prologue
222            new BytecodeTraverser().prologueStackHeights(method, method.getOsrPrologue(), stackHeights);
223          }
224        } finally {
225          if (VM.MeasureCompilationPhases) {
226            long end = Time.nanoTime();
227            osrSetupNanos += end - start;
228          }
229        }
230    
231        // Phase 3: Code gen
232        int[] bcMap;
233        MachineCode machineCode;
234        CodeArray instructions;
235        try {
236          if (VM.MeasureCompilationPhases) {
237            start = Time.nanoTime();
238          }
239    
240          // determine if we are going to insert edge counters for this method
241          if (options.PROFILE_EDGE_COUNTERS &&
242              !method.getDeclaringClass().hasBridgeFromNativeAnnotation() &&
243              (method.hasCondBranch() || method.hasSwitch())) {
244            ((BaselineCompiledMethod) compiledMethod).setHasCounterArray(); // yes, we will inject counters for this method.
245          }
246    
247          //do platform specific tasks before generating code;
248          initializeCompiler();
249    
250          machineCode = genCode();
251          instructions = machineCode.getInstructions();
252          bcMap = machineCode.getBytecodeMap();
253        } finally {
254          if (VM.MeasureCompilationPhases) {
255            long end = Time.nanoTime();
256            codeGenNanos += end - start;
257          }
258        }
259    
260        /* adjust machine code map, and restore original bytecode
261         * for building reference map later.
262         * TODO: revisit this code as part of OSR redesign
263         */
264        // Phase 4: OSR part 2
265        try {
266          if (VM.MeasureCompilationPhases) {
267            start = Time.nanoTime();
268          }
269          if (VM.BuildForAdaptiveSystem && method.isForOsrSpecialization()) {
270            int[] newmap = new int[bcMap.length - method.getOsrPrologueLength()];
271            System.arraycopy(bcMap, method.getOsrPrologueLength(), newmap, 0, newmap.length);
272            machineCode.setBytecodeMap(newmap);
273            bcMap = newmap;
274            // switch back to original state
275            method.finalizeOsrSpecialization();
276            // restore options
277            options.PROFILE_EDGE_COUNTERS = edge_counters;
278          }
279        } finally {
280          if (VM.MeasureCompilationPhases) {
281            long end = Time.nanoTime();
282            osrSetupNanos += end - start;
283          }
284        }
285    
286        // Phase 5: Encode machine code maps
287        try {
288          if (VM.MeasureCompilationPhases) {
289            start = Time.nanoTime();
290          }
291          if (method.isSynchronized()) {
292            ((BaselineCompiledMethod) compiledMethod).setLockAcquisitionOffset(lockOffset);
293          }
294          ((BaselineCompiledMethod) compiledMethod).encodeMappingInfo(refMaps, bcMap);
295          compiledMethod.compileComplete(instructions);
296          if (edgeCounterIdx > 0) {
297            EdgeCounts.allocateCounters(method, edgeCounterIdx);
298          }
299          if (shouldPrint) {
300            ((BaselineCompiledMethod) compiledMethod).printExceptionTable();
301            printEndHeader(method);
302          }
303        } finally {
304          if (VM.MeasureCompilationPhases) {
305            long end = Time.nanoTime();
306            encodingNanos += end - start;
307          }
308        }
309      }
310    
311      protected String getCompilerName() {
312        return "baseline";
313      }
314    }