Warn about empty parameter ABI with -Wabi=9.
authorJason Merrill <jason@redhat.com>
Wed, 13 Apr 2016 20:11:29 +0000 (16:11 -0400)
committerJason Merrill <jason@gcc.gnu.org>
Wed, 13 Apr 2016 20:11:29 +0000 (16:11 -0400)
* call.c (empty_class_msg, mark_for_abi_warning)
(warn_empty_class_abi): New.
(build_call_a): Use them.
* decl.c (store_parm_decls): Use mark_for_abi_warning.
* error.c (pp_format_to_string): New.

From-SVN: r234960

47 files changed:
gcc/ChangeLog
gcc/cfgexpand.c
gcc/cp/ChangeLog
gcc/cp/call.c
gcc/cp/cp-tree.h
gcc/cp/decl.c
gcc/cp/error.c
gcc/expr.c
gcc/testsuite/g++.dg/abi/empty12.C [new file with mode: 0644]
gcc/testsuite/g++.dg/abi/empty12.h [new file with mode: 0644]
gcc/testsuite/g++.dg/abi/empty12a.c [new file with mode: 0644]
gcc/testsuite/g++.dg/abi/empty13.C [new file with mode: 0644]
gcc/testsuite/g++.dg/abi/empty13.h [new file with mode: 0644]
gcc/testsuite/g++.dg/abi/empty13a.c [new file with mode: 0644]
gcc/testsuite/g++.dg/abi/empty14.C [new file with mode: 0644]
gcc/testsuite/g++.dg/abi/empty14.h [new file with mode: 0644]
gcc/testsuite/g++.dg/abi/empty14a.c [new file with mode: 0644]
gcc/testsuite/g++.dg/abi/empty15.C [new file with mode: 0644]
gcc/testsuite/g++.dg/abi/empty15.h [new file with mode: 0644]
gcc/testsuite/g++.dg/abi/empty15a.c [new file with mode: 0644]
gcc/testsuite/g++.dg/abi/empty16.C [new file with mode: 0644]
gcc/testsuite/g++.dg/abi/empty16.h [new file with mode: 0644]
gcc/testsuite/g++.dg/abi/empty16a.c [new file with mode: 0644]
gcc/testsuite/g++.dg/abi/empty17.C [new file with mode: 0644]
gcc/testsuite/g++.dg/abi/empty17.h [new file with mode: 0644]
gcc/testsuite/g++.dg/abi/empty17a.c [new file with mode: 0644]
gcc/testsuite/g++.dg/abi/empty18.C [new file with mode: 0644]
gcc/testsuite/g++.dg/abi/empty18.h [new file with mode: 0644]
gcc/testsuite/g++.dg/abi/empty18a.c [new file with mode: 0644]
gcc/testsuite/g++.dg/abi/empty19.C [new file with mode: 0644]
gcc/testsuite/g++.dg/abi/empty19.h [new file with mode: 0644]
gcc/testsuite/g++.dg/abi/empty19a.c [new file with mode: 0644]
gcc/testsuite/g++.dg/abi/empty20.C [new file with mode: 0644]
gcc/testsuite/g++.dg/abi/empty21.C [new file with mode: 0644]
gcc/testsuite/g++.dg/abi/pr60336-1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/abi/pr60336-10.C [new file with mode: 0644]
gcc/testsuite/g++.dg/abi/pr60336-11.C [new file with mode: 0644]
gcc/testsuite/g++.dg/abi/pr60336-12.C [new file with mode: 0644]
gcc/testsuite/g++.dg/abi/pr60336-2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/abi/pr60336-3.C [new file with mode: 0644]
gcc/testsuite/g++.dg/abi/pr60336-4.C [new file with mode: 0644]
gcc/testsuite/g++.dg/abi/pr60336-5.C [new file with mode: 0644]
gcc/testsuite/g++.dg/abi/pr60336-6.C [new file with mode: 0644]
gcc/testsuite/g++.dg/abi/pr60336-7.C [new file with mode: 0644]
gcc/testsuite/g++.dg/abi/pr60336-8.C [new file with mode: 0644]
gcc/testsuite/g++.dg/abi/pr60336-9.C [new file with mode: 0644]
gcc/testsuite/g++.dg/abi/pr68355.C [new file with mode: 0644]

index 1353a7abfea2cbe2e02cf40a24514c6258c2e641..e2ac9171a4d607065d27a050dba0168d2f9eb1fd 100644 (file)
@@ -1,3 +1,8 @@
+2016-04-13  Jason Merrill  <jason@redhat.com>
+
+       * cfgexpand.c (pass_expand::execute): Handle attribute "abi warning".
+       * expr.c (expand_expr_real_1): Likewise.
+
 2016-04-13  Ilya Enkovich  <ilya.enkovich@intel.com>
 
        * config/i386/i386.md (kunpckhi): Swap operands.
index 1341c14ce2b6b1cbfe5619bd54449bde43e1084d..4a5cf166869ce35f05128650e2ff624765ce6a34 100644 (file)
@@ -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);
 
index 28541bc7857ee33b2498860835d8e0ade4a43052..c9929b63b9c380d419cd05441dbca2ce806e81e1 100644 (file)
@@ -1,5 +1,12 @@
 2016-04-13  Jason Merrill  <jason@redhat.com>
 
+       Warn about empty parameter ABI with -Wabi=9.
+       * call.c (empty_class_msg, mark_for_abi_warning)
+       (warn_empty_class_abi): New.
+       (build_call_a): Use them.
+       * decl.c (store_parm_decls): Use mark_for_abi_warning.
+       * error.c (pp_format_to_string): New.
+
        Pass empty class parameters like C.
        * call.c (pass_as_empty_struct, empty_class_arg): New.
        (type_passed_as, build_x_va_arg): Use pass_as_empty_struct.
