GitLab will be upgraded to the 12.10.14-ce.0 on 28 Sept 2020 at 2.00pm (AEDT) to 2.30pm (AEDT). During the update, GitLab and Mattermost services will not be available. If you have any concerns with this, please talk to us at N110 (b) CSIT building.

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