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    package org.mmtk.policy.immix;
014    
015    import static org.mmtk.policy.Space.BYTES_IN_CHUNK;
016    import static org.mmtk.policy.immix.ImmixConstants.*;
017    
018    import org.mmtk.utility.Constants;
019    import org.mmtk.utility.Conversions;
020    import org.mmtk.utility.heap.Mmapper;
021    import org.mmtk.vm.VM;
022    
023    import org.vmmagic.pragma.Uninterruptible;
024    import org.vmmagic.unboxed.Address;
025    import org.vmmagic.unboxed.Extent;
026    
027    @Uninterruptible
028    public class Chunk implements Constants {
029    
030      public static Address align(Address ptr) {
031        return ptr.toWord().and(CHUNK_MASK.not()).toAddress();
032      }
033    
034      static boolean isAligned(Address ptr) {
035        return ptr.EQ(align(ptr));
036      }
037    
038      static int getByteOffset(Address ptr) {
039        return ptr.toWord().and(CHUNK_MASK).toInt();
040      }
041    
042      /**
043       * Return the number of pages of metadata required per chunk.
044       */
045      static int getRequiredMetaDataPages() {
046        Extent bytes = Extent.fromIntZeroExtend(ROUNDED_METADATA_BYTES_PER_CHUNK);
047        return Conversions.bytesToPagesUp(bytes);
048      }
049    
050      static void sweep(Address chunk, Address end, ImmixSpace space, int[] markHistogram, final byte markValue, final boolean resetMarks) {
051        if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(isAligned(chunk));
052        Address start = getFirstUsableBlock(chunk);
053        Address cursor = Block.getBlockMarkStateAddress(start);
054        for (int index = FIRST_USABLE_BLOCK_INDEX; index < BLOCKS_IN_CHUNK; index++) {
055          Address block = chunk.plus(index<<LOG_BYTES_IN_BLOCK);
056          if (block.GT(end)) break;
057          final boolean defragSource = space.inImmixDefragCollection() && Block.isDefragSource(block);
058          short marked = Block.sweepOneBlock(block, markHistogram, markValue, resetMarks);
059          if (marked == 0) {
060            if (!Block.isUnusedState(cursor)) {
061              space.release(block);
062              if (defragSource) Defrag.defragBytesFreed.inc(BYTES_IN_BLOCK);
063            }
064            if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(Block.isUnused(block));
065          } else {
066            if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(marked > 0 && marked <= LINES_IN_BLOCK);
067            Block.setState(cursor, marked);
068            if (defragSource) Defrag.defragBytesNotFreed.inc(BYTES_IN_BLOCK);
069          }
070          if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(Block.isUnused(block) || (Block.getBlockMarkState(block) == marked && marked > 0 && marked <= MAX_BLOCK_MARK_STATE));
071          cursor = cursor.plus(Block.BYTES_IN_BLOCK_STATE_ENTRY);
072        }
073      }
074    
075      static void clearMetaData(Address chunk) {
076        if (VM.VERIFY_ASSERTIONS) {
077          VM.assertions._assert(isAligned(chunk));
078          VM.assertions._assert(Conversions.isPageAligned(chunk));
079          VM.assertions._assert(Conversions.isPageAligned(ROUNDED_METADATA_BYTES_PER_CHUNK));
080        }
081        Mmapper.ensureMapped(chunk, ROUNDED_METADATA_PAGES_PER_CHUNK);
082        VM.memory.zero(false, chunk, Extent.fromIntZeroExtend(ROUNDED_METADATA_BYTES_PER_CHUNK));
083        if (VM.VERIFY_ASSERTIONS) checkMetaDataCleared(chunk, chunk);
084      }
085    
086      private static void checkMetaDataCleared(Address chunk, Address value) {
087        VM.assertions._assert(isAligned(chunk));
088        Address block = Chunk.getHighWater(chunk);
089        if (value.EQ(chunk)) {
090          VM.assertions._assert(block.isZero());
091          block = chunk.plus(Chunk.ROUNDED_METADATA_BYTES_PER_CHUNK);
092        } else {
093          block = block.plus(BYTES_IN_BLOCK); // start at first block after highwater
094          VM.assertions._assert(Block.align(block).EQ(block));
095        }
096        while (block.LT(chunk.plus(BYTES_IN_CHUNK))) {
097          VM.assertions._assert(Chunk.align(block).EQ(chunk));
098          VM.assertions._assert(Block.isUnused(block));
099          block = block.plus(BYTES_IN_BLOCK);
100        }
101      }
102    
103      static void updateHighWater(Address value) {
104        Address chunk = align(value);
105        if (getHighWater(chunk).LT(value)) {
106          setHighWater(chunk, value);
107        }
108      }
109    
110      private static void setHighWater(Address chunk, Address value) {
111        if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(isAligned(chunk));
112        chunk.plus(HIGHWATER_OFFSET).store(value);
113      }
114    
115      public static Address getHighWater(Address chunk) {
116        if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(isAligned(chunk));
117        return chunk.plus(HIGHWATER_OFFSET).loadAddress();
118      }
119    
120      static void setMap(Address chunk, int value) {
121        if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(isAligned(chunk));
122        chunk.plus(MAP_OFFSET).store(value);
123      }
124    
125      static int getMap(Address chunk) {
126        if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(isAligned(chunk));
127        int rtn = chunk.plus(MAP_OFFSET).loadInt();
128        return (rtn < 0) ? -rtn : rtn;
129      }
130    
131      static void resetLineMarksAndDefragStateTable(Address chunk, short threshold) {
132        if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(isAligned(chunk));
133        Address markStateBase = Block.getBlockMarkStateAddress(chunk);
134        Address defragStateBase = Block.getDefragStateAddress(chunk);
135        Address lineMarkBase = Line.getChunkMarkTable(chunk);
136        for (int b = FIRST_USABLE_BLOCK_INDEX; b < BLOCKS_IN_CHUNK; b++) {
137          Block.resetLineMarksAndDefragStateTable(threshold, markStateBase, defragStateBase, lineMarkBase, b);
138        }
139      }
140    
141      static Address getFirstUsableBlock(Address chunk) {
142        if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(isAligned(chunk));
143        Address rtn = chunk.plus(ROUNDED_METADATA_BYTES_PER_CHUNK);
144        if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(rtn.EQ(Block.align(rtn)));
145        return rtn;
146      }
147    
148      private static final int LOG_BYTES_IN_HIGHWATER_ENTRY = LOG_BYTES_IN_ADDRESS;
149      private static final int HIGHWATER_BYTES = 1<<LOG_BYTES_IN_HIGHWATER_ENTRY;
150      private static final int LOG_BYTES_IN_MAP_ENTRY = LOG_BYTES_IN_INT;
151      private static final int MAP_BYTES = 1<<LOG_BYTES_IN_MAP_ENTRY;
152    
153      /* byte offsets for each type of metadata */
154      static final int LINE_MARK_TABLE_OFFSET = 0;
155      static final int BLOCK_STATE_TABLE_OFFSET = LINE_MARK_TABLE_OFFSET + Line.LINE_MARK_TABLE_BYTES;
156      static final int BLOCK_DEFRAG_STATE_TABLE_OFFSET = BLOCK_STATE_TABLE_OFFSET + Block.BLOCK_STATE_TABLE_BYTES;
157      static final int HIGHWATER_OFFSET = BLOCK_DEFRAG_STATE_TABLE_OFFSET + Block.BLOCK_DEFRAG_STATE_TABLE_BYTES;
158      static final int MAP_OFFSET = HIGHWATER_OFFSET + HIGHWATER_BYTES;
159      static final int METADATA_BYTES_PER_CHUNK = MAP_OFFSET + MAP_BYTES;
160    
161      /* FIXME we round the metadata up to block sizes just to ensure the underlying allocator gives us aligned requests */
162      private static final int BLOCK_MASK = (1<<LOG_BYTES_IN_BLOCK) - 1;
163      static final int ROUNDED_METADATA_BYTES_PER_CHUNK = (METADATA_BYTES_PER_CHUNK + BLOCK_MASK) & ~BLOCK_MASK;
164      static final int ROUNDED_METADATA_PAGES_PER_CHUNK = ROUNDED_METADATA_BYTES_PER_CHUNK>>LOG_BYTES_IN_PAGE;
165      public static final int FIRST_USABLE_BLOCK_INDEX = ROUNDED_METADATA_BYTES_PER_CHUNK>>LOG_BYTES_IN_BLOCK;
166    }