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}