ALocalAlloc.cs 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. using System.Collections.Generic;
  2. namespace ChocolArm64.Translation
  3. {
  4. class ALocalAlloc
  5. {
  6. private class PathIo
  7. {
  8. private Dictionary<AILBlock, long> AllInputs;
  9. private Dictionary<AILBlock, long> CmnOutputs;
  10. private long AllOutputs;
  11. public PathIo()
  12. {
  13. AllInputs = new Dictionary<AILBlock, long>();
  14. CmnOutputs = new Dictionary<AILBlock, long>();
  15. }
  16. public PathIo(AILBlock Root, long Inputs, long Outputs) : this()
  17. {
  18. Set(Root, Inputs, Outputs);
  19. }
  20. public void Set(AILBlock Root, long Inputs, long Outputs)
  21. {
  22. if (!AllInputs.TryAdd(Root, Inputs))
  23. {
  24. AllInputs[Root] |= Inputs;
  25. }
  26. if (!CmnOutputs.TryAdd(Root, Outputs))
  27. {
  28. CmnOutputs[Root] &= Outputs;
  29. }
  30. AllOutputs |= Outputs;
  31. }
  32. public long GetInputs(AILBlock Root)
  33. {
  34. if (AllInputs.TryGetValue(Root, out long Inputs))
  35. {
  36. return Inputs | (AllOutputs & ~CmnOutputs[Root]);
  37. }
  38. return 0;
  39. }
  40. public long GetOutputs()
  41. {
  42. return AllOutputs;
  43. }
  44. }
  45. private Dictionary<AILBlock, PathIo> IntPaths;
  46. private Dictionary<AILBlock, PathIo> VecPaths;
  47. private struct BlockIo
  48. {
  49. public AILBlock Block;
  50. public AILBlock Entry;
  51. public long IntInputs;
  52. public long VecInputs;
  53. public long IntOutputs;
  54. public long VecOutputs;
  55. }
  56. private const int MaxOptGraphLength = 120;
  57. public ALocalAlloc(AILBlock[] Graph, AILBlock Root)
  58. {
  59. IntPaths = new Dictionary<AILBlock, PathIo>();
  60. VecPaths = new Dictionary<AILBlock, PathIo>();
  61. if (Graph.Length < MaxOptGraphLength)
  62. {
  63. InitializeOptimal(Graph, Root);
  64. }
  65. else
  66. {
  67. InitializeFast(Graph);
  68. }
  69. }
  70. private void InitializeOptimal(AILBlock[] Graph, AILBlock Root)
  71. {
  72. //This will go through all possible paths on the graph,
  73. //and store all inputs/outputs for each block. A register
  74. //that was previously written to already is not considered an input.
  75. //When a block can be reached by more than one path, then the
  76. //output from all paths needs to be set for this block, and
  77. //only outputs present in all of the parent blocks can be considered
  78. //when doing input elimination. Each block chain have a root, that's where
  79. //the code starts executing. They are present on the subroutine start point,
  80. //and on call return points too (address written to X30 by BL).
  81. HashSet<BlockIo> Visited = new HashSet<BlockIo>();
  82. Queue<BlockIo> Unvisited = new Queue<BlockIo>();
  83. void Enqueue(BlockIo Block)
  84. {
  85. if (!Visited.Contains(Block))
  86. {
  87. Unvisited.Enqueue(Block);
  88. Visited.Add(Block);
  89. }
  90. }
  91. Enqueue(new BlockIo()
  92. {
  93. Block = Root,
  94. Entry = Root
  95. });
  96. while (Unvisited.Count > 0)
  97. {
  98. BlockIo Current = Unvisited.Dequeue();
  99. Current.IntInputs |= Current.Block.IntInputs & ~Current.IntOutputs;
  100. Current.VecInputs |= Current.Block.VecInputs & ~Current.VecOutputs;
  101. Current.IntOutputs |= Current.Block.IntOutputs;
  102. Current.VecOutputs |= Current.Block.VecOutputs;
  103. //Check if this is a exit block
  104. //(a block that returns or calls another sub).
  105. if ((Current.Block.Next == null &&
  106. Current.Block.Branch == null) || Current.Block.HasStateStore)
  107. {
  108. if (!IntPaths.TryGetValue(Current.Block, out PathIo IntPath))
  109. {
  110. IntPaths.Add(Current.Block, IntPath = new PathIo());
  111. }
  112. if (!VecPaths.TryGetValue(Current.Block, out PathIo VecPath))
  113. {
  114. VecPaths.Add(Current.Block, VecPath = new PathIo());
  115. }
  116. IntPath.Set(Current.Entry, Current.IntInputs, Current.IntOutputs);
  117. VecPath.Set(Current.Entry, Current.VecInputs, Current.VecOutputs);
  118. }
  119. void EnqueueFromCurrent(AILBlock Block, bool RetTarget)
  120. {
  121. BlockIo BlkIO = new BlockIo() { Block = Block };
  122. if (RetTarget)
  123. {
  124. BlkIO.Entry = Block;
  125. BlkIO.IntInputs = 0;
  126. BlkIO.VecInputs = 0;
  127. BlkIO.IntOutputs = 0;
  128. BlkIO.VecOutputs = 0;
  129. }
  130. else
  131. {
  132. BlkIO.Entry = Current.Entry;
  133. BlkIO.IntInputs = Current.IntInputs;
  134. BlkIO.VecInputs = Current.VecInputs;
  135. BlkIO.IntOutputs = Current.IntOutputs;
  136. BlkIO.VecOutputs = Current.VecOutputs;
  137. }
  138. Enqueue(BlkIO);
  139. }
  140. if (Current.Block.Next != null)
  141. {
  142. EnqueueFromCurrent(Current.Block.Next, Current.Block.HasStateStore);
  143. }
  144. if (Current.Block.Branch != null)
  145. {
  146. EnqueueFromCurrent(Current.Block.Branch, false);
  147. }
  148. }
  149. }
  150. private void InitializeFast(AILBlock[] Graph)
  151. {
  152. //This is WAY faster than InitializeOptimal, but results in
  153. //uneeded loads and stores, so the resulting code will be slower.
  154. long IntInputs = 0;
  155. long IntOutputs = 0;
  156. long VecInputs = 0;
  157. long VecOutputs = 0;
  158. foreach (AILBlock Block in Graph)
  159. {
  160. IntInputs |= Block.IntInputs;
  161. IntOutputs |= Block.IntOutputs;
  162. VecInputs |= Block.VecInputs;
  163. VecOutputs |= Block.VecOutputs;
  164. }
  165. //It's possible that not all code paths writes to those output registers,
  166. //in those cases if we attempt to write an output registers that was
  167. //not written, we will be just writing zero and messing up the old register value.
  168. //So we just need to ensure that all outputs are loaded.
  169. IntInputs |= IntOutputs;
  170. VecInputs |= VecOutputs;
  171. foreach (AILBlock Block in Graph)
  172. {
  173. IntPaths.Add(Block, new PathIo(Block, IntInputs, IntOutputs));
  174. VecPaths.Add(Block, new PathIo(Block, VecInputs, VecOutputs));
  175. }
  176. }
  177. public long GetIntInputs(AILBlock Root) => GetInputsImpl(Root, IntPaths.Values);
  178. public long GetVecInputs(AILBlock Root) => GetInputsImpl(Root, VecPaths.Values);
  179. private long GetInputsImpl(AILBlock Root, IEnumerable<PathIo> Values)
  180. {
  181. long Inputs = 0;
  182. foreach (PathIo Path in Values)
  183. {
  184. Inputs |= Path.GetInputs(Root);
  185. }
  186. return Inputs;
  187. }
  188. public long GetIntOutputs(AILBlock Block) => IntPaths[Block].GetOutputs();
  189. public long GetVecOutputs(AILBlock Block) => VecPaths[Block].GetOutputs();
  190. }
  191. }