badc demos

End-to-end programs that exercise badc on something more substantial than a fixture. Build with the c5 dialect badc supports today (structs / unions / bitfields / enums, arrays of structs, function pointers, varargs, _Thread_local); see ../doc/std-conformance.md for the divergences from C99.

Each demo runs on macOS (aarch64), Linux (x86_64 and aarch64), and Windows (x86_64 and aarch64). The platform-specific bits sit behind a handful of #ifdefs; the libc / Winsock / pthread / Win32 thread bindings come from the shipped <sys/socket.h>, <sys/select.h>, <pthread.h>, and <windows.h> headers.

Build flavours

The multi-source library demos (miniz, kissfft, bzip2, tweetnacl, monocypher, bearssl) each build their smoke harness three different ways at both -O0 and -O:

  1. Amalgamation – one combined source file straight through badc. The single-TU path, the same shape sqlite3.c + shell.c already exercises.
  2. Translation unitsbadc -c on each .c file (emitting native ELF64 ET_REL .o files with machine code, .symtab, and .rela.text relocs), then badc -o app *.o to link them. Exercises the linker’s cross-TU symbol resolution and the C99 6.2.2 internal / external linkage rules.
  3. Archivebadc --ar -o libfoo.a *.c to bundle the library into a SysV ar(5) archive with / symbol index, then badc -o app main.c -L. -l foo to pull members in by reference (gcc-style). Exercises the on-demand archive resolution path.

The flat single-source demos (hello_server, coro_pool, threads) and stb’s header-only smoke stay single-TU – the multi-TU paths are exercised by the library demos.

hello_server.c

A select(2)-driven HTTP server that serves Hello, World! on port 8080. Multiplexes up to 8 concurrent connections in a single event loop – no threads, no fork.

cargo run -- -O -o hello demos/hello_server.c
./hello                           # listens on 0.0.0.0:8080
curl http://localhost:8080/       # -> Hello, World!

The byte-level constants (AF_INET, SOL_SOCKET, …) and the libc/Winsock symbol bindings come from <sys/socket.h>. The dialect-driven choices that don’t fit in a header (the macOS sin_len byte, the close vs closesocket spelling, the fcntl vs ioctlsocket non-blocking dance) sit behind a few #ifdefs in the source.

coro_pool.c

User-mode cooperative coroutines on top of a Cilk-shape work- stealing scheduler. Every task is a tiny state machine (an iterative fib(n) here) that advances one step per tick; the scheduler round-robins through two workers, and an idle worker steals from the other’s deque (FIFO own-pop, LIFO steal-pop).

The demo seeds every task on worker 0, so worker 1 has to steal to make any progress. The output reports who finished what and how many steals each worker performed.

cargo run -- -O -o coro demos/coro_pool.c
./coro

Pure user-mode – no syscalls beyond printf / malloc, so it works wherever the c5 dialect compiles.

threads.c

OS-level threads (POSIX pthread_create / Win32 CreateThread) sharing a task queue under a mutex. NUM_THREADS workers race to claim NUM_TASKS items off a counter; each task computes a recursive fib(n) so the work is heavy enough that all four workers see action. Output reports which worker picked each task.

cargo run -- -O -o threads demos/threads.c
./threads

arg flows in through the codegen’s host-ABI shuffling thunk: when a c5 function’s address is taken (here, worker_main passed to pthread_create / CreateThread), the codegen emits a small wrapper that copies the host’s first int-arg register (rdi / x0 / rcx) into the c5 stack slot the callee reads from. Each worker’s logical id rides through that channel; everything bigger (the task queue, the result table) still goes through globals.

sqlite3/

End-to-end of the upstream SQLite amalgamation. Pinned release, fetched on demand by demos/sqlite3/setup.py; in-memory and file-backed scenarios at both -O and noO. See sqlite3/README.md.

lua/

End-to-end of the upstream Lua 5.5.0 interpreter. Pinned release, fetched on demand by demos/lua/setup.py; builds the interpreter with badc at both -O and noO and runs a curated subset of the upstream test suite (bitwise, calls, closure, constructs, coroutine, cstack, errors, events, goto, literals, locals, math, nextvar, pm, sort, strings, tpack, utf8, vararg) against each lane. See lua/README.md.

miniz/

End-to-end of the upstream miniz deflate / inflate / CRC32 / Adler32 amalgamation. Smaller and integer-heavier than sqlite – the second non-trivial demo. See miniz/README.md.

kissfft/

End-to-end of the upstream KISS FFT amalgamation. First real FP exerciser: impulse FFT, forward+inverse round-trip, real-only kiss_fftr against a sine wave, all at -O and noO. See kissfft/README.md.

