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