RegisterUsage.cs 9.4 KB

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