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 */
013package org.mmtk.plan.refcount;
014
015import static org.mmtk.utility.Constants.BITS_IN_BYTE;
016
017import org.mmtk.vm.VM;
018import org.vmmagic.pragma.Inline;
019import org.vmmagic.pragma.Uninterruptible;
020import org.vmmagic.unboxed.ObjectReference;
021import org.vmmagic.unboxed.Word;
022
023@Uninterruptible
024public class RCHeader {
025
026  /* Requirements */
027  public static final int LOCAL_GC_BITS_REQUIRED = 0;
028  public static final int GLOBAL_GC_BITS_REQUIRED = 8;
029  public static final int GC_HEADER_WORDS_REQUIRED = 0;
030
031  /****************************************************************************
032   * Object Logging (applies to *all* objects)
033   */
034
035  /* Mask bits to signify the start/finish of logging an object */
036
037  /**
038   *
039   */
040  public static final int      LOG_BIT  = 0;
041  public static final Word       LOGGED = Word.zero();                          //...00000
042  public static final Word    UNLOGGED  = Word.one();                           //...00001
043  public static final Word BEING_LOGGED = Word.one().lsh(2).minus(Word.one());  //...00011
044  public static final Word LOGGING_MASK = LOGGED.or(UNLOGGED).or(BEING_LOGGED); //...00011
045
046  /**
047   * Return <code>true</code> if <code>object</code> is yet to be logged (for
048   * coalescing RC).
049   *
050   * @param object The object in question
051   * @return <code>true</code> if <code>object</code> needs to be logged.
052   */
053  @Inline
054  @Uninterruptible
055  public static boolean logRequired(ObjectReference object) {
056    Word value = VM.objectModel.readAvailableBitsWord(object);
057    return value.and(LOGGING_MASK).EQ(UNLOGGED);
058  }
059
060  /**
061   * Attempt to log <code>object</code> for coalescing RC. This is
062   * used to handle a race to log the object, and returns
063   * <code>true</code> if we are to log the object and
064   * <code>false</code> if we lost the race to log the object.
065   *
066   * <p>If this method returns <code>true</code>, it leaves the object
067   * in the <code>BEING_LOGGED</code> state.  It is the responsibility
068   * of the caller to change the object to <code>LOGGED</code> once
069   * the logging is complete.
070   *
071   * @see #makeLogged(ObjectReference)
072   * @param object The object in question
073   * @return <code>true</code> if the race to log
074   * <code>object</code>was won.
075   */
076  @Inline
077  @Uninterruptible
078  public static boolean attemptToLog(ObjectReference object) {
079    Word oldValue;
080    do {
081      oldValue = VM.objectModel.prepareAvailableBits(object);
082      if (oldValue.and(LOGGING_MASK).EQ(LOGGED)) {
083        return false;
084      }
085    } while ((oldValue.and(LOGGING_MASK).EQ(BEING_LOGGED)) ||
086             !VM.objectModel.attemptAvailableBits(object, oldValue, oldValue.or(BEING_LOGGED)));
087    if (VM.VERIFY_ASSERTIONS) {
088      Word value = VM.objectModel.readAvailableBitsWord(object);
089      VM.assertions._assert(value.and(LOGGING_MASK).EQ(BEING_LOGGED));
090    }
091    return true;
092  }
093
094
095  /**
096   * Signify completion of logging <code>object</code>.
097   *
098   * <code>object</code> is left in the <code>LOGGED</code> state.
099   *
100   * @see #attemptToLog(ObjectReference)
101   * @param object The object whose state is to be changed.
102   */
103  @Inline
104  @Uninterruptible
105  public static void makeLogged(ObjectReference object) {
106    Word value = VM.objectModel.readAvailableBitsWord(object);
107    if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(value.and(LOGGING_MASK).NE(LOGGED));
108    VM.objectModel.writeAvailableBitsWord(object, value.and(LOGGING_MASK.not()));
109  }
110
111  /**
112   * Change <code>object</code>'s state to <code>UNLOGGED</code>.
113   *
114   * @param object The object whose state is to be changed.
115   */
116  @Inline
117  @Uninterruptible
118  public static void makeUnlogged(ObjectReference object) {
119    Word oldValue, newValue;
120    do {
121      oldValue = VM.objectModel.prepareAvailableBits(object);
122      if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(oldValue.and(LOGGING_MASK).EQ(LOGGED));
123      newValue = oldValue.or(UNLOGGED);
124    } while(!VM.objectModel.attemptAvailableBits(object, oldValue, newValue));
125  }
126
127  /************************************************************************
128   * RC header word
129   */
130
131  /** The mark bit used for backup tracing. */
132  public static final int MARK_BIT = LOG_BIT + 2;
133  public static final Word MARK_BIT_MASK = Word.one().lsh(MARK_BIT);
134
135  /** The bit used for newly allocated objects. */
136  public static final int NEW_BIT = MARK_BIT + 1;
137  public static final Word NEW_BIT_MASK = Word.one().lsh(NEW_BIT);
138
139  /** Current not using any bits for cycle detection, etc */
140  public static final int BITS_USED = NEW_BIT + 1;
141
142  /* Reference counting increments */
143
144  public static final int INCREMENT_SHIFT = BITS_USED;
145  public static final Word INCREMENT = Word.one().lsh(INCREMENT_SHIFT);
146  public static final Word DOUBLE_INCREMENT = INCREMENT.lsh(1);
147  public static final Word LIVE_THRESHOLD = INCREMENT;
148
149  /* Return values from decRC */
150
151  public static final int DEC_KILL = 0;
152  public static final int DEC_ALIVE = 1;
153
154  /* Return values from incRC */
155
156  public static final int INC_OLD = 0;
157  public static final int INC_NEW = 1;
158
159  /* Limited bit thresholds and masks */
160
161  public static final Word refSticky = Word.one().lsh(BITS_IN_BYTE - BITS_USED).minus(Word.one()).lsh(INCREMENT_SHIFT);
162  public static final int refStickyValue = refSticky.rshl(INCREMENT_SHIFT).toInt();
163  public static final Word WRITE_MASK = refSticky.not();
164  public static final Word READ_MASK = refSticky;
165
166  /**
167   * @param object an object
168   * @return whether the object been marked by the most recent backup trace
169   */
170  @Inline
171  public static boolean isMarked(ObjectReference object) {
172    return isHeaderMarked(VM.objectModel.readAvailableBitsWord(object));
173  }
174
175  /**
176   * Clears the mark status for the given object.
177   * @param object the object whose status will be cleared
178   */
179  @Inline
180  public static void clearMarked(ObjectReference object) {
181    Word oldValue, newValue;
182    do {
183      oldValue = VM.objectModel.prepareAvailableBits(object);
184      if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(isHeaderMarked(oldValue));
185      newValue = oldValue.and(MARK_BIT_MASK.not());
186    } while (!VM.objectModel.attemptAvailableBits(object, oldValue, newValue));
187  }
188
189  /**
190   * @param header the header
191   * @return whether the header has been marked
192   */
193  @Inline
194  private static boolean isHeaderMarked(Word header) {
195    return header.and(MARK_BIT_MASK).EQ(MARK_BIT_MASK);
196  }
197
198  /**
199   * Attempts to atomically mark this object.
200   *
201   * @param object the object to mark
202   * @return {@code true} if the mark was performed, {@code false}
203   *  otherwise
204   */
205  @Inline
206  public static boolean testAndMark(ObjectReference object) {
207    Word oldValue, newValue;
208    do {
209      oldValue = VM.objectModel.prepareAvailableBits(object);
210      if (isHeaderMarked(oldValue)) {
211        return false;
212      }
213      newValue = oldValue.or(MARK_BIT_MASK);
214    } while (!VM.objectModel.attemptAvailableBits(object, oldValue, newValue));
215    return true;
216  }
217
218  /**
219   * @param object an object
220   * @return whether the object has been marked as new
221   */
222  @Inline
223  public static boolean isNew(ObjectReference object) {
224    return isHeaderNew(VM.objectModel.readAvailableBitsWord(object));
225  }
226
227  /**
228   * @param header an object's header
229   * @return whether the header has a new marking
230   */
231  @Inline
232  private static boolean isHeaderNew(Word header) {
233    return header.and(NEW_BIT_MASK).NE(NEW_BIT_MASK);
234  }
235
236  /**
237   * Perform any required initialization of the GC portion of the header.
238   *
239   * @param object the object
240   * @param initialInc start with a reference count of 1 (0 if <code>false</code>)
241   */
242  @Inline
243  public static void initializeHeader(ObjectReference object, boolean initialInc) {
244    Word existingValue = VM.objectModel.readAvailableBitsWord(object);
245    Word initialValue = existingValue.and(WRITE_MASK).or((initialInc) ? INCREMENT : Word.zero());
246    VM.objectModel.writeAvailableBitsWord(object, initialValue);
247  }
248
249  /**
250   * Return <code>true</code> if given object is live
251   *
252   * @param object The object whose liveness is to be tested
253   * @return <code>true</code> if the object is alive
254   */
255  @Inline
256  @Uninterruptible
257  public static boolean isLiveRC(ObjectReference object) {
258    Word value = VM.objectModel.readAvailableBitsWord(object);
259    if (isStuck(value)) return true;
260    return value.and(READ_MASK).GE(LIVE_THRESHOLD);
261  }
262
263  /**
264   * Return the reference count for the object.
265   *
266   * @param object The object whose liveness is to be tested
267   * @return <code>true</code> if the object is alive
268   */
269  @Inline
270  @Uninterruptible
271  public static int getRC(ObjectReference object) {
272    Word value = VM.objectModel.readAvailableBitsWord(object);
273    if (isStuck(value)) return refStickyValue;
274    return value.and(READ_MASK).rshl(INCREMENT_SHIFT).toInt();
275  }
276
277  /**
278   * Increment the reference count of an object.  Return either
279   * <code>INC_OLD</code> if the object is not new,
280   * <code>INC_NEW</code> if the object is new.
281   *
282   * @param object The object whose RC is to be incremented.
283   * @return <code>INC_OLD</code> if the object is not new,
284   * <code>INC_NEW</code> if the object is new.
285   */
286  @Inline
287  public static int incRC(ObjectReference object) {
288    Word oldValue, newValue;
289    int rtn;
290    if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(RCBase.isRCObject(object));
291    do {
292      oldValue = VM.objectModel.prepareAvailableBits(object);
293      if (isStuck(oldValue)) return INC_OLD;
294      if (RCBase.BUILD_FOR_GENRC) {
295        newValue = oldValue.plus(INCREMENT);
296        rtn = INC_OLD;
297      } else {
298        if (isHeaderNew(oldValue)) {
299          newValue = oldValue.plus(DOUBLE_INCREMENT);
300          newValue = newValue.or(NEW_BIT_MASK);
301          rtn = INC_NEW;
302        } else {
303          newValue = oldValue.plus(INCREMENT);
304          rtn = INC_OLD;
305        }
306      }
307    } while (!VM.objectModel.attemptAvailableBits(object, oldValue, newValue));
308    return rtn;
309  }
310
311  /**
312   * Decrement the reference count of an object.  Return either
313   * <code>DEC_KILL</code> if the count went to zero,
314   * <code>DEC_ALIVE</code> if the count did not go to zero.
315   *
316   * @param object The object whose RC is to be decremented.
317   * @return <code>DEC_KILL</code> if the count went to zero,
318   * <code>DEC_ALIVE</code> if the count did not go to zero.
319   */
320  @Inline
321  @Uninterruptible
322  public static int decRC(ObjectReference object) {
323    Word oldValue, newValue;
324    int rtn;
325    if (VM.VERIFY_ASSERTIONS) {
326      VM.assertions._assert(RCBase.isRCObject(object));
327      VM.assertions._assert(isLiveRC(object));
328    }
329    do {
330      oldValue = VM.objectModel.prepareAvailableBits(object);
331      if (isStuck(oldValue)) return DEC_ALIVE;
332      newValue = oldValue.minus(INCREMENT);
333      if (newValue.and(READ_MASK).LT(LIVE_THRESHOLD)) {
334        rtn = DEC_KILL;
335      } else {
336        rtn = DEC_ALIVE;
337      }
338    } while (!VM.objectModel.attemptAvailableBits(object, oldValue, newValue));
339    return rtn;
340  }
341
342  /**
343   * Initialize the reference count of an object.  Return either
344   * <code>INC_OLD</code> if the object is not new,
345   * <code>INC_NEW</code> if the object is new.
346   *
347   * @param object The object whose RC is to be initialized.
348   * @return <code>INC_OLD</code> if the object is not new,
349   * <code>INC_NEW</code> if the object is new.
350   */
351  @Inline
352  public static int initRC(ObjectReference object) {
353    Word oldValue, newValue;
354    int rtn;
355    if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(RCBase.isRCObject(object));
356    do {
357      oldValue = VM.objectModel.prepareAvailableBits(object);
358      newValue = oldValue.and(WRITE_MASK).or(INCREMENT);
359      if (RCBase.BUILD_FOR_GENRC) {
360        rtn = INC_OLD;
361      } else {
362        if (isHeaderNew(oldValue)) {
363          newValue = newValue.or(NEW_BIT_MASK);
364          rtn = INC_NEW;
365        } else {
366          rtn = INC_OLD;
367        }
368      }
369    } while (!VM.objectModel.attemptAvailableBits(object, oldValue, newValue));
370    return rtn;
371  }
372
373  /**
374   * Retain the reference count of an object.  Return either
375   * <code>INC_OLD</code> if the object is not new,
376   * <code>INC_NEW</code> if the object is new.
377   *
378   * @param object The object whose RC is to be retained.
379   * @return <code>INC_OLD</code> if the object is not new,
380   * <code>INC_NEW</code> if the object is new.
381   */
382  @Inline
383  public static int remainRC(ObjectReference object) {
384    Word oldValue, newValue;
385    int rtn;
386    if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(RCBase.isRCObject(object));
387    do {
388      oldValue = VM.objectModel.prepareAvailableBits(object);
389      newValue = oldValue;
390      if (RCBase.BUILD_FOR_GENRC) {
391        return INC_OLD;
392      } else {
393        if (isHeaderNew(oldValue)) {
394          newValue = newValue.or(NEW_BIT_MASK);
395          rtn = INC_NEW;
396        } else {
397          return INC_OLD;
398        }
399      }
400    } while (!VM.objectModel.attemptAvailableBits(object, oldValue, newValue));
401    return rtn;
402  }
403
404  /**
405   * @param value a word
406   * @return whether the word contains a sticky marking
407   */
408  @Inline
409  private static boolean isStuck(Word value) {
410    return value.and(refSticky).EQ(refSticky);
411  }
412}