JitCache.cs 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. using ARMeilleure.CodeGen;
  2. using ARMeilleure.CodeGen.Unwinding;
  3. using ARMeilleure.Memory;
  4. using ARMeilleure.Native;
  5. using System;
  6. using System.Collections.Generic;
  7. using System.Diagnostics;
  8. using System.Runtime.InteropServices;
  9. namespace ARMeilleure.Translation.Cache
  10. {
  11. static class JitCache
  12. {
  13. private const int PageSize = 4 * 1024;
  14. private const int PageMask = PageSize - 1;
  15. private const int CodeAlignment = 4; // Bytes.
  16. private const int CacheSize = 2047 * 1024 * 1024;
  17. private static ReservedRegion _jitRegion;
  18. private static JitCacheInvalidation _jitCacheInvalidator;
  19. private static CacheMemoryAllocator _cacheAllocator;
  20. private static readonly List<CacheEntry> _cacheEntries = new List<CacheEntry>();
  21. private static readonly object _lock = new object();
  22. private static bool _initialized;
  23. public static void Initialize(IJitMemoryAllocator allocator)
  24. {
  25. if (_initialized) return;
  26. lock (_lock)
  27. {
  28. if (_initialized) return;
  29. _jitRegion = new ReservedRegion(allocator, CacheSize);
  30. _jitCacheInvalidator = new JitCacheInvalidation(allocator);
  31. _cacheAllocator = new CacheMemoryAllocator(CacheSize);
  32. if (OperatingSystem.IsWindows())
  33. {
  34. JitUnwindWindows.InstallFunctionTableHandler(_jitRegion.Pointer, CacheSize, _jitRegion.Pointer + Allocate(PageSize));
  35. }
  36. _initialized = true;
  37. }
  38. }
  39. public static IntPtr Map(CompiledFunction func)
  40. {
  41. byte[] code = func.Code;
  42. lock (_lock)
  43. {
  44. Debug.Assert(_initialized);
  45. int funcOffset = Allocate(code.Length);
  46. IntPtr funcPtr = _jitRegion.Pointer + funcOffset;
  47. if (OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
  48. {
  49. unsafe
  50. {
  51. fixed (byte *codePtr = code)
  52. {
  53. JitSupportDarwin.Copy(funcPtr, (IntPtr)codePtr, (ulong)code.Length);
  54. }
  55. }
  56. }
  57. else
  58. {
  59. ReprotectAsWritable(funcOffset, code.Length);
  60. Marshal.Copy(code, 0, funcPtr, code.Length);
  61. ReprotectAsExecutable(funcOffset, code.Length);
  62. _jitCacheInvalidator.Invalidate(funcPtr, (ulong)code.Length);
  63. }
  64. Add(funcOffset, code.Length, func.UnwindInfo);
  65. return funcPtr;
  66. }
  67. }
  68. public static void Unmap(IntPtr pointer)
  69. {
  70. lock (_lock)
  71. {
  72. Debug.Assert(_initialized);
  73. int funcOffset = (int)(pointer.ToInt64() - _jitRegion.Pointer.ToInt64());
  74. bool result = TryFind(funcOffset, out CacheEntry entry);
  75. Debug.Assert(result);
  76. _cacheAllocator.Free(funcOffset, AlignCodeSize(entry.Size));
  77. Remove(funcOffset);
  78. }
  79. }
  80. private static void ReprotectAsWritable(int offset, int size)
  81. {
  82. int endOffs = offset + size;
  83. int regionStart = offset & ~PageMask;
  84. int regionEnd = (endOffs + PageMask) & ~PageMask;
  85. _jitRegion.Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart));
  86. }
  87. private static void ReprotectAsExecutable(int offset, int size)
  88. {
  89. int endOffs = offset + size;
  90. int regionStart = offset & ~PageMask;
  91. int regionEnd = (endOffs + PageMask) & ~PageMask;
  92. _jitRegion.Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart));
  93. }
  94. private static int Allocate(int codeSize)
  95. {
  96. codeSize = AlignCodeSize(codeSize);
  97. int allocOffset = _cacheAllocator.Allocate(codeSize);
  98. if (allocOffset < 0)
  99. {
  100. throw new OutOfMemoryException("JIT Cache exhausted.");
  101. }
  102. _jitRegion.ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize);
  103. return allocOffset;
  104. }
  105. private static int AlignCodeSize(int codeSize)
  106. {
  107. return checked(codeSize + (CodeAlignment - 1)) & ~(CodeAlignment - 1);
  108. }
  109. private static void Add(int offset, int size, UnwindInfo unwindInfo)
  110. {
  111. CacheEntry entry = new CacheEntry(offset, size, unwindInfo);
  112. int index = _cacheEntries.BinarySearch(entry);
  113. if (index < 0)
  114. {
  115. index = ~index;
  116. }
  117. _cacheEntries.Insert(index, entry);
  118. }
  119. private static void Remove(int offset)
  120. {
  121. int index = _cacheEntries.BinarySearch(new CacheEntry(offset, 0, default));
  122. if (index < 0)
  123. {
  124. index = ~index - 1;
  125. }
  126. if (index >= 0)
  127. {
  128. _cacheEntries.RemoveAt(index);
  129. }
  130. }
  131. public static bool TryFind(int offset, out CacheEntry entry)
  132. {
  133. lock (_lock)
  134. {
  135. int index = _cacheEntries.BinarySearch(new CacheEntry(offset, 0, default));
  136. if (index < 0)
  137. {
  138. index = ~index - 1;
  139. }
  140. if (index >= 0)
  141. {
  142. entry = _cacheEntries[index];
  143. return true;
  144. }
  145. }
  146. entry = default;
  147. return false;
  148. }
  149. }
  150. }