Skip to main content

Overview

Leo is a high-level language that compiles to Aleo instructions, the low-level bytecode executed by the Aleo Virtual Machine (AVM). Understanding this compilation process helps you write efficient Leo programs and debug issues.

Compilation Pipeline

The Leo compiler transforms your code through several stages:

Leo to Aleo Mapping

Program Declaration

Leo:
Aleo:
The program name must match the file name and end with .aleo.

Function Definitions

Leo:
Aleo Instructions:

Records

Leo:
Aleo:
Records are always private by default in Aleo.

Mappings

Leo:
Aleo:
Mappings are always public on-chain storage.

Finalizers

Leo:
Aleo:

Aleo Instruction Set

The Aleo VM provides a rich instruction set. Here are the most commonly generated instructions:

Arithmetic Instructions

Comparison Instructions

Logical Instructions

Bitwise Instructions

Type Casting

Compiles to:

Assertions

Compiles to:

Optimization Passes

The Leo compiler performs several optimization passes:

1. Loop Unrolling

Leo unrolls all loops at compile time since circuits cannot have dynamic control flow. Leo:
Unrolled:

2. Constant Folding

Constant expressions are evaluated at compile time. Leo:
Optimized:

3. Dead Code Elimination

Unreachable code is removed. Leo:
Optimized:

4. Static Single Assignment (SSA)

Variables are converted to SSA form for easier analysis. Leo:
SSA Form:

5. Flattening

Nested expressions are flattened into a sequence of simple operations. Leo:
Flattened:

Register Allocation

Aleo uses a register-based architecture. The Leo compiler allocates registers efficiently:
  • r0, r1, r2, …: Function inputs
  • rN: Temporary values
  • Output: Final result register
Example:

Visibility Modifiers

Leo’s public keyword affects Aleo instruction visibility: Leo:
Aleo:

Viewing Generated Code

To see the Aleo instructions generated from your Leo program:
The generated .aleo file will be in the build/ directory:

Debugging Tips

Use leo fmt

Format your code consistently:

Check Generated Instructions

Review the generated .aleo file to understand how your code compiles:

Use Assertions Liberally

Assertions help catch errors early:

Profile Circuit Size

Large circuits take longer to prove. Keep functions small and focused.

Aleo VM Execution

When your Leo program runs:
  1. Synthesis: Aleo instructions are synthesized into a constraint system
  2. Proving: A zero-knowledge proof is generated
  3. Verification: The proof is verified on-chain
  4. Finalization: If present, finalizer functions execute on-chain

Performance Considerations

Circuit Complexity

Each instruction adds constraints to the circuit. Minimize:
  • Nested loops (they unroll completely)
  • Complex conditionals
  • Large data structures

Prefer Simpler Operations

  • Addition/subtraction: ~1 constraint
  • Multiplication: ~1 constraint
  • Division/modulo: More expensive
  • Comparisons: Multiple constraints

Batch Operations

Group related operations in a single function to reduce proof overhead.

Disassembling Aleo Code

Leo includes a disassembler to convert Aleo bytecode back to readable instructions:
This is useful for understanding optimizations and debugging issues.

Aleo Documentation

Learn more about Aleo instructions

Grammar Reference

Leo syntax specification

Code Generation Implementation

The code generation pass is located in crates/passes/src/code_generation/. Key files:
  • program.rs: Generates program-level instructions
  • expression.rs: Handles expression compilation
  • statement.rs: Compiles statements
  • type_.rs: Type conversions
For implementation details, see the Leo source code at /home/daytona/workspace/source/crates/passes/src/code_generation/.