Reimplement the Linux HAL in native Swift using @c#2
Merged
Conversation
lhoward
force-pushed
the
native-swift-hal
branch
from
July 20, 2026 00:20
e58e298 to
f72f731
Compare
Migrate the entire swifthal_* HAL from ~2,900 lines of C to native Swift, exporting the identical C ABI via the Swift 6.3 @c attribute (SE-0495) so SwiftIO/AsyncSwiftIO link against it unchanged. Structure - Split the single C target into CLinuxHalSwiftIO (residual C) and a new Swift target LinuxHalSwiftIO (the public product), which @_exported imports the C module so `import LinuxHalSwiftIO` still surfaces the HAL declarations. - All subsystems are now Swift: spi, i2c, uart, gpio, os, platform, fs, timer, counter, and the adc/eth/i2s/lcd/pwm/wifi stubs. - gpio and timer use the Swift Dispatch overlay (no libdispatch/Blocks C runtime); gpio drives libgpiod directly. The interrupt block entry point, consumed only by AsyncSwiftIO, is now a native Swift closure API rather than an Objective-C block. Minimal C that remains (things Swift cannot express), named with the existing `swifthal_<subsys>__<helper>` internal convention: - swift_platform_hwcycle.c: hwcycle_get/to_ns (inline assembly). - swift_uart_native.c: termios2 line configuration (asm/termbits.h conflicts with Glibc's termios.h, so it cannot be exposed to Swift). - swift_hal_native.h: static-inline wrappers for ioctl request-number macros (SPI/I2C), variadic mq_open, and syslog, plus SPI mode-bit constants. Pre-existing bugs fixed along the way - os mq_recv tested `!= 0`, but mq_receive returns a byte count, so it returned -errno on every successful receive; corrected to `< 0`. - The RNG was defined as swiftHal_randomGet, which never matched the declared swifthal_random_get symbol and so could not be linked; renamed to match. - The SPI loopback test built SPI in a stored property, crashing test discovery on any machine without SPI hardware; made it lazy. Toolchain: CI bumped swift:6.2 -> swift:6.3 (@c requires 6.3). Verification: swift build (library, examples, tests) is green; a symbol audit confirms each swifthal_* symbol is defined exactly once (Swift for logic, C only for hwcycle + termios2); HALNativeTests (semaphore limit, mutex, RNG, uptime) pass, and an mq round-trip / sleep smoke check passed during development. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019ps1JiW8Nh8mbFERFeR9DV
lhoward
force-pushed
the
native-swift-hal
branch
from
July 20, 2026 01:00
f72f731 to
5f86dff
Compare
- Factor the dispatch-source suspend/resume/teardown patterns shared by GPIO and Timer into a DispatchSourceProtocol extension, and add handle() unwrappers instead of repeated Unmanaged boilerplate - GPIO: release the source fully in the event-handler error path rather than leaving a cancelled-but-installed source behind - FS: factor the dirent type/size fill shared by readdir and fs_stat; DT_UNKNOWN entries are now stat'ed once instead of twice - Counter: implement tick conversions as microseconds (1 MHz) instead of stubbing them to zero, making counter_read meaningful - Stubs: replace the memset spelling of LCD format zeroing with a plain pointee assignment - Correct stale comments (swift_uart_native.h prototypes, CLinuxHalSwiftIO target contents) and document the x86 TSC calibration cost Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015rhut2AwGnktQ4msgGo6z1
lhoward
force-pushed
the
native-swift-hal
branch
from
July 20, 2026 01:20
dfe35cf to
1be9c83
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Migrates the entire
swifthal_*HAL from ~2,900 lines of hand-written C to native Swift, exporting the identical C ABI via the Swift 6.3@cattribute (SE-0495).SwiftIO/AsyncSwiftIOlink against it unchanged — no caller sees the language change.Structure
CLinuxHalSwiftIO(residual C) and a new Swift targetLinuxHalSwiftIO(the public product). The Swift target@_exported imports the C module, soimport LinuxHalSwiftIOstill surfaces the HAL declarations to consumers.Dispatchoverlay (no libdispatch/Blocks C runtime); gpio drives libgpiod directly. The interrupt "block" entry point — consumed only by AsyncSwiftIO — becomes a native Swift closure API instead of an Objective-C block.Minimal C that remains
Things Swift genuinely cannot express, named with the existing
swifthal_<subsys>__<helper>internal convention:swift_platform_hwcycle.c—hwcycle_get/hwcycle_to_ns(inline assembly).swift_uart_native.c— termios2 line configuration (asm/termbits.hcollides with Glibc'stermios.h, so it can't be exposed to Swift).swift_hal_native.h—static inlinewrappers for ioctl request-number macros (SPI/I2C), variadicmq_open,syslog, plus SPI mode-bit constants.Pre-existing bugs fixed along the way
mq_recvtested!= 0, butmq_receivereturns a byte count, so it returned-errnoon every successful receive; corrected to< 0.swiftHal_randomGet, which never matched the declaredswifthal_random_getsymbol and so could not be linked; renamed to match the ABI.SPIin a stored property, crashing test discovery on any machine without SPI hardware; made itlazy.Toolchain
CI bumped
swift:6.2→swift:6.3(@crequires 6.3).Verification
swift build(library, examples, tests) is green.swifthal_*symbol is defined exactly once — Swift for logic, C only for hwcycle + termios2 helpers; no duplicates.HALNativeTests(semaphore limit enforcement, mutex, RNG fill, uptime) pass; an mq round-trip / sleep smoke check passed during development. The SPI loopback test still requires real hardware.Post-review fixes
A multi-agent code review of the branch produced eight confirmed findings. Five were migration regressions, fixed in
31af742:timeout == -1) looped forever instead of returning after onereadBufferpass, as the C did.transceiveused a trappingUInt32(...)on the transfer length; uart likewise trapped convertingnremaintoCInt. Both now truncate, matching C.-ENOSYS, leaving callers reading stack garbage.Two further findings were faithful ports of the original C behavior that were nonetheless worth improving, done separately in
e58e298:readdiraborted the whole enumeration when aDT_REGentry lost a race withunlink; it now skips the entry, matching theDT_UNKNOWNpath.interrupt_callback_uninstallonly cancelled the dispatch source, so a laterinterrupt_enablesilently resumed a permanently-cancelled source; it now releases it, and the later call returns-EINVAL.One finding is deliberately unaddressed:
fs_write/fs_readtruncate a single transfer larger than 2 GiB. That matches the original(int)nbytesand is not a realistic single-call scenario, so it stays a pure port.🤖 Generated with Claude Code