mirror of
https://git.zaroz.cloud/nintendo-back-up/Ryujinx.git
synced 2025-12-21 01:42:41 +00:00
Implement a new JIT for Arm devices (#6057)
* Implement a new JIT for Arm devices * Auto-format * Make a lot of Assembler members read-only * More read-only * Fix more warnings * ObjectDisposedException.ThrowIf * New JIT cache for platforms that enforce W^X, currently unused * Remove unused using * Fix assert * Pass memory manager type around * Safe memory manager mode support + other improvements * Actual safe memory manager mode masking support * PR feedback
This commit is contained in:
171
src/Ryujinx.Cpu/LightningJit/Graph/DataFlow.cs
Normal file
171
src/Ryujinx.Cpu/LightningJit/Graph/DataFlow.cs
Normal file
@@ -0,0 +1,171 @@
|
||||
namespace Ryujinx.Cpu.LightningJit.Graph
|
||||
{
|
||||
static class DataFlow
|
||||
{
|
||||
public static (RegisterMask[], RegisterMask[]) GetGlobalUses(IBlockList blocks)
|
||||
{
|
||||
// Compute local register inputs and outputs used inside blocks.
|
||||
RegisterMask[] localInputs = new RegisterMask[blocks.Count];
|
||||
RegisterMask[] localOutputs = new RegisterMask[blocks.Count];
|
||||
|
||||
for (int index = 0; index < blocks.Count; index++)
|
||||
{
|
||||
IBlock block = blocks[index];
|
||||
|
||||
RegisterUse use = block.ComputeUseMasks();
|
||||
|
||||
localInputs[block.Index] = use.Read;
|
||||
localOutputs[block.Index] = use.Write;
|
||||
}
|
||||
|
||||
// Compute global register inputs and outputs used across blocks.
|
||||
RegisterMask[] globalInputs = new RegisterMask[blocks.Count];
|
||||
RegisterMask[] globalOutputs = new RegisterMask[blocks.Count];
|
||||
|
||||
bool modified;
|
||||
|
||||
// Compute register outputs.
|
||||
do
|
||||
{
|
||||
modified = false;
|
||||
|
||||
for (int index = 0; index < blocks.Count; index++)
|
||||
{
|
||||
IBlock block = blocks[index];
|
||||
|
||||
int firstPIndex = GetFirstPredecessorIndex(block);
|
||||
if (firstPIndex >= 0)
|
||||
{
|
||||
IBlock predecessor = block.GetPredecessor(firstPIndex);
|
||||
|
||||
RegisterMask outputs = globalOutputs[predecessor.Index];
|
||||
|
||||
for (int pIndex = firstPIndex + 1; pIndex < block.PredecessorsCount; pIndex++)
|
||||
{
|
||||
predecessor = block.GetPredecessor(pIndex);
|
||||
|
||||
if (predecessor.EndsWithContextStore())
|
||||
{
|
||||
// If a block ended with a context store, then we don't need to care
|
||||
// about any of it's outputs, as they have already been saved to the context.
|
||||
// Common outputs must be reset as doing a context stores indicates we will
|
||||
// do a function call and wipe all register values.
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
outputs |= globalOutputs[predecessor.Index];
|
||||
}
|
||||
|
||||
outputs |= localOutputs[block.Index];
|
||||
modified |= Exchange(globalOutputs, block.Index, globalOutputs[block.Index] | outputs);
|
||||
}
|
||||
else
|
||||
{
|
||||
modified |= Exchange(globalOutputs, block.Index, localOutputs[block.Index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
while (modified);
|
||||
|
||||
// Compute register inputs.
|
||||
do
|
||||
{
|
||||
modified = false;
|
||||
|
||||
for (int index = blocks.Count - 1; index >= 0; index--)
|
||||
{
|
||||
IBlock block = blocks[index];
|
||||
|
||||
RegisterMask cmnOutputs = RegisterMask.Zero;
|
||||
RegisterMask allOutputs = RegisterMask.Zero;
|
||||
|
||||
int firstPIndex = GetFirstPredecessorIndex(block);
|
||||
if (firstPIndex == 0)
|
||||
{
|
||||
IBlock predecessor = block.GetPredecessor(0);
|
||||
|
||||
// Assumes that block index 0 is the entry block.
|
||||
cmnOutputs = block.Index != 0 ? globalOutputs[predecessor.Index] : RegisterMask.Zero;
|
||||
allOutputs = globalOutputs[predecessor.Index];
|
||||
|
||||
for (int pIndex = 1; pIndex < block.PredecessorsCount; pIndex++)
|
||||
{
|
||||
predecessor = block.GetPredecessor(pIndex);
|
||||
|
||||
if (!predecessor.EndsWithContextStore())
|
||||
{
|
||||
RegisterMask outputs = globalOutputs[predecessor.Index];
|
||||
|
||||
cmnOutputs &= outputs;
|
||||
allOutputs |= outputs;
|
||||
}
|
||||
else
|
||||
{
|
||||
cmnOutputs = RegisterMask.Zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (firstPIndex > 0)
|
||||
{
|
||||
IBlock predecessor = block.GetPredecessor(firstPIndex);
|
||||
|
||||
allOutputs = globalOutputs[predecessor.Index];
|
||||
|
||||
for (int pIndex = firstPIndex + 1; pIndex < block.PredecessorsCount; pIndex++)
|
||||
{
|
||||
predecessor = block.GetPredecessor(pIndex);
|
||||
|
||||
if (!predecessor.EndsWithContextStore())
|
||||
{
|
||||
allOutputs |= globalOutputs[predecessor.Index];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RegisterMask inputs = localInputs[block.Index];
|
||||
|
||||
// If this block will load from context at the end,
|
||||
// we don't need to care about what comes next.
|
||||
if (!block.EndsWithContextLoad())
|
||||
{
|
||||
for (int sIndex = 0; sIndex < block.SuccessorsCount; sIndex++)
|
||||
{
|
||||
inputs |= globalInputs[block.GetSuccessor(sIndex).Index] & ~localOutputs[block.Index];
|
||||
}
|
||||
}
|
||||
|
||||
inputs |= allOutputs & ~localOutputs[block.Index];
|
||||
inputs &= ~cmnOutputs;
|
||||
|
||||
modified |= Exchange(globalInputs, block.Index, globalInputs[block.Index] | inputs);
|
||||
}
|
||||
}
|
||||
while (modified);
|
||||
|
||||
return (globalInputs, globalOutputs);
|
||||
}
|
||||
|
||||
private static bool Exchange(RegisterMask[] masks, int blkIndex, RegisterMask value)
|
||||
{
|
||||
ref RegisterMask curValue = ref masks[blkIndex];
|
||||
bool changed = curValue != value;
|
||||
curValue = value;
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
private static int GetFirstPredecessorIndex(IBlock block)
|
||||
{
|
||||
for (int pIndex = 0; pIndex < block.PredecessorsCount; pIndex++)
|
||||
{
|
||||
if (!block.GetPredecessor(pIndex).EndsWithContextStore())
|
||||
{
|
||||
return pIndex;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
17
src/Ryujinx.Cpu/LightningJit/Graph/IBlock.cs
Normal file
17
src/Ryujinx.Cpu/LightningJit/Graph/IBlock.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
namespace Ryujinx.Cpu.LightningJit.Graph
|
||||
{
|
||||
interface IBlock
|
||||
{
|
||||
int Index { get; }
|
||||
|
||||
int PredecessorsCount { get; }
|
||||
int SuccessorsCount { get; }
|
||||
|
||||
IBlock GetSuccessor(int index);
|
||||
IBlock GetPredecessor(int index);
|
||||
|
||||
RegisterUse ComputeUseMasks();
|
||||
bool EndsWithContextLoad();
|
||||
bool EndsWithContextStore();
|
||||
}
|
||||
}
|
||||
9
src/Ryujinx.Cpu/LightningJit/Graph/IBlockList.cs
Normal file
9
src/Ryujinx.Cpu/LightningJit/Graph/IBlockList.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace Ryujinx.Cpu.LightningJit.Graph
|
||||
{
|
||||
interface IBlockList
|
||||
{
|
||||
int Count { get; }
|
||||
|
||||
IBlock this[int index] { get; }
|
||||
}
|
||||
}
|
||||
60
src/Ryujinx.Cpu/LightningJit/Graph/RegisterMask.cs
Normal file
60
src/Ryujinx.Cpu/LightningJit/Graph/RegisterMask.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Cpu.LightningJit.Graph
|
||||
{
|
||||
readonly struct RegisterMask : IEquatable<RegisterMask>
|
||||
{
|
||||
public readonly uint GprMask;
|
||||
public readonly uint FpSimdMask;
|
||||
public readonly uint PStateMask;
|
||||
|
||||
public static RegisterMask Zero => new(0u, 0u, 0u);
|
||||
|
||||
public RegisterMask(uint gprMask, uint fpSimdMask, uint pStateMask)
|
||||
{
|
||||
GprMask = gprMask;
|
||||
FpSimdMask = fpSimdMask;
|
||||
PStateMask = pStateMask;
|
||||
}
|
||||
|
||||
public static RegisterMask operator &(RegisterMask x, RegisterMask y)
|
||||
{
|
||||
return new(x.GprMask & y.GprMask, x.FpSimdMask & y.FpSimdMask, x.PStateMask & y.PStateMask);
|
||||
}
|
||||
|
||||
public static RegisterMask operator |(RegisterMask x, RegisterMask y)
|
||||
{
|
||||
return new(x.GprMask | y.GprMask, x.FpSimdMask | y.FpSimdMask, x.PStateMask | y.PStateMask);
|
||||
}
|
||||
|
||||
public static RegisterMask operator ~(RegisterMask x)
|
||||
{
|
||||
return new(~x.GprMask, ~x.FpSimdMask, ~x.PStateMask);
|
||||
}
|
||||
|
||||
public static bool operator ==(RegisterMask x, RegisterMask y)
|
||||
{
|
||||
return x.Equals(y);
|
||||
}
|
||||
|
||||
public static bool operator !=(RegisterMask x, RegisterMask y)
|
||||
{
|
||||
return !x.Equals(y);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is RegisterMask regMask && Equals(regMask);
|
||||
}
|
||||
|
||||
public bool Equals(RegisterMask other)
|
||||
{
|
||||
return GprMask == other.GprMask && FpSimdMask == other.FpSimdMask && PStateMask == other.PStateMask;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(GprMask, FpSimdMask, PStateMask);
|
||||
}
|
||||
}
|
||||
}
|
||||
24
src/Ryujinx.Cpu/LightningJit/Graph/RegisterUse.cs
Normal file
24
src/Ryujinx.Cpu/LightningJit/Graph/RegisterUse.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
namespace Ryujinx.Cpu.LightningJit.Graph
|
||||
{
|
||||
readonly struct RegisterUse
|
||||
{
|
||||
public readonly RegisterMask Read;
|
||||
public readonly RegisterMask Write;
|
||||
|
||||
public RegisterUse(RegisterMask read, RegisterMask write)
|
||||
{
|
||||
Read = read;
|
||||
Write = write;
|
||||
}
|
||||
|
||||
public RegisterUse(
|
||||
uint gprReadMask,
|
||||
uint gprWriteMask,
|
||||
uint fpSimdReadMask,
|
||||
uint fpSimdWriteMask,
|
||||
uint pStateReadMask,
|
||||
uint pStateWriteMask) : this(new(gprReadMask, fpSimdReadMask, pStateReadMask), new(gprWriteMask, fpSimdWriteMask, pStateWriteMask))
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user