Commit 6b3ecf77 authored by Kunshan Wang's avatar Kunshan Wang

Tested undefined funcs and func redef.

parent 5d383563
......@@ -120,8 +120,8 @@ trait InterpreterThreadState {
boxOf(curInst.results(index))
} catch {
case e: IndexOutOfBoundsException => throw new UvmRefImplException(
"Instruction %s is declared to have only %d results. Result %d is requested.".format(
curInst, curInst.results.length, index), e)
"Instruction %s is declared to have only %d results. Result %d is requested.".format(
curInst, curInst.results.length, index), e)
}
}
......@@ -163,7 +163,35 @@ trait InterpreterActions extends InterpreterThreadState {
if (!isRunning) throw new UvmRefImplException(ctx + "Attempt to run thread after it has reached exit.")
if (isFutexWaiting) throw new UvmRefImplException(ctx + "Attempt to run thread when it is waiting on a futex.")
topMu.justCreated = false
interpretCurrentInstruction()
topMu match {
case f: DefinedMuFrame => interpretCurrentInstruction()
case f: UndefinedMuFrame => executeUndefinedMuFrame()
}
}
/** Execute an undefined Mu frame. This will call the trap handler, and tail-call the same function. */
def executeUndefinedMuFrame(): Unit = {
val f = top.asInstanceOf[UndefinedMuFrame]
assert(f.virtInst != UndefinedMuFrame.VIRT_INST_NOT_STARTED)
f.virtInst match {
case UndefinedMuFrame.VIRT_INST_TRAP => {
logger.debug(ctx+"Executing virtual trap")
doTrap(Seq(), 0)
}
case UndefinedMuFrame.VIRT_INST_TAILCALL => {
logger.debug(ctx+"Executing virtual tail-call")
val calleeFunc = f.func
val argBoxes = f.boxes
val shouldIncrementPC = curStack.tailCallMu(calleeFunc, argBoxes)
// Now we are in a new frame.
if (shouldIncrementPC) {
// Impossible because the callee is always fresh. Added here for consistency.
continueNormally()
}
}
}
}
/** Set PC to the next instruction of the same basic block. */
......
......@@ -229,7 +229,7 @@ class InterpreterStack(val id: Int, val stackMemory: StackMemory, stackBottomFun
* @return true if the interpreter should increment the PC by continueNormally().
*/
def rebindPassValues(args: Seq[ValueBox]): Boolean = {
if(!state.isInstanceOf[FrameState.Ready]) {
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 {
......@@ -328,6 +328,10 @@ abstract class MuFrame(val func: Function, prev: Option[InterpreterFrame]) exten
object UndefinedMuFrame {
val logger = Logger(LoggerFactory.getLogger(getClass.getName))
val VIRT_INST_NOT_STARTED = 0
val VIRT_INST_TRAP = 1
val VIRT_INST_TAILCALL = 2
}
/**
......@@ -347,6 +351,8 @@ class UndefinedMuFrame(func: Function, prev: Option[InterpreterFrame]) extends M
}
}
var virtInst = VIRT_INST_NOT_STARTED
override def scannableBoxes = boxes
def resumeNormally(args: Seq[ValueBox]): Boolean = {
......@@ -361,11 +367,22 @@ class UndefinedMuFrame(func: Function, prev: Option[InterpreterFrame]) extends M
}
justCreated = false
virtInst = VIRT_INST_TRAP
state = FrameState.Running
false
} else {
throw new UvmRefImplException("Undefined frame for function %s is already executed. Could be double binding.".format(func.repr))
assert(virtInst != VIRT_INST_NOT_STARTED, "The previous if should handle justCreated UndefinedMuFrame")
assert(virtInst != VIRT_INST_TAILCALL, "TAILCALL is not an OSR point")
assert(virtInst == VIRT_INST_TRAP)
if (args.length != 0) {
throw new UvmRefImplException("Undefined function %s expects no param on its first virtual trap, got %d args.".format(
func.repr, args.length))
}
virtInst = VIRT_INST_TAILCALL
false
}
}
......
package uvm.refimpl.misc
import org.scalatest._
import java.io.FileReader
import uvm._
import uvm.types._
import uvm.ssavariables._
import uvm.refimpl._
import uvm.refimpl.itpr._
import MemoryOrder._
import AtomicRMWOptr._
import uvm.refimpl.mem.TypeSizes.Word
import ch.qos.logback.classic.Level._
import uvm.refimpl.UvmBundleTesterBase
import org.slf4j.LoggerFactory
import com.typesafe.scalalogging.Logger
import uvm.refimpl.TrapHandlerResult.Rebind
import uvm.refimpl.HowToResume.ThrowExc
object FuncRedefTests {
val logger = Logger(LoggerFactory.getLogger(getClass.getName))
}
class FuncRedefTests extends UvmBundleTesterBase {
import FuncRedefTests._
setLogLevels(
ROOT_LOGGER_NAME -> INFO,
"uvm.refimpl.misc" -> DEBUG,
"uvm.refimpl.itpr" -> DEBUG)
preloadBundles("tests/uvm-refimpl-test/primitives.uir", "tests/uvm-refimpl-test/redef-file1.uir")
def loadBundleFromFile(ctx: MuCtx, fileName: String): Unit = {
val r = new FileReader(fileName)
ctx.loadBundle(r)
r.close()
}
"Function redefinition" should "work" in {
val ctx = microVM.newContext()
val func = ctx.handleFromFunc("@main")
logger.debug("Starting...")
testFunc(ctx, func, Seq()) { (ctx, th, st, wp) =>
ctx.nameOf(ctx.curFunc(st, 0)) match {
case "@main" => {
ctx.nameOf(ctx.curInst(st, 0)) match {
case "@main.v1.entry.checkpoint1" => {
val Seq(curMeaning: MuIntValue) = ctx.dumpKeepalives(st, 0)
ctx.handleToSInt(curMeaning) shouldBe 42
}
case "@main.v1.entry.checkpoint2" => {
val Seq(fox: MuIntValue) = ctx.dumpKeepalives(st, 0)
ctx.handleToSInt(fox) shouldBe 99
}
case "@main.v1.entry.change_meaning" => {
logger.debug("Changing meaning...")
loadBundleFromFile(ctx, "tests/uvm-refimpl-test/redef-file3.uir")
}
case "@main.v1.entry.checkpoint3" => {
val Seq(newMeaning: MuIntValue) = ctx.dumpKeepalives(st, 0)
ctx.handleToSInt(newMeaning) shouldBe 43
}
}
}
case "@foxsay" => {
logger.debug("Undefined @foxsay trapped.")
ctx.curFuncVer(st, 0) shouldBe 0
ctx.curInst(st, 0) shouldBe 0
loadBundleFromFile(ctx, "tests/uvm-refimpl-test/redef-file2.uir")
}
}
returnFromTrap(st)
}
ctx.closeContext()
}
"Exceptions thrown in undefined function frames" should "be rethrown" in {
val ctx = microVM.newContext()
val func = ctx.handleFromFunc("@main2")
logger.debug("Starting2...")
var excReached = false
testFunc(ctx, func, Seq()) { (ctx, th, st, wp) =>
ctx.nameOf(ctx.curFunc(st, 0)) match {
case "@main2" => {
ctx.nameOf(ctx.curInst(st, 0)) match {
case "@main2.v1.nor.trap" => {
fail("Should not continue normally.")
}
case "@main2.v1.exc.trap" => {
excReached = true
val Seq(e: MuRefValue) = ctx.dumpKeepalives(st, 0)
val hExc = ctx.handleFromConst("@NULLREF").asInstanceOf[MuRefValue]
val eq = ctx.refEq(e, hExc)
eq shouldBe true
}
}
returnFromTrap(st)
}
case "@lonelyfox" => {
logger.debug("Undefined @lonelyfox trapped.")
ctx.curFuncVer(st, 0) shouldBe 0
ctx.curInst(st, 0) shouldBe 0
val hExc = ctx.handleFromConst("@NULLREF").asInstanceOf[MuRefValue]
Rebind(st, ThrowExc(hExc))
}
}
}
excReached shouldBe true
ctx.closeContext()
}
}
\ No newline at end of file
// require primitives.uir
.funcsig @IntReturner = @i64 ()
.funcsig @IntReturner = () -> (@i64)
.funcdef @meaning_of_life <@IntReturner> () {
RET <@i64> 42
.const @I64_42 <@i64> = 42
.funcdef @meaning_of_life VERSION %v1 <@IntReturner> {
%entry():
RET @I64_42
}
.funcdecl @foxsay <@IntReturner>
.funcdef @main <@noparamsnoret> () {
%cur_meaning = CALL <@IntReturner> @meaning_of_life ()
%checkpoint1 = TRAP <@void> %step2 %step2 KEEPALIVE (%cur_meaning)
%step2:
%fox = CALL <@IntReturner> @foxsay ()
%checkpoint2 = TRAP <@void> %step3 %step3 KEEPALIVE (%fox)
%step3:
%change_meaning = TRAP <@void> %step4 %step4 KEEPALIVE ()
%step4:
%new_meaning = CALL <@IntReturner> @meaning_of_life ()
%checkpoint3 = TRAP <@void> %exit %exit KEEPALIVE (%new_meaning)
%exit:
ICALL @uvm.thread_exit ()
THROW @NULLREF
}
\ No newline at end of file
.funcdef @main VERSION %v1 <@v_v> {
%entry():
%cur_meaning = CALL <@IntReturner> @meaning_of_life ()
[%checkpoint1] TRAP <> KEEPALIVE (%cur_meaning)
%fox = CALL <@IntReturner> @foxsay ()
[%checkpoint2] TRAP <> KEEPALIVE (%fox)
[%change_meaning] TRAP <> KEEPALIVE ()
%new_meaning = CALL <@IntReturner> @meaning_of_life ()
[%checkpoint3] TRAP <> KEEPALIVE (%new_meaning)
COMMINST @uvm.thread_exit
}
.funcdecl @lonelyfox <@v_v>
.funcdef @main2 VERSION %v1 <@v_v> {
%entry():
[%call] CALL <@v_v> @lonelyfox () EXC(%nor() %exc())
%nor():
[%trap] TRAP <>
COMMINST @uvm.thread_exit
%exc() [%e]:
[%trap] TRAP <> KEEPALIVE(%e)
COMMINST @uvm.thread_exit
}
.funcdef @foxsay <@IntReturner> () {
RET <@i64> 99
.const @I64_99 <@i64> = 99
.funcdef @foxsay VERSION %v1 <@IntReturner> {
%entry():
RET @I64_99
}
.funcdef @meaning_of_life <@IntReturner> () {
RET <@i64> 43
.const @I64_43 <@i64> = 43
.funcdef @meaning_of_life VERSION %v2 <@IntReturner> {
%entry():
RET @I64_43
}
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