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