Skip to main content

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:
Key features:
  • 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:
IR characteristics:
  • 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:
Optimization techniques:
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
Evaluates constant expressions at compile time:
Implemented in CodeGen/Optimizations/ConstantFolding.cs
Merges duplicate code at the end of basic blocks to reduce code size and improve instruction cache efficiency.
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:
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:
Backend features:

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)

Rejit mechanism from 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
PPTC reduces startup stutter significantly but requires disk space (typically 50-200 MB per game)

Dispatch Mechanisms

Managed Dispatch Loop

Unmanaged Dispatch Loop

Benefits:
  • 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:
Features:
  • Single-step execution
  • Precise PC tracking
  • GDB stub integration (see Debugging)
  • Breakpoint support

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 point
  • src/ARMeilleure/Translation/Compiler.cs:12 - Optimization pipeline
  • src/ARMeilleure/Decoders/Decoder.cs:11 - Instruction decoder
  • src/ARMeilleure/IntermediateRepresentation/Operation.cs:7 - IR operation structure
  • src/ARMeilleure/CodeGen/X86/CodeGenerator.cs:17 - x86-64 code generation
  • src/ARMeilleure/CodeGen/Arm64/CodeGenerator.cs - ARM64 code generation