mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-12-16 04:09:39 +00:00
Jit: Extract immediate handling to separate ConstantPropagation class
Restructuring things in this way brings two immediate benefits: * Code is deduplicated between Jit64 and JitArm64. * Materializing an immediate value in a register no longer results in us forgetting what the immediate value was. As a more long-term benefit, this lets us also run constant propagation as part of PPCAnalyst, which could let us do cool stuff in the future like statically determining whether a conditional branch will be taken. But I have nothing concrete planned for that right now.
This commit is contained in:
parent
e8060bd169
commit
f9601dc38c
@ -508,6 +508,8 @@ add_library(core
|
|||||||
PowerPC/Interpreter/Interpreter_Tables.cpp
|
PowerPC/Interpreter/Interpreter_Tables.cpp
|
||||||
PowerPC/Interpreter/Interpreter.cpp
|
PowerPC/Interpreter/Interpreter.cpp
|
||||||
PowerPC/Interpreter/Interpreter.h
|
PowerPC/Interpreter/Interpreter.h
|
||||||
|
PowerPC/JitCommon/ConstantPropagation.cpp
|
||||||
|
PowerPC/JitCommon/ConstantPropagation.h
|
||||||
PowerPC/JitCommon/DivUtils.cpp
|
PowerPC/JitCommon/DivUtils.cpp
|
||||||
PowerPC/JitCommon/DivUtils.h
|
PowerPC/JitCommon/DivUtils.h
|
||||||
PowerPC/JitCommon/JitAsmCommon.cpp
|
PowerPC/JitCommon/JitAsmCommon.cpp
|
||||||
|
|||||||
@ -42,6 +42,7 @@
|
|||||||
#include "Core/PowerPC/Jit64Common/Jit64Constants.h"
|
#include "Core/PowerPC/Jit64Common/Jit64Constants.h"
|
||||||
#include "Core/PowerPC/Jit64Common/Jit64PowerPCState.h"
|
#include "Core/PowerPC/Jit64Common/Jit64PowerPCState.h"
|
||||||
#include "Core/PowerPC/Jit64Common/TrampolineCache.h"
|
#include "Core/PowerPC/Jit64Common/TrampolineCache.h"
|
||||||
|
#include "Core/PowerPC/JitCommon/ConstantPropagation.h"
|
||||||
#include "Core/PowerPC/JitInterface.h"
|
#include "Core/PowerPC/JitInterface.h"
|
||||||
#include "Core/PowerPC/MMU.h"
|
#include "Core/PowerPC/MMU.h"
|
||||||
#include "Core/PowerPC/PPCAnalyst.h"
|
#include "Core/PowerPC/PPCAnalyst.h"
|
||||||
@ -921,6 +922,8 @@ bool Jit64::DoJit(u32 em_address, JitBlock* b, u32 nextPC)
|
|||||||
gpr.Start();
|
gpr.Start();
|
||||||
fpr.Start();
|
fpr.Start();
|
||||||
|
|
||||||
|
m_constant_propagation.Clear();
|
||||||
|
|
||||||
js.downcountAmount = 0;
|
js.downcountAmount = 0;
|
||||||
js.skipInstructions = 0;
|
js.skipInstructions = 0;
|
||||||
js.carryFlag = CarryFlag::InPPCState;
|
js.carryFlag = CarryFlag::InPPCState;
|
||||||
@ -1105,21 +1108,56 @@ bool Jit64::DoJit(u32 em_address, JitBlock* b, u32 nextPC)
|
|||||||
{
|
{
|
||||||
gpr.Flush();
|
gpr.Flush();
|
||||||
fpr.Flush();
|
fpr.Flush();
|
||||||
|
m_constant_propagation.Clear();
|
||||||
|
|
||||||
|
CompileInstruction(op);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// If we have an input register that is going to be used again, load it pre-emptively,
|
const JitCommon::ConstantPropagationResult constant_propagation_result =
|
||||||
// even if the instruction doesn't strictly need it in a register, to avoid redundant
|
m_constant_propagation.EvaluateInstruction(op.inst);
|
||||||
// loads later. Of course, don't do this if we're already out of registers.
|
|
||||||
// As a bit of a heuristic, make sure we have at least one register left over for the
|
|
||||||
// output, which needs to be bound in the actual instruction compilation.
|
|
||||||
// TODO: make this smarter in the case that we're actually register-starved, i.e.
|
|
||||||
// prioritize the more important registers.
|
|
||||||
gpr.PreloadRegisters(op.regsIn & op.gprInUse & ~op.gprDiscardable);
|
|
||||||
fpr.PreloadRegisters(op.fregsIn & op.fprInXmm & ~op.fprDiscardable);
|
|
||||||
}
|
|
||||||
|
|
||||||
CompileInstruction(op);
|
if (!constant_propagation_result.instruction_fully_executed)
|
||||||
|
{
|
||||||
|
if (!bJITRegisterCacheOff)
|
||||||
|
{
|
||||||
|
// If we have an input register that is going to be used again, load it pre-emptively,
|
||||||
|
// even if the instruction doesn't strictly need it in a register, to avoid redundant
|
||||||
|
// loads later. Of course, don't do this if we're already out of registers.
|
||||||
|
// As a bit of a heuristic, make sure we have at least one register left over for the
|
||||||
|
// output, which needs to be bound in the actual instruction compilation.
|
||||||
|
// TODO: make this smarter in the case that we're actually register-starved, i.e.
|
||||||
|
// prioritize the more important registers.
|
||||||
|
gpr.PreloadRegisters(op.regsIn & op.gprInUse & ~op.gprDiscardable);
|
||||||
|
fpr.PreloadRegisters(op.fregsIn & op.fprInXmm & ~op.fprDiscardable);
|
||||||
|
}
|
||||||
|
|
||||||
|
CompileInstruction(op);
|
||||||
|
|
||||||
|
m_constant_propagation.ClearGPRs(op.regsOut);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_constant_propagation.Apply(constant_propagation_result);
|
||||||
|
|
||||||
|
if (constant_propagation_result.gpr >= 0)
|
||||||
|
{
|
||||||
|
gpr.SetImmediate32(constant_propagation_result.gpr,
|
||||||
|
constant_propagation_result.gpr_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (constant_propagation_result.instruction_fully_executed)
|
||||||
|
{
|
||||||
|
if (constant_propagation_result.carry)
|
||||||
|
FinalizeCarry(*constant_propagation_result.carry);
|
||||||
|
|
||||||
|
if (constant_propagation_result.overflow)
|
||||||
|
GenerateConstantOverflow(*constant_propagation_result.overflow);
|
||||||
|
|
||||||
|
// FinalizeImmediateRC is called last, because it may trigger branch merging
|
||||||
|
if (constant_propagation_result.compute_rc)
|
||||||
|
FinalizeImmediateRC(constant_propagation_result.gpr_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
js.fpr_is_store_safe = op.fprIsStoreSafeAfterInst;
|
js.fpr_is_store_safe = op.fprIsStoreSafeAfterInst;
|
||||||
|
|
||||||
|
|||||||
@ -31,6 +31,7 @@
|
|||||||
#include "Core/PowerPC/Jit64Common/BlockCache.h"
|
#include "Core/PowerPC/Jit64Common/BlockCache.h"
|
||||||
#include "Core/PowerPC/Jit64Common/Jit64AsmCommon.h"
|
#include "Core/PowerPC/Jit64Common/Jit64AsmCommon.h"
|
||||||
#include "Core/PowerPC/Jit64Common/TrampolineCache.h"
|
#include "Core/PowerPC/Jit64Common/TrampolineCache.h"
|
||||||
|
#include "Core/PowerPC/JitCommon/ConstantPropagation.h"
|
||||||
#include "Core/PowerPC/JitCommon/JitBase.h"
|
#include "Core/PowerPC/JitCommon/JitBase.h"
|
||||||
#include "Core/PowerPC/JitCommon/JitCache.h"
|
#include "Core/PowerPC/JitCommon/JitCache.h"
|
||||||
|
|
||||||
@ -289,6 +290,8 @@ private:
|
|||||||
GPRRegCache gpr{*this};
|
GPRRegCache gpr{*this};
|
||||||
FPURegCache fpr{*this};
|
FPURegCache fpr{*this};
|
||||||
|
|
||||||
|
JitCommon::ConstantPropagation m_constant_propagation;
|
||||||
|
|
||||||
Jit64AsmRoutineManager asm_routines{*this};
|
Jit64AsmRoutineManager asm_routines{*this};
|
||||||
|
|
||||||
HyoutaUtilities::RangeSizeSet<u8*> m_free_ranges_near;
|
HyoutaUtilities::RangeSizeSet<u8*> m_free_ranges_near;
|
||||||
|
|||||||
@ -33,6 +33,7 @@
|
|||||||
#include "Core/PatchEngine.h"
|
#include "Core/PatchEngine.h"
|
||||||
#include "Core/PowerPC/Interpreter/Interpreter.h"
|
#include "Core/PowerPC/Interpreter/Interpreter.h"
|
||||||
#include "Core/PowerPC/JitArm64/JitArm64_RegCache.h"
|
#include "Core/PowerPC/JitArm64/JitArm64_RegCache.h"
|
||||||
|
#include "Core/PowerPC/JitCommon/ConstantPropagation.h"
|
||||||
#include "Core/PowerPC/JitInterface.h"
|
#include "Core/PowerPC/JitInterface.h"
|
||||||
#include "Core/PowerPC/PowerPC.h"
|
#include "Core/PowerPC/PowerPC.h"
|
||||||
#include "Core/System.h"
|
#include "Core/System.h"
|
||||||
@ -1169,6 +1170,8 @@ bool JitArm64::DoJit(u32 em_address, JitBlock* b, u32 nextPC)
|
|||||||
gpr.Start(js.gpa);
|
gpr.Start(js.gpa);
|
||||||
fpr.Start(js.fpa);
|
fpr.Start(js.fpa);
|
||||||
|
|
||||||
|
m_constant_propagation.Clear();
|
||||||
|
|
||||||
if (!js.noSpeculativeConstantsAddresses.contains(js.blockStart))
|
if (!js.noSpeculativeConstantsAddresses.contains(js.blockStart))
|
||||||
{
|
{
|
||||||
IntializeSpeculativeConstants();
|
IntializeSpeculativeConstants();
|
||||||
@ -1341,9 +1344,39 @@ bool JitArm64::DoJit(u32 em_address, JitBlock* b, u32 nextPC)
|
|||||||
FlushCarry();
|
FlushCarry();
|
||||||
gpr.Flush(FlushMode::All, ARM64Reg::INVALID_REG);
|
gpr.Flush(FlushMode::All, ARM64Reg::INVALID_REG);
|
||||||
fpr.Flush(FlushMode::All, ARM64Reg::INVALID_REG);
|
fpr.Flush(FlushMode::All, ARM64Reg::INVALID_REG);
|
||||||
}
|
m_constant_propagation.Clear();
|
||||||
|
|
||||||
CompileInstruction(op);
|
CompileInstruction(op);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const JitCommon::ConstantPropagationResult constant_propagation_result =
|
||||||
|
m_constant_propagation.EvaluateInstruction(op.inst);
|
||||||
|
|
||||||
|
if (!constant_propagation_result.instruction_fully_executed)
|
||||||
|
{
|
||||||
|
CompileInstruction(op);
|
||||||
|
|
||||||
|
m_constant_propagation.ClearGPRs(op.regsOut);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_constant_propagation.Apply(constant_propagation_result);
|
||||||
|
|
||||||
|
if (constant_propagation_result.gpr >= 0)
|
||||||
|
gpr.SetImmediate(constant_propagation_result.gpr, constant_propagation_result.gpr_value);
|
||||||
|
|
||||||
|
if (constant_propagation_result.instruction_fully_executed)
|
||||||
|
{
|
||||||
|
if (constant_propagation_result.carry)
|
||||||
|
ComputeCarry(*constant_propagation_result.carry);
|
||||||
|
|
||||||
|
if (constant_propagation_result.overflow)
|
||||||
|
GenerateConstantOverflow(*constant_propagation_result.overflow);
|
||||||
|
|
||||||
|
if (constant_propagation_result.compute_rc)
|
||||||
|
ComputeRC0(constant_propagation_result.gpr_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
js.fpr_is_store_safe = op.fprIsStoreSafeAfterInst;
|
js.fpr_is_store_safe = op.fprIsStoreSafeAfterInst;
|
||||||
|
|
||||||
|
|||||||
@ -16,6 +16,7 @@
|
|||||||
#include "Core/PowerPC/JitArm64/JitArm64Cache.h"
|
#include "Core/PowerPC/JitArm64/JitArm64Cache.h"
|
||||||
#include "Core/PowerPC/JitArm64/JitArm64_RegCache.h"
|
#include "Core/PowerPC/JitArm64/JitArm64_RegCache.h"
|
||||||
#include "Core/PowerPC/JitArmCommon/BackPatch.h"
|
#include "Core/PowerPC/JitArmCommon/BackPatch.h"
|
||||||
|
#include "Core/PowerPC/JitCommon/ConstantPropagation.h"
|
||||||
#include "Core/PowerPC/JitCommon/JitAsmCommon.h"
|
#include "Core/PowerPC/JitCommon/JitAsmCommon.h"
|
||||||
#include "Core/PowerPC/JitCommon/JitBase.h"
|
#include "Core/PowerPC/JitCommon/JitBase.h"
|
||||||
#include "Core/PowerPC/PPCAnalyst.h"
|
#include "Core/PowerPC/PPCAnalyst.h"
|
||||||
@ -397,6 +398,8 @@ protected:
|
|||||||
Arm64GPRCache gpr;
|
Arm64GPRCache gpr;
|
||||||
Arm64FPRCache fpr;
|
Arm64FPRCache fpr;
|
||||||
|
|
||||||
|
JitCommon::ConstantPropagation m_constant_propagation;
|
||||||
|
|
||||||
JitArm64BlockCache blocks{*this};
|
JitArm64BlockCache blocks{*this};
|
||||||
|
|
||||||
Arm64Gen::ARM64FloatEmitter m_float_emit;
|
Arm64Gen::ARM64FloatEmitter m_float_emit;
|
||||||
|
|||||||
19
Source/Core/Core/PowerPC/JitCommon/ConstantPropagation.cpp
Normal file
19
Source/Core/Core/PowerPC/JitCommon/ConstantPropagation.cpp
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// Copyright 2023 Dolphin Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include "Core/PowerPC/JitCommon/ConstantPropagation.h"
|
||||||
|
|
||||||
|
namespace JitCommon
|
||||||
|
{
|
||||||
|
ConstantPropagationResult ConstantPropagation::EvaluateInstruction(UGeckoInstruction inst) const
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConstantPropagation::Apply(ConstantPropagationResult result)
|
||||||
|
{
|
||||||
|
if (result.gpr >= 0)
|
||||||
|
SetGPR(result.gpr, result.gpr_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace JitCommon
|
||||||
86
Source/Core/Core/PowerPC/JitCommon/ConstantPropagation.h
Normal file
86
Source/Core/Core/PowerPC/JitCommon/ConstantPropagation.h
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
// Copyright 2023 Dolphin Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Common/BitSet.h"
|
||||||
|
#include "Common/CommonTypes.h"
|
||||||
|
#include "Core/PowerPC/PowerPC.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
namespace JitCommon
|
||||||
|
{
|
||||||
|
struct ConstantPropagationResult final
|
||||||
|
{
|
||||||
|
constexpr ConstantPropagationResult() = default;
|
||||||
|
|
||||||
|
constexpr ConstantPropagationResult(s8 gpr_, u32 gpr_value_, bool compute_rc_ = false)
|
||||||
|
: gpr_value(gpr_value_), gpr(gpr_), instruction_fully_executed(true), compute_rc(compute_rc_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// If gpr is non-negative, this is the value the instruction writes to that GPR.
|
||||||
|
u32 gpr_value = 0;
|
||||||
|
|
||||||
|
// If the instruction couldn't be evaluated or doesn't output to a GPR, this is -1.
|
||||||
|
// Otherwise, this is the GPR that the instruction writes to.
|
||||||
|
s8 gpr = -1;
|
||||||
|
|
||||||
|
// Whether the instruction was able to be fully evaluated with no side effects unaccounted for,
|
||||||
|
// or in other words, whether the JIT can skip emitting code for this instruction.
|
||||||
|
bool instruction_fully_executed = false;
|
||||||
|
|
||||||
|
// If true, CR0 needs to be set based on gpr_value.
|
||||||
|
bool compute_rc = false;
|
||||||
|
|
||||||
|
// If not std::nullopt, the instruction writes this to the carry flag.
|
||||||
|
std::optional<bool> carry = std::nullopt;
|
||||||
|
|
||||||
|
// If not std::nullopt, the instruction writes this to the overflow flag.
|
||||||
|
std::optional<bool> overflow = std::nullopt;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ConstantPropagation final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ConstantPropagationResult EvaluateInstruction(UGeckoInstruction inst) const;
|
||||||
|
|
||||||
|
void Apply(ConstantPropagationResult result);
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
bool HasGPR(Args... gprs) const
|
||||||
|
{
|
||||||
|
return HasGPRs(BitSet32{static_cast<int>(gprs)...});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HasGPRs(BitSet32 gprs) const { return (m_gpr_values_known & gprs) == gprs; }
|
||||||
|
|
||||||
|
u32 GetGPR(size_t gpr) const { return m_gpr_values[gpr]; }
|
||||||
|
|
||||||
|
void SetGPR(size_t gpr, u32 value)
|
||||||
|
{
|
||||||
|
m_gpr_values_known[gpr] = true;
|
||||||
|
m_gpr_values[gpr] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
void ClearGPR(Args... gprs)
|
||||||
|
{
|
||||||
|
ClearGPRs(BitSet32{static_cast<int>(gprs)...});
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClearGPRs(BitSet32 gprs) { m_gpr_values_known &= ~gprs; }
|
||||||
|
|
||||||
|
void Clear() { m_gpr_values_known = BitSet32{}; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr size_t GPR_COUNT = 32;
|
||||||
|
|
||||||
|
std::array<u32, GPR_COUNT> m_gpr_values;
|
||||||
|
BitSet32 m_gpr_values_known{};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace JitCommon
|
||||||
@ -455,6 +455,7 @@
|
|||||||
<ClInclude Include="Core\PowerPC\Interpreter\ExceptionUtils.h" />
|
<ClInclude Include="Core\PowerPC\Interpreter\ExceptionUtils.h" />
|
||||||
<ClInclude Include="Core\PowerPC\Interpreter\Interpreter_FPUtils.h" />
|
<ClInclude Include="Core\PowerPC\Interpreter\Interpreter_FPUtils.h" />
|
||||||
<ClInclude Include="Core\PowerPC\Interpreter\Interpreter.h" />
|
<ClInclude Include="Core\PowerPC\Interpreter\Interpreter.h" />
|
||||||
|
<ClInclude Include="Core\PowerPC\JitCommon\ConstantPropagation.h" />
|
||||||
<ClInclude Include="Core\PowerPC\JitCommon\DivUtils.h" />
|
<ClInclude Include="Core\PowerPC\JitCommon\DivUtils.h" />
|
||||||
<ClInclude Include="Core\PowerPC\JitCommon\JitAsmCommon.h" />
|
<ClInclude Include="Core\PowerPC\JitCommon\JitAsmCommon.h" />
|
||||||
<ClInclude Include="Core\PowerPC\JitCommon\JitBase.h" />
|
<ClInclude Include="Core\PowerPC\JitCommon\JitBase.h" />
|
||||||
@ -1139,6 +1140,7 @@
|
|||||||
<ClCompile Include="Core\PowerPC\Interpreter\Interpreter_SystemRegisters.cpp" />
|
<ClCompile Include="Core\PowerPC\Interpreter\Interpreter_SystemRegisters.cpp" />
|
||||||
<ClCompile Include="Core\PowerPC\Interpreter\Interpreter_Tables.cpp" />
|
<ClCompile Include="Core\PowerPC\Interpreter\Interpreter_Tables.cpp" />
|
||||||
<ClCompile Include="Core\PowerPC\Interpreter\Interpreter.cpp" />
|
<ClCompile Include="Core\PowerPC\Interpreter\Interpreter.cpp" />
|
||||||
|
<ClCompile Include="Core\PowerPC\JitCommon\ConstantPropagation.cpp" />
|
||||||
<ClCompile Include="Core\PowerPC\JitCommon\DivUtils.cpp" />
|
<ClCompile Include="Core\PowerPC\JitCommon\DivUtils.cpp" />
|
||||||
<ClCompile Include="Core\PowerPC\JitCommon\JitAsmCommon.cpp" />
|
<ClCompile Include="Core\PowerPC\JitCommon\JitAsmCommon.cpp" />
|
||||||
<ClCompile Include="Core\PowerPC\JitCommon\JitBase.cpp" />
|
<ClCompile Include="Core\PowerPC\JitCommon\JitBase.cpp" />
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user