A C header is available here: muapi.h
This chapter defines the Mu Client Interface, or "the API", the interfaces for the client to load bundles and manipulate the state of Mu, including creating stacks and threads, allocating objects, accessing memory and manipulating stacks and frames.
It is defined in the C programming language. A C header is available here: muapi.h. Clients can also be written in other languages via language bindings, which is beyond this specification.
Tools available: A Python script muapiparser.py is available. It parses the muapi.h header and generates a JSON-like tree. Language binding developers can use this script to automatically generate interfaces to higher-level languages.
Most API functions are also available in the form of instructions (such as refcast
and REFCAST
) or common
instructions (such as new_stack
and
@uvm.new_stack
). See their respective chapters.
How to start a Mu micro VM instance or a client is implementation-specific.
The Mu specification defines some types for using with common instructions, such
as @uvm.meta.byteref
. These types are always available. Whether other types,
signatures, constants, global cells or functions are already defined, declared
or exposed, or whether any Mu objects, stacks or Mu threads already created is
implementation-specific.
How to stop a Mu micro VM and/or a client is implementation-specific. Stopping the micro VM implies stopping all Mu threads in it.
Mu IDs and names are represented as:
typedef uint32_t MuID; typedef char *MuName;
A Mu instance is represented as a pointer to the struct MuVM
:
typedef struct MuVM MuVM; struct MuVM { void *header; // Refer to internal stuff MuCtx* (*new_context )(MuVM *mvm); MuID (*id_of )(MuVM *mvm, MuName name); MuName (*name_of )(MuVM *mvm, MuID id); void (*set_trap_handler)(MuVM *mvm, MuTrapHandler trap_handler, MuCPtr userdata); };
The client interacts with Mu for almost all tasks through client contexts, or simply context when unambiguous.
NOTE: In older versions of the spec, it was called "client agent".
A client context is represented as a pointer to the struct MuCtx
:
typedef struct MuCtx MuCtx; struct MuCtx { void *header; // Refer to internal stuff MuID (*id_of )(MuCtx *ctx, MuName name); MuName (*name_of )(MuCtx *ctx, MuID id); void (*close_context)(MuCtx *ctx); void (*load_bundle )(MuCtx *ctx, char *buf, MuArraySize sz); void (*load_hail )(MuCtx *ctx, char *buf, MuArraySize sz); ... };
The full list of member function pointers is listed in the header muapi.h.
Both the MuVM
and the MuCtx
struct contain many function pointer fields.
Each function pointer must be called with the MuVM
or MuCtx
pointer as
its first argument.
NOTE: This design is inspired by JNI. Exposing API functions as struct members rather than as global C functions has two advantages:
- A client can use multiple Mu implementations at the same time. Each implementation should provide its own structures.
- The client does not refer to any symbol in other dynamic libraries. So the client does not need to link against any binary libraries at compile time. This makes the client less coupled with a particular Mu implementation. It also allows the Mu micro VM to provide its API functions at the run time (JIT compile its own API functions, or even implementing the API in Mu IR itself).
The Mu instance is shared by all client threads, and its member functions can be called by any client thread without additional synchronisation. A client context can only be used by one client thread at a time.
NOTE: Client contexts are supposed to hold thread-local states similar to what a Mu thread holds. For example, implementations may reserve blocks of heap memory in client contexts so that memory allocation will not always require global locks. This trick is used by many garbage collectors that support parallel mutators, for example, Immix.
It holds Mu values for the client. The values are referred by the client via opaque handles. Those handles are defined as:
// Top value type. typedef void *MuValue; // Any Mu value // Abstract value type. typedef MuValue MuSeqValue; // array or vector typedef MuValue MuGenRefValue; // ref, iref, funcref, threadref, stackref, framecursorref, irnoderef // concrete value types typedef MuValue MuIntValue; // int<n> typedef MuValue MuFloatValue; // float typedef MuValue MuDoubleValue; // double typedef MuValue MuUPtrValue; // uptr typedef MuValue MuUFPValue; // ufuncptr typedef MuSeqValue MuStructValue; // struct<...> typedef MuSeqValue MuArrayValue; // array<T l> typedef MuSeqValue MuVectorValue; // vector<T l> typedef MuGenRefValue MuRefValue; // ref<T> typedef MuGenRefValue MuIRefValue; // iref<T> typedef MuGenRefValue MuTagRef64Value; // tagref64 typedef MuGenRefValue MuFuncRefValue; // funcref<sig> typedef MuGenRefValue MuThreadRefValue; // threadref typedef MuGenRefValue MuStackRefValue; // stackref typedef MuGenRefValue MuFCRefValue; // framecursorref typedef MuGenRefValue MuIRNodeRefValue; // irnoderef
Each handle can only hold a Mu value of the type shown in the comments above. Since the C programming language does not support user-defined type hierarchies, they are not checked by the C compiler, but the users must only pass appropriate handles to the appropriate API functions.
Handles have reference semantics: a handle refers to a value the client context holds. Handles can be copied as if they are pointers in C, and the resulting handles refer to the same value held by a context.
A handle is only valid within a client context. They must only be accessed with API functions that operates on the same context the handle is in. Handles are valid until either the value held by the context is explicitly deleted, or the client context is closed (explained later). Specifically, like SSA variables in the IR, a handle of general reference types always refer to the same object, location or other entities. This means a handle can keep Mu heap objects alive.
NOTE: The decision of using opaque handles serves two purposes. Both purposes are inspired by the Lua C API and JNI.
- To segregate the type system of Mu and the client. The type system of C is very different from Mu. For example, C does not have portable vector types. It does not support
int<n>
for n other than 8, 16, 32, 64 or 128. General reference typesref
,iref
,funcref
,threadref
andstackref
have no C counterparts, either, and their representations are specific to Mu implementations.- To simplify garbage collection. By recording all references held by the client, Mu can easily identify all roots. Making the representation of handles implementation-specific gives the client many choices, for instance:
- Handles can be raw pointers and all client-held references are pinned.
- Handles are indices in an indirection table. Since all memory accesses are done via one indirection, the Mu GC can still move objects.
Lua 4.0, introduced a stack-based API to pass values to/from the C program. That stack is unnecessary for Mu because Mu is not a stack-based VM. The API is also not designed to frequently exchange data frequently with the client (see the (unsafe) Native Interface for a more efficient (and less safe) interface).
For Lua users: The client context is the counterpart oflua_State
. Both maintain a registry of values in order to segregate Lua or Mu types and garbage collection from the C or client world. However, Mu client context can also access the Mu memory (heap, stack or global) which is shared between multiple client context as well as multiple Mu Threads running concurrently in the same Mu instance. In Lua,lua_State
instances are isolated.
For JNI users: The client context is the counterpart of the context of a JNI
invocation or an "attached" native thread, where there is a registry of Java
references. Mu handles are like local references, and the Mu API does not
have global references. For that need, consider using global cells
(.global
in the IR) to store shared values and use the appropriate
atomic access and memory order.
In this document, parameters of type MuVM*
, MuCtx*
and all subtypes of
MuValue
must no be the C NULL
pointer, or a handle to a Mu NULL
general reference value, unless explicitly stated otherwise. API functions will
not return C NULL
pointers unless explicitly stated otherwise.
NOTE: This is the billion dollar mistake. Language bindings of high-level
languages are encouraged to use the Option[T]
type or equivalent, if
available.
In the following functions, the first argument mvm
must be the same
MuVM*
pointer to the MuVM
structure that contains the function pointer.
MuCtx* (*new_context)(MuVM *mvm);
The newcontext
function creates a new client context.
For Lua users: This is similar to lua_newstate
, but multiple client
contexts share the Mu memory and can be used concurrently.
MuID (*id_of )(MuVM *mvm, MuName name); MuName (*name_of)(MuVM *mvm, MuID id);
The id_of
function looks up the corresponding ID by a name. The name_of
function looks up the name by an ID. Looking up names or IDs that do not exist
has undefined behaviour.
void (*set_trap_handler )(MuVM *mvm, MuTrapHandler trap_handler, MuCPtr userdata);
The set_trap_handler
function sets the handler for traps. This overrides the
trap handler registered by the @uvm.meta.set_trap_handler
instruction. See
the Trap Handling section below for more information.
userdata
will be passed to the handlers as the last element when they are
called.
In the following functions, the first argument ctx
must be the same
MuCtx*
pointer to the MuCtx
structure that contains the function
pointer.
MuID (*id_of )(MuCtx *ctx, MuName name); MuName (*name_of)(MuCtx *ctx, MuID id);
They are the same as the MuVM
functions. More often than not (such as trap
handling), a client function only has access to a MuCtx*
pointer rather than
a MuVM*
pointer.
void (*close_context)(MuCtx *ctx);
The close_context
function closes the current client context. After calling
this function, this MuCtx*
may no longer point to a valid MuCtx
structure and, thus, must not be used again. All values held by this context are
released. Specifically, all values of reference type are cleared so they no
longer strongly refer to objects and keep them alive. All handles created from
this context become invalid.
Implementations may release additional resources, such as context-specific (thread-local) memory allocation pools.
typedef uintptr_t MuArraySize; void (*load_bundle)(MuCtx *ctx, char *buf, MuArraySize sz); void (*load_hail )(MuCtx *ctx, char *buf, MuArraySize sz);
The load_bundle
function loads a Mu IR bundle, and the load_hail
function loads a HAIL script. The content is held in the memory pointed to by
buf
, and sz
is the length of the content in bytes.
Concurrency: The content of the bundle or the effect of the HAIL script is fully visible to other evaluations in the client that happen after this call.
TODO: These two functions should be made optional or lifted to the higher-level API which should be beyond this spec. The text-form bundle needs a parser, and the HAIL script is also not the most efficient way to load data into Mu at run time.
For Lua users: This is similar to
lua_load
, but a Mu bundle itself is not an executable thing like a Lua top-level function.For JVM users: This is similar to class loading.
load_bundle
loads the code and some Mu-level constants, but some Java-level constants, such as Strings, may be mapped to heap objects in Mu, and can be created and initialised using HAIL.
// Convert from C values to Mu values MuIntValue (*handle_from_sint8 )(MuCtx *ctx, int8_t num, int len); MuIntValue (*handle_from_uint8 )(MuCtx *ctx, uint8_t num, int len); MuIntValue (*handle_from_sint16 )(MuCtx *ctx, int16_t num, int len); MuIntValue (*handle_from_uint16 )(MuCtx *ctx, uint16_t num, int len); MuIntValue (*handle_from_sint32 )(MuCtx *ctx, int32_t num, int len); MuIntValue (*handle_from_uint32 )(MuCtx *ctx, uint32_t num, int len); MuIntValue (*handle_from_sint64 )(MuCtx *ctx, int64_t num, int len); MuIntValue (*handle_from_uint64 )(MuCtx *ctx, uint64_t num, int len); MuIntValue (*handle_from_uint64s)(MuCtx *ctx, uint64_t *nums, MuArraySize nnums, int len); MuFloatValue (*handle_from_float )(MuCtx *ctx, float num); MuDoubleValue (*handle_from_double )(MuCtx *ctx, double num); MuUPtrValue (*handle_from_ptr )(MuCtx *ctx, MuID mu_type, MuCPtr ptr); MuUFPValue (*handle_from_fp )(MuCtx *ctx, MuID mu_type, MuCFP fp);
where MuCPtr
and MuCFP
are convenient definitions for C types:
typedef void *MuCPtr; typedef void _MuCFP_Func(); typedef _MuCFP_Func* MuCFP; // it is void(*)()
The handle_from_XintYY
functions convert C integers to Mu int values. X
can be s
or u
, means the C value is signed or unsigned, respectively.
YY
is the number of bits in the C value.
The handle_from_uint64s
function converts an array of uint64_t
values
into a single Mu int value. nums
points to the array and nnums
is the
length of the array. Lower 64-bit words are stored in lower indices (little
endian).
The len
parameter is the length of the Mu int value, i.e. the n
in
int<n>
. If the length of the C value is different from the Mu type, it will
be truncated or extended. It is sign-extended for signed C types or
zero-extended for unsigned C types.
Other functions are:
handle_from_float
: Convert a Cfloat
value to a Mufloat
value.handle_from_double
: Convert a Cdouble
value to a Mudouble
value.handle_from_ptr
: Convert a C object pointer to a Mu pointer value. The Mu type ismu_type
which must beuptr<T>
for some typeT
.handle_from_fp
: Convert a C function pointer to a Mu function pointer value. The Mu type ismu_type
which must beufuncptr<sig>
for some signaturesig
.
For Lua users: These are the counterpart of
lua_pushnumber
,lua_pushboolean
,lua_pushinteger
,lua_pushstring
and so on. But Mu has much fewer primitive data types. Strings usually cannot be created this way since they are not primitive values.For JNI users: These are similar to the
NewLocalReference
function, but Mu handles can refer to not just reference types.
int8_t (*handle_to_sint8 )(MuCtx *ctx, MuIntValue opnd); uint8_t (*handle_to_uint8 )(MuCtx *ctx, MuIntValue opnd); int16_t (*handle_to_sint16)(MuCtx *ctx, MuIntValue opnd); uint16_t (*handle_to_uint16)(MuCtx *ctx, MuIntValue opnd); int32_t (*handle_to_sint32)(MuCtx *ctx, MuIntValue opnd); uint32_t (*handle_to_uint32)(MuCtx *ctx, MuIntValue opnd); int64_t (*handle_to_sint64)(MuCtx *ctx, MuIntValue opnd); uint64_t (*handle_to_uint64)(MuCtx *ctx, MuIntValue opnd); float (*handle_to_float )(MuCtx *ctx, MuFloatValue opnd); double (*handle_to_double)(MuCtx *ctx, MuDoubleValue opnd); MuCPtr (*handle_to_ptr )(MuCtx *ctx, MuUPtrValue opnd); MuCFP (*handle_to_fp )(MuCtx *ctx, MuUFPValue opnd);
The handle_to_XXX
functions convert Mu values to C values. Specifically:
The handle_to_XintYY
functions convert Mu int values to C integers. X
can be s
or u
, means the C value is signed or unsigned, respectively.
YY
is the number of bits in the C value. If the length of the Mu value is
different from the C type, it will be truncated or extended. It is sign-extended
for signed C types or zero-extended for unsigned C types.
Other functions are:
handle_to_float
: Convert a Mufloat
value to a Cfloat
value.handle_to_double
: Convert a Mudouble
value to a Cdouble
value.handle_to_ptr
: Convert a Muuptr<T>
value to a Cvoid*
value.handle_to_fp
: Convert a Muufuncptr<sig>
value to a Cvoid(*)()
value.
For Lua users: These are the counterpart of
lua_tonumber
,lua_toboolean
,lua_tostring
and so on. But Mu has much fewer primitive data types.For JNI users: Primitive types in JNI do not need to be converted. They are not held by handles and they are already compatible with C data types.
MuValue (*handle_from_const )(MuCtx *ctx, MuID id); MuIRefValue (*handle_from_global)(MuCtx *ctx, MuID id); MuFuncRefValue (*handle_from_func )(MuCtx *ctx, MuID id); MuValue (*handle_from_expose)(MuCtx *ctx, MuID id);
These instructions create handles which has the same value as the Mu global
variables whose ID is id
. Specifically,
handle_from_const
: Creates a handle from a Mu constant.handle_from_global
: Creates a handle from a Mu global cell.handle_from_func
: Creates a handle from a Mu function.handle_from_expose
: Creates a handle from an exposed function.
For Lua users: In Lua, global variables are top-level and they may refer to values or functions. These API functions are similar to the
lua_getglobal
function.For JNI users: This is similar to the
GetStaticObjectField
,GetStaticFieldID
orGetStaticMethodID
which allows the C program to access fields (including final fields) or methods. Mu does not support OOP directly, so methods may be just Mu functions that take objects as arguments.
void (*delete_value)(MuCtx *ctx, MuValue opnd);
Delete a value held by the context. After calling this function, the handle
opnd
cannot be used. If it was copied in C by assignment, all copies refer
to the same value held by the context, and they all become invalid.
For Lua users: This is similar to the
lua_pop
function.For JNI users: This is similar to the
DeleteLocalRef
routine.
typedef int MuBool; MuBool (*ref_eq )(MuCtx *ctx, MuGenRefValue lhs, MuGenRefValue rhs); MuBool (*ref_ult)(MuCtx *ctx, MuIRefValue lhs, MuIRefValue rhs);
The ref_eq
function compares two values of general reference type for
equality according to the semantics of the EQ
instruction. Both lhs
and
rhs
must have the same general reference type.
The ref_ult
function compares two internal reference lhs
and rhs
for
"less-than" according to the semantics of the ULT
instruction. Both lhs
and rhs
must have the same internal reference type.
The return value is 1 for true and 0 for false.
For Lua users:
ref_eq
is equivalent to thelua_rawequal
function for reference types.For JNI users:
ref_eq
is equivalent to theIsSameObject
function.
MuValue (*extract_value)(MuCtx *ctx, MuStructValue str, int index); MuValue (*insert_value )(MuCtx *ctx, MuStructValue str, int index, MuValue newval); MuValue (*extract_element)(MuCtx *ctx, MuSeqValue str, MuIntValue index); MuSeqValue (*insert_element )(MuCtx *ctx, MuSeqValue str, MuIntValue index, MuValue newval);
These functions manipulates values of composite types. Specifically,
extract_value
returns the value of a field atindex
in a struct valuestr
.insert_value
returns a new struct value which is the same asstr
except the field atindex
is replaced withnewval
.extract_element
returns the value of an element atindex
in an array or vector valueseq
.insert_element
returns a new array or vector value which is the same asseq
except the element atindex
is replaced withnewval
.
str
must have struct type. seq
must have array or vector type.
newval
must have the type of the field at index
, or the element type.
index
can be any integer type and is considered unsigned.
For Lua users:
extract_value
andinsert_value
are similar to thelua_getfield
and thelua_setfield
functions, but Mu struct fields are statically typed.extract_element
andinsert_element
are similar tolua_gettable
andlua_settable
with numerical indices, but Mu has arrays and vectors as value types.For JNI users:
extract_value
andinsert_value
are similarGet<type>Field
andSet<type>Field
.extract_element
andinsert_element
are similar to theGetObjectArrayElement
andSetObjectArrayElement
if accessing arrays of references. These Mu functions work with value types. Thepin
andunpin
Mu API function are similar toGet<PrimitiveType>ArrayElemets
andRelease<PrimitiveType>ArrayElements
when the array is in the heap.
MuRefValue (*new_fixed )(MuCtx *ctx, MuID mu_type); MuRefValue (*new_hybrid)(MuCtx *ctx, MuID mu_type, MuIntValue length);
new_fixed
allocates an object in the heap which has mu_type
type.
mu_type
must be a fixed-length type.
new_hybrid
allocates an object in the heap which has mu_type
type.
my_type
must be a hybrid. length
is the length of the variable part.
The return value is the object reference of the allocated object, or NULL
(A
NULL
pointer in C, which means it does not return a handle. It is not a
handle to a NULL
Mu reference.) when the allocation failed.
For Lua users: They are similar to the
lua_newtable
orlua_createtable
function, but Mu can allocate many different types on the heap. In this sense, it is also similar tolua_newuserdata
, but the allocated Mu object has associated metadata to identify references in the object.For JNI users: They are similar to the
AllocObject
and theNew<xxx>Array
routines. Mu is not aware of "initialiser".
MuValue (*refcast)(MuCtx *ctx, MuValue opnd, MuID new_type);
refcast
casts a value of general reference types to a new type new_type
.
The rules on the old type and the new type and the resulting value follow the
same rules as the REFCAST
instruction.
MuIRefValue (*get_iref )(MuCtx *ctx, MuRefValue opnd); MuIRefValue (*get_field_iref )(MuCtx *ctx, MuIRefValue opnd, int field); MuIRefValue (*get_elem_iref )(MuCtx *ctx, MuIRefValue opnd, MuIntValue index); MuIRefValue (*shift_iref )(MuCtx *ctx, MuIRefValue opnd, MuIntValue offset); MuIRefValue (*get_var_part_iref )(MuCtx *ctx, MuIRefValue opnd);
get_iref
converts an object reference into an internal reference to the memory location of the whole object.get_field_iref
gets an iref to thefield
-th field of the struct or of the fixed part of a hybrid referred by the irefopnd
.get_elem_iref
gets an iref to theindex
-th element of the array or vector referred by the irefopnd
.shift_iref
assumesopnd
refers to an element in a memory array (including arrays, vectors and the variable part of hybrids in the memory, see Mu and the Memory). It returns the iref ofopnd
moved byoffset
(forward if positive, backward if negative).get_var_part_iref
gets an iref to the 0-th element of the variable part of the hybrid referred by irefopnd
.
index
and offset
can be any integer types, and are considered signed.
MuValue (*load )(MuCtx *ctx, MuMemOrd ord, MuIRefValue loc); void (*store )(MuCtx *ctx, MuMemOrd ord, MuIRefValue loc, MuValue newval); MuValue (*cmpxchg )(MuCtx *ctx, MuMemOrd ord_succ, MuMemOrd ord_fail, MuBool weak, MuIRefValue loc, MuValue expected, MuValue desired, MuBool *is_succ); MuValue (*atomicrmw)(MuCtx *ctx, MuMemOrd ord, MuAtomicRMWOptr op, MuIRefValue loc, MuValue opnd); void (*fence )(MuCtx *ctx, MuMemOrd ord);
where:
typedef uint32_t MuFlag; // Memory orders typedef MuFlag MuMemOrd; #define MU_ORD_NOT_ATOMIC ((MuMemOrd)0x00) #define MU_ORD_RELAXED ((MuMemOrd)0x01) #define MU_ORD_CONSUME ((MuMemOrd)0x02) #define MU_ORD_ACQUIRE ((MuMemOrd)0x03) #define MU_ORD_RELEASE ((MuMemOrd)0x04) #define MU_ORD_ACQ_REL ((MuMemOrd)0x05) #define MU_ORD_SEQ_CST ((MuMemOrd)0x06) // Operations for the atomicrmw API function typedef MuFlag MuAtomicRMWOptr; #define MU_ARMW_XCHG ((MuAtomicRMWOptr)0x00) #define MU_ARMW_ADD ((MuAtomicRMWOptr)0x01) #define MU_ARMW_SUB ((MuAtomicRMWOptr)0x02) #define MU_ARMW_AND ((MuAtomicRMWOptr)0x03) #define MU_ARMW_NAND ((MuAtomicRMWOptr)0x04) #define MU_ARMW_OR ((MuAtomicRMWOptr)0x05) #define MU_ARMW_XOR ((MuAtomicRMWOptr)0x06) #define MU_ARMW_MAX ((MuAtomicRMWOptr)0x07) #define MU_ARMW_MIN ((MuAtomicRMWOptr)0x08) #define MU_ARMW_UMAX ((MuAtomicRMWOptr)0x09) #define MU_ARMW_UMIN ((MuAtomicRMWOptr)0x0A)
load
performs a load operation with arguments (ord
, T,loc
).store
performs a store operation with arguments (ord
, T,loc
,newval
).cmpxchg
performs a compare exchange operation with arguments (weak
,ord_sicc
,ord_fail
, T,loc
,expected
,desired
).weak
is Boolean encoded as int, where 1 means true and 0 means false.*is_succ
is set to 1 if successful, or 0 if failed.atomicrmw
performs an atomic-x operation with argument (ord
, T,loc
,opnd
), where the x in atomic-x isop
.fence
is a fence of memory orderord
.
In the above operations, T is the type of the referent type of internal
reference loc
. The type of newval
, expected
, desired
, opnd
and the return values of store
, cmpxchg
and atomicrmw
is the strong
variant of T (weakref<T>
becomes ref<T>
).
In the case where T is a general reference type, newval
, expected
,
desired
, opnd
and the return values may be handles to the Mu NULL
value.
The semantics of these instructions are defined by the Mu memory model.
For C users: Functions in
stdatomic.h
may be implemented in a way incompatible with Mu. This is implementation-defined.For JNI users:
load
andstore
are similar toGet<type>Field
andSet<type>Field
.
// Thread and stack creation and stack destruction MuStackRefValue (*new_stack )(MuCtx *ctx, MuFuncRefValue func); MuThreadRefValue (*new_thread_nor)(MuCtx *ctx, MuStackRefValue stack, MuRefValue threadlocal, MuValue *vals, MuBool nvals); MuThreadRefValue (*new_thread_exc)(MuCtx *ctx, MuStackRefValue stack, MuRefValue threadlocal, 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);
new_stack
creates a new Mu stack with func
as the bottom function.
Returns a stackref to the new stack. The stack-bottom frame will resume from the
very beginning of func
when resumed, and its state is READY<Ts>, where
Ts is the parameter types of func
.
new_thread_nor
and new_thread_exc
create a new Mu thread and binds it to
stack
. Returns a threadref to the new thread. stack
must be in the
READY<Ts> state for some types Ts.
new_thread_nor
passes values to the stack.vals
points to the array of values, andnvals
is the length of the array. The types of the values must match the Ts types of the stack state.new_thread_exc
throws the exceptionexc
to the stack.
The threadlocal
parameter is the initial thread-local reference of the new
thread. If it is a C NULL
pointer, it is equivalent to a Mu NULL
object
reference.
kill_stack
kills the stack
. The stack must be in the READY<Ts> state
for some Ts. The stack enters the DEAD state. If the stack contains
native frames, the behaviour is implementation-defined.
For Lua users:
new_stack
is similar tocoroutine.create
.new_thread
is similar tocoroutine.resume
, but actually creates a new thread. The real counterpart oflua_resume
is theSWAPSTACK
instruction, which is in the Mu IR but not available in the API.kill_stack
is similar tolua_close
.For JVM users: The JVM does not distinguish stacks and threads. The closest counterpart is the JVM TI function
StopThread
, but the Mukill_stack
message does not unwind the stack. It simply marks the whole stack for deletion.
set_threadlocal
sets the thread-local object reference of the thread
argument to threadlocal
.
get_threadlocal
returns the current thread-local object reference of
thread
.
Both get_threadlocal
and set_threadlocal
can only be used in the trap
handler and only on the thread that caused the trap.
MuFCRefValue (*new_cursor )(MuCtx *ctx, MuStackRefValue stack); void (*next_frame )(MuCtx *ctx, MuFCRefValue cursor); MuFCRefValue (*copy_cursor )(MuCtx *ctx, MuFCRefValue cursor); void (*close_cursor)(MuCtx *ctx, MuFCRefValue cursor);
new_cursor
allocates a frame cursor, referring to the top frame ofstack
. Returns the frame cursor reference.next_frame
moves the frame cursor so that it refers to the frame below its current frame.copy_cursor
allocates a frame cursor which refers to the same frame ascursor
. Returns the frame cursor reference.close_cursor
deallocates the cursor.
MuID (*cur_func )(MuCtx *ctx, MuFCRefValue cursor); MuID (*cur_func_ver )(MuCtx *ctx, MuFCRefValue cursor); MuID (*cur_inst )(MuCtx *ctx, MuFCRefValue cursor); void (*dump_keepalives)(MuCtx *ctx, MuFCRefValue cursor, MuValue *results);
These functions operate on the frame referred by cursor
.
cur_func
returns the ID of the frame. Returns 0 if the frame is native.cur_func_ver
returns the ID of the current function version of the frame. Returns 0 if the frame is native, or the function of the frame is undefined.cur_inst
returns the ID of the current instruction of the frame. Returns 0 if the frame is just created, its function is undefined, or the frame is native.dump_keepalives
dumps the values of the keep-alive variables of the current instruction of the frame. If the function is undefined, the arguments are the keep-alive variables. Cannot be used on native frames. As many handles as the keep-alive variables are written in theresults
array. Values returned in this array may be handles toNULL
values if the respective local variable isNULL
.
NOTE: The current instruction ID uniquely decides the list of keep-alive
variables, so the client can always know how long the results
array
should be allocated.
For Lua users: The debug interface provides many similar introspection functions.
For JVM users: The JVM TI function
GetFrameLocation
gets both the method ID and the location of instruction. It is like a combination ofcur_func_ver
andcur_inst
. JVM TI hasGetLocalVariable
which gets any local variable, but Mu only preserves some user-specified local variables for introspection. The user, however, can write theKEEPALIVE
clause to request all local variables to be available.
void (*pop_frames_to)(MuCtx *ctx, MuFCRefValue cursor); void (*push_frame )(MuCtx *ctx, MuStackRefValue stack, MuFuncRefValue func);
pop_frames_to
pops all frames abovecursor
.push_frame
creates a new frame on the top ofstack
for the current version of functionfunc
.
For JVM users:pop_frames_to
is similar to thePopFrame
JVM TI function, but the resumption point is immediately after the call site, not immediately before.
int (*tr64_is_fp )(MuCtx *ctx, MuTagRef64Value value); int (*tr64_is_int )(MuCtx *ctx, MuTagRef64Value value); int (*tr64_is_ref )(MuCtx *ctx, MuTagRef64Value value);
tr64_is_fp
checks ifvalue
holds an FP number.tr64_is_int
checks ifvalue
holds an integer.tr64_is_ref
checks ifvalue
holds a reference.
Return 1 or 0 for true or false.
MuDoubleValue (*tr64_to_fp )(MuCtx *ctx, MuTagRef64Value value); MuIntValue (*tr64_to_int )(MuCtx *ctx, MuTagRef64Value value); MuRefValue (*tr64_to_ref )(MuCtx *ctx, MuTagRef64Value value); MuIntValue (*tr64_to_tag )(MuCtx *ctx, MuTagRef64Value value);
tr64_to_fp
returns the FP number held byvalue
.tr64_to_int
returns the integer held byvalue
.tr64_to_ref
returns the reference held byvalue
, may be a handle to theNULL
value.tr64_to_tag
returns the integer tag held byvalue
that accompanies the reference.
They have undefined behaviours if %tr
does not hold the value of the
expected type.
MuTagRef64Value (*tr64_from_fp )(MuCtx *ctx, MuDoubleValue value); MuTagRef64Value (*tr64_from_int)(MuCtx *ctx, MuIntValue value); MuTagRef64Value (*tr64_from_ref)(MuCtx *ctx, MuRefValue ref, MuIntValue tag);
tr64_from_fp
creates atagref64
value from an FP numbervalue
.tr64_from_int
creates atagref64
value from an integervalue
.tr64_from_ref
creates atagref64
value from a referenceref
and the integer tagtag
.ref
may be a handle to theNULL
value.
Return the created tagref64
value.
typedef uint32_t MuWPID; void (*enable_watchpoint )(MuCtx *ctx, MuWPID wpid); void (*disable_watchpoint)(MuCtx *ctx, MuWPID wpid);
enable_watchpoint
enables all watchpoints of IDwpid
.disable_watchpoint
disables all watchpoints of IDwpid
.
Enabling or disabling any non-existing wpid
has undefined behaviour.
MuUPtrValue (*pin )(MuCtx *ctx, MuValue loc); // loc is either MuRefValue or MuIRefValue void (*unpin)(MuCtx *ctx, MuValue loc); // loc is either MuRefValue or MuIRefValue
pin
adds one instance of memory locationloc
to the pinning set local to the current client contextctx
. Returns a pointer which has the resulting address. The pointer has the same referent type as loc.unpin
removes one instance of memory locationloc
from the pinning set local to the current client contextctx
. Attempting to unpin locations that has not yet been added has undefined behaviour.
loc
can be either ref<T>
or iref<T>
. If it is ref<T>
, it pins
the underlying memory location as obtained by get_iref
. In both cases, the
result is uptr<T>
.
For Lua users: Lua userdata are always available as pointers. This assumes userdata are always pinned.
For JNI users: The
Get<PrimitiveType>ArrayElements
andRelease<PrimitiveType>ArrayElements>
has similar semantics as pinning and unpinning. Also note that the argument ofunpin
is the same reference as what is pinned, not the resulting (untraced) pointer.
MuValue (*expose )(MuCtx *ctx, MuFuncRefValue func, MuCallConv call_conv, MuIntValue cookie); void (*unexpose)(MuCtx *ctx, MuCallConv call_conv, MuValue value);
where:
typedef MuFlag MuCallConv; #define MU_CC_DEFAULT ((MuCallConv)0x00)
expose
exposes a Mu functionfunc
according to the calling conventioncall_conv
.cookie
is the attached cookie. The return value is specific to the calling convention.unexpose
invalidates an exposed valuevalue
created by the@uvm.native.expose
instruction or theexpose
API function with calling conventioncall_conv
.
See the Native Interface chapter for details of expose and unexpose. See the platform-specific native interface for the return value type.
Implementations may define other calling conventions in addition to
MU_CC_DEFAULT
.
A trap handler is called when a TRAP
or WATCHPOINT
instruction is
executed. It is also implicitly called when executing an undefined function.
It is unspecified on which stack the trap handler is run.
Mu is allowed to call these handlers concurrently. Mu does not guarantee the order in which these handlers are called, but guarantees the happen-before relation between the trap and the handler. See Memory Model.
The Mu thread that triggers the trap is temporarily unbound from the stack.
NOTE: This unbinding is only conceptual. It is a valid implementation to use the same Mu thread to execute the trap handler.
The signature of trap handlers is:
// Signature of the trap handler typedef void _MuTrapHandler_Func( // input parameters MuCtx *ctx, MuThreadRefValue thread, MuStackRefValue stack, MuWPID wpid, // output parameters MuTrapHandlerResult *result, MuStackRefValue *new_stack, MuValue **values, MuArraySize *nvalues, MuValuesFreer *freer, MuCPtr *freerdata, MuRefValue *exception, // input parameter (userdata) MuCPtr userdata);
where:
typedef MuFlag MuTrapHandlerResult; #define MU_THREAD_EXIT ((MuTrapHandlerResult)0x00) #define MU_REBIND_PASS_VALUES ((MuTrapHandlerResult)0x01) #define MU_REBIND_THROW_EXC ((MuTrapHandlerResult)0x02) typedef void _MuValuesFreer_Func(MuValue *values, MuCPtr freerdata); typedef _MuValuesFreer_Func* MuValuesFreer;
ctx
is a new client context created for this particular trap event.
thread
is a threadref to the thread that causes the trap. stack
is
stackref to the stack the thread was bound to when trap happened. Both
thread
and stack
are held by ctx
. wpid
is the watch point ID if
it is caused by a WATCHPOINT
instruction, or 0 if by TRAP
. userdata
is the pointer provided when registering the handler.
ctx
will be closed when the trap handler returns, so the client must not
close it manually.
Before returning, the trap handler should set *result
:
MU_THREAD_EXIT
: The threadthread
terminates.Mu_REBIND_PASS_VALUES
: The threadthread
will be rebound to a stack*new_stack
.*nvalues
values at*values
are passed to*new_stack
. The types of*values
must match the type expected by*new_stack
. Mu will copy the values from the*values
array. Then if*freer
is notNULL
, Mu will call(*freer)(*values, *freerdata)
.NOTE: Effectively, the client is returning an array to Mu. Mu does not know what stack the client is going to rebind to before calling, therefore the client must allocate the array for the values. The client is also responsible for freeing the array, but it loses control after returning to Mu. So Mu will call the "freer" function on behalf of the client. Usually the client can use
malloc
, and provide a thin wrapper overfree
as thefreer
.Mu_REBIND_THROW_EXC
: The threadthread
will be rebound to a stack*new_stack
. It throws exception*exception
to the stack.
In all cases, if *new_stack
, any value in *values
, and/or *exception
are used, they must be set and must be held by ctx
.
See IR Builder for this part of the API.
Signal handling is implementation-dependent. Mu may register signal handlers.