Commit 059a77ba authored by Kunshan Wang's avatar Kunshan Wang

Implemented callback. Old things work again, but need tests for

callback.
parent a0397cce
...@@ -28,7 +28,7 @@ class MicroVM(heapSize: Word = MicroVM.DEFAULT_HEAP_SIZE, ...@@ -28,7 +28,7 @@ class MicroVM(heapSize: Word = MicroVM.DEFAULT_HEAP_SIZE,
private implicit val memorySupport = memoryManager.memorySupport private implicit val memorySupport = memoryManager.memorySupport
val nativeCallHelper = new NativeCallHelper() implicit val nativeCallHelper = new NativeCallHelper()
val threadStackManager = new ThreadStackManager() val threadStackManager = new ThreadStackManager()
val trapManager = new TrapManager() val trapManager = new TrapManager()
......
...@@ -346,7 +346,7 @@ class ClientAgent(mutator: Mutator)( ...@@ -346,7 +346,7 @@ class ClientAgent(mutator: Mutator)(
def killStack(stack: Handle): Unit = { def killStack(stack: Handle): Unit = {
val sv = getStackNotNull(stack) val sv = getStackNotNull(stack)
sv.state = StackState.Dead sv.kill()
} }
private def nthFrame(stack: InterpreterStack, n: Int): InterpreterFrame = { private def nthFrame(stack: InterpreterStack, n: Int): InterpreterFrame = {
......
...@@ -2,11 +2,8 @@ package uvm.refimpl.itpr ...@@ -2,11 +2,8 @@ package uvm.refimpl.itpr
import scala.annotation.tailrec import scala.annotation.tailrec
import scala.collection.mutable.ArrayBuffer import scala.collection.mutable.ArrayBuffer
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import com.typesafe.scalalogging.Logger import com.typesafe.scalalogging.Logger
import uvm._ import uvm._
import uvm.comminsts._ import uvm.comminsts._
import uvm.refimpl._ import uvm.refimpl._
...@@ -14,6 +11,7 @@ import uvm.refimpl.mem._ ...@@ -14,6 +11,7 @@ import uvm.refimpl.mem._
import uvm.refimpl.mem.TypeSizes.Word import uvm.refimpl.mem.TypeSizes.Word
import uvm.ssavariables._ import uvm.ssavariables._
import uvm.types._ import uvm.types._
import uvm.refimpl.nat.NativeCallResult
object InterpreterThread { object InterpreterThread {
val logger = Logger(LoggerFactory.getLogger(getClass.getName)) val logger = Logger(LoggerFactory.getLogger(getClass.getName))
...@@ -518,14 +516,39 @@ class InterpreterThread(val id: Int, initialStack: InterpreterStack, val mutator ...@@ -518,14 +516,39 @@ class InterpreterThread(val id: Int, initialStack: InterpreterStack, val mutator
case i @ InstRet(retTy, retVal) => { case i @ InstRet(retTy, retVal) => {
val rvb = boxOf(retVal) val rvb = boxOf(retVal)
curStack.popFrame() curStack.popFrame()
val newCurInst = curInst // in the parent frame of the RET top match {
boxOf(newCurInst).copyFrom(rvb) case f: MuFrame => {
finishHalfExecutedInst() val newCurInst = curInst // in the parent frame of the RET
boxOf(newCurInst).copyFrom(rvb)
finishHalfExecutedInst()
}
case f: NativeFrame => {
// Now the top is a native frame, and it must be calling back to Mu.
// Set its return value
f.maybeCallback.get.retBox.copyFrom(rvb)
// Return to native, and keep an eye on the result, in case it calls back again.
val result = curStack.returnToNativeOnStack()
// Handle the control flow according to how the native function respond
handleNativeCallResult(result)
}
}
} }
case i @ InstRetVoid() => { case i @ InstRetVoid() => {
curStack.popFrame() curStack.popFrame()
finishHalfExecutedInst() top match {
case f: MuFrame => {
finishHalfExecutedInst()
}
case f: NativeFrame => {
// Now the top is a native frame, and it must be calling back to Mu.
// Since Mu returns void, we don't need to assign the return value.
// Return to native, and keep an eye on the result, in case it calls back again.
val result = curStack.returnToNativeOnStack()
// Handle the control flow according to how the native function respond
handleNativeCallResult(result)
}
}
} }
case i @ InstThrow(excVal) => { case i @ InstThrow(excVal) => {
...@@ -792,13 +815,9 @@ class InterpreterThread(val id: Int, initialStack: InterpreterStack, val mutator ...@@ -792,13 +815,9 @@ class InterpreterThread(val id: Int, initialStack: InterpreterStack, val mutator
val argBoxes = argList.map(boxOf) val argBoxes = argList.map(boxOf)
val retBox = boxOf(i) val retBox = boxOf(i)
??? val result = curStack.callNativeOnStack(sig, addr, argBoxes, retBox)
//
// microVM.threadStackManager.threadCallingNative = Some(this)
//
// microVM.nativeCallHelper.callNative(sig, addr, argBoxes, retBox)
continueNormally() handleNativeCallResult(result)
} }
case i @ InstNewStack(sig, callee, argList, excClause) => { case i @ InstNewStack(sig, callee, argList, excClause) => {
...@@ -873,7 +892,7 @@ class InterpreterThread(val id: Int, initialStack: InterpreterStack, val mutator ...@@ -873,7 +892,7 @@ class InterpreterThread(val id: Int, initialStack: InterpreterStack, val mutator
val sta = boxOf(s).asInstanceOf[BoxStack].stack.getOrElse { val sta = boxOf(s).asInstanceOf[BoxStack].stack.getOrElse {
throw new UvmRuntimeException(ctx + "Attempt to kill NULL stack.") throw new UvmRuntimeException(ctx + "Attempt to kill NULL stack.")
} }
sta.state = StackState.Dead sta.kill()
continueNormally() continueNormally()
} }
...@@ -1216,8 +1235,8 @@ class InterpreterThread(val id: Int, initialStack: InterpreterStack, val mutator ...@@ -1216,8 +1235,8 @@ class InterpreterThread(val id: Int, initialStack: InterpreterStack, val mutator
} }
} }
case f: NativeFrame => { case f: NativeFrame => {
throw new UvmRuntimeException(ctx + "Attempt to throw exception into a native frame. It has implementation-defined. "+ throw new UvmRuntimeException(ctx + "Attempt to throw exception into a native frame. It has implementation-defined. " +
"behaviour, and the refimpl does not allow it. Although not always forbidden elsewhere, it is almost always dangerous.") "behaviour, and the refimpl does not allow it. Although not always forbidden elsewhere, it is almost always dangerous.")
} }
} }
...@@ -1251,6 +1270,23 @@ class InterpreterThread(val id: Int, initialStack: InterpreterStack, val mutator ...@@ -1251,6 +1270,23 @@ class InterpreterThread(val id: Int, initialStack: InterpreterStack, val mutator
} }
} }
// Control flow involving native functions
/**
*
*/
private def handleNativeCallResult(result: NativeCallResult): Unit = result match {
case NativeCallResult.CallBack(func, cookie, args, retBox) => {
val funcVer = getFuncDefOrTriggerCallback(func)
curInstHalfExecuted = true
curStack.pushMuFrame(funcVer, args)
}
case NativeCallResult.Return() => {
continueNormally()
}
}
// Misc helper // Misc helper
private def writeBooleanResult(result: Boolean, box: ValueBox): Unit = { private def writeBooleanResult(result: Boolean, box: ValueBox): Unit = {
...@@ -1285,7 +1321,7 @@ class InterpreterThread(val id: Int, initialStack: InterpreterStack, val mutator ...@@ -1285,7 +1321,7 @@ class InterpreterThread(val id: Int, initialStack: InterpreterStack, val mutator
/** Terminate the thread. Please only let the thread terminate itself. */ /** Terminate the thread. Please only let the thread terminate itself. */
private def threadExit(): Unit = { private def threadExit(): Unit = {
curStack.state = StackState.Dead curStack.kill()
isRunning = false isRunning = false
} }
...@@ -1293,20 +1329,20 @@ class InterpreterThread(val id: Int, initialStack: InterpreterStack, val mutator ...@@ -1293,20 +1329,20 @@ class InterpreterThread(val id: Int, initialStack: InterpreterStack, val mutator
/** Unbind the current thread from the stack. */ /** Unbind the current thread from the stack. */
private def unbind(readyType: Type): Unit = { private def unbind(readyType: Type): Unit = {
curStack.state = StackState.Ready(readyType) curStack.unbindFromThread(readyType)
stack = None stack = None
} }
/** Unbind and kill the current stack. */ /** Unbind and kill the current stack. */
private def unbindAndKillStack(): Unit = { private def unbindAndKillStack(): Unit = {
curStack.state = StackState.Dead curStack.kill()
stack = None stack = None
} }
/** Rebind to a stack. */ /** Rebind to a stack. */
private def rebind(newStack: InterpreterStack): Unit = { private def rebind(newStack: InterpreterStack): Unit = {
stack = Some(newStack) stack = Some(newStack)
curStack.state = StackState.Running curStack.rebindToThread()
} }
/** Rebind to a stack and pass a value. */ /** Rebind to a stack and pass a value. */
......
...@@ -7,6 +7,7 @@ import scala.collection.mutable.HashMap ...@@ -7,6 +7,7 @@ import scala.collection.mutable.HashMap
import scala.collection.mutable.ArrayBuffer import scala.collection.mutable.ArrayBuffer
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import com.typesafe.scalalogging.Logger import com.typesafe.scalalogging.Logger
import uvm.refimpl.nat.NativeCallHelper
object ThreadStackManager { object ThreadStackManager {
val logger = Logger(LoggerFactory.getLogger(getClass.getName)) val logger = Logger(LoggerFactory.getLogger(getClass.getName))
...@@ -16,7 +17,7 @@ object ThreadStackManager { ...@@ -16,7 +17,7 @@ object ThreadStackManager {
* The manager of all Mu threads and stacks. Also responsible for the actual execution of Mu IR code, i.e. as the "boss" * The manager of all Mu threads and stacks. Also responsible for the actual execution of Mu IR code, i.e. as the "boss"
* of all InterpreterThread instances. * of all InterpreterThread instances.
*/ */
class ThreadStackManager(implicit microVM: MicroVM) { class ThreadStackManager(implicit microVM: MicroVM, nativeCallHelper: NativeCallHelper) {
import ThreadStackManager._ import ThreadStackManager._
private val stackRegistry = new HashMap[Int, InterpreterStack]() private val stackRegistry = new HashMap[Int, InterpreterStack]()
......
...@@ -8,6 +8,9 @@ import uvm.refimpl.mem._ ...@@ -8,6 +8,9 @@ import uvm.refimpl.mem._
import uvm.refimpl.mem.TypeSizes.Word import uvm.refimpl.mem.TypeSizes.Word
import scala.collection.mutable.HashMap import scala.collection.mutable.HashMap
import scala.collection.AbstractIterator import scala.collection.AbstractIterator
import uvm.refimpl.nat.NativeStackKeeper
import uvm.refimpl.nat.NativeCallResult
import uvm.refimpl.nat.NativeCallHelper
abstract class StackState abstract class StackState
...@@ -17,16 +20,27 @@ object StackState { ...@@ -17,16 +20,27 @@ object StackState {
case object Dead extends StackState case object Dead extends StackState
} }
class InterpreterStack(val id: Int, val stackMemory: StackMemory, stackBottomFunc: FuncVer, args: Seq[ValueBox]) { class InterpreterStack(val id: Int, val stackMemory: StackMemory, stackBottomFunc: FuncVer, args: Seq[ValueBox])(
var gcMark: Boolean = false // Mark for GC. implicit nativeCallHelper: NativeCallHelper) {
var gcMark: Boolean = false // Mark for GC.
var state: StackState = StackState.Ready(InternalTypes.VOID) // Initial state is READY<void>
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, args, None) private var _top: InterpreterFrame = InterpreterFrame.forMuFunc(stackBottomFunc, args, None)
def top = _top def top = _top
private def top_=(f: InterpreterFrame) = _top = f private def top_=(f: InterpreterFrame) = _top = f
var maybeNSK: Option[NativeStackKeeper] = None
private def ensureNSK(): Unit = {
if (maybeNSK == None) {
val nsk = new NativeStackKeeper()
maybeNSK = Some(nsk)
}
}
def frames: Iterator[InterpreterFrame] = new AbstractIterator[InterpreterFrame] { def frames: Iterator[InterpreterFrame] = new AbstractIterator[InterpreterFrame] {
var curFrame: Option[InterpreterFrame] = Some(top) var curFrame: Option[InterpreterFrame] = Some(top)
def hasNext = curFrame.isDefined def hasNext = curFrame.isDefined
...@@ -36,9 +50,9 @@ class InterpreterStack(val id: Int, val stackMemory: StackMemory, stackBottomFun ...@@ -36,9 +50,9 @@ class InterpreterStack(val id: Int, val stackMemory: StackMemory, stackBottomFun
res res
} }
} }
def muFrames: Iterator[MuFrame] = frames.filter(_.isInstanceOf[MuFrame]).map(_.asInstanceOf[MuFrame]) def muFrames: Iterator[MuFrame] = frames.filter(_.isInstanceOf[MuFrame]).map(_.asInstanceOf[MuFrame])
def pushMuFrame(funcVer: FuncVer, args: Seq[ValueBox]): Unit = { def pushMuFrame(funcVer: FuncVer, args: Seq[ValueBox]): Unit = {
val newFrame = InterpreterFrame.forMuFunc(funcVer, args, Some(top)) val newFrame = InterpreterFrame.forMuFunc(funcVer, args, Some(top))
top = newFrame top = newFrame
...@@ -51,9 +65,32 @@ class InterpreterStack(val id: Int, val stackMemory: StackMemory, stackBottomFun ...@@ -51,9 +65,32 @@ class InterpreterStack(val id: Int, val stackMemory: StackMemory, stackBottomFun
top = newFrame top = newFrame
top.savedStackPointer = stackMemory.top top.savedStackPointer = stackMemory.top
} }
def pushNativeFrame(func: Word): Unit = { private def pushNativeFrame(func: Word): Unit = {
val newFrame = InterpreterFrame.forNativeFunc(func, Some(top)) 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 = {
assert(top.isInstanceOf[MuFrame])
ensureNSK()
pushNativeFrame(func)
val result = maybeNSK.get.callNative(sig, func, args, retBox)
handleNativeCallResult(result)
result
}
def returnToNativeOnStack(): NativeCallResult = {
assert(top.isInstanceOf[NativeFrame])
val result = maybeNSK.get.returnToCallBack()
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)
} }
def popFrame(): Unit = { def popFrame(): Unit = {
...@@ -62,11 +99,31 @@ class InterpreterStack(val id: Int, val stackMemory: StackMemory, stackBottomFun ...@@ -62,11 +99,31 @@ class InterpreterStack(val id: Int, val stackMemory: StackMemory, stackBottomFun
throw new UvmRuntimeException("Attemting to pop the last frame of a stack. Stack ID: %d.".format(id)) throw new UvmRuntimeException("Attemting to pop the last frame of a stack. Stack ID: %d.".format(id))
} }
} }
private def popNativeFrame(): Unit = {
assert(top.isInstanceOf[NativeFrame])
popFrame()
}
def unwindTo(f: InterpreterFrame): Unit = { def unwindTo(f: InterpreterFrame): Unit = {
top = f top = f
stackMemory.rewind(top.savedStackPointer) stackMemory.rewind(top.savedStackPointer)
} }
def unbindFromThread(retTy: Type): Unit = {
state = StackState.Ready(retTy)
}
def rebindToThread(): Unit = {
state = StackState.Running
}
def kill(): Unit = {
state = StackState.Dead
maybeNSK.foreach { nsk =>
nsk.close()
}
}
} }
abstract class InterpreterFrame(val prev: Option[InterpreterFrame]) { abstract class InterpreterFrame(val prev: Option[InterpreterFrame]) {
...@@ -89,7 +146,7 @@ object InterpreterFrame { ...@@ -89,7 +146,7 @@ object InterpreterFrame {
frm frm
} }
def forNativeFunc(func: Word, prev: Option[InterpreterFrame]): NativeFrame = { def forNativeFunc(func: Word, prev: Option[InterpreterFrame]): NativeFrame = {
val frm = new NativeFrame(func, prev) val frm = new NativeFrame(func, prev)
frm frm
...@@ -98,7 +155,7 @@ object InterpreterFrame { ...@@ -98,7 +155,7 @@ object InterpreterFrame {
class MuFrame(val funcVer: FuncVer, prev: Option[InterpreterFrame]) extends InterpreterFrame(prev) { class MuFrame(val funcVer: FuncVer, prev: Option[InterpreterFrame]) extends InterpreterFrame(prev) {
val boxes = new HashMap[LocalVariable, ValueBox]() val boxes = new HashMap[LocalVariable, ValueBox]()
/** Edge-assigned instructions take values determined at look backedges */ /** Edge-assigned instructions take values determined at look backedges */
val edgeAssignedBoxes = new HashMap[EdgeAssigned, ValueBox]() val edgeAssignedBoxes = new HashMap[EdgeAssigned, ValueBox]()
...@@ -114,17 +171,17 @@ class MuFrame(val funcVer: FuncVer, prev: Option[InterpreterFrame]) extends Inte ...@@ -114,17 +171,17 @@ class MuFrame(val funcVer: FuncVer, prev: Option[InterpreterFrame]) extends Inte
* Examples include: * Examples include:
* <ul> * <ul>
* <li>CALL: When executing CALL, the interpretation continues with the new fame. At this time, the CALL is partially * <li>CALL: When executing CALL, the interpretation continues with the new fame. At this time, the CALL is partially
* executed. When returning, the CALL is exposed to the InterpreterThread again and the other half is executed -- * executed. When returning, the CALL is exposed to the InterpreterThread again and the other half is executed --
* assigning the return value to the ValueBox of the CALL instruction. * assigning the return value to the ValueBox of the CALL instruction.
* <li>SWAPSTACK: The first half is swapping away from the stack. The second half is when swapping back, the * <li>SWAPSTACK: The first half is swapping away from the stack. The second half is when swapping back, the
* interpreter will assign the return value and decide where to continue (normally or exceptionally). * interpreter will assign the return value and decide where to continue (normally or exceptionally).
* <li>TRAP/WATCHPOINT: The current stack becomes unbound on entering the client. When returning from the client, * <li>TRAP/WATCHPOINT: The current stack becomes unbound on entering the client. When returning from the client,
* there is a stack re-binding. The same thing happens as SWAPSTACK. * there is a stack re-binding. The same thing happens as SWAPSTACK.
* </ul> * </ul>
* The CCALL instruction does not use this flag, because the control flow is deterministic. Unlike swapstack, CCALL * The CCALL instruction does not use this flag, because the control flow is deterministic. Unlike swapstack, CCALL
* must return from the only source -- the native program. * must return from the only source -- the native program.
* </p> * </p>
* This flag is set when executing CALL, SWAPSTACK, TRAP or WATCHPOINT. It is cleared when executing * This flag is set when executing CALL, SWAPSTACK, TRAP or WATCHPOINT. It is cleared when executing
* InterpreterThread.finishHalfExecutedInst or InterpreterThread.catchException. Particularly, the RET, RETVOID, * InterpreterThread.finishHalfExecutedInst or InterpreterThread.catchException. Particularly, the RET, RETVOID,
* THROW, TRAP, WATCHPOINT and SWAPSTACK instruction will call those two functions. * THROW, TRAP, WATCHPOINT and SWAPSTACK instruction will call those two functions.
*/ */
...@@ -183,4 +240,9 @@ class MuFrame(val funcVer: FuncVer, prev: Option[InterpreterFrame]) extends Inte ...@@ -183,4 +240,9 @@ class MuFrame(val funcVer: FuncVer, prev: Option[InterpreterFrame]) extends Inte
} }
class NativeFrame(val func: Word, prev: Option[InterpreterFrame]) extends InterpreterFrame(prev) { class NativeFrame(val func: Word, prev: Option[InterpreterFrame]) extends InterpreterFrame(prev) {
/**
* When calling back from native, maybeCallback is set to the CallBack object.
* Useful when returning value to the native.
*/
var maybeCallback: Option[NativeCallResult.CallBack] = None
} }
...@@ -123,7 +123,7 @@ class SimpleImmixCollector(val heap: SimpleImmixHeap, val space: SimpleImmixSpac ...@@ -123,7 +123,7 @@ class SimpleImmixCollector(val heap: SimpleImmixHeap, val space: SimpleImmixSpac
for (st <- microVM.threadStackManager.iterateAllLiveStacks) { for (st <- microVM.threadStackManager.iterateAllLiveStacks) {
if (!st.gcMark) { if (!st.gcMark) {
logger.debug("Killing stack %d...".format(st.id)) logger.debug("Killing stack %d...".format(st.id))
st.state = StackState.Dead st.kill()
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment