|
|
This page is a working document about the JavaScript backend in GHC and is regularly updated. The JavaScript backend has been implemented (cc25d52e0f65d54c052908c7d91d5946342ab88a) and is included as a code generator as of GHC 9.6.
|
|
|
|
|
|
## Build instructions
|
|
|
|
|
|
Build instructions can be found on [javascript-backend/building](https://gitlab.haskell.org/ghc/ghc/-/wikis/javascript-backend/building).
|
|
|
|
|
|
## Current status and supported features
|
|
|
|
|
|
* [x] (cc25d52e0f65d54c052908c7d91d5946342ab88a) Boot libraries build
|
|
|
* [x] (394b91ce859653231813fb9af77c26664063c1b6) gitlab CI tests the backend
|
|
|
* [ ] (In progress: !9779) Template Haskell
|
|
|
* [ ] FFI: foreign imports
|
|
|
* [ ] FFI: foreign exports
|
|
|
* [ ] Dom integration
|
|
|
* [ ] Perf: compactor is implemented
|
|
|
* [ ] Perf: optimizer is implemented
|
|
|
* [x] FFI: support for foreign imports
|
|
|
* [ ] Template Haskell (In progress: !9779)
|
|
|
* [ ] FFI: support for "inlined" foreign imports as GHCJS did (JS syntax with named argument placeholders)
|
|
|
* [ ] FFI: support for foreign exports
|
|
|
|
|
|
## Roadmap
|
|
|
|
|
|
TODO
|
|
|
Short term (GHC 9.8):
|
|
|
|
|
|
## Build instructions
|
|
|
- Feature: support for Template Haskell (almost done, see !9779)
|
|
|
- Feature: support for Foreign exports
|
|
|
- Perf: optimize generated JS code for size and speed
|
|
|
- Perf: optimize JS backend
|
|
|
- Correctness: implement and use a typed JS EDSL in the JS backend itself (in progress)
|
|
|
- Correctness: fix bugs found by the testsuite that have been disabled for now
|
|
|
|
|
|
Medium term (GHC 9.10):
|
|
|
- FFI: "inlined" foreign imports (JS syntax with named argument placeholders)
|
|
|
- Feature: profiling (CC, eventlog, ticky-ticky, etc.)
|
|
|
- GHCi: GHCi support (including debugger)
|
|
|
|
|
|
Long term:
|
|
|
- Support for C-sources: idea is to compile via Emscripten and to automatically generate glue code.
|
|
|
- GHCi.js: Haskell code interpreter in the browser
|
|
|
|
|
|
Build instructions can be found on [javascript-backend/building](https://gitlab.haskell.org/ghc/ghc/-/wikis/javascript-backend/building).
|
|
|
|
|
|
## Further Reading and Demos
|
|
|
|
... | ... | @@ -36,10 +51,6 @@ In the long term, having the ability to select the backend without rebuilding th |
|
|
|
|
|
It should be a separate testsuite way as there are some features that are not expected to pass (e.g. support for `compact`).
|
|
|
|
|
|
### Is the JS RTS going to be merged into the tree?
|
|
|
|
|
|
Yes. Not necessarily in the `rts/` directory (perhaps `rts-js` ?) Currently GHCJS loads its JS RTS from its collection of JS files ("shims") we don't want to upstream this feature. Instead we would like to make the JS RTS a proper package/unit.
|
|
|
|
|
|
### Is the RTS going to support the full feature surface supported by GHC's C RTS?
|
|
|
|
|
|
Probably not (e.g. no plan for `compact` support afaik). Do we have a list of GHC's C RTS features that we could use to indicate supported features?
|
... | ... | @@ -52,7 +63,7 @@ Currently GHCJS just ignore "ways" (e.g. it returns the same code for both objec |
|
|
|
|
|
As we have workarounds, we don't plan to work on #20741 until we have a usable JS backend. But we might work on it after it's done. Any help or feedback on #20741 is welcome.
|
|
|
|
|
|
### What are we going to do about GHC's many native-code-centric flags (e.g. -fPIC, -shared, -msse2). Will these be errors if used when targetting Javascript?
|
|
|
### What are we going to do about GHC's many native-code-centric flags (e.g. -fPIC, -shared, -msse2). Will these be errors if used when targeting Javascript?
|
|
|
|
|
|
There are currently ignored by backends that don't use them. We could probably detect and report their use during command-line flags validation but it is out of the scope of the JS backend implementation.
|
|
|
|
... | ... | @@ -72,96 +83,18 @@ TODO: document current GHCJS and Asterius interfaces |
|
|
|
|
|
### What is the plan for handling the various C bits in base/text/bytestring/etc.? Specifically, has there been any motion on discussions with upstreams regarding upstreaming the Javascript implementations?
|
|
|
|
|
|
We plan to propose patches for these libraries. We can use CPP to condition JS specific code to `HOST_OS=ghcjs` and similarly in `.cabal` files.
|
|
|
|
|
|
### #21078 lists "one-shot JS code generator" as one of its tasks. I suspect I am misunderstanding but my first interpretation of this phrase suggests that `--make` mode will not be supported.
|
|
|
|
|
|
We do plan to support `--make` mode. It's just that one task to get there is first to support compiling a single module into a single `.o` without any linking step, which we have summarized as "one-shot".
|
|
|
We plan to propose patches for these libraries. We can use CPP to condition JS specific code to `HOST_ARCH=javascript` and similarly in `.cabal` files (`arch("javascript")`).
|
|
|
|
|
|
### Will GHC need to be aware of the system's Javascript interpreter (e.g. know the path to nodejs)? Presumably yes as we will at very least need to know how to start `iserv`.
|
|
|
|
|
|
At first it doesn't need to because users (or cabal/stack) can use `-pgmi` and `-opti` command line flags to setup the external interpreter to use.
|
|
|
|
|
|
In the longer term, it would probably be better for the external interpreter to be configured per target in a settings file. Probably something to discuss in the context of [#19877](https://gitlab.haskell.org/ghc/ghc/-/issues/19877 "Add support for several toolchains").
|
|
|
Yes. In the longer term, it would probably be better for the external interpreter to be configured per target in a settings file. Probably something to discuss in the context of [#19877](https://gitlab.haskell.org/ghc/ghc/-/issues/19877 "Add support for several toolchains").
|
|
|
|
|
|
### What needs to happen to put proper JS backend support in Cabal? Is upstream aware of this plan? Presumably `c-sources` support will require emscripten support in both Cabal and GHC?
|
|
|
|
|
|
This needs to be given some thoughts.
|
|
|
|
|
|
Currently Cabal doesn't support cross-compilation. We need it to support two toolchains: one for the host (plugins, Setup.hs), one for the target. It also needs to use the correct platform to resolve conditionals in `.cabal` files.
|
|
|
|
|
|
Need input from Luite about c-sources support with emscripten. We should also ensure that whatever solution we implement can also handle WebAssembly C compiler.
|
|
|
|
|
|
### Template Haskell support
|
|
|
|
|
|
The current Template Haskell support in the standalone GHCJS has two issues
|
|
|
|
|
|
```
|
|
|
* It uses a non-standard Template Haskell runnner, GHC gained its own `iserv` external interpreter in the meantime
|
|
|
* The GHCJS TH runner links JavaScript in the compiler, then sends it to the Template Haskell runner (`thrunner.js`) as a message. This occasionally runs into size limitations in node.js
|
|
|
```
|
|
|
|
|
|
A good solution fixes both problems. It has to integrate with iserv, but we still have some room for decisions:
|
|
|
|
|
|
```
|
|
|
* We could have a full bytecode interpreter running in JavaScript, running most of the code in bytecode.
|
|
|
* We could reuse most of the `iserv` infrastructure, but run compiled JS code (like `-fobject-code`). We'd have to fix the linker to get around the size issue.
|
|
|
```
|
|
|
|
|
|
## Building Issues and how to fix them
|
|
|
|
|
|
### Configure fails with: sub-word sized atomic operations not available
|
|
|
|
|
|
#### The error output
|
|
|
|
|
|
The error looks like this:
|
|
|
|
|
|
```
|
|
|
[nix-shell:~/programming/haskell/ghc]$ ./boot && ./configure --target=js-unknown-ghcjs
|
|
|
...
|
|
|
checking version of gcc... checking version of gcc... 15.0.0
|
|
|
15.0.0
|
|
|
checking whether CC supports -no-pie... no
|
|
|
checking whether C compiler supports __atomic_ builtins... yes
|
|
|
checking whether -latomic is needed for sub-word-sized atomic operations... failed
|
|
|
configure: error: sub-word-sized atomic operations are not available.
|
|
|
```
|
|
|
|
|
|
#### The cause and fix
|
|
|
|
|
|
If you got this error you are most likely using `nix` and `ghc.nix`. The error is caused by `emscripten` trying to write to the `emscripten_cache`, but it cannot write because the `nix store` is read-only.
|
|
|
|
|
|
The fix is hacky, we need to provide the `emscripten_cache` to emscripten so that it is mutable. Similarly, setting `EM_CACHE` to the cache in the nix store does not work. The workaround, in nix, is to copy the cache, make it writable and export `EM_CACHE` to point to the cache. This is done in the following lines in `ghc.nix`:
|
|
|
|
|
|
```
|
|
|
# we have to copy from the nixpkgs because we cannot unlink the symlink and copy
|
|
|
# similarly exporting the cache to the nix store fails during configuration
|
|
|
# for some reason
|
|
|
cp -Lr ${emscripten}/share/emscripten/cache .emscripten_cache
|
|
|
chmod u+rwX -R .emscripten_cache
|
|
|
export EM_CACHE=.emscripten_cache
|
|
|
```
|
|
|
|
|
|
See [this](https://discourse.nixos.org/t/emscripten-tries-to-write-to-nix/15263) post for more.
|
|
|
|
|
|
### JS Backend fails during stage1 with error: symbol memset missing .functype
|
|
|
|
|
|
#### The error output
|
|
|
|
|
|
This will fail during stage1 with:
|
|
|
|
|
|
```
|
|
|
/run/user/1729/ghc3035129_0/ghc_1.s:528:2: error:
|
|
|
error: symbol memset missing .functype
|
|
|
call memset
|
|
|
^
|
|
|
|
|
|
|
528 | call ...
|
|
|
```
|
|
|
`js-sources` support has been fixed for GHC. `c-sources` support is still far on the roadmap.
|
|
|
|
|
|
#### The cause and fix
|
|
|
Note that Cabal currently doesn't support cross-compilation. It would be good to make it support two toolchains: one for the host (plugins, Setup.hs), one for the target. It would also need to use the correct platform to resolve conditionals in `.cabal` files.
|
|
|
|
|
|
This error is a bug in `llvm` which `emscripten` uses to generate JS through wasm (via `wasm2js`). The fix is to use a more recent version of `llvm` or a patched version, and a more recent version of `emscripten` (at least `3.1.x`). Whichever you choose the llvm you use must include [this](https://github.com/llvm/llvm-project/commit/2368f18eb305ae9d5a4f2110c048e5daf5007992) patch, and that llvm must be the llvm that `emscripten` uses. See also [this](https://github.com/llvm/llvm-project/issues/53712) issue. Thus far this has only been problematic for nix users. If that is you please see [this](https://github.com/NixOS/nixpkgs/pull/182840) PR on nixpkgs. Other distros, such as Ubuntu have worked out of the box due to a patched llvm included in their package manager.
|
|
|
|
|
|
## References
|
|
|
|
... | ... | |