TranslatorStubs.cs 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. using ARMeilleure.Instructions;
  2. using ARMeilleure.IntermediateRepresentation;
  3. using ARMeilleure.State;
  4. using ARMeilleure.Translation.Cache;
  5. using System;
  6. using System.Reflection;
  7. using System.Runtime.InteropServices;
  8. using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
  9. namespace ARMeilleure.Translation
  10. {
  11. /// <summary>
  12. /// Represents a stub manager.
  13. /// </summary>
  14. class TranslatorStubs : IDisposable
  15. {
  16. private static readonly Lazy<IntPtr> _slowDispatchStub = new(GenerateSlowDispatchStub, isThreadSafe: true);
  17. private bool _disposed;
  18. private readonly Translator _translator;
  19. private readonly Lazy<IntPtr> _dispatchStub;
  20. private readonly Lazy<DispatcherFunction> _dispatchLoop;
  21. /// <summary>
  22. /// Gets the dispatch stub.
  23. /// </summary>
  24. /// <exception cref="ObjectDisposedException"><see cref="TranslatorStubs"/> instance was disposed</exception>
  25. public IntPtr DispatchStub
  26. {
  27. get
  28. {
  29. if (_disposed)
  30. {
  31. throw new ObjectDisposedException(null);
  32. }
  33. return _dispatchStub.Value;
  34. }
  35. }
  36. /// <summary>
  37. /// Gets the slow dispatch stub.
  38. /// </summary>
  39. /// <exception cref="ObjectDisposedException"><see cref="TranslatorStubs"/> instance was disposed</exception>
  40. public IntPtr SlowDispatchStub
  41. {
  42. get
  43. {
  44. if (_disposed)
  45. {
  46. throw new ObjectDisposedException(null);
  47. }
  48. return _slowDispatchStub.Value;
  49. }
  50. }
  51. /// <summary>
  52. /// Gets the dispatch loop function.
  53. /// </summary>
  54. /// <exception cref="ObjectDisposedException"><see cref="TranslatorStubs"/> instance was disposed</exception>
  55. public DispatcherFunction DispatchLoop
  56. {
  57. get
  58. {
  59. if (_disposed)
  60. {
  61. throw new ObjectDisposedException(null);
  62. }
  63. return _dispatchLoop.Value;
  64. }
  65. }
  66. /// <summary>
  67. /// Initializes a new instance of the <see cref="TranslatorStubs"/> class with the specified
  68. /// <see cref="Translator"/> instance.
  69. /// </summary>
  70. /// <param name="translator"><see cref="Translator"/> instance to use</param>
  71. /// <exception cref="ArgumentNullException"><paramref name="translator"/> is null</exception>
  72. public TranslatorStubs(Translator translator)
  73. {
  74. _translator = translator ?? throw new ArgumentNullException(nameof(translator));
  75. _dispatchStub = new(GenerateDispatchStub, isThreadSafe: true);
  76. _dispatchLoop = new(GenerateDispatchLoop, isThreadSafe: true);
  77. }
  78. /// <summary>
  79. /// Releases all resources used by the <see cref="TranslatorStubs"/> instance.
  80. /// </summary>
  81. public void Dispose()
  82. {
  83. Dispose(true);
  84. GC.SuppressFinalize(this);
  85. }
  86. /// <summary>
  87. /// Releases all unmanaged and optionally managed resources used by the <see cref="TranslatorStubs"/> instance.
  88. /// </summary>
  89. /// <param name="disposing"><see langword="true"/> to dispose managed resources also; otherwise just unmanaged resouces</param>
  90. protected virtual void Dispose(bool disposing)
  91. {
  92. if (!_disposed)
  93. {
  94. if (_dispatchStub.IsValueCreated)
  95. {
  96. JitCache.Unmap(_dispatchStub.Value);
  97. }
  98. if (_dispatchLoop.IsValueCreated)
  99. {
  100. JitCache.Unmap(Marshal.GetFunctionPointerForDelegate(_dispatchLoop.Value));
  101. }
  102. _disposed = true;
  103. }
  104. }
  105. /// <summary>
  106. /// Frees resources used by the <see cref="TranslatorStubs"/> instance.
  107. /// </summary>
  108. ~TranslatorStubs()
  109. {
  110. Dispose(false);
  111. }
  112. /// <summary>
  113. /// Generates a <see cref="DispatchStub"/>.
  114. /// </summary>
  115. /// <returns>Generated <see cref="DispatchStub"/></returns>
  116. private IntPtr GenerateDispatchStub()
  117. {
  118. var context = new EmitterContext();
  119. Operand lblFallback = Label();
  120. Operand lblEnd = Label();
  121. // Load the target guest address from the native context.
  122. Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
  123. Operand guestAddress = context.Load(OperandType.I64,
  124. context.Add(nativeContext, Const((ulong)NativeContext.GetDispatchAddressOffset())));
  125. // Check if guest address is within range of the AddressTable.
  126. Operand masked = context.BitwiseAnd(guestAddress, Const(~_translator.FunctionTable.Mask));
  127. context.BranchIfTrue(lblFallback, masked);
  128. Operand index = default;
  129. Operand page = Const((long)_translator.FunctionTable.Base);
  130. for (int i = 0; i < _translator.FunctionTable.Levels.Length; i++)
  131. {
  132. ref var level = ref _translator.FunctionTable.Levels[i];
  133. // level.Mask is not used directly because it is more often bigger than 32-bits, so it will not
  134. // be encoded as an immediate on x86's bitwise and operation.
  135. Operand mask = Const(level.Mask >> level.Index);
  136. index = context.BitwiseAnd(context.ShiftRightUI(guestAddress, Const(level.Index)), mask);
  137. if (i < _translator.FunctionTable.Levels.Length - 1)
  138. {
  139. page = context.Load(OperandType.I64, context.Add(page, context.ShiftLeft(index, Const(3))));
  140. context.BranchIfFalse(lblFallback, page);
  141. }
  142. }
  143. Operand hostAddress;
  144. Operand hostAddressAddr = context.Add(page, context.ShiftLeft(index, Const(3)));
  145. hostAddress = context.Load(OperandType.I64, hostAddressAddr);
  146. context.Tailcall(hostAddress, nativeContext);
  147. context.MarkLabel(lblFallback);
  148. hostAddress = context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress)), guestAddress);
  149. context.Tailcall(hostAddress, nativeContext);
  150. var cfg = context.GetControlFlowGraph();
  151. var retType = OperandType.I64;
  152. var argTypes = new[] { OperandType.I64 };
  153. var func = Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq).Map<GuestFunction>();
  154. return Marshal.GetFunctionPointerForDelegate(func);
  155. }
  156. /// <summary>
  157. /// Generates a <see cref="SlowDispatchStub"/>.
  158. /// </summary>
  159. /// <returns>Generated <see cref="SlowDispatchStub"/></returns>
  160. private static IntPtr GenerateSlowDispatchStub()
  161. {
  162. var context = new EmitterContext();
  163. // Load the target guest address from the native context.
  164. Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
  165. Operand guestAddress = context.Load(OperandType.I64,
  166. context.Add(nativeContext, Const((ulong)NativeContext.GetDispatchAddressOffset())));
  167. MethodInfo getFuncAddress = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress));
  168. Operand hostAddress = context.Call(getFuncAddress, guestAddress);
  169. context.Tailcall(hostAddress, nativeContext);
  170. var cfg = context.GetControlFlowGraph();
  171. var retType = OperandType.I64;
  172. var argTypes = new[] { OperandType.I64 };
  173. var func = Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq).Map<GuestFunction>();
  174. return Marshal.GetFunctionPointerForDelegate(func);
  175. }
  176. /// <summary>
  177. /// Generates a <see cref="DispatchLoop"/> function.
  178. /// </summary>
  179. /// <returns><see cref="DispatchLoop"/> function</returns>
  180. private DispatcherFunction GenerateDispatchLoop()
  181. {
  182. var context = new EmitterContext();
  183. Operand beginLbl = Label();
  184. Operand endLbl = Label();
  185. Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
  186. Operand guestAddress = context.Copy(
  187. context.AllocateLocal(OperandType.I64),
  188. context.LoadArgument(OperandType.I64, 1));
  189. Operand runningAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetRunningOffset()));
  190. Operand dispatchAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetDispatchAddressOffset()));
  191. context.MarkLabel(beginLbl);
  192. context.Store(dispatchAddress, guestAddress);
  193. context.Copy(guestAddress, context.Call(Const((ulong)DispatchStub), OperandType.I64, nativeContext));
  194. context.BranchIfFalse(endLbl, guestAddress);
  195. context.BranchIfFalse(endLbl, context.Load(OperandType.I32, runningAddress));
  196. context.Branch(beginLbl);
  197. context.MarkLabel(endLbl);
  198. context.Return();
  199. var cfg = context.GetControlFlowGraph();
  200. var retType = OperandType.None;
  201. var argTypes = new[] { OperandType.I64, OperandType.I64 };
  202. return Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq).Map<DispatcherFunction>();
  203. }
  204. }
  205. }