The Leo compiler applies approximately 25 sequential transformation passes to convert the Abstract Syntax Tree (AST) into Aleo bytecode. Each pass maintains specific invariants and prepares the AST for subsequent transformations.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/provablehq/leo/llms.txt
Use this file to discover all available pages before exploring further.
All passes implement the
Pass trait with a do_pass(input, state) -> Result<Output> method. They are executed sequentially through CompilerState in leo-compiler/src/compiler.rs:186-247.Pass Overview
The complete pass sequence fromleo-compiler/src/compiler.rs:
Analysis and Validation Passes
1. NameValidation
Purpose: Validates that all identifiers follow Leo naming rules and checks for reserved keywords. Checks:- Function names, variable names, type names are valid identifiers
- No use of reserved keywords as identifiers
- No shadowing of built-in types
2. GlobalVarsCollection
Purpose: Collects all global constants and mappings into the symbol table. What It Does:- Traverses top-level declarations
- Populates
CompilerState.symbol_tablewith global definitions - Validates global variable uniqueness
3. PathResolution
Purpose: Resolves import paths and external program references. What It Does:- Maps import statements to program stubs
- Resolves qualified identifiers (e.g.,
token.aleo/Token) - Validates that imported programs exist
4. GlobalItemsCollection
Purpose: Collects all functions, structs, and records into the symbol table. Symbol Table Structure:5. CheckInterfaces
Purpose: Validates that program interfaces match their declarations. Checks:- Record types have correct fields and visibilities
- Mapping types are valid
- Function signatures match between declaration and implementation
6. TypeChecking
Location:leo-passes/src/type_checking/mod.rs:76-126
Purpose: Comprehensive type checking and semantic validation.
What It Does:
- Infers types for all expressions
- Validates type compatibility in operations
- Builds the composite graph (struct/record dependencies)
- Builds the call graph (function call relationships)
- Enforces network limits (array sizes, function counts, etc.)
leo-passes/src/type_checking/mod.rs:36-74):
- Inline function selection
- Dead code elimination
- Cycle detection (Leo prohibits recursive functions)
7. Disambiguate
Purpose: Resolves ambiguous references to ensure every identifier has a unique binding.8. ProcessingAsync
Purpose: Validates async/await usage and transforms async functions. Checks:- Async functions only used in appropriate contexts
- Await expressions only in async blocks
- Finalize blocks have correct structure
9. StaticAnalyzing
Purpose: Performs static analysis to detect potential issues. Checks:- Unreachable code
- Unused variables (warnings)
- Infinite loops
- Division by zero in constant expressions
Optimization Passes
10. ConstPropUnrollAndMorphing
Location:leo-passes/src/const_prop_unroll_and_morphing.rs:34-46
Purpose: Iteratively applies const propagation, loop unrolling, and monomorphization until a fixed point is reached.
Structure:
This meta-pass runs up to 1024 iterations to handle interdependent transformations. Most programs reach a fixed point in 2-3 iterations.
Constant Propagation
Purpose: Evaluates constant expressions at compile time. What It Does:- Folds constant arithmetic (
2u32 + 3u32→5u32) - Evaluates constant conditionals
- Resolves const generic parameters
Loop Unrolling
Location:leo-passes/src/loop_unrolling/mod.rs:39-56
Purpose: Unrolls loops with statically known bounds.
What It Does:
- Detects loops with constant iteration counts
- Replicates loop body for each iteration
- Renames variables to maintain SSA form
Monomorphization
Location:leo-passes/src/monomorphization/mod.rs:17-106
Purpose: Instantiates const generic functions and types with concrete values.
Example:
leo-passes/src/monomorphization/mod.rs:58-69):
ABI Generation
Location:leo-compiler/src/compiler.rs:251-276
Purpose: Generates Application Binary Interface definitions after monomorphization.
Timing: ABIs are captured immediately after monomorphization to ensure all const generic types are resolved to concrete types.
11. StorageLowering
Purpose: Transforms high-level storage operations (mappings) into low-level primitives.12. OptionLowering
Purpose: TransformsOption<T> types into explicit tagged unions for circuit representation.
Static Single Assignment (SSA) Passes
13. SsaForming (Multiple Applications)
Location:leo-passes/src/static_single_assignment/mod.rs:17-94
Purpose: Converts the AST into Static Single Assignment form.
What It Does:
- Each variable is assigned exactly once
- Introduces phi nodes for control flow merges
- Simplifies complex expressions into sequences of assignments
leo-passes/src/static_single_assignment/mod.rs:22-50):
- First pass (
rename_defs: true): Initial SSA construction - After Destructuring (
rename_defs: false): Restore SSA after tuple unpacking - After WriteTransforming (
rename_defs: false): Restore SSA after write operations - After Flattening (
rename_defs: false): Restore SSA after control flow flattening - After SsaConstPropagation (
rename_defs: false): Final SSA restoration
SSA form simplifies optimization passes by ensuring each variable has a single definition point. This enables more aggressive optimizations like constant propagation and dead code elimination.
14. Destructuring
Purpose: Expands tuple destructuring into individual assignments. Example:15. WriteTransforming
Purpose: Transforms write operations to maintain SSA invariants.Control Flow Transformation
16. Flattening
Location:leo-passes/src/flattening/mod.rs:17-89
Purpose: Converts control flow into straight-line code using ternary expressions.
What It Does:
- Eliminates
ifstatements by executing both branches - Uses ternary operators to select between branch results
- Consolidates multiple return statements into a single return
- Does not transform async functions (they maintain control flow)
leo-passes/src/flattening/mod.rs:23-52):
Flattening is essential for ZK circuits, which cannot represent dynamic control flow. All paths are executed, and ternary operators select the correct result.
17. FunctionInlining
Purpose: Inlines function calls marked withinline keyword.
What It Does:
- Replaces
inlinefunction calls with the function body - Renames variables to avoid conflicts
- Eliminates function call overhead
- Function explicitly marked
inline - Function not called too many times (to avoid code bloat)
- Function not recursive
18. SsaConstPropagation
Purpose: Performs constant propagation specifically on SSA form. What It Does:- Leverages SSA’s single-assignment property
- Propagates constant values through ternary expressions
- More aggressive than regular constant propagation
Final Optimization Passes
19. CommonSubexpressionEliminating
Purpose: Eliminates redundant computations. What It Does:- Identifies expressions computed multiple times
- Reuses previously computed values
- Introduces temporary variables for common subexpressions
20. DeadCodeEliminating
Location:leo-passes/src/dead_code_elimination/mod.rs:17-94
Purpose: Removes unused code and variables.
What It Does:
- Identifies unused variable assignments
- Removes assignments that don’t contribute to outputs
- Tracks statement count before and after elimination
leo-passes/src/dead_code_elimination/mod.rs:23-46):
- No shadowing (enforced by earlier passes)
- Unique variable names (provided by SSA)
- Flattened code (provided by flattening pass)
Code Generation Pass
21. CodeGenerating
Location:leo-passes/src/code_generation/mod.rs:40-66
Purpose: Generates Aleo bytecode from the fully transformed AST.
Structure:
leo-passes/src/code_generation/mod.rs:69-92):
Aleo Instruction Set
The code generator emits the following instruction types (leo-passes/src/code_generation/mod.rs:371-431):
Arithmetic: add, sub, mul, div, rem, pow, abs, neg
Wrapped Arithmetic: add.w, sub.w, mul.w, div.w (modular arithmetic)
Comparison: is.eq, is.neq, gt, gte, lt, lte
Logical: and, or, not, xor, nand, nor
Bitwise: shl, shr, shl.w, shr.w
Control Flow: ternary, branch.eq, position
Cryptographic: commit, hash, sign.verify, ecdsa.verify
Mapping Operations: get, get.or_use, set, remove, contains
Special: cast, call, async, await, rand.chacha
Every Leo expression maps to one or more Aleo instructions. The code generator maintains a register allocator to track temporary values.
Pass Invariants
Each pass maintains and depends on specific invariants:| Pass | Establishes | Requires |
|---|---|---|
| NameValidation | Valid identifiers | AST parsed |
| TypeChecking | Type annotations, composite graph, call graph | Symbol table populated |
| SSA | Single assignment | Type-checked AST |
| Flattening | No control flow | SSA form |
| FunctionInlining | No inline functions | Flattened code |
| DeadCodeEliminating | No dead assignments | SSA form, flattened code |
| CodeGenerating | Aleo bytecode | All transformations complete |
Debugging Passes
Enable AST Snapshots
To debug a specific pass, enable AST snapshots:program_name.initial.jsonprogram_name.TypeChecking.jsonprogram_name.Flattening.json
Pass Testing
Test passes in isolation:Related Resources
- Compiler Architecture - Overall compiler design
- Zero-Knowledge Concepts - How passes enable ZK compilation
- Optimization - Performance tuning techniques