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)