index 84b62436da3aa460c52d6d929b93636b8939df69..687e7bd4dce7c6c11dda52b0c78b7ffc5359cf4e 100644 (file)
@@ -381,6 +381,11 @@ 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++)
       {
@@ -389,8 +394,19 @@ build_call_a (tree function, int n, tree *argarray)
        if (is_really_empty_class (type)
            && ! TREE_ADDRESSABLE (type))
          {
+           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);
+           if (decl && !varargs_function_p (decl))
+             mark_for_abi_warning (decl, empty_arg);
+           else
+             warn_empty_class_abi (empty_arg, loc);
+           warned = true;
+         }
       }
 
   return function;
@@ -6878,6 +6894,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);
+      warn_empty_class_abi (type, loc);
       tree ec = build0 (EMPTY_CLASS_EXPR, type);
       return build2 (COMPOUND_EXPR, type, expr, ec);
     }
@@ -7005,6 +7022,47 @@ empty_class_arg (tree val)
   return build2 (COMPOUND_EXPR, etype, val, empty);
 }
 
+/* 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)
+{
+  if (!warn_abi || !abi_version_crosses (10))
+    return;
+
+  warning_at (loc, OPT_Wabi, "%E", empty_class_msg (arg));
+}
+
+/* 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
    type TYPE.  */
 
index faea452531c66504b3cbe99cb62196d35fde4c2a..8d721c75849b96c7e26ad582553d5f14fa890660 100644 (file)
@@ -5540,6 +5540,8 @@ 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);
 extern bool sufficient_parms_p                 (const_tree);
@@ -5894,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);
index 5ca426bbd03c32b7157d8a223131d57d7133a169..7099199dea32f0d89c497d3e5ff88329ba634ecf 100644 (file)
@@ -14332,16 +14332,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)))
+               {
+                 mark_for_abi_warning (current_function_decl, type);
+                 warned = true;
+               }
            }
          else
            {
index aa5fd411b28f97930e54e5688f1c2e92e99b40cc..5aaa1773bca721042bd204b37a086e16612f41e0 100644 (file)
@@ -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;
+}
index 29d22b07256ce0adcb8c8eae5249847e144caf4b..5f1118c4f7b34d56cb145a259c5b22ddaa36a7c2 100644 (file)
@@ -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/empty12.C b/gcc/testsuite/g++.dg/abi/empty12.C
new file mode 100644 (file)
index 0000000..ce1f6f2
--- /dev/null
@@ -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 (file)
index 0000000..c61afcd
--- /dev/null
@@ -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 (file)
index 0000000..34a25ba
--- /dev/null
@@ -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 (file)
index 0000000..d1e0946
--- /dev/null
@@ -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 (file)
index 0000000..c61afcd
--- /dev/null
@@ -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 (file)
index 0000000..b4303a6
--- /dev/null
@@ -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 (file)
index 0000000..1b9c397
--- /dev/null
@@ -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 (file)
index 0000000..5842279
--- /dev/null
@@ -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 (file)
index 0000000..8b3d780
--- /dev/null
@@ -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 (file)
index 0000000..ac0a868
--- /dev/null
@@ -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 (file)
index 0000000..1c6f26f
--- /dev/null
@@ -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 (file)
index 0000000..325b2c5
--- /dev/null
@@ -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 (file)
index 0000000..de2bf5c
--- /dev/null
@@ -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 (file)
index 0000000..7552ae0
--- /dev/null
@@ -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 (file)
index 0000000..6cb7fbc
--- /dev/null
@@ -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 (file)
index 0000000..c7a37c0
--- /dev/null
@@ -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 (file)
index 0000000..9cf72ba
--- /dev/null
@@ -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 (file)
index 0000000..24408fd
--- /dev/null
@@ -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 (file)
index 0000000..6cad33c
--- /dev/null
@@ -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 (file)
index 0000000..86e7ecd
--- /dev/null
@@ -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 (file)
index 0000000..902860b
--- /dev/null
@@ -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 (file)
index 0000000..e3e855a
--- /dev/null
@@ -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 (file)
index 0000000..616b87b
--- /dev/null
@@ -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 (file)
index 0000000..767b1eb
--- /dev/null
@@ -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/empty20.C b/gcc/testsuite/g++.dg/abi/empty20.C
new file mode 100644 (file)
index 0000000..be1e826
--- /dev/null
@@ -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);
+}
diff --git a/gcc/testsuite/g++.dg/abi/empty21.C b/gcc/testsuite/g++.dg/abi/empty21.C
new file mode 100644 (file)
index 0000000..7538dd8
--- /dev/null
@@ -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 (file)
index 0000000..af08638
--- /dev/null
@@ -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 (file)
index 0000000..6c9c990
--- /dev/null
@@ -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 (file)
index 0000000..c92f3d4
--- /dev/null
@@ -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 (file)
index 0000000..83a7bb0
--- /dev/null
@@ -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 (file)
index 0000000..32eecb3
--- /dev/null
@@ -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 (file)
index 0000000..8ebd4dd
--- /dev/null
@@ -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 (file)
index 0000000..8790a66
--- /dev/null
@@ -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 (file)
index 0000000..b0c76ad
--- /dev/null
@@ -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 (file)
index 0000000..5879651
--- /dev/null
@@ -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 (file)
index 0000000..0e5d2ef
--- /dev/null
@@ -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 (file)
index 0000000..fdfc924
--- /dev/null
@@ -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 (file)
index 0000000..4ad333f
--- /dev/null
@@ -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 (file)
index 0000000..1354fc4
--- /dev/null
@@ -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-*-* } } }