From 575ac27fd5f18ffc9cfce8a99e987f52c5b898c9 Mon Sep 17 00:00:00 2001
From: Jakub Jelinek <jakub@redhat.com>
Date: Wed, 29 Apr 2020 09:01:49 +0200
Subject: [PATCH] c++, middle-end, rs6000: Fix C++17 ABI incompatibilities
 during class layout and [[no_unique_address]] handling [PR94707]

As reported by Iain and David, powerpc-darwin and powerpc-aix* have C++14
vs. C++17 ABI incompatibilities which are not fixed by mere adding of
cxx17_empty_base_field_p calls.  Unlike the issues that were seen on other
targets where the artificial empty base field affected function argument
passing or returning of values, on these two targets the difference is
during class layout, not afterwards (e.g.
struct empty_base {};
struct S : public empty_base { unsigned long long l[2]; };
will have different __alignof__ (S) between C++14 and C++17 (or possibly
with double instead of unsigned long long too)).

I've tried:
struct X { };
struct Y { int : 0; };
struct Z { int : 0; Y y; };
struct U : public X { X q; };
struct A { float a, b, c, d; };
struct B : public X { float a, b, c, d; };
struct C : public Y { float a, b, c, d; };
struct D : public Z { float a, b, c, d; };
struct E : public U { float a, b, c, d; };
struct F { [[no_unique_address]] X x; float a, b, c, d; };
struct G { [[no_unique_address]] Y y; float a, b, c, d; };
struct H { [[no_unique_address]] Z z; float a, b, c, d; };
struct I { [[no_unique_address]] U u; float a, b, c, d; };
struct J { float a, b; [[no_unique_address]] X x; float c, d; };
struct K { float a, b; [[no_unique_address]] Y y; float c, d; };
struct L { float a, b; [[no_unique_address]] Z z; float c, d; };
struct M { float a, b; [[no_unique_address]] U u; float c, d; };
 #define T(S, s) extern S s; extern void foo##s (S); int bar##s () { foo##s (s); return 0; }
T (A, a)
T (B, b)
T (C, c)
T (D, d)
T (E, e)
T (F, f)
T (G, g)
T (H, h)
T (I, i)
T (J, j)
T (K, k)
T (L, l)
T (M, m)
testcase on powerpc64-linux.  Results:
G++ 9 -std=c++14                A, B, C passed in fprs, the rest in gprs
G++ 9 -std=c++17                A passed in fprs, the rest in gprs
current trunk -std=c++14 & 17   A, B, C passed in fprs, the rest in gprs
patched trunk -std=c++14 & 17   A, B, C, F, G, J, K passed in fprs, the rest in gprs
clang++ [*] -std=c++14 & 17     A, B, C, F, G, J, K passed in fprs, the rest in gprs
[*] clang version 11.0.0 (git@github.com:llvm/llvm-project.git 5c352e69e76a26e4eda075e20aa6a9bb7686042c)

