Skip to content

powerpc-apple-darwin: fix the ABI - #421

Open
barracuda156 wants to merge 4 commits into
thepowersgang:masterfrom
barracuda156:powerpc-fix-abi
Open

powerpc-apple-darwin: fix the ABI#421
barracuda156 wants to merge 4 commits into
thepowersgang:masterfrom
barracuda156:powerpc-fix-abi

Conversation

@barracuda156

Copy link
Copy Markdown
Contributor

A follow-up to #418 that actually matches the ABI correctly (no need to disable asserts anymore) and fixes a few minor bugs.

The mrustc build is verified on the real hardware and confirmed to work.
rustc bootstrap is still broken, as it was, nothing changes here, but it is broken on unrelated issues (basically on LLVM).

P. S. We still need to address unwinding issue to fix the build on 10.5 (which has no libunwind in the system) and fix libs for ppc64 (broken currently).
On 10.6 the working native mrustc can be built right now from https://github.com/macos-powerpc/powerpc-ports/blob/c3731ad2d6bb8569e35f8446c134bcadeea02394/lang/mrustc/Portfile and verified via building several Rust ports, see macos-powerpc/powerpc-ports#62 for details.

@danifunker FYI

barracuda156 and others added 4 commits August 8, 2026 19:25
`ents[0].align` got tag_size and `ents[0].size` got tag_align. The two
are identical for every power-of-two integer tag on most targets, so
this stayed invisible, but it is wrong wherever a tag's size and
alignment differ - e.g. a repr(u64) enum with data on i686
(size 8, align 4).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0179wmF1vKBsFua4Wh36WFc5
Signed-off-by: Sergey Fedorov <vital.had@gmail.com>
Replaces the attribute-pinning approach: instead of laying types out by
mrustc's own rules and forcing the C compiler to match via
__attribute__((aligned))/packed, replicate GCC's layout algorithm
(ADJUST_FIELD_ALIGN + darwin_rs6000_special_round_type_align) so the
plain emitted C is already identical - and so repr(C) types keep
matching system headers, which GCC compiles *without* those attributes.

Differences from the pinned model:
- The cap is gated on Darwin (Target_IsDarwinPPC32: arch AND os). Other
  32-bit PowerPC OSes don't use the power alignment rule (SysV keeps
  long long/double 8-aligned in structs, matching rustc's i64:64
  datalayout for powerpc-unknown-linux-gnu), and ppc64 Darwin is
  natural-aligned.
- Unions follow GCC: members are capped like struct fields, then the
  union's alignment is raised to its *first* member's natural alignment
  (darwin_rs6000_special_round_type_align covers unions too). Pinning
  every union with an explicit aligned attribute diverges from the
  platform ABI: GCC gives union{uint32_t;uint64_t} align 4, the pinned
  version align 8, so any repr(C) struct embedding such a union no
  longer interoperates with C.
- Every field is capped (including the first); the record's total
  alignment is then raised to the natural alignment of its "innermost
  first field" (descend the first *emitted* member of each aggregate,
  stripping arrays). Equivalent to a first-field exemption for plain
  structs, and also correct when the first member is a union or its
  alignment comes from a kept 16-byte item.
- TYPE_USER_ALIGN is modelled against the emitted C
  (darwin_ppc32_type_has_c_user_align mirrors codegen's
  has_manual_align conditions, including alignment carried by ZST
  fields, which are not emitted as C fields and therefore cannot
  propagate user-alignment in GCC's eyes).
- Enum reprs get a final fixup computing size/align the way the C
  compiler sees the emitted struct e_X - variant structs are capped
  when they become DATA union members. This subsumes the
  final-variant-layout fix for niche enums and covers tagged enums too.
- repr(align(N)) structs always emit an explicit aligned attribute: C
  cannot infer a user alignment that happens to equal the largest field
  alignment, and on Darwin ppc32 the derived alignment can be lower
  than repr->align.

Verified against GCC 16 (iains/gcc-16-branch, the toolchain actually
used on PowerPC Macs) with a hand-checked layout matrix; the sizeof
static asserts stay enabled, and a full libstd builds and runs on
10.6/ppc with debug assertions on - no MINICARGO_NO_DEBUG_ASSERTIONS
needed.

Keeps the empty target_env and the libatomic linkage from the previous
implementation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0179wmF1vKBsFua4Wh36WFc5
Signed-off-by: Sergey Fedorov <vital.had@gmail.com>
std's atomics module documents ("Portability"): "All atomic types in
this module are guaranteed to be lock-free if they're available. This
means they don't internally acquire a global mutex." 32-bit PowerPC has
no 8-byte atomic instructions, so libatomic implements them with a
lock - exposing AtomicU64 on top of that breaks the documented
contract, and lock-based atomics are not address-free (unusable across
shared memory and in signal handlers).

rustc agrees: every 32-bit powerpc target sets max_atomic_width 32, and
rustc ships libstd for powerpc-unknown-linux-gnu built exactly that
way - the AtomicU64 uses in std are cfg(target_has_atomic)-gated.

The one exception in 1.74 libstd is the mach_timebase_info cache
(sys/unix/time.rs), which packs numer/denom into a single AtomicU64
with no fallback - upstream rustc could assume 64-bit atomics on every
Apple target it supported. Patch it to a pair of AtomicU32s: denom == 0
is still the uninitialized sentinel, and the numer store is published
by the Release store of denom (racing initializers write identical
values). With that, libstd 1.74 builds without AtomicU64.

Drops the now-unneeded -latomic from the powerpc-apple-darwin spec.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0179wmF1vKBsFua4Wh36WFc5
Signed-off-by: Sergey Fedorov <vital.had@gmail.com>
libdispatch exists on Mac OS X 10.6, and 10.6 runs on PowerPC -
"powerpc-apple-darwin is by definition 10.4/10.5" doesn't hold, and a
10.6/ppc build wants the dispatch-based Darwin parker.

Availability is a property of the deployment target, not the
architecture, and std has no cfg for the OS version - so make the
fallback opt-in: the std patch keys on `--cfg no_libdispatch`, and the
stable-1.74.0-macos-powerpc build_std override sets it (that override
targets 10.4/10.5). A 10.6+ build uses an override without the cfg and
keeps the dispatch parker.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0179wmF1vKBsFua4Wh36WFc5
Signed-off-by: Sergey Fedorov <vital.had@gmail.com>
Comment thread src/trans/target.cpp
Comment on lines -993 to -1006

bool make_field_ent(const Span& sp, const StaticTraitResolve& resolve, unsigned idx, ::HIR::TypeRef ty, Ent& out)
{
size_t size, align;
if( !Target_GetSizeAndAlignOf(sp, resolve, ty, size, align) )
{
DEBUG("Can't get size/align of " << ty);
return false;
}
out = Ent { idx, size, align, HIR::TypeRef(), false };
out.user_align = Target_TypeHasUserAlignment(sp, resolve, ty);
out.ty = mv$(ty);
return true;
}

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Why was this helper (and its use) removed?

Even if user_align is now gone, the rest seemed pretty useful

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It was added in powerpc-darwin-specific commit (which does not fully match the ABI), and not used in updated code (or outside of ppc). I do not insist on removing anything, of course; if we make a rewrite that still works correctly, it will be perfectly fine.

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