Overview
ARMeilleure is Ryujinx’s custom-built JIT (Just-In-Time) compiler for ARM CPU emulation. It translates ARM64 (and ARM32) guest code into optimized native x86-64 or ARM64 host code at runtime, providing high-performance CPU emulation.ARMeilleure uses a multi-stage translation pipeline: Decode → IR Translation → Optimization → Register Allocation → Code Generation
Translation Pipeline
Stage 1: Decoding
The decoder (src/ARMeilleure/Decoders/Decoder.cs) performs recursive guest code analysis:
- Basic block construction: Follows control flow (branches, calls, returns)
- Function size limits: 2500 instructions (high-CQ) or 500 (low-CQ) to prevent excessive compilation time
- Multi-block analysis: Handles complex control flow graphs
- Lazy decoding: Only decodes when execution reaches new code regions
Stage 2: IR Translation
Guest instructions are lifted into ARMeilleure’s intermediate representation:- SSA form support: Static Single Assignment for optimization passes
- Intrusive linked list: Efficient operation manipulation without allocations
- Typed operands: I32, I64, FP32, FP64, V128 (SIMD vector)
- Intrinsics: Hardware-accelerated operations (SIMD, crypto, etc.)
Stage 3: Optimization Passes
The compiler (src/ARMeilleure/Translation/Compiler.cs) applies optimization passes:
SSA Construction
SSA Construction
Constructs Static Single Assignment form for advanced optimizations:
- Phi node insertion at control flow merge points
- Def-use chain tracking
- Enables constant propagation and dead code elimination
Constant Folding
Constant Folding
Evaluates constant expressions at compile time:Implemented in
CodeGen/Optimizations/ConstantFolding.csTail Merge
Tail Merge
Merges duplicate code at the end of basic blocks to reduce code size and improve instruction cache efficiency.
Block Placement
Block Placement
Reorders basic blocks for:
- Better branch prediction (hot paths fall through)
- Improved instruction cache locality
- Reduced branch penalties
Stage 4: Register Allocation
Two allocation strategies based on compilation tier:- Linear Scan (Low-CQ)
- Hybrid (High-CQ)
Fast allocation for initial compilation:Characteristics:
- O(n) complexity
- Minimal compilation overhead
- Used for first-time execution
Stage 5: Code Generation
Native machine code generation for host architecture:x86-64 Backend
- SSE/AVX/AVX-512 SIMD support
- Hardware AES/SHA acceleration
- Optimized calling conventions (System V / Windows x64)
- Efficient stack frame management
ARM64 Backend
- Native ARM64 code on Apple Silicon / Linux ARM
- NEON SIMD instructions
- ARM crypto extensions
- Zero-overhead for ARM→ARM translation
Two-Tier Compilation
ARMeilleure uses adaptive compilation to balance startup time and performance:Low-CQ (Low Code Quality)
High-CQ (High Code Quality)
src/ARMeilleure/Translation/Translator.cs:479:
Hardware Capabilities Detection
ARMeilleure detects and utilizes host CPU features:Performance impact: Using AVX-512 can provide 2-4x speedup for vector operations compared to SSE2
Function Cache Management
Translation Cache
JIT Cache Invalidation
When guest code is modified (self-modifying code, JIT compilers):PPTC (Profiled Persistent Translation Cache)
ARMeilleure can save and load compiled code across sessions:1
Profile Collection
During initial gameplay, track which functions are executed frequently and compile them to high-CQ
2
Cache Generation
Serialize compiled functions to disk with:
- Function address and hash
- IR representation
- Compilation metadata
3
Cache Loading
On subsequent launches:
4
Validation
Verify cached functions against current guest code using hash comparison
Dispatch Mechanisms
Managed Dispatch Loop
Unmanaged Dispatch Loop
- Eliminates managed/native transitions
- Direct function table lookups
- Lower overhead for function calls
- 5-15% performance improvement
Performance Characteristics
Startup
Low-CQ compilation:
- ~0.1-0.5ms per function
- Minimal stuttering
- Gradual warmup
Runtime
Execution speed:
- 70-90% of native ARM hardware (x86 host)
- 95-100% of native (ARM host)
- High-CQ provides 20-40% speedup over low-CQ
Memory
Cache usage:
- ~2-10 KB per compiled function
- Function table: 8 bytes per 4KB page
- Total: 50-500 MB per game
Debugging Support
ARMeilleure includes integrated debugging capabilities:- Single-step execution
- Precise PC tracking
- GDB stub integration (see Debugging)
- Breakpoint support
Related Topics
Memory Management
How ARMeilleure interfaces with guest memory
HLE Services
How translated code calls into HLE services
Graphics Integration
GPU command submission from translated code
Performance Tuning
Optimization settings for ARMeilleure
Source Code Reference
Key files to explore:src/ARMeilleure/Translation/Translator.cs:22- Main translator entry pointsrc/ARMeilleure/Translation/Compiler.cs:12- Optimization pipelinesrc/ARMeilleure/Decoders/Decoder.cs:11- Instruction decodersrc/ARMeilleure/IntermediateRepresentation/Operation.cs:7- IR operation structuresrc/ARMeilleure/CodeGen/X86/CodeGenerator.cs:17- x86-64 code generationsrc/ARMeilleure/CodeGen/Arm64/CodeGenerator.cs- ARM64 code generation