Ptc.cs 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063
  1. using ARMeilleure.CodeGen;
  2. using ARMeilleure.CodeGen.Unwinding;
  3. using ARMeilleure.CodeGen.X86;
  4. using ARMeilleure.Common;
  5. using ARMeilleure.Memory;
  6. using ARMeilleure.Translation.Cache;
  7. using Ryujinx.Common;
  8. using Ryujinx.Common.Configuration;
  9. using Ryujinx.Common.Logging;
  10. using System;
  11. using System.Buffers.Binary;
  12. using System.Collections.Concurrent;
  13. using System.Collections.Generic;
  14. using System.Diagnostics;
  15. using System.IO;
  16. using System.IO.Compression;
  17. using System.Runtime;
  18. using System.Runtime.CompilerServices;
  19. using System.Runtime.InteropServices;
  20. using System.Threading;
  21. using static ARMeilleure.Translation.PTC.PtcFormatter;
  22. namespace ARMeilleure.Translation.PTC
  23. {
  24. public static class Ptc
  25. {
  26. private const string OuterHeaderMagicString = "PTCohd\0\0";
  27. private const string InnerHeaderMagicString = "PTCihd\0\0";
  28. private const uint InternalVersion = 2228; //! To be incremented manually for each change to the ARMeilleure project.
  29. private const string ActualDir = "0";
  30. private const string BackupDir = "1";
  31. private const string TitleIdTextDefault = "0000000000000000";
  32. private const string DisplayVersionDefault = "0";
  33. internal static readonly Symbol PageTableSymbol = new(SymbolType.Special, 1);
  34. internal static readonly Symbol CountTableSymbol = new(SymbolType.Special, 2);
  35. internal static readonly Symbol DispatchStubSymbol = new(SymbolType.Special, 3);
  36. private const byte FillingByte = 0x00;
  37. private const CompressionLevel SaveCompressionLevel = CompressionLevel.Fastest;
  38. // Carriers.
  39. private static MemoryStream _infosStream;
  40. private static List<byte[]> _codesList;
  41. private static MemoryStream _relocsStream;
  42. private static MemoryStream _unwindInfosStream;
  43. private static readonly ulong _outerHeaderMagic;
  44. private static readonly ulong _innerHeaderMagic;
  45. private static readonly ManualResetEvent _waitEvent;
  46. private static readonly object _lock;
  47. private static bool _disposed;
  48. internal static string TitleIdText { get; private set; }
  49. internal static string DisplayVersion { get; private set; }
  50. private static MemoryManagerMode _memoryMode;
  51. internal static string CachePathActual { get; private set; }
  52. internal static string CachePathBackup { get; private set; }
  53. internal static PtcState State { get; private set; }
  54. // Progress reporting helpers.
  55. private static volatile int _translateCount;
  56. private static volatile int _translateTotalCount;
  57. public static event Action<PtcLoadingState, int, int> PtcStateChanged;
  58. static Ptc()
  59. {
  60. InitializeCarriers();
  61. _outerHeaderMagic = BinaryPrimitives.ReadUInt64LittleEndian(EncodingCache.UTF8NoBOM.GetBytes(OuterHeaderMagicString).AsSpan());
  62. _innerHeaderMagic = BinaryPrimitives.ReadUInt64LittleEndian(EncodingCache.UTF8NoBOM.GetBytes(InnerHeaderMagicString).AsSpan());
  63. _waitEvent = new ManualResetEvent(true);
  64. _lock = new object();
  65. _disposed = false;
  66. TitleIdText = TitleIdTextDefault;
  67. DisplayVersion = DisplayVersionDefault;
  68. CachePathActual = string.Empty;
  69. CachePathBackup = string.Empty;
  70. Disable();
  71. }
  72. public static void Initialize(string titleIdText, string displayVersion, bool enabled, MemoryManagerMode memoryMode)
  73. {
  74. Wait();
  75. PtcProfiler.Wait();
  76. PtcProfiler.ClearEntries();
  77. Logger.Info?.Print(LogClass.Ptc, $"Initializing Profiled Persistent Translation Cache (enabled: {enabled}).");
  78. if (!enabled || string.IsNullOrEmpty(titleIdText) || titleIdText == TitleIdTextDefault)
  79. {
  80. TitleIdText = TitleIdTextDefault;
  81. DisplayVersion = DisplayVersionDefault;
  82. CachePathActual = string.Empty;
  83. CachePathBackup = string.Empty;
  84. Disable();
  85. return;
  86. }
  87. TitleIdText = titleIdText;
  88. DisplayVersion = !string.IsNullOrEmpty(displayVersion) ? displayVersion : DisplayVersionDefault;
  89. _memoryMode = memoryMode;
  90. string workPathActual = Path.Combine(AppDataManager.GamesDirPath, TitleIdText, "cache", "cpu", ActualDir);
  91. string workPathBackup = Path.Combine(AppDataManager.GamesDirPath, TitleIdText, "cache", "cpu", BackupDir);
  92. if (!Directory.Exists(workPathActual))
  93. {
  94. Directory.CreateDirectory(workPathActual);
  95. }
  96. if (!Directory.Exists(workPathBackup))
  97. {
  98. Directory.CreateDirectory(workPathBackup);
  99. }
  100. CachePathActual = Path.Combine(workPathActual, DisplayVersion);
  101. CachePathBackup = Path.Combine(workPathBackup, DisplayVersion);
  102. PreLoad();
  103. PtcProfiler.PreLoad();
  104. Enable();
  105. }
  106. private static void InitializeCarriers()
  107. {
  108. _infosStream = new MemoryStream();
  109. _codesList = new List<byte[]>();
  110. _relocsStream = new MemoryStream();
  111. _unwindInfosStream = new MemoryStream();
  112. }
  113. private static void DisposeCarriers()
  114. {
  115. _infosStream.Dispose();
  116. _codesList.Clear();
  117. _relocsStream.Dispose();
  118. _unwindInfosStream.Dispose();
  119. }
  120. private static bool AreCarriersEmpty()
  121. {
  122. return _infosStream.Length == 0L && _codesList.Count == 0 && _relocsStream.Length == 0L && _unwindInfosStream.Length == 0L;
  123. }
  124. private static void ResetCarriersIfNeeded()
  125. {
  126. if (AreCarriersEmpty())
  127. {
  128. return;
  129. }
  130. DisposeCarriers();
  131. InitializeCarriers();
  132. }
  133. private static void PreLoad()
  134. {
  135. string fileNameActual = string.Concat(CachePathActual, ".cache");
  136. string fileNameBackup = string.Concat(CachePathBackup, ".cache");
  137. FileInfo fileInfoActual = new FileInfo(fileNameActual);
  138. FileInfo fileInfoBackup = new FileInfo(fileNameBackup);
  139. if (fileInfoActual.Exists && fileInfoActual.Length != 0L)
  140. {
  141. if (!Load(fileNameActual, false))
  142. {
  143. if (fileInfoBackup.Exists && fileInfoBackup.Length != 0L)
  144. {
  145. Load(fileNameBackup, true);
  146. }
  147. }
  148. }
  149. else if (fileInfoBackup.Exists && fileInfoBackup.Length != 0L)
  150. {
  151. Load(fileNameBackup, true);
  152. }
  153. }
  154. private static unsafe bool Load(string fileName, bool isBackup)
  155. {
  156. using (FileStream compressedStream = new(fileName, FileMode.Open))
  157. using (DeflateStream deflateStream = new(compressedStream, CompressionMode.Decompress, true))
  158. {
  159. OuterHeader outerHeader = DeserializeStructure<OuterHeader>(compressedStream);
  160. if (!outerHeader.IsHeaderValid())
  161. {
  162. InvalidateCompressedStream(compressedStream);
  163. return false;
  164. }
  165. if (outerHeader.Magic != _outerHeaderMagic)
  166. {
  167. InvalidateCompressedStream(compressedStream);
  168. return false;
  169. }
  170. if (outerHeader.CacheFileVersion != InternalVersion)
  171. {
  172. InvalidateCompressedStream(compressedStream);
  173. return false;
  174. }
  175. if (outerHeader.Endianness != GetEndianness())
  176. {
  177. InvalidateCompressedStream(compressedStream);
  178. return false;
  179. }
  180. if (outerHeader.FeatureInfo != GetFeatureInfo())
  181. {
  182. InvalidateCompressedStream(compressedStream);
  183. return false;
  184. }
  185. if (outerHeader.MemoryManagerMode != GetMemoryManagerMode())
  186. {
  187. InvalidateCompressedStream(compressedStream);
  188. return false;
  189. }
  190. if (outerHeader.OSPlatform != GetOSPlatform())
  191. {
  192. InvalidateCompressedStream(compressedStream);
  193. return false;
  194. }
  195. IntPtr intPtr = IntPtr.Zero;
  196. try
  197. {
  198. intPtr = Marshal.AllocHGlobal(new IntPtr(outerHeader.UncompressedStreamSize));
  199. using (UnmanagedMemoryStream stream = new((byte*)intPtr.ToPointer(), outerHeader.UncompressedStreamSize, outerHeader.UncompressedStreamSize, FileAccess.ReadWrite))
  200. {
  201. try
  202. {
  203. deflateStream.CopyTo(stream);
  204. }
  205. catch
  206. {
  207. InvalidateCompressedStream(compressedStream);
  208. return false;
  209. }
  210. Debug.Assert(stream.Position == stream.Length);
  211. stream.Seek(0L, SeekOrigin.Begin);
  212. InnerHeader innerHeader = DeserializeStructure<InnerHeader>(stream);
  213. if (!innerHeader.IsHeaderValid())
  214. {
  215. InvalidateCompressedStream(compressedStream);
  216. return false;
  217. }
  218. if (innerHeader.Magic != _innerHeaderMagic)
  219. {
  220. InvalidateCompressedStream(compressedStream);
  221. return false;
  222. }
  223. ReadOnlySpan<byte> infosBytes = new(stream.PositionPointer, innerHeader.InfosLength);
  224. stream.Seek(innerHeader.InfosLength, SeekOrigin.Current);
  225. Hash128 infosHash = XXHash128.ComputeHash(infosBytes);
  226. if (innerHeader.InfosHash != infosHash)
  227. {
  228. InvalidateCompressedStream(compressedStream);
  229. return false;
  230. }
  231. ReadOnlySpan<byte> codesBytes = (int)innerHeader.CodesLength > 0 ? new(stream.PositionPointer, (int)innerHeader.CodesLength) : ReadOnlySpan<byte>.Empty;
  232. stream.Seek(innerHeader.CodesLength, SeekOrigin.Current);
  233. Hash128 codesHash = XXHash128.ComputeHash(codesBytes);
  234. if (innerHeader.CodesHash != codesHash)
  235. {
  236. InvalidateCompressedStream(compressedStream);
  237. return false;
  238. }
  239. ReadOnlySpan<byte> relocsBytes = new(stream.PositionPointer, innerHeader.RelocsLength);
  240. stream.Seek(innerHeader.RelocsLength, SeekOrigin.Current);
  241. Hash128 relocsHash = XXHash128.ComputeHash(relocsBytes);
  242. if (innerHeader.RelocsHash != relocsHash)
  243. {
  244. InvalidateCompressedStream(compressedStream);
  245. return false;
  246. }
  247. ReadOnlySpan<byte> unwindInfosBytes = new(stream.PositionPointer, innerHeader.UnwindInfosLength);
  248. stream.Seek(innerHeader.UnwindInfosLength, SeekOrigin.Current);
  249. Hash128 unwindInfosHash = XXHash128.ComputeHash(unwindInfosBytes);
  250. if (innerHeader.UnwindInfosHash != unwindInfosHash)
  251. {
  252. InvalidateCompressedStream(compressedStream);
  253. return false;
  254. }
  255. Debug.Assert(stream.Position == stream.Length);
  256. stream.Seek((long)Unsafe.SizeOf<InnerHeader>(), SeekOrigin.Begin);
  257. _infosStream.Write(infosBytes);
  258. stream.Seek(innerHeader.InfosLength, SeekOrigin.Current);
  259. _codesList.ReadFrom(stream);
  260. _relocsStream.Write(relocsBytes);
  261. stream.Seek(innerHeader.RelocsLength, SeekOrigin.Current);
  262. _unwindInfosStream.Write(unwindInfosBytes);
  263. stream.Seek(innerHeader.UnwindInfosLength, SeekOrigin.Current);
  264. Debug.Assert(stream.Position == stream.Length);
  265. }
  266. }
  267. finally
  268. {
  269. if (intPtr != IntPtr.Zero)
  270. {
  271. Marshal.FreeHGlobal(intPtr);
  272. }
  273. }
  274. }
  275. long fileSize = new FileInfo(fileName).Length;
  276. Logger.Info?.Print(LogClass.Ptc, $"{(isBackup ? "Loaded Backup Translation Cache" : "Loaded Translation Cache")} (size: {fileSize} bytes, translated functions: {GetEntriesCount()}).");
  277. return true;
  278. }
  279. private static void InvalidateCompressedStream(FileStream compressedStream)
  280. {
  281. compressedStream.SetLength(0L);
  282. }
  283. private static void PreSave()
  284. {
  285. _waitEvent.Reset();
  286. try
  287. {
  288. string fileNameActual = string.Concat(CachePathActual, ".cache");
  289. string fileNameBackup = string.Concat(CachePathBackup, ".cache");
  290. FileInfo fileInfoActual = new FileInfo(fileNameActual);
  291. if (fileInfoActual.Exists && fileInfoActual.Length != 0L)
  292. {
  293. File.Copy(fileNameActual, fileNameBackup, true);
  294. }
  295. Save(fileNameActual);
  296. }
  297. finally
  298. {
  299. ResetCarriersIfNeeded();
  300. GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
  301. }
  302. _waitEvent.Set();
  303. }
  304. private static unsafe void Save(string fileName)
  305. {
  306. int translatedFuncsCount;
  307. InnerHeader innerHeader = new InnerHeader();
  308. innerHeader.Magic = _innerHeaderMagic;
  309. innerHeader.InfosLength = (int)_infosStream.Length;
  310. innerHeader.CodesLength = _codesList.Length();
  311. innerHeader.RelocsLength = (int)_relocsStream.Length;
  312. innerHeader.UnwindInfosLength = (int)_unwindInfosStream.Length;
  313. OuterHeader outerHeader = new OuterHeader();
  314. outerHeader.Magic = _outerHeaderMagic;
  315. outerHeader.CacheFileVersion = InternalVersion;
  316. outerHeader.Endianness = GetEndianness();
  317. outerHeader.FeatureInfo = GetFeatureInfo();
  318. outerHeader.MemoryManagerMode = GetMemoryManagerMode();
  319. outerHeader.OSPlatform = GetOSPlatform();
  320. outerHeader.UncompressedStreamSize =
  321. (long)Unsafe.SizeOf<InnerHeader>() +
  322. innerHeader.InfosLength +
  323. innerHeader.CodesLength +
  324. innerHeader.RelocsLength +
  325. innerHeader.UnwindInfosLength;
  326. outerHeader.SetHeaderHash();
  327. IntPtr intPtr = IntPtr.Zero;
  328. try
  329. {
  330. intPtr = Marshal.AllocHGlobal(new IntPtr(outerHeader.UncompressedStreamSize));
  331. using (UnmanagedMemoryStream stream = new((byte*)intPtr.ToPointer(), outerHeader.UncompressedStreamSize, outerHeader.UncompressedStreamSize, FileAccess.ReadWrite))
  332. {
  333. stream.Seek((long)Unsafe.SizeOf<InnerHeader>(), SeekOrigin.Begin);
  334. ReadOnlySpan<byte> infosBytes = new(stream.PositionPointer, innerHeader.InfosLength);
  335. _infosStream.WriteTo(stream);
  336. ReadOnlySpan<byte> codesBytes = (int)innerHeader.CodesLength > 0 ? new(stream.PositionPointer, (int)innerHeader.CodesLength) : ReadOnlySpan<byte>.Empty;
  337. _codesList.WriteTo(stream);
  338. ReadOnlySpan<byte> relocsBytes = new(stream.PositionPointer, innerHeader.RelocsLength);
  339. _relocsStream.WriteTo(stream);
  340. ReadOnlySpan<byte> unwindInfosBytes = new(stream.PositionPointer, innerHeader.UnwindInfosLength);
  341. _unwindInfosStream.WriteTo(stream);
  342. Debug.Assert(stream.Position == stream.Length);
  343. innerHeader.InfosHash = XXHash128.ComputeHash(infosBytes);
  344. innerHeader.CodesHash = XXHash128.ComputeHash(codesBytes);
  345. innerHeader.RelocsHash = XXHash128.ComputeHash(relocsBytes);
  346. innerHeader.UnwindInfosHash = XXHash128.ComputeHash(unwindInfosBytes);
  347. innerHeader.SetHeaderHash();
  348. stream.Seek(0L, SeekOrigin.Begin);
  349. SerializeStructure(stream, innerHeader);
  350. translatedFuncsCount = GetEntriesCount();
  351. ResetCarriersIfNeeded();
  352. using (FileStream compressedStream = new(fileName, FileMode.OpenOrCreate))
  353. using (DeflateStream deflateStream = new(compressedStream, SaveCompressionLevel, true))
  354. {
  355. try
  356. {
  357. SerializeStructure(compressedStream, outerHeader);
  358. stream.Seek(0L, SeekOrigin.Begin);
  359. stream.CopyTo(deflateStream);
  360. }
  361. catch
  362. {
  363. compressedStream.Position = 0L;
  364. }
  365. if (compressedStream.Position < compressedStream.Length)
  366. {
  367. compressedStream.SetLength(compressedStream.Position);
  368. }
  369. }
  370. }
  371. }
  372. finally
  373. {
  374. if (intPtr != IntPtr.Zero)
  375. {
  376. Marshal.FreeHGlobal(intPtr);
  377. }
  378. }
  379. long fileSize = new FileInfo(fileName).Length;
  380. if (fileSize != 0L)
  381. {
  382. Logger.Info?.Print(LogClass.Ptc, $"Saved Translation Cache (size: {fileSize} bytes, translated functions: {translatedFuncsCount}).");
  383. }
  384. }
  385. internal static void LoadTranslations(Translator translator)
  386. {
  387. if (AreCarriersEmpty())
  388. {
  389. return;
  390. }
  391. long infosStreamLength = _infosStream.Length;
  392. long relocsStreamLength = _relocsStream.Length;
  393. long unwindInfosStreamLength = _unwindInfosStream.Length;
  394. _infosStream.Seek(0L, SeekOrigin.Begin);
  395. _relocsStream.Seek(0L, SeekOrigin.Begin);
  396. _unwindInfosStream.Seek(0L, SeekOrigin.Begin);
  397. using (BinaryReader relocsReader = new(_relocsStream, EncodingCache.UTF8NoBOM, true))
  398. using (BinaryReader unwindInfosReader = new(_unwindInfosStream, EncodingCache.UTF8NoBOM, true))
  399. {
  400. for (int index = 0; index < GetEntriesCount(); index++)
  401. {
  402. InfoEntry infoEntry = DeserializeStructure<InfoEntry>(_infosStream);
  403. if (infoEntry.Stubbed)
  404. {
  405. SkipCode(index, infoEntry.CodeLength);
  406. SkipReloc(infoEntry.RelocEntriesCount);
  407. SkipUnwindInfo(unwindInfosReader);
  408. continue;
  409. }
  410. bool isEntryChanged = infoEntry.Hash != ComputeHash(translator.Memory, infoEntry.Address, infoEntry.GuestSize);
  411. if (isEntryChanged || (!infoEntry.HighCq && PtcProfiler.ProfiledFuncs.TryGetValue(infoEntry.Address, out var value) && value.HighCq))
  412. {
  413. infoEntry.Stubbed = true;
  414. infoEntry.CodeLength = 0;
  415. UpdateInfo(infoEntry);
  416. StubCode(index);
  417. StubReloc(infoEntry.RelocEntriesCount);
  418. StubUnwindInfo(unwindInfosReader);
  419. if (isEntryChanged)
  420. {
  421. Logger.Info?.Print(LogClass.Ptc, $"Invalidated translated function (address: 0x{infoEntry.Address:X16})");
  422. }
  423. continue;
  424. }
  425. byte[] code = ReadCode(index, infoEntry.CodeLength);
  426. Counter<uint> callCounter = null;
  427. if (infoEntry.RelocEntriesCount != 0)
  428. {
  429. RelocEntry[] relocEntries = GetRelocEntries(relocsReader, infoEntry.RelocEntriesCount);
  430. PatchCode(translator, code, relocEntries, out callCounter);
  431. }
  432. UnwindInfo unwindInfo = ReadUnwindInfo(unwindInfosReader);
  433. TranslatedFunction func = FastTranslate(code, callCounter, infoEntry.GuestSize, unwindInfo, infoEntry.HighCq);
  434. translator.RegisterFunction(infoEntry.Address, func);
  435. bool isAddressUnique = translator.Functions.TryAdd(infoEntry.Address, func);
  436. Debug.Assert(isAddressUnique, $"The address 0x{infoEntry.Address:X16} is not unique.");
  437. }
  438. }
  439. if (_infosStream.Length != infosStreamLength || _infosStream.Position != infosStreamLength ||
  440. _relocsStream.Length != relocsStreamLength || _relocsStream.Position != relocsStreamLength ||
  441. _unwindInfosStream.Length != unwindInfosStreamLength || _unwindInfosStream.Position != unwindInfosStreamLength)
  442. {
  443. throw new Exception("The length of a memory stream has changed, or its position has not reached or has exceeded its end.");
  444. }
  445. Logger.Info?.Print(LogClass.Ptc, $"{translator.Functions.Count} translated functions loaded");
  446. }
  447. private static int GetEntriesCount()
  448. {
  449. return _codesList.Count;
  450. }
  451. [Conditional("DEBUG")]
  452. private static void SkipCode(int index, int codeLength)
  453. {
  454. Debug.Assert(_codesList[index].Length == 0);
  455. Debug.Assert(codeLength == 0);
  456. }
  457. private static void SkipReloc(int relocEntriesCount)
  458. {
  459. _relocsStream.Seek(relocEntriesCount * RelocEntry.Stride, SeekOrigin.Current);
  460. }
  461. private static void SkipUnwindInfo(BinaryReader unwindInfosReader)
  462. {
  463. int pushEntriesLength = unwindInfosReader.ReadInt32();
  464. _unwindInfosStream.Seek(pushEntriesLength * UnwindPushEntry.Stride + UnwindInfo.Stride, SeekOrigin.Current);
  465. }
  466. private static byte[] ReadCode(int index, int codeLength)
  467. {
  468. Debug.Assert(_codesList[index].Length == codeLength);
  469. return _codesList[index];
  470. }
  471. private static RelocEntry[] GetRelocEntries(BinaryReader relocsReader, int relocEntriesCount)
  472. {
  473. RelocEntry[] relocEntries = new RelocEntry[relocEntriesCount];
  474. for (int i = 0; i < relocEntriesCount; i++)
  475. {
  476. int position = relocsReader.ReadInt32();
  477. SymbolType type = (SymbolType)relocsReader.ReadByte();
  478. ulong value = relocsReader.ReadUInt64();
  479. relocEntries[i] = new RelocEntry(position, new Symbol(type, value));
  480. }
  481. return relocEntries;
  482. }
  483. private static void PatchCode(Translator translator, Span<byte> code, RelocEntry[] relocEntries, out Counter<uint> callCounter)
  484. {
  485. callCounter = null;
  486. foreach (RelocEntry relocEntry in relocEntries)
  487. {
  488. IntPtr? imm = null;
  489. Symbol symbol = relocEntry.Symbol;
  490. if (symbol.Type == SymbolType.FunctionTable)
  491. {
  492. ulong guestAddress = symbol.Value;
  493. if (translator.FunctionTable.IsValid(guestAddress))
  494. {
  495. unsafe { imm = (IntPtr)Unsafe.AsPointer(ref translator.FunctionTable.GetValue(guestAddress)); }
  496. }
  497. }
  498. else if (symbol.Type == SymbolType.DelegateTable)
  499. {
  500. int index = (int)symbol.Value;
  501. if (Delegates.TryGetDelegateFuncPtrByIndex(index, out IntPtr funcPtr))
  502. {
  503. imm = funcPtr;
  504. }
  505. }
  506. else if (symbol == PageTableSymbol)
  507. {
  508. imm = translator.Memory.PageTablePointer;
  509. }
  510. else if (symbol == CountTableSymbol)
  511. {
  512. callCounter = new Counter<uint>(translator.CountTable);
  513. unsafe { imm = (IntPtr)Unsafe.AsPointer(ref callCounter.Value); }
  514. }
  515. else if (symbol == DispatchStubSymbol)
  516. {
  517. imm = translator.Stubs.DispatchStub;
  518. }
  519. if (imm == null)
  520. {
  521. throw new Exception($"Unexpected reloc entry {relocEntry}.");
  522. }
  523. BinaryPrimitives.WriteUInt64LittleEndian(code.Slice(relocEntry.Position, 8), (ulong)imm.Value);
  524. }
  525. }
  526. private static UnwindInfo ReadUnwindInfo(BinaryReader unwindInfosReader)
  527. {
  528. int pushEntriesLength = unwindInfosReader.ReadInt32();
  529. UnwindPushEntry[] pushEntries = new UnwindPushEntry[pushEntriesLength];
  530. for (int i = 0; i < pushEntriesLength; i++)
  531. {
  532. int pseudoOp = unwindInfosReader.ReadInt32();
  533. int prologOffset = unwindInfosReader.ReadInt32();
  534. int regIndex = unwindInfosReader.ReadInt32();
  535. int stackOffsetOrAllocSize = unwindInfosReader.ReadInt32();
  536. pushEntries[i] = new UnwindPushEntry((UnwindPseudoOp)pseudoOp, prologOffset, regIndex, stackOffsetOrAllocSize);
  537. }
  538. int prologueSize = unwindInfosReader.ReadInt32();
  539. return new UnwindInfo(pushEntries, prologueSize);
  540. }
  541. private static TranslatedFunction FastTranslate(
  542. byte[] code,
  543. Counter<uint> callCounter,
  544. ulong guestSize,
  545. UnwindInfo unwindInfo,
  546. bool highCq)
  547. {
  548. CompiledFunction cFunc = new CompiledFunction(code, unwindInfo);
  549. IntPtr codePtr = JitCache.Map(cFunc);
  550. GuestFunction gFunc = Marshal.GetDelegateForFunctionPointer<GuestFunction>(codePtr);
  551. TranslatedFunction tFunc = new TranslatedFunction(gFunc, callCounter, guestSize, highCq);
  552. return tFunc;
  553. }
  554. private static void UpdateInfo(InfoEntry infoEntry)
  555. {
  556. _infosStream.Seek(-Unsafe.SizeOf<InfoEntry>(), SeekOrigin.Current);
  557. SerializeStructure(_infosStream, infoEntry);
  558. }
  559. private static void StubCode(int index)
  560. {
  561. _codesList[index] = Array.Empty<byte>();
  562. }
  563. private static void StubReloc(int relocEntriesCount)
  564. {
  565. for (int i = 0; i < relocEntriesCount * RelocEntry.Stride; i++)
  566. {
  567. _relocsStream.WriteByte(FillingByte);
  568. }
  569. }
  570. private static void StubUnwindInfo(BinaryReader unwindInfosReader)
  571. {
  572. int pushEntriesLength = unwindInfosReader.ReadInt32();
  573. for (int i = 0; i < pushEntriesLength * UnwindPushEntry.Stride + UnwindInfo.Stride; i++)
  574. {
  575. _unwindInfosStream.WriteByte(FillingByte);
  576. }
  577. }
  578. internal static void MakeAndSaveTranslations(Translator translator)
  579. {
  580. var profiledFuncsToTranslate = PtcProfiler.GetProfiledFuncsToTranslate(translator.Functions);
  581. _translateCount = 0;
  582. _translateTotalCount = profiledFuncsToTranslate.Count;
  583. int degreeOfParallelism = new DegreeOfParallelism(4d, 75d, 12.5d).GetDegreeOfParallelism(0, 32);
  584. if (_translateTotalCount == 0 || degreeOfParallelism == 0)
  585. {
  586. ResetCarriersIfNeeded();
  587. GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
  588. return;
  589. }
  590. Logger.Info?.Print(LogClass.Ptc, $"{_translateCount} of {_translateTotalCount} functions translated | Thread count: {degreeOfParallelism}");
  591. PtcStateChanged?.Invoke(PtcLoadingState.Start, _translateCount, _translateTotalCount);
  592. using AutoResetEvent progressReportEvent = new AutoResetEvent(false);
  593. Thread progressReportThread = new Thread(ReportProgress)
  594. {
  595. Name = "Ptc.ProgressReporter",
  596. Priority = ThreadPriority.Lowest,
  597. IsBackground = true
  598. };
  599. progressReportThread.Start(progressReportEvent);
  600. void TranslateFuncs()
  601. {
  602. while (profiledFuncsToTranslate.TryDequeue(out var item))
  603. {
  604. ulong address = item.address;
  605. Debug.Assert(PtcProfiler.IsAddressInStaticCodeRange(address));
  606. TranslatedFunction func = translator.Translate(address, item.funcProfile.Mode, item.funcProfile.HighCq);
  607. bool isAddressUnique = translator.Functions.TryAdd(address, func);
  608. Debug.Assert(isAddressUnique, $"The address 0x{address:X16} is not unique.");
  609. Interlocked.Increment(ref _translateCount);
  610. translator.RegisterFunction(address, func);
  611. if (State != PtcState.Enabled)
  612. {
  613. break;
  614. }
  615. }
  616. Translator.DisposePools();
  617. }
  618. List<Thread> threads = new List<Thread>();
  619. for (int i = 0; i < degreeOfParallelism; i++)
  620. {
  621. Thread thread = new Thread(TranslateFuncs);
  622. thread.IsBackground = true;
  623. threads.Add(thread);
  624. }
  625. threads.ForEach((thread) => thread.Start());
  626. threads.ForEach((thread) => thread.Join());
  627. threads.Clear();
  628. progressReportEvent.Set();
  629. progressReportThread.Join();
  630. PtcStateChanged?.Invoke(PtcLoadingState.Loaded, _translateCount, _translateTotalCount);
  631. Logger.Info?.Print(LogClass.Ptc, $"{_translateCount} of {_translateTotalCount} functions translated | Thread count: {degreeOfParallelism}");
  632. Thread preSaveThread = new Thread(PreSave);
  633. preSaveThread.IsBackground = true;
  634. preSaveThread.Start();
  635. }
  636. private static void ReportProgress(object state)
  637. {
  638. const int refreshRate = 50; // ms.
  639. AutoResetEvent endEvent = (AutoResetEvent)state;
  640. int count = 0;
  641. do
  642. {
  643. int newCount = _translateCount;
  644. if (count != newCount)
  645. {
  646. PtcStateChanged?.Invoke(PtcLoadingState.Loading, newCount, _translateTotalCount);
  647. count = newCount;
  648. }
  649. }
  650. while (!endEvent.WaitOne(refreshRate));
  651. }
  652. internal static Hash128 ComputeHash(IMemoryManager memory, ulong address, ulong guestSize)
  653. {
  654. return XXHash128.ComputeHash(memory.GetSpan(address, checked((int)(guestSize))));
  655. }
  656. internal static void WriteInfoCodeRelocUnwindInfo(ulong address, ulong guestSize, Hash128 hash, bool highCq, PtcInfo ptcInfo)
  657. {
  658. lock (_lock)
  659. {
  660. InfoEntry infoEntry = new InfoEntry();
  661. infoEntry.Address = address;
  662. infoEntry.GuestSize = guestSize;
  663. infoEntry.Hash = hash;
  664. infoEntry.HighCq = highCq;
  665. infoEntry.Stubbed = false;
  666. infoEntry.CodeLength = ptcInfo.Code.Length;
  667. infoEntry.RelocEntriesCount = ptcInfo.RelocEntriesCount;
  668. SerializeStructure(_infosStream, infoEntry);
  669. WriteCode(ptcInfo.Code.AsSpan());
  670. // WriteReloc.
  671. ptcInfo.RelocStream.WriteTo(_relocsStream);
  672. // WriteUnwindInfo.
  673. ptcInfo.UnwindInfoStream.WriteTo(_unwindInfosStream);
  674. }
  675. }
  676. private static void WriteCode(ReadOnlySpan<byte> code)
  677. {
  678. _codesList.Add(code.ToArray());
  679. }
  680. internal static bool GetEndianness()
  681. {
  682. return BitConverter.IsLittleEndian;
  683. }
  684. private static ulong GetFeatureInfo()
  685. {
  686. return (ulong)HardwareCapabilities.FeatureInfoEdx << 32 | (uint)HardwareCapabilities.FeatureInfoEcx;
  687. }
  688. private static byte GetMemoryManagerMode()
  689. {
  690. return (byte)_memoryMode;
  691. }
  692. private static uint GetOSPlatform()
  693. {
  694. uint osPlatform = 0u;
  695. osPlatform |= (RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD) ? 1u : 0u) << 0;
  696. osPlatform |= (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? 1u : 0u) << 1;
  697. osPlatform |= (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 1u : 0u) << 2;
  698. osPlatform |= (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? 1u : 0u) << 3;
  699. return osPlatform;
  700. }
  701. [StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 50*/)]
  702. private struct OuterHeader
  703. {
  704. public ulong Magic;
  705. public uint CacheFileVersion;
  706. public bool Endianness;
  707. public ulong FeatureInfo;
  708. public byte MemoryManagerMode;
  709. public uint OSPlatform;
  710. public long UncompressedStreamSize;
  711. public Hash128 HeaderHash;
  712. public void SetHeaderHash()
  713. {
  714. Span<OuterHeader> spanHeader = MemoryMarshal.CreateSpan(ref this, 1);
  715. HeaderHash = XXHash128.ComputeHash(MemoryMarshal.AsBytes(spanHeader).Slice(0, Unsafe.SizeOf<OuterHeader>() - Unsafe.SizeOf<Hash128>()));
  716. }
  717. public bool IsHeaderValid()
  718. {
  719. Span<OuterHeader> spanHeader = MemoryMarshal.CreateSpan(ref this, 1);
  720. return XXHash128.ComputeHash(MemoryMarshal.AsBytes(spanHeader).Slice(0, Unsafe.SizeOf<OuterHeader>() - Unsafe.SizeOf<Hash128>())) == HeaderHash;
  721. }
  722. }
  723. [StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 128*/)]
  724. private struct InnerHeader
  725. {
  726. public ulong Magic;
  727. public int InfosLength;
  728. public long CodesLength;
  729. public int RelocsLength;
  730. public int UnwindInfosLength;
  731. public Hash128 InfosHash;
  732. public Hash128 CodesHash;
  733. public Hash128 RelocsHash;
  734. public Hash128 UnwindInfosHash;
  735. public Hash128 HeaderHash;
  736. public void SetHeaderHash()
  737. {
  738. Span<InnerHeader> spanHeader = MemoryMarshal.CreateSpan(ref this, 1);
  739. HeaderHash = XXHash128.ComputeHash(MemoryMarshal.AsBytes(spanHeader).Slice(0, Unsafe.SizeOf<InnerHeader>() - Unsafe.SizeOf<Hash128>()));
  740. }
  741. public bool IsHeaderValid()
  742. {
  743. Span<InnerHeader> spanHeader = MemoryMarshal.CreateSpan(ref this, 1);
  744. return XXHash128.ComputeHash(MemoryMarshal.AsBytes(spanHeader).Slice(0, Unsafe.SizeOf<InnerHeader>() - Unsafe.SizeOf<Hash128>())) == HeaderHash;
  745. }
  746. }
  747. [StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 42*/)]
  748. private struct InfoEntry
  749. {
  750. public ulong Address;
  751. public ulong GuestSize;
  752. public Hash128 Hash;
  753. public bool HighCq;
  754. public bool Stubbed;
  755. public int CodeLength;
  756. public int RelocEntriesCount;
  757. }
  758. private static void Enable()
  759. {
  760. State = PtcState.Enabled;
  761. }
  762. public static void Continue()
  763. {
  764. if (State == PtcState.Enabled)
  765. {
  766. State = PtcState.Continuing;
  767. }
  768. }
  769. public static void Close()
  770. {
  771. if (State == PtcState.Enabled ||
  772. State == PtcState.Continuing)
  773. {
  774. State = PtcState.Closing;
  775. }
  776. }
  777. internal static void Disable()
  778. {
  779. State = PtcState.Disabled;
  780. }
  781. private static void Wait()
  782. {
  783. _waitEvent.WaitOne();
  784. }
  785. public static void Dispose()
  786. {
  787. if (!_disposed)
  788. {
  789. _disposed = true;
  790. Wait();
  791. _waitEvent.Dispose();
  792. DisposeCarriers();
  793. }
  794. }
  795. }
  796. }