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