Skip to content

Commit

Permalink
Merge pull request #79 from kabiroberai/kabir/wasi-base
Browse files Browse the repository at this point in the history
Generalize WASI to other Wasm engines
  • Loading branch information
kateinoigakukun authored Apr 17, 2024
2 parents 3d35845 + 3f7b005 commit d4e83dc
Show file tree
Hide file tree
Showing 15 changed files with 263 additions and 208 deletions.
27 changes: 21 additions & 6 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@ import class Foundation.ProcessInfo

let package = Package(
name: "WasmKit",
platforms: [.macOS(.v12)],
platforms: [.macOS(.v12), .iOS(.v14)],
products: [
.library(
name: "WasmKit",
targets: ["WasmKit"]
),
.library(
name: "WasmKitWASI",
targets: ["WasmKitWASI"]
),
.library(
name: "WASI",
targets: ["WASI"]
Expand All @@ -31,23 +35,34 @@ let package = Package(
name: "CLI",
dependencies: [
"WasmKit",
"WASI",
"WasmKitWASI",
.product(name: "ArgumentParser", package: "swift-argument-parser"),
.product(name: "SystemPackage", package: "swift-system"),
]
),
.target(
name: "WasmTypes"
),
.target(
name: "WASI",
dependencies: ["WasmTypes", "SystemExtras"]
),
.target(
name: "WasmKit",
dependencies: [
"WasmParser",
"SystemExtras",
"WasmTypes",
.product(name: "SystemPackage", package: "swift-system"),
]
),
.target(name: "WasmParser"),
.target(
name: "WASI",
dependencies: ["WasmKit", "SystemExtras"]
name: "WasmParser",
dependencies: ["WasmTypes"]
),
.target(
name: "WasmKitWASI",
dependencies: ["WasmKit", "WASI"]
),
.target(
name: "SystemExtras",
Expand All @@ -70,7 +85,7 @@ let package = Package(
.plugin(name: "GenerateOverlayForTesting", capability: .buildTool(), dependencies: ["WITTool"]),
.testTarget(
name: "WITOverlayGeneratorTests",
dependencies: ["WITOverlayGenerator", "WasmKit", "WASI"],
dependencies: ["WITOverlayGenerator", "WasmKit", "WasmKitWASI"],
exclude: ["Fixtures", "Compiled", "Generated"],
plugins: [.plugin(name: "GenerateOverlayForTesting")]
),
Expand Down
2 changes: 1 addition & 1 deletion Sources/CLI/Run/Run.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import ArgumentParser
import Foundation
import SystemPackage
import WASI
import WasmKitWASI
import WasmKit

struct Run: ParsableCommand {
Expand Down
2 changes: 1 addition & 1 deletion Sources/WASI/GuestMemorySupport.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import WasmKit
import WasmTypes

extension GuestPointee {
static func readFromGuest(_ pointer: inout UnsafeGuestRawPointer) -> Self {
Expand Down
4 changes: 2 additions & 2 deletions Sources/WASI/Platform/File.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ extension FdWASIFile {
let handle = FileHandle(fileDescriptor: fd.rawValue)
var nread: UInt32 = 0
for iovec in buffer {
try iovec.buffer.withHostPointer { rawBufferStart in
var bufferStart = rawBufferStart.bindMemory(
try iovec.buffer.withHostPointer(count: Int(iovec.length)) { rawBufferStart in
var bufferStart = rawBufferStart.baseAddress!.bindMemory(
to: UInt8.self, capacity: Int(iovec.length)
)
let bufferEnd = bufferStart + Int(iovec.length)
Expand Down
78 changes: 36 additions & 42 deletions Sources/WASI/WASI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ import Foundation
import SwiftShims // For swift_stdlib_random
import SystemExtras
import SystemPackage
import WasmKit
import WasmParser
import WasmTypes

protocol WASI {
/// Reads command-line argument data.
Expand Down Expand Up @@ -379,8 +378,8 @@ enum WASIAbi {
let length: WASIAbi.Size

func withHostBufferPointer<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R {
try buffer.withHostPointer { hostPointer in
try body(UnsafeRawBufferPointer(start: hostPointer, count: Int(length)))
try buffer.withHostPointer(count: Int(length)) { hostPointer in
try body(UnsafeRawBufferPointer(hostPointer))
}
}

Expand Down Expand Up @@ -782,16 +781,29 @@ enum WASIAbi {
}
}

struct WASIError: Error, CustomStringConvertible {
let description: String
public struct WASIError: Error, CustomStringConvertible {
public let description: String

public init(description: String) {
self.description = description
}
}

public struct WASIExitCode: Error {
public let code: UInt32
}

public struct WASIHostFunction {
public let type: FunctionType
public let implementation: (GuestMemory, [Value]) throws -> [Value]
}

struct WASIExitCode: Error {
let code: UInt32
public struct WASIHostModule {
public let functions: [String: WASIHostFunction]
}

extension WASI {
var _hostModules: [String: HostModule] {
var _hostModules: [String: WASIHostModule] {
let unimplementedFunctionTypes: [String: FunctionType] = [
"poll_oneoff": .init(parameters: [.i32, .i32, .i32, .i32], results: [.i32]),
"proc_raise": .init(parameters: [.i32], results: [.i32]),
Expand All @@ -803,23 +815,19 @@ extension WASI {

]

var preview1: [String: HostFunction] = unimplementedFunctionTypes.reduce(into: [:]) { functions, entry in
var preview1: [String: WASIHostFunction] = unimplementedFunctionTypes.reduce(into: [:]) { functions, entry in
let (name, type) = entry
functions[name] = HostFunction(type: type) { _, _ in
functions[name] = WASIHostFunction(type: type) { _, _ in
print("\"\(name)\" not implemented yet")
return [.i32(WASIAbi.Errno.ENOSYS.rawValue)]
}
}

func withMemoryBuffer<T>(
caller: Caller,
caller: GuestMemory,
body: (GuestMemory) throws -> T
) throws -> T {
guard case let .memory(memoryAddr) = caller.instance.exports["memory"] else {
throw WASIError(description: "Missing required \"memory\" export")
}
let memory = GuestMemory(store: caller.store, address: memoryAddr)
return try body(memory)
return try body(caller)
}

func readString(pointer: UInt32, length: UInt32, buffer: GuestMemory) throws -> String {
Expand All @@ -839,8 +847,8 @@ extension WASI {
}
}

func wasiFunction(type: FunctionType, implementation: @escaping (Caller, [Value]) throws -> [Value]) -> HostFunction {
return HostFunction(type: type) { caller, arguments in
func wasiFunction(type: FunctionType, implementation: @escaping (GuestMemory, [Value]) throws -> [Value]) -> WASIHostFunction {
return WASIHostFunction(type: type) { caller, arguments in
do {
return try implementation(caller, arguments)
} catch let errno as WASIAbi.Errno {
Expand Down Expand Up @@ -1339,7 +1347,7 @@ extension WASI {
}

return [
"wasi_snapshot_preview1": HostModule(globals: [:], functions: preview1)
"wasi_snapshot_preview1": WASIHostModule(functions: preview1)
]
}
}
Expand Down Expand Up @@ -1380,16 +1388,7 @@ public class WASIBridgeToHost: WASI {
self.fdTable = fdTable
}

public var hostModules: [String: HostModule] { _hostModules }

public func start(_ instance: ModuleInstance, runtime: Runtime) throws -> UInt32 {
do {
_ = try runtime.invoke(instance, function: "_start")
} catch let code as WASIExitCode {
return code.code
}
return 0
}
public var wasiHostModules: [String: WASIHostModule] { _hostModules }

func args_get(
argv: UnsafeGuestPointer<UnsafeGuestPointer<UInt8>>,
Expand All @@ -1402,8 +1401,7 @@ public class WASIBridgeToHost: WASI {
offsets += 1
let count = arg.utf8CString.withUnsafeBytes { bytes in
let count = UInt32(bytes.count)
buffer.raw.withHostPointer { hostRawPointer in
let hostDestBuffer = UnsafeMutableRawBufferPointer(start: hostRawPointer, count: bytes.count)
_ = buffer.raw.withHostPointer(count: bytes.count) { hostDestBuffer in
bytes.copyBytes(to: hostDestBuffer)
}
return count
Expand All @@ -1428,8 +1426,7 @@ public class WASIBridgeToHost: WASI {
offsets += 1
let count = "\(key)=\(value)".utf8CString.withUnsafeBytes { bytes in
let count = UInt32(bytes.count)
buffer.raw.withHostPointer { hostRawPointer in
let hostDestBuffer = UnsafeMutableRawBufferPointer(start: hostRawPointer, count: bytes.count)
_ = buffer.raw.withHostPointer(count: bytes.count) { hostDestBuffer in
bytes.copyBytes(to: hostDestBuffer)
}
return count
Expand Down Expand Up @@ -1630,8 +1627,7 @@ public class WASIBridgeToHost: WASI {
guard bytes.count <= maxPathLength else {
throw WASIAbi.Errno.ENAMETOOLONG
}
path.withHostPointer {
let buffer = UnsafeMutableRawBufferPointer(start: $0, count: Int(maxPathLength))
_ = path.withHostPointer(count: Int(maxPathLength)) { buffer in
bytes.copyBytes(to: buffer)
}
}
Expand Down Expand Up @@ -1692,10 +1688,7 @@ public class WASIBridgeToHost: WASI {
let copyingBytes = min(entry.dirNameLen, totalBufferSize - bufferUsed)
let rangeStart = buffer.baseAddress.raw.advanced(by: bufferUsed)
name.withUTF8 { bytes in
rangeStart.withHostPointer { rangeStart in
let hostBuffer = UnsafeMutableRawBufferPointer(
start: rangeStart, count: Int(copyingBytes)
)
_ = rangeStart.withHostPointer(count: Int(copyingBytes)) { hostBuffer in
bytes.copyBytes(to: hostBuffer, count: Int(copyingBytes))
}
}
Expand Down Expand Up @@ -1865,8 +1858,9 @@ public class WASIBridgeToHost: WASI {
}

func random_get(buffer: UnsafeGuestPointer<UInt8>, length: WASIAbi.Size) {
buffer.withHostPointer {
swift_stdlib_random(UnsafeMutableRawPointer($0), Int(length))
guard length > 0 else { return }
buffer.withHostPointer(count: Int(length)) {
swift_stdlib_random($0.baseAddress!, Int(length))
}
}
}
26 changes: 24 additions & 2 deletions Sources/WasmKit/Component/CanonicalCall.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@_exported import WasmTypes

struct CanonicalABIError: Error, CustomStringConvertible {
let description: String
}
Expand All @@ -15,8 +17,8 @@ public struct CanonicalCallContext {
/// The executing `Runtime` instance
public let runtime: Runtime
/// A reference to the guest memory.
public var guestMemory: GuestMemory {
GuestMemory(store: runtime.store, address: options.memory)
public var guestMemory: WasmKitGuestMemory {
WasmKitGuestMemory(store: runtime.store, address: options.memory)
}

public init(options: CanonicalOptions, moduleInstance: ModuleInstance, runtime: Runtime) {
Expand Down Expand Up @@ -47,3 +49,23 @@ public struct CanonicalCallContext {
return UnsafeGuestRawPointer(memorySpace: guestMemory, offset: new)
}
}

public struct WasmKitGuestMemory: GuestMemory {
private let store: Store
private let address: MemoryAddress

/// Creates a new memory instance from the given store and address
public init(store: Store, address: MemoryAddress) {
self.store = store
self.address = address
}

/// Executes the given closure with a mutable buffer pointer to the host memory region mapped as guest memory.
public func withUnsafeMutableBufferPointer<T>(offset: UInt, count: Int, _ body: (UnsafeMutableRawBufferPointer) throws -> T) rethrows -> T {
try store.withMemory(at: address) { memory in
try memory.data.withUnsafeMutableBufferPointer { buffer in
try body(UnsafeMutableRawBufferPointer(start: buffer.baseAddress! + Int(offset), count: count))
}
}
}
}
4 changes: 2 additions & 2 deletions Sources/WasmKit/Component/CanonicalLowering.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ public enum CanonicalLowering {
let newBuffer = try context.realloc(
old: 0, oldSize: 0, oldAlign: 1, newSize: UInt32(bytes.count)
)
newBuffer.withHostPointer { newBuffer in
UnsafeMutableRawBufferPointer(start: newBuffer, count: bytes.count).copyBytes(from: bytes)
newBuffer.withHostPointer(count: bytes.count) { newBuffer in
newBuffer.copyBytes(from: bytes)
}
return (newBuffer.offset, UInt32(bytes.count))
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/WasmKit/Docs.docc/Docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ This example shows how to run WASI application on WasmKit.

```swift
import WasmKit
import WASI
import WasmKitWASI
import Foundation

let bytes = try Data(contentsOf: URL(filePath: "./main.wasm"))
Expand Down
9 changes: 0 additions & 9 deletions Sources/WasmKit/Execution/Runtime/Store.swift
Original file line number Diff line number Diff line change
@@ -1,14 +1,5 @@
import WasmParser

/// > Note:
/// <https://webassembly.github.io/spec/core/exec/runtime.html#addresses>
public typealias FunctionAddress = Int
public typealias TableAddress = Int
public typealias MemoryAddress = Int
public typealias GlobalAddress = Int
public typealias ElementAddress = Int
public typealias DataAddress = Int
public typealias ExternAddress = Int
internal typealias ModuleAddress = Int

/// A collection of globals and functions that are exported from a host module.
Expand Down
Loading

0 comments on commit d4e83dc

Please sign in to comment.