Thanks for doing this.

Matthew Malcomson <matthew.malcom...@arm.com> writes:
> In C++17, an empty class deriving from an empty base is not an
> aggregate, while in C++14 it is.  In order to implement this, GCC adds
> an artificial field to such classes.
>
> This artificial field has no mapping to Fundamental Data Types in the
> AArch64 PCS ABI and hence should not count towards determining whether an
> object can be passed using the vector registers as per section
> "6.4.2 Parameter Passing Rules" in the AArch64 PCS.
> https://github.com/ARM-software/abi-aa/blob/master/aapcs64/aapcs64.rst#the-base-procedure-call-standard
>
> This patch avoids counting this artificial field in
> aapcs_vfp_sub_candidate, and hence calculates whether such objects
> should be passed in vector registers in the same manner as C++14 (where
> the artificial field does not exist).
>
> Before this change, the test below would pass the arguments to `f` in
> general registers.  After this change, the test passes the arguments to
> `f` using the vector registers.
>
> The new behaviour matches the behaviour of `armclang`, and also matches
> the behaviour when run with `-std=gnu++14`.
>
>> gcc -std=gnu++17 test.cpp
>
> ``` test.cpp
> struct base {};
>
> struct pair : base
> {
>   float first;
>   float second;
>   pair (float f, float s) : first(f), second(s) {}
> };
>
> void f (pair);
> int main()
> {
>   f({3.14, 666});
>   return 1;
> }
> ```
>
> We add a `-Wpsabi` warning to catch cases where this fix has changed the ABI 
> for
> some functions.  Unfortunately this warning is currently emitted multiple 
> times
> for each problem, but I feel this is not much of a problem and can be fixed
> later if needs be.

Agreed.  We need a better way of handling this in general (for all targets).

> Testing:
>     Bootstrapped on aarch64-linux.
>     Jakub has provided a testsuite change that catches the original problem.
>     https://gcc.gnu.org/pipermail/gcc-patches/2020-April/544204.html
>     This patch makes that testcase pass.
>
> gcc/ChangeLog:
>
> 2020-04-21  Matthew Malcomson  <matthew.malcom...@arm.com>
>           Jakub Jelinek  <ja...@redhat.com>
>
>       PR target/94383
>       * config/aarch64/aarch64.c (enum cpp17empty_state): New.
>       (aapcs_vfp_sub_candidate): Account for C++17 empty base class
>       artificial fields.
>       (aarch64_vfp_is_call_or_return_candidate): Warn when ABI PCS decision is
>       different after this fix.
>
>
>
> ###############     Attachment also inlined for ease of reply    
> ###############
>
>
> diff --git a/gcc/config/aarch64/aarch64.c b/gcc/config/aarch64/aarch64.c
> index 
> c90de65de127992cc0bd9603a32684ebb5bd4fdf..f9f40d52c846afae0d8d2cd9ed4737cdc4b88896
>  100644
> --- a/gcc/config/aarch64/aarch64.c
> +++ b/gcc/config/aarch64/aarch64.c
> @@ -15909,13 +15909,30 @@ aarch64_conditional_register_usage (void)
>      }
>  }
>  
> +enum cpp17empty_state {
> +    DONT_AVOID = 0,
> +    AVOID = 1,
> +    AVOID_DONE = 3

Should only be two spaces of indentation.

Some kind of prefix might be nice, since the names are very generic.
But maybe we can leave this as-is for now and make it a scoped enum
once we switch to C++11.

> +};
> +
>  /* Walk down the type tree of TYPE counting consecutive base elements.
>     If *MODEP is VOIDmode, then set it to the first valid floating point
>     type.  If a non-floating point type is found, or if a floating point
>     type that doesn't match a non-VOIDmode *MODEP is found, then return -1,
> -   otherwise return the count in the sub-tree.  */
> +   otherwise return the count in the sub-tree.
> +
> +   The AVOID_C17EMPTY_FIELD argument is to allow the caller to check whether
> +   this function has changed its behaviour after the fix for PR94384 -- this

behavior (alas)

