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