# RCC — Regoshi C Compiler

A fast, self-contained C23 C compiler targeting x86-64 on Windows and
Unix, and AArch64 (ARM64) on elf and darwin. Written from scratch in
C11 by Hosokawa-t, a 16 year old student. And then ported to linux,
arm64 and fixed the rest by Reini Urban.

The goal is to be fast, almost as fast as tcc, but with full gcc
compatibility and some inexpensive optimizations. We pass much more
tests than all other C compilers, we do support all standards, just
not the backwards incompatible ones. I.e. C11 `int nullptr;` is wrong.

## Benchmark Results

Six workloads: Fibonacci(38), Ackermann(3,10), Sieve of Eratosthenes (1M), 128×128 matrix multiply, floating-point math loop (500K), and bubble sort (5K).

Windows:

| Compiler   | Execute (ms) | Compile (ms) | Total (ms) |
| ---------- | -----------: | -----------: | ---------: |
| RCC        |         1201 |          472 |       1673 |
| TCC 0.9.27 |         1194 |          385 |       1579 |
| GCC -O0    |         1191 |          362 |       1553 |
| GCC -O2    |         1192 |           91 |       1283 |
| CLANG -O2  |         1196 |          145 |       1341 |

Linux:

| Compiler  | Compile (ms) | Execute (ms) | Total (ms) |
| :-------- | -----------: | -----------: | ---------: |
| RCC       |           19 |          583 |        602 |
| RCC -O1   |           17 |          576 |        593 |
| RCC -O2   |       **13** |          560 |    **573** |
| TCC       |           14 |          580 |        594 |
| SLIMCC    |           51 |          630 |        681 |
| XCC       |           14 |      **355** |    **369** |
| KEFIR     |          236 |          695 |        931 |
| KEFIR -O1 |          194 |          521 |        715 |
| CCC       |           37 |          584 |        621 |
| GCC -O0   |           75 |          581 |        656 |
| GCC -O2   |          202 |          215 |        417 |
| Clang -O0 |          106 |          687 |        793 |
| Clang -O2 |          177 |          246 |        423 |

- RCC vs TCC vs GCC -O2 execution: same speed on windows, competitive on linux.
- All outputs verified correct against TCC, GCC -O2 and CLANG -O2 references.
- **Compile-time performance**: RCC has now it's own native linker, same as TCC. The peephole optimizer uses a 3-line sliding window (single pass over emitted asm), while TCC works on an internal abstract representation. Together these account for the compile-time gap. Faster branch patching as in TCC is in works. Generated code quality is on par with TCC. CCC is claudes-c-compiler vibe-coded in rust, which can compile the kernel. XCC is very fast, but cannot compile much.

rcc -O1 -time:

    preprocess  bench.c:   7916 us
    parse       bench.c:    721 us
    typecheck   bench.c:     87 us
    opt         bench.c:     45 us
    codegen     bench.c:   1247 us
    link        bench_o1:   294 us

## Test Results

Linux x86-64, combined across all test suites (TCC compatibility,
RCC unit tests, NCC compliance vs GCC, c-testsuite, GCC torture —
test/torture/), generated by
[`test-all-compilers.sh`](test-all-compilers.sh). The failure breakdown
is torture-only (the suite with compile/runtime/dg-error granularity):
`c` = unexpected compile failure, `e` = dg-error/dg-warning test that
didn't report every expected error line, `r` = runtime failure.

<!-- TEST_RESULTS_TABLE_START -->

