aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Cpu/LightningJit/Graph
diff options
context:
space:
mode:
authorgdkchan <gab.dark.100@gmail.com>2024-01-20 11:11:28 -0300
committerGitHub <noreply@github.com>2024-01-20 11:11:28 -0300
commit427b7d06b5ab6d2b06784a9d283eaf836a04c27e (patch)
treeb69b500432626c89f6a4b7171a948b46c46b3723 /src/Ryujinx.Cpu/LightningJit/Graph
parent331c07807fd0db5d4452d6ef02962a6d19a56d7f (diff)
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
Diffstat (limited to 'src/Ryujinx.Cpu/LightningJit/Graph')
-rw-r--r--src/Ryujinx.Cpu/LightningJit/Graph/DataFlow.cs171
-rw-r--r--src/Ryujinx.Cpu/LightningJit/Graph/IBlock.cs17
-rw-r--r--src/Ryujinx.Cpu/LightningJit/Graph/IBlockList.cs9
-rw-r--r--src/Ryujinx.Cpu/LightningJit/Graph/RegisterMask.cs60
-rw-r--r--src/Ryujinx.Cpu/LightningJit/Graph/RegisterUse.cs24
5 files changed, 281 insertions, 0 deletions
diff --git a/src/Ryujinx.Cpu/LightningJit/Graph/DataFlow.cs b/src/Ryujinx.Cpu/LightningJit/Graph/DataFlow.cs
new file mode 100644
index 00000000..e5b369d6
--- /dev/null
+++ b/src/Ryujinx.Cpu/LightningJit/Graph/DataFlow.cs
@@ -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;
+ }
+ }
+}
diff --git a/src/Ryujinx.Cpu/LightningJit/Graph/IBlock.cs b/src/Ryujinx.Cpu/LightningJit/Graph/IBlock.cs
new file mode 100644
index 00000000..2e5d7132
--- /dev/null
+++ b/src/Ryujinx.Cpu/LightningJit/Graph/IBlock.cs
@@ -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();
+ }
+}
diff --git a/src/Ryujinx.Cpu/LightningJit/Graph/IBlockList.cs b/src/Ryujinx.Cpu/LightningJit/Graph/IBlockList.cs
new file mode 100644
index 00000000..aeec799c
--- /dev/null
+++ b/src/Ryujinx.Cpu/LightningJit/Graph/IBlockList.cs
@@ -0,0 +1,9 @@
+namespace Ryujinx.Cpu.LightningJit.Graph
+{
+ interface IBlockList
+ {
+ int Count { get; }
+
+ IBlock this[int index] { get; }
+ }
+}
diff --git a/src/Ryujinx.Cpu/LightningJit/Graph/RegisterMask.cs b/src/Ryujinx.Cpu/LightningJit/Graph/RegisterMask.cs
new file mode 100644
index 00000000..cde324d5
--- /dev/null
+++ b/src/Ryujinx.Cpu/LightningJit/Graph/RegisterMask.cs
@@ -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);
+ }
+ }
+}
diff --git a/src/Ryujinx.Cpu/LightningJit/Graph/RegisterUse.cs b/src/Ryujinx.Cpu/LightningJit/Graph/RegisterUse.cs
new file mode 100644
index 00000000..83ef4192
--- /dev/null
+++ b/src/Ryujinx.Cpu/LightningJit/Graph/RegisterUse.cs
@@ -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))
+ {
+ }
+ }
+}