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: Adjust empty class parameter passing ABI (PR c++/60336)


On Thu, Nov 02, 2017 at 02:50:17PM -0400, Jason Merrill wrote:
> We probably want to call them something like default_is_empty_type and
> default_is_empty_record, though.

Done.

> > For one thing I thought we should be consistent in treating these two
> > structs, regarding being empty:
> >
> > struct S { struct { } a; int a[0]; };
> > struct T { struct { } a; int a[]; };
> >
> > Without the (ugly, I know) special handling in is_empty_type, only T would be
> > considered empty.
> 
> Why would T be considered empty?  I don't see anything in
> is_empty_type that would cause that.
 
Sorry, I meant *non*-empty :(.  I've changed the code so that both S and T
are considered empty, because...

> > And that would mean that the g++.dg/abi/empty23.C test
> > crashes (the abort triggers).  The problem seems to be that when we're passing
> > a pointer to an empty struct, it got turned into a null pointer.
> 
> That seems like a bug that needs fixing regardless of whether we end
> up considering S and T empty.

...I fixed this.  The problem was that ix86_function_arg_advance tried to
skip parameters with empty types:

+  /* Skip empty records because they won't be passed.  */
+  if (type && targetm.calls.empty_record_p (type))
+    return;

but I think that's wrong.  Thus the testcase passes now.

> > And yeah, when passing such a struct by value, the flexible array member is
> > ignored.  The ABI of passing struct with a flexible array member has changed in
> > GCC 4.4.
> 
> Can we add a warning about such pass/return (as a separate patch)?

Oh, the warning is already there.

> > +  if (TREE_ADDRESSABLE (type))
> > +    return false;
> 
> I think we want to abort in this case, as the front end should have
> turned this into an invisible reference already.

Done.  Thanks,

Bootstrap/regtest running on x86_64-linux and ppc64-linux.

2017-11-03  Marek Polacek  <polacek@redhat.com>
	    H.J. Lu  <hongjiu.lu@intel.com>
	    Jason Merrill  <jason@redhat.com>

	PR c++/60336
	PR middle-end/67239
	PR target/68355
	* class.c (layout_class_type): Set TYPE_EMPTY_P and TYPE_WARN_EMPTY_P.

	* lto.c (compare_tree_sccs_1): Compare TYPE_WARN_EMPTY_P and
	TYPE_EMPTY_P.

	* calls.c (initialize_argument_information): Call
	warn_parameter_passing_abi target hook.
	(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 types.
	* common.opt: Update -fabi-version description.
	* config/i386/i386.c (init_cumulative_args): Set cum->warn_empty.
	(ix86_return_in_memory): Return false for empty types.
	(ix86_gimplify_va_arg): Call arg_int_size_in_bytes instead of
	int_size_in_bytes.
	(ix86_is_empty_record): New function.
	(ix86_warn_parameter_passing_abi): New function.
	(TARGET_EMPTY_RECORD_P): Redefine.
	(TARGET_WARN_PARAMETER_PASSING_ABI): Redefine.
	* config/i386/i386.h (CUMULATIVE_ARGS): Add warn_empty.
	* doc/tm.texi: Regenerated.
	* doc/tm.texi.in (TARGET_EMPTY_RECORD_P,
	TARGET_WARN_PARAMETER_PASSING_ABI): Add.
	* explow.c (hard_function_value): Call arg_int_size_in_bytes
	instead of int_size_in_bytes.
	* expr.c (copy_blkmode_to_reg): Likewise.
	* function.c (assign_parm_find_entry_rtl): Call
	warn_parameter_passing_abi target hook.
	(locate_and_pad_parm): Call arg size_in_bytes instead
	size_in_bytes.
	* lto-streamer-out.c (hash_tree): Hash TYPE_EMPTY_P and
	TYPE_WARN_EMPTY_P.
	* target.def (empty_record_p, warn_parameter_passing_abi): New target
	hook.
	* targhooks.c (hook_void_CUMULATIVE_ARGS_tree): New hook.
	(std_gimplify_va_arg_expr): Skip empty records.  Call
	arg_size_in_bytes instead size_in_bytes.
	* targhooks.h (hook_void_CUMULATIVE_ARGS_tree): Declare.
	* tree-core.h (tree_type_common): Update comment.  Add artificial_flag
	and empty_flag.
	* tree-streamer-in.c (unpack_ts_base_value_fields): Stream
	TYPE_WARN_EMPTY_P instead of TYPE_ARTIFICIAL.
	(unpack_ts_type_common_value_fields): Stream TYPE_EMPTY_P and
	TYPE_ARTIFICIAL.
	* tree-streamer-out.c (pack_ts_base_value_fields): Stream
	TYPE_WARN_EMPTY_P instead of TYPE_ARTIFICIAL.
	(pack_ts_type_common_value_fields): Stream TYPE_EMPTY_P and
	TYPE_ARTIFICIAL.
	* tree.c (is_empty_type): New function.
	(default_is_empty_record): New function.
	(arg_int_size_in_bytes): New function.
	(arg_size_in_bytes): New function.
	* tree.h: Define TYPE_EMPTY_P and TYPE_WARN_EMPTY_P.  Map
	TYPE_ARTIFICIAL to type_common.artificial_flag.
	(default_is_empty_record, arg_int_size_in_bytes,
	arg_size_in_bytes): Declare.

	* g++.dg/abi/empty12.C: New test.
	* g++.dg/abi/empty12.h: New test.
	* g++.dg/abi/empty12a.c: New test.
	* g++.dg/abi/empty13.C: New test.
	* g++.dg/abi/empty13.h: New test.
	* g++.dg/abi/empty13a.c: New test.
	* g++.dg/abi/empty14.C: New test.
	* g++.dg/abi/empty14.h: New test.
	* g++.dg/abi/empty14a.c: New test.
	* g++.dg/abi/empty15.C: New test.
	* g++.dg/abi/empty15.h: New test.
	* g++.dg/abi/empty15a.c: New test.
	* g++.dg/abi/empty16.C: New test.
	* g++.dg/abi/empty16.h: New test.
	* g++.dg/abi/empty16a.c: New test.
	* g++.dg/abi/empty17.C: New test.
	* g++.dg/abi/empty17.h: New test.
	* g++.dg/abi/empty17a.c: New test.
	* g++.dg/abi/empty18.C: New test.
	* g++.dg/abi/empty18.h: New test.
	* g++.dg/abi/empty18a.c: New test.
	* g++.dg/abi/empty19.C: New test.
	* g++.dg/abi/empty19.h: New test.
	* g++.dg/abi/empty19a.c: New test.
	* g++.dg/abi/empty20.C: New test.
	* g++.dg/abi/empty21.C: New test.
	* g++.dg/abi/empty22.C: New test.
	* g++.dg/abi/empty22.h: New test.
	* g++.dg/abi/empty22a.c: New test.
	* g++.dg/abi/empty23.C: New test.
	* g++.dg/abi/empty24.C: New test.
	* g++.dg/abi/pr60336-1.C: New test.
	* g++.dg/abi/pr60336-10.C: New test.
	* g++.dg/abi/pr60336-11.C: New test.
	* g++.dg/abi/pr60336-12.C: New test.
	* g++.dg/abi/pr60336-2.C: New test.
	* g++.dg/abi/pr60336-3.C: New test.
	* g++.dg/abi/pr60336-4.C: New test.
	* g++.dg/abi/pr60336-5.C: New test.
	* g++.dg/abi/pr60336-6.C: New test.
	* g++.dg/abi/pr60336-7.C: New test.
	* g++.dg/abi/pr60336-8.C: New test.
	* g++.dg/abi/pr60336-9.C: New test.
	* g++.dg/abi/pr68355.C: New test.
	* g++.dg/lto/pr60336_0.C: New test.

diff --git gcc/calls.c gcc/calls.c
index 3730f43c7a9..f9a6a5cce13 100644
--- gcc/calls.c
+++ gcc/calls.c
@@ -1850,6 +1850,8 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
       args[i].unsignedp = unsignedp;
       args[i].mode = mode;
 
+      targetm.calls.warn_parameter_passing_abi (args_so_far, type);
+
       args[i].reg = targetm.calls.function_arg (args_so_far, mode, type,
 						argpos < n_named_args);
 
@@ -5358,7 +5360,11 @@ 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_EMPTY_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.  */
@@ -5390,10 +5396,12 @@ 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
@@ -5426,9 +5434,9 @@ 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.  */
 	  excess = (arg->locate.size.constant
-		    - int_size_in_bytes (TREE_TYPE (pval))
+		    - arg_int_size_in_bytes (TREE_TYPE (pval))
 		    + partial);
-	  size_rtx = expand_expr (size_in_bytes (TREE_TYPE (pval)),
+	  size_rtx = expand_expr (arg_size_in_bytes (TREE_TYPE (pval)),
 				  NULL_RTX, TYPE_MODE (sizetype),
 				  EXPAND_NORMAL);
 	}
@@ -5504,10 +5512,12 @@ 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.
@@ -5585,6 +5595,9 @@ must_pass_in_stack_var_size_or_pad (machine_mode mode, const_tree type)
   if (TREE_ADDRESSABLE (type))
     return true;
 
+  if (TYPE_EMPTY_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 gcc/common.opt gcc/common.opt
index f8f2ed3db8a..28a0185f0cf 100644
--- gcc/common.opt
+++ gcc/common.opt
@@ -936,7 +936,7 @@ Driver Undocumented
 ;     Default in G++ 7.
 ;
 ; 12: Corrects the calling convention for classes with only deleted copy/move
-;     constructors.
+;     constructors and changes passing/returning of empty records.
 ;     Default in G++ 8.
 ;
 ; Additional positive integers will be assigned as new versions of
diff --git gcc/config/i386/i386.c gcc/config/i386/i386.c
index 29678722226..46b76aeefdd 100644
--- gcc/config/i386/i386.c
+++ gcc/config/i386/i386.c
@@ -7186,6 +7186,26 @@ init_cumulative_args (CUMULATIVE_ARGS *cum,  /* Argument info to initialize */
   cum->force_bnd_pass = 0;
   cum->decl = fndecl;
 
+  cum->warn_empty = !warn_abi || cum->stdarg;
+  if (!cum->warn_empty && fntype)
+    {
+      function_args_iterator iter;
+      tree argtype;
+      bool seen_empty_type = false;
+      FOREACH_FUNCTION_ARGS (fntype, argtype, iter)
+	{
+	  if (VOID_TYPE_P (argtype))
+	    break;
+	  if (TYPE_EMPTY_P (argtype))
+	    seen_empty_type = true;
+	  else if (seen_empty_type)
+	    {
+	      cum->warn_empty = true;
+	      break;
+	    }
+	}
+    }
+
   if (!TARGET_64BIT)
     {
       /* If there are variable arguments, then we won't pass anything
@@ -9293,6 +9313,10 @@ ix86_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED)
   if (POINTER_BOUNDS_TYPE_P (type))
     return false;
 
+  /* Empty records are never passed in memory.  */
+  if (type && TYPE_EMPTY_P (type))
+    return false;
+
   if (TARGET_64BIT)
     {
       if (ix86_function_type_abi (fntype) == MS_ABI)
@@ -9873,7 +9897,7 @@ 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);
+  size = arg_int_size_in_bytes (type);
   rsize = CEIL (size, UNITS_PER_WORD);
 
   nat_mode = type_natural_mode (type, NULL, false);
@@ -28753,6 +28777,44 @@ ix86_constant_alignment (const_tree exp, HOST_WIDE_INT align)
   return align;
 }
 
+/* Implement TARGET_EMPTY_RECORD_P.  */
+
+static bool
+ix86_is_empty_record (const_tree type)
+{
+  if (!TARGET_64BIT)
+    return false;
+  return default_is_empty_record (type);
+}
+
+/* Implement TARGET_WARN_PARAMETER_PASSING_ABI.  */
+
+static void
+ix86_warn_parameter_passing_abi (cumulative_args_t cum_v, tree type)
+{
+  CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v);
+
+  if (!cum->warn_empty)
+    return;
+
+  if (!TYPE_EMPTY_P (type))
+    return;
+
+  if (!TYPE_WARN_EMPTY_P (type))
+    return;
+
+  /* If the actual size of the type is zero, then there is no change
+     in how objects of this size are passed.  */
+  if (int_size_in_bytes (type) == 0)
+    return;
+
+  warning (OPT_Wabi, "empty class %qT parameter passing ABI "
+	   "changes in -fabi-version=12 (GCC 8)", type);
+
+  /* Only warn once.  */
+  cum->warn_empty = false;
+}
+
 /* Compute the alignment for a variable for Intel MCU psABI.  TYPE is
    the data type, and ALIGN is the alignment that the object would
    ordinarily have.  */
@@ -50300,6 +50362,12 @@ ix86_run_selftests (void)
 #undef TARGET_CONSTANT_ALIGNMENT
 #define TARGET_CONSTANT_ALIGNMENT ix86_constant_alignment
 
+#undef TARGET_EMPTY_RECORD_P
+#define TARGET_EMPTY_RECORD_P ix86_is_empty_record
+
+#undef TARGET_WARN_PARAMETER_PASSING_ABI
+#define TARGET_WARN_PARAMETER_PASSING_ABI ix86_warn_parameter_passing_abi
+
 #if CHECKING_P
 #undef TARGET_RUN_TARGET_SELFTESTS
 #define TARGET_RUN_TARGET_SELFTESTS selftest::ix86_run_selftests
diff --git gcc/config/i386/i386.h gcc/config/i386/i386.h
index 837906b5169..7f5d245b568 100644
--- gcc/config/i386/i386.h
+++ gcc/config/i386/i386.h
@@ -1633,6 +1633,8 @@ typedef struct ix86_args {
   int warn_avx;			/* True when we want to warn about AVX ABI.  */
   int warn_sse;			/* True when we want to warn about SSE ABI.  */
   int warn_mmx;			/* True when we want to warn about MMX ABI.  */
+  int warn_empty;		/* True when we want to warn about empty classes
+				   passing ABI change.  */
   int sse_regno;		/* next available sse register number */
   int mmx_words;		/* # mmx words passed so far */
   int mmx_nregs;		/* # mmx registers available for passing */
diff --git gcc/cp/class.c gcc/cp/class.c
index 98e62c6ad45..1d5ce7bc98b 100644
--- gcc/cp/class.c
+++ gcc/cp/class.c
@@ -6327,6 +6327,10 @@ layout_class_type (tree t, tree *virtuals_p)
       && tree_int_cst_lt (sizeof_biggest_empty_class,
 			  TYPE_SIZE_UNIT (t)))
     sizeof_biggest_empty_class = TYPE_SIZE_UNIT (t);
+
+  /* Handle empty records as per the x86-64 psABI.  */
+  TYPE_EMPTY_P (t) = targetm.calls.empty_record_p (t);
+  TYPE_WARN_EMPTY_P (t) = warn_abi && abi_version_crosses (12);
 }
 
 /* Determine the "key method" for the class type indicated by TYPE,
diff --git gcc/doc/tm.texi gcc/doc/tm.texi
index 72606f53f1c..f16e73c31b1 100644
--- gcc/doc/tm.texi
+++ gcc/doc/tm.texi
@@ -4559,6 +4559,16 @@ This target hook returns the mode to be used when accessing raw return registers
 This target hook returns the mode to be used when accessing raw argument registers in @code{__builtin_apply_args}.  Define this macro if the value in @var{reg_raw_mode} is not correct.
 @end deftypefn
 
+@deftypefn {Target Hook} bool TARGET_EMPTY_RECORD_P (const_tree @var{type})
+This target hook returns true if the type is an empty record.  The default
+is to return @code{false}.
+@end deftypefn
+
+@deftypefn {Target Hook} void TARGET_WARN_PARAMETER_PASSING_ABI (cumulative_args_t @var{ca}, tree @var{type})
+This target hook warns about the change in empty class parameter passing
+ABI.
+@end deftypefn
+
 @node Caller Saves
 @subsection Caller-Saves Register Allocation
 
diff --git gcc/doc/tm.texi.in gcc/doc/tm.texi.in
index e7d4ada290f..39f6fcaaa11 100644
--- gcc/doc/tm.texi.in
+++ gcc/doc/tm.texi.in
@@ -3439,6 +3439,10 @@ nothing when you use @option{-freg-struct-return} mode.
 
 @hook TARGET_GET_RAW_ARG_MODE
 
+@hook TARGET_EMPTY_RECORD_P
+
+@hook TARGET_WARN_PARAMETER_PASSING_ABI
+
 @node Caller Saves
 @subsection Caller-Saves Register Allocation
 
diff --git gcc/explow.c gcc/explow.c
index 662865d2808..72c2e01a44e 100644
--- gcc/explow.c
+++ gcc/explow.c
@@ -2166,7 +2166,7 @@ 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 = arg_int_size_in_bytes (valtype);
       opt_scalar_int_mode tmpmode;
 
       /* int_size_in_bytes can return -1.  We don't need a check here
diff --git gcc/expr.c gcc/expr.c
index 649a057e43d..60971066c7a 100644
--- gcc/expr.c
+++ gcc/expr.c
@@ -2749,7 +2749,7 @@ copy_blkmode_to_reg (machine_mode mode_in, tree src)
 
   x = expand_normal (src);
 
-  bytes = int_size_in_bytes (TREE_TYPE (src));
+  bytes = arg_int_size_in_bytes (TREE_TYPE (src));
   if (bytes == 0)
     return NULL_RTX;
 
diff --git gcc/function.c gcc/function.c
index fe3d9c1bbf3..671a49fa76f 100644
--- gcc/function.c
+++ gcc/function.c
@@ -2528,6 +2528,9 @@ assign_parm_find_entry_rtl (struct assign_parm_data_all *all,
       return;
     }
 
+  targetm.calls.warn_parameter_passing_abi (all->args_so_far,
+					    data->passed_type);
+
   entry_parm = targetm.calls.function_incoming_arg (all->args_so_far,
 						    data->promoted_mode,
 						    data->passed_type,
@@ -4140,8 +4143,9 @@ 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));
+  sizetree = (type
+	      ? arg_size_in_bytes (type)
+	      : size_int (GET_MODE_SIZE (passed_mode)));
   where_pad = targetm.calls.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,
diff --git gcc/lto-streamer-out.c gcc/lto-streamer-out.c
index 554f9cc9f01..83ade46ab07 100644
--- gcc/lto-streamer-out.c
+++ gcc/lto-streamer-out.c
@@ -1008,7 +1008,7 @@ hash_tree (struct streamer_tree_cache_d *cache, hash_map<tree, hashval_t> *map,
   else if (TYPE_P (t))
     hstate.add_flag (TYPE_UNSIGNED (t));
   if (TYPE_P (t))
-    hstate.add_flag (TYPE_ARTIFICIAL (t));
+    hstate.add_flag (TYPE_WARN_EMPTY_P (t));
   else
     hstate.add_flag (TREE_NO_WARNING (t));
   hstate.add_flag (TREE_NOTHROW (t));
@@ -1166,6 +1166,8 @@ hash_tree (struct streamer_tree_cache_d *cache, hash_map<tree, hashval_t> *map,
       hstate.commit_flag ();
       hstate.add_int (TYPE_PRECISION (t));
       hstate.add_int (TYPE_ALIGN (t));
+      hstate.add_int (TYPE_EMPTY_P (t));
+      hstate.add_int (TYPE_ARTIFICIAL (t));
     }
 
   if (CODE_CONTAINS_STRUCT (code, TS_TRANSLATION_UNIT_DECL))
diff --git gcc/lto/lto.c gcc/lto/lto.c
index 63ba73c0dbf..011f07fd593 100644
--- gcc/lto/lto.c
+++ gcc/lto/lto.c
@@ -1017,7 +1017,7 @@ compare_tree_sccs_1 (tree t1, tree t2, tree **map)
   else if (TYPE_P (t1))
     compare_values (TYPE_UNSIGNED);
   if (TYPE_P (t1))
-    compare_values (TYPE_ARTIFICIAL);
+    compare_values (TYPE_WARN_EMPTY_P);
   else
     compare_values (TREE_NO_WARNING);
   compare_values (TREE_NOTHROW);
@@ -1165,6 +1165,8 @@ compare_tree_sccs_1 (tree t1, tree t2, tree **map)
 	compare_values (TYPE_NONALIASED_COMPONENT);
       if (AGGREGATE_TYPE_P (t1))
 	compare_values (TYPE_TYPELESS_STORAGE);
+      compare_values (TYPE_ARTIFICIAL);
+      compare_values (TYPE_EMPTY_P);
       compare_values (TYPE_PACKED);
       compare_values (TYPE_RESTRICT);
       compare_values (TYPE_USER_ALIGN);
diff --git gcc/target.def gcc/target.def
index 577dad8fe86..81aedee80d9 100644
--- gcc/target.def
+++ gcc/target.def
@@ -5055,6 +5055,22 @@ DEFHOOK
  fixed_size_mode, (int regno),
  default_get_reg_raw_mode)
 
+/* Return true if a type is an empty record.  */
+DEFHOOK
+(empty_record_p,
+ "This target hook returns true if the type is an empty record.  The default\n\
+is to return @code{false}.",
+ bool, (const_tree type),
+ hook_bool_const_tree_false)
+
+/* Warn about the change in empty class parameter passing ABI.  */
+DEFHOOK
+(warn_parameter_passing_abi,
+ "This target hook warns about the change in empty class parameter passing\n\
+ABI.",
+ void, (cumulative_args_t ca, tree type),
+ hook_void_CUMULATIVE_ARGS_tree)
+
 HOOK_VECTOR_END (calls)
 
 DEFHOOK
diff --git gcc/targhooks.c gcc/targhooks.c
index dad1e109d23..0edc57b0a15 100644
--- gcc/targhooks.c
+++ gcc/targhooks.c
@@ -756,6 +756,12 @@ hook_int_CUMULATIVE_ARGS_mode_tree_bool_0 (
 }
 
 void
+hook_void_CUMULATIVE_ARGS_tree (cumulative_args_t ca ATTRIBUTE_UNUSED,
+				tree ATTRIBUTE_UNUSED)
+{
+}
+
+void
 default_function_arg_advance (cumulative_args_t ca ATTRIBUTE_UNUSED,
 			      machine_mode mode ATTRIBUTE_UNUSED,
 			      const_tree type ATTRIBUTE_UNUSED,
@@ -2108,6 +2114,7 @@ std_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
   /* va_list pointer is aligned to PARM_BOUNDARY.  If argument actually
      requires greater alignment, we must perform dynamic alignment.  */
   if (boundary > align
+      && !TYPE_EMPTY_P (type)
       && !integer_zerop (TYPE_SIZE (type)))
     {
       t = build2 (MODIFY_EXPR, TREE_TYPE (valist), valist_tmp,
@@ -2134,7 +2141,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 = arg_size_in_bytes (type);
   rounded_size = round_up (type_size, align);
 
   /* Reduce rounded_size so it's sharable with the postqueue.  */
diff --git gcc/targhooks.h gcc/targhooks.h
index 15bbf5cdf24..e431934cd60 100644
--- gcc/targhooks.h
+++ gcc/targhooks.h
@@ -135,6 +135,8 @@ extern bool hook_bool_CUMULATIVE_ARGS_mode_tree_bool_true
   (cumulative_args_t, machine_mode, const_tree, bool);
 extern int hook_int_CUMULATIVE_ARGS_mode_tree_bool_0
   (cumulative_args_t, machine_mode, tree, bool);
+extern void hook_void_CUMULATIVE_ARGS_tree
+  (cumulative_args_t, tree);
 extern const char *hook_invalid_arg_for_unprototyped_fn
   (const_tree, const_tree, const_tree);
 extern void default_function_arg_advance
diff --git gcc/testsuite/g++.dg/abi/empty12.C gcc/testsuite/g++.dg/abi/empty12.C
index e69de29bb2d..20d85ff873e 100644
--- gcc/testsuite/g++.dg/abi/empty12.C
+++ gcc/testsuite/g++.dg/abi/empty12.C
@@ -0,0 +1,17 @@
+// PR c++/60336
+// { dg-do run { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } }
+// { dg-options "-Wabi=11 -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 gcc/testsuite/g++.dg/abi/empty12.h gcc/testsuite/g++.dg/abi/empty12.h
index e69de29bb2d..c61afcda0fb 100644
--- gcc/testsuite/g++.dg/abi/empty12.h
+++ 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 gcc/testsuite/g++.dg/abi/empty12a.c gcc/testsuite/g++.dg/abi/empty12a.c
index e69de29bb2d..34a25bad75d 100644
--- gcc/testsuite/g++.dg/abi/empty12a.c
+++ 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 gcc/testsuite/g++.dg/abi/empty13.C gcc/testsuite/g++.dg/abi/empty13.C
index e69de29bb2d..0cb9a373e35 100644
--- gcc/testsuite/g++.dg/abi/empty13.C
+++ gcc/testsuite/g++.dg/abi/empty13.C
@@ -0,0 +1,17 @@
+// PR c++/60336
+// { dg-do run { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } }
+// { dg-options "-x c -fabi-version=11" }
+// { 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 gcc/testsuite/g++.dg/abi/empty13.h gcc/testsuite/g++.dg/abi/empty13.h
index e69de29bb2d..c61afcda0fb 100644
--- gcc/testsuite/g++.dg/abi/empty13.h
+++ 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 gcc/testsuite/g++.dg/abi/empty13a.c gcc/testsuite/g++.dg/abi/empty13a.c
index e69de29bb2d..b4303a63826 100644
--- gcc/testsuite/g++.dg/abi/empty13a.c
+++ 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 gcc/testsuite/g++.dg/abi/empty14.C gcc/testsuite/g++.dg/abi/empty14.C
index e69de29bb2d..2868d8ad3f3 100644
--- gcc/testsuite/g++.dg/abi/empty14.C
+++ gcc/testsuite/g++.dg/abi/empty14.C
@@ -0,0 +1,17 @@
+// PR c++/60336
+// { dg-do run { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } }
+// { dg-options "-Wabi=11 -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 gcc/testsuite/g++.dg/abi/empty14.h gcc/testsuite/g++.dg/abi/empty14.h
index e69de29bb2d..5842279cf37 100644
--- gcc/testsuite/g++.dg/abi/empty14.h
+++ 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 gcc/testsuite/g++.dg/abi/empty14a.c gcc/testsuite/g++.dg/abi/empty14a.c
index e69de29bb2d..8b3d7800c36 100644
--- gcc/testsuite/g++.dg/abi/empty14a.c
+++ 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 gcc/testsuite/g++.dg/abi/empty15.C gcc/testsuite/g++.dg/abi/empty15.C
index e69de29bb2d..12385f78c78 100644
--- gcc/testsuite/g++.dg/abi/empty15.C
+++ gcc/testsuite/g++.dg/abi/empty15.C
@@ -0,0 +1,17 @@
+// PR c++/60336
+// { dg-do run { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } }
+// { dg-options "-Wabi=11 -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 gcc/testsuite/g++.dg/abi/empty15.h gcc/testsuite/g++.dg/abi/empty15.h
index e69de29bb2d..1c6f26f5ae8 100644
--- gcc/testsuite/g++.dg/abi/empty15.h
+++ 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 gcc/testsuite/g++.dg/abi/empty15a.c gcc/testsuite/g++.dg/abi/empty15a.c
index e69de29bb2d..325b2c5ba09 100644
--- gcc/testsuite/g++.dg/abi/empty15a.c
+++ 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 gcc/testsuite/g++.dg/abi/empty16.C gcc/testsuite/g++.dg/abi/empty16.C
index e69de29bb2d..1ca52f9011e 100644
--- gcc/testsuite/g++.dg/abi/empty16.C
+++ gcc/testsuite/g++.dg/abi/empty16.C
@@ -0,0 +1,17 @@
+// PR c++/60336
+// { dg-do run { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } }
+// { dg-options "-Wabi=11 -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 gcc/testsuite/g++.dg/abi/empty16.h gcc/testsuite/g++.dg/abi/empty16.h
index e69de29bb2d..7552ae06576 100644
--- gcc/testsuite/g++.dg/abi/empty16.h
+++ 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 gcc/testsuite/g++.dg/abi/empty16a.c gcc/testsuite/g++.dg/abi/empty16a.c
index e69de29bb2d..6cb7fbccecc 100644
--- gcc/testsuite/g++.dg/abi/empty16a.c
+++ 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 gcc/testsuite/g++.dg/abi/empty17.C gcc/testsuite/g++.dg/abi/empty17.C
index e69de29bb2d..d386e5481af 100644
--- gcc/testsuite/g++.dg/abi/empty17.C
+++ gcc/testsuite/g++.dg/abi/empty17.C
@@ -0,0 +1,17 @@
+// PR c++/60336
+// { dg-do run { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } }
+// { dg-options "-Wabi=11 -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 gcc/testsuite/g++.dg/abi/empty17.h gcc/testsuite/g++.dg/abi/empty17.h
index e69de29bb2d..9cf72baca2e 100644
--- gcc/testsuite/g++.dg/abi/empty17.h
+++ 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 gcc/testsuite/g++.dg/abi/empty17a.c gcc/testsuite/g++.dg/abi/empty17a.c
index e69de29bb2d..24408fde09c 100644
--- gcc/testsuite/g++.dg/abi/empty17a.c
+++ 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 gcc/testsuite/g++.dg/abi/empty18.C gcc/testsuite/g++.dg/abi/empty18.C
index e69de29bb2d..be69c6a2115 100644
--- gcc/testsuite/g++.dg/abi/empty18.C
+++ gcc/testsuite/g++.dg/abi/empty18.C
@@ -0,0 +1,17 @@
+// PR c++/60336
+// { dg-do run { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } }
+// { dg-options "-Wabi=11 -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 gcc/testsuite/g++.dg/abi/empty18.h gcc/testsuite/g++.dg/abi/empty18.h
index e69de29bb2d..86e7ecdd211 100644
--- gcc/testsuite/g++.dg/abi/empty18.h
+++ 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 gcc/testsuite/g++.dg/abi/empty18a.c gcc/testsuite/g++.dg/abi/empty18a.c
index e69de29bb2d..902860bdc01 100644
--- gcc/testsuite/g++.dg/abi/empty18a.c
+++ gcc/testsuite/g++.dg/abi/empty18a.c
@@ -0,0 +1,6 @@
+#include "empty18.h"
+void fun(struct dummy d, struct foo f)
+{
+  if (f.i1 != -1)
+    __builtin_abort();
+}
diff --git gcc/testsuite/g++.dg/abi/empty19.C gcc/testsuite/g++.dg/abi/empty19.C
index e69de29bb2d..84f5b75558b 100644
--- gcc/testsuite/g++.dg/abi/empty19.C
+++ gcc/testsuite/g++.dg/abi/empty19.C
@@ -0,0 +1,17 @@
+// PR c++/60336
+// { dg-do run { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } }
+// { dg-options "-Wabi=11 -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);
+  return 0;
+}
diff --git gcc/testsuite/g++.dg/abi/empty19.h gcc/testsuite/g++.dg/abi/empty19.h
index e69de29bb2d..616b87bdd93 100644
--- gcc/testsuite/g++.dg/abi/empty19.h
+++ 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 gcc/testsuite/g++.dg/abi/empty19a.c gcc/testsuite/g++.dg/abi/empty19a.c
index e69de29bb2d..767b1eb7320 100644
--- gcc/testsuite/g++.dg/abi/empty19a.c
+++ gcc/testsuite/g++.dg/abi/empty19a.c
@@ -0,0 +1,6 @@
+#include "empty19.h"
+void fun(struct dummy d, struct foo f)
+{
+  if (f.i1 != -1)
+    __builtin_abort();
+}
diff --git gcc/testsuite/g++.dg/abi/empty20.C gcc/testsuite/g++.dg/abi/empty20.C
index e69de29bb2d..5022033f669 100644
--- gcc/testsuite/g++.dg/abi/empty20.C
+++ gcc/testsuite/g++.dg/abi/empty20.C
@@ -0,0 +1,19 @@
+// PR c++/60336
+// { dg-options "-Wabi=11 -O0" }
+
+struct A { };
+
+void f(A, A) { }	// No warning, trailing parms all empty
+void f(A, A, int) { }	// { dg-warning "ABI" "" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } }
+__attribute__ ((always_inline))
+inline void f(A a, int i) // No warning, always inlined
+{
+  f(a,a,i); // { dg-warning "ABI" "" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } }
+}
+int main()
+{
+  A a;
+  f(a,a);
+  f(a,a,42); // { dg-warning "ABI" "" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } }
+  f(a,42);
+}
diff --git gcc/testsuite/g++.dg/abi/empty21.C gcc/testsuite/g++.dg/abi/empty21.C
index e69de29bb2d..3b2e3b836b1 100644
--- gcc/testsuite/g++.dg/abi/empty21.C
+++ gcc/testsuite/g++.dg/abi/empty21.C
@@ -0,0 +1,23 @@
+// PR c++/60336
+// { dg-options "-Wabi=11" }
+
+#include <stdarg.h>
+
+struct A { };
+
+void f(int i, ...)
+{
+  va_list ap;
+  va_start (ap, i);
+  if (i >= 1)
+    va_arg (ap, A);
+  if (i >= 2)
+    va_arg (ap, int);
+}
+
+int main()
+{
+  f(0);
+  f(1, A()); // { dg-warning "ABI" "" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } }
+  f(2, A(), 42); // { dg-warning "ABI" "" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } }
+}
diff --git gcc/testsuite/g++.dg/abi/empty22.C gcc/testsuite/g++.dg/abi/empty22.C
index e69de29bb2d..f4f4a02bf31 100644
--- gcc/testsuite/g++.dg/abi/empty22.C
+++ gcc/testsuite/g++.dg/abi/empty22.C
@@ -0,0 +1,17 @@
+// PR c++/60336
+// { dg-do run { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } }
+// { dg-options "-Wabi=11 -x c" }
+// { dg-additional-sources "empty22a.c" }
+// { dg-prune-output "command line option" }
+
+#include "empty22.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 gcc/testsuite/g++.dg/abi/empty22.h gcc/testsuite/g++.dg/abi/empty22.h
index e69de29bb2d..8d54dc74519 100644
--- gcc/testsuite/g++.dg/abi/empty22.h
+++ gcc/testsuite/g++.dg/abi/empty22.h
@@ -0,0 +1,27 @@
+#ifdef __cplusplus
+struct A1
+{
+  void foo (void);
+  unsigned int : 0;
+};
+struct A2
+{
+  void bar (void);
+  unsigned int : 0;
+};
+struct dummy : A1, A2
+{
+  unsigned int : 0;
+};
+#else
+struct dummy {};
+#endif
+
+struct foo
+{
+  int i1;
+  int i2;
+  int i3;
+  int i4;
+  int i5;
+};
diff --git gcc/testsuite/g++.dg/abi/empty22a.c gcc/testsuite/g++.dg/abi/empty22a.c
index e69de29bb2d..7606c524263 100644
--- gcc/testsuite/g++.dg/abi/empty22a.c
+++ gcc/testsuite/g++.dg/abi/empty22a.c
@@ -0,0 +1,6 @@
+#include "empty22.h"
+void fun(struct dummy d, struct foo f)
+{
+  if (f.i1 != -1)
+    __builtin_abort();
+}
diff --git gcc/testsuite/g++.dg/abi/empty23.C gcc/testsuite/g++.dg/abi/empty23.C
index e69de29bb2d..dbeda81fb24 100644
--- gcc/testsuite/g++.dg/abi/empty23.C
+++ gcc/testsuite/g++.dg/abi/empty23.C
@@ -0,0 +1,25 @@
+// PR c++/60336
+// { dg-do run }
+// { dg-options "-Wabi=11" }
+
+struct S
+{
+  struct { } a;
+  __extension__ int b[0];
+};
+
+struct S s;
+struct S a[5];
+
+void
+foo (struct S, struct S *arg1, struct S) // { dg-warning "ABI" "" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } }
+{
+  if (arg1 != &a[1])
+    __builtin_abort ();
+}
+
+int
+main ()
+{
+  foo (s, &a[1], a[2]); // { dg-warning "ABI" "" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } }
+}
diff --git gcc/testsuite/g++.dg/abi/empty24.C gcc/testsuite/g++.dg/abi/empty24.C
index e69de29bb2d..822ced1ef50 100644
--- gcc/testsuite/g++.dg/abi/empty24.C
+++ gcc/testsuite/g++.dg/abi/empty24.C
@@ -0,0 +1,25 @@
+// PR c++/60336
+// { dg-do run }
+// { dg-options "-Wabi=11" }
+
+struct S
+{
+  struct { } a;
+  __extension__ int b[];
+};
+
+struct S s;
+struct S a[5];
+
+void
+foo (struct S, struct S *arg1, struct S) // { dg-warning "ABI" "" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } }
+{
+  if (arg1 != &a[1])
+    __builtin_abort ();
+}
+
+int
+main ()
+{
+  foo (s, &a[1], a[2]); // { dg-warning "ABI" "" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } }
+}
diff --git gcc/testsuite/g++.dg/abi/pr60336-1.C gcc/testsuite/g++.dg/abi/pr60336-1.C
index e69de29bb2d..59447890cec 100644
--- gcc/testsuite/g++.dg/abi/pr60336-1.C
+++ 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-*-* } && { ! { ia32 } } } } } }
diff --git gcc/testsuite/g++.dg/abi/pr60336-10.C gcc/testsuite/g++.dg/abi/pr60336-10.C
index e69de29bb2d..960cc2307d1 100644
--- gcc/testsuite/g++.dg/abi/pr60336-10.C
+++ gcc/testsuite/g++.dg/abi/pr60336-10.C
@@ -0,0 +1,50 @@
+// { dg-do run { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } }
+// { 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 gcc/testsuite/g++.dg/abi/pr60336-11.C gcc/testsuite/g++.dg/abi/pr60336-11.C
index e69de29bb2d..14cd6d0ff3d 100644
--- gcc/testsuite/g++.dg/abi/pr60336-11.C
+++ gcc/testsuite/g++.dg/abi/pr60336-11.C
@@ -0,0 +1,56 @@
+// { dg-do run { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } }
+// { 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 gcc/testsuite/g++.dg/abi/pr60336-12.C gcc/testsuite/g++.dg/abi/pr60336-12.C
index e69de29bb2d..09917547930 100644
--- gcc/testsuite/g++.dg/abi/pr60336-12.C
+++ gcc/testsuite/g++.dg/abi/pr60336-12.C
@@ -0,0 +1,57 @@
+// { dg-do run { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } }
+// { 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 gcc/testsuite/g++.dg/abi/pr60336-2.C gcc/testsuite/g++.dg/abi/pr60336-2.C
index e69de29bb2d..1c6c3eb8f01 100644
--- gcc/testsuite/g++.dg/abi/pr60336-2.C
+++ gcc/testsuite/g++.dg/abi/pr60336-2.C
@@ -0,0 +1,48 @@
+// { dg-do run { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } }
+// { dg-options "-O2 -Wabi=11" }
+
+#include <stdarg.h>
+
+struct dummy { };
+
+void
+test (struct dummy a, int m, ...) // { dg-warning "empty" }
+{
+  va_list va_arglist;
+  int i;
+  int count = 0;
+
+  if (m == 0)
+    count++;
+  va_start (va_arglist, m);
+  i = va_arg (va_arglist, int);
+  if (i == 1)
+    count++;
+  i = va_arg (va_arglist, int);
+  if (i == 2)
+  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-warning "empty" }
+  return 0;
+}
diff --git gcc/testsuite/g++.dg/abi/pr60336-3.C gcc/testsuite/g++.dg/abi/pr60336-3.C
index e69de29bb2d..4157e553b6b 100644
--- gcc/testsuite/g++.dg/abi/pr60336-3.C
+++ gcc/testsuite/g++.dg/abi/pr60336-3.C
@@ -0,0 +1,15 @@
+// { dg-do compile }
+// { dg-options "-O2 -Wabi=11" }
+
+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-warning "ABI" "" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } }
+  test2 (a0, 42); // { dg-warning "ABI" "" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } }
+}
diff --git gcc/testsuite/g++.dg/abi/pr60336-4.C gcc/testsuite/g++.dg/abi/pr60336-4.C
index e69de29bb2d..266f67a537d 100644
--- gcc/testsuite/g++.dg/abi/pr60336-4.C
+++ 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=11" }
+
+#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 gcc/testsuite/g++.dg/abi/pr60336-5.C gcc/testsuite/g++.dg/abi/pr60336-5.C
index e69de29bb2d..fe838750f55 100644
--- gcc/testsuite/g++.dg/abi/pr60336-5.C
+++ 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-*-* } && { ! { ia32 } } } } } }
diff --git gcc/testsuite/g++.dg/abi/pr60336-6.C gcc/testsuite/g++.dg/abi/pr60336-6.C
index e69de29bb2d..6e08c8f06fa 100644
--- gcc/testsuite/g++.dg/abi/pr60336-6.C
+++ 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-*-* } && { ! { ia32 } } } } } }
diff --git gcc/testsuite/g++.dg/abi/pr60336-7.C gcc/testsuite/g++.dg/abi/pr60336-7.C
index e69de29bb2d..3b8b8ba6f35 100644
--- gcc/testsuite/g++.dg/abi/pr60336-7.C
+++ 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-*-* } && { ! { ia32 } } } } } }
diff --git gcc/testsuite/g++.dg/abi/pr60336-8.C gcc/testsuite/g++.dg/abi/pr60336-8.C
index e69de29bb2d..a1ffb64ef02 100644
--- gcc/testsuite/g++.dg/abi/pr60336-8.C
+++ gcc/testsuite/g++.dg/abi/pr60336-8.C
@@ -0,0 +1,15 @@
+// { dg-do compile }
+// { dg-options "-O2 -Wabi=11" }
+
+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-warning "ABI" "" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } }
+  test2 (a0, 42); // { dg-warning "ABI" "" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } }
+}
diff --git gcc/testsuite/g++.dg/abi/pr60336-9.C gcc/testsuite/g++.dg/abi/pr60336-9.C
index e69de29bb2d..393f02b62f0 100644
--- gcc/testsuite/g++.dg/abi/pr60336-9.C
+++ 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-*-* } && { ! { ia32 } } } } } }
diff --git gcc/testsuite/g++.dg/abi/pr68355.C gcc/testsuite/g++.dg/abi/pr68355.C
index e69de29bb2d..1354fc497b5 100644
--- gcc/testsuite/g++.dg/abi/pr68355.C
+++ 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-*-* } } }
diff --git gcc/testsuite/g++.dg/lto/pr60336_0.C gcc/testsuite/g++.dg/lto/pr60336_0.C
index e69de29bb2d..a0a598c0029 100644
--- gcc/testsuite/g++.dg/lto/pr60336_0.C
+++ gcc/testsuite/g++.dg/lto/pr60336_0.C
@@ -0,0 +1,47 @@
+// { dg-lto-do run }
+
+#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 gcc/tree-core.h gcc/tree-core.h
index f74f1453de6..aaad8d48bcf 100644
--- gcc/tree-core.h
+++ gcc/tree-core.h
@@ -1265,8 +1265,8 @@ struct GTY(()) tree_base {
            all expressions
            all decls
 
-       TYPE_ARTIFICIAL in
-           all types
+       TYPE_WARN_EMPTY_P in
+	   all types
 
    default_def_flag:
 
@@ -1531,7 +1531,9 @@ struct GTY(()) tree_type_common {
   unsigned align : 6;
   unsigned warn_if_not_align : 6;
   unsigned typeless_storage : 1;
-  unsigned spare : 18;
+  unsigned artificial_flag : 1;
+  unsigned empty_flag : 1;
+  unsigned spare : 16;
 
   alias_set_type alias_set;
   tree pointer_to;
diff --git gcc/tree-streamer-in.c gcc/tree-streamer-in.c
index baf0c5bf837..7087186b9a8 100644
--- gcc/tree-streamer-in.c
+++ gcc/tree-streamer-in.c
@@ -130,7 +130,7 @@ unpack_ts_base_value_fields (struct bitpack_d *bp, tree expr)
     bp_unpack_value (bp, 1);
   TREE_ASM_WRITTEN (expr) = (unsigned) bp_unpack_value (bp, 1);
   if (TYPE_P (expr))
-    TYPE_ARTIFICIAL (expr) = (unsigned) bp_unpack_value (bp, 1);
+    TYPE_WARN_EMPTY_P (expr) = (unsigned) bp_unpack_value (bp, 1);
   else
     TREE_NO_WARNING (expr) = (unsigned) bp_unpack_value (bp, 1);
   TREE_NOTHROW (expr) = (unsigned) bp_unpack_value (bp, 1);
@@ -381,6 +381,8 @@ unpack_ts_type_common_value_fields (struct bitpack_d *bp, tree expr)
     TYPE_NONALIASED_COMPONENT (expr) = (unsigned) bp_unpack_value (bp, 1);
   if (AGGREGATE_TYPE_P (expr))
     TYPE_TYPELESS_STORAGE (expr) = (unsigned) bp_unpack_value (bp, 1);
+  TYPE_ARTIFICIAL (expr) = (unsigned) bp_unpack_value (bp, 1);
+  TYPE_EMPTY_P (expr) = (unsigned) bp_unpack_value (bp, 1);
   TYPE_PRECISION (expr) = bp_unpack_var_len_unsigned (bp);
   SET_TYPE_ALIGN (expr, bp_unpack_var_len_unsigned (bp));
 #ifdef ACCEL_COMPILER
diff --git gcc/tree-streamer-out.c gcc/tree-streamer-out.c
index 7f52d455f5e..622a414d533 100644
--- gcc/tree-streamer-out.c
+++ gcc/tree-streamer-out.c
@@ -100,7 +100,7 @@ pack_ts_base_value_fields (struct bitpack_d *bp, tree expr)
   bp_pack_value (bp, (TREE_CODE (expr) != SSA_NAME
 		      ? 0 : TREE_ASM_WRITTEN (expr)), 1);
   if (TYPE_P (expr))
-    bp_pack_value (bp, TYPE_ARTIFICIAL (expr), 1);
+    bp_pack_value (bp, TYPE_WARN_EMPTY_P (expr), 1);
   else
     bp_pack_value (bp, TREE_NO_WARNING (expr), 1);
   bp_pack_value (bp, TREE_NOTHROW (expr), 1);
@@ -330,6 +330,8 @@ pack_ts_type_common_value_fields (struct bitpack_d *bp, tree expr)
     bp_pack_value (bp, TYPE_NONALIASED_COMPONENT (expr), 1);
   if (AGGREGATE_TYPE_P (expr))
     bp_pack_value (bp, TYPE_TYPELESS_STORAGE (expr), 1);
+  bp_pack_value (bp, TYPE_ARTIFICIAL (expr), 1);
+  bp_pack_value (bp, TYPE_EMPTY_P (expr), 1);
   bp_pack_var_len_unsigned (bp, TYPE_PRECISION (expr));
   bp_pack_var_len_unsigned (bp, TYPE_ALIGN (expr));
 }
diff --git gcc/tree.c gcc/tree.c
index 28e157f5fd2..4603402f654 100644
--- gcc/tree.c
+++ gcc/tree.c
@@ -13811,6 +13811,64 @@ get_nonnull_args (const_tree fntype)
   return argmap;
 }
 
