InstEmitFArith.cs 19 KB

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