ALocalAlloc.cs 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  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 = 55;
  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. }
  126. else
  127. {
  128. BlkIO.Entry = Current.Entry;
  129. BlkIO.IntInputs = Current.IntInputs;
  130. BlkIO.VecInputs = Current.VecInputs;
  131. BlkIO.IntOutputs = Current.IntOutputs;
  132. BlkIO.VecOutputs = Current.VecOutputs;
  133. }
  134. Enqueue(BlkIO);
  135. }
  136. if (Current.Block.Next != null)
  137. {
  138. EnqueueFromCurrent(Current.Block.Next, Current.Block.HasStateStore);
  139. }
  140. if (Current.Block.Branch != null)
  141. {
  142. EnqueueFromCurrent(Current.Block.Branch, false);
  143. }
  144. }
  145. }
  146. private void InitializeFast(AILBlock[] Graph)
  147. {
  148. //This is WAY faster than InitializeOptimal, but results in
  149. //uneeded loads and stores, so the resulting code will be slower.
  150. long IntInputs = 0;
  151. long IntOutputs = 0;
  152. long VecInputs = 0;
  153. long VecOutputs = 0;
  154. foreach (AILBlock Block in Graph)
  155. {
  156. IntInputs |= Block.IntInputs;
  157. IntOutputs |= Block.IntOutputs;
  158. VecInputs |= Block.VecInputs;
  159. VecOutputs |= Block.VecOutputs;
  160. }
  161. //It's possible that not all code paths writes to those output registers,
  162. //in those cases if we attempt to write an output registers that was
  163. //not written, we will be just writing zero and messing up the old register value.
  164. //So we just need to ensure that all outputs are loaded.
  165. IntInputs |= IntOutputs;
  166. VecInputs |= VecOutputs;
  167. foreach (AILBlock Block in Graph)
  168. {
  169. IntPaths.Add(Block, new PathIo(Block, IntInputs, IntOutputs));
  170. VecPaths.Add(Block, new PathIo(Block, VecInputs, VecOutputs));
  171. }
  172. }
  173. public long GetIntInputs(AILBlock Root) => GetInputsImpl(Root, IntPaths.Values);
  174. public long GetVecInputs(AILBlock Root) => GetInputsImpl(Root, VecPaths.Values);
  175. private long GetInputsImpl(AILBlock Root, IEnumerable<PathIo> Values)
  176. {
  177. long Inputs = 0;
  178. foreach (PathIo Path in Values)
  179. {
  180. Inputs |= Path.GetInputs(Root);
  181. }
  182. return Inputs;
  183. }
  184. public long GetIntOutputs(AILBlock Block) => IntPaths[Block].GetOutputs();
  185. public long GetVecOutputs(AILBlock Block) => VecPaths[Block].GetOutputs();
  186. }
  187. }