/*
* 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.utility.deque;
import static org.mmtk.utility.Constants.*;
import org.mmtk.policy.RawPageSpace;
import org.mmtk.vm.VM;
import org.vmmagic.pragma.*;
import org.vmmagic.unboxed.*;
/**
* This supports <i>unsynchronized</i> enqueuing and dequeuing of buffers
* for shared use. The data can be added to and removed from either end
* of the deque. This class is based upon the SharedQueue class. The sorting
* routines were modified from code written by Narendran Sachindran and
* Matthew Hertz for GCTk.
*/
@Uninterruptible public abstract class SortSharedDeque extends SharedDeque {
private static final int BYTES_PUSHED = BYTES_IN_ADDRESS * 5;
private static final int MAX_STACK_SIZE = BYTES_PUSHED * 64;
private static final Offset INSERTION_SORT_LIMIT = Offset.fromIntSignExtend(80);
/***********************************************************************
*
* Class variables
*/
/**
* @param name human-readable name of ther queue
* @param rps The space from which the instance should obtain buffers
* @param arity the queue's arity (number of words per entry)
*/
public SortSharedDeque(String name, RawPageSpace rps, int arity) {
super(name, rps, arity);
stackBase = AddressArray.create(MAX_STACK_SIZE);
stackLoc = 0;
}
/***********************************************************************
*
* Sorting methods, utilities, and instance variables
*/
/**
* Return the sorting key for the object passed as a parameter.
*
* @param obj The address of the object whose key is wanted
* @return The value of the sorting key for this object
*/
protected abstract Word getKey(Address obj);
private static final Word mask16 = Word.fromIntZeroExtend(0xffff0000);
private static final Word mask8 = Word.fromIntZeroExtend(0x0000ff00);
private static final Word mask4 = Word.fromIntZeroExtend(0x000000f0);
private static final Word mask2 = Word.fromIntZeroExtend(0x0000000c);
private static final Word mask1 = Word.fromIntZeroExtend(0x00000002);
/**
* Find the highest bit that is set in a longword and return a mask
* with only that bit set.
*
* @param addr Value for which the mask needs to be found
* @return The highest bit set in the parameter
*/
private static Word getBitMask(Word addr) {
int shift = 0;
if (!addr.and(mask16).isZero()) {
addr = addr.rshl(16);
shift += 16;
}
if (!addr.and(mask8).isZero()) {
addr = addr.rshl(8);
shift += 8;
}
if (!addr.and(mask4).isZero()) {
addr = addr.rshl(4);
shift += 4;
}
if (!addr.and(mask2).isZero()) {
addr = addr.rshl(2);
shift += 2;
}
if (!addr.and(mask1).isZero()) {
shift += 1;
}
return (Word.one().lsh(shift));
}
/**
* Perform insertion sort within an intra-block address range.
*
* @param begin Start address of the range to be sorted
* @param end End address of the range to be sorted
*/
private void insertionSort(Address begin, Address end) {
Address rPtr = begin.minus(BYTES_IN_ADDRESS);
Address lPtr;
while (rPtr.GE(end)) {
Address rSlot = rPtr.loadAddress();
Word rKey = getKey(rSlot);
lPtr = rPtr.plus(BYTES_IN_ADDRESS);
while (lPtr.LE(begin)) {
Address lSlot = lPtr.loadAddress();
Word lKey = getKey(lSlot);
if (lKey.GT(rKey)) {
lPtr.minus(BYTES_IN_ADDRESS).store(lSlot);
lPtr = lPtr.plus(BYTES_IN_ADDRESS);
} else {
break;
}
}
lPtr.minus(BYTES_IN_ADDRESS).store(rSlot);
rPtr = rPtr.minus(BYTES_IN_ADDRESS);
}
}
/**
* Sort objects using radix exchange sort. An explicit stack is
* maintained to avoid using recursion.
*/
public void sort() {
Address startPtr, startLink, endPtr, endLink;
Word bitMask;
if (!head.EQ(HEAD_INITIAL_VALUE)) {
if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(tail.NE(TAIL_INITIAL_VALUE));
/* Obtain the bitmask for the first iteration and save the start &
end pointers and the bitmask on the stack */
initStack();
startPtr = popStack();
while (!startPtr.isZero()) {
startLink = popStack();
endPtr = popStack();
endLink = popStack();
bitMask = (popStack()).toWord();
if (startLink.NE(endLink)) {
partition(startPtr, startLink, endPtr, endLink, bitMask);
} else if (startPtr.GT(endPtr)) {
/* Use insertionSort for a limited number of objects within
a single block */
if (startPtr.diff(endPtr).sLT(INSERTION_SORT_LIMIT)) {
insertionSort(startPtr, endPtr);
} else {
partition(startPtr, startLink, endPtr, endLink, bitMask);
}
}
// Get the next set of data to sort
startPtr = popStack();
}
}
checkIfSorted();
}
/**
* Partition the slots in an address range based on the value in
* a particular bit. Place the start & end addresses of the two
* partitions & a bitmask per partition (which indicates the highest
* bit position at which the max & min of a partition differ) on the
* stack. This works just like the partition in quick sort, except
* that a bit value is being compared here
*
* @param startAddr The start address of the range to be sorted
* @param startLinkAddr The address where the start block has its next field
* @param endAddr The end address of the range to be sorted
* @param endLinkAddr The address where the end block has its next field
* @param bitMask The mask in which the bit to be commpared is set
*/
private void partition(Address startAddr, Address startLinkAddr,
Address endAddr, Address endLinkAddr,
Word bitMask) {
Address travPtr = endAddr;
Address travLink = endLinkAddr;
Address stopPtr = startAddr;
Address stopLink = startLinkAddr;
Address travSlot, stopSlot;
Word travKey, stopKey;
Word lmax = Word.zero(), rmax = Word.zero();
Word lmin = Word.max(), rmin = Word.max();
while (true) {
/* Compute the address range within the current block to compute. */
Address endOfBlock = travLink;
/* Move the left pointer until the right pointer is reached
or an address with a 0 value in the bit position is found. */
while (true) {
travSlot = travPtr.loadAddress();
travKey = getKey(travSlot);
/* If we reach the end. */
if (travPtr.EQ(stopPtr))
break;
/* If we found a 0 in bit position, break. */
if (travKey.and(bitMask).isZero())
break;
if (travKey.GT(rmax))
rmax = travKey;
if (travKey.LT(rmin))
rmin = travKey;
/* Move to the next entry. */
travPtr = travPtr.plus(BYTES_IN_ADDRESS);
/* If at end of remset block, move to next block */
if (travPtr.EQ(endOfBlock)) {
travLink = getPrev(travPtr);
endOfBlock = travLink;
travPtr = bufferStart(endOfBlock);
}
}
/* Store the end of the block. */
endOfBlock = bufferStart(stopPtr);
/* Move the right pointer until the left pointer is reached
or an address with a 1 value in the bit position is found. */
while (true) {
stopSlot = stopPtr.loadAddress();
stopKey = getKey(stopSlot);
/* Found a 1 in bit position, break. */
if (!stopKey.and(bitMask).isZero())
break;
if (stopKey.GT(lmax))
lmax = stopKey;
if (stopKey.LT(lmin))
lmin = stopKey;
if (stopPtr.EQ(travPtr))
break;
/* Move to the next entry, which may be in the next block. */
if (stopPtr.EQ(endOfBlock)) {
stopLink = getNext(stopLink);
stopPtr = stopLink;
endOfBlock = bufferStart(stopPtr);
}
stopPtr = stopPtr.minus(BYTES_IN_ADDRESS);
}
if (stopPtr.EQ(travPtr))
break;
/* interchange the values pointed to by the left and right pointers */
travPtr.store(stopSlot);
stopPtr.store(travSlot);
}
/* If max value is not equal to the min value in the right partition,
(not all slots are identical) push the right partition on to the stack */
if (rmax.GT(rmin)) {
if (travPtr.EQ(bufferStart(travPtr))) {
stopLink = getNext(travLink);
stopPtr = stopLink;
} else {
stopLink = travLink;
stopPtr = travPtr;
}
pushOnStack(getBitMask(rmax.xor(rmin)).toAddress());
pushOnStack(endLinkAddr);
pushOnStack(endAddr);
pushOnStack(stopLink);
pushOnStack(stopPtr.minus(BYTES_IN_ADDRESS));
}
/* if max value is not equal to the min value in the left partition,
(not all slots are identical) push the left partition on to the stack */
if (lmax.GT(lmin)) {
pushOnStack(getBitMask(lmax.xor(lmin)).toAddress());
pushOnStack(travLink);
pushOnStack(travPtr);
pushOnStack(startLinkAddr);
pushOnStack(startAddr);
}
}
/*************************************************************************
*
* Sorting Stack management routines
*/
/**
*
*/
private int stackLoc;
private final AddressArray stackBase;
/*
* Allocate memory for the stack and initialize it with the first range
* to partition
*/
private void initStack() {
stackLoc = 0;
Address endOfBlock = tail;
Address startPtr = bufferStart(endOfBlock);
Word min = Word.max();
Word max = Word.zero();
// Find the max. and min addresses in the object buffer
while (endOfBlock.NE(HEAD_INITIAL_VALUE)) {
startPtr = bufferStart(endOfBlock);
while (startPtr.LT(endOfBlock)) {
Address startSlot = startPtr.loadAddress();
Word startKey = getKey(startSlot);
if (startKey.GT(max))
max = startKey;
if (startKey.LT(min))
min = startKey;
startPtr = startPtr.plus(BYTES_IN_ADDRESS);
}
endOfBlock = getPrev(startPtr);
}
// If max and min are different (not all elements are equal), push the
// start, end and bitmask values on the stack
if (max.GT(min)) {
pushOnStack(getBitMask(max.xor(min)).toAddress());
pushOnStack(tail);
pushOnStack(bufferStart(tail));
pushOnStack(startPtr);
pushOnStack(startPtr.minus(BYTES_IN_ADDRESS));
}
}
/**
* Push an address on to the stack
*
* @param val The address to be pushed
*/
private void pushOnStack(Address val) {
stackBase.set(stackLoc, val);
stackLoc++;
}
/**
* Pop an address from the stack
*
* @return The address at the top of the stack, or 0 if stack is empty
*/
private Address popStack() {
if (stackLoc == 0)
return Address.zero();
stackLoc--;
return stackBase.get(stackLoc);
}
/**
* Debug routine, used to check if the object buffer is sorted correctly in
* decreasing final reference deletion time
*/
private void checkIfSorted() {
if (VM.VERIFY_ASSERTIONS) {
Address buf, end;
Word prevKey = Word.max();
end = tail;
buf = bufferStart(end);
while (buf.NE(HEAD_INITIAL_VALUE)) {
// iterate through the block
while (buf.LT(end)) {
Address slot = buf.loadAddress();
Word key = getKey(slot);
if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(key.LE(prevKey));
prevKey = key;
buf = buf.plus(BYTES_IN_ADDRESS);
}
end = getPrev(end);
buf = bufferStart(end);
}
}
}
}