The incremental compilation system tracks dependencies between different parts of the compilation process and only recompiles what’s necessary when source code changes.
How Incremental Compilation Works
Incremental compilation is built on the compiler’s query system:Query System
The compiler is structured as a collection of queries (e.g., “what is the type of this function?”, “what is the MIR for this function?”).
Dependency Tracking
As queries execute, the system tracks which queries depend on which other queries.
The Dependency Graph
Therustc_incremental crate manages the dependency graph:
Dependency Graph Structure
The dependency graph tracks relationships between queries:Query System
The query system is the foundation of incremental compilation:Every computation in the compiler that should be cached is expressed as a query. Queries are pure functions of their inputs and the type context.
Query Definition
Queries are defined inrustc_middle:
Query Execution
When a query is executed:Check Cache
Check Cache
First, check if a valid cached result exists from a previous compilation.
Track Dependencies
Track Dependencies
If recomputing, track all other queries that are invoked during execution.
Compute Result
Compute Result
Execute the query function to compute the result.
Cache Result
Cache Result
Store the result and its dependencies for future compilations.
Fingerprinting
The compiler uses fingerprints (hashes) to detect changes:Fingerprints allow the compiler to quickly determine if a query’s inputs or results have changed without comparing the full data structures.
Incremental Directory
Incremental compilation data is stored in the target directory:Stored Data
Dependency Graph
Complete graph of query dependencies from the previous compilation
Query Results
Cached results for queries that can be reused
Work Products
Compiled artifacts like object files that can be reused
Source Hashes
Fingerprints of source files to detect changes
Session Directory Management
The compiler manages incremental compilation sessions:Granularity
Incremental compilation works at different levels of granularity:Function-Level
Item-Level
Module-Level
Red-Green Algorithm
The incremental compilation system uses a “red-green” algorithm:Mark Changed Nodes Red
When source code changes, mark the corresponding dependency graph nodes as “red” (changed).
Verify Green
For each potentially changed node, check if its inputs actually changed. If not, mark it “green” (unchanged).
This algorithm minimizes recompilation by distinguishing between “changed” and “affected by changes” nodes.
Optimization Strategies
The incremental compilation system includes several optimizations:Work Product Reuse
Query Result Packing
Query results are efficiently packed and compressed for storage:Parallel Loading
The dependency graph and query cache are loaded in parallel:Limitations and Edge Cases
Incremental compilation has some limitations:Proc Macros
Proc Macros
Procedural macros can break incremental compilation if they have side effects or depend on external state.
Build Scripts
Build Scripts
Changes to build.rs files require full recompilation of the affected crate.
Compiler Flags
Compiler Flags
Changing compiler flags or features requires full recompilation.
External Dependencies
External Dependencies
Changes to dependencies require recompiling the affected parts of the crate.
Using Incremental Compilation
Incremental compilation is enabled by default for debug builds:Configuration
Performance Impact
Incremental compilation dramatically improves rebuild times:Small Changes
90-99% faster rebuilds when changing a single function
Medium Changes
50-80% faster when changing multiple related items
Large Changes
Still provides benefits even with widespread changes
Cold Build
Slight overhead (5-10%) on initial build due to tracking
Debugging Incremental Compilation
Several tools help debug incremental compilation:Future Improvements
The incremental compilation system continues to evolve:- Better handling of proc macros
- Cross-crate incremental compilation
- Improved cache compression
- Faster dependency graph operations
- More granular invalidation
Best Practices
Structure Code Well
Well-structured code with clear module boundaries benefits more from incremental compilation.
Use Cargo Workspaces
Split large projects into workspace members for better incremental compilation.
Next Steps
Compiler Overview
Return to the compiler architecture overview
MIR
Learn about the intermediate representation