Optimizer.cs 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. using Ryujinx.Graphics.Shader.IntermediateRepresentation;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Linq;
  5. namespace Ryujinx.Graphics.Shader.Translation.Optimizations
  6. {
  7. static class Optimizer
  8. {
  9. public static void Optimize(BasicBlock[] blocks, ShaderStage stage)
  10. {
  11. for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++)
  12. {
  13. GlobalToStorage.RunPass(blocks[blkIndex], stage);
  14. }
  15. bool modified;
  16. do
  17. {
  18. modified = false;
  19. for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++)
  20. {
  21. BasicBlock block = blocks[blkIndex];
  22. LinkedListNode<INode> node = block.Operations.First;
  23. while (node != null)
  24. {
  25. LinkedListNode<INode> nextNode = node.Next;
  26. bool isUnused = IsUnused(node.Value);
  27. if (!(node.Value is Operation operation) || isUnused)
  28. {
  29. if (isUnused)
  30. {
  31. RemoveNode(block, node);
  32. modified = true;
  33. }
  34. node = nextNode;
  35. continue;
  36. }
  37. ConstantFolding.RunPass(operation);
  38. Simplification.RunPass(operation);
  39. if (DestIsLocalVar(operation))
  40. {
  41. if (operation.Inst == Instruction.Copy)
  42. {
  43. PropagateCopy(operation);
  44. RemoveNode(block, node);
  45. modified = true;
  46. }
  47. else if ((operation.Inst == Instruction.PackHalf2x16 && PropagatePack(operation)) ||
  48. (operation.Inst == Instruction.ShuffleXor && MatchDdxOrDdy(operation)))
  49. {
  50. if (operation.Dest.UseOps.Count == 0)
  51. {
  52. RemoveNode(block, node);
  53. }
  54. modified = true;
  55. }
  56. }
  57. node = nextNode;
  58. }
  59. if (BranchElimination.RunPass(block))
  60. {
  61. RemoveNode(block, block.Operations.Last);
  62. modified = true;
  63. }
  64. }
  65. }
  66. while (modified);
  67. for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++)
  68. {
  69. BindlessToIndexed.RunPass(blocks[blkIndex]);
  70. }
  71. }
  72. private static void PropagateCopy(Operation copyOp)
  73. {
  74. // Propagate copy source operand to all uses of
  75. // the destination operand.
  76. Operand dest = copyOp.Dest;
  77. Operand src = copyOp.GetSource(0);
  78. INode[] uses = dest.UseOps.ToArray();
  79. foreach (INode useNode in uses)
  80. {
  81. for (int index = 0; index < useNode.SourcesCount; index++)
  82. {
  83. if (useNode.GetSource(index) == dest)
  84. {
  85. useNode.SetSource(index, src);
  86. }
  87. }
  88. }
  89. }
  90. private static bool PropagatePack(Operation packOp)
  91. {
  92. // Propagate pack source operands to uses by unpack
  93. // instruction. The source depends on the unpack instruction.
  94. bool modified = false;
  95. Operand dest = packOp.Dest;
  96. Operand src0 = packOp.GetSource(0);
  97. Operand src1 = packOp.GetSource(1);
  98. INode[] uses = dest.UseOps.ToArray();
  99. foreach (INode useNode in uses)
  100. {
  101. if (!(useNode is Operation operation) || operation.Inst != Instruction.UnpackHalf2x16)
  102. {
  103. continue;
  104. }
  105. if (operation.GetSource(0) == dest)
  106. {
  107. operation.TurnIntoCopy(operation.Index == 1 ? src1 : src0);
  108. modified = true;
  109. }
  110. }
  111. return modified;
  112. }
  113. public static bool MatchDdxOrDdy(Operation operation)
  114. {
  115. // It's assumed that "operation.Inst" is ShuffleXor,
  116. // that should be checked before calling this method.
  117. Debug.Assert(operation.Inst == Instruction.ShuffleXor);
  118. bool modified = false;
  119. Operand src2 = operation.GetSource(1);
  120. Operand src3 = operation.GetSource(2);
  121. if (src2.Type != OperandType.Constant || (src2.Value != 1 && src2.Value != 2))
  122. {
  123. return false;
  124. }
  125. if (src3.Type != OperandType.Constant || src3.Value != 0x1c03)
  126. {
  127. return false;
  128. }
  129. bool isDdy = src2.Value == 2;
  130. bool isDdx = !isDdy;
  131. // We can replace any use by a FSWZADD with DDX/DDY, when
  132. // the following conditions are true:
  133. // - The mask should be 0b10100101 for DDY, or 0b10011001 for DDX.
  134. // - The first source operand must be the shuffle output.
  135. // - The second source operand must be the shuffle first source operand.
  136. INode[] uses = operation.Dest.UseOps.ToArray();
  137. foreach (INode use in uses)
  138. {
  139. if (!(use is Operation test))
  140. {
  141. continue;
  142. }
  143. if (!(use is Operation useOp) || useOp.Inst != Instruction.SwizzleAdd)
  144. {
  145. continue;
  146. }
  147. Operand fswzaddSrc1 = useOp.GetSource(0);
  148. Operand fswzaddSrc2 = useOp.GetSource(1);
  149. Operand fswzaddSrc3 = useOp.GetSource(2);
  150. if (fswzaddSrc1 != operation.Dest)
  151. {
  152. continue;
  153. }
  154. if (fswzaddSrc2 != operation.GetSource(0))
  155. {
  156. continue;
  157. }
  158. if (fswzaddSrc3.Type != OperandType.Constant)
  159. {
  160. continue;
  161. }
  162. int mask = fswzaddSrc3.Value;
  163. if ((isDdx && mask != 0b10011001) ||
  164. (isDdy && mask != 0b10100101))
  165. {
  166. continue;
  167. }
  168. useOp.TurnInto(isDdx ? Instruction.Ddx : Instruction.Ddy, fswzaddSrc2);
  169. modified = true;
  170. }
  171. return modified;
  172. }
  173. private static void RemoveNode(BasicBlock block, LinkedListNode<INode> llNode)
  174. {
  175. // Remove a node from the nodes list, and also remove itself
  176. // from all the use lists on the operands that this node uses.
  177. block.Operations.Remove(llNode);
  178. Queue<INode> nodes = new Queue<INode>();
  179. nodes.Enqueue(llNode.Value);
  180. while (nodes.TryDequeue(out INode node))
  181. {
  182. for (int index = 0; index < node.SourcesCount; index++)
  183. {
  184. Operand src = node.GetSource(index);
  185. if (src.Type != OperandType.LocalVariable)
  186. {
  187. continue;
  188. }
  189. if (src.UseOps.Remove(node) && src.UseOps.Count == 0)
  190. {
  191. nodes.Enqueue(src.AsgOp);
  192. }
  193. }
  194. }
  195. }
  196. private static bool IsUnused(INode node)
  197. {
  198. return !HasSideEffects(node) && DestIsLocalVar(node) && node.Dest.UseOps.Count == 0;
  199. }
  200. private static bool HasSideEffects(INode node)
  201. {
  202. if (node is Operation operation)
  203. {
  204. switch (operation.Inst & Instruction.Mask)
  205. {
  206. case Instruction.AtomicAdd:
  207. case Instruction.AtomicAnd:
  208. case Instruction.AtomicCompareAndSwap:
  209. case Instruction.AtomicMaxS32:
  210. case Instruction.AtomicMaxU32:
  211. case Instruction.AtomicMinS32:
  212. case Instruction.AtomicMinU32:
  213. case Instruction.AtomicOr:
  214. case Instruction.AtomicSwap:
  215. case Instruction.AtomicXor:
  216. return true;
  217. }
  218. }
  219. return false;
  220. }
  221. private static bool DestIsLocalVar(INode node)
  222. {
  223. return node.Dest != null && node.Dest.Type == OperandType.LocalVariable;
  224. }
  225. }
  226. }