Skip to content

Commit 0625caa

Browse files
authored
wasm: add a webassembly compiler backend, based on using binaryen (#17368)
1 parent b9a8a21 commit 0625caa

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+7981
-8
lines changed

.github/workflows/native_backend_tests_ci.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ on:
1010
- 'vlib/builtin/**.v'
1111
- 'vlib/v/ast/**.v'
1212
- 'vlib/v/scanner/**.v'
13-
- 'vlib/v/scanner/**.v'
1413
- 'vlib/v/parser/**.v'
1514
- 'vlib/v/checker/**.v'
1615
- 'vlib/v/gen/c/**.v'
@@ -31,7 +30,6 @@ on:
3130
- 'vlib/builtin/**.v'
3231
- 'vlib/v/ast/**.v'
3332
- 'vlib/v/scanner/**.v'
34-
- 'vlib/v/scanner/**.v'
3533
- 'vlib/v/parser/**.v'
3634
- 'vlib/v/checker/**.v'
3735
- 'vlib/v/gen/c/**.v'
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
name: wasm backend CI
2+
3+
on:
4+
push:
5+
paths:
6+
- '!**'
7+
- '!**.md'
8+
- 'cmd/tools/builders/**.v'
9+
- 'vlib/builtin/**.v'
10+
- 'vlib/v/ast/**.v'
11+
- 'vlib/v/scanner/**.v'
12+
- 'vlib/v/parser/**.v'
13+
- 'vlib/v/checker/**.v'
14+
- 'vlib/v/gen/c/**.v'
15+
- 'vlib/v/builder/**.v'
16+
- 'vlib/v/cflag/**.v'
17+
- 'vlib/v/live/**.v'
18+
- 'vlib/v/util/**.v'
19+
- 'vlib/v/markused/**.v'
20+
- 'vlib/v/preludes/**.v'
21+
- 'vlib/v/gen/wasm/**.v'
22+
- 'vlib/v/gen/wasm/tests/**.v'
23+
pull_request:
24+
paths:
25+
- '!**'
26+
- '!**.md'
27+
- 'cmd/tools/builders/**.v'
28+
- 'vlib/builtin/**.v'
29+
- 'vlib/v/ast/**.v'
30+
- 'vlib/v/scanner/**.v'
31+
- 'vlib/v/parser/**.v'
32+
- 'vlib/v/checker/**.v'
33+
- 'vlib/v/gen/c/**.v'
34+
- 'vlib/v/builder/**.v'
35+
- 'vlib/v/cflag/**.v'
36+
- 'vlib/v/live/**.v'
37+
- 'vlib/v/util/**.v'
38+
- 'vlib/v/markused/**.v'
39+
- 'vlib/v/preludes/**.v'
40+
- 'vlib/v/gen/wasm/**.v'
41+
- 'vlib/v/gen/wasm/tests/**.v'
42+
43+
concurrency:
44+
group: wasm-backend-ci-${{ github.event.pull_request.number || github.sha }}
45+
cancel-in-progress: true
46+
47+
jobs:
48+
wasm-backend-ubuntu:
49+
runs-on: ubuntu-22.04
50+
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
51+
timeout-minutes: 121
52+
steps:
53+
- uses: actions/checkout@v3
54+
55+
- name: Install dependencies
56+
run: |
57+
sudo apt-get update
58+
sudo apt-get install --quiet -y clang gcc
59+
60+
- name: Build V
61+
run: make -j4 && ./v symlink -githubci
62+
63+
- name: Install binaryen as build dependency for the V WASM backend
64+
run: ./v cmd/tools/install_binaryen.vsh
65+
66+
- name: Build the V WASM backend
67+
run: ./v -cc clang -showcc -v cmd/tools/builders/wasm_builder.v
68+
69+
- name: Test the WASM backend
70+
run: ./v test vlib/v/gen/wasm/tests/
71+
72+
- name: Build examples
73+
run: VTEST_ONLY=wasm ./v build-examples
74+
75+
## wasm-backend-macos:
76+
## runs-on: macOS-12
77+
## if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
78+
## timeout-minutes: 121
79+
## steps:
80+
## - uses: actions/checkout@v3
81+
##
82+
## - name: Build V
83+
## run: make -j4 && ./v symlink -githubci
84+
##
85+
## - name: Install binaryen as build dependency for the V WASM backend
86+
## run: ./v cmd/tools/install_binaryen.vsh
87+
##
88+
## - name: Build the V WASM backend
89+
## run: ./v -cc clang -showcc -v cmd/tools/builders/wasm_builder.v
90+
##
91+
## - name: Test the WASM backend
92+
## run: ./v test vlib/v/gen/wasm/tests/
93+
##
94+
## - name: Build examples
95+
## run: VTEST_ONLY=wasm ./v build-examples

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ flake.nix
108108
.envrc
109109

110110
thirdparty/stdatomic/nix/cpp/*.h
111+
thirdparty/binaryen*
111112

112113
# ignore VLS log
113114
vls.log
@@ -120,3 +121,4 @@ vls.log
120121
# ignore Intellij files
121122
.idea/
122123
/*.iml
124+
wasm.v

cmd/tools/builders/wasm_builder.v

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module main
2+
3+
import v.builder.wasmbuilder
4+
5+
fn main() {
6+
wasmbuilder.start()
7+
}

cmd/tools/install_binaryen.vsh

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#!/usr/bin/env -S v -raw-vsh-tmp-prefix tmp
2+
3+
import net.http
4+
import json
5+
import os
6+
7+
struct JQ {
8+
tag_name string
9+
}
10+
11+
fn main() {
12+
root := os.real_path(os.dir(os.getenv_opt('VEXE') or { @VEXE }))
13+
os.chdir(root)! // make sure that the workfolder is stable
14+
15+
tloc := os.join_path(root, 'thirdparty')
16+
loc := os.join_path(tloc, 'binaryen')
17+
18+
if os.exists(loc) {
19+
eprintln('thirdparty/binaryen exists, will not overwrite')
20+
eprintln('delete the folder, and execute again')
21+
exit(1)
22+
}
23+
24+
jq := http.get_text('https://api.github.com/repos/WebAssembly/binaryen/releases/latest')
25+
tag := json.decode(JQ, jq)!.tag_name
26+
27+
name := $if windows {
28+
'x86_64-windows'
29+
} $else $if macos {
30+
$if arm64 {
31+
'arm64-macos'
32+
} $else {
33+
'x86_64-macos'
34+
}
35+
} $else $if linux {
36+
'x86_64-linux'
37+
} $else {
38+
eprintln('A premade binary library is not available for your system.')
39+
eprintln('Build it from source, following the documentation here: https://github.com/WebAssembly/binaryen/#building')
40+
exit(1)
41+
}
42+
43+
fname := 'binaryen-${tag}'
44+
url := 'https://github.com/WebAssembly/binaryen/releases/download/${tag}/${fname}-${name}.tar.gz'
45+
46+
saveloc := os.join_path(tloc, '${fname}.tar.gz')
47+
if !os.exists(saveloc) {
48+
println('Downloading archive: ${saveloc}, from url: ${url} ...')
49+
http.download_file(url, saveloc)!
50+
// defer { os.rm(saveloc) or {}! }
51+
}
52+
53+
mkdir_all(loc)!
54+
println(loc)
55+
56+
println('Extracting `${tloc}/${fname}` to `${tloc}/binaryen` ...')
57+
cmd := 'tar -xvf ${saveloc} --directory ${tloc}'
58+
if os.system(cmd) != 0 {
59+
eprintln('`${cmd}` exited with a non zero exit code')
60+
exit(1)
61+
}
62+
63+
println(cmd)
64+
println('Moving `${tloc}/${fname}` to `${tloc}/binaryen` ...')
65+
66+
os.rename_dir('${tloc}/${fname}', loc)!
67+
println('Done. You can now use `v -b wasm file.v` .')
68+
}

cmd/tools/modules/testing/common.v

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,18 @@ pub fn new_test_session(_vargs string, will_compile bool) TestSession {
252252
$if !macos {
253253
skip_files << 'examples/macos_tray/tray.v'
254254
}
255+
// examples/wasm/mandelbrot/mandelbrot.v requires special compilation flags: `-b wasm -os browser`, skip it for now:
256+
skip_files << 'examples/wasm/mandelbrot/mandelbrot.v'
257+
258+
// TODO: always build the wasm_builder in the future, not just when it was build manually before:
259+
wasm_builder_executable := $if !windows {
260+
'cmd/tools/builders/wasm_builder'
261+
} $else {
262+
'cmd/tools/builders/wasm_builder.exe'
263+
}
264+
if !os.exists(wasm_builder_executable) {
265+
skip_files << os.join_path('cmd/tools/builders/wasm_builder.v')
266+
}
255267
}
256268
vargs := _vargs.replace('-progress', '')
257269
vexe := pref.vexe_path()

cmd/tools/vbuild-examples.v

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ module main
33
import os
44
import testing
55

6-
const vroot = @VMODROOT
6+
const vroot = os.dir(os.real_path(os.getenv_opt('VEXE') or { @VEXE }))
77

88
// build as a project folder
99
const efolders = [
@@ -12,11 +12,14 @@ const efolders = [
1212
'examples/vweb_fullstack',
1313
]
1414

15+
pub fn normalised_vroot_path(path string) string {
16+
return os.real_path(os.join_path_single(vroot, path)).replace('\\', '/')
17+
}
18+
1519
fn main() {
1620
args_string := os.args[1..].join(' ')
1721
params := args_string.all_before('build-examples')
18-
skip_prefixes := efolders.map(os.real_path(os.join_path_single(vroot, it)).replace('\\',
19-
'/'))
22+
mut skip_prefixes := efolders.map(normalised_vroot_path(it))
2023
res := testing.v_build_failing_skipped(params, 'examples', skip_prefixes, fn (mut session testing.TestSession) {
2124
for x in efolders {
2225
pathsegments := x.split_any('/')

cmd/tools/vtest-self.v

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,10 @@ fn main() {
329329
tsession.skip_files << 'vlib/net/udp_test.v'
330330
}
331331

332+
if !os.exists('cmd/tools/builders/wasm_builder') {
333+
tsession.skip_files << 'vlib/v/gen/wasm/tests/wasm_test.v'
334+
}
335+
332336
mut werror := false
333337
mut sanitize_memory := false
334338
mut sanitize_address := false

cmd/v/v.v

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,5 +192,19 @@ fn rebuild(prefs &pref.Preferences) {
192192
println('using Go WIP backend...')
193193
util.launch_tool(prefs.is_verbose, 'builders/golang_builder', os.args[1..])
194194
}
195+
.wasm {
196+
assert_wasm_backend_thirdparty()
197+
util.launch_tool(prefs.is_verbose, 'builders/wasm_builder', os.args[1..])
198+
}
199+
}
200+
}
201+
202+
fn assert_wasm_backend_thirdparty() {
203+
vroot := os.dir(pref.vexe_path())
204+
if !os.exists('${vroot}/cmd/tools/builders/wasm_builder')
205+
&& !os.exists('${vroot}/thirdparty/binaryen') {
206+
eprintln('The WebAssembly backend requires `binaryen`, an external library dependency')
207+
eprintln('This can be installed with `./cmd/tools/install_binaryen.vsh`, to download prebuilt libraries for your platform')
208+
exit(1)
195209
}
196210
}

examples/wasm/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*.wasm

0 commit comments

Comments
 (0)