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;
014    
015    import java.util.LinkedList;
016    import org.jikesrvm.VM;
017    import org.jikesrvm.classloader.BytecodeConstants;
018    import org.jikesrvm.classloader.BytecodeStream;
019    import org.jikesrvm.classloader.NormalMethod;
020    import org.jikesrvm.compilers.common.CompiledMethods;
021    import org.jikesrvm.osr.bytecodes.AConstNull;
022    import org.jikesrvm.osr.bytecodes.DoubleStore;
023    import org.jikesrvm.osr.bytecodes.FloatStore;
024    import org.jikesrvm.osr.bytecodes.Goto;
025    import org.jikesrvm.osr.bytecodes.IntStore;
026    import org.jikesrvm.osr.bytecodes.InvokeCompiledMethod;
027    import org.jikesrvm.osr.bytecodes.InvokeStatic;
028    import org.jikesrvm.osr.bytecodes.LoadDoubleConst;
029    import org.jikesrvm.osr.bytecodes.LoadFloatConst;
030    import org.jikesrvm.osr.bytecodes.LoadIntConst;
031    import org.jikesrvm.osr.bytecodes.LoadLongConst;
032    import org.jikesrvm.osr.bytecodes.LoadRetAddrConst;
033    import org.jikesrvm.osr.bytecodes.LoadWordConst;
034    import org.jikesrvm.osr.bytecodes.LongStore;
035    import org.jikesrvm.osr.bytecodes.Nop;
036    import org.jikesrvm.osr.bytecodes.ParamInitEnd;
037    import org.jikesrvm.osr.bytecodes.Pop;
038    import org.jikesrvm.osr.bytecodes.RefStore;
039    import org.jikesrvm.osr.bytecodes.PseudoBytecode;
040    import org.jikesrvm.scheduler.RVMThread;
041    import org.vmmagic.unboxed.Offset;
042    
043    public class ExecutionState implements OSRConstants, BytecodeConstants {
044    
045      /** the caller's state if this method is an inlinee */
046      public ExecutionState callerState = null;
047    
048      /** callee's compiled method id */
049      public int callee_cmid = -1;
050    
051      /** the method of which the execution state belongs to */
052      public NormalMethod meth;
053    
054      /** the program pointer (bytecode index) */
055      public int bcIndex;
056    
057      /**
058       * runtime values of locals and stack expressions at the bytecode index Each
059       * element is an object of VariableElement.
060       */
061      public LinkedList<VariableElement> varElms;
062    
063      /** the thread on which the activation is running */
064      public RVMThread thread;
065    
066      /** the offset of frame pointer of the activation. */
067      public Offset fpOffset;
068    
069      /** the callee (threadSwitch)'s frame pointer of this activation. */
070      public Offset tsFPOffset;
071    
072      /**
073       * the compiled method id of the activation (a Java method may have multiple
074       * version of compiled method
075       */
076      public int cmid;
077    
078      /**
079       * Initializer
080       * @param whichThread
081       * @param framePointerOffset
082       * @param compiledMethodID
083       * @param pc
084       * @param tsFPOffset
085       */
086      public ExecutionState(RVMThread whichThread, Offset framePointerOffset, int compiledMethodID, int pc,
087                                Offset tsFPOffset) {
088        this.thread = whichThread;
089        this.fpOffset = framePointerOffset;
090        this.cmid = compiledMethodID;
091        this.bcIndex = pc;
092        this.tsFPOffset = tsFPOffset;
093    
094        this.varElms = new LinkedList<VariableElement>();
095        this.meth = (NormalMethod) CompiledMethods.getCompiledMethod(cmid).getMethod();
096      }
097    
098      /////////////////////////////
099      // instance methods for construction
100      ////////////////////////////
101    
102      /** add a VariableElement */
103    
104      public void add(VariableElement elm) {
105        this.varElms.add(elm);
106      }
107    
108      /** insert as the first element, for convinience. */
109      public void addFirst(VariableElement elm) {
110        this.varElms.addFirst(elm);
111      }
112    
113      /** returns thread. */
114      public RVMThread getThread() {
115        return this.thread;
116      }
117    
118      public Offset getFPOffset() {
119        return this.fpOffset;
120      }
121    
122      public void setMethod(NormalMethod m) {
123        this.meth = m;
124      }
125    
126      public NormalMethod getMethod() {
127        return this.meth;
128      }
129    
130      public Offset getTSFPOffset() {
131        return this.tsFPOffset;
132      }
133    
134      /** print the current state for debugging */
135      public void printState() {
136        VM.sysWriteln("Execution state of " + meth);
137        VM.sysWriteln("    thread index : ", thread.getThreadSlot());
138        VM.sysWriteln("       FP offset : ", fpOffset);
139        VM.sysWriteln("            cmid : ", cmid);
140        VM.sysWriteln("         bcIndex : ", bcIndex);
141    
142        for (VariableElement var : varElms) {
143          VM.sysWrite("  " + var + "\n");
144        }
145      }
146    
147      //////////////////////////////////////
148      // interface to recompilation
149      /////////////////////////////////////
150    
151      private Object[] objs;
152      private int objnum;
153      private int rid;
154    
155      /**
156       * Goes through variable elements and produces specialized
157       * prologue using pseudo-bytecode.
158       */
159      public byte[] generatePrologue() {
160    
161        int size = varElms.size();
162    
163        this.objs = new Object[size];
164        this.objnum = 0;
165        this.rid = ObjectHolder.handinRefs(this.objs);
166    
167        PseudoBytecode head = new Nop();
168        PseudoBytecode tail = head;
169    
170        int elmcount = 0;
171        // restore parameters first;
172        // restore "this"
173        if (!this.meth.isStatic()) {
174          VariableElement var = varElms.get(elmcount);
175          tail = processElement(var, tail, elmcount);
176          elmcount++;
177    
178          if (VM.VerifyAssertions) {
179            VM._assert(var.isLocal() && (var.getNumber() == 0));
180          }
181        }
182        // restore other parameters,
183        int paranum = this.meth.getParameterTypes().length;
184        for (int i = 0; i < paranum; i++) {
185          VariableElement var = varElms.get(elmcount);
186          tail = processElement(var, tail, elmcount);
187          elmcount++;
188          if (VM.VerifyAssertions) {
189            VM._assert(var.isLocal());
190            // the number may not match because of long and double type
191          }
192        }
193        // ok, ready to indicate param initialized, thread switch
194        // and stack overflow check happens here
195        tail.next = new ParamInitEnd();
196        tail = tail.next;
197    
198        // restore other locals and stack slots, assuming stack element
199        // were sorted
200        for (; elmcount < size; elmcount++) {
201          VariableElement var = varElms.get(elmcount);
202          tail = processElement(var, tail, elmcount);
203        }// end of for loop
204    
205        if (this.objnum != 0) {
206          tail.next = new LoadIntConst(this.rid);
207          tail = tail.next;
208    
209          tail.next = new InvokeStatic(CLEANREFS);
210          tail = tail.next;
211        } else {
212          ObjectHolder.cleanRefs(this.rid);
213        }
214    
215        // default situation
216        int branchTarget = this.bcIndex;
217    
218        /* when this method must start with a call of callee,
219         * we are using invokeCompiledMethod,
220         */
221        if (callee_cmid != -1) {
222          // remember the callee's cmid, and the index of original index
223          tail.next = new InvokeCompiledMethod(callee_cmid, this.bcIndex);
224          tail = tail.next;
225    
226          // if this method needs a call, than we must jump to
227          // the instruction after the call.
228          BytecodeStream bcodes = this.meth.getBytecodes();
229          bcodes.reset(this.bcIndex);
230    
231          int code = bcodes.nextInstruction();
232    
233          switch (code) {
234            case JBC_invokeinterface: {
235              branchTarget = this.bcIndex + 5;
236              break;
237            }
238            case JBC_invokespecial:
239            case JBC_invokestatic:
240            case JBC_invokevirtual: {
241              branchTarget = this.bcIndex + 3;
242              break;
243            }
244            default: {
245              if (VM.VerifyAssertions) {
246                VM._assert(VM.NOT_REACHED,
247                           "ExecutionState: unknown bytecode " + code + " at " + this.bcIndex + "@" + this.meth);
248              }
249              break;
250            }
251          }
252        }
253    
254        // add goto statement, be careful, after goto
255        // there may be several pop instructions
256        int pops = computeStackHeight(head);
257        branchTarget += pops;  // preserve space
258        {
259          Goto togo = new Goto(branchTarget);
260          int osize = togo.getSize();
261          togo.patch(branchTarget + osize);
262          int nsize = togo.getSize();
263          if (nsize != osize) {
264            togo.patch(branchTarget + nsize);
265          }
266    
267          tail.next = togo;
268          tail = tail.next;
269        }
270    
271        // compute stack heights and padding pops
272        tail = adjustStackHeight(tail, pops);
273    
274        int bsize = paddingBytecode(head);
275        byte[] prologue = generateBinaries(head, bsize);
276    
277        // clean fields
278        this.objs = null;
279        this.objnum = 0;
280    
281        return prologue;
282      }// end of method
283    
284      private PseudoBytecode processElement(VariableElement var, PseudoBytecode tail, int i) {
285        switch (var.getTypeCode()) {
286          case INT: {
287            tail.next = new LoadIntConst(var.getIntBits());
288            tail = tail.next;
289    
290            if (var.isLocal()) {
291              tail.next = new IntStore(var.getNumber());
292              tail = tail.next;
293            }
294            break;
295          }
296          case FLOAT: {
297            tail.next = new LoadFloatConst(var.getIntBits());
298            tail = tail.next;
299    
300            if (var.isLocal()) {
301              tail.next = new FloatStore(var.getNumber());
302              tail = tail.next;
303            }
304            break;
305          }
306          case LONG: {
307            tail.next = new LoadLongConst(var.getLongBits());
308            tail = tail.next;
309    
310            if (var.isLocal()) {
311              tail.next = new LongStore(var.getNumber());
312              tail = tail.next;
313            }
314            break;
315          }
316          case DOUBLE: {
317            tail.next = new LoadDoubleConst(var.getLongBits());
318            tail = tail.next;
319    
320            if (var.isLocal()) {
321              tail.next = new DoubleStore(var.getNumber());
322              tail = tail.next;
323            }
324            break;
325          }
326          case RET_ADDR: {
327            tail.next = new LoadRetAddrConst(var.getIntBits());
328            tail = tail.next;
329    
330            if (var.isLocal()) {
331              tail.next = new RefStore(var.getNumber());
332              tail = tail.next;
333            }
334            break;
335          }
336          case REF: {
337            this.objs[i] = var.getObject();
338    
339            if (this.objs[i] != null) {
340    
341              tail.next = new LoadIntConst(this.rid);
342              tail = tail.next;
343    
344              tail.next = new LoadIntConst(i);
345              tail = tail.next;
346    
347              // the opt compiler will adjust the type of
348              // return value to the real type of object
349              // when it sees the invoke target is GETREFAT
350              tail.next = new InvokeStatic(GETREFAT);
351              tail = tail.next;
352            } else {
353              // just give an aconst_null
354              tail.next = new AConstNull();
355              tail = tail.next;
356            }
357    
358            if (var.isLocal()) {
359              tail.next = new RefStore(var.getNumber());
360              tail = tail.next;
361            }
362    
363            this.objnum++;
364    
365            break;
366          }
367          case WORD: {
368            tail.next = new LoadWordConst(var.getWord());
369            tail = tail.next;
370    
371            if (var.isLocal()) {
372              tail.next = new RefStore(var.getNumber());
373              tail = tail.next;
374            }
375            break;
376          }
377          default:
378            if (VM.VerifyAssertions) {
379              VM._assert(VM.NOT_REACHED);
380            }
381            break;
382        } // end of switch
383    
384        return tail;
385      }
386    
387      private short maxStackHeight = 0;
388    
389      public short getMaxStackHeight() {
390        return this.maxStackHeight;
391      }
392    
393      private int computeStackHeight(PseudoBytecode head) {
394        /* skip the first Nop */
395        PseudoBytecode bcode = head.next;
396        short height = 0;
397        while (bcode != null) {
398          height += bcode.stackChanges();
399          if (height > this.maxStackHeight) {
400            this.maxStackHeight = height;
401          }
402          bcode = bcode.next;
403        }
404    
405        if (VM.VerifyAssertions) VM._assert(height >= 0);
406        return height;
407      }
408    
409      private static PseudoBytecode adjustStackHeight(PseudoBytecode last, int height) {
410        // append pop
411        for (int i = 0; i < height; i++) {
412          last.next = new Pop();
413          last = last.next;
414        }
415    
416        return last;
417      }
418    
419      /**
420       * Adds padding (NOP) at the beginning of pseudo bytecode
421       * to make the new bytecode size dividable by 4, then no branch
422       * target adjustment is needed in the original code.
423       * @param head
424       * @return the new bytecode size
425       */
426      private static int paddingBytecode(PseudoBytecode head) {
427        /* skip the first Nop. */
428        PseudoBytecode bcode = head.next;
429    
430        /* count the total size of prologue code. */
431        int bsize = 0;
432        while (bcode != null) {
433          bsize += bcode.getSize();
434          bcode = bcode.next;
435        }
436    
437        /* insert Nop at the beginning to make the code size of x4. */
438        int padding = 3 - (bsize + 3) & 0x03;
439    
440        for (int i = 0; i < padding; i++) {
441          bcode = new Nop();
442          bcode.next = head.next;
443          head.next = bcode;
444        }
445    
446        bsize += padding;
447    
448        return bsize;
449      }
450    
451      /* generating binary code from pseudo code, the size and the code
452       * list are padded and well calculated.
453       */
454      private static byte[] generateBinaries(PseudoBytecode bhead, int bsize) {
455    
456        /* patch the LoalAddrConst instruction, and generate codes. */
457        byte[] codes = new byte[bsize];
458    
459        /* skip the first NOP */
460        PseudoBytecode bcode = bhead.next;
461        int pos = 0;
462        while (bcode != null) {
463    
464          int size = bcode.getSize();
465    
466          if (bcode instanceof LoadRetAddrConst) {
467            LoadRetAddrConst laddr = (LoadRetAddrConst) bcode;
468    
469            /* CAUTION: path relative offset only. */
470            laddr.patch(laddr.getOffset() + bsize);
471          }
472    
473          if (VM.TraceOnStackReplacement) VM.sysWriteln(pos + " : " + bcode.toString());
474    
475          System.arraycopy(bcode.getBytes(), 0, codes, pos, size);
476    
477          pos += size;
478          bcode = bcode.next;
479        }
480    
481        return codes;
482      }
483    
484      @Override
485      public String toString() {
486        StringBuffer buf = new StringBuffer("Execution state " + this.bcIndex + "@" + this.meth + " " + this.thread);
487        for (int i = 0, n = varElms.size(); i < n; i++) {
488          VariableElement var = varElms.get(i);
489          buf.append("\n  ");
490          buf.append(var);
491        }
492    
493        return new String(buf);
494      }
495    }