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