This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: PING^1: [PATCH] Add TYPE_EMPTY_RECORD for C++ empty class


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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]