GitLab will be upgraded on June 2nd 2020 at 2.00 pm (AEDT) to 3.00 pm (AEDT) due to Critical Security Patch Availability. During the update, GitLab and Mattermost services will not be available. If you have any concerns with this, please talk to local Gitlab admin team.

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