PtcJumpTable.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. using ARMeilleure.Translation.Cache;
  2. using System;
  3. using System.Collections.Concurrent;
  4. using System.Collections.Generic;
  5. using System.Diagnostics;
  6. using System.IO;
  7. using System.Runtime.InteropServices;
  8. using static ARMeilleure.Translation.PTC.PtcFormatter;
  9. namespace ARMeilleure.Translation.PTC
  10. {
  11. class PtcJumpTable
  12. {
  13. [StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 16*/)]
  14. public struct TableEntry<TAddress>
  15. {
  16. public int EntryIndex;
  17. public long GuestAddress;
  18. public TAddress HostAddress; // int
  19. public TableEntry(int entryIndex, long guestAddress, TAddress hostAddress)
  20. {
  21. EntryIndex = entryIndex;
  22. GuestAddress = guestAddress;
  23. HostAddress = hostAddress;
  24. }
  25. }
  26. public enum DirectHostAddress : int
  27. {
  28. CallStub = 0,
  29. TailCallStub = 1,
  30. Host = 2
  31. }
  32. public enum IndirectHostAddress : int
  33. {
  34. CallStub = 0,
  35. TailCallStub = 1
  36. }
  37. private readonly List<TableEntry<DirectHostAddress>> _jumpTable;
  38. private readonly List<TableEntry<IndirectHostAddress>> _dynamicTable;
  39. public List<ulong> Targets { get; }
  40. public Dictionary<ulong, List<int>> Dependants { get; }
  41. public Dictionary<ulong, List<int>> Owners { get; }
  42. public PtcJumpTable()
  43. {
  44. _jumpTable = new List<TableEntry<DirectHostAddress>>();
  45. _dynamicTable = new List<TableEntry<IndirectHostAddress>>();
  46. Targets = new List<ulong>();
  47. Dependants = new Dictionary<ulong, List<int>>();
  48. Owners = new Dictionary<ulong, List<int>>();
  49. }
  50. public PtcJumpTable(
  51. List<TableEntry<DirectHostAddress>> jumpTable, List<TableEntry<IndirectHostAddress>> dynamicTable,
  52. List<ulong> targets, Dictionary<ulong, List<int>> dependants, Dictionary<ulong, List<int>> owners)
  53. {
  54. _jumpTable = jumpTable;
  55. _dynamicTable = dynamicTable;
  56. Targets = targets;
  57. Dependants = dependants;
  58. Owners = owners;
  59. }
  60. public static PtcJumpTable Deserialize(Stream stream)
  61. {
  62. var jumpTable = DeserializeList<TableEntry<DirectHostAddress>>(stream);
  63. var dynamicTable = DeserializeList<TableEntry<IndirectHostAddress>>(stream);
  64. var targets = DeserializeList<ulong>(stream);
  65. var dependants = DeserializeDictionary<ulong, List<int>>(stream, (stream) => DeserializeList<int>(stream));
  66. var owners = DeserializeDictionary<ulong, List<int>>(stream, (stream) => DeserializeList<int>(stream));
  67. return new PtcJumpTable(jumpTable, dynamicTable, targets, dependants, owners);
  68. }
  69. public static int GetSerializeSize(PtcJumpTable ptcJumpTable)
  70. {
  71. int size = 0;
  72. size += GetSerializeSizeList(ptcJumpTable._jumpTable);
  73. size += GetSerializeSizeList(ptcJumpTable._dynamicTable);
  74. size += GetSerializeSizeList(ptcJumpTable.Targets);
  75. size += GetSerializeSizeDictionary(ptcJumpTable.Dependants, (list) => GetSerializeSizeList(list));
  76. size += GetSerializeSizeDictionary(ptcJumpTable.Owners, (list) => GetSerializeSizeList(list));
  77. return size;
  78. }
  79. public static void Serialize(Stream stream, PtcJumpTable ptcJumpTable)
  80. {
  81. SerializeList(stream, ptcJumpTable._jumpTable);
  82. SerializeList(stream, ptcJumpTable._dynamicTable);
  83. SerializeList(stream, ptcJumpTable.Targets);
  84. SerializeDictionary(stream, ptcJumpTable.Dependants, (stream, list) => SerializeList(stream, list));
  85. SerializeDictionary(stream, ptcJumpTable.Owners, (stream, list) => SerializeList(stream, list));
  86. }
  87. public void Initialize(JumpTable jumpTable)
  88. {
  89. Targets.Clear();
  90. foreach (ulong guestAddress in jumpTable.Targets.Keys)
  91. {
  92. Targets.Add(guestAddress);
  93. }
  94. Dependants.Clear();
  95. foreach (var kv in jumpTable.Dependants)
  96. {
  97. Dependants.Add(kv.Key, new List<int>(kv.Value));
  98. }
  99. Owners.Clear();
  100. foreach (var kv in jumpTable.Owners)
  101. {
  102. Owners.Add(kv.Key, new List<int>(kv.Value));
  103. }
  104. }
  105. // For future use.
  106. public void Clean(ulong guestAddress)
  107. {
  108. if (Owners.TryGetValue(guestAddress, out List<int> entries))
  109. {
  110. foreach (int entry in entries)
  111. {
  112. if ((entry & JumpTable.DynamicEntryTag) == 0)
  113. {
  114. int removed = _jumpTable.RemoveAll(tableEntry => tableEntry.EntryIndex == entry);
  115. Debug.Assert(removed == 1);
  116. }
  117. else
  118. {
  119. if (JumpTable.DynamicTableElems > 1)
  120. {
  121. throw new NotSupportedException();
  122. }
  123. int removed = _dynamicTable.RemoveAll(tableEntry => tableEntry.EntryIndex == (entry & ~JumpTable.DynamicEntryTag));
  124. Debug.Assert(removed == 1);
  125. }
  126. }
  127. }
  128. Targets.Remove(guestAddress);
  129. Dependants.Remove(guestAddress);
  130. Owners.Remove(guestAddress);
  131. }
  132. public void ClearIfNeeded()
  133. {
  134. if (_jumpTable.Count == 0 && _dynamicTable.Count == 0 &&
  135. Targets.Count == 0 && Dependants.Count == 0 && Owners.Count == 0)
  136. {
  137. return;
  138. }
  139. _jumpTable.Clear();
  140. _jumpTable.TrimExcess();
  141. _dynamicTable.Clear();
  142. _dynamicTable.TrimExcess();
  143. Targets.Clear();
  144. Targets.TrimExcess();
  145. Dependants.Clear();
  146. Dependants.TrimExcess();
  147. Owners.Clear();
  148. Owners.TrimExcess();
  149. }
  150. public void WriteJumpTable(JumpTable jumpTable, ConcurrentDictionary<ulong, TranslatedFunction> funcs)
  151. {
  152. // Writes internal state to jump table in-memory, after PtcJumpTable was deserialized.
  153. foreach (var tableEntry in _jumpTable)
  154. {
  155. long guestAddress = tableEntry.GuestAddress;
  156. DirectHostAddress directHostAddress = tableEntry.HostAddress;
  157. long hostAddress;
  158. if (directHostAddress == DirectHostAddress.CallStub)
  159. {
  160. hostAddress = DirectCallStubs.DirectCallStub(false).ToInt64();
  161. }
  162. else if (directHostAddress == DirectHostAddress.TailCallStub)
  163. {
  164. hostAddress = DirectCallStubs.DirectCallStub(true).ToInt64();
  165. }
  166. else if (directHostAddress == DirectHostAddress.Host)
  167. {
  168. if (funcs.TryGetValue((ulong)guestAddress, out TranslatedFunction func))
  169. {
  170. hostAddress = func.FuncPtr.ToInt64();
  171. }
  172. else
  173. {
  174. if (!PtcProfiler.ProfiledFuncs.TryGetValue((ulong)guestAddress, out var value) || !value.HighCq)
  175. {
  176. throw new KeyNotFoundException($"({nameof(guestAddress)} = 0x{(ulong)guestAddress:X16})");
  177. }
  178. hostAddress = 0L;
  179. }
  180. }
  181. else
  182. {
  183. throw new InvalidOperationException(nameof(directHostAddress));
  184. }
  185. int entry = tableEntry.EntryIndex;
  186. jumpTable.Table.SetEntry(entry);
  187. jumpTable.ExpandIfNeededJumpTable(entry);
  188. IntPtr addr = jumpTable.GetEntryAddressJumpTable(entry);
  189. Marshal.WriteInt64(addr, 0, guestAddress);
  190. Marshal.WriteInt64(addr, 8, hostAddress);
  191. }
  192. }
  193. public void WriteDynamicTable(JumpTable jumpTable)
  194. {
  195. // Writes internal state to jump table in-memory, after PtcJumpTable was deserialized.
  196. if (JumpTable.DynamicTableElems > 1)
  197. {
  198. throw new NotSupportedException();
  199. }
  200. foreach (var tableEntry in _dynamicTable)
  201. {
  202. long guestAddress = tableEntry.GuestAddress;
  203. IndirectHostAddress indirectHostAddress = tableEntry.HostAddress;
  204. long hostAddress;
  205. if (indirectHostAddress == IndirectHostAddress.CallStub)
  206. {
  207. hostAddress = DirectCallStubs.IndirectCallStub(false).ToInt64();
  208. }
  209. else if (indirectHostAddress == IndirectHostAddress.TailCallStub)
  210. {
  211. hostAddress = DirectCallStubs.IndirectCallStub(true).ToInt64();
  212. }
  213. else
  214. {
  215. throw new InvalidOperationException(nameof(indirectHostAddress));
  216. }
  217. int entry = tableEntry.EntryIndex;
  218. jumpTable.DynTable.SetEntry(entry);
  219. jumpTable.ExpandIfNeededDynamicTable(entry);
  220. IntPtr addr = jumpTable.GetEntryAddressDynamicTable(entry);
  221. Marshal.WriteInt64(addr, 0, guestAddress);
  222. Marshal.WriteInt64(addr, 8, hostAddress);
  223. }
  224. }
  225. public void ReadJumpTable(JumpTable jumpTable)
  226. {
  227. // Reads in-memory jump table state and store internally for PtcJumpTable serialization.
  228. _jumpTable.Clear();
  229. IEnumerable<int> entries = jumpTable.Table.GetEntries();
  230. foreach (int entry in entries)
  231. {
  232. IntPtr addr = jumpTable.GetEntryAddressJumpTable(entry);
  233. long guestAddress = Marshal.ReadInt64(addr, 0);
  234. long hostAddress = Marshal.ReadInt64(addr, 8);
  235. DirectHostAddress directHostAddress;
  236. if (hostAddress == DirectCallStubs.DirectCallStub(false).ToInt64())
  237. {
  238. directHostAddress = DirectHostAddress.CallStub;
  239. }
  240. else if (hostAddress == DirectCallStubs.DirectCallStub(true).ToInt64())
  241. {
  242. directHostAddress = DirectHostAddress.TailCallStub;
  243. }
  244. else
  245. {
  246. directHostAddress = DirectHostAddress.Host;
  247. }
  248. _jumpTable.Add(new TableEntry<DirectHostAddress>(entry, guestAddress, directHostAddress));
  249. }
  250. }
  251. public void ReadDynamicTable(JumpTable jumpTable)
  252. {
  253. // Reads in-memory jump table state and store internally for PtcJumpTable serialization.
  254. if (JumpTable.DynamicTableElems > 1)
  255. {
  256. throw new NotSupportedException();
  257. }
  258. _dynamicTable.Clear();
  259. IEnumerable<int> entries = jumpTable.DynTable.GetEntries();
  260. foreach (int entry in entries)
  261. {
  262. IntPtr addr = jumpTable.GetEntryAddressDynamicTable(entry);
  263. long guestAddress = Marshal.ReadInt64(addr, 0);
  264. long hostAddress = Marshal.ReadInt64(addr, 8);
  265. IndirectHostAddress indirectHostAddress;
  266. if (hostAddress == DirectCallStubs.IndirectCallStub(false).ToInt64())
  267. {
  268. indirectHostAddress = IndirectHostAddress.CallStub;
  269. }
  270. else if (hostAddress == DirectCallStubs.IndirectCallStub(true).ToInt64())
  271. {
  272. indirectHostAddress = IndirectHostAddress.TailCallStub;
  273. }
  274. else
  275. {
  276. throw new InvalidOperationException($"({nameof(hostAddress)} = 0x{hostAddress:X16})");
  277. }
  278. _dynamicTable.Add(new TableEntry<IndirectHostAddress>(entry, guestAddress, indirectHostAddress));
  279. }
  280. }
  281. }
  282. }