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,
private implicit val memorySupport = memoryManager.memorySupport
val nativeCallHelper = new NativeCallHelper()
implicit val nativeCallHelper = new NativeCallHelper()
val threadStackManager = new ThreadStackManager()
val trapManager = new TrapManager()
......
......@@ -346,7 +346,7 @@ class ClientAgent(mutator: Mutator)(
def killStack(stack: Handle): Unit = {
val sv = getStackNotNull(stack)
sv.state = StackState.Dead
sv.kill()
}
private def nthFrame(stack: InterpreterStack, n: Int): InterpreterFrame = {
......
......@@ -2,11 +2,8 @@ package uvm.refimpl.itpr
import scala.annotation.tailrec
import scala.collection.mutable.ArrayBuffer
import org.slf4j.LoggerFactory
import com.typesafe.scalalogging.Logger
import uvm._
import uvm.comminsts._
import uvm.refimpl._
......@@ -14,6 +11,7 @@ import uvm.refimpl.mem._
import uvm.refimpl.mem.TypeSizes.Word
import uvm.ssavariables._
import uvm.types._
import uvm.refimpl.nat.NativeCallResult
object InterpreterThread {
val logger = Logger(LoggerFactory.getLogger(getClass.getName))
......@@ -518,14 +516,39 @@ class InterpreterThread(val id: Int, initialStack: InterpreterStack, val mutator
case i @ InstRet(retTy, retVal) => {
val rvb = boxOf(retVal)
curStack.popFrame()
val newCurInst = curInst // in the parent frame of the RET
boxOf(newCurInst).copyFrom(rvb)
finishHalfExecutedInst()
top match {
case f: MuFrame => {
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() => {
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) => {
......@@ -792,13 +815,9 @@ class InterpreterThread(val id: Int, initialStack: InterpreterStack, val mutator
val argBoxes = argList.map(boxOf)
val retBox = boxOf(i)
???
//
// microVM.threadStackManager.threadCallingNative = Some(this)
//
// microVM.nativeCallHelper.callNative(sig, addr, argBoxes, retBox)
val result = curStack.callNativeOnStack(sig, addr, argBoxes, retBox)
continueNormally()
handleNativeCallResult(result)
}
case i @ InstNewStack(sig, callee, argList, excClause) => {
......@@ -873,7 +892,7 @@ class InterpreterThread(val id: Int, initialStack: InterpreterStack, val mutator
val sta = boxOf(s).asInstanceOf[BoxStack].stack.getOrElse {
throw new UvmRuntimeException(ctx + "Attempt to kill NULL stack.")
}
sta.state = StackState.Dead
sta.kill()
continueNormally()
}
......@@ -1216,8 +1235,8 @@ class InterpreterThread(val id: Int, initialStack: InterpreterStack, val mutator
}
}
case f: NativeFrame => {
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.")
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.")
}
}
......@@ -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
private def writeBooleanResult(result: Boolean, box: ValueBox): Unit = {
......@@ -1285,7 +1321,7 @@ class InterpreterThread(val id: Int, initialStack: InterpreterStack, val mutator
/** Terminate the thread. Please only let the thread terminate itself. */
private def threadExit(): Unit = {
curStack.state = StackState.Dead
curStack.kill()
isRunning = false
}
......@@ -1293,20 +1329,20 @@ class InterpreterThread(val id: Int, initialStack: InterpreterStack, val mutator
/** Unbind the current thread from the stack. */
private def unbind(readyType: Type): Unit = {
curStack.state = StackState.Ready(readyType)
curStack.unbindFromThread(readyType)
stack = None
}
/** Unbind and kill the current stack. */
private def unbindAndKillStack(): Unit = {
curStack.state = StackState.Dead
curStack.kill()
stack = None
}
/** Rebind to a stack. */
private def rebind(newStack: InterpreterStack): Unit = {
stack = Some(newStack)
curStack.state = StackState.Running
curStack.rebindToThread()
}
/** Rebind to a stack and pass a value. */
......
......@@ -7,6 +7,7 @@ import scala.collection.mutable.HashMap
import scala.collection.mutable.ArrayBuffer
import org.slf4j.LoggerFactory
import com.typesafe.scalalogging.Logger
import uvm.refimpl.nat.NativeCallHelper
object ThreadStackManager {
val logger = Logger(LoggerFactory.getLogger(getClass.getName))
......@@ -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"
* of all InterpreterThread instances.
*/
class ThreadStackManager(implicit microVM: MicroVM) {
class ThreadStackManager(implicit microVM: MicroVM, nativeCallHelper: NativeCallHelper) {
import ThreadStackManager._
private val stackRegistry = new HashMap[Int, InterpreterStack]()
......
......@@ -8,6 +8,9 @@ import uvm.refimpl.mem._
import uvm.refimpl.mem.TypeSizes.Word
import scala.collection.mutable.HashMap
import scala.collection.AbstractIterator
import uvm.refimpl.nat.NativeStackKeeper
import uvm.refimpl.nat.NativeCallResult
import uvm.refimpl.nat.NativeCallHelper
abstract class StackState
......@@ -17,16 +20,27 @@ object StackState {
case object Dead extends StackState
}
class InterpreterStack(val id: Int, val stackMemory: StackMemory, stackBottomFunc: FuncVer, args: Seq[ValueBox]) {
var gcMark: Boolean = false // Mark for GC.
var state: StackState = StackState.Ready(InternalTypes.VOID) // Initial state is READY<void>
class InterpreterStack(val id: Int, val stackMemory: StackMemory, stackBottomFunc: FuncVer, args: Seq[ValueBox])(
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, args, None)
def top = _top
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] {
var curFrame: Option[InterpreterFrame] = Some(top)
def hasNext = curFrame.isDefined
......@@ -36,9 +50,9 @@ class InterpreterStack(val id: Int, val stackMemory: StackMemory, stackBottomFun
res
}
}
def muFrames: Iterator[MuFrame] = frames.filter(_.isInstanceOf[MuFrame]).map(_.asInstanceOf[MuFrame])
def pushMuFrame(funcVer: FuncVer, args: Seq[ValueBox]): Unit = {
val newFrame = InterpreterFrame.forMuFunc(funcVer, args, Some(top))
top = newFrame
......@@ -51,9 +65,32 @@ class InterpreterStack(val id: Int, val stackMemory: StackMemory, stackBottomFun
top = newFrame
top.savedStackPointer = stackMemory.top
}
def pushNativeFrame(func: Word): Unit = {
private def pushNativeFrame(func: Word): Unit = {
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 = {
......@@ -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))
}
}
private def popNativeFrame(): Unit = {
assert(top.isInstanceOf[NativeFrame])
popFrame()
}
def unwindTo(f: InterpreterFrame): Unit = {
top = f
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]) {
......@@ -89,7 +146,7 @@ object InterpreterFrame {
frm
}
def forNativeFunc(func: Word, prev: Option[InterpreterFrame]): NativeFrame = {
val frm = new NativeFrame(func, prev)
frm
......@@ -98,7 +155,7 @@ object InterpreterFrame {
class MuFrame(val funcVer: FuncVer, prev: Option[InterpreterFrame]) extends InterpreterFrame(prev) {
val boxes = new HashMap[LocalVariable, ValueBox]()
/** Edge-assigned instructions take values determined at look backedges */
val edgeAssignedBoxes = new HashMap[EdgeAssigned, ValueBox]()
......@@ -114,17 +171,17 @@ class MuFrame(val funcVer: FuncVer, prev: Option[InterpreterFrame]) extends Inte
* Examples include:
* <ul>
* <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.
* <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,
* there is a stack re-binding. The same thing happens as SWAPSTACK.
* </ul>
* 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.
* </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,
* 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
}
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
for (st <- microVM.threadStackManager.iterateAllLiveStacks) {
if (!st.gcMark) {
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