Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WASI runtime #1831

Draft
wants to merge 26 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
c4f9e34
JS runtime: implement more system primitives
vouillon Jan 29, 2025
ac4bfc7
Wasm runtime: clean-up
vouillon Jan 29, 2025
7ce1c04
Wasm runtime: implement more system primitives
vouillon Jan 29, 2025
9900af6
Don't add Unix.putenv
vouillon Feb 4, 2025
bd4f178
Add test from OCaml test suite
vouillon Feb 5, 2025
15daf96
Add tests
vouillon Feb 10, 2025
3b759c9
Changes
vouillon Feb 10, 2025
180cf56
Wasm runtime: implement ocaml_unix_findfirst / ocaml_unix_findnext
vouillon Feb 12, 2025
7086b43
More flexible API to call binaryen tools
vouillon Jan 30, 2025
a99e0cb
WAT file preprocessor
vouillon Dec 17, 2024
f1ef75f
Use preprocessor to manage runtime changes between OCaml versions
vouillon Jan 30, 2025
e837171
WAT preprocessor: add tests
vouillon Jan 30, 2025
d0110a0
Syntactic sugar for strings
vouillon Jan 31, 2025
6b93e99
Wasm runtime: use string syntactic sugar
vouillon Jan 31, 2025
b7cb34e
Preprocessor: use the export name to name functions without id
vouillon Jan 31, 2025
19b44c2
Preprocessor: add references to the Wasm standards
vouillon Feb 3, 2025
34e890e
Preprocessor: move tools as subcommands inside wasm_of_ocaml
vouillon Feb 4, 2025
5eddf4e
CHANGES.md
vouillon Feb 4, 2025
f83fb5c
Support for several Wasm runtimes (depending on flags)
vouillon Dec 17, 2024
72cc2ee
Runtimes: add primitive caml_throw_js_exception
vouillon Feb 19, 2025
f454362
WASI runtime
vouillon Dec 17, 2024
07ef3a6
Add flag trap-on-exception
vouillon Feb 14, 2025
0e0b296
Node wrapper: support for using alternative Wasm engines
vouillon Feb 14, 2025
cab3c45
CI updates
vouillon Feb 14, 2025
6d2a079
CI: use Wizard engine as well
vouillon Feb 6, 2025
3f3fcf1
WASI: support for separate compilation
vouillon Feb 19, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 95 additions & 2 deletions .github/workflows/build-wasm_of_ocaml.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ jobs:
matrix:
os:
- ubuntu-latest
os-name:
- Ubuntu
ocaml-compiler:
- "4.14"
- "5.0"
Expand All @@ -27,30 +29,50 @@ jobs:
- false
all_jane_street_tests:
- false
wasi:
- false
include:
- os: macos-latest
os-name: MacOS
ocaml-compiler: "5.3"
separate_compilation: true
jane_street_tests: false
all_jane_street_tests: false
wasi: false
- os: windows-latest
os-name: Windows
ocaml-compiler: "5.2"
separate_compilation: true
jane_street_tests: true
all_jane_street_tests: true
wasi: false
- os: ubuntu-latest
os-name: Ubuntu
ocaml-compiler: "5.2"
separate_compilation: true
jane_street_tests: true
all_jane_street_tests: true
wasi: false
- os: ubuntu-latest
os-name: Ubuntu
ocaml-compiler: "5.2"
separate_compilation: false
jane_street_tests: true
all_jane_street_tests: false
wasi: false
- os: ubuntu-latest
os-name: Ubuntu
ocaml-compiler: "5.3"
separate_compilation: true
jane_street_tests: false
all_jane_street_tests: false
wasi: true

runs-on: ${{ matrix.os }}

name:
${{ matrix.wasi && 'WASI / ' || '' }}${{ (! matrix.separate_compilation) && 'Whole program / ' || ''}}${{ matrix.ocaml-compiler }} / ${{ matrix.os-name }}${{ matrix.all_jane_street_tests && ' / Jane Street tests' || ''}}

