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_register_cache.cpp (29125B)


      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 #include "cpu_recompiler_register_cache.h"
      5 #include "common/log.h"
      6 #include "cpu_recompiler_code_generator.h"
      7 #include <cinttypes>
      8 Log_SetChannel(CPU::Recompiler);
      9 
     10 namespace CPU::Recompiler {
     11 
     12 Value::Value() = default;
     13 
     14 Value::Value(RegisterCache* regcache_, u64 constant_, RegSize size_, ValueFlags flags_)
     15   : regcache(regcache_), constant_value(constant_), size(size_), flags(flags_)
     16 {
     17 }
     18 
     19 Value::Value(const Value& other)
     20   : regcache(other.regcache), constant_value(other.constant_value), host_reg(other.host_reg), size(other.size),
     21     flags(other.flags)
     22 {
     23   AssertMsg(!other.IsScratch(), "Can't copy a temporary register");
     24 }
     25 
     26 Value::Value(Value&& other)
     27   : regcache(other.regcache), constant_value(other.constant_value), host_reg(other.host_reg), size(other.size),
     28     flags(other.flags)
     29 {
     30   other.Clear();
     31 }
     32 
     33 Value::Value(RegisterCache* regcache_, HostReg reg_, RegSize size_, ValueFlags flags_)
     34   : regcache(regcache_), host_reg(reg_), size(size_), flags(flags_)
     35 {
     36 }
     37 
     38 Value::~Value()
     39 {
     40   Release();
     41 }
     42 
     43 Value& Value::operator=(const Value& other)
     44 {
     45   AssertMsg(!other.IsScratch(), "Can't copy a temporary register");
     46 
     47   Release();
     48   regcache = other.regcache;
     49   constant_value = other.constant_value;
     50   host_reg = other.host_reg;
     51   size = other.size;
     52   flags = other.flags;
     53 
     54   return *this;
     55 }
     56 
     57 Value& Value::operator=(Value&& other)
     58 {
     59   Release();
     60   regcache = other.regcache;
     61   constant_value = other.constant_value;
     62   host_reg = other.host_reg;
     63   size = other.size;
     64   flags = other.flags;
     65   other.Clear();
     66   return *this;
     67 }
     68 
     69 void Value::Clear()
     70 {
     71   regcache = nullptr;
     72   constant_value = 0;
     73   host_reg = {};
     74   size = RegSize_8;
     75   flags = ValueFlags::None;
     76 }
     77 
     78 void Value::Release()
     79 {
     80   if (IsScratch())
     81   {
     82     DebugAssert(IsInHostRegister() && regcache);
     83     regcache->FreeHostReg(host_reg);
     84   }
     85 }
     86 
     87 void Value::ReleaseAndClear()
     88 {
     89   Release();
     90   Clear();
     91 }
     92 
     93 void Value::Discard()
     94 {
     95   DebugAssert(IsInHostRegister());
     96   regcache->DiscardHostReg(host_reg);
     97 }
     98 
     99 void Value::Undiscard()
    100 {
    101   DebugAssert(IsInHostRegister());
    102   regcache->UndiscardHostReg(host_reg);
    103 }
    104 
    105 RegisterCache::RegisterCache(CodeGenerator& code_generator) : m_code_generator(code_generator)
    106 {
    107   m_state.guest_reg_order.fill(Reg::count);
    108 }
    109 
    110 RegisterCache::~RegisterCache()
    111 {
    112   Assert(m_state_stack.empty());
    113 }
    114 
    115 void RegisterCache::SetHostRegAllocationOrder(std::initializer_list<HostReg> regs)
    116 {
    117   size_t index = 0;
    118   for (HostReg reg : regs)
    119   {
    120     m_state.host_reg_state[reg] = HostRegState::Usable;
    121     m_host_register_allocation_order[index++] = reg;
    122   }
    123   m_state.available_count = static_cast<u32>(index);
    124 }
    125 
    126 void RegisterCache::SetCallerSavedHostRegs(std::initializer_list<HostReg> regs)
    127 {
    128   for (HostReg reg : regs)
    129     m_state.host_reg_state[reg] |= HostRegState::CallerSaved;
    130 }
    131 
    132 void RegisterCache::SetCalleeSavedHostRegs(std::initializer_list<HostReg> regs)
    133 {
    134   for (HostReg reg : regs)
    135     m_state.host_reg_state[reg] |= HostRegState::CalleeSaved;
    136 }
    137 
    138 void RegisterCache::SetCPUPtrHostReg(HostReg reg)
    139 {
    140   m_cpu_ptr_host_register = reg;
    141 }
    142 
    143 bool RegisterCache::IsUsableHostReg(HostReg reg) const
    144 {
    145   return (m_state.host_reg_state[reg] & HostRegState::Usable) != HostRegState::None;
    146 }
    147 
    148 bool RegisterCache::IsHostRegInUse(HostReg reg) const
    149 {
    150   return (m_state.host_reg_state[reg] & HostRegState::InUse) != HostRegState::None;
    151 }
    152 
    153 bool RegisterCache::HasFreeHostRegister() const
    154 {
    155   for (const HostRegState state : m_state.host_reg_state)
    156   {
    157     if ((state & (HostRegState::Usable | HostRegState::InUse)) == (HostRegState::Usable))
    158       return true;
    159   }
    160 
    161   return false;
    162 }
    163 
    164 u32 RegisterCache::GetUsedHostRegisters() const
    165 {
    166   u32 count = 0;
    167   for (const HostRegState state : m_state.host_reg_state)
    168   {
    169     if ((state & (HostRegState::Usable | HostRegState::InUse)) == (HostRegState::Usable | HostRegState::InUse))
    170       count++;
    171   }
    172 
    173   return count;
    174 }
    175 
    176 u32 RegisterCache::GetFreeHostRegisters() const
    177 {
    178   u32 count = 0;
    179   for (const HostRegState state : m_state.host_reg_state)
    180   {
    181     if ((state & (HostRegState::Usable | HostRegState::InUse)) == (HostRegState::Usable))
    182       count++;
    183   }
    184 
    185   return count;
    186 }
    187 
    188 HostReg RegisterCache::AllocateHostReg(HostRegState state /* = HostRegState::InUse */)
    189 {
    190   if (m_state.allocator_inhibit_count > 0)
    191     Panic("Allocating when inhibited");
    192 
    193   // try for a free register in allocation order
    194   for (u32 i = 0; i < m_state.available_count; i++)
    195   {
    196     const HostReg reg = m_host_register_allocation_order[i];
    197     if ((m_state.host_reg_state[reg] & (HostRegState::Usable | HostRegState::InUse)) == HostRegState::Usable)
    198     {
    199       if (AllocateHostReg(reg, state))
    200         return reg;
    201     }
    202   }
    203 
    204   // evict one of the cached guest registers
    205   if (!EvictOneGuestRegister())
    206     Panic("Failed to evict guest register for new allocation");
    207 
    208   return AllocateHostReg(state);
    209 }
    210 
    211 bool RegisterCache::AllocateHostReg(HostReg reg, HostRegState state /*= HostRegState::InUse*/)
    212 {
    213   if ((m_state.host_reg_state[reg] & HostRegState::InUse) == HostRegState::InUse)
    214     return false;
    215 
    216   m_state.host_reg_state[reg] |= state;
    217 
    218   if ((m_state.host_reg_state[reg] & (HostRegState::CalleeSaved | HostRegState::CalleeSavedAllocated)) ==
    219       HostRegState::CalleeSaved)
    220   {
    221     // new register we need to save..
    222     DebugAssert(m_state.callee_saved_order_count < HostReg_Count);
    223     m_code_generator.EmitPushHostReg(reg, GetActiveCalleeSavedRegisterCount());
    224     m_state.callee_saved_order[m_state.callee_saved_order_count++] = reg;
    225     m_state.host_reg_state[reg] |= HostRegState::CalleeSavedAllocated;
    226   }
    227 
    228   return reg;
    229 }
    230 
    231 void RegisterCache::DiscardHostReg(HostReg reg)
    232 {
    233   DebugAssert(IsHostRegInUse(reg));
    234   DEBUG_LOG("Discarding host register {}", m_code_generator.GetHostRegName(reg));
    235   m_state.host_reg_state[reg] |= HostRegState::Discarded;
    236 }
    237 
    238 void RegisterCache::UndiscardHostReg(HostReg reg)
    239 {
    240   DebugAssert(IsHostRegInUse(reg));
    241   DEBUG_LOG("Undiscarding host register {}", m_code_generator.GetHostRegName(reg));
    242   m_state.host_reg_state[reg] &= ~HostRegState::Discarded;
    243 }
    244 
    245 void RegisterCache::FreeHostReg(HostReg reg)
    246 {
    247   DebugAssert(IsHostRegInUse(reg));
    248   DEBUG_LOG("Freeing host register {}", m_code_generator.GetHostRegName(reg));
    249   m_state.host_reg_state[reg] &= ~HostRegState::InUse;
    250 }
    251 
    252 void RegisterCache::EnsureHostRegFree(HostReg reg)
    253 {
    254   if (!IsHostRegInUse(reg))
    255     return;
    256 
    257   for (u8 i = 0; i < static_cast<u8>(Reg::count); i++)
    258   {
    259     if (m_state.guest_reg_state[i].IsInHostRegister() && m_state.guest_reg_state[i].GetHostRegister() == reg)
    260       FlushGuestRegister(static_cast<Reg>(i), true, true);
    261   }
    262 }
    263 
    264 Value RegisterCache::GetCPUPtr()
    265 {
    266   return Value::FromHostReg(this, m_cpu_ptr_host_register, HostPointerSize);
    267 }
    268 
    269 Value RegisterCache::AllocateScratch(RegSize size, HostReg reg /* = HostReg_Invalid */)
    270 {
    271   if (reg == HostReg_Invalid)
    272   {
    273     reg = AllocateHostReg();
    274   }
    275   else
    276   {
    277     Assert(!IsHostRegInUse(reg));
    278     if (!AllocateHostReg(reg))
    279       Panic("Failed to allocate specific host register");
    280   }
    281 
    282   DEBUG_LOG("Allocating host register {} as scratch", m_code_generator.GetHostRegName(reg));
    283   return Value::FromScratch(this, reg, size);
    284 }
    285 
    286 void RegisterCache::ReserveCallerSavedRegisters()
    287 {
    288   for (u32 reg = 0; reg < HostReg_Count; reg++)
    289   {
    290     if ((m_state.host_reg_state[reg] & (HostRegState::CalleeSaved | HostRegState::CalleeSavedAllocated)) ==
    291         HostRegState::CalleeSaved)
    292     {
    293       DebugAssert(m_state.callee_saved_order_count < HostReg_Count);
    294       m_code_generator.EmitPushHostReg(static_cast<HostReg>(reg), GetActiveCalleeSavedRegisterCount());
    295       m_state.callee_saved_order[m_state.callee_saved_order_count++] = static_cast<HostReg>(reg);
    296       m_state.host_reg_state[reg] |= HostRegState::CalleeSavedAllocated;
    297     }
    298   }
    299 }
    300 
    301 u32 RegisterCache::PushCallerSavedRegisters() const
    302 {
    303   u32 position = GetActiveCalleeSavedRegisterCount();
    304   u32 count = 0;
    305   for (u32 i = 0; i < HostReg_Count; i++)
    306   {
    307     if ((m_state.host_reg_state[i] & (HostRegState::CallerSaved | HostRegState::InUse | HostRegState::Discarded)) ==
    308         (HostRegState::CallerSaved | HostRegState::InUse))
    309     {
    310       m_code_generator.EmitPushHostReg(static_cast<HostReg>(i), position + count);
    311       count++;
    312     }
    313   }
    314 
    315   return count;
    316 }
    317 
    318 u32 RegisterCache::PopCallerSavedRegisters() const
    319 {
    320   u32 count = 0;
    321   for (u32 i = 0; i < HostReg_Count; i++)
    322   {
    323     if ((m_state.host_reg_state[i] & (HostRegState::CallerSaved | HostRegState::InUse | HostRegState::Discarded)) ==
    324         (HostRegState::CallerSaved | HostRegState::InUse))
    325     {
    326       count++;
    327     }
    328   }
    329   if (count == 0)
    330     return 0;
    331 
    332   u32 position = GetActiveCalleeSavedRegisterCount() + count - 1;
    333   u32 i = (HostReg_Count - 1);
    334   do
    335   {
    336     if ((m_state.host_reg_state[i] & (HostRegState::CallerSaved | HostRegState::InUse | HostRegState::Discarded)) ==
    337         (HostRegState::CallerSaved | HostRegState::InUse))
    338     {
    339       u32 reg_pair;
    340       for (reg_pair = (i - 1); reg_pair > 0 && reg_pair < HostReg_Count; reg_pair--)
    341       {
    342         if ((m_state.host_reg_state[reg_pair] &
    343              (HostRegState::CallerSaved | HostRegState::InUse | HostRegState::Discarded)) ==
    344             (HostRegState::CallerSaved | HostRegState::InUse))
    345         {
    346           m_code_generator.EmitPopHostRegPair(static_cast<HostReg>(reg_pair), static_cast<HostReg>(i), position);
    347           position -= 2;
    348           i = reg_pair;
    349           break;
    350         }
    351       }
    352 
    353       if (reg_pair == 0)
    354       {
    355         m_code_generator.EmitPopHostReg(static_cast<HostReg>(i), position);
    356         position--;
    357       }
    358     }
    359     i--;
    360   } while (i > 0);
    361   return count;
    362 }
    363 
    364 u32 RegisterCache::PopCalleeSavedRegisters(bool commit)
    365 {
    366   if (m_state.callee_saved_order_count == 0)
    367     return 0;
    368 
    369   u32 count = 0;
    370   u32 i = m_state.callee_saved_order_count;
    371   do
    372   {
    373     const HostReg reg = m_state.callee_saved_order[i - 1];
    374     DebugAssert((m_state.host_reg_state[reg] & (HostRegState::CalleeSaved | HostRegState::CalleeSavedAllocated)) ==
    375                 (HostRegState::CalleeSaved | HostRegState::CalleeSavedAllocated));
    376 
    377     if (i > 1)
    378     {
    379       const HostReg reg2 = m_state.callee_saved_order[i - 2];
    380       DebugAssert((m_state.host_reg_state[reg2] & (HostRegState::CalleeSaved | HostRegState::CalleeSavedAllocated)) ==
    381                   (HostRegState::CalleeSaved | HostRegState::CalleeSavedAllocated));
    382 
    383       m_code_generator.EmitPopHostRegPair(reg2, reg, i - 1);
    384       i -= 2;
    385       count += 2;
    386 
    387       if (commit)
    388       {
    389         m_state.host_reg_state[reg] &= ~HostRegState::CalleeSavedAllocated;
    390         m_state.host_reg_state[reg2] &= ~HostRegState::CalleeSavedAllocated;
    391       }
    392     }
    393     else
    394     {
    395       m_code_generator.EmitPopHostReg(reg, i - 1);
    396       if (commit)
    397         m_state.host_reg_state[reg] &= ~HostRegState::CalleeSavedAllocated;
    398       count++;
    399       i--;
    400     }
    401   } while (i > 0);
    402   if (commit)
    403     m_state.callee_saved_order_count = 0;
    404 
    405   return count;
    406 }
    407 
    408 void RegisterCache::ReserveCalleeSavedRegisters()
    409 {
    410   for (u32 reg = 0; reg < HostReg_Count; reg++)
    411   {
    412     if ((m_state.host_reg_state[reg] & (HostRegState::CalleeSaved | HostRegState::CalleeSavedAllocated)) ==
    413         HostRegState::CalleeSaved)
    414     {
    415       DebugAssert(m_state.callee_saved_order_count < HostReg_Count);
    416 
    417       // can we find a paired register? (mainly for ARM)
    418       u32 reg_pair;
    419       for (reg_pair = reg + 1; reg_pair < HostReg_Count; reg_pair++)
    420       {
    421         if ((m_state.host_reg_state[reg_pair] & (HostRegState::CalleeSaved | HostRegState::CalleeSavedAllocated)) ==
    422             HostRegState::CalleeSaved)
    423         {
    424           m_code_generator.EmitPushHostRegPair(static_cast<HostReg>(reg), static_cast<HostReg>(reg_pair),
    425                                                GetActiveCalleeSavedRegisterCount());
    426 
    427           m_state.callee_saved_order[m_state.callee_saved_order_count++] = static_cast<HostReg>(reg);
    428           m_state.host_reg_state[reg] |= HostRegState::CalleeSavedAllocated;
    429           m_state.callee_saved_order[m_state.callee_saved_order_count++] = static_cast<HostReg>(reg_pair);
    430           m_state.host_reg_state[reg_pair] |= HostRegState::CalleeSavedAllocated;
    431           reg = reg_pair;
    432           break;
    433         }
    434       }
    435 
    436       if (reg_pair == HostReg_Count)
    437       {
    438         m_code_generator.EmitPushHostReg(static_cast<HostReg>(reg), GetActiveCalleeSavedRegisterCount());
    439         m_state.callee_saved_order[m_state.callee_saved_order_count++] = static_cast<HostReg>(reg);
    440         m_state.host_reg_state[reg] |= HostRegState::CalleeSavedAllocated;
    441       }
    442     }
    443   }
    444 }
    445 
    446 void RegisterCache::AssumeCalleeSavedRegistersAreSaved()
    447 {
    448   for (u32 i = 0; i < HostReg_Count; i++)
    449   {
    450     if ((m_state.host_reg_state[i] & (HostRegState::CalleeSaved | HostRegState::CalleeSavedAllocated)) ==
    451         HostRegState::CalleeSaved)
    452     {
    453       m_state.host_reg_state[i] &= ~HostRegState::CalleeSaved;
    454     }
    455   }
    456 }
    457 
    458 void RegisterCache::PushState()
    459 {
    460   // need to copy this manually because of the load delay values
    461   RegAllocState save_state;
    462   save_state.host_reg_state = m_state.host_reg_state;
    463   save_state.callee_saved_order = m_state.callee_saved_order;
    464   save_state.guest_reg_state = m_state.guest_reg_state;
    465   save_state.guest_reg_order = m_state.guest_reg_order;
    466   save_state.available_count = m_state.available_count;
    467   save_state.callee_saved_order_count = m_state.callee_saved_order_count;
    468   save_state.guest_reg_order_count = m_state.guest_reg_order_count;
    469   save_state.allocator_inhibit_count = m_state.allocator_inhibit_count;
    470   save_state.load_delay_register = m_state.load_delay_register;
    471   save_state.load_delay_value.regcache = m_state.load_delay_value.regcache;
    472   save_state.load_delay_value.host_reg = m_state.load_delay_value.host_reg;
    473   save_state.load_delay_value.size = m_state.load_delay_value.size;
    474   save_state.load_delay_value.flags = m_state.load_delay_value.flags;
    475   save_state.next_load_delay_register = m_state.next_load_delay_register;
    476   save_state.next_load_delay_value.regcache = m_state.next_load_delay_value.regcache;
    477   save_state.next_load_delay_value.host_reg = m_state.next_load_delay_value.host_reg;
    478   save_state.next_load_delay_value.size = m_state.next_load_delay_value.size;
    479   save_state.next_load_delay_value.flags = m_state.next_load_delay_value.flags;
    480   m_state_stack.push(std::move(save_state));
    481 }
    482 
    483 void RegisterCache::PopState()
    484 {
    485   Assert(!m_state_stack.empty());
    486 
    487   // prevent destructor -> freeing of host reg
    488   m_state.load_delay_value.Clear();
    489   m_state.next_load_delay_value.Clear();
    490 
    491   m_state = std::move(m_state_stack.top());
    492   m_state_stack.pop();
    493 }
    494 
    495 Value RegisterCache::ReadGuestRegister(Reg guest_reg, bool cache /* = true */, bool force_host_register /* = false */,
    496                                        HostReg forced_host_reg /* = HostReg_Invalid */)
    497 {
    498   // register zero is always zero
    499   if (guest_reg == Reg::zero)
    500   {
    501     // return a scratch value of zero if it's forced
    502     if (force_host_register)
    503     {
    504       Value temp = AllocateScratch(RegSize_32, forced_host_reg);
    505       m_code_generator.EmitXor(temp.host_reg, temp.host_reg, temp);
    506       return temp;
    507     }
    508 
    509     return Value::FromConstantU32(0);
    510   }
    511 
    512   Value& cache_value = m_state.guest_reg_state[static_cast<u8>(guest_reg)];
    513   if (cache_value.IsValid())
    514   {
    515     if (cache_value.IsInHostRegister())
    516     {
    517       PushRegisterToOrder(guest_reg);
    518 
    519       // if it's in the wrong register, return it as scratch
    520       if (forced_host_reg == HostReg_Invalid || cache_value.GetHostRegister() == forced_host_reg)
    521         return cache_value;
    522 
    523       Value temp = AllocateScratch(RegSize_32, forced_host_reg);
    524       m_code_generator.EmitCopyValue(forced_host_reg, cache_value);
    525       return temp;
    526     }
    527     else if (force_host_register)
    528     {
    529       // if it's not in a register, it should be constant
    530       DebugAssert(cache_value.IsConstant());
    531 
    532       HostReg host_reg;
    533       if (forced_host_reg == HostReg_Invalid)
    534       {
    535         host_reg = AllocateHostReg();
    536       }
    537       else
    538       {
    539         Assert(!IsHostRegInUse(forced_host_reg));
    540         if (!AllocateHostReg(forced_host_reg))
    541           Panic("Failed to allocate specific host register");
    542         host_reg = forced_host_reg;
    543       }
    544 
    545       DEBUG_LOG("Allocated host register {} for constant guest register {} (0x{:X})",
    546                 m_code_generator.GetHostRegName(host_reg), GetRegName(guest_reg), cache_value.constant_value);
    547 
    548       m_code_generator.EmitCopyValue(host_reg, cache_value);
    549       cache_value.AddHostReg(this, host_reg);
    550       AppendRegisterToOrder(guest_reg);
    551 
    552       // if we're forcing a host register, we're probably going to be changing the value,
    553       // in which case the constant won't be correct anyway. so just drop it.
    554       cache_value.ClearConstant();
    555       return cache_value;
    556     }
    557     else
    558     {
    559       // constant
    560       return cache_value;
    561     }
    562   }
    563 
    564   HostReg host_reg;
    565   if (forced_host_reg == HostReg_Invalid)
    566   {
    567     host_reg = AllocateHostReg();
    568   }
    569   else
    570   {
    571     Assert(!IsHostRegInUse(forced_host_reg));
    572     if (!AllocateHostReg(forced_host_reg))
    573       Panic("Failed to allocate specific host register");
    574     host_reg = forced_host_reg;
    575   }
    576 
    577   m_code_generator.EmitLoadGuestRegister(host_reg, guest_reg);
    578 
    579   DEBUG_LOG("Loading guest register {} to host register {}{}", GetRegName(guest_reg),
    580             m_code_generator.GetHostRegName(host_reg, RegSize_32), cache ? " (cached)" : "");
    581 
    582   if (cache)
    583   {
    584     // Now in cache.
    585     cache_value.SetHostReg(this, host_reg, RegSize_32);
    586     AppendRegisterToOrder(guest_reg);
    587     return cache_value;
    588   }
    589   else
    590   {
    591     // Skip caching, return the register as a value.
    592     return Value::FromScratch(this, host_reg, RegSize_32);
    593   }
    594 }
    595 
    596 Value RegisterCache::ReadGuestRegisterToScratch(Reg guest_reg)
    597 {
    598   HostReg host_reg = AllocateHostReg();
    599 
    600   Value& cache_value = m_state.guest_reg_state[static_cast<u8>(guest_reg)];
    601   if (cache_value.IsValid())
    602   {
    603     m_code_generator.EmitCopyValue(host_reg, cache_value);
    604 
    605     if (cache_value.IsConstant())
    606     {
    607       DEBUG_LOG("Copying guest register {} from constant 0x{:08X} to scratch host register {}", GetRegName(guest_reg),
    608                 static_cast<u32>(cache_value.constant_value), m_code_generator.GetHostRegName(host_reg, RegSize_32));
    609     }
    610     else
    611     {
    612       DEBUG_LOG("Copying guest register {} from {} to scratch host register {}", GetRegName(guest_reg),
    613                 m_code_generator.GetHostRegName(cache_value.host_reg, RegSize_32),
    614                 m_code_generator.GetHostRegName(host_reg, RegSize_32));
    615     }
    616   }
    617   else
    618   {
    619     m_code_generator.EmitLoadGuestRegister(host_reg, guest_reg);
    620 
    621     DEBUG_LOG("Loading guest register {} to scratch host register {}", GetRegName(guest_reg),
    622               m_code_generator.GetHostRegName(host_reg, RegSize_32));
    623   }
    624 
    625   return Value::FromScratch(this, host_reg, RegSize_32);
    626 }
    627 
    628 Value RegisterCache::WriteGuestRegister(Reg guest_reg, Value&& value)
    629 {
    630   // ignore writes to register zero
    631   DebugAssert(value.size == RegSize_32);
    632   if (guest_reg == Reg::zero)
    633     return std::move(value);
    634 
    635   // cancel any load delay delay
    636   if (m_state.load_delay_register == guest_reg)
    637   {
    638     DEBUG_LOG("Cancelling load delay of register {} because of non-delayed write", GetRegName(guest_reg));
    639     m_state.load_delay_register = Reg::count;
    640     m_state.load_delay_value.ReleaseAndClear();
    641   }
    642 
    643   Value& cache_value = m_state.guest_reg_state[static_cast<u8>(guest_reg)];
    644   if (cache_value.IsInHostRegister() && value.IsInHostRegister() && cache_value.host_reg == value.host_reg)
    645   {
    646     // updating the register value.
    647     DEBUG_LOG("Updating guest register {} (in host register {})", GetRegName(guest_reg),
    648               m_code_generator.GetHostRegName(value.host_reg, RegSize_32));
    649     cache_value = std::move(value);
    650     cache_value.SetDirty();
    651     return cache_value;
    652   }
    653 
    654   InvalidateGuestRegister(guest_reg);
    655   DebugAssert(!cache_value.IsValid());
    656 
    657   if (value.IsConstant())
    658   {
    659     // No need to allocate a host register, and we can defer the store.
    660     cache_value = value;
    661     cache_value.SetDirty();
    662     return cache_value;
    663   }
    664 
    665   AppendRegisterToOrder(guest_reg);
    666 
    667   // If it's a temporary, we can bind that to the guest register.
    668   if (value.IsScratch())
    669   {
    670     DEBUG_LOG("Binding scratch register {} to guest register {}",
    671               m_code_generator.GetHostRegName(value.host_reg, RegSize_32), GetRegName(guest_reg));
    672 
    673     cache_value = std::move(value);
    674     cache_value.flags &= ~ValueFlags::Scratch;
    675     cache_value.SetDirty();
    676     return Value::FromHostReg(this, cache_value.host_reg, RegSize_32);
    677   }
    678 
    679   // Allocate host register, and copy value to it.
    680   HostReg host_reg = AllocateHostReg();
    681   m_code_generator.EmitCopyValue(host_reg, value);
    682   cache_value.SetHostReg(this, host_reg, RegSize_32);
    683   cache_value.SetDirty();
    684 
    685   DEBUG_LOG("Copying non-scratch register {} to {} to guest register {}",
    686             m_code_generator.GetHostRegName(value.host_reg, RegSize_32),
    687             m_code_generator.GetHostRegName(host_reg, RegSize_32), GetRegName(guest_reg));
    688 
    689   return Value::FromHostReg(this, cache_value.host_reg, RegSize_32);
    690 }
    691 
    692 void RegisterCache::WriteGuestRegisterDelayed(Reg guest_reg, Value&& value)
    693 {
    694   // ignore writes to register zero
    695   DebugAssert(value.size == RegSize_32);
    696   if (guest_reg == Reg::zero)
    697     return;
    698 
    699   // two load delays in a row? cancel the first one.
    700   if (guest_reg == m_state.load_delay_register)
    701   {
    702     DEBUG_LOG("Cancelling load delay of register {} due to new load delay", GetRegName(guest_reg));
    703     m_state.load_delay_register = Reg::count;
    704     m_state.load_delay_value.ReleaseAndClear();
    705   }
    706 
    707   // two load delay case with interpreter load delay
    708   m_code_generator.EmitCancelInterpreterLoadDelayForReg(guest_reg);
    709 
    710   // set up the load delay at the end of this instruction
    711   Value& cache_value = m_state.next_load_delay_value;
    712   Assert(m_state.next_load_delay_register == Reg::count);
    713   m_state.next_load_delay_register = guest_reg;
    714 
    715   // If it's a temporary, we can bind that to the guest register.
    716   if (value.IsScratch())
    717   {
    718     DEBUG_LOG("Binding scratch register {} to load-delayed guest register {}",
    719               m_code_generator.GetHostRegName(value.host_reg, RegSize_32), GetRegName(guest_reg));
    720 
    721     cache_value = std::move(value);
    722     return;
    723   }
    724 
    725   // Allocate host register, and copy value to it.
    726   cache_value = AllocateScratch(RegSize_32);
    727   m_code_generator.EmitCopyValue(cache_value.host_reg, value);
    728 
    729   DEBUG_LOG("Copying non-scratch register {} to {} to load-delayed guest register {}",
    730             m_code_generator.GetHostRegName(value.host_reg, RegSize_32),
    731             m_code_generator.GetHostRegName(cache_value.host_reg, RegSize_32), GetRegName(guest_reg));
    732 }
    733 
    734 void RegisterCache::UpdateLoadDelay()
    735 {
    736   // flush current load delay
    737   if (m_state.load_delay_register != Reg::count)
    738   {
    739     // have to clear first because otherwise it'll release the value
    740     Reg reg = m_state.load_delay_register;
    741     Value value = std::move(m_state.load_delay_value);
    742     m_state.load_delay_register = Reg::count;
    743     WriteGuestRegister(reg, std::move(value));
    744   }
    745 
    746   // next load delay -> load delay
    747   if (m_state.next_load_delay_register != Reg::count)
    748   {
    749     m_state.load_delay_register = m_state.next_load_delay_register;
    750     m_state.load_delay_value = std::move(m_state.next_load_delay_value);
    751     m_state.next_load_delay_register = Reg::count;
    752   }
    753 }
    754 
    755 void RegisterCache::CancelLoadDelay()
    756 {
    757   if (m_state.load_delay_register == Reg::count)
    758     return;
    759 
    760   DEBUG_LOG("Cancelling load delay of register {}", GetRegName(m_state.load_delay_register));
    761   m_state.load_delay_register = Reg::count;
    762   m_state.load_delay_value.ReleaseAndClear();
    763 }
    764 
    765 void RegisterCache::WriteLoadDelayToCPU(bool clear)
    766 {
    767   // There shouldn't be a flush at the same time as there's a new load delay.
    768   Assert(m_state.next_load_delay_register == Reg::count);
    769   if (m_state.load_delay_register != Reg::count)
    770   {
    771     DEBUG_LOG("Flushing pending load delay of {}", GetRegName(m_state.load_delay_register));
    772     m_code_generator.EmitStoreInterpreterLoadDelay(m_state.load_delay_register, m_state.load_delay_value);
    773     if (clear)
    774     {
    775       m_state.load_delay_register = Reg::count;
    776       m_state.load_delay_value.ReleaseAndClear();
    777     }
    778   }
    779 }
    780 
    781 void RegisterCache::FlushLoadDelay(bool clear)
    782 {
    783   Assert(m_state.next_load_delay_register == Reg::count);
    784 
    785   if (m_state.load_delay_register != Reg::count)
    786   {
    787     // if this is an exception exit, write the new value to the CPU register file, but keep it tracked for the next
    788     // non-exception-raised path. TODO: push/pop whole state would avoid this issue
    789     m_code_generator.EmitStoreGuestRegister(m_state.load_delay_register, m_state.load_delay_value);
    790 
    791     if (clear)
    792     {
    793       m_state.load_delay_register = Reg::count;
    794       m_state.load_delay_value.ReleaseAndClear();
    795     }
    796   }
    797 }
    798 
    799 void RegisterCache::FlushGuestRegister(Reg guest_reg, bool invalidate, bool clear_dirty)
    800 {
    801   Value& cache_value = m_state.guest_reg_state[static_cast<u8>(guest_reg)];
    802   if (cache_value.IsDirty())
    803   {
    804     if (cache_value.IsInHostRegister())
    805     {
    806       DEBUG_LOG("Flushing guest register {} from host register {}", GetRegName(guest_reg),
    807                 m_code_generator.GetHostRegName(cache_value.host_reg, RegSize_32));
    808     }
    809     else if (cache_value.IsConstant())
    810     {
    811       DEBUG_LOG("Flushing guest register {} from constant 0x{:X}", GetRegName(guest_reg), cache_value.constant_value);
    812     }
    813     m_code_generator.EmitStoreGuestRegister(guest_reg, cache_value);
    814     if (clear_dirty)
    815       cache_value.ClearDirty();
    816   }
    817 
    818   if (invalidate)
    819     InvalidateGuestRegister(guest_reg);
    820 }
    821 
    822 void RegisterCache::InvalidateGuestRegister(Reg guest_reg)
    823 {
    824   Value& cache_value = m_state.guest_reg_state[static_cast<u8>(guest_reg)];
    825   if (!cache_value.IsValid())
    826     return;
    827 
    828   if (cache_value.IsInHostRegister())
    829   {
    830     FreeHostReg(cache_value.host_reg);
    831     ClearRegisterFromOrder(guest_reg);
    832   }
    833 
    834   DEBUG_LOG("Invalidating guest register {}", GetRegName(guest_reg));
    835   cache_value.Clear();
    836 }
    837 
    838 void RegisterCache::InvalidateAllNonDirtyGuestRegisters()
    839 {
    840   for (u8 reg = 0; reg < static_cast<u8>(Reg::count); reg++)
    841   {
    842     Value& cache_value = m_state.guest_reg_state[reg];
    843     if (cache_value.IsValid() && !cache_value.IsDirty())
    844       InvalidateGuestRegister(static_cast<Reg>(reg));
    845   }
    846 }
    847 
    848 void RegisterCache::FlushAllGuestRegisters(bool invalidate, bool clear_dirty)
    849 {
    850   for (u8 reg = 0; reg < static_cast<u8>(Reg::count); reg++)
    851     FlushGuestRegister(static_cast<Reg>(reg), invalidate, clear_dirty);
    852 }
    853 
    854 void RegisterCache::FlushCallerSavedGuestRegisters(bool invalidate, bool clear_dirty)
    855 {
    856   for (u8 reg = 0; reg < static_cast<u8>(Reg::count); reg++)
    857   {
    858     const Value& gr = m_state.guest_reg_state[reg];
    859     if (!gr.IsInHostRegister() ||
    860         (m_state.host_reg_state[gr.GetHostRegister()] & HostRegState::CallerSaved) != HostRegState::CallerSaved)
    861     {
    862       continue;
    863     }
    864 
    865     FlushGuestRegister(static_cast<Reg>(reg), invalidate, clear_dirty);
    866   }
    867 }
    868 
    869 bool RegisterCache::EvictOneGuestRegister()
    870 {
    871   if (m_state.guest_reg_order_count == 0)
    872     return false;
    873 
    874   // evict the register used the longest time ago
    875   Reg evict_reg = m_state.guest_reg_order[m_state.guest_reg_order_count - 1];
    876   DEBUG_LOG("Evicting guest register {}", GetRegName(evict_reg));
    877   FlushGuestRegister(evict_reg, true, true);
    878 
    879   return HasFreeHostRegister();
    880 }
    881 
    882 void RegisterCache::ClearRegisterFromOrder(Reg reg)
    883 {
    884   for (u32 i = 0; i < m_state.guest_reg_order_count; i++)
    885   {
    886     if (m_state.guest_reg_order[i] == reg)
    887     {
    888       // move the registers after backwards into this spot
    889       const u32 count_after = m_state.guest_reg_order_count - i - 1;
    890       if (count_after > 0)
    891         std::memmove(&m_state.guest_reg_order[i], &m_state.guest_reg_order[i + 1], sizeof(Reg) * count_after);
    892       else
    893         m_state.guest_reg_order[i] = Reg::count;
    894 
    895       m_state.guest_reg_order_count--;
    896       return;
    897     }
    898   }
    899 
    900   Panic("Clearing register from order not in order");
    901 }
    902 
    903 void RegisterCache::PushRegisterToOrder(Reg reg)
    904 {
    905   for (u32 i = 0; i < m_state.guest_reg_order_count; i++)
    906   {
    907     if (m_state.guest_reg_order[i] == reg)
    908     {
    909       // move the registers after backwards into this spot
    910       const u32 count_before = i;
    911       if (count_before > 0)
    912         std::memmove(&m_state.guest_reg_order[1], &m_state.guest_reg_order[0], sizeof(Reg) * count_before);
    913 
    914       m_state.guest_reg_order[0] = reg;
    915       return;
    916     }
    917   }
    918 
    919   Panic("Attempt to push register which is not ordered");
    920 }
    921 
    922 void RegisterCache::AppendRegisterToOrder(Reg reg)
    923 {
    924   DebugAssert(m_state.guest_reg_order_count < HostReg_Count);
    925   if (m_state.guest_reg_order_count > 0)
    926     std::memmove(&m_state.guest_reg_order[1], &m_state.guest_reg_order[0], sizeof(Reg) * m_state.guest_reg_order_count);
    927   m_state.guest_reg_order[0] = reg;
    928   m_state.guest_reg_order_count++;
    929 }
    930 
    931 void RegisterCache::InhibitAllocation()
    932 {
    933   m_state.allocator_inhibit_count++;
    934 }
    935 
    936 void RegisterCache::UninhibitAllocation()
    937 {
    938   Assert(m_state.allocator_inhibit_count > 0);
    939   m_state.allocator_inhibit_count--;
    940 }
    941 
    942 } // namespace CPU::Recompiler