Tiny C Compiler
The Tiny C Compiler (TCC), also known as TinyCC, is a small, fast, and self-contained C compiler that enables the direct compilation and execution of C source code without requiring external assemblers or linkers, supporting most of the ISO C99 standard along with many GNU C extensions such as inline assembly.[1] Originally developed by French programmer Fabrice Bellard starting in 2001 as an evolution of his Obfuscated Tiny C Compiler project, TCC is licensed under the GNU Lesser General Public License (LGPL) and is designed for efficiency, achieving compilation speeds approximately 10 times faster than GCC with the -O0 optimization flag on comparable hardware.[2][3] TCC's core strengths lie in its minimal footprint and versatility, generating optimized machine code for multiple architectures including i386, x86-64, ARM, AArch64, and RISC-V64, while producing ELF objects and executables on Linux or PE formats for Windows.[3] It includes an integrated assembler with GAS-like syntax and a lightweight linker, allowing it to compile itself and handle dynamic libraries seamlessly, with optional features like memory and bound checking via the -b flag for safer code execution.[1] The compiler's one-pass code generation and register-based approach prioritize speed over extensive optimizations, making it ideal for resource-constrained environments such as rescue disks, embedded systems, or rapid prototyping.[2] Beyond traditional compilation, TCC supports C as a scripting language through its -run option (e.g.,#!/usr/bin/env tcc -run), enabling immediate execution of scripts much like interpreted languages, and provides libtcc, a library for just-in-time (JIT) code generation that can be embedded in other programs for dynamic compilation.[1][3] After Bellard ceased active development around 2018, the project transitioned to community maintenance via the official repository at repo.or.cz, where ongoing enhancements include bug fixes, RISC-V support improvements, and compatibility updates, with the latest stable release being version 0.9.27 from December 2017 and active commits as recent as November 2025.[4] These aspects have made TCC a notable tool in scenarios requiring quick builds, such as browser engines like Links or educational programming environments.[2]
Overview
Description and Purpose
The Tiny C Compiler (TCC) is a self-contained C compiler that integrates an assembler and linker into a single executable, historically measuring around 100 KB for the x86 version, including the C preprocessor.[2] This compact design makes it suitable for environments with limited storage and computational resources.[5] TCC's primary purpose is to enable rapid compilation of C code on low-resource systems, such as rescue disks or embedded devices, without requiring external dependencies like separate assemblers or linkers.[2] It prioritizes simplicity and execution speed over advanced optimizations, allowing users to treat C as a scripting language by directly running source files through command-line options.[6] For instance, scripts can be executed immediately using shebang lines like#!/usr/bin/tcc -run, producing performance comparable to natively compiled binaries.[2]
Initially created by Fabrice Bellard in 2001 as a demonstration of minimalism, TCC originated from his entry in the International Obfuscated C Code Contest (IOCCC).[7]
Design Principles
The Tiny C Compiler (TCC) embodies a minimalist design philosophy, prioritizing a compact footprint and rapid compilation over comprehensive feature sets found in larger compilers. Its codebase, primarily written in ANSI C with targeted assembly for architecture-specific components, spans fewer than 100,000 lines, enabling easy maintenance and adaptation. This lightweight structure facilitates single-pass compilation, where the parser—hardcoded for efficiency—processes source code directly into machine code without generating intermediate representations beyond a simple value stack for expressions.[1] Such an approach reduces complexity and memory usage, allowing TCC to compile substantial projects in seconds on modest hardware.[2] A key trade-off in TCC's design is the emphasis on functionality and compilation speed at the expense of advanced optimizations. While it performs basic expression-level optimizations, such as constant folding, it forgoes inter-procedural or global analyses to maintain its small size and swift execution. This results in generated code that prioritizes correctness and portability over peak runtime performance, making TCC suitable for scenarios where quick iteration outweighs marginal efficiency gains.[1] The compiler's self-sufficiency further underscores this philosophy: it integrates a full assembler and dynamic linker, eliminating dependencies on external tools like GNU Binutils, thus enabling standalone operation from source to executable in a single invocation.[6][1] Portability is a core tenet, achieved through a modular architecture that isolates platform-specific code in minimal assembly routines while keeping the bulk in portable C. This design allows straightforward ports to new architectures, such as from x86 to ARM, by adapting only the backend generator without overhauling the frontend.[1] Additionally, TCC's speed supports scripting-like usage, where C source files can be executed directly via a shebang, akin to interpreted languages.[2]History
Origins and Early Development
The Tiny C Compiler (TCC) originated as the Obfuscated Tiny C Compiler (OTCC), an entry in the 2001 International Obfuscated C Code Contest (IOCCC) submitted by French programmer Fabrice Bellard.[7][8] This contest submission was designed as a highly compact and obfuscated program that could compile a subset of the C language into efficient i386 machine code for Linux systems, demonstrating the feasibility of a self-contained compiler without relying on external assemblers or linkers.[7] Bellard's early motivations for OTCC centered on showcasing that a complete C compiler could be implemented in an extremely small footprint—under 100KB for code, data, and symbols—while still being practically useful, including the ability to bootstrap itself by compiling its own source code.[7][8] The project initially targeted the x86 architecture, specifically i386, and provided basic support for ANSI C features such as binary operators, function calls, loops, and integration with the standard C library via dynamic linking.[7] Following its public debut through the IOCCC in 2001, OTCC quickly evolved from a contest demonstration into the more robust and functional TCC around 2001–2002, with the initial version made available for download on Bellard's personal website.[2][8] This transition expanded its utility beyond obfuscation, establishing TCC as a lightweight alternative for rapid C compilation on x86 platforms.[2] Bellard, renowned for developing other compact software tools like QEMU and FFmpeg, leveraged his expertise in efficient code generation to refine the compiler's core during this foundational period.[2]Key Milestones and Releases
Following the initial development in the early 2000s, the Tiny C Compiler (TCC) saw several key enhancements in platform support and functionality through subsequent releases. Version 0.9.23, released on June 17, 2005, introduced initial support for generating Portable Executable (PE) files, enabling TCC to target the Windows operating system and compile Windows programs directly. In version 0.9.25, released on May 20, 2009, TCC added support for the x86-64 architecture, expanding its compatibility to 64-bit x86 systems and improving performance on modern hardware.[9][10] Version 0.9.26, released on February 15, 2013, further advanced cross-platform capabilities with improvements to x86-64 handling and the addition of ARM hardfloat support, facilitating compilation for ARM-based embedded systems.[9][10] Fabrice Bellard ceased active development on TCC prior to February 4, 2012, after which the project transitioned to community maintenance. The final official release under the original development model, version 0.9.27, was announced by community contributor grischka on December 17, 2017, incorporating enhancements such as ARM64 (aarch64) target support contributed by Edmund Grimley Evans.[11] Post-2017, community efforts via Git repositories have focused on patches for additional architectures, including experimental support for ARM variants and RISC-V targets through dedicated ports.[11][12]Technical Specifications
Supported Standards and Platforms
The Tiny C Compiler (TCC) provides full support for ANSI C, corresponding to the C89/C90 standards, including features such as structure bit fields and floating-point types like long double, double, and float.[1] It implements most features of the ISO C99 standard, such as variable-length arrays and inline functions, but has limitations including the absence of support for complex and imaginary number types.[1] TCC does not support later standards like C11 or C17.[1] In addition to standard C compliance, TCC incorporates many GNU C extensions, notably inline assembly using a syntax similar to GNU Assembler (gas).[1] This allows developers to embed architecture-specific assembly code directly within C programs for optimizations.[1] In the stable release (version 0.9.27 from 2018), TCC primarily targets the i386 architecture, with alpha ports for ARM and TMS320C67xx.[1] The ongoing development (mob) branch, actively maintained as of November 2025, extends support to several processor architectures, including x86 (32-bit), x86-64, ARM (32-bit and 64-bit/AArch64), and RISC-V (64-bit, primarily RV64 with partial implementation), along with the older TMS320C67xx port.[3] TCC operates on various operating systems, including Linux and Windows (producing PE-format executables and DLLs).[1] The development branch adds support for other Unix-like systems, BSD variants, and macOS (both Intel and Apple Silicon via AArch64), as of November 2025.[3] TCC supports cross-compilation, enabling the generation of executables for different architectures and environments, such as embedded systems or rescue disks, through configuration options like -m32 or -m64 for x86 variants.[1] This capability, enhanced in the development branch, makes it suitable for rapid prototyping in diverse hardware setups without requiring native compilation.[3]Architecture and Components
The Tiny C Compiler (TCC) employs a single-pass design that integrates lexical analysis, parsing, code generation, assembly, and linking into a unified phase, enabling direct production of executable binaries without reliance on external tools.[1] This approach processes the entire input source in one sequential traversal, where the lexer reads tokens via thenext() function—storing the current token in tok and additional data in tokc—followed immediately by parsing and code emission.[1] As a result, TCC generates relocatable or executable object files, such as ELF or PE formats, in a streamlined manner that prioritizes simplicity and speed.[1]
At its core, TCC consists of several integrated components: the main TCC executable, which handles standalone compilation of C source files; the libtcc library, which provides an API for embedding TCC in applications to compile and execute C code dynamically from strings or memory buffers; and built-in assembler and linker modules.[1] The assembler supports a gas-like syntax for inline assembly and separate .S or .s files, processing x86 opcodes in ATT format, while the linker performs a single pass over object files and libraries to eliminate unreferenced code and produce final outputs.[1] These components operate cohesively within the single-pass framework, with code generation relying on a register-based model and a value stack to emit machine code directly for supported architectures like x86 and ARM.[1]
TCC's codebase is primarily written in C, augmented by architecture-specific assembly code to handle low-level operations, and utilizes a recursive descent parser for its syntax analysis to maintain the compiler's lightweight and single-pass nature.[1] The parser is largely hardcoded for efficiency, processing most C constructs in a top-down manner during the initial scan, though it includes special handling for certain recursive structures.[1] For memory management, TCC offers an optional bounds-checking mode activated via the -b flag, which inserts runtime checks compatible with unchecked code, drawing from established techniques in safe C extensions.[1][13]
Features
Compilation and Linking
The Tiny C Compiler (TCC) is invoked via the command line using the syntaxtcc [options] [source_files] -o output, where source files are typically .c files, and the -o flag specifies the output executable name.[1] Common options include -g to generate debug information compatible with tools like GDB, and -c to produce an object file (.o) without linking.[1] For example, compiling a single source file hello.c into an executable hello is achieved with tcc hello.c -o hello.[1] TCC accepts many GCC-compatible options for compatibility, but advanced ones such as optimization levels (-O0 to -O2) are ignored, as TCC performs only basic expression-level optimizations like constant propagation and shift optimizations for multiplications and divisions where applicable.[1]
A key feature of TCC is its integrated linker, which eliminates the need for external tools like ld or as by handling object files, static libraries (.a), dynamic libraries (.so or .dll), and symbol relocation internally during compilation.[1] This self-contained approach allows TCC to process multiple input files—such as tcc file1.c file2.o libmath.a -o program—directly into a linked executable, resolving dependencies and applying relocations on the fly.[1] The linker supports both static linking via the -static flag and dynamic linking by default, enabling the use of shared libraries without additional steps.[1]
TCC generates executables in platform-specific formats: ELF (Executable and Linkable Format) for Linux and Unix-like systems, and PE (Portable Executable) for Windows, producing .exe files or DLLs as needed.[1] On Windows hosts, TCC generates PE executables, while ELF is the default on supported hosts.[1] This format support ensures portability across multiple architectures including x86, x86-64, ARM, AArch64, and RISC-V without requiring separate configuration.[1][5] Additionally, the libtcc library allows embedding TCC's compilation and linking capabilities into other programs for runtime code generation.[1]
Runtime and Extension Capabilities
The Tiny C Compiler (TCC) supports scripting capabilities that allow C code to be executed directly without traditional compilation and linking steps, treating C as a scripting language. Using the commandtcc -run source.c, TCC compiles the source file in memory and immediately executes it, passing any additional arguments to the main function. This feature enables the creation of executable scripts with a shebang line, such as #!/usr/local/bin/tcc -run, followed by the C code, making it suitable for rapid prototyping or ad-hoc computations. For instance, input from standard input can be compiled and run via tcc -run -, as in echo 'int main(){puts("hello");}' | tcc -run -.[1]
TCC's libtcc library provides an API for embedding the compiler into other programs, facilitating dynamic code generation and runtime execution. Key functions include tcc_new(), which initializes a new TCC compilation state; tcc_compile_string(), which compiles a C source string into the state; and tcc_relocate(), which relocates the compiled code into memory for execution, often via memory mapping. This API allows programs to load and run C code at runtime, accessing global symbols such as functions and variables dynamically. For example, a simple test implementation in libtcc_test.c demonstrates compiling a string like int add(int a, int b){return a+b;} and calling the resulting function.[1]
To enhance safety during runtime execution, TCC offers bounds and memory checking options. The -b flag instruments the compiled code to include runtime checks for array bounds, pointer arithmetic, and memory allocation (supported only on i386), detecting issues like out-of-bounds access or double frees, though it increases code size and execution time. These checks are compatible with existing unchecked code and assume valid pointers from external sources, providing a debugging aid without altering the program's logic.[1]
These runtime and extension features make TCC particularly valuable for embedding in interpreters or just-in-time (JIT) compilation scenarios, where dynamic code loading and execution are required. By leveraging libtcc, developers can integrate TCC as a backend for generating and running machine code on-the-fly, supporting targets like x86, x86-64, ARM, and others.[1]
Performance
Compilation Efficiency
The Tiny C Compiler (TCC) demonstrates exceptional compilation speed, achieving approximately 9 times the performance of GCC 3.2 in benchmarks conducted on a 2.4 GHz Pentium 4 processor.[2] For instance, compiling the Links Browser project, consisting of 76,936 lines of C code (expanding to 1,950,947 lines or 67.2 MB due to header inclusions), took 2.27 seconds with TCC version 0.9.22, compared to 20.0 seconds with GCC at optimization level -O0; this process included compilation, assembly, and linking, with real time measured.[2] TCC's efficiency stems from its single-pass design, where the parser processes the source code in one traversal without generating an intermediate representation beyond a value stack, directly producing linked binary code.[1] This approach minimizes analysis phases, limiting optimizations to basic expression-level constant propagation and register allocation using three temporary registers, thereby reducing overhead compared to multi-pass compilers like GCC.[1] Benchmark metrics further illustrate this advantage: TCC processed the aforementioned project at 859,000 lines per second and 29.6 MB per second, versus GCC's 98,000 lines per second and 3.4 MB per second.[2]| Compiler | Time (s) | Lines/second | MB/second |
|---|---|---|---|
| TCC 0.9.22 | 2.27 | 859,000 | 29.6 |
| GCC 3.2 -O0 | 20.0 | 98,000 | 3.4 |
Executable Characteristics
The executables produced by the Tiny C Compiler (TCC) are characterized by their relatively small size for simple programs, owing to TCC's minimalist design and lack of advanced optimizations that might introduce additional code. However, for more complex programs, TCC outputs tend to be larger than those from optimized compilers like GCC, primarily because of limited dead-code elimination capabilities that fail to remove unused code segments effectively.[2] This results in more conservative binaries that retain more instructions than necessary, though they remain functional across supported platforms. In terms of runtime performance, TCC-generated executables are generally slower than those from GCC, particularly for compute-intensive tasks, due to the absence of sophisticated optimizations.[2] TCC's basic register allocation strategy allocates registers in a straightforward manner without advanced heuristics, and it lacks features like function inlining or loop unrolling, leading to code that executes more calls and branches than optimized alternatives.[1] An illustrative example is using a modified version of TCC to compile the GCC compiler itself; while the process succeeds and produces a functional executable, the resulting binary is less efficient in both size and speed compared to self-compilation with GCC, highlighting TCC's trade-offs in favor of rapid compilation over peak runtime efficiency.[2] This enables quick iterations during development, where frequent rebuilds are prioritized over final optimized performance.Applications
Primary Use Cases
The Tiny C Compiler (TCC) is particularly valued in resource-constrained environments, such as rescue disks, embedded systems, or hardware with limited processing power, where its small footprint and rapid compilation enable quick prototyping without relying on heavier toolchains.[1] As a self-contained compiler that requires no external assemblers or linkers, TCC facilitates development on low-resource setups by generating executables efficiently on platforms like i386, x86-64, ARM, AArch64, and RISC-V under Linux and Windows, with experimental support for TMS320C67xx targets.[1][3] In scripting and automation tasks, TCC allows C code to function as a fast scripting language similar to Perl or Python, ideal for system tools or one-off programs that benefit from immediate execution.[1] Users can create executable scripts by prefixing C source files with a shebang line like#!/usr/local/bin/tcc -run, enabling compilation and runtime to occur seamlessly in a single step, which supports rapid iteration for administrative or utility scripts.[1] The library libtcc further enhances this by providing backend support for dynamic code generation within applications.[1]
For educational purposes, TCC serves as an accessible tool for teaching compiler concepts and C programming, owing to its straightforward design and transparent internals that demystify the compilation process.[1] Its support for ANSI C, ISO C99, and many GNU C extensions, combined with a developer's guide detailing its architecture, makes it suitable for students and instructors exploring language implementation without the complexity of larger compilers.[1]
TCC also finds application in cross-platform development, allowing developers to build small utilities across Linux, Windows, and Unix-like systems without installing extensive toolchains.[1] By targeting multiple architectures natively and maintaining a lightweight presence, it streamlines the creation of portable code for diverse environments, from desktop prototyping to basic system components.[1]
Notable Projects and Integrations
TCCBOOT is a compact boot loader developed by Fabrice Bellard that leverages the Tiny C Compiler (TCC) to compile and boot a Linux kernel directly from its source code, achieving this process in under 15 seconds on a 2.4 GHz Pentium 4 processor.[15] The boot loader, measuring just 138 KB in uncompressed size, demonstrates TCC's efficiency by handling the full compilation pipeline without external tools, making it suitable for minimal boot environments like ISO images of around 5.9 MB.[15] GNU Guix incorporates TCC into its full-source bootstrap chain to enable reproducible package building in isolated environments, starting from a minimal 357-byte seed and progressing to a complete GNU/Linux distribution.[16] Specifically, a bootstrappable variant of TCC (version 0.9.26 with patches) is compiled using Mes and MesCC, serving as a self-hosting C compiler before tools like GNU Make and ultimately GCC, with recent enhancements including RISC-V support for broader architectural compatibility.[16][17] A browser-based Linux emulation project utilizes TCC with the v86 JavaScript x86 emulator, where it is cross-compiled into a custom Linux image, allowing in-browser C compilation and execution.[18] TCC's libraries and headers, totaling about 300 KB, are integrated via Buildroot overlays into a musl libc-based root filesystem, enabling commands liketcc -run to compile and test C programs directly within the emulated environment.[18][19]
The Bun JavaScript runtime integrates TCC through its bun:ffi module's cc API, permitting developers to compile and execute C code from JavaScript by specifying source strings and symbol definitions with type mappings for primitives like i32 or f64.[20] This feature, introduced experimentally in Bun 1.1.28 in September 2024, links the compiled C directly to the runtime for efficient foreign function interfacing, including support for N-API types to handle JavaScript objects.[20][21]
TCC has been employed to compile older versions of the GNU Compiler Collection (GCC), as explored in community efforts to build GCC 4.x series using TCC as the host compiler, highlighting its capability for bootstrapping larger toolchains in resource-constrained setups.[22] Additionally, TCC facilitates compiling Windows programs in minimal environments, producing compact executables—such as a 1.5 KB "Hello World" binary—thanks to its support for Windows PE format added in version 0.9.23, ideal for rescue disks or low-disk-space scenarios. TCC continues to be used in reproducible build initiatives, with community updates enhancing multi-architecture compatibility as of 2025.[2][23][4]