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