JitCache.cs 5.0 KB

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