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 /* add padding (NOP) at the beginning of pseudo bytecode
420 * to make the new bytecode size dividable by 4, then no branch
421 * target adjustment is needed in the original code.
422 */
423 private static int paddingBytecode(PseudoBytecode head) {
424 /* skip the first Nop. */
425 PseudoBytecode bcode = head.next;
426
427 /* count the total size of prologue code. */
428 int bsize = 0;
429 while (bcode != null) {
430 bsize += bcode.getSize();
431 bcode = bcode.next;
432 }
433
434 /* insert Nop at the beginning to make the code size of x4. */
435 int padding = 3 - (bsize + 3) & 0x03;
436
437 for (int i = 0; i < padding; i++) {
438 bcode = new Nop();
439 bcode.next = head.next;
440 head.next = bcode;
441 }
442
443 bsize += padding;
444
445 return bsize;
446 }
447
448 /* generating binary code from pseudo code, the size and the code
449 * list are padded and well calculated.
450 */
451 private static byte[] generateBinaries(PseudoBytecode bhead, int bsize) {
452
453 /* patch the LoalAddrConst instruction, and generate codes. */
454 byte[] codes = new byte[bsize];
455
456 /* skip the first NOP */
457 PseudoBytecode bcode = bhead.next;
458 int pos = 0;
459 while (bcode != null) {
460
461 int size = bcode.getSize();
462
463 if (bcode instanceof LoadRetAddrConst) {
464 LoadRetAddrConst laddr = (LoadRetAddrConst) bcode;
465
466 /* CAUTION: path relative offset only. */
467 laddr.patch(laddr.getOffset() + bsize);
468 }
469
470 if (VM.TraceOnStackReplacement) VM.sysWriteln(pos + " : " + bcode.toString());
471
472 System.arraycopy(bcode.getBytes(), 0, codes, pos, size);
473
474 pos += size;
475 bcode = bcode.next;
476 }
477
478 return codes;
479 }
480
481 public String toString() {
482 StringBuffer buf = new StringBuffer("Execution state " + this.bcIndex + "@" + this.meth + " " + this.thread);
483 for (int i = 0, n = varElms.size(); i < n; i++) {
484 VariableElement var = varElms.get(i);
485 buf.append("\n ");
486 buf.append(var);
487 }
488
489 return new String(buf);
490 }
491 }