This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Re: Adjust empty class parameter passing ABI (PR c++/60336)
On Thu, 9 Nov 2017, Marek Polacek wrote:
> On Wed, Nov 08, 2017 at 09:06:51AM +0100, Richard Biener wrote:
> > > >> >> > + 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;
> > > >> >>
> > > >> >> Hmm, this assumes that any unnamed field can be ignored; I'm concerned
> > > >> >> that some front end might clear DECL_NAME for a reason that doesn't
> > > >> >> imply that the field is just padding.
> > > >> >
> > > >> > In that case I guess we need a new lang hook, right? Because in
> > > >> > default_is_empty_type we can't check FE-specific flags such as
> > > >> > DECL_C_BIT_FIELD. For C++, should that lang hook be just
> > > >> > is_really_empty_class? Or is there anything else I can do?
> > > >>
> > > >> Hmm, maybe leave it as it is and just document this assumption about
> > > >> FIELD_DECL with null DECL_NAME, both here and in tree.def.
> > > >
> > > > But are you sure you are not changing the ABI for a language other
> > > > than C++ then?
> > >
> > > No, that is the concern, we'd need to check that.
> > >
> > > > I don't think DECL_NAME has any special meaning - why
> > > > not check DECL_ARTIFICIAL or another flag that has appropriate
> > > > semantic - _what_ semantic are you looking for after all?
> > >
> > > That a struct consisting only of padding is considered empty.
> > >
> > > Perhaps we could use decl_flag_3 on FIELD_DECL for DECL_PADDING_P to
> > > indicate that a field isn't really data. Or perhaps we should remove
> > > such things from TYPE_FIELDS after layout is done.
> >
> > Both works for me. As said I'd rather not use NULL DECL_NAME - backends
> > might build record types without names for va_list. There's no
> > technical reason to have a DECL_NAME for sth the user cannot access -
> > which means every DECL_ARTIFICIAL entity. This patch would
> > retroactively introduce one and thus we'd have to audit each and
> > every piece of code building record types...
>
> Done in the below, hopefully. I only had to set DECL_PADDING_P on unnamed
> bit-fields. I've added testcases for zero-sized unnamed bit-fields as well
> as for classes with virtual functions.
>
> Moving TYPE_EMPTY_P to finalize_type_size revealed a bug in Cilk+, it was
> bogusly (I believe) setting TREE_ADDRESSABLE, so the assert fired. Since
> Cilk+ is being deprecated and the Cilk+ testsuite still passes, I'm not
> too worried about this change.
>
> So if you're fine with the DECL_PADDING_P change, all that remains is to
> check whether this patch is not changing the ABI for other languages than
> C++. I suppose one way to check that could be to do sth like
>
> if (strncmp (lang_hooks.name, "GNU C++", 7))
> {FILE *f=fopen("/tmp/A", "a");fprintf(f,"%s\n",main_input_filename);fclose(f);}
>
> and compare the assembly of the files it prints? But there were thousands of
> them I think :(.
Shouldn't most of GCCs own object files (and target library object files)
compare 1:1 before and after the patch? After all it should _only_ affect
parameter passing of empty aggregates (and the files the patch changes
of course)?
Richard.
> Bootstrapped/regtested on x86_64-linux and ppc64-linux.
>
> 2017-11-09 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
> * c-decl.c (grokdeclarator): Set DECL_PADDING_P on unnamed bit-fields.
>
> * class.c (layout_class_type): Set DECL_PADDING_P on padding.
> * decl.c (cxx_init_decl_processing): Set TRANSLATION_UNIT_WARN_EMPTY_P.
> (grokdeclarator): Set DECL_PADDING_P on unnamed bit-fields.
>
> * lto.c (compare_tree_sccs_1): Compare TYPE_EMPTY_P and DECL_PADDING_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.
> * cilk-common.c (cilk_init_builtins): Don't set TREE_ADDRESSABLE.
> * 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 DECL_PADDING_P.
> * stor-layout.c (finalize_type_size): Set TYPE_EMPTY_P.
> * target.def (empty_record_p, warn_parameter_passing_abi): New target
> hooks.
> * 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): Add empty_flag.
> (tree_decl_common): Update comments.
> * tree-streamer-in.c (unpack_ts_decl_common_value_fields): Stream
> DECL_PADDING_P.
> (unpack_ts_type_common_value_fields): Stream TYPE_EMPTY_P.
> * tree-streamer-out.c (pack_ts_decl_common_value_fields): Stream
> DECL_PADDING_P.
> (pack_ts_type_common_value_fields): Stream TYPE_EMPTY_P.
> * tree.c (default_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, DECL_PADDING_P and
> TRANSLATION_UNIT_WARN_EMPTY_P.
> (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/empty25.C: New test.
> * g++.dg/abi/empty25.h: New test.
> * g++.dg/abi/empty25a.c: New test.
> * g++.dg/abi/empty26.C: New test.
> * g++.dg/abi/empty26.h: New test.
> * g++.dg/abi/empty26a.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/c-decl.c gcc/c/c-decl.c
> index d95a2b6ea4f..dd01631e0bd 100644
> --- gcc/c/c-decl.c
> +++ gcc/c/c-decl.c
> @@ -6801,7 +6801,10 @@ grokdeclarator (const struct c_declarator *declarator,
> FIELD_DECL, declarator->u.id, type);
> DECL_NONADDRESSABLE_P (decl) = bitfield;
> if (bitfield && !declarator->u.id)
> - TREE_NO_WARNING (decl) = 1;
> + {
> + TREE_NO_WARNING (decl) = 1;
> + DECL_PADDING_P (decl) = 1;
> + }
>
> if (size_varies)
> C_DECL_VARIABLE_SIZE (decl) = 1;
> 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/cilk-common.c gcc/cilk-common.c
> index edde4711869..f3c477f7a96 100644
> --- gcc/cilk-common.c
> +++ gcc/cilk-common.c
> @@ -206,7 +206,6 @@ cilk_init_builtins (void)
> cilk_trees[CILK_TI_FRAME_CONTEXT] = context;
> /* We don't care about reserved, so no need to store it in cilk_trees. */
> cilk_trees[CILK_TI_FRAME_PEDIGREE] = pedigree;
> - TREE_ADDRESSABLE (frame) = 1;
>
> finish_builtin_struct (frame, "__cilkrts_st_frame_GCC", pedigree, NULL_TREE);
> cilk_frame_type_decl = frame;
> 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 769f1898b22..573dc2cf53a 100644
> --- gcc/config/i386/i386.c
> +++ gcc/config/i386/i386.c
> @@ -7188,6 +7188,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
> @@ -9295,6 +9315,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)
> @@ -9875,7 +9899,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);
> @@ -28810,6 +28834,47 @@ 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;
> +
> + tree ctx = cum->decl ? DECL_CONTEXT (cum->decl) : NULL_TREE;
> + if (ctx != NULL_TREE
> + && TREE_CODE (ctx) == TRANSLATION_UNIT_DECL
> + && !TRANSLATION_UNIT_WARN_EMPTY_P (ctx))
> + 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. */
> @@ -50357,6 +50422,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..c6b0349f8e7 100644
> --- gcc/cp/class.c
> +++ gcc/cp/class.c
> @@ -6198,6 +6198,7 @@ layout_class_type (tree t, tree *virtuals_p)
> DECL_CONTEXT (padding_field) = t;
> DECL_ARTIFICIAL (padding_field) = 1;
> DECL_IGNORED_P (padding_field) = 1;
> + DECL_PADDING_P (padding_field) = 1;
> layout_nonempty_base_or_field (rli, padding_field,
> NULL_TREE,
> empty_base_offsets);
> diff --git gcc/cp/decl.c gcc/cp/decl.c
> index 0ce8f2d3435..6f0f1baa179 100644
> --- gcc/cp/decl.c
> +++ gcc/cp/decl.c
> @@ -4012,6 +4012,10 @@ cxx_init_decl_processing (void)
> TREE_PUBLIC (global_namespace) = 1;
> DECL_CONTEXT (global_namespace)
> = build_translation_unit_decl (get_identifier (main_input_filename));
> + /* Remember whether we want the empty class passing ABI change warning
> + in this TU. */
> + TRANSLATION_UNIT_WARN_EMPTY_P (DECL_CONTEXT (global_namespace))
> + = warn_abi && abi_version_crosses (12);
> debug_hooks->register_main_translation_unit
> (DECL_CONTEXT (global_namespace));
> begin_scope (sk_namespace, global_namespace);
> @@ -12091,7 +12095,10 @@ grokdeclarator (const cp_declarator *declarator,
> FIELD_DECL, unqualified_id, type);
> DECL_NONADDRESSABLE_P (decl) = bitfield;
> if (bitfield && !unqualified_id)
> - TREE_NO_WARNING (decl) = 1;
> + {
> + TREE_NO_WARNING (decl) = 1;
> + DECL_PADDING_P (decl) = 1;
> + }
>
> if (storage_class == sc_mutable)
> {
> 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..e127e508f97 100644
> --- gcc/lto-streamer-out.c
> +++ gcc/lto-streamer-out.c
> @@ -1073,6 +1073,7 @@ hash_tree (struct streamer_tree_cache_d *cache, hash_map<tree, hashval_t> *map,
> {
> hstate.add_flag (DECL_PACKED (t));
> hstate.add_flag (DECL_NONADDRESSABLE_P (t));
> + hstate.add_flag (DECL_PADDING_P (t));
> hstate.add_int (DECL_OFFSET_ALIGN (t));
> }
> else if (code == VAR_DECL)
> @@ -1166,6 +1167,7 @@ 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));
> }
>
> if (CODE_CONTAINS_STRUCT (code, TS_TRANSLATION_UNIT_DECL))
> diff --git gcc/lto/lto.c gcc/lto/lto.c
> index 63ba73c0dbf..748ef02143c 100644
> --- gcc/lto/lto.c
> +++ gcc/lto/lto.c
> @@ -1087,6 +1087,7 @@ compare_tree_sccs_1 (tree t1, tree t2, tree **map)
> {
> compare_values (DECL_PACKED);
> compare_values (DECL_NONADDRESSABLE_P);
> + compare_values (DECL_PADDING_P);
> compare_values (DECL_OFFSET_ALIGN);
> }
> else if (code == VAR_DECL)
> @@ -1165,6 +1166,7 @@ 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_EMPTY_P);
> compare_values (TYPE_PACKED);
> compare_values (TYPE_RESTRICT);
> compare_values (TYPE_USER_ALIGN);
> diff --git gcc/stor-layout.c gcc/stor-layout.c
> index 7730ac33e4f..1dd215ec27d 100644
> --- gcc/stor-layout.c
> +++ gcc/stor-layout.c
> @@ -1859,6 +1859,9 @@ finalize_type_size (tree type)
> SET_TYPE_MODE (variant, mode);
> }
> }
> +
> + /* Handle empty records as per the x86-64 psABI. */
> + TYPE_EMPTY_P (type) = targetm.calls.empty_record_p (type);
> }
>
> /* Return a new underlying object for a bitfield started with FIELD. */
> 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/empty25.C gcc/testsuite/g++.dg/abi/empty25.C
> index e69de29bb2d..da6ef51ff0d 100644
> --- gcc/testsuite/g++.dg/abi/empty25.C
> +++ gcc/testsuite/g++.dg/abi/empty25.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 "empty25a.c" }
> +// { dg-prune-output "command line option" }
> +
> +#include "empty25.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-bogus "empty" }
> + return 0;
> +}
> diff --git gcc/testsuite/g++.dg/abi/empty25.h gcc/testsuite/g++.dg/abi/empty25.h
> index e69de29bb2d..2f22fd5e505 100644
> --- gcc/testsuite/g++.dg/abi/empty25.h
> +++ gcc/testsuite/g++.dg/abi/empty25.h
> @@ -0,0 +1,18 @@
> +#ifdef __cplusplus
> +struct dummy
> +{
> + virtual void bar (void) { }
> + 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/empty25a.c gcc/testsuite/g++.dg/abi/empty25a.c
> index e69de29bb2d..8c16e453c75 100644
> --- gcc/testsuite/g++.dg/abi/empty25a.c
> +++ gcc/testsuite/g++.dg/abi/empty25a.c
> @@ -0,0 +1,6 @@
> +#include "empty25.h"
> +void fun(struct dummy d, struct foo f)
> +{
> + if (f.i1 != -1)
> + __builtin_abort();
> +}
> diff --git gcc/testsuite/g++.dg/abi/empty26.C gcc/testsuite/g++.dg/abi/empty26.C
> index e69de29bb2d..ab2f54d8dab 100644
> --- gcc/testsuite/g++.dg/abi/empty26.C
> +++ gcc/testsuite/g++.dg/abi/empty26.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 "empty26a.c" }
> +// { dg-prune-output "command line option" }
> +
> +#include "empty26.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/empty26.h gcc/testsuite/g++.dg/abi/empty26.h
> index e69de29bb2d..8d54dc74519 100644
> --- gcc/testsuite/g++.dg/abi/empty26.h
> +++ gcc/testsuite/g++.dg/abi/empty26.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/empty26a.c gcc/testsuite/g++.dg/abi/empty26a.c
> index e69de29bb2d..bc0ae47ba2c 100644
> --- gcc/testsuite/g++.dg/abi/empty26a.c
> +++ gcc/testsuite/g++.dg/abi/empty26a.c
> @@ -0,0 +1,6 @@
> +#include "empty26.h"
> +void fun(struct dummy d, struct foo f)
> +{
> + if (f.i1 != -1)
> + __builtin_abort();
> +}
> 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..13ff5e3db71 100644
> --- gcc/tree-core.h
> +++ gcc/tree-core.h
> @@ -1531,7 +1531,8 @@ struct GTY(()) tree_type_common {
> unsigned align : 6;
> unsigned warn_if_not_align : 6;
> unsigned typeless_storage : 1;
> - unsigned spare : 18;
> + unsigned empty_flag : 1;
> + unsigned spare : 17;
>
> alias_set_type alias_set;
> tree pointer_to;
> @@ -1609,7 +1610,8 @@ struct GTY(()) tree_decl_common {
> unsigned lang_flag_7 : 1;
> unsigned lang_flag_8 : 1;
>
> - /* In VAR_DECL and PARM_DECL, this is DECL_REGISTER. */
> + /* In VAR_DECL and PARM_DECL, this is DECL_REGISTER
> + IN TRANSLATION_UNIT_DECL, this is TRANSLATION_UNIT_WARN_EMPTY_P. */
> unsigned decl_flag_0 : 1;
> /* In FIELD_DECL, this is DECL_BIT_FIELD
> In VAR_DECL and FUNCTION_DECL, this is DECL_EXTERNAL.
> @@ -1619,7 +1621,7 @@ struct GTY(()) tree_decl_common {
> In VAR_DECL, PARM_DECL and RESULT_DECL, this is
> DECL_HAS_VALUE_EXPR_P. */
> unsigned decl_flag_2 : 1;
> - /* 1 bit unused. */
> + /* In FIELD_DECL, this is DECL_PADDING_P. */
> unsigned decl_flag_3 : 1;
> /* Logically, these two would go in a theoretical base shared by var and
> parm decl. */
> diff --git gcc/tree-streamer-in.c gcc/tree-streamer-in.c
> index baf0c5bf837..36402c6a39c 100644
> --- gcc/tree-streamer-in.c
> +++ gcc/tree-streamer-in.c
> @@ -251,6 +251,7 @@ unpack_ts_decl_common_value_fields (struct bitpack_d *bp, tree expr)
> {
> DECL_PACKED (expr) = (unsigned) bp_unpack_value (bp, 1);
> DECL_NONADDRESSABLE_P (expr) = (unsigned) bp_unpack_value (bp, 1);
> + DECL_PADDING_P (expr) = (unsigned) bp_unpack_value (bp, 1);
> expr->decl_common.off_align = bp_unpack_value (bp, 8);
> }
>
> @@ -381,6 +382,7 @@ 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_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..08c58a4709d 100644
> --- gcc/tree-streamer-out.c
> +++ gcc/tree-streamer-out.c
> @@ -211,6 +211,7 @@ pack_ts_decl_common_value_fields (struct bitpack_d *bp, tree expr)
> {
> bp_pack_value (bp, DECL_PACKED (expr), 1);
> bp_pack_value (bp, DECL_NONADDRESSABLE_P (expr), 1);
> + bp_pack_value (bp, DECL_PADDING_P (expr), 1);
> bp_pack_value (bp, expr->decl_common.off_align, 8);
> }
>
> @@ -330,6 +331,7 @@ 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_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..e400c45d484 100644
> --- gcc/tree.c
> +++ gcc/tree.c
> @@ -13811,6 +13811,63 @@ 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_PADDING_P (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..fcd015edbd2 100644
> --- gcc/tree.h
> +++ gcc/tree.h
> @@ -696,6 +696,14 @@ 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 in this TU. */
> +#define TRANSLATION_UNIT_WARN_EMPTY_P(NODE) \
> + (TRANSLATION_UNIT_DECL_CHECK (NODE)->decl_common.decl_flag_0)
> +
> +/* 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)
>
> @@ -2619,6 +2627,12 @@ extern void decl_value_expr_insert (tree, tree);
> #define DECL_NONADDRESSABLE_P(NODE) \
> (FIELD_DECL_CHECK (NODE)->decl_common.decl_flag_2)
>
> +/* Used in a FIELD_DECL to indicate that this field is padding for
> + the purpose of determining whether the record this field is a member
> + of is considered empty. */
> +#define DECL_PADDING_P(NODE) \
> + (FIELD_DECL_CHECK (NODE)->decl_common.decl_flag_3)
> +
> /* A numeric unique identifier for a LABEL_DECL. The UID allocation is
> dense, unique within any one function, and may be used to index arrays.
> If the value is -1, then no UID has been assigned. */
> @@ -5428,6 +5442,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
>
>
--
Richard Biener <rguenther@suse.de>
SUSE LINUX GmbH, GF: Felix Imendoerffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nuernberg)