mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-22 10:56:20 +08:00
[kernel] speed up printf
This commit is contained in:
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user