gen: default GIAC_TYPE_ON_8BITS=1 to fix cross-GCC ABI mismatch#1
Merged
Conversation
The historical bitfield layout of class gen:
unsigned char type:5;
unsigned char type_unused:3;
signed char subtype;
unsigned short reserved;
is laid out and ACCESSED differently across GCC versions. GCC fuses
adjacent bitfield writes (g.type = ...; g.subtype = ...;) into a
single wider store using version-dependent bit placements: GCC 8
and GCC 10 produce instruction sequences that disagree on which
bits of the resulting word hold `type`.
This isn't theoretical. In the libgiac_julia / Giac.jl stack on
Windows:
- GIAC_jll is built with BinaryBuilder GCC 8
- libgiac_julia_jll (the CxxWrap wrapper) is GCC 10
- When libgiac writes a gen tagged _REAL (3) and the wrapper reads
it, the wrapper sees _DOUBLE_ (1) instead.
A C++ probe (s-celles/libgiac-julia-wrapper#5)
ran on Linux/macOS/Windows in both modes confirmed: with the
bitfield, GCC's optimizer fuses writes; the byte layout is correct
ONLY when libgiac and consumer use the same GCC. With
GIAC_TYPE_ON_8BITS, type is a plain `unsigned char` at offset 0 —
no bitfield, no fusion, every compiler emits the same simple byte
store/load. The ABI becomes compiler-invariant.
Cost: 3 mantissa bits on inline doubles encoded in a gen (48 → 45).
sizeof(gen) is unchanged at 64 bits. Per giac's author this is the
historical default and the safer cross-compiler path. Override by
removing this block in dispatch.h if you need the legacy layout.
Fixes: s-celles/Giac.jl#22
Refs: s-celles/libgiac-julia-wrapper#5
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Defaults the `type` field of `class gen` to a plain 8-bit `unsigned char` rather than the historical `unsigned char type:5; unsigned char type_unused:3;` bitfield, by adding a one-block `#ifndef GIAC_TYPE_ON_8BITS / #define GIAC_TYPE_ON_8BITS 1 / #endif` to `src/dispatch.h`.
Why
The bitfield layout is access-fused differently by different GCC versions. GCC's optimizer combines adjacent bitfield writes (`g.type = ...; g.subtype = ...;`) into a single wider store using version-dependent bit placements. GCC 8 and GCC 10 produce instruction sequences that disagree on which bits of the resulting word hold `type`.
This is a real, user-visible bug in the libgiac_julia / Giac.jl stack on Windows:
A multi-platform C++ probe (details) compiled the relevant snippets on Linux GCC 12, macOS clang, and Windows MSYS2 GCC 15.2 in both modes:
Same-compiler builds work correctly in both modes. The bug only appears with cross-compiler dynamic loading — exactly the production scenario.
With `GIAC_TYPE_ON_8BITS` the `type` field is a plain byte at offset 0; both GCC 8 and GCC 10 emit a trivial `mov BYTE PTR [g], type`. No fusion, no version-dependent packing. The ABI becomes compiler-invariant.
Cost
Per Bernard Parisse's recommendation (which is what motivated this PR):
Effects on the giac codebase
Downstream propagation
After this lands on `dev`:
Test plan