Skip to content

Initialize guest floating-point state explicitly - #137

Merged
encounter merged 2 commits into
decompals:mainfrom
N7Alpha:fix/mwcc-codegen-divergence
Jul 12, 2026
Merged

Initialize guest floating-point state explicitly#137
encounter merged 2 commits into
decompals:mainfrom
N7Alpha:fix/mwcc-codegen-divergence

Conversation

@N7Alpha

@N7Alpha N7Alpha commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

It looks like you need to set a flag in a control register at least on my M4 MacBook Air. My only validation is this fixed an issue in a vibe code decomp I had for 7 functions. Late in the decomp I wanted to switch to this project from Wine because I was losing my mind with Wine, but I lost 7 matched functions. GPT 5.6 root-caused the fix and matched all 7 wibo with no regressions on >3200 functions.

GPT 5.6

Bug

wibo inherited the host thread's floating-point environment when entering a Win32 guest. On macOS under Rosetta, the x87 control word was 0x037f, which selects 64-bit extended precision. Win32 processes and threads start with 0x027f, which selects 53-bit precision.

This changed an internal optimizer comparison in Metrowerks mwccarm.exe. The compiler consequently emitted a semantically equivalent switch comparison tree in a different order, preventing byte-identical reproduction of ARM objects that match the original ROM under Wine.

Root cause

The x87 precision-control bit was the causal hidden-state difference. Immediately before guest entry, wibo reported 0x037f. Loading only 0x027f—without changing heap placement, allocation contents, or MXCSR—made the canonical Wine and wibo objects byte-identical.

Fix

Initialize the floating-point environment whenever a TIB is installed for a guest thread:

  • reset the x87 unit;
  • load the Win32 x87 control word 0x027f;
  • load the default MXCSR control state 0x1f80.

Doing this in per-thread setup covers the initial executable, DLL/TLS callbacks, and threads created through CreateThread.

A new fixture captures floating-point state from a process-attach TLS callback and a created thread. It checks exact x87 control state and the MXCSR control bits while allowing exception status flags that may be raised during loader work.

@encounter
encounter force-pushed the fix/mwcc-codegen-divergence branch from dd835d9 to 61cc9c1 Compare July 12, 2026 23:22
@encounter encounter changed the title [MacOS-Rosetta] Initialize guest floating-point state Initialize guest floating-point state explicitly Jul 12, 2026
@encounter

Copy link
Copy Markdown
Member

Thanks! I verified this as a bug and rewrote it to match the existing codebase style.

@encounter
encounter merged commit e8f4795 into decompals:main Jul 12, 2026
12 checks passed
freeqaz added a commit to freeqaz/wibo that referenced this pull request Aug 5, 2026
This fork sat at 1.0.1-23 while upstream shipped 1.1.0 and 1.2.0, both of
which are explicitly about the toolchain we run: per-ABI msvcrt embedding,
"improve kernel32 compatibility for MSVC tools" (decompals#124), a module-TLS-init
race fix, and a per-thread FLS / Windows-faithful CRITICAL_SECTION fix whose
commit message names the symptom we have hit ("intermittent MSVC c2.dll
deadlock", decompals#126). 1.0.1 itself was cut because msvc_ppc_16.00.11886.00 -- our
exact compiler -- segfaulted. Staying behind on a fork whose upstream is
working on our workload is how a fork rots.

Target is upstream/main tip (e8f4795), not the 1.2.0 tag, for two reasons:
decompals#134 adds GetTempFileNameW, which is one of the local patches this fork
carried, so taking the tip lets us DELETE ours; and decompals#137 initializes guest
floating-point state explicitly, which is exactly the kind of thing that can
move a compiler's constant folding and is therefore better under test than
deferred.

Seven files conflicted. Resolutions, and which side won:

  CMakeLists.txt          BOTH -- two fixture registrations that landed on the
                          same line (ours test_mspdb, theirs test_env_strings).
  kernel32/minwinbase.h   THEIRS. Upstream added WIN32_FILE_ATTRIBUTE_DATA
                          independently, with both the P and LP aliases. Our
                          copy deleted as redundant.
  kernel32/fileapi.h      BOTH, deduplicated. Kept our GetTempPathW and
                          FindFirstFileExW (upstream has neither); dropped our
                          now-duplicate GetTempFileNameW and GetFileAttributesEx
                          declarations in favour of upstream's.
  kernel32/fileapi.cpp    THEIRS for GetFileAttributesExA/W -- upstream's has the
                          license.dat special case and correct ERROR_PATH_NOT_FOUND
                          mapping, ours had neither. Deleted our duplicate
                          GetTempFileNameW definition; upstream's (decompals#134) accepts a
                          null prefix, as Windows does, and ours rejected it.
  kernel32/memoryapi.cpp  THEIRS for both hunks. Upstream implemented the same two
                          fixes this fork carried -- growing the backing file for a
                          larger mapping, and FILE_MAP_ALL_ACCESS not meaning
                          copy-on-write -- and its versions are strictly better
                          (a protectAllowsFileGrowth guard, a size_t overflow check,
                          and one fileMapAccessFromDesiredAccess helper shared with
                          desiredAccessToProtect). Two more redundant local patches
                          deleted.
  kernel32/winbase.cpp    THEIRS for lstrcpynA (decompals#124 -- Windows-faithful null
                          handling, and it has a fixture, test/test_lstrcpyn.c).
                          Ours deleted. lstrcpyA/W and lstrcpynW stay -- upstream has
                          no such functions -- but lstrcpynW's null/count handling is
                          rewritten as a transcription of upstream's A so the pair
                          cannot drift.
  src/modules.cpp         BOTH: upstream's builtin table (it adds lib_shlwapi) plus
                          our WIBO_HAS_MSPDB entry.
  src/files.cpp           OURS, plus one adoption. pathFromWindows here is a fork
                          rewrite -- WIBO_PATH_MAP, WIBO_DRIVE_x, the WIBO_FS_CACHE
                          exists/dirent/case caches, /showIncludes rewriting -- and
                          all of it is load-bearing for dc3-decomp, so upstream's much
                          simpler version loses. Upstream's stripTrailingDots (decompals#118,
                          the NMAKE "nm12345." convention) is genuinely new and is
                          taken, called one step earlier than upstream calls it so
                          that the WIBO_DRIVE_x branch sees it too.

Four local patches are now gone as redundant: GetTempFileNameW, the file-mapping
growth, the FILE_MAP_ALL_ACCESS fix, and lstrcpynA. Everything else this fork
carries survived: sigsafe.cpp, the mimalloc foreign-free fix, the 8 MiB guest
stack, mspdb, the X360 linker support, wsprintfW, and the files.cpp caches.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants