[PATCH] c++: Fix ABI issue with alignas on armv7hl [PR94050]
Jason Merrill
jason@redhat.com
Sun Mar 8 06:25:17 GMT 2020
On 3/7/20 6:02 PM, Marek Polacek wrote:
> On Sat, Mar 07, 2020 at 01:21:41AM -0500, Jason Merrill wrote:
>> On 3/6/20 8:12 PM, Marek Polacek wrote:
>>> On Fri, Mar 06, 2020 at 05:49:07PM -0500, Jason Merrill wrote:
>>>> On 3/5/20 2:40 PM, Marek Polacek wrote:
>>>>> The static_assert in the following test was failing on armv7hl because
>>>>> we were disregarding the alignas specifier on Cell. BaseShape's data
>>>>> takes up 20B on 32-bit architectures, but we failed to round up its
>>>>> TYPE_SIZE. This happens since the
>>>>> <https://gcc.gnu.org/ml/gcc-patches/2019-06/msg01189.html>
>>>>> patch: here, in layout_class_type for TenuredCell, we see that the size
>>>>> of TenuredCell and its CLASSTYPE_AS_BASE match, so we set
>>>>>
>>>>> CLASSTYPE_AS_BASE (t) = t;
>>>>>
>>>>> But while TYPE_USER_ALIGN of TenuredCell was 0, TYPE_USER_ALIGN of its
>>>>> CLASSTYPE_AS_BASE was 1.
>>>>
>>>> Surely the bug is that TYPE_USER_ALIGN isn't set for TenuredCell?
>>>
>>> That's my understanding.
>>
>> So why is that? Where is setting TYPE_USER_ALIGN on as-base TenuredCell,
>> and why isn't it setting it on the main type as well? Or is it getting
>> cleared on the main type for some reason?
>
> Yeah, it's getting cleared in finalize_type_size, called from
> 6703 /* Let the back end lay out the type. */
> 6704 finish_record_layout (rli, /*free_p=*/true);
> because finalize_type_size has
> 1930 /* Don't override a larger alignment requirement coming from a user
> 1931 alignment of one of the fields. */
> 1932 if (mode_align >= TYPE_ALIGN (type))
> 1933 {
> 1934 SET_TYPE_ALIGN (type, mode_align);
> 1935 TYPE_USER_ALIGN (type) = 0;
> 1936 }
> (for aggregates it is only done on STRICT_ALIGNMENT platforms which is why we
> won't see this problem on e.g. i686).
>
> Here's the story of TYPE_USER_ALIGN:
> - first we set TYPE_USER_ALIGN on Cell in common_handle_aligned_attribute,
> as expected
> - in layout_class_type for Cell we copy T_U_A to its as-base type:
> TYPE_USER_ALIGN (base_t) = TYPE_USER_ALIGN (t);
> - then we call finish_record_layout and T_U_A is cleared on Cell, but not its
> as-base type. In finalize_type_size mode_align == TYPE_ALIGN (type) == 64.
> - so in layout_empty_base_or_field we do this:
> if (CLASSTYPE_USER_ALIGN (type))
> {
> rli->record_align = MAX (rli->record_align, CLASSTYPE_ALIGN (type));
> if (warn_packed)
> rli->unpacked_align = MAX (rli->unpacked_align, CLASSTYPE_ALIGN (type));
> TYPE_USER_ALIGN (rli->t) = 1;
> }
> type is Cell and rli->t is TenuredCell
> - then in layout_class_type we copy T_U_A from TenuredCell to its as-base type,
> then finish_record_layout clears it from the main type
> - then we perform the new optimization by replacing the as-base type, making
> T_U_A set to 0
> - and so BaseShape's T_U_A is never set.
>
> Does this explain things better?
Yes, thanks. So the problem is the interaction of finalize_type_size
deciding that we don't need TYPE_USER_ALIGN if it's the same alignment
we would get anyway, and layout_empty_base_or_field only adjusting the
alignment if TYPE_USER_ALIGN is set, which happened to work before
because CLASSTYPE_USER_ALIGN was accidentally(?) still set.
I think the right behavior is probably to update rli->*_align regardless
of CLASSTYPE_USER_ALIGN (type); if an empty base doesn't have
user-specified aligment, its alignment will be 1, so it shouldn't ever
be harmful. When I added that code I wasn't aware of the
finalize_type_size behavior.
But I'm not confident that won't be an ABI break itself, so let's go
ahead with your approach for GCC 10, except
> + /* If T's CLASSTYPE_AS_BASE is TYPE_USER_ALIGN, but T is not,
> + replacing the as-base type would change CLASSTYPE_USER_ALIGN,
> + causing us to lose the user-specified alignment as in PR94050. */
> + && !CLASSTYPE_USER_ALIGN (t)
How about
&& CLASSTYPE_USER_ALIGN (t) == CLASSTYPE_USER_ALIGN (CLASSTYPE_AS_BASE (t))
?
Jason
More information about the Gcc-patches
mailing list