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.utility.deque;
014
015import static org.mmtk.utility.Constants.*;
016
017import org.mmtk.policy.RawPageSpace;
018
019import org.mmtk.vm.VM;
020
021import org.vmmagic.pragma.*;
022import org.vmmagic.unboxed.*;
023
024/**
025 * This supports <i>unsynchronized</i> enqueuing and dequeuing of buffers
026 * for shared use.  The data can be added to and removed from either end
027 * of the deque. This class is based upon the SharedQueue class.  The sorting
028 * routines were modified from code written by Narendran Sachindran and
029 * Matthew Hertz for GCTk.
030 */
031@Uninterruptible public abstract class SortSharedDeque extends SharedDeque {
032
033
034  private static final int BYTES_PUSHED = BYTES_IN_ADDRESS * 5;
035  private static final int MAX_STACK_SIZE = BYTES_PUSHED * 64;
036  private static final Offset INSERTION_SORT_LIMIT = Offset.fromIntSignExtend(80);
037
038  /***********************************************************************
039   *
040   * Class variables
041   */
042
043  /**
044   * @param name human-readable name of ther queue
045   * @param rps The space from which the instance should obtain buffers
046   * @param arity the queue's arity (number of words per entry)
047   */
048  public SortSharedDeque(String name, RawPageSpace rps, int arity) {
049    super(name, rps, arity);
050    stackBase = AddressArray.create(MAX_STACK_SIZE);
051    stackLoc = 0;
052  }
053
054  /***********************************************************************
055   *
056   * Sorting methods, utilities, and instance variables
057   */
058
059
060  /**
061   * Return the sorting key for the object passed as a parameter.
062   *
063   * @param obj The address of the object whose key is wanted
064   * @return The value of the sorting key for this object
065   */
066  protected abstract Word getKey(Address obj);
067
068  private static final Word mask16 = Word.fromIntZeroExtend(0xffff0000);
069  private static final Word mask8 = Word.fromIntZeroExtend(0x0000ff00);
070  private static final Word mask4 = Word.fromIntZeroExtend(0x000000f0);
071  private static final Word mask2 = Word.fromIntZeroExtend(0x0000000c);
072  private static final Word mask1 = Word.fromIntZeroExtend(0x00000002);
073
074  /**
075   * Find the highest bit that is set in a longword and return a mask
076   * with only that bit set.
077   *
078   * @param addr Value for which the mask needs to be found
079   * @return The highest bit set in the parameter
080   */
081  private static Word getBitMask(Word addr) {
082    int shift = 0;
083    if (!addr.and(mask16).isZero()) {
084      addr = addr.rshl(16);
085      shift += 16;
086    }
087    if (!addr.and(mask8).isZero()) {
088      addr = addr.rshl(8);
089      shift += 8;
090    }
091    if (!addr.and(mask4).isZero()) {
092      addr = addr.rshl(4);
093      shift += 4;
094    }
095    if (!addr.and(mask2).isZero()) {
096      addr = addr.rshl(2);
097      shift += 2;
098    }
099    if (!addr.and(mask1).isZero()) {
100      shift += 1;
101    }
102    return (Word.one().lsh(shift));
103  }
104
105  /**
106   * Perform insertion sort within an intra-block address range.
107   *
108   *  @param begin Start address of the range to be sorted
109   *  @param end End address of the range to be sorted
110   */
111  private void insertionSort(Address begin, Address end) {
112    Address rPtr = begin.minus(BYTES_IN_ADDRESS);
113    Address lPtr;
114
115    while (rPtr.GE(end)) {
116      Address rSlot = rPtr.loadAddress();
117      Word rKey = getKey(rSlot);
118      lPtr = rPtr.plus(BYTES_IN_ADDRESS);
119      while (lPtr.LE(begin)) {
120        Address lSlot = lPtr.loadAddress();
121        Word lKey = getKey(lSlot);
122        if (lKey.GT(rKey)) {
123          lPtr.minus(BYTES_IN_ADDRESS).store(lSlot);
124          lPtr = lPtr.plus(BYTES_IN_ADDRESS);
125        } else {
126          break;
127        }
128      }
129      lPtr.minus(BYTES_IN_ADDRESS).store(rSlot);
130      rPtr = rPtr.minus(BYTES_IN_ADDRESS);
131    }
132  }
133
134  /**
135   * Sort objects using radix exchange sort. An explicit stack is
136   *  maintained to avoid using recursion.
137   */
138  public void sort() {
139    Address startPtr, startLink, endPtr, endLink;
140    Word bitMask;
141    if (!head.EQ(HEAD_INITIAL_VALUE)) {
142      if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(tail.NE(TAIL_INITIAL_VALUE));
143      /* Obtain the bitmask for the first iteration and save the start &
144         end pointers and the bitmask on the stack */
145      initStack();
146      startPtr = popStack();
147      while (!startPtr.isZero()) {
148        startLink = popStack();
149        endPtr = popStack();
150        endLink = popStack();
151        bitMask = (popStack()).toWord();
152        if (startLink.NE(endLink)) {
153          partition(startPtr, startLink, endPtr, endLink, bitMask);
154        } else if (startPtr.GT(endPtr)) {
155          /* Use insertionSort for a limited number of objects within
156             a single block */
157          if (startPtr.diff(endPtr).sLT(INSERTION_SORT_LIMIT)) {
158            insertionSort(startPtr, endPtr);
159          } else {
160            partition(startPtr, startLink, endPtr, endLink, bitMask);
161          }
162        }
163        // Get the next set of data to sort
164        startPtr = popStack();
165      }
166    }
167    checkIfSorted();
168  }
169
170  /**
171   * Partition the slots in an address range based on the value in
172   * a particular bit. Place the start &amp; end addresses of the two
173   * partitions &amp; a bitmask per partition (which indicates the highest
174   * bit position at which the max &amp; min of a partition differ) on the
175   * stack. This works just like the partition in quick sort, except
176   * that a bit value is being compared here
177   *
178   * @param startAddr The start address of the range to be sorted
179   * @param startLinkAddr The address where the start block has its next field
180   * @param endAddr The end address of the range to be sorted
181   * @param endLinkAddr The address where the end block has its next field
182   * @param bitMask The mask in which the bit to be commpared is set
183   */
184  private void partition(Address startAddr, Address startLinkAddr,
185                               Address endAddr, Address endLinkAddr,
186                               Word bitMask) {
187    Address travPtr = endAddr;
188    Address travLink = endLinkAddr;
189    Address stopPtr = startAddr;
190    Address stopLink = startLinkAddr;
191    Address travSlot, stopSlot;
192    Word travKey, stopKey;
193    Word lmax = Word.zero(), rmax = Word.zero();
194    Word lmin = Word.max(), rmin = Word.max();
195
196    while (true) {
197      /* Compute the address range within the current block to compute. */
198      Address endOfBlock = travLink;
199
200      /* Move the left pointer until the right pointer is reached
201         or an address with a 0 value in the bit position is found. */
202      while (true) {
203        travSlot = travPtr.loadAddress();
204        travKey = getKey(travSlot);
205
206        /* If we reach the end. */
207        if (travPtr.EQ(stopPtr))
208          break;
209        /* If we found a 0 in bit position, break. */
210        if (travKey.and(bitMask).isZero())
211          break;
212        if (travKey.GT(rmax))
213          rmax = travKey;
214        if (travKey.LT(rmin))
215          rmin = travKey;
216        /* Move to the next entry. */
217        travPtr = travPtr.plus(BYTES_IN_ADDRESS);
218        /* If at end of remset block, move to next block */
219        if (travPtr.EQ(endOfBlock)) {
220          travLink = getPrev(travPtr);
221          endOfBlock = travLink;
222          travPtr = bufferStart(endOfBlock);
223        }
224      }
225
226      /* Store the end of the block. */
227      endOfBlock = bufferStart(stopPtr);
228      /* Move the right pointer until the left pointer is reached
229         or an address with a 1 value in the bit position is found. */
230      while (true) {
231        stopSlot = stopPtr.loadAddress();
232        stopKey = getKey(stopSlot);
233        /* Found a 1 in bit position, break. */
234        if (!stopKey.and(bitMask).isZero())
235          break;
236        if (stopKey.GT(lmax))
237          lmax = stopKey;
238        if (stopKey.LT(lmin))
239          lmin = stopKey;
240        if (stopPtr.EQ(travPtr))
241          break;
242        /* Move to the next entry, which may be in the next block. */
243        if (stopPtr.EQ(endOfBlock)) {
244          stopLink = getNext(stopLink);
245          stopPtr = stopLink;
246          endOfBlock = bufferStart(stopPtr);
247        }
248        stopPtr = stopPtr.minus(BYTES_IN_ADDRESS);
249      }
250      if (stopPtr.EQ(travPtr))
251        break;
252      /* interchange the values pointed to by the left and right pointers */
253      travPtr.store(stopSlot);
254      stopPtr.store(travSlot);
255    }
256
257    /* If max value is not equal to the min value in the right partition,
258       (not all slots are identical) push the right partition on to the stack */
259    if (rmax.GT(rmin)) {
260      if (travPtr.EQ(bufferStart(travPtr))) {
261        stopLink = getNext(travLink);
262        stopPtr = stopLink;
263      } else {
264        stopLink = travLink;
265        stopPtr = travPtr;
266      }
267      pushOnStack(getBitMask(rmax.xor(rmin)).toAddress());
268      pushOnStack(endLinkAddr);
269      pushOnStack(endAddr);
270      pushOnStack(stopLink);
271      pushOnStack(stopPtr.minus(BYTES_IN_ADDRESS));
272    }
273    /* if max value is not equal to the min value in the left partition,
274       (not all slots are identical) push the left partition on to the stack */
275    if (lmax.GT(lmin)) {
276      pushOnStack(getBitMask(lmax.xor(lmin)).toAddress());
277      pushOnStack(travLink);
278      pushOnStack(travPtr);
279      pushOnStack(startLinkAddr);
280      pushOnStack(startAddr);
281    }
282  }
283
284  /*************************************************************************
285   *
286   * Sorting Stack management routines
287   */
288
289  /**
290   *
291   */
292  private int stackLoc;
293  private final AddressArray stackBase;
294
295  /*
296   * Allocate memory for the stack and initialize it with the first range
297   * to partition
298   */
299  private void initStack() {
300    stackLoc = 0;
301
302    Address endOfBlock = tail;
303    Address startPtr = bufferStart(endOfBlock);
304    Word min = Word.max();
305    Word max = Word.zero();
306    // Find the max. and min addresses in the object buffer
307    while (endOfBlock.NE(HEAD_INITIAL_VALUE)) {
308      startPtr = bufferStart(endOfBlock);
309      while (startPtr.LT(endOfBlock)) {
310        Address startSlot = startPtr.loadAddress();
311        Word startKey = getKey(startSlot);
312        if (startKey.GT(max))
313          max = startKey;
314        if (startKey.LT(min))
315          min = startKey;
316        startPtr = startPtr.plus(BYTES_IN_ADDRESS);
317      }
318      endOfBlock = getPrev(startPtr);
319    }
320
321    // If max and min are different (not all elements are equal), push the
322    // start, end and bitmask values on the stack
323    if (max.GT(min)) {
324      pushOnStack(getBitMask(max.xor(min)).toAddress());
325      pushOnStack(tail);
326      pushOnStack(bufferStart(tail));
327      pushOnStack(startPtr);
328      pushOnStack(startPtr.minus(BYTES_IN_ADDRESS));
329    }
330  }
331
332  /**
333   * Push an address on to the stack
334   *
335  * @param val The address to be pushed
336   */
337  private void pushOnStack(Address val) {
338    stackBase.set(stackLoc, val);
339    stackLoc++;
340  }
341
342  /**
343   * Pop an address from the stack
344   *
345   * @return The address at the top of the stack, or 0 if stack is empty
346   */
347  private Address popStack() {
348    if (stackLoc == 0)
349      return Address.zero();
350    stackLoc--;
351    return stackBase.get(stackLoc);
352  }
353
354  /**
355   * Debug routine, used to check if the object buffer is sorted correctly in
356   * decreasing final reference deletion time
357 */
358  private void checkIfSorted() {
359    if (VM.VERIFY_ASSERTIONS) {
360      Address buf, end;
361      Word prevKey = Word.max();
362      end = tail;
363      buf = bufferStart(end);
364      while (buf.NE(HEAD_INITIAL_VALUE)) {
365        // iterate through the block
366        while (buf.LT(end)) {
367          Address slot = buf.loadAddress();
368          Word key = getKey(slot);
369          if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(key.LE(prevKey));
370          prevKey = key;
371          buf = buf.plus(BYTES_IN_ADDRESS);
372        }
373        end = getPrev(end);
374        buf = bufferStart(end);
375      }
376    }
377  }
378}