Skip to content

Align UnionExp storage to real_t - #23966

Merged
thewilsonator merged 1 commit into
dlang:masterfrom
TurkeyMan:fix-unionexp-alignment
Oct 5, 2026
Merged

thewilsonator merged 1 commit into
dlang:masterfrom
TurkeyMan:fix-unionexp-alignment

Conversation

@TurkeyMan

Copy link
Copy Markdown
Contributor

UnionExp emplaces expression class instances into _AnonStruct_u, which is a union of char[classInstanceSize] buffers with a hand-picked align(8). On x86_64 posix targets real_t is the x87 80-bit type with 16-byte alignment, so RealExp and ComplexExp instances (and ComplexExp.value, a complex_t) end up at addresses that are only 8-aligned.

This is latent today because the copies happen to be emitted with alignment 1. It surfaced with ldc-developers/ldc#5279, which makes LDC carry the type alignment on struct copies: the self-hosted compiler then copies complex_t with movaps into an 8-aligned UnionExp and segfaults in constfold.Cast during CTFE.

The union now aligns to max(8, real_t.alignof). real_t is the only member type of these classes wider than a pointer. __traits(classInstanceAlignment) would state this more directly, but it is unavailable on the oldest supported host compilers. No change where real_t is at most 8-aligned (e.g. Windows).

UnionExp emplaces RealExp and ComplexExp into a union declared align(8),
but on x86_64 posix targets real_t is the x87 80-bit type with 16-byte
alignment, so those instances (and the complex_t field of ComplexExp)
were placed at addresses only 8-aligned. A compiler that copies the
complex_t field with its natural alignment emits movaps and faults.
@TurkeyMan

Copy link
Copy Markdown
Contributor Author

This surfaced when fixing a bug in LDC causing alignment to be lost and revert to align(1), causing ridiculous codegen on platforms that can't do unaligned load/store.

@dkorpel dkorpel left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why is the ternary needed?

private:
// Ensure that the union is suitably aligned.
align(8) union _AnonStruct_u
align(real_t.alignof > 8 ? real_t.alignof : 8) union _AnonStruct_u

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
align(real_t.alignof > 8 ? real_t.alignof : 8) union _AnonStruct_u
align(real_t.alignof) union _AnonStruct_u

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

real_t.alignof is 2 for Windows (at least for dmd).

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.

Yeah I guess... but it's not certain what real actually even means in D.

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.

real_t.alignof is 2 for Windows (at least for dmd).

Wow. Weird choices!

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

real is for programmer enrichment. We needed a new endless source of bugs to keep them entertained after having eliminated variable-width basic integer types, so we added a poorly-specified, totally non-portable, variable-width floating point type. And also made it nearly impossible to avoid by mandating its use in CTFE and math functions (though sadly this latter point has been eroded, because "less bugs" is viewed as desirable. Ridiculous!). Oh, and it's always 80-bit in CTFE, because portability. Except when it's not, because we haven't implemented that yet.

So to answer your question, real means fun!

@rainers rainers left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I stumbled upon this, too, when adding alignment info to some allocations, but thought the impact would be low.

@TurkeyMan

Copy link
Copy Markdown
Contributor Author

That last test seems to be pending for days, but it's marked "required"... do we just ignore it?

@thewilsonator
thewilsonator merged commit b0b7c89 into dlang:master Oct 5, 2026
42 checks passed
@TurkeyMan
TurkeyMan deleted the fix-unionexp-alignment branch October 5, 2026 01:49
TurkeyMan added a commit to TurkeyMan/ldc that referenced this pull request Oct 6, 2026
UnionExp emplaces RealExp and ComplexExp into a union declared align(8),
but on x86_64 posix targets real_t is the x87 80-bit type with 16-byte
alignment, so those instances (and the complex_t field of ComplexExp)
were placed at addresses only 8-aligned.

Upstream: dlang/dmd#23966
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.

5 participants