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.alloc;
014    
015    import org.mmtk.policy.Space;
016    import org.mmtk.utility.*;
017    
018    import org.mmtk.vm.VM;
019    
020    import org.vmmagic.pragma.*;
021    import org.vmmagic.unboxed.*;
022    
023    /**
024     * This class implements "block" data structures of various sizes.<p>
025     *
026     * Blocks are a non-shared (thread-local) coarse-grained unit of
027     * storage. Blocks are available in power-of-two sizes.<p>
028     *
029     * Virtual memory space is taken from a VM resource, and pages
030     * consumed by blocks are accounted for by a memory resource.
031     */
032    @Uninterruptible
033    public final class BlockAllocator implements Constants {
034      /****************************************************************************
035       *
036       * Class variables
037       */
038    
039      /**
040      *
041      */
042    
043      // block freelist
044      public static final int LOG_MIN_BLOCK = 12; // 4K bytes
045      public static final int LOG_MAX_BLOCK = 15; // 32K bytes
046      public static final byte MAX_BLOCK_SIZE_CLASS = LOG_MAX_BLOCK - LOG_MIN_BLOCK;
047      public static final int BLOCK_SIZE_CLASSES = MAX_BLOCK_SIZE_CLASS + 1;
048    
049      // metadata
050      private static final Offset NEXT_OFFSET = Offset.zero();
051      private static final Offset BMD_OFFSET = NEXT_OFFSET.plus(BYTES_IN_ADDRESS);
052      private static final Offset CSC_OFFSET = BMD_OFFSET.plus(1);
053      private static final Offset IU_OFFSET = CSC_OFFSET.plus(1);
054      private static final Offset FL_META_OFFSET = IU_OFFSET.plus(BYTES_IN_SHORT);
055      private static final byte BLOCK_SC_MASK = 0xf;             // lower 4 bits
056      private static final int BLOCK_PAGE_OFFSET_SHIFT = 4;      // higher 4 bits
057      private static final int MAX_BLOCK_PAGE_OFFSET = (1<<4)-1; // 4 bits
058      private static final int LOG_BYTES_IN_BLOCK_META = LOG_BYTES_IN_ADDRESS + 2;
059      private static final int LOG_BYTE_COVERAGE = LOG_MIN_BLOCK - LOG_BYTES_IN_BLOCK_META;
060    
061      public static final int META_DATA_BYTES_PER_REGION = 1 << (EmbeddedMetaData.LOG_BYTES_IN_REGION - LOG_BYTE_COVERAGE);
062      public static final Extent META_DATA_EXTENT = Extent.fromIntSignExtend(META_DATA_BYTES_PER_REGION);
063    
064      /****************************************************************************
065       *
066       * Allocation & freeing
067       */
068    
069      /**
070       * Allocate a block, returning the address of the first usable byte
071       * in the block.
072       *
073       * @param blockSizeClass The size class for the block to be allocated.
074       * @return The address of the first usable byte in the block, or
075       * zero on failure.
076       */
077      public static Address alloc(Space space, int blockSizeClass) {
078        if (VM.VERIFY_ASSERTIONS) VM.assertions._assert((blockSizeClass >= 0) && (blockSizeClass <= MAX_BLOCK_SIZE_CLASS));
079        int pages = pagesForSizeClass(blockSizeClass);
080        Address result = space.acquire(pages);
081        if (!result.isZero()) {
082          setBlkSizeMetaData(result, (byte) blockSizeClass);
083        }
084        return result;
085      }
086    
087      /**
088       * Free a block.  If the block is a sub-page block and the page is
089       * not completely free, then the block is added to the free list.
090       * Otherwise the block is returned to the virtual memory resource.
091       *
092       * @param block The address of the block to be freed
093       */
094      public static void free(Space space, Address block) {
095        space.release(block);
096      }
097    
098      /**
099       * Return the size in bytes of a block of a given size class
100       *
101       * @param blockSizeClass The size class in question
102       * @return The size in bytes of a block of this size class
103       */
104      @Inline
105      public static int blockSize(int blockSizeClass) {
106        return 1 << (LOG_MIN_BLOCK + blockSizeClass);
107      }
108    
109      /**
110       * Return the number of pages required when allocating space for
111       *         this size class.
112       *
113       * @param blockSizeClass The size class in question
114       * @return The number of pages required when allocating a block (or
115       * blocks) of this size class.
116       */
117      @Inline
118      private static int pagesForSizeClass(int blockSizeClass) {
119        return 1 << (LOG_MIN_BLOCK + blockSizeClass - LOG_BYTES_IN_PAGE);
120      }
121    
122      /****************************************************************************
123       *
124       * Block meta-data manipulation
125       */
126    
127      /**
128       * Set the <i>block size class</i> meta data field for a given
129       * address (all blocks on a given page are homogeneous with respect
130       * to block size class).
131       *
132       * @param block The address of interest
133       * @param sc The value to which this field is to be set
134       */
135      @Inline
136      private static void setBlkSizeMetaData(Address block, byte sc) {
137        if (VM.VERIFY_ASSERTIONS) {
138          VM.assertions._assert(block.EQ(Conversions.pageAlign(block)));
139          VM.assertions._assert(pagesForSizeClass(sc) - 1  <= MAX_BLOCK_PAGE_OFFSET);
140        }
141        Address address = block;
142        for (int i = 0; i < pagesForSizeClass(sc); i++) {
143          byte value = (byte) ((i << BLOCK_PAGE_OFFSET_SHIFT) | sc);
144          getMetaAddress(address).store(value, BMD_OFFSET);
145          if (VM.VERIFY_ASSERTIONS) {
146            VM.assertions._assert(getBlkStart(address).EQ(block));
147            VM.assertions._assert(getBlkSizeClass(address) == sc);
148          }
149          address = address.plus(1<<VM.LOG_BYTES_IN_PAGE);
150        }
151      }
152    
153      /**
154       * Get the <i>block size class</i> meta data field for a given page
155       * (all blocks on a given page are homogeneous with respect to block
156       * size class).
157       *
158       * @param address The address of interest
159       * @return The size class field for the block containing the given address
160       */
161      @Inline
162      private static byte getBlkSizeClass(Address address) {
163        address = Conversions.pageAlign(address);
164        byte rtn = (byte) (getMetaAddress(address).loadByte(BMD_OFFSET) & BLOCK_SC_MASK);
165        if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(rtn >= 0 && rtn <= MAX_BLOCK_SIZE_CLASS);
166        return rtn;
167      }
168    
169      /**
170       * Get the <i>address of the start of a block size class</i> a given page
171       * within the block.
172       *
173       * @param address The address of interest
174       * @return The address of the block containing the address
175       */
176      @Inline
177      public static Address getBlkStart(Address address) {
178        address = Conversions.pageAlign(address);
179        byte offset = (byte) (getMetaAddress(address).loadByte(BMD_OFFSET) >>> BLOCK_PAGE_OFFSET_SHIFT);
180        return address.minus(offset<<LOG_BYTES_IN_PAGE);
181      }
182    
183      /**
184       * Set the <i>client size class</i> meta data field for a given
185       * address (all blocks on a given page are homogeneous with respect
186       * to block size class).
187       *
188       * @param block The address of interest
189       * @param sc The value to which this field is to be set
190       */
191      @Inline
192      public static void setAllClientSizeClass(Address block, int blocksc, byte sc) {
193        if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(block.EQ(Conversions.pageAlign(block)));
194        Address address = block;
195        for (int i = 0; i < pagesForSizeClass(blocksc); i++) {
196          getMetaAddress(address).store(sc, CSC_OFFSET);
197          address = address.plus(1<<VM.LOG_BYTES_IN_PAGE);
198        }
199      }
200    
201      /**
202       * Get the <i>client size class</i> meta data field for a given page
203       * (all blocks on a given page are homogeneous with respect to block
204       * size class).
205       *
206       * @param address The address of interest
207       * @return The size class field for the block containing the given address
208       */
209      @Inline
210      public static byte getClientSizeClass(Address address) {
211        address = Conversions.pageAlign(address);
212        byte rtn = getMetaAddress(address).loadByte(CSC_OFFSET);
213        return rtn;
214      }
215    
216      /**
217       * Set the free list meta data field for a given address (this is
218       * per-block meta data that is stored along with the block metadata
219       * but not used by the block allocator).
220       *
221       * @param address The address of interest
222       * @param value The value to which this field is to be set
223       */
224      @Inline
225      public static void setFreeListMeta(Address address, Address value) {
226        getMetaAddress(address).plus(FL_META_OFFSET).store(value);
227      }
228    
229      /**
230       * Get the free list meta data field for a given address (this is
231       * per-block meta data that is stored along with the block metadata
232       * but not used by the block allocator).
233       *
234       * @param address The address of interest
235       * @return The free list meta data field for the block containing
236       * the given address
237       */
238      @Inline
239      public static Address getFreeListMeta(Address address) {
240        return getMetaAddress(address).plus(FL_META_OFFSET).loadAddress();
241      }
242    
243      /**
244       * Set the <i>prev</i> meta data field for a given address
245       *
246       * @param address The address of interest
247       * @param prev The value to which this field is to be set
248       */
249      @Inline
250      public static void setNext(Address address, Address prev) {
251        getMetaAddress(address, NEXT_OFFSET).store(prev);
252      }
253    
254      /**
255       * Get the <i>prev</i> meta data field for a given address
256       *
257       * @param address The address of interest
258       * @return The prev field for the block containing the given address
259       */
260      @Inline
261      public static Address getNext(Address address) {
262        return getMetaAddress(address, NEXT_OFFSET).loadAddress();
263      }
264    
265      /**
266       * Get the address of some metadata given the address for which the
267       * metadata is required and the offset into the metadata that is of
268       * interest.
269       *
270       * @param address The address for which the metadata is required
271       * @return The address of the specified meta data
272       */
273      @Inline
274      private static Address getMetaAddress(Address address) {
275        return getMetaAddress(address, Offset.zero());
276      }
277    
278      /**
279       * Get the address of some metadata given the address for which the
280       * metadata is required and the offset into the metadata that is of
281       * interest.
282       *
283       * @param address The address for which the metadata is required
284       * @param offset The offset (in bytes) into the metadata block (eg
285       * for the prev pointer, or next pointer)
286       * @return The address of the specified meta data
287       */
288      @Inline
289      private static Address getMetaAddress(Address address, Offset offset) {
290        return EmbeddedMetaData.getMetaDataBase(address).plus(
291               EmbeddedMetaData.getMetaDataOffset(address, LOG_BYTE_COVERAGE, LOG_BYTES_IN_BLOCK_META)).plus(offset);
292      }
293    
294      /****************************************************************************
295       *
296       * Block marking
297       */
298    
299      /**
300       * Mark the metadata for this block.
301       *
302       * @param ref
303       */
304      @Inline
305      public static void markBlockMeta(ObjectReference ref) {
306        getMetaAddress(VM.objectModel.refToAddress(ref)).plus(FL_META_OFFSET).store(Word.one());
307      }
308    
309      /**
310       * Mark the metadata for this block.
311       *
312       * @param block The block address
313       */
314      @Inline
315      public static void markBlockMeta(Address block) {
316        getMetaAddress(block).plus(FL_META_OFFSET).store(Word.one());
317      }
318    
319      /**
320       * Return true if the metadata for this block was set.
321       *
322       * @param block The block address
323       * @return value of the meta data.
324       */
325      @Inline
326      public static boolean checkBlockMeta(Address block) {
327        return getMetaAddress(block).plus(FL_META_OFFSET).loadWord().EQ(Word.one());
328      }
329    
330      /**
331       * Clear the metadata for this block
332       *
333       * @param block The block address
334       */
335      @Inline
336      public static void clearBlockMeta(Address block) {
337        getMetaAddress(block).plus(FL_META_OFFSET).store(Word.zero());
338      }
339    }