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}