TranslatorStubs.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  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. private readonly Lazy<WrapperFunction> _contextWrapper;
  22. /// <summary>
  23. /// Gets the dispatch stub.
  24. /// </summary>
  25. /// <exception cref="ObjectDisposedException"><see cref="TranslatorStubs"/> instance was disposed</exception>
  26. public IntPtr DispatchStub
  27. {
  28. get
  29. {
  30. ObjectDisposedException.ThrowIf(_disposed, this);
  31. return _dispatchStub.Value;
  32. }
  33. }
  34. /// <summary>
  35. /// Gets the slow dispatch stub.
  36. /// </summary>
  37. /// <exception cref="ObjectDisposedException"><see cref="TranslatorStubs"/> instance was disposed</exception>
  38. public IntPtr SlowDispatchStub
  39. {
  40. get
  41. {
  42. ObjectDisposedException.ThrowIf(_disposed, this);
  43. return _slowDispatchStub.Value;
  44. }
  45. }
  46. /// <summary>
  47. /// Gets the dispatch loop function.
  48. /// </summary>
  49. /// <exception cref="ObjectDisposedException"><see cref="TranslatorStubs"/> instance was disposed</exception>
  50. public DispatcherFunction DispatchLoop
  51. {
  52. get
  53. {
  54. ObjectDisposedException.ThrowIf(_disposed, this);
  55. return _dispatchLoop.Value;
  56. }
  57. }
  58. /// <summary>
  59. /// Gets the context wrapper function.
  60. /// </summary>
  61. /// <exception cref="ObjectDisposedException"><see cref="TranslatorStubs"/> instance was disposed</exception>
  62. public WrapperFunction ContextWrapper
  63. {
  64. get
  65. {
  66. ObjectDisposedException.ThrowIf(_disposed, this);
  67. return _contextWrapper.Value;
  68. }
  69. }
  70. /// <summary>
  71. /// Initializes a new instance of the <see cref="TranslatorStubs"/> class with the specified
  72. /// <see cref="Translator"/> instance.
  73. /// </summary>
  74. /// <param name="translator"><see cref="Translator"/> instance to use</param>
  75. /// <exception cref="ArgumentNullException"><paramref name="translator"/> is null</exception>
  76. public TranslatorStubs(Translator translator)
  77. {
  78. ArgumentNullException.ThrowIfNull(translator);
  79. _translator = translator;
  80. _dispatchStub = new(GenerateDispatchStub, isThreadSafe: true);
  81. _dispatchLoop = new(GenerateDispatchLoop, isThreadSafe: true);
  82. _contextWrapper = new(GenerateContextWrapper, isThreadSafe: true);
  83. }
  84. /// <summary>
  85. /// Releases all resources used by the <see cref="TranslatorStubs"/> instance.
  86. /// </summary>
  87. public void Dispose()
  88. {
  89. Dispose(true);
  90. GC.SuppressFinalize(this);
  91. }
  92. /// <summary>
  93. /// Releases all unmanaged and optionally managed resources used by the <see cref="TranslatorStubs"/> instance.
  94. /// </summary>
  95. /// <param name="disposing"><see langword="true"/> to dispose managed resources also; otherwise just unmanaged resouces</param>
  96. protected virtual void Dispose(bool disposing)
  97. {
  98. if (!_disposed)
  99. {
  100. if (_dispatchStub.IsValueCreated)
  101. {
  102. JitCache.Unmap(_dispatchStub.Value);
  103. }
  104. if (_dispatchLoop.IsValueCreated)
  105. {
  106. JitCache.Unmap(Marshal.GetFunctionPointerForDelegate(_dispatchLoop.Value));
  107. }
  108. _disposed = true;
  109. }
  110. }
  111. /// <summary>
  112. /// Frees resources used by the <see cref="TranslatorStubs"/> instance.
  113. /// </summary>
  114. ~TranslatorStubs()
  115. {
  116. Dispose(false);
  117. }
  118. /// <summary>
  119. /// Generates a <see cref="DispatchStub"/>.
  120. /// </summary>
  121. /// <returns>Generated <see cref="DispatchStub"/></returns>
  122. private IntPtr GenerateDispatchStub()
  123. {
  124. var context = new EmitterContext();
  125. Operand lblFallback = Label();
  126. Operand lblEnd = Label();
  127. // Load the target guest address from the native context.
  128. Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
  129. Operand guestAddress = context.Load(OperandType.I64,
  130. context.Add(nativeContext, Const((ulong)NativeContext.GetDispatchAddressOffset())));
  131. // Check if guest address is within range of the AddressTable.
  132. Operand masked = context.BitwiseAnd(guestAddress, Const(~_translator.FunctionTable.Mask));
  133. context.BranchIfTrue(lblFallback, masked);
  134. Operand index = default;
  135. Operand page = Const((long)_translator.FunctionTable.Base);
  136. for (int i = 0; i < _translator.FunctionTable.Levels.Length; i++)
  137. {
  138. ref var level = ref _translator.FunctionTable.Levels[i];
  139. // level.Mask is not used directly because it is more often bigger than 32-bits, so it will not
  140. // be encoded as an immediate on x86's bitwise and operation.
  141. Operand mask = Const(level.Mask >> level.Index);
  142. index = context.BitwiseAnd(context.ShiftRightUI(guestAddress, Const(level.Index)), mask);
  143. if (i < _translator.FunctionTable.Levels.Length - 1)
  144. {
  145. page = context.Load(OperandType.I64, context.Add(page, context.ShiftLeft(index, Const(3))));
  146. context.BranchIfFalse(lblFallback, page);
  147. }
  148. }
  149. Operand hostAddress;
  150. Operand hostAddressAddr = context.Add(page, context.ShiftLeft(index, Const(3)));
  151. hostAddress = context.Load(OperandType.I64, hostAddressAddr);
  152. context.Tailcall(hostAddress, nativeContext);
  153. context.MarkLabel(lblFallback);
  154. hostAddress = context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress)), guestAddress);
  155. context.Tailcall(hostAddress, nativeContext);
  156. var cfg = context.GetControlFlowGraph();
  157. var retType = OperandType.I64;
  158. var argTypes = new[] { OperandType.I64 };
  159. var func = Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<GuestFunction>();
  160. return Marshal.GetFunctionPointerForDelegate(func);
  161. }
  162. /// <summary>
  163. /// Generates a <see cref="SlowDispatchStub"/>.
  164. /// </summary>
  165. /// <returns>Generated <see cref="SlowDispatchStub"/></returns>
  166. private static IntPtr GenerateSlowDispatchStub()
  167. {
  168. var context = new EmitterContext();
  169. // Load the target guest address from the native context.
  170. Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
  171. Operand guestAddress = context.Load(OperandType.I64,
  172. context.Add(nativeContext, Const((ulong)NativeContext.GetDispatchAddressOffset())));
  173. MethodInfo getFuncAddress = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress));
  174. Operand hostAddress = context.Call(getFuncAddress, guestAddress);
  175. context.Tailcall(hostAddress, nativeContext);
  176. var cfg = context.GetControlFlowGraph();
  177. var retType = OperandType.I64;
  178. var argTypes = new[] { OperandType.I64 };
  179. var func = Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<GuestFunction>();
  180. return Marshal.GetFunctionPointerForDelegate(func);
  181. }
  182. /// <summary>
  183. /// Emits code that syncs FP state before executing guest code, or returns it to normal.
  184. /// </summary>
  185. /// <param name="context">Emitter context for the method</param>
  186. /// <param name="nativeContext">Pointer to the native context</param>
  187. /// <param name="enter">True if entering guest code, false otherwise</param>
  188. private void EmitSyncFpContext(EmitterContext context, Operand nativeContext, bool enter)
  189. {
  190. if (enter)
  191. {
  192. InstEmitSimdHelper.EnterArmFpMode(context, (flag) =>
  193. {
  194. Operand flagAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetRegisterOffset(new Register((int)flag, RegisterType.FpFlag))));
  195. return context.Load(OperandType.I32, flagAddress);
  196. });
  197. }
  198. else
  199. {
  200. InstEmitSimdHelper.ExitArmFpMode(context, (flag, value) =>
  201. {
  202. Operand flagAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetRegisterOffset(new Register((int)flag, RegisterType.FpFlag))));
  203. context.Store(flagAddress, value);
  204. });
  205. }
  206. }
  207. /// <summary>
  208. /// Generates a <see cref="DispatchLoop"/> function.
  209. /// </summary>
  210. /// <returns><see cref="DispatchLoop"/> function</returns>
  211. private DispatcherFunction GenerateDispatchLoop()
  212. {
  213. var context = new EmitterContext();
  214. Operand beginLbl = Label();
  215. Operand endLbl = Label();
  216. Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
  217. Operand guestAddress = context.Copy(
  218. context.AllocateLocal(OperandType.I64),
  219. context.LoadArgument(OperandType.I64, 1));
  220. Operand runningAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetRunningOffset()));
  221. Operand dispatchAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetDispatchAddressOffset()));
  222. EmitSyncFpContext(context, nativeContext, true);
  223. context.MarkLabel(beginLbl);
  224. context.Store(dispatchAddress, guestAddress);
  225. context.Copy(guestAddress, context.Call(Const((ulong)DispatchStub), OperandType.I64, nativeContext));
  226. context.BranchIfFalse(endLbl, guestAddress);
  227. context.BranchIfFalse(endLbl, context.Load(OperandType.I32, runningAddress));
  228. context.Branch(beginLbl);
  229. context.MarkLabel(endLbl);
  230. EmitSyncFpContext(context, nativeContext, false);
  231. context.Return();
  232. var cfg = context.GetControlFlowGraph();
  233. var retType = OperandType.None;
  234. var argTypes = new[] { OperandType.I64, OperandType.I64 };
  235. return Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<DispatcherFunction>();
  236. }
  237. /// <summary>
  238. /// Generates a <see cref="ContextWrapper"/> function.
  239. /// </summary>
  240. /// <returns><see cref="ContextWrapper"/> function</returns>
  241. private WrapperFunction GenerateContextWrapper()
  242. {
  243. var context = new EmitterContext();
  244. Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
  245. Operand guestMethod = context.LoadArgument(OperandType.I64, 1);
  246. EmitSyncFpContext(context, nativeContext, true);
  247. Operand returnValue = context.Call(guestMethod, OperandType.I64, nativeContext);
  248. EmitSyncFpContext(context, nativeContext, false);
  249. context.Return(returnValue);
  250. var cfg = context.GetControlFlowGraph();
  251. var retType = OperandType.I64;
  252. var argTypes = new[] { OperandType.I64, OperandType.I64 };
  253. return Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<WrapperFunction>();
  254. }
  255. }
  256. }