using ARMeilleure.Instructions; using ARMeilleure.IntermediateRepresentation; using ARMeilleure.State; using ARMeilleure.Translation.Cache; using System; using System.Reflection; using System.Runtime.InteropServices; using static ARMeilleure.IntermediateRepresentation.Operand.Factory; namespace ARMeilleure.Translation { /// /// Represents a stub manager. /// class TranslatorStubs : IDisposable { private static readonly Lazy _slowDispatchStub = new(GenerateSlowDispatchStub, isThreadSafe: true); private bool _disposed; private readonly Translator _translator; private readonly Lazy _dispatchStub; private readonly Lazy _dispatchLoop; /// /// Gets the dispatch stub. /// /// instance was disposed public IntPtr DispatchStub { get { if (_disposed) { throw new ObjectDisposedException(null); } return _dispatchStub.Value; } } /// /// Gets the slow dispatch stub. /// /// instance was disposed public IntPtr SlowDispatchStub { get { if (_disposed) { throw new ObjectDisposedException(null); } return _slowDispatchStub.Value; } } /// /// Gets the dispatch loop function. /// /// instance was disposed public DispatcherFunction DispatchLoop { get { if (_disposed) { throw new ObjectDisposedException(null); } return _dispatchLoop.Value; } } /// /// Initializes a new instance of the class with the specified /// instance. /// /// instance to use /// is null public TranslatorStubs(Translator translator) { _translator = translator ?? throw new ArgumentNullException(nameof(translator)); _dispatchStub = new(GenerateDispatchStub, isThreadSafe: true); _dispatchLoop = new(GenerateDispatchLoop, isThreadSafe: true); } /// /// Releases all resources used by the instance. /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// /// Releases all unmanaged and optionally managed resources used by the instance. /// /// to dispose managed resources also; otherwise just unmanaged resouces protected virtual void Dispose(bool disposing) { if (!_disposed) { if (_dispatchStub.IsValueCreated) { JitCache.Unmap(_dispatchStub.Value); } if (_dispatchLoop.IsValueCreated) { JitCache.Unmap(Marshal.GetFunctionPointerForDelegate(_dispatchLoop.Value)); } _disposed = true; } } /// /// Frees resources used by the instance. /// ~TranslatorStubs() { Dispose(false); } /// /// Generates a . /// /// Generated private IntPtr GenerateDispatchStub() { var context = new EmitterContext(); Operand lblFallback = Label(); Operand lblEnd = Label(); // Load the target guest address from the native context. Operand nativeContext = context.LoadArgument(OperandType.I64, 0); Operand guestAddress = context.Load(OperandType.I64, context.Add(nativeContext, Const((ulong)NativeContext.GetDispatchAddressOffset()))); // Check if guest address is within range of the AddressTable. Operand masked = context.BitwiseAnd(guestAddress, Const(~_translator.FunctionTable.Mask)); context.BranchIfTrue(lblFallback, masked); Operand index = default; Operand page = Const((long)_translator.FunctionTable.Base); for (int i = 0; i < _translator.FunctionTable.Levels.Length; i++) { ref var level = ref _translator.FunctionTable.Levels[i]; // level.Mask is not used directly because it is more often bigger than 32-bits, so it will not // be encoded as an immediate on x86's bitwise and operation. Operand mask = Const(level.Mask >> level.Index); index = context.BitwiseAnd(context.ShiftRightUI(guestAddress, Const(level.Index)), mask); if (i < _translator.FunctionTable.Levels.Length - 1) { page = context.Load(OperandType.I64, context.Add(page, context.ShiftLeft(index, Const(3)))); context.BranchIfFalse(lblFallback, page); } } Operand hostAddress; Operand hostAddressAddr = context.Add(page, context.ShiftLeft(index, Const(3))); hostAddress = context.Load(OperandType.I64, hostAddressAddr); context.Tailcall(hostAddress, nativeContext); context.MarkLabel(lblFallback); hostAddress = context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress)), guestAddress); context.Tailcall(hostAddress, nativeContext); var cfg = context.GetControlFlowGraph(); var retType = OperandType.I64; var argTypes = new[] { OperandType.I64 }; var func = Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq).Map(); return Marshal.GetFunctionPointerForDelegate(func); } /// /// Generates a . /// /// Generated private static IntPtr GenerateSlowDispatchStub() { var context = new EmitterContext(); // Load the target guest address from the native context. Operand nativeContext = context.LoadArgument(OperandType.I64, 0); Operand guestAddress = context.Load(OperandType.I64, context.Add(nativeContext, Const((ulong)NativeContext.GetDispatchAddressOffset()))); MethodInfo getFuncAddress = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress)); Operand hostAddress = context.Call(getFuncAddress, guestAddress); context.Tailcall(hostAddress, nativeContext); var cfg = context.GetControlFlowGraph(); var retType = OperandType.I64; var argTypes = new[] { OperandType.I64 }; var func = Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq).Map(); return Marshal.GetFunctionPointerForDelegate(func); } /// /// Generates a function. /// /// function private DispatcherFunction GenerateDispatchLoop() { var context = new EmitterContext(); Operand beginLbl = Label(); Operand endLbl = Label(); Operand nativeContext = context.LoadArgument(OperandType.I64, 0); Operand guestAddress = context.Copy( context.AllocateLocal(OperandType.I64), context.LoadArgument(OperandType.I64, 1)); Operand runningAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetRunningOffset())); Operand dispatchAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetDispatchAddressOffset())); context.MarkLabel(beginLbl); context.Store(dispatchAddress, guestAddress); context.Copy(guestAddress, context.Call(Const((ulong)DispatchStub), OperandType.I64, nativeContext)); context.BranchIfFalse(endLbl, guestAddress); context.BranchIfFalse(endLbl, context.Load(OperandType.I32, runningAddress)); context.Branch(beginLbl); context.MarkLabel(endLbl); context.Return(); var cfg = context.GetControlFlowGraph(); var retType = OperandType.None; var argTypes = new[] { OperandType.I64, OperandType.I64 }; return Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq).Map(); } } }