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 {
......@@ -264,7 +263,7 @@ class ClientAgent(mutator: Mutator)(
def load(ord: MemoryOrder, loc: Handle): Handle = {
val (ptr, ty) = loc.ty match {
case TypeIRef(t) => (false, t)
case TypeUPtr(t) => (true, t)
case TypeUPtr(t) => (true, t)
}
val uty = InternalTypePool.unmarkedOf(ty)
val addr = MemoryOperations.addressOf(ptr, loc.vb)
......@@ -278,7 +277,7 @@ class ClientAgent(mutator: Mutator)(
def store(ord: MemoryOrder, loc: Handle, newVal: Handle): Unit = {
val (ptr, ty) = loc.ty match {
case TypeIRef(t) => (false, t)
case TypeUPtr(t) => (true, t)
case TypeUPtr(t) => (true, t)
}
val uty = InternalTypePool.unmarkedOf(ty)
val addr = MemoryOperations.addressOf(ptr, loc.vb)
......@@ -291,7 +290,7 @@ class ClientAgent(mutator: Mutator)(
def cmpXchg(ordSucc: MemoryOrder, ordFail: MemoryOrder, weak: Boolean, loc: Handle, expected: Handle, desired: Handle): (Boolean, Handle) = {
val (ptr, ty) = loc.ty match {
case TypeIRef(t) => (false, t)
case TypeUPtr(t) => (true, t)
case TypeUPtr(t) => (true, t)
}
val uty = InternalTypePool.unmarkedOf(ty)
val addr = MemoryOperations.addressOf(ptr, loc.vb)
......@@ -305,7 +304,7 @@ class ClientAgent(mutator: Mutator)(
def atomicRMW(ord: MemoryOrder, op: AtomicRMWOptr, loc: Handle, opnd: Handle): Handle = {
val (ptr, ty) = loc.ty match {
case TypeIRef(t) => (false, t)
case TypeUPtr(t) => (true, t)
case TypeUPtr(t) => (true, t)
}
val uty = InternalTypePool.unmarkedOf(ty)
val addr = MemoryOperations.addressOf(ptr, loc.vb)
......@@ -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)
......@@ -377,8 +370,9 @@ class ClientAgent(mutator: Mutator)(
val sv = getStackNotNull(stack)
val fr = nthFrame(sv, frame)
fr match {
case f: NativeFrame => 0
case f: MuFrame => f.funcVer.id
case f: NativeFrame => 0
case f: UndefinedMuFrame => 0
case f: DefinedMuFrame => f.funcVer.id
}
}
......@@ -386,8 +380,9 @@ class ClientAgent(mutator: Mutator)(
val sv = getStackNotNull(stack)
val fr = nthFrame(sv, frame)
fr match {
case f: NativeFrame => 0
case f: MuFrame => f.curInst.id
case f: NativeFrame => 0
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 = {
......@@ -545,19 +539,19 @@ class ClientAgent(mutator: Mutator)(
}
unpin(objRef)
}
def expose(func: Handle, callConv: Flag, cookie: Handle): Handle = {
val TypeFuncRef(sig) = func.ty
val f = func.vb.asInstanceOf[BoxFunc].func.getOrElse {
throw new UvmRuntimeException("Attempt to expose NULL Mu function")
}
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))
}
def unexpose(callConv: Flag, addr: Handle): Unit = {
val a = addr.vb.asInstanceOf[BoxPointer].addr
microVM.nativeCallHelper.unexposeFunc(a)
......
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()
}
/** Pop a native frame */
private def popNativeFrame(): Unit = {
assert(top.isInstanceOf[NativeFrame])
_popFrame()
}
def returnToNativeOnStack(): NativeCallResult = {
/**
* 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.returnToCallBack()
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)
}
/** 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)
}
}
}
private def popNativeFrame(): Unit = {
assert(top.isInstanceOf[NativeFrame])
popFrame()
/** 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 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)