| /* |
| * 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.immix.ImmixConstants.*; |
| |
| import org.mmtk.utility.Constants; |
| import org.mmtk.vm.VM; |
| |
| import org.vmmagic.pragma.Uninterruptible; |
| import org.vmmagic.unboxed.Address; |
| import org.vmmagic.unboxed.Extent; |
| import org.vmmagic.unboxed.Offset; |
| |
| /** |
| * This class defines operations over block-granularity meta-data |
| * |
| */ |
| @Uninterruptible |
| public class Block implements Constants { |
| |
| static Address align(final Address ptr) { |
| return ptr.toWord().and(BLOCK_MASK.not()).toAddress(); |
| } |
| |
| public static boolean isAligned(final Address address) { |
| return address.EQ(align(address)); |
| } |
| |
| private static int getChunkIndex(final Address block) { |
| return block.toWord().and(CHUNK_MASK).rshl(LOG_BYTES_IN_BLOCK).toInt(); |
| } |
| |
| /*************************************************************************** |
| * Block marking |
| */ |
| public static boolean isUnused(final Address address) { |
| return getBlockMarkState(address) == UNALLOCATED_BLOCK_STATE; |
| } |
| |
| static boolean isUnusedState(Address cursor) { |
| return cursor.loadShort() == UNALLOCATED_BLOCK_STATE; |
| } |
| |
| static short getMarkState(Address cursor) { |
| return cursor.loadShort(); |
| } |
| |
| static void setState(Address cursor, short value) { |
| cursor.store(value); |
| } |
| |
| public static short getBlockMarkState(Address address) { |
| return getBlockMarkStateAddress(address).loadShort(); |
| } |
| |
| static void setBlockAsInUse(Address address) { |
| if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(isUnused(address)); |
| setBlockState(address, UNMARKED_BLOCK_STATE); |
| } |
| |
| public static void setBlockAsReused(Address address) { |
| if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(!isUnused(address)); |
| setBlockState(address, REUSED_BLOCK_STATE); |
| } |
| |
| static void setBlockAsUnallocated(Address address) { |
| if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(!isUnused(address)); |
| getBlockMarkStateAddress(address).store(UNALLOCATED_BLOCK_STATE); |
| } |
| |
| private static void setBlockState(Address address, short value) { |
| if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(value != UNALLOCATED_BLOCK_STATE); |
| getBlockMarkStateAddress(address).store(value); |
| } |
| |
| static Address getBlockMarkStateAddress(Address address) { |
| Address chunk = Chunk.align(address); |
| int index = getChunkIndex(address); |
| Address rtn = chunk.plus(Chunk.BLOCK_STATE_TABLE_OFFSET).plus(index<<LOG_BYTES_IN_BLOCK_STATE_ENTRY); |
| if (VM.VERIFY_ASSERTIONS) { |
| Address block = chunk.plus(index<<LOG_BYTES_IN_BLOCK); |
| VM.assertions._assert(isAligned(block)); |
| boolean valid = rtn.GE(chunk.plus(Chunk.BLOCK_STATE_TABLE_OFFSET)) && rtn.LT(chunk.plus(Chunk.BLOCK_STATE_TABLE_OFFSET+BLOCK_STATE_TABLE_BYTES)); |
| VM.assertions._assert(valid); |
| } |
| return rtn; |
| } |
| |
| /*************************************************************************** |
| * Sweeping |
| */ |
| static short sweepOneBlock(Address block, int[] markHistogram, final byte markState, final boolean resetMarkState) { |
| if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(isAligned(block)); |
| |
| final boolean unused = isUnused(block); |
| if (unused && !SANITY_CHECK_LINE_MARKS) |
| return 0; |
| |
| Address markTable = Line.getBlockMarkTable(block); |
| |
| short markCount = 0; |
| short conservativeSpillCount = 0; |
| byte mark, lastMark = 0; |
| for (int offset = 0; offset < (LINES_IN_BLOCK<<Line.LOG_BYTES_IN_LINE_STATUS); offset += Line.BYTES_IN_LINE_STATUS) { |
| if (VM.VERIFY_ASSERTIONS) { |
| VM.assertions._assert(markTable.plus(offset).GE(Chunk.align(block).plus(Chunk.LINE_MARK_TABLE_OFFSET))); |
| VM.assertions._assert(markTable.plus(offset).LT(Chunk.align(block).plus(Chunk.LINE_MARK_TABLE_OFFSET+Line.LINE_MARK_TABLE_BYTES))); |
| } |
| mark = markTable.loadByte(Offset.fromIntZeroExtend(offset)); |
| if (resetMarkState) |
| markTable.store((byte) (mark == markState ? RESET_LINE_MARK_STATE : 0), Offset.fromIntZeroExtend(offset)); |
| |
| if (mark == markState) |
| markCount++; |
| else if (lastMark == markState) |
| conservativeSpillCount++; |
| else if (SANITY_CHECK_LINE_MARKS && lastMark != markState) { |
| VM.memory.zero(block.plus(offset<<(LOG_BYTES_IN_LINE-Line.LOG_BYTES_IN_LINE_STATUS)),Extent.fromIntZeroExtend(BYTES_IN_LINE)); |
| } |
| |
| lastMark = mark; |
| } |
| if (VM.VERIFY_ASSERTIONS) { |
| VM.assertions._assert(markCount <= LINES_IN_BLOCK); |
| VM.assertions._assert(markCount + conservativeSpillCount <= LINES_IN_BLOCK); |
| VM.assertions._assert(markCount == 0 || !isUnused(block)); |
| } |
| |
| getDefragStateAddress(block).store(conservativeSpillCount); |
| if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(markCount >= conservativeSpillCount); |
| markHistogram[conservativeSpillCount] += markCount; |
| |
| markCount = (short) (markCount + conservativeSpillCount); |
| |
| return markCount; |
| } |
| |
| /**************************************************************************** |
| * Block defrag state |
| */ |
| public static boolean isDefragSource(Address address) { |
| return getDefragStateAddress(address).loadShort() == BLOCK_IS_DEFRAG_SOURCE; |
| } |
| |
| static void clearConservativeSpillCount(Address address) { |
| getDefragStateAddress(address).store((short) 0); |
| } |
| |
| static short getConservativeSpillCount(Address address) { |
| return getDefragStateAddress(address).loadShort(); |
| } |
| |
| static Address getDefragStateAddress(Address address) { |
| Address chunk = Chunk.align(address); |
| int index = getChunkIndex(address); |
| Address rtn = chunk.plus(Chunk.BLOCK_DEFRAG_STATE_TABLE_OFFSET).plus(index<<LOG_BYTES_IN_BLOCK_DEFRAG_STATE_ENTRY); |
| if (VM.VERIFY_ASSERTIONS) { |
| Address block = chunk.plus(index<<LOG_BYTES_IN_BLOCK); |
| VM.assertions._assert(isAligned(block)); |
| boolean valid = rtn.GE(chunk.plus(Chunk.BLOCK_DEFRAG_STATE_TABLE_OFFSET)) && rtn.LT(chunk.plus(Chunk.BLOCK_DEFRAG_STATE_TABLE_OFFSET+BLOCK_DEFRAG_STATE_TABLE_BYTES)); |
| VM.assertions._assert(valid); |
| } |
| return rtn; |
| } |
| |
| static void resetLineMarksAndDefragStateTable(short threshold, Address markStateBase, Address defragStateBase, |
| Address lineMarkBase, int block) { |
| Offset csOffset = Offset.fromIntZeroExtend(block<<LOG_BYTES_IN_BLOCK_DEFRAG_STATE_ENTRY); |
| short state = defragStateBase.loadShort(csOffset); |
| short defragState = BLOCK_IS_NOT_DEFRAG_SOURCE; |
| if (state >= threshold) defragState = BLOCK_IS_DEFRAG_SOURCE; |
| defragStateBase.store(defragState, csOffset); |
| } |
| |
| private static final short UNALLOCATED_BLOCK_STATE = 0; |
| private static final short UNMARKED_BLOCK_STATE = (short) (MAX_BLOCK_MARK_STATE + 1); |
| private static final short REUSED_BLOCK_STATE = (short) (MAX_BLOCK_MARK_STATE + 2); |
| |
| private static final short BLOCK_IS_NOT_DEFRAG_SOURCE = 0; |
| private static final short BLOCK_IS_DEFRAG_SOURCE = 1; |
| |
| /* block states */ |
| static final int LOG_BYTES_IN_BLOCK_STATE_ENTRY = LOG_BYTES_IN_SHORT; // use a short for now |
| static final int BYTES_IN_BLOCK_STATE_ENTRY = 1<<LOG_BYTES_IN_BLOCK_STATE_ENTRY; |
| static final int BLOCK_STATE_TABLE_BYTES = BLOCKS_IN_CHUNK<<LOG_BYTES_IN_BLOCK_STATE_ENTRY; |
| |
| /* per-block defrag state */ |
| static final int LOG_BYTES_IN_BLOCK_DEFRAG_STATE_ENTRY = LOG_BYTES_IN_SHORT; |
| static final int BYTES_IN_BLOCK_DEFRAG_STATE_ENTRY = 1<<LOG_BYTES_IN_BLOCK_DEFRAG_STATE_ENTRY; |
| |
| static final int BLOCK_DEFRAG_STATE_TABLE_BYTES = BLOCKS_IN_CHUNK<<LOG_BYTES_IN_BLOCK_DEFRAG_STATE_ENTRY; |
| } |