From 569e046ac457060d77cb6afe7b72e0535211b492 Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Fri, 12 May 2023 11:39:56 -0500 Subject: [PATCH] added: WASI support for threads at the platform layer --- CHANGELOG | 2 + compiler/src/wasm_emit.c | 2 +- core/builtin.onyx | 9 +- core/io/stdio.onyx | 2 + core/runtime/platform/wasi/platform.onyx | 34 ++++++- core/runtime/platform/wasi/wasi_defs.onyx | 108 ++++++++++++---------- interpreter/src/wasm/module_parsing.h | 2 +- 7 files changed, 104 insertions(+), 55 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index a7ecc1ea..c4a24056 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -4,6 +4,8 @@ Release v0.1.1 Additions: * Ability to have tags on `#foreign` block procedures. - This will enable the creation different binding generators, such jsbindgen. +* WASI support for threads at the platform layer. + - Requires passing '--multi-threaded' CLI flag. * `#distinct` types can now be made over any type. - Used to be only primitives. * New `logo-new-256.ico` for favicon on website. diff --git a/compiler/src/wasm_emit.c b/compiler/src/wasm_emit.c index 14102044..25e4656b 100644 --- a/compiler/src/wasm_emit.c +++ b/compiler/src/wasm_emit.c @@ -4957,7 +4957,7 @@ void onyx_wasm_module_link(OnyxWasmModule *module, OnyxWasmLinkOptions *options) .kind = WASM_FOREIGN_MEMORY, .min = options->memory_min_size, .max = options->memory_max_size, // NOTE: Why not use all 4 Gigs of memory? - .shared = context.options->runtime == Runtime_Js, + .shared = context.options->use_multi_threading && context.options->runtime != Runtime_Onyx, .mod = options->import_memory_module_name, .name = options->import_memory_import_name, diff --git a/core/builtin.onyx b/core/builtin.onyx index 05dec3c5..6e9ae87d 100644 --- a/core/builtin.onyx +++ b/core/builtin.onyx @@ -540,8 +540,15 @@ Link_Options :: struct { IMPORT_MEMORY_DEFAULT :: true; IMPORT_MEMORY_MODULE_NAME_DEFAULT :: "onyx"; IMPORT_MEMORY_IMPORT_NAME_DEFAULT :: "memory"; + } + + #if runtime.runtime == .Wasi && runtime.Multi_Threading_Enabled { + IMPORT_MEMORY_DEFAULT :: true; + IMPORT_MEMORY_MODULE_NAME_DEFAULT :: "env"; + IMPORT_MEMORY_IMPORT_NAME_DEFAULT :: "memory"; + } - } else { + #if !#defined(IMPORT_MEMORY_DEFAULT) { IMPORT_MEMORY_DEFAULT :: false; IMPORT_MEMORY_MODULE_NAME_DEFAULT :: ""; IMPORT_MEMORY_IMPORT_NAME_DEFAULT :: ""; diff --git a/core/io/stdio.onyx b/core/io/stdio.onyx index 7e61518b..14317c9b 100644 --- a/core/io/stdio.onyx +++ b/core/io/stdio.onyx @@ -66,6 +66,8 @@ __flush_stdio :: () { // that are sent directly to io.write. print :: #match #locked { (x: str) { + if !x do return; + io.write(&stdio.print_writer, x); if x[x.count - 1] == #char "\n" && stdio.auto_flush do __flush_stdio(); }, diff --git a/core/runtime/platform/wasi/platform.onyx b/core/runtime/platform/wasi/platform.onyx index 0394fb3a..9082bcf4 100644 --- a/core/runtime/platform/wasi/platform.onyx +++ b/core/runtime/platform/wasi/platform.onyx @@ -33,7 +33,7 @@ Supports_Processes :: false Supports_Time :: true Supports_Networking :: false Supports_Type_Info :: true -Supports_Threads :: false +Supports_Threads :: true Supports_Env_Vars :: true @@ -129,3 +129,35 @@ __start :: () { __flush_stdio(); } + +#if runtime.Multi_Threading_Enabled { + #local ThreadData :: struct { + id: i32; + tls_base, stack_base: rawptr; + func: (data: rawptr) -> void; + data: rawptr; + } + + __spawn_thread :: ( + id: i32, + tls_base, stack_base: rawptr, + func: (data: rawptr) -> void, data: rawptr) -> void + { + thread_data := core.alloc.on_heap(ThreadData.{ + id, tls_base, stack_base, func, data + }); + + wasi.thread_spawn(thread_data); + } + + // Currently, WASI does not provide a way to kill a thread preemptively. + __kill_thread :: (id: i32) => 0 + + #export "wasi_thread_start" + (wasi_tid: u32, use td: &ThreadData) { + runtime._thread_start(id, tls_base, stack_base, func, data); + runtime._thread_exit(id); + } +} + + diff --git a/core/runtime/platform/wasi/wasi_defs.onyx b/core/runtime/platform/wasi/wasi_defs.onyx index a825dbdb..abd1517a 100644 --- a/core/runtime/platform/wasi/wasi_defs.onyx +++ b/core/runtime/platform/wasi/wasi_defs.onyx @@ -1,5 +1,9 @@ package wasi +#if !#defined(WASI_VERSION) { + WASI_VERSION :: "wasi_snapshot_preview1" +} + Size :: #type u32; Filesize :: #type u64; Timestamp :: #type u64; @@ -365,42 +369,42 @@ PrestatTagged :: struct { // FUNCTIONS -args_get :: (argv: & &u8, argv_buf: &u8) -> Errno #foreign "wasi_snapshot_preview1" "args_get"--- -args_sizes_get :: (argc: &Size, argv_buf_size: &Size) -> Errno #foreign "wasi_snapshot_preview1" "args_sizes_get" --- - -environ_get :: (environ: & &u8, environ_buf: &u8) -> Errno #foreign "wasi_snapshot_preview1" "environ_get" --- -environ_sizes_get :: (environc: &Size, environ_buf_size: &Size) -> Errno #foreign "wasi_snapshot_preview1" "environ_sizes_get" --- - -clock_res_get :: (id: ClockID, resolution: &Timestamp) -> Errno #foreign "wasi_snapshot_preview1" "clock_res_get" --- -clock_time_get :: (id: ClockID, precision: Timestamp, time: &Timestamp) -> Errno #foreign "wasi_snapshot_preview1" "clock_time_get" --- - -fd_advise :: (fd: FileDescriptor, offset: Filesize, len: Filesize, advice: Advice) -> Errno #foreign "wasi_snapshot_preview1" "fd_advise" --- -fd_allocate :: (fd: FileDescriptor, offset: Filesize, len: Filesize) -> Errno #foreign "wasi_snapshot_preview1" "fd_allocate" --- -fd_close :: (fd: FileDescriptor) -> Errno #foreign "wasi_snapshot_preview1" "fd_close" --- -fd_datasync :: (fd: FileDescriptor) -> Errno #foreign "wasi_snapshot_preview1" "fd_datasync" --- -fd_fdstat_get :: (fd: FileDescriptor, stat: &FDStat) -> Errno #foreign "wasi_snapshot_preview1" "fd_fdstat_get" --- -fd_fdstat_set_flags :: (fd: FileDescriptor, flags: FDFlags) -> Errno #foreign "wasi_snapshot_preview1" "fd_fdstat_set_flags" --- -fd_fdstat_set_rights :: (fd: FileDescriptor, rights_base: Rights, rights_inheriting: Rights) -> Errno #foreign "wasi_snapshot_preview1" "fd_fdstat_set_rights" --- -fd_filestat_get :: (fd: FileDescriptor, buf: &FileStat) -> Errno #foreign "wasi_snapshot_preview1" "fd_filestat_get" --- -fd_filestat_set_size :: (fd: FileDescriptor, size: Filesize) -> Errno #foreign "wasi_snapshot_preview1" "fd_filestat_set_size" --- -fd_filestat_set_times :: (fd: FileDescriptor, atim: Timestamp, mtim: Timestamp, fst_flags: FSTFlags) -> Errno #foreign "wasi_snapshot_preview1" "fd_filestat_set_times" --- -fd_pread :: (fd: FileDescriptor, iovs: &IOVec, iovs_len: Size, offset: Filesize, nread: &Size) -> Errno #foreign "wasi_snapshot_preview1" "fd_pread" --- -fd_prestat_get :: (fd: FileDescriptor, buf: &PrestatTagged) -> Errno #foreign "wasi_snapshot_preview1" "fd_prestat_get" --- -fd_prestat_dir_name :: (fd: FileDescriptor, path: str) -> Errno #foreign "wasi_snapshot_preview1" "fd_prestat_dir_name" --- -fd_pwrite :: (fd: FileDescriptor, iovs: &IOVec, iovs_len: Size, offset: Filesize, nwritten: &Size) -> Errno #foreign "wasi_snapshot_preview1" "fd_pwrite" --- -fd_read :: (fd: FileDescriptor, iovs: &IOVec, iovs_len: Size, nread: &Size) -> Errno #foreign "wasi_snapshot_preview1" "fd_read" --- -fd_readdir :: (fd: FileDescriptor, buf: &u8, buf_len: Size, cookie: DirCookie, bufused: &Size) -> Errno #foreign "wasi_snapshot_preview1" "fd_readdir" --- -fd_renumber :: (fd: FileDescriptor, to: FileDescriptor) -> Errno #foreign "wasi_snapshot_preview1" "fd_renumber" --- -fd_seek :: (fd: FileDescriptor, offset: FileDelta, whence: Whence, newoffset: &Filesize) -> Errno #foreign "wasi_snapshot_preview1" "fd_seek" --- -fd_sync :: (fd: FileDescriptor) -> Errno #foreign "wasi_snapshot_preview1" "fd_sync" --- -fd_tell :: (fd: FileDescriptor, offset: &Filesize) -> Errno #foreign "wasi_snapshot_preview1" "fd_tell" --- -fd_write :: (fd: FileDescriptor, iovs: &IOVec, iovs_len: Size, nwritten: &Size) -> Errno #foreign "wasi_snapshot_preview1" "fd_write" --- - -path_create_directory :: (fd: FileDescriptor, path: str) -> Errno #foreign "wasi_snapshot_preview1" "path_create_directory" --- -path_filestat_get :: (fd: FileDescriptor, flags: LookupFlags, path: str, buf: &FileStat) -> Errno #foreign "wasi_snapshot_preview1" "path_filestat_get" --- -path_filestat_set_times :: (fd: FileDescriptor, flags: LookupFlags, path: str, atim: Timestamp, mtim: Timestamp, fst_flags: FSTFlags) -> Errno #foreign "wasi_snapshot_preview1" "path_filestat_set_times" --- - -path_link :: (fd: FileDescriptor, old_flags: LookupFlags, old_path: str, new_fd: FileDescriptor, new_path: str) -> Errno #foreign "wasi_snapshot_preview1" "path_link" --- +args_get :: (argv: & &u8, argv_buf: &u8) -> Errno #foreign WASI_VERSION "args_get"--- +args_sizes_get :: (argc: &Size, argv_buf_size: &Size) -> Errno #foreign WASI_VERSION "args_sizes_get" --- + +environ_get :: (environ: & &u8, environ_buf: &u8) -> Errno #foreign WASI_VERSION "environ_get" --- +environ_sizes_get :: (environc: &Size, environ_buf_size: &Size) -> Errno #foreign WASI_VERSION "environ_sizes_get" --- + +clock_res_get :: (id: ClockID, resolution: &Timestamp) -> Errno #foreign WASI_VERSION "clock_res_get" --- +clock_time_get :: (id: ClockID, precision: Timestamp, time: &Timestamp) -> Errno #foreign WASI_VERSION "clock_time_get" --- + +fd_advise :: (fd: FileDescriptor, offset: Filesize, len: Filesize, advice: Advice) -> Errno #foreign WASI_VERSION "fd_advise" --- +fd_allocate :: (fd: FileDescriptor, offset: Filesize, len: Filesize) -> Errno #foreign WASI_VERSION "fd_allocate" --- +fd_close :: (fd: FileDescriptor) -> Errno #foreign WASI_VERSION "fd_close" --- +fd_datasync :: (fd: FileDescriptor) -> Errno #foreign WASI_VERSION "fd_datasync" --- +fd_fdstat_get :: (fd: FileDescriptor, stat: &FDStat) -> Errno #foreign WASI_VERSION "fd_fdstat_get" --- +fd_fdstat_set_flags :: (fd: FileDescriptor, flags: FDFlags) -> Errno #foreign WASI_VERSION "fd_fdstat_set_flags" --- +fd_fdstat_set_rights :: (fd: FileDescriptor, rights_base: Rights, rights_inheriting: Rights) -> Errno #foreign WASI_VERSION "fd_fdstat_set_rights" --- +fd_filestat_get :: (fd: FileDescriptor, buf: &FileStat) -> Errno #foreign WASI_VERSION "fd_filestat_get" --- +fd_filestat_set_size :: (fd: FileDescriptor, size: Filesize) -> Errno #foreign WASI_VERSION "fd_filestat_set_size" --- +fd_filestat_set_times :: (fd: FileDescriptor, atim: Timestamp, mtim: Timestamp, fst_flags: FSTFlags) -> Errno #foreign WASI_VERSION "fd_filestat_set_times" --- +fd_pread :: (fd: FileDescriptor, iovs: &IOVec, iovs_len: Size, offset: Filesize, nread: &Size) -> Errno #foreign WASI_VERSION "fd_pread" --- +fd_prestat_get :: (fd: FileDescriptor, buf: &PrestatTagged) -> Errno #foreign WASI_VERSION "fd_prestat_get" --- +fd_prestat_dir_name :: (fd: FileDescriptor, path: str) -> Errno #foreign WASI_VERSION "fd_prestat_dir_name" --- +fd_pwrite :: (fd: FileDescriptor, iovs: &IOVec, iovs_len: Size, offset: Filesize, nwritten: &Size) -> Errno #foreign WASI_VERSION "fd_pwrite" --- +fd_read :: (fd: FileDescriptor, iovs: &IOVec, iovs_len: Size, nread: &Size) -> Errno #foreign WASI_VERSION "fd_read" --- +fd_readdir :: (fd: FileDescriptor, buf: &u8, buf_len: Size, cookie: DirCookie, bufused: &Size) -> Errno #foreign WASI_VERSION "fd_readdir" --- +fd_renumber :: (fd: FileDescriptor, to: FileDescriptor) -> Errno #foreign WASI_VERSION "fd_renumber" --- +fd_seek :: (fd: FileDescriptor, offset: FileDelta, whence: Whence, newoffset: &Filesize) -> Errno #foreign WASI_VERSION "fd_seek" --- +fd_sync :: (fd: FileDescriptor) -> Errno #foreign WASI_VERSION "fd_sync" --- +fd_tell :: (fd: FileDescriptor, offset: &Filesize) -> Errno #foreign WASI_VERSION "fd_tell" --- +fd_write :: (fd: FileDescriptor, iovs: &IOVec, iovs_len: Size, nwritten: &Size) -> Errno #foreign WASI_VERSION "fd_write" --- + +path_create_directory :: (fd: FileDescriptor, path: str) -> Errno #foreign WASI_VERSION "path_create_directory" --- +path_filestat_get :: (fd: FileDescriptor, flags: LookupFlags, path: str, buf: &FileStat) -> Errno #foreign WASI_VERSION "path_filestat_get" --- +path_filestat_set_times :: (fd: FileDescriptor, flags: LookupFlags, path: str, atim: Timestamp, mtim: Timestamp, fst_flags: FSTFlags) -> Errno #foreign WASI_VERSION "path_filestat_set_times" --- + +path_link :: (fd: FileDescriptor, old_flags: LookupFlags, old_path: str, new_fd: FileDescriptor, new_path: str) -> Errno #foreign WASI_VERSION "path_link" --- path_open :: (fd: FileDescriptor , dirflags: LookupFlags , path: str @@ -410,26 +414,28 @@ path_open :: (fd: FileDescriptor , fdflags: FDFlags , opened_fd: &FileDescriptor ) -> Errno - #foreign "wasi_snapshot_preview1" "path_open" --- - -path_readlink :: (fd: FileDescriptor, path: str, buf: &u8, buf_len: Size, bufused: &Size) -> Errno #foreign "wasi_snapshot_preview1" "path_readlink" --- -path_remove_directory :: (fd: FileDescriptor, path: str) -> Errno #foreign "wasi_snapshot_preview1" "path_remove_directory" --- -path_rename :: (fd: FileDescriptor, old_path: str, new_fd: FileDescriptor, new_path: str) -> Errno #foreign "wasi_snapshot_preview1" "path_rename" --- -path_symlink :: (old_path: &u8, old_path_len: Size, fd: FileDescriptor, new_path: str) -> Errno #foreign "wasi_snapshot_preview1" "path_symlink" --- -path_unlink_file :: (fd: FileDescriptor, path: str) -> Errno #foreign "wasi_snapshot_preview1" "path_unlink_file" --- + #foreign WASI_VERSION "path_open" --- -poll_oneoff :: (in: &Subscription, out: &Event, nsubscriptions: Size, nevents: &Size) -> Errno #foreign "wasi_snapshot_preview1" "poll_oneoff" --- +path_readlink :: (fd: FileDescriptor, path: str, buf: &u8, buf_len: Size, bufused: &Size) -> Errno #foreign WASI_VERSION "path_readlink" --- +path_remove_directory :: (fd: FileDescriptor, path: str) -> Errno #foreign WASI_VERSION "path_remove_directory" --- +path_rename :: (fd: FileDescriptor, old_path: str, new_fd: FileDescriptor, new_path: str) -> Errno #foreign WASI_VERSION "path_rename" --- +path_symlink :: (old_path: &u8, old_path_len: Size, fd: FileDescriptor, new_path: str) -> Errno #foreign WASI_VERSION "path_symlink" --- +path_unlink_file :: (fd: FileDescriptor, path: str) -> Errno #foreign WASI_VERSION "path_unlink_file" --- -proc_exit :: (rval: ExitCode) -> void #foreign "wasi_snapshot_preview1" "proc_exit" --- -proc_raise :: (sig: Signal) -> Errno #foreign "wasi_snapshot_preview1" "proc_raise" --- +poll_oneoff :: (in: &Subscription, out: &Event, nsubscriptions: Size, nevents: &Size) -> Errno #foreign WASI_VERSION "poll_oneoff" --- -sched_yield :: () -> Errno #foreign "wasi_snapshot_preview1" "sched_yield" --- +proc_exit :: (rval: ExitCode) -> void #foreign WASI_VERSION "proc_exit" --- +proc_raise :: (sig: Signal) -> Errno #foreign WASI_VERSION "proc_raise" --- -random_get :: (buf: &u8, buf_len: Size) -> Errno #foreign "wasi_snapshot_preview1" "random_get" --- +sched_yield :: () -> Errno #foreign WASI_VERSION "sched_yield" --- -sock_recv :: (fd: FileDescriptor, ri_data: &IOVec, ri_data_len: Size, ri_flags: RIFlags, ro_datalen: &Size, ro_flags: &ROFlags) -> Errno #foreign "wasi_snapshot_preview1" "sock_recv" --- -sock_send :: (fd: FileDescriptor, si_data: &IOVec, ri_data_len: Size, si_flags: SIFlags, so_datalen: &Size) -> Errno #foreign "wasi_snapshot_preview1" "sock_send" --- -sock_shutdown :: (fd: FileDescriptor, how: SDFlags) -> Errno #foreign "wasi_snapshot_preview1" "sock_shutdown" --- +random_get :: (buf: &u8, buf_len: Size) -> Errno #foreign WASI_VERSION "random_get" --- +sock_recv :: (fd: FileDescriptor, ri_data: &IOVec, ri_data_len: Size, ri_flags: RIFlags, ro_datalen: &Size, ro_flags: &ROFlags) -> Errno #foreign WASI_VERSION "sock_recv" --- +sock_send :: (fd: FileDescriptor, si_data: &IOVec, ri_data_len: Size, si_flags: SIFlags, so_datalen: &Size) -> Errno #foreign WASI_VERSION "sock_send" --- +sock_shutdown :: (fd: FileDescriptor, how: SDFlags) -> Errno #foreign WASI_VERSION "sock_shutdown" --- +// Whoever decided that THIS ONE IMPORT should use '-' instead of '_' should be fired. +// Spent way too long trying to figure out why I could not import this function... +thread_spawn :: (td: rawptr) -> u32 #foreign WASI_VERSION "thread-spawn" --- diff --git a/interpreter/src/wasm/module_parsing.h b/interpreter/src/wasm/module_parsing.h index 737ff91b..84965a57 100644 --- a/interpreter/src/wasm/module_parsing.h +++ b/interpreter/src/wasm/module_parsing.h @@ -133,7 +133,7 @@ static void parse_type_section(build_context *ctx) { } static wasm_limits_t parse_limits(build_context *ctx) { - bool maximum_present = CONSUME_BYTE(ctx) == 0x01; + bool maximum_present = CONSUME_BYTE(ctx) & 0x01; wasm_limits_t limits; limits.min = uleb128_to_uint(ctx->binary.data, &ctx->offset); -- 2.25.1