From 604551eb2d106d64eb9159bc17aa5c57bbca0ca4 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Sat, 29 Jun 2024 23:09:49 +0200 Subject: [PATCH] wasi: make the demo run on wasi and run it in CI --- .github/workflows/ci.yml | 8 ++++- base/runtime/entry_wasm.odin | 5 +++ base/runtime/os_specific_wasi.odin | 50 ++++++++++++++++++++++++++++-- base/runtime/wasm_allocator.odin | 2 +- core/encoding/cbor/tags.odin | 1 - core/os/os_wasi.odin | 12 +++++++ core/thread/thread.odin | 6 +++- core/thread/thread_js.odin | 47 ---------------------------- core/thread/thread_other.odin | 47 ++++++++++++++++++++++++++++ core/thread/thread_unix.odin | 2 ++ core/thread/thread_windows.odin | 2 ++ examples/demo/demo.odin | 1 + 12 files changed, 129 insertions(+), 54 deletions(-) delete mode 100644 core/thread/thread_js.odin create mode 100644 core/thread/thread_other.odin diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c9c453331..94f6bef12 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -97,7 +97,7 @@ jobs: - name: Download LLVM (MacOS ARM) if: matrix.os == 'macos-14' run: | - brew install llvm@17 + brew install llvm@17 wasmtime echo "/opt/homebrew/opt/llvm@17/bin" >> $GITHUB_PATH - name: Build Odin @@ -147,6 +147,12 @@ jobs: run: ./odin check examples/all -vet -strict-style -target:openbsd_amd64 if: matrix.os == 'ubuntu-latest' + - name: Run demo on WASI WASM32 + run: | + ./odin build examples/demo -target:wasi_wasm32 -vet -strict-style -out:demo.wasm + wasmtime ./demo.wasm + if: matrix.os == 'macos-14' + build_windows: name: Windows Build, Check, and Test runs-on: windows-2022 diff --git a/base/runtime/entry_wasm.odin b/base/runtime/entry_wasm.odin index a24c6f4b7..99cd8201d 100644 --- a/base/runtime/entry_wasm.odin +++ b/base/runtime/entry_wasm.odin @@ -22,6 +22,11 @@ when !ODIN_TEST && !ODIN_NO_ENTRY_POINT { @(link_name="_start", linkage="strong", require, export) _start :: proc "c" () { context = default_context() + + when ODIN_OS == .WASI { + _wasi_setup_args() + } + #force_no_inline _startup_runtime() intrinsics.__entry_point() } diff --git a/base/runtime/os_specific_wasi.odin b/base/runtime/os_specific_wasi.odin index 0e229ac7e..b85d7adea 100644 --- a/base/runtime/os_specific_wasi.odin +++ b/base/runtime/os_specific_wasi.odin @@ -2,10 +2,54 @@ //+private package runtime -import "core:sys/wasm/wasi" +foreign import wasi "wasi_snapshot_preview1" + +@(default_calling_convention="contextless") +foreign wasi { + fd_write :: proc( + fd: i32, + iovs: [][]byte, + n: ^uint, + ) -> u16 --- + + @(private="file") + args_sizes_get :: proc( + num_of_args: ^uint, + size_of_args: ^uint, + ) -> u16 --- + + @(private="file") + args_get :: proc( + argv: [^]cstring, + argv_buf: [^]byte, + ) -> u16 --- +} _stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) { - data_iovec := (wasi.ciovec_t)(data) - n, err := wasi.fd_write(1, {data_iovec}) + n: uint + err := fd_write(1, {data}, &n) return int(n), _OS_Errno(err) } + +_wasi_setup_args :: proc() { + num_of_args, size_of_args: uint + if errno := args_sizes_get(&num_of_args, &size_of_args); errno != 0 { + return + } + + err: Allocator_Error + if args__, err = make([]cstring, num_of_args); err != nil { + return + } + + args_buf: []byte + if args_buf, err = make([]byte, size_of_args); err != nil { + delete(args__) + return + } + + if errno := args_get(raw_data(args__), raw_data(args_buf)); errno != 0 { + delete(args__) + delete(args_buf) + } +} diff --git a/base/runtime/wasm_allocator.odin b/base/runtime/wasm_allocator.odin index fb0600ea2..f4b399c47 100644 --- a/base/runtime/wasm_allocator.odin +++ b/base/runtime/wasm_allocator.odin @@ -760,7 +760,7 @@ free :: proc(a: ^WASM_Allocator, ptr: rawptr, loc := #caller_location) { defer unlock(a) size := region.size - assert(region_is_in_use(region), "double free", loc=loc) + assert(region_is_in_use(region), "double free or corrupt region", loc=loc) prev_region_size_field := ([^]uint)(region)[-1] prev_region_size := prev_region_size_field & ~uint(FREE_REGION_FLAG) diff --git a/core/encoding/cbor/tags.odin b/core/encoding/cbor/tags.odin index 3dc79a5dd..17420af46 100644 --- a/core/encoding/cbor/tags.odin +++ b/core/encoding/cbor/tags.odin @@ -95,7 +95,6 @@ tag_register_number :: proc(impl: Tag_Implementation, nr: Tag_Number, id: string } // Controls initialization of default tag implementations. -// JS and WASI default to a panic allocator so we don't want to do it on those. INITIALIZE_DEFAULT_TAGS :: #config(CBOR_INITIALIZE_DEFAULT_TAGS, !ODIN_DEFAULT_TO_PANIC_ALLOCATOR && !ODIN_DEFAULT_TO_NIL_ALLOCATOR) @(private, init, disabled=!INITIALIZE_DEFAULT_TAGS) diff --git a/core/os/os_wasi.odin b/core/os/os_wasi.odin index 9bfd87322..8a1acb194 100644 --- a/core/os/os_wasi.odin +++ b/core/os/os_wasi.odin @@ -6,6 +6,8 @@ import "base:runtime" Handle :: distinct i32 Errno :: distinct i32 +INVALID_HANDLE :: -1 + ERROR_NONE :: Errno(wasi.errno_t.SUCCESS) O_RDONLY :: 0x00000 @@ -26,6 +28,16 @@ stdout: Handle = 1 stderr: Handle = 2 current_dir: Handle = 3 +args := _alloc_command_line_arguments() + +_alloc_command_line_arguments :: proc() -> (args: []string) { + args = make([]string, len(runtime.args__)) + for &arg, i in args { + arg = string(runtime.args__[i]) + } + return +} + write :: proc(fd: Handle, data: []byte) -> (int, Errno) { iovs := wasi.ciovec_t(data) n, err := wasi.fd_write(wasi.fd_t(fd), {iovs}) diff --git a/core/thread/thread.odin b/core/thread/thread.odin index 55f73d106..80e60d6cf 100644 --- a/core/thread/thread.odin +++ b/core/thread/thread.odin @@ -6,6 +6,8 @@ import "base:intrinsics" _ :: intrinsics +IS_SUPPORTED :: _IS_SUPPORTED + Thread_Proc :: #type proc(^Thread) MAX_USER_ARGUMENTS :: 8 @@ -58,7 +60,9 @@ Thread :: struct { creation_allocator: mem.Allocator, } -#assert(size_of(Thread{}.user_index) == size_of(uintptr)) +when IS_SUPPORTED { + #assert(size_of(Thread{}.user_index) == size_of(uintptr)) +} Thread_Priority :: enum { Normal, diff --git a/core/thread/thread_js.odin b/core/thread/thread_js.odin deleted file mode 100644 index 4f5b5b086..000000000 --- a/core/thread/thread_js.odin +++ /dev/null @@ -1,47 +0,0 @@ -//+build js -package thread - -import "base:intrinsics" -import "core:sync" -import "core:mem" - -Thread_Os_Specific :: struct {} - -_thread_priority_map := [Thread_Priority]i32{ - .Normal = 0, - .Low = -2, - .High = +2, -} - -_create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^Thread { - unimplemented("core:thread procedure not supported on js target") -} - -_start :: proc(t: ^Thread) { - unimplemented("core:thread procedure not supported on js target") -} - -_is_done :: proc(t: ^Thread) -> bool { - unimplemented("core:thread procedure not supported on js target") -} - -_join :: proc(t: ^Thread) { - unimplemented("core:thread procedure not supported on js target") -} - -_join_multiple :: proc(threads: ..^Thread) { - unimplemented("core:thread procedure not supported on js target") -} - -_destroy :: proc(thread: ^Thread) { - unimplemented("core:thread procedure not supported on js target") -} - -_terminate :: proc(using thread : ^Thread, exit_code: int) { - unimplemented("core:thread procedure not supported on js target") -} - -_yield :: proc() { - unimplemented("core:thread procedure not supported on js target") -} - diff --git a/core/thread/thread_other.odin b/core/thread/thread_other.odin new file mode 100644 index 000000000..34bbfda08 --- /dev/null +++ b/core/thread/thread_other.odin @@ -0,0 +1,47 @@ +//+build js, wasi, orca +package thread + +import "base:intrinsics" + +_IS_SUPPORTED :: false + +Thread_Os_Specific :: struct {} + +_thread_priority_map := [Thread_Priority]i32{ + .Normal = 0, + .Low = -2, + .High = +2, +} + +_create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^Thread { + unimplemented("core:thread procedure not supported on target") +} + +_start :: proc(t: ^Thread) { + unimplemented("core:thread procedure not supported on target") +} + +_is_done :: proc(t: ^Thread) -> bool { + unimplemented("core:thread procedure not supported on target") +} + +_join :: proc(t: ^Thread) { + unimplemented("core:thread procedure not supported on target") +} + +_join_multiple :: proc(threads: ..^Thread) { + unimplemented("core:thread procedure not supported on target") +} + +_destroy :: proc(thread: ^Thread) { + unimplemented("core:thread procedure not supported on target") +} + +_terminate :: proc(using thread : ^Thread, exit_code: int) { + unimplemented("core:thread procedure not supported on target") +} + +_yield :: proc() { + unimplemented("core:thread procedure not supported on target") +} + diff --git a/core/thread/thread_unix.odin b/core/thread/thread_unix.odin index 2218afdd3..363f50862 100644 --- a/core/thread/thread_unix.odin +++ b/core/thread/thread_unix.odin @@ -6,6 +6,8 @@ import "core:sync" import "core:sys/unix" import "core:time" +_IS_SUPPORTED :: true + CAS :: sync.atomic_compare_exchange_strong // NOTE(tetra): Aligned here because of core/unix/pthread_linux.odin/pthread_t. diff --git a/core/thread/thread_windows.odin b/core/thread/thread_windows.odin index 314ef5842..8da75a2d9 100644 --- a/core/thread/thread_windows.odin +++ b/core/thread/thread_windows.odin @@ -6,6 +6,8 @@ import "base:intrinsics" import "core:sync" import win32 "core:sys/windows" +_IS_SUPPORTED :: true + Thread_Os_Specific :: struct { win32_thread: win32.HANDLE, win32_thread_id: win32.DWORD, diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin index e27686099..0ad9f4ab0 100644 --- a/examples/demo/demo.odin +++ b/examples/demo/demo.odin @@ -1140,6 +1140,7 @@ prefix_table := [?]string{ print_mutex := b64(false) +@(disabled=!thread.IS_SUPPORTED) threading_example :: proc() { fmt.println("\n# threading_example")