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.policy.immix;
014
015import static org.mmtk.utility.Constants.BYTES_IN_PAGE;
016import static org.mmtk.utility.Constants.LOG_BYTES_IN_ADDRESS;
017
018import org.mmtk.plan.Plan;
019import org.mmtk.policy.Space;
020import org.mmtk.vm.VM;
021import org.vmmagic.pragma.Uninterruptible;
022import org.vmmagic.unboxed.Address;
023import org.vmmagic.unboxed.AddressArray;
024
025@Uninterruptible
026public final class ChunkList {
027  private static final int LOG_PAGES_IN_CHUNK_MAP_BLOCK = 0;
028  private static final int ENTRIES_IN_CHUNK_MAP_BLOCK = (BYTES_IN_PAGE << LOG_PAGES_IN_CHUNK_MAP_BLOCK) >> LOG_BYTES_IN_ADDRESS;
029  private static final int CHUNK_MAP_BLOCKS = 1 << 4;
030  private static final int MAX_ENTRIES_IN_CHUNK_MAP = ENTRIES_IN_CHUNK_MAP_BLOCK * CHUNK_MAP_BLOCKS;
031  private final AddressArray chunkMap =  AddressArray.create(CHUNK_MAP_BLOCKS);
032  private int chunkMapLimit = -1;
033  private int chunkMapCursor = -1;
034
035  void reset() {
036    chunkMapLimit = chunkMapCursor;
037  }
038
039  public Address getHeadChunk() {
040    if (chunkMapLimit < 0)
041      return Address.zero();
042    else
043      return getMapAddress(0).loadAddress();
044  }
045
046  public Address getTailChunk() {
047    if (chunkMapLimit < 0)
048      return Address.zero();
049    else
050      return getMapAddress(chunkMapLimit).loadAddress();
051  }
052
053  void addNewChunkToMap(Address chunk) {
054    if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(Chunk.isAligned(chunk));
055    if (chunkMapCursor == MAX_ENTRIES_IN_CHUNK_MAP - 1)
056      consolidateMap();
057    chunkMapCursor++;
058    int index = getChunkIndex(chunkMapCursor);
059    int map = getChunkMap(chunkMapCursor);
060    if (map >= CHUNK_MAP_BLOCKS) {
061      Space.printUsageMB();
062      VM.assertions.fail("Overflow of chunk map!");
063    }
064    if (chunkMap.get(map).isZero()) {
065      Address tmp = Plan.metaDataSpace.acquire(1 << LOG_PAGES_IN_CHUNK_MAP_BLOCK);
066      if (tmp.isZero()) {
067        Space.printUsageMB();
068        VM.assertions.fail("Failed to allocate space for chunk map.  Is metadata virtual memory exhausted?");
069      }
070      chunkMap.set(map, tmp);
071    }
072    Address entry = chunkMap.get(map).plus(index << LOG_BYTES_IN_ADDRESS);
073    entry.store(chunk);
074    Chunk.setMap(chunk, chunkMapCursor);
075    if (VM.VERIFY_ASSERTIONS) checkMap();
076  }
077
078  void removeChunkFromMap(Address chunk) {
079    if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(Chunk.isAligned(chunk));
080    int entry = Chunk.getMap(chunk);
081    getMapAddress(entry).store(Address.zero());  // zero it it
082    Chunk.setMap(chunk, -entry);
083    if (VM.VERIFY_ASSERTIONS) checkMap();
084  }
085
086  private int getChunkIndex(int entry) {
087    return entry & (ENTRIES_IN_CHUNK_MAP_BLOCK - 1);
088  }
089
090  private int getChunkMap(int entry) {
091    return entry & ~(ENTRIES_IN_CHUNK_MAP_BLOCK - 1);
092  }
093
094  private Address getMapAddress(int entry) {
095    if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(entry >= 0);
096    if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(entry <= chunkMapCursor);
097    int index = getChunkIndex(entry);
098    int map = getChunkMap(entry);
099    return chunkMap.get(map).plus(index << LOG_BYTES_IN_ADDRESS);
100  }
101
102  /**
103   * A chunk iterator.  Return the next chunk in sequence, or null if the
104   * next chunk is the same chunk (ie there is only one chunk in the iterator).
105   *
106   * @param chunk The chunk
107   * @return The next chunk in the sequence, or null if next is chunk.
108   */
109  public Address nextChunk(Address chunk) {
110    return nextChunk(chunk, chunk);
111  }
112
113  /**
114   * A chunk iterator.  Return the next chunk in sequence, or {@code null} if the
115   * next chunk is limit.
116   *
117   * @param chunk The chunk
118   * @param limit The starting point (if next is equal to this, we're done)
119   * @return The next chunk in the sequence, or {@code null} if next is limit.
120   */
121  private Address nextChunk(final Address chunk, final Address limit) {
122    return nextChunk(chunk, Chunk.getMap(limit), 1);
123  }
124
125  /**
126   * A chunk iterator.  Return the next chunk in sequence, strided
127   * by stride steps, or {@code null} if the next chunk is start.
128   *
129   * @param chunk The chunk
130   * @param start The point where this iterator started, which defines its end-point
131   * @param stride The stride by which the iterator should be stepped
132   * @return The next chunk in the sequence, or {@code null} if next is start.
133   */
134  public Address nextChunk(final Address chunk, final int start, final int stride) {
135    if (VM.VERIFY_ASSERTIONS) checkMap();
136    return nextChunk(Chunk.getMap(chunk), start, stride);
137  }
138
139  /**
140   * A chunk iterator.  Return the next chunk in sequence, strided
141   * by stride steps, or {@code null} if the next chunk is start.
142   *
143   * @param entry The entry we're currently up to
144   * @param start The point where this iterator started, which defines its end-point
145   * @param stride The stride by which the iterator should be stepped
146   * @return The next chunk in the sequence, or {@code null} if next is start.
147   */
148  private Address nextChunk(int entry, final int start, final int stride) {
149    if (VM.VERIFY_ASSERTIONS) checkMap();
150    Address chunk;
151    do {
152      entry += stride;
153      if (entry > chunkMapLimit) {
154        entry = entry % stride;
155      }
156      chunk = getMapAddress(entry).loadAddress();
157    } while (chunk.isZero() && entry != start);
158    return entry == start ? Address.zero() : chunk;
159  }
160
161  public Address firstChunk(int ordinal, int stride) {
162    if (ordinal > chunkMapCursor) return Address.zero();
163    if (VM.VERIFY_ASSERTIONS) checkMap();
164    Address chunk = getMapAddress(ordinal).loadAddress();
165    return chunk.isZero() ? nextChunk(ordinal, ordinal, stride) : chunk;
166  }
167
168  private void checkMap() {
169    VM.assertions._assert(chunkMapLimit <= chunkMapCursor);
170    for (int entry = 0; entry <= chunkMapCursor; entry++) {
171      Address chunk = getMapAddress(entry).loadAddress();
172      if (!chunk.isZero())
173        VM.assertions._assert(Chunk.getMap(chunk) == entry);
174    }
175  }
176
177  public void consolidateMap() {
178    int oldCursor = 0;
179    int newCursor = -1;
180    while (oldCursor <= chunkMapCursor) {
181      Address chunk = getMapAddress(oldCursor).loadAddress();
182      if (!chunk.isZero()) {
183        getMapAddress(++newCursor).store(chunk);
184        Chunk.setMap(chunk, newCursor);
185      }
186      oldCursor++;
187    }
188    chunkMapCursor = newCursor;
189    chunkMapLimit = newCursor;
190    if (VM.VERIFY_ASSERTIONS) checkMap();
191  }
192}