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.cpp
|
||||
PowerPC/Interpreter/Interpreter.h
|
||||
PowerPC/JitCommon/ConstantPropagation.cpp
|
||||
PowerPC/JitCommon/ConstantPropagation.h
|
||||
PowerPC/JitCommon/DivUtils.cpp
|
||||
PowerPC/JitCommon/DivUtils.h
|
||||
PowerPC/JitCommon/JitAsmCommon.cpp
|
||||
|
||||
@ -42,6 +42,7 @@
|
||||
#include "Core/PowerPC/Jit64Common/Jit64Constants.h"
|
||||
#include "Core/PowerPC/Jit64Common/Jit64PowerPCState.h"
|
||||
#include "Core/PowerPC/Jit64Common/TrampolineCache.h"
|
||||
#include "Core/PowerPC/JitCommon/ConstantPropagation.h"
|
||||
#include "Core/PowerPC/JitInterface.h"
|
||||
#include "Core/PowerPC/MMU.h"
|
||||
#include "Core/PowerPC/PPCAnalyst.h"
|
||||
@ -921,6 +922,8 @@ bool Jit64::DoJit(u32 em_address, JitBlock* b, u32 nextPC)
|
||||
gpr.Start();
|
||||
fpr.Start();
|
||||
|
||||
m_constant_propagation.Clear();
|
||||
|
||||
js.downcountAmount = 0;
|
||||
js.skipInstructions = 0;
|
||||
js.carryFlag = CarryFlag::InPPCState;
|
||||
@ -1105,21 +1108,56 @@ bool Jit64::DoJit(u32 em_address, JitBlock* b, u32 nextPC)
|
||||
{
|
||||
gpr.Flush();
|
||||
fpr.Flush();
|
||||
m_constant_propagation.Clear();
|
||||
|
||||
CompileInstruction(op);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
const JitCommon::ConstantPropagationResult constant_propagation_result =
|
||||
m_constant_propagation.EvaluateInstruction(op.inst);
|
||||
|
||||
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;
|
||||
|
||||
|
||||
@ -31,6 +31,7 @@
|
||||
#include "Core/PowerPC/Jit64Common/BlockCache.h"
|
||||
#include "Core/PowerPC/Jit64Common/Jit64AsmCommon.h"
|
||||
#include "Core/PowerPC/Jit64Common/TrampolineCache.h"
|
||||
#include "Core/PowerPC/JitCommon/ConstantPropagation.h"
|
||||
#include "Core/PowerPC/JitCommon/JitBase.h"
|
||||
#include "Core/PowerPC/JitCommon/JitCache.h"
|
||||
|
||||
@ -289,6 +290,8 @@ private:
|
||||
GPRRegCache gpr{*this};
|
||||
FPURegCache fpr{*this};
|
||||
|
||||
JitCommon::ConstantPropagation m_constant_propagation;
|
||||
|
||||
Jit64AsmRoutineManager asm_routines{*this};
|
||||
|
||||
HyoutaUtilities::RangeSizeSet<u8*> m_free_ranges_near;
|
||||
|
||||
@ -33,6 +33,7 @@
|
||||
#include "Core/PatchEngine.h"
|
||||
#include "Core/PowerPC/Interpreter/Interpreter.h"
|
||||
#include "Core/PowerPC/JitArm64/JitArm64_RegCache.h"
|
||||
#include "Core/PowerPC/JitCommon/ConstantPropagation.h"
|
||||
#include "Core/PowerPC/JitInterface.h"
|
||||
#include "Core/PowerPC/PowerPC.h"
|
||||
#include "Core/System.h"
|
||||
@ -1169,6 +1170,8 @@ bool JitArm64::DoJit(u32 em_address, JitBlock* b, u32 nextPC)
|
||||
gpr.Start(js.gpa);
|
||||
fpr.Start(js.fpa);
|
||||
|
||||
m_constant_propagation.Clear();
|
||||
|
||||
if (!js.noSpeculativeConstantsAddresses.contains(js.blockStart))
|
||||
{
|
||||
IntializeSpeculativeConstants();
|
||||
@ -1341,9 +1344,39 @@ bool JitArm64::DoJit(u32 em_address, JitBlock* b, u32 nextPC)
|
||||
FlushCarry();
|
||||
gpr.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;
|
||||
|
||||
|
||||
@ -16,6 +16,7 @@
|
||||
#include "Core/PowerPC/JitArm64/JitArm64Cache.h"
|
||||
#include "Core/PowerPC/JitArm64/JitArm64_RegCache.h"
|
||||
#include "Core/PowerPC/JitArmCommon/BackPatch.h"
|
||||
#include "Core/PowerPC/JitCommon/ConstantPropagation.h"
|
||||
#include "Core/PowerPC/JitCommon/JitAsmCommon.h"
|
||||
#include "Core/PowerPC/JitCommon/JitBase.h"
|
||||
#include "Core/PowerPC/PPCAnalyst.h"
|
||||
@ -397,6 +398,8 @@ protected:
|
||||
Arm64GPRCache gpr;
|
||||
Arm64FPRCache fpr;
|
||||
|
||||
JitCommon::ConstantPropagation m_constant_propagation;
|
||||
|
||||
JitArm64BlockCache blocks{*this};
|
||||
|
||||
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\Interpreter_FPUtils.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\JitAsmCommon.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_Tables.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\JitAsmCommon.cpp" />
|
||||
<ClCompile Include="Core\PowerPC\JitCommon\JitBase.cpp" />
|
||||
|
||||
Loading…
Reference in New Issue
Block a user