Ptc.cs 39 KB

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