Commit a52e314d authored by Kunshan Wang's avatar Kunshan Wang

Merge branch 'issue52-threadlocal'

parents 2ffbfdcd 5ad721d5
# Mu Reference Implementation version 2
Version 2.1.1
Version 2.2.0
This project is the current reference implementation of Mu, the micro virtual
machine designed by [The Micro Virtual Machine Project](http://microvm.org).
Version 2.1.x implements the [master branch of the Mu
Specification](https://github.com/microvm/microvm-spec/tree/goto-with-values)
which uses the goto-with-values form.
Version 2.2.0 implements the [Mu Specification with experimental threadlocal
object references](https://github.com/microvm/microvm-spec/tree/issue52-threadlocal).
This project is based on the previous works of
[simplest-microvm-project](https://github.com/microvm/simplest-microvm-project).
......
......@@ -7,7 +7,7 @@ organization := "org.microvm"
name := "microvm-refimpl2"
version := "2.1.1"
version := "2.2.0"
description := "The second reference implementation of Mu, the micro virtual machine"
......
......@@ -242,9 +242,16 @@ struct MuCtx {
// Thread and stack creation and stack destruction
MuStackRefValue (*new_stack )(MuCtx *ctx, MuFuncRefValue func);
MuThreadRefValue (*new_thread)(MuCtx *ctx, MuStackRefValue stack,
MuHowToResume htr, MuValue *vals, int nvals, MuRefValue exc);
MuRefValue threadlocal,
MuHowToResume htr, MuValue *vals, int nvals,
MuRefValue exc);
void (*kill_stack)(MuCtx *ctx, MuStackRefValue stack);
// Thread-local object reference
void (*set_threadlocal)(MuCtx *ctx, MuThreadRefValue thread,
MuRefValue threadlocal);
MuRefValue (*get_threadlocal)(MuCtx *ctx, MuThreadRefValue thread);
// Frame cursor operations
MuFCRefValue (*new_cursor )(MuCtx *ctx, MuStackRefValue stack);
void (*next_frame )(MuCtx *ctx, MuFCRefValue cursor);
......
......@@ -8,6 +8,7 @@
#include <muapi.h>
const char *hw_string = "Hello world!\n";
const char *hw2_string = "Goodbye world!\n";
const char *gc_conf =
"sosSize=524288\n"
......@@ -16,7 +17,6 @@ const char *gc_conf =
"stackSize=32768\n"
;
int main() {
MuVM *mvm = mu_refimpl2_new_ex(gc_conf);
......@@ -37,7 +37,9 @@ int main() {
".const @the_write <@write.fp> = 0x%lx\n"
".const @the_string <@cvoidptr> = 0x%lx\n"
".const @the_length <@csize_t> = 0x%lx\n"
, (uintptr_t)write, (uintptr_t)hw_string, (unsigned long)strlen(hw_string));
".const @the_length2 <@csize_t> = 0x%lx\n"
, (uintptr_t)write, (uintptr_t)hw_string,
(unsigned long)strlen(hw_string), (unsigned long)strlen(hw2_string));
char *bundle3 =
".funcsig @v_v = ()->()\n"
......@@ -61,11 +63,45 @@ int main() {
MuFuncRefValue func = ctx->handle_from_func(ctx, ctx->id_of(ctx, "@hw"));
MuStackRefValue stack = ctx->new_stack(ctx, func);
MuThreadRefValue thread = ctx->new_thread(ctx, stack, MU_REBIND_PASS_VALUES,
NULL, 0, NULL);
MuThreadRefValue thread = ctx->new_thread(ctx, stack, NULL,
MU_REBIND_PASS_VALUES, NULL, 0, NULL);
mvm->execute(mvm);
char *bundle4 =
".typedef @cchar = int<8>\n"
".typedef @charhybrid = hybrid<@cchar>\n"
".typedef @refvoid = ref<@void>\n"
".funcdef @hw2 VERSION %1 <@v_v> {\n"
" %entry():\n"
" %tl = COMMINST @uvm.get_threadlocal\n"
" %p = COMMINST @uvm.native.pin <@refvoid> (%tl)\n"
" %rv = CCALL #DEFAULT <@write.fp @write.sig> @the_write (@the_fd %p @the_length2)\n"
" COMMINST @uvm.native.unpin <@refvoid> (%tl)\n"
" COMMINST @uvm.thread_exit\n"
"}\n"
;
printf("Loading additional bundle...\n");
ctx->load_bundle(ctx, bundle4, strlen(bundle4));
printf("Bundle loaded. Create a thread-local string object...\n");
MuIntValue hlen = ctx->handle_from_sint32(ctx, 256, 32);
MuRefValue hobj = ctx->new_hybrid(ctx, ctx->id_of(ctx, "@charhybrid"), hlen);
MuUPtrValue hpobj = ctx->pin(ctx, hobj);
char *mustrbuf = (char*)ctx->handle_to_ptr(ctx, hpobj);
strcpy(mustrbuf, hw2_string);
ctx->unpin(ctx, hobj);
printf("Object populated. Create thread with threadlocal and execute...\n");
MuFuncRefValue func2 = ctx->handle_from_func(ctx, ctx->id_of(ctx, "@hw2"));
MuStackRefValue stack2 = ctx->new_stack(ctx, func2);
MuThreadRefValue thread2 = ctx->new_thread(ctx, stack2, hobj,
MU_REBIND_PASS_VALUES, NULL, 0, NULL);
mvm->execute(mvm);
mu_refimpl2_close(mvm);
return 0;
......
......@@ -86,7 +86,7 @@ with mu.new_context() as ctx:
_start = ctx.handle_from_func(id_of("@_start"))
st = ctx.new_stack(_start)
th = ctx.new_thread(st, PassValues(args, rvbox))
th = ctx.new_thread(st, None, PassValues(args, rvbox))
mu.execute()
......
......@@ -75,7 +75,7 @@ example::
arg1 = ctx.handle_from_int(10, 64)
arg2 = ctx.handle_from_double(3.14)
stack = ctx.new_stack(func)
thread = ctx.new_thread(stack, PassValues(arg1, arg2))
thread = ctx.new_thread(stack, None, PassValues(arg1, arg2))
mu.execute() # The thread will actually run now.
......@@ -112,7 +112,7 @@ be pasesed into methods like in C. For example::
func = ctx.handle_from_func(id_of("@factorial"))
arg = ctx.handle_from_int(10, 64)
stack = ctx.new_stack(func)
thread = ctx.new_thread(stack, PassValues(arg))
thread = ctx.new_thread(stack, None, PassValues(arg))
Type checks are performed on each and every method call. Handles (instances of
MuValue) must match the expected argument types. For example, if a method
......@@ -792,21 +792,29 @@ class MuCtx(_StructOfMethodsWrapper):
defaults to MU_SEQ_CST."""
return self.fence_(ord)
def new_thread(self, stack, how_to_resume):
def new_thread(self, stack, threadlocal, how_to_resume):
"""Wrapper of the underlying ``new_thread``.
This method now takes only a stack and a ``HowToResume`` value which can
be either ``PassValues`` or ``ThrowExc``.
This method now takes only a stack, a threadlocal and a ``HowToResume``
value which can be either ``PassValues`` or ``ThrowExc``.
Arguments:
stack: a MuStackRefValue, the initial stack the new thread should
bind to.
threadlocal: a MuRefValue or None, the initial thread-local objref
of the created thread.
how_to_resume: a HowToResume value. See the docstring of
MuTrapHandler.
Returns:
a MuThreadRefValue, referring to the newly created thread.
"""
if threadlocal == None:
threadlocal = 0
else:
_assert_instance(threadlocal, MuRefValue)
_assert_instance(how_to_resume, HowToResume)
if isinstance(how_to_resume, PassValues):
htr = CMU_REBIND_PASS_VALUES
......@@ -824,7 +832,7 @@ class MuCtx(_StructOfMethodsWrapper):
cnvals = 0
cexc = exc.c_mu_value
return self.new_thread_(stack, htr, cvals, cnvals, cexc)
return self.new_thread_(stack, threadlocal, htr, cvals, cnvals, cexc)
def dump_keepalives(self, cursor, nvals):
"""Wrapper of the underlying ``dump_keepalives``.
......@@ -1026,9 +1034,11 @@ _initialize_methods(MuCtx, [
("fence_" , None , [CMuMemOrd]),
("new_stack" , MuStackRefValue , [MuFuncRefValue]),
("new_thread_" , MuThreadRefValue , [MuStackRefValue, CMuHowToResume,
ctypes.POINTER(CMuValue), ctypes.c_int, CMuRefValue]),
("new_thread_" , MuThreadRefValue , [MuStackRefValue, CMuRefValue,
CMuHowToResume, ctypes.POINTER(CMuValue), ctypes.c_int, CMuRefValue]),
("kill_stack" , None , [MuStackRefValue]),
("set_threadlocal" , None , [MuThreadRefValue, MuRefValue]),
("get_threadlocal" , MuRefValue , [MuThreadRefValue]),
("new_cursor" , MuFCRefValue , [MuStackRefValue]),
("next_frame" , None , [MuFCRefValue]),
......
......@@ -80,6 +80,18 @@ with mu.new_context() as ctx:
.typedef @void = void
.typedef @refvoid = ref<@void>
.const @NULLREF <@refvoid> = NULL
.funcsig @trap_threadlocal.sig = (@refvoid @refvoid) -> ()
.funcdef @trap_threadlocal VERSION %v1 <@trap_threadlocal.sig> {
%entry(<@refvoid> %r1 <@refvoid> %r2):
%r3 = COMMINST @uvm.get_threadlocal
[%trap1] TRAP <> KEEPALIVE (%r1 %r2 %r3)
%r4 = COMMINST @uvm.get_threadlocal
[%trap2] TRAP <> KEEPALIVE (%r1 %r2 %r4)
COMMINST @uvm.thread_exit
}
""")
class TestRefImpl2CBinding(unittest.TestCase):
......@@ -249,7 +261,7 @@ class TestRefImpl2CBinding(unittest.TestCase):
main = ctx.handle_from_func(id_of("@main"))
st = ctx.new_stack(main)
th = ctx.new_thread(st, PassValues(forty_two))
th = ctx.new_thread(st, None, PassValues(forty_two))
mu.execute()
......@@ -277,7 +289,7 @@ class TestRefImpl2CBinding(unittest.TestCase):
func = ctx.handle_from_func(id_of("@trap_exit_test"))
st = ctx.new_stack(func)
th = ctx.new_thread(st, PassValues(forty_two))
th = ctx.new_thread(st, None, PassValues(forty_two))
mu.execute()
......@@ -313,7 +325,7 @@ class TestRefImpl2CBinding(unittest.TestCase):
func = ctx.handle_from_func(id_of("@trap_exit_test"))
st = ctx.new_stack(func)
th = ctx.new_thread(st, PassValues(forty_two))
th = ctx.new_thread(st, None, PassValues(forty_two))
mu.execute()
......@@ -370,7 +382,7 @@ class TestRefImpl2CBinding(unittest.TestCase):
func = ctx.handle_from_func(id_of("@trap_rebind_test"))
st = ctx.new_stack(func)
th = ctx.new_thread(st, PassValues())
th = ctx.new_thread(st, None, PassValues())
mu.execute()
......@@ -383,8 +395,71 @@ class TestRefImpl2CBinding(unittest.TestCase):
func = ctx.handle_from_func(id_of("@trap_rebind_test"))
st = ctx.new_stack(func)
th = ctx.new_thread(st, PassValues())
th = ctx.new_thread(st, None, PassValues())
mu.execute()
self.assertEqual(test_result2, [True])
def test_trap_threadlocal(self):
ctx = self.ctx
id_of = ctx.id_of
name_of = ctx.name_of
box = []
class MyHandler(MuTrapHandler):
def handle_trap(self, ctx, thread, stack, wpid):
print("Hey! This is Python!")
cursor = ctx.new_cursor(stack)
iid = ctx.cur_inst(cursor)
if name_of(iid) == "@trap_threadlocal.v1.entry.trap1":
r1, r2, r3 = ctx.dump_keepalives(cursor, 3)
r1 = r1.cast(MuRefValue)
r2 = r2.cast(MuRefValue)
r3 = r3.cast(MuRefValue)
ctx.close_cursor(cursor)
rl = ctx.get_threadlocal(thread)
eql = ctx.ref_eq(rl, r3)
box.append(eql)
eq1 = ctx.ref_eq(r1, r3)
box.append(eq1)
ctx.set_threadlocal(thread, r2)
elif name_of(iid) == "@trap_threadlocal.v1.entry.trap2":
r1, r2, r4 = ctx.dump_keepalives(cursor, 3)
r1 = r1.cast(MuRefValue)
r2 = r2.cast(MuRefValue)
r4 = r4.cast(MuRefValue)
ctx.close_cursor(cursor)
rl = ctx.get_threadlocal(thread)
eql = ctx.ref_eq(rl, r4)
box.append(eql)
eq2 = ctx.ref_eq(r2, r4)
box.append(eq2)
return Rebind(stack, PassValues())
mh1 = MyHandler()
mu.set_trap_handler(mh1)
ref1 = ctx.new_fixed(id_of("@i64"))
ref2 = ctx.new_fixed(id_of("@i64"))
func = ctx.handle_from_func(id_of("@trap_threadlocal"))
st = ctx.new_stack(func)
th = ctx.new_thread(st, ref1, PassValues(ref1, ref2))
mu.execute()
self.assertEqual(box, [True, True, True, True])
......@@ -199,7 +199,7 @@ instBody
| 'CCALL' callConv=flag '<' funcTy=type funcSig '>' callee=value argList excClause keepAliveClause # InstCCall
// Thread and Stack Operations
| 'NEWTHREAD' stack=value newStackClause excClause # InstNewThread
| 'NEWTHREAD' stack=value threadLocalClause? newStackClause excClause # InstNewThread
| 'SWAPSTACK' swappee=value curStackClause newStackClause excClause keepAliveClause # InstSwapStack
// Common Instructions
......@@ -234,6 +234,10 @@ excClause
keepAliveClause
: ('KEEPALIVE' '(' value* ')')?
;
threadLocalClause
: ('THREADLOCAL' '(' value ')')
;
flagList
: '[' flag* ']'
......
......@@ -15,6 +15,8 @@ object CommInsts extends SimpleNamespace[CommInst] {
commInst(0x202, "@uvm.kill_stack")
commInst(0x203, "@uvm.thread_exit", isTerminator = true)
commInst(0x204, "@uvm.current_stack")
commInst(0x205, "@uvm.set_threadlocal")
commInst(0x206, "@uvm.get_threadlocal")
commInst(0x211, "@uvm.tr64.is_fp")
commInst(0x212, "@uvm.tr64.is_int")
......
......@@ -421,6 +421,9 @@ private[textinput] class InstanceUIRTextReader(idFactory: IDFactory, source: Str
Some(ExcClause(ec.nor, ec.exc))
}
implicit def resThreadLocalClause(tlc: ThreadLocalClauseContext): Option[SSAVariable] =
Option(tlc).map { theTlc => resVar(theTlc.value()) }
implicit def resNewStackClause(nsc: NewStackClauseContext): NewStackAction = {
nsc match {
case a: NewStackPassValueContext => PassValues(a.typeList(), a.argList())
......@@ -581,8 +584,9 @@ private[textinput] class InstanceUIRTextReader(idFactory: IDFactory, source: Str
i.callee = ii.callee; i.argList = ii.argList; i.excClause = ii.excClause; i.keepAlives = ii.keepAliveClause
}
case ii: InstNewThreadContext =>
InstNewThread(null, null, null).later(phase4) { i =>
InstNewThread(null, null, null, null).later(phase4) { i =>
i.stack = ii.stack
i.threadLocal = ii.threadLocalClause
i.newStackAction = ii.newStackClause
i.excClause = ii.excClause
}
......
......@@ -499,14 +499,22 @@ class MuCtx(_mutator: Mutator)(
}
}
private def getThreadNotNull(thread: MuThreadRefValue): InterpreterThread = {
thread.vb.thread.getOrElse {
throw new UvmRuntimeException("Thread argument cannot be a NULL threadref value.")
}
}
/** Create a Mu thread and bind it to a Mu stack. */
def newThread(stack: MuStackRefValue, htr: HowToResume): MuThreadRefValue = {
def newThread(stack: MuStackRefValue, threadLocal: Option[MuRefValue], htr: HowToResume): MuThreadRefValue = {
val sv = getStackNotNull(stack)
val itprHtr = htr match {
case HowToResume.PassValues(values) => ItprHowToResume.PassValues(values.map(_.vb))
case HowToResume.ThrowExc(exc) => ItprHowToResume.ThrowExc(exc.vb.objRef)
}
val thr = microVM.threadStackManager.newThread(sv, itprHtr)
val threadLocalAddr = threadLocal.map(tl => tl.vb.objRef).getOrElse(0L)
val thr = microVM.threadStackManager.newThread(sv, threadLocalAddr, itprHtr)
val nb = BoxThread(Some(thr))
addHandle(MuThreadRefValue(InternalTypes.THREADREF, nb))
......@@ -518,6 +526,23 @@ class MuCtx(_mutator: Mutator)(
sv.kill()
}
/** Set the thread-local object reference */
def setThreadlocal(thread: MuThreadRefValue, threadLocal: MuRefValue): Unit = {
val th = getThreadNotNull(thread)
val threadLocalAddr = threadLocal.vb.objRef
th.threadLocal.objRef = threadLocalAddr
}
/** Get the thread-local object reference */
def getThreadlocal(thread: MuThreadRefValue): MuRefValue = {
val th = getThreadNotNull(thread)
val threadLocalAddr = th.threadLocal.objRef
val nb = BoxRef(threadLocalAddr)
addHandle(MuRefValue(InternalTypes.REF_VOID, nb))
}
private def getCursorNotNull(cursor: MuFCRefValue): FrameCursor = {
cursor.vb.cursor.getOrElse {
......
......@@ -144,6 +144,8 @@ object TypeInferer {
case "@uvm.kill_stack" => Seq()
case "@uvm.thread_exit" => Seq()
case "@uvm.current_stack" => Seq(STACKREF)
case "@uvm.set_threadlocal" => Seq()
case "@uvm.get_threadlocal" => Seq(REF_VOID)
case "@uvm.tr64.is_fp" => Seq(I1)
case "@uvm.tr64.is_int" => Seq(I1)
case "@uvm.tr64.is_ref" => Seq(I1)
......
......@@ -56,6 +56,17 @@ trait CommInstExecutor extends InterpreterActions with ObjectPinner {
continueNormally()
}
case "@uvm.set_threadlocal" => {
val Seq(tl) = argList
threadLocal copyFrom tl
continueNormally()
}
case "@uvm.get_threadlocal" => {
results(0) copyFrom threadLocal
continueNormally()
}
// 64-bit Tagged Reference
case "@uvm.tr64.is_fp" => {
......
......@@ -522,19 +522,21 @@ trait InstructionExecutor extends InterpreterActions with CommInstExecutor {
}
}
case i @ InstNewThread(stack, newStackAction, excClause) => {
case i @ InstNewThread(stack, threadLocal, newStackAction, excClause) => {
val newStack = stack.asStack.getOrElse {
throw new UvmUndefinedBehaviorException(ctx + "Attempt to bind a new thread to a NULL stack.")
}
val threadLocalAddr = threadLocal.map(tl => tl.asRef).getOrElse(0L)
val newThread = newStackAction match {
case PassValues(argTys, args) => {
val argBoxes = args.map(boxOf)
microVM.threadStackManager.newThread(newStack, HowToResume.PassValues(argBoxes))
microVM.threadStackManager.newThread(newStack, threadLocalAddr, HowToResume.PassValues(argBoxes))
}
case ThrowExc(exc) => {
val excAddr = exc.asRef
microVM.threadStackManager.newThread(newStack, HowToResume.ThrowExc(excAddr))
microVM.threadStackManager.newThread(newStack, threadLocalAddr, HowToResume.ThrowExc(excAddr))
}
}
results(0).asThread = Some(newThread)
......
......@@ -26,8 +26,14 @@ object HowToResume {
/**
* A thread that interprets Mu instruction.
* <p>
* @param id The thread ID
* @param initialStack The initial stack it binds to
* @param initialThreadLocal The initial thread-local object reference
* @param mutator The Mutator object, for memory management
* @param htr How to resume. Either pass value or throw exception.
*/
class InterpreterThread(val id: Int, initialStack: InterpreterStack, val mutator: Mutator, htr: HowToResume)(
class InterpreterThread(val id: Int, initialStack: InterpreterStack, initialThreadLocal: Long, htr: HowToResume, val mutator: Mutator)(
implicit protected val microVM: MicroVM) extends InstructionExecutor with HasID {
import InterpreterThread._
......@@ -37,6 +43,8 @@ class InterpreterThread(val id: Int, initialStack: InterpreterStack, val mutator
override def curThread = this
// Initialisation
threadLocal.asRef = initialThreadLocal
htr match {
case HowToResume.PassValues(values) => rebindPassValues(initialStack, values)
......@@ -65,6 +73,9 @@ trait InterpreterThreadState {
/** Object-pinnning multiset. */
val pinSet = new ArrayBuffer[Word]
/** Thread-local object reference. */
val threadLocal = BoxRef(0L)
protected def curStack = stack.get
protected def top: InterpreterFrame = curStack.top
......
......@@ -103,10 +103,10 @@ class ThreadStackManager(implicit microVM: MicroVM, nativeCallHelper: NativeCall
/**
* Create a new thread, bind to a given stack.
*/
def newThread(stack: InterpreterStack, htr: HowToResume): InterpreterThread = {
def newThread(stack: InterpreterStack, threadLocal: Long, htr: HowToResume): InterpreterThread = {
val mutator = microVM.memoryManager.makeMutator()
val id = threadRegistry.getID()
val thr = new InterpreterThread(id, stack, mutator, htr)
val thr = new InterpreterThread(id, stack, threadLocal, htr, mutator)
threadRegistry.put(thr)
thr
}
......
......@@ -278,7 +278,7 @@ object NativeMuCtx {
// NOTE: parameter exc must not be a MuValue because this parameter is ignored when htr is MU_REBIND_PASS_VALUE.
// Setting the type to MuValue (or MuRefValue) will force the exposer to eagerly resolve the underlying actual MuValue.
def new_thread(ctx: MuCtx, stack: MuStackRefValue, htr: MuHowToResume, vals: MuValueFakArrayPtr, nvals: Int, exc: MuValueFak): MuValueFak = {
def new_thread(ctx: MuCtx, stack: MuStackRefValue, threadLocal: Option[MuRefValue], htr: MuHowToResume, vals: MuValueFakArrayPtr, nvals: Int, exc: MuValueFak): MuValueFak = {
val scalaHtr = htr match {
case MU_REBIND_PASS_VALUES => {
val values = for (i <- 0L until nvals) yield {
......@@ -294,10 +294,17 @@ object NativeMuCtx {
HowToResume.ThrowExc(excVal)
}
}
val rv = ctx.newThread(stack, scalaHtr)
val rv = ctx.newThread(stack, threadLocal, scalaHtr)
exposeMuValue(ctx, rv)
}
def kill_stack(ctx: MuCtx, stack: MuStackRefValue): Unit = ctx.killStack(stack)
def set_threadlocal(ctx: MuCtx, thread: MuThreadRefValue, threadLocal:MuRefValue): Unit = {
ctx.setThreadlocal(thread, threadLocal)
}
def get_threadlocal(ctx: MuCtx, thread: MuThreadRefValue): MuValueFak = {
val rv = ctx.getThreadlocal(thread)
exposeMuValue(ctx, rv)
}
// Frame cursor operations
def new_cursor(ctx: MuCtx, stack: MuStackRefValue): MuValueFak = exposeMuValue(ctx, ctx.newCursor(stack))
......@@ -436,6 +443,7 @@ object ClientAccessibleClassExposer {
val TMicroVM = ru.typeTag[MicroVM].tpe
val TMuCtx = ru.typeTag[MuCtx].tpe
val TMuValue = ru.typeTag[MuValue].tpe
val TOptMuValue = ru.typeTag[Option[MuValue]].tpe
// com.kenai.jffi.Closure.Buffer param getters and return value setters.
// These are partially-applied functions, and will be called in closures (callback from C).
......@@ -456,6 +464,18 @@ object ClientAccessibleClassExposer {
muValue
}
def paramOptMuValue(index: Int, funcName: String, tpe: ru.Type)(buffer: Buffer): Any = {
val muOptValue = getOptMuValue(buffer, index)
muOptValue foreach { muValue => // only check if it is Some(value)
val t = mirror.classSymbol(muValue.getClass).toType
val tpeArg = tpe.typeArgs(0)
require(t <:< tpeArg, "Argument %d of %s expect %s, found %s".format(index, funcName, tpe, t))
}
muOptValue
}
def retVoid(buffer: Buffer, v: Any): Unit = {}
def retByte(buffer: Buffer, v: Any): Unit = buffer.setByteReturn(v.asInstanceOf[Byte])
......@@ -477,6 +497,15 @@ object ClientAccessibleClassExposer {
NativeClientSupport.getMuValueNotNull(addr)
}
private def getOptMuValue(buffer: Buffer, index: Int): Option[MuValue] = {
val addr = buffer.getAddress(index)
if (addr == 0L) {
None
} else {
Some(NativeClientSupport.getMuValueNotNull(addr))
}
}
private def exposeStr(str: String): Word = {
val ptr = NativeClientSupport.stringPool.getOrElseUpdate(str, {
val bytes = str.getBytes(StandardCharsets.US_ASCII)
......@@ -506,6 +535,7 @@ object ClientAccessibleClassExposer {
case t if t =:= TMicroVM => JType.POINTER
case t if t =:= TMuCtx => JType.POINTER
case t if t <:< TMuValue => JType.POINTER
case t if t <:< TOptMuValue => JType.POINTER
}
val MU_NATIVE_ERRNO = 6481626 // muErrno is set to this number if an exception is thrown
......@@ -565,6 +595,7 @@ class ClientAccessibleClassExposer[T: ru.TypeTag: ClassTag](obj: T) {
case t if t =:= TMicroVM => paramMicroVM(i) _
case t if t =:= TMuCtx => paramMuCtx(i) _
case t if t <:< TMuValue => paramMuValue(i, meth.name.toString, t) _
case t if t <:< TOptMuValue => paramOptMuValue(i, meth.name.toString, t) _
}
val returnSetter = returnType match {
......
......@@ -252,7 +252,8 @@ case class InstCCall(var callConv: Flag, var funcTy: Type, var sig: FuncSig, var
var argList: Seq[SSAVariable], var excClause: Option[ExcClause], var keepAlives: Seq[LocalVariable])
extends CallLike with HasExcClause with HasKeepAliveClause with OSRPoint
case class InstNewThread(var stack: SSAVariable, var newStackAction: NewStackAction, var excClause: Option[ExcClause]) extends Instruction with HasExcClause
case class InstNewThread(var stack: SSAVariable, var threadLocal: Option[SSAVariable],
var newStackAction: NewStackAction, var excClause: Option[ExcClause]) extends Instruction with HasExcClause
case class InstSwapStack(var swappee: SSAVariable, var curStackAction: CurStackAction, var newStackAction: NewStackAction,
var excClause: Option[ExcClause], var keepAlives: Seq[LocalVariable]) extends HasExcClause with HasKeepAliveClause with OSRPoint {
......
......@@ -40,7 +40,7 @@ object FactorialFromRPython extends App {
})
val sta = ctx.newStack(m)
val thr = ctx.newThread(sta, PassValues(Seq()))
val thr = ctx.newThread(sta, None, PassValues(Seq()))
microVM.execute() // run until all threads stop
......
......@@ -63,7 +63,7 @@ object Interact extends App {
val fortyTwo = ctx.handleFromInt(42, 64) // #8
val th = ctx.newThread(st, HowToResume.PassValues(Seq(fortyTwo))) // #9
val th = ctx.newThread(st, None, HowToResume.PassValues(Seq(fortyTwo))) // #9
// Close the context
ctx.closeContext() // #10
......
......@@ -10,12 +10,12 @@ import uvm.comminsts._
trait ExtraMatchers extends Assertions with Matchers {
import ExtraMatchers._
import ExtraMatchers_._
implicit def anythingToAnythingExtraMatcher[U](thing: U) = new AnythingExtraMatchers(thing)
val thatsIt = { f: Any => }
case object nan
case class ExactFloat(num: Float)
case class ExactDouble(num: Double)
val NaN = ExtraMatchers_.NaN
def exactly(num: Float) = ExactFloat(num)
def exactly(num: Double) = ExactDouble(num)
......@@ -23,7 +23,15 @@ trait ExtraMatchers extends Assertions with Matchers {
def bitsd(num: Long) = java.lang.Double.longBitsToDouble(num)
}
object ExtraMatchers_ {
case object NaN
case class ExactFloat(num: Float)
case class ExactDouble(num: Double)
}
object ExtraMatchers extends ExtraMatchers {
import ExtraMatchers_._
implicit class AnythingExtraMatchers[U](val thing: U) extends AnyVal {
def shouldBeA[T: ClassTag](f: T => Unit): Unit = {
val ct = classTag[T]
......@@ -48,7 +56,7 @@ object ExtraMatchers extends ExtraMatchers {
thing shouldBeA[ConstFloat] { its =>
its.constTy shouldBeA[TypeFloat] thatsIt
something match {
case `nan` => assert(its.num.isNaN)
case NaN => its.num.isNaN shouldBe true
case ExactFloat(num) => its.num shouldEqual num
case num: Float => its.num shouldEqual (num +- 0.001F)
case _ => its.num shouldEqual something
......@@ -60,7 +68,7 @@ object ExtraMatchers extends ExtraMatchers {
thing shouldBeA[ConstDouble] { its =>
its.constTy shouldBeA[TypeDouble] thatsIt
something match {