/* * This file is part of the Jikes RVM project (http://jikesrvm.org). * * This file is licensed to You under the Common Public License (CPL); * You may not use this file except in compliance with the License. You * may obtain a copy of the License at * * http://www.opensource.org/licenses/cpl1.0.php * * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. */ package org.jikesrvm.compilers.baseline; import org.jikesrvm.ArchitectureSpecific; import org.jikesrvm.SubordinateArchitecture; import org.jikesrvm.VM_CodeArray; import org.jikesrvm.VM_MachineCode; import org.jikesrvm.VM; import org.jikesrvm.classloader.VM_NormalMethod; import org.jikesrvm.compilers.common.VM_CompiledMethod; import org.jikesrvm.compilers.common.VM_CompiledMethods; import org.jikesrvm.osr.OSR_BytecodeTraverser; import org.jikesrvm.runtime.VM_Time; import org.jikesrvm.scheduler.VM_Scheduler; import org.vmmagic.unboxed.Offset; /** * Baseline compiler - platform independent code. * Platform dependent versions extend this class and define * the host of abstract methods defined by VM_CompilerFramework to complete * the implementation of a baseline compiler for a particular target, */ public abstract class VM_BaselineCompiler extends VM_CompilerFramework { private static long gcMapNanos; private static long osrSetupNanos; private static long codeGenNanos; private static long encodingNanos; /** * Options used during base compiler execution */ public static VM_BaselineOptions options; /** * Next edge counter entry to allocate */ protected int edgeCounterIdx; protected final Offset getEdgeCounterOffset() { return Offset.fromIntZeroExtend(method.getId() << LOG_BYTES_IN_ADDRESS); } /** * The types that locals can take. * There are two types of locals. First the parameters of the method, they only have one type * Second, the other locals, numbers get reused when stack shrinks and grows again. * Therefore, these can have more than one type assigned. * The compiler can use this information to assign registers to locals * See the VM_Compiler constructor. */ protected final byte[] localTypes; /** * Construct a VM_Compiler */ protected VM_BaselineCompiler(VM_BaselineCompiledMethod cm) { super(cm); shouldPrint = (!VM.runningTool && (options.PRINT_MACHINECODE) && (!options.hasMETHOD_TO_PRINT() || options.fuzzyMatchMETHOD_TO_PRINT(method.toString()))); if (!VM.runningTool && options.PRINT_METHOD) printMethodMessage(); if (shouldPrint && VM.runningVM && !fullyBootedVM) { shouldPrint = false; if (options.PRINT_METHOD) { VM.sysWriteln("\ttoo early in VM.boot() to print machine code"); } } if (!this.isSubArchComp) { abstractAsm = new ArchitectureSpecific.VM_Assembler( bcodes.length(), shouldPrint, (ArchitectureSpecific.VM_Compiler) this); } else { abstractAsm = new SubordinateArchitecture.VM_Assembler( bcodes.length(), shouldPrint, (SubordinateArchitecture.VM_Compiler) this); } localTypes = new byte[method.getLocalWords()]; } /** * Clear out crud from bootimage writing */ public static void initOptions() { options = new VM_BaselineOptions(); } /** * Now that VM is fully booted, enable options * such as PRINT_MACHINE_CODE that require a fully booted VM. */ public static void fullyBootedVM() { // If the user has requested machine code dumps, then force a test // of method to print option so extra classes needed to process // matching will be loaded and compiled upfront. Thus avoiding getting // stuck looping by just asking if we have a match in the middle of // compilation. Pick an obsure string for the check. if (options.hasMETHOD_TO_PRINT() && options.fuzzyMatchMETHOD_TO_PRINT("???")) { VM.sysWrite("??? is not a sensible string to specify for method name"); } if (!VM.BuildForAdaptiveSystem && options.PRELOAD_CLASS != null) { VM.sysWrite("Option preload_class should only be used when the optimizing compiler is the runtime"); VM.sysWrite(" compiler or in an adaptive system\n"); VM.sysExit(VM.EXIT_STATUS_BOGUS_COMMAND_LINE_ARG); } fullyBootedVM = true; } /** * Process a command line argument * @param prefix * @param arg Command line argument with prefix stripped off */ public static void processCommandLineArg(String prefix, String arg) { if (!options.processAsOption(prefix, arg)) { VM.sysWrite("VM_BaselineCompiler: Unrecognized argument \"" + arg + "\"\n"); VM.sysExit(VM.EXIT_STATUS_BOGUS_COMMAND_LINE_ARG); } } /** * Generate a report of time spent in various phases of the baseline compiler. * <p> NB: This method may be called in a context where classloading and/or * GC cannot be allowed. * Therefore we must use primitive sysWrites for output and avoid string * appends and other allocations. * * @param explain Should an explanation of the metrics be generated? */ public static void generateBaselineCompilerSubsystemReport(boolean explain) { if (!VM.MeasureCompilationPhases) return; VM.sysWriteln("\n\t\tBaseline Compiler SubSystem"); VM.sysWriteln("\tPhase\t\t\t Time"); VM.sysWriteln("\t\t\t\t(ms) (%ofTotal)"); double gcMapTime = VM_Time.nanosToMillis(gcMapNanos); double osrSetupTime = VM_Time.nanosToMillis(osrSetupNanos); double codeGenTime = VM_Time.nanosToMillis(codeGenNanos); double encodingTime = VM_Time.nanosToMillis(encodingNanos); double total = gcMapTime + osrSetupTime + codeGenTime + encodingTime; VM.sysWrite("\tCompute GC Maps\t\t", gcMapTime); VM.sysWriteln("\t", 100 * gcMapTime / total); if (osrSetupTime > 0) { VM.sysWrite("\tOSR setup \t\t", osrSetupTime); VM.sysWriteln("\t", 100 * osrSetupTime / total); } VM.sysWrite("\tCode generation\t\t", codeGenTime); VM.sysWriteln("\t", 100 * codeGenTime / total); VM.sysWrite("\tEncode GC/MC maps\t", encodingTime); VM.sysWriteln("\t", 100 * encodingTime / total); VM.sysWriteln("\tTOTAL\t\t\t", total); } /** * Compile the given method with the baseline compiler. * * @param method the VM_NormalMethod to compile. * @return the generated VM_CompiledMethod for said VM_NormalMethod. */ public static VM_CompiledMethod compile(VM_NormalMethod method, boolean forSubArch) { VM_BaselineCompiledMethod cm = (VM_BaselineCompiledMethod) VM_CompiledMethods.createCompiledMethod(method, VM_CompiledMethod.BASELINE, forSubArch); cm.compile(); return cm; } protected abstract void initializeCompiler(); /** * Top level driver for baseline compilation of a method. */ protected void compile() { if (shouldPrint) printStartHeader(method); // Phase 1: GC map computation long start = 0; VM_ReferenceMaps refMaps; try { if (VM.MeasureCompilationPhases) { start = VM_Scheduler.getCurrentThread().startTimedInterval(); } refMaps = new VM_ReferenceMaps((VM_BaselineCompiledMethod) compiledMethod, stackHeights, localTypes); } finally { if (VM.MeasureCompilationPhases) { long end = VM_Scheduler.getCurrentThread().endTimedInterval(); gcMapNanos += end - start; } } /* reference map and stackheights were computed using original bytecodes * and possibly new operand words * recompute the stack height, but keep the operand words of the code * generation consistant with reference map * TODO: revist this code as part of OSR redesign */ // Phase 2: OSR setup\ boolean edge_counters = options.EDGE_COUNTERS; try { if (VM.MeasureCompilationPhases) { start = VM_Scheduler.getCurrentThread().startTimedInterval(); } if (VM.BuildForAdaptiveSystem && method.isForOsrSpecialization()) { options.EDGE_COUNTERS = false; // we already allocated enough space for stackHeights, shift it back first System.arraycopy(stackHeights, 0, stackHeights, method.getOsrPrologueLength(), method.getBytecodeLength()); // NB: getBytecodeLength returns back the length of original bytecodes // compute stack height for prologue new OSR_BytecodeTraverser().prologueStackHeights(method, method.getOsrPrologue(), stackHeights); } } finally { if (VM.MeasureCompilationPhases) { long end = VM_Scheduler.getCurrentThread().endTimedInterval(); osrSetupNanos += end - start; } } // Phase 3: Code gen int[] bcMap; VM_MachineCode machineCode; VM_CodeArray instructions; try { if (VM.MeasureCompilationPhases) { start = VM_Scheduler.getCurrentThread().startTimedInterval(); } // determine if we are going to insert edge counters for this method if (options .EDGE_COUNTERS && !method.getDeclaringClass().hasBridgeFromNativeAnnotation() && (method.hasCondBranch() || method.hasSwitch())) { ((VM_BaselineCompiledMethod) compiledMethod).setHasCounterArray(); // yes, we will inject counters for this method. } //do platform specific tasks before generating code; initializeCompiler(); machineCode = genCode(); instructions = machineCode.getInstructions(); bcMap = machineCode.getBytecodeMap(); } finally { if (VM.MeasureCompilationPhases) { long end = VM_Scheduler.getCurrentThread().endTimedInterval(); codeGenNanos += end - start; } } /* adjust machine code map, and restore original bytecode * for building reference map later. * TODO: revisit this code as part of OSR redesign */ // Phase 4: OSR part 2 try { if (VM.MeasureCompilationPhases) { start = VM_Scheduler.getCurrentThread().startTimedInterval(); } if (VM.BuildForAdaptiveSystem && method.isForOsrSpecialization()) { int[] newmap = new int[bcMap.length - method.getOsrPrologueLength()]; System.arraycopy(bcMap, method.getOsrPrologueLength(), newmap, 0, newmap.length); machineCode.setBytecodeMap(newmap); bcMap = newmap; // switch back to original state method.finalizeOsrSpecialization(); // restore options options.EDGE_COUNTERS = edge_counters; } } finally { if (VM.MeasureCompilationPhases) { long end = VM_Scheduler.getCurrentThread().endTimedInterval(); osrSetupNanos += end - start; } } // Phase 5: Encode machine code maps try { if (VM.MeasureCompilationPhases) { start = VM_Scheduler.getCurrentThread().startTimedInterval(); } if (method.isSynchronized()) { ((VM_BaselineCompiledMethod) compiledMethod).setLockAcquisitionOffset(lockOffset); } ((VM_BaselineCompiledMethod) compiledMethod).encodeMappingInfo(refMaps, bcMap, instructions.length(), this.isSubArchComp); compiledMethod.compileComplete(instructions); if (edgeCounterIdx > 0) { VM_EdgeCounts.allocateCounters(method, edgeCounterIdx); } if (shouldPrint) { ((VM_BaselineCompiledMethod) compiledMethod).printExceptionTable(); printEndHeader(method); } } finally { if (VM.MeasureCompilationPhases) { long end = VM_Scheduler.getCurrentThread().endTimedInterval(); encodingNanos += end - start; } } } /** * Compile a stub method (method should never be invoked on this architecture) */ protected void compileStub() { if (shouldPrint) printStartHeader(method); // Phase 3: Code gen int[] bcMap; VM_MachineCode machineCode; VM_CodeArray instructions; VM_ReferenceMaps refMaps; refMaps = new VM_ReferenceMaps((VM_BaselineCompiledMethod) compiledMethod, stackHeights, localTypes); //do platform specific tasks before generating code; initializeCompiler(); machineCode = genStub(); instructions = machineCode.getInstructions(); bcMap = machineCode.getBytecodeMap(); ((VM_BaselineCompiledMethod) compiledMethod).encodeMappingInfo(refMaps, bcMap, instructions.length(), this.isSubArchComp); compiledMethod.compileComplete(instructions); printEndHeader(method); } protected String getCompilerName() { return "baseline"; } }