From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 89087 invoked by alias); 27 Oct 2017 10:01:15 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Received: (qmail 89076 invoked by uid 89); 27 Oct 2017 10:01:15 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-24.4 required=5.0 tests=BAYES_00,GIT_PATCH_0,GIT_PATCH_1,GIT_PATCH_2,GIT_PATCH_3,KAM_LAZY_DOMAIN_SECURITY,RP_MATCHES_RCVD,SPF_HELO_PASS,URIBL_BLACK autolearn=ham version=3.3.2 spammy=cum X-HELO: mx1.redhat.com Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Fri, 27 Oct 2017 10:01:05 +0000 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id AFF3BC056789; Fri, 27 Oct 2017 10:01:01 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com AFF3BC056789 Authentication-Results: ext-mx08.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx08.extmail.prod.ext.phx2.redhat.com; spf=fail smtp.mailfrom=polacek@redhat.com Received: from redhat.com (unknown [10.40.205.20]) by smtp.corp.redhat.com (Postfix) with ESMTPS id CB2F960BEB; Fri, 27 Oct 2017 10:00:59 +0000 (UTC) Date: Fri, 27 Oct 2017 10:17:00 -0000 From: Marek Polacek To: GCC Patches Cc: Jason Merrill , Richard Biener , Joseph Myers , "H.J. Lu" Subject: Adjust empty class parameter passing ABI (PR c++/60336) Message-ID: <20171027100056.GE12792@redhat.com> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline User-Agent: Mutt/1.9.1 (2017-09-22) X-SW-Source: 2017-10/txt/msg02036.txt.bz2 This is my attempt at the empty class ABI change. To recap quickly, the C++ compiler has used a different calling convention for passing empty classes, because C++ says they have size 1, while the GCC C extension gives them size 0. But this difference doesn't mean that they need to be passed differently; in either case there's no actual data involved. I've made use of all the previous patches: but this approach uses two target hooks which check whether a type is an empty type according to the x86_64 psABI, and the second implements the warning for it. It also uses a lang hook to determine whether to print a -Wabi warning. The new passing can be turned back into the old passing using -fabi-version=11. So I had to use the new langhook, otherwise I wouldn't be able make it dependent on the C++ ABI verison. Some earlier comments from Jason: > I'm still uneasy about how much this requires generic code to think > about empty types specifically. I'd prefer to encapsulate this as > much as possible. Rather than places saying (empty ? 0 : > int_size_in_bytes), I figured that would all be hidden in the target > code, along with the warning. Places where you currently emit a > warning from generic code ought to come from a target hook, either an > existing one or a new one called something like > warn_parameter_passing_abi. I hope I've improved this now. I've introduced two new wrappers, maybe_empty_type_size, and int_maybe_empty_type_size. I've moved the warning to its own target hook with a new field in CUMULATIVE_ARGS; that seems to work well. > Note that nothing in gcc/ currently refers to warn_abi or warn_psabi, > which are both c-common flags; some targets refer to warn_psabi. True. > > It also uses a lang hook to determine > > whether to print a -Wabi warning. The new passing can be turned back into > > the old passing using -fabi-version=11. So I had to use a new langhook, > > otherwise I couldn't make it dependent on the C++ ABI verison. > > Hmm, that's unfortunate. It is, but the possibility of using -fabi-version=11 to revert to the old behavior seems useful. > > The users will get the new empty records passing even with -m32. > > I thought we only wanted to make the change for -m64; -m32 is a legacy > ABI at this point, it doesn't seem advisable to change it. Sure thing, done (using the TARGET_64BIT check in ix86_is_empty_record_p). > > One thing I wasn't sure about is what to do with a struct that contains > > a zero-length array. We should consider it an empty type, but my > > is_empty_record_p won't do that, the reason is that there doesn't seem > > to be any change regarding passing: older GCCs wouldn't pass such a struct > > via stack. So there's no warning for that, either. > > Not warning for that makes sense. Doing that by making > is_empty_record_p give the wrong answer seems unwise. is_empty_record_p should now give the correct answer. As I said yesterday, I think we shouldn't consider struct S { struct { } a; int b[0]; } as empty, because [0] is a GNU extension and struct S { struct { } a; int b[]; } is considered non-empty. Also, for the record, the struct with the flexible array member [0] broke struct-layout when considered empty. Thus the uglyfying of is_empty_record_p. empty23.C and empty24.C test that. > Also, you need to handled unnamed bit-fields, as they aren't data members. Here's another hiccup: e.g. struct S { unsigned : 15; }; are considered empty, but they were considered empty even before my change (so we probably shouldn't warn), with "unsigned : 0;" it's different and there seems to be a change in how we pass that. So I kept the warning. Not sure how important this is. Any other concerns? Bootstrapped/regtested on x86_64-linux, ppc64-linux, and aarch64-linux. 2017-10-27 Marek Polacek H.J. Lu Jason Merrill 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_function_arg_advance): Skip empty records. (ix86_return_in_memory): Return false for empty types. (ix86_gimplify_va_arg): Call int_maybe_empty_type_size instead of int_size_in_bytes. (ix86_is_empty_record_p): New function. (ix86_warn_parameter_passing_abi): New function. (TARGET_EMPTY_RECORD_P): Redefine. (TARGET_WARN_PARAMETER_PASSING_ABI): Redefine. * config/i386/i386.h (CUMULATIVE_ARGS): Add warn_empty. * doc/tm.texi: Regenerated. * doc/tm.texi.in (TARGET_EMPTY_RECORD_P, TARGET_WARN_PARAMETER_PASSING_ABI): Add. * explow.c (hard_function_value): Call int_maybe_empty_type_size instead of int_size_in_bytes. * expr.c (copy_blkmode_to_reg): Likewise. * function.c (assign_parm_find_entry_rtl): Call warn_parameter_passing_abi target hook. (locate_and_pad_parm): Call maybe_empty_type_size instead size_in_bytes. * langhooks-def.h (LANG_HOOKS_WARN_EMPTY_CLASS_ABI): Define. * langhooks.h (struct lang_hooks): Add warn_empty_class_abi. * target.def (empty_record_p, warn_parameter_passing_abi): New target hook. * targhooks.c (hook_void_CUMULATIVE_ARGS_tree): New hook. (std_gimplify_va_arg_expr): Skip empty records. Call maybe_empty_type_size instead size_in_bytes. * targhooks.h (hook_void_CUMULATIVE_ARGS_tree): Declare. * tree.c (array_type_nelts_top): New function. (is_empty_type): New function. (is_empty_record_p): New function. (int_maybe_empty_type_size): New function. (maybe_empty_type_size): New function. * tree.h (array_type_nelts_top, is_empty_record_p, int_maybe_empty_type_size, maybe_empty_type_size): Declare. * cp-lang.c: Include "flags.h". (LANG_HOOKS_WARN_EMPTY_CLASS_ABI): Redefine. (cxx_warn_empty_class_abi): New function. * cp-tree.h (array_type_nelts_top): Remove declaration. * tree.c (array_type_nelts_top): Move to tree.c. * testsuite/g++.dg/abi/empty12.C: New test. * testsuite/g++.dg/abi/empty12.h: New test. * testsuite/g++.dg/abi/empty12a.c: New test. * testsuite/g++.dg/abi/empty13.C: New test. * testsuite/g++.dg/abi/empty13.h: New test. * testsuite/g++.dg/abi/empty13a.c: New test. * testsuite/g++.dg/abi/empty14.C: New test. * testsuite/g++.dg/abi/empty14.h: New test. * testsuite/g++.dg/abi/empty14a.c: New test. * testsuite/g++.dg/abi/empty15.C: New test. * testsuite/g++.dg/abi/empty15.h: New test. * testsuite/g++.dg/abi/empty15a.c: New test. * testsuite/g++.dg/abi/empty16.C: New test. * testsuite/g++.dg/abi/empty16.h: New test. * testsuite/g++.dg/abi/empty16a.c: New test. * testsuite/g++.dg/abi/empty17.C: New test. * testsuite/g++.dg/abi/empty17.h: New test. * testsuite/g++.dg/abi/empty17a.c: New test. * testsuite/g++.dg/abi/empty18.C: New test. * testsuite/g++.dg/abi/empty18.h: New test. * testsuite/g++.dg/abi/empty18a.c: New test. * testsuite/g++.dg/abi/empty19.C: New test. * testsuite/g++.dg/abi/empty19.h: New test. * testsuite/g++.dg/abi/empty19a.c: New test. * testsuite/g++.dg/abi/empty20.C: New test. * testsuite/g++.dg/abi/empty21.C: New test. * testsuite/g++.dg/abi/empty22.C: New test. * testsuite/g++.dg/abi/empty22.h: New test. * testsuite/g++.dg/abi/empty22a.c: New test. * testsuite/g++.dg/abi/empty23.C: New test. * testsuite/g++.dg/abi/empty24.C: New test. * testsuite/g++.dg/abi/pr60336-1.C: New test. * testsuite/g++.dg/abi/pr60336-10.C: New test. * testsuite/g++.dg/abi/pr60336-11.C: New test. * testsuite/g++.dg/abi/pr60336-12.C: New test. * testsuite/g++.dg/abi/pr60336-2.C: New test. * testsuite/g++.dg/abi/pr60336-3.C: New test. * testsuite/g++.dg/abi/pr60336-4.C: New test. * testsuite/g++.dg/abi/pr60336-5.C: New test. * testsuite/g++.dg/abi/pr60336-6.C: New test. * testsuite/g++.dg/abi/pr60336-7.C: New test. * testsuite/g++.dg/abi/pr60336-8.C: New test. * testsuite/g++.dg/abi/pr60336-9.C: New test. * testsuite/g++.dg/abi/pr68355.C: New test. diff --git gcc/calls.c gcc/calls.c index 3730f43c7a9..29f547942d8 100644 --- gcc/calls.c +++ gcc/calls.c @@ -1850,6 +1850,8 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED, args[i].unsignedp = unsignedp; args[i].mode = mode; + targetm.calls.warn_parameter_passing_abi (args_so_far, type); + args[i].reg = targetm.calls.function_arg (args_so_far, mode, type, argpos < n_named_args); @@ -5358,7 +5360,11 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags, Note that in C the default argument promotions will prevent such mismatches. */ - size = GET_MODE_SIZE (arg->mode); + if (targetm.calls.empty_record_p (TREE_TYPE (pval))) + size = 0; + else + size = GET_MODE_SIZE (arg->mode); + /* Compute how much space the push instruction will push. On many machines, pushing a byte will advance the stack pointer by a halfword. */ @@ -5390,10 +5396,12 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags, /* This isn't already where we want it on the stack, so put it there. This can either be done with push or copy insns. */ - if (!emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), NULL_RTX, - parm_align, partial, reg, used - size, argblock, - ARGS_SIZE_RTX (arg->locate.offset), reg_parm_stack_space, - ARGS_SIZE_RTX (arg->locate.alignment_pad), true)) + if (used + && !emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), + NULL_RTX, parm_align, partial, reg, used - size, + argblock, ARGS_SIZE_RTX (arg->locate.offset), + reg_parm_stack_space, + ARGS_SIZE_RTX (arg->locate.alignment_pad), true)) sibcall_failure = 1; /* Unless this is a partially-in-register argument, the argument is now @@ -5426,9 +5434,9 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags, /* PUSH_ROUNDING has no effect on us, because emit_push_insn for BLKmode is careful to avoid it. */ excess = (arg->locate.size.constant - - int_size_in_bytes (TREE_TYPE (pval)) + - int_maybe_empty_type_size (TREE_TYPE (pval)) + partial); - size_rtx = expand_expr (size_in_bytes (TREE_TYPE (pval)), + size_rtx = expand_expr (maybe_empty_type_size (TREE_TYPE (pval)), NULL_RTX, TYPE_MODE (sizetype), EXPAND_NORMAL); } @@ -5504,10 +5512,12 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags, } } - emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), size_rtx, - parm_align, partial, reg, excess, argblock, - ARGS_SIZE_RTX (arg->locate.offset), reg_parm_stack_space, - ARGS_SIZE_RTX (arg->locate.alignment_pad), false); + if (!CONST_INT_P (size_rtx) || INTVAL (size_rtx) != 0) + emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), size_rtx, + parm_align, partial, reg, excess, argblock, + ARGS_SIZE_RTX (arg->locate.offset), + reg_parm_stack_space, + ARGS_SIZE_RTX (arg->locate.alignment_pad), false); /* Unless this is a partially-in-register argument, the argument is now in the stack. @@ -5585,6 +5595,9 @@ must_pass_in_stack_var_size_or_pad (machine_mode mode, const_tree type) if (TREE_ADDRESSABLE (type)) return true; + if (targetm.calls.empty_record_p (type)) + return false; + /* If the padding and mode of the type is such that a copy into a register would put it into the wrong part of the register. */ if (mode == BLKmode diff --git gcc/common.opt gcc/common.opt index 836f05b95a2..41d97ae7475 100644 --- gcc/common.opt +++ gcc/common.opt @@ -932,7 +932,7 @@ Driver Undocumented ; Default in G++ 7. ; ; 12: Corrects the calling convention for classes with only deleted copy/move -; constructors. +; constructors and changes passing/returning of empty records. ; Default in G++ 8. ; ; Additional positive integers will be assigned as new versions of diff --git gcc/config/i386/i386.c gcc/config/i386/i386.c index 1facf121987..c7058ac5638 100644 --- gcc/config/i386/i386.c +++ gcc/config/i386/i386.c @@ -7186,6 +7186,26 @@ init_cumulative_args (CUMULATIVE_ARGS *cum, /* Argument info to initialize */ cum->force_bnd_pass = 0; cum->decl = fndecl; + cum->warn_empty = !warn_abi || cum->stdarg; + if (!cum->warn_empty && fntype) + { + function_args_iterator iter; + tree argtype; + bool seen_empty_type = false; + FOREACH_FUNCTION_ARGS (fntype, argtype, iter) + { + if (VOID_TYPE_P (argtype)) + break; + if (targetm.calls.empty_record_p (argtype)) + seen_empty_type = true; + else if (seen_empty_type) + { + cum->warn_empty = true; + break; + } + } + } + if (!TARGET_64BIT) { /* If there are variable arguments, then we won't pass anything @@ -8327,6 +8347,10 @@ ix86_function_arg_advance (cumulative_args_t cum_v, machine_mode mode, if (!cum->caller && cfun->machine->func_type != TYPE_NORMAL) return; + /* Skip empty records because they won't be passed. */ + if (type && targetm.calls.empty_record_p (type)) + return; + if (mode == BLKmode) bytes = int_size_in_bytes (type); else @@ -9293,6 +9317,10 @@ ix86_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED) if (POINTER_BOUNDS_TYPE_P (type)) return false; + /* Empty records are never passed in memory. */ + if (type && targetm.calls.empty_record_p (type)) + return false; + if (TARGET_64BIT) { if (ix86_function_type_abi (fntype) == MS_ABI) @@ -9873,7 +9901,7 @@ ix86_gimplify_va_arg (tree valist, tree type, gimple_seq *pre_p, indirect_p = pass_by_reference (NULL, TYPE_MODE (type), type, false); if (indirect_p) type = build_pointer_type (type); - size = int_size_in_bytes (type); + size = int_maybe_empty_type_size (type); rsize = CEIL (size, UNITS_PER_WORD); nat_mode = type_natural_mode (type, NULL, false); @@ -28772,6 +28800,44 @@ ix86_constant_alignment (const_tree exp, HOST_WIDE_INT align) return align; } +/* Implement TARGET_EMPTY_RECORD_P. */ + +static bool +ix86_is_empty_record_p (const_tree type) +{ + if (!TARGET_64BIT) + return false; + return is_empty_record_p (type); +} + +/* Implement TARGET_WARN_PARAMETER_PASSING_ABI. */ + +static void +ix86_warn_parameter_passing_abi (cumulative_args_t cum_v, tree type) +{ + CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v); + + if (!cum->warn_empty) + return; + + if (!targetm.calls.empty_record_p (type)) + return; + + if (!lang_hooks.warn_empty_class_abi ()) + 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. */ @@ -50311,6 +50377,12 @@ ix86_run_selftests (void) #undef TARGET_CONSTANT_ALIGNMENT #define TARGET_CONSTANT_ALIGNMENT ix86_constant_alignment +#undef TARGET_EMPTY_RECORD_P +#define TARGET_EMPTY_RECORD_P ix86_is_empty_record_p + +#undef TARGET_WARN_PARAMETER_PASSING_ABI +#define TARGET_WARN_PARAMETER_PASSING_ABI ix86_warn_parameter_passing_abi + #if CHECKING_P #undef TARGET_RUN_TARGET_SELFTESTS #define TARGET_RUN_TARGET_SELFTESTS selftest::ix86_run_selftests diff --git gcc/config/i386/i386.h gcc/config/i386/i386.h index 837906b5169..7f5d245b568 100644 --- gcc/config/i386/i386.h +++ gcc/config/i386/i386.h @@ -1633,6 +1633,8 @@ typedef struct ix86_args { int warn_avx; /* True when we want to warn about AVX ABI. */ int warn_sse; /* True when we want to warn about SSE ABI. */ int warn_mmx; /* True when we want to warn about MMX ABI. */ + int warn_empty; /* True when we want to warn about empty classes + passing ABI change. */ int sse_regno; /* next available sse register number */ int mmx_words; /* # mmx words passed so far */ int mmx_nregs; /* # mmx registers available for passing */ diff --git gcc/cp/cp-lang.c gcc/cp/cp-lang.c index 805319a4185..78336730a94 100644 --- gcc/cp/cp-lang.c +++ gcc/cp/cp-lang.c @@ -26,6 +26,7 @@ along with GCC; see the file COPYING3. If not see #include "langhooks.h" #include "langhooks-def.h" #include "cp-objcp-common.h" +#include "flags.h" enum c_language_kind c_language = clk_cxx; static void cp_init_ts (void); @@ -35,6 +36,7 @@ static tree cp_eh_personality (void); static tree get_template_innermost_arguments_folded (const_tree); static tree get_template_argument_pack_elems_folded (const_tree); static tree cxx_enum_underlying_base_type (const_tree); +static bool cxx_warn_empty_class_abi (void); /* Lang hooks common to C++ and ObjC++ are declared in cp/cp-objcp-common.h; consequently, there should be very few hooks below. */ @@ -78,6 +80,8 @@ static tree cxx_enum_underlying_base_type (const_tree); #define LANG_HOOKS_EH_RUNTIME_TYPE build_eh_type_type #undef LANG_HOOKS_ENUM_UNDERLYING_BASE_TYPE #define LANG_HOOKS_ENUM_UNDERLYING_BASE_TYPE cxx_enum_underlying_base_type +#undef LANG_HOOKS_WARN_EMPTY_CLASS_ABI +#define LANG_HOOKS_WARN_EMPTY_CLASS_ABI cxx_warn_empty_class_abi #if CHECKING_P #undef LANG_HOOKS_RUN_LANG_SELFTESTS @@ -234,6 +238,15 @@ tree cxx_enum_underlying_base_type (const_tree type) return underlying_type; } +/* Return true if we should warn about the change in empty class parameter + passing ABI. */ + +static bool +cxx_warn_empty_class_abi (void) +{ + return warn_abi && abi_version_crosses (12); +} + #if CHECKING_P namespace selftest { diff --git gcc/cp/cp-tree.h gcc/cp/cp-tree.h index f2570b00386..45f5d82be03 100644 --- gcc/cp/cp-tree.h +++ gcc/cp/cp-tree.h @@ -6916,7 +6916,6 @@ extern tree canonical_eh_spec (tree); extern tree build_exception_variant (tree, tree); extern tree bind_template_template_parm (tree, tree); extern tree array_type_nelts_total (tree); -extern tree array_type_nelts_top (tree); extern tree break_out_target_exprs (tree); extern tree build_ctor_subob_ref (tree, tree, tree); extern tree replace_placeholders (tree, tree, bool * = NULL); diff --git gcc/cp/tree.c gcc/cp/tree.c index 48d40945af3..8a878d70e34 100644 --- gcc/cp/tree.c +++ gcc/cp/tree.c @@ -2834,19 +2834,6 @@ cxx_print_statistics (void) } /* Return, as an INTEGER_CST node, the number of elements for TYPE - (which is an ARRAY_TYPE). This counts only elements of the top - array. */ - -tree -array_type_nelts_top (tree type) -{ - return fold_build2_loc (input_location, - PLUS_EXPR, sizetype, - array_type_nelts (type), - size_one_node); -} - -/* Return, as an INTEGER_CST node, the number of elements for TYPE (which is an ARRAY_TYPE). This one is a recursive count of all ARRAY_TYPEs that are clumped together. */ diff --git gcc/doc/tm.texi gcc/doc/tm.texi index c02f4d35116..9c4405a4cb5 100644 --- gcc/doc/tm.texi +++ gcc/doc/tm.texi @@ -4548,6 +4548,16 @@ This target hook returns the mode to be used when accessing raw return registers This target hook returns the mode to be used when accessing raw argument registers in @code{__builtin_apply_args}. Define this macro if the value in @var{reg_raw_mode} is not correct. @end deftypefn +@deftypefn {Target Hook} bool TARGET_EMPTY_RECORD_P (const_tree @var{type}) +This target hook returns true if the type is an empty record. The default +is to return @code{false}. +@end deftypefn + +@deftypefn {Target Hook} void TARGET_WARN_PARAMETER_PASSING_ABI (cumulative_args_t @var{ca}, tree @var{type}) +This target hook warns about the change in empty class parameter passing +ABI. +@end deftypefn + @node Caller Saves @subsection Caller-Saves Register Allocation diff --git gcc/doc/tm.texi.in gcc/doc/tm.texi.in index 37308e1e551..8b6b64f542f 100644 --- gcc/doc/tm.texi.in +++ gcc/doc/tm.texi.in @@ -3437,6 +3437,10 @@ nothing when you use @option{-freg-struct-return} mode. @hook TARGET_GET_RAW_ARG_MODE +@hook TARGET_EMPTY_RECORD_P + +@hook TARGET_WARN_PARAMETER_PASSING_ABI + @node Caller Saves @subsection Caller-Saves Register Allocation diff --git gcc/explow.c gcc/explow.c index 662865d2808..5c864fedd1b 100644 --- gcc/explow.c +++ gcc/explow.c @@ -2166,7 +2166,7 @@ hard_function_value (const_tree valtype, const_tree func, const_tree fntype, if (REG_P (val) && GET_MODE (val) == BLKmode) { - unsigned HOST_WIDE_INT bytes = int_size_in_bytes (valtype); + unsigned HOST_WIDE_INT bytes = int_maybe_empty_type_size (valtype); opt_scalar_int_mode tmpmode; /* int_size_in_bytes can return -1. We don't need a check here diff --git gcc/expr.c gcc/expr.c index 496d492c9fa..337bd6de579 100644 --- gcc/expr.c +++ gcc/expr.c @@ -2746,7 +2746,7 @@ copy_blkmode_to_reg (machine_mode mode, tree src) x = expand_normal (src); - bytes = int_size_in_bytes (TREE_TYPE (src)); + bytes = int_maybe_empty_type_size (TREE_TYPE (src)); if (bytes == 0) return NULL_RTX; diff --git gcc/function.c gcc/function.c index 339419ee1da..b7f97bf0ae5 100644 --- gcc/function.c +++ gcc/function.c @@ -2528,6 +2528,9 @@ assign_parm_find_entry_rtl (struct assign_parm_data_all *all, return; } + targetm.calls.warn_parameter_passing_abi (all->args_so_far, + data->passed_type); + entry_parm = targetm.calls.function_incoming_arg (all->args_so_far, data->promoted_mode, data->passed_type, @@ -4140,8 +4143,9 @@ locate_and_pad_parm (machine_mode passed_mode, tree type, int in_regs, part_size_in_regs = (reg_parm_stack_space == 0 ? partial : 0); - sizetree - = type ? size_in_bytes (type) : size_int (GET_MODE_SIZE (passed_mode)); + sizetree = (type + ? maybe_empty_type_size (type) + : size_int (GET_MODE_SIZE (passed_mode))); where_pad = targetm.calls.function_arg_padding (passed_mode, type); boundary = targetm.calls.function_arg_boundary (passed_mode, type); round_boundary = targetm.calls.function_arg_round_boundary (passed_mode, diff --git gcc/langhooks-def.h gcc/langhooks-def.h index 61b081bd7cc..4cb31a68d3d 100644 --- gcc/langhooks-def.h +++ gcc/langhooks-def.h @@ -132,6 +132,7 @@ extern int lhd_type_dwarf_attribute (const_tree, int); #define LANG_HOOKS_CUSTOM_FUNCTION_DESCRIPTORS false #define LANG_HOOKS_RUN_LANG_SELFTESTS lhd_do_nothing #define LANG_HOOKS_GET_SUBSTRING_LOCATION lhd_get_substring_location +#define LANG_HOOKS_WARN_EMPTY_CLASS_ABI hook_bool_void_false /* Attribute hooks. */ #define LANG_HOOKS_ATTRIBUTE_TABLE NULL @@ -344,7 +345,8 @@ extern void lhd_end_section (void); LANG_HOOKS_DEEP_UNSHARING, \ LANG_HOOKS_CUSTOM_FUNCTION_DESCRIPTORS, \ LANG_HOOKS_RUN_LANG_SELFTESTS, \ - LANG_HOOKS_GET_SUBSTRING_LOCATION \ + LANG_HOOKS_GET_SUBSTRING_LOCATION, \ + LANG_HOOKS_WARN_EMPTY_CLASS_ABI \ } #endif /* GCC_LANG_HOOKS_DEF_H */ diff --git gcc/langhooks.h gcc/langhooks.h index d1288f1965d..ed79b2d8626 100644 --- gcc/langhooks.h +++ gcc/langhooks.h @@ -538,6 +538,9 @@ struct lang_hooks const char *(*get_substring_location) (const substring_loc &, location_t *out_loc); + /* Return true when we should warn about the empty class ABI change. */ + bool (*warn_empty_class_abi) (void); + /* Whenever you add entries here, make sure you adjust langhooks-def.h and langhooks.c accordingly. */ }; diff --git gcc/target.def gcc/target.def index 6a1cd31f2ac..9445a412689 100644 --- gcc/target.def +++ gcc/target.def @@ -5043,6 +5043,22 @@ DEFHOOK machine_mode, (int regno), default_get_reg_raw_mode) +/* Return true if a type is an empty record. */ +DEFHOOK +(empty_record_p, + "This target hook returns true if the type is an empty record. The default\n\ +is to return @code{false}.", + bool, (const_tree type), + hook_bool_const_tree_false) + +/* Warn about the change in empty class parameter passing ABI. */ +DEFHOOK +(warn_parameter_passing_abi, + "This target hook warns about the change in empty class parameter passing\n\ +ABI.", + void, (cumulative_args_t ca, tree type), + hook_void_CUMULATIVE_ARGS_tree) + HOOK_VECTOR_END (calls) DEFHOOK diff --git gcc/targhooks.c gcc/targhooks.c index 92ecc90d4d4..f117382d565 100644 --- gcc/targhooks.c +++ gcc/targhooks.c @@ -734,6 +734,12 @@ hook_int_CUMULATIVE_ARGS_mode_tree_bool_0 ( } void +hook_void_CUMULATIVE_ARGS_tree (cumulative_args_t ca ATTRIBUTE_UNUSED, + tree ATTRIBUTE_UNUSED) +{ +} + +void default_function_arg_advance (cumulative_args_t ca ATTRIBUTE_UNUSED, machine_mode mode ATTRIBUTE_UNUSED, const_tree type ATTRIBUTE_UNUSED, @@ -2084,6 +2090,7 @@ std_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p, /* va_list pointer is aligned to PARM_BOUNDARY. If argument actually requires greater alignment, we must perform dynamic alignment. */ if (boundary > align + && !targetm.calls.empty_record_p (type) && !integer_zerop (TYPE_SIZE (type))) { t = build2 (MODIFY_EXPR, TREE_TYPE (valist), valist_tmp, @@ -2110,7 +2117,7 @@ std_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p, } /* Compute the rounded size of the type. */ - type_size = size_in_bytes (type); + type_size = maybe_empty_type_size (type); rounded_size = round_up (type_size, align); /* Reduce rounded_size so it's sharable with the postqueue. */ diff --git gcc/targhooks.h gcc/targhooks.h index f60bca257f7..722608f35cc 100644 --- gcc/targhooks.h +++ gcc/targhooks.h @@ -134,6 +134,8 @@ extern bool hook_bool_CUMULATIVE_ARGS_mode_tree_bool_true (cumulative_args_t, machine_mode, const_tree, bool); extern int hook_int_CUMULATIVE_ARGS_mode_tree_bool_0 (cumulative_args_t, machine_mode, tree, bool); +extern void hook_void_CUMULATIVE_ARGS_tree + (cumulative_args_t, tree); extern const char *hook_invalid_arg_for_unprototyped_fn (const_tree, const_tree, const_tree); extern void default_function_arg_advance diff --git gcc/testsuite/g++.dg/abi/empty12.C gcc/testsuite/g++.dg/abi/empty12.C index e69de29bb2d..20d85ff873e 100644 --- gcc/testsuite/g++.dg/abi/empty12.C +++ gcc/testsuite/g++.dg/abi/empty12.C @@ -0,0 +1,17 @@ +// PR c++/60336 +// { dg-do run { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } } +// { dg-options "-Wabi=11 -x c" } +// { dg-additional-sources "empty12a.c" } +// { dg-prune-output "command line option" } + +#include "empty12.h" +extern "C" void fun(struct dummy, struct foo); + +int main() +{ + struct dummy d; + struct foo f = { -1, -2, -3, -4, -5 }; + + fun(d, f); // { dg-warning "empty" } + return 0; +} diff --git gcc/testsuite/g++.dg/abi/empty12.h gcc/testsuite/g++.dg/abi/empty12.h index e69de29bb2d..c61afcda0fb 100644 --- gcc/testsuite/g++.dg/abi/empty12.h +++ gcc/testsuite/g++.dg/abi/empty12.h @@ -0,0 +1,9 @@ +struct dummy { }; +struct foo +{ + int i1; + int i2; + int i3; + int i4; + int i5; +}; diff --git gcc/testsuite/g++.dg/abi/empty12a.c gcc/testsuite/g++.dg/abi/empty12a.c index e69de29bb2d..34a25bad75d 100644 --- gcc/testsuite/g++.dg/abi/empty12a.c +++ gcc/testsuite/g++.dg/abi/empty12a.c @@ -0,0 +1,6 @@ +#include "empty12.h" +void fun(struct dummy d, struct foo f) +{ + if (f.i1 != -1) + __builtin_abort(); +} diff --git gcc/testsuite/g++.dg/abi/empty13.C gcc/testsuite/g++.dg/abi/empty13.C index e69de29bb2d..0cb9a373e35 100644 --- gcc/testsuite/g++.dg/abi/empty13.C +++ gcc/testsuite/g++.dg/abi/empty13.C @@ -0,0 +1,17 @@ +// PR c++/60336 +// { dg-do run { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } } +// { dg-options "-x c -fabi-version=11" } +// { dg-additional-sources "empty13a.c" } +// { dg-prune-output "command line option" } + +#include "empty13.h" +extern "C" void fun(struct dummy, struct foo); + +int main() +{ + struct dummy d; + struct foo f = { -1, -2, -3, -4, -5 }; + + fun(d, f); + return 0; +} diff --git gcc/testsuite/g++.dg/abi/empty13.h gcc/testsuite/g++.dg/abi/empty13.h index e69de29bb2d..c61afcda0fb 100644 --- gcc/testsuite/g++.dg/abi/empty13.h +++ gcc/testsuite/g++.dg/abi/empty13.h @@ -0,0 +1,9 @@ +struct dummy { }; +struct foo +{ + int i1; + int i2; + int i3; + int i4; + int i5; +}; diff --git gcc/testsuite/g++.dg/abi/empty13a.c gcc/testsuite/g++.dg/abi/empty13a.c index e69de29bb2d..b4303a63826 100644 --- gcc/testsuite/g++.dg/abi/empty13a.c +++ gcc/testsuite/g++.dg/abi/empty13a.c @@ -0,0 +1,6 @@ +#include "empty13.h" +void fun(struct dummy d, struct foo f) +{ + if (f.i1 == -1) + __builtin_abort(); +} diff --git gcc/testsuite/g++.dg/abi/empty14.C gcc/testsuite/g++.dg/abi/empty14.C index e69de29bb2d..2868d8ad3f3 100644 --- gcc/testsuite/g++.dg/abi/empty14.C +++ gcc/testsuite/g++.dg/abi/empty14.C @@ -0,0 +1,17 @@ +// PR c++/60336 +// { dg-do run { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } } +// { dg-options "-Wabi=11 -x c" } +// { dg-additional-sources "empty14a.c" } +// { dg-prune-output "command line option" } + +#include "empty14.h" +extern "C" void fun(struct dummy, struct foo); + +int main() +{ + struct dummy d; + struct foo f = { -1, -2, -3, -4, -5 }; + + fun(d, f); // { dg-warning "empty" } + return 0; +} diff --git gcc/testsuite/g++.dg/abi/empty14.h gcc/testsuite/g++.dg/abi/empty14.h index e69de29bb2d..5842279cf37 100644 --- gcc/testsuite/g++.dg/abi/empty14.h +++ gcc/testsuite/g++.dg/abi/empty14.h @@ -0,0 +1,10 @@ +struct dummy0 { }; +struct dummy { struct dummy0 d[140]; }; +struct foo +{ + int i1; + int i2; + int i3; + int i4; + int i5; +}; diff --git gcc/testsuite/g++.dg/abi/empty14a.c gcc/testsuite/g++.dg/abi/empty14a.c index e69de29bb2d..8b3d7800c36 100644 --- gcc/testsuite/g++.dg/abi/empty14a.c +++ gcc/testsuite/g++.dg/abi/empty14a.c @@ -0,0 +1,6 @@ +#include "empty14.h" +void fun(struct dummy d, struct foo f) +{ + if (f.i1 != -1) + __builtin_abort(); +} diff --git gcc/testsuite/g++.dg/abi/empty15.C gcc/testsuite/g++.dg/abi/empty15.C index e69de29bb2d..12385f78c78 100644 --- gcc/testsuite/g++.dg/abi/empty15.C +++ gcc/testsuite/g++.dg/abi/empty15.C @@ -0,0 +1,17 @@ +// PR c++/60336 +// { dg-do run { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } } +// { dg-options "-Wabi=11 -x c" } +// { dg-additional-sources "empty15a.c" } +// { dg-prune-output "command line option" } + +#include "empty15.h" +extern "C" void fun(struct dummy, struct foo); + +int main() +{ + struct dummy d; + struct foo f = { -1, -2, -3, -4, -5 }; + + fun(d, f); // { dg-warning "empty" } + return 0; +} diff --git gcc/testsuite/g++.dg/abi/empty15.h gcc/testsuite/g++.dg/abi/empty15.h index e69de29bb2d..1c6f26f5ae8 100644 --- gcc/testsuite/g++.dg/abi/empty15.h +++ gcc/testsuite/g++.dg/abi/empty15.h @@ -0,0 +1,30 @@ +struct A1 {}; +struct A2 {}; +struct B1 { struct A1 a; struct A2 b; }; +struct B2 { struct A1 a; struct A2 b; }; +struct C1 { struct B1 a; struct B2 b; }; +struct C2 { struct B1 a; struct B2 b; }; +struct D1 { struct C1 a; struct C2 b; }; +struct D2 { struct C1 a; struct C2 b; }; +struct E1 { struct D1 a; struct D2 b; }; +struct E2 { struct D1 a; struct D2 b; }; +struct F1 { struct E1 a; struct E2 b; }; +struct F2 { struct E1 a; struct E2 b; }; +struct G1 { struct F1 a; struct F2 b; }; +struct G2 { struct F1 a; struct F2 b; }; +struct H1 { struct G1 a; struct G2 b; }; +struct H2 { struct G1 a; struct G2 b; }; +struct I1 { struct H1 a; struct H2 b; }; +struct I2 { struct H1 a; struct H2 b; }; +struct J1 { struct I1 a; struct I2 b; }; +struct J2 { struct I1 a; struct I2 b; }; +struct dummy { struct J1 a; struct J2 b; }; + +struct foo +{ + int i1; + int i2; + int i3; + int i4; + int i5; +}; diff --git gcc/testsuite/g++.dg/abi/empty15a.c gcc/testsuite/g++.dg/abi/empty15a.c index e69de29bb2d..325b2c5ba09 100644 --- gcc/testsuite/g++.dg/abi/empty15a.c +++ gcc/testsuite/g++.dg/abi/empty15a.c @@ -0,0 +1,6 @@ +#include "empty15.h" +void fun(struct dummy d, struct foo f) +{ + if (f.i1 != -1) + __builtin_abort(); +} diff --git gcc/testsuite/g++.dg/abi/empty16.C gcc/testsuite/g++.dg/abi/empty16.C index e69de29bb2d..1ca52f9011e 100644 --- gcc/testsuite/g++.dg/abi/empty16.C +++ gcc/testsuite/g++.dg/abi/empty16.C @@ -0,0 +1,17 @@ +// PR c++/60336 +// { dg-do run { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } } +// { dg-options "-Wabi=11 -x c" } +// { dg-additional-sources "empty16a.c" } +// { dg-prune-output "command line option" } + +#include "empty16.h" +extern "C" void fun(struct dummy, struct foo); + +int main() +{ + struct dummy d; + struct foo f = { -1, -2, -3, -4, -5 }; + + fun(d, f); // { dg-warning "empty" } + return 0; +} diff --git gcc/testsuite/g++.dg/abi/empty16.h gcc/testsuite/g++.dg/abi/empty16.h index e69de29bb2d..7552ae06576 100644 --- gcc/testsuite/g++.dg/abi/empty16.h +++ gcc/testsuite/g++.dg/abi/empty16.h @@ -0,0 +1,16 @@ +#ifdef __cplusplus +struct A1 {}; +struct A2 {}; +struct dummy : A1, A2 {} ; +#else +struct dummy {}; +#endif + +struct foo +{ + int i1; + int i2; + int i3; + int i4; + int i5; +}; diff --git gcc/testsuite/g++.dg/abi/empty16a.c gcc/testsuite/g++.dg/abi/empty16a.c index e69de29bb2d..6cb7fbccecc 100644 --- gcc/testsuite/g++.dg/abi/empty16a.c +++ gcc/testsuite/g++.dg/abi/empty16a.c @@ -0,0 +1,6 @@ +#include "empty16.h" +void fun(struct dummy d, struct foo f) +{ + if (f.i1 != -1) + __builtin_abort(); +} diff --git gcc/testsuite/g++.dg/abi/empty17.C gcc/testsuite/g++.dg/abi/empty17.C index e69de29bb2d..d386e5481af 100644 --- gcc/testsuite/g++.dg/abi/empty17.C +++ gcc/testsuite/g++.dg/abi/empty17.C @@ -0,0 +1,17 @@ +// PR c++/60336 +// { dg-do run { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } } +// { dg-options "-Wabi=11 -x c" } +// { dg-additional-sources "empty17a.c" } +// { dg-prune-output "command line option" } + +#include "empty17.h" +extern "C" void fun(struct dummy, struct foo); + +int main() +{ + struct dummy d; + struct foo f = { -1, -2, -3, -4, -5 }; + + fun(d, f); // { dg-warning "empty" } + return 0; +} diff --git gcc/testsuite/g++.dg/abi/empty17.h gcc/testsuite/g++.dg/abi/empty17.h index e69de29bb2d..9cf72baca2e 100644 --- gcc/testsuite/g++.dg/abi/empty17.h +++ gcc/testsuite/g++.dg/abi/empty17.h @@ -0,0 +1,27 @@ +#ifdef __cplusplus +struct A1 +{ + void foo (void); + unsigned int : 15; +}; +struct A2 +{ + void bar (void); + unsigned int : 15; +}; +struct dummy : A1, A2 +{ + unsigned int : 15; +}; +#else +struct dummy {}; +#endif + +struct foo +{ + int i1; + int i2; + int i3; + int i4; + int i5; +}; diff --git gcc/testsuite/g++.dg/abi/empty17a.c gcc/testsuite/g++.dg/abi/empty17a.c index e69de29bb2d..24408fde09c 100644 --- gcc/testsuite/g++.dg/abi/empty17a.c +++ gcc/testsuite/g++.dg/abi/empty17a.c @@ -0,0 +1,6 @@ +#include "empty17.h" +void fun(struct dummy d, struct foo f) +{ + if (f.i1 != -1) + __builtin_abort(); +} diff --git gcc/testsuite/g++.dg/abi/empty18.C gcc/testsuite/g++.dg/abi/empty18.C index e69de29bb2d..be69c6a2115 100644 --- gcc/testsuite/g++.dg/abi/empty18.C +++ gcc/testsuite/g++.dg/abi/empty18.C @@ -0,0 +1,17 @@ +// PR c++/60336 +// { dg-do run { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } } +// { dg-options "-Wabi=11 -x c" } +// { dg-additional-sources "empty18a.c" } +// { dg-prune-output "command line option" } + +#include "empty18.h" +extern "C" void fun(struct dummy, struct foo); + +int main() +{ + struct dummy d; + struct foo f = { -1, -2, -3, -4, -5 }; + + fun(d, f); + return 0; +} diff --git gcc/testsuite/g++.dg/abi/empty18.h gcc/testsuite/g++.dg/abi/empty18.h index e69de29bb2d..86e7ecdd211 100644 --- gcc/testsuite/g++.dg/abi/empty18.h +++ gcc/testsuite/g++.dg/abi/empty18.h @@ -0,0 +1,9 @@ +struct dummy { int d[0]; }; +struct foo +{ + int i1; + int i2; + int i3; + int i4; + int i5; +}; diff --git gcc/testsuite/g++.dg/abi/empty18a.c gcc/testsuite/g++.dg/abi/empty18a.c index e69de29bb2d..902860bdc01 100644 --- gcc/testsuite/g++.dg/abi/empty18a.c +++ gcc/testsuite/g++.dg/abi/empty18a.c @@ -0,0 +1,6 @@ +#include "empty18.h" +void fun(struct dummy d, struct foo f) +{ + if (f.i1 != -1) + __builtin_abort(); +} diff --git gcc/testsuite/g++.dg/abi/empty19.C gcc/testsuite/g++.dg/abi/empty19.C index e69de29bb2d..84f5b75558b 100644 --- gcc/testsuite/g++.dg/abi/empty19.C +++ gcc/testsuite/g++.dg/abi/empty19.C @@ -0,0 +1,17 @@ +// PR c++/60336 +// { dg-do run { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } } +// { dg-options "-Wabi=11 -x c" } +// { dg-additional-sources "empty19a.c" } +// { dg-prune-output "command line option" } + +#include "empty19.h" +extern "C" void fun(struct dummy, struct foo); + +int main() +{ + struct dummy d; + struct foo f = { -1, -2, -3, -4, -5 }; + + fun(d, f); + return 0; +} diff --git gcc/testsuite/g++.dg/abi/empty19.h gcc/testsuite/g++.dg/abi/empty19.h index e69de29bb2d..616b87bdd93 100644 --- gcc/testsuite/g++.dg/abi/empty19.h +++ gcc/testsuite/g++.dg/abi/empty19.h @@ -0,0 +1,10 @@ +struct dummy0 { }; +struct dummy { struct dummy0 d[0]; }; +struct foo +{ + int i1; + int i2; + int i3; + int i4; + int i5; +}; diff --git gcc/testsuite/g++.dg/abi/empty19a.c gcc/testsuite/g++.dg/abi/empty19a.c index e69de29bb2d..767b1eb7320 100644 --- gcc/testsuite/g++.dg/abi/empty19a.c +++ gcc/testsuite/g++.dg/abi/empty19a.c @@ -0,0 +1,6 @@ +#include "empty19.h" +void fun(struct dummy d, struct foo f) +{ + if (f.i1 != -1) + __builtin_abort(); +} diff --git gcc/testsuite/g++.dg/abi/empty20.C gcc/testsuite/g++.dg/abi/empty20.C index e69de29bb2d..5022033f669 100644 --- gcc/testsuite/g++.dg/abi/empty20.C +++ gcc/testsuite/g++.dg/abi/empty20.C @@ -0,0 +1,19 @@ +// PR c++/60336 +// { dg-options "-Wabi=11 -O0" } + +struct A { }; + +void f(A, A) { } // No warning, trailing parms all empty +void f(A, A, int) { } // { dg-warning "ABI" "" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } } +__attribute__ ((always_inline)) +inline void f(A a, int i) // No warning, always inlined +{ + f(a,a,i); // { dg-warning "ABI" "" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } } +} +int main() +{ + A a; + f(a,a); + f(a,a,42); // { dg-warning "ABI" "" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } } + f(a,42); +} diff --git gcc/testsuite/g++.dg/abi/empty21.C gcc/testsuite/g++.dg/abi/empty21.C index e69de29bb2d..3b2e3b836b1 100644 --- gcc/testsuite/g++.dg/abi/empty21.C +++ gcc/testsuite/g++.dg/abi/empty21.C @@ -0,0 +1,23 @@ +// PR c++/60336 +// { dg-options "-Wabi=11" } + +#include + +struct A { }; + +void f(int i, ...) +{ + va_list ap; + va_start (ap, i); + if (i >= 1) + va_arg (ap, A); + if (i >= 2) + va_arg (ap, int); +} + +int main() +{ + f(0); + f(1, A()); // { dg-warning "ABI" "" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } } + f(2, A(), 42); // { dg-warning "ABI" "" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } } +} diff --git gcc/testsuite/g++.dg/abi/empty22.C gcc/testsuite/g++.dg/abi/empty22.C index e69de29bb2d..f4f4a02bf31 100644 --- gcc/testsuite/g++.dg/abi/empty22.C +++ gcc/testsuite/g++.dg/abi/empty22.C @@ -0,0 +1,17 @@ +// PR c++/60336 +// { dg-do run { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } } +// { dg-options "-Wabi=11 -x c" } +// { dg-additional-sources "empty22a.c" } +// { dg-prune-output "command line option" } + +#include "empty22.h" +extern "C" void fun(struct dummy, struct foo); + +int main() +{ + struct dummy d; + struct foo f = { -1, -2, -3, -4, -5 }; + + fun(d, f); // { dg-warning "empty" } + return 0; +} diff --git gcc/testsuite/g++.dg/abi/empty22.h gcc/testsuite/g++.dg/abi/empty22.h index e69de29bb2d..8d54dc74519 100644 --- gcc/testsuite/g++.dg/abi/empty22.h +++ gcc/testsuite/g++.dg/abi/empty22.h @@ -0,0 +1,27 @@ +#ifdef __cplusplus +struct A1 +{ + void foo (void); + unsigned int : 0; +}; +struct A2 +{ + void bar (void); + unsigned int : 0; +}; +struct dummy : A1, A2 +{ + unsigned int : 0; +}; +#else +struct dummy {}; +#endif + +struct foo +{ + int i1; + int i2; + int i3; + int i4; + int i5; +}; diff --git gcc/testsuite/g++.dg/abi/empty22a.c gcc/testsuite/g++.dg/abi/empty22a.c index e69de29bb2d..7606c524263 100644 --- gcc/testsuite/g++.dg/abi/empty22a.c +++ gcc/testsuite/g++.dg/abi/empty22a.c @@ -0,0 +1,6 @@ +#include "empty22.h" +void fun(struct dummy d, struct foo f) +{ + if (f.i1 != -1) + __builtin_abort(); +} diff --git gcc/testsuite/g++.dg/abi/empty23.C gcc/testsuite/g++.dg/abi/empty23.C index e69de29bb2d..b97d2804529 100644 --- gcc/testsuite/g++.dg/abi/empty23.C +++ gcc/testsuite/g++.dg/abi/empty23.C @@ -0,0 +1,25 @@ +// PR c++/60336 +// { dg-do run } +// { dg-options "-Wabi=11" } + +struct S +{ + struct { } a; + __extension__ int b[0]; +}; + +struct S s; +struct S a[5]; + +void +foo (struct S, struct S *arg1, struct S) +{ + if (arg1 != &a[1]) + __builtin_abort (); +} + +int +main () +{ + foo (s, &a[1], a[2]); +} diff --git gcc/testsuite/g++.dg/abi/empty24.C gcc/testsuite/g++.dg/abi/empty24.C index e69de29bb2d..81deb36ff9f 100644 --- gcc/testsuite/g++.dg/abi/empty24.C +++ gcc/testsuite/g++.dg/abi/empty24.C @@ -0,0 +1,25 @@ +// PR c++/60336 +// { dg-do run } +// { dg-options "-Wabi=11" } + +struct S +{ + struct { } a; + __extension__ int b[]; +}; + +struct S s; +struct S a[5]; + +void +foo (struct S, struct S *arg1, struct S) +{ + if (arg1 != &a[1]) + __builtin_abort (); +} + +int +main () +{ + foo (s, &a[1], a[2]); +} diff --git gcc/testsuite/g++.dg/abi/pr60336-1.C gcc/testsuite/g++.dg/abi/pr60336-1.C index e69de29bb2d..59447890cec 100644 --- gcc/testsuite/g++.dg/abi/pr60336-1.C +++ gcc/testsuite/g++.dg/abi/pr60336-1.C @@ -0,0 +1,17 @@ +// { dg-do compile } +// { dg-options "-O2 -std=c++11 -fno-pic" } +// { dg-require-effective-target fpic } + +struct dummy { }; +struct true_type { struct dummy i; }; + +extern true_type y; +extern void xxx (true_type c); + +void +yyy (void) +{ + xxx (y); +} + +// { dg-final { scan-assembler "jmp\[\t \]+\[^\$\]*?_Z3xxx9true_type" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } } } diff --git gcc/testsuite/g++.dg/abi/pr60336-10.C gcc/testsuite/g++.dg/abi/pr60336-10.C index e69de29bb2d..960cc2307d1 100644 --- gcc/testsuite/g++.dg/abi/pr60336-10.C +++ gcc/testsuite/g++.dg/abi/pr60336-10.C @@ -0,0 +1,50 @@ +// { dg-do run { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } } +// { dg-options "-O2" } + +#include + +struct dummy0 { }; +struct dummy1 { }; +struct dummy : dummy0, dummy1 { }; + +void +test (struct dummy a, int m, ...) +{ + va_list va_arglist; + int i; + int count = 0; + + if (m == 0) + count++; + va_start (va_arglist, m); + i = va_arg (va_arglist, int); + if (i == 1) + count++; + i = va_arg (va_arglist, int); + if (i == 2) + i = va_arg (va_arglist, int); + count++; + if (i == 3) + count++; + i = va_arg (va_arglist, int); + if (i == 4) + count++; + i = va_arg (va_arglist, int); + if (i == 5) + count++; + i = va_arg (va_arglist, int); + if (i == 6) + count++; + va_end (va_arglist); + if (count != 7) + __builtin_abort (); +} + +struct dummy a0; + +int +main () +{ + test (a0, 0, 1, 2, 3, 4, 5, 6); + return 0; +} diff --git gcc/testsuite/g++.dg/abi/pr60336-11.C gcc/testsuite/g++.dg/abi/pr60336-11.C index e69de29bb2d..14cd6d0ff3d 100644 --- gcc/testsuite/g++.dg/abi/pr60336-11.C +++ gcc/testsuite/g++.dg/abi/pr60336-11.C @@ -0,0 +1,56 @@ +// { dg-do run { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } } +// { dg-options "-O2" } + +#include + +struct dummy0 +{ + void bar (void); +}; +struct dummy1 +{ + void foo (void); +}; +struct dummy : dummy0, dummy1 { }; + +void +test (struct dummy a, int m, ...) +{ + va_list va_arglist; + int i; + int count = 0; + + if (m == 0) + count++; + va_start (va_arglist, m); + i = va_arg (va_arglist, int); + if (i == 1) + count++; + i = va_arg (va_arglist, int); + if (i == 2) + i = va_arg (va_arglist, int); + count++; + if (i == 3) + count++; + i = va_arg (va_arglist, int); + if (i == 4) + count++; + i = va_arg (va_arglist, int); + if (i == 5) + count++; + i = va_arg (va_arglist, int); + if (i == 6) + count++; + va_end (va_arglist); + if (count != 7) + __builtin_abort (); +} + +struct dummy a0; + +int +main () +{ + test (a0, 0, 1, 2, 3, 4, 5, 6); + return 0; +} diff --git gcc/testsuite/g++.dg/abi/pr60336-12.C gcc/testsuite/g++.dg/abi/pr60336-12.C index e69de29bb2d..09917547930 100644 --- gcc/testsuite/g++.dg/abi/pr60336-12.C +++ gcc/testsuite/g++.dg/abi/pr60336-12.C @@ -0,0 +1,57 @@ +// { dg-do run { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } } +// { dg-options "-O2" } + +#include + +struct dummy0 +{ +}; +struct dummy1 +{ + unsigned : 15; +}; +struct dummy : dummy0, dummy1 +{ +}; + +void +test (struct dummy a, int m, ...) +{ + va_list va_arglist; + int i; + int count = 0; + + if (m == 0) + count++; + va_start (va_arglist, m); + i = va_arg (va_arglist, int); + if (i == 1) + count++; + i = va_arg (va_arglist, int); + if (i == 2) + i = va_arg (va_arglist, int); + count++; + if (i == 3) + count++; + i = va_arg (va_arglist, int); + if (i == 4) + count++; + i = va_arg (va_arglist, int); + if (i == 5) + count++; + i = va_arg (va_arglist, int); + if (i == 6) + count++; + va_end (va_arglist); + if (count != 7) + __builtin_abort (); +} + +struct dummy a0; + +int +main () +{ + test (a0, 0, 1, 2, 3, 4, 5, 6); + return 0; +} diff --git gcc/testsuite/g++.dg/abi/pr60336-2.C gcc/testsuite/g++.dg/abi/pr60336-2.C index e69de29bb2d..1c6c3eb8f01 100644 --- gcc/testsuite/g++.dg/abi/pr60336-2.C +++ gcc/testsuite/g++.dg/abi/pr60336-2.C @@ -0,0 +1,48 @@ +// { dg-do run { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } } +// { dg-options "-O2 -Wabi=11" } + +#include + +struct dummy { }; + +void +test (struct dummy a, int m, ...) // { dg-warning "empty" } +{ + va_list va_arglist; + int i; + int count = 0; + + if (m == 0) + count++; + va_start (va_arglist, m); + i = va_arg (va_arglist, int); + if (i == 1) + count++; + i = va_arg (va_arglist, int); + if (i == 2) + i = va_arg (va_arglist, int); + count++; + if (i == 3) + count++; + i = va_arg (va_arglist, int); + if (i == 4) + count++; + i = va_arg (va_arglist, int); + if (i == 5) + count++; + i = va_arg (va_arglist, int); + if (i == 6) + count++; + va_end (va_arglist); + if (count != 7) + __builtin_abort (); +} + +struct dummy a0; + +int +main () +{ + test (a0, 0, 1, 2, 3, 4, 5, 6); // { dg-warning "empty" } + return 0; +} diff --git gcc/testsuite/g++.dg/abi/pr60336-3.C gcc/testsuite/g++.dg/abi/pr60336-3.C index e69de29bb2d..4157e553b6b 100644 --- gcc/testsuite/g++.dg/abi/pr60336-3.C +++ gcc/testsuite/g++.dg/abi/pr60336-3.C @@ -0,0 +1,15 @@ +// { dg-do compile } +// { dg-options "-O2 -Wabi=11" } + +struct dummy { struct { } __attribute__((aligned (4))) a[7]; }; + +extern void test1 (struct dummy, ...); +extern void (*test2) (struct dummy, ...); + +void +foo () +{ + struct dummy a0; + test1 (a0, 42); // { dg-warning "ABI" "" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } } + test2 (a0, 42); // { dg-warning "ABI" "" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } } +} diff --git gcc/testsuite/g++.dg/abi/pr60336-4.C gcc/testsuite/g++.dg/abi/pr60336-4.C index e69de29bb2d..266f67a537d 100644 --- gcc/testsuite/g++.dg/abi/pr60336-4.C +++ gcc/testsuite/g++.dg/abi/pr60336-4.C @@ -0,0 +1,48 @@ +// { dg-do run { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } } +// { dg-options "-O2 -fabi-version=11" } + +#include + +struct dummy { }; + +void +test (struct dummy a, int m, ...) +{ + va_list va_arglist; + int i; + int count = 0; + + if (m == 0) + count++; + va_start (va_arglist, m); + i = va_arg (va_arglist, int); + if (i == 1) + count++; + i = va_arg (va_arglist, int); + if (i == 2) + i = va_arg (va_arglist, int); + count++; + if (i == 3) + count++; + i = va_arg (va_arglist, int); + if (i == 4) + count++; + i = va_arg (va_arglist, int); + if (i == 5) + count++; + i = va_arg (va_arglist, int); + if (i == 6) + count++; + va_end (va_arglist); + if (count == 7) + __builtin_abort (); +} + +struct dummy a0; + +int +main () +{ + test (a0, 0, 1, 2, 3, 4, 5, 6); + return 0; +} diff --git gcc/testsuite/g++.dg/abi/pr60336-5.C gcc/testsuite/g++.dg/abi/pr60336-5.C index e69de29bb2d..fe838750f55 100644 --- gcc/testsuite/g++.dg/abi/pr60336-5.C +++ gcc/testsuite/g++.dg/abi/pr60336-5.C @@ -0,0 +1,17 @@ +// { dg-do compile } +// { dg-options "-O2 -std=c++11 -fno-pic" } +// { dg-require-effective-target fpic } + +struct dummy { }; +struct true_type { struct dummy i; struct dummy j; }; + +extern true_type y; +extern void xxx (true_type c); + +void +yyy (void) +{ + xxx (y); +} + +// { dg-final { scan-assembler "jmp\[\t \]+\[^\$\]*?_Z3xxx9true_type" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } } } diff --git gcc/testsuite/g++.dg/abi/pr60336-6.C gcc/testsuite/g++.dg/abi/pr60336-6.C index e69de29bb2d..6e08c8f06fa 100644 --- gcc/testsuite/g++.dg/abi/pr60336-6.C +++ gcc/testsuite/g++.dg/abi/pr60336-6.C @@ -0,0 +1,17 @@ +// { dg-do compile } +// { dg-options "-O2 -std=c++11 -fno-pic" } +// { dg-require-effective-target fpic } + +struct dummy { }; +struct true_type { struct dummy i1; struct dummy i2; }; + +extern true_type y; +extern void xxx (true_type c); + +void +yyy (void) +{ + xxx (y); +} + +// { dg-final { scan-assembler "jmp\[\t \]+\[^\$\]*?_Z3xxx9true_type" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } } } diff --git gcc/testsuite/g++.dg/abi/pr60336-7.C gcc/testsuite/g++.dg/abi/pr60336-7.C index e69de29bb2d..3b8b8ba6f35 100644 --- gcc/testsuite/g++.dg/abi/pr60336-7.C +++ gcc/testsuite/g++.dg/abi/pr60336-7.C @@ -0,0 +1,17 @@ +// { dg-do compile } +// { dg-options "-O2 -std=c++11 -fno-pic" } +// { dg-require-effective-target fpic } + +struct dummy { }; +struct true_type { struct dummy i[120]; }; + +extern true_type y; +extern void xxx (true_type c); + +void +yyy (void) +{ + xxx (y); +} + +// { dg-final { scan-assembler "jmp\[\t \]+\[^\$\]*?_Z3xxx9true_type" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } } } diff --git gcc/testsuite/g++.dg/abi/pr60336-8.C gcc/testsuite/g++.dg/abi/pr60336-8.C index e69de29bb2d..a1ffb64ef02 100644 --- gcc/testsuite/g++.dg/abi/pr60336-8.C +++ gcc/testsuite/g++.dg/abi/pr60336-8.C @@ -0,0 +1,15 @@ +// { dg-do compile } +// { dg-options "-O2 -Wabi=11" } + +struct dummy { struct{} a[7][3]; }; + +extern void test1 (struct dummy, ...); +extern void (*test2) (struct dummy, ...); + +void +foo () +{ + struct dummy a0; + test1 (a0, 42); // { dg-warning "ABI" "" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } } + test2 (a0, 42); // { dg-warning "ABI" "" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } } +} diff --git gcc/testsuite/g++.dg/abi/pr60336-9.C gcc/testsuite/g++.dg/abi/pr60336-9.C index e69de29bb2d..393f02b62f0 100644 --- gcc/testsuite/g++.dg/abi/pr60336-9.C +++ gcc/testsuite/g++.dg/abi/pr60336-9.C @@ -0,0 +1,28 @@ +// { dg-do compile } +// { dg-options "-O2 -std=c++11 -fno-pic" } +// { dg-require-effective-target fpic } + +struct A1 {}; struct A2 {}; +struct B1 { A1 a; A2 b; }; struct B2 { A1 a; A2 b; }; +struct C1 { B1 a; B2 b; }; struct C2 { B1 a; B2 b; }; +struct D1 { C1 a; C2 b; }; struct D2 { C1 a; C2 b; }; +struct E1 { D1 a; D2 b; }; struct E2 { D1 a; D2 b; }; +struct F1 { E1 a; E2 b; }; struct F2 { E1 a; E2 b; }; +struct G1 { F1 a; F2 b; }; struct G2 { F1 a; F2 b; }; +struct H1 { G1 a; G2 b; }; struct H2 { G1 a; G2 b; }; +struct I1 { H1 a; H2 b; }; struct I2 { H1 a; H2 b; }; +struct J1 { I1 a; I2 b; }; struct J2 { I1 a; I2 b; }; +struct dummy { J1 a; J2 b; }; + +struct true_type { struct dummy i; }; + +extern true_type y; +extern void xxx (true_type c); + +void +yyy (void) +{ + xxx (y); +} + +// { dg-final { scan-assembler "jmp\[\t \]+\[^\$\]*?_Z3xxx9true_type" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } } } diff --git gcc/testsuite/g++.dg/abi/pr68355.C gcc/testsuite/g++.dg/abi/pr68355.C index e69de29bb2d..1354fc497b5 100644 --- gcc/testsuite/g++.dg/abi/pr68355.C +++ gcc/testsuite/g++.dg/abi/pr68355.C @@ -0,0 +1,24 @@ +// { dg-do compile } +// { dg-options "-O2 -std=c++11 -fno-pic" } +// { dg-require-effective-target fpic } + +template +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 true_type; +extern void xxx (true_type c); + +void +yyy (void) +{ + true_type y; + xxx (y); +} + +// { dg-final { scan-assembler "jmp\[\t \]+\[^\$\]*?_Z3xxx17integral_constantIbLb1EE" { target i?86-*-* x86_64-*-* } } } diff --git gcc/tree.c gcc/tree.c index fa6fcb1da71..a32c238de5d 100644 --- gcc/tree.c +++ gcc/tree.c @@ -3135,6 +3135,20 @@ array_type_nelts (const_tree type) ? max : fold_build2 (MINUS_EXPR, TREE_TYPE (max), max, min)); } + +/* Return, as an INTEGER_CST node, the number of elements for TYPE + (which is an ARRAY_TYPE). This counts only elements of the top + array. */ + +tree +array_type_nelts_top (const_tree type) +{ + return fold_build2_loc (input_location, + PLUS_EXPR, sizetype, + array_type_nelts (type), + size_one_node); +} + /* If arg is static -- a reference to an object in static storage -- then return the object. This is not the same as the C meaning of `static'. @@ -13811,6 +13825,89 @@ get_nonnull_args (const_tree fntype) return argmap; } +/* Returns true if TYPE is a type where it and all of its subobjects + (recursively) are of structure, union, or array type. */ + +static bool +is_empty_type (tree type) +{ + if (RECORD_OR_UNION_TYPE_P (type)) + { + for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field)) + { + if (TREE_CODE (field) == FIELD_DECL) + { + tree ftype = TREE_TYPE (field); + /* Don't consider struct S { struct { } a; int b[0]; }; + an empty type. */ + if (TREE_CODE (ftype) == ARRAY_TYPE + && integer_zerop (array_type_nelts_top (ftype))) + { + tree t = DECL_CHAIN (field); + bool found = false; + /* See if the zero-length array is followed by another + FIELD_DECL. */ + while (t) + { + if (TREE_CODE (t) == FIELD_DECL) + { + found = true; + break; + } + t = DECL_CHAIN (t); + } + if (!found) + return false; + } + if ((DECL_NAME (field) || RECORD_OR_UNION_TYPE_P (ftype)) + && !is_empty_type (ftype)) + return false; + } + } + return true; + } + else if (TREE_CODE (type) == ARRAY_TYPE) + return (integer_zerop (array_type_nelts_top (type)) + || is_empty_type (TREE_TYPE (type))); + return false; +} + +/* Implement TARGET_EMPTY_RECORD_P. Return true if TYPE is an empty type + that shouldn't be passed via stack. */ + +bool +is_empty_record_p (const_tree type) +{ + if (!abi_version_at_least (12)) + return false; + + if (type == error_mark_node) + return false; + + if (TREE_ADDRESSABLE (type)) + return false; + + return is_empty_type (TYPE_MAIN_VARIANT (type)); +} + +/* Like int_size_in_bytes, but handle empty records specially. */ + +HOST_WIDE_INT +int_maybe_empty_type_size (const_tree type) +{ + return (targetm.calls.empty_record_p (type) + ? 0 : int_size_in_bytes (type)); +} + +/* Like size_in_bytes, but handle empty records specially. */ + +tree +maybe_empty_type_size (const_tree type) +{ + return (targetm.calls.empty_record_p (type) + ? size_zero_node : size_in_bytes (type)); +} + /* List of pointer types used to declare builtins before we have seen their real declaration. diff --git gcc/tree.h gcc/tree.h index 7214ae2275c..fb8af8bac7d 100644 --- gcc/tree.h +++ gcc/tree.h @@ -4099,6 +4099,7 @@ extern tree build_method_type (tree, tree); extern tree build_offset_type (tree, tree); extern tree build_complex_type (tree, bool named = false); extern tree array_type_nelts (const_tree); +extern tree array_type_nelts_top (const_tree); extern tree value_member (tree, tree); extern tree purpose_member (const_tree, tree); @@ -5434,6 +5435,9 @@ extern void gt_pch_nx (tree &, gt_pointer_operator, void *); extern bool nonnull_arg_p (const_tree); extern bool is_redundant_typedef (const_tree); +extern bool is_empty_record_p (const_tree); +extern HOST_WIDE_INT int_maybe_empty_type_size (const_tree); +extern tree maybe_empty_type_size (const_tree); extern location_t set_source_range (tree expr, location_t start, location_t finish); Marek