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.ia32;
014
015import static org.jikesrvm.classloader.ClassLoaderConstants.ArrayTypeCode;
016import static org.jikesrvm.classloader.ClassLoaderConstants.BooleanTypeCode;
017import static org.jikesrvm.classloader.ClassLoaderConstants.ByteTypeCode;
018import static org.jikesrvm.classloader.ClassLoaderConstants.CharTypeCode;
019import static org.jikesrvm.classloader.ClassLoaderConstants.ClassTypeCode;
020import static org.jikesrvm.classloader.ClassLoaderConstants.DoubleTypeCode;
021import static org.jikesrvm.classloader.ClassLoaderConstants.FloatTypeCode;
022import static org.jikesrvm.classloader.ClassLoaderConstants.IntTypeCode;
023import static org.jikesrvm.classloader.ClassLoaderConstants.LongTypeCode;
024import static org.jikesrvm.classloader.ClassLoaderConstants.ShortTypeCode;
025import static org.jikesrvm.classloader.ClassLoaderConstants.VoidTypeCode;
026import static org.jikesrvm.ia32.RegisterConstants.INSTRUCTION_WIDTH;
027import static org.jikesrvm.ia32.RegisterConstants.LG_INSTRUCTION_WIDTH;
028import static org.jikesrvm.ia32.StackframeLayoutConstants.BYTES_IN_STACKSLOT;
029import static org.jikesrvm.ia32.StackframeLayoutConstants.STACKFRAME_METHOD_ID_OFFSET;
030import static org.jikesrvm.ia32.StackframeLayoutConstants.STACKFRAME_RETURN_ADDRESS_OFFSET;
031import static org.jikesrvm.osr.OSRConstants.DOUBLE;
032import static org.jikesrvm.osr.OSRConstants.FLOAT;
033import static org.jikesrvm.osr.OSRConstants.INT;
034import static org.jikesrvm.osr.OSRConstants.LOCAL;
035import static org.jikesrvm.osr.OSRConstants.LONG;
036import static org.jikesrvm.osr.OSRConstants.REF;
037import static org.jikesrvm.osr.OSRConstants.RET_ADDR;
038import static org.jikesrvm.osr.OSRConstants.ReturnAddressTypeCode;
039import static org.jikesrvm.osr.OSRConstants.STACK;
040import static org.jikesrvm.osr.OSRConstants.WORD;
041import static org.jikesrvm.osr.OSRConstants.WordTypeCode;
042import static org.jikesrvm.runtime.JavaSizeConstants.BYTES_IN_DOUBLE;
043import static org.jikesrvm.runtime.JavaSizeConstants.BYTES_IN_INT;
044import static org.jikesrvm.runtime.UnboxedSizeConstants.BYTES_IN_ADDRESS;
045
046import org.jikesrvm.VM;
047import org.jikesrvm.classloader.NormalMethod;
048import org.jikesrvm.compilers.baseline.BaselineCompiledMethod;
049import org.jikesrvm.compilers.baseline.ia32.BaselineCompilerImpl;
050import org.jikesrvm.compilers.common.CompiledMethods;
051import org.jikesrvm.osr.BytecodeTraverser;
052import org.jikesrvm.osr.ExecutionState;
053import org.jikesrvm.osr.ExecutionStateExtractor;
054import org.jikesrvm.osr.VariableElement;
055import org.jikesrvm.runtime.Magic;
056import org.jikesrvm.scheduler.RVMThread;
057import org.vmmagic.unboxed.Address;
058import org.vmmagic.unboxed.Offset;
059import org.vmmagic.unboxed.Word;
060
061/**
062 * A class that retrieves the VM scope descriptor
063 * from a suspended thread whose top method was compiled by the
064 * baseline compiler.
065 */
066public final class BaselineExecutionStateExtractor extends ExecutionStateExtractor {
067
068  /**
069   * Implements ExecutionStateExtractor.extractState.
070   *
071   * @param thread : the suspended thread, the registers and stack frames are used.
072   * @param osrFPoff : the osr method's stack frame offset
073   * @param methFPoff : the real method's stack frame offset
074   * @param cmid   : the top application method ( system calls are unwounded ).
075   *
076   * return a ExecutionStateExtractor object.
077   */
078  @Override
079  public ExecutionState extractState(RVMThread thread, Offset osrFPoff, Offset methFPoff, int cmid) {
080
081    /* performs architecture and compiler dependent operations here
082    *
083    * When a thread is hung called from baseline compiled code,
084    * the hierarchy of calls on stack looks like follows
085    * ( starting from FP in the FP register ):
086    *
087    *           morph
088    *           yield
089    *           threadSwitch
090    *           threadSwitchFrom[Prologue|Backedge|Epilong]
091    *           foo ( real method ).
092    *
093    * The returned ExecutionState should have following
094    *
095    *     current thread
096    *     compiled method ID of "foo"
097    *     fp of foo's stack frame
098    *     bytecode index of foo's next instruction
099    *     the list of variable,value of foo at that point
100    *     which method (foo)
101    */
102
103    if (VM.TraceOnStackReplacement) {
104      VM.sysWriteln("BASE execStateExtractor starting ...");
105    }
106
107    byte[] stack = thread.getStack();
108
109    if (VM.VerifyAssertions) {
110      int fooCmid = Magic.getIntAtOffset(stack, methFPoff.plus(STACKFRAME_METHOD_ID_OFFSET));
111
112      if (VM.TraceOnStackReplacement) {
113        VM.sysWriteln("fooCmid = " + fooCmid);
114        VM.sysWriteln("   cmid = " + cmid);
115      }
116
117      VM._assert(fooCmid == cmid);
118    }
119
120    BaselineCompiledMethod fooCM = (BaselineCompiledMethod) CompiledMethods.getCompiledMethod(cmid);
121
122    NormalMethod fooM = (NormalMethod) fooCM.getMethod();
123
124    VM.disableGC();
125    Address rowIP = Magic.objectAsAddress(stack).loadAddress(osrFPoff.plus(STACKFRAME_RETURN_ADDRESS_OFFSET));
126    Offset ipOffset = fooCM.getInstructionOffset(rowIP);
127    VM.enableGC();
128
129    // CAUTION: IP Offset should point to next instruction
130    int bcIndex = fooCM.findBytecodeIndexForInstruction(ipOffset.plus(INSTRUCTION_WIDTH));
131
132    // assertions
133    if (VM.VerifyAssertions) {
134      if (bcIndex == -1) {
135
136        VM.sysWriteln("osrFPoff = ", osrFPoff);
137        VM.sysWriteln("instr_beg = ", Magic.objectAsAddress(fooCM.getEntryCodeArray()));
138
139        for (int i = (osrFPoff.toInt()) - 10; i < (osrFPoff.toInt()) + 10; i++) {
140          VM.sysWriteln("  stack[" + i + "] = " + stack[i]);
141        }
142
143        Offset ipIndex = ipOffset.toWord().rsha(LG_INSTRUCTION_WIDTH).toOffset();
144        VM.sysWriteln("ipIndex : ", ipIndex);
145        VM.sysWriteln("bcIndex : " + bcIndex);
146      }
147      VM._assert(bcIndex != -1);
148    }
149
150    // create execution state object
151    ExecutionState state = new ExecutionState(thread, methFPoff, cmid, bcIndex, osrFPoff);
152
153    /* extract values for local and stack, but first of all
154     * we need to get type information for current PC.
155     */
156    BytecodeTraverser typer = new BytecodeTraverser();
157    typer.computeLocalStackTypes(fooM, bcIndex);
158    byte[] localTypes = typer.getLocalTypes();
159    byte[] stackTypes = typer.getStackTypes();
160
161    if (VM.TraceOnStackReplacement) {
162      VM.sysWrite("BC Index : " + bcIndex + "\n");
163      VM.sysWrite("Local Types :");
164      for (byte localType : localTypes) {
165        VM.sysWrite(" " + (char) localType);
166      }
167      VM.sysWrite("\nStack Types :");
168      for (byte stackType : stackTypes) {
169        VM.sysWrite(" " + (char) stackType);
170      }
171      VM.sysWrite("\n");
172    }
173
174    // consult GC reference map again since the type matcher does not complete
175    // the flow analysis, it can not distinguish reference or non-reference
176    // type. We should remove non-reference type
177    for (int i = 0, n = localTypes.length; i < n; i++) {
178      // if typer reports a local is reference type, but the GC map says no
179      // then set the localType to uninitialized, see VM spec, bytecode verifier
180      if (localTypes[i] == ClassTypeCode) {
181        if (!fooCM.referenceMaps.isLocalRefType(fooM, ipOffset.plus(1 << LG_INSTRUCTION_WIDTH), i)) {
182          localTypes[i] = VoidTypeCode;
183          if (VM.TraceOnStackReplacement) {
184            VM.sysWriteln("GC maps disagrees with type matcher at " + i + "th local\n");
185          }
186        }
187      }
188    }
189
190    // go through the stack frame and extract values
191    // In the variable value list, we keep the order as follows:
192    // L0, L1, ..., S0, S1, ....
193
194    // adjust local offset and stack offset
195    // NOTE: do not call BaselineCompilerImpl.getFirstLocalOffset(method)
196    Offset startLocalOffset = methFPoff.plus(BaselineCompilerImpl.locationToOffset(fooCM.getGeneralLocalLocation(0)));
197
198    Offset stackOffset = methFPoff.plus(fooCM.getEmptyStackOffset());
199
200    // for locals
201    getVariableValue(stack, startLocalOffset, localTypes, fooCM, LOCAL, state);
202
203    // for stacks
204    getVariableValue(stack, stackOffset, stackTypes, fooCM, STACK, state);
205
206    if (VM.TraceOnStackReplacement) {
207      state.printState();
208    }
209
210    if (VM.TraceOnStackReplacement) {
211      VM.sysWriteln("BASE executionStateExtractor done ");
212    }
213    return state;
214  }
215
216  /* go over local/stack array, and build VariableElement. */
217  private static void getVariableValue(byte[] stack, Offset offset, byte[] types,
218                                       BaselineCompiledMethod compiledMethod, boolean kind, ExecutionState state) {
219    int size = types.length;
220    Offset vOffset = offset;
221    for (int i = 0; i < size; i++) {
222      if (VM.TraceOnStackReplacement) {
223        Word content = Magic.getWordAtOffset(stack, vOffset.minus(BYTES_IN_ADDRESS));
224        VM.sysWrite("0x", vOffset.minus(BYTES_IN_ADDRESS), "    0x");
225        VM.sysWriteln(content);
226        if ((types[i] == LongTypeCode) || (types[i] == DoubleTypeCode)) {
227          content = Magic.getWordAtOffset(stack, vOffset.minus(2 * BYTES_IN_ADDRESS));
228          VM.sysWrite("0x", vOffset.minus(2 * BYTES_IN_ADDRESS), "    0x");
229          VM.sysWriteln(content);
230        }
231      }
232
233      switch (types[i]) {
234        case VoidTypeCode:
235          vOffset = vOffset.minus(BYTES_IN_STACKSLOT);
236          break;
237
238        case BooleanTypeCode:
239        case ByteTypeCode:
240        case ShortTypeCode:
241        case CharTypeCode:
242        case IntTypeCode:
243        case FloatTypeCode: {
244          int value = Magic.getIntAtOffset(stack, vOffset.minus(BYTES_IN_INT));
245          vOffset = vOffset.minus(BYTES_IN_STACKSLOT);
246
247          byte tcode = (types[i] == FloatTypeCode) ? FLOAT : INT;
248
249          state.add(new VariableElement(kind, i, tcode, value));
250          break;
251        }
252        case LongTypeCode:
253        case DoubleTypeCode: {
254          //KV: this code would be nicer if VoidTypeCode would always follow a 64-bit value. Rigth now for LOCAL it follows, for STACK it proceeds
255          Offset memoff =
256              (kind == LOCAL) ? vOffset.minus(BYTES_IN_DOUBLE) : VM.BuildFor64Addr ? vOffset : vOffset.minus(
257                  BYTES_IN_STACKSLOT);
258          long value = Magic.getLongAtOffset(stack, memoff);
259
260          byte tcode = (types[i] == LongTypeCode) ? LONG : DOUBLE;
261
262          state.add(new VariableElement(kind, i, tcode, value));
263
264          if (kind == LOCAL) { //KV:VoidTypeCode is next
265            vOffset = vOffset.minus(2 * BYTES_IN_STACKSLOT);
266            i++;
267          } else {
268            vOffset = vOffset.minus(BYTES_IN_STACKSLOT); //KV:VoidTypeCode was already in front
269          }
270
271          break;
272        }
273        case ReturnAddressTypeCode: {
274          VM.disableGC();
275          Address rowIP = Magic.objectAsAddress(stack).loadAddress(vOffset);
276          Offset ipOffset = compiledMethod.getInstructionOffset(rowIP);
277          VM.enableGC();
278
279          vOffset = vOffset.minus(BYTES_IN_STACKSLOT);
280
281          if (VM.TraceOnStackReplacement) {
282            Offset ipIndex = ipOffset.toWord().rsha(LG_INSTRUCTION_WIDTH).toOffset();
283            VM.sysWrite("baseline ret_addr ip ", ipIndex, " --> ");
284          }
285
286          int bcIndex = compiledMethod.findBytecodeIndexForInstruction(ipOffset.plus(INSTRUCTION_WIDTH));
287
288          if (VM.TraceOnStackReplacement) {
289            VM.sysWrite(" bc " + bcIndex + "\n");
290          }
291
292          state.add(new VariableElement(kind, i, RET_ADDR, bcIndex));
293          break;
294        }
295
296        case ClassTypeCode:
297        case ArrayTypeCode: {
298          VM.disableGC();
299          Object ref = Magic.getObjectAtOffset(stack, vOffset.minus(BYTES_IN_ADDRESS));
300          VM.enableGC();
301
302          vOffset = vOffset.minus(BYTES_IN_STACKSLOT);
303
304          state.add(new VariableElement(kind, i, REF, ref));
305          break;
306        }
307        case WordTypeCode: {
308          Word value = Magic.getWordAtOffset(stack, vOffset.minus(BYTES_IN_ADDRESS));
309          vOffset = vOffset.minus(BYTES_IN_STACKSLOT);
310
311          state.add(new VariableElement(kind, i, WORD, value));
312          break;
313        }
314        default:
315          if (VM.VerifyAssertions) VM._assert(VM.NOT_REACHED);
316          break;
317      } // switch
318    } // for loop
319  }
320}