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    }