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