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.baseline;
014
015import org.jikesrvm.VM;
016import org.jikesrvm.architecture.ArchConstants;
017import org.jikesrvm.classloader.RVMArray;
018import org.jikesrvm.classloader.RVMMethod;
019import org.jikesrvm.classloader.NormalMethod;
020import org.jikesrvm.classloader.TypeReference;
021import org.jikesrvm.scheduler.SpinLock;
022import org.vmmagic.pragma.Interruptible;
023import org.vmmagic.pragma.Uninterruptible;
024import org.vmmagic.unboxed.Offset;
025
026/**
027 * class that provides stack (and local var) map for a baseline compiled method
028 * GC uses the methods provided here
029 */
030@Uninterruptible
031public final class ReferenceMaps {
032
033  public static final byte JSR_MASK = -128;     // byte = x'80'
034  public static final byte JSR_INDEX_MASK = 0x7F;
035
036  public static final int STARTINDEX = 0;
037  public static final int NOMORE = 0;
038  /** Kinds of merge operation when merging delta maps into table maps */
039  private enum MergeOperation {
040    OR, NAND, COPY
041  }
042
043  /** Serializes JSR processing */
044  public static final SpinLock jsrLock = new SpinLock();   // for serialization of JSR processing
045
046  /** Number of bits in each map element */
047  private static final int BITS_PER_MAP_ELEMENT = 8;
048  private byte[] referenceMaps;
049  private int[] MCSites;
050  /** Number of bits in each map */
051  private final int bitsPerMap;
052  /** Number of maps */
053  private int mapCount;
054  private JSRInfo jsrInfo;
055  /** identify which block a byte is part of */
056  final short[] byteToBlockMap;
057
058  /**
059   * @return size of individual maps
060   */
061  private int bytesPerMap() {
062    return ((bitsPerMap + 7) / 8) + 1;
063  }
064
065  ReferenceMaps(BaselineCompiledMethod cm, int[] stackHeights, byte[] localTypes) {
066
067    NormalMethod method = (NormalMethod) cm.getMethod();
068    // save input information and compute related data
069    this.bitsPerMap = (method.getLocalWords() + method.getOperandWords() + 1); // +1 for jsr bit
070
071    //   this.startLocal0Offset = BaselineCompilerImpl.getStartLocalOffset(method);
072
073    if (VM.TraceStkMaps) {
074      VM.sysWrite("ReferenceMaps constructor. Method name is:");
075      VM.sysWrite(method.getName());
076      VM.sysWrite(" -Class name is :");
077      VM.sysWrite(method.getDeclaringClass().getDescriptor());
078      VM.sysWrite("\n");
079      VM.sysWrite(" bytesPerMap = ", bytesPerMap());
080      VM.sysWrite(" - bitsPerMap = ", bitsPerMap);
081//      VM.sysWriteln(" - startLocal0Offset = ", startLocal0Offset);
082    }
083
084    // define the basic blocks
085    BuildBB buildBB = new BuildBB(method);
086    byteToBlockMap = buildBB.byteToBlockMap;
087
088    BuildReferenceMaps buildRefMaps = new BuildReferenceMaps();
089    buildRefMaps.buildReferenceMaps(method, stackHeights, localTypes, this, buildBB);
090
091    if (VM.ReferenceMapsBitStatistics) {
092      showReferenceMapStatistics(method);
093    }
094  }
095
096  /**
097   * Given a machine code instruction offset, return an index to
098   * identify the stack map closest to the offset ( but not beyond).<p>
099   *
100   * Usage note: "machCodeOffset" must point to the instruction *following*
101   *              the actual instruction
102   * whose stack map is sought. This allows us to properly handle the case where
103   * the only address we have to work with is a return address (i.e. from a stackframe)
104   * or an exception address (i.e. from a null pointer dereference, array bounds check,
105   * or divide by zero) on a machine architecture with variable length instructions.
106   * In such situations we'd have no idea how far to back up the instruction pointer
107   * to point to the "call site" or "exception site".<p>
108   *
109   * If the located site is within the scope of a jsr subroutine
110   *  the index value returned is a negative number.
111   *
112   * @param machCodeOffset offset into machine code (see above for constraints)
113   * @param method the method that contains the gc point
114   * @return index of the appropriate stack map
115   */
116  public int locateGCPoint(Offset machCodeOffset, RVMMethod method) {
117
118    machCodeOffset = machCodeOffset.minus(1 << ArchConstants.getLogInstructionWidth());  // this assumes that machCodeOffset points
119    // to "next" instruction eg bal type instruction
120
121    if (VM.TraceStkMaps) {
122      VM.sysWrite("ReferenceMaps-locateGCPoint for machine code offset = ");
123      VM.sysWrite(machCodeOffset);
124      VM.sysWrite("  --- in  method = ");
125      VM.sysWrite(method.getName());
126      VM.sysWrite("\n");
127    }
128
129    //  Scan the list of machine code addresses to find the
130    //  closest site offset BEFORE the input machine code index ( offset in the code)
131    Offset distance = Offset.zero();
132    int index = 0;
133    // get the first possible location
134    for (int i = 0; i < mapCount; i++) {
135      // get an initial non zero distance
136      distance = machCodeOffset.minus(MCSites[i]);
137      if (distance.sGE(Offset.zero())) {
138        index = i;
139        break;
140      }
141    }
142    // scan to find any better location i.e. closer to the site
143    for (int i = index + 1; i < mapCount; i++) {
144      Offset dist = machCodeOffset.minus(MCSites[i]);
145      if (dist.sLT(Offset.zero())) continue;
146      if (dist.sLE(distance)) {
147        index = i;
148        distance = dist;
149      }
150    }
151
152    if (VM.TraceStkMaps) {
153      showInfo();
154      VM.sysWrite(" ReferenceMaps-locateGCPoint located index  = ");
155      VM.sysWrite(index);
156      VM.sysWrite("  byte  = ");
157      VM.sysWrite(referenceMaps[index]);
158      VM.sysWrite("\n");
159      if (index - 1 >= 0) {
160        VM.sysWrite(" MCSites[index-1] = ");
161        VM.sysWrite(machCodeOffset.minus(MCSites[index - 1]));
162        VM.sysWrite("\n");
163      }
164      VM.sysWrite(" MCSites[index  ] = ");
165      VM.sysWrite(machCodeOffset.minus(MCSites[index]));
166      VM.sysWrite("\n");
167      if (index + 1 < MCSites.length) {
168        VM.sysWrite(" MCSites[index+1] = ");
169        VM.sysWrite(machCodeOffset.minus(MCSites[index + 1]));
170        VM.sysWrite("\n");
171      }
172    }
173
174    // test for a site within a jsr subroutine
175    if ((0x000000FF & (referenceMaps[index * bytesPerMap()] & JSR_MASK)) ==
176        (0x000000FF & JSR_MASK)) { // test for jsr map
177      index = -index;                       // indicate site within a jsr to caller
178      if (VM.TraceStkMaps) {
179        VM.sysWrite(" ReferenceMaps-locateGCPoint jsr mapid = ");
180        VM.sysWrite(-index);
181        VM.sysWrite("\n");
182      }
183    }
184
185    if (VM.TraceStkMaps) {
186      VM.sysWrite(" ReferenceMaps-locateGCPoint  machine offset = ");
187      VM.sysWrite(machCodeOffset);
188      VM.sysWrite("  - return map index = ");
189      VM.sysWrite(index);
190      VM.sysWrite("\n");
191    }
192
193    return index;
194  }
195
196  /**
197   * @param index offset in the reference stack frame,
198   * @param siteindex index that indicates the callsite (siteindex),
199   * @return return the offset where the next reference can be found,
200   *  {@link #NOMORE} when no more pointers can be found
201   */
202  public int getNextRefIndex(int index, int siteindex) {
203    if (VM.TraceStkMaps) {
204      VM.sysWrite("ReferenceMaps-getNextRef-inputs index = ");
205      VM.sysWrite(index);
206      VM.sysWrite(" -siteindex = ");
207      VM.sysWrite(siteindex);
208      VM.sysWrite("\n");
209    }
210
211    // use index to locate the gc point of interest
212    if (bytesPerMap() == 0) return 0;           // no map ie no refs
213    int mapindex = siteindex * bytesPerMap();
214
215    int bitnum;
216    if (index == STARTINDEX) {
217      // this is the initial scan for the map
218      int mapByteNum = mapindex;
219      int startbitnumb = 1;      // start search from beginning
220      bitnum = scanForNextRef(startbitnumb, mapByteNum, bitsPerMap, referenceMaps);
221
222      if (VM.TraceStkMaps) {
223        VM.sysWriteln("ReferenceMaps-getNextRef-initial call bitnum = ", bitnum);
224      }
225    } else {
226      // get bitnum and determine mapword to restart scan
227      bitnum = index + 1; // +1 for jsr bit
228
229      if (VM.TraceStkMaps) {
230        VM.sysWriteln("ReferenceMaps-getnextref- not initial- entry index,bitnum  = ", index, " ", bitnum);
231      }
232
233      // scan forward from current position to next ref
234      bitnum = scanForNextRef(bitnum + 1, mapindex, (bitsPerMap - (bitnum - 1)), referenceMaps);
235
236      if (VM.TraceStkMaps) {
237        VM.sysWriteln("ReferenceMaps-getnextref- not initial- scan returned bitnum = ", bitnum);
238      }
239    }
240
241    if (bitnum == NOMORE) {
242      if (VM.TraceStkMaps) VM.sysWriteln("  NOMORE");
243      return NOMORE;
244    } else {
245      int ans = bitnum - 1; //-1 for jsr bit
246      if (VM.TraceStkMaps) VM.sysWriteln("  result = ", ans);
247      return ans;
248    }
249  }
250
251  /**
252   * Given an offset in the jsr reference map,
253   * return the offset where the next returnAddress can be found.<p>
254
255   *  NOTE: There is only one JSR map for the entire method because it has to
256   *       be constructed at GC time and would normally require additional
257   *       storage.
258   * <p>
259   *       To avoid this, the space for one map is pre-allocated and the map
260   *       is built in that space.  When multiple threads exist and if GC runs
261   *       in multiple threads concurrently, then the MethodMap must be locked
262   *       when a JSR map is being scanned.  This should be a low probability
263   *       event.
264   *
265   * @param index offset in the JSR reference map,
266   * @return The offset where the next reference can be found or
267   *  <code>NOMORE</code> when no more pointers can be found
268   *
269   */
270  public int getNextJSRRefIndex(int index) {
271    // user index to locate the gc point of interest
272    if (bytesPerMap() == 0) return 0;           // no map ie no refs
273    int mapword = jsrInfo.mergedReferenceMap;
274
275    int bitnum;
276    if (index == STARTINDEX) {
277      // this is the initial scan for the map
278      int startbitnumb = 1;      // start search from beginning
279      bitnum = scanForNextRef(startbitnumb, mapword, bitsPerMap, jsrInfo.unusualReferenceMaps);
280      if (VM.TraceStkMaps) {
281        VM.sysWrite("ReferenceMaps-getJSRNextRef-initial call - startbitnum =", startbitnumb);
282        VM.sysWrite("  mapword = ", mapword);
283        VM.sysWrite(" bitspermap = ", bitsPerMap);
284        VM.sysWrite("      bitnum = ", bitnum);
285      }
286    } else {
287      // get bitnum and determine mapword to restart scan
288      bitnum = index;  // get the bit number from last time
289
290      // scan forward from current position to next ref
291      if (VM.TraceStkMaps) {
292        VM.sysWrite("ReferenceMaps.getJSRnextref - not initial- starting (index,bitnum) = ");
293        VM.sysWrite(index);
294        VM.sysWrite(", ");
295        VM.sysWrite(bitnum);
296      }
297
298      bitnum = scanForNextRef(bitnum + 1, mapword, (bitsPerMap - (bitnum - 1)), jsrInfo.unusualReferenceMaps);
299    }
300
301    if (bitnum == NOMORE) {
302      if (VM.TraceStkMaps) VM.sysWriteln("  NOMORE");
303      return NOMORE;
304    } else {
305      int ans = bitnum;
306      if (VM.TraceStkMaps) VM.sysWriteln("  result = ", ans);
307      return ans;
308    }
309  }
310
311  /**
312   * Given an offset in the jsr returnAddress map,
313   *   return the offset where the next returnAddress can be found.<p>
314   *
315   * NOTE: there is only one jsr returnAddress map for the entire method because it has to be
316   *       be constructed a GC time and would normally require additional storage.
317   *       To avoid this, the space for one map is pre-allocated and the map
318   *       is built in that space. When multiple threads exist and if GC runs
319   *       in multiple threads concurrently, then the MethodMap must be locked
320   *       when a jsr map is being scanned.
321   *       This should be a low probability event.<p>
322   *
323   * NOTE: return addresses are handled separately from references because they point
324   *       inside an object ( internal pointers)
325   *
326   * @param index offset in the JSR returnAddress map,
327   * @return The offset where the next reference can be found or
328   *  <code>NOMORE</code> when no more pointers can be found
329   */
330  public int getNextJSRReturnAddrIndex(int index) {
331    // use the preallocated map to locate the current point of interest
332    int mapword = jsrInfo.mergedReturnAddressMap;
333    if (bytesPerMap() == 0) {
334      if (VM.TraceStkMaps) {
335        VM.sysWriteln("ReferenceMaps-getJSRNextReturnAddr-initial call no returnaddresses");
336      }
337      return 0;  // no map ie no refs
338    }
339
340    int bitnum;
341    if (index == STARTINDEX) {
342      // this is the initial scan for the map
343      int startbitnumb = 1;      // start search from beginning
344      bitnum = scanForNextRef(startbitnumb, mapword, bitsPerMap, jsrInfo.unusualReferenceMaps);
345      if (VM.TraceStkMaps) {
346        VM.sysWrite("ReferenceMaps-getJSRNextReturnAddr-initial call startbitnum, mapword, bitspermap = ");
347        VM.sysWrite(startbitnumb);
348        VM.sysWrite(" , ");
349        VM.sysWrite(mapword);
350        VM.sysWrite(" , ");
351        VM.sysWrite(bitsPerMap);
352        VM.sysWrite(" \n ");
353        VM.sysWrite("ReferenceMaps-getJSRNextReturnAddr-initial call return bitnum = ");
354        VM.sysWrite(bitnum);
355        VM.sysWrite("\n");
356      }
357    } else {
358      // get bitnum and determine mapword to restart scan
359      bitnum = index;  // get the bit number
360      if (VM.TraceStkMaps) {
361        VM.sysWriteln("ReferenceMaps-getJSRnextReturnAddr- not initial- starting index, starting bitnum  = ",
362                      index,
363                      " ",
364                      bitnum);
365      }
366
367      // scan forward from current position to next ref
368      bitnum = scanForNextRef(bitnum + 1, mapword, (bitsPerMap - (bitnum - 1)), jsrInfo.unusualReferenceMaps);
369
370      if (VM.TraceStkMaps) {
371        VM.sysWriteln("ReferenceMaps-getJSRnextref- not initial- scan returned bitnum = ", bitnum);
372      }
373    }
374
375    if (bitnum == NOMORE) {
376      if (VM.TraceStkMaps) VM.sysWriteln("  NOMORE");
377      return NOMORE;
378    } else {
379      int ans = bitnum;
380      if (VM.TraceStkMaps) VM.sysWrite("ReferenceMaps-getJSRNextReturnAddr-return = ", ans);
381      return ans;
382    }
383  }
384
385  public int getStackDepth(int mapid) {
386    return bytesPerMap();
387  }
388
389  @Interruptible
390  public int size() {
391    int size = TypeReference.ReferenceMaps.peekType().asClass().getInstanceSize();
392    if (MCSites != null) size += RVMArray.IntArray.getInstanceSize(MCSites.length);
393    if (referenceMaps != null) size += RVMArray.ByteArray.getInstanceSize(referenceMaps.length);
394    if (jsrInfo != null && jsrInfo.unusualReferenceMaps != null) {
395      size += RVMArray.JavaLangObjectArray.getInstanceSize(jsrInfo.unusualReferenceMaps.length);
396    }
397    return size;
398  }
399
400  @Interruptible
401  public void startNewMaps(int gcPointCount, int jsrCount, int parameterWords) {
402    //  normal map information
403    mapCount = 0;
404    MCSites = new int[gcPointCount];
405    referenceMaps = new byte[gcPointCount * bytesPerMap()];
406
407    if (VM.TraceStkMaps) {
408      VM.sysWrite("ReferenceMaps-startNewMaps-  gcPointCount =  ");
409      VM.sysWrite(gcPointCount);
410      VM.sysWrite(" -jsrCount = ");
411      VM.sysWrite(jsrCount);
412      VM.sysWrite("\n");
413    }
414
415    if (jsrCount > 0) {
416
417      jsrInfo = new JSRInfo(2 * jsrCount);
418
419      // reserve a map for merging maps
420      jsrInfo.tempIndex = getNextMapElement();
421
422      // reserve map words for merged reference map
423      jsrInfo.mergedReferenceMap = getNextMapElement();
424
425      // reserve map words for merged return address map
426      jsrInfo.mergedReturnAddressMap = getNextMapElement();
427
428      //reserve maps for the jsrInfo.extraUnusualMapObject
429      // the reference map
430      int mapstart = getNextMapElement();
431      jsrInfo.extraUnusualMap.setReferenceMapIndex(mapstart);
432
433      //the set of non reference stores
434      mapstart = getNextMapElement();
435      jsrInfo.extraUnusualMap.setNonReferenceMapIndex(mapstart);
436
437      // the return address map
438      mapstart = getNextMapElement();
439      jsrInfo.extraUnusualMap.setReturnAddressMapIndex(mapstart);
440
441    }
442
443  }
444
445  /**
446   * Given the information about a GC point, record the information in the proper tables.
447   * @param byteindex the index in the bytecode of this site
448   * @param byteMap a byte array that describes the contents of the local variables and the java stack
449   * @param BBLastPtr the last offset of a byte that contains information about the map
450   * @param replacemap whether this map is a replacement for a currently
451   *        existing map
452   */
453  @Interruptible
454  public void recordStkMap(int byteindex, byte[] byteMap, int BBLastPtr, boolean replacemap) {
455
456    int mapNum = 0;
457
458    if (VM.TraceStkMaps) {
459      VM.sysWrite(" ReferenceMaps-recordStkMap bytecode offset = ");
460      VM.sysWrite(byteindex);
461      VM.sysWrite("\n");
462      VM.sysWrite(" input byte map = ");
463      for (int j = 0; j <= BBLastPtr; j++) {
464        VM.sysWrite(byteMap[j]);
465      }
466      VM.sysWrite("\n");
467      if (replacemap) {
468        VM.sysWrite(" ReferenceMaps-recordStkMap- replacing map at byteindex = ");
469        VM.sysWrite(byteindex);
470        VM.sysWrite("\n");
471      }
472    }
473
474    if (replacemap) {
475      // replace a map that already exists in the table
476      //  locate the site
477      for (mapNum = 0; mapNum < mapCount; mapNum++) {
478        if (MCSites[mapNum] == byteindex) {
479          // location found -clear out old map
480          int start = mapNum * bytesPerMap();  // get starting byte in map
481          for (int i = start; i < start + bytesPerMap(); i++) {
482            referenceMaps[i] = 0;
483          }
484          if (VM.TraceStkMaps) {
485            VM.sysWrite(" ReferenceMaps-recordStkMap replacing map number = ", mapNum);
486            VM.sysWriteln("  for machinecode index = ", MCSites[mapNum]);
487          }
488          break;
489        }
490      }
491    } else {
492      // add a map to the table - its a new site
493      //  allocate a new site
494      mapNum = mapCount++;
495      // fill in basic information
496      MCSites[mapNum] = byteindex; // gen and save bytecode offset
497      if (BBLastPtr == -1) return;   // empty map for this gc point
498    }
499
500    if (VM.TraceStkMaps) {
501      VM.sysWrite(" ReferenceMaps-recordStkMap map id  = ");
502      VM.sysWrite(mapNum);
503      VM.sysWrite("\n");
504    }
505
506    // convert Boolean array into array of bits ie create the map
507    int mapslot = mapNum * bytesPerMap();
508    int len = (BBLastPtr + 1);      // get last ptr in map
509    int offset = 0;              // offset from origin
510    int convertLength;                             //to start in the map
511    int word = mapslot;
512
513    // convert first byte of map
514    // get correct length for first map byte - smaller of bits in first byte or size of map
515    if (len < (BITS_PER_MAP_ELEMENT - 1)) {
516      convertLength = len;
517    } else {
518      convertLength = BITS_PER_MAP_ELEMENT - 1;
519    }
520    byte firstByte = convertMapElement(byteMap, offset, convertLength, BuildReferenceMaps.REFERENCE);
521    referenceMaps[word] = (byte) ((0x000000ff & firstByte) >>> 1); // shift for jsr bit ie set it to 0
522
523    if (VM.TraceStkMaps) {
524      VM.sysWrite(" ReferenceMaps-recordStkMap convert first map bytes- byte number = ");
525      VM.sysWrite(word);
526      VM.sysWrite(" byte value in map = ");
527      VM.sysWrite(referenceMaps[word]);
528      VM.sysWrite(" - before shift = ");
529      VM.sysWrite(firstByte);
530      VM.sysWrite("\n");
531    }
532
533    // update indexes for additional bytes
534    word++;                                // next byte in bit map
535    len -= (BITS_PER_MAP_ELEMENT - 1);   // remaining count
536    offset += (BITS_PER_MAP_ELEMENT - 1);   // offset into input array
537
538    // convert remaining byte array to bit array -
539    while (len > 0) {
540      // map takes multiple bytes -convert 1  at a time
541      if (len <= (BITS_PER_MAP_ELEMENT - 1)) {
542        convertLength = len;
543      } else {
544        convertLength = BITS_PER_MAP_ELEMENT;
545      }
546      // map takes multiple bytes -convert 1  at a time
547      referenceMaps[word] = convertMapElement(byteMap, offset, convertLength, BuildReferenceMaps.REFERENCE);
548
549      if (VM.TraceStkMaps) {
550        VM.sysWriteln(" ReferenceMaps-recordStkMap convert another map byte- byte number = ",
551                      word,
552                      " byte value = ",
553                      referenceMaps[word]);
554      }
555
556      len -= BITS_PER_MAP_ELEMENT;                // update remaining words
557      offset += BITS_PER_MAP_ELEMENT;                // and offset
558      word++;
559    }  // end of while
560
561    // update stats
562    if (VM.ReferenceMapsStatistics) {
563      if (!replacemap) {
564      }
565    }
566  }
567
568  /**
569   * Record a map for a point within a JSR Subroutine. This requires setting up one
570   * of the unusual maps.
571   * @param byteindex         index into the byte code array of the point for the map
572   * @param currReferenceMap  map of references and return addresses that were set
573   *                          within the JSR Subroutine
574   * @param BBLastPtr         map runs from -1  to BBLastPtr inclusively
575   * @param returnAddrIndex   Index in the stack where the return address
576   *                            for the jsr routine (in which this gcpoint is located)
577   *                            can be found
578   * @param replacemap        {@code false} if this is the first time this map point has been
579   *                          recorded.
580   */
581  @Interruptible
582  public void recordJSRSubroutineMap(int byteindex, byte[] currReferenceMap, int BBLastPtr, int returnAddrIndex,
583                                     boolean replacemap) {
584    int mapNum = 0;
585    int unusualMapIndex = 0;
586    int internalReturnIndex;
587    UnusualMaps jsrSiteMap;
588
589    if (replacemap) {
590      // update an already existing map
591      //  locate existing site  in table
592      jsrSiteMap = null;
593      findJSRSiteMap:
594      for (mapNum = 0; mapNum < mapCount; mapNum++) {
595        if (MCSites[mapNum] == byteindex) {
596          // GC site found - get index in unusual map table and the unusual Map
597          unusualMapIndex = JSR_INDEX_MASK & referenceMaps[mapNum * bytesPerMap()];
598          internalReturnIndex = returnAddrIndex - 1; //-1 for jsrbit
599          if (unusualMapIndex == JSR_INDEX_MASK) {
600            // greater than 127 jsrInfo.unusualMaps- sequential scan of locate others unusual map
601            for (unusualMapIndex = JSR_INDEX_MASK; unusualMapIndex < jsrInfo.numberUnusualMaps; unusualMapIndex++) {
602              if (jsrInfo.unusualMaps[unusualMapIndex].getReturnAddressIndex() == internalReturnIndex) {
603                jsrSiteMap = jsrInfo.unusualMaps[unusualMapIndex];
604                break findJSRSiteMap;
605              }
606            }
607            VM.sysFail(" can't find unusual map !!!!!!! - should never occur");
608          } else {
609            jsrSiteMap = jsrInfo.unusualMaps[unusualMapIndex];
610            break;
611          }
612        }
613      }
614    } else {
615      // new map, add to end of table
616      mapNum = mapCount++;          // get slot and update count
617      MCSites[mapNum] = byteindex;  // gen and save bytecode offset
618
619      // generate an UnusualMap for the site
620      jsrSiteMap = new UnusualMaps();
621
622      // add unusual map to UnusualMap table (table may need to be expanded)
623      unusualMapIndex = addUnusualMap(jsrSiteMap);
624
625      // set back pointer i.e. pointer from unusual maps back into referencemaps
626      jsrSiteMap.setNormalMapIndex(mapNum);
627
628      // setup index in reference maps
629      if (unusualMapIndex > JSR_INDEX_MASK) {
630        unusualMapIndex = JSR_INDEX_MASK;
631      }
632      referenceMaps[mapNum * bytesPerMap()] = (byte) ((byte) unusualMapIndex | JSR_MASK);
633
634      // setup new unusual Map
635      internalReturnIndex = returnAddrIndex - 1 + 2; // -1 for jsrbit  +2  to convert to our index
636      jsrSiteMap.setReturnAddressIndex(internalReturnIndex);
637
638      if (VM.TraceStkMaps) {
639        VM.sysWrite("ReferenceMaps-recordJSRSubroutineMap- input map = ");
640        for (int i = 0; i < BBLastPtr + 1; i++) {
641          VM.sysWrite(currReferenceMap[i]);
642        }
643        VM.sysWrite("\n");
644        VM.sysWrite("ReferenceMaps-recordJSRSubroutineMap- mapNum = ");
645        VM.sysWrite(mapNum);
646        VM.sysWrite(" - byteindex = ");
647        VM.sysWrite(byteindex);
648        VM.sysWrite(" - return address index = ");
649        VM.sysWrite(internalReturnIndex);
650        VM.sysWrite(" - reference map byte = ");
651        VM.sysWrite(referenceMaps[mapNum * bytesPerMap()]);
652        VM.sysWrite("\n");
653      }
654    }    // end else clause - add new map
655
656    // for new maps, setup maps in UnusualMap, for existing map replace them
657
658    // setup Reference Map
659    int refindex =
660        scanByteArray(currReferenceMap,
661                      BBLastPtr,
662                      BuildReferenceMaps.SET_TO_REFERENCE,
663                      jsrSiteMap.getReferenceMapIndex(),
664                      true);
665    jsrSiteMap.setReferenceMapIndex(refindex);
666
667    if (VM.TraceStkMaps) {
668      VM.sysWrite("                 - reference map index = ");
669      VM.sysWrite(refindex);
670      VM.sysWrite(" - reference map  = ");
671      for (int i = refindex; i < refindex + bytesPerMap(); i++) {
672        VM.sysWrite(jsrInfo.unusualReferenceMaps[i]);
673      }
674      VM.sysWrite("\n");
675    }
676
677    // setup NONReference Map
678    int nonrefindex =
679        scanByteArray(currReferenceMap,
680                      BBLastPtr,
681                      BuildReferenceMaps.SET_TO_NONREFERENCE,
682                      jsrSiteMap.getNonReferenceMapIndex(),
683                      true);
684    jsrSiteMap.setNonReferenceMapIndex(nonrefindex);
685
686    if (VM.TraceStkMaps) {
687      VM.sysWrite("                 - NONreference map index = ");
688      VM.sysWrite(nonrefindex);
689      VM.sysWrite(" - NON reference map  = ");
690      for (int i = nonrefindex; i < nonrefindex + bytesPerMap(); i++) {
691        VM.sysWrite(jsrInfo.unusualReferenceMaps[i]);
692      }
693      VM.sysWrite("\n");
694    }
695
696    // setup returnAddress Map
697    int addrindex =
698        scanByteArray(currReferenceMap,
699                      BBLastPtr,
700                      BuildReferenceMaps.RETURN_ADDRESS,
701                      jsrSiteMap.getReturnAddressMapIndex(),
702                      false);
703    jsrSiteMap.setReturnAddressMapIndex(addrindex);
704
705    if (VM.TraceStkMaps) {
706      VM.sysWrite("                 - returnAddress map index = ");
707      VM.sysWrite(addrindex);
708      VM.sysWrite(" - return Address map  = ");
709      for (int i = addrindex; i < addrindex + bytesPerMap(); i++) {
710        VM.sysWrite(jsrInfo.unusualReferenceMaps[i]);
711      }
712      VM.sysWrite("\n");
713    }
714
715    if (VM.TraceStkMaps) {
716      VM.sysWrite("ReferenceMaps-recordJSRSubroutineMap- unusualmap index = ");
717      VM.sysWrite(unusualMapIndex);
718      VM.sysWrite("\n");
719    }
720
721    // update stats
722    if (VM.ReferenceMapsStatistics) {
723      if (!replacemap) {
724      }
725    }
726  }
727
728  /**
729   * Add an UnusualMap to the array of unusual maps, expand the array
730   * and referencemap array if necessary
731   *
732   * @param jsrSiteMap   unusualMap to be added to array
733   * @return number of the added map in the array
734   */
735  @Interruptible
736  private int addUnusualMap(UnusualMaps jsrSiteMap) {
737    if (jsrInfo.unusualMaps == null) {
738      // start up code
739      jsrInfo.unusualMaps = new UnusualMaps[5];
740      jsrInfo.numberUnusualMaps = 0;
741    }
742    // add to array and bump count
743    jsrInfo.unusualMaps[jsrInfo.numberUnusualMaps] = jsrSiteMap;
744    int returnnumber = jsrInfo.numberUnusualMaps;
745    jsrInfo.numberUnusualMaps++;
746
747    // do we need to extend the maps
748    if (jsrInfo.numberUnusualMaps == jsrInfo.unusualMaps.length) {
749      // array is full, expand arrays for jsrInfo.unusualMaps and unusual referencemaps
750      UnusualMaps[] temp = new UnusualMaps[jsrInfo.numberUnusualMaps + 5];
751      for (int i = 0; i < jsrInfo.numberUnusualMaps; i++) {
752        temp[i] = jsrInfo.unusualMaps[i];
753      }
754      jsrInfo.unusualMaps = temp;
755
756      byte[] temp2 = new byte[jsrInfo.unusualReferenceMaps.length + (5 * bytesPerMap() * 3)];
757      for (int i = 0; i < jsrInfo.unusualReferenceMaps.length; i++) {
758        temp2[i] = jsrInfo.unusualReferenceMaps[i];
759      }
760      jsrInfo.unusualReferenceMaps = temp2;
761    }
762    return returnnumber;
763  }
764
765  /**
766   * Setup a map  within a JSR Subroutine. This requires using up one
767   * of the unusual maps. This routine is called when the caller gets a
768   * negative mapindex value return from {@link #locateGCPoint}. This routine
769   * searches the map tables and uses its stack frameAddress input to build
770   * reference and returnAddress maps. The caller uses the getNext...
771   * routines to scan these maps for offsets in the frame of the
772   * related references.<p>
773   *
774   * Steps for this routine:
775   * <ol>
776   *   <li>use the mapid to get index of the Unusual Map
777   *   <li>from the unusual map and the frame - get the location of the jsr invoker
778   *   <li>from the invoker address and the code base address - get the machine code offset
779   *   from the machine code offset locate the map for that instruction
780   *   <li>if the invoker was itself in a jsr- merge the delta maps of each jsr and
781   *     compute the new total delta maps
782   *   <li>else the invoker was not already in a jsr merge the unusual map differences
783   *     with the invoker map
784   * </ol>
785   *
786   * @param mapid             Index of map of instruction where map is required
787   *                          ( this value was returned by locateGCpoint)
788   *
789   * @return the index of the JSR invoker
790   */
791  public int setupJSRSubroutineMap(int mapid) {
792
793    // first clear the  maps in the jsrInfo.extraUnusualMap
794    int j = jsrInfo.extraUnusualMap.getReferenceMapIndex();
795    int k = jsrInfo.extraUnusualMap.getNonReferenceMapIndex();
796    int l = jsrInfo.extraUnusualMap.getReturnAddressMapIndex();
797    for (int i = 0; i < bytesPerMap(); i++) {
798      jsrInfo.unusualReferenceMaps[j + i] = 0;
799      jsrInfo.unusualReferenceMaps[k + i] = 0;
800      jsrInfo.unusualReferenceMaps[l + i] = 0;
801    }
802
803    // use the mapid to get index of the Unusual Map
804    //
805    if (VM.TraceStkMaps) {
806      VM.sysWriteln("ReferenceMaps-setupJSRSubroutineMap- mapid = ", mapid, "   - mapid = ", -mapid);
807      VM.sysWriteln("  -referenceMaps[(- mapid) * bytesPerMap] = ", referenceMaps[(-mapid) * bytesPerMap()]);
808      VM.sysWriteln("        unusual mapid index = ", referenceMaps[(-mapid) * bytesPerMap()] & JSR_INDEX_MASK);
809    }
810
811    int unusualMapid = (referenceMaps[(-mapid) * bytesPerMap()] & JSR_INDEX_MASK);
812
813    // if jsr map is > 127 go search for the right one
814    if (unusualMapid == JSR_INDEX_MASK) {
815      unusualMapid = findUnusualMap(-mapid);
816    }
817
818    UnusualMaps unusualMap = jsrInfo.unusualMaps[unusualMapid];
819    unusualMapcopy(unusualMap); // deep copy unusual map into the extra map
820
821    // from the unusual map and the frame - get the location of the jsr invoker
822    //
823    return unusualMap.getReturnAddressIndex();
824  }
825
826  public int getNextJSRAddressIndex(Offset nextMachineCodeOffset, NormalMethod m) {
827    int jsrMapid = locateGCPoint(nextMachineCodeOffset, m);
828
829    if (jsrMapid >= 0) {
830      finalMergeMaps((jsrMapid * bytesPerMap()), jsrInfo.extraUnusualMap);
831
832      if (VM.TraceStkMaps) {
833        VM.sysWriteln("ReferenceMaps-setupJSRsubroutineMap- afterfinalMerge jsrInfo.extraUnusualMap = ");
834        jsrInfo.extraUnusualMap.showInfo();
835        VM.sysWriteln();
836        VM.sysWriteln("     jsrInfo.mergedReferenceMap Index = ", jsrInfo.mergedReferenceMap);
837        VM.sysWrite("     jsrInfo.mergedReferenceMap  = ");
838        jsrInfo.showAnUnusualMap(jsrInfo.mergedReferenceMap, bytesPerMap());
839        VM.sysWriteln(jsrInfo.unusualReferenceMaps[jsrInfo.mergedReferenceMap]);
840        VM.sysWriteln("     jsrInfo.mergedReturnAddressMap Index = ", jsrInfo.mergedReturnAddressMap);
841        VM.sysWriteln("    jsrInfo.mergedReturnAddressMap  = ",
842                      jsrInfo.unusualReferenceMaps[jsrInfo.mergedReturnAddressMap]);
843        showInfo();
844        jsrInfo.showUnusualMapInfo(bytesPerMap());
845      }
846      return 0;
847    }
848
849    jsrMapid = -jsrMapid;
850
851    if (VM.TraceStkMaps) {
852      VM.sysWriteln("ReferenceMaps-setupJSRsubroutineMap- outer MapIndex = ",
853                    jsrMapid,
854                    "  unusualMapIndex = ",
855                    referenceMaps[jsrMapid]);
856    }
857
858    // merge unusual maps- occurs in nested jsr conditions
859    //  merge each nested delta into the maps of the extraUnusualmap
860    int unusualMapIndex = JSR_INDEX_MASK & referenceMaps[jsrMapid * bytesPerMap()];
861    if (unusualMapIndex == JSR_INDEX_MASK) {
862      unusualMapIndex = findUnusualMap(jsrMapid);
863    }
864    jsrInfo.extraUnusualMap = combineDeltaMaps(unusualMapIndex);
865
866    // Locate the next JSR from the current
867    //
868    UnusualMaps thisMap = jsrInfo.unusualMaps[unusualMapIndex];
869    if (VM.TraceStkMaps) {
870      VM.sysWriteln("ReferenceMaps-setupJSRsubroutineMap- nested jsrs jsrInfo.extraUnusualMap = ");
871      jsrInfo.extraUnusualMap.showInfo();
872      VM.sysWriteln();
873      VM.sysWriteln("ReferenceMaps-setupJSRsubroutineMap- nested jsrs thisMap = ");
874      thisMap.showInfo();
875      VM.sysWriteln();
876    }
877    return thisMap.getReturnAddressIndex();
878  }
879
880  /**
881   * Called when all the recording for this map is complete. Can now
882   * sort or perform other cleanups
883   */
884  public void recordingComplete() {
885  }
886
887  /**
888   * After code is generated, translate the bytecode indices
889   * recorded in MCSites array into real machine code offsets.
890   *
891   * @param b2m map of byte code index to machine code offsets
892   */
893  public void translateByte2Machine(int[] b2m) {
894    for (int i = 0; i < MCSites.length; i++) {
895      MCSites[i] = b2m[MCSites[i]] << ArchConstants.getLogInstructionWidth();
896    }
897  }
898
899  /**
900   * Convert a portion of an array word of Bytes into a bitmap of references.
901   *
902   * @param curBBMap a byte array that describes the contents of the local variables and the java stack
903   * @param offset a starting offset in the array
904   * @param len length of the scan, max is {@link #BITS_PER_MAP_ELEMENT}
905   * @param reftype the type of byte to scan for
906   * @return a bitword
907   */
908  private byte convertMapElement(byte[] curBBMap, int offset, int len, byte reftype) {
909    byte bitmap = 0;
910    byte mask = JSR_MASK;     // starting bit mask
911    for (int i = offset; i < offset + len; i++) {
912      if (curBBMap[i] == reftype) {
913        bitmap = (byte) (bitmap | mask);  // add bit to mask
914      }
915      mask = (byte) ((0x000000ff & mask) >>> 1);             // shift for next byte and bit
916    }
917    return bitmap;
918  }
919
920  /**
921   * @return next free word in referencemaps for GC call sites
922   */
923  @Interruptible
924  private int getNextMapElement() {
925    if (jsrInfo.unusualReferenceMaps == null) {
926      // start up code
927      jsrInfo.unusualReferenceMaps = new byte[((6 * 3) + 1) * bytesPerMap()];  // 3 maps per unusual map
928    }
929
930    if (jsrInfo.freeMapSlot >= jsrInfo.unusualReferenceMaps.length) {
931      // map is full - get new array, twice the size
932      byte[] newArray = new byte[jsrInfo.unusualReferenceMaps.length << 1];
933      // copy array from old to new
934      for (int i = 0; i < jsrInfo.unusualReferenceMaps.length; i++) {
935        newArray[i] = jsrInfo.unusualReferenceMaps[i];
936      }
937      // replace old array with the new
938      jsrInfo.unusualReferenceMaps = newArray;   // replace array
939    }
940
941    int allocate = jsrInfo.freeMapSlot;
942    jsrInfo.freeMapSlot = jsrInfo.freeMapSlot + bytesPerMap();
943    return allocate;
944  }
945
946  /**
947   * given a index in the local area (biased : local0 has index 1)
948   *   this routine determines the correspondig offset in the stack
949   */
950  /*  public int convertIndexToOffset(int index)   {
951      if (index == 0) return NOMORE; //invalid
952
953      // convert from top of local words
954      int offset = startLocal0Offset - (index <<LOG_BYTES_IN_ADDRESS); // no jsrbit here
955      if (VM.TraceStkMaps) {
956        VM.sysWriteln("convertIndexToOffset- input index = ", index, "  offset = ", offset);
957      }
958      return offset;
959    }
960  */
961
962  /**
963   * Scans the map for the next reference.
964   *
965   * @param bitnum starting bitnumber in a map (inclusive)
966   * @param wordnum index of the corresponding byte,
967   * @param remaining remaining number of bits in the map,
968   * @param map map to search
969   * @return TODO document me
970   */
971  private int scanForNextRef(int bitnum, int wordnum, int remaining, byte[] map) {
972    int retbit, count = 0;
973
974    // adjust bitnum and wordnum to bit within word
975    while (bitnum > BITS_PER_MAP_ELEMENT) {
976      wordnum++;
977      bitnum -= BITS_PER_MAP_ELEMENT;
978      count += BITS_PER_MAP_ELEMENT;
979    }
980
981    // determine remaining bits in this byte - first byte of scan
982    int remain = (BITS_PER_MAP_ELEMENT + 1) - bitnum;    // remaining bits in this word
983    if (remain >= remaining) {
984      // last word in this map
985      retbit = scanByte(bitnum, wordnum, remaining, map);
986      if (retbit == 0) return 0;
987      return (retbit + count);
988    }
989    // search at least the rest of this byte
990    int startbit = bitnum;    // start at this bit
991    retbit = scanByte(startbit, wordnum, remain, map);
992    if (retbit != 0) return (retbit + count);
993
994    // search additional bytes of map
995    startbit = 1;            // start from beginning from now on
996    remaining -= remain;     // remaing bits in map
997    count += BITS_PER_MAP_ELEMENT; // remember you did the first byte
998    while (remaining > BITS_PER_MAP_ELEMENT) {
999      wordnum++;       // bump to next word
1000      remaining -= BITS_PER_MAP_ELEMENT; // search this wordd
1001      retbit = scanByte(startbit, wordnum, BITS_PER_MAP_ELEMENT, map);
1002      if (retbit != 0) return (retbit + count);
1003      count += BITS_PER_MAP_ELEMENT;
1004    } // end while
1005
1006    // scan last byte of map
1007    wordnum++;
1008    retbit = scanByte(startbit, wordnum, remaining, map); // last word
1009    if (retbit != 0) return (retbit + count);
1010    return 0;
1011  }
1012
1013  /**
1014   * Scans for a reference in a byte.
1015   *
1016   * @param bitnum bitnumber in the map
1017   * @param bytenum index of the corresponding map byte
1018   * @param toscan the remaining number of bits in the byte,
1019   * @param map the map
1020   * @return next ref in the byte or zero if not found
1021   */
1022  private int scanByte(int bitnum, int bytenum, int toscan, byte[] map) {
1023    int count = 0, mask;
1024
1025    if (VM.TraceStkMaps) {
1026      VM.sysWrite(" scanByte- inputs  bitnum = ", bitnum);
1027      VM.sysWrite("  bytenum = ", bytenum);
1028      VM.sysWriteln(" toscan = ", toscan);
1029      VM.sysWriteln("     stackmap byte = ", map[bytenum]);
1030    }
1031
1032    // convert bitnum to mask
1033    mask = (1 << (BITS_PER_MAP_ELEMENT - bitnum));  // generate mask
1034
1035    // scan rest of word
1036    while (toscan > 0) {
1037      if ((mask & map[bytenum]) == 0) {
1038        // this bit not a ref
1039        mask = mask >>> 1; // move mask bit
1040        count++;        // inc count of bits checked
1041        toscan--;    // decrement remaining count
1042      } else {
1043        // ref bit found
1044        if (VM.TraceStkMaps) {
1045          VM.sysWriteln(" scanByte- return bit number = ", bitnum + count);
1046        }
1047        return bitnum + count;
1048      }
1049    } // end while
1050    return 0;   // no more refs
1051  }
1052
1053  /**
1054   * Scans the byte array to look for the type of information that was requested. Builds a
1055   * bit array in the stack maps with the information.
1056   *
1057   * @param byteMap bytearray where each byte describes the corresponding word on a stack
1058   * @param BBLastPtr length of the byte array
1059   * @param refType type of information that is to be scanned
1060   * @param mapslot slot where map should be stored, 0 for next free slot
1061   * @param skipOneBit should a bit in the bitarray be skipped? Necessary for setRef and
1062   * setNonRef maps so so they are properly merged with jsr base maps.
1063   * @return index of the map in the reference map
1064   */
1065  @Interruptible
1066  int scanByteArray(byte[] byteMap, int BBLastPtr, byte refType, int mapslot, boolean skipOneBit) {
1067    skipOneBit = false;
1068
1069    if (BBLastPtr == -1) return -1;     // no map for this jsr
1070
1071    // get a place to hold the map if necessary
1072    if (mapslot == 0) {
1073      mapslot = getNextMapElement();     // get first word of map
1074    }
1075
1076    // initialize search variables
1077    int len = (BBLastPtr + 1);      // get length of map
1078    int offset = 0;                    // offset from origin
1079    int word = mapslot;                // first word of map
1080
1081    // map may take multiple words -convert 1 at a time
1082    while (len > 0) {
1083      boolean doSkip = (offset == 0 && skipOneBit);  // skip a bit if first word and skipOneBit is set
1084      int bitsToDo = doSkip ? BITS_PER_MAP_ELEMENT - 1 : BITS_PER_MAP_ELEMENT;
1085      if (len < bitsToDo) {
1086        bitsToDo = len;
1087      }
1088
1089      byte result = convertMapElement(byteMap, offset, bitsToDo, refType);
1090      if (doSkip) {
1091        result =
1092            (byte) ((0x000000ff & result) >>>
1093                    1);   // shift right to skip high bit for jsr to be consistent with normal maps
1094      }
1095      jsrInfo.unusualReferenceMaps[word] = result;
1096
1097      len -= bitsToDo;                // update remaining words
1098      offset += bitsToDo;                // and offset
1099      word++;                            // get next word
1100    }
1101    return mapslot;
1102  }
1103
1104
1105  /**
1106   * Makes a deep copy of {@code from} into {@code jsrInfo.extraUnusualMap}
1107   * @param from the map to copy from
1108   */
1109  private void unusualMapcopy(UnusualMaps from) {
1110    jsrInfo.extraUnusualMap.setReturnAddressIndex(from.getReturnAddressIndex());
1111    copyBitMap(jsrInfo.extraUnusualMap.getReferenceMapIndex(), from.getReferenceMapIndex());
1112    copyBitMap(jsrInfo.extraUnusualMap.getNonReferenceMapIndex(), from.getNonReferenceMapIndex());
1113    copyBitMap(jsrInfo.extraUnusualMap.getReturnAddressMapIndex(), from.getReturnAddressMapIndex());
1114  }
1115
1116  /**
1117   * Copies a bit map into the extra unusualmap.
1118   * @param extramapindex  the index of the map in the jsrInfo.extraUnusualMap ie the "to" map
1119   * @param index he index of the map to copy ie the "from" map
1120   */
1121  private void copyBitMap(int extramapindex, int index) {
1122    if (VM.TraceStkMaps) {
1123      VM.sysWriteln(" copyBitMap from map index = ",
1124                    index,
1125                    " copyBitMap from value = ",
1126                    jsrInfo.unusualReferenceMaps[index]);
1127    }
1128
1129    // copy the map over to the extra map
1130    for (int i = 0; i < bytesPerMap(); i++) {
1131      jsrInfo.unusualReferenceMaps[extramapindex + i] = jsrInfo.unusualReferenceMaps[index + i];
1132    }
1133
1134    if (VM.TraceStkMaps) {
1135      VM.sysWriteln(" extraUnusualBitMap index = ",
1136                    extramapindex,
1137                    " extraunusualBitMap value = ",
1138                    jsrInfo.unusualReferenceMaps[extramapindex]);
1139    }
1140  }
1141
1142  /**
1143   *
1144   *  m
1145   *  NOTE: while the routine is written to combine 2 jsrInfo.unusualMaps in general
1146   *      in reality the target map is always the same ( the jsrInfo.extraUnusualMap)
1147   */
1148
1149  /**
1150   * Merges unusual maps (occurs in nested jsr conditions) by merging each nested
1151   * delta map ( as represented by the jsrMapid of the location site) into the
1152   * jsrInfo.extraUnusualMap where the deltas are accumulated
1153   *
1154   * @param jsrUnusualMapid the delta map's id
1155   * @return merged map
1156   */
1157  private UnusualMaps combineDeltaMaps(int jsrUnusualMapid) {
1158    //get the delta unusualMap
1159    UnusualMaps deltaMap = jsrInfo.unusualMaps[jsrUnusualMapid];
1160
1161    // get the map indicies of the inner jsr map
1162    int reftargetindex = jsrInfo.extraUnusualMap.getReferenceMapIndex();
1163    int nreftargetindex = jsrInfo.extraUnusualMap.getNonReferenceMapIndex();
1164    int addrtargetindex = jsrInfo.extraUnusualMap.getReturnAddressMapIndex();
1165
1166    // get the map indices of the outer jsr map
1167    int refdeltaindex = deltaMap.getReferenceMapIndex();
1168    int nrefdeltaindex = deltaMap.getNonReferenceMapIndex();
1169    int addrdeltaindex = deltaMap.getReturnAddressMapIndex();
1170
1171    if (VM.TraceStkMaps) {
1172      // display original maps
1173      VM.sysWriteln("combineDeltaMaps- original ref map id  = ", reftargetindex);
1174      VM.sysWrite("combineDeltaMaps- original ref map  = ");
1175      for (int i = 0; i < bytesPerMap(); i++) {
1176        VM.sysWrite(jsrInfo.unusualReferenceMaps[reftargetindex + i]);
1177      }
1178      VM.sysWriteln();
1179      VM.sysWriteln("combineDeltaMaps- original nref map id  = ", nreftargetindex);
1180      VM.sysWrite("combineDeltaMaps original nref map  = ");
1181      for (int i = 0; i < bytesPerMap(); i++) {
1182        VM.sysWrite(jsrInfo.unusualReferenceMaps[nreftargetindex + i]);
1183      }
1184      VM.sysWriteln();
1185      VM.sysWriteln("combineDeltaMaps- original retaddr map id  = ", addrtargetindex);
1186      VM.sysWrite("combineDeltaMaps original retaddr map  = ");
1187      for (int i = 0; i < bytesPerMap(); i++) {
1188        VM.sysWrite(jsrInfo.unusualReferenceMaps[addrtargetindex + i]);
1189      }
1190      VM.sysWriteln();
1191
1192      VM.sysWriteln("combineDeltaMaps- delta ref map id  = ", refdeltaindex);
1193      VM.sysWrite("combineDeltaMaps- original delta  ref map  = ");
1194      for (int i = 0; i < bytesPerMap(); i++) {
1195        VM.sysWrite(jsrInfo.unusualReferenceMaps[refdeltaindex + i]);
1196      }
1197      VM.sysWriteln();
1198      VM.sysWriteln("combineDeltaMaps- delta nref map id  = ", nrefdeltaindex);
1199      VM.sysWrite("combineDeltaMaps original delta nref map  = ");
1200      for (int i = 0; i < bytesPerMap(); i++) {
1201        VM.sysWrite(jsrInfo.unusualReferenceMaps[nrefdeltaindex + i]);
1202      }
1203      VM.sysWriteln();
1204      VM.sysWriteln("combineDeltaMaps- delta retaddr map id  = ", addrdeltaindex);
1205      VM.sysWrite("combineDeltaMaps original  delta retaddr map  = ");
1206      for (int i = 0; i < bytesPerMap(); i++) {
1207        VM.sysWrite(jsrInfo.unusualReferenceMaps[addrdeltaindex + i]);
1208      }
1209      VM.sysWriteln();
1210
1211      // display indices
1212      VM.sysWriteln("combineDeltaMaps- ref target mapid  = ", reftargetindex);
1213      VM.sysWriteln("                        ref delta mapid = ", refdeltaindex);
1214      VM.sysWriteln("combineDeltaMaps- NONref target mapid  = ", nreftargetindex);
1215      VM.sysWriteln("                        NONref delta mapid = ", nrefdeltaindex);
1216      VM.sysWriteln("combineDeltaMaps- retaddr target mapid  = ", addrtargetindex);
1217      VM.sysWriteln("                         retaddr delta mapid = ", addrdeltaindex);
1218      VM.sysWriteln("                         jsrInfo.tempIndex = ", jsrInfo.tempIndex);
1219    }
1220
1221    // merge the reference maps
1222    mergeMap(jsrInfo.tempIndex, reftargetindex, MergeOperation.COPY); // save refs made in inner jsr sub(s)
1223    mergeMap(reftargetindex, refdeltaindex, MergeOperation.OR);       // get refs from outer loop
1224    mergeMap(reftargetindex, nreftargetindex, MergeOperation.NAND);   // turn off non refs made in inner jsr sub(s)
1225    mergeMap(reftargetindex, addrtargetindex, MergeOperation.NAND);   // then the return adresses
1226    mergeMap(reftargetindex, jsrInfo.tempIndex, MergeOperation.OR);   // OR inrefs made in inner jsr sub(s)
1227
1228    // merge the non reference maps
1229    mergeMap(jsrInfo.tempIndex, nreftargetindex, MergeOperation.COPY); // save nonrefs made in inner loop(s)
1230    mergeMap(nreftargetindex, nrefdeltaindex, MergeOperation.OR);      // get nrefs from outer loop
1231    mergeMap(nreftargetindex, reftargetindex, MergeOperation.NAND);    // turn off refs made in inner jsr sub(s)
1232    mergeMap(nreftargetindex, addrtargetindex, MergeOperation.NAND);   // then the return adresses
1233    mergeMap(nreftargetindex, jsrInfo.tempIndex, MergeOperation.OR);   // OR in non refs made in inner jsr sub(s)
1234
1235    // merge return address maps
1236    mergeMap(addrtargetindex, addrdeltaindex, MergeOperation.OR);
1237
1238    if (VM.TraceStkMaps) {
1239      //display final maps
1240      VM.sysWrite("setupjsrmap-combineDeltaMaps- merged ref map  = ");
1241      for (int i = 0; i < bytesPerMap(); i++) {
1242        VM.sysWrite(jsrInfo.unusualReferenceMaps[reftargetindex + i]);
1243      }
1244      VM.sysWriteln();
1245      VM.sysWrite("setupjsrmap-combineDeltaMaps- merged nonref map  = ");
1246      for (int i = 0; i < bytesPerMap(); i++) {
1247        VM.sysWrite(jsrInfo.unusualReferenceMaps[nreftargetindex + i]);
1248      }
1249      VM.sysWriteln();
1250      VM.sysWrite("setupjsrmap-combineDeltaMaps- merged retaddr map  = ");
1251      for (int i = 0; i < bytesPerMap(); i++) {
1252        VM.sysWrite(jsrInfo.unusualReferenceMaps[addrtargetindex + i]);
1253      }
1254      VM.sysWriteln();
1255    }
1256
1257    return jsrInfo.extraUnusualMap;
1258  }
1259
1260  /**
1261   * Merges a delta map into a target map.
1262   *
1263   * @param targetindex the delta map's index in the reference map table
1264   * @param deltaindex the target map's index in the reference map tbale
1265   * @param Op the merge operation to use
1266   */
1267  private void mergeMap(int targetindex, int deltaindex, MergeOperation Op) {
1268    int i;
1269    // Merge the maps
1270    if (Op == MergeOperation.COPY) {
1271      for (i = 0; i < bytesPerMap(); i++) {
1272        jsrInfo.unusualReferenceMaps[targetindex + i] = jsrInfo.unusualReferenceMaps[deltaindex + i];
1273      }
1274    }
1275    if (Op == MergeOperation.OR) {
1276      for (i = 0; i < bytesPerMap(); i++) {
1277        jsrInfo.unusualReferenceMaps[targetindex + i] =
1278            (byte) (jsrInfo.unusualReferenceMaps[targetindex + i] | jsrInfo.unusualReferenceMaps[deltaindex + i]);
1279      }
1280    }
1281    if (Op == MergeOperation.NAND) {
1282      for (i = 0; i < bytesPerMap(); i++) {
1283        short temp = (byte) (~(jsrInfo.unusualReferenceMaps[deltaindex + i]));
1284        jsrInfo.unusualReferenceMaps[targetindex + i] = (byte) (jsrInfo.unusualReferenceMaps[targetindex + i] & temp);
1285      }
1286    }
1287  }
1288
1289  /**
1290   * This method will merge the jsr invoker's base map with changes
1291   * due to *all* nested jsr subroutines.<p>
1292   *
1293   * The nested jsr subroutine maps were merged into a single delta
1294   * map prior to the calling of this method.  We therefore know that
1295   * the base map can never be due to a subroutine (since all
1296   * subroutines have been merged), and therefore that there are no
1297   * return address maps due to the invoker (since return addresses
1298   * are only due to the subroutine maps).
1299   *
1300   * @param jsrBaseMapIndex The map index for the invoker
1301   * @param deltaMap The map for the invoked subroutine/s (all nested
1302   * subroutine maps are guaranteed to have been combined prior to
1303   * calling this)
1304   */
1305  private void finalMergeMaps(int jsrBaseMapIndex, UnusualMaps deltaMap) {
1306    int i;
1307
1308    /* clear out the destination (merged) maps */
1309    for (i = 0; i < bytesPerMap(); i++) {
1310      jsrInfo.unusualReferenceMaps[jsrInfo.mergedReferenceMap + i] = 0;
1311      jsrInfo.unusualReferenceMaps[jsrInfo.mergedReturnAddressMap + i] = 0;
1312    }
1313
1314    /* get the indices of the maps for the combined subroutine map */
1315    int refMapIndex = deltaMap.getReferenceMapIndex();
1316    int nonRefMapIndex = deltaMap.getNonReferenceMapIndex();
1317    int returnAddressMapIndex = deltaMap.getReturnAddressMapIndex();
1318
1319    /* merge the subroutine delta map into the invoker (base) map */
1320    for (i = 0; i < bytesPerMap(); i++) {
1321      /* first establish the change in the maps due to the combined subroutines */
1322      byte deltaRef = jsrInfo.unusualReferenceMaps[refMapIndex + i];
1323      byte deltaNonRef = jsrInfo.unusualReferenceMaps[nonRefMapIndex + i];
1324      byte deltaRtnAddr = jsrInfo.unusualReferenceMaps[returnAddressMapIndex + i];
1325      byte deltaAny = (byte) (deltaRef | deltaNonRef | deltaRtnAddr);
1326
1327      /* There is no merging to be done for the return address map
1328       * since the invoker cannot have any return addressses since it
1329       * is guaranteed not to be a subroutine (and only subroutines
1330       * can generate return address map entries) */
1331      jsrInfo.unusualReferenceMaps[jsrInfo.mergedReturnAddressMap + i] = deltaRtnAddr;
1332
1333      /* Get the base reference map (the high bit is used to denote jsr) */
1334      byte thisBase = referenceMaps[jsrBaseMapIndex + i];
1335      byte nextBase = (i + 1 < bytesPerMap()) ? referenceMaps[jsrBaseMapIndex + i + 1] : 0;
1336      byte baseRef = (byte) ((thisBase << 1) | ((0xff & nextBase) >>> 7));
1337
1338      /* Merge the reference maps */
1339      byte mergedRef = (byte) (deltaRef | (baseRef & ~deltaAny));
1340      jsrInfo.unusualReferenceMaps[jsrInfo.mergedReferenceMap + i] = mergedRef;
1341
1342      /*
1343         VM.sysWrite(" **** thisBase = "); VM.sysWrite(thisBase);
1344         VM.sysWrite("     nextBase = "); VM.sysWrite(nextBase);
1345         VM.sysWrite("     deltaRef = "); VM.sysWrite(deltaRef);
1346         VM.sysWrite("     deltaNonRef = "); VM.sysWrite(deltaNonRef);
1347         VM.sysWrite("     base = "); VM.sysWrite(base);
1348         VM.sysWrite("     newRef = "); VM.sysWrite(newRef);
1349         VM.sysWrite("\n");
1350       */
1351    }
1352
1353    if (VM.TraceStkMaps) {
1354      //Note: this displays each byte as a word ... only look at low order byte
1355      VM.sysWrite("finalmergemaps-jsr total set2ref delta map  = ");
1356      for (i = 0; i < bytesPerMap(); i++) {
1357        VM.sysWrite(jsrInfo.unusualReferenceMaps[refMapIndex + i]);
1358      }
1359      VM.sysWrite("\n");
1360
1361      VM.sysWrite("              -jsr total set2nonref delta map  = ");
1362      for (i = 0; i < bytesPerMap(); i++) {
1363        VM.sysWrite(jsrInfo.unusualReferenceMaps[nonRefMapIndex + i]);
1364      }
1365      VM.sysWrite("\n");
1366
1367      VM.sysWrite("              -jsr base map  = ");
1368      for (i = 0; i < bytesPerMap(); i++) {
1369        // ORIGINAL VM.sysWrite( jsrInfo.unusualReferenceMaps[jsrBaseMapIndex + i]);
1370        VM.sysWrite(referenceMaps[jsrBaseMapIndex + i]);
1371      }
1372      VM.sysWrite("\n");
1373
1374      VM.sysWrite("              -combined merged ref map  = ");
1375      for (i = 0; i < bytesPerMap(); i++) {
1376        VM.sysWrite(jsrInfo.unusualReferenceMaps[jsrInfo.mergedReferenceMap + i]);
1377      }
1378      VM.sysWrite("\n");
1379
1380      VM.sysWrite("              -combined merged return address map  = ");
1381      for (i = 0; i < bytesPerMap(); i++) {
1382        VM.sysWrite(jsrInfo.unusualReferenceMaps[jsrInfo.mergedReturnAddressMap + i]);
1383      }
1384      VM.sysWrite("\n");
1385    }
1386  }
1387
1388  /**
1389   * This routine is used to clean out the MethodMap of structures that
1390   * were allocated from temporary storage. Temporary storage is freed up
1391   *  between stack frames as the GC scans the stack.
1392   */
1393  public void cleanupPointers() {
1394    if (VM.TraceStkMaps) VM.sysWrite("ReferenceMaps- cleanupPointers\n");
1395  }
1396
1397  /**
1398   * This routine is used to find an Unusual map with an index
1399   * greater than 127
1400   *
1401   */
1402
1403  /**
1404   * Finds an unsual map with an index greater than 127. It returns the index
1405   * by doing a sequential scan and looking for the mapid in the normal map
1406   * directory.
1407   *
1408   * @param mapid the map's id
1409   * @return the map's index
1410   */
1411  int findUnusualMap(int mapid) {
1412    int i;
1413    // Greater than 127 map sites- can't use direct index.
1414    // Do sequential scan for rest of maps.  It's slow but should almost never
1415    // happen.
1416
1417    for (i = JSR_INDEX_MASK; i < jsrInfo.numberUnusualMaps; i++) {
1418      if (jsrInfo.unusualMaps[i].getNormalMapIndex() == mapid) {
1419        break;
1420      }
1421    }
1422    if (i >= jsrInfo.numberUnusualMaps) {
1423      VM.sysFail(" can't find jsr map - PANIC !!!!");
1424    }
1425    return i;
1426  }
1427
1428  /**
1429   * Shows the basic information for each of the maps.
1430   * This is for testing use.
1431   */
1432  public void showInfo() {
1433    VM.sysWriteln("showInfo- reference maps");
1434    if (MCSites == null) {
1435      VM.sysWrite(" no MCSites array - assume using cached data - can't do showInfo()");
1436      return;
1437    }
1438
1439    VM.sysWrite(" MCSites.length = ", MCSites.length);
1440    VM.sysWrite(" mapCount = ", mapCount);
1441//    VM.sysWriteln(" startLocal0Offset = ", startLocal0Offset);
1442
1443    for (int i = 0; i < mapCount; i++) {
1444      VM.sysWrite("mapid = ", i);
1445      VM.sysWrite(" - machine  code offset ", MCSites[i]);
1446      VM.sysWrite("  -reference Map  =  ");
1447      for (int j = 0; j < bytesPerMap(); j++) {
1448        VM.sysWriteHex(referenceMaps[(i * bytesPerMap()) + j]);
1449      }
1450      VM.sysWriteln();
1451    }
1452  }
1453
1454  /**
1455   * Show the basic information for a single map. This is for testing
1456   * use.
1457   *
1458   * @param MCSiteIndex index of the machine code site
1459   */
1460  public void showAMap(int MCSiteIndex) {
1461    VM.sysWriteln("show the map for MCSite index= ", MCSiteIndex);
1462    VM.sysWrite("machine code offset = ", MCSites[MCSiteIndex]);
1463    VM.sysWrite("   reference Map  =  ");
1464    for (int i = 0; i < bytesPerMap(); i++) {
1465      VM.sysWrite(referenceMaps[(MCSiteIndex * bytesPerMap()) + i]);
1466    }
1467    VM.sysWriteln();
1468  }
1469
1470  /**
1471   * Show the offsets for all the maps. This is for test use.
1472   */
1473  public void showOffsets() {
1474    VM.sysWrite("in showOffset- #maps = ");
1475    VM.sysWrite(mapCount);
1476    VM.sysWrite("\n");
1477    int i, tindex = 0;
1478
1479    if (mapCount == 0) {
1480      VM.sysWrite(" no maps for method");
1481      return;
1482    }
1483    for (i = 0; i < mapCount; i++) {
1484      tindex = getNextRefIndex(tindex, i);
1485      VM.sysWrite("initial offset  = ");
1486      VM.sysWrite(tindex);
1487      VM.sysWrite(" for map ");
1488      VM.sysWrite(i);
1489      VM.sysWrite("\n");
1490      while (tindex != 0) {
1491        tindex = getNextRefIndex(tindex, i);
1492        VM.sysWrite("next offset = ");
1493        VM.sysWrite(tindex);
1494        if (tindex == 0) VM.sysWrite("---------------- end of map");
1495      }
1496    }
1497  }
1498
1499  @Interruptible
1500  public int showReferenceMapStatistics(RVMMethod method) {
1501    int index = 0;
1502    int totalCount = 0;
1503    int count;
1504
1505    VM.sysWrite("-- Number of refs for method =  ");
1506    VM.sysWrite(method.getDeclaringClass().getDescriptor());
1507    VM.sysWrite(".");
1508    VM.sysWrite(method.getName());
1509    VM.sysWrite("---------------------------\n");
1510
1511    for (int i = 0; i < mapCount; i++) {
1512      byte mapindex = referenceMaps[i * bytesPerMap()];
1513      if (mapindex < 0) {
1514        // check for non jsr map
1515        VM.sysWrite("  -----skipping jsr map------- \n ");
1516        continue;
1517      }
1518      index = getNextRefIndex(index, i);
1519      count = 0;
1520      while (index != 0) {
1521        totalCount++;
1522        count++;
1523        index = getNextRefIndex(index, i);
1524        // display number of refs at each site - very noisy
1525        if (index == 0) {
1526          VM.sysWriteln("  -----map machine code offset = ", MCSites[i], "    number of refs in this map = ", count);
1527        }
1528      }
1529    }
1530    VM.sysWrite("----- Total number of refs in method  = ");
1531    VM.sysWrite(totalCount);
1532    VM.sysWrite("  total number of maps in method = ");
1533    VM.sysWrite(mapCount);
1534    VM.sysWrite("\n");
1535
1536    return totalCount;
1537  }
1538
1539  /* Interface for general queries such as given a GC point, if a stack slot
1540   * or a local variable is a reference.
1541   */
1542
1543  /**
1544   * Query if a local variable has a reference type value
1545   * @param method  The method we're asking about.
1546   * @param mcoff  The machine code offset of the instruction *following* the
1547   *               actual instruction.
1548   * @param lidx the local index
1549   * @return {@code true}, if it is a reference type. {@code false}, otherwise
1550   */
1551  public boolean isLocalRefType(RVMMethod method, Offset mcoff, int lidx) {
1552    int bytenum, bitnum;
1553    byte[] maps;
1554
1555    if (bytesPerMap() == 0) return false;           // no map ie no refs
1556    int mapid = locateGCPoint(mcoff, method);
1557
1558    if (mapid >= 0) {
1559      // normal case
1560      bytenum = mapid * bytesPerMap();
1561      bitnum = lidx + 1 + 1; // 1 for being 1 based +1 for jsr bit
1562      maps = referenceMaps;
1563    } else {
1564      // in JSR
1565      bytenum = jsrInfo.mergedReferenceMap;
1566      bitnum = lidx + 1; // 1 for being 1 based
1567      maps = jsrInfo.unusualReferenceMaps;
1568    }
1569
1570    // adjust bitnum and wordnum to bit within word
1571    while (bitnum > BITS_PER_MAP_ELEMENT) {
1572      bytenum++;
1573      bitnum -= BITS_PER_MAP_ELEMENT;
1574    }
1575
1576    int mask = (1 << (BITS_PER_MAP_ELEMENT - bitnum));  // generate mask
1577
1578    return ((mask & maps[bytenum]) != 0);
1579  }
1580}