From 72eade3143d18682f9146eacd269e9d14f59a592 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Fri, 10 Oct 2025 15:20:19 +0100 Subject: [PATCH 1/8] Fix most of the errors when building WasmKit for WASI Wasm is the only unsupported platform for WasmKit in the Swift Package Index build matrix. One possible use case is when an implementation for a future Wasm feature is emulated with older Wasm features, when running WasmKit itself in an older Wasm engine. The only remaining roadblock is the lack of `swiftasynccall`, which prevents `_CWasmKit` module from building, but the rest of the modules seem to build fine. For proper testing we also need to fix `swift test` in the Swift toolchain. --- Package.swift | 2 +- Sources/SystemExtras/Clock.swift | 16 ++-- Sources/SystemExtras/Constants.swift | 11 +-- Sources/SystemExtras/FileAtOperations.swift | 2 + Sources/SystemExtras/FileOperations.swift | 23 +++++- Sources/SystemExtras/Syscalls.swift | 4 +- Sources/WASI/Platform/PlatformTypes.swift | 16 ++-- .../Platform/SandboxPrimitives/Open.swift | 2 + Sources/WASI/WASI.swift | 4 +- Sources/WIT/PackageResolver.swift | 74 +++++++++---------- Sources/WITExtractor/SwiftAPIDigester.swift | 2 +- 11 files changed, 95 insertions(+), 61 deletions(-) diff --git a/Package.swift b/Package.swift index cc6f66cf..520184c9 100644 --- a/Package.swift +++ b/Package.swift @@ -121,7 +121,7 @@ let package = Package( if ProcessInfo.processInfo.environment["SWIFTCI_USE_LOCAL_DEPS"] == nil { package.dependencies += [ .package(url: "https://github.com/apple/swift-argument-parser", from: "1.2.2"), - .package(url: "https://github.com/apple/swift-system", .upToNextMajor(from: "1.3.0")), + .package(url: "https://github.com/apple/swift-system", from: "1.6.0"), ] } else { package.dependencies += [ diff --git a/Sources/SystemExtras/Clock.swift b/Sources/SystemExtras/Clock.swift index b0282d20..85a2d1c8 100644 --- a/Sources/SystemExtras/Clock.swift +++ b/Sources/SystemExtras/Clock.swift @@ -12,6 +12,8 @@ import Android #elseif os(Windows) import CSystem import ucrt +#elseif os(WASI) +import WASILibc #else #error("Unsupported Platform") #endif @@ -41,12 +43,12 @@ extension Clock { public static var rawMonotonic: Clock { Clock(rawValue: _CLOCK_MONOTONIC_RAW) } #endif - #if SYSTEM_PACKAGE_DARWIN || os(Linux) || os(Android) || os(OpenBSD) || os(FreeBSD) || os(WASI) + #if SYSTEM_PACKAGE_DARWIN || os(Linux) || os(Android) || os(OpenBSD) || os(FreeBSD) @_alwaysEmitIntoClient public static var monotonic: Clock { Clock(rawValue: _CLOCK_MONOTONIC) } #endif - #if os(OpenBSD) || os(FreeBSD) || os(WASI) + #if os(OpenBSD) || os(FreeBSD) @_alwaysEmitIntoClient public static var uptime: Clock { Clock(rawValue: _CLOCK_UPTIME) } #endif @@ -92,10 +94,10 @@ extension Clock { public var rawValue: CInterop.TimeSpec @_alwaysEmitIntoClient - public var seconds: Int { rawValue.tv_sec } + public var seconds: Int64 { .init(rawValue.tv_sec) } @_alwaysEmitIntoClient - public var nanoseconds: Int { rawValue.tv_nsec } + public var nanoseconds: Int64 { .init(rawValue.tv_nsec) } @_alwaysEmitIntoClient public init(rawValue: CInterop.TimeSpec) { @@ -104,17 +106,17 @@ extension Clock { @_alwaysEmitIntoClient public init(seconds: Int, nanoseconds: Int) { - self.init(rawValue: CInterop.TimeSpec(tv_sec: seconds, tv_nsec: nanoseconds)) + self.init(rawValue: CInterop.TimeSpec(tv_sec: .init(seconds), tv_nsec: nanoseconds)) } @_alwaysEmitIntoClient public static var now: TimeSpec { - return TimeSpec(rawValue: CInterop.TimeSpec(tv_sec: 0, tv_nsec: Int(_UTIME_NOW))) + return TimeSpec(rawValue: CInterop.TimeSpec(tv_sec: 0, tv_nsec: Int(UTIME_NOW))) } @_alwaysEmitIntoClient public static var omit: TimeSpec { - return TimeSpec(rawValue: CInterop.TimeSpec(tv_sec: 0, tv_nsec: Int(_UTIME_OMIT))) + return TimeSpec(rawValue: CInterop.TimeSpec(tv_sec: 0, tv_nsec: Int(UTIME_OMIT))) } } } diff --git a/Sources/SystemExtras/Constants.swift b/Sources/SystemExtras/Constants.swift index 1c48c0ed..29a81d6a 100644 --- a/Sources/SystemExtras/Constants.swift +++ b/Sources/SystemExtras/Constants.swift @@ -12,6 +12,8 @@ import Android #elseif os(Windows) import CSystem import ucrt +#elseif os(WASI) +import WASILibc #else #error("Unsupported Platform") #endif @@ -43,7 +45,7 @@ internal var _AT_NO_AUTOMOUNT: CInt { AT_NO_AUTOMOUNT } #endif */ -#if !os(Windows) +#if !os(Windows) && !os(WASI) @_alwaysEmitIntoClient internal var _F_GETFL: CInt { F_GETFL } @_alwaysEmitIntoClient @@ -61,7 +63,7 @@ internal var _O_SYNC: CInt { O_SYNC } internal var _O_RSYNC: CInt { O_RSYNC } #endif -#if !os(Windows) +#if !os(Windows) && !os(WASI) @_alwaysEmitIntoClient internal var _UTIME_NOW: CInt { #if os(Linux) || os(Android) @@ -132,16 +134,15 @@ internal var _CLOCK_BOOTTIME: CInterop.ClockId { CLOCK_BOOTTIME } @_alwaysEmitIntoClient internal var _CLOCK_MONOTONIC_RAW: CInterop.ClockId { CLOCK_MONOTONIC_RAW } #endif -#if SYSTEM_PACKAGE_DARWIN || os(Linux) || os(Android) || os(OpenBSD) || os(FreeBSD) || os(WASI) +#if SYSTEM_PACKAGE_DARWIN || os(Linux) || os(Android) || os(OpenBSD) || os(FreeBSD) @_alwaysEmitIntoClient internal var _CLOCK_MONOTONIC: CInterop.ClockId { CLOCK_MONOTONIC } #endif - #if SYSTEM_PACKAGE_DARWIN @_alwaysEmitIntoClient internal var _CLOCK_UPTIME_RAW: CInterop.ClockId { CLOCK_UPTIME_RAW } #endif -#if os(OpenBSD) || os(FreeBSD) || os(WASI) +#if os(OpenBSD) || os(FreeBSD) @_alwaysEmitIntoClient internal var _CLOCK_UPTIME: CInterop.ClockId { CLOCK_UPTIME } #endif diff --git a/Sources/SystemExtras/FileAtOperations.swift b/Sources/SystemExtras/FileAtOperations.swift index 5f8f3838..7baf2847 100644 --- a/Sources/SystemExtras/FileAtOperations.swift +++ b/Sources/SystemExtras/FileAtOperations.swift @@ -12,6 +12,8 @@ import Android #elseif os(Windows) import ucrt import WinSDK +#elseif os(WASI) +import WASILibc #else #error("Unsupported Platform") #endif diff --git a/Sources/SystemExtras/FileOperations.swift b/Sources/SystemExtras/FileOperations.swift index fc61b1d8..7b6f1902 100644 --- a/Sources/SystemExtras/FileOperations.swift +++ b/Sources/SystemExtras/FileOperations.swift @@ -12,6 +12,8 @@ import Android #elseif os(Windows) import ucrt import WinSDK +#elseif os(WASI) +import WASILibc #else #error("Unsupported Platform") #endif @@ -344,6 +346,8 @@ extension FileDescriptor { #if os(Windows) // FIXME: Need to query by `NtQueryInformationFile`? return .success(OpenOptions(rawValue: 0)) + #elseif os(WASI) + return .failure(Errno.notSupported) #else valueOrErrno(retryOnInterrupt: false) { system_fcntl(self.rawValue, _F_GETFL) @@ -511,15 +515,31 @@ extension FileDescriptor { @_alwaysEmitIntoClient public var name: String { +#if os(WASI) + // ClangImporter can't handle `char d_name[]`, but it's right after `unsigned char d_type`. + withUnsafePointer(to: &rawValue.pointee.d_type) { dType in + let dName = dType + 1 + return dName.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout.size(ofValue: dName)) { + // String initializer copies the given buffer contents, so it's safe. + return String(cString: $0) + } + } +#else withUnsafePointer(to: &rawValue.pointee.d_name) { dName in dName.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout.size(ofValue: dName)) { // String initializer copies the given buffer contents, so it's safe. return String(cString: $0) } } + #endif } public var fileType: FileType { +#if os(WASI) + // https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md#-filetype-variant + // https://github.com/WebAssembly/wasi-libc/blob/6b45da5b05bc0edda355a6de46101d4b21631985/libc-bottom-half/headers/public/wasi/api.h#L780C9-L780C32 + FileType(rawValue: .init(rawValue.pointee.d_type)) +#else switch CInt(rawValue.pointee.d_type) { case _DT_REG: return .file case _DT_BLK: return .blockDevice @@ -529,6 +549,7 @@ extension FileDescriptor { case _DT_SOCK: return .socket default: return .unknown } +#endif } } @@ -657,7 +678,7 @@ public enum FileTime { public typealias FileTime = Clock.TimeSpec #endif -#if !os(Windows) +#if !os(Windows) && !os(WASI) // MARK: - Synchronized Input and Output diff --git a/Sources/SystemExtras/Syscalls.swift b/Sources/SystemExtras/Syscalls.swift index 3a8aa578..00ee641f 100644 --- a/Sources/SystemExtras/Syscalls.swift +++ b/Sources/SystemExtras/Syscalls.swift @@ -12,6 +12,8 @@ import Android #elseif os(Windows) import ucrt import WinSDK +#elseif os(WASI) +import WASILibc #else #error("Unsupported Platform") #endif @@ -127,7 +129,7 @@ internal func system_symlinkat( extension CInterop { #if SYSTEM_PACKAGE_DARWIN public typealias DirP = UnsafeMutablePointer - #elseif os(Linux) || os(Android) + #elseif os(Linux) || os(Android) || os(WASI) public typealias DirP = OpaquePointer #else #error("Unsupported Platform") diff --git a/Sources/WASI/Platform/PlatformTypes.swift b/Sources/WASI/Platform/PlatformTypes.swift index 6b0637ac..647a4ce2 100644 --- a/Sources/WASI/Platform/PlatformTypes.swift +++ b/Sources/WASI/Platform/PlatformTypes.swift @@ -40,15 +40,17 @@ extension WASIAbi.Fdflags { if platformOpenOptions.contains(.append) { fdFlags.insert(.APPEND) } - if platformOpenOptions.contains(.dataSync) { - fdFlags.insert(.DSYNC) - } if platformOpenOptions.contains(.nonBlocking) { fdFlags.insert(.NONBLOCK) } +#if !os(WASI) + if platformOpenOptions.contains(.dataSync) { + fdFlags.insert(.DSYNC) + } if platformOpenOptions.contains(.fileSync) { fdFlags.insert(.SYNC) } +#endif #if os(Linux) if platformOpenOptions.contains(.readSync) { fdFlags.insert(.RSYNC) @@ -64,15 +66,17 @@ extension WASIAbi.Fdflags { flags.insert(.append) } #if !os(Windows) - if self.contains(.DSYNC) { - flags.insert(.dataSync) - } if self.contains(.NONBLOCK) { flags.insert(.nonBlocking) } +#if !os(WASI) + if self.contains(.DSYNC) { + flags.insert(.dataSync) + } if self.contains(.SYNC) { flags.insert(.fileSync) } +#endif #if os(Linux) if self.contains(.RSYNC) { flags.insert(.readSync) diff --git a/Sources/WASI/Platform/SandboxPrimitives/Open.swift b/Sources/WASI/Platform/SandboxPrimitives/Open.swift index a99f7bd0..830353df 100644 --- a/Sources/WASI/Platform/SandboxPrimitives/Open.swift +++ b/Sources/WASI/Platform/SandboxPrimitives/Open.swift @@ -15,6 +15,8 @@ import SystemPackage #elseif os(Windows) import CSystem import ucrt +#elseif os(WASI) + import WASILibc #else #error("Unsupported Platform") #endif diff --git a/Sources/WASI/WASI.swift b/Sources/WASI/WASI.swift index 1e4896e5..5bd648ce 100644 --- a/Sources/WASI/WASI.swift +++ b/Sources/WASI/WASI.swift @@ -12,6 +12,8 @@ import WasmTypes import Android #elseif os(Windows) import ucrt +#elseif os(WASI) + import WASILibc #else #error("Unsupported Platform") #endif @@ -1391,7 +1393,7 @@ public class WASIBridgeToHost: WASI { fdTable[2] = .file(StdioFileEntry(fd: stderr, accessMode: .write)) for (guestPath, hostPath) in preopens { - #if os(Windows) + #if os(Windows) || os(WASI) let fd = try FileDescriptor.open(FilePath(hostPath), .readWrite) #else let fd = try hostPath.withCString { cHostPath in diff --git a/Sources/WIT/PackageResolver.swift b/Sources/WIT/PackageResolver.swift index cfd1e29a..4eaff8da 100644 --- a/Sources/WIT/PackageResolver.swift +++ b/Sources/WIT/PackageResolver.swift @@ -195,52 +195,50 @@ extension SourceFileSyntax { } } -#if !os(WASI) - /// A ``PackageFileLoader`` adapter for local file system. - public struct LocalFileLoader: PackageFileLoader { - public typealias FilePath = String +/// A ``PackageFileLoader`` adapter for local file system. +public struct LocalFileLoader: PackageFileLoader { + public typealias FilePath = String - let fileManager: FileManager + let fileManager: FileManager - public init(fileManager: FileManager = .default) { - self.fileManager = fileManager - } + public init(fileManager: FileManager = .default) { + self.fileManager = fileManager + } - enum Error: Swift.Error { - case failedToLoadFile(FilePath) - } + enum Error: Swift.Error { + case failedToLoadFile(FilePath) + } - private func isDirectory(filePath: String) -> Bool { - var isDirectory: ObjCBool = false - let exists = fileManager.fileExists(atPath: filePath, isDirectory: &isDirectory) - return exists && isDirectory.boolValue - } + private func isDirectory(filePath: String) -> Bool { + var isDirectory: ObjCBool = false + let exists = fileManager.fileExists(atPath: filePath, isDirectory: &isDirectory) + return exists && isDirectory.boolValue + } - public func contentsOfWITFile(at filePath: String) throws -> String { - guard let bytes = fileManager.contents(atPath: filePath) else { - throw Error.failedToLoadFile(filePath) - } - return String(decoding: bytes, as: UTF8.self) + public func contentsOfWITFile(at filePath: String) throws -> String { + guard let bytes = fileManager.contents(atPath: filePath) else { + throw Error.failedToLoadFile(filePath) } + return String(decoding: bytes, as: UTF8.self) + } - public func packageFiles(in packageDirectory: String) throws -> [String] { - let dirURL = URL(fileURLWithPath: packageDirectory) - return try fileManager.contentsOfDirectory(atPath: packageDirectory).filter { fileName in - return fileName.hasSuffix(".wit") - && { - let filePath = dirURL.appendingPathComponent(fileName) - return !isDirectory(filePath: filePath.path) - }() - } - .map { dirURL.appendingPathComponent($0).path } + public func packageFiles(in packageDirectory: String) throws -> [String] { + let dirURL = URL(fileURLWithPath: packageDirectory) + return try fileManager.contentsOfDirectory(atPath: packageDirectory).filter { fileName in + return fileName.hasSuffix(".wit") + && { + let filePath = dirURL.appendingPathComponent(fileName) + return !isDirectory(filePath: filePath.path) + }() } + .map { dirURL.appendingPathComponent($0).path } + } - public func dependencyDirectories(from packageDirectory: String) throws -> [String] { - let dirURL = URL(fileURLWithPath: packageDirectory) - let depsDir = dirURL.appendingPathComponent("deps") - guard isDirectory(filePath: depsDir.path) else { return [] } - return try fileManager.contentsOfDirectory(atPath: depsDir.path) - } + public func dependencyDirectories(from packageDirectory: String) throws -> [String] { + let dirURL = URL(fileURLWithPath: packageDirectory) + let depsDir = dirURL.appendingPathComponent("deps") + guard isDirectory(filePath: depsDir.path) else { return [] } + return try fileManager.contentsOfDirectory(atPath: depsDir.path) } +} -#endif diff --git a/Sources/WITExtractor/SwiftAPIDigester.swift b/Sources/WITExtractor/SwiftAPIDigester.swift index 5c6b2604..a60c6d93 100644 --- a/Sources/WITExtractor/SwiftAPIDigester.swift +++ b/Sources/WITExtractor/SwiftAPIDigester.swift @@ -118,7 +118,7 @@ struct SwiftAPIDigester { @available(macOS 11, iOS 14.0, watchOS 7.0, tvOS 14.0, *) func dumpSDK(moduleName: String, arguments: [String]) throws -> Output { - #if os(iOS) || os(watchOS) || os(tvOS) || os(visionOS) + #if os(iOS) || os(watchOS) || os(tvOS) || os(visionOS) || os(WASI) fatalError("WITExtractor does not support platforms where Foundation.Process is unavailable") #else var args = [ From d9b4c5da1288ae88f769291b0cc0dde4acbccf4c Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Fri, 10 Oct 2025 15:26:14 +0100 Subject: [PATCH 2/8] `UTIME_OMIT`/`UTIME_NOW` only available in Musl-based libcs --- Sources/SystemExtras/Clock.swift | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Sources/SystemExtras/Clock.swift b/Sources/SystemExtras/Clock.swift index 85a2d1c8..8733aedc 100644 --- a/Sources/SystemExtras/Clock.swift +++ b/Sources/SystemExtras/Clock.swift @@ -111,12 +111,20 @@ extension Clock { @_alwaysEmitIntoClient public static var now: TimeSpec { +#if os(WASI) return TimeSpec(rawValue: CInterop.TimeSpec(tv_sec: 0, tv_nsec: Int(UTIME_NOW))) +#else + return TimeSpec(rawValue: CInterop.TimeSpec(tv_sec: 0, tv_nsec: Int(_UTIME_NOW))) +#endif } @_alwaysEmitIntoClient public static var omit: TimeSpec { +#if os(WASI) return TimeSpec(rawValue: CInterop.TimeSpec(tv_sec: 0, tv_nsec: Int(UTIME_OMIT))) +#else + return TimeSpec(rawValue: CInterop.TimeSpec(tv_sec: 0, tv_nsec: Int(_UTIME_OMIT))) +#endif } } } From 112a223f2227a04822e90db2b864c4c9475dc66a Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Fri, 10 Oct 2025 16:22:03 +0100 Subject: [PATCH 3/8] Disable direct threading for WASI, add `CSystemExtras` --- Package.swift | 7 ++- Sources/CLI/Commands/Run.swift | 8 +++- Sources/CSystemExtras/CSystemExtras.c | 0 Sources/CSystemExtras/include/clock.h | 5 ++ Sources/SystemExtras/Clock.swift | 6 +++ Sources/WASI/Clock.swift | 4 +- Sources/WASI/Platform/PlatformTypes.swift | 32 ++++++------- .../Platform/SandboxPrimitives/Open.swift | 8 +++- Sources/WIT/PackageResolver.swift | 1 - Sources/WasmKit/Engine.swift | 16 +++++-- .../Execution/DispatchInstruction.swift | 2 + Sources/WasmKit/Execution/Execution.swift | 46 ++++++++++--------- .../Instructions/InstructionSupport.swift | 6 ++- Sources/_CWasmKit/include/Platform.h | 2 +- Sources/_CWasmKit/include/_CWasmKit.h | 2 + Utilities/Sources/VMGen.swift | 2 + 16 files changed, 92 insertions(+), 55 deletions(-) create mode 100644 Sources/CSystemExtras/CSystemExtras.c create mode 100644 Sources/CSystemExtras/include/clock.h diff --git a/Package.swift b/Package.swift index 520184c9..68eeab8c 100644 --- a/Package.swift +++ b/Package.swift @@ -89,7 +89,8 @@ let package = Package( .target( name: "SystemExtras", dependencies: [ - .product(name: "SystemPackage", package: "swift-system") + .product(name: "SystemPackage", package: "swift-system"), + .target(name: "CSystemExtras", condition: .when(platforms: [.wasi])), ], exclude: ["CMakeLists.txt"], swiftSettings: [ @@ -97,6 +98,8 @@ let package = Package( ] ), + .target(name: "CSystemExtras"), + .executableTarget( name: "WITTool", dependencies: [ @@ -121,7 +124,7 @@ let package = Package( if ProcessInfo.processInfo.environment["SWIFTCI_USE_LOCAL_DEPS"] == nil { package.dependencies += [ .package(url: "https://github.com/apple/swift-argument-parser", from: "1.2.2"), - .package(url: "https://github.com/apple/swift-system", from: "1.6.0"), + .package(url: "https://github.com/apple/swift-system", from: "1.5.0"), ] } else { package.dependencies += [ diff --git a/Sources/CLI/Commands/Run.swift b/Sources/CLI/Commands/Run.swift index e7461fae..7c525617 100644 --- a/Sources/CLI/Commands/Run.swift +++ b/Sources/CLI/Commands/Run.swift @@ -59,12 +59,16 @@ struct Run: ParsableCommand { var directories: [String] = [] enum ThreadingModel: String, ExpressibleByArgument, CaseIterable { - case direct + #if !os(WASI) + case direct + #endif case token func resolve() -> EngineConfiguration.ThreadingModel { switch self { - case .direct: return .direct + #if !os(WASI) + case .direct: return .direct + #endif case .token: return .token } } diff --git a/Sources/CSystemExtras/CSystemExtras.c b/Sources/CSystemExtras/CSystemExtras.c new file mode 100644 index 00000000..e69de29b diff --git a/Sources/CSystemExtras/include/clock.h b/Sources/CSystemExtras/include/clock.h new file mode 100644 index 00000000..ddcdc6fa --- /dev/null +++ b/Sources/CSystemExtras/include/clock.h @@ -0,0 +1,5 @@ +#include + +inline static clockid_t csystemextras_monotonic_clockid() { + return CLOCK_MONOTONIC; +} diff --git a/Sources/SystemExtras/Clock.swift b/Sources/SystemExtras/Clock.swift index 8733aedc..3a040258 100644 --- a/Sources/SystemExtras/Clock.swift +++ b/Sources/SystemExtras/Clock.swift @@ -13,6 +13,7 @@ import Android import CSystem import ucrt #elseif os(WASI) +import CSystemExtras import WASILibc #else #error("Unsupported Platform") @@ -48,6 +49,11 @@ extension Clock { public static var monotonic: Clock { Clock(rawValue: _CLOCK_MONOTONIC) } #endif + #if os(WASI) + @_alwaysEmitIntoClient + public static var monotonic: Clock { Clock(rawValue: csystemextras_monotonic_clockid()) } + #endif + #if os(OpenBSD) || os(FreeBSD) @_alwaysEmitIntoClient public static var uptime: Clock { Clock(rawValue: _CLOCK_UPTIME) } diff --git a/Sources/WASI/Clock.swift b/Sources/WASI/Clock.swift index c84aee5c..8b096b82 100644 --- a/Sources/WASI/Clock.swift +++ b/Sources/WASI/Clock.swift @@ -92,12 +92,10 @@ public protocol MonotonicClock { /// A monotonic clock that uses the system's monotonic clock. public struct SystemMonotonicClock: MonotonicClock { private var underlying: SystemExtras.Clock { - #if os(Linux) || os(Android) + #if os(Linux) || os(Android) || os(WASI) return .monotonic #elseif os(macOS) || os(iOS) || os(watchOS) || os(tvOS) || os(visionOS) return .rawUptime - #elseif os(WASI) - return .monotonic #elseif os(OpenBSD) || os(FreeBSD) return .uptime #else diff --git a/Sources/WASI/Platform/PlatformTypes.swift b/Sources/WASI/Platform/PlatformTypes.swift index 647a4ce2..9d0be178 100644 --- a/Sources/WASI/Platform/PlatformTypes.swift +++ b/Sources/WASI/Platform/PlatformTypes.swift @@ -43,14 +43,14 @@ extension WASIAbi.Fdflags { if platformOpenOptions.contains(.nonBlocking) { fdFlags.insert(.NONBLOCK) } -#if !os(WASI) - if platformOpenOptions.contains(.dataSync) { - fdFlags.insert(.DSYNC) - } - if platformOpenOptions.contains(.fileSync) { - fdFlags.insert(.SYNC) - } -#endif + #if !os(WASI) + if platformOpenOptions.contains(.dataSync) { + fdFlags.insert(.DSYNC) + } + if platformOpenOptions.contains(.fileSync) { + fdFlags.insert(.SYNC) + } + #endif #if os(Linux) if platformOpenOptions.contains(.readSync) { fdFlags.insert(.RSYNC) @@ -69,14 +69,14 @@ extension WASIAbi.Fdflags { if self.contains(.NONBLOCK) { flags.insert(.nonBlocking) } -#if !os(WASI) - if self.contains(.DSYNC) { - flags.insert(.dataSync) - } - if self.contains(.SYNC) { - flags.insert(.fileSync) - } -#endif + #if !os(WASI) + if self.contains(.DSYNC) { + flags.insert(.dataSync) + } + if self.contains(.SYNC) { + flags.insert(.fileSync) + } + #endif #if os(Linux) if self.contains(.RSYNC) { flags.insert(.readSync) diff --git a/Sources/WASI/Platform/SandboxPrimitives/Open.swift b/Sources/WASI/Platform/SandboxPrimitives/Open.swift index 830353df..d3439571 100644 --- a/Sources/WASI/Platform/SandboxPrimitives/Open.swift +++ b/Sources/WASI/Platform/SandboxPrimitives/Open.swift @@ -129,13 +129,17 @@ struct PathResolution { throw openErrno } - try self.symlink(component: component) + #if os(WASI) + throw Errno.notSupported + #else + try self.symlink(component: component) + #endif #endif } } } - #if !os(Windows) + #if !os(Windows) && !os(WASI) mutating func symlink(component: FilePath.Component) throws { /// Thin wrapper around readlinkat(2) func _readlinkat(_ fd: CInt, _ path: UnsafePointer) throws -> FilePath { diff --git a/Sources/WIT/PackageResolver.swift b/Sources/WIT/PackageResolver.swift index 4eaff8da..2bd95145 100644 --- a/Sources/WIT/PackageResolver.swift +++ b/Sources/WIT/PackageResolver.swift @@ -241,4 +241,3 @@ public struct LocalFileLoader: PackageFileLoader { return try fileManager.contentsOfDirectory(atPath: depsDir.path) } } - diff --git a/Sources/WasmKit/Engine.swift b/Sources/WasmKit/Engine.swift index c559fa47..3de2c87e 100644 --- a/Sources/WasmKit/Engine.swift +++ b/Sources/WasmKit/Engine.swift @@ -34,10 +34,12 @@ public struct EngineConfiguration { /// The threading model, which determines how to dispatch instruction /// execution, to use for the virtual machine interpreter. public enum ThreadingModel { - /// Direct threaded code - /// - Note: This is the default model for platforms that support - /// `musttail` calls. - case direct + #if !os(WASI) + /// Direct threaded code + /// - Note: This is the default model for platforms that support + /// `musttail` calls. + case direct + #endif /// Indirect threaded code /// - Note: This is a fallback model for platforms that do not support /// `musttail` calls. @@ -48,7 +50,11 @@ public struct EngineConfiguration { } static var defaultForCurrentPlatform: ThreadingModel { - return useDirectThreadedCode ? .direct : .token + #if os(WASI) + return .token + #else + return useDirectThreadedCode ? .direct : .token + #endif } } diff --git a/Sources/WasmKit/Execution/DispatchInstruction.swift b/Sources/WasmKit/Execution/DispatchInstruction.swift index e21fa2f6..edb09a8c 100644 --- a/Sources/WasmKit/Execution/DispatchInstruction.swift +++ b/Sources/WasmKit/Execution/DispatchInstruction.swift @@ -1930,6 +1930,7 @@ extension Execution { } } +#if !os(WASI) extension Instruction { /// The tail-calling execution handler for the instruction. var handler: UInt { @@ -1941,3 +1942,4 @@ extension Instruction { } } } +#endif diff --git a/Sources/WasmKit/Execution/Execution.swift b/Sources/WasmKit/Execution/Execution.swift index 4f86cb75..2281a42b 100644 --- a/Sources/WasmKit/Execution/Execution.swift +++ b/Sources/WasmKit/Execution/Execution.swift @@ -385,8 +385,10 @@ extension Execution { ) do { switch self.store.value.engine.configuration.threadingModel { - case .direct: - try runDirectThreaded(sp: sp, pc: pc, md: md, ms: ms) + #if !os(WASI) + case .direct: + try runDirectThreaded(sp: sp, pc: pc, md: md, ms: ms) + #endif case .token: try runTokenThreaded(sp: &sp, pc: &pc, md: &md, ms: &ms) } @@ -395,27 +397,29 @@ extension Execution { } } - /// Starts the main execution loop using the direct threading model. - @inline(never) - mutating func runDirectThreaded( - sp: Sp, pc: Pc, md: Md, ms: Ms - ) throws { - var pc = pc - let handler = pc.read(wasmkit_tc_exec.self) - wasmkit_tc_start(handler, sp, pc, md, ms, &self) - if let (rawError, trappingSp) = self.trap { - let error = unsafeBitCast(rawError, to: Error.self) - // Manually release the error object because the trap is caught in C and - // held as a raw pointer. - wasmkit_swift_errorRelease(rawError) - - guard let trap = error as? Trap else { - throw error + #if !os(WASI) + /// Starts the main execution loop using the direct threading model. + @inline(never) + mutating func runDirectThreaded( + sp: Sp, pc: Pc, md: Md, ms: Ms + ) throws { + var pc = pc + let handler = pc.read(wasmkit_tc_exec.self) + wasmkit_tc_start(handler, sp, pc, md, ms, &self) + if let (rawError, trappingSp) = self.trap { + let error = unsafeBitCast(rawError, to: Error.self) + // Manually release the error object because the trap is caught in C and + // held as a raw pointer. + wasmkit_swift_errorRelease(rawError) + + guard let trap = error as? Trap else { + throw error + } + // Attach backtrace if the thrown error is a trap + throw trap.withBacktrace(Self.captureBacktrace(sp: trappingSp, store: store.value)) } - // Attach backtrace if the thrown error is a trap - throw trap.withBacktrace(Self.captureBacktrace(sp: trappingSp, store: store.value)) } - } + #endif #if EngineStats /// A helper structure for collecting instruction statistics. diff --git a/Sources/WasmKit/Execution/Instructions/InstructionSupport.swift b/Sources/WasmKit/Execution/Instructions/InstructionSupport.swift index 2888bee8..885b291a 100644 --- a/Sources/WasmKit/Execution/Instructions/InstructionSupport.swift +++ b/Sources/WasmKit/Execution/Instructions/InstructionSupport.swift @@ -220,8 +220,10 @@ typealias OpcodeID = UInt64 extension Instruction { func headSlot(threadingModel: EngineConfiguration.ThreadingModel) -> CodeSlot { switch threadingModel { - case .direct: - return CodeSlot(handler) + #if !os(WASI) + case .direct: + return CodeSlot(handler) + #endif case .token: return opcodeID } diff --git a/Sources/_CWasmKit/include/Platform.h b/Sources/_CWasmKit/include/Platform.h index ec4ef3c1..65ab4099 100644 --- a/Sources/_CWasmKit/include/Platform.h +++ b/Sources/_CWasmKit/include/Platform.h @@ -5,7 +5,7 @@ // rather than a language standard feature after LLVM 19. We check // `__has_attribute(swiftasynccc)` too for compatibility with older versions. // See https://github.com/llvm/llvm-project/pull/85347 -#if __has_feature(swiftasynccc) || __has_extension(swiftasynccc) +#if !defined(__wasi__) && (__has_feature(swiftasynccc) || __has_extension(swiftasynccc)) # define WASMKIT_HAS_SWIFTASYNCCC 1 #else # define WASMKIT_HAS_SWIFTASYNCCC 0 diff --git a/Sources/_CWasmKit/include/_CWasmKit.h b/Sources/_CWasmKit/include/_CWasmKit.h index ad7e41d1..6876aa37 100644 --- a/Sources/_CWasmKit/include/_CWasmKit.h +++ b/Sources/_CWasmKit/include/_CWasmKit.h @@ -17,6 +17,7 @@ typedef void *_Nullable Pc; typedef void *_Nullable Md; typedef size_t Ms; +#if WASMKIT_USE_DIRECT_THREADED_CODE /// The function type for executing a single instruction and transitioning to /// the next instruction by tail calling. `swiftasync` calling convention is /// used to keep `state` in the context register and to force tail calling. @@ -40,6 +41,7 @@ static inline void wasmkit_tc_start( ) { exec(sp, pc, md, ms, state); } +#endif static inline void wasmkit_fwrite_stderr(const char *_Nonnull str, size_t len) { fwrite(str, 1, len, stderr); diff --git a/Utilities/Sources/VMGen.swift b/Utilities/Sources/VMGen.swift index dabb0b5b..3669f020 100644 --- a/Utilities/Sources/VMGen.swift +++ b/Utilities/Sources/VMGen.swift @@ -460,6 +460,7 @@ enum VMGen { + generateDirectThreadedCode(instructions: instructions, inlineImpls: inlineImpls) + """ + #if !os(WASI) extension Instruction { /// The tail-calling execution handler for the instruction. var handler: UInt { @@ -471,6 +472,7 @@ enum VMGen { } } } + #endif """ ), From bc220f5fa055240ed811e50b3d00870922e8d2d9 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Fri, 10 Oct 2025 16:28:31 +0100 Subject: [PATCH 4/8] Add WASI job to `main.yml` workflow --- .github/workflows/main.yml | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6778c1cb..62effd74 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -126,7 +126,7 @@ jobs: wasi-swift-sdk-checksum: "b64dfad9e1c9ccdf06f35cf9b1a00317e000df0c0de0b3eb9f49d6db0fcba4d9" test-args: "-Xswiftc -DWASMKIT_CI_TOOLCHAIN_NIGHTLY" - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 name: "build-linux (${{ matrix.swift }})" steps: @@ -240,7 +240,7 @@ jobs: run: swift test build-cmake: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 container: image: swift:6.2-noble steps: @@ -254,3 +254,16 @@ jobs: - run: cmake -G Ninja -B ./build - run: cmake --build ./build - run: ./build/bin/wasmkit-cli --version + + build-wasi: + runs-on: ubuntu-24.04 + container: + image: swift:6.2-noble + steps: + - uses: actions/checkout@v4 + - name: Install jq + run: apt-get update && apt-get install -y jq + - name: Install Swift SDK + run: swift sdk install https://download.swift.org/swift-6.2-branch/wasm-sdk/swift-6.2-DEVELOPMENT-SNAPSHOT-2025-10-09-a/swift-6.2-DEVELOPMENT-SNAPSHOT-2025-10-09-a_wasm.artifactbundle.tar.gz --checksum 048667dbddd50bfb797d09d194bd0c1c53b251a121f4b870402154b0adab556e + - run: swift build --swift-sdk "$(swiftc -print-target-info | jq -r '.swiftCompilerTag')_wasm" + From fb998b0cd48064f0e86d644790633853b73781ec Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Fri, 10 Oct 2025 16:31:17 +0100 Subject: [PATCH 5/8] Use correct 6.2 Swift SDK in `main.yml` workflow --- .github/workflows/main.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 62effd74..f88eacb8 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -264,6 +264,7 @@ jobs: - name: Install jq run: apt-get update && apt-get install -y jq - name: Install Swift SDK - run: swift sdk install https://download.swift.org/swift-6.2-branch/wasm-sdk/swift-6.2-DEVELOPMENT-SNAPSHOT-2025-10-09-a/swift-6.2-DEVELOPMENT-SNAPSHOT-2025-10-09-a_wasm.artifactbundle.tar.gz --checksum 048667dbddd50bfb797d09d194bd0c1c53b251a121f4b870402154b0adab556e - - run: swift build --swift-sdk "$(swiftc -print-target-info | jq -r '.swiftCompilerTag')_wasm" + run: swift sdk install https://download.swift.org/swift-6.2-release/wasm/swift-6.2-RELEASE/swift-6.2-RELEASE_wasm.artifactbundle.tar.gz --checksum fe4e8648309fce86ea522e9e0d1dc48e82df6ba6e5743dbf0c53db8429fb5224 + - name: Build with the Swift SDK + run: swift build --swift-sdk "$(swiftc -print-target-info | jq -r '.swiftCompilerTag')_wasm" From 454ff4b559e33171c1920df3157c4e21eb26ea61 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Fri, 10 Oct 2025 16:35:46 +0100 Subject: [PATCH 6/8] Bring back direct threading decls back on WASI --- Sources/WasmKit/Engine.swift | 10 ++++---- .../Execution/DispatchInstruction.swift | 6 +++-- Sources/WasmKit/Execution/Execution.swift | 24 +++++++++---------- .../Instructions/InstructionSupport.swift | 6 ++--- Utilities/Sources/VMGen.swift | 6 +++-- 5 files changed, 26 insertions(+), 26 deletions(-) diff --git a/Sources/WasmKit/Engine.swift b/Sources/WasmKit/Engine.swift index 3de2c87e..d8f82b5c 100644 --- a/Sources/WasmKit/Engine.swift +++ b/Sources/WasmKit/Engine.swift @@ -34,12 +34,10 @@ public struct EngineConfiguration { /// The threading model, which determines how to dispatch instruction /// execution, to use for the virtual machine interpreter. public enum ThreadingModel { - #if !os(WASI) - /// Direct threaded code - /// - Note: This is the default model for platforms that support - /// `musttail` calls. - case direct - #endif + /// Direct threaded code + /// - Note: This is the default model for platforms that support + /// `musttail` calls. + case direct /// Indirect threaded code /// - Note: This is a fallback model for platforms that do not support /// `musttail` calls. diff --git a/Sources/WasmKit/Execution/DispatchInstruction.swift b/Sources/WasmKit/Execution/DispatchInstruction.swift index edb09a8c..51f0a288 100644 --- a/Sources/WasmKit/Execution/DispatchInstruction.swift +++ b/Sources/WasmKit/Execution/DispatchInstruction.swift @@ -1930,16 +1930,18 @@ extension Execution { } } -#if !os(WASI) extension Instruction { /// The tail-calling execution handler for the instruction. var handler: UInt { + #if os(WASI) + fatalError("Direct threading is not supported on WASI") + #else return withUnsafePointer(to: wasmkit_tc_exec_handlers) { let count = MemoryLayout.size(ofValue: wasmkit_tc_exec_handlers) / MemoryLayout.size return $0.withMemoryRebound(to: UInt.self, capacity: count) { $0[Int(self.opcodeID)] } } + #endif } } -#endif diff --git a/Sources/WasmKit/Execution/Execution.swift b/Sources/WasmKit/Execution/Execution.swift index 2281a42b..b0d09dfa 100644 --- a/Sources/WasmKit/Execution/Execution.swift +++ b/Sources/WasmKit/Execution/Execution.swift @@ -385,10 +385,8 @@ extension Execution { ) do { switch self.store.value.engine.configuration.threadingModel { - #if !os(WASI) - case .direct: - try runDirectThreaded(sp: sp, pc: pc, md: md, ms: ms) - #endif + case .direct: + try runDirectThreaded(sp: sp, pc: pc, md: md, ms: ms) case .token: try runTokenThreaded(sp: &sp, pc: &pc, md: &md, ms: &ms) } @@ -397,12 +395,14 @@ extension Execution { } } - #if !os(WASI) - /// Starts the main execution loop using the direct threading model. - @inline(never) - mutating func runDirectThreaded( - sp: Sp, pc: Pc, md: Md, ms: Ms - ) throws { + /// Starts the main execution loop using the direct threading model. + @inline(never) + mutating func runDirectThreaded( + sp: Sp, pc: Pc, md: Md, ms: Ms + ) throws { + #if os(WASI) + fatalError("Direct threading is not supported on WASI") + #else var pc = pc let handler = pc.read(wasmkit_tc_exec.self) wasmkit_tc_start(handler, sp, pc, md, ms, &self) @@ -418,8 +418,8 @@ extension Execution { // Attach backtrace if the thrown error is a trap throw trap.withBacktrace(Self.captureBacktrace(sp: trappingSp, store: store.value)) } - } - #endif + #endif + } #if EngineStats /// A helper structure for collecting instruction statistics. diff --git a/Sources/WasmKit/Execution/Instructions/InstructionSupport.swift b/Sources/WasmKit/Execution/Instructions/InstructionSupport.swift index 885b291a..2888bee8 100644 --- a/Sources/WasmKit/Execution/Instructions/InstructionSupport.swift +++ b/Sources/WasmKit/Execution/Instructions/InstructionSupport.swift @@ -220,10 +220,8 @@ typealias OpcodeID = UInt64 extension Instruction { func headSlot(threadingModel: EngineConfiguration.ThreadingModel) -> CodeSlot { switch threadingModel { - #if !os(WASI) - case .direct: - return CodeSlot(handler) - #endif + case .direct: + return CodeSlot(handler) case .token: return opcodeID } diff --git a/Utilities/Sources/VMGen.swift b/Utilities/Sources/VMGen.swift index 3669f020..c82b8645 100644 --- a/Utilities/Sources/VMGen.swift +++ b/Utilities/Sources/VMGen.swift @@ -460,19 +460,21 @@ enum VMGen { + generateDirectThreadedCode(instructions: instructions, inlineImpls: inlineImpls) + """ - #if !os(WASI) extension Instruction { /// The tail-calling execution handler for the instruction. var handler: UInt { + #if os(WASI) + fatalError("Direct threading is not supported on WASI") + #else return withUnsafePointer(to: wasmkit_tc_exec_handlers) { let count = MemoryLayout.size(ofValue: wasmkit_tc_exec_handlers) / MemoryLayout.size return $0.withMemoryRebound(to: UInt.self, capacity: count) { $0[Int(self.opcodeID)] } } + #endif } } - #endif """ ), From ea6525e9b0db8558d265e3f4ddea02e2b313153e Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Fri, 10 Oct 2025 16:40:23 +0100 Subject: [PATCH 7/8] Bump `swift-argument-parser` to 1.6.2 --- Package.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index 68eeab8c..1f856ab2 100644 --- a/Package.swift +++ b/Package.swift @@ -123,7 +123,7 @@ let package = Package( if ProcessInfo.processInfo.environment["SWIFTCI_USE_LOCAL_DEPS"] == nil { package.dependencies += [ - .package(url: "https://github.com/apple/swift-argument-parser", from: "1.2.2"), + .package(url: "https://github.com/apple/swift-argument-parser", branch: "release/1.6.2"), .package(url: "https://github.com/apple/swift-system", from: "1.5.0"), ] } else { From 90f4dc76b406942064c8298154eff587e7c805d5 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Fri, 10 Oct 2025 17:53:10 +0100 Subject: [PATCH 8/8] Lower SAP requirement to 1.5.1 in `Package.swift` --- Package.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index 1f856ab2..aa82983a 100644 --- a/Package.swift +++ b/Package.swift @@ -123,7 +123,7 @@ let package = Package( if ProcessInfo.processInfo.environment["SWIFTCI_USE_LOCAL_DEPS"] == nil { package.dependencies += [ - .package(url: "https://github.com/apple/swift-argument-parser", branch: "release/1.6.2"), + .package(url: "https://github.com/apple/swift-argument-parser", from: "1.5.1"), .package(url: "https://github.com/apple/swift-system", from: "1.5.0"), ] } else {