> +   fix is to avoid artificial fields in empty base classes.
> +   When called pointing at a value of DONT_AVOID then this function does not
> +   avoid the artificial fields -- this is useful to check whether the 
> function
> +   returns something different after the fix.
> +   When called pointing at a value which has the AVOID bit set, this function
> +   avoids such artificial fields and sets the value to AVOID_DONE when one of
> +   these fields has been set.  */
>  static int
> -aapcs_vfp_sub_candidate (const_tree type, machine_mode *modep)
> +aapcs_vfp_sub_candidate (const_tree type, machine_mode *modep,
> +                      enum cpp17empty_state *avoid_c17empty_field)
>  {
>    machine_mode mode;
>    HOST_WIDE_INT size;
> @@ -15992,7 +16009,8 @@ aapcs_vfp_sub_candidate (const_tree type, 
> machine_mode *modep)
>           || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
>         return -1;
>  
> -     count = aapcs_vfp_sub_candidate (TREE_TYPE (type), modep);
> +     count = aapcs_vfp_sub_candidate (TREE_TYPE (type), modep,
> +                                      avoid_c17empty_field);
>       if (count == -1
>           || !index
>           || !TYPE_MAX_VALUE (index)
> @@ -16030,7 +16048,22 @@ aapcs_vfp_sub_candidate (const_tree type, 
> machine_mode *modep)
>           if (TREE_CODE (field) != FIELD_DECL)
>             continue;
>  
> -         sub_count = aapcs_vfp_sub_candidate (TREE_TYPE (field), modep);
> +         /* Ignore C++17 empty base fields, while their type indicates
> +            they do contain padding, they have zero size and thus don't
> +            contain any padding.  */
> +         if (DECL_ARTIFICIAL (field)
> +             && DECL_NAME (field) == NULL_TREE
> +             && RECORD_OR_UNION_TYPE_P (TREE_TYPE (field))
> +             && DECL_SIZE (field)
> +             && integer_zerop (DECL_SIZE (field))
> +             && (*avoid_c17empty_field & AVOID))
> +           {

For the record, it feels a bit weird to be checking DECL_NAME in an
ABI decision like this.  I think any zero-sized field should really be
handled in the same way (and they explicitly are for "Pure Scalable Types").
But that's a deeper issue and I realise the intention is just to restrict
the fix as much as possible to the C++17 case.

I haven't audited the compiler to see if there could be other artificial
decls with the same properties.  I don't think that's necessary though,
since any such decls ought to be handled in the same way as the C++17
ones.  I.e. the "risk" in that direction is that we'll accidentally
catch other cases that need the same fix.

> +             *avoid_c17empty_field = AVOID_DONE;
> +             continue;
> +           }
> +
> +         sub_count = aapcs_vfp_sub_candidate (TREE_TYPE (field), modep,
> +                                              avoid_c17empty_field);
>           if (sub_count < 0)
>             return -1;
>           count += sub_count;
> @@ -16063,7 +16096,8 @@ aapcs_vfp_sub_candidate (const_tree type, 
> machine_mode *modep)
>           if (TREE_CODE (field) != FIELD_DECL)
>             continue;
>  
> -         sub_count = aapcs_vfp_sub_candidate (TREE_TYPE (field), modep);
> +         sub_count = aapcs_vfp_sub_candidate (TREE_TYPE (field), modep,
> +                                              avoid_c17empty_field);
>           if (sub_count < 0)
>             return -1;
>           count = count > sub_count ? count : sub_count;
> @@ -16180,10 +16214,22 @@ aarch64_vfp_is_call_or_return_candidate 
> (machine_mode mode,
>      }
>    else if (type && composite_p)
>      {
> -      int ag_count = aapcs_vfp_sub_candidate (type, &new_mode);
> +      enum cpp17empty_state after_fix = AVOID;
> +      int ag_count = aapcs_vfp_sub_candidate (type, &new_mode, &after_fix);
>  
>        if (ag_count > 0 && ag_count <= HA_MAX_NUM_FLDS)
>       {
> +       int alt;
> +       enum cpp17empty_state before_fix = DONT_AVOID;
> +       if (warn_psabi
> +           && after_fix == AVOID_DONE
> +           && ((alt = aapcs_vfp_sub_candidate (type, &new_mode,
> +                                               &before_fix))
> +               != ag_count)
> +           && (alt <= 0 || alt > HA_MAX_NUM_FLDS))

I think this last line should be a gcc_assert before the inform().
Something has gone wrong if we return a different in-range aggregate
size here.

> +         inform (input_location, "parameter passing of type %qT changed for "
> +                 "std=c++17 in GCC 10", type);

I tripped over this wording a bit.  It sounded like it was saying that
the choice of standard started to matter in GCC 10, whereas it's really
the opposite.

Not sure I can suggest anything better, but would it work to turn
it around and say:

  prior to GCC 10, parameters of type %qT were passed in an
  incompatible way for C++17

or just:

  prior to GCC 10, parameters of type %qT were handled incorrectly for C++17

Other suggestions welcome. :-)

LGTM otherwise.

Thanks,
Richard

Reply via email to