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