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