diff --git a/Source/Core/Core/PowerPC/JitArm64/Jit.cpp b/Source/Core/Core/PowerPC/JitArm64/Jit.cpp index ea05833a72..4b107870b8 100644 --- a/Source/Core/Core/PowerPC/JitArm64/Jit.cpp +++ b/Source/Core/Core/PowerPC/JitArm64/Jit.cpp @@ -1359,7 +1359,10 @@ bool JitArm64::DoJit(u32 em_address, JitBlock* b, u32 nextPC) m_constant_propagation.Apply(constant_propagation_result); if (constant_propagation_result.gpr >= 0) + { + // Mark the GPR as dirty in the register cache gpr.SetImmediate(constant_propagation_result.gpr, constant_propagation_result.gpr_value); + } if (constant_propagation_result.instruction_fully_executed) { diff --git a/Source/Core/Core/PowerPC/JitArm64/JitArm64_RegCache.cpp b/Source/Core/Core/PowerPC/JitArm64/JitArm64_RegCache.cpp index 53731b0723..ebee3ba3ee 100644 --- a/Source/Core/Core/PowerPC/JitArm64/JitArm64_RegCache.cpp +++ b/Source/Core/Core/PowerPC/JitArm64/JitArm64_RegCache.cpp @@ -115,7 +115,7 @@ void Arm64RegCache::FlushMostStaleRegister() const u32 last_used = reg.GetLastUsed(); if (last_used > most_stale_amount && reg.GetType() != RegType::NotLoaded && - reg.GetType() != RegType::Discarded && reg.GetType() != RegType::Immediate) + reg.GetType() != RegType::Discarded) { most_stale_preg = i; most_stale_amount = last_used; @@ -145,6 +145,18 @@ void Arm64GPRCache::Start(PPCAnalyst::BlockRegStats& stats) { } +// Returns if a register is set as an immediate. Only valid for guest GPRs. +bool Arm64GPRCache::IsImm(size_t preg) const +{ + return m_jit->GetConstantPropagation().HasGPR(preg); +} + +// Gets the immediate that a register is set to. Only valid for guest GPRs. +u32 Arm64GPRCache::GetImm(size_t preg) const +{ + return m_jit->GetConstantPropagation().GetGPR(preg); +} + bool Arm64GPRCache::IsCallerSaved(ARM64Reg reg) const { return ARM64XEmitter::CALLER_SAVED_GPRS[DecodeReg(reg)]; @@ -183,6 +195,7 @@ void Arm64GPRCache::FlushRegister(size_t index, FlushMode mode, ARM64Reg tmp_reg GuestRegInfo guest_reg = GetGuestByIndex(index); OpArg& reg = guest_reg.reg; size_t bitsize = guest_reg.bitsize; + const bool is_gpr = index >= GUEST_GPR_OFFSET && index < GUEST_GPR_OFFSET + GUEST_GPR_COUNT; if (reg.GetType() == RegType::Register) { @@ -196,11 +209,12 @@ void Arm64GPRCache::FlushRegister(size_t index, FlushMode mode, ARM64Reg tmp_reg reg.Flush(); } } - else if (reg.GetType() == RegType::Immediate) + else if (is_gpr && IsImm(index - GUEST_GPR_OFFSET)) { if (reg.IsDirty()) { - if (!reg.GetImm()) + const u32 imm = GetImm(index - GUEST_GPR_OFFSET); + if (imm == 0) { m_emit->STR(IndexType::Unsigned, bitsize == 64 ? ARM64Reg::ZR : ARM64Reg::WZR, PPC_REG, u32(guest_reg.ppc_offset)); @@ -222,7 +236,7 @@ void Arm64GPRCache::FlushRegister(size_t index, FlushMode mode, ARM64Reg tmp_reg const ARM64Reg encoded_tmp_reg = bitsize != 64 ? tmp_reg : EncodeRegTo64(tmp_reg); - m_emit->MOVI2R(encoded_tmp_reg, reg.GetImm()); + m_emit->MOVI2R(encoded_tmp_reg, imm); m_emit->STR(IndexType::Unsigned, encoded_tmp_reg, PPC_REG, u32(guest_reg.ppc_offset)); if (allocated_tmp_reg) @@ -241,10 +255,10 @@ void Arm64GPRCache::FlushRegisters(BitSet32 regs, FlushMode mode, ARM64Reg tmp_r for (auto iter = regs.begin(); iter != regs.end(); ++iter) { const int i = *iter; - ASSERT_MSG(DYNA_REC, ignore_discarded_registers != IgnoreDiscardedRegisters::No || - m_guest_registers[GUEST_GPR_OFFSET + i].GetType() != RegType::Discarded, + m_guest_registers[GUEST_GPR_OFFSET + i].GetType() != RegType::Discarded || + IsImm(i), "Attempted to flush discarded register"); if (i + 1 < int(GUEST_GPR_COUNT) && regs[i + 1]) @@ -252,10 +266,10 @@ void Arm64GPRCache::FlushRegisters(BitSet32 regs, FlushMode mode, ARM64Reg tmp_r // We've got two guest registers in a row to store OpArg& reg1 = m_guest_registers[GUEST_GPR_OFFSET + i]; OpArg& reg2 = m_guest_registers[GUEST_GPR_OFFSET + i + 1]; - const bool reg1_imm = reg1.GetType() == RegType::Immediate; - const bool reg2_imm = reg2.GetType() == RegType::Immediate; - const bool reg1_zero = reg1_imm && reg1.GetImm() == 0; - const bool reg2_zero = reg2_imm && reg2.GetImm() == 0; + const bool reg1_imm = IsImm(i); + const bool reg2_imm = IsImm(i + 1); + const bool reg1_zero = reg1_imm && GetImm(i) == 0; + const bool reg2_zero = reg2_imm && GetImm(i + 1) == 0; const bool flush_all = mode == FlushMode::All; if (reg1.IsDirty() && reg2.IsDirty() && (reg1.GetType() == RegType::Register || (reg1_imm && (reg1_zero || flush_all))) && @@ -331,6 +345,7 @@ ARM64Reg Arm64GPRCache::BindForRead(size_t index) GuestRegInfo guest_reg = GetGuestByIndex(index); OpArg& reg = guest_reg.reg; size_t bitsize = guest_reg.bitsize; + const bool is_gpr = index >= GUEST_GPR_OFFSET && index < GUEST_GPR_OFFSET + GUEST_GPR_COUNT; IncrementAllUsed(); reg.ResetLastUsed(); @@ -339,17 +354,15 @@ ARM64Reg Arm64GPRCache::BindForRead(size_t index) { case RegType::Register: // already in a reg return reg.GetReg(); - case RegType::Immediate: // Is an immediate + case RegType::Discarded: // Is an immediate or discarded { + ASSERT_MSG(DYNA_REC, is_gpr && IsImm(index - GUEST_GPR_OFFSET), + "Attempted to read discarded register"); ARM64Reg host_reg = bitsize != 64 ? GetReg() : EncodeRegTo64(GetReg()); - m_emit->MOVI2R(host_reg, reg.GetImm()); + m_emit->MOVI2R(host_reg, GetImm(index - GUEST_GPR_OFFSET)); reg.Load(host_reg); return host_reg; } - break; - case RegType::Discarded: - ASSERT_MSG(DYNA_REC, false, "Attempted to read discarded register"); - break; case RegType::NotLoaded: // Register isn't loaded at /all/ { // This is a bit annoying. We try to keep these preloaded as much as possible @@ -376,7 +389,7 @@ void Arm64GPRCache::SetImmediateInternal(size_t index, u32 imm, bool dirty) OpArg& reg = guest_reg.reg; if (reg.GetType() == RegType::Register) UnlockRegister(EncodeRegTo32(reg.GetReg())); - reg.LoadToImm(imm); + reg.Discard(); reg.SetDirty(dirty); m_jit->GetConstantPropagation().SetGPR(index - GUEST_GPR_OFFSET, imm); } @@ -391,28 +404,32 @@ void Arm64GPRCache::BindForWrite(size_t index, bool will_read, bool will_write) reg.ResetLastUsed(); const RegType reg_type = reg.GetType(); - if (reg_type == RegType::NotLoaded || reg_type == RegType::Discarded) + if (reg_type != RegType::Register) { - const ARM64Reg host_reg = bitsize != 64 ? GetReg() : EncodeRegTo64(GetReg()); - reg.Load(host_reg); - reg.SetDirty(will_write); - if (will_read) + if (is_gpr && IsImm(index - GUEST_GPR_OFFSET)) { - ASSERT_MSG(DYNA_REC, reg_type != RegType::Discarded, "Attempted to load a discarded value"); - m_emit->LDR(IndexType::Unsigned, host_reg, PPC_REG, u32(guest_reg.ppc_offset)); + const ARM64Reg host_reg = bitsize != 64 ? GetReg() : EncodeRegTo64(GetReg()); + if (will_read || !will_write) + { + // TODO: Emitting this instruction when (!will_read && !will_write) would be unnecessary if + // we had some way to indicate to Flush that the immediate value should be written to + // ppcState even though there is a host register allocated + m_emit->MOVI2R(host_reg, GetImm(index - GUEST_GPR_OFFSET)); + } + reg.Load(host_reg); } - } - else if (reg_type == RegType::Immediate) - { - const ARM64Reg host_reg = bitsize != 64 ? GetReg() : EncodeRegTo64(GetReg()); - if (will_read || !will_write) + else { - // TODO: Emitting this instruction when (!will_read && !will_write) would be unnecessary if we - // had some way to indicate to Flush that the immediate value should be written to ppcState - // even though there is a host register allocated - m_emit->MOVI2R(host_reg, reg.GetImm()); + const ARM64Reg host_reg = bitsize != 64 ? GetReg() : EncodeRegTo64(GetReg()); + reg.Load(host_reg); + reg.SetDirty(will_write); + if (will_read) + { + ASSERT_MSG(DYNA_REC, reg_type != RegType::Discarded, "Attempted to load a discarded value"); + m_emit->LDR(IndexType::Unsigned, host_reg, PPC_REG, u32(guest_reg.ppc_offset)); + } + return; } - reg.Load(host_reg); } if (will_write) @@ -518,7 +535,7 @@ void Arm64FPRCache::Flush(FlushMode mode, ARM64Reg tmp_reg, ASSERT_MSG(DYNA_REC, ignore_discarded_registers != IgnoreDiscardedRegisters::No, "Attempted to flush discarded register"); } - else if (reg_type != RegType::NotLoaded && reg_type != RegType::Immediate) + else if (reg_type != RegType::NotLoaded) { FlushRegister(i, mode, tmp_reg); } @@ -782,7 +799,7 @@ void Arm64FPRCache::FlushByHost(ARM64Reg host_reg, ARM64Reg tmp_reg) const RegType reg_type = reg.GetType(); if (reg_type != RegType::NotLoaded && reg_type != RegType::Discarded && - reg_type != RegType::Immediate && reg.GetReg() == host_reg) + reg.GetReg() == host_reg) { FlushRegister(i, FlushMode::All, tmp_reg); return; diff --git a/Source/Core/Core/PowerPC/JitArm64/JitArm64_RegCache.h b/Source/Core/Core/PowerPC/JitArm64/JitArm64_RegCache.h index ed33f6a149..f0d58c64c4 100644 --- a/Source/Core/Core/PowerPC/JitArm64/JitArm64_RegCache.h +++ b/Source/Core/Core/PowerPC/JitArm64/JitArm64_RegCache.h @@ -61,9 +61,8 @@ static_assert(PPCSTATE_OFF(xer_so_ov) < 4096, "STRB can't store xer_so_ov!"); enum class RegType { NotLoaded, - Discarded, // Reg is not loaded because we know it won't be read before the next write + Discarded, // Reg is in ConstantPropagation, or isn't loaded at all Register, // Reg type is register - Immediate, // Reg is really a IMM LowerPair, // Only the lower pair of a paired register Duplicated, // The lower reg is the same as the upper one (physical upper doesn't actually have // the duplicated value) @@ -94,24 +93,17 @@ public: RegType GetType() const { return m_type; } Arm64Gen::ARM64Reg GetReg() const { return m_reg; } - u32 GetImm() const { return m_value; } void Load(Arm64Gen::ARM64Reg reg, RegType type = RegType::Register) { m_type = type; m_reg = reg; } - void LoadToImm(u32 imm) - { - m_type = RegType::Immediate; - m_value = imm; - - m_reg = Arm64Gen::ARM64Reg::INVALID_REG; - } void Discard() { // Invalidate any previous information m_type = RegType::Discarded; m_reg = Arm64Gen::ARM64Reg::INVALID_REG; + m_dirty = true; // Arbitrarily large value that won't roll over on a lot of increments m_last_used = 0xFFFF; @@ -121,6 +113,7 @@ public: // Invalidate any previous information m_type = RegType::NotLoaded; m_reg = Arm64Gen::ARM64Reg::INVALID_REG; + m_dirty = false; // Arbitrarily large value that won't roll over on a lot of increments m_last_used = 0xFFFF; @@ -137,9 +130,6 @@ private: RegType m_type = RegType::NotLoaded; // store type Arm64Gen::ARM64Reg m_reg = Arm64Gen::ARM64Reg::INVALID_REG; // host register we are in - // For REG_IMM - u32 m_value = 0; // IMM value - u32 m_last_used = 0; bool m_dirty = false; @@ -339,11 +329,11 @@ public: SetImmediateInternal(GUEST_GPR_OFFSET + preg, imm, dirty); } - // Returns if a register is set as an immediate. Only valid for guest GPRs. - bool IsImm(size_t preg) const { return GetGuestGPROpArg(preg).GetType() == RegType::Immediate; } + // Returns whether a register is set as an immediate. Only valid for guest GPRs. + bool IsImm(size_t preg) const; // Gets the immediate that a register is set to. Only valid for guest GPRs. - u32 GetImm(size_t preg) const { return GetGuestGPROpArg(preg).GetImm(); } + u32 GetImm(size_t preg) const; bool IsImm(size_t preg, u32 imm) { return IsImm(preg) && GetImm(preg) == imm; }