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.compilers.opt.runtimesupport; 014 015 import org.jikesrvm.ArchitectureSpecific; 016 import org.jikesrvm.VM; 017 import org.jikesrvm.Constants; 018 import org.jikesrvm.ArchitectureSpecificOpt.OptGCMapIteratorConstants; 019 import org.jikesrvm.compilers.common.CompiledMethod; 020 import org.jikesrvm.compilers.common.CompiledMethods; 021 import org.jikesrvm.mm.mminterface.GCMapIterator; 022 import org.jikesrvm.mm.mminterface.MemoryManager; 023 import org.jikesrvm.runtime.Magic; 024 import org.vmmagic.pragma.Uninterruptible; 025 import org.vmmagic.unboxed.Address; 026 import org.vmmagic.unboxed.Offset; 027 import org.vmmagic.unboxed.WordArray; 028 029 /** 030 * This class contains its architecture-independent code for iteration 031 * across the references represented by a frame built by the OPT compiler. 032 * 033 * @see org.jikesrvm.ArchitectureSpecificOpt.OptGCMapIterator 034 */ 035 @Uninterruptible 036 public abstract class OptGenericGCMapIterator extends GCMapIterator 037 implements OptGCMapIteratorConstants, Constants { 038 039 /** 040 * The compiled method 041 */ 042 protected OptCompiledMethod compiledMethod; 043 044 /** 045 * The GC map for this method 046 */ 047 private OptMachineCodeMap map; 048 049 /** 050 * Used to index into the GC map 051 */ 052 private int mapIndex; 053 054 /** 055 * This shows which register to inspect and report on. 056 * If it is bigger than LAST_GCMAP_REG than we should look at the spills 057 */ 058 private int currentRegister; 059 060 /** 061 * This caches the spill location, so that we can check for missed refs 062 * hiding in spills 063 */ 064 private Address spillLoc; 065 066 /** 067 * just used for debugging, all output statements use VM.syswrite 068 */ 069 private static final boolean DEBUG = false; 070 071 /** 072 * just used for verbose debugging, all output statements use VM.syswrite 073 */ 074 static final boolean VERBOSE = false; 075 076 /** 077 * when set to true, all registers and spills will be inspected for 078 * values that look like references. 079 * 080 * THIS CAN BE COSTLY. USE WITH CARE 081 */ 082 static final boolean lookForMissedReferencesInRegs = false; 083 static final boolean lookForMissedReferencesInSpills = false; 084 085 // Constructor 086 protected OptGenericGCMapIterator(WordArray registerLocations) { 087 super(); 088 this.registerLocations = registerLocations; 089 } 090 091 /** 092 * Initialize the iterator for another stack frame scan 093 * @param cm The compiled method we are interested in 094 * @param instructionOffset The place in the method where we currently are 095 * @param framePtr The current frame pointer 096 */ 097 @Override 098 public final void setupIterator(CompiledMethod cm, Offset instructionOffset, Address framePtr) { 099 if (DEBUG) { 100 VM.sysWrite("\n\t ==========================\n"); 101 VM.sysWrite("Reference map request made"); 102 VM.sysWrite(" for machine code offset: "); 103 VM.sysWrite(instructionOffset); 104 VM.sysWrite("\n"); 105 VM.sysWrite("\tframePtr: "); 106 VM.sysWrite(framePtr); 107 VM.sysWrite("\n"); 108 } 109 110 reset(); 111 112 // retrieve and save the corresponding OptMachineCodeMap for 113 // this method and instructionOffset 114 compiledMethod = (OptCompiledMethod) cm; 115 map = compiledMethod.getMCMap(); 116 mapIndex = map.findGCMapIndex(instructionOffset); 117 if (mapIndex == OptGCMap.ERROR) { 118 if (instructionOffset.sLT(Offset.zero())) { 119 VM.sysWriteln("OptGenericGCMapIterator.setupIterator called with negative instructionOffset", 120 instructionOffset); 121 } else { 122 Offset possibleLen = 123 Offset.fromIntZeroExtend(cm.numberOfInstructions() << ArchitectureSpecific.RegisterConstants 124 .LG_INSTRUCTION_WIDTH); 125 if (possibleLen.sLT(instructionOffset)) { 126 VM.sysWriteln("OptGenericGCMapIterator.setupIterator called with too big of an instructionOffset"); 127 VM.sysWriteln("offset is", instructionOffset); 128 VM.sysWriteln(" bytes of machine code for method ", possibleLen); 129 } else { 130 VM.sysWriteln( 131 "OptGenericGCMapIterator.setupIterator called with apparently valid offset, but no GC map found!"); 132 VM.sysWrite("Method: "); 133 VM.sysWrite(compiledMethod.getMethod()); 134 VM.sysWrite(", Machine Code (MC) Offset: "); 135 VM.sysWriteln(instructionOffset); 136 VM.sysFail("OptGenericMapIterator: findGCMapIndex failed\n"); 137 } 138 } 139 VM.sysWrite("Supposed method: "); 140 VM.sysWrite(compiledMethod.getMethod()); 141 VM.sysWriteln("\nBase of its code array", Magic.objectAsAddress(cm.getEntryCodeArray())); 142 Address ra = cm.getInstructionAddress(instructionOffset); 143 VM.sysWriteln("Calculated actual return address is ", ra); 144 CompiledMethod realCM = CompiledMethods.findMethodForInstruction(ra); 145 if (realCM == null) { 146 VM.sysWriteln("Unable to find compiled method corresponding to this return address"); 147 } else { 148 VM.sysWrite("Found compiled method "); 149 VM.sysWrite(realCM.getMethod()); 150 VM.sysWriteln(" whose code contains this return address"); 151 } 152 VM.sysFail("OptGenericMapIterator: setupIterator failed\n"); 153 } 154 155 // save the frame pointer 156 this.framePtr = framePtr; 157 158 if (DEBUG) { 159 VM.sysWrite("\tMethod: "); 160 VM.sysWrite(compiledMethod.getMethod()); 161 VM.sysWrite("\n "); 162 163 if (mapIndex == OptGCMap.NO_MAP_ENTRY) { 164 VM.sysWrite("... empty map found\n"); 165 } else { 166 VM.sysWrite("... found a map\n"); 167 } 168 169 if (lookForMissedReferencesInSpills) { 170 VM.sysWrite("FramePtr: "); 171 VM.sysWrite(framePtr); 172 VM.sysWrite("\tFirst Spill: "); 173 VM.sysWrite(getFirstSpillLoc()); 174 VM.sysWrite("\tLast Spill: "); 175 VM.sysWrite(getLastSpillLoc()); 176 VM.sysWrite("\n"); 177 } 178 } 179 } 180 181 /** 182 * Returns the next address that contains a reference 183 * @return the value of the next reference 184 */ 185 @Override 186 public final Address getNextReferenceAddress() { 187 if (DEBUG) { VM.sysWrite(" next => "); } 188 189 // make sure we have a map entry to look at 190 if (mapIndex == OptGCMap.NO_MAP_ENTRY) { 191 if (DEBUG) { 192 VM.sysWrite(" No Map, returning 0\n"); 193 } 194 if (lookForMissedReferencesInRegs) { 195 checkAllRegistersForMissedReferences(); 196 } 197 198 // make sure we update the registerLocations before returning! 199 updateLocateRegisters(); 200 return Address.zero(); 201 } 202 203 // Have we gone through all the registers yet? 204 if (currentRegisterIsValid()) { 205 // See if there are any more 206 while (currentRegisterIsValid() && !map.registerIsSet(mapIndex, getCurrentRegister())) { 207 if (lookForMissedReferencesInRegs) { 208 // inspect the register we are skipping 209 checkCurrentRegisterForMissedReferences(); 210 } 211 updateCurrentRegister(); 212 } 213 214 // If we found a register, return the value 215 if (currentRegisterIsValid()) { 216 Address regLocation; 217 // currentRegister contains a reference, return that location 218 regLocation = registerLocations.get(getCurrentRegister()).toAddress(); 219 if (DEBUG) { 220 VM.sysWrite(" *** Ref found in reg#"); 221 VM.sysWrite(getCurrentRegister()); 222 VM.sysWrite(", location ==>"); 223 VM.sysWrite(regLocation); 224 VM.sysWrite(", contents ==>"); 225 VM.sysWrite(regLocation.loadWord()); 226 VM.sysWrite("\n"); 227 } 228 229 // update for the next call to this routine 230 updateCurrentRegister(); 231 return regLocation; 232 } 233 } 234 235 // we already processes the registers, check to see if there are any 236 // references in spill locations. 237 // To do this we request the nextLocation from the ref map. 238 // If it returns a non-sentinel value we have a reference is a spill. 239 mapIndex = map.nextLocation(mapIndex); 240 if (mapIndex == OptGCMap.NO_MAP_ENTRY) { 241 if (DEBUG) { 242 VM.sysWrite(" No more to return, returning 0\n"); 243 } 244 245 if (lookForMissedReferencesInSpills) { 246 if (spillLoc.isZero()) { 247 // Didn't have any refs in spill locations, so we should 248 // check for spills among the whole spill area 249 checkForMissedSpills(Address.zero(), Address.zero()); 250 } else { 251 // check for spills after the last one we saw 252 checkForMissedSpills(spillLoc, Address.zero()); 253 } 254 } 255 256 // OK, we are done returning references for this GC point/method/FP 257 // so now we must update the LocateRegister array for the next 258 // stack frame 259 updateLocateRegisters(); 260 return Address.zero(); 261 } else { 262 // Determine the spill location given the frame ptr and spill offset. 263 // (The location of spills varies among architectures.) 264 Address newSpillLoc = getStackLocation(framePtr, map.gcMapInformation(mapIndex)); 265 266 if (DEBUG) { 267 VM.sysWrite(" *** Ref found in Spill Loc: "); 268 VM.sysWrite(newSpillLoc); 269 VM.sysWrite(", offset: "); 270 VM.sysWrite(map.gcMapInformation(mapIndex)); 271 VM.sysWrite(", value ==>"); 272 VM.sysWrite(newSpillLoc.loadWord()); 273 VM.sysWrite("\n"); 274 } 275 276 if (lookForMissedReferencesInSpills) { 277 checkForMissedSpills(spillLoc, newSpillLoc); 278 } 279 280 spillLoc = newSpillLoc; 281 // found another ref, return it 282 return spillLoc; 283 } 284 } 285 286 /** 287 * This method is called repeatedly to process derived pointers related 288 * to JSRs. (They are pointers to code and need to be updated if the 289 * code moves.) 290 * @return the next code pointer or 0 if no more exist 291 */ 292 @Override 293 public final Address getNextReturnAddressAddress() { 294 // Since the Opt compiler inlines JSRs, this method will always return 0 295 // signaling the end of the list of such pointers. 296 if (DEBUG) { 297 VM.sysWrite("\t\t getNextReturnAddressOffset returning 0\n"); 298 } 299 return Address.zero(); 300 } 301 302 /** 303 * scan of this frame is complete 304 * clean up any pointers to allow GC to reclaim dead objects 305 */ 306 @Override 307 public final void cleanupPointers() { 308 // primitive types aren't worth reinitializing because setUpIterator 309 // will take care of this. 310 map = null; 311 compiledMethod = null; 312 } 313 314 @Override 315 public final int getType() { 316 return CompiledMethod.OPT; 317 } 318 319 /** 320 * Externally visible method called to reset internal state 321 */ 322 @Override 323 public final void reset() { 324 currentRegister = FIRST_GCMAP_REG; 325 spillLoc = Address.zero(); 326 } 327 328 /** 329 * return the current register we are processing 330 * @return the current register we are processing 331 */ 332 public final int getCurrentRegister() { 333 return currentRegister; 334 } 335 336 /** 337 * update the state of the current register we are processing 338 */ 339 public final void updateCurrentRegister() { 340 currentRegister++; 341 } 342 343 /** 344 * Determines if the value of "currentRegister" is valid, or if we 345 * processed all registers 346 * @return whether the currentRegister is valid 347 */ 348 public final boolean currentRegisterIsValid() { 349 return currentRegister <= LAST_GCMAP_REG; 350 } 351 352 /** 353 * If any non-volatile gprs were saved by the method being processed 354 * then update the registerLocations array with the locations where the 355 * registers were saved. 356 */ 357 protected abstract void updateLocateRegisters(); 358 359 /** 360 * Determine the stack location given the frame ptr and spill offset. 361 * (The offset direction varies among architectures.) 362 * @param framePtr the frame pointer 363 * @param offset the offset 364 * @return the resulting stack location 365 */ 366 public abstract Address getStackLocation(Address framePtr, int offset); 367 368 /** 369 * Get address of the first spill location 370 * (The location of spills varies among architectures.) 371 * @return the first spill location 372 */ 373 public abstract Address getFirstSpillLoc(); 374 375 /** 376 * Get address of the last spill location 377 * (The location of spills varies among architectures.) 378 * @return the last spill location 379 */ 380 public abstract Address getLastSpillLoc(); 381 382 /** 383 * This method inspects the "current" register for values that look like refs. 384 */ 385 final void checkCurrentRegisterForMissedReferences() { 386 int currentReg = getCurrentRegister(); 387 if (VERBOSE) { 388 VM.sysWrite(" Inspecting Regs: "); 389 VM.sysWrite(currentReg); 390 VM.sysWrite("\n"); 391 } 392 checkRegistersForMissedReferences(currentReg, currentReg); 393 } 394 395 /** 396 * This method inspects all the registers for values that look like refs. 397 */ 398 final void checkAllRegistersForMissedReferences() { 399 if (VERBOSE) { 400 VM.sysWrite(" Inspecting Regs: "); 401 VM.sysWrite(FIRST_GCMAP_REG); 402 VM.sysWrite(" ... "); 403 VM.sysWrite(LAST_GCMAP_REG); 404 VM.sysWrite("\n"); 405 } 406 checkRegistersForMissedReferences(FIRST_GCMAP_REG, LAST_GCMAP_REG); 407 } 408 409 /** 410 * This method inspects the registers from firstReg to lastReg (inclusive) 411 * for values that look like pointers. 412 * @param firstReg first reg to check 413 * @param lastReg last reg to check 414 */ 415 final void checkRegistersForMissedReferences(int firstReg, int lastReg) { 416 for (int i = firstReg; i <= lastReg; i++) { 417 Address regLocation = registerLocations.get(i).toAddress(); 418 Address regValue = regLocation.loadAddress(); 419 if (MemoryManager.addressInVM(regValue)) { 420 VM.sysWrite(" reg#", getCurrentRegister()); 421 VM.sysWrite(", location ==>", regLocation); 422 VM.sysWriteln(", suspicious value ==>", regValue); 423 } 424 } 425 } 426 427 /** 428 * This method inspects spill locations between the parameters passed 429 * to determine if they look like heap points 430 * If the first parameter is 0, it looks from the beginning of the frame 431 * until new. 432 * @param ref1 the last spill found as a reference 433 * @param ref2 the next spill found as a reference 434 */ 435 final void checkForMissedSpills(Address ref1, Address ref2) { 436 if (ref1.isZero()) { 437 // Search from start of spill area 438 ref1 = getFirstSpillLoc(); 439 if (DEBUG) { 440 VM.sysWrite("Updated, ref1: "); 441 VM.sysWrite(ref1); 442 VM.sysWrite("\n"); 443 } 444 } 445 446 if (ref2.isZero()) { 447 // Search up to end of spill area 448 ref2 = getLastSpillLoc(); 449 if (DEBUG) { 450 VM.sysWrite("Updated, ref2: "); 451 VM.sysWrite(ref2); 452 VM.sysWrite("\n"); 453 } 454 } 455 456 // since different archs will have the relative order of ref1, ref2 457 // differently, we normalize them by ensuring that ref1 < ref2; 458 if (ref1.GT(ref2)) { 459 Address tmp = ref1; 460 ref1 = ref2; 461 ref2 = tmp; 462 } 463 464 for (Address i = ref1.plus(BYTES_IN_ADDRESS); i.LT(ref2); i = i.plus(BYTES_IN_ADDRESS)) { 465 Address ptr = i.loadAddress(); 466 if (DEBUG) { 467 VM.sysWrite(" Inspecting Spill: "); 468 VM.sysWrite(i); 469 VM.sysWrite(" with value ==>"); 470 VM.sysWrite(ptr); 471 VM.sysWrite("\n"); 472 } 473 474 if (MemoryManager.addressInVM(ptr)) { 475 VM.sysWrite(" spill location:"); 476 VM.sysWrite(i); 477 VM.sysWrite(" contains a suspicious value ==>"); 478 VM.sysWrite(ptr); 479 VM.sysWrite("\n"); 480 VM.sysWrite("FramePtr: "); 481 VM.sysWrite(framePtr); 482 VM.sysWrite("\tFirst Spill: "); 483 VM.sysWrite(getFirstSpillLoc()); 484 VM.sysWrite("\tLast Spill: "); 485 VM.sysWrite(getLastSpillLoc()); 486 VM.sysWrite("\n"); 487 } 488 } 489 } 490 } 491