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 package org.mmtk.plan.refcount;
014
015 import org.mmtk.utility.Constants;
016 import org.mmtk.vm.VM;
017 import org.vmmagic.pragma.Inline;
018 import org.vmmagic.pragma.Uninterruptible;
019 import org.vmmagic.unboxed.ObjectReference;
020 import org.vmmagic.unboxed.Offset;
021 import org.vmmagic.unboxed.Word;
022
023 @Uninterruptible
024 public class RCHeader implements Constants {
025 /* Requirements */
026 public static final int LOCAL_GC_BITS_REQUIRED = 0;
027 public static final int GLOBAL_GC_BITS_REQUIRED = 2;
028 public static final int GC_HEADER_WORDS_REQUIRED = 1;
029
030 /****************************************************************************
031 * Object Logging (applies to *all* objects)
032 */
033
034 /* Mask bits to signify the start/finish of logging an object */
035 public static final int LOG_BIT = 0;
036 public static final Word LOGGED = Word.zero(); //...00000
037 public static final Word UNLOGGED = Word.one(); //...00001
038 public static final Word BEING_LOGGED = Word.one().lsh(2).minus(Word.one()); //...00011
039 public static final Word LOGGING_MASK = LOGGED.or(UNLOGGED).or(BEING_LOGGED); //...00011
040
041 /**
042 * Return true if <code>object</code> is yet to be logged (for
043 * coalescing RC).
044 *
045 * @param object The object in question
046 * @return <code>true</code> if <code>object</code> needs to be logged.
047 */
048 @Inline
049 @Uninterruptible
050 public static boolean logRequired(ObjectReference object) {
051 Word value = VM.objectModel.readAvailableBitsWord(object);
052 return value.and(LOGGING_MASK).EQ(UNLOGGED);
053 }
054
055 /**
056 * Attempt to log <code>object</code> for coalescing RC. This is
057 * used to handle a race to log the object, and returns
058 * <code>true</code> if we are to log the object and
059 * <code>false</code> if we lost the race to log the object.
060 *
061 * <p>If this method returns <code>true</code>, it leaves the object
062 * in the <code>BEING_LOGGED</code> state. It is the responsibility
063 * of the caller to change the object to <code>LOGGED</code> once
064 * the logging is complete.
065 *
066 * @see #makeLogged(ObjectReference)
067 * @param object The object in question
068 * @return <code>true</code> if the race to log
069 * <code>object</code>was won.
070 */
071 @Inline
072 @Uninterruptible
073 public static boolean attemptToLog(ObjectReference object) {
074 Word oldValue;
075 do {
076 oldValue = VM.objectModel.prepareAvailableBits(object);
077 if (oldValue.and(LOGGING_MASK).EQ(LOGGED)) {
078 return false;
079 }
080 } while ((oldValue.and(LOGGING_MASK).EQ(BEING_LOGGED)) ||
081 !VM.objectModel.attemptAvailableBits(object, oldValue, oldValue.or(BEING_LOGGED)));
082 if (VM.VERIFY_ASSERTIONS) {
083 Word value = VM.objectModel.readAvailableBitsWord(object);
084 VM.assertions._assert(value.and(LOGGING_MASK).EQ(BEING_LOGGED));
085 }
086 return true;
087 }
088
089
090 /**
091 * Signify completion of logging <code>object</code>.
092 *
093 * <code>object</code> is left in the <code>LOGGED</code> state.
094 *
095 * @see #attemptToLog(ObjectReference)
096 * @param object The object whose state is to be changed.
097 */
098 @Inline
099 @Uninterruptible
100 public static void makeLogged(ObjectReference object) {
101 Word value = VM.objectModel.readAvailableBitsWord(object);
102 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(value.and(LOGGING_MASK).NE(LOGGED));
103 VM.objectModel.writeAvailableBitsWord(object, value.and(LOGGING_MASK.not()));
104 }
105
106 /**
107 * Change <code>object</code>'s state to <code>UNLOGGED</code>.
108 *
109 * @param object The object whose state is to be changed.
110 */
111 @Inline
112 @Uninterruptible
113 public static void makeUnlogged(ObjectReference object) {
114 Word value = VM.objectModel.readAvailableBitsWord(object);
115 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(value.and(LOGGING_MASK).EQ(LOGGED));
116 VM.objectModel.writeAvailableBitsWord(object, value.or(UNLOGGED));
117 }
118
119 /************************************************************************
120 * RC header word
121 */
122
123 /* Header offset */
124 public static final Offset RC_HEADER_OFFSET = VM.objectModel.GC_HEADER_OFFSET();
125
126 /* Reserved to allow alignment hole filling to work */
127 public static final int RESERVED_ALIGN_BIT = 0;
128
129 /* The mark bit used for backup tracing. */
130 public static final int MARK_BIT = 1;
131 public static final Word MARK_BIT_MASK = Word.one().lsh(MARK_BIT);
132
133 /* Current not using any bits for cycle detection, etc */
134 public static final int BITS_USED = 2;
135
136 /* Reference counting increments */
137 public static final int INCREMENT_SHIFT = BITS_USED;
138 public static final Word INCREMENT = Word.one().lsh(INCREMENT_SHIFT);
139 public static final int AVAILABLE_BITS = BITS_IN_ADDRESS - BITS_USED;
140 public static final Word INCREMENT_LIMIT = Word.one().lsh(BITS_IN_ADDRESS-1).not();
141 public static final Word LIVE_THRESHOLD = INCREMENT;
142
143 /* Return values from decRC */
144 public static final int DEC_KILL = 0;
145 public static final int DEC_ALIVE = 1;
146
147 /**
148 * Has this object been marked by the most recent backup trace.
149 */
150 @Inline
151 public static boolean isMarked(ObjectReference object) {
152 return isHeaderMarked(object.toAddress().loadWord(RC_HEADER_OFFSET));
153 }
154
155 /**
156 * Has this object been marked by the most recent backup trace.
157 */
158 @Inline
159 public static void clearMarked(ObjectReference object) {
160 Word oldValue, newValue;
161 do {
162 oldValue = object.toAddress().prepareWord(RC_HEADER_OFFSET);
163 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(isHeaderMarked(oldValue));
164 newValue = oldValue.and(MARK_BIT_MASK.not());
165 } while (!object.toAddress().attempt(oldValue, newValue, RC_HEADER_OFFSET));
166 /*
167 Word header = object.toAddress().loadWord(RC_HEADER_OFFSET);
168 object.toAddress().store(header.and(MARK_BIT_MASK.not()), RC_HEADER_OFFSET);*/
169 }
170
171 /**
172 * Has this object been marked by the most recent backup trace.
173 */
174 @Inline
175 private static boolean isHeaderMarked(Word header) {
176 return header.and(MARK_BIT_MASK).EQ(MARK_BIT_MASK);
177 }
178
179 /**
180 * Attempt to atomically mark this object. Return true if the mark was performed.
181 */
182 @Inline
183 public static boolean testAndMark(ObjectReference object) {
184 Word oldValue, newValue;
185 do {
186 oldValue = object.toAddress().prepareWord(RC_HEADER_OFFSET);
187 if (isHeaderMarked(oldValue)) {
188 return false;
189 }
190 newValue = oldValue.or(MARK_BIT_MASK);
191 } while (!object.toAddress().attempt(oldValue, newValue, RC_HEADER_OFFSET));
192 return true;
193 }
194
195 /**
196 * Perform any required initialization of the GC portion of the header.
197 *
198 * @param object the object
199 * @param initialInc start with a reference count of 1 (0 if false)
200 */
201 @Inline
202 public static void initializeHeader(ObjectReference object, boolean initialInc) {
203 Word initialValue = (initialInc) ? INCREMENT : Word.zero();
204 object.toAddress().store(initialValue, RC_HEADER_OFFSET);
205 }
206
207 /**
208 * Return true if given object is live
209 *
210 * @param object The object whose liveness is to be tested
211 * @return True if the object is alive
212 */
213 @Inline
214 @Uninterruptible
215 public static boolean isLiveRC(ObjectReference object) {
216 return object.toAddress().loadWord(RC_HEADER_OFFSET).GE(LIVE_THRESHOLD);
217 }
218
219 /**
220 * Return the reference count for the object.
221 *
222 * @param object The object whose liveness is to be tested
223 * @return True if the object is alive
224 */
225 @Inline
226 @Uninterruptible
227 public static int getRC(ObjectReference object) {
228 return object.toAddress().loadWord(RC_HEADER_OFFSET).rshl(INCREMENT_SHIFT).toInt();
229 }
230
231 /**
232 * Increment the reference count of an object.
233 *
234 * @param object The object whose reference count is to be incremented.
235 */
236 @Inline
237 public static void incRC(ObjectReference object) {
238 Word oldValue, newValue;
239 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(RCBase.isRCObject(object));
240 do {
241 oldValue = object.toAddress().prepareWord(RC_HEADER_OFFSET);
242 newValue = oldValue.plus(INCREMENT);
243 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(newValue.LE(INCREMENT_LIMIT));
244 } while (!object.toAddress().attempt(oldValue, newValue, RC_HEADER_OFFSET));
245 }
246
247 /**
248 * Decrement the reference count of an object. Return either
249 * <code>DEC_KILL</code> if the count went to zero,
250 * <code>DEC_ALIVE</code> if the count did not go to zero.
251 *
252 * @param object The object whose RC is to be decremented.
253 * @return <code>DEC_KILL</code> if the count went to zero,
254 * <code>DEC_ALIVE</code> if the count did not go to zero.
255 */
256 @Inline
257 @Uninterruptible
258 public static int decRC(ObjectReference object) {
259 Word oldValue, newValue;
260 int rtn;
261 if (VM.VERIFY_ASSERTIONS) {
262 VM.assertions._assert(RCBase.isRCObject(object));
263 VM.assertions._assert(isLiveRC(object));
264 }
265 do {
266 oldValue = object.toAddress().prepareWord(RC_HEADER_OFFSET);
267 newValue = oldValue.minus(INCREMENT);
268 if (newValue.LT(LIVE_THRESHOLD)) {
269 rtn = DEC_KILL;
270 } else {
271 rtn = DEC_ALIVE;
272 }
273 } while (!object.toAddress().attempt(oldValue, newValue, RC_HEADER_OFFSET));
274 return rtn;
275 }
276 }