using ChocolArm64.Memory; using ChocolArm64.State; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Reflection; using System.Reflection.Emit; namespace ChocolArm64 { class ATranslatedSub { private delegate long AA64Subroutine(AThreadState Register, AMemory Memory); private AA64Subroutine ExecDelegate; public static int StateArgIdx { get; private set; } public static int MemoryArgIdx { get; private set; } public static Type[] FixedArgTypes { get; private set; } public DynamicMethod Method { get; private set; } public ReadOnlyCollection Params { get; private set; } private HashSet Callers; private ATranslatedSubType Type; private int CallCount; private bool NeedsReJit; private int MinCallCountForReJit = 250; public ATranslatedSub(DynamicMethod Method, List Params) { if (Method == null) { throw new ArgumentNullException(nameof(Method)); } if (Params == null) { throw new ArgumentNullException(nameof(Params)); } this.Method = Method; this.Params = Params.AsReadOnly(); Callers = new HashSet(); PrepareDelegate(); } static ATranslatedSub() { MethodInfo MthdInfo = typeof(AA64Subroutine).GetMethod("Invoke"); ParameterInfo[] Params = MthdInfo.GetParameters(); FixedArgTypes = new Type[Params.Length]; for (int Index = 0; Index < Params.Length; Index++) { Type ParamType = Params[Index].ParameterType; FixedArgTypes[Index] = ParamType; if (ParamType == typeof(AThreadState)) { StateArgIdx = Index; } else if (ParamType == typeof(AMemory)) { MemoryArgIdx = Index; } } } private void PrepareDelegate() { string Name = $"{Method.Name}_Dispatch"; DynamicMethod Mthd = new DynamicMethod(Name, typeof(long), FixedArgTypes); ILGenerator Generator = Mthd.GetILGenerator(); Generator.EmitLdargSeq(FixedArgTypes.Length); foreach (ARegister Reg in Params) { Generator.EmitLdarg(StateArgIdx); Generator.Emit(OpCodes.Ldfld, Reg.GetField()); } Generator.Emit(OpCodes.Call, Method); Generator.Emit(OpCodes.Ret); ExecDelegate = (AA64Subroutine)Mthd.CreateDelegate(typeof(AA64Subroutine)); } public bool ShouldReJit() { if (NeedsReJit && CallCount < MinCallCountForReJit) { CallCount++; return false; } return NeedsReJit; } public long Execute(AThreadState ThreadState, AMemory Memory) { return ExecDelegate(ThreadState, Memory); } public void AddCaller(long Position) { lock (Callers) { Callers.Add(Position); } } public long[] GetCallerPositions() { lock (Callers) { return Callers.ToArray(); } } public void SetType(ATranslatedSubType Type) { this.Type = Type; if (Type == ATranslatedSubType.SubTier0) { NeedsReJit = true; } } public void MarkForReJit() => NeedsReJit = true; } }