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 class loading and/or
126       * GC cannot be allowed. Therefore we must use primitive sysWrites for output and avoid string
127       * appends and other allocations.
128       * <p>
129       * FIXME should this method be uninterruptible?
130       *
131       * @param explain Should an explanation of the metrics be generated?
132       */
133      public static void generateBaselineCompilerSubsystemReport(boolean explain) {
134        if (!VM.MeasureCompilationPhases) return;
135    
136        VM.sysWriteln("\n\t\tBaseline Compiler SubSystem");
137        VM.sysWriteln("\tPhase\t\t\t    Time");
138        VM.sysWriteln("\t\t\t\t(ms)    (%ofTotal)");
139    
140        double gcMapTime = Time.nanosToMillis(gcMapNanos);
141        double osrSetupTime = Time.nanosToMillis(osrSetupNanos);
142        double codeGenTime = Time.nanosToMillis(codeGenNanos);
143        double encodingTime = Time.nanosToMillis(encodingNanos);
144        double total = gcMapTime + osrSetupTime + codeGenTime + encodingTime;
145    
146        VM.sysWrite("\tCompute GC Maps\t\t", gcMapTime);
147        VM.sysWriteln("\t", 100 * gcMapTime / total);
148    
149        if (osrSetupTime > 0) {
150          VM.sysWrite("\tOSR setup \t\t", osrSetupTime);
151          VM.sysWriteln("\t", 100 * osrSetupTime / total);
152        }
153    
154        VM.sysWrite("\tCode generation\t\t", codeGenTime);
155        VM.sysWriteln("\t", 100 * codeGenTime / total);
156    
157        VM.sysWrite("\tEncode GC/MC maps\t", encodingTime);
158        VM.sysWriteln("\t", 100 * encodingTime / total);
159    
160        VM.sysWriteln("\tTOTAL\t\t\t", total);
161      }
162    
163      /**
164       * Compile the given method with the baseline compiler.
165       *
166       * @param method the NormalMethod to compile.
167       * @return the generated CompiledMethod for said NormalMethod.
168       */
169      public static CompiledMethod compile(NormalMethod method) {
170        if (VM.VerifyAssertions) VM._assert(!method.getDeclaringClass().hasSaveVolatileAnnotation(), "Baseline compiler doesn't implement SaveVolatile");
171    
172        BaselineCompiledMethod cm =
173            (BaselineCompiledMethod) CompiledMethods.createCompiledMethod(method, CompiledMethod.BASELINE);
174        cm.compile();
175        return cm;
176      }
177    
178      protected abstract void initializeCompiler();
179    
180      /**
181       * Top level driver for baseline compilation of a method.
182       */
183      protected void compile() {
184        if (shouldPrint) printStartHeader(method);
185    
186        // Phase 1: GC map computation
187        long start = 0;
188        ReferenceMaps refMaps;
189        try {
190          if (VM.MeasureCompilationPhases) {
191            start = Time.nanoTime();
192          }
193          refMaps = new ReferenceMaps((BaselineCompiledMethod) compiledMethod, stackHeights, localTypes);
194        } finally {
195          if (VM.MeasureCompilationPhases) {
196            long end = Time.nanoTime();
197            gcMapNanos += end - start;
198          }
199        }
200    
201        /* reference map and stackheights were computed using original bytecodes
202         * and possibly new operand words
203         * recompute the stack height, but keep the operand words of the code
204         * generation consistent with reference map
205         * TODO: revisit this code as part of OSR redesign
206         */
207        // Phase 2: OSR setup\
208        boolean edge_counters = options.PROFILE_EDGE_COUNTERS;
209        try {
210          if (VM.MeasureCompilationPhases) {
211            start = Time.nanoTime();
212          }
213          if (VM.BuildForAdaptiveSystem && method.isForOsrSpecialization()) {
214            options.PROFILE_EDGE_COUNTERS = false;
215            // we already allocated enough space for stackHeights, shift it back first
216            System.arraycopy(stackHeights,
217                             0,
218                             stackHeights,
219                             method.getOsrPrologueLength(),
220                             method.getBytecodeLength());   // NB: getBytecodeLength returns back the length of original bytecodes
221    
222            // compute stack height for prologue
223            new BytecodeTraverser().prologueStackHeights(method, method.getOsrPrologue(), stackHeights);
224          }
225        } finally {
226          if (VM.MeasureCompilationPhases) {
227            long end = Time.nanoTime();
228            osrSetupNanos += end - start;
229          }
230        }
231    
232        // Phase 3: Code generation
233        int[] bcMap;
234        MachineCode machineCode;
235        CodeArray instructions;
236        try {
237          if (VM.MeasureCompilationPhases) {
238            start = Time.nanoTime();
239          }
240    
241          // determine if we are going to insert edge counters for this method
242          if (options.PROFILE_EDGE_COUNTERS &&
243              !method.getDeclaringClass().hasBridgeFromNativeAnnotation() &&
244              (method.hasCondBranch() || method.hasSwitch())) {
245            ((BaselineCompiledMethod) compiledMethod).setHasCounterArray(); // yes, we will inject counters for this method.
246          }
247    
248          //do platform specific tasks before generating code;
249          initializeCompiler();
250    
251          machineCode = genCode();
252          instructions = machineCode.getInstructions();
253          bcMap = machineCode.getBytecodeMap();
254        } finally {
255          if (VM.MeasureCompilationPhases) {
256            long end = Time.nanoTime();
257            codeGenNanos += end - start;
258          }
259        }
260    
261        /* adjust machine code map, and restore original bytecode
262         * for building reference map later.
263         * TODO: revisit this code as part of OSR redesign
264         */
265        // Phase 4: OSR part 2
266        try {
267          if (VM.MeasureCompilationPhases) {
268            start = Time.nanoTime();
269          }
270          if (VM.BuildForAdaptiveSystem && method.isForOsrSpecialization()) {
271            int[] newmap = new int[bcMap.length - method.getOsrPrologueLength()];
272            System.arraycopy(bcMap, method.getOsrPrologueLength(), newmap, 0, newmap.length);
273            machineCode.setBytecodeMap(newmap);
274            bcMap = newmap;
275            // switch back to original state
276            method.finalizeOsrSpecialization();
277            // restore options
278            options.PROFILE_EDGE_COUNTERS = edge_counters;
279          }
280        } finally {
281          if (VM.MeasureCompilationPhases) {
282            long end = Time.nanoTime();
283            osrSetupNanos += end - start;
284          }
285        }
286    
287        // Phase 5: Encode machine code maps
288        try {
289          if (VM.MeasureCompilationPhases) {
290            start = Time.nanoTime();
291          }
292          if (method.isSynchronized()) {
293            ((BaselineCompiledMethod) compiledMethod).setLockAcquisitionOffset(lockOffset);
294          }
295          ((BaselineCompiledMethod) compiledMethod).encodeMappingInfo(refMaps, bcMap);
296          compiledMethod.compileComplete(instructions);
297          if (edgeCounterIdx > 0) {
298            EdgeCounts.allocateCounters(method, edgeCounterIdx);
299          }
300          if (shouldPrint) {
301            ((BaselineCompiledMethod) compiledMethod).printExceptionTable();
302            printEndHeader(method);
303          }
304        } finally {
305          if (VM.MeasureCompilationPhases) {
306            long end = Time.nanoTime();
307            encodingNanos += end - start;
308          }
309        }
310      }
311    
312      @Override
313      protected String getCompilerName() {
314        return "baseline";
315      }
316    }