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.osr;
014    
015    import org.jikesrvm.ArchitectureSpecific.BaselineCompilerImpl;
016    import org.jikesrvm.VM;
017    import org.jikesrvm.adaptive.controller.ControllerMemory;
018    import org.jikesrvm.adaptive.controller.ControllerPlan;
019    import org.jikesrvm.classloader.ExceptionHandlerMap;
020    import org.jikesrvm.classloader.NormalMethod;
021    import org.jikesrvm.compilers.common.CompiledMethod;
022    import org.jikesrvm.compilers.common.RuntimeCompiler;
023    import org.jikesrvm.compilers.opt.OptOptions;
024    import org.jikesrvm.compilers.opt.driver.CompilationPlan;
025    import org.jikesrvm.compilers.opt.driver.OptimizationPlanElement;
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     */
032    public 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
083       * @return a BaselineCompiledMethod
084       */
085      public static CompiledMethod baselineCompile(ExecutionState state) {
086        NormalMethod method = state.getMethod();
087    
088        if (VM.TraceOnStackReplacement) {VM.sysWriteln("BASE : starts compiling " + method); }
089    
090        /* generate prologue bytes */
091        byte[] prologue = state.generatePrologue();
092    
093        if (VM.TraceOnStackReplacement) {VM.sysWriteln("prologue length " + prologue.length);}
094    
095        // the compiler will call setForOsrSpecialization after generating the reference map
096        /* set a flag for specialization, compiler will see it, and
097         * know how to do it properly.
098         */
099        method.setForOsrSpecialization(prologue, state.getMaxStackHeight());
100    
101        /* for baseline compilation, we do not adjust the exception table and line table
102        * because the compiler will generate maps after compilation.
103        * Any necessary adjustment should be made during the compilation
104        */
105        CompiledMethod newCompiledMethod = BaselineCompilerImpl.compile(method);
106    
107        // compiled method was already set by BaselineCompilerImpl.compile
108        // the call here does nothing
109    //    method.finalizeOsrSpecialization();
110    
111        // mark the method is a specialized one
112        newCompiledMethod.setSpecialForOSR();
113    
114        if (VM.TraceOnStackReplacement) {
115    //        ((BaselineCompiledMethod)newCompiledMethod).printCodeMapEntries();
116          VM.sysWriteln("BASE : done, CMID 0x" +
117                        Integer.toHexString(newCompiledMethod.getId()) +
118                        "(" + newCompiledMethod.getId() + ") JTOC offset " +
119                        VM.addressAsHexString(newCompiledMethod.getOsrJTOCoffset().toWord().toAddress()));
120        }
121    
122        return newCompiledMethod;
123      }
124    
125      /**
126       * <ol>
127       *   <li>generate prologue PSEUDO_bytecode from the state.
128       *   <li>make new bytecodes with prologue.
129       *   <li>set method's bytecode to specialized one.
130       *   <li>adjust exception map, line number map.
131       *   <li>compile the method.
132       *   <li>restore bytecode, exception, linenumber map to the original one.
133       * </ol>
134       */
135      public static CompiledMethod optCompile(ExecutionState state) {
136    
137        NormalMethod method = state.getMethod();
138        if (VM.TraceOnStackReplacement) { VM.sysWriteln("OPT : starts compiling " + method); }
139    
140        ControllerPlan latestPlan = ControllerMemory.findLatestPlan(method);
141    
142        OptOptions _options = null;
143        if (latestPlan != null) {
144          _options = latestPlan.getCompPlan().options.dup();
145        } else {
146          // no previous compilation plan, a long run loop promoted from baseline.
147          // this only happens when testing, not in real code
148          _options = new OptOptions();
149          _options.setOptLevel(0);
150        }
151        // disable OSR points in specialized method
152        _options.OSR_GUARDED_INLINING = false;
153    
154        CompilationPlan compPlan =
155            new CompilationPlan(method,
156                                    (OptimizationPlanElement[]) RuntimeCompiler.optimizationPlan,
157                                    null,
158                                    _options);
159    
160        // it is also necessary to recompile the current method
161        // without OSR.
162    
163        /* generate prologue bytes */
164        byte[] prologue = state.generatePrologue();
165        int prosize = prologue.length;
166    
167        method.setForOsrSpecialization(prologue, state.getMaxStackHeight());
168    
169        int[] oldStartPCs = null;
170        int[] oldEndPCs = null;
171        int[] oldHandlerPCs = null;
172    
173        /* adjust exception table. */
174        {
175          // if (VM.TraceOnStackReplacement) { VM.sysWrite("OPT adjust exception table.\n"); }
176    
177          ExceptionHandlerMap exceptionHandlerMap = method.getExceptionHandlerMap();
178    
179          if (exceptionHandlerMap != null) {
180    
181            oldStartPCs = exceptionHandlerMap.getStartPC();
182            oldEndPCs = exceptionHandlerMap.getEndPC();
183            oldHandlerPCs = exceptionHandlerMap.getHandlerPC();
184    
185            int n = oldStartPCs.length;
186    
187            int[] newStartPCs = new int[n];
188            System.arraycopy(oldStartPCs, 0, newStartPCs, 0, n);
189            exceptionHandlerMap.setStartPC(newStartPCs);
190    
191            int[] newEndPCs = new int[n];
192            System.arraycopy(oldEndPCs, 0, newEndPCs, 0, n);
193            exceptionHandlerMap.setEndPC(newEndPCs);
194    
195            int[] newHandlerPCs = new int[n];
196            System.arraycopy(oldHandlerPCs, 0, newHandlerPCs, 0, n);
197            exceptionHandlerMap.setHandlerPC(newHandlerPCs);
198    
199            for (int i = 0; i < n; i++) {
200              newStartPCs[i] += prosize;
201              newEndPCs[i] += prosize;
202              newHandlerPCs[i] += prosize;
203            }
204          }
205        }
206    
207        CompiledMethod newCompiledMethod = RuntimeCompiler.recompileWithOptOnStackSpecialization(compPlan);
208    
209        // restore original bytecode, exception table, and line number table
210        method.finalizeOsrSpecialization();
211    
212        {
213          ExceptionHandlerMap exceptionHandlerMap = method.getExceptionHandlerMap();
214    
215          if (exceptionHandlerMap != null) {
216            exceptionHandlerMap.setStartPC(oldStartPCs);
217            exceptionHandlerMap.setEndPC(oldEndPCs);
218            exceptionHandlerMap.setHandlerPC(oldHandlerPCs);
219          }
220        }
221    
222        // compilation failed because compilation is in progress,
223        // reverse back to the baseline
224        if (newCompiledMethod == null) {
225          if (VM.TraceOnStackReplacement) {
226            VM.sysWriteln("OPT : fialed, because compilation in progress, " + "fall back to baseline");
227          }
228          return baselineCompile(state);
229        }
230    
231        // mark the method is a specialized one
232        newCompiledMethod.setSpecialForOSR();
233    
234        if (VM.TraceOnStackReplacement) VM.sysWriteln("OPT : done\n");
235    
236        return newCompiledMethod;
237      }
238    }