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