aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.Shader/Translation/Optimizations
diff options
context:
space:
mode:
authorgdk <gab.dark.100@gmail.com>2019-10-13 03:02:07 -0300
committerThog <thog@protonmail.com>2020-01-09 02:13:00 +0100
commit1876b346fea647e8284a66bb6d62c38801035cff (patch)
tree6eeff094298cda84d1613dc5ec0691e51d7b35f1 /Ryujinx.Graphics.Shader/Translation/Optimizations
parentf617fb542a0e3d36012d77a4b5acbde7b08902f2 (diff)
Initial work
Diffstat (limited to 'Ryujinx.Graphics.Shader/Translation/Optimizations')
-rw-r--r--Ryujinx.Graphics.Shader/Translation/Optimizations/BranchElimination.cs64
-rw-r--r--Ryujinx.Graphics.Shader/Translation/Optimizations/ConstantFolding.cs323
-rw-r--r--Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs145
-rw-r--r--Ryujinx.Graphics.Shader/Translation/Optimizations/HalfConversion.cs47
-rw-r--r--Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs177
-rw-r--r--Ryujinx.Graphics.Shader/Translation/Optimizations/Simplification.cs147
6 files changed, 903 insertions, 0 deletions
diff --git a/Ryujinx.Graphics.Shader/Translation/Optimizations/BranchElimination.cs b/Ryujinx.Graphics.Shader/Translation/Optimizations/BranchElimination.cs
new file mode 100644
index 00000000..c87d1474
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Translation/Optimizations/BranchElimination.cs
@@ -0,0 +1,64 @@
+using Ryujinx.Graphics.Shader.IntermediateRepresentation;
+using System;
+
+namespace Ryujinx.Graphics.Shader.Translation.Optimizations
+{
+ static class BranchElimination
+ {
+ public static bool RunPass(BasicBlock block)
+ {
+ if (block.HasBranch && IsRedundantBranch((Operation)block.GetLastOp(), Next(block)))
+ {
+ block.Branch = null;
+
+ return true;
+ }
+
+ return false;
+ }
+
+ private static bool IsRedundantBranch(Operation current, BasicBlock nextBlock)
+ {
+ // Here we check that:
+ // - The current block ends with a branch.
+ // - The next block only contains a branch.
+ // - The branch on the next block is unconditional.
+ // - Both branches are jumping to the same location.
+ // In this case, the branch on the current block can be removed,
+ // as the next block is going to jump to the same place anyway.
+ if (nextBlock == null)
+ {
+ return false;
+ }
+
+ if (!(nextBlock.Operations.First?.Value is Operation next))
+ {
+ return false;
+ }
+
+ if (next.Inst != Instruction.Branch)
+ {
+ return false;
+ }
+
+ return current.Dest == next.Dest;
+ }
+
+ private static BasicBlock Next(BasicBlock block)
+ {
+ block = block.Next;
+
+ while (block != null && block.Operations.Count == 0)
+ {
+ if (block.HasBranch)
+ {
+ throw new InvalidOperationException("Found a bogus empty block that \"ends with a branch\".");
+ }
+
+ block = block.Next;
+ }
+
+ return block;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Translation/Optimizations/ConstantFolding.cs b/Ryujinx.Graphics.Shader/Translation/Optimizations/ConstantFolding.cs
new file mode 100644
index 00000000..d64579b7
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Translation/Optimizations/ConstantFolding.cs
@@ -0,0 +1,323 @@
+using Ryujinx.Graphics.Shader.Decoders;
+using Ryujinx.Graphics.Shader.IntermediateRepresentation;
+using System;
+
+using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
+
+namespace Ryujinx.Graphics.Shader.Translation.Optimizations
+{
+ static class ConstantFolding
+ {
+ public static void RunPass(Operation operation)
+ {
+ if (!AreAllSourcesConstant(operation))
+ {
+ return;
+ }
+
+ switch (operation.Inst)
+ {
+ case Instruction.Add:
+ EvaluateBinary(operation, (x, y) => x + y);
+ break;
+
+ case Instruction.BitwiseAnd:
+ EvaluateBinary(operation, (x, y) => x & y);
+ break;
+
+ case Instruction.BitwiseExclusiveOr:
+ EvaluateBinary(operation, (x, y) => x ^ y);
+ break;
+
+ case Instruction.BitwiseNot:
+ EvaluateUnary(operation, (x) => ~x);
+ break;
+
+ case Instruction.BitwiseOr:
+ EvaluateBinary(operation, (x, y) => x | y);
+ break;
+
+ case Instruction.BitfieldExtractS32:
+ BitfieldExtractS32(operation);
+ break;
+
+ case Instruction.BitfieldExtractU32:
+ BitfieldExtractU32(operation);
+ break;
+
+ case Instruction.Clamp:
+ EvaluateTernary(operation, (x, y, z) => Math.Clamp(x, y, z));
+ break;
+
+ case Instruction.ClampU32:
+ EvaluateTernary(operation, (x, y, z) => (int)Math.Clamp((uint)x, (uint)y, (uint)z));
+ break;
+
+ case Instruction.CompareEqual:
+ EvaluateBinary(operation, (x, y) => x == y);
+ break;
+
+ case Instruction.CompareGreater:
+ EvaluateBinary(operation, (x, y) => x > y);
+ break;
+
+ case Instruction.CompareGreaterOrEqual:
+ EvaluateBinary(operation, (x, y) => x >= y);
+ break;
+
+ case Instruction.CompareGreaterOrEqualU32:
+ EvaluateBinary(operation, (x, y) => (uint)x >= (uint)y);
+ break;
+
+ case Instruction.CompareGreaterU32:
+ EvaluateBinary(operation, (x, y) => (uint)x > (uint)y);
+ break;
+
+ case Instruction.CompareLess:
+ EvaluateBinary(operation, (x, y) => x < y);
+ break;
+
+ case Instruction.CompareLessOrEqual:
+ EvaluateBinary(operation, (x, y) => x <= y);
+ break;
+
+ case Instruction.CompareLessOrEqualU32:
+ EvaluateBinary(operation, (x, y) => (uint)x <= (uint)y);
+ break;
+
+ case Instruction.CompareLessU32:
+ EvaluateBinary(operation, (x, y) => (uint)x < (uint)y);
+ break;
+
+ case Instruction.CompareNotEqual:
+ EvaluateBinary(operation, (x, y) => x != y);
+ break;
+
+ case Instruction.Divide:
+ EvaluateBinary(operation, (x, y) => y != 0 ? x / y : 0);
+ break;
+
+ case Instruction.FP | Instruction.Add:
+ EvaluateFPBinary(operation, (x, y) => x + y);
+ break;
+
+ case Instruction.FP | Instruction.Clamp:
+ EvaluateFPTernary(operation, (x, y, z) => Math.Clamp(x, y, z));
+ break;
+
+ case Instruction.FP | Instruction.CompareEqual:
+ EvaluateFPBinary(operation, (x, y) => x == y);
+ break;
+
+ case Instruction.FP | Instruction.CompareGreater:
+ EvaluateFPBinary(operation, (x, y) => x > y);
+ break;
+
+ case Instruction.FP | Instruction.CompareGreaterOrEqual:
+ EvaluateFPBinary(operation, (x, y) => x >= y);
+ break;
+
+ case Instruction.FP | Instruction.CompareLess:
+ EvaluateFPBinary(operation, (x, y) => x < y);
+ break;
+
+ case Instruction.FP | Instruction.CompareLessOrEqual:
+ EvaluateFPBinary(operation, (x, y) => x <= y);
+ break;
+
+ case Instruction.FP | Instruction.CompareNotEqual:
+ EvaluateFPBinary(operation, (x, y) => x != y);
+ break;
+
+ case Instruction.FP | Instruction.Divide:
+ EvaluateFPBinary(operation, (x, y) => x / y);
+ break;
+
+ case Instruction.FP | Instruction.Multiply:
+ EvaluateFPBinary(operation, (x, y) => x * y);
+ break;
+
+ case Instruction.FP | Instruction.Negate:
+ EvaluateFPUnary(operation, (x) => -x);
+ break;
+
+ case Instruction.FP | Instruction.Subtract:
+ EvaluateFPBinary(operation, (x, y) => x - y);
+ break;
+
+ case Instruction.IsNan:
+ EvaluateFPUnary(operation, (x) => float.IsNaN(x));
+ break;
+
+ case Instruction.Maximum:
+ EvaluateBinary(operation, (x, y) => Math.Max(x, y));
+ break;
+
+ case Instruction.MaximumU32:
+ EvaluateBinary(operation, (x, y) => (int)Math.Max((uint)x, (uint)y));
+ break;
+
+ case Instruction.Minimum:
+ EvaluateBinary(operation, (x, y) => Math.Min(x, y));
+ break;
+
+ case Instruction.MinimumU32:
+ EvaluateBinary(operation, (x, y) => (int)Math.Min((uint)x, (uint)y));
+ break;
+
+ case Instruction.Multiply:
+ EvaluateBinary(operation, (x, y) => x * y);
+ break;
+
+ case Instruction.Negate:
+ EvaluateUnary(operation, (x) => -x);
+ break;
+
+ case Instruction.ShiftLeft:
+ EvaluateBinary(operation, (x, y) => x << y);
+ break;
+
+ case Instruction.ShiftRightS32:
+ EvaluateBinary(operation, (x, y) => x >> y);
+ break;
+
+ case Instruction.ShiftRightU32:
+ EvaluateBinary(operation, (x, y) => (int)((uint)x >> y));
+ break;
+
+ case Instruction.Subtract:
+ EvaluateBinary(operation, (x, y) => x - y);
+ break;
+
+ case Instruction.UnpackHalf2x16:
+ UnpackHalf2x16(operation);
+ break;
+ }
+ }
+
+ private static bool AreAllSourcesConstant(Operation operation)
+ {
+ for (int index = 0; index < operation.SourcesCount; index++)
+ {
+ if (operation.GetSource(index).Type != OperandType.Constant)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private static void BitfieldExtractS32(Operation operation)
+ {
+ int value = GetBitfieldExtractValue(operation);
+
+ int shift = 32 - operation.GetSource(2).Value;
+
+ value = (value << shift) >> shift;
+
+ operation.TurnIntoCopy(Const(value));
+ }
+
+ private static void BitfieldExtractU32(Operation operation)
+ {
+ operation.TurnIntoCopy(Const(GetBitfieldExtractValue(operation)));
+ }
+
+ private static int GetBitfieldExtractValue(Operation operation)
+ {
+ int value = operation.GetSource(0).Value;
+ int lsb = operation.GetSource(1).Value;
+ int length = operation.GetSource(2).Value;
+
+ return value.Extract(lsb, length);
+ }
+
+ private static void UnpackHalf2x16(Operation operation)
+ {
+ int value = operation.GetSource(0).Value;
+
+ value = (value >> operation.ComponentIndex * 16) & 0xffff;
+
+ operation.TurnIntoCopy(ConstF(HalfConversion.HalfToSingle(value)));
+ }
+
+ private static void FPNegate(Operation operation)
+ {
+ float value = operation.GetSource(0).AsFloat();
+
+ operation.TurnIntoCopy(ConstF(-value));
+ }
+
+ private static void EvaluateUnary(Operation operation, Func<int, int> op)
+ {
+ int x = operation.GetSource(0).Value;
+
+ operation.TurnIntoCopy(Const(op(x)));
+ }
+
+ private static void EvaluateFPUnary(Operation operation, Func<float, float> op)
+ {
+ float x = operation.GetSource(0).AsFloat();
+
+ operation.TurnIntoCopy(ConstF(op(x)));
+ }
+
+ private static void EvaluateFPUnary(Operation operation, Func<float, bool> op)
+ {
+ float x = operation.GetSource(0).AsFloat();
+
+ operation.TurnIntoCopy(Const(op(x) ? IrConsts.True : IrConsts.False));
+ }
+
+ private static void EvaluateBinary(Operation operation, Func<int, int, int> op)
+ {
+ int x = operation.GetSource(0).Value;
+ int y = operation.GetSource(1).Value;
+
+ operation.TurnIntoCopy(Const(op(x, y)));
+ }
+
+ private static void EvaluateBinary(Operation operation, Func<int, int, bool> op)
+ {
+ int x = operation.GetSource(0).Value;
+ int y = operation.GetSource(1).Value;
+
+ operation.TurnIntoCopy(Const(op(x, y) ? IrConsts.True : IrConsts.False));
+ }
+
+ private static void EvaluateFPBinary(Operation operation, Func<float, float, float> op)
+ {
+ float x = operation.GetSource(0).AsFloat();
+ float y = operation.GetSource(1).AsFloat();
+
+ operation.TurnIntoCopy(ConstF(op(x, y)));
+ }
+
+ private static void EvaluateFPBinary(Operation operation, Func<float, float, bool> op)
+ {
+ float x = operation.GetSource(0).AsFloat();
+ float y = operation.GetSource(1).AsFloat();
+
+ operation.TurnIntoCopy(Const(op(x, y) ? IrConsts.True : IrConsts.False));
+ }
+
+ private static void EvaluateTernary(Operation operation, Func<int, int, int, int> op)
+ {
+ int x = operation.GetSource(0).Value;
+ int y = operation.GetSource(1).Value;
+ int z = operation.GetSource(2).Value;
+
+ operation.TurnIntoCopy(Const(op(x, y, z)));
+ }
+
+ private static void EvaluateFPTernary(Operation operation, Func<float, float, float, float> op)
+ {
+ float x = operation.GetSource(0).AsFloat();
+ float y = operation.GetSource(1).AsFloat();
+ float z = operation.GetSource(2).AsFloat();
+
+ operation.TurnIntoCopy(ConstF(op(x, y, z)));
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs b/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs
new file mode 100644
index 00000000..06db2a80
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs
@@ -0,0 +1,145 @@
+using Ryujinx.Graphics.Shader.IntermediateRepresentation;
+using System.Collections.Generic;
+
+using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
+
+namespace Ryujinx.Graphics.Shader.Translation.Optimizations
+{
+ static class GlobalToStorage
+ {
+ private const int StorageDescsBaseOffset = 0x44; // In words.
+
+ private const int UbeStorageDescsBaseOffset = 0x84; // In words.
+ private const int UbeStorageMaxCount = 14;
+
+ private const int StorageDescSize = 4; // In words.
+ private const int StorageMaxCount = 16;
+
+ private const int StorageDescsSize = StorageDescSize * StorageMaxCount;
+
+ public static void RunPass(BasicBlock block, ShaderStage stage)
+ {
+ int sbStart = GetStorageBaseCbOffset(stage);
+
+ int sbEnd = sbStart + StorageDescsSize;
+
+ // This one is only used on compute shaders.
+ // Compute shaders uses two separate sets of storage.
+ int ubeSbStart = UbeStorageDescsBaseOffset;
+ int ubeSbEnd = UbeStorageDescsBaseOffset + StorageDescSize * UbeStorageMaxCount;
+
+ for (LinkedListNode<INode> node = block.Operations.First; node != null; node = node.Next)
+ {
+ if (!(node.Value is Operation operation))
+ {
+ continue;
+ }
+
+ if (operation.Inst == Instruction.LoadGlobal ||
+ operation.Inst == Instruction.StoreGlobal)
+ {
+ Operand source = operation.GetSource(0);
+
+ if (source.AsgOp is Operation asgOperation)
+ {
+ int storageIndex = SearchForStorageBase(asgOperation, sbStart, sbEnd);
+
+ /*if (storageIndex < 0 && stage == ShaderStage.Compute)
+ {
+ storageIndex = SearchForStorageBase(asgOperation, ubeSbStart, ubeSbEnd);
+ }*/
+
+ if (storageIndex >= 0)
+ {
+ node = ReplaceGlobalWithStorage(node, storageIndex);
+ }
+ }
+ }
+ }
+ }
+
+ private static LinkedListNode<INode> ReplaceGlobalWithStorage(LinkedListNode<INode> node, int storageIndex)
+ {
+ Operation operation = (Operation)node.Value;
+
+ Operation storageOp;
+
+ if (operation.Inst == Instruction.LoadGlobal)
+ {
+ Operand source = operation.GetSource(0);
+
+ storageOp = new Operation(Instruction.LoadStorage, operation.Dest, Const(storageIndex), source);
+ }
+ else
+ {
+ Operand src1 = operation.GetSource(0);
+ Operand src2 = operation.GetSource(1);
+
+ storageOp = new Operation(Instruction.StoreStorage, null, Const(storageIndex), src1, src2);
+ }
+
+ for (int index = 0; index < operation.SourcesCount; index++)
+ {
+ operation.SetSource(index, null);
+ }
+
+ LinkedListNode<INode> oldNode = node;
+
+ node = node.List.AddAfter(node, storageOp);
+
+ node.List.Remove(oldNode);
+
+ return node;
+ }
+
+ private static int SearchForStorageBase(Operation operation, int sbStart, int sbEnd)
+ {
+ Queue<Operation> assignments = new Queue<Operation>();
+
+ assignments.Enqueue(operation);
+
+ while (assignments.TryDequeue(out operation))
+ {
+ for (int index = 0; index < operation.SourcesCount; index++)
+ {
+ Operand source = operation.GetSource(index);
+
+ if (source.Type == OperandType.ConstantBuffer)
+ {
+ int slot = source.GetCbufSlot();
+ int offset = source.GetCbufOffset();
+
+ if (slot == 0 && offset >= sbStart && offset < sbEnd)
+ {
+ int storageIndex = (offset - sbStart) / StorageDescSize;
+
+ return storageIndex;
+ }
+ }
+
+ if (source.AsgOp is Operation asgOperation)
+ {
+ assignments.Enqueue(asgOperation);
+ }
+ }
+ }
+
+ return -1;
+ }
+
+ private static int GetStorageBaseCbOffset(ShaderStage stage)
+ {
+ switch (stage)
+ {
+ case ShaderStage.Compute: return StorageDescsBaseOffset + 2 * StorageDescsSize;
+ case ShaderStage.Vertex: return StorageDescsBaseOffset;
+ case ShaderStage.TessellationControl: return StorageDescsBaseOffset + 1 * StorageDescsSize;
+ case ShaderStage.TessellationEvaluation: return StorageDescsBaseOffset + 2 * StorageDescsSize;
+ case ShaderStage.Geometry: return StorageDescsBaseOffset + 3 * StorageDescsSize;
+ case ShaderStage.Fragment: return StorageDescsBaseOffset + 4 * StorageDescsSize;
+ }
+
+ return 0;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Translation/Optimizations/HalfConversion.cs b/Ryujinx.Graphics.Shader/Translation/Optimizations/HalfConversion.cs
new file mode 100644
index 00000000..96060272
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Translation/Optimizations/HalfConversion.cs
@@ -0,0 +1,47 @@
+using System;
+
+namespace Ryujinx.Graphics.Shader.Translation.Optimizations
+{
+ static class HalfConversion
+ {
+ public static float HalfToSingle(int value)
+ {
+ int mantissa = (value >> 0) & 0x3ff;
+ int exponent = (value >> 10) & 0x1f;
+ int sign = (value >> 15) & 0x1;
+
+ if (exponent == 0x1f)
+ {
+ // NaN or Infinity.
+ mantissa <<= 13;
+ exponent = 0xff;
+ }
+ else if (exponent != 0 || mantissa != 0 )
+ {
+ if (exponent == 0)
+ {
+ // Denormal.
+ int e = -1;
+ int m = mantissa;
+
+ do
+ {
+ e++;
+ m <<= 1;
+ }
+ while ((m & 0x400) == 0);
+
+ mantissa = m & 0x3ff;
+ exponent = e;
+ }
+
+ mantissa <<= 13;
+ exponent = 127 - 15 + exponent;
+ }
+
+ int output = (sign << 31) | (exponent << 23) | mantissa;
+
+ return BitConverter.Int32BitsToSingle(output);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs b/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs
new file mode 100644
index 00000000..d5e57546
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs
@@ -0,0 +1,177 @@
+using Ryujinx.Graphics.Shader.IntermediateRepresentation;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Ryujinx.Graphics.Shader.Translation.Optimizations
+{
+ static class Optimizer
+ {
+ public static void Optimize(BasicBlock[] blocks, ShaderStage stage)
+ {
+ for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++)
+ {
+ GlobalToStorage.RunPass(blocks[blkIndex], stage);
+ }
+
+ bool modified;
+
+ do
+ {
+ modified = false;
+
+ for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++)
+ {
+ BasicBlock block = blocks[blkIndex];
+
+ LinkedListNode<INode> node = block.Operations.First;
+
+ while (node != null)
+ {
+ LinkedListNode<INode> nextNode = node.Next;
+
+ bool isUnused = IsUnused(node.Value);
+
+ if (!(node.Value is Operation operation) || isUnused)
+ {
+ if (isUnused)
+ {
+ RemoveNode(block, node);
+
+ modified = true;
+ }
+
+ node = nextNode;
+
+ continue;
+ }
+
+ ConstantFolding.RunPass(operation);
+
+ Simplification.RunPass(operation);
+
+ if (DestIsLocalVar(operation))
+ {
+ if (operation.Inst == Instruction.Copy)
+ {
+ PropagateCopy(operation);
+
+ RemoveNode(block, node);
+
+ modified = true;
+ }
+ else if (operation.Inst == Instruction.PackHalf2x16 && PropagatePack(operation))
+ {
+ if (operation.Dest.UseOps.Count == 0)
+ {
+ RemoveNode(block, node);
+ }
+
+ modified = true;
+ }
+ }
+
+ node = nextNode;
+ }
+
+ if (BranchElimination.RunPass(block))
+ {
+ RemoveNode(block, block.Operations.Last);
+
+ modified = true;
+ }
+ }
+ }
+ while (modified);
+ }
+
+ private static void PropagateCopy(Operation copyOp)
+ {
+ // Propagate copy source operand to all uses of
+ // the destination operand.
+ Operand dest = copyOp.Dest;
+ Operand src = copyOp.GetSource(0);
+
+ INode[] uses = dest.UseOps.ToArray();
+
+ foreach (INode useNode in uses)
+ {
+ for (int index = 0; index < useNode.SourcesCount; index++)
+ {
+ if (useNode.GetSource(index) == dest)
+ {
+ useNode.SetSource(index, src);
+ }
+ }
+ }
+ }
+
+ private static bool PropagatePack(Operation packOp)
+ {
+ // Propagate pack source operands to uses by unpack
+ // instruction. The source depends on the unpack instruction.
+ bool modified = false;
+
+ Operand dest = packOp.Dest;
+ Operand src0 = packOp.GetSource(0);
+ Operand src1 = packOp.GetSource(1);
+
+ INode[] uses = dest.UseOps.ToArray();
+
+ foreach (INode useNode in uses)
+ {
+ if (!(useNode is Operation operation) || operation.Inst != Instruction.UnpackHalf2x16)
+ {
+ continue;
+ }
+
+ if (operation.GetSource(0) == dest)
+ {
+ operation.TurnIntoCopy(operation.ComponentIndex == 1 ? src1 : src0);
+
+ modified = true;
+ }
+ }
+
+ return modified;
+ }
+
+ private static void RemoveNode(BasicBlock block, LinkedListNode<INode> llNode)
+ {
+ // Remove a node from the nodes list, and also remove itself
+ // from all the use lists on the operands that this node uses.
+ block.Operations.Remove(llNode);
+
+ Queue<INode> nodes = new Queue<INode>();
+
+ nodes.Enqueue(llNode.Value);
+
+ while (nodes.TryDequeue(out INode node))
+ {
+ for (int index = 0; index < node.SourcesCount; index++)
+ {
+ Operand src = node.GetSource(index);
+
+ if (src.Type != OperandType.LocalVariable)
+ {
+ continue;
+ }
+
+ if (src.UseOps.Remove(node) && src.UseOps.Count == 0)
+ {
+ nodes.Enqueue(src.AsgOp);
+ }
+ }
+ }
+ }
+
+ private static bool IsUnused(INode node)
+ {
+ return DestIsLocalVar(node) && node.Dest.UseOps.Count == 0;
+ }
+
+ private static bool DestIsLocalVar(INode node)
+ {
+ return node.Dest != null && node.Dest.Type == OperandType.LocalVariable;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Translation/Optimizations/Simplification.cs b/Ryujinx.Graphics.Shader/Translation/Optimizations/Simplification.cs
new file mode 100644
index 00000000..8d05f99a
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Translation/Optimizations/Simplification.cs
@@ -0,0 +1,147 @@
+using Ryujinx.Graphics.Shader.IntermediateRepresentation;
+
+using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
+
+namespace Ryujinx.Graphics.Shader.Translation.Optimizations
+{
+ static class Simplification
+ {
+ private const int AllOnes = ~0;
+
+ public static void RunPass(Operation operation)
+ {
+ switch (operation.Inst)
+ {
+ case Instruction.Add:
+ case Instruction.BitwiseExclusiveOr:
+ TryEliminateBinaryOpCommutative(operation, 0);
+ break;
+
+ case Instruction.BitwiseAnd:
+ TryEliminateBitwiseAnd(operation);
+ break;
+
+ case Instruction.BitwiseOr:
+ TryEliminateBitwiseOr(operation);
+ break;
+
+ case Instruction.ConditionalSelect:
+ TryEliminateConditionalSelect(operation);
+ break;
+
+ case Instruction.Divide:
+ TryEliminateBinaryOpY(operation, 1);
+ break;
+
+ case Instruction.Multiply:
+ TryEliminateBinaryOpCommutative(operation, 1);
+ break;
+
+ case Instruction.ShiftLeft:
+ case Instruction.ShiftRightS32:
+ case Instruction.ShiftRightU32:
+ case Instruction.Subtract:
+ TryEliminateBinaryOpY(operation, 0);
+ break;
+ }
+ }
+
+ private static void TryEliminateBitwiseAnd(Operation operation)
+ {
+ // Try to recognize and optimize those 3 patterns (in order):
+ // x & 0xFFFFFFFF == x, 0xFFFFFFFF & y == y,
+ // x & 0x00000000 == 0x00000000, 0x00000000 & y == 0x00000000
+ Operand x = operation.GetSource(0);
+ Operand y = operation.GetSource(1);
+
+ if (IsConstEqual(x, AllOnes))
+ {
+ operation.TurnIntoCopy(y);
+ }
+ else if (IsConstEqual(y, AllOnes))
+ {
+ operation.TurnIntoCopy(x);
+ }
+ else if (IsConstEqual(x, 0) || IsConstEqual(y, 0))
+ {
+ operation.TurnIntoCopy(Const(0));
+ }
+ }
+
+ private static void TryEliminateBitwiseOr(Operation operation)
+ {
+ // Try to recognize and optimize those 3 patterns (in order):
+ // x | 0x00000000 == x, 0x00000000 | y == y,
+ // x | 0xFFFFFFFF == 0xFFFFFFFF, 0xFFFFFFFF | y == 0xFFFFFFFF
+ Operand x = operation.GetSource(0);
+ Operand y = operation.GetSource(1);
+
+ if (IsConstEqual(x, 0))
+ {
+ operation.TurnIntoCopy(y);
+ }
+ else if (IsConstEqual(y, 0))
+ {
+ operation.TurnIntoCopy(x);
+ }
+ else if (IsConstEqual(x, AllOnes) || IsConstEqual(y, AllOnes))
+ {
+ operation.TurnIntoCopy(Const(AllOnes));
+ }
+ }
+
+ private static void TryEliminateBinaryOpY(Operation operation, int comparand)
+ {
+ Operand x = operation.GetSource(0);
+ Operand y = operation.GetSource(1);
+
+ if (IsConstEqual(y, comparand))
+ {
+ operation.TurnIntoCopy(x);
+ }
+ }
+
+ private static void TryEliminateBinaryOpCommutative(Operation operation, int comparand)
+ {
+ Operand x = operation.GetSource(0);
+ Operand y = operation.GetSource(1);
+
+ if (IsConstEqual(x, comparand))
+ {
+ operation.TurnIntoCopy(y);
+ }
+ else if (IsConstEqual(y, comparand))
+ {
+ operation.TurnIntoCopy(x);
+ }
+ }
+
+ private static void TryEliminateConditionalSelect(Operation operation)
+ {
+ Operand cond = operation.GetSource(0);
+
+ if (cond.Type != OperandType.Constant)
+ {
+ return;
+ }
+
+ // The condition is constant, we can turn it into a copy, and select
+ // the source based on the condition value.
+ int srcIndex = cond.Value != 0 ? 1 : 2;
+
+ Operand source = operation.GetSource(srcIndex);
+
+ operation.TurnIntoCopy(source);
+ }
+
+ private static bool IsConstEqual(Operand operand, int comparand)
+ {
+ if (operand.Type != OperandType.Constant)
+ {
+ return false;
+ }
+
+ return operand.Value == comparand;
+ }
+ }
+} \ No newline at end of file