private static TranslatedFunction FastTranslate(byte[] code, UnwindInfo unwindInfo, bool highCq) { CompiledFunction cFunc = new CompiledFunction(code, unwindInfo); IntPtr codePtr = JitCache.Map(cFunc); GuestFunction gFunc = Marshal.GetDelegateForFunctionPointer <GuestFunction>(codePtr); TranslatedFunction tFunc = new TranslatedFunction(gFunc, highCq); return(tFunc); }
public static ulong GetIndirectFunctionAddress(ulong address, ulong entryAddress) { TranslatedFunction function = _context.Translator.GetOrTranslate(address, GetContext().ExecutionMode); ulong ptr = (ulong)function.GetPointer().ToInt64(); if (function.HighCq) { // Rewrite the host function address in the table to point to the highCq function. Marshal.WriteInt64((IntPtr)entryAddress, 8, (long)ptr); } return(ptr); }
private static TranslatedFunction FastTranslate(ReadOnlySpan <byte> code, ulong guestSize, UnwindInfo unwindInfo, bool highCq) { CompiledFunction cFunc = new CompiledFunction(code.ToArray(), unwindInfo); IntPtr codePtr = JitCache.Map(cFunc); GuestFunction gFunc = Marshal.GetDelegateForFunctionPointer <GuestFunction>(codePtr); TranslatedFunction tFunc = new TranslatedFunction(gFunc, guestSize, highCq); return(tFunc); }
internal static void MakeAndSaveTranslations(ConcurrentDictionary <ulong, TranslatedFunction> funcs, IMemoryManager memory, JumpTable jumpTable) { var profiledFuncsToTranslate = PtcProfiler.GetProfiledFuncsToTranslate(funcs); if (profiledFuncsToTranslate.Count == 0) { return; } _translateCount = 0; ThreadPool.QueueUserWorkItem(TranslationLogger, profiledFuncsToTranslate.Count); int maxDegreeOfParallelism = (Environment.ProcessorCount * 3) / 4; Parallel.ForEach(profiledFuncsToTranslate, new ParallelOptions { MaxDegreeOfParallelism = maxDegreeOfParallelism }, (item, state) => { ulong address = item.Key; Debug.Assert(PtcProfiler.IsAddressInStaticCodeRange(address)); TranslatedFunction func = Translator.Translate(memory, jumpTable, address, item.Value.mode, item.Value.highCq); bool isAddressUnique = funcs.TryAdd(address, func); Debug.Assert(isAddressUnique, $"The address 0x{address:X16} is not unique."); if (func.HighCq) { jumpTable.RegisterFunction(address, func); } Interlocked.Increment(ref _translateCount); if (State != PtcState.Enabled) { state.Stop(); } }); _loggerEvent.Set(); PtcJumpTable.Initialize(jumpTable); PtcJumpTable.ReadJumpTable(jumpTable); PtcJumpTable.ReadDynamicTable(jumpTable); ThreadPool.QueueUserWorkItem(PreSave); }
public JITROMDelegate JITROM(Span <byte> ROM, SortedSet <UInt16> BreakPoints = null) { if (BreakPoints == null) { BreakPoints = new SortedSet <ushort>(); } var exe = disasm.DisassembleProgram(ROM); JITContext ctx = new JITContext { BreakPoints = BreakPoints, Disasm = exe }; var(res, gen) = CreateMethod("JITROM", ctx); //Find all function calls Dictionary <UInt16, TranslatedFunction> Functions = exe.Entries.Where(x => !x.IsData && x.Instr.Value.Instruction == Instruction.CALL) .Select(x => x.Instr.Value.Immediate16) .Distinct() .ToDictionary(x => x, x => (TranslatedFunction)null); //Function calls will get translated to their own method to use the runtime call stack foreach (var f in Functions.Keys.ToArray()) { var(tmpMethod, TmpGen) = CreateMethod("method_" + f.ToString("X4"), ctx); Functions[f] = new TranslatedFunction { Method = tmpMethod, Generator = TmpGen, Info = tmpMethod }; } ctx.Functions = Functions; //As detecting where a funnction ends is not trivial we're just going to JIT all functions to the end of the rom, disgusting i know foreach (var f in Functions.Keys.Where(x => x >= Chip8State.ProgramStart)) { JITBlock(Functions[f].Generator, ctx, (f - Chip8State.ProgramStart) / 2, exe.Entries.Length); } //Then JIT the whole ROM JITBlock(gen, ctx, 0, exe.Entries.Length); return((JITROMDelegate)res.CreateDelegate(typeof(JITROMDelegate))); }
public static ulong GetIndirectFunctionAddress(ulong address, ulong entryAddress) { TranslatedFunction function = Context.Translator.GetOrTranslate(address, GetContext().ExecutionMode, hintRejit: true); ulong ptr = (ulong)function.FuncPtr.ToInt64(); if (function.HighCq) { Debug.Assert(Context.Translator.JumpTable.CheckEntryFromAddressDynamicTable((IntPtr)entryAddress)); // Rewrite the host function address in the table to point to the highCq function. Marshal.WriteInt64((IntPtr)entryAddress, 8, (long)ptr); } return(ptr); }
public void RegisterFunction(ulong address, TranslatedFunction func) { Targets.AddOrUpdate(address, func, (key, oldFunc) => func); long funcPtr = func.FuncPtr.ToInt64(); // Update all jump table entries that target this address. if (Dependants.TryGetValue(address, out List <int> myDependants)) { lock (myDependants) { foreach (int entry in myDependants) { IntPtr addr = GetEntryAddressJumpTable(entry); Marshal.WriteInt64(addr, 8, funcPtr); } } } }
private bool ShouldOptOut(TranslatedFunction declaration) { switch (declaration.Name) { // Skip PushID(const char*) and prefer PushID(const char*, const char*) case "PushID": // Skip GetID(const char*) and prefer GetID(const char*, const char*) case "GetID": return(declaration.Parameters.Length == 1); // This function takes a string, but not one that's written by humans. // If we were to call this from C# we'd want to actually pass in a ReadOnlySpan<byte> or something. case "AddFontFromMemoryCompressedBase85TTF": return(true); // There is a UTF16 equivalent of this function, so it doesn't make sense to generate a wrapper case "AddInputCharactersUTF8": return(true); default: return(false); } }
internal static void MakeAndSaveTranslations( ConcurrentDictionary <ulong, TranslatedFunction> funcs, IMemoryManager memory, JumpTable jumpTable, EntryTable <uint> countTable) { var profiledFuncsToTranslate = PtcProfiler.GetProfiledFuncsToTranslate(funcs); _translateCount = 0; _translateTotalCount = profiledFuncsToTranslate.Count; int degreeOfParallelism = new DegreeOfParallelism(4d, 75d, 12.5d).GetDegreeOfParallelism(0, 32); if (_translateTotalCount == 0 || degreeOfParallelism == 0) { ResetCarriersIfNeeded(); PtcJumpTable.ClearIfNeeded(); GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce; return; } Logger.Info?.Print(LogClass.Ptc, $"{_translateCount} of {_translateTotalCount} functions translated | Thread count: {degreeOfParallelism}"); PtcStateChanged?.Invoke(PtcLoadingState.Start, _translateCount, _translateTotalCount); using AutoResetEvent progressReportEvent = new AutoResetEvent(false); Thread progressReportThread = new Thread(ReportProgress) { Name = "Ptc.ProgressReporter", Priority = ThreadPriority.Lowest, IsBackground = true }; progressReportThread.Start(progressReportEvent); void TranslateFuncs() { while (profiledFuncsToTranslate.TryDequeue(out var item)) { ulong address = item.address; Debug.Assert(PtcProfiler.IsAddressInStaticCodeRange(address)); TranslatedFunction func = Translator.Translate(memory, jumpTable, countTable, address, item.funcProfile.Mode, item.funcProfile.HighCq); bool isAddressUnique = funcs.TryAdd(address, func); Debug.Assert(isAddressUnique, $"The address 0x{address:X16} is not unique."); if (func.HighCq) { jumpTable.RegisterFunction(address, func); } Interlocked.Increment(ref _translateCount); if (State != PtcState.Enabled) { break; } } Translator.DisposePools(); } List <Thread> threads = new List <Thread>(); for (int i = 0; i < degreeOfParallelism; i++) { Thread thread = new Thread(TranslateFuncs); thread.IsBackground = true; threads.Add(thread); } threads.ForEach((thread) => thread.Start()); threads.ForEach((thread) => thread.Join()); threads.Clear(); progressReportEvent.Set(); progressReportThread.Join(); PtcStateChanged?.Invoke(PtcLoadingState.Loaded, _translateCount, _translateTotalCount); Logger.Info?.Print(LogClass.Ptc, $"{_translateCount} of {_translateTotalCount} functions translated | Thread count: {degreeOfParallelism}"); PtcJumpTable.Initialize(jumpTable); PtcJumpTable.ReadJumpTable(jumpTable); PtcJumpTable.ReadDynamicTable(jumpTable); Thread preSaveThread = new Thread(PreSave); preSaveThread.IsBackground = true; preSaveThread.Start(); }
internal static void LoadTranslations( ConcurrentDictionary <ulong, TranslatedFunction> funcs, IMemoryManager memory, JumpTable jumpTable, EntryTable <uint> countTable) { if (AreCarriersEmpty()) { return; } long infosStreamLength = _infosStream.Length; long relocsStreamLength = _relocsStream.Length; long unwindInfosStreamLength = _unwindInfosStream.Length; _infosStream.Seek(0L, SeekOrigin.Begin); _relocsStream.Seek(0L, SeekOrigin.Begin); _unwindInfosStream.Seek(0L, SeekOrigin.Begin); using (BinaryReader relocsReader = new(_relocsStream, EncodingCache.UTF8NoBOM, true)) using (BinaryReader unwindInfosReader = new(_unwindInfosStream, EncodingCache.UTF8NoBOM, true)) { for (int index = 0; index < GetEntriesCount(); index++) { InfoEntry infoEntry = DeserializeStructure <InfoEntry>(_infosStream); if (infoEntry.Stubbed) { SkipCode(index, infoEntry.CodeLength); SkipReloc(infoEntry.RelocEntriesCount); SkipUnwindInfo(unwindInfosReader); continue; } bool isEntryChanged = infoEntry.Hash != ComputeHash(memory, infoEntry.Address, infoEntry.GuestSize); if (isEntryChanged || (!infoEntry.HighCq && PtcProfiler.ProfiledFuncs.TryGetValue(infoEntry.Address, out var value) && value.HighCq)) { infoEntry.Stubbed = true; infoEntry.CodeLength = 0; UpdateInfo(infoEntry); StubCode(index); StubReloc(infoEntry.RelocEntriesCount); StubUnwindInfo(unwindInfosReader); if (isEntryChanged) { PtcJumpTable.Clean(infoEntry.Address); Logger.Info?.Print(LogClass.Ptc, $"Invalidated translated function (address: 0x{infoEntry.Address:X16})"); } continue; } byte[] code = ReadCode(index, infoEntry.CodeLength); Counter <uint> callCounter = null; if (infoEntry.RelocEntriesCount != 0) { RelocEntry[] relocEntries = GetRelocEntries(relocsReader, infoEntry.RelocEntriesCount); PatchCode(code, relocEntries, memory.PageTablePointer, jumpTable, countTable, out callCounter); } UnwindInfo unwindInfo = ReadUnwindInfo(unwindInfosReader); TranslatedFunction func = FastTranslate(code, callCounter, infoEntry.GuestSize, unwindInfo, infoEntry.HighCq); bool isAddressUnique = funcs.TryAdd(infoEntry.Address, func); Debug.Assert(isAddressUnique, $"The address 0x{infoEntry.Address:X16} is not unique."); } } if (_infosStream.Length != infosStreamLength || _infosStream.Position != infosStreamLength || _relocsStream.Length != relocsStreamLength || _relocsStream.Position != relocsStreamLength || _unwindInfosStream.Length != unwindInfosStreamLength || _unwindInfosStream.Position != unwindInfosStreamLength) { throw new Exception("The length of a memory stream has changed, or its position has not reached or has exceeded its end."); } jumpTable.Initialize(PtcJumpTable, funcs); PtcJumpTable.WriteJumpTable(jumpTable, funcs); PtcJumpTable.WriteDynamicTable(jumpTable); Logger.Info?.Print(LogClass.Ptc, $"{funcs.Count} translated functions loaded"); }
public static ulong GetFunctionAddress(ulong address) { TranslatedFunction function = _context.Translator.GetOrTranslate(address, GetContext().ExecutionMode); return((ulong)function.FuncPtr.ToInt64()); }
internal static void MakeAndSaveTranslations(ConcurrentDictionary <ulong, TranslatedFunction> funcs, IMemoryManager memory, JumpTable jumpTable) { var profiledFuncsToTranslate = PtcProfiler.GetProfiledFuncsToTranslate(funcs); if (profiledFuncsToTranslate.Count == 0) { return; } _translateCount = 0; ThreadPool.QueueUserWorkItem(TranslationLogger, profiledFuncsToTranslate.Count); void TranslateFuncs() { while (profiledFuncsToTranslate.TryDequeue(out var item)) { ulong address = item.address; Debug.Assert(PtcProfiler.IsAddressInStaticCodeRange(address)); TranslatedFunction func = Translator.Translate(memory, jumpTable, address, item.mode, item.highCq); bool isAddressUnique = funcs.TryAdd(address, func); Debug.Assert(isAddressUnique, $"The address 0x{address:X16} is not unique."); if (func.HighCq) { jumpTable.RegisterFunction(address, func); } Interlocked.Increment(ref _translateCount); if (State != PtcState.Enabled) { break; } } } int maxDegreeOfParallelism = (Environment.ProcessorCount * 3) / 4; List <Thread> threads = new List <Thread>(); for (int i = 0; i < maxDegreeOfParallelism; i++) { Thread thread = new Thread(TranslateFuncs); thread.IsBackground = true; threads.Add(thread); } threads.ForEach((thread) => thread.Start()); threads.ForEach((thread) => thread.Join()); threads.Clear(); Translator.ResetPools(); _loggerEvent.Set(); PtcJumpTable.Initialize(jumpTable); PtcJumpTable.ReadJumpTable(jumpTable); PtcJumpTable.ReadDynamicTable(jumpTable); ThreadPool.QueueUserWorkItem(PreSave); }
internal static void LoadTranslations(ConcurrentDictionary <ulong, TranslatedFunction> funcs, IMemoryManager memory, JumpTable jumpTable) { if ((int)_infosStream.Length == 0 || (int)_codesStream.Length == 0 || (int)_relocsStream.Length == 0 || (int)_unwindInfosStream.Length == 0) { return; } Debug.Assert(funcs.Count == 0); _infosStream.Seek(0L, SeekOrigin.Begin); _codesStream.Seek(0L, SeekOrigin.Begin); _relocsStream.Seek(0L, SeekOrigin.Begin); _unwindInfosStream.Seek(0L, SeekOrigin.Begin); using (BinaryReader infosReader = new BinaryReader(_infosStream, EncodingCache.UTF8NoBOM, true)) using (BinaryReader codesReader = new BinaryReader(_codesStream, EncodingCache.UTF8NoBOM, true)) using (BinaryReader relocsReader = new BinaryReader(_relocsStream, EncodingCache.UTF8NoBOM, true)) using (BinaryReader unwindInfosReader = new BinaryReader(_unwindInfosStream, EncodingCache.UTF8NoBOM, true)) { for (int i = 0; i < GetInfosEntriesCount(); i++) { InfoEntry infoEntry = ReadInfo(infosReader); if (infoEntry.Stubbed) { SkipCode(infoEntry.CodeLen); SkipReloc(infoEntry.RelocEntriesCount); SkipUnwindInfo(unwindInfosReader); } else if (infoEntry.HighCq || !PtcProfiler.ProfiledFuncs.TryGetValue(infoEntry.Address, out var value) || !value.highCq) { byte[] code = ReadCode(codesReader, infoEntry.CodeLen); if (infoEntry.RelocEntriesCount != 0) { RelocEntry[] relocEntries = GetRelocEntries(relocsReader, infoEntry.RelocEntriesCount); PatchCode(code, relocEntries, memory.PageTablePointer, jumpTable); } UnwindInfo unwindInfo = ReadUnwindInfo(unwindInfosReader); TranslatedFunction func = FastTranslate(code, infoEntry.GuestSize, unwindInfo, infoEntry.HighCq); bool isAddressUnique = funcs.TryAdd(infoEntry.Address, func); Debug.Assert(isAddressUnique, $"The address 0x{infoEntry.Address:X16} is not unique."); } else { infoEntry.Stubbed = true; UpdateInfo(infoEntry); StubCode(infoEntry.CodeLen); StubReloc(infoEntry.RelocEntriesCount); StubUnwindInfo(unwindInfosReader); } } } if (_infosStream.Position < _infosStream.Length || _codesStream.Position < _codesStream.Length || _relocsStream.Position < _relocsStream.Length || _unwindInfosStream.Position < _unwindInfosStream.Length) { throw new Exception("Could not reach the end of one or more memory streams."); } jumpTable.Initialize(PtcJumpTable, funcs); PtcJumpTable.WriteJumpTable(jumpTable, funcs); PtcJumpTable.WriteDynamicTable(jumpTable); Logger.Info?.Print(LogClass.Ptc, $"{funcs.Count} translated functions loaded"); }
internal static void MakeAndSaveTranslations(ConcurrentDictionary <ulong, TranslatedFunction> funcs, IMemoryManager memory, JumpTable jumpTable) { if (PtcProfiler.ProfiledFuncs.Count == 0) { return; } _translateCount = 0; _rejitCount = 0; ThreadPool.QueueUserWorkItem(TranslationLogger, (funcs.Count, PtcProfiler.ProfiledFuncs.Count)); int maxDegreeOfParallelism = (Environment.ProcessorCount * 3) / 4; Parallel.ForEach(PtcProfiler.ProfiledFuncs, new ParallelOptions { MaxDegreeOfParallelism = maxDegreeOfParallelism }, (item, state) => { ulong address = item.Key; Debug.Assert(PtcProfiler.IsAddressInStaticCodeRange(address)); if (!funcs.ContainsKey(address)) { TranslatedFunction func = Translator.Translate(memory, jumpTable, address, item.Value.mode, item.Value.highCq); funcs.TryAdd(address, func); if (func.HighCq) { jumpTable.RegisterFunction(address, func); } Interlocked.Increment(ref _translateCount); } else if (item.Value.highCq && !funcs[address].HighCq) { TranslatedFunction func = Translator.Translate(memory, jumpTable, address, item.Value.mode, highCq: true); funcs[address] = func; jumpTable.RegisterFunction(address, func); Interlocked.Increment(ref _rejitCount); } if (State != PtcState.Enabled) { state.Stop(); } }); _loggerEvent.Set(); if (_translateCount != 0 || _rejitCount != 0) { PtcJumpTable.Initialize(jumpTable); PtcJumpTable.ReadJumpTable(jumpTable); PtcJumpTable.ReadDynamicTable(jumpTable); ThreadPool.QueueUserWorkItem(PreSave); } }
// This transformation is an opt-out heuristic // It'd be not-ideal if it was wrong, but it's expected that the user will be smart enough to avoid calling the nonsense overload // In the long term we'll rely on a parameter being explicitly marked as a string once https://github.com/ocornut/imgui/pull/3038 is merged protected override TransformationResult TransformFunction(TransformationContext context, TranslatedFunction declaration) { // Opt out of specific functions that shouldn't get overloads if (ShouldOptOut(declaration)) { return(declaration); } // Check if this function has a string parameter ImmutableArray <int> .Builder?stringParameters = null; bool firstParameterHasEnd = false; for (int i = 0; i < declaration.Parameters.Length; i++) { TranslatedParameter parameter = declaration.Parameters[i]; // If the parameter is a `byte*`, it's a string parameter if (parameter.Type is PointerTypeReference { Inner: CSharpBuiltinTypeReference cSharpPointee } && cSharpPointee == CSharpBuiltinType.Byte)
private static ulong GetFunctionAddressWithHint(ulong address, bool hintRejit) { TranslatedFunction function = Context.Translator.GetOrTranslate(address, GetContext().ExecutionMode, hintRejit); return((ulong)function.FuncPtr.ToInt64()); }
protected override TransformationResult TransformFunction(TransformationContext context, TranslatedFunction declaration) { TransformationResult result = declaration; if (declaration.Name == "DebugCheckVersionAndDataLayout") { if (IMGUI_VERSION is not null) { result.Add(IMGUI_VERSION); } if (IMGUI_VERSION_NUM is not null) { result.Add(IMGUI_VERSION_NUM); } } return(result); }
public static bool TryFastTranslateDyn( Translator translator, ulong address, ulong funcSize, bool highCq, ref TtcInfo ttcInfoRef, out TranslatedFunction translatedFuncDyn) { ttcInfoRef = null; translatedFuncDyn = null; if (!Translator.OverlapsWith(address, funcSize, Translator.StaticCodeStart, Translator.StaticCodeSize) && (highCq || funcSize > MinFuncSizeDyn)) { Hash128 preHash = translator.ComputeHash(address, funcSize); Hash128 hash = highCq ? ~preHash : preHash; if (!translator.TtcInfos.TryGetValue(hash, out TtcInfo ttcInfoOut)) { TtcInfo ttcInfoNew = new TtcInfo(); ttcInfoNew.IsBusy = true; ttcInfoNew.LastGuestAddress = address; ttcInfoNew.GuestSize = funcSize; if (translator.TtcInfos.TryAdd(hash, ttcInfoNew)) { ttcInfoRef = ttcInfoNew; } else { ttcInfoNew.Dispose(); } } else { lock (ttcInfoOut) { if (!ttcInfoOut.IsBusy) { ttcInfoOut.IsBusy = true; ttcInfoOut.LastGuestAddress = address; if (ttcInfoOut.RelocEntriesCount != 0) { RelocEntry[] relocEntries = Ptc.GetRelocEntries(ttcInfoOut.RelocStream, ttcInfoOut.RelocEntriesCount, reloadStream: true); JitCache.ModifyMapped(ttcInfoOut.TranslatedFunc.FuncPtr, ttcInfoOut.HostSize, (code) => PatchCodeDyn(translator, code, relocEntries, address)); } if (ttcInfoOut.TranslatedFunc.CallCounter != null && Volatile.Read(ref ttcInfoOut.TranslatedFunc.CallCounter.Value) > Translator.MinsCallForRejit) { Volatile.Write(ref ttcInfoOut.TranslatedFunc.CallCounter.Value, Translator.MinsCallForRejit); } translatedFuncDyn = ttcInfoOut.TranslatedFunc; Logger.Debug?.Print(LogClass.Ttc, $"Fast translated dynamic function 0x{preHash} " + $"(HighCq: {highCq}{(!highCq ? $" [CallCounter: {ttcInfoOut.TranslatedFunc.CallCounter.Value}]" : string.Empty)}, HostSize: {ttcInfoOut.HostSize}) " + $"| DynFuncs: {translator.TtcInfos.Count}."); return(true); } } } } return(false); }
protected override TransformationResult TransformFunction(TransformationContext context, TranslatedFunction declaration) { // Private/protected functions are always stripped if (declaration.Accessibility is AccessModifier.Private or AccessModifier.Protected) { return(null); } // Check if any parents are private/protected foreach (TranslatedDeclaration parent in context.Parents) { if (parent.Accessibility is AccessModifier.Private or AccessModifier.Protected) { return(null); } } return(base.TransformFunction(context, declaration)); }
protected override TransformationResult TransformFunction(TransformationContext context, TranslatedFunction declaration) => declaration with