Now swiftwasm project succeeds to pass quite a few test suite, so we are planning to send some patches to upstream as Max said.
Before sending them, I want to confirm and clarify the direction of supporting WebAssembly.
Implementation design of stdlib
There are some topics to discuss for the implementation of stdlib for WebAssembly.
I know there is some discussion about implementing a subset of stdlib without ICU. But I want to use ICU to support WebAssembly incrementally. I’ve sent some patches to ICU project to build it for WebAssembly.
After these patches applied, it'll get to be able to built for WebAssembly and integrated with Swift stdlib. Actually, I prepared a patched prebuilt ICU library and it works well on WebAssembly with Swift.
Swift stdlib depends on libc but libc can’t be built for WebAssembly because pure WebAssembly doesn't have a system call interface.
wasi-libc is a bytecode alliance project which enables us to build a libc compatible library depending on WASI. As same as I want to use ICU to achieve incremental support, I want to depend on WASI to use wasi-libc also.
We need to provide some polyfills for some missing features like pthread, but swiftwasm project has already implemented them and it works well.
Basically we are focusing to run Swift executable files correctly on WebAssembly runtime, so we want to depend on ICU and WASI to support WebAssembly progressively. I also consider supporting WASI independent target in the future, but for that kind of target, we have to implement a subset of stdlib.
In addition, we want to package wasi-libc and ICU libraries in Swift toolchain of WASM. If a general SDK like wasi-sdk becomes popular in the future, we may use it. But at present, it’s difficult to install wasi-sdk, and unlike the general sysroot, the path to be installed is not fixed. For this reason, we think it is appropriate to distribute them as part of the Swift toolchain at this time.
Implementation design of runtime
As zhuowei said in https://github.com/apple/swift/pull/24684, some of Swift runtime features can’t work on WebAssembly.
Relative Pointer does not work on WASM. See the PR #24684 description for details.
The swiftwasm project has temporarily applied a hacky patch as a solution to this issue. However, the use cases of Relative Pointer have been almost completely identified, and related features also work well.
When merging to the upstream, we plan to implement some kind of abstract pointer type instead of using the RelativePointer type directly. I think that this design should be discussed with the core team more.
swifterror and swiftself don’t work on WASM also. See the PR #24684 description for details.
I have tried to solve this in several ways.
A conversion from non-throws to throws function and a conversion from thin function to thick function is the root cause of this issue.
First, I tried to emit the thunk function at the SIL level. ABI compatibility between function types is checked in the process of converting a typed AST to SIL. For example, conversion from
() -> Int type to
() -> Optional<Int> type requires thunk, but conversion from
()-> Void type to
() throws -> Void type doesn’t require thunk.
This patch https://github.com/swiftwasm/swift/pull/6 enables many cases of function calls to work on WASM. However, there was one problem with this solution.
Thunk functions were created at the SIL level, but in some cases, the functions were rewritten as
thin_to_thick_function instructions by optimization. e.g. CapturePropagation rewrites
partial_apply to a
thin_to_thick_function function. As described above,
thin_to_thick_function instruction outputs function call without thunk and it's an invalid instruction as WASM.
So I implemented IRGen as thin_to_thick_function emits valid LLVM IR for WASM. This change transforms
thin_to_thick_function into function call using LLVM IR level thunk like
partial_apply. Here is the patch PR. https://github.com/swiftwasm/swift/pull/186
These changes make output executable files no longer cause WASM runtime errors in function calls.
WebAssembly doesn't support multi-thread yet, so we run the test suite in a single thread. This makes it difficult to run some test suite that expects to be crashed. Should we ignore these kinds of suite temporarily using ifdef? or is there a better way?