sots-engine/src/shim/shim.cfg.fpu127f
alex d462513f84 shim: force and verify the x87 control word around the turn gate
New fpu_force module (4 register-transparent asm stubs, same pattern as the M0
Initialize hook, so the [unverified] prototypes of the turn-gate functions are
never relied on) plus two shim.cfg keys:

  fpu.force=<cw>|off      fldcw at StrategyClient::EndTurn and
                          StrategyServer::BeginProcessTurn, and nowhere else
  fpu.sample_ticks=on|off per-frame sampler, logs only when the word CHANGES

Forcing is deliberately one write per turn: re-forcing inside the pipeline would
guarantee the value is present without proving it ever held, which is the exact
false negative this experiment has to avoid. Verification is kept separate --
StrategyServer::ProcessTurn is hooked sample-only, and its reading plus the
existing per-hook fpu_cw fields (38 samples per turn across phases 4, 6 and 8)
are what establish that the setting lasted the whole turn.

Six shim.cfg variants, identical apart from the fpu.force line, and docs.

Used to settle STATE_CHECKSUM.md 3.5: 53-bit and 64-bit x87 produce byte-
identical turn results, so an x64/SSE port has no double-rounding budget to
preserve; 24-bit and round-up each move exactly one thing. Findings and evidence
live in the notes repo (findings/subsystems/fpu-precision-sensitivity.md).

clean_room_check.sh OK; host ctest 32/32.
2026-09-08 05:09:16 -04:00

31 lines
1.6 KiB
Text

# Lane F, x87 precision-sensitivity experiment (verify/results/fpu-cw in the notes repo).
# 0x127f = 53-bit significand, round-to-nearest. THE CONTROL: exactly what the game sets itself, so this run must reproduce the stock oracle hashes.
#
# Every shim.cfg.fpu* file is identical except the fpu.force line: the hook set, the trace path
# and the flush policy are held fixed so the control word is the only variable across runs.
#
# x87 control-word fields: bits 0-5 exception masks, bits 8-9 precision control
# (00 = 24-bit / 10 = 53-bit / 11 = 64-bit significand), bits 10-11 rounding control
# (00 nearest / 01 down / 10 up / 11 truncate), bit 12 infinity control (ignored since the 387).
hooks=trace
hook.Shim::SelfTest::Fill=off
hook.Mars::GlobalConsts::LoadFile=off
hook.Game::WeaponDictionary::Init=off
hook.Game::SectionDictionary::SectionDictionary=off
hook.Game::StrategyServer::ProcessFleetMovement=off
hook.Game::TechTree::ProcessResearch=trace
hook.Game::ServerPlayer::ComputeBudget=trace
hook.Game::ServerPlayer::OnTechResearched=trace
hook.Game::ServerSystem::ProcessTurn=trace
hook.Game::StrategyServer::MoveFleet=trace
trace.path=C:\SOTS\shim.trace.jsonl
trace.inline_max=256
trace.flush=always
# Forced once at the turn gate (StrategyClient::EndTurn and StrategyServer::BeginProcessTurn),
# never re-forced inside the pipeline -- re-forcing would guarantee the value is present without
# proving it ever held.
fpu.force=0x127f
# Per-tick sampler: logs to shim.log whenever the word MOVES, so the log carries a timeline of
# the value rather than a single claim made at a single instant.
fpu.sample_ticks=on