/ src / Ryujinx.Graphics.Shader / Instructions / InstEmitIntegerComparison.cs
InstEmitIntegerComparison.cs
  1  using Ryujinx.Graphics.Shader.Decoders;
  2  using Ryujinx.Graphics.Shader.IntermediateRepresentation;
  3  using Ryujinx.Graphics.Shader.Translation;
  4  using System;
  5  using static Ryujinx.Graphics.Shader.Instructions.InstEmitAluHelper;
  6  using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper;
  7  using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
  8  
  9  namespace Ryujinx.Graphics.Shader.Instructions
 10  {
 11      static partial class InstEmit
 12      {
 13          public static void IcmpR(EmitterContext context)
 14          {
 15              InstIcmpR op = context.GetOp<InstIcmpR>();
 16  
 17              var srcA = GetSrcReg(context, op.SrcA);
 18              var srcB = GetSrcReg(context, op.SrcB);
 19              var srcC = GetSrcReg(context, op.SrcC);
 20  
 21              EmitIcmp(context, op.IComp, srcA, srcB, srcC, op.Dest, op.Signed);
 22          }
 23  
 24          public static void IcmpI(EmitterContext context)
 25          {
 26              InstIcmpI op = context.GetOp<InstIcmpI>();
 27  
 28              var srcA = GetSrcReg(context, op.SrcA);
 29              var srcB = GetSrcImm(context, Imm20ToSInt(op.Imm20));
 30              var srcC = GetSrcReg(context, op.SrcC);
 31  
 32              EmitIcmp(context, op.IComp, srcA, srcB, srcC, op.Dest, op.Signed);
 33          }
 34  
 35          public static void IcmpC(EmitterContext context)
 36          {
 37              InstIcmpC op = context.GetOp<InstIcmpC>();
 38  
 39              var srcA = GetSrcReg(context, op.SrcA);
 40              var srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
 41              var srcC = GetSrcReg(context, op.SrcC);
 42  
 43              EmitIcmp(context, op.IComp, srcA, srcB, srcC, op.Dest, op.Signed);
 44          }
 45  
 46          public static void IcmpRc(EmitterContext context)
 47          {
 48              InstIcmpRc op = context.GetOp<InstIcmpRc>();
 49  
 50              var srcA = GetSrcReg(context, op.SrcA);
 51              var srcB = GetSrcReg(context, op.SrcC);
 52              var srcC = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
 53  
 54              EmitIcmp(context, op.IComp, srcA, srcB, srcC, op.Dest, op.Signed);
 55          }
 56  
 57          public static void IsetR(EmitterContext context)
 58          {
 59              InstIsetR op = context.GetOp<InstIsetR>();
 60  
 61              var srcA = GetSrcReg(context, op.SrcA);
 62              var srcB = GetSrcReg(context, op.SrcB);
 63  
 64              EmitIset(context, op.IComp, op.Bop, srcA, srcB, op.SrcPred, op.SrcPredInv, op.Dest, op.BVal, op.Signed, op.X, op.WriteCC);
 65          }
 66  
 67          public static void IsetI(EmitterContext context)
 68          {
 69              InstIsetI op = context.GetOp<InstIsetI>();
 70  
 71              var srcA = GetSrcReg(context, op.SrcA);
 72              var srcB = GetSrcImm(context, Imm20ToSInt(op.Imm20));
 73  
 74              EmitIset(context, op.IComp, op.Bop, srcA, srcB, op.SrcPred, op.SrcPredInv, op.Dest, op.BVal, op.Signed, op.X, op.WriteCC);
 75          }
 76  
 77          public static void IsetC(EmitterContext context)
 78          {
 79              InstIsetC op = context.GetOp<InstIsetC>();
 80  
 81              var srcA = GetSrcReg(context, op.SrcA);
 82              var srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
 83  
 84              EmitIset(context, op.IComp, op.Bop, srcA, srcB, op.SrcPred, op.SrcPredInv, op.Dest, op.BVal, op.Signed, op.X, op.WriteCC);
 85          }
 86  
 87          public static void IsetpR(EmitterContext context)
 88          {
 89              InstIsetpR op = context.GetOp<InstIsetpR>();
 90  
 91              var srcA = GetSrcReg(context, op.SrcA);
 92              var srcB = GetSrcReg(context, op.SrcB);
 93  
 94              EmitIsetp(context, op.IComp, op.Bop, srcA, srcB, op.SrcPred, op.SrcPredInv, op.DestPred, op.DestPredInv, op.Signed, op.X);
 95          }
 96  
 97          public static void IsetpI(EmitterContext context)
 98          {
 99              InstIsetpI op = context.GetOp<InstIsetpI>();
100  
101              var srcA = GetSrcReg(context, op.SrcA);
102              var srcB = GetSrcImm(context, Imm20ToSInt(op.Imm20));
103  
104              EmitIsetp(context, op.IComp, op.Bop, srcA, srcB, op.SrcPred, op.SrcPredInv, op.DestPred, op.DestPredInv, op.Signed, op.X);
105          }
106  
107          public static void IsetpC(EmitterContext context)
108          {
109              InstIsetpC op = context.GetOp<InstIsetpC>();
110  
111              var srcA = GetSrcReg(context, op.SrcA);
112              var srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
113  
114              EmitIsetp(context, op.IComp, op.Bop, srcA, srcB, op.SrcPred, op.SrcPredInv, op.DestPred, op.DestPredInv, op.Signed, op.X);
115          }
116  
117          private static void EmitIcmp(
118              EmitterContext context,
119              IComp cmpOp,
120              Operand srcA,
121              Operand srcB,
122              Operand srcC,
123              int rd,
124              bool isSigned)
125          {
126              Operand cmpRes = GetIntComparison(context, cmpOp, srcC, Const(0), isSigned);
127  
128              Operand res = context.ConditionalSelect(cmpRes, srcA, srcB);
129  
130              context.Copy(GetDest(rd), res);
131          }
132  
133          private static void EmitIset(
134              EmitterContext context,
135              IComp cmpOp,
136              BoolOp logicOp,
137              Operand srcA,
138              Operand srcB,
139              int srcPred,
140              bool srcPredInv,
141              int rd,
142              bool boolFloat,
143              bool isSigned,
144              bool extended,
145              bool writeCC)
146          {
147              Operand res = GetIntComparison(context, cmpOp, srcA, srcB, isSigned, extended);
148              Operand pred = GetPredicate(context, srcPred, srcPredInv);
149  
150              res = GetPredLogicalOp(context, logicOp, res, pred);
151  
152              Operand dest = GetDest(rd);
153  
154              if (boolFloat)
155              {
156                  res = context.ConditionalSelect(res, ConstF(1), Const(0));
157  
158                  context.Copy(dest, res);
159  
160                  SetFPZnFlags(context, res, writeCC);
161              }
162              else
163              {
164                  context.Copy(dest, res);
165  
166                  SetZnFlags(context, res, writeCC, extended);
167              }
168          }
169  
170          private static void EmitIsetp(
171              EmitterContext context,
172              IComp cmpOp,
173              BoolOp logicOp,
174              Operand srcA,
175              Operand srcB,
176              int srcPred,
177              bool srcPredInv,
178              int destPred,
179              int destPredInv,
180              bool isSigned,
181              bool extended)
182          {
183              Operand p0Res = GetIntComparison(context, cmpOp, srcA, srcB, isSigned, extended);
184              Operand p1Res = context.BitwiseNot(p0Res);
185              Operand pred = GetPredicate(context, srcPred, srcPredInv);
186  
187              p0Res = GetPredLogicalOp(context, logicOp, p0Res, pred);
188              p1Res = GetPredLogicalOp(context, logicOp, p1Res, pred);
189  
190              context.Copy(Register(destPred, RegisterType.Predicate), p0Res);
191              context.Copy(Register(destPredInv, RegisterType.Predicate), p1Res);
192          }
193  
194          private static Operand GetIntComparison(
195              EmitterContext context,
196              IComp cond,
197              Operand srcA,
198              Operand srcB,
199              bool isSigned,
200              bool extended)
201          {
202              return extended
203                  ? GetIntComparisonExtended(context, cond, srcA, srcB, isSigned)
204                  : GetIntComparison(context, cond, srcA, srcB, isSigned);
205          }
206  
207          private static Operand GetIntComparisonExtended(EmitterContext context, IComp cond, Operand srcA, Operand srcB, bool isSigned)
208          {
209              Operand res;
210  
211              if (cond == IComp.T)
212              {
213                  res = Const(IrConsts.True);
214              }
215              else if (cond == IComp.F)
216              {
217                  res = Const(IrConsts.False);
218              }
219              else
220              {
221                  res = context.ISubtract(srcA, srcB);
222  #pragma warning disable IDE0059 // Remove unnecessary value assignment
223                  res = context.IAdd(res, context.BitwiseNot(GetCF()));
224  #pragma warning restore IDE0059
225  
226                  switch (cond)
227                  {
228                      case IComp.Eq: // r = xh == yh && xl == yl
229                          res = context.BitwiseAnd(context.ICompareEqual(srcA, srcB), GetZF());
230                          break;
231                      case IComp.Lt: // r = xh < yh || (xh == yh && xl < yl)
232                          Operand notC = context.BitwiseNot(GetCF());
233                          Operand prevLt = context.BitwiseAnd(context.ICompareEqual(srcA, srcB), notC);
234                          res = isSigned
235                              ? context.BitwiseOr(context.ICompareLess(srcA, srcB), prevLt)
236                              : context.BitwiseOr(context.ICompareLessUnsigned(srcA, srcB), prevLt);
237                          break;
238                      case IComp.Le: // r = xh < yh || (xh == yh && xl <= yl)
239                          Operand zOrNotC = context.BitwiseOr(GetZF(), context.BitwiseNot(GetCF()));
240                          Operand prevLe = context.BitwiseAnd(context.ICompareEqual(srcA, srcB), zOrNotC);
241                          res = isSigned
242                              ? context.BitwiseOr(context.ICompareLess(srcA, srcB), prevLe)
243                              : context.BitwiseOr(context.ICompareLessUnsigned(srcA, srcB), prevLe);
244                          break;
245                      case IComp.Gt: // r = xh > yh || (xh == yh && xl > yl)
246                          Operand notZAndC = context.BitwiseAnd(context.BitwiseNot(GetZF()), GetCF());
247                          Operand prevGt = context.BitwiseAnd(context.ICompareEqual(srcA, srcB), notZAndC);
248                          res = isSigned
249                              ? context.BitwiseOr(context.ICompareGreater(srcA, srcB), prevGt)
250                              : context.BitwiseOr(context.ICompareGreaterUnsigned(srcA, srcB), prevGt);
251                          break;
252                      case IComp.Ge: // r = xh > yh || (xh == yh && xl >= yl)
253                          Operand prevGe = context.BitwiseAnd(context.ICompareEqual(srcA, srcB), GetCF());
254                          res = isSigned
255                              ? context.BitwiseOr(context.ICompareGreater(srcA, srcB), prevGe)
256                              : context.BitwiseOr(context.ICompareGreaterUnsigned(srcA, srcB), prevGe);
257                          break;
258                      case IComp.Ne: // r = xh != yh || xl != yl
259                          res = context.BitwiseOr(context.ICompareNotEqual(srcA, srcB), context.BitwiseNot(GetZF()));
260                          break;
261                      default:
262                          throw new ArgumentException($"Unexpected condition \"{cond}\".");
263                  }
264              }
265  
266              return res;
267          }
268  
269          private static Operand GetIntComparison(EmitterContext context, IComp cond, Operand srcA, Operand srcB, bool isSigned)
270          {
271              Operand res;
272  
273              if (cond == IComp.T)
274              {
275                  res = Const(IrConsts.True);
276              }
277              else if (cond == IComp.F)
278              {
279                  res = Const(IrConsts.False);
280              }
281              else
282              {
283                  var inst = cond switch
284                  {
285                      IComp.Lt => Instruction.CompareLessU32,
286                      IComp.Eq => Instruction.CompareEqual,
287                      IComp.Le => Instruction.CompareLessOrEqualU32,
288                      IComp.Gt => Instruction.CompareGreaterU32,
289                      IComp.Ne => Instruction.CompareNotEqual,
290                      IComp.Ge => Instruction.CompareGreaterOrEqualU32,
291                      _ => throw new InvalidOperationException($"Unexpected condition \"{cond}\"."),
292                  };
293  
294                  if (isSigned)
295                  {
296                      switch (cond)
297                      {
298                          case IComp.Lt:
299                              inst = Instruction.CompareLess;
300                              break;
301                          case IComp.Le:
302                              inst = Instruction.CompareLessOrEqual;
303                              break;
304                          case IComp.Gt:
305                              inst = Instruction.CompareGreater;
306                              break;
307                          case IComp.Ge:
308                              inst = Instruction.CompareGreaterOrEqual;
309                              break;
310                      }
311                  }
312  
313                  res = context.Add(inst, Local(), srcA, srcB);
314              }
315  
316              return res;
317          }
318      }
319  }