summaryrefslogtreecommitdiff
path: root/vere/pkg/noun/jets/e/urwasm.c
diff options
context:
space:
mode:
authorpolwex <polwex@sortug.com>2025-10-05 21:56:51 +0700
committerpolwex <polwex@sortug.com>2025-10-05 21:56:51 +0700
commitfcedfddf00b3f994e4f4e40332ac7fc192c63244 (patch)
tree51d38e62c7bdfcc5f9a5e9435fe820c93cfc9a3d /vere/pkg/noun/jets/e/urwasm.c
claude is gud
Diffstat (limited to 'vere/pkg/noun/jets/e/urwasm.c')
-rw-r--r--vere/pkg/noun/jets/e/urwasm.c3086
1 files changed, 3086 insertions, 0 deletions
diff --git a/vere/pkg/noun/jets/e/urwasm.c b/vere/pkg/noun/jets/e/urwasm.c
new file mode 100644
index 0000000..626aef3
--- /dev/null
+++ b/vere/pkg/noun/jets/e/urwasm.c
@@ -0,0 +1,3086 @@
+/// @file
+
+#include "jets/k.h"
+#include "jets/q.h"
+#include "jets/w.h"
+
+#include "noun.h"
+
+#include "wasm3.h"
+#include "m3_env.h"
+
+// #define URWASM_SUBROAD
+#define URWASM_STATEFUL
+
+#define ONCE_CTX 63
+#define RUN_CTX 7
+
+#define AX_RUNNABLE 374
+#define AX_ARROWS 1502
+
+#define AX_CALL 20
+#define AX_MEMREAD 383
+#define AX_MEMWRITE 94
+#define AX_CALL_EXT 375
+#define AX_GLOBAL_SET 4
+#define AX_GLOBAL_GET 22
+#define AX_MEM_SIZE 186
+#define AX_MEM_GROW 381
+#define AX_GET_ACC 374
+#define AX_SET_ACC 92
+#define AX_GET_ALL_GLOB 43
+#define AX_SET_ALL_GLOB 380
+
+#define AX_TRY 43
+#define AX_CATCH 4
+#define AX_RETURN 20
+#define AX_FAIL 47
+
+#define ARROW_CTX 511
+#define MONAD_CTX 127
+
+#define arr_sam 62
+#define arr_sam_2 124
+#define arr_sam_3 125
+#define arr_sam_6 250
+#define arr_sam_7 251
+
+#define seed_module 2
+#define seed_past 6
+#define seed_shop 14
+#define seed_import 15
+
+#define uw__lia c3_s3('l', 'i', 'a')
+
+#define uw_lia_run_version 1
+
+#define ERR(string) ("\r\n\033[31m>>> " string "\033[0m\r\n")
+#define WUT(string) ("\r\n\033[33m>> " string "\033[0m\r\n")
+#define DBG(string) ("\r\n" string "\r\n")
+
+#define KICK1(TRAP) uw_kick_nock(TRAP, 2)
+#define KICK2(TRAP) KICK1(KICK1(TRAP))
+
+// [a b c d e f g h]
+static inline u3_noun
+uw_octo(u3_noun a,
+ u3_noun b,
+ u3_noun c,
+ u3_noun d,
+ u3_noun e,
+ u3_noun f,
+ u3_noun g,
+ u3_noun h)
+{
+ return u3nc(a, u3nq(b, c, d, u3nq(e, f, g, h)));
+}
+
+// kick by nock. axe RETAINED (ignore if direct)
+static u3_noun
+uw_kick_nock(u3_noun cor, u3_noun axe)
+{
+ u3_noun fol = u3x_at(axe, cor);
+ return u3n_nock_on(cor, u3k(fol));
+}
+
+// slam by nock
+static u3_noun
+uw_slam_nock(u3_noun gat, u3_noun sam)
+{
+ u3_noun cor = u3nc(u3k(u3h(gat)), u3nc(sam, u3k(u3t(u3t(gat)))));
+ u3z(gat);
+ return uw_kick_nock(cor, 2);
+}
+
+static u3_noun
+uw_slam_check(u3_noun gat, u3_noun sam, c3_t is_stateful)
+{
+ u3_noun bat = u3k(u3h(gat));
+ u3_noun cor = u3nc(u3k(bat), u3nc(sam, u3k(u3t(u3t(gat)))));
+ u3z(gat);
+
+ if (!is_stateful)
+ {
+ return u3n_nock_on(cor, bat);
+ }
+ else
+ {
+ u3_noun ton = u3n_nock_an(cor, bat);
+
+ u3_noun tag, pro;
+ if (c3n == u3r_cell(ton, &tag, &pro))
+ {
+ return u3m_bail(c3__fail);
+ }
+ if (0 == tag)
+ {
+ u3k(pro);
+ u3z(ton);
+ return pro;
+ }
+ else if (2 == tag)
+ {
+ return u3m_bail(c3__exit);
+ }
+ else
+ {
+ return u3m_bail(c3__fail);
+ }
+ }
+}
+
+static inline void
+_push_list(u3_noun som, u3_noun *lit)
+{
+ if (u3_none == *lit)
+ {
+ u3z(som);
+ }
+ else
+ {
+ *lit = u3nc(som, *lit);
+ }
+}
+
+static inline u3_weak
+_pop_list(u3_weak *lit)
+{
+ if (u3_none == *lit)
+ {
+ return u3_none;
+ }
+ u3_noun hed, tel;
+ if (c3n == u3r_cell(*lit, &hed, &tel))
+ {
+ return u3m_bail(c3__fail);
+ }
+ u3k(hed);
+ u3k(tel);
+ u3z(*lit);
+ *lit = tel;
+ return hed;
+}
+
+static const M3Result UrwasmArrowExit = "An imported arrow returned %2";
+
+static const c3_m uw_run_m = uw__lia + c3__run + uw_lia_run_version;
+
+static_assert(
+ (c3y == u3a_is_cat(uw_run_m)),
+ "u3we_run key tag must be a direct atom"
+);
+
+typedef struct {
+ u3_noun call_bat;
+ u3_noun memread_bat;
+ u3_noun memwrite_bat;
+ u3_noun call_ext_bat;
+ u3_noun try_bat;
+ u3_noun catch_bat;
+ u3_noun return_bat;
+ u3_noun fail_bat;
+ u3_noun global_set_bat;
+ u3_noun global_get_bat;
+ u3_noun mem_grow_bat;
+ u3_noun mem_size_bat;
+ u3_noun get_acc_bat;
+ u3_noun set_acc_bat;
+ u3_noun get_all_glob_bat;
+ u3_noun set_all_glob_bat;
+//
+ u3_noun call_ctx;
+ u3_noun memread_ctx;
+ u3_noun memwrite_ctx;
+ u3_noun global_set_ctx;
+ u3_noun global_get_ctx;
+ u3_noun mem_grow_ctx;
+ u3_noun mem_size_ctx;
+ u3_noun get_all_glob_ctx;
+ u3_noun set_all_glob_ctx;
+} match_data_struct;
+
+// memory arena with exponential growth
+typedef struct {
+ c3_w siz_w; // size in bytes
+ c3_y pad_y; // alignment padding
+ c3_t ini_t; // already initialized
+ u3i_slab sab_u; // associated slab
+ c3_y* buf_y; // allocated buffer
+ c3_y* nex_y; // next allocation
+ c3_y* end_y; // end of arena
+ jmp_buf* esc_u; // escape buffer
+} uw_arena;
+
+typedef struct {
+ IM3Module wasm_module; // p
+ u3_noun lia_shop; // q, transferred
+ u3_noun acc; // p.r, transferred
+ u3_noun map; // q.r, retained
+ match_data_struct* match;
+ u3_noun arrow_yil; // transferred
+ u3_noun susp_list; // transferred
+ u3_noun resolution; // resolved %1 block, transferred
+ uw_arena box_arena;
+ uw_arena code_arena;
+ u3_noun yil_previous; // transferred
+ u3_noun queue; // transferred
+ c3_t is_stateful;
+} lia_state;
+
+typedef enum {
+ west_call,
+ west_call_ext,
+ west_try,
+ west_catch_try,
+ west_catch_err,
+ west_link_wasm,
+} wasm3_ext_suspend_tag;
+
+typedef enum {
+ lst_call = 0,
+ // lst_call_ext = 1, // not necessary
+ lst_try = 2,
+ lst_catch_try = 3,
+ lst_catch_err = 4,
+ lst_link_wasm = 5,
+} lia_suspend_tag;
+
+static void
+_uw_arena_init_size(uw_arena* ren_u, c3_w siz_w)
+{
+ ren_u->siz_w = siz_w;
+ u3i_slab_init(&ren_u->sab_u, 3, siz_w + 12); // size + max padding
+ ren_u->buf_y = ren_u->nex_y = c3_align(ren_u->sab_u.buf_y, 16, C3_ALGHI);
+ ren_u->end_y = ren_u->buf_y + ren_u->siz_w;
+ c3_y pad_y = ren_u->buf_y - ren_u->sab_u.buf_y;
+ if (pad_y > 12)
+ {
+ u3m_bail(c3__fail);
+ }
+ ren_u->pad_y = pad_y;
+ ren_u->ini_t = 1;
+}
+
+static void
+_uw_arena_init(uw_arena* ren_u)
+{
+ _uw_arena_init_size(ren_u, (c3_w)1 << 23);
+}
+
+static void
+_uw_arena_grow(uw_arena* ren_u)
+{
+ if (!ren_u->ini_t)
+ {
+ u3m_bail(c3__fail);
+ }
+ c3_w new_w = ren_u->siz_w * 2;
+ if (new_w / 2 != ren_u->siz_w)
+ {
+ u3m_bail(c3__fail);
+ }
+ ren_u->siz_w = new_w;
+
+ u3i_slab_free(&ren_u->sab_u);
+
+ u3i_slab_init(&ren_u->sab_u, 3, new_w + 12); // size + max padding
+ ren_u->buf_y = ren_u->nex_y = c3_align(ren_u->sab_u.buf_y, 16, C3_ALGHI);
+ ren_u->end_y = ren_u->nex_y + new_w;
+ c3_y pad_y = ren_u->nex_y - ren_u->sab_u.buf_y;
+ if (pad_y > 12)
+ {
+ u3m_bail(c3__fail);
+ }
+ ren_u->pad_y = pad_y;
+}
+
+static void
+_uw_arena_reset(uw_arena* ren_u)
+{
+ if (!ren_u->ini_t)
+ {
+ u3m_bail(c3__fail);
+ }
+ ren_u->nex_y = ren_u->buf_y;
+ memset(ren_u->buf_y, 0, (size_t)ren_u->siz_w);
+}
+
+static void
+_uw_arena_free(uw_arena* ren_u)
+{
+ if (!ren_u->ini_t)
+ {
+ u3m_bail(c3__fail);
+ }
+ u3i_slab_free(&ren_u->sab_u);
+ ren_u->ini_t = 0;
+}
+
+// Code page allocation: simple bump allocator for non-growing objects,
+// i.e. code pages
+// save allocation length for realloc
+// CodeArena->esc_u MUST be initialized by the caller to handle OOM
+//
+static uw_arena* CodeArena;
+
+static void*
+_calloc_code(size_t num_i, size_t len_i)
+{
+ if (!CodeArena->ini_t)
+ {
+ u3m_bail(c3__fail);
+ }
+
+ void* lag_v = CodeArena->nex_y;
+
+ size_t byt_i = num_i * len_i;
+ if (byt_i / len_i != num_i)
+ {
+ u3m_bail(c3__fail);
+ }
+
+ if (byt_i >= UINT64_MAX - 16)
+ {
+ u3m_bail(c3__fail);
+ }
+ c3_d byt_d = byt_i + 16; // c3_d for length + alignment padding
+
+ c3_y* nex_y = CodeArena->nex_y + byt_d;
+ nex_y = c3_align(nex_y, 16, C3_ALGHI);
+
+ if (nex_y >= CodeArena->end_y)
+ { // OOM, jump out to increase the arena size and try again
+ _longjmp(*CodeArena->esc_u, c3__code);
+ }
+
+ *((c3_d*)lag_v) = byt_d - 16; // corruption check
+ *((c3_d*)lag_v + 1) = byt_d - 16;
+
+ CodeArena->nex_y = nex_y;
+ return ((c3_d*)lag_v + 2);
+}
+
+static void*
+_realloc_code(void* lag_v, size_t len_i)
+{
+ if (!CodeArena->ini_t)
+ {
+ u3m_bail(c3__fail);
+ }
+ if (!lag_v)
+ {
+ return _calloc_code(len_i, 1);
+ }
+ c3_d old1_d = *((c3_d*)lag_v - 1);
+ c3_d old2_d = *((c3_d*)lag_v - 2);
+ if (old1_d != old2_d)
+ {
+ u3m_bail(c3__fail);
+ }
+ if (len_i >= UINT64_MAX)
+ {
+ u3m_bail(c3__fail);
+ }
+ c3_d len_d = len_i;
+ void* new_v = _calloc_code(len_d, 1);
+ memcpy(new_v, lag_v, c3_min(len_d, old1_d));
+
+ return new_v;
+}
+
+static void
+_free_code(void* lag_v)
+{
+ if (!CodeArena->ini_t)
+ {
+ u3m_bail(c3__fail);
+ }
+ // noop
+}
+
+// Struct/array allocation: [len_d cap_d data]
+// BoxArena->esc_u MUST be initialized by the caller to handle OOM
+//
+static uw_arena* BoxArena;
+
+// allocate with capacity
+// the allocated buffer
+static void*
+_malloc_box_cap(c3_d len_d, c3_d cap_d)
+{
+ if (!BoxArena->ini_t)
+ {
+ u3m_bail(c3__fail);
+ }
+
+ void* lag_v = BoxArena->nex_y;
+
+ if (cap_d >= UINT64_MAX - 16)
+ {
+ u3m_bail(c3__fail);
+ }
+ c3_d pac_d = cap_d + 16; // c3_d for length + capacity
+
+ c3_y* nex_y = BoxArena->nex_y + pac_d;
+ nex_y = c3_align(nex_y, 16, C3_ALGHI);
+
+ if (nex_y >= BoxArena->end_y)
+ { // OOM, jump out to increase the arena size and try again
+ _longjmp(*BoxArena->esc_u, c3__box);
+ }
+
+ *((c3_d*)lag_v) = len_d;
+ *((c3_d*)lag_v + 1) = cap_d;
+
+ BoxArena->nex_y = nex_y;
+ return ((c3_d*)lag_v + 2);
+}
+
+static void*
+_calloc_box(size_t num_i, size_t len_i)
+{
+ size_t byt_i = num_i * len_i;
+ if (byt_i / len_i != num_i)
+ {
+ u3m_bail(c3__fail);
+ }
+ if (byt_i > UINT64_MAX - 16)
+ {
+ u3m_bail(c3__fail);
+ }
+ c3_d byt_d = byt_i;
+ return _malloc_box_cap(byt_d, byt_d);
+}
+
+static void*
+_realloc_box(void* lag_v, size_t len_i)
+{
+ if (!BoxArena->ini_t)
+ {
+ u3m_bail(c3__fail);
+ }
+ if (!lag_v)
+ {
+ return _calloc_box(len_i, 1);
+ }
+ c3_d old_d = *((c3_d*)lag_v - 2);
+ c3_d cap_d = *((c3_d*)lag_v - 1);
+ if (len_i >= UINT64_MAX)
+ {
+ u3m_bail(c3__fail);
+ }
+ c3_d len_d = len_i;
+ if (len_d <= cap_d)
+ {
+ *((c3_d*)lag_v - 2) = len_d;
+ return lag_v;
+ }
+
+ // while (cap_d <= len_d)
+ // {
+ // cap_d *= 2;
+ // }
+ cap_d <<= c3_bits_dabl(len_d) - c3_bits_dabl(cap_d);
+ cap_d <<= (cap_d <= len_d);
+
+ // overflow check
+ if (cap_d <= len_d)
+ u3m_bail(c3__fail);
+
+ void* new_v = _malloc_box_cap(len_d, cap_d);
+ memcpy(new_v, lag_v, old_d);
+
+ return new_v;
+}
+
+static void
+_free_box(void* lag_v)
+{
+ if (!BoxArena->ini_t)
+ {
+ u3m_bail(c3__fail);
+ }
+ // noop
+}
+
+// bailing allocator to prevent wasm3 from touching the arenas
+
+static void*
+_calloc_bail(size_t num_i, size_t len_i)
+{
+ u3m_bail(c3__fail);
+}
+
+static void*
+_realloc_bail(void* lag_v, size_t len_i)
+{
+ u3m_bail(c3__fail);
+}
+
+static void
+_free_bail(void* lag_v)
+{
+ u3m_bail(c3__fail);
+}
+
+
+static u3_noun
+_atoms_from_stack(void** valptrs, c3_w n, c3_y* types)
+{
+ u3_noun out = u3_nul;
+ while (n--)
+ {
+ switch (types[n])
+ { // TODO 64 bit vere
+ case c_m3Type_i32:
+ case c_m3Type_f32:
+ {
+ out = u3nc(u3i_word(*(c3_w*)valptrs[n]), out);
+ break;
+ }
+ case c_m3Type_i64:
+ case c_m3Type_f64:
+ {
+ out = u3nc(u3i_chub(*(c3_d*)valptrs[n]), out);
+ break;
+ }
+ default:
+ {
+ return u3m_bail(c3__fail);
+ }
+ }
+ }
+ return out;
+}
+
+// RETAIN argument
+static c3_o
+_atoms_to_stack(u3_noun atoms, void** valptrs, c3_w n, c3_y* types)
+{
+ for (c3_w i = 0; i < n; i++)
+ {
+ if (c3y == u3ud(atoms))
+ {
+ return c3n;
+ }
+ u3_noun atom;
+ u3x_cell(atoms, &atom, &atoms);
+ if (c3n == u3ud(atom))
+ {
+ return u3m_bail(c3__fail);
+ }
+ switch (types[i])
+ {
+ case c_m3Type_i32:
+ case c_m3Type_f32:
+ {
+ *(c3_w*)valptrs[i] = u3r_word(0, atom);
+ break;
+ }
+ case c_m3Type_i64:
+ case c_m3Type_f64:
+ {
+ *(c3_d*)valptrs[i] = u3r_chub(0, atom);
+ break;
+ }
+ default:
+ {
+ return u3m_bail(c3__fail);
+ }
+ }
+ }
+ return __(u3_nul == atoms);
+}
+
+static u3_noun
+_coins_from_stack(void** valptrs, c3_w n, c3_y* types)
+{
+ u3_noun out = u3_nul;
+ while (n--)
+ {
+ switch (types[n])
+ { // TODO 64 bit vere
+ case c_m3Type_i32:
+ {
+ out = u3nc(u3nc(c3__i32, u3i_word(*(c3_w*)valptrs[n])), out);
+ break;
+ }
+ case c_m3Type_i64:
+ {
+ out = u3nc(u3nc(c3__i64, u3i_chub(*(c3_d*)valptrs[n])), out);
+ break;
+ }
+ case c_m3Type_f32:
+ {
+ out = u3nc(u3nc(c3__f32, u3i_word(*(c3_w*)valptrs[n])), out);
+ break;
+ }
+ case c_m3Type_f64:
+ {
+ out = u3nc(u3nc(c3__f64, u3i_chub(*(c3_d*)valptrs[n])), out);
+ break;
+ }
+ default:
+ {
+ return u3m_bail(c3__fail);
+ }
+ }
+ }
+ return out;
+}
+
+// RETAIN argument
+static c3_o
+_coins_to_stack(u3_noun coins, void** valptrs, c3_w n, c3_y* types)
+{
+ for (c3_w i = 0; i < n; i++)
+ {
+ if (c3y == u3ud(coins))
+ {
+ return c3n;
+ }
+ u3_noun coin;
+ u3x_cell(coins, &coin, &coins);
+ if (c3y == u3ud(coin))
+ {
+ return u3m_bail(c3__fail);
+ }
+ u3_noun tag, value;
+ u3x_cell(coin, &tag, &value);
+ if (c3n == u3ud(value))
+ {
+ return u3m_bail(c3__fail);
+ }
+ switch (types[i])
+ {
+ case c_m3Type_i32:
+ {
+ if (c3__i32 != tag)
+ {
+ return c3n;
+ }
+ *(c3_w*)valptrs[i] = u3r_word(0, value);
+ break;
+ }
+ case c_m3Type_i64:
+ {
+ if (c3__i64 != tag)
+ {
+ return c3n;
+ }
+ *(c3_d*)valptrs[i] = u3r_chub(0, value);
+ break;
+ }
+ case c_m3Type_f32:
+ {
+ if (c3__f32 != tag)
+ {
+ return c3n;
+ }
+ *(c3_w*)valptrs[i] = u3r_word(0, value);
+ break;
+ }
+ case c_m3Type_f64:
+ {
+ if (c3__f64 != tag)
+ {
+ return c3n;
+ }
+ *(c3_d*)valptrs[i] = u3r_chub(0, value);
+ break;
+ }
+ default:
+ {
+ return u3m_bail(c3__fail);
+ }
+ }
+ }
+ return __(u3_nul == coins);
+}
+
+static c3_t
+_deterministic_trap(M3Result result)
+{
+ return ( result == m3Err_trapOutOfBoundsMemoryAccess
+ || result == m3Err_trapDivisionByZero
+ || result == m3Err_trapIntegerOverflow
+ || result == m3Err_trapIntegerConversion
+ || result == m3Err_trapIndirectCallTypeMismatch
+ || result == m3Err_trapTableIndexOutOfRange
+ || result == m3Err_trapTableElementIsNull
+ || result == UrwasmArrowExit
+ );
+}
+
+static u3_noun
+_reduce_monad(u3_noun monad, lia_state* sat_u)
+{
+ u3_noun monad_bat = u3h(monad);
+ if (c3y == u3r_sing(monad_bat, sat_u->match->call_bat))
+ {
+ if (c3n == u3r_sing(u3at(ARROW_CTX, monad), sat_u->match->call_ctx))
+ {
+ return u3m_bail(c3__fail);
+ }
+ // call
+ u3_atom name = u3x_atom(u3at(arr_sam_2, monad));
+ u3_noun args = u3at(arr_sam_3, monad);
+
+ c3_w met_w = u3r_met(3, name);
+ c3_c* name_c = u3a_malloc(met_w + 1);
+ u3r_bytes(0, met_w, (c3_y*)name_c, name);
+ name_c[met_w] = 0;
+
+ M3Result result;
+
+ IM3Function f;
+ result = m3_FindFunction(&f, sat_u->wasm_module->runtime, name_c);
+
+ if (result)
+ {
+ fprintf(stderr, ERR("function %s search error: %s"), name_c, result);
+ return u3m_bail(c3__fail);
+ }
+
+ c3_w n_in = f->funcType->numArgs;
+ c3_w n_out = f->funcType->numRets;
+ c3_y* types = f->funcType->types;
+
+ c3_d *vals_in = u3a_calloc(n_in, sizeof(c3_d));
+ void **valptrs_in = u3a_calloc(n_in, sizeof(void*));
+ for (c3_w i = 0; i < n_in; i++)
+ {
+ valptrs_in[i] = &vals_in[i];
+ }
+
+ c3_d *vals_out = u3a_calloc(n_out, sizeof(c3_d));
+ void **valptrs_out = u3a_calloc(n_out, sizeof(void*));
+ for (c3_w i = 0; i < n_out; i++)
+ {
+ valptrs_out[i] = &vals_out[i];
+ }
+
+ if (c3n == _atoms_to_stack(args, valptrs_in, n_in, (types+n_out)))
+ {
+ fprintf(stderr, ERR("function %s wrong number of args"), name_c);
+ return u3m_bail(c3__fail);
+ }
+
+ c3_w edge_1 = sat_u->wasm_module->runtime->edge_suspend;
+
+ // printf("\r\n\r\n invoke %s\r\n\r\n", name_c);
+
+ { // push on suspend stacks
+ c3_d f_idx_d = f - sat_u->wasm_module->functions;
+ m3_SuspendStackPush64(sat_u->wasm_module->runtime, f_idx_d);
+ m3_SuspendStackPush64(sat_u->wasm_module->runtime, west_call);
+ m3_SuspendStackPushExtTag(sat_u->wasm_module->runtime);
+ _push_list(
+ u3nc(lst_call, u3k(name)),
+ &sat_u->susp_list
+ );
+ }
+
+ M3Result result_call = m3_Call(f, n_in, (const void**)valptrs_in);
+ // printf("\r\n done %s\r\n", name_c);
+
+ if (result_call != m3Err_ComputationBlock
+ && result_call != m3Err_SuspensionError)
+ { // pop suspend stacks
+ m3_SuspendStackPopExtTag(sat_u->wasm_module->runtime);
+ c3_d tag;
+ m3_SuspendStackPop64(sat_u->wasm_module->runtime, &tag);
+ if (tag != -1 && tag != west_call)
+ {
+ printf(ERR("call tag mismatch: %"PRIc3_d), tag);
+ return u3m_bail(c3__fail);
+ }
+ m3_SuspendStackPop64(sat_u->wasm_module->runtime, NULL);
+ u3_noun frame = _pop_list(&sat_u->susp_list);
+ if (u3_none != frame && lst_call != u3h(frame))
+ {
+ printf(ERR("wrong frame: call"));
+ return u3m_bail(c3__fail);
+ }
+ u3z(frame);
+ }
+
+ u3_noun yil;
+ if (result_call == m3Err_ComputationBlock)
+ {
+ yil = sat_u->arrow_yil;
+ sat_u->arrow_yil = u3_none;
+ if (yil == u3_none)
+ {
+ return u3m_bail(c3__fail);
+ }
+ }
+ else if (_deterministic_trap(result_call))
+ {
+ fprintf(stderr, WUT("%s call trapped: %s"), name_c, result_call);
+ yil = u3nc(2, 0);
+ }
+ else if (result_call == m3Err_functionImportMissing)
+ {
+ return u3m_bail(c3__exit);
+ }
+ else if (result_call)
+ {
+ fprintf(stderr, ERR("%s call failed: %s"), name_c, result_call);
+ return u3m_bail(c3__fail);
+ }
+ else
+ {
+ result = m3_GetResults(f, n_out, (const void**)valptrs_out);
+ if (result)
+ {
+ fprintf(stderr, ERR("function %s failed to get results"), name_c);
+ return u3m_bail(c3__fail);
+ }
+ yil = u3nc(0, _atoms_from_stack(valptrs_out, n_out, types));
+ }
+
+ c3_w edge_2 = sat_u->wasm_module->runtime->edge_suspend;
+ if (edge_1 != edge_2 && !result_call)
+ {
+ fprintf(stderr, ERR("imbalanced suspension stack on succesfull return: %d vs %d"), edge_1, edge_2);
+ return u3m_bail(c3__fail);
+ }
+
+ u3a_free(name_c);
+ u3a_free(vals_in);
+ u3a_free(valptrs_in);
+ u3a_free(vals_out);
+ u3a_free(valptrs_out);
+ u3z(monad);
+
+ return yil;
+ }
+ else if (c3y == u3r_sing(monad_bat, sat_u->match->memread_bat))
+ {
+ if (c3n == u3r_sing(u3at(ARROW_CTX, monad), sat_u->match->memread_ctx))
+ {
+ return u3m_bail(c3__fail);
+ }
+ // memread
+ u3_atom ptr = u3x_atom(u3at(arr_sam_2, monad));
+ u3_noun len = u3at(arr_sam_3, monad);
+
+ c3_w ptr_w = u3r_word(0, ptr);
+ c3_l len_l = (c3y == u3a_is_cat(len)) ? len : u3m_bail(c3__fail);
+ c3_w len_buf_w;
+ c3_y* buf_y = m3_GetMemory(sat_u->wasm_module->runtime, &len_buf_w, 0);
+
+ if (buf_y == NULL)
+ {
+ fprintf(stderr, ERR("memread failed to get memory"));
+ return u3m_bail(c3__fail);
+ }
+
+ if (ptr_w + len_l > len_buf_w)
+ {
+ fprintf(stderr, ERR("memread out of bounds"));
+ return u3m_bail(c3__fail);
+ }
+
+ u3z(monad);
+ return u3nt(0, len_l, u3i_bytes(len_l, (buf_y + ptr_w)));
+ }
+ else if (c3y == u3r_sing(monad_bat, sat_u->match->memwrite_bat))
+ {
+ if (c3n == u3r_sing(u3at(ARROW_CTX, monad), sat_u->match->memwrite_ctx))
+ {
+ return u3m_bail(c3__fail);
+ }
+ // memwrite
+ u3_atom ptr = u3x_atom(u3at(arr_sam_2, monad));
+ u3_noun len = u3at(arr_sam_6, monad);
+ u3_noun src = u3at(arr_sam_7, monad);
+
+ c3_w ptr_w = u3r_word(0, ptr);
+ c3_l len_l = (c3y == u3a_is_cat(len)) ? len : u3m_bail(c3__fail);
+
+ c3_w len_buf_w;
+ c3_y* buf_y = m3_GetMemory(sat_u->wasm_module->runtime, &len_buf_w, 0);
+
+ if (buf_y == NULL)
+ {
+ fprintf(stderr, ERR("memwrite failed to get memory"));
+ return u3m_bail(c3__fail);
+ }
+
+ if (ptr_w + len_l > len_buf_w)
+ {
+ fprintf(stderr, ERR("memwrite out of bounds"));
+ return u3m_bail(c3__fail);
+ }
+
+ u3r_bytes(0, len_l, (buf_y + ptr_w), u3x_atom(src));
+
+ u3z(monad);
+ return u3nc(0, 0);
+ }
+ else if (c3y == u3r_sing(monad_bat, sat_u->match->call_ext_bat))
+ {
+ // call-ext
+ if (u3_nul == sat_u->lia_shop)
+ {
+ // Suspended computation will have exactly one blocking point, which
+ // must be at the top of the stack. You are at this point.
+ // There is no useful info to be saved here, name/args are not enough
+ // to qualify the external call, which can and will be nondeterministic
+ // (like all IO in urbit)
+ //
+ // On wasm3 side op_CallRaw will store the information about the
+ // called function. It shall be the top frame of the suspension stack,
+ // since only Lia can block, so wasm3 has to call Lia to get blocked.
+ //
+ // A frame is pushed in wasm3 to trigger the callback and signal to
+ // _apply_diff that the computation is blocked
+ //
+ m3_SuspendStackPush64(sat_u->wasm_module->runtime, west_call_ext);
+ m3_SuspendStackPushExtTag(sat_u->wasm_module->runtime);
+
+ u3_noun name = u3at(arr_sam_2, monad);
+ u3_noun args = u3at(arr_sam_3, monad);
+
+ u3_noun yil = u3nt(1, u3k(name), u3k(args));
+ u3z(monad);
+ return yil;
+ }
+ else
+ {
+ u3z(monad);
+
+ u3_noun lia_buy, tel;
+ u3x_cell(sat_u->lia_shop, &lia_buy, &tel);
+ u3_noun yil = u3nc(0, u3k(lia_buy));
+ u3k(tel);
+ u3z(sat_u->lia_shop);
+ sat_u->lia_shop = tel;
+ return yil;
+ }
+ }
+ else if (c3y == u3r_sing(monad_bat, sat_u->match->try_bat))
+ {
+ // try
+ u3_noun monad_b = u3at(60, monad);
+ u3_noun cont = u3at(61, monad);
+ u3_weak yil;
+ u3_noun monad_cont;
+ { // push on suspend stacks
+ m3_SuspendStackPush64(sat_u->wasm_module->runtime, west_try);
+ m3_SuspendStackPushExtTag(sat_u->wasm_module->runtime);
+ _push_list(u3nc(lst_try, u3k(cont)), &sat_u->susp_list);
+ }
+ {
+ yil = _reduce_monad(u3k(monad_b), sat_u);
+
+ if (1 != u3h(yil))
+ { // pop suspend stacks
+ m3_SuspendStackPopExtTag(sat_u->wasm_module->runtime);
+ c3_d tag;
+ m3_SuspendStackPop64(sat_u->wasm_module->runtime, &tag);
+ if (tag != -1 && tag != west_try)
+ {
+ printf(ERR("try tag mismatch: %"PRIc3_d), tag);
+ return u3m_bail(c3__fail);
+ }
+ u3_noun frame = _pop_list(&sat_u->susp_list);
+ if (u3_none != frame && lst_try != u3h(frame))
+ {
+ printf(ERR("wrong frame: try"));
+ return u3m_bail(c3__fail);
+ }
+ u3z(frame);
+ }
+
+ if (0 == u3h(yil))
+ {
+ // any unconstrained nock computation is a potential urwasm reentry:
+ // save the pointers before that, restore after
+ uw_arena* box_arena_frame = BoxArena;
+ uw_arena* code_arena_frame = CodeArena;
+ monad_cont = uw_slam_check(
+ u3k(cont),
+ u3k(u3t(yil)),
+ sat_u->is_stateful
+ );
+ BoxArena = box_arena_frame;
+ CodeArena = code_arena_frame;
+ u3z(yil);
+ yil = u3_none;
+ }
+ }
+
+ u3z(monad);
+ if (u3_none == yil)
+ {
+ return _reduce_monad(monad_cont, sat_u);
+ }
+ else
+ {
+ return yil;
+ }
+ }
+ else if (c3y == u3r_sing(monad_bat, sat_u->match->catch_bat))
+ {
+ // catch
+ u3_noun monad_try = u3at(120, monad);
+ u3_noun monad_catch = u3at(121, monad);
+ u3_noun cont = u3at(61, monad);
+ u3_weak yil;
+ u3_noun monad_cont;
+
+ {
+ { // push on suspend stacks
+ m3_SuspendStackPush64(sat_u->wasm_module->runtime, west_catch_try);
+ m3_SuspendStackPushExtTag(sat_u->wasm_module->runtime);
+ _push_list(
+ u3nt(lst_catch_try, u3k(monad_catch), u3k(cont)),
+ &sat_u->susp_list
+ );
+ }
+ yil = _reduce_monad(u3k(monad_try), sat_u);
+
+ if (1 != u3h(yil))
+ { // pop suspend stacks
+ m3_SuspendStackPopExtTag(sat_u->wasm_module->runtime);
+ c3_d tag;
+ m3_SuspendStackPop64(sat_u->wasm_module->runtime, &tag);
+ if (tag != -1 && tag != west_catch_try)
+ {
+ printf(ERR("catch-try tag mismatch: %"PRIc3_d), tag);
+ return u3m_bail(c3__fail);
+ }
+ u3_noun frame = _pop_list(&sat_u->susp_list);
+ if (u3_none != frame && lst_catch_try != u3h(frame))
+ {
+ printf(ERR("wrong frame: catch-try"));
+ return u3m_bail(c3__fail);
+ }
+ u3z(frame);
+ }
+
+ if (0 == u3h(yil))
+ {
+ uw_arena* box_arena_frame = BoxArena;
+ uw_arena* code_arena_frame = CodeArena;
+ monad_cont = uw_slam_check(
+ u3k(cont),
+ u3k(u3t(yil)),
+ sat_u->is_stateful
+ );
+ BoxArena = box_arena_frame;
+ CodeArena = code_arena_frame;
+ u3z(yil);
+ yil = u3_none;
+ }
+ else if (2 == u3h(yil))
+ {
+ u3z(yil);
+
+ { // push on suspend stacks
+ m3_SuspendStackPush64(sat_u->wasm_module->runtime, west_catch_err);
+ m3_SuspendStackPushExtTag(sat_u->wasm_module->runtime);
+ _push_list(
+ u3nc(lst_catch_err, u3k(cont)),
+ &sat_u->susp_list
+ );
+ }
+
+ yil = _reduce_monad(u3k(monad_catch), sat_u);
+
+ if (1 != u3h(yil))
+ { // pop suspend stacks
+ m3_SuspendStackPopExtTag(sat_u->wasm_module->runtime);
+ c3_d tag;
+ m3_SuspendStackPop64(sat_u->wasm_module->runtime, &tag);
+ if (tag != -1 && tag != west_catch_err)
+ {
+ printf(ERR("catch-err tag mismatch: %"PRIc3_d), tag);
+ return u3m_bail(c3__fail);
+ }
+ u3_noun frame = _pop_list(&sat_u->susp_list);
+ if (u3_none != frame && lst_catch_err != u3h(frame))
+ {
+ printf(ERR("wrong frame: catch-err"));
+ return u3m_bail(c3__fail);
+ }
+ u3z(frame);
+ }
+
+ if (0 == u3h(yil))
+ {
+ uw_arena* box_arena_frame = BoxArena;
+ uw_arena* code_arena_frame = CodeArena;
+ monad_cont = uw_slam_check(
+ u3k(cont),
+ u3k(u3t(yil)),
+ sat_u->is_stateful
+ );
+ BoxArena = box_arena_frame;
+ CodeArena = code_arena_frame;
+ u3z(yil);
+ yil = u3_none;
+ }
+ }
+ }
+
+ u3z(monad);
+ if (u3_none == yil)
+ {
+ return _reduce_monad(monad_cont, sat_u);
+ }
+ else
+ {
+ return yil;
+ }
+ }
+ else if (c3y == u3r_sing(monad_bat, sat_u->match->return_bat))
+ {
+ // return
+ u3_noun yil = u3nc(0, u3k(u3at(30, monad)));
+ u3z(monad);
+ return yil;
+ }
+ else if (c3y == u3r_sing(monad_bat, sat_u->match->global_set_bat))
+ {
+ if (c3n == u3r_sing(u3at(ARROW_CTX, monad), sat_u->match->global_set_ctx))
+ {
+ return u3m_bail(c3__fail);
+ }
+ // global-set
+ u3_atom name = u3x_atom(u3at(arr_sam_2, monad));
+ u3_atom value = u3x_atom(u3at(arr_sam_3, monad));
+
+ c3_w met_w = u3r_met(3, name);
+ c3_c* name_c = u3a_malloc(met_w + 1);
+ u3r_bytes(0, met_w, (c3_y*)name_c, name);
+ name_c[met_w] = 0;
+
+ IM3Global glob = m3_FindGlobal(sat_u->wasm_module, name_c);
+
+ if (!glob)
+ {
+ fprintf(stderr, ERR("global %s not found"), name_c);
+ return u3m_bail(c3__fail);
+ }
+
+ if (!glob->isMutable)
+ {
+ fprintf(stderr, ERR("global %s not mutable"), name_c);
+ return u3m_bail(c3__fail);
+ }
+
+ M3TaggedValue glob_value;
+ M3Result result = m3_GetGlobal(glob, &glob_value);
+ if (result)
+ {
+ fprintf(stderr, ERR("couldn't get global %s: %s"), name_c, result);
+ return u3m_bail(c3__fail);
+ }
+ switch (glob_value.type)
+ {
+ default:
+ {
+ return u3m_bail(c3__fail);
+ }
+ case c_m3Type_i32:
+ {
+ glob_value.value.i32 = u3r_word(0, value);
+ break;
+ }
+ case c_m3Type_i64:
+ {
+ glob_value.value.i64 = u3r_chub(0, value);
+ break;
+ }
+ case c_m3Type_f32:
+ {
+ glob_value.value.f32 = u3r_word(0, value);
+ break;
+ }
+ case c_m3Type_f64:
+ {
+ glob_value.value.f64 = u3r_chub(0, value);
+ break;
+ }
+ }
+ result = m3_SetGlobal(glob, &glob_value);
+ if (result)
+ {
+ fprintf(stderr, ERR("couldn't set global %s: %s"), name_c, result);
+ return u3m_bail(c3__fail);
+ }
+ u3z(monad);
+ u3a_free(name_c);
+ return u3nc(0, 0);
+ }
+ else if (c3y == u3r_sing(monad_bat, sat_u->match->global_get_bat))
+ {
+ if (c3n == u3r_sing(u3at(ARROW_CTX, monad), sat_u->match->global_get_ctx))
+ {
+ return u3m_bail(c3__fail);
+ }
+ // global-get
+ u3_atom name = u3x_atom(u3at(arr_sam, monad));
+
+ c3_w met_w = u3r_met(3, name);
+ c3_c* name_c = u3a_malloc(met_w + 1);
+ u3r_bytes(0, met_w, (c3_y*)name_c, name);
+ name_c[met_w] = 0;
+
+ IM3Global glob = m3_FindGlobal(sat_u->wasm_module, name_c);
+ if (!glob)
+ {
+ fprintf(stderr, ERR("global %s not found"), name_c);
+ return u3m_bail(c3__fail);
+ }
+
+ M3TaggedValue glob_value;
+ M3Result result = m3_GetGlobal(glob, &glob_value);
+ if (result)
+ {
+ fprintf(stderr, ERR("couldn't get global %s: %s"), name_c, result);
+ return u3m_bail(c3__fail);
+ }
+
+ u3_noun out;
+ switch (glob_value.type)
+ {
+ default:
+ {
+ return u3m_bail(c3__fail);
+ }
+ case c_m3Type_i32:
+ {
+ out = u3i_word(glob_value.value.i32);
+ break;
+ }
+ case c_m3Type_i64:
+ {
+ out = u3i_chub(glob_value.value.i64);
+ break;
+ }
+ case c_m3Type_f32:
+ {
+ out = u3i_word(glob_value.value.f32);
+ break;
+ }
+ case c_m3Type_f64:
+ {
+ out = u3i_chub(glob_value.value.f64);
+ break;
+ }
+ }
+
+ u3z(monad);
+ u3a_free(name_c);
+ return u3nc(0, out);
+
+ }
+ else if (c3y == u3r_sing(monad_bat, sat_u->match->mem_size_bat))
+ {
+ if (c3n == u3r_sing(u3at(MONAD_CTX, monad), sat_u->match->mem_size_ctx))
+ {
+ return u3m_bail(c3__fail);
+ }
+ // memory-size
+ if (!sat_u->wasm_module->memoryInfo.hasMemory)
+ {
+ fprintf(stderr, ERR("memsize no memory"));
+ return u3m_bail(c3__fail);
+ }
+ c3_w num_pages = sat_u->wasm_module->runtime->memory.numPages;
+
+ u3z(monad);
+ return u3nc(0, u3i_word(num_pages));
+ }
+ else if (c3y == u3r_sing(monad_bat, sat_u->match->mem_grow_bat))
+ {
+ if (c3n == u3r_sing(u3at(ARROW_CTX, monad), sat_u->match->mem_grow_ctx))
+ {
+ return u3m_bail(c3__fail);
+ }
+ // memory-grow
+ if (!sat_u->wasm_module->memoryInfo.hasMemory)
+ {
+ fprintf(stderr, ERR("memgrow no memory"));
+ return u3m_bail(c3__fail);
+ }
+
+ u3_noun delta = u3at(arr_sam, monad);
+
+ c3_l delta_l = (c3y == u3a_is_cat(delta)) ? delta : u3m_bail(c3__fail);
+
+ c3_w n_pages = sat_u->wasm_module->runtime->memory.numPages;
+ c3_w required_pages = n_pages + delta_l;
+
+ M3Result result = ResizeMemory(sat_u->wasm_module->runtime, required_pages);
+
+ if (result)
+ {
+ fprintf(stderr, ERR("failed to resize memory: %s"), result);
+ return u3m_bail(c3__fail);
+ }
+
+ u3z(monad);
+ return u3nc(0, u3i_word(n_pages));
+ }
+ else if (c3y == u3r_sing(monad_bat, sat_u->match->get_acc_bat))
+ {
+ u3z(monad);
+ return u3nc(0, u3k(sat_u->acc));
+ }
+ else if (c3y == u3r_sing(monad_bat, sat_u->match->set_acc_bat))
+ {
+ u3_noun new = u3k(u3at(arr_sam, monad));
+ u3z(monad);
+ u3z(sat_u->acc);
+ sat_u->acc = new;
+ return u3nc(0, 0);
+ }
+ else if (c3y == u3r_sing(monad_bat, sat_u->match->get_all_glob_bat))
+ {
+ if (c3n == u3r_sing(u3at(MONAD_CTX, monad), sat_u->match->get_all_glob_ctx))
+ {
+ return u3m_bail(c3__fail);
+ }
+ u3z(monad);
+ u3_noun atoms = u3_nul;
+ c3_w n_globals = sat_u->wasm_module->numGlobals;
+ c3_w n_globals_import = sat_u->wasm_module->numGlobImports;
+ while (n_globals-- > n_globals_import)
+ {
+ M3Global glob = sat_u->wasm_module->globals[n_globals];
+ switch (glob.type)
+ {
+ default:
+ {
+ return u3m_bail(c3__fail);
+ }
+ case c_m3Type_i32:
+ {
+ atoms = u3nc(u3i_word(glob.intValue), atoms);
+ break;
+ }
+ case c_m3Type_i64:
+ {
+ atoms = u3nc(u3i_chub(glob.intValue), atoms);
+ break;
+ }
+ case c_m3Type_f32:
+ {
+ atoms = u3nc(u3i_word(glob.f32Value), atoms);
+ break;
+ }
+ case c_m3Type_f64:
+ {
+ atoms = u3nc(u3i_chub(glob.f64Value), atoms);
+ break;
+ }
+ }
+ }
+ return u3nc(0, atoms);
+ }
+ else if (c3y == u3r_sing(monad_bat, sat_u->match->set_all_glob_bat))
+ {
+ if (c3n == u3r_sing(u3at(ARROW_CTX, monad), sat_u->match->set_all_glob_ctx))
+ {
+ return u3m_bail(c3__fail);
+ }
+ u3_noun atoms = u3at(arr_sam, monad);
+ c3_w n_globals = sat_u->wasm_module->numGlobals;
+ c3_w n_globals_import = sat_u->wasm_module->numGlobImports;
+ for (c3_w i = n_globals_import; i < n_globals; i++)
+ {
+ IM3Global glob = &sat_u->wasm_module->globals[i];
+ u3_noun atom;
+ u3x_cell(atoms, &atom, &atoms);
+ u3x_atom(atom);
+ switch (glob->type)
+ {
+ default:
+ {
+ return u3m_bail(c3__fail);
+ }
+ case c_m3Type_i32:
+ {
+ glob->intValue = u3r_word(0, atom);
+ break;
+ }
+ case c_m3Type_i64:
+ {
+ glob->intValue = u3r_chub(0, atom);
+ break;
+ }
+ case c_m3Type_f32:
+ {
+ glob->f32Value = u3r_word(0, atom);
+ break;
+ }
+ case c_m3Type_f64:
+ {
+ glob->f64Value = u3r_chub(0, atom);
+ break;
+ }
+ }
+ }
+ if (u3_nul != atoms)
+ {
+ fprintf(stderr, WUT("glob list too long"));
+ return u3m_bail(c3__exit);
+ }
+ u3z(monad);
+ return u3nc(0, 0);
+ }
+ else if (c3y == u3r_sing(monad_bat, sat_u->match->fail_bat))
+ {
+ u3z(monad);
+ return u3nc(2, 0);
+ }
+ else
+ {
+ return u3m_bail(c3__fail);
+ }
+}
+
+static const M3Result
+_resume_callback(M3Result result_m3, IM3Runtime runtime)
+{
+ if (result_m3 == m3Err_ComputationBlock
+ || result_m3 == m3Err_SuspensionError)
+ {
+ return result_m3;
+ }
+ M3Result result = m3Err_none;
+ lia_state* sat_u = runtime->userdata_resume;
+ m3_SuspendStackPopExtTag(runtime);
+ c3_d tag_d;
+ m3_SuspendStackPop64(runtime, &tag_d);
+ switch (tag_d)
+ {
+ default:
+ {
+ u3m_bail(c3__fail);
+ }
+ case west_call:
+ {
+ c3_d f_idx_d;
+ m3_SuspendStackPop64(sat_u->wasm_module->runtime, &f_idx_d);
+ u3_noun frame = _pop_list(&sat_u->susp_list);
+ if (lst_call != u3h(frame))
+ {
+ printf(ERR("wrong frame: call"));
+ u3m_bail(c3__fail);
+ }
+ u3_noun name = u3t(frame);
+ c3_w met_w = u3r_met(3, name);
+ c3_c* name_c = u3a_malloc(met_w + 1);
+ u3r_bytes(0, met_w, (c3_y*)name_c, name);
+ u3z(frame);
+ name_c[met_w] = 0;
+
+ u3_noun yil;
+ if (_deterministic_trap(result_m3))
+ {
+ fprintf(stderr, WUT("%s call trapped: %s"), name_c, result_m3);
+ yil = u3nc(2, 0);
+ }
+ else if (result_m3)
+ {
+ fprintf(stderr, ERR("%s call failed: %s"), name_c, result_m3);
+ u3m_bail(c3__fail);
+ }
+ else
+ {
+ IM3Function f = runtime->modules->functions + f_idx_d;
+ c3_w n_out_w = f->funcType->numRets;
+ c3_d *vals_out = u3a_calloc(n_out_w, sizeof(c3_d));
+ void **valptrs_out = u3a_calloc(n_out_w, sizeof(void*));
+ for (c3_w i = 0; i < n_out_w; i++)
+ {
+ valptrs_out[i] = &vals_out[i];
+ }
+ M3Result result_tmp = m3_GetResults(f,
+ n_out_w,
+ (const void**)valptrs_out
+ );
+ if (result_tmp)
+ {
+ fprintf(stderr,
+ ERR("function %s failed to get results: %s"), name_c, result_tmp
+ );
+ u3m_bail(c3__fail);
+ }
+ yil = u3nc(0,
+ _atoms_from_stack(valptrs_out, n_out_w, f->funcType->types)
+ );
+ u3a_free(valptrs_out);
+ u3a_free(vals_out);
+ }
+ if (u3_none != sat_u->resolution)
+ {
+ u3m_bail(c3__fail);
+ }
+ sat_u->resolution = yil;
+ u3a_free(name_c);
+ break;
+ }
+
+ case west_call_ext:
+ {
+ if (1 == u3h(sat_u->resolution))
+ {
+ // it's a new block, it's not yet resolved
+ // restore the frame
+ //
+ m3_SuspendStackPush64(runtime, tag_d);
+ m3_SuspendStackPushExtTag(runtime);
+ result = m3Err_ComputationBlock;
+ }
+ // else the block is resolved and sat_u->resolution holds the result
+ //
+ break;
+ }
+
+ case west_try:
+ {
+ if (1 != u3h(sat_u->resolution))
+ {
+ u3_noun frame = _pop_list(&sat_u->susp_list);
+ if (lst_try != u3h(frame))
+ {
+ printf(ERR("wrong frame: try"));
+ u3m_bail(c3__fail);
+ }
+ if (0 == u3h(sat_u->resolution))
+ {
+ u3_noun cont = u3t(frame);
+ u3_noun p_res = u3t(sat_u->resolution);
+ uw_arena* box_arena_frame = BoxArena;
+ uw_arena* code_arena_frame = CodeArena;
+ u3_noun monad_cont = uw_slam_check(
+ u3k(cont),
+ u3k(p_res),
+ sat_u->is_stateful
+ );
+ BoxArena = box_arena_frame;
+ CodeArena = code_arena_frame;
+ u3z(sat_u->resolution);
+ sat_u->resolution = _reduce_monad(monad_cont, sat_u);
+ }
+ // if %2 then nothing to do, sat_u->resolution already holds %2 result
+ //
+ u3z(frame);
+ }
+ else
+ {
+ // we shouldn't be here
+ //
+ u3m_bail(c3__fail);
+ }
+ break;
+ }
+
+ case west_catch_try:
+ {
+ if (1 != u3h(sat_u->resolution))
+ {
+ u3_noun frame = _pop_list(&sat_u->susp_list);
+ if (lst_catch_try != u3h(frame))
+ {
+ printf(ERR("wrong frame: catch-try"));
+ u3m_bail(c3__fail);
+ }
+ if (0 == u3h(sat_u->resolution))
+ {
+ u3_noun cont = u3t(u3t(frame));
+ u3_noun p_res = u3t(sat_u->resolution);
+ uw_arena* box_arena_frame = BoxArena;
+ uw_arena* code_arena_frame = CodeArena;
+ u3_noun monad_cont = uw_slam_check(
+ u3k(cont),
+ u3k(p_res),
+ sat_u->is_stateful
+ );
+ BoxArena = box_arena_frame;
+ CodeArena = code_arena_frame;
+ u3z(sat_u->resolution);
+ sat_u->resolution = _reduce_monad(monad_cont, sat_u);
+ }
+ // %2
+ //
+ else
+ {
+ u3_noun cont = u3t(u3t(frame));
+ u3_noun monad_catch = u3h(u3t(frame));
+ { // push on suspend stacks
+ m3_SuspendStackPush64(sat_u->wasm_module->runtime, west_catch_err);
+ m3_SuspendStackPushExtTag(runtime);
+ _push_list(
+ u3nc(lst_catch_err, u3k(cont)),
+ &sat_u->susp_list
+ );
+ }
+
+ u3_noun yil = _reduce_monad(u3k(monad_catch), sat_u);
+
+ if (1 != u3h(yil))
+ { // pop suspend stacks
+ m3_SuspendStackPopExtTag(runtime);
+ c3_d tag;
+ m3_SuspendStackPop64(sat_u->wasm_module->runtime, &tag);
+ if (tag != -1 && tag != west_catch_err)
+ {
+ printf(ERR("catch-err tag mismatch: %"PRIc3_d), tag);
+ u3m_bail(c3__fail);
+ }
+ u3_noun frame1 = _pop_list(&sat_u->susp_list);
+ if (lst_catch_err != u3h(frame1))
+ {
+ printf(ERR("wrong frame: catch-err"));
+ u3m_bail(c3__fail);
+ }
+ u3z(frame1);
+ }
+
+ if (2 == u3h(yil))
+ {
+ // sat_u->resolution already has %2, do nothing
+ u3z(yil);
+ }
+ else if (1 == u3h(yil))
+ {
+ u3z(sat_u->resolution);
+ sat_u->resolution = yil;
+ }
+ else // %0
+ {
+ u3_noun p_res = u3t(yil);
+ uw_arena* box_arena_frame = BoxArena;
+ uw_arena* code_arena_frame = CodeArena;
+ u3_noun monad_cont = uw_slam_check(
+ u3k(cont),
+ u3k(p_res),
+ sat_u->is_stateful
+ );
+ BoxArena = box_arena_frame;
+ CodeArena = code_arena_frame;
+ u3z(sat_u->resolution);
+ u3z(yil);
+ sat_u->resolution = _reduce_monad(monad_cont, sat_u);
+ }
+ }
+ u3z(frame);
+ }
+ else
+ {
+ // we shouldn't be here
+ //
+ u3m_bail(c3__fail);
+ }
+ break;
+ }
+ case west_catch_err:
+ {
+ if (1 != u3h(sat_u->resolution))
+ {
+ u3_noun frame = _pop_list(&sat_u->susp_list);
+ if (lst_catch_err != u3h(frame))
+ {
+ printf(ERR("wrong frame: catch-err"));
+ u3m_bail(c3__fail);
+ }
+ if (0 == u3h(sat_u->resolution))
+ {
+ u3_noun cont = u3t(frame);
+ u3_noun p_res = u3t(sat_u->resolution);
+ uw_arena* box_arena_frame = BoxArena;
+ uw_arena* code_arena_frame = CodeArena;
+ u3_noun monad_cont = uw_slam_check(
+ u3k(cont),
+ u3k(p_res),
+ sat_u->is_stateful
+ );
+ BoxArena = box_arena_frame;
+ CodeArena = code_arena_frame;
+ u3z(sat_u->resolution);
+ sat_u->resolution = _reduce_monad(monad_cont, sat_u);
+ }
+ // if %2 then nothing to do, sat_u->resolution already holds %2 result
+ //
+ u3z(frame);
+ }
+ else
+ {
+ // we shouldn't be here
+ //
+ u3m_bail(c3__fail);
+ }
+ break;
+ }
+ case west_link_wasm:
+ {
+ if (1 != u3h(sat_u->resolution))
+ {
+ c3_d _sp_offset_d, func_idx_d;
+ m3_SuspendStackPop64(runtime, &func_idx_d);
+ m3_SuspendStackPop64(runtime, &_sp_offset_d);
+ if (2 == u3h(sat_u->resolution))
+ {
+ u3z(sat_u->resolution);
+ sat_u->resolution = u3_none;
+ result = UrwasmArrowExit;
+ }
+ else // %0
+ {
+ IM3Function f = runtime->modules->functions + func_idx_d;
+ uint64_t * _sp = (uint64_t *)(runtime->base + _sp_offset_d);
+ c3_w n_out = f->funcType->numRets;
+ c3_y* types = f->funcType->types;
+ void **valptrs_out = u3a_calloc(n_out, sizeof(void*));
+ const char *mod = f->import.moduleUtf8;
+ const char *name = f->import.fieldUtf8;
+ for (c3_w i = 0; i < n_out; i++)
+ {
+ valptrs_out[i] = &_sp[i];
+ }
+ c3_o pushed = _coins_to_stack(
+ u3t(sat_u->resolution),
+ valptrs_out,
+ n_out,
+ types
+ );
+
+ if (c3n == pushed)
+ {
+ printf(ERR("import result type mismatch: %s/%s"), mod, name);
+ result = "import result type mismatch";
+ }
+
+ u3z(sat_u->resolution);
+ sat_u->resolution = u3_none;
+ u3a_free(valptrs_out);
+ }
+ }
+ else
+ {
+ // we shouldn't be here
+ //
+ u3m_bail(c3__fail);
+ }
+ break;
+ }
+ }
+
+ return result;
+}
+
+// TRANSFERS sat->arrow_yil if m3Err_ComputationBlock is thrown
+static const void *
+_link_wasm_with_arrow_map(
+ IM3Runtime runtime,
+ IM3ImportContext _ctx,
+ uint64_t * _sp,
+ void * _mem
+)
+{
+ const char *mod = _ctx->function->import.moduleUtf8;
+ const char *name = _ctx->function->import.fieldUtf8;
+ lia_state* sat_u = _ctx->userdata;
+
+ u3_noun key = u3nc(u3i_string(mod), u3i_string(name));
+ u3_weak arrow = u3kdb_get(u3k(sat_u->map), key);
+ if (u3_none == arrow)
+ {
+ fprintf(stderr, ERR("import not found: %s/%s"), mod, name);
+ return m3Err_functionImportMissing;
+ }
+ c3_w n_in = _ctx->function->funcType->numArgs;
+ c3_w n_out = _ctx->function->funcType->numRets;
+ c3_y* types = _ctx->function->funcType->types;
+ void **valptrs_in = u3a_calloc(n_in, sizeof(void*));
+ for (c3_w i = 0; i < n_in; i++)
+ {
+ valptrs_in[i] = &_sp[i+n_out];
+ }
+ void **valptrs_out = u3a_calloc(n_out, sizeof(void*));
+ for (c3_w i = 0; i < n_out; i++)
+ {
+ valptrs_out[i] = &_sp[i];
+ }
+
+ u3_noun coin_wasm_list = _coins_from_stack(valptrs_in, n_in, (types+n_out));
+
+ { // push on suspend stacks
+ m3_SuspendStackPush64(runtime, (c3_d)((c3_y*)_sp - (c3_y*)runtime->base));
+ c3_d func_idx_d = _ctx->function - runtime->modules->functions;
+ m3_SuspendStackPush64(runtime, func_idx_d);
+ m3_SuspendStackPush64(runtime, west_link_wasm);
+ m3_SuspendStackPushExtTag(runtime);
+ }
+
+ uw_arena* box_arena_frame = BoxArena;
+ uw_arena* code_arena_frame = CodeArena;
+ u3_noun script = uw_slam_check(arrow, coin_wasm_list, sat_u->is_stateful);
+ BoxArena = box_arena_frame;
+ CodeArena = code_arena_frame;
+ u3_noun yil = _reduce_monad(script, sat_u);
+
+ M3Result result = m3Err_none;
+
+ if (1 != u3h(yil))
+ { // pop suspend stacks
+ m3_SuspendStackPopExtTag(runtime);
+ c3_d tag;
+ m3_SuspendStackPop64(runtime, &tag);
+ if (tag != -1 && tag != west_link_wasm)
+ {
+ printf(ERR("west_link tag mismatch: %"PRIc3_d), tag);
+ u3m_bail(c3__fail);
+ }
+ m3_SuspendStackPop64(runtime, NULL);
+ m3_SuspendStackPop64(runtime, NULL);
+
+ }
+
+ if (1 == u3h(yil))
+ {
+ if (sat_u->arrow_yil != u3_none)
+ {
+ u3z(yil);
+ result = "non-empty sat_u->arrow_yil on block";
+ }
+ else
+ {
+ sat_u->arrow_yil = yil;
+ result = m3Err_ComputationBlock; // start suspending if not yet suspending
+ }
+ }
+ else if (2 == u3h(yil))
+ {
+ u3z(yil);
+ result = UrwasmArrowExit;
+ }
+ else
+ {
+ c3_o pushed = _coins_to_stack(u3t(yil), valptrs_out, n_out, types);
+ u3z(yil);
+ if (c3n == pushed)
+ {
+ fprintf(stderr, ERR("import result type mismatch: %s/%s"), mod, name);
+ result = "import result type mismatch";
+ }
+ }
+ u3a_free(valptrs_in);
+ u3a_free(valptrs_out);
+ return result;
+}
+
+// key: [uw_run_m seed]
+// stored nouns:
+// $@ ~ :: tombstone value
+// $: yield=* :: +2
+// queue=(list script) :: +6
+// box_arena=[buffer=octs padding=@] :: [[+56 +57] +29]
+// memory=[buffer=octs max_stack_offset=@] :: [[+120 +121] +61]
+// runtime_offset=@ :: +62
+// lia_shop=(list) :: +126
+// acc=* :: +254
+// susp_list=(list) :: +255
+// ==
+// arguments RETAINED
+// on success allocates sat_u->wasm_module->runtime->memory.mallocated
+// and initializes the arenas
+static c3_t
+_get_state(u3_noun hint, u3_noun seed, lia_state* sat_u)
+{
+ // u3_weak get = u3z_find_m(u3z_memo_keep, uw_run_m, seed);
+ // XX order of search matters (sentinel value ~
+ // from previous invocation is closer to the home road)
+ // and u3z_find_m searches from home road down, which is the opposite
+ // of what we want
+ //
+ u3_noun key = u3z_key(uw_run_m, seed);
+ u3_weak get = u3z_find_up(key);
+ u3z(key);
+
+ if (u3_none == get || u3_nul == get)
+ {
+ return 0;
+ }
+ else
+ {
+ u3_noun yil_previous;
+ u3_noun queue;
+ u3_noun p_box_buffer, q_box_buffer, pad_box;
+ u3_noun p_mem_buffer, q_mem_buffer, stack_offset;
+ u3_noun runtime_offset;
+ u3_noun lia_shop;
+ u3_noun acc;
+ u3_noun susp_list;
+
+ if ( c3n == u3r_mean(get,
+ 2, &yil_previous,
+ 6, &queue,
+ 56, &p_box_buffer,
+ 57, &q_box_buffer,
+ 29, &pad_box,
+ 120, &p_mem_buffer,
+ 121, &q_mem_buffer,
+ 61, &stack_offset,
+ 62, &runtime_offset,
+ 126, &lia_shop,
+ 254, &acc,
+ 255, &susp_list,
+ 0)
+ )
+ {
+ return u3m_bail(c3__fail);
+ }
+ c3_w box_len_w = (c3y == u3a_is_cat(p_box_buffer))
+ ? p_box_buffer
+ : u3m_bail(c3__fail);
+
+ c3_w pad_w = (c3y == u3a_is_cat(pad_box))
+ ? pad_box
+ : u3m_bail(c3__fail);
+
+ c3_w run_off_w = (c3y == u3a_is_cat(runtime_offset))
+ ? runtime_offset
+ : u3m_bail(c3__fail);
+
+ c3_w len_buf_w = (c3y == u3a_is_cat(p_mem_buffer))
+ ? p_mem_buffer
+ : u3m_bail(c3__fail);
+
+ c3_w stk_off_w = (c3y == u3a_is_cat(stack_offset))
+ ? stack_offset
+ : u3m_bail(c3__fail);
+
+ _uw_arena_init_size(BoxArena, box_len_w);
+ u3r_bytes(pad_w, box_len_w, BoxArena->buf_y, q_box_buffer);
+ _uw_arena_init(CodeArena);
+
+ M3Result result;
+ IM3Runtime wasm3_runtime = (IM3Runtime)(BoxArena->buf_y + run_off_w);
+ wasm3_runtime->base = BoxArena->buf_y;
+ wasm3_runtime->base_transient = CodeArena->buf_y;
+ m3_RewritePointersRuntime(wasm3_runtime, BoxArena->buf_y, 0 /*is_store*/);
+ IM3Module wasm3_module = wasm3_runtime->modules;
+ c3_w n_imports = wasm3_module->numFuncImports;
+
+ // make sure to not touch BoxArena
+ m3_SetAllocators(_calloc_bail, _free_bail, _realloc_bail);
+ m3_SetTransientAllocators(_calloc_code, _free_code, _realloc_code);
+ m3_SetMemoryAllocators(_calloc_bail, _free_bail, _realloc_bail);
+
+ jmp_buf esc;
+ CodeArena->esc_u = &esc;
+ c3_i jmp_i;
+
+ while (1)
+ {
+ wasm3_runtime->base_transient = CodeArena->buf_y;
+
+ if (0 == (jmp_i = setjmp(esc)))
+ {
+ for (c3_w i = 0; i < n_imports; i++)
+ {
+ M3Function f = wasm3_module->functions[i];
+ const char* mod = f.import.moduleUtf8;
+ const char* name = f.import.fieldUtf8;
+
+ result = m3_LinkRawFunctionEx(
+ wasm3_module, mod, name,
+ NULL, &_link_wasm_with_arrow_map,
+ sat_u
+ );
+
+ if (result)
+ {
+ fprintf(stderr, ERR("link error: %s"), result);
+ return u3m_bail(c3__fail);
+ }
+ }
+
+ result = m3_CompileModule(wasm3_module);
+ if (result)
+ {
+ fprintf(stderr, ERR("compilation error: %s"), result);
+ return u3m_bail(c3__fail);
+ }
+
+ break;
+ }
+ else
+ {
+ if (jmp_i == c3__code)
+ {
+ _uw_arena_grow(CodeArena);
+ }
+ else
+ {
+ return u3m_bail(c3__fail);
+ }
+ continue;
+ }
+ }
+
+ {
+ sat_u->yil_previous = u3k(yil_previous);
+ sat_u->queue = u3k(queue);
+ sat_u->wasm_module = wasm3_module;
+ sat_u->lia_shop = u3k(lia_shop);
+ sat_u->acc = u3k(acc);
+ // sat_u->map to be filled afterwards
+ // sat_u->match same
+ // sat_u->resolution same
+ sat_u->arrow_yil = u3_none;
+ sat_u->susp_list = u3k(susp_list);
+ M3MemoryHeader* mem = u3a_malloc(len_buf_w + sizeof(M3MemoryHeader));
+ mem->runtime = wasm3_runtime;
+ mem->maxStack = BoxArena->buf_y + stk_off_w;
+ mem->length = len_buf_w;
+ u3r_bytes(0, len_buf_w, (u8*)(mem + 1), q_mem_buffer);
+ wasm3_runtime->memory.mallocated = mem;
+ }
+
+ u3z(get);
+
+ return 1;
+ }
+}
+
+// arguments RETAINED, returned yield transfered.
+// transfers sat_u->yil_previous if it is returned, and replaces
+// the struct value with u3_none
+static u3_noun
+_apply_diff(u3_noun input_tag, u3_noun p_input, lia_state* sat_u)
+{
+ m3_SetAllocators(_calloc_bail, _free_bail, _realloc_bail);
+ m3_SetTransientAllocators(_calloc_bail, _free_bail, _realloc_bail);
+ m3_SetMemoryAllocators(u3a_calloc, u3a_free, u3a_realloc);
+
+ if (input_tag == c3y)
+ {
+ if (sat_u->wasm_module->runtime->edge_suspend)
+ {
+ // appended new script but the computation is still suspended:
+ // add script to queue, return previous yield
+ if (sat_u->yil_previous == u3_none)
+ {
+ return u3m_bail(c3__fail);
+ }
+ sat_u->queue = u3kb_weld(sat_u->queue, u3nc(u3k(p_input), u3_nul)); // snoc
+ u3_noun yil = sat_u->yil_previous;
+ sat_u->yil_previous = u3_none;
+ return yil;
+ }
+ else
+ {
+ return _reduce_monad(u3k(p_input), sat_u);
+ }
+ }
+ else
+ {
+ if (!sat_u->wasm_module->runtime->edge_suspend)
+ {
+ // appended external call resolution but no block to resolve:
+ // snoc result to shop, return previous yield
+ if (sat_u->yil_previous == u3_none)
+ {
+ return u3m_bail(c3__fail);
+ }
+ sat_u->lia_shop = u3kb_weld(sat_u->lia_shop, u3nc(u3k(p_input), u3_nul)); // snoc
+ u3_noun yil = sat_u->yil_previous;
+ sat_u->yil_previous = u3_none;
+ return yil;
+ }
+ // else resume
+ IM3Runtime run_u = sat_u->wasm_module->runtime;
+ run_u->resume_external = _resume_callback;
+ run_u->userdata_resume = sat_u;
+ if (sat_u->resolution != u3_none)
+ {
+ return u3m_bail(c3__fail);
+ }
+ sat_u->resolution = u3nc(0, u3k(p_input));
+ M3Result result = m3_Resume(run_u);
+ u3_noun yil;
+ if (result == m3Err_ComputationBlock)
+ {
+ yil = sat_u->resolution;
+ sat_u->resolution = u3_none;
+ if (yil == u3_none)
+ {
+ yil = sat_u->arrow_yil;
+ sat_u->arrow_yil = u3_none;
+ if (yil == u3_none)
+ {
+ return u3m_bail(c3__fail);
+ }
+ }
+ }
+ else if (_deterministic_trap(result))
+ {
+ fprintf(stderr, WUT("function call trapped: %s"), result); // XX get name of entry function?
+ yil = u3nc(2, 0);
+ }
+ else if (result == m3Err_functionImportMissing)
+ {
+ return u3m_bail(c3__exit);
+ }
+ else if (result)
+ {
+ fprintf(stderr, ERR("resumption failed: %s"), result);
+ return u3m_bail(c3__fail);
+ }
+ else
+ {
+ yil = sat_u->resolution;
+ sat_u->resolution = u3_none;
+ if (yil == u3_none)
+ {
+ return u3m_bail(c3__fail);
+ }
+
+ if (sat_u->queue != u3_none)
+ {
+ while (u3h(yil) == 0 && sat_u->queue != u3_nul)
+ {
+ u3z(yil);
+ u3_noun deferred_script = _pop_list(&sat_u->queue);
+ yil = _reduce_monad(deferred_script, sat_u);
+ }
+ }
+ }
+
+ return yil;
+ }
+}
+
+// try to save new state, replacing old state with a tombstone value
+// frees wasm3 memory buffer, releases arenas
+// RETAINS arguments, transfers sat_u->lia_shop/susp_list/queue and
+// replaces them with u3_none if save is succesful
+static void
+_move_state(
+ lia_state* sat_u,
+ u3_noun seed_old,
+ u3_noun seed_new,
+ u3_noun hint,
+ u3_noun yil)
+{
+ if ( (c3__oust == hint)
+ || (2 == u3h(yil))
+ || (c3__rand == hint && 0 == u3h(yil))
+ )
+ {
+ u3z_save_m(u3z_memo_keep, uw_run_m, seed_old, u3_nul);
+ IM3Runtime run_u = sat_u->wasm_module->runtime;
+ M3MemoryHeader* mem_u = run_u->memory.mallocated;
+ u3a_free(mem_u);
+ _uw_arena_free(CodeArena);
+ _uw_arena_free(BoxArena);
+ return;
+ }
+
+ IM3Runtime run_u = sat_u->wasm_module->runtime;
+ M3MemoryHeader* mem_u = run_u->memory.mallocated;
+ c3_w stk_off_w = (u8*)mem_u->maxStack - BoxArena->buf_y;
+ if (c3n == u3a_is_cat(stk_off_w))
+ {
+ u3m_bail(c3__fail);
+ }
+
+ c3_w len_buf_w = mem_u->length;
+ if (c3n == u3a_is_cat(len_buf_w))
+ {
+ u3m_bail(c3__fail);
+ }
+
+ u3_atom q_buf = u3i_bytes(len_buf_w, (c3_y*)(mem_u + 1));
+
+ u3a_free(mem_u);
+
+ m3_RewritePointersRuntime(run_u, BoxArena->buf_y, 1 /*is_store*/);
+ c3_w run_off_w = (c3_y*)run_u - BoxArena->buf_y;
+ if (c3n == u3a_is_cat(run_off_w))
+ {
+ u3m_bail(c3__fail);
+ }
+
+ _uw_arena_free(CodeArena);
+
+ c3_w box_len_w = BoxArena->siz_w;
+ if (c3n == u3a_is_cat(box_len_w))
+ {
+ u3m_bail(c3__fail);
+ }
+
+ c3_y pad_y = BoxArena->pad_y;
+
+ u3_atom q_box = u3i_slab_mint(&BoxArena->sab_u);
+ BoxArena->ini_t = 0;
+
+ u3_noun stash = uw_octo(
+ u3k(yil),
+ sat_u->queue,
+ u3nc(u3nc(box_len_w, q_box), pad_y),
+ u3nc(u3nc(len_buf_w, q_buf), stk_off_w),
+ run_off_w,
+ sat_u->lia_shop,
+ u3k(sat_u->acc), // accumulator will be returned
+ sat_u->susp_list
+ );
+ sat_u->lia_shop = u3_none;
+ sat_u->susp_list = u3_none;
+ sat_u->queue = u3_none;
+
+ u3z_save_m(u3z_memo_keep, uw_run_m, seed_old, u3_nul);
+
+ u3z_save_m(u3z_memo_keep, uw_run_m, seed_new, stash);
+
+ u3z(stash);
+}
+
+u3_weak
+u3we_lia_run_v1(u3_noun cor)
+{
+#ifndef URWASM_STATEFUL
+ return u3_none;
+#else
+
+ u3_noun hint = u3at(u3x_sam_7, cor);
+ if (c3__none == hint)
+ {
+ return u3_none;
+ }
+
+ // strand: save %1, delete in other cases
+ c3_t rand_t = (c3__rand == hint);
+
+ // agent: always save
+ c3_t gent_t = (c3__gent == hint);
+
+ // oust: don't save
+ c3_t oust_t = (c3__oust == hint);
+
+ // omit: run statelessly
+ c3_t omit_t = !(rand_t || gent_t || oust_t);
+
+ #ifdef URWASM_SUBROAD
+
+ // enter subroad, 4MB safety buffer
+ u3m_hate(1 << 20);
+
+ #endif
+
+ u3_noun ctx = u3at(RUN_CTX, cor);
+ u3r_mug(ctx);
+
+ u3_noun input = u3at(u3x_sam_2, cor);
+ u3_noun seed = u3at(u3x_sam_6, cor);
+
+ u3_noun runnable = uw_kick_nock(u3k(ctx), AX_RUNNABLE);
+ u3_noun arrows = KICK1(uw_kick_nock(u3k(ctx), AX_ARROWS));
+
+ u3_noun try_gate = uw_kick_nock(u3k(runnable), AX_TRY);
+ u3_noun try_gate_inner = KICK1(try_gate);
+
+ u3_noun seed_new;
+ u3_noun input_tag, p_input;
+ u3x_cell(input, &input_tag, &p_input);
+
+ if (input_tag == c3y)
+ {
+ u3_noun p_input_gate = u3nt(u3nc(0, 7), 0, u3k(p_input)); // =>(p.input |=(* +>))
+ u3_noun past_new = uw_slam_nock(
+ u3k(try_gate_inner),
+ u3nc(
+ u3k(u3at(seed_past, seed)),
+ p_input_gate
+ )
+ );
+ seed_new = u3nq(
+ u3k(u3at(seed_module, seed)),
+ past_new,
+ u3k(u3at(seed_shop, seed)),
+ u3k(u3at(seed_import, seed))
+ );
+ }
+ else if (input_tag == c3n)
+ {
+ seed_new = u3nq(
+ u3k(u3at(seed_module, seed)),
+ u3k(u3at(seed_past, seed)),
+ u3nc(u3k(p_input), u3k(u3at(seed_shop, seed))),
+ u3k(u3at(seed_import, seed))
+ );
+ }
+ else
+ {
+ return u3m_bail(c3__fail);
+ }
+
+ u3_noun call_script = KICK1(uw_kick_nock(u3k(arrows), AX_CALL));
+ u3_noun memread_script = KICK1(uw_kick_nock(u3k(arrows), AX_MEMREAD));
+ u3_noun memwrite_script = KICK1(uw_kick_nock(u3k(arrows), AX_MEMWRITE));
+ u3_noun call_ext_script = KICK1(uw_kick_nock(u3k(arrows), AX_CALL_EXT));
+ u3_noun global_set_script = KICK1(uw_kick_nock(u3k(arrows), AX_GLOBAL_SET));
+ u3_noun global_get_script = KICK1(uw_kick_nock(u3k(arrows), AX_GLOBAL_GET));
+ u3_noun mem_grow_script = KICK1(uw_kick_nock(u3k(arrows), AX_MEM_GROW));
+ u3_noun mem_size_script = uw_kick_nock(u3k(arrows), AX_MEM_SIZE);
+ u3_noun get_acc_script = uw_kick_nock(u3k(arrows), AX_GET_ACC);
+ u3_noun set_acc_script = KICK1(uw_kick_nock(u3k(arrows), AX_SET_ACC));
+ u3_noun get_all_glob_script = uw_kick_nock(u3k(arrows), AX_GET_ALL_GLOB);
+ u3_noun set_all_glob_script = KICK1(uw_kick_nock( arrows, AX_SET_ALL_GLOB));
+
+ u3_noun try_script = KICK1(try_gate_inner);
+ u3_noun catch_script = KICK2(uw_kick_nock(u3k(runnable), AX_CATCH));
+ u3_noun return_script = KICK1(uw_kick_nock(u3k(runnable), AX_RETURN));
+ u3_noun fail_script = uw_kick_nock( runnable, AX_FAIL);
+
+ u3_noun call_bat = u3k(u3h(call_script));
+ u3_noun memread_bat = u3k(u3h(memread_script));
+ u3_noun memwrite_bat = u3k(u3h(memwrite_script));
+ u3_noun call_ext_bat = u3k(u3h(call_ext_script));
+ u3_noun try_bat = u3k(u3h(try_script));
+ u3_noun catch_bat = u3k(u3h(catch_script));
+ u3_noun return_bat = u3k(u3h(return_script));
+ u3_noun fail_bat = u3k(u3h(fail_script));
+ u3_noun global_set_bat = u3k(u3h(global_set_script));
+ u3_noun global_get_bat = u3k(u3h(global_get_script));
+ u3_noun mem_grow_bat = u3k(u3h(mem_grow_script));
+ u3_noun mem_size_bat = u3k(u3h(mem_size_script));
+ u3_noun get_acc_bat = u3k(u3h(get_acc_script));
+ u3_noun set_acc_bat = u3k(u3h(set_acc_script));
+ u3_noun get_all_glob_bat = u3k(u3h(get_all_glob_script));
+ u3_noun set_all_glob_bat = u3k(u3h(set_all_glob_script));
+
+ u3_noun call_ctx = u3k(u3at(ARROW_CTX, call_script));
+ u3_noun memread_ctx = u3k(u3at(ARROW_CTX, memread_script));
+ u3_noun memwrite_ctx = u3k(u3at(ARROW_CTX, memwrite_script));
+ u3_noun global_set_ctx = u3k(u3at(ARROW_CTX, global_set_script));
+ u3_noun global_get_ctx = u3k(u3at(ARROW_CTX, global_get_script));
+ u3_noun mem_grow_ctx = u3k(u3at(ARROW_CTX, mem_grow_script));
+ u3_noun mem_size_ctx = u3k(u3at(MONAD_CTX, mem_size_script));
+ u3_noun get_all_glob_ctx = u3k(u3at(MONAD_CTX, get_all_glob_script));
+ u3_noun set_all_glob_ctx = u3k(u3at(ARROW_CTX, set_all_glob_script));
+
+ u3z(call_script);
+ u3z(memread_script);
+ u3z(memwrite_script);
+ u3z(call_ext_script);
+ u3z(try_script);
+ u3z(catch_script);
+ u3z(return_script);
+ u3z(fail_script);
+ u3z(global_set_script);
+ u3z(global_get_script);
+ u3z(mem_grow_script);
+ u3z(mem_size_script);
+ u3z(get_acc_script);
+ u3z(set_acc_script);
+ u3z(get_all_glob_script);
+ u3z(set_all_glob_script);
+
+ match_data_struct match = {
+ call_bat,
+ memread_bat,
+ memwrite_bat,
+ call_ext_bat,
+ try_bat,
+ catch_bat,
+ return_bat,
+ fail_bat,
+ global_set_bat,
+ global_get_bat,
+ mem_grow_bat,
+ mem_size_bat,
+ get_acc_bat,
+ set_acc_bat,
+ get_all_glob_bat,
+ set_all_glob_bat,
+ //
+ call_ctx,
+ memread_ctx,
+ memwrite_ctx,
+ global_set_ctx,
+ global_get_ctx,
+ mem_grow_ctx,
+ mem_size_ctx,
+ get_all_glob_ctx,
+ set_all_glob_ctx,
+ };
+
+ lia_state sat;
+
+ BoxArena = &sat.box_arena;
+ CodeArena = &sat.code_arena;
+
+ u3_noun yil;
+ if (!omit_t)
+ {
+ sat.is_stateful = 1;
+ if (_get_state(hint, seed, &sat))
+ {
+ sat.map = u3t(u3at(seed_import, seed));
+ sat.match = &match;
+ sat.resolution = u3_none;
+ yil = _apply_diff(input_tag, p_input, &sat);
+ }
+ else
+ { // instantiate state with retries
+ u3_noun octs = u3at(seed_module, seed_new);
+ u3_noun p_octs, q_octs;
+ u3x_cell(octs, &p_octs, &q_octs);
+ c3_w bin_len_w = (c3y == u3a_is_cat(p_octs)) ? p_octs
+ : u3m_bail(c3__fail);
+ c3_y* bin_y;
+ M3Result result;
+ IM3Environment wasm3_env;
+ IM3Runtime wasm3_runtime = NULL;
+ IM3Module wasm3_module;
+
+ _uw_arena_init(CodeArena);
+ _uw_arena_init(BoxArena);
+
+ m3_SetAllocators(_calloc_box, _free_box, _realloc_box);
+ m3_SetTransientAllocators(_calloc_code, _free_code, _realloc_code);
+ m3_SetMemoryAllocators(u3a_calloc, u3a_free, u3a_realloc);
+ jmp_buf esc;
+ CodeArena->esc_u = BoxArena->esc_u = &esc;
+ c3_i jmp_i;
+
+ while (1)
+ {
+ if (0 == (jmp_i = setjmp(esc)))
+ {
+ bin_y = _calloc_box(bin_len_w, 1);
+ u3r_bytes(0, bin_len_w, bin_y, u3x_atom(q_octs));
+
+ wasm3_env = m3_NewEnvironment();
+ if (!wasm3_env)
+ {
+ fprintf(stderr, ERR("env is null"));
+ return u3m_bail(c3__fail);
+ }
+
+ wasm3_runtime = m3_NewRuntime(
+ wasm3_env,
+ 1 << 21,
+ NULL,
+ 1 /* suspend */
+ );
+ if (!wasm3_runtime)
+ {
+ fprintf(stderr, ERR("runtime is null"));
+ return u3m_bail(c3__fail);
+ }
+
+ result = m3_ParseModule(wasm3_env, &wasm3_module, bin_y, bin_len_w);
+ if (result)
+ {
+ fprintf(stderr, ERR("parse binary error: %s"), result);
+ return u3m_bail(c3__fail);
+ }
+
+ result = m3_LoadModule(wasm3_runtime, wasm3_module);
+ if (result)
+ {
+ fprintf(stderr, ERR("load module error: %s"), result);
+ return u3m_bail(c3__fail);
+ }
+
+ result = m3_ValidateModule(wasm3_module);
+ if (result)
+ {
+ fprintf(stderr, ERR("validation error: %s"), result);
+ return u3m_bail(c3__fail);
+ }
+
+ c3_w n_imports = wasm3_module->numFuncImports;
+ u3_noun lia_shop = u3at(seed_shop, seed_new);
+ u3_noun import = u3at(seed_import, seed_new);
+
+ u3_noun acc, map;
+ u3x_cell(import, &acc, &map);
+ {
+ sat.yil_previous = u3_none;
+ sat.queue = u3_nul;
+ sat.wasm_module = wasm3_module;
+ sat.lia_shop = u3qb_flop(lia_shop);
+ sat.acc = u3k(acc);
+ sat.map = map;
+ sat.match = &match;
+ sat.arrow_yil = u3_none;
+ sat.susp_list = u3_nul;
+ sat.resolution = u3_none;
+ }
+
+ for (c3_w i = 0; i < n_imports; i++)
+ {
+ M3Function f = wasm3_module->functions[i];
+ const char* mod = f.import.moduleUtf8;
+ const char* name = f.import.fieldUtf8;
+
+ result = m3_LinkRawFunctionEx(
+ wasm3_module, mod, name,
+ NULL, &_link_wasm_with_arrow_map,
+ &sat
+ );
+
+ if (result)
+ {
+ fprintf(stderr, ERR("link error: %s"), result);
+ return u3m_bail(c3__fail);
+ }
+ }
+
+ result = m3_CompileModule(wasm3_module);
+ if (result)
+ {
+ fprintf(stderr, ERR("compilation error: %s"), result);
+ return u3m_bail(c3__fail);
+ }
+
+ break;
+ }
+ else
+ {
+ // escaped, grow arena and retry
+ if (wasm3_runtime)
+ {
+ u3a_free(wasm3_runtime->memory.mallocated);
+ }
+
+ if (jmp_i == c3__box)
+ {
+ _uw_arena_grow(BoxArena);
+ _uw_arena_reset(CodeArena);
+ }
+ else if (jmp_i == c3__code)
+ {
+ _uw_arena_grow(CodeArena);
+ _uw_arena_reset(BoxArena);
+ }
+ else
+ {
+ return u3m_bail(c3__fail);
+ }
+
+ continue;
+ }
+ }
+
+ wasm3_runtime->base = BoxArena->buf_y;
+ wasm3_runtime->base_transient = CodeArena->buf_y;
+ // sanity check: struct and code allocators should not be used
+ // when running wasm
+ m3_SetAllocators(_calloc_bail, _free_bail, _realloc_bail);
+ m3_SetTransientAllocators(_calloc_bail, _free_bail, _realloc_bail);
+
+ result = m3_RunStart(wasm3_module);
+
+ if (result == m3Err_ComputationBlock)
+ {
+ yil = sat.arrow_yil;
+ sat.arrow_yil = u3_none;
+ if (yil == u3_none)
+ {
+ return u3m_bail(c3__fail);
+ }
+ }
+ else if (_deterministic_trap(result))
+ {
+ fprintf(stderr, WUT("start function call trapped: %s"), result);
+ yil = u3nc(2, 0);
+ }
+ else if (result == m3Err_functionImportMissing)
+ {
+ return u3m_bail(c3__exit);
+ }
+ else if (result)
+ {
+ fprintf(stderr, ERR("start function failed: %s"), result);
+ return u3m_bail(c3__fail);
+ }
+ else
+ {
+ u3_noun monad = u3at(seed_past, seed_new);
+ yil = _reduce_monad(u3k(monad), &sat);
+ }
+ }
+
+ _move_state(&sat, seed, seed_new, hint, yil);
+ }
+ else
+ {
+ sat.is_stateful = 0;
+ M3Result result;
+ IM3Environment wasm3_env;
+ IM3Runtime wasm3_runtime = NULL;
+ IM3Module wasm3_module;
+ u3_noun p_octs, q_octs;
+
+ u3_noun octs = u3at(seed_module, seed_new);
+ u3x_cell(octs, &p_octs, &q_octs);
+ c3_w bin_len_w = (c3y == u3a_is_cat(p_octs)) ? p_octs
+ : u3m_bail(c3__fail);
+ c3_y* bin_y = u3r_bytes_alloc(0, bin_len_w, u3x_atom(q_octs));
+
+ m3_SetAllocators(u3a_calloc, u3a_free, u3a_realloc);
+ m3_SetTransientAllocators(u3a_calloc, u3a_free, u3a_realloc);
+ m3_SetMemoryAllocators(u3a_calloc, u3a_free, u3a_realloc);
+
+ wasm3_env = m3_NewEnvironment();
+ if (!wasm3_env)
+ {
+ fprintf(stderr, ERR("env is null"));
+ return u3m_bail(c3__fail);
+ }
+
+ // 2MB stack
+ wasm3_runtime = m3_NewRuntime(wasm3_env, 1 << 21, NULL, 0 /* suspend */);
+ if (!wasm3_runtime)
+ {
+ fprintf(stderr, ERR("runtime is null"));
+ return u3m_bail(c3__fail);
+ }
+
+ // save the stack to restore it later before calling m3_FreeRuntime
+ // since it is allocated and freed seperately; no need to do it in
+ // stateful code branch since there we will allocate and free
+ // whole arena
+
+ void* stk_u = wasm3_runtime->stack;
+
+ result = m3_ParseModule(wasm3_env, &wasm3_module, bin_y, bin_len_w);
+ if (result)
+ {
+ fprintf(stderr, ERR("parse binary error: %s"), result);
+ return u3m_bail(c3__fail);
+ }
+
+ result = m3_LoadModule(wasm3_runtime, wasm3_module);
+ if (result)
+ {
+ fprintf(stderr, ERR("load module error: %s"), result);
+ return u3m_bail(c3__fail);
+ }
+
+ result = m3_ValidateModule(wasm3_module);
+ if (result)
+ {
+ fprintf(stderr, ERR("validation error: %s"), result);
+ return u3m_bail(c3__fail);
+ }
+
+ c3_w n_imports = wasm3_module->numFuncImports;
+ u3_noun lia_shop = u3at(seed_shop, seed_new);
+ u3_noun import = u3at(seed_import, seed_new);
+
+ u3_noun acc, map;
+ u3x_cell(import, &acc, &map);
+ {
+ sat.yil_previous = u3_none;
+ sat.queue = u3_none;
+ sat.wasm_module = wasm3_module;
+ sat.lia_shop = u3qb_flop(lia_shop);
+ sat.acc = u3k(acc);
+ sat.map = map;
+ sat.match = &match;
+ sat.arrow_yil = u3_none;
+ sat.susp_list = u3_none;
+ sat.resolution = u3_none;
+ }
+
+ for (c3_w i = 0; i < n_imports; i++)
+ {
+ M3Function f = wasm3_module->functions[i];
+ const char* mod = f.import.moduleUtf8;
+ const char* name = f.import.fieldUtf8;
+
+ result = m3_LinkRawFunctionEx(
+ wasm3_module, mod, name,
+ NULL, &_link_wasm_with_arrow_map,
+ &sat
+ );
+
+ if (result)
+ {
+ fprintf(stderr, ERR("link error: %s"), result);
+ return u3m_bail(c3__fail);
+ }
+ }
+
+ // don't compile module since here we don't care about the ordering
+ // of code pages when we don't suspend, the functions will
+ // get compiled on call
+ //
+
+ result = m3_RunStart(wasm3_module);
+
+ if (result == m3Err_ComputationBlock)
+ {
+ yil = sat.arrow_yil;
+ sat.arrow_yil = u3_none;
+ if (yil == u3_none)
+ {
+ return u3m_bail(c3__fail);
+ }
+ }
+ else if (_deterministic_trap(result))
+ {
+ fprintf(stderr, WUT("start function call trapped: %s"), result);
+ yil = u3nc(2, 0);
+ }
+ else if (result == m3Err_functionImportMissing)
+ {
+ return u3m_bail(c3__exit);
+ }
+ else if (result)
+ {
+ fprintf(stderr, ERR("start function failed: %s"), result);
+ return u3m_bail(c3__fail);
+ }
+ else
+ {
+ u3_noun monad = u3at(seed_past, seed_new);
+ yil = _reduce_monad(u3k(monad), &sat);
+ }
+
+ wasm3_runtime->stack = stk_u;
+ m3_FreeRuntime(wasm3_runtime);
+ m3_FreeEnvironment(wasm3_env);
+ u3a_free(bin_y);
+ }
+
+ // any of these could be u3_none
+ //
+ {
+ u3z(sat.lia_shop);
+ u3z(sat.susp_list);
+ u3z(sat.yil_previous);
+ u3z(sat.queue);
+ }
+
+ u3z(match.call_bat);
+ u3z(match.memread_bat);
+ u3z(match.memwrite_bat);
+ u3z(match.call_ext_bat);
+ u3z(match.try_bat);
+ u3z(match.catch_bat);
+ u3z(match.return_bat);
+ u3z(match.fail_bat);
+ u3z(match.global_set_bat);
+ u3z(match.global_get_bat);
+ u3z(match.mem_grow_bat);
+ u3z(match.mem_size_bat);
+
+ u3z(match.call_ctx);
+ u3z(match.memread_ctx);
+ u3z(match.memwrite_ctx);
+ u3z(global_set_ctx);
+ u3z(global_get_ctx);
+ u3z(mem_grow_ctx);
+ u3z(mem_size_ctx);
+
+ #ifdef URWASM_SUBROAD
+ // exit subroad, copying the result
+ u3_noun pro = u3m_love(u3nc(u3nc(yil, sat.acc), seed_new));
+ #else
+ u3_noun pro = u3nc(u3nc(yil, sat.acc), seed_new);
+ #endif
+
+ return pro;
+
+#endif // URWASM_STATEFUL
+}
+
+
+u3_weak
+u3we_lia_run_once(u3_noun cor)
+{
+ if (c3__none == u3at(u3x_sam_6, cor))
+ {
+ return u3_none;
+ }
+
+ #ifdef URWASM_SUBROAD
+ // enter subroad, 4MB safety buffer
+ u3m_hate(1 << 20);
+ #endif
+
+ u3_noun ctx = u3at(ONCE_CTX, cor);
+ u3r_mug(ctx);
+
+ u3_noun runnable = uw_kick_nock(u3k(ctx), AX_RUNNABLE);
+ u3_noun arrows = KICK1(uw_kick_nock(u3k(ctx), AX_ARROWS));
+
+ u3_noun call_script = KICK1(uw_kick_nock(u3k(arrows), AX_CALL));
+ u3_noun memread_script = KICK1(uw_kick_nock(u3k(arrows), AX_MEMREAD));
+ u3_noun memwrite_script = KICK1(uw_kick_nock(u3k(arrows), AX_MEMWRITE));
+ u3_noun call_ext_script = KICK1(uw_kick_nock(u3k(arrows), AX_CALL_EXT));
+ u3_noun global_set_script = KICK1(uw_kick_nock(u3k(arrows), AX_GLOBAL_SET));
+ u3_noun global_get_script = KICK1(uw_kick_nock(u3k(arrows), AX_GLOBAL_GET));
+ u3_noun mem_grow_script = KICK1(uw_kick_nock(u3k(arrows), AX_MEM_GROW));
+ u3_noun mem_size_script = uw_kick_nock(u3k(arrows), AX_MEM_SIZE);
+ u3_noun get_acc_script = uw_kick_nock(u3k(arrows), AX_GET_ACC);
+ u3_noun set_acc_script = KICK1(uw_kick_nock(u3k(arrows), AX_SET_ACC));
+ u3_noun get_all_glob_script = uw_kick_nock(u3k(arrows), AX_GET_ALL_GLOB);
+ u3_noun set_all_glob_script = KICK1(uw_kick_nock( arrows, AX_SET_ALL_GLOB));
+
+ u3_noun try_script = KICK2(uw_kick_nock(u3k(runnable), AX_TRY));
+ u3_noun catch_script = KICK2(uw_kick_nock(u3k(runnable), AX_CATCH));
+ u3_noun return_script = KICK1(uw_kick_nock(u3k(runnable), AX_RETURN));
+ u3_noun fail_script = uw_kick_nock( runnable, AX_FAIL);
+
+ u3_noun call_bat = u3k(u3h(call_script));
+ u3_noun memread_bat = u3k(u3h(memread_script));
+ u3_noun memwrite_bat = u3k(u3h(memwrite_script));
+ u3_noun call_ext_bat = u3k(u3h(call_ext_script));
+ u3_noun try_bat = u3k(u3h(try_script));
+ u3_noun catch_bat = u3k(u3h(catch_script));
+ u3_noun return_bat = u3k(u3h(return_script));
+ u3_noun fail_bat = u3k(u3h(fail_script));
+ u3_noun global_set_bat = u3k(u3h(global_set_script));
+ u3_noun global_get_bat = u3k(u3h(global_get_script));
+ u3_noun mem_grow_bat = u3k(u3h(mem_grow_script));
+ u3_noun mem_size_bat = u3k(u3h(mem_size_script));
+ u3_noun get_acc_bat = u3k(u3h(get_acc_script));
+ u3_noun set_acc_bat = u3k(u3h(set_acc_script));
+ u3_noun get_all_glob_bat = u3k(u3h(get_all_glob_script));
+ u3_noun set_all_glob_bat = u3k(u3h(set_all_glob_script));
+
+ u3_noun call_ctx = u3k(u3at(ARROW_CTX, call_script));
+ u3_noun memread_ctx = u3k(u3at(ARROW_CTX, memread_script));
+ u3_noun memwrite_ctx = u3k(u3at(ARROW_CTX, memwrite_script));
+ u3_noun global_set_ctx = u3k(u3at(ARROW_CTX, global_set_script));
+ u3_noun global_get_ctx = u3k(u3at(ARROW_CTX, global_get_script));
+ u3_noun mem_grow_ctx = u3k(u3at(ARROW_CTX, mem_grow_script));
+ u3_noun mem_size_ctx = u3k(u3at(MONAD_CTX, mem_size_script));
+ u3_noun get_all_glob_ctx = u3k(u3at(MONAD_CTX, get_all_glob_script));
+ u3_noun set_all_glob_ctx = u3k(u3at(ARROW_CTX, set_all_glob_script));
+
+ u3z(call_script);
+ u3z(memread_script);
+ u3z(memwrite_script);
+ u3z(call_ext_script);
+ u3z(try_script);
+ u3z(catch_script);
+ u3z(return_script);
+ u3z(fail_script);
+ u3z(global_set_script);
+ u3z(global_get_script);
+ u3z(mem_grow_script);
+ u3z(mem_size_script);
+ u3z(get_acc_script);
+ u3z(set_acc_script);
+ u3z(get_all_glob_script);
+ u3z(set_all_glob_script);
+
+
+ match_data_struct match = {
+ call_bat,
+ memread_bat,
+ memwrite_bat,
+ call_ext_bat,
+ try_bat,
+ catch_bat,
+ return_bat,
+ fail_bat,
+ global_set_bat,
+ global_get_bat,
+ mem_grow_bat,
+ mem_size_bat,
+ get_acc_bat,
+ set_acc_bat,
+ get_all_glob_bat,
+ set_all_glob_bat,
+ //
+ call_ctx,
+ memread_ctx,
+ memwrite_ctx,
+ global_set_ctx,
+ global_get_ctx,
+ mem_grow_ctx,
+ mem_size_ctx,
+ get_all_glob_ctx,
+ set_all_glob_ctx,
+ };
+
+ u3_noun octs = u3at(u3x_sam_4, cor);
+ u3_noun p_octs, q_octs;
+ u3x_cell(octs, &p_octs, &q_octs);
+
+ c3_w bin_len_w = (c3y == u3a_is_cat(p_octs)) ? p_octs : u3m_bail(c3__fail);
+ c3_y* bin_y = u3r_bytes_alloc(0, bin_len_w, u3x_atom(q_octs));
+
+ M3Result result;
+
+ m3_SetAllocators(u3a_calloc, u3a_free, u3a_realloc);
+ m3_SetTransientAllocators(u3a_calloc, u3a_free, u3a_realloc);
+ m3_SetMemoryAllocators(u3a_calloc, u3a_free, u3a_realloc);
+
+ IM3Environment wasm3_env = m3_NewEnvironment();
+ if (!wasm3_env)
+ {
+ fprintf(stderr, ERR("env is null"));
+ return u3m_bail(c3__fail);
+ }
+
+ // 2MB stack
+ IM3Runtime wasm3_runtime = m3_NewRuntime(
+ wasm3_env,
+ 1 << 21,
+ NULL,
+ 0 /* suspend */
+ );
+ if (!wasm3_runtime)
+ {
+ fprintf(stderr, ERR("runtime is null"));
+ return u3m_bail(c3__fail);
+ }
+
+ void* stk_u = wasm3_runtime->stack;
+
+ IM3Module wasm3_module;
+ result = m3_ParseModule(wasm3_env, &wasm3_module, bin_y, bin_len_w);
+ if (result)
+ {
+ fprintf(stderr, ERR("parse binary error: %s"), result);
+ return u3m_bail(c3__fail);
+ }
+
+ result = m3_LoadModule(wasm3_runtime, wasm3_module);
+ if (result)
+ {
+ fprintf(stderr, ERR("load module error: %s"), result);
+ return u3m_bail(c3__fail);
+ }
+
+ result = m3_ValidateModule(wasm3_module);
+ if (result)
+ {
+ fprintf(stderr, ERR("validation error: %s"), result);
+ return u3m_bail(c3__fail);
+ }
+
+ c3_w n_imports = wasm3_module->numFuncImports;
+ u3_noun monad = u3at(u3x_sam_7, cor);
+ u3_noun import = u3at(u3x_sam_5, cor);
+
+ u3_noun acc, map;
+ u3x_cell(import, &acc, &map);
+
+ lia_state sat = {
+ wasm3_module,
+ u3_nul,
+ u3k(acc),
+ map,
+ &match,
+ u3_none,
+ u3_none,
+ u3_none
+ };
+
+ sat.is_stateful = 0;
+
+ for (c3_w i = 0; i < n_imports; i++)
+ {
+ M3Function f = wasm3_module->functions[i];
+ const char * mod = f.import.moduleUtf8;
+ const char * name = f.import.fieldUtf8;
+
+ result = m3_LinkRawFunctionEx(
+ wasm3_module, mod, name,
+ NULL, &_link_wasm_with_arrow_map,
+ (void *)&sat
+ );
+
+ if (result)
+ {
+ fprintf(stderr, ERR("link error: %s"), result);
+ return u3m_bail(c3__fail);
+ }
+ }
+
+ u3_noun yil;
+
+ result = m3_RunStart(wasm3_module);
+
+ if (result == m3Err_ComputationBlock)
+ {
+ yil = sat.arrow_yil;
+ sat.arrow_yil = u3_none;
+ if (yil == u3_none)
+ {
+ return u3m_bail(c3__fail);
+ }
+ }
+ else if (_deterministic_trap(result))
+ {
+ fprintf(stderr, WUT("start function call trapped: %s"), result);
+ yil = u3nc(2, 0);
+ }
+ else if (result == m3Err_functionImportMissing)
+ {
+ return u3m_bail(c3__exit);
+ }
+ else if (result)
+ {
+ fprintf(stderr, ERR("start function failed: %s"), result);
+ return u3m_bail(c3__fail);
+ }
+ else
+ {
+ yil = _reduce_monad(u3k(monad), &sat);
+ }
+
+ wasm3_runtime->stack = stk_u;
+ m3_FreeRuntime(wasm3_runtime);
+ m3_FreeEnvironment(wasm3_env);
+
+ u3a_free(bin_y);
+
+ u3z(match.call_bat);
+ u3z(match.memread_bat);
+ u3z(match.memwrite_bat);
+ u3z(match.call_ext_bat);
+ u3z(match.try_bat);
+ u3z(match.catch_bat);
+ u3z(match.return_bat);
+ u3z(match.fail_bat);
+ u3z(match.global_set_bat);
+ u3z(match.global_get_bat);
+ u3z(match.mem_grow_bat);
+ u3z(match.mem_size_bat);
+ u3z(match.get_acc_bat);
+ u3z(match.set_acc_bat);
+ u3z(match.get_all_glob_bat);
+ u3z(match.set_all_glob_bat);
+
+ u3z(match.call_ctx);
+ u3z(match.memread_ctx);
+ u3z(match.memwrite_ctx);
+ u3z(global_set_ctx);
+ u3z(global_get_ctx);
+ u3z(mem_grow_ctx);
+ u3z(mem_size_ctx);
+ u3z(get_all_glob_ctx);
+ u3z(set_all_glob_ctx);
+
+ #ifdef URWASM_SUBROAD
+ // exit subroad, copying the result
+ u3_noun pro = u3m_love(u3nc(yil, sat.acc));
+ #else
+ u3_noun pro = u3nc(yil, sat.acc);
+ #endif
+
+ return pro;
+}