| Compiler | Passed | Failed | Skipped | Notes                      |
| -------- | ------ | ------ | ------- | -------------------------- |
| rcc      | 4195   | 0      | 354     | 100% pass rate             |
| gcc      | 4271   | 86     | 196     | 98%, 39c/1r failures       |
| ccc      | 3897   | 241    | 413     | 94%, 151c/31r failures     |
| clang    | 3708   | 649    | 191     | 85%, 530c/36e/15r failures |
| tcc      | 2676   | 480    | 1395    | 84%, 383c/17r failures     |
| kefir    | 2989   | 996    | 566     | 74%, 892c/15r failures     |
| antcc    | 1959   | 1091   | 1394    | 64%, 974c/23r failures     |
| slimcc   | 2077   | 1397   | 1077    | 59%, 1324c/7r failures     |
| lacc     | 1403   | 1967   | 1073    | 41%, 1771c/14r failures    |
| scc      | 1430   | 2569   | 444     | 35%, 2389c/31r failures    |
| xcc      | 753    | 2541   | 1256    | 22%, 2123c failures        |
| cproc    | 332    | 3707   | 404     | 8%, 3347c failures         |

<!-- TEST_RESULTS_TABLE_END -->

All compilers but rcc fail the -Whomoglyph test/test_unicode.c

## Key Features

- **Register-machine codegen** — 8-register allocator on x86-64 (r10, r11, rbx, r12–r15, rsi), 12-register on ARM64 (x10–x15, x19–x24) with dynamic allocation, no stack machine overhead. The register allocator is a simple first-fit bitmask with no spilling to stack except for the predefined spill slots. If all registers are in use, it spills the additional registers on the stack. Currently with a spill warning on -W.
- **Two-pass function emission** — Body generated to buffer first; prologue only pushes callee-saved registers actually used. Recursive functions like `fib` get zero callee-saved pushes.
- **Peephole optimizer** — Integrated inline peephole optimizer with:
  - Copy propagation (`mov r10, rax; mov r12, r10` → `mov r12, rax`)
  - Immediate folding (`mov r10, 1; add r11, r10` → `add r11, 1`)
  - Identity elimination (`mov r10, 0; add r11, r10` → deleted)
  - Strength reduction (multiply by power-of-2 → shift) in codegen already.
  - 3-instruction chain folding (`load; op; mov dst` → `load dst; op dst`)
  - Dead jump elimination (`jmp .L; .L:` → `.L:`)
  - Operates on emitted bytecode via `asm_record`/`asm_peep_try` with no separate pass
