public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* RFC: C++ PATCH to adjust empty class parameter passing ABI
@ 2016-04-12 20:28 Jason Merrill
  2016-04-13 15:32 ` Jakub Jelinek
  2016-04-13 22:01 ` Jonathan Wakely
  0 siblings, 2 replies; 16+ messages in thread
From: Jason Merrill @ 2016-04-12 20:28 UTC (permalink / raw)
  To: gcc-patches List, Jonathan Wakely; +Cc: H.J. Lu

[-- Attachment #1: Type: text/plain, Size: 2361 bytes --]

A revision of the patch previously posted at

   https://gcc.gnu.org/ml/gcc-patches/2016-03/msg00841.html

To recap quickly, the C++ compiler has used a different calling 
convention for passing empty classes, because C++ says they have size 1, 
while the GCC C extension gives them size 0.  But this difference 
doesn't mean that they need to be passed differently; in either case 
there's no actual data involved.  And the Clang folks recently pointed 
out that if you squint properly, it seems that the x86_64 psABI says 
that an empty class shouldn't be passed either in registers or memory. 
H.J. had a back-end fix, but it seemed to me that a front-end fix would 
be simpler and safer.

The first patch corrects errors in what the C++ front end considers an 
empty classes; unnamed bit-fields aren't really members.  I'm checking 
this in now.

The second patch is the actual ABI change under -fabi-version=10, 
including a warning.  Jonathan, please check this in with your library 
changes.

Unfortunately, a drawback of doing this in the front end is that it's 
difficult to warn only about affected cases; the front end doesn't know 
what's actually going to be emitted, and has to warn conservatively, 
leading to false positives particularly for inline functions.

The third patch is a sketch of an attempt to address this by delaying 
the warning until expand time.  We can't use front end information at 
this point because it has been thrown away by pass_ipa_free_lang_data, 
so I'm resorting to pre-generating the warning and stashing it away. 
This is several kinds of kludge, including hashing on a call location, 
but it greatly improves the accuracy of the warning.  I'm hoping that 
someone will have a better idea about how to approach this.

For normal calls we could observe the difference in DECL_ARG_TYPE for a 
RECORD_TYPE parameter, but that won't help with varargs, for which the 
front end hides the real type from the back end.  I suppose we could 
tell the back end about the magic empty_struct_type and warn when it 
comes up.  Or we could just warn about varargs in the front end, and not 
worry about the rare false positives.  I suppose I'll pursue that direction.

Also note that in some cases the following parameter is still passed in 
the same place even when the empty parameter changes.

Any other clever ideas?

Jason

[-- Attachment #2: really-empty.patch --]
[-- Type: text/x-patch, Size: 1021 bytes --]

commit 0d8472ada9703f2dc0e288a230b280ec45d60583
Author: Jason Merrill <jason@redhat.com>
Date:   Fri Mar 11 13:39:52 2016 -0500

    	* class.c (is_really_empty_class): A zero-length array is empty.
    	An unnamed bit-field doesn't make a class non-empty.

diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index e66f0b9..02a992f 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -8406,12 +8406,15 @@ is_really_empty_class (tree type)
       for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
 	if (TREE_CODE (field) == FIELD_DECL
 	    && !DECL_ARTIFICIAL (field)
+	    /* An unnamed bit-field is not a data member.  */
+	    && (DECL_NAME (field) || !DECL_C_BIT_FIELD (field))
 	    && !is_really_empty_class (TREE_TYPE (field)))
 	  return false;
       return true;
     }
   else if (TREE_CODE (type) == ARRAY_TYPE)
-    return is_really_empty_class (TREE_TYPE (type));
+    return (integer_zerop (array_type_nelts_top (type))
+	    || is_really_empty_class (TREE_TYPE (type)));
   return false;
 }
 

[-- Attachment #3: empty-abi.patch --]
[-- Type: text/x-patch, Size: 29443 bytes --]

commit 2dad2abc1694e7a70f523cf639ebbe4b945247db
Author: Jason Merrill <jason@redhat.com>
Date:   Fri Mar 11 13:40:02 2016 -0500

    	Pass empty class parameters like C.
    
    	* call.c (pass_as_empty_struct, empty_class_arg)
    	(warn_empty_class_abi): New.
    	(type_passed_as, build_x_va_arg): Use pass_as_empty_struct.
    	(build_call_a): Use empty_class_arg, warn_empty_class_abi.
    	* cp-tree.h (CPTI_EMPTY_STRUCT, empty_struct_type): New.
    	* decl.c (cxx_init_decl_processing): Create empty_struct_type.
    	(store_parm_decls): Use warn_empty_class_abi.

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index ed23490..3ba9cd2 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -214,6 +214,8 @@ static void add_candidates (tree, tree, const vec<tree, va_gc> *, tree, tree,
 			    tsubst_flags_t);
 static conversion *merge_conversion_sequences (conversion *, conversion *);
 static tree build_temp (tree, tree, int, diagnostic_t *, tsubst_flags_t);
+static bool pass_as_empty_struct (tree type);
+static tree empty_class_arg (tree);
 
 /* Returns nonzero iff the destructor name specified in NAME matches BASETYPE.
    NAME can take many forms...  */
@@ -379,16 +381,28 @@ build_call_a (tree function, int n, tree *argarray)
   /* Don't pass empty class objects by value.  This is useful
      for tags in STL, which are used to control overload resolution.
      We don't need to handle other cases of copying empty classes.  */
+  bool warned = false;
+  if (decl && !TREE_PUBLIC (decl))
+    /* Don't warn about the ABI of a function local to this TU.  */
+    warned = true;
+  tree empty_arg = NULL_TREE;
   if (! decl || ! DECL_BUILT_IN (decl))
     for (i = 0; i < n; i++)
       {
 	tree arg = CALL_EXPR_ARG (function, i);
-	if (is_empty_class (TREE_TYPE (arg))
-	    && ! TREE_ADDRESSABLE (TREE_TYPE (arg)))
+	tree type = TREE_TYPE (arg);
+	if (is_really_empty_class (type)
+	    && ! TREE_ADDRESSABLE (type))
 	  {
-	    tree t = build0 (EMPTY_CLASS_EXPR, TREE_TYPE (arg));
-	    arg = build2 (COMPOUND_EXPR, TREE_TYPE (t), arg, t);
-	    CALL_EXPR_ARG (function, i) = arg;
+	    empty_arg = arg;
+	    CALL_EXPR_ARG (function, i) = empty_class_arg (arg);
+	  }
+	/* Warn about ABI changes for a non-final argument.  */
+	else if (!warned && empty_arg)
+	  {
+	    location_t loc = EXPR_LOC_OR_LOC (empty_arg, input_location);
+	    warn_empty_class_abi (empty_arg, loc);
+	    warned = true;
 	  }
       }
 
@@ -6872,6 +6886,14 @@ build_x_va_arg (source_location loc, tree expr, tree type)
       expr = build_va_arg (loc, expr, ref);
       return convert_from_reference (expr);
     }
+  else if (is_really_empty_class (type) && !TREE_ADDRESSABLE (type))
+    {
+      /* Do the reverse of empty_class_arg.  */
+      tree etype = pass_as_empty_struct (type) ? empty_struct_type : type;
+      expr = build_va_arg (loc, expr, etype);
+      tree ec = build0 (EMPTY_CLASS_EXPR, type);
+      return build2 (COMPOUND_EXPR, type, expr, ec);
+    }
 
   return build_va_arg (loc, expr, type);
 }
@@ -6968,6 +6990,58 @@ convert_default_arg (tree type, tree arg, tree fn, int parmnum,
   return arg;
 }
 
+/* Return true iff TYPE should be passed and returned as a size 0 type rather
+   than its normal size, for compatibility with C.  */
+
+static bool
+pass_as_empty_struct (tree type)
+{
+  return (abi_version_at_least (10)
+	  && type != error_mark_node
+	  && COMPLETE_TYPE_P (type)
+	  && !TREE_ADDRESSABLE (type)
+	  && is_really_empty_class (type));
+}
+
+/* Adjust the value VAL of empty class type TYPE for argument passing.
+   Keep this synced with build_x_va_arg.  */
+
+static tree
+empty_class_arg (tree val)
+{
+  /* Don't pass empty class objects by value.  This is useful
+     for tags in STL, which are used to control overload resolution.
+     We don't need to handle other cases of copying empty classes.  */
+  tree type = TREE_TYPE (val);
+  tree etype = pass_as_empty_struct (type) ? empty_struct_type : type;
+  tree empty = build0 (EMPTY_CLASS_EXPR, etype);
+  return build2 (COMPOUND_EXPR, etype, val, empty);
+}
+
+/* Warn about the change in empty class parameter passing ABI.  Returns true
+   if we warned.  */
+
+void
+warn_empty_class_abi (tree arg, location_t loc)
+{
+  if (!warn_abi || !abi_version_crosses (10))
+    return;
+
+  tree type;
+  if (TYPE_P (arg))
+    type = arg;
+  else
+    {
+      if (TREE_TYPE (arg) == empty_struct_type
+	  && TREE_CODE (arg) == COMPOUND_EXPR)
+	arg = TREE_OPERAND (arg, 0);
+      type = TREE_TYPE (arg);
+    }
+
+  warning_at (loc, OPT_Wabi, "empty class %qT parameter passing ABI "
+	      "changes in -fabi-version=10 (GCC 6)", type);
+}
+
 /* Returns the type which will really be used for passing an argument of
    type TYPE.  */
 
@@ -6986,6 +7060,8 @@ type_passed_as (tree type)
 	   && COMPLETE_TYPE_P (type)
 	   && tree_int_cst_lt (TYPE_SIZE (type), TYPE_SIZE (integer_type_node)))
     type = integer_type_node;
+  else if (pass_as_empty_struct (type))
+    type = empty_struct_type;
 
   return type;
 }
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 0f7e08f..dd1cfa4 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -1150,6 +1150,8 @@ enum cp_tree_index
     CPTI_NULLPTR,
     CPTI_NULLPTR_TYPE,
 
+    CPTI_EMPTY_STRUCT,
+
     CPTI_MAX
 };
 
@@ -1185,6 +1187,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
 #define current_aggr			cp_global_trees[CPTI_AGGR_TAG]
 #define nullptr_node			cp_global_trees[CPTI_NULLPTR]
 #define nullptr_type_node		cp_global_trees[CPTI_NULLPTR_TYPE]
+#define empty_struct_type		cp_global_trees[CPTI_EMPTY_STRUCT]
 
 /* We cache these tree nodes so as to call get_identifier less
    frequently.  */
@@ -5537,6 +5540,7 @@ extern tree build_addr_func			(tree, tsubst_flags_t);
 extern void set_flags_from_callee		(tree);
 extern tree build_call_a			(tree, int, tree*);
 extern tree build_call_n			(tree, int, ...);
+extern void warn_empty_class_abi		(tree, location_t);
 extern bool null_ptr_cst_p			(tree);
 extern bool null_member_pointer_value_p		(tree);
 extern bool sufficient_parms_p			(const_tree);
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 9260f4c..f1c274f 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -4152,6 +4152,10 @@ cxx_init_decl_processing (void)
     nullptr_node = build_int_cst (nullptr_type_node, 0);
   }
 
+  empty_struct_type = make_node (RECORD_TYPE);
+  finish_builtin_struct (empty_struct_type, "__empty_struct",
+			 NULL_TREE, NULL_TREE);
+
   abort_fndecl
     = build_library_fn_ptr ("__cxa_pure_virtual", void_ftype,
 			    ECF_NORETURN | ECF_NOTHROW);
@@ -14297,16 +14301,34 @@ store_parm_decls (tree current_function_parms)
 	     they end in the correct forward order.  */
       specparms = nreverse (specparms);
 
