Skip to content

xm-darwin: force 32-bit HOST_WIDE_INT to match Linux i386 reference#35

Merged
Xeeynamo merged 4 commits into
decompals:masterfrom
andreadellacorte:xm-darwin-host-wide-int
May 13, 2026
Merged

xm-darwin: force 32-bit HOST_WIDE_INT to match Linux i386 reference#35
Xeeynamo merged 4 commits into
decompals:masterfrom
andreadellacorte:xm-darwin-host-wide-int

Conversation

@andreadellacorte
Copy link
Copy Markdown
Contributor

Problem

The macOS-native cc1 binaries (added in #34) produce non-bit-identical machine code vs the Linux i386 reference build for the same C input. Concrete repro: when used as the cc1 binary for rood-reverse (a PSX decompilation), 8 of the project's PRGs fail `make check`. Same C source, same maspsx/binutils chain, same preprocessor — only the cc1 binary differs.

For example, MENU8.PRG/88.o is 44 bytes shorter when compiled by the macOS cc1 vs the Linux cc1, and that drift cascades across sections downstream.

Root cause

`machmode.h:49-55` derives `HOST_WIDE_INT` from `long`:

```c
#if HOST_BITS_PER_LONG > HOST_BITS_PER_INT
#define HOST_BITS_PER_WIDE_INT HOST_BITS_PER_LONG
#define HOST_WIDE_INT long
#else
#define HOST_BITS_PER_WIDE_INT HOST_BITS_PER_INT
#define HOST_WIDE_INT int
#endif
```

  • Linux build: `--host=i386-pc-linux` + `CFLAGS="-m32 -static"` → LONG=32, INT=32 → `HOST_WIDE_INT = int` (32-bit)
  • macOS build: LP64 → LONG=64, INT=32 → `HOST_WIDE_INT = long` (64-bit)

cc1's internal integer-constant arithmetic (constant folding, RTL constants, shifts, sign-extension) all operate on `HOST_WIDE_INT`. With 64-bit width, intermediate values keep precision the source never asked for, propagating into different code generation in non-trivial ways.

Fix

Override `HOST_WIDE_INT` to 32-bit in `patches/xm-darwin.h`. cc1 itself remains a 64-bit Mach-O binary; only target-constant width is constrained. Host pointer / `size_t` semantics are unaffected.

Test

Adds `tests/host_wide_int.c` — compiles `return 1u << 31;` and greps for `# 0x80000000$` in the output. cc1 prints constants at `HOST_WIDE_INT` width:

  • 32-bit (correct): `li $2,-2147483648 # 0x80000000`
  • 64-bit (broken): `li $2,-2147483648 # 0xffffffff80000000`

The grep catches the divergence without needing bit-exact .o comparisons. Wired into all three `*-macos.sh` scripts and the corresponding Linux Dockerfiles for parity.

Validation

With this patch applied:

  • All three macOS cc1 binaries (2.7.2-psx, 2.7.2-cdk, 2.8.1-psx) pass the new test
  • Existing `little_endian` / `section_attribute` / `-msoft-float` / etc. tests still pass
  • rood-reverse `make check` goes from 8 PRG mismatches to ✔ all match on aarch64-darwin

The macOS build of cc1 produces non-bit-identical machine code vs the
Linux i386 reference build for the same C input. Root cause: machmode.h
derives HOST_WIDE_INT from `long`, which is 64-bit on darwin LP64 and
32-bit on i386-host Linux (where the reference is built with -m32):

    #if HOST_BITS_PER_LONG > HOST_BITS_PER_INT
    #define HOST_WIDE_INT long       /* 64-bit on darwin */
    #else
    #define HOST_WIDE_INT int        /* 32-bit on i386 */
    #endif

cc1's internal integer-constant arithmetic (constant folding, RTL
constants, shifts, sign-extension) all operate on HOST_WIDE_INT. With
64-bit width, intermediate values keep precision the source never asked
for, propagating into different code generation in non-trivial ways.

Override HOST_WIDE_INT to 32-bit in xm-darwin.h. cc1 itself remains a
64-bit Mach-O binary; only target-constant width is constrained, mirroring
the i386-host Linux build. Host pointer/size_t semantics are unaffected.

Add a regression test (tests/host_wide_int.c) that compiles a function
returning `1u << 31` and greps for `# 0x80000000$` in the output. cc1
prints constants at HOST_WIDE_INT width, so a 64-bit-host build emits
`# 0xffffffff80000000` (sign-extended); the test catches the divergence
without needing to compare bit-exact machine code. Wired into all three
macos.sh build scripts and the corresponding Linux Dockerfiles for
parity.

Concrete validation: rood-reverse (https://github.com/ser-pounce/rood-reverse)
goes from 8 PRG mismatches in `make check` to all-match with this patch.
The size of the codegen drift before the fix was substantial — e.g.
MENU8.PRG/88.o was 44 bytes shorter than the Linux reference, which
cascaded into VMA shifts across multiple downstream sections.
Copy link
Copy Markdown
Collaborator

@Xeeynamo Xeeynamo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good so far. I only ask to remove the comment that have been added. A git blame on file and line is enough to go back to the PR and find the intention of a change.

Comment thread gcc-2.7.2-cdk-macos.sh Outdated
Comment thread gcc-2.7.2-cdk.Dockerfile Outdated
andreadellacorte and others added 3 commits May 13, 2026 11:14
Co-authored-by: Luciano Ciccariello <Xeeynamo@users.noreply.github.com>
Co-authored-by: Luciano Ciccariello <Xeeynamo@users.noreply.github.com>
@Xeeynamo Xeeynamo merged commit b74211c into decompals:master May 13, 2026
25 checks passed
@andreadellacorte andreadellacorte deleted the xm-darwin-host-wide-int branch May 13, 2026 12:55
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