diff options
author | Marek Polacek <polacek@redhat.com> | 2017-11-22 16:06:18 +0000 |
---|---|---|
committer | Marek Polacek <mpolacek@gcc.gnu.org> | 2017-11-22 16:06:18 +0000 |
commit | 974aedcce8d3eb6d46493c1e597b75743cb4c3db (patch) | |
tree | 76a622fb22ebe257b9dfc386c92eb31cc07221bc | |
parent | fc7160b2c718d7974aee97ca9b8448ec760bbfb7 (diff) | |
download | gcc-974aedcce8d3eb6d46493c1e597b75743cb4c3db.zip gcc-974aedcce8d3eb6d46493c1e597b75743cb4c3db.tar.gz gcc-974aedcce8d3eb6d46493c1e597b75743cb4c3db.tar.bz2 |
re PR c++/60336 (empty struct value is passed differently in C and C++)
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.
* common.opt: Update -fabi-version description.
* config/i386/i386.c (init_cumulative_args): Set cum->warn_empty.
(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.
* dwarf2out.c (get_ultimate_context): Move to tree.c.
* 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 (aggregate_value_p): Return 0 for empty types.
(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.
(get_ultimate_context): 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, get_ultimate_context): 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/empty27.C: New test.
* g++.dg/abi/empty28.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.
Co-Authored-By: H.J. Lu <hongjiu.lu@intel.com>
Co-Authored-By: Jason Merrill <jason@redhat.com>
From-SVN: r255066
82 files changed, 1453 insertions, 38 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 1538448..4b248cd 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,61 @@ +2017-11-22 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 + * 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_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. + * dwarf2out.c (get_ultimate_context): Move to tree.c. + * 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 (aggregate_value_p): Return 0 for empty types. + (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. + (get_ultimate_context): 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, get_ultimate_context): Declare. + 2017-11-22 Thomas Preud'homme <thomas.preudhomme@arm.com> * config/arm/arm.c (cmse_clear_registers): New function. diff --git a/gcc/c/ChangeLog b/gcc/c/ChangeLog index a4dc563..b936f99 100644 --- a/gcc/c/ChangeLog +++ b/gcc/c/ChangeLog @@ -1,3 +1,10 @@ +2017-11-22 Marek Polacek <polacek@redhat.com> + + PR c++/60336 + PR middle-end/67239 + PR target/68355 + * c-decl.c (grokdeclarator): Set DECL_PADDING_P on unnamed bit-fields. + 2017-11-21 David Malcolm <dmalcolm@redhat.com> PR c/83056 diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c index 9c3beab..c66bc40 100644 --- a/gcc/c/c-decl.c +++ b/gcc/c/c-decl.c @@ -6822,7 +6822,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 a/gcc/calls.c b/gcc/calls.c index 64f9f50..8ae9899 100644 --- a/gcc/calls.c +++ b/gcc/calls.c @@ -2002,6 +2002,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); @@ -5514,7 +5516,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. */ @@ -5546,10 +5552,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 @@ -5582,9 +5590,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); } @@ -5660,10 +5668,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. @@ -5741,6 +5751,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 a/gcc/common.opt b/gcc/common.opt index f8f2ed3..28a0185 100644 --- a/gcc/common.opt +++ b/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 a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c index b1ce630..202ef7a 100644 --- a/gcc/config/i386/i386.c +++ b/gcc/config/i386/i386.c @@ -7196,6 +7196,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 @@ -9883,7 +9903,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); @@ -28847,6 +28867,46 @@ 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; + + const_tree ctx = get_ultimate_context (cum->decl); + if (ctx != NULL_TREE + && !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. */ @@ -50574,6 +50634,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 a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h index 6eadc03..fde8467 100644 --- a/gcc/config/i386/i386.h +++ b/gcc/config/i386/i386.h @@ -1640,6 +1640,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 a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 42252bf..f7d3cfa 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,12 @@ +2017-11-22 Marek Polacek <polacek@redhat.com> + + PR c++/60336 + PR middle-end/67239 + PR target/68355 + * 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. + 2017-11-21 Martin Liska <mliska@suse.cz> * class.c (finalize_literal_type_property): Add quotes for diff --git a/gcc/cp/class.c b/gcc/cp/class.c index 529f37f..73529a9 100644 --- a/gcc/cp/class.c +++ b/gcc/cp/class.c @@ -6196,6 +6196,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 a/gcc/cp/decl.c b/gcc/cp/decl.c index a7cb615..121b4a4 100644 --- a/gcc/cp/decl.c +++ b/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); @@ -12090,7 +12094,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 a/gcc/doc/tm.texi b/gcc/doc/tm.texi index 72606f5..f16e73c 100644 --- a/gcc/doc/tm.texi +++ b/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 a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in index e7d4ada..39f6fca 100644 --- a/gcc/doc/tm.texi.in +++ b/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 a/gcc/dwarf2out.c b/gcc/dwarf2out.c index 8207dd5..4b0216e 100644 --- a/gcc/dwarf2out.c +++ b/gcc/dwarf2out.c @@ -5102,21 +5102,6 @@ get_AT_file (dw_die_ref die, enum dwarf_attribute attr_kind) return a ? AT_file (a) : NULL; } -/* Returns the ultimate TRANSLATION_UNIT_DECL context of DECL or NULL. */ - -static const_tree -get_ultimate_context (const_tree decl) -{ - while (decl && TREE_CODE (decl) != TRANSLATION_UNIT_DECL) - { - if (TREE_CODE (decl) == BLOCK) - decl = BLOCK_SUPERCONTEXT (decl); - else - decl = get_containing_scope (decl); - } - return decl; -} - /* Return TRUE if the language is C++. */ static inline bool diff --git a/gcc/explow.c b/gcc/explow.c index 96334b2..e2c8e45 100644 --- a/gcc/explow.c +++ b/gcc/explow.c @@ -2176,7 +2176,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 @@ -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 a/gcc/function.c b/gcc/function.c index fe3d9c1..1fa538b 100644 --- a/gcc/function.c +++ b/gcc/function.c @@ -2084,6 +2084,9 @@ aggregate_value_p (const_tree exp, const_tree fntype) if (TREE_ADDRESSABLE (type)) return 1; + if (TYPE_EMPTY_P (type)) + return 0; + if (flag_pcc_struct_return && AGGREGATE_TYPE_P (type)) return 1; @@ -2528,6 +2531,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 +4146,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 a/gcc/lto-streamer-out.c b/gcc/lto-streamer-out.c index 554f9cc..e127e50 100644 --- a/gcc/lto-streamer-out.c +++ b/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 a/gcc/lto/ChangeLog b/gcc/lto/ChangeLog index 4edb153..ed55a5f 100644 --- a/gcc/lto/ChangeLog +++ b/gcc/lto/ChangeLog @@ -1,3 +1,10 @@ +2017-11-22 Marek Polacek <polacek@redhat.com> + + PR c++/60336 + PR middle-end/67239 + PR target/68355 + * lto.c (compare_tree_sccs_1): Compare TYPE_EMPTY_P and DECL_PADDING_P. + 2017-11-10 Jan Hubicka <hubicka@ucw.cz> * lto-partition.c (lto_balanced_map): Use frequency accessor. diff --git a/gcc/lto/lto.c b/gcc/lto/lto.c index 63ba73c..748ef02 100644 --- a/gcc/lto/lto.c +++ b/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 a/gcc/stor-layout.c b/gcc/stor-layout.c index 0ce97a5..1eef578 100644 --- a/gcc/stor-layout.c +++ b/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 a/gcc/target.def b/gcc/target.def index 577dad8..81aedee 100644 --- a/gcc/target.def +++ b/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 a/gcc/targhooks.c b/gcc/targhooks.c index dad1e109..0edc57b 100644 --- a/gcc/targhooks.c +++ b/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 a/gcc/targhooks.h b/gcc/targhooks.h index 15bbf5c..e431934 100644 --- a/gcc/targhooks.h +++ b/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 a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index d557f4a..cb453e3 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,64 @@ +2017-11-22 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 + * 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/empty27.C: New test. + * g++.dg/abi/empty28.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. + 2017-11-22 Thomas Preud'homme <thomas.preudhomme@arm.com> * gcc.target/arm/cmse/mainline/hard-sp/cmse-13.c: Adapt expectations diff --git a/gcc/testsuite/g++.dg/abi/empty12.C b/gcc/testsuite/g++.dg/abi/empty12.C new file mode 100644 index 0000000..20d85ff --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/abi/empty12.h b/gcc/testsuite/g++.dg/abi/empty12.h new file mode 100644 index 0000000..c61afcd --- /dev/null +++ b/gcc/testsuite/g++.dg/abi/empty12.h @@ -0,0 +1,9 @@ +struct dummy { }; +struct foo +{ + int i1; + int i2; + int i3; + int i4; + int i5; +}; diff --git a/gcc/testsuite/g++.dg/abi/empty12a.c b/gcc/testsuite/g++.dg/abi/empty12a.c new file mode 100644 index 0000000..34a25ba --- /dev/null +++ b/gcc/testsuite/g++.dg/abi/empty12a.c @@ -0,0 +1,6 @@ +#include "empty12.h" +void fun(struct dummy d, struct foo f) +{ + if (f.i1 != -1) + __builtin_abort(); +} diff --git a/gcc/testsuite/g++.dg/abi/empty13.C b/gcc/testsuite/g++.dg/abi/empty13.C new file mode 100644 index 0000000..0cb9a37 --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/abi/empty13.h b/gcc/testsuite/g++.dg/abi/empty13.h new file mode 100644 index 0000000..c61afcd --- /dev/null +++ b/gcc/testsuite/g++.dg/abi/empty13.h @@ -0,0 +1,9 @@ +struct dummy { }; +struct foo +{ + int i1; + int i2; + int i3; + int i4; + int i5; +}; diff --git a/gcc/testsuite/g++.dg/abi/empty13a.c b/gcc/testsuite/g++.dg/abi/empty13a.c new file mode 100644 index 0000000..b4303a6 --- /dev/null +++ b/gcc/testsuite/g++.dg/abi/empty13a.c @@ -0,0 +1,6 @@ +#include "empty13.h" +void fun(struct dummy d, struct foo f) +{ + if (f.i1 == -1) + __builtin_abort(); +} diff --git a/gcc/testsuite/g++.dg/abi/empty14.C b/gcc/testsuite/g++.dg/abi/empty14.C new file mode 100644 index 0000000..2868d8a --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/abi/empty14.h b/gcc/testsuite/g++.dg/abi/empty14.h new file mode 100644 index 0000000..5842279 --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/abi/empty14a.c b/gcc/testsuite/g++.dg/abi/empty14a.c new file mode 100644 index 0000000..8b3d780 --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/abi/empty15.C b/gcc/testsuite/g++.dg/abi/empty15.C new file mode 100644 index 0000000..12385f7 --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/abi/empty15.h b/gcc/testsuite/g++.dg/abi/empty15.h new file mode 100644 index 0000000..1c6f26f --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/abi/empty15a.c b/gcc/testsuite/g++.dg/abi/empty15a.c new file mode 100644 index 0000000..325b2c5 --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/abi/empty16.C b/gcc/testsuite/g++.dg/abi/empty16.C new file mode 100644 index 0000000..1ca52f9 --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/abi/empty16.h b/gcc/testsuite/g++.dg/abi/empty16.h new file mode 100644 index 0000000..7552ae0 --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/abi/empty16a.c b/gcc/testsuite/g++.dg/abi/empty16a.c new file mode 100644 index 0000000..6cb7fbc --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/abi/empty17.C b/gcc/testsuite/g++.dg/abi/empty17.C new file mode 100644 index 0000000..d386e54 --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/abi/empty17.h b/gcc/testsuite/g++.dg/abi/empty17.h new file mode 100644 index 0000000..9cf72ba --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/abi/empty17a.c b/gcc/testsuite/g++.dg/abi/empty17a.c new file mode 100644 index 0000000..24408fd --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/abi/empty18.C b/gcc/testsuite/g++.dg/abi/empty18.C new file mode 100644 index 0000000..be69c6a --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/abi/empty18.h b/gcc/testsuite/g++.dg/abi/empty18.h new file mode 100644 index 0000000..86e7ecd --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/abi/empty18a.c b/gcc/testsuite/g++.dg/abi/empty18a.c new file mode 100644 index 0000000..902860b --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/abi/empty19.C b/gcc/testsuite/g++.dg/abi/empty19.C new file mode 100644 index 0000000..84f5b75 --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/abi/empty19.h b/gcc/testsuite/g++.dg/abi/empty19.h new file mode 100644 index 0000000..616b87b --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/abi/empty19a.c b/gcc/testsuite/g++.dg/abi/empty19a.c new file mode 100644 index 0000000..767b1eb --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/abi/empty20.C b/gcc/testsuite/g++.dg/abi/empty20.C new file mode 100644 index 0000000..5022033 --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/abi/empty21.C b/gcc/testsuite/g++.dg/abi/empty21.C new file mode 100644 index 0000000..3b2e3b8 --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/abi/empty22.C b/gcc/testsuite/g++.dg/abi/empty22.C new file mode 100644 index 0000000..f4f4a02 --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/abi/empty22.h b/gcc/testsuite/g++.dg/abi/empty22.h new file mode 100644 index 0000000..8d54dc7 --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/abi/empty22a.c b/gcc/testsuite/g++.dg/abi/empty22a.c new file mode 100644 index 0000000..7606c52 --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/abi/empty23.C b/gcc/testsuite/g++.dg/abi/empty23.C new file mode 100644 index 0000000..dbeda81 --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/abi/empty24.C b/gcc/testsuite/g++.dg/abi/empty24.C new file mode 100644 index 0000000..822ced1 --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/abi/empty25.C b/gcc/testsuite/g++.dg/abi/empty25.C new file mode 100644 index 0000000..da6ef51 --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/abi/empty25.h b/gcc/testsuite/g++.dg/abi/empty25.h new file mode 100644 index 0000000..2f22fd5 --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/abi/empty25a.c b/gcc/testsuite/g++.dg/abi/empty25a.c new file mode 100644 index 0000000..8c16e45 --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/abi/empty26.C b/gcc/testsuite/g++.dg/abi/empty26.C new file mode 100644 index 0000000..ab2f54d --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/abi/empty26.h b/gcc/testsuite/g++.dg/abi/empty26.h new file mode 100644 index 0000000..8d54dc7 --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/abi/empty26a.c b/gcc/testsuite/g++.dg/abi/empty26a.c new file mode 100644 index 0000000..bc0ae47 --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/abi/empty27.C b/gcc/testsuite/g++.dg/abi/empty27.C new file mode 100644 index 0000000..5d14e7c --- /dev/null +++ b/gcc/testsuite/g++.dg/abi/empty27.C @@ -0,0 +1,26 @@ +// PR c++/60336 +// { dg-do compile } +// { dg-options "-Wabi=12" } + +struct foo +{ + int i1; + int i2; + int i3; + int i4; + int i5; +}; + +namespace N { + class E { }; + void fun (class E, struct foo); +} + +int main() +{ + N::E d; + struct foo f = { -1, -2, -3, -4, -5 }; + + N::fun(d, f); // { dg-bogus "empty" } + return 0; +} diff --git a/gcc/testsuite/g++.dg/abi/empty28.C b/gcc/testsuite/g++.dg/abi/empty28.C new file mode 100644 index 0000000..7e0765d --- /dev/null +++ b/gcc/testsuite/g++.dg/abi/empty28.C @@ -0,0 +1,28 @@ +// PR c++/60336 +// { dg-do compile } +// { dg-options "-Wabi=12" } + +struct foo +{ + int i1; + int i2; + int i3; + int i4; + int i5; +}; + +struct N { + class E { }; + void fun (class E, struct foo) { } // { dg-bogus "empty" } +}; + + +int main() +{ + struct N n; + N::E d; + struct foo f = { -1, -2, -3, -4, -5 }; + + n.fun(d, f); // { dg-bogus "empty" } + return 0; +} diff --git a/gcc/testsuite/g++.dg/abi/pr60336-1.C b/gcc/testsuite/g++.dg/abi/pr60336-1.C new file mode 100644 index 0000000..5944789 --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/abi/pr60336-10.C b/gcc/testsuite/g++.dg/abi/pr60336-10.C new file mode 100644 index 0000000..960cc23 --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/abi/pr60336-11.C b/gcc/testsuite/g++.dg/abi/pr60336-11.C new file mode 100644 index 0000000..14cd6d0 --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/abi/pr60336-12.C b/gcc/testsuite/g++.dg/abi/pr60336-12.C new file mode 100644 index 0000000..0991754 --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/abi/pr60336-2.C b/gcc/testsuite/g++.dg/abi/pr60336-2.C new file mode 100644 index 0000000..1c6c3eb --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/abi/pr60336-3.C b/gcc/testsuite/g++.dg/abi/pr60336-3.C new file mode 100644 index 0000000..4157e55 --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/abi/pr60336-4.C b/gcc/testsuite/g++.dg/abi/pr60336-4.C new file mode 100644 index 0000000..266f67a --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/abi/pr60336-5.C b/gcc/testsuite/g++.dg/abi/pr60336-5.C new file mode 100644 index 0000000..fe83875 --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/abi/pr60336-6.C b/gcc/testsuite/g++.dg/abi/pr60336-6.C new file mode 100644 index 0000000..6e08c8f --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/abi/pr60336-7.C b/gcc/testsuite/g++.dg/abi/pr60336-7.C new file mode 100644 index 0000000..3b8b8ba --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/abi/pr60336-8.C b/gcc/testsuite/g++.dg/abi/pr60336-8.C new file mode 100644 index 0000000..a1ffb64 --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/abi/pr60336-9.C b/gcc/testsuite/g++.dg/abi/pr60336-9.C new file mode 100644 index 0000000..393f02b --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/abi/pr68355.C b/gcc/testsuite/g++.dg/abi/pr68355.C new file mode 100644 index 0000000..1354fc4 --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/lto/pr60336_0.C b/gcc/testsuite/g++.dg/lto/pr60336_0.C new file mode 100644 index 0000000..a0a598c --- /dev/null +++ b/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 a/gcc/tree-core.h b/gcc/tree-core.h index aa54221..4f9c6c7 100644 --- a/gcc/tree-core.h +++ b/gcc/tree-core.h @@ -1532,7 +1532,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; @@ -1610,7 +1611,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. @@ -1620,7 +1622,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 a/gcc/tree-streamer-in.c b/gcc/tree-streamer-in.c index baf0c5b..36402c6 100644 --- a/gcc/tree-streamer-in.c +++ b/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 a/gcc/tree-streamer-out.c b/gcc/tree-streamer-out.c index 7f52d45..08c58a4 100644 --- a/gcc/tree-streamer-out.c +++ b/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)); } @@ -8712,6 +8712,21 @@ get_containing_scope (const_tree t) return (TYPE_P (t) ? TYPE_CONTEXT (t) : DECL_CONTEXT (t)); } +/* Returns the ultimate TRANSLATION_UNIT_DECL context of DECL or NULL. */ + +const_tree +get_ultimate_context (const_tree decl) +{ + while (decl && TREE_CODE (decl) != TRANSLATION_UNIT_DECL) + { + if (TREE_CODE (decl) == BLOCK) + decl = BLOCK_SUPERCONTEXT (decl); + else + decl = get_containing_scope (decl); + } + return decl; +} + /* Return the innermost context enclosing DECL that is a FUNCTION_DECL, or zero if none. */ @@ -13822,6 +13837,62 @@ 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; + + if (TREE_ADDRESSABLE (type)) + return false; + + 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. @@ -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,10 @@ 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. */ +#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. */ @@ -4515,6 +4527,10 @@ storage_order_barrier_p (const_tree t) extern tree get_containing_scope (const_tree); +/* Returns the ultimate TRANSLATION_UNIT_DECL context of DECL or NULL. */ + +extern const_tree get_ultimate_context (const_tree); + /* Return the FUNCTION_DECL which provides this _DECL with its context, or zero if none. */ extern tree decl_function_context (const_tree); @@ -5438,6 +5454,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); |