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 }