[kernel] speed up printf

This commit is contained in:
ParantezTech
2026-06-29 13:28:30 +03:00
parent 7f85971e39
commit b424df5a64
@@ -602,9 +602,7 @@ public static class KernelMemoryCompatExports
} }
else else
{ {
ulong NextGpArg() => vaCursor.NextGpArg(); rendered = FormatString(ctx, format, ref vaCursor);
double NextFloatArg() => vaCursor.NextFloatArg();
rendered = FormatString(ctx, format, NextGpArg, NextFloatArg);
} }
Console.Write(rendered); Console.Write(rendered);
@@ -2908,9 +2906,8 @@ public static class KernelMemoryCompatExports
var format = Encoding.UTF8.GetString(formatBytes); var format = Encoding.UTF8.GetString(formatBytes);
var result = FormatString(ctx, format); 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) private static int VsnprintfCore(CpuContext ctx)
@@ -2931,12 +2928,9 @@ public static class KernelMemoryCompatExports
return WriteSnprintfOutput(ctx, destination, bufferSize, formatBytes); return WriteSnprintfOutput(ctx, destination, bufferSize, formatBytes);
} }
ulong NextGpArg() => vaCursor.NextGpArg(); var rendered = FormatString(ctx, format, ref vaCursor);
double NextFloatArg() => vaCursor.NextFloatArg();
var rendered = FormatString(ctx, format, NextGpArg, NextFloatArg);
var outputBytes = Encoding.UTF8.GetBytes(rendered); return WriteSnprintfOutput(ctx, destination, bufferSize, rendered);
return WriteSnprintfOutput(ctx, destination, bufferSize, outputBytes);
} }
private static int SwprintfCore(CpuContext ctx) private static int SwprintfCore(CpuContext ctx)
@@ -2977,9 +2971,7 @@ public static class KernelMemoryCompatExports
else else
{ {
TraceWidePrintfVaList(ctx, "vswprintf", format, vaListAddress, vaCursor); TraceWidePrintfVaList(ctx, "vswprintf", format, vaListAddress, vaCursor);
ulong NextGpArg() => vaCursor.NextGpArg(); rendered = FormatString(ctx, format, ref vaCursor);
double NextFloatArg() => vaCursor.NextFloatArg();
rendered = FormatString(ctx, format, NextGpArg, NextFloatArg);
} }
TraceWidePrintf(ctx, "vswprintf", destination, bufferSize, format, rendered); TraceWidePrintf(ctx, "vswprintf", destination, bufferSize, format, rendered);
@@ -2994,10 +2986,8 @@ public static class KernelMemoryCompatExports
return false; return false;
} }
if (!TryReadUInt32Compat(ctx, vaListAddress + 0, out var gpOffset) || Span<byte> vaList = stackalloc byte[24];
!TryReadUInt32Compat(ctx, vaListAddress + 4, out var fpOffset) || if (!TryReadCompat(ctx, vaListAddress, vaList))
!TryReadUInt64Compat(ctx, vaListAddress + 8, out var overflowArgArea) ||
!TryReadUInt64Compat(ctx, vaListAddress + 16, out var regSaveArea))
{ {
return false; return false;
} }
@@ -3005,10 +2995,10 @@ public static class KernelMemoryCompatExports
cursor = new SysVAmd64VaListCursor( cursor = new SysVAmd64VaListCursor(
ctx, ctx,
vaListAddress, vaListAddress,
gpOffset, BinaryPrimitives.ReadUInt32LittleEndian(vaList),
fpOffset, BinaryPrimitives.ReadUInt32LittleEndian(vaList[4..]),
overflowArgArea, BinaryPrimitives.ReadUInt64LittleEndian(vaList[8..]),
regSaveArea); BinaryPrimitives.ReadUInt64LittleEndian(vaList[16..]));
return true; return true;
} }
@@ -3038,6 +3028,21 @@ public static class KernelMemoryCompatExports
return (int)OrbisGen2Result.ORBIS_GEN2_OK; 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( private static int WriteSwprintfOutput(
CpuContext ctx, CpuContext ctx,
ulong destination, ulong destination,
@@ -3329,30 +3334,8 @@ public static class KernelMemoryCompatExports
internal static string FormatStringFromVarArgs(CpuContext ctx, string format, int firstGpArgIndex) internal static string FormatStringFromVarArgs(CpuContext ctx, string format, int firstGpArgIndex)
{ {
var gpIndex = Math.Max(0, firstGpArgIndex); var argumentSource = new RegisterPrintfArgumentSource(ctx, Math.Max(0, firstGpArgIndex));
return FormatString(ctx, format, ref argumentSource);
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);
} }
private static string FormatString(CpuContext ctx, string format) private static string FormatString(CpuContext ctx, string format)
@@ -3360,13 +3343,13 @@ public static class KernelMemoryCompatExports
return FormatStringFromVarArgs(ctx, format, firstGpArgIndex: 3); return FormatStringFromVarArgs(ctx, format, firstGpArgIndex: 3);
} }
private static string FormatString( private static string FormatString<TArgumentSource>(
CpuContext ctx, CpuContext ctx,
string format, string format,
Func<ulong> nextGpArg, ref TArgumentSource argumentSource)
Func<double> nextFloatArg) where TArgumentSource : struct, IPrintfArgumentSource
{ {
var sb = new StringBuilder(); var sb = new StringBuilder(format.Length + 32);
for (var i = 0; i < format.Length; i++) for (var i = 0; i < format.Length; i++)
{ {
@@ -3405,7 +3388,7 @@ public static class KernelMemoryCompatExports
var width = 0; var width = 0;
if (i < format.Length && format[i] == '*') if (i < format.Length && format[i] == '*')
{ {
width = unchecked((int)nextGpArg()); width = unchecked((int)argumentSource.NextGpArg());
i++; i++;
if (width < 0) if (width < 0)
{ {
@@ -3428,7 +3411,7 @@ public static class KernelMemoryCompatExports
i++; i++;
if (i < format.Length && format[i] == '*') if (i < format.Length && format[i] == '*')
{ {
precision = unchecked((int)nextGpArg()); precision = unchecked((int)argumentSource.NextGpArg());
i++; i++;
} }
else if (i < format.Length && char.IsDigit(format[i])) else if (i < format.Length && char.IsDigit(format[i]))
@@ -3482,14 +3465,14 @@ public static class KernelMemoryCompatExports
{ {
long value = lengthMod switch long value = lengthMod switch
{ {
"hh" => unchecked((sbyte)nextGpArg()), "hh" => unchecked((sbyte)argumentSource.NextGpArg()),
"h" => unchecked((short)nextGpArg()), "h" => unchecked((short)argumentSource.NextGpArg()),
"l" => unchecked((long)nextGpArg()), "l" => unchecked((long)argumentSource.NextGpArg()),
"ll" => unchecked((long)nextGpArg()), "ll" => unchecked((long)argumentSource.NextGpArg()),
"j" => unchecked((long)nextGpArg()), "j" => unchecked((long)argumentSource.NextGpArg()),
"z" => unchecked((long)nextGpArg()), "z" => unchecked((long)argumentSource.NextGpArg()),
"t" => unchecked((long)nextGpArg()), "t" => unchecked((long)argumentSource.NextGpArg()),
_ => unchecked((int)nextGpArg()) _ => unchecked((int)argumentSource.NextGpArg())
}; };
var formatted = value.ToString(); var formatted = value.ToString();
@@ -3506,14 +3489,14 @@ public static class KernelMemoryCompatExports
{ {
ulong value = lengthMod switch ulong value = lengthMod switch
{ {
"hh" => (byte)nextGpArg(), "hh" => (byte)argumentSource.NextGpArg(),
"h" => (ushort)nextGpArg(), "h" => (ushort)argumentSource.NextGpArg(),
"l" => nextGpArg(), "l" => argumentSource.NextGpArg(),
"ll" => nextGpArg(), "ll" => argumentSource.NextGpArg(),
"j" => nextGpArg(), "j" => argumentSource.NextGpArg(),
"z" => nextGpArg(), "z" => argumentSource.NextGpArg(),
"t" => nextGpArg(), "t" => argumentSource.NextGpArg(),
_ => (uint)nextGpArg() _ => (uint)argumentSource.NextGpArg()
}; };
var formatted = value.ToString(); var formatted = value.ToString();
@@ -3526,14 +3509,14 @@ public static class KernelMemoryCompatExports
{ {
ulong value = lengthMod switch ulong value = lengthMod switch
{ {
"hh" => (byte)nextGpArg(), "hh" => (byte)argumentSource.NextGpArg(),
"h" => (ushort)nextGpArg(), "h" => (ushort)argumentSource.NextGpArg(),
"l" => nextGpArg(), "l" => argumentSource.NextGpArg(),
"ll" => nextGpArg(), "ll" => argumentSource.NextGpArg(),
"j" => nextGpArg(), "j" => argumentSource.NextGpArg(),
"z" => nextGpArg(), "z" => argumentSource.NextGpArg(),
"t" => nextGpArg(), "t" => argumentSource.NextGpArg(),
_ => (uint)nextGpArg() _ => (uint)argumentSource.NextGpArg()
}; };
var formatted = specifier == 'x' var formatted = specifier == 'x'
@@ -3551,14 +3534,14 @@ public static class KernelMemoryCompatExports
{ {
ulong value = lengthMod switch ulong value = lengthMod switch
{ {
"hh" => (byte)nextGpArg(), "hh" => (byte)argumentSource.NextGpArg(),
"h" => (ushort)nextGpArg(), "h" => (ushort)argumentSource.NextGpArg(),
"l" => nextGpArg(), "l" => argumentSource.NextGpArg(),
"ll" => nextGpArg(), "ll" => argumentSource.NextGpArg(),
"j" => nextGpArg(), "j" => argumentSource.NextGpArg(),
"z" => nextGpArg(), "z" => argumentSource.NextGpArg(),
"t" => nextGpArg(), "t" => argumentSource.NextGpArg(),
_ => (uint)nextGpArg() _ => (uint)argumentSource.NextGpArg()
}; };
var formatted = Convert.ToString((long)value, 8); var formatted = Convert.ToString((long)value, 8);
@@ -3571,7 +3554,7 @@ public static class KernelMemoryCompatExports
case 'p': case 'p':
{ {
var value = nextGpArg(); var value = argumentSource.NextGpArg();
var formatted = value == 0 var formatted = value == 0
? "(nil)" ? "(nil)"
: $"0x{value:X}"; : $"0x{value:X}";
@@ -3581,7 +3564,7 @@ public static class KernelMemoryCompatExports
case 's': case 's':
{ {
var strAddr = nextGpArg(); var strAddr = argumentSource.NextGpArg();
TracePrintfStringArgument(ctx, lengthMod, strAddr); TracePrintfStringArgument(ctx, lengthMod, strAddr);
if (strAddr == 0) if (strAddr == 0)
{ {
@@ -3620,14 +3603,14 @@ public static class KernelMemoryCompatExports
string renderedChar; string renderedChar;
if (lengthMod == "l") if (lengthMod == "l")
{ {
var scalar = unchecked((ushort)nextGpArg()); var scalar = unchecked((ushort)argumentSource.NextGpArg());
renderedChar = TryConvertWideScalarToString(scalar, out var wideCharText) renderedChar = TryConvertWideScalarToString(scalar, out var wideCharText)
? wideCharText ? wideCharText
: "?"; : "?";
} }
else else
{ {
renderedChar = ((char)(byte)nextGpArg()).ToString(); renderedChar = ((char)(byte)argumentSource.NextGpArg()).ToString();
} }
sb.Append(PadString(renderedChar, width, leftAlign, false)); sb.Append(PadString(renderedChar, width, leftAlign, false));
@@ -3641,7 +3624,7 @@ public static class KernelMemoryCompatExports
case 'g': case 'g':
case 'G': case 'G':
{ {
var value = nextFloatArg(); var value = argumentSource.NextFloatArg();
var formatStr = precision >= 0 var formatStr = precision >= 0
? $"{{0:{specifier}{precision}}}" ? $"{{0:{specifier}{precision}}}"
@@ -3659,7 +3642,7 @@ public static class KernelMemoryCompatExports
case 'n': case 'n':
{ {
var addr = nextGpArg(); var addr = argumentSource.NextGpArg();
if (addr != 0) if (addr != 0)
{ {
_ = TryWriteInt32(ctx, addr, sb.Length); _ = TryWriteInt32(ctx, addr, sb.Length);
@@ -3699,7 +3682,46 @@ public static class KernelMemoryCompatExports
return leftAlign ? str + padding : padding + str; 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 GpSaveAreaLimit = 48;
private const uint FpSaveAreaLimit = 176; private const uint FpSaveAreaLimit = 176;
@@ -4297,9 +4319,32 @@ public static class KernelMemoryCompatExports
} }
const int maxChunkSize = 4096; const int maxChunkSize = 4096;
const int inlineChunkSize = 256;
Span<byte> 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<byte>(Math.Min(maxChunkSize, limit)); var chunk = GC.AllocateUninitializedArray<byte>(Math.Min(maxChunkSize, limit));
var writer = new ArrayBufferWriter<byte>(Math.Min(limit, 256)); var writer = new ArrayBufferWriter<byte>(Math.Min(limit, 256));
ulong offset = 0; if (offset != 0)
{
firstSpan.CopyTo(writer.GetSpan(firstReadLength));
writer.Advance(firstReadLength);
}
while (offset < (ulong)limit) while (offset < (ulong)limit)
{ {
var current = address + offset; var current = address + offset;