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
|
||||
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: Install wasmer to execute WASM modules
|
||||
run: |
|
||||
curl https://get.wasmer.io -sSfL | sh
|
||||
sudo ln -s ~/.wasmer/bin/wasmer /usr/local/bin
|
||||
|
||||
- name: Build the V WASM backend
|
||||
run: ./v -cc clang -showcc -v cmd/tools/builders/wasm_builder.v
|
||||
@ -82,8 +84,10 @@ jobs:
|
||||
- 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: Install wasmer to execute WASM modules
|
||||
run: |
|
||||
curl https://get.wasmer.io -sSfL | sh
|
||||
sudo ln -s ~/.wasmer/bin/wasmer /usr/local/bin
|
||||
|
||||
- name: Build the V WASM backend
|
||||
run: ./v -cc clang -showcc -v cmd/tools/builders/wasm_builder.v
|
||||
@ -94,26 +98,26 @@ jobs:
|
||||
- name: Build examples
|
||||
run: VTEST_ONLY=wasm ./v build-examples
|
||||
|
||||
wasm-backend-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: 121
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Build V
|
||||
run: .\make.bat -msvc
|
||||
- name: Symlink V
|
||||
run: .\v.exe 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 msvc -showcc -v cmd/tools/builders/wasm_builder.v
|
||||
|
||||
- name: Test the WASM backend
|
||||
run: v -stats test vlib/v/gen/wasm/tests/
|
||||
|
||||
- name: Build examples
|
||||
run: $env:VTEST_ONLY='wasm'; v build-examples
|
||||
# wasm-backend-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: 121
|
||||
# steps:
|
||||
# - uses: actions/checkout@v3
|
||||
#
|
||||
# - name: Build V
|
||||
# run: .\make.bat -msvc
|
||||
# - name: Symlink V
|
||||
# run: .\v.exe 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 msvc -showcc -v cmd/tools/builders/wasm_builder.v
|
||||
#
|
||||
# - name: Test the WASM backend
|
||||
# run: v -stats test vlib/v/gen/wasm/tests/
|
||||
#
|
||||
# - name: 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..])
|
||||
}
|
||||
.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)
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,20 @@ fn __memory_grow(size usize) usize
|
||||
fn __memory_fill(dest &u8, value isize, 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 returns a `byteptr` pointing to the memory address of the allocated space.
|
||||
// Unlike `v_calloc` vcalloc checks for negative values given in `n`.
|
||||
|
@ -7,7 +7,7 @@ pub fn print(s string) {
|
||||
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.
|
||||
@ -20,7 +20,7 @@ pub fn println(s string) {
|
||||
len: 1
|
||||
}]!
|
||||
|
||||
WASM.fd_write(1, &elm[0], 2, -1)
|
||||
WASM.fd_write(1, &elm[0], 2, 0)
|
||||
}
|
||||
|
||||
// eprint prints a message to stderr.
|
||||
@ -30,7 +30,7 @@ pub fn eprint(s string) {
|
||||
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.
|
||||
@ -43,7 +43,7 @@ pub fn eprintln(s string) {
|
||||
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.
|
||||
@ -57,6 +57,5 @@ pub fn exit(code int) {
|
||||
pub fn panic(s string) {
|
||||
eprint('V panic: ')
|
||||
eprintln(s)
|
||||
_ := *&u8(-1)
|
||||
exit(1)
|
||||
}
|
||||
|
@ -52,21 +52,18 @@ fn (nn int) str_l(max int) string {
|
||||
}
|
||||
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 `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`.
|
||||
// Example: assert i8(-2).str() == '-2'
|
||||
pub fn (n i8) str() string {
|
||||
|
@ -1,4 +1,3 @@
|
||||
[wasm_import_namespace: 'wasi_snapshot_preview1']
|
||||
module builtin
|
||||
|
||||
struct CIOVec {
|
||||
@ -9,6 +8,9 @@ struct CIOVec {
|
||||
type Errno = u16
|
||||
type FileDesc = int
|
||||
|
||||
[wasm_import_namespace: 'wasi_snapshot_preview1']
|
||||
fn WASM.fd_write(fd FileDesc, iovs &CIOVec, iovs_len usize, retptr &usize) Errno
|
||||
|
||||
[wasm_import_namespace: 'wasi_snapshot_preview1']
|
||||
[noreturn]
|
||||
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) {
|
||||
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() {
|
||||
node_basename := $if windows { 'node.exe' } $else { 'node' }
|
||||
os.find_abs_path_of_executable(node_basename) or {
|
||||
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 {
|
||||
go_basename := $if windows { 'go.exe' } $else { 'go' }
|
||||
os.find_abs_path_of_executable(go_basename) or {
|
||||
@ -96,8 +128,7 @@ fn (mut b Builder) run_compiled_executable_and_exit() {
|
||||
} else {
|
||||
compiled_file
|
||||
}
|
||||
mut run_args := []string{cap: b.pref.run_args.len + 1}
|
||||
if b.pref.backend.is_js() {
|
||||
if b.pref.backend.is_js() || b.pref.backend == .wasm {
|
||||
run_args << compiled_file
|
||||
} else if b.pref.backend == .golang {
|
||||
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
|
||||
|
||||
import v.ast
|
||||
import v.token
|
||||
import v.gen.wasm.binaryen
|
||||
import wasm
|
||||
|
||||
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()
|
||||
)
|
||||
pub fn (mut g Gen) as_numtype(a wasm.ValType) wasm.NumType {
|
||||
if a in [.funcref_t, .externref_t, .v128_t] {
|
||||
g.w_error("as_numtype: called with '${a}'")
|
||||
}
|
||||
|
||||
return unsafe { wasm.NumType(a) }
|
||||
}
|
||||
|
||||
// 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.
|
||||
// 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_)
|
||||
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() {
|
||||
g.needs_stack = true
|
||||
return wasm.type_i32
|
||||
if typ.is_ptr() || typ.is_pointer() {
|
||||
return wasm.ValType.i32_t
|
||||
}
|
||||
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
|
||||
wasm.ValType.i32_t
|
||||
}
|
||||
ast.i64_type_idx, ast.u64_type_idx, ast.int_literal_type_idx {
|
||||
wasm.type_i64
|
||||
ast.i64_type_idx, ast.u64_type_idx {
|
||||
wasm.ValType.i64_t
|
||||
}
|
||||
ast.f32_type_idx {
|
||||
wasm.type_f32
|
||||
wasm.ValType.f32_t
|
||||
}
|
||||
ast.f64_type_idx, ast.float_literal_type_idx {
|
||||
wasm.type_f64
|
||||
ast.f64_type_idx {
|
||||
wasm.ValType.f64_t
|
||||
}
|
||||
else {
|
||||
wasm.type_i32
|
||||
wasm.ValType.i32_t
|
||||
}
|
||||
}
|
||||
}
|
||||
if typ == ast.bool_type_idx {
|
||||
return wasm.type_i32
|
||||
return wasm.ValType.i32_t
|
||||
}
|
||||
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)
|
||||
g.pool.type_size(typ)
|
||||
return wasm.ValType.i32_t // pointer
|
||||
}
|
||||
ast.Alias {
|
||||
return g.get_wasm_type(ts.info.parent_type)
|
||||
}
|
||||
ast.ArrayFixed {
|
||||
return wasm.type_i32
|
||||
return wasm.ValType.i32_t // pointer
|
||||
}
|
||||
ast.Enum {
|
||||
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}")
|
||||
}
|
||||
|
||||
fn infix_kind_return_bool(op token.Kind) bool {
|
||||
return op in [.eq, .ne, .gt, .lt, .ge, .le]
|
||||
}
|
||||
pub fn (mut g Gen) infix_from_typ(typ ast.Type, op token.Kind) {
|
||||
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 {
|
||||
.plus {
|
||||
return binaryen.addint32()
|
||||
g.func.add(wasm_typ)
|
||||
}
|
||||
.minus {
|
||||
return binaryen.subint32()
|
||||
g.func.sub(wasm_typ)
|
||||
}
|
||||
.mul {
|
||||
return binaryen.mulint32()
|
||||
g.func.mul(wasm_typ)
|
||||
}
|
||||
.mod {
|
||||
if typ.is_signed() {
|
||||
return binaryen.remsint32()
|
||||
} else {
|
||||
return binaryen.remuint32()
|
||||
}
|
||||
g.func.rem(wasm_typ, typ.is_signed())
|
||||
}
|
||||
.div {
|
||||
if typ.is_signed() {
|
||||
return binaryen.divsint32()
|
||||
} else {
|
||||
return binaryen.divuint32()
|
||||
}
|
||||
g.func.div(wasm_typ, typ.is_signed())
|
||||
}
|
||||
.eq {
|
||||
return binaryen.eqint32()
|
||||
g.func.eq(wasm_typ)
|
||||
}
|
||||
.ne {
|
||||
return binaryen.neint32()
|
||||
g.func.ne(wasm_typ)
|
||||
}
|
||||
.gt {
|
||||
if typ.is_signed() {
|
||||
return binaryen.gtsint32()
|
||||
} else {
|
||||
return binaryen.gtuint32()
|
||||
}
|
||||
g.func.gt(wasm_typ, typ.is_signed())
|
||||
}
|
||||
.lt {
|
||||
if typ.is_signed() {
|
||||
return binaryen.ltsint32()
|
||||
} else {
|
||||
return binaryen.ltuint32()
|
||||
}
|
||||
g.func.lt(wasm_typ, typ.is_signed())
|
||||
}
|
||||
.ge {
|
||||
if typ.is_signed() {
|
||||
return binaryen.gesint32()
|
||||
} else {
|
||||
return binaryen.geuint32()
|
||||
}
|
||||
g.func.ge(wasm_typ, typ.is_signed())
|
||||
}
|
||||
.le {
|
||||
if typ.is_signed() {
|
||||
return binaryen.lesint32()
|
||||
} else {
|
||||
return binaryen.leuint32()
|
||||
g.func.le(wasm_typ, typ.is_signed())
|
||||
}
|
||||
}
|
||||
/*.logical_or {
|
||||
return binaryen.orint32() // TODO: logical or
|
||||
}*/
|
||||
.xor {
|
||||
return binaryen.xorint32()
|
||||
g.func.b_xor(wasm_typ)
|
||||
}
|
||||
.pipe {
|
||||
return binaryen.orint32()
|
||||
g.func.b_or(wasm_typ)
|
||||
}
|
||||
.amp {
|
||||
return binaryen.andint32()
|
||||
g.func.b_and(wasm_typ)
|
||||
}
|
||||
.left_shift {
|
||||
return binaryen.shlint32()
|
||||
g.func.b_shl(wasm_typ)
|
||||
}
|
||||
.right_shift {
|
||||
return binaryen.shrsint32()
|
||||
g.func.b_shr(wasm_typ, true)
|
||||
}
|
||||
.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.func.b_shr(wasm_typ, false)
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
fn static_arrays() {
|
||||
fn static_arrays() (int, int, i64) {
|
||||
a := [8]int{}
|
||||
b := [10, 12, 150]!
|
||||
c := [TEST{}, TEST{
|
||||
b: 10
|
||||
}]!
|
||||
|
||||
return a[2], b[1], c[1].b
|
||||
}
|
||||
|
||||
fn index_expression() {
|
||||
@ -16,7 +18,11 @@ fn index_expression() {
|
||||
|
||||
a := b[2]
|
||||
c := 'hello'[4]
|
||||
d := c'hello'[2]
|
||||
d := unsafe { c'hello'[2] }
|
||||
|
||||
println(a)
|
||||
println(c)
|
||||
println(d)
|
||||
}
|
||||
|
||||
fn test_this(index int) int {
|
||||
@ -31,9 +37,31 @@ struct AA {
|
||||
a [10]&int
|
||||
}
|
||||
|
||||
fn main() {
|
||||
fn test_stuff() &int {
|
||||
a := AA{}
|
||||
|
||||
mut b := &int(0)
|
||||
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() {
|
||||
print('hello!')
|
||||
println('hello!')
|
||||
panic('nooo!')
|
||||
}
|
||||
|
||||
fn str_methods() {
|
||||
@ -16,3 +15,19 @@ fn str_implicit() {
|
||||
a := 100
|
||||
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
|
||||
}
|
||||
|
||||
fn labelcfor() {
|
||||
fn labelcfor() (int, int) {
|
||||
mut idx := 0
|
||||
mut val := 0
|
||||
|
||||
hello: for {
|
||||
for {
|
||||
val++
|
||||
if val == 10 {
|
||||
continue hello
|
||||
}
|
||||
val++
|
||||
idx++
|
||||
|
||||
if val == 100 {
|
||||
break hello
|
||||
@ -75,6 +77,8 @@ fn labelcfor() {
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
return val, idx
|
||||
}
|
||||
|
||||
fn infcfor() int {
|
||||
@ -89,3 +93,32 @@ fn infcfor() int {
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
fn enums() {
|
||||
fn enums() Hello {
|
||||
mut a := Hello.a
|
||||
a = .c
|
||||
return a
|
||||
}
|
||||
|
||||
struct AA {
|
||||
@ -19,6 +20,7 @@ struct AA {
|
||||
fn of() {
|
||||
a := __offsetof(AA, b)
|
||||
b := sizeof(AA)
|
||||
_, _ := a, b
|
||||
}
|
||||
|
||||
fn constant() int {
|
||||
@ -55,3 +57,35 @@ fn ptr_arith() {
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
pub fn multireturn(a int) (int, f64, i64) {
|
||||
pub fn multireturn(a int) (int, i64, i64) {
|
||||
return 2, a + 2, 10 - a
|
||||
}
|
||||
|
||||
@ -21,10 +21,94 @@ pub fn side_effect() int {
|
||||
return 22
|
||||
}
|
||||
|
||||
pub fn test_side_effect() int {
|
||||
pub fn run_side_effect() int {
|
||||
mut a := 15
|
||||
|
||||
_, a = side_effect(), 10
|
||||
|
||||
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() {
|
||||
a := Hello{}
|
||||
_ := Hello{}
|
||||
}
|
||||
|
||||
struct DD {
|
||||
@ -177,3 +177,25 @@ fn postfix_test() {
|
||||
fn postfix_test_mut(mut a TEST) {
|
||||
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
|
||||
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()
|
||||
vexe := os.getenv('VEXE')
|
||||
vroot := os.dir(vexe)
|
||||
@ -33,7 +50,7 @@ fn test_wasm() {
|
||||
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)}'
|
||||
cmd := '${os.quoted_path(vexe)} -b wasm run ${os.quoted_path(full_test_path)} 2> ${os.quoted_path(tmperrfile)}'
|
||||
if is_verbose {
|
||||
println(cmd)
|
||||
}
|
||||
@ -65,6 +82,9 @@ fn test_wasm() {
|
||||
bench.fail()
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
os.write_file(outfile, res_wasm.output.trim_right('\r\n').replace('\r\n',
|
||||
'\n'))!
|
||||
}
|
||||
bench.ok()
|
||||
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_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).
|
||||
* `wasm` - V outputs a WebAssembly module directly (see -os wasi|browser) (EXPERIMENTAL).
|
||||
|
||||
-d <flag>[=<value>], -define <flag>[=<value>]
|
||||
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 JS-specific build flags, use `v help build-js`.
|
||||
For Native-specific build flags, use `v help build-native`.
|
||||
For WebAssembly-specific build flags, use `v help build-wasm`.
|
||||
|
||||
See also:
|
||||
`v help run` for documentation regarding `v run`.
|
||||
|
@ -228,6 +228,7 @@ pub mut:
|
||||
// checker settings:
|
||||
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.
|
||||
wasm_validate bool // validate webassembly code, by calling `wasm-validate`
|
||||
}
|
||||
|
||||
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]
|
||||
current_args := args[i..].clone()
|
||||
match arg {
|
||||
'-wasm-validate' {
|
||||
res.wasm_validate = true
|
||||
}
|
||||
'-apk' {
|
||||
res.is_apk = true
|
||||
res.build_options << arg
|
||||
|
@ -666,6 +666,21 @@ pub fn (mut t Transformer) expr(mut node ast.Expr) ast.Expr {
|
||||
ast.UnsafeExpr {
|
||||
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 {}
|
||||
}
|
||||
return node
|
||||
|
@ -163,7 +163,7 @@ pub fn launch_tool(is_verbose bool, tool_name string, args []string) {
|
||||
for emodule in emodules {
|
||||
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'] {
|
||||
// These tools will be called by users in cases where there
|
||||
// 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..]
|
||||
}
|
||||
|
||||
// name
|
||||
pub fn (mut mod Module) name(name string) {
|
||||
fn (mut mod Module) name(name string) {
|
||||
mod.u32(u32(name.len))
|
||||
mod.buf << name.bytes()
|
||||
}
|
||||
|
||||
// start_subsection
|
||||
pub fn (mut mod Module) start_subsection(sec Subsection) int {
|
||||
fn (mut mod Module) start_subsection(sec Subsection) int {
|
||||
mod.buf << u8(sec)
|
||||
return mod.patch_start()
|
||||
}
|
||||
|
||||
// start_section
|
||||
pub fn (mut mod Module) start_section(sec Section) int {
|
||||
fn (mut mod Module) start_section(sec Section) int {
|
||||
mod.buf << u8(sec)
|
||||
return mod.patch_start()
|
||||
}
|
||||
|
||||
// end_section
|
||||
pub fn (mut mod Module) end_section(tpatch int) {
|
||||
fn (mut mod Module) end_section(tpatch int) {
|
||||
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))
|
||||
for ft in mod.fn_imports {
|
||||
mod.u32(u32(ft.mod.len))
|
||||
mod.buf << ft.mod.bytes()
|
||||
mod.u32(u32(ft.name.len))
|
||||
mod.buf << ft.name.bytes()
|
||||
mod.name(ft.mod)
|
||||
mod.name(ft.name)
|
||||
mod.buf << 0x00 // function
|
||||
mod.u32(u32(ft.tidx))
|
||||
}
|
||||
for gt in mod.global_imports {
|
||||
mod.u32(u32(gt.mod.len))
|
||||
mod.buf << gt.mod.bytes()
|
||||
mod.u32(u32(gt.name.len))
|
||||
mod.buf << gt.name.bytes()
|
||||
mod.name(gt.mod)
|
||||
mod.name(gt.name)
|
||||
mod.buf << 0x03 // global
|
||||
mod.global_type(gt.typ, gt.is_mut)
|
||||
}
|
||||
@ -244,7 +236,7 @@ pub fn (mut mod Module) compile() []u8 {
|
||||
continue
|
||||
}
|
||||
lsz++
|
||||
mod.name(ft.name)
|
||||
mod.name(ft.export_name or { ft.name })
|
||||
mod.buf << 0x00 // function
|
||||
mod.u32(u32(ft.idx + mod.fn_imports.len))
|
||||
}
|
||||
|
@ -43,4 +43,11 @@ mut:
|
||||
locals []FunctionLocal
|
||||
pub:
|
||||
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)
|
||||
}
|
||||
|
||||
// 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.
|
||||
// WebAssembly instruction: `unreachable`.
|
||||
pub fn (mut func Function) unreachable() {
|
||||
@ -919,7 +930,7 @@ pub fn (mut func Function) c_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) {
|
||||
assert func.label == label, 'c_end: called with an invalid label ${label}'
|
||||
func.label--
|
||||
@ -945,11 +956,6 @@ pub fn (mut func Function) c_br_if(label LabelIndex) {
|
||||
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.
|
||||
// If this function does not exist when calling `compile` on the module, it will panic.
|
||||
// WebAssembly instruction: `call`.
|
||||
|
Loading…
Reference in New Issue
Block a user