diff --git a/ports/webassembly/Makefile b/ports/webassembly/Makefile index 435636531cc26..bfd518910ad16 100644 --- a/ports/webassembly/Makefile +++ b/ports/webassembly/Makefile @@ -46,7 +46,7 @@ INC += -I$(BUILD) INC += -I$(VARIANT_DIR) CFLAGS += -std=c99 -Wall -Werror -Wdouble-promotion -Wfloat-conversion -CFLAGS += -Os -DNDEBUG +CFLAGS += -Oz -fdata-sections -ffunction-sections -DNDEBUG CFLAGS += $(INC) EXPORTED_FUNCTIONS_EXTRA += ,\ @@ -107,6 +107,7 @@ SRC_SHARED = $(addprefix shared/,\ ) SRC_C += \ + mp_stubs.c \ lexer_dedent.c \ main.c \ modjs.c \ @@ -123,6 +124,12 @@ SRC_JS += \ objpyproxy.js \ proxy_js.js \ +SRC_C_RELOCATABLE = \ + near_api.c \ + modnear.c + +SRC_C_ALL_RELOCATABLE = $(SRC_C) $(SRC_C_RELOCATABLE) + OBJ += $(PY_O) OBJ += $(addprefix $(BUILD)/, $(SRC_SHARED:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) @@ -157,3 +164,29 @@ test_min: $(BUILD)/micropython.min.mjs $(TOP)/tests/run-tests.py # Remaining make rules. include $(TOP)/py/mkrules.mk + +OBJ_EXCLUDE = \ + $(BUILD)/py/modio.o \ + $(BUILD)/py/modsys.o \ + $(BUILD)/py/modbuiltins.o + + +#OBJ_WASM = $(filter-out $(OBJ_EXCLUDE),$(OBJ)) +OBJ_WASM = $(OBJ) +OBJ_WASM += $(BUILD)/mp_stubs.o + +WASM_CFLAGS = -O0 -std=c99 -Wall -Werror -Wdouble-promotion -Wfloat-conversion $(INC) +WASM_LDFLAGS = -Wl,--gc-sections -sSTANDALONE_WASM=0 -sSINGLE_FILE=1 -sALLOW_MEMORY_GROWTH=0 -sWASM_BIGINT=1 + +wasm: $(BUILD)/custom_wasm.o $(OBJ_WASM) + $(ECHO) "LINK $@ $(OBJ_WASM)" + $(Q)emcc $(WASM_CFLAGS) $(WASM_LDFLAGS) \ + -o $(BUILD)/micropython.wasm \ + $^ \ + -s IMPORTED_MEMORY=0 -s ERROR_ON_UNDEFINED_SYMBOLS=0 \ + -sEXPORTED_FUNCTIONS='["_mp_sum","_c_sum","_sanity_check"]' \ + --no-entry + +$(BUILD)/custom_wasm.o: custom_wasm.c + $(ECHO) "CC $<" + $(Q)emcc $(WASM_CFLAGS) -c -o $@ $^ diff --git a/ports/webassembly/compile_micropython.sh b/ports/webassembly/compile_micropython.sh new file mode 100755 index 0000000000000..986fca39cce25 --- /dev/null +++ b/ports/webassembly/compile_micropython.sh @@ -0,0 +1,124 @@ +#!/bin/bash + +#!/bin/bash + +emcc -Oz \ + micropython_exports.c \ + ../../py/mpstate.c \ + ../../py/nlr.c \ + ../../py/obj.c \ + ../../py/objtype.c \ + ../../py/runtime.c \ + ../../py/gc.c \ + ../../py/malloc.c \ + ../../py/qstr.c \ + ../../py/vstr.c \ + ../../py/mpprint.c \ + ../../py/parse.c \ + ../../py/lexer.c \ + ../../py/scope.c \ + ../../py/compile.c \ + ../../py/emitcommon.c \ + ../../py/emitbc.c \ + ../../py/asmbase.c \ + ../../py/asmx64.c \ + ../../py/emitnx64.c \ + ../../py/asmx86.c \ + ../../py/emitnx86.c \ + ../../py/asmthumb.c \ + ../../py/emitnthumb.c \ + ../../py/emitinlinethumb.c \ + ../../py/asmarm.c \ + ../../py/emitnarm.c \ + ../../py/formatfloat.c \ + ../../py/parsenumbase.c \ + ../../py/parsenum.c \ + ../../py/emitglue.c \ + ../../py/persistentcode.c \ + ../../py/scheduler.c \ + ../../py/nativeglue.c \ + ../../py/pairheap.c \ + ../../py/ringbuf.c \ + ../../py/stackctrl.c \ + ../../py/argcheck.c \ + ../../py/warning.c \ + ../../py/profile.c \ + ../../py/map.c \ + ../../py/objarray.c \ + ../../py/objbool.c \ + ../../py/objboundmeth.c \ + ../../py/objcell.c \ + ../../py/objclosure.c \ + ../../py/objcomplex.c \ + ../../py/objdeque.c \ + ../../py/objdict.c \ + ../../py/objenumerate.c \ + ../../py/objexcept.c \ + ../../py/objfilter.c \ + ../../py/objfloat.c \ + ../../py/objfun.c \ + ../../py/objgenerator.c \ + ../../py/objgetitemiter.c \ + ../../py/objint.c \ + ../../py/objlist.c \ + ../../py/objmap.c \ + ../../py/objmodule.c \ + ../../py/objobject.c \ + ../../py/objpolyiter.c \ + ../../py/objproperty.c \ + ../../py/objnone.c \ + ../../py/objnamedtuple.c \ + ../../py/objrange.c \ + ../../py/objreversed.c \ + ../../py/objset.c \ + ../../py/objsingleton.c \ + ../../py/objslice.c \ + ../../py/objstr.c \ + ../../py/objstrunicode.c \ + ../../py/objstringio.c \ + ../../py/objtuple.c \ + ../../py/objtype.c \ + ../../py/objzip.c \ + ../../py/opmethods.c \ + ../../py/sequence.c \ + ../../py/stream.c \ + ../../py/binary.c \ + ../../py/builtinimport.c \ + ../../py/builtinevex.c \ + ../../py/builtinhelp.c \ + ../../py/modarray.c \ + ../../py/modbuiltins.c \ + ../../py/modcollections.c \ + ../../py/modgc.c \ + ../../py/modio.c \ + ../../py/modmath.c \ + ../../py/modcmath.c \ + ../../py/modmicropython.c \ + ../../py/modstruct.c \ + ../../py/modsys.c \ + ../../py/modthread.c \ + ../../py/vm.c \ + ../../py/bc.c \ + ../../py/showbc.c \ + ../../py/repl.c \ + ../../py/smallint.c \ + ../../py/frozenmod.c \ + -o micropython_core.wasm \ + -I. -I../.. -I../../py -Ivariants/standard/ -Ibuild-standard/ \ + -s SIDE_MODULE=2 \ + -s EXPORTED_FUNCTIONS='["_mp_js_init","_mp_js_do_str","_mp_js_get_int"]' \ + -s EXPORTED_RUNTIME_METHODS=[] \ + -s ALLOW_MEMORY_GROWTH=1 \ + --no-entry + + +#emcc -Oz \ +# micropython_exports.c \ +# ../../py/*.c \ +# -o micropython_core.wasm \ +# -I. -I../.. -I../../py -Ivariants/standard/ -Ibuild-standard/ \ +# -s SIDE_MODULE=2 \ +# -s EXPORTED_FUNCTIONS='["_mp_js_init","_mp_js_do_str","_mp_js_get_int"]' \ +# -s EXPORTED_RUNTIME_METHODS=[] \ +# -s ALLOW_MEMORY_GROWTH=0 \ +# --no-entry diff --git a/ports/webassembly/custom_wasm.c b/ports/webassembly/custom_wasm.c new file mode 100644 index 0000000000000..5d2fe33218048 --- /dev/null +++ b/ports/webassembly/custom_wasm.c @@ -0,0 +1,45 @@ +#include +#include "py/mpconfig.h" +#include "py/mpstate.h" +#include "py/builtin.h" +#include "py/obj.h" +#include "py/compile.h" +#include "py/runtime.h" +#include "py/gc.h" + +extern void value_return(uint64_t value_len, uint64_t value_ptr); + +//#define HEAP_SIZE 32768 + +//static char heap[HEAP_SIZE]; + +void init_micropython(void) { + // gc_init(heap, heap + sizeof(heap)); + mp_init(); +} + + +void mp_sum() { + init_micropython(); + + //mp_obj_t args[2] = {mp_obj_new_int(40), mp_obj_new_int(2)}; + //mp_obj_t fun = mp_load_name(qstr_from_str("sum")); + //mp_obj_t result = mp_call_function_n_kw(fun, 2, 0, args); + + //mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, "sum(a, b)", 9, false); + //mp_parse_tree_t pt = mp_parse(lex, MP_PARSE_EVAL_INPUT); + //mp_obj_t module_fun = mp_compile(&pt, lex->source_name, false); + + //mp_obj_t result = mp_call_function_n_kw(module_fun, 2, 0, args); + + //value_return(mp_obj_get_int(result), sizeof(uint32_t)); + value_return(4, 0); +} + +void c_sum(uint32_t a, uint32_t b) { + value_return(sizeof(uint32_t), (a + b)); +} + +void sanity_check() { + value_return(0, 4); +} diff --git a/ports/webassembly/easy.wat b/ports/webassembly/easy.wat new file mode 100644 index 0000000000000..9ee82774ba76b --- /dev/null +++ b/ports/webassembly/easy.wat @@ -0,0 +1,18 @@ +(module + (import "env" "value_return" (func $value_return (param i64 i64))) + (func $hello_easy (export "hello_easy") + ;; Store the value 42 at memory location 0 + i32.const 0 + i32.const 42 + i32.store + + ;; Push the pointer (0) and length (4) of our result onto the stack + i64.const 0 ;; pointer + i64.const 4 ;; length (4 bytes for i32) + + ;; Call value_return + call $value_return + ) + (memory 1) + (export "memory" (memory 0)) +) diff --git a/ports/webassembly/easy_contract.c b/ports/webassembly/easy_contract.c new file mode 100644 index 0000000000000..128bf851740b7 --- /dev/null +++ b/ports/webassembly/easy_contract.c @@ -0,0 +1,9 @@ +#include + +void value_return(uint64_t ptr, uint64_t len); + +__attribute__((export_name("hello_easy_c"))) +void hello_easy_c() { + value_return(0, 4); +} + diff --git a/ports/webassembly/easy_python.c b/ports/webassembly/easy_python.c new file mode 100644 index 0000000000000..246168c1ad800 --- /dev/null +++ b/ports/webassembly/easy_python.c @@ -0,0 +1,18 @@ +#include + +void value_return(uint64_t ptr, uint64_t len); + +void mp_js_init(); +int mp_js_do_str(const char* src); +int mp_js_get_int(const char* name); + +__attribute__((export_name("hello_easy_python"))) +void hello_easy_python() { + //mp_js_init(); + + //mp_js_do_str("result = 42"); + + //int32_t result = mp_js_get_int("result"); + + value_return(0, sizeof(int32_t)); +} diff --git a/ports/webassembly/micropython_exports.c b/ports/webassembly/micropython_exports.c new file mode 100644 index 0000000000000..8233433715f71 --- /dev/null +++ b/ports/webassembly/micropython_exports.c @@ -0,0 +1,35 @@ +#include +#include "py/compile.h" +#include "py/runtime.h" +#include "py/gc.h" +#include "py/mperrno.h" + +#define MICROPY_HEAP_SIZE (256 * 1024) +static char heap[MICROPY_HEAP_SIZE]; + +__attribute__((visibility("default"))) +void mp_js_init(void) { + mp_stack_set_limit(10000); + gc_init(heap, heap + sizeof(heap)); + mp_init(); +} + +__attribute__((visibility("default"))) +void mp_js_do_str(const char* src) { + nlr_buf_t nlr; + if (nlr_push(&nlr) == 0) { + mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, src, strlen(src), 0); + mp_parse_tree_t parse_tree = mp_parse(lex, MP_PARSE_FILE_INPUT); + mp_obj_t module_fun = mp_compile(&parse_tree, lex->source_name, false); + mp_call_function_0(module_fun); + nlr_pop(); + } else { + mp_obj_print_exception(&mp_plat_print, (mp_obj_t)nlr.ret_val); + } +} + +__attribute__((visibility("default"))) +int mp_js_get_int(const char* name) { + mp_obj_t obj = mp_load_name(qstr_from_str(name)); + return mp_obj_get_int(obj); +} diff --git a/ports/webassembly/micropython_lib.c b/ports/webassembly/micropython_lib.c new file mode 100644 index 0000000000000..71fdc8058851a --- /dev/null +++ b/ports/webassembly/micropython_lib.c @@ -0,0 +1,36 @@ +#include +#include "py/compile.h" +#include "py/runtime.h" +#include "py/gc.h" +#include "py/mperrno.h" + +//#define MICROPY_HEAP_SIZE (256 * 1024) +//static char heap[MICROPY_HEAP_SIZE]; + +void mp_init(); + +void mp_js_init(void) { + mp_stack_set_limit(10000); +// gc_init(heap, heap + sizeof(heap)); + mp_init(); +} + +mp_obj_t mp_js_do_str(const char* src) { + //nlr_buf_t nlr; + //if (nlr_push(&nlr) == 0) { + mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, src, strlen(src), 0); + mp_parse_tree_t parse_tree = mp_parse(lex, MP_PARSE_FILE_INPUT); + mp_obj_t module_fun = mp_compile(&parse_tree, lex->source_name, false); + mp_call_function_0(module_fun); + //nlr_pop(); + return mp_const_none; + //} else { + // return (mp_obj_t)nlr.ret_val; + //} + //return mp_const_none; +} + +int mp_js_get_int(const char* name) { + mp_obj_t obj = mp_load_name(qstr_from_str(name)); + return mp_obj_get_int(obj); +} diff --git a/ports/webassembly/modnear.c b/ports/webassembly/modnear.c new file mode 100644 index 0000000000000..668d8191e9d79 --- /dev/null +++ b/ports/webassembly/modnear.c @@ -0,0 +1,60 @@ +#include "py/runtime.h" +#include "py/obj.h" +#include "py/objstr.h" +#include "py/mphal.h" +#include "near_api.h" + +static mp_obj_t mod_near_input(mp_obj_t register_id_obj) { + uint64_t register_id = mp_obj_get_int(register_id_obj); + near_input(register_id); + return mp_const_none; +} + +static mp_obj_t mod_near_read_register(mp_obj_t register_id_obj, mp_obj_t ptr_obj) { + uint64_t register_id = mp_obj_get_int(register_id_obj); + uint64_t ptr = mp_obj_get_int(ptr_obj); + near_read_register(register_id, ptr); + return mp_const_none; +} + +static mp_obj_t mod_near_register_len(mp_obj_t register_id_obj) { + uint64_t register_id = mp_obj_get_int(register_id_obj); + uint64_t len = near_register_len(register_id); + return mp_obj_new_int_from_ull(len); +} + +static mp_obj_t mod_near_value_return(mp_obj_t value_len_obj, mp_obj_t value_ptr_obj) { + uint64_t value_len = mp_obj_get_int(value_len_obj); + uint64_t value_ptr = mp_obj_get_int(value_ptr_obj); + near_value_return(value_len, value_ptr); + return mp_const_none; +} + +static mp_obj_t mod_near_log_utf8(mp_obj_t len_obj, mp_obj_t ptr_obj) { + uint64_t len = mp_obj_get_int(len_obj); + uint64_t ptr = mp_obj_get_int(ptr_obj); + near_log_utf8(len, ptr); + return mp_const_none; +} + +static MP_DEFINE_CONST_FUN_OBJ_1(mod_near_input_obj, mod_near_input); +static MP_DEFINE_CONST_FUN_OBJ_2(mod_near_read_register_obj, mod_near_read_register); +static MP_DEFINE_CONST_FUN_OBJ_1(mod_near_register_len_obj, mod_near_register_len); +static MP_DEFINE_CONST_FUN_OBJ_2(mod_near_value_return_obj, mod_near_value_return); +static MP_DEFINE_CONST_FUN_OBJ_2(mod_near_log_utf8_obj, mod_near_log_utf8); + +static const mp_rom_map_elem_t near_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_near) }, + { MP_ROM_QSTR(MP_QSTR_input), MP_ROM_PTR(&mod_near_input_obj) }, + { MP_ROM_QSTR(MP_QSTR_read_register), MP_ROM_PTR(&mod_near_read_register_obj) }, + { MP_ROM_QSTR(MP_QSTR_register_len), MP_ROM_PTR(&mod_near_register_len_obj) }, + { MP_ROM_QSTR(MP_QSTR_value_return), MP_ROM_PTR(&mod_near_value_return_obj) }, + { MP_ROM_QSTR(MP_QSTR_log_utf8), MP_ROM_PTR(&mod_near_log_utf8_obj) }, +}; +static MP_DEFINE_CONST_DICT(near_module_globals, near_module_globals_table); + +const mp_obj_module_t mp_module_near = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&near_module_globals, +}; + diff --git a/ports/webassembly/mp_stubs.c b/ports/webassembly/mp_stubs.c new file mode 100644 index 0000000000000..5bb9705069795 --- /dev/null +++ b/ports/webassembly/mp_stubs.c @@ -0,0 +1,75 @@ +#include "py/obj.h" +#include "py/mpconfig.h" + +mp_obj_t mp_sys_stdin_obj = MP_OBJ_NULL; +mp_obj_t mp_sys_stdout_obj = MP_OBJ_NULL; +mp_obj_t mp_sys_stderr_obj = MP_OBJ_NULL; + + +#include + +#include + +int32_t invoke_ii(int32_t a, int32_t b) { + return 0; +} + +void invoke_viii(int32_t a, int32_t b, int32_t c, int32_t d) { +} + +int32_t invoke_iiii(int32_t a, int32_t b, int32_t c, int32_t d) { + return 0; +} + +void invoke_v(int32_t a) { +} + +int32_t invoke_iii(int32_t a, int32_t b, int32_t c) { + return 0; +} + +void invoke_vi(int32_t a, int32_t b) { +} + +void invoke_vii(int32_t a, int32_t b, int32_t c) { +} + +int32_t invoke_i(int32_t a) { + return 0; +} + +void mp_js_hook(void) { +} + +int32_t invoke_iiiii(int32_t a, int32_t b, int32_t c, int32_t d, int32_t e) { + return 0; +} + +void invoke_viiii(int32_t a, int32_t b, int32_t c, int32_t d, int32_t e) { +} + +void emscripten_scan_registers(int32_t a) { +} + +uint32_t mp_js_ticks_ms(void) { + return 0; +} + +int fd_write(void) { + return 0; +} + +void _emscripten_throw_longjmp(void) { +} + +uint32_t mp_hal_stdout_tx_strn(const char *str, size_t len) { + return 0; +} + +int __wasi_fd_close(int a) { + return 0; +} + +int __wasi_fd_write(int a, int b, int c, int d) { + return 0; +} diff --git a/ports/webassembly/mpconfigport.h b/ports/webassembly/mpconfigport.h index ab56162ca2b07..74c75bfc7b249 100644 --- a/ports/webassembly/mpconfigport.h +++ b/ports/webassembly/mpconfigport.h @@ -45,41 +45,41 @@ #define MICROPY_ENABLE_GC (1) #define MICROPY_ENABLE_PYSTACK (1) #define MICROPY_STACK_CHECK (0) -#define MICROPY_KBD_EXCEPTION (1) -#define MICROPY_REPL_EVENT_DRIVEN (1) +#define MICROPY_KBD_EXCEPTION (0) +#define MICROPY_REPL_EVENT_DRIVEN (0) #define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) #define MICROPY_ENABLE_DOC_STRING (1) #define MICROPY_WARNINGS (1) #define MICROPY_ERROR_PRINTER (&mp_stderr_print) #define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_DOUBLE) #define MICROPY_USE_INTERNAL_ERRNO (1) -#define MICROPY_USE_INTERNAL_PRINTF (0) +#define MICROPY_USE_INTERNAL_PRINTF (1) #define MICROPY_EPOCH_IS_1970 (1) -#define MICROPY_PY_ASYNCIO_TASK_QUEUE_PUSH_CALLBACK (1) -#define MICROPY_PY_RANDOM_SEED_INIT_FUNC (mp_js_random_u32()) -#define MICROPY_PY_TIME_GMTIME_LOCALTIME_MKTIME (1) -#define MICROPY_PY_TIME_TIME_TIME_NS (1) -#define MICROPY_PY_TIME_INCLUDEFILE "ports/webassembly/modtime.c" +#define MICROPY_PY_ASYNCIO_TASK_QUEUE_PUSH_CALLBACK (0) +//#define MICROPY_PY_RANDOM_SEED_INIT_FUNC (mp_js_random_u32()) +#define MICROPY_PY_TIME_GMTIME_LOCALTIME_MKTIME (0) +#define MICROPY_PY_TIME_TIME_TIME_NS (0) +//#define MICROPY_PY_TIME_INCLUDEFILE "ports/webassembly/modtime.c" #ifndef MICROPY_VFS -#define MICROPY_VFS (1) +#define MICROPY_VFS (0) #endif -#define MICROPY_VFS_POSIX (MICROPY_VFS) +#define MICROPY_VFS_POSIX (0) #define MICROPY_PY_SYS_PLATFORM "webassembly" #ifndef MICROPY_PY_JS -#define MICROPY_PY_JS (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#define MICROPY_PY_JS (0) #endif #ifndef MICROPY_PY_JSFFI -#define MICROPY_PY_JSFFI (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#define MICROPY_PY_JSFFI (0) #endif -#define MICROPY_EVENT_POLL_HOOK \ - do { \ - extern void mp_handle_pending(bool); \ - mp_handle_pending(true); \ - } while (0); +//#define MICROPY_EVENT_POLL_HOOK \ +// do { \ +// extern void mp_handle_pending(bool); \ +// mp_handle_pending(true); \ +// } while (0); // Whether the VM will periodically call mp_js_hook(), which checks for // interrupt characters on stdin (or equivalent input). @@ -126,3 +126,15 @@ typedef long mp_off_t; extern const struct _mp_print_t mp_stderr_print; uint32_t mp_js_random_u32(void); + +#define MICROPY_PY_NEAR (1) + +#if MICROPY_PY_NEAR +extern const struct _mp_obj_module_t mp_module_near; +#define MICROPY_PY_NEAR_DEF { MP_ROM_QSTR(MP_QSTR_near), MP_ROM_PTR(&mp_module_near) }, +#else +#define MICROPY_PY_NEAR_DEF +#endif + +#define MICROPY_PORT_BUILTIN_MODULES \ + MICROPY_PY_NEAR_DEF diff --git a/ports/webassembly/mphalport.c b/ports/webassembly/mphalport.c index 9ab47762e3e97..90c9af5b4d958 100644 --- a/ports/webassembly/mphalport.c +++ b/ports/webassembly/mphalport.c @@ -35,7 +35,7 @@ static void stderr_print_strn(void *env, const char *str, size_t len) { const mp_print_t mp_stderr_print = {NULL, stderr_print_strn}; -mp_uint_t mp_hal_stdout_tx_strn(const char *str, size_t len) { +mp_uint_t __attribute__((weak)) mp_hal_stdout_tx_strn(const char *str, size_t len) { return write(1, str, len); } diff --git a/ports/webassembly/near_api.c b/ports/webassembly/near_api.c new file mode 100644 index 0000000000000..7e4bb19a1932a --- /dev/null +++ b/ports/webassembly/near_api.c @@ -0,0 +1,22 @@ +#include "near_api.h" +#include + +EM_JS(void, near_input, (uint64_t register_id), { + env.input(register_id); +}); + +EM_JS(void, near_read_register, (uint64_t register_id, uint64_t ptr), { + env.read_register(register_id, ptr); +}); + +EM_JS(uint64_t, near_register_len, (uint64_t register_id), { + return env.register_len(register_id); +}); + +EM_JS(void, near_value_return, (uint64_t value_len, uint64_t value_ptr), { + env.value_return(value_len, value_ptr); +}); + +EM_JS(void, near_log_utf8, (uint64_t len, uint64_t ptr), { + env.log_utf8(len, ptr); +}); diff --git a/ports/webassembly/near_api.h b/ports/webassembly/near_api.h new file mode 100644 index 0000000000000..23e7ffe0e22c0 --- /dev/null +++ b/ports/webassembly/near_api.h @@ -0,0 +1,12 @@ +#ifndef MICROPY_INCLUDED_WASM_NEAR_API_H +#define MICROPY_INCLUDED_WASM_NEAR_API_H + +#include "py/obj.h" + +void near_input(uint64_t register_id); +void near_read_register(uint64_t register_id, uint64_t ptr); +uint64_t near_register_len(uint64_t register_id); +void near_value_return(uint64_t value_len, uint64_t value_ptr); +void near_log_utf8(uint64_t len, uint64_t ptr); + +#endif // MICROPY_INCLUDED_WASM_NEAR_API_H diff --git a/ports/webassembly/notes.md b/ports/webassembly/notes.md new file mode 100644 index 0000000000000..0fe2feb0589f7 --- /dev/null +++ b/ports/webassembly/notes.md @@ -0,0 +1,97 @@ +# Notes + +## What I am trying to achieve +A contract with exported function which calls uPy for some calculation and returns result of this calculation. + +## What is achieved +Wrapper and uPy are build and linked together in a single .wasm. + +If uPy is used in runtime, the contract call fails when being prepared for execution by NEAR VM. If uPy is not used by `hello_easy_python`, the exceution of this function succeeds on testnet. + +## What works +(meaning) +- .wasm can be deployed on chain +near contract deploy pywasm-test-account.testnet use-file easy_python.wasm without-init-call network-config testnet sign-with-seed-phrase '***REDACTED***' --seed-phrase-hd-path 'm/44'\''/397'\''/0'\''' send + +- Function can be called on NEAR VM successfully +near contract call-function as-transaction pywasm-test-account.testnet hello_easy_python json-args {} prepaid-gas '30.0 Tgas' attached-deposit '0 NEAR' sign-as pywasm-test-account.testnet network-config testnet sign-with-seed-phrase '***REDACTED***' --seed-phrase-hd-path 'm/44'\''/397'\''/0'\''' send + +### .wat -> .wasm +wat2wasm easy.wat + +### Building and running simple C code -> .wasm +emcc -Oz easy_contract.c -o easy_contract.wasm \ + -s STANDALONE_WASM \ + -s EXPORTED_FUNCTIONS='["_hello_easy_c"]' \ + -s IMPORTED_MEMORY \ + -s ALLOW_MEMORY_GROWTH=0 \ + -s ERROR_ON_UNDEFINED_SYMBOLS=0 + +### Building C -> .wasm, with MPy linked but not used +emcc -c micropython_lib.c -fPIC -I. -I../.. -I../../py -Ivariants/standard/ -Ibuild-standard/ -DMICROPY_PERSISTENT_CODE_LOAD=0 -DMICROPY_VFS_POSIX=0 -o micropython_lib.o + +emcc -Oz easy_python.c micropython_lib.o -o easy_python.wasm -s STANDALONE_WASM -s EXPORTED_FUNCTIONS='["_hello_easy_python"]' -s IMPORTED_MEMORY -s ALLOW_MEMORY_GROWTH=1 -s ERROR_ON_UNDEFINED_SYMBOLS=0 --no-entry + +This is one iteration better, we link **some** Micropython code into the resulting .wasm but if actually used causes VM to crash on compile time (see below). + +## What does not work + +### C -> .wasm + mp_init() +Problems with mp_init() - in runtime - Near VM compilation fails + +Might be a linking issue, or memory handling issue, for example this code would cause the same error: + + +```c + #define MICROPY_HEAP_SIZE (32 * 1024) + static char heap[MICROPY_HEAP_SIZE]; + + __attribute__((export_name("hello_easy_c"))) + void hello_easy_c() { + + uint32_t result = 42; + *((int32_t*)heap) = result; + value_return((uint64_t)heap, sizeof(int32_t)); + } +``` + +Error: + 0: Error: An error occurred during a `FunctionCall` Action, parameter is debug message. + CompilationError(PrepareError(Memory)) + +Location: + src/common.rs:955 + + +### Linking Micropython -> .wasm, then using it as a library +./compile_micropython.sh (fails due to symbols being included twice) + +or building .wasm using new makefile rule "make relocatable" - the resulting file is not self-sufficient and relies on missing mp_init + +Import[484]: + 21 - memory[0] pages: initial=1 <- env.memory + 20 - table[0] type=funcref initial=433 <- env.__indirect_function_table + 19 - global[0] i32 mutable=1 <- env.__stack_pointer + 18 - global[1] i32 mutable=0 <- env.__memory_base + 17 - global[2] i32 mutable=0 <- env.__table_base + 16 - func[0] sig=3 <- env.emscripten_longjmp + 15 - func[1] sig=6 <- env.memset + 14 - func[2] sig=6 <- env.memcpy + 13 - func[3] sig=6 <- env.strncmp + 12 - func[4] sig=6 <- env.memcmp + 11 - func[5] sig=2 <- env.strlen + 10 - func[6] sig=6 <- env.memmove + 9 - func[7] sig=5 <- env.strcmp + 8 - func[8] sig=3 <- env.mp_reader_new_file + 7 - func[9] sig=10 <- env.pow + 6 - func[10] sig=11 <- env.nan + 5 - func[11] sig=5 <- env.invoke_ii + 4 - func[12] sig=5 <__wasm_setjmp_test> <- env.__wasm_setjmp_test + 3 - func[13] sig=0 <- env.setTempRet0 + 2 - func[14] sig=8 <- env.getTempRet0 + 1 - func[15] sig=4 <- env.invoke_viii +71 - func[16] sig=1 <__wasm_setjmp> <- env.__wasm_setjmp +[...] + +Of course, those functions are not available and their implementation should be included in the .wasm + diff --git a/ports/webassembly/qstrdefsport.h b/ports/webassembly/qstrdefsport.h index 421344bd494da..af1976632ed4d 100644 --- a/ports/webassembly/qstrdefsport.h +++ b/ports/webassembly/qstrdefsport.h @@ -2,3 +2,10 @@ // *FORMAT-OFF* Q(/lib) Q(asyncio.core) +Q(near) +Q(input) +Q(read_register) +Q(register_len) +Q(value_return) +Q(log_utf8) + diff --git a/py/misc.h b/py/misc.h index cf1810d4e784b..38bfbf8f8a6e2 100644 --- a/py/misc.h +++ b/py/misc.h @@ -374,8 +374,8 @@ static uint32_t mp_ctz(uint32_t x) { // mp_int_t can be larger than long, i.e. Windows 64-bit, nan-box variants static inline uint32_t mp_clz_mpi(mp_int_t x) { - MP_STATIC_ASSERT(sizeof(mp_int_t) == sizeof(long long) - || sizeof(mp_int_t) == sizeof(long)); + //MP_STATIC_ASSERT(sizeof(mp_int_t) == sizeof(long long) + // || sizeof(mp_int_t) == sizeof(long)); // ugly, but should compile to single intrinsic unless O0 is set if (sizeof(mp_int_t) == sizeof(long)) { pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy