SultanC
  1. Home
  2. Documentation
  3. Reference

Targets

Documentation pages

Targets

sultanc targets lists the 38 registered compilation targets. This page records, for each one, which backend generates its code, what standard library support it has, how its executable is linked and how it is verified. It also describes where target-specific code lives in the repository, for contributors who add or port a target.

Backends

SultanC has two code-generation backends, selected with --backend=native or --backend=llvm.

The native backend is SultanC's own code generator and, with the built-in linkers, needs no external toolchain. It is available for the x86_64 and aarch64 targets of Linux, Android, FreeBSD, macOS and Windows (x86_64-linux, aarch64-linux-gnu, x86_64-android, aarch64-android, x86_64-freebsd, aarch64-freebsd, arm64-darwin, x86_64-darwin, x86_64-windows-msvc, aarch64-windows-msvc) and for wasm32-wasi and wasm32-browser. Requesting it for any other target reports SULTANC-BACKEND-UNAVAILABLE.

The LLVM backend is available for every target and requires a compiler built with LLVM (./build.sh --enable-llvm). It produces objects for every target. Targets without an object format (NVPTX, XCore) are compiled with --emit=asm, which works for every target. The status of each target under the LLVM backend, with what was run and what was only built, is kept in LLVM targets progress.

The الدعم (support) column of sultanc targets carries one of three values: مضيف ذاتي (self-hosted, arm64-darwin and x86_64-linux), ترجمة متقاطعة (cross-compilation, every other target) and تجريبي (experimental, none today). The compiler also compiles itself on the Android and FreeBSD x86_64 and aarch64 targets; Self-hosting on Android and FreeBSD records how that was verified.

Target matrix

Operating-system targets

These targets have the full standard library, including files, processes, sockets and threads (except where noted). All of them use raw kernel calls or the platform's system interface and link no libc; Platform dependencies lists every import.

Target Architecture Backend Executable Verified run
x86_64-linux x86_64 native, LLVM built-in ELF linker native
aarch64-linux-gnu aarch64 native, LLVM built-in ELF linker qemu-user
riscv64-linux-gnu riscv64 LLVM built-in ELF linker qemu-user
loongarch64-linux-gnu loongarch64 LLVM built-in ELF linker qemu-user
powerpc64le-linux-gnu powerpc64le LLVM built-in ELF linker qemu-user
s390x-linux-gnu s390x LLVM built-in ELF linker qemu-user
mips64el-linux-gnuabi64 mips64el (n64) LLVM built-in ELF linker qemu-user
sparcv9-linux-gnu sparcv9 LLVM built-in ELF linker qemu-user
i686-linux-gnu x86 LLVM built-in ELF linker qemu-user
armv7-linux-gnueabihf arm LLVM built-in ELF linker qemu-user
x86_64-android x86_64 native, LLVM built-in ELF linker native (static executable on the Linux kernel)
aarch64-android aarch64 native, LLVM built-in ELF linker qemu-user
armv7-android arm LLVM built-in ELF linker qemu-user
arm64-darwin aarch64 native, LLVM built-in Mach-O linker natively on arm64 (native backend)
x86_64-darwin x86_64 native, LLVM built-in Mach-O linker built
x86_64-windows-msvc x86_64 native, LLVM built-in COFF linker Wine
aarch64-windows-msvc aarch64 native, LLVM built-in COFF linker built
x86_64-freebsd x86_64 native, LLVM built-in ELF linker FreeBSD 14.5 VM under qemu
aarch64-freebsd aarch64 native, LLVM built-in ELF linker FreeBSD 14.5 VM under qemu
riscv64-freebsd riscv64 LLVM built-in ELF linker FreeBSD 14.5 VM under qemu

Linux and Android targets use the Linux kernel ABI directly. The 32-bit targets (i686-linux-gnu, armv7-linux-gnueabihf, armv7-android) use the time64 and large-file system calls. On them, and on every other target that lacks a divide instruction or a wide divide, the backend expands division in IR so no compiler-rt is linked (see What lives where).

