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 & end addresses of the two 173 * partitions & a bitmask per partition (which indicates the highest 174 * bit position at which the max & 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}