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 */
013package org.jikesrvm.osr;
014
015import org.jikesrvm.VM;
016import org.jikesrvm.adaptive.controller.ControllerMemory;
017import org.jikesrvm.adaptive.controller.ControllerPlan;
018import org.jikesrvm.classloader.ExceptionHandlerMap;
019import org.jikesrvm.classloader.NormalMethod;
020import org.jikesrvm.compilers.common.CompiledMethod;
021import org.jikesrvm.compilers.common.RuntimeCompiler;
022import org.jikesrvm.compilers.opt.OptOptions;
023import org.jikesrvm.compilers.opt.driver.CompilationPlan;
024import org.jikesrvm.compilers.opt.driver.OptimizationPlanElement;
025import org.jikesrvm.util.Services;
026
027/**
028 * SpecialCompiler is a wrapper for compiling specialized byte code.
029 * It accepts an instance of ExecutionState, generates the specialized
030 * byte code, and compiles it to machine code instructions.
031 */
032public class SpecialCompiler {
033
034  /**
035   * recompile an execution state
036   * @param state a list of execution states
037   * @param invalidate Is this an invalidation?
038   * @return the compiled method for the root state
039   */
040  public static CompiledMethod recompileState(ExecutionState state, boolean invalidate) {
041
042    // compile from callee to caller
043    CompiledMethod newCM = null;
044    do {
045      if (!invalidate) {
046        newCM = optCompile(state);
047      } else {
048        newCM = baselineCompile(state);
049      }
050
051      if (VM.TraceOnStackReplacement) {
052        VM.sysWriteln("new CMID 0x" + Integer.toHexString(newCM.getId()) + "(" + newCM.getId() + ") for " + newCM.getMethod());
053      }
054
055      if (state.callerState == null) break;
056      state = state.callerState;
057      // set callee_cmid of the caller
058      state.callee_cmid = newCM.getId();
059
060    } while (true);
061
062    return newCM;
063  }
064
065  /**
066   * Compiles the method with the baseline compiler.
067   * <ol>
068   *   <li>generate  prologue (PSEUDO_bytecode) from the state.
069   *   <li>make up new byte code with prologue.
070   *   <li>set method's bytecode to the specilizaed byte code.
071   *   <li>call BaselineCompilerImpl.compile,
072   *    the 'compile' method is customized to process pseudo instructions,
073   *    and it will reset the byte code to the original one, and adjust
074   *    the map from bytecode to the generated machine code. then the
075   *    reference map can be generated corrected relying on the original
076   *    bytecode.
077   * </ol>
078   * <p>
079   * NOTE: this is different from optCompile which resets the
080  *    bytecode after compilation. I believe this minimizes the
081  *    work to change both compilers.
082   * @param state the execution state for the compilation
083   * @return the compiled method produced by the baseline compiler
084   */
085  public static CompiledMethod baselineCompile(ExecutionState state) {
086    NormalMethod method = state.getMethod();
087
088    if (VM.TraceOnStackReplacement) {
089      VM.sysWriteln("BASE : starts compiling " + method);
090    }
091
092    /* generate prologue bytes */
093    byte[] prologue = state.generatePrologue();
094
095    if (VM.TraceOnStackReplacement) {
096      VM.sysWriteln("prologue length " + prologue.length);
097    }
098
099    // the compiler will call setForOsrSpecialization after generating the reference map
100    /* set a flag for specialization, compiler will see it, and
101     * know how to do it properly.
102     */
103    method.setForOsrSpecialization(prologue, state.getMaxStackHeight());
104
105    /* for baseline compilation, we do not adjust the exception table and line table
106    * because the compiler will generate maps after compilation.
107    * Any necessary adjustment should be made during the compilation
108    */
109    CompiledMethod newCompiledMethod;
110    if (VM.BuildForIA32) {
111      newCompiledMethod = org.jikesrvm.compilers.baseline.ia32.BaselineCompilerImpl.compile(method);
112    } else {
113      if (VM.VerifyAssertions) VM._assert(VM.BuildForPowerPC);
114      newCompiledMethod = org.jikesrvm.compilers.baseline.ppc.BaselineCompilerImpl.compile(method);
115    }
116
117    // compiled method was already set by BaselineCompilerImpl.compile
118    // the call here does nothing
119//    method.finalizeOsrSpecialization();
120
121    // mark the method is a specialized one
122    newCompiledMethod.setSpecialForOSR();
123
124    if (VM.TraceOnStackReplacement) {
125//        ((BaselineCompiledMethod)newCompiledMethod).printCodeMapEntries();
126      VM.sysWriteln("BASE : done, CMID 0x" +
127                    Integer.toHexString(newCompiledMethod.getId()) +
128                    "(" + newCompiledMethod.getId() + ") JTOC offset " +
129                    Services.addressAsHexString(newCompiledMethod.getOsrJTOCoffset().toWord().toAddress()));
130    }
131
132    return newCompiledMethod;
133  }
134
135  /**
136   * <ol>
137   *   <li>generate prologue PSEUDO_bytecode from the state.
138   *   <li>make new bytecodes with prologue.
139   *   <li>set method's bytecode to specialized one.
140   *   <li>adjust exception map, line number map.
141   *   <li>compile the method.
142   *   <li>restore bytecode, exception, linenumber map to the original one.
143   * </ol>
144   *
145   * @param state the execution state for the compilation
146   * @return the compiled method produced by the optimizing compiler
147   */
148  public static CompiledMethod optCompile(ExecutionState state) {
149
150    NormalMethod method = state.getMethod();
151    if (VM.TraceOnStackReplacement) {
152      VM.sysWriteln("OPT : starts compiling " + method);
153    }
154
155    ControllerPlan latestPlan = ControllerMemory.findLatestPlan(method);
156
157    OptOptions _options = null;
158    if (latestPlan != null) {
159      _options = latestPlan.getCompPlan().options.dup();
160    } else {
161      // no previous compilation plan, a long run loop promoted from baseline.
162      // this only happens when testing, not in real code
163      _options = new OptOptions();
164      _options.setOptLevel(0);
165    }
166    // disable OSR points in specialized method
167    _options.OSR_GUARDED_INLINING = false;
168
169    CompilationPlan compPlan =
170        new CompilationPlan(method,
171                                (OptimizationPlanElement[]) RuntimeCompiler.optimizationPlan,
172                                null,
173                                _options);
174
175    // it is also necessary to recompile the current method
176    // without OSR.
177
178    /* generate prologue bytes */
179    byte[] prologue = state.generatePrologue();
180    int prosize = prologue.length;
181
182    method.setForOsrSpecialization(prologue, state.getMaxStackHeight());
183
184    int[] oldStartPCs = null;
185    int[] oldEndPCs = null;
186    int[] oldHandlerPCs = null;
187
188    /* adjust exception table. */
189    {
190      // if (VM.TraceOnStackReplacement) { VM.sysWrite("OPT adjust exception table.\n"); }
191
192      ExceptionHandlerMap exceptionHandlerMap = method.getExceptionHandlerMap();
193
194      if (exceptionHandlerMap != null) {
195
196        oldStartPCs = exceptionHandlerMap.getStartPC();
197        oldEndPCs = exceptionHandlerMap.getEndPC();
198        oldHandlerPCs = exceptionHandlerMap.getHandlerPC();
199
200        int n = oldStartPCs.length;
201
202        int[] newStartPCs = new int[n];
203        System.arraycopy(oldStartPCs, 0, newStartPCs, 0, n);
204        exceptionHandlerMap.setStartPC(newStartPCs);
205
206        int[] newEndPCs = new int[n];
207        System.arraycopy(oldEndPCs, 0, newEndPCs, 0, n);
208        exceptionHandlerMap.setEndPC(newEndPCs);
209
210        int[] newHandlerPCs = new int[n];
211        System.arraycopy(oldHandlerPCs, 0, newHandlerPCs, 0, n);
212        exceptionHandlerMap.setHandlerPC(newHandlerPCs);
213
214        for (int i = 0; i < n; i++) {
215          newStartPCs[i] += prosize;
216          newEndPCs[i] += prosize;
217          newHandlerPCs[i] += prosize;
218        }
219      }
220    }
221
222    CompiledMethod newCompiledMethod = RuntimeCompiler.recompileWithOptOnStackSpecialization(compPlan);
223
224    // restore original bytecode, exception table, and line number table
225    method.finalizeOsrSpecialization();
226
227    {
228      ExceptionHandlerMap exceptionHandlerMap = method.getExceptionHandlerMap();
229
230      if (exceptionHandlerMap != null) {
231        exceptionHandlerMap.setStartPC(oldStartPCs);
232        exceptionHandlerMap.setEndPC(oldEndPCs);
233        exceptionHandlerMap.setHandlerPC(oldHandlerPCs);
234      }
235    }
236
237    // compilation failed because compilation is in progress,
238    // reverse back to the baseline
239    if (newCompiledMethod == null) {
240      if (VM.TraceOnStackReplacement) {
241        VM.sysWriteln("OPT : fialed, because compilation in progress, " + "fall back to baseline");
242      }
243      return baselineCompile(state);
244    }
245
246    // mark the method is a specialized one
247    newCompiledMethod.setSpecialForOSR();
248
249    if (VM.TraceOnStackReplacement) VM.sysWriteln("OPT : done\n");
250
251    return newCompiledMethod;
252  }
253}