/* * 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 static org.mmtk.utility.Constants.BITS_IN_BYTE; import org.mmtk.vm.VM; import org.vmmagic.pragma.Inline; import org.vmmagic.pragma.Uninterruptible; import org.vmmagic.unboxed.ObjectReference; import org.vmmagic.unboxed.Word; @Uninterruptible public class RCHeader { /* Requirements */ public static final int LOCAL_GC_BITS_REQUIRED = 0; public static final int GLOBAL_GC_BITS_REQUIRED = 8; public static final int GC_HEADER_WORDS_REQUIRED = 0; /**************************************************************************** * 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 <code>true</code> 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 oldValue, newValue; do { oldValue = VM.objectModel.prepareAvailableBits(object); if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(oldValue.and(LOGGING_MASK).EQ(LOGGED)); newValue = oldValue.or(UNLOGGED); } while(!VM.objectModel.attemptAvailableBits(object, oldValue, newValue)); } /************************************************************************ * RC header word */ /** The mark bit used for backup tracing. */ public static final int MARK_BIT = LOG_BIT + 2; public static final Word MARK_BIT_MASK = Word.one().lsh(MARK_BIT); /** The bit used for newly allocated objects. */ public static final int NEW_BIT = MARK_BIT + 1; public static final Word NEW_BIT_MASK = Word.one().lsh(NEW_BIT); /** Current not using any bits for cycle detection, etc */ public static final int BITS_USED = NEW_BIT + 1; /* Reference counting increments */ public static final int INCREMENT_SHIFT = BITS_USED; public static final Word INCREMENT = Word.one().lsh(INCREMENT_SHIFT); public static final Word DOUBLE_INCREMENT = INCREMENT.lsh(1); 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; /* Return values from incRC */ public static final int INC_OLD = 0; public static final int INC_NEW = 1; /* Limited bit thresholds and masks */ public static final Word refSticky = Word.one().lsh(BITS_IN_BYTE - BITS_USED).minus(Word.one()).lsh(INCREMENT_SHIFT); public static final int refStickyValue = refSticky.rshl(INCREMENT_SHIFT).toInt(); public static final Word WRITE_MASK = refSticky.not(); public static final Word READ_MASK = refSticky; /** * @param object an object * @return whether the object been marked by the most recent backup trace */ @Inline public static boolean isMarked(ObjectReference object) { return isHeaderMarked(VM.objectModel.readAvailableBitsWord(object)); } /** * Clears the mark status for the given object. * @param object the object whose status will be cleared */ @Inline public static void clearMarked(ObjectReference object) { Word oldValue, newValue; do { oldValue = VM.objectModel.prepareAvailableBits(object); if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(isHeaderMarked(oldValue)); newValue = oldValue.and(MARK_BIT_MASK.not()); } while (!VM.objectModel.attemptAvailableBits(object, oldValue, newValue)); } /** * @param header the header * @return whether the header has been marked */ @Inline private static boolean isHeaderMarked(Word header) { return header.and(MARK_BIT_MASK).EQ(MARK_BIT_MASK); } /** * Attempts to atomically mark this object. * * @param object the object to mark * @return {@code true} if the mark was performed, {@code false} * otherwise */ @Inline public static boolean testAndMark(ObjectReference object) { Word oldValue, newValue; do { oldValue = VM.objectModel.prepareAvailableBits(object); if (isHeaderMarked(oldValue)) { return false; } newValue = oldValue.or(MARK_BIT_MASK); } while (!VM.objectModel.attemptAvailableBits(object, oldValue, newValue)); return true; } /** * @param object an object * @return whether the object has been marked as new */ @Inline public static boolean isNew(ObjectReference object) { return isHeaderNew(VM.objectModel.readAvailableBitsWord(object)); } /** * @param header an object's header * @return whether the header has a new marking */ @Inline private static boolean isHeaderNew(Word header) { return header.and(NEW_BIT_MASK).NE(NEW_BIT_MASK); } /** * 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 <code>false</code>) */ @Inline public static void initializeHeader(ObjectReference object, boolean initialInc) { Word existingValue = VM.objectModel.readAvailableBitsWord(object); Word initialValue = existingValue.and(WRITE_MASK).or((initialInc) ? INCREMENT : Word.zero()); VM.objectModel.writeAvailableBitsWord(object, initialValue); } /** * Return <code>true</code> if given object is live * * @param object The object whose liveness is to be tested * @return <code>true</code> if the object is alive */ @Inline @Uninterruptible public static boolean isLiveRC(ObjectReference object) { Word value = VM.objectModel.readAvailableBitsWord(object); if (isStuck(value)) return true; return value.and(READ_MASK).GE(LIVE_THRESHOLD); } /** * Return the reference count for the object. * * @param object The object whose liveness is to be tested * @return <code>true</code> if the object is alive */ @Inline @Uninterruptible public static int getRC(ObjectReference object) { Word value = VM.objectModel.readAvailableBitsWord(object); if (isStuck(value)) return refStickyValue; return value.and(READ_MASK).rshl(INCREMENT_SHIFT).toInt(); } /** * Increment the reference count of an object. Return either * <code>INC_OLD</code> if the object is not new, * <code>INC_NEW</code> if the object is new. * * @param object The object whose RC is to be incremented. * @return <code>INC_OLD</code> if the object is not new, * <code>INC_NEW</code> if the object is new. */ @Inline public static int incRC(ObjectReference object) { Word oldValue, newValue; int rtn; if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(RCBase.isRCObject(object)); do { oldValue = VM.objectModel.prepareAvailableBits(object); if (isStuck(oldValue)) return INC_OLD; if (RCBase.BUILD_FOR_GENRC) { newValue = oldValue.plus(INCREMENT); rtn = INC_OLD; } else { if (isHeaderNew(oldValue)) { newValue = oldValue.plus(DOUBLE_INCREMENT); newValue = newValue.or(NEW_BIT_MASK); rtn = INC_NEW; } else { newValue = oldValue.plus(INCREMENT); rtn = INC_OLD; } } } while (!VM.objectModel.attemptAvailableBits(object, oldValue, newValue)); return rtn; } /** * 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 = VM.objectModel.prepareAvailableBits(object); if (isStuck(oldValue)) return DEC_ALIVE; newValue = oldValue.minus(INCREMENT); if (newValue.and(READ_MASK).LT(LIVE_THRESHOLD)) { rtn = DEC_KILL; } else { rtn = DEC_ALIVE; } } while (!VM.objectModel.attemptAvailableBits(object, oldValue, newValue)); return rtn; } /** * Initialize the reference count of an object. Return either * <code>INC_OLD</code> if the object is not new, * <code>INC_NEW</code> if the object is new. * * @param object The object whose RC is to be initialized. * @return <code>INC_OLD</code> if the object is not new, * <code>INC_NEW</code> if the object is new. */ @Inline public static int initRC(ObjectReference object) { Word oldValue, newValue; int rtn; if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(RCBase.isRCObject(object)); do { oldValue = VM.objectModel.prepareAvailableBits(object); newValue = oldValue.and(WRITE_MASK).or(INCREMENT); if (RCBase.BUILD_FOR_GENRC) { rtn = INC_OLD; } else { if (isHeaderNew(oldValue)) { newValue = newValue.or(NEW_BIT_MASK); rtn = INC_NEW; } else { rtn = INC_OLD; } } } while (!VM.objectModel.attemptAvailableBits(object, oldValue, newValue)); return rtn; } /** * Retain the reference count of an object. Return either * <code>INC_OLD</code> if the object is not new, * <code>INC_NEW</code> if the object is new. * * @param object The object whose RC is to be retained. * @return <code>INC_OLD</code> if the object is not new, * <code>INC_NEW</code> if the object is new. */ @Inline public static int remainRC(ObjectReference object) { Word oldValue, newValue; int rtn; if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(RCBase.isRCObject(object)); do { oldValue = VM.objectModel.prepareAvailableBits(object); newValue = oldValue; if (RCBase.BUILD_FOR_GENRC) { return INC_OLD; } else { if (isHeaderNew(oldValue)) { newValue = newValue.or(NEW_BIT_MASK); rtn = INC_NEW; } else { return INC_OLD; } } } while (!VM.objectModel.attemptAvailableBits(object, oldValue, newValue)); return rtn; } /** * @param value a word * @return whether the word contains a sticky marking */ @Inline private static boolean isStuck(Word value) { return value.and(refSticky).EQ(refSticky); } }