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