PCSX2 Documentation/PCSX2 EE Recompiler

From PCSX2 Wiki
Jump to navigation Jump to search

WORK IN PROGRESS I think it is about time that I start to contribute to this project ;)

It is a summary of my understanding of the EE recompiler. The idea is to collect the key information on the recompiler. It is interesting as a general info and it would be useful to port/improve it one day.

External Reference

Global Overview of the EE recompiler

Others useful documentation

  1. Introduction to Dynamic Recompilation
  2. Recompilers: All 'dems buzzwords?

The 3 recompiler phases

  1. The recompilation phase:
    The purpose is to compile an EE instruction list into an X86 instruction list (also know as an instruction block). Instructions are stored in a buffer called x86Ptr. It can be seen as instruction cache.
  2. The execution phase:
    The x86 instruction block will be executed.
  3. The pause phase:
    The purpose is to emulate the others HW block (VU, GIF, DMA etc..) In particular EE interrupts are handled here.

Internal detail of the EE recompiler

An important part of the recompiler is the management of block. You can see below a nice schematic.

Recompiler block.png

[code] static R5900LutReserve_RAM* recLutReserve_RAM = NULL; [/code] Main BASEBLOCK array buffer. Each BASEBLOCK contains a function pointer to a x86 generated code. (around 36MB is allocated for recLutReserve_RAM , note there is a waste of ~4MB for the ROM)

The buffer is indirectly accessed through a Lookup table called recLUT [code] static __aligned16 uptr recLUT[_64kb]; [/code] Basically the RAM memory is splitted in 32MB zone. It is more complex for ROM, but boot ROM isn't really important to emulate.

You can get current BASEBLOCK of the PC with the macro [code] PC_GETBLOCK(pc) [/code]

There is also an hardware lut. It cheaply converts a virtual address to the physical address (static TLB)

All those blocks are managed by the BaseBlocks class. [code] static BaseBlocks recBlocks; [/code]

_DynGen_* functions generate dispatcher functions and return a function pointer to the function. Full initialization is done in _DynGen_Dispatchers.

  • JITCompile (generated by _DynGen_JITCompile) will

1/ Call recRecompile(cpuRegs.pc) to recompile the current block 2/ Jump to the recompiled block PC_GETBLOCK(cpuRegs.pc)->m_pFnptr()

Basically all BASEBLOCK will contains JITCompile as init address.

  • JITCompileInBlock (generated by _DynGen_JITCompileInBlock)

1/ Jump to JITCompile

Basically after the compilation of BLOCK of size N. First BASEBLOCK will contains the x86 address. The remaining N-1 BLOCK will contain JITCompileInBlock.

  • DispatcherReg (generated by _DynGen_DispatcherReg) will

1/ Jump to the current block (Note. Stack won't be realigned)

  • EnterRecompiledCode (generated by _DynGen_EnterRecompiledCode) will

1/ Setup the base frame pointer 2/ Align the stack pointer 3/ Save edi/esi/ebx on the stack 4/ Simulate a function call? (potentially to help debugger to unwind the stack) 5/ Simulate the stack frame preparation "push ebp, mov ebp, esp" 6/ Save esp, ebp into static variable (for debug check). Code can surely be removed. 7/ Jump to DispatcherReg 8/ Handle the return of DispatcherReg (Leave and restore edi/esi/ebx) 9/ Handle the return of current function (leave and ret)

  • ExitRecompiledCode is the return address of DispatcherReg (end of EnterRecompiledCode)
  • DispatchBlockDiscard (generated by _DynGen_DispatchBlockDiscard) is a wrapper to the C++ function dyna_block_discard
  • DispatchPageReset (generated by _DynGen_DispatchPageReset) is a wrapper to the C++ function dyna_page_reset


The details of the "recompilation" stage Input: the PC (first instruction address) Output: x86 code in a buffer that is ready to be executed.