Commit 9da89df9 authored by Kunshan Wang's avatar Kunshan Wang

Implemented SWAPSTACK

parent 0702c852
......@@ -5,6 +5,7 @@ import uvm.types._
import uvm.ssavariables._
import uvm.refimpl._
import uvm.refimpl.mem._
import uvm.refimpl.mem.TypeSizes.Word
import scala.collection.mutable.HashMap
import scala.collection.AbstractIterator
......@@ -34,14 +35,18 @@ class InterpreterStack(val id: Int, val stackMemory: StackMemory, stackBottomFun
def pushFrame(funcVer: FuncVer, args: Seq[ValueBox]): Unit = {
val newFrame = InterpreterFrame.frameForCall(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)
stackMemory.rewind(top.savedStackPointer)
top = newFrame
top.savedStackPointer = stackMemory.top
}
def popFrame(): Unit = {
stackMemory.rewind(top.savedStackPointer)
top = top.prev.getOrElse {
throw new UvmRuntimeException("Attemting to pop the last frame of a stack. Stack ID: %d.".format(id))
}
......@@ -51,11 +56,36 @@ class InterpreterStack(val id: Int, val stackMemory: StackMemory, stackBottomFun
class InterpreterFrame(val funcVer: FuncVer, val prev: Option[InterpreterFrame]) {
val boxes = new HashMap[LocalVariable, ValueBox]()
/** Current basic block */
var curBB: BasicBlock = funcVer.entry
/** Current instruction index within the current basic block */
var curInstIndex: Int = 0
var savedStackPointer: Long = 0
/**
* curInstHalfExecuted is true if the current instruction is partially executed and may continue when resumed.
* <p>
* 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 --
* 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).
* <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>
* 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()
......@@ -77,8 +107,13 @@ class InterpreterFrame(val funcVer: FuncVer, val prev: Option[InterpreterFrame])
curBB.insts(curInstIndex)
} catch {
case e: IndexOutOfBoundsException =>
throw new UvmRefImplException(("Current instruction beyond the last instruction of a basic block. " +
"FuncVer: %s, BasicBlock: %s").format(funcVer.repr, curBB.repr))
if (curInstIndex == -1) {
throw new UvmRefImplException(("The current stack has never been bound to a thread." +
"FuncVer: %s, BasicBlock: %s").format(funcVer.repr, curBB.repr))
} else {
throw new UvmRefImplException(("Current instruction %d beyond the last instruction of a basic block. " +
"FuncVer: %s, BasicBlock: %s").format(curInstIndex, funcVer.repr, curBB.repr))
}
}
def incPC() {
......
......@@ -964,11 +964,11 @@ class UvmInterpreterSpec extends UvmBundleTesterBase {
"WATCHPOINT" should "do nothing when disabled" in {
val ca = microVM.newClientAgent()
ca.disableWatchPoint(1)
val func = ca.putFunction("@wptest")
testFunc(ca, func, Seq()) { (ca, th, st, wp) =>
nameOf(ca.currentInstruction(st, 0)) match {
case "@wptest_v1.trap_dis" => {
......@@ -983,7 +983,7 @@ class UvmInterpreterSpec extends UvmBundleTesterBase {
"WATCHPOINT" should "work with all supported destinations when enabled" in {
val ca = microVM.newClientAgent()
ca.enableWatchPoint(1)
val exc = ca.newFixed("@i32")
......@@ -1021,10 +1021,10 @@ class UvmInterpreterSpec extends UvmBundleTesterBase {
ca.close()
}
"TRAP and WATCHPOINT" should "throw exceptions out of function when no exceptional dest" in {
val ca = microVM.newClientAgent()
ca.enableWatchPoint(2)
val exc1 = ca.newFixed("@void")
......@@ -1046,7 +1046,7 @@ class UvmInterpreterSpec extends UvmBundleTesterBase {
lp1.vb.asRef shouldBe exc1.vb.asRef
lp2.vb.asRef shouldBe exc2.vb.asRef
TrapRebindPassVoid(st)
}
case n => fail("Unexpected trap " + n)
......@@ -1055,6 +1055,63 @@ class UvmInterpreterSpec extends UvmBundleTesterBase {
ca.close()
}
"SWAPSTAK" should "work" in {
val ca = microVM.newClientAgent()
val func = ca.putFunction("@testswapstack")
var coro1Reached = false
var coro2Reached = false
var main1Reached = false
var main2Reached = false
testFunc(ca, func, Seq()) { (ca, th, st, wp) =>
nameOf(ca.currentInstruction(st, 0)) match {
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")
p.vb.asSInt(64) shouldBe 2L
coro1Reached = true
TrapRebindPassVoid(st)
}
case "@corostackfunc_v1.trap_coro2" => {
val Seq(css1) = ca.dumpKeepalives(st, 0)
css1.vb.asDouble shouldBe 3.0d
coro2Reached = true
TrapRebindPassVoid(st)
}
case "@testswapstack_v1.trap_main1" => {
val Seq(mss1) = ca.dumpKeepalives(st, 0)
mss1.vb.asSInt(64) shouldBe 3L
main1Reached = true
TrapRebindPassVoid(st)
}
case "@testswapstack_v1.trap_main2" => {
val Seq(excVal) = ca.dumpKeepalives(st, 0)
excVal.vb.asSInt(64) shouldBe 7L
main2Reached = true
TrapRebindPassVoid(st)
}
}
}
coro1Reached shouldBe true
coro2Reached shouldBe true
main1Reached shouldBe true
main2Reached shouldBe true
fail()
ca.close()
}
}
\ No newline at end of file
......@@ -747,57 +747,53 @@
COMMINST @uvm.thread_exit
}
//
// .funcsig @watchpointtest_sig = @noparamsnoret
// .funcdef @watchpointtest VERSION @watchpointtest_v1 <@watchpointtest_sig> () {
// %entry:
// BRANCH %head
//
// %head:
// %wp = WATCHPOINT 42 <@i64> %dis %ena %enaexc KEEPALIVE ()
//
// %dis:
// %trapdis = TRAP <@void> %exit %exit KEEPALIVE ()
//
// %ena:
// %trapena = TRAP <@void> %exit %exit KEEPALIVE (%wp)
//
// %enaexc:
// %exc = LANDINGPAD
// %trapena = TRAP <@void> %exit %exit KEEPALIVE (%exc)
//
// %exit:
// COMMINST @uvm.thread_exit
// }
//
// .funcsig @testswapstack_sig = @noparamsnoret
// .funcdef @testswapstack VERSION @testswapstack_v1 <@testswapstack_sig> () {
// %entry:
// %valueret = ALLOCA <@i64>
// %shouldstop = ALLOCA <@i64> // initially zeroed
// %thisstack = COMMINST @uvm.current_stack ()
//
// %iter = NEWSTACK <@itersig> @rangeiter (3 %thisstack %valueret %shouldstop)
// COMMINST @uvm.swap_stack (%iter)
// BRANCH %head
//
// %head:
// %value = LOAD <@i64> %valueret
// %stop = LOAD <@i64> %shouldstop
// TRAP <@void> %head2 %head2 KEEPALIVE (%value %stop)
//
// %head2:
// %eq = EQ <@i64> %stop 0
// BRANCH2 %eq %body %exit
//
// %body:
// COMMINST @uvm.swap_stack (%iter)
// BRANCH %head
//
// %exit:
// COMMINST @uvm.thread_exit
// }
//
.funcsig @corostackfunc_sig = @void (@stack @i64)
.funcdef @corostackfunc VERSION @corostackfunc_v1 <@corostackfunc_sig> (%fromsta %p) {
%entry:
%trap_coro1 = TRAP <@void> KEEPALIVE(%fromsta %p)
%pp1 = ADD <@i64> %p @I64_1
%css1 = SWAPSTACK %fromsta RET_WITH <@double> PASS_VALUE <@i64> %pp1
%trap_coro2 = TRAP <@void> KEEPALIVE(%css1)
%css2 = SWAPSTACK %fromsta RET_WITH <@void> PASS_VOID KEEPALIVE(%css1)
%excref = NEW <@i64>
%exciref = GETIREF <@i64> %excref
STORE <@i64> %exciref @I64_7 // Does not need to be atomic. It is single threaded.
%css3 = SWAPSTACK %fromsta KILL_OLD THROW_EXC %excref
}
.funcdef @testswapstack VERSION @testswapstack_v1 <@noparamsnoret> () {
%entry:
%thisstack = COMMINST @uvm.current_stack ()
%coro = NEWSTACK <@corostackfunc_sig> @corostackfunc (%thisstack @I64_2)
%mss1 = SWAPSTACK %coro RET_WITH <@i64> PASS_VOID
%trap_main1 = TRAP <@void> KEEPALIVE(%mss1)
%mss2 = SWAPSTACK %coro RET_WITH <@void> PASS_VALUE <@double> @D_3
%mss3 = SWAPSTACK %coro RET_WITH <@void> PASS_VOID EXC(%unreachable %exit)
%exit:
%exc = LANDINGPAD
%excref = REFCAST <@refvoid @refi64> %exc
%exciref = GETIREF <@i64> %excref
%excval = LOAD <@i64> %exciref
%trap_main2 = TRAP <@void> KEEPALIVE(%excval)
COMMINST @uvm.thread_exit
%unreachable:
%trap_unreachable = TRAP <@void>
COMMINST @uvm.thread_exit
}
// .typedef @irefi64 = iref<@i64>
//
// .typedef @irefstack = iref<@stack>
......
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