From b424df5a64ccc7746793bbcc9e639bb799898b59 Mon Sep 17 00:00:00 2001 From: ParantezTech Date: Mon, 29 Jun 2026 13:28:30 +0300 Subject: [PATCH] [kernel] speed up printf --- .../Kernel/KernelMemoryCompatExports.cs | 227 +++++++++++------- 1 file changed, 136 insertions(+), 91 deletions(-) diff --git a/src/SharpEmu.Libs/Kernel/KernelMemoryCompatExports.cs b/src/SharpEmu.Libs/Kernel/KernelMemoryCompatExports.cs index 8bec571..7a35800 100644 --- a/src/SharpEmu.Libs/Kernel/KernelMemoryCompatExports.cs +++ b/src/SharpEmu.Libs/Kernel/KernelMemoryCompatExports.cs @@ -602,9 +602,7 @@ public static class KernelMemoryCompatExports } else { - ulong NextGpArg() => vaCursor.NextGpArg(); - double NextFloatArg() => vaCursor.NextFloatArg(); - rendered = FormatString(ctx, format, NextGpArg, NextFloatArg); + rendered = FormatString(ctx, format, ref vaCursor); } Console.Write(rendered); @@ -2908,9 +2906,8 @@ public static class KernelMemoryCompatExports var format = Encoding.UTF8.GetString(formatBytes); var result = FormatString(ctx, format); - var outputBytes = Encoding.UTF8.GetBytes(result); - return WriteSnprintfOutput(ctx, destination, bufferSize, outputBytes); + return WriteSnprintfOutput(ctx, destination, bufferSize, result); } private static int VsnprintfCore(CpuContext ctx) @@ -2931,12 +2928,9 @@ public static class KernelMemoryCompatExports return WriteSnprintfOutput(ctx, destination, bufferSize, formatBytes); } - ulong NextGpArg() => vaCursor.NextGpArg(); - double NextFloatArg() => vaCursor.NextFloatArg(); - var rendered = FormatString(ctx, format, NextGpArg, NextFloatArg); + var rendered = FormatString(ctx, format, ref vaCursor); - var outputBytes = Encoding.UTF8.GetBytes(rendered); - return WriteSnprintfOutput(ctx, destination, bufferSize, outputBytes); + return WriteSnprintfOutput(ctx, destination, bufferSize, rendered); } private static int SwprintfCore(CpuContext ctx) @@ -2977,9 +2971,7 @@ public static class KernelMemoryCompatExports else { TraceWidePrintfVaList(ctx, "vswprintf", format, vaListAddress, vaCursor); - ulong NextGpArg() => vaCursor.NextGpArg(); - double NextFloatArg() => vaCursor.NextFloatArg(); - rendered = FormatString(ctx, format, NextGpArg, NextFloatArg); + rendered = FormatString(ctx, format, ref vaCursor); } TraceWidePrintf(ctx, "vswprintf", destination, bufferSize, format, rendered); @@ -2994,10 +2986,8 @@ public static class KernelMemoryCompatExports return false; } - if (!TryReadUInt32Compat(ctx, vaListAddress + 0, out var gpOffset) || - !TryReadUInt32Compat(ctx, vaListAddress + 4, out var fpOffset) || - !TryReadUInt64Compat(ctx, vaListAddress + 8, out var overflowArgArea) || - !TryReadUInt64Compat(ctx, vaListAddress + 16, out var regSaveArea)) + Span vaList = stackalloc byte[24]; + if (!TryReadCompat(ctx, vaListAddress, vaList)) { return false; } @@ -3005,10 +2995,10 @@ public static class KernelMemoryCompatExports cursor = new SysVAmd64VaListCursor( ctx, vaListAddress, - gpOffset, - fpOffset, - overflowArgArea, - regSaveArea); + BinaryPrimitives.ReadUInt32LittleEndian(vaList), + BinaryPrimitives.ReadUInt32LittleEndian(vaList[4..]), + BinaryPrimitives.ReadUInt64LittleEndian(vaList[8..]), + BinaryPrimitives.ReadUInt64LittleEndian(vaList[16..])); return true; } @@ -3038,6 +3028,21 @@ public static class KernelMemoryCompatExports return (int)OrbisGen2Result.ORBIS_GEN2_OK; } + private static int WriteSnprintfOutput( + CpuContext ctx, + ulong destination, + ulong bufferSize, + string output) + { + if (bufferSize == 0 || destination == 0) + { + ctx[CpuRegister.Rax] = unchecked((ulong)Encoding.UTF8.GetByteCount(output)); + return (int)OrbisGen2Result.ORBIS_GEN2_OK; + } + + return WriteSnprintfOutput(ctx, destination, bufferSize, Encoding.UTF8.GetBytes(output)); + } + private static int WriteSwprintfOutput( CpuContext ctx, ulong destination, @@ -3329,30 +3334,8 @@ public static class KernelMemoryCompatExports internal static string FormatStringFromVarArgs(CpuContext ctx, string format, int firstGpArgIndex) { - var gpIndex = Math.Max(0, firstGpArgIndex); - - ulong GetGpArg(int index) - { - return index switch - { - 0 => ctx[CpuRegister.Rdi], - 1 => ctx[CpuRegister.Rsi], - 2 => ctx[CpuRegister.Rdx], - 3 => ctx[CpuRegister.Rcx], - 4 => ctx[CpuRegister.R8], - 5 => ctx[CpuRegister.R9], - _ => ReadStackArg(ctx, (ulong)(index - 6) * 8) - }; - } - - ulong NextGpArg() => GetGpArg(gpIndex++); - double NextFloatArg() - { - var rawBits = NextGpArg(); - return BitConverter.Int64BitsToDouble(unchecked((long)rawBits)); - } - - return FormatString(ctx, format, NextGpArg, NextFloatArg); + var argumentSource = new RegisterPrintfArgumentSource(ctx, Math.Max(0, firstGpArgIndex)); + return FormatString(ctx, format, ref argumentSource); } private static string FormatString(CpuContext ctx, string format) @@ -3360,13 +3343,13 @@ public static class KernelMemoryCompatExports return FormatStringFromVarArgs(ctx, format, firstGpArgIndex: 3); } - private static string FormatString( + private static string FormatString( CpuContext ctx, string format, - Func nextGpArg, - Func nextFloatArg) + ref TArgumentSource argumentSource) + where TArgumentSource : struct, IPrintfArgumentSource { - var sb = new StringBuilder(); + var sb = new StringBuilder(format.Length + 32); for (var i = 0; i < format.Length; i++) { @@ -3405,7 +3388,7 @@ public static class KernelMemoryCompatExports var width = 0; if (i < format.Length && format[i] == '*') { - width = unchecked((int)nextGpArg()); + width = unchecked((int)argumentSource.NextGpArg()); i++; if (width < 0) { @@ -3428,7 +3411,7 @@ public static class KernelMemoryCompatExports i++; if (i < format.Length && format[i] == '*') { - precision = unchecked((int)nextGpArg()); + precision = unchecked((int)argumentSource.NextGpArg()); i++; } else if (i < format.Length && char.IsDigit(format[i])) @@ -3482,14 +3465,14 @@ public static class KernelMemoryCompatExports { long value = lengthMod switch { - "hh" => unchecked((sbyte)nextGpArg()), - "h" => unchecked((short)nextGpArg()), - "l" => unchecked((long)nextGpArg()), - "ll" => unchecked((long)nextGpArg()), - "j" => unchecked((long)nextGpArg()), - "z" => unchecked((long)nextGpArg()), - "t" => unchecked((long)nextGpArg()), - _ => unchecked((int)nextGpArg()) + "hh" => unchecked((sbyte)argumentSource.NextGpArg()), + "h" => unchecked((short)argumentSource.NextGpArg()), + "l" => unchecked((long)argumentSource.NextGpArg()), + "ll" => unchecked((long)argumentSource.NextGpArg()), + "j" => unchecked((long)argumentSource.NextGpArg()), + "z" => unchecked((long)argumentSource.NextGpArg()), + "t" => unchecked((long)argumentSource.NextGpArg()), + _ => unchecked((int)argumentSource.NextGpArg()) }; var formatted = value.ToString(); @@ -3506,14 +3489,14 @@ public static class KernelMemoryCompatExports { ulong value = lengthMod switch { - "hh" => (byte)nextGpArg(), - "h" => (ushort)nextGpArg(), - "l" => nextGpArg(), - "ll" => nextGpArg(), - "j" => nextGpArg(), - "z" => nextGpArg(), - "t" => nextGpArg(), - _ => (uint)nextGpArg() + "hh" => (byte)argumentSource.NextGpArg(), + "h" => (ushort)argumentSource.NextGpArg(), + "l" => argumentSource.NextGpArg(), + "ll" => argumentSource.NextGpArg(), + "j" => argumentSource.NextGpArg(), + "z" => argumentSource.NextGpArg(), + "t" => argumentSource.NextGpArg(), + _ => (uint)argumentSource.NextGpArg() }; var formatted = value.ToString(); @@ -3526,14 +3509,14 @@ public static class KernelMemoryCompatExports { ulong value = lengthMod switch { - "hh" => (byte)nextGpArg(), - "h" => (ushort)nextGpArg(), - "l" => nextGpArg(), - "ll" => nextGpArg(), - "j" => nextGpArg(), - "z" => nextGpArg(), - "t" => nextGpArg(), - _ => (uint)nextGpArg() + "hh" => (byte)argumentSource.NextGpArg(), + "h" => (ushort)argumentSource.NextGpArg(), + "l" => argumentSource.NextGpArg(), + "ll" => argumentSource.NextGpArg(), + "j" => argumentSource.NextGpArg(), + "z" => argumentSource.NextGpArg(), + "t" => argumentSource.NextGpArg(), + _ => (uint)argumentSource.NextGpArg() }; var formatted = specifier == 'x' @@ -3551,14 +3534,14 @@ public static class KernelMemoryCompatExports { ulong value = lengthMod switch { - "hh" => (byte)nextGpArg(), - "h" => (ushort)nextGpArg(), - "l" => nextGpArg(), - "ll" => nextGpArg(), - "j" => nextGpArg(), - "z" => nextGpArg(), - "t" => nextGpArg(), - _ => (uint)nextGpArg() + "hh" => (byte)argumentSource.NextGpArg(), + "h" => (ushort)argumentSource.NextGpArg(), + "l" => argumentSource.NextGpArg(), + "ll" => argumentSource.NextGpArg(), + "j" => argumentSource.NextGpArg(), + "z" => argumentSource.NextGpArg(), + "t" => argumentSource.NextGpArg(), + _ => (uint)argumentSource.NextGpArg() }; var formatted = Convert.ToString((long)value, 8); @@ -3571,7 +3554,7 @@ public static class KernelMemoryCompatExports case 'p': { - var value = nextGpArg(); + var value = argumentSource.NextGpArg(); var formatted = value == 0 ? "(nil)" : $"0x{value:X}"; @@ -3581,7 +3564,7 @@ public static class KernelMemoryCompatExports case 's': { - var strAddr = nextGpArg(); + var strAddr = argumentSource.NextGpArg(); TracePrintfStringArgument(ctx, lengthMod, strAddr); if (strAddr == 0) { @@ -3620,14 +3603,14 @@ public static class KernelMemoryCompatExports string renderedChar; if (lengthMod == "l") { - var scalar = unchecked((ushort)nextGpArg()); + var scalar = unchecked((ushort)argumentSource.NextGpArg()); renderedChar = TryConvertWideScalarToString(scalar, out var wideCharText) ? wideCharText : "?"; } else { - renderedChar = ((char)(byte)nextGpArg()).ToString(); + renderedChar = ((char)(byte)argumentSource.NextGpArg()).ToString(); } sb.Append(PadString(renderedChar, width, leftAlign, false)); @@ -3641,7 +3624,7 @@ public static class KernelMemoryCompatExports case 'g': case 'G': { - var value = nextFloatArg(); + var value = argumentSource.NextFloatArg(); var formatStr = precision >= 0 ? $"{{0:{specifier}{precision}}}" @@ -3659,7 +3642,7 @@ public static class KernelMemoryCompatExports case 'n': { - var addr = nextGpArg(); + var addr = argumentSource.NextGpArg(); if (addr != 0) { _ = TryWriteInt32(ctx, addr, sb.Length); @@ -3699,7 +3682,46 @@ public static class KernelMemoryCompatExports return leftAlign ? str + padding : padding + str; } - private struct SysVAmd64VaListCursor + private interface IPrintfArgumentSource + { + ulong NextGpArg(); + + double NextFloatArg(); + } + + private struct RegisterPrintfArgumentSource : IPrintfArgumentSource + { + private readonly CpuContext _ctx; + private int _gpIndex; + + public RegisterPrintfArgumentSource(CpuContext ctx, int gpIndex) + { + _ctx = ctx; + _gpIndex = gpIndex; + } + + public ulong NextGpArg() + { + var index = _gpIndex++; + return index switch + { + 0 => _ctx[CpuRegister.Rdi], + 1 => _ctx[CpuRegister.Rsi], + 2 => _ctx[CpuRegister.Rdx], + 3 => _ctx[CpuRegister.Rcx], + 4 => _ctx[CpuRegister.R8], + 5 => _ctx[CpuRegister.R9], + _ => ReadStackArg(_ctx, (ulong)(index - 6) * 8) + }; + } + + public double NextFloatArg() + { + return BitConverter.Int64BitsToDouble(unchecked((long)NextGpArg())); + } + } + + private struct SysVAmd64VaListCursor : IPrintfArgumentSource { private const uint GpSaveAreaLimit = 48; private const uint FpSaveAreaLimit = 176; @@ -4297,9 +4319,32 @@ public static class KernelMemoryCompatExports } const int maxChunkSize = 4096; + const int inlineChunkSize = 256; + Span inlineChunk = stackalloc byte[inlineChunkSize]; + var firstPageRemaining = maxChunkSize - (int)(address & (maxChunkSize - 1)); + var firstReadLength = Math.Min(limit, Math.Min(inlineChunkSize, firstPageRemaining)); + var firstSpan = inlineChunk[..firstReadLength]; + ulong offset = 0; + if (TryReadCompat(ctx, address, firstSpan)) + { + var nulIndex = firstSpan.IndexOf((byte)0); + if (nulIndex >= 0) + { + bytes = firstSpan[..nulIndex].ToArray(); + return true; + } + + offset = unchecked((ulong)firstReadLength); + } + var chunk = GC.AllocateUninitializedArray(Math.Min(maxChunkSize, limit)); var writer = new ArrayBufferWriter(Math.Min(limit, 256)); - ulong offset = 0; + if (offset != 0) + { + firstSpan.CopyTo(writer.GetSpan(firstReadLength)); + writer.Advance(firstReadLength); + } + while (offset < (ulong)limit) { var current = address + offset;