duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

cpu_newrec_compiler.h (20317B)


      1 // SPDX-FileCopyrightText: 2023 Connor McLaughlin <stenzek@gmail.com>
      2 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
      3 
      4 #pragma once
      5 #include "cpu_code_cache_private.h"
      6 #include "cpu_recompiler_types.h"
      7 #include "cpu_types.h"
      8 #include <array>
      9 #include <bitset>
     10 #include <optional>
     11 #include <utility>
     12 #include <vector>
     13 
     14 namespace CPU::NewRec {
     15 
     16 // Global options
     17 static constexpr bool EMULATE_LOAD_DELAYS = true;
     18 static constexpr bool SWAP_BRANCH_DELAY_SLOTS = true;
     19 
     20 // Arch-specific options
     21 #if defined(CPU_ARCH_X64)
     22 static constexpr u32 NUM_HOST_REGS = 16;
     23 static constexpr bool HAS_MEMORY_OPERANDS = true;
     24 #elif defined(CPU_ARCH_ARM32)
     25 static constexpr u32 NUM_HOST_REGS = 16;
     26 static constexpr bool HAS_MEMORY_OPERANDS = false;
     27 #elif defined(CPU_ARCH_ARM64)
     28 static constexpr u32 NUM_HOST_REGS = 32;
     29 static constexpr bool HAS_MEMORY_OPERANDS = false;
     30 #elif defined(CPU_ARCH_RISCV64)
     31 static constexpr u32 NUM_HOST_REGS = 32;
     32 static constexpr bool HAS_MEMORY_OPERANDS = false;
     33 #endif
     34 
     35 // TODO: Get rid of the virtuals... somehow.
     36 class Compiler
     37 {
     38 public:
     39   Compiler();
     40   virtual ~Compiler();
     41 
     42   const void* CompileBlock(CodeCache::Block* block, u32* host_code_size, u32* host_far_code_size);
     43 
     44 protected:
     45   enum FlushFlags : u32
     46   {
     47     FLUSH_FLUSH_MIPS_REGISTERS = (1 << 0),
     48     FLUSH_INVALIDATE_MIPS_REGISTERS = (1 << 1),
     49     FLUSH_FREE_CALLER_SAVED_REGISTERS = (1 << 2),
     50     FLUSH_FREE_UNNEEDED_CALLER_SAVED_REGISTERS = (1 << 3),
     51     FLUSH_FREE_ALL_REGISTERS = (1 << 4),
     52     FLUSH_PC = (1 << 5),
     53     FLUSH_INSTRUCTION_BITS = (1 << 6),
     54     FLUSH_CYCLES = (1 << 7),
     55     FLUSH_LOAD_DELAY = (1 << 8),
     56     FLUSH_LOAD_DELAY_FROM_STATE = (1 << 9),
     57     FLUSH_GTE_DONE_CYCLE = (1 << 10),
     58     FLUSH_GTE_STALL_FROM_STATE = (1 << 11),
     59     FLUSH_INVALIDATE_SPECULATIVE_CONSTANTS = (1 << 12),
     60 
     61     FLUSH_FOR_C_CALL = (FLUSH_FREE_CALLER_SAVED_REGISTERS),
     62     FLUSH_FOR_LOADSTORE = (FLUSH_FREE_CALLER_SAVED_REGISTERS | FLUSH_CYCLES),
     63     FLUSH_FOR_BRANCH = (FLUSH_FLUSH_MIPS_REGISTERS),
     64     FLUSH_FOR_EXCEPTION =
     65       (FLUSH_CYCLES | FLUSH_GTE_DONE_CYCLE), // GTE cycles needed because it stalls when a GTE instruction is next.
     66     FLUSH_FOR_INTERPRETER = (FLUSH_FLUSH_MIPS_REGISTERS | FLUSH_INVALIDATE_MIPS_REGISTERS |
     67                              FLUSH_FREE_CALLER_SAVED_REGISTERS | FLUSH_PC | FLUSH_CYCLES | FLUSH_INSTRUCTION_BITS |
     68                              FLUSH_LOAD_DELAY | FLUSH_GTE_DONE_CYCLE | FLUSH_INVALIDATE_SPECULATIVE_CONSTANTS),
     69     FLUSH_END_BLOCK = 0xFFFFFFFFu & ~(FLUSH_PC | FLUSH_CYCLES | FLUSH_GTE_DONE_CYCLE | FLUSH_INSTRUCTION_BITS |
     70                                       FLUSH_GTE_STALL_FROM_STATE | FLUSH_INVALIDATE_SPECULATIVE_CONSTANTS),
     71   };
     72 
     73   union CompileFlags
     74   {
     75     struct
     76     {
     77       u32 const_s : 1;  // S is constant
     78       u32 const_t : 1;  // T is constant
     79       u32 const_lo : 1; // LO is constant
     80       u32 const_hi : 1; // HI is constant
     81 
     82       u32 valid_host_d : 1;  // D is valid in host register
     83       u32 valid_host_s : 1;  // S is valid in host register
     84       u32 valid_host_t : 1;  // T is valid in host register
     85       u32 valid_host_lo : 1; // LO is valid in host register
     86       u32 valid_host_hi : 1; // HI is valid in host register
     87 
     88       u32 host_d : 5;  // D host register
     89       u32 host_s : 5;  // S host register
     90       u32 host_t : 5;  // T host register
     91       u32 host_lo : 5; // LO host register
     92 
     93       u32 delay_slot_swapped : 1;
     94       u32 pad1 : 2; // 28..31
     95 
     96       u32 host_hi : 5; // HI host register
     97 
     98       u32 mips_s : 5; // S guest register
     99       u32 mips_t : 5; // T guest register
    100 
    101       u32 pad2 : 15; // 32 bits
    102     };
    103 
    104     u64 bits;
    105 
    106     ALWAYS_INLINE Reg MipsS() const { return static_cast<Reg>(mips_s); }
    107     ALWAYS_INLINE Reg MipsT() const { return static_cast<Reg>(mips_t); }
    108   };
    109   static_assert(sizeof(CompileFlags) == sizeof(u64));
    110 
    111   enum TemplateFlag : u32
    112   {
    113     TF_READS_S = (1 << 0),
    114     TF_READS_T = (1 << 1),
    115     TF_READS_LO = (1 << 2),
    116     TF_READS_HI = (1 << 3),
    117     TF_WRITES_D = (1 << 4),
    118     TF_WRITES_T = (1 << 5),
    119     TF_WRITES_LO = (1 << 6),
    120     TF_WRITES_HI = (1 << 7),
    121     TF_COMMUTATIVE = (1 << 8), // S op T == T op S
    122     TF_CAN_OVERFLOW = (1 << 9),
    123 
    124     // TF_NORENAME = // TODO
    125     TF_LOAD_DELAY = (1 << 10),
    126     TF_GTE_STALL = (1 << 11),
    127 
    128     TF_NO_NOP = (1 << 12),
    129     TF_NEEDS_REG_S = (1 << 13),
    130     TF_NEEDS_REG_T = (1 << 14),
    131     TF_CAN_SWAP_DELAY_SLOT = (1 << 15),
    132 
    133     TF_RENAME_WITH_ZERO_T = (1 << 16), // add commutative for S as well
    134     TF_RENAME_WITH_ZERO_IMM = (1 << 17),
    135 
    136     TF_PGXP_WITHOUT_CPU = (1 << 18),
    137   };
    138 
    139   enum HostRegFlags : u8
    140   {
    141     HR_ALLOCATED = (1 << 0),
    142     HR_NEEDED = (1 << 1),
    143     HR_MODE_READ = (1 << 2),  // valid
    144     HR_MODE_WRITE = (1 << 3), // dirty
    145 
    146     HR_USABLE = (1 << 7),
    147     HR_CALLEE_SAVED = (1 << 6),
    148 
    149     ALLOWED_HR_FLAGS = HR_MODE_READ | HR_MODE_WRITE,
    150     IMMUTABLE_HR_FLAGS = HR_USABLE | HR_CALLEE_SAVED,
    151   };
    152 
    153   enum HostRegAllocType : u8
    154   {
    155     HR_TYPE_TEMP,
    156     HR_TYPE_CPU_REG,
    157     HR_TYPE_PC_WRITEBACK,
    158     HR_TYPE_LOAD_DELAY_VALUE,
    159     HR_TYPE_NEXT_LOAD_DELAY_VALUE,
    160     HR_TYPE_MEMBASE,
    161   };
    162 
    163   struct HostRegAlloc
    164   {
    165     u8 flags;
    166     HostRegAllocType type;
    167     Reg reg;
    168     u16 counter;
    169   };
    170 
    171   enum class BranchCondition : u8
    172   {
    173     Equal,
    174     NotEqual,
    175     GreaterThanZero,
    176     GreaterEqualZero,
    177     LessThanZero,
    178     LessEqualZero,
    179   };
    180 
    181   ALWAYS_INLINE bool HasConstantReg(Reg r) const { return m_constant_regs_valid.test(static_cast<u32>(r)); }
    182   ALWAYS_INLINE bool HasDirtyConstantReg(Reg r) const { return m_constant_regs_dirty.test(static_cast<u32>(r)); }
    183   ALWAYS_INLINE bool HasConstantRegValue(Reg r, u32 val) const
    184   {
    185     return m_constant_regs_valid.test(static_cast<u32>(r)) && m_constant_reg_values[static_cast<u32>(r)] == val;
    186   }
    187   ALWAYS_INLINE u32 GetConstantRegU32(Reg r) const { return m_constant_reg_values[static_cast<u32>(r)]; }
    188   ALWAYS_INLINE s32 GetConstantRegS32(Reg r) const
    189   {
    190     return static_cast<s32>(m_constant_reg_values[static_cast<u32>(r)]);
    191   }
    192   void SetConstantReg(Reg r, u32 v);
    193   void ClearConstantReg(Reg r);
    194   void FlushConstantReg(Reg r);
    195   void FlushConstantRegs(bool invalidate);
    196 
    197   Reg MipsD() const;
    198   u32 GetConditionalBranchTarget(CompileFlags cf) const;
    199   u32 GetBranchReturnAddress(CompileFlags cf) const;
    200   bool TrySwapDelaySlot(Reg rs = Reg::zero, Reg rt = Reg::zero, Reg rd = Reg::zero);
    201   void SetCompilerPC(u32 newpc);
    202   void TruncateBlock();
    203 
    204   const TickCount* GetFetchMemoryAccessTimePtr() const;
    205 
    206   virtual const void* GetCurrentCodePointer() = 0;
    207 
    208   virtual void Reset(CodeCache::Block* block, u8* code_buffer, u32 code_buffer_space, u8* far_code_buffer,
    209                      u32 far_code_space);
    210   virtual void BeginBlock();
    211   virtual void GenerateBlockProtectCheck(const u8* ram_ptr, const u8* shadow_ptr, u32 size) = 0;
    212   virtual void GenerateICacheCheckAndUpdate() = 0;
    213   virtual void GenerateCall(const void* func, s32 arg1reg = -1, s32 arg2reg = -1, s32 arg3reg = -1) = 0;
    214   virtual void EndBlock(const std::optional<u32>& newpc, bool do_event_test) = 0;
    215   virtual void EndBlockWithException(Exception excode) = 0;
    216   virtual const void* EndCompile(u32* code_size, u32* far_code_size) = 0;
    217 
    218   ALWAYS_INLINE bool IsHostRegAllocated(u32 r) const { return (m_host_regs[r].flags & HR_ALLOCATED) != 0; }
    219   static const char* GetReadWriteModeString(u32 flags);
    220   virtual const char* GetHostRegName(u32 reg) const = 0;
    221   u32 GetFreeHostReg(u32 flags);
    222   u32 AllocateHostReg(u32 flags, HostRegAllocType type = HR_TYPE_TEMP, Reg reg = Reg::count);
    223   std::optional<u32> CheckHostReg(u32 flags, HostRegAllocType type = HR_TYPE_TEMP, Reg reg = Reg::count);
    224   u32 AllocateTempHostReg(u32 flags = 0);
    225   void SwapHostRegAlloc(u32 lhs, u32 rhs);
    226   void FlushHostReg(u32 reg);
    227   void FreeHostReg(u32 reg);
    228   void ClearHostReg(u32 reg);
    229   void MarkRegsNeeded(HostRegAllocType type, Reg reg);
    230   void RenameHostReg(u32 reg, u32 new_flags, HostRegAllocType new_type, Reg new_reg);
    231   void ClearHostRegNeeded(u32 reg);
    232   void ClearHostRegsNeeded();
    233   void DeleteMIPSReg(Reg reg, bool flush);
    234   bool TryRenameMIPSReg(Reg to, Reg from, u32 fromhost, Reg other);
    235   void UpdateHostRegCounters();
    236 
    237   virtual void LoadHostRegWithConstant(u32 reg, u32 val) = 0;
    238   virtual void LoadHostRegFromCPUPointer(u32 reg, const void* ptr) = 0;
    239   virtual void StoreConstantToCPUPointer(u32 val, const void* ptr) = 0;
    240   virtual void StoreHostRegToCPUPointer(u32 reg, const void* ptr) = 0;
    241   virtual void CopyHostReg(u32 dst, u32 src) = 0;
    242   virtual void Flush(u32 flags);
    243 
    244   /// Returns true if there is a load delay which will be stored at the end of the instruction.
    245   bool HasLoadDelay() const { return m_load_delay_register != Reg::count; }
    246 
    247   /// Cancels any pending load delay to the specified register.
    248   void CancelLoadDelaysToReg(Reg reg);
    249 
    250   /// Moves load delay to the next load delay, and writes any previous load delay to the destination register.
    251   void UpdateLoadDelay();
    252 
    253   /// Flushes the load delay, i.e. writes it to the destination register.
    254   void FinishLoadDelay();
    255 
    256   /// Flushes the load delay, but only if it matches the specified register.
    257   void FinishLoadDelayToReg(Reg reg);
    258 
    259   /// Uses a caller-saved register for load delays when PGXP is enabled.
    260   u32 GetFlagsForNewLoadDelayedReg() const;
    261 
    262   void BackupHostState();
    263   void RestoreHostState();
    264 
    265   /// Registers loadstore for possible backpatching.
    266   void AddLoadStoreInfo(void* code_address, u32 code_size, u32 address_register, u32 data_register,
    267                         MemoryAccessSize size, bool is_signed, bool is_load);
    268 
    269   void CompileInstruction();
    270   void CompileBranchDelaySlot(bool dirty_pc = true);
    271 
    272   void CompileTemplate(void (Compiler::*const_func)(CompileFlags), void (Compiler::*func)(CompileFlags),
    273                        const void* pgxp_cpu_func, u32 tflags);
    274   void CompileLoadStoreTemplate(void (Compiler::*func)(CompileFlags, MemoryAccessSize, bool, bool,
    275                                                        const std::optional<VirtualMemoryAddress>&),
    276                                 MemoryAccessSize size, bool store, bool sign, u32 tflags);
    277   void FlushForLoadStore(const std::optional<VirtualMemoryAddress>& address, bool store, bool use_fastmem);
    278   void CompileMoveRegTemplate(Reg dst, Reg src, bool pgxp_move);
    279 
    280   virtual void GeneratePGXPCallWithMIPSRegs(const void* func, u32 arg1val, Reg arg2reg = Reg::count,
    281                                             Reg arg3reg = Reg::count) = 0;
    282 
    283   virtual void Compile_Fallback() = 0;
    284 
    285   void Compile_j();
    286   virtual void Compile_jr(CompileFlags cf) = 0;
    287   void Compile_jr_const(CompileFlags cf);
    288   void Compile_jal();
    289   virtual void Compile_jalr(CompileFlags cf) = 0;
    290   void Compile_jalr_const(CompileFlags cf);
    291   void Compile_syscall();
    292   void Compile_break();
    293 
    294   void Compile_b_const(CompileFlags cf);
    295   void Compile_b(CompileFlags cf);
    296   void Compile_blez(CompileFlags cf);
    297   void Compile_blez_const(CompileFlags cf);
    298   void Compile_bgtz(CompileFlags cf);
    299   void Compile_bgtz_const(CompileFlags cf);
    300   void Compile_beq(CompileFlags cf);
    301   void Compile_beq_const(CompileFlags cf);
    302   void Compile_bne(CompileFlags cf);
    303   void Compile_bne_const(CompileFlags cf);
    304   virtual void Compile_bxx(CompileFlags cf, BranchCondition cond) = 0;
    305   void Compile_bxx_const(CompileFlags cf, BranchCondition cond);
    306 
    307   void Compile_sll_const(CompileFlags cf);
    308   virtual void Compile_sll(CompileFlags cf) = 0;
    309   void Compile_srl_const(CompileFlags cf);
    310   virtual void Compile_srl(CompileFlags cf) = 0;
    311   void Compile_sra_const(CompileFlags cf);
    312   virtual void Compile_sra(CompileFlags cf) = 0;
    313   void Compile_sllv_const(CompileFlags cf);
    314   virtual void Compile_sllv(CompileFlags cf) = 0;
    315   void Compile_srlv_const(CompileFlags cf);
    316   virtual void Compile_srlv(CompileFlags cf) = 0;
    317   void Compile_srav_const(CompileFlags cf);
    318   virtual void Compile_srav(CompileFlags cf) = 0;
    319   void Compile_mult_const(CompileFlags cf);
    320   virtual void Compile_mult(CompileFlags cf) = 0;
    321   void Compile_multu_const(CompileFlags cf);
    322   virtual void Compile_multu(CompileFlags cf) = 0;
    323   void Compile_div_const(CompileFlags cf);
    324   virtual void Compile_div(CompileFlags cf) = 0;
    325   void Compile_divu_const(CompileFlags cf);
    326   virtual void Compile_divu(CompileFlags cf) = 0;
    327   void Compile_add_const(CompileFlags cf);
    328   virtual void Compile_add(CompileFlags cf) = 0;
    329   void Compile_addu_const(CompileFlags cf);
    330   virtual void Compile_addu(CompileFlags cf) = 0;
    331   void Compile_sub_const(CompileFlags cf);
    332   virtual void Compile_sub(CompileFlags cf) = 0;
    333   void Compile_subu_const(CompileFlags cf);
    334   virtual void Compile_subu(CompileFlags cf) = 0;
    335   void Compile_and_const(CompileFlags cf);
    336   virtual void Compile_and(CompileFlags cf) = 0;
    337   void Compile_or_const(CompileFlags cf);
    338   virtual void Compile_or(CompileFlags cf) = 0;
    339   void Compile_xor_const(CompileFlags cf);
    340   virtual void Compile_xor(CompileFlags cf) = 0;
    341   void Compile_nor_const(CompileFlags cf);
    342   virtual void Compile_nor(CompileFlags cf) = 0;
    343   void Compile_slt_const(CompileFlags cf);
    344   virtual void Compile_slt(CompileFlags cf) = 0;
    345   void Compile_sltu_const(CompileFlags cf);
    346   virtual void Compile_sltu(CompileFlags cf) = 0;
    347 
    348   void Compile_addi_const(CompileFlags cf);
    349   virtual void Compile_addi(CompileFlags cf) = 0;
    350   void Compile_addiu_const(CompileFlags cf);
    351   virtual void Compile_addiu(CompileFlags cf) = 0;
    352   void Compile_slti_const(CompileFlags cf);
    353   virtual void Compile_slti(CompileFlags cf) = 0;
    354   void Compile_sltiu_const(CompileFlags cf);
    355   virtual void Compile_sltiu(CompileFlags cf) = 0;
    356   void Compile_andi_const(CompileFlags cf);
    357   virtual void Compile_andi(CompileFlags cf) = 0;
    358   void Compile_ori_const(CompileFlags cf);
    359   virtual void Compile_ori(CompileFlags cf) = 0;
    360   void Compile_xori_const(CompileFlags cf);
    361   virtual void Compile_xori(CompileFlags cf) = 0;
    362   void Compile_lui();
    363 
    364   virtual void Compile_lxx(CompileFlags cf, MemoryAccessSize size, bool sign, bool use_fastmem,
    365                            const std::optional<VirtualMemoryAddress>& address) = 0;
    366   virtual void Compile_lwx(CompileFlags cf, MemoryAccessSize size, bool sign, bool use_fastmem,
    367                            const std::optional<VirtualMemoryAddress>& address) = 0; // lwl/lwr
    368   virtual void Compile_lwc2(CompileFlags cf, MemoryAccessSize size, bool sign, bool use_fastmem,
    369                             const std::optional<VirtualMemoryAddress>& address) = 0;
    370   virtual void Compile_sxx(CompileFlags cf, MemoryAccessSize size, bool sign, bool use_fastmem,
    371                            const std::optional<VirtualMemoryAddress>& address) = 0;
    372   virtual void Compile_swx(CompileFlags cf, MemoryAccessSize size, bool sign, bool use_fastmem,
    373                            const std::optional<VirtualMemoryAddress>& address) = 0; // swl/swr
    374   virtual void Compile_swc2(CompileFlags cf, MemoryAccessSize size, bool sign, bool use_fastmem,
    375                             const std::optional<VirtualMemoryAddress>& address) = 0;
    376 
    377   static u32* GetCop0RegPtr(Cop0Reg reg);
    378   static u32 GetCop0RegWriteMask(Cop0Reg reg);
    379 
    380   static void MIPSSignedDivide(s32 num, s32 denom, u32* lo, u32* hi);
    381   static void MIPSUnsignedDivide(u32 num, u32 denom, u32* lo, u32* hi);
    382 
    383   void Compile_mfc0(CompileFlags cf);
    384   virtual void Compile_mtc0(CompileFlags cf) = 0;
    385   virtual void Compile_rfe(CompileFlags cf) = 0;
    386 
    387   void AddGTETicks(TickCount ticks);
    388   void StallUntilGTEComplete();
    389   virtual void Compile_mfc2(CompileFlags cf) = 0;
    390   virtual void Compile_mtc2(CompileFlags cf) = 0;
    391   virtual void Compile_cop2(CompileFlags cf) = 0;
    392 
    393   enum GTERegisterAccessAction : u8
    394   {
    395     Ignore,
    396     Direct,
    397     ZeroExtend16,
    398     SignExtend16,
    399     CallHandler,
    400     PushFIFO,
    401   };
    402 
    403   static std::pair<u32*, GTERegisterAccessAction> GetGTERegisterPointer(u32 index, bool writing);
    404 
    405   CodeCache::Block* m_block = nullptr;
    406   u32 m_compiler_pc = 0;
    407   TickCount m_cycles = 0;
    408   TickCount m_gte_done_cycle = 0;
    409 
    410   const Instruction* inst = nullptr;
    411   CodeCache::InstructionInfo* iinfo = nullptr;
    412   u32 m_current_instruction_pc = 0;
    413   bool m_current_instruction_branch_delay_slot = false;
    414   bool m_branch_delay_slot_swapped = false;
    415 
    416   bool m_dirty_pc = false;
    417   bool m_dirty_instruction_bits = false;
    418   bool m_dirty_gte_done_cycle = false;
    419   bool m_block_ended = false;
    420 
    421   std::bitset<static_cast<size_t>(Reg::count)> m_constant_regs_valid = {};
    422   std::bitset<static_cast<size_t>(Reg::count)> m_constant_regs_dirty = {};
    423   std::array<u32, static_cast<size_t>(Reg::count)> m_constant_reg_values = {};
    424 
    425   std::array<HostRegAlloc, NUM_HOST_REGS> m_host_regs = {};
    426   u16 m_register_alloc_counter = 0;
    427 
    428   bool m_load_delay_dirty = true;
    429   Reg m_load_delay_register = Reg::count;
    430   u32 m_load_delay_value_register = 0;
    431 
    432   Reg m_next_load_delay_register = Reg::count;
    433   u32 m_next_load_delay_value_register = 0;
    434 
    435   struct HostStateBackup
    436   {
    437     TickCount cycles;
    438     TickCount gte_done_cycle;
    439     u32 compiler_pc;
    440     bool dirty_pc;
    441     bool dirty_instruction_bits;
    442     bool dirty_gte_done_cycle;
    443     bool block_ended;
    444     const Instruction* inst;
    445     CodeCache::InstructionInfo* iinfo;
    446     u32 current_instruction_pc;
    447     bool current_instruction_delay_slot;
    448     std::bitset<static_cast<size_t>(Reg::count)> const_regs_valid;
    449     std::bitset<static_cast<size_t>(Reg::count)> const_regs_dirty;
    450     std::array<u32, static_cast<size_t>(Reg::count)> const_regs_values;
    451     std::array<HostRegAlloc, NUM_HOST_REGS> host_regs;
    452     u16 register_alloc_counter;
    453     bool load_delay_dirty;
    454     Reg load_delay_register;
    455     u32 load_delay_value_register;
    456     Reg next_load_delay_register;
    457     u32 next_load_delay_value_register;
    458   };
    459 
    460   // we need two of these, one for branch delays, and another if we have an overflow in the delay slot
    461   std::array<HostStateBackup, 2> m_host_state_backup = {};
    462   u32 m_host_state_backup_count = 0;
    463 
    464   //////////////////////////////////////////////////////////////////////////
    465   // Speculative Constants
    466   //////////////////////////////////////////////////////////////////////////
    467   using SpecValue = std::optional<u32>;
    468   struct SpeculativeConstants
    469   {
    470     std::array<SpecValue, static_cast<u8>(Reg::count)> regs;
    471     std::unordered_map<PhysicalMemoryAddress, SpecValue> memory;
    472     SpecValue cop0_sr;
    473   };
    474 
    475   void InitSpeculativeRegs();
    476   void InvalidateSpeculativeValues();
    477   SpecValue SpecReadReg(Reg reg);
    478   void SpecWriteReg(Reg reg, SpecValue value);
    479   void SpecInvalidateReg(Reg reg);
    480   void SpecCopyReg(Reg dst, Reg src);
    481   SpecValue SpecReadMem(u32 address);
    482   void SpecWriteMem(VirtualMemoryAddress address, SpecValue value);
    483   void SpecInvalidateMem(VirtualMemoryAddress address);
    484   bool SpecIsCacheIsolated();
    485 
    486   SpeculativeConstants m_speculative_constants;
    487 
    488   void SpecExec_b();
    489   void SpecExec_jal();
    490   void SpecExec_jalr();
    491   void SpecExec_sll();
    492   void SpecExec_srl();
    493   void SpecExec_sra();
    494   void SpecExec_sllv();
    495   void SpecExec_srlv();
    496   void SpecExec_srav();
    497   void SpecExec_mult();
    498   void SpecExec_multu();
    499   void SpecExec_div();
    500   void SpecExec_divu();
    501   void SpecExec_add();
    502   void SpecExec_addu();
    503   void SpecExec_sub();
    504   void SpecExec_subu();
    505   void SpecExec_and();
    506   void SpecExec_or();
    507   void SpecExec_xor();
    508   void SpecExec_nor();
    509   void SpecExec_slt();
    510   void SpecExec_sltu();
    511   void SpecExec_addi();
    512   void SpecExec_addiu();
    513   void SpecExec_slti();
    514   void SpecExec_sltiu();
    515   void SpecExec_andi();
    516   void SpecExec_ori();
    517   void SpecExec_xori();
    518   void SpecExec_lui();
    519   SpecValue SpecExec_LoadStoreAddr();
    520   void SpecExec_lxx(MemoryAccessSize size, bool sign);
    521   void SpecExec_lwx(bool lwr); // lwl/lwr
    522   void SpecExec_sxx(MemoryAccessSize size);
    523   void SpecExec_swx(bool swr); // swl/swr
    524   void SpecExec_swc2();
    525   void SpecExec_mfc0();
    526   void SpecExec_mtc0();
    527   void SpecExec_rfe();
    528 
    529   // PGXP memory callbacks
    530   static const std::array<std::array<const void*, 2>, 3> s_pgxp_mem_load_functions;
    531   static const std::array<const void*, 3> s_pgxp_mem_store_functions;
    532 };
    533 
    534 void BackpatchLoadStore(void* exception_pc, const CodeCache::LoadstoreBackpatchInfo& info);
    535 
    536 u32 CompileLoadStoreThunk(void* thunk_code, u32 thunk_space, void* code_address, u32 code_size, TickCount cycles_to_add,
    537                           TickCount cycles_to_remove, u32 gpr_bitmask, u8 address_register, u8 data_register,
    538                           MemoryAccessSize size, bool is_signed, bool is_load);
    539 
    540 extern Compiler* g_compiler;
    541 } // namespace CPU::NewRec