steps:
- name: Set git to use LF
if: ${{ matrix.os == 'windows-latest' && matrix.ocaml-compiler < 5.2 }}
Expand All @@ -77,6 +99,54 @@ jobs:
with:
node-version: latest

- name: Set-up Rust toolchain
if: matrix.wasi
uses: actions-rust-lang/setup-rust-toolchain@v1

- name: Checkout Wasmtime
if: matrix.wasi
uses: actions/checkout@v4
with:
repository: bytecodealliance/wasmtime
path: wasmtime
submodules: true

- name: Build Wasmtime
if: matrix.wasi
working-directory: ./wasmtime
run: |
cargo build
echo `pwd`/target/debug >> "$GITHUB_PATH"

- name: Checkout Virgil
if: matrix.wasi
uses: actions/checkout@v4
with:
repository: titzer/virgil
path: virgil

- name: Build Virgil
if: matrix.wasi
working-directory: ./virgil
run: |
export PATH=$PATH:`pwd`/bin
echo `pwd`/bin >> "$GITHUB_PATH"
make

- name: Checkout Wizard engine
if: matrix.wasi
uses: actions/checkout@v4
with:
repository: titzer/wizard-engine
path: wizard-engine

- name: Build Wizard engine
if: matrix.wasi
working-directory: ./wizard-engine
run: |
make -j 4
echo `pwd`/bin >> "$GITHUB_PATH"

- name: Set-up OCaml ${{ matrix.ocaml-compiler }}
uses: ocaml/setup-ocaml@v3
with:
Expand Down Expand Up @@ -127,7 +197,7 @@ jobs:
opam install num cohttp-lwt-unix ppx_expect cstruct uucp

- name: Run tests
if: ${{ matrix.separate_compilation }}
if: ${{ matrix.separate_compilation && ! matrix.wasi }}
working-directory: ./wasm_of_ocaml
run: opam exec -- dune build @runtest-wasm

Expand All @@ -136,11 +206,34 @@ jobs:
# See https://github.com/libuv/libuv/issues/3622

- name: Run tests with CPS effects
if: ${{ matrix.ocaml-compiler >= '5.' && matrix.separate_compilation }}
if: ${{ matrix.ocaml-compiler >= '5.' && matrix.separate_compilation && ! matrix.wasi }}
continue-on-error: ${{ matrix.os == 'windows-latest' }}
working-directory: ./wasm_of_ocaml
run: opam exec -- dune build @runtest-wasm --profile with-effects

- name: Run tests (WASI runtime - node)
if: ${{ matrix.wasi }}
working-directory: ./wasm_of_ocaml
run: opam exec -- dune build @runtest-wasm --profile wasi

- name: Run tests (WASI runtime - wasmtime)
if: ${{ matrix.wasi }}
working-directory: ./wasm_of_ocaml
env:
WASM_ENGINE: wasmtime
WASI_FLAGS: --enable trap-on-exception
RUST_BACKTRACE: 0
continue-on-error: true
run: opam exec -- dune build @runtest-wasm --profile wasi

- name: Run tests (WASI runtime - Wizard engine)
if: ${{ matrix.wasi }}
working-directory: ./wasm_of_ocaml
env:
WASM_ENGINE: wizard-fast
continue-on-error: true
run: opam exec -- dune build @runtest-wasm --profile wasi

- name: Run Base tests
if: matrix.all_jane_street_tests
continue-on-error: ${{ matrix.os == 'windows-latest' }}
Expand Down
6 changes: 6 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# dev

