dmd icon indicating copy to clipboard operation
dmd copied to clipboard

Implement C-style atomic alignment rules for shared types

Open ibuclaw opened this issue 1 year ago • 10 comments

@kinke this is trivially done. As it's target-specific, I'm not keen on adding tests for it.

struct S
{
    size_t[2] values;
}

struct T
{
    shared(S) field;
}
pragma(msg, S.alignof);           // 8u
pragma(msg, shared(S).alignof);   // 16u

In C, _Atomic overrides any explicit alignment too, however I don't think we should follow that example, so align(8) struct S still has an alignment of 8 when it is qualified shared.

ibuclaw avatar Jan 21 '24 22:01 ibuclaw

Thanks for your pull request, @ibuclaw!

Bugzilla references

Your PR doesn't reference any Bugzilla issue.

If your PR contains non-trivial changes, please reference a Bugzilla issue or create a manual changelog.

Testing this PR locally

If you don't have a local development environment setup, you can use Digger to test this PR:

dub run digger -- build "stable + dmd#16072"

dlang-bot avatar Jan 21 '24 22:01 dlang-bot

What about basic types like integers?

rikkimax avatar Jan 21 '24 22:01 rikkimax

What about basic types like integers?

They have the same alignment as their nonatomic equivalent. Any potential deviations don't apply to targets DMD cares about (ie. SystemZ) - GDC and LDC can deal with this in their own vendor implementation of Target::alignsize.

ibuclaw avatar Jan 21 '24 22:01 ibuclaw

What about basic types like integers?

They have the same alignment as their nonatomic equivalent. Any potential deviations don't apply to targets DMD cares about (ie. SystemZ) - GDC and LDC can deal with this in their own vendor implementation of Target::alignsize.

Saying that, you've just reminded me that I didn't consider i386.

https://cpp.godbolt.org/z/Trq38qdPn

ibuclaw avatar Jan 21 '24 22:01 ibuclaw

What about basic types like integers?

They have the same alignment as their nonatomic equivalent. Any potential deviations don't apply to targets DMD cares about (ie. SystemZ) - GDC and LDC can deal with this in their own vendor implementation of Target::alignsize.

Saying that, you've just reminded me that I didn't consider i386.

https://cpp.godbolt.org/z/Trq38qdPn

Right, this isn't about the type.

Atomics does not care about the type.

The CPU cares about the size and the pointer address alignment.

This is why I kinda think the storage class for atomic is well worth it. We can encode the restriction surrounding size and pointer to make it work appropriately for the target.

rikkimax avatar Jan 21 '24 22:01 rikkimax

What about basic types like integers? The CPU cares about the size and the pointer address alignment.

So then the question is not just basic types, but also all derived types too, all of which are usually pointer aligned, but are sized 2 * size_t.sizeof.

ibuclaw avatar Jan 21 '24 22:01 ibuclaw

What about basic types like integers? The CPU cares about the size and the pointer address alignment.

So then the question is not just basic types, but also all derived types too, all of which are usually pointer aligned, but are sized 2 * size_t.sizeof.

I already covered that interestingly enough.

atomic(size_t[2])*

The pointer is not atomic, but the type it points to is. It would not be transitive like shared is.

This also covers another question, regarding validation. Maximum sizes of the type is just as important for a CT error as alignment is (which should be forced based upon target).

rikkimax avatar Jan 21 '24 23:01 rikkimax

This also covers another question, regarding validation. Maximum sizes of the type is just as important for a CT error as alignment is (which should be forced based upon target).

Well, anything that can't be done in CPU is just becomes a soft implementation.

ibuclaw avatar Jan 21 '24 23:01 ibuclaw

This also covers another question, regarding validation. Maximum sizes of the type is just as important for a CT error as alignment is (which should be forced based upon target).

Well, anything that can't be done in CPU is just becomes a soft implementation.

As far as I'm aware only GDC supports that. But that is ok, it can be compiler-specific (since it is already target-specific). Unless more work is done for the compilers as fallback.

However, I'm more inclined to think that it is better to error, so that people can write their own fallbacks. So that such a decision is informed, rather than an uninformed surprise.

rikkimax avatar Jan 21 '24 23:01 rikkimax

This won't work due to the transitiveness of shared. Suppose we have a struct S { int dummy; void[] slice; } on a 64-bit target, with S.slice.offsetof == 8 and S.alignof == 8. Now when declaring a shared S global var, the type of global.slice is shared(void[]), but its alignment is 8, not 16 as a standalone shared void[] var.

I'm not sure we need to tackle this at the language level, mainly because I think the problem is almost entirely restricted to aggregates with double-word size. Aligning those manually or using some Atomic!T library wrapper might not be too bad.

kinke avatar Jan 22 '24 11:01 kinke