Commit ebe76f0d authored by Kunshan Wang's avatar Kunshan Wang

Migrated "futex" and "simple" tests.

parent 566eec26
FN=$1
sed -i 's/newClientAgent/newContext/g' $FN
sed -i 's/\bca\b/ctx/g' $FN
sed -i 's/ctx\.close()/ctx.closeContext()/g' $FN
sed -i 's/putInt("@i32",/i32(/g' $FN
sed -i 's/putInt("@i64",/i64(/g' $FN
sed -i 's/putConstant/handleFromConst/g' $FN
sed -i 's/putGlobal/handleFromGlobal/g' $FN
sed -i 's/putFunction/handleFromFunc/g' $FN
sed -i 's/toInt(\(\w\+\),\s*signExt\s*=\s*true)/handleToSInt(\1.asInstanceOf[MuIntValue])/g' $FN
sed -i 's/currentInstruction/curInst/g' $FN
sed -i 's/TrapRebindPassVoid/returnFromTrap/g' $FN
sed -i 's/TrapRebindPassValue(\(\w\+\),\s*\(\w\+\)\s*)/Rebind(\1, PassValues(Seq(\2)))/g' $FN
FN=$1
sed -i 's/\(%\w\+\)\s*=\s*TRAP/[\1] TRAP/g' $FN
sed -i 's/NEWSTACK\s*<\(@\w\+\)>\s*\(@\w\+\)/COMMINST @uvm.new_stack <[\1]> (\2)/g' $FN
sed -i 's/COMMINST\s*@uvm\.new_thread\s*(\([@%]\w\+\))/NEWTHREAD \1 PASS_VALUES /g' $FN
sed -i 's/TRAP\s*<@void>/TRAP <>/g' $FN
sed -i 's/noparamsnoret/v_v/g' $FN
sed -i 's/@void\s*(\([^)]*\))/(\1) -> ()/g' $FN
...@@ -694,16 +694,21 @@ trait InstructionExecutor extends InterpreterActions with CommInstExecutor { ...@@ -694,16 +694,21 @@ trait InstructionExecutor extends InterpreterActions with CommInstExecutor {
val newStack = boxOf(stack).asInstanceOf[BoxStack].stack.getOrElse { val newStack = boxOf(stack).asInstanceOf[BoxStack].stack.getOrElse {
throw new UvmRuntimeException(ctx + "Attempt to bind to a NULL stack.") throw new UvmRuntimeException(ctx + "Attempt to bind to a NULL stack.")
} }
newStackAction match {
val newThread = newStackAction match {
case PassValues(argTys, args) => { case PassValues(argTys, args) => {
val argBoxes = args.map(boxOf) val argBoxes = args.map(boxOf)
rebindPassValues(newStack, argBoxes) microVM.threadStackManager.newThread(newStack, HowToResume.PassValues(argBoxes))
} }
case ThrowExc(exc) => { case ThrowExc(exc) => {
val excBox = boxOf(exc) val excBox = boxOf(exc)
rebindThrowExc(newStack, excBox) val excAddr = excBox.asInstanceOf[BoxRef].objRef
microVM.threadStackManager.newThread(newStack, HowToResume.ThrowExc(excAddr))
} }
} }
resultBox(0).asInstanceOf[BoxThread].thread = Some(newThread)
continueNormally()
} }
case i @ InstSwapStack(swappee, curStackAction, newStackAction, excClause, keepAlives) => { case i @ InstSwapStack(swappee, curStackAction, newStackAction, excClause, keepAlives) => {
......
...@@ -144,7 +144,7 @@ trait InterpreterActions extends InterpreterThreadState { ...@@ -144,7 +144,7 @@ trait InterpreterActions extends InterpreterThreadState {
/** Write the return value of futex. May be written from FutexManager */ /** Write the return value of futex. May be written from FutexManager */
def futexReturn(rv: Int): Unit = { def futexReturn(rv: Int): Unit = {
assert(curInst.isInstanceOf[InstCommInst]) assert(curInst.isInstanceOf[InstCommInst])
assert(Seq("@uvm.futex.wait", "@uvm.futex.wait_timeout") contains assert(Seq("@uvm.futex.wait", "@uvm.futex.wait_timeout", "@uvm.futex.wake", "@uvm.futex.cmp_requeue") contains
curInst.asInstanceOf[InstCommInst].inst.name.get) curInst.asInstanceOf[InstCommInst].inst.name.get)
logger.debug(ctx + "Setting futex return value") logger.debug(ctx + "Setting futex return value")
......
...@@ -229,6 +229,9 @@ class InterpreterStack(val id: Int, val stackMemory: StackMemory, stackBottomFun ...@@ -229,6 +229,9 @@ class InterpreterStack(val id: Int, val stackMemory: StackMemory, stackBottomFun
* @return true if the interpreter should increment the PC by continueNormally(). * @return true if the interpreter should increment the PC by continueNormally().
*/ */
def rebindPassValues(args: Seq[ValueBox]): Boolean = { def rebindPassValues(args: Seq[ValueBox]): Boolean = {
if(!state.isInstanceOf[FrameState.Ready]) {
throw new UvmRuntimeException("Attempt to bind to a stack not in the ready state. Actual state: %s".format(state))
}
top match { top match {
case mf: MuFrame => { case mf: MuFrame => {
mf.resumeNormally(args) mf.resumeNormally(args)
......
...@@ -14,6 +14,8 @@ import com.typesafe.scalalogging.Logger ...@@ -14,6 +14,8 @@ import com.typesafe.scalalogging.Logger
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import ch.qos.logback.classic.{ Logger => LLogger } import ch.qos.logback.classic.{ Logger => LLogger }
import ch.qos.logback.classic.Level import ch.qos.logback.classic.Level
import uvm.refimpl.TrapHandlerResult.Rebind
import uvm.refimpl.HowToResume.PassValues
object UvmBundleTesterBase { object UvmBundleTesterBase {
val logger = Logger(LoggerFactory.getLogger(getClass.getName)) val logger = Logger(LoggerFactory.getLogger(getClass.getName))
...@@ -98,4 +100,6 @@ abstract class UvmBundleTesterBase extends FlatSpec with Matchers { ...@@ -98,4 +100,6 @@ abstract class UvmBundleTesterBase extends FlatSpec with Matchers {
def i64(num: BigInt) = ctx.handleFromInt(num, 64) def i64(num: BigInt) = ctx.handleFromInt(num, 64)
def func(id: Int) = ctx.handleFromFunc(id) def func(id: Int) = ctx.handleFromFunc(id)
} }
def returnFromTrap(st: MuStackRefValue) = Rebind(st, PassValues(Seq()))
} }
\ No newline at end of file
...@@ -12,6 +12,8 @@ import AtomicRMWOptr._ ...@@ -12,6 +12,8 @@ import AtomicRMWOptr._
import uvm.refimpl.mem.TypeSizes.Word import uvm.refimpl.mem.TypeSizes.Word
import ch.qos.logback.classic.Level._ import ch.qos.logback.classic.Level._
import uvm.refimpl.UvmBundleTesterBase import uvm.refimpl.UvmBundleTesterBase
import uvm.refimpl.TrapHandlerResult.{ ThreadExit, Rebind }
import uvm.refimpl.HowToResume.{ PassValues, ThrowExc }
class UvmInterpreterFutexTests extends UvmBundleTesterBase { class UvmInterpreterFutexTests extends UvmBundleTesterBase {
setLogLevels( setLogLevels(
...@@ -22,150 +24,150 @@ class UvmInterpreterFutexTests extends UvmBundleTesterBase { ...@@ -22,150 +24,150 @@ class UvmInterpreterFutexTests extends UvmBundleTesterBase {
"tests/uvm-refimpl-test/futex-tests.uir") "tests/uvm-refimpl-test/futex-tests.uir")
"Futex" should "wake up the waiting thread" in { "Futex" should "wake up the waiting thread" in {
val ca = microVM.newClientAgent() val ctx = microVM.newContext()
val func = ca.putFunction("@futex_setter") val func = ctx.handleFromFunc("@futex_setter")
var trapWaiterReached = false var trapWaiterReached = false
var trapSetterReached = false var trapSetterReached = false
testFunc(ca, func, Seq()) { (ca, th, st, wp) => testFunc(ctx, func, Seq()) { (ctx, th, st, wp) =>
val trapName = nameOf(ca.currentInstruction(st, 0)) val trapName = nameOf(ctx.curInst(st, 0))
trapName match { trapName match {
case "@futex_waiter_v1.entry.trap_waiter" => { case "@futex_waiter_v1.entry.trap_waiter" => {
val Seq(rv, sv) = ca.dumpKeepalives(st, 0) val Seq(rv, sv) = ctx.dumpKeepalives(st, 0)
ca.toInt(rv, signExt = true) shouldBe 0 ctx.handleToSInt(rv.asInstanceOf[MuIntValue]) shouldBe 0
ca.toInt(sv, signExt = true) shouldBe 42 ctx.handleToSInt(sv.asInstanceOf[MuIntValue]) shouldBe 42
trapWaiterReached = true trapWaiterReached = true
TrapRebindPassVoid(st) returnFromTrap(st)
} }
case "@futex_setter_v1.wait_exit.trap_setter" => { case "@futex_setter_v1.wait_exit.trap_setter" => {
trapSetterReached = true trapSetterReached = true
TrapRebindPassVoid(st) returnFromTrap(st)
} }
case _ => fail("Should not hit " + trapName) case _ => fail("Should not hit " + trapName)
} }
} }
ca.close() ctx.closeContext()
trapWaiterReached shouldBe true trapWaiterReached shouldBe true
trapSetterReached shouldBe true trapSetterReached shouldBe true
} }
"Futex" should "wake up if not explicitly woken within the give timeout" in { "Futex" should "wake up if not explicitly woken within the give timeout" in {
val ca = microVM.newClientAgent() val ctx = microVM.newContext()
val func = ca.putFunction("@futex_delayer") val func = ctx.handleFromFunc("@futex_delayer")
var trapDelayerReached = false var trapDelayerReached = false
testFunc(ca, func, Seq()) { (ca, th, st, wp) => testFunc(ctx, func, Seq()) { (ctx, th, st, wp) =>
val trapName = nameOf(ca.currentInstruction(st, 0)) val trapName = nameOf(ctx.curInst(st, 0))
trapName match { trapName match {
case "@futex_delayer_v1.entry.trap_delayer" => { case "@futex_delayer_v1.entry.trap_delayer" => {
val Seq(rv) = ca.dumpKeepalives(st, 0) val Seq(rv) = ctx.dumpKeepalives(st, 0)
ca.toInt(rv, signExt = true) shouldBe -3 ctx.handleToSInt(rv.asInstanceOf[MuIntValue]) shouldBe -3
trapDelayerReached = true trapDelayerReached = true
TrapRebindPassVoid(st) returnFromTrap(st)
} }
case _ => fail("Should not hit " + trapName) case _ => fail("Should not hit " + trapName)
} }
} }
ca.close() ctx.closeContext()
trapDelayerReached shouldBe true trapDelayerReached shouldBe true
} }
"Futex" should "not sleep if the memory location does not hold the expected value" in { "Futex" should "not sleep if the memory location does not hold the expected value" in {
val ca = microVM.newClientAgent() val ctx = microVM.newContext()
val func = ca.putFunction("@futex_no_sleep") val func = ctx.handleFromFunc("@futex_no_sleep")
var trapNoSleepReached = false var trapNoSleepReached = false
testFunc(ca, func, Seq()) { (ca, th, st, wp) => testFunc(ctx, func, Seq()) { (ctx, th, st, wp) =>
val trapName = nameOf(ca.currentInstruction(st, 0)) val trapName = nameOf(ctx.curInst(st, 0))
trapName match { trapName match {
case "@futex_no_sleep_v1.entry.trap_no_sleep" => { case "@futex_no_sleep_v1.entry.trap_no_sleep" => {
val Seq(rv) = ca.dumpKeepalives(st, 0) val Seq(rv) = ctx.dumpKeepalives(st, 0)
ca.toInt(rv, signExt = true) shouldBe -1 ctx.handleToSInt(rv.asInstanceOf[MuIntValue]) shouldBe -1
trapNoSleepReached = true trapNoSleepReached = true
TrapRebindPassVoid(st) returnFromTrap(st)
} }
case _ => fail("Should not hit " + trapName) case _ => fail("Should not hit " + trapName)
} }
} }
ca.close() ctx.closeContext()
trapNoSleepReached shouldBe true trapNoSleepReached shouldBe true
} }
"Futex" should "wake up requeued threads" in { "Futex" should "wake up requeued threads" in {
val ca = microVM.newClientAgent() val ctx = microVM.newContext()
val func = ca.putFunction("@futex_requeue_test") val func = ctx.handleFromFunc("@futex_requeue_test")
var mainExit = false var mainExit = false
var subExit = 0 var subExit = 0
testFunc(ca, func, Seq()) { (ca, th, st, wp) => testFunc(ctx, func, Seq()) { (ctx, th, st, wp) =>
val trapName = nameOf(ca.currentInstruction(st, 0)) val trapName = nameOf(ctx.curInst(st, 0))
trapName match { trapName match {
case "@futex_requeue_waiter_v1.entry.trap_requeue_waiter" => { case "@futex_requeue_waiter_v1.entry.trap_requeue_waiter" => {
val Seq(rv) = ca.dumpKeepalives(st, 0) val Seq(rv) = ctx.dumpKeepalives(st, 0)
ca.toInt(rv, signExt = true) shouldBe 0 ctx.handleToSInt(rv.asInstanceOf[MuIntValue]) shouldBe 0
subExit += 1 subExit += 1
TrapRebindPassVoid(st) returnFromTrap(st)
} }
case "@futex_requeue_test_v1.wait_body.trap_wait" => { case "@futex_requeue_test_v1.wait_body.trap_wait" => {
val Seq(nt, nt2) = ca.dumpKeepalives(st, 0) val Seq(nt, nt2) = ctx.dumpKeepalives(st, 0)
val nthr = nt.vb.asThread.get val nthr = nt.vb.asThread.get
val nthr2 = nt2.vb.asThread.get val nthr2 = nt2.vb.asThread.get
if (nthr.isFutexWaiting && nthr2.isFutexWaiting) { if (nthr.isFutexWaiting && nthr2.isFutexWaiting) {
val one = ca.putInt("@i32", 1) val one = ctx.i32(1)
TrapRebindPassValue(st, one) Rebind(st, PassValues(Seq(one)))
} else { } else {
val zero = ca.putInt("@i32", 0) val zero = ctx.i32(0)
TrapRebindPassValue(st, zero) Rebind(st, PassValues(Seq(zero)))
} }
} }
case "@futex_requeue_test_v1.wait_exit.trap_setter" => { case "@futex_requeue_test_v1.wait_exit.trap_setter" => {
val Seq(nwakes, nwakes2) = ca.dumpKeepalives(st, 0) val Seq(nwakes, nwakes2) = ctx.dumpKeepalives(st, 0)
ca.toInt(nwakes, signExt = true) shouldBe 1 ctx.handleToSInt(nwakes.asInstanceOf[MuIntValue]) shouldBe 1
ca.toInt(nwakes2, signExt = true) shouldBe 1 ctx.handleToSInt(nwakes2.asInstanceOf[MuIntValue]) shouldBe 1
mainExit = true mainExit = true
TrapRebindPassVoid(st) returnFromTrap(st)
} }
case _ => fail("Should not hit " + trapName) case _ => fail("Should not hit " + trapName)
} }
} }
ca.close() ctx.closeContext()
mainExit shouldBe true mainExit shouldBe true
subExit shouldBe 2 subExit shouldBe 2
...@@ -185,60 +187,60 @@ class UvmInterpreterFutexTests extends UvmBundleTesterBase { ...@@ -185,60 +187,60 @@ class UvmInterpreterFutexTests extends UvmBundleTesterBase {
} }
"Futex" should "work across GC" in { "Futex" should "work across GC" in {
val ca = microVM.newClientAgent() val ctx = microVM.newContext()
val func = ca.putFunction("@futex_with_gc") val func = ctx.handleFromFunc("@futex_with_gc")
var mainExit = false var mainExit = false
var subExit = false var subExit = false
verboseGC { verboseGC {
testFunc(ca, func, Seq()) { (ca, th, st, wp) => testFunc(ctx, func, Seq()) { (ctx, th, st, wp) =>
val trapName = nameOf(ca.currentInstruction(st, 0)) val trapName = nameOf(ctx.curInst(st, 0))
trapName match { trapName match {
case "@futex_gc_waiter_v1.entry.trap_gc_waiter" => { case "@futex_gc_waiter_v1.entry.trap_gc_waiter" => {
val Seq(rv) = ca.dumpKeepalives(st, 0) val Seq(rv) = ctx.dumpKeepalives(st, 0)
ca.toInt(rv, signExt = true) shouldBe 0 ctx.handleToSInt(rv.asInstanceOf[MuIntValue]) shouldBe 0
subExit = true subExit = true
TrapRebindPassVoid(st) returnFromTrap(st)
} }
case "@futex_with_gc_v1.wait_body.trap_wait" => { case "@futex_with_gc_v1.wait_body.trap_wait" => {
val Seq(nt) = ca.dumpKeepalives(st, 0) val Seq(nt) = ctx.dumpKeepalives(st, 0)
val nthr = nt.vb.asThread.get val nthr = nt.vb.asThread.get
if (nthr.isFutexWaiting) { if (nthr.isFutexWaiting) {
val one = ca.putInt("@i32", 1) val one = ctx.i32(1)
TrapRebindPassValue(st, one) Rebind(st, PassValues(Seq(one)))
} else { } else {
val zero = ca.putInt("@i32", 0) val zero = ctx.i32(0)
TrapRebindPassValue(st, zero) Rebind(st, PassValues(Seq(zero)))
} }
} }
case "@futex_with_gc_v1.wait_exit.trap_gc" => { case "@futex_with_gc_v1.wait_exit.trap_gc" => {
gc() gc()
TrapRebindPassVoid(st) returnFromTrap(st)
} }
case "@futex_with_gc_v1.wait_exit.trap_exit" => { case "@futex_with_gc_v1.wait_exit.trap_exit" => {
val Seq(nwakes) = ca.dumpKeepalives(st, 0) val Seq(nwakes) = ctx.dumpKeepalives(st, 0)
ca.toInt(nwakes, signExt = true) shouldBe 1 ctx.handleToSInt(nwakes.asInstanceOf[MuIntValue]) shouldBe 1
mainExit = true mainExit = true
TrapRebindPassVoid(st) returnFromTrap(st)
} }
case _ => fail("Should not hit " + trapName) case _ => fail("Should not hit " + trapName)
} }
} }
ca.close() ctx.closeContext()
} }
mainExit shouldBe true mainExit shouldBe true
......
...@@ -22,110 +22,110 @@ class UvmInterpreterSimpleTests extends UvmBundleTesterBase { ...@@ -22,110 +22,110 @@ class UvmInterpreterSimpleTests extends UvmBundleTesterBase {
"tests/uvm-refimpl-test/simple-tests.uir") "tests/uvm-refimpl-test/simple-tests.uir")
"Factorial functions" should "work" in { "Factorial functions" should "work" in {
val ca = microVM.newClientAgent() val ctx = microVM.newContext()
val func = ca.putFunction("@test_fac") val func = ctx.handleFromFunc("@test_fac")
testFunc(ca, func, Seq()) { (ca, th, st, wp) => testFunc(ctx, func, Seq()) { (ctx, th, st, wp) =>
val Seq(r1, r2, r3) = ca.dumpKeepalives(st, 0) val Seq(r1, r2, r3) = ctx.dumpKeepalives(st, 0)
r1.vb.asInt shouldEqual 3628800 r1.vb.asInt shouldEqual 3628800
r2.vb.asInt shouldEqual 3628800 r2.vb.asInt shouldEqual 3628800
r3.vb.asInt shouldEqual 3628800 r3.vb.asInt shouldEqual 3628800
TrapRebindPassVoid(st) returnFromTrap(st)
} }
ca.close() ctx.closeContext()
} }
"Fibonacci functions" should "work" in { "Fibonacci functions" should "work" in {
val ca = microVM.newClientAgent() val ctx = microVM.newContext()
val func = ca.putFunction("@test_fib") val func = ctx.handleFromFunc("@test_fib")
val watch = true val watch = true
testFunc(ca, func, Seq()) { (ca, th, st, wp) => testFunc(ctx, func, Seq()) { (ctx, th, st, wp) =>
val trapName = nameOf(ca.currentInstruction(st, 0)) val trapName = nameOf(ctx.curInst(st, 0))
trapName match { trapName match {
case "@fibonacci_mat_v1.head.watch" => { case "@fibonacci_mat_v1.head.watch" => {
if (watch) { if (watch) {
val vhs = ca.dumpKeepalives(st, 0) val vhs = ctx.dumpKeepalives(st, 0)
val vs = vhs.map(_.vb.asInt) val vs = vhs.map(_.vb.asInt)
println("watch " + vs) println("watch " + vs)
} }
TrapRebindPassVoid(st) returnFromTrap(st)
} }
case "@test_fib_v1.entry.checktrap" => { case "@test_fib_v1.entry.checktrap" => {
val Seq(r1, r2) = ca.dumpKeepalives(st, 0) val Seq(r1, r2) = ctx.dumpKeepalives(st, 0)
r1.vb.asInt shouldEqual 55 r1.vb.asInt shouldEqual 55
r2.vb.asInt shouldEqual 55 r2.vb.asInt shouldEqual 55
TrapRebindPassVoid(st) returnFromTrap(st)
} }
case _ => fail("Should not hit " + trapName) case _ => fail("Should not hit " + trapName)
} }
} }
ca.close() ctx.closeContext()
} }
"Coroutine test" should "work" in { "Coroutine test" should "work" in {
val ca = microVM.newClientAgent() val ctx = microVM.newContext()
val func = ca.putFunction("@test_coroutine") val func = ctx.handleFromFunc("@test_coroutine")
testFunc(ca, func, Seq()) { (ca, th, st, wp) => testFunc(ctx, func, Seq()) { (ctx, th, st, wp) =>
val trapName = nameOf(ca.currentInstruction(st, 0)) val trapName = nameOf(ctx.curInst(st, 0))
trapName match { trapName match {
case "@test_coroutine_v1.body.trap_body" => { case "@test_coroutine_v1.body.trap_body" => {
val Seq(v) = ca.dumpKeepalives(st, 0) val Seq(v) = ctx.dumpKeepalives(st, 0)
println(v.vb.asSInt(64)) println(v.vb.asSInt(64))
TrapRebindPassVoid(st) returnFromTrap(st)
} }
case "@test_coroutine_v1.exit.trap_exit" => { case "@test_coroutine_v1.exit.trap_exit" => {
val Seq(exc) = ca.dumpKeepalives(st, 0) val Seq(exc) = ctx.dumpKeepalives(st, 0)
val hsi = ca.putGlobal("@StopIteration") val hsi = ctx.handleFromGlobal("@StopIteration")
val hrsi = ca.load(MemoryOrder.NOT_ATOMIC, hsi) val hrsi = ctx.load(MemoryOrder.NOT_ATOMIC, hsi)
exc.vb.asRef shouldEqual hrsi.vb.asRef exc.vb.asRef shouldEqual hrsi.vb.asRef
TrapRebindPassVoid(st) returnFromTrap(st)
} }
case _ => fail("Should not hit " + trapName) case _ => fail("Should not hit " + trapName)
} }
} }
ca.close() ctx.closeContext()
} }
"Multi-threading test" should "work" in { "Multi-threading test" should "work" in {
val ca = microVM.newClientAgent() val ctx = microVM.newContext()
val func = ca.putFunction("@test_multithreading") val func = ctx.handleFromFunc("@test_multithreading")