Slide 1

Slide 1 text

Porting Swift to WebAssembly WebAssembly Live! Extended @kateinoigakukun 1

Slide 2

Slide 2 text

Who am I Yuta Saito (a.k.a @kateinoigakukun) • Co-maintainer of SwiftWasm • Working at Merpay, Inc. • Love Swift ! 2

Slide 3

Slide 3 text

Motivation: Why Swift? • Safe • Flexible • Not only for iOS application • Multi-paradigm • Compiled Language • Seamless C-interoperability 3

Slide 4

Slide 4 text

Motivation: Why porting to WebAssembly? • Portable • Access to Web developer community 4

Slide 5

Slide 5 text

The all story started with an issue 5

Slide 6

Slide 6 text

The story of SwiftWasm • Apr, 2019: Initial efforts made by @patcheng, @maxdesiatov and @zhuowei • Setting up basic build system for cross-compiling for Wasm • Build stdlib using WASI and wasi-libc for POSIX APIs. 6

Slide 7

Slide 7 text

The story of SwiftWasm • Sep, 2019: Started hunting Wasm specific bugs around compiler • Relative Pointer • Swift Calling Convention • etc... 7

Slide 8

Slide 8 text

Relative Pointer • Offset is 32-bit, saving lots of space on 64-bit platforms. • Calculated by PC-relative relocation designed for jumping. 8

Slide 9

Slide 9 text

Issue with Relative Pointer No PC-relative relocation like R_X86_64_PC32 at that time WASM_RELOC(R_WASM_TABLE_NUMBER_LEB, 20) WASM_RELOC(R_WASM_MEMORY_ADDR_TLS_SLEB, 21) WASM_RELOC(R_WASM_FUNCTION_OFFSET_I64, 22) +WASM_RELOC(R_WASM_MEMORY_ADDR_LOCREL_I32, 23) // S + A - P https://reviews.llvm.org/D96659 9

Slide 10

Slide 10 text

Swift Calling Convention declare swiftcc void @foo(i32, i32) @data = global i8* bitcast (void (i32, i32)* @foo to i8*) define swiftcc void @bar() { %1 = load i8*, i8** @data %2 = bitcast i8* %1 to void (i32, i32, i32, i32)* ; Crash on WebAssembly! ; >> call_indirect to a signature that does not match call swiftcc void %2(i32 1, i32 2, i32 swiftself 0, swifterror 0) %3 = bitcast i8* %1 to void (i32, i32, i32)* ; Crash on WebAssembly! ; >> call_indirect to a signature that does not match call swiftcc void %3(i32 1, i32 2, i32 swiftself 0) ret void } 10

Slide 11

Slide 11 text

Swift Calling Convention ; lowered as: ; declare swiftcc void @foo(i32, i32, swiftself i32, swifterror i32); declare swiftcc void @foo(i32, i32) @data = global i8* bitcast (void (i32, i32)* @foo to i8*) define void @main() { %1 = load i8*, i8** @data %2 = bitcast i8* %1 to void (i32, i32, i32, i32)* call swiftcc void %2(i32 1, i32 2, i32 swiftself 0, swifterror 0) %3 = bitcast i8* %1 to void (i32, i32, i32)* ; lowered as: ; call swiftcc void %2(i32 1, i32 2, i32 swiftself 0, i32 swifterror 0) call swiftcc void %3(i32 1, i32 2, i32 swiftself 0) ret void } https://reviews.llvm.org/D76049 11

Slide 12

Slide 12 text

The story of SwiftWasm • Jan, 2020: Develop WebAssembly debugger https://github.com/kateinoigakukun/wasminspect 12

Slide 13

Slide 13 text

The story of SwiftWasm • Mar, 2020: Passing all standard library test suites 13

Slide 14

Slide 14 text

The story of SwiftWasm • Mar, 2020: Continuous Integration and Delivery 14

Slide 15

Slide 15 text

The story of SwiftWasm • Apr, 2020: Published JavaScriptKit: JavaScript interoperation • Jul, 2020: Published Tokamak: SwiftUI compatible UI framework for Wasm 15

Slide 16

Slide 16 text

Tokamak: SwiftUI compatible UI framework for Wasm import TokamakDOM struct Counter: View { @State var count: Int = 0 var body: some View { VStack { Text("\(count)") HStack { Button("Reset") { count = 0 } Button("Increment") { count += 1 } } } } } Try it out!: https://pad.swiftwasm.org/ 16

Slide 17

Slide 17 text

The story of SwiftWasm • Nov, 2020: Swift cross-module DCE to reduce binary size • Jan, 2021: Experimental concurrency support with JS event loop 17

Slide 18

Slide 18 text

import JavaScriptKit let fetch = JSObject.global.fetch.function!.async func printZen() async throws { let result = try await fetch("https://api.github.com/zen").bject! let text = try await result.asyncing.text!() print(text) } JavaScriptEventLoop.runAsync { try! await printZen() } 18

Slide 19

Slide 19 text

import JavaScriptKit let fetch = JSObject.global.fetch.function!.async func printZen() async throws { let result = try await fetch("https://api.github.com/zen").object! let text = try await result.asyncing.text!() print(text) } JavaScriptEventLoop.runAsync { try! await printZen() } 19

Slide 20

Slide 20 text

import JavaScriptKit let fetch = JSObject.global.fetch.function!.async func printZen() async throws { let result = try await fetch("https://api.github.com/zen").object! let text = try await result.asyncing.text!() print(text) } JavaScriptEventLoop.runAsync { try! await printZen() } 20

Slide 21

Slide 21 text

import JavaScriptKit let fetch = JSObject.global.fetch.function!.async func printZen() async throws { let result = try await fetch("https://api.github.com/zen").object! let text = try await result.asyncing.text!() print(text) } JavaScriptEventLoop.runAsync { try! await printZen() } 21

Slide 22

Slide 22 text

import JavaScriptKit let fetch = JSObject.global.fetch.function!.async func printZen() async throws { let result = try await fetch("https://api.github.com/zen").object! let text = try await result.asyncing.text!() print(text) } JavaScriptEventLoop.runAsync { try! await printZen() } 22

Slide 23

Slide 23 text

import JavaScriptKit let fetch = JSObject.global.fetch.function!.async func printZen() async throws { let result = try await fetch("https://api.github.com/zen").object! let text = try await result.asyncing.text!() print(text) } JavaScriptEventLoop.runAsync { try! await printZen() } 23

Slide 24

Slide 24 text

Future Works • WASI-less "bare metal" target • Performance and Binary size improvement 24

Slide 25

Slide 25 text

Contributions are welcome! • Website: https://swiftwasm.org • GitHub: https://github.com/swiftwasm 25

Slide 26

Slide 26 text

Thanks! • All SwiftWasm contributors and sponsors • The Swift community 26