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_generic.cpp (8664B)


      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 "common/log.h"
      5 #include "cpu_core.h"
      6 #include "cpu_core_private.h"
      7 #include "cpu_recompiler_code_generator.h"
      8 #include "settings.h"
      9 Log_SetChannel(Recompiler::CodeGenerator);
     10 
     11 namespace CPU::Recompiler {
     12 
     13 void CodeGenerator::EmitLoadGuestRegister(HostReg host_reg, Reg guest_reg)
     14 {
     15   EmitLoadCPUStructField(host_reg, RegSize_32, State::GPRRegisterOffset(static_cast<u32>(guest_reg)));
     16 }
     17 
     18 void CodeGenerator::EmitStoreGuestRegister(Reg guest_reg, const Value& value)
     19 {
     20   DebugAssert(value.size == RegSize_32);
     21   EmitStoreCPUStructField(State::GPRRegisterOffset(static_cast<u32>(guest_reg)), value);
     22 }
     23 
     24 void CodeGenerator::EmitStoreInterpreterLoadDelay(Reg reg, const Value& value)
     25 {
     26   DebugAssert(value.size == RegSize_32 && value.IsInHostRegister());
     27   EmitStoreCPUStructField(OFFSETOF(State, load_delay_reg), Value::FromConstantU8(static_cast<u8>(reg)));
     28   EmitStoreCPUStructField(OFFSETOF(State, load_delay_value), value);
     29   m_load_delay_dirty = true;
     30 }
     31 
     32 Value CodeGenerator::EmitLoadGuestMemory(Instruction instruction, const CodeCache::InstructionInfo& info,
     33                                          const Value& address, const SpeculativeValue& address_spec, RegSize size)
     34 {
     35   if (address.IsConstant() && !SpeculativeIsCacheIsolated())
     36   {
     37     TickCount read_ticks;
     38     void* ptr = GetDirectReadMemoryPointer(
     39       static_cast<u32>(address.constant_value),
     40       (size == RegSize_8) ? MemoryAccessSize::Byte :
     41                             ((size == RegSize_16) ? MemoryAccessSize::HalfWord : MemoryAccessSize::Word),
     42       &read_ticks);
     43     if (ptr)
     44     {
     45       Value result = m_register_cache.AllocateScratch(size);
     46 
     47       // TODO: mask off...
     48       if (CodeCache::IsUsingFastmem() && Bus::IsRAMAddress(static_cast<u32>(address.constant_value)))
     49       {
     50         // have to mask away the high bits for mirrors, since we don't map them in fastmem
     51         EmitLoadGuestRAMFastmem(Value::FromConstantU32(static_cast<u32>(address.constant_value) & Bus::g_ram_mask),
     52                                 size, result);
     53       }
     54       else
     55       {
     56         EmitLoadGlobal(result.GetHostRegister(), size, ptr);
     57       }
     58 
     59       m_delayed_cycles_add += read_ticks;
     60       return result;
     61     }
     62   }
     63 
     64   Value result = m_register_cache.AllocateScratch(HostPointerSize);
     65 
     66   const bool use_fastmem = !g_settings.cpu_recompiler_memory_exceptions &&
     67                            (address_spec ? Bus::CanUseFastmemForAddress(*address_spec) : true) &&
     68                            !SpeculativeIsCacheIsolated();
     69   if (address_spec)
     70   {
     71     if (!use_fastmem)
     72     {
     73       DEBUG_LOG("Non-constant load at 0x{:08X}, speculative address 0x{:08X}, using fastmem = {}", info.pc,
     74                 *address_spec, use_fastmem ? "yes" : "no");
     75     }
     76   }
     77   else
     78   {
     79     DEBUG_LOG("Non-constant load at 0x{:08X}, speculative address UNKNOWN, using fastmem = {}", info.pc,
     80               use_fastmem ? "yes" : "no");
     81   }
     82 
     83   if (CodeCache::IsUsingFastmem() && use_fastmem)
     84   {
     85     EmitLoadGuestMemoryFastmem(instruction, info, address, size, result);
     86   }
     87   else
     88   {
     89     AddPendingCycles(true);
     90     m_register_cache.FlushCallerSavedGuestRegisters(true, true);
     91     EmitLoadGuestMemorySlowmem(instruction, info, address, size, result, false);
     92   }
     93 
     94   // Downcast to ignore upper 56/48/32 bits. This should be a noop.
     95   if (result.size != size)
     96   {
     97     switch (size)
     98     {
     99       case RegSize_8:
    100         ConvertValueSizeInPlace(&result, RegSize_8, false);
    101         break;
    102 
    103       case RegSize_16:
    104         ConvertValueSizeInPlace(&result, RegSize_16, false);
    105         break;
    106 
    107       case RegSize_32:
    108         ConvertValueSizeInPlace(&result, RegSize_32, false);
    109         break;
    110 
    111       default:
    112         UnreachableCode();
    113         break;
    114     }
    115   }
    116 
    117   return result;
    118 }
    119 
    120 void CodeGenerator::EmitStoreGuestMemory(Instruction instruction, const CodeCache::InstructionInfo& info,
    121                                          const Value& address, const SpeculativeValue& address_spec, RegSize size,
    122                                          const Value& value)
    123 {
    124   if (address.IsConstant() && !SpeculativeIsCacheIsolated())
    125   {
    126     void* ptr = GetDirectWriteMemoryPointer(
    127       static_cast<u32>(address.constant_value),
    128       (size == RegSize_8) ? MemoryAccessSize::Byte :
    129                             ((size == RegSize_16) ? MemoryAccessSize::HalfWord : MemoryAccessSize::Word));
    130     if (ptr)
    131     {
    132       if (value.size != size)
    133         EmitStoreGlobal(ptr, value.ViewAsSize(size));
    134       else
    135         EmitStoreGlobal(ptr, value);
    136 
    137       return;
    138     }
    139   }
    140 
    141   const bool use_fastmem = !g_settings.cpu_recompiler_memory_exceptions &&
    142                            (address_spec ? Bus::CanUseFastmemForAddress(*address_spec) : true) &&
    143                            !SpeculativeIsCacheIsolated();
    144   if (address_spec)
    145   {
    146     if (!use_fastmem)
    147     {
    148       DEBUG_LOG("Non-constant store at 0x{:08X}, speculative address 0x{:08X}, using fastmem = {}", info.pc,
    149                 *address_spec, use_fastmem ? "yes" : "no");
    150     }
    151   }
    152   else
    153   {
    154     DEBUG_LOG("Non-constant store at 0x{:08X}, speculative address UNKNOWN, using fastmem = {}", info.pc,
    155               use_fastmem ? "yes" : "no");
    156   }
    157 
    158   if (CodeCache::IsUsingFastmem() && use_fastmem)
    159   {
    160     EmitStoreGuestMemoryFastmem(instruction, info, address, size, value);
    161   }
    162   else
    163   {
    164     AddPendingCycles(true);
    165     m_register_cache.FlushCallerSavedGuestRegisters(true, true);
    166     EmitStoreGuestMemorySlowmem(instruction, info, address, size, value, false);
    167   }
    168 }
    169 
    170 #if 0 // Not used
    171 
    172 void CodeGenerator::EmitICacheCheckAndUpdate()
    173 {
    174   Value temp = m_register_cache.AllocateScratch(RegSize_32);
    175 
    176   if (GetSegmentForAddress(m_pc) >= Segment::KSEG1)
    177   {
    178     EmitLoadCPUStructField(temp.GetHostRegister(), RegSize_32, OFFSETOF(State, pending_ticks));
    179     EmitAdd(temp.GetHostRegister(), temp.GetHostRegister(),
    180             Value::FromConstantU32(static_cast<u32>(m_block->uncached_fetch_ticks)), false);
    181     EmitStoreCPUStructField(OFFSETOF(State, pending_ticks), temp);
    182   }
    183   else
    184   {
    185     // cached path
    186     Value temp2 = m_register_cache.AllocateScratch(RegSize_32);
    187 
    188     m_register_cache.InhibitAllocation();
    189 
    190     VirtualMemoryAddress current_pc = m_pc & ICACHE_TAG_ADDRESS_MASK;
    191     for (u32 i = 0; i < m_block->icache_line_count; i++, current_pc += ICACHE_LINE_SIZE)
    192     {
    193       const VirtualMemoryAddress tag = GetICacheTagForAddress(current_pc);
    194       const TickCount fill_ticks = GetICacheFillTicks(current_pc);
    195       if (fill_ticks <= 0)
    196         continue;
    197 
    198       const u32 line = GetICacheLine(current_pc);
    199       const u32 offset = OFFSETOF(State, icache_tags) + (line * sizeof(u32));
    200       LabelType cache_hit;
    201 
    202       EmitLoadCPUStructField(temp.GetHostRegister(), RegSize_32, offset);
    203       EmitCopyValue(temp2.GetHostRegister(), Value::FromConstantU32(current_pc));
    204       EmitCmp(temp2.GetHostRegister(), temp);
    205       EmitConditionalBranch(Condition::Equal, false, temp.GetHostRegister(), temp2, &cache_hit);
    206 
    207       EmitLoadCPUStructField(temp.GetHostRegister(), RegSize_32, OFFSETOF(State, pending_ticks));
    208       EmitStoreCPUStructField(offset, temp2);
    209       EmitAdd(temp.GetHostRegister(), temp.GetHostRegister(), Value::FromConstantU32(static_cast<u32>(fill_ticks)),
    210               false);
    211       EmitStoreCPUStructField(OFFSETOF(State, pending_ticks), temp);
    212       EmitBindLabel(&cache_hit);
    213     }
    214 
    215     m_register_cache.UninhibitAllocation();
    216   }
    217 }
    218 
    219 #endif
    220 
    221 #if 0 // Not Used
    222 
    223 void CodeGenerator::EmitStallUntilGTEComplete()
    224 {
    225   Value pending_ticks = m_register_cache.AllocateScratch(RegSize_32);
    226   Value gte_completion_tick = m_register_cache.AllocateScratch(RegSize_32);
    227   EmitLoadCPUStructField(pending_ticks.GetHostRegister(), RegSize_32, OFFSETOF(State, pending_ticks));
    228   EmitLoadCPUStructField(gte_completion_tick.GetHostRegister(), RegSize_32, OFFSETOF(State, gte_completion_tick));
    229 
    230   // commit cycles here, should always be nonzero
    231   if (m_delayed_cycles_add > 0)
    232   {
    233     EmitAdd(pending_ticks.GetHostRegister(), pending_ticks.GetHostRegister(),
    234       Value::FromConstantU32(m_delayed_cycles_add), false);
    235     m_delayed_cycles_add = 0;
    236   }
    237 
    238   LabelType gte_done;
    239   EmitSub(gte_completion_tick.GetHostRegister(), gte_completion_tick.GetHostRegister(), pending_ticks, true);
    240   EmitConditionalBranch(Condition::Below, false, &gte_done);
    241 
    242   // add stall ticks
    243   EmitAdd(pending_ticks.GetHostRegister(), pending_ticks.GetHostRegister(), gte_completion_tick, false);
    244 
    245   // store new ticks
    246   EmitBindLabel(&gte_done);
    247   EmitStoreCPUStructField(OFFSETOF(State, pending_ticks), pending_ticks);
    248 }
    249 
    250 #endif
    251 
    252 } // namespace CPU::Recompiler