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