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 public final void setupIterator(CompiledMethod cm, Offset instructionOffset, Address framePtr) {
098 if (DEBUG) {
099 VM.sysWrite("\n\t ==========================\n");
100 VM.sysWrite("Reference map request made");
101 VM.sysWrite(" for machine code offset: ");
102 VM.sysWrite(instructionOffset);
103 VM.sysWrite("\n");
104 VM.sysWrite("\tframePtr: ");
105 VM.sysWrite(framePtr);
106 VM.sysWrite("\n");
107 }
108
109 reset();
110
111 // retrieve and save the corresponding OptMachineCodeMap for
112 // this method and instructionOffset
113 compiledMethod = (OptCompiledMethod) cm;
114 map = compiledMethod.getMCMap();
115 mapIndex = map.findGCMapIndex(instructionOffset);
116 if (mapIndex == OptGCMap.ERROR) {
117 if (instructionOffset.sLT(Offset.zero())) {
118 VM.sysWriteln("OptGenericGCMapIterator.setupIterator called with negative instructionOffset",
119 instructionOffset);
120 } else {
121 Offset possibleLen =
122 Offset.fromIntZeroExtend(cm.numberOfInstructions() << ArchitectureSpecific.RegisterConstants
123 .LG_INSTRUCTION_WIDTH);
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 public final Address getNextReferenceAddress() {
185 if (DEBUG) { VM.sysWrite(" next => "); }
186
187 // make sure we have a map entry to look at
188 if (mapIndex == OptGCMap.NO_MAP_ENTRY) {
189 if (DEBUG) {
190 VM.sysWrite(" No Map, returning 0\n");
191 }
192 if (lookForMissedReferencesInRegs) {
193 checkAllRegistersForMissedReferences();
194 }
195
196 // make sure we update the registerLocations before returning!
197 updateLocateRegisters();
198 return Address.zero();
199 }
200
201 // Have we gone through all the registers yet?
202 if (currentRegisterIsValid()) {
203 // See if there are any more
204 while (currentRegisterIsValid() && !map.registerIsSet(mapIndex, getCurrentRegister())) {
205 if (lookForMissedReferencesInRegs) {
206 // inspect the register we are skipping
207 checkCurrentRegisterForMissedReferences();
208 }
209 updateCurrentRegister();
210 }
211
212 // If we found a register, return the value
213 if (currentRegisterIsValid()) {
214 Address regLocation;
215 // currentRegister contains a reference, return that location
216 regLocation = registerLocations.get(getCurrentRegister()).toAddress();
217 if (DEBUG) {
218 VM.sysWrite(" *** Ref found in reg#");
219 VM.sysWrite(getCurrentRegister());
220 VM.sysWrite(", location ==>");
221 VM.sysWrite(regLocation);
222 VM.sysWrite(", contents ==>");
223 VM.sysWrite(regLocation.loadWord());
224 VM.sysWrite("\n");
225 }
226
227 // update for the next call to this routine
228 updateCurrentRegister();
229 return regLocation;
230 }
231 }
232
233 // we already processes the registers, check to see if there are any
234 // references in spill locations.
235 // To do this we request the nextLocation from the ref map.
236 // If it returns a non-sentinel value we have a reference is a spill.
237 mapIndex = map.nextLocation(mapIndex);
238 if (mapIndex == OptGCMap.NO_MAP_ENTRY) {
239 if (DEBUG) {
240 VM.sysWrite(" No more to return, returning 0\n");
241 }
242
243 if (lookForMissedReferencesInSpills) {
244 if (spillLoc.isZero()) {
245 // Didn't have any refs in spill locations, so we should
246 // check for spills among the whole spill area
247 checkForMissedSpills(Address.zero(), Address.zero());
248 } else {
249 // check for spills after the last one we saw
250 checkForMissedSpills(spillLoc, Address.zero());
251 }
252 }
253
254 // OK, we are done returning references for this GC point/method/FP
255 // so now we must update the LocateRegister array for the next
256 // stack frame
257 updateLocateRegisters();
258 return Address.zero();
259 } else {
260 // Determine the spill location given the frame ptr and spill offset.
261 // (The location of spills varies among architectures.)
262 Address newSpillLoc = getStackLocation(framePtr, map.gcMapInformation(mapIndex));
263
264 if (DEBUG) {
265 VM.sysWrite(" *** Ref found in Spill Loc: ");
266 VM.sysWrite(newSpillLoc);
267 VM.sysWrite(", offset: ");
268 VM.sysWrite(map.gcMapInformation(mapIndex));
269 VM.sysWrite(", value ==>");
270 VM.sysWrite(newSpillLoc.loadWord());
271 VM.sysWrite("\n");
272 }
273
274 if (lookForMissedReferencesInSpills) {
275 checkForMissedSpills(spillLoc, newSpillLoc);
276 }
277
278 spillLoc = newSpillLoc;
279 // found another ref, return it
280 return spillLoc;
281 }
282 }
283
284 /**
285 * This method is called repeatedly to process derived pointers related
286 * to JSRs. (They are pointers to code and need to be updated if the
287 * code moves.)
288 * @return the next code pointer or 0 if no more exist
289 */
290 public final Address getNextReturnAddressAddress() {
291 // Since the Opt compiler inlines JSRs, this method will always return 0
292 // signaling the end of the list of such pointers.
293 if (DEBUG) {
294 VM.sysWrite("\t\t getNextReturnAddressOffset returning 0\n");
295 }
296 return Address.zero();
297 }
298
299 /**
300 * scan of this frame is complete
301 * clean up any pointers to allow GC to reclaim dead objects
302 */
303 public final void cleanupPointers() {
304 // primitive types aren't worth reinitializing because setUpIterator
305 // will take care of this.
306 map = null;
307 compiledMethod = null;
308 }
309
310 /**
311 * lets GC ask what type of iterator without using instanceof which can
312 * cause an allocation
313 */
314 public final int getType() {
315 return CompiledMethod.OPT;
316 }
317
318 /**
319 * Externally visible method called to reset internal state
320 */
321 public final void reset() {
322 currentRegister = FIRST_GCMAP_REG;
323 spillLoc = Address.zero();
324 }
325
326 /**
327 * return the current register we are processing
328 * @return the current register we are processing
329 */
330 public final int getCurrentRegister() {
331 return currentRegister;
332 }
333
334 /**
335 * update the state of the current register we are processing
336 */
337 public final void updateCurrentRegister() {
338 currentRegister++;
339 }
340
341 /**
342 * Determines if the value of "currentRegister" is valid, or if we
343 * processed all registers
344 * @return whether the currentRegister is valid
345 */
346 public final boolean currentRegisterIsValid() {
347 return currentRegister <= LAST_GCMAP_REG;
348 }
349
350 /**
351 * If any non-volatile gprs were saved by the method being processed
352 * then update the registerLocations array with the locations where the
353 * registers were saved.
354 */
355 protected abstract void updateLocateRegisters();
356
357 /**
358 * Determine the stack location given the frame ptr and spill offset.
359 * (The offset direction varies among architectures.)
360 * @param framePtr the frame pointer
361 * @param offset the offset
362 * @return the resulting stack location
363 */
364 public abstract Address getStackLocation(Address framePtr, int offset);
365
366 /**
367 * Get address of the first spill location
368 * (The location of spills varies among architectures.)
369 * @return the first spill location
370 */
371 public abstract Address getFirstSpillLoc();
372
373 /**
374 * Get address of the last spill location
375 * (The location of spills varies among architectures.)
376 * @return the last spill location
377 */
378 public abstract Address getLastSpillLoc();
379
380 /**
381 * This method inspects the "current" register for values that look like refs.
382 */
383 final void checkCurrentRegisterForMissedReferences() {
384 int currentReg = getCurrentRegister();
385 if (VERBOSE) {
386 VM.sysWrite(" Inspecting Regs: ");
387 VM.sysWrite(currentReg);
388 VM.sysWrite("\n");
389 }
390 checkRegistersForMissedReferences(currentReg, currentReg);
391 }
392
393 /**
394 * This method inspects all the registers for values that look like refs.
395 */
396 final void checkAllRegistersForMissedReferences() {
397 if (VERBOSE) {
398 VM.sysWrite(" Inspecting Regs: ");
399 VM.sysWrite(FIRST_GCMAP_REG);
400 VM.sysWrite(" ... ");
401 VM.sysWrite(LAST_GCMAP_REG);
402 VM.sysWrite("\n");
403 }
404 checkRegistersForMissedReferences(FIRST_GCMAP_REG, LAST_GCMAP_REG);
405 }
406
407 /**
408 * This method inspects the registers from firstReg to lastReg (inclusive)
409 * for values that look like pointers.
410 * @param firstReg first reg to check
411 * @param lastReg last reg to check
412 */
413 final void checkRegistersForMissedReferences(int firstReg, int lastReg) {
414 for (int i = firstReg; i <= lastReg; i++) {
415 Address regLocation = registerLocations.get(i).toAddress();
416 Address regValue = regLocation.loadAddress();
417 if (MemoryManager.addressInVM(regValue)) {
418 VM.sysWrite(" reg#", getCurrentRegister());
419 VM.sysWrite(", location ==>", regLocation);
420 VM.sysWriteln(", suspicious value ==>", regValue);
421 }
422 }
423 }
424
425 /**
426 * This method inspects spill locations between the parameters passed
427 * to determine if they look like heap points
428 * If the first parameter is 0, it looks from the beginning of the frame
429 * until new.
430 * @param ref1 the last spill found as a reference
431 * @param ref2 the next spill found as a reference
432 */
433 final void checkForMissedSpills(Address ref1, Address ref2) {
434 if (ref1.isZero()) {
435 // Search from start of spill area
436 ref1 = getFirstSpillLoc();
437 if (DEBUG) {
438 VM.sysWrite("Updated, ref1: ");
439 VM.sysWrite(ref1);
440 VM.sysWrite("\n");
441 }
442 }
443
444 if (ref2.isZero()) {
445 // Search up to end of spill area
446 ref2 = getLastSpillLoc();
447 if (DEBUG) {
448 VM.sysWrite("Updated, ref2: ");
449 VM.sysWrite(ref2);
450 VM.sysWrite("\n");
451 }
452 }
453
454 // since different archs will have the relative order of ref1, ref2
455 // differently, we normalize them by ensuring that ref1 < ref2;
456 if (ref1.GT(ref2)) {
457 Address tmp = ref1;
458 ref1 = ref2;
459 ref2 = tmp;
460 }
461
462 for (Address i = ref1.plus(BYTES_IN_ADDRESS); i.LT(ref2); i = i.plus(BYTES_IN_ADDRESS)) {
463 Address ptr = i.loadAddress();
464 if (DEBUG) {
465 VM.sysWrite(" Inspecting Spill: ");
466 VM.sysWrite(i);
467 VM.sysWrite(" with value ==>");
468 VM.sysWrite(ptr);
469 VM.sysWrite("\n");
470 }
471
472 if (MemoryManager.addressInVM(ptr)) {
473 VM.sysWrite(" spill location:");
474 VM.sysWrite(i);
475 VM.sysWrite(" contains a suspicious value ==>");
476 VM.sysWrite(ptr);
477 VM.sysWrite("\n");
478 VM.sysWrite("FramePtr: ");
479 VM.sysWrite(framePtr);
480 VM.sysWrite("\tFirst Spill: ");
481 VM.sysWrite(getFirstSpillLoc());
482 VM.sysWrite("\tLast Spill: ");
483 VM.sysWrite(getLastSpillLoc());
484 VM.sysWrite("\n");
485 }
486 }
487 }
488 }
489