The FreeBSD targets use raw system calls with no libc. They require FreeBSD 13 or later (for close_range) and are built against the 14.2 headers. The executable is static, with ELFOSABI_FREEBSD and the .note.tag ABI note, which the built-in linker keeps and covers with a PT_NOTE header.

Linking

The compiler chooses the linker; there is no option to pick one.

Output Linker
ELF executables of x86_64 and aarch64 (Linux, Android, FreeBSD), riscv64 (Linux, FreeBSD), loongarch64, i686, armv7 (Linux, Android), powerpc64le, s390x, mips64el and sparcv9 Linux SultanC's ELF linker (compiler/link/elf): ELF64 and ELF32, little- and big-endian images. riscv64, loongarch64 and the 32-bit and big-endian targets get static executables only, which is what SultanC programs are.
Mach-O, COFF and WebAssembly executables SultanC's Mach-O, COFF and wasm linkers
freestanding and accelerator targets none: objects or assembly (Cortex-M images are linked outside the compiler, see Cortex-M)

A hosted ELF target the ELF linker does not lay out would be linked by ld.lld -static -e _start (compiler/driver/external_linker.sn) only when its target policy names ld.lld; none does now. ld.lld is found in this order: $SULTANC_LINKER; bin/ld.lld of the LLVM installation whose libLLVM the compiler is linked with (the directory next to its lib/); ld.lld on PATH.

A link input the target's linker does not read (no object, archive, library or linker script) is reported as SULTANC-LINK-INPUT-UNSUPPORTED with its path. For macOS and iOS a text-based dylib stub of an Apple SDK (.tbd, as MacOSX.sdk/usr/lib/libSystem.tbd) stands for the dylib its install name names, as with ld64: the image loads that dylib and binds the symbols the stub exports to it.

WebAssembly

Target Backend Executable Notes
wasm32-wasi native, LLVM built-in wasm linker the full std over WASI preview 1; sockets are limited to preopened ones; no processes, threads or file locks. Runs under a wasmtime-compatible runner.
wasm32-browser native, LLVM built-in wasm linker freestanding: language core only. The module has no imports; its _start returns main's value, and node runs it.
wasm64-unknown-unknown LLVM objects freestanding: language core only. The wasm linker lays out 32-bit memories only.

Freestanding and accelerator targets

These targets have no OS layer; programs use the language core and std modules that do not depend on an operating system.

Target Architecture Output
riscv32-unknown-elf riscv32 objects, assembly
avr-unknown-unknown avr objects, assembly
msp430-unknown-elf msp430 objects, assembly
hexagon-unknown-elf hexagon objects, assembly
lanai-unknown-unknown lanai objects, assembly
bpfel-unknown-none bpf objects, assembly
amdgcn-amd-amdhsa amdgpu objects, assembly
spirv64-unknown-unknown spirv64 SPIR-V objects, assembly
nvptx64-nvidia-cuda nvptx64 assembly only
xcore-unknown-unknown xcore assembly only
thumbv6m-none-eabi arm (ARMv6-M) objects, assembly; Cortex-M images
thumbv7m-none-eabi arm (ARMv7-M) objects, assembly; Cortex-M images
thumbv7em-none-eabi arm (ARMv7E-M) objects, assembly; Cortex-M images
thumbv7em-none-eabihf arm (ARMv7E-M, FPU) objects, assembly; Cortex-M images

These targets cannot be linked into an executable by SultanC: requesting exe reports SULTANC-LINK-OUTPUT-UNSUPPORTED. Use -c or --emit=obj for objects and --emit=asm for assembly. tests/en/target/language_core.sn (records, tagged unions, generics, and integer arithmetic of every width) compiles for each of them. A program that uses an OS service (printing, files) is rejected for them at check with SULTANC-TARGET-FEATURE-UNSUPPORTED; the compiler itself is such a program, so sultanc check compiler/main.sn passes for the 21 hosted targets only. Function values are closures whose environment the runtime allocates, and these targets have no allocator, so a program that takes a function value is rejected for them (SULTANC-BACKEND-UNSUPPORTED-CONSTRUCT). BPF before ISA v4 has no signed division; signed division divides the magnitudes there. eBPF has no floating point, so LLVM rejects float arithmetic for it.

