InstEmitFArith.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  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. namespace Ryujinx.Graphics.Shader.Instructions
  9. {
  10. static partial class InstEmit
  11. {
  12. public static void Dadd(EmitterContext context) => EmitFPAdd(context, Instruction.FP64);
  13. public static void Dfma(EmitterContext context) => EmitFPFma(context, Instruction.FP64);
  14. public static void Dmul(EmitterContext context) => EmitFPMultiply(context, Instruction.FP64);
  15. public static void Fadd(EmitterContext context) => EmitFPAdd(context, Instruction.FP32);
  16. public static void Ffma(EmitterContext context) => EmitFPFma(context, Instruction.FP32);
  17. public static void Ffma32i(EmitterContext context)
  18. {
  19. IOpCodeFArith op = (IOpCodeFArith)context.CurrOp;
  20. bool saturate = op.RawOpCode.Extract(55);
  21. bool negateA = op.RawOpCode.Extract(56);
  22. bool negateC = op.RawOpCode.Extract(57);
  23. Operand srcA = context.FPNegate(GetSrcA(context), negateA);
  24. Operand srcC = context.FPNegate(GetDest(context), negateC);
  25. Operand srcB = GetSrcB(context);
  26. Operand dest = GetDest(context);
  27. context.Copy(dest, context.FPSaturate(context.FPFusedMultiplyAdd(srcA, srcB, srcC), saturate));
  28. SetFPZnFlags(context, dest, op.SetCondCode);
  29. }
  30. public static void Fmnmx(EmitterContext context)
  31. {
  32. IOpCodeFArith op = (IOpCodeFArith)context.CurrOp;
  33. bool absoluteA = op.AbsoluteA;
  34. bool negateB = op.RawOpCode.Extract(45);
  35. bool negateA = op.RawOpCode.Extract(48);
  36. bool absoluteB = op.RawOpCode.Extract(49);
  37. Operand srcA = context.FPAbsNeg(GetSrcA(context), absoluteA, negateA);
  38. Operand srcB = context.FPAbsNeg(GetSrcB(context), absoluteB, negateB);
  39. Operand resMin = context.FPMinimum(srcA, srcB);
  40. Operand resMax = context.FPMaximum(srcA, srcB);
  41. Operand pred = GetPredicate39(context);
  42. Operand dest = GetDest(context);
  43. context.Copy(dest, context.ConditionalSelect(pred, resMin, resMax));
  44. SetFPZnFlags(context, dest, op.SetCondCode);
  45. }
  46. public static void Fmul(EmitterContext context) => EmitFPMultiply(context, Instruction.FP32);
  47. public static void Fset(EmitterContext context)
  48. {
  49. OpCodeSet op = (OpCodeSet)context.CurrOp;
  50. Condition cmpOp = (Condition)op.RawOpCode.Extract(48, 4);
  51. bool negateA = op.RawOpCode.Extract(43);
  52. bool absoluteB = op.RawOpCode.Extract(44);
  53. bool boolFloat = op.RawOpCode.Extract(52);
  54. bool negateB = op.RawOpCode.Extract(53);
  55. bool absoluteA = op.RawOpCode.Extract(54);
  56. Operand srcA = context.FPAbsNeg(GetSrcA(context), absoluteA, negateA);
  57. Operand srcB = context.FPAbsNeg(GetSrcB(context), absoluteB, negateB);
  58. Operand res = GetFPComparison(context, cmpOp, srcA, srcB);
  59. Operand pred = GetPredicate39(context);
  60. res = GetPredLogicalOp(context, op.LogicalOp, res, pred);
  61. Operand dest = GetDest(context);
  62. if (boolFloat)
  63. {
  64. res = context.ConditionalSelect(res, ConstF(1), Const(0));
  65. context.Copy(dest, res);
  66. SetFPZnFlags(context, res, op.SetCondCode);
  67. }
  68. else
  69. {
  70. context.Copy(dest, res);
  71. SetZnFlags(context, res, op.SetCondCode, op.Extended);
  72. }
  73. // TODO: X
  74. }
  75. public static void Fsetp(EmitterContext context)
  76. {
  77. OpCodeSet op = (OpCodeSet)context.CurrOp;
  78. Condition cmpOp = (Condition)op.RawOpCode.Extract(48, 4);
  79. bool negateB = op.RawOpCode.Extract(6);
  80. bool absoluteA = op.RawOpCode.Extract(7);
  81. bool negateA = op.RawOpCode.Extract(43);
  82. bool absoluteB = op.RawOpCode.Extract(44);
  83. Operand srcA = context.FPAbsNeg(GetSrcA(context), absoluteA, negateA);
  84. Operand srcB = context.FPAbsNeg(GetSrcB(context), absoluteB, negateB);
  85. Operand p0Res = GetFPComparison(context, cmpOp, srcA, srcB);
  86. Operand p1Res = context.BitwiseNot(p0Res);
  87. Operand pred = GetPredicate39(context);
  88. p0Res = GetPredLogicalOp(context, op.LogicalOp, p0Res, pred);
  89. p1Res = GetPredLogicalOp(context, op.LogicalOp, p1Res, pred);
  90. context.Copy(Register(op.Predicate3), p0Res);
  91. context.Copy(Register(op.Predicate0), p1Res);
  92. }
  93. public static void Fswzadd(EmitterContext context)
  94. {
  95. OpCodeAlu op = (OpCodeAlu)context.CurrOp;
  96. int mask = op.RawOpCode.Extract(28, 8);
  97. Operand srcA = GetSrcA(context);
  98. Operand srcB = GetSrcB(context);
  99. Operand dest = GetDest(context);
  100. context.Copy(dest, context.FPSwizzleAdd(srcA, srcB, mask));
  101. SetFPZnFlags(context, dest, op.SetCondCode);
  102. }
  103. public static void Hadd2(EmitterContext context)
  104. {
  105. Hadd2Hmul2Impl(context, isAdd: true);
  106. }
  107. public static void Hfma2(EmitterContext context)
  108. {
  109. IOpCodeHfma op = (IOpCodeHfma)context.CurrOp;
  110. Operand[] srcA = GetHfmaSrcA(context);
  111. Operand[] srcB = GetHfmaSrcB(context);
  112. Operand[] srcC = GetHfmaSrcC(context);
  113. Operand[] res = new Operand[2];
  114. for (int index = 0; index < res.Length; index++)
  115. {
  116. res[index] = context.FPFusedMultiplyAdd(srcA[index], srcB[index], srcC[index]);
  117. res[index] = context.FPSaturate(res[index], op.Saturate);
  118. }
  119. context.Copy(GetDest(context), GetHalfPacked(context, res));
  120. }
  121. public static void Hmul2(EmitterContext context)
  122. {
  123. Hadd2Hmul2Impl(context, isAdd: false);
  124. }
  125. private static void Hadd2Hmul2Impl(EmitterContext context, bool isAdd)
  126. {
  127. OpCode op = context.CurrOp;
  128. bool saturate = op.RawOpCode.Extract(op is IOpCodeReg ? 32 : 52);
  129. Operand[] srcA = GetHalfSrcA(context, isAdd);
  130. Operand[] srcB = GetHalfSrcB(context);
  131. Operand[] res = new Operand[2];
  132. for (int index = 0; index < res.Length; index++)
  133. {
  134. if (isAdd)
  135. {
  136. res[index] = context.FPAdd(srcA[index], srcB[index]);
  137. }
  138. else
  139. {
  140. res[index] = context.FPMultiply(srcA[index], srcB[index]);
  141. }
  142. res[index] = context.FPSaturate(res[index], saturate);
  143. }
  144. context.Copy(GetDest(context), GetHalfPacked(context, res));
  145. }
  146. public static void Hset2(EmitterContext context)
  147. {
  148. OpCodeSet op = (OpCodeSet)context.CurrOp;
  149. bool isRegVariant = op is IOpCodeReg;
  150. bool boolFloat = isRegVariant
  151. ? op.RawOpCode.Extract(49)
  152. : op.RawOpCode.Extract(53);
  153. Condition cmpOp = isRegVariant
  154. ? (Condition)op.RawOpCode.Extract(35, 4)
  155. : (Condition)op.RawOpCode.Extract(49, 4);
  156. Operand[] srcA = GetHalfSrcA(context);
  157. Operand[] srcB = GetHalfSrcB(context);
  158. Operand[] res = new Operand[2];
  159. res[0] = GetFPComparison(context, cmpOp, srcA[0], srcB[0]);
  160. res[1] = GetFPComparison(context, cmpOp, srcA[1], srcB[1]);
  161. Operand pred = GetPredicate39(context);
  162. res[0] = GetPredLogicalOp(context, op.LogicalOp, res[0], pred);
  163. res[1] = GetPredLogicalOp(context, op.LogicalOp, res[1], pred);
  164. if (boolFloat)
  165. {
  166. res[0] = context.ConditionalSelect(res[0], ConstF(1), Const(0));
  167. res[1] = context.ConditionalSelect(res[1], ConstF(1), Const(0));
  168. context.Copy(GetDest(context), context.PackHalf2x16(res[0], res[1]));
  169. }
  170. else
  171. {
  172. Operand low = context.BitwiseAnd(res[0], Const(0xffff));
  173. Operand high = context.ShiftLeft (res[1], Const(16));
  174. Operand packed = context.BitwiseOr(low, high);
  175. context.Copy(GetDest(context), packed);
  176. }
  177. }
  178. public static void Hsetp2(EmitterContext context)
  179. {
  180. OpCodeSet op = (OpCodeSet)context.CurrOp;
  181. bool isRegVariant = op is IOpCodeReg;
  182. bool hAnd = isRegVariant
  183. ? op.RawOpCode.Extract(49)
  184. : op.RawOpCode.Extract(53);
  185. Condition cmpOp = isRegVariant
  186. ? (Condition)op.RawOpCode.Extract(35, 4)
  187. : (Condition)op.RawOpCode.Extract(49, 4);
  188. Operand[] srcA = GetHalfSrcA(context);
  189. Operand[] srcB = GetHalfSrcB(context);
  190. Operand p0Res = GetFPComparison(context, cmpOp, srcA[0], srcB[0]);
  191. Operand p1Res = GetFPComparison(context, cmpOp, srcA[1], srcB[1]);
  192. if (hAnd)
  193. {
  194. p0Res = context.BitwiseAnd(p0Res, p1Res);
  195. p1Res = context.BitwiseNot(p0Res);
  196. }
  197. Operand pred = GetPredicate39(context);
  198. p0Res = GetPredLogicalOp(context, op.LogicalOp, p0Res, pred);
  199. p1Res = GetPredLogicalOp(context, op.LogicalOp, p1Res, pred);
  200. context.Copy(Register(op.Predicate3), p0Res);
  201. context.Copy(Register(op.Predicate0), p1Res);
  202. }
  203. public static void Mufu(EmitterContext context)
  204. {
  205. IOpCodeFArith op = (IOpCodeFArith)context.CurrOp;
  206. bool negateB = op.RawOpCode.Extract(48);
  207. Operand res = context.FPAbsNeg(GetSrcA(context), op.AbsoluteA, negateB);
  208. MufuOperation subOp = (MufuOperation)context.CurrOp.RawOpCode.Extract(20, 4);
  209. switch (subOp)
  210. {
  211. case MufuOperation.Cosine:
  212. res = context.FPCosine(res);
  213. break;
  214. case MufuOperation.Sine:
  215. res = context.FPSine(res);
  216. break;
  217. case MufuOperation.ExponentB2:
  218. res = context.FPExponentB2(res);
  219. break;
  220. case MufuOperation.LogarithmB2:
  221. res = context.FPLogarithmB2(res);
  222. break;
  223. case MufuOperation.Reciprocal:
  224. res = context.FPReciprocal(res);
  225. break;
  226. case MufuOperation.ReciprocalSquareRoot:
  227. res = context.FPReciprocalSquareRoot(res);
  228. break;
  229. case MufuOperation.SquareRoot:
  230. res = context.FPSquareRoot(res);
  231. break;
  232. default: /* TODO */ break;
  233. }
  234. context.Copy(GetDest(context), context.FPSaturate(res, op.Saturate));
  235. }
  236. private static void EmitFPAdd(EmitterContext context, Instruction fpType)
  237. {
  238. IOpCodeFArith op = (IOpCodeFArith)context.CurrOp;
  239. bool isFP64 = fpType == Instruction.FP64;
  240. bool absoluteA = op.AbsoluteA, absoluteB, negateA, negateB;
  241. if (op is OpCodeFArithImm32)
  242. {
  243. negateB = op.RawOpCode.Extract(53);
  244. negateA = op.RawOpCode.Extract(56);
  245. absoluteB = op.RawOpCode.Extract(57);
  246. }
  247. else
  248. {
  249. negateB = op.RawOpCode.Extract(45);
  250. negateA = op.RawOpCode.Extract(48);
  251. absoluteB = op.RawOpCode.Extract(49);
  252. }
  253. Operand srcA = context.FPAbsNeg(GetSrcA(context, isFP64), absoluteA, negateA, fpType);
  254. Operand srcB = context.FPAbsNeg(GetSrcB(context, isFP64), absoluteB, negateB, fpType);
  255. Operand res = context.FPSaturate(context.FPAdd(srcA, srcB, fpType), op.Saturate, fpType);
  256. SetDest(context, res, isFP64);
  257. SetFPZnFlags(context, res, op.SetCondCode, fpType);
  258. }
  259. private static void EmitFPFma(EmitterContext context, Instruction fpType)
  260. {
  261. IOpCodeFArith op = (IOpCodeFArith)context.CurrOp;
  262. bool isFP64 = fpType == Instruction.FP64;
  263. bool negateB = op.RawOpCode.Extract(48);
  264. bool negateC = op.RawOpCode.Extract(49);
  265. Operand srcA = GetSrcA(context, isFP64);
  266. Operand srcB = context.FPNegate(GetSrcB(context, isFP64), negateB, fpType);
  267. Operand srcC = context.FPNegate(GetSrcC(context, isFP64), negateC, fpType);
  268. Operand res = context.FPSaturate(context.FPFusedMultiplyAdd(srcA, srcB, srcC, fpType), op.Saturate, fpType);
  269. SetDest(context, res, isFP64);
  270. SetFPZnFlags(context, res, op.SetCondCode, fpType);
  271. }
  272. private static void EmitFPMultiply(EmitterContext context, Instruction fpType)
  273. {
  274. IOpCodeFArith op = (IOpCodeFArith)context.CurrOp;
  275. bool isFP64 = fpType == Instruction.FP64;
  276. bool isImm32 = op is OpCodeFArithImm32;
  277. bool negateB = !isImm32 && op.RawOpCode.Extract(48);
  278. Operand srcA = GetSrcA(context, isFP64);
  279. Operand srcB = context.FPNegate(GetSrcB(context, isFP64), negateB, fpType);
  280. if (op.Scale != FPMultiplyScale.None)
  281. {
  282. Operand scale = op.Scale switch
  283. {
  284. FPMultiplyScale.Divide2 => ConstF(0.5f),
  285. FPMultiplyScale.Divide4 => ConstF(0.25f),
  286. FPMultiplyScale.Divide8 => ConstF(0.125f),
  287. FPMultiplyScale.Multiply2 => ConstF(2f),
  288. FPMultiplyScale.Multiply4 => ConstF(4f),
  289. FPMultiplyScale.Multiply8 => ConstF(8f),
  290. _ => ConstF(1) // Invalid, behave as if it had no scale.
  291. };
  292. if (scale.AsFloat() == 1)
  293. {
  294. context.Config.PrintLog($"Invalid FP multiply scale \"{op.Scale}\".");
  295. }
  296. if (isFP64)
  297. {
  298. scale = context.FP32ConvertToFP64(scale);
  299. }
  300. srcA = context.FPMultiply(srcA, scale, fpType);
  301. }
  302. bool saturate = isImm32 ? op.RawOpCode.Extract(55) : op.Saturate;
  303. Operand res = context.FPSaturate(context.FPMultiply(srcA, srcB, fpType), saturate, fpType);
  304. SetDest(context, res, isFP64);
  305. SetFPZnFlags(context, res, op.SetCondCode, fpType);
  306. }
  307. private static Operand GetFPComparison(
  308. EmitterContext context,
  309. Condition cond,
  310. Operand srcA,
  311. Operand srcB)
  312. {
  313. Operand res;
  314. if (cond == Condition.Always)
  315. {
  316. res = Const(IrConsts.True);
  317. }
  318. else if (cond == Condition.Never)
  319. {
  320. res = Const(IrConsts.False);
  321. }
  322. else if (cond == Condition.Nan || cond == Condition.Number)
  323. {
  324. res = context.BitwiseOr(context.IsNan(srcA), context.IsNan(srcB));
  325. if (cond == Condition.Number)
  326. {
  327. res = context.BitwiseNot(res);
  328. }
  329. }
  330. else
  331. {
  332. Instruction inst;
  333. switch (cond & ~Condition.Nan)
  334. {
  335. case Condition.Less: inst = Instruction.CompareLess; break;
  336. case Condition.Equal: inst = Instruction.CompareEqual; break;
  337. case Condition.LessOrEqual: inst = Instruction.CompareLessOrEqual; break;
  338. case Condition.Greater: inst = Instruction.CompareGreater; break;
  339. case Condition.NotEqual: inst = Instruction.CompareNotEqual; break;
  340. case Condition.GreaterOrEqual: inst = Instruction.CompareGreaterOrEqual; break;
  341. default: throw new InvalidOperationException($"Unexpected condition \"{cond}\".");
  342. }
  343. res = context.Add(inst | Instruction.FP32, Local(), srcA, srcB);
  344. if ((cond & Condition.Nan) != 0)
  345. {
  346. res = context.BitwiseOr(res, context.IsNan(srcA));
  347. res = context.BitwiseOr(res, context.IsNan(srcB));
  348. }
  349. }
  350. return res;
  351. }
  352. private static Operand[] GetHfmaSrcA(EmitterContext context)
  353. {
  354. IOpCodeHfma op = (IOpCodeHfma)context.CurrOp;
  355. return GetHalfUnpacked(context, GetSrcA(context), op.SwizzleA);
  356. }
  357. private static Operand[] GetHfmaSrcB(EmitterContext context)
  358. {
  359. IOpCodeHfma op = (IOpCodeHfma)context.CurrOp;
  360. Operand[] operands = GetHalfUnpacked(context, GetSrcB(context), op.SwizzleB);
  361. return FPAbsNeg(context, operands, false, op.NegateB);
  362. }
  363. private static Operand[] GetHfmaSrcC(EmitterContext context)
  364. {
  365. IOpCodeHfma op = (IOpCodeHfma)context.CurrOp;
  366. Operand[] operands = GetHalfUnpacked(context, GetSrcC(context), op.SwizzleC);
  367. return FPAbsNeg(context, operands, false, op.NegateC);
  368. }
  369. private static void SetDest(EmitterContext context, Operand value, bool isFP64)
  370. {
  371. if (isFP64)
  372. {
  373. IOpCodeRd op = (IOpCodeRd)context.CurrOp;
  374. context.Copy(Register(op.Rd.Index, op.Rd.Type), context.UnpackDouble2x32Low(value));
  375. context.Copy(Register(op.Rd.Index | 1, op.Rd.Type), context.UnpackDouble2x32High(value));
  376. }
  377. else
  378. {
  379. context.Copy(GetDest(context), value);
  380. }
  381. }
  382. }
  383. }