+      /* Don't warn about the ABI of a function local to this TU.  */
+      bool warned = !TREE_PUBLIC (current_function_decl);
+      bool saw_nonempty = false;
       for (parm = specparms; parm; parm = next)
 	{
 	  next = DECL_CHAIN (parm);
 	  if (TREE_CODE (parm) == PARM_DECL)
 	    {
+	      tree type = TREE_TYPE (parm);
 	      if (DECL_NAME (parm) == NULL_TREE
-		  || !VOID_TYPE_P (parm))
+		  || !VOID_TYPE_P (type))
 		pushdecl (parm);
 	      else
 		error ("parameter %qD declared void", parm);
+	      /* If this isn't the last parameter, maybe warn about ABI change
+		 in passing empty classes.  */
+	      if (processing_template_decl)
+		continue;
+	      if (TREE_ADDRESSABLE (type)
+		  || !is_really_empty_class (type))
+		saw_nonempty = true;
+	      else if (!warned
+		       && (saw_nonempty
+			   || varargs_function_p (current_function_decl)))
+		{
+		  warn_empty_class_abi (parm, DECL_SOURCE_LOCATION (parm));
+		  warned = true;
+		}
 	    }
 	  else
 	    {
diff --git a/gcc/testsuite/g++.dg/abi/empty12.C b/gcc/testsuite/g++.dg/abi/empty12.C
new file mode 100644
index 0000000..ce1f6f2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/empty12.C
@@ -0,0 +1,17 @@
+// PR c++/60336
+// { dg-do run }
+// { dg-options "-Wabi=9 -x c" }
+// { dg-additional-sources "empty12a.c" }
+// { dg-prune-output "command line option" }
+
+#include "empty12.h"
+extern "C" void fun(struct dummy, struct foo);
+
+int main()
+{
+  struct dummy d;
+  struct foo f = { -1, -2, -3, -4, -5 };
+
+  fun(d, f); // { dg-warning "empty" }
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/abi/empty12.h b/gcc/testsuite/g++.dg/abi/empty12.h
new file mode 100644
index 0000000..c61afcd
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/empty12.h
@@ -0,0 +1,9 @@
+struct dummy { };
+struct foo
+{
+  int i1;
+  int i2;
+  int i3;
+  int i4;
+  int i5;
+};
diff --git a/gcc/testsuite/g++.dg/abi/empty12a.c b/gcc/testsuite/g++.dg/abi/empty12a.c
new file mode 100644
index 0000000..34a25ba
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/empty12a.c
@@ -0,0 +1,6 @@
+#include "empty12.h"
+void fun(struct dummy d, struct foo f)
+{
+  if (f.i1 != -1)
+    __builtin_abort();
+}
diff --git a/gcc/testsuite/g++.dg/abi/empty13.C b/gcc/testsuite/g++.dg/abi/empty13.C
new file mode 100644
index 0000000..d1e0946
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/empty13.C
@@ -0,0 +1,17 @@
+// PR c++/60336
+// { dg-do run }
+// { dg-options "-x c -fabi-version=9" }
+// { dg-additional-sources "empty13a.c" }
+// { dg-prune-output "command line option" }
+
+#include "empty13.h"
+extern "C" void fun(struct dummy, struct foo);
+
+int main()
+{
+  struct dummy d;
+  struct foo f = { -1, -2, -3, -4, -5 };
+
+  fun(d, f);
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/abi/empty13.h b/gcc/testsuite/g++.dg/abi/empty13.h
new file mode 100644
index 0000000..c61afcd
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/empty13.h
@@ -0,0 +1,9 @@
+struct dummy { };
+struct foo
+{
+  int i1;
+  int i2;
+  int i3;
+  int i4;
+  int i5;
+};
diff --git a/gcc/testsuite/g++.dg/abi/empty13a.c b/gcc/testsuite/g++.dg/abi/empty13a.c
new file mode 100644
index 0000000..b4303a6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/empty13a.c
@@ -0,0 +1,6 @@
+#include "empty13.h"
+void fun(struct dummy d, struct foo f)
+{
+  if (f.i1 == -1)
+    __builtin_abort();
+}
diff --git a/gcc/testsuite/g++.dg/abi/empty14.C b/gcc/testsuite/g++.dg/abi/empty14.C
new file mode 100644
index 0000000..1b9c397
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/empty14.C
@@ -0,0 +1,17 @@
+// PR c++/60336
+// { dg-do run }
+// { dg-options "-Wabi=9 -x c" }
+// { dg-additional-sources "empty14a.c" }
+// { dg-prune-output "command line option" }
+
+#include "empty14.h"
+extern "C" void fun(struct dummy, struct foo);
+
+int main()
+{
+  struct dummy d;
+  struct foo f = { -1, -2, -3, -4, -5 };
+
+  fun(d, f); // { dg-warning "empty" }
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/abi/empty14.h b/gcc/testsuite/g++.dg/abi/empty14.h
new file mode 100644
index 0000000..5842279
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/empty14.h
@@ -0,0 +1,10 @@
+struct dummy0 { };
+struct dummy { struct dummy0 d[140]; };
+struct foo
+{
+  int i1;
+  int i2;
+  int i3;
+  int i4;
+  int i5;
+};
diff --git a/gcc/testsuite/g++.dg/abi/empty14a.c b/gcc/testsuite/g++.dg/abi/empty14a.c
new file mode 100644
index 0000000..8b3d780
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/empty14a.c
@@ -0,0 +1,6 @@
+#include "empty14.h"
+void fun(struct dummy d, struct foo f)
+{
+  if (f.i1 != -1)
+    __builtin_abort();
+}
diff --git a/gcc/testsuite/g++.dg/abi/empty15.C b/gcc/testsuite/g++.dg/abi/empty15.C
new file mode 100644
index 0000000..ac0a868
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/empty15.C
@@ -0,0 +1,17 @@
+// PR c++/60336
+// { dg-do run }
+// { dg-options "-Wabi=9 -x c" }
+// { dg-additional-sources "empty15a.c" }
+// { dg-prune-output "command line option" }
+
+#include "empty15.h"
+extern "C" void fun(struct dummy, struct foo);
+
+int main()
+{
+  struct dummy d;
+  struct foo f = { -1, -2, -3, -4, -5 };
+
+  fun(d, f); // { dg-warning "empty" }
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/abi/empty15.h b/gcc/testsuite/g++.dg/abi/empty15.h
new file mode 100644
index 0000000..1c6f26f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/empty15.h
@@ -0,0 +1,30 @@
+struct A1 {};
+struct A2 {};
+struct B1 { struct A1 a; struct A2 b; };
+struct B2 { struct A1 a; struct A2 b; };
+struct C1 { struct B1 a; struct B2 b; };
+struct C2 { struct B1 a; struct B2 b; };
+struct D1 { struct C1 a; struct C2 b; };
+struct D2 { struct C1 a; struct C2 b; };
+struct E1 { struct D1 a; struct D2 b; };
+struct E2 { struct D1 a; struct D2 b; };
+struct F1 { struct E1 a; struct E2 b; };
+struct F2 { struct E1 a; struct E2 b; };
+struct G1 { struct F1 a; struct F2 b; };
+struct G2 { struct F1 a; struct F2 b; };
+struct H1 { struct G1 a; struct G2 b; };
+struct H2 { struct G1 a; struct G2 b; };
+struct I1 { struct H1 a; struct H2 b; };
+struct I2 { struct H1 a; struct H2 b; };
+struct J1 { struct I1 a; struct I2 b; };
+struct J2 { struct I1 a; struct I2 b; };
+struct dummy { struct J1 a; struct J2 b; };
+
+struct foo
+{
+  int i1;
+  int i2;
+  int i3;
+  int i4;
+  int i5;
+};
diff --git a/gcc/testsuite/g++.dg/abi/empty15a.c b/gcc/testsuite/g++.dg/abi/empty15a.c
new file mode 100644
index 0000000..325b2c5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/empty15a.c
@@ -0,0 +1,6 @@
+#include "empty15.h"
+void fun(struct dummy d, struct foo f)
+{
+  if (f.i1 != -1)
+    __builtin_abort();
+}
diff --git a/gcc/testsuite/g++.dg/abi/empty16.C b/gcc/testsuite/g++.dg/abi/empty16.C
new file mode 100644
index 0000000..de2bf5c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/empty16.C
@@ -0,0 +1,17 @@
+// PR c++/60336
+// { dg-do run }
+// { dg-options "-Wabi=9 -x c" }
+// { dg-additional-sources "empty16a.c" }
+// { dg-prune-output "command line option" }
+
+#include "empty16.h"
+extern "C" void fun(struct dummy, struct foo);
+
+int main()
+{
+  struct dummy d;
+  struct foo f = { -1, -2, -3, -4, -5 };
+
+  fun(d, f); // { dg-warning "empty" }
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/abi/empty16.h b/gcc/testsuite/g++.dg/abi/empty16.h
new file mode 100644
index 0000000..7552ae0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/empty16.h
@@ -0,0 +1,16 @@
+#ifdef __cplusplus
+struct A1 {};
+struct A2 {};
+struct dummy : A1, A2 {} ;
+#else
+struct dummy {};
+#endif
+
+struct foo
+{
+  int i1;
+  int i2;
+  int i3;
+  int i4;
+  int i5;
+};
diff --git a/gcc/testsuite/g++.dg/abi/empty16a.c b/gcc/testsuite/g++.dg/abi/empty16a.c
new file mode 100644
index 0000000..6cb7fbc
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/empty16a.c
@@ -0,0 +1,6 @@
+#include "empty16.h"
+void fun(struct dummy d, struct foo f)
+{
+  if (f.i1 != -1)
+    __builtin_abort();
+}
diff --git a/gcc/testsuite/g++.dg/abi/empty17.C b/gcc/testsuite/g++.dg/abi/empty17.C
new file mode 100644
index 0000000..c7a37c0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/empty17.C
@@ -0,0 +1,17 @@
+// PR c++/60336
+// { dg-do run }
+// { dg-options "-Wabi=9 -x c" }
+// { dg-additional-sources "empty17a.c" }
+// { dg-prune-output "command line option" }
+
+#include "empty17.h"
+extern "C" void fun(struct dummy, struct foo);
+
+int main()
+{
+  struct dummy d;
+  struct foo f = { -1, -2, -3, -4, -5 };
+
+  fun(d, f); // { dg-warning "empty" }
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/abi/empty17.h b/gcc/testsuite/g++.dg/abi/empty17.h
new file mode 100644
index 0000000..9cf72ba
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/empty17.h
@@ -0,0 +1,27 @@
+#ifdef __cplusplus
+struct A1
+{
+  void foo (void);
+  unsigned int : 15;
+};
+struct A2
+{
+  void bar (void);
+  unsigned int : 15;
+};
+struct dummy : A1, A2
+{
+  unsigned int : 15;
+};
+#else
+struct dummy {};
+#endif
+
+struct foo
+{
+  int i1;
+  int i2;
+  int i3;
+  int i4;
+  int i5;
+};
diff --git a/gcc/testsuite/g++.dg/abi/empty17a.c b/gcc/testsuite/g++.dg/abi/empty17a.c
new file mode 100644
index 0000000..24408fd
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/empty17a.c
@@ -0,0 +1,6 @@
+#include "empty17.h"
+void fun(struct dummy d, struct foo f)
+{
+  if (f.i1 != -1)
+    __builtin_abort();
+}
diff --git a/gcc/testsuite/g++.dg/abi/empty18.C b/gcc/testsuite/g++.dg/abi/empty18.C
new file mode 100644
index 0000000..6cad33c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/empty18.C
@@ -0,0 +1,17 @@
+// PR c++/60336
+// { dg-do run }
+// { dg-options "-Wabi=9 -x c" }
+// { dg-additional-sources "empty18a.c" }
+// { dg-prune-output "command line option" }
+
+#include "empty18.h"
+extern "C" void fun(struct dummy, struct foo);
+
+int main()
+{
+  struct dummy d;
+  struct foo f = { -1, -2, -3, -4, -5 };
+
+  fun(d, f);			// { dg-warning "empty" }
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/abi/empty18.h b/gcc/testsuite/g++.dg/abi/empty18.h
new file mode 100644
index 0000000..86e7ecd
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/empty18.h
@@ -0,0 +1,9 @@
+struct dummy { int d[0]; };
+struct foo
+{
+  int i1;
+  int i2;
+  int i3;
+  int i4;
+  int i5;
+};
diff --git a/gcc/testsuite/g++.dg/abi/empty18a.c b/gcc/testsuite/g++.dg/abi/empty18a.c
new file mode 100644
index 0000000..902860b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/empty18a.c
@@ -0,0 +1,6 @@
+#include "empty18.h"
+void fun(struct dummy d, struct foo f)
+{
+  if (f.i1 != -1)
+    __builtin_abort();
+}
diff --git a/gcc/testsuite/g++.dg/abi/empty19.C b/gcc/testsuite/g++.dg/abi/empty19.C
new file mode 100644
index 0000000..e3e855a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/empty19.C
@@ -0,0 +1,17 @@
+// PR c++/60336
+// { dg-do run }
+// { dg-options "-Wabi=9 -x c" }
+// { dg-additional-sources "empty19a.c" }
+// { dg-prune-output "command line option" }
+
+#include "empty19.h"
+extern "C" void fun(struct dummy, struct foo);
+
+int main()
+{
+  struct dummy d;
+  struct foo f = { -1, -2, -3, -4, -5 };
+
+  fun(d, f); // { dg-warning "empty" }
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/abi/empty19.h b/gcc/testsuite/g++.dg/abi/empty19.h
new file mode 100644
index 0000000..616b87b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/empty19.h
@@ -0,0 +1,10 @@
+struct dummy0 { };
+struct dummy { struct dummy0 d[0]; };
+struct foo
+{
+  int i1;
+  int i2;
+  int i3;
+  int i4;
+  int i5;
+};
diff --git a/gcc/testsuite/g++.dg/abi/empty19a.c b/gcc/testsuite/g++.dg/abi/empty19a.c
new file mode 100644
index 0000000..767b1eb
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/empty19a.c
@@ -0,0 +1,6 @@
+#include "empty19.h"
+void fun(struct dummy d, struct foo f)
+{
+  if (f.i1 != -1)
+    __builtin_abort();
+}
diff --git a/gcc/testsuite/g++.dg/abi/empty21.C b/gcc/testsuite/g++.dg/abi/empty21.C
new file mode 100644
index 0000000..7538dd8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/empty21.C
@@ -0,0 +1,21 @@
+// { dg-options "-Wabi=9" }
+
+#include <stdarg.h>
+
+struct A { };
+
+void f(int i, ...)
+{
+  va_list ap;
+  va_start (ap, i);
+  if (i >= 1)
+    va_arg (ap, A);		// { dg-warning "ABI" }
+  if (i >= 2)
+    va_arg (ap, int);
+}
+
+int main()
+{
+  f(0);
+  f(2, A(), 42);		// { dg-warning "ABI" }
+}
diff --git a/gcc/testsuite/g++.dg/abi/pr60336-1.C b/gcc/testsuite/g++.dg/abi/pr60336-1.C
new file mode 100644
index 0000000..af08638
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/pr60336-1.C
@@ -0,0 +1,17 @@
+// { dg-do compile }
+// { dg-options "-O2 -std=c++11 -fno-pic" }
+// { dg-require-effective-target fpic }
+
+struct dummy { };
+struct true_type { struct dummy i; };
+
+extern true_type y;
+extern void xxx (true_type c);
+
+void
+yyy (void)
+{
+  xxx (y);
+}
+
+// { dg-final { scan-assembler "jmp\[\t \]+\[^\$\]*?_Z3xxx9true_type" { target i?86-*-* x86_64-*-* } } }
diff --git a/gcc/testsuite/g++.dg/abi/pr60336-10.C b/gcc/testsuite/g++.dg/abi/pr60336-10.C
new file mode 100644
index 0000000..6c9c990
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/pr60336-10.C
@@ -0,0 +1,50 @@
+// { dg-do run }
+// { dg-options "-O2" }
+
+#include <stdarg.h>
+
+struct dummy0 { };
+struct dummy1 { };
+struct dummy : dummy0, dummy1 { };
+
+void
+test (struct dummy a, int m, ...)
+{
+  va_list va_arglist;
+  int i;
+  int count = 0;
+
+  if (m == 0)
+    count++;
+  va_start (va_arglist, m);
+  i = va_arg (va_arglist, int);
+  if (i == 1)
+    count++;
+  i = va_arg (va_arglist, int);
+  if (i == 2)
+  i = va_arg (va_arglist, int);
+    count++;
+  if (i == 3)
+    count++;
+  i = va_arg (va_arglist, int);
+  if (i == 4)
+    count++;
+  i = va_arg (va_arglist, int);
+  if (i == 5)
+    count++;
+  i = va_arg (va_arglist, int);
+  if (i == 6)
+    count++;
+  va_end (va_arglist);
+  if (count != 7)
+    __builtin_abort ();
+}
+
+struct dummy a0;
+
+int
+main ()
+{
+  test (a0, 0, 1, 2, 3, 4, 5, 6);
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/abi/pr60336-11.C b/gcc/testsuite/g++.dg/abi/pr60336-11.C
new file mode 100644
index 0000000..c92f3d4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/pr60336-11.C
@@ -0,0 +1,56 @@
+// { dg-do run }
+// { dg-options "-O2" }
+
+#include <stdarg.h>
+
+struct dummy0
+{
+  void bar (void);
+};
+struct dummy1
+{
+  void foo (void);
+};
+struct dummy : dummy0, dummy1 { };
+
+void
+test (struct dummy a, int m, ...)
+{
+  va_list va_arglist;
+  int i;
+  int count = 0;
+
+  if (m == 0)
+    count++;
+  va_start (va_arglist, m);
+  i = va_arg (va_arglist, int);
+  if (i == 1)
+    count++;
+  i = va_arg (va_arglist, int);
+  if (i == 2)
+  i = va_arg (va_arglist, int);
+    count++;
+  if (i == 3)
+    count++;
+  i = va_arg (va_arglist, int);
+  if (i == 4)
+    count++;
+  i = va_arg (va_arglist, int);
+  if (i == 5)
+    count++;
+  i = va_arg (va_arglist, int);
+  if (i == 6)
+    count++;
+  va_end (va_arglist);
+  if (count != 7)
+    __builtin_abort ();
+}
+
+struct dummy a0;
+
+int
+main ()
+{
+  test (a0, 0, 1, 2, 3, 4, 5, 6);
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/abi/pr60336-12.C b/gcc/testsuite/g++.dg/abi/pr60336-12.C
new file mode 100644
index 0000000..83a7bb0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/pr60336-12.C
@@ -0,0 +1,57 @@
+// { dg-do run }
+// { dg-options "-O2" }
+
+#include <stdarg.h>
+
+struct dummy0
+{
+};
+struct dummy1
+{
+  unsigned : 15;
+};
+struct dummy : dummy0, dummy1
+{
+};
+
+void
+test (struct dummy a, int m, ...)
+{
+  va_list va_arglist;
+  int i;
+  int count = 0;
+
+  if (m == 0)
+    count++;
+  va_start (va_arglist, m);
+  i = va_arg (va_arglist, int);
+  if (i == 1)
+    count++;
+  i = va_arg (va_arglist, int);
+  if (i == 2)
+  i = va_arg (va_arglist, int);
+    count++;
+  if (i == 3)
+    count++;
+  i = va_arg (va_arglist, int);
+  if (i == 4)
+    count++;
+  i = va_arg (va_arglist, int);
+  if (i == 5)
+    count++;
+  i = va_arg (va_arglist, int);
+  if (i == 6)
+    count++;
+  va_end (va_arglist);
+  if (count != 7)
+    __builtin_abort ();
+}
+
+struct dummy a0;
+
+int
+main ()
+{
+  test (a0, 0, 1, 2, 3, 4, 5, 6);
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/abi/pr60336-2.C b/gcc/testsuite/g++.dg/abi/pr60336-2.C
new file mode 100644
index 0000000..32eecb3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/pr60336-2.C
@@ -0,0 +1,48 @@
+// { dg-do run }
+// { dg-options "-O2 -Wabi=9" }
+
+#include <stdarg.h>
+
+struct dummy { };
+
+void
+test (struct dummy a, int m, ...) // { dg-message "empty" }
+{
+  va_list va_arglist;
+  int i;
+  int count = 0;
+
+  if (m == 0)
+    count++;
+  va_start (va_arglist, m);
+  i = va_arg (va_arglist, int);
+  if (i == 1)
+    count++;
+  i = va_arg (va_arglist, int);
+  if (i == 2)
+  i = va_arg (va_arglist, int);
+    count++;
+  if (i == 3)
+    count++;
+  i = va_arg (va_arglist, int);
+  if (i == 4)
+    count++;
+  i = va_arg (va_arglist, int);
+  if (i == 5)
+    count++;
+  i = va_arg (va_arglist, int);
+  if (i == 6)
+    count++;
+  va_end (va_arglist);
+  if (count != 7)
+    __builtin_abort ();
+}
+
+struct dummy a0;
+
+int
+main ()
+{
+  test (a0, 0, 1, 2, 3, 4, 5, 6); // { dg-message "empty" }
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/abi/pr60336-3.C b/gcc/testsuite/g++.dg/abi/pr60336-3.C
new file mode 100644
index 0000000..8ebd4dd
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/pr60336-3.C
@@ -0,0 +1,15 @@
+// { dg-do compile }
+// { dg-options "-O2 -Wabi=9" }
+
+struct dummy { struct{}__attribute__((aligned (4))) a[7]; };
+
+extern void test1 (struct dummy, ...);
+extern void (*test2) (struct dummy, ...);
+
+void
+foo ()
+{
+  struct dummy a0;
+  test1 (a0, 42); // { dg-message "empty" }
+  test2 (a0, 42); // { dg-message "empty" }
+}
diff --git a/gcc/testsuite/g++.dg/abi/pr60336-4.C b/gcc/testsuite/g++.dg/abi/pr60336-4.C
new file mode 100644
index 0000000..8790a66
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/pr60336-4.C
@@ -0,0 +1,48 @@
+// { dg-do run { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } }
+// { dg-options "-O2 -fabi-version=9" }
+
+#include <stdarg.h>
+
+struct dummy { };
+
+void
+test (struct dummy a, int m, ...)
+{
+  va_list va_arglist;
+  int i;
+  int count = 0;
+
+  if (m == 0)
+    count++;
+  va_start (va_arglist, m);
+  i = va_arg (va_arglist, int);
+  if (i == 1)
+    count++;
+  i = va_arg (va_arglist, int);
+  if (i == 2)
+  i = va_arg (va_arglist, int);
+    count++;
+  if (i == 3)
+    count++;
+  i = va_arg (va_arglist, int);
+  if (i == 4)
+    count++;
+  i = va_arg (va_arglist, int);
+  if (i == 5)
+    count++;
+  i = va_arg (va_arglist, int);
+  if (i == 6)
+    count++;
+  va_end (va_arglist);
+  if (count == 7)
+    __builtin_abort ();
+}
+
+struct dummy a0;
+
+int
+main ()
+{
+  test (a0, 0, 1, 2, 3, 4, 5, 6);
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/abi/pr60336-5.C b/gcc/testsuite/g++.dg/abi/pr60336-5.C
new file mode 100644
index 0000000..b0c76ad
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/pr60336-5.C
@@ -0,0 +1,17 @@
+// { dg-do compile }
+// { dg-options "-O2 -std=c++11 -fno-pic" }
+// { dg-require-effective-target fpic }
+
+struct dummy { };
+struct true_type { struct dummy i; struct dummy j; };
+
+extern true_type y;
+extern void xxx (true_type c);
+
+void
+yyy (void)
+{
+  xxx (y);
+}
+
+// { dg-final { scan-assembler "jmp\[\t \]+\[^\$\]*?_Z3xxx9true_type" { target i?86-*-* x86_64-*-* } } }
diff --git a/gcc/testsuite/g++.dg/abi/pr60336-6.C b/gcc/testsuite/g++.dg/abi/pr60336-6.C
new file mode 100644
index 0000000..5879651
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/pr60336-6.C
@@ -0,0 +1,17 @@
+// { dg-do compile }
+// { dg-options "-O2 -std=c++11 -fno-pic" }
+// { dg-require-effective-target fpic }
+
+struct dummy { };
+struct true_type { struct dummy i1; struct dummy i2; };
+
+extern true_type y;
+extern void xxx (true_type c);
+
+void
+yyy (void)
+{
+  xxx (y);
+}
+
+// { dg-final { scan-assembler "jmp\[\t \]+\[^\$\]*?_Z3xxx9true_type" { target i?86-*-* x86_64-*-* } } }
diff --git a/gcc/testsuite/g++.dg/abi/pr60336-7.C b/gcc/testsuite/g++.dg/abi/pr60336-7.C
new file mode 100644
index 0000000..0e5d2ef
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/pr60336-7.C
@@ -0,0 +1,17 @@
+// { dg-do compile }
+// { dg-options "-O2 -std=c++11 -fno-pic" }
+// { dg-require-effective-target fpic }
+
+struct dummy { };
+struct true_type { struct dummy i[120]; };
+
+extern true_type y;
+extern void xxx (true_type c);
+
+void
+yyy (void)
+{
+  xxx (y);
+}
+
+// { dg-final { scan-assembler "jmp\[\t \]+\[^\$\]*?_Z3xxx9true_type" { target i?86-*-* x86_64-*-* } } }
diff --git a/gcc/testsuite/g++.dg/abi/pr60336-8.C b/gcc/testsuite/g++.dg/abi/pr60336-8.C
new file mode 100644
index 0000000..fdfc924
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/pr60336-8.C
@@ -0,0 +1,15 @@
+// { dg-do compile }
+// { dg-options "-O2 -Wabi=9" }
+
+struct dummy { struct{} a[7][3]; };
+
+extern void test1 (struct dummy, ...);
+extern void (*test2) (struct dummy, ...);
+
+void
+foo ()
+{
+  struct dummy a0;
+  test1 (a0, 42); // { dg-message "empty" }
+  test2 (a0, 42); // { dg-message "empty" }
+}
diff --git a/gcc/testsuite/g++.dg/abi/pr60336-9.C b/gcc/testsuite/g++.dg/abi/pr60336-9.C
new file mode 100644
index 0000000..4ad333f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/pr60336-9.C
@@ -0,0 +1,28 @@
+// { dg-do compile }
+// { dg-options "-O2 -std=c++11 -fno-pic" }
+// { dg-require-effective-target fpic }
+
+struct A1 {}; struct A2 {};
+struct B1 { A1 a; A2 b; }; struct B2 { A1 a; A2 b; };
+struct C1 { B1 a; B2 b; }; struct C2 { B1 a; B2 b; };
+struct D1 { C1 a; C2 b; }; struct D2 { C1 a; C2 b; };
+struct E1 { D1 a; D2 b; }; struct E2 { D1 a; D2 b; };
+struct F1 { E1 a; E2 b; }; struct F2 { E1 a; E2 b; };
+struct G1 { F1 a; F2 b; }; struct G2 { F1 a; F2 b; };
+struct H1 { G1 a; G2 b; }; struct H2 { G1 a; G2 b; };
+struct I1 { H1 a; H2 b; }; struct I2 { H1 a; H2 b; };
+struct J1 { I1 a; I2 b; }; struct J2 { I1 a; I2 b; };
+struct dummy { J1 a; J2 b; };
+
+struct true_type { struct dummy i; };
+
+extern true_type y;
+extern void xxx (true_type c);
+
+void
+yyy (void)
+{
+  xxx (y);
+}
+
+// { dg-final { scan-assembler "jmp\[\t \]+\[^\$\]*?_Z3xxx9true_type" { target i?86-*-* x86_64-*-* } } }
diff --git a/gcc/testsuite/g++.dg/abi/pr68355.C b/gcc/testsuite/g++.dg/abi/pr68355.C
new file mode 100644
index 0000000..1354fc4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/pr68355.C
@@ -0,0 +1,24 @@
+// { dg-do compile }
+// { dg-options "-O2 -std=c++11 -fno-pic" }
+// { dg-require-effective-target fpic }
+
+template<typename _Tp, _Tp __v>
+struct integral_constant
+{
+  static constexpr _Tp value = __v;
+  typedef _Tp value_type;
+  typedef integral_constant<_Tp, __v> type;
+  constexpr operator value_type() const { return value; }
+};
+
+typedef integral_constant<bool, true> true_type;
+extern void xxx (true_type c);
+
+void
+yyy (void)
+{
+  true_type y;
+  xxx (y);
+}
+
+// { dg-final { scan-assembler "jmp\[\t \]+\[^\$\]*?_Z3xxx17integral_constantIbLb1EE" { target i?86-*-* x86_64-*-* } } }

[-- Attachment #4: abi-warn-emit.patch --]
[-- Type: text/x-patch, Size: 11061 bytes --]

commit e7a1517f3f04ab8f42fc3f07f2e1887f14c0d2c7
Author: Jason Merrill <jason@redhat.com>
Date:   Tue Apr 12 13:16:50 2016 -0400

    warn-pass

diff --git a/gcc/calls.c b/gcc/calls.c
index 6415e08..3b1348e 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -2340,6 +2340,8 @@ avoid_likely_spilled_reg (rtx x)
    If the value is stored in TARGET then TARGET is returned.
    If IGNORE is nonzero, then we ignore the value of the function call.  */
 
+void
+maybe_warn_emitted_call (location_t loc);
 rtx
 expand_call (tree exp, rtx target, int ignore)
 {
@@ -2490,6 +2492,9 @@ expand_call (tree exp, rtx target, int ignore)
   if (AGGREGATE_TYPE_P (rettype))
     warning (OPT_Waggregate_return, "function call has aggregate value");
 
+  if (EXPR_HAS_LOCATION (exp))
+    maybe_warn_emitted_call (EXPR_LOCATION (exp));
+
   /* If the result of a non looping pure or const function call is
      ignored (or void), and none of its arguments are volatile, we can
      avoid expanding the call and just evaluate the arguments for
diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index 1341c14..a1baade 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -6083,6 +6083,9 @@ stack_protect_prologue (void)
   emit_move_insn (x, y);
 }
 
+void
+maybe_warn_emitted_fn (tree fn);
+
 /* Translate the intermediate representation contained in the CFG
    from GIMPLE trees to RTL.
 
@@ -6186,6 +6189,8 @@ pass_expand::execute (function *fun)
   /* Mark arrays indexed with non-constant indices with TREE_ADDRESSABLE.  */
   discover_nonconstant_array_refs ();
 
+  maybe_warn_emitted_fn (current_function_decl);
+
   targetm.expand_to_rtl_hook ();
   crtl->stack_alignment_needed = STACK_BOUNDARY;
   crtl->max_used_stack_slot_alignment = STACK_BOUNDARY;
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 3ba9cd2..ee1e492 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -216,6 +216,7 @@ static conversion *merge_conversion_sequences (conversion *, conversion *);
 static tree build_temp (tree, tree, int, diagnostic_t *, tsubst_flags_t);
 static bool pass_as_empty_struct (tree type);
 static tree empty_class_arg (tree);
+static void cp_push_warn_emitted_call (location_t, tree);
 
 /* Returns nonzero iff the destructor name specified in NAME matches BASETYPE.
    NAME can take many forms...  */
@@ -400,8 +401,8 @@ build_call_a (tree function, int n, tree *argarray)
 	/* Warn about ABI changes for a non-final argument.  */
 	else if (!warned && empty_arg)
 	  {
-	    location_t loc = EXPR_LOC_OR_LOC (empty_arg, input_location);
-	    warn_empty_class_abi (empty_arg, loc);
+	    cp_push_warn_emitted_call (input_location, TREE_TYPE (empty_arg));
+	    CALL_EXPR_ABI_SENSITIVE_P (function) = true;
 	    warned = true;
 	  }
       }
@@ -6891,6 +6892,7 @@ build_x_va_arg (source_location loc, tree expr, tree type)
       /* Do the reverse of empty_class_arg.  */
       tree etype = pass_as_empty_struct (type) ? empty_struct_type : type;
       expr = build_va_arg (loc, expr, etype);
+      cp_push_warn_emitted_call (loc, type);
       tree ec = build0 (EMPTY_CLASS_EXPR, type);
       return build2 (COMPOUND_EXPR, type, expr, ec);
     }
@@ -7021,25 +7023,37 @@ empty_class_arg (tree val)
 /* Warn about the change in empty class parameter passing ABI.  Returns true
    if we warned.  */
 
+static tree
+empty_class_msg (tree type)
+{
+  pp_printf (global_dc->printer, "empty class %qT parameter passing ABI "
+	     "changes in -fabi-version=10 (GCC 6)", type);
+  tree msg = get_identifier (pp_formatted_text (global_dc->printer));
+  pp_clear_output_area (global_dc->printer);
+  return msg;
+}
+
+extern void push_warn_emitted_call (location_t, tree);
+extern void push_warn_emitted_fn (tree, tree);
+
 void
-warn_empty_class_abi (tree arg, location_t loc)
+cp_push_warn_emitted_fn (tree fn, tree type)
 {
   if (!warn_abi || !abi_version_crosses (10))
     return;
 
-  tree type;
-  if (TYPE_P (arg))
-    type = arg;
-  else
-    {
-      if (TREE_TYPE (arg) == empty_struct_type
-	  && TREE_CODE (arg) == COMPOUND_EXPR)
-	arg = TREE_OPERAND (arg, 0);
-      type = TREE_TYPE (arg);
-    }
+  tree msg = empty_class_msg (type);
+  push_warn_emitted_fn (fn, msg);
+}
+
+void
+cp_push_warn_emitted_call (location_t loc, tree type)
+{
+  if (!warn_abi || !abi_version_crosses (10))
+    return;
 
-  warning_at (loc, OPT_Wabi, "empty class %qT parameter passing ABI "
-	      "changes in -fabi-version=10 (GCC 6)", type);
+  tree msg = empty_class_msg (type);
+  push_warn_emitted_call (loc, msg);
 }
 
 /* Returns the type which will really be used for passing an argument of
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index dd1cfa4..0f66e97 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -3391,6 +3391,10 @@ extern void decl_shadowed_for_var_insert (tree, tree);
    should be performed at instantiation time.  */
 #define KOENIG_LOOKUP_P(NODE) TREE_LANG_FLAG_0 (CALL_EXPR_CHECK (NODE))
 
+/* Kludge.  */
+#define CALL_EXPR_ABI_SENSITIVE_P(NODE) \
+  TREE_LANG_FLAG_1 (CALL_EXPR_CHECK (NODE))
+
 /* True if CALL_EXPR expresses list-initialization of an object.  */
 #define CALL_EXPR_LIST_INIT_P(NODE) \
   TREE_LANG_FLAG_3 (TREE_CHECK2 ((NODE),CALL_EXPR,AGGR_INIT_EXPR))
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index f1c274f..7ff4f2f 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -14271,6 +14271,8 @@ use_eh_spec_block (tree fn)
 
    Also install to binding contour return value identifier, if any.  */
 
+extern void
+cp_push_warn_emitted_fn (tree fn, tree type);
 static void
 store_parm_decls (tree current_function_parms)
 {
@@ -14326,7 +14328,7 @@ store_parm_decls (tree current_function_parms)
 		       && (saw_nonempty
 			   || varargs_function_p (current_function_decl)))
 		{
-		  warn_empty_class_abi (parm, DECL_SOURCE_LOCATION (parm));
+		  cp_push_warn_emitted_fn (current_function_decl, type);
 		  warned = true;
 		}
 	    }
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 00e211e..c94fe93 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -6903,7 +6903,9 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 				    koenig_p,
 				    complain);
 
-	    if (close_paren_loc != UNKNOWN_LOCATION)
+	    if (close_paren_loc != UNKNOWN_LOCATION
+		&& ! (TREE_CODE (postfix_expression) == CALL_EXPR
+		      && CALL_EXPR_ABI_SENSITIVE_P (postfix_expression)))
 	      {
 		location_t combined_loc = make_location (token->location,
 							 start_loc,
diff --git a/gcc/expr.c b/gcc/expr.c
index 29d22b0..3c81fbc 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -61,6 +61,48 @@ along with GCC; see the file COPYING3.  If not see
 #include "rtl-chkp.h"
 #include "ccmp.h"
 
+struct location_hash
+  : int_hash<location_t, UNKNOWN_LOCATION, BUILTINS_LOCATION> {};
+typedef hash_map<location_hash, tree> location_tree_map;
+
+static GTY(()) location_tree_map *warn_emitted_calls;
+
+void
+push_warn_emitted_call (location_t loc, tree msg)
+{
+  if (warn_emitted_calls == NULL)
+    warn_emitted_calls = location_tree_map::create_ggc (13);
+  loc = get_pure_location (loc);
+  warn_emitted_calls->put (loc, msg);
+}
+
+void
+maybe_warn_emitted_call (location_t loc)
+{
+  loc = get_pure_location (loc);
+  if (warn_emitted_calls && warn_abi)
+    if (tree *msg = warn_emitted_calls->get (loc))
+      warning_at (loc, OPT_Wabi, "%D", *msg);
+}
+
+static GTY(()) hash_map<tree, tree> *warn_emitted_fns;
+
+void
+push_warn_emitted_fn (tree fn, tree msg)
+{
+  if (warn_emitted_fns == NULL)
+    warn_emitted_fns = hash_map<tree,tree>::create_ggc (13);
+  warn_emitted_fns->put (fn, msg);
+}
+
+void
+maybe_warn_emitted_fn (tree fn)
+{
+  if (warn_emitted_fns && warn_abi)
+    if (tree *msg = warn_emitted_fns->get (fn))
+      warning_at (DECL_SOURCE_LOCATION (fn), OPT_Wabi, "%D", *msg);
+}
+
 
 /* If this is nonzero, we do not bother generating VOLATILE
    around volatile memory references, and we are willing to
diff --git a/gcc/testsuite/g++.dg/abi/empty20.C b/gcc/testsuite/g++.dg/abi/empty20.C
new file mode 100644
index 0000000..4450a28
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/empty20.C
@@ -0,0 +1,18 @@
+// { dg-options "-Wabi=9 -O0" }
+
+struct A { };
+
+void f(A, A) { }		// No warning, trailing parms all empty
+void f(A, A, int) { }		// { dg-warning "ABI" }
+__attribute__ ((always_inline))
+inline void f(A a, int i)	// No warning, always inlined
+{
+  f(a,a,i);			// { dg-warning "ABI" }
+}
+int main()
+{
+  A a;
+  f(a,a);
+  f(a,a,42);			// { dg-warning "ABI" }
+  f(a,42);
+}
diff --git a/gcc/tree-stdarg.c b/gcc/tree-stdarg.c
index 13b92f0..664d498 100644
--- a/gcc/tree-stdarg.c
+++ b/gcc/tree-stdarg.c
@@ -1006,6 +1006,8 @@ gimple_call_ifn_va_arg_p (gimple *stmt)
 
 /* Expand IFN_VA_ARGs in FUN.  */
 
+void
+maybe_warn_emitted_call (location_t loc);
 static void
 expand_ifn_va_arg_1 (function *fun)
 {
@@ -1024,6 +1026,8 @@ expand_ifn_va_arg_1 (function *fun)
 	if (!gimple_call_ifn_va_arg_p (stmt))
 	  continue;
 
+	maybe_warn_emitted_call (gimple_location (stmt));
+
 	modified = true;
 
 	type = TREE_TYPE (TREE_TYPE (gimple_call_arg (stmt, 1)));

commit 5817a569c22c79fa9077631093eaa1964e84c462
Author: Jason Merrill <jason@redhat.com>
Date:   Tue Apr 12 14:13:14 2016 -0400

    	* gimple.c (gimple_build_call_from_tree): Copy EXPR_LOCATION.

diff --git a/gcc/gimple.c b/gcc/gimple.c
index b0e19d5..bd12565 100644
--- a/gcc/gimple.c
+++ b/gcc/gimple.c
@@ -371,6 +371,7 @@ gimple_build_call_from_tree (tree t)
   gimple_call_set_nothrow (call, TREE_NOTHROW (t));
   gimple_set_no_warning (call, TREE_NO_WARNING (t));
   gimple_call_set_with_bounds (call, CALL_WITH_BOUNDS_P (t));
+  gimple_set_location (call, EXPR_LOCATION (t));
 
   return call;
 }
diff --git a/gcc/testsuite/g++.dg/ext/builtin-object-size3.C b/gcc/testsuite/g++.dg/ext/builtin-object-size3.C
index 09263e5..8d30984 100644
--- a/gcc/testsuite/g++.dg/ext/builtin-object-size3.C
+++ b/gcc/testsuite/g++.dg/ext/builtin-object-size3.C
@@ -3,15 +3,15 @@
 
 void baz (int *, int *);
 
-#define MEMCPY(d,s,l) __builtin___memcpy_chk (d, s, l, __builtin_object_size (d, 0))
+#define MEMCPY(d,s,l) __builtin___memcpy_chk (d, s, l, __builtin_object_size (d, 0)) // { dg-warning "will always overflow" }
 
 int
 foo ()
 {
   int *p = new int;
   int *q = new int[4];
-  MEMCPY (p, "abcdefghijklmnopqrstuvwxyz", sizeof (int));
-  MEMCPY (q, "abcdefghijklmnopqrstuvwxyz", 4 * sizeof (int));
+  MEMCPY (p, "abcdefghijklmnopqrstuvwxyz", sizeof (int)); // { dg-bogus "" }
+  MEMCPY (q, "abcdefghijklmnopqrstuvwxyz", 4 * sizeof (int)); // { dg-bogus "" }
   baz (p, q);
 }
 
@@ -20,7 +20,7 @@ bar ()
 {
   int *p = new int;
   int *q = new int[4];
-  MEMCPY (p, "abcdefghijklmnopqrstuvwxyz", sizeof (int) + 1);		// { dg-warning "will always overflow destination buffer" }
-  MEMCPY (q, "abcdefghijklmnopqrstuvwxyz", 4 * sizeof (int) + 1);	// { dg-warning "will always overflow destination buffer" }
+  MEMCPY (p, "abcdefghijklmnopqrstuvwxyz", sizeof (int) + 1);		// { dg-message "MEMCPY" }
+  MEMCPY (q, "abcdefghijklmnopqrstuvwxyz", 4 * sizeof (int) + 1);	// { dg-message "MEMCPY" }
   baz (p, q);
 }

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: RFC: C++ PATCH to adjust empty class parameter passing ABI
  2016-04-12 20:28 RFC: C++ PATCH to adjust empty class parameter passing ABI Jason Merrill
@ 2016-04-13 15:32 ` Jakub Jelinek
  2016-04-13 15:54   ` Jason Merrill
  2016-04-13 22:01 ` Jonathan Wakely
  1 sibling, 1 reply; 16+ messages in thread
From: Jakub Jelinek @ 2016-04-13 15:32 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches List, Jonathan Wakely, H.J. Lu

On Tue, Apr 12, 2016 at 04:27:48PM -0400, Jason Merrill wrote:
> Unfortunately, a drawback of doing this in the front end is that it's
> difficult to warn only about affected cases; the front end doesn't know
> what's actually going to be emitted, and has to warn conservatively, leading
> to false positives particularly for inline functions.
> 
> The third patch is a sketch of an attempt to address this by delaying the
> warning until expand time.  We can't use front end information at this point
> because it has been thrown away by pass_ipa_free_lang_data, so I'm resorting
> to pre-generating the warning and stashing it away. This is several kinds of
> kludge, including hashing on a call location, but it greatly improves the
> accuracy of the warning.  I'm hoping that someone will have a better idea
> about how to approach this.

Is the intent that we emit different warnings between e.g.
-fkeep-inline-functions vs. without that, or -O0 vs. -O2?
Do you want to warn just on the out of line definitions (visible outside of
the containing TU), or also calls to those?

For e.g. warning on calls to those functions it could be similar to the
warning attribute - where we warn if a call to function with that attribute
is expanded - if it is inlined or optimized away etc., we don't warn.

For warnings on the definitions, perhaps stick it into the IL (could be
internal fn, we have e.g. IFN_ANNOTATE that can hold some info, though right
now it is removed quite early).  We'd need to remove it when inlining from
the IL, and decide what to do if such functions are e.g. cloned (SRA,
IPA-CP, ...).

	Jakub

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: RFC: C++ PATCH to adjust empty class parameter passing ABI
  2016-04-13 15:32 ` Jakub Jelinek
@ 2016-04-13 15:54   ` Jason Merrill
  2016-04-13 19:11     ` Jason Merrill
  0 siblings, 1 reply; 16+ messages in thread
From: Jason Merrill @ 2016-04-13 15:54 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches List, Jonathan Wakely, H.J. Lu

On 04/13/2016 11:32 AM, Jakub Jelinek wrote:
> On Tue, Apr 12, 2016 at 04:27:48PM -0400, Jason Merrill wrote:
>> Unfortunately, a drawback of doing this in the front end is that it's
>> difficult to warn only about affected cases; the front end doesn't know
>> what's actually going to be emitted, and has to warn conservatively, leading
>> to false positives particularly for inline functions.
>>
>> The third patch is a sketch of an attempt to address this by delaying the
>> warning until expand time.  We can't use front end information at this point
>> because it has been thrown away by pass_ipa_free_lang_data, so I'm resorting
>> to pre-generating the warning and stashing it away. This is several kinds of
>> kludge, including hashing on a call location, but it greatly improves the
>> accuracy of the warning.  I'm hoping that someone will have a better idea
>> about how to approach this.
>
> Is the intent that we emit different warnings between e.g.
> -fkeep-inline-functions vs. without that, or -O0 vs. -O2?
> Do you want to warn just on the out of line definitions (visible outside of
> the containing TU), or also calls to those?

That's what I'm thinking, since only actually emitted definitions/calls 
have ABI.

> For e.g. warning on calls to those functions it could be similar to the
> warning attribute - where we warn if a call to function with that attribute
> is expanded - if it is inlined or optimized away etc., we don't warn.

Ah, that sounds promising, thanks.

Jason

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: RFC: C++ PATCH to adjust empty class parameter passing ABI
  2016-04-13 15:54   ` Jason Merrill
@ 2016-04-13 19:11     ` Jason Merrill
  2016-04-13 19:18       ` Jakub Jelinek
  0 siblings, 1 reply; 16+ messages in thread
From: Jason Merrill @ 2016-04-13 19:11 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches List, Jonathan Wakely, H.J. Lu

[-- Attachment #1: Type: text/plain, Size: 1677 bytes --]

On 04/13/2016 11:54 AM, Jason Merrill wrote:
> On 04/13/2016 11:32 AM, Jakub Jelinek wrote:
>> On Tue, Apr 12, 2016 at 04:27:48PM -0400, Jason Merrill wrote:
>>> Unfortunately, a drawback of doing this in the front end is that it's
>>> difficult to warn only about affected cases; the front end doesn't know
>>> what's actually going to be emitted, and has to warn conservatively,
>>> leading
>>> to false positives particularly for inline functions.
>>>
>>> The third patch is a sketch of an attempt to address this by delaying
>>> the
>>> warning until expand time.  We can't use front end information at
>>> this point
>>> because it has been thrown away by pass_ipa_free_lang_data, so I'm
>>> resorting
>>> to pre-generating the warning and stashing it away. This is several
>>> kinds of
>>> kludge, including hashing on a call location, but it greatly improves
>>> the
>>> accuracy of the warning.  I'm hoping that someone will have a better
>>> idea
>>> about how to approach this.
>>
>> Is the intent that we emit different warnings between e.g.
>> -fkeep-inline-functions vs. without that, or -O0 vs. -O2?
>> Do you want to warn just on the out of line definitions (visible
>> outside of
>> the containing TU), or also calls to those?
>
> That's what I'm thinking, since only actually emitted definitions/calls
> have ABI.
>
>> For e.g. warning on calls to those functions it could be similar to the
>> warning attribute - where we warn if a call to function with that
>> attribute
>> is expanded - if it is inlined or optimized away etc., we don't warn.
>
> Ah, that sounds promising, thanks.

Here's a patch along those lines, that passes testing.  Does it look OK?


[-- Attachment #2: warn-attr.patch --]
[-- Type: text/x-patch, Size: 7626 bytes --]

commit 761983a023b5217ef831a43f423779940c788ecf
Author: Jason Merrill <jason@redhat.com>
Date:   Tue Apr 12 13:16:50 2016 -0400

    gcc/
    	* cfgexpand.c (pass_expand::execute): Handle attribute abi_warning.
    	* expr.c (expand_expr_real_1): Likewise.
    gcc/cp/
    	* call.c (empty_class_msg, mark_for_abi_warning): New.
    	(build_call_a): Use them.
    	* decl.c (store_parm_decls): Use mark_for_abi_warning.
    	* error.c (pp_format_to_string): New.

diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index 1341c14..4e5ccbb 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -73,6 +73,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "builtins.h"
 #include "tree-chkp.h"
 #include "rtl-chkp.h"
+#include "langhooks.h"
 
 /* Some systems use __main in a way incompatible with its use in gcc, in these
    cases use the macros NAME__MAIN to give a quoted symbol and SYMBOL__MAIN to
@@ -6221,6 +6222,15 @@ pass_expand::execute (function *fun)
 		 (int) PARAM_VALUE (PARAM_SSP_BUFFER_SIZE));
     }
 
+  if (warn_abi)
+    if (tree attr = lookup_attribute ("abi_warning",
+				      DECL_ATTRIBUTES (current_function_decl)))
+      warning_at (DECL_SOURCE_LOCATION (current_function_decl),
+		  OPT_Wabi, "definition of %qs: %s",
+		  identifier_to_locale (lang_hooks.decl_printable_name
+					(current_function_decl, 1)),
+		  TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr))));
+
   /* Set up parameters and prepare for return, for the function.  */
   expand_function_start (current_function_decl);
 
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 9cee3b7..97d790f 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -401,7 +401,10 @@ build_call_a (tree function, int n, tree *argarray)
 	else if (!warned && empty_arg)
 	  {
 	    location_t loc = EXPR_LOC_OR_LOC (empty_arg, input_location);
-	    warn_empty_class_abi (empty_arg, loc);
+	    if (decl && !varargs_function_p (decl))
+	      mark_for_abi_warning (decl, empty_arg);
+	    else
+	      warn_empty_class_abi (empty_arg, loc);
 	    warned = true;
 	  }
       }
@@ -7019,8 +7022,19 @@ empty_class_arg (tree val)
   return build2 (COMPOUND_EXPR, etype, val, empty);
 }
 
-/* Warn about the change in empty class parameter passing ABI.  Returns true
-   if we warned.  */
+/* Generate a message warning about the change in empty class parameter passing ABI.  */
+
+static tree
+empty_class_msg (tree type)
+{
+  if (!TYPE_P (type))
+    type = TREE_TYPE (type);
+
+  return pp_format_to_string ("empty class %qT parameter passing ABI "
+			      "changes in -fabi-version=10 (GCC 6)", type);
+}
+
+/* Warn immediately about the change in empty class parameter ABI.  */
 
 void
 warn_empty_class_abi (tree arg, location_t loc)
@@ -7028,19 +7042,24 @@ warn_empty_class_abi (tree arg, location_t loc)
   if (!warn_abi || !abi_version_crosses (10))
     return;
 
-  tree type;
-  if (TYPE_P (arg))
-    type = arg;
-  else
-    {
-      if (TREE_TYPE (arg) == empty_struct_type
-	  && TREE_CODE (arg) == COMPOUND_EXPR)
-	arg = TREE_OPERAND (arg, 0);
-      type = TREE_TYPE (arg);
-    }
+  warning_at (loc, OPT_Wabi, "%E", empty_class_msg (arg));
+}
 
-  warning_at (loc, OPT_Wabi, "empty class %qT parameter passing ABI "
-	      "changes in -fabi-version=10 (GCC 6)", type);
+/* Tack a warning about the change in empty class parameter ABI onto FN, so
+   that we get a warning if a definition or call is emitted.  */
+
+void
+mark_for_abi_warning (tree fn, tree type)
+{
+  if (!warn_abi || !abi_version_crosses (10))
+    return;
+  if (lookup_attribute ("abi_warning", DECL_ATTRIBUTES (fn)))
+    return;
+
+  tree msg = empty_class_msg (type);
+  msg = build_tree_list (NULL_TREE, msg);
+  DECL_ATTRIBUTES (fn) = tree_cons (get_identifier ("abi_warning"), msg,
+				    DECL_ATTRIBUTES (fn));
 }
 
 /* Returns the type which will really be used for passing an argument of
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 60abbaa..8d721c7 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5540,6 +5540,7 @@ extern tree build_addr_func			(tree, tsubst_flags_t);
 extern void set_flags_from_callee		(tree);
 extern tree build_call_a			(tree, int, tree*);
 extern tree build_call_n			(tree, int, ...);
+extern void mark_for_abi_warning		(tree, tree);
 extern void warn_empty_class_abi		(tree, location_t);
 extern bool null_ptr_cst_p			(tree);
 extern bool null_member_pointer_value_p		(tree);
@@ -5895,6 +5896,7 @@ extern bool pedwarn_cxx98                       (location_t, int, const char *,
 extern location_t location_of                   (tree);
 extern void qualified_name_lookup_error		(tree, tree, tree,
 						 location_t);
+extern tree pp_format_to_string			(const char *, ...);
 
 /* in except.c */
 extern void init_exception_processing		(void);
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 0326900..7099199 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -14357,7 +14357,7 @@ store_parm_decls (tree current_function_parms)
 		       && (saw_nonempty
 			   || varargs_function_p (current_function_decl)))
 		{
-		  warn_empty_class_abi (parm, DECL_SOURCE_LOCATION (parm));
+		  mark_for_abi_warning (current_function_decl, type);
 		  warned = true;
 		}
 	    }
diff --git a/gcc/cp/error.c b/gcc/cp/error.c
index aa5fd41..5aaa177 100644
--- a/gcc/cp/error.c
+++ b/gcc/cp/error.c
@@ -3718,3 +3718,25 @@ qualified_name_lookup_error (tree scope, tree name,
       suggest_alternatives_for (location, name);
     }
 }
+
+/* Like error et al, but return the formatted message as a STRING_CST.  */
+
+tree
+pp_format_to_string (const char *msg, ...)
+{
+  pretty_printer *pp = global_dc->printer;
+  text_info text;
+  va_list ap;
+
+  va_start (ap, msg);
+  text.err_no = errno;
+  text.args_ptr = &ap;
+  text.format_spec = msg;
+  pp_format (pp, &text);
+  pp_output_formatted_text (pp);
+  va_end (ap);
+  const char *fmt = pp_formatted_text (pp);
+  tree str = build_string (strlen (fmt) + 1, fmt);
+  pp_clear_output_area (pp);
+  return str;
+}
diff --git a/gcc/expr.c b/gcc/expr.c
index 29d22b0..f852c51 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -10579,6 +10579,13 @@ expand_expr_real_1 (tree exp, rtx target, machine_mode tmode,
 		      0, "%Kcall to %qs declared with attribute warning: %s",
 		      exp, identifier_to_locale (lang_hooks.decl_printable_name (fndecl, 1)),
 		      TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr))));
+	if (warn_abi && fndecl
+	    && (attr = lookup_attribute ("abi_warning",
+					 DECL_ATTRIBUTES (fndecl))) != NULL)
+	  warning_at (tree_nonartificial_location (exp),
+		      OPT_Wabi, "%Kcall to %qs: %s",
+		      exp, identifier_to_locale (lang_hooks.decl_printable_name (fndecl, 1)),
+		      TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr))));
 
 	/* Check for a built-in function.  */
 	if (fndecl && DECL_BUILT_IN (fndecl))
diff --git a/gcc/testsuite/g++.dg/abi/empty20.C b/gcc/testsuite/g++.dg/abi/empty20.C
new file mode 100644
index 0000000..be1e826
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/empty20.C
@@ -0,0 +1,25 @@
+// { dg-options "-Wabi=9 -O0" }
+
+struct A { };
+
+void f(A, A) { }		// No warning, trailing parms all empty
+void f(A, A, int) { }		// { dg-warning "ABI" }
+__attribute__ ((always_inline))
+inline void f(A a, int i) { }	// No warning, always inlined
+__attribute__ ((always_inline))
+inline void f2(A a, int i)	// But the call within the fn gets a warning
+{				// when it's inlined into main
+  f(a,a,i);			// { dg-warning "ABI" }
+}
+inline void f3(A a, int i)	// This one is never called
+{
+  f(a,a,i);
+}
+int main()
+{
+  A a;
+  f(a,a);
+  f(a,a,42);			// { dg-warning "ABI" }
+  f(a,42);
+  f2(a,42);
+}

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: RFC: C++ PATCH to adjust empty class parameter passing ABI
  2016-04-13 19:11     ` Jason Merrill
@ 2016-04-13 19:18       ` Jakub Jelinek
  2016-04-13 20:12         ` Jason Merrill
  0 siblings, 1 reply; 16+ messages in thread
From: Jakub Jelinek @ 2016-04-13 19:18 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches List, Jonathan Wakely, H.J. Lu

On Wed, Apr 13, 2016 at 03:11:34PM -0400, Jason Merrill wrote:
> commit 761983a023b5217ef831a43f423779940c788ecf
> Author: Jason Merrill <jason@redhat.com>
> Date:   Tue Apr 12 13:16:50 2016 -0400
> 
>     gcc/
>     	* cfgexpand.c (pass_expand::execute): Handle attribute abi_warning.
>     	* expr.c (expand_expr_real_1): Likewise.
>     gcc/cp/
>     	* call.c (empty_class_msg, mark_for_abi_warning): New.
>     	(build_call_a): Use them.
>     	* decl.c (store_parm_decls): Use mark_for_abi_warning.
>     	* error.c (pp_format_to_string): New.

I think you should put a space into the attribute name instead of _
to make it clear that it is not an attribute users can use directly in their
code through __attribute__.

Otherwise it looks reasonable to me.

	Jakub

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: RFC: C++ PATCH to adjust empty class parameter passing ABI
  2016-04-13 19:18       ` Jakub Jelinek
@ 2016-04-13 20:12         ` Jason Merrill
  2016-04-13 22:56           ` H.J. Lu
                             ` (2 more replies)
  0 siblings, 3 replies; 16+ messages in thread
From: Jason Merrill @ 2016-04-13 20:12 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches List, Jonathan Wakely, H.J. Lu

On 04/13/2016 03:18 PM, Jakub Jelinek wrote:
> On Wed, Apr 13, 2016 at 03:11:34PM -0400, Jason Merrill wrote:
>> commit 761983a023b5217ef831a43f423779940c788ecf
>> Author: Jason Merrill <jason@redhat.com>
>> Date:   Tue Apr 12 13:16:50 2016 -0400
>>
>>      gcc/
>>      	* cfgexpand.c (pass_expand::execute): Handle attribute abi_warning.
>>      	* expr.c (expand_expr_real_1): Likewise.
>>      gcc/cp/
>>      	* call.c (empty_class_msg, mark_for_abi_warning): New.
>>      	(build_call_a): Use them.
>>      	* decl.c (store_parm_decls): Use mark_for_abi_warning.
>>      	* error.c (pp_format_to_string): New.
>
> I think you should put a space into the attribute name instead of _
> to make it clear that it is not an attribute users can use directly in their
> code through __attribute__.
>
> Otherwise it looks reasonable to me.

Thanks, applied with that change.

Jason

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: RFC: C++ PATCH to adjust empty class parameter passing ABI
  2016-04-12 20:28 RFC: C++ PATCH to adjust empty class parameter passing ABI Jason Merrill
  2016-04-13 15:32 ` Jakub Jelinek
@ 2016-04-13 22:01 ` Jonathan Wakely
  1 sibling, 0 replies; 16+ messages in thread
From: Jonathan Wakely @ 2016-04-13 22:01 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches List, H.J. Lu, libstdc++

[-- Attachment #1: Type: text/plain, Size: 1125 bytes --]

On 12/04/16 16:27 -0400, Jason Merrill wrote:
>A revision of the patch previously posted at
>
>  https://gcc.gnu.org/ml/gcc-patches/2016-03/msg00841.html
>
>To recap quickly, the C++ compiler has used a different calling 
>convention for passing empty classes, because C++ says they have size 
>1, while the GCC C extension gives them size 0.  But this difference 
>doesn't mean that they need to be passed differently; in either case 
>there's no actual data involved.  And the Clang folks recently pointed 
>out that if you squint properly, it seems that the x86_64 psABI says 
>that an empty class shouldn't be passed either in registers or memory. 
>H.J. had a back-end fix, but it seemed to me that a front-end fix 
>would be simpler and safer.
>
>The first patch corrects errors in what the C++ front end considers an 
>empty classes; unnamed bit-fields aren't really members.  I'm checking 
>this in now.
>
>The second patch is the actual ABI change under -fabi-version=10, 
>including a warning.  Jonathan, please check this in with your library 
>changes.

I've committed the library changes as the attached patch.


[-- Attachment #2: patch.txt --]
[-- Type: text/x-patch, Size: 18725 bytes --]

commit dd715d3cb76a536aa4496e9efdf3f932aeb605ff
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Wed Apr 13 20:47:23 2016 +0100

    Adjust for new empty class parameter passing ABI.
    
    	* include/bits/c++config (_GLIBCXX_BEGIN_NAMESPACE_EMPTY_TYPES,
    	_GLIBCXX_END_NAMESPACE_EMPTY_TYPES, _GLIBCXX_ABI_TAG_EMPTY): Define.
    	* include/bits/hashtable.h (_Hashtable::_M_emplace): Change signatures
    	of functions taking empty structs by value. Add a template parameter
    	to overloads without hints. Rename overloads with hints to
    	_M_emplace_hint.
    	(_Hashtable::_M_erase(true_type, const_iterator),
    	_Hashtable::_M_erase(false_type, const_iterator)): Change signatures
    	by reordering parameters.
    	* include/bits/hashtable_policy.h (_Insert::insert): Adjust to call
    	_M_emplace_hint instead of _M_emplace.
    	* include/bits/shared_ptr.h (shared_ptr(_Tp1*, _Deleter, _Alloc),
    	shared_ptr(nullptr_t, _Deleter, _Alloc)): Use _GLIBCXX_ABI_TAG_EMPTY.
    	* include/bits/shared_ptr_base.h (_Sp_counted_deleter, __shared_count,
    	__shared_ptr): Likewise.
    	* include/bits/stl_algo.h (replace_if): Likewise.
    	* include/bits/stl_pair.h (piecewise_construct_t,
    	piecewise_construct): Use _GLIBCXX_BEGIN_NAMESPACE_EMPTY_TYPES.
    	* include/bits/uses_allocator.h (allocator_arg_t, allocator_arg,
    	__uses_alloc0): Likewise.
    	* include/ext/pb_ds/assoc_container.hpp (basic_hash_table): Likewise.
    	* testsuite/20_util/scoped_allocator/69293_neg.cc: Adjust dg-error.
    	* testsuite/20_util/shared_ptr/cons/43820_neg.cc: Likewise.
    	* testsuite/20_util/shared_ptr/cons/void_neg.cc: Likewise.
    	* testsuite/20_util/uses_allocator/69293_neg.cc: Likewise.
    	* testsuite/20_util/uses_allocator/cons_neg.cc: Likewise.
    	* testsuite/ext/profile/mutex_extensions_neg.cc: Likewise.

diff --git a/libstdc++-v3/include/bits/c++config b/libstdc++-v3/include/bits/c++config
index 57024e4..bde003c 100644
--- a/libstdc++-v3/include/bits/c++config
+++ b/libstdc++-v3/include/bits/c++config
@@ -84,13 +84,32 @@
 # define _GLIBCXX_DEPRECATED
 #endif
 
+#if __cplusplus
+
 // Macros for ABI tag attributes.
 #ifndef _GLIBCXX_ABI_TAG_CXX11
 # define _GLIBCXX_ABI_TAG_CXX11 __attribute ((__abi_tag__ ("cxx11")))
 #endif
 
-
-#if __cplusplus
+#if __GXX_ABI_VERSION >= 1010
+namespace std
+{
+  inline namespace _V2 { }
+}
+# define _GLIBCXX_BEGIN_NAMESPACE_EMPTY_TYPES \
+  _GLIBCXX_END_NAMESPACE_VERSION \
+  namespace _V2 { \
+  _GLIBCXX_BEGIN_NAMESPACE_VERSION
+# define _GLIBCXX_END_NAMESPACE_EMPTY_TYPES \
+  _GLIBCXX_END_NAMESPACE_VERSION \
+  } \
+  _GLIBCXX_BEGIN_NAMESPACE_VERSION
+# define _GLIBCXX_ABI_TAG_EMPTY __attribute ((__abi_tag__ ("cxxempty")))
+#else
+# define _GLIBCXX_BEGIN_NAMESPACE_EMPTY_TYPES
+# define _GLIBCXX_END_NAMESPACE_EMPTY_TYPES
+# define _GLIBCXX_ABI_TAG_EMPTY
+#endif
 
 // Macro for constexpr, to support in mixed 03/0x mode.
 #ifndef _GLIBCXX_CONSTEXPR
diff --git a/libstdc++-v3/include/bits/hashtable.h b/libstdc++-v3/include/bits/hashtable.h
index 5748920..22b7187 100644
--- a/libstdc++-v3/include/bits/hashtable.h
+++ b/libstdc++-v3/include/bits/hashtable.h
@@ -663,24 +663,26 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _M_insert_multi_node(__node_type* __hint,
 			   __hash_code __code, __node_type* __n);
 
-      template<typename... _Args>
-	std::pair<iterator, bool>
-	_M_emplace(std::true_type, _Args&&... __args);
+      template<bool _Uniq, typename... _Args>
+	typename enable_if<_Uniq, std::pair<iterator, bool>>::type
+	_M_emplace(__bool_constant<_Uniq>, _Args&&... __args);
 
-      template<typename... _Args>
-	iterator
-	_M_emplace(std::false_type __uk, _Args&&... __args)
-	{ return _M_emplace(cend(), __uk, std::forward<_Args>(__args)...); }
+      template<bool _Uniq, typename... _Args>
+	typename enable_if<!_Uniq, iterator>::type
+	_M_emplace(__bool_constant<_Uniq> __uk, _Args&&... __args)
+	{
+	  return _M_emplace_hint(cend(), __uk, std::forward<_Args>(__args)...);
+	}
 
       // Emplace with hint, useless when keys are unique.
       template<typename... _Args>
 	iterator
-	_M_emplace(const_iterator, std::true_type __uk, _Args&&... __args)
+	_M_emplace_hint(const_iterator, std::true_type __uk, _Args&&... __args)
 	{ return _M_emplace(__uk, std::forward<_Args>(__args)...).first; }
 
       template<typename... _Args>
 	iterator
-	_M_emplace(const_iterator, std::false_type, _Args&&... __args);
+	_M_emplace_hint(const_iterator, std::false_type, _Args&&... __args);
 
       template<typename _Arg, typename _NodeGenerator>
 	std::pair<iterator, bool>
@@ -712,10 +714,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 		  const _NodeGenerator&, std::false_type);
 
       size_type
-      _M_erase(std::true_type, const key_type&);
+      _M_erase(const key_type&, std::true_type);
 
       size_type
-      _M_erase(std::false_type, const key_type&);
+      _M_erase(const key_type&, std::false_type);
 
       iterator
       _M_erase(size_type __bkt, __node_base* __prev_n, __node_type* __n);
@@ -731,8 +733,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	iterator
 	emplace_hint(const_iterator __hint, _Args&&... __args)
 	{
-	  return _M_emplace(__hint, __unique_keys(),
-			    std::forward<_Args>(__args)...);
+	  return _M_emplace_hint(__hint, __unique_keys(),
+				 std::forward<_Args>(__args)...);
 	}
 
       // Insert member functions via inheritance.
@@ -748,7 +750,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       size_type
       erase(const key_type& __k)
-      { return _M_erase(__unique_keys(), __k); }
+      { return _M_erase(__k, __unique_keys()); }
 
       iterator
       erase(const_iterator, const_iterator);
@@ -1502,12 +1504,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	   typename _Alloc, typename _ExtractKey, typename _Equal,
 	   typename _H1, typename _H2, typename _Hash, typename _RehashPolicy,
 	   typename _Traits>
-    template<typename... _Args>
+    template<bool _Uniq, typename... _Args>
       auto
       _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
 		 _H1, _H2, _Hash, _RehashPolicy, _Traits>::
-      _M_emplace(std::true_type, _Args&&... __args)
-      -> pair<iterator, bool>
+      _M_emplace(__bool_constant<_Uniq>, _Args&&... __args)
+      -> typename enable_if<_Uniq, pair<iterator, bool>>::type
       {
 	// First build the node to get access to the hash code
 	__node_type* __node = this->_M_allocate_node(std::forward<_Args>(__args)...);
@@ -1544,7 +1546,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       auto
       _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
 		 _H1, _H2, _Hash, _RehashPolicy, _Traits>::
-      _M_emplace(const_iterator __hint, std::false_type, _Args&&... __args)
+      _M_emplace_hint(const_iterator __hint, std::false_type, _Args&&... __args)
       -> iterator
       {
 	// First build the node to get its hash code.
@@ -1769,7 +1771,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     auto
     _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
 	       _H1, _H2, _Hash, _RehashPolicy, _Traits>::
-    _M_erase(std::true_type, const key_type& __k)
+    _M_erase(const key_type& __k, std::true_type)
     -> size_type
     {
       __hash_code __code = this->_M_hash_code(__k);
@@ -1793,7 +1795,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     auto
     _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
 	       _H1, _H2, _Hash, _RehashPolicy, _Traits>::
-    _M_erase(std::false_type, const key_type& __k)
+    _M_erase(const key_type& __k, std::false_type)
     -> size_type
     {
       __hash_code __code = this->_M_hash_code(__k);
diff --git a/libstdc++-v3/include/bits/hashtable_policy.h b/libstdc++-v3/include/bits/hashtable_policy.h
index 7a2ac92..ceb78b4 100644
--- a/libstdc++-v3/include/bits/hashtable_policy.h
+++ b/libstdc++-v3/include/bits/hashtable_policy.h
@@ -906,8 +906,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	insert(const_iterator __hint, _Pair&& __v)
 	{
 	  __hashtable& __h = this->_M_conjure_hashtable();
-	  return __h._M_emplace(__hint, __unique_keys(),
-				std::forward<_Pair>(__v));
+	  return __h._M_emplace_hint(__hint, __unique_keys(),
+				     std::forward<_Pair>(__v));
 	}
    };
 
diff --git a/libstdc++-v3/include/bits/shared_ptr.h b/libstdc++-v3/include/bits/shared_ptr.h
index b22477e..f4c2754 100644
--- a/libstdc++-v3/include/bits/shared_ptr.h
+++ b/libstdc++-v3/include/bits/shared_ptr.h
@@ -166,6 +166,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        *  __shared_ptr will release __p by calling __d(__p)
        */
       template<typename _Tp1, typename _Deleter, typename _Alloc>
+	_GLIBCXX_ABI_TAG_EMPTY
 	shared_ptr(_Tp1* __p, _Deleter __d, _Alloc __a)
 	: __shared_ptr<_Tp>(__p, __d, std::move(__a)) { }
 
@@ -185,6 +186,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        *  The last owner will call __d(__p)
        */
       template<typename _Deleter, typename _Alloc>
+	_GLIBCXX_ABI_TAG_EMPTY
 	shared_ptr(nullptr_t __p, _Deleter __d, _Alloc __a)
 	: __shared_ptr<_Tp>(__p, __d, std::move(__a)) { }
 
diff --git a/libstdc++-v3/include/bits/shared_ptr_base.h b/libstdc++-v3/include/bits/shared_ptr_base.h
index e844c9c..6d523e1 100644
--- a/libstdc++-v3/include/bits/shared_ptr_base.h
+++ b/libstdc++-v3/include/bits/shared_ptr_base.h
@@ -436,6 +436,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	typedef _Sp_ebo_helper<1, _Alloc>	_Alloc_base;
 
       public:
+	_GLIBCXX_ABI_TAG_EMPTY
 	_Impl(_Ptr __p, _Deleter __d, const _Alloc& __a) noexcept
 	: _M_ptr(__p), _Del_base(__d), _Alloc_base(__a)
 	{ }
@@ -454,6 +455,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       : _M_impl(__p, __d, _Alloc()) { }
 
       // __d(__p) must not throw.
+      _GLIBCXX_ABI_TAG_EMPTY
       _Sp_counted_deleter(_Ptr __p, _Deleter __d, const _Alloc& __a) noexcept
       : _M_impl(__p, __d, __a) { }
 
@@ -584,6 +586,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	{ }
 
       template<typename _Ptr, typename _Deleter, typename _Alloc>
+	_GLIBCXX_ABI_TAG_EMPTY
 	__shared_count(_Ptr __p, _Deleter __d, _Alloc __a) : _M_pi(0)
 	{
 	  typedef _Sp_counted_deleter<_Ptr, _Deleter, _Alloc, _Lp> _Sp_cd_type;
@@ -900,6 +903,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	}
 
       template<typename _Tp1, typename _Deleter, typename _Alloc>
+	_GLIBCXX_ABI_TAG_EMPTY
 	__shared_ptr(_Tp1* __p, _Deleter __d, _Alloc __a)
 	: _M_ptr(__p), _M_refcount(__p, __d, std::move(__a))
 	{
@@ -914,6 +918,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	{ }
 
       template<typename _Deleter, typename _Alloc>
+	_GLIBCXX_ABI_TAG_EMPTY
         __shared_ptr(nullptr_t __p, _Deleter __d, _Alloc __a)
 	: _M_ptr(0), _M_refcount(__p, __d, std::move(__a))
 	{ }
@@ -1039,6 +1044,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	{ __shared_ptr(__p, __d).swap(*this); }
 
       template<typename _Tp1, typename _Deleter, typename _Alloc>
+	_GLIBCXX_ABI_TAG_EMPTY
 	void
         reset(_Tp1* __p, _Deleter __d, _Alloc __a)
         { __shared_ptr(__p, __d, std::move(__a)).swap(*this); }
diff --git a/libstdc++-v3/include/bits/stl_algo.h b/libstdc++-v3/include/bits/stl_algo.h
index fbd03a7..28e1e3b 100644
--- a/libstdc++-v3/include/bits/stl_algo.h
+++ b/libstdc++-v3/include/bits/stl_algo.h
@@ -4270,6 +4270,7 @@ _GLIBCXX_BEGIN_NAMESPACE_ALGO
    *  is true then the assignment @c *i = @p __new_value is performed.
   */
   template<typename _ForwardIterator, typename _Predicate, typename _Tp>
+    _GLIBCXX_ABI_TAG_EMPTY
     void
     replace_if(_ForwardIterator __first, _ForwardIterator __last,
 	       _Predicate __pred, const _Tp& __new_value)
diff --git a/libstdc++-v3/include/bits/stl_pair.h b/libstdc++-v3/include/bits/stl_pair.h
index 37ee5cc..e6ff00e 100644
--- a/libstdc++-v3/include/bits/stl_pair.h
+++ b/libstdc++-v3/include/bits/stl_pair.h
@@ -72,12 +72,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
    */
 
 #if __cplusplus >= 201103L
+
+_GLIBCXX_BEGIN_NAMESPACE_EMPTY_TYPES
+
   /// piecewise_construct_t
   struct piecewise_construct_t { explicit piecewise_construct_t() = default; };
 
   /// piecewise_construct
   constexpr piecewise_construct_t piecewise_construct = piecewise_construct_t();
 
+_GLIBCXX_END_NAMESPACE_EMPTY_TYPES
+
   // Forward declarations.
   template<typename...>
     class tuple;
diff --git a/libstdc++-v3/include/bits/uses_allocator.h b/libstdc++-v3/include/bits/uses_allocator.h
index b1ff58a..86162a8 100644
--- a/libstdc++-v3/include/bits/uses_allocator.h
+++ b/libstdc++-v3/include/bits/uses_allocator.h
@@ -42,11 +42,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     using __is_erased_or_convertible
       = __or_<is_same<_Tp, __erased_type>, is_convertible<_Alloc, _Tp>>;
 
+_GLIBCXX_BEGIN_NAMESPACE_EMPTY_TYPES
+
   /// [allocator.tag]
   struct allocator_arg_t { explicit allocator_arg_t() = default; };
 
   constexpr allocator_arg_t allocator_arg = allocator_arg_t();
 
+_GLIBCXX_END_NAMESPACE_EMPTY_TYPES
+
   template<typename _Tp, typename _Alloc, typename = __void_t<>>
     struct __uses_allocator_helper
     : false_type { };
@@ -65,11 +69,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   struct __uses_alloc_base { };
 
+_GLIBCXX_BEGIN_NAMESPACE_EMPTY_TYPES
+
   struct __uses_alloc0 : __uses_alloc_base
   {
     struct _Sink { void operator=(const void*) { } } _M_a;
   };
 
+_GLIBCXX_END_NAMESPACE_EMPTY_TYPES
+
   template<typename _Alloc>
     struct __uses_alloc1 : __uses_alloc_base { const _Alloc* _M_a; };
 
diff --git a/libstdc++-v3/include/ext/pb_ds/assoc_container.hpp b/libstdc++-v3/include/ext/pb_ds/assoc_container.hpp
index 571d946..a6b1e27 100644
--- a/libstdc++-v3/include/ext/pb_ds/assoc_container.hpp
+++ b/libstdc++-v3/include/ext/pb_ds/assoc_container.hpp
@@ -120,36 +120,44 @@ namespace __gnu_pbds
       basic_hash_table(T0 t0) : base_type(t0) { }
 
     template<typename T0, typename T1>
+      _GLIBCXX_ABI_TAG_EMPTY
       basic_hash_table(T0 t0, T1 t1) : base_type(t0, t1) { }
 
     template<typename T0, typename T1, typename T2>
+      _GLIBCXX_ABI_TAG_EMPTY
       basic_hash_table(T0 t0, T1 t1, T2 t2) : base_type(t0, t1, t2) { }
 
     template<typename T0, typename T1, typename T2, typename T3>
+      _GLIBCXX_ABI_TAG_EMPTY
       basic_hash_table(T0 t0, T1 t1, T2 t2, T3 t3)
       : base_type(t0, t1, t2, t3) { }
 
     template<typename T0, typename T1, typename T2, typename T3, typename T4>
+      _GLIBCXX_ABI_TAG_EMPTY
       basic_hash_table(T0 t0, T1 t1, T2 t2, T3 t3, T4 t4)
       : base_type(t0, t1, t2, t3, t4) { }
 
     template<typename T0, typename T1, typename T2, typename T3, typename T4,
 	     typename T5>
+      _GLIBCXX_ABI_TAG_EMPTY
       basic_hash_table(T0 t0, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5)
       : base_type(t0, t1, t2, t3, t4, t5) { }
 
     template<typename T0, typename T1, typename T2, typename T3, typename T4,
 	     typename T5, typename T6>
+      _GLIBCXX_ABI_TAG_EMPTY
       basic_hash_table(T0 t0, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6)
       : base_type(t0, t1, t2, t3, t4, t5, t6) { }
 
     template<typename T0, typename T1, typename T2, typename T3, typename T4,
 	     typename T5, typename T6, typename T7>
+      _GLIBCXX_ABI_TAG_EMPTY
       basic_hash_table(T0 t0, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7)
       : base_type(t0, t1, t2, t3, t4, t5, t6, t7) { }
 
     template<typename T0, typename T1, typename T2, typename T3, typename T4,
 	     typename T5, typename T6, typename T7, typename T8>
+      _GLIBCXX_ABI_TAG_EMPTY
       basic_hash_table(T0 t0, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6,
 		       T7 t7, T8 t8)
       : base_type(t0, t1, t2, t3, t4, t5, t6, t7, t8)
diff --git a/libstdc++-v3/testsuite/20_util/scoped_allocator/69293_neg.cc b/libstdc++-v3/testsuite/20_util/scoped_allocator/69293_neg.cc
index f3b2d87..5487b0a 100644
--- a/libstdc++-v3/testsuite/20_util/scoped_allocator/69293_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/scoped_allocator/69293_neg.cc
@@ -47,5 +47,5 @@ test01()
   scoped_alloc sa;
   auto p = sa.allocate(1);
   sa.construct(p);  // this is required to be ill-formed
-  // { dg-error "static assertion failed" "" { target *-*-* } 89 }
+  // { dg-error "static assertion failed" "" { target *-*-* } 97 }
 }
diff --git a/libstdc++-v3/testsuite/20_util/shared_ptr/cons/43820_neg.cc b/libstdc++-v3/testsuite/20_util/shared_ptr/cons/43820_neg.cc
index 395094f..a0ce1f5 100644
--- a/libstdc++-v3/testsuite/20_util/shared_ptr/cons/43820_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/shared_ptr/cons/43820_neg.cc
@@ -32,7 +32,7 @@ void test01()
 {
   X* px = 0;
   std::shared_ptr<X> p1(px);   // { dg-error "here" }
-  // { dg-error "incomplete" "" { target *-*-* } 889 }
+  // { dg-error "incomplete" "" { target *-*-* } 892 }
 
   std::shared_ptr<X> p9(ap());  // { dg-error "here" }
   // { dg-error "incomplete" "" { target *-*-* } 307 }
diff --git a/libstdc++-v3/testsuite/20_util/shared_ptr/cons/void_neg.cc b/libstdc++-v3/testsuite/20_util/shared_ptr/cons/void_neg.cc
index 8843ffe..88036fd 100644
--- a/libstdc++-v3/testsuite/20_util/shared_ptr/cons/void_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/shared_ptr/cons/void_neg.cc
@@ -25,5 +25,5 @@
 void test01()
 {
   std::shared_ptr<void> p((void*)nullptr);   // { dg-error "here" }
-  // { dg-error "incomplete" "" { target *-*-* } 888 }
+  // { dg-error "incomplete" "" { target *-*-* } 891 }
 }
diff --git a/libstdc++-v3/testsuite/20_util/uses_allocator/69293_neg.cc b/libstdc++-v3/testsuite/20_util/uses_allocator/69293_neg.cc
index 19417fc..88e1ea1 100644
--- a/libstdc++-v3/testsuite/20_util/uses_allocator/69293_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/uses_allocator/69293_neg.cc
@@ -45,5 +45,5 @@ test01()
 {
   alloc_type a;
   std::tuple<X> t(std::allocator_arg, a); // this is required to be ill-formed
-  // { dg-error "static assertion failed" "" { target *-*-* } 89 }
+  // { dg-error "static assertion failed" "" { target *-*-* } 97 }
 }
diff --git a/libstdc++-v3/testsuite/20_util/uses_allocator/cons_neg.cc b/libstdc++-v3/testsuite/20_util/uses_allocator/cons_neg.cc
index b3df4ae..e4cd276 100644
--- a/libstdc++-v3/testsuite/20_util/uses_allocator/cons_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/uses_allocator/cons_neg.cc
@@ -44,4 +44,4 @@ void test01()
 
   tuple<Type> t(allocator_arg, a, 1);
 }
-// { dg-error "static assertion failed" "" { target *-*-* } 89 }
+// { dg-error "static assertion failed" "" { target *-*-* } 97 }
diff --git a/libstdc++-v3/testsuite/ext/profile/mutex_extensions_neg.cc b/libstdc++-v3/testsuite/ext/profile/mutex_extensions_neg.cc
index e59d666..8ffdee8 100644
--- a/libstdc++-v3/testsuite/ext/profile/mutex_extensions_neg.cc
+++ b/libstdc++-v3/testsuite/ext/profile/mutex_extensions_neg.cc
@@ -25,7 +25,7 @@
 
 #include <vector>
 
-// { dg-error "multiple inlined namespaces" "" { target *-*-* } 324 }
+// { dg-error "multiple inlined namespaces" "" { target *-*-* } 343 }
 
 // "template argument 1 is invalid"
 // { dg-prune-output "tuple:993" }

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: RFC: C++ PATCH to adjust empty class parameter passing ABI
  2016-04-13 20:12         ` Jason Merrill
@ 2016-04-13 22:56           ` H.J. Lu
  2016-04-14  0:35           ` H.J. Lu
  2016-04-14  8:03           ` Christophe Lyon
  2 siblings, 0 replies; 16+ messages in thread
From: H.J. Lu @ 2016-04-13 22:56 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Jakub Jelinek, gcc-patches List, Jonathan Wakely

[-- Attachment #1: Type: text/plain, Size: 1140 bytes --]

On Wed, Apr 13, 2016 at 1:12 PM, Jason Merrill <jason@redhat.com> wrote:
> On 04/13/2016 03:18 PM, Jakub Jelinek wrote:
>>
>> On Wed, Apr 13, 2016 at 03:11:34PM -0400, Jason Merrill wrote:
>>>
>>> commit 761983a023b5217ef831a43f423779940c788ecf
>>> Author: Jason Merrill <jason@redhat.com>
>>> Date:   Tue Apr 12 13:16:50 2016 -0400
>>>
>>>      gcc/
>>>         * cfgexpand.c (pass_expand::execute): Handle attribute
>>> abi_warning.
>>>         * expr.c (expand_expr_real_1): Likewise.
>>>      gcc/cp/
>>>         * call.c (empty_class_msg, mark_for_abi_warning): New.
>>>         (build_call_a): Use them.
>>>         * decl.c (store_parm_decls): Use mark_for_abi_warning.
>>>         * error.c (pp_format_to_string): New.
>>
>>
>> I think you should put a space into the attribute name instead of _
>> to make it clear that it is not an attribute users can use directly in
>> their
>> code through __attribute__.
>>
>> Otherwise it looks reasonable to me.
>
>
> Thanks, applied with that change.
>
> Jason
>

Here are tests from PR 60336 and PR 68355.  They pass
on Linux/x86-64 with -m32/-mx32/-m64.  OK for trunk?

Thanks.

-- 
H.J.

[-- Attachment #2: 0001-Add-more-tests-for-empty-C-class-passing.patch --]
[-- Type: text/x-patch, Size: 12923 bytes --]

From d6420be9ce4f5e9e5c6a97ca66fa07309bfc11f7 Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@gmail.com>
Date: Sun, 15 Nov 2015 13:19:05 -0800
Subject: [PATCH] Add more tests for empty C++ class passing

	PR c++/60336
	PR middle-end/67239
	PR target/68355
	* g++.dg/pr60336-1.C: Likewise.
	* g++.dg/pr60336-2.C: Likewise.
	* g++.dg/pr60336-3.C: Likewise.
	* g++.dg/pr60336-4.C: Likewise.
	* g++.dg/pr60336-5.C: Likewise.
	* g++.dg/pr60336-6.C: Likewise.
	* g++.dg/pr60336-7.C: Likewise.
	* g++.dg/pr60336-8.C: Likewise.
	* g++.dg/pr60336-9.C: Likewise.
	* g++.dg/pr60336-10.C: Likewise.
	* g++.dg/pr60336-11.C: Likewise.
	* g++.dg/pr60336-12.C: Likewise.
	* g++.dg/pr68355.C: Likewise.
---
 gcc/testsuite/g++.dg/pr60336-1.C  | 17 ++++++++++++
 gcc/testsuite/g++.dg/pr60336-10.C | 50 ++++++++++++++++++++++++++++++++++
 gcc/testsuite/g++.dg/pr60336-11.C | 56 ++++++++++++++++++++++++++++++++++++++
 gcc/testsuite/g++.dg/pr60336-12.C | 57 +++++++++++++++++++++++++++++++++++++++
 gcc/testsuite/g++.dg/pr60336-2.C  | 48 +++++++++++++++++++++++++++++++++
 gcc/testsuite/g++.dg/pr60336-3.C  | 15 +++++++++++
 gcc/testsuite/g++.dg/pr60336-4.C  | 48 +++++++++++++++++++++++++++++++++
 gcc/testsuite/g++.dg/pr60336-5.C  | 17 ++++++++++++
 gcc/testsuite/g++.dg/pr60336-6.C  | 17 ++++++++++++
 gcc/testsuite/g++.dg/pr60336-7.C  | 17 ++++++++++++
 gcc/testsuite/g++.dg/pr60336-8.C  | 15 +++++++++++
 gcc/testsuite/g++.dg/pr60336-9.C  | 28 +++++++++++++++++++
 gcc/testsuite/g++.dg/pr68355.C    | 24 +++++++++++++++++
 13 files changed, 409 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/pr60336-1.C
 create mode 100644 gcc/testsuite/g++.dg/pr60336-10.C
 create mode 100644 gcc/testsuite/g++.dg/pr60336-11.C
 create mode 100644 gcc/testsuite/g++.dg/pr60336-12.C
 create mode 100644 gcc/testsuite/g++.dg/pr60336-2.C
 create mode 100644 gcc/testsuite/g++.dg/pr60336-3.C
 create mode 100644 gcc/testsuite/g++.dg/pr60336-4.C
 create mode 100644 gcc/testsuite/g++.dg/pr60336-5.C
 create mode 100644 gcc/testsuite/g++.dg/pr60336-6.C
 create mode 100644 gcc/testsuite/g++.dg/pr60336-7.C
 create mode 100644 gcc/testsuite/g++.dg/pr60336-8.C
 create mode 100644 gcc/testsuite/g++.dg/pr60336-9.C
 create mode 100644 gcc/testsuite/g++.dg/pr68355.C

diff --git a/gcc/testsuite/g++.dg/pr60336-1.C b/gcc/testsuite/g++.dg/pr60336-1.C
new file mode 100644
index 0000000..946f8accd
--- /dev/null
+++ b/gcc/testsuite/g++.dg/pr60336-1.C
@@ -0,0 +1,17 @@
+// { dg-do compile { target i?86-*-* x86_64-*-* } }
+// { dg-options "-O2 -std=c++11 -fno-pic" }
+// { dg-require-effective-target fpic }
+
+struct dummy { };
+struct true_type { struct dummy i; };
+
+extern true_type y;
+extern void xxx (true_type c);
+
+void
+yyy (void)
+{
+  xxx (y);
+}
+
+// { dg-final { scan-assembler "jmp\[\t \]+\[^\$\]*?_Z3xxx9true_type" { target i?86-*-* x86_64-*-* } } }
diff --git a/gcc/testsuite/g++.dg/pr60336-10.C b/gcc/testsuite/g++.dg/pr60336-10.C
new file mode 100644
index 0000000..58de85b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/pr60336-10.C
@@ -0,0 +1,50 @@
+// { dg-do run { target i?86-*-* x86_64-*-* } }
+// { dg-options "-O2" }
+
+#include <stdarg.h>
+
+struct dummy0 { };
+struct dummy1 { };
+struct dummy : dummy0, dummy1 { };
+
+void
+test (struct dummy a, int m, ...)
+{
+  va_list va_arglist;
+  int i;
+  int count = 0;
+
+  if (m == 0)
+    count++;
+  va_start (va_arglist, m);
+  i = va_arg (va_arglist, int);
+  if (i == 1)
+    count++;
+  i = va_arg (va_arglist, int);
+  if (i == 2)
+    count++;
+  i = va_arg (va_arglist, int);
+  if (i == 3)
+    count++;
+  i = va_arg (va_arglist, int);
+  if (i == 4)
+    count++;
+  i = va_arg (va_arglist, int);
+  if (i == 5)
+    count++;
+  i = va_arg (va_arglist, int);
+  if (i == 6)
+    count++;
+  va_end (va_arglist);
+  if (count != 7)
+    __builtin_abort ();
+}
+
+struct dummy a0;
+
+int
+main ()
+{
+  test (a0, 0, 1, 2, 3, 4, 5, 6);
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/pr60336-11.C b/gcc/testsuite/g++.dg/pr60336-11.C
new file mode 100644
index 0000000..5b03b26
--- /dev/null
+++ b/gcc/testsuite/g++.dg/pr60336-11.C
@@ -0,0 +1,56 @@
+// { dg-do run { target i?86-*-* x86_64-*-* } }
+// { dg-options "-O2" }
+
+#include <stdarg.h>
+
+struct dummy0
+{
+  void bar (void);
+};
+struct dummy1
+{
+  void foo (void);
+};
+struct dummy : dummy0, dummy1 { };
+
+void
+test (struct dummy a, int m, ...)
+{
+  va_list va_arglist;
+  int i;
+  int count = 0;
+
+  if (m == 0)
+    count++;
+  va_start (va_arglist, m);
+  i = va_arg (va_arglist, int);
+  if (i == 1)
+    count++;
+  i = va_arg (va_arglist, int);
+  if (i == 2)
+    count++;
+  i = va_arg (va_arglist, int);
+  if (i == 3)
+    count++;
+  i = va_arg (va_arglist, int);
+  if (i == 4)
+    count++;
+  i = va_arg (va_arglist, int);
+  if (i == 5)
+    count++;
+  i = va_arg (va_arglist, int);
+  if (i == 6)
+    count++;
+  va_end (va_arglist);
+  if (count != 7)
+    __builtin_abort ();
+}
+
+struct dummy a0;
+
+int
+main ()
+{
+  test (a0, 0, 1, 2, 3, 4, 5, 6);
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/pr60336-12.C b/gcc/testsuite/g++.dg/pr60336-12.C
new file mode 100644
index 0000000..1cae768
--- /dev/null
+++ b/gcc/testsuite/g++.dg/pr60336-12.C
@@ -0,0 +1,57 @@
+// { dg-do run { target i?86-*-* x86_64-*-* } }
+// { dg-options "-O2" }
+
+#include <stdarg.h>
+
+struct dummy0
+{
+};
+struct dummy1
+{
+  unsigned : 15;
+};
+struct dummy : dummy0, dummy1
+{
+};
+
+void
+test (struct dummy a, int m, ...)
+{
+  va_list va_arglist;
+  int i;
+  int count = 0;
+
+  if (m == 0)
+    count++;
+  va_start (va_arglist, m);
+  i = va_arg (va_arglist, int);
+  if (i == 1)
+    count++;
+  i = va_arg (va_arglist, int);
+  if (i == 2)
+    count++;
+  i = va_arg (va_arglist, int);
+  if (i == 3)
+    count++;
+  i = va_arg (va_arglist, int);
+  if (i == 4)
+    count++;
+  i = va_arg (va_arglist, int);
+  if (i == 5)
+    count++;
+  i = va_arg (va_arglist, int);
+  if (i == 6)
+    count++;
+  va_end (va_arglist);
+  if (count != 7)
+    __builtin_abort ();
+}
+
+struct dummy a0;
+
+int
+main ()
+{
+  test (a0, 0, 1, 2, 3, 4, 5, 6);
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/pr60336-2.C b/gcc/testsuite/g++.dg/pr60336-2.C
new file mode 100644
index 0000000..f18cf1e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/pr60336-2.C
@@ -0,0 +1,48 @@
+// { dg-do run }
+// { dg-options "-O2 -Wabi=9" }
+
+#include <stdarg.h>
+
+struct dummy { };
+
+void
+test (struct dummy a, int m, ...) // { dg-warning "empty" }
+{
+  va_list va_arglist;
+  int i;
+  int count = 0;
+
+  if (m == 0)
+    count++;
+  va_start (va_arglist, m);
+  i = va_arg (va_arglist, int);
+  if (i == 1)
+    count++;
+  i = va_arg (va_arglist, int);
+  if (i == 2)
+    count++;
+  i = va_arg (va_arglist, int);
+  if (i == 3)
+    count++;
+  i = va_arg (va_arglist, int);
+  if (i == 4)
+    count++;
+  i = va_arg (va_arglist, int);
+  if (i == 5)
+    count++;
+  i = va_arg (va_arglist, int);
+  if (i == 6)
+    count++;
+  va_end (va_arglist);
+  if (count != 7)
+    __builtin_abort ();
+}
+
+struct dummy a0;
+
+int
+main ()
+{
+  test (a0, 0, 1, 2, 3, 4, 5, 6); // { dg-warning "empty" }
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/pr60336-3.C b/gcc/testsuite/g++.dg/pr60336-3.C
new file mode 100644
index 0000000..334a879
--- /dev/null
+++ b/gcc/testsuite/g++.dg/pr60336-3.C
@@ -0,0 +1,15 @@
+// { dg-do compile }
+// { dg-options "-O2 -Wabi=9" }
+
+struct dummy { struct{}__attribute__((aligned (4))) a[7]; };
+
+extern void test1 (struct dummy, ...);
+extern void (*test2) (struct dummy, ...);
+
+void
+foo ()
+{
+  struct dummy a0;
+  test1 (a0, 1); // { dg-warning "empty" }
+  test2 (a0, 1); // { dg-warning "empty" }
+}
diff --git a/gcc/testsuite/g++.dg/pr60336-4.C b/gcc/testsuite/g++.dg/pr60336-4.C
new file mode 100644
index 0000000..b103724
--- /dev/null
+++ b/gcc/testsuite/g++.dg/pr60336-4.C
@@ -0,0 +1,48 @@
+// { dg-do run }
+// { dg-options "-O2" }
+
+#include <stdarg.h>
+
+struct dummy { };
+
+void
+test (struct dummy a, int m, ...)
+{
+  va_list va_arglist;
+  int i;
+  int count = 0;
+
+  if (m == 0)
+    count++;
+  va_start (va_arglist, m);
+  i = va_arg (va_arglist, int);
+  if (i == 1)
+    count++;
+  i = va_arg (va_arglist, int);
+  if (i == 2)
+    count++;
+  i = va_arg (va_arglist, int);
+  if (i == 3)
+    count++;
+  i = va_arg (va_arglist, int);
+  if (i == 4)
+    count++;
+  i = va_arg (va_arglist, int);
+  if (i == 5)
+    count++;
+  i = va_arg (va_arglist, int);
+  if (i == 6)
+    count++;
+  va_end (va_arglist);
+  if (count != 7)
+    __builtin_abort ();
+}
+
+struct dummy a0;
+
+int
+main ()
+{
+  test (a0, 0, 1, 2, 3, 4, 5, 6);
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/pr60336-5.C b/gcc/testsuite/g++.dg/pr60336-5.C
new file mode 100644
index 0000000..a051f6e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/pr60336-5.C
@@ -0,0 +1,17 @@
+// { dg-do compile { target i?86-*-* x86_64-*-* } }
+// { dg-options "-O2 -std=c++11 -fno-pic" }
+// { dg-require-effective-target fpic }
+
+struct dummy { };
+struct true_type { struct dummy i; struct dummy j; };
+
+extern true_type y;
+extern void xxx (true_type c);
+
+void
+yyy (void)
+{
+  xxx (y);
+}
+
+// { dg-final { scan-assembler "jmp\[\t \]+\[^\$\]*?_Z3xxx9true_type" { target i?86-*-* x86_64-*-* } } }
diff --git a/gcc/testsuite/g++.dg/pr60336-6.C b/gcc/testsuite/g++.dg/pr60336-6.C
new file mode 100644
index 0000000..22728d3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/pr60336-6.C
@@ -0,0 +1,17 @@
+// { dg-do compile { target i?86-*-* x86_64-*-* } }
+// { dg-options "-O2 -std=c++11 -fno-pic" }
+// { dg-require-effective-target fpic }
+
+struct dummy { };
+struct true_type { struct dummy i1; struct dummy i2; };
+
+extern true_type y;
+extern void xxx (true_type c);
+
+void
+yyy (void)
+{
+  xxx (y);
+}
+
+// { dg-final { scan-assembler "jmp\[\t \]+\[^\$\]*?_Z3xxx9true_type" { target i?86-*-* x86_64-*-* } } }
diff --git a/gcc/testsuite/g++.dg/pr60336-7.C b/gcc/testsuite/g++.dg/pr60336-7.C
new file mode 100644
index 0000000..5fc8320
--- /dev/null
+++ b/gcc/testsuite/g++.dg/pr60336-7.C
@@ -0,0 +1,17 @@
+// { dg-do compile { target i?86-*-* x86_64-*-* } }
+// { dg-options "-O2 -std=c++11 -fno-pic" }
+// { dg-require-effective-target fpic }
+
+struct dummy { };
+struct true_type { struct dummy i[120]; };
+
+extern true_type y;
+extern void xxx (true_type c);
+
+void
+yyy (void)
+{
+  xxx (y);
+}
+
+// { dg-final { scan-assembler "jmp\[\t \]+\[^\$\]*?_Z3xxx9true_type" { target i?86-*-* x86_64-*-* } } }
diff --git a/gcc/testsuite/g++.dg/pr60336-8.C b/gcc/testsuite/g++.dg/pr60336-8.C
new file mode 100644
index 0000000..38b6d34
--- /dev/null
+++ b/gcc/testsuite/g++.dg/pr60336-8.C
@@ -0,0 +1,15 @@
+// { dg-do compile }
+// { dg-options "-O2 -Wabi=9" }
+
+struct dummy { struct{} a[7][3]; };
+
+extern void test1 (struct dummy, ...);
+extern void (*test2) (struct dummy, ...);
+
+void
+foo ()
+{
+  struct dummy a0;
+  test1 (a0, 1); // { dg-warning "empty" }
+  test2 (a0, 1); // { dg-warning "empty" }
+}
diff --git a/gcc/testsuite/g++.dg/pr60336-9.C b/gcc/testsuite/g++.dg/pr60336-9.C
new file mode 100644
index 0000000..3d768c0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/pr60336-9.C
@@ -0,0 +1,28 @@
+// { dg-do compile { target i?86-*-* x86_64-*-* } }
+// { dg-options "-O2 -std=c++11 -fno-pic" }
+// { dg-require-effective-target fpic }
+
+struct A1 {}; struct A2 {};
+struct B1 { A1 a; A2 b; }; struct B2 { A1 a; A2 b; };
+struct C1 { B1 a; B2 b; }; struct C2 { B1 a; B2 b; };
+struct D1 { C1 a; C2 b; }; struct D2 { C1 a; C2 b; };
+struct E1 { D1 a; D2 b; }; struct E2 { D1 a; D2 b; };
+struct F1 { E1 a; E2 b; }; struct F2 { E1 a; E2 b; };
+struct G1 { F1 a; F2 b; }; struct G2 { F1 a; F2 b; };
+struct H1 { G1 a; G2 b; }; struct H2 { G1 a; G2 b; };
+struct I1 { H1 a; H2 b; }; struct I2 { H1 a; H2 b; };
+struct J1 { I1 a; I2 b; }; struct J2 { I1 a; I2 b; };
+struct dummy { J1 a; J2 b; };
+
+struct true_type { struct dummy i; };
+
+extern true_type y;
+extern void xxx (true_type c);
+
+void
+yyy (void)
+{
+  xxx (y);
+}
+
+// { dg-final { scan-assembler "jmp\[\t \]+\[^\$\]*?_Z3xxx9true_type" { target i?86-*-* x86_64-*-* } } }
diff --git a/gcc/testsuite/g++.dg/pr68355.C b/gcc/testsuite/g++.dg/pr68355.C
new file mode 100644
index 0000000..5c2f0da
--- /dev/null
+++ b/gcc/testsuite/g++.dg/pr68355.C
@@ -0,0 +1,24 @@
+// { dg-do compile { target i?86-*-* x86_64-*-* } }
+// { dg-options "-O2 -std=c++11 -fno-pic" }
+// { dg-require-effective-target fpic }
+
+template<typename _Tp, _Tp __v>
+struct integral_constant
+{
+  static constexpr _Tp value = __v;
+  typedef _Tp value_type;
+  typedef integral_constant<_Tp, __v> type;
+  constexpr operator value_type() const { return value; }
+};
+
+typedef integral_constant<bool, true> true_type;
+extern void xxx (true_type c);
+
+void
+yyy (void)
+{
+  true_type y;
+  xxx (y);
+}
+
+// { dg-final { scan-assembler "jmp\[\t \]+\[^\$\]*?_Z3xxx17integral_constantIbLb1EE" { target i?86-*-* x86_64-*-* } } }
-- 
2.5.5


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: RFC: C++ PATCH to adjust empty class parameter passing ABI
  2016-04-13 20:12         ` Jason Merrill
  2016-04-13 22:56           ` H.J. Lu
@ 2016-04-14  0:35           ` H.J. Lu
  2016-04-14  8:03           ` Christophe Lyon
  2 siblings, 0 replies; 16+ messages in thread
From: H.J. Lu @ 2016-04-14  0:35 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Jakub Jelinek, gcc-patches List, Jonathan Wakely

On Wed, Apr 13, 2016 at 1:12 PM, Jason Merrill <jason@redhat.com> wrote:
> On 04/13/2016 03:18 PM, Jakub Jelinek wrote:
>>
>> On Wed, Apr 13, 2016 at 03:11:34PM -0400, Jason Merrill wrote:
>>>
>>> commit 761983a023b5217ef831a43f423779940c788ecf
>>> Author: Jason Merrill <jason@redhat.com>
>>> Date:   Tue Apr 12 13:16:50 2016 -0400
>>>
>>>      gcc/
>>>         * cfgexpand.c (pass_expand::execute): Handle attribute
>>> abi_warning.
>>>         * expr.c (expand_expr_real_1): Likewise.
>>>      gcc/cp/
>>>         * call.c (empty_class_msg, mark_for_abi_warning): New.
>>>         (build_call_a): Use them.
>>>         * decl.c (store_parm_decls): Use mark_for_abi_warning.
>>>         * error.c (pp_format_to_string): New.
>>
>>
>> I think you should put a space into the attribute name instead of _
>> to make it clear that it is not an attribute users can use directly in
>> their
>> code through __attribute__.
>>
>> Otherwise it looks reasonable to me.
>
>
> Thanks, applied with that change.

I opened:

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70649

-- 
H.J.

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: RFC: C++ PATCH to adjust empty class parameter passing ABI
  2016-04-13 20:12         ` Jason Merrill
  2016-04-13 22:56           ` H.J. Lu
  2016-04-14  0:35           ` H.J. Lu
@ 2016-04-14  8:03           ` Christophe Lyon
  2016-04-14  9:41             ` Kyrill Tkachov
  2 siblings, 1 reply; 16+ messages in thread
From: Christophe Lyon @ 2016-04-14  8:03 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Jakub Jelinek, gcc-patches List, Jonathan Wakely, H.J. Lu

On 13 April 2016 at 22:12, Jason Merrill <jason@redhat.com> wrote:
> On 04/13/2016 03:18 PM, Jakub Jelinek wrote:
>>
>> On Wed, Apr 13, 2016 at 03:11:34PM -0400, Jason Merrill wrote:
>>>
>>> commit 761983a023b5217ef831a43f423779940c788ecf
>>> Author: Jason Merrill <jason@redhat.com>
>>> Date:   Tue Apr 12 13:16:50 2016 -0400
>>>
>>>      gcc/
>>>         * cfgexpand.c (pass_expand::execute): Handle attribute
>>> abi_warning.
>>>         * expr.c (expand_expr_real_1): Likewise.
>>>      gcc/cp/
>>>         * call.c (empty_class_msg, mark_for_abi_warning): New.
>>>         (build_call_a): Use them.
>>>         * decl.c (store_parm_decls): Use mark_for_abi_warning.
>>>         * error.c (pp_format_to_string): New.
>>
>>
>> I think you should put a space into the attribute name instead of _
>> to make it clear that it is not an attribute users can use directly in
>> their
>> code through __attribute__.
>>
>> Otherwise it looks reasonable to me.
>
>
> Thanks, applied with that change.
>

Hi,

I'm seeing g++.dg/abi/empty13.C failing at execution on
aarch64-none-linux-gnu (using qemu):
qemu: uncaught target signal 11 (Segmentation fault) - core dumped

and aarch64_be-none-elf (using the Foundation Model):
Terminated by exception.

But it passes on aarch64-none-elf (using the Foundation Model too)

Am I the only one?

Christophe.



> Jason
>

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: RFC: C++ PATCH to adjust empty class parameter passing ABI
  2016-04-14  8:03           ` Christophe Lyon
@ 2016-04-14  9:41             ` Kyrill Tkachov
  2016-04-14 10:25               ` Ramana Radhakrishnan
  0 siblings, 1 reply; 16+ messages in thread
From: Kyrill Tkachov @ 2016-04-14  9:41 UTC (permalink / raw)
  To: Christophe Lyon, Jason Merrill
  Cc: Jakub Jelinek, gcc-patches List, Jonathan Wakely, H.J. Lu


On 14/04/16 09:02, Christophe Lyon wrote:
> On 13 April 2016 at 22:12, Jason Merrill <jason@redhat.com> wrote:
>> On 04/13/2016 03:18 PM, Jakub Jelinek wrote:
>>> On Wed, Apr 13, 2016 at 03:11:34PM -0400, Jason Merrill wrote:
>>>> commit 761983a023b5217ef831a43f423779940c788ecf
>>>> Author: Jason Merrill <jason@redhat.com>
>>>> Date:   Tue Apr 12 13:16:50 2016 -0400
>>>>
>>>>       gcc/
>>>>          * cfgexpand.c (pass_expand::execute): Handle attribute
>>>> abi_warning.
>>>>          * expr.c (expand_expr_real_1): Likewise.
>>>>       gcc/cp/
>>>>          * call.c (empty_class_msg, mark_for_abi_warning): New.
>>>>          (build_call_a): Use them.
>>>>          * decl.c (store_parm_decls): Use mark_for_abi_warning.
>>>>          * error.c (pp_format_to_string): New.
>>>
>>> I think you should put a space into the attribute name instead of _
>>> to make it clear that it is not an attribute users can use directly in
>>> their
>>> code through __attribute__.
>>>
>>> Otherwise it looks reasonable to me.
>>
>> Thanks, applied with that change.
>>
> Hi,
>
> I'm seeing g++.dg/abi/empty13.C failing at execution on
> aarch64-none-linux-gnu (using qemu):
> qemu: uncaught target signal 11 (Segmentation fault) - core dumped
>
> and aarch64_be-none-elf (using the Foundation Model):
> Terminated by exception.
>
> But it passes on aarch64-none-elf (using the Foundation Model too)
>
> Am I the only one?
Hi Christophe,

I see the test failing on aarch64-none-linux-gnu (native)
with no output, just:
spawn [open ...]
FAIL: g++.dg/abi/empty13.C  -std=gnu++98 execution test

And I see it passing on aarch64-none-elf.

I haven't tried aarch64_be-none-elf yet.

Kyrill


> Christophe.
>
>
>
>> Jason
>>

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: RFC: C++ PATCH to adjust empty class parameter passing ABI
  2016-04-14  9:41             ` Kyrill Tkachov
@ 2016-04-14 10:25               ` Ramana Radhakrishnan
  2016-04-14 10:36                 ` Jakub Jelinek
  0 siblings, 1 reply; 16+ messages in thread
From: Ramana Radhakrishnan @ 2016-04-14 10:25 UTC (permalink / raw)
  To: Kyrill Tkachov
  Cc: Christophe Lyon, Jason Merrill, Jakub Jelinek, gcc-patches List,
	Jonathan Wakely, H.J. Lu

On Thu, Apr 14, 2016 at 10:41 AM, Kyrill Tkachov
<kyrylo.tkachov@foss.arm.com> wrote:
>
> On 14/04/16 09:02, Christophe Lyon wrote:
>>
>> On 13 April 2016 at 22:12, Jason Merrill <jason@redhat.com> wrote:
>>>
>>> On 04/13/2016 03:18 PM, Jakub Jelinek wrote:
>>>>
>>>> On Wed, Apr 13, 2016 at 03:11:34PM -0400, Jason Merrill wrote:
>>>>>
>>>>> commit 761983a023b5217ef831a43f423779940c788ecf
>>>>> Author: Jason Merrill <jason@redhat.com>
>>>>> Date:   Tue Apr 12 13:16:50 2016 -0400
>>>>>
>>>>>       gcc/
>>>>>          * cfgexpand.c (pass_expand::execute): Handle attribute
>>>>> abi_warning.
>>>>>          * expr.c (expand_expr_real_1): Likewise.
>>>>>       gcc/cp/
>>>>>          * call.c (empty_class_msg, mark_for_abi_warning): New.
>>>>>          (build_call_a): Use them.
>>>>>          * decl.c (store_parm_decls): Use mark_for_abi_warning.
>>>>>          * error.c (pp_format_to_string): New.
>>>>
>>>>
>>>> I think you should put a space into the attribute name instead of _
>>>> to make it clear that it is not an attribute users can use directly in
>>>> their
>>>> code through __attribute__.
>>>>
>>>> Otherwise it looks reasonable to me.
>>>
>>>
>>> Thanks, applied with that change.
>>>
>> Hi,
>>
>> I'm seeing g++.dg/abi/empty13.C failing at execution on
>> aarch64-none-linux-gnu (using qemu):
>> qemu: uncaught target signal 11 (Segmentation fault) - core dumped
>>
>> and aarch64_be-none-elf (using the Foundation Model):
>> Terminated by exception.
>>
>> But it passes on aarch64-none-elf (using the Foundation Model too)
>>
>> Am I the only one?
>
> Hi Christophe,
>
> I see the test failing on aarch64-none-linux-gnu (native)
> with no output, just:
> spawn [open ...]
> FAIL: g++.dg/abi/empty13.C  -std=gnu++98 execution test
>
> And I see it passing on aarch64-none-elf.

IIRC on AArch64 we document that empty classes and structs take up a
slot as per the PCS .

http://infocenter.arm.com/help/topic/com.arm.doc.ihi0059b/IHI0059B_cppabi64.pdf

See section 3.1

For the purposes of parameter passing in
[
AAPCS64
], a parameter whose type is an empty class shall be treated as if its
type were an aggregate with a
single
member of type unsigned byte.



regards
Ramana



>
> I haven't tried aarch64_be-none-elf yet.
>
> Kyrill
>
>
>> Christophe.
>>
>>
>>
>>> Jason
>>>
>

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: RFC: C++ PATCH to adjust empty class parameter passing ABI
  2016-04-14 10:25               ` Ramana Radhakrishnan
@ 2016-04-14 10:36                 ` Jakub Jelinek
  2016-04-14 11:19                   ` Ramana Radhakrishnan
  0 siblings, 1 reply; 16+ messages in thread
From: Jakub Jelinek @ 2016-04-14 10:36 UTC (permalink / raw)
  To: Ramana Radhakrishnan
  Cc: Kyrill Tkachov, Christophe Lyon, Jason Merrill, gcc-patches List,
	Jonathan Wakely, H.J. Lu

On Thu, Apr 14, 2016 at 11:25:07AM +0100, Ramana Radhakrishnan wrote:
> > I see the test failing on aarch64-none-linux-gnu (native)
> > with no output, just:
> > spawn [open ...]
> > FAIL: g++.dg/abi/empty13.C  -std=gnu++98 execution test
> >
> > And I see it passing on aarch64-none-elf.
> 
> IIRC on AArch64 we document that empty classes and structs take up a
> slot as per the PCS .
> 
> http://infocenter.arm.com/help/topic/com.arm.doc.ihi0059b/IHI0059B_cppabi64.pdf
> 
> See section 3.1
> 
> For the purposes of parameter passing in
> [
> AAPCS64
> ], a parameter whose type is an empty class shall be treated as if its
> type were an aggregate with a
> single
> member of type unsigned byte.

One thing is passing such arguments in registers, another is how they are
passed on the stack (e.g. if there are several other arguments that are
passed in registers).
Also, note that the (new) definition of C++ empty class I believe includes
even empty classes that are normally very large, i.e. don't pretend they
are just single byte, but can be e.g. 1024 bytes or longer.
Do you want to still pass them in registers and/or on the stack as if
it is 1 byte, 0 bytes or more bytes?
How do you pass empty C structures?

E.g. looking at sparc-solaris2.12 cross, C for empty structures passes
both in registers and on the stack as if there is one argument (and C++ did
too for the empty classes that pretended to have size 1), so appart from the
ICE in emit_move_insn there is no ABI change, only perhaps for the larger
structs.

Guess we need to figure out what do the various targets do for C empty
structures and decide what we do want to do with C++ empty classes.
Perhaps the C++ FE should just set some flag on the empty class parameters,
and let the backends decide what to do with those?

	Jakub

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: RFC: C++ PATCH to adjust empty class parameter passing ABI
  2016-04-14 10:36                 ` Jakub Jelinek
@ 2016-04-14 11:19                   ` Ramana Radhakrishnan
  2016-04-14 11:26                     ` Jakub Jelinek
  0 siblings, 1 reply; 16+ messages in thread
From: Ramana Radhakrishnan @ 2016-04-14 11:19 UTC (permalink / raw)
  To: Jakub Jelinek
  Cc: Kyrill Tkachov, Christophe Lyon, Jason Merrill, gcc-patches List,
	Jonathan Wakely, H.J. Lu, Richard Earnshaw

On Thu, Apr 14, 2016 at 11:36 AM, Jakub Jelinek <jakub@redhat.com> wrote:
> On Thu, Apr 14, 2016 at 11:25:07AM +0100, Ramana Radhakrishnan wrote:
>> > I see the test failing on aarch64-none-linux-gnu (native)
>> > with no output, just:
>> > spawn [open ...]
>> > FAIL: g++.dg/abi/empty13.C  -std=gnu++98 execution test
>> >
>> > And I see it passing on aarch64-none-elf.
>>


I don't understand why this passes on aarch64-none-elf btw.

>> IIRC on AArch64 we document that empty classes and structs take up a
>> slot as per the PCS .
>>
>> http://infocenter.arm.com/help/topic/com.arm.doc.ihi0059b/IHI0059B_cppabi64.pdf
>>
>> See section 3.1
>>
>> For the purposes of parameter passing in
>> [
>> AAPCS64
>> ], a parameter whose type is an empty class shall be treated as if its
>> type were an aggregate with a
>> single
>> member of type unsigned byte.
>
> One thing is passing such arguments in registers, another is how they are
> passed on the stack (e.g. if there are several other arguments that are
> passed in registers).


It would just be treated like a structure with an unsigned byte and
treated the same way for parameter passing on the stack - i.e. IIRC
take a slot of 8 bytes.

> Also, note that the (new) definition of C++ empty class I believe includes
> even empty classes that are normally very large, i.e. don't pretend they
> are just single byte, but can be e.g. 1024 bytes or longer.
> Do you want to still pass them in registers and/or on the stack as if
> it is 1 byte, 0 bytes or more bytes?

I don't know off-hand.

Is this a valid example for what you have in mind ?

struct baz
{
  char a[1024];
};
struct foo : baz
{
};

int bar (struct foo b, int x)

if so, that would get treated as though it is just a 1 byte quantity
for C++. as per current behaviour by both gcc and llvm for aarch64.

> How do you pass empty C structures?

I don't think we do anything special for empty C structures which IIRC
means they don't take a slot.


>
> E.g. looking at sparc-solaris2.12 cross, C for empty structures passes
> both in registers and on the stack as if there is one argument (and C++ did
> too for the empty classes that pretended to have size 1), so appart from the
> ICE in emit_move_insn there is no ABI change, only perhaps for the larger
> structs.
>
> Guess we need to figure out what do the various targets do for C empty
> structures and decide what we do want to do with C++ empty classes.
> Perhaps the C++ FE should just set some flag on the empty class parameters,
> and let the backends decide what to do with those.



Ramana

>
>         Jakub

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: RFC: C++ PATCH to adjust empty class parameter passing ABI
  2016-04-14 11:19                   ` Ramana Radhakrishnan
@ 2016-04-14 11:26                     ` Jakub Jelinek
  2016-04-14 12:24                       ` Ramana Radhakrishnan
  0 siblings, 1 reply; 16+ messages in thread
From: Jakub Jelinek @ 2016-04-14 11:26 UTC (permalink / raw)
  To: Ramana Radhakrishnan
  Cc: Kyrill Tkachov, Christophe Lyon, Jason Merrill, gcc-patches List,
	Jonathan Wakely, H.J. Lu, Richard Earnshaw

On Thu, Apr 14, 2016 at 12:19:26PM +0100, Ramana Radhakrishnan wrote:
> Is this a valid example for what you have in mind ?
> 
> struct baz
> {
>   char a[1024];
> };
> struct foo : baz
> {
> };
> 
> int bar (struct foo b, int x)

No, I meant say:
struct A {};
struct B { A a[1024]; };
int bar (struct B b, int c)
{ 
  return c;
} 
int baz (int a, int b, int c, int d, int e, int f, int g, struct B h, int i)
{
  return g + i;
}

Strangely, we warn about this with -Wabi=9 even on x86_64-linux on both
testcases, while only on baz it actually changes code generation.

> if so, that would get treated as though it is just a 1 byte quantity
> for C++. as per current behaviour by both gcc and llvm for aarch64.
> 
> > How do you pass empty C structures?
> 
> I don't think we do anything special for empty C structures which IIRC
> means they don't take a slot.

I think the intent of the C++ changes was to make them roughly match (at
least for the empty classes where C++ says sizeof (...) == 1 while
C says sizeof (...) == 0) what C does.

	Jakub

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: RFC: C++ PATCH to adjust empty class parameter passing ABI
  2016-04-14 11:26                     ` Jakub Jelinek
@ 2016-04-14 12:24                       ` Ramana Radhakrishnan
  0 siblings, 0 replies; 16+ messages in thread
From: Ramana Radhakrishnan @ 2016-04-14 12:24 UTC (permalink / raw)
  To: Jakub Jelinek
  Cc: Kyrill Tkachov, Christophe Lyon, Jason Merrill, gcc-patches List,
	Jonathan Wakely, H.J. Lu, Richard Earnshaw

On Thu, Apr 14, 2016 at 12:25 PM, Jakub Jelinek <jakub@redhat.com> wrote:
> On Thu, Apr 14, 2016 at 12:19:26PM +0100, Ramana Radhakrishnan wrote:
>> Is this a valid example for what you have in mind ?
>>
>> struct baz
>> {
>>   char a[1024];
>> };
>> struct foo : baz
>> {
>> };
>>
>> int bar (struct foo b, int x)
>
> No, I meant say:
> struct A {};
> struct B { A a[1024]; };
> int bar (struct B b, int c)
> {
>   return c;
> }
> int baz (int a, int b, int c, int d, int e, int f, int g, struct B h, int i)
> {
>   return g + i;
> }
>
> Strangely, we warn about this with -Wabi=9 even on x86_64-linux on both
> testcases, while only on baz it actually changes code generation.
>

Seems to be treated as passing a 1 byte quantity before the change.
Note you need one more parameter on aarch64 to force struct B into the
stack.

IIRC the same problem should happen on AArch32 as well but I haven't
tried that yet.


regards
Ramana

^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2016-04-14 12:24 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-04-12 20:28 RFC: C++ PATCH to adjust empty class parameter passing ABI Jason Merrill
2016-04-13 15:32 ` Jakub Jelinek
2016-04-13 15:54   ` Jason Merrill
2016-04-13 19:11     ` Jason Merrill
2016-04-13 19:18       ` Jakub Jelinek
2016-04-13 20:12         ` Jason Merrill
2016-04-13 22:56           ` H.J. Lu
2016-04-14  0:35           ` H.J. Lu
2016-04-14  8:03           ` Christophe Lyon
2016-04-14  9:41             ` Kyrill Tkachov
2016-04-14 10:25               ` Ramana Radhakrishnan
2016-04-14 10:36                 ` Jakub Jelinek
2016-04-14 11:19                   ` Ramana Radhakrishnan
2016-04-14 11:26                     ` Jakub Jelinek
2016-04-14 12:24                       ` Ramana Radhakrishnan
2016-04-13 22:01 ` Jonathan Wakely

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).