bzip2/

End-to-end of the upstream bzip2 1.0.8 library. Integer + bit-twiddle heavy (BWT, MTF, RLE, Huffman); exercises a different code shape from miniz’s deflate. See bzip2/README.md.

libmill/, libdill/, coroutines/

Cooperative-concurrency libraries whose context switches stress the inline-asm surface: libmill (Go-style CSP coroutines + channels), libdill (structured concurrency), and tsoding/coroutines (hand-written SysV asm in naked functions). Pinned upstream commits, fetched by each demo’s setup.py; each smoke builds the library with badc at -O0 and -O and runs the upstream tests (spawn / yield / channel rendezvous; exact-output examples for coroutines). On x86-64, libmill / libdill run their upstream asm setjmp/longjmp context switches; other architectures use sigsetjmp. Both pair with a one-instruction asm sp move (see each setup.py for the badc-motivated patches); coroutines compiles unpatched but runs only on linux-x86_64 by upstream design.

uemacs/

MicroEMACS (torvalds/uemacs), a termios + termcap editor: the 35 translation units of the upstream Makefile compiled with badc under its own defines and linked by badc’s linker against the system terminfo library, at -O0 and -O. The smoke drives each binary under a pseudo-terminal (TERM=vt100, 24x80) through the editor’s startup-file language and through raw keystrokes, and checks the files it writes against the expected text and against a host-cc build of the same tree. POSIX only. See uemacs/README.md.

picocom/

picocom 3.1, a serial terminal emulator: the 7 translation units of the upstream Makefile compiled with badc under its own defines and linked by badc’s linker, at -O0 and -O. Nothing beyond libc is linked – the terminal layer is termios plus the modem-line ioctls. The smoke gives each binary a pty pair standing in for the serial port and a second pty for its own terminal, sends a line each way through it, and ends the session with C-a C-x. POSIX only. See picocom/README.md.

screen/

GNU Screen 5.0.0, the terminal multiplexer: ./configure + make generate the derived sources and the host-cc reference, then the compile line for each of the 38 units is taken out of make -n and replayed through badc -c, and badc’s linker produces screen against the system terminfo and crypt libraries, at -O0 and -O. The smoke runs each binary as both halves of its own client/server pair – a detached session driven over the session socket with stuff, hardcopy and quit, and an attached session under a pseudo-terminal – and compares the window text and the terminal output with the reference build’s. Linux only: upstream 5.0.0 does not compile on macOS with any compiler. See screen/README.md.

vim/

Vim 9.1.0800, the largest hosted program in the demo set: ./configure with every embedded interpreter and the GUI off, then make, generate the derived sources and the host-cc reference, and the compile line of each of the 123 objects the link consumed is replayed through badc -c. badc’s linker produces the editor against the same libraries, at -O0 and -O. The smoke runs each binary in ex mode from a script and again under a pseudo-terminal from keystrokes, and checks the file each run writes and the terminal output against the reference build’s. Linux only: the macOS build reaches SDK headers badc’s own set does not carry. See vim/README.md.

gui_hello/

