On Wed, 1 Nov 2017, Marek Polacek wrote:

> On Fri, Oct 27, 2017 at 12:46:12PM +0200, Richard Biener wrote:
> > On Fri, 27 Oct 2017, Jakub Jelinek wrote:
> > 
> > > On Fri, Oct 27, 2017 at 12:31:46PM +0200, Richard Biener wrote:
> > > > I fear it doesn't work at all with LTO (you'll always get the old ABI
> > > > if I read the patch correctly).  This is because the function
> > > > computing the size looks at flag_abi_version which isn't saved
> > > > per function / TU.
> > > > 
> > > > Similarly you'll never get the ABI warning with LTO (less of a big
> > > > deal of course) because the langhook doesn't reflect things correctly
> > > > either.
> > > > 
> > > > So...  can we instead compute whether a type is "empty" according
> > > > to the ABI early and store the result in the type (thinking of
> > > > doing this in layout_type?).  Similarly set a flag whether to
> > > > warn.  Why do you warn from backends / code emission and not
> > > > from the FEs?  Is that to avoid warnings for calls that got inlined?
> > > > Maybe the FE could set a flag on the call itself (ok, somewhat
> > > > awkward to funnel through gimple).
> > > 
> > > Warning in the FE is too early both because of the inlining, never
> > > emitted functions and because whether an empty struct is passed 
> > > differently
> > > from the past matters on the backend (whether its psABI says it should be
> > > not passed at all or not).
> > > 
> > > Perhaps if empty types are rare enough it could be an artificial attribute
> > > on the type if we can't get a spare bit for that.  But computing in the FE
> > > or before free_lang_data and saving on the type whether it is empty or not
> > > seems reasonable to me.
> > 
> > There are 18 unused bits in tree_type_common if we don't want to re-use
> > any.  For the warning I first thought of setting TREE_NO_WARNING on it
> > but that bit is used already.  OTOH given the "fit" of TREE_NO_WARNING
> > I'd move TYPE_ARTIFICIAL somewhere else.
> 
> All right, should be done in the below.  I've introduced two new flags,
> TYPE_EMPTY_P (says whether the type is empty according to the psABI), and
> TYPE_WARN_EMPTY_P (whether we should warn).  I've added two new fields to
> type_type_common and moved TYPE_ARTIFICIAL there; TYPE_WARN_EMPTY_P is now
> mapped to nowarning_flag.  So this should work with LTO, as demonstrated
> by g++.dg/lto/pr60336_0.C.  
> 
> Regarding LTO and -Wabi warning, I've added Optimization to c.opt so that
> we get warnings with LTO.  But as pointed out IRC, this doesn't fully work
> with cross-inlining.  I tried to do some flags merging in inline_call, but
> that didn't help, one of the problems is that warn_abi_version lives in
> c-family only.  Not sure if I'll be able to improve things here though.
> 
> Bootstrapped/regtested on x86_64-linux, ppc64-linux, and aarch64-linux.
> Bootstrap-lto passed on x86_64-linux and ppc64-linux.

To me the tree.c stuff is_empty_type looks awfully ABI dependent
and should thus reside in i386.c near the target hook implementation?

What goes wrong if we do not introduce new int_maybe_empty_type_size
and maybe_empty_type_size but instead change int_size_in_bytes and
size_in_bytes to return 0 if TYPE_EMPTY_P ()?  If the ABI can omit
passing things assuming the size is zero should work as well, no?
Otherwise I'd really prefer seeing explicit TYPE_EMPTY_P checks
which would reduce the number of "indirect" greps one has to do when
looking for effects of TYPE_EMPTY_P.

Otherwise the middle-end/LTO parts look ok.

I'd omit the 'Optimization' change on the Wabi warning flag if it
doesn't fully give us what we want and address this as a followup.

I think 'Optimization' is also used for -help reporting and thus
could be confusing at first.

Still needs FE and target maintainer approval -- the target maintainer
wants to look at the seemingly ABI independent functions in tree.c.

Thanks,
Richard.

