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