mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
wasm: remove dependency on thirdparty/binaryen, webassembly backend rewrite (#18120)
This commit is contained in:
parent
1c7df29bed
commit
c422919481
58
.github/workflows/wasm_backend_tests_ci.yml
vendored
58
.github/workflows/wasm_backend_tests_ci.yml
vendored
@ -60,8 +60,10 @@ jobs:
|
|||||||
- name: Build V
|
- name: Build V
|
||||||
run: make -j4 && ./v symlink -githubci
|
run: make -j4 && ./v symlink -githubci
|
||||||
|
|
||||||
- name: Install binaryen as build dependency for the V WASM backend
|
- name: Install wasmer to execute WASM modules
|
||||||
run: ./v cmd/tools/install_binaryen.vsh
|
run: |
|
||||||
|
curl https://get.wasmer.io -sSfL | sh
|
||||||
|
sudo ln -s ~/.wasmer/bin/wasmer /usr/local/bin
|
||||||
|
|
||||||
- name: Build the V WASM backend
|
- name: Build the V WASM backend
|
||||||
run: ./v -cc clang -showcc -v cmd/tools/builders/wasm_builder.v
|
run: ./v -cc clang -showcc -v cmd/tools/builders/wasm_builder.v
|
||||||
@ -82,8 +84,10 @@ jobs:
|
|||||||
- name: Build V
|
- name: Build V
|
||||||
run: make -j4 && ./v symlink -githubci
|
run: make -j4 && ./v symlink -githubci
|
||||||
|
|
||||||
- name: Install binaryen as build dependency for the V WASM backend
|
- name: Install wasmer to execute WASM modules
|
||||||
run: ./v cmd/tools/install_binaryen.vsh
|
run: |
|
||||||
|
curl https://get.wasmer.io -sSfL | sh
|
||||||
|
sudo ln -s ~/.wasmer/bin/wasmer /usr/local/bin
|
||||||
|
|
||||||
- name: Build the V WASM backend
|
- name: Build the V WASM backend
|
||||||
run: ./v -cc clang -showcc -v cmd/tools/builders/wasm_builder.v
|
run: ./v -cc clang -showcc -v cmd/tools/builders/wasm_builder.v
|
||||||
@ -94,26 +98,26 @@ jobs:
|
|||||||
- name: Build examples
|
- name: Build examples
|
||||||
run: VTEST_ONLY=wasm ./v build-examples
|
run: VTEST_ONLY=wasm ./v build-examples
|
||||||
|
|
||||||
wasm-backend-windows:
|
# wasm-backend-windows:
|
||||||
runs-on: windows-2022
|
# runs-on: windows-2022
|
||||||
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
|
# if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
|
||||||
timeout-minutes: 121
|
# timeout-minutes: 121
|
||||||
steps:
|
# steps:
|
||||||
- uses: actions/checkout@v3
|
# - uses: actions/checkout@v3
|
||||||
|
#
|
||||||
- name: Build V
|
# - name: Build V
|
||||||
run: .\make.bat -msvc
|
# run: .\make.bat -msvc
|
||||||
- name: Symlink V
|
# - name: Symlink V
|
||||||
run: .\v.exe symlink -githubci
|
# run: .\v.exe symlink -githubci
|
||||||
|
#
|
||||||
- name: Install binaryen as build dependency for the V WASM backend
|
# - name: Install binaryen as build dependency for the V WASM backend
|
||||||
run: v cmd/tools/install_binaryen.vsh
|
# run: v cmd/tools/install_binaryen.vsh
|
||||||
|
#
|
||||||
- name: Build the V WASM backend
|
# - name: Build the V WASM backend
|
||||||
run: v -cc msvc -showcc -v cmd/tools/builders/wasm_builder.v
|
# run: v -cc msvc -showcc -v cmd/tools/builders/wasm_builder.v
|
||||||
|
#
|
||||||
- name: Test the WASM backend
|
# - name: Test the WASM backend
|
||||||
run: v -stats test vlib/v/gen/wasm/tests/
|
# run: v -stats test vlib/v/gen/wasm/tests/
|
||||||
|
#
|
||||||
- name: Build examples
|
# - name: Build examples
|
||||||
run: $env:VTEST_ONLY='wasm'; v build-examples
|
# run: $env:VTEST_ONLY='wasm'; v build-examples
|
||||||
|
73
.github/workflows/wasm_wabt_tests_ci.yml
vendored
73
.github/workflows/wasm_wabt_tests_ci.yml
vendored
@ -1,73 +0,0 @@
|
|||||||
name: wasm wabt validate tests CI
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
paths:
|
|
||||||
- '!**'
|
|
||||||
- '!**.md'
|
|
||||||
- 'vlib/builtin/**.v'
|
|
||||||
- 'vlib/wasm/**.v'
|
|
||||||
- 'vlib/wasm/tests/**.v'
|
|
||||||
pull_request:
|
|
||||||
paths:
|
|
||||||
- '!**'
|
|
||||||
- '!**.md'
|
|
||||||
- 'vlib/builtin/**.v'
|
|
||||||
- 'vlib/wasm/**.v'
|
|
||||||
- 'vlib/wasm/tests/**.v'
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: wasm-wabt-ci-${{ github.event.pull_request.number || github.sha }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
wasm-wabt-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: 31
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Build V
|
|
||||||
run: make && ./v symlink -githubci
|
|
||||||
|
|
||||||
- name: Install wabt to get the wasm-validate executable
|
|
||||||
run: v cmd/tools/install_wabt.vsh
|
|
||||||
|
|
||||||
- name: Test the WASM backend
|
|
||||||
run: v test vlib/wasm/
|
|
||||||
|
|
||||||
wasm-wabt-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: 31
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Build V
|
|
||||||
run: make && ./v symlink -githubci
|
|
||||||
|
|
||||||
- name: Install wabt to get the wasm-validate executable
|
|
||||||
run: v cmd/tools/install_wabt.vsh
|
|
||||||
|
|
||||||
- name: Test the WASM backend
|
|
||||||
run: v test vlib/wasm/
|
|
||||||
|
|
||||||
wasm-wabt-windows:
|
|
||||||
runs-on: windows-2022
|
|
||||||
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
|
|
||||||
timeout-minutes: 31
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Build V
|
|
||||||
run: .\make.bat -msvc
|
|
||||||
|
|
||||||
- name: Symlink V
|
|
||||||
run: .\v.exe symlink -githubci
|
|
||||||
|
|
||||||
- name: Install wabt to get the wasm-validate executable
|
|
||||||
run: v cmd/tools/install_wabt.vsh
|
|
||||||
|
|
||||||
- name: Test the WASM backend
|
|
||||||
run: v test vlib/wasm/
|
|
0
cmd/tools/install_wabt.vsh
Normal file → Executable file
0
cmd/tools/install_wabt.vsh
Normal file → Executable file
11
cmd/v/v.v
11
cmd/v/v.v
@ -193,18 +193,7 @@ fn rebuild(prefs &pref.Preferences) {
|
|||||||
util.launch_tool(prefs.is_verbose, 'builders/golang_builder', os.args[1..])
|
util.launch_tool(prefs.is_verbose, 'builders/golang_builder', os.args[1..])
|
||||||
}
|
}
|
||||||
.wasm {
|
.wasm {
|
||||||
assert_wasm_backend_thirdparty()
|
|
||||||
util.launch_tool(prefs.is_verbose, 'builders/wasm_builder', os.args[1..])
|
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -6,6 +6,20 @@ fn __memory_grow(size usize) usize
|
|||||||
fn __memory_fill(dest &u8, value isize, size isize)
|
fn __memory_fill(dest &u8, value isize, size isize)
|
||||||
fn __memory_copy(dest &u8, src &u8, size isize)
|
fn __memory_copy(dest &u8, src &u8, size isize)
|
||||||
|
|
||||||
|
// add doc comments for the below functions
|
||||||
|
|
||||||
|
// __reinterpret_f32_u32 converts a `u32` to a `f32` without changing the bit pattern.
|
||||||
|
pub fn __reinterpret_f32_u32(v f32) u32
|
||||||
|
|
||||||
|
// __reinterpret_u32_f32 converts a `f32` to a `u32` without changing the bit pattern.
|
||||||
|
pub fn __reinterpret_u32_f32(v u32) f32
|
||||||
|
|
||||||
|
// __reinterpret_f64_u64 converts a `u64` to a `f64` without changing the bit pattern.
|
||||||
|
pub fn __reinterpret_f64_u64(v f64) u64
|
||||||
|
|
||||||
|
// __reinterpret_u64_f64 converts a `f64` to a `u64` without changing the bit pattern.
|
||||||
|
pub fn __reinterpret_u64_f64(v u64) f64
|
||||||
|
|
||||||
// vcalloc dynamically allocates a zeroed `n` bytes block of memory on the heap.
|
// 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.
|
// vcalloc returns a `byteptr` pointing to the memory address of the allocated space.
|
||||||
// Unlike `v_calloc` vcalloc checks for negative values given in `n`.
|
// Unlike `v_calloc` vcalloc checks for negative values given in `n`.
|
||||||
|
@ -7,7 +7,7 @@ pub fn print(s string) {
|
|||||||
len: usize(s.len)
|
len: usize(s.len)
|
||||||
}
|
}
|
||||||
|
|
||||||
WASM.fd_write(1, &elm, 1, -1)
|
WASM.fd_write(1, &elm, 1, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// println prints a message with a line end, to stdout.
|
// println prints a message with a line end, to stdout.
|
||||||
@ -20,7 +20,7 @@ pub fn println(s string) {
|
|||||||
len: 1
|
len: 1
|
||||||
}]!
|
}]!
|
||||||
|
|
||||||
WASM.fd_write(1, &elm[0], 2, -1)
|
WASM.fd_write(1, &elm[0], 2, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// eprint prints a message to stderr.
|
// eprint prints a message to stderr.
|
||||||
@ -30,7 +30,7 @@ pub fn eprint(s string) {
|
|||||||
len: usize(s.len)
|
len: usize(s.len)
|
||||||
}
|
}
|
||||||
|
|
||||||
WASM.fd_write(2, &elm, 1, -1)
|
WASM.fd_write(2, &elm, 1, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// eprintln prints a message with a line end, to stderr.
|
// eprintln prints a message with a line end, to stderr.
|
||||||
@ -43,7 +43,7 @@ pub fn eprintln(s string) {
|
|||||||
len: 1
|
len: 1
|
||||||
}]!
|
}]!
|
||||||
|
|
||||||
WASM.fd_write(2, &elm[0], 2, -1)
|
WASM.fd_write(2, &elm[0], 2, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// exit terminates execution immediately and returns exit `code` to the shell.
|
// exit terminates execution immediately and returns exit `code` to the shell.
|
||||||
@ -57,6 +57,5 @@ pub fn exit(code int) {
|
|||||||
pub fn panic(s string) {
|
pub fn panic(s string) {
|
||||||
eprint('V panic: ')
|
eprint('V panic: ')
|
||||||
eprintln(s)
|
eprintln(s)
|
||||||
_ := *&u8(-1)
|
|
||||||
exit(1)
|
exit(1)
|
||||||
}
|
}
|
||||||
|
@ -52,21 +52,18 @@ fn (nn int) str_l(max int) string {
|
|||||||
}
|
}
|
||||||
diff := max - index
|
diff := max - index
|
||||||
vmemmove(buf, voidptr(buf + index), diff + 1)
|
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(buf, diff)
|
||||||
|
|
||||||
// return tos(memdup(&buf[0] + index, (max - index)), (max - index))
|
// return tos(memdup(&buf[0] + index, (max - index)), (max - index))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// str returns the value of the `u8` as a `string`.
|
||||||
|
// Example: assert u8(2).str() == '2'
|
||||||
|
pub fn (n u8) str() string {
|
||||||
|
return int(n).str_l(5)
|
||||||
|
}
|
||||||
|
|
||||||
// str returns the value of the `i8` as a `string`.
|
// str returns the value of the `i8` as a `string`.
|
||||||
// Example: assert i8(-2).str() == '-2'
|
// Example: assert i8(-2).str() == '-2'
|
||||||
pub fn (n i8) str() string {
|
pub fn (n i8) str() string {
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
[wasm_import_namespace: 'wasi_snapshot_preview1']
|
|
||||||
module builtin
|
module builtin
|
||||||
|
|
||||||
struct CIOVec {
|
struct CIOVec {
|
||||||
@ -9,6 +8,9 @@ struct CIOVec {
|
|||||||
type Errno = u16
|
type Errno = u16
|
||||||
type FileDesc = int
|
type FileDesc = int
|
||||||
|
|
||||||
|
[wasm_import_namespace: 'wasi_snapshot_preview1']
|
||||||
fn WASM.fd_write(fd FileDesc, iovs &CIOVec, iovs_len usize, retptr &usize) Errno
|
fn WASM.fd_write(fd FileDesc, iovs &CIOVec, iovs_len usize, retptr &usize) Errno
|
||||||
|
|
||||||
|
[wasm_import_namespace: 'wasi_snapshot_preview1']
|
||||||
[noreturn]
|
[noreturn]
|
||||||
fn WASM.proc_exit(rval int)
|
fn WASM.proc_exit(rval int)
|
||||||
|
@ -82,12 +82,44 @@ fn (mut b Builder) run_compiled_executable_and_exit() {
|
|||||||
if !(b.pref.is_test || b.pref.is_run || b.pref.is_crun) {
|
if !(b.pref.is_test || b.pref.is_run || b.pref.is_crun) {
|
||||||
exit(0)
|
exit(0)
|
||||||
}
|
}
|
||||||
compiled_file := os.real_path(b.pref.out_name)
|
mut compiled_file := b.pref.out_name
|
||||||
|
if b.pref.backend == .wasm && !compiled_file.ends_with('.wasm') {
|
||||||
|
compiled_file += '.wasm'
|
||||||
|
}
|
||||||
|
compiled_file = os.real_path(compiled_file)
|
||||||
|
|
||||||
|
mut run_args := []string{cap: b.pref.run_args.len + 1}
|
||||||
|
|
||||||
run_file := if b.pref.backend.is_js() {
|
run_file := if b.pref.backend.is_js() {
|
||||||
node_basename := $if windows { 'node.exe' } $else { 'node' }
|
node_basename := $if windows { 'node.exe' } $else { 'node' }
|
||||||
os.find_abs_path_of_executable(node_basename) or {
|
os.find_abs_path_of_executable(node_basename) or {
|
||||||
panic('Could not find `${node_basename}` in system path. Do you have Node.js installed?')
|
panic('Could not find `${node_basename}` in system path. Do you have Node.js installed?')
|
||||||
}
|
}
|
||||||
|
} else if b.pref.backend == .wasm {
|
||||||
|
mut actual_run := ['wasmer', 'wasmtime', 'wavm', 'wasm3']
|
||||||
|
mut actual_rf := ''
|
||||||
|
|
||||||
|
// -autofree bug
|
||||||
|
// error: cannot convert 'struct string' to 'struct _option_string'
|
||||||
|
// mut actual_rf := ?string(none)
|
||||||
|
|
||||||
|
for runtime in actual_run {
|
||||||
|
basename := $if windows { runtime + '.exe' } $else { runtime }
|
||||||
|
|
||||||
|
if rf := os.find_abs_path_of_executable(basename) {
|
||||||
|
if basename == 'wavm' {
|
||||||
|
run_args << 'run'
|
||||||
|
}
|
||||||
|
actual_rf = rf
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if actual_rf == '' {
|
||||||
|
panic('Could not find `wasmer`, `wasmtime`, `wavm`, or `wasm3` in system path. Do you have any installed?')
|
||||||
|
}
|
||||||
|
|
||||||
|
actual_rf
|
||||||
} else if b.pref.backend == .golang {
|
} else if b.pref.backend == .golang {
|
||||||
go_basename := $if windows { 'go.exe' } $else { 'go' }
|
go_basename := $if windows { 'go.exe' } $else { 'go' }
|
||||||
os.find_abs_path_of_executable(go_basename) or {
|
os.find_abs_path_of_executable(go_basename) or {
|
||||||
@ -96,8 +128,7 @@ fn (mut b Builder) run_compiled_executable_and_exit() {
|
|||||||
} else {
|
} else {
|
||||||
compiled_file
|
compiled_file
|
||||||
}
|
}
|
||||||
mut run_args := []string{cap: b.pref.run_args.len + 1}
|
if b.pref.backend.is_js() || b.pref.backend == .wasm {
|
||||||
if b.pref.backend.is_js() {
|
|
||||||
run_args << compiled_file
|
run_args << compiled_file
|
||||||
} else if b.pref.backend == .golang {
|
} else if b.pref.backend == .golang {
|
||||||
run_args << ['run', compiled_file]
|
run_args << ['run', compiled_file]
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,91 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,69 +1,73 @@
|
|||||||
|
// Copyright (c) 2023 l-m.dev. All rights reserved.
|
||||||
|
// Use of this source code is governed by an MIT license
|
||||||
|
// that can be found in the LICENSE file.
|
||||||
module wasm
|
module wasm
|
||||||
|
|
||||||
import v.ast
|
import v.ast
|
||||||
import v.token
|
import v.token
|
||||||
import v.gen.wasm.binaryen
|
import wasm
|
||||||
|
|
||||||
const (
|
pub fn (mut g Gen) as_numtype(a wasm.ValType) wasm.NumType {
|
||||||
type_none = binaryen.typenone()
|
if a in [.funcref_t, .externref_t, .v128_t] {
|
||||||
type_auto = binaryen.typeauto()
|
g.w_error("as_numtype: called with '${a}'")
|
||||||
type_i32 = binaryen.typeint32()
|
}
|
||||||
type_i64 = binaryen.typeint64()
|
|
||||||
type_f32 = binaryen.typefloat32()
|
return unsafe { wasm.NumType(a) }
|
||||||
type_f64 = binaryen.typefloat64()
|
}
|
||||||
)
|
|
||||||
|
// unwraps int_literal to i64_t
|
||||||
|
pub fn (mut g Gen) get_wasm_type_int_literal(typ_ ast.Type) wasm.ValType {
|
||||||
|
if typ_ == ast.int_literal_type_idx {
|
||||||
|
return wasm.ValType.i64_t
|
||||||
|
}
|
||||||
|
return g.get_wasm_type(typ_)
|
||||||
|
}
|
||||||
|
|
||||||
// "Register size" types such as int, i64 and bool boil down to their WASM counterparts.
|
// "Register size" types such as int, i64 and bool boil down to their WASM counterparts.
|
||||||
// Structures and unions are pointers, i32.
|
// Structures and unions are pointers, i32.
|
||||||
fn (mut g Gen) get_wasm_type(typ_ ast.Type) binaryen.Type {
|
pub fn (mut g Gen) get_wasm_type(typ_ ast.Type) wasm.ValType {
|
||||||
typ := ast.mktyp(typ_)
|
typ := ast.mktyp(typ_)
|
||||||
if typ == ast.void_type_idx {
|
if typ == ast.void_type_idx {
|
||||||
return wasm.type_none
|
g.w_error("get_wasm_type: called with 'void'")
|
||||||
}
|
}
|
||||||
if typ.is_any_kind_of_pointer() {
|
if typ.is_ptr() || typ.is_pointer() {
|
||||||
g.needs_stack = true
|
return wasm.ValType.i32_t
|
||||||
return wasm.type_i32
|
|
||||||
}
|
}
|
||||||
if typ in ast.number_type_idxs {
|
if typ in ast.number_type_idxs {
|
||||||
return match typ {
|
return match typ {
|
||||||
ast.isize_type_idx, ast.usize_type_idx, ast.i8_type_idx, ast.u8_type_idx,
|
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.char_type_idx, ast.rune_type_idx, ast.i16_type_idx, ast.u16_type_idx,
|
||||||
ast.int_type_idx, ast.u32_type_idx {
|
ast.int_type_idx, ast.u32_type_idx {
|
||||||
wasm.type_i32
|
wasm.ValType.i32_t
|
||||||
}
|
}
|
||||||
ast.i64_type_idx, ast.u64_type_idx, ast.int_literal_type_idx {
|
ast.i64_type_idx, ast.u64_type_idx {
|
||||||
wasm.type_i64
|
wasm.ValType.i64_t
|
||||||
}
|
}
|
||||||
ast.f32_type_idx {
|
ast.f32_type_idx {
|
||||||
wasm.type_f32
|
wasm.ValType.f32_t
|
||||||
}
|
}
|
||||||
ast.f64_type_idx, ast.float_literal_type_idx {
|
ast.f64_type_idx {
|
||||||
wasm.type_f64
|
wasm.ValType.f64_t
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
wasm.type_i32
|
wasm.ValType.i32_t
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if typ == ast.bool_type_idx {
|
if typ == ast.bool_type_idx {
|
||||||
return wasm.type_i32
|
return wasm.ValType.i32_t
|
||||||
}
|
}
|
||||||
ts := g.table.sym(typ)
|
ts := g.table.sym(typ)
|
||||||
match ts.info {
|
match ts.info {
|
||||||
ast.Struct {
|
ast.Struct {
|
||||||
g.get_type_size_align(typ)
|
g.pool.type_size(typ)
|
||||||
return wasm.type_i32 // pointer
|
return wasm.ValType.i32_t // 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 {
|
ast.Alias {
|
||||||
return g.get_wasm_type(ts.info.parent_type)
|
return g.get_wasm_type(ts.info.parent_type)
|
||||||
}
|
}
|
||||||
ast.ArrayFixed {
|
ast.ArrayFixed {
|
||||||
return wasm.type_i32
|
return wasm.ValType.i32_t // pointer
|
||||||
}
|
}
|
||||||
ast.Enum {
|
ast.Enum {
|
||||||
return g.get_wasm_type(ts.info.typ)
|
return g.get_wasm_type(ts.info.typ)
|
||||||
@ -74,251 +78,68 @@ fn (mut g Gen) get_wasm_type(typ_ ast.Type) binaryen.Type {
|
|||||||
g.w_error("get_wasm_type: unreachable type '${*g.table.sym(typ)}' ${ts.info}")
|
g.w_error("get_wasm_type: unreachable type '${*g.table.sym(typ)}' ${ts.info}")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn infix_kind_return_bool(op token.Kind) bool {
|
pub fn (mut g Gen) infix_from_typ(typ ast.Type, op token.Kind) {
|
||||||
return op in [.eq, .ne, .gt, .lt, .ge, .le]
|
if g.is_param_type(typ) {
|
||||||
}
|
eprintln(*g.table.sym(typ))
|
||||||
|
panic('unimplemented')
|
||||||
fn (mut g Gen) infix_from_typ(typ ast.Type, op token.Kind) binaryen.Op {
|
}
|
||||||
wasm_typ := g.get_wasm_type(typ)
|
|
||||||
|
wasm_typ := g.as_numtype(g.get_wasm_type(typ))
|
||||||
match wasm_typ {
|
|
||||||
wasm.type_i32 {
|
match op {
|
||||||
match op {
|
.plus {
|
||||||
.plus {
|
g.func.add(wasm_typ)
|
||||||
return binaryen.addint32()
|
}
|
||||||
}
|
.minus {
|
||||||
.minus {
|
g.func.sub(wasm_typ)
|
||||||
return binaryen.subint32()
|
}
|
||||||
}
|
.mul {
|
||||||
.mul {
|
g.func.mul(wasm_typ)
|
||||||
return binaryen.mulint32()
|
}
|
||||||
}
|
.mod {
|
||||||
.mod {
|
g.func.rem(wasm_typ, typ.is_signed())
|
||||||
if typ.is_signed() {
|
}
|
||||||
return binaryen.remsint32()
|
.div {
|
||||||
} else {
|
g.func.div(wasm_typ, typ.is_signed())
|
||||||
return binaryen.remuint32()
|
}
|
||||||
}
|
.eq {
|
||||||
}
|
g.func.eq(wasm_typ)
|
||||||
.div {
|
}
|
||||||
if typ.is_signed() {
|
.ne {
|
||||||
return binaryen.divsint32()
|
g.func.ne(wasm_typ)
|
||||||
} else {
|
}
|
||||||
return binaryen.divuint32()
|
.gt {
|
||||||
}
|
g.func.gt(wasm_typ, typ.is_signed())
|
||||||
}
|
}
|
||||||
.eq {
|
.lt {
|
||||||
return binaryen.eqint32()
|
g.func.lt(wasm_typ, typ.is_signed())
|
||||||
}
|
}
|
||||||
.ne {
|
.ge {
|
||||||
return binaryen.neint32()
|
g.func.ge(wasm_typ, typ.is_signed())
|
||||||
}
|
}
|
||||||
.gt {
|
.le {
|
||||||
if typ.is_signed() {
|
g.func.le(wasm_typ, typ.is_signed())
|
||||||
return binaryen.gtsint32()
|
}
|
||||||
} else {
|
.xor {
|
||||||
return binaryen.gtuint32()
|
g.func.b_xor(wasm_typ)
|
||||||
}
|
}
|
||||||
}
|
.pipe {
|
||||||
.lt {
|
g.func.b_or(wasm_typ)
|
||||||
if typ.is_signed() {
|
}
|
||||||
return binaryen.ltsint32()
|
.amp {
|
||||||
} else {
|
g.func.b_and(wasm_typ)
|
||||||
return binaryen.ltuint32()
|
}
|
||||||
}
|
.left_shift {
|
||||||
}
|
g.func.b_shl(wasm_typ)
|
||||||
.ge {
|
}
|
||||||
if typ.is_signed() {
|
.right_shift {
|
||||||
return binaryen.gesint32()
|
g.func.b_shr(wasm_typ, true)
|
||||||
} else {
|
}
|
||||||
return binaryen.geuint32()
|
.unsigned_right_shift {
|
||||||
}
|
g.func.b_shr(wasm_typ, false)
|
||||||
}
|
}
|
||||||
.le {
|
else {
|
||||||
if typ.is_signed() {
|
g.w_error('bad infix: op `${op}`')
|
||||||
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}`')
|
|
||||||
}
|
}
|
||||||
|
@ -1,168 +0,0 @@
|
|||||||
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()
|
|
||||||
stack_base := round_up_to_multiple(g.constant_data_offset, 1024)
|
|
||||||
heap_base := if g.needs_stack {
|
|
||||||
stack_base + 1024 * 16 // 16KiB of stack
|
|
||||||
} else {
|
|
||||||
stack_base
|
|
||||||
}
|
|
||||||
pages_needed := heap_base / (1024 * 64) + 1
|
|
||||||
|
|
||||||
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, pages_needed, pages_needed + 4, c'memory', data.data,
|
|
||||||
passive.data, data_offsets.data, data_len.data, data.len, false, false, c'memory')
|
|
||||||
binaryen.addglobal(g.mod, c'__heap_base', type_i32, false, g.literalint(heap_base,
|
|
||||||
ast.int_type))
|
|
||||||
}
|
|
||||||
if g.needs_stack {
|
|
||||||
// `g.constant_data_offset` rounded up to a multiple of 1024
|
|
||||||
binaryen.addglobal(g.mod, c'__vsp', type_i32, true, g.literalint(stack_base, 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)
|
|
||||||
}
|
|
||||||
}
|
|
12
vlib/v/gen/wasm/serialise/alignment_test.v
Normal file
12
vlib/v/gen/wasm/serialise/alignment_test.v
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import v.ast
|
||||||
|
import v.gen.wasm.serialise
|
||||||
|
|
||||||
|
fn test_alignment() {
|
||||||
|
table := ast.new_table()
|
||||||
|
|
||||||
|
mut pool := serialise.new_pool(table)
|
||||||
|
pool.append(ast.BoolLiteral{ val: true }, 0) // +0, +1
|
||||||
|
pool.append(ast.FloatLiteral{ val: '0' }, ast.f32_type) // +3, +4
|
||||||
|
pool.append(ast.BoolLiteral{ val: true }, 0) // +0, +1
|
||||||
|
assert pool.buf.len == 9
|
||||||
|
}
|
449
vlib/v/gen/wasm/serialise/serialise.v
Normal file
449
vlib/v/gen/wasm/serialise/serialise.v
Normal file
@ -0,0 +1,449 @@
|
|||||||
|
// Copyright (c) 2023 l-m.dev. All rights reserved.
|
||||||
|
// Use of this source code is governed by an MIT license
|
||||||
|
// that can be found in the LICENSE file.
|
||||||
|
module serialise
|
||||||
|
|
||||||
|
import v.ast
|
||||||
|
// import v.eval
|
||||||
|
import math.bits
|
||||||
|
import strconv
|
||||||
|
|
||||||
|
[noinit]
|
||||||
|
pub struct Pool {
|
||||||
|
mut:
|
||||||
|
table &ast.Table
|
||||||
|
// eval eval.Eval
|
||||||
|
structs map[ast.Type]StructInfo
|
||||||
|
strings []StringInfo // string intern
|
||||||
|
pub:
|
||||||
|
null_terminated bool
|
||||||
|
intern_strings bool
|
||||||
|
store_relocs bool
|
||||||
|
pub mut:
|
||||||
|
buf []u8
|
||||||
|
relocs []Reloc
|
||||||
|
highest_alignment int
|
||||||
|
}
|
||||||
|
|
||||||
|
struct StringInfo {
|
||||||
|
pos int
|
||||||
|
len int
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct StructInfo {
|
||||||
|
pub mut:
|
||||||
|
offsets []int
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Reloc {
|
||||||
|
pub:
|
||||||
|
pos int
|
||||||
|
offset int
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (mut p Pool) type_struct_info(typ ast.Type) ?StructInfo {
|
||||||
|
ts := p.table.sym(typ)
|
||||||
|
|
||||||
|
if ts.info !is ast.Struct {
|
||||||
|
return none
|
||||||
|
}
|
||||||
|
|
||||||
|
if typ.idx() in p.structs {
|
||||||
|
return p.structs[typ.idx()]
|
||||||
|
}
|
||||||
|
|
||||||
|
// will cache inside `p.structs`
|
||||||
|
p.type_size(typ)
|
||||||
|
return p.structs[typ.idx()]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (mut p Pool) type_size(typ ast.Type) (int, int) {
|
||||||
|
ts := p.table.sym(typ)
|
||||||
|
if ts.size != -1 && typ.idx() in p.structs {
|
||||||
|
return ts.size, ts.align
|
||||||
|
}
|
||||||
|
|
||||||
|
if ts.info is ast.Enum {
|
||||||
|
return p.table.type_size(ts.info.typ)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ts.info !is ast.Struct {
|
||||||
|
return p.table.type_size(typ)
|
||||||
|
}
|
||||||
|
|
||||||
|
ti := ts.info as ast.Struct
|
||||||
|
|
||||||
|
// code borrowed from native, inserted in wasm, and now here!
|
||||||
|
|
||||||
|
mut strc := StructInfo{}
|
||||||
|
mut size := 0
|
||||||
|
mut align := 1
|
||||||
|
for f in ti.fields {
|
||||||
|
f_size, f_align := p.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
|
||||||
|
p.structs[typ.idx()] = strc
|
||||||
|
|
||||||
|
mut ts_ := p.table.sym(typ)
|
||||||
|
ts_.size = size
|
||||||
|
ts_.align = align
|
||||||
|
|
||||||
|
return size, align
|
||||||
|
}
|
||||||
|
|
||||||
|
[params]
|
||||||
|
pub struct PoolOpts {
|
||||||
|
null_terminated bool = true
|
||||||
|
intern_strings bool = true
|
||||||
|
store_relocs bool = true
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_pool(table &ast.Table, opts PoolOpts) Pool {
|
||||||
|
return Pool{
|
||||||
|
table: table
|
||||||
|
null_terminated: opts.null_terminated
|
||||||
|
intern_strings: opts.intern_strings
|
||||||
|
store_relocs: opts.store_relocs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut p Pool) zero_fill(size int) {
|
||||||
|
// TODO: eventually support a way to utilise a BSS section
|
||||||
|
|
||||||
|
for i := 0; i < size; i++ {
|
||||||
|
p.buf << 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut p Pool) alignment(align int) int {
|
||||||
|
if align > p.highest_alignment {
|
||||||
|
p.highest_alignment = align
|
||||||
|
}
|
||||||
|
padding := (align - p.buf.len % align) % align
|
||||||
|
p.zero_fill(padding)
|
||||||
|
pos := p.buf.len
|
||||||
|
return pos
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
fn (mut p Pool) append_struct(init ast.StructInit) ?int {
|
||||||
|
old_len := p.buf.len
|
||||||
|
|
||||||
|
size, align := p.type_size(v.typ)
|
||||||
|
ts := g.table.sym(v.typ)
|
||||||
|
ts_info := ts.info as ast.Struct
|
||||||
|
|
||||||
|
pos := p.alignment(align)
|
||||||
|
|
||||||
|
if init.fields.len == 0 && !(ts_info.fields.any(it.has_default_expr)) {
|
||||||
|
for i := 0 ; i < size ; i++ {
|
||||||
|
p.buf << 0
|
||||||
|
}
|
||||||
|
return pos
|
||||||
|
}
|
||||||
|
|
||||||
|
/* for i, f in ts_info.fields {
|
||||||
|
field_to_be_set := init.fields.map(it.name).filter(f.name)
|
||||||
|
} */
|
||||||
|
|
||||||
|
/* for i, f in ts_info.fields {
|
||||||
|
field_to_be_set := init.fields.map(it.name).contains(f.name)
|
||||||
|
|
||||||
|
if !field_to_be_set {
|
||||||
|
offset := g.structs[v.typ.idx()].offsets[i]
|
||||||
|
offset_var := g.offset(v, f.typ, offset)
|
||||||
|
|
||||||
|
fsize, _ := g.get_type_size_align(f.typ)
|
||||||
|
|
||||||
|
if f.has_default_expr {
|
||||||
|
g.expr(f.default_expr, f.typ)
|
||||||
|
g.set(offset_var)
|
||||||
|
} else {
|
||||||
|
g.zero_fill(offset_var, fsize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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[v.typ.idx()].offsets[field.i]
|
||||||
|
offset_var := g.offset(v, f.expected_type, offset)
|
||||||
|
|
||||||
|
g.expr(f.expr, f.expected_type)
|
||||||
|
g.set(offset_var)
|
||||||
|
} */
|
||||||
|
|
||||||
|
return pos
|
||||||
|
}*/
|
||||||
|
|
||||||
|
pub fn eval_escape_codes_raw(str string) !string {
|
||||||
|
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 {
|
||||||
|
return error('invalid \\u escape code (${str[i..i + 4]})')
|
||||||
|
}
|
||||||
|
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 {
|
||||||
|
return error('invalid \\x escape code (${str[i..i + 2]})')
|
||||||
|
}
|
||||||
|
i += 2
|
||||||
|
buffer << u8(c)
|
||||||
|
}
|
||||||
|
`0`...`7` {
|
||||||
|
c := strconv.parse_int(str[i..i + 3], 8, 8) or {
|
||||||
|
return error('invalid escape code \\${str[i..i + 3]}')
|
||||||
|
}
|
||||||
|
i += 3
|
||||||
|
buffer << u8(c)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return error('invalid escape code \\${str[i]}')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer.bytestr()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn eval_escape_codes(str_lit ast.StringLiteral) !string {
|
||||||
|
if str_lit.is_raw {
|
||||||
|
return str_lit.val
|
||||||
|
}
|
||||||
|
|
||||||
|
return eval_escape_codes_raw(str_lit.val)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (mut p Pool) append_string(val string) int {
|
||||||
|
data := val.bytes()
|
||||||
|
|
||||||
|
if p.intern_strings {
|
||||||
|
for str in p.strings {
|
||||||
|
if data.len > str.len || (p.null_terminated && data.len != str.len) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: aggressive string interning if `p.null_terminated`
|
||||||
|
if p.buf[str.pos..str.pos + data.len] == data {
|
||||||
|
return str.pos
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pos := p.buf.len
|
||||||
|
p.buf << data
|
||||||
|
if p.null_terminated {
|
||||||
|
p.buf << 0
|
||||||
|
}
|
||||||
|
|
||||||
|
p.strings << StringInfo{
|
||||||
|
pos: pos
|
||||||
|
len: data.len
|
||||||
|
}
|
||||||
|
|
||||||
|
return pos
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (mut p Pool) append(init ast.Expr, typ ast.Type) (int, bool) {
|
||||||
|
match init {
|
||||||
|
ast.BoolLiteral {
|
||||||
|
pos := p.buf.len
|
||||||
|
p.buf << u8(init.val)
|
||||||
|
return pos, true
|
||||||
|
}
|
||||||
|
ast.FloatLiteral {
|
||||||
|
assert typ.is_pure_float()
|
||||||
|
|
||||||
|
mut pos := 0
|
||||||
|
if typ == ast.f32_type {
|
||||||
|
pos = p.alignment(4)
|
||||||
|
p.u32(bits.f32_bits(init.val.f32()))
|
||||||
|
} else {
|
||||||
|
pos = p.alignment(8)
|
||||||
|
p.u64(bits.f64_bits(init.val.f64()))
|
||||||
|
}
|
||||||
|
|
||||||
|
return pos, true
|
||||||
|
}
|
||||||
|
ast.IntegerLiteral {
|
||||||
|
assert typ.is_pure_int()
|
||||||
|
|
||||||
|
size, align := p.table.type_size(typ)
|
||||||
|
pos := p.alignment(align)
|
||||||
|
|
||||||
|
match size {
|
||||||
|
1 {
|
||||||
|
p.u8(u8(init.val.i8()))
|
||||||
|
}
|
||||||
|
2 {
|
||||||
|
p.u16(u16(init.val.i16()))
|
||||||
|
}
|
||||||
|
4 {
|
||||||
|
p.u32(u32(init.val.int()))
|
||||||
|
}
|
||||||
|
8 {
|
||||||
|
p.u64(u64(init.val.i64()))
|
||||||
|
}
|
||||||
|
else {}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pos, true
|
||||||
|
}
|
||||||
|
ast.CharLiteral {
|
||||||
|
// 3 extra bytes for improved program correctness, thank me later
|
||||||
|
rne := u32(eval_escape_codes_raw(init.val) or { panic('Pool.append: ${err}') }.runes()[0])
|
||||||
|
pos := p.alignment(4)
|
||||||
|
p.u32(rne)
|
||||||
|
|
||||||
|
return pos, true
|
||||||
|
}
|
||||||
|
ast.StringLiteral {
|
||||||
|
val := eval_escape_codes(init) or { panic('Pool.append: ${err}') }
|
||||||
|
str_pos := p.append_string(val)
|
||||||
|
|
||||||
|
if typ != ast.string_type {
|
||||||
|
// c'str'
|
||||||
|
return str_pos, true
|
||||||
|
}
|
||||||
|
|
||||||
|
_, align := p.type_size(ast.string_type)
|
||||||
|
tss := p.table.sym(ast.string_type).info as ast.Struct
|
||||||
|
pos := p.alignment(align)
|
||||||
|
|
||||||
|
for field in tss.fields {
|
||||||
|
match field.name {
|
||||||
|
'str' {
|
||||||
|
p.ptr(str_pos)
|
||||||
|
}
|
||||||
|
'len' {
|
||||||
|
p.u32(u32(val.len))
|
||||||
|
}
|
||||||
|
'is_lit' {
|
||||||
|
p.u32(1)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
panic('ast.string: field `${field.name}` is unknown')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pos, true
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
size, align := p.type_size(typ)
|
||||||
|
pos := p.alignment(align)
|
||||||
|
p.zero_fill(size)
|
||||||
|
return pos, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut p Pool) u64(v u64) {
|
||||||
|
p.buf << u8(v)
|
||||||
|
p.buf << u8(v >> u64(8))
|
||||||
|
p.buf << u8(v >> u64(16))
|
||||||
|
p.buf << u8(v >> u64(24))
|
||||||
|
p.buf << u8(v >> u64(32))
|
||||||
|
p.buf << u8(v >> u64(40))
|
||||||
|
p.buf << u8(v >> u64(48))
|
||||||
|
p.buf << u8(v >> u64(56))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut p Pool) u32(v u32) {
|
||||||
|
p.buf << u8(v)
|
||||||
|
p.buf << u8(v >> u32(8))
|
||||||
|
p.buf << u8(v >> u32(16))
|
||||||
|
p.buf << u8(v >> u32(24))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut p Pool) u16(v u16) {
|
||||||
|
p.buf << u8(v)
|
||||||
|
p.buf << u8(v >> u32(8))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut p Pool) u8(v u8) {
|
||||||
|
p.buf << v
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut p Pool) ptr(offset int) int {
|
||||||
|
assert p.table.pointer_size in [1, 2, 4, 8]
|
||||||
|
pos := p.buf.len // p.alignment(p.table.pointer_size)
|
||||||
|
|
||||||
|
if p.store_relocs {
|
||||||
|
p.relocs << Reloc{
|
||||||
|
pos: pos
|
||||||
|
offset: offset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match p.table.pointer_size {
|
||||||
|
1 {
|
||||||
|
p.u8(u8(offset))
|
||||||
|
}
|
||||||
|
2 {
|
||||||
|
p.u16(u16(offset))
|
||||||
|
}
|
||||||
|
4 {
|
||||||
|
p.u32(u32(offset))
|
||||||
|
}
|
||||||
|
8 {
|
||||||
|
p.u64(u64(offset))
|
||||||
|
}
|
||||||
|
else {}
|
||||||
|
}
|
||||||
|
return pos
|
||||||
|
}
|
@ -84,3 +84,60 @@ pub fn powi(a i64, b i64) i64 {
|
|||||||
|
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn sqrti(a i64) i64 {
|
||||||
|
mut x := a
|
||||||
|
mut q, mut r := i64(1), i64(0)
|
||||||
|
for ; q <= x; {
|
||||||
|
q <<= 2
|
||||||
|
}
|
||||||
|
for ; q > 1; {
|
||||||
|
q >>= 2
|
||||||
|
t := x - r - q
|
||||||
|
r >>= 1
|
||||||
|
if t >= 0 {
|
||||||
|
x = t
|
||||||
|
r += q
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println('--- test printing numbers')
|
||||||
|
println(-20)
|
||||||
|
println(0)
|
||||||
|
println(22)
|
||||||
|
println(-1000000022)
|
||||||
|
|
||||||
|
println('--- test powi')
|
||||||
|
println(powi(2, 62))
|
||||||
|
println(powi(0, -2))
|
||||||
|
println(powi(2, -1))
|
||||||
|
|
||||||
|
println('--- test sqrti')
|
||||||
|
println(sqrti(i64(123456789) * i64(123456789)))
|
||||||
|
println(sqrti(144))
|
||||||
|
println(sqrti(0))
|
||||||
|
|
||||||
|
println('--- test negate')
|
||||||
|
println(negate(20))
|
||||||
|
println(negate(-1))
|
||||||
|
|
||||||
|
println('--- test inc')
|
||||||
|
println(inc(20))
|
||||||
|
println(inc(-1))
|
||||||
|
|
||||||
|
println('--- test lcm')
|
||||||
|
println(lcm(2, 3))
|
||||||
|
println(lcm(-2, 3))
|
||||||
|
println(lcm(-2, -3))
|
||||||
|
println(lcm(0, 0))
|
||||||
|
|
||||||
|
println('--- test gcd')
|
||||||
|
|
||||||
|
println(gcd(6, 9))
|
||||||
|
println(gcd(6, -9))
|
||||||
|
println(gcd(-6, -9))
|
||||||
|
println(gcd(0, 0))
|
||||||
|
}
|
||||||
|
29
vlib/v/gen/wasm/tests/arith.vv.out
Normal file
29
vlib/v/gen/wasm/tests/arith.vv.out
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
--- test printing numbers
|
||||||
|
-20
|
||||||
|
0
|
||||||
|
22
|
||||||
|
-1000000022
|
||||||
|
--- test powi
|
||||||
|
4611686018427387904
|
||||||
|
-1
|
||||||
|
0
|
||||||
|
--- test sqrti
|
||||||
|
123456789
|
||||||
|
12
|
||||||
|
0
|
||||||
|
--- test negate
|
||||||
|
-20
|
||||||
|
1
|
||||||
|
--- test inc
|
||||||
|
20
|
||||||
|
-1
|
||||||
|
--- test lcm
|
||||||
|
6
|
||||||
|
6
|
||||||
|
6
|
||||||
|
0
|
||||||
|
--- test gcd
|
||||||
|
3
|
||||||
|
3
|
||||||
|
3
|
||||||
|
0
|
@ -3,12 +3,14 @@ struct TEST {
|
|||||||
b i64
|
b i64
|
||||||
}
|
}
|
||||||
|
|
||||||
fn static_arrays() {
|
fn static_arrays() (int, int, i64) {
|
||||||
a := [8]int{}
|
a := [8]int{}
|
||||||
b := [10, 12, 150]!
|
b := [10, 12, 150]!
|
||||||
c := [TEST{}, TEST{
|
c := [TEST{}, TEST{
|
||||||
b: 10
|
b: 10
|
||||||
}]!
|
}]!
|
||||||
|
|
||||||
|
return a[2], b[1], c[1].b
|
||||||
}
|
}
|
||||||
|
|
||||||
fn index_expression() {
|
fn index_expression() {
|
||||||
@ -16,7 +18,11 @@ fn index_expression() {
|
|||||||
|
|
||||||
a := b[2]
|
a := b[2]
|
||||||
c := 'hello'[4]
|
c := 'hello'[4]
|
||||||
d := c'hello'[2]
|
d := unsafe { c'hello'[2] }
|
||||||
|
|
||||||
|
println(a)
|
||||||
|
println(c)
|
||||||
|
println(d)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_this(index int) int {
|
fn test_this(index int) int {
|
||||||
@ -31,9 +37,31 @@ struct AA {
|
|||||||
a [10]&int
|
a [10]&int
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn test_stuff() &int {
|
||||||
a := AA{}
|
a := AA{}
|
||||||
|
|
||||||
mut b := &int(0)
|
mut b := &int(0)
|
||||||
b = a.a[2]
|
b = a.a[2]
|
||||||
|
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println('--- static_arrays()')
|
||||||
|
a, b, c := static_arrays()
|
||||||
|
println(a)
|
||||||
|
println(b)
|
||||||
|
println(c)
|
||||||
|
|
||||||
|
println('--- index_expression()')
|
||||||
|
index_expression()
|
||||||
|
|
||||||
|
println('--- test_this()')
|
||||||
|
println(test_this(2))
|
||||||
|
println(test_this(10))
|
||||||
|
println(test_this(-1))
|
||||||
|
|
||||||
|
println('--- test_stuff()')
|
||||||
|
v := test_stuff()
|
||||||
|
println(v)
|
||||||
}
|
}
|
||||||
|
14
vlib/v/gen/wasm/tests/arrays.vv.out
Normal file
14
vlib/v/gen/wasm/tests/arrays.vv.out
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
--- static_arrays()
|
||||||
|
0
|
||||||
|
12
|
||||||
|
10
|
||||||
|
--- index_expression()
|
||||||
|
150
|
||||||
|
111
|
||||||
|
108
|
||||||
|
--- test_this()
|
||||||
|
108
|
||||||
|
10
|
||||||
|
10
|
||||||
|
--- test_stuff()
|
||||||
|
0
|
@ -1,7 +1,6 @@
|
|||||||
fn test() {
|
fn test() {
|
||||||
print('hello!')
|
print('hello!')
|
||||||
println('hello!')
|
println('hello!')
|
||||||
panic('nooo!')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn str_methods() {
|
fn str_methods() {
|
||||||
@ -16,3 +15,19 @@ fn str_implicit() {
|
|||||||
a := 100
|
a := 100
|
||||||
println(a + 10)
|
println(a + 10)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn assertions() {
|
||||||
|
assert true, 'hello'
|
||||||
|
assert true
|
||||||
|
|
||||||
|
// assert false, 'no can do'
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
test()
|
||||||
|
str_methods()
|
||||||
|
str_implicit()
|
||||||
|
assertions()
|
||||||
|
|
||||||
|
// panic('nooo!')
|
||||||
|
}
|
||||||
|
6
vlib/v/gen/wasm/tests/builtin.vv.out
Normal file
6
vlib/v/gen/wasm/tests/builtin.vv.out
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
hello!hello!
|
||||||
|
128-192322
|
||||||
|
false
|
||||||
|
false
|
||||||
|
true
|
||||||
|
110
|
@ -59,15 +59,17 @@ fn addcfor() int {
|
|||||||
return val
|
return val
|
||||||
}
|
}
|
||||||
|
|
||||||
fn labelcfor() {
|
fn labelcfor() (int, int) {
|
||||||
|
mut idx := 0
|
||||||
mut val := 0
|
mut val := 0
|
||||||
|
|
||||||
hello: for {
|
hello: for {
|
||||||
for {
|
for {
|
||||||
|
val++
|
||||||
if val == 10 {
|
if val == 10 {
|
||||||
continue hello
|
continue hello
|
||||||
}
|
}
|
||||||
val++
|
idx++
|
||||||
|
|
||||||
if val == 100 {
|
if val == 100 {
|
||||||
break hello
|
break hello
|
||||||
@ -75,6 +77,8 @@ fn labelcfor() {
|
|||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return val, idx
|
||||||
}
|
}
|
||||||
|
|
||||||
fn infcfor() int {
|
fn infcfor() int {
|
||||||
@ -89,3 +93,32 @@ fn infcfor() int {
|
|||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println('--- func()')
|
||||||
|
println(func(10, true))
|
||||||
|
println(func(0, false))
|
||||||
|
println(func(0, true))
|
||||||
|
|
||||||
|
println('--- test()')
|
||||||
|
println(test(true))
|
||||||
|
println(test(false))
|
||||||
|
println(test(true && false))
|
||||||
|
|
||||||
|
println('--- boolfor()')
|
||||||
|
println(boolfor())
|
||||||
|
|
||||||
|
println('--- inffor()')
|
||||||
|
println(inffor())
|
||||||
|
|
||||||
|
println('--- addcfor()')
|
||||||
|
println(addcfor())
|
||||||
|
|
||||||
|
println('--- labelcfor()')
|
||||||
|
a, b := labelcfor()
|
||||||
|
println(a)
|
||||||
|
println(b)
|
||||||
|
|
||||||
|
println('--- infcfor()')
|
||||||
|
println(infcfor())
|
||||||
|
}
|
||||||
|
19
vlib/v/gen/wasm/tests/control_flow.vv.out
Normal file
19
vlib/v/gen/wasm/tests/control_flow.vv.out
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
--- func()
|
||||||
|
10
|
||||||
|
0
|
||||||
|
0
|
||||||
|
--- test()
|
||||||
|
2
|
||||||
|
5
|
||||||
|
5
|
||||||
|
--- boolfor()
|
||||||
|
1
|
||||||
|
--- inffor()
|
||||||
|
1
|
||||||
|
--- addcfor()
|
||||||
|
45
|
||||||
|
--- labelcfor()
|
||||||
|
100
|
||||||
|
99
|
||||||
|
--- infcfor()
|
||||||
|
10
|
@ -6,9 +6,10 @@ enum Hello as u64 {
|
|||||||
e
|
e
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enums() {
|
fn enums() Hello {
|
||||||
mut a := Hello.a
|
mut a := Hello.a
|
||||||
a = .c
|
a = .c
|
||||||
|
return a
|
||||||
}
|
}
|
||||||
|
|
||||||
struct AA {
|
struct AA {
|
||||||
@ -19,6 +20,7 @@ struct AA {
|
|||||||
fn of() {
|
fn of() {
|
||||||
a := __offsetof(AA, b)
|
a := __offsetof(AA, b)
|
||||||
b := sizeof(AA)
|
b := sizeof(AA)
|
||||||
|
_, _ := a, b
|
||||||
}
|
}
|
||||||
|
|
||||||
fn constant() int {
|
fn constant() int {
|
||||||
@ -55,3 +57,35 @@ fn ptr_arith() {
|
|||||||
}
|
}
|
||||||
println((*b).str())
|
println((*b).str())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn defer_if(cond bool) {
|
||||||
|
if cond {
|
||||||
|
defer {
|
||||||
|
println('defer_if: defer!')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println('defer_if: start')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_defer() {
|
||||||
|
defer {
|
||||||
|
println('defer!')
|
||||||
|
}
|
||||||
|
println('before defer')
|
||||||
|
defer_if(true)
|
||||||
|
defer_if(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println('ptr_arith')
|
||||||
|
ptr_arith()
|
||||||
|
run_defer()
|
||||||
|
println('constants')
|
||||||
|
println(runtime_init)
|
||||||
|
println(hello)
|
||||||
|
// println(float)
|
||||||
|
println(integer)
|
||||||
|
println('enums')
|
||||||
|
println(int(enums()))
|
||||||
|
println(sizeof(Hello))
|
||||||
|
}
|
||||||
|
17
vlib/v/gen/wasm/tests/misc.vv.out
Normal file
17
vlib/v/gen/wasm/tests/misc.vv.out
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
ptr_arith
|
||||||
|
12
|
||||||
|
14
|
||||||
|
102
|
||||||
|
before defer
|
||||||
|
defer_if: start
|
||||||
|
defer_if: defer!
|
||||||
|
defer_if: start
|
||||||
|
defer!
|
||||||
|
constants
|
||||||
|
100
|
||||||
|
hello
|
||||||
|
|
||||||
|
888
|
||||||
|
enums
|
||||||
|
30
|
||||||
|
8
|
@ -3,7 +3,7 @@ fn multi(a i16) i64 {
|
|||||||
return one + two
|
return one + two
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn multireturn(a int) (int, f64, i64) {
|
pub fn multireturn(a int) (int, i64, i64) {
|
||||||
return 2, a + 2, 10 - a
|
return 2, a + 2, 10 - a
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -21,10 +21,94 @@ pub fn side_effect() int {
|
|||||||
return 22
|
return 22
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn test_side_effect() int {
|
pub fn run_side_effect() int {
|
||||||
mut a := 15
|
mut a := 15
|
||||||
|
|
||||||
_, a = side_effect(), 10
|
_, a = side_effect(), 10
|
||||||
|
|
||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn run_normal_stuff() {
|
||||||
|
println(multi(2))
|
||||||
|
{
|
||||||
|
a, b, c := multireturn(20)
|
||||||
|
println(a)
|
||||||
|
println(b)
|
||||||
|
println(c)
|
||||||
|
}
|
||||||
|
println(accept())
|
||||||
|
println(run_side_effect())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn if_cond(cond bool) (int, int) {
|
||||||
|
return if cond { 10, 15 } else { 30, 35 }
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AA {
|
||||||
|
a int
|
||||||
|
b int
|
||||||
|
}
|
||||||
|
|
||||||
|
fn take_struct(test AA) int {
|
||||||
|
return test.a
|
||||||
|
}
|
||||||
|
|
||||||
|
fn give_two_struct(cond bool) (AA, AA) {
|
||||||
|
return if cond {
|
||||||
|
AA{
|
||||||
|
a: 15
|
||||||
|
}, AA{
|
||||||
|
a: 15
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
AA{
|
||||||
|
a: 20
|
||||||
|
}, AA{
|
||||||
|
a: 592
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn give_struct() int {
|
||||||
|
return take_struct(AA{ a: 15 })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn if_struct_test(cond bool) AA {
|
||||||
|
a := if cond {
|
||||||
|
AA{}
|
||||||
|
} else {
|
||||||
|
AA{
|
||||||
|
a: 125
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_stmt_exprs() {
|
||||||
|
mut a := 0
|
||||||
|
mut b := 0
|
||||||
|
a, b = if_cond(true)
|
||||||
|
println(true)
|
||||||
|
println(a)
|
||||||
|
println(b)
|
||||||
|
a, b = if_cond(false)
|
||||||
|
println(false)
|
||||||
|
println(a)
|
||||||
|
println(b)
|
||||||
|
println(give_struct())
|
||||||
|
mut struc := if_struct_test(false)
|
||||||
|
println(struc.a)
|
||||||
|
struc = if_struct_test(true)
|
||||||
|
println(struc.a)
|
||||||
|
c, d := give_two_struct(false)
|
||||||
|
println(c.a)
|
||||||
|
println(d.a)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println('--- normal multi exprs')
|
||||||
|
run_normal_stuff()
|
||||||
|
println('--- struct based multi exprs/stmt exprs')
|
||||||
|
run_stmt_exprs()
|
||||||
|
}
|
||||||
|
19
vlib/v/gen/wasm/tests/multi_expr.vv.out
Normal file
19
vlib/v/gen/wasm/tests/multi_expr.vv.out
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
--- normal multi exprs
|
||||||
|
12
|
||||||
|
2
|
||||||
|
22
|
||||||
|
-10
|
||||||
|
45
|
||||||
|
10
|
||||||
|
--- struct based multi exprs/stmt exprs
|
||||||
|
true
|
||||||
|
10
|
||||||
|
15
|
||||||
|
false
|
||||||
|
30
|
||||||
|
35
|
||||||
|
15
|
||||||
|
125
|
||||||
|
0
|
||||||
|
20
|
||||||
|
592
|
@ -142,7 +142,7 @@ struct Hello {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn recurse() {
|
pub fn recurse() {
|
||||||
a := Hello{}
|
_ := Hello{}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DD {
|
struct DD {
|
||||||
@ -177,3 +177,25 @@ fn postfix_test() {
|
|||||||
fn postfix_test_mut(mut a TEST) {
|
fn postfix_test_mut(mut a TEST) {
|
||||||
a.b++
|
a.b++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
zeroed()
|
||||||
|
field()
|
||||||
|
println(selector(0))
|
||||||
|
println(reassign(10))
|
||||||
|
println(give(42))
|
||||||
|
println(return_make(123))
|
||||||
|
accept()
|
||||||
|
println(accept_multi(5))
|
||||||
|
x, y := test(2, 3)
|
||||||
|
println(x)
|
||||||
|
println(y)
|
||||||
|
recurse()
|
||||||
|
z, w := valer()
|
||||||
|
println(z)
|
||||||
|
println(w)
|
||||||
|
postfix_test()
|
||||||
|
mut a := TEST{}
|
||||||
|
postfix_test_mut(mut a)
|
||||||
|
println(a.b)
|
||||||
|
}
|
||||||
|
10
vlib/v/gen/wasm/tests/structs.vv.out
Normal file
10
vlib/v/gen/wasm/tests/structs.vv.out
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
32
|
||||||
|
20
|
||||||
|
42
|
||||||
|
123
|
||||||
|
10
|
||||||
|
12
|
||||||
|
8
|
||||||
|
20
|
||||||
|
30
|
||||||
|
1
|
@ -6,6 +6,23 @@ 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
|
// TODO some logic copy pasted from valgrind_test.v and compiler_test.v, move to a module
|
||||||
fn test_wasm() {
|
fn test_wasm() {
|
||||||
|
mut runtimes := ['wasmer', 'wasmtime', 'wavm', 'wasm3']
|
||||||
|
mut runtime_found := false
|
||||||
|
|
||||||
|
for runtime in runtimes {
|
||||||
|
basename := $if windows { runtime + '.exe' } $else { runtime }
|
||||||
|
|
||||||
|
if rf := os.find_abs_path_of_executable(basename) {
|
||||||
|
runtime_found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !runtime_found {
|
||||||
|
eprintln('cannot find suitable wasm runtime, exiting...')
|
||||||
|
exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
mut bench := benchmark.new_benchmark()
|
mut bench := benchmark.new_benchmark()
|
||||||
vexe := os.getenv('VEXE')
|
vexe := os.getenv('VEXE')
|
||||||
vroot := os.dir(vexe)
|
vroot := os.dir(vexe)
|
||||||
@ -33,7 +50,7 @@ fn test_wasm() {
|
|||||||
tmperrfile := '${dir}/${test}.tmperr'
|
tmperrfile := '${dir}/${test}.tmperr'
|
||||||
outfile := '${dir}/${test}.out'
|
outfile := '${dir}/${test}.out'
|
||||||
// force binaryen to print without colour
|
// 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)}'
|
cmd := '${os.quoted_path(vexe)} -b wasm run ${os.quoted_path(full_test_path)} 2> ${os.quoted_path(tmperrfile)}'
|
||||||
if is_verbose {
|
if is_verbose {
|
||||||
println(cmd)
|
println(cmd)
|
||||||
}
|
}
|
||||||
@ -65,6 +82,9 @@ fn test_wasm() {
|
|||||||
bench.fail()
|
bench.fail()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
os.write_file(outfile, res_wasm.output.trim_right('\r\n').replace('\r\n',
|
||||||
|
'\n'))!
|
||||||
}
|
}
|
||||||
bench.ok()
|
bench.ok()
|
||||||
eprintln(bench.step_message_ok(relative_test_path))
|
eprintln(bench.step_message_ok(relative_test_path))
|
||||||
|
25
vlib/v/help/build/build-wasm.txt
Normal file
25
vlib/v/help/build/build-wasm.txt
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
This command compiles the given target, along with their dependencies,
|
||||||
|
into an executable using the WebAssembly code generation backend.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
v -b wasm [-options] ['run'] <target.v|target_directory> [run options]
|
||||||
|
|
||||||
|
|
||||||
|
For more general build help, see also `v help build`.
|
||||||
|
|
||||||
|
# Interfacing WebAssembly code generation, passing options to it:
|
||||||
|
-wasm-validate
|
||||||
|
After compiling the WebAssembly module, execute wasm-validate to validate the module.
|
||||||
|
Useful for debugging the compiler.
|
||||||
|
|
||||||
|
-os <browser|wasi>, -target-os <browser|wasi>
|
||||||
|
Change the target WebAssembly execution environment that V compiles for.
|
||||||
|
|
||||||
|
The `wasi` target is the default execution enviroment.
|
||||||
|
When targeting WASI, the generated WebAssembly module can be run in a variety of environments that support the WASI specification.
|
||||||
|
WASI provides a standardized interface to interact with the host operating system, allowing WebAssembly modules to perform tasks like file I/O, networking, and more.
|
||||||
|
The specific version of the WASI specification targeted by V is 'wasi_snapshot_preview1'.
|
||||||
|
|
||||||
|
The `browser` target is an experimental environment that compiles for a stripped down builtin, for use in browsers.
|
||||||
|
The produced WebAssembly module will have functions exported that are `pub` and inside the `module main`.
|
||||||
|
See `examples/wasm/mandelbrot` for an example.
|
@ -45,6 +45,7 @@ NB: the build flags are shared with the run command too:
|
|||||||
* `js_node` - V outputs JS source code to run with nodejs.
|
* `js_node` - V outputs JS source code to run with nodejs.
|
||||||
* `js_freestanding` - V outputs JS source code with no hard runtime dependency.
|
* `js_freestanding` - V outputs JS source code with no hard runtime dependency.
|
||||||
* `native` - V outputs a native executable directly (see -arch x64|arm64 and -os linux|macos) (EXPERIMENTAL).
|
* `native` - V outputs a native executable directly (see -arch x64|arm64 and -os linux|macos) (EXPERIMENTAL).
|
||||||
|
* `wasm` - V outputs a WebAssembly module directly (see -os wasi|browser) (EXPERIMENTAL).
|
||||||
|
|
||||||
-d <flag>[=<value>], -define <flag>[=<value>]
|
-d <flag>[=<value>], -define <flag>[=<value>]
|
||||||
Define the provided flag.
|
Define the provided flag.
|
||||||
@ -209,6 +210,7 @@ NB: the build flags are shared with the run command too:
|
|||||||
For C-specific build flags, use `v help build-c`.
|
For C-specific build flags, use `v help build-c`.
|
||||||
For JS-specific build flags, use `v help build-js`.
|
For JS-specific build flags, use `v help build-js`.
|
||||||
For Native-specific build flags, use `v help build-native`.
|
For Native-specific build flags, use `v help build-native`.
|
||||||
|
For WebAssembly-specific build flags, use `v help build-wasm`.
|
||||||
|
|
||||||
See also:
|
See also:
|
||||||
`v help run` for documentation regarding `v run`.
|
`v help run` for documentation regarding `v run`.
|
||||||
|
@ -228,6 +228,7 @@ pub mut:
|
|||||||
// checker settings:
|
// checker settings:
|
||||||
checker_match_exhaustive_cutoff_limit int = 12
|
checker_match_exhaustive_cutoff_limit int = 12
|
||||||
thread_stack_size int = 8388608 // Change with `-thread-stack-size 4194304`. Note: on macos it was 524288, which is too small for more complex programs with many nested callexprs.
|
thread_stack_size int = 8388608 // Change with `-thread-stack-size 4194304`. Note: on macos it was 524288, which is too small for more complex programs with many nested callexprs.
|
||||||
|
wasm_validate bool // validate webassembly code, by calling `wasm-validate`
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_args(known_external_commands []string, args []string) (&Preferences, string) {
|
pub fn parse_args(known_external_commands []string, args []string) (&Preferences, string) {
|
||||||
@ -303,6 +304,9 @@ pub fn parse_args_and_show_errors(known_external_commands []string, args []strin
|
|||||||
arg := args[i]
|
arg := args[i]
|
||||||
current_args := args[i..].clone()
|
current_args := args[i..].clone()
|
||||||
match arg {
|
match arg {
|
||||||
|
'-wasm-validate' {
|
||||||
|
res.wasm_validate = true
|
||||||
|
}
|
||||||
'-apk' {
|
'-apk' {
|
||||||
res.is_apk = true
|
res.is_apk = true
|
||||||
res.build_options << arg
|
res.build_options << arg
|
||||||
|
@ -666,6 +666,21 @@ pub fn (mut t Transformer) expr(mut node ast.Expr) ast.Expr {
|
|||||||
ast.UnsafeExpr {
|
ast.UnsafeExpr {
|
||||||
node.expr = t.expr(mut node.expr)
|
node.expr = t.expr(mut node.expr)
|
||||||
}
|
}
|
||||||
|
// segfaults with vlib/v/tests/const_fixed_array_containing_references_to_itself_test.v
|
||||||
|
/*
|
||||||
|
ast.Ident {
|
||||||
|
mut obj := node.obj
|
||||||
|
if obj !in [ast.Var, ast.ConstField, ast.GlobalField, ast.AsmRegister] {
|
||||||
|
obj = node.scope.find(node.name) or { return node }
|
||||||
|
}
|
||||||
|
|
||||||
|
match mut obj {
|
||||||
|
ast.ConstField {
|
||||||
|
obj.expr = t.expr(mut obj.expr)
|
||||||
|
}
|
||||||
|
else {}
|
||||||
|
}
|
||||||
|
}*/
|
||||||
else {}
|
else {}
|
||||||
}
|
}
|
||||||
return node
|
return node
|
||||||
|
@ -163,7 +163,7 @@ pub fn launch_tool(is_verbose bool, tool_name string, args []string) {
|
|||||||
for emodule in emodules {
|
for emodule in emodules {
|
||||||
check_module_is_installed(emodule, is_verbose) or { panic(err) }
|
check_module_is_installed(emodule, is_verbose) or { panic(err) }
|
||||||
}
|
}
|
||||||
mut compilation_command := '${os.quoted_path(vexe)} -skip-unused '
|
mut compilation_command := '${os.quoted_path(vexe)} '
|
||||||
if tool_name in ['vself', 'vup', 'vdoctor', 'vsymlink'] {
|
if tool_name in ['vself', 'vup', 'vdoctor', 'vsymlink'] {
|
||||||
// These tools will be called by users in cases where there
|
// These tools will be called by users in cases where there
|
||||||
// is high chance of there being a problem somewhere. Thus
|
// is high chance of there being a problem somewhere. Thus
|
||||||
|
@ -110,26 +110,22 @@ fn (mut mod Module) patch(ft Function) {
|
|||||||
mod.buf << ft.code[ptr..]
|
mod.buf << ft.code[ptr..]
|
||||||
}
|
}
|
||||||
|
|
||||||
// name
|
fn (mut mod Module) name(name string) {
|
||||||
pub fn (mut mod Module) name(name string) {
|
|
||||||
mod.u32(u32(name.len))
|
mod.u32(u32(name.len))
|
||||||
mod.buf << name.bytes()
|
mod.buf << name.bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
// start_subsection
|
fn (mut mod Module) start_subsection(sec Subsection) int {
|
||||||
pub fn (mut mod Module) start_subsection(sec Subsection) int {
|
|
||||||
mod.buf << u8(sec)
|
mod.buf << u8(sec)
|
||||||
return mod.patch_start()
|
return mod.patch_start()
|
||||||
}
|
}
|
||||||
|
|
||||||
// start_section
|
fn (mut mod Module) start_section(sec Section) int {
|
||||||
pub fn (mut mod Module) start_section(sec Section) int {
|
|
||||||
mod.buf << u8(sec)
|
mod.buf << u8(sec)
|
||||||
return mod.patch_start()
|
return mod.patch_start()
|
||||||
}
|
}
|
||||||
|
|
||||||
// end_section
|
fn (mut mod Module) end_section(tpatch int) {
|
||||||
pub fn (mut mod Module) end_section(tpatch int) {
|
|
||||||
mod.patch_len(tpatch)
|
mod.patch_len(tpatch)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,18 +156,14 @@ pub fn (mut mod Module) compile() []u8 {
|
|||||||
{
|
{
|
||||||
mod.u32(u32(mod.fn_imports.len + mod.global_imports.len))
|
mod.u32(u32(mod.fn_imports.len + mod.global_imports.len))
|
||||||
for ft in mod.fn_imports {
|
for ft in mod.fn_imports {
|
||||||
mod.u32(u32(ft.mod.len))
|
mod.name(ft.mod)
|
||||||
mod.buf << ft.mod.bytes()
|
mod.name(ft.name)
|
||||||
mod.u32(u32(ft.name.len))
|
|
||||||
mod.buf << ft.name.bytes()
|
|
||||||
mod.buf << 0x00 // function
|
mod.buf << 0x00 // function
|
||||||
mod.u32(u32(ft.tidx))
|
mod.u32(u32(ft.tidx))
|
||||||
}
|
}
|
||||||
for gt in mod.global_imports {
|
for gt in mod.global_imports {
|
||||||
mod.u32(u32(gt.mod.len))
|
mod.name(gt.mod)
|
||||||
mod.buf << gt.mod.bytes()
|
mod.name(gt.name)
|
||||||
mod.u32(u32(gt.name.len))
|
|
||||||
mod.buf << gt.name.bytes()
|
|
||||||
mod.buf << 0x03 // global
|
mod.buf << 0x03 // global
|
||||||
mod.global_type(gt.typ, gt.is_mut)
|
mod.global_type(gt.typ, gt.is_mut)
|
||||||
}
|
}
|
||||||
@ -244,7 +236,7 @@ pub fn (mut mod Module) compile() []u8 {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
lsz++
|
lsz++
|
||||||
mod.name(ft.name)
|
mod.name(ft.export_name or { ft.name })
|
||||||
mod.buf << 0x00 // function
|
mod.buf << 0x00 // function
|
||||||
mod.u32(u32(ft.idx + mod.fn_imports.len))
|
mod.u32(u32(ft.idx + mod.fn_imports.len))
|
||||||
}
|
}
|
||||||
|
@ -43,4 +43,11 @@ mut:
|
|||||||
locals []FunctionLocal
|
locals []FunctionLocal
|
||||||
pub:
|
pub:
|
||||||
name string
|
name string
|
||||||
|
pub mut:
|
||||||
|
export_name ?string
|
||||||
|
}
|
||||||
|
|
||||||
|
// export_name sets the export name of the function to `name`
|
||||||
|
pub fn (mut func Function) export_name(name string) {
|
||||||
|
func.export_name = name
|
||||||
}
|
}
|
||||||
|
@ -861,6 +861,17 @@ pub fn (mut func Function) cast_trapping(a NumType, is_signed bool, b NumType) {
|
|||||||
func.cast(a, is_signed, b)
|
func.cast(a, is_signed, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// reinterpret returns a value which has the same bit-pattern as its operand value, in its result type.
|
||||||
|
// WebAssembly instruction: `f32.reinterpret_i32`, `i32.reinterpret_f32`, `f64.reinterpret_i64`, `i64.reinterpret_f64`.
|
||||||
|
pub fn (mut func Function) reinterpret(a NumType) {
|
||||||
|
match a {
|
||||||
|
.f32_t { func.code << 0xBC } // i32.reinterpret_f32
|
||||||
|
.i32_t { func.code << 0xBE } // f32.reinterpret_i32
|
||||||
|
.f64_t { func.code << 0xBD } // i64.reinterpret_f64
|
||||||
|
.i64_t { func.code << 0xBF } // f64.reinterpret_i64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// unreachable denotes a point in code that should not be reachable, it is an unconditional trap.
|
// unreachable denotes a point in code that should not be reachable, it is an unconditional trap.
|
||||||
// WebAssembly instruction: `unreachable`.
|
// WebAssembly instruction: `unreachable`.
|
||||||
pub fn (mut func Function) unreachable() {
|
pub fn (mut func Function) unreachable() {
|
||||||
@ -919,7 +930,7 @@ pub fn (mut func Function) c_return() {
|
|||||||
func.code << 0x0F // return
|
func.code << 0x0F // return
|
||||||
}
|
}
|
||||||
|
|
||||||
// c_end ends the block or loop with the label passed in at `label`.
|
// c_end ends the block, loop or if expression with the label passed in at `label`.
|
||||||
pub fn (mut func Function) c_end(label LabelIndex) {
|
pub fn (mut func Function) c_end(label LabelIndex) {
|
||||||
assert func.label == label, 'c_end: called with an invalid label ${label}'
|
assert func.label == label, 'c_end: called with an invalid label ${label}'
|
||||||
func.label--
|
func.label--
|
||||||
@ -945,11 +956,6 @@ pub fn (mut func Function) c_br_if(label LabelIndex) {
|
|||||||
func.u32(u32(v))
|
func.u32(u32(v))
|
||||||
}
|
}
|
||||||
|
|
||||||
// c_end_if closes the current if expression.
|
|
||||||
pub fn (mut func Function) c_end_if() {
|
|
||||||
func.code << 0x0B // END expression opcode
|
|
||||||
}
|
|
||||||
|
|
||||||
// call calls a locally defined function.
|
// call calls a locally defined function.
|
||||||
// If this function does not exist when calling `compile` on the module, it will panic.
|
// If this function does not exist when calling `compile` on the module, it will panic.
|
||||||
// WebAssembly instruction: `call`.
|
// WebAssembly instruction: `call`.
|
||||||
|
Loading…
Reference in New Issue
Block a user