This chapter describes the function call-based Mu IR building API. It is part of the Mu Client Interface.
The C functions and types are also defined in muapi.h.
There must be a way for the client to load Mu IR bundles into the micro VM.
Both text- or binary-encoded IR require parsers, which is difficult to verify. This API, instead, consists of a set of functions rather than well-known data format.
In this API, the client builds the Mu IR bundle by calling API functions. Each
function creates an AST node inside the micro VM and gives the client an opaque
handle to it, or modify existing AST nodes by adding more children to them. When
finished building, the client calls another API function
(load_bundle_from_node) to tell the micro VM to load the bundle.
For LLVM users: Like the LLVM API, the client creates nodes by invoking functions. But in Mu, AST nodes are held within the micro VM, and is opaque to the client.
All API functions are also available as common instructions.
This API is minimal, just like other parts of the Mu Client API. For example, the AST is only mutable in certain ways (such as appending instructions in the end of a basic block, but not deleting instructions), so it is not suitable to be used by the client for arbitrary transformations. However, clients or middle-wares may provide higher-level abstractions over this API, such as supporting non-SSA forms, or supporting other serialisable forms such as the text form. Currently the API still accepts text-form IR, but in the future we plan to move it outside the micro VM.
There are many function pointers in the MuCtx struct:
struct MuCtx {
// more members here
/// IR Builder API from here on.
MuBundleNode (*new_bundle)(MuCtx *ctx);
void (*load_bundle_from_node )(MuCtx *ctx, MuBundleNode b);
void (*abort_bundle_node )(MuCtx *ctx, MuBundleNode b);
MuChildNode (*get_node )(MuCtx *ctx, MuBundleNode b, MuID id);
MuID (*get_id )(MuCtx *ctx, MuBundleNode b, MuChildNode node);
void (*set_name )(MuCtx *ctx, MuBundleNode b, MuChildNode node, MuName name);
MuTypeNode (*new_type_int )(MuCtx *ctx, MuBundleNode b, int len);
MuTypeNode (*new_type_float )(MuCtx *ctx, MuBundleNode b);
MuTypeNode (*new_type_double )(MuCtx *ctx, MuBundleNode b);
MuTypeNode (*new_type_uptr )(MuCtx *ctx, MuBundleNode b);
// more functions here ...
};
IR nodes are referenced by Mu values of primitive type irnoderef.
In the C API, it is represented by the handle type MuIRNodeRefValue.
MuIRNode is its alias.
There are typedefs for different kinds of IR nodes. They are all aliases to
MuIRNodeRefValue. They are created to assist humans, but the C programming
language cannot check the types for the client.
MuBundleNode, MuChildNode, MuTypeNode, etc. are its subtypes. They
are only supposed to refer to nodes of their specified type in the comments:
typedef MuGenRefValue MuIRNodeRefValue; // irnoderef // Subtypes of MuIRNodeRefValue. These are used in the IR Builder API. // Shorter aliases typedef MuIRNodeRefValue MuIRNode; // All IR Nodes // IR node reference hierarchy typedef MuIRNode MuBundleNode; // Bundle // NOTE: All MuChildNode can have ID (ctx->get_id) and name (ctx->set_name) typedef MuIRNode MuChildNode; // All children of bundle typedef MuChildNode MuTypeNode; // Type typedef MuChildNode MuFuncSigNode; // Function signature typedef MuChildNode MuVarNode; // Variable typedef MuVarNode MuGlobalVarNode; // Global variable typedef MuGlobalVarNode MuConstNode; // Constant typedef MuGlobalVarNode MuGlobalNode; // Global cell typedef MuGlobalVarNode MuFuncNode; // Function typedef MuGlobalVarNode MuExpFuncNode; // Exposed function typedef MuVarNode MuLocalVarNode; // Local variable typedef MuLocalVarNode MuNorParamNode; // Normal parameter typedef MuLocalVarNode MuExcParamNode; // Exception parameter typedef MuLocalVarNode MuInstResNode; // Instruction result typedef MuChildNode MuFuncVerNode; // Function version typedef MuChildNode MuBBNode; // Basic block typedef MuChildNode MuInstNode; // Instruction (itself, not result)
All MuChildNode subtypes are "identified entities" (see Mu IR). They have IDs and optionally names. The ID of each
MuChildNode is generated automatically. The ID can be obtained by the
get_id function. The names are assigned by the client by the set_name
function.
The bundle node is created by the new_bundle function:
MuBundleNode b = ctx->new_bundle(ctx);
Top-level nodes are created by their respective functions. They take the bundle as argument, which means they are added to the bundle on creation:
MuTypeNode i1 = ctx->new_type_int(ctx, b, 1); MuTypeNode i8 = ctx->new_type_int(ctx, b, 8); MuTypeNode i32 = ctx->new_type_int(ctx, b, 32); MuTypeNode i64 = ctx->new_type_int(ctx, b, 64);
IDs are automatically generated. IDs of the node can be obtained by the
get_id function:
MuID i32_id = ctx->get_id(ctx, b, i32);
And the name can be set by the set_name function:
ctx->set_name(ctx, b, i32, "@i32");
All nodes can just work without names, but names may provide extra debug information if the implementation supports.
When finished creating a bundle, use the load_bundle_from_node function to
load it:
ctx->load_bundle_from_node(ctx, b);
Or if the client want to cancel the bundle building process, use the
abort_bundle_node function:
ctx->abort_bundle_node(ctx, b);
Both load_bundle_from_node and abort_bundle_node will invalidate all
references to all nodes in the bundle! This also prevents getting the IDs or
setting names of invalidated node references. If the client wants the ID, it
should do it before loading:
MuBundleNode b = ctx->new_bundle(ctx); MuTypeNode i32 = ctx->new_type_int(ctx, b, 32); MuID i32_id = ctx->get_id(ctx, b, i32); // Get the ID ctx->load_bundle_from_node(ctx, b); // before loading.
When creating subsequent bundles, it may refer to nodes in already-loaded
bundles. Use the get_node function to get a reference:
// Building the second bundle MuBundleNode b2 = ctx->new_bundle(ctx); MuTypeNode i32 = (MuTypeNode)ctx->get_node(ctx, b2, i32_id);
IR node references obtained in this way can only be used in the bundle
specified by the parameter of the get_node function.
Some nodes are created in more than one steps. For example, the ref<T> type
is created in two steps:
MuTypeNode refi32 = ctx->new_type_ref(ctx, b2); ctx->set_type_ref(ctx, refi32, i32);
The first step creates the type and the second type sets the referent type. In this way, the client can create recursive types, such as linked list.
To define a function, two nodes need to be created: A function and a function version:
MuFuncSigNode sig = ..... MuFuncNode func = ctx->new_func(ctx, b2, sig); MuFuncVerNode funcver = ctx->new_func_ver(ctx, b2, func);
If only the MuFuncNode is defined, it will declare a function without
defining it.
Basic blocks can be added to the function version. Basic blocks have parameters. They need to be added to the basic block, too:
MuBasicBlock entry = ctx->new_bb(ctx, funcver); MuNorParam p0 = ctx->new_nor_param(ctx, entry, i32); MuNorParam p1 = ctx->new_nor_param(ctx, entry, i32);
Instructions can be added to basic blocks. NOTE that the results of instructions are separate IR nodes! They have to be added separately:
// The ADD instruction produces one result.
MuInstNode add = ctx->new_binop(ctx, entry, MU_BINOP_ADD, i32, p0, p1);
MuInstResNode add_r = ctx->new_inst_res(ctx, add);
// Mu functions may return multiple return values.
MuVarNode args[] = { xxx, xxx, xxx };
MuInstNode call = ctx->new_call(ctx, entry, some_sig, some_func, args, 3);
MuInstResNode call_r0 = ctx->new_inst_res(ctx, call);
MuInstResNode call_r1 = ctx->new_inst_res(ctx, call);
MuInstResNode call_r2 = ctx->new_inst_res(ctx, call);
MuInstResNode call_r3 = ctx->new_inst_res(ctx, call);
Unlike the text form, the exception clauses are just destinations. For instructions that may have exception clauses, if you set the normal destination, you must also set the exceptional destination:
MuVarNode nordestargs[] = { xxx, xxx };
ctx->add_dest(ctx, call, MU_DEST_NORMAL, some_bb, nordestargs, 2);
MuVarNode excdestargs[] = { xxx };
ctx->add_dest(ctx, call, MU_DEST_EXCEPT, other_bb, excdestargs, 1);
Other instructions have destinations, too, such as the BRANCH2 instruction:
MuInstNode br2 = new_branch2(ctx, funcver, some_condition);
MuVarNode truedestargs[] = { xxx, xxx, xxx, xxx };
ctx->add_dest(ctx, br2, MU_DEST_TRUE, if_true_bb, truedestargs, 4);
MuVarNode falsedestargs[] = { xxx, xxx, xxx, xxx, xxx };
ctx->add_dest(ctx, br2, MU_DEST_FALSE, if_false_bb, truedestargs, 5);
The contents of the arrays are copied into the micro VM, so the client may free those arrays after calling the API functions. The micro VM and the client do not share data other than handles.
Like the rest of the Mu Client API, all functions take a MuCtx* as the first
parameter, and other parameters of handle types (subtypes of MuValue,
including all MuIRNode subtypes), must be created by that MuCtx.
All array parameters also have their length passed via another parameter. For
example, MuTypeNode* fieldtys in new_type_struct is followed by
MuArraySize nfieldtys which is the length of the fieldtys array.
typedef uintptr_t MuArraySize;
If the array pointer argument (such as fieldtys) is a C NULL pointer, it
means the array is empty. In this case, the micro VM will disregard the length
argument (such as nfieldtys).
There is one exception: MuName is char*, but it is '\0'-terminated
ASCII string.
Many functions (such as new_inst_binop) have enumeration-like arguments
(such as MuBinOptr and its values MU_BINOP_ADD, MU_BINOP_SUB, etc.).
In muapi.h, these types are defined to be integers and these constants are
defined as macros:
typedef uint32_t MuFlag;
NOTE: In C, theenumdefinition will create constants ofinttypes, but implementations may extend the lengths and introduce potential ABI compatibility problems. For this reason, we chose to explicitly use a predictable int typeuint32_t.
MuBundleNode (*new_bundle)(MuCtx *ctx);
new_bundle creates a new Mu IR bundle.
void (*load_bundle_from_node )(MuCtx *ctx, MuBundleNode b); void (*abort_bundle_node )(MuCtx *ctx, MuBundleNode b);
load_bundle_from_node loads the bundle b into the micro VM.
abort_bundle_node discards the bundle b. Both functions invalidate all
irnoderef references in the bundle.
MuChildNode (*get_node )(MuCtx *ctx, MuBundleNode b, MuID id);
get_node creates a reference to a top-level definition node already
loaded into the micro VM. id is the ID of that node. IR node reference
created by this function can only be used in the bundle b.
MuID (*get_id )(MuCtx *ctx, MuBundleNode b, MuChildNode node);
get_id gets the ID of the node node in bundle b. b must not have
been loaded.
void (*set_name )(MuCtx *ctx, MuBundleNode b, MuChildNode node, MuName name);
set_name sets the name of the node node to name. node must be in
bundle b, which must not be loaded.
MuTypeNode (*new_type_int )(MuCtx *ctx, MuBundleNode b, int len); MuTypeNode (*new_type_float )(MuCtx *ctx, MuBundleNode b); MuTypeNode (*new_type_double )(MuCtx *ctx, MuBundleNode b); MuTypeNode (*new_type_uptr )(MuCtx *ctx, MuBundleNode b); void (*set_type_uptr )(MuCtx *ctx, MuTypeNode uptr, MuTypeNode ty); MuTypeNode (*new_type_ufuncptr )(MuCtx *ctx, MuBundleNode b); void (*set_type_ufuncptr )(MuCtx *ctx, MuTypeNode ufuncptr, MuFuncSigNode sig); MuTypeNode (*new_type_struct )(MuCtx *ctx, MuBundleNode b, MuTypeNode *fieldtys, MuArraySize nfieldtys); MuTypeNode (*new_type_hybrid )(MuCtx *ctx, MuBundleNode b, MuTypeNode *fixedtys, MuArraySize nfixedtys, MuTypeNode varty); MuTypeNode (*new_type_array )(MuCtx *ctx, MuBundleNode b, MuTypeNode elemty, uint64_t len); MuTypeNode (*new_type_vector )(MuCtx *ctx, MuBundleNode b, MuTypeNode elemty, uint64_t len); MuTypeNode (*new_type_void )(MuCtx *ctx, MuBundleNode b); MuTypeNode (*new_type_ref )(MuCtx *ctx, MuBundleNode b); void (*set_type_ref )(MuCtx *ctx, MuTypeNode ref, MuTypeNode ty); MuTypeNode (*new_type_iref )(MuCtx *ctx, MuBundleNode b); void (*set_type_iref )(MuCtx *ctx, MuTypeNode iref, MuTypeNode ty); MuTypeNode (*new_type_weakref )(MuCtx *ctx, MuBundleNode b); void (*set_type_weakref )(MuCtx *ctx, MuTypeNode weakref, MuTypeNode ty); MuTypeNode (*new_type_funcref )(MuCtx *ctx, MuBundleNode b); void (*set_type_funcref )(MuCtx *ctx, MuTypeNode funcref, MuFuncSigNode sig); MuTypeNode (*new_type_tagref64 )(MuCtx *ctx, MuBundleNode b); MuTypeNode (*new_type_threadref )(MuCtx *ctx, MuBundleNode b); MuTypeNode (*new_type_stackref )(MuCtx *ctx, MuBundleNode b); MuTypeNode (*new_type_framecursorref)(MuCtx *ctx, MuBundleNode b); MuTypeNode (*new_type_irnoderef )(MuCtx *ctx, MuBundleNode b);
The new_type_T functions create a node for the Mu type T, and add it to
the bundle b. Some functions take extra parameters:
new_type_int:lenis the length of the integer.new_type_struct:fieldtyspoints to an array of field types.nfieldtypesis the length of the array.new_type_hybrid:fixedtyspoints to an array of types in the fixed part.nfixedtypesis the length of the array.vartyis the type of the variable part.new_type_arrayandnew_type_vector:elemtyis the type of the element, andlenis the length of the array or vector.
The set_type_T functions fill in more details of types. These types are
references or pointers. The second parameter of these functions is the type to
modify. The last parameter is the data type, or the signature of the function
they refer to.
MuFuncSigNode (*new_funcsig )(MuCtx *ctx, MuBundleNode b, MuTypeNode *paramtys, MuArraySize nparamtys, MuTypeNode *rettys, MuArraySize nrettys);
new_funcsig creates a function signature node and add it to the bundle
b. paramtys and rettys point to its parameter types and return
types, and their lengths are nparamtys and nrettys, respectively.
MuConstNode (*new_const_int )(MuCtx *ctx, MuBundleNode b, MuTypeNode ty, uint64_t value); MuConstNode (*new_const_int_ex )(MuCtx *ctx, MuBundleNode b, MuTypeNode ty, uint64_t *values, MuArraySize nvalues); MuConstNode (*new_const_float )(MuCtx *ctx, MuBundleNode b, MuTypeNode ty, float value); MuConstNode (*new_const_double )(MuCtx *ctx, MuBundleNode b, MuTypeNode ty, double value); MuConstNode (*new_const_null )(MuCtx *ctx, MuBundleNode b, MuTypeNode ty); MuConstNode (*new_const_seq )(MuCtx *ctx, MuBundleNode b, MuTypeNode ty, MuConstNode *elems, MuArraySize nelems);
These functions create constant nodes and add them to the bundle b.
new_const_intandnew_const_int_excreate an integer constant. These functions are applicable forint,uptrandufuncptrtypes.tyis the type of the constant.new_const_intis used if the size of the constant is less than or equal to 64 bits.valueis the value. If the Mu type has less than 64 bits, it will be truncated.new_const_int_exis used if the size of the constant is greater than 64 bits. The value is segmented into 64-bit words from the least significant bits. Lower words are stored first in thevaluesarray, whose length isnvalues. The value is truncated if the size of the Mu type is not a multiple of 64 bits.
new_const_floatandnew_const_doublecreate constants of the
float type and the double type, respectively. ty must be
float or double. value is the value.
new_const_nullcreates aNULLconstant of general reference types.
ty is the type and must be a general reference type.
new_const_seqcan create constants ofstruct,arrayor
vector types. ty is the type. elems points to an array of other
constants which are the fields or elements. nelems is the length of the
array, and must match the actual number of fields or elements of the type.
All constants are created in one step, because constants cannot be recursive.
MuGlobalNode (*new_global_cell)(MuCtx *ctx, MuBundleNode b, MuTypeNode ty); MuFuncNode (*new_func )(MuCtx *ctx, MuBundleNode b, MuFuncSigNode sig); MuExpFuncNode (*new_exp_func )(MuCtx *ctx, MuBundleNode b, MuFuncNode func, MuCallConv callconv, MuConstNode cookie);
new_global_cellcreates a global cell node and adds it to the bundleb.tyis its type.new_funccreates a function node and adds it to the bundleb.sigis its signature. If a function node is created but the function version node is not, it declares a function without definition.new_exp_funccreates an exposed function node and adds it to the bundleb.funcis the function to expose;callconvis the calling convention;cookieis the cookie.
The calling conventions are defined as:
typedef MuFlag MuCallConv; #define MU_CC_DEFAULT ((MuCallConv)0x00)
MuFuncVerNode (*new_func_ver )(MuCtx *ctx, MuBundleNode b, MuFuncNode func);
The new_func_ver function creates a function version for function func
and adds it to the bundle b.
func may be created in the current bundle, and may also be created in a
previous bundle already loaded. In order to define a previously declared
function, or redefine an existing function, the client should use the
get_node function to get a handle to the function.
Example:
MuFuncNode old_func = ctx->get_node(ctx, b, the_id_of_the_old_func); MuFuncVerNode new_ver = ctx->new_func_ver(ctx, b, old_func);
MuBBNode (*new_bb )(MuCtx *ctx, MuFuncVerNode fv); MuNorParamNode (*new_nor_param )(MuCtx *ctx, MuBBNode bb, MuTypeNode ty); MuExcParamNode (*new_exc_param )(MuCtx *ctx, MuBBNode bb);
new_bbcreates a basic block node and adds it to the function versionfv. The first block added to a function version is the entry block.new_nor_paramadds a new normal parameter to a basic block.tyis the type of the parameter.new_exc_paramadds an exceptional parameter to the basic block. A basic block has at most one exceptional parameter.
MuInstResNode (*new_inst_res )(MuCtx *ctx, MuInstNode inst);
new_inst_res creates an instruction result node and adds it to the
instruction inst. Each instruction must have exactly as many result
nodes as the number of results it produces.
void (*add_dest )(MuCtx *ctx, MuInstNode inst, MuDestKind kind, MuBBNode dest, MuVarNode *vars, MuArraySize nvars); typedef MuFlag MuDestKind; #define MU_DEST_NORMAL ((MuDeskKind)0x01) #define MU_DEST_EXCEPT ((MuDeskKind)0x02) #define MU_DEST_TRUE ((MuDeskKind)0x03) #define MU_DEST_FALSE ((MuDeskKind)0x04) #define MU_DEST_DEFAULT ((MuDeskKind)0x05) #define MU_DEST_DISABLED ((MuDeskKind)0x06) #define MU_DEST_ENABLED ((MuDeskKind)0x07)
add_dest adds a destination clause to an instruction. dest is the basic
block to branch to. vars points to an array of the arguments to that basic
block, and nvars is its length.
kind can be one of the following:
MU_DEST_NORMAL: For theBRANCHinstruction, it is the only destination; for all other instructions that may have exception clauses, it is the normal destination.MU_DEST_EXCEPT: For instructions with exception clauses and theWATCHPOINTinstruction, it is the exceptional destination.MU_DEST_TRUE: Only used forBRAHCH2. It is the destination if the condition is true.MU_DEST_FALSE: Only used forBRAHCH2. It is the destination if the condition is false.MU_DEST_DEFAULT: Only used forSWITCH. It is the destination for all other values not covered by any of its cases.MU_DEST_DISABLED: ForWATCHPOINTandWPBRANCH, it is the destination when the watch point is disabled.MU_DEST_ENABLED: ForWATCHPOINT, it is the normal destination when the watch point is enabled; forWPBRANCH, it is the destination when the watch point is enabled.
void (*add_keepalives)(MuCtx *ctx, MuInstNode inst, MuLocalVarNode *vars, MuArraySize nvars);
add_keepalives adds keep-alive variables to the instruction inst.
inst must be an OSR point. vars points to the array of keep-alive
variables, and nvars is its length. This function can only be called once
per instruction.
TODO: Document all instruction-creating API functions.
MuInstNode (*new_binop )(MuCtx *ctx, MuBBNode bb, MuBinOptr optr, MuTypeNode ty, MuVarNode opnd1, MuVarNode opnd2);
MuInstNode (*new_cmp )(MuCtx *ctx, MuBBNode bb, MuCmpOptr optr, MuTypeNode ty, MuVarNode opnd1, MuVarNode opnd2);
MuInstNode (*new_conv )(MuCtx *ctx, MuBBNode bb, MuConvOptr optr, MuTypeNode from_ty, MuTypeNode to_ty, MuVarNode opnd);
MuInstNode (*new_select )(MuCtx *ctx, MuBBNode bb, MuTypeNode cond_ty, MuTypeNode opnd_ty, MuVarNode cond, MuVarNode if_true, MuVarNode if_false);
MuInstNode (*new_branch )(MuCtx *ctx, MuBBNode bb);
MuInstNode (*new_branch2 )(MuCtx *ctx, MuBBNode bb, MuVarNode cond);
MuInstNode (*new_switch )(MuCtx *ctx, MuBBNode bb, MuTypeNode opnd_ty, MuVarNode opnd);
void (*add_switch_dest )(MuCtx *ctx, MuInstNode sw, MuConstNode key, MuBBNode dest, MuVarNode *vars, MuArraySize nvars);
MuInstNode (*new_call )(MuCtx *ctx, MuBBNode bb, MuFuncSigNode sig, MuVarNode callee, MuVarNode *args, MuArraySize nargs);
MuInstNode (*new_tailcall )(MuCtx *ctx, MuBBNode bb, MuFuncSigNode sig, MuVarNode callee, MuVarNode *args, MuArraySize nargs);
MuInstNode (*new_ret )(MuCtx *ctx, MuBBNode bb, MuVarNode *rvs, MuArraySize nrvs);
MuInstNode (*new_throw )(MuCtx *ctx, MuBBNode bb, MuVarNode exc);
MuInstNode (*new_extractvalue )(MuCtx *ctx, MuBBNode bb, MuTypeNode strty, int index, MuVarNode opnd);
MuInstNode (*new_insertvalue )(MuCtx *ctx, MuBBNode bb, MuTypeNode strty, int index, MuVarNode opnd, MuVarNode newval);
MuInstNode (*new_extractelement)(MuCtx *ctx, MuBBNode bb, MuTypeNode seqty, MuTypeNode indty, MuVarNode opnd, MuVarNode index);
MuInstNode (*new_insertelement )(MuCtx *ctx, MuBBNode bb, MuTypeNode seqty, MuTypeNode indty, MuVarNode opnd, MuVarNode index, MuVarNode newval);
MuInstNode (*new_shufflevector )(MuCtx *ctx, MuBBNode bb, MuTypeNode vecty, MuTypeNode maskty, MuVarNode vec1, MuVarNode vec2, MuVarNode mask);
MuInstNode (*new_new )(MuCtx *ctx, MuBBNode bb, MuTypeNode allocty);
MuInstNode (*new_newhybrid )(MuCtx *ctx, MuBBNode bb, MuTypeNode allocty, MuTypeNode lenty, MuVarNode length);
MuInstNode (*new_alloca )(MuCtx *ctx, MuBBNode bb, MuTypeNode allocty);
MuInstNode (*new_allocahybrid )(MuCtx *ctx, MuBBNode bb, MuTypeNode allocty, MuTypeNode lenty, MuVarNode length);
MuInstNode (*new_getiref )(MuCtx *ctx, MuBBNode bb, MuTypeNode refty, MuVarNode opnd);
MuInstNode (*new_getfieldiref )(MuCtx *ctx, MuBBNode bb, MuBool is_ptr, MuTypeNode refty, int index, MuVarNode opnd);
MuInstNode (*new_getelemiref )(MuCtx *ctx, MuBBNode bb, MuBool is_ptr, MuTypeNode refty, MuTypeNode indty, MuVarNode opnd, MuVarNode index);
MuInstNode (*new_shiftiref )(MuCtx *ctx, MuBBNode bb, MuBool is_ptr, MuTypeNode refty, MuTypeNode offty, MuVarNode opnd, MuVarNode offset);
MuInstNode (*new_getvarpartiref)(MuCtx *ctx, MuBBNode bb, MuBool is_ptr, MuTypeNode refty, MuVarNode opnd);
MuInstNode (*new_load )(MuCtx *ctx, MuBBNode bb, MuBool is_ptr, MuMemOrd ord, MuTypeNode refty, MuVarNode loc);
MuInstNode (*new_store )(MuCtx *ctx, MuBBNode bb, MuBool is_ptr, MuMemOrd ord, MuTypeNode refty, MuVarNode loc, MuVarNode newval);
MuInstNode (*new_cmpxchg )(MuCtx *ctx, MuBBNode bb, MuBool is_ptr, MuBool is_weak, MuMemOrd ord_succ, MuMemOrd ord_fail, MuTypeNode refty, MuVarNode loc, MuVarNode expected, MuVarNode desired);
MuInstNode (*new_atomicrmw )(MuCtx *ctx, MuBBNode bb, MuBool is_ptr, MuMemOrd ord, MuAtomicRMWOptr optr, MuTypeNode refTy, MuVarNode loc, MuVarNode opnd);
MuInstNode (*new_fence )(MuCtx *ctx, MuBBNode bb, MuMemOrd ord);
MuInstNode (*new_trap )(MuCtx *ctx, MuBBNode bb, MuTypeNode *rettys, MuArraySize nrettys);
MuInstNode (*new_watchpoint )(MuCtx *ctx, MuBBNode bb, MuWPID wpid, MuTypeNode *rettys, MuArraySize nrettys);
MuInstNode (*new_wpbranch )(MuCtx *ctx, MuBBNode bb, MuWPID wpid);
MuInstNode (*new_ccall )(MuCtx *ctx, MuBBNode bb, MuCallConv callconv, MuTypeNode callee_ty, MuFuncSigNode sig, MuVarNode callee, MuVarNode *args, MuArraySize nargs);
MuInstNode (*new_newthread )(MuCtx *ctx, MuBBNode bb, MuVarNode stack, MuVarNode threadlocal);
MuInstNode (*new_swapstack_ret )(MuCtx *ctx, MuBBNode bb, MuVarNode swappee, MuTypeNode *ret_tys, MuArraySize nret_tys);
MuInstNode (*new_swapstack_kill)(MuCtx *ctx, MuBBNode bb, MuVarNode swappee);
void (*set_newstack_pass_values)(MuCtx *ctx, MuInstNode inst, MuTypeNode *tys, MuVarNode *vars, MuArraySize nvars);
void (*set_newstack_throw_exc )(MuCtx *ctx, MuInstNode inst, MuVarNode exc);
MuInstNode (*new_comminst )(MuCtx *ctx, MuBBNode bb, MuCommInst opcode,
MuFlag *flags, MuArraySize nflags,
MuTypeNode *tys, MuArraySize ntys,
MuFuncSigNode *sigs, MuArraySize nsigs,
MuVarNode *args, MuArraySize nargs);