diff mbox series

arm: Fix parameter passing for [[no_unique_address]]

Message ID mpt1ro6wquj.fsf@arm.com
State New
Headers show
Series arm: Fix parameter passing for [[no_unique_address]] | expand

Commit Message

Richard Sandiford April 29, 2020, 10:04 a.m. UTC
This patch makes the ABI code ignore zero-sized [[no_unique_address]]
fields when deciding whether something is a HFA or HVA.

For the tests, I wanted an -march setting that was stable enough
to use check-function-bodies and also wanted to force -mfloat-abi=hard.
I couldn't see any existing way of doing both together, since most
arm-related effective-target keywords are agnostic about the choice
between -mfloat-abi=softfp and -mfloat-abi=hard.  I therefore added
a new effective-target keyword for this combination.

I used the arm_arch_* framework for the effective-target rather than
writing a new set of custom Tcl routines.  This has the nice property
of separating the "compile and assemble" cases from the "link and run"
cases.  I only need compilation to work for the new tests, so requiring
linking to work would be an unnecessary restriction.

However, including an ABI requirement is arguably stretching what the
list was originally intended to handle.  The name arm_arch_v8a_hard
doesn't fit very naturally with some of the NEON-based tests.
On the other hand, the naming convention isn't entirely consistent,
so any choice would be inconsistent with something.

Like with the aarch64 patch, clang produces equivalent code for all
tests except unions Q and R, which clang passes in s0 rather than r0.
I think this is a clang bug, since the "x" field contains a single-byte
FDT and so shouldn't be ignored.

Tested on arm-linux-gnueabihf and armeb-eabi.  OK to install?

Richard


2020-04-29  Richard Sandiford  <richard.sandiford@arm.com>

gcc/
	* doc/sourcebuild.texi (arm_arch_v8a_hard_ok): Document new
	effective-target keyword.
	(arm_arch_v8a_hard_multilib): Likewise.
	(arm_arch_v8a_hard): Document new dg-add-options keyword.
	* config/arm/arm.c (arm_return_in_memory): Note that the APCS
	code is deprecated and has not been updated to handle
	DECL_FIELD_ABI_IGNORED.
	(WARN_PSABI_EMPTY_CXX17_BASE): New constant.
	(WARN_PSABI_NO_UNIQUE_ADDRESS): Likewise.
	(aapcs_vfp_sub_candidate): Replace the boolean pointer parameter
	avoid_cxx17_empty_base with a pointer to a bitmask.  Ignore fields
	whose DECL_FIELD_ABI_IGNORED bit is set when determining whether
	something actually is a HFA or HVA.  Record whether we see a
	[[no_unique_address]] field that previous GCCs would not have
	ignored in this way.
	(aapcs_vfp_is_call_or_return_candidate): Update the calls to
	aapcs_vfp_sub_candidate and report a -Wpsabi warning for the
	[[no_unique_address]] case.  Use TYPE_MAIN_VARIANT in the
	diagnostic messages.
	(arm_needs_doubleword_align): Add a comment explaining why we
	consider even zero-sized fields.

gcc/testsuite/
	* lib/target-supports.exp: Add v8a_hard to the list of arm_arch_*
	targets.
	* g++.target/arm/no_unique_address_1.C: New test.
	* g++.target/arm/no_unique_address_2.C: Likewise.
---
 gcc/config/arm/arm.c                          | 108 +++++++---
 gcc/doc/sourcebuild.texi                      |  15 ++
 .../g++.target/arm/no_unique_address_1.C      | 201 ++++++++++++++++++
 .../g++.target/arm/no_unique_address_2.C      | 201 ++++++++++++++++++
 gcc/testsuite/lib/target-supports.exp         |   1 +
 5 files changed, 498 insertions(+), 28 deletions(-)
 create mode 100644 gcc/testsuite/g++.target/arm/no_unique_address_1.C
 create mode 100644 gcc/testsuite/g++.target/arm/no_unique_address_2.C

Comments

Kyrylo Tkachov April 29, 2020, 10:42 a.m. UTC | #1
Hi Richard,

> -----Original Message-----
> From: Richard Sandiford <richard.sandiford@arm.com>
> Sent: 29 April 2020 11:04
> To: gcc-patches@gcc.gnu.org
> Cc: nickc@redhat.com; Richard Earnshaw <Richard.Earnshaw@arm.com>;
> Ramana Radhakrishnan <Ramana.Radhakrishnan@arm.com>; Kyrylo
> Tkachov <Kyrylo.Tkachov@arm.com>
> Subject: [PATCH] arm: Fix parameter passing for [[no_unique_address]]
> 
> This patch makes the ABI code ignore zero-sized [[no_unique_address]]
> fields when deciding whether something is a HFA or HVA.
> 
> For the tests, I wanted an -march setting that was stable enough
> to use check-function-bodies and also wanted to force -mfloat-abi=hard.
> I couldn't see any existing way of doing both together, since most
> arm-related effective-target keywords are agnostic about the choice
> between -mfloat-abi=softfp and -mfloat-abi=hard.  I therefore added
> a new effective-target keyword for this combination.
> 
> I used the arm_arch_* framework for the effective-target rather than
> writing a new set of custom Tcl routines.  This has the nice property
> of separating the "compile and assemble" cases from the "link and run"
> cases.  I only need compilation to work for the new tests, so requiring
> linking to work would be an unnecessary restriction.
> 
> However, including an ABI requirement is arguably stretching what the
> list was originally intended to handle.  The name arm_arch_v8a_hard
> doesn't fit very naturally with some of the NEON-based tests.
> On the other hand, the naming convention isn't entirely consistent,
> so any choice would be inconsistent with something.
> 
> Like with the aarch64 patch, clang produces equivalent code for all
> tests except unions Q and R, which clang passes in s0 rather than r0.
> I think this is a clang bug, since the "x" field contains a single-byte
> FDT and so shouldn't be ignored.

Worth reporting it?

> 
> Tested on arm-linux-gnueabihf and armeb-eabi.  OK to install?

Ok.
Thanks,
Kyrill

