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.MemberReference;
018    import org.jikesrvm.classloader.MethodReference;
019    import org.jikesrvm.classloader.NormalMethod;
020    import org.jikesrvm.compilers.common.CompiledMethod;
021    import org.jikesrvm.compilers.common.CompiledMethods;
022    import org.jikesrvm.compilers.opt.regalloc.ia32.PhysicalRegisterConstants;
023    import org.jikesrvm.compilers.opt.runtimesupport.OptCompiledMethod;
024    import org.jikesrvm.ia32.ArchConstants;
025    import org.jikesrvm.osr.OSRConstants;
026    import org.jikesrvm.osr.EncodedOSRMap;
027    import org.jikesrvm.osr.ExecutionStateExtractor;
028    import org.jikesrvm.osr.ExecutionState;
029    import org.jikesrvm.osr.OSRMapIterator;
030    import org.jikesrvm.osr.VariableElement;
031    import org.jikesrvm.runtime.Magic;
032    import org.jikesrvm.runtime.RuntimeEntrypoints;
033    import org.jikesrvm.scheduler.RVMThread;
034    import org.vmmagic.unboxed.Address;
035    import org.vmmagic.unboxed.Offset;
036    import org.vmmagic.unboxed.Word;
037    import org.vmmagic.unboxed.WordArray;
038    
039    /**
040     * OptExecutionStateExtractor is a subclass of ExecutionStateExtractor.
041     * It extracts the execution state from an optimized activation.
042     */
043    public abstract class OptExecutionStateExtractor extends ExecutionStateExtractor
044        implements Constants, ArchConstants, OSRConstants, PhysicalRegisterConstants {
045    
046      @Override
047      public ExecutionState extractState(RVMThread thread, Offset osrFPoff, Offset methFPoff, int cmid) {
048    
049        /* perform machine and compiler dependent operations here
050        * osrFPoff is the fp offset of
051        * OptSaveVolatile.threadSwithFrom<...>
052        *
053        *  (stack grows downward)
054        *          foo
055        *     |->     <-- methFPoff
056        *     |
057        *     |    <tsfrom>
058        *     |--     <-- osrFPoff
059        *
060        *
061        * The threadSwitchFrom method saves all volatiles, nonvolatiles, and
062        * scratch registers. All register values for 'foo' can be obtained
063        * from the register save area of '<tsfrom>' method.
064        */
065    
066        byte[] stack = thread.getStack();
067    
068        // get registers for the caller ( real method )
069        TempRegisters registers = new TempRegisters(thread.contextRegisters);
070    
071        if (VM.VerifyAssertions) {
072          int foocmid = Magic.getIntAtOffset(stack, methFPoff.plus(STACKFRAME_METHOD_ID_OFFSET));
073          if (foocmid != cmid) {
074            CompiledMethod cm = CompiledMethods.getCompiledMethod(cmid);
075            VM.sysWriteln("unmatch method, it should be " + cm.getMethod());
076            CompiledMethod foo = CompiledMethods.getCompiledMethod(foocmid);
077            VM.sysWriteln("but now it is " + foo.getMethod());
078            walkOnStack(stack, osrFPoff);
079          }
080          VM._assert(foocmid == cmid);
081        }
082    
083        OptCompiledMethod fooCM = (OptCompiledMethod) CompiledMethods.getCompiledMethod(cmid);
084    
085        /* Following code get the machine code offset to the
086         * next instruction. All operation of the stack frame
087         * are kept in GC critical section.
088         * All code in the section should not cause any GC
089         * activities, and avoid lazy compilation.
090         */
091    
092        /* Following code is architecture dependent. In IA32, the return address
093         * saved in caller stack frames, so use osrFP to get the next instruction
094         * address of foo
095         */
096    
097        // get the next machine code offset of the real method
098        VM.disableGC();
099        Address osrFP = Magic.objectAsAddress(stack).plus(osrFPoff);
100        Address nextIP = Magic.getReturnAddressUnchecked(osrFP);
101        Offset ipOffset = fooCM.getInstructionOffset(nextIP);
102        VM.enableGC();
103    
104        EncodedOSRMap fooOSRMap = fooCM.getOSRMap();
105    
106        /* get register reference map from OSR map
107         * we are using this map to convert addresses to objects,
108         * thus we can operate objects out of GC section.
109         */
110        int regmap = fooOSRMap.getRegisterMapForMCOffset(ipOffset);
111    
112        {
113          int bufCMID = Magic.getIntAtOffset(stack, osrFPoff.plus(STACKFRAME_METHOD_ID_OFFSET));
114          CompiledMethod bufCM = CompiledMethods.getCompiledMethod(bufCMID);
115    
116          // offset in bytes, convert it to stack words from fpIndex
117          // SaveVolatile can only be compiled by OPT compiler
118          if (VM.VerifyAssertions) VM._assert(bufCM instanceof OptCompiledMethod);
119          restoreValuesFromOptSaveVolatile(stack, osrFPoff, registers, regmap, bufCM);
120        }
121    
122        // return a list of states: from caller to callee
123        // if the osr happens in an inlined method, the state is
124        // a chain of recoverd methods.
125        ExecutionState state =
126            getExecStateSequence(thread, stack, ipOffset, methFPoff, cmid, osrFPoff, registers, fooOSRMap);
127    
128        // reverse callerState points, it becomes callee -> caller
129        ExecutionState prevState = null;
130        ExecutionState nextState = state;
131        while (nextState != null) {
132          // 1. current node
133          state = nextState;
134          // 1. hold the next state first
135          nextState = nextState.callerState;
136          // 2. redirect pointer
137          state.callerState = prevState;
138          // 3. move prev to current
139          prevState = state;
140        }
141    
142        if (VM.TraceOnStackReplacement) {
143          VM.sysWriteln("OptExecState : recovered states " + thread.toString());
144          ExecutionState temp = state;
145          do {
146            VM.sysWriteln(temp.toString());
147            temp = temp.callerState;
148          } while (temp != null);
149        }
150    
151        return state;
152      }
153    
154      /* OptSaveVolatile has different stack layout from DynamicBridge
155        * Have to separately recover them now, but there should be unified
156        * later on.
157        *
158        *    |----------|
159        *    |   NON    |
160        *    |Volatiles |
161        *    |          | <-- volatile offset
162        *    |Volatiles |
163        *    |          |
164        *    |FPR states|
165        *    |__________|  ___ FP
166      */
167      private void restoreValuesFromOptSaveVolatile(byte[] stack, Offset osrFPoff, TempRegisters registers, int regmap,
168                                                    CompiledMethod cm) {
169    
170        OptCompiledMethod tsfromCM = (OptCompiledMethod) cm;
171    
172        boolean saveVolatile = tsfromCM.isSaveVolatile();
173        if (VM.VerifyAssertions) {
174          VM._assert(saveVolatile);
175        }
176    
177        WordArray gprs = registers.gprs;
178    
179        // enter critical section
180        // precall methods potientially causing dynamic compilation
181        int firstNonVolatile = tsfromCM.getFirstNonVolatileGPR();
182        int nonVolatiles = tsfromCM.getNumberOfNonvolatileGPRs();
183        int nonVolatileOffset = tsfromCM.getUnsignedNonVolatileOffset() + (nonVolatiles - 1) * BYTES_IN_STACKSLOT;
184    
185        VM.disableGC();
186    
187        // recover nonvolatile GPRs
188        for (int i = firstNonVolatile + nonVolatiles - 1; i >= firstNonVolatile; i--) {
189          gprs.set(NONVOLATILE_GPRS[i].value(), Magic.objectAsAddress(stack).loadWord(osrFPoff.minus(nonVolatileOffset)));
190          nonVolatileOffset -= BYTES_IN_STACKSLOT;
191        }
192    
193        // restore with VOLATILES yet
194        int volatileOffset = nonVolatileOffset;
195        for (int i = NUM_VOLATILE_GPRS - 1; i >= 0; i--) {
196          gprs.set(VOLATILE_GPRS[i].value(), Magic.objectAsAddress(stack).loadWord(osrFPoff.minus(volatileOffset)));
197          volatileOffset -= BYTES_IN_STACKSLOT;
198        }
199    
200        // currently, all FPRS are volatile on intel,
201        // DO nothing.
202    
203        // convert addresses in registers to references, starting from register 0
204        // powerPC starts from register 1
205        for (int i = 0; i < NUM_GPRS; i++) {
206          if (EncodedOSRMap.registerIsSet(regmap, i)) {
207            registers.objs[i] = Magic.addressAsObject(registers.gprs.get(i).toAddress());
208          }
209        }
210    
211        VM.enableGC();
212    
213        if (VM.TraceOnStackReplacement) {
214          for (GPR reg : GPR.values()) {
215            VM.sysWrite(reg.toString());
216            VM.sysWrite(" = ");
217            VM.sysWrite(registers.gprs.get(reg.value()).toAddress());
218            VM.sysWrite("\n");
219          }
220        }
221      }
222    
223      private ExecutionState getExecStateSequence(RVMThread thread, byte[] stack, Offset ipOffset, Offset fpOffset,
224                                                      int cmid, Offset tsFPOffset, TempRegisters registers,
225                                                      EncodedOSRMap osrmap) {
226    
227        // go through the stack frame and extract values
228        // In the variable value list, we keep the order as follows:
229        // L0, L1, ..., S0, S1, ....
230    
231        /* go over osr map element, build list of VariableElement.
232        * assuming iterator has ordered element as
233        *     L0, L1, ..., S0, S1, ...
234        *
235        *     ThreadSwitch
236        *     threadSwitchFromOsr
237        *     FOO                                        <-- fpOffset
238        *
239        * Also, all registers saved by threadSwitchFromDeopt method
240        * is restored in "registers", address for object is converted
241        * back to object references.
242        *
243        * This method should be called in non-GC critical section since
244        * it allocates many objects.
245        */
246    
247        // for 64-bit type values which have two int parts.
248        // this holds the high part.
249        int lvalue_one = 0;
250        int lvtype_one = 0;
251    
252        // now recover execution states
253        OSRMapIterator iterator = osrmap.getOsrMapIteratorForMCOffset(ipOffset);
254        if (VM.VerifyAssertions) VM._assert(iterator != null);
255    
256        ExecutionState state = new ExecutionState(thread, fpOffset, cmid, iterator.getBcIndex(), tsFPOffset);
257        MethodReference mref = MemberReference.getMemberRef(iterator.getMethodId()).asMethodReference();
258        state.setMethod((NormalMethod) mref.peekResolvedMethod());
259        // this is not caller, but the callee, reverse it when outside
260        // of this function.
261        state.callerState = null;
262    
263        if (VM.TraceOnStackReplacement) {
264          VM.sysWriteln("osr map table of " + state.meth.toString());
265        }
266    
267        while (iterator.hasMore()) {
268    
269          if (iterator.getMethodId() != state.meth.getId()) {
270            ExecutionState newstate = new ExecutionState(thread, fpOffset, cmid, iterator.getBcIndex(), tsFPOffset);
271            mref = MemberReference.getMemberRef(iterator.getMethodId()).asMethodReference();
272            newstate.setMethod((NormalMethod) mref.peekResolvedMethod());
273            // this is not caller, but the callee, reverse it when outside
274            // of this function.
275            newstate.callerState = state;
276    
277            state = newstate;
278    
279            if (VM.TraceOnStackReplacement) {
280              VM.sysWriteln("osr map table of " + state.meth.toString());
281            }
282    
283          }
284    
285          // create a VariableElement for it.
286          boolean kind = iterator.getKind();
287          char num = iterator.getNumber();
288          byte tcode = iterator.getTypeCode();
289          byte vtype = iterator.getValueType();
290          int value = iterator.getValue();
291    
292          iterator.moveToNext();
293    
294          if (VM.TraceOnStackReplacement) {
295            VM.sysWrite((kind == LOCAL) ? "L" : "S");
296            VM.sysWrite((int)num);
297            VM.sysWrite(" , ");
298            if (vtype == ICONST) {
299              VM.sysWrite("ICONST ");
300              VM.sysWrite(value);
301            } else if (vtype == PHYREG) {
302              VM.sysWrite("PHYREG ");
303              VM.sysWrite(GPR.lookup(value).toString());
304            } else if (vtype == SPILL) {
305              VM.sysWrite("SPILL  ");
306              VM.sysWrite(value);
307            }
308            VM.sysWriteln();
309          }
310    
311          switch (tcode) {
312            case INT: {
313              int ibits = getIntBitsFrom(vtype, value, stack, fpOffset, registers);
314              state.add(new VariableElement(kind, num, tcode, ibits));
315              break;
316            }
317            case FLOAT: {
318              float fv = (float) getDoubleFrom(vtype, value, stack, fpOffset, registers);
319              int ibits = Magic.floatAsIntBits(fv);
320              state.add(new VariableElement(kind, num, tcode, ibits));
321              break;
322            }
323            case HIGH_64BIT: {
324              lvalue_one = value;
325              lvtype_one = vtype;
326              break;
327            }
328            case LONG: {
329              long lbits = getLongBitsFrom(lvtype_one, lvalue_one, vtype, value, stack, fpOffset, registers);
330              lvalue_one = 0;
331              lvtype_one = 0;
332              state.add(new VariableElement(kind, num, LONG, lbits));
333    
334              break;
335            }
336            case DOUBLE: {
337              double dv = getDoubleFrom(vtype, value, stack, fpOffset, registers);
338              long lbits = Magic.doubleAsLongBits(dv);
339              state.add(new VariableElement(kind, num, tcode, lbits));
340              break;
341            }
342            // I believe I did not handle return address correctly because
343            // the opt compiler did inlining of JSR/RET.
344            // To be VERIFIED.
345            case RET_ADDR: {
346              int bcIndex = getIntBitsFrom(vtype, value, stack, fpOffset, registers);
347              state.add(new VariableElement(kind, num, tcode, bcIndex));
348              break;
349            }
350            case WORD: { //KV:TODO
351              if (VM.BuildFor64Addr) VM._assert(VM.NOT_REACHED);
352              int word = getIntBitsFrom(vtype, value, stack, fpOffset, registers);
353    
354              state.add(new VariableElement(kind, num, tcode, word));
355              break;
356            }
357            case REF: {
358              Object ref = getObjectFrom(vtype, value, stack, fpOffset, registers);
359    
360              state.add(new VariableElement(kind, num, tcode, ref));
361              break;
362            }
363            default:
364              if (VM.VerifyAssertions) VM._assert(VM.NOT_REACHED);
365              break;
366          } // switch
367        } // for loop
368    
369        return state;
370      }
371    
372      /** auxillary functions to get value from different places. */
373      private static int getIntBitsFrom(int vtype, int value, byte[] stack, Offset fpOffset, TempRegisters registers) {
374        // for INT_CONST type, the value is the value
375        if (vtype == ICONST || vtype == ACONST) {
376          return value;
377    
378          // for physical register type, it is the register number
379          // because all registers are saved in threadswitch's stack
380          // frame, we get value from it.
381        } else if (vtype == PHYREG) {
382          return registers.gprs.get(value).toInt();
383    
384          // for spilled locals, the value is the spilled position
385          // it is on FOO's stackframe.
386          // ASSUMING, spill offset is offset to FP in bytes.
387        } else if (vtype == SPILL) {
388    
389          return Magic.getIntAtOffset(stack, fpOffset.minus(value));
390    
391        } else {
392          if (VM.VerifyAssertions) VM._assert(NOT_REACHED);
393          return -1;
394        }
395      }
396    
397      private static long getLongBitsFrom(int vtypeHigh, int valueHigh, int vtypeLow, int valueLow, byte[] stack, Offset fpOffset,
398                                          TempRegisters registers) {
399    
400        // for LCONST type, the value is the value
401        if (vtypeLow == LCONST || vtypeLow == ACONST) {
402          if (VM.VerifyAssertions) VM._assert(vtypeHigh == vtypeLow);
403          return ((((long) valueHigh) << 32) | ((valueLow) & 0x0FFFFFFFFL));
404    
405        } else if (VM.BuildFor32Addr) {
406          if (VM.VerifyAssertions) VM._assert(vtypeHigh == PHYREG || vtypeHigh == SPILL);
407          if (VM.VerifyAssertions) VM._assert(vtypeLow == PHYREG || vtypeLow == SPILL);
408          /* For physical registers, value is the register number.
409           * For spilled locals, the value is the spilled position on FOO's stackframe. */
410          long lowPart, highPart;
411    
412          if (vtypeLow == PHYREG) {
413            lowPart = (registers.gprs.get(valueLow).toInt()) & 0x0FFFFFFFFL;
414          } else {
415            lowPart = (Magic.getIntAtOffset(stack, fpOffset.minus(valueLow))) & 0x0FFFFFFFFL;
416          }
417    
418          if (vtypeHigh == PHYREG) {
419            highPart = (registers.gprs.get(valueHigh).toInt());
420          } else {
421            highPart = (Magic.getIntAtOffset(stack, fpOffset.minus(valueHigh)));
422          }
423    
424          return (highPart << 32) | lowPart;
425        } else if (VM.BuildFor64Addr) {
426          // for physical register type, it is the register number
427          // because all registers are saved in threadswitch's stack
428          // frame, we get value from it.
429          if (vtypeLow == PHYREG) {
430            return registers.gprs.get(valueLow).toLong();
431    
432            // for spilled locals, the value is the spilled position
433            // it is on FOO's stackframe.
434            // ASSUMING, spill offset is offset to FP in bytes.
435          } else if (vtypeLow == SPILL) {
436            return Magic.getLongAtOffset(stack, fpOffset.minus(valueLow));
437          }
438        }
439        if (VM.VerifyAssertions) VM._assert(NOT_REACHED);
440        return -1L;
441      }
442    
443      private static double getDoubleFrom(int vtype, int value, byte[] stack, Offset fpOffset,
444                                          TempRegisters registers) {
445        if (vtype == PHYREG) {
446          return registers.fprs[value - FIRST_DOUBLE];
447    
448        } else if (vtype == SPILL) {
449    
450          long lbits = Magic.getLongAtOffset(stack, fpOffset.minus(value));
451          return Magic.longBitsAsDouble(lbits);
452          //KV:TODO: why not use getDoubleAtOffset ???
453    
454        } else {
455          if (VM.VerifyAssertions) VM._assert(VM.NOT_REACHED);
456          return -1.0;
457        }
458      }
459    
460      private static Object getObjectFrom(int vtype, int value, byte[] stack, Offset fpOffset,
461                                          TempRegisters registers) {
462        if (vtype == ICONST) { //kv:todo : to become ACONST
463          // the only constant object for 64bit addressing is NULL
464          if (VM.VerifyAssertions) VM._assert(VM.BuildFor32Addr || value == 0);
465          return Magic.addressAsObject(Address.fromIntSignExtend(value));
466    
467        } else if (vtype == PHYREG) {
468          return registers.objs[value];
469    
470        } else if (vtype == SPILL) {
471          return Magic.getObjectAtOffset(stack, fpOffset.minus(value));
472    
473        } else {
474          VM.sysWrite("fatal error : ( vtype = " + vtype + " )\n");
475          if (VM.VerifyAssertions) VM._assert(VM.NOT_REACHED);
476          return null;
477        }
478      }
479    
480      @SuppressWarnings("unused")
481      private static void dumpStackContent(byte[] stack, Offset fpOffset) {
482        int cmid = Magic.getIntAtOffset(stack, fpOffset.plus(STACKFRAME_METHOD_ID_OFFSET));
483        OptCompiledMethod cm = (OptCompiledMethod) CompiledMethods.getCompiledMethod(cmid);
484    
485        int firstNonVolatile = cm.getFirstNonVolatileGPR();
486        int nonVolatiles = cm.getNumberOfNonvolatileGPRs();
487        int nonVolatileOffset = cm.getUnsignedNonVolatileOffset() + (nonVolatiles - 1) * BYTES_IN_STACKSLOT;
488    
489        VM.sysWriteln("stack of " + cm.getMethod());
490        VM.sysWriteln("      fp offset ", fpOffset);
491        VM.sysWriteln(" NV area offset ", nonVolatileOffset);
492        VM.sysWriteln("   first NV GPR ", firstNonVolatile);
493    
494        Address aFP = Magic.objectAsAddress(stack).plus(fpOffset);
495        for (Address a = aFP.plus(nonVolatileOffset); a.GE(aFP); a = a.minus(BYTES_IN_STACKSLOT)) {
496          Word content = a.loadWord();
497          VM.sysWriteHex(a);
498          VM.sysWrite("  ");
499          VM.sysWrite(content);
500          VM.sysWriteln();
501        }
502      }
503    
504      @SuppressWarnings("unused")
505      private static void dumpRegisterContent(WordArray gprs) {
506        for (GPR reg : GPR.values()) {
507          VM.sysWrite(reg.toString());
508          VM.sysWrite(" = ");
509          VM.sysWriteln(gprs.get(reg.value()));
510        }
511      }
512    
513      /* walk on stack frame, print out methods
514       */
515      private static void walkOnStack(byte[] stack, Offset fpOffset) {
516        VM.disableGC();
517    
518        Address fp = Magic.objectAsAddress(stack).plus(fpOffset);
519    
520        while (Magic.getCallerFramePointer(fp).NE(STACKFRAME_SENTINEL_FP)) {
521          int cmid = Magic.getCompiledMethodID(fp);
522    
523          if (cmid == INVISIBLE_METHOD_ID) {
524            VM.sysWriteln(" invisible method ");
525          } else {
526            CompiledMethod cm = CompiledMethods.getCompiledMethod(cmid);
527            fpOffset = fp.diff(Magic.objectAsAddress(stack));
528            VM.enableGC();
529    
530            VM.sysWriteln(cm.getMethod().toString());
531    
532            VM.disableGC();
533            fp = Magic.objectAsAddress(stack).plus(fpOffset);
534            if (cm.getMethod().getDeclaringClass().hasBridgeFromNativeAnnotation()) {
535              fp = RuntimeEntrypoints.unwindNativeStackFrame(fp);
536            }
537          }
538    
539          fp = Magic.getCallerFramePointer(fp);
540        }
541    
542        VM.enableGC();
543      }
544    }