/*
* 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.vmmagic.unboxed.harness;
import org.mmtk.harness.lang.Trace;
import org.mmtk.harness.lang.Trace.Item;
import org.vmmagic.unboxed.Address;
import static org.vmmagic.unboxed.harness.MemoryConstants.*;
/**
* Represents a single page of memory.
*/
final class MemoryPage {
/** Is this page currently readable */
boolean readable;
/** The base address of this page */
private final Address pageAddress;
/** The raw data on this page */
private final int[] data;
/** Watched indexes */
private final boolean[] watch;
/** Mask of index into page */
private static final int INDEX_MASK = (BYTES_IN_PAGE - 1);
/** log_2 of bytes in a memory cell */
private static final int LOG_BYTES_IN_CELL = LOG_BYTES_IN_INT;
/** Bytes in a memory cell */
private static final int BYTES_IN_CELL = BYTES_IN_INT;
/** Dimensions of memory cells (the contents of a memory page) */
private static final int CELL_MASK = INT_MASK;
/**
* Create a new MemoryPage based on the given address
*/
MemoryPage(Address pageAddress) {
this.pageAddress = pageAddress;
this.readable = true;
this.data = new int[BYTES_IN_PAGE >>> LOG_BYTES_IN_CELL];
this.watch = getWatchPoints();
if (Trace.isEnabled(Item.MEMORY)) {
Object[] args = { pageAddress };
Trace.trace(Item.MEMORY,"Mapping page %s%n", args);
}
}
/**
* The base address of a given cell
* @param index
* @return
*/
private Address cellAddress(int index) {
return pageAddress.plus(index<<LOG_BYTES_IN_CELL);
}
/**
* Zero the memory in this page.
*/
public void zero() {
for(int i=0; i < data.length; i++) {
write(i, 0);
}
}
/**
* Construct a long value from 2 ints (high and low order 32-bit words)
* @param high High 32-bits of result
* @param low Low 32-bits of result
* @return
*/
@SuppressWarnings("cast") // Make cast explicit, because oddness can happen
private long longFrom2Ints(int high, int low) {
return (((long)high) << 32) |(((long)low & 0xFFFFFFFFL));
}
/**
* Calculate the index to use in the page data based on the given address.
* @param address The address being used.
* @return The index into the page data.
*/
private int getIndex(Address address) {
assert SimulatedMemory.onSamePage(address,pageAddress) :
"Invalid access of " + address + " in page " + pageAddress;
return ((int)((address.toLong()) & INDEX_MASK)) >>> LOG_BYTES_IN_CELL;
}
/**
* Load a byte value from this page.
* @param address Address of byte to return
* @return The contents of the byte at the address
*/
public byte getByte(Address address) {
int bitShift = ((address.toInt()) & ~CELL_MASK) << MemoryConstants.LOG_BITS_IN_BYTE;
int index = getIndex(address);
return (byte)(read(index) >>> bitShift);
}
/**
* Load a char value from this page.
* @param address Address of char to return
* @return The contents of the char at the address
*/
public char getChar(Address address) {
int bitShift = ((address.toInt()) & ~CELL_MASK) << MemoryConstants.LOG_BITS_IN_BYTE;
assert bitShift == 0 || bitShift == 16: "misaligned char access at "+address;
int index = getIndex(address);
return (char)(read(index) >>> bitShift);
}
/**
* Load an integer value from this page.
* @param address Address of int to return
* @return The contents of the int at the address
*/
public int getInt(Address address) {
assert ((address.toInt()) % MemoryConstants.BYTES_IN_INT) == 0: "misaligned 4b access at "+address;
return read(getIndex(address));
}
/**
* Load a long value from this page.
* @param address Address of long to return
* @return The contents of the long at the address
*/
public long getLong(Address address) {
if (SimulatedMemory.ALIGN_CHECK_LONG) {
assert ((address.toLong()) % MemoryConstants.BYTES_IN_LONG) == 0: "misaligned 8b access at "+address;
}
return longFrom2Ints(getInt(address.plus(BYTES_IN_CELL)),getInt(address));
}
@SuppressWarnings("cast")
public synchronized byte setByte(Address address, byte value) {
int shift = ((address.toInt()) & ~MemoryConstants.INT_MASK) << MemoryConstants.LOG_BITS_IN_BYTE;
int mask = 0x000000FF << shift;
int newValue = (((int)value) << shift) & mask;
int index = getIndex(address);
int oldValue = read(index);
newValue = (oldValue & ~mask) | newValue;
write(index, newValue);
return (byte)(oldValue >>> shift);
}
@SuppressWarnings("cast")
public synchronized char setChar(Address address, char value) {
int shift = (address.toInt() & ~MemoryConstants.INT_MASK) << MemoryConstants.LOG_BITS_IN_BYTE;
assert shift == 0 || shift == 16: "misaligned 2b access at "+address+", shift="+shift;
int mask = 0x0000FFFF << shift;
int newValue = (((int)value) << shift) & mask;
int index = getIndex(address);
int oldValue = read(index);
newValue = (oldValue & ~mask) | newValue;
write(index, newValue);
return (char)(oldValue >>> shift);
}
public synchronized int setInt(Address address, int value) {
assert ((address.toInt()) % MemoryConstants.BYTES_IN_INT) == 0: "misaligned 4b access at "+address;
int index = getIndex(address);
int old = read(index);
write(index, value);
return old;
}
public synchronized long setLong(Address address, long value) {
if (SimulatedMemory.ALIGN_CHECK_LONG) {
assert ((address.toInt()) % MemoryConstants.BYTES_IN_LONG) == 0: "misaligned 8b access at "+address;
}
try {
int index = getIndex(address);
long old = longFrom2Ints(read(index+1), read(index));
write(index, (int)(value & 0xFFFFFFFFL));
write(index+1, (int)(value >>> 32));
return old;
} catch (RuntimeException e) {
System.err.println("Error setting address "+address);
throw e;
}
}
public synchronized boolean exchangeInt(Address address, int oldValue, int value) {
int old = getInt(address);
if (old != oldValue) return false;
setInt(address,value);
return true;
}
public synchronized boolean exchangeLong(Address address, long oldValue, long value) {
long old = getLong(address);
if (old != oldValue) return false;
setLong(address,value);
return true;
}
/**
* Perform the actual read of memory.
*/
private synchronized int read(int index) {
int value = data[index];
if (isWatched(index)) {
Trace.printf("%4d load %s = %08x%n", Thread.currentThread().getId(),
cellAddress(index), value);
//new Throwable().printStackTrace();
}
return value;
}
/**
* Perform the actual write of memory, possibly reporting values if watching is enabled for the given address.
*/
private void write(int index, int value) {
if (isWatched(index)) {
Trace.printf("%4d store %s: %08x -> %08x%n", Thread.currentThread().getId(),
cellAddress(index), data[index], value);
//new Throwable().printStackTrace();
}
data[index] = value;
}
/*************************************************************************
* Watch-point management
*/
/**
* Return a boolean array indicating which words in this page are being watched.
*/
private boolean[] getWatchPoints() {
boolean[] result = null;
for(Address addr: SimulatedMemory.watches) {
if (SimulatedMemory.onSamePage(addr,pageAddress)) {
if (result == null) {
result = new boolean[data.length];
}
int index = getIndex(addr);
result[index] = true;
System.err.println("Watching address "+addr);
}
}
return result;
}
/**
* Is the given word being watched ?
* @param index
* @return
*/
private boolean isWatched(int index) {
return hasWatches() ? watch[index] : false;
}
/**
* @return {@code true} if there are watch-points on this page
*/
boolean hasWatches() {
return watch != null;
}
}