LocalAlloc.cs 8.7 KB

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