> 2017-11-01  Marek Polacek  <pola...@redhat.com>
>           H.J. Lu  <hongjiu...@intel.com>
>           Jason Merrill  <ja...@redhat.com>
> 
>       PR c++/60336
>       PR middle-end/67239
>       PR target/68355
>       * c.opt (Wabi, Wabi=): Add Optimization.
> 
>       * class.c (layout_class_type): Set TYPE_EMPTY_P and TYPE_WARN_EMPTY_P.
>       * cp-tree.h (array_type_nelts_top): Remove.
>       * tree.c (array_type_nelts_top): Move to tree.c.
> 
>       * 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_function_arg_advance): Skip empty records.
>       (ix86_return_in_memory): Return false for empty types.
>       (ix86_gimplify_va_arg): Call int_maybe_empty_type_size instead of
>       int_size_in_bytes.
>       (ix86_is_empty_record_p): 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 int_maybe_empty_type_size
>       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 maybe_empty_type_size 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
>       maybe_empty_type_size 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 (array_type_nelts_top): New function.
>       (is_empty_type): New function.
>       (is_empty_record_p): New function.
>       (int_maybe_empty_type_size): New function.
>       (maybe_empty_type_size): New function.
>       * tree.h: Define TYPE_EMPTY_P and TYPE_WARN_EMPTY_P.  Map
>       TYPE_ARTIFICIAL to type_common.artificial_flag.
>       (array_type_nelts_top, is_empty_record_p, int_maybe_empty_type_size,
>       maybe_empty_type_size): 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/c-family/c.opt gcc/c-family/c.opt
> index dae124ac1c2..5c45d1b5062 100644
> --- gcc/c-family/c.opt
> +++ gcc/c-family/c.opt
> @@ -257,11 +257,11 @@ C ObjC C++ ObjC++ Joined Separate MissingArgError(macro 
> name missing after %qs)
>  -U<macro>    Undefine <macro>.
>  
>  Wabi
> -C ObjC C++ ObjC++ LTO Var(warn_abi) Warning
> +C ObjC C++ ObjC++ LTO Optimization Var(warn_abi) Warning
>  Warn about things that will change when compiling with an ABI-compliant 
> compiler.
>  
>  Wabi=
> -C ObjC C++ ObjC++ LTO Joined RejectNegative UInteger Warning
> +C ObjC C++ ObjC++ LTO Optimization Joined RejectNegative UInteger Warning
>  Warn about things that change between the current -fabi-version and the 
> specified version.
>  
>  Wabi-tag
> diff --git gcc/calls.c gcc/calls.c
> index 3730f43c7a9..aefa4b561e6 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))
> +                 - int_maybe_empty_type_size (TREE_TYPE (pval))
>                   + partial);
> -       size_rtx = expand_expr (size_in_bytes (TREE_TYPE (pval)),
> +       size_rtx = expand_expr (maybe_empty_type_size (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 382635f4fc7..67dcf8aa38c 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
> @@ -8327,6 +8347,10 @@ ix86_function_arg_advance (cumulative_args_t cum_v, 
> machine_mode mode,
>    if (!cum->caller && cfun->machine->func_type != TYPE_NORMAL)
>      return;
>  
> +  /* Skip empty records because they won't be passed.  */
> +  if (type && TYPE_EMPTY_P (type))
> +    return;
> +
>    if (mode == BLKmode)
>      bytes = int_size_in_bytes (type);
>    else
> @@ -9293,6 +9317,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 +9901,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 = int_maybe_empty_type_size (type);
>    rsize = CEIL (size, UNITS_PER_WORD);
>  
>    nat_mode = type_natural_mode (type, NULL, false);
> @@ -28759,6 +28787,44 @@ ix86_constant_alignment (const_tree exp, 
> HOST_WIDE_INT align)
>    return align;
>  }
>  
> +/* Implement TARGET_EMPTY_RECORD_P.  */
> +
> +static bool
> +ix86_is_empty_record_p (const_tree type)
> +{
> +  if (!TARGET_64BIT)
> +    return false;
> +  return is_empty_record_p (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.  */
> @@ -50308,6 +50374,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_p
> +
> +#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 9ef50657cae..7d09a06dd24 100644
> --- gcc/cp/class.c
> +++ gcc/cp/class.c
> @@ -6324,6 +6324,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/cp/cp-tree.h gcc/cp/cp-tree.h
> index 3aefd7e40f4..984f496a459 100644
> --- gcc/cp/cp-tree.h
> +++ gcc/cp/cp-tree.h
> @@ -6916,7 +6916,6 @@ extern tree canonical_eh_spec                   (tree);
>  extern tree build_exception_variant          (tree, tree);
>  extern tree bind_template_template_parm              (tree, tree);
>  extern tree array_type_nelts_total           (tree);
> -extern tree array_type_nelts_top             (tree);
>  extern tree break_out_target_exprs           (tree);
>  extern tree build_ctor_subob_ref             (tree, tree, tree);
>  extern tree replace_placeholders             (tree, tree, bool * = NULL);
> diff --git gcc/cp/tree.c gcc/cp/tree.c
> index 48d40945af3..8a878d70e34 100644
> --- gcc/cp/tree.c
> +++ gcc/cp/tree.c
> @@ -2834,19 +2834,6 @@ cxx_print_statistics (void)
>  }
>  
>  /* Return, as an INTEGER_CST node, the number of elements for TYPE
> -   (which is an ARRAY_TYPE).  This counts only elements of the top
> -   array.  */
> -
> -tree
> -array_type_nelts_top (tree type)
> -{
> -  return fold_build2_loc (input_location,
> -                   PLUS_EXPR, sizetype,
> -                   array_type_nelts (type),
> -                   size_one_node);
> -}
> -
> -/* Return, as an INTEGER_CST node, the number of elements for TYPE
>     (which is an ARRAY_TYPE).  This one is a recursive count of all
>     ARRAY_TYPEs that are clumped together.  */
>  
> diff --git gcc/doc/tm.texi gcc/doc/tm.texi
> index be8b3620684..f8ff820383b 100644
> --- gcc/doc/tm.texi
> +++ gcc/doc/tm.texi
> @@ -4548,6 +4548,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 d433e3a9c6b..1e896c70cee 100644
> --- gcc/doc/tm.texi.in
> +++ gcc/doc/tm.texi.in
> @@ -3437,6 +3437,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..5c864fedd1b 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 = int_maybe_empty_type_size (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 496d492c9fa..337bd6de579 100644
> --- gcc/expr.c
> +++ gcc/expr.c
> @@ -2746,7 +2746,7 @@ copy_blkmode_to_reg (machine_mode mode, tree src)
>  
>    x = expand_normal (src);
>  
> -  bytes = int_size_in_bytes (TREE_TYPE (src));
> +  bytes = int_maybe_empty_type_size (TREE_TYPE (src));
>    if (bytes == 0)
>      return NULL_RTX;
>  
> diff --git gcc/function.c gcc/function.c
> index fe3d9c1bbf3..dd227993050 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
> +           ? maybe_empty_type_size (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 7bddb8b170a..d86607db3c4 100644
> --- gcc/target.def
> +++ gcc/target.def
> @@ -5042,6 +5042,22 @@ DEFHOOK
>   machine_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 92ecc90d4d4..2e7137bae38 100644
> --- gcc/targhooks.c
> +++ gcc/targhooks.c
> @@ -734,6 +734,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,
> @@ -2084,6 +2090,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,
> @@ -2110,7 +2117,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 = maybe_empty_type_size (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 f60bca257f7..722608f35cc 100644
> --- gcc/targhooks.h
> +++ gcc/targhooks.h
> @@ -134,6 +134,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..b97d2804529 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)
> +{
> +  if (arg1 != &a[1])
> +    __builtin_abort ();
> +}
> +
> +int
> +main ()
> +{
> +  foo (s, &a[1], a[2]);
> +}
> diff --git gcc/testsuite/g++.dg/abi/empty24.C 
> gcc/testsuite/g++.dg/abi/empty24.C
> index e69de29bb2d..81deb36ff9f 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)
> +{
> +  if (arg1 != &a[1])
> +    __builtin_abort ();
> +}
> +
> +int
> +main ()
> +{
> +  foo (s, &a[1], a[2]);
> +}
> 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..73a6ec394bc 100644
> --- gcc/tree.c
> +++ gcc/tree.c
> @@ -3135,6 +3135,20 @@ array_type_nelts (const_tree type)
>         ? max
>         : fold_build2 (MINUS_EXPR, TREE_TYPE (max), max, min));
>  }
> +
> +/* Return, as an INTEGER_CST node, the number of elements for TYPE
> +   (which is an ARRAY_TYPE).  This counts only elements of the top
> +   array.  */
> +
> +tree
> +array_type_nelts_top (const_tree type)
> +{
> +  return fold_build2_loc (input_location,
> +                       PLUS_EXPR, sizetype,
> +                       array_type_nelts (type),
> +                       size_one_node);
> +}
> +
>  
>  /* If arg is static -- a reference to an object in static storage -- then
>     return the object.  This is not the same as the C meaning of `static'.
> @@ -13811,6 +13825,87 @@ 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
> +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)
> +         {
> +           tree ftype = TREE_TYPE (field);
> +           /* Don't consider struct S { struct { } a; int b[0]; };
> +              an empty type.  */
> +           if (TREE_CODE (ftype) == ARRAY_TYPE
> +               && integer_zerop (array_type_nelts_top (ftype)))
> +             {
> +               tree t = DECL_CHAIN (field);
> +               bool found = false;
> +               /* See if the zero-length array is followed by another
> +                  FIELD_DECL.  */
> +               while (t)
> +                 {
> +                   if (TREE_CODE (t) == FIELD_DECL)
> +                     {
> +                       found = true;
> +                       break;
> +                     }
> +                   t = DECL_CHAIN (t);
> +                 }
> +               if (!found)
> +                 return false;
> +             }
> +           if ((DECL_NAME (field) || RECORD_OR_UNION_TYPE_P (ftype))
> +               && !is_empty_type (ftype))
> +             return false;
> +         }
> +     }
> +      return true;
> +    }
> +  else if (TREE_CODE (type) == ARRAY_TYPE)
> +    return (integer_zerop (array_type_nelts_top (type))
> +         || 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
> +is_empty_record_p (const_tree type)
> +{
> +  if (!abi_version_at_least (12))
> +    return false;
> +
> +  if (type == error_mark_node)
> +    return false;
> +
> +  if (TREE_ADDRESSABLE (type))
> +    return false;
> +
> +  return is_empty_type (TYPE_MAIN_VARIANT (type));
> +}
> +
> +/* Like int_size_in_bytes, but handle empty records specially.  */
> +
> +HOST_WIDE_INT
> +int_maybe_empty_type_size (const_tree type)
> +{
> +  return TYPE_EMPTY_P (type) ? 0 : int_size_in_bytes (type);
> +}
> +
> +/* Like size_in_bytes, but handle empty records specially.  */
> +
> +tree
> +maybe_empty_type_size (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..616f56eba95 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.  */
> @@ -4093,6 +4101,7 @@ extern tree build_method_type (tree, tree);
>  extern tree build_offset_type (tree, tree);
>  extern tree build_complex_type (tree, bool named = false);
>  extern tree array_type_nelts (const_tree);
> +extern tree array_type_nelts_top (const_tree);
>  
>  extern tree value_member (tree, tree);
>  extern tree purpose_member (const_tree, tree);
> @@ -5428,6 +5437,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 is_empty_record_p (const_tree);
> +extern HOST_WIDE_INT int_maybe_empty_type_size (const_tree);
> +extern tree maybe_empty_type_size (const_tree);
>  
>  extern location_t
>  set_source_range (tree expr, location_t start, location_t finish);
> 
>       Marek
> 
> 

-- 
Richard Biener <rguent...@suse.de>
SUSE LINUX GmbH, GF: Felix Imendoerffer, Jane Smithard, Graham Norton, HRB 
21284 (AG Nuernberg)

Reply via email to