Xo 0.26 release notes
Downloads: Xo 0.26.0 on GitHub,
with archives for macOS (Apple silicon and Intel, signed and
notarized), Linux (arm64 and x86-64), and Windows (x86-64, Authenticode
signed), and a SHA256SUMS file.
Getting started shows how to install
one.
Xo against Go
Each release is measured against Go on the same Mac (bench/results/release-vs-go-v0.26.0.md, lines 11 to 22):
- Run time: Xo is faster on 14 of 20 workloads, within noise on 3, and slower on 3 (conc rwmutex 3.78 times Go’s wall time, conc pingpong 1.51, llvm nbody 1.43).
- Peak memory of the run: lower than Go’s on all 20 (0.38 to 0.97).
- Binary size: 1.81 to 1.89 MB against Go’s 2.39 to 2.43 MB.
- Compile: cold builds 2.0 to 2.4 s against Go’s 1.9 to 2.0 s; a warm build of an unchanged program, 0.42 to 0.64 s against 0.04 to 0.08 s, is the largest gap.
Behavior changes to read before upgrading
xo run DIRruns exactly DIR; a plain directory never walks below it (decision 0345). In a parent of two programs,xo run .used to run the first one it found; it now says which subdirectories hold a program.build,check,test,vet,doc,fmt,fix,parseandauditcover subdirectories only withDIR/..., which stops past 6,000 .xo files or 60,000 entries. Writexo check ./...wherexo check .used to cover the tree.xo buildandxo runno longer read_test.xofiles (decision 0348). A type error in a test file no longer fails a build, a helper declared only in a test file is not visible to the program, and ausethat only tests have is neither fetched nor bound.xo testandxo checkreport test files’ errors as before.Fault tracebacks by default. An uncaught fault in a native program prints
traceback:and one line per frame down to main, as a hardware fault’s report already did.XO_TRACEBACK=0restores the one-line report. The binary is unchanged; each fault costs about 0.55 us more, a caught one included.Embedded DWARF on macOS (decision 0346). Darwin executables carry their debug information in a
__DWARFsegment instead of aNAME.dSYMbeside them, so a copied or packaged binary keeps file and line names in crash reports, tracebacks, the profiler,--race, atos and lldb.--dsymalso writes the bundle;-sand--releaseleave both out (--release --debug-infokeeps the segment). Release binaries ship with their DWARF: the darwin archive grows from 23.2 MB to 34.4 MB (+11 MB).--raceis native by default wherever a prebuilt ThreadSanitizer runtime ships (darwin/arm64, darwin/amd64, linux/arm64, linux/amd64, windows/amd64), so it no longer needs clang. In 0.25.2xo build --race .picked the LLVM backend and failed without clang. Other targets still use LLVM. (decision 0273 addendum)opt?on an Option in a test block fails the test (decision 0347) witherror: missing value: EXPR, instead of being error XO0204. Helper functions and fn literals keep the usual rules.xo has a memory budget (decision 0349): past three quarters of physical memory, at least 16 GiB (18 GiB on a 24 GB Mac), xo stops with
fatal error: memory budget exceeded: ...and the phase it was in, instead of swapping the machine. Builds of xo itself peak at 9.9 to 11.2 GB counted.XO_MAX_MEMsets another budget (offfor none); any native program can use it.Daylight saving gaps:
Ambiguity.EarlierandLaterwere swapped. For a local time inside a spring-forward gap,Earliernow gives the earlier instant andLaterthe later one, as in an overlap: 02:30 on 2026-03-08 in New York is 01:30 EST withEarlierand 03:30 EDT withLater. A program that relied on the old result swaps the two arguments.Large Maps branch instead of copying (decision 0291): on the native backends, a Map or Set of 1024 or more entries written while another value shares it no longer copies its lists; the written copy reads the shared lists in place. bench/perf/xo branch: 0.071 of the wall time (65.6 to 4.7 ms), 0.733 of the peak RSS; the HTTP CRUD server within noise.
XO_MAP_LOG=1builds without it.LLVM builds of large programs split: over 100 MB of IR (xo itself),
--backend=llvmsplits into modules of about 50 MB with thin LTO and runs as many clang jobs as three quarters of memory allows at 3.5 GB each; the LLVM build of xo peaks at 11.1 GB instead of passing 20 GB. Smaller programs are unchanged (one module, full LTO).XO_LLVM_SPLITandXO_LLVM_JOBSoverride.
Also new or fixed
--hybridis deprecated and warns: since the Go compiler’s removal it is a plain Go backend build.--holesbuilds natively.xo clean -cache [-n]empties xo’s caches (the module cache and the seeds stay).- Build outputs:
-o DIRwritesDIR/NAME; a build writes into a hidden stage and renames into place, so concurrent builds of one output and an interrupted build leave nothing behind;--watchbuilds go to the user cache;xo initignores.xo/;-o /dev/nullworks. - Crash bundles go to
<user cache>/xo/crash(the newest 100 kept) instead of the system temporary directory. xo runpasses a stop request on to the program when not on a terminal.- New diagnostics and notes: XO1511 (an
embed.Dirliteral path it does not hold, with a fix); habit notes and fixes for.or(x),.unwrap_or(x)and friends (opt ?? x) and for a missinguse std/json; contract failures print values read inside calls. - The Go backend refuses a network program for wasip1/wasm (it spun at full CPU before).
xo build --targets=all(and any list mixing architectures) builds each target with its own native backend and names each binary NAME-OS-ARCH (0.25.2 failed, and native builds overwrote each other);--targets=windows/amd64 -o appwritesapp.exe.- Native runtime: a
StrorBytesslice 2 GiB or more into its string read the wrong memory;List.join, large buffers andto_hexover 4 GiB fault instead of returning a cut result; on Linux the 50 us run-next grace uses epoll_pwait2 instead of a 1 ms epoll_wait. - Debug information of functions with thousands of overflow checks is shared across fault stubs (a generated 24,000 line function wrote over 4 GiB of assembly), and the SSA builder’s variable reads stopped being quadratic on such functions.
- XO1201’s note says to build with
--backend=go. - Runtime: idle workers no longer spin on a run-next slot they may not
take yet (an HTTP/2 download server at 0.664x the CPU, Go’s rate); a
traceback leak under
task.isolatefixed;--racereports name lines again under macOS 26’s atos. - macOS: the immortal empty list is back at a page start, which keeps a
native program clear of an XProtect false positive (MACOS.SOMA.AUENB);
the asynchronous preemption tables moved to
__DATA_CONST. - The archive’s README starts with getting started for that archive, and a guide for Go programmers covers Option and Result.
Windows
- Native windows/amd64 programs do their I/O on completion ports (IOCP, decision 0324).
xo.exeandxo-gobind.exeare Authenticode signed (Azure Artifact Signing).