aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarek Polacek <polacek@redhat.com>2017-11-22 16:06:18 +0000
committerMarek Polacek <mpolacek@gcc.gnu.org>2017-11-22 16:06:18 +0000
commit974aedcce8d3eb6d46493c1e597b75743cb4c3db (patch)
tree76a622fb22ebe257b9dfc386c92eb31cc07221bc
parentfc7160b2c718d7974aee97ca9b8448ec760bbfb7 (diff)
downloadgcc-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
-rw-r--r--gcc/ChangeLog58
-rw-r--r--gcc/c/ChangeLog7
-rw-r--r--gcc/c/c-decl.c5
-rw-r--r--gcc/calls.c35
-rw-r--r--gcc/common.opt2
-rw-r--r--gcc/config/i386/i386.c68
-rw-r--r--gcc/config/i386/i386.h2
-rw-r--r--gcc/cp/ChangeLog9
-rw-r--r--gcc/cp/class.c1
-rw-r--r--gcc/cp/decl.c9
-rw-r--r--gcc/doc/tm.texi10
-rw-r--r--gcc/doc/tm.texi.in4
-rw-r--r--gcc/dwarf2out.c15
-rw-r--r--gcc/explow.c2
-rw-r--r--gcc/expr.c2
-rw-r--r--gcc/function.c11
-rw-r--r--gcc/lto-streamer-out.c2
-rw-r--r--gcc/lto/ChangeLog7
-rw-r--r--gcc/lto/lto.c2
-rw-r--r--gcc/stor-layout.c3
-rw-r--r--gcc/target.def16
-rw-r--r--gcc/targhooks.c9
-rw-r--r--gcc/targhooks.h2
-rw-r--r--gcc/testsuite/ChangeLog61
-rw-r--r--gcc/testsuite/g++.dg/abi/empty12.C17
-rw-r--r--gcc/testsuite/g++.dg/abi/empty12.h9
-rw-r--r--gcc/testsuite/g++.dg/abi/empty12a.c6
-rw-r--r--gcc/testsuite/g++.dg/abi/empty13.C17
-rw-r--r--gcc/testsuite/g++.dg/abi/empty13.h9
-rw-r--r--gcc/testsuite/g++.dg/abi/empty13a.c6
-rw-r--r--gcc/testsuite/g++.dg/abi/empty14.C17
-rw-r--r--gcc/testsuite/g++.dg/abi/empty14.h10
-rw-r--r--gcc/testsuite/g++.dg/abi/empty14a.c6
-rw-r--r--gcc/testsuite/g++.dg/abi/empty15.C17
-rw-r--r--gcc/testsuite/g++.dg/abi/empty15.h30
-rw-r--r--gcc/testsuite/g++.dg/abi/empty15a.c6
-rw-r--r--gcc/testsuite/g++.dg/abi/empty16.C17
-rw-r--r--gcc/testsuite/g++.dg/abi/empty16.h16
-rw-r--r--gcc/testsuite/g++.dg/abi/empty16a.c6
-rw-r--r--gcc/testsuite/g++.dg/abi/empty17.C17
-rw-r--r--gcc/testsuite/g++.dg/abi/empty17.h27
-rw-r--r--gcc/testsuite/g++.dg/abi/empty17a.c6
-rw-r--r--gcc/testsuite/g++.dg/abi/empty18.C17
-rw-r--r--gcc/testsuite/g++.dg/abi/empty18.h9
-rw-r--r--gcc/testsuite/g++.dg/abi/empty18a.c6
-rw-r--r--gcc/testsuite/g++.dg/abi/empty19.C17
-rw-r--r--gcc/testsuite/g++.dg/abi/empty19.h10
-rw-r--r--gcc/testsuite/g++.dg/abi/empty19a.c6
-rw-r--r--gcc/testsuite/g++.dg/abi/empty20.C19
-rw-r--r--gcc/testsuite/g++.dg/abi/empty21.C23
-rw-r--r--gcc/testsuite/g++.dg/abi/empty22.C17
-rw-r--r--gcc/testsuite/g++.dg/abi/empty22.h27
-rw-r--r--gcc/testsuite/g++.dg/abi/empty22a.c6
-rw-r--r--gcc/testsuite/g++.dg/abi/empty23.C25
-rw-r--r--gcc/testsuite/g++.dg/abi/empty24.C25
-rw-r--r--gcc/testsuite/g++.dg/abi/empty25.C17
-rw-r--r--gcc/testsuite/g++.dg/abi/empty25.h18
-rw-r--r--gcc/testsuite/g++.dg/abi/empty25a.c6
-rw-r--r--gcc/testsuite/g++.dg/abi/empty26.C17
-rw-r--r--gcc/testsuite/g++.dg/abi/empty26.h27
-rw-r--r--gcc/testsuite/g++.dg/abi/empty26a.c6
-rw-r--r--gcc/testsuite/g++.dg/abi/empty27.C26
-rw-r--r--gcc/testsuite/g++.dg/abi/empty28.C28
-rw-r--r--gcc/testsuite/g++.dg/abi/pr60336-1.C17
-rw-r--r--gcc/testsuite/g++.dg/abi/pr60336-10.C50
-rw-r--r--gcc/testsuite/g++.dg/abi/pr60336-11.C56
-rw-r--r--gcc/testsuite/g++.dg/abi/pr60336-12.C57
-rw-r--r--gcc/testsuite/g++.dg/abi/pr60336-2.C48
-rw-r--r--gcc/testsuite/g++.dg/abi/pr60336-3.C15
-rw-r--r--gcc/testsuite/g++.dg/abi/pr60336-4.C48
-rw-r--r--gcc/testsuite/g++.dg/abi/pr60336-5.C17
-rw-r--r--gcc/testsuite/g++.dg/abi/pr60336-6.C17
-rw-r--r--gcc/testsuite/g++.dg/abi/pr60336-7.C17
-rw-r--r--gcc/testsuite/g++.dg/abi/pr60336-8.C15
-rw-r--r--gcc/testsuite/g++.dg/abi/pr60336-9.C28
-rw-r--r--gcc/testsuite/g++.dg/abi/pr68355.C24
-rw-r--r--gcc/testsuite/g++.dg/lto/pr60336_0.C47
-rw-r--r--gcc/tree-core.h8
-rw-r--r--gcc/tree-streamer-in.c2
-rw-r--r--gcc/tree-streamer-out.c2
-rw-r--r--gcc/tree.c71
-rw-r--r--gcc/tree.h19
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
diff --git a/gcc/expr.c b/gcc/expr.c
index 3341e94..487837a 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -2749,7 +2749,7 @@ copy_blkmode_to_reg (machine_mode mode_in, tree src)
x = expand_normal (src);
- bytes = int_size_in_bytes (TREE_TYPE (src));
+ bytes = arg_int_size_in_bytes (TREE_TYPE (src));
if (bytes == 0)
return NULL_RTX;
diff --git 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));
}
diff --git a/gcc/tree.c b/gcc/tree.c
index 72da683..7efd644 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -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.
diff --git a/gcc/tree.h b/gcc/tree.h
index 0ec092a..c2cabfc 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -696,6 +696,14 @@ extern void omp_clause_range_check_failed (const_tree, const char *, int,
emitted. */
#define TREE_NO_WARNING(NODE) ((NODE)->base.nowarning_flag)
+/* Nonzero if we should warn about the change in empty class parameter
+ passing ABI in this TU. */
+#define TRANSLATION_UNIT_WARN_EMPTY_P(NODE) \
+ (TRANSLATION_UNIT_DECL_CHECK (NODE)->decl_common.decl_flag_0)
+
+/* Nonzero if this type is "empty" according to the particular psABI. */
+#define TYPE_EMPTY_P(NODE) (TYPE_CHECK (NODE)->type_common.empty_flag)
+
/* Used to indicate that this TYPE represents a compiler-generated entity. */
#define TYPE_ARTIFICIAL(NODE) (TYPE_CHECK (NODE)->base.nowarning_flag)
@@ -2619,6 +2627,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);