Commit d67a307a authored by Kunshan Wang's avatar Kunshan Wang

WIP: Re-designed the stack-frame hierarchy.

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