+/* Returns true if TYPE is a type where it and all of its subobjects
+   (recursively) are of structure, union, or array type.  */
+
+static bool
+default_is_empty_type (tree type)
+{
+  if (RECORD_OR_UNION_TYPE_P (type))
+    {
+      for (tree 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)))
+	    && !default_is_empty_type (TREE_TYPE (field)))
+	  return false;
+      return true;
+    }
+  else if (TREE_CODE (type) == ARRAY_TYPE)
+    return (integer_minus_onep (array_type_nelts (type))
+	    || TYPE_DOMAIN (type) == NULL_TREE
+	    || default_is_empty_type (TREE_TYPE (type)));
+  return false;
+}
+
+/* Implement TARGET_EMPTY_RECORD_P.  Return true if TYPE is an empty type
+   that shouldn't be passed via stack.  */
+
+bool
+default_is_empty_record (const_tree type)
+{
+  if (!abi_version_at_least (12))
+    return false;
+
+  if (type == error_mark_node)
+    return false;
+
+  /* The front end should have turned this into an invisible reference
+     already.  */
+  gcc_assert (!TREE_ADDRESSABLE (type));
+
+  return default_is_empty_type (TYPE_MAIN_VARIANT (type));
+}
+
+/* Like int_size_in_bytes, but handle empty records specially.  */
+
+HOST_WIDE_INT
+arg_int_size_in_bytes (const_tree type)
+{
+  return TYPE_EMPTY_P (type) ? 0 : int_size_in_bytes (type);
+}
+
+/* Like size_in_bytes, but handle empty records specially.  */
+
+tree
+arg_size_in_bytes (const_tree type)
+{
+  return TYPE_EMPTY_P (type) ? size_zero_node : size_in_bytes (type);
+}
+
 /* List of pointer types used to declare builtins before we have seen their
    real declaration.
 
diff --git gcc/tree.h gcc/tree.h
index 277aa919780..1f20e018567 100644
--- gcc/tree.h
+++ gcc/tree.h
@@ -696,8 +696,16 @@ extern void omp_clause_range_check_failed (const_tree, const char *, int,
    emitted.  */
 #define TREE_NO_WARNING(NODE) ((NODE)->base.nowarning_flag)
 
+/* Nonzero if we should warn about the change in empty class parameter
+   passing ABI.  */
+#define TYPE_WARN_EMPTY_P(NODE) (TYPE_CHECK (NODE)->base.nowarning_flag)
+
+/* Nonzero if this type is "empty" according to the particular psABI.  */
+#define TYPE_EMPTY_P(NODE) (TYPE_CHECK (NODE)->type_common.empty_flag)
+
 /* Used to indicate that this TYPE represents a compiler-generated entity.  */
-#define TYPE_ARTIFICIAL(NODE) (TYPE_CHECK (NODE)->base.nowarning_flag)
+#define TYPE_ARTIFICIAL(NODE) \
+  (TYPE_CHECK (NODE)->type_common.artificial_flag)
 
 /* In an IDENTIFIER_NODE, this means that assemble_name was called with
    this string as an argument.  */
@@ -5428,6 +5436,9 @@ 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 default_is_empty_record (const_tree);
+extern HOST_WIDE_INT arg_int_size_in_bytes (const_tree);
+extern tree arg_size_in_bytes (const_tree);
 
 extern location_t
 set_source_range (tree expr, location_t start, location_t finish);

	Marek


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