ALocalAlloc.cs 7.7 KB

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