aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics/Shader/Instructions/InstEmitAlu.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Ryujinx.Graphics/Shader/Instructions/InstEmitAlu.cs')
-rw-r--r--Ryujinx.Graphics/Shader/Instructions/InstEmitAlu.cs684
1 files changed, 684 insertions, 0 deletions
diff --git a/Ryujinx.Graphics/Shader/Instructions/InstEmitAlu.cs b/Ryujinx.Graphics/Shader/Instructions/InstEmitAlu.cs
new file mode 100644
index 00000000..f7815e23
--- /dev/null
+++ b/Ryujinx.Graphics/Shader/Instructions/InstEmitAlu.cs
@@ -0,0 +1,684 @@
+using Ryujinx.Graphics.Shader.Decoders;
+using Ryujinx.Graphics.Shader.IntermediateRepresentation;
+using Ryujinx.Graphics.Shader.Translation;
+using System;
+
+using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper;
+using static Ryujinx.Graphics.Shader.Instructions.InstEmitAluHelper;
+using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
+
+namespace Ryujinx.Graphics.Shader.Instructions
+{
+ static partial class InstEmit
+ {
+ public static void Bfe(EmitterContext context)
+ {
+ OpCodeAlu op = (OpCodeAlu)context.CurrOp;
+
+ bool isReverse = op.RawOpCode.Extract(40);
+ bool isSigned = op.RawOpCode.Extract(48);
+
+ Operand srcA = GetSrcA(context);
+ Operand srcB = GetSrcB(context);
+
+ if (isReverse)
+ {
+ srcA = context.BitfieldReverse(srcA);
+ }
+
+ Operand position = context.BitwiseAnd(srcB, Const(0xff));
+
+ Operand size = context.BitfieldExtractU32(srcB, Const(8), Const(8));
+
+ Operand res = isSigned
+ ? context.BitfieldExtractS32(srcA, position, size)
+ : context.BitfieldExtractU32(srcA, position, size);
+
+ context.Copy(GetDest(context), res);
+
+ //TODO: CC, X, corner cases
+ }
+
+ public static void Iadd(EmitterContext context)
+ {
+ OpCodeAlu op = (OpCodeAlu)context.CurrOp;
+
+ bool negateA = false, negateB = false;
+
+ if (!(op is OpCodeAluImm32))
+ {
+ negateB = op.RawOpCode.Extract(48);
+ negateA = op.RawOpCode.Extract(49);
+ }
+
+ Operand srcA = context.INegate(GetSrcA(context), negateA);
+ Operand srcB = context.INegate(GetSrcB(context), negateB);
+
+ Operand res = context.IAdd(srcA, srcB);
+
+ bool isSubtraction = negateA || negateB;
+
+ if (op.Extended)
+ {
+ //Add carry, or subtract borrow.
+ res = context.IAdd(res, isSubtraction
+ ? context.BitwiseNot(GetCF(context))
+ : context.BitwiseAnd(GetCF(context), Const(1)));
+ }
+
+ SetIaddFlags(context, res, srcA, srcB, op.SetCondCode, op.Extended, isSubtraction);
+
+ context.Copy(GetDest(context), res);
+ }
+
+ public static void Iadd3(EmitterContext context)
+ {
+ OpCodeAlu op = (OpCodeAlu)context.CurrOp;
+
+ IntegerHalfPart partC = (IntegerHalfPart)op.RawOpCode.Extract(31, 2);
+ IntegerHalfPart partB = (IntegerHalfPart)op.RawOpCode.Extract(33, 2);
+ IntegerHalfPart partA = (IntegerHalfPart)op.RawOpCode.Extract(35, 2);
+
+ IntegerShift mode = (IntegerShift)op.RawOpCode.Extract(37, 2);
+
+ bool negateC = op.RawOpCode.Extract(49);
+ bool negateB = op.RawOpCode.Extract(50);
+ bool negateA = op.RawOpCode.Extract(51);
+
+ Operand Extend(Operand src, IntegerHalfPart part)
+ {
+ if (!(op is OpCodeAluReg) || part == IntegerHalfPart.B32)
+ {
+ return src;
+ }
+
+ if (part == IntegerHalfPart.H0)
+ {
+ return context.BitwiseAnd(src, Const(0xffff));
+ }
+ else if (part == IntegerHalfPart.H1)
+ {
+ return context.ShiftRightU32(src, Const(16));
+ }
+ else
+ {
+ //TODO: Warning.
+ }
+
+ return src;
+ }
+
+ Operand srcA = context.INegate(Extend(GetSrcA(context), partA), negateA);
+ Operand srcB = context.INegate(Extend(GetSrcB(context), partB), negateB);
+ Operand srcC = context.INegate(Extend(GetSrcC(context), partC), negateC);
+
+ Operand res = context.IAdd(srcA, srcB);
+
+ if (op is OpCodeAluReg && mode != IntegerShift.NoShift)
+ {
+ if (mode == IntegerShift.ShiftLeft)
+ {
+ res = context.ShiftLeft(res, Const(16));
+ }
+ else if (mode == IntegerShift.ShiftRight)
+ {
+ res = context.ShiftRightU32(res, Const(16));
+ }
+ else
+ {
+ //TODO: Warning.
+ }
+ }
+
+ res = context.IAdd(res, srcC);
+
+ context.Copy(GetDest(context), res);
+
+ //TODO: CC, X, corner cases
+ }
+
+ public static void Imnmx(EmitterContext context)
+ {
+ OpCodeAlu op = (OpCodeAlu)context.CurrOp;
+
+ bool isSignedInt = op.RawOpCode.Extract(48);
+
+ Operand srcA = GetSrcA(context);
+ Operand srcB = GetSrcB(context);
+
+ Operand resMin = isSignedInt
+ ? context.IMinimumS32(srcA, srcB)
+ : context.IMinimumU32(srcA, srcB);
+
+ Operand resMax = isSignedInt
+ ? context.IMaximumS32(srcA, srcB)
+ : context.IMaximumU32(srcA, srcB);
+
+ Operand pred = GetPredicate39(context);
+
+ Operand dest = GetDest(context);
+
+ context.Copy(dest, context.ConditionalSelect(pred, resMin, resMax));
+
+ SetZnFlags(context, dest, op.SetCondCode);
+
+ //TODO: X flags.
+ }
+
+ public static void Iscadd(EmitterContext context)
+ {
+ OpCodeAlu op = (OpCodeAlu)context.CurrOp;
+
+ bool negateA = false, negateB = false;
+
+ if (!(op is OpCodeAluImm32))
+ {
+ negateB = op.RawOpCode.Extract(48);
+ negateA = op.RawOpCode.Extract(49);
+ }
+
+ int shift = op is OpCodeAluImm32
+ ? op.RawOpCode.Extract(53, 5)
+ : op.RawOpCode.Extract(39, 5);
+
+ Operand srcA = GetSrcA(context);
+ Operand srcB = GetSrcB(context);
+
+ srcA = context.ShiftLeft(srcA, Const(shift));
+
+ srcA = context.INegate(srcA, negateA);
+ srcB = context.INegate(srcB, negateB);
+
+ Operand res = context.IAdd(srcA, srcB);
+
+ context.Copy(GetDest(context), res);
+
+ //TODO: CC, X
+ }
+
+ public static void Iset(EmitterContext context)
+ {
+ OpCodeSet op = (OpCodeSet)context.CurrOp;
+
+ bool boolFloat = op.RawOpCode.Extract(44);
+ bool isSigned = op.RawOpCode.Extract(48);
+
+ IntegerCondition cmpOp = (IntegerCondition)op.RawOpCode.Extract(49, 3);
+
+ Operand srcA = GetSrcA(context);
+ Operand srcB = GetSrcB(context);
+
+ Operand res = GetIntComparison(context, cmpOp, srcA, srcB, isSigned);
+
+ Operand pred = GetPredicate39(context);
+
+ res = GetPredLogicalOp(context, op.LogicalOp, res, pred);
+
+ Operand dest = GetDest(context);
+
+ if (boolFloat)
+ {
+ context.Copy(dest, context.ConditionalSelect(res, ConstF(1), Const(0)));
+ }
+ else
+ {
+ context.Copy(dest, res);
+ }
+
+ //TODO: CC, X
+ }
+
+ public static void Isetp(EmitterContext context)
+ {
+ OpCodeSet op = (OpCodeSet)context.CurrOp;
+
+ bool isSigned = op.RawOpCode.Extract(48);
+
+ IntegerCondition cmpOp = (IntegerCondition)op.RawOpCode.Extract(49, 3);
+
+ Operand srcA = GetSrcA(context);
+ Operand srcB = GetSrcB(context);
+
+ Operand p0Res = GetIntComparison(context, cmpOp, srcA, srcB, isSigned);
+
+ Operand p1Res = context.BitwiseNot(p0Res);
+
+ Operand pred = GetPredicate39(context);
+
+ p0Res = GetPredLogicalOp(context, op.LogicalOp, p0Res, pred);
+ p1Res = GetPredLogicalOp(context, op.LogicalOp, p1Res, pred);
+
+ context.Copy(Register(op.Predicate3), p0Res);
+ context.Copy(Register(op.Predicate0), p1Res);
+ }
+
+ public static void Lop(EmitterContext context)
+ {
+ IOpCodeLop op = (IOpCodeLop)context.CurrOp;
+
+ Operand srcA = context.BitwiseNot(GetSrcA(context), op.InvertA);
+ Operand srcB = context.BitwiseNot(GetSrcB(context), op.InvertB);
+
+ Operand res = srcB;
+
+ switch (op.LogicalOp)
+ {
+ case LogicalOperation.And: res = context.BitwiseAnd (srcA, srcB); break;
+ case LogicalOperation.Or: res = context.BitwiseOr (srcA, srcB); break;
+ case LogicalOperation.ExclusiveOr: res = context.BitwiseExclusiveOr(srcA, srcB); break;
+ }
+
+ EmitLopPredWrite(context, op, res);
+
+ Operand dest = GetDest(context);
+
+ context.Copy(dest, res);
+
+ SetZnFlags(context, dest, op.SetCondCode, op.Extended);
+ }
+
+ public static void Lop3(EmitterContext context)
+ {
+ IOpCodeLop op = (IOpCodeLop)context.CurrOp;
+
+ Operand srcA = GetSrcA(context);
+ Operand srcB = GetSrcB(context);
+ Operand srcC = GetSrcC(context);
+
+ bool regVariant = op is OpCodeLopReg;
+
+ int truthTable = regVariant
+ ? op.RawOpCode.Extract(28, 8)
+ : op.RawOpCode.Extract(48, 8);
+
+ Operand res = Lop3Expression.GetFromTruthTable(context, srcA, srcB, srcC, truthTable);
+
+ if (regVariant)
+ {
+ EmitLopPredWrite(context, op, res);
+ }
+
+ Operand dest = GetDest(context);
+
+ context.Copy(dest, res);
+
+ SetZnFlags(context, dest, op.SetCondCode, op.Extended);
+ }
+
+ public static void Psetp(EmitterContext context)
+ {
+ OpCodePsetp op = (OpCodePsetp)context.CurrOp;
+
+ bool invertA = op.RawOpCode.Extract(15);
+ bool invertB = op.RawOpCode.Extract(32);
+
+ Operand srcA = context.BitwiseNot(Register(op.Predicate12), invertA);
+ Operand srcB = context.BitwiseNot(Register(op.Predicate29), invertB);
+
+ Operand p0Res = GetPredLogicalOp(context, op.LogicalOpAB, srcA, srcB);
+
+ Operand p1Res = context.BitwiseNot(p0Res);
+
+ Operand pred = GetPredicate39(context);
+
+ p0Res = GetPredLogicalOp(context, op.LogicalOp, p0Res, pred);
+ p1Res = GetPredLogicalOp(context, op.LogicalOp, p1Res, pred);
+
+ context.Copy(Register(op.Predicate3), p0Res);
+ context.Copy(Register(op.Predicate0), p1Res);
+ }
+
+ public static void Rro(EmitterContext context)
+ {
+ //This is the range reduction operator,
+ //we translate it as a simple move, as it
+ //should be always followed by a matching
+ //MUFU instruction.
+ OpCodeAlu op = (OpCodeAlu)context.CurrOp;
+
+ bool negateB = op.RawOpCode.Extract(45);
+ bool absoluteB = op.RawOpCode.Extract(49);
+
+ Operand srcB = GetSrcB(context);
+
+ srcB = context.FPAbsNeg(srcB, absoluteB, negateB);
+
+ context.Copy(GetDest(context), srcB);
+ }
+
+ public static void Shl(EmitterContext context)
+ {
+ OpCodeAlu op = (OpCodeAlu)context.CurrOp;
+
+ bool isMasked = op.RawOpCode.Extract(39);
+
+ Operand srcB = GetSrcB(context);
+
+ if (isMasked)
+ {
+ srcB = context.BitwiseAnd(srcB, Const(0x1f));
+ }
+
+ Operand res = context.ShiftLeft(GetSrcA(context), srcB);
+
+ if (!isMasked)
+ {
+ //Clamped shift value.
+ Operand isLessThan32 = context.ICompareLessUnsigned(srcB, Const(32));
+
+ res = context.ConditionalSelect(isLessThan32, res, Const(0));
+ }
+
+ //TODO: X, CC
+
+ context.Copy(GetDest(context), res);
+ }
+
+ public static void Shr(EmitterContext context)
+ {
+ OpCodeAlu op = (OpCodeAlu)context.CurrOp;
+
+ bool isMasked = op.RawOpCode.Extract(39);
+ bool isReverse = op.RawOpCode.Extract(40);
+ bool isSigned = op.RawOpCode.Extract(48);
+
+ Operand srcA = GetSrcA(context);
+ Operand srcB = GetSrcB(context);
+
+ if (isReverse)
+ {
+ srcA = context.BitfieldReverse(srcA);
+ }
+
+ if (isMasked)
+ {
+ srcB = context.BitwiseAnd(srcB, Const(0x1f));
+ }
+
+ Operand res = isSigned
+ ? context.ShiftRightS32(srcA, srcB)
+ : context.ShiftRightU32(srcA, srcB);
+
+ if (!isMasked)
+ {
+ //Clamped shift value.
+ Operand resShiftBy32;
+
+ if (isSigned)
+ {
+ resShiftBy32 = context.ShiftRightS32(srcA, Const(31));
+ }
+ else
+ {
+ resShiftBy32 = Const(0);
+ }
+
+ Operand isLessThan32 = context.ICompareLessUnsigned(srcB, Const(32));
+
+ res = context.ConditionalSelect(isLessThan32, res, resShiftBy32);
+ }
+
+ //TODO: X, CC
+
+ context.Copy(GetDest(context), res);
+ }
+
+ public static void Xmad(EmitterContext context)
+ {
+ OpCodeAlu op = (OpCodeAlu)context.CurrOp;
+
+ bool signedA = context.CurrOp.RawOpCode.Extract(48);
+ bool signedB = context.CurrOp.RawOpCode.Extract(49);
+ bool highA = context.CurrOp.RawOpCode.Extract(53);
+ bool highB = false;
+
+ XmadCMode mode;
+
+ if (op is OpCodeAluReg)
+ {
+ highB = context.CurrOp.RawOpCode.Extract(35);
+
+ mode = (XmadCMode)context.CurrOp.RawOpCode.Extract(50, 3);
+ }
+ else
+ {
+ mode = (XmadCMode)context.CurrOp.RawOpCode.Extract(50, 2);
+
+ if (!(op is OpCodeAluImm))
+ {
+ highB = context.CurrOp.RawOpCode.Extract(52);
+ }
+ }
+
+ Operand srcA = GetSrcA(context);
+ Operand srcB = GetSrcB(context);
+ Operand srcC = GetSrcC(context);
+
+ //XMAD immediates are 16-bits unsigned integers.
+ if (srcB.Type == OperandType.Constant)
+ {
+ srcB = Const(srcB.Value & 0xffff);
+ }
+
+ Operand Extend16To32(Operand src, bool high, bool signed)
+ {
+ if (signed && high)
+ {
+ return context.ShiftRightS32(src, Const(16));
+ }
+ else if (signed)
+ {
+ return context.BitfieldExtractS32(src, Const(0), Const(16));
+ }
+ else if (high)
+ {
+ return context.ShiftRightU32(src, Const(16));
+ }
+ else
+ {
+ return context.BitwiseAnd(src, Const(0xffff));
+ }
+ }
+
+ srcA = Extend16To32(srcA, highA, signedA);
+ srcB = Extend16To32(srcB, highB, signedB);
+
+ bool productShiftLeft = false;
+ bool merge = false;
+
+ if (!(op is OpCodeAluRegCbuf))
+ {
+ productShiftLeft = context.CurrOp.RawOpCode.Extract(36);
+ merge = context.CurrOp.RawOpCode.Extract(37);
+ }
+
+ bool extended;
+
+ if ((op is OpCodeAluReg) || (op is OpCodeAluImm))
+ {
+ extended = context.CurrOp.RawOpCode.Extract(38);
+ }
+ else
+ {
+ extended = context.CurrOp.RawOpCode.Extract(54);
+ }
+
+ Operand res = context.IMultiply(srcA, srcB);
+
+ if (productShiftLeft)
+ {
+ res = context.ShiftLeft(res, Const(16));
+ }
+
+ switch (mode)
+ {
+ case XmadCMode.Cfull: break;
+
+ case XmadCMode.Clo: srcC = Extend16To32(srcC, high: false, signed: false); break;
+ case XmadCMode.Chi: srcC = Extend16To32(srcC, high: true, signed: false); break;
+
+ case XmadCMode.Cbcc:
+ {
+ srcC = context.IAdd(srcC, context.ShiftLeft(GetSrcB(context), Const(16)));
+
+ break;
+ }
+
+ case XmadCMode.Csfu:
+ {
+ Operand signAdjustA = context.ShiftLeft(context.ShiftRightU32(srcA, Const(31)), Const(16));
+ Operand signAdjustB = context.ShiftLeft(context.ShiftRightU32(srcB, Const(31)), Const(16));
+
+ srcC = context.ISubtract(srcC, context.IAdd(signAdjustA, signAdjustB));
+
+ break;
+ }
+
+ default: /* TODO: Warning */ break;
+ }
+
+ Operand product = res;
+
+ if (extended)
+ {
+ //Add with carry.
+ res = context.IAdd(res, context.BitwiseAnd(GetCF(context), Const(1)));
+ }
+ else
+ {
+ //Add (no carry in).
+ res = context.IAdd(res, srcC);
+ }
+
+ SetIaddFlags(context, res, product, srcC, op.SetCondCode, extended);
+
+ if (merge)
+ {
+ res = context.BitwiseAnd(res, Const(0xffff));
+ res = context.BitwiseOr(res, context.ShiftLeft(GetSrcB(context), Const(16)));
+ }
+
+ context.Copy(GetDest(context), res);
+ }
+
+ private static Operand GetIntComparison(
+ EmitterContext context,
+ IntegerCondition cond,
+ Operand srcA,
+ Operand srcB,
+ bool isSigned)
+ {
+ Operand res;
+
+ if (cond == IntegerCondition.Always)
+ {
+ res = Const(IrConsts.True);
+ }
+ else if (cond == IntegerCondition.Never)
+ {
+ res = Const(IrConsts.False);
+ }
+ else
+ {
+ Instruction inst;
+
+ switch (cond)
+ {
+ case IntegerCondition.Less: inst = Instruction.CompareLessU32; break;
+ case IntegerCondition.Equal: inst = Instruction.CompareEqual; break;
+ case IntegerCondition.LessOrEqual: inst = Instruction.CompareLessOrEqualU32; break;
+ case IntegerCondition.Greater: inst = Instruction.CompareGreaterU32; break;
+ case IntegerCondition.NotEqual: inst = Instruction.CompareNotEqual; break;
+ case IntegerCondition.GreaterOrEqual: inst = Instruction.CompareGreaterOrEqualU32; break;
+
+ default: throw new InvalidOperationException($"Unexpected condition \"{cond}\".");
+ }
+
+ if (isSigned)
+ {
+ switch (cond)
+ {
+ case IntegerCondition.Less: inst = Instruction.CompareLess; break;
+ case IntegerCondition.LessOrEqual: inst = Instruction.CompareLessOrEqual; break;
+ case IntegerCondition.Greater: inst = Instruction.CompareGreater; break;
+ case IntegerCondition.GreaterOrEqual: inst = Instruction.CompareGreaterOrEqual; break;
+ }
+ }
+
+ res = context.Add(inst, Local(), srcA, srcB);
+ }
+
+ return res;
+ }
+
+ private static void EmitLopPredWrite(EmitterContext context, IOpCodeLop op, Operand result)
+ {
+ if (op is OpCodeLop opLop && !opLop.Predicate48.IsPT)
+ {
+ Operand pRes;
+
+ if (opLop.CondOp == ConditionalOperation.False)
+ {
+ pRes = Const(IrConsts.False);
+ }
+ else if (opLop.CondOp == ConditionalOperation.True)
+ {
+ pRes = Const(IrConsts.True);
+ }
+ else if (opLop.CondOp == ConditionalOperation.Zero)
+ {
+ pRes = context.ICompareEqual(result, Const(0));
+ }
+ else /* if (opLop.CondOp == ConditionalOperation.NotZero) */
+ {
+ pRes = context.ICompareNotEqual(result, Const(0));
+ }
+
+ context.Copy(Register(opLop.Predicate48), pRes);
+ }
+ }
+
+ private static void SetIaddFlags(
+ EmitterContext context,
+ Operand res,
+ Operand srcA,
+ Operand srcB,
+ bool setCC,
+ bool extended,
+ bool isSubtraction = false)
+ {
+ if (!setCC)
+ {
+ return;
+ }
+
+ if (!extended || isSubtraction)
+ {
+ //C = d < a
+ context.Copy(GetCF(context), context.ICompareLessUnsigned(res, srcA));
+ }
+ else
+ {
+ //C = (d == a && CIn) || d < a
+ Operand tempC0 = context.ICompareEqual (res, srcA);
+ Operand tempC1 = context.ICompareLessUnsigned(res, srcA);
+
+ tempC0 = context.BitwiseAnd(tempC0, GetCF(context));
+
+ context.Copy(GetCF(context), context.BitwiseOr(tempC0, tempC1));
+ }
+
+ //V = (d ^ a) & ~(a ^ b) < 0
+ Operand tempV0 = context.BitwiseExclusiveOr(res, srcA);
+ Operand tempV1 = context.BitwiseExclusiveOr(srcA, srcB);
+
+ tempV1 = context.BitwiseNot(tempV1);
+
+ Operand tempV = context.BitwiseAnd(tempV0, tempV1);
+
+ context.Copy(GetVF(context), context.ICompareLess(tempV, Const(0)));
+
+ SetZnFlags(context, res, setCC: true, extended: extended);
+ }
+ }
+} \ No newline at end of file