Cortex-M targets

The four Arm M-profile targets are bare-metal ELF targets of the LLVM backend (the native backend has no 32-bit Arm code generator). Their target conditions are الهدف.المعمارية == "arm" with الهدف.المنصة == "freestanding", and environment eabi or eabihf.

Target Cores Processor baseline Floating point Divide std atomics QEMU boards
thumbv6m-none-eabi Cortex-M0, M0+, M1 cortex-m0, +strict-align,+atomics-32 none expanded in IR 32-bit load, store, fence microbit
thumbv7m-none-eabi Cortex-M3 cortex-m3 none SDIV/UDIV 32-bit load, store, fence lm3s6965evb, mps2-an385
thumbv7em-none-eabi Cortex-M4, M7 cortex-m4 (DSP), FPU off none SDIV/UDIV 32-bit load, store, fence mps2-an386, mps2-an500
thumbv7em-none-eabihf Cortex-M4F, M7 cortex-m4, +vfp4d16sp (FPv4-SP-D16) f32 in hardware SDIV/UDIV 32-bit load, store, fence mps2-an386, mps2-an500

The facts live in compiler/backend/target/target_registry.sn (the M-profile class is the target's processor, target_model.sn صنف_ذراع_إم) and are passed to LLVM as CPU and feature strings (أساس_توليد_ذراع_إم).

  • ABI: AAPCS, little-endian ELF32 relocatable objects with Arm build attributes (CPU name, architecture profile M, Tag_ABI_align_preserved: the stack is 8-byte aligned at every public interface). eabi passes floats in core registers; eabihf uses AAPCS-VFP (s0-s15, d0-d7), which LLVM selects from the environment.
  • No run-time library. SultanC links neither libgcc nor compiler-rt, so no object for these targets references an __aeabi_* routine. 64-bit division is expanded in IR as on every 32-bit target. On ARMv6-M, which has no divide instruction, no 32 x 32 -> 64 multiply and no 64-bit shift, 32-bit division, 64-bit multiplication and 64-bit shifts by a variable amount are expanded too (compiler/backend/provider/llvm/freestanding_routines.sn) instead of calling __aeabi_uidiv, __aeabi_lmul, __aeabi_llsl, __aeabi_llsr and __aeabi_lasr.
  • Floating point. A float operation the FPU cannot do would call a soft-float routine (__aeabi_fadd, __aeabi_dmul, __aeabi_f2d, ...). It is rejected instead, with SULTANC-TARGET-FEATURE-UNSUPPORTED at the source statement that holds the operation, naming the operation, its type and the source function: every float arithmetic, comparison or conversion on the eabi targets, every f64 one on thumbv7em-none-eabihf, and frem everywhere. Loading, storing and moving floats, negation, abs and copysign are allowed. A Cortex-M7 runs thumbv7em-none-eabihf code; its double-precision FPv5-D16 needs a processor selection the registry does not offer yet.
  • Atomics. ARMv6-M has no exclusive accesses (LDREX/STREX); ARMv7-M has them up to 32 bits only. On these targets std.sync.atomic declares the 32-bit atomic loads and stores and the fences (atomic_u32, atomic_u32_new, atomic_u32_load, atomic_u32_store, atomic_fence), which are LDR/STR with DMB. The 64-bit and atomic_usize forms and every read-modify-write operation are declared for other targets only (SULTANC-TARGET-FEATURE-UNSUPPORTED for the Arabic names; the English aliases report SULTANC-NAME-UNKNOWN). The target conditions the bootstrap compilers parse cannot tell ARMv6-M from ARMv7-M, so the 32-bit read-modify-write atomics of ARMv7-M are not offered yet. Any atomic wider than the target does inline is rejected by the backend.
  • Like every freestanding target, these have no allocator: strings, vectors and closures are rejected.

Start-up code and linking

The compiler emits the start-up into the object of every Cortex-M program with an entry, as it emits _start for the hosted targets (compiler/backend/provider/llvm/cortex_m_entry.sn), so no assembler or C toolchain is involved:

  • .vector_table: the initial stack pointer __stack_top, Reset_Handler and the 14 system exceptions, each a weak alias of a spinning Default_Handler;
  • Reset_Handler copies .data from FLASH to RAM, zeroes .bss, enables the FPU on thumbv7em-none-eabihf (CPACR CP10/CP11), runs .init_array, calls main and passes the low word of its result to __sultanc_exit, whose weak default stops the core in a WFI loop;
  • __sultanc_semihosting_exit (Arm semihosting SYS_EXIT_EXTENDED) and __sultanc_semihosting_fault (exit status 132; a SultanC trap is UDF, which escalates to a HardFault), for runs under QEMU or a debugger.

ARMv6-M instructions only, so one sequence serves every class. The linker scripts in library/std/os/platform/none/arm/cortex_m are data files the linker is given explicitly:

File Contents
cortex_m.ld the section layout: vector table at the start of FLASH, .text, .rodata, .init_array, .ARM.extab/.ARM.exidx in FLASH, .data in RAM loaded from FLASH, .bss in RAM, __stack_top at the end of RAM, and the bounds the reset handler reads
board_mps2.ld, board_lm3s6965.ld, board_nrf51.ld the FLASH and RAM regions of the QEMU boards, then INCLUDE cortex_m.ld
semihosting.ld __sultanc_exit = __sultanc_semihosting_exit; and HardFault_Handler = __sultanc_semihosting_fault;, for QEMU and debugger runs only: on hardware with no debugger, BKPT faults

SultanC does not link these targets: requesting exe reports SULTANC-LINK-OUTPUT-UNSUPPORTED. The built-in ELF linker has no Arm relocations or linker-script support yet. Link an image with ld.lld, for example:

./build/sultanc --backend=llvm --target=thumbv7m-none-eabi --emit=obj program.sn -o program.o
D=library/std/os/platform/none/arm/cortex_m
ld.lld -T $D/board_mps2.ld -L $D --gc-sections program.o $D/semihosting.ld -o program.elf
qemu-system-arm -M mps2-an385 -nographic -monitor none -serial none \
  -semihosting-config enable=on,target=native -kernel program.elf; echo $?

tools/tests/cortex_m_tests.sh does this for every test in tests/{en,ar}/target that names a Cortex-M target and runs each image on the target's QEMU boards.

Not yet supported: ve-linux-gnu

ve-linux-gnu (NEC Vector Engine) compiles to objects and assembly but has no std OS layer. VE has no kernel trap: a system call is a request to the VEOS daemon. The number and arguments are stored in a per-thread shared-memory area whose address the VEOS and glibc start-up places in the thread control block (tcbhead_t.shm_offset, glibc-ve sysdeps/unix/sysv/linux/ve/sysdep.h), and then monc is issued. Porting needs that start-up protocol and VE hardware or an emulator to verify it.

What lives where

Layer Owner Notes
Kernel ABI (syscall numbers, flags, record layouts, byte order) std: library/std/os/platform/** Linux tables are generated from each architecture's kernel UAPI headers (tools/generate/generate_linux_syscalls.py, generate_linux_uapi.py). The 32-bit ABIs take the ilp32: rows of tools/generate/registry/linux_syscalls.def (clock_gettime64, ppoll_time64, mmap2, _llseek, fcntl64). FreeBSD's are generated from its headers (releng/14.2) into platform/unix/freebsd: one system-call table for every architecture (generate_freebsd_syscalls.py, registry/freebsd_syscalls.def, each row with its syscalls.master prototype), and the flag values, struct stat offsets and errno classification (generate_freebsd_uapi.py, read with clang -E -dM and offsetof).
OS providers (files, processes, sockets, memory, clocks) std One guarded branch per OS in library/std/os/*.sn. The direct-kernel provider in platform/unix/linux also serves FreeBSD: it takes its numbers from the selected table (syscalls/selection.sn) and its constants from the selected kernel's values (linux/uapi.sn or freebsd/uapi.sn, same function names). Where FreeBSD's call differs, a FreeBSD block handles it: fstatat for statx, getdirentries records, __getcwd, wait4 for waitid, fork for clone, getrlimit/setrlimit for prlimit64, and the __sysctl hw.pagesize for the procfs auxv.
Kernel entry instruction, _start, error-flag normalization backend (compiler/backend/provider/llvm/{lowering,entry}.sn) The only per-architecture code outside std. i386 passes the number and sixth argument through a stack block so EBP can be saved. FreeBSD flags errors with the carry flag (amd64, arm64) or t0 (riscv) and a positive errno, which the sequence turns into -errno. Its _start takes the argument block from rdi, x0 or a0, and the executable carries the .note.tag ABI note (osrel 1402000) besides ELFOSABI_FREEBSD.
Arithmetic the target lacks backend (compiler/backend/provider/llvm/wide_division.sn; checked multiply in lowering.sn) Expanded in IR so no compiler-rt is linked. 64-bit division on 32- and 16-bit targets is expanded, and so is division of every width on targets with no divide instruction (ARMv7-A, which does not guarantee SDIV/UDIV; AVR; MSP430; Hexagon; Lanai), by one restoring shift-and-subtract function per width (sultanc.udivmodN). SPARC V9 divides 8-, 16- and 32-bit operands in 64 bits (SDIVX/UDIVX) instead of the deprecated Y-register SDIV/UDIV, because QEMU 8.2's sdiv also divides by the zero-extended divisor and returns wrong narrow signed quotients. Where there is no 128-bit product (WebAssembly, SPARC V9, the 32-bit targets), a checked 64-bit multiply is built from 32-bit halves, and every llvm.*mul.with.overflow.i64 the optimizer forms (InstCombine turns b > MAX / a into one) is replaced by the same expansion after the pipeline, since LLVM would call __multi3. BPF divides signed values through their magnitudes.
Address spaces backend (compiler/backend/provider/llvm/values.sn) AMDGPU stack slots live in the private address space; each is cast to a flat pointer next to its alloca.
Linking built-in linkers (ELF; Mach-O; COFF; wasm) Static executables, no libc. See Linking.

A native backend for a new architecture needs only the kernel entry and _start; the std layer is shared with the LLVM backend.

Verification

Each target is verified at the strongest level its environment allows.

Level Targets Meaning
Native run x86_64-linux, x86_64-android; arm64-darwin on an arm64 Mac the built executable runs on the host
qemu-user aarch64, riscv64, loongarch64, powerpc64le, s390x, mips64el, sparcv9, i686 and armv7 Linux; aarch64 and armv7 Android examples run under qemu-<arch> and their output is compared with x86_64
Wine x86_64-windows-msvc the PE executable runs under Wine
FreeBSD VM x86_64-freebsd, aarch64-freebsd and riscv64-freebsd under qemu system emulation examples and target tests run in a FreeBSD 14.5 guest
System-call translator x86_64-freebsd examples run on Linux under tools/tests/freebsd_abi_emulator.c (single-threaded programs)
WASI runner wasm32-wasi wasmtime
node wasm32-browser the module's _start result is the exit status
Built x86_64-darwin, aarch64-windows-msvc the executable links; tools/audit/macos_imports_audit.py checks Mach-O imports against the SDK
qemu-system-arm thumbv6m-none-eabi (microbit), thumbv7m-none-eabi (lm3s6965evb, mps2-an385), thumbv7em-none-eabi and thumbv7em-none-eabihf (mps2-an386, mps2-an500) tools/tests/cortex_m_tests.sh links images with ld.lld and the linker scripts, runs them with semihosting and compares the exit status
Objects and assembly other freestanding and accelerator targets the compiler produces objects or assembly

tools/tests/target_tests.sh runs tests/{en,ar}/target on every target their // أهداف: header names: check, then an executable (or an object or assembly), then a run where the host can run it, comparing the exit status and standard output. On an x86_64 Linux host with qemu-user, wasmtime, node and Wine, it runs 192 of the 296 test-target pairs and builds the other 104.

Every program in examples/en (90 of the 93 files; the other three are two module helpers and a C-library FFI example) gives the same output and exit status as on x86_64 Linux under qemu-user on every Linux and Android architecture, under wasmtime (except the examples that need processes or the host environment), and on the FreeBSD x86_64, aarch64 and riscv64 VMs together with the target tests. The per-target counts are in LLVM targets progress. tools/tests/freebsd_examples.sh runs the x86_64 examples on a Linux host through a ptrace system-call translator (tools/tests/freebsd_abi_emulator.c).

Cross-checking a Linux target

./build/sultanc --backend=llvm --target=riscv64-linux-gnu examples/en/fs/files.sn -o files
qemu-riscv64 ./files

Regenerating the FreeBSD tables

The generators read only the sys/ and include/ headers of a FreeBSD source tree.

git clone --depth 1 -b releng/14.2 https://github.com/freebsd/freebsd-src
python3 tools/generate/generate_freebsd_syscalls.py --src freebsd-src
python3 tools/generate/generate_freebsd_uapi.py --src freebsd-src

Running FreeBSD under qemu

The official VM images run under qemu's system emulation (TCG; no KVM needed). On Ubuntu 24.04 the packages are qemu-system-arm qemu-system-misc qemu-efi-aarch64 u-boot-qemu opensbi. The images are in https://download.freebsd.org/releases/VM-IMAGES/14.5-RELEASE/{aarch64,riscv64}/Latest/: FreeBSD-14.5-RELEASE-arm64-aarch64-ufs.qcow2.xz and FreeBSD-14.5-RELEASE-riscv-riscv64-ufs.qcow2.xz. Check them against CHECKSUM.SHA256. The 14.2 release is no longer on the mirror; 14.5 runs binaries built against the 14.2 headers. Converting an image with qemu-img convert -c -O qcow2 halves its size.

# aarch64: UEFI firmware, virtio PCI devices, SSH on host port 2223
qemu-system-aarch64 -M virt -cpu cortex-a72 -smp 4 -m 2048 -display none \
  -bios /usr/share/qemu-efi-aarch64/QEMU_EFI.fd \
  -drive file=arm64-aarch64.qcow2,if=none,id=hd0,format=qcow2 -device virtio-blk-pci,drive=hd0 \
  -netdev user,id=n0,hostfwd=tcp:127.0.0.1:2223-:22 -device virtio-net-pci,netdev=n0 \
  -serial unix:ser-aarch64.sock,server,nowait -monitor none

# riscv64: OpenSBI, then U-Boot (S-mode), which boots the image's EFI loader; SSH on 2224
qemu-system-riscv64 -M virt -smp 4 -m 2048 -display none \
  -bios /usr/lib/riscv64-linux-gnu/opensbi/generic/fw_jump.bin \
  -kernel /usr/lib/u-boot/qemu-riscv64_smode/u-boot.bin \
  -drive file=riscv-riscv64.qcow2,if=none,id=hd0,format=qcow2 -device virtio-blk-device,drive=hd0 \
  -netdev user,id=n0,hostfwd=tcp:127.0.0.1:2224-:22 -device virtio-net-device,netdev=n0 \
  -serial unix:ser-riscv64.sock,server,nowait -monitor none

The images log root in on the serial console without a password. Over the console socket, install a key in /root/.ssh/authorized_keys, add PermitRootLogin prohibit-password to /etc/ssh/sshd_config, then run sysrc sshd_enable=YES; service sshd keygen; service sshd start (sshd is off in the images, and generating the host keys takes several minutes on riscv64 under TCG). Copy a program with scp -P 2223 (or 2224) and run it over ssh. Under TCG on a 4-core host the aarch64 VM boots in about 3 minutes and the riscv64 VM in about 10, and riscv64 programs run roughly ten times slower than aarch64 ones.

The amd64 image prints only to the video console. Without a network, a run can still be driven from the host: QEMU's monitor sendkey types at the console (root, then a command line), screendump shows the screen, and the guest writes its results to the serial port (... > /dev/cuau0), which -serial unix:... hands to the host. The programs reach the guest on an ISO image (genisoimage -R) mounted with mount_cd9660 (-cdrom on amd64, virtio-scsi on aarch64, and a read-only virtio-blk-device on riscv64, where it is /dev/vtbd1).

Self-hosting on Android and FreeBSD

A compiler built for x86_64-android, aarch64-android, x86_64-freebsd or aarch64-freebsd runs there, compiles programs for its own platform by default and compiles itself. Build one from an x86_64 Linux host with the native backend (the module source below leaves out the LLVM provider, as ./build.sh does for a compiler without LLVM):

./build/sultanc --target=x86_64-freebsd \
  --module-source=compiler.backend.provider.llvm.llvm_codegen=compiler/backend/provider/llvm/unavailable.sn \
  compiler/main.sn -o sultanc

On the target, run it from a directory that holds compiler/ and library/ (or set SULTANC_STD). The compiler learns where it runs from the host-environment identity in compiler/runtime/host_environment_model.sn, whose platform codes include Android (6) and FreeBSD (7); compiler/backend/target/target_registry.sn maps them to the registered targets, so sultanc program.sn with no --target builds for the platform it runs on.

Target check compiler/main.sn --target Compiler builds and links Run (hello program; the compiler compiling itself)
x86_64-android passes yes (33 MB static ELF) natively on the Linux kernel: hello prints its output; the compiler it builds is byte-identical to itself
aarch64-android passes yes (31 MB static ELF) qemu-aarch64: hello prints its output; the compiler it builds is byte-identical to itself (about 3 minutes)
x86_64-freebsd passes yes (33 MB static ELF, FreeBSD ABI note) the system-call translator: hello prints its output; the compiler it builds is byte-identical to itself
aarch64-freebsd passes yes (31 MB static ELF, FreeBSD ABI note) FreeBSD 14.5 arm64 VM (qemu -cpu neoverse-n1): check passes, hello prints its output; the compiler it builds (about 9 minutes, 1.3 GB resident) is byte-identical to the one the x86_64 Linux compiler builds from the same source directory, and it compiles hello again

A compiler records the absolute paths of its sources, so two builds compare equal only when they are compiled from the same directory.

On these targets (and on Linux and Windows) aarch64 code uses ARMv8.0 atomics, LDAXR/STLXR loops, from both the native and the LLVM backend (see Concurrency), so the executables run on ARMv8.0 cores such as the Cortex-A53, A57 and A72 (Raspberry Pi 3 and 4) as well as on later ones: an aarch64-linux-gnu compiler checks compiler/main.sn and compiles hello under qemu-aarch64 -cpu cortex-a57. Only arm64-darwin uses the ARMv8.1 LSE atomics (LDADDAL, CASAL and the rest), which every Apple Silicon core has. sultanc run is not available on these hosts: the interpreter's host kernel bridge exists for Linux only, and the run reports SULTANC-INTERPRETER-UNSUPPORTED-MACHINE-PRIMITIVE. Compile the program and run the executable instead.

Limitations

  • The native backend covers the x86_64 and aarch64 operating-system targets and wasm32; every other target needs the LLVM backend.
  • sultanc run is not available on Android and FreeBSD hosts.
  • ve-linux-gnu has no std OS layer.
  • Freestanding and accelerator targets produce objects or assembly only; Cortex-M images are linked with ld.lld outside the compiler.
  • Cortex-M: no soft-float routines (float operations without FPU support are rejected), no Cortex-M7 double-precision FPU selection, and only 32-bit atomic loads, stores and fences in std.
  • x86_64-darwin and aarch64-windows-msvc are built but not run.
  • wasm64-unknown-unknown produces objects only.
  • sultanc check compiler/main.sn passes for the hosted targets only: the compiler uses files and processes.
  • wasm32-wasi has no processes, threads or file locks, and its sockets are limited to preopened ones.

See also