diff --git a/.github/workflows/native_backend_tests_ci.yml b/.github/workflows/native_backend_tests_ci.yml index aae976cf29..4d43fff9d4 100644 --- a/.github/workflows/native_backend_tests_ci.yml +++ b/.github/workflows/native_backend_tests_ci.yml @@ -10,7 +10,6 @@ on: - 'vlib/builtin/**.v' - 'vlib/v/ast/**.v' - 'vlib/v/scanner/**.v' - - 'vlib/v/scanner/**.v' - 'vlib/v/parser/**.v' - 'vlib/v/checker/**.v' - 'vlib/v/gen/c/**.v' @@ -31,7 +30,6 @@ on: - 'vlib/builtin/**.v' - 'vlib/v/ast/**.v' - 'vlib/v/scanner/**.v' - - 'vlib/v/scanner/**.v' - 'vlib/v/parser/**.v' - 'vlib/v/checker/**.v' - 'vlib/v/gen/c/**.v' diff --git a/.github/workflows/wasm_backend_tests_ci.yml b/.github/workflows/wasm_backend_tests_ci.yml new file mode 100644 index 0000000000..bc326b3ce1 --- /dev/null +++ b/.github/workflows/wasm_backend_tests_ci.yml @@ -0,0 +1,95 @@ +name: wasm backend CI + +on: + push: + paths: + - '!**' + - '!**.md' + - 'cmd/tools/builders/**.v' + - 'vlib/builtin/**.v' + - 'vlib/v/ast/**.v' + - 'vlib/v/scanner/**.v' + - 'vlib/v/parser/**.v' + - 'vlib/v/checker/**.v' + - 'vlib/v/gen/c/**.v' + - 'vlib/v/builder/**.v' + - 'vlib/v/cflag/**.v' + - 'vlib/v/live/**.v' + - 'vlib/v/util/**.v' + - 'vlib/v/markused/**.v' + - 'vlib/v/preludes/**.v' + - 'vlib/v/gen/wasm/**.v' + - 'vlib/v/gen/wasm/tests/**.v' + pull_request: + paths: + - '!**' + - '!**.md' + - 'cmd/tools/builders/**.v' + - 'vlib/builtin/**.v' + - 'vlib/v/ast/**.v' + - 'vlib/v/scanner/**.v' + - 'vlib/v/parser/**.v' + - 'vlib/v/checker/**.v' + - 'vlib/v/gen/c/**.v' + - 'vlib/v/builder/**.v' + - 'vlib/v/cflag/**.v' + - 'vlib/v/live/**.v' + - 'vlib/v/util/**.v' + - 'vlib/v/markused/**.v' + - 'vlib/v/preludes/**.v' + - 'vlib/v/gen/wasm/**.v' + - 'vlib/v/gen/wasm/tests/**.v' + +concurrency: + group: wasm-backend-ci-${{ github.event.pull_request.number || github.sha }} + cancel-in-progress: true + +jobs: + wasm-backend-ubuntu: + runs-on: ubuntu-22.04 + if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v' + timeout-minutes: 121 + steps: + - uses: actions/checkout@v3 + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install --quiet -y clang gcc + + - name: Build V + run: make -j4 && ./v symlink -githubci + + - name: Install binaryen as build dependency for the V WASM backend + run: ./v cmd/tools/install_binaryen.vsh + + - name: Build the V WASM backend + run: ./v -cc clang -showcc -v cmd/tools/builders/wasm_builder.v + + - name: Test the WASM backend + run: ./v test vlib/v/gen/wasm/tests/ + + - name: Build examples + run: VTEST_ONLY=wasm ./v build-examples + +## wasm-backend-macos: +## runs-on: macOS-12 +## if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v' +## timeout-minutes: 121 +## steps: +## - uses: actions/checkout@v3 +## +## - name: Build V +## run: make -j4 && ./v symlink -githubci +## +## - name: Install binaryen as build dependency for the V WASM backend +## run: ./v cmd/tools/install_binaryen.vsh +## +## - name: Build the V WASM backend +## run: ./v -cc clang -showcc -v cmd/tools/builders/wasm_builder.v +## +## - name: Test the WASM backend +## run: ./v test vlib/v/gen/wasm/tests/ +## +## - name: Build examples +## run: VTEST_ONLY=wasm ./v build-examples diff --git a/.gitignore b/.gitignore index 5a18286057..2ce7e04499 100644 --- a/.gitignore +++ b/.gitignore @@ -108,6 +108,7 @@ flake.nix .envrc thirdparty/stdatomic/nix/cpp/*.h +thirdparty/binaryen* # ignore VLS log vls.log @@ -120,3 +121,4 @@ vls.log # ignore Intellij files .idea/ /*.iml +wasm.v diff --git a/cmd/tools/builders/wasm_builder.v b/cmd/tools/builders/wasm_builder.v new file mode 100644 index 0000000000..cbeef71eb0 --- /dev/null +++ b/cmd/tools/builders/wasm_builder.v @@ -0,0 +1,7 @@ +module main + +import v.builder.wasmbuilder + +fn main() { + wasmbuilder.start() +} diff --git a/cmd/tools/install_binaryen.vsh b/cmd/tools/install_binaryen.vsh new file mode 100755 index 0000000000..50d40fa7cf --- /dev/null +++ b/cmd/tools/install_binaryen.vsh @@ -0,0 +1,68 @@ +#!/usr/bin/env -S v -raw-vsh-tmp-prefix tmp + +import net.http +import json +import os + +struct JQ { + tag_name string +} + +fn main() { + root := os.real_path(os.dir(os.getenv_opt('VEXE') or { @VEXE })) + os.chdir(root)! // make sure that the workfolder is stable + + tloc := os.join_path(root, 'thirdparty') + loc := os.join_path(tloc, 'binaryen') + + if os.exists(loc) { + eprintln('thirdparty/binaryen exists, will not overwrite') + eprintln('delete the folder, and execute again') + exit(1) + } + + jq := http.get_text('https://api.github.com/repos/WebAssembly/binaryen/releases/latest') + tag := json.decode(JQ, jq)!.tag_name + + name := $if windows { + 'x86_64-windows' + } $else $if macos { + $if arm64 { + 'arm64-macos' + } $else { + 'x86_64-macos' + } + } $else $if linux { + 'x86_64-linux' + } $else { + eprintln('A premade binary library is not available for your system.') + eprintln('Build it from source, following the documentation here: https://github.com/WebAssembly/binaryen/#building') + exit(1) + } + + fname := 'binaryen-${tag}' + url := 'https://github.com/WebAssembly/binaryen/releases/download/${tag}/${fname}-${name}.tar.gz' + + saveloc := os.join_path(tloc, '${fname}.tar.gz') + if !os.exists(saveloc) { + println('Downloading archive: ${saveloc}, from url: ${url} ...') + http.download_file(url, saveloc)! + // defer { os.rm(saveloc) or {}! } + } + + mkdir_all(loc)! + println(loc) + + println('Extracting `${tloc}/${fname}` to `${tloc}/binaryen` ...') + cmd := 'tar -xvf ${saveloc} --directory ${tloc}' + if os.system(cmd) != 0 { + eprintln('`${cmd}` exited with a non zero exit code') + exit(1) + } + + println(cmd) + println('Moving `${tloc}/${fname}` to `${tloc}/binaryen` ...') + + os.rename_dir('${tloc}/${fname}', loc)! + println('Done. You can now use `v -b wasm file.v` .') +} diff --git a/cmd/tools/modules/testing/common.v b/cmd/tools/modules/testing/common.v index 21fb4acf26..bfbbafd971 100644 --- a/cmd/tools/modules/testing/common.v +++ b/cmd/tools/modules/testing/common.v @@ -252,6 +252,18 @@ pub fn new_test_session(_vargs string, will_compile bool) TestSession { $if !macos { skip_files << 'examples/macos_tray/tray.v' } + // examples/wasm/mandelbrot/mandelbrot.v requires special compilation flags: `-b wasm -os browser`, skip it for now: + skip_files << 'examples/wasm/mandelbrot/mandelbrot.v' + + // TODO: always build the wasm_builder in the future, not just when it was build manually before: + wasm_builder_executable := $if !windows { + 'cmd/tools/builders/wasm_builder' + } $else { + 'cmd/tools/builders/wasm_builder.exe' + } + if !os.exists(wasm_builder_executable) { + skip_files << os.join_path('cmd/tools/builders/wasm_builder.v') + } } vargs := _vargs.replace('-progress', '') vexe := pref.vexe_path() diff --git a/cmd/tools/vbuild-examples.v b/cmd/tools/vbuild-examples.v index fbc94d3270..46cc3b9d6e 100644 --- a/cmd/tools/vbuild-examples.v +++ b/cmd/tools/vbuild-examples.v @@ -3,7 +3,7 @@ module main import os import testing -const vroot = @VMODROOT +const vroot = os.dir(os.real_path(os.getenv_opt('VEXE') or { @VEXE })) // build as a project folder const efolders = [ @@ -12,11 +12,14 @@ const efolders = [ 'examples/vweb_fullstack', ] +pub fn normalised_vroot_path(path string) string { + return os.real_path(os.join_path_single(vroot, path)).replace('\\', '/') +} + fn main() { args_string := os.args[1..].join(' ') params := args_string.all_before('build-examples') - skip_prefixes := efolders.map(os.real_path(os.join_path_single(vroot, it)).replace('\\', - '/')) + mut skip_prefixes := efolders.map(normalised_vroot_path(it)) res := testing.v_build_failing_skipped(params, 'examples', skip_prefixes, fn (mut session testing.TestSession) { for x in efolders { pathsegments := x.split_any('/') diff --git a/cmd/tools/vtest-self.v b/cmd/tools/vtest-self.v index ea68d3f2b2..780977a3df 100644 --- a/cmd/tools/vtest-self.v +++ b/cmd/tools/vtest-self.v @@ -329,6 +329,10 @@ fn main() { tsession.skip_files << 'vlib/net/udp_test.v' } + if !os.exists('cmd/tools/builders/wasm_builder') { + tsession.skip_files << 'vlib/v/gen/wasm/tests/wasm_test.v' + } + mut werror := false mut sanitize_memory := false mut sanitize_address := false diff --git a/cmd/v/v.v b/cmd/v/v.v index 9213d6ddef..2f9b2efce4 100755 --- a/cmd/v/v.v +++ b/cmd/v/v.v @@ -192,5 +192,19 @@ fn rebuild(prefs &pref.Preferences) { println('using Go WIP backend...') util.launch_tool(prefs.is_verbose, 'builders/golang_builder', os.args[1..]) } + .wasm { + assert_wasm_backend_thirdparty() + util.launch_tool(prefs.is_verbose, 'builders/wasm_builder', os.args[1..]) + } + } +} + +fn assert_wasm_backend_thirdparty() { + vroot := os.dir(pref.vexe_path()) + if !os.exists('${vroot}/cmd/tools/builders/wasm_builder') + && !os.exists('${vroot}/thirdparty/binaryen') { + eprintln('The WebAssembly backend requires `binaryen`, an external library dependency') + eprintln('This can be installed with `./cmd/tools/install_binaryen.vsh`, to download prebuilt libraries for your platform') + exit(1) } } diff --git a/examples/wasm/.gitignore b/examples/wasm/.gitignore new file mode 100644 index 0000000000..19e1bced9a --- /dev/null +++ b/examples/wasm/.gitignore @@ -0,0 +1 @@ +*.wasm diff --git a/examples/wasm/functions.v b/examples/wasm/functions.v new file mode 100644 index 0000000000..804519db7b --- /dev/null +++ b/examples/wasm/functions.v @@ -0,0 +1,57 @@ +// `pub` functions will be exported. +// +// ``` +// v -b wasm -no-builtin functions.v +// wasmer functions.wasm -i main.powi +// wasmer functions.wasm -i main.gcd +// ``` + +pub fn gcd(a_ i64, b_ i64) i64 { + mut a := a_ + mut b := b_ + if a < 0 { + a = -a + } + if b < 0 { + b = -b + } + for b != 0 { + a %= b + if a == 0 { + return b + } + b %= a + } + return a +} + +pub fn powi(a i64, b i64) i64 { + mut b_ := b + mut p := a + mut v := i64(1) + + if b_ < 0 { // exponent < 0 + if a == 0 { + return -1 // division by 0 + } + return if a * a != 1 { + 0 + } else { + if (b_ & 1) > 0 { + a + } else { + 1 + } + } + } + + for b_ > 0 { + if b_ & 1 > 0 { + v *= p + } + p *= p + b_ >>= 1 + } + + return v +} diff --git a/examples/wasm/hello_world.v b/examples/wasm/hello_world.v new file mode 100644 index 0000000000..3010bdb062 --- /dev/null +++ b/examples/wasm/hello_world.v @@ -0,0 +1,9 @@ +// +// $ v -b wasm hello_world.v +// $ wasmer hello_world.wasm +// Hello WASI! +// + +fn main() { + println('Hello WASI!') +} diff --git a/examples/wasm/mandelbrot/README.md b/examples/wasm/mandelbrot/README.md new file mode 100644 index 0000000000..9c4cb6c4af --- /dev/null +++ b/examples/wasm/mandelbrot/README.md @@ -0,0 +1,12 @@ +# V Mandelbrot Example + +1. First, create `mandelbrot.wasm`. Compile with `-os browser`. + +``` +v -b wasm -os browser mandelbrot.v +``` + +2. Then, open the `mandelbrot.html` file in the browser. + - CORS errors do not allow `mandelbrot.wasm` to be loaded. + - Use `python -m http.server 8080` + - Use `emrun mandelbrot.html` \ No newline at end of file diff --git a/examples/wasm/mandelbrot/mandelbrot.html b/examples/wasm/mandelbrot/mandelbrot.html new file mode 100644 index 0000000000..7d65a30707 --- /dev/null +++ b/examples/wasm/mandelbrot/mandelbrot.html @@ -0,0 +1,47 @@ + + + + + + + V Mandelbrot WebAssembly Example + + + + + + \ No newline at end of file diff --git a/examples/wasm/mandelbrot/mandelbrot.v b/examples/wasm/mandelbrot/mandelbrot.v new file mode 100644 index 0000000000..81f64be0f3 --- /dev/null +++ b/examples/wasm/mandelbrot/mandelbrot.v @@ -0,0 +1,41 @@ +fn JS.canvas_x() int +fn JS.canvas_y() int +fn JS.setpixel(x int, y int, c f64) + +// `main` must be public! +pub fn main() { + max_x := JS.canvas_x() + max_y := JS.canvas_y() + + println('starting main.main!') + + mut y := 0 + for y < max_y { + y += 1 + mut x := 0 + for x < max_x { + x += 1 + + e := (f64(y) / 50) - 1.5 + f := (f64(x) / 50) - 1.0 + + mut a := 0.0 + mut b := 0.0 + mut i := 0.0 + mut j := 0.0 + mut c := 0.0 + + for i * i + j * j < 4 && c < 255 { + i = a * a - b * b + e + j = 2 * a * b + f + a = i + b = j + c += 1 + } + + JS.setpixel(x, y, c) + } + } + + panic('reached the end!') +} diff --git a/vlib/builtin/wasm/alloc.v b/vlib/builtin/wasm/alloc.v new file mode 100644 index 0000000000..6c1d6b9a53 --- /dev/null +++ b/vlib/builtin/wasm/alloc.v @@ -0,0 +1,81 @@ +[has_globals] +module builtin + +// Shitty `sbrk` basic `malloc` and `free` impl +// TODO: implement pure V `walloc` later + +const wasm_page_size = 64 * 1024 + +__global g_heap_base = isize(0) + +fn init() { + g_heap_base = __memory_grow(3) + if g_heap_base == -1 { + panic('g_heap_base: malloc() == nil') + } + g_heap_base *= wasm_page_size +} + +// malloc dynamically allocates a `n` bytes block of memory on the heap. +// malloc returns a `byteptr` pointing to the memory address of the allocated space. +// unlike the `calloc` family of functions - malloc will not zero the memory block. +[unsafe] +pub fn malloc(n isize) &u8 { + if n <= 0 { + panic('malloc(n <= 0)') + } + + res := g_heap_base + g_heap_base += n + + return &u8(res) +} + +// free allows for manually freeing memory allocated at the address `ptr`. +// currently does not free any memory. +[unsafe] +pub fn free(ptr voidptr) { + _ := ptr +} + +// vcalloc dynamically allocates a zeroed `n` bytes block of memory on the heap. +// vcalloc returns a `byteptr` pointing to the memory address of the allocated space. +// Unlike `v_calloc` vcalloc checks for negative values given in `n`. +[unsafe] +pub fn vcalloc(n isize) &u8 { + if n <= 0 { + panic('vcalloc(n <= 0)') + } else if n == 0 { + return &u8(0) + } + + res := unsafe { malloc(n) } + + __memory_fill(res, 0, n) + + return res +} + +// vmemcpy copies n bytes from memory area src to memory area dest. +// The memory areas **CAN** overlap. vmemcpy returns a pointer to `dest`. +[unsafe] +pub fn vmemcpy(dest voidptr, const_src voidptr, n isize) voidptr { + __memory_copy(dest, const_src, n) + return dest +} + +// vmemmove copies n bytes from memory area src to memory area dest. +// The memory areas **CAN** overlap. vmemmove returns a pointer to `dest`. +[unsafe] +pub fn vmemmove(dest voidptr, const_src voidptr, n isize) voidptr { + __memory_copy(dest, const_src, n) + return dest +} + +// vmemset fills the first `n` bytes of the memory area pointed to by `s`, +// with the constant byte `c`. It returns a pointer to the memory area `s`. +[unsafe] +pub fn vmemset(s voidptr, c int, n isize) voidptr { + __memory_fill(s, c, n) + return s +} diff --git a/vlib/builtin/wasm/browser/builtin.v b/vlib/builtin/wasm/browser/builtin.v new file mode 100644 index 0000000000..2e870119f5 --- /dev/null +++ b/vlib/builtin/wasm/browser/builtin.v @@ -0,0 +1,16 @@ +module builtin + +fn JS.__panic_abort(&u8, int) +fn JS.__writeln(&u8, int) + +// panic calls the `__panic_abort` JS panic handler. +[noreturn] +pub fn panic(s string) { + JS.__panic_abort(s.str, s.len) + for {} +} + +// println prints a message with a line end, to stdout. stdout is flushed. +pub fn println(s string) { + JS.__writeln(s.str, s.len) +} diff --git a/vlib/builtin/wasm/builtin.v b/vlib/builtin/wasm/builtin.v new file mode 100644 index 0000000000..079c2c1907 --- /dev/null +++ b/vlib/builtin/wasm/builtin.v @@ -0,0 +1,5 @@ +module builtin + +fn __memory_grow(size isize) isize +fn __memory_fill(dest &u8, value isize, size isize) +fn __memory_copy(dest &u8, src &u8, size isize) diff --git a/vlib/builtin/wasm/string.v b/vlib/builtin/wasm/string.v new file mode 100644 index 0000000000..465d02477f --- /dev/null +++ b/vlib/builtin/wasm/string.v @@ -0,0 +1,7 @@ +module builtin + +pub struct string { +pub: + str &u8 + len int +} diff --git a/vlib/builtin/wasm/wasi/builtin.v b/vlib/builtin/wasm/wasi/builtin.v new file mode 100644 index 0000000000..e0313cf555 --- /dev/null +++ b/vlib/builtin/wasm/wasi/builtin.v @@ -0,0 +1,61 @@ +module builtin + +// print prints a message to stdout. Unlike `println` stdout is not automatically flushed. +pub fn print(s string) { + elm := CIOVec{ + buf: s.str + len: usize(s.len) + } + + WASM.fd_write(1, &elm, 1, -1) +} + +// println prints a message with a line end, to stdout. +pub fn println(s string) { + elm := [CIOVec{ + buf: s.str + len: usize(s.len) + }, CIOVec{ + buf: c'\n' + len: 1 + }]! + + WASM.fd_write(1, &elm[0], 2, -1) +} + +// eprint prints a message to stderr. +pub fn eprint(s string) { + elm := CIOVec{ + buf: s.str + len: usize(s.len) + } + + WASM.fd_write(2, &elm, 1, -1) +} + +// eprintln prints a message with a line end, to stderr. +pub fn eprintln(s string) { + elm := [CIOVec{ + buf: s.str + len: usize(s.len) + }, CIOVec{ + buf: c'\n' + len: 1 + }]! + + WASM.fd_write(2, &elm[0], 2, -1) +} + +// exit terminates execution immediately and returns exit `code` to the shell. +[noreturn] +pub fn exit(code int) { + WASM.proc_exit(code) +} + +// panic prints a nice error message, then exits the process with exit code of 1. +[noreturn] +pub fn panic(s string) { + eprint('V panic: ') + eprintln(s) + exit(1) +} diff --git a/vlib/builtin/wasm/wasi/int.v b/vlib/builtin/wasm/wasi/int.v new file mode 100644 index 0000000000..b526bd8612 --- /dev/null +++ b/vlib/builtin/wasm/wasi/int.v @@ -0,0 +1,232 @@ +module builtin + +type byte = u8 +type i32 = int + +const ( + // digit pairs in reverse order + digit_pairs = '00102030405060708090011121314151617181910212223242526272829203132333435363738393041424344454647484940515253545556575859506162636465666768696071727374757677787970818283848586878889809192939495969798999' +) + +// This implementation is the quickest with gcc -O2 +// str_l returns the string representation of the integer nn with max chars. +[direct_array_access; inline] +fn (nn int) str_l(max int) string { + unsafe { + mut n := i64(nn) + mut d := 0 + if n == 0 { + return '0' + } + + mut is_neg := false + if n < 0 { + n = -n + is_neg = true + } + mut index := max + mut buf := malloc(max + 1) + buf[index] = 0 + index-- + + for n > 0 { + n1 := int(n / 100) + // calculate the digit_pairs start index + d = int(u32(int(n) - (n1 * 100)) << 1) + n = n1 + buf[index] = digit_pairs.str[d] + index-- + d++ + buf[index] = digit_pairs.str[d] + index-- + } + index++ + // remove head zero + if d < 20 { + index++ + } + // Prepend - if it's negative + if is_neg { + index-- + buf[index] = `-` + } + diff := max - index + vmemmove(buf, voidptr(buf + index), diff + 1) + /* + // === manual memory move for bare metal === + mut c:= 0 + for c < diff { + buf[c] = buf[c+index] + c++ + } + buf[c] = 0 + */ + return tos(buf, diff) + + // return tos(memdup(&buf[0] + index, (max - index)), (max - index)) + } +} + +// str returns the value of the `i8` as a `string`. +// Example: assert i8(-2).str() == '-2' +pub fn (n i8) str() string { + return int(n).str_l(5) +} + +// str returns the value of the `i16` as a `string`. +// Example: assert i16(-20).str() == '-20' +pub fn (n i16) str() string { + return int(n).str_l(7) +} + +// str returns the value of the `u16` as a `string`. +// Example: assert u16(20).str() == '20' +pub fn (n u16) str() string { + return int(n).str_l(7) +} + +// str returns the value of the `int` as a `string`. +// Example: assert int(-2020).str() == '-2020' +pub fn (n int) str() string { + return n.str_l(12) +} + +// str returns the value of the `u32` as a `string`. +// Example: assert u32(20000).str() == '20000' +[direct_array_access; inline] +pub fn (nn u32) str() string { + unsafe { + mut n := nn + mut d := u32(0) + if n == 0 { + return '0' + } + max := 12 + mut buf := malloc(max + 1) + mut index := max + buf[index] = 0 + index-- + for n > 0 { + n1 := n / u32(100) + d = ((n - (n1 * u32(100))) << u32(1)) + n = n1 + buf[index] = digit_pairs[d] + index-- + d++ + buf[index] = digit_pairs[d] + index-- + } + index++ + // remove head zero + if d < u32(20) { + index++ + } + diff := max - index + vmemmove(buf, voidptr(buf + index), diff + 1) + return tos(buf, diff) + + // return tos(memdup(&buf[0] + index, (max - index)), (max - index)) + } +} + +// str returns the value of the `int_literal` as a `string`. +[inline] +pub fn (n int_literal) str() string { + return i64(n).str() +} + +// str returns the value of the `i64` as a `string`. +// Example: assert i64(-200000).str() == '-200000' +[direct_array_access; inline] +pub fn (nn i64) str() string { + unsafe { + mut n := nn + mut d := i64(0) + if n == 0 { + return '0' + } else if n == i64(-9223372036854775807 - 1) { + // math.min_i64 + return '-9223372036854775808' + } + max := 20 + mut buf := malloc(max + 1) + mut is_neg := false + if n < 0 { + n = -n + is_neg = true + } + mut index := max + buf[index] = 0 + index-- + for n > 0 { + n1 := n / i64(100) + d = (u32(n - (n1 * i64(100))) << i64(1)) + n = n1 + buf[index] = digit_pairs[d] + index-- + d++ + buf[index] = digit_pairs[d] + index-- + } + index++ + // remove head zero + if d < i64(20) { + index++ + } + // Prepend - if it's negative + if is_neg { + index-- + buf[index] = `-` + } + diff := max - index + vmemmove(buf, voidptr(buf + index), diff + 1) + return tos(buf, diff) + // return tos(memdup(&buf[0] + index, (max - index)), (max - index)) + } +} + +// str returns the value of the `u64` as a `string`. +// Example: assert u64(2000000).str() == '2000000' +[direct_array_access; inline] +pub fn (nn u64) str() string { + unsafe { + mut n := nn + mut d := u64(0) + if n == 0 { + return '0' + } + max := 20 + mut buf := malloc(max + 1) + mut index := max + buf[index] = 0 + index-- + for n > 0 { + n1 := n / 100 + d = ((n - (n1 * 100)) << 1) + n = n1 + buf[index] = digit_pairs[d] + index-- + d++ + buf[index] = digit_pairs[d] + index-- + } + index++ + // remove head zero + if d < 20 { + index++ + } + diff := max - index + vmemmove(buf, voidptr(buf + index), diff + 1) + return tos(buf, diff) + // return tos(memdup(&buf[0] + index, (max - index)), (max - index)) + } +} + +// str returns the value of the `bool` as a `string`. +// Example: assert (2 > 1).str() == 'true' +pub fn (b bool) str() string { + if b { + return 'true' + } + return 'false' +} diff --git a/vlib/builtin/wasm/wasi/string.v b/vlib/builtin/wasm/wasi/string.v new file mode 100644 index 0000000000..fe3df0b7c5 --- /dev/null +++ b/vlib/builtin/wasm/wasi/string.v @@ -0,0 +1,16 @@ +module builtin + +// tos creates a V string, given a C style pointer to a 0 terminated block. +// Note: the memory block pointed by s is *reused, not copied*! +// It will panic, when the pointer `s` is 0. +// See also `tos_clone`. +[unsafe] +pub fn tos(s &u8, len int) string { + if s == 0 { + panic('tos(): nil string') + } + return string{ + str: unsafe { s } + len: len + } +} diff --git a/vlib/builtin/wasm/wasi/wasi.v b/vlib/builtin/wasm/wasi/wasi.v new file mode 100644 index 0000000000..731b9e905e --- /dev/null +++ b/vlib/builtin/wasm/wasi/wasi.v @@ -0,0 +1,14 @@ +[wasm_import_namespace: 'wasi_snapshot_preview1'] +module builtin + +struct CIOVec { + buf &u8 + len usize +} + +type Errno = u16 +type FileDesc = int + +fn WASM.fd_write(fd FileDesc, iovs &CIOVec, iovs_len usize, retptr &usize) Errno +[noreturn] +fn WASM.proc_exit(rval int) diff --git a/vlib/os/os.c.v b/vlib/os/os.c.v index 38b823c536..8b03e2c643 100644 --- a/vlib/os/os.c.v +++ b/vlib/os/os.c.v @@ -211,6 +211,24 @@ pub fn file_size(path string) u64 { return 0 } +// rename_dir renames the folder from `src` to `dst`. +// Use mv to move or rename a file in a platform independent manner. +pub fn rename_dir(src string, dst string) ! { + $if windows { + w_src := src.replace('/', '\\') + w_dst := dst.replace('/', '\\') + ret := C._wrename(w_src.to_wide(), w_dst.to_wide()) + if ret != 0 { + return error_with_code('failed to rename ${src} to ${dst}', int(ret)) + } + } $else { + ret := C.rename(&char(src.str), &char(dst.str)) + if ret != 0 { + return error_with_code('failed to rename ${src} to ${dst}', ret) + } + } +} + // rename renames the file or folder from `src` to `dst`. // Use mv to move or rename a file in a platform independent manner. pub fn rename(src string, dst string) ! { diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index a9722453aa..936a691352 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -889,6 +889,7 @@ pub: is_c2v_prefix bool // for `--x` (`x--$`), only for translated code until c2v can handle it pub mut: expr Expr + typ Type auto_locked string } diff --git a/vlib/v/ast/types.v b/vlib/v/ast/types.v index bbbb6139b3..d031e1c7c9 100644 --- a/vlib/v/ast/types.v +++ b/vlib/v/ast/types.v @@ -36,6 +36,7 @@ pub enum Language { v c js + wasm amd64 // aka x86_64 i386 arm64 // 64-bit arm @@ -887,6 +888,7 @@ pub fn (t &Table) type_size(typ Type) (int, int) { .placeholder, .void, .none_, .generic_inst {} .voidptr, .byteptr, .charptr, .function, .usize, .isize, .any, .thread, .chan { size = t.pointer_size + align = t.pointer_size } .i8, .u8, .char, .bool { size = 1 @@ -1075,6 +1077,7 @@ pub: is_flag bool is_multi_allowed bool uses_exprs bool + typ Type } [minify] diff --git a/vlib/v/builder/compile.v b/vlib/v/builder/compile.v index f8c800e2ef..119bba1f85 100644 --- a/vlib/v/builder/compile.v +++ b/vlib/v/builder/compile.v @@ -197,6 +197,16 @@ pub fn (v Builder) get_builtin_files() []string { if v.pref.backend.is_js() { builtin_files << v.v_files_from_dir(os.join_path(location, 'builtin', 'js')) + } else if v.pref.backend == .wasm { + builtin_files << v.v_files_from_dir(os.join_path(location, 'builtin', + 'wasm')) + if v.pref.os == .browser { + builtin_files << v.v_files_from_dir(os.join_path(location, 'builtin', + 'wasm', 'browser')) + } else { + builtin_files << v.v_files_from_dir(os.join_path(location, 'builtin', + 'wasm', 'wasi')) + } } else { builtin_files << v.v_files_from_dir(os.join_path(location, 'builtin')) } diff --git a/vlib/v/builder/wasmbuilder/wasmbuilder.v b/vlib/v/builder/wasmbuilder/wasmbuilder.v new file mode 100644 index 0000000000..95675a4a53 --- /dev/null +++ b/vlib/v/builder/wasmbuilder/wasmbuilder.v @@ -0,0 +1,36 @@ +module wasmbuilder + +import v.pref +import v.util +import v.builder +import v.gen.wasm + +pub fn start() { + mut args_and_flags := util.join_env_vflags_and_os_args()[1..] + prefs, _ := pref.parse_args([], args_and_flags) + builder.compile('build', prefs, compile_wasm) +} + +pub fn compile_wasm(mut b builder.Builder) { + mut files := b.get_builtin_files() + files << b.get_user_files() + b.set_module_lookup_paths() + if b.pref.is_verbose { + println('all .v files:') + println(files) + } + mut name := b.pref.out_name + if name.ends_with('/-') || name.ends_with(r'\-') || name == '-' { + name = '-' + } else if !name.ends_with('.wasm') { + name += '.wasm' + } + build_wasm(mut b, files, name) +} + +pub fn build_wasm(mut b builder.Builder, v_files []string, out_file string) { + b.front_and_middle_stages(v_files) or { return } + util.timing_start('WebAssembly GEN') + wasm.gen(b.parsed_files, b.table, out_file, b.pref) + util.timing_measure('WebAssembly GEN') +} diff --git a/vlib/v/checker/postfix.v b/vlib/v/checker/postfix.v index 6ebaa21ecb..6fb548c0f7 100644 --- a/vlib/v/checker/postfix.v +++ b/vlib/v/checker/postfix.v @@ -35,5 +35,6 @@ fn (mut c Checker) postfix_expr(mut node ast.PostfixExpr) ast.Type { } else { node.auto_locked, _ = c.fail_if_immutable(node.expr) } + node.typ = typ return typ } diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index 216add9291..b5b850831b 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -196,6 +196,7 @@ fn (mut f Fmt) write_language_prefix(lang ast.Language) { match lang { .c { f.write('C.') } .js { f.write('JS.') } + .wasm { f.write('WASM.') } else {} } } diff --git a/vlib/v/gen/wasm/binaryen/binaryen.c.v b/vlib/v/gen/wasm/binaryen/binaryen.c.v new file mode 100644 index 0000000000..a20d034618 --- /dev/null +++ b/vlib/v/gen/wasm/binaryen/binaryen.c.v @@ -0,0 +1,3944 @@ +[translated] +module binaryen + +$if dynamic_binaryen ? { + #flag -lbinaryen +} $else { + #flag -I@VEXEROOT/thirdparty/binaryen/include + #flag -L@VEXEROOT/thirdparty/binaryen/lib + #flag -lbinaryen + #flag linux -lstdc++ +} + +type Index = u32 +type Type = u64 + +[c: 'BinaryenTypeNone'] +pub fn typenone() Type + +[c: 'BinaryenTypeInt32'] +pub fn typeint32() Type + +[c: 'BinaryenTypeInt64'] +pub fn typeint64() Type + +[c: 'BinaryenTypeFloat32'] +pub fn typefloat32() Type + +[c: 'BinaryenTypeFloat64'] +pub fn typefloat64() Type + +[c: 'BinaryenTypeVec128'] +pub fn typevec128() Type + +[c: 'BinaryenTypeFuncref'] +pub fn typefuncref() Type + +[c: 'BinaryenTypeExternref'] +pub fn typeexternref() Type + +[c: 'BinaryenTypeAnyref'] +pub fn typeanyref() Type + +[c: 'BinaryenTypeEqref'] +pub fn typeeqref() Type + +[c: 'BinaryenTypeI31ref'] +pub fn typei31ref() Type + +[c: 'BinaryenTypeStructref'] +pub fn typestructref() Type + +[c: 'BinaryenTypeArrayref'] +pub fn typearrayref() Type + +[c: 'BinaryenTypeStringref'] +pub fn typestringref() Type + +[c: 'BinaryenTypeStringviewWTF8'] +pub fn typestringviewwtf8() Type + +[c: 'BinaryenTypeStringviewWTF16'] +pub fn typestringviewwtf16() Type + +[c: 'BinaryenTypeStringviewIter'] +pub fn typestringviewiter() Type + +[c: 'BinaryenTypeNullref'] +pub fn typenullref() Type + +[c: 'BinaryenTypeNullExternref'] +pub fn typenullexternref() Type + +[c: 'BinaryenTypeNullFuncref'] +pub fn typenullfuncref() Type + +[c: 'BinaryenTypeUnreachable'] +pub fn typeunreachable() Type + +[c: 'BinaryenTypeAuto'] +pub fn typeauto() Type + +fn (t Type) str() string { + return match t { + typenone() { 'void' } + typeint32() { 'i32' } + typeint64() { 'i64' } + typefloat32() { 'f32' } + typefloat64() { 'f64' } + else { 'unknown binaryen type' } + } +} + +[c: 'BinaryenTypeCreate'] +pub fn typecreate(valuetypes &Type, numtypes Index) Type + +[c: 'BinaryenTypeArity'] +pub fn typearity(t Type) u32 + +[c: 'BinaryenTypeExpand'] +pub fn typeexpand(t Type, buf &Type) + +type PackedType = u32 + +[c: 'BinaryenPackedTypeNotPacked'] +pub fn packedtypenotpacked() PackedType + +[c: 'BinaryenPackedTypeInt8'] +pub fn packedtypeint8() PackedType + +[c: 'BinaryenPackedTypeInt16'] +pub fn packedtypeint16() PackedType + +type HeapType = &u32 + +[c: 'BinaryenHeapTypeExt'] +pub fn heaptypeext() HeapType + +[c: 'BinaryenHeapTypeFunc'] +pub fn heaptypefunc() HeapType + +[c: 'BinaryenHeapTypeAny'] +pub fn heaptypeany() HeapType + +[c: 'BinaryenHeapTypeEq'] +pub fn heaptypeeq() HeapType + +[c: 'BinaryenHeapTypeI31'] +pub fn heaptypei31() HeapType + +[c: 'BinaryenHeapTypeData'] +pub fn heaptypedata() HeapType + +[c: 'BinaryenHeapTypeArray'] +pub fn heaptypearray() HeapType + +[c: 'BinaryenHeapTypeString'] +pub fn heaptypestring() HeapType + +[c: 'BinaryenHeapTypeStringviewWTF8'] +pub fn heaptypestringviewwtf8() HeapType + +[c: 'BinaryenHeapTypeStringviewWTF16'] +pub fn heaptypestringviewwtf16() HeapType + +[c: 'BinaryenHeapTypeStringviewIter'] +pub fn heaptypestringviewiter() HeapType + +[c: 'BinaryenHeapTypeNone'] +pub fn heaptypenone() HeapType + +[c: 'BinaryenHeapTypeNoext'] +pub fn heaptypenoext() HeapType + +[c: 'BinaryenHeapTypeNofunc'] +pub fn heaptypenofunc() HeapType + +[c: 'BinaryenHeapTypeIsBasic'] +pub fn heaptypeisbasic(heaptype HeapType) bool + +[c: 'BinaryenHeapTypeIsSignature'] +pub fn heaptypeissignature(heaptype HeapType) bool + +[c: 'BinaryenHeapTypeIsStruct'] +pub fn heaptypeisstruct(heaptype HeapType) bool + +[c: 'BinaryenHeapTypeIsArray'] +pub fn heaptypeisarray(heaptype HeapType) bool + +[c: 'BinaryenHeapTypeIsBottom'] +pub fn heaptypeisbottom(heaptype HeapType) bool + +[c: 'BinaryenHeapTypeGetBottom'] +pub fn heaptypegetbottom(heaptype HeapType) HeapType + +[c: 'BinaryenHeapTypeIsSubType'] +pub fn heaptypeissubtype(left HeapType, right HeapType) bool + +[c: 'BinaryenStructTypeGetNumFields'] +pub fn structtypegetnumfields(heaptype HeapType) Index + +[c: 'BinaryenStructTypeGetFieldType'] +pub fn structtypegetfieldtype(heaptype HeapType, index Index) Type + +[c: 'BinaryenStructTypeGetFieldPackedType'] +pub fn structtypegetfieldpackedtype(heaptype HeapType, index Index) PackedType + +[c: 'BinaryenStructTypeIsFieldMutable'] +pub fn structtypeisfieldmutable(heaptype HeapType, index Index) bool + +[c: 'BinaryenArrayTypeGetElementType'] +pub fn arraytypegetelementtype(heaptype HeapType) Type + +[c: 'BinaryenArrayTypeGetElementPackedType'] +pub fn arraytypegetelementpackedtype(heaptype HeapType) PackedType + +[c: 'BinaryenArrayTypeIsElementMutable'] +pub fn arraytypeiselementmutable(heaptype HeapType) bool + +[c: 'BinaryenSignatureTypeGetParams'] +pub fn signaturetypegetparams(heaptype HeapType) Type + +[c: 'BinaryenSignatureTypeGetResults'] +pub fn signaturetypegetresults(heaptype HeapType) Type + +[c: 'BinaryenTypeGetHeapType'] +pub fn typegetheaptype(type_ Type) HeapType + +[c: 'BinaryenTypeIsNullable'] +pub fn typeisnullable(type_ Type) bool + +[c: 'BinaryenTypeFromHeapType'] +pub fn typefromheaptype(heaptype HeapType, nullable bool) Type + +type TypeSystem = u32 + +[c: 'BinaryenTypeSystemEquirecursive'] +pub fn typesystemequirecursive() TypeSystem + +[c: 'BinaryenTypeSystemNominal'] +pub fn typesystemnominal() TypeSystem + +[c: 'BinaryenTypeSystemIsorecursive'] +pub fn typesystemisorecursive() TypeSystem + +[c: 'BinaryenGetTypeSystem'] +pub fn gettypesystem() TypeSystem + +[c: 'BinaryenSetTypeSystem'] +pub fn settypesystem(typesystem TypeSystem) + +type ExpressionId = u32 + +[c: 'BinaryenInvalidId'] +pub fn invalidid() ExpressionId + +type ExternalKind = u32 + +[c: 'BinaryenExternalFunction'] +pub fn externalfunction() ExternalKind + +[c: 'BinaryenExternalTable'] +pub fn externaltable() ExternalKind + +[c: 'BinaryenExternalMemory'] +pub fn externalmemory() ExternalKind + +[c: 'BinaryenExternalGlobal'] +pub fn externalglobal() ExternalKind + +[c: 'BinaryenExternalTag'] +pub fn externaltag() ExternalKind + +type Features = u32 + +[c: 'BinaryenFeatureMVP'] +pub fn featuremvp() Features + +[c: 'BinaryenFeatureAtomics'] +pub fn featureatomics() Features + +[c: 'BinaryenFeatureBulkMemory'] +pub fn featurebulkmemory() Features + +[c: 'BinaryenFeatureMutableGlobals'] +pub fn featuremutableglobals() Features + +[c: 'BinaryenFeatureNontrappingFPToInt'] +pub fn featurenontrappingfptoint() Features + +[c: 'BinaryenFeatureSignExt'] +pub fn featuresignext() Features + +[c: 'BinaryenFeatureSIMD128'] +pub fn featuresimd128() Features + +[c: 'BinaryenFeatureExceptionHandling'] +pub fn featureexceptionhandling() Features + +[c: 'BinaryenFeatureTailCall'] +pub fn featuretailcall() Features + +[c: 'BinaryenFeatureReferenceTypes'] +pub fn featurereferencetypes() Features + +[c: 'BinaryenFeatureMultivalue'] +pub fn featuremultivalue() Features + +[c: 'BinaryenFeatureGC'] +pub fn featuregc() Features + +[c: 'BinaryenFeatureMemory64'] +pub fn featurememory64() Features + +[c: 'BinaryenFeatureRelaxedSIMD'] +pub fn featurerelaxedsimd() Features + +[c: 'BinaryenFeatureExtendedConst'] +pub fn featureextendedconst() Features + +[c: 'BinaryenFeatureStrings'] +pub fn featurestrings() Features + +[c: 'BinaryenFeatureMultiMemories'] +pub fn featuremultimemories() Features + +[c: 'BinaryenFeatureAll'] +pub fn featureall() Features + +type Module = voidptr + +[c: 'BinaryenModuleCreate'] +pub fn modulecreate() Module + +[c: 'BinaryenModuleDispose'] +pub fn moduledispose(module_ Module) + +union LiteralContainer { + i32_ int + i64_ i64 + f32_ f32 + f64_ f64 + v128 [16]u8 + func &char +} + +struct Literal { + type_ &u32 + lit LiteralContainer +} + +[c: 'BinaryenLiteralInt32'] +pub fn literalint32(x int) Literal + +[c: 'BinaryenLiteralInt64'] +pub fn literalint64(x i64) Literal + +[c: 'BinaryenLiteralFloat32'] +pub fn literalfloat32(x f32) Literal + +[c: 'BinaryenLiteralFloat64'] +pub fn literalfloat64(x f64) Literal + +[c: 'BinaryenLiteralVec128'] +pub fn literalvec128(x &u8) Literal + +[c: 'BinaryenLiteralFloat32Bits'] +pub fn literalfloat32bits(x int) Literal + +[c: 'BinaryenLiteralFloat64Bits'] +pub fn literalfloat64bits(x i64) Literal + +type Op = int + +[c: 'BinaryenClzInt32'] +pub fn clzint32() Op + +[c: 'BinaryenCtzInt32'] +pub fn ctzint32() Op + +[c: 'BinaryenPopcntInt32'] +pub fn popcntint32() Op + +[c: 'BinaryenNegFloat32'] +pub fn negfloat32() Op + +[c: 'BinaryenAbsFloat32'] +pub fn absfloat32() Op + +[c: 'BinaryenCeilFloat32'] +pub fn ceilfloat32() Op + +[c: 'BinaryenFloorFloat32'] +pub fn floorfloat32() Op + +[c: 'BinaryenTruncFloat32'] +pub fn truncfloat32() Op + +[c: 'BinaryenNearestFloat32'] +pub fn nearestfloat32() Op + +[c: 'BinaryenSqrtFloat32'] +pub fn sqrtfloat32() Op + +[c: 'BinaryenEqZInt32'] +pub fn eqzint32() Op + +[c: 'BinaryenClzInt64'] +pub fn clzint64() Op + +[c: 'BinaryenCtzInt64'] +pub fn ctzint64() Op + +[c: 'BinaryenPopcntInt64'] +pub fn popcntint64() Op + +[c: 'BinaryenNegFloat64'] +pub fn negfloat64() Op + +[c: 'BinaryenAbsFloat64'] +pub fn absfloat64() Op + +[c: 'BinaryenCeilFloat64'] +pub fn ceilfloat64() Op + +[c: 'BinaryenFloorFloat64'] +pub fn floorfloat64() Op + +[c: 'BinaryenTruncFloat64'] +pub fn truncfloat64() Op + +[c: 'BinaryenNearestFloat64'] +pub fn nearestfloat64() Op + +[c: 'BinaryenSqrtFloat64'] +pub fn sqrtfloat64() Op + +[c: 'BinaryenEqZInt64'] +pub fn eqzint64() Op + +[c: 'BinaryenExtendSInt32'] +pub fn extendsint32() Op + +[c: 'BinaryenExtendUInt32'] +pub fn extenduint32() Op + +[c: 'BinaryenWrapInt64'] +pub fn wrapint64() Op + +[c: 'BinaryenTruncSFloat32ToInt32'] +pub fn truncsfloat32toint32() Op + +[c: 'BinaryenTruncSFloat32ToInt64'] +pub fn truncsfloat32toint64() Op + +[c: 'BinaryenTruncUFloat32ToInt32'] +pub fn truncufloat32toint32() Op + +[c: 'BinaryenTruncUFloat32ToInt64'] +pub fn truncufloat32toint64() Op + +[c: 'BinaryenTruncSFloat64ToInt32'] +pub fn truncsfloat64toint32() Op + +[c: 'BinaryenTruncSFloat64ToInt64'] +pub fn truncsfloat64toint64() Op + +[c: 'BinaryenTruncUFloat64ToInt32'] +pub fn truncufloat64toint32() Op + +[c: 'BinaryenTruncUFloat64ToInt64'] +pub fn truncufloat64toint64() Op + +[c: 'BinaryenReinterpretFloat32'] +pub fn reinterpretfloat32() Op + +[c: 'BinaryenReinterpretFloat64'] +pub fn reinterpretfloat64() Op + +[c: 'BinaryenConvertSInt32ToFloat32'] +pub fn convertsint32tofloat32() Op + +[c: 'BinaryenConvertSInt32ToFloat64'] +pub fn convertsint32tofloat64() Op + +[c: 'BinaryenConvertUInt32ToFloat32'] +pub fn convertuint32tofloat32() Op + +[c: 'BinaryenConvertUInt32ToFloat64'] +pub fn convertuint32tofloat64() Op + +[c: 'BinaryenConvertSInt64ToFloat32'] +pub fn convertsint64tofloat32() Op + +[c: 'BinaryenConvertSInt64ToFloat64'] +pub fn convertsint64tofloat64() Op + +[c: 'BinaryenConvertUInt64ToFloat32'] +pub fn convertuint64tofloat32() Op + +[c: 'BinaryenConvertUInt64ToFloat64'] +pub fn convertuint64tofloat64() Op + +[c: 'BinaryenPromoteFloat32'] +pub fn promotefloat32() Op + +[c: 'BinaryenDemoteFloat64'] +pub fn demotefloat64() Op + +[c: 'BinaryenReinterpretInt32'] +pub fn reinterpretint32() Op + +[c: 'BinaryenReinterpretInt64'] +pub fn reinterpretint64() Op + +[c: 'BinaryenExtendS8Int32'] +pub fn extends8int32() Op + +[c: 'BinaryenExtendS16Int32'] +pub fn extends16int32() Op + +[c: 'BinaryenExtendS8Int64'] +pub fn extends8int64() Op + +[c: 'BinaryenExtendS16Int64'] +pub fn extends16int64() Op + +[c: 'BinaryenExtendS32Int64'] +pub fn extends32int64() Op + +[c: 'BinaryenAddInt32'] +pub fn addint32() Op + +[c: 'BinaryenSubInt32'] +pub fn subint32() Op + +[c: 'BinaryenMulInt32'] +pub fn mulint32() Op + +[c: 'BinaryenDivSInt32'] +pub fn divsint32() Op + +[c: 'BinaryenDivUInt32'] +pub fn divuint32() Op + +[c: 'BinaryenRemSInt32'] +pub fn remsint32() Op + +[c: 'BinaryenRemUInt32'] +pub fn remuint32() Op + +[c: 'BinaryenAndInt32'] +pub fn andint32() Op + +[c: 'BinaryenOrInt32'] +pub fn orint32() Op + +[c: 'BinaryenXorInt32'] +pub fn xorint32() Op + +[c: 'BinaryenShlInt32'] +pub fn shlint32() Op + +[c: 'BinaryenShrUInt32'] +pub fn shruint32() Op + +[c: 'BinaryenShrSInt32'] +pub fn shrsint32() Op + +[c: 'BinaryenRotLInt32'] +pub fn rotlint32() Op + +[c: 'BinaryenRotRInt32'] +pub fn rotrint32() Op + +[c: 'BinaryenEqInt32'] +pub fn eqint32() Op + +[c: 'BinaryenNeInt32'] +pub fn neint32() Op + +[c: 'BinaryenLtSInt32'] +pub fn ltsint32() Op + +[c: 'BinaryenLtUInt32'] +pub fn ltuint32() Op + +[c: 'BinaryenLeSInt32'] +pub fn lesint32() Op + +[c: 'BinaryenLeUInt32'] +pub fn leuint32() Op + +[c: 'BinaryenGtSInt32'] +pub fn gtsint32() Op + +[c: 'BinaryenGtUInt32'] +pub fn gtuint32() Op + +[c: 'BinaryenGeSInt32'] +pub fn gesint32() Op + +[c: 'BinaryenGeUInt32'] +pub fn geuint32() Op + +[c: 'BinaryenAddInt64'] +pub fn addint64() Op + +[c: 'BinaryenSubInt64'] +pub fn subint64() Op + +[c: 'BinaryenMulInt64'] +pub fn mulint64() Op + +[c: 'BinaryenDivSInt64'] +pub fn divsint64() Op + +[c: 'BinaryenDivUInt64'] +pub fn divuint64() Op + +[c: 'BinaryenRemSInt64'] +pub fn remsint64() Op + +[c: 'BinaryenRemUInt64'] +pub fn remuint64() Op + +[c: 'BinaryenAndInt64'] +pub fn andint64() Op + +[c: 'BinaryenOrInt64'] +pub fn orint64() Op + +[c: 'BinaryenXorInt64'] +pub fn xorint64() Op + +[c: 'BinaryenShlInt64'] +pub fn shlint64() Op + +[c: 'BinaryenShrUInt64'] +pub fn shruint64() Op + +[c: 'BinaryenShrSInt64'] +pub fn shrsint64() Op + +[c: 'BinaryenRotLInt64'] +pub fn rotlint64() Op + +[c: 'BinaryenRotRInt64'] +pub fn rotrint64() Op + +[c: 'BinaryenEqInt64'] +pub fn eqint64() Op + +[c: 'BinaryenNeInt64'] +pub fn neint64() Op + +[c: 'BinaryenLtSInt64'] +pub fn ltsint64() Op + +[c: 'BinaryenLtUInt64'] +pub fn ltuint64() Op + +[c: 'BinaryenLeSInt64'] +pub fn lesint64() Op + +[c: 'BinaryenLeUInt64'] +pub fn leuint64() Op + +[c: 'BinaryenGtSInt64'] +pub fn gtsint64() Op + +[c: 'BinaryenGtUInt64'] +pub fn gtuint64() Op + +[c: 'BinaryenGeSInt64'] +pub fn gesint64() Op + +[c: 'BinaryenGeUInt64'] +pub fn geuint64() Op + +[c: 'BinaryenAddFloat32'] +pub fn addfloat32() Op + +[c: 'BinaryenSubFloat32'] +pub fn subfloat32() Op + +[c: 'BinaryenMulFloat32'] +pub fn mulfloat32() Op + +[c: 'BinaryenDivFloat32'] +pub fn divfloat32() Op + +[c: 'BinaryenCopySignFloat32'] +pub fn copysignfloat32() Op + +[c: 'BinaryenMinFloat32'] +pub fn minfloat32() Op + +[c: 'BinaryenMaxFloat32'] +pub fn maxfloat32() Op + +[c: 'BinaryenEqFloat32'] +pub fn eqfloat32() Op + +[c: 'BinaryenNeFloat32'] +pub fn nefloat32() Op + +[c: 'BinaryenLtFloat32'] +pub fn ltfloat32() Op + +[c: 'BinaryenLeFloat32'] +pub fn lefloat32() Op + +[c: 'BinaryenGtFloat32'] +pub fn gtfloat32() Op + +[c: 'BinaryenGeFloat32'] +pub fn gefloat32() Op + +[c: 'BinaryenAddFloat64'] +pub fn addfloat64() Op + +[c: 'BinaryenSubFloat64'] +pub fn subfloat64() Op + +[c: 'BinaryenMulFloat64'] +pub fn mulfloat64() Op + +[c: 'BinaryenDivFloat64'] +pub fn divfloat64() Op + +[c: 'BinaryenCopySignFloat64'] +pub fn copysignfloat64() Op + +[c: 'BinaryenMinFloat64'] +pub fn minfloat64() Op + +[c: 'BinaryenMaxFloat64'] +pub fn maxfloat64() Op + +[c: 'BinaryenEqFloat64'] +pub fn eqfloat64() Op + +[c: 'BinaryenNeFloat64'] +pub fn nefloat64() Op + +[c: 'BinaryenLtFloat64'] +pub fn ltfloat64() Op + +[c: 'BinaryenLeFloat64'] +pub fn lefloat64() Op + +[c: 'BinaryenGtFloat64'] +pub fn gtfloat64() Op + +[c: 'BinaryenGeFloat64'] +pub fn gefloat64() Op + +[c: 'BinaryenAtomicRMWAdd'] +pub fn atomicrmwadd() Op + +[c: 'BinaryenAtomicRMWSub'] +pub fn atomicrmwsub() Op + +[c: 'BinaryenAtomicRMWAnd'] +pub fn atomicrmwand() Op + +[c: 'BinaryenAtomicRMWOr'] +pub fn atomicrmwor() Op + +[c: 'BinaryenAtomicRMWXor'] +pub fn atomicrmwxor() Op + +[c: 'BinaryenAtomicRMWXchg'] +pub fn atomicrmwxchg() Op + +[c: 'BinaryenTruncSatSFloat32ToInt32'] +pub fn truncsatsfloat32toint32() Op + +[c: 'BinaryenTruncSatSFloat32ToInt64'] +pub fn truncsatsfloat32toint64() Op + +[c: 'BinaryenTruncSatUFloat32ToInt32'] +pub fn truncsatufloat32toint32() Op + +[c: 'BinaryenTruncSatUFloat32ToInt64'] +pub fn truncsatufloat32toint64() Op + +[c: 'BinaryenTruncSatSFloat64ToInt32'] +pub fn truncsatsfloat64toint32() Op + +[c: 'BinaryenTruncSatSFloat64ToInt64'] +pub fn truncsatsfloat64toint64() Op + +[c: 'BinaryenTruncSatUFloat64ToInt32'] +pub fn truncsatufloat64toint32() Op + +[c: 'BinaryenTruncSatUFloat64ToInt64'] +pub fn truncsatufloat64toint64() Op + +[c: 'BinaryenSplatVecI8x16'] +pub fn splatveci8x16() Op + +[c: 'BinaryenExtractLaneSVecI8x16'] +pub fn extractlanesveci8x16() Op + +[c: 'BinaryenExtractLaneUVecI8x16'] +pub fn extractlaneuveci8x16() Op + +[c: 'BinaryenReplaceLaneVecI8x16'] +pub fn replacelaneveci8x16() Op + +[c: 'BinaryenSplatVecI16x8'] +pub fn splatveci16x8() Op + +[c: 'BinaryenExtractLaneSVecI16x8'] +pub fn extractlanesveci16x8() Op + +[c: 'BinaryenExtractLaneUVecI16x8'] +pub fn extractlaneuveci16x8() Op + +[c: 'BinaryenReplaceLaneVecI16x8'] +pub fn replacelaneveci16x8() Op + +[c: 'BinaryenSplatVecI32x4'] +pub fn splatveci32x4() Op + +[c: 'BinaryenExtractLaneVecI32x4'] +pub fn extractlaneveci32x4() Op + +[c: 'BinaryenReplaceLaneVecI32x4'] +pub fn replacelaneveci32x4() Op + +[c: 'BinaryenSplatVecI64x2'] +pub fn splatveci64x2() Op + +[c: 'BinaryenExtractLaneVecI64x2'] +pub fn extractlaneveci64x2() Op + +[c: 'BinaryenReplaceLaneVecI64x2'] +pub fn replacelaneveci64x2() Op + +[c: 'BinaryenSplatVecF32x4'] +pub fn splatvecf32x4() Op + +[c: 'BinaryenExtractLaneVecF32x4'] +pub fn extractlanevecf32x4() Op + +[c: 'BinaryenReplaceLaneVecF32x4'] +pub fn replacelanevecf32x4() Op + +[c: 'BinaryenSplatVecF64x2'] +pub fn splatvecf64x2() Op + +[c: 'BinaryenExtractLaneVecF64x2'] +pub fn extractlanevecf64x2() Op + +[c: 'BinaryenReplaceLaneVecF64x2'] +pub fn replacelanevecf64x2() Op + +[c: 'BinaryenEqVecI8x16'] +pub fn eqveci8x16() Op + +[c: 'BinaryenNeVecI8x16'] +pub fn neveci8x16() Op + +[c: 'BinaryenLtSVecI8x16'] +pub fn ltsveci8x16() Op + +[c: 'BinaryenLtUVecI8x16'] +pub fn ltuveci8x16() Op + +[c: 'BinaryenGtSVecI8x16'] +pub fn gtsveci8x16() Op + +[c: 'BinaryenGtUVecI8x16'] +pub fn gtuveci8x16() Op + +[c: 'BinaryenLeSVecI8x16'] +pub fn lesveci8x16() Op + +[c: 'BinaryenLeUVecI8x16'] +pub fn leuveci8x16() Op + +[c: 'BinaryenGeSVecI8x16'] +pub fn gesveci8x16() Op + +[c: 'BinaryenGeUVecI8x16'] +pub fn geuveci8x16() Op + +[c: 'BinaryenEqVecI16x8'] +pub fn eqveci16x8() Op + +[c: 'BinaryenNeVecI16x8'] +pub fn neveci16x8() Op + +[c: 'BinaryenLtSVecI16x8'] +pub fn ltsveci16x8() Op + +[c: 'BinaryenLtUVecI16x8'] +pub fn ltuveci16x8() Op + +[c: 'BinaryenGtSVecI16x8'] +pub fn gtsveci16x8() Op + +[c: 'BinaryenGtUVecI16x8'] +pub fn gtuveci16x8() Op + +[c: 'BinaryenLeSVecI16x8'] +pub fn lesveci16x8() Op + +[c: 'BinaryenLeUVecI16x8'] +pub fn leuveci16x8() Op + +[c: 'BinaryenGeSVecI16x8'] +pub fn gesveci16x8() Op + +[c: 'BinaryenGeUVecI16x8'] +pub fn geuveci16x8() Op + +[c: 'BinaryenEqVecI32x4'] +pub fn eqveci32x4() Op + +[c: 'BinaryenNeVecI32x4'] +pub fn neveci32x4() Op + +[c: 'BinaryenLtSVecI32x4'] +pub fn ltsveci32x4() Op + +[c: 'BinaryenLtUVecI32x4'] +pub fn ltuveci32x4() Op + +[c: 'BinaryenGtSVecI32x4'] +pub fn gtsveci32x4() Op + +[c: 'BinaryenGtUVecI32x4'] +pub fn gtuveci32x4() Op + +[c: 'BinaryenLeSVecI32x4'] +pub fn lesveci32x4() Op + +[c: 'BinaryenLeUVecI32x4'] +pub fn leuveci32x4() Op + +[c: 'BinaryenGeSVecI32x4'] +pub fn gesveci32x4() Op + +[c: 'BinaryenGeUVecI32x4'] +pub fn geuveci32x4() Op + +[c: 'BinaryenEqVecI64x2'] +pub fn eqveci64x2() Op + +[c: 'BinaryenNeVecI64x2'] +pub fn neveci64x2() Op + +[c: 'BinaryenLtSVecI64x2'] +pub fn ltsveci64x2() Op + +[c: 'BinaryenGtSVecI64x2'] +pub fn gtsveci64x2() Op + +[c: 'BinaryenLeSVecI64x2'] +pub fn lesveci64x2() Op + +[c: 'BinaryenGeSVecI64x2'] +pub fn gesveci64x2() Op + +[c: 'BinaryenEqVecF32x4'] +pub fn eqvecf32x4() Op + +[c: 'BinaryenNeVecF32x4'] +pub fn nevecf32x4() Op + +[c: 'BinaryenLtVecF32x4'] +pub fn ltvecf32x4() Op + +[c: 'BinaryenGtVecF32x4'] +pub fn gtvecf32x4() Op + +[c: 'BinaryenLeVecF32x4'] +pub fn levecf32x4() Op + +[c: 'BinaryenGeVecF32x4'] +pub fn gevecf32x4() Op + +[c: 'BinaryenEqVecF64x2'] +pub fn eqvecf64x2() Op + +[c: 'BinaryenNeVecF64x2'] +pub fn nevecf64x2() Op + +[c: 'BinaryenLtVecF64x2'] +pub fn ltvecf64x2() Op + +[c: 'BinaryenGtVecF64x2'] +pub fn gtvecf64x2() Op + +[c: 'BinaryenLeVecF64x2'] +pub fn levecf64x2() Op + +[c: 'BinaryenGeVecF64x2'] +pub fn gevecf64x2() Op + +[c: 'BinaryenNotVec128'] +pub fn notvec128() Op + +[c: 'BinaryenAndVec128'] +pub fn andvec128() Op + +[c: 'BinaryenOrVec128'] +pub fn orvec128() Op + +[c: 'BinaryenXorVec128'] +pub fn xorvec128() Op + +[c: 'BinaryenAndNotVec128'] +pub fn andnotvec128() Op + +[c: 'BinaryenBitselectVec128'] +pub fn bitselectvec128() Op + +[c: 'BinaryenAnyTrueVec128'] +pub fn anytruevec128() Op + +[c: 'BinaryenPopcntVecI8x16'] +pub fn popcntveci8x16() Op + +[c: 'BinaryenAbsVecI8x16'] +pub fn absveci8x16() Op + +[c: 'BinaryenNegVecI8x16'] +pub fn negveci8x16() Op + +[c: 'BinaryenAllTrueVecI8x16'] +pub fn alltrueveci8x16() Op + +[c: 'BinaryenBitmaskVecI8x16'] +pub fn bitmaskveci8x16() Op + +[c: 'BinaryenShlVecI8x16'] +pub fn shlveci8x16() Op + +[c: 'BinaryenShrSVecI8x16'] +pub fn shrsveci8x16() Op + +[c: 'BinaryenShrUVecI8x16'] +pub fn shruveci8x16() Op + +[c: 'BinaryenAddVecI8x16'] +pub fn addveci8x16() Op + +[c: 'BinaryenAddSatSVecI8x16'] +pub fn addsatsveci8x16() Op + +[c: 'BinaryenAddSatUVecI8x16'] +pub fn addsatuveci8x16() Op + +[c: 'BinaryenSubVecI8x16'] +pub fn subveci8x16() Op + +[c: 'BinaryenSubSatSVecI8x16'] +pub fn subsatsveci8x16() Op + +[c: 'BinaryenSubSatUVecI8x16'] +pub fn subsatuveci8x16() Op + +[c: 'BinaryenMinSVecI8x16'] +pub fn minsveci8x16() Op + +[c: 'BinaryenMinUVecI8x16'] +pub fn minuveci8x16() Op + +[c: 'BinaryenMaxSVecI8x16'] +pub fn maxsveci8x16() Op + +[c: 'BinaryenMaxUVecI8x16'] +pub fn maxuveci8x16() Op + +[c: 'BinaryenAvgrUVecI8x16'] +pub fn avgruveci8x16() Op + +[c: 'BinaryenAbsVecI16x8'] +pub fn absveci16x8() Op + +[c: 'BinaryenNegVecI16x8'] +pub fn negveci16x8() Op + +[c: 'BinaryenAllTrueVecI16x8'] +pub fn alltrueveci16x8() Op + +[c: 'BinaryenBitmaskVecI16x8'] +pub fn bitmaskveci16x8() Op + +[c: 'BinaryenShlVecI16x8'] +pub fn shlveci16x8() Op + +[c: 'BinaryenShrSVecI16x8'] +pub fn shrsveci16x8() Op + +[c: 'BinaryenShrUVecI16x8'] +pub fn shruveci16x8() Op + +[c: 'BinaryenAddVecI16x8'] +pub fn addveci16x8() Op + +[c: 'BinaryenAddSatSVecI16x8'] +pub fn addsatsveci16x8() Op + +[c: 'BinaryenAddSatUVecI16x8'] +pub fn addsatuveci16x8() Op + +[c: 'BinaryenSubVecI16x8'] +pub fn subveci16x8() Op + +[c: 'BinaryenSubSatSVecI16x8'] +pub fn subsatsveci16x8() Op + +[c: 'BinaryenSubSatUVecI16x8'] +pub fn subsatuveci16x8() Op + +[c: 'BinaryenMulVecI16x8'] +pub fn mulveci16x8() Op + +[c: 'BinaryenMinSVecI16x8'] +pub fn minsveci16x8() Op + +[c: 'BinaryenMinUVecI16x8'] +pub fn minuveci16x8() Op + +[c: 'BinaryenMaxSVecI16x8'] +pub fn maxsveci16x8() Op + +[c: 'BinaryenMaxUVecI16x8'] +pub fn maxuveci16x8() Op + +[c: 'BinaryenAvgrUVecI16x8'] +pub fn avgruveci16x8() Op + +[c: 'BinaryenQ15MulrSatSVecI16x8'] +pub fn q15mulrsatsveci16x8() Op + +[c: 'BinaryenExtMulLowSVecI16x8'] +pub fn extmullowsveci16x8() Op + +[c: 'BinaryenExtMulHighSVecI16x8'] +pub fn extmulhighsveci16x8() Op + +[c: 'BinaryenExtMulLowUVecI16x8'] +pub fn extmullowuveci16x8() Op + +[c: 'BinaryenExtMulHighUVecI16x8'] +pub fn extmulhighuveci16x8() Op + +[c: 'BinaryenAbsVecI32x4'] +pub fn absveci32x4() Op + +[c: 'BinaryenNegVecI32x4'] +pub fn negveci32x4() Op + +[c: 'BinaryenAllTrueVecI32x4'] +pub fn alltrueveci32x4() Op + +[c: 'BinaryenBitmaskVecI32x4'] +pub fn bitmaskveci32x4() Op + +[c: 'BinaryenShlVecI32x4'] +pub fn shlveci32x4() Op + +[c: 'BinaryenShrSVecI32x4'] +pub fn shrsveci32x4() Op + +[c: 'BinaryenShrUVecI32x4'] +pub fn shruveci32x4() Op + +[c: 'BinaryenAddVecI32x4'] +pub fn addveci32x4() Op + +[c: 'BinaryenSubVecI32x4'] +pub fn subveci32x4() Op + +[c: 'BinaryenMulVecI32x4'] +pub fn mulveci32x4() Op + +[c: 'BinaryenMinSVecI32x4'] +pub fn minsveci32x4() Op + +[c: 'BinaryenMinUVecI32x4'] +pub fn minuveci32x4() Op + +[c: 'BinaryenMaxSVecI32x4'] +pub fn maxsveci32x4() Op + +[c: 'BinaryenMaxUVecI32x4'] +pub fn maxuveci32x4() Op + +[c: 'BinaryenDotSVecI16x8ToVecI32x4'] +pub fn dotsveci16x8toveci32x4() Op + +[c: 'BinaryenExtMulLowSVecI32x4'] +pub fn extmullowsveci32x4() Op + +[c: 'BinaryenExtMulHighSVecI32x4'] +pub fn extmulhighsveci32x4() Op + +[c: 'BinaryenExtMulLowUVecI32x4'] +pub fn extmullowuveci32x4() Op + +[c: 'BinaryenExtMulHighUVecI32x4'] +pub fn extmulhighuveci32x4() Op + +[c: 'BinaryenAbsVecI64x2'] +pub fn absveci64x2() Op + +[c: 'BinaryenNegVecI64x2'] +pub fn negveci64x2() Op + +[c: 'BinaryenAllTrueVecI64x2'] +pub fn alltrueveci64x2() Op + +[c: 'BinaryenBitmaskVecI64x2'] +pub fn bitmaskveci64x2() Op + +[c: 'BinaryenShlVecI64x2'] +pub fn shlveci64x2() Op + +[c: 'BinaryenShrSVecI64x2'] +pub fn shrsveci64x2() Op + +[c: 'BinaryenShrUVecI64x2'] +pub fn shruveci64x2() Op + +[c: 'BinaryenAddVecI64x2'] +pub fn addveci64x2() Op + +[c: 'BinaryenSubVecI64x2'] +pub fn subveci64x2() Op + +[c: 'BinaryenMulVecI64x2'] +pub fn mulveci64x2() Op + +[c: 'BinaryenExtMulLowSVecI64x2'] +pub fn extmullowsveci64x2() Op + +[c: 'BinaryenExtMulHighSVecI64x2'] +pub fn extmulhighsveci64x2() Op + +[c: 'BinaryenExtMulLowUVecI64x2'] +pub fn extmullowuveci64x2() Op + +[c: 'BinaryenExtMulHighUVecI64x2'] +pub fn extmulhighuveci64x2() Op + +[c: 'BinaryenAbsVecF32x4'] +pub fn absvecf32x4() Op + +[c: 'BinaryenNegVecF32x4'] +pub fn negvecf32x4() Op + +[c: 'BinaryenSqrtVecF32x4'] +pub fn sqrtvecf32x4() Op + +[c: 'BinaryenAddVecF32x4'] +pub fn addvecf32x4() Op + +[c: 'BinaryenSubVecF32x4'] +pub fn subvecf32x4() Op + +[c: 'BinaryenMulVecF32x4'] +pub fn mulvecf32x4() Op + +[c: 'BinaryenDivVecF32x4'] +pub fn divvecf32x4() Op + +[c: 'BinaryenMinVecF32x4'] +pub fn minvecf32x4() Op + +[c: 'BinaryenMaxVecF32x4'] +pub fn maxvecf32x4() Op + +[c: 'BinaryenPMinVecF32x4'] +pub fn pminvecf32x4() Op + +[c: 'BinaryenPMaxVecF32x4'] +pub fn pmaxvecf32x4() Op + +[c: 'BinaryenCeilVecF32x4'] +pub fn ceilvecf32x4() Op + +[c: 'BinaryenFloorVecF32x4'] +pub fn floorvecf32x4() Op + +[c: 'BinaryenTruncVecF32x4'] +pub fn truncvecf32x4() Op + +[c: 'BinaryenNearestVecF32x4'] +pub fn nearestvecf32x4() Op + +[c: 'BinaryenAbsVecF64x2'] +pub fn absvecf64x2() Op + +[c: 'BinaryenNegVecF64x2'] +pub fn negvecf64x2() Op + +[c: 'BinaryenSqrtVecF64x2'] +pub fn sqrtvecf64x2() Op + +[c: 'BinaryenAddVecF64x2'] +pub fn addvecf64x2() Op + +[c: 'BinaryenSubVecF64x2'] +pub fn subvecf64x2() Op + +[c: 'BinaryenMulVecF64x2'] +pub fn mulvecf64x2() Op + +[c: 'BinaryenDivVecF64x2'] +pub fn divvecf64x2() Op + +[c: 'BinaryenMinVecF64x2'] +pub fn minvecf64x2() Op + +[c: 'BinaryenMaxVecF64x2'] +pub fn maxvecf64x2() Op + +[c: 'BinaryenPMinVecF64x2'] +pub fn pminvecf64x2() Op + +[c: 'BinaryenPMaxVecF64x2'] +pub fn pmaxvecf64x2() Op + +[c: 'BinaryenCeilVecF64x2'] +pub fn ceilvecf64x2() Op + +[c: 'BinaryenFloorVecF64x2'] +pub fn floorvecf64x2() Op + +[c: 'BinaryenTruncVecF64x2'] +pub fn truncvecf64x2() Op + +[c: 'BinaryenNearestVecF64x2'] +pub fn nearestvecf64x2() Op + +[c: 'BinaryenExtAddPairwiseSVecI8x16ToI16x8'] +pub fn extaddpairwisesveci8x16toi16x8() Op + +[c: 'BinaryenExtAddPairwiseUVecI8x16ToI16x8'] +pub fn extaddpairwiseuveci8x16toi16x8() Op + +[c: 'BinaryenExtAddPairwiseSVecI16x8ToI32x4'] +pub fn extaddpairwisesveci16x8toi32x4() Op + +[c: 'BinaryenExtAddPairwiseUVecI16x8ToI32x4'] +pub fn extaddpairwiseuveci16x8toi32x4() Op + +[c: 'BinaryenTruncSatSVecF32x4ToVecI32x4'] +pub fn truncsatsvecf32x4toveci32x4() Op + +[c: 'BinaryenTruncSatUVecF32x4ToVecI32x4'] +pub fn truncsatuvecf32x4toveci32x4() Op + +[c: 'BinaryenConvertSVecI32x4ToVecF32x4'] +pub fn convertsveci32x4tovecf32x4() Op + +[c: 'BinaryenConvertUVecI32x4ToVecF32x4'] +pub fn convertuveci32x4tovecf32x4() Op + +[c: 'BinaryenLoad8SplatVec128'] +pub fn load8splatvec128() Op + +[c: 'BinaryenLoad16SplatVec128'] +pub fn load16splatvec128() Op + +[c: 'BinaryenLoad32SplatVec128'] +pub fn load32splatvec128() Op + +[c: 'BinaryenLoad64SplatVec128'] +pub fn load64splatvec128() Op + +[c: 'BinaryenLoad8x8SVec128'] +pub fn load8x8svec128() Op + +[c: 'BinaryenLoad8x8UVec128'] +pub fn load8x8uvec128() Op + +[c: 'BinaryenLoad16x4SVec128'] +pub fn load16x4svec128() Op + +[c: 'BinaryenLoad16x4UVec128'] +pub fn load16x4uvec128() Op + +[c: 'BinaryenLoad32x2SVec128'] +pub fn load32x2svec128() Op + +[c: 'BinaryenLoad32x2UVec128'] +pub fn load32x2uvec128() Op + +[c: 'BinaryenLoad32ZeroVec128'] +pub fn load32zerovec128() Op + +[c: 'BinaryenLoad64ZeroVec128'] +pub fn load64zerovec128() Op + +[c: 'BinaryenLoad8LaneVec128'] +pub fn load8lanevec128() Op + +[c: 'BinaryenLoad16LaneVec128'] +pub fn load16lanevec128() Op + +[c: 'BinaryenLoad32LaneVec128'] +pub fn load32lanevec128() Op + +[c: 'BinaryenLoad64LaneVec128'] +pub fn load64lanevec128() Op + +[c: 'BinaryenStore8LaneVec128'] +pub fn store8lanevec128() Op + +[c: 'BinaryenStore16LaneVec128'] +pub fn store16lanevec128() Op + +[c: 'BinaryenStore32LaneVec128'] +pub fn store32lanevec128() Op + +[c: 'BinaryenStore64LaneVec128'] +pub fn store64lanevec128() Op + +[c: 'BinaryenNarrowSVecI16x8ToVecI8x16'] +pub fn narrowsveci16x8toveci8x16() Op + +[c: 'BinaryenNarrowUVecI16x8ToVecI8x16'] +pub fn narrowuveci16x8toveci8x16() Op + +[c: 'BinaryenNarrowSVecI32x4ToVecI16x8'] +pub fn narrowsveci32x4toveci16x8() Op + +[c: 'BinaryenNarrowUVecI32x4ToVecI16x8'] +pub fn narrowuveci32x4toveci16x8() Op + +[c: 'BinaryenExtendLowSVecI8x16ToVecI16x8'] +pub fn extendlowsveci8x16toveci16x8() Op + +[c: 'BinaryenExtendHighSVecI8x16ToVecI16x8'] +pub fn extendhighsveci8x16toveci16x8() Op + +[c: 'BinaryenExtendLowUVecI8x16ToVecI16x8'] +pub fn extendlowuveci8x16toveci16x8() Op + +[c: 'BinaryenExtendHighUVecI8x16ToVecI16x8'] +pub fn extendhighuveci8x16toveci16x8() Op + +[c: 'BinaryenExtendLowSVecI16x8ToVecI32x4'] +pub fn extendlowsveci16x8toveci32x4() Op + +[c: 'BinaryenExtendHighSVecI16x8ToVecI32x4'] +pub fn extendhighsveci16x8toveci32x4() Op + +[c: 'BinaryenExtendLowUVecI16x8ToVecI32x4'] +pub fn extendlowuveci16x8toveci32x4() Op + +[c: 'BinaryenExtendHighUVecI16x8ToVecI32x4'] +pub fn extendhighuveci16x8toveci32x4() Op + +[c: 'BinaryenExtendLowSVecI32x4ToVecI64x2'] +pub fn extendlowsveci32x4toveci64x2() Op + +[c: 'BinaryenExtendHighSVecI32x4ToVecI64x2'] +pub fn extendhighsveci32x4toveci64x2() Op + +[c: 'BinaryenExtendLowUVecI32x4ToVecI64x2'] +pub fn extendlowuveci32x4toveci64x2() Op + +[c: 'BinaryenExtendHighUVecI32x4ToVecI64x2'] +pub fn extendhighuveci32x4toveci64x2() Op + +[c: 'BinaryenConvertLowSVecI32x4ToVecF64x2'] +pub fn convertlowsveci32x4tovecf64x2() Op + +[c: 'BinaryenConvertLowUVecI32x4ToVecF64x2'] +pub fn convertlowuveci32x4tovecf64x2() Op + +[c: 'BinaryenTruncSatZeroSVecF64x2ToVecI32x4'] +pub fn truncsatzerosvecf64x2toveci32x4() Op + +[c: 'BinaryenTruncSatZeroUVecF64x2ToVecI32x4'] +pub fn truncsatzerouvecf64x2toveci32x4() Op + +[c: 'BinaryenDemoteZeroVecF64x2ToVecF32x4'] +pub fn demotezerovecf64x2tovecf32x4() Op + +[c: 'BinaryenPromoteLowVecF32x4ToVecF64x2'] +pub fn promotelowvecf32x4tovecf64x2() Op + +[c: 'BinaryenSwizzleVecI8x16'] +pub fn swizzleveci8x16() Op + +[c: 'BinaryenRefIsNull'] +pub fn refisnull() Op + +[c: 'BinaryenRefIsFunc'] +pub fn refisfunc() Op + +[c: 'BinaryenRefIsData'] +pub fn refisdata() Op + +[c: 'BinaryenRefIsI31'] +pub fn refisi31() Op + +[c: 'BinaryenRefAsNonNull'] +pub fn refasnonnull() Op + +[c: 'BinaryenRefAsFunc'] +pub fn refasfunc() Op + +[c: 'BinaryenRefAsData'] +pub fn refasdata() Op + +[c: 'BinaryenRefAsI31'] +pub fn refasi31() Op + +[c: 'BinaryenRefAsExternInternalize'] +pub fn refasexterninternalize() Op + +[c: 'BinaryenRefAsExternExternalize'] +pub fn refasexternexternalize() Op + +[c: 'BinaryenBrOnNull'] +pub fn bronnull() Op + +[c: 'BinaryenBrOnNonNull'] +pub fn bronnonnull() Op + +[c: 'BinaryenBrOnCast'] +pub fn broncast() Op + +[c: 'BinaryenBrOnCastFail'] +pub fn broncastfail() Op + +[c: 'BinaryenBrOnFunc'] +pub fn bronfunc() Op + +[c: 'BinaryenBrOnNonFunc'] +pub fn bronnonfunc() Op + +[c: 'BinaryenBrOnData'] +pub fn brondata() Op + +[c: 'BinaryenBrOnNonData'] +pub fn bronnondata() Op + +[c: 'BinaryenBrOnI31'] +pub fn broni31() Op + +[c: 'BinaryenBrOnNonI31'] +pub fn bronnoni31() Op + +[c: 'BinaryenStringNewUTF8'] +pub fn stringnewutf8() Op + +[c: 'BinaryenStringNewWTF8'] +pub fn stringnewwtf8() Op + +[c: 'BinaryenStringNewReplace'] +pub fn stringnewreplace() Op + +[c: 'BinaryenStringNewWTF16'] +pub fn stringnewwtf16() Op + +[c: 'BinaryenStringNewUTF8Array'] +pub fn stringnewutf8array() Op + +[c: 'BinaryenStringNewWTF8Array'] +pub fn stringnewwtf8array() Op + +[c: 'BinaryenStringNewReplaceArray'] +pub fn stringnewreplacearray() Op + +[c: 'BinaryenStringNewWTF16Array'] +pub fn stringnewwtf16array() Op + +[c: 'BinaryenStringMeasureUTF8'] +pub fn stringmeasureutf8() Op + +[c: 'BinaryenStringMeasureWTF8'] +pub fn stringmeasurewtf8() Op + +[c: 'BinaryenStringMeasureWTF16'] +pub fn stringmeasurewtf16() Op + +[c: 'BinaryenStringMeasureIsUSV'] +pub fn stringmeasureisusv() Op + +[c: 'BinaryenStringMeasureWTF16View'] +pub fn stringmeasurewtf16view() Op + +[c: 'BinaryenStringEncodeUTF8'] +pub fn stringencodeutf8() Op + +[c: 'BinaryenStringEncodeWTF8'] +pub fn stringencodewtf8() Op + +[c: 'BinaryenStringEncodeWTF16'] +pub fn stringencodewtf16() Op + +[c: 'BinaryenStringEncodeUTF8Array'] +pub fn stringencodeutf8array() Op + +[c: 'BinaryenStringEncodeWTF8Array'] +pub fn stringencodewtf8array() Op + +[c: 'BinaryenStringEncodeWTF16Array'] +pub fn stringencodewtf16array() Op + +[c: 'BinaryenStringAsWTF8'] +pub fn stringaswtf8() Op + +[c: 'BinaryenStringAsWTF16'] +pub fn stringaswtf16() Op + +[c: 'BinaryenStringAsIter'] +pub fn stringasiter() Op + +[c: 'BinaryenStringIterMoveAdvance'] +pub fn stringitermoveadvance() Op + +[c: 'BinaryenStringIterMoveRewind'] +pub fn stringitermoverewind() Op + +[c: 'BinaryenStringSliceWTF8'] +pub fn stringslicewtf8() Op + +[c: 'BinaryenStringSliceWTF16'] +pub fn stringslicewtf16() Op + +type Expression = voidptr + +[c: 'BinaryenBlock'] +pub fn block(module_ Module, name &i8, children &Expression, numchildren Index, type_ Type) Expression + +[c: 'BinaryenIf'] +pub fn bif(module_ Module, condition Expression, iftrue Expression, iffalse Expression) Expression + +[c: 'BinaryenLoop'] +pub fn loop(module_ Module, in_ &i8, body Expression) Expression + +[c: 'BinaryenBreak'] +pub fn br(module_ Module, name &i8, condition Expression, value Expression) Expression + +[c: 'BinaryenSwitch'] +pub fn switch(module_ Module, names &&u8, numnames Index, defaultname &i8, condition Expression, value Expression) Expression + +[c: 'BinaryenCall'] +pub fn call(module_ Module, target &i8, operands &Expression, numoperands Index, returntype Type) Expression + +[c: 'BinaryenCallIndirect'] +pub fn callindirect(module_ Module, table &i8, target Expression, operands &Expression, numoperands Index, params Type, results Type) Expression + +[c: 'BinaryenReturnCall'] +pub fn returncall(module_ Module, target &i8, operands &Expression, numoperands Index, returntype Type) Expression + +[c: 'BinaryenReturnCallIndirect'] +pub fn returncallindirect(module_ Module, table &i8, target Expression, operands &Expression, numoperands Index, params Type, results Type) Expression + +[c: 'BinaryenLocalGet'] +pub fn localget(module_ Module, index Index, type_ Type) Expression + +[c: 'BinaryenLocalSet'] +pub fn localset(module_ Module, index Index, value Expression) Expression + +[c: 'BinaryenLocalTee'] +pub fn localtee(module_ Module, index Index, value Expression, type_ Type) Expression + +[c: 'BinaryenGlobalGet'] +pub fn globalget(module_ Module, name &i8, type_ Type) Expression + +[c: 'BinaryenGlobalSet'] +pub fn globalset(module_ Module, name &i8, value Expression) Expression + +[c: 'BinaryenLoad'] +pub fn load(module_ Module, bytes u32, signed_ bool, offset u32, align u32, type_ Type, ptr Expression, memoryname &i8) Expression + +[c: 'BinaryenStore'] +pub fn store(module_ Module, bytes u32, offset u32, align u32, ptr Expression, value Expression, type_ Type, memoryname &i8) Expression + +[c: 'BinaryenConst'] +pub fn constant(module_ Module, value Literal) Expression + +[c: 'BinaryenUnary'] +pub fn unary(module_ Module, op Op, value Expression) Expression + +[c: 'BinaryenBinary'] +pub fn binary(module_ Module, op Op, left Expression, right Expression) Expression + +[c: 'BinaryenSelect'] +pub fn bselect(module_ Module, condition Expression, iftrue Expression, iffalse Expression, type_ Type) Expression + +[c: 'BinaryenDrop'] +pub fn drop(module_ Module, value Expression) Expression + +[c: 'BinaryenReturn'] +pub fn ret(module_ Module, value Expression) Expression + +[c: 'BinaryenMemorySize'] +pub fn memorysize(module_ Module, memoryname &i8, memoryis64 bool) Expression + +[c: 'BinaryenMemoryGrow'] +pub fn memorygrow(module_ Module, delta Expression, memoryname &i8, memoryis64 bool) Expression + +[c: 'BinaryenNop'] +pub fn nop(module_ Module) Expression + +[c: 'BinaryenUnreachable'] +pub fn unreachable(module_ Module) Expression + +[c: 'BinaryenAtomicLoad'] +pub fn atomicload(module_ Module, bytes u32, offset u32, type_ Type, ptr Expression, memoryname &i8) Expression + +[c: 'BinaryenAtomicStore'] +pub fn atomicstore(module_ Module, bytes u32, offset u32, ptr Expression, value Expression, type_ Type, memoryname &i8) Expression + +[c: 'BinaryenAtomicRMW'] +pub fn atomicrmw(module_ Module, op Op, bytes Index, offset Index, ptr Expression, value Expression, type_ Type, memoryname &i8) Expression + +[c: 'BinaryenAtomicCmpxchg'] +pub fn atomiccmpxchg(module_ Module, bytes Index, offset Index, ptr Expression, expected Expression, replacement Expression, type_ Type, memoryname &i8) Expression + +[c: 'BinaryenAtomicWait'] +pub fn atomicwait(module_ Module, ptr Expression, expected Expression, timeout Expression, type_ Type, memoryname &i8) Expression + +[c: 'BinaryenAtomicNotify'] +pub fn atomicnotify(module_ Module, ptr Expression, notifycount Expression, memoryname &i8) Expression + +[c: 'BinaryenAtomicFence'] +pub fn atomicfence(module_ Module) Expression + +[c: 'BinaryenSIMDExtract'] +pub fn simdextract(module_ Module, op Op, vec Expression, index u8) Expression + +[c: 'BinaryenSIMDReplace'] +pub fn simdreplace(module_ Module, op Op, vec Expression, index u8, value Expression) Expression + +[c: 'BinaryenSIMDShuffle'] +pub fn simdshuffle(module_ Module, left Expression, right Expression, mask &u8) Expression + +[c: 'BinaryenSIMDTernary'] +pub fn simdternary(module_ Module, op Op, a Expression, b Expression, c Expression) Expression + +[c: 'BinaryenSIMDShift'] +pub fn simdshift(module_ Module, op Op, vec Expression, shift Expression) Expression + +[c: 'BinaryenSIMDLoad'] +pub fn simdload(module_ Module, op Op, offset u32, align u32, ptr Expression, name &i8) Expression + +[c: 'BinaryenSIMDLoadStoreLane'] +pub fn simdloadstorelane(module_ Module, op Op, offset u32, align u32, index u8, ptr Expression, vec Expression, memoryname &i8) Expression + +[c: 'BinaryenMemoryInit'] +pub fn memoryinit(module_ Module, segment u32, dest Expression, offset Expression, size Expression, memoryname &i8) Expression + +[c: 'BinaryenDataDrop'] +pub fn datadrop(module_ Module, segment u32) Expression + +[c: 'BinaryenMemoryCopy'] +pub fn memorycopy(module_ Module, dest Expression, source Expression, size Expression, destmemory &i8, sourcememory &i8) Expression + +[c: 'BinaryenMemoryFill'] +pub fn memoryfill(module_ Module, dest Expression, value Expression, size Expression, memoryname &i8) Expression + +[c: 'BinaryenRefNull'] +pub fn refnull(module_ Module, type_ Type) Expression + +[c: 'BinaryenRefIs'] +pub fn refis(module_ Module, op Op, value Expression) Expression + +[c: 'BinaryenRefAs'] +pub fn refas(module_ Module, op Op, value Expression) Expression + +[c: 'BinaryenRefFunc'] +pub fn reffunc(module_ Module, func &i8, type_ Type) Expression + +[c: 'BinaryenRefEq'] +pub fn refeq(module_ Module, left Expression, right Expression) Expression + +[c: 'BinaryenTableGet'] +pub fn tableget(module_ Module, name &i8, index Expression, type_ Type) Expression + +[c: 'BinaryenTableSet'] +pub fn tableset(module_ Module, name &i8, index Expression, value Expression) Expression + +[c: 'BinaryenTableSize'] +pub fn tablesize(module_ Module, name &i8) Expression + +[c: 'BinaryenTableGrow'] +pub fn tablegrow(module_ Module, name &i8, value Expression, delta Expression) Expression + +[c: 'BinaryenTry'] +pub fn try(module_ Module, name &i8, body Expression, catchtags &&u8, numcatchtags Index, catchbodies &Expression, numcatchbodies Index, delegatetarget &i8) Expression + +[c: 'BinaryenThrow'] +pub fn throw(module_ Module, tag &i8, operands &Expression, numoperands Index) Expression + +[c: 'BinaryenRethrow'] +pub fn rethrow(module_ Module, target &i8) Expression + +[c: 'BinaryenTupleMake'] +pub fn tuplemake(module_ Module, operands &Expression, numoperands Index) Expression + +[c: 'BinaryenTupleExtract'] +pub fn tupleextract(module_ Module, tuple Expression, index Index) Expression + +[c: 'BinaryenPop'] +pub fn pop(module_ Module, type_ Type) Expression + +[c: 'BinaryenI31New'] +pub fn i31new(module_ Module, value Expression) Expression + +[c: 'BinaryenI31Get'] +pub fn i31get(module_ Module, i31 Expression, signed_ bool) Expression + +[c: 'BinaryenCallRef'] +pub fn callref(module_ Module, target Expression, operands &Expression, numoperands Index, type_ Type, isreturn bool) Expression + +[c: 'BinaryenRefTest'] +pub fn reftest(module_ Module, ref Expression, intendedtype HeapType) Expression + +[c: 'BinaryenRefCast'] +pub fn refcast(module_ Module, ref Expression, intendedtype HeapType) Expression + +[c: 'BinaryenBrOn'] +pub fn bron(module_ Module, op Op, name &i8, ref Expression, intendedtype HeapType) Expression + +[c: 'BinaryenStructNew'] +pub fn structnew(module_ Module, operands &Expression, numoperands Index, type_ HeapType) Expression + +[c: 'BinaryenStructGet'] +pub fn structget(module_ Module, index Index, ref Expression, type_ Type, signed_ bool) Expression + +[c: 'BinaryenStructSet'] +pub fn structset(module_ Module, index Index, ref Expression, value Expression) Expression + +[c: 'BinaryenArrayNew'] +pub fn arraynew(module_ Module, type_ HeapType, size Expression, init Expression) Expression + +[c: 'BinaryenArrayInit'] +pub fn arrayinit(module_ Module, type_ HeapType, values &Expression, numvalues Index) Expression + +[c: 'BinaryenArrayGet'] +pub fn arrayget(module_ Module, ref Expression, index Expression, type_ Type, signed_ bool) Expression + +[c: 'BinaryenArraySet'] +pub fn arrayset(module_ Module, ref Expression, index Expression, value Expression) Expression + +[c: 'BinaryenArrayLen'] +pub fn arraylen(module_ Module, ref Expression) Expression + +[c: 'BinaryenArrayCopy'] +pub fn arraycopy(module_ Module, destref Expression, destindex Expression, srcref Expression, srcindex Expression, length Expression) Expression + +[c: 'BinaryenStringNew'] +pub fn stringnew(module_ Module, op Op, ptr Expression, length Expression, start Expression, end Expression) Expression + +[c: 'BinaryenStringConst'] +pub fn stringconst(module_ Module, name &i8) Expression + +[c: 'BinaryenStringMeasure'] +pub fn stringmeasure(module_ Module, op Op, ref Expression) Expression + +[c: 'BinaryenStringEncode'] +pub fn stringencode(module_ Module, op Op, ref Expression, ptr Expression, start Expression) Expression + +[c: 'BinaryenStringConcat'] +pub fn stringconcat(module_ Module, left Expression, right Expression) Expression + +[c: 'BinaryenStringEq'] +pub fn stringeq(module_ Module, left Expression, right Expression) Expression + +[c: 'BinaryenStringAs'] +pub fn stringas(module_ Module, op Op, ref Expression) Expression + +[c: 'BinaryenStringWTF8Advance'] +pub fn stringwtf8advance(module_ Module, ref Expression, pos Expression, bytes Expression) Expression + +[c: 'BinaryenStringWTF16Get'] +pub fn stringwtf16get(module_ Module, ref Expression, pos Expression) Expression + +[c: 'BinaryenStringIterNext'] +pub fn stringiternext(module_ Module, ref Expression) Expression + +[c: 'BinaryenStringIterMove'] +pub fn stringitermove(module_ Module, op Op, ref Expression, num Expression) Expression + +[c: 'BinaryenStringSliceWTF'] +pub fn stringslicewtf(module_ Module, op Op, ref Expression, start Expression, end Expression) Expression + +[c: 'BinaryenStringSliceIter'] +pub fn stringsliceiter(module_ Module, ref Expression, num Expression) Expression + +[c: 'BinaryenExpressionGetId'] +pub fn expressiongetid(expr Expression) ExpressionId + +[c: 'BinaryenExpressionGetType'] +pub fn expressiongettype(expr Expression) Type + +[c: 'BinaryenExpressionSetType'] +pub fn expressionsettype(expr Expression, type_ Type) + +[c: 'BinaryenExpressionPrint'] +pub fn expressionprint(expr Expression) + +[c: 'BinaryenExpressionFinalize'] +pub fn expressionfinalize(expr Expression) + +[c: 'BinaryenExpressionCopy'] +pub fn expressioncopy(expr Expression, module_ Module) Expression + +[c: 'BinaryenBlockGetName'] +pub fn blockgetname(expr Expression) &i8 + +[c: 'BinaryenBlockSetName'] +pub fn blocksetname(expr Expression, name &i8) + +[c: 'BinaryenBlockGetNumChildren'] +pub fn blockgetnumchildren(expr Expression) Index + +[c: 'BinaryenBlockGetChildAt'] +pub fn blockgetchildat(expr Expression, index Index) Expression + +[c: 'BinaryenBlockSetChildAt'] +pub fn blocksetchildat(expr Expression, index Index, childexpr Expression) + +[c: 'BinaryenBlockAppendChild'] +pub fn blockappendchild(expr Expression, childexpr Expression) Index + +[c: 'BinaryenBlockInsertChildAt'] +pub fn blockinsertchildat(expr Expression, index Index, childexpr Expression) + +[c: 'BinaryenBlockRemoveChildAt'] +pub fn blockremovechildat(expr Expression, index Index) Expression + +[c: 'BinaryenIfGetCondition'] +pub fn ifgetcondition(expr Expression) Expression + +[c: 'BinaryenIfSetCondition'] +pub fn ifsetcondition(expr Expression, condexpr Expression) + +[c: 'BinaryenIfGetIfTrue'] +pub fn ifgetiftrue(expr Expression) Expression + +[c: 'BinaryenIfSetIfTrue'] +pub fn ifsetiftrue(expr Expression, iftrueexpr Expression) + +[c: 'BinaryenIfGetIfFalse'] +pub fn ifgetiffalse(expr Expression) Expression + +[c: 'BinaryenIfSetIfFalse'] +pub fn ifsetiffalse(expr Expression, iffalseexpr Expression) + +[c: 'BinaryenLoopGetName'] +pub fn loopgetname(expr Expression) &i8 + +[c: 'BinaryenLoopSetName'] +pub fn loopsetname(expr Expression, name &i8) + +[c: 'BinaryenLoopGetBody'] +pub fn loopgetbody(expr Expression) Expression + +[c: 'BinaryenLoopSetBody'] +pub fn loopsetbody(expr Expression, bodyexpr Expression) + +[c: 'BinaryenBreakGetName'] +pub fn breakgetname(expr Expression) &i8 + +[c: 'BinaryenBreakSetName'] +pub fn breaksetname(expr Expression, name &i8) + +[c: 'BinaryenBreakGetCondition'] +pub fn breakgetcondition(expr Expression) Expression + +[c: 'BinaryenBreakSetCondition'] +pub fn breaksetcondition(expr Expression, condexpr Expression) + +[c: 'BinaryenBreakGetValue'] +pub fn breakgetvalue(expr Expression) Expression + +[c: 'BinaryenBreakSetValue'] +pub fn breaksetvalue(expr Expression, valueexpr Expression) + +[c: 'BinaryenSwitchGetNumNames'] +pub fn switchgetnumnames(expr Expression) Index + +[c: 'BinaryenSwitchGetNameAt'] +pub fn switchgetnameat(expr Expression, index Index) &i8 + +[c: 'BinaryenSwitchSetNameAt'] +pub fn switchsetnameat(expr Expression, index Index, name &i8) + +[c: 'BinaryenSwitchAppendName'] +pub fn switchappendname(expr Expression, name &i8) Index + +[c: 'BinaryenSwitchInsertNameAt'] +pub fn switchinsertnameat(expr Expression, index Index, name &i8) + +[c: 'BinaryenSwitchRemoveNameAt'] +pub fn switchremovenameat(expr Expression, index Index) &i8 + +[c: 'BinaryenSwitchGetDefaultName'] +pub fn switchgetdefaultname(expr Expression) &i8 + +[c: 'BinaryenSwitchSetDefaultName'] +pub fn switchsetdefaultname(expr Expression, name &i8) + +[c: 'BinaryenSwitchGetCondition'] +pub fn switchgetcondition(expr Expression) Expression + +[c: 'BinaryenSwitchSetCondition'] +pub fn switchsetcondition(expr Expression, condexpr Expression) + +[c: 'BinaryenSwitchGetValue'] +pub fn switchgetvalue(expr Expression) Expression + +[c: 'BinaryenSwitchSetValue'] +pub fn switchsetvalue(expr Expression, valueexpr Expression) + +[c: 'BinaryenCallGetTarget'] +pub fn callgettarget(expr Expression) &i8 + +[c: 'BinaryenCallSetTarget'] +pub fn callsettarget(expr Expression, target &i8) + +[c: 'BinaryenCallGetNumOperands'] +pub fn callgetnumoperands(expr Expression) Index + +[c: 'BinaryenCallGetOperandAt'] +pub fn callgetoperandat(expr Expression, index Index) Expression + +[c: 'BinaryenCallSetOperandAt'] +pub fn callsetoperandat(expr Expression, index Index, operandexpr Expression) + +[c: 'BinaryenCallAppendOperand'] +pub fn callappendoperand(expr Expression, operandexpr Expression) Index + +[c: 'BinaryenCallInsertOperandAt'] +pub fn callinsertoperandat(expr Expression, index Index, operandexpr Expression) + +[c: 'BinaryenCallRemoveOperandAt'] +pub fn callremoveoperandat(expr Expression, index Index) Expression + +[c: 'BinaryenCallIsReturn'] +pub fn callisreturn(expr Expression) bool + +[c: 'BinaryenCallSetReturn'] +pub fn callsetreturn(expr Expression, isreturn bool) + +[c: 'BinaryenCallIndirectGetTarget'] +pub fn callindirectgettarget(expr Expression) Expression + +[c: 'BinaryenCallIndirectSetTarget'] +pub fn callindirectsettarget(expr Expression, targetexpr Expression) + +[c: 'BinaryenCallIndirectGetTable'] +pub fn callindirectgettable(expr Expression) &i8 + +[c: 'BinaryenCallIndirectSetTable'] +pub fn callindirectsettable(expr Expression, table &i8) + +[c: 'BinaryenCallIndirectGetNumOperands'] +pub fn callindirectgetnumoperands(expr Expression) Index + +[c: 'BinaryenCallIndirectGetOperandAt'] +pub fn callindirectgetoperandat(expr Expression, index Index) Expression + +[c: 'BinaryenCallIndirectSetOperandAt'] +pub fn callindirectsetoperandat(expr Expression, index Index, operandexpr Expression) + +[c: 'BinaryenCallIndirectAppendOperand'] +pub fn callindirectappendoperand(expr Expression, operandexpr Expression) Index + +[c: 'BinaryenCallIndirectInsertOperandAt'] +pub fn callindirectinsertoperandat(expr Expression, index Index, operandexpr Expression) + +[c: 'BinaryenCallIndirectRemoveOperandAt'] +pub fn callindirectremoveoperandat(expr Expression, index Index) Expression + +[c: 'BinaryenCallIndirectIsReturn'] +pub fn callindirectisreturn(expr Expression) bool + +[c: 'BinaryenCallIndirectSetReturn'] +pub fn callindirectsetreturn(expr Expression, isreturn bool) + +[c: 'BinaryenCallIndirectGetParams'] +pub fn callindirectgetparams(expr Expression) Type + +[c: 'BinaryenCallIndirectSetParams'] +pub fn callindirectsetparams(expr Expression, params Type) + +[c: 'BinaryenCallIndirectGetResults'] +pub fn callindirectgetresults(expr Expression) Type + +[c: 'BinaryenCallIndirectSetResults'] +pub fn callindirectsetresults(expr Expression, params Type) + +[c: 'BinaryenLocalGetGetIndex'] +pub fn localgetgetindex(expr Expression) Index + +[c: 'BinaryenLocalGetSetIndex'] +pub fn localgetsetindex(expr Expression, index Index) + +[c: 'BinaryenLocalSetIsTee'] +pub fn localsetistee(expr Expression) bool + +[c: 'BinaryenLocalSetGetIndex'] +pub fn localsetgetindex(expr Expression) Index + +[c: 'BinaryenLocalSetSetIndex'] +pub fn localsetsetindex(expr Expression, index Index) + +[c: 'BinaryenLocalSetGetValue'] +pub fn localsetgetvalue(expr Expression) Expression + +[c: 'BinaryenLocalSetSetValue'] +pub fn localsetsetvalue(expr Expression, valueexpr Expression) + +[c: 'BinaryenGlobalGetGetName'] +pub fn globalgetgetname(expr Expression) &i8 + +[c: 'BinaryenGlobalGetSetName'] +pub fn globalgetsetname(expr Expression, name &i8) + +[c: 'BinaryenGlobalSetGetName'] +pub fn globalsetgetname(expr Expression) &i8 + +[c: 'BinaryenGlobalSetSetName'] +pub fn globalsetsetname(expr Expression, name &i8) + +[c: 'BinaryenGlobalSetGetValue'] +pub fn globalsetgetvalue(expr Expression) Expression + +[c: 'BinaryenGlobalSetSetValue'] +pub fn globalsetsetvalue(expr Expression, valueexpr Expression) + +[c: 'BinaryenTableGetGetTable'] +pub fn tablegetgettable(expr Expression) &i8 + +[c: 'BinaryenTableGetSetTable'] +pub fn tablegetsettable(expr Expression, table &i8) + +[c: 'BinaryenTableGetGetIndex'] +pub fn tablegetgetindex(expr Expression) Expression + +[c: 'BinaryenTableGetSetIndex'] +pub fn tablegetsetindex(expr Expression, indexexpr Expression) + +[c: 'BinaryenTableSetGetTable'] +pub fn tablesetgettable(expr Expression) &i8 + +[c: 'BinaryenTableSetSetTable'] +pub fn tablesetsettable(expr Expression, table &i8) + +[c: 'BinaryenTableSetGetIndex'] +pub fn tablesetgetindex(expr Expression) Expression + +[c: 'BinaryenTableSetSetIndex'] +pub fn tablesetsetindex(expr Expression, indexexpr Expression) + +[c: 'BinaryenTableSetGetValue'] +pub fn tablesetgetvalue(expr Expression) Expression + +[c: 'BinaryenTableSetSetValue'] +pub fn tablesetsetvalue(expr Expression, valueexpr Expression) + +[c: 'BinaryenTableSizeGetTable'] +pub fn tablesizegettable(expr Expression) &i8 + +[c: 'BinaryenTableSizeSetTable'] +pub fn tablesizesettable(expr Expression, table &i8) + +[c: 'BinaryenTableGrowGetTable'] +pub fn tablegrowgettable(expr Expression) &i8 + +[c: 'BinaryenTableGrowSetTable'] +pub fn tablegrowsettable(expr Expression, table &i8) + +[c: 'BinaryenTableGrowGetValue'] +pub fn tablegrowgetvalue(expr Expression) Expression + +[c: 'BinaryenTableGrowSetValue'] +pub fn tablegrowsetvalue(expr Expression, valueexpr Expression) + +[c: 'BinaryenTableGrowGetDelta'] +pub fn tablegrowgetdelta(expr Expression) Expression + +[c: 'BinaryenTableGrowSetDelta'] +pub fn tablegrowsetdelta(expr Expression, deltaexpr Expression) + +[c: 'BinaryenMemoryGrowGetDelta'] +pub fn memorygrowgetdelta(expr Expression) Expression + +[c: 'BinaryenMemoryGrowSetDelta'] +pub fn memorygrowsetdelta(expr Expression, deltaexpr Expression) + +[c: 'BinaryenLoadIsAtomic'] +pub fn loadisatomic(expr Expression) bool + +[c: 'BinaryenLoadSetAtomic'] +pub fn loadsetatomic(expr Expression, isatomic bool) + +[c: 'BinaryenLoadIsSigned'] +pub fn loadissigned(expr Expression) bool + +[c: 'BinaryenLoadSetSigned'] +pub fn loadsetsigned(expr Expression, issigned bool) + +[c: 'BinaryenLoadGetOffset'] +pub fn loadgetoffset(expr Expression) u32 + +[c: 'BinaryenLoadSetOffset'] +pub fn loadsetoffset(expr Expression, offset u32) + +[c: 'BinaryenLoadGetBytes'] +pub fn loadgetbytes(expr Expression) u32 + +[c: 'BinaryenLoadSetBytes'] +pub fn loadsetbytes(expr Expression, bytes u32) + +[c: 'BinaryenLoadGetAlign'] +pub fn loadgetalign(expr Expression) u32 + +[c: 'BinaryenLoadSetAlign'] +pub fn loadsetalign(expr Expression, align u32) + +[c: 'BinaryenLoadGetPtr'] +pub fn loadgetptr(expr Expression) Expression + +[c: 'BinaryenLoadSetPtr'] +pub fn loadsetptr(expr Expression, ptrexpr Expression) + +[c: 'BinaryenStoreIsAtomic'] +pub fn storeisatomic(expr Expression) bool + +[c: 'BinaryenStoreSetAtomic'] +pub fn storesetatomic(expr Expression, isatomic bool) + +[c: 'BinaryenStoreGetBytes'] +pub fn storegetbytes(expr Expression) u32 + +[c: 'BinaryenStoreSetBytes'] +pub fn storesetbytes(expr Expression, bytes u32) + +[c: 'BinaryenStoreGetOffset'] +pub fn storegetoffset(expr Expression) u32 + +[c: 'BinaryenStoreSetOffset'] +pub fn storesetoffset(expr Expression, offset u32) + +[c: 'BinaryenStoreGetAlign'] +pub fn storegetalign(expr Expression) u32 + +[c: 'BinaryenStoreSetAlign'] +pub fn storesetalign(expr Expression, align u32) + +[c: 'BinaryenStoreGetPtr'] +pub fn storegetptr(expr Expression) Expression + +[c: 'BinaryenStoreSetPtr'] +pub fn storesetptr(expr Expression, ptrexpr Expression) + +[c: 'BinaryenStoreGetValue'] +pub fn storegetvalue(expr Expression) Expression + +[c: 'BinaryenStoreSetValue'] +pub fn storesetvalue(expr Expression, valueexpr Expression) + +[c: 'BinaryenStoreGetValueType'] +pub fn storegetvaluetype(expr Expression) Type + +[c: 'BinaryenStoreSetValueType'] +pub fn storesetvaluetype(expr Expression, valuetype Type) + +[c: 'BinaryenConstGetValueI32'] +pub fn constgetvaluei32(expr Expression) int + +[c: 'BinaryenConstSetValueI32'] +pub fn constsetvaluei32(expr Expression, value int) + +[c: 'BinaryenConstGetValueI64'] +pub fn constgetvaluei64(expr Expression) i64 + +[c: 'BinaryenConstSetValueI64'] +pub fn constsetvaluei64(expr Expression, value i64) + +[c: 'BinaryenConstGetValueI64Low'] +pub fn constgetvaluei64low(expr Expression) int + +[c: 'BinaryenConstSetValueI64Low'] +pub fn constsetvaluei64low(expr Expression, valuelow int) + +[c: 'BinaryenConstGetValueI64High'] +pub fn constgetvaluei64high(expr Expression) int + +[c: 'BinaryenConstSetValueI64High'] +pub fn constsetvaluei64high(expr Expression, valuehigh int) + +[c: 'BinaryenConstGetValueF32'] +pub fn constgetvaluef32(expr Expression) f32 + +[c: 'BinaryenConstSetValueF32'] +pub fn constsetvaluef32(expr Expression, value f32) + +[c: 'BinaryenConstGetValueF64'] +pub fn constgetvaluef64(expr Expression) f64 + +[c: 'BinaryenConstSetValueF64'] +pub fn constsetvaluef64(expr Expression, value f64) + +[c: 'BinaryenConstGetValueV128'] +pub fn constgetvaluev128(expr Expression, out &u8) + +[c: 'BinaryenConstSetValueV128'] +pub fn constsetvaluev128(expr Expression, value &u8) + +[c: 'BinaryenUnaryGetOp'] +pub fn unarygetop(expr Expression) Op + +[c: 'BinaryenUnarySetOp'] +pub fn unarysetop(expr Expression, op Op) + +[c: 'BinaryenUnaryGetValue'] +pub fn unarygetvalue(expr Expression) Expression + +[c: 'BinaryenUnarySetValue'] +pub fn unarysetvalue(expr Expression, valueexpr Expression) + +[c: 'BinaryenBinaryGetOp'] +pub fn binarygetop(expr Expression) Op + +[c: 'BinaryenBinarySetOp'] +pub fn binarysetop(expr Expression, op Op) + +[c: 'BinaryenBinaryGetLeft'] +pub fn binarygetleft(expr Expression) Expression + +[c: 'BinaryenBinarySetLeft'] +pub fn binarysetleft(expr Expression, leftexpr Expression) + +[c: 'BinaryenBinaryGetRight'] +pub fn binarygetright(expr Expression) Expression + +[c: 'BinaryenBinarySetRight'] +pub fn binarysetright(expr Expression, rightexpr Expression) + +[c: 'BinaryenSelectGetIfTrue'] +pub fn selectgetiftrue(expr Expression) Expression + +[c: 'BinaryenSelectSetIfTrue'] +pub fn selectsetiftrue(expr Expression, iftrueexpr Expression) + +[c: 'BinaryenSelectGetIfFalse'] +pub fn selectgetiffalse(expr Expression) Expression + +[c: 'BinaryenSelectSetIfFalse'] +pub fn selectsetiffalse(expr Expression, iffalseexpr Expression) + +[c: 'BinaryenSelectGetCondition'] +pub fn selectgetcondition(expr Expression) Expression + +[c: 'BinaryenSelectSetCondition'] +pub fn selectsetcondition(expr Expression, condexpr Expression) + +[c: 'BinaryenDropGetValue'] +pub fn dropgetvalue(expr Expression) Expression + +[c: 'BinaryenDropSetValue'] +pub fn dropsetvalue(expr Expression, valueexpr Expression) + +[c: 'BinaryenReturnGetValue'] +pub fn returngetvalue(expr Expression) Expression + +[c: 'BinaryenReturnSetValue'] +pub fn returnsetvalue(expr Expression, valueexpr Expression) + +[c: 'BinaryenAtomicRMWGetOp'] +pub fn atomicrmwgetop(expr Expression) Op + +[c: 'BinaryenAtomicRMWSetOp'] +pub fn atomicrmwsetop(expr Expression, op Op) + +[c: 'BinaryenAtomicRMWGetBytes'] +pub fn atomicrmwgetbytes(expr Expression) u32 + +[c: 'BinaryenAtomicRMWSetBytes'] +pub fn atomicrmwsetbytes(expr Expression, bytes u32) + +[c: 'BinaryenAtomicRMWGetOffset'] +pub fn atomicrmwgetoffset(expr Expression) u32 + +[c: 'BinaryenAtomicRMWSetOffset'] +pub fn atomicrmwsetoffset(expr Expression, offset u32) + +[c: 'BinaryenAtomicRMWGetPtr'] +pub fn atomicrmwgetptr(expr Expression) Expression + +[c: 'BinaryenAtomicRMWSetPtr'] +pub fn atomicrmwsetptr(expr Expression, ptrexpr Expression) + +[c: 'BinaryenAtomicRMWGetValue'] +pub fn atomicrmwgetvalue(expr Expression) Expression + +[c: 'BinaryenAtomicRMWSetValue'] +pub fn atomicrmwsetvalue(expr Expression, valueexpr Expression) + +[c: 'BinaryenAtomicCmpxchgGetBytes'] +pub fn atomiccmpxchggetbytes(expr Expression) u32 + +[c: 'BinaryenAtomicCmpxchgSetBytes'] +pub fn atomiccmpxchgsetbytes(expr Expression, bytes u32) + +[c: 'BinaryenAtomicCmpxchgGetOffset'] +pub fn atomiccmpxchggetoffset(expr Expression) u32 + +[c: 'BinaryenAtomicCmpxchgSetOffset'] +pub fn atomiccmpxchgsetoffset(expr Expression, offset u32) + +[c: 'BinaryenAtomicCmpxchgGetPtr'] +pub fn atomiccmpxchggetptr(expr Expression) Expression + +[c: 'BinaryenAtomicCmpxchgSetPtr'] +pub fn atomiccmpxchgsetptr(expr Expression, ptrexpr Expression) + +[c: 'BinaryenAtomicCmpxchgGetExpected'] +pub fn atomiccmpxchggetexpected(expr Expression) Expression + +[c: 'BinaryenAtomicCmpxchgSetExpected'] +pub fn atomiccmpxchgsetexpected(expr Expression, expectedexpr Expression) + +[c: 'BinaryenAtomicCmpxchgGetReplacement'] +pub fn atomiccmpxchggetreplacement(expr Expression) Expression + +[c: 'BinaryenAtomicCmpxchgSetReplacement'] +pub fn atomiccmpxchgsetreplacement(expr Expression, replacementexpr Expression) + +[c: 'BinaryenAtomicWaitGetPtr'] +pub fn atomicwaitgetptr(expr Expression) Expression + +[c: 'BinaryenAtomicWaitSetPtr'] +pub fn atomicwaitsetptr(expr Expression, ptrexpr Expression) + +[c: 'BinaryenAtomicWaitGetExpected'] +pub fn atomicwaitgetexpected(expr Expression) Expression + +[c: 'BinaryenAtomicWaitSetExpected'] +pub fn atomicwaitsetexpected(expr Expression, expectedexpr Expression) + +[c: 'BinaryenAtomicWaitGetTimeout'] +pub fn atomicwaitgettimeout(expr Expression) Expression + +[c: 'BinaryenAtomicWaitSetTimeout'] +pub fn atomicwaitsettimeout(expr Expression, timeoutexpr Expression) + +[c: 'BinaryenAtomicWaitGetExpectedType'] +pub fn atomicwaitgetexpectedtype(expr Expression) Type + +[c: 'BinaryenAtomicWaitSetExpectedType'] +pub fn atomicwaitsetexpectedtype(expr Expression, expectedtype Type) + +[c: 'BinaryenAtomicNotifyGetPtr'] +pub fn atomicnotifygetptr(expr Expression) Expression + +[c: 'BinaryenAtomicNotifySetPtr'] +pub fn atomicnotifysetptr(expr Expression, ptrexpr Expression) + +[c: 'BinaryenAtomicNotifyGetNotifyCount'] +pub fn atomicnotifygetnotifycount(expr Expression) Expression + +[c: 'BinaryenAtomicNotifySetNotifyCount'] +pub fn atomicnotifysetnotifycount(expr Expression, notifycountexpr Expression) + +[c: 'BinaryenAtomicFenceGetOrder'] +pub fn atomicfencegetorder(expr Expression) u8 + +[c: 'BinaryenAtomicFenceSetOrder'] +pub fn atomicfencesetorder(expr Expression, order u8) + +[c: 'BinaryenSIMDExtractGetOp'] +pub fn simdextractgetop(expr Expression) Op + +[c: 'BinaryenSIMDExtractSetOp'] +pub fn simdextractsetop(expr Expression, op Op) + +[c: 'BinaryenSIMDExtractGetVec'] +pub fn simdextractgetvec(expr Expression) Expression + +[c: 'BinaryenSIMDExtractSetVec'] +pub fn simdextractsetvec(expr Expression, vecexpr Expression) + +[c: 'BinaryenSIMDExtractGetIndex'] +pub fn simdextractgetindex(expr Expression) u8 + +[c: 'BinaryenSIMDExtractSetIndex'] +pub fn simdextractsetindex(expr Expression, index u8) + +[c: 'BinaryenSIMDReplaceGetOp'] +pub fn simdreplacegetop(expr Expression) Op + +[c: 'BinaryenSIMDReplaceSetOp'] +pub fn simdreplacesetop(expr Expression, op Op) + +[c: 'BinaryenSIMDReplaceGetVec'] +pub fn simdreplacegetvec(expr Expression) Expression + +[c: 'BinaryenSIMDReplaceSetVec'] +pub fn simdreplacesetvec(expr Expression, vecexpr Expression) + +[c: 'BinaryenSIMDReplaceGetIndex'] +pub fn simdreplacegetindex(expr Expression) u8 + +[c: 'BinaryenSIMDReplaceSetIndex'] +pub fn simdreplacesetindex(expr Expression, index u8) + +[c: 'BinaryenSIMDReplaceGetValue'] +pub fn simdreplacegetvalue(expr Expression) Expression + +[c: 'BinaryenSIMDReplaceSetValue'] +pub fn simdreplacesetvalue(expr Expression, valueexpr Expression) + +[c: 'BinaryenSIMDShuffleGetLeft'] +pub fn simdshufflegetleft(expr Expression) Expression + +[c: 'BinaryenSIMDShuffleSetLeft'] +pub fn simdshufflesetleft(expr Expression, leftexpr Expression) + +[c: 'BinaryenSIMDShuffleGetRight'] +pub fn simdshufflegetright(expr Expression) Expression + +[c: 'BinaryenSIMDShuffleSetRight'] +pub fn simdshufflesetright(expr Expression, rightexpr Expression) + +[c: 'BinaryenSIMDShuffleGetMask'] +pub fn simdshufflegetmask(expr Expression, mask &u8) + +[c: 'BinaryenSIMDShuffleSetMask'] +pub fn simdshufflesetmask(expr Expression, mask &u8) + +[c: 'BinaryenSIMDTernaryGetOp'] +pub fn simdternarygetop(expr Expression) Op + +[c: 'BinaryenSIMDTernarySetOp'] +pub fn simdternarysetop(expr Expression, op Op) + +[c: 'BinaryenSIMDTernaryGetA'] +pub fn simdternarygeta(expr Expression) Expression + +[c: 'BinaryenSIMDTernarySetA'] +pub fn simdternaryseta(expr Expression, aexpr Expression) + +[c: 'BinaryenSIMDTernaryGetB'] +pub fn simdternarygetb(expr Expression) Expression + +[c: 'BinaryenSIMDTernarySetB'] +pub fn simdternarysetb(expr Expression, bexpr Expression) + +[c: 'BinaryenSIMDTernaryGetC'] +pub fn simdternarygetc(expr Expression) Expression + +[c: 'BinaryenSIMDTernarySetC'] +pub fn simdternarysetc(expr Expression, cexpr Expression) + +[c: 'BinaryenSIMDShiftGetOp'] +pub fn simdshiftgetop(expr Expression) Op + +[c: 'BinaryenSIMDShiftSetOp'] +pub fn simdshiftsetop(expr Expression, op Op) + +[c: 'BinaryenSIMDShiftGetVec'] +pub fn simdshiftgetvec(expr Expression) Expression + +[c: 'BinaryenSIMDShiftSetVec'] +pub fn simdshiftsetvec(expr Expression, vecexpr Expression) + +[c: 'BinaryenSIMDShiftGetShift'] +pub fn simdshiftgetshift(expr Expression) Expression + +[c: 'BinaryenSIMDShiftSetShift'] +pub fn simdshiftsetshift(expr Expression, shiftexpr Expression) + +[c: 'BinaryenSIMDLoadGetOp'] +pub fn simdloadgetop(expr Expression) Op + +[c: 'BinaryenSIMDLoadSetOp'] +pub fn simdloadsetop(expr Expression, op Op) + +[c: 'BinaryenSIMDLoadGetOffset'] +pub fn simdloadgetoffset(expr Expression) u32 + +[c: 'BinaryenSIMDLoadSetOffset'] +pub fn simdloadsetoffset(expr Expression, offset u32) + +[c: 'BinaryenSIMDLoadGetAlign'] +pub fn simdloadgetalign(expr Expression) u32 + +[c: 'BinaryenSIMDLoadSetAlign'] +pub fn simdloadsetalign(expr Expression, align u32) + +[c: 'BinaryenSIMDLoadGetPtr'] +pub fn simdloadgetptr(expr Expression) Expression + +[c: 'BinaryenSIMDLoadSetPtr'] +pub fn simdloadsetptr(expr Expression, ptrexpr Expression) + +[c: 'BinaryenSIMDLoadStoreLaneGetOp'] +pub fn simdloadstorelanegetop(expr Expression) Op + +[c: 'BinaryenSIMDLoadStoreLaneSetOp'] +pub fn simdloadstorelanesetop(expr Expression, op Op) + +[c: 'BinaryenSIMDLoadStoreLaneGetOffset'] +pub fn simdloadstorelanegetoffset(expr Expression) u32 + +[c: 'BinaryenSIMDLoadStoreLaneSetOffset'] +pub fn simdloadstorelanesetoffset(expr Expression, offset u32) + +[c: 'BinaryenSIMDLoadStoreLaneGetAlign'] +pub fn simdloadstorelanegetalign(expr Expression) u32 + +[c: 'BinaryenSIMDLoadStoreLaneSetAlign'] +pub fn simdloadstorelanesetalign(expr Expression, align u32) + +[c: 'BinaryenSIMDLoadStoreLaneGetIndex'] +pub fn simdloadstorelanegetindex(expr Expression) u8 + +[c: 'BinaryenSIMDLoadStoreLaneSetIndex'] +pub fn simdloadstorelanesetindex(expr Expression, index u8) + +[c: 'BinaryenSIMDLoadStoreLaneGetPtr'] +pub fn simdloadstorelanegetptr(expr Expression) Expression + +[c: 'BinaryenSIMDLoadStoreLaneSetPtr'] +pub fn simdloadstorelanesetptr(expr Expression, ptrexpr Expression) + +[c: 'BinaryenSIMDLoadStoreLaneGetVec'] +pub fn simdloadstorelanegetvec(expr Expression) Expression + +[c: 'BinaryenSIMDLoadStoreLaneSetVec'] +pub fn simdloadstorelanesetvec(expr Expression, vecexpr Expression) + +[c: 'BinaryenSIMDLoadStoreLaneIsStore'] +pub fn simdloadstorelaneisstore(expr Expression) bool + +[c: 'BinaryenMemoryInitGetSegment'] +pub fn memoryinitgetsegment(expr Expression) u32 + +[c: 'BinaryenMemoryInitSetSegment'] +pub fn memoryinitsetsegment(expr Expression, segmentindex u32) + +[c: 'BinaryenMemoryInitGetDest'] +pub fn memoryinitgetdest(expr Expression) Expression + +[c: 'BinaryenMemoryInitSetDest'] +pub fn memoryinitsetdest(expr Expression, destexpr Expression) + +[c: 'BinaryenMemoryInitGetOffset'] +pub fn memoryinitgetoffset(expr Expression) Expression + +[c: 'BinaryenMemoryInitSetOffset'] +pub fn memoryinitsetoffset(expr Expression, offsetexpr Expression) + +[c: 'BinaryenMemoryInitGetSize'] +pub fn memoryinitgetsize(expr Expression) Expression + +[c: 'BinaryenMemoryInitSetSize'] +pub fn memoryinitsetsize(expr Expression, sizeexpr Expression) + +[c: 'BinaryenDataDropGetSegment'] +pub fn datadropgetsegment(expr Expression) u32 + +[c: 'BinaryenDataDropSetSegment'] +pub fn datadropsetsegment(expr Expression, segmentindex u32) + +[c: 'BinaryenMemoryCopyGetDest'] +pub fn memorycopygetdest(expr Expression) Expression + +[c: 'BinaryenMemoryCopySetDest'] +pub fn memorycopysetdest(expr Expression, destexpr Expression) + +[c: 'BinaryenMemoryCopyGetSource'] +pub fn memorycopygetsource(expr Expression) Expression + +[c: 'BinaryenMemoryCopySetSource'] +pub fn memorycopysetsource(expr Expression, sourceexpr Expression) + +[c: 'BinaryenMemoryCopyGetSize'] +pub fn memorycopygetsize(expr Expression) Expression + +[c: 'BinaryenMemoryCopySetSize'] +pub fn memorycopysetsize(expr Expression, sizeexpr Expression) + +[c: 'BinaryenMemoryFillGetDest'] +pub fn memoryfillgetdest(expr Expression) Expression + +[c: 'BinaryenMemoryFillSetDest'] +pub fn memoryfillsetdest(expr Expression, destexpr Expression) + +[c: 'BinaryenMemoryFillGetValue'] +pub fn memoryfillgetvalue(expr Expression) Expression + +[c: 'BinaryenMemoryFillSetValue'] +pub fn memoryfillsetvalue(expr Expression, valueexpr Expression) + +[c: 'BinaryenMemoryFillGetSize'] +pub fn memoryfillgetsize(expr Expression) Expression + +[c: 'BinaryenMemoryFillSetSize'] +pub fn memoryfillsetsize(expr Expression, sizeexpr Expression) + +[c: 'BinaryenRefIsGetOp'] +pub fn refisgetop(expr Expression) Op + +[c: 'BinaryenRefIsSetOp'] +pub fn refissetop(expr Expression, op Op) + +[c: 'BinaryenRefIsGetValue'] +pub fn refisgetvalue(expr Expression) Expression + +[c: 'BinaryenRefIsSetValue'] +pub fn refissetvalue(expr Expression, valueexpr Expression) + +[c: 'BinaryenRefAsGetOp'] +pub fn refasgetop(expr Expression) Op + +[c: 'BinaryenRefAsSetOp'] +pub fn refassetop(expr Expression, op Op) + +[c: 'BinaryenRefAsGetValue'] +pub fn refasgetvalue(expr Expression) Expression + +[c: 'BinaryenRefAsSetValue'] +pub fn refassetvalue(expr Expression, valueexpr Expression) + +[c: 'BinaryenRefFuncGetFunc'] +pub fn reffuncgetfunc(expr Expression) &i8 + +[c: 'BinaryenRefFuncSetFunc'] +pub fn reffuncsetfunc(expr Expression, funcname &i8) + +[c: 'BinaryenRefEqGetLeft'] +pub fn refeqgetleft(expr Expression) Expression + +[c: 'BinaryenRefEqSetLeft'] +pub fn refeqsetleft(expr Expression, left Expression) + +[c: 'BinaryenRefEqGetRight'] +pub fn refeqgetright(expr Expression) Expression + +[c: 'BinaryenRefEqSetRight'] +pub fn refeqsetright(expr Expression, right Expression) + +[c: 'BinaryenTryGetName'] +pub fn trygetname(expr Expression) &i8 + +[c: 'BinaryenTrySetName'] +pub fn trysetname(expr Expression, name &i8) + +[c: 'BinaryenTryGetBody'] +pub fn trygetbody(expr Expression) Expression + +[c: 'BinaryenTrySetBody'] +pub fn trysetbody(expr Expression, bodyexpr Expression) + +[c: 'BinaryenTryGetNumCatchTags'] +pub fn trygetnumcatchtags(expr Expression) Index + +[c: 'BinaryenTryGetNumCatchBodies'] +pub fn trygetnumcatchbodies(expr Expression) Index + +[c: 'BinaryenTryGetCatchTagAt'] +pub fn trygetcatchtagat(expr Expression, index Index) &i8 + +[c: 'BinaryenTrySetCatchTagAt'] +pub fn trysetcatchtagat(expr Expression, index Index, catchtag &i8) + +[c: 'BinaryenTryAppendCatchTag'] +pub fn tryappendcatchtag(expr Expression, catchtag &i8) Index + +[c: 'BinaryenTryInsertCatchTagAt'] +pub fn tryinsertcatchtagat(expr Expression, index Index, catchtag &i8) + +[c: 'BinaryenTryRemoveCatchTagAt'] +pub fn tryremovecatchtagat(expr Expression, index Index) &i8 + +[c: 'BinaryenTryGetCatchBodyAt'] +pub fn trygetcatchbodyat(expr Expression, index Index) Expression + +[c: 'BinaryenTrySetCatchBodyAt'] +pub fn trysetcatchbodyat(expr Expression, index Index, catchexpr Expression) + +[c: 'BinaryenTryAppendCatchBody'] +pub fn tryappendcatchbody(expr Expression, catchexpr Expression) Index + +[c: 'BinaryenTryInsertCatchBodyAt'] +pub fn tryinsertcatchbodyat(expr Expression, index Index, catchexpr Expression) + +[c: 'BinaryenTryRemoveCatchBodyAt'] +pub fn tryremovecatchbodyat(expr Expression, index Index) Expression + +[c: 'BinaryenTryHasCatchAll'] +pub fn tryhascatchall(expr Expression) bool + +[c: 'BinaryenTryGetDelegateTarget'] +pub fn trygetdelegatetarget(expr Expression) &i8 + +[c: 'BinaryenTrySetDelegateTarget'] +pub fn trysetdelegatetarget(expr Expression, delegatetarget &i8) + +[c: 'BinaryenTryIsDelegate'] +pub fn tryisdelegate(expr Expression) bool + +[c: 'BinaryenThrowGetTag'] +pub fn throwgettag(expr Expression) &i8 + +[c: 'BinaryenThrowSetTag'] +pub fn throwsettag(expr Expression, tagname &i8) + +[c: 'BinaryenThrowGetNumOperands'] +pub fn throwgetnumoperands(expr Expression) Index + +[c: 'BinaryenThrowGetOperandAt'] +pub fn throwgetoperandat(expr Expression, index Index) Expression + +[c: 'BinaryenThrowSetOperandAt'] +pub fn throwsetoperandat(expr Expression, index Index, operandexpr Expression) + +[c: 'BinaryenThrowAppendOperand'] +pub fn throwappendoperand(expr Expression, operandexpr Expression) Index + +[c: 'BinaryenThrowInsertOperandAt'] +pub fn throwinsertoperandat(expr Expression, index Index, operandexpr Expression) + +[c: 'BinaryenThrowRemoveOperandAt'] +pub fn throwremoveoperandat(expr Expression, index Index) Expression + +[c: 'BinaryenRethrowGetTarget'] +pub fn rethrowgettarget(expr Expression) &i8 + +[c: 'BinaryenRethrowSetTarget'] +pub fn rethrowsettarget(expr Expression, target &i8) + +[c: 'BinaryenTupleMakeGetNumOperands'] +pub fn tuplemakegetnumoperands(expr Expression) Index + +[c: 'BinaryenTupleMakeGetOperandAt'] +pub fn tuplemakegetoperandat(expr Expression, index Index) Expression + +[c: 'BinaryenTupleMakeSetOperandAt'] +pub fn tuplemakesetoperandat(expr Expression, index Index, operandexpr Expression) + +[c: 'BinaryenTupleMakeAppendOperand'] +pub fn tuplemakeappendoperand(expr Expression, operandexpr Expression) Index + +[c: 'BinaryenTupleMakeInsertOperandAt'] +pub fn tuplemakeinsertoperandat(expr Expression, index Index, operandexpr Expression) + +[c: 'BinaryenTupleMakeRemoveOperandAt'] +pub fn tuplemakeremoveoperandat(expr Expression, index Index) Expression + +[c: 'BinaryenTupleExtractGetTuple'] +pub fn tupleextractgettuple(expr Expression) Expression + +[c: 'BinaryenTupleExtractSetTuple'] +pub fn tupleextractsettuple(expr Expression, tupleexpr Expression) + +[c: 'BinaryenTupleExtractGetIndex'] +pub fn tupleextractgetindex(expr Expression) Index + +[c: 'BinaryenTupleExtractSetIndex'] +pub fn tupleextractsetindex(expr Expression, index Index) + +[c: 'BinaryenI31NewGetValue'] +pub fn i31newgetvalue(expr Expression) Expression + +[c: 'BinaryenI31NewSetValue'] +pub fn i31newsetvalue(expr Expression, valueexpr Expression) + +[c: 'BinaryenI31GetGetI31'] +pub fn i31getgeti31(expr Expression) Expression + +[c: 'BinaryenI31GetSetI31'] +pub fn i31getseti31(expr Expression, i31expr Expression) + +[c: 'BinaryenI31GetIsSigned'] +pub fn i31getissigned(expr Expression) bool + +[c: 'BinaryenI31GetSetSigned'] +pub fn i31getsetsigned(expr Expression, signed_ bool) + +[c: 'BinaryenCallRefGetNumOperands'] +pub fn callrefgetnumoperands(expr Expression) Index + +[c: 'BinaryenCallRefGetOperandAt'] +pub fn callrefgetoperandat(expr Expression, index Index) Expression + +[c: 'BinaryenCallRefSetOperandAt'] +pub fn callrefsetoperandat(expr Expression, index Index, operandexpr Expression) + +[c: 'BinaryenCallRefAppendOperand'] +pub fn callrefappendoperand(expr Expression, operandexpr Expression) Index + +[c: 'BinaryenCallRefInsertOperandAt'] +pub fn callrefinsertoperandat(expr Expression, index Index, operandexpr Expression) + +[c: 'BinaryenCallRefRemoveOperandAt'] +pub fn callrefremoveoperandat(expr Expression, index Index) Expression + +[c: 'BinaryenCallRefGetTarget'] +pub fn callrefgettarget(expr Expression) Expression + +[c: 'BinaryenCallRefSetTarget'] +pub fn callrefsettarget(expr Expression, targetexpr Expression) + +[c: 'BinaryenCallRefIsReturn'] +pub fn callrefisreturn(expr Expression) bool + +[c: 'BinaryenCallRefSetReturn'] +pub fn callrefsetreturn(expr Expression, isreturn bool) + +[c: 'BinaryenRefTestGetRef'] +pub fn reftestgetref(expr Expression) Expression + +[c: 'BinaryenRefTestSetRef'] +pub fn reftestsetref(expr Expression, refexpr Expression) + +[c: 'BinaryenRefTestGetIntendedType'] +pub fn reftestgetintendedtype(expr Expression) HeapType + +[c: 'BinaryenRefTestSetIntendedType'] +pub fn reftestsetintendedtype(expr Expression, intendedtype HeapType) + +[c: 'BinaryenRefCastGetRef'] +pub fn refcastgetref(expr Expression) Expression + +[c: 'BinaryenRefCastSetRef'] +pub fn refcastsetref(expr Expression, refexpr Expression) + +[c: 'BinaryenRefCastGetIntendedType'] +pub fn refcastgetintendedtype(expr Expression) HeapType + +[c: 'BinaryenRefCastSetIntendedType'] +pub fn refcastsetintendedtype(expr Expression, intendedtype HeapType) + +[c: 'BinaryenBrOnGetOp'] +pub fn brongetop(expr Expression) Op + +[c: 'BinaryenBrOnSetOp'] +pub fn bronsetop(expr Expression, op Op) + +[c: 'BinaryenBrOnGetName'] +pub fn brongetname(expr Expression) &i8 + +[c: 'BinaryenBrOnSetName'] +pub fn bronsetname(expr Expression, namestr &i8) + +[c: 'BinaryenBrOnGetRef'] +pub fn brongetref(expr Expression) Expression + +[c: 'BinaryenBrOnSetRef'] +pub fn bronsetref(expr Expression, refexpr Expression) + +[c: 'BinaryenBrOnGetIntendedType'] +pub fn brongetintendedtype(expr Expression) HeapType + +[c: 'BinaryenBrOnSetIntendedType'] +pub fn bronsetintendedtype(expr Expression, intendedtype HeapType) + +[c: 'BinaryenStructNewGetNumOperands'] +pub fn structnewgetnumoperands(expr Expression) Index + +[c: 'BinaryenStructNewGetOperandAt'] +pub fn structnewgetoperandat(expr Expression, index Index) Expression + +[c: 'BinaryenStructNewSetOperandAt'] +pub fn structnewsetoperandat(expr Expression, index Index, operandexpr Expression) + +[c: 'BinaryenStructNewAppendOperand'] +pub fn structnewappendoperand(expr Expression, operandexpr Expression) Index + +[c: 'BinaryenStructNewInsertOperandAt'] +pub fn structnewinsertoperandat(expr Expression, index Index, operandexpr Expression) + +[c: 'BinaryenStructNewRemoveOperandAt'] +pub fn structnewremoveoperandat(expr Expression, index Index) Expression + +[c: 'BinaryenStructGetGetIndex'] +pub fn structgetgetindex(expr Expression) Index + +[c: 'BinaryenStructGetSetIndex'] +pub fn structgetsetindex(expr Expression, index Index) + +[c: 'BinaryenStructGetGetRef'] +pub fn structgetgetref(expr Expression) Expression + +[c: 'BinaryenStructGetSetRef'] +pub fn structgetsetref(expr Expression, refexpr Expression) + +[c: 'BinaryenStructGetIsSigned'] +pub fn structgetissigned(expr Expression) bool + +[c: 'BinaryenStructGetSetSigned'] +pub fn structgetsetsigned(expr Expression, signed_ bool) + +[c: 'BinaryenStructSetGetIndex'] +pub fn structsetgetindex(expr Expression) Index + +[c: 'BinaryenStructSetSetIndex'] +pub fn structsetsetindex(expr Expression, index Index) + +[c: 'BinaryenStructSetGetRef'] +pub fn structsetgetref(expr Expression) Expression + +[c: 'BinaryenStructSetSetRef'] +pub fn structsetsetref(expr Expression, refexpr Expression) + +[c: 'BinaryenStructSetGetValue'] +pub fn structsetgetvalue(expr Expression) Expression + +[c: 'BinaryenStructSetSetValue'] +pub fn structsetsetvalue(expr Expression, valueexpr Expression) + +[c: 'BinaryenArrayNewGetInit'] +pub fn arraynewgetinit(expr Expression) Expression + +[c: 'BinaryenArrayNewSetInit'] +pub fn arraynewsetinit(expr Expression, initexpr Expression) + +[c: 'BinaryenArrayNewGetSize'] +pub fn arraynewgetsize(expr Expression) Expression + +[c: 'BinaryenArrayNewSetSize'] +pub fn arraynewsetsize(expr Expression, sizeexpr Expression) + +[c: 'BinaryenArrayInitGetNumValues'] +pub fn arrayinitgetnumvalues(expr Expression) Index + +[c: 'BinaryenArrayInitGetValueAt'] +pub fn arrayinitgetvalueat(expr Expression, index Index) Expression + +[c: 'BinaryenArrayInitSetValueAt'] +pub fn arrayinitsetvalueat(expr Expression, index Index, valueexpr Expression) + +[c: 'BinaryenArrayInitAppendValue'] +pub fn arrayinitappendvalue(expr Expression, valueexpr Expression) Index + +[c: 'BinaryenArrayInitInsertValueAt'] +pub fn arrayinitinsertvalueat(expr Expression, index Index, valueexpr Expression) + +[c: 'BinaryenArrayInitRemoveValueAt'] +pub fn arrayinitremovevalueat(expr Expression, index Index) Expression + +[c: 'BinaryenArrayGetGetRef'] +pub fn arraygetgetref(expr Expression) Expression + +[c: 'BinaryenArrayGetSetRef'] +pub fn arraygetsetref(expr Expression, refexpr Expression) + +[c: 'BinaryenArrayGetGetIndex'] +pub fn arraygetgetindex(expr Expression) Expression + +[c: 'BinaryenArrayGetSetIndex'] +pub fn arraygetsetindex(expr Expression, indexexpr Expression) + +[c: 'BinaryenArrayGetIsSigned'] +pub fn arraygetissigned(expr Expression) bool + +[c: 'BinaryenArrayGetSetSigned'] +pub fn arraygetsetsigned(expr Expression, signed_ bool) + +[c: 'BinaryenArraySetGetRef'] +pub fn arraysetgetref(expr Expression) Expression + +[c: 'BinaryenArraySetSetRef'] +pub fn arraysetsetref(expr Expression, refexpr Expression) + +[c: 'BinaryenArraySetGetIndex'] +pub fn arraysetgetindex(expr Expression) Expression + +[c: 'BinaryenArraySetSetIndex'] +pub fn arraysetsetindex(expr Expression, indexexpr Expression) + +[c: 'BinaryenArraySetGetValue'] +pub fn arraysetgetvalue(expr Expression) Expression + +[c: 'BinaryenArraySetSetValue'] +pub fn arraysetsetvalue(expr Expression, valueexpr Expression) + +[c: 'BinaryenArrayLenGetRef'] +pub fn arraylengetref(expr Expression) Expression + +[c: 'BinaryenArrayLenSetRef'] +pub fn arraylensetref(expr Expression, refexpr Expression) + +[c: 'BinaryenArrayCopyGetDestRef'] +pub fn arraycopygetdestref(expr Expression) Expression + +[c: 'BinaryenArrayCopySetDestRef'] +pub fn arraycopysetdestref(expr Expression, destrefexpr Expression) + +[c: 'BinaryenArrayCopyGetDestIndex'] +pub fn arraycopygetdestindex(expr Expression) Expression + +[c: 'BinaryenArrayCopySetDestIndex'] +pub fn arraycopysetdestindex(expr Expression, destindexexpr Expression) + +[c: 'BinaryenArrayCopyGetSrcRef'] +pub fn arraycopygetsrcref(expr Expression) Expression + +[c: 'BinaryenArrayCopySetSrcRef'] +pub fn arraycopysetsrcref(expr Expression, srcrefexpr Expression) + +[c: 'BinaryenArrayCopyGetSrcIndex'] +pub fn arraycopygetsrcindex(expr Expression) Expression + +[c: 'BinaryenArrayCopySetSrcIndex'] +pub fn arraycopysetsrcindex(expr Expression, srcindexexpr Expression) + +[c: 'BinaryenArrayCopyGetLength'] +pub fn arraycopygetlength(expr Expression) Expression + +[c: 'BinaryenArrayCopySetLength'] +pub fn arraycopysetlength(expr Expression, lengthexpr Expression) + +[c: 'BinaryenStringNewGetOp'] +pub fn stringnewgetop(expr Expression) Op + +[c: 'BinaryenStringNewSetOp'] +pub fn stringnewsetop(expr Expression, op Op) + +[c: 'BinaryenStringNewGetPtr'] +pub fn stringnewgetptr(expr Expression) Expression + +[c: 'BinaryenStringNewSetPtr'] +pub fn stringnewsetptr(expr Expression, ptrexpr Expression) + +[c: 'BinaryenStringNewGetLength'] +pub fn stringnewgetlength(expr Expression) Expression + +[c: 'BinaryenStringNewSetLength'] +pub fn stringnewsetlength(expr Expression, lengthexpr Expression) + +[c: 'BinaryenStringNewGetStart'] +pub fn stringnewgetstart(expr Expression) Expression + +[c: 'BinaryenStringNewSetStart'] +pub fn stringnewsetstart(expr Expression, startexpr Expression) + +[c: 'BinaryenStringNewGetEnd'] +pub fn stringnewgetend(expr Expression) Expression + +[c: 'BinaryenStringNewSetEnd'] +pub fn stringnewsetend(expr Expression, endexpr Expression) + +[c: 'BinaryenStringConstGetString'] +pub fn stringconstgetstring(expr Expression) &i8 + +[c: 'BinaryenStringConstSetString'] +pub fn stringconstsetstring(expr Expression, stringstr &i8) + +[c: 'BinaryenStringMeasureGetOp'] +pub fn stringmeasuregetop(expr Expression) Op + +[c: 'BinaryenStringMeasureSetOp'] +pub fn stringmeasuresetop(expr Expression, op Op) + +[c: 'BinaryenStringMeasureGetRef'] +pub fn stringmeasuregetref(expr Expression) Expression + +[c: 'BinaryenStringMeasureSetRef'] +pub fn stringmeasuresetref(expr Expression, refexpr Expression) + +[c: 'BinaryenStringEncodeGetOp'] +pub fn stringencodegetop(expr Expression) Op + +[c: 'BinaryenStringEncodeSetOp'] +pub fn stringencodesetop(expr Expression, op Op) + +[c: 'BinaryenStringEncodeGetRef'] +pub fn stringencodegetref(expr Expression) Expression + +[c: 'BinaryenStringEncodeSetRef'] +pub fn stringencodesetref(expr Expression, refexpr Expression) + +[c: 'BinaryenStringEncodeGetPtr'] +pub fn stringencodegetptr(expr Expression) Expression + +[c: 'BinaryenStringEncodeSetPtr'] +pub fn stringencodesetptr(expr Expression, ptrexpr Expression) + +[c: 'BinaryenStringEncodeGetStart'] +pub fn stringencodegetstart(expr Expression) Expression + +[c: 'BinaryenStringEncodeSetStart'] +pub fn stringencodesetstart(expr Expression, startexpr Expression) + +[c: 'BinaryenStringConcatGetLeft'] +pub fn stringconcatgetleft(expr Expression) Expression + +[c: 'BinaryenStringConcatSetLeft'] +pub fn stringconcatsetleft(expr Expression, leftexpr Expression) + +[c: 'BinaryenStringConcatGetRight'] +pub fn stringconcatgetright(expr Expression) Expression + +[c: 'BinaryenStringConcatSetRight'] +pub fn stringconcatsetright(expr Expression, rightexpr Expression) + +[c: 'BinaryenStringEqGetLeft'] +pub fn stringeqgetleft(expr Expression) Expression + +[c: 'BinaryenStringEqSetLeft'] +pub fn stringeqsetleft(expr Expression, leftexpr Expression) + +[c: 'BinaryenStringEqGetRight'] +pub fn stringeqgetright(expr Expression) Expression + +[c: 'BinaryenStringEqSetRight'] +pub fn stringeqsetright(expr Expression, rightexpr Expression) + +[c: 'BinaryenStringAsGetOp'] +pub fn stringasgetop(expr Expression) Op + +[c: 'BinaryenStringAsSetOp'] +pub fn stringassetop(expr Expression, op Op) + +[c: 'BinaryenStringAsGetRef'] +pub fn stringasgetref(expr Expression) Expression + +[c: 'BinaryenStringAsSetRef'] +pub fn stringassetref(expr Expression, refexpr Expression) + +[c: 'BinaryenStringWTF8AdvanceGetRef'] +pub fn stringwtf8advancegetref(expr Expression) Expression + +[c: 'BinaryenStringWTF8AdvanceSetRef'] +pub fn stringwtf8advancesetref(expr Expression, refexpr Expression) + +[c: 'BinaryenStringWTF8AdvanceGetPos'] +pub fn stringwtf8advancegetpos(expr Expression) Expression + +[c: 'BinaryenStringWTF8AdvanceSetPos'] +pub fn stringwtf8advancesetpos(expr Expression, posexpr Expression) + +[c: 'BinaryenStringWTF8AdvanceGetBytes'] +pub fn stringwtf8advancegetbytes(expr Expression) Expression + +[c: 'BinaryenStringWTF8AdvanceSetBytes'] +pub fn stringwtf8advancesetbytes(expr Expression, bytesexpr Expression) + +[c: 'BinaryenStringWTF16GetGetRef'] +pub fn stringwtf16getgetref(expr Expression) Expression + +[c: 'BinaryenStringWTF16GetSetRef'] +pub fn stringwtf16getsetref(expr Expression, refexpr Expression) + +[c: 'BinaryenStringWTF16GetGetPos'] +pub fn stringwtf16getgetpos(expr Expression) Expression + +[c: 'BinaryenStringWTF16GetSetPos'] +pub fn stringwtf16getsetpos(expr Expression, posexpr Expression) + +[c: 'BinaryenStringIterNextGetRef'] +pub fn stringiternextgetref(expr Expression) Expression + +[c: 'BinaryenStringIterNextSetRef'] +pub fn stringiternextsetref(expr Expression, refexpr Expression) + +[c: 'BinaryenStringIterMoveGetOp'] +pub fn stringitermovegetop(expr Expression) Op + +[c: 'BinaryenStringIterMoveSetOp'] +pub fn stringitermovesetop(expr Expression, op Op) + +[c: 'BinaryenStringIterMoveGetRef'] +pub fn stringitermovegetref(expr Expression) Expression + +[c: 'BinaryenStringIterMoveSetRef'] +pub fn stringitermovesetref(expr Expression, refexpr Expression) + +[c: 'BinaryenStringIterMoveGetNum'] +pub fn stringitermovegetnum(expr Expression) Expression + +[c: 'BinaryenStringIterMoveSetNum'] +pub fn stringitermovesetnum(expr Expression, numexpr Expression) + +[c: 'BinaryenStringSliceWTFGetOp'] +pub fn stringslicewtfgetop(expr Expression) Op + +[c: 'BinaryenStringSliceWTFSetOp'] +pub fn stringslicewtfsetop(expr Expression, op Op) + +[c: 'BinaryenStringSliceWTFGetRef'] +pub fn stringslicewtfgetref(expr Expression) Expression + +[c: 'BinaryenStringSliceWTFSetRef'] +pub fn stringslicewtfsetref(expr Expression, refexpr Expression) + +[c: 'BinaryenStringSliceWTFGetStart'] +pub fn stringslicewtfgetstart(expr Expression) Expression + +[c: 'BinaryenStringSliceWTFSetStart'] +pub fn stringslicewtfsetstart(expr Expression, startexpr Expression) + +[c: 'BinaryenStringSliceWTFGetEnd'] +pub fn stringslicewtfgetend(expr Expression) Expression + +[c: 'BinaryenStringSliceWTFSetEnd'] +pub fn stringslicewtfsetend(expr Expression, endexpr Expression) + +[c: 'BinaryenStringSliceIterGetRef'] +pub fn stringsliceitergetref(expr Expression) Expression + +[c: 'BinaryenStringSliceIterSetRef'] +pub fn stringsliceitersetref(expr Expression, refexpr Expression) + +[c: 'BinaryenStringSliceIterGetNum'] +pub fn stringsliceitergetnum(expr Expression) Expression + +[c: 'BinaryenStringSliceIterSetNum'] +pub fn stringsliceitersetnum(expr Expression, numexpr Expression) + +type Function = voidptr + +[c: 'BinaryenAddFunction'] +pub fn addfunction(module_ Module, name &i8, params Type, results Type, vartypes &Type, numvartypes Index, body Expression) Function + +[c: 'BinaryenGetFunction'] +pub fn getfunction(module_ Module, name &i8) Function + +[c: 'BinaryenRemoveFunction'] +pub fn removefunction(module_ Module, name &i8) + +[c: 'BinaryenGetNumFunctions'] +pub fn getnumfunctions(module_ Module) Index + +[c: 'BinaryenGetFunctionByIndex'] +pub fn getfunctionbyindex(module_ Module, index Index) Function + +[c: 'BinaryenAddFunctionImport'] +pub fn addfunctionimport(module_ Module, internalname &i8, externalmodulename &i8, externalbasename &i8, params Type, results Type) + +[c: 'BinaryenAddTableImport'] +pub fn addtableimport(module_ Module, internalname &i8, externalmodulename &i8, externalbasename &i8) + +[c: 'BinaryenAddMemoryImport'] +pub fn addmemoryimport(module_ Module, internalname &i8, externalmodulename &i8, externalbasename &i8, shared_ u8) + +[c: 'BinaryenAddGlobalImport'] +pub fn addglobalimport(module_ Module, internalname &i8, externalmodulename &i8, externalbasename &i8, globaltype Type, mutable_ bool) + +[c: 'BinaryenAddTagImport'] +pub fn addtagimport(module_ Module, internalname &i8, externalmodulename &i8, externalbasename &i8, params Type, results Type) + +type Export = voidptr + +[c: 'BinaryenAddExport'] +pub fn addexport(module_ Module, internalname &i8, externalname &i8) Export + +[c: 'BinaryenAddFunctionExport'] +pub fn addfunctionexport(module_ Module, internalname &i8, externalname &i8) Export + +[c: 'BinaryenAddTableExport'] +pub fn addtableexport(module_ Module, internalname &i8, externalname &i8) Export + +[c: 'BinaryenAddMemoryExport'] +pub fn addmemoryexport(module_ Module, internalname &i8, externalname &i8) Export + +[c: 'BinaryenAddGlobalExport'] +pub fn addglobalexport(module_ Module, internalname &i8, externalname &i8) Export + +[c: 'BinaryenAddTagExport'] +pub fn addtagexport(module_ Module, internalname &i8, externalname &i8) Export + +[c: 'BinaryenGetExport'] +pub fn getexport(module_ Module, externalname &i8) Export + +[c: 'BinaryenRemoveExport'] +pub fn removeexport(module_ Module, externalname &i8) + +[c: 'BinaryenGetNumExports'] +pub fn getnumexports(module_ Module) Index + +[c: 'BinaryenGetExportByIndex'] +pub fn getexportbyindex(module_ Module, index Index) Export + +type Global = voidptr + +[c: 'BinaryenAddGlobal'] +pub fn addglobal(module_ Module, name &i8, type_ Type, mutable_ bool, init Expression) Global + +[c: 'BinaryenGetGlobal'] +pub fn getglobal(module_ Module, name &i8) Global + +[c: 'BinaryenRemoveGlobal'] +pub fn removeglobal(module_ Module, name &i8) + +[c: 'BinaryenGetNumGlobals'] +pub fn getnumglobals(module_ Module) Index + +[c: 'BinaryenGetGlobalByIndex'] +pub fn getglobalbyindex(module_ Module, index Index) Global + +type Tag = voidptr + +[c: 'BinaryenAddTag'] +pub fn addtag(module_ Module, name &i8, params Type, results Type) Tag + +[c: 'BinaryenGetTag'] +pub fn gettag(module_ Module, name &i8) Tag + +[c: 'BinaryenRemoveTag'] +pub fn removetag(module_ Module, name &i8) + +type Table = voidptr + +[c: 'BinaryenAddTable'] +pub fn addtable(module_ Module, table &i8, initial Index, maximum Index, tabletype Type) Table + +[c: 'BinaryenRemoveTable'] +pub fn removetable(module_ Module, table &i8) + +[c: 'BinaryenGetNumTables'] +pub fn getnumtables(module_ Module) Index + +[c: 'BinaryenGetTable'] +pub fn gettable(module_ Module, name &i8) Table + +[c: 'BinaryenGetTableByIndex'] +pub fn gettablebyindex(module_ Module, index Index) Table + +type ElementSegment = voidptr + +[c: 'BinaryenAddActiveElementSegment'] +pub fn addactiveelementsegment(module_ Module, table &i8, name &i8, funcnames &&u8, numfuncnames Index, offset Expression) ElementSegment + +[c: 'BinaryenAddPassiveElementSegment'] +pub fn addpassiveelementsegment(module_ Module, name &i8, funcnames &&u8, numfuncnames Index) ElementSegment + +[c: 'BinaryenRemoveElementSegment'] +pub fn removeelementsegment(module_ Module, name &i8) + +[c: 'BinaryenGetNumElementSegments'] +pub fn getnumelementsegments(module_ Module) Index + +[c: 'BinaryenGetElementSegment'] +pub fn getelementsegment(module_ Module, name &i8) ElementSegment + +[c: 'BinaryenGetElementSegmentByIndex'] +pub fn getelementsegmentbyindex(module_ Module, index Index) ElementSegment + +[c: 'BinaryenSetMemory'] +pub fn setmemory(module_ Module, initial Index, maximum Index, exportname &i8, segments &&u8, segmentpassive &bool, segmentoffsets &Expression, segmentsizes &Index, numsegments Index, shared_ bool, memory64 bool, name &i8) + +[c: 'BinaryenHasMemory'] +pub fn hasmemory(module_ Module) bool + +[c: 'BinaryenMemoryGetInitial'] +pub fn memorygetinitial(module_ Module, name &i8) Index + +[c: 'BinaryenMemoryHasMax'] +pub fn memoryhasmax(module_ Module, name &i8) bool + +[c: 'BinaryenMemoryGetMax'] +pub fn memorygetmax(module_ Module, name &i8) Index + +[c: 'BinaryenMemoryImportGetModule'] +pub fn memoryimportgetmodule(module_ Module, name &i8) &i8 + +[c: 'BinaryenMemoryImportGetBase'] +pub fn memoryimportgetbase(module_ Module, name &i8) &i8 + +[c: 'BinaryenMemoryIsshared_'] +pub fn memoryisshared_(module_ Module, name &i8) bool + +[c: 'BinaryenMemoryIs64'] +pub fn memoryis64(module_ Module, name &i8) bool + +[c: 'BinaryenGetNumMemorySegments'] +pub fn getnummemorysegments(module_ Module) u32 + +[c: 'BinaryenGetMemorySegmentByteOffset'] +pub fn getmemorysegmentbyteoffset(module_ Module, id Index) u32 + +[c: 'BinaryenGetMemorySegmentByteLength'] +pub fn getmemorysegmentbytelength(module_ Module, id Index) usize + +[c: 'BinaryenGetMemorySegmentPassive'] +pub fn getmemorysegmentpassive(module_ Module, id Index) bool + +[c: 'BinaryenCopyMemorySegmentData'] +pub fn copymemorysegmentdata(module_ Module, id Index, buffer &i8) + +[c: 'BinaryenSetStart'] +pub fn setstart(module_ Module, start Function) + +[c: 'BinaryenModuleGetFeatures'] +pub fn modulegetfeatures(module_ Module) Features + +[c: 'BinaryenModuleSetFeatures'] +pub fn modulesetfeatures(module_ Module, features Features) + +[c: 'BinaryenModuleParse'] +pub fn moduleparse(text &i8) Module + +[c: 'BinaryenModulePrint'] +pub fn moduleprint(module_ Module) + +[c: 'BinaryenModulePrintStackIR'] +pub fn moduleprintstackir(module_ Module, optimize bool) + +[c: 'BinaryenModulePrintAsmjs'] +pub fn moduleprintasmjs(module_ Module) + +[c: 'BinaryenModuleValidate'] +pub fn modulevalidate(module_ Module) bool + +[c: 'BinaryenModuleOptimize'] +pub fn moduleoptimize(module_ Module) + +[c: 'BinaryenModuleUpdateMaps'] +pub fn moduleupdatemaps(module_ Module) + +[c: 'BinaryenGetOptimizeLevel'] +pub fn getoptimizelevel() int + +[c: 'BinaryenSetOptimizeLevel'] +pub fn setoptimizelevel(level int) + +[c: 'BinaryenGetShrinkLevel'] +pub fn getshrinklevel() int + +[c: 'BinaryenSetShrinkLevel'] +pub fn setshrinklevel(level int) + +[c: 'BinaryenGetDebugInfo'] +pub fn getdebuginfo() bool + +[c: 'BinaryenSetDebugInfo'] +pub fn setdebuginfo(on bool) + +[c: 'BinaryenGetLowMemoryUnused'] +pub fn getlowmemoryunused() bool + +[c: 'BinaryenSetLowMemoryUnused'] +pub fn setlowmemoryunused(on bool) + +[c: 'BinaryenGetZeroFilledMemory'] +pub fn getzerofilledmemory() bool + +[c: 'BinaryenSetZeroFilledMemory'] +pub fn setzerofilledmemory(on bool) + +[c: 'BinaryenGetFastMath'] +pub fn getfastmath() bool + +[c: 'BinaryenSetFastMath'] +pub fn setfastmath(value bool) + +[c: 'BinaryenGetPassArgument'] +pub fn getpassargument(name &i8) &i8 + +[c: 'BinaryenSetPassArgument'] +pub fn setpassargument(name &i8, value &i8) + +[c: 'BinaryenClearPassArguments'] +pub fn clearpassarguments() + +[c: 'BinaryenGetAlwaysInlineMaxSize'] +pub fn getalwaysinlinemaxsize() Index + +[c: 'BinaryenSetAlwaysInlineMaxSize'] +pub fn setalwaysinlinemaxsize(size Index) + +[c: 'BinaryenGetFlexibleInlineMaxSize'] +pub fn getflexibleinlinemaxsize() Index + +[c: 'BinaryenSetFlexibleInlineMaxSize'] +pub fn setflexibleinlinemaxsize(size Index) + +[c: 'BinaryenGetOneCallerInlineMaxSize'] +pub fn getonecallerinlinemaxsize() Index + +[c: 'BinaryenSetOneCallerInlineMaxSize'] +pub fn setonecallerinlinemaxsize(size Index) + +[c: 'BinaryenGetAllowInliningFunctionsWithLoops'] +pub fn getallowinliningfunctionswithloops() bool + +[c: 'BinaryenSetAllowInliningFunctionsWithLoops'] +pub fn setallowinliningfunctionswithloops(enabled bool) + +[c: 'BinaryenModuleRunPasses'] +pub fn modulerunpasses(module_ Module, passes &&u8, numpasses Index) + +[c: 'BinaryenModuleAutoDrop'] +pub fn moduleautodrop(module_ Module) + +[c: 'BinaryenModuleWrite'] +pub fn modulewrite(module_ Module, output &i8, outputsize usize) usize + +[c: 'BinaryenModuleWriteText'] +pub fn modulewritetext(module_ Module, output &i8, outputsize usize) usize + +[c: 'BinaryenModuleWriteStackIR'] +pub fn modulewritestackir(module_ Module, output &i8, outputsize usize, optimize bool) usize + +pub struct BufferSizes { +pub: + outputBytes usize + sourceMapBytes usize +} + +[c: 'BinaryenModuleWriteWithSourceMap'] +pub fn modulewritewithsourcemap(module_ Module, url &i8, output &i8, outputsize usize, sourcemap &i8, sourcemapsize usize) BufferSizes + +pub struct ModuleAllocateAndWriteResult { +pub: + binary voidptr + binaryBytes usize + sourceMap &i8 +} + +[c: 'BinaryenModuleAllocateAndWrite'] +pub fn moduleallocateandwrite(module_ Module, sourcemapurl &i8) ModuleAllocateAndWriteResult + +[c: 'BinaryenModuleAllocateAndWriteText'] +pub fn moduleallocateandwritetext(module_ Module) &i8 + +[c: 'BinaryenModuleAllocateAndWriteStackIR'] +pub fn moduleallocateandwritestackir(module_ Module, optimize bool) &i8 + +[c: 'BinaryenModuleRead'] +pub fn moduleread(input &i8, inputsize usize) Module + +[c: 'BinaryenModuleInterpret'] +pub fn moduleinterpret(module_ Module) + +[c: 'BinaryenModuleAddDebugInfoFileName'] +pub fn moduleadddebuginfofilename(module_ Module, filename &i8) Index + +[c: 'BinaryenModuleGetDebugInfoFileName'] +pub fn modulegetdebuginfofilename(module_ Module, index Index) &i8 + +[c: 'BinaryenFunctionGetName'] +pub fn functiongetname(func Function) &i8 + +[c: 'BinaryenFunctionGetParams'] +pub fn functiongetparams(func Function) Type + +[c: 'BinaryenFunctionGetResults'] +pub fn functiongetresults(func Function) Type + +[c: 'BinaryenFunctionGetNumVars'] +pub fn functiongetnumvars(func Function) Index + +[c: 'BinaryenFunctionGetVar'] +pub fn functiongetvar(func Function, index Index) Type + +[c: 'BinaryenFunctionGetNumLocals'] +pub fn functiongetnumlocals(func Function) Index + +[c: 'BinaryenFunctionHasLocalName'] +pub fn functionhaslocalname(func Function, index Index) bool + +[c: 'BinaryenFunctionGetLocalName'] +pub fn functiongetlocalname(func Function, index Index) &i8 + +[c: 'BinaryenFunctionSetLocalName'] +pub fn functionsetlocalname(func Function, index Index, name &i8) + +[c: 'BinaryenFunctionGetBody'] +pub fn functiongetbody(func Function) Expression + +[c: 'BinaryenFunctionSetBody'] +pub fn functionsetbody(func Function, body Expression) + +[c: 'BinaryenFunctionOptimize'] +pub fn functionoptimize(func Function, module_ Module) + +[c: 'BinaryenFunctionRunPasses'] +pub fn functionrunpasses(func Function, module_ Module, passes &&u8, numpasses Index) + +[c: 'BinaryenFunctionSetDebugLocation'] +pub fn functionsetdebuglocation(func Function, expr Expression, fileindex Index, linenumber Index, columnnumber Index) + +[c: 'BinaryenTableGetName'] +pub fn tablegetname(table Table) &i8 + +[c: 'BinaryenTableSetName'] +pub fn tablesetname(table Table, name &i8) + +[c: 'BinaryenTableGetInitial'] +pub fn tablegetinitial(table Table) Index + +[c: 'BinaryenTableSetInitial'] +pub fn tablesetinitial(table Table, initial Index) + +[c: 'BinaryenTableHasMax'] +pub fn tablehasmax(table Table) bool + +[c: 'BinaryenTableGetMax'] +pub fn tablegetmax(table Table) Index + +[c: 'BinaryenTableSetMax'] +pub fn tablesetmax(table Table, max Index) + +[c: 'BinaryenElementSegmentGetName'] +pub fn elementsegmentgetname(elem ElementSegment) &i8 + +[c: 'BinaryenElementSegmentSetName'] +pub fn elementsegmentsetname(elem ElementSegment, name &i8) + +[c: 'BinaryenElementSegmentGetTable'] +pub fn elementsegmentgettable(elem ElementSegment) &i8 + +[c: 'BinaryenElementSegmentSetTable'] +pub fn elementsegmentsettable(elem ElementSegment, table &i8) + +[c: 'BinaryenElementSegmentGetOffset'] +pub fn elementsegmentgetoffset(elem ElementSegment) Expression + +[c: 'BinaryenElementSegmentGetLength'] +pub fn elementsegmentgetlength(elem ElementSegment) Index + +[c: 'BinaryenElementSegmentGetData'] +pub fn elementsegmentgetdata(elem ElementSegment, dataid Index) &i8 + +[c: 'BinaryenElementSegmentIsPassive'] +pub fn elementsegmentispassive(elem ElementSegment) bool + +[c: 'BinaryenGlobalGetName'] +pub fn globalgetname(global Global) &i8 + +[c: 'BinaryenGlobalGetType'] +pub fn globalgettype(global Global) Type + +[c: 'BinaryenGlobalIsMutable'] +pub fn globalismutable(global Global) bool + +[c: 'BinaryenGlobalGetInitExpr'] +pub fn globalgetinitexpr(global Global) Expression + +[c: 'BinaryenTagGetName'] +pub fn taggetname(tag Tag) &i8 + +[c: 'BinaryenTagGetParams'] +pub fn taggetparams(tag Tag) Type + +[c: 'BinaryenTagGetResults'] +pub fn taggetresults(tag Tag) Type + +[c: 'BinaryenFunctionImportGetModule'] +pub fn functionimportgetmodule(import_ Function) &i8 + +[c: 'BinaryenTableImportGetModule'] +pub fn tableimportgetmodule(import_ Table) &i8 + +[c: 'BinaryenGlobalImportGetModule'] +pub fn globalimportgetmodule(import_ Global) &i8 + +[c: 'BinaryenTagImportGetModule'] +pub fn tagimportgetmodule(import_ Tag) &i8 + +[c: 'BinaryenFunctionImportGetBase'] +pub fn functionimportgetbase(import_ Function) &i8 + +[c: 'BinaryenTableImportGetBase'] +pub fn tableimportgetbase(import_ Table) &i8 + +[c: 'BinaryenGlobalImportGetBase'] +pub fn globalimportgetbase(import_ Global) &i8 + +[c: 'BinaryenTagImportGetBase'] +pub fn tagimportgetbase(import_ Tag) &i8 + +[c: 'BinaryenExportGetKind'] +pub fn exportgetkind(export_ Export) ExternalKind + +[c: 'BinaryenExportGetName'] +pub fn exportgetname(export_ Export) &i8 + +[c: 'BinaryenExportGetValue'] +pub fn exportgetvalue(export_ Export) &i8 + +[c: 'BinaryenAddCustomSection'] +pub fn addcustomsection(module_ Module, name &i8, contents &i8, contentssize Index) + +type SideEffects = u32 + +[c: 'BinaryenSideEffectNone'] +pub fn sideeffectnone() SideEffects + +[c: 'BinaryenSideEffectBranches'] +pub fn sideeffectbranches() SideEffects + +[c: 'BinaryenSideEffectCalls'] +pub fn sideeffectcalls() SideEffects + +[c: 'BinaryenSideEffectReadsLocal'] +pub fn sideeffectreadslocal() SideEffects + +[c: 'BinaryenSideEffectWritesLocal'] +pub fn sideeffectwriteslocal() SideEffects + +[c: 'BinaryenSideEffectReadsGlobal'] +pub fn sideeffectreadsglobal() SideEffects + +[c: 'BinaryenSideEffectWritesGlobal'] +pub fn sideeffectwritesglobal() SideEffects + +[c: 'BinaryenSideEffectReadsMemory'] +pub fn sideeffectreadsmemory() SideEffects + +[c: 'BinaryenSideEffectWritesMemory'] +pub fn sideeffectwritesmemory() SideEffects + +[c: 'BinaryenSideEffectReadsTable'] +pub fn sideeffectreadstable() SideEffects + +[c: 'BinaryenSideEffectWritesTable'] +pub fn sideeffectwritestable() SideEffects + +[c: 'BinaryenSideEffectImplicitTrap'] +pub fn sideeffectimplicittrap() SideEffects + +[c: 'BinaryenSideEffectTrapsNeverHappen'] +pub fn sideeffecttrapsneverhappen() SideEffects + +[c: 'BinaryenSideEffectIsAtomic'] +pub fn sideeffectisatomic() SideEffects + +[c: 'BinaryenSideEffectThrows'] +pub fn sideeffectthrows() SideEffects + +[c: 'BinaryenSideEffectDanglingPop'] +pub fn sideeffectdanglingpop() SideEffects + +[c: 'BinaryenSideEffectAny'] +pub fn sideeffectany() SideEffects + +[c: 'BinaryenExpressionGetSideEffects'] +pub fn expressiongetsideeffects(expr Expression, module_ Module) SideEffects + +type Relooper = voidptr +type RelooperBlock = voidptr + +[c: 'RelooperCreate'] +pub fn reloopercreate(module_ Module) Relooper + +[c: 'RelooperAddBlock'] +pub fn relooperaddblock(relooper Relooper, code Expression) RelooperBlock + +[c: 'RelooperAddBranch'] +pub fn relooperaddbranch(from RelooperBlock, to RelooperBlock, condition Expression, code Expression) + +[c: 'RelooperAddBlockWithSwitch'] +pub fn relooperaddblockwithswitch(relooper Relooper, code Expression, condition Expression) RelooperBlock + +[c: 'RelooperAddBranchForSwitch'] +pub fn relooperaddbranchforswitch(from RelooperBlock, to RelooperBlock, indexes &Index, numindexes Index, code Expression) + +[c: 'RelooperRenderAndDispose'] +pub fn relooperrenderanddispose(relooper Relooper, entry RelooperBlock, labelhelper Index) Expression + +type ExpressionRunner = voidptr +type ExpressionRunnerFlags = u32 + +[c: 'ExpressionRunnerFlagsDefault'] +pub fn expressionrunnerflagsdefault() ExpressionRunnerFlags + +[c: 'ExpressionRunnerFlagsPreserveSideeffects'] +pub fn expressionrunnerflagspreservesideeffects() ExpressionRunnerFlags + +[c: 'ExpressionRunnerFlagsTraverseCalls'] +pub fn expressionrunnerflagstraversecalls() ExpressionRunnerFlags + +[c: 'ExpressionRunnerCreate'] +pub fn expressionrunnercreate(module_ Module, flags ExpressionRunnerFlags, maxdepth Index, maxloopiterations Index) ExpressionRunner + +[c: 'ExpressionRunnerSetLocalValue'] +pub fn expressionrunnersetlocalvalue(runner ExpressionRunner, index Index, value Expression) bool + +[c: 'ExpressionRunnerSetGlobalValue'] +pub fn expressionrunnersetglobalvalue(runner ExpressionRunner, name &i8, value Expression) bool + +[c: 'ExpressionRunnerRunAndDispose'] +pub fn expressionrunnerrunanddispose(runner ExpressionRunner, expr Expression) Expression + +type TypeBuilder = voidptr +type TypeBuilderErrorReason = u32 + +[c: 'TypeBuilderErrorReasonSelfSupertype'] +pub fn typebuildererrorreasonselfsupertype() TypeBuilderErrorReason + +[c: 'TypeBuilderErrorReasonInvalidSupertype'] +pub fn typebuildererrorreasoninvalidsupertype() TypeBuilderErrorReason + +[c: 'TypeBuilderErrorReasonForwardSupertypeReference'] +pub fn typebuildererrorreasonforwardsupertypereference() TypeBuilderErrorReason + +[c: 'TypeBuilderErrorReasonForwardChildReference'] +pub fn typebuildererrorreasonforwardchildreference() TypeBuilderErrorReason + +type BasicHeapType = u32 + +[c: 'TypeBuilderCreate'] +pub fn typebuildercreate(size Index) TypeBuilder + +[c: 'TypeBuilderGrow'] +pub fn typebuildergrow(builder TypeBuilder, count Index) + +[c: 'TypeBuilderGetSize'] +pub fn typebuildergetsize(builder TypeBuilder) Index + +[c: 'TypeBuilderSetBasicHeapType'] +pub fn typebuildersetbasicheaptype(builder TypeBuilder, index Index, basicheaptype BasicHeapType) + +[c: 'TypeBuilderSetSignatureType'] +pub fn typebuildersetsignaturetype(builder TypeBuilder, index Index, paramtypes Type, resulttypes Type) + +[c: 'TypeBuilderSetStructType'] +pub fn typebuildersetstructtype(builder TypeBuilder, index Index, fieldtypes &Type, fieldpackedtypes &Type, fieldmutables &bool, numfields int) + +[c: 'TypeBuilderSetArrayType'] +pub fn typebuildersetarraytype(builder TypeBuilder, index Index, elementtype Type, elementpackedtype PackedType, elementmutable int) + +[c: 'TypeBuilderIsBasic'] +pub fn typebuilderisbasic(builder TypeBuilder, index Index) bool + +[c: 'TypeBuilderGetBasic'] +pub fn typebuildergetbasic(builder TypeBuilder, index Index) BasicHeapType + +[c: 'TypeBuilderGetTempHeapType'] +pub fn typebuildergettempheaptype(builder TypeBuilder, index Index) HeapType + +[c: 'TypeBuilderGetTempTupleType'] +pub fn typebuildergettemptupletype(builder TypeBuilder, types &Type, numtypes Index) Type + +[c: 'TypeBuilderGetTempRefType'] +pub fn typebuildergettempreftype(builder TypeBuilder, heaptype HeapType, nullable int) Type + +[c: 'TypeBuilderSetSubType'] +pub fn typebuildersetsubtype(builder TypeBuilder, index Index, supertype HeapType) + +[c: 'TypeBuilderCreateRecGroup'] +pub fn typebuildercreaterecgroup(builder TypeBuilder, index Index, length Index) + +[c: 'TypeBuilderBuildAndDispose'] +pub fn typebuilderbuildanddispose(builder TypeBuilder, heaptypes &HeapType, errorindex &Index, errorreason &TypeBuilderErrorReason) bool + +[c: 'ModuleSetTypeName'] +pub fn modulesettypename(module_ Module, heaptype HeapType, name &i8) + +[c: 'ModuleSetFieldName'] +pub fn modulesetfieldname(module_ Module, heaptype HeapType, index Index, name &i8) + +[c: 'BinaryenSetColorsEnabled'] +pub fn setcolorsenabled(enabled bool) + +[c: 'BinaryenAreColorsEnabled'] +pub fn arecolorsenabled() bool diff --git a/vlib/v/gen/wasm/cast.v b/vlib/v/gen/wasm/cast.v new file mode 100644 index 0000000000..21d201bc79 --- /dev/null +++ b/vlib/v/gen/wasm/cast.v @@ -0,0 +1,91 @@ +module wasm + +import v.ast +import v.gen.wasm.binaryen + +fn (mut g Gen) is_signed(typ ast.Type) bool { + if typ.is_pure_float() { + return true + } + return typ.is_signed() +} + +fn (mut g Gen) unary_cast(from binaryen.Type, is_signed bool, to binaryen.Type) binaryen.Op { + if is_signed { + match from { + type_i32 { + match to { + type_i64 { return binaryen.extendsint32() } + type_f32 { return binaryen.convertsint32tofloat32() } + type_f64 { return binaryen.convertsint32tofloat64() } + else {} + } + } + type_i64 { + match to { + type_i32 { return binaryen.wrapint64() } + type_f32 { return binaryen.convertsint64tofloat32() } + type_f64 { return binaryen.convertsint64tofloat64() } + else {} + } + } + type_f32 { + match to { + type_i32 { return binaryen.truncsfloat32toint32() } + type_i64 { return binaryen.truncsfloat32toint64() } + type_f64 { return binaryen.promotefloat32() } + else {} + } + } + type_f64 { + match to { + type_i32 { return binaryen.truncsfloat64toint32() } + type_i64 { return binaryen.truncsfloat64toint64() } + type_f32 { return binaryen.demotefloat64() } + else {} + } + } + else {} + } + } else { + match from { + type_i32 { + match to { + type_i64 { return binaryen.extenduint32() } + type_f32 { return binaryen.convertuint32tofloat32() } + type_f64 { return binaryen.convertuint32tofloat64() } + else {} + } + } + type_i64 { + match to { + type_i32 { return binaryen.wrapint64() } + type_f32 { return binaryen.convertuint64tofloat32() } + type_f64 { return binaryen.convertuint64tofloat64() } + else {} + } + } + else {} + } + } + g.w_error('bad cast: from ${from} (is signed: ${is_signed}) to ${to}') +} + +fn (mut g Gen) cast_t(expr binaryen.Expression, from ast.Type, to ast.Type) binaryen.Expression { + return g.cast(expr, g.get_wasm_type(from), g.is_signed(from), g.get_wasm_type(to)) +} + +fn (mut g Gen) cast(expr binaryen.Expression, from binaryen.Type, is_signed bool, to binaryen.Type) binaryen.Expression { + if from == to { + return expr + } + + // In the official spec, integers are represented in twos complement. + // WebAssembly does not keep signedness information in it's types + // and uses instructions with variants for signed or unsigned values. + // + // You only need to know if the original type is signed or not to + // perform casting. + + return binaryen.unary(g.mod, g.unary_cast(from, is_signed, to), expr) +} diff --git a/vlib/v/gen/wasm/gen.v b/vlib/v/gen/wasm/gen.v new file mode 100644 index 0000000000..867232061d --- /dev/null +++ b/vlib/v/gen/wasm/gen.v @@ -0,0 +1,1227 @@ +module wasm + +import v.ast +import v.pref +import v.util +import v.token +import v.errors +import v.eval +import v.gen.wasm.binaryen +import os + +[heap; minify] +pub struct Gen { + out_name string + pref &pref.Preferences = unsafe { nil } // Preferences shared from V struct + files []&ast.File +mut: + file_path string // current ast.File path + file_path_idx int // current binaryen debug info index, see `BinaryenModuleAddDebugInfoFileName` + warnings []errors.Warning + errors []errors.Error + table &ast.Table = unsafe { nil } + eval eval.Eval + enum_vals map[string]Enum + // + bp_idx int // Base pointer temporary's index for function, if needed (-1 for none) + stack_frame int // Size of the current stack frame, if needed + mod binaryen.Module // Current Binaryen WebAssembly module + curr_ret []ast.Type // Current return value, multi returns will be split into an array + local_temporaries []Temporary // Local WebAssembly temporaries, referenced with an index + local_addresses map[string]Stack // Local stack structures relative to `bp_idx` + structs map[ast.Type]StructInfo // Cached struct field offsets + // + lbl int + for_labels []string // A stack of strings containing the names of blocks/loops to break/continue to + stack_patches []BlockPatch + needs_stack bool // If true, will use `memory` and `__vsp` + constant_data []ConstantData + constant_data_offset int + module_import_namespace string // `[wasm_import_namespace: 'wasi_snapshot_preview1']` else `env` + globals map[string]GlobalData +} + +// Constants and globals +struct GlobalData { + init ast.Expr + ast_typ ast.Type + abs_address int // relative to `Gen.constant_data_offset` +} + +fn (gd GlobalData) to_var(name string) Global { + return Global{ + name: name + ast_typ: gd.ast_typ + abs_address: gd.abs_address + } +} + +struct StructInfo { +mut: + offsets []int +} + +pub fn (mut g Gen) v_error(s string, pos token.Pos) { + if g.pref.output_mode == .stdout { + util.show_compiler_message('error:', pos: pos, file_path: g.file_path, message: s) + exit(1) + } else { + g.errors << errors.Error{ + file_path: g.file_path + pos: pos + reporter: .gen + message: s + } + } +} + +pub fn (mut g Gen) warning(s string, pos token.Pos) { + if g.pref.output_mode == .stdout { + util.show_compiler_message('warning:', pos: pos, file_path: g.file_path, message: s) + } else { + g.warnings << errors.Warning{ + file_path: g.file_path + pos: pos + reporter: .gen + message: s + } + } +} + +[noreturn] +pub fn (mut g Gen) w_error(s string) { + if g.pref.is_verbose { + print_backtrace() + } + util.verror('wasm error', s) +} + +fn (mut g Gen) vsp_leave() binaryen.Expression { + return binaryen.globalset(g.mod, c'__vsp', binaryen.localget(g.mod, g.bp_idx, type_i32)) +} + +fn (mut g Gen) setup_stack_frame(body binaryen.Expression) binaryen.Expression { + // The V WASM stack grows upwards. This is a choice that came + // to me after considering the following. + // + // 1. Store operator offsets cannot be negative. + // 2. The size allocated for the stack is unknown until + // the end of the function's generation. + // This means that stack deallocation code when returning + // early from function's do not know how much to free. + // 3. I came up with an alternative, a single exit point + // inside a function, with values "falling through" to the + // end of a function and being returned. + // This would fix problem 2. It did not work... + // https://github.com/WebAssembly/binaryen/issues/5490 + // + // Any other option would cause a large amount of boilerplate + // WASM code being duplicated at every return statement. + // + // stack_enter: + // global.get $__vsp + // local.tee $bp_idx + // i32.const {stack_frame} + // i32.add + // global.set $__vsp + // stack_leave: + // local.get $bp_idx + // global.set $__vsp + + // No stack allocations needed! + if g.stack_frame == 0 { + g.stack_patches.clear() + return body + } + g.needs_stack = true + + padded_stack_frame := round_up_to_multiple(g.stack_frame, 8) + + stack_enter := binaryen.globalset(g.mod, c'__vsp', binaryen.binary(g.mod, binaryen.addint32(), + binaryen.constant(g.mod, binaryen.literalint32(padded_stack_frame)), binaryen.localtee(g.mod, + g.bp_idx, binaryen.globalget(g.mod, c'__vsp', type_i32), type_i32))) + mut n_body := [stack_enter, body] + + if g.curr_ret[0] == ast.void_type { + n_body << g.vsp_leave() + } + + for bp in g.stack_patches { + // Insert stack leave on all return calls + binaryen.blockinsertchildat(bp.block, bp.idx, g.vsp_leave()) + } + g.stack_patches.clear() + + return g.mkblock(n_body) +} + +fn (mut g Gen) function_return_wasm_type(typ ast.Type) binaryen.Type { + if typ == ast.void_type { + return type_none + } + types := g.unpack_type(typ).filter(g.table.sym(it).info !is ast.Struct).map(g.get_wasm_type(it)) + if types.len == 0 { + return type_none + } + return binaryen.typecreate(types.data, types.len) +} + +fn (g Gen) unpack_type(typ ast.Type) []ast.Type { + ts := g.table.sym(typ) + return match ts.info { + ast.MultiReturn { + ts.info.types + } + else { + [typ] + } + } +} + +fn (mut g Gen) fn_external_import(node ast.FnDecl) { + if !node.no_body || node.is_method { + g.v_error('interop functions cannot have bodies', node.body_pos) + } + if node.language == .js && g.pref.os == .wasi { + g.v_error('javascript interop functions are not allowed in a `wasi` build', node.pos) + } + + mut paraml := []binaryen.Type{cap: node.params.len} + for arg in node.params { + if !g.is_pure_type(arg.typ) { + g.v_error('arguments to interop functions must be numbers, pointers or booleans', + arg.type_pos) + } + paraml << g.get_wasm_type(arg.typ) + } + if !(node.return_type == ast.void_type || g.is_pure_type(node.return_type)) { + g.v_error('interop functions must return numbers, pointers or booleans', node.return_type_pos) + } + return_type := g.get_wasm_type(node.return_type) + + // internal name: `JS.setpixel` + // external name: `setpixel` + binaryen.addfunctionimport(g.mod, node.name.str, g.module_import_namespace.str, node.short_name.str, + binaryen.typecreate(paraml.data, paraml.len), return_type) +} + +fn (mut g Gen) bare_function_start() { + g.bp_idx = g.new_local_temporary_anon(ast.int_type) + g.stack_frame = 0 +} + +fn (mut g Gen) bare_function(name string, expr binaryen.Expression) binaryen.Function { + mut temporaries := []binaryen.Type{cap: g.local_temporaries.len} + for idx := 0; idx < g.local_temporaries.len; idx++ { + temporaries << g.local_temporaries[idx].typ + } + + func := binaryen.addfunction(g.mod, name.str, type_none, type_none, temporaries.data, + temporaries.len, expr) + + g.local_temporaries.clear() + g.local_addresses = map[string]Stack{} + assert g.for_labels.len == 0 + + return func +} + +fn (mut g Gen) fn_decl(node ast.FnDecl) { + if node.language in [.js, .wasm] { + g.fn_external_import(node) + return + } + + name := if node.is_method { + '${g.table.get_type_name(node.receiver.typ)}.${node.name}' + } else { + node.name + } + + util.timing_start('${@METHOD}: ${name}') + defer { + util.timing_measure('${@METHOD}: ${name}') + } + + if node.no_body { + return + } + if g.pref.is_verbose { + // println(term.green('\n${name}:')) + } + if node.is_deprecated { + g.warning('fn_decl: ${name} is deprecated', node.pos) + } + + // The first parameter is an address of returned struct, + // regardless if the struct contains one field. + // (this should change and is currently a TODO to simplify generation) + // + // All structs are passed by reference regardless if the struct contains one field. + // (todo again...) + // + // Multi returns are implemented with a binaryen tuple type, not a struct reference. + + return_type := g.function_return_wasm_type(node.return_type) + + mut paraml := []binaryen.Type{cap: node.params.len + 1} + g.bp_idx = -1 + g.stack_frame = 0 + + g.curr_ret = g.unpack_type(node.return_type) + + for idx, typ in g.curr_ret { + sym := g.table.sym(typ) + if sym.info is ast.Struct { + g.local_temporaries << Temporary{ + name: '__return${idx}' + typ: type_i32 // pointer + ast_typ: typ + idx: g.local_temporaries.len + } + paraml << ast.voidptr_type + } + } + + for p in node.params { + typ := g.get_wasm_type(p.typ) + /* + if g.table.sym(p.typ).info is ast.Struct { + println("INIT: ${g.structs}, ${g.table.sym(p.typ)}, ${g.table.sym(p.typ).idx}, ${p.typ}, ${p.typ.idx()}") + }*/ + g.local_temporaries << Temporary{ + name: p.name + typ: typ + ast_typ: p.typ + idx: g.local_temporaries.len + } + paraml << typ + } + params_type := binaryen.typecreate(paraml.data, paraml.len) + + g.bp_idx = g.new_local_temporary_anon(ast.int_type) + mut wasm_expr := g.expr_stmts(node.stmts, ast.void_type) + wasm_expr = g.setup_stack_frame(wasm_expr) + + mut temporaries := []binaryen.Type{cap: g.local_temporaries.len - paraml.len} + for idx := paraml.len; idx < g.local_temporaries.len; idx++ { + temporaries << g.local_temporaries[idx].typ + } + function := binaryen.addfunction(g.mod, name.str, params_type, return_type, temporaries.data, + temporaries.len, wasm_expr) + //&& g.pref.os == .browser + if node.is_pub && node.mod == 'main' { + binaryen.addfunctionexport(g.mod, name.str, name.str) + } + if g.pref.is_debug { + binaryen.functionsetdebuglocation(function, wasm_expr, g.file_path_idx, node.pos.line_nr, + node.pos.col) + } + + if g.pref.printfn_list.len > 0 && node.name in g.pref.printfn_list { + binaryen.expressionprint(wasm_expr) + } + + // WTF?? map values are not resetting??? + // g.local_addresses.clear() + g.local_temporaries.clear() + g.local_addresses = map[string]Stack{} + assert g.for_labels.len == 0 +} + +fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw ast.Type, expected_type ast.Type) binaryen.Expression { + if expr is ast.IntegerLiteral { + return g.literal(expr.val, expected_type) + } else if expr is ast.FloatLiteral { + return g.literal(expr.val, expected_type) + } + + got_type := ast.mktyp(got_type_raw) + return g.cast_t(g.expr(expr, got_type), got_type, expected_type) +} + +fn (mut g Gen) literalint(val i64, expected ast.Type) binaryen.Expression { + match g.get_wasm_type(expected) { + type_i32 { return binaryen.constant(g.mod, binaryen.literalint32(int(val))) } + type_i64 { return binaryen.constant(g.mod, binaryen.literalint64(val)) } + else {} + } + g.w_error('literalint: bad type `${expected}`') +} + +fn (mut g Gen) literal(val string, expected ast.Type) binaryen.Expression { + match g.get_wasm_type(expected) { + type_i32 { return binaryen.constant(g.mod, binaryen.literalint32(val.int())) } + type_i64 { return binaryen.constant(g.mod, binaryen.literalint64(val.i64())) } + type_f32 { return binaryen.constant(g.mod, binaryen.literalfloat32(val.f32())) } + type_f64 { return binaryen.constant(g.mod, binaryen.literalfloat64(val.f64())) } + else {} + } + g.w_error('literal: bad type `${expected}`') +} + +fn (mut g Gen) postfix_expr(node ast.PostfixExpr) binaryen.Expression { + kind := if node.op == .inc { token.Kind.plus } else { token.Kind.minus } + + var := g.get_var_from_expr(node.expr) + op := g.infix_from_typ(node.typ, kind) + + expr := binaryen.binary(g.mod, op, g.get_or_lea_lop(var, node.typ), g.literal('1', + node.typ)) + + return g.set_var(var, expr, ast_typ: node.typ) +} + +fn (mut g Gen) infix_expr(node ast.InfixExpr, expected ast.Type) binaryen.Expression { + if node.op in [.logical_or, .and] { + mut exprs := []binaryen.Expression{cap: 2} + + left := g.expr(node.left, node.left_type) + + temporary := g.new_local_temporary_anon(ast.bool_type) + exprs << binaryen.localset(g.mod, temporary, left) + + cmp := if node.op == .logical_or { + binaryen.unary(g.mod, binaryen.eqzint32(), binaryen.localget(g.mod, temporary, + type_i32)) + } else { + binaryen.localget(g.mod, temporary, type_i32) + } + exprs << binaryen.bif(g.mod, cmp, g.expr(node.right, node.right_type), binaryen.localget(g.mod, + temporary, type_i32)) + + return g.mkblock(exprs) + } + + op := g.infix_from_typ(node.left_type, node.op) + + infix := binaryen.binary(g.mod, op, g.expr(node.left, node.left_type), g.expr_with_cast(node.right, + node.right_type, node.left_type)) + + res_typ := if infix_kind_return_bool(node.op) { + ast.bool_type + } else { + node.left_type + } + return g.cast_t(infix, res_typ, expected) +} + +fn (mut g Gen) prefix_expr(node ast.PrefixExpr) binaryen.Expression { + expr := g.expr(node.right, node.right_type) + + return match node.op { + .minus { + if node.right_type.is_pure_float() { + if node.right_type == ast.f32_type_idx { + binaryen.unary(g.mod, binaryen.negfloat32(), expr) + } else { + binaryen.unary(g.mod, binaryen.negfloat64(), expr) + } + } else { + // -val == 0 - val + + if g.get_wasm_type(node.right_type) == type_i32 { + binaryen.binary(g.mod, binaryen.subint32(), binaryen.constant(g.mod, + binaryen.literalint32(0)), expr) + } else { + binaryen.binary(g.mod, binaryen.subint64(), binaryen.constant(g.mod, + binaryen.literalint64(0)), expr) + } + } + } + .not { + binaryen.unary(g.mod, binaryen.eqzint32(), expr) + } + .bit_not { + // ~val == val ^ -1 + + if g.get_wasm_type(node.right_type) == type_i32 { + binaryen.binary(g.mod, binaryen.xorint32(), expr, binaryen.constant(g.mod, + binaryen.literalint32(-1))) + } else { + binaryen.binary(g.mod, binaryen.xorint64(), expr, binaryen.constant(g.mod, + binaryen.literalint64(-1))) + } + } + .amp { + g.lea_var_from_expr(node.right) + } + .mul { + g.deref(expr, node.right_type) + } + else { + // impl deref (.mul), and impl address of (.amp) + g.w_error('`${node.op}val` prefix expression not implemented') + } + } +} + +fn (mut g Gen) mknblock(name string, nodes []binaryen.Expression) binaryen.Expression { + if nodes.len == 0 { + return binaryen.nop(g.mod) + } + + g.lbl++ + return binaryen.block(g.mod, '${name}${g.lbl}'.str, nodes.data, nodes.len, type_auto) +} + +fn (mut g Gen) mkblock(nodes []binaryen.Expression) binaryen.Expression { + if nodes.len == 0 { + return binaryen.nop(g.mod) + } + + g.lbl++ + return binaryen.block(g.mod, 'BLK${g.lbl}'.str, nodes.data, nodes.len, type_auto) +} + +fn (mut g Gen) if_branch(ifexpr ast.IfExpr, idx int) binaryen.Expression { + curr := ifexpr.branches[idx] + + next := if ifexpr.has_else && idx + 2 >= ifexpr.branches.len { + g.expr_stmts(ifexpr.branches[idx + 1].stmts, ifexpr.typ) + } else if idx + 1 >= ifexpr.branches.len { + unsafe { nil } + } else { + g.if_branch(ifexpr, idx + 1) + } + return binaryen.bif(g.mod, g.expr(curr.cond, ast.bool_type), g.expr_stmts(curr.stmts, + ifexpr.typ), next) +} + +fn (mut g Gen) if_expr(ifexpr ast.IfExpr) binaryen.Expression { + return g.if_branch(ifexpr, 0) +} + +const wasm_builtins = ['__memory_grow', '__memory_fill', '__memory_copy'] + +fn (mut g Gen) wasm_builtin(name string, node ast.CallExpr) binaryen.Expression { + mut args := []binaryen.Expression{cap: node.args.len} + for idx, arg in node.args { + args << g.expr(arg.expr, node.expected_arg_types[idx]) + } + + match name { + '__memory_grow' { + return binaryen.memorygrow(g.mod, args[0], c'memory', false) + } + '__memory_fill' { + return binaryen.memoryfill(g.mod, args[0], args[1], args[2], c'memory') + } + '__memory_copy' { + return binaryen.memorycopy(g.mod, args[0], args[1], args[2], c'memory', c'memory') + } + else { + panic('unreachable') + } + } +} + +[params] +struct AssignOpts { + op token.Kind = .assign +} + +fn (mut g Gen) assign_expr_to_var(address LocalOrPointer, right ast.Expr, expected ast.Type, cfg AssignOpts) binaryen.Expression { + return match right { + ast.StructInit { + if cfg.op !in [.decl_assign, .assign] { + op := token.assign_op_to_infix_op(cfg.op) + name := '${g.table.get_type_name(expected)}.${op}' + + // args: [&return, &left, &right] + args := [g.get_or_lea_lop(address, expected), + g.get_or_lea_lop(address, expected), g.expr(right, expected)] + + binaryen.call(g.mod, name.str, args.data, args.len, type_none) + } else { + g.init_struct(address as Var, right) + } + } + ast.StringLiteral { + if g.table.sym(expected).info !is ast.Struct { + offset, _ := g.allocate_string(right) + return g.set_var(address as Var, g.literalint(offset, ast.int_type)) + } + + var := (address as Var) as Stack + + offset, len := g.allocate_string(right) + + g.mknblock('STRINGINIT', [ + g.set_var_v(Stack{ address: var.address, ast_typ: ast.charptr_type }, + g.literalint(offset, ast.u32_type), + offset: 0 + ), + g.set_var_v(Stack{ address: var.address, ast_typ: ast.int_type }, g.literalint(len, + ast.int_type), + offset: g.table.pointer_size + ), + ]) + } + ast.ArrayInit { + var := (address as Var) as Stack + mut exprs := []binaryen.Expression{} + + if !right.is_fixed { + g.w_error('wasm backend does not support non fixed arrays yet') + } + elm_typ := right.elem_type + elm_size, _ := g.get_type_size_align(elm_typ) + mut offset := 0 + if !right.has_val { + return g.mknblock('ARRAYINIT(ZERO)', [ + g.zero_fill(right.typ, var.address), + ]) + } + // [10, 15]! + for e in right.exprs { + exprs << g.assign_expr_to_var(Var(Stack{ + address: var.address + offset + ast_typ: elm_typ + }), e, elm_typ) + offset += elm_size + } + + g.mknblock('ARRAYINIT', exprs) + } + else { + initexpr := g.expr(right, expected) + + expr := if cfg.op !in [.decl_assign, .assign] { + if g.is_pure_type(expected) { + val := g.get_or_lea_lop(address, expected) + op := g.infix_from_typ(expected, token.assign_op_to_infix_op(cfg.op)) + + binaryen.binary(g.mod, op, val, initexpr) + } else { + g.w_error('arith unimplemented or unreachable for struct') + } + } else { + initexpr + } + g.set_var(address, expr, ast_typ: expected) + } + } +} + +fn (mut g Gen) expr_impl(node ast.Expr, expected ast.Type) binaryen.Expression { + return match node { + ast.ParExpr, ast.UnsafeExpr { + g.expr(node.expr, expected) + } + ast.ArrayInit { + pos := g.allocate_local_var('_anonarray', node.typ) + expr := g.assign_expr_to_var(Var(Stack{ address: pos, ast_typ: node.typ }), + node, node.typ) + g.mknblock('EXPR(ARRAYINIT)', [expr, g.lea_address(pos)]) + } + ast.GoExpr { + g.w_error('wasm backend does not support threads') + } + ast.IndexExpr { + // TODO: IMPLICIT BOUNDS CHECKING + /* + if !node.is_direct { + g.w_error('implicit bounds checks are not implemented, create one manually') + }*/ + + mut ptr := g.expr(node.left, ast.voidptr_type) + if node.left_type == ast.string_type { + ptr = g.deref(ptr, ast.voidptr_type) + } + + size, _ := g.get_type_size_align(expected) + index := g.expr(node.index, ast.int_type) + + // ptr + index * size + new_ptr := binaryen.binary(g.mod, binaryen.addint32(), ptr, binaryen.binary(g.mod, + binaryen.mulint32(), index, g.literalint(size, ast.int_type))) + + g.deref(new_ptr, expected) + } + ast.SelectorExpr { + g.cast_t(g.get_or_lea_lop(g.get_var_from_expr(node), node.typ), node.typ, + expected) + } + ast.StructInit { + pos := g.allocate_local_var('_anonstruct', node.typ) + expr := g.assign_expr_to_var(Var(Stack{ address: pos, ast_typ: node.typ }), + node, node.typ) + g.mknblock('EXPR(STRUCTINIT)', [expr, g.lea_address(pos)]) + } + ast.MatchExpr { + g.w_error('wasm backend does not support match expressions yet') + } + ast.EnumVal { + type_name := g.table.get_type_name(node.typ) + ts_type := (g.table.sym(node.typ).info as ast.Enum).typ + g.literalint(g.enum_vals[type_name].fields[node.val], ts_type) + } + ast.OffsetOf { + styp := g.table.sym(node.struct_type) + if styp.kind != .struct_ { + g.v_error('__offsetof expects a struct Type as first argument', node.pos) + } + off := g.get_field_offset(node.struct_type, node.field) + g.literalint(off, ast.u32_type) + } + ast.SizeOf { + size, _ := g.table.type_size(node.typ) + g.literalint(size, ast.u32_type) + } + ast.BoolLiteral { + val := if node.val { binaryen.literalint32(1) } else { binaryen.literalint32(0) } + binaryen.constant(g.mod, val) + } + ast.StringLiteral { + if g.table.sym(expected).info !is ast.Struct { + offset, _ := g.allocate_string(node) + return g.literalint(offset, ast.int_type) + } + + pos := g.allocate_local_var('_anonstring', ast.string_type) + + expr := g.assign_expr_to_var(Var(Stack{ address: pos, ast_typ: ast.string_type }), + node, ast.string_type) + g.mknblock('EXPR(STRINGINIT)', [expr, g.lea_address(pos)]) + } + ast.InfixExpr { + g.infix_expr(node, expected) + } + ast.PrefixExpr { + g.prefix_expr(node) + } + ast.PostfixExpr { + g.postfix_expr(node) + } + ast.CharLiteral { + rns := node.val.runes()[0] + g.literalint(rns, ast.u32_type) + } + ast.Ident { + // TODO: only supports local identifiers, no path.expressions or global names + g.get_var_t(node, expected) + } + ast.IntegerLiteral, ast.FloatLiteral { + g.literal(node.val, expected) + } + ast.IfExpr { + if node.branches.len == 2 && node.is_expr { + left := g.expr_stmts(node.branches[0].stmts, expected) + right := g.expr_stmts(node.branches[1].stmts, expected) + binaryen.bselect(g.mod, g.expr(node.branches[0].cond, ast.bool_type_idx), + left, right, g.get_wasm_type(expected)) + } else { + g.if_expr(node) + } + } + ast.CastExpr { + expr := g.expr(node.expr, node.expr_type) + + if node.typ == ast.bool_type { + panic('unreachable') + } + + /* + if node.typ == ast.bool_type { + // WebAssembly booleans use the `i32` type + // = 0 | is false + // > 0 | is truestyp_to_str_fn_name + // + // It's a checker error to cast to bool anyway... + + bexpr := g.cast(expr, g.get_wasm_type(node.expr_type), g.is_signed(node.expr_type), + type_i32) + binaryen.bselect(g.mod, bexpr, binaryen.constant(g.mod, binaryen.literalint32(1)), binaryen.constant(g.mod, + binaryen.literalint32(0)), type_i32) + } else + */ + g.cast(expr, g.get_wasm_type(node.expr_type), g.is_signed(node.expr_type), + g.get_wasm_type(node.typ)) + } + ast.CallExpr { + mut name := node.name + mut arguments := []binaryen.Expression{cap: node.args.len + 1} + + fret := g.function_return_wasm_type(node.return_type) + is_print := name in ['panic', 'println', 'print', 'eprintln', 'eprint'] + + if name in wasm.wasm_builtins { + return g.wasm_builtin(node.name, node) + } + + if node.is_method { + name = '${g.table.get_type_name(node.receiver_type)}.${node.name}' + } + + ret_types := g.unpack_type(node.return_type) + structs := ret_types.filter(g.table.sym(it).info is ast.Struct) + mut structs_addrs := []int{cap: structs.len} + + // ABI: {return structs} {method `self`}, then {arguments} + for typ in structs { + pos := g.allocate_local_var('_anonstruct', typ) + structs_addrs << pos + arguments << g.lea_address(pos) + } + if node.is_method { + expr := if !node.left_type.is_ptr() && node.receiver_type.is_ptr() { + ast.Expr(ast.PrefixExpr{ + op: .amp + right: node.left + }) + } else { + node.left + } + arguments << g.expr(expr, node.receiver_type) + } + for idx, arg in node.args { + mut expr := arg.expr + typ := arg.typ + + if is_print && typ != ast.string_type { + has_str, _, _ := g.table.sym(typ).str_method_info() + if typ != ast.string_type && !has_str { + g.v_error('cannot implicitly convert as argument does not have a .str() function', + arg.pos) + } + + expr = ast.CallExpr{ + name: 'str' + left: expr + left_type: typ + receiver_type: typ + return_type: ast.string_type + is_method: true + } + } + arguments << g.expr(expr, node.expected_arg_types[idx]) + } + + mut call := binaryen.call(g.mod, name.str, arguments.data, arguments.len, + fret) + if structs.len != 0 { + mut temporary := 0 + // The function's return values contains structs and must be reordered + + if ret_types.len - structs.len != 0 { + temporary = g.new_local_temporary_anon_wtyp(fret) + call = binaryen.localset(g.mod, temporary, call) + } + mut exprs := []binaryen.Expression{} + + mut sidx := 0 + mut tidx := 0 + for typ in ret_types { + ts := g.table.sym(typ) + + if ts.info is ast.Struct { + exprs << g.lea_address(structs_addrs[tidx]) + tidx++ + } else { + exprs << binaryen.tupleextract(g.mod, binaryen.localget(g.mod, + temporary, fret), sidx) + sidx++ + } + } + + vexpr := if exprs.len != 1 { + binaryen.tuplemake(g.mod, exprs.data, exprs.len) + } else { + exprs[0] + } + + call = g.mkblock([call, vexpr]) + } + if expected == ast.void_type && node.return_type != ast.void_type { + call = binaryen.drop(g.mod, call) + } + if node.is_noreturn { + // `[noreturn]` functions cannot return values + call = g.mkblock([call, binaryen.unreachable(g.mod)]) + } + call + } + else { + g.w_error('wasm.expr(): unhandled node: ' + node.type_name()) + } + } +} + +fn (mut g Gen) expr(node ast.Expr, expected ast.Type) binaryen.Expression { + expr := g.expr_impl(node, expected) + + return expr +} + +fn (mut g Gen) multi_assign_stmt(node ast.AssignStmt) binaryen.Expression { + if node.has_cross_var { + g.w_error('complex assign statements are not implemented') + } + + // + // Expected to be a `a, b := multi_return()`. + // + + mut exprs := []binaryen.Expression{cap: node.left.len + 1} + + ret := (node.right[0] as ast.CallExpr).return_type + wret := g.get_wasm_type(ret) + temporary := g.new_local_temporary_anon(ret) + + // Set multi return function to temporary, then use `tuple.extract`. + exprs << binaryen.localset(g.mod, temporary, g.expr(node.right[0], 0)) + + for i := 0; i < node.left.len; i++ { + left := node.left[i] + typ := node.left_types[i] + // rtyp := node.right_types[i] + + if left is ast.Ident { + // `_ = expr` + if left.kind == .blank_ident { + continue + } + if node.op == .decl_assign { + g.new_local(left, typ) + } + } + var := g.get_var_from_expr(left) + popexpr := binaryen.tupleextract(g.mod, binaryen.localget(g.mod, temporary, wret), + i) + exprs << g.set_var(var, popexpr) + } + + return g.mkblock(exprs) +} + +fn (mut g Gen) new_for_label(node_label string) string { + g.lbl++ + label := if node_label != '' { + node_label + } else { + g.lbl.str() + } + g.for_labels << label + + return label +} + +fn (mut g Gen) pop_for_label() { + g.for_labels.pop() +} + +struct BlockPatch { +mut: + idx int + block binaryen.Expression +} + +fn (mut g Gen) expr_stmt(node ast.Stmt, expected ast.Type) binaryen.Expression { + return match node { + ast.Block { + g.expr_stmts(node.stmts, expected) + } + ast.Return { + mut leave_expr_list := []binaryen.Expression{cap: node.exprs.len} + mut exprs := []binaryen.Expression{cap: node.exprs.len} + for idx, expr in node.exprs { + if g.table.sym(g.curr_ret[idx]).info is ast.Struct { + // Could be adapted to use random pointers? + /* + if expr is ast.StructInit { + var := g.local_temporaries[g.get_local_temporary('__return${idx}')] + leave_expr_list << g.init_struct(var, expr) + }*/ + var := g.local_temporaries[g.get_local_temporary('__return${idx}')] + address := g.expr(expr, g.curr_ret[idx]) + + leave_expr_list << g.blit(address, g.curr_ret[idx], binaryen.localget(g.mod, + var.idx, var.typ)) + } else { + exprs << g.expr(expr, g.curr_ret[idx]) + } + } + + mut patch := BlockPatch{ + idx: leave_expr_list.len + } + // leave_expr_list << g.vsp_leave() + + ret_expr := if exprs.len == 1 { + exprs[0] + } else if exprs.len == 0 { + unsafe { nil } + } else { + binaryen.tuplemake(g.mod, exprs.data, exprs.len) + } + leave_expr_list << binaryen.ret(g.mod, ret_expr) + + patch.block = g.mkblock(leave_expr_list) + g.stack_patches << patch + + patch.block + } + ast.ExprStmt { + g.expr(node.expr, expected) + } + ast.ForStmt { + lbl := g.new_for_label(node.label) + + lpp_name := 'L${lbl}' + blk_name := 'B${lbl}' + expr := if !node.is_inf { + // binaryen.bif(g.mod, g.expr(node.cond, ast.bool_type)) + + body := g.expr_stmts(node.stmts, ast.void_type) + lbody := [ + // If !condition, leave. + binaryen.br(g.mod, blk_name.str, binaryen.unary(g.mod, binaryen.eqzint32(), + g.expr(node.cond, ast.bool_type)), unsafe { nil }), + // Body. + body, + // Unconditional loop back to top. + binaryen.br(g.mod, lpp_name.str, unsafe { nil }, unsafe { nil }), + ] + loop := binaryen.loop(g.mod, lpp_name.str, g.mkblock(lbody)) + + binaryen.block(g.mod, blk_name.str, &loop, 1, type_none) + } else { + loop_top := binaryen.br(g.mod, lpp_name.str, unsafe { nil }, unsafe { nil }) + + loop := binaryen.loop(g.mod, lpp_name.str, g.mkblock([ + g.expr_stmts(node.stmts, ast.void_type), + loop_top, + ])) + + binaryen.block(g.mod, blk_name.str, &loop, 1, type_none) + } + g.pop_for_label() + expr + } + ast.ForCStmt { + mut for_stmt := []binaryen.Expression{} + if node.has_init { + for_stmt << g.expr_stmt(node.init, ast.void_type) + } + + lbl := g.new_for_label(node.label) + lpp_name := 'L${lbl}' + blk_name := 'B${lbl}' + + mut loop_exprs := []binaryen.Expression{} + if node.has_cond { + condexpr := binaryen.unary(g.mod, binaryen.eqzint32(), g.expr(node.cond, + ast.bool_type)) + loop_exprs << binaryen.br(g.mod, blk_name.str, condexpr, unsafe { nil }) + } + loop_exprs << g.expr_stmts(node.stmts, ast.void_type) + + if node.has_inc { + loop_exprs << g.expr_stmt(node.inc, ast.void_type) + } + loop_exprs << binaryen.br(g.mod, lpp_name.str, unsafe { nil }, unsafe { nil }) + loop := binaryen.loop(g.mod, lpp_name.str, g.mkblock(loop_exprs)) + + for_stmt << binaryen.block(g.mod, blk_name.str, &loop, 1, type_none) + g.pop_for_label() + g.mkblock(for_stmt) + } + ast.BranchStmt { + mut blabel := if node.label != '' { + node.label + } else { + g.for_labels[g.for_labels.len - 1] + } + + if node.kind == .key_break { + blabel = 'B${blabel}' + } else { + blabel = 'L${blabel}' + } + binaryen.br(g.mod, blabel.str, unsafe { nil }, unsafe { nil }) + } + ast.AssignStmt { + if (node.left.len > 1 && node.right.len == 1) || node.has_cross_var { + // `a, b := foo()` + // `a, b := if cond { 1, 2 } else { 3, 4 }` + // `a, b = b, a` + + g.multi_assign_stmt(node) + } else { + // `a := 1` | `a,b := 1,2` + + mut exprs := []binaryen.Expression{cap: node.left.len} + for i, left in node.left { + right := node.right[i] + typ := node.left_types[i] + + // `_ = expr` must be evaluated even if the value is being dropped! + // The optimiser would remove expressions without side effects. + + // a = expr + // b *= expr + // _ = expr + // a.b = expr + // *a = expr + // a[b] = expr + + if left is ast.Ident { + // `_ = expr` + if left.kind == .blank_ident { + exprs << binaryen.drop(g.mod, g.expr(right, typ)) + continue + } + if node.op == .decl_assign { + g.new_local(left, typ) + } + } + + var := g.get_var_from_expr(left) + exprs << g.assign_expr_to_var(var, right, typ, op: node.op) + } + + if exprs.len == 1 { + exprs[0] + } else if exprs.len != 0 { + g.mkblock(exprs) + } else { + binaryen.nop(g.mod) + } + } + } + else { + g.w_error('wasm.expr_stmt(): unhandled node: ' + node.type_name()) + } + } +} + +pub fn (mut g Gen) expr_stmts(stmts []ast.Stmt, expected ast.Type) binaryen.Expression { + if stmts.len == 0 { + return binaryen.nop(g.mod) + } + if stmts.len == 1 { + return g.expr_stmt(stmts[0], expected) + } + mut exprl := []binaryen.Expression{cap: stmts.len} + for idx, stmt in stmts { + rtyp := if idx + 1 == stmts.len { + expected + } else { + ast.void_type + } + exprl << g.expr_stmt(stmt, rtyp) + } + return g.mkblock(exprl) +} + +fn (mut g Gen) toplevel_stmt(node ast.Stmt) { + match node { + ast.FnDecl { + g.fn_decl(node) + } + ast.Module { + if ns := node.attrs.find_first('wasm_import_namespace') { + g.module_import_namespace = ns.arg + } else { + g.module_import_namespace = 'env' + } + } + ast.GlobalDecl {} + ast.ConstDecl {} + ast.Import {} + ast.StructDecl {} + ast.EnumDecl {} + ast.TypeDecl {} + else { + g.w_error('wasm.toplevel_stmt(): unhandled node: ' + node.type_name()) + } + } +} + +pub fn (mut g Gen) toplevel_stmts(stmts []ast.Stmt) { + for stmt in stmts { + g.toplevel_stmt(stmt) + } +} + +struct Enum { +mut: + fields map[string]i64 +} + +pub fn (mut g Gen) calculate_enum_fields() { + // `enum Enum as u64` is supported + for name, decl in g.table.enum_decls { + mut enum_vals := Enum{} + mut value := if decl.is_flag { i64(1) } else { 0 } + for field in decl.fields { + if field.has_expr { + value = g.eval.expr(field.expr, decl.typ).int_val() + } + enum_vals.fields[field.name] = value + if decl.is_flag { + value <<= 1 + } else { + value++ + } + } + g.enum_vals[name] = enum_vals + } +} + +pub fn gen(files []&ast.File, table &ast.Table, out_name string, w_pref &pref.Preferences) { + mut g := &Gen{ + table: table + pref: w_pref + files: files + eval: eval.new_eval(table, w_pref) + mod: binaryen.modulecreate() + } + g.table.pointer_size = 4 + // Offset all pointers by 8, so that 0 never points to valid memory + g.constant_data_offset = 8 + binaryen.modulesetfeatures(g.mod, binaryen.featureall()) + + if g.pref.os == .browser { + eprintln('`-os browser` is experimental and will not live up to expectations...') + } + + g.calculate_enum_fields() + for file in g.files { + g.file_path = file.path + if g.pref.is_debug { + g.file_path_idx = binaryen.moduleadddebuginfofilename(g.mod, g.file_path.str) + } + if file.errors.len > 0 { + util.verror('wasm error', file.errors[0].str()) + } + g.toplevel_stmts(file.stmts) + } + if g.globals.len != 0 { + g.needs_stack = true + } + g.housekeeping() + if binaryen.modulevalidate(g.mod) { + binaryen.setdebuginfo(w_pref.is_debug) + if w_pref.is_prod { + binaryen.setoptimizelevel(3) + binaryen.moduleoptimize(g.mod) + } + if out_name == '-' { + if g.pref.is_verbose { + binaryen.moduleprint(g.mod) + } else { + binaryen.moduleprintstackir(g.mod, w_pref.is_prod) + } + } else { + bytes := binaryen.moduleallocateandwrite(g.mod, unsafe { nil }) + str := unsafe { (&char(bytes.binary)).vstring_with_len(int(bytes.binaryBytes)) } + os.write_file(out_name, str) or { panic(err) } + } + } else { + binaryen.moduledispose(g.mod) + g.w_error('validation failed, this should not happen. report an issue with the above messages') + } + binaryen.moduledispose(g.mod) +} diff --git a/vlib/v/gen/wasm/mem.v b/vlib/v/gen/wasm/mem.v new file mode 100644 index 0000000000..9b2b8c3c30 --- /dev/null +++ b/vlib/v/gen/wasm/mem.v @@ -0,0 +1,745 @@ +module wasm + +import v.ast +import v.gen.wasm.binaryen +import strconv + +type Var = Global | Stack | Temporary | ast.Ident + +fn (v Var) ast_typ() int { + if v is Temporary { + return v.ast_typ + } + if v is Stack { + return v.ast_typ + } + if v is Global { + return v.ast_typ + } + panic('unreachable') +} + +fn (v Var) address() int { + return (v as Stack).address +} + +struct Temporary { + name string + typ binaryen.Type + ast_typ ast.Type + // + idx int +} + +struct Stack { + name string + ast_typ ast.Type + // + address int +} + +struct Global { + name string + ast_typ ast.Type + // + abs_address int +} + +fn (g Gen) is_pure_type(typ ast.Type) bool { + if typ.is_pure_int() || typ.is_pure_float() || typ == ast.char_type_idx || typ.is_real_pointer() + || typ.is_bool() { + return true + } + ts := g.table.sym(typ) + if ts.info is ast.Alias { + return g.is_pure_type(ts.info.parent_type) + } + return false +} + +fn (mut g Gen) new_global_const(obj ast.ScopeObject) { + obj_expr := match obj { + ast.ConstField { obj.expr } + ast.GlobalField { obj.expr } + else { panic('unreachable') } + } + typ := ast.mktyp(obj.typ) + + size, align := g.get_type_size_align(typ) + padding := (align - g.constant_data_offset % align) % align + g.globals[obj.name] = GlobalData{ + init: obj_expr + ast_typ: typ + abs_address: g.constant_data_offset + } + g.constant_data_offset += size + padding +} + +fn (mut g Gen) get_var_from_ident(ident ast.Ident) Var { + mut obj := ident.obj + if obj !in [ast.Var, ast.ConstField, ast.GlobalField, ast.AsmRegister] { + obj = ident.scope.find(ident.name) or { g.w_error('unknown variable ${ident.name}') } + } + match mut obj { + ast.Var { + if obj.name !in g.local_addresses { + return g.local_temporaries[g.get_local_temporary(obj.name)] + } + return g.local_addresses[obj.name] + } + ast.ConstField { + if obj.name !in g.globals { + g.new_global_const(obj) + } + return g.globals[obj.name].to_var(obj.name) + } + ast.GlobalField { + if obj.name !in g.globals { + g.new_global_const(obj) + } + return g.globals[obj.name].to_var(obj.name) + } + else { + g.w_error('unsupported variable type type:${obj} name:${ident.name}') + } + } +} + +type LocalOrPointer = Var | binaryen.Expression + +fn (mut g Gen) get_or_lea_lop(lp LocalOrPointer, expected ast.Type) binaryen.Expression { + size, _ := g.table.type_size(expected) + + mut offset := 0 + mut parent_typ := expected + mut is_expr := false + + expr := match lp { + binaryen.Expression { + is_expr = true + lp + } + Var { + match lp { + Temporary { + parent_typ = lp.ast_typ + binaryen.localget(g.mod, lp.idx, g.get_wasm_type(expected)) + } + Stack { + parent_typ = lp.ast_typ + offset = lp.address + g.get_bp() + } + Global { + parent_typ = lp.ast_typ + is_expr = true + g.literalint(lp.abs_address, ast.int_type) + } + else { + panic('unreachable') + } + } + } + } + + if !is_expr && parent_typ == expected { + return expr + } + + return binaryen.load(g.mod, u32(size), g.is_signed(expected), u32(offset), 0, g.get_wasm_type(expected), + expr, c'memory') +} + +fn (mut g Gen) lea_lop(lp LocalOrPointer, expected ast.Type) binaryen.Expression { + expr := match lp { + binaryen.Expression { + lp + } + Var { + match lp { + Temporary { + binaryen.localget(g.mod, lp.idx, g.get_wasm_type(expected)) + } + Stack { + g.get_bp() + } + Global { + g.literalint(lp.abs_address, ast.int_type) + } + else { + panic('unreachable') + } + } + } + } + + return expr +} + +fn (mut g Gen) lea_var_from_expr(node ast.Expr) binaryen.Expression { + var := g.get_var_from_expr(node) + + return match var { + binaryen.Expression { + var + } + Var { + match var { + Temporary { + if g.is_pure_type(var.ast_typ) { + g.w_error('lea_var_from_expr: you cannot take the address of a pure temporary') + } + binaryen.localget(g.mod, var.idx, type_i32) + } + Stack { + g.lea_address(var.address) + } + Global { + g.literalint(var.abs_address, ast.int_type) + } + else { + panic('unreachable') + } + } + } + } +} + +fn (mut g Gen) local_or_pointer_add_offset(v Var, offset int) LocalOrPointer { + return match v { + Temporary { + binaryen.binary(g.mod, binaryen.addint32(), binaryen.localget(g.mod, v.idx, + type_i32), binaryen.constant(g.mod, binaryen.literalint32(offset))) + } + Stack { + Var(Stack{ + ...v + address: v.address + offset + }) + } + Global { + Var(Global{ + ...v + abs_address: v.abs_address + offset + }) + } + ast.Ident { + panic('unreachable') + } + } +} + +// TODO: return `Var | binaryen.Expression` +fn (mut g Gen) get_var_from_expr(node ast.Expr) LocalOrPointer { + return match node { + ast.Ident { + g.get_var_from_ident(node) + } + ast.SelectorExpr { + address := g.get_var_from_expr(node.expr) + offset := g.get_field_offset(node.expr_type, node.field_name) + + match address { + binaryen.Expression { + binaryen.binary(g.mod, binaryen.addint32(), address, binaryen.constant(g.mod, + binaryen.literalint32(offset))) + } + Var { + g.local_or_pointer_add_offset(address, offset) + } + } + } + ast.IndexExpr { + address := g.get_var_from_expr(node.left) + + ts := g.table.sym(node.left_type) + deref_type := if g.is_pure_type(node.left_type) { + node.left_type.set_nr_muls(node.left_type.nr_muls() - 1) + } else if ts.kind == .array_fixed { + (ts.info as ast.ArrayFixed).elem_type + } else { + node.left_type + } + size, _ := g.get_type_size_align(deref_type) + + index := g.expr(node.index, ast.int_type) + mut ptr_address := binaryen.Expression(0) + + if address is binaryen.Expression { + ptr_address = address + } + if address is Var { + ptr_address = g.get_var_t(address, ast.voidptr_type) + } + + // ptr + index * size + binaryen.binary(g.mod, binaryen.addint32(), ptr_address, binaryen.binary(g.mod, + binaryen.mulint32(), index, g.literalint(size, ast.int_type))) + } + ast.PrefixExpr { + g.lea_lop(g.get_var_from_expr(node.right), ast.voidptr_type) + } + else { + g.w_error('get_var_from_expr: unexpected `${node.type_name()}`') + } + } +} + +fn (mut g Gen) get_local_temporary(name string) int { + if g.local_temporaries.len == 0 { + g.w_error('get_local: g.local_temporaries.len == 0') + } + mut c := g.local_temporaries.len + for { + c-- + if g.local_temporaries[c].name == name { + return c + } + if c == 0 { + break + } + } + g.w_error("get_local: cannot get '${name}'") +} + +fn (mut g Gen) new_local_temporary_anon_wtyp(w_typ binaryen.Type) int { + ret := g.local_temporaries.len + g.local_temporaries << Temporary{ + name: '_' + typ: w_typ + idx: ret + } + return ret +} + +fn (mut g Gen) new_local_temporary_anon(typ ast.Type) int { + ret := g.local_temporaries.len + g.local_temporaries << Temporary{ + name: '_' + typ: g.get_wasm_type(typ) + ast_typ: typ + idx: ret + } + return ret +} + +fn (mut g Gen) new_local_temporary(name string, typ ast.Type) Temporary { + idx := g.local_temporaries.len + var := Temporary{ + name: name + typ: g.get_wasm_type(typ) + ast_typ: typ + idx: idx + } + g.local_temporaries << var + return var +} + +fn (mut g Gen) new_local(var ast.Ident, typ ast.Type) { + if g.is_pure_type(typ) { + g.new_local_temporary(var.name, typ) + return + } + + ts := g.table.sym(typ) + match ts.info { + ast.Struct, ast.ArrayFixed { + g.allocate_local_var(var.name, typ) + } + ast.Enum { + g.new_local_temporary(var.name, ts.info.typ) + } + else { + g.w_error('new_local: type `${*ts}` (${ts.info.type_name()}) is not a supported local type') + } + } +} + +fn (mut g Gen) deref(expr binaryen.Expression, expected ast.Type) binaryen.Expression { + size, _ := g.get_type_size_align(expected) + return binaryen.load(g.mod, u32(size), g.is_signed(expected), 0, 0, g.get_wasm_type(expected), + expr, c'memory') +} + +fn (mut g Gen) get_field_offset(typ ast.Type, name string) int { + ts := g.table.sym(typ) + field := ts.find_field(name) or { g.w_error('could not find field `${name}` on init') } + if typ !in g.structs { + g.get_type_size_align(typ.idx()) + } + return g.structs[typ.idx()].offsets[field.i] +} + +fn (mut g Gen) get_type_size_align(typ ast.Type) (int, int) { + ts := g.table.sym(typ) + if ts.size != -1 && typ in g.structs { + return ts.size, ts.align + } + + if ts.info !is ast.Struct { + return g.table.type_size(typ) + } + + ti := ts.info as ast.Struct + + // Code borrowed from native, hope you don't mind! + + mut strc := StructInfo{} + mut size := 0 + mut align := 1 + for f in ti.fields { + f_size, f_align := g.table.type_size(f.typ) + if f_size == 0 { + strc.offsets << 0 + continue + } + padding := (f_align - size % f_align) % f_align + strc.offsets << size + padding + size += f_size + padding + if f_align > align { + align = f_align + } + } + size = (size + align - 1) / align * align + g.structs[typ.idx()] = strc + + mut ts_ := g.table.sym(typ) + ts_.size = size + ts_.align = align + + return size, align +} + +fn (mut g Gen) allocate_local_var(name string, typ ast.Type) int { + size, align := g.get_type_size_align(typ) + padding := (align - g.stack_frame % align) % align + address := g.stack_frame + g.stack_frame += size + padding + g.local_addresses[name] = Stack{ + name: name + ast_typ: typ + address: address + } + + return address +} + +fn (mut g Gen) get_bp() binaryen.Expression { + return binaryen.localget(g.mod, g.bp_idx, type_i32) +} + +fn (mut g Gen) lea_address(address int) binaryen.Expression { + return if address != 0 { + binaryen.binary(g.mod, binaryen.addint32(), g.get_bp(), binaryen.constant(g.mod, + binaryen.literalint32(address))) + } else { + g.get_bp() + } +} + +// Will automatcally cast value from `var` to `ast_type`, will ignore if struct value. +// TODO: When supporting base types on the stack, actually cast them. +fn (mut g Gen) get_var_t(var Var, ast_typ ast.Type) binaryen.Expression { + return match var { + ast.Ident { + g.get_var_t(g.get_var_from_ident(var), ast_typ) + } + Temporary { + expr := binaryen.localget(g.mod, var.idx, var.typ) + g.cast_t(expr, var.ast_typ, ast_typ) + } + Stack { + address := if var.address != 0 { + binaryen.binary(g.mod, binaryen.addint32(), g.get_bp(), binaryen.constant(g.mod, + binaryen.literalint32(var.address))) + } else { + g.get_bp() + } + + if g.is_pure_type(var.ast_typ) { + g.cast_t(g.deref(address, var.ast_typ), var.ast_typ, ast_typ) + } else { + address + } + } + Global { + address := g.literalint(var.abs_address, ast.int_type) + if g.is_pure_type(var.ast_typ) { + g.cast_t(g.deref(address, var.ast_typ), var.ast_typ, ast_typ) + } else { + address + } + } + } +} + +[params] +struct SetConfig { + offset int + ast_typ ast.Type +} + +fn (mut g Gen) set_to_address(address_expr binaryen.Expression, expr binaryen.Expression, ast_typ ast.Type) binaryen.Expression { + return if !g.is_pure_type(ast_typ) { + // `expr` is pointer + g.blit(expr, ast_typ, address_expr) + } else { + size, _ := g.table.type_size(ast_typ) + binaryen.store(g.mod, u32(size), 0, 0, address_expr, expr, g.get_wasm_type(ast_typ), + c'memory') + } +} + +fn (mut g Gen) set_var_v(address Var, expr binaryen.Expression, cfg SetConfig) binaryen.Expression { + return g.set_var(address, expr, cfg) +} + +fn (mut g Gen) set_var(address LocalOrPointer, expr binaryen.Expression, cfg SetConfig) binaryen.Expression { + ast_typ := if cfg.ast_typ != 0 { + cfg.ast_typ + } else { + (address as Var).ast_typ() + } + match address { + binaryen.Expression { + return g.set_to_address(address, expr, ast_typ) + } + Var { + var := address + + return match var { + ast.Ident { + g.set_var(g.get_var_from_ident(var), expr, cfg) + } + Temporary { + binaryen.localset(g.mod, var.idx, expr) + } + Stack { + if !g.is_pure_type(ast_typ) { + // `expr` is pointer + g.blit_local(expr, ast_typ, var.address + cfg.offset) + } else { + size, _ := g.table.type_size(ast_typ) + // println("address: ${var.address}, offset: ${cfg.offset}") + binaryen.store(g.mod, u32(size), u32(var.address + cfg.offset), + 0, g.get_bp(), expr, g.get_wasm_type(ast_typ), c'memory') + } + } + Global { + address_expr := g.literalint(var.abs_address + cfg.offset, ast.int_type) + g.set_to_address(address_expr, expr, ast_typ) + } + } + } + } +} + +// zero out stack memory in known local `address`. +fn (mut g Gen) zero_fill(ast_typ ast.Type, address int) binaryen.Expression { + size, _ := g.get_type_size_align(ast_typ) + + if size <= 4 { + zero := g.literalint(0, ast.int_type) + return binaryen.store(g.mod, u32(size), u32(address), 0, g.get_bp(), zero, type_i32, + c'memory') + } else if size <= 8 { + zero := g.literalint(0, ast.i64_type) + return binaryen.store(g.mod, u32(size), u32(address), 0, g.get_bp(), zero, type_i64, + c'memory') + } + return binaryen.memoryfill(g.mod, g.lea_address(address), g.literalint(0, ast.int_type), + g.literalint(size, ast.int_type), c'memory') +} + +// `memcpy` from `ptr` to known local `address` in stack memory. +fn (mut g Gen) blit_local(ptr binaryen.Expression, ast_typ ast.Type, address int) binaryen.Expression { + size, _ := g.get_type_size_align(ast_typ) + return binaryen.memorycopy(g.mod, g.lea_address(address), ptr, binaryen.constant(g.mod, + binaryen.literalint32(size)), c'memory', c'memory') +} + +// `memcpy` from `ptr` to `dest` +fn (mut g Gen) blit(ptr binaryen.Expression, ast_typ ast.Type, dest binaryen.Expression) binaryen.Expression { + size, _ := g.get_type_size_align(ast_typ) + return binaryen.memorycopy(g.mod, dest, ptr, binaryen.constant(g.mod, binaryen.literalint32(size)), + c'memory', c'memory') +} + +fn (mut g Gen) init_struct(var Var, init ast.StructInit) binaryen.Expression { + match var { + ast.Ident { + return g.init_struct(g.get_var_from_ident(var), init) + } + Stack { + mut exprs := []binaryen.Expression{} + + ts := g.table.sym(var.ast_typ) + match ts.info { + ast.Struct { + if init.fields.len == 0 && !(ts.info.fields.any(it.has_default_expr)) { + // Struct definition contains no default initialisers + // AND struct init contains no set values. + return g.mknblock('STRUCTINIT(ZERO)', [ + g.zero_fill(var.ast_typ, var.address), + ]) + } + + for i, f in ts.info.fields { + field_to_be_set := init.fields.map(it.name).contains(f.name) + fts := g.table.sym(f.typ) + if !field_to_be_set { + g.get_type_size_align(var.ast_typ) + offset := g.structs[var.ast_typ.idx()].offsets[i] + if f.has_default_expr { + init_expr := g.expr(f.default_expr, f.typ) // or `unaliased_typ`? + exprs << g.set_var_v(var, init_expr, ast_typ: f.typ, offset: offset) + } else { + if fts.info is ast.Struct { + exprs << g.init_struct(Stack{ + address: var.address + offset + ast_typ: f.typ + }, ast.StructInit{}) + } else { + exprs << g.zero_fill(f.typ, var.address + offset) + } + } + // TODO: replace invocations of `set_var` with `assign_expr_to_var`? + } + } + } + else {} + } + + for f in init.fields { + field := ts.find_field(f.name) or { + g.w_error('could not find field `${f.name}` on init') + } + offset := g.structs[var.ast_typ.idx()].offsets[field.i] + initexpr := g.expr(f.expr, f.expected_type) + + exprs << g.set_var_v(var, initexpr, ast_typ: f.expected_type, offset: offset) + } + + return g.mknblock('STRUCTINIT', exprs) + } + else {} + } + panic('unreachable') +} + +// From native, this should be taken out into `StringLiteral.eval_escape_codes()` +fn (mut g Gen) eval_escape_codes(str_lit ast.StringLiteral) string { + if str_lit.is_raw { + return str_lit.val + } + + str := str_lit.val + mut buffer := []u8{} + + mut i := 0 + for i < str.len { + if str[i] != `\\` { + buffer << str[i] + i++ + continue + } + + // skip \ + i++ + match str[i] { + `\\`, `'`, `"` { + buffer << str[i] + i++ + } + `a`, `b`, `f` { + buffer << str[i] - u8(90) + i++ + } + `n` { + buffer << `\n` + i++ + } + `r` { + buffer << `\r` + i++ + } + `t` { + buffer << `\t` + i++ + } + `u` { + i++ + utf8 := strconv.parse_int(str[i..i + 4], 16, 16) or { + g.w_error('invalid \\u escape code (${str[i..i + 4]})') + 0 + } + i += 4 + buffer << u8(utf8) + buffer << u8(utf8 >> 8) + } + `v` { + buffer << `\v` + i++ + } + `x` { + i++ + c := strconv.parse_int(str[i..i + 2], 16, 8) or { + g.w_error('invalid \\x escape code (${str[i..i + 2]})') + 0 + } + i += 2 + buffer << u8(c) + } + `0`...`7` { + c := strconv.parse_int(str[i..i + 3], 8, 8) or { + g.w_error('invalid escape code \\${str[i..i + 3]}') + 0 + } + i += 3 + buffer << u8(c) + } + else { + g.w_error('invalid escape code \\${str[i]}') + } + } + } + + return buffer.bytestr() +} + +struct ConstantData { + offset int + data []u8 +} + +fn (mut g Gen) constant_data_intern_offset(data []u8) ?(int, int) { + for d in g.constant_data { + if d.data == data { + return d.offset, d.data.len + } + } + return none +} + +// (offset, len) +fn (mut g Gen) allocate_string(node ast.StringLiteral) (int, int) { + data := g.eval_escape_codes(node).bytes() + + // `-prod` will only intern strings. + if g.pref.is_prod { + if offset, len := g.constant_data_intern_offset(data) { + return offset, len + } + } + + offset := g.constant_data_offset + g.constant_data << ConstantData{ + offset: offset + data: data + } + + padding := (8 - offset % 8) % 8 + g.constant_data_offset += data.len + padding + + return offset, data.len +} diff --git a/vlib/v/gen/wasm/ops.v b/vlib/v/gen/wasm/ops.v new file mode 100644 index 0000000000..7d9c1fa14f --- /dev/null +++ b/vlib/v/gen/wasm/ops.v @@ -0,0 +1,320 @@ +module wasm + +import v.ast +import v.token +import v.gen.wasm.binaryen + +const ( + type_none = binaryen.typenone() + type_auto = binaryen.typeauto() + type_i32 = binaryen.typeint32() + type_i64 = binaryen.typeint64() + type_f32 = binaryen.typefloat32() + type_f64 = binaryen.typefloat64() +) + +// "Register size" types such as int, i64 and bool boil down to their WASM counterparts. +// Structures and unions are pointers, i32. +fn (mut g Gen) get_wasm_type(typ_ ast.Type) binaryen.Type { + typ := ast.mktyp(typ_) + if typ == ast.void_type_idx { + return wasm.type_none + } + if typ.is_real_pointer() { + return wasm.type_i32 + } + if typ in ast.number_type_idxs { + return match typ { + ast.isize_type_idx, ast.usize_type_idx, ast.i8_type_idx, ast.u8_type_idx, + ast.char_type_idx, ast.rune_type_idx, ast.i16_type_idx, ast.u16_type_idx, + ast.int_type_idx, ast.u32_type_idx { + wasm.type_i32 + } + ast.i64_type_idx, ast.u64_type_idx, ast.int_literal_type_idx { + wasm.type_i64 + } + ast.f32_type_idx { + wasm.type_f32 + } + ast.f64_type_idx, ast.float_literal_type_idx { + wasm.type_f64 + } + else { + wasm.type_i32 + } + } + } + if typ == ast.bool_type_idx { + return wasm.type_i32 + } + ts := g.table.sym(typ) + match ts.info { + ast.Struct { + g.get_type_size_align(typ) + return wasm.type_i32 // pointer + } + ast.MultiReturn { + // TODO: cache?? + mut paraml := ts.info.types.map(g.get_wasm_type(it)) + return binaryen.typecreate(paraml.data, paraml.len) + } + ast.Alias { + return g.get_wasm_type(ts.info.parent_type) + } + ast.ArrayFixed { + return wasm.type_i32 + } + else {} + } + + g.w_error("get_wasm_type: unreachable type '${*g.table.sym(typ)}' ${ts.info}") +} + +fn infix_kind_return_bool(op token.Kind) bool { + return op in [.eq, .ne, .gt, .lt, .ge, .le] +} + +fn (mut g Gen) infix_from_typ(typ ast.Type, op token.Kind) binaryen.Op { + wasm_typ := g.get_wasm_type(typ) + + match wasm_typ { + wasm.type_i32 { + match op { + .plus { + return binaryen.addint32() + } + .minus { + return binaryen.subint32() + } + .mul { + return binaryen.mulint32() + } + .mod { + if typ.is_signed() { + return binaryen.remsint32() + } else { + return binaryen.remuint32() + } + } + .div { + if typ.is_signed() { + return binaryen.divsint32() + } else { + return binaryen.divuint32() + } + } + .eq { + return binaryen.eqint32() + } + .ne { + return binaryen.neint32() + } + .gt { + if typ.is_signed() { + return binaryen.gtsint32() + } else { + return binaryen.gtuint32() + } + } + .lt { + if typ.is_signed() { + return binaryen.ltsint32() + } else { + return binaryen.ltuint32() + } + } + .ge { + if typ.is_signed() { + return binaryen.gesint32() + } else { + return binaryen.geuint32() + } + } + .le { + if typ.is_signed() { + return binaryen.lesint32() + } else { + return binaryen.leuint32() + } + } + /*.logical_or { + return binaryen.orint32() // TODO: logical or + }*/ + .xor { + return binaryen.xorint32() + } + .pipe { + return binaryen.orint32() + } + .amp { + return binaryen.andint32() + } + .left_shift { + return binaryen.shlint32() + } + .right_shift { + return binaryen.shrsint32() + } + .unsigned_right_shift { + return binaryen.shruint32() + } + else {} + } + } + wasm.type_i64 { + match op { + .plus { + return binaryen.addint64() + } + .minus { + return binaryen.subint64() + } + .mul { + return binaryen.mulint64() + } + .mod { + if typ.is_signed() { + return binaryen.remsint64() + } else { + return binaryen.remuint64() + } + } + .div { + if typ.is_signed() { + return binaryen.divsint64() + } else { + return binaryen.divuint64() + } + } + .eq { + return binaryen.eqint64() + } + .ne { + return binaryen.neint64() + } + .gt { + if typ.is_signed() { + return binaryen.gtsint64() + } else { + return binaryen.gtuint64() + } + } + .lt { + if typ.is_signed() { + return binaryen.ltsint64() + } else { + return binaryen.ltuint64() + } + } + .ge { + if typ.is_signed() { + return binaryen.gesint64() + } else { + return binaryen.geuint64() + } + } + .le { + if typ.is_signed() { + return binaryen.lesint64() + } else { + return binaryen.leuint64() + } + } + /*.logical_or { + return binaryen.orint64() // TODO: logical or + }*/ + .xor { + return binaryen.xorint64() + } + .pipe { + return binaryen.orint64() + } + .amp { + return binaryen.andint64() + } + .left_shift { + return binaryen.shlint64() + } + .right_shift { + return binaryen.shrsint64() + } + .unsigned_right_shift { + return binaryen.shruint64() + } + else {} + } + } + wasm.type_f32 { + match op { + .plus { + return binaryen.addfloat32() + } + .minus { + return binaryen.subfloat32() + } + .mul { + return binaryen.mulfloat32() + } + .div { + return binaryen.divfloat32() + } + .eq { + return binaryen.eqfloat32() + } + .ne { + return binaryen.nefloat32() + } + .gt { + return binaryen.gtfloat32() + } + .lt { + return binaryen.ltfloat32() + } + .ge { + return binaryen.gefloat32() + } + .le { + return binaryen.lefloat32() + } + else {} + } + } + wasm.type_f64 { + match op { + .plus { + return binaryen.addfloat64() + } + .minus { + return binaryen.subfloat64() + } + .mul { + return binaryen.mulfloat64() + } + .div { + return binaryen.divfloat64() + } + .eq { + return binaryen.eqfloat64() + } + .ne { + return binaryen.nefloat64() + } + .gt { + return binaryen.gtfloat64() + } + .lt { + return binaryen.ltfloat64() + } + .ge { + return binaryen.gefloat64() + } + .le { + return binaryen.lefloat64() + } + else {} + } + } + else {} + } + g.w_error('bad infix: op `${op}`') +} diff --git a/vlib/v/gen/wasm/serialisation.v b/vlib/v/gen/wasm/serialisation.v new file mode 100644 index 0000000000..5cdde92167 --- /dev/null +++ b/vlib/v/gen/wasm/serialisation.v @@ -0,0 +1,161 @@ +module wasm + +import v.ast +import v.gen.wasm.binaryen +import encoding.binary as bin +import math.bits + +fn (mut g Gen) bake_constants_plus_initialisers() []GlobalData { + mut initialisers := []GlobalData{} + + for _, global in g.globals { + match global.init { + /* + ast.ArrayInit { + // TODO: call a seraliser recursively over all elements + + if !global.init.is_fixed { + g.w_error('wasm backend does not support non fixed arrays yet') + } + for global.init + }*/ + ast.BoolLiteral { + g.constant_data << ConstantData{ + offset: global.abs_address + data: [u8(global.init.val)] + } + } + ast.FloatLiteral { + mut buf := []u8{len: 8} + wtyp := g.get_wasm_type(global.ast_typ) + match wtyp { + type_f32 { + bin.little_endian_put_u32(mut buf, bits.f32_bits(global.init.val.f32())) + } + type_f64 { + bin.little_endian_put_u64(mut buf, bits.f64_bits(global.init.val.f64())) + unsafe { + buf.len = 4 + } + } + else {} + } + + g.constant_data << ConstantData{ + offset: global.abs_address + data: buf + } + } + ast.StringLiteral { + offset, len := g.allocate_string(global.init) + + if g.table.sym(global.ast_typ).info !is ast.Struct { + mut buf := []u8{len: 4} + bin.little_endian_put_u32(mut buf, u32(offset)) + g.constant_data << ConstantData{ + offset: global.abs_address + data: buf + } + } else { + mut buf := []u8{len: 8} + bin.little_endian_put_u32(mut buf, u32(offset)) + bin.little_endian_put_u32_at(mut buf, u32(len), 4) + + g.constant_data << ConstantData{ + offset: global.abs_address + data: buf + } + } + } + ast.IntegerLiteral { + mut buf := []u8{len: 8} + wtyp := g.get_wasm_type(global.ast_typ) + match wtyp { + type_i32 { + bin.little_endian_put_u32(mut buf, u32(global.init.val.int())) + } + type_i64 { + bin.little_endian_put_u64(mut buf, u64(global.init.val.i64())) + unsafe { + buf.len = 4 + } + } + else {} + } + + g.constant_data << ConstantData{ + offset: global.abs_address + data: buf + } + } + else { + initialisers << global + } + } + } + + return initialisers +} + +fn round_up_to_multiple(val int, multiple int) int { + return val + (multiple - val % multiple) % multiple +} + +fn (mut g Gen) make_vinit() binaryen.Function { + runtime_inits := g.bake_constants_plus_initialisers() + + g.bare_function_start() + + mut body := runtime_inits.map(g.set_var_v(it.to_var(''), g.expr(it.init, it.ast_typ))) + + for mod_name in g.table.modules { + if mod_name == 'v.reflection' { + g.w_error('the wasm backend does not implement `v.reflection` yet') + } + + init_fn_name := if mod_name != 'builtin' { '${mod_name}.init' } else { 'init' } + if _ := g.table.find_fn(init_fn_name) { + body << binaryen.call(g.mod, init_fn_name.str, unsafe { nil }, 0, type_none) + } + cleanup_fn_name := if mod_name != 'builtin' { '${mod_name}.cleanup' } else { 'cleanup' } + if _ := g.table.find_fn(cleanup_fn_name) { + body << binaryen.call(g.mod, cleanup_fn_name.str, unsafe { nil }, 0, type_none) + } + } + + return g.bare_function('_vinit', g.mkblock(body)) +} + +fn (mut g Gen) housekeeping() { + // `_vinit` should be used to initialise the WASM module, + // then `main.main` can be called safely. + vinit := g.make_vinit() + + if g.needs_stack || g.constant_data.len != 0 { + data := g.constant_data.map(it.data.data) + data_len := g.constant_data.map(it.data.len) + data_offsets := g.constant_data.map(binaryen.constant(g.mod, binaryen.literalint32(it.offset))) + passive := []bool{len: g.constant_data.len, init: false} + + binaryen.setmemory(g.mod, 1, 4, c'memory', data.data, passive.data, data_offsets.data, + data_len.data, data.len, false, false, c'memory') + } + if g.needs_stack { + // `g.constant_data_offset` rounded up to a multiple of 1024 + offset := round_up_to_multiple(g.constant_data_offset, 1024) + + binaryen.addglobal(g.mod, c'__vsp', type_i32, true, g.literalint(offset, ast.int_type)) + } + if g.pref.os == .wasi { + main_expr := g.mkblock([binaryen.call(g.mod, c'_vinit', unsafe { nil }, 0, type_none), + binaryen.call(g.mod, c'main.main', unsafe { nil }, 0, type_none)]) + binaryen.addfunction(g.mod, c'_start', type_none, type_none, unsafe { nil }, 0, + main_expr) + binaryen.addfunctionexport(g.mod, c'_start', c'_start') + } else { + // In `browser` mode, and function can be exported and called regardless. + // To avoid uninitialised data, `_vinit` is set to be ran immediately on + // WASM module creation. + binaryen.setstart(g.mod, vinit) + } +} diff --git a/vlib/v/gen/wasm/tests/arith.vv b/vlib/v/gen/wasm/tests/arith.vv new file mode 100644 index 0000000000..9ed4573322 --- /dev/null +++ b/vlib/v/gen/wasm/tests/arith.vv @@ -0,0 +1,86 @@ +fn mul(a i64, unsigned u32) f64 { + mut one := a + + one *= 2 / unsigned + + return one +} + +fn typ(a int) i64 { + mut one := a + sec := one + i64(a) + + return sec +} + +pub fn gcd(a_ i64, b_ i64) i64 { + mut a := a_ + mut b := b_ + if a < 0 { + a = -a + } + if b < 0 { + b = -b + } + for b != 0 { + a %= b + if a == 0 { + return b + } + b %= a + } + return a +} + +pub fn lcm(a i64, b i64) i64 { + if a == 0 { + return a + } + res := a * (b / gcd(b, a)) + if res < 0 { + return -res + } + return res +} + +pub fn inc(a f64) int { + mut b := a + b++ + b-- + return int(b) +} + +pub fn negate(a int) i64 { + return ~a + 1 +} + +pub fn powi(a i64, b i64) i64 { + mut b_ := b + mut p := a + mut v := i64(1) + + if b_ < 0 { // exponent < 0 + if a == 0 { + return -1 // division by 0 + } + return if a * a != 1 { + 0 + } else { + if (b_ & 1) > 0 { + a + } else { + 1 + } + } + } + + for b_ > 0 { + if b_ & 1 > 0 { + v *= p + } + p *= p + b_ >>= 1 + } + + return v +} diff --git a/vlib/v/gen/wasm/tests/arrays.vv b/vlib/v/gen/wasm/tests/arrays.vv new file mode 100644 index 0000000000..7ae783e2b9 --- /dev/null +++ b/vlib/v/gen/wasm/tests/arrays.vv @@ -0,0 +1,28 @@ +struct TEST { + a int + b i64 +} + +fn static_arrays() { + a := [8]int{} + b := [10, 12, 150]! + c := [TEST{}, TEST{ + b: 10 + }]! +} + +fn index_expression() { + b := [10, 12, 150]! + + a := b[2] + c := 'hello'[4] + d := c'hello'[2] +} + +fn test_this(index int) int { + a := 'hello' + if index < a.len && index >= 0 { + return a[index] + } + return 10 +} diff --git a/vlib/v/gen/wasm/tests/builtin.vv b/vlib/v/gen/wasm/tests/builtin.vv new file mode 100644 index 0000000000..3358959137 --- /dev/null +++ b/vlib/v/gen/wasm/tests/builtin.vv @@ -0,0 +1,18 @@ +fn test() { + print('hello!') + println('hello!') + panic('nooo!') +} + +fn str_methods() { + print(128.str()) + println(i64(-192322).str()) + println(false.str()) +} + +fn str_implicit() { + println(false) + println(true) + a := 100 + println(a + 10) +} diff --git a/vlib/v/gen/wasm/tests/control_flow.vv b/vlib/v/gen/wasm/tests/control_flow.vv new file mode 100644 index 0000000000..219f3fb17c --- /dev/null +++ b/vlib/v/gen/wasm/tests/control_flow.vv @@ -0,0 +1,91 @@ +fn func(a int, cond bool) i64 { + mut src := 0 + + if cond { + src = a + } else if cond { + src = 22 + } else if cond { + src = 25 + } + + if cond { + src = a + } else if cond { + src = 22 + } else if cond { + src = 25 + } else { + src = src + src + } + + return src +} + +fn test(cond bool) int { + return if cond { + 2 + } else if !cond { + 5 + } else { + 6 + } +} + +fn boolfor() int { + mut val := 0 + for val == 0 { + val++ + } + return val +} + +fn inffor() int { + mut val := 0 + for { + if val != 0 { + break + } + val++ + } + return val +} + +fn addcfor() int { + mut val := 0 + for i := 0; i < 10; i++ { + val += i + } + return val +} + +fn labelcfor() { + mut val := 0 + + hello: for { + for { + if val == 10 { + continue hello + } + val++ + + if val == 100 { + break hello + } + } + break + } +} + +fn infcfor() int { + mut val := 0 + + for i := 0; true; i++ { + if val >= 10 { + return val + } + val += i + } + + return 0 +} diff --git a/vlib/v/gen/wasm/tests/misc.vv b/vlib/v/gen/wasm/tests/misc.vv new file mode 100644 index 0000000000..171fabdb14 --- /dev/null +++ b/vlib/v/gen/wasm/tests/misc.vv @@ -0,0 +1,57 @@ +enum Hello as u64 { + a + b + c = 20 + 10 + d + e +} + +fn enums() { + mut a := Hello.a + a = .c +} + +struct AA { + a u8 + b i64 +} + +fn of() { + a := __offsetof(AA, b) + b := sizeof(AA) +} + +fn constant() int { + return 100 +} + +const hello = 'hello\n' + +const float = 1.0 + +const integer = 888 + +const runtime_init = constant() + +struct EE { + a int + b int +} + +fn ptr_arith() { + mut a := EE{} + mut b := &a.b + + unsafe { + *b = 12 + } + println(a.b.str()) + unsafe { + *b = 14 + } + println(a.b.str()) + unsafe { + *b = 102 + } + println((*b).str()) +} diff --git a/vlib/v/gen/wasm/tests/multi_expr.vv b/vlib/v/gen/wasm/tests/multi_expr.vv new file mode 100644 index 0000000000..784dafd9ca --- /dev/null +++ b/vlib/v/gen/wasm/tests/multi_expr.vv @@ -0,0 +1,30 @@ +fn multi(a i16) i64 { + one, two := a, 10 + return one + two +} + +pub fn multireturn(a int) (int, f64, i64) { + return 2, a + 2, 10 - a +} + +pub fn test() (int, int) { + return 25, 15 +} + +pub fn accept() int { + mut a, _ := test() + a += 20 + return a +} + +pub fn side_effect() int { + return 22 +} + +pub fn test_side_effect() int { + mut a := 15 + + _, a = side_effect(), 10 + + return a +} diff --git a/vlib/v/gen/wasm/tests/structs.vv b/vlib/v/gen/wasm/tests/structs.vv new file mode 100644 index 0000000000..48b1cc007e --- /dev/null +++ b/vlib/v/gen/wasm/tests/structs.vv @@ -0,0 +1,179 @@ +struct AA { + a int = 22 + b i64 +} + +pub fn zeroed() { + _ := AA{} +} + +pub fn field() { + _ := AA{ + a: 23 + } +} + +pub fn selector(input int) int { + mut a := AA{} + + c := 10 + a.a + + return c +} + +pub fn reassign(input int) int { + mut a := AA{} + + a = AA{ + b: input + } + + return int(a.b + input) +} + +struct BB { +mut: + a i64 = 22 + b i64 + c i64 +} + +pub fn give(val int) int { + mut a := BB{} + + a.b = val + + return take(a) +} + +pub fn take(input BB) int { + return int(input.b) +} + +struct BB_ { +mut: + a i64 = 22 + b AA_ +} + +struct AA_ { +mut: + a i64 = 91 + b i64 = 92 + c i64 = 93 +} + +fn e() BB_ { + return BB_{ + a: 2 + } +} + +pub fn make(nval AA_) i64 { + val := BB_{ + b: nval + } + + return val.b.b +} + +pub fn return_make(nval int) int { + val := make(AA_{ b: nval }) + return int(val) +} + +fn my_func(val int) AA_ { + return AA_{ + b: val + } +} + +fn accept() { + my_func(20) +} + +struct CC { +mut: + a i64 = 91 + b i64 = 92 + c i64 = 93 +} + +fn my_func_multi(val int) (CC, CC) { + return CC{ + b: val + }, CC{ + a: val + } +} + +pub fn accept_multi(val int) int { + a, b := my_func_multi(val) + return int(a.b + b.a) +} + +struct Vector { + x int + y int +} + +fn add(a Vector, b Vector) Vector { + return Vector{a.x + b.x, a.y + b.y} +} + +pub fn test(a int, b int) (int, int) { + vec := Vector{a, b} + + ret := add(vec, Vector{10, 5}) + + return ret.x, ret.y +} + +struct Aello { + a int + b i64 + c int +} + +struct Hello { + a int = 20 + b Aello + c int = 222 +} + +pub fn recurse() { + a := Hello{} +} + +struct DD { + x int + y int +} + +fn (a DD) + (b DD) DD { + return DD{a.x + b.x, a.y + b.y} +} + +pub fn valer() (int, int) { + mut a := DD{10, 15} + + a += DD{10, 15} + + return a.x, a.y +} + +struct TEST { +mut: + a int + b int +} + +fn postfix_test() { + mut a := TEST{} + a.b++ + a.a++ +} + +fn postfix_test_mut(mut a TEST) { + a.b++ +} diff --git a/vlib/v/gen/wasm/tests/wasm_test.v b/vlib/v/gen/wasm/tests/wasm_test.v new file mode 100644 index 0000000000..768a106825 --- /dev/null +++ b/vlib/v/gen/wasm/tests/wasm_test.v @@ -0,0 +1,78 @@ +import os +import benchmark +import term + +const is_verbose = os.getenv('VTEST_SHOW_CMD') != '' + +// TODO some logic copy pasted from valgrind_test.v and compiler_test.v, move to a module +fn test_wasm() { + mut bench := benchmark.new_benchmark() + vexe := os.getenv('VEXE') + vroot := os.dir(vexe) + dir := os.join_path(vroot, 'vlib/v/gen/wasm/tests') + files := os.ls(dir) or { panic(err) } + // + wrkdir := os.join_path(os.vtmp_dir(), 'v', 'tests', 'wasm') + os.mkdir_all(wrkdir) or { panic(err) } + defer { + os.rmdir_all(wrkdir) or {} + } + os.chdir(wrkdir) or {} + tests := files.filter(it.ends_with('.vv')) + if tests.len == 0 { + println('no wasm tests found') + assert false + } + bench.set_total_expected_steps(tests.len) + for test in tests { + bench.step() + full_test_path := os.real_path(os.join_path(dir, test)) + test_file_name := os.file_name(test) + relative_test_path := full_test_path.replace(vroot + '/', '') + work_test_path := '${wrkdir}/${test_file_name}' + tmperrfile := '${dir}/${test}.tmperr' + outfile := '${dir}/${test}.out' + // force binaryen to print without colour + cmd := '${os.quoted_path(vexe)} -o - -b wasm ${os.quoted_path(full_test_path)} 2> ${os.quoted_path(tmperrfile)}' + if is_verbose { + println(cmd) + } + res_wasm := os.execute(cmd) + if res_wasm.exit_code != 0 { + bench.fail() + eprintln(bench.step_message_fail(cmd)) + + if os.exists(tmperrfile) { + err := os.read_file(tmperrfile) or { panic(err) } + eprintln(err) + } + + continue + } + os.rm(tmperrfile) or {} + if expected_ := os.read_file(outfile) { + mut expected := expected_ + expected = expected.trim_right('\r\n').replace('\r\n', '\n') + mut found := res_wasm.output.trim_right('\r\n').replace('\r\n', '\n') + found = found.trim_space() + if expected != found { + println(term.red('FAIL')) + println('============') + println('expected: "${expected}" len=${expected.len}') + println('============') + println('found:"${found}" len=${found.len}') + println('============\n') + bench.fail() + continue + } + } + bench.ok() + eprintln(bench.step_message_ok(relative_test_path)) + } + bench.stop() + eprintln(term.h_divider('-')) + eprintln(bench.total_message('wasm')) + if bench.nfail > 0 { + exit(1) + } +} diff --git a/vlib/v/parser/fn.v b/vlib/v/parser/fn.v index af120a589b..27b6a084cf 100644 --- a/vlib/v/parser/fn.v +++ b/vlib/v/parser/fn.v @@ -14,6 +14,8 @@ pub fn (mut p Parser) call_expr(language ast.Language, mod string) ast.CallExpr 'C.${p.check_name()}' } else if language == .js { 'JS.${p.check_js_name()}' + } else if language == .wasm { + 'WASM.${p.check_name()}' } else if mod.len > 0 { '${mod}.${p.check_name()}' } else { @@ -242,6 +244,8 @@ fn (mut p Parser) fn_decl() ast.FnDecl { language = .c } else if p.tok.kind == .name && p.tok.lit == 'JS' { language = .js + } else if p.tok.kind == .name && p.tok.lit == 'WASM' { + language = .wasm } p.fn_language = language if language != .v { @@ -474,6 +478,8 @@ run them via `v file.v` instead', name = 'C.${name}' } else if language == .js { name = 'JS.${name}' + } else if language == .wasm { + name = 'WASM.${name}' } else { name = p.prepend_mod(name) } diff --git a/vlib/v/parser/parse_type.v b/vlib/v/parser/parse_type.v index 07f3efdb62..2b03bdf96b 100644 --- a/vlib/v/parser/parse_type.v +++ b/vlib/v/parser/parse_type.v @@ -300,6 +300,8 @@ pub fn (mut p Parser) parse_language() ast.Language { ast.Language.c } else if p.tok.lit == 'JS' { ast.Language.js + } else if p.tok.lit == 'WASM' { + ast.Language.wasm } else { ast.Language.v } diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 32534bb3db..839e65b24f 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -2387,6 +2387,9 @@ pub fn (mut p Parser) name_expr() ast.Expr { } else if p.tok.lit == 'JS' { language = ast.Language.js p.check_for_impure_v(language, p.tok.pos()) + } else if p.tok.lit == 'WASM' { + language = ast.Language.wasm + p.check_for_impure_v(language, p.tok.pos()) } mut mod := '' // p.warn('resetting') @@ -2484,6 +2487,8 @@ pub fn (mut p Parser) name_expr() ast.Expr { mod = 'C' } else if language == .js { mod = 'JS' + } else if language == .wasm { + mod = 'WASM' } else { if p.tok.lit in p.imports { // mark the imported module as used @@ -3421,6 +3426,12 @@ fn (mut p Parser) module_decl() ast.Module { 'translated' { p.is_translated = true } + 'wasm_import_namespace' { + if !p.pref.is_fmt && p.pref.backend != .wasm { + p.error_with_pos('[wasm_import_namespace] is allowed only in the wasm backend', + ma.pos) + } + } else { p.error_with_pos('unknown module attribute `[${ma.name}]`', ma.pos) return mod_node @@ -3899,6 +3910,7 @@ fn (mut p Parser) enum_decl() ast.EnumDecl { is_flag: is_flag is_multi_allowed: is_multi_allowed uses_exprs: uses_exprs + typ: enum_type } is_pub: is_pub }) diff --git a/vlib/v/parser/struct.v b/vlib/v/parser/struct.v index 8578aef5e5..acf0cbbb00 100644 --- a/vlib/v/parser/struct.v +++ b/vlib/v/parser/struct.v @@ -29,6 +29,8 @@ fn (mut p Parser) struct_decl(is_anon bool) ast.StructDecl { ast.Language.c } else if p.tok.lit == 'JS' && p.peek_tok.kind == .dot { ast.Language.js + } else if p.tok.lit == 'WASM' && p.peek_tok.kind == .dot { + ast.Language.wasm } else { ast.Language.v } @@ -88,6 +90,9 @@ fn (mut p Parser) struct_decl(is_anon bool) ast.StructDecl { } else if language == .js { name = 'JS.${name}' orig_name = name + } else if language == .wasm { + name = 'WASM.${name}' + orig_name = name } else { name = p.prepend_mod(name) } @@ -510,7 +515,7 @@ fn (mut p Parser) interface_decl() ast.InterfaceDecl { ast.Language.v } if language != .v { - p.next() // C || JS + p.next() // C || JS | WASM p.next() // . } name_pos := p.tok.pos() diff --git a/vlib/v/pref/default.v b/vlib/v/pref/default.v index e845dfc230..1aecb4d2e8 100644 --- a/vlib/v/pref/default.v +++ b/vlib/v/pref/default.v @@ -112,7 +112,7 @@ pub fn (mut p Preferences) fill_with_defaults() { } if p.os == ._auto { // No OS specifed? Use current system - p.os = get_host_os() + p.os = if p.backend != .wasm { get_host_os() } else { .wasi } } // p.try_to_use_tcc_by_default() diff --git a/vlib/v/pref/os.v b/vlib/v/pref/os.v index 330f5552bd..0f7a2e112f 100644 --- a/vlib/v/pref/os.v +++ b/vlib/v/pref/os.v @@ -27,6 +27,8 @@ pub enum OS { wasm32 wasm32_emscripten wasm32_wasi + browser // -b wasm -os browser + wasi // -b wasm -os wasi raw all } @@ -107,6 +109,13 @@ pub fn os_from_string(os_str string) !OS { 'wasm32_emscripten' { return .wasm32_emscripten } + // Native WASM options: + 'browser' { + return .browser + } + 'wasi' { + return .wasi + } else { // handle deprecated names: match os_str { @@ -148,6 +157,8 @@ pub fn (o OS) str() string { .wasm32 { return 'WebAssembly' } .wasm32_emscripten { return 'WebAssembly(Emscripten)' } .wasm32_wasi { return 'WebAssembly(WASI)' } + .browser { return 'browser' } + .wasi { return 'wasi' } .raw { return 'Raw' } .all { return 'all' } } diff --git a/vlib/v/pref/pref.v b/vlib/v/pref/pref.v index 7afd45d042..eb237e9654 100644 --- a/vlib/v/pref/pref.v +++ b/vlib/v/pref/pref.v @@ -53,6 +53,7 @@ pub enum Backend { js_browser // The JavaScript browser backend js_freestanding // The JavaScript freestanding backend native // The Native backend + wasm // The WebAssembly backend } pub fn (b Backend) is_js() bool { @@ -694,7 +695,7 @@ pub fn parse_args_and_show_errors(known_external_commands []string, args []strin res.build_options << '${arg} ${sbackend}' b := backend_from_string(sbackend) or { eprintln('Unknown V backend: ${sbackend}') - eprintln('Valid -backend choices are: c, go, interpret, js, js_node, js_browser, js_freestanding, native') + eprintln('Valid -backend choices are: c, go, interpret, js, js_node, js_browser, js_freestanding, native, wasm') exit(1) } if b.is_js() { @@ -786,6 +787,14 @@ pub fn parse_args_and_show_errors(known_external_commands []string, args []strin eprintln_cond(show_output, "Note: building an optimized binary takes much longer. It shouldn't be used with `v run`.") eprintln_cond(show_output, 'Use `v run` without optimization, or build an optimized binary with -prod first, then run it separately.') } + if res.os in [.browser, .wasi] && res.backend != .wasm { + eprintln('OS `${res.os}` forbidden for backends other than wasm') + exit(1) + } + if res.backend == .wasm && res.os !in [.browser, .wasi, ._auto] { + eprintln('Native WebAssembly backend OS must be `browser` or `wasi`') + exit(1) + } // res.use_cache = true if command != 'doc' && res.out_name.ends_with('.v') { @@ -980,6 +989,7 @@ pub fn backend_from_string(s string) !Backend { 'js_browser' { return .js_browser } 'js_freestanding' { return .js_freestanding } 'native' { return .native } + 'wasm' { return .wasm } else { return error('Unknown backend type ${s}') } } }