Is that what we want?  I think it matches the stated intent of P0840R2 or
what Jason/Jonathan said, and doing something different like e.g. not
treating C, G and K as homogenous because of the int : 0 in empty bases
or in zero sized [[no_unique_address] fields would be quite hard to
implement (because for C++14 the FIELD_DECL just isn't there).

2020-04-29  Jakub Jelinek  <jakub@redhat.com>

	PR target/94707
	* tree-core.h (tree_decl_common): Note decl_flag_0 used for
	DECL_FIELD_ABI_IGNORED.
	* tree.h (DECL_FIELD_ABI_IGNORED): Define.
	* calls.h (cxx17_empty_base_field_p): Change into a temporary
	macro, check DECL_FIELD_ABI_IGNORED flag with no "no_unique_address"
	attribute.
	* calls.c (cxx17_empty_base_field_p): Remove.
	* tree-streamer-out.c (pack_ts_decl_common_value_fields): Handle
	DECL_FIELD_ABI_IGNORED.
	* tree-streamer-in.c (unpack_ts_decl_common_value_fields): Likewise.
	* lto-streamer-out.c (hash_tree): Likewise.
	* config/rs6000/rs6000-call.c (rs6000_aggregate_candidate): Rename
	cxx17_empty_base_seen to empty_base_seen, change type to int *,
	adjust recursive calls, use DECL_FIELD_ABI_IGNORED instead of
	cxx17_empty_base_field_p, if "no_unique_address" attribute is
	present, propagate that to the caller too.
	(rs6000_discover_homogeneous_aggregate): Adjust
	rs6000_aggregate_candidate caller, emit different diagnostics
	when c++17 empty base fields are present and when empty
	[[no_unique_address]] fields are present.
	* config/rs6000/rs6000.c (rs6000_special_round_type_align,
	darwin_rs6000_special_round_type_align): Skip DECL_FIELD_ABI_IGNORED
	fields.

	* class.c (build_base_field): Set DECL_FIELD_ABI_IGNORED on C++17 empty
	base artificial FIELD_DECLs.
	(layout_class_type): Set DECL_FIELD_ABI_IGNORED on empty class
	field_poverlapping_p FIELD_DECLs.

	* lto-common.c (compare_tree_sccs_1): Handle DECL_FIELD_ABI_IGNORED.

	* g++.target/powerpc/pr94707-1.C: New test.
	* g++.target/powerpc/pr94707-2.C: New test.
	* g++.target/powerpc/pr94707-3.C: New test.
	* g++.target/powerpc/pr94707-4.C: New test.
	* g++.target/powerpc/pr94707-5.C: New test.
	* g++.target/powerpc/pr94707-4.C: New test.
---
 gcc/ChangeLog                                | 27 +++++++++++++
 gcc/calls.c                                  | 18 ---------
 gcc/calls.h                                  |  5 ++-
 gcc/config/rs6000/rs6000-call.c              | 36 +++++++++++-------
 gcc/config/rs6000/rs6000.c                   |  8 +++-
 gcc/cp/ChangeLog                             |  8 ++++
 gcc/cp/class.c                               |  6 ++-
 gcc/lto-streamer-out.c                       |  1 +
 gcc/lto/ChangeLog                            |  5 +++
 gcc/lto/lto-common.c                         |  1 +
 gcc/testsuite/ChangeLog                      | 10 +++++
 gcc/testsuite/g++.target/powerpc/pr94707-1.C | 38 +++++++++++++++++++
 gcc/testsuite/g++.target/powerpc/pr94707-2.C | 34 +++++++++++++++++
 gcc/testsuite/g++.target/powerpc/pr94707-3.C | 40 ++++++++++++++++++++
 gcc/testsuite/g++.target/powerpc/pr94707-4.C | 34 +++++++++++++++++
 gcc/testsuite/g++.target/powerpc/pr94707-5.C | 35 +++++++++++++++++
 gcc/testsuite/g++.target/powerpc/pr94707-6.C |  6 +++
 gcc/tree-core.h                              |  3 +-
 gcc/tree-streamer-in.c                       |  1 +
 gcc/tree-streamer-out.c                      |  1 +
 gcc/tree.h                                   |  7 ++++
 21 files changed, 288 insertions(+), 36 deletions(-)
 create mode 100644 gcc/testsuite/g++.target/powerpc/pr94707-1.C
 create mode 100644 gcc/testsuite/g++.target/powerpc/pr94707-2.C
 create mode 100644 gcc/testsuite/g++.target/powerpc/pr94707-3.C
 create mode 100644 gcc/testsuite/g++.target/powerpc/pr94707-4.C
 create mode 100644 gcc/testsuite/g++.target/powerpc/pr94707-5.C
 create mode 100644 gcc/testsuite/g++.target/powerpc/pr94707-6.C

diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index c4afdb053f72..738a74def202 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,30 @@
+2020-04-29  Jakub Jelinek  <jakub@redhat.com>
+
+	PR target/94707
+	* tree-core.h (tree_decl_common): Note decl_flag_0 used for
+	DECL_FIELD_ABI_IGNORED.
+	* tree.h (DECL_FIELD_ABI_IGNORED): Define.
+	* calls.h (cxx17_empty_base_field_p): Change into a temporary
+	macro, check DECL_FIELD_ABI_IGNORED flag with no "no_unique_address"
+	attribute.
+	* calls.c (cxx17_empty_base_field_p): Remove.
+	* tree-streamer-out.c (pack_ts_decl_common_value_fields): Handle
+	DECL_FIELD_ABI_IGNORED.
+	* tree-streamer-in.c (unpack_ts_decl_common_value_fields): Likewise.
+	* lto-streamer-out.c (hash_tree): Likewise.
+	* config/rs6000/rs6000-call.c (rs6000_aggregate_candidate): Rename
+	cxx17_empty_base_seen to empty_base_seen, change type to int *,
+	adjust recursive calls, use DECL_FIELD_ABI_IGNORED instead of
+	cxx17_empty_base_field_p, if "no_unique_address" attribute is
+	present, propagate that to the caller too.
+	(rs6000_discover_homogeneous_aggregate): Adjust
+	rs6000_aggregate_candidate caller, emit different diagnostics
+	when c++17 empty base fields are present and when empty
+	[[no_unique_address]] fields are present.
+	* config/rs6000/rs6000.c (rs6000_special_round_type_align,
+	darwin_rs6000_special_round_type_align): Skip DECL_FIELD_ABI_IGNORED
+	fields.
+
 2020-04-29  Richard Biener  <rguenther@suse.de>
 
 	* tree-ssa-loop-im.c (ref_always_accessed::operator ()):
diff --git a/gcc/calls.c b/gcc/calls.c
index 9ac7f949737d..5bd922779af7 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -6261,23 +6261,5 @@ must_pass_va_arg_in_stack (tree type)
   return targetm.calls.must_pass_in_stack (arg);
 }
 
-/* Return true if FIELD is the C++17 empty base field that should
-   be ignored for ABI calling convention decisions in order to
-   maintain ABI compatibility between C++14 and earlier, which doesn't
-   add this FIELD to classes with empty bases, and C++17 and later
-   which does.  */
-
-bool
-cxx17_empty_base_field_p (const_tree field)
-{
-  return (TREE_CODE (field) == FIELD_DECL
-	  && DECL_ARTIFICIAL (field)
-	  && RECORD_OR_UNION_TYPE_P (TREE_TYPE (field))
-	  && DECL_SIZE (field)
-	  && integer_zerop (DECL_SIZE (field))
-	  && TYPE_SIZE (TREE_TYPE (field))
-	  && !integer_zerop (TYPE_SIZE (TREE_TYPE (field))));
-}
-
 /* Tell the garbage collector about GTY markers in this source file.  */
 #include "gt-calls.h"
diff --git a/gcc/calls.h b/gcc/calls.h
index 4ee493607774..e1c944efbb6f 100644
--- a/gcc/calls.h
+++ b/gcc/calls.h
@@ -135,6 +135,9 @@ extern tree get_attr_nonstring_decl (tree, tree * = NULL);
 extern void maybe_warn_nonstring_arg (tree, tree);
 extern bool get_size_range (tree, tree[2], bool = false);
 extern rtx rtx_for_static_chain (const_tree, bool);
-extern bool cxx17_empty_base_field_p (const_tree);
+/* FIXME: Remove after all backends are converted.  */
+#define cxx17_empty_base_field_p(t) \
+  (DECL_FIELD_ABI_IGNORED (t)						\
+   && !lookup_attribute ("no_unique_address", DECL_ATTRIBUTES (t)))
 
 #endif // GCC_CALLS_H
diff --git a/gcc/config/rs6000/rs6000-call.c b/gcc/config/rs6000/rs6000-call.c
index d4875ddeeb5c..800ac2b5b5e1 100644
--- a/gcc/config/rs6000/rs6000-call.c
+++ b/gcc/config/rs6000/rs6000-call.c
@@ -5529,7 +5529,7 @@ const struct altivec_builtin_types altivec_overloaded_builtins[] = {
 
 static int
 rs6000_aggregate_candidate (const_tree type, machine_mode *modep,
-			    bool *cxx17_empty_base_seen)
+			    int *empty_base_seen)
 {
   machine_mode mode;
   HOST_WIDE_INT size;
@@ -5600,7 +5600,7 @@ rs6000_aggregate_candidate (const_tree type, machine_mode *modep,
 	  return -1;
 
 	count = rs6000_aggregate_candidate (TREE_TYPE (type), modep,
-					    cxx17_empty_base_seen);
+					    empty_base_seen);
 	if (count == -1
 	    || !index
 	    || !TYPE_MAX_VALUE (index)
@@ -5638,14 +5638,18 @@ rs6000_aggregate_candidate (const_tree type, machine_mode *modep,
 	    if (TREE_CODE (field) != FIELD_DECL)
 	      continue;
 
-	    if (cxx17_empty_base_field_p (field))
+	    if (DECL_FIELD_ABI_IGNORED (field))
 	      {
-		*cxx17_empty_base_seen = true;
+		if (lookup_attribute ("no_unique_address",
+				      DECL_ATTRIBUTES (field)))
+		  *empty_base_seen |= 2;
+		else
+		  *empty_base_seen |= 1;
 		continue;
 	      }
 
 	    sub_count = rs6000_aggregate_candidate (TREE_TYPE (field), modep,
-						    cxx17_empty_base_seen);
+						    empty_base_seen);
 	    if (sub_count < 0)
 	      return -1;
 	    count += sub_count;
@@ -5679,7 +5683,7 @@ rs6000_aggregate_candidate (const_tree type, machine_mode *modep,
 	      continue;
 
 	    sub_count = rs6000_aggregate_candidate (TREE_TYPE (field), modep,
-						    cxx17_empty_base_seen);
+						    empty_base_seen);
 	    if (sub_count < 0)
 	      return -1;
 	    count = count > sub_count ? count : sub_count;
@@ -5720,9 +5724,9 @@ rs6000_discover_homogeneous_aggregate (machine_mode mode, const_tree type,
       && AGGREGATE_TYPE_P (type))
     {
       machine_mode field_mode = VOIDmode;
-      bool cxx17_empty_base_seen = false;
+      int empty_base_seen = 0;
       int field_count = rs6000_aggregate_candidate (type, &field_mode,
-						    &cxx17_empty_base_seen);
+						    &empty_base_seen);
 
       if (field_count > 0)
 	{
@@ -5737,16 +5741,22 @@ rs6000_discover_homogeneous_aggregate (machine_mode mode, const_tree type,
 		*elt_mode = field_mode;
 	      if (n_elts)
 		*n_elts = field_count;
-	      if (cxx17_empty_base_seen && warn_psabi)
+	      if (empty_base_seen && warn_psabi)
 		{
 		  static unsigned last_reported_type_uid;
 		  unsigned uid = TYPE_UID (TYPE_MAIN_VARIANT (type));
 		  if (uid != last_reported_type_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);
+		      if (empty_base_seen & 1)
+			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);
+		      else
+			inform (input_location,
+				"parameter passing for argument of type %qT "
+				"with %<[[no_unique_address]]%> members "
+				"changed in GCC 10.1", type);
 		      last_reported_type_uid = uid;
 		    }
 		}
diff --git a/gcc/config/rs6000/rs6000.c b/gcc/config/rs6000/rs6000.c
index 2007617cdee3..d4e16ce7fccc 100644
--- a/gcc/config/rs6000/rs6000.c
+++ b/gcc/config/rs6000/rs6000.c
@@ -7204,7 +7204,9 @@ rs6000_special_round_type_align (tree type, unsigned int computed,
   tree field = TYPE_FIELDS (type);
 
   /* Skip all non field decls */
-  while (field != NULL && TREE_CODE (field) != FIELD_DECL)
+  while (field != NULL
+	 && (TREE_CODE (field) != FIELD_DECL
+	     || DECL_FIELD_ABI_IGNORED (field)))
     field = DECL_CHAIN (field);
 
   if (field != NULL && field != type)
@@ -7236,7 +7238,9 @@ darwin_rs6000_special_round_type_align (tree type, unsigned int computed,
   do {
     tree field = TYPE_FIELDS (type);
     /* Skip all non field decls */
-    while (field != NULL && TREE_CODE (field) != FIELD_DECL)
+    while (field != NULL
+	   && (TREE_CODE (field) != FIELD_DECL
+	       || DECL_FIELD_ABI_IGNORED (field)))
       field = DECL_CHAIN (field);
     if (! field)
       break;
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index f2bfe84b1e63..41db16e782fa 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,3 +1,11 @@
+2020-04-29  Jakub Jelinek  <jakub@redhat.com>
+
+	PR target/94707
+	* class.c (build_base_field): Set DECL_FIELD_ABI_IGNORED on C++17 empty
+	base artificial FIELD_DECLs.
+	(layout_class_type): Set DECL_FIELD_ABI_IGNORED on empty class
+	field_poverlapping_p FIELD_DECLs.
+
 2020-04-29  Patrick Palka  <ppalka@redhat.com>
 
 	PR c++/94819
diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index e211db323772..1f524a319174 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -4515,6 +4515,7 @@ build_base_field (record_layout_info rli, tree binfo, tree access,
 	  DECL_FIELD_OFFSET (decl) = BINFO_OFFSET (binfo);
 	  DECL_FIELD_BIT_OFFSET (decl) = bitsize_zero_node;
 	  SET_DECL_OFFSET_ALIGN (decl, BITS_PER_UNIT);
+	  DECL_FIELD_ABI_IGNORED (decl) = 1;
 	}
 
       /* An empty virtual base causes a class to be non-empty
@@ -6522,7 +6523,10 @@ layout_class_type (tree t, tree *virtuals_p)
 	  SET_DECL_MODE (field, TYPE_MODE (type));
 	}
       else if (might_overlap && is_empty_class (type))
-	layout_empty_base_or_field (rli, field, empty_base_offsets);
+	{
+	  DECL_FIELD_ABI_IGNORED (field) = 1;
+	  layout_empty_base_or_field (rli, field, empty_base_offsets);
+	}
       else
 	layout_nonempty_base_or_field (rli, field, NULL_TREE,
 				       empty_base_offsets);
diff --git a/gcc/lto-streamer-out.c b/gcc/lto-streamer-out.c
index 52ef94718db5..47fc32ed8be6 100644
--- a/gcc/lto-streamer-out.c
+++ b/gcc/lto-streamer-out.c
@@ -1080,6 +1080,7 @@ hash_tree (struct streamer_tree_cache_d *cache, hash_map<tree, hashval_t> *map,
 	  hstate.add_flag (DECL_PACKED (t));
 	  hstate.add_flag (DECL_NONADDRESSABLE_P (t));
 	  hstate.add_flag (DECL_PADDING_P (t));
+	  hstate.add_flag (DECL_FIELD_ABI_IGNORED (t));
 	  hstate.add_int (DECL_OFFSET_ALIGN (t));
 	}
       else if (code == VAR_DECL)
diff --git a/gcc/lto/ChangeLog b/gcc/lto/ChangeLog
index a0c14899d756..e0b1aaf63fe6 100644
--- a/gcc/lto/ChangeLog
+++ b/gcc/lto/ChangeLog
@@ -1,3 +1,8 @@
+2020-04-29  Jakub Jelinek  <jakub@redhat.com>
+
+	PR target/94707
+	* lto-common.c (compare_tree_sccs_1): Handle DECL_FIELD_ABI_IGNORED.
+
 2020-04-17  Martin Liska  <mliska@suse.cz>
 
 	PR lto/94612
diff --git a/gcc/lto/lto-common.c b/gcc/lto/lto-common.c
index cee5f0e99358..c8fd6f5c5958 100644
--- a/gcc/lto/lto-common.c
+++ b/gcc/lto/lto-common.c
@@ -1179,6 +1179,7 @@ compare_tree_sccs_1 (tree t1, tree t2, tree **map)
 	  compare_values (DECL_PACKED);
 	  compare_values (DECL_NONADDRESSABLE_P);
 	  compare_values (DECL_PADDING_P);
+	  compare_values (DECL_FIELD_ABI_IGNORED);
 	  compare_values (DECL_OFFSET_ALIGN);
 	}
       else if (code == VAR_DECL)
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 512974831c24..a8b69a15ccb0 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,13 @@
+2020-04-29  Jakub Jelinek  <jakub@redhat.com>
+
+	PR target/94707
+	* g++.target/powerpc/pr94707-1.C: New test.
+	* g++.target/powerpc/pr94707-2.C: New test.
+	* g++.target/powerpc/pr94707-3.C: New test.
+	* g++.target/powerpc/pr94707-4.C: New test.
+	* g++.target/powerpc/pr94707-5.C: New test.
+	* g++.target/powerpc/pr94707-4.C: New test.
+
 2020-04-29  Patrick Palka  <ppalka@redhat.com>
 
 	PR c++/94819
diff --git a/gcc/testsuite/g++.target/powerpc/pr94707-1.C b/gcc/testsuite/g++.target/powerpc/pr94707-1.C
new file mode 100644
index 000000000000..45b457395aa3
--- /dev/null
+++ b/gcc/testsuite/g++.target/powerpc/pr94707-1.C
@@ -0,0 +1,38 @@
+// PR target/94707
+// { dg-do compile { target powerpc_elfv2 } }
+// { dg-options "-O2 -std=c++14" }
+// Test that for all the calls in this testcase the C++17 empty base
+// artificial fields and [[no_unique_address]] empty class non-static
+// data members are ignored in the decision about passing homogeneous
+// arguments.
+// { dg-final { scan-assembler-times {(?n)^\s+lfs\s+(?:%f)?4,} 7 } }
+
+struct X { };
+struct Y { int : 0; };
+struct Z { int : 0; Y y; };
+struct U : public X { X q; };
+struct A { float a, b, c, d; };
+struct B : public X { float a, b, c, d; };
+struct C : public Y { float a, b, c, d; };
+struct D : public Z { float a, b, c, d; };
+struct E : public U { float a, b, c, d; };
+struct F { [[no_unique_address]] X x; float a, b, c, d; };
+struct G { [[no_unique_address]] Y y; float a, b, c, d; };
+struct H { [[no_unique_address]] Z z; float a, b, c, d; };
+struct I { [[no_unique_address]] U u; float a, b, c, d; };
+struct J { float a, b; [[no_unique_address]] X x; float c, d; };
+struct K { float a, b; [[no_unique_address]] Y y; float c, d; };
+struct L { float a, b; [[no_unique_address]] Z z; float c, d; };
+struct M { float a, b; [[no_unique_address]] U u; float c, d; };
+#define T(S, s) extern S s; extern void foo##s (S); int bar##s () { foo##s (s); return 0; }
+// { dg-message "parameter passing for argument of type 'F' with '\\\[\\\[no_unique_address\\\]\\\]' members changed in GCC 10.1" "" { target *-*-* } .-1 }
+// { dg-message "parameter passing for argument of type 'G' with '\\\[\\\[no_unique_address\\\]\\\]' members changed in GCC 10.1" "" { target *-*-* } .-2 }
+// { dg-message "parameter passing for argument of type 'J' with '\\\[\\\[no_unique_address\\\]\\\]' members changed in GCC 10.1" "" { target *-*-* } .-3 }
+// { dg-message "parameter passing for argument of type 'K' with '\\\[\\\[no_unique_address\\\]\\\]' members changed in GCC 10.1" "" { target *-*-* } .-4 }
+T (A, a)
+T (B, b)
+T (C, c)
+T (F, f)
+T (G, g)
+T (J, j)
+T (K, k)
diff --git a/gcc/testsuite/g++.target/powerpc/pr94707-2.C b/gcc/testsuite/g++.target/powerpc/pr94707-2.C
new file mode 100644
index 000000000000..6d26146a0dcb
--- /dev/null
+++ b/gcc/testsuite/g++.target/powerpc/pr94707-2.C
@@ -0,0 +1,34 @@
+// PR target/94707
+// { dg-do compile { target powerpc_elfv2 } }
+// { dg-options "-O2 -std=c++14" }
+// Test that for no calls in this testcase the C++17 empty base
+// artificial fields and [[no_unique_address]] empty class non-static
+// data members are ignored in the decision about passing homogeneous
+// arguments.
+// { dg-final { scan-assembler-not {(?n)^\s+lfs\s+(?:%f)?4,} } }
+
+struct X { };
+struct Y { int : 0; };
+struct Z { int : 0; Y y; };
+struct U : public X { X q; };
+struct A { float a, b, c, d; };
+struct B : public X { float a, b, c, d; };
+struct C : public Y { float a, b, c, d; };
+struct D : public Z { float a, b, c, d; };
+struct E : public U { float a, b, c, d; };
+struct F { [[no_unique_address]] X x; float a, b, c, d; };
+struct G { [[no_unique_address]] Y y; float a, b, c, d; };
+struct H { [[no_unique_address]] Z z; float a, b, c, d; };
+struct I { [[no_unique_address]] U u; float a, b, c, d; };
+struct J { float a, b; [[no_unique_address]] X x; float c, d; };
+struct K { float a, b; [[no_unique_address]] Y y; float c, d; };
+struct L { float a, b; [[no_unique_address]] Z z; float c, d; };
+struct M { float a, b; [[no_unique_address]] U u; float c, d; };
+#define T(S, s) extern S s; extern void foo##s (S); int bar##s () { foo##s (s); return 0; }
+// { dg-bogus "parameter passing for argument of type" }
+T (D, d)
+T (E, e)
+T (H, h)
+T (I, i)
+T (L, l)
+T (M, m)
diff --git a/gcc/testsuite/g++.target/powerpc/pr94707-3.C b/gcc/testsuite/g++.target/powerpc/pr94707-3.C
new file mode 100644
index 000000000000..52a6a9a95127
--- /dev/null
+++ b/gcc/testsuite/g++.target/powerpc/pr94707-3.C
@@ -0,0 +1,40 @@
+// PR target/94707
+// { dg-do compile { target powerpc_elfv2 } }
+// { dg-options "-O2 -std=c++17" }
+// Test that for all the calls in this testcase the C++17 empty base
+// artificial fields and [[no_unique_address]] empty class non-static
+// data members are ignored in the decision about passing homogeneous
+// arguments.
+// { dg-final { scan-assembler-times {(?n)^\s+lfs\s+(?:%f)?4,} 7 } }
+
+struct X { };
+struct Y { int : 0; };
+struct Z { int : 0; Y y; };
+struct U : public X { X q; };
+struct A { float a, b, c, d; };
+struct B : public X { float a, b, c, d; };
+struct C : public Y { float a, b, c, d; };
+struct D : public Z { float a, b, c, d; };
+struct E : public U { float a, b, c, d; };
+struct F { [[no_unique_address]] X x; float a, b, c, d; };
+struct G { [[no_unique_address]] Y y; float a, b, c, d; };
+struct H { [[no_unique_address]] Z z; float a, b, c, d; };
+struct I { [[no_unique_address]] U u; float a, b, c, d; };
+struct J { float a, b; [[no_unique_address]] X x; float c, d; };
+struct K { float a, b; [[no_unique_address]] Y y; float c, d; };
+struct L { float a, b; [[no_unique_address]] Z z; float c, d; };
+struct M { float a, b; [[no_unique_address]] U u; float c, d; };
+#define T(S, s) extern S s; extern void foo##s (S); int bar##s () { foo##s (s); return 0; }
+// { dg-message "parameter passing for argument of type 'B' when C\\+\\+17 is enabled changed to match C\\+\\+14 in GCC 10.1" "" { target *-*-* } .-1 }
+// { dg-message "parameter passing for argument of type 'C' when C\\+\\+17 is enabled changed to match C\\+\\+14 in GCC 10.1" "" { target *-*-* } .-2 }
+// { dg-message "parameter passing for argument of type 'F' with '\\\[\\\[no_unique_address\\\]\\\]' members changed in GCC 10.1" "" { target *-*-* } .-3 }
+// { dg-message "parameter passing for argument of type 'G' with '\\\[\\\[no_unique_address\\\]\\\]' members changed in GCC 10.1" "" { target *-*-* } .-4 }
+// { dg-message "parameter passing for argument of type 'J' with '\\\[\\\[no_unique_address\\\]\\\]' members changed in GCC 10.1" "" { target *-*-* } .-5 }
+// { dg-message "parameter passing for argument of type 'K' with '\\\[\\\[no_unique_address\\\]\\\]' members changed in GCC 10.1" "" { target *-*-* } .-6 }
+T (A, a)
+T (B, b)
+T (C, c)
+T (F, f)
+T (G, g)
+T (J, j)
+T (K, k)
diff --git a/gcc/testsuite/g++.target/powerpc/pr94707-4.C b/gcc/testsuite/g++.target/powerpc/pr94707-4.C
new file mode 100644
index 000000000000..0e9bb3b6d4d6
--- /dev/null
+++ b/gcc/testsuite/g++.target/powerpc/pr94707-4.C
@@ -0,0 +1,34 @@
+// PR target/94707
+// { dg-do compile { target powerpc_elfv2 } }
+// { dg-options "-O2 -std=c++17" }
+// Test that for no calls in this testcase the C++17 empty base
+// artificial fields and [[no_unique_address]] empty class non-static
+// data members are ignored in the decision about passing homogeneous
+// arguments.
+// { dg-final { scan-assembler-not {(?n)^\s+lfs\s+(?:%f)?4,} } }
+
+struct X { };
+struct Y { int : 0; };
+struct Z { int : 0; Y y; };
+struct U : public X { X q; };
+struct A { float a, b, c, d; };
+struct B : public X { float a, b, c, d; };
+struct C : public Y { float a, b, c, d; };
+struct D : public Z { float a, b, c, d; };
+struct E : public U { float a, b, c, d; };
+struct F { [[no_unique_address]] X x; float a, b, c, d; };
+struct G { [[no_unique_address]] Y y; float a, b, c, d; };
+struct H { [[no_unique_address]] Z z; float a, b, c, d; };
+struct I { [[no_unique_address]] U u; float a, b, c, d; };
+struct J { float a, b; [[no_unique_address]] X x; float c, d; };
+struct K { float a, b; [[no_unique_address]] Y y; float c, d; };
+struct L { float a, b; [[no_unique_address]] Z z; float c, d; };
+struct M { float a, b; [[no_unique_address]] U u; float c, d; };
+#define T(S, s) extern S s; extern void foo##s (S); int bar##s () { foo##s (s); return 0; }
+// { dg-bogus "parameter passing for argument of type" }
+T (D, d)
+T (E, e)
+T (H, h)
+T (I, i)
+T (L, l)
+T (M, m)
diff --git a/gcc/testsuite/g++.target/powerpc/pr94707-5.C b/gcc/testsuite/g++.target/powerpc/pr94707-5.C
new file mode 100644
index 000000000000..ed3d1b25aeb3
--- /dev/null
+++ b/gcc/testsuite/g++.target/powerpc/pr94707-5.C
@@ -0,0 +1,35 @@
+// PR target/94707
+// { dg-do compile { target powerpc*-*-darwin* } }
+// { dg-require-effective-target ilp32 }
+// { dg-options "-std=c++14" }
+
+struct X { };
+struct Y { int : 0; };
+struct Z { int : 0; Y y; };
+struct U : public X { X q; };
+struct A { double a; };
+struct B : public X { double a; };
+struct C : public Y { double a; };
+struct D : public Z { double a; };
+struct E : public U { double a; };
+struct F { [[no_unique_address]] X x; double a; };
+struct G { [[no_unique_address]] Y y; double a; };
+struct H { [[no_unique_address]] Z z; double a; };
+struct I { [[no_unique_address]] U u; double a; };
+struct J { double a; [[no_unique_address]] X x; };
+struct K { double a; [[no_unique_address]] Y y; };
+struct L { double a; [[no_unique_address]] Z z; };
+struct M { double a; [[no_unique_address]] U u; };
+static_assert (__alignof__ (A) == 8, "");
+static_assert (__alignof__ (B) == 8, "");
+static_assert (__alignof__ (C) == 8, "");
+static_assert (__alignof__ (D) == 4, "");
+static_assert (__alignof__ (E) == 4, "");
+static_assert (__alignof__ (F) == 8, "");
+static_assert (__alignof__ (G) == 8, "");
+static_assert (__alignof__ (H) == 4, "");
+static_assert (__alignof__ (I) == 4, "");
+static_assert (__alignof__ (J) == 8, "");
+static_assert (__alignof__ (K) == 8, "");
+static_assert (__alignof__ (L) == 8, "");
+static_assert (__alignof__ (M) == 8, "");
diff --git a/gcc/testsuite/g++.target/powerpc/pr94707-6.C b/gcc/testsuite/g++.target/powerpc/pr94707-6.C
new file mode 100644
index 000000000000..65dcca590031
--- /dev/null
+++ b/gcc/testsuite/g++.target/powerpc/pr94707-6.C
@@ -0,0 +1,6 @@
+// PR target/94707
+// { dg-do compile { target powerpc*-*-darwin* } }
+// { dg-require-effective-target ilp32 }
+// { dg-options "-O2 -std=c++17" }
+
+#include "pr94707-5.C"
diff --git a/gcc/tree-core.h b/gcc/tree-core.h
index d84fe959accc..eb01c2434cd0 100644
--- a/gcc/tree-core.h
+++ b/gcc/tree-core.h
@@ -1709,7 +1709,8 @@ struct GTY(()) tree_decl_common {
   unsigned lang_flag_8 : 1;
 
   /* In VAR_DECL and PARM_DECL, this is DECL_REGISTER
-     IN TRANSLATION_UNIT_DECL, this is TRANSLATION_UNIT_WARN_EMPTY_P.  */
+     In TRANSLATION_UNIT_DECL, this is TRANSLATION_UNIT_WARN_EMPTY_P.
+     In FIELD_DECL, this is DECL_FIELD_ABI_IGNORED.  */
   unsigned decl_flag_0 : 1;
   /* In FIELD_DECL, this is DECL_BIT_FIELD
      In VAR_DECL and FUNCTION_DECL, this is DECL_EXTERNAL.
diff --git a/gcc/tree-streamer-in.c b/gcc/tree-streamer-in.c
index 0bfc272d0763..5b653cdf5ad2 100644
--- a/gcc/tree-streamer-in.c
+++ b/gcc/tree-streamer-in.c
@@ -256,6 +256,7 @@ unpack_ts_decl_common_value_fields (struct bitpack_d *bp, tree expr)
       DECL_PACKED (expr) = (unsigned) bp_unpack_value (bp, 1);
       DECL_NONADDRESSABLE_P (expr) = (unsigned) bp_unpack_value (bp, 1);
       DECL_PADDING_P (expr) = (unsigned) bp_unpack_value (bp, 1);
+      DECL_FIELD_ABI_IGNORED (expr) = (unsigned) bp_unpack_value (bp, 1);
       expr->decl_common.off_align = bp_unpack_value (bp, 8);
     }
 
diff --git a/gcc/tree-streamer-out.c b/gcc/tree-streamer-out.c
index 5bbcebba87ed..0b54ffb63f35 100644
--- a/gcc/tree-streamer-out.c
+++ b/gcc/tree-streamer-out.c
@@ -217,6 +217,7 @@ pack_ts_decl_common_value_fields (struct bitpack_d *bp, tree expr)
       bp_pack_value (bp, DECL_PACKED (expr), 1);
       bp_pack_value (bp, DECL_NONADDRESSABLE_P (expr), 1);
       bp_pack_value (bp, DECL_PADDING_P (expr), 1);
+      bp_pack_value (bp, DECL_FIELD_ABI_IGNORED (expr), 1);
       bp_pack_value (bp, expr->decl_common.off_align, 8);
     }
 
diff --git a/gcc/tree.h b/gcc/tree.h
index 1c28785d411f..bd0c51b2a182 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -2750,6 +2750,13 @@ extern void decl_value_expr_insert (tree, tree);
 /* In a FIELD_DECL, indicates this field should be bit-packed.  */
 #define DECL_PACKED(NODE) (FIELD_DECL_CHECK (NODE)->base.u.bits.packed_flag)
 
+/* In a FIELD_DECL, indicates this field should be ignored for ABI decisions
+   like passing/returning containing struct by value.
+   Set for C++17 empty base artificial FIELD_DECLs as well as
+   empty [[no_unique_address]] non-static data members.  */
+#define DECL_FIELD_ABI_IGNORED(NODE) \
+  (FIELD_DECL_CHECK (NODE)->decl_common.decl_flag_0)
+
 /* Nonzero in a FIELD_DECL means it is a bit field, and must be accessed
    specially.  */
 #define DECL_BIT_FIELD(NODE) (FIELD_DECL_CHECK (NODE)->decl_common.decl_flag_1)
-- 
GitLab