WARNING! Access to this system is limited to authorised users only.
Unauthorised users may be subject to prosecution.
Unauthorised access to this system is a criminal offence under Australian law (Federal Crimes Act 1914 Part VIA)
It is a criminal offence to:
(1) Obtain access to data without authority. -Penalty 2 years imprisonment.
(2) Damage, delete, alter or insert data without authority. -Penalty 10 years imprisonment.
User activity is monitored and recorded. Anyone using this system expressly consents to such monitoring and recording.

Commit 59c285d4 authored by Kunshan Wang's avatar Kunshan Wang
Browse files

WIP: updating interpreter...

parent 7e002594
......@@ -16,8 +16,7 @@ case class Handle(ty: Type, vb: ValueBox)
abstract class TrapHandlerResult
case class TrapExit() extends TrapHandlerResult
case class TrapRebindPassValue(newStack: Handle, value: Handle) extends TrapHandlerResult
case class TrapRebindPassVoid(newStack: Handle) extends TrapHandlerResult
case class TrapRebindPassValues(newStack: Handle, values: Seq[Handle]) extends TrapHandlerResult
case class TrapRebindThrowExc(newStack: Handle, exc: Handle) extends TrapHandlerResult
trait TrapHandler {
......@@ -318,18 +317,12 @@ class ClientAgent(mutator: Mutator)(
def fence(ord: MemoryOrder): Unit = {
}
def newStack(func: Handle, args: Seq[Handle]): Handle = {
def newStack(func: Handle): Handle = {
val funcVal = func.vb.asInstanceOf[BoxFunc].func.getOrElse {
throw new UvmRuntimeException("Stack-bottom function must not be NULL")
}
val funcVer = funcVal.versions.headOption.getOrElse {
throw new UvmRuntimeException("Stack-bottom function %s is not defined.".format(funcVal.repr))
}
val argBoxes = args.map(_.vb)
val sta = microVM.threadStackManager.newStack(funcVer, argBoxes, mutator)
val sta = microVM.threadStackManager.newStack(funcVal, mutator)
val nb = BoxStack(Some(sta))
newHandle(InternalTypes.STACK, nb)
......@@ -378,7 +371,8 @@ class ClientAgent(mutator: Mutator)(
val fr = nthFrame(sv, frame)
fr match {
case f: NativeFrame => 0
case f: MuFrame => f.funcVer.id
case f: UndefinedMuFrame => 0
case f: DefinedMuFrame => f.funcVer.id
}
}
......@@ -387,7 +381,8 @@ class ClientAgent(mutator: Mutator)(
val fr = nthFrame(sv, frame)
fr match {
case f: NativeFrame => 0
case f: MuFrame => f.curInst.id
case f: UndefinedMuFrame => 0
case f: DefinedMuFrame => f.curInst.id
}
}
......@@ -398,7 +393,12 @@ class ClientAgent(mutator: Mutator)(
case f: NativeFrame => {
throw new UvmRefImplException("Attempt to dump keepalive variables for a native frame for native funciton 0x%x".format(f.func))
}
case f: MuFrame => {
case f: UndefinedMuFrame => {
for ((ty,box) <- f.func.sig.paramTys zip f.boxes) yield {
newHandle(ty, box)
}
}
case f: DefinedMuFrame => {
val i = f.curInst
i match {
case hkac: HasKeepAliveClause => {
......@@ -429,7 +429,7 @@ class ClientAgent(mutator: Mutator)(
}
}
def pushFrame(stack: Handle, func: Handle, argList: Seq[Handle]): Unit = {
def pushFrame(stack: Handle, func: Handle): Unit = {
val sta = stack.vb.asInstanceOf[BoxStack].stack.getOrElse {
throw new UvmRuntimeException("Stack must not be NULL")
}
......@@ -438,13 +438,7 @@ class ClientAgent(mutator: Mutator)(
throw new UvmRuntimeException("Stack-bottom function must not be NULL")
}
val funcVer = funcVal.versions.headOption.getOrElse {
throw new UvmRuntimeException("Stack-bottom function %s is not defined.".format(funcVal.repr))
}
val argBoxes = argList.map(_.vb)
sta.pushMuFrame(funcVer, argBoxes)
sta.pushFrame(funcVal)
}
def tr64IsFp(handle: Handle): Boolean = {
......@@ -554,7 +548,7 @@ class ClientAgent(mutator: Mutator)(
val c = cookie.vb.asInstanceOf[BoxInt].value.toLong
val addr = microVM.nativeCallHelper.exposeFuncDynamic(f,c)
val addr = microVM.nativeCallHelper.exposeFuncDynamic(f, c)
newHandle(InternalTypePool.funcPtrOf(sig), BoxPointer(addr))
}
......
This diff is collapsed.
package uvm.refimpl.itpr
import uvm.FuncVer
import uvm.Function
import uvm.refimpl.MicroVM
import uvm.refimpl.mem._
import scala.collection.mutable.HashMap
......@@ -28,7 +28,7 @@ class ThreadStackManager(implicit microVM: MicroVM, nativeCallHelper: NativeCall
def getThreadByID(id: Int): Option[InterpreterThread] = threadRegistry.get(id)
def iterateAllLiveStacks: Iterable[InterpreterStack] = stackRegistry.values.filter(_.state != StackState.Dead)
def iterateAllLiveStacks: Iterable[InterpreterStack] = stackRegistry.values.filter(_.state != FrameState.Dead)
def iterateAllLiveThreads: Iterable[InterpreterThread] = threadRegistry.values.filter(_.isRunning)
......@@ -55,10 +55,10 @@ class ThreadStackManager(implicit microVM: MicroVM, nativeCallHelper: NativeCall
* via the "new_stack" message or micro VM threads (the InterpreterThread class) which can execute the NEWSTACK
* instruction.
*/
def newStack(funcVer: FuncVer, args: Seq[ValueBox], mutator: Mutator): InterpreterStack = {
def newStack(func: Function, mutator: Mutator): InterpreterStack = {
val stackMemory = microVM.memoryManager.makeStackMemory(mutator)
val id = makeStackID()
val sta = new InterpreterStack(id, stackMemory, funcVer, args)
val sta = new InterpreterStack(id, stackMemory, func)
stackRegistry.put(id, sta)
sta
}
......
......@@ -361,8 +361,8 @@ object MemoryOperations {
}
}
def store(ptr: Boolean, ty: Type, loc: Word, nvb: ValueBox, br: ValueBox)(implicit memorySupport: MemorySupport): Unit = {
def storeScalar(ty: Type, loc: Word, nvb: ValueBox, br: ValueBox): Unit = ty match {
def store(ptr: Boolean, ty: Type, loc: Word, nvb: ValueBox)(implicit memorySupport: MemorySupport): Unit = {
def storeScalar(ty: Type, loc: Word, nvb: ValueBox): Unit = ty match {
case TypeInt(l) =>
val bi = nvb.asInstanceOf[BoxInt].value
l match {
......@@ -412,12 +412,11 @@ object MemoryOperations {
ty match {
case TypeVector(ety, len) =>
val nvbs = nvb.asInstanceOf[BoxSeq].values
val brs = br.asInstanceOf[BoxSeq].values
val elemSkip = alignUp(sizeOf(ety), alignOf(ety))
for (((brElem, nvbElem), i) <- (brs zip nvbs).zipWithIndex) {
storeScalar(ety, loc + elemSkip * i, nvbElem, brElem)
for ((nvbElem, i) <- nvbs.zipWithIndex) {
storeScalar(ety, loc + elemSkip * i, nvbElem)
}
case sty => storeScalar(sty, loc, nvb, br)
case sty => storeScalar(sty, loc, nvb)
}
}
......
......@@ -14,28 +14,37 @@ import uvm.refimpl.nat.NativeCallHelper
import org.slf4j.LoggerFactory
import com.typesafe.scalalogging.Logger
abstract class StackState
object StackState {
case class Ready(t: Type) extends uvm.refimpl.itpr.StackState
case object Running extends StackState
case object Dead extends StackState
/** The state of a frame. */
abstract class FrameState
object FrameState {
/** If values of ts are given, it is ready to resume. */
case class Ready(ts: Seq[Type]) extends FrameState
/** A thread is executing it. */
case object Running extends FrameState
/** Killed. */
case object Dead extends FrameState
}
class InterpreterStack(val id: Int, val stackMemory: StackMemory, stackBottomFunc: FuncVer, args: Seq[ValueBox])(
/**
* Implements a Mu Stack. Contains both Mu frames and native frames.
*/
class InterpreterStack(val id: Int, val stackMemory: StackMemory, stackBottomFunc: Function)(
implicit nativeCallHelper: NativeCallHelper) {
var gcMark: Boolean = false // Mark for GC.
private var _state: StackState = StackState.Ready(InternalTypes.VOID) // Initial state is READY<void>
def state = _state
private def state_=(s: StackState) = _state = s
private var _top: InterpreterFrame = InterpreterFrame.forMuFunc(stackBottomFunc, 0L, args, None)
private var _top: InterpreterFrame = InterpreterFrame.forMuFunc(stackMemory.top, stackBottomFunc, 0L, None)
/** The top frame */
def top = _top
private def top_=(f: InterpreterFrame) = _top = f
/** The state of the stack (i.e. state of the top frame) */
def state = top.state
/** A lazily created native stack keeper. */
var maybeNSK: Option[NativeStackKeeper] = None
/** Lazily create the nsk. */
private def ensureNSK(): Unit = {
if (maybeNSK == None) {
val nsk = new NativeStackKeeper()
......@@ -43,6 +52,7 @@ class InterpreterStack(val id: Int, val stackMemory: StackMemory, stackBottomFun
}
}
/** Iterate through all frames. */
def frames: Iterator[InterpreterFrame] = new AbstractIterator[InterpreterFrame] {
var curFrame: Option[InterpreterFrame] = Some(top)
def hasNext = curFrame.isDefined
......@@ -53,122 +63,323 @@ class InterpreterStack(val id: Int, val stackMemory: StackMemory, stackBottomFun
}
}
/** Iterate through Mu frames only. */
def muFrames: Iterator[MuFrame] = frames.filter(_.isInstanceOf[MuFrame]).map(_.asInstanceOf[MuFrame])
def pushMuFrame(funcVer: FuncVer, args: Seq[ValueBox]): Unit = {
val newFrame = InterpreterFrame.forMuFunc(funcVer, 0L, args, Some(top))
///////// frame manipulations
/** Create a new Mu frame, usually for Mu calling Mu, or OSR. */
private def pushMuFrame(func: Function): Unit = {
assert(top.isInstanceOf[MuFrame])
val newFrame = InterpreterFrame.forMuFunc(stackMemory.top, func, 0L, Some(top))
top = newFrame
top.savedStackPointer = stackMemory.top
}
def pushMuFrameForCallBack(funcVer: FuncVer, cookie: Long, args: Seq[ValueBox]): Unit = {
val newFrame = InterpreterFrame.forMuFunc(funcVer, cookie, args, Some(top))
/** Create a new Mu frame for calling from native. */
private def pushMuFrameForCallBack(func: Function, cookie: Long): Unit = {
assert(top.isInstanceOf[NativeFrame])
val newFrame = InterpreterFrame.forMuFunc(stackMemory.top, func, cookie, Some(top))
top = newFrame
top.savedStackPointer = stackMemory.top
}
def replaceTopMuFrame(funcVer: FuncVer, args: Seq[ValueBox]): Unit = {
val newFrame = InterpreterFrame.forMuFunc(funcVer, 0L, args, top.prev)
stackMemory.rewind(top.savedStackPointer)
/** Replace the top Mu frame with a frame of a different function. Usually used by tailcall. */
private def replaceTopMuFrame(func: Function): Unit = {
assert(top.isInstanceOf[MuFrame])
if (top.isInstanceOf[DefinedMuFrame]) {
stackMemory.rewind(top.asInstanceOf[DefinedMuFrame].savedStackPointer)
}
val newFrame = InterpreterFrame.forMuFunc(stackMemory.top, func, 0L, top.prev)
top = newFrame
top.savedStackPointer = stackMemory.top
}
private def pushNativeFrame(func: Word): Unit = {
/** Push a (conceptual) native frame. The "real native frame" is held by the NSK slave, of course. */
private def pushNativeFrame(sig: FuncSig, func: Word): Unit = {
assert(top.isInstanceOf[MuFrame])
val newFrame = InterpreterFrame.forNativeFunc(func, Some(top))
top = newFrame
top.savedStackPointer = stackMemory.top
}
def callNativeOnStack(sig: FuncSig, func: Word, args: Seq[ValueBox], retBox: ValueBox): NativeCallResult = {
/** Pop a Mu frame or a native frame. Throw error when popping the bottom frame. */
private def _popFrame(): Unit = {
top = top.prev.getOrElse {
throw new UvmRuntimeException("Attemting to pop the last frame of a stack. Stack ID: %d.".format(id))
}
}
/** Pop a Mu frame */
private def popMuFrame(): Unit = {
assert(top.isInstanceOf[MuFrame])
ensureNSK()
pushNativeFrame(func)
val result = maybeNSK.get.callNative(sig, func, args, retBox)
handleNativeCallResult(result)
result
if (top.isInstanceOf[DefinedMuFrame]) {
stackMemory.rewind(top.asInstanceOf[DefinedMuFrame].savedStackPointer)
}
_popFrame()
}
def returnToNativeOnStack(): NativeCallResult = {
/** Pop a native frame */
private def popNativeFrame(): Unit = {
assert(top.isInstanceOf[NativeFrame])
val result = maybeNSK.get.returnToCallBack()
_popFrame()
}
/**
* Return from a Mu frame to a native frame. This will eventually resume in Mu.
* @return true if the interpreter needs to increment the PC.
*/
private def returnToNative(maybeRvb: Option[ValueBox]): Boolean = {
assert(top.isInstanceOf[NativeFrame])
val result = maybeNSK.get.returnToNative(maybeRvb)
handleNativeCallResult(result)
result
}
private def handleNativeCallResult(nsr: NativeCallResult): Unit = nsr match {
case r: NativeCallResult.Return => popNativeFrame()
case r: NativeCallResult.CallBack => top.asInstanceOf[NativeFrame].maybeCallback = Some(r)
/**
* Handle native call result. This will eventually resume in Mu.
* @return true if the interpreter needs to increment the PC.
*/
private def handleNativeCallResult(ncr: NativeCallResult): Boolean = {
ncr match {
case r @ NativeCallResult.ReturnToMu(maybeRvb) => {
assert(top.isInstanceOf[NativeFrame])
popNativeFrame()
assert(top.isInstanceOf[MuFrame])
top.asInstanceOf[MuFrame].resumeNormally(maybeRvb.toSeq)
}
case r @ NativeCallResult.CallMu(func, cookie, args) => {
assert(top.isInstanceOf[NativeFrame])
pushMuFrameForCallBack(func, cookie)
top.asInstanceOf[MuFrame].resumeNormally(args.toSeq)
}
}
}
def popFrame(): Unit = {
stackMemory.rewind(top.savedStackPointer)
top = top.prev.getOrElse {
throw new UvmRuntimeException("Attemting to pop the last frame of a stack. Stack ID: %d.".format(id))
///////// interpreter/client-visible stack state transitions
/**
* Mu calling a Mu function.
*/
def callMu(func: Function, args: Seq[ValueBox]): Boolean = {
assert(top.isInstanceOf[MuFrame])
top.state = FrameState.Ready(func.sig.retTys)
pushMuFrame(func)
top.asInstanceOf[MuFrame].resumeNormally(args)
}
/** Mu tail-calling a Mu function. */
def tailCallMu(func: Function, args: Seq[ValueBox]): Boolean = {
assert(top.isInstanceOf[MuFrame])
replaceTopMuFrame(func)
top.asInstanceOf[MuFrame].resumeNormally(args)
}
private def popNativeFrame(): Unit = {
assert(top.isInstanceOf[NativeFrame])
popFrame()
/** Returning from a Mu frame. Will eventually resume in Mu.*/
def retFromMu(rvs: Seq[ValueBox]): Boolean = {
assert(top.isInstanceOf[MuFrame])
popMuFrame()
top match {
case f: MuFrame => {
f.resumeNormally(rvs)
}
case f: NativeFrame => {
val maybeRvb = rvs match {
case Seq() => None
case Seq(b) => Some(b)
case bs => throw new UvmRefImplException(
"Cannot return multiple values to native functions. Native func: 0x%x".format(f.func))
}
returnToNative(maybeRvb)
}
}
}
/** Mu calling a native function. */
def callNative(sig: FuncSig, func: Word, args: Seq[ValueBox]): Boolean = {
assert(top.isInstanceOf[MuFrame])
ensureNSK()
pushNativeFrame(sig, func)
val result = maybeNSK.get.callNative(sig, func, args)
handleNativeCallResult(result)
}
/** Unwind to a Mu frame. The interpreter will handle the resumption. */
def unwindTo(f: InterpreterFrame): Unit = {
top = f
stackMemory.rewind(top.savedStackPointer)
top match {
case mf: DefinedMuFrame => {
stackMemory.rewind(mf.savedStackPointer)
top.state = FrameState.Running
}
case mf: UndefinedMuFrame => {
throw new UvmRefImplException("Unwound to a frame for undefined Mu function %s. Stack ID: %d.".format(mf.func.repr, id))
}
case nf: NativeFrame => {
throw new UnimplementedOprationException("Cannot unwind to native frames. Stack ID: %d.".format(id))
}
}
}
def unbindFromThread(retTy: Type): Unit = {
state = StackState.Ready(retTy)
/** Unbind the stack from a thread, returning with values. */
def unbindRetWith(retTys: Seq[Type]): Unit = {
assert(top.isInstanceOf[MuFrame])
top.state = FrameState.Ready(retTys)
}
def rebindToThread(): Unit = {
state = StackState.Running
/**
* Rebind to this stack, passing values.
* @return true if the interpreter should increment the PC by continueNormally().
*/
def rebindPassValues(args: Seq[ValueBox]): Boolean = {
top match {
case mf: MuFrame => {
mf.resumeNormally(args)
}
case nf: NativeFrame => {
val maybeRvb = args match {
case Seq() => None
case Seq(vb) => Some(vb)
case bs => throw new UvmRuntimeException("Attempted to return multiple values to a native frame. " +
"Stack ID: %d, Native function: 0x%x".format(id, nf.func))
}
returnToNative(maybeRvb)
}
}
}
/** Kill the stack */
def kill(): Unit = {
state = StackState.Dead
for (f <- frames) {
f.state = FrameState.Dead
}
maybeNSK.foreach { nsk =>
nsk.close()
}
}
/** Pop a frame. Part of the API. Also used by THROW */
def popFrame(): Unit = {
_popFrame()
}
/** Push a Mu frame. Part of the API. */
def pushFrame(func: Function) {
pushMuFrame(func)
}
}
abstract class InterpreterFrame(val prev: Option[InterpreterFrame]) {
/**
* The stack pointer to restore to when unwinding THE CURRENT FRAME. In other word, this value is the stackMemory.top
* of the stack when the current frame is pushed.
* <p>
* Native frames do not really use this. They use their own stacks.
* The state of the frame. Must be set when creating the frame.
*/
var savedStackPointer: Word = 0
var state: FrameState = _
}
object InterpreterFrame {
def forMuFunc(funcVer: FuncVer, cookie: Long, args: Seq[ValueBox], prev: Option[InterpreterFrame]): MuFrame = {
val frm = new MuFrame(funcVer, cookie, prev) // Bottom frame
for ((p, a) <- (funcVer.entry.norParams zip args)) {
frm.boxes(p).copyFrom(a)
def forMuFunc(savedStackPointer: Word, func: Function, cookie: Long, prev: Option[InterpreterFrame]): MuFrame = {
val frm = func.versions.headOption match {
case None => new UndefinedMuFrame(func, prev)
case Some(funcVer) => new DefinedMuFrame(savedStackPointer, funcVer, cookie, prev)
}
frm.state = FrameState.Ready(func.sig.retTys)
frm
}
def forNativeFunc(func: Word, prev: Option[InterpreterFrame]): NativeFrame = {
val frm = new NativeFrame(func, prev)
// A white lie. A native frame cannot be created until actualy calling a native function.
frm.state = FrameState.Running
frm
}
}
class NativeFrame(val func: Word, prev: Option[InterpreterFrame]) extends InterpreterFrame(prev)
abstract class MuFrame(val func: Function, prev: Option[InterpreterFrame]) extends InterpreterFrame(prev) {
/**
* Resume a Mu frame.
* @return true if the interpreter should increment the PC
*/
def resumeNormally(args: Seq[ValueBox]): Boolean
/**
* true if the frame is just created (push_frame or new_stack). Binding a thread to the stack or executing an
* instruction will make it false.
*/
var justCreated: Boolean = true
/**
* Traverse over all local variable boxes. For GC.
*/
def scannableBoxes: TraversableOnce[ValueBox]
}
object UndefinedMuFrame {
val logger = Logger(LoggerFactory.getLogger(getClass.getName))
}
/**
* A Mu frame
* A Mu frame for undefined Mu frames. Such frames can still be resumed, but will trap as soon as being executed.
*/
class UndefinedMuFrame(func: Function, prev: Option[InterpreterFrame]) extends MuFrame(func, prev) {
import UndefinedMuFrame._
val boxes = func.sig.paramTys.map { ty =>
try {
ValueBox.makeBoxForType(ty)
} catch {
case e: UvmRefImplException => {
logger.error("Having problem creating box for parameter type: %s".format(ty))
throw e
}
}
}
override def scannableBoxes = boxes
def resumeNormally(args: Seq[ValueBox]): Boolean = {
if (justCreated) {
if (boxes.length != args.length) {
throw new UvmRefImplException("Function %s (not undefined yet) expects %d params, got %d args.".format(
func.repr, boxes.length, args.length))
}
for ((dst, src) <- boxes zip args) {
dst.copyFrom(src)
}
justCreated = false
state = FrameState.Running
false
} else {
throw new UvmRefImplException("Undefined frame for function %s is already executed. Could be double binding.".format(func.repr))
}
}
}
object DefinedMuFrame {
val logger = Logger(LoggerFactory.getLogger(getClass.getName))
}
/**
* A Mu frame for defined Mu frames.
*
* @param savedStackPointer: The stack pointer to restore to when unwinding THE CURRENT FRAME. In other word, this value is the stackMemory.top
* of the stack when the current frame is pushed.
* @param cookie: The cookie in the native interface. When called by another Mu function, cookie can be any value.
*/
class MuFrame(val funcVer: FuncVer, val cookie: Long, prev: Option[InterpreterFrame]) extends InterpreterFrame(prev) {
class DefinedMuFrame(val savedStackPointer: Word, val funcVer: FuncVer, val cookie: Long, prev: Option[InterpreterFrame])
extends MuFrame(funcVer.func, prev) {
import DefinedMuFrame._
/** Boxes for all local variables. */
val boxes = new HashMap[LocalVariable, ValueBox]()
override def scannableBoxes = boxes.values
/** Edge-assigned instructions take values determined at look backedges */
val edgeAssignedBoxes = new HashMap[Parameter, ValueBox]()
......@@ -178,32 +389,6 @@ class MuFrame(val funcVer: FuncVer, val cookie: Long, prev: Option[InterpreterFr