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_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