001/*
002 *  This file is part of the Jikes RVM project (http://jikesrvm.org).
003 *
004 *  This file is licensed to You under the Eclipse Public License (EPL);
005 *  You may not use this file except in compliance with the License. You
006 *  may obtain a copy of the License at
007 *
008 *      http://www.opensource.org/licenses/eclipse-1.0.php
009 *
010 *  See the COPYRIGHT.txt file distributed with this work for information
011 *  regarding copyright ownership.
012 */
013package org.jikesrvm.compilers.opt.runtimesupport;
014
015import java.util.List;
016import org.jikesrvm.VM;
017import org.jikesrvm.compilers.opt.ir.GCIRMapElement;
018import org.jikesrvm.compilers.opt.ir.RegSpillListElement;
019import org.vmmagic.pragma.Interruptible;
020import org.vmmagic.pragma.Uninterruptible;
021
022/**
023 * A class that encapsulates the GCMap portion of the machine code maps.
024 * An instance of this class is created to encode and instance of a
025 * GCIRMap into an int[].  The int[] is stored persistently,
026 * but the instance of the OptGCMap is NOT.
027 *
028 * <ul>
029 * <li> each map will be a sequence of 1 or more ints
030 * <li> the first int in each map is a bit map of registers that
031 *   contain references (the MSB is used for chaining,
032 *   we assume it will never contain a reference)
033 * <li> the remaining ints will be spill locations
034 * <li> the sequence will continue as long as the most significant bit
035 *   is set to 1
036 * </ul>
037 *
038 *  Note: This file contains two types of methods
039 *  <ul>
040 *    <li>1) methods called during compilation to create the GC maps
041 *            these methods are virtual
042 *    <li>2) methods called at GC time (no allocation allowed!)
043 *            these methods are static
044 *  </ul>
045 */
046@Uninterruptible
047public final class OptGCMap {
048  public static final int NO_MAP_ENTRY = -1;
049  public static final int ERROR = -2;
050
051  /**
052   *  The initial allocation size for a map
053   */
054  public static final int INITIAL_MAP_SIZE = 16;
055
056  /**
057   * bit pattern for the "next" bit in the GC maps array
058   */
059  private static final int NEXT_BIT = 0x80000000;
060
061  /**
062   * the index of the last map entry in use
063   */
064  private int lastGCMapEntry;
065
066  /**
067   *  The gc map array, a sequence of gc maps.  Each sequence starts
068   *  with a register bit mask and is followed by a list of spills.
069   *  The most significant bit of the spill location is used to chain
070   *  the list.
071   */
072  private int[] gcMapInformation;
073
074  public static final boolean DEBUG = false;
075
076  public static final int FIRST_GCMAP_REG;
077  public static final int LAST_GCMAP_REG;
078
079  static {
080    if (VM.BuildForIA32) {
081      FIRST_GCMAP_REG = org.jikesrvm.compilers.opt.runtimesupport.ia32.OptGCMapIteratorConstants.FIRST_GCMAP_REG;
082      LAST_GCMAP_REG = org.jikesrvm.compilers.opt.runtimesupport.ia32.OptGCMapIteratorConstants.LAST_GCMAP_REG;
083    } else {
084      if (VM.VerifyAssertions) VM._assert(VM.BuildForPowerPC);
085      FIRST_GCMAP_REG = org.jikesrvm.compilers.opt.runtimesupport.ppc.OptGCMapIteratorConstants.FIRST_GCMAP_REG;
086      LAST_GCMAP_REG = org.jikesrvm.compilers.opt.runtimesupport.ppc.OptGCMapIteratorConstants.LAST_GCMAP_REG;
087    }
088  }
089
090  /**
091   * Constructor, called during compilation
092   */
093  OptGCMap() {
094    lastGCMapEntry = -1;
095    gcMapInformation = new int[INITIAL_MAP_SIZE];   // initial map size
096  }
097
098  /**
099   * Completes the encoding of the map.
100   *
101   * @return the final GC map
102   */
103  @Interruptible
104  public int[] finish() {
105    if ((gcMapInformation != null) && (lastGCMapEntry < gcMapInformation.length - 1)) {
106      resizeMapInformation(lastGCMapEntry + 1);
107    }
108    return gcMapInformation;
109  }
110
111  /**
112   * Construct the GCMap for the argument GCIRMapElement
113   * @param irMapElem  The IR Map element to create a GCMap for
114   * @return the GCMap index.
115   */
116  @Interruptible
117  public int generateGCMapEntry(GCIRMapElement irMapElem) {
118    // the index into the GC maps we will use for this instruction.
119    int mapIndex = NO_MAP_ENTRY;
120
121    // Before requesting the (first) map entry, lets make sure we
122    // will need it.  If the reg/spill list is empty, we don't
123    // need a map slot, i.e., no references are live at this instruction
124    List<RegSpillListElement> regSpillList = irMapElem.regSpillList();
125    if (!regSpillList.isEmpty()) {
126
127      // For efficiency we create our own bit map and then set the
128      // appropriate array value
129      int bitMap = 0;
130      // count the spills so we know how big of an array we'll need
131      int numSpills = 0;
132      int numRegs = 0;
133
134      // Because the output data structure (the map) stores register
135      // information before spills, we need to traverse the list twice
136      // the first time we get the register mask, the 2nd time we get
137      // the spills
138      // process the register
139      for (RegSpillListElement elem : regSpillList) {
140        if (elem.isSpill()) {
141          numSpills++;
142        } else {
143          numRegs++;
144          int realRegNumber = elem.getRealRegNumber();
145
146          if (VM.VerifyAssertions && realRegNumber > LAST_GCMAP_REG) {
147            System.out.println(elem);
148            System.out.println(LAST_GCMAP_REG);
149            VM._assert(VM.NOT_REACHED, "reg > last GC Map Reg!!");
150          }
151
152          // get the bit position for this register number
153          int bitPosition = getRegBitPosition(realRegNumber);
154
155          // Set the appropriate bit
156          bitMap = bitMap | (NEXT_BIT >>> bitPosition);
157        }
158      }
159
160      // get the next free Map entry
161      int index = setRegisterBitMap(bitMap);
162
163      int[] spillArray = new int[numSpills];
164      int spillIndex = 0;
165      // Now we need to walk the list again to process the spills.
166      // first, get a fresh enumerator
167      for (RegSpillListElement elem : regSpillList) {
168        if (elem.isSpill()) {
169          spillArray[spillIndex++] = elem.getSpill();
170        }
171      }
172
173      // add the spills into the map
174      addAllSpills(spillArray);
175
176      // don't forget to report that there are no more spills
177      mapIndex = endCurrentMap(index);
178
179    }
180    return mapIndex;
181  }
182
183  ////////////////////////////////////////////
184  // Methods called at GC time
185  ////////////////////////////////////////////
186
187  public static int gcMapInformation(int mapEntry, int[] gcMap) {
188    // before returning remember to clear the MSB.
189    return gcMap[mapEntry] & ~NEXT_BIT;
190  }
191
192  public static boolean registerIsSet(int entry, int registerNumber, int[] gcMap) {
193    if (VM.VerifyAssertions) {
194      VM._assert(registerNumber >= FIRST_GCMAP_REG && registerNumber <= LAST_GCMAP_REG, "Bad registerNumber");
195    }
196
197    // Get the bit position for the register number
198    int bitPosition = getRegBitPosition(registerNumber);
199
200    // Using the register number passed construct the appropriate bit string,
201    // "and" it with the value, and see if we get a positive value
202    return (gcMap[entry] & (NEXT_BIT >>> bitPosition)) > 0;
203  }
204
205  /**
206   * @param currentIndex the index of the current location
207   * @param gcMap the encoded GCMap
208   * @return the next (relative) location or -1 for no more locations
209   */
210  public static int nextLocation(int currentIndex, int[] gcMap) {
211    // Does the next entry contain anything useful?
212    if (nextBitSet(currentIndex, gcMap)) {
213      // if so, return the next index
214      return currentIndex + 1;
215    } else {
216      return -1;
217    }
218  }
219
220  private static int getRegBitPosition(int registerNumber) {
221    //  Because we can't use bit position 0 (that is the next bit), we
222    // adjust depending on the value of FIRST_GCMAP_REG
223    //
224    // For example,
225    //  FIRST_GCMAP_REG = 1 => registerNumber = 1    (PPC)
226    //  FIRST_GCMAP_REG = 0 => registerNumber = 1    (IA32)
227    //
228    return registerNumber - FIRST_GCMAP_REG + 1;
229  }
230
231  /**
232   * Determines if the next bit is set for the entry passed in the gc map passed
233   * @param entry the entry (index) to check
234   * @param gcMap the gcmap
235   * @return whether the next bit is set
236   */
237  private static boolean nextBitSet(int entry, int[] gcMap) {
238    return (gcMap[entry] & NEXT_BIT) == NEXT_BIT;
239  }
240
241  /**
242   * Dumps the GCmap that starts at entry.
243   * @param entry  the entry where the map begins
244   * @param gcMap the encoded GCmaps
245   */
246  @Interruptible
247  public static void dumpMap(int entry, int[] gcMap) {
248    VM.sysWrite("Regs [");
249    // Inspect the register bit map for the entry passed and print
250    // those bit map entries that are true
251    for (int registerNumber = FIRST_GCMAP_REG; registerNumber <= LAST_GCMAP_REG; registerNumber++) {
252      if (registerIsSet(entry, registerNumber, gcMap)) {
253        VM.sysWrite(registerNumber, " ");
254      }
255    }
256    VM.sysWrite("]");
257    VM.sysWrite(" Spills [");
258    while (nextBitSet(entry, gcMap)) {
259      entry++;
260      VM.sysWrite(gcMapInformation(entry, gcMap));
261      VM.sysWrite(" ");
262    }
263    VM.sysWrite("]");
264  }
265
266  ////////////////////////////////////////////
267  // Helper methods for GCMap creation
268  ////////////////////////////////////////////
269  /**
270   * Returns the next GC map entry for use
271   * @return the entry in the map table that can be used
272   */
273  @Interruptible
274  private int getNextMapEntry() {
275    // make sure we have enough room
276    int oldLength = gcMapInformation.length - 1;
277    if (lastGCMapEntry >= oldLength) {
278      // expand the mapInformation array to be twice as big
279      resizeMapInformation(oldLength << 1);
280    }
281    return ++lastGCMapEntry;
282  }
283
284  /**
285   * Resize the map array
286   * @param newSize the new size for the map array
287   */
288  @Interruptible
289  private void resizeMapInformation(int newSize) {
290    int[] newMapInformation = new int[newSize];
291    for (int i = 0; i <= lastGCMapEntry; i++) {
292      newMapInformation[i] = gcMapInformation[i];
293    }
294    gcMapInformation = newMapInformation;
295  }
296
297  //////////
298  // Setters for GC Maps
299  //////////
300
301  /**
302   * Sets the register map information at the next available entry
303   * @param  bitMap    map entry
304   * @return The index of that entry.
305   */
306  @Interruptible
307  private int setRegisterBitMap(int bitMap) {
308    // Set the appropriate bit, but make sure we preserve the NEXT bit!
309    int entry = getNextMapEntry();
310    gcMapInformation[entry] = bitMap | NEXT_BIT;
311    return entry;
312  }
313
314  /**
315   * If we will be looking for missed references we need to sort the list
316   *  of spills and then add them to the map, otherwise, nothing to do
317   * @param spillArray an array of spills
318   */
319  @Interruptible
320  private void addAllSpills(int[] spillArray) {
321    // 1) sort the spills to increase the odds of reusing the GC map
322    java.util.Arrays.sort(spillArray);
323
324    // 2) add them to the map using addSpillLocation
325    for (int spill : spillArray) {
326      addSpillLocation(spill);
327    }
328  }
329
330  /**
331   * Adds the passed spill value to the current map
332   * @param spill the spill location
333   */
334  @Interruptible
335  private void addSpillLocation(int spill) {
336    // make sure the value doesn't overflow the maximum spill location
337    if (VM.VerifyAssertions && ((spill < 0) || (spill > 32767))) {
338      String msg = "Unexpected spill passed:" + spill;
339      VM._assert(VM.NOT_REACHED, msg);
340    }
341    // get the next entry (with the NEXT bit set) ...
342    int entry = getNextMapEntry();
343    gcMapInformation[entry] = spill | NEXT_BIT;
344  }
345
346  /**
347   * Ends the current map
348   * @param firstIndex the index of the beginning of the map
349   * @return the index of the beginning of the map (may be different)
350   */
351  @Interruptible
352  private int endCurrentMap(int firstIndex) {
353    int lastEntry = lastGCMapEntry;
354
355    // adjust the last entry so that the NEXT bit is not set.
356    gcMapInformation[lastEntry] = gcMapInformation[lastEntry] & ~NEXT_BIT;
357
358    if (DEBUG) {
359      System.out.println("\nendCurrentMap called with firstIndex: " +
360                         firstIndex +
361                         ", lastGCMapEntry: " +
362                         lastGCMapEntry);
363      System.out.println("gc map array before reuse checking");
364      for (int i = 0; i <= lastGCMapEntry; i++) {
365        System.out.println(i + ": " + gcMapInformation[i]);
366      }
367    }
368
369    // Now that we know the complete map information, let's determine if
370    // we really need to store it, or instead can reuse a previous map.
371    int candidateBeginningIndex = 0; //this will be the beginning
372    int candidateIndex = candidateBeginningIndex;  // this will walk the map
373    int curIndex = firstIndex;
374    while (candidateIndex < firstIndex && curIndex <= lastEntry) {
375      int old = gcMapInformation[candidateIndex++];
376      int cur = gcMapInformation[curIndex++];
377      if (old != cur) {
378        if (DEBUG) {
379          System.out.println("entries at " + (candidateIndex - 1) + " and " + (curIndex - 1) + " don't match");
380        }
381        // this entry won't work, advance to candidateIndex to GC map entry
382        //  and reset curIndex
383        while ((old & NEXT_BIT) != 0) {
384          old = gcMapInformation[candidateIndex++];
385        }
386
387        // update the beginning index too
388        candidateBeginningIndex = candidateIndex;
389        curIndex = firstIndex;
390      } else if ((old & NEXT_BIT) == 0) {
391        // we've checked all of the candidate without stopping, so we found
392        //  a winner to reuse
393
394        if (DEBUG) {
395          System.out.println("found a matching map: [" +
396                             candidateBeginningIndex +
397                             ", " +
398                             (candidateIndex - 1) +
399                             "] == [" +
400                             firstIndex +
401                             ", " +
402                             lastGCMapEntry +
403                             "]");
404        }
405
406        lastGCMapEntry = firstIndex - 1;
407        return candidateBeginningIndex;
408      }
409    }
410
411    return firstIndex;
412  }
413}