TranslatorStubs.cs 9.3 KB

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