blob: 82b3eef943e0acf62a6d6a60073411e5853e0303 [file] [log] [blame]
/*
* 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.utility.heap;
import org.mmtk.policy.Space;
import org.mmtk.utility.GenericFreeList;
import org.mmtk.utility.Log;
import org.mmtk.vm.Lock;
import org.mmtk.vm.VM;
import org.vmmagic.pragma.Inline;
import org.vmmagic.pragma.Interruptible;
import org.vmmagic.pragma.Uninterruptible;
import org.vmmagic.unboxed.Address;
import org.vmmagic.unboxed.Extent;
import org.vmmagic.unboxed.Word;
/**
* This class manages the mapping of spaces to virtual memory ranges.<p>
*
*/
@Uninterruptible
public class Map {
/* set the map base address so that we have an unused (null) chunk at the bottome of the space for 64 bit */
private static final Address MAP_BASE_ADDRESS = Space.BITS_IN_ADDRESS == 32 ? Address.zero() : Space.HEAP_START.minus(Space.BYTES_IN_CHUNK);
/****************************************************************************
*
* Class variables
*/
private static final int[] descriptorMap;
private static final int[] linkageMap;
private static final Space[] spaceMap;
private static final GenericFreeList regionMap;
public static final GenericFreeList globalPageMap;
private static int sharedDiscontigFLCount = 0;
private static final FreeListPageResource[] sharedFLMap;
private static int totalAvailableDiscontiguousChunks = 0;
private static final Lock lock = VM.newLock("Map lock");
/****************************************************************************
*
* Initialization
*/
/**
* Class initializer. Create our two maps
*/
static {
descriptorMap = new int[Space.MAX_CHUNKS];
linkageMap = new int[Space.MAX_CHUNKS];
spaceMap = new Space[Space.MAX_CHUNKS];
regionMap = new GenericFreeList(Space.MAX_CHUNKS);
globalPageMap = new GenericFreeList(1, 1, Space.MAX_SPACES);
sharedFLMap = new FreeListPageResource[Space.MAX_SPACES];
if (VM.VERIFY_ASSERTIONS)
VM.assertions._assert(Space.BITS_IN_ADDRESS == Space.LOG_ADDRESS_SPACE ||
Space.HEAP_END.diff(MAP_BASE_ADDRESS).toWord().rshl(Space.LOG_ADDRESS_SPACE).isZero());
}
/****************************************************************************
*
* Map accesses and insertion
*/
/**
* Insert a space and its descriptor into the map, associating it
* with a particular address range.
*
* @param start The start address of the region to be associated
* with this space.
* @param extent The size of the region, in bytes
* @param descriptor The descriptor for this space
* @param space The space to be associated with this region
*/
public static void insert(Address start, Extent extent, int descriptor,
Space space) {
Extent e = Extent.zero();
while (e.LT(extent)) {
int index = getChunkIndex(start.plus(e));
if (descriptorMap[index] != 0) {
Log.write("Conflicting virtual address request for space \"");
Log.write(space.getName()); Log.write("\" at ");
Log.writeln(start.plus(e));
Space.printVMMap();
VM.assertions.fail("exiting");
}
descriptorMap[index] = descriptor;
VM.barriers.setArrayNoBarrier(spaceMap, index, space);
e = e.plus(Space.BYTES_IN_CHUNK);
}
}
/**
* Allocate some number of contiguous chunks within a discontiguous region
*
* @param descriptor The descriptor for the space to which these chunks will be assigned
* @param space The space to which these chunks will be assigned
* @param chunks The number of chunks required
* @param previous The previous contgiuous set of chunks for this space (to create a linked list of contiguous regions for each space)
* @return The address of the assigned memory. This always succeeds. If the request fails we fail right here.
*/
public static Address allocateContiguousChunks(int descriptor, Space space, int chunks, Address previous) {
lock.acquire();
int chunk = regionMap.alloc(chunks);
if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(chunk != 0);
if (chunk == -1) {
Log.write("Unable to allocate virtual address space for space \"");
Log.write(space.getName()); Log.write("\" for ");
Log.write(chunks); Log.write(" chunks (");
Log.write(chunks<<Space.LOG_BYTES_IN_CHUNK); Log.writeln(" bytes)");
Space.printVMMap();
VM.assertions.fail("exiting");
}
totalAvailableDiscontiguousChunks -= chunks;
Address rtn = addressForChunkIndex(chunk);
insert(rtn, Extent.fromIntZeroExtend(chunks<<Space.LOG_BYTES_IN_CHUNK), descriptor, space);
linkageMap[chunk] = previous.isZero() ? 0 : getChunkIndex(previous);
lock.release();
return rtn;
}
/**
* Return the address of the next contiguous region associated with some discontiguous space by following the linked list for that space.
*
* @param start The current region (return the next region in the list)
* @return Return the next contiguous region after start in the linked list of regions
*/
public static Address getNextContiguousRegion(Address start) {
if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(start.EQ(Space.chunkAlign(start, true)));
int chunk = getChunkIndex(start);
return (chunk == 0) ? Address.zero() : (linkageMap[chunk] == 0) ? Address.zero() : addressForChunkIndex(linkageMap[chunk]);
}
/**
* Return the size of a contiguous region in chunks.
*
* @param start The start address of the region whose size is being requested
* @return The size of the region in question
*/
public static int getContiguousRegionChunks(Address start) {
if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(start.EQ(Space.chunkAlign(start, true)));
int chunk = getChunkIndex(start);
return regionMap.size(chunk);
}
/**
* Return the size of a contiguous region in bytes.
*
* @param start The start address of the region whose size is being requested
* @return The size of the region in question
*/
public static Extent getContiguousRegionSize(Address start) {
return Word.fromIntSignExtend(getContiguousRegionChunks(start)).lsh(Space.LOG_BYTES_IN_CHUNK).toExtent();
}
/**
* Free all chunks in a linked list of contiguous chunks. This means starting
* with lastChunk and then walking the chain of contiguous regions, freeing each.
*
* @param lastChunk The last chunk in the linked list of chunks to be freed
*/
public static void freeAllChunks(Address lastChunk) {
lock.acquire();
if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(lastChunk.EQ(Space.chunkAlign(lastChunk, true)));
int chunk = getChunkIndex(lastChunk);
while (chunk != 0) {
int next = linkageMap[chunk];
freeContiguousChunks(chunk);
chunk = next;
}
lock.release();
}
/**
* Free some set of contiguous chunks, given the chunk address
*
* @param start The start address of the first chunk in the series
* @return The number of chunks which were contiguously allocated
*/
public static int freeContiguousChunks(Address start) {
lock.acquire();
if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(start.EQ(Space.chunkAlign(start, true)));
int rtn = freeContiguousChunks(getChunkIndex(start));
lock.release();
return rtn;
}
/**
* Free some set of contiguous chunks, given the chunk index
*
* @param chunk The chunk index of the region to be freed
* @return The number of chunks freed
*/
private static int freeContiguousChunks(int chunk) {
int chunks = regionMap.free(chunk);
totalAvailableDiscontiguousChunks += chunks;
for (int offset = 0; offset < chunks; offset++) {
descriptorMap[chunk + offset] = 0;
VM.barriers.setArrayNoBarrier(spaceMap, chunk + offset, null);
linkageMap[chunk + offset] = 0;
}
return chunks;
}
/**
* Finalize the space map, establishing which virtual memory
* is nailed down, and then placing the rest into a map to
* be used by discontiguous spaces.
*/
@Interruptible
public static void finalizeStaticSpaceMap() {
/* establish bounds of discontiguous space */
Address startAddress = Space.getDiscontigStart();
int firstChunk = getChunkIndex(startAddress);
int lastChunk = getChunkIndex(Space.getDiscontigEnd());
int unavailStartChunk = lastChunk + 1;
int trailingChunks = Space.MAX_CHUNKS - unavailStartChunk;
int pages = (1 + lastChunk - firstChunk) * Space.PAGES_IN_CHUNK;
globalPageMap.resizeFreeList(pages, pages);
for (int pr = 0; pr < sharedDiscontigFLCount; pr++)
sharedFLMap[pr].resizeFreeList(startAddress);
/* set up the region map free list */
int allocedChunk = regionMap.alloc(firstChunk); // block out entire bottom of address range
for (int chunkIndex = firstChunk; chunkIndex <= lastChunk; chunkIndex++)
allocedChunk = regionMap.alloc(1); // Tentatively allocate all usable chunks
allocedChunk = regionMap.alloc(trailingChunks); // block out entire top of address range
if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(allocedChunk == unavailStartChunk);
/* set up the global page map and place chunks on free list */
int firstPage = 0;
for (int chunkIndex = firstChunk; chunkIndex <= lastChunk; chunkIndex++) {
if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(spaceMap[chunkIndex] == null);
totalAvailableDiscontiguousChunks++;
regionMap.free(chunkIndex); // put this chunk on the free list
globalPageMap.setUncoalescable(firstPage);
int allocedPages = globalPageMap.alloc(Space.PAGES_IN_CHUNK); // populate the global page map
if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(allocedPages == firstPage);
firstPage += Space.PAGES_IN_CHUNK;
}
}
/**
* Return the ordinal number for some free list space wishing to share a discontiguous region.
* @return The ordinal number for a free list space wishing to share a discontiguous region
*/
@Interruptible
public static int getDiscontigFreeListPROrdinal(FreeListPageResource pr) {
sharedFLMap[sharedDiscontigFLCount] = pr;
sharedDiscontigFLCount++;
return sharedDiscontigFLCount;
}
/**
* Return the total number of chunks available (unassigned) within the
* range of virtual memory apportioned to discontiguous spaces.
*
* @return The number of available chunks for use by discontiguous spaces.
*/
public static int getAvailableDiscontiguousChunks() {
return totalAvailableDiscontiguousChunks;
}
/**
* Return the total number of clients contending for chunks. This
* is useful when establishing conservative bounds on the number
* of remaining chunks.
*
* @return The total number of clients who may contend for chunks.
*/
public static int getChunkConsumerCount() {
return sharedDiscontigFLCount;
}
/**
* Return the space in which this address resides.
*
* @param address The address in question
* @return The space in which the address resides
*/
@Inline
public static Space getSpaceForAddress(Address address) {
int index = getChunkIndex(address);
return spaceMap[index];
}
/**
* Return the space descriptor for the space in which this object
* resides.
*
* @param object The object in question
* @return The space descriptor for the space in which the object
* resides
*/
@Inline
public static int getDescriptorForAddress(Address object) {
int index = getChunkIndex(object);
return descriptorMap[index];
}
/**
* Hash an address to a chunk (this is simply done via bit shifting)
*
* @param address The address to be hashed
* @return The chunk number that this address hashes into
*/
@Inline
private static int getChunkIndex(Address address) {
if (Space.BYTES_IN_ADDRESS == 8) {
if (address.LT(Space.HEAP_START) || address.GE(Space.HEAP_END))
return 0;
else
return address.diff(MAP_BASE_ADDRESS).toWord().rshl(Space.LOG_BYTES_IN_CHUNK).toInt();
} else
return address.toWord().rshl(Space.LOG_BYTES_IN_CHUNK).toInt();
}
@Inline
private static Address addressForChunkIndex(int chunk) {
if (Space.BYTES_IN_ADDRESS == 8) {
if (chunk == 0)
return Address.zero();
else
return MAP_BASE_ADDRESS.plus(Word.fromIntZeroExtend(chunk).lsh(Space.LOG_BYTES_IN_CHUNK).toExtent());
} else
return Word.fromIntZeroExtend(chunk).lsh(Space.LOG_BYTES_IN_CHUNK).toAddress();
}
}