blob: a987b5a3d8a12424f71a58f581555741889bb78b [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.plan.refcount;
import org.mmtk.utility.Constants;
import org.mmtk.vm.VM;
import org.vmmagic.pragma.Inline;
import org.vmmagic.pragma.Uninterruptible;
import org.vmmagic.unboxed.ObjectReference;
import org.vmmagic.unboxed.Offset;
import org.vmmagic.unboxed.Word;
@Uninterruptible
public class RCHeader implements Constants {
/* Requirements */
public static final int LOCAL_GC_BITS_REQUIRED = 0;
public static final int GLOBAL_GC_BITS_REQUIRED = 2;
public static final int GC_HEADER_WORDS_REQUIRED = 1;
/****************************************************************************
* Object Logging (applies to *all* objects)
*/
/* Mask bits to signify the start/finish of logging an object */
public static final int LOG_BIT = 0;
public static final Word LOGGED = Word.zero(); //...00000
public static final Word UNLOGGED = Word.one(); //...00001
public static final Word BEING_LOGGED = Word.one().lsh(2).minus(Word.one()); //...00011
public static final Word LOGGING_MASK = LOGGED.or(UNLOGGED).or(BEING_LOGGED); //...00011
/**
* Return true if <code>object</code> is yet to be logged (for
* coalescing RC).
*
* @param object The object in question
* @return <code>true</code> if <code>object</code> needs to be logged.
*/
@Inline
@Uninterruptible
public static boolean logRequired(ObjectReference object) {
Word value = VM.objectModel.readAvailableBitsWord(object);
return value.and(LOGGING_MASK).EQ(UNLOGGED);
}
/**
* Attempt to log <code>object</code> for coalescing RC. This is
* used to handle a race to log the object, and returns
* <code>true</code> if we are to log the object and
* <code>false</code> if we lost the race to log the object.
*
* <p>If this method returns <code>true</code>, it leaves the object
* in the <code>BEING_LOGGED</code> state. It is the responsibility
* of the caller to change the object to <code>LOGGED</code> once
* the logging is complete.
*
* @see #makeLogged(ObjectReference)
* @param object The object in question
* @return <code>true</code> if the race to log
* <code>object</code>was won.
*/
@Inline
@Uninterruptible
public static boolean attemptToLog(ObjectReference object) {
Word oldValue;
do {
oldValue = VM.objectModel.prepareAvailableBits(object);
if (oldValue.and(LOGGING_MASK).EQ(LOGGED)) {
return false;
}
} while ((oldValue.and(LOGGING_MASK).EQ(BEING_LOGGED)) ||
!VM.objectModel.attemptAvailableBits(object, oldValue, oldValue.or(BEING_LOGGED)));
if (VM.VERIFY_ASSERTIONS) {
Word value = VM.objectModel.readAvailableBitsWord(object);
VM.assertions._assert(value.and(LOGGING_MASK).EQ(BEING_LOGGED));
}
return true;
}
/**
* Signify completion of logging <code>object</code>.
*
* <code>object</code> is left in the <code>LOGGED</code> state.
*
* @see #attemptToLog(ObjectReference)
* @param object The object whose state is to be changed.
*/
@Inline
@Uninterruptible
public static void makeLogged(ObjectReference object) {
Word value = VM.objectModel.readAvailableBitsWord(object);
if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(value.and(LOGGING_MASK).NE(LOGGED));
VM.objectModel.writeAvailableBitsWord(object, value.and(LOGGING_MASK.not()));
}
/**
* Change <code>object</code>'s state to <code>UNLOGGED</code>.
*
* @param object The object whose state is to be changed.
*/
@Inline
@Uninterruptible
public static void makeUnlogged(ObjectReference object) {
Word value = VM.objectModel.readAvailableBitsWord(object);
if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(value.and(LOGGING_MASK).EQ(LOGGED));
VM.objectModel.writeAvailableBitsWord(object, value.or(UNLOGGED));
}
/************************************************************************
* RC header word
*/
/* Header offset */
public static final Offset RC_HEADER_OFFSET = VM.objectModel.GC_HEADER_OFFSET();
/* Reserved to allow alignment hole filling to work */
public static final int RESERVED_ALIGN_BIT = 0;
/* The mark bit used for backup tracing. */
public static final int MARK_BIT = 1;
public static final Word MARK_BIT_MASK = Word.one().lsh(MARK_BIT);
/* Current not using any bits for cycle detection, etc */
public static final int BITS_USED = 2;
/* Reference counting increments */
public static final int INCREMENT_SHIFT = BITS_USED;
public static final Word INCREMENT = Word.one().lsh(INCREMENT_SHIFT);
public static final int AVAILABLE_BITS = BITS_IN_ADDRESS - BITS_USED;
public static final Word INCREMENT_LIMIT = Word.one().lsh(BITS_IN_ADDRESS-1).not();
public static final Word LIVE_THRESHOLD = INCREMENT;
/* Return values from decRC */
public static final int DEC_KILL = 0;
public static final int DEC_ALIVE = 1;
/**
* Has this object been marked by the most recent backup trace.
*/
@Inline
public static boolean isMarked(ObjectReference object) {
return isHeaderMarked(object.toAddress().loadWord(RC_HEADER_OFFSET));
}
/**
* Has this object been marked by the most recent backup trace.
*/
@Inline
public static void clearMarked(ObjectReference object) {
Word oldValue, newValue;
do {
oldValue = object.toAddress().prepareWord(RC_HEADER_OFFSET);
if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(isHeaderMarked(oldValue));
newValue = oldValue.and(MARK_BIT_MASK.not());
} while (!object.toAddress().attempt(oldValue, newValue, RC_HEADER_OFFSET));
/*
Word header = object.toAddress().loadWord(RC_HEADER_OFFSET);
object.toAddress().store(header.and(MARK_BIT_MASK.not()), RC_HEADER_OFFSET);*/
}
/**
* Has this object been marked by the most recent backup trace.
*/
@Inline
private static boolean isHeaderMarked(Word header) {
return header.and(MARK_BIT_MASK).EQ(MARK_BIT_MASK);
}
/**
* Attempt to atomically mark this object. Return true if the mark was performed.
*/
@Inline
public static boolean testAndMark(ObjectReference object) {
Word oldValue, newValue;
do {
oldValue = object.toAddress().prepareWord(RC_HEADER_OFFSET);
if (isHeaderMarked(oldValue)) {
return false;
}
newValue = oldValue.or(MARK_BIT_MASK);
} while (!object.toAddress().attempt(oldValue, newValue, RC_HEADER_OFFSET));
return true;
}
/**
* Perform any required initialization of the GC portion of the header.
*
* @param object the object
* @param initialInc start with a reference count of 1 (0 if false)
*/
@Inline
public static void initializeHeader(ObjectReference object, boolean initialInc) {
Word initialValue = (initialInc) ? INCREMENT : Word.zero();
object.toAddress().store(initialValue, RC_HEADER_OFFSET);
}
/**
* Return true if given object is live
*
* @param object The object whose liveness is to be tested
* @return True if the object is alive
*/
@Inline
@Uninterruptible
public static boolean isLiveRC(ObjectReference object) {
return object.toAddress().loadWord(RC_HEADER_OFFSET).GE(LIVE_THRESHOLD);
}
/**
* Return the reference count for the object.
*
* @param object The object whose liveness is to be tested
* @return True if the object is alive
*/
@Inline
@Uninterruptible
public static int getRC(ObjectReference object) {
return object.toAddress().loadWord(RC_HEADER_OFFSET).rshl(INCREMENT_SHIFT).toInt();
}
/**
* Increment the reference count of an object.
*
* @param object The object whose reference count is to be incremented.
*/
@Inline
public static void incRC(ObjectReference object) {
Word oldValue, newValue;
if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(RCBase.isRCObject(object));
do {
oldValue = object.toAddress().prepareWord(RC_HEADER_OFFSET);
newValue = oldValue.plus(INCREMENT);
if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(newValue.LE(INCREMENT_LIMIT));
} while (!object.toAddress().attempt(oldValue, newValue, RC_HEADER_OFFSET));
}
/**
* Decrement the reference count of an object. Return either
* <code>DEC_KILL</code> if the count went to zero,
* <code>DEC_ALIVE</code> if the count did not go to zero.
*
* @param object The object whose RC is to be decremented.
* @return <code>DEC_KILL</code> if the count went to zero,
* <code>DEC_ALIVE</code> if the count did not go to zero.
*/
@Inline
@Uninterruptible
public static int decRC(ObjectReference object) {
Word oldValue, newValue;
int rtn;
if (VM.VERIFY_ASSERTIONS) {
VM.assertions._assert(RCBase.isRCObject(object));
VM.assertions._assert(isLiveRC(object));
}
do {
oldValue = object.toAddress().prepareWord(RC_HEADER_OFFSET);
newValue = oldValue.minus(INCREMENT);
if (newValue.LT(LIVE_THRESHOLD)) {
rtn = DEC_KILL;
} else {
rtn = DEC_ALIVE;
}
} while (!object.toAddress().attempt(oldValue, newValue, RC_HEADER_OFFSET));
return rtn;
}
}