Three “show a window with a label” demos – Win32 (using the new #pragma subsystem(windows) + #pragma entrypoint(WinMain)), Linux X11, macOS Cocoa via raw objc_msgSend. Cross-builds to all five supported targets; CI runs the smoke build-only since runners have no display server. See gui_hello/README.md.

raylib/

badc compiles raylib 5.5 (RGFW desktop backend) from source and links a small game (Lode Runner) into a standalone binary. The pure game-logic self-test runs through badc on any host; the full standalone build + headless run is wired for macOS today (the X11 / Win32 header surface for the Linux / Windows ports is pending). See raylib/README.md.

stb/

badc builds smoke_main.c – the curated stb_*.h set with their STB_*_IMPLEMENTATION macros, plus stb_vorbis.c, as one translation unit – at both -O and noO, runs it, and pins each scenario’s stdout: sprintf, perlin, image, jpg, bmp, ds, rect_pack, c_lexer, connected_components, divide, dxt, easy_font, hexwave, leakcheck, truetype, herringbone_wang, vorbis, voxel_render, textedit and include.

curl/

badc compiles the curl 8.11.1 library (HTTP + file:// + WebSocket, threaded resolver, IPv6, no external dependencies) and builds it as a static archive, a shared library, and an executable linked against each, plus a flavour that binds a badc-compiled client to the platform’s installed libcurl to check the frontend matches the OS ABI. HTTPS is provided by badc-compiled BearSSL (USE_BEARSSL); the smoke drives HTTP / HTTPS / file:// transfers against a hermetic loopback server, so it needs no external network. See curl/README.md.

nasm/

Builds the NASM 2.16.03 assembler (84 translation units) with badc and runs NASM’s own travis/nasm-t.py golden suite against it: each fixture is assembled with the produced nasm and its object bytes, listings, and diagnostics are compared to committed goldens, so a codegen defect surfaces as a byte mismatch and no reference build is needed. Runs on all five targets, native Windows included, with no make or ./configure. See nasm/README.md.

yasm/

Builds the yasm 1.3.0 modular assembler – a core library plus pluggable arch / parser / preprocessor / object-format modules – with badc from a frozen per-target config. The build first derives several C sources, including the x86 instruction tables whose Python generator runs under the badc-built CPython: a badc-built interpreter emits the tables a badc-built assembler is then compiled from. See yasm/README.md.

qemu/

Builds the QEMU 11.1.1 system emulator with badc – well over a thousand translation units per target (device models, the TCG code generator, the block layer, the QAPI-generated marshallers, the character / network back ends), the widest single exercise of the C front end and object emitter in the demo set. badc compiles every unit, archives the in-tree libraries with --ar, and self-links the emulator with its own linker – no system linker in the chain. Both self-compiled, self-linked qemu-system-aarch64 and qemu-system-x86_64 boot a Linux kernel plus a busybox initramfs to an interactive userspace shell and power off cleanly under TCG (aarch64 loads a raw Image on -M virt; x86_64 boots an EFI-stub bzImage through OVMF). The vendored build config (no meson / make / configure) is per target; both the aarch64 and x86_64 Linux configs are captured in-repo. See qemu/README.md.

efi_hello/

UEFI application that prints “Hello, EFI!” through SystemTable->ConOut->OutputString. Subsystem = IMAGE_SUBSYSTEM_EFI_APPLICATION (10); the firmware loader invokes efi_main(EFI_HANDLE, EFI_SYSTEM_TABLE *) directly, with no CRT shim and no msvcrt import. Build-only in CI; running needs a UEFI shell (TianoCore’s UEFI Shell, OVMF under qemu, or a real machine’s firmware shell).

edk2/

Builds a UEFI application from real TianoCore EDK II MdePkg sources with badc, for X64 and AArch64, and boots each under OVMF/QEMU in CI. badc compiles the MdePkg library closure and links a PE32+ EFI application with its own linker – no external ld/lld, no GenFw – that the firmware loads and runs; the app formats through EDK II’s UnicodeSPrint and writes to ConOut, so a correct boot exercises the whole closure end to end. See edk2/README.md.

kernel/

Freestanding UEFI “kernels” badc compiles for x86_64 and AArch64, booted under QEMU through the UEFI firmware badc built (OVMF on x86_64, ArmVirtQemu/AAVMF on aarch64). kernel.c is a live end-to-end test of badc’s inline assembly on the boot path (cpuid + a table-encoder bswap on x86_64, mrs on AArch64, raw-byte templates on both). preempt.c is a preemptive multitasking kernel: it installs its own timer interrupt and context-switches three threads on every tick through a __attribute__((naked)) interrupt service routine, on both x86_64 (8259 PIC + 8254 PIT + IDT) and AArch64 (GICv2 + virtual generic timer + EL1 vector table). The build is the hard gate; each target also boots where QEMU and the firmware are present. See kernel/README.md.

nt_hello/

NT-native usermode skeleton. Subsystem = IMAGE_SUBSYSTEM_NATIVE (1), entry = NtProcessStartup, calls ntdll!NtTerminateProcess to exit. Same image shape as smss.exe / autochk.exe / boot-time chkdsk.exe. Build-only in CI; runs on Windows via the BootExecute registry value or during smss/csrss bringup.

wdm_driver/

Windows kernel-mode driver skeleton. Subsystem = IMAGE_SUBSYSTEM_NATIVE (1) via the driver pragma alias, entry = DriverEntry(PDRIVER_OBJECT, PUNICODE_STRING), registers a DRIVER_UNLOAD callback so the service is stoppable via sc stop. Build-only; loading needs admin and test-signing on the target. Same compiler plumbing as nt_hello; differs only in entry signature.

nt_loader/

Launches user-mode NT-native programs (e.g. nt_hello) through a transacted SEC_IMAGE section and waits up to two seconds on a named event the child signals. Builds in both UNICODE (wmain, __wgetmainargs) and ANSI (main, __getmainargs) modes from the same source; #define USE_UNICODE selects.

^ To the top