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 ...@@ -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