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 014package org.mmtk.utility.alloc; 015 016import org.mmtk.policy.Space; 017import org.mmtk.policy.immix.Block; 018import org.mmtk.policy.immix.Chunk; 019import org.mmtk.policy.immix.Line; 020import org.mmtk.policy.immix.ImmixSpace; 021import static org.mmtk.policy.immix.ImmixConstants.*; 022 023import org.mmtk.utility.Log; 024import org.mmtk.utility.options.Options; 025import org.mmtk.vm.VM; 026 027import org.vmmagic.unboxed.*; 028import org.vmmagic.pragma.*; 029 030/** 031 * 032 */ 033@Uninterruptible 034public class ImmixAllocator extends Allocator { 035 036 /**************************************************************************** 037 * 038 * Instance variables 039 */ 040 041 /** space this allocator is associated with */ 042 protected final ImmixSpace space; 043 private final boolean hot; 044 private final boolean copy; 045 046 /** bump pointer */ 047 private Address cursor; 048 /** limit for bump pointer */ 049 private Address limit; 050 /** bump pointer for large objects */ 051 private Address largeCursor; 052 /** limit for bump pointer for large objects */ 053 private Address largeLimit; 054 /** is the current request for large or small? */ 055 private boolean requestForLarge; 056 /** did the last allocation straddle a line? */ 057 private boolean straddle; 058 /** approximation to bytes allocated (measured at 99% accurate) 07/10/30 */ 059 private int lineUseCount; 060 private Address markTable; 061 private Address recyclableBlock; 062 private int line; 063 private boolean recyclableExhausted; 064 065 /** 066 * Constructor. 067 * 068 * @param space The space to bump point into. 069 * @param hot TODO 070 * @param copy TODO 071 */ 072 public ImmixAllocator(ImmixSpace space, boolean hot, boolean copy) { 073 this.space = space; 074 this.hot = hot; 075 this.copy = copy; 076 reset(); 077 } 078 079 /** 080 * Reset the allocator. Note that this does not reset the space. 081 */ 082 public void reset() { 083 cursor = Address.zero(); 084 limit = Address.zero(); 085 largeCursor = Address.zero(); 086 largeLimit = Address.zero(); 087 markTable = Address.zero(); 088 recyclableBlock = Address.zero(); 089 requestForLarge = false; 090 recyclableExhausted = false; 091 line = LINES_IN_BLOCK; 092 lineUseCount = 0; 093 } 094 095 /***************************************************************************** 096 * 097 * Public interface 098 */ 099 100 /** 101 * Allocate space for a new object. This is frequently executed code and 102 * the coding is deliberately sensitive to the optimizing compiler. 103 * After changing this, always check the IR/MC that is generated. 104 * 105 * @param bytes The number of bytes allocated 106 * @param align The requested alignment 107 * @param offset The offset from the alignment 108 * @return The address of the first byte of the allocated region 109 */ 110 @Inline 111 public final Address alloc(int bytes, int align, int offset) { 112 /* establish how much we need */ 113 Address start = alignAllocationNoFill(cursor, align, offset); 114 Address end = start.plus(bytes); 115 116 /* check whether we've exceeded the limit */ 117 if (end.GT(limit)) { 118 if (bytes > BYTES_IN_LINE) 119 return overflowAlloc(bytes, align, offset); 120 else 121 return allocSlowHot(bytes, align, offset); 122 } 123 124 /* sufficient memory is available, so we can finish performing the allocation */ 125 fillAlignmentGap(cursor, start); 126 cursor = end; 127 128 return start; 129 } 130 131 /** 132 * Allocate space for a new object. This is frequently executed code and 133 * the coding is deliberately sensitive to the optimizing compiler. 134 * After changing this, always check the IR/MC that is generated. 135 * 136 * @param bytes The number of bytes allocated 137 * @param align The requested alignment 138 * @param offset The offset from the alignment 139 * @return The address of the first byte of the allocated region 140 */ 141 public final Address overflowAlloc(int bytes, int align, int offset) { 142 /* establish how much we need */ 143 Address start = alignAllocationNoFill(largeCursor, align, offset); 144 Address end = start.plus(bytes); 145 146 /* check whether we've exceeded the limit */ 147 if (end.GT(largeLimit)) { 148 requestForLarge = true; 149 Address rtn = allocSlowInline(bytes, align, offset); 150 requestForLarge = false; 151 return rtn; 152 } 153 154 /* sufficient memory is available, so we can finish performing the allocation */ 155 fillAlignmentGap(largeCursor, start); 156 largeCursor = end; 157 158 return start; 159 } 160 161 @Inline 162 public final boolean getLastAllocLineStraddle() { 163 return straddle; 164 } 165 166 /** 167 * External allocation slow path (called by superclass when slow path is 168 * actually taken. This is necessary (rather than a direct call 169 * from the fast path) because of the possibility of a thread switch 170 * and corresponding re-association of bump pointers to kernel 171 * threads. 172 * 173 * @param bytes The number of bytes allocated 174 * @param align The requested alignment 175 * @param offset The offset from the alignment 176 * @return The address of the first byte of the allocated region or 177 * zero on failure 178 */ 179 @Override 180 protected final Address allocSlowOnce(int bytes, int align, int offset) { 181 Address ptr = space.getSpace(hot, copy, lineUseCount); 182 183 if (ptr.isZero()) { 184 lineUseCount = 0; 185 return ptr; // failed allocation --- we will need to GC 186 } 187 188 /* we have been given a clean block */ 189 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(Block.isAligned(ptr)); 190 lineUseCount = LINES_IN_BLOCK; 191 192 if (requestForLarge) { 193 largeCursor = ptr; 194 largeLimit = ptr.plus(BYTES_IN_BLOCK); 195 } else { 196 cursor = ptr; 197 limit = ptr.plus(BYTES_IN_BLOCK); 198 } 199 200 return alloc(bytes, align, offset); 201 } 202 203 /**************************************************************************** 204 * 205 * Bump allocation 206 */ 207 208 /** 209 * Internal allocation slow path. This is called whenever the bump 210 * pointer reaches the internal limit. The code is forced out of 211 * line. If required we perform an external slow path take, which 212 * we inline into this method since this is already out of line. 213 * 214 * @param bytes The number of bytes allocated 215 * @param align The requested alignment 216 * @param offset The offset from the alignment 217 * @return The address of the first byte of the allocated region 218 */ 219 @NoInline 220 private Address allocSlowHot(int bytes, int align, int offset) { 221 if (acquireRecyclableLines(bytes, align, offset)) 222 return alloc(bytes, align, offset); 223 else 224 return allocSlowInline(bytes, align, offset); 225 } 226 227 private boolean acquireRecyclableLines(int bytes, int align, int offset) { 228 while (line < LINES_IN_BLOCK || acquireRecyclableBlock()) { 229 line = space.getNextAvailableLine(markTable, line); 230 if (line < LINES_IN_BLOCK) { 231 int endLine = space.getNextUnavailableLine(markTable, line); 232 cursor = recyclableBlock.plus(Extent.fromIntSignExtend(line << LOG_BYTES_IN_LINE)); 233 limit = recyclableBlock.plus(Extent.fromIntSignExtend(endLine << LOG_BYTES_IN_LINE)); 234 if (SANITY_CHECK_LINE_MARKS) { 235 Address tmp = cursor; 236 while (tmp.LT(limit)) { 237 if (tmp.loadByte() != (byte) 0) { 238 Log.write("cursor: "); Log.writeln(cursor); 239 Log.write(" limit: "); Log.writeln(limit); 240 Log.write("current: "); Log.write(tmp); 241 Log.write(" value: "); Log.write(tmp.loadByte()); 242 Log.write(" line: "); Log.write(line); 243 Log.write("endline: "); Log.write(endLine); 244 Log.write(" chunk: "); Log.write(Chunk.align(cursor)); 245 Log.write(" hw: "); Log.write(Chunk.getHighWater(Chunk.align(cursor))); 246 Log.writeln(" values: "); 247 Address tmp2 = cursor; 248 while (tmp2.LT(limit)) { 249 Log.write(tmp2.loadByte()); 250 Log.write(" "); 251 } 252 Log.writeln(); 253 } 254 VM.assertions._assert(tmp.loadByte() == (byte) 0); 255 tmp = tmp.plus(1); 256 } 257 } 258 if (VM.VERIFY_ASSERTIONS && bytes <= BYTES_IN_LINE) { 259 Address start = alignAllocationNoFill(cursor, align, offset); 260 Address end = start.plus(bytes); 261 VM.assertions._assert(end.LE(limit)); 262 } 263 VM.memory.zero(false, cursor, limit.diff(cursor).toWord().toExtent()); 264 if (VM.VERIFY_ASSERTIONS && Options.verbose.getValue() >= 9) { 265 Log.write("Z["); Log.write(cursor); Log.write("->"); Log.write(limit); Log.writeln("]"); 266 } 267 268 line = endLine; 269 if (VM.VERIFY_ASSERTIONS && copy) VM.assertions._assert(!Block.isDefragSource(cursor)); 270 return true; 271 } 272 } 273 return false; 274 } 275 276 private boolean acquireRecyclableBlock() { 277 boolean rtn; 278 rtn = acquireRecyclableBlockAddressOrder(); 279 if (rtn) { 280 markTable = Line.getBlockMarkTable(recyclableBlock); 281 line = 0; 282 } 283 return rtn; 284 } 285 286 @Inline 287 private boolean acquireRecyclableBlockAddressOrder() { 288 if (recyclableExhausted) { 289 if (VM.VERIFY_ASSERTIONS && Options.verbose.getValue() >= 9) { 290 Log.writeln("[no recyclable available]"); 291 } 292 return false; 293 } 294 int markState = 0; 295 boolean usable = false; 296 while (!usable) { 297 Address next = recyclableBlock.plus(BYTES_IN_BLOCK); 298 if (recyclableBlock.isZero() || ImmixSpace.isRecycleAllocChunkAligned(next)) { 299 recyclableBlock = space.acquireReusableBlocks(); 300 if (recyclableBlock.isZero()) { 301 recyclableExhausted = true; 302 if (VM.VERIFY_ASSERTIONS && Options.verbose.getValue() >= 9) { 303 Log.writeln("[recyclable exhausted]"); 304 } 305 line = LINES_IN_BLOCK; 306 return false; 307 } 308 } else { 309 recyclableBlock = next; 310 } 311 markState = Block.getBlockMarkState(recyclableBlock); 312 usable = (markState > 0 && markState <= ImmixSpace.getReusuableMarkStateThreshold(copy)); 313 if (copy && Block.isDefragSource(recyclableBlock)) 314 usable = false; 315 } 316 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(!Block.isUnused(recyclableBlock)); 317 Block.setBlockAsReused(recyclableBlock); 318 319 lineUseCount += (LINES_IN_BLOCK - markState); 320 return true; // found something good 321 } 322 323 private void zeroBlock(Address block) { 324 // FIXME: efficiency check here! 325 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(block.toWord().and(Word.fromIntSignExtend(BYTES_IN_BLOCK - 1)).isZero()); 326 VM.memory.zero(false, block, Extent.fromIntZeroExtend(BYTES_IN_BLOCK)); 327 } 328 329 /** @return the space associated with this squish allocator */ 330 @Override 331 public final Space getSpace() { 332 return space; 333 } 334 335 /** 336 * Print out the status of the allocator (for debugging) 337 */ 338 public final void show() { 339 Log.write("cursor = "); Log.write(cursor); 340 Log.write(" limit = "); Log.writeln(limit); 341 } 342}