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.ia32; 014 015import static org.jikesrvm.VM.NOT_REACHED; 016import static org.jikesrvm.compilers.opt.regalloc.ia32.PhysicalRegisterConstants.FIRST_DOUBLE; 017import static org.jikesrvm.ia32.RegisterConstants.NONVOLATILE_GPRS; 018import static org.jikesrvm.ia32.RegisterConstants.NUM_GPRS; 019import static org.jikesrvm.ia32.RegisterConstants.NUM_VOLATILE_GPRS; 020import static org.jikesrvm.ia32.RegisterConstants.VOLATILE_GPRS; 021import static org.jikesrvm.ia32.StackframeLayoutConstants.BYTES_IN_STACKSLOT; 022import static org.jikesrvm.ia32.StackframeLayoutConstants.INVISIBLE_METHOD_ID; 023import static org.jikesrvm.ia32.StackframeLayoutConstants.STACKFRAME_METHOD_ID_OFFSET; 024import static org.jikesrvm.ia32.StackframeLayoutConstants.STACKFRAME_SENTINEL_FP; 025import static org.jikesrvm.osr.OSRConstants.ACONST; 026import static org.jikesrvm.osr.OSRConstants.DOUBLE; 027import static org.jikesrvm.osr.OSRConstants.FLOAT; 028import static org.jikesrvm.osr.OSRConstants.HIGH_64BIT; 029import static org.jikesrvm.osr.OSRConstants.ICONST; 030import static org.jikesrvm.osr.OSRConstants.INT; 031import static org.jikesrvm.osr.OSRConstants.LCONST; 032import static org.jikesrvm.osr.OSRConstants.LOCAL; 033import static org.jikesrvm.osr.OSRConstants.LONG; 034import static org.jikesrvm.osr.OSRConstants.PHYREG; 035import static org.jikesrvm.osr.OSRConstants.REF; 036import static org.jikesrvm.osr.OSRConstants.RET_ADDR; 037import static org.jikesrvm.osr.OSRConstants.SPILL; 038import static org.jikesrvm.osr.OSRConstants.WORD; 039 040import org.jikesrvm.VM; 041import org.jikesrvm.classloader.MemberReference; 042import org.jikesrvm.classloader.MethodReference; 043import org.jikesrvm.classloader.NormalMethod; 044import org.jikesrvm.compilers.common.CompiledMethod; 045import org.jikesrvm.compilers.common.CompiledMethods; 046import org.jikesrvm.compilers.opt.runtimesupport.OptCompiledMethod; 047import org.jikesrvm.ia32.RegisterConstants.GPR; 048import org.jikesrvm.osr.EncodedOSRMap; 049import org.jikesrvm.osr.ExecutionState; 050import org.jikesrvm.osr.ExecutionStateExtractor; 051import org.jikesrvm.osr.OSRMapIterator; 052import org.jikesrvm.osr.VariableElement; 053import org.jikesrvm.runtime.Magic; 054import org.jikesrvm.runtime.RuntimeEntrypoints; 055import org.jikesrvm.scheduler.RVMThread; 056import org.vmmagic.unboxed.Address; 057import org.vmmagic.unboxed.Offset; 058import org.vmmagic.unboxed.Word; 059import org.vmmagic.unboxed.WordArray; 060 061/** 062 * OptExecutionStateExtractor is a subclass of ExecutionStateExtractor. 063 * It extracts the execution state from an optimized activation. 064 */ 065public final class OptExecutionStateExtractor extends ExecutionStateExtractor { 066 067 @Override 068 public ExecutionState extractState(RVMThread thread, Offset osrFPoff, Offset methFPoff, int cmid) { 069 070 /* perform machine and compiler dependent operations here 071 * osrFPoff is the fp offset of 072 * OptSaveVolatile.threadSwithFrom<...> 073 * 074 * (stack grows downward) 075 * foo 076 * |-> <-- methFPoff 077 * | 078 * | <tsfrom> 079 * |-- <-- osrFPoff 080 * 081 * 082 * The threadSwitchFrom method saves all volatiles, nonvolatiles, and 083 * scratch registers. All register values for 'foo' can be obtained 084 * from the register save area of '<tsfrom>' method. 085 */ 086 087 byte[] stack = thread.getStack(); 088 089 // get registers for the caller ( real method ) 090 TempRegisters registers = new TempRegisters(thread.getContextRegisters()); 091 092 if (VM.VerifyAssertions) { 093 int foocmid = Magic.getIntAtOffset(stack, methFPoff.plus(STACKFRAME_METHOD_ID_OFFSET)); 094 if (foocmid != cmid) { 095 CompiledMethod cm = CompiledMethods.getCompiledMethod(cmid); 096 VM.sysWriteln("unmatch method, it should be " + cm.getMethod()); 097 CompiledMethod foo = CompiledMethods.getCompiledMethod(foocmid); 098 VM.sysWriteln("but now it is " + foo.getMethod()); 099 walkOnStack(stack, osrFPoff); 100 } 101 VM._assert(foocmid == cmid); 102 } 103 104 OptCompiledMethod fooCM = (OptCompiledMethod) CompiledMethods.getCompiledMethod(cmid); 105 106 /* Following code get the machine code offset to the 107 * next instruction. All operation of the stack frame 108 * are kept in GC critical section. 109 * All code in the section should not cause any GC 110 * activities, and avoid lazy compilation. 111 */ 112 113 /* Following code is architecture dependent. In IA32, the return address 114 * saved in caller stack frames, so use osrFP to get the next instruction 115 * address of foo 116 */ 117 118 // get the next machine code offset of the real method 119 VM.disableGC(); 120 Address osrFP = Magic.objectAsAddress(stack).plus(osrFPoff); 121 Address nextIP = Magic.getReturnAddressUnchecked(osrFP); 122 Offset ipOffset = fooCM.getInstructionOffset(nextIP); 123 VM.enableGC(); 124 125 EncodedOSRMap fooOSRMap = fooCM.getOSRMap(); 126 127 /* get register reference map from OSR map 128 * we are using this map to convert addresses to objects, 129 * thus we can operate objects out of GC section. 130 */ 131 int regmap = fooOSRMap.getRegisterMapForMCOffset(ipOffset); 132 133 { 134 int bufCMID = Magic.getIntAtOffset(stack, osrFPoff.plus(STACKFRAME_METHOD_ID_OFFSET)); 135 CompiledMethod bufCM = CompiledMethods.getCompiledMethod(bufCMID); 136 137 // offset in bytes, convert it to stack words from fpIndex 138 // SaveVolatile can only be compiled by OPT compiler 139 if (VM.VerifyAssertions) VM._assert(bufCM instanceof OptCompiledMethod); 140 restoreValuesFromOptSaveVolatile(stack, osrFPoff, registers, regmap, bufCM); 141 } 142 143 // return a list of states: from caller to callee 144 // if the osr happens in an inlined method, the state is 145 // a chain of recoverd methods. 146 ExecutionState state = 147 getExecStateSequence(thread, stack, ipOffset, methFPoff, cmid, osrFPoff, registers, fooOSRMap); 148 149 // reverse callerState points, it becomes callee -> caller 150 ExecutionState prevState = null; 151 ExecutionState nextState = state; 152 while (nextState != null) { 153 // 1. current node 154 state = nextState; 155 // 1. hold the next state first 156 nextState = nextState.callerState; 157 // 2. redirect pointer 158 state.callerState = prevState; 159 // 3. move prev to current 160 prevState = state; 161 } 162 163 if (VM.TraceOnStackReplacement) { 164 VM.sysWriteln("OptExecState : recovered states " + thread.toString()); 165 ExecutionState temp = state; 166 do { 167 VM.sysWriteln(temp.toString()); 168 temp = temp.callerState; 169 } while (temp != null); 170 } 171 172 return state; 173 } 174 175 /* OptSaveVolatile has different stack layout from DynamicBridge 176 * Have to separately recover them now, but there should be unified 177 * later on. 178 * 179 * |----------| 180 * | NON | 181 * |Volatiles | 182 * | | <-- volatile offset 183 * |Volatiles | 184 * | | 185 * |FPR states| 186 * |__________| ___ FP 187 */ 188 private void restoreValuesFromOptSaveVolatile(byte[] stack, Offset osrFPoff, TempRegisters registers, int regmap, 189 CompiledMethod cm) { 190 191 OptCompiledMethod tsfromCM = (OptCompiledMethod) cm; 192 193 boolean saveVolatile = tsfromCM.isSaveVolatile(); 194 if (VM.VerifyAssertions) { 195 VM._assert(saveVolatile); 196 } 197 198 WordArray gprs = registers.gprs; 199 200 // enter critical section 201 // precall methods potientially causing dynamic compilation 202 int firstNonVolatile = tsfromCM.getFirstNonVolatileGPR(); 203 int nonVolatiles = tsfromCM.getNumberOfNonvolatileGPRs(); 204 int nonVolatileOffset = tsfromCM.getUnsignedNonVolatileOffset() + (nonVolatiles - 1) * BYTES_IN_STACKSLOT; 205 206 VM.disableGC(); 207 208 // recover nonvolatile GPRs 209 for (int i = firstNonVolatile + nonVolatiles - 1; i >= firstNonVolatile; i--) { 210 gprs.set(NONVOLATILE_GPRS[i].value(), Magic.objectAsAddress(stack).loadWord(osrFPoff.minus(nonVolatileOffset))); 211 nonVolatileOffset -= BYTES_IN_STACKSLOT; 212 } 213 214 // restore with VOLATILES yet 215 int volatileOffset = nonVolatileOffset; 216 for (int i = NUM_VOLATILE_GPRS - 1; i >= 0; i--) { 217 gprs.set(VOLATILE_GPRS[i].value(), Magic.objectAsAddress(stack).loadWord(osrFPoff.minus(volatileOffset))); 218 volatileOffset -= BYTES_IN_STACKSLOT; 219 } 220 221 // currently, all FPRS are volatile on intel, 222 // DO nothing. 223 224 // convert addresses in registers to references, starting from register 0 225 // powerPC starts from register 1 226 for (int i = 0; i < NUM_GPRS; i++) { 227 if (EncodedOSRMap.registerIsSet(regmap, i)) { 228 registers.objs[i] = Magic.addressAsObject(registers.gprs.get(i).toAddress()); 229 } 230 } 231 232 VM.enableGC(); 233 234 if (VM.TraceOnStackReplacement) { 235 for (GPR reg : GPR.values()) { 236 VM.sysWrite(reg.toString()); 237 VM.sysWrite(" = "); 238 VM.sysWrite(registers.gprs.get(reg.value()).toAddress()); 239 VM.sysWrite("\n"); 240 } 241 } 242 } 243 244 private ExecutionState getExecStateSequence(RVMThread thread, byte[] stack, Offset ipOffset, Offset fpOffset, 245 int cmid, Offset tsFPOffset, TempRegisters registers, 246 EncodedOSRMap osrmap) { 247 248 // go through the stack frame and extract values 249 // In the variable value list, we keep the order as follows: 250 // L0, L1, ..., S0, S1, .... 251 252 /* go over osr map element, build list of VariableElement. 253 * assuming iterator has ordered element as 254 * L0, L1, ..., S0, S1, ... 255 * 256 * ThreadSwitch 257 * threadSwitchFromOsr 258 * FOO <-- fpOffset 259 * 260 * Also, all registers saved by threadSwitchFromDeopt method 261 * is restored in "registers", address for object is converted 262 * back to object references. 263 * 264 * This method should be called in non-GC critical section since 265 * it allocates many objects. 266 */ 267 268 // for 64-bit type values which have two int parts. 269 // this holds the high part. 270 int lvalue_one = 0; 271 int lvtype_one = 0; 272 273 // now recover execution states 274 OSRMapIterator iterator = osrmap.getOsrMapIteratorForMCOffset(ipOffset); 275 if (VM.VerifyAssertions) VM._assert(iterator != null); 276 277 ExecutionState state = new ExecutionState(thread, fpOffset, cmid, iterator.getBcIndex(), tsFPOffset); 278 MethodReference mref = MemberReference.getMethodRef(iterator.getMethodId()); 279 state.setMethod((NormalMethod) mref.peekResolvedMethod()); 280 // this is not caller, but the callee, reverse it when outside 281 // of this function. 282 state.callerState = null; 283 284 if (VM.TraceOnStackReplacement) { 285 VM.sysWriteln("osr map table of " + state.meth.toString()); 286 } 287 288 while (iterator.hasMore()) { 289 290 if (iterator.getMethodId() != state.meth.getId()) { 291 ExecutionState newstate = new ExecutionState(thread, fpOffset, cmid, iterator.getBcIndex(), tsFPOffset); 292 mref = MemberReference.getMethodRef(iterator.getMethodId()); 293 newstate.setMethod((NormalMethod) mref.peekResolvedMethod()); 294 // this is not caller, but the callee, reverse it when outside 295 // of this function. 296 newstate.callerState = state; 297 298 state = newstate; 299 300 if (VM.TraceOnStackReplacement) { 301 VM.sysWriteln("osr map table of " + state.meth.toString()); 302 } 303 304 } 305 306 // create a VariableElement for it. 307 boolean kind = iterator.getKind(); 308 char num = iterator.getNumber(); 309 byte tcode = iterator.getTypeCode(); 310 byte vtype = iterator.getValueType(); 311 int value = iterator.getValue(); 312 313 iterator.moveToNext(); 314 315 if (VM.TraceOnStackReplacement) { 316 VM.sysWrite((kind == LOCAL) ? "L" : "S"); 317 VM.sysWrite((int)num); 318 VM.sysWrite(" , "); 319 if (vtype == ICONST) { 320 VM.sysWrite("ICONST "); 321 VM.sysWrite(value); 322 } else if (vtype == PHYREG) { 323 VM.sysWrite("PHYREG "); 324 VM.sysWrite(GPR.lookup(value).toString()); 325 } else if (vtype == SPILL) { 326 VM.sysWrite("SPILL "); 327 VM.sysWrite(value); 328 } 329 VM.sysWriteln(); 330 } 331 332 switch (tcode) { 333 case INT: { 334 int ibits = getIntBitsFrom(vtype, value, stack, fpOffset, registers); 335 state.add(new VariableElement(kind, num, tcode, ibits)); 336 break; 337 } 338 case FLOAT: { 339 float fv = (float) getDoubleFrom(vtype, value, stack, fpOffset, registers); 340 int ibits = Magic.floatAsIntBits(fv); 341 state.add(new VariableElement(kind, num, tcode, ibits)); 342 break; 343 } 344 case HIGH_64BIT: { 345 lvalue_one = value; 346 lvtype_one = vtype; 347 break; 348 } 349 case LONG: { 350 long lbits = getLongBitsFrom(lvtype_one, lvalue_one, vtype, value, stack, fpOffset, registers); 351 lvalue_one = 0; 352 lvtype_one = 0; 353 state.add(new VariableElement(kind, num, LONG, lbits)); 354 355 break; 356 } 357 case DOUBLE: { 358 double dv = getDoubleFrom(vtype, value, stack, fpOffset, registers); 359 long lbits = Magic.doubleAsLongBits(dv); 360 state.add(new VariableElement(kind, num, tcode, lbits)); 361 break; 362 } 363 // I believe I did not handle return address correctly because 364 // the opt compiler did inlining of JSR/RET. 365 // To be VERIFIED. 366 case RET_ADDR: { 367 int bcIndex = getIntBitsFrom(vtype, value, stack, fpOffset, registers); 368 state.add(new VariableElement(kind, num, tcode, bcIndex)); 369 break; 370 } 371 case WORD: { //KV:TODO 372 if (VM.BuildFor64Addr) { 373 if (VM.VerifyAssertions) { 374 VM._assert(VM.NOT_REACHED); 375 } else { 376 VM.sysFail("Case not yet implemented for 64-bit addresssing."); 377 } 378 } 379 int word = getIntBitsFrom(vtype, value, stack, fpOffset, registers); 380 381 state.add(new VariableElement(kind, num, tcode, word)); 382 break; 383 } 384 case REF: { 385 Object ref = getObjectFrom(vtype, value, stack, fpOffset, registers); 386 387 state.add(new VariableElement(kind, num, tcode, ref)); 388 break; 389 } 390 default: 391 if (VM.VerifyAssertions) VM._assert(VM.NOT_REACHED); 392 break; 393 } // switch 394 } // for loop 395 396 return state; 397 } 398 399 private static int getIntBitsFrom(int vtype, int value, byte[] stack, Offset fpOffset, TempRegisters registers) { 400 // for INT_CONST type, the value is the value 401 if (vtype == ICONST || vtype == ACONST) { 402 return value; 403 404 // for physical register type, it is the register number 405 // because all registers are saved in threadswitch's stack 406 // frame, we get value from it. 407 } else if (vtype == PHYREG) { 408 return registers.gprs.get(value).toInt(); 409 410 // for spilled locals, the value is the spilled position 411 // it is on FOO's stackframe. 412 // ASSUMING, spill offset is offset to FP in bytes. 413 } else if (vtype == SPILL) { 414 415 return Magic.getIntAtOffset(stack, fpOffset.minus(value)); 416 417 } else { 418 if (VM.VerifyAssertions) VM._assert(NOT_REACHED); 419 return -1; 420 } 421 } 422 423 private static long getLongBitsFrom(int vtypeHigh, int valueHigh, int vtypeLow, int valueLow, byte[] stack, Offset fpOffset, 424 TempRegisters registers) { 425 426 // for LCONST type, the value is the value 427 if (vtypeLow == LCONST || vtypeLow == ACONST) { 428 if (VM.VerifyAssertions) VM._assert(vtypeHigh == vtypeLow); 429 return ((((long) valueHigh) << 32) | ((valueLow) & 0x0FFFFFFFFL)); 430 431 } else if (VM.BuildFor32Addr) { 432 if (VM.VerifyAssertions) VM._assert(vtypeHigh == PHYREG || vtypeHigh == SPILL); 433 if (VM.VerifyAssertions) VM._assert(vtypeLow == PHYREG || vtypeLow == SPILL); 434 /* For physical registers, value is the register number. 435 * For spilled locals, the value is the spilled position on FOO's stackframe. */ 436 long lowPart, highPart; 437 438 if (vtypeLow == PHYREG) { 439 lowPart = (registers.gprs.get(valueLow).toInt()) & 0x0FFFFFFFFL; 440 } else { 441 lowPart = (Magic.getIntAtOffset(stack, fpOffset.minus(valueLow))) & 0x0FFFFFFFFL; 442 } 443 444 if (vtypeHigh == PHYREG) { 445 highPart = (registers.gprs.get(valueHigh).toInt()); 446 } else { 447 highPart = (Magic.getIntAtOffset(stack, fpOffset.minus(valueHigh))); 448 } 449 450 return (highPart << 32) | lowPart; 451 } else if (VM.BuildFor64Addr) { 452 // for physical register type, it is the register number 453 // because all registers are saved in threadswitch's stack 454 // frame, we get value from it. 455 if (vtypeLow == PHYREG) { 456 return registers.gprs.get(valueLow).toLong(); 457 458 // for spilled locals, the value is the spilled position 459 // it is on FOO's stackframe. 460 // ASSUMING, spill offset is offset to FP in bytes. 461 } else if (vtypeLow == SPILL) { 462 return Magic.getLongAtOffset(stack, fpOffset.minus(valueLow)); 463 } 464 } 465 if (VM.VerifyAssertions) VM._assert(NOT_REACHED); 466 return -1L; 467 } 468 469 private static double getDoubleFrom(int vtype, int value, byte[] stack, Offset fpOffset, 470 TempRegisters registers) { 471 if (vtype == PHYREG) { 472 return registers.fprs[value - FIRST_DOUBLE]; 473 474 } else if (vtype == SPILL) { 475 476 long lbits = Magic.getLongAtOffset(stack, fpOffset.minus(value)); 477 return Magic.longBitsAsDouble(lbits); 478 //KV:TODO: why not use getDoubleAtOffset ??? 479 480 } else { 481 if (VM.VerifyAssertions) VM._assert(VM.NOT_REACHED); 482 return -1.0; 483 } 484 } 485 486 private static Object getObjectFrom(int vtype, int value, byte[] stack, Offset fpOffset, 487 TempRegisters registers) { 488 if (vtype == ICONST) { //kv:todo : to become ACONST 489 // the only constant object for 64bit addressing is NULL 490 if (VM.VerifyAssertions) VM._assert(VM.BuildFor32Addr || value == 0); 491 return Magic.addressAsObject(Address.fromIntSignExtend(value)); 492 493 } else if (vtype == PHYREG) { 494 return registers.objs[value]; 495 496 } else if (vtype == SPILL) { 497 return Magic.getObjectAtOffset(stack, fpOffset.minus(value)); 498 499 } else { 500 VM.sysWrite("fatal error : ( vtype = " + vtype + " )\n"); 501 if (VM.VerifyAssertions) VM._assert(VM.NOT_REACHED); 502 return null; 503 } 504 } 505 506 @SuppressWarnings("unused") 507 private static void dumpStackContent(byte[] stack, Offset fpOffset) { 508 int cmid = Magic.getIntAtOffset(stack, fpOffset.plus(STACKFRAME_METHOD_ID_OFFSET)); 509 OptCompiledMethod cm = (OptCompiledMethod) CompiledMethods.getCompiledMethod(cmid); 510 511 int firstNonVolatile = cm.getFirstNonVolatileGPR(); 512 int nonVolatiles = cm.getNumberOfNonvolatileGPRs(); 513 int nonVolatileOffset = cm.getUnsignedNonVolatileOffset() + (nonVolatiles - 1) * BYTES_IN_STACKSLOT; 514 515 VM.sysWriteln("stack of " + cm.getMethod()); 516 VM.sysWriteln(" fp offset ", fpOffset); 517 VM.sysWriteln(" NV area offset ", nonVolatileOffset); 518 VM.sysWriteln(" first NV GPR ", firstNonVolatile); 519 520 Address aFP = Magic.objectAsAddress(stack).plus(fpOffset); 521 for (Address a = aFP.plus(nonVolatileOffset); a.GE(aFP); a = a.minus(BYTES_IN_STACKSLOT)) { 522 Word content = a.loadWord(); 523 VM.sysWriteHex(a); 524 VM.sysWrite(" "); 525 VM.sysWrite(content); 526 VM.sysWriteln(); 527 } 528 } 529 530 @SuppressWarnings("unused") 531 private static void dumpRegisterContent(WordArray gprs) { 532 for (GPR reg : GPR.values()) { 533 VM.sysWrite(reg.toString()); 534 VM.sysWrite(" = "); 535 VM.sysWriteln(gprs.get(reg.value())); 536 } 537 } 538 539 /* walk on stack frame, print out methods 540 */ 541 private static void walkOnStack(byte[] stack, Offset fpOffset) { 542 VM.disableGC(); 543 544 Address fp = Magic.objectAsAddress(stack).plus(fpOffset); 545 546 while (Magic.getCallerFramePointer(fp).NE(STACKFRAME_SENTINEL_FP)) { 547 int cmid = Magic.getCompiledMethodID(fp); 548 549 if (cmid == INVISIBLE_METHOD_ID) { 550 VM.sysWriteln(" invisible method "); 551 } else { 552 CompiledMethod cm = CompiledMethods.getCompiledMethod(cmid); 553 fpOffset = fp.diff(Magic.objectAsAddress(stack)); 554 VM.enableGC(); 555 556 VM.sysWriteln(cm.getMethod().toString()); 557 558 VM.disableGC(); 559 fp = Magic.objectAsAddress(stack).plus(fpOffset); 560 if (cm.getMethod().getDeclaringClass().hasBridgeFromNativeAnnotation()) { 561 fp = RuntimeEntrypoints.unwindNativeStackFrame(fp); 562 } 563 } 564 565 fp = Magic.getCallerFramePointer(fp); 566 } 567 568 VM.enableGC(); 569 } 570}