LocalAlloc.cs 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. using System.Collections.Generic;
  2. namespace ChocolArm64.Translation
  3. {
  4. class LocalAlloc
  5. {
  6. private class PathIo
  7. {
  8. private Dictionary<ILBlock, long> _allInputs;
  9. private Dictionary<ILBlock, long> _cmnOutputs;
  10. private long _allOutputs;
  11. public PathIo()
  12. {
  13. _allInputs = new Dictionary<ILBlock, long>();
  14. _cmnOutputs = new Dictionary<ILBlock, long>();
  15. }
  16. public PathIo(ILBlock root, long inputs, long outputs) : this()
  17. {
  18. Set(root, inputs, outputs);
  19. }
  20. public void Set(ILBlock 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(ILBlock 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<ILBlock, PathIo> _intPaths;
  46. private Dictionary<ILBlock, PathIo> _vecPaths;
  47. private struct BlockIo
  48. {
  49. public ILBlock Block;
  50. public ILBlock 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 LocalAlloc(ILBlock[] graph, ILBlock root)
  58. {
  59. _intPaths = new Dictionary<ILBlock, PathIo>();
  60. _vecPaths = new Dictionary<ILBlock, 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(ILBlock[] graph, ILBlock 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(ILBlock 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(ILBlock[] 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 (ILBlock 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 (ILBlock 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(ILBlock root) => GetInputsImpl(root, _intPaths.Values);
  176. public long GetVecInputs(ILBlock root) => GetInputsImpl(root, _vecPaths.Values);
  177. private long GetInputsImpl(ILBlock 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(ILBlock block) => _intPaths[block].GetOutputs();
  187. public long GetVecOutputs(ILBlock block) => _vecPaths[block].GetOutputs();
  188. }
  189. }