> 
> Richard
> 
> 
> 2020-04-29  Richard Sandiford  <richard.sandiford@arm.com>
> 
> gcc/
> 	* doc/sourcebuild.texi (arm_arch_v8a_hard_ok): Document new
> 	effective-target keyword.
> 	(arm_arch_v8a_hard_multilib): Likewise.
> 	(arm_arch_v8a_hard): Document new dg-add-options keyword.
> 	* config/arm/arm.c (arm_return_in_memory): Note that the APCS
> 	code is deprecated and has not been updated to handle
> 	DECL_FIELD_ABI_IGNORED.
> 	(WARN_PSABI_EMPTY_CXX17_BASE): New constant.
> 	(WARN_PSABI_NO_UNIQUE_ADDRESS): Likewise.
> 	(aapcs_vfp_sub_candidate): Replace the boolean pointer parameter
> 	avoid_cxx17_empty_base with a pointer to a bitmask.  Ignore fields
> 	whose DECL_FIELD_ABI_IGNORED bit is set when determining
> whether
> 	something actually is a HFA or HVA.  Record whether we see a
> 	[[no_unique_address]] field that previous GCCs would not have
> 	ignored in this way.
> 	(aapcs_vfp_is_call_or_return_candidate): Update the calls to
> 	aapcs_vfp_sub_candidate and report a -Wpsabi warning for the
> 	[[no_unique_address]] case.  Use TYPE_MAIN_VARIANT in the
> 	diagnostic messages.
> 	(arm_needs_doubleword_align): Add a comment explaining why we
> 	consider even zero-sized fields.
> 
> gcc/testsuite/
> 	* lib/target-supports.exp: Add v8a_hard to the list of arm_arch_*
> 	targets.
> 	* g++.target/arm/no_unique_address_1.C: New test.
> 	* g++.target/arm/no_unique_address_2.C: Likewise.
> ---
>  gcc/config/arm/arm.c                          | 108 +++++++---
>  gcc/doc/sourcebuild.texi                      |  15 ++
>  .../g++.target/arm/no_unique_address_1.C      | 201 ++++++++++++++++++
>  .../g++.target/arm/no_unique_address_2.C      | 201 ++++++++++++++++++
>  gcc/testsuite/lib/target-supports.exp         |   1 +
>  5 files changed, 498 insertions(+), 28 deletions(-)
>  create mode 100644 gcc/testsuite/g++.target/arm/no_unique_address_1.C
>  create mode 100644 gcc/testsuite/g++.target/arm/no_unique_address_2.C
> 
> diff --git a/gcc/doc/sourcebuild.texi b/gcc/doc/sourcebuild.texi
> index b69612024be..d8da77d5ba4 100644
> --- a/gcc/doc/sourcebuild.texi
> +++ b/gcc/doc/sourcebuild.texi
> @@ -1829,6 +1829,16 @@ Some multilibs may be incompatible with these
> options.
>  ARM target supports @code{-mfpu=vfp3 -mfloat-abi=softfp}.
>  Some multilibs may be incompatible with these options.
> 
> +@item arm_arch_v8a_hard_ok
> +The compiler is targeting @code{arm*-*-*} and can compile and assemble
> code
> +using the options @code{-march=armv8-a -mfpu=neon-fp-armv8 -mfloat-
> abi=hard}.
> +This is not enough to guarantee that linking works.
> +
> +@item arm_arch_v8a_hard_multilib
> +The compiler is targeting @code{arm*-*-*} and can build programs using
> +the options @code{-march=armv8-a -mfpu=neon-fp-armv8 -mfloat-
> abi=hard}.
> +The target can also run the resulting binaries.
> +
>  @item arm_v8_vfp_ok
>  ARM target supports @code{-mfpu=fp-armv8 -mfloat-abi=softfp}.
>  Some multilibs may be incompatible with these options.
> @@ -2586,6 +2596,11 @@ the
> @ref{arm_neon_fp16_ok,,arm_neon_fp16_ok effective target keyword}.
>  arm vfp3 floating point support; see
>  the @ref{arm_vfp3_ok,,arm_vfp3_ok effective target keyword}.
> 
> +@item arm_arch_v8a_hard
> +Add options for ARMv8-A and the hard-float variant of the AAPCS,
> +if this is supported by the compiler; see the
> +@ref{arm_arch_v8a_hard_ok,,arm_arch_v8a_hard_ok} effective target
> keyword.
> +
>  @item arm_v8_1a_neon
>  Add options for ARMv8.1-A with Adv.SIMD support, if this is supported
>  by the target; see the @ref{arm_v8_1a_neon_ok,,arm_v8_1a_neon_ok}
> diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c
> index 30a2a3aeb0e..dcaae7bf7aa 100644
> --- a/gcc/config/arm/arm.c
> +++ b/gcc/config/arm/arm.c
> @@ -5963,6 +5963,8 @@ arm_return_in_memory (const_tree type,
> const_tree fntype)
> 
>        /* Find the first field, ignoring non FIELD_DECL things which will
>  	 have been created by C++.  */
> +      /* NOTE: This code is deprecated and has not been updated to handle
> +	 DECL_FIELD_ABI_IGNORED.  */
>        for (field = TYPE_FIELDS (type);
>  	   field && TREE_CODE (field) != FIELD_DECL;
>  	   field = DECL_CHAIN (field))
> @@ -6135,23 +6137,42 @@ aapcs_vfp_cum_init (CUMULATIVE_ARGS *pcum
> ATTRIBUTE_UNUSED,
>    pcum->aapcs_vfp_reg_alloc = 0;
>  }
> 
> +/* Bitmasks that indicate whether earlier versions of GCC would have
> +   taken a different path through the ABI logic.  This should result in
> +   a -Wpsabi warning if the earlier path led to a different ABI decision.
> +
> +   WARN_PSABI_EMPTY_CXX17_BASE
> +      Indicates that the type includes an artificial empty C++17 base field
> +      that, prior to GCC 10.1, would prevent the type from being treated as
> +      a HFA or HVA.  See PR94711 for details.
> +
> +   WARN_PSABI_NO_UNIQUE_ADDRESS
> +      Indicates that the type includes an empty [[no_unique_address]] field
> +      that, prior to GCC 10.1, would prevent the type from being treated as
> +      a HFA or HVA.  */
> +const unsigned int WARN_PSABI_EMPTY_CXX17_BASE = 1U << 0;
> +const unsigned int WARN_PSABI_NO_UNIQUE_ADDRESS = 1U << 1;
> +
>  /* 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.
> 
> -   The AVOID_CXX17_EMPTY_BASE argument is to allow the caller to check
> whether
> -   this function has changed its behavior after the fix for PR94384 -- this fix
> -   is to avoid artificial fields in empty base classes.
> -   When called with this argument as a NULL pointer 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, this function avoids such artificial fields
> -   and sets the value to TRUE when one of these fields has been set.  */
> +   The WARN_PSABI_FLAGS argument allows the caller to check whether this
> +   function has changed its behavior relative to earlier versions of GCC.
> +   Normally the argument should be nonnull and point to a zero-initialized
> +   variable.  The function then records whether the ABI decision might
> +   be affected by a known fix to the ABI logic, setting the associated
> +   WARN_PSABI_* bits if so.
> +
> +   When the argument is instead a null pointer, the function tries to
> +   simulate the behavior of GCC before all such ABI fixes were made.
> +   This is useful to check whether the function returns something
> +   different after the ABI fixes.  */
>  static int
>  aapcs_vfp_sub_candidate (const_tree type, machine_mode *modep,
> -			 bool *avoid_cxx17_empty_base)
> +			 unsigned int *warn_psabi_flags)
>  {
>    machine_mode mode;
>    HOST_WIDE_INT size;
> @@ -6224,7 +6245,7 @@ aapcs_vfp_sub_candidate (const_tree type,
> machine_mode *modep,
>  	  return -1;
> 
>  	count = aapcs_vfp_sub_candidate (TREE_TYPE (type), modep,
> -					 avoid_cxx17_empty_base);
> +					 warn_psabi_flags);
>  	if (count == -1
>  	    || !index
>  	    || !TYPE_MAX_VALUE (index)
> @@ -6262,20 +6283,30 @@ aapcs_vfp_sub_candidate (const_tree type,
> machine_mode *modep,
>  	    if (TREE_CODE (field) != FIELD_DECL)
>  	      continue;
> 
> -	    /* Ignore C++17 empty base fields, while their type indicates they
> -	       contain padding, this is only sometimes contributed to the
> derived
> -	       class.
> -	       When the padding is contributed to the derived class that's
> -	       caught by the general test for padding below.  */
> -	    if (cxx17_empty_base_field_p (field)
> -		&& avoid_cxx17_empty_base)
> +	    if (DECL_FIELD_ABI_IGNORED (field))
>  	      {
> -		*avoid_cxx17_empty_base = true;
> -		continue;
> +		/* See whether this is something that earlier versions of
> +		   GCC failed to ignore.  */
> +		unsigned int flag;
> +		if (lookup_attribute ("no_unique_address",
> +				      DECL_ATTRIBUTES (field)))
> +		  flag = WARN_PSABI_NO_UNIQUE_ADDRESS;
> +		else if (cxx17_empty_base_field_p (field))
> +		  flag = WARN_PSABI_EMPTY_CXX17_BASE;
> +		else
> +		  /* No compatibility problem.  */
> +		  continue;
> +
> +		/* Simulate the old behavior when WARN_PSABI_FLAGS is
> null.  */
> +		if (warn_psabi_flags)
> +		  {
> +		    *warn_psabi_flags |= flag;
> +		    continue;
> +		  }
>  	      }
> 
>  	    sub_count = aapcs_vfp_sub_candidate (TREE_TYPE (field), modep,
> -						 avoid_cxx17_empty_base);
> +						 warn_psabi_flags);
>  	    if (sub_count < 0)
>  	      return -1;
>  	    count += sub_count;
> @@ -6309,7 +6340,7 @@ aapcs_vfp_sub_candidate (const_tree type,
> machine_mode *modep,
>  	      continue;
> 
>  	    sub_count = aapcs_vfp_sub_candidate (TREE_TYPE (field), modep,
> -						 avoid_cxx17_empty_base);
> +						 warn_psabi_flags);
>  	    if (sub_count < 0)
>  	      return -1;
>  	    count = count > sub_count ? count : sub_count;
> @@ -6371,24 +6402,32 @@ aapcs_vfp_is_call_or_return_candidate (enum
> arm_pcs pcs_variant,
>       out from the mode.  */
>    if (type)
>      {
> -      bool avoided = false;
> -      int ag_count = aapcs_vfp_sub_candidate (type, &new_mode, &avoided);
> +      unsigned int warn_psabi_flags = 0;
> +      int ag_count = aapcs_vfp_sub_candidate (type, &new_mode,
> +					      &warn_psabi_flags);
>        if (ag_count > 0 && ag_count <= 4)
>  	{
>  	  static unsigned last_reported_type_uid;
>  	  unsigned uid = TYPE_UID (TYPE_MAIN_VARIANT (type));
>  	  int alt;
>  	  if (warn_psabi
> -	      && avoided
> +	      && warn_psabi_flags
>  	      && uid != last_reported_type_uid
>  	      && ((alt = aapcs_vfp_sub_candidate (type, &new_mode, NULL))
>  		  != ag_count))
>  	    {
>  	      gcc_assert (alt == -1);
>  	      last_reported_type_uid = uid;
> -	      inform (input_location, "parameter passing for argument of type
> "
> -		      "%qT when C++17 is enabled changed to match C++14 "
> -		      "in GCC 10.1", type);
> +	      /* Use TYPE_MAIN_VARIANT to strip any redundant const
> +		 qualification.  */
> +	      if (warn_psabi_flags & WARN_PSABI_NO_UNIQUE_ADDRESS)
> +		inform (input_location, "parameter passing for argument of "
> +			"type %qT with %<[[no_unique_address]]%>
> members "
> +			"changed in GCC 10.1", TYPE_MAIN_VARIANT (type));
> +	      else if (warn_psabi_flags & WARN_PSABI_EMPTY_CXX17_BASE)
> +		inform (input_location, "parameter passing for argument of "
> +			"type %qT when C++17 is enabled changed to match
> "
> +			"C++14 in GCC 10.1", TYPE_MAIN_VARIANT (type));
>  	    }
>  	  *count = ag_count;
>  	}
> @@ -6933,7 +6972,20 @@ arm_needs_doubleword_align (machine_mode
> mode, const_tree type)
> 
>    int ret = 0;
>    int ret2 = 0;
> -  /* Record/aggregate types: Use greatest member alignment of any
> member.  */
> +  /* Record/aggregate types: Use greatest member alignment of any
> member.
> +
> +     Note that we explicitly consider zero-sized fields here, even though
> +     they don't map to AAPCS machine types.  For example, in:
> +
> +	 struct __attribute__((aligned(8))) empty {};
> +
> +	 struct s {
> +	   [[no_unique_address]] empty e;
> +	   int x;
> +	 };
> +
> +     "s" contains only one Fundamental Data Type (the int field)
> +     but gains 8-byte alignment and size thanks to "e".  */
>    for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
>      if (DECL_ALIGN (field) > PARM_BOUNDARY)
>        {
> diff --git a/gcc/testsuite/lib/target-supports.exp b/gcc/testsuite/lib/target-
> supports.exp
> index f416d5cafda..13761491e63 100644
> --- a/gcc/testsuite/lib/target-supports.exp
> +++ b/gcc/testsuite/lib/target-supports.exp
> @@ -4443,6 +4443,7 @@ foreach { armfunc armflag armdefs } {
>  	v7ve "-march=armv7ve -marm"
>  		"__ARM_ARCH_7A__ && __ARM_FEATURE_IDIV"
>  	v8a "-march=armv8-a" __ARM_ARCH_8A__
> +	v8a_hard "-march=armv8-a -mfpu=neon-fp-armv8 -mfloat-abi=hard"
> __ARM_ARCH_8A__
>  	v8_1a "-march=armv8.1-a" __ARM_ARCH_8A__
>  	v8_2a "-march=armv8.2-a" __ARM_ARCH_8A__
>  	v8r "-march=armv8-r" __ARM_ARCH_8R__
> diff --git a/gcc/testsuite/g++.target/arm/no_unique_address_1.C
> b/gcc/testsuite/g++.target/arm/no_unique_address_1.C
> new file mode 100644
> index 00000000000..038aa00a499
> --- /dev/null
> +++ b/gcc/testsuite/g++.target/arm/no_unique_address_1.C
> @@ -0,0 +1,201 @@
> +/* { dg-require-effective-target arm_arch_v8a_hard_ok } */
> +/* { dg-options "-std=c++11 -O -foptimize-sibling-calls" } */
> +/* { dg-add-options arm_arch_v8a_hard } */
> +/* { dg-final { check-function-bodies "**" "" "" } } */
> +
> +struct X { };
> +struct Y { int : 0; };
> +struct Z { int : 0; Y y; };
> +struct W : public X { X q; };
> +
> +struct A { float a; };
> +
> +struct B : public X { float a; };
> +struct C : public Y { float a; };
> +struct D : public Z { float a; };
> +struct E : public W { float a; };
> +
> +struct F { [[no_unique_address]] X x; float a; };
> +struct G { [[no_unique_address]] Y y; float a; };
> +struct H { [[no_unique_address]] Z z; float a; };
> +struct I { [[no_unique_address]] W w; float a; };
> +
> +struct J { float a; [[no_unique_address]] X x; float b; };
> +struct K { float a; [[no_unique_address]] Y y; float b; };
> +struct L { float a; [[no_unique_address]] Z z; float b; };
> +struct M { float a; [[no_unique_address]] W w; float b; };
> +
> +struct N : public A { float b; };
> +struct O { [[no_unique_address]] A a; float b; };
> +
> +struct P : public Y { int : 0; float a, b, c, d; };
> +
> +union Q { X x; float a; };
> +union R { [[no_unique_address]] X x; float a; };
> +
> +union S { A a; float b; };
> +union T { F f; float b; };
> +union U { N n; O o; };
> +
> +typedef S Salias;
> +typedef T Talias;
> +typedef U Ualias;
> +
> +#define T(S, s) extern int callee_##s (S)
> +
> +/*
> +** _Z8caller_aR1A:
> +**	vldr.32	s0, \[r0\]
> +**	b	.*
> +*/
> +T (A, a); int caller_a (A &a) { return callee_a (a); } /* { dg-bogus {argument of
> type 'A'} } */
> +
> +/*
> +** _Z8caller_bR1B:
> +**	vldr.32	s0, \[r0\]
> +**	b	.*
> +*/
> +T (B, b); int caller_b (B &b) { return callee_b (b); } /* { dg-bogus {argument
> of type 'B'} } */
> +
> +/*
> +** _Z8caller_cR1C:
> +**	vldr.32	s0, \[r0\]
> +**	b	.*
> +*/
> +T (C, c); int caller_c (C &c) { return callee_c (c); } /* { dg-bogus {argument of
> type 'C'} } */
> +
> +/*
> +** _Z8caller_dR1D:
> +**	ldm	r0, {r0, r1}
> +**	b	.*
> +*/
> +T (D, d); int caller_d (D &d) { return callee_d (d); } /* { dg-bogus {argument
> of type 'D'} } */
> +
> +/*
> +** _Z8caller_eR1E:
> +**	ldm	r0, {r0, r1}
> +**	b	.*
> +*/
> +T (E, e); int caller_e (E &e) { return callee_e (e); } /* { dg-bogus {argument of
> type 'E'} } */
> +
> +/*
> +** _Z8caller_fR1F:
> +**	vldr.32	s0, \[r0\]
> +**	b	.*
> +*/
> +T (F, f); int caller_f (F &f) { return callee_f (f); } /* { dg-message {parameter
> passing for argument of type 'F' with '\[\[no_unique_address\]\]' members
> changed in GCC 10.1} } */
> +
> +/*
> +** _Z8caller_gR1G:
> +**	vldr.32	s0, \[r0\]
> +**	b	.*
> +*/
> +T (G, g); int caller_g (G &g) { return callee_g (g); } /* { dg-message
> {parameter passing for argument of type 'G' with
> '\[\[no_unique_address\]\]' members changed in GCC 10.1} } */
> +
> +/*
> +** _Z8caller_hR1H:
> +**	ldm	r0, {r0, r1}
> +**	b	.*
> +*/
> +T (H, h); int caller_h (H &h) { return callee_h (h); } /* { dg-bogus {argument
> of type 'H'} } */
> +
> +/*
> +** _Z8caller_iR1I:
> +**	ldm	r0, {r0, r1}
> +**	b	.*
> +*/
> +T (I, i); int caller_i (I &i) { return callee_i (i); } /* { dg-bogus {argument of type
> 'I'} } */
> +
> +/*
> +** _Z8caller_jR1J:
> +**	vldr.32	s0, \[r0\]
> +**	vldr.32	s1, \[r0, #4\]
> +**	b	.*
> +*/
> +T (J, j); int caller_j (J &j) { return callee_j (j); } /* { dg-message {parameter
> passing for argument of type 'J' with '\[\[no_unique_address\]\]' members
> changed in GCC 10.1} } */
> +
> +/*
> +** _Z8caller_kR1K:
> +**	vldr.32	s0, \[r0\]
> +**	vldr.32	s1, \[r0, #4\]
> +**	b	.*
> +*/
> +T (K, k); int caller_k (K &k) { return callee_k (k); } /* { dg-message {parameter
> passing for argument of type 'K' with '\[\[no_unique_address\]\]' members
> changed in GCC 10.1} } */
> +
> +/*
> +** _Z8caller_lR1L:
> +**	ldm	r0, {r0, r1, r2}
> +**	b	.*
> +*/
> +T (L, l); int caller_l (L &l) { return callee_l (l); } /* { dg-bogus {argument of
> type 'L'} } */
> +
> +/*
> +** _Z8caller_mR1M:
> +**	ldm	r0, {r0, r1, r2}
> +**	b	.*
> +*/
> +T (M, m); int caller_m (M &m) { return callee_m (m); } /* { dg-bogus
> {argument of type 'M'} } */
> +
> +/*
> +** _Z8caller_nR1N:
> +**	vldr.32	s0, \[r0\]
> +**	vldr.32	s1, \[r0, #4\]
> +**	b	.*
> +*/
> +T (N, n); int caller_n (N &n) { return callee_n (n); } /* { dg-bogus {argument
> of type 'N'} } */
> +
> +/*
> +** _Z8caller_oR1O:
> +**	vldr.32	s0, \[r0\]
> +**	vldr.32	s1, \[r0, #4\]
> +**	b	.*
> +*/
> +T (O, o); int caller_o (O &o) { return callee_o (o); } /* { dg-bogus {argument
> of type 'O'} } */
> +
> +/*
> +** _Z8caller_pR1P:
> +**	vldr.32	s0, \[r0\]
> +**	vldr.32	s1, \[r0, #4\]
> +**	vldr.32	s2, \[r0, #8\]
> +**	vldr.32	s3, \[r0, #12\]
> +**	b	.*
> +*/
> +T (P, p); int caller_p (P &p) { return callee_p (p); } /* { dg-bogus {argument of
> type 'P'} } */
> +
> +/*
> +** _Z8caller_qR1Q:
> +**	ldr	r0, \[r0\]
> +**	b	.*
> +*/
> +T (Q, q); int caller_q (Q &q) { return callee_q (q); } /* { dg-bogus {argument
> of type 'Q'} } */
> +
> +/*
> +** _Z8caller_rR1R:
> +**	ldr	r0, \[r0\]
> +**	b	.*
> +*/
> +T (R, r); int caller_r (R &r) { return callee_r (r); } /* { dg-bogus {argument of
> type 'R'} } */
> +
> +/*
> +** _Z8caller_sR1S:
> +**	vldr.32	s0, \[r0\]	@ int
> +**	b	.*
> +*/
> +T (Salias, s); int caller_s (Salias &s) { return callee_s (s); } /* { dg-bogus
> {argument of type 'S'} } */
> +
> +/*
> +** _Z8caller_tR1T:
> +**	vldr.32	s0, \[r0\]	@ int
> +**	b	.*
> +*/
> +T (Talias, t); int caller_t (Talias &t) { return callee_t (t); } /* { dg-message
> {parameter passing for argument of type 'T' with '\[\[no_unique_address\]\]'
> members changed in GCC 10.1} } */
> +
> +/*
> +** _Z8caller_uR1U:
> +**	vldr.32	s0, \[r0\]
> +**	vldr.32	s1, \[r0, #4\]
> +**	b	.*
> +*/
> +T (Ualias, u); int caller_u (Ualias &u) { return callee_u (u); } /* { dg-bogus
> {argument of type 'U'} } */
> +
> +/* { dg-bogus {argument of type 'const} "should not be printed as const"
> { target *-*-*} 0 } */
> diff --git a/gcc/testsuite/g++.target/arm/no_unique_address_2.C
> b/gcc/testsuite/g++.target/arm/no_unique_address_2.C
> new file mode 100644
> index 00000000000..8be5de2539a
> --- /dev/null
> +++ b/gcc/testsuite/g++.target/arm/no_unique_address_2.C
> @@ -0,0 +1,201 @@
> +/* { dg-require-effective-target arm_arch_v8a_hard_ok } */
> +/* { dg-options "-std=c++17 -O -foptimize-sibling-calls" } */
> +/* { dg-add-options arm_arch_v8a_hard } */
> +/* { dg-final { check-function-bodies "**" "" "" } } */
> +
> +struct X { };
> +struct Y { int : 0; };
> +struct Z { int : 0; Y y; };
> +struct W : public X { X q; };
> +
> +struct A { float a; };
> +
> +struct B : public X { float a; };
> +struct C : public Y { float a; };
> +struct D : public Z { float a; };
> +struct E : public W { float a; };
> +
> +struct F { [[no_unique_address]] X x; float a; };
> +struct G { [[no_unique_address]] Y y; float a; };
> +struct H { [[no_unique_address]] Z z; float a; };
> +struct I { [[no_unique_address]] W w; float a; };
> +
> +struct J { float a; [[no_unique_address]] X x; float b; };
> +struct K { float a; [[no_unique_address]] Y y; float b; };
> +struct L { float a; [[no_unique_address]] Z z; float b; };
> +struct M { float a; [[no_unique_address]] W w; float b; };
> +
> +struct N : public A { float b; };
> +struct O { [[no_unique_address]] A a; float b; };
> +
> +struct P : public Y { int : 0; float a, b, c, d; };
> +
> +union Q { X x; float a; };
> +union R { [[no_unique_address]] X x; float a; };
> +
> +union S { A a; float b; };
> +union T { F f; float b; };
> +union U { N n; O o; };
> +
> +typedef S Salias;
> +typedef T Talias;
> +typedef U Ualias;
> +
> +#define T(S, s) extern int callee_##s (S)
> +
> +/*
> +** _Z8caller_aR1A:
> +**	vldr.32	s0, \[r0\]
> +**	b	.*
> +*/
> +T (A, a); int caller_a (A &a) { return callee_a (a); } /* { dg-bogus {argument of
> type 'A'} } */
> +
> +/*
> +** _Z8caller_bR1B:
> +**	vldr.32	s0, \[r0\]
> +**	b	.*
> +*/
> +T (B, b); int caller_b (B &b) { return callee_b (b); } /* { dg-message
> {parameter passing for argument of type 'B' when C\+\+17 is enabled
> changed to match C\+\+14 in GCC 10.1} } */
> +
> +/*
> +** _Z8caller_cR1C:
> +**	vldr.32	s0, \[r0\]
> +**	b	.*
> +*/
> +T (C, c); int caller_c (C &c) { return callee_c (c); } /* { dg-message {parameter
> passing for argument of type 'C' when C\+\+17 is enabled changed to match
> C\+\+14 in GCC 10.1} } */
> +
> +/*
> +** _Z8caller_dR1D:
> +**	ldm	r0, {r0, r1}
> +**	b	.*
> +*/
> +T (D, d); int caller_d (D &d) { return callee_d (d); } /* { dg-bogus {argument
> of type 'D'} } */
> +
> +/*
> +** _Z8caller_eR1E:
> +**	ldm	r0, {r0, r1}
> +**	b	.*
> +*/
> +T (E, e); int caller_e (E &e) { return callee_e (e); } /* { dg-bogus {argument of
> type 'E'} } */
> +
> +/*
> +** _Z8caller_fR1F:
> +**	vldr.32	s0, \[r0\]
> +**	b	.*
> +*/
> +T (F, f); int caller_f (F &f) { return callee_f (f); } /* { dg-message {parameter
> passing for argument of type 'F' with '\[\[no_unique_address\]\]' members
> changed in GCC 10.1} } */
> +
> +/*
> +** _Z8caller_gR1G:
> +**	vldr.32	s0, \[r0\]
> +**	b	.*
> +*/
> +T (G, g); int caller_g (G &g) { return callee_g (g); } /* { dg-message
> {parameter passing for argument of type 'G' with
> '\[\[no_unique_address\]\]' members changed in GCC 10.1} } */
> +
> +/*
> +** _Z8caller_hR1H:
> +**	ldm	r0, {r0, r1}
> +**	b	.*
> +*/
> +T (H, h); int caller_h (H &h) { return callee_h (h); } /* { dg-bogus {argument
> of type 'H'} } */
> +
> +/*
> +** _Z8caller_iR1I:
> +**	ldm	r0, {r0, r1}
> +**	b	.*
> +*/
> +T (I, i); int caller_i (I &i) { return callee_i (i); } /* { dg-bogus {argument of type
> 'I'} } */
> +
> +/*
> +** _Z8caller_jR1J:
> +**	vldr.32	s0, \[r0\]
> +**	vldr.32	s1, \[r0, #4\]
> +**	b	.*
> +*/
> +T (J, j); int caller_j (J &j) { return callee_j (j); } /* { dg-message {parameter
> passing for argument of type 'J' with '\[\[no_unique_address\]\]' members
> changed in GCC 10.1} } */
> +
> +/*
> +** _Z8caller_kR1K:
> +**	vldr.32	s0, \[r0\]
> +**	vldr.32	s1, \[r0, #4\]
> +**	b	.*
> +*/
> +T (K, k); int caller_k (K &k) { return callee_k (k); } /* { dg-message {parameter
> passing for argument of type 'K' with '\[\[no_unique_address\]\]' members
> changed in GCC 10.1} } */
> +
> +/*
> +** _Z8caller_lR1L:
> +**	ldm	r0, {r0, r1, r2}
> +**	b	.*
> +*/
> +T (L, l); int caller_l (L &l) { return callee_l (l); } /* { dg-bogus {argument of
> type 'L'} } */
> +
> +/*
> +** _Z8caller_mR1M:
> +**	ldm	r0, {r0, r1, r2}
> +**	b	.*
> +*/
> +T (M, m); int caller_m (M &m) { return callee_m (m); } /* { dg-bogus
> {argument of type 'M'} } */
> +
> +/*
> +** _Z8caller_nR1N:
> +**	vldr.32	s0, \[r0\]
> +**	vldr.32	s1, \[r0, #4\]
> +**	b	.*
> +*/
> +T (N, n); int caller_n (N &n) { return callee_n (n); } /* { dg-bogus {argument
> of type 'N'} } */
> +
> +/*
> +** _Z8caller_oR1O:
> +**	vldr.32	s0, \[r0\]
> +**	vldr.32	s1, \[r0, #4\]
> +**	b	.*
> +*/
> +T (O, o); int caller_o (O &o) { return callee_o (o); } /* { dg-bogus {argument
> of type 'O'} } */
> +
> +/*
> +** _Z8caller_pR1P:
> +**	vldr.32	s0, \[r0\]
> +**	vldr.32	s1, \[r0, #4\]
> +**	vldr.32	s2, \[r0, #8\]
> +**	vldr.32	s3, \[r0, #12\]
> +**	b	.*
> +*/
> +T (P, p); int caller_p (P &p) { return callee_p (p); } /* { dg-message
> {parameter passing for argument of type 'P' when C\+\+17 is enabled
> changed to match C\+\+14 in GCC 10.1} } */
> +
> +/*
> +** _Z8caller_qR1Q:
> +**	ldr	r0, \[r0\]
> +**	b	.*
> +*/
> +T (Q, q); int caller_q (Q &q) { return callee_q (q); } /* { dg-bogus {argument
> of type 'Q'} } */
> +
> +/*
> +** _Z8caller_rR1R:
> +**	ldr	r0, \[r0\]
> +**	b	.*
> +*/
> +T (R, r); int caller_r (R &r) { return callee_r (r); } /* { dg-bogus {argument of
> type 'R'} } */
> +
> +/*
> +** _Z8caller_sR1S:
> +**	vldr.32	s0, \[r0\]	@ int
> +**	b	.*
> +*/
> +T (Salias, s); int caller_s (Salias &s) { return callee_s (s); } /* { dg-bogus
> {argument of type 'S'} } */
> +
> +/*
> +** _Z8caller_tR1T:
> +**	vldr.32	s0, \[r0\]	@ int
> +**	b	.*
> +*/
> +T (Talias, t); int caller_t (Talias &t) { return callee_t (t); } /* { dg-message
> {parameter passing for argument of type 'T' with '\[\[no_unique_address\]\]'
> members changed in GCC 10.1} } */
> +
> +/*
> +** _Z8caller_uR1U:
> +**	vldr.32	s0, \[r0\]
> +**	vldr.32	s1, \[r0, #4\]
> +**	b	.*
> +*/
> +T (Ualias, u); int caller_u (Ualias &u) { return callee_u (u); } /* { dg-bogus
> {argument of type 'U'} } */
> +
> +/* { dg-bogus {argument of type 'const} "should not be printed as const"
> { target *-*-*} 0 } */
diff mbox series

Patch

diff --git a/gcc/doc/sourcebuild.texi b/gcc/doc/sourcebuild.texi
index b69612024be..d8da77d5ba4 100644
--- a/gcc/doc/sourcebuild.texi
+++ b/gcc/doc/sourcebuild.texi
@@ -1829,6 +1829,16 @@  Some multilibs may be incompatible with these options.
 ARM target supports @code{-mfpu=vfp3 -mfloat-abi=softfp}.
 Some multilibs may be incompatible with these options.
 
+@item arm_arch_v8a_hard_ok
+The compiler is targeting @code{arm*-*-*} and can compile and assemble code
+using the options @code{-march=armv8-a -mfpu=neon-fp-armv8 -mfloat-abi=hard}.
+This is not enough to guarantee that linking works.
+
+@item arm_arch_v8a_hard_multilib
+The compiler is targeting @code{arm*-*-*} and can build programs using
+the options @code{-march=armv8-a -mfpu=neon-fp-armv8 -mfloat-abi=hard}.
+The target can also run the resulting binaries.
+
 @item arm_v8_vfp_ok
 ARM target supports @code{-mfpu=fp-armv8 -mfloat-abi=softfp}.
 Some multilibs may be incompatible with these options.
@@ -2586,6 +2596,11 @@  the @ref{arm_neon_fp16_ok,,arm_neon_fp16_ok effective target keyword}.
 arm vfp3 floating point support; see
 the @ref{arm_vfp3_ok,,arm_vfp3_ok effective target keyword}.
 
+@item arm_arch_v8a_hard
+Add options for ARMv8-A and the hard-float variant of the AAPCS,
+if this is supported by the compiler; see the
+@ref{arm_arch_v8a_hard_ok,,arm_arch_v8a_hard_ok} effective target keyword.
+
 @item arm_v8_1a_neon
 Add options for ARMv8.1-A with Adv.SIMD support, if this is supported
 by the target; see the @ref{arm_v8_1a_neon_ok,,arm_v8_1a_neon_ok}
diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c
index 30a2a3aeb0e..dcaae7bf7aa 100644
--- a/gcc/config/arm/arm.c
+++ b/gcc/config/arm/arm.c
@@ -5963,6 +5963,8 @@  arm_return_in_memory (const_tree type, const_tree fntype)
 
       /* Find the first field, ignoring non FIELD_DECL things which will
 	 have been created by C++.  */
+      /* NOTE: This code is deprecated and has not been updated to handle
+	 DECL_FIELD_ABI_IGNORED.  */
       for (field = TYPE_FIELDS (type);
 	   field && TREE_CODE (field) != FIELD_DECL;
 	   field = DECL_CHAIN (field))
@@ -6135,23 +6137,42 @@  aapcs_vfp_cum_init (CUMULATIVE_ARGS *pcum  ATTRIBUTE_UNUSED,
   pcum->aapcs_vfp_reg_alloc = 0;
 }
 
+/* Bitmasks that indicate whether earlier versions of GCC would have
+   taken a different path through the ABI logic.  This should result in
+   a -Wpsabi warning if the earlier path led to a different ABI decision.
+
+   WARN_PSABI_EMPTY_CXX17_BASE
+      Indicates that the type includes an artificial empty C++17 base field
+      that, prior to GCC 10.1, would prevent the type from being treated as
+      a HFA or HVA.  See PR94711 for details.
+
+   WARN_PSABI_NO_UNIQUE_ADDRESS
+      Indicates that the type includes an empty [[no_unique_address]] field
+      that, prior to GCC 10.1, would prevent the type from being treated as
+      a HFA or HVA.  */
+const unsigned int WARN_PSABI_EMPTY_CXX17_BASE = 1U << 0;
+const unsigned int WARN_PSABI_NO_UNIQUE_ADDRESS = 1U << 1;
+
 /* 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.
 
-   The AVOID_CXX17_EMPTY_BASE argument is to allow the caller to check whether
-   this function has changed its behavior after the fix for PR94384 -- this fix
-   is to avoid artificial fields in empty base classes.
-   When called with this argument as a NULL pointer 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, this function avoids such artificial fields
-   and sets the value to TRUE when one of these fields has been set.  */
+   The WARN_PSABI_FLAGS argument allows the caller to check whether this
+   function has changed its behavior relative to earlier versions of GCC.
+   Normally the argument should be nonnull and point to a zero-initialized
+   variable.  The function then records whether the ABI decision might
+   be affected by a known fix to the ABI logic, setting the associated
+   WARN_PSABI_* bits if so.
+
+   When the argument is instead a null pointer, the function tries to
+   simulate the behavior of GCC before all such ABI fixes were made.
+   This is useful to check whether the function returns something
+   different after the ABI fixes.  */
 static int
 aapcs_vfp_sub_candidate (const_tree type, machine_mode *modep,
-			 bool *avoid_cxx17_empty_base)
+			 unsigned int *warn_psabi_flags)
 {
   machine_mode mode;
   HOST_WIDE_INT size;
@@ -6224,7 +6245,7 @@  aapcs_vfp_sub_candidate (const_tree type, machine_mode *modep,
 	  return -1;
 
 	count = aapcs_vfp_sub_candidate (TREE_TYPE (type), modep,
-					 avoid_cxx17_empty_base);
+					 warn_psabi_flags);
 	if (count == -1
 	    || !index
 	    || !TYPE_MAX_VALUE (index)
@@ -6262,20 +6283,30 @@  aapcs_vfp_sub_candidate (const_tree type, machine_mode *modep,
 	    if (TREE_CODE (field) != FIELD_DECL)
 	      continue;
 
-	    /* Ignore C++17 empty base fields, while their type indicates they
-	       contain padding, this is only sometimes contributed to the derived
-	       class.
-	       When the padding is contributed to the derived class that's
-	       caught by the general test for padding below.  */
-	    if (cxx17_empty_base_field_p (field)
-		&& avoid_cxx17_empty_base)
+	    if (DECL_FIELD_ABI_IGNORED (field))
 	      {
-		*avoid_cxx17_empty_base = true;
-		continue;
+		/* See whether this is something that earlier versions of
+		   GCC failed to ignore.  */
+		unsigned int flag;
+		if (lookup_attribute ("no_unique_address",
+				      DECL_ATTRIBUTES (field)))
+		  flag = WARN_PSABI_NO_UNIQUE_ADDRESS;
+		else if (cxx17_empty_base_field_p (field))
+		  flag = WARN_PSABI_EMPTY_CXX17_BASE;
+		else
+		  /* No compatibility problem.  */
+		  continue;
+
+		/* Simulate the old behavior when WARN_PSABI_FLAGS is null.  */
+		if (warn_psabi_flags)
+		  {
+		    *warn_psabi_flags |= flag;
+		    continue;
+		  }
 	      }
 
 	    sub_count = aapcs_vfp_sub_candidate (TREE_TYPE (field), modep,
-						 avoid_cxx17_empty_base);
+						 warn_psabi_flags);
 	    if (sub_count < 0)
 	      return -1;
 	    count += sub_count;
@@ -6309,7 +6340,7 @@  aapcs_vfp_sub_candidate (const_tree type, machine_mode *modep,
 	      continue;
 
 	    sub_count = aapcs_vfp_sub_candidate (TREE_TYPE (field), modep,
-						 avoid_cxx17_empty_base);
+						 warn_psabi_flags);
 	    if (sub_count < 0)
 	      return -1;
 	    count = count > sub_count ? count : sub_count;
@@ -6371,24 +6402,32 @@  aapcs_vfp_is_call_or_return_candidate (enum arm_pcs pcs_variant,
      out from the mode.  */
   if (type)
     {
-      bool avoided = false;
-      int ag_count = aapcs_vfp_sub_candidate (type, &new_mode, &avoided);
+      unsigned int warn_psabi_flags = 0;
+      int ag_count = aapcs_vfp_sub_candidate (type, &new_mode,
+					      &warn_psabi_flags);
       if (ag_count > 0 && ag_count <= 4)
 	{
 	  static unsigned last_reported_type_uid;
 	  unsigned uid = TYPE_UID (TYPE_MAIN_VARIANT (type));
 	  int alt;
 	  if (warn_psabi
-	      && avoided
+	      && warn_psabi_flags
 	      && uid != last_reported_type_uid
 	      && ((alt = aapcs_vfp_sub_candidate (type, &new_mode, NULL))
 		  != ag_count))
 	    {
 	      gcc_assert (alt == -1);
 	      last_reported_type_uid = uid;
-	      inform (input_location, "parameter passing for argument of type "
-		      "%qT when C++17 is enabled changed to match C++14 "
-		      "in GCC 10.1", type);
+	      /* Use TYPE_MAIN_VARIANT to strip any redundant const
+		 qualification.  */
+	      if (warn_psabi_flags & WARN_PSABI_NO_UNIQUE_ADDRESS)
+		inform (input_location, "parameter passing for argument of "
+			"type %qT with %<[[no_unique_address]]%> members "
+			"changed in GCC 10.1", TYPE_MAIN_VARIANT (type));
+	      else if (warn_psabi_flags & WARN_PSABI_EMPTY_CXX17_BASE)
+		inform (input_location, "parameter passing for argument of "
+			"type %qT when C++17 is enabled changed to match "
+			"C++14 in GCC 10.1", TYPE_MAIN_VARIANT (type));
 	    }
 	  *count = ag_count;
 	}
@@ -6933,7 +6972,20 @@  arm_needs_doubleword_align (machine_mode mode, const_tree type)
 
   int ret = 0;
   int ret2 = 0;
-  /* Record/aggregate types: Use greatest member alignment of any member.  */
+  /* Record/aggregate types: Use greatest member alignment of any member.
+
+     Note that we explicitly consider zero-sized fields here, even though
+     they don't map to AAPCS machine types.  For example, in:
+
+	 struct __attribute__((aligned(8))) empty {};
+
+	 struct s {
+	   [[no_unique_address]] empty e;
+	   int x;
+	 };
+
+     "s" contains only one Fundamental Data Type (the int field)
+     but gains 8-byte alignment and size thanks to "e".  */
   for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
     if (DECL_ALIGN (field) > PARM_BOUNDARY)
       {
diff --git a/gcc/testsuite/lib/target-supports.exp b/gcc/testsuite/lib/target-supports.exp
index f416d5cafda..13761491e63 100644
--- a/gcc/testsuite/lib/target-supports.exp
+++ b/gcc/testsuite/lib/target-supports.exp
@@ -4443,6 +4443,7 @@  foreach { armfunc armflag armdefs } {
 	v7ve "-march=armv7ve -marm"
 		"__ARM_ARCH_7A__ && __ARM_FEATURE_IDIV"
 	v8a "-march=armv8-a" __ARM_ARCH_8A__
+	v8a_hard "-march=armv8-a -mfpu=neon-fp-armv8 -mfloat-abi=hard" __ARM_ARCH_8A__
 	v8_1a "-march=armv8.1-a" __ARM_ARCH_8A__
 	v8_2a "-march=armv8.2-a" __ARM_ARCH_8A__
 	v8r "-march=armv8-r" __ARM_ARCH_8R__
diff --git a/gcc/testsuite/g++.target/arm/no_unique_address_1.C b/gcc/testsuite/g++.target/arm/no_unique_address_1.C
new file mode 100644
index 00000000000..038aa00a499
--- /dev/null
+++ b/gcc/testsuite/g++.target/arm/no_unique_address_1.C
@@ -0,0 +1,201 @@ 
+/* { dg-require-effective-target arm_arch_v8a_hard_ok } */
+/* { dg-options "-std=c++11 -O -foptimize-sibling-calls" } */
+/* { dg-add-options arm_arch_v8a_hard } */
+/* { dg-final { check-function-bodies "**" "" "" } } */
+
+struct X { };
+struct Y { int : 0; };
+struct Z { int : 0; Y y; };
+struct W : public X { X q; };
+
+struct A { float a; };
+
+struct B : public X { float a; };
+struct C : public Y { float a; };
+struct D : public Z { float a; };
+struct E : public W { float a; };
+
+struct F { [[no_unique_address]] X x; float a; };
+struct G { [[no_unique_address]] Y y; float a; };
+struct H { [[no_unique_address]] Z z; float a; };
+struct I { [[no_unique_address]] W w; float a; };
+
+struct J { float a; [[no_unique_address]] X x; float b; };
+struct K { float a; [[no_unique_address]] Y y; float b; };
+struct L { float a; [[no_unique_address]] Z z; float b; };
+struct M { float a; [[no_unique_address]] W w; float b; };
+
+struct N : public A { float b; };
+struct O { [[no_unique_address]] A a; float b; };
+
+struct P : public Y { int : 0; float a, b, c, d; };
+
+union Q { X x; float a; };
+union R { [[no_unique_address]] X x; float a; };
+
+union S { A a; float b; };
+union T { F f; float b; };
+union U { N n; O o; };
+
+typedef S Salias;
+typedef T Talias;
+typedef U Ualias;
+
+#define T(S, s) extern int callee_##s (S)
+
+/*
+** _Z8caller_aR1A:
+**	vldr.32	s0, \[r0\]
+**	b	.*
+*/
+T (A, a); int caller_a (A &a) { return callee_a (a); } /* { dg-bogus {argument of type 'A'} } */
+
+/*
+** _Z8caller_bR1B:
+**	vldr.32	s0, \[r0\]
+**	b	.*
+*/
+T (B, b); int caller_b (B &b) { return callee_b (b); } /* { dg-bogus {argument of type 'B'} } */
+
+/*
+** _Z8caller_cR1C:
+**	vldr.32	s0, \[r0\]
+**	b	.*
+*/
+T (C, c); int caller_c (C &c) { return callee_c (c); } /* { dg-bogus {argument of type 'C'} } */
+
+/*
+** _Z8caller_dR1D:
+**	ldm	r0, {r0, r1}
+**	b	.*
+*/
+T (D, d); int caller_d (D &d) { return callee_d (d); } /* { dg-bogus {argument of type 'D'} } */
+
+/*
+** _Z8caller_eR1E:
+**	ldm	r0, {r0, r1}
+**	b	.*
+*/
+T (E, e); int caller_e (E &e) { return callee_e (e); } /* { dg-bogus {argument of type 'E'} } */
+
+/*
+** _Z8caller_fR1F:
+**	vldr.32	s0, \[r0\]
+**	b	.*
+*/
+T (F, f); int caller_f (F &f) { return callee_f (f); } /* { dg-message {parameter passing for argument of type 'F' with '\[\[no_unique_address\]\]' members changed in GCC 10.1} } */
+
+/*
+** _Z8caller_gR1G:
+**	vldr.32	s0, \[r0\]
+**	b	.*
+*/
+T (G, g); int caller_g (G &g) { return callee_g (g); } /* { dg-message {parameter passing for argument of type 'G' with '\[\[no_unique_address\]\]' members changed in GCC 10.1} } */
+
+/*
+** _Z8caller_hR1H:
+**	ldm	r0, {r0, r1}
+**	b	.*
+*/
+T (H, h); int caller_h (H &h) { return callee_h (h); } /* { dg-bogus {argument of type 'H'} } */
+
+/*
+** _Z8caller_iR1I:
+**	ldm	r0, {r0, r1}
+**	b	.*
+*/
+T (I, i); int caller_i (I &i) { return callee_i (i); } /* { dg-bogus {argument of type 'I'} } */
+
+/*
+** _Z8caller_jR1J:
+**	vldr.32	s0, \[r0\]
+**	vldr.32	s1, \[r0, #4\]
+**	b	.*
+*/
+T (J, j); int caller_j (J &j) { return callee_j (j); } /* { dg-message {parameter passing for argument of type 'J' with '\[\[no_unique_address\]\]' members changed in GCC 10.1} } */
+
+/*
+** _Z8caller_kR1K:
+**	vldr.32	s0, \[r0\]
+**	vldr.32	s1, \[r0, #4\]
+**	b	.*
+*/
+T (K, k); int caller_k (K &k) { return callee_k (k); } /* { dg-message {parameter passing for argument of type 'K' with '\[\[no_unique_address\]\]' members changed in GCC 10.1} } */
+
+/*
+** _Z8caller_lR1L:
+**	ldm	r0, {r0, r1, r2}
+**	b	.*
+*/
+T (L, l); int caller_l (L &l) { return callee_l (l); } /* { dg-bogus {argument of type 'L'} } */
+
+/*
+** _Z8caller_mR1M:
+**	ldm	r0, {r0, r1, r2}
+**	b	.*
+*/
+T (M, m); int caller_m (M &m) { return callee_m (m); } /* { dg-bogus {argument of type 'M'} } */
+
+/*
+** _Z8caller_nR1N:
+**	vldr.32	s0, \[r0\]
+**	vldr.32	s1, \[r0, #4\]
+**	b	.*
+*/
+T (N, n); int caller_n (N &n) { return callee_n (n); } /* { dg-bogus {argument of type 'N'} } */
+
+/*
+** _Z8caller_oR1O:
+**	vldr.32	s0, \[r0\]
+**	vldr.32	s1, \[r0, #4\]
+**	b	.*
+*/
+T (O, o); int caller_o (O &o) { return callee_o (o); } /* { dg-bogus {argument of type 'O'} } */
+
+/*
+** _Z8caller_pR1P:
+**	vldr.32	s0, \[r0\]
+**	vldr.32	s1, \[r0, #4\]
+**	vldr.32	s2, \[r0, #8\]
+**	vldr.32	s3, \[r0, #12\]
+**	b	.*
+*/
+T (P, p); int caller_p (P &p) { return callee_p (p); } /* { dg-bogus {argument of type 'P'} } */
+
+/*
+** _Z8caller_qR1Q:
+**	ldr	r0, \[r0\]
+**	b	.*
+*/
+T (Q, q); int caller_q (Q &q) { return callee_q (q); } /* { dg-bogus {argument of type 'Q'} } */
+
+/*
+** _Z8caller_rR1R:
+**	ldr	r0, \[r0\]
+**	b	.*
+*/
+T (R, r); int caller_r (R &r) { return callee_r (r); } /* { dg-bogus {argument of type 'R'} } */
+
+/*
+** _Z8caller_sR1S:
+**	vldr.32	s0, \[r0\]	@ int
+**	b	.*
+*/
+T (Salias, s); int caller_s (Salias &s) { return callee_s (s); } /* { dg-bogus {argument of type 'S'} } */
+
+/*
+** _Z8caller_tR1T:
+**	vldr.32	s0, \[r0\]	@ int
+**	b	.*
+*/
+T (Talias, t); int caller_t (Talias &t) { return callee_t (t); } /* { dg-message {parameter passing for argument of type 'T' with '\[\[no_unique_address\]\]' members changed in GCC 10.1} } */
+
+/*
+** _Z8caller_uR1U:
+**	vldr.32	s0, \[r0\]
+**	vldr.32	s1, \[r0, #4\]
+**	b	.*
+*/
+T (Ualias, u); int caller_u (Ualias &u) { return callee_u (u); } /* { dg-bogus {argument of type 'U'} } */
+
+/* { dg-bogus {argument of type 'const} "should not be printed as const" { target *-*-*} 0 } */
diff --git a/gcc/testsuite/g++.target/arm/no_unique_address_2.C b/gcc/testsuite/g++.target/arm/no_unique_address_2.C
new file mode 100644
index 00000000000..8be5de2539a
--- /dev/null
+++ b/gcc/testsuite/g++.target/arm/no_unique_address_2.C
@@ -0,0 +1,201 @@ 
+/* { dg-require-effective-target arm_arch_v8a_hard_ok } */
+/* { dg-options "-std=c++17 -O -foptimize-sibling-calls" } */
+/* { dg-add-options arm_arch_v8a_hard } */
+/* { dg-final { check-function-bodies "**" "" "" } } */
+
+struct X { };
+struct Y { int : 0; };
+struct Z { int : 0; Y y; };
+struct W : public X { X q; };
+
+struct A { float a; };
+
+struct B : public X { float a; };
+struct C : public Y { float a; };
+struct D : public Z { float a; };
+struct E : public W { float a; };
+
+struct F { [[no_unique_address]] X x; float a; };
+struct G { [[no_unique_address]] Y y; float a; };
+struct H { [[no_unique_address]] Z z; float a; };
+struct I { [[no_unique_address]] W w; float a; };
+
+struct J { float a; [[no_unique_address]] X x; float b; };
+struct K { float a; [[no_unique_address]] Y y; float b; };
+struct L { float a; [[no_unique_address]] Z z; float b; };
+struct M { float a; [[no_unique_address]] W w; float b; };
+
+struct N : public A { float b; };
+struct O { [[no_unique_address]] A a; float b; };
+
+struct P : public Y { int : 0; float a, b, c, d; };
+
+union Q { X x; float a; };
+union R { [[no_unique_address]] X x; float a; };
+
+union S { A a; float b; };
+union T { F f; float b; };
+union U { N n; O o; };
+
+typedef S Salias;
+typedef T Talias;
+typedef U Ualias;
+
+#define T(S, s) extern int callee_##s (S)
+
+/*
+** _Z8caller_aR1A:
+**	vldr.32	s0, \[r0\]
+**	b	.*
+*/
+T (A, a); int caller_a (A &a) { return callee_a (a); } /* { dg-bogus {argument of type 'A'} } */
+
+/*
+** _Z8caller_bR1B:
+**	vldr.32	s0, \[r0\]
+**	b	.*
+*/
+T (B, b); int caller_b (B &b) { return callee_b (b); } /* { dg-message {parameter passing for argument of type 'B' when C\+\+17 is enabled changed to match C\+\+14 in GCC 10.1} } */
+
+/*
+** _Z8caller_cR1C:
+**	vldr.32	s0, \[r0\]
+**	b	.*
+*/
+T (C, c); int caller_c (C &c) { return callee_c (c); } /* { dg-message {parameter passing for argument of type 'C' when C\+\+17 is enabled changed to match C\+\+14 in GCC 10.1} } */
+
+/*
+** _Z8caller_dR1D:
+**	ldm	r0, {r0, r1}
+**	b	.*
+*/
+T (D, d); int caller_d (D &d) { return callee_d (d); } /* { dg-bogus {argument of type 'D'} } */
+
+/*
+** _Z8caller_eR1E:
+**	ldm	r0, {r0, r1}
+**	b	.*
+*/
+T (E, e); int caller_e (E &e) { return callee_e (e); } /* { dg-bogus {argument of type 'E'} } */
+
+/*
+** _Z8caller_fR1F:
+**	vldr.32	s0, \[r0\]
+**	b	.*
+*/
+T (F, f); int caller_f (F &f) { return callee_f (f); } /* { dg-message {parameter passing for argument of type 'F' with '\[\[no_unique_address\]\]' members changed in GCC 10.1} } */
+
+/*
+** _Z8caller_gR1G:
+**	vldr.32	s0, \[r0\]
+**	b	.*
+*/
+T (G, g); int caller_g (G &g) { return callee_g (g); } /* { dg-message {parameter passing for argument of type 'G' with '\[\[no_unique_address\]\]' members changed in GCC 10.1} } */
+
+/*
+** _Z8caller_hR1H:
+**	ldm	r0, {r0, r1}
+**	b	.*
+*/
+T (H, h); int caller_h (H &h) { return callee_h (h); } /* { dg-bogus {argument of type 'H'} } */
+
+/*
+** _Z8caller_iR1I:
+**	ldm	r0, {r0, r1}
+**	b	.*
+*/
+T (I, i); int caller_i (I &i) { return callee_i (i); } /* { dg-bogus {argument of type 'I'} } */
+
+/*
+** _Z8caller_jR1J:
+**	vldr.32	s0, \[r0\]
+**	vldr.32	s1, \[r0, #4\]
+**	b	.*
+*/
+T (J, j); int caller_j (J &j) { return callee_j (j); } /* { dg-message {parameter passing for argument of type 'J' with '\[\[no_unique_address\]\]' members changed in GCC 10.1} } */
+
+/*
+** _Z8caller_kR1K:
+**	vldr.32	s0, \[r0\]
+**	vldr.32	s1, \[r0, #4\]
+**	b	.*
+*/
+T (K, k); int caller_k (K &k) { return callee_k (k); } /* { dg-message {parameter passing for argument of type 'K' with '\[\[no_unique_address\]\]' members changed in GCC 10.1} } */
+
+/*
+** _Z8caller_lR1L:
+**	ldm	r0, {r0, r1, r2}
+**	b	.*
+*/
+T (L, l); int caller_l (L &l) { return callee_l (l); } /* { dg-bogus {argument of type 'L'} } */
+
+/*
+** _Z8caller_mR1M:
+**	ldm	r0, {r0, r1, r2}
+**	b	.*
+*/
+T (M, m); int caller_m (M &m) { return callee_m (m); } /* { dg-bogus {argument of type 'M'} } */
+
+/*
+** _Z8caller_nR1N:
+**	vldr.32	s0, \[r0\]
+**	vldr.32	s1, \[r0, #4\]
+**	b	.*
+*/
+T (N, n); int caller_n (N &n) { return callee_n (n); } /* { dg-bogus {argument of type 'N'} } */
+
+/*
+** _Z8caller_oR1O:
+**	vldr.32	s0, \[r0\]
+**	vldr.32	s1, \[r0, #4\]
+**	b	.*
+*/
+T (O, o); int caller_o (O &o) { return callee_o (o); } /* { dg-bogus {argument of type 'O'} } */
+
+/*
+** _Z8caller_pR1P:
+**	vldr.32	s0, \[r0\]
+**	vldr.32	s1, \[r0, #4\]
+**	vldr.32	s2, \[r0, #8\]
+**	vldr.32	s3, \[r0, #12\]
+**	b	.*
+*/
+T (P, p); int caller_p (P &p) { return callee_p (p); } /* { dg-message {parameter passing for argument of type 'P' when C\+\+17 is enabled changed to match C\+\+14 in GCC 10.1} } */
+
+/*
+** _Z8caller_qR1Q:
+**	ldr	r0, \[r0\]
+**	b	.*
+*/
+T (Q, q); int caller_q (Q &q) { return callee_q (q); } /* { dg-bogus {argument of type 'Q'} } */
+
+/*
+** _Z8caller_rR1R:
+**	ldr	r0, \[r0\]
+**	b	.*
+*/
+T (R, r); int caller_r (R &r) { return callee_r (r); } /* { dg-bogus {argument of type 'R'} } */
+
+/*
+** _Z8caller_sR1S:
+**	vldr.32	s0, \[r0\]	@ int
+**	b	.*
+*/
+T (Salias, s); int caller_s (Salias &s) { return callee_s (s); } /* { dg-bogus {argument of type 'S'} } */
+
+/*
+** _Z8caller_tR1T:
+**	vldr.32	s0, \[r0\]	@ int
+**	b	.*
+*/
+T (Talias, t); int caller_t (Talias &t) { return callee_t (t); } /* { dg-message {parameter passing for argument of type 'T' with '\[\[no_unique_address\]\]' members changed in GCC 10.1} } */
+
+/*
+** _Z8caller_uR1U:
+**	vldr.32	s0, \[r0\]
+**	vldr.32	s1, \[r0, #4\]
+**	b	.*
+*/
+T (Ualias, u); int caller_u (Ualias &u) { return callee_u (u); } /* { dg-bogus {argument of type 'U'} } */
+
+/* { dg-bogus {argument of type 'const} "should not be printed as const" { target *-*-*} 0 } */