Commit ff2c290e authored by Kunshan Wang's avatar Kunshan Wang

Trap handler and tests.

In theory, all Mu client API functions are available to C, but not all
are tested.  Now a client in C is known to be able to create stack,
thread, handle traps and introspect stacks.
parent 22a4132f
...@@ -120,6 +120,9 @@ struct MuVM { ...@@ -120,6 +120,9 @@ struct MuVM {
// Set handlers // Set handlers
void (*set_trap_handler )(MuVM *mvm, MuTrapHandler trap_handler, MuCPtr userdata); void (*set_trap_handler )(MuVM *mvm, MuTrapHandler trap_handler, MuCPtr userdata);
// Proprietary API: let the micro VM execute
void (*execute)(MuVM *mvm);
}; };
// A local context. It can only be used by one thread at a time. It holds many // A local context. It can only be used by one thread at a time. It holds many
...@@ -221,7 +224,7 @@ struct MuCtx { ...@@ -221,7 +224,7 @@ struct MuCtx {
// Thread and stack creation and stack destruction // Thread and stack creation and stack destruction
MuStackRefValue (*new_stack )(MuCtx *ctx, MuFuncRefValue func); MuStackRefValue (*new_stack )(MuCtx *ctx, MuFuncRefValue func);
MuThreadRefValue (*new_thread)(MuCtx *ctx, MuStackRefValue stack, MuThreadRefValue (*new_thread)(MuCtx *ctx, MuStackRefValue stack,
MuHowToResume *htr, MuValue *vals, int nvals, MuRefValue exc); MuHowToResume htr, MuValue *vals, int nvals, MuRefValue exc);
void (*kill_stack)(MuCtx *ctx, MuStackRefValue stack); void (*kill_stack)(MuCtx *ctx, MuStackRefValue stack);
// Frame cursor operations // Frame cursor operations
......
...@@ -17,14 +17,14 @@ class TrapManager(implicit microVM: MicroVM) { ...@@ -17,14 +17,14 @@ class TrapManager(implicit microVM: MicroVM) {
object DefaultTrapHandler extends TrapHandler { object DefaultTrapHandler extends TrapHandler {
def handleTrap(ctx: MuCtx, thread: MuThreadRefValue, stack: MuStackRefValue, watchPointID: Int): TrapHandlerResult = { def handleTrap(ctx: MuCtx, thread: MuThreadRefValue, stack: MuStackRefValue, watchPointID: Int): TrapHandlerResult = {
val thrID = thread.vb.asInstanceOf[BoxThread].thread.get.id val thrID = thread.vb.asInstanceOf[BoxThread].thread.get.id
// val curFuncID = ctx. val staID = stack.vb.asInstanceOf[BoxStack].stack.get.id
// val funcVerID = ca.currentFuncVer(stack, 0) val cursor = ctx.newCursor(stack)
// val funcVer = microVM.globalBundle.funcVerNs(funcVerID) val curFuncID = ctx.curFunc(cursor)
// val instID = ca.currentInstruction(stack, 0) val curFuncVerID = ctx.curFuncVer(cursor)
// val inst = microVM.globalBundle.varNs(instID) val curInstID = ctx.curInst(cursor)
// throw new UvmRuntimeException("Unhandled trap. Thread %d, funcver %s, trap inst %s, watch point ID %d".format( ctx.closeCursor(cursor)
// thr.id, funcVer.repr, inst.repr, watchPointID)) throw new UvmRuntimeException("Unhandled trap. thread %d, stack: %d, func: %d, funcver: %d, inst %d, watch point ID %d".format(
??? thrID, staID, curFuncID, curFuncVerID, curInstID, watchPointID))
} }
} }
......
...@@ -22,13 +22,129 @@ import uvm.ssavariables.MemoryOrder ...@@ -22,13 +22,129 @@ import uvm.ssavariables.MemoryOrder
import uvm.ssavariables.AtomicRMWOptr import uvm.ssavariables.AtomicRMWOptr
import uvm.ssavariables.Flag import uvm.ssavariables.Flag
import uvm.ssavariables.Flag import uvm.ssavariables.Flag
import com.kenai.jffi.Invoker
import com.kenai.jffi.CallContext
import com.kenai.jffi.HeapInvocationBuffer
import NativeClientSupport._
object NativeTrapHandler {
val jffiInvoker = Invoker.getInstance
val trapHandlerCallContext = new CallContext(
JType.VOID, // return value
// input params
JType.POINTER, //MuCtx *ctx,
JType.POINTER, //MuThreadRefValue thread,
JType.POINTER, //MuStackRefValue stack,
JType.SINT32, //int wpid,
// output params
JType.POINTER, //MuTrapHandlerResult *result,
JType.POINTER, //MuStackRefValue *new_stack,
JType.POINTER, //MuValue **values,
JType.POINTER, //int *nvalues,
JType.POINTER, //MuValuesFreer *freer,
JType.POINTER, //MuCPtr *freerdata,
JType.POINTER, //MuRefValue *exception,
// user data
JType.POINTER //MuCPtr userdata
);
val freerCallContext = new CallContext(
JType.VOID, // return value
// input params
JType.POINTER, // MuValue *values,
JType.POINTER //MuCPtr freerdata
)
def callFreer(freerFP: MuValuesFreerFP, valuesPtr: MuValueFakArrayPtr, freerData: MuCPtr): Unit = {
val freerFunc = new com.kenai.jffi.Function(freerFP, freerCallContext)
logger.debug("Calling freer: 0x%x with args (0x%x, 0x%x)".format(freerFP, valuesPtr, freerData))
jffiInvoker.invokeLLrL(freerFunc, valuesPtr, freerData)
logger.debug("Returned from freer")
}
}
/** /**
* This object calls a C function to handle the trap. * This object calls a C function to handle the trap.
*/ */
class NativeTrapHandler(val funcAddr: Long, val userdata: Long) extends TrapHandler { class NativeTrapHandler(val funcAddr: MuTrapHandlerFP, val userdata: MuCPtr) extends TrapHandler {
import NativeTrapHandler._
import NativeMuHelpers._
val jffiFunction = new com.kenai.jffi.Function(funcAddr, trapHandlerCallContext)
def handleTrap(ctx: MuCtx, thread: MuThreadRefValue, stack: MuStackRefValue, watchPointID: Int): TrapHandlerResult = { def handleTrap(ctx: MuCtx, thread: MuThreadRefValue, stack: MuStackRefValue, watchPointID: Int): TrapHandlerResult = {
??? logger.debug("Trapped. Prepare to call native trap handler: %d 0x%x".format(funcAddr, funcAddr))
val hib = new HeapInvocationBuffer(jffiFunction)
// input args
val ctxFak = exposeMuCtx(ctx) // It is a fresh context.
hib.putAddress(ctxFak)
val threadFak = exposeMuValue(ctx, thread) // It is a fresh handle, too.
hib.putAddress(threadFak)
val stackFak = exposeMuValue(ctx, stack) // It is a fresh handle, too.
hib.putAddress(stackFak)
hib.putInt(watchPointID)
// output args
val nOutArgs = 7
val outBuf = jnrMemoryManager.allocateDirect((7L * WORD_SIZE_BYTES).toInt, true) // output values are received here.
val outBufAddr = outBuf.address()
for (i <- 0 until nOutArgs) {
val outAddr = outBufAddr + i * WORD_SIZE_BYTES
logger.debug("The %d-th out arg: 0x%x".format(i, outAddr))
hib.putAddress(outAddr)
}
// user data
hib.putAddress(userdata)
// Call
logger.debug("Calling native trap handler: 0x%x".format(funcAddr))
jffiInvoker.invokeLong(jffiFunction, hib)
logger.debug("Returned from native trap handler: 0x%x".format(funcAddr))
// Inspect return value
val result = outBuf.getInt(0L)
val scalaResult: TrapHandlerResult = result match {
case MU_THREAD_EXIT => TrapHandlerResult.ThreadExit()
case MU_REBIND_PASS_VALUES => {
val nsFak = outBuf.getAddress(1 * WORD_SIZE_BYTES)
val newStack = getMuValueNotNull(nsFak).asInstanceOf[MuStackRefValue]
val valuesPtr = outBuf.getAddress(2 * WORD_SIZE_BYTES)
val nValues = outBuf.getInt(3 * WORD_SIZE_BYTES)
val valueFaks = for (i <- 0 until nValues) yield {
theMemory.getAddress(valuesPtr + i * WORD_SIZE_BYTES)
}
val freerFP = outBuf.getAddress(4 * WORD_SIZE_BYTES)
if (freerFP != 0L) {
val freerData = outBuf.getAddress(5 * WORD_SIZE_BYTES)
callFreer(freerFP, valuesPtr, freerData)
}
val values = valueFaks.map(getMuValueNotNull)
TrapHandlerResult.Rebind(newStack, HowToResume.PassValues(values))
}
case MU_REBIND_THROW_EXC => {
val nsFak = outBuf.getAddress(1 * WORD_SIZE_BYTES)
val newStack = getMuValueNotNull(nsFak).asInstanceOf[MuStackRefValue]
val excFak = outBuf.getAddress(6 * WORD_SIZE_BYTES)
val excVal = getMuValueNotNull(excFak).asInstanceOf[MuRefValue]
TrapHandlerResult.Rebind(newStack, HowToResume.ThrowExc(excVal))
}
}
unexposeMuCtx(ctxFak) // Mu will close the context, but not un-expose it.
scalaResult
} }
} }
...@@ -37,7 +153,6 @@ class NativeTrapHandler(val funcAddr: Long, val userdata: Long) extends TrapHand ...@@ -37,7 +153,6 @@ class NativeTrapHandler(val funcAddr: Long, val userdata: Long) extends TrapHand
*/ */
object NativeMuVM { object NativeMuVM {
import NativeMuHelpers._ import NativeMuHelpers._
import NativeClientSupport._
def new_context(microVM: MicroVM): MuCtxFak = { def new_context(microVM: MicroVM): MuCtxFak = {
val ctx = microVM.newContext() val ctx = microVM.newContext()
...@@ -49,6 +164,7 @@ object NativeMuVM { ...@@ -49,6 +164,7 @@ object NativeMuVM {
def set_trap_handler(microVM: MicroVM, trap_handler: MuTrapHandlerFP, userdata: MuCPtr): Unit = { def set_trap_handler(microVM: MicroVM, trap_handler: MuTrapHandlerFP, userdata: MuCPtr): Unit = {
microVM.setTrapHandler(new NativeTrapHandler(trap_handler, userdata)) microVM.setTrapHandler(new NativeTrapHandler(trap_handler, userdata))
} }
def execute(microVM: MicroVM): Unit = microVM.execute()
} }
/** /**
...@@ -59,7 +175,6 @@ object NativeMuVM { ...@@ -59,7 +175,6 @@ object NativeMuVM {
*/ */
object NativeMuCtx { object NativeMuCtx {
import NativeMuHelpers._ import NativeMuHelpers._
import NativeClientSupport._
def id_of(ctx: MuCtx, name: MuName): MuID = ctx.idOf(name) def id_of(ctx: MuCtx, name: MuName): MuID = ctx.idOf(name)
def name_of(ctx: MuCtx, id: MuID): MuName = ctx.nameOf(id) def name_of(ctx: MuCtx, id: MuID): MuName = ctx.nameOf(id)
...@@ -157,7 +272,10 @@ object NativeMuCtx { ...@@ -157,7 +272,10 @@ object NativeMuCtx {
def fence(ctx: MuCtx, ord: MuMemOrd): Unit = ctx.fence(ord) def fence(ctx: MuCtx, ord: MuMemOrd): Unit = ctx.fence(ord)
def new_stack(ctx: MuCtx, func: MuFuncRefValue): MuValueFak = exposeMuValue(ctx, ctx.newStack(func)) def new_stack(ctx: MuCtx, func: MuFuncRefValue): MuValueFak = exposeMuValue(ctx, ctx.newStack(func))
def new_thread(ctx: MuCtx, stack: MuStackRefValue, htr: MuHowToResume, vals: MuValueFakArrayPtr, nvals: Int, exc: MuRefValue): MuValueFak = {
// 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 = {
val scalaHtr = htr match { val scalaHtr = htr match {
case MU_REBIND_PASS_VALUES => { case MU_REBIND_PASS_VALUES => {
val values = for (i <- 0L until nvals) yield { val values = for (i <- 0L until nvals) yield {
...@@ -169,7 +287,8 @@ object NativeMuCtx { ...@@ -169,7 +287,8 @@ object NativeMuCtx {
HowToResume.PassValues(values) HowToResume.PassValues(values)
} }
case MU_REBIND_THROW_EXC => { case MU_REBIND_THROW_EXC => {
HowToResume.ThrowExc(exc) val excVal = getMuValueNotNull(exc).asInstanceOf[MuRefValue]
HowToResume.ThrowExc(excVal)
} }
} }
val rv = ctx.newThread(stack, scalaHtr) val rv = ctx.newThread(stack, scalaHtr)
...@@ -211,10 +330,10 @@ object NativeMuCtx { ...@@ -211,10 +330,10 @@ object NativeMuCtx {
def enable_watchpoint(ctx: MuCtx, wpid: Int): Unit = ctx.enableWatchpoint(wpid) def enable_watchpoint(ctx: MuCtx, wpid: Int): Unit = ctx.enableWatchpoint(wpid)
def disable_watchpoint(ctx: MuCtx, wpid: Int): Unit = ctx.disableWatchpoint(wpid) def disable_watchpoint(ctx: MuCtx, wpid: Int): Unit = ctx.disableWatchpoint(wpid)
def pin(ctx: MuCtx, loc: MuValue): MuValueFak = exposeMuValue(ctx, ctx.pin(loc)) def pin(ctx: MuCtx, loc: MuValue): MuValueFak = exposeMuValue(ctx, ctx.pin(loc))
def unpin(ctx: MuCtx, loc: MuValue): Unit = ctx.unpin(loc) def unpin(ctx: MuCtx, loc: MuValue): Unit = ctx.unpin(loc)
def expose(ctx: MuCtx, func: MuFuncRefValue, call_conv: MuCallConv, cookie: MuIntValue): MuValueFak = exposeMuValue(ctx, ctx.expose(func, call_conv, cookie)) def expose(ctx: MuCtx, func: MuFuncRefValue, call_conv: MuCallConv, cookie: MuIntValue): MuValueFak = exposeMuValue(ctx, ctx.expose(func, call_conv, cookie))
def unexpose(ctx: MuCtx, call_conv: MuCallConv, value: MuUFPValue): Unit = ctx.unexpose(call_conv, value) def unexpose(ctx: MuCtx, call_conv: MuCallConv, value: MuUFPValue): Unit = ctx.unexpose(call_conv, value)
} }
...@@ -223,7 +342,6 @@ object NativeMuCtx { ...@@ -223,7 +342,6 @@ object NativeMuCtx {
* These functions are not exposed. * These functions are not exposed.
*/ */
object NativeMuHelpers { object NativeMuHelpers {
import NativeClientSupport._
private val ONE = BigInt(1) private val ONE = BigInt(1)
...@@ -264,7 +382,7 @@ object NativeMuHelpers { ...@@ -264,7 +382,7 @@ object NativeMuHelpers {
val MU_MIN = 0x08 val MU_MIN = 0x08
val MU_UMAX = 0x09 val MU_UMAX = 0x09
val MU_UMIN = 0x0A val MU_UMIN = 0x0A
val MU_DEFAULT = 0x00 val MU_DEFAULT = 0x00
implicit def intToMemOrd(ord: MuMemOrd): MemoryOrder.MemoryOrder = ord match { implicit def intToMemOrd(ord: MuMemOrd): MemoryOrder.MemoryOrder = ord match {
...@@ -290,14 +408,13 @@ object NativeMuHelpers { ...@@ -290,14 +408,13 @@ object NativeMuHelpers {
case MU_UMAX => AtomicRMWOptr.UMAX case MU_UMAX => AtomicRMWOptr.UMAX
case MU_UMIN => AtomicRMWOptr.UMIN case MU_UMIN => AtomicRMWOptr.UMIN
} }
implicit def intToCallConv(cc: MuCallConv): Flag = cc match { implicit def intToCallConv(cc: MuCallConv): Flag = cc match {
case MU_DEFAULT => Flag("#DEFAULT") case MU_DEFAULT => Flag("#DEFAULT")
} }
} }
object ClientAccessibleClassExposer { object ClientAccessibleClassExposer {
import NativeClientSupport._
val MAX_NAME_SIZE = 65536 val MAX_NAME_SIZE = 65536
...@@ -405,8 +522,6 @@ object ClientAccessibleClassExposer { ...@@ -405,8 +522,6 @@ object ClientAccessibleClassExposer {
*/ */
class ClientAccessibleClassExposer[T: ru.TypeTag: ClassTag](obj: T) { class ClientAccessibleClassExposer[T: ru.TypeTag: ClassTag](obj: T) {
import ClientAccessibleClassExposer._ import ClientAccessibleClassExposer._
import NativeSupport._
import NativeClientSupport._
/** /**
* A list of JFFI closure handles, one for each declared method in obj, in the declared order. * A list of JFFI closure handles, one for each declared method in obj, in the declared order.
...@@ -516,6 +631,7 @@ object NativeClientSupport { ...@@ -516,6 +631,7 @@ object NativeClientSupport {
type CharArrayPtr = Word type CharArrayPtr = Word
type MuValueFakPtr = Word type MuValueFakPtr = Word
type MuValueFakArrayPtr = Word type MuValueFakArrayPtr = Word
type MuValuesFreerFP = Word
// Mu API C-level types. // Mu API C-level types.
type MuID = Int type MuID = Int
......
...@@ -27,6 +27,7 @@ class NativeClientSupportTest extends UvmBundleTesterBase { ...@@ -27,6 +27,7 @@ class NativeClientSupportTest extends UvmBundleTesterBase {
def test_with_ctx(mvm: Word, theID: Int, theName: String): CBool def test_with_ctx(mvm: Word, theID: Int, theName: String): CBool
def test_basic_conv(mvm: Word): CBool def test_basic_conv(mvm: Word): CBool
def test_global_vars(mvm: Word, the_plus_one_fp: Word): CBool def test_global_vars(mvm: Word, the_plus_one_fp: Word): CBool
def test_traps(mvm: Word): CBool
} }
val ncs_tests = LibraryLoader.create(classOf[NcsTestsLib]).load(fileName) val ncs_tests = LibraryLoader.create(classOf[NcsTestsLib]).load(fileName)
...@@ -77,4 +78,9 @@ class NativeClientSupportTest extends UvmBundleTesterBase { ...@@ -77,4 +78,9 @@ class NativeClientSupportTest extends UvmBundleTesterBase {
val result = ncs_tests.test_global_vars(microVMFuncTableAddr, thePlusOneFP) val result = ncs_tests.test_global_vars(microVMFuncTableAddr, thePlusOneFP)
assertNativeSuccess(result) assertNativeSuccess(result)
} }
it should "support traps" in {
val result = ncs_tests.test_traps(microVMFuncTableAddr)
assertNativeSuccess(result)
}
} }
\ No newline at end of file
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
#include <stdint.h> #include <stdint.h>
#include <inttypes.h> #include <inttypes.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdlib.h>
#include <muapi.h> #include <muapi.h>
...@@ -11,7 +12,13 @@ ...@@ -11,7 +12,13 @@
return false; \ return false; \
} }
#define MU_ASSERT_EQUALS_TRAP(a, b, f) if (!(a == b)) { \
muprintf("%s (%" f ") is not equal to %s (%" f ")\n", #a, a, #b, b); \
exit(1); \
}
#define ID(name) ctx->id_of(ctx, name) #define ID(name) ctx->id_of(ctx, name)
#define NAME(id) ctx->name_of(ctx, id)
#define muprintf(fmt, ...) printf("[C:%d:%s] " fmt, __LINE__, __func__, ## __VA_ARGS__) #define muprintf(fmt, ...) printf("[C:%d:%s] " fmt, __LINE__, __func__, ## __VA_ARGS__)
...@@ -208,3 +215,127 @@ bool test_global_vars(MuVM *mvm, int64_t(*the_plus_one_fp)(int64_t)) { ...@@ -208,3 +215,127 @@ bool test_global_vars(MuVM *mvm, int64_t(*the_plus_one_fp)(int64_t)) {
return true; return true;
} }
struct simple_context {
int magic;
};
void malloc_freer(MuValue *values, MuCPtr freerdata) {
free(values);
}
void nop_freer(MuValue *values, MuCPtr freerdata) {
}
void simple_trap_handler(MuCtx *ctx, MuThreadRefValue thread,
MuStackRefValue stack, int wpid, MuTrapHandlerResult *result,
MuStackRefValue *new_stack, MuValue **values, int *nvalues,
MuValuesFreer *freer, MuCPtr *freerdata, MuRefValue *exception,
MuCPtr userdata) {
muprintf("Hi! I am the native trap handler!\n");
struct simple_context *userctx = (struct simple_context*)userdata;
int magic = userctx->magic;
muprintf("My magic is %d\n", magic);
MU_ASSERT_EQUALS_TRAP(magic, 42, "d");
muprintf("I am going to introspect the stack.\n");
MuFCRefValue *cursor = ctx->new_cursor(ctx, stack);
MuID fid = ctx->cur_func(ctx, cursor);
muprintf("Function: %d: %s\n", fid, NAME(fid));
MuID fvid = ctx->cur_func_ver(ctx, cursor);
muprintf("Version: %d: %s\n", fvid, NAME(fvid));
MuID iid = ctx->cur_inst(ctx, cursor);
muprintf("Instruction: %d: %s\n", iid, NAME(iid));
MuID trap1_id = ID("@trapper.v1.entry.trap1");
MuID trap2_id = ID("@trapper.v1.entry.trap2");
MU_ASSERT_EQUALS_TRAP(fid , ID("@trapper"), "d");
MU_ASSERT_EQUALS_TRAP(fvid, ID("@trapper.v1"), "d");
muprintf("Selecting branch according to the instruction ID: %d\n", iid);
if (iid == trap1_id) {
muprintf("It is %%trap1\n");
MuValue kas[1];
muprintf("Dumping keep-alives...\n");
ctx->dump_keepalives(ctx, cursor, kas);
muprintf("Dumped\n");
ctx->close_cursor(ctx, cursor);
int64_t oldKa = ctx->handle_to_sint64(ctx, kas[0]);
muprintf("The keep-alive variable is %" PRId64 "\n", oldKa);
MU_ASSERT_EQUALS_TRAP(oldKa, 42LL, PRId64);
int64_t newKa = oldKa + 1;
muprintf("Prepare to return\n");
muprintf("Writing result at %p\n", result);
*result = MU_REBIND_PASS_VALUES;
muprintf("Writing new_stack at %p\n", new_stack);
*new_stack = stack;
muprintf("Allocating values array, writing at %p\n", values);
*values = (MuValue*)malloc(8);
muprintf("Setting the only value. Addr: %p\n", &(*values)[0]);
(*values)[0] = ctx->handle_from_sint64(ctx, newKa, 64);
muprintf("Writing nvalues at %p\n", nvalues);
*nvalues = 1;
muprintf("Writing freer at %p\n", freer);
*freer = malloc_freer;
muprintf("Writing freerdata at %p\n", freerdata);
*freerdata = NULL;
muprintf("Bye!\n");
return;
} else if (iid == trap2_id) {
muprintf("It is %%trap2\n");
MuValue kas[1];
ctx->dump_keepalives(ctx, cursor, kas);
ctx->close_cursor(ctx, cursor);
muprintf("Introspect the KAs...\n");
int64_t v1 = ctx->handle_to_sint64(ctx, kas[0]);
muprintf("KA value %%v1 is %" PRId64 "\n", v1);
MU_ASSERT_EQUALS_TRAP(v1, 43LL, PRId64);
muprintf("Prepare to return from trap handler...\n");
*result = MU_REBIND_PASS_VALUES;
*new_stack = stack;
*values = NULL;
*nvalues = 0;
*freer = NULL;
*freerdata = NULL;
muprintf("Bye!\n");
return;
} else {
muprintf("Unknown trap. ID: %d\n", iid);
MuName trapName = NAME(iid);
muprintf("name: %s\n", trapName);
exit(1);
}
return; // Unreachable, but the C compiler may not be smart enough.
}
bool test_traps(MuVM *mvm) {
struct simple_context userctx = { 42 };
mvm->set_trap_handler(mvm, simple_trap_handler, &userctx);
MuCtx *ctx = mvm->new_context(mvm);
MuValue args[1];
args[0] = ctx->handle_from_sint64(ctx, 42LL, 64);
MuFuncRefValue func = ctx->handle_from_func(ctx, ID("@trapper"));
MuStackRefValue stack = ctx->new_stack(ctx, func);
MuThreadRefValue thread = ctx->new_thread(ctx, stack, MU_REBIND_PASS_VALUES,
args, 1, NULL);
mvm->execute(mvm);
ctx->close_context(ctx);
return true;
}
...@@ -9,3 +9,10 @@ ...@@ -9,3 +9,10 @@
} }
.expose @plus_one_native = @plus_one #DEFAULT @I64_0 .expose @plus_one_native = @plus_one #DEFAULT @I64_0
.funcdef @trapper VERSION %v1 <@i_i> {
%entry(<@i64> %n):
%v1 = [%trap1] TRAP <@i64> KEEPALIVE (%n)
[%trap2] TRAP <> KEEPALIVE (%v1)
COMMINST @uvm.thread_exit
}
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