| /* |
| * 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.policy.immix; |
| |
| import static org.mmtk.policy.Space.BYTES_IN_CHUNK; |
| import static org.mmtk.policy.immix.ImmixConstants.*; |
| |
| import org.mmtk.utility.Constants; |
| import org.mmtk.utility.Conversions; |
| import org.mmtk.utility.heap.Mmapper; |
| import org.mmtk.vm.VM; |
| |
| import org.vmmagic.pragma.Uninterruptible; |
| import org.vmmagic.unboxed.Address; |
| import org.vmmagic.unboxed.Extent; |
| |
| @Uninterruptible |
| public class Chunk implements Constants { |
| |
| public static Address align(Address ptr) { |
| return ptr.toWord().and(CHUNK_MASK.not()).toAddress(); |
| } |
| |
| static boolean isAligned(Address ptr) { |
| return ptr.EQ(align(ptr)); |
| } |
| |
| static int getByteOffset(Address ptr) { |
| return ptr.toWord().and(CHUNK_MASK).toInt(); |
| } |
| |
| /** |
| * Return the number of pages of metadata required per chunk. |
| */ |
| static int getRequiredMetaDataPages() { |
| Extent bytes = Extent.fromIntZeroExtend(ROUNDED_METADATA_BYTES_PER_CHUNK); |
| return Conversions.bytesToPagesUp(bytes); |
| } |
| |
| static void sweep(Address chunk, Address end, ImmixSpace space, int[] markHistogram, final byte markValue, final boolean resetMarks) { |
| if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(isAligned(chunk)); |
| Address start = getFirstUsableBlock(chunk); |
| Address cursor = Block.getBlockMarkStateAddress(start); |
| for (int index = FIRST_USABLE_BLOCK_INDEX; index < BLOCKS_IN_CHUNK; index++) { |
| Address block = chunk.plus(index<<LOG_BYTES_IN_BLOCK); |
| if (block.GT(end)) break; |
| final boolean defragSource = space.inImmixDefragCollection() && Block.isDefragSource(block); |
| short marked = Block.sweepOneBlock(block, markHistogram, markValue, resetMarks); |
| if (marked == 0) { |
| if (!Block.isUnusedState(cursor)) { |
| space.release(block); |
| if (defragSource) Defrag.defragBytesFreed.inc(BYTES_IN_BLOCK); |
| } |
| if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(Block.isUnused(block)); |
| } else { |
| if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(marked > 0 && marked <= LINES_IN_BLOCK); |
| Block.setState(cursor, marked); |
| if (defragSource) Defrag.defragBytesNotFreed.inc(BYTES_IN_BLOCK); |
| } |
| if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(Block.isUnused(block) || (Block.getBlockMarkState(block) == marked && marked > 0 && marked <= MAX_BLOCK_MARK_STATE)); |
| cursor = cursor.plus(Block.BYTES_IN_BLOCK_STATE_ENTRY); |
| } |
| } |
| |
| static void clearMetaData(Address chunk) { |
| if (VM.VERIFY_ASSERTIONS) { |
| VM.assertions._assert(isAligned(chunk)); |
| VM.assertions._assert(Conversions.isPageAligned(chunk)); |
| VM.assertions._assert(Conversions.isPageAligned(ROUNDED_METADATA_BYTES_PER_CHUNK)); |
| } |
| Mmapper.ensureMapped(chunk, ROUNDED_METADATA_PAGES_PER_CHUNK); |
| VM.memory.zero(chunk, Extent.fromIntZeroExtend(ROUNDED_METADATA_BYTES_PER_CHUNK)); |
| if (VM.VERIFY_ASSERTIONS) checkMetaDataCleared(chunk, chunk); |
| } |
| |
| private static void checkMetaDataCleared(Address chunk, Address value) { |
| VM.assertions._assert(isAligned(chunk)); |
| Address block = Chunk.getHighWater(chunk); |
| if (value.EQ(chunk)) { |
| VM.assertions._assert(block.isZero()); |
| block = chunk.plus(Chunk.ROUNDED_METADATA_BYTES_PER_CHUNK); |
| } else { |
| block = block.plus(BYTES_IN_BLOCK); // start at first block after highwater |
| VM.assertions._assert(Block.align(block).EQ(block)); |
| } |
| while (block.LT(chunk.plus(BYTES_IN_CHUNK))) { |
| VM.assertions._assert(Chunk.align(block).EQ(chunk)); |
| VM.assertions._assert(Block.isUnused(block)); |
| block = block.plus(BYTES_IN_BLOCK); |
| } |
| } |
| |
| static void updateHighWater(Address value) { |
| Address chunk = align(value); |
| if (getHighWater(chunk).LT(value)) { |
| setHighWater(chunk, value); |
| } |
| } |
| |
| private static void setHighWater(Address chunk, Address value) { |
| if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(isAligned(chunk)); |
| chunk.plus(HIGHWATER_OFFSET).store(value); |
| } |
| |
| public static Address getHighWater(Address chunk) { |
| if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(isAligned(chunk)); |
| return chunk.plus(HIGHWATER_OFFSET).loadAddress(); |
| } |
| |
| static void setMap(Address chunk, int value) { |
| if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(isAligned(chunk)); |
| chunk.plus(MAP_OFFSET).store(value); |
| } |
| |
| static int getMap(Address chunk) { |
| if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(isAligned(chunk)); |
| int rtn = chunk.plus(MAP_OFFSET).loadInt(); |
| return (rtn < 0) ? -rtn : rtn; |
| } |
| |
| static void resetLineMarksAndDefragStateTable(Address chunk, short threshold) { |
| if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(isAligned(chunk)); |
| Address markStateBase = Block.getBlockMarkStateAddress(chunk); |
| Address defragStateBase = Block.getDefragStateAddress(chunk); |
| Address lineMarkBase = Line.getChunkMarkTable(chunk); |
| for (int b = FIRST_USABLE_BLOCK_INDEX; b < BLOCKS_IN_CHUNK; b++) { |
| Block.resetLineMarksAndDefragStateTable(threshold, markStateBase, defragStateBase, lineMarkBase, b); |
| } |
| } |
| |
| static Address getFirstUsableBlock(Address chunk) { |
| if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(isAligned(chunk)); |
| Address rtn = chunk.plus(ROUNDED_METADATA_BYTES_PER_CHUNK); |
| if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(rtn.EQ(Block.align(rtn))); |
| return rtn; |
| } |
| |
| private static final int LOG_BYTES_IN_HIGHWATER_ENTRY = LOG_BYTES_IN_ADDRESS; |
| private static final int HIGHWATER_BYTES = 1<<LOG_BYTES_IN_HIGHWATER_ENTRY; |
| private static final int LOG_BYTES_IN_MAP_ENTRY = LOG_BYTES_IN_INT; |
| private static final int MAP_BYTES = 1<<LOG_BYTES_IN_MAP_ENTRY; |
| |
| /* byte offsets for each type of metadata */ |
| static final int LINE_MARK_TABLE_OFFSET = 0; |
| static final int BLOCK_STATE_TABLE_OFFSET = LINE_MARK_TABLE_OFFSET + Line.LINE_MARK_TABLE_BYTES; |
| static final int BLOCK_DEFRAG_STATE_TABLE_OFFSET = BLOCK_STATE_TABLE_OFFSET + Block.BLOCK_STATE_TABLE_BYTES; |
| static final int HIGHWATER_OFFSET = BLOCK_DEFRAG_STATE_TABLE_OFFSET + Block.BLOCK_DEFRAG_STATE_TABLE_BYTES; |
| static final int MAP_OFFSET = HIGHWATER_OFFSET + HIGHWATER_BYTES; |
| static final int METADATA_BYTES_PER_CHUNK = MAP_OFFSET + MAP_BYTES; |
| |
| /* FIXME we round the metadata up to block sizes just to ensure the underlying allocator gives us aligned requests */ |
| private static final int BLOCK_MASK = (1<<LOG_BYTES_IN_BLOCK) - 1; |
| static final int ROUNDED_METADATA_BYTES_PER_CHUNK = (METADATA_BYTES_PER_CHUNK + BLOCK_MASK) & ~BLOCK_MASK; |
| static final int ROUNDED_METADATA_PAGES_PER_CHUNK = ROUNDED_METADATA_BYTES_PER_CHUNK>>LOG_BYTES_IN_PAGE; |
| public static final int FIRST_USABLE_BLOCK_INDEX = ROUNDED_METADATA_BYTES_PER_CHUNK>>LOG_BYTES_IN_BLOCK; |
| } |