Commit d67a307a authored by Kunshan Wang's avatar Kunshan Wang

WIP: Re-designed the stack-frame hierarchy.

parent f3796d66
......@@ -9,3 +9,4 @@ target
.tmpBin
.d8_history
hs_err_pid*.log
/bin/
......@@ -9,7 +9,7 @@ licenses := Seq("CC BY-SA 4.0" -> url("https://creativecommons.org/licenses/by-s
scalaVersion := "2.11.7"
libraryDependencies := Seq(
"org.antlr" % "antlr4" % "4.5",
"org.antlr" % "antlr4" % "4.5.1",
"com.typesafe.scala-logging" %% "scala-logging" % "3.1.0",
"ch.qos.logback" % "logback-classic" % "1.1.2",
"com.github.jnr" % "jnr-ffi" % "2.0.3",
......
resolvers += "simplytyped.com" at "http://simplytyped.com/repo/releases"
resolvers += "simplytyped" at "http://simplytyped.github.io/repo/releases"
// addSbtPlugin("com.simplytyped" % "sbt-antlr4" % "0.7.2")
addSbtPlugin("com.simplytyped" % "sbt-antlr4" % "0.7.7")
lazy val root = (project in file(".")).dependsOn(sbtAntlr4Plugin)
// lazy val root = (project in file(".")).dependsOn(sbtAntlr4Plugin)
lazy val sbtAntlr4Plugin = uri("https://github.com/ihji/sbt-antlr4.git")
// lazy val sbtAntlr4Plugin = uri("https://github.com/ihji/sbt-antlr4.git")
addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.5.0")
addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "4.0.0")
......@@ -369,30 +369,43 @@ class ClientAgent(mutator: Mutator)(
def currentFuncVer(stack: Handle, frame: Int): Int = {
val sv = getStackNotNull(stack)
val fr = nthFrame(sv, frame)
fr.funcVer.id
fr match {
case f: NativeFrame => 0
case f: MuFrame => f.funcVer.id
}
}
def currentInstruction(stack: Handle, frame: Int): Int = {
val sv = getStackNotNull(stack)
val fr = nthFrame(sv, frame)
fr.curInst.id
fr match {
case f: NativeFrame => 0
case f: MuFrame => f.curInst.id
}
}
def dumpKeepalives(stack: Handle, frame: Int): Seq[Handle] = {
val sv = getStackNotNull(stack)
val fr = nthFrame(sv, frame)
val i = fr.curInst
i match {
case hkac: HasKeepAliveClause => {
val kas = hkac.keepAlives
for (ka <- kas) yield {
val box = fr.boxes(ka)
val ty = TypeInferer.inferType(ka)
newHandle(ty, box)
}
fr match {
case f: NativeFrame => {
throw new UvmRefImplException("Attempt to dump keepalive variables for a native frame for native funciton 0x%x".format(f.func))
}
case _ => {
throw new UvmRuntimeException("The current instruction %s does not have keep-alive clause.".format(i.repr))
case f: MuFrame => {
val i = f.curInst
i match {
case hkac: HasKeepAliveClause => {
val kas = hkac.keepAlives
for (ka <- kas) yield {
val box = f.boxes(ka)
val ty = TypeInferer.inferType(ka)
newHandle(ty, box)
}
}
case _ => {
throw new UvmRuntimeException("The current instruction %s does not have keep-alive clause.".format(i.repr))
}
}
}
}
}
......@@ -400,9 +413,12 @@ class ClientAgent(mutator: Mutator)(
def popFrame(stack: Handle): Unit = {
val st = getStackNotNull(stack)
val top = st.top
top.prev match {
case None => throw new UvmRuntimeException("Attempting to pop the last frame of a stack.")
case Some(prev) => st.top = prev
top match {
case f: NativeFrame => throw new UvmRuntimeException("Attempting to pop a native frame. It has implementation-defined behaviour and this refimpl does not allow it.")
case f: MuFrame => f.prev match {
case None => throw new UvmRuntimeException("Attempting to pop the last frame of a stack.")
case Some(prev) => st.popFrame()
}
}
}
......@@ -421,7 +437,7 @@ class ClientAgent(mutator: Mutator)(
val argBoxes = argList.map(_.vb)
sta.pushFrame(funcVer, argBoxes)
sta.pushMuFrame(funcVer, argBoxes)
}
def tr64IsFp(handle: Handle): Boolean = {
......
......@@ -74,26 +74,35 @@ class InterpreterThread(val id: Int, initialStack: InterpreterStack, val mutator
// Convenient functions to get/set states
private def curStack = stack.get
private def top = curStack.top
private def curBB = top.curBB
private def curInst = top.curInst
private def curInstHalfExecuted = top.curInstHalfExecuted
private def curInstHalfExecuted_=(v: Boolean) = top.curInstHalfExecuted_=(v)
private def top: InterpreterFrame = curStack.top
private def topMu: MuFrame = curStack.top match {
case f: MuFrame => f
case f: NativeFrame => throw new UvmRefImplException(("Attempt to access the top frame of stack %d as a Mu frame " +
"while it is actually a native frame for native function 0x%x.").format(curStack.id, f.func))
}
private def curBB = topMu.curBB
private def curInst = topMu.curInst
private def curInstHalfExecuted = topMu.curInstHalfExecuted
private def curInstHalfExecuted_=(v: Boolean) = topMu.curInstHalfExecuted_=(v)
private def incPC(): Unit = top.incPC()
private def jump(bb: BasicBlock, ix: Int): Unit = top.jump(bb, ix)
private def incPC(): Unit = topMu.incPC()
private def jump(bb: BasicBlock, ix: Int): Unit = topMu.jump(bb, ix)
/** Get the value box of an SSA variable in a stack. */
private def boxOf(s: InterpreterStack, v: SSAVariable): ValueBox = v match {
case g: GlobalVariable => microVM.constantPool.getGlobalVarBox(g)
case l: LocalVariable => s.top.boxes(l)
case l: LocalVariable => s.top match {
case f: MuFrame => f.boxes(l)
case f: NativeFrame => throw new UvmRefImplException(("Attempt to find box for local variable %s on the top frame of stack %d as a Mu frame " +
"while the frame is actually a native frame for native function 0x%x.").format(l.repr, curStack.id, f.func))
}
}
/** Get the value box of an SSA variable in the current stack. */
private def boxOf(v: SSAVariable): ValueBox = boxOf(curStack, v)
/** Get the edge-assigned value box of an edge-assigned instruction in a stack. */
private def edgeAssignedBoxOf(s: InterpreterStack, ea: EdgeAssigned): ValueBox = s.top.edgeAssignedBoxes(ea)
private def edgeAssignedBoxOf(s: InterpreterStack, ea: EdgeAssigned): ValueBox = topMu.edgeAssignedBoxes(ea)
/** Get the edge-assigned value box of an edge-assigned instruction in the current stack. */
private def edgeAssignedBoxOf(ea: EdgeAssigned): ValueBox = edgeAssignedBoxOf(curStack, ea)
......@@ -103,15 +112,18 @@ class InterpreterThread(val id: Int, initialStack: InterpreterStack, val mutator
/** Make a string to identify the current function version, basic block and instruction for debugging. */
private def ctx = stack match {
case None => "(Thred not bound to stack): "
case Some(_) => {
val ix = top.curInstIndex
if (ix >= curBB.insts.size) {
"TID %d, FuncVer %s, BasicBlock %s, Instruction exceeds the basic block (error)".format(id, top.funcVer.repr, curBB.repr)
} else {
"TID %d, FuncVer %s, BasicBlock %s, Instruction %s (%s): ".format(id, top.funcVer.repr, curBB.repr, curInst.repr, curInst match {
case ci: InstCommInst => ci.inst.name.get
case _ => curInst.getClass.getSimpleName()
})
case Some(s) => s.top match {
case f: NativeFrame => "TID %d, Native frame for function 0x%x: ".format(id, f.func)
case f: MuFrame => {
val ix = f.curInstIndex
if (ix >= curBB.insts.size) {
"TID %d, FuncVer %s, BB %s, Inst(%d): index out of the basic block boundary (error): ".format(id, f.funcVer.repr, curBB.repr, ix)
} else {
"TID %d, FuncVer %s, BB %s, Inst(%d): %s (%s): ".format(id, f.funcVer.repr, curBB.repr, ix, curInst.repr, curInst match {
case ci: InstCommInst => ci.inst.name.get
case _ => curInst.getClass.getSimpleName()
})
}
}
}
}
......@@ -488,7 +500,7 @@ class InterpreterThread(val id: Int, initialStack: InterpreterStack, val mutator
val argBoxes = argList.map(boxOf)
curInstHalfExecuted = true
curStack.pushFrame(funcVer, argBoxes)
curStack.pushMuFrame(funcVer, argBoxes)
}
case i @ InstTailCall(sig, callee, argList) => {
......@@ -500,7 +512,7 @@ class InterpreterThread(val id: Int, initialStack: InterpreterStack, val mutator
val argBoxes = argList.map(boxOf)
curStack.replaceTop(funcVer, argBoxes)
curStack.replaceTopMuFrame(funcVer, argBoxes)
}
case i @ InstRet(retTy, retVal) => {
......@@ -774,18 +786,18 @@ class InterpreterThread(val id: Int, initialStack: InterpreterStack, val mutator
if (callConv != Flag("#DEFAULT")) {
throw new UvmRefImplException(ctx + "Currently only support the #DEFAULT callConv. %s found.".format(callConv.name))
}
val addr = boxOf(callee).asInstanceOf[BoxPointer].addr
val argBoxes = argList.map(boxOf)
val retBox = boxOf(i)
???
//
// microVM.threadStackManager.threadCallingNative = Some(this)
//
// microVM.nativeCallHelper.callNative(sig, addr, argBoxes, retBox)
//
// microVM.threadStackManager.threadCallingNative = Some(this)
//
// microVM.nativeCallHelper.callNative(sig, addr, argBoxes, retBox)
continueNormally()
}
......@@ -1195,20 +1207,24 @@ class InterpreterThread(val id: Int, initialStack: InterpreterStack, val mutator
*/
private def catchException(exc: Word): Unit = {
@tailrec
def unwindUntilCatchable(f: InterpreterFrame): (InterpreterFrame, BasicBlock) = {
maybeFindExceptionHandler(f.curInst) match {
def unwindUntilCatchable(frame: InterpreterFrame): (InterpreterFrame, BasicBlock) = frame match {
case f: MuFrame => maybeFindExceptionHandler(f.curInst) match {
case Some(bb) => (f, bb)
case None => f.prev match {
case None => throw new UvmRuntimeException(ctx + "Exception is thrown out of the bottom frame.")
case Some(prev) => unwindUntilCatchable(prev)
}
}
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.")
}
}
val s = curStack
val f = s.top
val (newFrame, newBB) = unwindUntilCatchable(f)
s.top = newFrame
s.unwindTo(newFrame)
branchAndMovePC(newBB, exc)
curInstHalfExecuted = false
......
......@@ -22,7 +22,10 @@ class InterpreterStack(val id: Int, val stackMemory: StackMemory, stackBottomFun
var state: StackState = StackState.Ready(InternalTypes.VOID) // Initial state is READY<void>
var top: InterpreterFrame = InterpreterFrame.frameForCall(stackBottomFunc, args, None)
private var _top: InterpreterFrame = InterpreterFrame.forMuFunc(stackBottomFunc, args, None)
def top = _top
private def top_=(f: InterpreterFrame) = _top = f
def frames: Iterator[InterpreterFrame] = new AbstractIterator[InterpreterFrame] {
var curFrame: Option[InterpreterFrame] = Some(top)
......@@ -33,19 +36,25 @@ class InterpreterStack(val id: Int, val stackMemory: StackMemory, stackBottomFun
res
}
}
def pushFrame(funcVer: FuncVer, args: Seq[ValueBox]): Unit = {
val newFrame = InterpreterFrame.frameForCall(funcVer, args, Some(top))
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
top.savedStackPointer = stackMemory.top
}
def replaceTop(funcVer: FuncVer, args: Seq[ValueBox]): Unit = {
val newFrame = InterpreterFrame.frameForCall(funcVer, args, top.prev)
def replaceTopMuFrame(funcVer: FuncVer, args: Seq[ValueBox]): Unit = {
val newFrame = InterpreterFrame.forMuFunc(funcVer, args, top.prev)
stackMemory.rewind(top.savedStackPointer)
top = newFrame
top.savedStackPointer = stackMemory.top
}
def pushNativeFrame(func: Word): Unit = {
val newFrame = InterpreterFrame.forNativeFunc(func, Some(top))
}
def popFrame(): Unit = {
stackMemory.rewind(top.savedStackPointer)
......@@ -53,9 +62,41 @@ 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))
}
}
def unwindTo(f: InterpreterFrame): Unit = {
top = f
stackMemory.rewind(top.savedStackPointer)
}
}
class InterpreterFrame(val funcVer: FuncVer, val prev: Option[InterpreterFrame]) {
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.
*/
var savedStackPointer: Word = 0
}
object InterpreterFrame {
def forMuFunc(funcVer: FuncVer, args: Seq[ValueBox], prev: Option[InterpreterFrame]): MuFrame = {
val frm = new MuFrame(funcVer, prev) // Bottom frame
for ((p, a) <- (funcVer.params zip args)) {
frm.boxes(p).copyFrom(a)
}
frm
}
def forNativeFunc(func: Word, prev: Option[InterpreterFrame]): NativeFrame = {
val frm = new NativeFrame(func, prev)
frm
}
}
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 */
......@@ -80,18 +121,15 @@ class InterpreterFrame(val funcVer: FuncVer, val prev: Option[InterpreterFrame])
* <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
* InterpreterThread.finishHalfExecutedInst or InterpreterThread.catchException. Particularly, the RET, RETVOID,
* THROW, TRAP, WATCHPOINT and SWAPSTACK instruction will call those two functions.
*/
var curInstHalfExecuted: Boolean = false
/**
* 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.
*/
var savedStackPointer: Word = 0
makeBoxes()
private def makeBoxes() {
......@@ -144,14 +182,5 @@ class InterpreterFrame(val funcVer: FuncVer, val prev: Option[InterpreterFrame])
}
}
object InterpreterFrame {
def frameForCall(funcVer: FuncVer, args: Seq[ValueBox], prev: Option[InterpreterFrame]): InterpreterFrame = {
val frm = new InterpreterFrame(funcVer, prev) // Bottom frame
for ((p, a) <- (funcVer.params zip args)) {
frm.boxes(p).copyFrom(a)
}
frm
}
}
\ No newline at end of file
class NativeFrame(val func: Word, prev: Option[InterpreterFrame]) extends InterpreterFrame(prev) {
}
......@@ -79,7 +79,7 @@ class AllScanner(val handler: RefFieldHandler)(
private def traceStack(sta: InterpreterStack) {
logger.debug(s"Tracing stack ${sta.id} for registers...")
for (fra <- sta.frames; vb <- fra.boxes.values) vb match {
for (fra <- sta.muFrames; vb <- fra.boxes.values) vb match {
case hor: HasObjRef => this.boxToHeap(hor)
case bst: BoxStack => this.boxToStack(bst)
case _ =>
......
......@@ -1313,7 +1313,7 @@ class UvmInterpreterSpec extends UvmBundleTesterBase {
case "@corostackfunc_v1.trap_coro1" => {
val Seq(fromSta, p) = ca.dumpKeepalives(st, 0)
fromSta.vb.asStack.get.top.funcVer shouldBe microVM.globalBundle.funcVerNs("@testswapstack_v1")
fromSta.vb.asStack.get.top.asInstanceOf[MuFrame].funcVer shouldBe microVM.globalBundle.funcVerNs("@testswapstack_v1")
p.vb.asSInt(64) shouldBe 2L
coro1Reached = true
......
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