- **Shadow space** — Maximal 32-byte shadow space in stack frame; no `sub rsp`/`add rsp` per call for ≤4 args.
- **Compile-Time Function Execution (CTFE)** — AST interpreter evaluates pure functions with constant arguments at compile time with -O1.
- **C preprocessor** — `#include`, `#define`, `#ifdef`/`#ifndef`/`#if`, `#pragma once`, macro expansion with token pasting.
- **Floating-point support** — `float`/`double/long double` arithmetic, casts, function calls via SSE2 on x86-64 (xmm0–xmm7) or via ARM64 NEON/FP (v0–v7). 80-bit long double x87 on x86-64 via `fld`/`fstp` (truncated to 64 bits on store). ARM64 ELF 128-bit long double passed in register pairs (v0–v7 in even-odd pairs) following the AAPCS64 calling convention. Float args properly classified as SSE/FP class with separate GP/FP argument counters. ARM64 on APPLE only uses 8-byte doubles.
- **Vector/SIMD support** — x86-64 SSE/AVX and ARM64 NEON vector types via `__attribute__((vector_size(N)))`, with full arithmetic, comparison, shuffle, and broadcast operations on float and integer element types.
- **Windows x64 ABI** — Shadow space, correct volatile/non-volatile register handling, 16-byte stack alignment.
- **SystemV x64 ABI** — No Shadow space. amd64 calling convention. Float and struct alignment specialities.
- **ARM64 ABI (AAPCS64)** — x29 frame pointer, x30 link register, x0–x7 argument/return registers, x8 indirect result register, x9–x15 caller-saved, x19–x28 callee-saved. Variadic args passed on the stack. 16-byte stack alignment. NEON v0–v7 for FP/SIMD args; long double pairs on ELF use even-odd register pairs.
- **Inline builtins** — `memset`, `memcpy`, `memcmp`, `strlen`, `strcmp`, `strchr` expanded inline(`rep stosb`/`rep movsb`/`repe cmpsb`/`repne scasb`/ byte loops), avoiding libc call overhead. Also most other GCC/clang builtins, and `_FORTIFY_SOURCE` check functions. Mandatory SSE4.2 not yet.
- **Bounds checking builtins** — `__builtin_object_size` returns compile-time size for arrays/structs, `(size_t)-1` for pointers. `__builtin_dynamic_object_size` additionally reads the glibc malloc chunk header at runtime for heap pointers, returning the actual allocated size (may be larger than requested due to rounding). Unlike GCC -O2 which tracks malloc size through the optimizer, rcc reads the chunk metadata.
- **Insecure C11-C26 unicode identifier** checks, instead using true TR39 advised homoglyph/confusable checks via my [libu8indent](https://github.com/rurban/libu8ident/) library. Checking unicode security guidelines for identifiers.
- Simple function inliner and const-loop unroller with -O2.

## Supported C Features

Structs, unions, enums, typedefs, arrays (multi-dimensional), pointers (including function pointers), `for`/`while`/`do-while`/`switch`/`goto`, computed goto (`&&label`, including label-address differences `&&a - &&b` in `static` initializers), `sizeof`, `_Bool`, `static`, `extern`, C23 `constexpr`, `static_assert`, `nullptr`, `bool`/`true`/`false`, `[[attributes]]`, `__has_c_attribute`, `__has_include`, `<stdckdint.h>`, `0b` binary, digit separators, `u8` prefix, `__auto_type`, `__VA_OPT__`, `enum` > `int`, C23 `_BitInt(N)` / `unsigned _BitInt(N)` (including N > 64 via an embedded multi-limb runtime), `_Decimal32`/`_Decimal64`/`_Decimal128` (IEEE 754-2008 decimal floating point via the bundled libdfp/libbid runtime), `#warning`/`#error`/`#elifdef`/`#elifndef`, `__attribute__((warning/error/diagnose_if))`, `__builtin_object_size`/`__builtin_dynamic_object_size`, variadic `printf`, string literals, compound assignment operators, pre/post increment, ternary operator, comma operator, designated initializers, \_Generic, attribute `__cleanup__`, `__aligned__`, `__packed__`, `__constructor__`, `__destructor__`, `vector_size`, `section("name")`, c23 [[attribute]], Windows and SystemV long doubles (internally all using SSE), ARM64 long doubles (128-bit quad precision via register pairs in elf, 8 byte on APPLE), safe unicode identifiers and strings (unlike C11/C23), minimal `"wchar.h"`, inline, weak, `target_clones` (FMV with IFUNC resolver, asm .altinstr_replacements), gcc/enum/ms bitfields, old K&R function definitions, VLA's, atomics (LL/SC on ARM64, xadd/lock on x86), GNU alias, args... macro syntax, basic -g DWARF debugging support (line numbers only), most GCC extensions and builtins, -fpie, -fpic, TLS, int128, `_Complex`/`__complex__`, `_FORTIFY_SOURCE`, SIMD/NEON xmmintrin.h support, `#embed`, `_BitInt`, defer.

TODO: full \_Float16/\_Float32/\_Float64/\_Float128 support (still aliased to float/double/long double), `__STDC_IEC_60559_TYPES__` and `__STDC_DEC_FP__` feature macros.

Unsupported (skipped in torture tests):

- **GNU nested functions with escaping function pointers** — `int f2(...){...}` defined inside another function and passed/stored as a value (`g(f2)`) is supported on Linux (x86-64 and ARM64) via a runtime trampoline: a small per-activation stub written into the enclosing function's own stack frame at the point of reference, loading the static-chain pointer and jumping to the nested function's real code. The trampoline's executability relies on the standard `.note.GNU-stack` executable-stack ELF marking (matching what GCC itself emits for its own nested functions) rather than a runtime `mprotect` call, since libgcc's `__enable_execute_stack` is unreliable across real environments. Not yet implemented on Darwin (Mach-O) or Windows (PE) — no equivalent executable-stack wiring there yet. Direct calls to a nested function, and variable/`&&label` capture through the enclosing function's frame (static-chain pointer: `%r10` on x86-64, `x18` on AArch64), are supported on x86-64 Linux/Windows and ARM64 — including nonlocal `goto` from a nested function back into an enclosing `__label__`. K&R-style and VLA-typed nested function parameters aren't supported yet.
- **VLA struct member `offsetof`** — rcc stores VLA array members as fat pointers (size=16, align=8), which gives different member offsets than GCC's flat in-struct layout.
- `__attribute__((` **scalar_storage_order** `()))`, `__attribute__((` **mode** `()))`
- `-finstrument`, use perf instead.

Top-level `__asm__("...")` statements in AT&T, Intel or ARM syntax are supported and emitted in source order. Unlike GCC (which hoists all file-scope `asm` blocks to the top of the output at `-O2`/`-O3` unless `-fno-toplevel-reorder` is used), rcc always preserves their original position relative to functions.

The test suites has all tests passed on linux, darwin, windows, mingw-cross,
arm64-cross, darwin-cross.

## Build

```bash
gcc -std=c11 -O2 -o rcc src/main.c src/lexer.c src/parser.c src/type.c src/codegen.c src/alloc.c src/preprocess.c src/opt.c
```

## Usage

```bash
# Compile to executable
./rcc.exe -o output.exe source.c ...

# Output assembly
./rcc.exe -S -o output.S source.c

# Run tests and benchmark
make check
make bench
```

`run_tests` supports `--parallel` (auto-detect worker count from CPU
core count) and `--jobs N`. `make check`, `make test-all`, `make
test-torture` and `make test-full` all run with `--parallel` (At least
~2x faster on the full TCC+torture+compliance+c-testsuite run). The
cross-test runners `mingw-test.sh` (under Wine), `arm64-test.sh`
(under qemu), `darwin-test.sh` (compilation only, no Mach-O execution
on Linux) also run their full suites in parallel. Since run_tests runs
now in the cross environment natively (qemu or wine), same as the
compiler and tests, it is much faster now.

## Options

    -I path            add include path
    -include file      pre-include header
    -Dname[=val]       define a macro value
    -Uname             undefine a macro value
    -E                 preprocessor-only
    -S                 assemble-only
    -c                 compile-only
    -o file            set output filename
    -O0                disable peephole optimizer
    -O1                enable CTFE optimizations
    -O2                enable -finline, -funroll optimizations
    -g                 emit DWARF line-number debug info
    -std={c23,c17,c11,c99,c89,...}
                       sets __STDC_VERSION__
    -nostdinc          do not search system include directories
    -W                 print diagnostic warnings (-Wshadow, stack spilling with -v)
    -Werror            error on all warnings (but not internal stack spill warnings)
    -pedantic-errors   same
    -Wfatal-errors     exit at the first error (errors are collected otherwise)
    -fmax-errors=N     exit after N errors (default 20, 0 = unlimited)
    -Wno-homoglypth    suppress homoglyph unicode identifier warnings
    -Wunknown-warning-option
                       for autoconf probes
    -Lpath             add linker path
    -lname             add lib
    -pthread           link with pthreads library
    -shared            create shared library
    -static            link statically
    -rdynamic          export all symbols to the dynamic symbol table (=> -Wl,-E)
    -nodefaultlibs     no libc
    -rpath path        => -Wl,-rpath,path
    -soname name       => -Wl,-soname,name
    -Wp,-MMD,file      write make dependency rules
    -MD, -MMD          write make dependency rules to a .d file
    -MF file           set the dependency output file
    -MT target         set the dependency rule target
    -MQ target         like -MT, quoting make metacharacters
    -MP                add phony targets for each prerequisite
    -Wl,<opt>          pass option to linker
    -mms-bitfields     use MSVC bitfields (default on Windows)
    -mno-ms-bitfields  use GCC bitfields (default on non-Windows)
    -pie|-fPIE|-fpie   generate position-independent executable
    -fPIC|-fpic        generate position-independent code
    -fmacro-prefix-map=old=new  remap paths in diagnostics
    -time              print timing for each compilation substep
    -v                 be more verbose
    -xc                treat input as C
    -x none            reset language input
    -###               dry-run (print commands, don't execute)
    -dM                dump all macro definitions (use with -E)
    -fdump-ast         dump AST to stderr for debugging
    -fexec-charset=cs  set execution character set (default UTF-8)
    -print-search-dirs print install, include and library paths
    --help
    --version

## Project Structure

| File                  | Description                                                                 |
| --------------------- | --------------------------------------------------------------------------- |
| `src/main.c`          | Driver: CLI, assembler/linker invocation                                    |
| `src/lexer.c`         | Tokenizer with number/string/char literal support                           |
| `src/preprocess.c`    | C preprocessor (`#include`, `#define`, `#if`, macros)                       |
| `src/parser.c`        | Recursive-descent parser → AST                                              |
| `src/type.c`          | Type system (primitives, pointers, arrays, structs, functions)              |
| `src/codegen.c`       | x86-64/ARM64 code generator with register allocator and peephole optimizer  |
| `src/opt.c`           | AST-level optimizer and CTFE interpreter                                    |
| `src/alloc.c`         | Arena memory allocator                                                      |
| `src/unicode.{c,h}`   | libu8ident unicode identifier checks                                        |
| `src/rcc.h`           | Shared data structures and declarations                                     |
| `src/asm.{c,h}`       | Built-in assembler: parse generated `.s` text → `ObjFile` → ELF/Mach-O/COFF |
| `src/codegen_asm.h`   | Codegen ASM wrappers — emit assembled bytes directly via encoder functions  |
| `src/x86_enc.{c,h}`   | x86-64 instruction encoder (ModR/M, REX, VEX, SSE/AVX)                      |
| `src/arm64_enc.{c,h}` | ARM64 / AArch64 instruction encoder                                         |
| `src/obj.{c,h}`       | Object file representation: sections, symbols, relocations, SecBuf          |
| `src/elf_write.c`     | ELF object file writer                                                      |
| `src/macho_write.c`   | Mach-O object file writer                                                   |
| `src/coff_write.c`    | COFF object file writer                                                     |
| `include/`            | Minimal C standard library headers (`stdio.h`, `math.h`, etc.)              |
| `bench/`              | Benchmark suite and runner script                                           |
| `test/`               | Test programs                                                               |

## Unix fork

The original windows repo is now at https://github.com/DocDamage/realtime-c-compiler with
[those](tcc_test_report_mingw1.1.md) test results (61/129 passed tcc tests), and [those](https://github.com/rurban/rcc/blob/old-mingw/bench/bench_report_mingw.md) benchmarks. Tested in the `old-mingw` branch via github actions.

This fork passes all tests for all architectures (x86_64 on linux and windows, aarch64 on macos) on all our testsuites. 4004/4381 GCC torture tests passed on Linux, Windows and macOS (100%). It passes much more tests than clang, gcc and all other known C compilers.

## Old Known Limitations

- **GNU Assembler (GAS) ≥2.45 on x86-64**: `call` and `jmp` to global labels in
  Intel syntax cause `operand type mismatch` errors. RCC emits `.intel_syntax noprefix`
  by default, but GAS ≥2.45 rejects direct branches to global symbols in this mode.
  Local labels (`.L.xxx`) work fine. Tests with user-defined function calls
  (`bitops-1`, `fprintf-1`, etc.) may fail to assemble under these versions.
  Root cause: rcc emits `lea r11, [rip + sI]` but GAS requires AT&T `sI(%rip)` for globals.
  Affected: most of the big torture tests with many global structs —
  20040709-1/2/3, 20071018-1, 20071030-1, 20080502-1, 20080506-1, 930106-1
  Workaround: assemble with `as --32` or use an older binutils (<2.45).
  That's why we had to switch from Intel syntax to AT&T syntax.

## License

LGPL-2.1 — see [LICENSE](LICENSE) file.
