blob: 3ca09813846eb99d89eca0d30e57a3a830051a8a [file] [log] [blame]
/*
* This file is part of the Jikes RVM project (http://jikesrvm.org).
*
* This file is licensed to You under the Eclipse Public License (EPL);
* You may not use this file except in compliance with the License. You
* may obtain a copy of the License at
*
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.
*/
package org.mmtk.utility.alloc;
import org.mmtk.policy.Space;
import org.mmtk.policy.immix.Block;
import org.mmtk.policy.immix.Chunk;
import org.mmtk.policy.immix.Line;
import org.mmtk.policy.immix.ImmixSpace;
import static org.mmtk.policy.immix.ImmixConstants.*;
import org.mmtk.utility.Constants;
import org.mmtk.utility.Log;
import org.mmtk.utility.options.Options;
import org.mmtk.vm.VM;
import org.vmmagic.unboxed.*;
import org.vmmagic.pragma.*;
/**
*
*/
@Uninterruptible
public class ImmixAllocator extends Allocator implements Constants {
/****************************************************************************
*
* Instance variables
*/
protected final ImmixSpace space; /* space this allocator is associated with */
private final boolean hot;
private final boolean copy;
private Address cursor; /* bump pointer */
private Address limit; /* limit for bump pointer */
private Address largeCursor; /* bump pointer for large objects */
private Address largeLimit; /* limit for bump pointer for large objects */
private boolean requestForLarge; /* is the current request for large or small? */
private boolean straddle; /* did the last allocation straddle a line? */
private int lineUseCount; /* approximation to bytes allocated (measured at 99% accurate) 07/10/30 */
private Address markTable;
private Address recyclableBlock;
private int line;
private boolean recyclableExhausted;
/**
* Constructor.
*
* @param space The space to bump point into.
* @param hot TODO
* @param copy TODO
*/
public ImmixAllocator(ImmixSpace space, boolean hot, boolean copy) {
this.space = space;
this.hot = hot;
this.copy = copy;
reset();
}
/**
* Reset the allocator. Note that this does not reset the space.
*/
public void reset() {
cursor = Address.zero();
limit = Address.zero();
largeCursor = Address.zero();
largeLimit = Address.zero();
markTable = Address.zero();
recyclableBlock = Address.zero();
requestForLarge = false;
recyclableExhausted = false;
line = LINES_IN_BLOCK;
lineUseCount = 0;
}
/*****************************************************************************
*
* Public interface
*/
/**
* Allocate space for a new object. This is frequently executed code and
* the coding is deliberaetly sensitive to the optimizing compiler.
* After changing this, always check the IR/MC that is generated.
*
* @param bytes The number of bytes allocated
* @param align The requested alignment
* @param offset The offset from the alignment
* @return The address of the first byte of the allocated region
*/
@Inline
public final Address alloc(int bytes, int align, int offset) {
/* establish how much we need */
Address start = alignAllocationNoFill(cursor, align, offset);
Address end = start.plus(bytes);
/* check whether we've exceeded the limit */
if (end.GT(limit)) {
if (bytes > BYTES_IN_LINE)
return overflowAlloc(bytes, align, offset);
else
return allocSlowHot(bytes, align, offset);
}
/* sufficient memory is available, so we can finish performing the allocation */
fillAlignmentGap(cursor, start);
cursor = end;
return start;
}
/**
* Allocate space for a new object. This is frequently executed code and
* the coding is deliberaetly sensitive to the optimizing compiler.
* After changing this, always check the IR/MC that is generated.
*
* @param bytes The number of bytes allocated
* @param align The requested alignment
* @param offset The offset from the alignment
* @return The address of the first byte of the allocated region
*/
public final Address overflowAlloc(int bytes, int align, int offset) {
/* establish how much we need */
Address start = alignAllocationNoFill(largeCursor, align, offset);
Address end = start.plus(bytes);
/* check whether we've exceeded the limit */
if (end.GT(largeLimit)) {
requestForLarge = true;
Address rtn = allocSlowInline(bytes, align, offset);
requestForLarge = false;
return rtn;
}
/* sufficient memory is available, so we can finish performing the allocation */
fillAlignmentGap(largeCursor, start);
largeCursor = end;
return start;
}
@Inline
public final boolean getLastAllocLineStraddle() {
return straddle;
}
/**
* External allocation slow path (called by superclass when slow path is
* actually taken. This is necessary (rather than a direct call
* from the fast path) because of the possibility of a thread switch
* and corresponding re-association of bump pointers to kernel
* threads.
*
* @param bytes The number of bytes allocated
* @param align The requested alignment
* @param offset The offset from the alignment
* @return The address of the first byte of the allocated region or
* zero on failure
*/
protected final Address allocSlowOnce(int bytes, int align, int offset) {
boolean success = false;
while (!success) {
Address ptr = space.getSpace(hot, copy, lineUseCount);
if (ptr.isZero()) {
lineUseCount = 0;
return ptr; // failed allocation --- we will need to GC
}
/* we have been given a clean block */
success = true;
lineUseCount = LINES_IN_BLOCK;
if (VM.VERIFY_ASSERTIONS)
VM.assertions._assert(Block.isAligned(ptr));
zeroBlock(ptr);
if (requestForLarge) {
largeCursor = ptr;
largeLimit = ptr.plus(BYTES_IN_BLOCK);
} else {
cursor = ptr;
limit = ptr.plus(BYTES_IN_BLOCK);
}
}
return alloc(bytes, align, offset);
}
/****************************************************************************
*
* Bump allocation
*/
/**
* Internal allocation slow path. This is called whenever the bump
* pointer reaches the internal limit. The code is forced out of
* line. If required we perform an external slow path take, which
* we inline into this method since this is already out of line.
*
* @param bytes The number of bytes allocated
* @param align The requested alignment
* @param offset The offset from the alignment
* @return The address of the first byte of the allocated region
*/
@NoInline
private Address allocSlowHot(int bytes, int align, int offset) {
if (acquireRecyclableLines(bytes, align, offset))
return alloc(bytes, align, offset);
else
return allocSlowInline(bytes, align, offset);
}
private boolean acquireRecyclableLines(int bytes, int align, int offset) {
while (line < LINES_IN_BLOCK || acquireRecyclableBlock()) {
line = space.getNextAvailableLine(markTable, line);
if (line < LINES_IN_BLOCK) {
int endLine = space.getNextUnavailableLine(markTable, line);
cursor = recyclableBlock.plus(Extent.fromIntSignExtend(line<<LOG_BYTES_IN_LINE));
limit = recyclableBlock.plus(Extent.fromIntSignExtend(endLine<<LOG_BYTES_IN_LINE));
if (SANITY_CHECK_LINE_MARKS) {
Address tmp = cursor;
while (tmp.LT(limit)) {
if (tmp.loadByte() != (byte) 0) {
Log.write("cursor: "); Log.writeln(cursor);
Log.write(" limit: "); Log.writeln(limit);
Log.write("current: "); Log.write(tmp);
Log.write(" value: "); Log.write(tmp.loadByte());
Log.write(" line: "); Log.write(line);
Log.write("endline: "); Log.write(endLine);
Log.write(" chunk: "); Log.write(Chunk.align(cursor));
Log.write(" hw: "); Log.write(Chunk.getHighWater(Chunk.align(cursor)));
Log.writeln(" values: ");
Address tmp2 = cursor;
while(tmp2.LT(limit)) { Log.write(tmp2.loadByte()); Log.write(" ");}
Log.writeln();
}
VM.assertions._assert(tmp.loadByte() == (byte) 0);
tmp = tmp.plus(1);
}
}
if (VM.VERIFY_ASSERTIONS && bytes <= BYTES_IN_LINE) {
Address start = alignAllocationNoFill(cursor, align, offset);
Address end = start.plus(bytes);
VM.assertions._assert(end.LE(limit));
}
VM.memory.zero(cursor, limit.diff(cursor).toWord().toExtent());
if (VM.VERIFY_ASSERTIONS && Options.verbose.getValue() >= 9) {
Log.write("Z["); Log.write(cursor); Log.write("->"); Log.write(limit); Log.writeln("]");
}
line = endLine;
if (VM.VERIFY_ASSERTIONS && copy) VM.assertions._assert(!Block.isDefragSource(cursor));
return true;
}
}
return false;
}
private boolean acquireRecyclableBlock() {
boolean rtn;
rtn = acquireRecyclableBlockAddressOrder();
if (rtn) {
markTable = Line.getBlockMarkTable(recyclableBlock);
line = 0;
}
return rtn;
}
@Inline
private boolean acquireRecyclableBlockAddressOrder() {
if (recyclableExhausted) {
if (VM.VERIFY_ASSERTIONS && Options.verbose.getValue() >= 9) {
Log.writeln("[no recyclable available]");
}
return false;
}
int markState = 0;
boolean usable = false;
while (!usable) {
Address next = recyclableBlock.plus(BYTES_IN_BLOCK);
if (recyclableBlock.isZero() || ImmixSpace.isRecycleAllocChunkAligned(next)) {
recyclableBlock = space.acquireReusableBlocks();
if (recyclableBlock.isZero()) {
recyclableExhausted = true;
if (VM.VERIFY_ASSERTIONS && Options.verbose.getValue() >= 9) {
Log.writeln("[recyclable exhausted]");
}
line = LINES_IN_BLOCK;
return false;
}
} else {
recyclableBlock = next;
}
markState = Block.getBlockMarkState(recyclableBlock);
usable = (markState > 0 && markState <= ImmixSpace.getReusuableMarkStateThreshold(copy));
if (copy && Block.isDefragSource(recyclableBlock))
usable = false;
}
if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(!Block.isUnused(recyclableBlock));
Block.setBlockAsReused(recyclableBlock);
lineUseCount += (LINES_IN_BLOCK-markState);
return true; // found something good
}
private void zeroBlock(Address block) {
// FIXME: efficiency check here!
if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(block.toWord().and(Word.fromIntSignExtend(BYTES_IN_BLOCK-1)).isZero());
VM.memory.zero(block, Extent.fromIntZeroExtend(BYTES_IN_BLOCK));
}
/** @return the space associated with this squish allocator */
public final Space getSpace() { return space; }
/**
* Print out the status of the allocator (for debugging)
*/
public final void show() {
Log.write("cursor = "); Log.write(cursor);
Log.write(" limit = "); Log.writeln(limit);
}
}