6.3 KiB
Xenia Xbox 360 Emulator: JIT vs Interpreter Architecture Analysis
Analysis of Xenia's architecture comparing JIT vs interpreter approaches for Rust porting. Key findings: JIT pipeline [1a-1d] uses Xbyak with no Rust equivalent, MMIO relies on hardware exceptions [2a-2d], while interpreter approach uses explicit MMIO checking [3a-3d]. The PPC context [4a-4d] and opcode infrastructure [5a-5d] support both approaches. Memory system [6a-6c] requires unsafe Rust regardless. Kernel HLE [7a-7c] and GPU shader translation [8a-8c] present additional challenges.
1. JIT Code Generation Pipeline
The current PPC-to-x64 JIT compilation pipeline using HIR and Xbyak
1a. JIT Pipeline Initialization (ppc_translator.cc:44)
Sets up scanner, HIR builder, compiler, and Xbyak assembler
PPCTranslator::PPCTranslator(PPCFrontend* frontend) : frontend_(frontend) {
1b. HIR Optimization Passes (ppc_translator.cc:57)
Adds multiple optimization passes to the compiler pipeline
compiler_->AddPass(std::make_unique<passes::ControlFlowAnalysisPass>());
1c. Xbyak-based Code Emitter (x64_emitter.h:208)
X64Emitter inherits from Xbyak for runtime x64 code generation
class X64Emitter : public Xbyak::CodeGenerator {
1d. Host-to-Guest Thunk (x64_backend.cc:656)
Raw x64 assembly emitted for host↔guest ABI transitions
mov(rdi, ptr[rsi + offsetof(ppc::PPCContext, virtual_membase)]); // membase
2. MMIO Exception Handling
Hardware exception-based MMIO interception used by the JIT
2a. Exception Handler Entry (mmio_handler.cc:402)
Catches access violations to handle MMIO operations
bool MMIOHandler::ExceptionCallback(Exception* ex) {
2b. Filter Access Violations (mmio_handler.cc:403)
Only processes memory access violations
if (ex->code() != Exception::Code::kAccessViolation) {
2c. Address Translation (mmio_handler.cc:427)
Translates host fault address back to guest virtual address
fault_guest_virtual_address = host_to_guest_virtual_(
2d. Instruction Decoding (mmio_handler.cc:449)
Decodes the faulting x64 instruction to determine the operation
if (!TryDecodeLoadStore(p, decoded_load_store)) {
3. Interpreter Alternative Approach
Explicit MMIO checking pattern that enables interpreter implementation
3a. Explicit MMIO Check Function (x64_seq_memory.cc:1216)
Pattern for checking MMIO ranges without exceptions
static T MMIOAwareLoad(void* _ctx, unsigned int guestaddr) {
3b. Range Lookup (x64_seq_memory.cc:1225)
Explicitly checks if address is in mapped MMIO range
auto gaddr = ctx->processor->memory()->LookupVirtualMappedRange(guestaddr);
3c. MMIO Callback Invocation (x64_seq_memory.cc:1236)
Calls MMIO read callback instead of accessing memory directly
value = gaddr->read(nullptr, gaddr->callback_context, guestaddr);
3d. MMIO Handler Interface (mmio_handler.h:73)
Public API for explicit MMIO checking in interpreter mode
bool CheckLoad(uint32_t virtual_address, uint32_t* out_value);
4. PPC Context and State Management
Register file and thread state structures that would need Rust porting
4a. PPC Register File (ppc_context.h:378)
32 GPRs, CTR, LR, MSR in the context structure
uint64_t r[32]; // 0x20 General purpose registers
4b. Floating-Point and Vector Registers (ppc_context.h:384)
32 FPRs and 128 VMX128 vector registers
double f[32]; // 0x120 Floating-point registers
4c. Thread State Context (thread_state.h:49)
Each guest thread owns a PPCContext pointer
ppc::PPCContext* context_;
4d. Big-Endian Wrapper (byte_order.h:134)
Type that handles big-endian conversion for guest structures
template <typename T>
using be = endian_store<T, std::endian::big>;
5. Opcode Dispatch Infrastructure
PPC opcode enumeration and lookup that enables interpreter implementation
5a. PPC Opcode Enumeration (ppc_opcode.h:14)
Complete enum of all PPC opcodes including VMX128 extensions
enum class PPCOpcode : uint32_t {
5b. Opcode Dispatch Table (ppc_opcode_lookup_gen.cc:262)
Fast lookup table for opcode decoding
case 0b000101: PPC_DECODER_HIT(vrlw128);
5c. VMX128 Instruction Encoding (ppc_emit_altivec.cc:37)
Macros for decoding Xbox 360-specific VMX128 instructions
#define VX128(op, xop) (OP(op) | (((uint32_t)(xop)) & 0x3d0))
5d. Basic Block Discovery (ppc_scanner.h:36)
Finds basic block boundaries for potential interpreter caching
std::vector<BlockInfo> FindBlocks(GuestFunction* function);
6. Memory System Architecture
4GB virtual address space management required for both approaches
6a. Virtual Memory Allocation (memory.cc:142)
Creates 4GB+ file-backed mapping for guest address space
mapping_ = xe::memory::CreateFileMappingHandle(
6b. Virtual Memory Base (memory.cc:167)
Sets up virtual and physical memory base addresses
virtual_membase_ = mapping_base_;
6c. Guest Address Translation (ppc_context.h:433)
Fast inline function for translating guest to host addresses
inline T TranslateVirtual(uint32_t guest_address) const {
7. Kernel HLE System
Massive kernel export table that would need Rust porting
7a. Kernel Export Table (xboxkrnl_table.inc:15)
96KB+ table of all xboxkrnl exports
XE_EXPORT(xboxkrnl, 0x00000001, DbgBreakPoint, kFunction),
7b. Guest Thread Structure (xthread.h:256)
Packed big-endian X_KTHREAD structure living in guest memory
xe::be<uint32_t> stack_base; // 0x5C
7c. Kernel State Management (emulator.h:349)
Central kernel object tracking all guest kernel state
std::unique_ptr<kernel::KernelState> kernel_state_;
8. GPU Shader Translation
Complex shader translation pipeline that poses challenges for Rust porting
8a. SPIR-V Builder Initialization (spirv_shader_translator.cc:155)
Uses glslang C++ API for SPIR-V generation
builder_ = std::make_unique<SpirvBuilder>(
8b. Shader Interpreter (shader_interpreter.h:51)
Existing interpreter for simple shaders without texture fetches
static bool CanInterpretShader(const Shader& shader) {
8c. C++ Dependency (premake5.lua:31)
glslang dependency with no direct Rust equivalent
"glslang-spirv",