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