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