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}