skip to content
==> src/imports.c <==
1/* LaneData and the mesh::* imports (mesh-host/src/wasm.rs `LaneData`, `linker`). */
2#include "mesh/exec_wasm_imports.h"
3
4#include "mesh/abi/control_block.h"
5#include "mesh/abi/exchange.h"
6#include "mesh/abi/ext.h"
7#include "mesh/abi/fault.h"
8#include "mesh/abi/header.h"
9#include "mesh/abi/input_queue.h"
10#include "mesh/abi/pipe.h"
11#include "mesh/abi/program_table.h"
12#include "mesh/abi/wasm.h"
13#include "mesh/lease.h"
14
15#include <string.h>
16
17static size_t ctl(const mesh_wasm_lane_data *d, size_t off) { return mesh_prog_ctl(d->slot) + off; }
18
19void mesh_wasm_lane_data_init(mesh_wasm_lane_data *d, mesh_mailbox mb, uint32_t slot, uint32_t node, uint32_t nodes,
20                              uint32_t task_ms, const _Atomic uint64_t *epoch) {
21    memset(d, 0, sizeof *d);
22    size_t e = mesh_prog_entry(slot);
23    d->mb = mb;
24    d->slot = slot;
25    d->node = node;
26    d->nodes = nodes;
27    d->view_off = mesh_mailbox_get(mb, e + MESH_PT_VIEW_OFF);
28    d->view_len = mesh_mailbox_get(mb, e + MESH_PT_VIEW_BYTES);
29    d->req_off = mesh_mailbox_get(mb, e + MESH_PT_REQ_OFF);
30    d->req_max = mesh_mailbox_get(mb, e + MESH_PT_REQ_MAX);
31    d->resp_off = mesh_mailbox_get(mb, e + MESH_PT_RESP_OFF);
32    d->resp_max = mesh_mailbox_get(mb, e + MESH_PT_RESP_MAX);
33    d->input_off = mesh_mailbox_get(mb, e + MESH_PT_INPUT_OFF);
34    d->input_len = mesh_mailbox_get(mb, e + MESH_PT_INPUT_LEN);
35    d->lockstep = mesh_mailbox_get(mb, e + MESH_PT_PEER) == MESH_PEER_MODE_LOCKSTEP;
36    d->ticks = task_ms;
37    d->epoch = epoch;
38}
39
40void mesh_wasm_lane_data_task(mesh_wasm_lane_data *d, uint32_t ticket) {
41    d->ticket = ticket;
42    memset(d->peer_set, 0, sizeof d->peer_set);
43}
44
45void mesh_wasm_lane_fault(const mesh_wasm_lane_data *d, uint32_t code) {
46    if (mesh_mailbox_get(d->mb, ctl(d, MESH_P_FAULT)) == 0) mesh_mailbox_cas(d->mb, ctl(d, MESH_P_FAULT), 0, code);
47}
48
49static bool window(const mesh_wasm_lane_data *d, uint32_t h, size_t *base, uint32_t *len) {
50    if (h == MESH_WASM_W_VIEW) {
51        *base = d->view_off;
52        *len = d->view_len;
53        return true;
54    }
55    if (h == MESH_WASM_W_EXCHANGE && d->lockstep) {
56        *base = MESH_EX_BASE;
57        *len = MESH_EX_BYTES;
58        return true;
59    }
60    if (h >= MESH_WASM_W_PEER && h < MESH_WASM_W_PEER + MESH_PROG_SLOTS && d->peer_set[h - MESH_WASM_W_PEER]) {
61        *base = d->peer_off[h - MESH_WASM_W_PEER];
62        *len = d->peer_len[h - MESH_WASM_W_PEER];
63        return true;
64    }
65    return false;
66}
67
68bool mesh_wasm_lane_at(const mesh_wasm_lane_data *d, uint32_t h, uint32_t off, uint32_t n, size_t align, size_t *at) {
69    size_t base;
70    uint32_t len;
71    if (!window(d, h, &base, &len)) {
72        mesh_wasm_lane_fault(d, h >= MESH_WASM_W_PEER ? MESH_FAULT_PEER_VIEW | (h - MESH_WASM_W_PEER) << 8
73                                                      : MESH_FAULT_VIEW_BOUNDS | off << 8);
74        return false;
75    }
76    size_t a = base + off;
77    if ((uint64_t)off + n > len || a % align != 0) {
78        mesh_wasm_lane_fault(d, MESH_FAULT_VIEW_BOUNDS | off << 8);
79        return false;
80    }
81    *at = a;
82    return true;
83}
84
85uint32_t mesh_wasm_lane_reads_mask(const mesh_wasm_lane_data *d) {
86    size_t e = mesh_prog_entry(d->slot);
87    for (;;) {
88        uint32_t g = mesh_mailbox_get(d->mb, e + MESH_PT_GEN);
89        if (g & 1) continue;
90        uint32_t m = mesh_mailbox_get(d->mb, e + MESH_PT_READS_MASK);
91        atomic_thread_fence(memory_order_seq_cst);
92        if (mesh_mailbox_get(d->mb, e + MESH_PT_GEN) == g) return m;
93    }
94}
95
96int32_t mesh_wasm_lane_peer(mesh_wasm_lane_data *d, uint32_t s) {
97    if (s >= MESH_PROG_SLOTS) return -1;
98    if ((mesh_wasm_lane_reads_mask(d) & (1u << s)) == 0) {
99        mesh_wasm_lane_fault(d, MESH_FAULT_PEER_VIEW | s << 8);
100        return -1;
101    }
102    size_t e = mesh_prog_entry(s);
103    uint32_t st, off, len;
104    for (;;) {
105        uint32_t g = mesh_mailbox_get(d->mb, e + MESH_PT_GEN);
106        if (g & 1) continue;
107        st = mesh_mailbox_get(d->mb, e + MESH_PT_STATE);
108        off = mesh_mailbox_get(d->mb, e + MESH_PT_VIEW_OFF);
109        len = mesh_mailbox_get(d->mb, e + MESH_PT_VIEW_BYTES);
110        atomic_thread_fence(memory_order_seq_cst);
111        if (mesh_mailbox_get(d->mb, e + MESH_PT_GEN) == g) break;
112    }
113    if (st == MESH_ST_EMPTY || st == MESH_ST_FAILED) {
114        d->peer_set[s] = false;
115        return -1;
116    }
117    d->peer_set[s] = true;
118    d->peer_off[s] = off;
119    d->peer_len[s] = len;
120    return (int32_t)len;
121}
122
123void mesh_wasm_copy_out(mesh_mailbox mb, size_t a, uint8_t *dst, size_t n) {
124    if (a % 4 == 0 && n % 4 == 0) {
125        for (size_t i = 0; i < n / 4; i++) {
126            uint32_t w = mesh_mailbox_get(mb, a + 4 * i);
127            memcpy(dst + 4 * i, &w, 4); /* little-endian hosts only, as the mailbox */
128        }
129    } else {
130        for (size_t i = 0; i < n; i++) dst[i] = mesh_mailbox_get8(mb, a + i);
131    }
132}
133
134void mesh_wasm_copy_in(mesh_mailbox mb, size_t a, const uint8_t *src, size_t n) {
135    if (a % 4 == 0 && n % 4 == 0) {
136        for (size_t i = 0; i < n / 4; i++) {
137            uint32_t w;
138            memcpy(&w, src + 4 * i, 4);
139            mesh_mailbox_publish(mb, a + 4 * i, w);
140        }
141    } else {
142        for (size_t i = 0; i < n; i++) mesh_mailbox_set8(mb, a + i, src[i]);
143    }
144}
145
146/* ---- the imports ------------------------------------------------------------------------ */
147
148#define D mesh_wasm_lane_data *d = wasm_instance_user(inst)
149#define A(i) ((uint32_t)io[i])
150#define UNUSED (void)ctx
151
152/* Linear memory [ptr, ptr + len) of the instance, or NULL. */
153static uint8_t *span(wasm_instance *inst, uint32_t ptr, uint32_t len) {
154    uint64_t size;
155    uint8_t *mem = wasm_memory(inst, &size);
156    if (!mem || (uint64_t)ptr + len > size) return NULL;
157    return mem + ptr;
158}
159
160static int info(wasm_instance *inst, void *ctx, uint64_t *io) {
161    UNUSED;
162    D;
163    mesh_mailbox mb = d->mb;
164    uint32_t v;
165    switch (A(0)) {
166    case MESH_WASM_I_SLOT: v = d->slot; break;
167    case MESH_WASM_I_NODE: v = d->node; break;
168    case MESH_WASM_I_NODES: v = d->nodes; break;
169    case MESH_WASM_I_TICKET: v = d->ticket; break;
170    case MESH_WASM_I_SYSTEM: v = mesh_mailbox_get(mb, MESH_H_PEER); break;
171    case MESH_WASM_I_SYSTEMS: v = mesh_mailbox_get(mb, MESH_H_SYSTEMS); break;
172    case MESH_WASM_I_GROUP_RANK: v = mesh_mailbox_get(mb, MESH_H_GROUP_RANK); break;
173    case MESH_WASM_I_GROUP_N: v = mesh_mailbox_get(mb, MESH_H_GROUP_N); break;
174    case MESH_WASM_I_PARAM: v = mesh_mailbox_get(mb, mesh_prog_ext(d->slot) + MESH_PX_PARAM); break;
175    case MESH_WASM_I_SMS: v = mesh_mailbox_get(mb, MESH_H_SMS); break;
176    case MESH_WASM_I_VIEW_BYTES: v = d->view_len; break;
177    case MESH_WASM_I_REQ_LEN: {
178        uint32_t n = mesh_mailbox_get(mb, ctl(d, MESH_P_REQ_LEN));
179        v = n < d->req_max ? n : d->req_max;
180        break;
181    }
182    case MESH_WASM_I_REQ_OFF: v = d->req_off; break;
183    case MESH_WASM_I_RESP_OFF: v = d->resp_off; break;
184    case MESH_WASM_I_RESP_MAX: v = d->resp_max; break;
185    case MESH_WASM_I_INPUT_LEN: v = d->input_len; break;
186    case MESH_WASM_I_LOCKSTEP: v = d->lockstep; break;
187    case MESH_WASM_I_READS_MASK: v = mesh_wasm_lane_reads_mask(d); break;
188    default: v = 0; break;
189    }
190    io[0] = v;
191    return WASM_NUM_OK;
192}
193
194static int ld8(wasm_instance *inst, void *ctx, uint64_t *io) {
195    UNUSED;
196    D;
197    size_t a;
198    io[0] = mesh_wasm_lane_at(d, A(0), A(1), 1, 1, &a) ? mesh_mailbox_get8(d->mb, a) : 0;
199    return WASM_NUM_OK;
200}
201
202static int ld32(wasm_instance *inst, void *ctx, uint64_t *io) {
203    UNUSED;
204    D;
205    size_t a;
206    io[0] = mesh_wasm_lane_at(d, A(0), A(1), 4, 4, &a) ? mesh_mailbox_get(d->mb, a) : 0;
207    return WASM_NUM_OK;
208}
209
210static int ld64(wasm_instance *inst, void *ctx, uint64_t *io) {
211    UNUSED;
212    D;
213    size_t a;
214    io[0] = mesh_wasm_lane_at(d, A(0), A(1), 8, 8, &a) ? mesh_mailbox_get64(d->mb, a) : 0;
215    return WASM_NUM_OK;
216}
217
218static int st8(wasm_instance *inst, void *ctx, uint64_t *io) {
219    UNUSED;
220    D;
221    size_t a;
222    if (mesh_wasm_lane_at(d, A(0), A(1), 1, 1, &a)) mesh_mailbox_set8(d->mb, a, (uint8_t)A(2));
223    return WASM_NUM_OK;
224}
225
226static int st32(wasm_instance *inst, void *ctx, uint64_t *io) {
227    UNUSED;
228    D;
229    size_t a;
230    if (mesh_wasm_lane_at(d, A(0), A(1), 4, 4, &a)) mesh_mailbox_publish(d->mb, a, A(2));
231    return WASM_NUM_OK;
232}
233
234static int st64(wasm_instance *inst, void *ctx, uint64_t *io) {
235    UNUSED;
236    D;
237    size_t a;
238    if (mesh_wasm_lane_at(d, A(0), A(1), 8, 8, &a)) mesh_mailbox_publish64(d->mb, a, io[2]);
239    return WASM_NUM_OK;
240}
241
242static int cas32(wasm_instance *inst, void *ctx, uint64_t *io) {
243    UNUSED;
244    D;
245    size_t a;
246    io[0] = mesh_wasm_lane_at(d, A(0), A(1), 4, 4, &a) ? mesh_mailbox_cas(d->mb, a, A(2), A(3)) : 0;
247    return WASM_NUM_OK;
248}
249
250static int add32(wasm_instance *inst, void *ctx, uint64_t *io) {
251    UNUSED;
252    D;
253    size_t a;
254    io[0] = mesh_wasm_lane_at(d, A(0), A(1), 4, 4, &a) ? mesh_mailbox_fetch_add(d->mb, a, A(2)) : 0;
255    return WASM_NUM_OK;
256}
257
258static int add64(wasm_instance *inst, void *ctx, uint64_t *io) {
259    UNUSED;
260    D;
261    size_t a;
262    io[0] = mesh_wasm_lane_at(d, A(0), A(1), 8, 8, &a) ? mesh_mailbox_fetch_add64(d->mb, a, io[2]) : 0;
263    return WASM_NUM_OK;
264}
265
266static int read_(wasm_instance *inst, void *ctx, uint64_t *io) {
267    UNUSED;
268    D;
269    uint64_t size;
270    if (!wasm_memory(inst, &size)) return WASM_NUM_OK;
271    size_t a;
272    if (!mesh_wasm_lane_at(d, A(0), A(1), A(3), 1, &a)) return WASM_NUM_OK;
273    uint8_t *dst = span(inst, A(2), A(3));
274    if (!dst) {
275        mesh_wasm_lane_fault(d, MESH_FAULT_VIEW_BOUNDS | A(1) << 8);
276        return WASM_NUM_OK;
277    }
278    mesh_wasm_copy_out(d->mb, a, dst, A(3));
279    return WASM_NUM_OK;
280}
281
282static int write_(wasm_instance *inst, void *ctx, uint64_t *io) {
283    UNUSED;
284    D;
285    uint64_t size;
286    if (!wasm_memory(inst, &size)) return WASM_NUM_OK;
287    size_t a;
288    if (!mesh_wasm_lane_at(d, A(0), A(1), A(3), 1, &a)) return WASM_NUM_OK;
289    const uint8_t *src = span(inst, A(2), A(3));
290    if (!src) {
291        mesh_wasm_lane_fault(d, MESH_FAULT_VIEW_BOUNDS | A(1) << 8);
292        return WASM_NUM_OK;
293    }
294    mesh_wasm_copy_in(d->mb, a, src, A(3));
295    return WASM_NUM_OK;
296}
297
298static int peer(wasm_instance *inst, void *ctx, uint64_t *io) {
299    UNUSED;
300    D;
301    io[0] = (uint32_t)mesh_wasm_lane_peer(d, A(0));
302    return WASM_NUM_OK;
303}
304
305static int find(wasm_instance *inst, void *ctx, uint64_t *io) {
306    UNUSED;
307    D;
308    int32_t found = -1;
309    uint64_t size;
310    const uint8_t *name = wasm_memory(inst, &size) ? span(inst, A(0), A(1)) : NULL;
311    uint32_t len = A(1);
312    if (name && len <= MESH_NAME_MAX) {
313        for (uint32_t s = 0; s < MESH_PROG_SLOTS && found < 0; s++) {
314            size_t e = mesh_prog_entry(s);
315            if (mesh_mailbox_get(d->mb, e + MESH_PT_STATE) == MESH_ST_EMPTY) continue;
316            bool same = true;
317            for (uint32_t i = 0; i < MESH_NAME_BYTES && same; i++)
318                same = mesh_mailbox_get8(d->mb, e + MESH_PT_NAME + i) == (i < len ? name[i] : 0);
319            if (same) found = (int32_t)s;
320        }
321    }
322    io[0] = (uint32_t)found;
323    return WASM_NUM_OK;
324}
325
326static int prog_state(wasm_instance *inst, void *ctx, uint64_t *io) {
327    UNUSED;
328    D;
329    io[0] = A(0) < MESH_PROG_SLOTS ? mesh_mailbox_get(d->mb, mesh_prog_entry(A(0)) + MESH_PT_STATE) : MESH_ST_EMPTY;
330    return WASM_NUM_OK;
331}
332
333static int count(wasm_instance *inst, void *ctx, uint64_t *io) {
334    UNUSED;
335    D;
336    if (A(0) < MESH_COUNTERS) mesh_mailbox_fetch_add(d->mb, ctl(d, MESH_P_COUNTERS + 4 * (size_t)A(0)), A(1));
337    return WASM_NUM_OK;
338}
339
340static int counter(wasm_instance *inst, void *ctx, uint64_t *io) {
341    UNUSED;
342    D;
343    io[0] = A(0) < MESH_COUNTERS ? mesh_mailbox_get(d->mb, ctl(d, MESH_P_COUNTERS + 4 * (size_t)A(0))) : 0;
344    return WASM_NUM_OK;
345}
346
347static int input_pending(wasm_instance *inst, void *ctx, uint64_t *io) {
348    UNUSED;
349    D;
350    if (d->input_len == 0) {
351        io[0] = 0;
352        return WASM_NUM_OK;
353    }
354    size_t q = d->view_off + d->input_off;
355    io[0] = (uint32_t)(mesh_mailbox_get(d->mb, q + MESH_IQ_HEAD) - mesh_mailbox_get(d->mb, q + MESH_IQ_TAIL));
356    return WASM_NUM_OK;
357}
358
359static int input_pop(wasm_instance *inst, void *ctx, uint64_t *io) {
360    UNUSED;
361    D;
362    io[0] = (uint32_t)-1;
363    if (d->input_len == 0) return WASM_NUM_OK;
364    size_t q = d->view_off + d->input_off;
365    uint32_t tail = mesh_mailbox_get(d->mb, q + MESH_IQ_TAIL);
366    if (mesh_mailbox_get(d->mb, q + MESH_IQ_HEAD) == tail) return WASM_NUM_OK;
367    uint8_t b = mesh_mailbox_get8(d->mb, q + MESH_IQ_DATA + (tail & (d->input_len - 1)));
368    mesh_mailbox_publish(d->mb, q + MESH_IQ_TAIL, tail + 1u);
369    io[0] = b;
370    return WASM_NUM_OK;
371}
372
373static int respond(wasm_instance *inst, void *ctx, uint64_t *io) {
374    UNUSED;
375    D;
376    uint32_t len = A(0);
377    if (len > d->resp_max) {
378        mesh_wasm_lane_fault(d, MESH_FAULT_RESP_BOUNDS | len << 8);
379        len = d->resp_max;
380    }
381    mesh_mailbox_set(d->mb, ctl(d, MESH_P_RESP_LEN), len);
382    mesh_mailbox_publish64(d->mb, ctl(d, MESH_P_RESP_SEQ), mesh_mailbox_get64(d->mb, ctl(d, MESH_P_REQ_SEQ)));
383    return WASM_NUM_OK;
384}
385
386static int exchange(wasm_instance *inst, void *ctx, uint64_t *io) {
387    UNUSED;
388    D;
389    if (!d->lockstep) {
390        mesh_wasm_lane_fault(d, MESH_FAULT_PEER_VIEW | 0xffu << 8);
391        io[0] = MESH_ROUND_TIMEOUT;
392        return WASM_NUM_OK;
393    }
394    io[0] = mesh_round(d->mb, io[0], NULL);
395    /* The wait is the cluster's, not the program's: the task's budget starts over. */
396    if (d->epoch) wasm_set_deadline(inst, atomic_load(d->epoch) + d->ticks);
397    return WASM_NUM_OK;
398}
399
400/* Pipes (mesh-abi pipe): records straight between linear memory and the ring. */
401static int pipe_push(wasm_instance *inst, void *ctx, uint64_t *io) {
402    UNUSED;
403    D;
404    uint32_t to = A(0), ptr = A(1), len = A(2);
405    uint64_t size;
406    if (!wasm_memory(inst, &size)) {
407        io[0] = (uint32_t)MESH_PIPE_SIZE;
408        return WASM_NUM_OK;
409    }
410    const uint8_t *rec = span(inst, ptr, len);
411    if (!rec) {
412        mesh_wasm_lane_fault(d, MESH_FAULT_VIEW_BOUNDS | ptr << 8);
413        io[0] = (uint32_t)MESH_PIPE_SIZE;
414        return WASM_NUM_OK;
415    }
416    mesh_words w = mesh_mailbox_words(d->mb);
417    int r = mesh_pipe_push(&w, d->slot, to, rec, len);
418    if (r == -MESH_PIPE_ERR_NOT_CONNECTED || r == -MESH_PIPE_ERR_SIZE) mesh_wasm_lane_fault(d, MESH_FAULT_PIPE | to << 8);
419    io[0] = (uint32_t)mesh_pipe_code(r);
420    return WASM_NUM_OK;
421}
422
423static int pipe_pop(wasm_instance *inst, void *ctx, uint64_t *io) {
424    UNUSED;
425    D;
426    uint32_t ptr = A(0), len = A(1);
427    uint64_t size;
428    if (!wasm_memory(inst, &size)) {
429        io[0] = (uint32_t)MESH_PIPE_SIZE;
430        return WASM_NUM_OK;
431    }
432    uint8_t *out = span(inst, ptr, len);
433    if (!out) {
434        mesh_wasm_lane_fault(d, MESH_FAULT_VIEW_BOUNDS | ptr << 8);
435        io[0] = (uint32_t)MESH_PIPE_SIZE;
436        return WASM_NUM_OK;
437    }
438    mesh_words w = mesh_mailbox_words(d->mb);
439    int r = mesh_pipe_pop(&w, d->slot, out, len);
440    if (r == -MESH_PIPE_ERR_NOT_CONNECTED || r == -MESH_PIPE_ERR_SIZE) mesh_wasm_lane_fault(d, MESH_FAULT_PIPE | d->slot << 8);
441    io[0] = (uint32_t)mesh_pipe_code(r);
442    return WASM_NUM_OK;
443}
444
445static int pipe_len(wasm_instance *inst, void *ctx, uint64_t *io) {
446    UNUSED;
447    D;
448    mesh_words w = mesh_mailbox_words(d->mb);
449    mesh_pipe_inbox in;
450    int r = mesh_pipe_target(&w, d->slot, A(0), &in);
451    io[0] = r == 0 ? (uint32_t)(int32_t)mesh_pipe_inbox_len(&in, &w) : (uint32_t)mesh_pipe_code(r);
452    return WASM_NUM_OK;
453}
454
455static int host_ns(wasm_instance *inst, void *ctx, uint64_t *io) {
456    (void)inst, (void)ctx;
457    io[0] = mesh_host_ns();
458    return WASM_NUM_OK;
459}
460
461static int fault(wasm_instance *inst, void *ctx, uint64_t *io) {
462    UNUSED;
463    D;
464    mesh_wasm_lane_fault(d, A(0));
465    return WASM_NUM_OK;
466}
467
468static const wasm_host_func IMPORTS[] = {
469    {MESH_WASM_MODULE, "info", "i>i", info, NULL},
470    {MESH_WASM_MODULE, "ld8", "ii>i", ld8, NULL},
471    {MESH_WASM_MODULE, "ld32", "ii>i", ld32, NULL},
472    {MESH_WASM_MODULE, "ld64", "ii>l", ld64, NULL},
473    {MESH_WASM_MODULE, "st8", "iii>", st8, NULL},
474    {MESH_WASM_MODULE, "st32", "iii>", st32, NULL},
475    {MESH_WASM_MODULE, "st64", "iil>", st64, NULL},
476    {MESH_WASM_MODULE, "cas32", "iiii>i", cas32, NULL},
477    {MESH_WASM_MODULE, "add32", "iii>i", add32, NULL},
478    {MESH_WASM_MODULE, "add64", "iil>l", add64, NULL},
479    {MESH_WASM_MODULE, "read", "iiii>", read_, NULL},
480    {MESH_WASM_MODULE, "write", "iiii>", write_, NULL},
481    {MESH_WASM_MODULE, "peer", "i>i", peer, NULL},
482    {MESH_WASM_MODULE, "find", "ii>i", find, NULL},
483    {MESH_WASM_MODULE, "prog_state", "i>i", prog_state, NULL},
484    {MESH_WASM_MODULE, "count", "ii>", count, NULL},
485    {MESH_WASM_MODULE, "counter", "i>i", counter, NULL},
486    {MESH_WASM_MODULE, "input_pending", ">i", input_pending, NULL},
487    {MESH_WASM_MODULE, "input_pop", ">i", input_pop, NULL},
488    {MESH_WASM_MODULE, "respond", "i>", respond, NULL},
489    {MESH_WASM_MODULE, "exchange", "l>i", exchange, NULL},
490    {MESH_WASM_MODULE, "pipe_push", "iii>i", pipe_push, NULL},
491    {MESH_WASM_MODULE, "pipe_pop", "ii>i", pipe_pop, NULL},
492    {MESH_WASM_MODULE, "pipe_len", "i>i", pipe_len, NULL},
493    {MESH_WASM_MODULE, "host_ns", ">l", host_ns, NULL},
494    {MESH_WASM_MODULE, "fault", "i>", fault, NULL},
495};
496
497const wasm_host_func *mesh_wasm_imports(size_t *n) {
498    *n = sizeof IMPORTS / sizeof IMPORTS[0];
499    return IMPORTS;
500}