[cfe-dev] [RFC] Use preferred alignment instead of ABI alignment for complete object when applicable
James Y Knight via cfe-dev
cfe-dev at lists.llvm.org
Wed Aug 19 19:32:23 PDT 2020
On Wed, Aug 19, 2020 at 5:21 PM Xiangling Liao <xiangxdh at gmail.com> wrote:
> Thank you for your explanation, I think I see what you mean now. But I
> want to double confirm that my understanding is correct.
>
>
>
> Regarding the previous two concepts you listed:
>
>
>
> 1. Given a pointer to some object of type T or a field of type T inside
> another object, what can I know for sure about the alignment of that
> pointer? (How many of the low bits are absolutely guaranteed to be 0,
> assuming the program doesn't involve Undefined Behavior?)
>
>
>
> “*what can I know for sure about the alignment of that pointer*” I assume
> you mean “*the alignment of the pointee*”. So this means we should always
> use ABI alignment for the pointee, because it’s an unknown object for the
> compiler right?
>
>
>
> Besides that, any other scenarios you are aware of that we should always
> use ABI alignment?
>
>
Yes, exactly right. But this, I suspect, covers the majority of the callers
asking about type alignment within the compiler.
2. If I want to allocate a brand new complete object of type T, at what
> alignment should I use to place it? This must be at least the alignment
> from 1. But beyond that, it could differ by circumstance, e.g. stack,
> global, and function parameter allocations could make different choices. In
> some cases, increasing the alignment of an allocation above the required
> minimum may just be an optimization, but in other cases it has an ABI
> impact.
>
>
>
> Does this mean when we *“allocate a brand new complete object of type T”,
> *we will prioritize/take “PreferredAlignment”?
>
>
Correct. Except, today, we only use the preferred alignment when allocating
memory backing a variable on the stack or in global/thread local
memory. See the code in ASTContext::getDeclAlign
<https://github.com/llvm/llvm-project/blob/428bebaf10e177db5e42206ca8f871f0bcbef058/clang/lib/AST/ASTContext.cpp#L1729>
.
We do *not* currently use preferred alignment for dynamic memory allocation
like "new double", as you've noticed. That may well be simply because on
all previously supported ABIs, the difference isn't even observable for
dynamic allocation, because the only types which had different preferred
alignments were 'double' and 'long long'. These got a preferred alignment
of 8 vs the abi alignment of 4. But, since the minimum alignment from
'operator new' is already 8 or 16, and no size-cookie is used for arrays of
these types (since they don't have destructors), it is actually irrelevant
whether we've asked for 4 or 8 byte alignment in this context.
It's entirely possible that there's additional locations in the code beyond
that where the preferred type alignment should or could be used. But
that'll need to be decided on a case by case basis looking at what each
caller to getTypeAlign is using it for.
Regarding X86-32, can you show me an example of how X86-32 makes the choice
> between PreferredAlignment and ABI alignment for “double” type?
>
See above.
>
> Thank you,
>
> Xiangling
>
>
>
>
>
> On Wed, Aug 19, 2020 at 3:16 PM James Y Knight <jyknight at google.com>
> wrote:
>
>> I wasn't talking about the alignment of a pointer, but rather the
>> alignment of a value of type 'double', which has a preferred alignment of 8.
>>
>> Not all double values in memory are 8 byte aligned -- some are placed
>> only at 4 byte aligned offsets. Therefore, the compiler can only assume an
>> alignment of 4 for an unknown object of type 'double' -- this is the abi
>> alignment.
>>
>> On Tue, Aug 18, 2020, 3:07 PM Xiangling Liao <xiangxdh at gmail.com> wrote:
>>
>>> Yeah, I agree that the alignment of a pointer to double on AIX should be
>>> 4 under 32-bit.
>>>
>>> I should've made it clearer. By "useless" I mean, we can always trust
>>> "PreferredAlignment" but not "ABI alignment" to give us the actual
>>> alignment value on AIX of a type "T".
>>>
>>> More specifically, regarding aggregate, double, long double type,
>>> "PreferredAlignment" gives us the actual alignment of these types. For
>>> other types, on AIX, "PreferredAlignment" also gives us the actual
>>> alignment of these types by containing the ABI alignment value underneath.
>>> So that means, for the example you mentioned previously, even if the
>>> alignment of the pointer under 32-bit would take "PreferredAlignment", it
>>> is actually using "ABI alignment" which is 4.
>>>
>>> That's why we proposed to use "PreferredAlignment" for AIX almost
>>> everywhere instead and use "flag" to force using ABI alignment when
>>> necessary.
>>>
>>> And I understand that we can investigate each spot where an ABI
>>> alignment is used, and may replace it with "PreferredAlignment" if needed,
>>> but it seems pretty time-consuming and I am not confident to know each
>>> usage of those four functions well and make the right decision.
>>>
>>> Regards,
>>> Xiangling
>>>
>>> On Tue, Aug 18, 2020 at 12:21 PM James Y Knight <jyknight at google.com>
>>> wrote:
>>>
>>>>
>>>>
>>>> On Tue, Aug 18, 2020 at 11:01 AM Xiangling Liao <xiangxdh at gmail.com>
>>>> wrote:
>>>>
>>>>> It looks to me as if at least examples 1, 2, and 4 are places that
>>>>> should *not* be changed to use the "AIX preferred" alignment, because
>>>>> they are inquiring about the guaranteed alignment of an existing pointer,
>>>>> which may well not have been placed at the preferred alignment. I haven't
>>>>> dug into the code beyond your snippet so I'm not sure about examples 3 and
>>>>> 5.
>>>>>
>>>>>
>>>>>
>>>>> To be honest, I am not familiar with every snippet where functions
>>>>> like “getTypeAlignInChars” is used. So I just list some locations that call
>>>>> “getTypeAlignInChars”. But I did mean that we probably should replace them
>>>>> with “PreferredAlignment” for AIX.
>>>>>
>>>>>
>>>>>
>>>>> 1. In AIX rules, not only an aggregate can now have a different
>>>>> preferred alignment from guaranteed alignment, but also double, long double.
>>>>>
>>>>>
>>>>>
>>>>> We see the semantic of “PreferredAlignment” on AIX represents a “T”’s
>>>>> actual alignment. In other words, ABI alignment on AIX is kinda useless
>>>>> except for being used to let “alignof(T)” return a correct value.
>>>>>
>>>>>
>>>> I feel like there's a fundamental misunderstanding here -- the ABI
>>>> alignment is not at all useless. Please see my previous email about the two
>>>> different concepts of alignment.
>>>>
>>>> For example, the AIX preferred alignment of 'double' is 8, and the
>>>> guaranteed ("ABI") alignment is 4. If you compile the following function,
>>>> the load/store instructions must be emitted with "align 4" and not "align
>>>> 8" on them, because that's all that you know about a pointer to double.
>>>> void foo(double* out, double* in) { *out = *in * 2; }
>>>>
>>>> 2. For the example1, you mentioned the “the guaranteed alignment of an
>>>>> existing pointer may well not have been placed at the preferred alignment”.
>>>>> But as I know, what function “getPreferredTypeAlign” does is to get the
>>>>> ABI/guaranteed alignment of a “T” first by invoking “getTypeInfo”, and then
>>>>> minor adjust some values. So we seems don’t need to worry about we cannot
>>>>> get a pointer’s ABI alignment, and many other cases as well, if that’s the
>>>>> correct one for AIX.
>>>>>
>>>>>
>>>>>
>>>>> Besides, I am not sure if I understand the code snippet right in
>>>>> example1.
>>>>>
>>>>>
>>>>>
>>>>> …
>>>>>
>>>>> // If that failed, return the type's alignment.
>>>>>
>>>>> return S.Context.getTypeAlignInChars
>>>>> (E->getType()->getPointeeType());
>>>>>
>>>>> }
>>>>>
>>>>> …
>>>>>
>>>>>
>>>>>
>>>>> A pointer’s pointeeType can be anything, which may be an aggregate, double,
>>>>> or long doble etc. For those types, on AIX, we do need to get the
>>>>> “PreferredAlignment”?
>>>>>
>>>>>
>>>>>
>>>>> 3. So far, I didn’t investigate every usage of those four functions:
>>>>>
>>>>> 1) getTypeAlignInChars [invokes getTypeAlign]
>>>>>
>>>>> 2) getTypeAlign <=> getTypeInfo(T).Align
>>>>> <https://clang.llvm.org/doxygen/structclang_1_1TypeInfo.html#af98dcefc364629eff868a1bb4010ebd8>
>>>>>
>>>>> 3) getTypeInfo [ invokes ‘getTypeInfoImpl’]
>>>>>
>>>>> 4) getTypeInfoInChars
>>>>>
>>>>>
>>>>>
>>>>> Because I am not sure if we really want to spend a lot of time on
>>>>> digging each crack. An initial scan of them shows a large amount of
>>>>> them used in Clang. How do you think about this?
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>>> And most importantly, besides of “adding a flag” way to handle speical
>>>>> alignment on AIX, I am wondering in which way we can handle it better?
>>>>>
>>>>>
>>>>>
>>>>>
>>>>> Regards,
>>>>> Xiangling
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>>> On Tue, Aug 18, 2020 at 8:48 AM James Y Knight <jyknight at google.com>
>>>>> wrote:
>>>>>
>>>>>>
>>>>>>
>>>>>> On Tue, Aug 18, 2020, 12:54 AM David Rector <davrecthreads at gmail.com>
>>>>>> wrote:
>>>>>>
>>>>>>> I would have to think that, whatever the documentation for e.g.
>>>>>>> getTypeAlignInChars(T), most users expect it to return T’s actual
>>>>>>> alignment for their target, whether it was determined from the ABI, or the
>>>>>>> AIX alignment scheme, or whatever.
>>>>>>>
>>>>>>
>>>>>> There really are two concepts at play here.
>>>>>>
>>>>>> 1. Given a pointer to some object of type T or a field of type T
>>>>>> inside another object, what can I know for sure about the alignment of that
>>>>>> pointer? (How many of the low bits are absolutely guaranteed to be 0,
>>>>>> assuming the program doesn't involve Undefined Behavior?)
>>>>>>
>>>>>> 2. If I want to allocate a brand new complete object of type T, at
>>>>>> what alignment should I use to place it? This must be at least the
>>>>>> alignment from 1. But beyond that, it could differ by circumstance, e.g.
>>>>>> stack, global, and function parameter allocations could make different
>>>>>> choices. In some cases, increasing the alignment of an allocation above the
>>>>>> required minimum may just be an optimization, but in other cases it has an
>>>>>> ABI impact.
>>>>>>
>>>>>> AIX is not the first platform where these alignments differ -- the
>>>>>> type 'double' on x86-32 being the other notable example. But, the AIX rules
>>>>>> trigger differences in more circumstances than we've had before, and thus
>>>>>> we now need to potentially make a choice in other parts of the code.
>>>>>> Importantly: in AIX rules, an aggregate can now have a different preferred
>>>>>> alignment from guaranteed alignment, too.
>>>>>>
>>>>>> The first case brought up where this surely matters, is 'new T[100]'
>>>>>> where T has a destructor. This thread is exploring whether there are other
>>>>>> such cases that we might need to change, too. I suspect that there are not
>>>>>> many, but that remains to be determined.
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>>> In fact, maybe we can go further: I would think that any valid use
>>>>>>> of type alignment which explicitly ignores AIX or other target-specific
>>>>>>> alignment schemes are probably just implementation details used only in the
>>>>>>> calculation of some composed type’s alignment under that scheme. In other
>>>>>>> words I would assume every function call in which AIX preferred alignment
>>>>>>> needed to be ignored is in the some layout-building implementation — is
>>>>>>> that right?
>>>>>>>
>>>>>>> E.g. the lib/AST/RecordLayoutBuilder.cpp example you cite below
>>>>>>> fits this description — in LayoutField(FieldDecl *D, …) you propose
>>>>>>> to change the call to fetch the ABI/non-AIX type info via
>>>>>>> getTypeInfo(D->getType(), true/* NeedsABIAlignment */);
>>>>>>> but only so that you can later tweak that info whenever
>>>>>>> DefaultsToAIXPowerAlignment==true.
>>>>>>>
>>>>>>> If all the changes you would need to make are indeed similar to
>>>>>>> this, these expanded usages are really only implementation details needed
>>>>>>> to build type layouts, and thus the goal should simply be to sufficiently
>>>>>>> hide these details from common programmers who don’t need to build type
>>>>>>> layouts, only access them.
>>>>>>>
>>>>>>> I think your original option #2 ("add a flag"), i.e. adding new
>>>>>>> defaulted parameters to a few functions, seems to be a reasonable way to
>>>>>>> hide these new details, so long as the new parameters are well named and
>>>>>>> documented. It also seems sufficiently general to be useful for
>>>>>>> implementing other special default alignment schemes in the future.
>>>>>>>
>>>>>>> Dave
>>>>>>>
>>>>>>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20200819/b59a8fbc/attachment-0001.html>
More information about the cfe-dev
mailing list