## Features/Changes
* Runtime: support more Unix functions (#1829)
* Compiler: use a Wasm text files preprocessor (#1822)

# 6.0.1 (2025-02-07) - Lille

## Features/Changes
Expand Down
102 changes: 86 additions & 16 deletions compiler/bin-wasm_of_ocaml/compile.ml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,54 @@ let output_gen output_file f =
Code.Var.set_stable (Config.Flag.stable_var ());
Filename.gen_file output_file f

let with_runtime_files ~runtime_wasm_files f =
let inputs =
List.map
~f:(fun file ->
{ Wat_preprocess.module_name = "env"
; file
; source =
(if Link.Wasm_binary.check_file ~file
then File
else Contents (Js_of_ocaml_compiler.Fs.read_file file))
})
runtime_wasm_files
in
Wat_preprocess.with_preprocessed_files ~variables:[] ~inputs f

let build_runtime ~runtime_file =
(* Keep this variables in sync with gen/gen.ml *)
let variables =
[ "wasi", Config.Flag.wasi (); "trap-on-exception", Config.Flag.trap_on_exception () ]
in
match
List.find_opt Runtime_files.precompiled_runtimes ~f:(fun (flags, _) ->
assert (
List.length flags = List.length variables
&& List.for_all2 ~f:(fun (k, _) (k', _) -> String.equal k k') flags variables);
Poly.equal flags variables)
with
| Some (_, contents) -> Fs.write_file ~name:runtime_file ~contents
| None ->
let inputs =
List.map
~f:(fun (module_name, contents) ->
{ Wat_preprocess.module_name
; file = module_name ^ ".wat"
; source = Contents contents
})
(if Config.Flag.wasi ()
then ("libc", Runtime_files.wasi_libc) :: Runtime_files.wat_files
else Runtime_files.wat_files)
in
Runtime.build
~link_options:[ "-g" ]
~opt_options:[ "-g"; "-O2" ]
~variables:
(List.map ~f:(fun (k, v) : (_ * Wat_preprocess.value) -> k, Bool v) variables)
~inputs
~output_file:runtime_file

let link_and_optimize
~profile
~sourcemap_root
Expand All @@ -91,7 +139,7 @@ let link_and_optimize
let enable_source_maps = Option.is_some opt_sourcemap_file in
Fs.with_intermediate_file (Filename.temp_file "runtime" ".wasm")
@@ fun runtime_file ->
Fs.write_file ~name:runtime_file ~contents:Runtime_files.wasm_runtime;
build_runtime ~runtime_file;
Fs.with_intermediate_file (Filename.temp_file "wasm-merged" ".wasm")
@@ fun temp_file ->
opt_with
Expand All @@ -100,11 +148,15 @@ let link_and_optimize
then Some (Filename.temp_file "wasm-merged" ".wasm.map")
else None)
@@ fun opt_temp_sourcemap ->
(with_runtime_files ~runtime_wasm_files
@@ fun runtime_inputs ->
Binaryen.link
~runtime_files:(runtime_file :: runtime_wasm_files)
~input_files:wat_files
~inputs:
(({ Binaryen.module_name = "env"; file = runtime_file } :: runtime_inputs)
@ List.map ~f:(fun file -> { Binaryen.module_name = "OCaml"; file }) wat_files)
~opt_output_sourcemap:opt_temp_sourcemap
~output_file:temp_file;
~output_file:temp_file
());
Fs.with_intermediate_file (Filename.temp_file "wasm-dce" ".wasm")
@@ fun temp_file' ->
opt_with
Expand All @@ -113,7 +165,10 @@ let link_and_optimize
@@ fun opt_temp_sourcemap' ->
let primitives =
Binaryen.dead_code_elimination
~dependencies:Runtime_files.dependencies
~dependencies:
(if Config.Flag.wasi ()
then Runtime_files.wasi_dependencies
else Runtime_files.dependencies)
~opt_input_sourcemap:opt_temp_sourcemap
~opt_output_sourcemap:opt_temp_sourcemap'
~input_file:temp_file
Expand All @@ -124,7 +179,8 @@ let link_and_optimize
~opt_input_sourcemap:opt_temp_sourcemap'
~opt_output_sourcemap:opt_sourcemap
~input_file:temp_file'
~output_file;
~output_file
();
Option.iter
~f:(update_sourcemap ~sourcemap_root ~sourcemap_don't_inline_content)
opt_sourcemap_file;
Expand All @@ -133,20 +189,23 @@ let link_and_optimize
let link_runtime ~profile runtime_wasm_files output_file =
Fs.with_intermediate_file (Filename.temp_file "runtime" ".wasm")
@@ fun runtime_file ->
Fs.write_file ~name:runtime_file ~contents:Runtime_files.wasm_runtime;
build_runtime ~runtime_file;
Fs.with_intermediate_file (Filename.temp_file "wasm-merged" ".wasm")
@@ fun temp_file ->
with_runtime_files ~runtime_wasm_files
@@ fun runtime_inputs ->
Binaryen.link
~opt_output_sourcemap:None
~runtime_files:(runtime_file :: runtime_wasm_files)
~input_files:[]
~output_file:temp_file;
~inputs:({ Binaryen.module_name = "env"; file = runtime_file } :: runtime_inputs)
~output_file:temp_file
();
Binaryen.optimize
~profile
~opt_input_sourcemap:None
~opt_output_sourcemap:None
~input_file:temp_file
~output_file
()

let generate_prelude ~out_file =
Filename.gen_file out_file
Expand Down Expand Up @@ -186,7 +245,8 @@ let build_prelude z =
~input_file:prelude_file
~output_file:tmp_prelude_file
~opt_input_sourcemap:None
~opt_output_sourcemap:None;
~opt_output_sourcemap:None
();
Zip.add_file z ~name:"prelude.wasm" ~file:tmp_prelude_file;
predefined_exceptions

Expand Down Expand Up @@ -216,7 +276,13 @@ let build_js_runtime ~primitives ?runtime_arguments () =
in
let prelude = Link.output_js always_required_js in
let init_fun =
match Parse_js.parse (Parse_js.Lexer.of_string Runtime_files.js_runtime) with
match
Parse_js.parse
(Parse_js.Lexer.of_string
(if Config.Flag.wasi ()
then Runtime_files.js_wasi_launcher
else Runtime_files.js_launcher))
with
| [ (Expression_statement f, _) ] -> f
| _ -> assert false
in
Expand Down Expand Up @@ -413,7 +479,8 @@ let run
~opt_input_sourcemap:None
~opt_output_sourcemap:opt_tmp_map_file
~input_file:wat_file
~output_file:tmp_wasm_file;
~output_file:tmp_wasm_file
();
{ Link.unit_name; unit_info; strings; fragments }
in
cont unit_data unit_name tmp_wasm_file opt_tmp_map_file
Expand Down Expand Up @@ -455,9 +522,12 @@ let run
tmp_wasm_file
in
let wasm_name =
Printf.sprintf
"code-%s"
(String.sub (Digest.to_hex (Digest.file tmp_wasm_file)) ~pos:0 ~len:20)
if Config.Flag.wasi ()
then "code"
else
Printf.sprintf
"code-%s"
(String.sub (Digest.to_hex (Digest.file tmp_wasm_file)) ~pos:0 ~len:20)
in
let tmp_wasm_file' = Filename.concat tmp_dir (wasm_name ^ ".wasm") in
Sys.rename tmp_wasm_file tmp_wasm_file';
Expand Down
14 changes: 9 additions & 5 deletions compiler/bin-wasm_of_ocaml/dune
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
(executable
(name wasm_of_ocaml)
(public_name wasm_of_ocaml)
(executables
(names wasm_of_ocaml wasmoo_link_wasm)
(public_names wasm_of_ocaml -)
(package wasm_of_ocaml-compiler)
(libraries
jsoo_cmdline
Expand All @@ -25,9 +25,13 @@
(target runtime_files.ml)
(deps
gen/gen.exe
../../runtime/wasm/runtime.wasm
../../runtime/wasm/runtime.js
../../runtime/wasm/deps.json)
../../runtime/wasm/deps.json
../../runtime/wasm/runtime-wasi.js
../../runtime/wasm/deps-wasi.json
../../runtime/wasm/libc.wasm
(glob_files ../../runtime/wasm/*.wat)
(glob_files ../../runtime/wasm/runtime-*.wasm))
(action
(with-stdout-to
%{target}
Expand Down
Loading
Loading