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