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