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_recompiler_code_generator.h (14780B)


      1 // SPDX-FileCopyrightText: 2019-2022 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 "cpu_code_cache_private.h"
      7 #include "cpu_recompiler_register_cache.h"
      8 #include "cpu_recompiler_thunks.h"
      9 #include "cpu_recompiler_types.h"
     10 #include "cpu_types.h"
     11 
     12 #include <array>
     13 #include <utility>
     14 
     15 namespace CPU::Recompiler {
     16 
     17 enum class Condition : u8
     18 {
     19   Always,
     20   NotEqual,
     21   Equal,
     22   Overflow,
     23   Greater,
     24   GreaterEqual,
     25   LessEqual,
     26   Less,
     27   Negative,
     28   PositiveOrZero,
     29   Above,      // unsigned variant of Greater
     30   AboveEqual, // unsigned variant of GreaterEqual
     31   Below,      // unsigned variant of Less
     32   BelowEqual, // unsigned variant of LessEqual
     33 
     34   NotZero,
     35   Zero
     36 };
     37 
     38 class CodeGenerator
     39 {
     40 public:
     41   using SpeculativeValue = std::optional<u32>;
     42 
     43   struct CodeBlockInstruction
     44   {
     45     const Instruction* instruction;
     46     const CodeCache::InstructionInfo* info;
     47   };
     48 
     49   CodeGenerator();
     50   ~CodeGenerator();
     51 
     52   static const char* GetHostRegName(HostReg reg, RegSize size = HostPointerSize);
     53 
     54   static void BackpatchLoadStore(void* host_pc, const CodeCache::LoadstoreBackpatchInfo& lbi);
     55 
     56   const void* CompileBlock(CodeCache::Block* block, u32* out_host_code_size, u32* out_host_far_code_size);
     57 
     58   //////////////////////////////////////////////////////////////////////////
     59   // Code Generation
     60   //////////////////////////////////////////////////////////////////////////
     61   void EmitBeginBlock(bool allocate_registers = true);
     62   void EmitEndBlock(bool free_registers, const void* jump_to);
     63   void EmitExceptionExit();
     64   void EmitExceptionExitOnBool(const Value& value);
     65   const void* FinalizeBlock(u32* out_host_code_size, u32* out_host_far_code_size);
     66 
     67   void EmitSignExtend(HostReg to_reg, RegSize to_size, HostReg from_reg, RegSize from_size);
     68   void EmitZeroExtend(HostReg to_reg, RegSize to_size, HostReg from_reg, RegSize from_size);
     69   void EmitCopyValue(HostReg to_reg, const Value& value);
     70   void EmitAdd(HostReg to_reg, HostReg from_reg, const Value& value, bool set_flags);
     71   void EmitSub(HostReg to_reg, HostReg from_reg, const Value& value, bool set_flags);
     72   void EmitCmp(HostReg to_reg, const Value& value);
     73   void EmitMul(HostReg to_reg_hi, HostReg to_reg_lo, const Value& lhs, const Value& rhs, bool signed_multiply);
     74   void EmitDiv(HostReg to_reg_quotient, HostReg to_reg_remainder, HostReg num, HostReg denom, RegSize size,
     75                bool signed_divide);
     76   void EmitInc(HostReg to_reg, RegSize size);
     77   void EmitDec(HostReg to_reg, RegSize size);
     78   void EmitShl(HostReg to_reg, HostReg from_reg, RegSize size, const Value& amount_value,
     79                bool assume_amount_masked = true);
     80   void EmitShr(HostReg to_reg, HostReg from_reg, RegSize size, const Value& amount_value,
     81                bool assume_amount_masked = true);
     82   void EmitSar(HostReg to_reg, HostReg from_reg, RegSize size, const Value& amount_value,
     83                bool assume_amount_masked = true);
     84   void EmitAnd(HostReg to_reg, HostReg from_reg, const Value& value);
     85   void EmitOr(HostReg to_reg, HostReg from_reg, const Value& value);
     86   void EmitXor(HostReg to_reg, HostReg from_reg, const Value& value);
     87   void EmitTest(HostReg to_reg, const Value& value);
     88   void EmitNot(HostReg to_reg, RegSize size);
     89   void EmitSetConditionResult(HostReg to_reg, RegSize to_size, Condition condition);
     90 
     91   void EmitLoadGuestRegister(HostReg host_reg, Reg guest_reg);
     92   void EmitStoreGuestRegister(Reg guest_reg, const Value& value);
     93   void EmitStoreInterpreterLoadDelay(Reg reg, const Value& value);
     94   void EmitFlushInterpreterLoadDelay();
     95   void EmitMoveNextInterpreterLoadDelay();
     96   void EmitCancelInterpreterLoadDelayForReg(Reg reg);
     97   void EmitICacheCheckAndUpdate();
     98   void EmitBlockProtectCheck(const u8* ram_ptr, const u8* shadow_ptr, u32 size);
     99   void EmitStallUntilGTEComplete();
    100   void EmitLoadCPUStructField(HostReg host_reg, RegSize size, u32 offset);
    101   void EmitStoreCPUStructField(u32 offset, const Value& value);
    102   void EmitAddCPUStructField(u32 offset, const Value& value);
    103   void EmitLoadGlobal(HostReg host_reg, RegSize size, const void* ptr);
    104   void EmitStoreGlobal(void* ptr, const Value& value);
    105   void EmitLoadGlobalAddress(HostReg host_reg, const void* ptr);
    106 
    107   // Automatically generates an exception handler.
    108   Value EmitLoadGuestMemory(Instruction instruction, const CodeCache::InstructionInfo& info, const Value& address,
    109                             const SpeculativeValue& address_spec, RegSize size);
    110   void EmitLoadGuestRAMFastmem(const Value& address, RegSize size, Value& result);
    111   void EmitLoadGuestMemoryFastmem(Instruction instruction, const CodeCache::InstructionInfo& info, const Value& address,
    112                                   RegSize size, Value& result);
    113   void EmitLoadGuestMemorySlowmem(Instruction instruction, const CodeCache::InstructionInfo& info, const Value& address,
    114                                   RegSize size, Value& result, bool in_far_code);
    115   void EmitStoreGuestMemory(Instruction instruction, const CodeCache::InstructionInfo& info, const Value& address,
    116                             const SpeculativeValue& address_spec, RegSize size, const Value& value);
    117   void EmitStoreGuestMemoryFastmem(Instruction instruction, const CodeCache::InstructionInfo& info,
    118                                    const Value& address, RegSize size, const Value& value);
    119   void EmitStoreGuestMemorySlowmem(Instruction instruction, const CodeCache::InstructionInfo& info,
    120                                    const Value& address, RegSize size, const Value& value, bool in_far_code);
    121   void EnsureMembaseLoaded();
    122   void EmitUpdateFastmemBase();
    123 
    124   // Unconditional branch to pointer. May allocate a scratch register.
    125   void EmitBranch(const void* address, bool allow_scratch = true);
    126   void EmitBranch(LabelType* label);
    127 
    128   // Branching, generates two paths.
    129   void EmitConditionalBranch(Condition condition, bool invert, HostReg value, RegSize size, LabelType* label);
    130   void EmitConditionalBranch(Condition condition, bool invert, HostReg lhs, const Value& rhs, LabelType* label);
    131   void EmitConditionalBranch(Condition condition, bool invert, LabelType* label);
    132   void EmitBranchIfBitClear(HostReg reg, RegSize size, u8 bit, LabelType* label);
    133   void EmitBranchIfBitSet(HostReg reg, RegSize size, u8 bit, LabelType* label);
    134   void EmitBindLabel(LabelType* label);
    135 
    136   u32 PrepareStackForCall();
    137   void RestoreStackAfterCall(u32 adjust_size);
    138 
    139   void EmitCall(const void* ptr);
    140   void EmitFunctionCallPtr(Value* return_value, const void* ptr);
    141   void EmitFunctionCallPtr(Value* return_value, const void* ptr, const Value& arg1);
    142   void EmitFunctionCallPtr(Value* return_value, const void* ptr, const Value& arg1, const Value& arg2);
    143   void EmitFunctionCallPtr(Value* return_value, const void* ptr, const Value& arg1, const Value& arg2,
    144                            const Value& arg3);
    145   void EmitFunctionCallPtr(Value* return_value, const void* ptr, const Value& arg1, const Value& arg2,
    146                            const Value& arg3, const Value& arg4);
    147 
    148   template<typename FunctionType>
    149   void EmitFunctionCall(Value* return_value, const FunctionType ptr)
    150   {
    151     EmitFunctionCallPtr(return_value, reinterpret_cast<const void**>(ptr));
    152   }
    153 
    154   template<typename FunctionType>
    155   void EmitFunctionCall(Value* return_value, const FunctionType ptr, const Value& arg1)
    156   {
    157     EmitFunctionCallPtr(return_value, reinterpret_cast<const void**>(ptr), arg1);
    158   }
    159 
    160   template<typename FunctionType>
    161   void EmitFunctionCall(Value* return_value, const FunctionType ptr, const Value& arg1, const Value& arg2)
    162   {
    163     EmitFunctionCallPtr(return_value, reinterpret_cast<const void**>(ptr), arg1, arg2);
    164   }
    165 
    166   template<typename FunctionType>
    167   void EmitFunctionCall(Value* return_value, const FunctionType ptr, const Value& arg1, const Value& arg2,
    168                         const Value& arg3)
    169   {
    170     EmitFunctionCallPtr(return_value, reinterpret_cast<const void**>(ptr), arg1, arg2, arg3);
    171   }
    172 
    173   template<typename FunctionType>
    174   void EmitFunctionCall(Value* return_value, const FunctionType ptr, const Value& arg1, const Value& arg2,
    175                         const Value& arg3, const Value& arg4)
    176   {
    177     EmitFunctionCallPtr(return_value, reinterpret_cast<const void**>(ptr), arg1, arg2, arg3, arg4);
    178   }
    179 
    180   // Host register saving.
    181   void EmitPushHostReg(HostReg reg, u32 position);
    182   void EmitPushHostRegPair(HostReg reg, HostReg reg2, u32 position);
    183   void EmitPopHostReg(HostReg reg, u32 position);
    184   void EmitPopHostRegPair(HostReg reg, HostReg reg2, u32 position);
    185 
    186   // Value ops
    187   Value AddValues(const Value& lhs, const Value& rhs, bool set_flags);
    188   Value SubValues(const Value& lhs, const Value& rhs, bool set_flags);
    189   std::pair<Value, Value> MulValues(const Value& lhs, const Value& rhs, bool signed_multiply);
    190   Value ShlValues(const Value& lhs, const Value& rhs, bool assume_amount_masked = true);
    191   Value ShrValues(const Value& lhs, const Value& rhs, bool assume_amount_masked = true);
    192   Value SarValues(const Value& lhs, const Value& rhs, bool assume_amount_masked = true);
    193   Value OrValues(const Value& lhs, const Value& rhs);
    194   void OrValueInPlace(Value& lhs, const Value& rhs);
    195   Value AndValues(const Value& lhs, const Value& rhs);
    196   void AndValueInPlace(Value& lhs, const Value& rhs);
    197   Value XorValues(const Value& lhs, const Value& rhs);
    198   Value NotValue(const Value& val);
    199 
    200   const TickCount* GetFetchMemoryAccessTimePtr() const;
    201 
    202   // Raising exception if condition is true.
    203   void GenerateExceptionExit(Instruction instruction, const CodeCache::InstructionInfo& info, Exception excode,
    204                              Condition condition = Condition::Always);
    205 
    206 private:
    207   // Host register setup
    208   void InitHostRegs();
    209 
    210   Value ConvertValueSize(const Value& value, RegSize size, bool sign_extend);
    211   void ConvertValueSizeInPlace(Value* value, RegSize size, bool sign_extend);
    212 
    213   Value GetValueInHostRegister(const Value& value, bool allow_zero_register = true);
    214   Value GetValueInHostOrScratchRegister(const Value& value, bool allow_zero_register = true);
    215 
    216   void SwitchToFarCode();
    217   void SwitchToNearCode();
    218   void* GetStartNearCodePointer() const;
    219   void* GetCurrentCodePointer() const;
    220   void* GetCurrentNearCodePointer() const;
    221   void* GetCurrentFarCodePointer() const;
    222 
    223   //////////////////////////////////////////////////////////////////////////
    224   // Code Generation Helpers
    225   //////////////////////////////////////////////////////////////////////////
    226   // branch target, memory address, etc
    227   void BlockPrologue();
    228   void BlockEpilogue();
    229   void InstructionPrologue(Instruction instruction, const CodeCache::InstructionInfo& info, TickCount cycles,
    230                            bool force_sync = false);
    231   void InstructionEpilogue(Instruction instruction, const CodeCache::InstructionInfo& info);
    232   void TruncateBlockAtCurrentInstruction();
    233   void AddPendingCycles(bool commit);
    234   void AddGTETicks(TickCount ticks);
    235   void StallUntilGTEComplete();
    236 
    237   Value CalculatePC(u32 offset = 0);
    238   Value GetCurrentInstructionPC(u32 offset = 0);
    239   void WriteNewPC(const Value& value, bool commit);
    240 
    241   Value DoGTERegisterRead(u32 index);
    242   void DoGTERegisterWrite(u32 index, const Value& value);
    243 
    244   //////////////////////////////////////////////////////////////////////////
    245   // Instruction Code Generators
    246   //////////////////////////////////////////////////////////////////////////
    247   bool CompileInstruction(Instruction instruction, const CodeCache::InstructionInfo& info);
    248   bool Compile_Fallback(Instruction instruction, const CodeCache::InstructionInfo& info);
    249   bool Compile_Nop(Instruction instruction, const CodeCache::InstructionInfo& info);
    250   bool Compile_Bitwise(Instruction instruction, const CodeCache::InstructionInfo& info);
    251   bool Compile_Shift(Instruction instruction, const CodeCache::InstructionInfo& info);
    252   bool Compile_Load(Instruction instruction, const CodeCache::InstructionInfo& info);
    253   bool Compile_Store(Instruction instruction, const CodeCache::InstructionInfo& info);
    254   bool Compile_LoadLeftRight(Instruction instruction, const CodeCache::InstructionInfo& info);
    255   bool Compile_StoreLeftRight(Instruction instruction, const CodeCache::InstructionInfo& info);
    256   bool Compile_MoveHiLo(Instruction instruction, const CodeCache::InstructionInfo& info);
    257   bool Compile_Add(Instruction instruction, const CodeCache::InstructionInfo& info);
    258   bool Compile_Subtract(Instruction instruction, const CodeCache::InstructionInfo& info);
    259   bool Compile_Multiply(Instruction instruction, const CodeCache::InstructionInfo& info);
    260   bool Compile_Divide(Instruction instruction, const CodeCache::InstructionInfo& info);
    261   bool Compile_SignedDivide(Instruction instruction, const CodeCache::InstructionInfo& info);
    262   bool Compile_SetLess(Instruction instruction, const CodeCache::InstructionInfo& info);
    263   bool Compile_Branch(Instruction instruction, const CodeCache::InstructionInfo& info);
    264   bool Compile_lui(Instruction instruction, const CodeCache::InstructionInfo& info);
    265   bool Compile_cop0(Instruction instruction, const CodeCache::InstructionInfo& info);
    266   bool Compile_cop2(Instruction instruction, const CodeCache::InstructionInfo& info);
    267 
    268   CodeCache::Block* m_block = nullptr;
    269   CodeBlockInstruction m_block_start = {};
    270   CodeBlockInstruction m_block_end = {};
    271   CodeBlockInstruction m_current_instruction = {};
    272   RegisterCache m_register_cache;
    273   CodeEmitter m_near_emitter;
    274   CodeEmitter m_far_emitter;
    275   CodeEmitter* m_emit;
    276 
    277   TickCount m_delayed_cycles_add = 0;
    278   TickCount m_gte_done_cycle = 0;
    279 
    280   u32 m_pc = 0;
    281   bool m_pc_valid = false;
    282   bool m_block_linked = false;
    283 
    284   // whether various flags need to be reset.
    285   bool m_current_instruction_in_branch_delay_slot_dirty = false;
    286   bool m_branch_was_taken_dirty = false;
    287   bool m_current_instruction_was_branch_taken_dirty = false;
    288   bool m_load_delay_dirty = false;
    289   bool m_next_load_delay_dirty = false;
    290   bool m_gte_busy_cycles_dirty = false;
    291   bool m_membase_loaded = false;
    292 
    293   //////////////////////////////////////////////////////////////////////////
    294   // Speculative Constants
    295   //////////////////////////////////////////////////////////////////////////
    296   struct SpeculativeConstants
    297   {
    298     std::array<SpeculativeValue, static_cast<u8>(Reg::count)> regs;
    299     std::unordered_map<PhysicalMemoryAddress, SpeculativeValue> memory;
    300     SpeculativeValue cop0_sr;
    301   };
    302 
    303   void InitSpeculativeRegs();
    304   void InvalidateSpeculativeValues();
    305   SpeculativeValue SpeculativeReadReg(Reg reg);
    306   void SpeculativeWriteReg(Reg reg, SpeculativeValue value);
    307   SpeculativeValue SpeculativeReadMemory(u32 address);
    308   void SpeculativeWriteMemory(VirtualMemoryAddress address, SpeculativeValue value);
    309   bool SpeculativeIsCacheIsolated();
    310 
    311   SpeculativeConstants m_speculative_constants;
    312 };
    313 
    314 } // namespace CPU::Recompiler