[cpu] Implement SysV variadic float ABI (xmm0-7 capture, float returns, printf %f) (#59)

* [cpu] Implement SysV variadic float ABI (xmm0-7 capture, float returns, printf %f)

The import trampoline spilled only xmm0 and never reloaded a return xmm0. The
guest uses the System V AMD64 ABI: variadic float args pass in xmm0..xmm7 and
float/double returns come back in xmm0. As a result variadic float args past
the first were unavailable to HLE handlers, float returns never reached the
guest, and direct printf read %f/%e/%g from GP registers instead of XMM,
printing garbage and desynchronizing every following argument.

- Trampoline: spill xmm0..xmm7 into a 0x80-byte save area below the GP argpack
  (r12 stays at the argpack base) and reload the return xmm0 in the epilogue.
- Gateway: read xmm0..7 from the save area into CpuContext and write the
  handler's xmm0 back. XMM is caller-saved in SysV, so restoring xmm0 on return
  is safe for non-float imports too.
- RegisterPrintfArgumentSource: read float args from xmm0..7 with independent
  GP/FP counters and a shared stack-overflow cursor.

Every emitted byte was decoded; a unit test confirms float args read xmm0..7
(not GP) and interleaved "%d %f %d %f" stays synchronized. Build 0/0.

* [cpu] Document the scalar-only leaf-import constraint at its registration site

- IsLeafImport: spell out the no-XMM-args / no-XMM-return invariant the fast
  path relies on and what breaks if it is violated; record the 2026-07-11 audit.
- Name every previously uncommented NID in the leaf list (mutex lock/unlock,
  usleep, the Ampr/Apr command-buffer block, the unknown AGC packet NID).
- IsNoBlockLeafImport: document that it is a sub-filter of IsLeafImport and
  that its five extra entries currently take the full gateway path; fix the
  mislabeled K-jXhbt2gn4 comment (pthread_mutex_trylock, not
  scePthreadMutexTrylock, which is upoVrzMHFeE).
- Point the DispatchImport call-site note at the audited list.

Comment-only change: the comment-stripped diff is empty and the solution
builds with 0 warnings / 0 errors.
This commit is contained in:
PandaCatz
2026-07-11 09:41:25 -05:00
committed by GitHub
parent ef74680167
commit f43f7cde9c
3 changed files with 136 additions and 58 deletions
@@ -3885,33 +3885,59 @@ public static class KernelMemoryCompatExports
private struct RegisterPrintfArgumentSource : IPrintfArgumentSource
{
// SysV AMD64: variadic integer/pointer args use the GP registers (rdi..r9, up to
// 6) and variadic float/double args use xmm0..xmm7 (8) — INDEPENDENT counters.
// Args beyond the registers spill to the stack in source order, so GP-overflow
// and FP-overflow share one stack cursor.
private readonly CpuContext _ctx;
private int _gpIndex;
private int _fpIndex;
private int _stackIndex;
public RegisterPrintfArgumentSource(CpuContext ctx, int gpIndex)
{
_ctx = ctx;
_gpIndex = gpIndex;
_fpIndex = 0;
_stackIndex = 0;
}
public ulong NextGpArg()
{
var index = _gpIndex++;
return index switch
var index = _gpIndex;
if (index < 6)
{
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)
};
_gpIndex++;
return index switch
{
0 => _ctx[CpuRegister.Rdi],
1 => _ctx[CpuRegister.Rsi],
2 => _ctx[CpuRegister.Rdx],
3 => _ctx[CpuRegister.Rcx],
4 => _ctx[CpuRegister.R8],
_ => _ctx[CpuRegister.R9],
};
}
return ReadStackArg(_ctx, (ulong)(_stackIndex++) * 8);
}
public double NextFloatArg()
{
return BitConverter.Int64BitsToDouble(unchecked((long)NextGpArg()));
// Float/double variadic args live in xmm0..xmm7 (low 64 bits), NOT the GP
// registers. Reading them from GP prints garbage and desynchronizes every
// subsequent argument.
ulong bits;
if (_fpIndex < 8)
{
_ctx.GetXmmRegister(_fpIndex++, out bits, out _);
}
else
{
bits = ReadStackArg(_ctx, (ulong)(_stackIndex++) * 8);
}
return BitConverter.Int64BitsToDouble(unchecked((long)bits));
}
}