{"slug": "badc-optimizing-cross-platform-c-compiler-built-with-claude", "title": "Badc – optimizing cross-platform C compiler built with Claude", "summary": "Developer kromych released badc, a cross-platform optimizing C compiler built with Claude that supports C99, C11, and later standards. The compiler can build Python 3.14 on five targets, JIT-compile code, and produce native binaries for macOS, Linux, and Windows. It aims to provide a nimble, one-executable toolchain for everyday use.", "body_md": "`badc`\n\nis a rather small cross-platform optimizing compiler (also a compiler-as-library)\nof the C language.\n\nIt had appeared out of necessity to quickly tweak how and what a C compiler emits. Then it was captivating making it being able to become a nimble practical tool for everyday use rather than a niche hack. Modern approaches to coding would make building a compiler easier than that had been before I thought :)\n\nNow `badc`\n\nimplements a very large portion of the C99, C11 standards and some\npopular idioms from the later standards as well as few extensions. All of that is\nenough to build and test Python 3.14 on all of the five supported targets (and\nthere are more [ demos](/kromych/badc/blob/master/demos) included, read on!).\n\n`badc`\n\n's small footprint and embedded headers (which you can override or `--install`\n\nto some path for tweaking or inspecting) give a one-executable experience of the\nportable tools. The compiler's codebase of moderate size can be used as a small\nself-sufficient toolchain or can be used as a library giving *your project* the\nability to build C code or just run it (the default when using as a library).\n\nA fun extension is that `badc`\n\ncan automatically add the header(s)\nfor the standard library so the bare `hello.c`\n\nwith\n\n```\nint main() {\n    puts(\"Hello\");\n    return 0;\n}\n```\n\nworks:\n\n```\ninfo: auto-including <stdio.h> for undeclared `puts`\ninfo: wrote file hello for target `macos-aarch64`\n```\n\n`badc`\n\nis able to produce the debug information so that the binaries it generates\ncan be debugged and/or their performance can be profiled (use `-g`\n\n).\n\n`badc`\n\noptimizes when you specify `-O`\n\nand can produce code that's faster\nthan `clang -O0`\n\n, especially on ARM64. To get an idea of the codegen\nquality, take a look at [ ./tests/snapshots](/kromych/badc/blob/master/tests/snapshots) with assembly and\nSSA snapshots of the test fixtures. The optimized binaries will run on any modern\nARM64 processor, and on x86_64 processors not older than Intel Haswell and AMD Zen\n(circa 2013, the optimizer uses FMA3 instructions).\n\n`badc`\n\nemits position-independent code and the real native binaries (macOS Mach-O,\nLinux ELF, or Windows PE32+), on any of five targets, from any host:\n\n- macOS (\n`ARM64`\n\n), - Linux (\n`ARM64`\n\n,`x86_64`\n\n), - Windows ({\n`ARM64`\n\n,`x86_64`\n\n}`x`\n\n{`console`\n\n,`GUI`\n\n,`NT`\n\n,`driver`\n\n}).\n\nIt supports also separate translation units (always translated to ELF) and has a small\nlinker (so no relaxations or LTO). `badc`\n\ntries hard not to get in the way with assumptions\non the runtime library, and `--freestanding`\n\nas available should you need that. `EFI`\n\nis supported as well.\n\n`badc`\n\ncan also JIT-compile into the machine code in-process so no binary is written\nto the disk. Finally, it recognizes being used as `#!`\n\nso that C source code becomes\na (fast) script.\n\nThere are various demo's under [ demos](/kromych/badc/blob/master/demos):\n\n- Few small-ish ones (\n`threads.c`\n\n,`coro_pool.c`\n\n,`hello_server.c`\n\n), `maze.c`\n\n- maze builder and solver,`gui_hello`\n\n- GUI demos for macOS, Linux and Windows,`wdm_driver`\n\n,`nt_hello`\n\n,`nt_loader`\n\n- examples of the Windows native (NT) executable, Windows driver,`efi_hello`\n\n- a UEFI binary,`sqlite3`\n\n- the most famous embedded database,`miniz`\n\n- compression, CRC32, integers, bit twiddling,`kissfft`\n\n- floating points, Fast Fourier Transform,`bzip2`\n\n- compression, integers, bit twiddling,`stb`\n\n- header-only C library with lots of incredible features (math noise generation, sound, JPEG, PNG, BMP, PSD support to name a few). It really stresses all of the compiler.`chibicc`\n\n- a small C compiler`tinycc`\n\n- a cool and small C toolchain`TweetNaCl`\n\n,`Monocypher`\n\n,`BearSSL`\n\n- cryptography`Lua`\n\n- the embeddable scripting language`quickjs`\n\n- JavaScript interpreter- Tool command language`TCL`\n\n`Python`\n\n- Python 3.14\n\nBesides these, there are some fun test fixtures implementing Horner scheme, RK4, 8-Queens and more.\n\nFinally, there's an option to run the IR (intermediate representation) with tracking pointer access and bounds to catch memory issues.\n\n`badc`\n\nused to be bad when the projects just started out and the name stuck.There is some compiler-building jargon in this document here and there. You can safely skip it, and jump to the usage section right away.\n\nFor the\n\ntruecompiler heads there is the`--dump-ssa`\n\noption which prints each function's SSA IR plus the register allocator's per-value placement to stderr before lowering.\n\nIt started out as a Rust port of Robert Swierczek's teeny-tiny C compiler in 4 functions\n[c4](https://github.com/rswier/c4) and grew from there. There then has been enough divergence\nfrom the original to call the dialect **c5**. Due to that facetious naming the source tree\nspells that out as the `c5`\n\nmodule and `C5Error`\n\ntype.\n\nThe venerable 4-function `c4.c`\n\ncompiler ships as a test fixture and self-hosts:\n\n```\nbadc -O -o c4 tests/fixtures/c/c4.c   # compile c4 to a native binary\n./c4 hello.c                          # which then runs hello.c\n```\n\nAnd you can really crank the fun up with something like\n\n```\nbadc -O --jit tests/fixtures/c/c4.c tests/fixtures/c/c4.c tests/fixtures/c/c4.c tests/fixtures/c/c4.c\n```\n\nto run it quadro-nested :)\n\nDuring the development, the `badc`\n\ncompiler was \"spiraling\" out from the stack\nIR execution and evolving frontend to the 3-operand IR and SSA IR and the optimizing\nbackend.\n\nIt lowers through an SSA intermediate representation and a graph-coloring register allocator, but doesn't go for the exquisite optimization passes a titan toolchain like clang, gcc or msvc run. All told, to stay slim, it's unlikely to surpass the ability of multi-gigabyte compiler suites to squeeze the last drop of perf from the machine, and that's fine.\n\nYou can download one of the binary release packages matching your\nhardware and the OS. There is one small binary inside, and that's\nall you should need to start using `badc`\n\n.\n\nIf you have Rust installed, clone the repo, and install it with\n\n```\ncargo install --path . --features full\n```\n\nor just\n\n```\ncargo install badc --features full\n```\n\nif you're not interested in building from the source code.\n\nThe `--features full`\n\nis required for the command-line compiler: the\ncrate's default feature set is the host-architecture JIT library alone\n(so `cargo add badc`\n\npulls in a slim dependency), and the `badc`\n\nbinary\nadditionally needs the native object writers and the cross-translation-unit\nlinker, which the `full`\n\nfeature enables.\n\nNow `badc`\n\nis available on the PATH.\n\nA first run:\n\n```\nbadc --jit hello.c # runs native code in-process\nHello 123\n```\n\nor\n\n```\nbadc -O hello.c     # Produces native optimized binary\n./hello             # produced by the previous line\nHello 123\n```\n\nHere's a quick debugging session:\n\n```\nbadc -g hello.c     # Build with the debug information\ninfo: wrote file hello for target macos-aarch64\n```\n\nNow run under the debugger (`lldb`\n\n, `gdb`\n\n, `rr`\n\n), set breakpoints, check out the local variables:\n\n```\nlldb ./hello\n\n(lldb) target create \"./hello\"\nCurrent executable set to '/Users/krom/src/compilers/badc/hello' (arm64).\n(lldb) b main\nBreakpoint 1: where = hello`main + 16 at hello.c:5, address = 0x00000001000006fc\n(lldb) l\nnote: No source available\n(lldb) run\nProcess 19800 launched: '/Users/krom/src/compilers/badc/hello' (arm64)\nProcess 19800 stopped\n* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1\n    frame #0: 0x00000001000006fc hello`main at hello.c:5\n   2    #include <stdlib.h>\n   3\n   4    int main() {\n-> 5        int a = 123;\n   6        printf(\"Hello %d\\n\", a);\n   7        return 0;\n   8    }\nTarget 0: (hello) stopped.\n\n(lldb) n\nProcess 19800 stopped\n* thread #1, queue = 'com.apple.main-thread', stop reason = step over\n    frame #0: 0x0000000100000704 hello`main at hello.c:6\n   3\n   4    int main() {\n   5        int a = 123;\n-> 6        printf(\"Hello %d\\n\", a);\n   7        return 0;\n   8    }\nTarget 0: (hello) stopped.\n\n(lldb) v\n(int) a = 123\n```\n\nThe first non-flag argument is the source file. By default `badc`\n\nlowers it to a native binary at the obvious path next to the\nsource (`hello.c`\n\n-> `hello`\n\non POSIX targets, `hello.exe`\n\non\nWindows targets); pass `-o <path>`\n\nto choose a different one.\n\nThe three execution modes:\n\n| flag | what it does |\n|---|---|\n| (default) | Lower to a native Mach-O / ELF / PE32+ at `-o <path>` and exit. |\n`--jit` |\nLower in-process, mmap the result, call `main` directly. |\n`--interp` |\nRun the SSA IR under a watchful VM (pointer tracking, traces). |\n\nFlags (`--target=<spec>`\n\n, `--optimize`\n\n/ `-O`\n\n, `--dump-ssa`\n\n,\n`--list-symbols`\n\n, `-H`\n\n/ `--show-includes`\n\n, plus the VM-only\n`--track-pointers`\n\n/ `--trace`\n\n) can appear anywhere before the\nsource. `-D NAME[=VALUE]`\n\n, `-U NAME`\n\n, `-I path`\n\n, and `-include FILE`\n\nwork the same way they do on gcc / clang. Source-driven\nbuild flags ride on `#pragma`\n\ns -- see \"Headers and bindings\"\nbelow.\n\nA `.c`\n\nfile may start with a shebang. With `badc`\n\non `PATH`\n\n,\n`chmod +x script.c`\n\nmakes the file directly executable -- in\nwhich case the shebang line picks the mode (`#!/usr/bin/env badc --interp`\n\nfor the VM, the bare form for native compilation).\n\nFive targets are supported, and you cross-compile from any host to any of them:\n\n`--target=` |\nformat |\n|---|---|\n`macos-aarch64` |\nMach-O |\n`linux-aarch64` |\nELF |\n`linux-x64` |\nELF |\n`windows-x64` |\nPE32+ |\n`windows-arm64` |\nPE32+ |\n\nA single `badc`\n\ninvocation can mix `.c`\n\nsource files, `.o`\n\nobject files, and `.a`\n\narchives:\n\n```\nbadc -c foo.c bar.c               # emits foo.o + bar.o (native ELF64 ET_REL, target pinned)\nbadc -o app foo.o bar.o           # links them into a final binary\n\nbadc --ar -o libfoo.a foo.c bar.c # bundles into a SysV ar(5) archive\nbadc -o app main.c -L. -l foo     # link against libfoo.a, gcc-style\n```\n\n`badc`\n\nships its own linker -- there's no `ld`\n\n/ `lld`\n\n/\n`link.exe`\n\ndependency. Object files are standard ELF64 ET_REL\nrelocatables: a `.text`\n\nsection of native machine code,\n`.data`\n\n/ `.bss`\n\nfor static storage, `.symtab`\n\n/ `.strtab`\n\nfor the name table, and `.rela.text`\n\ncarrying the relocations\nthe linker applies once each unit's final position is known.\nThe target is pinned at `-c`\n\ntime, and the objects are also\nlinkable by `ld`\n\n/ `lld`\n\n. Archives are ar(5) with a SysV-style\nsymbol index. The `full`\n\ncargo feature gates the entire\npipeline; library consumers that don't need\nmulti-TU artifacts can opt out via\n`default-features = false, features = [\"std\"]`\n\nto keep the\nfootprint slim.\n\nStorage-class linkage follows C99 6.2.2: `static`\n\nat file\nscope is internal, bare or `extern`\n\ndeclarations are external,\nand `extern T x;`\n\nwith no defining declaration becomes an\nunresolved external that the linker tries to satisfy from the\nremaining objects or archive members.\n\nA summary of what the dialect parses + lowers, and where it\ndiverges from C99, lives in [ std-conformance.md](/kromych/badc/blob/master/std-conformance.md). Short\nversion: c5 covers most of the language and few features of the later standards.\nThe doc enumerates rejected idioms, divergent behavior, and the c5-only extensions\n(\n\n`#pragma dylib`\n\n/ `binding`\n\n/ `export`\n\n/ `entrypoint`\n\n/ `subsystem`\n\n).The preprocessor pre-defines a small standard set, double-underscore wrapped in the gcc / clang / msvc convention so they don't collide with user identifiers:\n\n```\n    __BADC_VERSION__   <crate version>   // string literal from Cargo.toml, e.g. \"0.0.9\"\n    __BADC_TARGET__    \"macos-aarch64\"   // canonical target id (string literal)\n    __aarch64__ / __arm64__              // AArch64 targets\n    __x86_64__ / __amd64__               // x86_64 targets\n    _WIN32 / _WIN64                      // Windows targets only\n    __BADC_WINDOWS__                     // Windows targets only\n    __APPLE__                            // macOS target only\n    __linux__                            // Linux targets only\n```\n\nThe MSVC/MinGW mimicry surface (`_MSC_VER`\n\n/ `__MINGW32__`\n\n/ `__int64`\n\n/ `__declspec`\n\n/ etc.) lives in `headers/include/msvc_compat.h`\n\nand is opted into per translation unit with `-include msvc_compat.h`\n\n.\n\nThe header tells the compiler which dylib's/so's/dll's the target offers and which local names resolve to which exported symbols. A snippet:\n\n```\n#pragma dylib(libsystem, \"/usr/lib/libSystem.B.dylib\")\n#pragma binding(libsystem::printf, \"_printf\")\n\nint printf(char *fmt, ...);\n```\n\nThe codegen drives its IAT / `.got`\n\n/ `DT_NEEDED`\n\nrecords from\nthese declarations. When the source calls `printf`\n\n, the parser\ntype-checks the call against the prototype; the codegen looks up\nthe binding to learn that the loader should resolve `_printf`\n\nfrom\n`libSystem.B.dylib`\n\n. Switching target swaps the header and the\nbindings change with it -- `printf`\n\nlands on bare `printf`\n\nfrom\n`libc.so.6`\n\non Linux, `printf`\n\nfrom `msvcrt.dll`\n\non Windows.\n\nValidation runs at codegen entry: every intrinsic the program\n*references* must have a matching binding for the chosen target.\nUnused bindings cost nothing -- they describe the surface without\nforcing you to pull in everything they name.\n\n`badc`\n\nuses `#pragma`\n\n's to lighten the command line. One can specify\ndylib bindings, exports, alignment, the entry-point name, and the Windows\nsubsystem -- every knob lives next to the code it configures\nso the source carries enough context to build with a bare\n`badc <file>`\n\n.\n\n```\n#pragma once                       // single-inclusion guard for headers.\n#pragma dylib(libc, \"libc.so.6\")   // declare a dylib c5 can bind into.\n#pragma binding(libc::sin, \"sin\")  // map a portable name to its dylib symbol.\n#pragma export(my_api)             // promote a function to a shared-object export.\n#pragma pack(N) / pop / push       // override the default 8-byte struct alignment.\n#pragma entrypoint(WinMain)        // override the default `main` entry point.\n#pragma subsystem(windows)         // pick the PE subsystem (console | windows | native | efi_*).\n```\n\n`#pragma entrypoint(<name>)`\n\nlets the source declare a\nnon-`main`\n\nentry without a build-driver flag; the compiler\nresolves the name through the same symbol-table lookup it uses\nfor `main`\n\n. `#pragma subsystem(<kind>)`\n\ndrives the\nPE optional-header `Subsystem`\n\nbyte. The accepted kinds are\n`console`\n\n(default, `IMAGE_SUBSYSTEM_WINDOWS_CUI = 3`\n\n),\n`windows`\n\n(`IMAGE_SUBSYSTEM_WINDOWS_GUI = 2`\n\n), `native`\n\n(`IMAGE_SUBSYSTEM_NATIVE = 1`\n\n, with `nt`\n\n/ `driver`\n\nas\naliases), and the EFI variants `efi_application`\n\n,\n`efi_boot_service_driver`\n\n, `efi_runtime_driver`\n\n, and\n`efi_rom`\n\n. With `console`\n\n/ `windows`\n\n, `entrypoint(WinMain)`\n\nplus `subsystem(windows)`\n\nis what a Win32 GUI app needs to\nskip the loader's auto-attach to a console window. Non-PE\ntargets keep the default and ignore the directive, so the\nsame source builds for every OS.\n\nUnknown directives (and `#include`\n\ns that don't resolve through\nthe search-path / embedded-header chain) emit a warning rather\nthan failing the build; pass `-H`\n\n/ `--show-includes`\n\nto see\nthe gcc-`-H`\n\n-shape resolution trace on stderr.\n\nIf something is not available, define it yourself for a\nquick fix, open an issue or use runtime linking with `dlopen`\n\n/ `dlsym`\n\nor `LoadLibrary/GetProcAddress`\n\n:\n\n```\nint main() {\n    int *h, *fn;\n    h = dlopen(0, 2);                  // RTLD_NOW\n    fn = dlsym(h, \"strlen\");\n    return fn(\"hello, world!\");        // exits 13\n}\n```\n\n`dlopen(NULL, RTLD_NOW)`\n\nreturns the calling process's symbol\nscope -- libc on POSIX, the loaded set on Windows.\n\nFor a flavour of what's reachable from each system:\n\n**macOS**--`dlsym(h, \"objc_msgSend\")`\n\ngives you the Objective-C runtime entry point. The CoreFoundation / AppKit / Foundation surfaces are one`dlopen(\"/System/Library/.../X.framework/X\")`\n\naway.**Linux**--`clock_gettime`\n\n,`nanosleep`\n\n,`pipe2`\n\n, the entire`pthread_*`\n\nfamily. Anything in`/usr/lib`\n\n's sonames if you spell the path.**Windows**--`dlopen`\n\nresolves to`LoadLibraryA`\n\n, so`dlopen(\"user32.dll\", 0)`\n\nplus`dlsym(h, \"MessageBoxA\")`\n\ngives you a callable Win32 API entry point.\n\nSame encoder + relocations as the AOT path. badc mmaps the result\nexecutable, resolves libc through a runtime-built fake GOT, and\ncalls `main`\n\ndirectly via a transmuted function pointer. No\nsubprocess, no on-disk binary -- parse, lower, exec all happen\ninside the badc process:\n\n```\nbadc --jit tests/fixtures/c/c4.c hello.c       # JIT'd c4 self-hosts hello.c\n```\n\nFive hosts are supported:\n\n| host | mapping |\n|---|---|\n| Linux/aarch64 | mmap RW -> mprotect RX, manual `dc cvau` / `ic ivau` |\n| Linux/x86_64 | mmap RW -> mprotect RX, hardware-coherent I-cache (no-op) |\n| macOS/aarch64 | mmap RWX + `MAP_JIT` , `pthread_jit_write_protect_np` toggle |\n| Windows/x86_64 | VirtualAlloc RW -> VirtualProtect RX, FlushInstructionCache (no-op) |\n| Windows/aarch64 | VirtualAlloc RW -> VirtualProtect RX, FlushInstructionCache |\n\nlibc is bound at JIT time: a writable \"fake GOT\" gets one entry\nper resolved import, and the codegen's existing GOT relocations\nare patched against this region. POSIX uses `dlopen(NULL, RTLD_NOW)`\n\n+ `dlsym`\n\nto find each symbol in the loaded process;\nWindows uses `LoadLibraryA`\n\nper declared dylib (kernel32, msvcrt,\nws2_32, ...) + `GetProcAddress`\n\n. macOS uses Apple's `MAP_JIT`\n\n+\nper-thread W^X toggle for the hardware-enforced W^X on Apple\nSilicon.\n\nFor more, one can use `objdump`\n\n, `readelf`\n\n, etc.\n\nThe codegen always lowers through an SSA intermediate\nrepresentation and a graph-coloring register allocator. A\nhandful of cheap rewrites run unconditionally; `--optimize`\n\nadds a set of SSA passes on top.\n\nAlways on: drop self-`mov`\n\ns and fuse compare + branch into\n`cmp`\n\n/ `b.cond`\n\n(or `cmp`\n\n/ `jcc`\n\n) without materializing a `0`\n\n/`1`\n\nboolean in between. The register allocator builds an\ninterference graph over phi-congruence classes and colors it\ngreedily, spilling to frame slots only under pressure.\n\n`examples/bench.rs`\n\nruns a few pure-computation workloads\n(`fib32`\n\n, `quicksort-50k`\n\n, `matmul-50`\n\n) through the VM and the\nin-process JIT and reports per-iteration timings:\n\n```\ncargo run --release --example bench -- --iter 10\n```\n\n`--interp`\n\nruns the program through the SSA interpreter\ninstead of compiling to native:\n\n``` bash\n$ cargo run --quiet --features full -- --interp hello.c\n\nHello 123\nexit(0)\n```\n\nThe VM keeps code, stack, and data in three distinct address ranges\nand refuses to mix them. Function pointers carry a `CODE_BASE`\n\nbias; loading or storing through one is rejected, and so is\ncalling through a fabricated integer (`fp = 42; fp();`\n\n) -- the\ncall site refuses an address it didn't originate.\n\n`--track-pointers`\n\nopts in to allocation tracking. With it on,\n`free`\n\non an unknown or already-freed pointer errors, and any\naccess into a freed allocation (or past the end of a live one) is\nreported with the offending allocation's id. `--trace`\n\nopts in to\na per-instruction trace on stdout (off by default -- it's noisy).\n\nNative and JIT modes skip this safety net by design. Use\n`--interp`\n\nif you want the watchful version, especially while\ndebugging memory-shape issues.\n\nThe library compiles under `--no-default-features`\n\n:\n\n```\ncargo build --no-default-features --lib\n```\n\nIn that mode the `StdHost`\n\nadapter (file IO, env vars, real\nstdin/stdout) is gone. Consumers supply their own `Host`\n\nimpl and\nconstruct the VM with `Vm::with_host(program, my_host)`\n\n. Everything\nelse -- lexer, parser, preprocessor, VM dispatch, pointer tracking,\nnative backends -- runs on `extern crate alloc`\n\n.\n\nThe CLI binary requires the `std`\n\nand `full`\n\nfeatures (see the\ninstall section above).\n\n```\ncargo test --features full\n```\n\n`--features full`\n\nruns the full suite. A bare `cargo test`\n\nexercises\nonly the host-only JIT library (the default feature set), gating out\nthe `native*`\n\n, `linker`\n\n, and `dwarf`\n\nmodules that emit on-disk images.\n\nTests are split by what they exercise. `lexer`\n\n, `parser`\n\n, and\n`codegen`\n\ndrive each phase directly. `programs`\n\nand `intrinsics`\n\nload real C sources from `tests/fixtures/c/`\n\nand check the exit\ncode under the SSA interpreter. `types`\n\nchecks the\nwarning-not-error behaviour. `pointer_tracking`\n\nexercises the\nopt-in safety net. `native`\n\n, `native_elf`\n\n, `native_elf_x64`\n\n,\n`native_pe_x64`\n\n, and `native_pe_arm64`\n\ncompile each fixture\nthrough the matching backend and exec it under the host kernel,\nincluding an `-O`\n\nrerun that asserts the exit code is unchanged.\n`jit`\n\ncovers the in-process path the same way. `linker`\n\nexercises\nthe multi-TU object / archive path, `dwarf`\n\nthe debug-info emit,\nand `deferred`\n\nthe lazy-symbol resolution.\n\nA few fixtures under `tests/fixtures/c/`\n\nare worth reading on their\nown, each pinning a distinct hard feature:\n\n`c4.c`\n\n-- the original c4 compiler; self-hosts (see above).`fma_numeric_kernels.c`\n\n-- Horner polynomial evaluation, a dense matrix-product inner loop, and a fourth-order Runge-Kutta step, all multiply-add heavy; checks that the`-O`\n\nfused multiply-add contraction keeps single-rounding parity with the VM.`fma_contraction.c`\n\n-- the`a*b+c`\n\n/`a*b-c`\n\n/`c-a*b`\n\ncontraction shapes plus explicit C99`fma`\n\n/`fmaf`\n\n.`aapcs64_variadic_host_abi.c`\n\n,`sysv_variadic_host_abi.c`\n\n-- the per-target variadic calling conventions on the host ABI.`setjmp_longjmp_roundtrip.c`\n\n-- non-local control flow, including the CRT-free AArch64`setjmp`\n\n/`longjmp`\n\nintrinsic on Windows.`struct_by_value_param.c`\n\n,`struct_by_value_return.c`\n\n-- aggregate pass / return through the hidden out-pointer ABI.`bitfield_storage_unit.c`\n\n-- C99 6.7.2.1 bitfield packing across storage units.\n\nRelease builds add the JIT and native fixture-parity paths that debug builds skip:\n\n```\ncargo test --release --lib\n```\n\nCI runs the matrix on `ubuntu-latest`\n\n, `ubuntu-24.04-arm`\n\n,\n`macos-latest`\n\n, `windows-latest`\n\n, and `windows-11-arm`\n\n. Every\nrunner additionally runs the demo smokes -- sqlite3, miniz,\nkissfft, bzip2, tweetnacl, monocypher, bearssl, lua, stb,\nchibicc, tinycc, gui_hello, nt_loader -- end-to-end (or\nbuild-only for the GUI demos, which need a display). See\n[ demos/](/kromych/badc/blob/master/demos) for what each exercises. The PE-via-\nWINE lane is gated on\n\n`BADC_RUN_WINE=1`\n\n; a bare `cargo test`\n\non a developer machine skips it, and CI doesn't currently\nset it (the native Windows runners cover the same surface\ndirectly).`tools/core-walker.py`\n\nwalks the saved-rbp chain in a Linux ELF\ncore dump and reports each frame's saved return address as a\nfile offset into the original non-PIE x64 binary (load base\nfixed at `0x400000`\n\n). Useful for naming the crashing function\nwhen a higher-level debugger path is blocked. Modes:\n\n- default: walk the rbp chain, resolve each frame's saved return address.\n`--dump-around-rbp`\n\n: print the 16 8-byte slots around`rbp`\n\n.`--scan-stack`\n\n: ignore the rbp chain, scan upward from`rsp`\n\nfor any 8-byte slot that looks like a code address, and resolve each. Useful when stack corruption broke the rbp chain -- the actual return addresses are usually still on the stack, just no longer reachable through the saved-rbp links.`--list-segments`\n\n: list every PT_LOAD in the core file with its vaddr range. Useful for understanding where the stack and the emulator's mappings ended up after a corruption.\n\nThis is a personal educational/research project, it has not been\nsponsored or suggested by anyone, i.e. it is a product of my own\nvolition. That said, in no event I'll be responsible for how you\nuse this project or what happens due to that. See [LICENSE](/kromych/badc/blob/master/LICENSE)\nfor the exact terms.", "url": "https://wpnews.pro/news/badc-optimizing-cross-platform-c-compiler-built-with-claude", "canonical_source": "https://github.com/kromych/badc", "published_at": "2026-06-22 05:05:42+00:00", "updated_at": "2026-06-22 05:10:22.228525+00:00", "lang": "en", "topics": ["developer-tools", "artificial-intelligence", "large-language-models"], "entities": ["badc", "Claude", "Python", "macOS", "Linux", "Windows", "ARM64", "x86_64"], "alternates": {"html": "https://wpnews.pro/news/badc-optimizing-cross-platform-c-compiler-built-with-claude", "markdown": "https://wpnews.pro/news/badc-optimizing-cross-platform-c-compiler-built-with-claude.md", "text": "https://wpnews.pro/news/badc-optimizing-cross-platform-c-compiler-built-with-claude.txt", "jsonld": "https://wpnews.pro/news/badc-optimizing-cross-platform-c-compiler-built-with-claude.jsonld"}}