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