Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/src/core/cpu_code_cache_private.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 "bus.h"
7
#include "common/bitfield.h"
8
#include "common/perf_scope.h"
9
#include "cpu_code_cache.h"
10
#include "cpu_core_private.h"
11
#include "cpu_types.h"
12
13
#include <array>
14
#include <unordered_map>
15
16
namespace CPU::CodeCache {
17
18
enum : u32
19
{
20
LUT_TABLE_COUNT = 0x10000,
21
LUT_TABLE_SIZE = 0x10000 / sizeof(u32), // 16384, one for each PC
22
LUT_TABLE_SHIFT = 16,
23
24
MAX_BLOCK_EXIT_LINKS = 2,
25
};
26
27
using CodeLUT = const void**;
28
using CodeLUTArray = std::array<CodeLUT, LUT_TABLE_COUNT>;
29
using BlockLinkMap = std::unordered_multimap<u32, void*>; // TODO: try ordered?
30
31
enum RegInfoFlags : u8
32
{
33
RI_LIVE = (1 << 0),
34
RI_USED = (1 << 1),
35
RI_LASTUSE = (1 << 2),
36
};
37
38
struct InstructionInfo
39
{
40
bool is_branch_instruction : 1;
41
bool is_direct_branch_instruction : 1;
42
bool is_unconditional_branch_instruction : 1;
43
bool is_branch_delay_slot : 1;
44
bool is_load_instruction : 1;
45
bool is_store_instruction : 1;
46
bool is_load_delay_slot : 1;
47
bool is_last_instruction : 1;
48
bool has_load_delay : 1;
49
50
u8 reg_flags[static_cast<u8>(Reg::count)];
51
// Reg write_reg[3];
52
Reg read_reg[3];
53
54
// If unset, values which are not live will not be written back to memory.
55
// Tends to break stuff at the moment.
56
static constexpr bool WRITE_DEAD_VALUES = true;
57
58
/// Returns true if the register is used later in the block, and this isn't the last instruction to use it.
59
/// In other words, the register is worth keeping in a host register/caching it.
60
inline bool UsedTest(Reg reg) const { return (reg_flags[static_cast<u8>(reg)] & (RI_USED | RI_LASTUSE)) == RI_USED; }
61
62
/// Returns true if the value should be computed/written back.
63
/// Basically, this means it's either used before it's overwritten, or not overwritten by the end of the block.
64
inline bool LiveTest(Reg reg) const
65
{
66
return WRITE_DEAD_VALUES || ((reg_flags[static_cast<u8>(reg)] & RI_LIVE) != 0);
67
}
68
69
/// Returns true if the register can be renamed into another.
70
inline bool RenameTest(Reg reg) const { return (reg == Reg::zero || !UsedTest(reg) || !LiveTest(reg)); }
71
72
/// Returns true if this instruction reads this register.
73
inline bool ReadsReg(Reg reg) const { return (read_reg[0] == reg || read_reg[1] == reg || read_reg[2] == reg); }
74
};
75
76
enum class BlockState : u8
77
{
78
Valid,
79
Invalidated,
80
NeedsRecompile,
81
FallbackToInterpreter
82
};
83
84
enum class BlockFlags : u8
85
{
86
None = 0,
87
ContainsLoadStoreInstructions = (1 << 0),
88
SpansPages = (1 << 1),
89
BranchDelaySpansPages = (1 << 2),
90
IsUsingICache = (1 << 3),
91
NeedsDynamicFetchTicks = (1 << 4),
92
};
93
IMPLEMENT_ENUM_CLASS_BITWISE_OPERATORS(BlockFlags);
94
95
enum class PageProtectionMode : u8
96
{
97
WriteProtected,
98
ManualCheck,
99
Unprotected,
100
};
101
102
struct BlockMetadata
103
{
104
TickCount uncached_fetch_ticks;
105
u32 icache_line_count;
106
BlockFlags flags;
107
};
108
109
struct alignas(16) Block
110
{
111
u32 pc;
112
u32 size; // in guest instructions
113
const void* host_code;
114
115
// links to previous/next block within page
116
Block* next_block_in_page;
117
118
BlockLinkMap::iterator exit_links[MAX_BLOCK_EXIT_LINKS];
119
u8 num_exit_links;
120
121
// TODO: Move up so it's part of the same cache line
122
BlockState state;
123
BlockFlags flags;
124
PageProtectionMode protection;
125
126
TickCount uncached_fetch_ticks;
127
u32 icache_line_count;
128
129
u32 host_code_size;
130
u32 compile_frame;
131
u8 compile_count;
132
133
// followed by Instruction * size, InstructionRegInfo * size
134
ALWAYS_INLINE const Instruction* Instructions() const { return reinterpret_cast<const Instruction*>(this + 1); }
135
ALWAYS_INLINE Instruction* Instructions() { return reinterpret_cast<Instruction*>(this + 1); }
136
137
ALWAYS_INLINE const InstructionInfo* InstructionsInfo() const
138
{
139
return reinterpret_cast<const InstructionInfo*>(Instructions() + size);
140
}
141
ALWAYS_INLINE InstructionInfo* InstructionsInfo()
142
{
143
return reinterpret_cast<InstructionInfo*>(Instructions() + size);
144
}
145
146
// returns true if the block has a given flag
147
ALWAYS_INLINE bool HasFlag(BlockFlags flag) const { return ((flags & flag) != BlockFlags::None); }
148
149
// returns the page index for the start of the block
150
ALWAYS_INLINE u32 StartPageIndex() const { return Bus::GetRAMCodePageIndex(pc); }
151
152
// returns the page index for the last instruction in the block (inclusive)
153
ALWAYS_INLINE u32 EndPageIndex() const { return Bus::GetRAMCodePageIndex(pc + ((size - 1) * sizeof(Instruction))); }
154
155
// returns true if the block spans multiple pages
156
ALWAYS_INLINE bool SpansPages() const { return StartPageIndex() != EndPageIndex(); }
157
};
158
159
using BlockLUTArray = std::array<Block**, LUT_TABLE_COUNT>;
160
161
struct LoadstoreBackpatchInfo
162
{
163
union
164
{
165
struct
166
{
167
u32 gpr_bitmask;
168
u16 cycles;
169
u16 address_register : 5;
170
u16 data_register : 5;
171
u16 size : 2;
172
u16 is_signed : 1;
173
u16 is_load : 1;
174
};
175
176
const void* thunk_address; // only needed for oldrec
177
};
178
179
u32 guest_pc;
180
u32 guest_block;
181
u8 code_size;
182
183
MemoryAccessSize AccessSize() const { return static_cast<MemoryAccessSize>(size); }
184
u32 AccessSizeInBytes() const { return 1u << size; }
185
};
186
#ifdef CPU_ARCH_ARM32
187
static_assert(sizeof(LoadstoreBackpatchInfo) == 20);
188
#else
189
static_assert(sizeof(LoadstoreBackpatchInfo) == 24);
190
#endif
191
192
static inline bool AddressInRAM(VirtualMemoryAddress pc)
193
{
194
return VirtualAddressToPhysical(pc) < Bus::g_ram_size;
195
}
196
197
struct PageProtectionInfo
198
{
199
Block* first_block_in_page;
200
Block* last_block_in_page;
201
202
PageProtectionMode mode;
203
u16 invalidate_count;
204
u32 invalidate_frame;
205
};
206
static_assert(sizeof(PageProtectionInfo) == (sizeof(Block*) * 2 + 8));
207
208
template<PGXPMode pgxp_mode>
209
void InterpretCachedBlock(const Block* block);
210
211
template<PGXPMode pgxp_mode>
212
void InterpretUncachedBlock();
213
214
void LogCurrentState();
215
216
#if defined(_DEBUG) || defined(_DEVEL) || false
217
// Enable disassembly of host assembly code.
218
#define ENABLE_HOST_DISASSEMBLY 1
219
#endif
220
221
/// Access to normal code allocator.
222
u8* GetFreeCodePointer();
223
u32 GetFreeCodeSpace();
224
void CommitCode(u32 length);
225
226
/// Access to far code allocator.
227
u8* GetFreeFarCodePointer();
228
u32 GetFreeFarCodeSpace();
229
void CommitFarCode(u32 length);
230
231
/// Adjusts the free code pointer to the specified alignment, padding with bytes.
232
/// Assumes alignment is a power-of-two.
233
void AlignCode(u32 alignment);
234
235
const void* GetInterpretUncachedBlockFunction();
236
237
void CompileOrRevalidateBlock(u32 start_pc);
238
void DiscardAndRecompileBlock(u32 start_pc);
239
const void* CreateBlockLink(Block* from_block, void* code, u32 newpc);
240
const void* CreateSelfBlockLink(Block* block, void* code, const void* block_start);
241
242
void AddLoadStoreInfo(void* code_address, u32 code_size, u32 guest_pc, const void* thunk_address);
243
void AddLoadStoreInfo(void* code_address, u32 code_size, u32 guest_pc, u32 guest_block, TickCount cycles,
244
u32 gpr_bitmask, u8 address_register, u8 data_register, MemoryAccessSize size, bool is_signed,
245
bool is_load);
246
bool HasPreviouslyFaultedOnPC(u32 guest_pc);
247
248
u32 EmitASMFunctions(void* code, u32 code_size);
249
u32 EmitJump(void* code, const void* dst, bool flush_icache);
250
void EmitAlignmentPadding(void* dst, size_t size);
251
252
void DisassembleAndLogHostCode(const void* start, u32 size);
253
u32 GetHostInstructionCount(const void* start, u32 size);
254
255
extern CodeLUTArray g_code_lut;
256
257
extern NORETURN_FUNCTION_POINTER void (*g_enter_recompiler)();
258
extern const void* g_compile_or_revalidate_block;
259
extern const void* g_run_events_and_dispatch;
260
extern const void* g_dispatcher;
261
extern const void* g_block_dispatcher;
262
extern const void* g_interpret_block;
263
extern const void* g_discard_and_recompile_block;
264
265
#ifdef ENABLE_RECOMPILER_PROFILING
266
267
extern PerfScope MIPSPerfScope;
268
269
#endif // ENABLE_RECOMPILER_PROFILING
270
271
} // namespace CPU::CodeCache
272
273