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 }