/*
This file is part of JOP, the Java Optimized Processor
see <http://www.jopdesign.com/>
Copyright (C) 2008, Martin Schoeberl (martin@jopdesign.com)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jopdesign.sys;
/**
* JOP's implementation of scoped memory.
*
* The backing store of a nested scope is allocated
* within the backing store of the outer scope.
* A nested scope starts after the size for object allocations
* for the outer scope.
*
* @author Martin Schoeberl (martin@jopdesign.com)
*
*/
public class Memory {
// TODO should be set at some time, before the first
// scope (e.g. mission memory) is created
final static int IM_SIZE = 32*1024;
int cnt;
/** Start address of memory area */
int startPtr;
/** Allocation pointer */
int allocPtr;
/**
* End of area for local allocations.
* Points to the last usable word.
*/
int endLocalPtr;
/**
* End of backing store.
* Points to the last usable word.
*/
int endBsPtr;
/**
* Allocation pointer for the nested backing store.
*/
int allocBsPtr;
// TODO: support for multiple parallel scopes
// We need also an allocation pointer for the backing
// store.
/** Parent scope */
Memory parent;
/** Nesting level */
public int level;
/**
* A reference for an inner memory that shall be reused
* for enterPrivateMemory.
*/
Memory inner;
/**
* The singleton reference for the immortal memory.
*/
public static Memory immortal;
Memory() {
}
/**
* Create a Scope object that represents immortal memory.
* Should only be called by GC on JVM startup.
* @return
*/
static Memory getImmortal(int start, int end) {
if (immortal==null) {
immortal = new Memory();
immortal.startPtr = start;
immortal.endBsPtr = end;
immortal.endLocalPtr = immortal.startPtr + IM_SIZE - 1;
immortal.allocBsPtr = immortal.endLocalPtr+1;
// the Scope object itself is already allocated
immortal.allocPtr = GC.allocationPointer;
immortal.parent = null;
immortal.level = 0;
}
return immortal;
}
public Memory(int size, int bsSize) {
cnt = 0;
if (RtThreadImpl.mission) {
// should be atomic? probably not
Scheduler s = Scheduler.sched[RtThreadImpl.sys.cpuId];
parent = s.ref[s.active].currentArea;
} else {
parent = RtThreadImpl.initArea;
}
// If backing store size is not set, take all
if (bsSize==0) {
bsSize = parent.endBsPtr-parent.allocBsPtr+1;
}
// new memory area is within parents backing store and
// starts after the area for objects allocated in the parent
if (bsSize<size || parent.endBsPtr-parent.allocBsPtr+1 < bsSize) {
throw GC.OOMError;
}
startPtr = parent.allocBsPtr;
// use bsSize backing store
endBsPtr = startPtr+bsSize-1;
// adjust parents BS allocation pointer
parent.allocBsPtr = endBsPtr+1;
allocPtr = startPtr;
endLocalPtr = startPtr+size-1;
// Own offered BS for nested scopes starts after
// the local area
allocBsPtr = endLocalPtr+1;
level = parent.level+1;
}
/**
* Create a scope and use all available backing store from the outer scope.
*
* @param size
*/
public Memory(int size) {
this(size, 0);
}
// that's for our scratchpad memory
// public Scope(int[] localMem) {
// backingStore = localMem;
// cnt = 0;
// allocPtr = 0;
// // clean it
// for (int i=0; i<localMem.length; ++i) {
// localMem[i] = 0;
// }
// }
/*
* public int getSize() { //return backingStore.length*4; return size; }
*/
public void enter(Runnable logic) {
// Anders thinks cnt is useless here
synchronized (this) {
++cnt;
}
if (cnt != 1) {
throw new Error("No cyclic enter and no sharing between threads");
}
// clean the scope memory
// would be more efficient to clean after exit...
// for (int i = startPtr; i <= endLocalPtr; ++i) {
// Native.wrMem(0, i);
// }
// activate the memory area
RtThreadImpl rtt = null;
Memory outer = null;
if (RtThreadImpl.mission) {
// This method is only used by scopes within PrivateMemory so we
// are certain that threads are running.
Scheduler s = Scheduler.sched[RtThreadImpl.sys.cpuId];
int nr = s.active;
rtt = s.ref[nr];
outer = rtt.currentArea;
// TODO: Illegal reference when used as part of enterPrivateMemory
rtt.currentArea = this;
logic.run();
// exit the area
rtt.currentArea = outer;
} else {
// without RtThreads running, main thread
outer = RtThreadImpl.initArea;
RtThreadImpl.initArea = this;
logic.run();
RtThreadImpl.initArea = outer;
}
cnt = 0;
// clean the scope memory
for (int i = startPtr; i < allocPtr; ++i) {
Native.wrMem(0, i);
}
inner = null;
allocPtr = startPtr;
}
// just a enter without pointer reset (and cleanup)
// need to be checked that the area is actually owned
// by a thread when it is private memory
public void executeInArea(Runnable logic) {
// activate the memory area
RtThreadImpl rtt = null;
Memory outer = null;
if (RtThreadImpl.mission) {
// This method is only used by scopes within PrivateMemory so we
// are certain that threads are running.
Scheduler s = Scheduler.sched[RtThreadImpl.sys.cpuId];
int nr = s.active;
rtt = s.ref[nr];
outer = rtt.currentArea;
rtt.currentArea = this;
logic.run();
// exit the area
// TODO: Illegal reference when executeInArea() is called from
// a nested private memory.
rtt.currentArea = outer;
} else {
// without RtThreads running, main thread
outer = RtThreadImpl.initArea;
RtThreadImpl.initArea = this;
logic.run();
RtThreadImpl.initArea = outer;
}
}
/**
* Return the memory region which we are currently in.
* @return
*/
public static Memory getCurrentMemory() {
Memory m;
if (RtThreadImpl.mission) {
Scheduler s = Scheduler.sched[RtThreadImpl.sys.cpuId];
m = s.ref[s.active].currentArea;
} else {
m = RtThreadImpl.initArea;
}
return m;
}
/**
* This is SCJ style inner scopes for private memory.
*
* At the moment we will just create a local Memory
* object for each enter. However, this is a memory
* leak and one instance shall be reused.
*
* @param size
* @param logic
*/
public void enterPrivateMemory(int size, Runnable logic) {
// TODO: is this assignment allowed?
// The scope object lives in an outer one,
// so it is not!
// That is actually an example where we need allocateInArea().
if (inner==null) {
// TODO: Illegal reference
inner = new Memory();
}
// Now set all fields for inner and adapt this
inner.parent = this;
// check if remaining BS is at least size
if (this.endBsPtr-this.allocBsPtr+1<size) {
throw GC.OOMError;
}
inner.startPtr = this.allocBsPtr;
// use bsSize backing store
// Take all backing store
inner.endBsPtr = this.endBsPtr;
// save this BS allocation pointer
int bsPtr = this.allocBsPtr;
// adjust parents BS allocation pointer
this.allocBsPtr = inner.endBsPtr+1;
inner.allocPtr = inner.startPtr;
inner.endLocalPtr = inner.startPtr+size-1;
// Own offered BS for nested scopes starts after
// the local area
inner.allocBsPtr = inner.endLocalPtr+1;
inner.level = this.level+1;
inner.enter(logic);
// Reset BS allocation pointer
this.allocBsPtr = bsPtr;
}
// executeInArea -- don't forget to synchronize new
public static Memory getMemoryArea(Object object){
// Debug stuff
// int i = Native.toInt(object);
// System.out.println("Object reference: "+i);
//
// int j = Native.rdMem(i+GC.OFF_MEM);
// System.out.println("Memory object reference: "+j);
Memory m = (Memory)Native.toObject(
Native.rdMem(Native.toInt(object)+ GC.OFF_MEM));
return m;
}
public int size(){
return endLocalPtr - startPtr + 1;
}
public int memoryConsumed(){
return allocPtr - startPtr + 1;
}
public int memoryRemaining(){
return endLocalPtr - allocPtr;
}
public int bStoreRemaining(){
return endBsPtr - allocBsPtr;
}
}