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.policy.immix;
014
015import static org.mmtk.policy.immix.ImmixConstants.*;
016import static org.mmtk.utility.Constants.LOG_BYTES_IN_SHORT;
017
018import org.mmtk.vm.VM;
019import org.vmmagic.pragma.Uninterruptible;
020import org.vmmagic.unboxed.Address;
021import org.vmmagic.unboxed.Extent;
022import org.vmmagic.unboxed.Offset;
023
024/**
025 * This class defines operations over block-granularity meta-data
026 *
027 */
028@Uninterruptible
029public class Block  {
030
031  static Address align(final Address ptr) {
032    return ptr.toWord().and(BLOCK_MASK.not()).toAddress();
033  }
034
035  public static boolean isAligned(final Address address) {
036    return address.EQ(align(address));
037  }
038
039  private static int getChunkIndex(final Address block) {
040    return block.toWord().and(CHUNK_MASK).rshl(LOG_BYTES_IN_BLOCK).toInt();
041  }
042
043  /***************************************************************************
044   * Block marking
045   */
046
047
048  /**
049   * @param address the block's address
050   * @return whether the block is unallocated
051   */
052  public static boolean isUnused(final Address address) {
053    return getBlockMarkState(address) == UNALLOCATED_BLOCK_STATE;
054  }
055
056  static boolean isUnusedState(Address cursor) {
057    return cursor.loadShort() == UNALLOCATED_BLOCK_STATE;
058  }
059
060  static short getMarkState(Address cursor) {
061    return cursor.loadShort();
062  }
063
064  static void setState(Address cursor, short value) {
065    cursor.store(value);
066  }
067
068  public static short getBlockMarkState(Address address) {
069    return getBlockMarkStateAddress(address).loadShort();
070  }
071
072  static void setBlockAsInUse(Address address) {
073    if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(isUnused(address));
074    setBlockState(address, UNMARKED_BLOCK_STATE);
075  }
076
077  public static void setBlockAsReused(Address address) {
078    if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(!isUnused(address));
079    setBlockState(address, REUSED_BLOCK_STATE);
080  }
081
082  static void setBlockAsUnallocated(Address address) {
083    if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(!isUnused(address));
084    getBlockMarkStateAddress(address).store(UNALLOCATED_BLOCK_STATE);
085  }
086
087  private static void setBlockState(Address address, short value) {
088    if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(value != UNALLOCATED_BLOCK_STATE);
089    getBlockMarkStateAddress(address).store(value);
090  }
091
092  static Address getBlockMarkStateAddress(Address address) {
093    Address chunk = Chunk.align(address);
094    int index = getChunkIndex(address);
095    Address rtn = chunk.plus(Chunk.BLOCK_STATE_TABLE_OFFSET).plus(index << LOG_BYTES_IN_BLOCK_STATE_ENTRY);
096    if (VM.VERIFY_ASSERTIONS) {
097      Address block = chunk.plus(index << LOG_BYTES_IN_BLOCK);
098      VM.assertions._assert(isAligned(block));
099      boolean valid = rtn.GE(chunk.plus(Chunk.BLOCK_STATE_TABLE_OFFSET)) && rtn.LT(chunk.plus(Chunk.BLOCK_STATE_TABLE_OFFSET + BLOCK_STATE_TABLE_BYTES));
100      VM.assertions._assert(valid);
101    }
102    return rtn;
103  }
104
105  /***************************************************************************
106   * Sweeping
107   */
108
109  /**
110   * Sweeps one block.<p>
111   *
112   * TODO: needs better documentation.
113   *
114   * @param block the block's address
115   * @param markHistogram the mark histogram
116   * @param markState the mark value
117   * @param resetMarkState whether to reset the mark state
118   * @return number of marked lines
119   */
120  static short sweepOneBlock(Address block, int[] markHistogram, final byte markState, final boolean resetMarkState) {
121    if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(isAligned(block));
122
123    final boolean unused = isUnused(block);
124    if (unused && !SANITY_CHECK_LINE_MARKS)
125      return 0;
126
127    Address markTable = Line.getBlockMarkTable(block);
128
129    short markCount = 0;
130    short conservativeSpillCount = 0;
131    byte mark, lastMark = 0;
132    for (int offset = 0; offset < (LINES_IN_BLOCK << Line.LOG_BYTES_IN_LINE_STATUS); offset += Line.BYTES_IN_LINE_STATUS) {
133       if (VM.VERIFY_ASSERTIONS) {
134        VM.assertions._assert(markTable.plus(offset).GE(Chunk.align(block).plus(Chunk.LINE_MARK_TABLE_OFFSET)));
135        VM.assertions._assert(markTable.plus(offset).LT(Chunk.align(block).plus(Chunk.LINE_MARK_TABLE_OFFSET + Line.LINE_MARK_TABLE_BYTES)));
136      }
137      mark = markTable.loadByte(Offset.fromIntZeroExtend(offset));
138      if (resetMarkState)
139        markTable.store(mark == markState ? RESET_LINE_MARK_STATE : 0, Offset.fromIntZeroExtend(offset));
140
141      if (mark == markState)
142        markCount++;
143      else if (lastMark == markState)
144        conservativeSpillCount++;
145      else if (SANITY_CHECK_LINE_MARKS && lastMark != markState) {
146        VM.memory.zero(false, block.plus(offset << (LOG_BYTES_IN_LINE - Line.LOG_BYTES_IN_LINE_STATUS)),Extent.fromIntZeroExtend(BYTES_IN_LINE));
147      }
148
149      lastMark = mark;
150    }
151    if (VM.VERIFY_ASSERTIONS) {
152      VM.assertions._assert(markCount <= LINES_IN_BLOCK);
153      VM.assertions._assert(markCount + conservativeSpillCount <= LINES_IN_BLOCK);
154      VM.assertions._assert(markCount == 0 || !isUnused(block));
155    }
156
157    getDefragStateAddress(block).store(conservativeSpillCount);
158    if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(markCount >= conservativeSpillCount);
159    markHistogram[conservativeSpillCount] += markCount;
160
161    markCount = (short) (markCount + conservativeSpillCount);
162
163    return markCount;
164  }
165
166  /****************************************************************************
167   * Block defrag state
168   */
169
170  /**
171   * @param address the block's address
172   * @return whether the defrag state is {@link #BLOCK_IS_DEFRAG_SOURCE}
173   */
174  public static boolean isDefragSource(Address address) {
175    return getDefragStateAddress(address).loadShort() == BLOCK_IS_DEFRAG_SOURCE;
176  }
177
178  static void clearConservativeSpillCount(Address address) {
179    getDefragStateAddress(address).store((short) 0);
180  }
181
182  static short getConservativeSpillCount(Address address) {
183    return getDefragStateAddress(address).loadShort();
184  }
185
186  static Address getDefragStateAddress(Address address) {
187    Address chunk = Chunk.align(address);
188    int index = getChunkIndex(address);
189    Address rtn = chunk.plus(Chunk.BLOCK_DEFRAG_STATE_TABLE_OFFSET).plus(index << LOG_BYTES_IN_BLOCK_DEFRAG_STATE_ENTRY);
190    if (VM.VERIFY_ASSERTIONS) {
191      Address block = chunk.plus(index << LOG_BYTES_IN_BLOCK);
192      VM.assertions._assert(isAligned(block));
193      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));
194      VM.assertions._assert(valid);
195    }
196    return rtn;
197  }
198
199  static void resetLineMarksAndDefragStateTable(short threshold, Address markStateBase, Address defragStateBase,
200      Address lineMarkBase, int block) {
201    Offset csOffset = Offset.fromIntZeroExtend(block << LOG_BYTES_IN_BLOCK_DEFRAG_STATE_ENTRY);
202    short state = defragStateBase.loadShort(csOffset);
203    short defragState = BLOCK_IS_NOT_DEFRAG_SOURCE;
204    if (state >= threshold) defragState = BLOCK_IS_DEFRAG_SOURCE;
205    defragStateBase.store(defragState, csOffset);
206  }
207
208  private static final short UNALLOCATED_BLOCK_STATE = 0;
209  private static final short UNMARKED_BLOCK_STATE = (short) (MAX_BLOCK_MARK_STATE + 1);
210  private static final short REUSED_BLOCK_STATE = (short) (MAX_BLOCK_MARK_STATE + 2);
211
212  private static final short BLOCK_IS_NOT_DEFRAG_SOURCE = 0;
213  private static final short BLOCK_IS_DEFRAG_SOURCE = 1;
214
215  /* block states */
216  static final int LOG_BYTES_IN_BLOCK_STATE_ENTRY = LOG_BYTES_IN_SHORT; // use a short for now
217  static final int BYTES_IN_BLOCK_STATE_ENTRY = 1 << LOG_BYTES_IN_BLOCK_STATE_ENTRY;
218  static final int BLOCK_STATE_TABLE_BYTES = BLOCKS_IN_CHUNK << LOG_BYTES_IN_BLOCK_STATE_ENTRY;
219
220  /* per-block defrag state */
221  static final int LOG_BYTES_IN_BLOCK_DEFRAG_STATE_ENTRY = LOG_BYTES_IN_SHORT;
222  static final int BYTES_IN_BLOCK_DEFRAG_STATE_ENTRY = 1 << LOG_BYTES_IN_BLOCK_DEFRAG_STATE_ENTRY;
223
224  static final int BLOCK_DEFRAG_STATE_TABLE_BYTES = BLOCKS_IN_CHUNK << LOG_BYTES_IN_BLOCK_DEFRAG_STATE_ENTRY;
225}