Building Swift Concurrency for aarch64-none-none-elf in Embedded Swift 6.4

Hi everyone,

I was working on a bare-metal kernel AetherKernel for the Raspberry Pi 4 (Cortex-A72) using Embedded Swift. Since the natural target for a bare-metal AArch64 kernel is aarch64-none-none-elf, I hit a blocker: the toolchain does not build or ship libswift_Concurrency.a for this triple.

To get the concurrency runtime onto the board, I'm using a workaround: I target arm64-apple-none-macho instead (same AArch64 ISA, Mach-O container), -force_load its concurrency library, and post-process the Mach-O binary into a flat kernel8.img. This works great—async/await and a custom SerialExecutor run successfully on the hardware—but it means compiling a non-Apple bare-metal kernel through an Apple-platform triple purely to obtain the _Concurrency archive.

The Build System Root Cause

During the Swift 6.4 cycle, several updates have expanded non-Darwin embedded concurrency support:

  • Commit f161a60e1213 extended the macOS-host arch filter in stdlib/public/Concurrency/CMakeLists.txt to add ARMv8-M targets (armv8m.main|armv8.1m.main).
  • Commit 01ac09a1792e fixed SDK macro handling for non-Darwin embedded targets.

This indicates that _Concurrency is actively being brought up across the embedded target matrix. However, the ELF spelling of ARM64 (aarch64) appears to have been missed.

Looking at the build configuration in release/6.4.x (verified at commit 9140a9cd4ec5):

First, aarch64-none-none-elf is defined as a first-class embedded stdlib target in stdlib/public/CMakeLists.txt:

if("AArch64" IN_LIST LLVM_TARGETS_TO_BUILD)
  list(APPEND EMBEDDED_STDLIB_TARGET_TRIPLES
    "aarch64  aarch64-none-none-elf     aarch64-none-none-elf"
  )
endif()

Second, in stdlib/public/Concurrency/CMakeLists.txt, the macOS-host architecture filter drops it during the loop over EMBEDDED_STDLIB_TARGET_TRIPLES (line 322):

elseif (SWIFT_HOST_VARIANT STREQUAL "macosx")
  if(NOT "${mod}" MATCHES "x86_64|arm64|arm64e|armv7|armv7m|armv7em|armv8m.main|armv8.1m.main")
    continue()        # line 324 — mod="aarch64-none-none-elf" matches nothing here
  endif()

arm64-apple-none-macho matches arm64 and builds, but aarch64-none-none-elf matches nothing (since arm64 is not a substring of aarch64) and gets skipped. The Linux-host path (lines 318-321) also skips it.

Third, the CPU-define branch (line 337) only matches arm64 and misses aarch64:

elseif("${arch}" MATCHES "arm64")
  list(APPEND extra_c_compile_flags -DTARGET_CPU_ARM64=1)

Proposed Fix

Since the architecturally identical Mach-O triple already compiles and runs this runtime on bare metal, there doesn't seem to be an architectural blocker.

The fix appears to be adjusting two lines in stdlib/public/Concurrency/CMakeLists.txt:

  1. Add aarch64 to the host arch filter: "x86_64|aarch64|arm64|..."
  2. Update the CPU define: elseif("${arch}" MATCHES "arm64|aarch64")

Is this omission intentional, or is it an oversight? If it's the latter, I'd be happy to open a pull request to resolve this. I'd also appreciate any guidance on how the working group prefers build-configuration changes like this to be verified/tested in the compiler repo.

Thanks!

2 Likes

GitHub - infektyd/AetherKernel: Bare-metal Embedded Swift kernel for Raspberry Pi 4 — async/await on real hardware via a custom SerialExecutor, no OS underneath · GitHub this what I was working on.