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.mmtk.utility.heap; 014 015import static org.mmtk.utility.Constants.BITS_IN_ADDRESS; 016import static org.mmtk.utility.Constants.BYTES_IN_ADDRESS; 017 018import org.mmtk.policy.Space; 019import org.mmtk.utility.GenericFreeList; 020import org.mmtk.utility.Log; 021import org.mmtk.utility.options.Options; 022import org.mmtk.vm.Lock; 023import org.mmtk.vm.VM; 024import org.vmmagic.pragma.Inline; 025import org.vmmagic.pragma.Interruptible; 026import org.vmmagic.pragma.Uninterruptible; 027import org.vmmagic.unboxed.Address; 028import org.vmmagic.unboxed.Extent; 029import org.vmmagic.unboxed.Word; 030 031/** 032 * This class manages the mapping of spaces to virtual memory ranges. 033 * 034 */ 035@Uninterruptible 036public class Map { 037 038 /** set the map base address so that we have an unused {@code null} chunk at the bottome of the space for 64 bit */ 039 private static final Address MAP_BASE_ADDRESS = BITS_IN_ADDRESS == 32 ? Address.zero() : Space.HEAP_START.minus(Space.BYTES_IN_CHUNK); 040 041 /**************************************************************************** 042 * 043 * Class variables 044 */ 045 046 /** 047 * 048 */ 049 private static final int[] descriptorMap; 050 private static final int[] prevLink; 051 private static final int[] nextLink; 052 private static final Space[] spaceMap; 053 private static final GenericFreeList regionMap; 054 public static final GenericFreeList globalPageMap; 055 private static int sharedDiscontigFLCount = 0; 056 private static final FreeListPageResource[] sharedFLMap; 057 private static int totalAvailableDiscontiguousChunks = 0; 058 059 private static boolean finalized = false; 060 061 private static final Lock lock = VM.newLock("Map lock"); 062 063 /**************************************************************************** 064 * 065 * Initialization 066 */ 067 068 /** 069 * Class initializer. Create our two maps 070 */ 071 static { 072 descriptorMap = new int[Space.MAX_CHUNKS]; 073 prevLink = new int[Space.MAX_CHUNKS]; 074 nextLink = new int[Space.MAX_CHUNKS]; 075 spaceMap = new Space[Space.MAX_CHUNKS]; 076 regionMap = new GenericFreeList(Space.MAX_CHUNKS); 077 globalPageMap = new GenericFreeList(1, 1, Space.MAX_SPACES); 078 sharedFLMap = new FreeListPageResource[Space.MAX_SPACES]; 079 if (VM.VERIFY_ASSERTIONS) 080 VM.assertions._assert(BITS_IN_ADDRESS == Space.LOG_ADDRESS_SPACE || 081 Space.HEAP_END.diff(MAP_BASE_ADDRESS).toWord().rshl(Space.LOG_ADDRESS_SPACE).isZero()); 082 } 083 084 /**************************************************************************** 085 * 086 * Map accesses and insertion 087 */ 088 089 /** 090 * Insert a space and its descriptor into the map, associating it 091 * with a particular address range. 092 * 093 * @param start The start address of the region to be associated 094 * with this space. 095 * @param extent The size of the region, in bytes 096 * @param descriptor The descriptor for this space 097 * @param space The space to be associated with this region 098 */ 099 public static void insert(Address start, Extent extent, int descriptor, 100 Space space) { 101 Extent e = Extent.zero(); 102 while (e.LT(extent)) { 103 int index = getChunkIndex(start.plus(e)); 104 if (descriptorMap[index] != 0) { 105 Log.write("Conflicting virtual address request for space \""); 106 Log.write(space.getName()); Log.write("\" at "); 107 Log.writeln(start.plus(e)); 108 Space.printVMMap(); 109 VM.assertions.fail("exiting"); 110 } 111 descriptorMap[index] = descriptor; 112 VM.barriers.objectArrayStoreNoGCBarrier(spaceMap, index, space); 113 e = e.plus(Space.BYTES_IN_CHUNK); 114 } 115 } 116 117 /** 118 * Allocate some number of contiguous chunks within a discontiguous region. 119 * 120 * @param descriptor The descriptor for the space to which these chunks will be assigned 121 * @param space The space to which these chunks will be assigned 122 * @param chunks The number of chunks required 123 * @param head The previous contiguous set of chunks for this space (to create a linked list of contiguous regions for each space) 124 * @return The address of the assigned memory. If the request fails we return Address.zero(). 125 */ 126 public static Address allocateContiguousChunks(int descriptor, Space space, int chunks, Address head) { 127 lock.acquire(); 128 int chunk = regionMap.alloc(chunks); 129 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(chunk != 0); 130 if (chunk == -1) { 131 if (Options.verbose.getValue() > 3) { 132 Log.write("Unable to allocate virtual address space for space \""); 133 Log.write(space.getName()); Log.write("\" for "); 134 Log.write(chunks); Log.write(" chunks ("); 135 Log.write(chunks << Space.LOG_BYTES_IN_CHUNK); Log.writeln(" bytes), requesting GC."); 136 if (Options.verbose.getValue() > 7) { 137 Space.printVMMap(); 138 } 139 } 140 lock.release(); 141 return Address.zero(); 142 } 143 totalAvailableDiscontiguousChunks -= chunks; 144 Address rtn = addressForChunkIndex(chunk); 145 insert(rtn, Extent.fromIntZeroExtend(chunks << Space.LOG_BYTES_IN_CHUNK), descriptor, space); 146 if (head.isZero()) { 147 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(nextLink[chunk] == 0); 148 } else { 149 nextLink[chunk] = getChunkIndex(head); 150 prevLink[getChunkIndex(head)] = chunk; 151 } 152 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(prevLink[chunk] == 0); 153 lock.release(); 154 return rtn; 155 } 156 157 /** 158 * Return the address of the next contiguous region associated with some discontiguous space by following the linked list for that space. 159 * 160 * @param start The current region (return the next region in the list) 161 * @return Return the next contiguous region after start in the linked list of regions 162 */ 163 public static Address getNextContiguousRegion(Address start) { 164 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(start.EQ(Space.chunkAlign(start, true))); 165 int chunk = getChunkIndex(start); 166 return (chunk == 0) ? Address.zero() : (nextLink[chunk] == 0) ? Address.zero() : addressForChunkIndex(nextLink[chunk]); 167 } 168 169 /** 170 * Return the size of a contiguous region in chunks. 171 * 172 * @param start The start address of the region whose size is being requested 173 * @return The size of the region in question 174 */ 175 public static int getContiguousRegionChunks(Address start) { 176 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(start.EQ(Space.chunkAlign(start, true))); 177 int chunk = getChunkIndex(start); 178 return regionMap.size(chunk); 179 } 180 181 /** 182 * Return the size of a contiguous region in bytes. 183 * 184 * @param start The start address of the region whose size is being requested 185 * @return The size of the region in question 186 */ 187 public static Extent getContiguousRegionSize(Address start) { 188 return Word.fromIntSignExtend(getContiguousRegionChunks(start)).lsh(Space.LOG_BYTES_IN_CHUNK).toExtent(); 189 } 190 191 /** 192 * Free all chunks in a linked list of contiguous chunks. This means starting 193 * with one and then walking the chains of contiguous regions, freeing each. 194 * 195 * @param anyChunk Any chunk in the linked list of chunks to be freed 196 */ 197 public static void freeAllChunks(Address anyChunk) { 198 lock.acquire(); 199 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(anyChunk.EQ(Space.chunkAlign(anyChunk, true))); 200 if (!anyChunk.isZero()) { 201 int chunk = getChunkIndex(anyChunk); 202 while (nextLink[chunk] != 0) { 203 freeContiguousChunks(nextLink[chunk]); 204 } 205 while (prevLink[chunk] != 0) { 206 freeContiguousChunks(prevLink[chunk]); 207 } 208 freeContiguousChunks(chunk); 209 } 210 lock.release(); 211 } 212 213 /** 214 * Free some set of contiguous chunks, given the chunk address 215 * 216 * @param start The start address of the first chunk in the series 217 * @return The number of chunks which were contiguously allocated 218 */ 219 public static int freeContiguousChunks(Address start) { 220 lock.acquire(); 221 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(start.EQ(Space.chunkAlign(start, true))); 222 int rtn = freeContiguousChunks(getChunkIndex(start)); 223 lock.release(); 224 return rtn; 225 } 226 227 /** 228 * Free some set of contiguous chunks, given the chunk index 229 * 230 * @param chunk The chunk index of the region to be freed 231 * @return The number of chunks freed 232 */ 233 private static int freeContiguousChunks(int chunk) { 234 int chunks = regionMap.free(chunk); 235 totalAvailableDiscontiguousChunks += chunks; 236 int next = nextLink[chunk]; 237 int prev = prevLink[chunk]; 238 if (next != 0) prevLink[next] = prev; 239 if (prev != 0) nextLink[prev] = next; 240 nextLink[chunk] = prevLink[chunk] = 0; 241 for (int offset = 0; offset < chunks; offset++) { 242 descriptorMap[chunk + offset] = 0; 243 VM.barriers.objectArrayStoreNoGCBarrier(spaceMap, chunk + offset, null); 244 } 245 return chunks; 246 } 247 248 /** 249 * Finalize the space map, establishing which virtual memory 250 * is nailed down, and then placing the rest into a map to 251 * be used by discontiguous spaces. 252 */ 253 @Interruptible 254 public static void finalizeStaticSpaceMap() { 255 /* establish bounds of discontiguous space */ 256 Address startAddress = Space.getDiscontigStart(); 257 int firstChunk = getChunkIndex(startAddress); 258 int lastChunk = getChunkIndex(Space.getDiscontigEnd()); 259 int unavailStartChunk = lastChunk + 1; 260 int trailingChunks = Space.MAX_CHUNKS - unavailStartChunk; 261 int pages = (1 + lastChunk - firstChunk) * Space.PAGES_IN_CHUNK; 262 globalPageMap.resizeFreeList(pages, pages); 263 for (int pr = 0; pr < sharedDiscontigFLCount; pr++) 264 sharedFLMap[pr].resizeFreeList(startAddress); 265 266 /* set up the region map free list */ 267 int allocedChunk = regionMap.alloc(firstChunk); // block out entire bottom of address range 268 for (int chunkIndex = firstChunk; chunkIndex <= lastChunk; chunkIndex++) 269 allocedChunk = regionMap.alloc(1); // Tentatively allocate all usable chunks 270 allocedChunk = regionMap.alloc(trailingChunks); // block out entire top of address range 271 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(allocedChunk == unavailStartChunk); 272 273 /* set up the global page map and place chunks on free list */ 274 int firstPage = 0; 275 for (int chunkIndex = firstChunk; chunkIndex <= lastChunk; chunkIndex++) { 276 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(spaceMap[chunkIndex] == null); 277 totalAvailableDiscontiguousChunks++; 278 regionMap.free(chunkIndex); // put this chunk on the free list 279 globalPageMap.setUncoalescable(firstPage); 280 int allocedPages = globalPageMap.alloc(Space.PAGES_IN_CHUNK); // populate the global page map 281 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(allocedPages == firstPage); 282 firstPage += Space.PAGES_IN_CHUNK; 283 } 284 285 finalized = true; 286 } 287 288 public static boolean isFinalized() { 289 return finalized; 290 } 291 292 /** 293 * @param pr the resource that wants to share the discontiguous region 294 * @return The ordinal number for a free list space wishing to share a discontiguous region 295 */ 296 @Interruptible 297 public static int getDiscontigFreeListPROrdinal(FreeListPageResource pr) { 298 sharedFLMap[sharedDiscontigFLCount] = pr; 299 sharedDiscontigFLCount++; 300 return sharedDiscontigFLCount; 301 } 302 303 /** 304 * Return the total number of chunks available (unassigned) within the 305 * range of virtual memory apportioned to discontiguous spaces. 306 * 307 * @return The number of available chunks for use by discontiguous spaces. 308 */ 309 public static int getAvailableDiscontiguousChunks() { 310 return totalAvailableDiscontiguousChunks; 311 } 312 313 /** 314 * Return the total number of clients contending for chunks. This 315 * is useful when establishing conservative bounds on the number 316 * of remaining chunks. 317 * 318 * @return The total number of clients who may contend for chunks. 319 */ 320 public static int getChunkConsumerCount() { 321 return sharedDiscontigFLCount; 322 } 323 324 /** 325 * Return the space in which this address resides. 326 * 327 * @param address The address in question 328 * @return The space in which the address resides 329 */ 330 @Inline 331 public static Space getSpaceForAddress(Address address) { 332 int index = getChunkIndex(address); 333 return spaceMap[index]; 334 } 335 336 /** 337 * Return the space descriptor for the space in which this object 338 * resides. 339 * 340 * @param object The object in question 341 * @return The space descriptor for the space in which the object 342 * resides 343 */ 344 @Inline 345 public static int getDescriptorForAddress(Address object) { 346 int index = getChunkIndex(object); 347 return descriptorMap[index]; 348 } 349 350 /** 351 * Hash an address to a chunk (this is simply done via bit shifting) 352 * 353 * @param address The address to be hashed 354 * @return The chunk number that this address hashes into 355 */ 356 @Inline 357 private static int getChunkIndex(Address address) { 358 if (BYTES_IN_ADDRESS == 8) { 359 if (address.LT(Space.HEAP_START) || address.GE(Space.HEAP_END)) 360 return 0; 361 else 362 return address.diff(MAP_BASE_ADDRESS).toWord().rshl(Space.LOG_BYTES_IN_CHUNK).toInt(); 363 } else 364 return address.toWord().rshl(Space.LOG_BYTES_IN_CHUNK).toInt(); 365 } 366 @Inline 367 private static Address addressForChunkIndex(int chunk) { 368 if (BYTES_IN_ADDRESS == 8) { 369 if (chunk == 0) 370 return Address.zero(); 371 else 372 return MAP_BASE_ADDRESS.plus(Word.fromIntZeroExtend(chunk).lsh(Space.LOG_BYTES_IN_CHUNK).toExtent()); 373 } else 374 return Word.fromIntZeroExtend(chunk).lsh(Space.LOG_BYTES_IN_CHUNK).toAddress(); 375 } 376}