/* * 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; } }