public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
@ 2015-12-08 16:22 H.J. Lu
  2015-12-09 14:05 ` Richard Biener
  0 siblings, 1 reply; 52+ messages in thread
From: H.J. Lu @ 2015-12-08 16:22 UTC (permalink / raw)
  To: Richard Biener; +Cc: Jason Merrill, GCC Patches

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

On Mon, Nov 23, 2015 at 12:53 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
> On Mon, Nov 23, 2015 at 1:57 AM, Richard Biener
> <richard.guenther@gmail.com> wrote:
>> On Sat, Nov 21, 2015 at 12:46 AM, H.J. Lu <hjl.tools@gmail.com> wrote:
>>> On Fri, Nov 20, 2015 at 2:17 PM, Jason Merrill <jason@redhat.com> wrote:
>>>> On 11/20/2015 01:52 PM, H.J. Lu wrote:
>>>>>
>>>>> On Tue, Nov 17, 2015 at 4:22 AM, Richard Biener
>>>>> <richard.guenther@gmail.com> wrote:
>>>>>>
>>>>>> On Tue, Nov 17, 2015 at 12:01 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
>>>>>>>
>>>>>>> Empty record should be returned and passed the same way in C and C++.
>>>>>>> This patch adds LANG_HOOKS_EMPTY_RECORD_P for C++ empty class, which
>>>>>>> defaults to return false.  For C++, LANG_HOOKS_EMPTY_RECORD_P is defined
>>>>>>> to is_really_empty_class, which returns true for C++ empty classes.  For
>>>>>>> LTO, we stream out a bit to indicate if a record is empty and we store
>>>>>>> it in TYPE_LANG_FLAG_0 when streaming in.  get_ref_base_and_extent is
>>>>>>> changed to set bitsize to 0 for empty records.  Middle-end and x86
>>>>>>> backend are updated to ignore empty records for parameter passing and
>>>>>>> function value return.  Other targets may need similar changes.
>>>>>>
>>>>>>
>>>>>> Please avoid a new langhook for this and instead claim a bit in
>>>>>> tree_type_common
>>>>>> like for example restrict_flag (double-check it is unused for
>>>>>> non-pointers).
>>>>>
>>>>>
>>>>> There is no bit in tree_type_common I can overload.  restrict_flag is
>>>>> checked for non-pointers to issue an error when it is used on
>>>>> non-pointers:
>>>>>
>>>>>
>>>>> /export/gnu/import/git/sources/gcc/gcc/testsuite/g++.dg/template/qualttp20.C:19:38:
>>>>> error: ‘__restrict__’ qualifiers cannot be applied to ‘AS::L’
>>>>>     typedef typename T::L __restrict__ r;// { dg-error "'__restrict__'
>>>>> qualifiers cannot" "" }
>>>>
>>>>
>>>> The C++ front end only needs to check TYPE_RESTRICT for this purpose on
>>>> front-end-specific type codes like TEMPLATE_TYPE_PARM; cp_type_quals could
>>>> handle that specifically if you change TYPE_RESTRICT to only apply to
>>>> pointers.
>>>>
>>>
>>> restrict_flag is also checked in this case:
>>>
>>> [hjl@gnu-6 gcc]$ cat x.i
>>> struct dummy { };
>>>
>>> struct dummy
>>> foo (struct dummy __restrict__ i)
>>> {
>>>   return i;
>>> }
>>> [hjl@gnu-6 gcc]$ gcc -S x.i -Wall
>>> x.i:4:13: error: invalid use of ‘restrict’
>>>  foo (struct dummy __restrict__ i)
>>>              ^
>>> x.i:4:13: error: invalid use of ‘restrict’
>>> [hjl@gnu-6 gcc]$
>>>
>>> restrict_flag can't also be used to indicate `i' is an empty record.
>>
>> I'm sure this error can be done during parsing w/o relying on TYPE_RESTRICT.
>>
>> But well, use any other free bit (but do not enlarge
>> tree_type_common).  Eventually
>> you can free up a bit by putting sth into type_lang_specific currently
>> using bits
>> in tree_type_common.
>
> There are no bits in tree_type_common I can move.  Instead,
> this patch overloads side_effects_flag in tree_base.  Tested on
> Linux/x86-64.  OK for trunk?
>
> Thanks.
>
> --
> H.J.



-- 
H.J.

[-- Attachment #2: 0001-Add-TYPE_EMPTY_RECORD-for-C-empty-class.patch --]
[-- Type: text/x-patch, Size: 31923 bytes --]

From 1e3e6ed42ed86b74d01bd3a6b869c15dba964c21 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 TYPE_EMPTY_RECORD for C++ empty class

Empty record should be returned and passed the same way in C and C++.
This patch overloads a bit, side_effects_flag, in tree_base for C++
empty class.  Middle-end and x86 backend are updated to ignore empty
records for parameter passing and function value return.  Other targets
may need similar changes.

get_ref_base_and_extent is changed to set bitsize to 0 for empty records
so that when ref_maybe_used_by_call_p_1 calls get_ref_base_and_extent to
get 0 as the maximum size on empty record.  Otherwise, find_tail_calls
won't perform tail call optimization for functions with empty record
parameters, as shown in g++.dg/pr60336-1.C and g++.dg/pr60336-2.C.

gcc/

	PR c++/60336
	PR middle-end/67239
	PR target/68355
	* calls.c (initialize_argument_information): Replace
	targetm.calls.function_arg, targetm.calls.function_incoming_arg
	and targetm.calls.function_arg_advance with function_arg,
	function_incoming_arg and function_arg_advance.
	(expand_call): Likewise.
	(emit_library_call_value_1): Likewise.
	(store_one_arg): Use 0 for empty record size.  Don't
	push 0 size argument onto stack.
	(must_pass_in_stack_var_size_or_pad): Return false for empty
	record.
	* dse.c (get_call_args): Replace targetm.calls.function_arg
	and targetm.calls.function_arg_advance with function_arg and
	function_arg_advance.
	* expr.c (block_move_libcall_safe_for_call_parm): Likewise.
	* function.c (aggregate_value_p): Replace
	targetm.calls.return_in_memory with return_in_memory.
	(assign_parm_find_entry_rtl): Replace
	targetm.calls.function_incoming_arg with function_incoming_arg.
	(assign_parms): Replace targetm.calls.function_arg_advance with
	function_arg_advance.
	(gimplify_parameters): Replace targetm.calls.function_arg_advance
	with function_arg_advance.
	(locate_and_pad_parm): Use 0 for empty record size.
	(function_arg_advance): New wrapper function.
	(function_arg): Likewise.
	(function_incoming_arg): Likewise.
	(return_in_memory): Likewise.
	* lto-streamer-out.c (hash_tree): Call hstate.add_flag with
	TYPE_EMPTY_RECORD for types.
	* print-tree.c (print_node): Also handle TYPE_EMPTY_RECORD.
	* ubsan.c (ubsan_type_descriptor): Likewise.
	* target.h (function_arg_advance): New prototype.
	(function_arg): Likewise.
	(function_incoming_arg): Likewise.
	(return_in_memory): Likewise.
	* targhooks.c (std_gimplify_va_arg_expr): Use 0 for empty record
	size.
	* tree-dfa.c (get_ref_base_and_extent): Likewise.
	* tree-core.h (tree_base): Mention TYPE_EMPTY_RECORD in comments
	for side_effects_flag.
	* tree-streamer-in.c (unpack_ts_base_value_fields): Stream in
	TYPE_EMPTY_RECORD for types.
	* tree-streamer-out.c (pack_ts_base_value_fields): Stream out
	TYPE_EMPTY_RECORD for types.
	* tree.h (TYPE_EMPTY_RECORD): New.
	(type_is_empty_record_p): New static inline function.
	* var-tracking.c (prepare_call_arguments): Replace
	targetm.calls.function_arg and targetm.calls.function_arg_advance
	with function_arg and function_arg_advance.
	* config/i386/i386.c (ix86_gimplify_va_arg): Use 0 for empty
	record size.

gcc/c

	PR c++/60336
	PR middle-end/67239
	PR target/68355
	* c-aux-info.c (gen_type): Add TYPE_EMPTY_RECORD check.

gcc/cp/

	PR c++/60336
	PR middle-end/67239
	PR target/68355
	* class.c (finish_struct_1): Set TYPE_EMPTY_RECORD with return
	value from is_really_empty_class ().

gcc/lto/

	PR c++/60336
	PR middle-end/67239
	PR target/68355
	* lto.c (compare_tree_sccs_1): Call compare_values with
	TYPE_EMPTY_RECORD for types.

gcc/testsuite/

	PR c++/60336
	PR middle-end/67239
	PR target/68355
	* g++.dg/abi/empty12.C: New test.
	* g++.dg/abi/empty12.h: Likewise.
	* g++.dg/abi/empty12a.c: Likewise.
	* g++.dg/pr60336-1.C: Likewise.
	* g++.dg/pr60336-2.C: Likewise.
	* g++.dg/pr68355.C: Likewise.
---
 gcc/c/c-aux-info.c                  |  2 +
 gcc/calls.c                         | 76 +++++++++++++++++++++---------------
 gcc/config/i386/i386.c              |  3 +-
 gcc/cp/class.c                      |  2 +
 gcc/dse.c                           |  4 +-
 gcc/expr.c                          |  5 +--
 gcc/function.c                      | 78 ++++++++++++++++++++++++++++++-------
 gcc/lto-streamer-out.c              |  2 +
 gcc/lto/lto.c                       |  2 +
 gcc/print-tree.c                    |  3 ++
 gcc/target.h                        |  8 ++++
 gcc/targhooks.c                     |  5 ++-
 gcc/testsuite/g++.dg/abi/empty12.C  | 17 ++++++++
 gcc/testsuite/g++.dg/abi/empty12.h  |  9 +++++
 gcc/testsuite/g++.dg/abi/empty12a.c |  6 +++
 gcc/testsuite/g++.dg/pr60336-1.C    | 17 ++++++++
 gcc/testsuite/g++.dg/pr60336-2.C    | 28 +++++++++++++
 gcc/testsuite/g++.dg/pr68355.C      | 24 ++++++++++++
 gcc/tree-core.h                     |  3 ++
 gcc/tree-dfa.c                      |  2 +
 gcc/tree-streamer-in.c              |  5 ++-
 gcc/tree-streamer-out.c             |  5 ++-
 gcc/tree.h                          | 12 ++++++
 gcc/ubsan.c                         |  3 +-
 gcc/var-tracking.c                  | 18 ++++-----
 25 files changed, 274 insertions(+), 65 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/abi/empty12.C
 create mode 100644 gcc/testsuite/g++.dg/abi/empty12.h
 create mode 100644 gcc/testsuite/g++.dg/abi/empty12a.c
 create mode 100644 gcc/testsuite/g++.dg/pr60336-1.C
 create mode 100644 gcc/testsuite/g++.dg/pr60336-2.C
 create mode 100644 gcc/testsuite/g++.dg/pr68355.C

diff --git a/gcc/c/c-aux-info.c b/gcc/c/c-aux-info.c
index 79d9851..93b001b 100644
--- a/gcc/c/c-aux-info.c
+++ b/gcc/c/c-aux-info.c
@@ -433,6 +433,8 @@ gen_type (const char *ret_val, tree t, formals_style style)
     ret_val = concat ("volatile ", ret_val, NULL);
   if (TYPE_RESTRICT (t))
     ret_val = concat ("restrict ", ret_val, NULL);
+  if (TYPE_EMPTY_RECORD (t))
+    ret_val = concat ("empty-record ", ret_val, NULL);
   return ret_val;
 }
 
diff --git a/gcc/calls.c b/gcc/calls.c
index b56556a..a81cb3d 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -1391,8 +1391,8 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
       args[i].unsignedp = unsignedp;
       args[i].mode = mode;
 
-      args[i].reg = targetm.calls.function_arg (args_so_far, mode, type,
-						argpos < n_named_args);
+      args[i].reg = function_arg (args_so_far, mode, type,
+				  argpos < n_named_args);
 
       if (args[i].reg && CONST_INT_P (args[i].reg))
 	{
@@ -1405,8 +1405,8 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
 	 arguments have to go into the incoming registers.  */
       if (targetm.calls.function_incoming_arg != targetm.calls.function_arg)
 	args[i].tail_call_reg
-	  = targetm.calls.function_incoming_arg (args_so_far, mode, type,
-						 argpos < n_named_args);
+	  = function_incoming_arg (args_so_far, mode, type,
+				   argpos < n_named_args);
       else
 	args[i].tail_call_reg = args[i].reg;
 
@@ -1467,8 +1467,8 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
       /* Increment ARGS_SO_FAR, which has info about which arg-registers
 	 have been used, etc.  */
 
-      targetm.calls.function_arg_advance (args_so_far, TYPE_MODE (type),
-					  type, argpos < n_named_args);
+      function_arg_advance (args_so_far, TYPE_MODE (type), type,
+			    argpos < n_named_args);
     }
 }
 
@@ -3320,14 +3320,11 @@ expand_call (tree exp, rtx target, int ignore)
       /* Set up next argument register.  For sibling calls on machines
 	 with register windows this should be the incoming register.  */
       if (pass == 0)
-	next_arg_reg = targetm.calls.function_incoming_arg (args_so_far,
-							    VOIDmode,
-							    void_type_node,
-							    true);
+	next_arg_reg = function_incoming_arg (args_so_far, VOIDmode,
+					      void_type_node, true);
       else
-	next_arg_reg = targetm.calls.function_arg (args_so_far,
-						   VOIDmode, void_type_node,
-						   true);
+	next_arg_reg = function_arg (args_so_far, VOIDmode,
+				     void_type_node, true);
 
       if (pass == 1 && (return_flags & ERF_RETURNS_ARG))
 	{
@@ -3939,8 +3936,8 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
       argvec[count].mode = Pmode;
       argvec[count].partial = 0;
 
-      argvec[count].reg = targetm.calls.function_arg (args_so_far,
-						      Pmode, NULL_TREE, true);
+      argvec[count].reg = function_arg (args_so_far, Pmode, NULL_TREE,
+					true);
       gcc_assert (targetm.calls.arg_partial_bytes (args_so_far, Pmode,
 						   NULL_TREE, 1) == 0);
 
@@ -3957,7 +3954,7 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
 	  || reg_parm_stack_space > 0)
 	args_size.constant += argvec[count].locate.size.constant;
 
-      targetm.calls.function_arg_advance (args_so_far, Pmode, (tree) 0, true);
+      function_arg_advance (args_so_far, Pmode, (tree) 0, true);
 
       count++;
     }
@@ -4022,7 +4019,7 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
       mode = promote_function_mode (NULL_TREE, mode, &unsigned_p, NULL_TREE, 0);
       argvec[count].mode = mode;
       argvec[count].value = convert_modes (mode, GET_MODE (val), val, unsigned_p);
-      argvec[count].reg = targetm.calls.function_arg (args_so_far, mode,
+      argvec[count].reg = function_arg (args_so_far, mode,
 						      NULL_TREE, true);
 
       argvec[count].partial
@@ -4052,7 +4049,7 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
 			     GET_MODE_SIZE (mode) <= UNITS_PER_WORD);
 #endif
 
-      targetm.calls.function_arg_advance (args_so_far, mode, (tree) 0, true);
+      function_arg_advance (args_so_far, mode, (tree) 0, true);
     }
 
   /* If this machine requires an external definition for library
@@ -4399,8 +4396,7 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
 	       build_function_type (tfom, NULL_TREE),
 	       original_args_size.constant, args_size.constant,
 	       struct_value_size,
-	       targetm.calls.function_arg (args_so_far,
-					   VOIDmode, void_type_node, true),
+	       function_arg (args_so_far, VOIDmode, void_type_node, true),
 	       valreg,
 	       old_inhibit_defer_pop + 1, call_fusage, flags, args_so_far);
 
@@ -4835,7 +4831,10 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
 	 Note that in C the default argument promotions
 	 will prevent such mismatches.  */
 
-      size = GET_MODE_SIZE (arg->mode);
+      if (type_is_empty_record_p (TREE_TYPE (pval)))
+	size = 0;
+      else
+	size = GET_MODE_SIZE (arg->mode);
       /* Compute how much space the push instruction will push.
 	 On many machines, pushing a byte will advance the stack
 	 pointer by a halfword.  */
@@ -4865,10 +4864,14 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
 
       /* This isn't already where we want it on the stack, so put it there.
 	 This can either be done with push or copy insns.  */
-      if (!emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), NULL_RTX,
-		      parm_align, partial, reg, used - size, argblock,
-		      ARGS_SIZE_RTX (arg->locate.offset), reg_parm_stack_space,
-		      ARGS_SIZE_RTX (arg->locate.alignment_pad), true))
+      if (used
+	  && !emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval),
+			      NULL_RTX, parm_align, partial, reg,
+			      used - size, argblock,
+			      ARGS_SIZE_RTX (arg->locate.offset),
+			      reg_parm_stack_space,
+			      ARGS_SIZE_RTX (arg->locate.alignment_pad),
+			      true))
 	sibcall_failure = 1;
 
       /* Unless this is a partially-in-register argument, the argument is now
@@ -4900,10 +4903,15 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
 	{
 	  /* PUSH_ROUNDING has no effect on us, because emit_push_insn
 	     for BLKmode is careful to avoid it.  */
+	  bool empty_record = type_is_empty_record_p (TREE_TYPE (pval));
 	  excess = (arg->locate.size.constant
-		    - int_size_in_bytes (TREE_TYPE (pval))
+		    - (empty_record
+		       ? 0
+		       : int_size_in_bytes (TREE_TYPE (pval)))
 		    + partial);
-	  size_rtx = expand_expr (size_in_bytes (TREE_TYPE (pval)),
+	  size_rtx = expand_expr ((empty_record
+				   ? size_zero_node
+				   : size_in_bytes (TREE_TYPE (pval))),
 				  NULL_RTX, TYPE_MODE (sizetype),
 				  EXPAND_NORMAL);
 	}
@@ -4971,10 +4979,13 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
 	    }
 	}
 
-      emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), size_rtx,
-		      parm_align, partial, reg, excess, argblock,
-		      ARGS_SIZE_RTX (arg->locate.offset), reg_parm_stack_space,
-		      ARGS_SIZE_RTX (arg->locate.alignment_pad), false);
+      if (!CONST_INT_P (size_rtx) || INTVAL (size_rtx) != 0)
+	emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval),
+			size_rtx, parm_align, partial, reg, excess,
+			argblock, ARGS_SIZE_RTX (arg->locate.offset),
+			reg_parm_stack_space,
+			ARGS_SIZE_RTX (arg->locate.alignment_pad),
+			false);
 
       /* Unless this is a partially-in-register argument, the argument is now
 	 in the stack.
@@ -5052,6 +5063,9 @@ must_pass_in_stack_var_size_or_pad (machine_mode mode, const_tree type)
   if (TREE_ADDRESSABLE (type))
     return true;
 
+  if (type_is_empty_record_p (type))
+    return false;
+
   /* If the padding and mode of the type is such that a copy into
      a register would put it into the wrong part of the register.  */
   if (mode == BLKmode
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index cc42544..01d4d80 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -10277,7 +10277,8 @@ ix86_gimplify_va_arg (tree valist, tree type, gimple_seq *pre_p,
   indirect_p = pass_by_reference (NULL, TYPE_MODE (type), type, false);
   if (indirect_p)
     type = build_pointer_type (type);
-  size = int_size_in_bytes (type);
+  bool empty_record = type && type_is_empty_record_p (type);
+  size = empty_record ? 0 : int_size_in_bytes (type);
   rsize = CEIL (size, UNITS_PER_WORD);
 
   nat_mode = type_natural_mode (type, NULL, false);
diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index 216a301..d97aae6 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -6802,6 +6802,8 @@ finish_struct_1 (tree t)
 	  TYPE_TRANSPARENT_AGGR (t) = 0;
 	}
     }
+
+  TYPE_EMPTY_RECORD (t) = is_really_empty_class (t);
 }
 
 /* Insert FIELDS into T for the sorted case if the FIELDS count is
diff --git a/gcc/dse.c b/gcc/dse.c
index 35eef71..594106f 100644
--- a/gcc/dse.c
+++ b/gcc/dse.c
@@ -2366,7 +2366,7 @@ get_call_args (rtx call_insn, tree fn, rtx *args, int nargs)
     {
       machine_mode mode = TYPE_MODE (TREE_VALUE (arg));
       rtx reg, link, tmp;
-      reg = targetm.calls.function_arg (args_so_far, mode, NULL_TREE, true);
+      reg = function_arg (args_so_far, mode, NULL_TREE, true);
       if (!reg || !REG_P (reg) || GET_MODE (reg) != mode
 	  || GET_MODE_CLASS (mode) != MODE_INT)
 	return false;
@@ -2400,7 +2400,7 @@ get_call_args (rtx call_insn, tree fn, rtx *args, int nargs)
       if (tmp)
 	args[idx] = tmp;
 
-      targetm.calls.function_arg_advance (args_so_far, mode, NULL_TREE, true);
+      function_arg_advance (args_so_far, mode, NULL_TREE, true);
     }
   if (arg != void_list_node || idx != nargs)
     return false;
diff --git a/gcc/expr.c b/gcc/expr.c
index bd43dc4..384581a 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -1198,13 +1198,12 @@ block_move_libcall_safe_for_call_parm (void)
     for ( ; arg != void_list_node ; arg = TREE_CHAIN (arg))
       {
 	machine_mode mode = TYPE_MODE (TREE_VALUE (arg));
-	rtx tmp = targetm.calls.function_arg (args_so_far, mode,
-					      NULL_TREE, true);
+	rtx tmp = function_arg (args_so_far, mode, NULL_TREE, true);
 	if (!tmp || !REG_P (tmp))
 	  return false;
 	if (targetm.calls.arg_partial_bytes (args_so_far, mode, NULL, 1))
 	  return false;
-	targetm.calls.function_arg_advance (args_so_far, mode,
+	function_arg_advance (args_so_far, mode,
 					    NULL_TREE, true);
       }
   }
diff --git a/gcc/function.c b/gcc/function.c
index afc2c87..d8924fe 100644
--- a/gcc/function.c
+++ b/gcc/function.c
@@ -2073,7 +2073,7 @@ aggregate_value_p (const_tree exp, const_tree fntype)
   if (flag_pcc_struct_return && AGGREGATE_TYPE_P (type))
     return 1;
 
-  if (targetm.calls.return_in_memory (type, fntype))
+  if (return_in_memory (type, fntype))
     return 1;
 
   /* Make sure we have suitable call-clobbered regs to return
@@ -2523,10 +2523,10 @@ assign_parm_find_entry_rtl (struct assign_parm_data_all *all,
       return;
     }
 
-  entry_parm = targetm.calls.function_incoming_arg (all->args_so_far,
-						    data->promoted_mode,
-						    data->passed_type,
-						    data->named_arg);
+  entry_parm = function_incoming_arg (all->args_so_far,
+				      data->promoted_mode,
+				      data->passed_type,
+				      data->named_arg);
 
   if (entry_parm == 0)
     data->promoted_mode = data->passed_mode;
@@ -2550,9 +2550,9 @@ assign_parm_find_entry_rtl (struct assign_parm_data_all *all,
       if (targetm.calls.pretend_outgoing_varargs_named (all->args_so_far))
 	{
 	  rtx tem;
-	  tem = targetm.calls.function_incoming_arg (all->args_so_far,
-						     data->promoted_mode,
-						     data->passed_type, true);
+	  tem = function_incoming_arg (all->args_so_far,
+				       data->promoted_mode,
+				       data->passed_type, true);
 	  in_regs = tem != NULL;
 	}
     }
@@ -3752,8 +3752,8 @@ assign_parms (tree fndecl)
 	}
 
       /* Update info on where next arg arrives in registers.  */
-      targetm.calls.function_arg_advance (all.args_so_far, data.promoted_mode,
-					  data.passed_type, data.named_arg);
+      function_arg_advance (all.args_so_far, data.promoted_mode,
+			    data.passed_type, data.named_arg);
 
       if (POINTER_BOUNDS_TYPE_P (data.passed_type))
 	bound_no++;
@@ -3949,8 +3949,8 @@ gimplify_parameters (void)
 	continue;
 
       /* Update info on where next arg arrives in registers.  */
-      targetm.calls.function_arg_advance (all.args_so_far, data.promoted_mode,
-					  data.passed_type, data.named_arg);
+      function_arg_advance (all.args_so_far, data.promoted_mode,
+			    data.passed_type, data.named_arg);
 
       /* ??? Once upon a time variable_size stuffed parameter list
 	 SAVE_EXPRs (amongst others) onto a pending sizes list.  This
@@ -4093,8 +4093,11 @@ locate_and_pad_parm (machine_mode passed_mode, tree type, int in_regs,
 
   part_size_in_regs = (reg_parm_stack_space == 0 ? partial : 0);
 
-  sizetree
-    = type ? size_in_bytes (type) : size_int (GET_MODE_SIZE (passed_mode));
+  if (type)
+    sizetree = (type_is_empty_record_p (type)
+		? size_zero_node : size_in_bytes (type));
+  else
+    sizetree = size_int (GET_MODE_SIZE (passed_mode));
   where_pad = FUNCTION_ARG_PADDING (passed_mode, type);
   boundary = targetm.calls.function_arg_boundary (passed_mode, type);
   round_boundary = targetm.calls.function_arg_round_boundary (passed_mode,
@@ -6821,5 +6824,52 @@ make_pass_match_asm_constraints (gcc::context *ctxt)
   return new pass_match_asm_constraints (ctxt);
 }
 
+/* Wrapper for targetm.calls.function_arg_advance.  */
+
+void
+function_arg_advance (cumulative_args_t ca, machine_mode mode,
+		      const_tree type, bool named)
+{
+  if (type && type_is_empty_record_p (type))
+    return;
+
+  targetm.calls.function_arg_advance (ca, mode, type, named);
+}
+
+/* Wrapper for targetm.calls.function_arg.  */
+
+rtx
+function_arg (cumulative_args_t ca, machine_mode mode, const_tree type,
+	      bool named)
+{
+  if (type && type_is_empty_record_p (type))
+    return NULL;
+
+  return targetm.calls.function_arg (ca, mode, type, named);
+}
+
+/* Wrapper for targetm.calls.function_incoming_arg.  */
+
+rtx
+function_incoming_arg (cumulative_args_t ca, machine_mode mode,
+		       const_tree type, bool named)
+{
+  if (type && type_is_empty_record_p (type))
+    return NULL;
+
+  return targetm.calls.function_incoming_arg (ca, mode, type, named);
+}
+
+/* Wrapper for targetm.calls.return_in_memory.  */
+
+bool
+return_in_memory (const_tree type, const_tree fntype)
+{
+  if (type && type_is_empty_record_p (type))
+    return false;
+
+  return targetm.calls.return_in_memory (type, fntype);
+}
+
 
 #include "gt-function.h"
diff --git a/gcc/lto-streamer-out.c b/gcc/lto-streamer-out.c
index 0d610f1..898cee9 100644
--- a/gcc/lto-streamer-out.c
+++ b/gcc/lto-streamer-out.c
@@ -940,6 +940,8 @@ hash_tree (struct streamer_tree_cache_d *cache, hash_map<tree, hashval_t> *map,
       hstate.add_flag (TREE_READONLY (t));
       hstate.add_flag (TREE_PUBLIC (t));
     }
+  else
+    hstate.add_flag (TYPE_EMPTY_RECORD (t));
   hstate.add_flag (TREE_ADDRESSABLE (t));
   hstate.add_flag (TREE_THIS_VOLATILE (t));
   if (DECL_P (t))
diff --git a/gcc/lto/lto.c b/gcc/lto/lto.c
index 2661491..df403bf 100644
--- a/gcc/lto/lto.c
+++ b/gcc/lto/lto.c
@@ -1002,6 +1002,8 @@ compare_tree_sccs_1 (tree t1, tree t2, tree **map)
       compare_values (TREE_READONLY);
       compare_values (TREE_PUBLIC);
     }
+  else
+    compare_values (TYPE_EMPTY_RECORD);
   compare_values (TREE_ADDRESSABLE);
   compare_values (TREE_THIS_VOLATILE);
   if (DECL_P (t1))
diff --git a/gcc/print-tree.c b/gcc/print-tree.c
index cb0f1fd..bb489ff 100644
--- a/gcc/print-tree.c
+++ b/gcc/print-tree.c
@@ -587,6 +587,9 @@ print_node (FILE *file, const char *prefix, tree node, int indent)
       if (TYPE_RESTRICT (node))
 	fputs (" restrict", file);
 
+      if (TYPE_EMPTY_RECORD (node))
+	fputs (" empty-record", file);
+
       if (TYPE_LANG_FLAG_0 (node))
 	fputs (" type_0", file);
       if (TYPE_LANG_FLAG_1 (node))
diff --git a/gcc/target.h b/gcc/target.h
index ffc4d6a..eb01a76 100644
--- a/gcc/target.h
+++ b/gcc/target.h
@@ -104,6 +104,14 @@ extern bool target_default_pointer_address_modes_p (void);
    behaviour.  */
 extern unsigned int get_move_ratio (bool);
 
+extern void function_arg_advance (cumulative_args_t, machine_mode,
+				  const_tree, bool);
+extern rtx function_arg (cumulative_args_t, machine_mode, const_tree,
+			 bool);
+extern rtx function_incoming_arg (cumulative_args_t, machine_mode,
+				  const_tree, bool);
+extern bool return_in_memory (const_tree, const_tree);
+
 struct stdarg_info;
 struct spec_info_def;
 struct hard_reg_set_container;
diff --git a/gcc/targhooks.c b/gcc/targhooks.c
index 01d3686..de10ec3 100644
--- a/gcc/targhooks.c
+++ b/gcc/targhooks.c
@@ -1829,9 +1829,12 @@ std_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
   /* Hoist the valist value into a temporary for the moment.  */
   valist_tmp = get_initialized_tmp_var (valist, pre_p, NULL);
 
+  bool empty_record = type_is_empty_record_p (type);
+
   /* va_list pointer is aligned to PARM_BOUNDARY.  If argument actually
      requires greater alignment, we must perform dynamic alignment.  */
   if (boundary > align
+      && !empty_record
       && !integer_zerop (TYPE_SIZE (type)))
     {
       t = build2 (MODIFY_EXPR, TREE_TYPE (valist), valist_tmp,
@@ -1858,7 +1861,7 @@ std_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
     }
 
   /* Compute the rounded size of the type.  */
-  type_size = size_in_bytes (type);
+  type_size = empty_record ? size_zero_node : size_in_bytes (type);
   rounded_size = round_up (type_size, align);
 
   /* Reduce rounded_size so it's sharable with the postqueue.  */
diff --git a/gcc/testsuite/g++.dg/abi/empty12.C b/gcc/testsuite/g++.dg/abi/empty12.C
new file mode 100644
index 0000000..430d57d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/empty12.C
@@ -0,0 +1,17 @@
+// PR c++/60336
+// { dg-do run }
+// { dg-options "-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);
+  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/pr60336-1.C b/gcc/testsuite/g++.dg/pr60336-1.C
new file mode 100644
index 0000000..af08638
--- /dev/null
+++ b/gcc/testsuite/g++.dg/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/pr60336-2.C b/gcc/testsuite/g++.dg/pr60336-2.C
new file mode 100644
index 0000000..7b902e8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/pr60336-2.C
@@ -0,0 +1,28 @@
+// { dg-do run }
+// { dg-options "-O2" }
+
+#include <stdarg.h>
+
+struct dummy { struct{}__attribute__((aligned (4))) a[7]; };
+
+void
+test (struct dummy a, ...)
+{
+  va_list va_arglist;
+  int i;
+
+  va_start (va_arglist, a);
+  i = va_arg (va_arglist, int);
+  if (i != 0x10)
+    __builtin_abort ();
+  va_end (va_arglist);
+}
+
+struct dummy a0;
+
+int
+main ()
+{
+  test (a0, 0x10);
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/pr68355.C b/gcc/testsuite/g++.dg/pr68355.C
new file mode 100644
index 0000000..1354fc4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/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-*-* } } }
diff --git a/gcc/tree-core.h b/gcc/tree-core.h
index 9cc64d9..67bffa4 100644
--- a/gcc/tree-core.h
+++ b/gcc/tree-core.h
@@ -1086,6 +1086,9 @@ struct GTY(()) tree_base {
        FORCED_LABEL in
            LABEL_DECL
 
+       TYPE_EMPTY_RECORD in
+           all types
+
    volatile_flag:
 
        TREE_THIS_VOLATILE in
diff --git a/gcc/tree-dfa.c b/gcc/tree-dfa.c
index bb5cd49..1634ed6 100644
--- a/gcc/tree-dfa.c
+++ b/gcc/tree-dfa.c
@@ -394,6 +394,8 @@ get_ref_base_and_extent (tree exp, HOST_WIDE_INT *poffset,
       machine_mode mode = TYPE_MODE (TREE_TYPE (exp));
       if (mode == BLKmode)
 	size_tree = TYPE_SIZE (TREE_TYPE (exp));
+      else if (type_is_empty_record_p (TREE_TYPE (exp)))
+	bitsize = 0;
       else
 	bitsize = int (GET_MODE_PRECISION (mode));
     }
diff --git a/gcc/tree-streamer-in.c b/gcc/tree-streamer-in.c
index 65a1ce3..7b67534 100644
--- a/gcc/tree-streamer-in.c
+++ b/gcc/tree-streamer-in.c
@@ -112,7 +112,10 @@ unpack_ts_base_value_fields (struct bitpack_d *bp, tree expr)
       TREE_PUBLIC (expr) = (unsigned) bp_unpack_value (bp, 1);
     }
   else
-    bp_unpack_value (bp, 4);
+    {
+      TYPE_EMPTY_RECORD (expr) = (unsigned) bp_unpack_value (bp, 1);
+      bp_unpack_value (bp, 3);
+    }
   TREE_ADDRESSABLE (expr) = (unsigned) bp_unpack_value (bp, 1);
   TREE_THIS_VOLATILE (expr) = (unsigned) bp_unpack_value (bp, 1);
   if (DECL_P (expr))
diff --git a/gcc/tree-streamer-out.c b/gcc/tree-streamer-out.c
index d0b7f6d..9b14db8 100644
--- a/gcc/tree-streamer-out.c
+++ b/gcc/tree-streamer-out.c
@@ -83,7 +83,10 @@ pack_ts_base_value_fields (struct bitpack_d *bp, tree expr)
       bp_pack_value (bp, TREE_PUBLIC (expr), 1);
     }
   else
-    bp_pack_value (bp, 0, 4);
+    {
+      bp_pack_value (bp, TYPE_EMPTY_RECORD (expr), 1);
+      bp_pack_value (bp, 0, 3);
+    }
   bp_pack_value (bp, TREE_ADDRESSABLE (expr), 1);
   bp_pack_value (bp, TREE_THIS_VOLATILE (expr), 1);
   if (DECL_P (expr))
diff --git a/gcc/tree.h b/gcc/tree.h
index cb52deb..569f709 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -766,6 +766,10 @@ extern void omp_clause_range_check_failed (const_tree, const char *, int,
    computed gotos.  */
 #define FORCED_LABEL(NODE) (LABEL_DECL_CHECK (NODE)->base.side_effects_flag)
 
+/* Nonzero in a type considered an empty record.  */
+#define TYPE_EMPTY_RECORD(NODE) \
+  (TYPE_CHECK (NODE)->base.side_effects_flag)
+
 /* Nonzero means this expression is volatile in the C sense:
    its address should be of type `volatile WHATEVER *'.
    In other words, the declared item is volatile qualified.
@@ -5337,6 +5341,14 @@ type_with_alias_set_p (const_tree t)
   return false;
 }
 
+/* Return true if type T is an empty record.  */
+
+static inline bool
+type_is_empty_record_p (const_tree t)
+{
+  return TYPE_EMPTY_RECORD (TYPE_MAIN_VARIANT (t));
+}
+
 extern location_t set_block (location_t loc, tree block);
 
 extern void gt_ggc_mx (tree &);
diff --git a/gcc/ubsan.c b/gcc/ubsan.c
index 6fc6233..6dafc90 100644
--- a/gcc/ubsan.c
+++ b/gcc/ubsan.c
@@ -379,10 +379,11 @@ ubsan_type_descriptor (tree type, enum ubsan_print_style pstyle)
 
   if (pstyle == UBSAN_PRINT_POINTER)
     {
-      pp_printf (&pretty_name, "'%s%s%s%s%s%s%s",
+      pp_printf (&pretty_name, "'%s%s%s%s%s%s%s%s",
 		 TYPE_VOLATILE (type2) ? "volatile " : "",
 		 TYPE_READONLY (type2) ? "const " : "",
 		 TYPE_RESTRICT (type2) ? "restrict " : "",
+		 TYPE_EMPTY_RECORD (type2) ? "empty-record " : "",
 		 TYPE_ATOMIC (type2) ? "_Atomic " : "",
 		 TREE_CODE (type2) == RECORD_TYPE
 		 ? "struct "
diff --git a/gcc/var-tracking.c b/gcc/var-tracking.c
index 9185bfd..e9fdbe9 100644
--- a/gcc/var-tracking.c
+++ b/gcc/var-tracking.c
@@ -6140,10 +6140,10 @@ prepare_call_arguments (basic_block bb, rtx_insn *insn)
 		  rtx reg;
 		  INIT_CUMULATIVE_ARGS (args_so_far_v, type, NULL_RTX, fndecl,
 					nargs + 1);
-		  reg = targetm.calls.function_arg (args_so_far, mode,
-						    struct_addr, true);
-		  targetm.calls.function_arg_advance (args_so_far, mode,
-						      struct_addr, true);
+		  reg = function_arg (args_so_far, mode, struct_addr,
+				      true);
+		  function_arg_advance (args_so_far, mode, struct_addr,
+					true);
 		  if (reg == NULL_RTX)
 		    {
 		      for (; link; link = XEXP (link, 1))
@@ -6164,8 +6164,8 @@ prepare_call_arguments (basic_block bb, rtx_insn *insn)
 		  machine_mode mode;
 		  t = TYPE_ARG_TYPES (type);
 		  mode = TYPE_MODE (TREE_VALUE (t));
-		  this_arg = targetm.calls.function_arg (args_so_far, mode,
-							 TREE_VALUE (t), true);
+		  this_arg = function_arg (args_so_far, mode,
+					   TREE_VALUE (t), true);
 		  if (this_arg && !REG_P (this_arg))
 		    this_arg = NULL_RTX;
 		  else if (this_arg == NULL_RTX)
@@ -6280,8 +6280,7 @@ prepare_call_arguments (basic_block bb, rtx_insn *insn)
 		argtype = build_pointer_type (argtype);
 		mode = TYPE_MODE (argtype);
 	      }
-	    reg = targetm.calls.function_arg (args_so_far, mode,
-					      argtype, true);
+	    reg = function_arg (args_so_far, mode, argtype, true);
 	    if (TREE_CODE (argtype) == REFERENCE_TYPE
 		&& INTEGRAL_TYPE_P (TREE_TYPE (argtype))
 		&& reg
@@ -6335,8 +6334,7 @@ prepare_call_arguments (basic_block bb, rtx_insn *insn)
 			}
 		  }
 	      }
-	    targetm.calls.function_arg_advance (args_so_far, mode,
-						argtype, true);
+	    function_arg_advance (args_so_far, mode, argtype, true);
 	    t = TREE_CHAIN (t);
 	  }
       }
-- 
2.5.0


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

* Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
  2015-12-08 16:22 PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class H.J. Lu
@ 2015-12-09 14:05 ` Richard Biener
  2015-12-09 18:53   ` H.J. Lu
  0 siblings, 1 reply; 52+ messages in thread
From: Richard Biener @ 2015-12-09 14:05 UTC (permalink / raw)
  To: H.J. Lu; +Cc: Jason Merrill, GCC Patches

On Tue, Dec 8, 2015 at 5:22 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
> On Mon, Nov 23, 2015 at 12:53 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
>> On Mon, Nov 23, 2015 at 1:57 AM, Richard Biener
>> <richard.guenther@gmail.com> wrote:
>>> On Sat, Nov 21, 2015 at 12:46 AM, H.J. Lu <hjl.tools@gmail.com> wrote:
>>>> On Fri, Nov 20, 2015 at 2:17 PM, Jason Merrill <jason@redhat.com> wrote:
>>>>> On 11/20/2015 01:52 PM, H.J. Lu wrote:
>>>>>>
>>>>>> On Tue, Nov 17, 2015 at 4:22 AM, Richard Biener
>>>>>> <richard.guenther@gmail.com> wrote:
>>>>>>>
>>>>>>> On Tue, Nov 17, 2015 at 12:01 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
>>>>>>>>
>>>>>>>> Empty record should be returned and passed the same way in C and C++.
>>>>>>>> This patch adds LANG_HOOKS_EMPTY_RECORD_P for C++ empty class, which
>>>>>>>> defaults to return false.  For C++, LANG_HOOKS_EMPTY_RECORD_P is defined
>>>>>>>> to is_really_empty_class, which returns true for C++ empty classes.  For
>>>>>>>> LTO, we stream out a bit to indicate if a record is empty and we store
>>>>>>>> it in TYPE_LANG_FLAG_0 when streaming in.  get_ref_base_and_extent is
>>>>>>>> changed to set bitsize to 0 for empty records.  Middle-end and x86
>>>>>>>> backend are updated to ignore empty records for parameter passing and
>>>>>>>> function value return.  Other targets may need similar changes.
>>>>>>>
>>>>>>>
>>>>>>> Please avoid a new langhook for this and instead claim a bit in
>>>>>>> tree_type_common
>>>>>>> like for example restrict_flag (double-check it is unused for
>>>>>>> non-pointers).
>>>>>>
>>>>>>
>>>>>> There is no bit in tree_type_common I can overload.  restrict_flag is
>>>>>> checked for non-pointers to issue an error when it is used on
>>>>>> non-pointers:
>>>>>>
>>>>>>
>>>>>> /export/gnu/import/git/sources/gcc/gcc/testsuite/g++.dg/template/qualttp20.C:19:38:
>>>>>> error: ‘__restrict__’ qualifiers cannot be applied to ‘AS::L’
>>>>>>     typedef typename T::L __restrict__ r;// { dg-error "'__restrict__'
>>>>>> qualifiers cannot" "" }
>>>>>
>>>>>
>>>>> The C++ front end only needs to check TYPE_RESTRICT for this purpose on
>>>>> front-end-specific type codes like TEMPLATE_TYPE_PARM; cp_type_quals could
>>>>> handle that specifically if you change TYPE_RESTRICT to only apply to
>>>>> pointers.
>>>>>
>>>>
>>>> restrict_flag is also checked in this case:
>>>>
>>>> [hjl@gnu-6 gcc]$ cat x.i
>>>> struct dummy { };
>>>>
>>>> struct dummy
>>>> foo (struct dummy __restrict__ i)
>>>> {
>>>>   return i;
>>>> }
>>>> [hjl@gnu-6 gcc]$ gcc -S x.i -Wall
>>>> x.i:4:13: error: invalid use of ‘restrict’
>>>>  foo (struct dummy __restrict__ i)
>>>>              ^
>>>> x.i:4:13: error: invalid use of ‘restrict’
>>>> [hjl@gnu-6 gcc]$
>>>>
>>>> restrict_flag can't also be used to indicate `i' is an empty record.
>>>
>>> I'm sure this error can be done during parsing w/o relying on TYPE_RESTRICT.
>>>
>>> But well, use any other free bit (but do not enlarge
>>> tree_type_common).  Eventually
>>> you can free up a bit by putting sth into type_lang_specific currently
>>> using bits
>>> in tree_type_common.
>>
>> There are no bits in tree_type_common I can move.  Instead,
>> this patch overloads side_effects_flag in tree_base.  Tested on
>> Linux/x86-64.  OK for trunk?

I'm fine with using side_effects_flag for this.

I miss an explanation of where this detail of the ABI is documented and wonder
if the term "empty record" is also used in that document and how it is
documented.

Thus

+/* Nonzero in a type considered an empty record.  */
+#define TYPE_EMPTY_RECORD(NODE) \
+  (TYPE_CHECK (NODE)->base.side_effects_flag)

should refer to the ABI where is defined what an "empty record" is and how
it is handled by the backend(s).

+/* Return true if type T is an empty record.  */
+
+static inline bool
+type_is_empty_record_p (const_tree t)
+{
+  return TYPE_EMPTY_RECORD (TYPE_MAIN_VARIANT (t));

the type checker should probably check the bit is consistent across
variants so it can be tested on any of them.

You fail to adjust other targets gimplification hooks which suggests
this is a detail of the x86 psABI and not the C++ ABI?  If so I miss
a -Wpsabi warning for this change.

Did you investigate the effect of this patch on a larger code base?
More specifically does it only affect variadic functions?

An entry for changes.html is warranted as well.

Thanks,
Richard.

>> Thanks.
>>
>> --
>> H.J.
>
>
>
> --
> H.J.

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

* Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
  2015-12-09 14:05 ` Richard Biener
@ 2015-12-09 18:53   ` H.J. Lu
  2015-12-09 21:14     ` H.J. Lu
  2015-12-09 21:31     ` Markus Trippelsdorf
  0 siblings, 2 replies; 52+ messages in thread
From: H.J. Lu @ 2015-12-09 18:53 UTC (permalink / raw)
  To: Richard Biener; +Cc: Jason Merrill, GCC Patches

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

On Wed, Dec 9, 2015 at 6:05 AM, Richard Biener
<richard.guenther@gmail.com> wrote:
> On Tue, Dec 8, 2015 at 5:22 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
>> On Mon, Nov 23, 2015 at 12:53 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
>>> On Mon, Nov 23, 2015 at 1:57 AM, Richard Biener
>>> <richard.guenther@gmail.com> wrote:
>>>> On Sat, Nov 21, 2015 at 12:46 AM, H.J. Lu <hjl.tools@gmail.com> wrote:
>>>>> On Fri, Nov 20, 2015 at 2:17 PM, Jason Merrill <jason@redhat.com> wrote:
>>>>>> On 11/20/2015 01:52 PM, H.J. Lu wrote:
>>>>>>>
>>>>>>> On Tue, Nov 17, 2015 at 4:22 AM, Richard Biener
>>>>>>> <richard.guenther@gmail.com> wrote:
>>>>>>>>
>>>>>>>> On Tue, Nov 17, 2015 at 12:01 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
>>>>>>>>>
>>>>>>>>> Empty record should be returned and passed the same way in C and C++.
>>>>>>>>> This patch adds LANG_HOOKS_EMPTY_RECORD_P for C++ empty class, which
>>>>>>>>> defaults to return false.  For C++, LANG_HOOKS_EMPTY_RECORD_P is defined
>>>>>>>>> to is_really_empty_class, which returns true for C++ empty classes.  For
>>>>>>>>> LTO, we stream out a bit to indicate if a record is empty and we store
>>>>>>>>> it in TYPE_LANG_FLAG_0 when streaming in.  get_ref_base_and_extent is
>>>>>>>>> changed to set bitsize to 0 for empty records.  Middle-end and x86
>>>>>>>>> backend are updated to ignore empty records for parameter passing and
>>>>>>>>> function value return.  Other targets may need similar changes.
>>>>>>>>
>>>>>>>>
>>>>>>>> Please avoid a new langhook for this and instead claim a bit in
>>>>>>>> tree_type_common
>>>>>>>> like for example restrict_flag (double-check it is unused for
>>>>>>>> non-pointers).
>>>>>>>
>>>>>>>
>>>>>>> There is no bit in tree_type_common I can overload.  restrict_flag is
>>>>>>> checked for non-pointers to issue an error when it is used on
>>>>>>> non-pointers:
>>>>>>>
>>>>>>>
>>>>>>> /export/gnu/import/git/sources/gcc/gcc/testsuite/g++.dg/template/qualttp20.C:19:38:
>>>>>>> error: ‘__restrict__’ qualifiers cannot be applied to ‘AS::L’
>>>>>>>     typedef typename T::L __restrict__ r;// { dg-error "'__restrict__'
>>>>>>> qualifiers cannot" "" }
>>>>>>
>>>>>>
>>>>>> The C++ front end only needs to check TYPE_RESTRICT for this purpose on
>>>>>> front-end-specific type codes like TEMPLATE_TYPE_PARM; cp_type_quals could
>>>>>> handle that specifically if you change TYPE_RESTRICT to only apply to
>>>>>> pointers.
>>>>>>
>>>>>
>>>>> restrict_flag is also checked in this case:
>>>>>
>>>>> [hjl@gnu-6 gcc]$ cat x.i
>>>>> struct dummy { };
>>>>>
>>>>> struct dummy
>>>>> foo (struct dummy __restrict__ i)
>>>>> {
>>>>>   return i;
>>>>> }
>>>>> [hjl@gnu-6 gcc]$ gcc -S x.i -Wall
>>>>> x.i:4:13: error: invalid use of ‘restrict’
>>>>>  foo (struct dummy __restrict__ i)
>>>>>              ^
>>>>> x.i:4:13: error: invalid use of ‘restrict’
>>>>> [hjl@gnu-6 gcc]$
>>>>>
>>>>> restrict_flag can't also be used to indicate `i' is an empty record.
>>>>
>>>> I'm sure this error can be done during parsing w/o relying on TYPE_RESTRICT.
>>>>
>>>> But well, use any other free bit (but do not enlarge
>>>> tree_type_common).  Eventually
>>>> you can free up a bit by putting sth into type_lang_specific currently
>>>> using bits
>>>> in tree_type_common.
>>>
>>> There are no bits in tree_type_common I can move.  Instead,
>>> this patch overloads side_effects_flag in tree_base.  Tested on
>>> Linux/x86-64.  OK for trunk?
>
> I'm fine with using side_effects_flag for this.
>
> I miss an explanation of where this detail of the ABI is documented and wonder
> if the term "empty record" is also used in that document and how it is
> documented.
>
> Thus
>
> +/* Nonzero in a type considered an empty record.  */
> +#define TYPE_EMPTY_RECORD(NODE) \
> +  (TYPE_CHECK (NODE)->base.side_effects_flag)
>
> should refer to the ABI where is defined what an "empty record" is and how
> it is handled by the backend(s).

Empty C++ class is a corner case which isn't covered in psABI nor C++ ABI.
There is no mention of "empty record" in GCC documentation.  But there are
plenty of "empty class" in gcc/cp.  This change affects all targets.  C++ ABI
should specify how it should be passed.

> +/* Return true if type T is an empty record.  */
> +
> +static inline bool
> +type_is_empty_record_p (const_tree t)
> +{
> +  return TYPE_EMPTY_RECORD (TYPE_MAIN_VARIANT (t));
>
> the type checker should probably check the bit is consistent across
> variants so it can be tested on any of them.

TYPE_EMPTY_RECORD is only relevant for parameter passing.  For

struct foo;
typedef foo bar;

TYPE_EMPTY_RECORD has no impact.  Since call only uses
the main variant type, checking TYPE_MAIN_VARIANT is sufficient.

> You fail to adjust other targets gimplification hooks which suggests
> this is a detail of the x86 psABI and not the C++ ABI?  If so I miss
> a -Wpsabi warning for this change.

My change is generic.  The only x86 specific change is

diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index d30fbff..308d9a4e 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -10292,7 +10292,8 @@ ix86_gimplify_va_arg (tree valist, tree type, gimple_seq
 *pre_p,
   indirect_p = pass_by_reference (NULL, TYPE_MODE (type), type, false);
   if (indirect_p)
     type = build_pointer_type (type);
-  size = int_size_in_bytes (type);
+  bool empty_record = type && type_is_empty_record_p (type);
+  size = empty_record ? 0 : int_size_in_bytes (type);
   rsize = CEIL (size, UNITS_PER_WORD);

   nat_mode = type_natural_mode (type, NULL, false);

which is for 64-bit variadic functions.  I added a testcase,
g++.dg/pr60336-2.C, for it.  There are also other variadic tests
in g++.dg/compat.  If a target doesn't use std_gimplify_va_arg_expr,
it needs similar change.  I see that TARGET_GIMPLIFY_VA_ARG_EXPR
is also defined in

aarch64/aarch64.c
alpha/alpha.c
arm/arm.c
epiphany/epiphany.c
ia64/ia64.c
mep/mep.c
mips/mips.c
msp430/msp430.c
pa/pa.c
rs6000/rs6000.c
s390/s390.c
sh/sh.c
sparc/sparc.c
spu/spu.c
stormy16/stormy16.c
tilegx/tilegx.c
tilepro/tilepro.c
visium/visium.c
xtensa/xtensa.c

I will prepare a separate patch for those targets to try.

> Did you investigate the effect of this patch on a larger code base?

I use it to bootstrap GCC.

> More specifically does it only affect variadic functions?

See above.

> An entry for changes.html is warranted as well.
>
>

I will prepare a patch for changes.html.

Here is the updated patch with -Wpsabi warning:

/export/gnu/import/git/sources/gcc/gcc/testsuite/g++.dg/abi/empty12.C:15:12:
note: the ABI of passing empty record has changed in GCC 6

Thanks.

-- 
H.J.

[-- Attachment #2: 0001-Add-TYPE_EMPTY_RECORD-for-C-empty-class.patch --]
[-- Type: text/x-patch, Size: 32635 bytes --]

From 3cb893e2c9c096ebea82fd7f72d52926a12d77e4 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 TYPE_EMPTY_RECORD for C++ empty class

Empty record should be returned and passed the same way in C and C++.
This patch overloads a bit, side_effects_flag, in tree_base for C++
empty class.  Middle-end and x86 backend are updated to ignore empty
records for parameter passing and function value return.  Other targets
may need similar changes.

get_ref_base_and_extent is changed to set bitsize to 0 for empty records
so that when ref_maybe_used_by_call_p_1 calls get_ref_base_and_extent to
get 0 as the maximum size on empty record.  Otherwise, find_tail_calls
won't perform tail call optimization for functions with empty record
parameters, as shown in g++.dg/pr60336-1.C and g++.dg/pr60336-2.C.

gcc/

	PR c++/60336
	PR middle-end/67239
	PR target/68355
	* calls.c (initialize_argument_information): Replace
	targetm.calls.function_arg, targetm.calls.function_incoming_arg
	and targetm.calls.function_arg_advance with function_arg,
	function_incoming_arg and function_arg_advance.
	(expand_call): Likewise.
	(emit_library_call_value_1): Likewise.
	(store_one_arg): Use 0 for empty record size.  Don't
	push 0 size argument onto stack.
	(must_pass_in_stack_var_size_or_pad): Return false for empty
	record.
	* dse.c (get_call_args): Replace targetm.calls.function_arg
	and targetm.calls.function_arg_advance with function_arg and
	function_arg_advance.
	* expr.c (block_move_libcall_safe_for_call_parm): Likewise.
	* function.c (aggregate_value_p): Replace
	targetm.calls.return_in_memory with return_in_memory.
	(assign_parm_find_entry_rtl): Replace
	targetm.calls.function_incoming_arg with function_incoming_arg.
	(assign_parms): Replace targetm.calls.function_arg_advance with
	function_arg_advance.
	(gimplify_parameters): Replace targetm.calls.function_arg_advance
	with function_arg_advance.
	(locate_and_pad_parm): Use 0 for empty record size.
	(warn_empty_record): New function.
	(function_arg_advance): New wrapper function.
	(function_arg): Likewise.
	(function_incoming_arg): Likewise.
	(return_in_memory): Likewise.
	* lto-streamer-out.c (hash_tree): Call hstate.add_flag with
	TYPE_EMPTY_RECORD for types.
	* print-tree.c (print_node): Also handle TYPE_EMPTY_RECORD.
	* ubsan.c (ubsan_type_descriptor): Likewise.
	* target.h (function_arg_advance): New prototype.
	(function_arg): Likewise.
	(function_incoming_arg): Likewise.
	(return_in_memory): Likewise.
	* targhooks.c (std_gimplify_va_arg_expr): Use 0 for empty record
	size.
	* tree-dfa.c (get_ref_base_and_extent): Likewise.
	* tree-core.h (tree_base): Mention TYPE_EMPTY_RECORD in comments
	for side_effects_flag.
	* tree-streamer-in.c (unpack_ts_base_value_fields): Stream in
	TYPE_EMPTY_RECORD for types.
	* tree-streamer-out.c (pack_ts_base_value_fields): Stream out
	TYPE_EMPTY_RECORD for types.
	* tree.h (TYPE_EMPTY_RECORD): New.
	(type_is_empty_record_p): New static inline function.
	* var-tracking.c (prepare_call_arguments): Replace
	targetm.calls.function_arg and targetm.calls.function_arg_advance
	with function_arg and function_arg_advance.
	* config/i386/i386.c (ix86_gimplify_va_arg): Use 0 for empty
	record size.

gcc/c

	PR c++/60336
	PR middle-end/67239
	PR target/68355
	* c-aux-info.c (gen_type): Add TYPE_EMPTY_RECORD check.

gcc/cp/

	PR c++/60336
	PR middle-end/67239
	PR target/68355
	* class.c (finish_struct_1): Set TYPE_EMPTY_RECORD with return
	value from is_really_empty_class ().

gcc/lto/

	PR c++/60336
	PR middle-end/67239
	PR target/68355
	* lto.c (compare_tree_sccs_1): Call compare_values with
	TYPE_EMPTY_RECORD for types.

gcc/testsuite/

	PR c++/60336
	PR middle-end/67239
	PR target/68355
	* g++.dg/abi/empty12.C: New test.
	* g++.dg/abi/empty12.h: Likewise.
	* g++.dg/abi/empty12a.c: Likewise.
	* g++.dg/pr60336-1.C: Likewise.
	* g++.dg/pr60336-2.C: Likewise.
	* g++.dg/pr68355.C: Likewise.
---
 gcc/c/c-aux-info.c                  |  2 +
 gcc/calls.c                         | 76 ++++++++++++++++------------
 gcc/config/i386/i386.c              |  3 +-
 gcc/cp/class.c                      |  2 +
 gcc/dse.c                           |  4 +-
 gcc/expr.c                          |  5 +-
 gcc/function.c                      | 98 +++++++++++++++++++++++++++++++------
 gcc/lto-streamer-out.c              |  2 +
 gcc/lto/lto.c                       |  2 +
 gcc/print-tree.c                    |  3 ++
 gcc/target.h                        |  8 +++
 gcc/targhooks.c                     |  5 +-
 gcc/testsuite/g++.dg/abi/empty12.C  | 17 +++++++
 gcc/testsuite/g++.dg/abi/empty12.h  |  9 ++++
 gcc/testsuite/g++.dg/abi/empty12a.c |  6 +++
 gcc/testsuite/g++.dg/pr60336-1.C    | 17 +++++++
 gcc/testsuite/g++.dg/pr60336-2.C    | 28 +++++++++++
 gcc/testsuite/g++.dg/pr68355.C      | 24 +++++++++
 gcc/tree-core.h                     |  3 ++
 gcc/tree-dfa.c                      |  2 +
 gcc/tree-streamer-in.c              |  5 +-
 gcc/tree-streamer-out.c             |  5 +-
 gcc/tree.h                          | 12 +++++
 gcc/ubsan.c                         |  3 +-
 gcc/var-tracking.c                  | 18 +++----
 25 files changed, 294 insertions(+), 65 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/abi/empty12.C
 create mode 100644 gcc/testsuite/g++.dg/abi/empty12.h
 create mode 100644 gcc/testsuite/g++.dg/abi/empty12a.c
 create mode 100644 gcc/testsuite/g++.dg/pr60336-1.C
 create mode 100644 gcc/testsuite/g++.dg/pr60336-2.C
 create mode 100644 gcc/testsuite/g++.dg/pr68355.C

diff --git a/gcc/c/c-aux-info.c b/gcc/c/c-aux-info.c
index 79d9851..93b001b 100644
--- a/gcc/c/c-aux-info.c
+++ b/gcc/c/c-aux-info.c
@@ -433,6 +433,8 @@ gen_type (const char *ret_val, tree t, formals_style style)
     ret_val = concat ("volatile ", ret_val, NULL);
   if (TYPE_RESTRICT (t))
     ret_val = concat ("restrict ", ret_val, NULL);
+  if (TYPE_EMPTY_RECORD (t))
+    ret_val = concat ("empty-record ", ret_val, NULL);
   return ret_val;
 }
 
diff --git a/gcc/calls.c b/gcc/calls.c
index 1eb4ec7..557c51e 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -1399,8 +1399,8 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
       args[i].unsignedp = unsignedp;
       args[i].mode = mode;
 
-      args[i].reg = targetm.calls.function_arg (args_so_far, mode, type,
-						argpos < n_named_args);
+      args[i].reg = function_arg (args_so_far, mode, type,
+				  argpos < n_named_args);
 
       if (args[i].reg && CONST_INT_P (args[i].reg))
 	{
@@ -1413,8 +1413,8 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
 	 arguments have to go into the incoming registers.  */
       if (targetm.calls.function_incoming_arg != targetm.calls.function_arg)
 	args[i].tail_call_reg
-	  = targetm.calls.function_incoming_arg (args_so_far, mode, type,
-						 argpos < n_named_args);
+	  = function_incoming_arg (args_so_far, mode, type,
+				   argpos < n_named_args);
       else
 	args[i].tail_call_reg = args[i].reg;
 
@@ -1475,8 +1475,8 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
       /* Increment ARGS_SO_FAR, which has info about which arg-registers
 	 have been used, etc.  */
 
-      targetm.calls.function_arg_advance (args_so_far, TYPE_MODE (type),
-					  type, argpos < n_named_args);
+      function_arg_advance (args_so_far, TYPE_MODE (type), type,
+			    argpos < n_named_args);
     }
 }
 
@@ -3328,14 +3328,11 @@ expand_call (tree exp, rtx target, int ignore)
       /* Set up next argument register.  For sibling calls on machines
 	 with register windows this should be the incoming register.  */
       if (pass == 0)
-	next_arg_reg = targetm.calls.function_incoming_arg (args_so_far,
-							    VOIDmode,
-							    void_type_node,
-							    true);
+	next_arg_reg = function_incoming_arg (args_so_far, VOIDmode,
+					      void_type_node, true);
       else
-	next_arg_reg = targetm.calls.function_arg (args_so_far,
-						   VOIDmode, void_type_node,
-						   true);
+	next_arg_reg = function_arg (args_so_far, VOIDmode,
+				     void_type_node, true);
 
       if (pass == 1 && (return_flags & ERF_RETURNS_ARG))
 	{
@@ -3947,8 +3944,8 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
       argvec[count].mode = Pmode;
       argvec[count].partial = 0;
 
-      argvec[count].reg = targetm.calls.function_arg (args_so_far,
-						      Pmode, NULL_TREE, true);
+      argvec[count].reg = function_arg (args_so_far, Pmode, NULL_TREE,
+					true);
       gcc_assert (targetm.calls.arg_partial_bytes (args_so_far, Pmode,
 						   NULL_TREE, 1) == 0);
 
@@ -3965,7 +3962,7 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
 	  || reg_parm_stack_space > 0)
 	args_size.constant += argvec[count].locate.size.constant;
 
-      targetm.calls.function_arg_advance (args_so_far, Pmode, (tree) 0, true);
+      function_arg_advance (args_so_far, Pmode, (tree) 0, true);
 
       count++;
     }
@@ -4030,7 +4027,7 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
       mode = promote_function_mode (NULL_TREE, mode, &unsigned_p, NULL_TREE, 0);
       argvec[count].mode = mode;
       argvec[count].value = convert_modes (mode, GET_MODE (val), val, unsigned_p);
-      argvec[count].reg = targetm.calls.function_arg (args_so_far, mode,
+      argvec[count].reg = function_arg (args_so_far, mode,
 						      NULL_TREE, true);
 
       argvec[count].partial
@@ -4060,7 +4057,7 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
 			     GET_MODE_SIZE (mode) <= UNITS_PER_WORD);
 #endif
 
-      targetm.calls.function_arg_advance (args_so_far, mode, (tree) 0, true);
+      function_arg_advance (args_so_far, mode, (tree) 0, true);
     }
 
   /* If this machine requires an external definition for library
@@ -4407,8 +4404,7 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
 	       build_function_type (tfom, NULL_TREE),
 	       original_args_size.constant, args_size.constant,
 	       struct_value_size,
-	       targetm.calls.function_arg (args_so_far,
-					   VOIDmode, void_type_node, true),
+	       function_arg (args_so_far, VOIDmode, void_type_node, true),
 	       valreg,
 	       old_inhibit_defer_pop + 1, call_fusage, flags, args_so_far);
 
@@ -4843,7 +4839,10 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
 	 Note that in C the default argument promotions
 	 will prevent such mismatches.  */
 
-      size = GET_MODE_SIZE (arg->mode);
+      if (type_is_empty_record_p (TREE_TYPE (pval)))
+	size = 0;
+      else
+	size = GET_MODE_SIZE (arg->mode);
       /* Compute how much space the push instruction will push.
 	 On many machines, pushing a byte will advance the stack
 	 pointer by a halfword.  */
@@ -4873,10 +4872,14 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
 
       /* This isn't already where we want it on the stack, so put it there.
 	 This can either be done with push or copy insns.  */
-      if (!emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), NULL_RTX,
-		      parm_align, partial, reg, used - size, argblock,
-		      ARGS_SIZE_RTX (arg->locate.offset), reg_parm_stack_space,
-		      ARGS_SIZE_RTX (arg->locate.alignment_pad), true))
+      if (used
+	  && !emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval),
+			      NULL_RTX, parm_align, partial, reg,
+			      used - size, argblock,
+			      ARGS_SIZE_RTX (arg->locate.offset),
+			      reg_parm_stack_space,
+			      ARGS_SIZE_RTX (arg->locate.alignment_pad),
+			      true))
 	sibcall_failure = 1;
 
       /* Unless this is a partially-in-register argument, the argument is now
@@ -4908,10 +4911,15 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
 	{
 	  /* PUSH_ROUNDING has no effect on us, because emit_push_insn
 	     for BLKmode is careful to avoid it.  */
+	  bool empty_record = type_is_empty_record_p (TREE_TYPE (pval));
 	  excess = (arg->locate.size.constant
-		    - int_size_in_bytes (TREE_TYPE (pval))
+		    - (empty_record
+		       ? 0
+		       : int_size_in_bytes (TREE_TYPE (pval)))
 		    + partial);
-	  size_rtx = expand_expr (size_in_bytes (TREE_TYPE (pval)),
+	  size_rtx = expand_expr ((empty_record
+				   ? size_zero_node
+				   : size_in_bytes (TREE_TYPE (pval))),
 				  NULL_RTX, TYPE_MODE (sizetype),
 				  EXPAND_NORMAL);
 	}
@@ -4986,10 +4994,13 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
 	    }
 	}
 
-      emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), size_rtx,
-		      parm_align, partial, reg, excess, argblock,
-		      ARGS_SIZE_RTX (arg->locate.offset), reg_parm_stack_space,
-		      ARGS_SIZE_RTX (arg->locate.alignment_pad), false);
+      if (!CONST_INT_P (size_rtx) || INTVAL (size_rtx) != 0)
+	emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval),
+			size_rtx, parm_align, partial, reg, excess,
+			argblock, ARGS_SIZE_RTX (arg->locate.offset),
+			reg_parm_stack_space,
+			ARGS_SIZE_RTX (arg->locate.alignment_pad),
+			false);
 
       /* Unless this is a partially-in-register argument, the argument is now
 	 in the stack.
@@ -5067,6 +5078,9 @@ must_pass_in_stack_var_size_or_pad (machine_mode mode, const_tree type)
   if (TREE_ADDRESSABLE (type))
     return true;
 
+  if (type_is_empty_record_p (type))
+    return false;
+
   /* If the padding and mode of the type is such that a copy into
      a register would put it into the wrong part of the register.  */
   if (mode == BLKmode
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index d30fbff..308d9a4e 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -10292,7 +10292,8 @@ ix86_gimplify_va_arg (tree valist, tree type, gimple_seq *pre_p,
   indirect_p = pass_by_reference (NULL, TYPE_MODE (type), type, false);
   if (indirect_p)
     type = build_pointer_type (type);
-  size = int_size_in_bytes (type);
+  bool empty_record = type && type_is_empty_record_p (type);
+  size = empty_record ? 0 : int_size_in_bytes (type);
   rsize = CEIL (size, UNITS_PER_WORD);
 
   nat_mode = type_natural_mode (type, NULL, false);
diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index 216a301..d97aae6 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -6802,6 +6802,8 @@ finish_struct_1 (tree t)
 	  TYPE_TRANSPARENT_AGGR (t) = 0;
 	}
     }
+
+  TYPE_EMPTY_RECORD (t) = is_really_empty_class (t);
 }
 
 /* Insert FIELDS into T for the sorted case if the FIELDS count is
diff --git a/gcc/dse.c b/gcc/dse.c
index 35eef71..594106f 100644
--- a/gcc/dse.c
+++ b/gcc/dse.c
@@ -2366,7 +2366,7 @@ get_call_args (rtx call_insn, tree fn, rtx *args, int nargs)
     {
       machine_mode mode = TYPE_MODE (TREE_VALUE (arg));
       rtx reg, link, tmp;
-      reg = targetm.calls.function_arg (args_so_far, mode, NULL_TREE, true);
+      reg = function_arg (args_so_far, mode, NULL_TREE, true);
       if (!reg || !REG_P (reg) || GET_MODE (reg) != mode
 	  || GET_MODE_CLASS (mode) != MODE_INT)
 	return false;
@@ -2400,7 +2400,7 @@ get_call_args (rtx call_insn, tree fn, rtx *args, int nargs)
       if (tmp)
 	args[idx] = tmp;
 
-      targetm.calls.function_arg_advance (args_so_far, mode, NULL_TREE, true);
+      function_arg_advance (args_so_far, mode, NULL_TREE, true);
     }
   if (arg != void_list_node || idx != nargs)
     return false;
diff --git a/gcc/expr.c b/gcc/expr.c
index bd43dc4..384581a 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -1198,13 +1198,12 @@ block_move_libcall_safe_for_call_parm (void)
     for ( ; arg != void_list_node ; arg = TREE_CHAIN (arg))
       {
 	machine_mode mode = TYPE_MODE (TREE_VALUE (arg));
-	rtx tmp = targetm.calls.function_arg (args_so_far, mode,
-					      NULL_TREE, true);
+	rtx tmp = function_arg (args_so_far, mode, NULL_TREE, true);
 	if (!tmp || !REG_P (tmp))
 	  return false;
 	if (targetm.calls.arg_partial_bytes (args_so_far, mode, NULL, 1))
 	  return false;
-	targetm.calls.function_arg_advance (args_so_far, mode,
+	function_arg_advance (args_so_far, mode,
 					    NULL_TREE, true);
       }
   }
diff --git a/gcc/function.c b/gcc/function.c
index b513ead..eccd968 100644
--- a/gcc/function.c
+++ b/gcc/function.c
@@ -2074,7 +2074,7 @@ aggregate_value_p (const_tree exp, const_tree fntype)
   if (flag_pcc_struct_return && AGGREGATE_TYPE_P (type))
     return 1;
 
-  if (targetm.calls.return_in_memory (type, fntype))
+  if (return_in_memory (type, fntype))
     return 1;
 
   /* Make sure we have suitable call-clobbered regs to return
@@ -2524,10 +2524,10 @@ assign_parm_find_entry_rtl (struct assign_parm_data_all *all,
       return;
     }
 
-  entry_parm = targetm.calls.function_incoming_arg (all->args_so_far,
-						    data->promoted_mode,
-						    data->passed_type,
-						    data->named_arg);
+  entry_parm = function_incoming_arg (all->args_so_far,
+				      data->promoted_mode,
+				      data->passed_type,
+				      data->named_arg);
 
   if (entry_parm == 0)
     data->promoted_mode = data->passed_mode;
@@ -2551,9 +2551,9 @@ assign_parm_find_entry_rtl (struct assign_parm_data_all *all,
       if (targetm.calls.pretend_outgoing_varargs_named (all->args_so_far))
 	{
 	  rtx tem;
-	  tem = targetm.calls.function_incoming_arg (all->args_so_far,
-						     data->promoted_mode,
-						     data->passed_type, true);
+	  tem = function_incoming_arg (all->args_so_far,
+				       data->promoted_mode,
+				       data->passed_type, true);
 	  in_regs = tem != NULL;
 	}
     }
@@ -3791,8 +3791,8 @@ assign_parms (tree fndecl)
 	}
 
       /* Update info on where next arg arrives in registers.  */
-      targetm.calls.function_arg_advance (all.args_so_far, data.promoted_mode,
-					  data.passed_type, data.named_arg);
+      function_arg_advance (all.args_so_far, data.promoted_mode,
+			    data.passed_type, data.named_arg);
 
       if (POINTER_BOUNDS_TYPE_P (data.passed_type))
 	bound_no++;
@@ -3988,8 +3988,8 @@ gimplify_parameters (void)
 	continue;
 
       /* Update info on where next arg arrives in registers.  */
-      targetm.calls.function_arg_advance (all.args_so_far, data.promoted_mode,
-					  data.passed_type, data.named_arg);
+      function_arg_advance (all.args_so_far, data.promoted_mode,
+			    data.passed_type, data.named_arg);
 
       /* ??? Once upon a time variable_size stuffed parameter list
 	 SAVE_EXPRs (amongst others) onto a pending sizes list.  This
@@ -4132,8 +4132,11 @@ locate_and_pad_parm (machine_mode passed_mode, tree type, int in_regs,
 
   part_size_in_regs = (reg_parm_stack_space == 0 ? partial : 0);
 
-  sizetree
-    = type ? size_in_bytes (type) : size_int (GET_MODE_SIZE (passed_mode));
+  if (type)
+    sizetree = (type_is_empty_record_p (type)
+		? size_zero_node : size_in_bytes (type));
+  else
+    sizetree = size_int (GET_MODE_SIZE (passed_mode));
   where_pad = FUNCTION_ARG_PADDING (passed_mode, type);
   boundary = targetm.calls.function_arg_boundary (passed_mode, type);
   round_boundary = targetm.calls.function_arg_round_boundary (passed_mode,
@@ -6862,5 +6865,72 @@ make_pass_match_asm_constraints (gcc::context *ctxt)
   return new pass_match_asm_constraints (ctxt);
 }
 
+static void
+warn_empty_record (void)
+{
+  if (warn_psabi)
+    inform (input_location, "the ABI of passing empty record has"
+	    " changed in GCC 6");
+}
+
+/* Wrapper for targetm.calls.function_arg_advance.  */
+
+void
+function_arg_advance (cumulative_args_t ca, machine_mode mode,
+		      const_tree type, bool named)
+{
+  if (type && type_is_empty_record_p (type))
+    {
+      warn_empty_record ();
+      return;
+    }
+
+  targetm.calls.function_arg_advance (ca, mode, type, named);
+}
+
+/* Wrapper for targetm.calls.function_arg.  */
+
+rtx
+function_arg (cumulative_args_t ca, machine_mode mode, const_tree type,
+	      bool named)
+{
+  if (type && type_is_empty_record_p (type))
+    {
+      warn_empty_record ();
+      return NULL;
+    }
+
+  return targetm.calls.function_arg (ca, mode, type, named);
+}
+
+/* Wrapper for targetm.calls.function_incoming_arg.  */
+
+rtx
+function_incoming_arg (cumulative_args_t ca, machine_mode mode,
+		       const_tree type, bool named)
+{
+  if (type && type_is_empty_record_p (type))
+    {
+      warn_empty_record ();
+      return NULL;
+    }
+
+  return targetm.calls.function_incoming_arg (ca, mode, type, named);
+}
+
+/* Wrapper for targetm.calls.return_in_memory.  */
+
+bool
+return_in_memory (const_tree type, const_tree fntype)
+{
+  if (type && type_is_empty_record_p (type))
+    {
+      warn_empty_record ();
+      return false;
+    }
+
+  return targetm.calls.return_in_memory (type, fntype);
+}
+
 
 #include "gt-function.h"
diff --git a/gcc/lto-streamer-out.c b/gcc/lto-streamer-out.c
index a874846..509f0b3 100644
--- a/gcc/lto-streamer-out.c
+++ b/gcc/lto-streamer-out.c
@@ -944,6 +944,8 @@ hash_tree (struct streamer_tree_cache_d *cache, hash_map<tree, hashval_t> *map,
       hstate.add_flag (TREE_READONLY (t));
       hstate.add_flag (TREE_PUBLIC (t));
     }
+  else
+    hstate.add_flag (TYPE_EMPTY_RECORD (t));
   hstate.add_flag (TREE_ADDRESSABLE (t));
   hstate.add_flag (TREE_THIS_VOLATILE (t));
   if (DECL_P (t))
diff --git a/gcc/lto/lto.c b/gcc/lto/lto.c
index 90712b4..6755132 100644
--- a/gcc/lto/lto.c
+++ b/gcc/lto/lto.c
@@ -1002,6 +1002,8 @@ compare_tree_sccs_1 (tree t1, tree t2, tree **map)
       compare_values (TREE_READONLY);
       compare_values (TREE_PUBLIC);
     }
+  else
+    compare_values (TYPE_EMPTY_RECORD);
   compare_values (TREE_ADDRESSABLE);
   compare_values (TREE_THIS_VOLATILE);
   if (DECL_P (t1))
diff --git a/gcc/print-tree.c b/gcc/print-tree.c
index cb0f1fd..bb489ff 100644
--- a/gcc/print-tree.c
+++ b/gcc/print-tree.c
@@ -587,6 +587,9 @@ print_node (FILE *file, const char *prefix, tree node, int indent)
       if (TYPE_RESTRICT (node))
 	fputs (" restrict", file);
 
+      if (TYPE_EMPTY_RECORD (node))
+	fputs (" empty-record", file);
+
       if (TYPE_LANG_FLAG_0 (node))
 	fputs (" type_0", file);
       if (TYPE_LANG_FLAG_1 (node))
diff --git a/gcc/target.h b/gcc/target.h
index ffc4d6a..eb01a76 100644
--- a/gcc/target.h
+++ b/gcc/target.h
@@ -104,6 +104,14 @@ extern bool target_default_pointer_address_modes_p (void);
    behaviour.  */
 extern unsigned int get_move_ratio (bool);
 
+extern void function_arg_advance (cumulative_args_t, machine_mode,
+				  const_tree, bool);
+extern rtx function_arg (cumulative_args_t, machine_mode, const_tree,
+			 bool);
+extern rtx function_incoming_arg (cumulative_args_t, machine_mode,
+				  const_tree, bool);
+extern bool return_in_memory (const_tree, const_tree);
+
 struct stdarg_info;
 struct spec_info_def;
 struct hard_reg_set_container;
diff --git a/gcc/targhooks.c b/gcc/targhooks.c
index dcf0863..7a8d1e8 100644
--- a/gcc/targhooks.c
+++ b/gcc/targhooks.c
@@ -1827,9 +1827,12 @@ std_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
   /* Hoist the valist value into a temporary for the moment.  */
   valist_tmp = get_initialized_tmp_var (valist, pre_p, NULL);
 
+  bool empty_record = type_is_empty_record_p (type);
+
   /* va_list pointer is aligned to PARM_BOUNDARY.  If argument actually
      requires greater alignment, we must perform dynamic alignment.  */
   if (boundary > align
+      && !empty_record
       && !integer_zerop (TYPE_SIZE (type)))
     {
       t = build2 (MODIFY_EXPR, TREE_TYPE (valist), valist_tmp,
@@ -1856,7 +1859,7 @@ std_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
     }
 
   /* Compute the rounded size of the type.  */
-  type_size = size_in_bytes (type);
+  type_size = empty_record ? size_zero_node : size_in_bytes (type);
   rounded_size = round_up (type_size, align);
 
   /* Reduce rounded_size so it's sharable with the postqueue.  */
diff --git a/gcc/testsuite/g++.dg/abi/empty12.C b/gcc/testsuite/g++.dg/abi/empty12.C
new file mode 100644
index 0000000..db2fd24
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/empty12.C
@@ -0,0 +1,17 @@
+// PR c++/60336
+// { dg-do run }
+// { dg-options "-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-message "note: the ABI of passing empty record has changed in GCC 6" }
+  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/pr60336-1.C b/gcc/testsuite/g++.dg/pr60336-1.C
new file mode 100644
index 0000000..2136f48
--- /dev/null
+++ b/gcc/testsuite/g++.dg/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-message "note: the ABI of passing empty record has changed in GCC 6" }
+}
+
+// { dg-final { scan-assembler "jmp\[\t \]+\[^\$\]*?_Z3xxx9true_type" { target i?86-*-* x86_64-*-* } } }
diff --git a/gcc/testsuite/g++.dg/pr60336-2.C b/gcc/testsuite/g++.dg/pr60336-2.C
new file mode 100644
index 0000000..7ab7d23
--- /dev/null
+++ b/gcc/testsuite/g++.dg/pr60336-2.C
@@ -0,0 +1,28 @@
+// { dg-do run }
+// { dg-options "-O2" }
+
+#include <stdarg.h>
+
+struct dummy { struct{}__attribute__((aligned (4))) a[7]; };
+
+void
+test (struct dummy a, ...) // { dg-message "note: the ABI of passing empty record has changed in GCC 6" }
+{
+  va_list va_arglist;
+  int i;
+
+  va_start (va_arglist, a);
+  i = va_arg (va_arglist, int);
+  if (i != 0x10)
+    __builtin_abort ();
+  va_end (va_arglist);
+}
+
+struct dummy a0;
+
+int
+main ()
+{
+  test (a0, 0x10); // { dg-message "note: the ABI of passing empty record has changed in GCC 6" }
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/pr68355.C b/gcc/testsuite/g++.dg/pr68355.C
new file mode 100644
index 0000000..90584d0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/pr68355.C
@@ -0,0 +1,24 @@
+// { dg-do compile }
+// { dg-options "-Wno-psabi -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-*-* } } }
diff --git a/gcc/tree-core.h b/gcc/tree-core.h
index 9cc64d9..67bffa4 100644
--- a/gcc/tree-core.h
+++ b/gcc/tree-core.h
@@ -1086,6 +1086,9 @@ struct GTY(()) tree_base {
        FORCED_LABEL in
            LABEL_DECL
 
+       TYPE_EMPTY_RECORD in
+           all types
+
    volatile_flag:
 
        TREE_THIS_VOLATILE in
diff --git a/gcc/tree-dfa.c b/gcc/tree-dfa.c
index bb5cd49..1634ed6 100644
--- a/gcc/tree-dfa.c
+++ b/gcc/tree-dfa.c
@@ -394,6 +394,8 @@ get_ref_base_and_extent (tree exp, HOST_WIDE_INT *poffset,
       machine_mode mode = TYPE_MODE (TREE_TYPE (exp));
       if (mode == BLKmode)
 	size_tree = TYPE_SIZE (TREE_TYPE (exp));
+      else if (type_is_empty_record_p (TREE_TYPE (exp)))
+	bitsize = 0;
       else
 	bitsize = int (GET_MODE_PRECISION (mode));
     }
diff --git a/gcc/tree-streamer-in.c b/gcc/tree-streamer-in.c
index 3162d1a..2aee7ad 100644
--- a/gcc/tree-streamer-in.c
+++ b/gcc/tree-streamer-in.c
@@ -112,7 +112,10 @@ unpack_ts_base_value_fields (struct bitpack_d *bp, tree expr)
       TREE_PUBLIC (expr) = (unsigned) bp_unpack_value (bp, 1);
     }
   else
-    bp_unpack_value (bp, 4);
+    {
+      TYPE_EMPTY_RECORD (expr) = (unsigned) bp_unpack_value (bp, 1);
+      bp_unpack_value (bp, 3);
+    }
   TREE_ADDRESSABLE (expr) = (unsigned) bp_unpack_value (bp, 1);
   TREE_THIS_VOLATILE (expr) = (unsigned) bp_unpack_value (bp, 1);
   if (DECL_P (expr))
diff --git a/gcc/tree-streamer-out.c b/gcc/tree-streamer-out.c
index bfd0644..4306fcc 100644
--- a/gcc/tree-streamer-out.c
+++ b/gcc/tree-streamer-out.c
@@ -83,7 +83,10 @@ pack_ts_base_value_fields (struct bitpack_d *bp, tree expr)
       bp_pack_value (bp, TREE_PUBLIC (expr), 1);
     }
   else
-    bp_pack_value (bp, 0, 4);
+    {
+      bp_pack_value (bp, TYPE_EMPTY_RECORD (expr), 1);
+      bp_pack_value (bp, 0, 3);
+    }
   bp_pack_value (bp, TREE_ADDRESSABLE (expr), 1);
   bp_pack_value (bp, TREE_THIS_VOLATILE (expr), 1);
   if (DECL_P (expr))
diff --git a/gcc/tree.h b/gcc/tree.h
index aef825d..9b13d5c 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -766,6 +766,10 @@ extern void omp_clause_range_check_failed (const_tree, const char *, int,
    computed gotos.  */
 #define FORCED_LABEL(NODE) (LABEL_DECL_CHECK (NODE)->base.side_effects_flag)
 
+/* Nonzero in a type considered an empty record.  */
+#define TYPE_EMPTY_RECORD(NODE) \
+  (TYPE_CHECK (NODE)->base.side_effects_flag)
+
 /* Nonzero means this expression is volatile in the C sense:
    its address should be of type `volatile WHATEVER *'.
    In other words, the declared item is volatile qualified.
@@ -5379,6 +5383,14 @@ get_finish (location_t loc)
   return get_range_from_loc (line_table, loc).m_finish;
 }
 
+/* Return true if type T is an empty record.  */
+
+static inline bool
+type_is_empty_record_p (const_tree t)
+{
+  return TYPE_EMPTY_RECORD (TYPE_MAIN_VARIANT (t));
+}
+
 extern location_t set_block (location_t loc, tree block);
 
 extern void gt_ggc_mx (tree &);
diff --git a/gcc/ubsan.c b/gcc/ubsan.c
index 6fc6233..6dafc90 100644
--- a/gcc/ubsan.c
+++ b/gcc/ubsan.c
@@ -379,10 +379,11 @@ ubsan_type_descriptor (tree type, enum ubsan_print_style pstyle)
 
   if (pstyle == UBSAN_PRINT_POINTER)
     {
-      pp_printf (&pretty_name, "'%s%s%s%s%s%s%s",
+      pp_printf (&pretty_name, "'%s%s%s%s%s%s%s%s",
 		 TYPE_VOLATILE (type2) ? "volatile " : "",
 		 TYPE_READONLY (type2) ? "const " : "",
 		 TYPE_RESTRICT (type2) ? "restrict " : "",
+		 TYPE_EMPTY_RECORD (type2) ? "empty-record " : "",
 		 TYPE_ATOMIC (type2) ? "_Atomic " : "",
 		 TREE_CODE (type2) == RECORD_TYPE
 		 ? "struct "
diff --git a/gcc/var-tracking.c b/gcc/var-tracking.c
index 9185bfd..e9fdbe9 100644
--- a/gcc/var-tracking.c
+++ b/gcc/var-tracking.c
@@ -6140,10 +6140,10 @@ prepare_call_arguments (basic_block bb, rtx_insn *insn)
 		  rtx reg;
 		  INIT_CUMULATIVE_ARGS (args_so_far_v, type, NULL_RTX, fndecl,
 					nargs + 1);
-		  reg = targetm.calls.function_arg (args_so_far, mode,
-						    struct_addr, true);
-		  targetm.calls.function_arg_advance (args_so_far, mode,
-						      struct_addr, true);
+		  reg = function_arg (args_so_far, mode, struct_addr,
+				      true);
+		  function_arg_advance (args_so_far, mode, struct_addr,
+					true);
 		  if (reg == NULL_RTX)
 		    {
 		      for (; link; link = XEXP (link, 1))
@@ -6164,8 +6164,8 @@ prepare_call_arguments (basic_block bb, rtx_insn *insn)
 		  machine_mode mode;
 		  t = TYPE_ARG_TYPES (type);
 		  mode = TYPE_MODE (TREE_VALUE (t));
-		  this_arg = targetm.calls.function_arg (args_so_far, mode,
-							 TREE_VALUE (t), true);
+		  this_arg = function_arg (args_so_far, mode,
+					   TREE_VALUE (t), true);
 		  if (this_arg && !REG_P (this_arg))
 		    this_arg = NULL_RTX;
 		  else if (this_arg == NULL_RTX)
@@ -6280,8 +6280,7 @@ prepare_call_arguments (basic_block bb, rtx_insn *insn)
 		argtype = build_pointer_type (argtype);
 		mode = TYPE_MODE (argtype);
 	      }
-	    reg = targetm.calls.function_arg (args_so_far, mode,
-					      argtype, true);
+	    reg = function_arg (args_so_far, mode, argtype, true);
 	    if (TREE_CODE (argtype) == REFERENCE_TYPE
 		&& INTEGRAL_TYPE_P (TREE_TYPE (argtype))
 		&& reg
@@ -6335,8 +6334,7 @@ prepare_call_arguments (basic_block bb, rtx_insn *insn)
 			}
 		  }
 	      }
-	    targetm.calls.function_arg_advance (args_so_far, mode,
-						argtype, true);
+	    function_arg_advance (args_so_far, mode, argtype, true);
 	    t = TREE_CHAIN (t);
 	  }
       }
-- 
2.5.0


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

* Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
  2015-12-09 18:53   ` H.J. Lu
@ 2015-12-09 21:14     ` H.J. Lu
  2015-12-09 21:31     ` Markus Trippelsdorf
  1 sibling, 0 replies; 52+ messages in thread
From: H.J. Lu @ 2015-12-09 21:14 UTC (permalink / raw)
  To: Richard Biener; +Cc: Jason Merrill, GCC Patches

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

On Wed, Dec 9, 2015 at 10:53 AM, H.J. Lu <hjl.tools@gmail.com> wrote:
> On Wed, Dec 9, 2015 at 6:05 AM, Richard Biener
> <richard.guenther@gmail.com> wrote:
>> On Tue, Dec 8, 2015 at 5:22 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
>>> On Mon, Nov 23, 2015 at 12:53 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
>>>> On Mon, Nov 23, 2015 at 1:57 AM, Richard Biener
>>>> <richard.guenther@gmail.com> wrote:
>>>>> On Sat, Nov 21, 2015 at 12:46 AM, H.J. Lu <hjl.tools@gmail.com> wrote:
>>>>>> On Fri, Nov 20, 2015 at 2:17 PM, Jason Merrill <jason@redhat.com> wrote:
>>>>>>> On 11/20/2015 01:52 PM, H.J. Lu wrote:
>>>>>>>>
>>>>>>>> On Tue, Nov 17, 2015 at 4:22 AM, Richard Biener
>>>>>>>> <richard.guenther@gmail.com> wrote:
>>>>>>>>>
>>>>>>>>> On Tue, Nov 17, 2015 at 12:01 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
>>>>>>>>>>
>>>>>>>>>> Empty record should be returned and passed the same way in C and C++.
>>>>>>>>>> This patch adds LANG_HOOKS_EMPTY_RECORD_P for C++ empty class, which
>>>>>>>>>> defaults to return false.  For C++, LANG_HOOKS_EMPTY_RECORD_P is defined
>>>>>>>>>> to is_really_empty_class, which returns true for C++ empty classes.  For
>>>>>>>>>> LTO, we stream out a bit to indicate if a record is empty and we store
>>>>>>>>>> it in TYPE_LANG_FLAG_0 when streaming in.  get_ref_base_and_extent is
>>>>>>>>>> changed to set bitsize to 0 for empty records.  Middle-end and x86
>>>>>>>>>> backend are updated to ignore empty records for parameter passing and
>>>>>>>>>> function value return.  Other targets may need similar changes.
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> Please avoid a new langhook for this and instead claim a bit in
>>>>>>>>> tree_type_common
>>>>>>>>> like for example restrict_flag (double-check it is unused for
>>>>>>>>> non-pointers).
>>>>>>>>
>>>>>>>>
>>>>>>>> There is no bit in tree_type_common I can overload.  restrict_flag is
>>>>>>>> checked for non-pointers to issue an error when it is used on
>>>>>>>> non-pointers:
>>>>>>>>
>>>>>>>>
>>>>>>>> /export/gnu/import/git/sources/gcc/gcc/testsuite/g++.dg/template/qualttp20.C:19:38:
>>>>>>>> error: ‘__restrict__’ qualifiers cannot be applied to ‘AS::L’
>>>>>>>>     typedef typename T::L __restrict__ r;// { dg-error "'__restrict__'
>>>>>>>> qualifiers cannot" "" }
>>>>>>>
>>>>>>>
>>>>>>> The C++ front end only needs to check TYPE_RESTRICT for this purpose on
>>>>>>> front-end-specific type codes like TEMPLATE_TYPE_PARM; cp_type_quals could
>>>>>>> handle that specifically if you change TYPE_RESTRICT to only apply to
>>>>>>> pointers.
>>>>>>>
>>>>>>
>>>>>> restrict_flag is also checked in this case:
>>>>>>
>>>>>> [hjl@gnu-6 gcc]$ cat x.i
>>>>>> struct dummy { };
>>>>>>
>>>>>> struct dummy
>>>>>> foo (struct dummy __restrict__ i)
>>>>>> {
>>>>>>   return i;
>>>>>> }
>>>>>> [hjl@gnu-6 gcc]$ gcc -S x.i -Wall
>>>>>> x.i:4:13: error: invalid use of ‘restrict’
>>>>>>  foo (struct dummy __restrict__ i)
>>>>>>              ^
>>>>>> x.i:4:13: error: invalid use of ‘restrict’
>>>>>> [hjl@gnu-6 gcc]$
>>>>>>
>>>>>> restrict_flag can't also be used to indicate `i' is an empty record.
>>>>>
>>>>> I'm sure this error can be done during parsing w/o relying on TYPE_RESTRICT.
>>>>>
>>>>> But well, use any other free bit (but do not enlarge
>>>>> tree_type_common).  Eventually
>>>>> you can free up a bit by putting sth into type_lang_specific currently
>>>>> using bits
>>>>> in tree_type_common.
>>>>
>>>> There are no bits in tree_type_common I can move.  Instead,
>>>> this patch overloads side_effects_flag in tree_base.  Tested on
>>>> Linux/x86-64.  OK for trunk?
>>
>> I'm fine with using side_effects_flag for this.
>>
>> I miss an explanation of where this detail of the ABI is documented and wonder
>> if the term "empty record" is also used in that document and how it is
>> documented.
>>
>> Thus
>>
>> +/* Nonzero in a type considered an empty record.  */
>> +#define TYPE_EMPTY_RECORD(NODE) \
>> +  (TYPE_CHECK (NODE)->base.side_effects_flag)
>>
>> should refer to the ABI where is defined what an "empty record" is and how
>> it is handled by the backend(s).
>
> Empty C++ class is a corner case which isn't covered in psABI nor C++ ABI.
> There is no mention of "empty record" in GCC documentation.  But there are
> plenty of "empty class" in gcc/cp.  This change affects all targets.  C++ ABI
> should specify how it should be passed.
>
>> +/* Return true if type T is an empty record.  */
>> +
>> +static inline bool
>> +type_is_empty_record_p (const_tree t)
>> +{
>> +  return TYPE_EMPTY_RECORD (TYPE_MAIN_VARIANT (t));
>>
>> the type checker should probably check the bit is consistent across
>> variants so it can be tested on any of them.
>
> TYPE_EMPTY_RECORD is only relevant for parameter passing.  For
>
> struct foo;
> typedef foo bar;
>
> TYPE_EMPTY_RECORD has no impact.  Since call only uses
> the main variant type, checking TYPE_MAIN_VARIANT is sufficient.
>
>> You fail to adjust other targets gimplification hooks which suggests
>> this is a detail of the x86 psABI and not the C++ ABI?  If so I miss
>> a -Wpsabi warning for this change.
>
> My change is generic.  The only x86 specific change is
>
> diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
> index d30fbff..308d9a4e 100644
> --- a/gcc/config/i386/i386.c
> +++ b/gcc/config/i386/i386.c
> @@ -10292,7 +10292,8 @@ ix86_gimplify_va_arg (tree valist, tree type, gimple_seq
>  *pre_p,
>    indirect_p = pass_by_reference (NULL, TYPE_MODE (type), type, false);
>    if (indirect_p)
>      type = build_pointer_type (type);
> -  size = int_size_in_bytes (type);
> +  bool empty_record = type && type_is_empty_record_p (type);
> +  size = empty_record ? 0 : int_size_in_bytes (type);
>    rsize = CEIL (size, UNITS_PER_WORD);
>
>    nat_mode = type_natural_mode (type, NULL, false);
>
> which is for 64-bit variadic functions.  I added a testcase,
> g++.dg/pr60336-2.C, for it.  There are also other variadic tests
> in g++.dg/compat.  If a target doesn't use std_gimplify_va_arg_expr,
> it needs similar change.  I see that TARGET_GIMPLIFY_VA_ARG_EXPR
> is also defined in
>
> aarch64/aarch64.c
> alpha/alpha.c
> arm/arm.c
> epiphany/epiphany.c
> ia64/ia64.c
> mep/mep.c
> mips/mips.c
> msp430/msp430.c
> pa/pa.c
> rs6000/rs6000.c
> s390/s390.c
> sh/sh.c
> sparc/sparc.c
> spu/spu.c
> stormy16/stormy16.c
> tilegx/tilegx.c
> tilepro/tilepro.c
> visium/visium.c
> xtensa/xtensa.c
>
> I will prepare a separate patch for those targets to try.
>

Here is a patch for other backends to properly pass C++ empty class.
One can check out hjl/pr60336/master branch from

https://github.com/hjl-tools/gcc

to get both generic and target-dependent patches.

-- 
H.J.

[-- Attachment #2: 0001-Properly-pass-C-empty-class.patch --]
[-- Type: text/x-patch, Size: 16071 bytes --]

From 7332cc2ab12c46639fa029ba2af023c654db7a93 Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@gmail.com>
Date: Wed, 9 Dec 2015 12:48:07 -0800
Subject: [PATCH] Properly pass C++ empty class

Use 0 for empty record size in TARGET_GIMPLIFY_VA_ARG_EXPR.

	PR c++/60336
	PR middle-end/67239
	PR target/68355
	* config/aarch64/aarch64.c (aarch64_gimplify_va_arg_expr): Use 0
	for empty record size.
	* config/mep/mep.c (mep_gimplify_va_arg_expr): Likewise.
	* config/mips/mips.c (mips_std_gimplify_va_arg_expr): Likewise.
	(mips_gimplify_va_arg_expr): Likewise.
	* config/msp430/msp430.c (msp430_gimplify_va_arg_expr): Likewise.
	* config/pa/pa.c (hppa_gimplify_va_arg_expr): Likewise.
	* config/rs6000/rs6000.c (rs6000_gimplify_va_arg): Likewise.
	* config/s390/s390.c (s390_gimplify_va_arg): Likewise.
	* config/sh/sh.c (sh_gimplify_va_arg_expr): Likewise.
	* config/sparc/sparc.c (sparc_gimplify_va_arg): Likewise.
	* config/spu/spu.c (spu_gimplify_va_arg_expr): Likewise.
	* config/stormy16/stormy16.c (xstormy16_gimplify_va_arg_expr):
	Likewise.
	* config/visium/visium.c (visium_gimplify_va_arg): Likewise.
	* config/xtensa/xtensa.c (xtensa_gimplify_va_arg_expr): Likewise.
	* config/alpha/alpha.c (alpha_setup_incoming_varargs): Replace
	targetm.calls.function_arg_advance with function_arg_advance.
	(alpha_gimplify_va_arg_1): Use 0 for empty record size.
	* config/microblaze/microblaze.c (microblaze_expand_prologue):
	Replace targetm.calls.function_arg with function_arg.  Replace
	targetm.calls.function_arg_advance with function_arg_advance.
	* config/tilegx/tilegx.c (tilegx_setup_incoming_varargs): Replace
	targetm.calls.function_arg_advance with function_arg_advance.
	(tilegx_gimplify_va_arg_expr): Use 0 for empty record size.
	* config/tilepro/tilepro.c (tilepro_setup_incoming_varargs):
	Replace targetm.calls.function_arg_advance with
	function_arg_advance.
	(tilepro_gimplify_va_arg_expr): Use 0 for empty record size.
---
 gcc/config/aarch64/aarch64.c       |  3 ++-
 gcc/config/alpha/alpha.c           |  6 +++---
 gcc/config/mep/mep.c               |  3 ++-
 gcc/config/microblaze/microblaze.c | 11 +++++------
 gcc/config/mips/mips.c             |  6 ++++--
 gcc/config/msp430/msp430.c         |  3 ++-
 gcc/config/pa/pa.c                 |  3 ++-
 gcc/config/rs6000/rs6000.c         |  3 ++-
 gcc/config/s390/s390.c             |  3 ++-
 gcc/config/sh/sh.c                 |  3 ++-
 gcc/config/sparc/sparc.c           |  3 ++-
 gcc/config/spu/spu.c               |  3 ++-
 gcc/config/stormy16/stormy16.c     |  6 ++++--
 gcc/config/tilegx/tilegx.c         |  7 ++++---
 gcc/config/tilepro/tilepro.c       |  7 ++++---
 gcc/config/visium/visium.c         |  3 ++-
 gcc/config/xtensa/xtensa.c         |  3 ++-
 17 files changed, 46 insertions(+), 30 deletions(-)

diff --git a/gcc/config/aarch64/aarch64.c b/gcc/config/aarch64/aarch64.c
index 1e1b864..cfb9f66 100644
--- a/gcc/config/aarch64/aarch64.c
+++ b/gcc/config/aarch64/aarch64.c
@@ -9618,7 +9618,8 @@ aarch64_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
 
   stack = build3 (COMPONENT_REF, TREE_TYPE (f_stack), unshare_expr (valist),
 		  f_stack, NULL_TREE);
-  size = int_size_in_bytes (type);
+  bool empty_record = type && type_is_empty_record_p (type);
+  size = empty_record ? 0 : int_size_in_bytes (type);
   align = aarch64_function_arg_alignment (mode, type) / BITS_PER_UNIT;
 
   dw_align = false;
diff --git a/gcc/config/alpha/alpha.c b/gcc/config/alpha/alpha.c
index 4cfae82..d5d2f66 100644
--- a/gcc/config/alpha/alpha.c
+++ b/gcc/config/alpha/alpha.c
@@ -6117,8 +6117,7 @@ alpha_setup_incoming_varargs (cumulative_args_t pcum, machine_mode mode,
   CUMULATIVE_ARGS cum = *get_cumulative_args (pcum);
 
   /* Skip the current argument.  */
-  targetm.calls.function_arg_advance (pack_cumulative_args (&cum), mode, type,
-				      true);
+  function_arg_advance (pack_cumulative_args (&cum), mode, type, true);
 
 #if TARGET_ABI_OPEN_VMS
   /* For VMS, we allocate space for all 6 arg registers plus a count.
@@ -6304,7 +6303,8 @@ alpha_gimplify_va_arg_1 (tree type, tree base, tree offset,
   gimple_seq_add_seq (pre_p, internal_post);
 
   /* Update the offset field.  */
-  type_size = TYPE_SIZE_UNIT (TYPE_MAIN_VARIANT (type));
+  bool empty_record = type && type_is_empty_record_p (type);
+  type_size = empty_record ? 0 : TYPE_SIZE_UNIT (TYPE_MAIN_VARIANT (type));
   if (type_size == NULL || TREE_OVERFLOW (type_size))
     t = size_zero_node;
   else
diff --git a/gcc/config/mep/mep.c b/gcc/config/mep/mep.c
index 5ab56bd..65aec24 100644
--- a/gcc/config/mep/mep.c
+++ b/gcc/config/mep/mep.c
@@ -3525,7 +3525,8 @@ mep_gimplify_va_arg_expr (tree valist, tree type,
 
   ivc2_vec = TARGET_IVC2 && VECTOR_TYPE_P (type);
 
-  size = int_size_in_bytes (type);
+  bool empty_record = type && type_is_empty_record_p (type);
+  size = empty_record ? 0 : int_size_in_bytes (type);
   by_reference = (size > (ivc2_vec ? 8 : 4)) || (size <= 0);
 
   if (by_reference)
diff --git a/gcc/config/microblaze/microblaze.c b/gcc/config/microblaze/microblaze.c
index aebbc3b..7a7c397 100644
--- a/gcc/config/microblaze/microblaze.c
+++ b/gcc/config/microblaze/microblaze.c
@@ -2795,8 +2795,8 @@ microblaze_expand_prologue (void)
 	  passed_mode = Pmode;
 	}
 
-      entry_parm = targetm.calls.function_arg (args_so_far, passed_mode,
-					       passed_type, true);
+      entry_parm = function_arg (args_so_far, passed_mode, passed_type,
+				 true);
 
       if (entry_parm)
 	{
@@ -2816,8 +2816,7 @@ microblaze_expand_prologue (void)
 	  break;
 	}
 
-      targetm.calls.function_arg_advance (args_so_far, passed_mode,
-					  passed_type, true);
+      function_arg_advance (args_so_far, passed_mode, passed_type, true);
 
       next_arg = TREE_CHAIN (cur_arg);
       if (next_arg == 0)
@@ -2831,8 +2830,8 @@ microblaze_expand_prologue (void)
 
   /* Split parallel insn into a sequence of insns.  */
 
-  next_arg_reg = targetm.calls.function_arg (args_so_far, VOIDmode,
-					     void_type_node, true);
+  next_arg_reg = function_arg (args_so_far, VOIDmode, void_type_node,
+			       true);
   if (next_arg_reg != 0 && GET_CODE (next_arg_reg) == PARALLEL)
     {
       rtvec adjust = XVEC (next_arg_reg, 0);
diff --git a/gcc/config/mips/mips.c b/gcc/config/mips/mips.c
index 6145944..61c6d0c 100644
--- a/gcc/config/mips/mips.c
+++ b/gcc/config/mips/mips.c
@@ -6308,7 +6308,8 @@ mips_std_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
     }
 
   /* Compute the rounded size of the type.  */
-  type_size = size_in_bytes (type);
+  bool empty_record = type && type_is_empty_record_p (type);
+  type_size = empty_record ? 0 : size_in_bytes (type);
   rounded_size = round_up (type_size, align);
 
   /* Reduce rounded_size so it's sharable with the postqueue.  */
@@ -6397,7 +6398,8 @@ mips_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
 
       ovfl = build3 (COMPONENT_REF, TREE_TYPE (f_ovfl), valist, f_ovfl,
 		     NULL_TREE);
-      size = int_size_in_bytes (type);
+      bool empty_record = type && type_is_empty_record_p (type);
+      size = empty_record ? 0 : int_size_in_bytes (type);
 
       if (GET_MODE_CLASS (TYPE_MODE (type)) == MODE_FLOAT
 	  && GET_MODE_SIZE (TYPE_MODE (type)) <= UNITS_PER_FPVALUE)
diff --git a/gcc/config/msp430/msp430.c b/gcc/config/msp430/msp430.c
index 88301c8..1726f5b 100644
--- a/gcc/config/msp430/msp430.c
+++ b/gcc/config/msp430/msp430.c
@@ -1451,7 +1451,8 @@ msp430_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
     }
 
   /* Compute the rounded size of the type.  */
-  type_size = size_in_bytes (type);
+  bool empty_record = type && type_is_empty_record_p (type);
+  type_size = empty_record ? 0 : size_in_bytes (type);
   rounded_size = round_up (type_size, align);
 
   /* Reduce rounded_size so it's sharable with the postqueue.  */
diff --git a/gcc/config/pa/pa.c b/gcc/config/pa/pa.c
index b8caab5..ef956cf 100644
--- a/gcc/config/pa/pa.c
+++ b/gcc/config/pa/pa.c
@@ -6305,7 +6305,8 @@ hppa_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
 	  type = ptr;
 	  ptr = build_pointer_type (type);
 	}
-      size = int_size_in_bytes (type);
+      bool empty_record = type && type_is_empty_record_p (type);
+      size = empty_record ? 0 : int_size_in_bytes (type);
       valist_type = TREE_TYPE (valist);
 
       /* Args grow down.  Not handled by generic routines.  */
diff --git a/gcc/config/rs6000/rs6000.c b/gcc/config/rs6000/rs6000.c
index 6b22f93..ef829ed 100644
--- a/gcc/config/rs6000/rs6000.c
+++ b/gcc/config/rs6000/rs6000.c
@@ -11983,7 +11983,8 @@ rs6000_gimplify_va_arg (tree valist, tree type, gimple_seq *pre_p,
   sav = build3 (COMPONENT_REF, TREE_TYPE (f_sav), unshare_expr (valist),
 		f_sav, NULL_TREE);
 
-  size = int_size_in_bytes (type);
+  bool empty_record = type && type_is_empty_record_p (type);
+  size = empty_record ? 0 : int_size_in_bytes (type);
   rsize = (size + 3) / 4;
   align = 1;
 
diff --git a/gcc/config/s390/s390.c b/gcc/config/s390/s390.c
index f8928b9..6f6639f 100644
--- a/gcc/config/s390/s390.c
+++ b/gcc/config/s390/s390.c
@@ -11569,7 +11569,8 @@ s390_gimplify_va_arg (tree valist, tree type, gimple_seq *pre_p,
   valist = unshare_expr (valist);
   ovf = build3 (COMPONENT_REF, TREE_TYPE (f_ovf), valist, f_ovf, NULL_TREE);
 
-  size = int_size_in_bytes (type);
+  bool empty_record = type && type_is_empty_record_p (type);
+  size = empty_record ? 0 : int_size_in_bytes (type);
 
   s390_check_type_for_vector_abi (type, true, false);
 
diff --git a/gcc/config/sh/sh.c b/gcc/config/sh/sh.c
index 919ea1c..f1f66f9 100644
--- a/gcc/config/sh/sh.c
+++ b/gcc/config/sh/sh.c
@@ -8765,7 +8765,8 @@ sh_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
   if (pass_by_ref)
     type = build_pointer_type (type);
 
-  size = int_size_in_bytes (type);
+  bool empty_record = type && type_is_empty_record_p (type);
+  size = empty_record ? 0 : int_size_in_bytes (type);
   rsize = (size + UNITS_PER_WORD - 1) & -UNITS_PER_WORD;
   pptr_type_node = build_pointer_type (ptr_type_node);
 
diff --git a/gcc/config/sparc/sparc.c b/gcc/config/sparc/sparc.c
index 55ddacf..559bc69 100644
--- a/gcc/config/sparc/sparc.c
+++ b/gcc/config/sparc/sparc.c
@@ -7425,7 +7425,8 @@ sparc_gimplify_va_arg (tree valist, tree type, gimple_seq *pre_p,
   else
     {
       indirect = false;
-      size = int_size_in_bytes (type);
+      bool empty_record = type && type_is_empty_record_p (type);
+      size = empty_record ? 0 : int_size_in_bytes (type);
       rsize = ROUND_UP (size, UNITS_PER_WORD);
       align = 0;
 
diff --git a/gcc/config/spu/spu.c b/gcc/config/spu/spu.c
index 31502fb..1b24b12 100644
--- a/gcc/config/spu/spu.c
+++ b/gcc/config/spu/spu.c
@@ -4028,7 +4028,8 @@ spu_gimplify_va_arg_expr (tree valist, tree type, gimple_seq * pre_p,
 					   false);
   if (pass_by_reference_p)
     type = build_pointer_type (type);
-  size = int_size_in_bytes (type);
+  bool empty_record = type && type_is_empty_record_p (type);
+  size = empty_record ? 0 : int_size_in_bytes (type);
   rsize = ((size + UNITS_PER_WORD - 1) / UNITS_PER_WORD) * UNITS_PER_WORD;
 
   /* build conditional expression to calculate addr. The expression
diff --git a/gcc/config/stormy16/stormy16.c b/gcc/config/stormy16/stormy16.c
index f626853..bff3e27 100644
--- a/gcc/config/stormy16/stormy16.c
+++ b/gcc/config/stormy16/stormy16.c
@@ -1337,8 +1337,10 @@ xstormy16_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
   count = build3 (COMPONENT_REF, TREE_TYPE (f_count), valist, f_count,
 		  NULL_TREE);
 
+  bool empty_record = type && type_is_empty_record_p (type);
   must_stack = targetm.calls.must_pass_in_stack (TYPE_MODE (type), type);
-  size_tree = round_up (size_in_bytes (type), UNITS_PER_WORD);
+  size_tree = round_up (empty_record ? integer_zero_node : size_in_bytes (type),
+			UNITS_PER_WORD);
   gimplify_expr (&size_tree, pre_p, NULL, is_gimple_val, fb_rvalue);
 
   size_of_reg_args = NUM_ARGUMENT_REGISTERS * UNITS_PER_WORD;
@@ -1374,7 +1376,7 @@ xstormy16_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
   /* Arguments larger than a word might need to skip over some
      registers, since arguments are either passed entirely in
      registers or entirely on the stack.  */
-  size = PUSH_ROUNDING (int_size_in_bytes (type));
+  size = PUSH_ROUNDING (empty_record ? 0 : int_size_in_bytes (type));
   if (size > 2 || size < 0 || must_stack)
     {
       tree r, u;
diff --git a/gcc/config/tilegx/tilegx.c b/gcc/config/tilegx/tilegx.c
index d221062..cdacd36 100644
--- a/gcc/config/tilegx/tilegx.c
+++ b/gcc/config/tilegx/tilegx.c
@@ -396,8 +396,8 @@ tilegx_setup_incoming_varargs (cumulative_args_t cum,
   /* The caller has advanced CUM up to, but not beyond, the last named
      argument.  Advance a local copy of CUM past the last "real" named
      argument, to find out how many registers are left over.  */
-  targetm.calls.function_arg_advance (pack_cumulative_args (&local_cum),
-				      mode, type, true);
+  function_arg_advance (pack_cumulative_args (&local_cum), mode, type,
+			true);
   first_reg = local_cum;
 
   if (local_cum < TILEGX_NUM_ARG_REGS)
@@ -473,7 +473,8 @@ tilegx_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
   if (pass_by_reference_p)
     type = build_pointer_type (type);
 
-  size = int_size_in_bytes (type);
+  bool empty_record = type && type_is_empty_record_p (type);
+  size = empty_record ? 0 : int_size_in_bytes (type);
   rsize = ((size + UNITS_PER_WORD - 1) / UNITS_PER_WORD) * UNITS_PER_WORD;
 
   /* If the alignment of the type is greater than the default for a
diff --git a/gcc/config/tilepro/tilepro.c b/gcc/config/tilepro/tilepro.c
index 248b24e..1549c32 100644
--- a/gcc/config/tilepro/tilepro.c
+++ b/gcc/config/tilepro/tilepro.c
@@ -348,8 +348,8 @@ tilepro_setup_incoming_varargs (cumulative_args_t cum,
   /* The caller has advanced CUM up to, but not beyond, the last named
      argument.  Advance a local copy of CUM past the last "real" named
      argument, to find out how many registers are left over.  */
-  targetm.calls.function_arg_advance (pack_cumulative_args (&local_cum),
-				      mode, type, true);
+  function_arg_advance (pack_cumulative_args (&local_cum), mode, type,
+			true);
   first_reg = local_cum;
 
   if (local_cum < TILEPRO_NUM_ARG_REGS)
@@ -421,7 +421,8 @@ tilepro_gimplify_va_arg_expr (tree valist, tree type, gimple_seq * pre_p,
   if (pass_by_reference_p)
     type = build_pointer_type (type);
 
-  size = int_size_in_bytes (type);
+  bool empty_record = type && type_is_empty_record_p (type);
+  size = empty_record ? 0 : int_size_in_bytes (type);
   rsize = ((size + UNITS_PER_WORD - 1) / UNITS_PER_WORD) * UNITS_PER_WORD;
 
   /* If the alignment of the type is greater than the default for a
diff --git a/gcc/config/visium/visium.c b/gcc/config/visium/visium.c
index 0bf275c..4f252d0 100644
--- a/gcc/config/visium/visium.c
+++ b/gcc/config/visium/visium.c
@@ -1529,7 +1529,8 @@ visium_gimplify_va_arg (tree valist, tree type, gimple_seq *pre_p,
       return build_va_arg_indirect_ref (t);
     }
 
-  size = int_size_in_bytes (type);
+  bool empty_record = type && type_is_empty_record_p (type);
+  size = empty_record ? 0 : int_size_in_bytes (type);
   rsize = (size + UNITS_PER_WORD - 1) & -UNITS_PER_WORD;
   f_ovfl = TYPE_FIELDS (va_list_type_node);
   f_gbase = TREE_CHAIN (f_ovfl);
diff --git a/gcc/config/xtensa/xtensa.c b/gcc/config/xtensa/xtensa.c
index 0f58655..43911a1 100644
--- a/gcc/config/xtensa/xtensa.c
+++ b/gcc/config/xtensa/xtensa.c
@@ -3161,7 +3161,8 @@ xtensa_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
   ndx = build3 (COMPONENT_REF, TREE_TYPE (f_ndx), unshare_expr (valist),
 		f_ndx, NULL_TREE);
 
-  type_size = size_in_bytes (type);
+  bool empty_record = type && type_is_empty_record_p (type);
+  type_size = empty_record ? 0 : size_in_bytes (type);
   va_size = round_up (type_size, UNITS_PER_WORD);
   gimplify_expr (&va_size, pre_p, NULL, is_gimple_val, fb_rvalue);
 
-- 
2.5.0


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

* Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
  2015-12-09 18:53   ` H.J. Lu
  2015-12-09 21:14     ` H.J. Lu
@ 2015-12-09 21:31     ` Markus Trippelsdorf
  2015-12-10 11:24       ` Richard Biener
  1 sibling, 1 reply; 52+ messages in thread
From: Markus Trippelsdorf @ 2015-12-09 21:31 UTC (permalink / raw)
  To: H.J. Lu; +Cc: Richard Biener, Jason Merrill, GCC Patches

On 2015.12.09 at 10:53 -0800, H.J. Lu wrote:
> 
> Empty C++ class is a corner case which isn't covered in psABI nor C++ ABI.
> There is no mention of "empty record" in GCC documentation.  But there are
> plenty of "empty class" in gcc/cp.  This change affects all targets.  C++ ABI
> should specify how it should be passed.

There is a C++ ABI mailinglist, where you could discuss this issue:
http://sourcerytools.com/cgi-bin/mailman/listinfo/cxx-abi-dev

-- 
Markus

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

* Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
  2015-12-09 21:31     ` Markus Trippelsdorf
@ 2015-12-10 11:24       ` Richard Biener
  2015-12-11 23:52         ` H.J. Lu
  0 siblings, 1 reply; 52+ messages in thread
From: Richard Biener @ 2015-12-10 11:24 UTC (permalink / raw)
  To: Markus Trippelsdorf; +Cc: H.J. Lu, Jason Merrill, GCC Patches

On Wed, Dec 9, 2015 at 10:31 PM, Markus Trippelsdorf
<markus@trippelsdorf.de> wrote:
> On 2015.12.09 at 10:53 -0800, H.J. Lu wrote:
>>
>> Empty C++ class is a corner case which isn't covered in psABI nor C++ ABI.
>> There is no mention of "empty record" in GCC documentation.  But there are
>> plenty of "empty class" in gcc/cp.  This change affects all targets.  C++ ABI
>> should specify how it should be passed.
>
> There is a C++ ABI mailinglist, where you could discuss this issue:
> http://sourcerytools.com/cgi-bin/mailman/listinfo/cxx-abi-dev

Yep.  As long as the ABI doesn't state how to pass those I'd rather _not_ change
GCCs way.

Richard.

> --
> Markus

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

* Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
  2015-12-10 11:24       ` Richard Biener
@ 2015-12-11 23:52         ` H.J. Lu
  2015-12-12 14:51           ` Jason Merrill
  0 siblings, 1 reply; 52+ messages in thread
From: H.J. Lu @ 2015-12-11 23:52 UTC (permalink / raw)
  To: Richard Biener; +Cc: Markus Trippelsdorf, Jason Merrill, GCC Patches

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

On Thu, Dec 10, 2015 at 3:24 AM, Richard Biener
<richard.guenther@gmail.com> wrote:
> On Wed, Dec 9, 2015 at 10:31 PM, Markus Trippelsdorf
> <markus@trippelsdorf.de> wrote:
>> On 2015.12.09 at 10:53 -0800, H.J. Lu wrote:
>>>
>>> Empty C++ class is a corner case which isn't covered in psABI nor C++ ABI.
>>> There is no mention of "empty record" in GCC documentation.  But there are
>>> plenty of "empty class" in gcc/cp.  This change affects all targets.  C++ ABI
>>> should specify how it should be passed.
>>
>> There is a C++ ABI mailinglist, where you could discuss this issue:
>> http://sourcerytools.com/cgi-bin/mailman/listinfo/cxx-abi-dev
>
> Yep.  As long as the ABI doesn't state how to pass those I'd rather _not_ change
> GCCs way.

It is agreed that GCC is wrong on this:

http://sourcerytools.com/pipermail/cxx-abi-dev/2015-December/002876.html

Here is the updated patch.   I updated -WpsABI to warn empty
record which are passed in a variable argument list or aren't the last
arguments.   They are triggered in:

/export/build/gnu/gcc-x32/build-x86_64-linux/prev-x86_64-pc-linux-gnu/libstdc++-v3/include/bits/hashtable.h:1507:7:
note: the ABI of passing empty record has changed in GCC 6
/export/build/gnu/gcc-x32/build-x86_64-linux/prev-x86_64-pc-linux-gnu/libstdc++-v3/include/bits/hashtable_policy.h:901:67:
note: the ABI of passing empty record has changed in GCC 6
/export/gnu/import/git/sources/gcc/libstdc++-v3/src/c++11/cxx11-shim_facets.cc:238:4:
note: the ABI of passing empty record has changed in GCC 6
/export/gnu/import/git/sources/gcc/libstdc++-v3/src/c++11/cxx11-shim_facets.cc:266:26:
note: the ABI of passing empty record has changed in GCC 6
/export/gnu/import/git/sources/gcc/libstdc++-v3/src/c++11/cxx11-shim_facets.cc:273:4:
note: the ABI of passing empty record has changed in GCC 6
/export/gnu/import/git/sources/gcc/libstdc++-v3/src/c++11/cxx11-shim_facets.cc:289:61:
note: the ABI of passing empty record has changed in GCC 6
/export/gnu/import/git/sources/gcc/libstdc++-v3/src/c++11/cxx11-shim_facets.cc:296:11:
note: the ABI of passing empty record has changed in GCC 6
/export/gnu/import/git/sources/gcc/libstdc++-v3/src/c++11/cxx11-shim_facets.cc:304:11:
note: the ABI of passing empty record has changed in GCC 6
/export/gnu/import/git/sources/gcc/libstdc++-v3/src/c++11/cxx11-shim_facets.cc:312:11:
note: the ABI of passing empty record has changed in GCC 6
/export/gnu/import/git/sources/gcc/libstdc++-v3/src/c++11/cxx11-shim_facets.cc:320:11:
note: the ABI of passing empty record has changed in GCC 6
/export/gnu/import/git/sources/gcc/libstdc++-v3/src/c++11/cxx11-shim_facets.cc:328:11:
note: the ABI of passing empty record has changed in GCC 6
/export/gnu/import/git/sources/gcc/libstdc++-v3/src/c++11/cxx11-shim_facets.cc:341:4:
note: the ABI of passing empty record has changed in GCC 6
/export/gnu/import/git/sources/gcc/libstdc++-v3/src/c++11/cxx11-shim_facets.cc:375:19:
note: the ABI of passing empty record has changed in GCC 6
/export/gnu/import/git/sources/gcc/libstdc++-v3/src/c++11/cxx11-shim_facets.cc:375:4:
note: the ABI of passing empty record has changed in GCC 6
/export/gnu/import/git/sources/gcc/libstdc++-v3/src/c++11/cxx11-shim_facets.cc:390:19:
note: the ABI of passing empty record has changed in GCC 6
/export/gnu/import/git/sources/gcc/libstdc++-v3/src/c++11/cxx11-shim_facets.cc:390:4:
note: the ABI of passing empty record has changed in GCC 6
/export/gnu/import/git/sources/gcc/libstdc++-v3/src/c++11/cxx11-shim_facets.cc:415:16:
note: the ABI of passing empty record has changed in GCC 6
/export/gnu/import/git/sources/gcc/libstdc++-v3/src/c++11/cxx11-shim_facets.cc:425:12:
note: the ABI of passing empty record has changed in GCC 6
/export/gnu/import/git/sources/gcc/libstdc++-v3/src/c++11/cxx11-shim_facets.cc:442:29:
note: the ABI of passing empty record has changed in GCC 6
/export/gnu/import/git/sources/gcc/libstdc++-v3/src/c++11/cxx11-shim_facets.cc:449:4:
note: the ABI of passing empty record has changed in GCC 6
/export/gnu/import/git/sources/gcc/libstdc++-v3/src/c++11/cxx11-shim_facets.cc:457:4:
note: the ABI of passing empty record has changed in GCC 6
/export/gnu/import/git/sources/gcc/libstdc++-v3/src/c++11/cxx11-shim_facets.cc:500:5:
note: the ABI of passing empty record has changed in GCC 6
/export/gnu/import/git/sources/gcc/libstdc++-v3/src/c++11/cxx11-shim_facets.cc:529:5:
note: the ABI of passing empty record has changed in GCC 6
/export/gnu/import/git/sources/gcc/libstdc++-v3/src/c++11/cxx11-shim_facets.cc:547:5:
note: the ABI of passing empty record has changed in GCC 6
/export/gnu/import/git/sources/gcc/libstdc++-v3/src/c++11/cxx11-shim_facets.cc:569:5:
note: the ABI of passing empty record has changed in GCC 6
/export/gnu/import/git/sources/gcc/libstdc++-v3/src/c++11/cxx11-shim_facets.cc:617:5:
note: the ABI of passing empty record has changed in GCC 6
/export/gnu/import/git/sources/gcc/libstdc++-v3/src/c++11/cxx11-shim_facets.cc:637:5:
note: the ABI of passing empty record has changed in GCC 6
/export/gnu/import/git/sources/gcc/libstdc++-v3/src/c++11/cxx11-shim_facets.cc:657:5:
note: the ABI of passing empty record has changed in GCC 6
/export/gnu/import/git/sources/gcc/libstdc++-v3/src/c++11/cxx11-shim_facets.cc:673:5:
note: the ABI of passing empty record has changed in GCC 6
/export/gnu/import/git/sources/gcc/libstdc++-v3/src/c++11/cxx11-shim_facets.cc:686:5:
note: the ABI of passing empty record has changed in GCC 6
/export/gnu/import/git/sources/gcc/libstdc++-v3/src/c++11/cxx11-shim_facets.cc:722:5:
note: the ABI of passing empty record has changed in GCC 6
/export/gnu/import/git/sources/gcc/libstdc++-v3/src/c++11/cxx11-shim_facets.cc:753:5:
note: the ABI of passing empty record has changed in GCC 6


-- 
H.J.

[-- Attachment #2: 0001-Add-TYPE_EMPTY_RECORD-for-C-empty-class.patch --]
[-- Type: text/x-patch, Size: 36605 bytes --]

From 1a690882987e3fc940743d3368b8993acd9ec8a6 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 1/2] Add TYPE_EMPTY_RECORD for C++ empty class

Empty record should be returned and passed the same way in C and C++.
This patch overloads a bit, side_effects_flag, in tree_base for C++
empty class.  Middle-end and x86 backend are updated to ignore empty
records for parameter passing and function value return.  Other targets
may need similar changes.

get_ref_base_and_extent is changed to set bitsize to 0 for empty records
so that when ref_maybe_used_by_call_p_1 calls get_ref_base_and_extent to
get 0 as the maximum size on empty record.  Otherwise, find_tail_calls
won't perform tail call optimization for functions with empty record
parameters, as shown in g++.dg/pr60336-1.C and g++.dg/pr60336-2.C.

gcc/

	PR c++/60336
	PR middle-end/67239
	PR target/68355
	* calls.c (initialize_argument_information): Warn empty record
	if they are used in a variable argument list or aren't the last
	arguments.  Replace targetm.calls.function_arg,
	targetm.calls.function_incoming_arg and
	targetm.calls.function_arg_advance with function_arg,
	function_incoming_arg and function_arg_advance.
	(expand_call): Replace targetm.calls.function_arg,
	targetm.calls.function_incoming_arg and
	targetm.calls.function_arg_advance with function_arg,
	function_incoming_arg and function_arg_advance.
	(emit_library_call_value_1): Likewise.
	(store_one_arg): Use 0 for empty record size.  Don't
	push 0 size argument onto stack.
	(must_pass_in_stack_var_size_or_pad): Return false for empty
	record.
	* dse.c (get_call_args): Replace targetm.calls.function_arg
	and targetm.calls.function_arg_advance with function_arg and
	function_arg_advance.
	* expr.c (block_move_libcall_safe_for_call_parm): Likewise.
	* function.c (aggregate_value_p): Replace
	targetm.calls.return_in_memory with return_in_memory.
	(assign_parm_data_all): Add warn_empty_record.
	(assign_parms_augmented_arg_list): Set warn_empty_record if
	empty records are used in a variable argument list or aren't
	the last arguments.
	(assign_parm_find_entry_rtl): Warn empty record if
	warn_empty_record is set.  Replace
	targetm.calls.function_incoming_arg with function_incoming_arg.
	(assign_parms): Only warn empty record once.  Replace
	targetm.calls.function_arg_advance with function_arg_advance.
	(gimplify_parameters): Replace targetm.calls.function_arg_advance
	with function_arg_advance.
	(locate_and_pad_parm): Use 0 for empty record size.
	(warn_empty_record): New function.
	(function_arg_advance): New wrapper function.
	(function_arg): Likewise.
	(function_incoming_arg): Likewise.
	(return_in_memory): Likewise.
	* lto-streamer-out.c (hash_tree): Call hstate.add_flag with
	TYPE_EMPTY_RECORD for types.
	* print-tree.c (print_node): Also handle TYPE_EMPTY_RECORD.
	* ubsan.c (ubsan_type_descriptor): Likewise.
	* target.h (function_arg_advance): New prototype.
	(function_arg): Likewise.
	(function_incoming_arg): Likewise.
	(return_in_memory): Likewise.
	* targhooks.c (std_gimplify_va_arg_expr): Use 0 for empty record
	size.
	* tree-dfa.c (get_ref_base_and_extent): Likewise.
	* tree-core.h (tree_base): Mention TYPE_EMPTY_RECORD in comments
	for side_effects_flag.
	* tree-streamer-in.c (unpack_ts_base_value_fields): Stream in
	TYPE_EMPTY_RECORD for types.
	* tree-streamer-out.c (pack_ts_base_value_fields): Stream out
	TYPE_EMPTY_RECORD for types.
	* tree.h (TYPE_EMPTY_RECORD): New.
	(type_is_empty_record_p): New static inline function.
	* var-tracking.c (prepare_call_arguments): Replace
	targetm.calls.function_arg and targetm.calls.function_arg_advance
	with function_arg and function_arg_advance.
	* config/i386/i386.c (ix86_gimplify_va_arg): Use 0 for empty
	record size.

gcc/c

	PR c++/60336
	PR middle-end/67239
	PR target/68355
	* c-aux-info.c (gen_type): Add TYPE_EMPTY_RECORD check.

gcc/cp/

	PR c++/60336
	PR middle-end/67239
	PR target/68355
	* class.c (finish_struct_1): Set TYPE_EMPTY_RECORD with return
	value from is_really_empty_class ().

gcc/lto/

	PR c++/60336
	PR middle-end/67239
	PR target/68355
	* lto.c (compare_tree_sccs_1): Call compare_values with
	TYPE_EMPTY_RECORD for types.

gcc/testsuite/

	PR c++/60336
	PR middle-end/67239
	PR target/68355
	* g++.dg/abi/empty12.C: New test.
	* g++.dg/abi/empty12.h: Likewise.
	* g++.dg/abi/empty12a.c: Likewise.
	* g++.dg/pr60336-1.C: Likewise.
	* g++.dg/pr60336-2.C: Likewise.
	* g++.dg/pr60336-3.C: Likewise.
	* g++.dg/pr68355.C: Likewise.
---
 gcc/c/c-aux-info.c                  |   2 +
 gcc/calls.c                         |  96 ++++++++++++++++++----------
 gcc/config/i386/i386.c              |   3 +-
 gcc/cp/class.c                      |   2 +
 gcc/dse.c                           |   4 +-
 gcc/expr.c                          |   5 +-
 gcc/function.c                      | 122 +++++++++++++++++++++++++++++++-----
 gcc/lto-streamer-out.c              |   2 +
 gcc/lto/lto.c                       |   2 +
 gcc/print-tree.c                    |   3 +
 gcc/target.h                        |   8 +++
 gcc/targhooks.c                     |   5 +-
 gcc/testsuite/g++.dg/abi/empty12.C  |  17 +++++
 gcc/testsuite/g++.dg/abi/empty12.h  |   9 +++
 gcc/testsuite/g++.dg/abi/empty12a.c |   6 ++
 gcc/testsuite/g++.dg/pr60336-1.C    |  17 +++++
 gcc/testsuite/g++.dg/pr60336-2.C    |  28 +++++++++
 gcc/testsuite/g++.dg/pr60336-3.C    |  15 +++++
 gcc/testsuite/g++.dg/pr68355.C      |  24 +++++++
 gcc/tree-core.h                     |   3 +
 gcc/tree-dfa.c                      |   2 +
 gcc/tree-streamer-in.c              |   5 +-
 gcc/tree-streamer-out.c             |   5 +-
 gcc/tree.h                          |  12 ++++
 gcc/ubsan.c                         |   3 +-
 gcc/var-tracking.c                  |  18 +++---
 26 files changed, 351 insertions(+), 67 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/abi/empty12.C
 create mode 100644 gcc/testsuite/g++.dg/abi/empty12.h
 create mode 100644 gcc/testsuite/g++.dg/abi/empty12a.c
 create mode 100644 gcc/testsuite/g++.dg/pr60336-1.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/pr68355.C

diff --git a/gcc/c/c-aux-info.c b/gcc/c/c-aux-info.c
index 79d9851..93b001b 100644
--- a/gcc/c/c-aux-info.c
+++ b/gcc/c/c-aux-info.c
@@ -433,6 +433,8 @@ gen_type (const char *ret_val, tree t, formals_style style)
     ret_val = concat ("volatile ", ret_val, NULL);
   if (TYPE_RESTRICT (t))
     ret_val = concat ("restrict ", ret_val, NULL);
+  if (TYPE_EMPTY_RECORD (t))
+    ret_val = concat ("empty-record ", ret_val, NULL);
   return ret_val;
 }
 
diff --git a/gcc/calls.c b/gcc/calls.c
index 1eb4ec7..3f3ae02b 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -1152,7 +1152,10 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
   bitmap_obstack_initialize (NULL);
 
   /* In this loop, we consider args in the order they are written.
-     We fill up ARGS from the back.  */
+     We fill up ARGS from the back.  Warn empty record if they are used
+     in a variable argument list or they aren't the last arguments.  */
+  bool seen_empty_record = false;
+  bool warn_empty_record = stdarg_p (fndecl ? TREE_TYPE (fndecl) : fntype);
 
   i = num_actuals - 1;
   {
@@ -1185,6 +1188,15 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
       {
 	tree argtype = TREE_TYPE (arg);
 
+	if (!warn_empty_record)
+	  {
+	    if (argtype != error_mark_node
+		&& type_is_empty_record_p (argtype))
+	      seen_empty_record = true;
+	    else if (seen_empty_record)
+	      warn_empty_record = true;
+	  }
+
 	/* Remember last param with pointer and associate it
 	   with following pointer bounds.  */
 	if (CALL_WITH_BOUNDS_P (exp)
@@ -1399,8 +1411,13 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
       args[i].unsignedp = unsignedp;
       args[i].mode = mode;
 
-      args[i].reg = targetm.calls.function_arg (args_so_far, mode, type,
-						argpos < n_named_args);
+      args[i].reg = function_arg (args_so_far, mode, type,
+				  argpos < n_named_args,
+				  warn_empty_record);
+
+      /* Only warn empty record once.  */
+      if (type_is_empty_record_p (type))
+	warn_empty_record = false;
 
       if (args[i].reg && CONST_INT_P (args[i].reg))
 	{
@@ -1413,8 +1430,8 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
 	 arguments have to go into the incoming registers.  */
       if (targetm.calls.function_incoming_arg != targetm.calls.function_arg)
 	args[i].tail_call_reg
-	  = targetm.calls.function_incoming_arg (args_so_far, mode, type,
-						 argpos < n_named_args);
+	  = function_incoming_arg (args_so_far, mode, type,
+				   argpos < n_named_args);
       else
 	args[i].tail_call_reg = args[i].reg;
 
@@ -1475,8 +1492,8 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
       /* Increment ARGS_SO_FAR, which has info about which arg-registers
 	 have been used, etc.  */
 
-      targetm.calls.function_arg_advance (args_so_far, TYPE_MODE (type),
-					  type, argpos < n_named_args);
+      function_arg_advance (args_so_far, TYPE_MODE (type), type,
+			    argpos < n_named_args);
     }
 }
 
@@ -3328,14 +3345,11 @@ expand_call (tree exp, rtx target, int ignore)
       /* Set up next argument register.  For sibling calls on machines
 	 with register windows this should be the incoming register.  */
       if (pass == 0)
-	next_arg_reg = targetm.calls.function_incoming_arg (args_so_far,
-							    VOIDmode,
-							    void_type_node,
-							    true);
+	next_arg_reg = function_incoming_arg (args_so_far, VOIDmode,
+					      void_type_node, true);
       else
-	next_arg_reg = targetm.calls.function_arg (args_so_far,
-						   VOIDmode, void_type_node,
-						   true);
+	next_arg_reg = function_arg (args_so_far, VOIDmode,
+				     void_type_node, true);
 
       if (pass == 1 && (return_flags & ERF_RETURNS_ARG))
 	{
@@ -3947,8 +3961,8 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
       argvec[count].mode = Pmode;
       argvec[count].partial = 0;
 
-      argvec[count].reg = targetm.calls.function_arg (args_so_far,
-						      Pmode, NULL_TREE, true);
+      argvec[count].reg = function_arg (args_so_far, Pmode, NULL_TREE,
+					true);
       gcc_assert (targetm.calls.arg_partial_bytes (args_so_far, Pmode,
 						   NULL_TREE, 1) == 0);
 
@@ -3965,7 +3979,7 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
 	  || reg_parm_stack_space > 0)
 	args_size.constant += argvec[count].locate.size.constant;
 
-      targetm.calls.function_arg_advance (args_so_far, Pmode, (tree) 0, true);
+      function_arg_advance (args_so_far, Pmode, (tree) 0, true);
 
       count++;
     }
@@ -4030,8 +4044,7 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
       mode = promote_function_mode (NULL_TREE, mode, &unsigned_p, NULL_TREE, 0);
       argvec[count].mode = mode;
       argvec[count].value = convert_modes (mode, GET_MODE (val), val, unsigned_p);
-      argvec[count].reg = targetm.calls.function_arg (args_so_far, mode,
-						      NULL_TREE, true);
+      argvec[count].reg = function_arg (args_so_far, mode, NULL_TREE, true);
 
       argvec[count].partial
 	= targetm.calls.arg_partial_bytes (args_so_far, mode, NULL_TREE, 1);
@@ -4060,7 +4073,7 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
 			     GET_MODE_SIZE (mode) <= UNITS_PER_WORD);
 #endif
 
-      targetm.calls.function_arg_advance (args_so_far, mode, (tree) 0, true);
+      function_arg_advance (args_so_far, mode, (tree) 0, true);
     }
 
   /* If this machine requires an external definition for library
@@ -4407,8 +4420,7 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
 	       build_function_type (tfom, NULL_TREE),
 	       original_args_size.constant, args_size.constant,
 	       struct_value_size,
-	       targetm.calls.function_arg (args_so_far,
-					   VOIDmode, void_type_node, true),
+	       function_arg (args_so_far, VOIDmode, void_type_node, true),
 	       valreg,
 	       old_inhibit_defer_pop + 1, call_fusage, flags, args_so_far);
 
@@ -4843,7 +4855,10 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
 	 Note that in C the default argument promotions
 	 will prevent such mismatches.  */
 
-      size = GET_MODE_SIZE (arg->mode);
+      if (type_is_empty_record_p (TREE_TYPE (pval)))
+	size = 0;
+      else
+	size = GET_MODE_SIZE (arg->mode);
       /* Compute how much space the push instruction will push.
 	 On many machines, pushing a byte will advance the stack
 	 pointer by a halfword.  */
@@ -4873,10 +4888,14 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
 
       /* This isn't already where we want it on the stack, so put it there.
 	 This can either be done with push or copy insns.  */
-      if (!emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), NULL_RTX,
-		      parm_align, partial, reg, used - size, argblock,
-		      ARGS_SIZE_RTX (arg->locate.offset), reg_parm_stack_space,
-		      ARGS_SIZE_RTX (arg->locate.alignment_pad), true))
+      if (used
+	  && !emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval),
+			      NULL_RTX, parm_align, partial, reg,
+			      used - size, argblock,
+			      ARGS_SIZE_RTX (arg->locate.offset),
+			      reg_parm_stack_space,
+			      ARGS_SIZE_RTX (arg->locate.alignment_pad),
+			      true))
 	sibcall_failure = 1;
 
       /* Unless this is a partially-in-register argument, the argument is now
@@ -4908,10 +4927,15 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
 	{
 	  /* PUSH_ROUNDING has no effect on us, because emit_push_insn
 	     for BLKmode is careful to avoid it.  */
+	  bool empty_record = type_is_empty_record_p (TREE_TYPE (pval));
 	  excess = (arg->locate.size.constant
-		    - int_size_in_bytes (TREE_TYPE (pval))
+		    - (empty_record
+		       ? 0
+		       : int_size_in_bytes (TREE_TYPE (pval)))
 		    + partial);
-	  size_rtx = expand_expr (size_in_bytes (TREE_TYPE (pval)),
+	  size_rtx = expand_expr ((empty_record
+				   ? size_zero_node
+				   : size_in_bytes (TREE_TYPE (pval))),
 				  NULL_RTX, TYPE_MODE (sizetype),
 				  EXPAND_NORMAL);
 	}
@@ -4986,10 +5010,13 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
 	    }
 	}
 
-      emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), size_rtx,
-		      parm_align, partial, reg, excess, argblock,
-		      ARGS_SIZE_RTX (arg->locate.offset), reg_parm_stack_space,
-		      ARGS_SIZE_RTX (arg->locate.alignment_pad), false);
+      if (!CONST_INT_P (size_rtx) || INTVAL (size_rtx) != 0)
+	emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval),
+			size_rtx, parm_align, partial, reg, excess,
+			argblock, ARGS_SIZE_RTX (arg->locate.offset),
+			reg_parm_stack_space,
+			ARGS_SIZE_RTX (arg->locate.alignment_pad),
+			false);
 
       /* Unless this is a partially-in-register argument, the argument is now
 	 in the stack.
@@ -5067,6 +5094,9 @@ must_pass_in_stack_var_size_or_pad (machine_mode mode, const_tree type)
   if (TREE_ADDRESSABLE (type))
     return true;
 
+  if (type_is_empty_record_p (type))
+    return false;
+
   /* If the padding and mode of the type is such that a copy into
      a register would put it into the wrong part of the register.  */
   if (mode == BLKmode
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index d30fbff..308d9a4e 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -10292,7 +10292,8 @@ ix86_gimplify_va_arg (tree valist, tree type, gimple_seq *pre_p,
   indirect_p = pass_by_reference (NULL, TYPE_MODE (type), type, false);
   if (indirect_p)
     type = build_pointer_type (type);
-  size = int_size_in_bytes (type);
+  bool empty_record = type && type_is_empty_record_p (type);
+  size = empty_record ? 0 : int_size_in_bytes (type);
   rsize = CEIL (size, UNITS_PER_WORD);
 
   nat_mode = type_natural_mode (type, NULL, false);
diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index 216a301..d97aae6 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -6802,6 +6802,8 @@ finish_struct_1 (tree t)
 	  TYPE_TRANSPARENT_AGGR (t) = 0;
 	}
     }
+
+  TYPE_EMPTY_RECORD (t) = is_really_empty_class (t);
 }
 
 /* Insert FIELDS into T for the sorted case if the FIELDS count is
diff --git a/gcc/dse.c b/gcc/dse.c
index 35eef71..594106f 100644
--- a/gcc/dse.c
+++ b/gcc/dse.c
@@ -2366,7 +2366,7 @@ get_call_args (rtx call_insn, tree fn, rtx *args, int nargs)
     {
       machine_mode mode = TYPE_MODE (TREE_VALUE (arg));
       rtx reg, link, tmp;
-      reg = targetm.calls.function_arg (args_so_far, mode, NULL_TREE, true);
+      reg = function_arg (args_so_far, mode, NULL_TREE, true);
       if (!reg || !REG_P (reg) || GET_MODE (reg) != mode
 	  || GET_MODE_CLASS (mode) != MODE_INT)
 	return false;
@@ -2400,7 +2400,7 @@ get_call_args (rtx call_insn, tree fn, rtx *args, int nargs)
       if (tmp)
 	args[idx] = tmp;
 
-      targetm.calls.function_arg_advance (args_so_far, mode, NULL_TREE, true);
+      function_arg_advance (args_so_far, mode, NULL_TREE, true);
     }
   if (arg != void_list_node || idx != nargs)
     return false;
diff --git a/gcc/expr.c b/gcc/expr.c
index bd43dc4..384581a 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -1198,13 +1198,12 @@ block_move_libcall_safe_for_call_parm (void)
     for ( ; arg != void_list_node ; arg = TREE_CHAIN (arg))
       {
 	machine_mode mode = TYPE_MODE (TREE_VALUE (arg));
-	rtx tmp = targetm.calls.function_arg (args_so_far, mode,
-					      NULL_TREE, true);
+	rtx tmp = function_arg (args_so_far, mode, NULL_TREE, true);
 	if (!tmp || !REG_P (tmp))
 	  return false;
 	if (targetm.calls.arg_partial_bytes (args_so_far, mode, NULL, 1))
 	  return false;
-	targetm.calls.function_arg_advance (args_so_far, mode,
+	function_arg_advance (args_so_far, mode,
 					    NULL_TREE, true);
       }
   }
diff --git a/gcc/function.c b/gcc/function.c
index b513ead..9040a92 100644
--- a/gcc/function.c
+++ b/gcc/function.c
@@ -2074,7 +2074,7 @@ aggregate_value_p (const_tree exp, const_tree fntype)
   if (flag_pcc_struct_return && AGGREGATE_TYPE_P (type))
     return 1;
 
-  if (targetm.calls.return_in_memory (type, fntype))
+  if (return_in_memory (type, fntype))
     return 1;
 
   /* Make sure we have suitable call-clobbered regs to return
@@ -2243,6 +2243,7 @@ struct assign_parm_data_all
   HOST_WIDE_INT pretend_args_size;
   HOST_WIDE_INT extra_pretend_bytes;
   int reg_parm_stack_space;
+  bool warn_empty_record;
 };
 
 struct assign_parm_data_one
@@ -2408,6 +2409,27 @@ assign_parms_augmented_arg_list (struct assign_parm_data_all *all)
   if (targetm.calls.split_complex_arg)
     split_complex_args (&fnargs);
 
+  /* Warn empty record if they are used in a variable argument list or
+     they aren't the last arguments.  */
+  bool warn_empty_record = stdarg_p (fntype);
+  if (!warn_empty_record)
+    {
+      unsigned int i;
+      bool seen_empty_record = false;
+      FOR_EACH_VEC_ELT (fnargs, i, arg)
+	{
+	  tree type = TREE_TYPE (arg);
+	  if (type != error_mark_node && type_is_empty_record_p (type))
+	    seen_empty_record = true;
+	  else if (seen_empty_record)
+	    {
+	      warn_empty_record = true;
+	      break;
+	    }
+	}
+    }
+  all->warn_empty_record = warn_empty_record;
+
   return fnargs;
 }
 
@@ -2524,10 +2546,11 @@ assign_parm_find_entry_rtl (struct assign_parm_data_all *all,
       return;
     }
 
-  entry_parm = targetm.calls.function_incoming_arg (all->args_so_far,
-						    data->promoted_mode,
-						    data->passed_type,
-						    data->named_arg);
+  entry_parm = function_incoming_arg (all->args_so_far,
+				      data->promoted_mode,
+				      data->passed_type,
+				      data->named_arg,
+				      all->warn_empty_record);
 
   if (entry_parm == 0)
     data->promoted_mode = data->passed_mode;
@@ -2551,9 +2574,9 @@ assign_parm_find_entry_rtl (struct assign_parm_data_all *all,
       if (targetm.calls.pretend_outgoing_varargs_named (all->args_so_far))
 	{
 	  rtx tem;
-	  tem = targetm.calls.function_incoming_arg (all->args_so_far,
-						     data->promoted_mode,
-						     data->passed_type, true);
+	  tem = function_incoming_arg (all->args_so_far,
+				       data->promoted_mode,
+				       data->passed_type, true);
 	  in_regs = tem != NULL;
 	}
     }
@@ -3715,6 +3738,10 @@ assign_parms (tree fndecl)
       /* Find out where the parameter arrives in this function.  */
       assign_parm_find_entry_rtl (&all, &data);
 
+      /* Only warn empty record once.  */
+      if (type_is_empty_record_p (data.passed_type))
+	all.warn_empty_record = false;
+
       /* Find out where stack space for this parameter might be.  */
       if (assign_parm_is_stack_parm (&all, &data))
 	{
@@ -3791,8 +3818,8 @@ assign_parms (tree fndecl)
 	}
 
       /* Update info on where next arg arrives in registers.  */
-      targetm.calls.function_arg_advance (all.args_so_far, data.promoted_mode,
-					  data.passed_type, data.named_arg);
+      function_arg_advance (all.args_so_far, data.promoted_mode,
+			    data.passed_type, data.named_arg);
 
       if (POINTER_BOUNDS_TYPE_P (data.passed_type))
 	bound_no++;
@@ -3988,8 +4015,8 @@ gimplify_parameters (void)
 	continue;
 
       /* Update info on where next arg arrives in registers.  */
-      targetm.calls.function_arg_advance (all.args_so_far, data.promoted_mode,
-					  data.passed_type, data.named_arg);
+      function_arg_advance (all.args_so_far, data.promoted_mode,
+			    data.passed_type, data.named_arg);
 
       /* ??? Once upon a time variable_size stuffed parameter list
 	 SAVE_EXPRs (amongst others) onto a pending sizes list.  This
@@ -4132,8 +4159,11 @@ locate_and_pad_parm (machine_mode passed_mode, tree type, int in_regs,
 
   part_size_in_regs = (reg_parm_stack_space == 0 ? partial : 0);
 
-  sizetree
-    = type ? size_in_bytes (type) : size_int (GET_MODE_SIZE (passed_mode));
+  if (type)
+    sizetree = (type_is_empty_record_p (type)
+		? size_zero_node : size_in_bytes (type));
+  else
+    sizetree = size_int (GET_MODE_SIZE (passed_mode));
   where_pad = FUNCTION_ARG_PADDING (passed_mode, type);
   boundary = targetm.calls.function_arg_boundary (passed_mode, type);
   round_boundary = targetm.calls.function_arg_round_boundary (passed_mode,
@@ -6862,5 +6892,69 @@ make_pass_match_asm_constraints (gcc::context *ctxt)
   return new pass_match_asm_constraints (ctxt);
 }
 
+static void
+warn_empty_record (void)
+{
+  if (warn_psabi)
+    inform (input_location, "the ABI of passing empty record has"
+	    " changed in GCC 6");
+}
+
+/* Wrapper for targetm.calls.function_arg_advance.  */
+
+void
+function_arg_advance (cumulative_args_t ca, machine_mode mode,
+		      const_tree type, bool named)
+{
+  if (type && type_is_empty_record_p (type))
+    return;
+
+  targetm.calls.function_arg_advance (ca, mode, type, named);
+}
+
+/* Wrapper for targetm.calls.function_arg.  */
+
+rtx
+function_arg (cumulative_args_t ca, machine_mode mode, const_tree type,
+	      bool named, bool warn_empty_record_p)
+{
+  if (type && type_is_empty_record_p (type))
+    {
+      if (warn_empty_record_p)
+	warn_empty_record ();
+      return NULL;
+    }
+
+  return targetm.calls.function_arg (ca, mode, type, named);
+}
+
+/* Wrapper for targetm.calls.function_incoming_arg.  */
+
+rtx
+function_incoming_arg (cumulative_args_t ca, machine_mode mode,
+		       const_tree type, bool named,
+		       bool warn_empty_record_p)
+{
+  if (type && type_is_empty_record_p (type))
+    {
+      if (warn_empty_record_p)
+	warn_empty_record ();
+      return NULL;
+    }
+
+  return targetm.calls.function_incoming_arg (ca, mode, type, named);
+}
+
+/* Wrapper for targetm.calls.return_in_memory.  */
+
+bool
+return_in_memory (const_tree type, const_tree fntype)
+{
+  if (type && type_is_empty_record_p (type))
+    return false;
+
+  return targetm.calls.return_in_memory (type, fntype);
+}
+
 
 #include "gt-function.h"
diff --git a/gcc/lto-streamer-out.c b/gcc/lto-streamer-out.c
index a874846..509f0b3 100644
--- a/gcc/lto-streamer-out.c
+++ b/gcc/lto-streamer-out.c
@@ -944,6 +944,8 @@ hash_tree (struct streamer_tree_cache_d *cache, hash_map<tree, hashval_t> *map,
       hstate.add_flag (TREE_READONLY (t));
       hstate.add_flag (TREE_PUBLIC (t));
     }
+  else
+    hstate.add_flag (TYPE_EMPTY_RECORD (t));
   hstate.add_flag (TREE_ADDRESSABLE (t));
   hstate.add_flag (TREE_THIS_VOLATILE (t));
   if (DECL_P (t))
diff --git a/gcc/lto/lto.c b/gcc/lto/lto.c
index 90712b4..6755132 100644
--- a/gcc/lto/lto.c
+++ b/gcc/lto/lto.c
@@ -1002,6 +1002,8 @@ compare_tree_sccs_1 (tree t1, tree t2, tree **map)
       compare_values (TREE_READONLY);
       compare_values (TREE_PUBLIC);
     }
+  else
+    compare_values (TYPE_EMPTY_RECORD);
   compare_values (TREE_ADDRESSABLE);
   compare_values (TREE_THIS_VOLATILE);
   if (DECL_P (t1))
diff --git a/gcc/print-tree.c b/gcc/print-tree.c
index cb0f1fd..bb489ff 100644
--- a/gcc/print-tree.c
+++ b/gcc/print-tree.c
@@ -587,6 +587,9 @@ print_node (FILE *file, const char *prefix, tree node, int indent)
       if (TYPE_RESTRICT (node))
 	fputs (" restrict", file);
 
+      if (TYPE_EMPTY_RECORD (node))
+	fputs (" empty-record", file);
+
       if (TYPE_LANG_FLAG_0 (node))
 	fputs (" type_0", file);
       if (TYPE_LANG_FLAG_1 (node))
diff --git a/gcc/target.h b/gcc/target.h
index ffc4d6a..4f25a54 100644
--- a/gcc/target.h
+++ b/gcc/target.h
@@ -104,6 +104,14 @@ extern bool target_default_pointer_address_modes_p (void);
    behaviour.  */
 extern unsigned int get_move_ratio (bool);
 
+extern void function_arg_advance (cumulative_args_t, machine_mode,
+				  const_tree, bool);
+extern rtx function_arg (cumulative_args_t, machine_mode, const_tree,
+			 bool, bool = false);
+extern rtx function_incoming_arg (cumulative_args_t, machine_mode,
+				  const_tree, bool, bool = false);
+extern bool return_in_memory (const_tree, const_tree);
+
 struct stdarg_info;
 struct spec_info_def;
 struct hard_reg_set_container;
diff --git a/gcc/targhooks.c b/gcc/targhooks.c
index dcf0863..7a8d1e8 100644
--- a/gcc/targhooks.c
+++ b/gcc/targhooks.c
@@ -1827,9 +1827,12 @@ std_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
   /* Hoist the valist value into a temporary for the moment.  */
   valist_tmp = get_initialized_tmp_var (valist, pre_p, NULL);
 
+  bool empty_record = type_is_empty_record_p (type);
+
   /* va_list pointer is aligned to PARM_BOUNDARY.  If argument actually
      requires greater alignment, we must perform dynamic alignment.  */
   if (boundary > align
+      && !empty_record
       && !integer_zerop (TYPE_SIZE (type)))
     {
       t = build2 (MODIFY_EXPR, TREE_TYPE (valist), valist_tmp,
@@ -1856,7 +1859,7 @@ std_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
     }
 
   /* Compute the rounded size of the type.  */
-  type_size = size_in_bytes (type);
+  type_size = empty_record ? size_zero_node : size_in_bytes (type);
   rounded_size = round_up (type_size, align);
 
   /* Reduce rounded_size so it's sharable with the postqueue.  */
diff --git a/gcc/testsuite/g++.dg/abi/empty12.C b/gcc/testsuite/g++.dg/abi/empty12.C
new file mode 100644
index 0000000..db2fd24
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/empty12.C
@@ -0,0 +1,17 @@
+// PR c++/60336
+// { dg-do run }
+// { dg-options "-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-message "note: the ABI of passing empty record has changed in GCC 6" }
+  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/pr60336-1.C b/gcc/testsuite/g++.dg/pr60336-1.C
new file mode 100644
index 0000000..af08638
--- /dev/null
+++ b/gcc/testsuite/g++.dg/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/pr60336-2.C b/gcc/testsuite/g++.dg/pr60336-2.C
new file mode 100644
index 0000000..7ab7d23
--- /dev/null
+++ b/gcc/testsuite/g++.dg/pr60336-2.C
@@ -0,0 +1,28 @@
+// { dg-do run }
+// { dg-options "-O2" }
+
+#include <stdarg.h>
+
+struct dummy { struct{}__attribute__((aligned (4))) a[7]; };
+
+void
+test (struct dummy a, ...) // { dg-message "note: the ABI of passing empty record has changed in GCC 6" }
+{
+  va_list va_arglist;
+  int i;
+
+  va_start (va_arglist, a);
+  i = va_arg (va_arglist, int);
+  if (i != 0x10)
+    __builtin_abort ();
+  va_end (va_arglist);
+}
+
+struct dummy a0;
+
+int
+main ()
+{
+  test (a0, 0x10); // { dg-message "note: the ABI of passing empty record has changed in GCC 6" }
+  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..3afdc8d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/pr60336-3.C
@@ -0,0 +1,15 @@
+// { dg-do compile }
+// { dg-options "-O2" }
+
+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); // { dg-message "note: the ABI of passing empty record has changed in GCC 6" }
+  test2 (a0); // { dg-message "note: the ABI of passing empty record has changed in GCC 6" }
+}
diff --git a/gcc/testsuite/g++.dg/pr68355.C b/gcc/testsuite/g++.dg/pr68355.C
new file mode 100644
index 0000000..1354fc4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/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-*-* } } }
diff --git a/gcc/tree-core.h b/gcc/tree-core.h
index 9cc64d9..67bffa4 100644
--- a/gcc/tree-core.h
+++ b/gcc/tree-core.h
@@ -1086,6 +1086,9 @@ struct GTY(()) tree_base {
        FORCED_LABEL in
            LABEL_DECL
 
+       TYPE_EMPTY_RECORD in
+           all types
+
    volatile_flag:
 
        TREE_THIS_VOLATILE in
diff --git a/gcc/tree-dfa.c b/gcc/tree-dfa.c
index bb5cd49..1634ed6 100644
--- a/gcc/tree-dfa.c
+++ b/gcc/tree-dfa.c
@@ -394,6 +394,8 @@ get_ref_base_and_extent (tree exp, HOST_WIDE_INT *poffset,
       machine_mode mode = TYPE_MODE (TREE_TYPE (exp));
       if (mode == BLKmode)
 	size_tree = TYPE_SIZE (TREE_TYPE (exp));
+      else if (type_is_empty_record_p (TREE_TYPE (exp)))
+	bitsize = 0;
       else
 	bitsize = int (GET_MODE_PRECISION (mode));
     }
diff --git a/gcc/tree-streamer-in.c b/gcc/tree-streamer-in.c
index 3162d1a..2aee7ad 100644
--- a/gcc/tree-streamer-in.c
+++ b/gcc/tree-streamer-in.c
@@ -112,7 +112,10 @@ unpack_ts_base_value_fields (struct bitpack_d *bp, tree expr)
       TREE_PUBLIC (expr) = (unsigned) bp_unpack_value (bp, 1);
     }
   else
-    bp_unpack_value (bp, 4);
+    {
+      TYPE_EMPTY_RECORD (expr) = (unsigned) bp_unpack_value (bp, 1);
+      bp_unpack_value (bp, 3);
+    }
   TREE_ADDRESSABLE (expr) = (unsigned) bp_unpack_value (bp, 1);
   TREE_THIS_VOLATILE (expr) = (unsigned) bp_unpack_value (bp, 1);
   if (DECL_P (expr))
diff --git a/gcc/tree-streamer-out.c b/gcc/tree-streamer-out.c
index bfd0644..4306fcc 100644
--- a/gcc/tree-streamer-out.c
+++ b/gcc/tree-streamer-out.c
@@ -83,7 +83,10 @@ pack_ts_base_value_fields (struct bitpack_d *bp, tree expr)
       bp_pack_value (bp, TREE_PUBLIC (expr), 1);
     }
   else
-    bp_pack_value (bp, 0, 4);
+    {
+      bp_pack_value (bp, TYPE_EMPTY_RECORD (expr), 1);
+      bp_pack_value (bp, 0, 3);
+    }
   bp_pack_value (bp, TREE_ADDRESSABLE (expr), 1);
   bp_pack_value (bp, TREE_THIS_VOLATILE (expr), 1);
   if (DECL_P (expr))
diff --git a/gcc/tree.h b/gcc/tree.h
index aef825d..9b13d5c 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -766,6 +766,10 @@ extern void omp_clause_range_check_failed (const_tree, const char *, int,
    computed gotos.  */
 #define FORCED_LABEL(NODE) (LABEL_DECL_CHECK (NODE)->base.side_effects_flag)
 
+/* Nonzero in a type considered an empty record.  */
+#define TYPE_EMPTY_RECORD(NODE) \
+  (TYPE_CHECK (NODE)->base.side_effects_flag)
+
 /* Nonzero means this expression is volatile in the C sense:
    its address should be of type `volatile WHATEVER *'.
    In other words, the declared item is volatile qualified.
@@ -5379,6 +5383,14 @@ get_finish (location_t loc)
   return get_range_from_loc (line_table, loc).m_finish;
 }
 
+/* Return true if type T is an empty record.  */
+
+static inline bool
+type_is_empty_record_p (const_tree t)
+{
+  return TYPE_EMPTY_RECORD (TYPE_MAIN_VARIANT (t));
+}
+
 extern location_t set_block (location_t loc, tree block);
 
 extern void gt_ggc_mx (tree &);
diff --git a/gcc/ubsan.c b/gcc/ubsan.c
index 6fc6233..6dafc90 100644
--- a/gcc/ubsan.c
+++ b/gcc/ubsan.c
@@ -379,10 +379,11 @@ ubsan_type_descriptor (tree type, enum ubsan_print_style pstyle)
 
   if (pstyle == UBSAN_PRINT_POINTER)
     {
-      pp_printf (&pretty_name, "'%s%s%s%s%s%s%s",
+      pp_printf (&pretty_name, "'%s%s%s%s%s%s%s%s",
 		 TYPE_VOLATILE (type2) ? "volatile " : "",
 		 TYPE_READONLY (type2) ? "const " : "",
 		 TYPE_RESTRICT (type2) ? "restrict " : "",
+		 TYPE_EMPTY_RECORD (type2) ? "empty-record " : "",
 		 TYPE_ATOMIC (type2) ? "_Atomic " : "",
 		 TREE_CODE (type2) == RECORD_TYPE
 		 ? "struct "
diff --git a/gcc/var-tracking.c b/gcc/var-tracking.c
index 9185bfd..e9fdbe9 100644
--- a/gcc/var-tracking.c
+++ b/gcc/var-tracking.c
@@ -6140,10 +6140,10 @@ prepare_call_arguments (basic_block bb, rtx_insn *insn)
 		  rtx reg;
 		  INIT_CUMULATIVE_ARGS (args_so_far_v, type, NULL_RTX, fndecl,
 					nargs + 1);
-		  reg = targetm.calls.function_arg (args_so_far, mode,
-						    struct_addr, true);
-		  targetm.calls.function_arg_advance (args_so_far, mode,
-						      struct_addr, true);
+		  reg = function_arg (args_so_far, mode, struct_addr,
+				      true);
+		  function_arg_advance (args_so_far, mode, struct_addr,
+					true);
 		  if (reg == NULL_RTX)
 		    {
 		      for (; link; link = XEXP (link, 1))
@@ -6164,8 +6164,8 @@ prepare_call_arguments (basic_block bb, rtx_insn *insn)
 		  machine_mode mode;
 		  t = TYPE_ARG_TYPES (type);
 		  mode = TYPE_MODE (TREE_VALUE (t));
-		  this_arg = targetm.calls.function_arg (args_so_far, mode,
-							 TREE_VALUE (t), true);
+		  this_arg = function_arg (args_so_far, mode,
+					   TREE_VALUE (t), true);
 		  if (this_arg && !REG_P (this_arg))
 		    this_arg = NULL_RTX;
 		  else if (this_arg == NULL_RTX)
@@ -6280,8 +6280,7 @@ prepare_call_arguments (basic_block bb, rtx_insn *insn)
 		argtype = build_pointer_type (argtype);
 		mode = TYPE_MODE (argtype);
 	      }
-	    reg = targetm.calls.function_arg (args_so_far, mode,
-					      argtype, true);
+	    reg = function_arg (args_so_far, mode, argtype, true);
 	    if (TREE_CODE (argtype) == REFERENCE_TYPE
 		&& INTEGRAL_TYPE_P (TREE_TYPE (argtype))
 		&& reg
@@ -6335,8 +6334,7 @@ prepare_call_arguments (basic_block bb, rtx_insn *insn)
 			}
 		  }
 	      }
-	    targetm.calls.function_arg_advance (args_so_far, mode,
-						argtype, true);
+	    function_arg_advance (args_so_far, mode, argtype, true);
 	    t = TREE_CHAIN (t);
 	  }
       }
-- 
2.5.0


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

* Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
  2015-12-11 23:52         ` H.J. Lu
@ 2015-12-12 14:51           ` Jason Merrill
  2015-12-12 15:27             ` Jakub Jelinek
  0 siblings, 1 reply; 52+ messages in thread
From: Jason Merrill @ 2015-12-12 14:51 UTC (permalink / raw)
  To: H.J. Lu, Richard Biener; +Cc: Markus Trippelsdorf, GCC Patches

On 12/11/2015 06:52 PM, H.J. Lu wrote:
> On Thu, Dec 10, 2015 at 3:24 AM, Richard Biener
> <richard.guenther@gmail.com> wrote:
>> On Wed, Dec 9, 2015 at 10:31 PM, Markus Trippelsdorf
>> <markus@trippelsdorf.de> wrote:
>>> On 2015.12.09 at 10:53 -0800, H.J. Lu wrote:
>>>>
>>>> Empty C++ class is a corner case which isn't covered in psABI nor C++ ABI.
>>>> There is no mention of "empty record" in GCC documentation.  But there are
>>>> plenty of "empty class" in gcc/cp.  This change affects all targets.  C++ ABI
>>>> should specify how it should be passed.
>>>
>>> There is a C++ ABI mailinglist, where you could discuss this issue:
>>> http://sourcerytools.com/cgi-bin/mailman/listinfo/cxx-abi-dev
>>
>> Yep.  As long as the ABI doesn't state how to pass those I'd rather _not_ change
>> GCCs way.
>
> It is agreed that GCC is wrong on this:
>
> http://sourcerytools.com/pipermail/cxx-abi-dev/2015-December/002876.html

Yes, I think this is just a (nasty) bug on some GCC targets.

> Here is the updated patch.   I updated -WpsABI to warn empty
> record which are passed in a variable argument list or aren't the last
> arguments.   They are triggered in:
>
> /export/build/gnu/gcc-x32/build-x86_64-linux/prev-x86_64-pc-linux-gnu/libstdc++-v3/include/bits/hashtable.h:1507:7:
> note: the ABI of passing empty record has changed in GCC 6
 >....

Oof.  Well, at least it's all C++11 stuff, and GCC 5 still defaulted to 
C++98...

Jason

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

* Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
  2015-12-12 14:51           ` Jason Merrill
@ 2015-12-12 15:27             ` Jakub Jelinek
  2015-12-12 16:45               ` H.J. Lu
  2015-12-12 18:43               ` Marc Glisse
  0 siblings, 2 replies; 52+ messages in thread
From: Jakub Jelinek @ 2015-12-12 15:27 UTC (permalink / raw)
  To: Jason Merrill; +Cc: H.J. Lu, Richard Biener, Markus Trippelsdorf, GCC Patches

On Sat, Dec 12, 2015 at 09:51:23AM -0500, Jason Merrill wrote:
> On 12/11/2015 06:52 PM, H.J. Lu wrote:
> >On Thu, Dec 10, 2015 at 3:24 AM, Richard Biener
> ><richard.guenther@gmail.com> wrote:
> >>On Wed, Dec 9, 2015 at 10:31 PM, Markus Trippelsdorf
> >><markus@trippelsdorf.de> wrote:
> >>>On 2015.12.09 at 10:53 -0800, H.J. Lu wrote:
> >>>>
> >>>>Empty C++ class is a corner case which isn't covered in psABI nor C++ ABI.
> >>>>There is no mention of "empty record" in GCC documentation.  But there are
> >>>>plenty of "empty class" in gcc/cp.  This change affects all targets.  C++ ABI
> >>>>should specify how it should be passed.
> >>>
> >>>There is a C++ ABI mailinglist, where you could discuss this issue:
> >>>http://sourcerytools.com/cgi-bin/mailman/listinfo/cxx-abi-dev
> >>
> >>Yep.  As long as the ABI doesn't state how to pass those I'd rather _not_ change
> >>GCCs way.
> >
> >It is agreed that GCC is wrong on this:
> >
> >http://sourcerytools.com/pipermail/cxx-abi-dev/2015-December/002876.html
> 
> Yes, I think this is just a (nasty) bug on some GCC targets.

Well, the argument in that thread is weird, because C and C++ empty structs
are different, so it isn't surprising they are passed differently.
C++ makes those sizeof == 1, while C has them sizeof == 0.
So I rather think clang should change rather than GCC.

	Jakub

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

* Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
  2015-12-12 15:27             ` Jakub Jelinek
@ 2015-12-12 16:45               ` H.J. Lu
  2015-12-12 18:43               ` Marc Glisse
  1 sibling, 0 replies; 52+ messages in thread
From: H.J. Lu @ 2015-12-12 16:45 UTC (permalink / raw)
  To: Jakub Jelinek
  Cc: Jason Merrill, Richard Biener, Markus Trippelsdorf, GCC Patches

On Sat, Dec 12, 2015 at 7:27 AM, Jakub Jelinek <jakub@redhat.com> wrote:
> On Sat, Dec 12, 2015 at 09:51:23AM -0500, Jason Merrill wrote:
>> On 12/11/2015 06:52 PM, H.J. Lu wrote:
>> >On Thu, Dec 10, 2015 at 3:24 AM, Richard Biener
>> ><richard.guenther@gmail.com> wrote:
>> >>On Wed, Dec 9, 2015 at 10:31 PM, Markus Trippelsdorf
>> >><markus@trippelsdorf.de> wrote:
>> >>>On 2015.12.09 at 10:53 -0800, H.J. Lu wrote:
>> >>>>
>> >>>>Empty C++ class is a corner case which isn't covered in psABI nor C++ ABI.
>> >>>>There is no mention of "empty record" in GCC documentation.  But there are
>> >>>>plenty of "empty class" in gcc/cp.  This change affects all targets.  C++ ABI
>> >>>>should specify how it should be passed.
>> >>>
>> >>>There is a C++ ABI mailinglist, where you could discuss this issue:
>> >>>http://sourcerytools.com/cgi-bin/mailman/listinfo/cxx-abi-dev
>> >>
>> >>Yep.  As long as the ABI doesn't state how to pass those I'd rather _not_ change
>> >>GCCs way.
>> >
>> >It is agreed that GCC is wrong on this:
>> >
>> >http://sourcerytools.com/pipermail/cxx-abi-dev/2015-December/002876.html
>>
>> Yes, I think this is just a (nasty) bug on some GCC targets.
>
> Well, the argument in that thread is weird, because C and C++ empty structs
> are different, so it isn't surprising they are passed differently.
> C++ makes those sizeof == 1, while C has them sizeof == 0.
> So I rather think clang should change rather than GCC.
>

But according to x86-64 psABI,  sizeof == 1 should be passed in register,
not on stack.  This leads to weird GCC bugs like:

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


-- 
H.J.

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

* Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
  2015-12-12 15:27             ` Jakub Jelinek
  2015-12-12 16:45               ` H.J. Lu
@ 2015-12-12 18:43               ` Marc Glisse
  2015-12-14 20:16                 ` Jason Merrill
  1 sibling, 1 reply; 52+ messages in thread
From: Marc Glisse @ 2015-12-12 18:43 UTC (permalink / raw)
  To: Jakub Jelinek
  Cc: Jason Merrill, H.J. Lu, Richard Biener, Markus Trippelsdorf, GCC Patches

On Sat, 12 Dec 2015, Jakub Jelinek wrote:

> On Sat, Dec 12, 2015 at 09:51:23AM -0500, Jason Merrill wrote:
>> On 12/11/2015 06:52 PM, H.J. Lu wrote:
>>> On Thu, Dec 10, 2015 at 3:24 AM, Richard Biener
>>> <richard.guenther@gmail.com> wrote:
>>>> On Wed, Dec 9, 2015 at 10:31 PM, Markus Trippelsdorf
>>>> <markus@trippelsdorf.de> wrote:
>>>>> On 2015.12.09 at 10:53 -0800, H.J. Lu wrote:
>>>>>>
>>>>>> Empty C++ class is a corner case which isn't covered in psABI nor C++ ABI.
>>>>>> There is no mention of "empty record" in GCC documentation.  But there are
>>>>>> plenty of "empty class" in gcc/cp.  This change affects all targets.  C++ ABI
>>>>>> should specify how it should be passed.


About this patch, aren't we supposed to enable new C++ ABIs with 
-fabi-version=42 (or whatever the next number is)?


>>>>> There is a C++ ABI mailinglist, where you could discuss this issue:
>>>>> http://sourcerytools.com/cgi-bin/mailman/listinfo/cxx-abi-dev
>>>>
>>>> Yep.  As long as the ABI doesn't state how to pass those I'd rather _not_ change
>>>> GCCs way.
>>>
>>> It is agreed that GCC is wrong on this:
>>>
>>> http://sourcerytools.com/pipermail/cxx-abi-dev/2015-December/002876.html
>>
>> Yes, I think this is just a (nasty) bug on some GCC targets.
>
> Well, the argument in that thread is weird, because C and C++ empty structs
> are different, so it isn't surprising they are passed differently.
> C++ makes those sizeof == 1, while C has them sizeof == 0.

Maybe it isn't surprising, but it isn't particularly helpful either. It 
increases the number of places where the 2 are incompatible.
(I personally don't care about empty C structs)

> So I rather think clang should change rather than GCC.

Passing empty arguments (with trivial copy constructor, etc) is a complete 
waste. Not passing them is strictly superior. The only reason to prefer 
passing them is that that's what gcc does and changing would break the 
ABI. I could understand clang being unhappy if they have to both break ABI 
compatibility with their older versions and move towards an inferior 
ABI... (arguably they could have copied the de facto gcc ABI to begin 
with) They might be willing to do it on linux-x86, but not on ios/mac 
where they dominate and compatibility with their own earlier versions 
matters more than compatibility with gcc?

-- 
Marc Glisse

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

* Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
  2015-12-12 18:43               ` Marc Glisse
@ 2015-12-14 20:16                 ` Jason Merrill
  2015-12-14 20:39                   ` H.J. Lu
  0 siblings, 1 reply; 52+ messages in thread
From: Jason Merrill @ 2015-12-14 20:16 UTC (permalink / raw)
  To: gcc-patches, Jakub Jelinek; +Cc: H.J. Lu, Richard Biener, Markus Trippelsdorf

On 12/12/2015 01:42 PM, Marc Glisse wrote:
> On Sat, 12 Dec 2015, Jakub Jelinek wrote:
>
>> On Sat, Dec 12, 2015 at 09:51:23AM -0500, Jason Merrill wrote:
>>> On 12/11/2015 06:52 PM, H.J. Lu wrote:
>>>> On Thu, Dec 10, 2015 at 3:24 AM, Richard Biener
>>>> <richard.guenther@gmail.com> wrote:
>>>>> On Wed, Dec 9, 2015 at 10:31 PM, Markus Trippelsdorf
>>>>> <markus@trippelsdorf.de> wrote:
>>>>>> On 2015.12.09 at 10:53 -0800, H.J. Lu wrote:
>>>>>>>
>>>>>>> Empty C++ class is a corner case which isn't covered in psABI nor
>>>>>>> C++ ABI.
>>>>>>> There is no mention of "empty record" in GCC documentation.  But
>>>>>>> there are
>>>>>>> plenty of "empty class" in gcc/cp.  This change affects all
>>>>>>> targets.  C++ ABI
>>>>>>> should specify how it should be passed.
>
>
> About this patch, aren't we supposed to enable new C++ ABIs with
> -fabi-version=42 (or whatever the next number is)?

Yes, the patch should definitely make this conditional on 
abi_version_at_least.

>>>>>> There is a C++ ABI mailinglist, where you could discuss this issue:
>>>>>> http://sourcerytools.com/cgi-bin/mailman/listinfo/cxx-abi-dev
>>>>>
>>>>> Yep.  As long as the ABI doesn't state how to pass those I'd rather
>>>>> _not_ change GCCs way.
>>>>
>>>> It is agreed that GCC is wrong on this:
>>>>
>>>> http://sourcerytools.com/pipermail/cxx-abi-dev/2015-December/002876.html
>>>>
>>>
>>> Yes, I think this is just a (nasty) bug on some GCC targets.
>>
>> Well, the argument in that thread is weird, because C and C++ empty structs
>> are different, so it isn't surprising they are passed differently.
>> C++ makes those sizeof == 1, while C has them sizeof == 0.
>
> Maybe it isn't surprising, but it isn't particularly helpful either. It
> increases the number of places where the 2 are incompatible.
> (I personally don't care about empty C structs)

Yep.  The C standard doesn't have empty structs; it's a GNU extension. 
But in any case argument passing can be compatible between C and C++, so 
it really should be.

Jason

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

* Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
  2015-12-14 20:16                 ` Jason Merrill
@ 2015-12-14 20:39                   ` H.J. Lu
  2015-12-14 20:44                     ` Jason Merrill
  0 siblings, 1 reply; 52+ messages in thread
From: H.J. Lu @ 2015-12-14 20:39 UTC (permalink / raw)
  To: Jason Merrill
  Cc: GCC Patches, Jakub Jelinek, Richard Biener, Markus Trippelsdorf

On Mon, Dec 14, 2015 at 12:16 PM, Jason Merrill <jason@redhat.com> wrote:
> On 12/12/2015 01:42 PM, Marc Glisse wrote:
>>
>> On Sat, 12 Dec 2015, Jakub Jelinek wrote:
>>
>>> On Sat, Dec 12, 2015 at 09:51:23AM -0500, Jason Merrill wrote:
>>>>
>>>> On 12/11/2015 06:52 PM, H.J. Lu wrote:
>>>>>
>>>>> On Thu, Dec 10, 2015 at 3:24 AM, Richard Biener
>>>>> <richard.guenther@gmail.com> wrote:
>>>>>>
>>>>>> On Wed, Dec 9, 2015 at 10:31 PM, Markus Trippelsdorf
>>>>>> <markus@trippelsdorf.de> wrote:
>>>>>>>
>>>>>>> On 2015.12.09 at 10:53 -0800, H.J. Lu wrote:
>>>>>>>>
>>>>>>>>
>>>>>>>> Empty C++ class is a corner case which isn't covered in psABI nor
>>>>>>>> C++ ABI.
>>>>>>>> There is no mention of "empty record" in GCC documentation.  But
>>>>>>>> there are
>>>>>>>> plenty of "empty class" in gcc/cp.  This change affects all
>>>>>>>> targets.  C++ ABI
>>>>>>>> should specify how it should be passed.
>>
>>
>>
>> About this patch, aren't we supposed to enable new C++ ABIs with
>> -fabi-version=42 (or whatever the next number is)?
>
>
> Yes, the patch should definitely make this conditional on
> abi_version_at_least.
>
>>>>>>> There is a C++ ABI mailinglist, where you could discuss this issue:
>>>>>>> http://sourcerytools.com/cgi-bin/mailman/listinfo/cxx-abi-dev
>>>>>>
>>>>>>
>>>>>> Yep.  As long as the ABI doesn't state how to pass those I'd rather
>>>>>> _not_ change GCCs way.
>>>>>
>>>>>
>>>>> It is agreed that GCC is wrong on this:
>>>>>
>>>>>
>>>>> http://sourcerytools.com/pipermail/cxx-abi-dev/2015-December/002876.html
>>>>>
>>>>
>>>> Yes, I think this is just a (nasty) bug on some GCC targets.
>>>
>>>
>>> Well, the argument in that thread is weird, because C and C++ empty
>>> structs
>>> are different, so it isn't surprising they are passed differently.
>>> C++ makes those sizeof == 1, while C has them sizeof == 0.
>>
>>
>> Maybe it isn't surprising, but it isn't particularly helpful either. It
>> increases the number of places where the 2 are incompatible.
>> (I personally don't care about empty C structs)
>
>
> Yep.  The C standard doesn't have empty structs; it's a GNU extension. But
> in any case argument passing can be compatible between C and C++, so it
> really should be.
>
>

Before I make any changes, I'd like to ask if we should make
argument passing can be compatible between C and C++ for
all targets GCC support or just x86.


-- 
H.J.

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

* Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
  2015-12-14 20:39                   ` H.J. Lu
@ 2015-12-14 20:44                     ` Jason Merrill
  2015-12-14 22:08                       ` H.J. Lu
  0 siblings, 1 reply; 52+ messages in thread
From: Jason Merrill @ 2015-12-14 20:44 UTC (permalink / raw)
  To: H.J. Lu; +Cc: GCC Patches, Jakub Jelinek, Richard Biener, Markus Trippelsdorf

On 12/14/2015 03:39 PM, H.J. Lu wrote:
> On Mon, Dec 14, 2015 at 12:16 PM, Jason Merrill <jason@redhat.com> wrote:
>> On 12/12/2015 01:42 PM, Marc Glisse wrote:
>>>
>>> On Sat, 12 Dec 2015, Jakub Jelinek wrote:
>>>
>>>> On Sat, Dec 12, 2015 at 09:51:23AM -0500, Jason Merrill wrote:
>>>>>
>>>>> On 12/11/2015 06:52 PM, H.J. Lu wrote:
>>>>>>
>>>>>> On Thu, Dec 10, 2015 at 3:24 AM, Richard Biener
>>>>>> <richard.guenther@gmail.com> wrote:
>>>>>>>
>>>>>>> On Wed, Dec 9, 2015 at 10:31 PM, Markus Trippelsdorf
>>>>>>> <markus@trippelsdorf.de> wrote:
>>>>>>>>
>>>>>>>> On 2015.12.09 at 10:53 -0800, H.J. Lu wrote:
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> Empty C++ class is a corner case which isn't covered in psABI nor
>>>>>>>>> C++ ABI.
>>>>>>>>> There is no mention of "empty record" in GCC documentation.  But
>>>>>>>>> there are
>>>>>>>>> plenty of "empty class" in gcc/cp.  This change affects all
>>>>>>>>> targets.  C++ ABI
>>>>>>>>> should specify how it should be passed.
>>>
>>>
>>>
>>> About this patch, aren't we supposed to enable new C++ ABIs with
>>> -fabi-version=42 (or whatever the next number is)?
>>
>>
>> Yes, the patch should definitely make this conditional on
>> abi_version_at_least.
>>
>>>>>>>> There is a C++ ABI mailinglist, where you could discuss this issue:
>>>>>>>> http://sourcerytools.com/cgi-bin/mailman/listinfo/cxx-abi-dev
>>>>>>>
>>>>>>>
>>>>>>> Yep.  As long as the ABI doesn't state how to pass those I'd rather
>>>>>>> _not_ change GCCs way.
>>>>>>
>>>>>>
>>>>>> It is agreed that GCC is wrong on this:
>>>>>>
>>>>>>
>>>>>> http://sourcerytools.com/pipermail/cxx-abi-dev/2015-December/002876.html
>>>>>>
>>>>>
>>>>> Yes, I think this is just a (nasty) bug on some GCC targets.
>>>>
>>>>
>>>> Well, the argument in that thread is weird, because C and C++ empty
>>>> structs
>>>> are different, so it isn't surprising they are passed differently.
>>>> C++ makes those sizeof == 1, while C has them sizeof == 0.
>>>
>>>
>>> Maybe it isn't surprising, but it isn't particularly helpful either. It
>>> increases the number of places where the 2 are incompatible.
>>> (I personally don't care about empty C structs)
>>
>>
>> Yep.  The C standard doesn't have empty structs; it's a GNU extension. But
>> in any case argument passing can be compatible between C and C++, so it
>> really should be.
>>
>>
>
> Before I make any changes, I'd like to ask if we should make
> argument passing can be compatible between C and C++ for
> all targets GCC support or just x86.

All.

Jason

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

* Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
  2015-12-14 20:44                     ` Jason Merrill
@ 2015-12-14 22:08                       ` H.J. Lu
  2016-01-26 19:27                         ` Jason Merrill
  0 siblings, 1 reply; 52+ messages in thread
From: H.J. Lu @ 2015-12-14 22:08 UTC (permalink / raw)
  To: Jason Merrill
  Cc: GCC Patches, Jakub Jelinek, Richard Biener, Markus Trippelsdorf

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

On Mon, Dec 14, 2015 at 12:43 PM, Jason Merrill <jason@redhat.com> wrote:
> On 12/14/2015 03:39 PM, H.J. Lu wrote:
>>
>> On Mon, Dec 14, 2015 at 12:16 PM, Jason Merrill <jason@redhat.com> wrote:
>>>
>>> On 12/12/2015 01:42 PM, Marc Glisse wrote:
>>>>
>>>>
>>>> On Sat, 12 Dec 2015, Jakub Jelinek wrote:
>>>>
>>>>> On Sat, Dec 12, 2015 at 09:51:23AM -0500, Jason Merrill wrote:
>>>>>>
>>>>>>
>>>>>> On 12/11/2015 06:52 PM, H.J. Lu wrote:
>>>>>>>
>>>>>>>
>>>>>>> On Thu, Dec 10, 2015 at 3:24 AM, Richard Biener
>>>>>>> <richard.guenther@gmail.com> wrote:
>>>>>>>>
>>>>>>>>
>>>>>>>> On Wed, Dec 9, 2015 at 10:31 PM, Markus Trippelsdorf
>>>>>>>> <markus@trippelsdorf.de> wrote:
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> On 2015.12.09 at 10:53 -0800, H.J. Lu wrote:
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> Empty C++ class is a corner case which isn't covered in psABI nor
>>>>>>>>>> C++ ABI.
>>>>>>>>>> There is no mention of "empty record" in GCC documentation.  But
>>>>>>>>>> there are
>>>>>>>>>> plenty of "empty class" in gcc/cp.  This change affects all
>>>>>>>>>> targets.  C++ ABI
>>>>>>>>>> should specify how it should be passed.
>>>>
>>>>
>>>>
>>>>
>>>> About this patch, aren't we supposed to enable new C++ ABIs with
>>>> -fabi-version=42 (or whatever the next number is)?
>>>
>>>
>>>
>>> Yes, the patch should definitely make this conditional on
>>> abi_version_at_least.
>>>
>>>>>>>>> There is a C++ ABI mailinglist, where you could discuss this issue:
>>>>>>>>> http://sourcerytools.com/cgi-bin/mailman/listinfo/cxx-abi-dev
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> Yep.  As long as the ABI doesn't state how to pass those I'd rather
>>>>>>>> _not_ change GCCs way.
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> It is agreed that GCC is wrong on this:
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> http://sourcerytools.com/pipermail/cxx-abi-dev/2015-December/002876.html
>>>>>>>
>>>>>>
>>>>>> Yes, I think this is just a (nasty) bug on some GCC targets.
>>>>>
>>>>>
>>>>>
>>>>> Well, the argument in that thread is weird, because C and C++ empty
>>>>> structs
>>>>> are different, so it isn't surprising they are passed differently.
>>>>> C++ makes those sizeof == 1, while C has them sizeof == 0.
>>>>
>>>>
>>>>
>>>> Maybe it isn't surprising, but it isn't particularly helpful either. It
>>>> increases the number of places where the 2 are incompatible.
>>>> (I personally don't care about empty C structs)
>>>
>>>
>>>
>>> Yep.  The C standard doesn't have empty structs; it's a GNU extension.
>>> But
>>> in any case argument passing can be compatible between C and C++, so it
>>> really should be.
>>>
>>>
>>
>> Before I make any changes, I'd like to ask if we should make
>> argument passing can be compatible between C and C++ for
>> all targets GCC support or just x86.
>
>
> All.

Here is the patch to guard this ABI change with the ABI level 10,
which is updated in GCC 6.  OK for master if there is no regression
on x86?

The patch for non-x86 targets is at

https://gcc.gnu.org/ml/gcc-patches/2015-12/msg01063.html


-- 
H.J.

[-- Attachment #2: 0001-Add-TYPE_EMPTY_RECORD-for-C-empty-class.patch --]
[-- Type: text/x-patch, Size: 40124 bytes --]

From fccd449a091589fedaf6ee4998271a16d93147fc 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 TYPE_EMPTY_RECORD for C++ empty class

Empty record should be returned and passed the same way in C and C++.
This patch overloads a bit, side_effects_flag, in tree_base for C++
empty class.  Middle-end and x86 backend are updated to ignore empty
records for parameter passing and function value return.  Other targets
may need similar changes.

get_ref_base_and_extent is changed to set bitsize to 0 for empty records
so that when ref_maybe_used_by_call_p_1 calls get_ref_base_and_extent to
get 0 as the maximum size on empty record.  Otherwise, find_tail_calls
won't perform tail call optimization for functions with empty record
parameters, as shown in g++.dg/pr60336-1.C and g++.dg/pr60336-2.C.

This ABI change is enabled only if the ABI level is at least 10, which
is updated in GCC 6.

gcc/

	PR c++/60336
	PR middle-end/67239
	PR target/68355
	* calls.c (initialize_argument_information): Warn empty record
	if they are used in a variable argument list or aren't the last
	arguments.  Replace targetm.calls.function_arg,
	targetm.calls.function_incoming_arg and
	targetm.calls.function_arg_advance with function_arg,
	function_incoming_arg and function_arg_advance.
	(expand_call): Replace targetm.calls.function_arg,
	targetm.calls.function_incoming_arg and
	targetm.calls.function_arg_advance with function_arg,
	function_incoming_arg and function_arg_advance.
	(emit_library_call_value_1): Likewise.
	(store_one_arg): Use 0 for empty record size.  Don't
	push 0 size argument onto stack.
	(must_pass_in_stack_var_size_or_pad): Return false for empty
	record.
	* dse.c (get_call_args): Replace targetm.calls.function_arg
	and targetm.calls.function_arg_advance with function_arg and
	function_arg_advance.
	* expr.c (block_move_libcall_safe_for_call_parm): Likewise.
	* function.c (aggregate_value_p): Replace
	targetm.calls.return_in_memory with return_in_memory.
	(assign_parm_data_all): Add warn_empty_record.
	(assign_parms_augmented_arg_list): Set warn_empty_record if
	empty records are used in a variable argument list or aren't
	the last arguments.
	(assign_parm_find_entry_rtl): Warn empty record if
	warn_empty_record is set.  Replace
	targetm.calls.function_incoming_arg with function_incoming_arg.
	(assign_parms): Only warn empty record once.  Replace
	targetm.calls.function_arg_advance with function_arg_advance.
	(gimplify_parameters): Replace targetm.calls.function_arg_advance
	with function_arg_advance.
	(locate_and_pad_parm): Use 0 for empty record size.
	(warn_empty_record): New function.
	(function_arg_advance): New wrapper function.
	(function_arg): Likewise.
	(function_incoming_arg): Likewise.
	(return_in_memory): Likewise.
	* lto-streamer-out.c (hash_tree): Call hstate.add_flag with
	TYPE_EMPTY_RECORD for types.
	* print-tree.c (print_node): Also handle TYPE_EMPTY_RECORD.
	* ubsan.c (ubsan_type_descriptor): Likewise.
	* target.h (function_arg_advance): New prototype.
	(function_arg): Likewise.
	(function_incoming_arg): Likewise.
	(return_in_memory): Likewise.
	* targhooks.c (std_gimplify_va_arg_expr): Use 0 for empty record
	size.
	* tree-dfa.c (get_ref_base_and_extent): Likewise.
	* tree-core.h (tree_base): Mention TYPE_EMPTY_RECORD in comments
	for side_effects_flag.
	* tree-streamer-in.c (unpack_ts_base_value_fields): Stream in
	TYPE_EMPTY_RECORD for types.
	* tree-streamer-out.c (pack_ts_base_value_fields): Stream out
	TYPE_EMPTY_RECORD for types.
	* tree.h (TYPE_EMPTY_RECORD): New.
	(type_is_empty_record_p): New static inline function.
	* var-tracking.c (prepare_call_arguments): Replace
	targetm.calls.function_arg and targetm.calls.function_arg_advance
	with function_arg and function_arg_advance.
	* config/i386/i386.c (ix86_gimplify_va_arg): Use 0 for empty
	record size.

gcc/c

	PR c++/60336
	PR middle-end/67239
	PR target/68355
	* c-aux-info.c (gen_type): Add TYPE_EMPTY_RECORD check.

gcc/cp/

	PR c++/60336
	PR middle-end/67239
	PR target/68355
	* class.c (finish_struct_1): Set TYPE_EMPTY_RECORD with return
	value from is_really_empty_class () if the ABI level is at least
	10.

gcc/lto/

	PR c++/60336
	PR middle-end/67239
	PR target/68355
	* lto.c (compare_tree_sccs_1): Call compare_values with
	TYPE_EMPTY_RECORD for types.

gcc/testsuite/

	PR c++/60336
	PR middle-end/67239
	PR target/68355
	* g++.dg/abi/empty12.C: New test.
	* g++.dg/abi/empty12.h: Likewise.
	* g++.dg/abi/empty12a.c: Likewise.
	* g++.dg/abi/empty13.C: Likewise.
	* g++.dg/abi/empty13.h: Likewise.
	* g++.dg/abi/empty13a.c: Likewise.
	* 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/pr68355.C: Likewise.
---
 gcc/c/c-aux-info.c                  |   2 +
 gcc/calls.c                         |  99 +++++++++++++++++++----------
 gcc/config/i386/i386.c              |   3 +-
 gcc/cp/class.c                      |   3 +
 gcc/dse.c                           |   4 +-
 gcc/expr.c                          |   5 +-
 gcc/function.c                      | 123 ++++++++++++++++++++++++++++++++----
 gcc/lto-streamer-out.c              |   2 +
 gcc/lto/lto.c                       |   2 +
 gcc/print-tree.c                    |   3 +
 gcc/target.h                        |   8 +++
 gcc/targhooks.c                     |   5 +-
 gcc/testsuite/g++.dg/abi/empty12.C  |  17 +++++
 gcc/testsuite/g++.dg/abi/empty12.h  |   9 +++
 gcc/testsuite/g++.dg/abi/empty12a.c |   6 ++
 gcc/testsuite/g++.dg/abi/empty13.C  |  17 +++++
 gcc/testsuite/g++.dg/abi/empty13.h  |   9 +++
 gcc/testsuite/g++.dg/abi/empty13a.c |   6 ++
 gcc/testsuite/g++.dg/pr60336-1.C    |  17 +++++
 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/pr68355.C      |  24 +++++++
 gcc/tree-core.h                     |   3 +
 gcc/tree-dfa.c                      |   2 +
 gcc/tree-streamer-in.c              |   5 +-
 gcc/tree-streamer-out.c             |   5 +-
 gcc/tree.h                          |  12 ++++
 gcc/ubsan.c                         |   3 +-
 gcc/var-tracking.c                  |  18 +++---
 30 files changed, 456 insertions(+), 67 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/abi/empty12.C
 create mode 100644 gcc/testsuite/g++.dg/abi/empty12.h
 create mode 100644 gcc/testsuite/g++.dg/abi/empty12a.c
 create mode 100644 gcc/testsuite/g++.dg/abi/empty13.C
 create mode 100644 gcc/testsuite/g++.dg/abi/empty13.h
 create mode 100644 gcc/testsuite/g++.dg/abi/empty13a.c
 create mode 100644 gcc/testsuite/g++.dg/pr60336-1.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/pr68355.C

diff --git a/gcc/c/c-aux-info.c b/gcc/c/c-aux-info.c
index 79d9851..93b001b 100644
--- a/gcc/c/c-aux-info.c
+++ b/gcc/c/c-aux-info.c
@@ -433,6 +433,8 @@ gen_type (const char *ret_val, tree t, formals_style style)
     ret_val = concat ("volatile ", ret_val, NULL);
   if (TYPE_RESTRICT (t))
     ret_val = concat ("restrict ", ret_val, NULL);
+  if (TYPE_EMPTY_RECORD (t))
+    ret_val = concat ("empty-record ", ret_val, NULL);
   return ret_val;
 }
 
diff --git a/gcc/calls.c b/gcc/calls.c
index 1eb4ec7..9c0e57d 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -1152,7 +1152,13 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
   bitmap_obstack_initialize (NULL);
 
   /* In this loop, we consider args in the order they are written.
-     We fill up ARGS from the back.  */
+     We fill up ARGS from the back.  Warn empty record if they are used
+     in a variable argument list or they aren't the last arguments.
+     Set warn_empty_record to true if we don't warn empty record to
+     avoid walking arguments.  */
+  bool seen_empty_record = false;
+  bool warn_empty_record
+    = (!warn_psabi || stdarg_p (fndecl ? TREE_TYPE (fndecl) : fntype));
 
   i = num_actuals - 1;
   {
@@ -1185,6 +1191,15 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
       {
 	tree argtype = TREE_TYPE (arg);
 
+	if (!warn_empty_record)
+	  {
+	    if (argtype != error_mark_node
+		&& type_is_empty_record_p (argtype))
+	      seen_empty_record = true;
+	    else if (seen_empty_record)
+	      warn_empty_record = true;
+	  }
+
 	/* Remember last param with pointer and associate it
 	   with following pointer bounds.  */
 	if (CALL_WITH_BOUNDS_P (exp)
@@ -1399,8 +1414,13 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
       args[i].unsignedp = unsignedp;
       args[i].mode = mode;
 
-      args[i].reg = targetm.calls.function_arg (args_so_far, mode, type,
-						argpos < n_named_args);
+      args[i].reg = function_arg (args_so_far, mode, type,
+				  argpos < n_named_args,
+				  warn_empty_record);
+
+      /* Only warn empty record once.  */
+      if (type_is_empty_record_p (type))
+	warn_empty_record = false;
 
       if (args[i].reg && CONST_INT_P (args[i].reg))
 	{
@@ -1413,8 +1433,8 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
 	 arguments have to go into the incoming registers.  */
       if (targetm.calls.function_incoming_arg != targetm.calls.function_arg)
 	args[i].tail_call_reg
-	  = targetm.calls.function_incoming_arg (args_so_far, mode, type,
-						 argpos < n_named_args);
+	  = function_incoming_arg (args_so_far, mode, type,
+				   argpos < n_named_args);
       else
 	args[i].tail_call_reg = args[i].reg;
 
@@ -1475,8 +1495,8 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
       /* Increment ARGS_SO_FAR, which has info about which arg-registers
 	 have been used, etc.  */
 
-      targetm.calls.function_arg_advance (args_so_far, TYPE_MODE (type),
-					  type, argpos < n_named_args);
+      function_arg_advance (args_so_far, TYPE_MODE (type), type,
+			    argpos < n_named_args);
     }
 }
 
@@ -3328,14 +3348,11 @@ expand_call (tree exp, rtx target, int ignore)
       /* Set up next argument register.  For sibling calls on machines
 	 with register windows this should be the incoming register.  */
       if (pass == 0)
-	next_arg_reg = targetm.calls.function_incoming_arg (args_so_far,
-							    VOIDmode,
-							    void_type_node,
-							    true);
+	next_arg_reg = function_incoming_arg (args_so_far, VOIDmode,
+					      void_type_node, true);
       else
-	next_arg_reg = targetm.calls.function_arg (args_so_far,
-						   VOIDmode, void_type_node,
-						   true);
+	next_arg_reg = function_arg (args_so_far, VOIDmode,
+				     void_type_node, true);
 
       if (pass == 1 && (return_flags & ERF_RETURNS_ARG))
 	{
@@ -3947,8 +3964,8 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
       argvec[count].mode = Pmode;
       argvec[count].partial = 0;
 
-      argvec[count].reg = targetm.calls.function_arg (args_so_far,
-						      Pmode, NULL_TREE, true);
+      argvec[count].reg = function_arg (args_so_far, Pmode, NULL_TREE,
+					true);
       gcc_assert (targetm.calls.arg_partial_bytes (args_so_far, Pmode,
 						   NULL_TREE, 1) == 0);
 
@@ -3965,7 +3982,7 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
 	  || reg_parm_stack_space > 0)
 	args_size.constant += argvec[count].locate.size.constant;
 
-      targetm.calls.function_arg_advance (args_so_far, Pmode, (tree) 0, true);
+      function_arg_advance (args_so_far, Pmode, (tree) 0, true);
 
       count++;
     }
@@ -4030,8 +4047,7 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
       mode = promote_function_mode (NULL_TREE, mode, &unsigned_p, NULL_TREE, 0);
       argvec[count].mode = mode;
       argvec[count].value = convert_modes (mode, GET_MODE (val), val, unsigned_p);
-      argvec[count].reg = targetm.calls.function_arg (args_so_far, mode,
-						      NULL_TREE, true);
+      argvec[count].reg = function_arg (args_so_far, mode, NULL_TREE, true);
 
       argvec[count].partial
 	= targetm.calls.arg_partial_bytes (args_so_far, mode, NULL_TREE, 1);
@@ -4060,7 +4076,7 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
 			     GET_MODE_SIZE (mode) <= UNITS_PER_WORD);
 #endif
 
-      targetm.calls.function_arg_advance (args_so_far, mode, (tree) 0, true);
+      function_arg_advance (args_so_far, mode, (tree) 0, true);
     }
 
   /* If this machine requires an external definition for library
@@ -4407,8 +4423,7 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
 	       build_function_type (tfom, NULL_TREE),
 	       original_args_size.constant, args_size.constant,
 	       struct_value_size,
-	       targetm.calls.function_arg (args_so_far,
-					   VOIDmode, void_type_node, true),
+	       function_arg (args_so_far, VOIDmode, void_type_node, true),
 	       valreg,
 	       old_inhibit_defer_pop + 1, call_fusage, flags, args_so_far);
 
@@ -4843,7 +4858,10 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
 	 Note that in C the default argument promotions
 	 will prevent such mismatches.  */
 
-      size = GET_MODE_SIZE (arg->mode);
+      if (type_is_empty_record_p (TREE_TYPE (pval)))
+	size = 0;
+      else
+	size = GET_MODE_SIZE (arg->mode);
       /* Compute how much space the push instruction will push.
 	 On many machines, pushing a byte will advance the stack
 	 pointer by a halfword.  */
@@ -4873,10 +4891,14 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
 
       /* This isn't already where we want it on the stack, so put it there.
 	 This can either be done with push or copy insns.  */
-      if (!emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), NULL_RTX,
-		      parm_align, partial, reg, used - size, argblock,
-		      ARGS_SIZE_RTX (arg->locate.offset), reg_parm_stack_space,
-		      ARGS_SIZE_RTX (arg->locate.alignment_pad), true))
+      if (used
+	  && !emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval),
+			      NULL_RTX, parm_align, partial, reg,
+			      used - size, argblock,
+			      ARGS_SIZE_RTX (arg->locate.offset),
+			      reg_parm_stack_space,
+			      ARGS_SIZE_RTX (arg->locate.alignment_pad),
+			      true))
 	sibcall_failure = 1;
 
       /* Unless this is a partially-in-register argument, the argument is now
@@ -4908,10 +4930,15 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
 	{
 	  /* PUSH_ROUNDING has no effect on us, because emit_push_insn
 	     for BLKmode is careful to avoid it.  */
+	  bool empty_record = type_is_empty_record_p (TREE_TYPE (pval));
 	  excess = (arg->locate.size.constant
-		    - int_size_in_bytes (TREE_TYPE (pval))
+		    - (empty_record
+		       ? 0
+		       : int_size_in_bytes (TREE_TYPE (pval)))
 		    + partial);
-	  size_rtx = expand_expr (size_in_bytes (TREE_TYPE (pval)),
+	  size_rtx = expand_expr ((empty_record
+				   ? size_zero_node
+				   : size_in_bytes (TREE_TYPE (pval))),
 				  NULL_RTX, TYPE_MODE (sizetype),
 				  EXPAND_NORMAL);
 	}
@@ -4986,10 +5013,13 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
 	    }
 	}
 
-      emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), size_rtx,
-		      parm_align, partial, reg, excess, argblock,
-		      ARGS_SIZE_RTX (arg->locate.offset), reg_parm_stack_space,
-		      ARGS_SIZE_RTX (arg->locate.alignment_pad), false);
+      if (!CONST_INT_P (size_rtx) || INTVAL (size_rtx) != 0)
+	emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval),
+			size_rtx, parm_align, partial, reg, excess,
+			argblock, ARGS_SIZE_RTX (arg->locate.offset),
+			reg_parm_stack_space,
+			ARGS_SIZE_RTX (arg->locate.alignment_pad),
+			false);
 
       /* Unless this is a partially-in-register argument, the argument is now
 	 in the stack.
@@ -5067,6 +5097,9 @@ must_pass_in_stack_var_size_or_pad (machine_mode mode, const_tree type)
   if (TREE_ADDRESSABLE (type))
     return true;
 
+  if (type_is_empty_record_p (type))
+    return false;
+
   /* If the padding and mode of the type is such that a copy into
      a register would put it into the wrong part of the register.  */
   if (mode == BLKmode
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index cecea24..0e6fac4 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -10293,7 +10293,8 @@ ix86_gimplify_va_arg (tree valist, tree type, gimple_seq *pre_p,
   indirect_p = pass_by_reference (NULL, TYPE_MODE (type), type, false);
   if (indirect_p)
     type = build_pointer_type (type);
-  size = int_size_in_bytes (type);
+  bool empty_record = type && type_is_empty_record_p (type);
+  size = empty_record ? 0 : int_size_in_bytes (type);
   rsize = CEIL (size, UNITS_PER_WORD);
 
   nat_mode = type_natural_mode (type, NULL, false);
diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index 216a301..e50fb70 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -6802,6 +6802,9 @@ finish_struct_1 (tree t)
 	  TYPE_TRANSPARENT_AGGR (t) = 0;
 	}
     }
+
+  if (abi_version_at_least (10))
+    TYPE_EMPTY_RECORD (t) = is_really_empty_class (t);
 }
 
 /* Insert FIELDS into T for the sorted case if the FIELDS count is
diff --git a/gcc/dse.c b/gcc/dse.c
index 35eef71..594106f 100644
--- a/gcc/dse.c
+++ b/gcc/dse.c
@@ -2366,7 +2366,7 @@ get_call_args (rtx call_insn, tree fn, rtx *args, int nargs)
     {
       machine_mode mode = TYPE_MODE (TREE_VALUE (arg));
       rtx reg, link, tmp;
-      reg = targetm.calls.function_arg (args_so_far, mode, NULL_TREE, true);
+      reg = function_arg (args_so_far, mode, NULL_TREE, true);
       if (!reg || !REG_P (reg) || GET_MODE (reg) != mode
 	  || GET_MODE_CLASS (mode) != MODE_INT)
 	return false;
@@ -2400,7 +2400,7 @@ get_call_args (rtx call_insn, tree fn, rtx *args, int nargs)
       if (tmp)
 	args[idx] = tmp;
 
-      targetm.calls.function_arg_advance (args_so_far, mode, NULL_TREE, true);
+      function_arg_advance (args_so_far, mode, NULL_TREE, true);
     }
   if (arg != void_list_node || idx != nargs)
     return false;
diff --git a/gcc/expr.c b/gcc/expr.c
index bd43dc4..384581a 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -1198,13 +1198,12 @@ block_move_libcall_safe_for_call_parm (void)
     for ( ; arg != void_list_node ; arg = TREE_CHAIN (arg))
       {
 	machine_mode mode = TYPE_MODE (TREE_VALUE (arg));
-	rtx tmp = targetm.calls.function_arg (args_so_far, mode,
-					      NULL_TREE, true);
+	rtx tmp = function_arg (args_so_far, mode, NULL_TREE, true);
 	if (!tmp || !REG_P (tmp))
 	  return false;
 	if (targetm.calls.arg_partial_bytes (args_so_far, mode, NULL, 1))
 	  return false;
-	targetm.calls.function_arg_advance (args_so_far, mode,
+	function_arg_advance (args_so_far, mode,
 					    NULL_TREE, true);
       }
   }
diff --git a/gcc/function.c b/gcc/function.c
index 035a49e..5e95563 100644
--- a/gcc/function.c
+++ b/gcc/function.c
@@ -2074,7 +2074,7 @@ aggregate_value_p (const_tree exp, const_tree fntype)
   if (flag_pcc_struct_return && AGGREGATE_TYPE_P (type))
     return 1;
 
-  if (targetm.calls.return_in_memory (type, fntype))
+  if (return_in_memory (type, fntype))
     return 1;
 
   /* Make sure we have suitable call-clobbered regs to return
@@ -2243,6 +2243,7 @@ struct assign_parm_data_all
   HOST_WIDE_INT pretend_args_size;
   HOST_WIDE_INT extra_pretend_bytes;
   int reg_parm_stack_space;
+  bool warn_empty_record;
 };
 
 struct assign_parm_data_one
@@ -2408,6 +2409,28 @@ assign_parms_augmented_arg_list (struct assign_parm_data_all *all)
   if (targetm.calls.split_complex_arg)
     split_complex_args (&fnargs);
 
+  /* Warn empty record if they are used in a variable argument list or
+     they aren't the last arguments.  Set warn_empty_record to true if
+     we don't warn empty record to avoid walking arguments.  */
+  bool warn_empty_record = !warn_psabi || stdarg_p (fntype);
+  if (!warn_empty_record)
+    {
+      unsigned int i;
+      bool seen_empty_record = false;
+      FOR_EACH_VEC_ELT (fnargs, i, arg)
+	{
+	  tree type = TREE_TYPE (arg);
+	  if (type != error_mark_node && type_is_empty_record_p (type))
+	    seen_empty_record = true;
+	  else if (seen_empty_record)
+	    {
+	      warn_empty_record = true;
+	      break;
+	    }
+	}
+    }
+  all->warn_empty_record = warn_empty_record;
+
   return fnargs;
 }
 
@@ -2524,10 +2547,11 @@ assign_parm_find_entry_rtl (struct assign_parm_data_all *all,
       return;
     }
 
-  entry_parm = targetm.calls.function_incoming_arg (all->args_so_far,
-						    data->promoted_mode,
-						    data->passed_type,
-						    data->named_arg);
+  entry_parm = function_incoming_arg (all->args_so_far,
+				      data->promoted_mode,
+				      data->passed_type,
+				      data->named_arg,
+				      all->warn_empty_record);
 
   if (entry_parm == 0)
     data->promoted_mode = data->passed_mode;
@@ -2551,9 +2575,9 @@ assign_parm_find_entry_rtl (struct assign_parm_data_all *all,
       if (targetm.calls.pretend_outgoing_varargs_named (all->args_so_far))
 	{
 	  rtx tem;
-	  tem = targetm.calls.function_incoming_arg (all->args_so_far,
-						     data->promoted_mode,
-						     data->passed_type, true);
+	  tem = function_incoming_arg (all->args_so_far,
+				       data->promoted_mode,
+				       data->passed_type, true);
 	  in_regs = tem != NULL;
 	}
     }
@@ -3715,6 +3739,10 @@ assign_parms (tree fndecl)
       /* Find out where the parameter arrives in this function.  */
       assign_parm_find_entry_rtl (&all, &data);
 
+      /* Only warn empty record once.  */
+      if (type_is_empty_record_p (data.passed_type))
+	all.warn_empty_record = false;
+
       /* Find out where stack space for this parameter might be.  */
       if (assign_parm_is_stack_parm (&all, &data))
 	{
@@ -3791,8 +3819,8 @@ assign_parms (tree fndecl)
 	}
 
       /* Update info on where next arg arrives in registers.  */
-      targetm.calls.function_arg_advance (all.args_so_far, data.promoted_mode,
-					  data.passed_type, data.named_arg);
+      function_arg_advance (all.args_so_far, data.promoted_mode,
+			    data.passed_type, data.named_arg);
 
       if (POINTER_BOUNDS_TYPE_P (data.passed_type))
 	bound_no++;
@@ -3988,8 +4016,8 @@ gimplify_parameters (void)
 	continue;
 
       /* Update info on where next arg arrives in registers.  */
-      targetm.calls.function_arg_advance (all.args_so_far, data.promoted_mode,
-					  data.passed_type, data.named_arg);
+      function_arg_advance (all.args_so_far, data.promoted_mode,
+			    data.passed_type, data.named_arg);
 
       /* ??? Once upon a time variable_size stuffed parameter list
 	 SAVE_EXPRs (amongst others) onto a pending sizes list.  This
@@ -4132,8 +4160,11 @@ locate_and_pad_parm (machine_mode passed_mode, tree type, int in_regs,
 
   part_size_in_regs = (reg_parm_stack_space == 0 ? partial : 0);
 
-  sizetree
-    = type ? size_in_bytes (type) : size_int (GET_MODE_SIZE (passed_mode));
+  if (type)
+    sizetree = (type_is_empty_record_p (type)
+		? size_zero_node : size_in_bytes (type));
+  else
+    sizetree = size_int (GET_MODE_SIZE (passed_mode));
   where_pad = FUNCTION_ARG_PADDING (passed_mode, type);
   boundary = targetm.calls.function_arg_boundary (passed_mode, type);
   round_boundary = targetm.calls.function_arg_round_boundary (passed_mode,
@@ -6862,5 +6893,69 @@ make_pass_match_asm_constraints (gcc::context *ctxt)
   return new pass_match_asm_constraints (ctxt);
 }
 
+static void
+warn_empty_record (void)
+{
+  if (warn_psabi)
+    inform (input_location, "the ABI of passing empty record has"
+	    " changed in GCC 6");
+}
+
+/* Wrapper for targetm.calls.function_arg_advance.  */
+
+void
+function_arg_advance (cumulative_args_t ca, machine_mode mode,
+		      const_tree type, bool named)
+{
+  if (type && type_is_empty_record_p (type))
+    return;
+
+  targetm.calls.function_arg_advance (ca, mode, type, named);
+}
+
+/* Wrapper for targetm.calls.function_arg.  */
+
+rtx
+function_arg (cumulative_args_t ca, machine_mode mode, const_tree type,
+	      bool named, bool warn_empty_record_p)
+{
+  if (type && type_is_empty_record_p (type))
+    {
+      if (warn_empty_record_p)
+	warn_empty_record ();
+      return NULL;
+    }
+
+  return targetm.calls.function_arg (ca, mode, type, named);
+}
+
+/* Wrapper for targetm.calls.function_incoming_arg.  */
+
+rtx
+function_incoming_arg (cumulative_args_t ca, machine_mode mode,
+		       const_tree type, bool named,
+		       bool warn_empty_record_p)
+{
+  if (type && type_is_empty_record_p (type))
+    {
+      if (warn_empty_record_p)
+	warn_empty_record ();
+      return NULL;
+    }
+
+  return targetm.calls.function_incoming_arg (ca, mode, type, named);
+}
+
+/* Wrapper for targetm.calls.return_in_memory.  */
+
+bool
+return_in_memory (const_tree type, const_tree fntype)
+{
+  if (type && type_is_empty_record_p (type))
+    return false;
+
+  return targetm.calls.return_in_memory (type, fntype);
+}
+
 
 #include "gt-function.h"
diff --git a/gcc/lto-streamer-out.c b/gcc/lto-streamer-out.c
index 049313d..2132aa3 100644
--- a/gcc/lto-streamer-out.c
+++ b/gcc/lto-streamer-out.c
@@ -944,6 +944,8 @@ hash_tree (struct streamer_tree_cache_d *cache, hash_map<tree, hashval_t> *map,
       hstate.add_flag (TREE_READONLY (t));
       hstate.add_flag (TREE_PUBLIC (t));
     }
+  else
+    hstate.add_flag (TYPE_EMPTY_RECORD (t));
   hstate.add_flag (TREE_ADDRESSABLE (t));
   hstate.add_flag (TREE_THIS_VOLATILE (t));
   if (DECL_P (t))
diff --git a/gcc/lto/lto.c b/gcc/lto/lto.c
index fcf7caf..03fa877 100644
--- a/gcc/lto/lto.c
+++ b/gcc/lto/lto.c
@@ -1004,6 +1004,8 @@ compare_tree_sccs_1 (tree t1, tree t2, tree **map)
       compare_values (TREE_READONLY);
       compare_values (TREE_PUBLIC);
     }
+  else
+    compare_values (TYPE_EMPTY_RECORD);
   compare_values (TREE_ADDRESSABLE);
   compare_values (TREE_THIS_VOLATILE);
   if (DECL_P (t1))
diff --git a/gcc/print-tree.c b/gcc/print-tree.c
index cb0f1fd..bb489ff 100644
--- a/gcc/print-tree.c
+++ b/gcc/print-tree.c
@@ -587,6 +587,9 @@ print_node (FILE *file, const char *prefix, tree node, int indent)
       if (TYPE_RESTRICT (node))
 	fputs (" restrict", file);
 
+      if (TYPE_EMPTY_RECORD (node))
+	fputs (" empty-record", file);
+
       if (TYPE_LANG_FLAG_0 (node))
 	fputs (" type_0", file);
       if (TYPE_LANG_FLAG_1 (node))
diff --git a/gcc/target.h b/gcc/target.h
index ffc4d6a..4f25a54 100644
--- a/gcc/target.h
+++ b/gcc/target.h
@@ -104,6 +104,14 @@ extern bool target_default_pointer_address_modes_p (void);
    behaviour.  */
 extern unsigned int get_move_ratio (bool);
 
+extern void function_arg_advance (cumulative_args_t, machine_mode,
+				  const_tree, bool);
+extern rtx function_arg (cumulative_args_t, machine_mode, const_tree,
+			 bool, bool = false);
+extern rtx function_incoming_arg (cumulative_args_t, machine_mode,
+				  const_tree, bool, bool = false);
+extern bool return_in_memory (const_tree, const_tree);
+
 struct stdarg_info;
 struct spec_info_def;
 struct hard_reg_set_container;
diff --git a/gcc/targhooks.c b/gcc/targhooks.c
index dcf0863..7a8d1e8 100644
--- a/gcc/targhooks.c
+++ b/gcc/targhooks.c
@@ -1827,9 +1827,12 @@ std_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
   /* Hoist the valist value into a temporary for the moment.  */
   valist_tmp = get_initialized_tmp_var (valist, pre_p, NULL);
 
+  bool empty_record = type_is_empty_record_p (type);
+
   /* va_list pointer is aligned to PARM_BOUNDARY.  If argument actually
      requires greater alignment, we must perform dynamic alignment.  */
   if (boundary > align
+      && !empty_record
       && !integer_zerop (TYPE_SIZE (type)))
     {
       t = build2 (MODIFY_EXPR, TREE_TYPE (valist), valist_tmp,
@@ -1856,7 +1859,7 @@ std_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
     }
 
   /* Compute the rounded size of the type.  */
-  type_size = size_in_bytes (type);
+  type_size = empty_record ? size_zero_node : size_in_bytes (type);
   rounded_size = round_up (type_size, align);
 
   /* Reduce rounded_size so it's sharable with the postqueue.  */
diff --git a/gcc/testsuite/g++.dg/abi/empty12.C b/gcc/testsuite/g++.dg/abi/empty12.C
new file mode 100644
index 0000000..db2fd24
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/empty12.C
@@ -0,0 +1,17 @@
+// PR c++/60336
+// { dg-do run }
+// { dg-options "-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-message "note: the ABI of passing empty record has changed in GCC 6" }
+  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/pr60336-1.C b/gcc/testsuite/g++.dg/pr60336-1.C
new file mode 100644
index 0000000..af08638
--- /dev/null
+++ b/gcc/testsuite/g++.dg/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/pr60336-2.C b/gcc/testsuite/g++.dg/pr60336-2.C
new file mode 100644
index 0000000..386c82a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/pr60336-2.C
@@ -0,0 +1,48 @@
+// { dg-do run }
+// { dg-options "-O2" }
+
+#include <stdarg.h>
+
+struct dummy { };
+
+void
+test (struct dummy a, int m, ...) // { dg-message "note: the ABI of passing empty record has changed in GCC 6" }
+{
+  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 "note: the ABI of passing empty record has changed in GCC 6" }
+  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..3afdc8d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/pr60336-3.C
@@ -0,0 +1,15 @@
+// { dg-do compile }
+// { dg-options "-O2" }
+
+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); // { dg-message "note: the ABI of passing empty record has changed in GCC 6" }
+  test2 (a0); // { dg-message "note: the ABI of passing empty record has changed in GCC 6" }
+}
diff --git a/gcc/testsuite/g++.dg/pr60336-4.C b/gcc/testsuite/g++.dg/pr60336-4.C
new file mode 100644
index 0000000..8790a66
--- /dev/null
+++ b/gcc/testsuite/g++.dg/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/pr68355.C b/gcc/testsuite/g++.dg/pr68355.C
new file mode 100644
index 0000000..1354fc4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/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-*-* } } }
diff --git a/gcc/tree-core.h b/gcc/tree-core.h
index 9cc64d9..67bffa4 100644
--- a/gcc/tree-core.h
+++ b/gcc/tree-core.h
@@ -1086,6 +1086,9 @@ struct GTY(()) tree_base {
        FORCED_LABEL in
            LABEL_DECL
 
+       TYPE_EMPTY_RECORD in
+           all types
+
    volatile_flag:
 
        TREE_THIS_VOLATILE in
diff --git a/gcc/tree-dfa.c b/gcc/tree-dfa.c
index bb5cd49..1634ed6 100644
--- a/gcc/tree-dfa.c
+++ b/gcc/tree-dfa.c
@@ -394,6 +394,8 @@ get_ref_base_and_extent (tree exp, HOST_WIDE_INT *poffset,
       machine_mode mode = TYPE_MODE (TREE_TYPE (exp));
       if (mode == BLKmode)
 	size_tree = TYPE_SIZE (TREE_TYPE (exp));
+      else if (type_is_empty_record_p (TREE_TYPE (exp)))
+	bitsize = 0;
       else
 	bitsize = int (GET_MODE_PRECISION (mode));
     }
diff --git a/gcc/tree-streamer-in.c b/gcc/tree-streamer-in.c
index 3162d1a..2aee7ad 100644
--- a/gcc/tree-streamer-in.c
+++ b/gcc/tree-streamer-in.c
@@ -112,7 +112,10 @@ unpack_ts_base_value_fields (struct bitpack_d *bp, tree expr)
       TREE_PUBLIC (expr) = (unsigned) bp_unpack_value (bp, 1);
     }
   else
-    bp_unpack_value (bp, 4);
+    {
+      TYPE_EMPTY_RECORD (expr) = (unsigned) bp_unpack_value (bp, 1);
+      bp_unpack_value (bp, 3);
+    }
   TREE_ADDRESSABLE (expr) = (unsigned) bp_unpack_value (bp, 1);
   TREE_THIS_VOLATILE (expr) = (unsigned) bp_unpack_value (bp, 1);
   if (DECL_P (expr))
diff --git a/gcc/tree-streamer-out.c b/gcc/tree-streamer-out.c
index bfd0644..4306fcc 100644
--- a/gcc/tree-streamer-out.c
+++ b/gcc/tree-streamer-out.c
@@ -83,7 +83,10 @@ pack_ts_base_value_fields (struct bitpack_d *bp, tree expr)
       bp_pack_value (bp, TREE_PUBLIC (expr), 1);
     }
   else
-    bp_pack_value (bp, 0, 4);
+    {
+      bp_pack_value (bp, TYPE_EMPTY_RECORD (expr), 1);
+      bp_pack_value (bp, 0, 3);
+    }
   bp_pack_value (bp, TREE_ADDRESSABLE (expr), 1);
   bp_pack_value (bp, TREE_THIS_VOLATILE (expr), 1);
   if (DECL_P (expr))
diff --git a/gcc/tree.h b/gcc/tree.h
index 96ffa83..8af745f 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -766,6 +766,10 @@ extern void omp_clause_range_check_failed (const_tree, const char *, int,
    computed gotos.  */
 #define FORCED_LABEL(NODE) (LABEL_DECL_CHECK (NODE)->base.side_effects_flag)
 
+/* Nonzero in a type considered an empty record.  */
+#define TYPE_EMPTY_RECORD(NODE) \
+  (TYPE_CHECK (NODE)->base.side_effects_flag)
+
 /* Nonzero means this expression is volatile in the C sense:
    its address should be of type `volatile WHATEVER *'.
    In other words, the declared item is volatile qualified.
@@ -5379,6 +5383,14 @@ get_finish (location_t loc)
   return get_range_from_loc (line_table, loc).m_finish;
 }
 
+/* Return true if type T is an empty record.  */
+
+static inline bool
+type_is_empty_record_p (const_tree t)
+{
+  return TYPE_EMPTY_RECORD (TYPE_MAIN_VARIANT (t));
+}
+
 extern location_t set_block (location_t loc, tree block);
 
 extern void gt_ggc_mx (tree &);
diff --git a/gcc/ubsan.c b/gcc/ubsan.c
index 6fc6233..6dafc90 100644
--- a/gcc/ubsan.c
+++ b/gcc/ubsan.c
@@ -379,10 +379,11 @@ ubsan_type_descriptor (tree type, enum ubsan_print_style pstyle)
 
   if (pstyle == UBSAN_PRINT_POINTER)
     {
-      pp_printf (&pretty_name, "'%s%s%s%s%s%s%s",
+      pp_printf (&pretty_name, "'%s%s%s%s%s%s%s%s",
 		 TYPE_VOLATILE (type2) ? "volatile " : "",
 		 TYPE_READONLY (type2) ? "const " : "",
 		 TYPE_RESTRICT (type2) ? "restrict " : "",
+		 TYPE_EMPTY_RECORD (type2) ? "empty-record " : "",
 		 TYPE_ATOMIC (type2) ? "_Atomic " : "",
 		 TREE_CODE (type2) == RECORD_TYPE
 		 ? "struct "
diff --git a/gcc/var-tracking.c b/gcc/var-tracking.c
index 07eea84..670b475 100644
--- a/gcc/var-tracking.c
+++ b/gcc/var-tracking.c
@@ -6135,10 +6135,10 @@ prepare_call_arguments (basic_block bb, rtx_insn *insn)
 		  rtx reg;
 		  INIT_CUMULATIVE_ARGS (args_so_far_v, type, NULL_RTX, fndecl,
 					nargs + 1);
-		  reg = targetm.calls.function_arg (args_so_far, mode,
-						    struct_addr, true);
-		  targetm.calls.function_arg_advance (args_so_far, mode,
-						      struct_addr, true);
+		  reg = function_arg (args_so_far, mode, struct_addr,
+				      true);
+		  function_arg_advance (args_so_far, mode, struct_addr,
+					true);
 		  if (reg == NULL_RTX)
 		    {
 		      for (; link; link = XEXP (link, 1))
@@ -6159,8 +6159,8 @@ prepare_call_arguments (basic_block bb, rtx_insn *insn)
 		  machine_mode mode;
 		  t = TYPE_ARG_TYPES (type);
 		  mode = TYPE_MODE (TREE_VALUE (t));
-		  this_arg = targetm.calls.function_arg (args_so_far, mode,
-							 TREE_VALUE (t), true);
+		  this_arg = function_arg (args_so_far, mode,
+					   TREE_VALUE (t), true);
 		  if (this_arg && !REG_P (this_arg))
 		    this_arg = NULL_RTX;
 		  else if (this_arg == NULL_RTX)
@@ -6275,8 +6275,7 @@ prepare_call_arguments (basic_block bb, rtx_insn *insn)
 		argtype = build_pointer_type (argtype);
 		mode = TYPE_MODE (argtype);
 	      }
-	    reg = targetm.calls.function_arg (args_so_far, mode,
-					      argtype, true);
+	    reg = function_arg (args_so_far, mode, argtype, true);
 	    if (TREE_CODE (argtype) == REFERENCE_TYPE
 		&& INTEGRAL_TYPE_P (TREE_TYPE (argtype))
 		&& reg
@@ -6330,8 +6329,7 @@ prepare_call_arguments (basic_block bb, rtx_insn *insn)
 			}
 		  }
 	      }
-	    targetm.calls.function_arg_advance (args_so_far, mode,
-						argtype, true);
+	    function_arg_advance (args_so_far, mode, argtype, true);
 	    t = TREE_CHAIN (t);
 	  }
       }
-- 
2.5.0


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

* Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
  2015-12-14 22:08                       ` H.J. Lu
@ 2016-01-26 19:27                         ` Jason Merrill
  2016-01-26 19:52                           ` H.J. Lu
  0 siblings, 1 reply; 52+ messages in thread
From: Jason Merrill @ 2016-01-26 19:27 UTC (permalink / raw)
  To: H.J. Lu; +Cc: GCC Patches, Jakub Jelinek, Richard Biener, Markus Trippelsdorf

On 12/14/2015 05:08 PM, H.J. Lu wrote:
> +  if (abi_version_at_least (10))
> +    TYPE_EMPTY_RECORD (t) = is_really_empty_class (t);

This should use is_empty_class or CLASSTYPE_EMPTY_P.  We don't want to 
change how classes with just a vptr are passed.

Otherwise, it looks OK to me.

Jason

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

* Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
  2016-01-26 19:27                         ` Jason Merrill
@ 2016-01-26 19:52                           ` H.J. Lu
  2016-01-26 20:23                             ` Marc Glisse
  0 siblings, 1 reply; 52+ messages in thread
From: H.J. Lu @ 2016-01-26 19:52 UTC (permalink / raw)
  To: Jason Merrill
  Cc: GCC Patches, Jakub Jelinek, Richard Biener, Markus Trippelsdorf

On Tue, Jan 26, 2016 at 11:27 AM, Jason Merrill <jason@redhat.com> wrote:
> On 12/14/2015 05:08 PM, H.J. Lu wrote:
>>
>> +  if (abi_version_at_least (10))
>> +    TYPE_EMPTY_RECORD (t) = is_really_empty_class (t);
>
>
> This should use is_empty_class or CLASSTYPE_EMPTY_P.  We don't want to
> change how classes with just a vptr are passed.
>
> Otherwise, it looks OK to me.

Is true_type an empty class here?  is_empty_class returns false
on this:

[hjl@gnu-skl-1 gcc]$ cat x.cc
struct dummy { };
struct true_type { struct dummy i; };

extern true_type y;
extern void xxx (true_type c);

void
yyy (void)
{
  xxx (y);
}
[hjl@gnu-skl-1 gcc]$


-- 
H.J.

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

* Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
  2016-01-26 19:52                           ` H.J. Lu
@ 2016-01-26 20:23                             ` Marc Glisse
  2016-01-26 20:26                               ` H.J. Lu
  0 siblings, 1 reply; 52+ messages in thread
From: Marc Glisse @ 2016-01-26 20:23 UTC (permalink / raw)
  To: H.J. Lu
  Cc: Jason Merrill, GCC Patches, Jakub Jelinek, Richard Biener,
	Markus Trippelsdorf

On Tue, 26 Jan 2016, H.J. Lu wrote:

> On Tue, Jan 26, 2016 at 11:27 AM, Jason Merrill <jason@redhat.com> wrote:
>> On 12/14/2015 05:08 PM, H.J. Lu wrote:
>>>
>>> +  if (abi_version_at_least (10))
>>> +    TYPE_EMPTY_RECORD (t) = is_really_empty_class (t);
>>
>>
>> This should use is_empty_class or CLASSTYPE_EMPTY_P.  We don't want to
>> change how classes with just a vptr are passed.
>>
>> Otherwise, it looks OK to me.
>
> Is true_type an empty class here?  is_empty_class returns false
> on this:

It isn't empty in the usual C++ sense (we can't apply the empty base 
optimization to something that derives from it, for instance), or the one 
described in the itanium ABI (the relevant one here I guess). On the other 
hand, it is rather useless to pass it by value, so a different notion of 
empty might have been useful when the ABI was defined...

> [hjl@gnu-skl-1 gcc]$ cat x.cc
> struct dummy { };
> struct true_type { struct dummy i; };
>
> extern true_type y;
> extern void xxx (true_type c);
>
> void
> yyy (void)
> {
>  xxx (y);
> }
> [hjl@gnu-skl-1 gcc]$

-- 
Marc Glisse

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

* Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
  2016-01-26 20:23                             ` Marc Glisse
@ 2016-01-26 20:26                               ` H.J. Lu
  2016-01-26 20:44                                 ` Marc Glisse
  0 siblings, 1 reply; 52+ messages in thread
From: H.J. Lu @ 2016-01-26 20:26 UTC (permalink / raw)
  To: GCC Patches
  Cc: Jason Merrill, Jakub Jelinek, Richard Biener, Markus Trippelsdorf

On Tue, Jan 26, 2016 at 12:23 PM, Marc Glisse <marc.glisse@inria.fr> wrote:
> On Tue, 26 Jan 2016, H.J. Lu wrote:
>
>> On Tue, Jan 26, 2016 at 11:27 AM, Jason Merrill <jason@redhat.com> wrote:
>>>
>>> On 12/14/2015 05:08 PM, H.J. Lu wrote:
>>>>
>>>>
>>>> +  if (abi_version_at_least (10))
>>>> +    TYPE_EMPTY_RECORD (t) = is_really_empty_class (t);
>>>
>>>
>>>
>>> This should use is_empty_class or CLASSTYPE_EMPTY_P.  We don't want to
>>> change how classes with just a vptr are passed.
>>>
>>> Otherwise, it looks OK to me.
>>
>>
>> Is true_type an empty class here?  is_empty_class returns false
>> on this:
>
>
> It isn't empty in the usual C++ sense (we can't apply the empty base
> optimization to something that derives from it, for instance), or the one
> described in the itanium ABI (the relevant one here I guess). On the other
> hand, it is rather useless to pass it by value, so a different notion of

llvm/clang treats it as empty class and I think it should be treated
as "empty" class.

> empty might have been useful when the ABI was defined...

I proposed to update x86-64 psABI:

https://groups.google.com/forum/#!topic/x86-64-abi/VTE-LJ9VnDk

>
>> [hjl@gnu-skl-1 gcc]$ cat x.cc
>> struct dummy { };
>> struct true_type { struct dummy i; };
>>
>> extern true_type y;
>> extern void xxx (true_type c);
>>
>> void
>> yyy (void)
>> {
>>  xxx (y);
>> }
>> [hjl@gnu-skl-1 gcc]$
>
>
> --
> Marc Glisse



-- 
H.J.

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

* Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
  2016-01-26 20:26                               ` H.J. Lu
@ 2016-01-26 20:44                                 ` Marc Glisse
  2016-01-26 21:21                                   ` H.J. Lu
  0 siblings, 1 reply; 52+ messages in thread
From: Marc Glisse @ 2016-01-26 20:44 UTC (permalink / raw)
  To: H.J. Lu
  Cc: GCC Patches, Jason Merrill, Jakub Jelinek, Richard Biener,
	Markus Trippelsdorf

On Tue, 26 Jan 2016, H.J. Lu wrote:

> On Tue, Jan 26, 2016 at 12:23 PM, Marc Glisse <marc.glisse@inria.fr> wrote:
>> On Tue, 26 Jan 2016, H.J. Lu wrote:
>>
>>> On Tue, Jan 26, 2016 at 11:27 AM, Jason Merrill <jason@redhat.com> wrote:
>>>>
>>>> On 12/14/2015 05:08 PM, H.J. Lu wrote:
>>>>>
>>>>>
>>>>> +  if (abi_version_at_least (10))
>>>>> +    TYPE_EMPTY_RECORD (t) = is_really_empty_class (t);
>>>>
>>>>
>>>>
>>>> This should use is_empty_class or CLASSTYPE_EMPTY_P.  We don't want to
>>>> change how classes with just a vptr are passed.
>>>>
>>>> Otherwise, it looks OK to me.
>>>
>>>
>>> Is true_type an empty class here?  is_empty_class returns false
>>> on this:
>>
>>
>> It isn't empty in the usual C++ sense (we can't apply the empty base
>> optimization to something that derives from it, for instance), or the one
>> described in the itanium ABI (the relevant one here I guess). On the other
>> hand, it is rather useless to pass it by value, so a different notion of
>
> llvm/clang treats it as empty class and I think it should be treated
> as "empty" class.

Is it still empty if there are several empty members? Is there a clear 
definition somewhere of what empty means? I guess it makes sense to 
recursively allow "empty" members for this purpose.

>> empty might have been useful when the ABI was defined...
>
> I proposed to update x86-64 psABI:
>
> https://groups.google.com/forum/#!topic/x86-64-abi/VTE-LJ9VnDk

Does the full document have a definition of empty anywhere?

>>> [hjl@gnu-skl-1 gcc]$ cat x.cc
>>> struct dummy { };
>>> struct true_type { struct dummy i; };
>>>
>>> extern true_type y;
>>> extern void xxx (true_type c);
>>>
>>> void
>>> yyy (void)
>>> {
>>>  xxx (y);
>>> }
>>> [hjl@gnu-skl-1 gcc]$
>>
>>
>> --
>> Marc Glisse

-- 
Marc Glisse

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

* Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
  2016-01-26 20:44                                 ` Marc Glisse
@ 2016-01-26 21:21                                   ` H.J. Lu
  2016-01-26 21:40                                     ` Jakub Jelinek
  0 siblings, 1 reply; 52+ messages in thread
From: H.J. Lu @ 2016-01-26 21:21 UTC (permalink / raw)
  To: GCC Patches
  Cc: Jason Merrill, Jakub Jelinek, Richard Biener, Markus Trippelsdorf

On Tue, Jan 26, 2016 at 12:44 PM, Marc Glisse <marc.glisse@inria.fr> wrote:
> On Tue, 26 Jan 2016, H.J. Lu wrote:
>
>> On Tue, Jan 26, 2016 at 12:23 PM, Marc Glisse <marc.glisse@inria.fr>
>> wrote:
>>>
>>> On Tue, 26 Jan 2016, H.J. Lu wrote:
>>>
>>>> On Tue, Jan 26, 2016 at 11:27 AM, Jason Merrill <jason@redhat.com>
>>>> wrote:
>>>>>
>>>>>
>>>>> On 12/14/2015 05:08 PM, H.J. Lu wrote:
>>>>>>
>>>>>>
>>>>>>
>>>>>> +  if (abi_version_at_least (10))
>>>>>> +    TYPE_EMPTY_RECORD (t) = is_really_empty_class (t);
>>>>>
>>>>>
>>>>>
>>>>>
>>>>> This should use is_empty_class or CLASSTYPE_EMPTY_P.  We don't want to
>>>>> change how classes with just a vptr are passed.
>>>>>
>>>>> Otherwise, it looks OK to me.
>>>>
>>>>
>>>>
>>>> Is true_type an empty class here?  is_empty_class returns false
>>>> on this:
>>>
>>>
>>>
>>> It isn't empty in the usual C++ sense (we can't apply the empty base
>>> optimization to something that derives from it, for instance), or the one
>>> described in the itanium ABI (the relevant one here I guess). On the
>>> other
>>> hand, it is rather useless to pass it by value, so a different notion of
>>
>>
>> llvm/clang treats it as empty class and I think it should be treated
>> as "empty" class.
>
>
> Is it still empty if there are several empty members? Is there a clear
> definition somewhere of what empty means? I guess it makes sense to
> recursively allow "empty" members for this purpose.

Like this:

/* Returns true if TYPE is POD for the purpose of layout and an empty
   class or an class with empty classes.  */

static bool
is_empty_record (tree type)
{
  if (type == error_mark_node)
    return false;

  if (!CLASS_TYPE_P (type))
    return false;

  if (CLASSTYPE_NON_LAYOUT_POD_P (type))
    return false;

  gcc_assert (COMPLETE_TYPE_P (type));

  if (CLASSTYPE_EMPTY_P (type))
    return true;

  tree field;

  for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
    if (TREE_CODE (field) == FIELD_DECL
        && !DECL_ARTIFICIAL (field)
        && !is_empty_record (TREE_TYPE (field)))
      return false;

  return true;
}

-- 
H.J.

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

* Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
  2016-01-26 21:21                                   ` H.J. Lu
@ 2016-01-26 21:40                                     ` Jakub Jelinek
  2016-01-26 22:21                                       ` H.J. Lu
  0 siblings, 1 reply; 52+ messages in thread
From: Jakub Jelinek @ 2016-01-26 21:40 UTC (permalink / raw)
  To: H.J. Lu; +Cc: GCC Patches, Jason Merrill, Richard Biener, Markus Trippelsdorf

On Tue, Jan 26, 2016 at 01:21:52PM -0800, H.J. Lu wrote:
> Like this:
> 
> /* Returns true if TYPE is POD for the purpose of layout and an empty
>    class or an class with empty classes.  */
> 
> static bool
> is_empty_record (tree type)
> {
>   if (type == error_mark_node)
>     return false;
> 
>   if (!CLASS_TYPE_P (type))
>     return false;
> 
>   if (CLASSTYPE_NON_LAYOUT_POD_P (type))
>     return false;
> 
>   gcc_assert (COMPLETE_TYPE_P (type));
> 
>   if (CLASSTYPE_EMPTY_P (type))
>     return true;
> 
>   tree field;
> 
>   for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
>     if (TREE_CODE (field) == FIELD_DECL
>         && !DECL_ARTIFICIAL (field)
>         && !is_empty_record (TREE_TYPE (field)))
>       return false;
> 
>   return true;
> }

So you say that K1 in e.g.:

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 K1 { J1 a; J2 b; };
int v;
__attribute__((noinline, noclone))
K1 foo (int a, K1 x, int b)
{
  v = a + b;
  return x;
}
K1 k, m;
void
bar (void)
{
  m = foo (1, k, 2);
}

is empty class?  What does clang do with this?

	Jakub

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

* Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
  2016-01-26 21:40                                     ` Jakub Jelinek
@ 2016-01-26 22:21                                       ` H.J. Lu
  2016-01-27  8:10                                         ` Marc Glisse
  0 siblings, 1 reply; 52+ messages in thread
From: H.J. Lu @ 2016-01-26 22:21 UTC (permalink / raw)
  To: Jakub Jelinek
  Cc: GCC Patches, Jason Merrill, Richard Biener, Markus Trippelsdorf

On Tue, Jan 26, 2016 at 1:40 PM, Jakub Jelinek <jakub@redhat.com> wrote:
> On Tue, Jan 26, 2016 at 01:21:52PM -0800, H.J. Lu wrote:
>> Like this:
>>
>> /* Returns true if TYPE is POD for the purpose of layout and an empty
>>    class or an class with empty classes.  */
>>
>> static bool
>> is_empty_record (tree type)
>> {
>>   if (type == error_mark_node)
>>     return false;
>>
>>   if (!CLASS_TYPE_P (type))
>>     return false;
>>
>>   if (CLASSTYPE_NON_LAYOUT_POD_P (type))
>>     return false;
>>
>>   gcc_assert (COMPLETE_TYPE_P (type));
>>
>>   if (CLASSTYPE_EMPTY_P (type))
>>     return true;
>>
>>   tree field;
>>
>>   for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
>>     if (TREE_CODE (field) == FIELD_DECL
>>         && !DECL_ARTIFICIAL (field)
>>         && !is_empty_record (TREE_TYPE (field)))
>>       return false;
>>
>>   return true;
>> }
>
> So you say that K1 in e.g.:
>
> 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 K1 { J1 a; J2 b; };
> int v;
> __attribute__((noinline, noclone))
> K1 foo (int a, K1 x, int b)
> {
>   v = a + b;
>   return x;
> }
> K1 k, m;
> void
> bar (void)
> {
>   m = foo (1, k, 2);
> }
>
> is empty class?  What does clang do with this?
>
>         Jakub

Revised:

/* Returns true if TYPE is POD of one-byte or less in size for the purpose
   of layout and an empty class or an class with empty classes.  */

static bool
is_empty_record (tree type)
{
  if (type == error_mark_node)
    return false;

  if (!CLASS_TYPE_P (type))
    return false;

  if (CLASSTYPE_NON_LAYOUT_POD_P (type))
    return false;

  gcc_assert (COMPLETE_TYPE_P (type));

  if (CLASSTYPE_EMPTY_P (type))
    return true;

  if (int_size_in_bytes (type) > 1)
    return false;

  tree field;

  for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
    if (TREE_CODE (field) == FIELD_DECL
        && !DECL_ARTIFICIAL (field)
        && !is_empty_record (TREE_TYPE (field)))
      return false;

  return true;
}


-- 
H.J.

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

* Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
  2016-01-26 22:21                                       ` H.J. Lu
@ 2016-01-27  8:10                                         ` Marc Glisse
  2016-01-27  8:21                                           ` Jakub Jelinek
  0 siblings, 1 reply; 52+ messages in thread
From: Marc Glisse @ 2016-01-27  8:10 UTC (permalink / raw)
  To: H.J. Lu
  Cc: Jakub Jelinek, GCC Patches, Jason Merrill, Richard Biener,
	Markus Trippelsdorf

On Tue, 26 Jan 2016, H.J. Lu wrote:

> On Tue, Jan 26, 2016 at 1:40 PM, Jakub Jelinek <jakub@redhat.com> wrote:
>> On Tue, Jan 26, 2016 at 01:21:52PM -0800, H.J. Lu wrote:
>>> Like this:
>>>
>>> /* Returns true if TYPE is POD for the purpose of layout and an empty
>>>    class or an class with empty classes.  */
>>>
>>> static bool
>>> is_empty_record (tree type)
>>> {
>>>   if (type == error_mark_node)
>>>     return false;
>>>
>>>   if (!CLASS_TYPE_P (type))
>>>     return false;
>>>
>>>   if (CLASSTYPE_NON_LAYOUT_POD_P (type))
>>>     return false;
>>>
>>>   gcc_assert (COMPLETE_TYPE_P (type));
>>>
>>>   if (CLASSTYPE_EMPTY_P (type))
>>>     return true;
>>>
>>>   tree field;
>>>
>>>   for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
>>>     if (TREE_CODE (field) == FIELD_DECL
>>>         && !DECL_ARTIFICIAL (field)
>>>         && !is_empty_record (TREE_TYPE (field)))
>>>       return false;
>>>
>>>   return true;
>>> }
>>
>> So you say that K1 in e.g.:
>>
>> 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 K1 { J1 a; J2 b; };
>> int v;
>> __attribute__((noinline, noclone))
>> K1 foo (int a, K1 x, int b)
>> {
>>   v = a + b;
>>   return x;
>> }
>> K1 k, m;
>> void
>> bar (void)
>> {
>>   m = foo (1, k, 2);
>> }
>>
>> is empty class?  What does clang do with this?
>>
>>         Jakub
>
> Revised:
>
> /* Returns true if TYPE is POD of one-byte or less in size for the purpose
>   of layout and an empty class or an class with empty classes.  */
>
> static bool
> is_empty_record (tree type)
> {
>  if (type == error_mark_node)
>    return false;
>
>  if (!CLASS_TYPE_P (type))
>    return false;
>
>  if (CLASSTYPE_NON_LAYOUT_POD_P (type))
>    return false;
>
>  gcc_assert (COMPLETE_TYPE_P (type));
>
>  if (CLASSTYPE_EMPTY_P (type))
>    return true;
>
>  if (int_size_in_bytes (type) > 1)
>    return false;

That's completely arbitrary :-(

If we are defining a new ABI, I preferred your previous version (at least 
the idea, I didn't check the code). If we are trying to follow clang, then 
we need either a clear English definition from a clang developer or at 
least someone should check what conditions they use in the code. Trying to 
infer it from a couple examples sounds too fragile for an ABI decision.

>  tree field;
>
>  for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
>    if (TREE_CODE (field) == FIELD_DECL
>        && !DECL_ARTIFICIAL (field)
>        && !is_empty_record (TREE_TYPE (field)))
>      return false;
>
>  return true;
> }

-- 
Marc Glisse

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

* Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
  2016-01-27  8:10                                         ` Marc Glisse
@ 2016-01-27  8:21                                           ` Jakub Jelinek
  2016-01-27  9:03                                             ` Marc Glisse
  0 siblings, 1 reply; 52+ messages in thread
From: Jakub Jelinek @ 2016-01-27  8:21 UTC (permalink / raw)
  To: gcc-patches; +Cc: H.J. Lu, Jason Merrill, Richard Biener, Markus Trippelsdorf

On Wed, Jan 27, 2016 at 09:10:14AM +0100, Marc Glisse wrote:
> >Revised:
> >
> >/* Returns true if TYPE is POD of one-byte or less in size for the purpose
> >  of layout and an empty class or an class with empty classes.  */
> >
> >static bool
> >is_empty_record (tree type)
> >{
> > if (type == error_mark_node)
> >   return false;
> >
> > if (!CLASS_TYPE_P (type))
> >   return false;
> >
> > if (CLASSTYPE_NON_LAYOUT_POD_P (type))
> >   return false;
> >
> > gcc_assert (COMPLETE_TYPE_P (type));
> >
> > if (CLASSTYPE_EMPTY_P (type))
> >   return true;
> >
> > if (int_size_in_bytes (type) > 1)
> >   return false;
> 
> That's completely arbitrary :-(

Yeah.  Because (adapted to be compilable with C):
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 K1 { struct J1 a; struct J2 b; };
int v;
__attribute__((noinline, noclone))
struct K1 foo (int a, struct K1 x, int b)
{
  v = a + b;
  return x;
}
struct K1 k, m;
void
bar (void)
{
  m = foo (1, k, 2);
}
then would have a different calling convention between C and C++,
so where is the argument that we change anything just to make the two
compatible?  Though, of course, those two will never be compatible,
it is enough to add struct L1 { int a; struct K1 b; int c; }; and
that structure has 1024+8 bytes in C++ and 8 bytes in C.
As clang generates different code for the above between C and C++, it
clearly special cases for some reason just the most common case.
IMHO it is not worth to change GCC ABI...

	Jakub

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

* Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
  2016-01-27  8:21                                           ` Jakub Jelinek
@ 2016-01-27  9:03                                             ` Marc Glisse
  2016-01-27 13:46                                               ` H.J. Lu
  0 siblings, 1 reply; 52+ messages in thread
From: Marc Glisse @ 2016-01-27  9:03 UTC (permalink / raw)
  To: Jakub Jelinek
  Cc: gcc-patches, H.J. Lu, Jason Merrill, Richard Biener, Markus Trippelsdorf

On Wed, 27 Jan 2016, Jakub Jelinek wrote:

> On Wed, Jan 27, 2016 at 09:10:14AM +0100, Marc Glisse wrote:
>>> Revised:
>>>
>>> /* Returns true if TYPE is POD of one-byte or less in size for the purpose
>>>  of layout and an empty class or an class with empty classes.  */
>>>
>>> static bool
>>> is_empty_record (tree type)
>>> {
>>> if (type == error_mark_node)
>>>   return false;
>>>
>>> if (!CLASS_TYPE_P (type))
>>>   return false;
>>>
>>> if (CLASSTYPE_NON_LAYOUT_POD_P (type))
>>>   return false;
>>>
>>> gcc_assert (COMPLETE_TYPE_P (type));
>>>
>>> if (CLASSTYPE_EMPTY_P (type))
>>>   return true;
>>>
>>> if (int_size_in_bytes (type) > 1)
>>>   return false;
>>
>> That's completely arbitrary :-(
>
> Yeah.  Because (adapted to be compilable with C):
> 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 K1 { struct J1 a; struct J2 b; };
> int v;
> __attribute__((noinline, noclone))
> struct K1 foo (int a, struct K1 x, int b)
> {
>  v = a + b;
>  return x;
> }
> struct K1 k, m;
> void
> bar (void)
> {
>  m = foo (1, k, 2);
> }
> then would have a different calling convention between C and C++,
> so where is the argument that we change anything just to make the two
> compatible?  Though, of course, those two will never be compatible,
> it is enough to add struct L1 { int a; struct K1 b; int c; }; and
> that structure has 1024+8 bytes in C++ and 8 bytes in C.

I don't know how empty classes are used in C in practice, but it could 
make sense to have ABI compatibility as long as no empty struct is used as 
a member of another struct (I also suggested an attribute to let 
C++ use the same layout as C here: PR63579). But then the usual definition 
of empty would be sufficient.

> As clang generates different code for the above between C and C++, it
> clearly special cases for some reason just the most common case.
> IMHO it is not worth to change GCC ABI...

I was interested in this change because it improves C++, C compatibility 
was a convenient excuse ;-)

-- 
Marc Glisse

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

* Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
  2016-01-27  9:03                                             ` Marc Glisse
@ 2016-01-27 13:46                                               ` H.J. Lu
  2016-01-27 15:39                                                 ` H.J. Lu
  0 siblings, 1 reply; 52+ messages in thread
From: H.J. Lu @ 2016-01-27 13:46 UTC (permalink / raw)
  To: GCC Patches
  Cc: Jakub Jelinek, Jason Merrill, Richard Biener, Markus Trippelsdorf

On Wed, Jan 27, 2016 at 1:03 AM, Marc Glisse <marc.glisse@inria.fr> wrote:
> On Wed, 27 Jan 2016, Jakub Jelinek wrote:
>
>> On Wed, Jan 27, 2016 at 09:10:14AM +0100, Marc Glisse wrote:
>>>>
>>>> Revised:
>>>>
>>>> /* Returns true if TYPE is POD of one-byte or less in size for the
>>>> purpose
>>>>  of layout and an empty class or an class with empty classes.  */
>>>>
>>>> static bool
>>>> is_empty_record (tree type)
>>>> {
>>>> if (type == error_mark_node)
>>>>   return false;
>>>>
>>>> if (!CLASS_TYPE_P (type))
>>>>   return false;
>>>>
>>>> if (CLASSTYPE_NON_LAYOUT_POD_P (type))
>>>>   return false;
>>>>
>>>> gcc_assert (COMPLETE_TYPE_P (type));
>>>>
>>>> if (CLASSTYPE_EMPTY_P (type))
>>>>   return true;
>>>>
>>>> if (int_size_in_bytes (type) > 1)
>>>>   return false;
>>>
>>>
>>> That's completely arbitrary :-(
>>
>>
>> Yeah.  Because (adapted to be compilable with C):
>> 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 K1 { struct J1 a; struct J2 b; };
>> int v;
>> __attribute__((noinline, noclone))
>> struct K1 foo (int a, struct K1 x, int b)
>> {
>>  v = a + b;
>>  return x;
>> }
>> struct K1 k, m;
>> void
>> bar (void)
>> {
>>  m = foo (1, k, 2);
>> }
>> then would have a different calling convention between C and C++,
>> so where is the argument that we change anything just to make the two
>> compatible?  Though, of course, those two will never be compatible,
>> it is enough to add struct L1 { int a; struct K1 b; int c; }; and
>> that structure has 1024+8 bytes in C++ and 8 bytes in C.
>
>
> I don't know how empty classes are used in C in practice, but it could make
> sense to have ABI compatibility as long as no empty struct is used as a
> member of another struct (I also suggested an attribute to let C++ use the
> same layout as C here: PR63579). But then the usual definition of empty
> would be sufficient.
>
>> As clang generates different code for the above between C and C++, it
>> clearly special cases for some reason just the most common case.
>> IMHO it is not worth to change GCC ABI...
>
>
> I was interested in this change because it improves C++, C compatibility was
> a convenient excuse ;-)
>

I opened  a clang bug:

https://llvm.org/bugs/show_bug.cgi?id=26337

I propose the following definitions:

i. An empty record is:
   1. A class without member.  Or
   2. An array of empty records.  Or
   3. A class with empty records.
ii. An empty record type for parameter passing is POD for the purpose of layout
and
   1. A class without member.  Or
   2. A class with empty records.

/* An empty record is:
   1. A class without member.  Or
   2. An array of empty records.  Or
   3. A class with empty records.  */

/* Returns true if TYPE is an empty record or an array of empty records.  */

static bool
is_empty_record_or_array_of_empty_record (tree type)
{
  if (CLASS_TYPE_P (type))
    {
      if (CLASSTYPE_EMPTY_P (type))
        return true;

      tree field;

      for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
        if (TREE_CODE (field) == FIELD_DECL
            && !DECL_ARTIFICIAL (field)
            && !is_empty_record_or_array_of_empty_record (TREE_TYPE (field)))
          return false;
      return true;
    }
  else if (TREE_CODE (type) == ARRAY_TYPE)
    return is_empty_record_or_array_of_empty_record (TREE_TYPE (type));
  return false;
}

/* Returns true if TYPE is POD for the purpose of layout and
   1. A class without member.  Or
   2. A class with empty records.  */

static bool
is_empty_record_for_parm (tree type)
{
  if (type == error_mark_node)
    return false;

  if (!CLASS_TYPE_P (type))
    return false;

  if (CLASSTYPE_NON_LAYOUT_POD_P (type))
    return false;

  gcc_assert (COMPLETE_TYPE_P (type));

  if (CLASSTYPE_EMPTY_P (type))
    return true;

  tree field;

  for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
    if (TREE_CODE (field) == FIELD_DECL
        && !DECL_ARTIFICIAL (field)
        && !is_empty_record_or_array_of_empty_record (TREE_TYPE (field)))
      return false;

  return true;
}


-- 
H.J.

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

* Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
  2016-01-27 13:46                                               ` H.J. Lu
@ 2016-01-27 15:39                                                 ` H.J. Lu
  2016-03-01  1:02                                                   ` Jason Merrill
  0 siblings, 1 reply; 52+ messages in thread
From: H.J. Lu @ 2016-01-27 15:39 UTC (permalink / raw)
  To: GCC Patches
  Cc: Jakub Jelinek, Jason Merrill, Richard Biener, Markus Trippelsdorf

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

On Wed, Jan 27, 2016 at 5:46 AM, H.J. Lu <hjl.tools@gmail.com> wrote:
> On Wed, Jan 27, 2016 at 1:03 AM, Marc Glisse <marc.glisse@inria.fr> wrote:
>> On Wed, 27 Jan 2016, Jakub Jelinek wrote:
>>
>>> On Wed, Jan 27, 2016 at 09:10:14AM +0100, Marc Glisse wrote:
>>>>>
>>>>> Revised:
>>>>>
>>>>> /* Returns true if TYPE is POD of one-byte or less in size for the
>>>>> purpose
>>>>>  of layout and an empty class or an class with empty classes.  */
>>>>>
>>>>> static bool
>>>>> is_empty_record (tree type)
>>>>> {
>>>>> if (type == error_mark_node)
>>>>>   return false;
>>>>>
>>>>> if (!CLASS_TYPE_P (type))
>>>>>   return false;
>>>>>
>>>>> if (CLASSTYPE_NON_LAYOUT_POD_P (type))
>>>>>   return false;
>>>>>
>>>>> gcc_assert (COMPLETE_TYPE_P (type));
>>>>>
>>>>> if (CLASSTYPE_EMPTY_P (type))
>>>>>   return true;
>>>>>
>>>>> if (int_size_in_bytes (type) > 1)
>>>>>   return false;
>>>>
>>>>
>>>> That's completely arbitrary :-(
>>>
>>>
>>> Yeah.  Because (adapted to be compilable with C):
>>> 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 K1 { struct J1 a; struct J2 b; };
>>> int v;
>>> __attribute__((noinline, noclone))
>>> struct K1 foo (int a, struct K1 x, int b)
>>> {
>>>  v = a + b;
>>>  return x;
>>> }
>>> struct K1 k, m;
>>> void
>>> bar (void)
>>> {
>>>  m = foo (1, k, 2);
>>> }
>>> then would have a different calling convention between C and C++,
>>> so where is the argument that we change anything just to make the two
>>> compatible?  Though, of course, those two will never be compatible,
>>> it is enough to add struct L1 { int a; struct K1 b; int c; }; and
>>> that structure has 1024+8 bytes in C++ and 8 bytes in C.
>>
>>
>> I don't know how empty classes are used in C in practice, but it could make
>> sense to have ABI compatibility as long as no empty struct is used as a
>> member of another struct (I also suggested an attribute to let C++ use the
>> same layout as C here: PR63579). But then the usual definition of empty
>> would be sufficient.
>>
>>> As clang generates different code for the above between C and C++, it
>>> clearly special cases for some reason just the most common case.
>>> IMHO it is not worth to change GCC ABI...
>>
>>
>> I was interested in this change because it improves C++, C compatibility was
>> a convenient excuse ;-)
>>
>
> I opened  a clang bug:
>
> https://llvm.org/bugs/show_bug.cgi?id=26337
>
> I propose the following definitions:
>
> i. An empty record is:
>    1. A class without member.  Or
>    2. An array of empty records.  Or
>    3. A class with empty records.
> ii. An empty record type for parameter passing is POD for the purpose of layout
> and
>    1. A class without member.  Or
>    2. A class with empty records.
>
> /* An empty record is:
>    1. A class without member.  Or
>    2. An array of empty records.  Or
>    3. A class with empty records.  */
>
> /* Returns true if TYPE is an empty record or an array of empty records.  */
>
> static bool
> is_empty_record_or_array_of_empty_record (tree type)
> {
>   if (CLASS_TYPE_P (type))
>     {
>       if (CLASSTYPE_EMPTY_P (type))
>         return true;
>
>       tree field;
>
>       for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
>         if (TREE_CODE (field) == FIELD_DECL
>             && !DECL_ARTIFICIAL (field)
>             && !is_empty_record_or_array_of_empty_record (TREE_TYPE (field)))
>           return false;
>       return true;
>     }
>   else if (TREE_CODE (type) == ARRAY_TYPE)
>     return is_empty_record_or_array_of_empty_record (TREE_TYPE (type));
>   return false;
> }
>
> /* Returns true if TYPE is POD for the purpose of layout and
>    1. A class without member.  Or
>    2. A class with empty records.  */
>
> static bool
> is_empty_record_for_parm (tree type)
> {
>   if (type == error_mark_node)
>     return false;
>
>   if (!CLASS_TYPE_P (type))
>     return false;
>
>   if (CLASSTYPE_NON_LAYOUT_POD_P (type))
>     return false;
>
>   gcc_assert (COMPLETE_TYPE_P (type));
>
>   if (CLASSTYPE_EMPTY_P (type))
>     return true;
>
>   tree field;
>
>   for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
>     if (TREE_CODE (field) == FIELD_DECL
>         && !DECL_ARTIFICIAL (field)
>         && !is_empty_record_or_array_of_empty_record (TREE_TYPE (field)))
>       return false;
>
>   return true;
> }
>

Here is the updated patch with new testcases.  OK for trunk?


-- 
H.J.

[-- Attachment #2: 0001-Properly-pass-C-empty-class.patch --]
[-- Type: text/x-patch, Size: 16071 bytes --]

From 9a3aacb5b95b317a10e65b4ed80d3f490660b171 Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@gmail.com>
Date: Wed, 9 Dec 2015 12:48:07 -0800
Subject: [PATCH] Properly pass C++ empty class

Use 0 for empty record size in TARGET_GIMPLIFY_VA_ARG_EXPR.

	PR c++/60336
	PR middle-end/67239
	PR target/68355
	* config/aarch64/aarch64.c (aarch64_gimplify_va_arg_expr): Use 0
	for empty record size.
	* config/mep/mep.c (mep_gimplify_va_arg_expr): Likewise.
	* config/mips/mips.c (mips_std_gimplify_va_arg_expr): Likewise.
	(mips_gimplify_va_arg_expr): Likewise.
	* config/msp430/msp430.c (msp430_gimplify_va_arg_expr): Likewise.
	* config/pa/pa.c (hppa_gimplify_va_arg_expr): Likewise.
	* config/rs6000/rs6000.c (rs6000_gimplify_va_arg): Likewise.
	* config/s390/s390.c (s390_gimplify_va_arg): Likewise.
	* config/sh/sh.c (sh_gimplify_va_arg_expr): Likewise.
	* config/sparc/sparc.c (sparc_gimplify_va_arg): Likewise.
	* config/spu/spu.c (spu_gimplify_va_arg_expr): Likewise.
	* config/stormy16/stormy16.c (xstormy16_gimplify_va_arg_expr):
	Likewise.
	* config/visium/visium.c (visium_gimplify_va_arg): Likewise.
	* config/xtensa/xtensa.c (xtensa_gimplify_va_arg_expr): Likewise.
	* config/alpha/alpha.c (alpha_setup_incoming_varargs): Replace
	targetm.calls.function_arg_advance with function_arg_advance.
	(alpha_gimplify_va_arg_1): Use 0 for empty record size.
	* config/microblaze/microblaze.c (microblaze_expand_prologue):
	Replace targetm.calls.function_arg with function_arg.  Replace
	targetm.calls.function_arg_advance with function_arg_advance.
	* config/tilegx/tilegx.c (tilegx_setup_incoming_varargs): Replace
	targetm.calls.function_arg_advance with function_arg_advance.
	(tilegx_gimplify_va_arg_expr): Use 0 for empty record size.
	* config/tilepro/tilepro.c (tilepro_setup_incoming_varargs):
	Replace targetm.calls.function_arg_advance with
	function_arg_advance.
	(tilepro_gimplify_va_arg_expr): Use 0 for empty record size.
---
 gcc/config/aarch64/aarch64.c       |  3 ++-
 gcc/config/alpha/alpha.c           |  6 +++---
 gcc/config/mep/mep.c               |  3 ++-
 gcc/config/microblaze/microblaze.c | 11 +++++------
 gcc/config/mips/mips.c             |  6 ++++--
 gcc/config/msp430/msp430.c         |  3 ++-
 gcc/config/pa/pa.c                 |  3 ++-
 gcc/config/rs6000/rs6000.c         |  3 ++-
 gcc/config/s390/s390.c             |  3 ++-
 gcc/config/sh/sh.c                 |  3 ++-
 gcc/config/sparc/sparc.c           |  3 ++-
 gcc/config/spu/spu.c               |  3 ++-
 gcc/config/stormy16/stormy16.c     |  6 ++++--
 gcc/config/tilegx/tilegx.c         |  7 ++++---
 gcc/config/tilepro/tilepro.c       |  7 ++++---
 gcc/config/visium/visium.c         |  3 ++-
 gcc/config/xtensa/xtensa.c         |  3 ++-
 17 files changed, 46 insertions(+), 30 deletions(-)

diff --git a/gcc/config/aarch64/aarch64.c b/gcc/config/aarch64/aarch64.c
index df3dec0..43fd394 100644
--- a/gcc/config/aarch64/aarch64.c
+++ b/gcc/config/aarch64/aarch64.c
@@ -9605,7 +9605,8 @@ aarch64_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
 
   stack = build3 (COMPONENT_REF, TREE_TYPE (f_stack), unshare_expr (valist),
 		  f_stack, NULL_TREE);
-  size = int_size_in_bytes (type);
+  bool empty_record = type && type_is_empty_record_p (type);
+  size = empty_record ? 0 : int_size_in_bytes (type);
   align = aarch64_function_arg_alignment (mode, type) / BITS_PER_UNIT;
 
   dw_align = false;
diff --git a/gcc/config/alpha/alpha.c b/gcc/config/alpha/alpha.c
index e023d3b..c3c0513 100644
--- a/gcc/config/alpha/alpha.c
+++ b/gcc/config/alpha/alpha.c
@@ -6117,8 +6117,7 @@ alpha_setup_incoming_varargs (cumulative_args_t pcum, machine_mode mode,
   CUMULATIVE_ARGS cum = *get_cumulative_args (pcum);
 
   /* Skip the current argument.  */
-  targetm.calls.function_arg_advance (pack_cumulative_args (&cum), mode, type,
-				      true);
+  function_arg_advance (pack_cumulative_args (&cum), mode, type, true);
 
 #if TARGET_ABI_OPEN_VMS
   /* For VMS, we allocate space for all 6 arg registers plus a count.
@@ -6304,7 +6303,8 @@ alpha_gimplify_va_arg_1 (tree type, tree base, tree offset,
   gimple_seq_add_seq (pre_p, internal_post);
 
   /* Update the offset field.  */
-  type_size = TYPE_SIZE_UNIT (TYPE_MAIN_VARIANT (type));
+  bool empty_record = type && type_is_empty_record_p (type);
+  type_size = empty_record ? 0 : TYPE_SIZE_UNIT (TYPE_MAIN_VARIANT (type));
   if (type_size == NULL || TREE_OVERFLOW (type_size))
     t = size_zero_node;
   else
diff --git a/gcc/config/mep/mep.c b/gcc/config/mep/mep.c
index 9c4cd86..f63ad57 100644
--- a/gcc/config/mep/mep.c
+++ b/gcc/config/mep/mep.c
@@ -3525,7 +3525,8 @@ mep_gimplify_va_arg_expr (tree valist, tree type,
 
   ivc2_vec = TARGET_IVC2 && VECTOR_TYPE_P (type);
 
-  size = int_size_in_bytes (type);
+  bool empty_record = type && type_is_empty_record_p (type);
+  size = empty_record ? 0 : int_size_in_bytes (type);
   by_reference = (size > (ivc2_vec ? 8 : 4)) || (size <= 0);
 
   if (by_reference)
diff --git a/gcc/config/microblaze/microblaze.c b/gcc/config/microblaze/microblaze.c
index baff67a..e144ff1 100644
--- a/gcc/config/microblaze/microblaze.c
+++ b/gcc/config/microblaze/microblaze.c
@@ -2798,8 +2798,8 @@ microblaze_expand_prologue (void)
 	  passed_mode = Pmode;
 	}
 
-      entry_parm = targetm.calls.function_arg (args_so_far, passed_mode,
-					       passed_type, true);
+      entry_parm = function_arg (args_so_far, passed_mode, passed_type,
+				 true);
 
       if (entry_parm)
 	{
@@ -2819,8 +2819,7 @@ microblaze_expand_prologue (void)
 	  break;
 	}
 
-      targetm.calls.function_arg_advance (args_so_far, passed_mode,
-					  passed_type, true);
+      function_arg_advance (args_so_far, passed_mode, passed_type, true);
 
       next_arg = TREE_CHAIN (cur_arg);
       if (next_arg == 0)
@@ -2834,8 +2833,8 @@ microblaze_expand_prologue (void)
 
   /* Split parallel insn into a sequence of insns.  */
 
-  next_arg_reg = targetm.calls.function_arg (args_so_far, VOIDmode,
-					     void_type_node, true);
+  next_arg_reg = function_arg (args_so_far, VOIDmode, void_type_node,
+			       true);
   if (next_arg_reg != 0 && GET_CODE (next_arg_reg) == PARALLEL)
     {
       rtvec adjust = XVEC (next_arg_reg, 0);
diff --git a/gcc/config/mips/mips.c b/gcc/config/mips/mips.c
index 890e947..d9d0c59 100644
--- a/gcc/config/mips/mips.c
+++ b/gcc/config/mips/mips.c
@@ -6308,7 +6308,8 @@ mips_std_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
     }
 
   /* Compute the rounded size of the type.  */
-  type_size = size_in_bytes (type);
+  bool empty_record = type && type_is_empty_record_p (type);
+  type_size = empty_record ? 0 : size_in_bytes (type);
   rounded_size = round_up (type_size, align);
 
   /* Reduce rounded_size so it's sharable with the postqueue.  */
@@ -6397,7 +6398,8 @@ mips_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
 
       ovfl = build3 (COMPONENT_REF, TREE_TYPE (f_ovfl), valist, f_ovfl,
 		     NULL_TREE);
-      size = int_size_in_bytes (type);
+      bool empty_record = type && type_is_empty_record_p (type);
+      size = empty_record ? 0 : int_size_in_bytes (type);
 
       if (GET_MODE_CLASS (TYPE_MODE (type)) == MODE_FLOAT
 	  && GET_MODE_SIZE (TYPE_MODE (type)) <= UNITS_PER_FPVALUE)
diff --git a/gcc/config/msp430/msp430.c b/gcc/config/msp430/msp430.c
index 182ca59..6b1d5ca 100644
--- a/gcc/config/msp430/msp430.c
+++ b/gcc/config/msp430/msp430.c
@@ -1451,7 +1451,8 @@ msp430_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
     }
 
   /* Compute the rounded size of the type.  */
-  type_size = size_in_bytes (type);
+  bool empty_record = type && type_is_empty_record_p (type);
+  type_size = empty_record ? 0 : size_in_bytes (type);
   rounded_size = round_up (type_size, align);
 
   /* Reduce rounded_size so it's sharable with the postqueue.  */
diff --git a/gcc/config/pa/pa.c b/gcc/config/pa/pa.c
index 8b1c832..104dc88 100644
--- a/gcc/config/pa/pa.c
+++ b/gcc/config/pa/pa.c
@@ -6342,7 +6342,8 @@ hppa_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
 	  type = ptr;
 	  ptr = build_pointer_type (type);
 	}
-      size = int_size_in_bytes (type);
+      bool empty_record = type && type_is_empty_record_p (type);
+      size = empty_record ? 0 : int_size_in_bytes (type);
       valist_type = TREE_TYPE (valist);
 
       /* Args grow down.  Not handled by generic routines.  */
diff --git a/gcc/config/rs6000/rs6000.c b/gcc/config/rs6000/rs6000.c
index 7b9201a..bca478a 100644
--- a/gcc/config/rs6000/rs6000.c
+++ b/gcc/config/rs6000/rs6000.c
@@ -12074,7 +12074,8 @@ rs6000_gimplify_va_arg (tree valist, tree type, gimple_seq *pre_p,
   sav = build3 (COMPONENT_REF, TREE_TYPE (f_sav), unshare_expr (valist),
 		f_sav, NULL_TREE);
 
-  size = int_size_in_bytes (type);
+  bool empty_record = type && type_is_empty_record_p (type);
+  size = empty_record ? 0 : int_size_in_bytes (type);
   rsize = (size + 3) / 4;
   align = 1;
 
diff --git a/gcc/config/s390/s390.c b/gcc/config/s390/s390.c
index 3be64de..b2de2f0 100644
--- a/gcc/config/s390/s390.c
+++ b/gcc/config/s390/s390.c
@@ -11630,7 +11630,8 @@ s390_gimplify_va_arg (tree valist, tree type, gimple_seq *pre_p,
   valist = unshare_expr (valist);
   ovf = build3 (COMPONENT_REF, TREE_TYPE (f_ovf), valist, f_ovf, NULL_TREE);
 
-  size = int_size_in_bytes (type);
+  bool empty_record = type && type_is_empty_record_p (type);
+  size = empty_record ? 0 : int_size_in_bytes (type);
 
   s390_check_type_for_vector_abi (type, true, false);
 
diff --git a/gcc/config/sh/sh.c b/gcc/config/sh/sh.c
index 0b18ce5..3977f12 100644
--- a/gcc/config/sh/sh.c
+++ b/gcc/config/sh/sh.c
@@ -8765,7 +8765,8 @@ sh_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
   if (pass_by_ref)
     type = build_pointer_type (type);
 
-  size = int_size_in_bytes (type);
+  bool empty_record = type && type_is_empty_record_p (type);
+  size = empty_record ? 0 : int_size_in_bytes (type);
   rsize = (size + UNITS_PER_WORD - 1) & -UNITS_PER_WORD;
   pptr_type_node = build_pointer_type (ptr_type_node);
 
diff --git a/gcc/config/sparc/sparc.c b/gcc/config/sparc/sparc.c
index 71609f2..3dae250 100644
--- a/gcc/config/sparc/sparc.c
+++ b/gcc/config/sparc/sparc.c
@@ -7412,7 +7412,8 @@ sparc_gimplify_va_arg (tree valist, tree type, gimple_seq *pre_p,
   else
     {
       indirect = false;
-      size = int_size_in_bytes (type);
+      bool empty_record = type && type_is_empty_record_p (type);
+      size = empty_record ? 0 : int_size_in_bytes (type);
       rsize = ROUND_UP (size, UNITS_PER_WORD);
       align = 0;
 
diff --git a/gcc/config/spu/spu.c b/gcc/config/spu/spu.c
index 401c295..cafcd86 100644
--- a/gcc/config/spu/spu.c
+++ b/gcc/config/spu/spu.c
@@ -4028,7 +4028,8 @@ spu_gimplify_va_arg_expr (tree valist, tree type, gimple_seq * pre_p,
 					   false);
   if (pass_by_reference_p)
     type = build_pointer_type (type);
-  size = int_size_in_bytes (type);
+  bool empty_record = type && type_is_empty_record_p (type);
+  size = empty_record ? 0 : int_size_in_bytes (type);
   rsize = ((size + UNITS_PER_WORD - 1) / UNITS_PER_WORD) * UNITS_PER_WORD;
 
   /* build conditional expression to calculate addr. The expression
diff --git a/gcc/config/stormy16/stormy16.c b/gcc/config/stormy16/stormy16.c
index 50af15b..631bb63 100644
--- a/gcc/config/stormy16/stormy16.c
+++ b/gcc/config/stormy16/stormy16.c
@@ -1337,8 +1337,10 @@ xstormy16_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
   count = build3 (COMPONENT_REF, TREE_TYPE (f_count), valist, f_count,
 		  NULL_TREE);
 
+  bool empty_record = type && type_is_empty_record_p (type);
   must_stack = targetm.calls.must_pass_in_stack (TYPE_MODE (type), type);
-  size_tree = round_up (size_in_bytes (type), UNITS_PER_WORD);
+  size_tree = round_up (empty_record ? integer_zero_node : size_in_bytes (type),
+			UNITS_PER_WORD);
   gimplify_expr (&size_tree, pre_p, NULL, is_gimple_val, fb_rvalue);
 
   size_of_reg_args = NUM_ARGUMENT_REGISTERS * UNITS_PER_WORD;
@@ -1374,7 +1376,7 @@ xstormy16_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
   /* Arguments larger than a word might need to skip over some
      registers, since arguments are either passed entirely in
      registers or entirely on the stack.  */
-  size = PUSH_ROUNDING (int_size_in_bytes (type));
+  size = PUSH_ROUNDING (empty_record ? 0 : int_size_in_bytes (type));
   if (size > 2 || size < 0 || must_stack)
     {
       tree r, u;
diff --git a/gcc/config/tilegx/tilegx.c b/gcc/config/tilegx/tilegx.c
index 06c832c..3c7fe2b 100644
--- a/gcc/config/tilegx/tilegx.c
+++ b/gcc/config/tilegx/tilegx.c
@@ -396,8 +396,8 @@ tilegx_setup_incoming_varargs (cumulative_args_t cum,
   /* The caller has advanced CUM up to, but not beyond, the last named
      argument.  Advance a local copy of CUM past the last "real" named
      argument, to find out how many registers are left over.  */
-  targetm.calls.function_arg_advance (pack_cumulative_args (&local_cum),
-				      mode, type, true);
+  function_arg_advance (pack_cumulative_args (&local_cum), mode, type,
+			true);
   first_reg = local_cum;
 
   if (local_cum < TILEGX_NUM_ARG_REGS)
@@ -473,7 +473,8 @@ tilegx_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
   if (pass_by_reference_p)
     type = build_pointer_type (type);
 
-  size = int_size_in_bytes (type);
+  bool empty_record = type && type_is_empty_record_p (type);
+  size = empty_record ? 0 : int_size_in_bytes (type);
   rsize = ((size + UNITS_PER_WORD - 1) / UNITS_PER_WORD) * UNITS_PER_WORD;
 
   /* If the alignment of the type is greater than the default for a
diff --git a/gcc/config/tilepro/tilepro.c b/gcc/config/tilepro/tilepro.c
index 628cd04..d1d00cf 100644
--- a/gcc/config/tilepro/tilepro.c
+++ b/gcc/config/tilepro/tilepro.c
@@ -348,8 +348,8 @@ tilepro_setup_incoming_varargs (cumulative_args_t cum,
   /* The caller has advanced CUM up to, but not beyond, the last named
      argument.  Advance a local copy of CUM past the last "real" named
      argument, to find out how many registers are left over.  */
-  targetm.calls.function_arg_advance (pack_cumulative_args (&local_cum),
-				      mode, type, true);
+  function_arg_advance (pack_cumulative_args (&local_cum), mode, type,
+			true);
   first_reg = local_cum;
 
   if (local_cum < TILEPRO_NUM_ARG_REGS)
@@ -421,7 +421,8 @@ tilepro_gimplify_va_arg_expr (tree valist, tree type, gimple_seq * pre_p,
   if (pass_by_reference_p)
     type = build_pointer_type (type);
 
-  size = int_size_in_bytes (type);
+  bool empty_record = type && type_is_empty_record_p (type);
+  size = empty_record ? 0 : int_size_in_bytes (type);
   rsize = ((size + UNITS_PER_WORD - 1) / UNITS_PER_WORD) * UNITS_PER_WORD;
 
   /* If the alignment of the type is greater than the default for a
diff --git a/gcc/config/visium/visium.c b/gcc/config/visium/visium.c
index 7881b0e..d225a6d 100644
--- a/gcc/config/visium/visium.c
+++ b/gcc/config/visium/visium.c
@@ -1529,7 +1529,8 @@ visium_gimplify_va_arg (tree valist, tree type, gimple_seq *pre_p,
       return build_va_arg_indirect_ref (t);
     }
 
-  size = int_size_in_bytes (type);
+  bool empty_record = type && type_is_empty_record_p (type);
+  size = empty_record ? 0 : int_size_in_bytes (type);
   rsize = (size + UNITS_PER_WORD - 1) & -UNITS_PER_WORD;
   f_ovfl = TYPE_FIELDS (va_list_type_node);
   f_gbase = TREE_CHAIN (f_ovfl);
diff --git a/gcc/config/xtensa/xtensa.c b/gcc/config/xtensa/xtensa.c
index 64d089b..a725a8b 100644
--- a/gcc/config/xtensa/xtensa.c
+++ b/gcc/config/xtensa/xtensa.c
@@ -3161,7 +3161,8 @@ xtensa_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
   ndx = build3 (COMPONENT_REF, TREE_TYPE (f_ndx), unshare_expr (valist),
 		f_ndx, NULL_TREE);
 
-  type_size = size_in_bytes (type);
+  bool empty_record = type && type_is_empty_record_p (type);
+  type_size = empty_record ? 0 : size_in_bytes (type);
   va_size = round_up (type_size, UNITS_PER_WORD);
   gimplify_expr (&va_size, pre_p, NULL, is_gimple_val, fb_rvalue);
 
-- 
2.5.0


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

* Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
  2016-01-27 15:39                                                 ` H.J. Lu
@ 2016-03-01  1:02                                                   ` Jason Merrill
  2016-03-01 22:44                                                     ` H.J. Lu
  0 siblings, 1 reply; 52+ messages in thread
From: Jason Merrill @ 2016-03-01  1:02 UTC (permalink / raw)
  To: H.J. Lu, GCC Patches; +Cc: Jakub Jelinek, Richard Biener, Markus Trippelsdorf

On 01/27/2016 10:39 AM, H.J. Lu wrote:
> Here is the updated patch with new testcases.  OK for trunk?

This is not a complete patch.

Please update type_is_empty_record_p to use the definition from the 
recent discussion:

> An empty type is a type where it and all of its subobjects (recursively)
> are of class, structure, union, or array type.

This shouldn't need to rely on the front-end setting a flag.  Be sure to 
ignore unnamed bit-fields, as they are not subobjects.

I would also suggest having this function abort if the type is 
TREE_ADDRESSABLE.

Jason

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

* Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
  2016-03-01  1:02                                                   ` Jason Merrill
@ 2016-03-01 22:44                                                     ` H.J. Lu
  2016-03-02 16:25                                                       ` Ulrich Weigand
  0 siblings, 1 reply; 52+ messages in thread
From: H.J. Lu @ 2016-03-01 22:44 UTC (permalink / raw)
  To: Jason Merrill
  Cc: GCC Patches, Jakub Jelinek, Richard Biener, Markus Trippelsdorf

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

On Mon, Feb 29, 2016 at 5:02 PM, Jason Merrill <jason@redhat.com> wrote:
> On 01/27/2016 10:39 AM, H.J. Lu wrote:
>>
>> Here is the updated patch with new testcases.  OK for trunk?
>
>
> This is not a complete patch.
>
> Please update type_is_empty_record_p to use the definition from the recent
> discussion:

Here are 3 patches.  If all targets should follow x86 backends
on passing empty types, only first 2 patches are needed.  Otherwise
the 3rd patch is needed to limit this to x86 targets.

>> An empty type is a type where it and all of its subobjects (recursively)
>> are of class, structure, union, or array type.
>
>
> This shouldn't need to rely on the front-end setting a flag.  Be sure to
> ignore unnamed bit-fields, as they are not subobjects.

Done.

> I would also suggest having this function abort if the type is
> TREE_ADDRESSABLE.
>

I have

bool
type_is_empty_type_p (const_tree type)
{
...
 if (TREE_ADDRESSABLE (type))
    return false;
...
}

since any type may be passed to it.

Tested on x86-64 with

RUNTESTFLAGS="--target_board='unix{-mx32,-m32,}'"

OK for trunk?

Thanks.

-- 
H.J.

[-- Attachment #2: 0001-Properly-pass-empty-type-for-i386-targets.patch --]
[-- Type: text/x-patch, Size: 51670 bytes --]

From 49bf71d9e6983b20150061ccd7332d4b73309cad 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 1/3] Properly pass empty type for i386 targets

According to x86 psABIs, no memory slot nor register should be used to
pass or return an object of empty type whose address isn't needed.
Middle-end and x86 backend are updated to ignore empty types, whose
address aren't needed, for parameter passing and function value return.
Other targets need similar changes if they want to follow x86 psABIs.

get_ref_base_and_extent is changed to set bitsize to 0 for empty types
so that when ref_maybe_used_by_call_p_1 calls get_ref_base_and_extent to
get 0 as the maximum size on empty type.  Otherwise, find_tail_calls
won't perform tail call optimization for functions with empty type
parameters, as shown in g++.dg/pr68355.C.

This ABI change is enabled only if the ABI level is at least 10, which
is updated in GCC 6.

gcc/

	PR c++/60336
	PR middle-end/67239
	PR target/68355
	* calls.c (initialize_argument_information): Warn empty type
	if they are used in a variable argument list or aren't the last
	arguments.  Replace targetm.calls.function_arg,
	targetm.calls.function_incoming_arg and
	targetm.calls.function_arg_advance with function_arg,
	function_incoming_arg and function_arg_advance.
	(expand_call): Replace targetm.calls.function_arg,
	targetm.calls.function_incoming_arg and
	targetm.calls.function_arg_advance with function_arg,
	function_incoming_arg and function_arg_advance.
	(emit_library_call_value_1): Likewise.
	(store_one_arg): Use 0 for empty type size.  Don't
	push 0 size argument onto stack.
	(must_pass_in_stack_var_size_or_pad): Return false for empty
	type.
	* dse.c (get_call_args): Replace targetm.calls.function_arg
	and targetm.calls.function_arg_advance with function_arg and
	function_arg_advance.
	* explow.c (hard_function_value): Use 0 for empty type size.
	* expr.c (block_move_libcall_safe_for_call_parm): Replace
	targetm.calls.function_arg and targetm.calls.function_arg_advance
	with function_arg and function_arg_advance.
	(copy_blkmode_to_reg): Use 0 for empty type size.
	* function.c (aggregate_value_p): Replace
	targetm.calls.return_in_memory with return_in_memory.
	(assign_parm_data_all): Add warn_empty_type.
	(assign_parms_augmented_arg_list): Set warn_empty_type if
	empty types are used in a variable argument list or aren't
	the last arguments.
	(assign_parm_find_entry_rtl): Warn empty type if warn_empty_type
	is set.  Replace
	targetm.calls.function_incoming_arg with function_incoming_arg.
	(assign_parms): Only warn empty type once.  Replace
	targetm.calls.function_arg_advance with function_arg_advance.
	(gimplify_parameters): Replace targetm.calls.function_arg_advance
	with function_arg_advance.
	(locate_and_pad_parm): Use 0 for empty type size.
	(warn_empty_type): New function.
	(function_arg_advance): New wrapper function.
	(function_arg): Likewise.
	(function_incoming_arg): Likewise.
	(return_in_memory): Likewise.
	* target.h (function_arg_advance): New prototype.
	(function_arg): Likewise.
	(function_incoming_arg): Likewise.
	(return_in_memory): Likewise.
	* targhooks.c (std_gimplify_va_arg_expr): Use 0 for empty type
	size.
	* tree-dfa.c (get_ref_base_and_extent): Likewise.
	* tree.c (is_empty_type): New functiom.
	(type_is_empty_type_p): Likewise.
	* tree.h (type_is_empty_type_p): New prototype.
	* var-tracking.c (prepare_call_arguments): Replace
	targetm.calls.function_arg and targetm.calls.function_arg_advance
	with function_arg and function_arg_advance.
	* config/i386/i386.c (ix86_gimplify_va_arg): Use 0 for empty
	type size.

gcc/testsuite/

	PR c++/60336
	PR middle-end/67239
	PR target/68355
	* g++.dg/abi/empty12.C: New test.
	* g++.dg/abi/empty12.h: Likewise.
	* g++.dg/abi/empty12a.c: Likewise.
	* g++.dg/abi/empty13.C: Likewise.
	* g++.dg/abi/empty13.h: Likewise.
	* g++.dg/abi/empty13a.c: Likewise.
	* g++.dg/abi/empty14.C: Likewise.
	* g++.dg/abi/empty14.h: Likewise.
	* g++.dg/abi/empty14a.c: Likewise.
	* g++.dg/abi/empty15.C: Likewise.
	* g++.dg/abi/empty15.h: Likewise.
	* g++.dg/abi/empty15a.c: Likewise.
	* g++.dg/abi/empty16.C: Likewise.
	* g++.dg/abi/empty16.h: Likewise.
	* g++.dg/abi/empty16a.c: Likewise.
	* g++.dg/abi/empty17.C: Likewise.
	* g++.dg/abi/empty17.h: Likewise.
	* g++.dg/abi/empty17a.c: Likewise.
	* 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/calls.c                         |  99 +++++++++++++++++++----------
 gcc/config/i386/i386.c              |   3 +-
 gcc/dse.c                           |   4 +-
 gcc/explow.c                        |   4 +-
 gcc/expr.c                          |   8 +--
 gcc/function.c                      | 123 ++++++++++++++++++++++++++++++++----
 gcc/target.h                        |   8 +++
 gcc/targhooks.c                     |   5 +-
 gcc/testsuite/g++.dg/abi/empty12.C  |  17 +++++
 gcc/testsuite/g++.dg/abi/empty12.h  |   9 +++
 gcc/testsuite/g++.dg/abi/empty12a.c |   6 ++
 gcc/testsuite/g++.dg/abi/empty13.C  |  17 +++++
 gcc/testsuite/g++.dg/abi/empty13.h  |   9 +++
 gcc/testsuite/g++.dg/abi/empty13a.c |   6 ++
 gcc/testsuite/g++.dg/abi/empty14.C  |  17 +++++
 gcc/testsuite/g++.dg/abi/empty14.h  |  10 +++
 gcc/testsuite/g++.dg/abi/empty14a.c |   6 ++
 gcc/testsuite/g++.dg/abi/empty15.C  |  17 +++++
 gcc/testsuite/g++.dg/abi/empty15.h  |  30 +++++++++
 gcc/testsuite/g++.dg/abi/empty15a.c |   6 ++
 gcc/testsuite/g++.dg/abi/empty16.C  |  17 +++++
 gcc/testsuite/g++.dg/abi/empty16.h  |  16 +++++
 gcc/testsuite/g++.dg/abi/empty16a.c |   6 ++
 gcc/testsuite/g++.dg/abi/empty17.C  |  17 +++++
 gcc/testsuite/g++.dg/abi/empty17.h  |  27 ++++++++
 gcc/testsuite/g++.dg/abi/empty17a.c |   6 ++
 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 +++++++
 gcc/tree-dfa.c                      |   4 +-
 gcc/tree.c                          |  39 ++++++++++++
 gcc/tree.h                          |   2 +
 gcc/var-tracking.c                  |  18 +++---
 43 files changed, 898 insertions(+), 67 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/abi/empty12.C
 create mode 100644 gcc/testsuite/g++.dg/abi/empty12.h
 create mode 100644 gcc/testsuite/g++.dg/abi/empty12a.c
 create mode 100644 gcc/testsuite/g++.dg/abi/empty13.C
 create mode 100644 gcc/testsuite/g++.dg/abi/empty13.h
 create mode 100644 gcc/testsuite/g++.dg/abi/empty13a.c
 create mode 100644 gcc/testsuite/g++.dg/abi/empty14.C
 create mode 100644 gcc/testsuite/g++.dg/abi/empty14.h
 create mode 100644 gcc/testsuite/g++.dg/abi/empty14a.c
 create mode 100644 gcc/testsuite/g++.dg/abi/empty15.C
 create mode 100644 gcc/testsuite/g++.dg/abi/empty15.h
 create mode 100644 gcc/testsuite/g++.dg/abi/empty15a.c
 create mode 100644 gcc/testsuite/g++.dg/abi/empty16.C
 create mode 100644 gcc/testsuite/g++.dg/abi/empty16.h
 create mode 100644 gcc/testsuite/g++.dg/abi/empty16a.c
 create mode 100644 gcc/testsuite/g++.dg/abi/empty17.C
 create mode 100644 gcc/testsuite/g++.dg/abi/empty17.h
 create mode 100644 gcc/testsuite/g++.dg/abi/empty17a.c
 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/calls.c b/gcc/calls.c
index 8f573b8..d1b498c 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -1159,7 +1159,13 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
   bitmap_obstack_initialize (NULL);
 
   /* In this loop, we consider args in the order they are written.
-     We fill up ARGS from the back.  */
+     We fill up ARGS from the back.  Warn empty type if they are used
+     in a variable argument list or they aren't the last arguments.
+     Set warn_empty_type to true if we don't warn empty type to
+     avoid walking arguments.  */
+  bool seen_empty_type = false;
+  bool warn_empty_type
+    = (!warn_psabi || stdarg_p (fndecl ? TREE_TYPE (fndecl) : fntype));
 
   i = num_actuals - 1;
   {
@@ -1192,6 +1198,15 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
       {
 	tree argtype = TREE_TYPE (arg);
 
+	if (!warn_empty_type)
+	  {
+	    if (argtype != error_mark_node
+		&& type_is_empty_type_p (argtype))
+	      seen_empty_type = true;
+	    else if (seen_empty_type)
+	      warn_empty_type = true;
+	  }
+
 	/* Remember last param with pointer and associate it
 	   with following pointer bounds.  */
 	if (CALL_WITH_BOUNDS_P (exp)
@@ -1406,8 +1421,13 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
       args[i].unsignedp = unsignedp;
       args[i].mode = mode;
 
-      args[i].reg = targetm.calls.function_arg (args_so_far, mode, type,
-						argpos < n_named_args);
+      args[i].reg = function_arg (args_so_far, mode, type,
+				  argpos < n_named_args,
+				  warn_empty_type);
+
+      /* Only warn empty type once.  */
+      if (type_is_empty_type_p (type))
+	warn_empty_type = false;
 
       if (args[i].reg && CONST_INT_P (args[i].reg))
 	{
@@ -1420,8 +1440,8 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
 	 arguments have to go into the incoming registers.  */
       if (targetm.calls.function_incoming_arg != targetm.calls.function_arg)
 	args[i].tail_call_reg
-	  = targetm.calls.function_incoming_arg (args_so_far, mode, type,
-						 argpos < n_named_args);
+	  = function_incoming_arg (args_so_far, mode, type,
+				   argpos < n_named_args);
       else
 	args[i].tail_call_reg = args[i].reg;
 
@@ -1482,8 +1502,8 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
       /* Increment ARGS_SO_FAR, which has info about which arg-registers
 	 have been used, etc.  */
 
-      targetm.calls.function_arg_advance (args_so_far, TYPE_MODE (type),
-					  type, argpos < n_named_args);
+      function_arg_advance (args_so_far, TYPE_MODE (type), type,
+			    argpos < n_named_args);
     }
 }
 
@@ -3335,14 +3355,11 @@ expand_call (tree exp, rtx target, int ignore)
       /* Set up next argument register.  For sibling calls on machines
 	 with register windows this should be the incoming register.  */
       if (pass == 0)
-	next_arg_reg = targetm.calls.function_incoming_arg (args_so_far,
-							    VOIDmode,
-							    void_type_node,
-							    true);
+	next_arg_reg = function_incoming_arg (args_so_far, VOIDmode,
+					      void_type_node, true);
       else
-	next_arg_reg = targetm.calls.function_arg (args_so_far,
-						   VOIDmode, void_type_node,
-						   true);
+	next_arg_reg = function_arg (args_so_far, VOIDmode,
+				     void_type_node, true);
 
       if (pass == 1 && (return_flags & ERF_RETURNS_ARG))
 	{
@@ -3954,8 +3971,8 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
       argvec[count].mode = Pmode;
       argvec[count].partial = 0;
 
-      argvec[count].reg = targetm.calls.function_arg (args_so_far,
-						      Pmode, NULL_TREE, true);
+      argvec[count].reg = function_arg (args_so_far, Pmode, NULL_TREE,
+					true);
       gcc_assert (targetm.calls.arg_partial_bytes (args_so_far, Pmode,
 						   NULL_TREE, 1) == 0);
 
@@ -3972,7 +3989,7 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
 	  || reg_parm_stack_space > 0)
 	args_size.constant += argvec[count].locate.size.constant;
 
-      targetm.calls.function_arg_advance (args_so_far, Pmode, (tree) 0, true);
+      function_arg_advance (args_so_far, Pmode, (tree) 0, true);
 
       count++;
     }
@@ -4037,8 +4054,7 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
       mode = promote_function_mode (NULL_TREE, mode, &unsigned_p, NULL_TREE, 0);
       argvec[count].mode = mode;
       argvec[count].value = convert_modes (mode, GET_MODE (val), val, unsigned_p);
-      argvec[count].reg = targetm.calls.function_arg (args_so_far, mode,
-						      NULL_TREE, true);
+      argvec[count].reg = function_arg (args_so_far, mode, NULL_TREE, true);
 
       argvec[count].partial
 	= targetm.calls.arg_partial_bytes (args_so_far, mode, NULL_TREE, 1);
@@ -4067,7 +4083,7 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
 			     GET_MODE_SIZE (mode) <= UNITS_PER_WORD);
 #endif
 
-      targetm.calls.function_arg_advance (args_so_far, mode, (tree) 0, true);
+      function_arg_advance (args_so_far, mode, (tree) 0, true);
     }
 
   /* If this machine requires an external definition for library
@@ -4414,8 +4430,7 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
 	       build_function_type (tfom, NULL_TREE),
 	       original_args_size.constant, args_size.constant,
 	       struct_value_size,
-	       targetm.calls.function_arg (args_so_far,
-					   VOIDmode, void_type_node, true),
+	       function_arg (args_so_far, VOIDmode, void_type_node, true),
 	       valreg,
 	       old_inhibit_defer_pop + 1, call_fusage, flags, args_so_far);
 
@@ -4850,7 +4865,10 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
 	 Note that in C the default argument promotions
 	 will prevent such mismatches.  */
 
-      size = GET_MODE_SIZE (arg->mode);
+      if (type_is_empty_type_p (TREE_TYPE (pval)))
+	size = 0;
+      else
+	size = GET_MODE_SIZE (arg->mode);
       /* Compute how much space the push instruction will push.
 	 On many machines, pushing a byte will advance the stack
 	 pointer by a halfword.  */
@@ -4880,10 +4898,14 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
 
       /* This isn't already where we want it on the stack, so put it there.
 	 This can either be done with push or copy insns.  */
-      if (!emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), NULL_RTX,
-		      parm_align, partial, reg, used - size, argblock,
-		      ARGS_SIZE_RTX (arg->locate.offset), reg_parm_stack_space,
-		      ARGS_SIZE_RTX (arg->locate.alignment_pad), true))
+      if (used
+	  && !emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval),
+			      NULL_RTX, parm_align, partial, reg,
+			      used - size, argblock,
+			      ARGS_SIZE_RTX (arg->locate.offset),
+			      reg_parm_stack_space,
+			      ARGS_SIZE_RTX (arg->locate.alignment_pad),
+			      true))
 	sibcall_failure = 1;
 
       /* Unless this is a partially-in-register argument, the argument is now
@@ -4915,10 +4937,15 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
 	{
 	  /* PUSH_ROUNDING has no effect on us, because emit_push_insn
 	     for BLKmode is careful to avoid it.  */
+	  bool empty_type = type_is_empty_type_p (TREE_TYPE (pval));
 	  excess = (arg->locate.size.constant
-		    - int_size_in_bytes (TREE_TYPE (pval))
+		    - (empty_type
+		       ? 0
+		       : int_size_in_bytes (TREE_TYPE (pval)))
 		    + partial);
-	  size_rtx = expand_expr (size_in_bytes (TREE_TYPE (pval)),
+	  size_rtx = expand_expr ((empty_type
+				   ? size_zero_node
+				   : size_in_bytes (TREE_TYPE (pval))),
 				  NULL_RTX, TYPE_MODE (sizetype),
 				  EXPAND_NORMAL);
 	}
@@ -4993,10 +5020,13 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
 	    }
 	}
 
-      emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), size_rtx,
-		      parm_align, partial, reg, excess, argblock,
-		      ARGS_SIZE_RTX (arg->locate.offset), reg_parm_stack_space,
-		      ARGS_SIZE_RTX (arg->locate.alignment_pad), false);
+      if (!CONST_INT_P (size_rtx) || INTVAL (size_rtx) != 0)
+	emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval),
+			size_rtx, parm_align, partial, reg, excess,
+			argblock, ARGS_SIZE_RTX (arg->locate.offset),
+			reg_parm_stack_space,
+			ARGS_SIZE_RTX (arg->locate.alignment_pad),
+			false);
 
       /* Unless this is a partially-in-register argument, the argument is now
 	 in the stack.
@@ -5074,6 +5104,9 @@ must_pass_in_stack_var_size_or_pad (machine_mode mode, const_tree type)
   if (TREE_ADDRESSABLE (type))
     return true;
 
+  if (type_is_empty_type_p (type))
+    return false;
+
   /* If the padding and mode of the type is such that a copy into
      a register would put it into the wrong part of the register.  */
   if (mode == BLKmode
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index d8a2909..07bb06a 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -10326,7 +10326,8 @@ ix86_gimplify_va_arg (tree valist, tree type, gimple_seq *pre_p,
   indirect_p = pass_by_reference (NULL, TYPE_MODE (type), type, false);
   if (indirect_p)
     type = build_pointer_type (type);
-  size = int_size_in_bytes (type);
+  bool empty_type = type && type_is_empty_type_p (type);
+  size = empty_type ? 0 : int_size_in_bytes (type);
   rsize = CEIL (size, UNITS_PER_WORD);
 
   nat_mode = type_natural_mode (type, NULL, false);
diff --git a/gcc/dse.c b/gcc/dse.c
index eef5644..ae108a8 100644
--- a/gcc/dse.c
+++ b/gcc/dse.c
@@ -2356,7 +2356,7 @@ get_call_args (rtx call_insn, tree fn, rtx *args, int nargs)
     {
       machine_mode mode = TYPE_MODE (TREE_VALUE (arg));
       rtx reg, link, tmp;
-      reg = targetm.calls.function_arg (args_so_far, mode, NULL_TREE, true);
+      reg = function_arg (args_so_far, mode, NULL_TREE, true);
       if (!reg || !REG_P (reg) || GET_MODE (reg) != mode
 	  || GET_MODE_CLASS (mode) != MODE_INT)
 	return false;
@@ -2390,7 +2390,7 @@ get_call_args (rtx call_insn, tree fn, rtx *args, int nargs)
       if (tmp)
 	args[idx] = tmp;
 
-      targetm.calls.function_arg_advance (args_so_far, mode, NULL_TREE, true);
+      function_arg_advance (args_so_far, mode, NULL_TREE, true);
     }
   if (arg != void_list_node || idx != nargs)
     return false;
diff --git a/gcc/explow.c b/gcc/explow.c
index cd7c568..eb2d7a1 100644
--- a/gcc/explow.c
+++ b/gcc/explow.c
@@ -1856,7 +1856,9 @@ hard_function_value (const_tree valtype, const_tree func, const_tree fntype,
   if (REG_P (val)
       && GET_MODE (val) == BLKmode)
     {
-      unsigned HOST_WIDE_INT bytes = int_size_in_bytes (valtype);
+      unsigned HOST_WIDE_INT bytes = (type_is_empty_type_p (valtype)
+				      ? 0
+				      : int_size_in_bytes (valtype));
       machine_mode tmpmode;
 
       /* int_size_in_bytes can return -1.  We don't need a check here
diff --git a/gcc/expr.c b/gcc/expr.c
index 29d22b0..38e9124 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -1199,13 +1199,12 @@ block_move_libcall_safe_for_call_parm (void)
     for ( ; arg != void_list_node ; arg = TREE_CHAIN (arg))
       {
 	machine_mode mode = TYPE_MODE (TREE_VALUE (arg));
-	rtx tmp = targetm.calls.function_arg (args_so_far, mode,
-					      NULL_TREE, true);
+	rtx tmp = function_arg (args_so_far, mode, NULL_TREE, true);
 	if (!tmp || !REG_P (tmp))
 	  return false;
 	if (targetm.calls.arg_partial_bytes (args_so_far, mode, NULL, 1))
 	  return false;
-	targetm.calls.function_arg_advance (args_so_far, mode,
+	function_arg_advance (args_so_far, mode,
 					    NULL_TREE, true);
       }
   }
@@ -2207,7 +2206,8 @@ copy_blkmode_to_reg (machine_mode mode, tree src)
 
   x = expand_normal (src);
 
-  bytes = int_size_in_bytes (TREE_TYPE (src));
+  bytes = (type_is_empty_type_p (TREE_TYPE (src))
+	   ? 0 : int_size_in_bytes (TREE_TYPE (src)));
   if (bytes == 0)
     return NULL_RTX;
 
diff --git a/gcc/function.c b/gcc/function.c
index 1ac8e26..99bd548 100644
--- a/gcc/function.c
+++ b/gcc/function.c
@@ -2078,7 +2078,7 @@ aggregate_value_p (const_tree exp, const_tree fntype)
   if (flag_pcc_struct_return && AGGREGATE_TYPE_P (type))
     return 1;
 
-  if (targetm.calls.return_in_memory (type, fntype))
+  if (return_in_memory (type, fntype))
     return 1;
 
   /* Make sure we have suitable call-clobbered regs to return
@@ -2247,6 +2247,7 @@ struct assign_parm_data_all
   HOST_WIDE_INT pretend_args_size;
   HOST_WIDE_INT extra_pretend_bytes;
   int reg_parm_stack_space;
+  bool warn_empty_type;
 };
 
 struct assign_parm_data_one
@@ -2412,6 +2413,28 @@ assign_parms_augmented_arg_list (struct assign_parm_data_all *all)
   if (targetm.calls.split_complex_arg)
     split_complex_args (&fnargs);
 
+  /* Warn empty type if they are used in a variable argument list or
+     they aren't the last arguments.  Set warn_empty_type to true if
+     we don't warn empty type to avoid walking arguments.  */
+  bool warn_empty_type = !warn_psabi || stdarg_p (fntype);
+  if (!warn_empty_type)
+    {
+      unsigned int i;
+      bool seen_empty_type = false;
+      FOR_EACH_VEC_ELT (fnargs, i, arg)
+	{
+	  tree type = TREE_TYPE (arg);
+	  if (type != error_mark_node && type_is_empty_type_p (type))
+	    seen_empty_type = true;
+	  else if (seen_empty_type)
+	    {
+	      warn_empty_type = true;
+	      break;
+	    }
+	}
+    }
+  all->warn_empty_type = warn_empty_type;
+
   return fnargs;
 }
 
@@ -2528,10 +2551,11 @@ assign_parm_find_entry_rtl (struct assign_parm_data_all *all,
       return;
     }
 
-  entry_parm = targetm.calls.function_incoming_arg (all->args_so_far,
-						    data->promoted_mode,
-						    data->passed_type,
-						    data->named_arg);
+  entry_parm = function_incoming_arg (all->args_so_far,
+				      data->promoted_mode,
+				      data->passed_type,
+				      data->named_arg,
+				      all->warn_empty_type);
 
   if (entry_parm == 0)
     data->promoted_mode = data->passed_mode;
@@ -2555,9 +2579,9 @@ assign_parm_find_entry_rtl (struct assign_parm_data_all *all,
       if (targetm.calls.pretend_outgoing_varargs_named (all->args_so_far))
 	{
 	  rtx tem;
-	  tem = targetm.calls.function_incoming_arg (all->args_so_far,
-						     data->promoted_mode,
-						     data->passed_type, true);
+	  tem = function_incoming_arg (all->args_so_far,
+				       data->promoted_mode,
+				       data->passed_type, true);
 	  in_regs = tem != NULL;
 	}
     }
@@ -3719,6 +3743,10 @@ assign_parms (tree fndecl)
       /* Find out where the parameter arrives in this function.  */
       assign_parm_find_entry_rtl (&all, &data);
 
+      /* Only warn empty type once.  */
+      if (type_is_empty_type_p (data.passed_type))
+	all.warn_empty_type = false;
+
       /* Find out where stack space for this parameter might be.  */
       if (assign_parm_is_stack_parm (&all, &data))
 	{
@@ -3795,8 +3823,8 @@ assign_parms (tree fndecl)
 	}
 
       /* Update info on where next arg arrives in registers.  */
-      targetm.calls.function_arg_advance (all.args_so_far, data.promoted_mode,
-					  data.passed_type, data.named_arg);
+      function_arg_advance (all.args_so_far, data.promoted_mode,
+			    data.passed_type, data.named_arg);
 
       if (POINTER_BOUNDS_TYPE_P (data.passed_type))
 	bound_no++;
@@ -3992,8 +4020,8 @@ gimplify_parameters (void)
 	continue;
 
       /* Update info on where next arg arrives in registers.  */
-      targetm.calls.function_arg_advance (all.args_so_far, data.promoted_mode,
-					  data.passed_type, data.named_arg);
+      function_arg_advance (all.args_so_far, data.promoted_mode,
+			    data.passed_type, data.named_arg);
 
       /* ??? Once upon a time variable_size stuffed parameter list
 	 SAVE_EXPRs (amongst others) onto a pending sizes list.  This
@@ -4136,8 +4164,11 @@ locate_and_pad_parm (machine_mode passed_mode, tree type, int in_regs,
 
   part_size_in_regs = (reg_parm_stack_space == 0 ? partial : 0);
 
-  sizetree
-    = type ? size_in_bytes (type) : size_int (GET_MODE_SIZE (passed_mode));
+  if (type)
+    sizetree = (type_is_empty_type_p (type)
+		? size_zero_node : size_in_bytes (type));
+  else
+    sizetree = size_int (GET_MODE_SIZE (passed_mode));
   where_pad = FUNCTION_ARG_PADDING (passed_mode, type);
   boundary = targetm.calls.function_arg_boundary (passed_mode, type);
   round_boundary = targetm.calls.function_arg_round_boundary (passed_mode,
@@ -6866,5 +6897,69 @@ make_pass_match_asm_constraints (gcc::context *ctxt)
   return new pass_match_asm_constraints (ctxt);
 }
 
+static void
+warn_empty_type (void)
+{
+  if (warn_psabi)
+    inform (input_location, "the ABI of passing empty type has"
+	    " changed in GCC 6");
+}
+
+/* Wrapper for targetm.calls.function_arg_advance.  */
+
+void
+function_arg_advance (cumulative_args_t ca, machine_mode mode,
+		      const_tree type, bool named)
+{
+  if (type && type_is_empty_type_p (type))
+    return;
+
+  targetm.calls.function_arg_advance (ca, mode, type, named);
+}
+
+/* Wrapper for targetm.calls.function_arg.  */
+
+rtx
+function_arg (cumulative_args_t ca, machine_mode mode, const_tree type,
+	      bool named, bool warn_empty_type_p)
+{
+  if (type && type_is_empty_type_p (type))
+    {
+      if (warn_empty_type_p)
+	warn_empty_type ();
+      return NULL;
+    }
+
+  return targetm.calls.function_arg (ca, mode, type, named);
+}
+
+/* Wrapper for targetm.calls.function_incoming_arg.  */
+
+rtx
+function_incoming_arg (cumulative_args_t ca, machine_mode mode,
+		       const_tree type, bool named,
+		       bool warn_empty_type_p)
+{
+  if (type && type_is_empty_type_p (type))
+    {
+      if (warn_empty_type_p)
+	warn_empty_type ();
+      return NULL;
+    }
+
+  return targetm.calls.function_incoming_arg (ca, mode, type, named);
+}
+
+/* Wrapper for targetm.calls.return_in_memory.  */
+
+bool
+return_in_memory (const_tree type, const_tree fntype)
+{
+  if (type && type_is_empty_type_p (type))
+    return false;
+
+  return targetm.calls.return_in_memory (type, fntype);
+}
+
 
 #include "gt-function.h"
diff --git a/gcc/target.h b/gcc/target.h
index 43022bd..3968a1f 100644
--- a/gcc/target.h
+++ b/gcc/target.h
@@ -104,6 +104,14 @@ extern bool target_default_pointer_address_modes_p (void);
    behavior.  */
 extern unsigned int get_move_ratio (bool);
 
+extern void function_arg_advance (cumulative_args_t, machine_mode,
+				  const_tree, bool);
+extern rtx function_arg (cumulative_args_t, machine_mode, const_tree,
+			 bool, bool = false);
+extern rtx function_incoming_arg (cumulative_args_t, machine_mode,
+				  const_tree, bool, bool = false);
+extern bool return_in_memory (const_tree, const_tree);
+
 struct stdarg_info;
 struct spec_info_def;
 struct hard_reg_set_container;
diff --git a/gcc/targhooks.c b/gcc/targhooks.c
index 74af91a..f0a6759 100644
--- a/gcc/targhooks.c
+++ b/gcc/targhooks.c
@@ -1828,9 +1828,12 @@ std_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
   /* Hoist the valist value into a temporary for the moment.  */
   valist_tmp = get_initialized_tmp_var (valist, pre_p, NULL);
 
+  bool empty_type = type_is_empty_type_p (type);
+
   /* va_list pointer is aligned to PARM_BOUNDARY.  If argument actually
      requires greater alignment, we must perform dynamic alignment.  */
   if (boundary > align
+      && !empty_type
       && !integer_zerop (TYPE_SIZE (type)))
     {
       t = build2 (MODIFY_EXPR, TREE_TYPE (valist), valist_tmp,
@@ -1857,7 +1860,7 @@ std_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
     }
 
   /* Compute the rounded size of the type.  */
-  type_size = size_in_bytes (type);
+  type_size = empty_type ? size_zero_node : size_in_bytes (type);
   rounded_size = round_up (type_size, align);
 
   /* Reduce rounded_size so it's sharable with the postqueue.  */
diff --git a/gcc/testsuite/g++.dg/abi/empty12.C b/gcc/testsuite/g++.dg/abi/empty12.C
new file mode 100644
index 0000000..ff2909c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/empty12.C
@@ -0,0 +1,17 @@
+// PR c++/60336
+// { dg-do run }
+// { dg-options "-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-message "note: the ABI of passing empty type has changed in GCC 6" }
+  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..712d4e8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/empty14.C
@@ -0,0 +1,17 @@
+// PR c++/60336
+// { dg-do run }
+// { dg-options "-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-message "note: the ABI of passing empty type has changed in GCC 6" }
+  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..24bf047
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/empty15.C
@@ -0,0 +1,17 @@
+// PR c++/60336
+// { dg-do run }
+// { dg-options "-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-message "note: the ABI of passing empty type has changed in GCC 6" }
+  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..f058720
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/empty16.C
@@ -0,0 +1,17 @@
+// PR c++/60336
+// { dg-do run }
+// { dg-options "-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-message "note: the ABI of passing empty type has changed in GCC 6" }
+  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..f6f88c8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/empty17.C
@@ -0,0 +1,17 @@
+// PR c++/60336
+// { dg-do run }
+// { dg-options "-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-message "note: the ABI of passing empty type has changed in GCC 6" }
+  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/pr60336-1.C b/gcc/testsuite/g++.dg/pr60336-1.C
new file mode 100644
index 0000000..af08638
--- /dev/null
+++ b/gcc/testsuite/g++.dg/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/pr60336-10.C b/gcc/testsuite/g++.dg/pr60336-10.C
new file mode 100644
index 0000000..6c9c990
--- /dev/null
+++ b/gcc/testsuite/g++.dg/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/pr60336-11.C b/gcc/testsuite/g++.dg/pr60336-11.C
new file mode 100644
index 0000000..c92f3d4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/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/pr60336-12.C b/gcc/testsuite/g++.dg/pr60336-12.C
new file mode 100644
index 0000000..83a7bb0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/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/pr60336-2.C b/gcc/testsuite/g++.dg/pr60336-2.C
new file mode 100644
index 0000000..ad63cee
--- /dev/null
+++ b/gcc/testsuite/g++.dg/pr60336-2.C
@@ -0,0 +1,48 @@
+// { dg-do run }
+// { dg-options "-O2" }
+
+#include <stdarg.h>
+
+struct dummy { };
+
+void
+test (struct dummy a, int m, ...) // { dg-message "note: the ABI of passing empty type has changed in GCC 6" }
+{
+  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 "note: the ABI of passing empty type has changed in GCC 6" }
+  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..9ec4914
--- /dev/null
+++ b/gcc/testsuite/g++.dg/pr60336-3.C
@@ -0,0 +1,15 @@
+// { dg-do compile }
+// { dg-options "-O2" }
+
+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); // { dg-message "note: the ABI of passing empty type has changed in GCC 6" }
+  test2 (a0); // { dg-message "note: the ABI of passing empty type has changed in GCC 6" }
+}
diff --git a/gcc/testsuite/g++.dg/pr60336-4.C b/gcc/testsuite/g++.dg/pr60336-4.C
new file mode 100644
index 0000000..8790a66
--- /dev/null
+++ b/gcc/testsuite/g++.dg/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/pr60336-5.C b/gcc/testsuite/g++.dg/pr60336-5.C
new file mode 100644
index 0000000..b0c76ad
--- /dev/null
+++ b/gcc/testsuite/g++.dg/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/pr60336-6.C b/gcc/testsuite/g++.dg/pr60336-6.C
new file mode 100644
index 0000000..5879651
--- /dev/null
+++ b/gcc/testsuite/g++.dg/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/pr60336-7.C b/gcc/testsuite/g++.dg/pr60336-7.C
new file mode 100644
index 0000000..0e5d2ef
--- /dev/null
+++ b/gcc/testsuite/g++.dg/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/pr60336-8.C b/gcc/testsuite/g++.dg/pr60336-8.C
new file mode 100644
index 0000000..2c22f78
--- /dev/null
+++ b/gcc/testsuite/g++.dg/pr60336-8.C
@@ -0,0 +1,15 @@
+// { dg-do compile }
+// { dg-options "-O2" }
+
+struct dummy { struct{} a[7][3]; };
+
+extern void test1 (struct dummy, ...);
+extern void (*test2) (struct dummy, ...);
+
+void
+foo ()
+{
+  struct dummy a0;
+  test1 (a0); // { dg-message "note: the ABI of passing empty type has changed in GCC 6" }
+  test2 (a0); // { dg-message "note: the ABI of passing empty type has changed in GCC 6" }
+}
diff --git a/gcc/testsuite/g++.dg/pr60336-9.C b/gcc/testsuite/g++.dg/pr60336-9.C
new file mode 100644
index 0000000..4ad333f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/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/pr68355.C b/gcc/testsuite/g++.dg/pr68355.C
new file mode 100644
index 0000000..1354fc4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/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-*-* } } }
diff --git a/gcc/tree-dfa.c b/gcc/tree-dfa.c
index 0e98056..b74d34b 100644
--- a/gcc/tree-dfa.c
+++ b/gcc/tree-dfa.c
@@ -392,7 +392,9 @@ get_ref_base_and_extent (tree exp, HOST_WIDE_INT *poffset,
   else if (!VOID_TYPE_P (TREE_TYPE (exp)))
     {
       machine_mode mode = TYPE_MODE (TREE_TYPE (exp));
-      if (mode == BLKmode)
+      if (type_is_empty_type_p (TREE_TYPE (exp)))
+	bitsize = 0;
+      else if (mode == BLKmode)
 	size_tree = TYPE_SIZE (TREE_TYPE (exp));
       else
 	bitsize = int (GET_MODE_BITSIZE (mode));
diff --git a/gcc/tree.c b/gcc/tree.c
index b8333d4..2a325d2 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -14050,4 +14050,43 @@ combined_fn_name (combined_fn fn)
     return internal_fn_name (as_internal_fn (fn));
 }
 
+/* Returns true if TYPE is a type where it and all of its subobjects
+  (recursively) are of class, structure, union, or array type.  */
+
+static bool
+is_empty_type (tree type)
+{
+  if (RECORD_OR_UNION_TYPE_P (type))
+    {
+      tree field;
+      for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
+	if (TREE_CODE (field) == FIELD_DECL
+	    && (DECL_NAME (field)
+		|| RECORD_OR_UNION_TYPE_P (TREE_TYPE (field)))
+	    && !is_empty_type (TREE_TYPE (field)))
+	  return false;
+      return true;
+    }
+  else if (TREE_CODE (type) == ARRAY_TYPE)
+    return is_empty_type (TREE_TYPE (type));
+  return false;
+}
+
+/* Return true if TYPE is an empty type whose address isn't needed.  */
+
+bool
+type_is_empty_type_p (const_tree type)
+{
+  if (!abi_version_at_least (10))
+    return false;
+
+  if (type == error_mark_node)
+    return false;
+
+  if (TREE_ADDRESSABLE (type))
+    return false;
+
+  return is_empty_type (TYPE_MAIN_VARIANT (type));
+}
+
 #include "gt-tree.h"
diff --git a/gcc/tree.h b/gcc/tree.h
index 544a6a1..9686482 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -5399,6 +5399,8 @@ extern void gt_pch_nx (tree &, gt_pointer_operator, void *);
 extern bool nonnull_arg_p (const_tree);
 extern bool is_redundant_typedef (const_tree);
 
+extern bool type_is_empty_type_p (const_tree t);
+
 extern location_t
 set_source_range (tree expr, location_t start, location_t finish);
 
diff --git a/gcc/var-tracking.c b/gcc/var-tracking.c
index 9f09d30..15a6fa0 100644
--- a/gcc/var-tracking.c
+++ b/gcc/var-tracking.c
@@ -6241,10 +6241,10 @@ prepare_call_arguments (basic_block bb, rtx_insn *insn)
 		  rtx reg;
 		  INIT_CUMULATIVE_ARGS (args_so_far_v, type, NULL_RTX, fndecl,
 					nargs + 1);
-		  reg = targetm.calls.function_arg (args_so_far, mode,
-						    struct_addr, true);
-		  targetm.calls.function_arg_advance (args_so_far, mode,
-						      struct_addr, true);
+		  reg = function_arg (args_so_far, mode, struct_addr,
+				      true);
+		  function_arg_advance (args_so_far, mode, struct_addr,
+					true);
 		  if (reg == NULL_RTX)
 		    {
 		      for (; link; link = XEXP (link, 1))
@@ -6265,8 +6265,8 @@ prepare_call_arguments (basic_block bb, rtx_insn *insn)
 		  machine_mode mode;
 		  t = TYPE_ARG_TYPES (type);
 		  mode = TYPE_MODE (TREE_VALUE (t));
-		  this_arg = targetm.calls.function_arg (args_so_far, mode,
-							 TREE_VALUE (t), true);
+		  this_arg = function_arg (args_so_far, mode,
+					   TREE_VALUE (t), true);
 		  if (this_arg && !REG_P (this_arg))
 		    this_arg = NULL_RTX;
 		  else if (this_arg == NULL_RTX)
@@ -6381,8 +6381,7 @@ prepare_call_arguments (basic_block bb, rtx_insn *insn)
 		argtype = build_pointer_type (argtype);
 		mode = TYPE_MODE (argtype);
 	      }
-	    reg = targetm.calls.function_arg (args_so_far, mode,
-					      argtype, true);
+	    reg = function_arg (args_so_far, mode, argtype, true);
 	    if (TREE_CODE (argtype) == REFERENCE_TYPE
 		&& INTEGRAL_TYPE_P (TREE_TYPE (argtype))
 		&& reg
@@ -6436,8 +6435,7 @@ prepare_call_arguments (basic_block bb, rtx_insn *insn)
 			}
 		  }
 	      }
-	    targetm.calls.function_arg_advance (args_so_far, mode,
-						argtype, true);
+	    function_arg_advance (args_so_far, mode, argtype, true);
 	    t = TREE_CHAIN (t);
 	  }
       }
-- 
2.5.0


[-- Attachment #3: 0002-Properly-pass-empty-type-for-non-i386-targets.patch --]
[-- Type: text/x-patch, Size: 15977 bytes --]

From f81f201327d48760ec26b7149d29bb3fc7c49f64 Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@gmail.com>
Date: Wed, 9 Dec 2015 12:48:07 -0800
Subject: [PATCH 2/3] Properly pass empty type for non-i386 targets

Use 0 for empty type size in TARGET_GIMPLIFY_VA_ARG_EXPR.

	PR c++/60336
	PR middle-end/67239
	PR target/68355
	* config/aarch64/aarch64.c (aarch64_gimplify_va_arg_expr): Use 0
	for empty type size.
	* config/mep/mep.c (mep_gimplify_va_arg_expr): Likewise.
	* config/mips/mips.c (mips_std_gimplify_va_arg_expr): Likewise.
	(mips_gimplify_va_arg_expr): Likewise.
	* config/msp430/msp430.c (msp430_gimplify_va_arg_expr): Likewise.
	* config/pa/pa.c (hppa_gimplify_va_arg_expr): Likewise.
	* config/rs6000/rs6000.c (rs6000_gimplify_va_arg): Likewise.
	* config/s390/s390.c (s390_gimplify_va_arg): Likewise.
	* config/sh/sh.c (sh_gimplify_va_arg_expr): Likewise.
	* config/sparc/sparc.c (sparc_gimplify_va_arg): Likewise.
	* config/spu/spu.c (spu_gimplify_va_arg_expr): Likewise.
	* config/stormy16/stormy16.c (xstormy16_gimplify_va_arg_expr):
	Likewise.
	* config/visium/visium.c (visium_gimplify_va_arg): Likewise.
	* config/xtensa/xtensa.c (xtensa_gimplify_va_arg_expr): Likewise.
	* config/alpha/alpha.c (alpha_setup_incoming_varargs): Replace
	targetm.calls.function_arg_advance with function_arg_advance.
	(alpha_gimplify_va_arg_1): Use 0 for empty type size.
	* config/microblaze/microblaze.c (microblaze_expand_prologue):
	Replace targetm.calls.function_arg with function_arg.  Replace
	targetm.calls.function_arg_advance with function_arg_advance.
	* config/tilegx/tilegx.c (tilegx_setup_incoming_varargs): Replace
	targetm.calls.function_arg_advance with function_arg_advance.
	(tilegx_gimplify_va_arg_expr): Use 0 for empty type size.
	* config/tilepro/tilepro.c (tilepro_setup_incoming_varargs):
	Replace targetm.calls.function_arg_advance with
	function_arg_advance.
	(tilepro_gimplify_va_arg_expr): Use 0 for empty type size.
---
 gcc/config/aarch64/aarch64.c       |  3 ++-
 gcc/config/alpha/alpha.c           |  6 +++---
 gcc/config/mep/mep.c               |  3 ++-
 gcc/config/microblaze/microblaze.c | 11 +++++------
 gcc/config/mips/mips.c             |  6 ++++--
 gcc/config/msp430/msp430.c         |  3 ++-
 gcc/config/pa/pa.c                 |  3 ++-
 gcc/config/rs6000/rs6000.c         |  3 ++-
 gcc/config/s390/s390.c             |  3 ++-
 gcc/config/sh/sh.c                 |  3 ++-
 gcc/config/sparc/sparc.c           |  3 ++-
 gcc/config/spu/spu.c               |  3 ++-
 gcc/config/stormy16/stormy16.c     |  6 ++++--
 gcc/config/tilegx/tilegx.c         |  7 ++++---
 gcc/config/tilepro/tilepro.c       |  7 ++++---
 gcc/config/visium/visium.c         |  3 ++-
 gcc/config/xtensa/xtensa.c         |  3 ++-
 17 files changed, 46 insertions(+), 30 deletions(-)

diff --git a/gcc/config/aarch64/aarch64.c b/gcc/config/aarch64/aarch64.c
index 801f95a..ccd204f 100644
--- a/gcc/config/aarch64/aarch64.c
+++ b/gcc/config/aarch64/aarch64.c
@@ -9674,7 +9674,8 @@ aarch64_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
 
   stack = build3 (COMPONENT_REF, TREE_TYPE (f_stack), unshare_expr (valist),
 		  f_stack, NULL_TREE);
-  size = int_size_in_bytes (type);
+  bool empty_type = type && type_is_empty_type_p (type);
+  size = empty_type ? 0 : int_size_in_bytes (type);
   align = aarch64_function_arg_alignment (mode, type) / BITS_PER_UNIT;
 
   dw_align = false;
diff --git a/gcc/config/alpha/alpha.c b/gcc/config/alpha/alpha.c
index e023d3b..0b1b8a5 100644
--- a/gcc/config/alpha/alpha.c
+++ b/gcc/config/alpha/alpha.c
@@ -6117,8 +6117,7 @@ alpha_setup_incoming_varargs (cumulative_args_t pcum, machine_mode mode,
   CUMULATIVE_ARGS cum = *get_cumulative_args (pcum);
 
   /* Skip the current argument.  */
-  targetm.calls.function_arg_advance (pack_cumulative_args (&cum), mode, type,
-				      true);
+  function_arg_advance (pack_cumulative_args (&cum), mode, type, true);
 
 #if TARGET_ABI_OPEN_VMS
   /* For VMS, we allocate space for all 6 arg registers plus a count.
@@ -6304,7 +6303,8 @@ alpha_gimplify_va_arg_1 (tree type, tree base, tree offset,
   gimple_seq_add_seq (pre_p, internal_post);
 
   /* Update the offset field.  */
-  type_size = TYPE_SIZE_UNIT (TYPE_MAIN_VARIANT (type));
+  bool empty_type = type && type_is_empty_type_p (type);
+  type_size = empty_type ? 0 : TYPE_SIZE_UNIT (TYPE_MAIN_VARIANT (type));
   if (type_size == NULL || TREE_OVERFLOW (type_size))
     t = size_zero_node;
   else
diff --git a/gcc/config/mep/mep.c b/gcc/config/mep/mep.c
index 9c4cd86..4ce09eb 100644
--- a/gcc/config/mep/mep.c
+++ b/gcc/config/mep/mep.c
@@ -3525,7 +3525,8 @@ mep_gimplify_va_arg_expr (tree valist, tree type,
 
   ivc2_vec = TARGET_IVC2 && VECTOR_TYPE_P (type);
 
-  size = int_size_in_bytes (type);
+  bool empty_type = type && type_is_empty_type_p (type);
+  size = empty_type ? 0 : int_size_in_bytes (type);
   by_reference = (size > (ivc2_vec ? 8 : 4)) || (size <= 0);
 
   if (by_reference)
diff --git a/gcc/config/microblaze/microblaze.c b/gcc/config/microblaze/microblaze.c
index baff67a..e144ff1 100644
--- a/gcc/config/microblaze/microblaze.c
+++ b/gcc/config/microblaze/microblaze.c
@@ -2798,8 +2798,8 @@ microblaze_expand_prologue (void)
 	  passed_mode = Pmode;
 	}
 
-      entry_parm = targetm.calls.function_arg (args_so_far, passed_mode,
-					       passed_type, true);
+      entry_parm = function_arg (args_so_far, passed_mode, passed_type,
+				 true);
 
       if (entry_parm)
 	{
@@ -2819,8 +2819,7 @@ microblaze_expand_prologue (void)
 	  break;
 	}
 
-      targetm.calls.function_arg_advance (args_so_far, passed_mode,
-					  passed_type, true);
+      function_arg_advance (args_so_far, passed_mode, passed_type, true);
 
       next_arg = TREE_CHAIN (cur_arg);
       if (next_arg == 0)
@@ -2834,8 +2833,8 @@ microblaze_expand_prologue (void)
 
   /* Split parallel insn into a sequence of insns.  */
 
-  next_arg_reg = targetm.calls.function_arg (args_so_far, VOIDmode,
-					     void_type_node, true);
+  next_arg_reg = function_arg (args_so_far, VOIDmode, void_type_node,
+			       true);
   if (next_arg_reg != 0 && GET_CODE (next_arg_reg) == PARALLEL)
     {
       rtvec adjust = XVEC (next_arg_reg, 0);
diff --git a/gcc/config/mips/mips.c b/gcc/config/mips/mips.c
index 5af3d1e..f9165e6 100644
--- a/gcc/config/mips/mips.c
+++ b/gcc/config/mips/mips.c
@@ -6308,7 +6308,8 @@ mips_std_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
     }
 
   /* Compute the rounded size of the type.  */
-  type_size = size_in_bytes (type);
+  bool empty_type = type && type_is_empty_type_p (type);
+  type_size = empty_type ? 0 : size_in_bytes (type);
   rounded_size = round_up (type_size, align);
 
   /* Reduce rounded_size so it's sharable with the postqueue.  */
@@ -6397,7 +6398,8 @@ mips_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
 
       ovfl = build3 (COMPONENT_REF, TREE_TYPE (f_ovfl), valist, f_ovfl,
 		     NULL_TREE);
-      size = int_size_in_bytes (type);
+      bool empty_type = type && type_is_empty_type_p (type);
+      size = empty_type ? 0 : int_size_in_bytes (type);
 
       if (GET_MODE_CLASS (TYPE_MODE (type)) == MODE_FLOAT
 	  && GET_MODE_SIZE (TYPE_MODE (type)) <= UNITS_PER_FPVALUE)
diff --git a/gcc/config/msp430/msp430.c b/gcc/config/msp430/msp430.c
index 6f63116..b1bd318 100644
--- a/gcc/config/msp430/msp430.c
+++ b/gcc/config/msp430/msp430.c
@@ -1465,7 +1465,8 @@ msp430_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
     }
 
   /* Compute the rounded size of the type.  */
-  type_size = size_in_bytes (type);
+  bool empty_type = type && type_is_empty_type_p (type);
+  type_size = empty_type ? 0 : size_in_bytes (type);
   rounded_size = round_up (type_size, align);
 
   /* Reduce rounded_size so it's sharable with the postqueue.  */
diff --git a/gcc/config/pa/pa.c b/gcc/config/pa/pa.c
index 8b1c832..e19e00b 100644
--- a/gcc/config/pa/pa.c
+++ b/gcc/config/pa/pa.c
@@ -6342,7 +6342,8 @@ hppa_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
 	  type = ptr;
 	  ptr = build_pointer_type (type);
 	}
-      size = int_size_in_bytes (type);
+      bool empty_type = type && type_is_empty_type_p (type);
+      size = empty_type ? 0 : int_size_in_bytes (type);
       valist_type = TREE_TYPE (valist);
 
       /* Args grow down.  Not handled by generic routines.  */
diff --git a/gcc/config/rs6000/rs6000.c b/gcc/config/rs6000/rs6000.c
index 5b03f9e..0000d9b 100644
--- a/gcc/config/rs6000/rs6000.c
+++ b/gcc/config/rs6000/rs6000.c
@@ -12069,7 +12069,8 @@ rs6000_gimplify_va_arg (tree valist, tree type, gimple_seq *pre_p,
   sav = build3 (COMPONENT_REF, TREE_TYPE (f_sav), unshare_expr (valist),
 		f_sav, NULL_TREE);
 
-  size = int_size_in_bytes (type);
+  bool empty_type = type && type_is_empty_type_p (type);
+  size = empty_type ? 0 : int_size_in_bytes (type);
   rsize = (size + 3) / 4;
   align = 1;
 
diff --git a/gcc/config/s390/s390.c b/gcc/config/s390/s390.c
index 8924367..076f381 100644
--- a/gcc/config/s390/s390.c
+++ b/gcc/config/s390/s390.c
@@ -11999,7 +11999,8 @@ s390_gimplify_va_arg (tree valist, tree type, gimple_seq *pre_p,
   valist = unshare_expr (valist);
   ovf = build3 (COMPONENT_REF, TREE_TYPE (f_ovf), valist, f_ovf, NULL_TREE);
 
-  size = int_size_in_bytes (type);
+  bool empty_type = type && type_is_empty_type_p (type);
+  size = empty_type ? 0 : int_size_in_bytes (type);
 
   s390_check_type_for_vector_abi (type, true, false);
 
diff --git a/gcc/config/sh/sh.c b/gcc/config/sh/sh.c
index 8c8fe3c..b4a2691 100644
--- a/gcc/config/sh/sh.c
+++ b/gcc/config/sh/sh.c
@@ -8767,7 +8767,8 @@ sh_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
   if (pass_by_ref)
     type = build_pointer_type (type);
 
-  size = int_size_in_bytes (type);
+  bool empty_type = type && type_is_empty_type_p (type);
+  size = empty_type ? 0 : int_size_in_bytes (type);
   rsize = (size + UNITS_PER_WORD - 1) & -UNITS_PER_WORD;
   pptr_type_node = build_pointer_type (ptr_type_node);
 
diff --git a/gcc/config/sparc/sparc.c b/gcc/config/sparc/sparc.c
index 080be61..efbab37 100644
--- a/gcc/config/sparc/sparc.c
+++ b/gcc/config/sparc/sparc.c
@@ -7336,7 +7336,8 @@ sparc_gimplify_va_arg (tree valist, tree type, gimple_seq *pre_p,
   else
     {
       indirect = false;
-      size = int_size_in_bytes (type);
+      bool empty_type = type && type_is_empty_type_p (type);
+      size = empty_type ? 0 : int_size_in_bytes (type);
       rsize = ROUND_UP (size, UNITS_PER_WORD);
       align = 0;
 
diff --git a/gcc/config/spu/spu.c b/gcc/config/spu/spu.c
index 401c295..432f8e2 100644
--- a/gcc/config/spu/spu.c
+++ b/gcc/config/spu/spu.c
@@ -4028,7 +4028,8 @@ spu_gimplify_va_arg_expr (tree valist, tree type, gimple_seq * pre_p,
 					   false);
   if (pass_by_reference_p)
     type = build_pointer_type (type);
-  size = int_size_in_bytes (type);
+  bool empty_type = type && type_is_empty_type_p (type);
+  size = empty_type ? 0 : int_size_in_bytes (type);
   rsize = ((size + UNITS_PER_WORD - 1) / UNITS_PER_WORD) * UNITS_PER_WORD;
 
   /* build conditional expression to calculate addr. The expression
diff --git a/gcc/config/stormy16/stormy16.c b/gcc/config/stormy16/stormy16.c
index 50af15b..8e9676f 100644
--- a/gcc/config/stormy16/stormy16.c
+++ b/gcc/config/stormy16/stormy16.c
@@ -1337,8 +1337,10 @@ xstormy16_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
   count = build3 (COMPONENT_REF, TREE_TYPE (f_count), valist, f_count,
 		  NULL_TREE);
 
+  bool empty_type = type && type_is_empty_type_p (type);
   must_stack = targetm.calls.must_pass_in_stack (TYPE_MODE (type), type);
-  size_tree = round_up (size_in_bytes (type), UNITS_PER_WORD);
+  size_tree = round_up (empty_type ? integer_zero_node : size_in_bytes (type),
+			UNITS_PER_WORD);
   gimplify_expr (&size_tree, pre_p, NULL, is_gimple_val, fb_rvalue);
 
   size_of_reg_args = NUM_ARGUMENT_REGISTERS * UNITS_PER_WORD;
@@ -1374,7 +1376,7 @@ xstormy16_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
   /* Arguments larger than a word might need to skip over some
      registers, since arguments are either passed entirely in
      registers or entirely on the stack.  */
-  size = PUSH_ROUNDING (int_size_in_bytes (type));
+  size = PUSH_ROUNDING (empty_type ? 0 : int_size_in_bytes (type));
   if (size > 2 || size < 0 || must_stack)
     {
       tree r, u;
diff --git a/gcc/config/tilegx/tilegx.c b/gcc/config/tilegx/tilegx.c
index 06c832c..001a93e 100644
--- a/gcc/config/tilegx/tilegx.c
+++ b/gcc/config/tilegx/tilegx.c
@@ -396,8 +396,8 @@ tilegx_setup_incoming_varargs (cumulative_args_t cum,
   /* The caller has advanced CUM up to, but not beyond, the last named
      argument.  Advance a local copy of CUM past the last "real" named
      argument, to find out how many registers are left over.  */
-  targetm.calls.function_arg_advance (pack_cumulative_args (&local_cum),
-				      mode, type, true);
+  function_arg_advance (pack_cumulative_args (&local_cum), mode, type,
+			true);
   first_reg = local_cum;
 
   if (local_cum < TILEGX_NUM_ARG_REGS)
@@ -473,7 +473,8 @@ tilegx_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
   if (pass_by_reference_p)
     type = build_pointer_type (type);
 
-  size = int_size_in_bytes (type);
+  bool empty_type = type && type_is_empty_type_p (type);
+  size = empty_type ? 0 : int_size_in_bytes (type);
   rsize = ((size + UNITS_PER_WORD - 1) / UNITS_PER_WORD) * UNITS_PER_WORD;
 
   /* If the alignment of the type is greater than the default for a
diff --git a/gcc/config/tilepro/tilepro.c b/gcc/config/tilepro/tilepro.c
index 628cd04..6cadfd6 100644
--- a/gcc/config/tilepro/tilepro.c
+++ b/gcc/config/tilepro/tilepro.c
@@ -348,8 +348,8 @@ tilepro_setup_incoming_varargs (cumulative_args_t cum,
   /* The caller has advanced CUM up to, but not beyond, the last named
      argument.  Advance a local copy of CUM past the last "real" named
      argument, to find out how many registers are left over.  */
-  targetm.calls.function_arg_advance (pack_cumulative_args (&local_cum),
-				      mode, type, true);
+  function_arg_advance (pack_cumulative_args (&local_cum), mode, type,
+			true);
   first_reg = local_cum;
 
   if (local_cum < TILEPRO_NUM_ARG_REGS)
@@ -421,7 +421,8 @@ tilepro_gimplify_va_arg_expr (tree valist, tree type, gimple_seq * pre_p,
   if (pass_by_reference_p)
     type = build_pointer_type (type);
 
-  size = int_size_in_bytes (type);
+  bool empty_type = type && type_is_empty_type_p (type);
+  size = empty_type ? 0 : int_size_in_bytes (type);
   rsize = ((size + UNITS_PER_WORD - 1) / UNITS_PER_WORD) * UNITS_PER_WORD;
 
   /* If the alignment of the type is greater than the default for a
diff --git a/gcc/config/visium/visium.c b/gcc/config/visium/visium.c
index cd28f9b..d10853a 100644
--- a/gcc/config/visium/visium.c
+++ b/gcc/config/visium/visium.c
@@ -1586,7 +1586,8 @@ visium_gimplify_va_arg (tree valist, tree type, gimple_seq *pre_p,
       return build_va_arg_indirect_ref (t);
     }
 
-  size = int_size_in_bytes (type);
+  bool empty_type = type && type_is_empty_type_p (type);
+  size = empty_type ? 0 : int_size_in_bytes (type);
   rsize = (size + UNITS_PER_WORD - 1) & -UNITS_PER_WORD;
   f_ovfl = TYPE_FIELDS (va_list_type_node);
   f_gbase = TREE_CHAIN (f_ovfl);
diff --git a/gcc/config/xtensa/xtensa.c b/gcc/config/xtensa/xtensa.c
index 64d089b..30359eb 100644
--- a/gcc/config/xtensa/xtensa.c
+++ b/gcc/config/xtensa/xtensa.c
@@ -3161,7 +3161,8 @@ xtensa_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
   ndx = build3 (COMPONENT_REF, TREE_TYPE (f_ndx), unshare_expr (valist),
 		f_ndx, NULL_TREE);
 
-  type_size = size_in_bytes (type);
+  bool empty_type = type && type_is_empty_type_p (type);
+  type_size = empty_type ? 0 : size_in_bytes (type);
   va_size = round_up (type_size, UNITS_PER_WORD);
   gimplify_expr (&va_size, pre_p, NULL, is_gimple_val, fb_rvalue);
 
-- 
2.5.0


[-- Attachment #4: 0003-Add-a-call-target-hook-function_skip_empty_type_p.patch --]
[-- Type: text/x-patch, Size: 12802 bytes --]

From 56fac78dffedad6abfad7f86f90a25b8c930eb6f Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@gmail.com>
Date: Sun, 7 Feb 2016 09:30:58 -0800
Subject: [PATCH 3/3] Add a call target hook: function_skip_empty_type_p

A function call target hook, function_skip_empty_type_p, is added, which
defaults to return false.  This ABI change is enabled only if the ABI
level is at least 10, which is updated in GCC 6, and the target hook
TARGET_FUNCTION_SKIP_EMPTY_TYPE_P returns true, which is used by target
whose psABI specifies that no memory slot nor register should be used to
pass or return an object of empty type whose address isn't needed.

gcc/

	PR c++/60336
	PR middle-end/67239
	PR target/68355
	* target.def (calls): Add function_skip_empty_type_p.
	* tree.c (type_is_empty_type_p): Return false if
	targetm.calls.function_skip_empty_type_p return false.
	* config/i386/i386.c (TARGET_FUNCTION_SKIP_EMPTY_TYPE_P): New
	hook to return true.
	* doc/tm.texi.in (TARGET_FUNCTION_SKIP_EMPTY_TYPE_P): New
	hook.
	* doc/tm.texi: Regenerated.

gcc/testsuite/

	PR c++/60336
	PR middle-end/67239
	PR target/68355
	* g++.dg/abi/empty12.C: Enabled only for x86 targets.
	* g++.dg/abi/empty12.h: Likewise.
	* g++.dg/abi/empty12a.c: Likewise.
	* g++.dg/abi/empty13.C: Likewise.
	* g++.dg/abi/empty13.h: Likewise.
	* g++.dg/abi/empty13a.c: Likewise.
	* g++.dg/abi/empty14.C: Likewise.
	* g++.dg/abi/empty14.h: Likewise.
	* g++.dg/abi/empty14a.c: Likewise.
	* g++.dg/abi/empty15.C: Likewise.
	* g++.dg/abi/empty15.h: Likewise.
	* g++.dg/abi/empty15a.c: Likewise.
	* g++.dg/abi/empty16.C: Likewise.
	* g++.dg/abi/empty16.h: Likewise.
	* g++.dg/abi/empty16a.c: Likewise.
	* g++.dg/abi/empty17.C: Likewise.
	* g++.dg/abi/empty17.h: Likewise.
	* g++.dg/abi/empty17a.c: Likewise.
	* 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/config/i386/i386.c             |  3 +++
 gcc/doc/tm.texi                    |  6 ++++++
 gcc/doc/tm.texi.in                 |  2 ++
 gcc/target.def                     | 10 ++++++++++
 gcc/testsuite/g++.dg/abi/empty12.C |  2 +-
 gcc/testsuite/g++.dg/abi/empty13.C |  2 +-
 gcc/testsuite/g++.dg/abi/empty14.C |  2 +-
 gcc/testsuite/g++.dg/abi/empty15.C |  2 +-
 gcc/testsuite/g++.dg/abi/empty16.C |  2 +-
 gcc/testsuite/g++.dg/abi/empty17.C |  2 +-
 gcc/testsuite/g++.dg/pr60336-1.C   |  2 +-
 gcc/testsuite/g++.dg/pr60336-10.C  |  2 +-
 gcc/testsuite/g++.dg/pr60336-11.C  |  2 +-
 gcc/testsuite/g++.dg/pr60336-12.C  |  2 +-
 gcc/testsuite/g++.dg/pr60336-2.C   |  2 +-
 gcc/testsuite/g++.dg/pr60336-3.C   |  2 +-
 gcc/testsuite/g++.dg/pr60336-5.C   |  2 +-
 gcc/testsuite/g++.dg/pr60336-6.C   |  2 +-
 gcc/testsuite/g++.dg/pr60336-7.C   |  2 +-
 gcc/testsuite/g++.dg/pr60336-8.C   |  2 +-
 gcc/testsuite/g++.dg/pr60336-9.C   |  2 +-
 gcc/testsuite/g++.dg/pr68355.C     |  2 +-
 gcc/tree.c                         |  3 +++
 23 files changed, 42 insertions(+), 18 deletions(-)

diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index 07bb06a..c4618b4 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -54551,6 +54551,9 @@ ix86_addr_space_zero_address_valid (addr_space_t as)
 #undef TARGET_PROMOTE_FUNCTION_MODE
 #define TARGET_PROMOTE_FUNCTION_MODE ix86_promote_function_mode
 
+#undef TARGET_FUNCTION_SKIP_EMPTY_TYPE_P
+#define TARGET_FUNCTION_SKIP_EMPTY_TYPE_P hook_bool_void_true
+
 #undef  TARGET_OVERRIDE_OPTIONS_AFTER_CHANGE
 #define TARGET_OVERRIDE_OPTIONS_AFTER_CHANGE ix86_override_options_after_change
 
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index 8b3bccd..9cd0451 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -969,6 +969,12 @@ also define the hook to @code{default_promote_function_mode_always_promote}
 if you would like to apply the same rules given by @code{PROMOTE_MODE}.
 @end deftypefn
 
+@deftypefn {Target Hook} bool TARGET_FUNCTION_SKIP_EMPTY_TYPE_P (void)
+This hook should return true if no memory slot nor register should be
+used to pass or return an object of empty type.  The default is to
+return @code{false}.
+@end deftypefn
+
 @defmac PARM_BOUNDARY
 Normal alignment required for function parameters on the stack, in
 bits.  All stack parameters receive at least this much alignment
diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
index f31c763..3b978e6 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -923,6 +923,8 @@ Do not define this macro if it would never modify @var{m}.
 
 @hook TARGET_PROMOTE_FUNCTION_MODE
 
+@hook TARGET_FUNCTION_SKIP_EMPTY_TYPE_P
+
 @defmac PARM_BOUNDARY
 Normal alignment required for function parameters on the stack, in
 bits.  All stack parameters receive at least this much alignment
diff --git a/gcc/target.def b/gcc/target.def
index 5c8e4e1..b6d28cf 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -4035,6 +4035,16 @@ supported by the target.",
 /* Members of struct call have no special macro prefix.  */
 HOOK_VECTOR (TARGET_CALLS, calls)
 
+/* Returns true if no memory slot nor register should be used to pass
+   or return an object of empty type.  */
+DEFHOOK
+(function_skip_empty_type_p,
+ "This hook should return true if no memory slot nor register should be\n\
+used to pass or return an object of empty type.  The default is to\n\
+return @code{false}.",
+ bool, (void),
+ hook_bool_void_false)
+
 DEFHOOK
 (promote_function_mode,
  "Like @code{PROMOTE_MODE}, but it is applied to outgoing function arguments or\n\
diff --git a/gcc/testsuite/g++.dg/abi/empty12.C b/gcc/testsuite/g++.dg/abi/empty12.C
index ff2909c..3e97af6 100644
--- a/gcc/testsuite/g++.dg/abi/empty12.C
+++ b/gcc/testsuite/g++.dg/abi/empty12.C
@@ -1,5 +1,5 @@
 // PR c++/60336
-// { dg-do run }
+// { dg-do run { target i?86-*-* x86_64-*-* } }
 // { dg-options "-x c" }
 // { dg-additional-sources "empty12a.c" }
 // { dg-prune-output "command line option" }
diff --git a/gcc/testsuite/g++.dg/abi/empty13.C b/gcc/testsuite/g++.dg/abi/empty13.C
index d1e0946..d2e297e 100644
--- a/gcc/testsuite/g++.dg/abi/empty13.C
+++ b/gcc/testsuite/g++.dg/abi/empty13.C
@@ -1,5 +1,5 @@
 // PR c++/60336
-// { dg-do run }
+// { dg-do run { target i?86-*-* x86_64-*-* } }
 // { dg-options "-x c -fabi-version=9" }
 // { dg-additional-sources "empty13a.c" }
 // { dg-prune-output "command line option" }
diff --git a/gcc/testsuite/g++.dg/abi/empty14.C b/gcc/testsuite/g++.dg/abi/empty14.C
index 712d4e8..2085188 100644
--- a/gcc/testsuite/g++.dg/abi/empty14.C
+++ b/gcc/testsuite/g++.dg/abi/empty14.C
@@ -1,5 +1,5 @@
 // PR c++/60336
-// { dg-do run }
+// { dg-do run { target i?86-*-* x86_64-*-* } }
 // { dg-options "-x c" }
 // { dg-additional-sources "empty14a.c" }
 // { dg-prune-output "command line option" }
diff --git a/gcc/testsuite/g++.dg/abi/empty15.C b/gcc/testsuite/g++.dg/abi/empty15.C
index 24bf047..34f78ae 100644
--- a/gcc/testsuite/g++.dg/abi/empty15.C
+++ b/gcc/testsuite/g++.dg/abi/empty15.C
@@ -1,5 +1,5 @@
 // PR c++/60336
-// { dg-do run }
+// { dg-do run { target i?86-*-* x86_64-*-* } }
 // { dg-options "-x c" }
 // { dg-additional-sources "empty15a.c" }
 // { dg-prune-output "command line option" }
diff --git a/gcc/testsuite/g++.dg/abi/empty16.C b/gcc/testsuite/g++.dg/abi/empty16.C
index f058720..200d2ab 100644
--- a/gcc/testsuite/g++.dg/abi/empty16.C
+++ b/gcc/testsuite/g++.dg/abi/empty16.C
@@ -1,5 +1,5 @@
 // PR c++/60336
-// { dg-do run }
+// { dg-do run { target i?86-*-* x86_64-*-* } }
 // { dg-options "-x c" }
 // { dg-additional-sources "empty16a.c" }
 // { dg-prune-output "command line option" }
diff --git a/gcc/testsuite/g++.dg/abi/empty17.C b/gcc/testsuite/g++.dg/abi/empty17.C
index f6f88c8..7221101 100644
--- a/gcc/testsuite/g++.dg/abi/empty17.C
+++ b/gcc/testsuite/g++.dg/abi/empty17.C
@@ -1,5 +1,5 @@
 // PR c++/60336
-// { dg-do run }
+// { dg-do run { target i?86-*-* x86_64-*-* } }
 // { dg-options "-x c" }
 // { dg-additional-sources "empty17a.c" }
 // { dg-prune-output "command line option" }
diff --git a/gcc/testsuite/g++.dg/pr60336-1.C b/gcc/testsuite/g++.dg/pr60336-1.C
index af08638..946f8accd 100644
--- a/gcc/testsuite/g++.dg/pr60336-1.C
+++ b/gcc/testsuite/g++.dg/pr60336-1.C
@@ -1,4 +1,4 @@
-// { dg-do compile }
+// { dg-do compile { target i?86-*-* x86_64-*-* } }
 // { dg-options "-O2 -std=c++11 -fno-pic" }
 // { dg-require-effective-target fpic }
 
diff --git a/gcc/testsuite/g++.dg/pr60336-10.C b/gcc/testsuite/g++.dg/pr60336-10.C
index 6c9c990..57707fc 100644
--- a/gcc/testsuite/g++.dg/pr60336-10.C
+++ b/gcc/testsuite/g++.dg/pr60336-10.C
@@ -1,4 +1,4 @@
-// { dg-do run }
+// { dg-do run { target i?86-*-* x86_64-*-* } }
 // { dg-options "-O2" }
 
 #include <stdarg.h>
diff --git a/gcc/testsuite/g++.dg/pr60336-11.C b/gcc/testsuite/g++.dg/pr60336-11.C
index c92f3d4..32000b2 100644
--- a/gcc/testsuite/g++.dg/pr60336-11.C
+++ b/gcc/testsuite/g++.dg/pr60336-11.C
@@ -1,4 +1,4 @@
-// { dg-do run }
+// { dg-do run { target i?86-*-* x86_64-*-* } }
 // { dg-options "-O2" }
 
 #include <stdarg.h>
diff --git a/gcc/testsuite/g++.dg/pr60336-12.C b/gcc/testsuite/g++.dg/pr60336-12.C
index 83a7bb0..febcc6f 100644
--- a/gcc/testsuite/g++.dg/pr60336-12.C
+++ b/gcc/testsuite/g++.dg/pr60336-12.C
@@ -1,4 +1,4 @@
-// { dg-do run }
+// { dg-do run { target i?86-*-* x86_64-*-* } }
 // { dg-options "-O2" }
 
 #include <stdarg.h>
diff --git a/gcc/testsuite/g++.dg/pr60336-2.C b/gcc/testsuite/g++.dg/pr60336-2.C
index ad63cee..dcde99c 100644
--- a/gcc/testsuite/g++.dg/pr60336-2.C
+++ b/gcc/testsuite/g++.dg/pr60336-2.C
@@ -1,4 +1,4 @@
-// { dg-do run }
+// { dg-do run { target i?86-*-* x86_64-*-* } }
 // { dg-options "-O2" }
 
 #include <stdarg.h>
diff --git a/gcc/testsuite/g++.dg/pr60336-3.C b/gcc/testsuite/g++.dg/pr60336-3.C
index 9ec4914..d126cb5 100644
--- a/gcc/testsuite/g++.dg/pr60336-3.C
+++ b/gcc/testsuite/g++.dg/pr60336-3.C
@@ -1,4 +1,4 @@
-// { dg-do compile }
+// { dg-do compile { target i?86-*-* x86_64-*-* } }
 // { dg-options "-O2" }
 
 struct dummy { struct{}__attribute__((aligned (4))) a[7]; };
diff --git a/gcc/testsuite/g++.dg/pr60336-5.C b/gcc/testsuite/g++.dg/pr60336-5.C
index b0c76ad..a051f6e 100644
--- a/gcc/testsuite/g++.dg/pr60336-5.C
+++ b/gcc/testsuite/g++.dg/pr60336-5.C
@@ -1,4 +1,4 @@
-// { dg-do compile }
+// { dg-do compile { target i?86-*-* x86_64-*-* } }
 // { dg-options "-O2 -std=c++11 -fno-pic" }
 // { dg-require-effective-target fpic }
 
diff --git a/gcc/testsuite/g++.dg/pr60336-6.C b/gcc/testsuite/g++.dg/pr60336-6.C
index 5879651..22728d3 100644
--- a/gcc/testsuite/g++.dg/pr60336-6.C
+++ b/gcc/testsuite/g++.dg/pr60336-6.C
@@ -1,4 +1,4 @@
-// { dg-do compile }
+// { dg-do compile { target i?86-*-* x86_64-*-* } }
 // { dg-options "-O2 -std=c++11 -fno-pic" }
 // { dg-require-effective-target fpic }
 
diff --git a/gcc/testsuite/g++.dg/pr60336-7.C b/gcc/testsuite/g++.dg/pr60336-7.C
index 0e5d2ef..5fc8320 100644
--- a/gcc/testsuite/g++.dg/pr60336-7.C
+++ b/gcc/testsuite/g++.dg/pr60336-7.C
@@ -1,4 +1,4 @@
-// { dg-do compile }
+// { dg-do compile { target i?86-*-* x86_64-*-* } }
 // { dg-options "-O2 -std=c++11 -fno-pic" }
 // { dg-require-effective-target fpic }
 
diff --git a/gcc/testsuite/g++.dg/pr60336-8.C b/gcc/testsuite/g++.dg/pr60336-8.C
index 2c22f78..e88dbb4 100644
--- a/gcc/testsuite/g++.dg/pr60336-8.C
+++ b/gcc/testsuite/g++.dg/pr60336-8.C
@@ -1,4 +1,4 @@
-// { dg-do compile }
+// { dg-do compile { target i?86-*-* x86_64-*-* } }
 // { dg-options "-O2" }
 
 struct dummy { struct{} a[7][3]; };
diff --git a/gcc/testsuite/g++.dg/pr60336-9.C b/gcc/testsuite/g++.dg/pr60336-9.C
index 4ad333f..3d768c0 100644
--- a/gcc/testsuite/g++.dg/pr60336-9.C
+++ b/gcc/testsuite/g++.dg/pr60336-9.C
@@ -1,4 +1,4 @@
-// { dg-do compile }
+// { dg-do compile { target i?86-*-* x86_64-*-* } }
 // { dg-options "-O2 -std=c++11 -fno-pic" }
 // { dg-require-effective-target fpic }
 
diff --git a/gcc/testsuite/g++.dg/pr68355.C b/gcc/testsuite/g++.dg/pr68355.C
index 1354fc4..5c2f0da 100644
--- a/gcc/testsuite/g++.dg/pr68355.C
+++ b/gcc/testsuite/g++.dg/pr68355.C
@@ -1,4 +1,4 @@
-// { dg-do compile }
+// { dg-do compile { target i?86-*-* x86_64-*-* } }
 // { dg-options "-O2 -std=c++11 -fno-pic" }
 // { dg-require-effective-target fpic }
 
diff --git a/gcc/tree.c b/gcc/tree.c
index 2a325d2..1b86141 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -14077,6 +14077,9 @@ is_empty_type (tree type)
 bool
 type_is_empty_type_p (const_tree type)
 {
+  if (!targetm.calls.function_skip_empty_type_p ())
+    return false;
+
   if (!abi_version_at_least (10))
     return false;
 
-- 
2.5.0


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

* Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
  2016-03-01 22:44                                                     ` H.J. Lu
@ 2016-03-02 16:25                                                       ` Ulrich Weigand
  2016-03-02 17:34                                                         ` H.J. Lu
  0 siblings, 1 reply; 52+ messages in thread
From: Ulrich Weigand @ 2016-03-02 16:25 UTC (permalink / raw)
  To: H.J. Lu
  Cc: Jason Merrill, GCC Patches, Jakub Jelinek, Richard Biener,
	Markus Trippelsdorf

H.J. Lu wrote:

> +  if (type && type_is_empty_type_p (type))
> +    {
> +      if (warn_empty_type_p)
> +	warn_empty_type ();
> +      return NULL;
> +    }

If I understand the problem correctly, for C code empty types
already were handled correctly, the problem occured just for
C++ code (since the size of an empty size is != 0 there).

However, it seems this warning check would now appear also
for C code, even though there really shouldn't be any ABI
changes there.  Would it not be better to only generate the
warning for C++ (maybe check whether the type size is nonzero)?

Bye,
Ulrich

-- 
  Dr. Ulrich Weigand
  GNU/Linux compilers and toolchain
  Ulrich.Weigand@de.ibm.com

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

* Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
  2016-03-02 16:25                                                       ` Ulrich Weigand
@ 2016-03-02 17:34                                                         ` H.J. Lu
  2016-03-15 15:35                                                           ` Jason Merrill
  0 siblings, 1 reply; 52+ messages in thread
From: H.J. Lu @ 2016-03-02 17:34 UTC (permalink / raw)
  To: Ulrich Weigand
  Cc: Jason Merrill, GCC Patches, Jakub Jelinek, Richard Biener,
	Markus Trippelsdorf

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

On Wed, Mar 2, 2016 at 8:25 AM, Ulrich Weigand <uweigand@de.ibm.com> wrote:
> H.J. Lu wrote:
>
>> +  if (type && type_is_empty_type_p (type))
>> +    {
>> +      if (warn_empty_type_p)
>> +     warn_empty_type ();
>> +      return NULL;
>> +    }
>
> If I understand the problem correctly, for C code empty types
> already were handled correctly, the problem occured just for
> C++ code (since the size of an empty size is != 0 there).
>
> However, it seems this warning check would now appear also
> for C code, even though there really shouldn't be any ABI
> changes there.  Would it not be better to only generate the
> warning for C++ (maybe check whether the type size is nonzero)?
>

Very good point.  Here is the updated patch to return false from
type_is_empty_type_p for type of zero size.

-- 
H.J.

[-- Attachment #2: 0001-Properly-pass-empty-type-for-i386-targets.patch --]
[-- Type: text/x-patch, Size: 51744 bytes --]

From d800ae5689948887bcaff21f7b85a5d5193123b0 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 1/3] Properly pass empty type for i386 targets

According to x86 psABIs, no memory slot nor register should be used to
pass or return an object of empty type whose address isn't needed.
Middle-end and x86 backend are updated to ignore empty types, whose
address aren't needed, for parameter passing and function value return.
Other targets need similar changes if they want to follow x86 psABIs.

get_ref_base_and_extent is changed to set bitsize to 0 for empty types
so that when ref_maybe_used_by_call_p_1 calls get_ref_base_and_extent to
get 0 as the maximum size on empty type.  Otherwise, find_tail_calls
won't perform tail call optimization for functions with empty type
parameters, as shown in g++.dg/pr68355.C.

This ABI change is enabled only if the ABI level is at least 10, which
is updated in GCC 6.

gcc/

	PR c++/60336
	PR middle-end/67239
	PR target/68355
	* calls.c (initialize_argument_information): Warn empty type
	if they are used in a variable argument list or aren't the last
	arguments.  Replace targetm.calls.function_arg,
	targetm.calls.function_incoming_arg and
	targetm.calls.function_arg_advance with function_arg,
	function_incoming_arg and function_arg_advance.
	(expand_call): Replace targetm.calls.function_arg,
	targetm.calls.function_incoming_arg and
	targetm.calls.function_arg_advance with function_arg,
	function_incoming_arg and function_arg_advance.
	(emit_library_call_value_1): Likewise.
	(store_one_arg): Use 0 for empty type size.  Don't
	push 0 size argument onto stack.
	(must_pass_in_stack_var_size_or_pad): Return false for empty
	type.
	* dse.c (get_call_args): Replace targetm.calls.function_arg
	and targetm.calls.function_arg_advance with function_arg and
	function_arg_advance.
	* explow.c (hard_function_value): Use 0 for empty type size.
	* expr.c (block_move_libcall_safe_for_call_parm): Replace
	targetm.calls.function_arg and targetm.calls.function_arg_advance
	with function_arg and function_arg_advance.
	(copy_blkmode_to_reg): Use 0 for empty type size.
	* function.c (aggregate_value_p): Replace
	targetm.calls.return_in_memory with return_in_memory.
	(assign_parm_data_all): Add warn_empty_type.
	(assign_parms_augmented_arg_list): Set warn_empty_type if
	empty types are used in a variable argument list or aren't
	the last arguments.
	(assign_parm_find_entry_rtl): Warn empty type if warn_empty_type
	is set.  Replace
	targetm.calls.function_incoming_arg with function_incoming_arg.
	(assign_parms): Only warn empty type once.  Replace
	targetm.calls.function_arg_advance with function_arg_advance.
	(gimplify_parameters): Replace targetm.calls.function_arg_advance
	with function_arg_advance.
	(locate_and_pad_parm): Use 0 for empty type size.
	(warn_empty_type): New function.
	(function_arg_advance): New wrapper function.
	(function_arg): Likewise.
	(function_incoming_arg): Likewise.
	(return_in_memory): Likewise.
	* target.h (function_arg_advance): New prototype.
	(function_arg): Likewise.
	(function_incoming_arg): Likewise.
	(return_in_memory): Likewise.
	* targhooks.c (std_gimplify_va_arg_expr): Use 0 for empty type
	size.
	* tree-dfa.c (get_ref_base_and_extent): Likewise.
	* tree.c (is_empty_type): New functiom.
	(type_is_empty_type_p): Likewise.
	* tree.h (type_is_empty_type_p): New prototype.
	* var-tracking.c (prepare_call_arguments): Replace
	targetm.calls.function_arg and targetm.calls.function_arg_advance
	with function_arg and function_arg_advance.
	* config/i386/i386.c (ix86_gimplify_va_arg): Use 0 for empty
	type size.

gcc/testsuite/

	PR c++/60336
	PR middle-end/67239
	PR target/68355
	* g++.dg/abi/empty12.C: New test.
	* g++.dg/abi/empty12.h: Likewise.
	* g++.dg/abi/empty12a.c: Likewise.
	* g++.dg/abi/empty13.C: Likewise.
	* g++.dg/abi/empty13.h: Likewise.
	* g++.dg/abi/empty13a.c: Likewise.
	* g++.dg/abi/empty14.C: Likewise.
	* g++.dg/abi/empty14.h: Likewise.
	* g++.dg/abi/empty14a.c: Likewise.
	* g++.dg/abi/empty15.C: Likewise.
	* g++.dg/abi/empty15.h: Likewise.
	* g++.dg/abi/empty15a.c: Likewise.
	* g++.dg/abi/empty16.C: Likewise.
	* g++.dg/abi/empty16.h: Likewise.
	* g++.dg/abi/empty16a.c: Likewise.
	* g++.dg/abi/empty17.C: Likewise.
	* g++.dg/abi/empty17.h: Likewise.
	* g++.dg/abi/empty17a.c: Likewise.
	* 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/calls.c                         |  99 +++++++++++++++++++----------
 gcc/config/i386/i386.c              |   3 +-
 gcc/dse.c                           |   4 +-
 gcc/explow.c                        |   4 +-
 gcc/expr.c                          |   8 +--
 gcc/function.c                      | 123 ++++++++++++++++++++++++++++++++----
 gcc/target.h                        |   8 +++
 gcc/targhooks.c                     |   5 +-
 gcc/testsuite/g++.dg/abi/empty12.C  |  17 +++++
 gcc/testsuite/g++.dg/abi/empty12.h  |   9 +++
 gcc/testsuite/g++.dg/abi/empty12a.c |   6 ++
 gcc/testsuite/g++.dg/abi/empty13.C  |  17 +++++
 gcc/testsuite/g++.dg/abi/empty13.h  |   9 +++
 gcc/testsuite/g++.dg/abi/empty13a.c |   6 ++
 gcc/testsuite/g++.dg/abi/empty14.C  |  17 +++++
 gcc/testsuite/g++.dg/abi/empty14.h  |  10 +++
 gcc/testsuite/g++.dg/abi/empty14a.c |   6 ++
 gcc/testsuite/g++.dg/abi/empty15.C  |  17 +++++
 gcc/testsuite/g++.dg/abi/empty15.h  |  30 +++++++++
 gcc/testsuite/g++.dg/abi/empty15a.c |   6 ++
 gcc/testsuite/g++.dg/abi/empty16.C  |  17 +++++
 gcc/testsuite/g++.dg/abi/empty16.h  |  16 +++++
 gcc/testsuite/g++.dg/abi/empty16a.c |   6 ++
 gcc/testsuite/g++.dg/abi/empty17.C  |  17 +++++
 gcc/testsuite/g++.dg/abi/empty17.h  |  27 ++++++++
 gcc/testsuite/g++.dg/abi/empty17a.c |   6 ++
 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 +++++++
 gcc/tree-dfa.c                      |   4 +-
 gcc/tree.c                          |  43 +++++++++++++
 gcc/tree.h                          |   2 +
 gcc/var-tracking.c                  |  18 +++---
 43 files changed, 902 insertions(+), 67 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/abi/empty12.C
 create mode 100644 gcc/testsuite/g++.dg/abi/empty12.h
 create mode 100644 gcc/testsuite/g++.dg/abi/empty12a.c
 create mode 100644 gcc/testsuite/g++.dg/abi/empty13.C
 create mode 100644 gcc/testsuite/g++.dg/abi/empty13.h
 create mode 100644 gcc/testsuite/g++.dg/abi/empty13a.c
 create mode 100644 gcc/testsuite/g++.dg/abi/empty14.C
 create mode 100644 gcc/testsuite/g++.dg/abi/empty14.h
 create mode 100644 gcc/testsuite/g++.dg/abi/empty14a.c
 create mode 100644 gcc/testsuite/g++.dg/abi/empty15.C
 create mode 100644 gcc/testsuite/g++.dg/abi/empty15.h
 create mode 100644 gcc/testsuite/g++.dg/abi/empty15a.c
 create mode 100644 gcc/testsuite/g++.dg/abi/empty16.C
 create mode 100644 gcc/testsuite/g++.dg/abi/empty16.h
 create mode 100644 gcc/testsuite/g++.dg/abi/empty16a.c
 create mode 100644 gcc/testsuite/g++.dg/abi/empty17.C
 create mode 100644 gcc/testsuite/g++.dg/abi/empty17.h
 create mode 100644 gcc/testsuite/g++.dg/abi/empty17a.c
 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/calls.c b/gcc/calls.c
index 8f573b8..d1b498c 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -1159,7 +1159,13 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
   bitmap_obstack_initialize (NULL);
 
   /* In this loop, we consider args in the order they are written.
-     We fill up ARGS from the back.  */
+     We fill up ARGS from the back.  Warn empty type if they are used
+     in a variable argument list or they aren't the last arguments.
+     Set warn_empty_type to true if we don't warn empty type to
+     avoid walking arguments.  */
+  bool seen_empty_type = false;
+  bool warn_empty_type
+    = (!warn_psabi || stdarg_p (fndecl ? TREE_TYPE (fndecl) : fntype));
 
   i = num_actuals - 1;
   {
@@ -1192,6 +1198,15 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
       {
 	tree argtype = TREE_TYPE (arg);
 
+	if (!warn_empty_type)
+	  {
+	    if (argtype != error_mark_node
+		&& type_is_empty_type_p (argtype))
+	      seen_empty_type = true;
+	    else if (seen_empty_type)
+	      warn_empty_type = true;
+	  }
+
 	/* Remember last param with pointer and associate it
 	   with following pointer bounds.  */
 	if (CALL_WITH_BOUNDS_P (exp)
@@ -1406,8 +1421,13 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
       args[i].unsignedp = unsignedp;
       args[i].mode = mode;
 
-      args[i].reg = targetm.calls.function_arg (args_so_far, mode, type,
-						argpos < n_named_args);
+      args[i].reg = function_arg (args_so_far, mode, type,
+				  argpos < n_named_args,
+				  warn_empty_type);
+
+      /* Only warn empty type once.  */
+      if (type_is_empty_type_p (type))
+	warn_empty_type = false;
 
       if (args[i].reg && CONST_INT_P (args[i].reg))
 	{
@@ -1420,8 +1440,8 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
 	 arguments have to go into the incoming registers.  */
       if (targetm.calls.function_incoming_arg != targetm.calls.function_arg)
 	args[i].tail_call_reg
-	  = targetm.calls.function_incoming_arg (args_so_far, mode, type,
-						 argpos < n_named_args);
+	  = function_incoming_arg (args_so_far, mode, type,
+				   argpos < n_named_args);
       else
 	args[i].tail_call_reg = args[i].reg;
 
@@ -1482,8 +1502,8 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
       /* Increment ARGS_SO_FAR, which has info about which arg-registers
 	 have been used, etc.  */
 
-      targetm.calls.function_arg_advance (args_so_far, TYPE_MODE (type),
-					  type, argpos < n_named_args);
+      function_arg_advance (args_so_far, TYPE_MODE (type), type,
+			    argpos < n_named_args);
     }
 }
 
@@ -3335,14 +3355,11 @@ expand_call (tree exp, rtx target, int ignore)
       /* Set up next argument register.  For sibling calls on machines
 	 with register windows this should be the incoming register.  */
       if (pass == 0)
-	next_arg_reg = targetm.calls.function_incoming_arg (args_so_far,
-							    VOIDmode,
-							    void_type_node,
-							    true);
+	next_arg_reg = function_incoming_arg (args_so_far, VOIDmode,
+					      void_type_node, true);
       else
-	next_arg_reg = targetm.calls.function_arg (args_so_far,
-						   VOIDmode, void_type_node,
-						   true);
+	next_arg_reg = function_arg (args_so_far, VOIDmode,
+				     void_type_node, true);
 
       if (pass == 1 && (return_flags & ERF_RETURNS_ARG))
 	{
@@ -3954,8 +3971,8 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
       argvec[count].mode = Pmode;
       argvec[count].partial = 0;
 
-      argvec[count].reg = targetm.calls.function_arg (args_so_far,
-						      Pmode, NULL_TREE, true);
+      argvec[count].reg = function_arg (args_so_far, Pmode, NULL_TREE,
+					true);
       gcc_assert (targetm.calls.arg_partial_bytes (args_so_far, Pmode,
 						   NULL_TREE, 1) == 0);
 
@@ -3972,7 +3989,7 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
 	  || reg_parm_stack_space > 0)
 	args_size.constant += argvec[count].locate.size.constant;
 
-      targetm.calls.function_arg_advance (args_so_far, Pmode, (tree) 0, true);
+      function_arg_advance (args_so_far, Pmode, (tree) 0, true);
 
       count++;
     }
@@ -4037,8 +4054,7 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
       mode = promote_function_mode (NULL_TREE, mode, &unsigned_p, NULL_TREE, 0);
       argvec[count].mode = mode;
       argvec[count].value = convert_modes (mode, GET_MODE (val), val, unsigned_p);
-      argvec[count].reg = targetm.calls.function_arg (args_so_far, mode,
-						      NULL_TREE, true);
+      argvec[count].reg = function_arg (args_so_far, mode, NULL_TREE, true);
 
       argvec[count].partial
 	= targetm.calls.arg_partial_bytes (args_so_far, mode, NULL_TREE, 1);
@@ -4067,7 +4083,7 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
 			     GET_MODE_SIZE (mode) <= UNITS_PER_WORD);
 #endif
 
-      targetm.calls.function_arg_advance (args_so_far, mode, (tree) 0, true);
+      function_arg_advance (args_so_far, mode, (tree) 0, true);
     }
 
   /* If this machine requires an external definition for library
@@ -4414,8 +4430,7 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
 	       build_function_type (tfom, NULL_TREE),
 	       original_args_size.constant, args_size.constant,
 	       struct_value_size,
-	       targetm.calls.function_arg (args_so_far,
-					   VOIDmode, void_type_node, true),
+	       function_arg (args_so_far, VOIDmode, void_type_node, true),
 	       valreg,
 	       old_inhibit_defer_pop + 1, call_fusage, flags, args_so_far);
 
@@ -4850,7 +4865,10 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
 	 Note that in C the default argument promotions
 	 will prevent such mismatches.  */
 
-      size = GET_MODE_SIZE (arg->mode);
+      if (type_is_empty_type_p (TREE_TYPE (pval)))
+	size = 0;
+      else
+	size = GET_MODE_SIZE (arg->mode);
       /* Compute how much space the push instruction will push.
 	 On many machines, pushing a byte will advance the stack
 	 pointer by a halfword.  */
@@ -4880,10 +4898,14 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
 
       /* This isn't already where we want it on the stack, so put it there.
 	 This can either be done with push or copy insns.  */
-      if (!emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), NULL_RTX,
-		      parm_align, partial, reg, used - size, argblock,
-		      ARGS_SIZE_RTX (arg->locate.offset), reg_parm_stack_space,
-		      ARGS_SIZE_RTX (arg->locate.alignment_pad), true))
+      if (used
+	  && !emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval),
+			      NULL_RTX, parm_align, partial, reg,
+			      used - size, argblock,
+			      ARGS_SIZE_RTX (arg->locate.offset),
+			      reg_parm_stack_space,
+			      ARGS_SIZE_RTX (arg->locate.alignment_pad),
+			      true))
 	sibcall_failure = 1;
 
       /* Unless this is a partially-in-register argument, the argument is now
@@ -4915,10 +4937,15 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
 	{
 	  /* PUSH_ROUNDING has no effect on us, because emit_push_insn
 	     for BLKmode is careful to avoid it.  */
+	  bool empty_type = type_is_empty_type_p (TREE_TYPE (pval));
 	  excess = (arg->locate.size.constant
-		    - int_size_in_bytes (TREE_TYPE (pval))
+		    - (empty_type
+		       ? 0
+		       : int_size_in_bytes (TREE_TYPE (pval)))
 		    + partial);
-	  size_rtx = expand_expr (size_in_bytes (TREE_TYPE (pval)),
+	  size_rtx = expand_expr ((empty_type
+				   ? size_zero_node
+				   : size_in_bytes (TREE_TYPE (pval))),
 				  NULL_RTX, TYPE_MODE (sizetype),
 				  EXPAND_NORMAL);
 	}
@@ -4993,10 +5020,13 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
 	    }
 	}
 
-      emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), size_rtx,
-		      parm_align, partial, reg, excess, argblock,
-		      ARGS_SIZE_RTX (arg->locate.offset), reg_parm_stack_space,
-		      ARGS_SIZE_RTX (arg->locate.alignment_pad), false);
+      if (!CONST_INT_P (size_rtx) || INTVAL (size_rtx) != 0)
+	emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval),
+			size_rtx, parm_align, partial, reg, excess,
+			argblock, ARGS_SIZE_RTX (arg->locate.offset),
+			reg_parm_stack_space,
+			ARGS_SIZE_RTX (arg->locate.alignment_pad),
+			false);
 
       /* Unless this is a partially-in-register argument, the argument is now
 	 in the stack.
@@ -5074,6 +5104,9 @@ must_pass_in_stack_var_size_or_pad (machine_mode mode, const_tree type)
   if (TREE_ADDRESSABLE (type))
     return true;
 
+  if (type_is_empty_type_p (type))
+    return false;
+
   /* If the padding and mode of the type is such that a copy into
      a register would put it into the wrong part of the register.  */
   if (mode == BLKmode
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index 8a026ae..0cf6fb2 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -10330,7 +10330,8 @@ ix86_gimplify_va_arg (tree valist, tree type, gimple_seq *pre_p,
   indirect_p = pass_by_reference (NULL, TYPE_MODE (type), type, false);
   if (indirect_p)
     type = build_pointer_type (type);
-  size = int_size_in_bytes (type);
+  bool empty_type = type && type_is_empty_type_p (type);
+  size = empty_type ? 0 : int_size_in_bytes (type);
   rsize = CEIL (size, UNITS_PER_WORD);
 
   nat_mode = type_natural_mode (type, NULL, false);
diff --git a/gcc/dse.c b/gcc/dse.c
index eef5644..ae108a8 100644
--- a/gcc/dse.c
+++ b/gcc/dse.c
@@ -2356,7 +2356,7 @@ get_call_args (rtx call_insn, tree fn, rtx *args, int nargs)
     {
       machine_mode mode = TYPE_MODE (TREE_VALUE (arg));
       rtx reg, link, tmp;
-      reg = targetm.calls.function_arg (args_so_far, mode, NULL_TREE, true);
+      reg = function_arg (args_so_far, mode, NULL_TREE, true);
       if (!reg || !REG_P (reg) || GET_MODE (reg) != mode
 	  || GET_MODE_CLASS (mode) != MODE_INT)
 	return false;
@@ -2390,7 +2390,7 @@ get_call_args (rtx call_insn, tree fn, rtx *args, int nargs)
       if (tmp)
 	args[idx] = tmp;
 
-      targetm.calls.function_arg_advance (args_so_far, mode, NULL_TREE, true);
+      function_arg_advance (args_so_far, mode, NULL_TREE, true);
     }
   if (arg != void_list_node || idx != nargs)
     return false;
diff --git a/gcc/explow.c b/gcc/explow.c
index cd7c568..eb2d7a1 100644
--- a/gcc/explow.c
+++ b/gcc/explow.c
@@ -1856,7 +1856,9 @@ hard_function_value (const_tree valtype, const_tree func, const_tree fntype,
   if (REG_P (val)
       && GET_MODE (val) == BLKmode)
     {
-      unsigned HOST_WIDE_INT bytes = int_size_in_bytes (valtype);
+      unsigned HOST_WIDE_INT bytes = (type_is_empty_type_p (valtype)
+				      ? 0
+				      : int_size_in_bytes (valtype));
       machine_mode tmpmode;
 
       /* int_size_in_bytes can return -1.  We don't need a check here
diff --git a/gcc/expr.c b/gcc/expr.c
index 29d22b0..38e9124 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -1199,13 +1199,12 @@ block_move_libcall_safe_for_call_parm (void)
     for ( ; arg != void_list_node ; arg = TREE_CHAIN (arg))
       {
 	machine_mode mode = TYPE_MODE (TREE_VALUE (arg));
-	rtx tmp = targetm.calls.function_arg (args_so_far, mode,
-					      NULL_TREE, true);
+	rtx tmp = function_arg (args_so_far, mode, NULL_TREE, true);
 	if (!tmp || !REG_P (tmp))
 	  return false;
 	if (targetm.calls.arg_partial_bytes (args_so_far, mode, NULL, 1))
 	  return false;
-	targetm.calls.function_arg_advance (args_so_far, mode,
+	function_arg_advance (args_so_far, mode,
 					    NULL_TREE, true);
       }
   }
@@ -2207,7 +2206,8 @@ copy_blkmode_to_reg (machine_mode mode, tree src)
 
   x = expand_normal (src);
 
-  bytes = int_size_in_bytes (TREE_TYPE (src));
+  bytes = (type_is_empty_type_p (TREE_TYPE (src))
+	   ? 0 : int_size_in_bytes (TREE_TYPE (src)));
   if (bytes == 0)
     return NULL_RTX;
 
diff --git a/gcc/function.c b/gcc/function.c
index 1ac8e26..99bd548 100644
--- a/gcc/function.c
+++ b/gcc/function.c
@@ -2078,7 +2078,7 @@ aggregate_value_p (const_tree exp, const_tree fntype)
   if (flag_pcc_struct_return && AGGREGATE_TYPE_P (type))
     return 1;
 
-  if (targetm.calls.return_in_memory (type, fntype))
+  if (return_in_memory (type, fntype))
     return 1;
 
   /* Make sure we have suitable call-clobbered regs to return
@@ -2247,6 +2247,7 @@ struct assign_parm_data_all
   HOST_WIDE_INT pretend_args_size;
   HOST_WIDE_INT extra_pretend_bytes;
   int reg_parm_stack_space;
+  bool warn_empty_type;
 };
 
 struct assign_parm_data_one
@@ -2412,6 +2413,28 @@ assign_parms_augmented_arg_list (struct assign_parm_data_all *all)
   if (targetm.calls.split_complex_arg)
     split_complex_args (&fnargs);
 
+  /* Warn empty type if they are used in a variable argument list or
+     they aren't the last arguments.  Set warn_empty_type to true if
+     we don't warn empty type to avoid walking arguments.  */
+  bool warn_empty_type = !warn_psabi || stdarg_p (fntype);
+  if (!warn_empty_type)
+    {
+      unsigned int i;
+      bool seen_empty_type = false;
+      FOR_EACH_VEC_ELT (fnargs, i, arg)
+	{
+	  tree type = TREE_TYPE (arg);
+	  if (type != error_mark_node && type_is_empty_type_p (type))
+	    seen_empty_type = true;
+	  else if (seen_empty_type)
+	    {
+	      warn_empty_type = true;
+	      break;
+	    }
+	}
+    }
+  all->warn_empty_type = warn_empty_type;
+
   return fnargs;
 }
 
@@ -2528,10 +2551,11 @@ assign_parm_find_entry_rtl (struct assign_parm_data_all *all,
       return;
     }
 
-  entry_parm = targetm.calls.function_incoming_arg (all->args_so_far,
-						    data->promoted_mode,
-						    data->passed_type,
-						    data->named_arg);
+  entry_parm = function_incoming_arg (all->args_so_far,
+				      data->promoted_mode,
+				      data->passed_type,
+				      data->named_arg,
+				      all->warn_empty_type);
 
   if (entry_parm == 0)
     data->promoted_mode = data->passed_mode;
@@ -2555,9 +2579,9 @@ assign_parm_find_entry_rtl (struct assign_parm_data_all *all,
       if (targetm.calls.pretend_outgoing_varargs_named (all->args_so_far))
 	{
 	  rtx tem;
-	  tem = targetm.calls.function_incoming_arg (all->args_so_far,
-						     data->promoted_mode,
-						     data->passed_type, true);
+	  tem = function_incoming_arg (all->args_so_far,
+				       data->promoted_mode,
+				       data->passed_type, true);
 	  in_regs = tem != NULL;
 	}
     }
@@ -3719,6 +3743,10 @@ assign_parms (tree fndecl)
       /* Find out where the parameter arrives in this function.  */
       assign_parm_find_entry_rtl (&all, &data);
 
+      /* Only warn empty type once.  */
+      if (type_is_empty_type_p (data.passed_type))
+	all.warn_empty_type = false;
+
       /* Find out where stack space for this parameter might be.  */
       if (assign_parm_is_stack_parm (&all, &data))
 	{
@@ -3795,8 +3823,8 @@ assign_parms (tree fndecl)
 	}
 
       /* Update info on where next arg arrives in registers.  */
-      targetm.calls.function_arg_advance (all.args_so_far, data.promoted_mode,
-					  data.passed_type, data.named_arg);
+      function_arg_advance (all.args_so_far, data.promoted_mode,
+			    data.passed_type, data.named_arg);
 
       if (POINTER_BOUNDS_TYPE_P (data.passed_type))
 	bound_no++;
@@ -3992,8 +4020,8 @@ gimplify_parameters (void)
 	continue;
 
       /* Update info on where next arg arrives in registers.  */
-      targetm.calls.function_arg_advance (all.args_so_far, data.promoted_mode,
-					  data.passed_type, data.named_arg);
+      function_arg_advance (all.args_so_far, data.promoted_mode,
+			    data.passed_type, data.named_arg);
 
       /* ??? Once upon a time variable_size stuffed parameter list
 	 SAVE_EXPRs (amongst others) onto a pending sizes list.  This
@@ -4136,8 +4164,11 @@ locate_and_pad_parm (machine_mode passed_mode, tree type, int in_regs,
 
   part_size_in_regs = (reg_parm_stack_space == 0 ? partial : 0);
 
-  sizetree
-    = type ? size_in_bytes (type) : size_int (GET_MODE_SIZE (passed_mode));
+  if (type)
+    sizetree = (type_is_empty_type_p (type)
+		? size_zero_node : size_in_bytes (type));
+  else
+    sizetree = size_int (GET_MODE_SIZE (passed_mode));
   where_pad = FUNCTION_ARG_PADDING (passed_mode, type);
   boundary = targetm.calls.function_arg_boundary (passed_mode, type);
   round_boundary = targetm.calls.function_arg_round_boundary (passed_mode,
@@ -6866,5 +6897,69 @@ make_pass_match_asm_constraints (gcc::context *ctxt)
   return new pass_match_asm_constraints (ctxt);
 }
 
+static void
+warn_empty_type (void)
+{
+  if (warn_psabi)
+    inform (input_location, "the ABI of passing empty type has"
+	    " changed in GCC 6");
+}
+
+/* Wrapper for targetm.calls.function_arg_advance.  */
+
+void
+function_arg_advance (cumulative_args_t ca, machine_mode mode,
+		      const_tree type, bool named)
+{
+  if (type && type_is_empty_type_p (type))
+    return;
+
+  targetm.calls.function_arg_advance (ca, mode, type, named);
+}
+
+/* Wrapper for targetm.calls.function_arg.  */
+
+rtx
+function_arg (cumulative_args_t ca, machine_mode mode, const_tree type,
+	      bool named, bool warn_empty_type_p)
+{
+  if (type && type_is_empty_type_p (type))
+    {
+      if (warn_empty_type_p)
+	warn_empty_type ();
+      return NULL;
+    }
+
+  return targetm.calls.function_arg (ca, mode, type, named);
+}
+
+/* Wrapper for targetm.calls.function_incoming_arg.  */
+
+rtx
+function_incoming_arg (cumulative_args_t ca, machine_mode mode,
+		       const_tree type, bool named,
+		       bool warn_empty_type_p)
+{
+  if (type && type_is_empty_type_p (type))
+    {
+      if (warn_empty_type_p)
+	warn_empty_type ();
+      return NULL;
+    }
+
+  return targetm.calls.function_incoming_arg (ca, mode, type, named);
+}
+
+/* Wrapper for targetm.calls.return_in_memory.  */
+
+bool
+return_in_memory (const_tree type, const_tree fntype)
+{
+  if (type && type_is_empty_type_p (type))
+    return false;
+
+  return targetm.calls.return_in_memory (type, fntype);
+}
+
 
 #include "gt-function.h"
diff --git a/gcc/target.h b/gcc/target.h
index 43022bd..3968a1f 100644
--- a/gcc/target.h
+++ b/gcc/target.h
@@ -104,6 +104,14 @@ extern bool target_default_pointer_address_modes_p (void);
    behavior.  */
 extern unsigned int get_move_ratio (bool);
 
+extern void function_arg_advance (cumulative_args_t, machine_mode,
+				  const_tree, bool);
+extern rtx function_arg (cumulative_args_t, machine_mode, const_tree,
+			 bool, bool = false);
+extern rtx function_incoming_arg (cumulative_args_t, machine_mode,
+				  const_tree, bool, bool = false);
+extern bool return_in_memory (const_tree, const_tree);
+
 struct stdarg_info;
 struct spec_info_def;
 struct hard_reg_set_container;
diff --git a/gcc/targhooks.c b/gcc/targhooks.c
index 74af91a..f0a6759 100644
--- a/gcc/targhooks.c
+++ b/gcc/targhooks.c
@@ -1828,9 +1828,12 @@ std_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
   /* Hoist the valist value into a temporary for the moment.  */
   valist_tmp = get_initialized_tmp_var (valist, pre_p, NULL);
 
+  bool empty_type = type_is_empty_type_p (type);
+
   /* va_list pointer is aligned to PARM_BOUNDARY.  If argument actually
      requires greater alignment, we must perform dynamic alignment.  */
   if (boundary > align
+      && !empty_type
       && !integer_zerop (TYPE_SIZE (type)))
     {
       t = build2 (MODIFY_EXPR, TREE_TYPE (valist), valist_tmp,
@@ -1857,7 +1860,7 @@ std_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
     }
 
   /* Compute the rounded size of the type.  */
-  type_size = size_in_bytes (type);
+  type_size = empty_type ? size_zero_node : size_in_bytes (type);
   rounded_size = round_up (type_size, align);
 
   /* Reduce rounded_size so it's sharable with the postqueue.  */
diff --git a/gcc/testsuite/g++.dg/abi/empty12.C b/gcc/testsuite/g++.dg/abi/empty12.C
new file mode 100644
index 0000000..ff2909c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/empty12.C
@@ -0,0 +1,17 @@
+// PR c++/60336
+// { dg-do run }
+// { dg-options "-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-message "note: the ABI of passing empty type has changed in GCC 6" }
+  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..712d4e8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/empty14.C
@@ -0,0 +1,17 @@
+// PR c++/60336
+// { dg-do run }
+// { dg-options "-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-message "note: the ABI of passing empty type has changed in GCC 6" }
+  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..24bf047
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/empty15.C
@@ -0,0 +1,17 @@
+// PR c++/60336
+// { dg-do run }
+// { dg-options "-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-message "note: the ABI of passing empty type has changed in GCC 6" }
+  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..f058720
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/empty16.C
@@ -0,0 +1,17 @@
+// PR c++/60336
+// { dg-do run }
+// { dg-options "-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-message "note: the ABI of passing empty type has changed in GCC 6" }
+  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..f6f88c8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/empty17.C
@@ -0,0 +1,17 @@
+// PR c++/60336
+// { dg-do run }
+// { dg-options "-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-message "note: the ABI of passing empty type has changed in GCC 6" }
+  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/pr60336-1.C b/gcc/testsuite/g++.dg/pr60336-1.C
new file mode 100644
index 0000000..af08638
--- /dev/null
+++ b/gcc/testsuite/g++.dg/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/pr60336-10.C b/gcc/testsuite/g++.dg/pr60336-10.C
new file mode 100644
index 0000000..6c9c990
--- /dev/null
+++ b/gcc/testsuite/g++.dg/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/pr60336-11.C b/gcc/testsuite/g++.dg/pr60336-11.C
new file mode 100644
index 0000000..c92f3d4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/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/pr60336-12.C b/gcc/testsuite/g++.dg/pr60336-12.C
new file mode 100644
index 0000000..83a7bb0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/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/pr60336-2.C b/gcc/testsuite/g++.dg/pr60336-2.C
new file mode 100644
index 0000000..ad63cee
--- /dev/null
+++ b/gcc/testsuite/g++.dg/pr60336-2.C
@@ -0,0 +1,48 @@
+// { dg-do run }
+// { dg-options "-O2" }
+
+#include <stdarg.h>
+
+struct dummy { };
+
+void
+test (struct dummy a, int m, ...) // { dg-message "note: the ABI of passing empty type has changed in GCC 6" }
+{
+  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 "note: the ABI of passing empty type has changed in GCC 6" }
+  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..9ec4914
--- /dev/null
+++ b/gcc/testsuite/g++.dg/pr60336-3.C
@@ -0,0 +1,15 @@
+// { dg-do compile }
+// { dg-options "-O2" }
+
+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); // { dg-message "note: the ABI of passing empty type has changed in GCC 6" }
+  test2 (a0); // { dg-message "note: the ABI of passing empty type has changed in GCC 6" }
+}
diff --git a/gcc/testsuite/g++.dg/pr60336-4.C b/gcc/testsuite/g++.dg/pr60336-4.C
new file mode 100644
index 0000000..8790a66
--- /dev/null
+++ b/gcc/testsuite/g++.dg/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/pr60336-5.C b/gcc/testsuite/g++.dg/pr60336-5.C
new file mode 100644
index 0000000..b0c76ad
--- /dev/null
+++ b/gcc/testsuite/g++.dg/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/pr60336-6.C b/gcc/testsuite/g++.dg/pr60336-6.C
new file mode 100644
index 0000000..5879651
--- /dev/null
+++ b/gcc/testsuite/g++.dg/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/pr60336-7.C b/gcc/testsuite/g++.dg/pr60336-7.C
new file mode 100644
index 0000000..0e5d2ef
--- /dev/null
+++ b/gcc/testsuite/g++.dg/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/pr60336-8.C b/gcc/testsuite/g++.dg/pr60336-8.C
new file mode 100644
index 0000000..2c22f78
--- /dev/null
+++ b/gcc/testsuite/g++.dg/pr60336-8.C
@@ -0,0 +1,15 @@
+// { dg-do compile }
+// { dg-options "-O2" }
+
+struct dummy { struct{} a[7][3]; };
+
+extern void test1 (struct dummy, ...);
+extern void (*test2) (struct dummy, ...);
+
+void
+foo ()
+{
+  struct dummy a0;
+  test1 (a0); // { dg-message "note: the ABI of passing empty type has changed in GCC 6" }
+  test2 (a0); // { dg-message "note: the ABI of passing empty type has changed in GCC 6" }
+}
diff --git a/gcc/testsuite/g++.dg/pr60336-9.C b/gcc/testsuite/g++.dg/pr60336-9.C
new file mode 100644
index 0000000..4ad333f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/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/pr68355.C b/gcc/testsuite/g++.dg/pr68355.C
new file mode 100644
index 0000000..1354fc4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/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-*-* } } }
diff --git a/gcc/tree-dfa.c b/gcc/tree-dfa.c
index 0e98056..b74d34b 100644
--- a/gcc/tree-dfa.c
+++ b/gcc/tree-dfa.c
@@ -392,7 +392,9 @@ get_ref_base_and_extent (tree exp, HOST_WIDE_INT *poffset,
   else if (!VOID_TYPE_P (TREE_TYPE (exp)))
     {
       machine_mode mode = TYPE_MODE (TREE_TYPE (exp));
-      if (mode == BLKmode)
+      if (type_is_empty_type_p (TREE_TYPE (exp)))
+	bitsize = 0;
+      else if (mode == BLKmode)
 	size_tree = TYPE_SIZE (TREE_TYPE (exp));
       else
 	bitsize = int (GET_MODE_BITSIZE (mode));
diff --git a/gcc/tree.c b/gcc/tree.c
index b8333d4..900a4fa 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -14050,4 +14050,47 @@ combined_fn_name (combined_fn fn)
     return internal_fn_name (as_internal_fn (fn));
 }
 
+/* Returns true if TYPE is a type where it and all of its subobjects
+  (recursively) are of structure, union, or array type.  */
+
+static bool
+is_empty_type (tree type)
+{
+  if (RECORD_OR_UNION_TYPE_P (type))
+    {
+      tree field;
+      for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
+	if (TREE_CODE (field) == FIELD_DECL
+	    && (DECL_NAME (field)
+		|| RECORD_OR_UNION_TYPE_P (TREE_TYPE (field)))
+	    && !is_empty_type (TREE_TYPE (field)))
+	  return false;
+      return true;
+    }
+  else if (TREE_CODE (type) == ARRAY_TYPE)
+    return is_empty_type (TREE_TYPE (type));
+  return false;
+}
+
+/* Return true if TYPE is an empty type of non-zero size whose address
+   isn't needed.  */
+
+bool
+type_is_empty_type_p (const_tree type)
+{
+  if (!abi_version_at_least (10))
+    return false;
+
+  if (type == error_mark_node)
+    return false;
+
+  if (TREE_ADDRESSABLE (type))
+    return false;
+
+  if (int_size_in_bytes (type) == 0)
+    return false;
+
+  return is_empty_type (TYPE_MAIN_VARIANT (type));
+}
+
 #include "gt-tree.h"
diff --git a/gcc/tree.h b/gcc/tree.h
index 544a6a1..9686482 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -5399,6 +5399,8 @@ extern void gt_pch_nx (tree &, gt_pointer_operator, void *);
 extern bool nonnull_arg_p (const_tree);
 extern bool is_redundant_typedef (const_tree);
 
+extern bool type_is_empty_type_p (const_tree t);
+
 extern location_t
 set_source_range (tree expr, location_t start, location_t finish);
 
diff --git a/gcc/var-tracking.c b/gcc/var-tracking.c
index 9f09d30..15a6fa0 100644
--- a/gcc/var-tracking.c
+++ b/gcc/var-tracking.c
@@ -6241,10 +6241,10 @@ prepare_call_arguments (basic_block bb, rtx_insn *insn)
 		  rtx reg;
 		  INIT_CUMULATIVE_ARGS (args_so_far_v, type, NULL_RTX, fndecl,
 					nargs + 1);
-		  reg = targetm.calls.function_arg (args_so_far, mode,
-						    struct_addr, true);
-		  targetm.calls.function_arg_advance (args_so_far, mode,
-						      struct_addr, true);
+		  reg = function_arg (args_so_far, mode, struct_addr,
+				      true);
+		  function_arg_advance (args_so_far, mode, struct_addr,
+					true);
 		  if (reg == NULL_RTX)
 		    {
 		      for (; link; link = XEXP (link, 1))
@@ -6265,8 +6265,8 @@ prepare_call_arguments (basic_block bb, rtx_insn *insn)
 		  machine_mode mode;
 		  t = TYPE_ARG_TYPES (type);
 		  mode = TYPE_MODE (TREE_VALUE (t));
-		  this_arg = targetm.calls.function_arg (args_so_far, mode,
-							 TREE_VALUE (t), true);
+		  this_arg = function_arg (args_so_far, mode,
+					   TREE_VALUE (t), true);
 		  if (this_arg && !REG_P (this_arg))
 		    this_arg = NULL_RTX;
 		  else if (this_arg == NULL_RTX)
@@ -6381,8 +6381,7 @@ prepare_call_arguments (basic_block bb, rtx_insn *insn)
 		argtype = build_pointer_type (argtype);
 		mode = TYPE_MODE (argtype);
 	      }
-	    reg = targetm.calls.function_arg (args_so_far, mode,
-					      argtype, true);
+	    reg = function_arg (args_so_far, mode, argtype, true);
 	    if (TREE_CODE (argtype) == REFERENCE_TYPE
 		&& INTEGRAL_TYPE_P (TREE_TYPE (argtype))
 		&& reg
@@ -6436,8 +6435,7 @@ prepare_call_arguments (basic_block bb, rtx_insn *insn)
 			}
 		  }
 	      }
-	    targetm.calls.function_arg_advance (args_so_far, mode,
-						argtype, true);
+	    function_arg_advance (args_so_far, mode, argtype, true);
 	    t = TREE_CHAIN (t);
 	  }
       }
-- 
2.5.0


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

* Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
  2016-03-02 17:34                                                         ` H.J. Lu
@ 2016-03-15 15:35                                                           ` Jason Merrill
  2016-03-15 16:00                                                             ` H.J. Lu
  2016-03-15 21:40                                                             ` Joseph Myers
  0 siblings, 2 replies; 52+ messages in thread
From: Jason Merrill @ 2016-03-15 15:35 UTC (permalink / raw)
  To: H.J. Lu, Ulrich Weigand
  Cc: GCC Patches, Jakub Jelinek, Richard Biener, Markus Trippelsdorf,
	Jakub Jelinek

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

I'm concerned about how this patch changes both target-independent code 
and target-specific code, with a passing remark that other targets might 
need to make similar changes.  I'm also concerned about the effect of 
this on other languages that might not want the same change.  So, here's 
an alternative patch that implements the change in the front end (and 
includes your testcases, thanks!).

Thoughts?

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

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

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

diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index f6ad696..1027dad 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -8361,6 +8361,8 @@ 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;

commit 4a683e9e5e3b3ee824dbf86dd2ad7508ea4fdc3f
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 3ad3bd5..d7cfb99 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...  */
@@ -341,7 +343,6 @@ build_call_a (tree function, int n, tree *argarray)
   tree decl;
   tree result_type;
   tree fntype;
-  int i;
 
   function = build_addr_func (function, tf_warning_or_error);
 
@@ -379,16 +380,24 @@ 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;
   if (! decl || ! DECL_BUILT_IN (decl))
-    for (i = 0; i < n; i++)
+    for (int 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;
+	    location_t loc = EXPR_LOC_OR_LOC (arg, input_location);
+	    CALL_EXPR_ARG (function, i) = empty_class_arg (arg);
+	    /* Warn about ABI changes for a non-final argument.  */
+	    if (!warned && i < n-1
+		&& warn_empty_class_abi (arg, loc))
+	      warned = true;
 	  }
       }
 
@@ -6871,6 +6880,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);
 }
@@ -6967,6 +6984,65 @@ 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.  */
+
+bool
+warn_empty_class_abi (tree arg, location_t loc)
+{
+  if (!warn_abi || !abi_version_crosses (10))
+    return false;
+
+  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);
+    }
+
+  if (!dependent_type_p (type)
+      && !TREE_ADDRESSABLE (type)
+      && is_really_empty_class (type))
+    {
+      warning_at (loc, OPT_Wabi, "empty class %qT parameter passing ABI "
+	       "changes in -fabi-version=10 (GCC 6)", type);
+      return true;
+    }
+  return false;
+}
+
 /* Returns the type which will really be used for passing an argument of
    type TYPE.  */
 
@@ -6985,6 +7061,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 a08c59b..3f24f7d 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.  */
@@ -5571,6 +5574,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 bool 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 27c3597..4ee4ccc 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -4145,6 +4145,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);
@@ -14243,6 +14247,9 @@ 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 = (processing_template_decl
+		     || !TREE_PUBLIC (current_function_decl));
       for (parm = specparms; parm; parm = next)
 	{
 	  next = DECL_CHAIN (parm);
@@ -14253,6 +14260,13 @@ store_parm_decls (tree current_function_parms)
 		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 (!warned
+		  && (parm != specparms
+		      || 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/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-*-* } } }

commit b3d57318c2372703cf725d4cfd3752bc1f2a65fa
Author: Jason Merrill <jason@redhat.com>
Date:   Thu Mar 10 13:56:16 2016 -0500

    wabi

diff --git a/libstdc++-v3/acinclude.m4 b/libstdc++-v3/acinclude.m4
index 95df24a..94fde7e 100644
--- a/libstdc++-v3/acinclude.m4
+++ b/libstdc++-v3/acinclude.m4
@@ -765,7 +765,7 @@ AC_DEFUN([GLIBCXX_EXPORT_FLAGS], [
   # OPTIMIZE_CXXFLAGS = -O3 -fstrict-aliasing -fvtable-gc
   AC_SUBST(OPTIMIZE_CXXFLAGS)
 
-  WARN_FLAGS='-Wall -Wextra -Wwrite-strings -Wcast-qual -Wabi'
+  WARN_FLAGS='-Wall -Wextra -Wwrite-strings -Wcast-qual -Werror=abi -Wabi=9'
   AC_SUBST(WARN_FLAGS)
 ])
 
diff --git a/libstdc++-v3/configure b/libstdc++-v3/configure
index acbc6a6..4913211 100755
--- a/libstdc++-v3/configure
+++ b/libstdc++-v3/configure
@@ -81354,7 +81354,7 @@ $as_echo "$gxx_include_dir" >&6; }
   # OPTIMIZE_CXXFLAGS = -O3 -fstrict-aliasing -fvtable-gc
 
 
-  WARN_FLAGS='-Wall -Wextra -Wwrite-strings -Wcast-qual -Wabi'
+  WARN_FLAGS='-Wall -Wextra -Wwrite-strings -Wcast-qual -Werror=abi -Wabi=9'
 
 
 

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

* Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
  2016-03-15 15:35                                                           ` Jason Merrill
@ 2016-03-15 16:00                                                             ` H.J. Lu
  2016-03-15 19:32                                                               ` Jason Merrill
  2016-03-15 21:40                                                             ` Joseph Myers
  1 sibling, 1 reply; 52+ messages in thread
From: H.J. Lu @ 2016-03-15 16:00 UTC (permalink / raw)
  To: Jason Merrill
  Cc: Ulrich Weigand, GCC Patches, Jakub Jelinek, Richard Biener,
	Markus Trippelsdorf

On Tue, Mar 15, 2016 at 8:35 AM, Jason Merrill <jason@redhat.com> wrote:
> I'm concerned about how this patch changes both target-independent code and
> target-specific code, with a passing remark that other targets might need to
> make similar changes.  I'm also concerned about the effect of this on other
> languages that might not want the same change.  So, here's an alternative
> patch that implements the change in the front end (and includes your
> testcases, thanks!).
>
> Thoughts?

On x86-64, I got

libtool: compile:
/export/build/gnu/gcc-x32/build-x86_64-linux/./gcc/xgcc -shared-libgcc
-B/export/build/gnu/gcc-x32/build-x86_64-linux/./gcc -nostdinc++
-L/export/build/gnu/gcc-x32/build-x86_64-linux/x86_64-pc-linux-gnu/libstdc++-v3/src
-L/export/build/gnu/gcc-x32/build-x86_64-linux/x86_64-pc-linux-gnu/libstdc++-v3/src/.libs
-L/export/build/gnu/gcc-x32/build-x86_64-linux/x86_64-pc-linux-gnu/libstdc++-v3/libsupc++/.libs
-B/usr/gcc-6.0.0-x32/x86_64-pc-linux-gnu/bin/
-B/usr/gcc-6.0.0-x32/x86_64-pc-linux-gnu/lib/ -isystem
/usr/gcc-6.0.0-x32/x86_64-pc-linux-gnu/include -isystem
/usr/gcc-6.0.0-x32/x86_64-pc-linux-gnu/sys-include
-I/export/gnu/import/git/sources/gcc/libstdc++-v3/../libgcc
-I/export/build/gnu/gcc-x32/build-x86_64-linux/x86_64-pc-linux-gnu/libstdc++-v3/include/x86_64-pc-linux-gnu
-I/export/build/gnu/gcc-x32/build-x86_64-linux/x86_64-pc-linux-gnu/libstdc++-v3/include
-I/export/gnu/import/git/sources/gcc/libstdc++-v3/libsupc++
-std=gnu++11 -D_GLIBCXX_SHARED -fno-implicit-templates -Wall -Wextra
-Wwrite-strings -Wcast-qual -Werror=abi -Wabi=9
-fdiagnostics-show-location=once -ffunction-sections -fdata-sections
-frandom-seed=cow-shim_facets.lo -g -O2 -D_GNU_SOURCE -c
/export/gnu/import/git/sources/gcc/libstdc++-v3/src/c++11/cow-shim_facets.cc
 -fPIC -DPIC -D_GLIBCXX_SHARED -o cow-shim_facets.o
In file included from
/export/gnu/import/git/sources/gcc/libstdc++-v3/src/c++11/cow-shim_facets.cc:35:0:
/export/gnu/import/git/sources/gcc/libstdc++-v3/src/c++11/cxx11-shim_facets.cc:
In instantiation of
‘std::__facet_shims::{anonymous}::numpunct_shim<_CharT>::numpunct_shim(const
facet*, std::__facet_shims::{anonymous}::numpunct_shim<_CharT>::__cache_type*)
[with _CharT = char; std::__facet_shims::facet = std::locale::facet;
std::__facet_shims::{anonymous}::numpunct_shim<_CharT>::__cache_type =
std::__numpunct_cache<char>]’:
/export/gnu/import/git/sources/gcc/libstdc++-v3/src/c++11/cxx11-shim_facets.cc:461:20:
  required from here
/export/gnu/import/git/sources/gcc/libstdc++-v3/src/c++11/cxx11-shim_facets.cc:238:25:
error: empty class ‘std::__facet_shims::other_abi {aka
std::integral_constant<bool, true>}’ parameter passing ABI changes in
-fabi-version=10 (GCC 6) [-Werror=abi]
    __numpunct_fill_cache(other_abi{}, f, c);
    ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~
/export/gnu/import/git/sources/gcc/libstdc++-v3/src/c++11/cxx11-shim_facets.cc:
In instantiation of ‘int
std::__facet_shims::{anonymous}::collate_shim<_CharT>::do_compare(const
_CharT*, const _CharT*, const _CharT*, const _CharT*) const [with
_CharT = char]’:
/export/gnu/import/git/sources/gcc/libstdc++-v3/src/c++11/cxx11-shim_facets.cc:462:20:
  required from here
/export/gnu/import/git/sources/gcc/libstdc++-v3/src/c++11/cxx11-shim_facets.cc:265:28:
error: empty class ‘std::__facet_shims::other_abi {aka
std::integral_constant<bool, true>}’ parameter passing ABI changes in
-fabi-version=10 (GCC 6) [-Werror=abi]
    return __collate_compare(other_abi{}, _M_get(),
           ~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~
        lo1, hi1, lo2, hi2);
        ~~~~~~~~~~~~~~~~~~~
/export/gnu/import/git/sources/gcc/libstdc++-v3/src/c++11/cxx11-shim_facets.cc:
In instantiation of
‘std::__facet_shims::{anonymous}::collate_shim<_CharT>::string_type
std::__facet_shims::{anonymous}::collate_shim<_CharT>::do_transform(const
_CharT*, const _CharT*) const [with _CharT = char;
std::__facet_shims::{anonymous}::collate_shim<_CharT>::string_type =
std::basic_string<char>]’:
/export/gnu/import/git/sources/gcc/libstdc++-v3/src/c++11/cxx11-shim_facets.cc:462:20:
  required from here
/export/gnu/import/git/sources/gcc/libstdc++-v3/src/c++11/cxx11-shim_facets.cc:273:23:
error: empty class ‘std::__facet_shims::other_abi {aka
std::integral_constant<bool, true>}’ parameter passing ABI changes in
-fabi-version=10 (GCC 6) [-Werror=abi]
    __collate_transform(other_abi{}, _M_get(), st, lo, hi);
    ~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/export/gnu/import/git/sources/gcc/libstdc++-v3/src/c++11/cxx11-shim_facets.cc:
In instantiation of
‘std::__facet_shims::{anonymous}::moneypunct_shim<_CharT,
_Intl>::moneypunct_shim(const facet*,
std::__facet_shims::{anonymous}::moneypunct_shim<_CharT,
_Intl>::__cache_type*) [with _CharT = char; bool _Intl = true;
std::__facet_shims::facet = std::locale::facet;
std::__facet_shims::{anonymous}::moneypunct_shim<_CharT,
_Intl>::__cache_type = std::__moneypunct_cache<char, true>]’:
...

-- 
H.J.

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

* Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
  2016-03-15 16:00                                                             ` H.J. Lu
@ 2016-03-15 19:32                                                               ` Jason Merrill
  2016-03-16 12:38                                                                 ` H.J. Lu
  0 siblings, 1 reply; 52+ messages in thread
From: Jason Merrill @ 2016-03-15 19:32 UTC (permalink / raw)
  To: H.J. Lu
  Cc: Ulrich Weigand, GCC Patches, Jakub Jelinek, Richard Biener,
	Markus Trippelsdorf

On 03/15/2016 12:00 PM, H.J. Lu wrote:
> On Tue, Mar 15, 2016 at 8:35 AM, Jason Merrill <jason@redhat.com> wrote:
>> I'm concerned about how this patch changes both target-independent code and
>> target-specific code, with a passing remark that other targets might need to
>> make similar changes.  I'm also concerned about the effect of this on other
>> languages that might not want the same change.  So, here's an alternative
>> patch that implements the change in the front end (and includes your
>> testcases, thanks!).
>>
>> Thoughts?
>
> On x86-64, I got
>
> /export/gnu/import/git/sources/gcc/libstdc++-v3/src/c++11/cxx11-shim_facets.cc:273:23:
> error: empty class ‘std::__facet_shims::other_abi {aka
> std::integral_constant<bool, true>}’ parameter passing ABI changes in
> -fabi-version=10 (GCC 6) [-Werror=abi]
>      __collate_transform(other_abi{}, _M_get(), st, lo, hi);

Right, need to remove the -Werror=abi bit from the patch until Jonathan 
updates libstdc++.

Jason

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

* Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
  2016-03-15 15:35                                                           ` Jason Merrill
  2016-03-15 16:00                                                             ` H.J. Lu
@ 2016-03-15 21:40                                                             ` Joseph Myers
  2016-03-15 22:31                                                               ` H.J. Lu
  1 sibling, 1 reply; 52+ messages in thread
From: Joseph Myers @ 2016-03-15 21:40 UTC (permalink / raw)
  To: Jason Merrill
  Cc: H.J. Lu, Ulrich Weigand, GCC Patches, Jakub Jelinek,
	Richard Biener, Markus Trippelsdorf, Jakub Jelinek

I'm not sure if the zero-size arrays (a GNU extension) are considered to 
make a struct non-empty, but in any case I think the tests should cover 
such arrays as elements of structs.

-- 
Joseph S. Myers
joseph@codesourcery.com

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

* Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
  2016-03-15 21:40                                                             ` Joseph Myers
@ 2016-03-15 22:31                                                               ` H.J. Lu
  2016-03-15 22:35                                                                 ` Joseph Myers
  0 siblings, 1 reply; 52+ messages in thread
From: H.J. Lu @ 2016-03-15 22:31 UTC (permalink / raw)
  To: Joseph Myers
  Cc: Jason Merrill, Ulrich Weigand, GCC Patches, Jakub Jelinek,
	Richard Biener, Markus Trippelsdorf

On Tue, Mar 15, 2016 at 2:39 PM, Joseph Myers <joseph@codesourcery.com> wrote:
> I'm not sure if the zero-size arrays (a GNU extension) are considered to
> make a struct non-empty, but in any case I think the tests should cover
> such arrays as elements of structs.

There are couple tests for structs with members of array
of empty types.  testsuite/g++.dg/abi/empty14.h has

struct dummy0 { };
struct dummy { struct dummy0 d[140]; };

-- 
H.J.

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

* Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
  2016-03-15 22:31                                                               ` H.J. Lu
@ 2016-03-15 22:35                                                                 ` Joseph Myers
  2016-03-16  0:23                                                                   ` H.J. Lu
  0 siblings, 1 reply; 52+ messages in thread
From: Joseph Myers @ 2016-03-15 22:35 UTC (permalink / raw)
  To: H.J. Lu
  Cc: Jason Merrill, Ulrich Weigand, GCC Patches, Jakub Jelinek,
	Richard Biener, Markus Trippelsdorf

On Tue, 15 Mar 2016, H.J. Lu wrote:

> On Tue, Mar 15, 2016 at 2:39 PM, Joseph Myers <joseph@codesourcery.com> wrote:
> > I'm not sure if the zero-size arrays (a GNU extension) are considered to
> > make a struct non-empty, but in any case I think the tests should cover
> > such arrays as elements of structs.
> 
> There are couple tests for structs with members of array
> of empty types.  testsuite/g++.dg/abi/empty14.h has

My concern is the other way round - structs with elements such as 
"int a[0];", an array [0] of a nonempty type.  My reading of the subobject 
definition is that such an array should not cause the struct to be 
considered nonempty (it doesn't result in any int subobjects).

-- 
Joseph S. Myers
joseph@codesourcery.com

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

* Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
  2016-03-15 22:35                                                                 ` Joseph Myers
@ 2016-03-16  0:23                                                                   ` H.J. Lu
  2016-03-16  0:25                                                                     ` Joseph Myers
  0 siblings, 1 reply; 52+ messages in thread
From: H.J. Lu @ 2016-03-16  0:23 UTC (permalink / raw)
  To: Joseph Myers
  Cc: Jason Merrill, Ulrich Weigand, GCC Patches, Jakub Jelinek,
	Richard Biener, Markus Trippelsdorf

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

On Tue, Mar 15, 2016 at 3:34 PM, Joseph Myers <joseph@codesourcery.com> wrote:
> On Tue, 15 Mar 2016, H.J. Lu wrote:
>
>> On Tue, Mar 15, 2016 at 2:39 PM, Joseph Myers <joseph@codesourcery.com> wrote:
>> > I'm not sure if the zero-size arrays (a GNU extension) are considered to
>> > make a struct non-empty, but in any case I think the tests should cover
>> > such arrays as elements of structs.
>>
>> There are couple tests for structs with members of array
>> of empty types.  testsuite/g++.dg/abi/empty14.h has
>
> My concern is the other way round - structs with elements such as
> "int a[0];", an array [0] of a nonempty type.  My reading of the subobject
> definition is that such an array should not cause the struct to be
> considered nonempty (it doesn't result in any int subobjects).

This is a test for struct with zero-size array, which isn't treated
as empty type.  C++ and C are compatible in its passing.

-- 
H.J.

[-- Attachment #2: 0001-Add-a-test-for-struct-with-zero-size-array.patch --]
[-- Type: text/x-patch, Size: 1744 bytes --]

From 549583547f8dfb284b6ae083031757371907671f Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@gmail.com>
Date: Tue, 15 Mar 2016 17:20:08 -0700
Subject: [PATCH] Add a test for struct with zero-size array

---
 gcc/testsuite/g++.dg/abi/empty18.C  | 17 +++++++++++++++++
 gcc/testsuite/g++.dg/abi/empty18.h  |  9 +++++++++
 gcc/testsuite/g++.dg/abi/empty18a.c |  6 ++++++
 3 files changed, 32 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/abi/empty18.C
 create mode 100644 gcc/testsuite/g++.dg/abi/empty18.h
 create mode 100644 gcc/testsuite/g++.dg/abi/empty18a.c

diff --git a/gcc/testsuite/g++.dg/abi/empty18.C b/gcc/testsuite/g++.dg/abi/empty18.C
new file mode 100644
index 0000000..cf850ce
--- /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);
+  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();
+}
-- 
2.5.0


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

* Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
  2016-03-16  0:23                                                                   ` H.J. Lu
@ 2016-03-16  0:25                                                                     ` Joseph Myers
  2016-03-16  2:17                                                                       ` H.J. Lu
  2016-03-16  2:51                                                                       ` Jason Merrill
  0 siblings, 2 replies; 52+ messages in thread
From: Joseph Myers @ 2016-03-16  0:25 UTC (permalink / raw)
  To: H.J. Lu
  Cc: Jason Merrill, Ulrich Weigand, GCC Patches, Jakub Jelinek,
	Richard Biener, Markus Trippelsdorf

On Tue, 15 Mar 2016, H.J. Lu wrote:

> On Tue, Mar 15, 2016 at 3:34 PM, Joseph Myers <joseph@codesourcery.com> wrote:
> > On Tue, 15 Mar 2016, H.J. Lu wrote:
> >
> >> On Tue, Mar 15, 2016 at 2:39 PM, Joseph Myers <joseph@codesourcery.com> wrote:
> >> > I'm not sure if the zero-size arrays (a GNU extension) are considered to
> >> > make a struct non-empty, but in any case I think the tests should cover
> >> > such arrays as elements of structs.
> >>
> >> There are couple tests for structs with members of array
> >> of empty types.  testsuite/g++.dg/abi/empty14.h has
> >
> > My concern is the other way round - structs with elements such as
> > "int a[0];", an array [0] of a nonempty type.  My reading of the subobject
> > definition is that such an array should not cause the struct to be
> > considered nonempty (it doesn't result in any int subobjects).
> 
> This is a test for struct with zero-size array, which isn't treated
> as empty type.  C++ and C are compatible in its passing.

Where is the current definition of empty types you're proposing for use in 
GCC?  Is the behavior of this case clear from that definition?

-- 
Joseph S. Myers
joseph@codesourcery.com

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

* Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
  2016-03-16  0:25                                                                     ` Joseph Myers
@ 2016-03-16  2:17                                                                       ` H.J. Lu
  2016-03-16  9:46                                                                         ` Bernhard Reutner-Fischer
  2016-03-16  2:51                                                                       ` Jason Merrill
  1 sibling, 1 reply; 52+ messages in thread
From: H.J. Lu @ 2016-03-16  2:17 UTC (permalink / raw)
  To: Joseph Myers
  Cc: Jason Merrill, Ulrich Weigand, GCC Patches, Jakub Jelinek,
	Richard Biener, Markus Trippelsdorf

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

On Tue, Mar 15, 2016 at 5:25 PM, Joseph Myers <joseph@codesourcery.com> wrote:
> On Tue, 15 Mar 2016, H.J. Lu wrote:
>
>> On Tue, Mar 15, 2016 at 3:34 PM, Joseph Myers <joseph@codesourcery.com> wrote:
>> > On Tue, 15 Mar 2016, H.J. Lu wrote:
>> >
>> >> On Tue, Mar 15, 2016 at 2:39 PM, Joseph Myers <joseph@codesourcery.com> wrote:
>> >> > I'm not sure if the zero-size arrays (a GNU extension) are considered to
>> >> > make a struct non-empty, but in any case I think the tests should cover
>> >> > such arrays as elements of structs.
>> >>
>> >> There are couple tests for structs with members of array
>> >> of empty types.  testsuite/g++.dg/abi/empty14.h has
>> >
>> > My concern is the other way round - structs with elements such as
>> > "int a[0];", an array [0] of a nonempty type.  My reading of the subobject
>> > definition is that such an array should not cause the struct to be
>> > considered nonempty (it doesn't result in any int subobjects).
>>
>> This is a test for struct with zero-size array, which isn't treated
>> as empty type.  C++ and C are compatible in its passing.
>
> Where is the current definition of empty types you're proposing for use in
> GCC?  Is the behavior of this case clear from that definition?

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

Jason's patch follows it.  Here is a test for struct with zero-size
array of empty type, which is treated as empty type.


-- 
H.J.

[-- Attachment #2: 0001-Add-a-test-for-struct-with-zero-size-array-of-empty-.patch --]
[-- Type: text/x-patch, Size: 1816 bytes --]

From 222c8fcf6518b8689ead18516ce49ba71a1c0a49 Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@gmail.com>
Date: Tue, 15 Mar 2016 19:14:30 -0700
Subject: [PATCH] Add a test for struct with zero-size array of empty type

---
 gcc/testsuite/g++.dg/abi/empty19.C  | 17 +++++++++++++++++
 gcc/testsuite/g++.dg/abi/empty19.h  | 10 ++++++++++
 gcc/testsuite/g++.dg/abi/empty19a.c |  6 ++++++
 3 files changed, 33 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/abi/empty19.C
 create mode 100644 gcc/testsuite/g++.dg/abi/empty19.h
 create mode 100644 gcc/testsuite/g++.dg/abi/empty19a.c

diff --git a/gcc/testsuite/g++.dg/abi/empty19.C b/gcc/testsuite/g++.dg/abi/empty19.C
new file mode 100644
index 0000000..489eb3a
--- /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 "empty14a.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();
+}
-- 
2.5.0


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

* Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
  2016-03-16  0:25                                                                     ` Joseph Myers
  2016-03-16  2:17                                                                       ` H.J. Lu
@ 2016-03-16  2:51                                                                       ` Jason Merrill
  2016-03-16 11:55                                                                         ` H.J. Lu
  1 sibling, 1 reply; 52+ messages in thread
From: Jason Merrill @ 2016-03-16  2:51 UTC (permalink / raw)
  To: Joseph Myers, H.J. Lu
  Cc: Ulrich Weigand, GCC Patches, Jakub Jelinek, Richard Biener,
	Markus Trippelsdorf

On 03/15/2016 08:25 PM, Joseph Myers wrote:
> On Tue, 15 Mar 2016, H.J. Lu wrote:
>
>> On Tue, Mar 15, 2016 at 3:34 PM, Joseph Myers <joseph@codesourcery.com> wrote:
>>> On Tue, 15 Mar 2016, H.J. Lu wrote:
>>>
>>>> On Tue, Mar 15, 2016 at 2:39 PM, Joseph Myers <joseph@codesourcery.com> wrote:
>>>>> I'm not sure if the zero-size arrays (a GNU extension) are considered to
>>>>> make a struct non-empty, but in any case I think the tests should cover
>>>>> such arrays as elements of structs.
>>>>
>>>> There are couple tests for structs with members of array
>>>> of empty types.  testsuite/g++.dg/abi/empty14.h has
>>>
>>> My concern is the other way round - structs with elements such as
>>> "int a[0];", an array [0] of a nonempty type.  My reading of the subobject
>>> definition is that such an array should not cause the struct to be
>>> considered nonempty (it doesn't result in any int subobjects).
>>
>> This is a test for struct with zero-size array, which isn't treated
>> as empty type.  C++ and C are compatible in its passing.
>
> Where is the current definition of empty types you're proposing for use in
> GCC?  Is the behavior of this case clear from that definition?

"An empty type is a type where it and all of its subobjects 
(recursively) are of structure, union, or array type.  No memory slot 
nor register should be used to pass or return an object of empty type."

It seems to me that such a struct should be considered an empty type 
under this definition, since a zero-length array has no subobjects.

Jason

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

* Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
  2016-03-16  2:17                                                                       ` H.J. Lu
@ 2016-03-16  9:46                                                                         ` Bernhard Reutner-Fischer
  2016-03-16 11:53                                                                           ` H.J. Lu
  0 siblings, 1 reply; 52+ messages in thread
From: Bernhard Reutner-Fischer @ 2016-03-16  9:46 UTC (permalink / raw)
  To: H.J. Lu, Joseph Myers
  Cc: Jason Merrill, Ulrich Weigand, GCC Patches, Jakub Jelinek,
	Richard Biener, Markus Trippelsdorf

On March 16, 2016 3:17:20 AM GMT+01:00, "H.J. Lu" <hjl.tools@gmail.com> wrote:

>> Where is the current definition of empty types you're proposing for
>use in
>> GCC?  Is the behavior of this case clear from that definition?
>
>https://gcc.gnu.org/ml/gcc/2016-03/msg00071.html
>
>Jason's patch follows it.  Here is a test for struct with zero-size
>array of empty type, which is treated as empty type.

index 0000000..489eb3a
--- /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 "empty14a.c" }

14a ? Not 19a ?
Thanks


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

* Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
  2016-03-16  9:46                                                                         ` Bernhard Reutner-Fischer
@ 2016-03-16 11:53                                                                           ` H.J. Lu
  0 siblings, 0 replies; 52+ messages in thread
From: H.J. Lu @ 2016-03-16 11:53 UTC (permalink / raw)
  To: Bernhard Reutner-Fischer
  Cc: Joseph Myers, Jason Merrill, Ulrich Weigand, GCC Patches,
	Jakub Jelinek, Richard Biener, Markus Trippelsdorf

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

On Wed, Mar 16, 2016 at 2:45 AM, Bernhard Reutner-Fischer
<rep.dot.nop@gmail.com> wrote:
> On March 16, 2016 3:17:20 AM GMT+01:00, "H.J. Lu" <hjl.tools@gmail.com> wrote:
>
>>> Where is the current definition of empty types you're proposing for
>>use in
>>> GCC?  Is the behavior of this case clear from that definition?
>>
>>https://gcc.gnu.org/ml/gcc/2016-03/msg00071.html
>>
>>Jason's patch follows it.  Here is a test for struct with zero-size
>>array of empty type, which is treated as empty type.
>
> index 0000000..489eb3a
> --- /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 "empty14a.c" }
>
> 14a ? Not 19a ?
> Thanks
>
>

Here is the updated patch.

-- 
H.J.

[-- Attachment #2: 0001-Add-a-test-for-struct-with-zero-size-array-of-empty-.patch --]
[-- Type: text/x-patch, Size: 1816 bytes --]

From d7da4b56dddbd75da163b9fd3cc9ff4241be6ca9 Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@gmail.com>
Date: Tue, 15 Mar 2016 19:14:30 -0700
Subject: [PATCH] Add a test for struct with zero-size array of empty type

---
 gcc/testsuite/g++.dg/abi/empty19.C  | 17 +++++++++++++++++
 gcc/testsuite/g++.dg/abi/empty19.h  | 10 ++++++++++
 gcc/testsuite/g++.dg/abi/empty19a.c |  6 ++++++
 3 files changed, 33 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/abi/empty19.C
 create mode 100644 gcc/testsuite/g++.dg/abi/empty19.h
 create mode 100644 gcc/testsuite/g++.dg/abi/empty19a.c

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();
+}
-- 
2.5.0


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

* Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
  2016-03-16  2:51                                                                       ` Jason Merrill
@ 2016-03-16 11:55                                                                         ` H.J. Lu
  2016-03-16 14:33                                                                           ` Jason Merrill
  0 siblings, 1 reply; 52+ messages in thread
From: H.J. Lu @ 2016-03-16 11:55 UTC (permalink / raw)
  To: Jason Merrill
  Cc: Joseph Myers, Ulrich Weigand, GCC Patches, Jakub Jelinek,
	Richard Biener, Markus Trippelsdorf

On Tue, Mar 15, 2016 at 7:51 PM, Jason Merrill <jason@redhat.com> wrote:
> On 03/15/2016 08:25 PM, Joseph Myers wrote:
>>
>> On Tue, 15 Mar 2016, H.J. Lu wrote:
>>
>>> On Tue, Mar 15, 2016 at 3:34 PM, Joseph Myers <joseph@codesourcery.com>
>>> wrote:
>>>>
>>>> On Tue, 15 Mar 2016, H.J. Lu wrote:
>>>>
>>>>> On Tue, Mar 15, 2016 at 2:39 PM, Joseph Myers <joseph@codesourcery.com>
>>>>> wrote:
>>>>>>
>>>>>> I'm not sure if the zero-size arrays (a GNU extension) are considered
>>>>>> to
>>>>>> make a struct non-empty, but in any case I think the tests should
>>>>>> cover
>>>>>> such arrays as elements of structs.
>>>>>
>>>>>
>>>>> There are couple tests for structs with members of array
>>>>> of empty types.  testsuite/g++.dg/abi/empty14.h has
>>>>
>>>>
>>>> My concern is the other way round - structs with elements such as
>>>> "int a[0];", an array [0] of a nonempty type.  My reading of the
>>>> subobject
>>>> definition is that such an array should not cause the struct to be
>>>> considered nonempty (it doesn't result in any int subobjects).
>>>
>>>
>>> This is a test for struct with zero-size array, which isn't treated
>>> as empty type.  C++ and C are compatible in its passing.
>>
>>
>> Where is the current definition of empty types you're proposing for use in
>> GCC?  Is the behavior of this case clear from that definition?
>
>
> "An empty type is a type where it and all of its subobjects (recursively)
> are of structure, union, or array type.  No memory slot nor register should
> be used to pass or return an object of empty type."
>
> It seems to me that such a struct should be considered an empty type under
> this definition, since a zero-length array has no subobjects.
>

Since zero-size array is GCC extension, we can change it.   Do we
want to change its passing for C?

-- 
H.J.

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

* Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
  2016-03-15 19:32                                                               ` Jason Merrill
@ 2016-03-16 12:38                                                                 ` H.J. Lu
  2016-03-16 16:58                                                                   ` Jason Merrill
  0 siblings, 1 reply; 52+ messages in thread
From: H.J. Lu @ 2016-03-16 12:38 UTC (permalink / raw)
  To: Jason Merrill
  Cc: Ulrich Weigand, GCC Patches, Jakub Jelinek, Richard Biener,
	Markus Trippelsdorf

On Tue, Mar 15, 2016 at 12:32 PM, Jason Merrill <jason@redhat.com> wrote:
> On 03/15/2016 12:00 PM, H.J. Lu wrote:
>>
>> On Tue, Mar 15, 2016 at 8:35 AM, Jason Merrill <jason@redhat.com> wrote:
>>>
>>> I'm concerned about how this patch changes both target-independent code
>>> and
>>> target-specific code, with a passing remark that other targets might need
>>> to
>>> make similar changes.  I'm also concerned about the effect of this on
>>> other
>>> languages that might not want the same change.  So, here's an alternative
>>> patch that implements the change in the front end (and includes your
>>> testcases, thanks!).
>>>
>>> Thoughts?
>>
>>
>> On x86-64, I got
>>
>>
>> /export/gnu/import/git/sources/gcc/libstdc++-v3/src/c++11/cxx11-shim_facets.cc:273:23:
>> error: empty class ‘std::__facet_shims::other_abi {aka
>> std::integral_constant<bool, true>}’ parameter passing ABI changes in
>> -fabi-version=10 (GCC 6) [-Werror=abi]
>>      __collate_transform(other_abi{}, _M_get(), st, lo, hi);
>
>
> Right, need to remove the -Werror=abi bit from the patch until Jonathan
> updates libstdc++.
>
> Jason
>

I got

FAIL: g++.dg/abi/pr60336-1.C   scan-assembler jmp[\t ]+[^$]*?_Z3xxx9true_type
FAIL: g++.dg/abi/pr60336-5.C   scan-assembler jmp[\t ]+[^$]*?_Z3xxx9true_type
FAIL: g++.dg/abi/pr60336-6.C   scan-assembler jmp[\t ]+[^$]*?_Z3xxx9true_type
FAIL: g++.dg/abi/pr60336-7.C   scan-assembler jmp[\t ]+[^$]*?_Z3xxx9true_type
FAIL: g++.dg/abi/pr60336-9.C   scan-assembler jmp[\t ]+[^$]*?_Z3xxx9true_type
FAIL: g++.dg/abi/pr68355.C   scan-assembler jmp[\t
]+[^$]*?_Z3xxx17integral_constantIbLb1EE

They are expected since get_ref_base_and_extent needs to be
changed to set bitsize to 0 for empty types so that when
ref_maybe_used_by_call_p_1 calls get_ref_base_and_extent to
get 0 as the maximum size on empty type.  Otherwise, find_tail_calls
won't perform tail call optimization for functions with empty type
parameters.

-- 
H.J.

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

* Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
  2016-03-16 11:55                                                                         ` H.J. Lu
@ 2016-03-16 14:33                                                                           ` Jason Merrill
  2016-03-16 14:48                                                                             ` H.J. Lu
  0 siblings, 1 reply; 52+ messages in thread
From: Jason Merrill @ 2016-03-16 14:33 UTC (permalink / raw)
  To: H.J. Lu
  Cc: Joseph Myers, Ulrich Weigand, GCC Patches, Jakub Jelinek,
	Richard Biener, Markus Trippelsdorf

On 03/16/2016 07:55 AM, H.J. Lu wrote:
> On Tue, Mar 15, 2016 at 7:51 PM, Jason Merrill <jason@redhat.com> wrote:
>> On 03/15/2016 08:25 PM, Joseph Myers wrote:
>>>
>>> On Tue, 15 Mar 2016, H.J. Lu wrote:
>>>
>>>> On Tue, Mar 15, 2016 at 3:34 PM, Joseph Myers <joseph@codesourcery.com>
>>>> wrote:
>>>>>
>>>>> On Tue, 15 Mar 2016, H.J. Lu wrote:
>>>>>
>>>>>> On Tue, Mar 15, 2016 at 2:39 PM, Joseph Myers <joseph@codesourcery.com>
>>>>>> wrote:
>>>>>>>
>>>>>>> I'm not sure if the zero-size arrays (a GNU extension) are considered
>>>>>>> to
>>>>>>> make a struct non-empty, but in any case I think the tests should
>>>>>>> cover
>>>>>>> such arrays as elements of structs.
>>>>>>
>>>>>>
>>>>>> There are couple tests for structs with members of array
>>>>>> of empty types.  testsuite/g++.dg/abi/empty14.h has
>>>>>
>>>>>
>>>>> My concern is the other way round - structs with elements such as
>>>>> "int a[0];", an array [0] of a nonempty type.  My reading of the
>>>>> subobject
>>>>> definition is that such an array should not cause the struct to be
>>>>> considered nonempty (it doesn't result in any int subobjects).
>>>>
>>>>
>>>> This is a test for struct with zero-size array, which isn't treated
>>>> as empty type.  C++ and C are compatible in its passing.
>>>
>>>
>>> Where is the current definition of empty types you're proposing for use in
>>> GCC?  Is the behavior of this case clear from that definition?
>>
>>
>> "An empty type is a type where it and all of its subobjects (recursively)
>> are of structure, union, or array type.  No memory slot nor register should
>> be used to pass or return an object of empty type."
>>
>> It seems to me that such a struct should be considered an empty type under
>> this definition, since a zero-length array has no subobjects.
>
> Since zero-size array is GCC extension, we can change it.   Do we
> want to change its passing for C?

I would think so; it seems to follow clearly from this definition.  I 
have trouble imagining that anyone would ever pass an object containing 
a zero-length array by value, so it shouldn't matter much either way, 
but I consistency is good.

Jason

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

* Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
  2016-03-16 14:33                                                                           ` Jason Merrill
@ 2016-03-16 14:48                                                                             ` H.J. Lu
  0 siblings, 0 replies; 52+ messages in thread
From: H.J. Lu @ 2016-03-16 14:48 UTC (permalink / raw)
  To: Jason Merrill
  Cc: Joseph Myers, Ulrich Weigand, GCC Patches, Jakub Jelinek,
	Richard Biener, Markus Trippelsdorf

On Wed, Mar 16, 2016 at 7:33 AM, Jason Merrill <jason@redhat.com> wrote:
> On 03/16/2016 07:55 AM, H.J. Lu wrote:
>>
>> On Tue, Mar 15, 2016 at 7:51 PM, Jason Merrill <jason@redhat.com> wrote:
>>>
>>> On 03/15/2016 08:25 PM, Joseph Myers wrote:
>>>>
>>>>
>>>> On Tue, 15 Mar 2016, H.J. Lu wrote:
>>>>
>>>>> On Tue, Mar 15, 2016 at 3:34 PM, Joseph Myers <joseph@codesourcery.com>
>>>>> wrote:
>>>>>>
>>>>>>
>>>>>> On Tue, 15 Mar 2016, H.J. Lu wrote:
>>>>>>
>>>>>>> On Tue, Mar 15, 2016 at 2:39 PM, Joseph Myers
>>>>>>> <joseph@codesourcery.com>
>>>>>>> wrote:
>>>>>>>>
>>>>>>>>
>>>>>>>> I'm not sure if the zero-size arrays (a GNU extension) are
>>>>>>>> considered
>>>>>>>> to
>>>>>>>> make a struct non-empty, but in any case I think the tests should
>>>>>>>> cover
>>>>>>>> such arrays as elements of structs.
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> There are couple tests for structs with members of array
>>>>>>> of empty types.  testsuite/g++.dg/abi/empty14.h has
>>>>>>
>>>>>>
>>>>>>
>>>>>> My concern is the other way round - structs with elements such as
>>>>>> "int a[0];", an array [0] of a nonempty type.  My reading of the
>>>>>> subobject
>>>>>> definition is that such an array should not cause the struct to be
>>>>>> considered nonempty (it doesn't result in any int subobjects).
>>>>>
>>>>>
>>>>>
>>>>> This is a test for struct with zero-size array, which isn't treated
>>>>> as empty type.  C++ and C are compatible in its passing.
>>>>
>>>>
>>>>
>>>> Where is the current definition of empty types you're proposing for use
>>>> in
>>>> GCC?  Is the behavior of this case clear from that definition?
>>>
>>>
>>>
>>> "An empty type is a type where it and all of its subobjects (recursively)
>>> are of structure, union, or array type.  No memory slot nor register
>>> should
>>> be used to pass or return an object of empty type."
>>>
>>> It seems to me that such a struct should be considered an empty type
>>> under
>>> this definition, since a zero-length array has no subobjects.
>>
>>
>> Since zero-size array is GCC extension, we can change it.   Do we
>> want to change its passing for C?
>
>
> I would think so; it seems to follow clearly from this definition.  I have
> trouble imagining that anyone would ever pass an object containing a
> zero-length array by value, so it shouldn't matter much either way, but I
> consistency is good.
>

This requires change in both C and C++ frontends.

-- 
H.J.

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

* Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
  2016-03-16 12:38                                                                 ` H.J. Lu
@ 2016-03-16 16:58                                                                   ` Jason Merrill
  2016-03-16 17:02                                                                     ` H.J. Lu
  0 siblings, 1 reply; 52+ messages in thread
From: Jason Merrill @ 2016-03-16 16:58 UTC (permalink / raw)
  To: H.J. Lu
  Cc: Ulrich Weigand, GCC Patches, Jakub Jelinek, Richard Biener,
	Markus Trippelsdorf

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

On 03/16/2016 08:38 AM, H.J. Lu wrote:
> FAIL: g++.dg/abi/pr60336-1.C   scan-assembler jmp[\t ]+[^$]*?_Z3xxx9true_type
> FAIL: g++.dg/abi/pr60336-5.C   scan-assembler jmp[\t ]+[^$]*?_Z3xxx9true_type
> FAIL: g++.dg/abi/pr60336-6.C   scan-assembler jmp[\t ]+[^$]*?_Z3xxx9true_type
> FAIL: g++.dg/abi/pr60336-7.C   scan-assembler jmp[\t ]+[^$]*?_Z3xxx9true_type
> FAIL: g++.dg/abi/pr60336-9.C   scan-assembler jmp[\t ]+[^$]*?_Z3xxx9true_type
> FAIL: g++.dg/abi/pr68355.C   scan-assembler jmp[\t
> ]+[^$]*?_Z3xxx17integral_constantIbLb1EE

These pass for me on x86_64, but I do see calls with -m32.

> They are expected since get_ref_base_and_extent needs to be
> changed to set bitsize to 0 for empty types so that when
> ref_maybe_used_by_call_p_1 calls get_ref_base_and_extent to
> get 0 as the maximum size on empty type.  Otherwise, find_tail_calls
> won't perform tail call optimization for functions with empty type
> parameters.

That isn't why the optimization isn't happening in pr68355 with -m32; 
the .optimized dump has

   xxx (D.2289); [tail call]

Rather, the failure seems to happen in load_register_parameter, at

>               /* Check for overlap with already clobbered argument area,
>                  providing that this has non-zero size.  */
>               if (is_sibcall
>                   && (size == 0
>                       || mem_overlaps_already_clobbered_arg_p
>                                            (XEXP (args[i].value, 0), size)))
>                 *sibcall_failure = 1;

The code seems to contradict the comment, and seems to have been broken 
by r162402.  Applying this additional patch fixes those tests.


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

commit b9e170023d97cef94f9b88ded1dfd3b4cf993294
Author: Jason Merrill <jason@redhat.com>
Date:   Wed Mar 16 12:57:37 2016 -0400

    	* calls.c (load_register_parameters): Fix zero size sibcall logic.

diff --git a/gcc/calls.c b/gcc/calls.c
index 7b28f43..6415e08 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -2083,9 +2083,9 @@ load_register_parameters (struct arg_data *args, int num_actuals,
 	      /* Check for overlap with already clobbered argument area,
 	         providing that this has non-zero size.  */
 	      if (is_sibcall
-		  && (size == 0
-		      || mem_overlaps_already_clobbered_arg_p 
-					   (XEXP (args[i].value, 0), size)))
+		  && size != 0
+		  && (mem_overlaps_already_clobbered_arg_p
+		      (XEXP (args[i].value, 0), size)))
 		*sibcall_failure = 1;
 
 	      if (size % UNITS_PER_WORD == 0

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

* Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
  2016-03-16 16:58                                                                   ` Jason Merrill
@ 2016-03-16 17:02                                                                     ` H.J. Lu
  2016-03-16 19:39                                                                       ` H.J. Lu
  0 siblings, 1 reply; 52+ messages in thread
From: H.J. Lu @ 2016-03-16 17:02 UTC (permalink / raw)
  To: Jason Merrill
  Cc: Ulrich Weigand, GCC Patches, Jakub Jelinek, Richard Biener,
	Markus Trippelsdorf

On Wed, Mar 16, 2016 at 9:58 AM, Jason Merrill <jason@redhat.com> wrote:
> On 03/16/2016 08:38 AM, H.J. Lu wrote:
>>
>> FAIL: g++.dg/abi/pr60336-1.C   scan-assembler jmp[\t
>> ]+[^$]*?_Z3xxx9true_type
>> FAIL: g++.dg/abi/pr60336-5.C   scan-assembler jmp[\t
>> ]+[^$]*?_Z3xxx9true_type
>> FAIL: g++.dg/abi/pr60336-6.C   scan-assembler jmp[\t
>> ]+[^$]*?_Z3xxx9true_type
>> FAIL: g++.dg/abi/pr60336-7.C   scan-assembler jmp[\t
>> ]+[^$]*?_Z3xxx9true_type
>> FAIL: g++.dg/abi/pr60336-9.C   scan-assembler jmp[\t
>> ]+[^$]*?_Z3xxx9true_type
>> FAIL: g++.dg/abi/pr68355.C   scan-assembler jmp[\t
>> ]+[^$]*?_Z3xxx17integral_constantIbLb1EE
>
>
> These pass for me on x86_64, but I do see calls with -m32.
>
>> They are expected since get_ref_base_and_extent needs to be
>> changed to set bitsize to 0 for empty types so that when
>> ref_maybe_used_by_call_p_1 calls get_ref_base_and_extent to
>> get 0 as the maximum size on empty type.  Otherwise, find_tail_calls
>> won't perform tail call optimization for functions with empty type
>> parameters.
>
>
> That isn't why the optimization isn't happening in pr68355 with -m32; the
> .optimized dump has
>
>   xxx (D.2289); [tail call]
>
> Rather, the failure seems to happen in load_register_parameter, at
>
>>               /* Check for overlap with already clobbered argument area,
>>                  providing that this has non-zero size.  */
>>               if (is_sibcall
>>                   && (size == 0
>>                       || mem_overlaps_already_clobbered_arg_p
>>                                            (XEXP (args[i].value, 0),
>> size)))
>>                 *sibcall_failure = 1;
>
>
> The code seems to contradict the comment, and seems to have been broken by
> r162402.  Applying this additional patch fixes those tests.
>

I am running the full test now.

-- 
H.J.

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

* Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
  2016-03-16 17:02                                                                     ` H.J. Lu
@ 2016-03-16 19:39                                                                       ` H.J. Lu
  2016-03-16 19:43                                                                         ` Jason Merrill
  0 siblings, 1 reply; 52+ messages in thread
From: H.J. Lu @ 2016-03-16 19:39 UTC (permalink / raw)
  To: Jason Merrill
  Cc: Ulrich Weigand, GCC Patches, Jakub Jelinek, Richard Biener,
	Markus Trippelsdorf

On Wed, Mar 16, 2016 at 10:02 AM, H.J. Lu <hjl.tools@gmail.com> wrote:
> On Wed, Mar 16, 2016 at 9:58 AM, Jason Merrill <jason@redhat.com> wrote:
>> On 03/16/2016 08:38 AM, H.J. Lu wrote:
>>>
>>> FAIL: g++.dg/abi/pr60336-1.C   scan-assembler jmp[\t
>>> ]+[^$]*?_Z3xxx9true_type
>>> FAIL: g++.dg/abi/pr60336-5.C   scan-assembler jmp[\t
>>> ]+[^$]*?_Z3xxx9true_type
>>> FAIL: g++.dg/abi/pr60336-6.C   scan-assembler jmp[\t
>>> ]+[^$]*?_Z3xxx9true_type
>>> FAIL: g++.dg/abi/pr60336-7.C   scan-assembler jmp[\t
>>> ]+[^$]*?_Z3xxx9true_type
>>> FAIL: g++.dg/abi/pr60336-9.C   scan-assembler jmp[\t
>>> ]+[^$]*?_Z3xxx9true_type
>>> FAIL: g++.dg/abi/pr68355.C   scan-assembler jmp[\t
>>> ]+[^$]*?_Z3xxx17integral_constantIbLb1EE
>>
>>
>> These pass for me on x86_64, but I do see calls with -m32.
>>
>>> They are expected since get_ref_base_and_extent needs to be
>>> changed to set bitsize to 0 for empty types so that when
>>> ref_maybe_used_by_call_p_1 calls get_ref_base_and_extent to
>>> get 0 as the maximum size on empty type.  Otherwise, find_tail_calls
>>> won't perform tail call optimization for functions with empty type
>>> parameters.
>>
>>
>> That isn't why the optimization isn't happening in pr68355 with -m32; the
>> .optimized dump has
>>
>>   xxx (D.2289); [tail call]
>>
>> Rather, the failure seems to happen in load_register_parameter, at
>>
>>>               /* Check for overlap with already clobbered argument area,
>>>                  providing that this has non-zero size.  */
>>>               if (is_sibcall
>>>                   && (size == 0
>>>                       || mem_overlaps_already_clobbered_arg_p
>>>                                            (XEXP (args[i].value, 0),
>>> size)))
>>>                 *sibcall_failure = 1;
>>
>>
>> The code seems to contradict the comment, and seems to have been broken by
>> r162402.  Applying this additional patch fixes those tests.
>>
>
> I am running the full test now.

On x86-64, I got

export/gnu/import/git/sources/gcc/gcc/testsuite/gcc.dg/ubsan/object-size-9.c:11:13:
runtime error: load of address 0x000000600ffa with insufficient space
for an object of type 'char'
0x000000600ffa: note: pointer points here
<memory cannot be printed>
PASS: gcc.dg/ubsan/object-size-9.c   -O2  execution test
FAIL: gcc.dg/ubsan/object-size-9.c   -O2  output pattern test
Output was:


-- 
H.J.

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

* Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class
  2016-03-16 19:39                                                                       ` H.J. Lu
@ 2016-03-16 19:43                                                                         ` Jason Merrill
  0 siblings, 0 replies; 52+ messages in thread
From: Jason Merrill @ 2016-03-16 19:43 UTC (permalink / raw)
  To: H.J. Lu
  Cc: Ulrich Weigand, GCC Patches, Jakub Jelinek, Richard Biener,
	Markus Trippelsdorf

On 03/16/2016 03:39 PM, H.J. Lu wrote:
> On Wed, Mar 16, 2016 at 10:02 AM, H.J. Lu <hjl.tools@gmail.com> wrote:
>> On Wed, Mar 16, 2016 at 9:58 AM, Jason Merrill <jason@redhat.com> wrote:
>>> On 03/16/2016 08:38 AM, H.J. Lu wrote:
>>>>
>>>> FAIL: g++.dg/abi/pr60336-1.C   scan-assembler jmp[\t
>>>> ]+[^$]*?_Z3xxx9true_type
>>>> FAIL: g++.dg/abi/pr60336-5.C   scan-assembler jmp[\t
>>>> ]+[^$]*?_Z3xxx9true_type
>>>> FAIL: g++.dg/abi/pr60336-6.C   scan-assembler jmp[\t
>>>> ]+[^$]*?_Z3xxx9true_type
>>>> FAIL: g++.dg/abi/pr60336-7.C   scan-assembler jmp[\t
>>>> ]+[^$]*?_Z3xxx9true_type
>>>> FAIL: g++.dg/abi/pr60336-9.C   scan-assembler jmp[\t
>>>> ]+[^$]*?_Z3xxx9true_type
>>>> FAIL: g++.dg/abi/pr68355.C   scan-assembler jmp[\t
>>>> ]+[^$]*?_Z3xxx17integral_constantIbLb1EE
>>>
>>>
>>> These pass for me on x86_64, but I do see calls with -m32.
>>>
>>>> They are expected since get_ref_base_and_extent needs to be
>>>> changed to set bitsize to 0 for empty types so that when
>>>> ref_maybe_used_by_call_p_1 calls get_ref_base_and_extent to
>>>> get 0 as the maximum size on empty type.  Otherwise, find_tail_calls
>>>> won't perform tail call optimization for functions with empty type
>>>> parameters.
>>>
>>>
>>> That isn't why the optimization isn't happening in pr68355 with -m32; the
>>> .optimized dump has
>>>
>>>    xxx (D.2289); [tail call]
>>>
>>> Rather, the failure seems to happen in load_register_parameter, at
>>>
>>>>                /* Check for overlap with already clobbered argument area,
>>>>                   providing that this has non-zero size.  */
>>>>                if (is_sibcall
>>>>                    && (size == 0
>>>>                        || mem_overlaps_already_clobbered_arg_p
>>>>                                             (XEXP (args[i].value, 0),
>>>> size)))
>>>>                  *sibcall_failure = 1;
>>>
>>>
>>> The code seems to contradict the comment, and seems to have been broken by
>>> r162402.  Applying this additional patch fixes those tests.
>>>
>>
>> I am running the full test now.
>
> On x86-64, I got
>
> export/gnu/import/git/sources/gcc/gcc/testsuite/gcc.dg/ubsan/object-size-9.c:11:13:
> runtime error: load of address 0x000000600ffa with insufficient space
> for an object of type 'char'
> 0x000000600ffa: note: pointer points here
> <memory cannot be printed>
> PASS: gcc.dg/ubsan/object-size-9.c   -O2  execution test
> FAIL: gcc.dg/ubsan/object-size-9.c   -O2  output pattern test
> Output was:

That looks like a dejagnu glitch; the output you quote seems to match 
the expected output from the test.

Jason

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

end of thread, other threads:[~2016-03-16 19:43 UTC | newest]

Thread overview: 52+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-12-08 16:22 PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class H.J. Lu
2015-12-09 14:05 ` Richard Biener
2015-12-09 18:53   ` H.J. Lu
2015-12-09 21:14     ` H.J. Lu
2015-12-09 21:31     ` Markus Trippelsdorf
2015-12-10 11:24       ` Richard Biener
2015-12-11 23:52         ` H.J. Lu
2015-12-12 14:51           ` Jason Merrill
2015-12-12 15:27             ` Jakub Jelinek
2015-12-12 16:45               ` H.J. Lu
2015-12-12 18:43               ` Marc Glisse
2015-12-14 20:16                 ` Jason Merrill
2015-12-14 20:39                   ` H.J. Lu
2015-12-14 20:44                     ` Jason Merrill
2015-12-14 22:08                       ` H.J. Lu
2016-01-26 19:27                         ` Jason Merrill
2016-01-26 19:52                           ` H.J. Lu
2016-01-26 20:23                             ` Marc Glisse
2016-01-26 20:26                               ` H.J. Lu
2016-01-26 20:44                                 ` Marc Glisse
2016-01-26 21:21                                   ` H.J. Lu
2016-01-26 21:40                                     ` Jakub Jelinek
2016-01-26 22:21                                       ` H.J. Lu
2016-01-27  8:10                                         ` Marc Glisse
2016-01-27  8:21                                           ` Jakub Jelinek
2016-01-27  9:03                                             ` Marc Glisse
2016-01-27 13:46                                               ` H.J. Lu
2016-01-27 15:39                                                 ` H.J. Lu
2016-03-01  1:02                                                   ` Jason Merrill
2016-03-01 22:44                                                     ` H.J. Lu
2016-03-02 16:25                                                       ` Ulrich Weigand
2016-03-02 17:34                                                         ` H.J. Lu
2016-03-15 15:35                                                           ` Jason Merrill
2016-03-15 16:00                                                             ` H.J. Lu
2016-03-15 19:32                                                               ` Jason Merrill
2016-03-16 12:38                                                                 ` H.J. Lu
2016-03-16 16:58                                                                   ` Jason Merrill
2016-03-16 17:02                                                                     ` H.J. Lu
2016-03-16 19:39                                                                       ` H.J. Lu
2016-03-16 19:43                                                                         ` Jason Merrill
2016-03-15 21:40                                                             ` Joseph Myers
2016-03-15 22:31                                                               ` H.J. Lu
2016-03-15 22:35                                                                 ` Joseph Myers
2016-03-16  0:23                                                                   ` H.J. Lu
2016-03-16  0:25                                                                     ` Joseph Myers
2016-03-16  2:17                                                                       ` H.J. Lu
2016-03-16  9:46                                                                         ` Bernhard Reutner-Fischer
2016-03-16 11:53                                                                           ` H.J. Lu
2016-03-16  2:51                                                                       ` Jason Merrill
2016-03-16 11:55                                                                         ` H.J. Lu
2016-03-16 14:33                                                                           ` Jason Merrill
2016-03-16 14:48                                                                             ` H.J. Lu

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).