mirror of
https://git.ryujinx.app/ryubing/ryujinx.git
synced 2025-08-05 17:58:28 +02:00

* Catch more cases on bindless elimination * Match blocks with the same comparison condition * Shader cache version bump
236 lines
7.2 KiB
C#
236 lines
7.2 KiB
C#
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:
|
|
TryEliminateBinaryOpCommutative(operation, 0);
|
|
break;
|
|
|
|
case Instruction.BitwiseAnd:
|
|
TryEliminateBitwiseAnd(operation);
|
|
break;
|
|
|
|
case Instruction.BitwiseExclusiveOr:
|
|
if (!TryEliminateXorSwap(operation))
|
|
{
|
|
TryEliminateBinaryOpCommutative(operation, 0);
|
|
}
|
|
break;
|
|
|
|
case Instruction.BitwiseOr:
|
|
TryEliminateBitwiseOr(operation);
|
|
break;
|
|
|
|
case Instruction.CompareNotEqual:
|
|
TryEliminateCompareNotEqual(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 bool TryEliminateXorSwap(Operation xCopyOp)
|
|
{
|
|
// Try to recognize XOR swap pattern:
|
|
// x = x ^ y
|
|
// y = x ^ y
|
|
// x = x ^ y
|
|
// Or, in SSA:
|
|
// x2 = x ^ y
|
|
// y2 = x2 ^ y
|
|
// x3 = x2 ^ y2
|
|
// Transform it into something more sane:
|
|
// temp = y
|
|
// y = x
|
|
// x = temp
|
|
|
|
// Note that because XOR is commutative, there are actually
|
|
// multiple possible combinations of this pattern, for
|
|
// simplicity this only catches one of them.
|
|
|
|
Operand x = xCopyOp.GetSource(0);
|
|
Operand y = xCopyOp.GetSource(1);
|
|
|
|
if (x.AsgOp is not Operation tCopyOp || tCopyOp.Inst != Instruction.BitwiseExclusiveOr ||
|
|
y.AsgOp is not Operation yCopyOp || yCopyOp.Inst != Instruction.BitwiseExclusiveOr)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (tCopyOp == yCopyOp)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (yCopyOp.GetSource(0) != x ||
|
|
yCopyOp.GetSource(1) != tCopyOp.GetSource(1) ||
|
|
x.UseOps.Count != 2)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
x = tCopyOp.GetSource(0);
|
|
y = tCopyOp.GetSource(1);
|
|
|
|
tCopyOp.TurnIntoCopy(y); // Temp = Y
|
|
yCopyOp.TurnIntoCopy(x); // Y = X
|
|
xCopyOp.TurnIntoCopy(tCopyOp.Dest); // X = Temp
|
|
|
|
return true;
|
|
}
|
|
|
|
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 TryEliminateCompareNotEqual(Operation operation)
|
|
{
|
|
// Comparison instruction returns 0 if the result is false, and -1 if true.
|
|
// Doing a not equal zero comparison on the result is redundant, so we can just copy the first result in this case.
|
|
|
|
Operand lhs = operation.GetSource(0);
|
|
Operand rhs = operation.GetSource(1);
|
|
|
|
if (lhs.Type == OperandType.Constant)
|
|
{
|
|
(lhs, rhs) = (rhs, lhs);
|
|
}
|
|
|
|
if (rhs.Type != OperandType.Constant || rhs.Value != 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (lhs.AsgOp is not Operation compareOp || !compareOp.Inst.IsComparison())
|
|
{
|
|
return;
|
|
}
|
|
|
|
operation.TurnIntoCopy(lhs);
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|