StructuredProgramContext.cs 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. using Ryujinx.Graphics.Shader.IntermediateRepresentation;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using static Ryujinx.Graphics.Shader.StructuredIr.AstHelper;
  5. namespace Ryujinx.Graphics.Shader.StructuredIr
  6. {
  7. class StructuredProgramContext
  8. {
  9. private HashSet<BasicBlock> _loopTails;
  10. private Stack<(AstBlock Block, int EndIndex)> _blockStack;
  11. private Dictionary<Operand, AstOperand> _localsMap;
  12. private Dictionary<int, AstAssignment> _gotoTempAsgs;
  13. private List<GotoStatement> _gotos;
  14. private AstBlock _currBlock;
  15. private int _currEndIndex;
  16. public StructuredProgramInfo Info { get; }
  17. public StructuredProgramContext(int blocksCount)
  18. {
  19. _loopTails = new HashSet<BasicBlock>();
  20. _blockStack = new Stack<(AstBlock, int)>();
  21. _localsMap = new Dictionary<Operand, AstOperand>();
  22. _gotoTempAsgs = new Dictionary<int, AstAssignment>();
  23. _gotos = new List<GotoStatement>();
  24. _currBlock = new AstBlock(AstBlockType.Main);
  25. _currEndIndex = blocksCount;
  26. Info = new StructuredProgramInfo(_currBlock);
  27. }
  28. public void EnterBlock(BasicBlock block)
  29. {
  30. while (_currEndIndex == block.Index)
  31. {
  32. (_currBlock, _currEndIndex) = _blockStack.Pop();
  33. }
  34. if (_gotoTempAsgs.TryGetValue(block.Index, out AstAssignment gotoTempAsg))
  35. {
  36. AddGotoTempReset(block, gotoTempAsg);
  37. }
  38. LookForDoWhileStatements(block);
  39. }
  40. public void LeaveBlock(BasicBlock block, Operation branchOp)
  41. {
  42. LookForIfStatements(block, branchOp);
  43. }
  44. private void LookForDoWhileStatements(BasicBlock block)
  45. {
  46. // Check if we have any predecessor whose index is greater than the
  47. // current block, this indicates a loop.
  48. bool done = false;
  49. foreach (BasicBlock predecessor in block.Predecessors.OrderByDescending(x => x.Index))
  50. {
  51. if (predecessor.Index < block.Index)
  52. {
  53. break;
  54. }
  55. if (predecessor.Index < _currEndIndex && !done)
  56. {
  57. Operation branchOp = (Operation)predecessor.GetLastOp();
  58. NewBlock(AstBlockType.DoWhile, branchOp, predecessor.Index + 1);
  59. _loopTails.Add(predecessor);
  60. done = true;
  61. }
  62. else
  63. {
  64. AddGotoTempReset(block, GetGotoTempAsg(block.Index));
  65. break;
  66. }
  67. }
  68. }
  69. private void LookForIfStatements(BasicBlock block, Operation branchOp)
  70. {
  71. if (block.Branch == null)
  72. {
  73. return;
  74. }
  75. bool isLoop = block.Branch.Index <= block.Index;
  76. if (block.Branch.Index <= _currEndIndex && !isLoop)
  77. {
  78. NewBlock(AstBlockType.If, branchOp, block.Branch.Index);
  79. }
  80. else if (!_loopTails.Contains(block))
  81. {
  82. AstAssignment gotoTempAsg = GetGotoTempAsg(block.Branch.Index);
  83. IAstNode cond = GetBranchCond(AstBlockType.DoWhile, branchOp);
  84. AddNode(Assign(gotoTempAsg.Destination, cond));
  85. AstOperation branch = new AstOperation(branchOp.Inst);
  86. AddNode(branch);
  87. GotoStatement gotoStmt = new GotoStatement(branch, gotoTempAsg, isLoop);
  88. _gotos.Add(gotoStmt);
  89. }
  90. }
  91. private AstAssignment GetGotoTempAsg(int index)
  92. {
  93. if (_gotoTempAsgs.TryGetValue(index, out AstAssignment gotoTempAsg))
  94. {
  95. return gotoTempAsg;
  96. }
  97. AstOperand gotoTemp = NewTemp(VariableType.Bool);
  98. gotoTempAsg = Assign(gotoTemp, Const(IrConsts.False));
  99. _gotoTempAsgs.Add(index, gotoTempAsg);
  100. return gotoTempAsg;
  101. }
  102. private void AddGotoTempReset(BasicBlock block, AstAssignment gotoTempAsg)
  103. {
  104. AddNode(gotoTempAsg);
  105. // For block 0, we don't need to add the extra "reset" at the beginning,
  106. // because it is already the first node to be executed on the shader,
  107. // so it is reset to false by the "local" assignment anyway.
  108. if (block.Index != 0)
  109. {
  110. Info.MainBlock.AddFirst(Assign(gotoTempAsg.Destination, Const(IrConsts.False)));
  111. }
  112. }
  113. private void NewBlock(AstBlockType type, Operation branchOp, int endIndex)
  114. {
  115. NewBlock(type, GetBranchCond(type, branchOp), endIndex);
  116. }
  117. private void NewBlock(AstBlockType type, IAstNode cond, int endIndex)
  118. {
  119. AstBlock childBlock = new AstBlock(type, cond);
  120. AddNode(childBlock);
  121. _blockStack.Push((_currBlock, _currEndIndex));
  122. _currBlock = childBlock;
  123. _currEndIndex = endIndex;
  124. }
  125. private IAstNode GetBranchCond(AstBlockType type, Operation branchOp)
  126. {
  127. IAstNode cond;
  128. if (branchOp.Inst == Instruction.Branch)
  129. {
  130. cond = Const(type == AstBlockType.If ? IrConsts.False : IrConsts.True);
  131. }
  132. else
  133. {
  134. cond = GetOperandUse(branchOp.GetSource(0));
  135. Instruction invInst = type == AstBlockType.If
  136. ? Instruction.BranchIfTrue
  137. : Instruction.BranchIfFalse;
  138. if (branchOp.Inst == invInst)
  139. {
  140. cond = new AstOperation(Instruction.LogicalNot, cond);
  141. }
  142. }
  143. return cond;
  144. }
  145. public void AddNode(IAstNode node)
  146. {
  147. _currBlock.Add(node);
  148. }
  149. public GotoStatement[] GetGotos()
  150. {
  151. return _gotos.ToArray();
  152. }
  153. private AstOperand NewTemp(VariableType type)
  154. {
  155. AstOperand newTemp = Local(type);
  156. Info.Locals.Add(newTemp);
  157. return newTemp;
  158. }
  159. public AstOperand GetOperandDef(Operand operand)
  160. {
  161. if (TryGetUserAttributeIndex(operand, out int attrIndex))
  162. {
  163. Info.OAttributes.Add(attrIndex);
  164. }
  165. return GetOperand(operand);
  166. }
  167. public AstOperand GetOperandUse(Operand operand)
  168. {
  169. if (TryGetUserAttributeIndex(operand, out int attrIndex))
  170. {
  171. Info.IAttributes.Add(attrIndex);
  172. }
  173. else if (operand.Type == OperandType.ConstantBuffer)
  174. {
  175. Info.CBuffers.Add(operand.GetCbufSlot());
  176. }
  177. return GetOperand(operand);
  178. }
  179. private AstOperand GetOperand(Operand operand)
  180. {
  181. if (operand == null)
  182. {
  183. return null;
  184. }
  185. if (operand.Type != OperandType.LocalVariable)
  186. {
  187. return new AstOperand(operand);
  188. }
  189. if (!_localsMap.TryGetValue(operand, out AstOperand astOperand))
  190. {
  191. astOperand = new AstOperand(operand);
  192. _localsMap.Add(operand, astOperand);
  193. Info.Locals.Add(astOperand);
  194. }
  195. return astOperand;
  196. }
  197. private static bool TryGetUserAttributeIndex(Operand operand, out int attrIndex)
  198. {
  199. if (operand.Type == OperandType.Attribute)
  200. {
  201. if (operand.Value >= AttributeConsts.UserAttributeBase &&
  202. operand.Value < AttributeConsts.UserAttributeEnd)
  203. {
  204. attrIndex = (operand.Value - AttributeConsts.UserAttributeBase) >> 4;
  205. return true;
  206. }
  207. else if (operand.Value >= AttributeConsts.FragmentOutputColorBase &&
  208. operand.Value < AttributeConsts.FragmentOutputColorEnd)
  209. {
  210. attrIndex = (operand.Value - AttributeConsts.FragmentOutputColorBase) >> 4;
  211. return true;
  212. }
  213. }
  214. attrIndex = 0;
  215. return false;
  216. }
  217. }
  218. }