/ src / Ryujinx.Graphics.Shader / Instructions / InstEmitFloatArithmetic.cs
InstEmitFloatArithmetic.cs
  1  using Ryujinx.Graphics.Shader.Decoders;
  2  using Ryujinx.Graphics.Shader.IntermediateRepresentation;
  3  using Ryujinx.Graphics.Shader.Translation;
  4  using static Ryujinx.Graphics.Shader.Instructions.InstEmitAluHelper;
  5  using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper;
  6  using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
  7  
  8  namespace Ryujinx.Graphics.Shader.Instructions
  9  {
 10      static partial class InstEmit
 11      {
 12          public static void DaddR(EmitterContext context)
 13          {
 14              InstDaddR op = context.GetOp<InstDaddR>();
 15  
 16              var srcA = GetSrcReg(context, op.SrcA, isFP64: true);
 17              var srcB = GetSrcReg(context, op.SrcB, isFP64: true);
 18  
 19              EmitFadd(context, Instruction.FP64, srcA, srcB, op.Dest, op.NegA, op.NegB, op.AbsA, op.AbsB, false, op.WriteCC);
 20          }
 21  
 22          public static void DaddI(EmitterContext context)
 23          {
 24              InstDaddI op = context.GetOp<InstDaddI>();
 25  
 26              var srcA = GetSrcReg(context, op.SrcA, isFP64: true);
 27              var srcB = GetSrcImm(context, Imm20ToFloat(op.Imm20), isFP64: true);
 28  
 29              EmitFadd(context, Instruction.FP64, srcA, srcB, op.Dest, op.NegA, op.NegB, op.AbsA, op.AbsB, false, op.WriteCC);
 30          }
 31  
 32          public static void DaddC(EmitterContext context)
 33          {
 34              InstDaddC op = context.GetOp<InstDaddC>();
 35  
 36              var srcA = GetSrcReg(context, op.SrcA, isFP64: true);
 37              var srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset, isFP64: true);
 38  
 39              EmitFadd(context, Instruction.FP64, srcA, srcB, op.Dest, op.NegA, op.NegB, op.AbsA, op.AbsB, false, op.WriteCC);
 40          }
 41  
 42          public static void DfmaR(EmitterContext context)
 43          {
 44              InstDfmaR op = context.GetOp<InstDfmaR>();
 45  
 46              var srcA = GetSrcReg(context, op.SrcA, isFP64: true);
 47              var srcB = GetSrcReg(context, op.SrcB, isFP64: true);
 48              var srcC = GetSrcReg(context, op.SrcC, isFP64: true);
 49  
 50              EmitFfma(context, Instruction.FP64, srcA, srcB, srcC, op.Dest, op.NegA, op.NegC, false, op.WriteCC);
 51          }
 52  
 53          public static void DfmaI(EmitterContext context)
 54          {
 55              InstDfmaI op = context.GetOp<InstDfmaI>();
 56  
 57              var srcA = GetSrcReg(context, op.SrcA, isFP64: true);
 58              var srcB = GetSrcImm(context, Imm20ToFloat(op.Imm20), isFP64: true);
 59              var srcC = GetSrcReg(context, op.SrcC, isFP64: true);
 60  
 61              EmitFfma(context, Instruction.FP64, srcA, srcB, srcC, op.Dest, op.NegA, op.NegC, false, op.WriteCC);
 62          }
 63  
 64          public static void DfmaC(EmitterContext context)
 65          {
 66              InstDfmaC op = context.GetOp<InstDfmaC>();
 67  
 68              var srcA = GetSrcReg(context, op.SrcA, isFP64: true);
 69              var srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset, isFP64: true);
 70              var srcC = GetSrcReg(context, op.SrcC, isFP64: true);
 71  
 72              EmitFfma(context, Instruction.FP64, srcA, srcB, srcC, op.Dest, op.NegA, op.NegC, false, op.WriteCC);
 73          }
 74  
 75          public static void DfmaRc(EmitterContext context)
 76          {
 77              InstDfmaRc op = context.GetOp<InstDfmaRc>();
 78  
 79              var srcA = GetSrcReg(context, op.SrcA, isFP64: true);
 80              var srcB = GetSrcReg(context, op.SrcC, isFP64: true);
 81              var srcC = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset, isFP64: true);
 82  
 83              EmitFfma(context, Instruction.FP64, srcA, srcB, srcC, op.Dest, op.NegA, op.NegC, false, op.WriteCC);
 84          }
 85  
 86          public static void DmulR(EmitterContext context)
 87          {
 88              InstDmulR op = context.GetOp<InstDmulR>();
 89  
 90              var srcA = GetSrcReg(context, op.SrcA, isFP64: true);
 91              var srcB = GetSrcReg(context, op.SrcB, isFP64: true);
 92  
 93              EmitFmul(context, Instruction.FP64, MultiplyScale.NoScale, srcA, srcB, op.Dest, op.NegA, false, op.WriteCC);
 94          }
 95  
 96          public static void DmulI(EmitterContext context)
 97          {
 98              InstDmulI op = context.GetOp<InstDmulI>();
 99  
100              var srcA = GetSrcReg(context, op.SrcA, isFP64: true);
101              var srcB = GetSrcImm(context, Imm20ToFloat(op.Imm20), isFP64: true);
102  
103              EmitFmul(context, Instruction.FP64, MultiplyScale.NoScale, srcA, srcB, op.Dest, op.NegA, false, op.WriteCC);
104          }
105  
106          public static void DmulC(EmitterContext context)
107          {
108              InstDmulC op = context.GetOp<InstDmulC>();
109  
110              var srcA = GetSrcReg(context, op.SrcA, isFP64: true);
111              var srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset, isFP64: true);
112  
113              EmitFmul(context, Instruction.FP64, MultiplyScale.NoScale, srcA, srcB, op.Dest, op.NegA, false, op.WriteCC);
114          }
115  
116          public static void FaddR(EmitterContext context)
117          {
118              InstFaddR op = context.GetOp<InstFaddR>();
119  
120              var srcA = GetSrcReg(context, op.SrcA);
121              var srcB = GetSrcReg(context, op.SrcB);
122  
123              EmitFadd(context, Instruction.FP32, srcA, srcB, op.Dest, op.NegA, op.NegB, op.AbsA, op.AbsB, op.Sat, op.WriteCC);
124          }
125  
126          public static void FaddI(EmitterContext context)
127          {
128              InstFaddI op = context.GetOp<InstFaddI>();
129  
130              var srcA = GetSrcReg(context, op.SrcA);
131              var srcB = GetSrcImm(context, Imm20ToFloat(op.Imm20));
132  
133              EmitFadd(context, Instruction.FP32, srcA, srcB, op.Dest, op.NegA, op.NegB, op.AbsA, op.AbsB, op.Sat, op.WriteCC);
134          }
135  
136          public static void FaddC(EmitterContext context)
137          {
138              InstFaddC op = context.GetOp<InstFaddC>();
139  
140              var srcA = GetSrcReg(context, op.SrcA);
141              var srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
142  
143              EmitFadd(context, Instruction.FP32, srcA, srcB, op.Dest, op.NegA, op.NegB, op.AbsA, op.AbsB, op.Sat, op.WriteCC);
144          }
145  
146          public static void Fadd32i(EmitterContext context)
147          {
148              InstFadd32i op = context.GetOp<InstFadd32i>();
149  
150              var srcA = GetSrcReg(context, op.SrcA);
151              var srcB = GetSrcImm(context, op.Imm32);
152  
153              EmitFadd(context, Instruction.FP32, srcA, srcB, op.Dest, op.NegA, op.NegB, op.AbsA, op.AbsB, false, op.WriteCC);
154          }
155  
156          public static void FfmaR(EmitterContext context)
157          {
158              InstFfmaR op = context.GetOp<InstFfmaR>();
159  
160              var srcA = GetSrcReg(context, op.SrcA);
161              var srcB = GetSrcReg(context, op.SrcB);
162              var srcC = GetSrcReg(context, op.SrcC);
163  
164              EmitFfma(context, Instruction.FP32, srcA, srcB, srcC, op.Dest, op.NegA, op.NegC, op.Sat, op.WriteCC);
165          }
166  
167          public static void FfmaI(EmitterContext context)
168          {
169              InstFfmaI op = context.GetOp<InstFfmaI>();
170  
171              var srcA = GetSrcReg(context, op.SrcA);
172              var srcB = GetSrcImm(context, Imm20ToFloat(op.Imm20));
173              var srcC = GetSrcReg(context, op.SrcC);
174  
175              EmitFfma(context, Instruction.FP32, srcA, srcB, srcC, op.Dest, op.NegA, op.NegC, op.Sat, op.WriteCC);
176          }
177  
178          public static void FfmaC(EmitterContext context)
179          {
180              InstFfmaC op = context.GetOp<InstFfmaC>();
181  
182              var srcA = GetSrcReg(context, op.SrcA);
183              var srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
184              var srcC = GetSrcReg(context, op.SrcC);
185  
186              EmitFfma(context, Instruction.FP32, srcA, srcB, srcC, op.Dest, op.NegA, op.NegC, op.Sat, op.WriteCC);
187          }
188  
189          public static void FfmaRc(EmitterContext context)
190          {
191              InstFfmaRc op = context.GetOp<InstFfmaRc>();
192  
193              var srcA = GetSrcReg(context, op.SrcA);
194              var srcB = GetSrcReg(context, op.SrcC);
195              var srcC = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
196  
197              EmitFfma(context, Instruction.FP32, srcA, srcB, srcC, op.Dest, op.NegA, op.NegC, op.Sat, op.WriteCC);
198          }
199  
200          public static void Ffma32i(EmitterContext context)
201          {
202              InstFfma32i op = context.GetOp<InstFfma32i>();
203  
204              var srcA = GetSrcReg(context, op.SrcA);
205              var srcB = GetSrcImm(context, op.Imm32);
206              var srcC = GetSrcReg(context, op.Dest);
207  
208              EmitFfma(context, Instruction.FP32, srcA, srcB, srcC, op.Dest, op.NegA, op.NegC, op.Sat, op.WriteCC);
209          }
210  
211          public static void FmulR(EmitterContext context)
212          {
213              InstFmulR op = context.GetOp<InstFmulR>();
214  
215              var srcA = GetSrcReg(context, op.SrcA);
216              var srcB = GetSrcReg(context, op.SrcB);
217  
218              EmitFmul(context, Instruction.FP32, op.Scale, srcA, srcB, op.Dest, op.NegA, op.Sat, op.WriteCC);
219          }
220  
221          public static void FmulI(EmitterContext context)
222          {
223              InstFmulI op = context.GetOp<InstFmulI>();
224  
225              var srcA = GetSrcReg(context, op.SrcA);
226              var srcB = GetSrcImm(context, Imm20ToFloat(op.Imm20));
227  
228              EmitFmul(context, Instruction.FP32, op.Scale, srcA, srcB, op.Dest, op.NegA, op.Sat, op.WriteCC);
229          }
230  
231          public static void FmulC(EmitterContext context)
232          {
233              InstFmulC op = context.GetOp<InstFmulC>();
234  
235              var srcA = GetSrcReg(context, op.SrcA);
236              var srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
237  
238              EmitFmul(context, Instruction.FP32, op.Scale, srcA, srcB, op.Dest, op.NegA, op.Sat, op.WriteCC);
239          }
240  
241          public static void Fmul32i(EmitterContext context)
242          {
243              InstFmul32i op = context.GetOp<InstFmul32i>();
244  
245              var srcA = GetSrcReg(context, op.SrcA);
246              var srcB = GetSrcImm(context, op.Imm32);
247  
248              EmitFmul(context, Instruction.FP32, MultiplyScale.NoScale, srcA, srcB, op.Dest, false, op.Sat, op.WriteCC);
249          }
250  
251          public static void Hadd2R(EmitterContext context)
252          {
253              InstHadd2R op = context.GetOp<InstHadd2R>();
254  
255              var srcA = GetHalfSrc(context, op.ASwizzle, op.SrcA, op.NegA, op.AbsA);
256              var srcB = GetHalfSrc(context, op.BSwizzle, op.SrcB, op.NegB, op.AbsB);
257  
258              EmitHadd2Hmul2(context, op.OFmt, srcA, srcB, isAdd: true, op.Dest, op.Sat);
259          }
260  
261          public static void Hadd2I(EmitterContext context)
262          {
263              InstHadd2I op = context.GetOp<InstHadd2I>();
264  
265              var srcA = GetHalfSrc(context, op.ASwizzle, op.SrcA, op.NegA, op.AbsA);
266              var srcB = GetHalfSrc(context, op.BimmH0, op.BimmH1);
267  
268              EmitHadd2Hmul2(context, op.OFmt, srcA, srcB, isAdd: true, op.Dest, op.Sat);
269          }
270  
271          public static void Hadd2C(EmitterContext context)
272          {
273              InstHadd2C op = context.GetOp<InstHadd2C>();
274  
275              var srcA = GetHalfSrc(context, op.ASwizzle, op.SrcA, op.NegA, op.AbsA);
276              var srcB = GetHalfSrc(context, HalfSwizzle.F32, op.CbufSlot, op.CbufOffset, op.NegB, op.AbsB);
277  
278              EmitHadd2Hmul2(context, op.OFmt, srcA, srcB, isAdd: true, op.Dest, op.Sat);
279          }
280  
281          public static void Hadd232i(EmitterContext context)
282          {
283              InstHadd232i op = context.GetOp<InstHadd232i>();
284  
285              var srcA = GetHalfSrc(context, op.ASwizzle, op.SrcA, op.NegA, false);
286              var srcB = GetHalfSrc(context, op.Imm);
287  
288              EmitHadd2Hmul2(context, OFmt.F16, srcA, srcB, isAdd: true, op.Dest, op.Sat);
289          }
290  
291          public static void Hfma2R(EmitterContext context)
292          {
293              InstHfma2R op = context.GetOp<InstHfma2R>();
294  
295              var srcA = GetHalfSrc(context, op.ASwizzle, op.SrcA, false, false);
296              var srcB = GetHalfSrc(context, op.BSwizzle, op.SrcB, op.NegA, false);
297              var srcC = GetHalfSrc(context, op.CSwizzle, op.SrcC, op.NegC, false);
298  
299              EmitHfma2(context, op.OFmt, srcA, srcB, srcC, op.Dest, op.Sat);
300          }
301  
302          public static void Hfma2I(EmitterContext context)
303          {
304              InstHfma2I op = context.GetOp<InstHfma2I>();
305  
306              var srcA = GetHalfSrc(context, op.ASwizzle, op.SrcA, false, false);
307              var srcB = GetHalfSrc(context, op.BimmH0, op.BimmH1);
308              var srcC = GetHalfSrc(context, op.CSwizzle, op.SrcC, op.NegC, false);
309  
310              EmitHfma2(context, op.OFmt, srcA, srcB, srcC, op.Dest, op.Sat);
311          }
312  
313          public static void Hfma2C(EmitterContext context)
314          {
315              InstHfma2C op = context.GetOp<InstHfma2C>();
316  
317              var srcA = GetHalfSrc(context, op.ASwizzle, op.SrcA, false, false);
318              var srcB = GetHalfSrc(context, HalfSwizzle.F32, op.CbufSlot, op.CbufOffset, op.NegA, false);
319              var srcC = GetHalfSrc(context, op.CSwizzle, op.SrcC, op.NegC, false);
320  
321              EmitHfma2(context, op.OFmt, srcA, srcB, srcC, op.Dest, op.Sat);
322          }
323  
324          public static void Hfma2Rc(EmitterContext context)
325          {
326              InstHfma2Rc op = context.GetOp<InstHfma2Rc>();
327  
328              var srcA = GetHalfSrc(context, op.ASwizzle, op.SrcA, false, false);
329              var srcB = GetHalfSrc(context, op.CSwizzle, op.SrcC, op.NegA, false);
330              var srcC = GetHalfSrc(context, HalfSwizzle.F32, op.CbufSlot, op.CbufOffset, op.NegC, false);
331  
332              EmitHfma2(context, op.OFmt, srcA, srcB, srcC, op.Dest, op.Sat);
333          }
334  
335          public static void Hfma232i(EmitterContext context)
336          {
337              InstHfma232i op = context.GetOp<InstHfma232i>();
338  
339              var srcA = GetHalfSrc(context, op.ASwizzle, op.SrcA, false, false);
340              var srcB = GetHalfSrc(context, op.Imm);
341              var srcC = GetHalfSrc(context, HalfSwizzle.F16, op.Dest, op.NegC, false);
342  
343              EmitHfma2(context, OFmt.F16, srcA, srcB, srcC, op.Dest, saturate: false);
344          }
345  
346          public static void Hmul2R(EmitterContext context)
347          {
348              InstHmul2R op = context.GetOp<InstHmul2R>();
349  
350              var srcA = GetHalfSrc(context, op.ASwizzle, op.SrcA, false, op.AbsA);
351              var srcB = GetHalfSrc(context, op.BSwizzle, op.SrcB, op.NegA, op.AbsB);
352  
353              EmitHadd2Hmul2(context, op.OFmt, srcA, srcB, isAdd: false, op.Dest, op.Sat);
354          }
355  
356          public static void Hmul2I(EmitterContext context)
357          {
358              InstHmul2I op = context.GetOp<InstHmul2I>();
359  
360              var srcA = GetHalfSrc(context, op.ASwizzle, op.SrcA, op.NegA, op.AbsA);
361              var srcB = GetHalfSrc(context, op.BimmH0, op.BimmH1);
362  
363              EmitHadd2Hmul2(context, op.OFmt, srcA, srcB, isAdd: false, op.Dest, op.Sat);
364          }
365  
366          public static void Hmul2C(EmitterContext context)
367          {
368              InstHmul2C op = context.GetOp<InstHmul2C>();
369  
370              var srcA = GetHalfSrc(context, op.ASwizzle, op.SrcA, false, op.AbsA);
371              var srcB = GetHalfSrc(context, HalfSwizzle.F32, op.CbufSlot, op.CbufOffset, op.NegA, op.AbsB);
372  
373              EmitHadd2Hmul2(context, op.OFmt, srcA, srcB, isAdd: false, op.Dest, op.Sat);
374          }
375  
376          public static void Hmul232i(EmitterContext context)
377          {
378              InstHmul232i op = context.GetOp<InstHmul232i>();
379  
380              var srcA = GetHalfSrc(context, op.ASwizzle, op.SrcA, false, false);
381              var srcB = GetHalfSrc(context, op.Imm32);
382  
383              EmitHadd2Hmul2(context, OFmt.F16, srcA, srcB, isAdd: false, op.Dest, op.Sat);
384          }
385  
386          private static void EmitFadd(
387              EmitterContext context,
388              Instruction fpType,
389              Operand srcA,
390              Operand srcB,
391              int rd,
392              bool negateA,
393              bool negateB,
394              bool absoluteA,
395              bool absoluteB,
396              bool saturate,
397              bool writeCC)
398          {
399              bool isFP64 = fpType == Instruction.FP64;
400  
401              srcA = context.FPAbsNeg(srcA, absoluteA, negateA, fpType);
402              srcB = context.FPAbsNeg(srcB, absoluteB, negateB, fpType);
403  
404              Operand res = context.FPSaturate(context.FPAdd(srcA, srcB, fpType), saturate, fpType);
405  
406              SetDest(context, res, rd, isFP64);
407  
408              SetFPZnFlags(context, res, writeCC, fpType);
409          }
410  
411          private static void EmitFfma(
412              EmitterContext context,
413              Instruction fpType,
414              Operand srcA,
415              Operand srcB,
416              Operand srcC,
417              int rd,
418              bool negateB,
419              bool negateC,
420              bool saturate,
421              bool writeCC)
422          {
423              bool isFP64 = fpType == Instruction.FP64;
424  
425              srcB = context.FPNegate(srcB, negateB, fpType);
426              srcC = context.FPNegate(srcC, negateC, fpType);
427  
428              Operand res = context.FPSaturate(context.FPFusedMultiplyAdd(srcA, srcB, srcC, fpType), saturate, fpType);
429  
430              SetDest(context, res, rd, isFP64);
431  
432              SetFPZnFlags(context, res, writeCC, fpType);
433          }
434  
435          private static void EmitFmul(
436              EmitterContext context,
437              Instruction fpType,
438              MultiplyScale scale,
439              Operand srcA,
440              Operand srcB,
441              int rd,
442              bool negateB,
443              bool saturate,
444              bool writeCC)
445          {
446              bool isFP64 = fpType == Instruction.FP64;
447  
448              srcB = context.FPNegate(srcB, negateB, fpType);
449  
450              if (scale != MultiplyScale.NoScale)
451              {
452                  Operand scaleConst = scale switch
453                  {
454                      MultiplyScale.D2 => ConstF(0.5f),
455                      MultiplyScale.D4 => ConstF(0.25f),
456                      MultiplyScale.D8 => ConstF(0.125f),
457                      MultiplyScale.M2 => ConstF(2f),
458                      MultiplyScale.M4 => ConstF(4f),
459                      MultiplyScale.M8 => ConstF(8f),
460                      _ => ConstF(1f), // Invalid, behave as if it had no scale.
461                  };
462  
463                  if (scaleConst.AsFloat() == 1f)
464                  {
465                      context.TranslatorContext.GpuAccessor.Log($"Invalid FP multiply scale \"{scale}\".");
466                  }
467  
468                  if (isFP64)
469                  {
470                      scaleConst = context.FP32ConvertToFP64(scaleConst);
471                  }
472  
473                  srcA = context.FPMultiply(srcA, scaleConst, fpType);
474              }
475  
476              Operand res = context.FPSaturate(context.FPMultiply(srcA, srcB, fpType), saturate, fpType);
477  
478              SetDest(context, res, rd, isFP64);
479  
480              SetFPZnFlags(context, res, writeCC, fpType);
481          }
482  
483          private static void EmitHadd2Hmul2(
484              EmitterContext context,
485              OFmt swizzle,
486              Operand[] srcA,
487              Operand[] srcB,
488              bool isAdd,
489              int rd,
490              bool saturate)
491          {
492              Operand[] res = new Operand[2];
493  
494              for (int index = 0; index < res.Length; index++)
495              {
496                  if (isAdd)
497                  {
498                      res[index] = context.FPAdd(srcA[index], srcB[index]);
499                  }
500                  else
501                  {
502                      res[index] = context.FPMultiply(srcA[index], srcB[index]);
503                  }
504  
505                  res[index] = context.FPSaturate(res[index], saturate);
506              }
507  
508              context.Copy(GetDest(rd), GetHalfPacked(context, swizzle, res, rd));
509          }
510  
511          public static void EmitHfma2(
512              EmitterContext context,
513              OFmt swizzle,
514              Operand[] srcA,
515              Operand[] srcB,
516              Operand[] srcC,
517              int rd,
518              bool saturate)
519          {
520              Operand[] res = new Operand[2];
521  
522              for (int index = 0; index < res.Length; index++)
523              {
524                  res[index] = context.FPFusedMultiplyAdd(srcA[index], srcB[index], srcC[index]);
525                  res[index] = context.FPSaturate(res[index], saturate);
526              }
527  
528              context.Copy(GetDest(rd), GetHalfPacked(context, swizzle, res, rd));
529          }
530      }
531  }