327 lines
12 KiB
C#
327 lines
12 KiB
C#
|
using Ryujinx.Graphics.Shader.Decoders;
|
||
|
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||
|
using Ryujinx.Graphics.Shader.Translation;
|
||
|
using System;
|
||
|
|
||
|
using static Ryujinx.Graphics.Shader.Instructions.InstEmitAluHelper;
|
||
|
using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper;
|
||
|
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
|
||
|
|
||
|
namespace Ryujinx.Graphics.Shader.Instructions
|
||
|
{
|
||
|
static partial class InstEmit
|
||
|
{
|
||
|
public static void Csetp(EmitterContext context)
|
||
|
{
|
||
|
InstCsetp op = context.GetOp<InstCsetp>();
|
||
|
|
||
|
// TODO: Implement that properly.
|
||
|
|
||
|
Operand p0Res = Const(IrConsts.True);
|
||
|
Operand p1Res = context.BitwiseNot(p0Res);
|
||
|
Operand srcPred = GetPredicate(context, op.SrcPred, op.SrcPredInv);
|
||
|
|
||
|
p0Res = GetPredLogicalOp(context, op.Bop, p0Res, srcPred);
|
||
|
p1Res = GetPredLogicalOp(context, op.Bop, p1Res, srcPred);
|
||
|
|
||
|
context.Copy(Register(op.DestPred, RegisterType.Predicate), p0Res);
|
||
|
context.Copy(Register(op.DestPredInv, RegisterType.Predicate), p1Res);
|
||
|
}
|
||
|
|
||
|
public static void IcmpR(EmitterContext context)
|
||
|
{
|
||
|
InstIcmpR op = context.GetOp<InstIcmpR>();
|
||
|
|
||
|
var srcA = GetSrcReg(context, op.SrcA);
|
||
|
var srcB = GetSrcReg(context, op.SrcB);
|
||
|
var srcC = GetSrcReg(context, op.SrcC);
|
||
|
|
||
|
EmitIcmp(context, op.IComp, srcA, srcB, srcC, op.Dest, op.Signed);
|
||
|
}
|
||
|
|
||
|
public static void IcmpI(EmitterContext context)
|
||
|
{
|
||
|
InstIcmpI op = context.GetOp<InstIcmpI>();
|
||
|
|
||
|
var srcA = GetSrcReg(context, op.SrcA);
|
||
|
var srcB = GetSrcImm(context, Imm20ToSInt(op.Imm20));
|
||
|
var srcC = GetSrcReg(context, op.SrcC);
|
||
|
|
||
|
EmitIcmp(context, op.IComp, srcA, srcB, srcC, op.Dest, op.Signed);
|
||
|
}
|
||
|
|
||
|
public static void IcmpC(EmitterContext context)
|
||
|
{
|
||
|
InstIcmpC op = context.GetOp<InstIcmpC>();
|
||
|
|
||
|
var srcA = GetSrcReg(context, op.SrcA);
|
||
|
var srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
|
||
|
var srcC = GetSrcReg(context, op.SrcC);
|
||
|
|
||
|
EmitIcmp(context, op.IComp, srcA, srcB, srcC, op.Dest, op.Signed);
|
||
|
}
|
||
|
|
||
|
public static void IcmpRc(EmitterContext context)
|
||
|
{
|
||
|
InstIcmpRc op = context.GetOp<InstIcmpRc>();
|
||
|
|
||
|
var srcA = GetSrcReg(context, op.SrcA);
|
||
|
var srcB = GetSrcReg(context, op.SrcC);
|
||
|
var srcC = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
|
||
|
|
||
|
EmitIcmp(context, op.IComp, srcA, srcB, srcC, op.Dest, op.Signed);
|
||
|
}
|
||
|
|
||
|
public static void IsetR(EmitterContext context)
|
||
|
{
|
||
|
InstIsetR op = context.GetOp<InstIsetR>();
|
||
|
|
||
|
var srcA = GetSrcReg(context, op.SrcA);
|
||
|
var srcB = GetSrcReg(context, op.SrcB);
|
||
|
|
||
|
EmitIset(context, op.IComp, op.Bop, srcA, srcB, op.SrcPred, op.SrcPredInv, op.Dest, op.BVal, op.Signed, op.X, op.WriteCC);
|
||
|
}
|
||
|
|
||
|
public static void IsetI(EmitterContext context)
|
||
|
{
|
||
|
InstIsetI op = context.GetOp<InstIsetI>();
|
||
|
|
||
|
var srcA = GetSrcReg(context, op.SrcA);
|
||
|
var srcB = GetSrcImm(context, Imm20ToSInt(op.Imm20));
|
||
|
|
||
|
EmitIset(context, op.IComp, op.Bop, srcA, srcB, op.SrcPred, op.SrcPredInv, op.Dest, op.BVal, op.Signed, op.X, op.WriteCC);
|
||
|
}
|
||
|
|
||
|
public static void IsetC(EmitterContext context)
|
||
|
{
|
||
|
InstIsetC op = context.GetOp<InstIsetC>();
|
||
|
|
||
|
var srcA = GetSrcReg(context, op.SrcA);
|
||
|
var srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
|
||
|
|
||
|
EmitIset(context, op.IComp, op.Bop, srcA, srcB, op.SrcPred, op.SrcPredInv, op.Dest, op.BVal, op.Signed, op.X, op.WriteCC);
|
||
|
}
|
||
|
|
||
|
public static void IsetpR(EmitterContext context)
|
||
|
{
|
||
|
InstIsetpR op = context.GetOp<InstIsetpR>();
|
||
|
|
||
|
var srcA = GetSrcReg(context, op.SrcA);
|
||
|
var srcB = GetSrcReg(context, op.SrcB);
|
||
|
|
||
|
EmitIsetp(context, op.IComp, op.Bop, srcA, srcB, op.SrcPred, op.SrcPredInv, op.DestPred, op.DestPredInv, op.Signed, op.X);
|
||
|
}
|
||
|
|
||
|
public static void IsetpI(EmitterContext context)
|
||
|
{
|
||
|
InstIsetpI op = context.GetOp<InstIsetpI>();
|
||
|
|
||
|
var srcA = GetSrcReg(context, op.SrcA);
|
||
|
var srcB = GetSrcImm(context, Imm20ToSInt(op.Imm20));
|
||
|
|
||
|
EmitIsetp(context, op.IComp, op.Bop, srcA, srcB, op.SrcPred, op.SrcPredInv, op.DestPred, op.DestPredInv, op.Signed, op.X);
|
||
|
}
|
||
|
|
||
|
public static void IsetpC(EmitterContext context)
|
||
|
{
|
||
|
InstIsetpC op = context.GetOp<InstIsetpC>();
|
||
|
|
||
|
var srcA = GetSrcReg(context, op.SrcA);
|
||
|
var srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
|
||
|
|
||
|
EmitIsetp(context, op.IComp, op.Bop, srcA, srcB, op.SrcPred, op.SrcPredInv, op.DestPred, op.DestPredInv, op.Signed, op.X);
|
||
|
}
|
||
|
|
||
|
private static void EmitIcmp(
|
||
|
EmitterContext context,
|
||
|
IComp cmpOp,
|
||
|
Operand srcA,
|
||
|
Operand srcB,
|
||
|
Operand srcC,
|
||
|
int rd,
|
||
|
bool isSigned)
|
||
|
{
|
||
|
Operand cmpRes = GetIntComparison(context, cmpOp, srcC, Const(0), isSigned);
|
||
|
|
||
|
Operand res = context.ConditionalSelect(cmpRes, srcA, srcB);
|
||
|
|
||
|
context.Copy(GetDest(rd), res);
|
||
|
}
|
||
|
|
||
|
private static void EmitIset(
|
||
|
EmitterContext context,
|
||
|
IComp cmpOp,
|
||
|
BoolOp logicOp,
|
||
|
Operand srcA,
|
||
|
Operand srcB,
|
||
|
int srcPred,
|
||
|
bool srcPredInv,
|
||
|
int rd,
|
||
|
bool boolFloat,
|
||
|
bool isSigned,
|
||
|
bool extended,
|
||
|
bool writeCC)
|
||
|
{
|
||
|
Operand res = GetIntComparison(context, cmpOp, srcA, srcB, isSigned, extended);
|
||
|
Operand pred = GetPredicate(context, srcPred, srcPredInv);
|
||
|
|
||
|
res = GetPredLogicalOp(context, logicOp, res, pred);
|
||
|
|
||
|
Operand dest = GetDest(rd);
|
||
|
|
||
|
if (boolFloat)
|
||
|
{
|
||
|
res = context.ConditionalSelect(res, ConstF(1), Const(0));
|
||
|
|
||
|
context.Copy(dest, res);
|
||
|
|
||
|
SetFPZnFlags(context, res, writeCC);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
context.Copy(dest, res);
|
||
|
|
||
|
SetZnFlags(context, res, writeCC, extended);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static void EmitIsetp(
|
||
|
EmitterContext context,
|
||
|
IComp cmpOp,
|
||
|
BoolOp logicOp,
|
||
|
Operand srcA,
|
||
|
Operand srcB,
|
||
|
int srcPred,
|
||
|
bool srcPredInv,
|
||
|
int destPred,
|
||
|
int destPredInv,
|
||
|
bool isSigned,
|
||
|
bool extended)
|
||
|
{
|
||
|
Operand p0Res = GetIntComparison(context, cmpOp, srcA, srcB, isSigned, extended);
|
||
|
Operand p1Res = context.BitwiseNot(p0Res);
|
||
|
Operand pred = GetPredicate(context, srcPred, srcPredInv);
|
||
|
|
||
|
p0Res = GetPredLogicalOp(context, logicOp, p0Res, pred);
|
||
|
p1Res = GetPredLogicalOp(context, logicOp, p1Res, pred);
|
||
|
|
||
|
context.Copy(Register(destPred, RegisterType.Predicate), p0Res);
|
||
|
context.Copy(Register(destPredInv, RegisterType.Predicate), p1Res);
|
||
|
}
|
||
|
|
||
|
private static Operand GetIntComparison(
|
||
|
EmitterContext context,
|
||
|
IComp cond,
|
||
|
Operand srcA,
|
||
|
Operand srcB,
|
||
|
bool isSigned,
|
||
|
bool extended)
|
||
|
{
|
||
|
return extended
|
||
|
? GetIntComparisonExtended(context, cond, srcA, srcB, isSigned)
|
||
|
: GetIntComparison(context, cond, srcA, srcB, isSigned);
|
||
|
}
|
||
|
|
||
|
private static Operand GetIntComparisonExtended(EmitterContext context, IComp cond, Operand srcA, Operand srcB, bool isSigned)
|
||
|
{
|
||
|
Operand res;
|
||
|
|
||
|
if (cond == IComp.T)
|
||
|
{
|
||
|
res = Const(IrConsts.True);
|
||
|
}
|
||
|
else if (cond == IComp.F)
|
||
|
{
|
||
|
res = Const(IrConsts.False);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
res = context.ISubtract(srcA, srcB);
|
||
|
res = context.IAdd(res, context.BitwiseNot(GetCF()));
|
||
|
|
||
|
switch (cond)
|
||
|
{
|
||
|
case IComp.Eq: // r = xh == yh && xl == yl
|
||
|
res = context.BitwiseAnd(context.ICompareEqual(srcA, srcB), GetZF());
|
||
|
break;
|
||
|
case IComp.Lt: // r = xh < yh || (xh == yh && xl < yl)
|
||
|
Operand notC = context.BitwiseNot(GetCF());
|
||
|
Operand prevLt = context.BitwiseAnd(context.ICompareEqual(srcA, srcB), notC);
|
||
|
res = isSigned
|
||
|
? context.BitwiseOr(context.ICompareLess(srcA, srcB), prevLt)
|
||
|
: context.BitwiseOr(context.ICompareLessUnsigned(srcA, srcB), prevLt);
|
||
|
break;
|
||
|
case IComp.Le: // r = xh < yh || (xh == yh && xl <= yl)
|
||
|
Operand zOrNotC = context.BitwiseOr(GetZF(), context.BitwiseNot(GetCF()));
|
||
|
Operand prevLe = context.BitwiseAnd(context.ICompareEqual(srcA, srcB), zOrNotC);
|
||
|
res = isSigned
|
||
|
? context.BitwiseOr(context.ICompareLess(srcA, srcB), prevLe)
|
||
|
: context.BitwiseOr(context.ICompareLessUnsigned(srcA, srcB), prevLe);
|
||
|
break;
|
||
|
case IComp.Gt: // r = xh > yh || (xh == yh && xl > yl)
|
||
|
Operand notZAndC = context.BitwiseAnd(context.BitwiseNot(GetZF()), GetCF());
|
||
|
Operand prevGt = context.BitwiseAnd(context.ICompareEqual(srcA, srcB), notZAndC);
|
||
|
res = isSigned
|
||
|
? context.BitwiseOr(context.ICompareGreater(srcA, srcB), prevGt)
|
||
|
: context.BitwiseOr(context.ICompareGreaterUnsigned(srcA, srcB), prevGt);
|
||
|
break;
|
||
|
case IComp.Ge: // r = xh > yh || (xh == yh && xl >= yl)
|
||
|
Operand prevGe = context.BitwiseAnd(context.ICompareEqual(srcA, srcB), GetCF());
|
||
|
res = isSigned
|
||
|
? context.BitwiseOr(context.ICompareGreater(srcA, srcB), prevGe)
|
||
|
: context.BitwiseOr(context.ICompareGreaterUnsigned(srcA, srcB), prevGe);
|
||
|
break;
|
||
|
case IComp.Ne: // r = xh != yh || xl != yl
|
||
|
res = context.BitwiseOr(context.ICompareNotEqual(srcA, srcB), context.BitwiseNot(GetZF()));
|
||
|
break;
|
||
|
default:
|
||
|
throw new ArgumentException($"Unexpected condition \"{cond}\".");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
private static Operand GetIntComparison(EmitterContext context, IComp cond, Operand srcA, Operand srcB, bool isSigned)
|
||
|
{
|
||
|
Operand res;
|
||
|
|
||
|
if (cond == IComp.T)
|
||
|
{
|
||
|
res = Const(IrConsts.True);
|
||
|
}
|
||
|
else if (cond == IComp.F)
|
||
|
{
|
||
|
res = Const(IrConsts.False);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
var inst = cond switch
|
||
|
{
|
||
|
IComp.Lt => Instruction.CompareLessU32,
|
||
|
IComp.Eq => Instruction.CompareEqual,
|
||
|
IComp.Le => Instruction.CompareLessOrEqualU32,
|
||
|
IComp.Gt => Instruction.CompareGreaterU32,
|
||
|
IComp.Ne => Instruction.CompareNotEqual,
|
||
|
IComp.Ge => Instruction.CompareGreaterOrEqualU32,
|
||
|
_ => throw new InvalidOperationException($"Unexpected condition \"{cond}\".")
|
||
|
};
|
||
|
|
||
|
if (isSigned)
|
||
|
{
|
||
|
switch (cond)
|
||
|
{
|
||
|
case IComp.Lt: inst = Instruction.CompareLess; break;
|
||
|
case IComp.Le: inst = Instruction.CompareLessOrEqual; break;
|
||
|
case IComp.Gt: inst = Instruction.CompareGreater; break;
|
||
|
case IComp.Ge: inst = Instruction.CompareGreaterOrEqual; break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
res = context.Add(inst, Local(), srcA, srcB);
|
||
|
}
|
||
|
|
||
|
return res;
|
||
|
}
|
||
|
}
|
||
|
}
|