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}