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
014package org.mmtk.utility.alloc;
015
016import org.mmtk.policy.Space;
017import org.mmtk.policy.immix.Block;
018import org.mmtk.policy.immix.Chunk;
019import org.mmtk.policy.immix.Line;
020import org.mmtk.policy.immix.ImmixSpace;
021import static org.mmtk.policy.immix.ImmixConstants.*;
022
023import org.mmtk.utility.Log;
024import org.mmtk.utility.options.Options;
025import org.mmtk.vm.VM;
026
027import org.vmmagic.unboxed.*;
028import org.vmmagic.pragma.*;
029
030/**
031 *
032 */
033@Uninterruptible
034public class ImmixAllocator extends Allocator {
035
036  /****************************************************************************
037   *
038   * Instance variables
039   */
040
041  /** space this allocator is associated with */
042  protected final ImmixSpace space;
043  private final boolean hot;
044  private final boolean copy;
045
046  /** bump pointer */
047  private Address cursor;
048  /** limit for bump pointer */
049  private Address limit;
050  /** bump pointer for large objects */
051  private Address largeCursor;
052  /** limit for bump pointer for large objects */
053  private Address largeLimit;
054  /** is the current request for large or small? */
055  private boolean requestForLarge;
056  /** did the last allocation straddle a line? */
057  private boolean straddle;
058  /** approximation to bytes allocated (measured at 99% accurate)  07/10/30 */
059  private int lineUseCount;
060  private Address markTable;
061  private Address recyclableBlock;
062  private int line;
063  private boolean recyclableExhausted;
064
065  /**
066   * Constructor.
067   *
068   * @param space The space to bump point into.
069   * @param hot TODO
070   * @param copy TODO
071   */
072  public ImmixAllocator(ImmixSpace space, boolean hot, boolean copy) {
073    this.space = space;
074    this.hot = hot;
075    this.copy = copy;
076    reset();
077  }
078
079  /**
080   * Reset the allocator. Note that this does not reset the space.
081   */
082  public void reset() {
083    cursor = Address.zero();
084    limit = Address.zero();
085    largeCursor = Address.zero();
086    largeLimit = Address.zero();
087    markTable = Address.zero();
088    recyclableBlock = Address.zero();
089    requestForLarge = false;
090    recyclableExhausted = false;
091    line = LINES_IN_BLOCK;
092    lineUseCount = 0;
093  }
094
095  /*****************************************************************************
096   *
097   * Public interface
098   */
099
100  /**
101   * Allocate space for a new object.  This is frequently executed code and
102   * the coding is deliberately sensitive to the optimizing compiler.
103   * After changing this, always check the IR/MC that is generated.
104   *
105   * @param bytes The number of bytes allocated
106   * @param align The requested alignment
107   * @param offset The offset from the alignment
108   * @return The address of the first byte of the allocated region
109   */
110  @Inline
111  public final Address alloc(int bytes, int align, int offset) {
112    /* establish how much we need */
113    Address start = alignAllocationNoFill(cursor, align, offset);
114    Address end = start.plus(bytes);
115
116    /* check whether we've exceeded the limit */
117    if (end.GT(limit)) {
118      if (bytes > BYTES_IN_LINE)
119        return overflowAlloc(bytes, align, offset);
120      else
121        return allocSlowHot(bytes, align, offset);
122    }
123
124    /* sufficient memory is available, so we can finish performing the allocation */
125    fillAlignmentGap(cursor, start);
126    cursor = end;
127
128    return start;
129  }
130
131  /**
132   * Allocate space for a new object.  This is frequently executed code and
133   * the coding is deliberately sensitive to the optimizing compiler.
134   * After changing this, always check the IR/MC that is generated.
135   *
136   * @param bytes The number of bytes allocated
137   * @param align The requested alignment
138   * @param offset The offset from the alignment
139   * @return The address of the first byte of the allocated region
140   */
141  public final Address overflowAlloc(int bytes, int align, int offset) {
142    /* establish how much we need */
143    Address start = alignAllocationNoFill(largeCursor, align, offset);
144    Address end = start.plus(bytes);
145
146    /* check whether we've exceeded the limit */
147    if (end.GT(largeLimit)) {
148      requestForLarge = true;
149      Address rtn =  allocSlowInline(bytes, align, offset);
150      requestForLarge = false;
151      return rtn;
152    }
153
154    /* sufficient memory is available, so we can finish performing the allocation */
155    fillAlignmentGap(largeCursor, start);
156    largeCursor = end;
157
158    return start;
159  }
160
161  @Inline
162  public final boolean getLastAllocLineStraddle() {
163    return straddle;
164  }
165
166  /**
167   * External allocation slow path (called by superclass when slow path is
168   * actually taken.  This is necessary (rather than a direct call
169   * from the fast path) because of the possibility of a thread switch
170   * and corresponding re-association of bump pointers to kernel
171   * threads.
172   *
173   * @param bytes The number of bytes allocated
174   * @param align The requested alignment
175   * @param offset The offset from the alignment
176   * @return The address of the first byte of the allocated region or
177   * zero on failure
178   */
179  @Override
180  protected final Address allocSlowOnce(int bytes, int align, int offset) {
181    Address ptr = space.getSpace(hot, copy, lineUseCount);
182
183    if (ptr.isZero()) {
184      lineUseCount = 0;
185      return ptr; // failed allocation --- we will need to GC
186    }
187
188    /* we have been given a clean block */
189    if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(Block.isAligned(ptr));
190    lineUseCount = LINES_IN_BLOCK;
191
192    if (requestForLarge) {
193      largeCursor = ptr;
194      largeLimit = ptr.plus(BYTES_IN_BLOCK);
195    } else {
196      cursor = ptr;
197      limit = ptr.plus(BYTES_IN_BLOCK);
198    }
199
200    return alloc(bytes, align, offset);
201  }
202
203  /****************************************************************************
204   *
205   * Bump allocation
206   */
207
208  /**
209   * Internal allocation slow path.  This is called whenever the bump
210   * pointer reaches the internal limit.  The code is forced out of
211   * line.  If required we perform an external slow path take, which
212   * we inline into this method since this is already out of line.
213   *
214   * @param bytes The number of bytes allocated
215   * @param align The requested alignment
216   * @param offset The offset from the alignment
217   * @return The address of the first byte of the allocated region
218   */
219  @NoInline
220  private Address allocSlowHot(int bytes, int align, int offset) {
221    if (acquireRecyclableLines(bytes, align, offset))
222      return alloc(bytes, align, offset);
223    else
224      return allocSlowInline(bytes, align, offset);
225  }
226
227  private boolean acquireRecyclableLines(int bytes, int align, int offset) {
228    while (line < LINES_IN_BLOCK || acquireRecyclableBlock()) {
229      line = space.getNextAvailableLine(markTable, line);
230      if (line < LINES_IN_BLOCK) {
231        int endLine = space.getNextUnavailableLine(markTable, line);
232        cursor = recyclableBlock.plus(Extent.fromIntSignExtend(line << LOG_BYTES_IN_LINE));
233        limit = recyclableBlock.plus(Extent.fromIntSignExtend(endLine << LOG_BYTES_IN_LINE));
234        if (SANITY_CHECK_LINE_MARKS) {
235          Address tmp = cursor;
236          while (tmp.LT(limit)) {
237            if (tmp.loadByte() != (byte) 0) {
238              Log.write("cursor: "); Log.writeln(cursor);
239              Log.write(" limit: "); Log.writeln(limit);
240              Log.write("current: "); Log.write(tmp);
241              Log.write("  value: "); Log.write(tmp.loadByte());
242              Log.write("   line: "); Log.write(line);
243              Log.write("endline: "); Log.write(endLine);
244              Log.write("  chunk: "); Log.write(Chunk.align(cursor));
245              Log.write("     hw: "); Log.write(Chunk.getHighWater(Chunk.align(cursor)));
246              Log.writeln(" values: ");
247              Address tmp2 = cursor;
248              while (tmp2.LT(limit)) {
249                Log.write(tmp2.loadByte());
250                Log.write(" ");
251              }
252              Log.writeln();
253            }
254            VM.assertions._assert(tmp.loadByte() == (byte) 0);
255            tmp = tmp.plus(1);
256          }
257        }
258        if (VM.VERIFY_ASSERTIONS && bytes <= BYTES_IN_LINE) {
259          Address start = alignAllocationNoFill(cursor, align, offset);
260          Address end = start.plus(bytes);
261          VM.assertions._assert(end.LE(limit));
262        }
263        VM.memory.zero(false, cursor, limit.diff(cursor).toWord().toExtent());
264        if (VM.VERIFY_ASSERTIONS && Options.verbose.getValue() >= 9) {
265          Log.write("Z["); Log.write(cursor); Log.write("->"); Log.write(limit); Log.writeln("]");
266        }
267
268        line = endLine;
269        if (VM.VERIFY_ASSERTIONS && copy) VM.assertions._assert(!Block.isDefragSource(cursor));
270        return true;
271      }
272    }
273    return false;
274  }
275
276  private boolean acquireRecyclableBlock() {
277    boolean rtn;
278    rtn = acquireRecyclableBlockAddressOrder();
279    if (rtn) {
280      markTable = Line.getBlockMarkTable(recyclableBlock);
281      line = 0;
282    }
283    return rtn;
284  }
285
286  @Inline
287  private boolean acquireRecyclableBlockAddressOrder() {
288    if (recyclableExhausted) {
289      if (VM.VERIFY_ASSERTIONS && Options.verbose.getValue() >= 9) {
290        Log.writeln("[no recyclable available]");
291      }
292      return false;
293    }
294    int markState = 0;
295    boolean usable = false;
296    while (!usable) {
297      Address next = recyclableBlock.plus(BYTES_IN_BLOCK);
298      if (recyclableBlock.isZero() || ImmixSpace.isRecycleAllocChunkAligned(next)) {
299        recyclableBlock = space.acquireReusableBlocks();
300        if (recyclableBlock.isZero()) {
301          recyclableExhausted = true;
302          if (VM.VERIFY_ASSERTIONS && Options.verbose.getValue() >= 9) {
303            Log.writeln("[recyclable exhausted]");
304          }
305          line = LINES_IN_BLOCK;
306          return false;
307        }
308      } else {
309        recyclableBlock = next;
310      }
311      markState = Block.getBlockMarkState(recyclableBlock);
312      usable = (markState > 0 && markState <= ImmixSpace.getReusuableMarkStateThreshold(copy));
313      if (copy && Block.isDefragSource(recyclableBlock))
314        usable = false;
315    }
316    if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(!Block.isUnused(recyclableBlock));
317    Block.setBlockAsReused(recyclableBlock);
318
319    lineUseCount += (LINES_IN_BLOCK - markState);
320    return true; // found something good
321  }
322
323  private void zeroBlock(Address block) {
324    // FIXME: efficiency check here!
325    if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(block.toWord().and(Word.fromIntSignExtend(BYTES_IN_BLOCK - 1)).isZero());
326    VM.memory.zero(false, block, Extent.fromIntZeroExtend(BYTES_IN_BLOCK));
327   }
328
329  /** @return the space associated with this squish allocator */
330  @Override
331  public final Space getSpace() {
332    return space;
333  }
334
335  /**
336   * Print out the status of the allocator (for debugging)
337   */
338  public final void show() {
339    Log.write("cursor = "); Log.write(cursor);
340    Log.write(" limit = "); Log.writeln(limit);
341  }
342}