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, >e_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(>e_done); 247 EmitStoreCPUStructField(OFFSETOF(State, pending_ticks), pending_ticks); 248 } 249 250 #endif 251 252 } // namespace CPU::Recompiler