On Mon, Feb 29, 2016 at 5:02 PM, Jason Merrill <ja...@redhat.com> wrote: > On 01/27/2016 10:39 AM, H.J. Lu wrote: >> >> Here is the updated patch with new testcases. OK for trunk? > > > This is not a complete patch. > > Please update type_is_empty_record_p to use the definition from the recent > discussion:
Here are 3 patches. If all targets should follow x86 backends on passing empty types, only first 2 patches are needed. Otherwise the 3rd patch is needed to limit this to x86 targets. >> An empty type is a type where it and all of its subobjects (recursively) >> are of class, structure, union, or array type. > > > This shouldn't need to rely on the front-end setting a flag. Be sure to > ignore unnamed bit-fields, as they are not subobjects. Done. > I would also suggest having this function abort if the type is > TREE_ADDRESSABLE. > I have bool type_is_empty_type_p (const_tree type) { ... if (TREE_ADDRESSABLE (type)) return false; ... } since any type may be passed to it. Tested on x86-64 with RUNTESTFLAGS="--target_board='unix{-mx32,-m32,}'" OK for trunk? Thanks. -- H.J.
From 49bf71d9e6983b20150061ccd7332d4b73309cad Mon Sep 17 00:00:00 2001 From: "H.J. Lu" <hjl.to...@gmail.com> Date: Sun, 15 Nov 2015 13:19:05 -0800 Subject: [PATCH 1/3] Properly pass empty type for i386 targets According to x86 psABIs, no memory slot nor register should be used to pass or return an object of empty type whose address isn't needed. Middle-end and x86 backend are updated to ignore empty types, whose address aren't needed, for parameter passing and function value return. Other targets need similar changes if they want to follow x86 psABIs. get_ref_base_and_extent is changed to set bitsize to 0 for empty types so that when ref_maybe_used_by_call_p_1 calls get_ref_base_and_extent to get 0 as the maximum size on empty type. Otherwise, find_tail_calls won't perform tail call optimization for functions with empty type parameters, as shown in g++.dg/pr68355.C. This ABI change is enabled only if the ABI level is at least 10, which is updated in GCC 6. gcc/ PR c++/60336 PR middle-end/67239 PR target/68355 * calls.c (initialize_argument_information): Warn empty type if they are used in a variable argument list or aren't the last arguments. Replace targetm.calls.function_arg, targetm.calls.function_incoming_arg and targetm.calls.function_arg_advance with function_arg, function_incoming_arg and function_arg_advance. (expand_call): Replace targetm.calls.function_arg, targetm.calls.function_incoming_arg and targetm.calls.function_arg_advance with function_arg, function_incoming_arg and function_arg_advance. (emit_library_call_value_1): Likewise. (store_one_arg): Use 0 for empty type size. Don't push 0 size argument onto stack. (must_pass_in_stack_var_size_or_pad): Return false for empty type. * dse.c (get_call_args): Replace targetm.calls.function_arg and targetm.calls.function_arg_advance with function_arg and function_arg_advance. * explow.c (hard_function_value): Use 0 for empty type size. * expr.c (block_move_libcall_safe_for_call_parm): Replace targetm.calls.function_arg and targetm.calls.function_arg_advance with function_arg and function_arg_advance. (copy_blkmode_to_reg): Use 0 for empty type size. * function.c (aggregate_value_p): Replace targetm.calls.return_in_memory with return_in_memory. (assign_parm_data_all): Add warn_empty_type. (assign_parms_augmented_arg_list): Set warn_empty_type if empty types are used in a variable argument list or aren't the last arguments. (assign_parm_find_entry_rtl): Warn empty type if warn_empty_type is set. Replace targetm.calls.function_incoming_arg with function_incoming_arg. (assign_parms): Only warn empty type once. Replace targetm.calls.function_arg_advance with function_arg_advance. (gimplify_parameters): Replace targetm.calls.function_arg_advance with function_arg_advance. (locate_and_pad_parm): Use 0 for empty type size. (warn_empty_type): New function. (function_arg_advance): New wrapper function. (function_arg): Likewise. (function_incoming_arg): Likewise. (return_in_memory): Likewise. * target.h (function_arg_advance): New prototype. (function_arg): Likewise. (function_incoming_arg): Likewise. (return_in_memory): Likewise. * targhooks.c (std_gimplify_va_arg_expr): Use 0 for empty type size. * tree-dfa.c (get_ref_base_and_extent): Likewise. * tree.c (is_empty_type): New functiom. (type_is_empty_type_p): Likewise. * tree.h (type_is_empty_type_p): New prototype. * var-tracking.c (prepare_call_arguments): Replace targetm.calls.function_arg and targetm.calls.function_arg_advance with function_arg and function_arg_advance. * config/i386/i386.c (ix86_gimplify_va_arg): Use 0 for empty type size. gcc/testsuite/ PR c++/60336 PR middle-end/67239 PR target/68355 * g++.dg/abi/empty12.C: New test. * g++.dg/abi/empty12.h: Likewise. * g++.dg/abi/empty12a.c: Likewise. * g++.dg/abi/empty13.C: Likewise. * g++.dg/abi/empty13.h: Likewise. * g++.dg/abi/empty13a.c: Likewise. * g++.dg/abi/empty14.C: Likewise. * g++.dg/abi/empty14.h: Likewise. * g++.dg/abi/empty14a.c: Likewise. * g++.dg/abi/empty15.C: Likewise. * g++.dg/abi/empty15.h: Likewise. * g++.dg/abi/empty15a.c: Likewise. * g++.dg/abi/empty16.C: Likewise. * g++.dg/abi/empty16.h: Likewise. * g++.dg/abi/empty16a.c: Likewise. * g++.dg/abi/empty17.C: Likewise. * g++.dg/abi/empty17.h: Likewise. * g++.dg/abi/empty17a.c: Likewise. * g++.dg/pr60336-1.C: Likewise. * g++.dg/pr60336-2.C: Likewise. * g++.dg/pr60336-3.C: Likewise. * g++.dg/pr60336-4.C: Likewise. * g++.dg/pr60336-5.C: Likewise. * g++.dg/pr60336-6.C: Likewise. * g++.dg/pr60336-7.C: Likewise. * g++.dg/pr60336-8.C: Likewise. * g++.dg/pr60336-9.C: Likewise. * g++.dg/pr60336-10.C: Likewise. * g++.dg/pr60336-11.C: Likewise. * g++.dg/pr60336-12.C: Likewise. * g++.dg/pr68355.C: Likewise. --- gcc/calls.c | 99 +++++++++++++++++++---------- gcc/config/i386/i386.c | 3 +- gcc/dse.c | 4 +- gcc/explow.c | 4 +- gcc/expr.c | 8 +-- gcc/function.c | 123 ++++++++++++++++++++++++++++++++---- gcc/target.h | 8 +++ gcc/targhooks.c | 5 +- gcc/testsuite/g++.dg/abi/empty12.C | 17 +++++ gcc/testsuite/g++.dg/abi/empty12.h | 9 +++ gcc/testsuite/g++.dg/abi/empty12a.c | 6 ++ gcc/testsuite/g++.dg/abi/empty13.C | 17 +++++ gcc/testsuite/g++.dg/abi/empty13.h | 9 +++ gcc/testsuite/g++.dg/abi/empty13a.c | 6 ++ gcc/testsuite/g++.dg/abi/empty14.C | 17 +++++ gcc/testsuite/g++.dg/abi/empty14.h | 10 +++ gcc/testsuite/g++.dg/abi/empty14a.c | 6 ++ gcc/testsuite/g++.dg/abi/empty15.C | 17 +++++ gcc/testsuite/g++.dg/abi/empty15.h | 30 +++++++++ gcc/testsuite/g++.dg/abi/empty15a.c | 6 ++ gcc/testsuite/g++.dg/abi/empty16.C | 17 +++++ gcc/testsuite/g++.dg/abi/empty16.h | 16 +++++ gcc/testsuite/g++.dg/abi/empty16a.c | 6 ++ gcc/testsuite/g++.dg/abi/empty17.C | 17 +++++ gcc/testsuite/g++.dg/abi/empty17.h | 27 ++++++++ gcc/testsuite/g++.dg/abi/empty17a.c | 6 ++ gcc/testsuite/g++.dg/pr60336-1.C | 17 +++++ gcc/testsuite/g++.dg/pr60336-10.C | 50 +++++++++++++++ gcc/testsuite/g++.dg/pr60336-11.C | 56 ++++++++++++++++ gcc/testsuite/g++.dg/pr60336-12.C | 57 +++++++++++++++++ gcc/testsuite/g++.dg/pr60336-2.C | 48 ++++++++++++++ gcc/testsuite/g++.dg/pr60336-3.C | 15 +++++ gcc/testsuite/g++.dg/pr60336-4.C | 48 ++++++++++++++ gcc/testsuite/g++.dg/pr60336-5.C | 17 +++++ gcc/testsuite/g++.dg/pr60336-6.C | 17 +++++ gcc/testsuite/g++.dg/pr60336-7.C | 17 +++++ gcc/testsuite/g++.dg/pr60336-8.C | 15 +++++ gcc/testsuite/g++.dg/pr60336-9.C | 28 ++++++++ gcc/testsuite/g++.dg/pr68355.C | 24 +++++++ gcc/tree-dfa.c | 4 +- gcc/tree.c | 39 ++++++++++++ gcc/tree.h | 2 + gcc/var-tracking.c | 18 +++--- 43 files changed, 898 insertions(+), 67 deletions(-) create mode 100644 gcc/testsuite/g++.dg/abi/empty12.C create mode 100644 gcc/testsuite/g++.dg/abi/empty12.h create mode 100644 gcc/testsuite/g++.dg/abi/empty12a.c create mode 100644 gcc/testsuite/g++.dg/abi/empty13.C create mode 100644 gcc/testsuite/g++.dg/abi/empty13.h create mode 100644 gcc/testsuite/g++.dg/abi/empty13a.c create mode 100644 gcc/testsuite/g++.dg/abi/empty14.C create mode 100644 gcc/testsuite/g++.dg/abi/empty14.h create mode 100644 gcc/testsuite/g++.dg/abi/empty14a.c create mode 100644 gcc/testsuite/g++.dg/abi/empty15.C create mode 100644 gcc/testsuite/g++.dg/abi/empty15.h create mode 100644 gcc/testsuite/g++.dg/abi/empty15a.c create mode 100644 gcc/testsuite/g++.dg/abi/empty16.C create mode 100644 gcc/testsuite/g++.dg/abi/empty16.h create mode 100644 gcc/testsuite/g++.dg/abi/empty16a.c create mode 100644 gcc/testsuite/g++.dg/abi/empty17.C create mode 100644 gcc/testsuite/g++.dg/abi/empty17.h create mode 100644 gcc/testsuite/g++.dg/abi/empty17a.c create mode 100644 gcc/testsuite/g++.dg/pr60336-1.C create mode 100644 gcc/testsuite/g++.dg/pr60336-10.C create mode 100644 gcc/testsuite/g++.dg/pr60336-11.C create mode 100644 gcc/testsuite/g++.dg/pr60336-12.C create mode 100644 gcc/testsuite/g++.dg/pr60336-2.C create mode 100644 gcc/testsuite/g++.dg/pr60336-3.C create mode 100644 gcc/testsuite/g++.dg/pr60336-4.C create mode 100644 gcc/testsuite/g++.dg/pr60336-5.C create mode 100644 gcc/testsuite/g++.dg/pr60336-6.C create mode 100644 gcc/testsuite/g++.dg/pr60336-7.C create mode 100644 gcc/testsuite/g++.dg/pr60336-8.C create mode 100644 gcc/testsuite/g++.dg/pr60336-9.C create mode 100644 gcc/testsuite/g++.dg/pr68355.C diff --git a/gcc/calls.c b/gcc/calls.c index 8f573b8..d1b498c 100644 --- a/gcc/calls.c +++ b/gcc/calls.c @@ -1159,7 +1159,13 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED, bitmap_obstack_initialize (NULL); /* In this loop, we consider args in the order they are written. - We fill up ARGS from the back. */ + We fill up ARGS from the back. Warn empty type if they are used + in a variable argument list or they aren't the last arguments. + Set warn_empty_type to true if we don't warn empty type to + avoid walking arguments. */ + bool seen_empty_type = false; + bool warn_empty_type + = (!warn_psabi || stdarg_p (fndecl ? TREE_TYPE (fndecl) : fntype)); i = num_actuals - 1; { @@ -1192,6 +1198,15 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED, { tree argtype = TREE_TYPE (arg); + if (!warn_empty_type) + { + if (argtype != error_mark_node + && type_is_empty_type_p (argtype)) + seen_empty_type = true; + else if (seen_empty_type) + warn_empty_type = true; + } + /* Remember last param with pointer and associate it with following pointer bounds. */ if (CALL_WITH_BOUNDS_P (exp) @@ -1406,8 +1421,13 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED, args[i].unsignedp = unsignedp; args[i].mode = mode; - args[i].reg = targetm.calls.function_arg (args_so_far, mode, type, - argpos < n_named_args); + args[i].reg = function_arg (args_so_far, mode, type, + argpos < n_named_args, + warn_empty_type); + + /* Only warn empty type once. */ + if (type_is_empty_type_p (type)) + warn_empty_type = false; if (args[i].reg && CONST_INT_P (args[i].reg)) { @@ -1420,8 +1440,8 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED, arguments have to go into the incoming registers. */ if (targetm.calls.function_incoming_arg != targetm.calls.function_arg) args[i].tail_call_reg - = targetm.calls.function_incoming_arg (args_so_far, mode, type, - argpos < n_named_args); + = function_incoming_arg (args_so_far, mode, type, + argpos < n_named_args); else args[i].tail_call_reg = args[i].reg; @@ -1482,8 +1502,8 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED, /* Increment ARGS_SO_FAR, which has info about which arg-registers have been used, etc. */ - targetm.calls.function_arg_advance (args_so_far, TYPE_MODE (type), - type, argpos < n_named_args); + function_arg_advance (args_so_far, TYPE_MODE (type), type, + argpos < n_named_args); } } @@ -3335,14 +3355,11 @@ expand_call (tree exp, rtx target, int ignore) /* Set up next argument register. For sibling calls on machines with register windows this should be the incoming register. */ if (pass == 0) - next_arg_reg = targetm.calls.function_incoming_arg (args_so_far, - VOIDmode, - void_type_node, - true); + next_arg_reg = function_incoming_arg (args_so_far, VOIDmode, + void_type_node, true); else - next_arg_reg = targetm.calls.function_arg (args_so_far, - VOIDmode, void_type_node, - true); + next_arg_reg = function_arg (args_so_far, VOIDmode, + void_type_node, true); if (pass == 1 && (return_flags & ERF_RETURNS_ARG)) { @@ -3954,8 +3971,8 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value, argvec[count].mode = Pmode; argvec[count].partial = 0; - argvec[count].reg = targetm.calls.function_arg (args_so_far, - Pmode, NULL_TREE, true); + argvec[count].reg = function_arg (args_so_far, Pmode, NULL_TREE, + true); gcc_assert (targetm.calls.arg_partial_bytes (args_so_far, Pmode, NULL_TREE, 1) == 0); @@ -3972,7 +3989,7 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value, || reg_parm_stack_space > 0) args_size.constant += argvec[count].locate.size.constant; - targetm.calls.function_arg_advance (args_so_far, Pmode, (tree) 0, true); + function_arg_advance (args_so_far, Pmode, (tree) 0, true); count++; } @@ -4037,8 +4054,7 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value, mode = promote_function_mode (NULL_TREE, mode, &unsigned_p, NULL_TREE, 0); argvec[count].mode = mode; argvec[count].value = convert_modes (mode, GET_MODE (val), val, unsigned_p); - argvec[count].reg = targetm.calls.function_arg (args_so_far, mode, - NULL_TREE, true); + argvec[count].reg = function_arg (args_so_far, mode, NULL_TREE, true); argvec[count].partial = targetm.calls.arg_partial_bytes (args_so_far, mode, NULL_TREE, 1); @@ -4067,7 +4083,7 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value, GET_MODE_SIZE (mode) <= UNITS_PER_WORD); #endif - targetm.calls.function_arg_advance (args_so_far, mode, (tree) 0, true); + function_arg_advance (args_so_far, mode, (tree) 0, true); } /* If this machine requires an external definition for library @@ -4414,8 +4430,7 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value, build_function_type (tfom, NULL_TREE), original_args_size.constant, args_size.constant, struct_value_size, - targetm.calls.function_arg (args_so_far, - VOIDmode, void_type_node, true), + function_arg (args_so_far, VOIDmode, void_type_node, true), valreg, old_inhibit_defer_pop + 1, call_fusage, flags, args_so_far); @@ -4850,7 +4865,10 @@ 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_is_empty_type_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. */ @@ -4880,10 +4898,14 @@ 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 @@ -4915,10 +4937,15 @@ 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. */ + bool empty_type = type_is_empty_type_p (TREE_TYPE (pval)); excess = (arg->locate.size.constant - - int_size_in_bytes (TREE_TYPE (pval)) + - (empty_type + ? 0 + : int_size_in_bytes (TREE_TYPE (pval))) + partial); - size_rtx = expand_expr (size_in_bytes (TREE_TYPE (pval)), + size_rtx = expand_expr ((empty_type + ? size_zero_node + : size_in_bytes (TREE_TYPE (pval))), NULL_RTX, TYPE_MODE (sizetype), EXPAND_NORMAL); } @@ -4993,10 +5020,13 @@ 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. @@ -5074,6 +5104,9 @@ must_pass_in_stack_var_size_or_pad (machine_mode mode, const_tree type) if (TREE_ADDRESSABLE (type)) return true; + if (type_is_empty_type_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/config/i386/i386.c b/gcc/config/i386/i386.c index d8a2909..07bb06a 100644 --- a/gcc/config/i386/i386.c +++ b/gcc/config/i386/i386.c @@ -10326,7 +10326,8 @@ 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); + bool empty_type = type && type_is_empty_type_p (type); + size = empty_type ? 0 : int_size_in_bytes (type); rsize = CEIL (size, UNITS_PER_WORD); nat_mode = type_natural_mode (type, NULL, false); diff --git a/gcc/dse.c b/gcc/dse.c index eef5644..ae108a8 100644 --- a/gcc/dse.c +++ b/gcc/dse.c @@ -2356,7 +2356,7 @@ get_call_args (rtx call_insn, tree fn, rtx *args, int nargs) { machine_mode mode = TYPE_MODE (TREE_VALUE (arg)); rtx reg, link, tmp; - reg = targetm.calls.function_arg (args_so_far, mode, NULL_TREE, true); + reg = function_arg (args_so_far, mode, NULL_TREE, true); if (!reg || !REG_P (reg) || GET_MODE (reg) != mode || GET_MODE_CLASS (mode) != MODE_INT) return false; @@ -2390,7 +2390,7 @@ get_call_args (rtx call_insn, tree fn, rtx *args, int nargs) if (tmp) args[idx] = tmp; - targetm.calls.function_arg_advance (args_so_far, mode, NULL_TREE, true); + function_arg_advance (args_so_far, mode, NULL_TREE, true); } if (arg != void_list_node || idx != nargs) return false; diff --git a/gcc/explow.c b/gcc/explow.c index cd7c568..eb2d7a1 100644 --- a/gcc/explow.c +++ b/gcc/explow.c @@ -1856,7 +1856,9 @@ 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 = (type_is_empty_type_p (valtype) + ? 0 + : int_size_in_bytes (valtype)); machine_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 29d22b0..38e9124 100644 --- a/gcc/expr.c +++ b/gcc/expr.c @@ -1199,13 +1199,12 @@ block_move_libcall_safe_for_call_parm (void) for ( ; arg != void_list_node ; arg = TREE_CHAIN (arg)) { machine_mode mode = TYPE_MODE (TREE_VALUE (arg)); - rtx tmp = targetm.calls.function_arg (args_so_far, mode, - NULL_TREE, true); + rtx tmp = function_arg (args_so_far, mode, NULL_TREE, true); if (!tmp || !REG_P (tmp)) return false; if (targetm.calls.arg_partial_bytes (args_so_far, mode, NULL, 1)) return false; - targetm.calls.function_arg_advance (args_so_far, mode, + function_arg_advance (args_so_far, mode, NULL_TREE, true); } } @@ -2207,7 +2206,8 @@ copy_blkmode_to_reg (machine_mode mode, tree src) x = expand_normal (src); - bytes = int_size_in_bytes (TREE_TYPE (src)); + bytes = (type_is_empty_type_p (TREE_TYPE (src)) + ? 0 : int_size_in_bytes (TREE_TYPE (src))); if (bytes == 0) return NULL_RTX; diff --git a/gcc/function.c b/gcc/function.c index 1ac8e26..99bd548 100644 --- a/gcc/function.c +++ b/gcc/function.c @@ -2078,7 +2078,7 @@ aggregate_value_p (const_tree exp, const_tree fntype) if (flag_pcc_struct_return && AGGREGATE_TYPE_P (type)) return 1; - if (targetm.calls.return_in_memory (type, fntype)) + if (return_in_memory (type, fntype)) return 1; /* Make sure we have suitable call-clobbered regs to return @@ -2247,6 +2247,7 @@ struct assign_parm_data_all HOST_WIDE_INT pretend_args_size; HOST_WIDE_INT extra_pretend_bytes; int reg_parm_stack_space; + bool warn_empty_type; }; struct assign_parm_data_one @@ -2412,6 +2413,28 @@ assign_parms_augmented_arg_list (struct assign_parm_data_all *all) if (targetm.calls.split_complex_arg) split_complex_args (&fnargs); + /* Warn empty type if they are used in a variable argument list or + they aren't the last arguments. Set warn_empty_type to true if + we don't warn empty type to avoid walking arguments. */ + bool warn_empty_type = !warn_psabi || stdarg_p (fntype); + if (!warn_empty_type) + { + unsigned int i; + bool seen_empty_type = false; + FOR_EACH_VEC_ELT (fnargs, i, arg) + { + tree type = TREE_TYPE (arg); + if (type != error_mark_node && type_is_empty_type_p (type)) + seen_empty_type = true; + else if (seen_empty_type) + { + warn_empty_type = true; + break; + } + } + } + all->warn_empty_type = warn_empty_type; + return fnargs; } @@ -2528,10 +2551,11 @@ assign_parm_find_entry_rtl (struct assign_parm_data_all *all, return; } - entry_parm = targetm.calls.function_incoming_arg (all->args_so_far, - data->promoted_mode, - data->passed_type, - data->named_arg); + entry_parm = function_incoming_arg (all->args_so_far, + data->promoted_mode, + data->passed_type, + data->named_arg, + all->warn_empty_type); if (entry_parm == 0) data->promoted_mode = data->passed_mode; @@ -2555,9 +2579,9 @@ assign_parm_find_entry_rtl (struct assign_parm_data_all *all, if (targetm.calls.pretend_outgoing_varargs_named (all->args_so_far)) { rtx tem; - tem = targetm.calls.function_incoming_arg (all->args_so_far, - data->promoted_mode, - data->passed_type, true); + tem = function_incoming_arg (all->args_so_far, + data->promoted_mode, + data->passed_type, true); in_regs = tem != NULL; } } @@ -3719,6 +3743,10 @@ assign_parms (tree fndecl) /* Find out where the parameter arrives in this function. */ assign_parm_find_entry_rtl (&all, &data); + /* Only warn empty type once. */ + if (type_is_empty_type_p (data.passed_type)) + all.warn_empty_type = false; + /* Find out where stack space for this parameter might be. */ if (assign_parm_is_stack_parm (&all, &data)) { @@ -3795,8 +3823,8 @@ assign_parms (tree fndecl) } /* Update info on where next arg arrives in registers. */ - targetm.calls.function_arg_advance (all.args_so_far, data.promoted_mode, - data.passed_type, data.named_arg); + function_arg_advance (all.args_so_far, data.promoted_mode, + data.passed_type, data.named_arg); if (POINTER_BOUNDS_TYPE_P (data.passed_type)) bound_no++; @@ -3992,8 +4020,8 @@ gimplify_parameters (void) continue; /* Update info on where next arg arrives in registers. */ - targetm.calls.function_arg_advance (all.args_so_far, data.promoted_mode, - data.passed_type, data.named_arg); + function_arg_advance (all.args_so_far, data.promoted_mode, + data.passed_type, data.named_arg); /* ??? Once upon a time variable_size stuffed parameter list SAVE_EXPRs (amongst others) onto a pending sizes list. This @@ -4136,8 +4164,11 @@ 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)); + if (type) + sizetree = (type_is_empty_type_p (type) + ? size_zero_node : size_in_bytes (type)); + else + sizetree = size_int (GET_MODE_SIZE (passed_mode)); where_pad = 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, @@ -6866,5 +6897,69 @@ make_pass_match_asm_constraints (gcc::context *ctxt) return new pass_match_asm_constraints (ctxt); } +static void +warn_empty_type (void) +{ + if (warn_psabi) + inform (input_location, "the ABI of passing empty type has" + " changed in GCC 6"); +} + +/* Wrapper for targetm.calls.function_arg_advance. */ + +void +function_arg_advance (cumulative_args_t ca, machine_mode mode, + const_tree type, bool named) +{ + if (type && type_is_empty_type_p (type)) + return; + + targetm.calls.function_arg_advance (ca, mode, type, named); +} + +/* Wrapper for targetm.calls.function_arg. */ + +rtx +function_arg (cumulative_args_t ca, machine_mode mode, const_tree type, + bool named, bool warn_empty_type_p) +{ + if (type && type_is_empty_type_p (type)) + { + if (warn_empty_type_p) + warn_empty_type (); + return NULL; + } + + return targetm.calls.function_arg (ca, mode, type, named); +} + +/* Wrapper for targetm.calls.function_incoming_arg. */ + +rtx +function_incoming_arg (cumulative_args_t ca, machine_mode mode, + const_tree type, bool named, + bool warn_empty_type_p) +{ + if (type && type_is_empty_type_p (type)) + { + if (warn_empty_type_p) + warn_empty_type (); + return NULL; + } + + return targetm.calls.function_incoming_arg (ca, mode, type, named); +} + +/* Wrapper for targetm.calls.return_in_memory. */ + +bool +return_in_memory (const_tree type, const_tree fntype) +{ + if (type && type_is_empty_type_p (type)) + return false; + + return targetm.calls.return_in_memory (type, fntype); +} + #include "gt-function.h" diff --git a/gcc/target.h b/gcc/target.h index 43022bd..3968a1f 100644 --- a/gcc/target.h +++ b/gcc/target.h @@ -104,6 +104,14 @@ extern bool target_default_pointer_address_modes_p (void); behavior. */ extern unsigned int get_move_ratio (bool); +extern void function_arg_advance (cumulative_args_t, machine_mode, + const_tree, bool); +extern rtx function_arg (cumulative_args_t, machine_mode, const_tree, + bool, bool = false); +extern rtx function_incoming_arg (cumulative_args_t, machine_mode, + const_tree, bool, bool = false); +extern bool return_in_memory (const_tree, const_tree); + struct stdarg_info; struct spec_info_def; struct hard_reg_set_container; diff --git a/gcc/targhooks.c b/gcc/targhooks.c index 74af91a..f0a6759 100644 --- a/gcc/targhooks.c +++ b/gcc/targhooks.c @@ -1828,9 +1828,12 @@ std_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p, /* Hoist the valist value into a temporary for the moment. */ valist_tmp = get_initialized_tmp_var (valist, pre_p, NULL); + bool empty_type = type_is_empty_type_p (type); + /* va_list pointer is aligned to PARM_BOUNDARY. If argument actually requires greater alignment, we must perform dynamic alignment. */ if (boundary > align + && !empty_type && !integer_zerop (TYPE_SIZE (type))) { t = build2 (MODIFY_EXPR, TREE_TYPE (valist), valist_tmp, @@ -1857,7 +1860,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 = empty_type ? size_zero_node : 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/testsuite/g++.dg/abi/empty12.C b/gcc/testsuite/g++.dg/abi/empty12.C new file mode 100644 index 0000000..ff2909c --- /dev/null +++ b/gcc/testsuite/g++.dg/abi/empty12.C @@ -0,0 +1,17 @@ +// PR c++/60336 +// { dg-do run } +// { dg-options "-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-message "note: the ABI of passing empty type has changed in GCC 6" } + 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..d1e0946 --- /dev/null +++ b/gcc/testsuite/g++.dg/abi/empty13.C @@ -0,0 +1,17 @@ +// PR c++/60336 +// { dg-do run } +// { dg-options "-x c -fabi-version=9" } +// { 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..712d4e8 --- /dev/null +++ b/gcc/testsuite/g++.dg/abi/empty14.C @@ -0,0 +1,17 @@ +// PR c++/60336 +// { dg-do run } +// { dg-options "-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-message "note: the ABI of passing empty type has changed in GCC 6" } + 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..24bf047 --- /dev/null +++ b/gcc/testsuite/g++.dg/abi/empty15.C @@ -0,0 +1,17 @@ +// PR c++/60336 +// { dg-do run } +// { dg-options "-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-message "note: the ABI of passing empty type has changed in GCC 6" } + 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..f058720 --- /dev/null +++ b/gcc/testsuite/g++.dg/abi/empty16.C @@ -0,0 +1,17 @@ +// PR c++/60336 +// { dg-do run } +// { dg-options "-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-message "note: the ABI of passing empty type has changed in GCC 6" } + 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..f6f88c8 --- /dev/null +++ b/gcc/testsuite/g++.dg/abi/empty17.C @@ -0,0 +1,17 @@ +// PR c++/60336 +// { dg-do run } +// { dg-options "-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-message "note: the ABI of passing empty type has changed in GCC 6" } + 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/pr60336-1.C b/gcc/testsuite/g++.dg/pr60336-1.C new file mode 100644 index 0000000..af08638 --- /dev/null +++ b/gcc/testsuite/g++.dg/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-*-* } } } diff --git a/gcc/testsuite/g++.dg/pr60336-10.C b/gcc/testsuite/g++.dg/pr60336-10.C new file mode 100644 index 0000000..6c9c990 --- /dev/null +++ b/gcc/testsuite/g++.dg/pr60336-10.C @@ -0,0 +1,50 @@ +// { dg-do run } +// { 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/pr60336-11.C b/gcc/testsuite/g++.dg/pr60336-11.C new file mode 100644 index 0000000..c92f3d4 --- /dev/null +++ b/gcc/testsuite/g++.dg/pr60336-11.C @@ -0,0 +1,56 @@ +// { dg-do run } +// { 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/pr60336-12.C b/gcc/testsuite/g++.dg/pr60336-12.C new file mode 100644 index 0000000..83a7bb0 --- /dev/null +++ b/gcc/testsuite/g++.dg/pr60336-12.C @@ -0,0 +1,57 @@ +// { dg-do run } +// { 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/pr60336-2.C b/gcc/testsuite/g++.dg/pr60336-2.C new file mode 100644 index 0000000..ad63cee --- /dev/null +++ b/gcc/testsuite/g++.dg/pr60336-2.C @@ -0,0 +1,48 @@ +// { dg-do run } +// { dg-options "-O2" } + +#include <stdarg.h> + +struct dummy { }; + +void +test (struct dummy a, int m, ...) // { dg-message "note: the ABI of passing empty type has changed in GCC 6" } +{ + 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-message "note: the ABI of passing empty type has changed in GCC 6" } + return 0; +} diff --git a/gcc/testsuite/g++.dg/pr60336-3.C b/gcc/testsuite/g++.dg/pr60336-3.C new file mode 100644 index 0000000..9ec4914 --- /dev/null +++ b/gcc/testsuite/g++.dg/pr60336-3.C @@ -0,0 +1,15 @@ +// { dg-do compile } +// { dg-options "-O2" } + +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); // { dg-message "note: the ABI of passing empty type has changed in GCC 6" } + test2 (a0); // { dg-message "note: the ABI of passing empty type has changed in GCC 6" } +} diff --git a/gcc/testsuite/g++.dg/pr60336-4.C b/gcc/testsuite/g++.dg/pr60336-4.C new file mode 100644 index 0000000..8790a66 --- /dev/null +++ b/gcc/testsuite/g++.dg/pr60336-4.C @@ -0,0 +1,48 @@ +// { dg-do run { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } } +// { dg-options "-O2 -fabi-version=9" } + +#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/pr60336-5.C b/gcc/testsuite/g++.dg/pr60336-5.C new file mode 100644 index 0000000..b0c76ad --- /dev/null +++ b/gcc/testsuite/g++.dg/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-*-* } } } diff --git a/gcc/testsuite/g++.dg/pr60336-6.C b/gcc/testsuite/g++.dg/pr60336-6.C new file mode 100644 index 0000000..5879651 --- /dev/null +++ b/gcc/testsuite/g++.dg/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-*-* } } } diff --git a/gcc/testsuite/g++.dg/pr60336-7.C b/gcc/testsuite/g++.dg/pr60336-7.C new file mode 100644 index 0000000..0e5d2ef --- /dev/null +++ b/gcc/testsuite/g++.dg/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-*-* } } } diff --git a/gcc/testsuite/g++.dg/pr60336-8.C b/gcc/testsuite/g++.dg/pr60336-8.C new file mode 100644 index 0000000..2c22f78 --- /dev/null +++ b/gcc/testsuite/g++.dg/pr60336-8.C @@ -0,0 +1,15 @@ +// { dg-do compile } +// { dg-options "-O2" } + +struct dummy { struct{} a[7][3]; }; + +extern void test1 (struct dummy, ...); +extern void (*test2) (struct dummy, ...); + +void +foo () +{ + struct dummy a0; + test1 (a0); // { dg-message "note: the ABI of passing empty type has changed in GCC 6" } + test2 (a0); // { dg-message "note: the ABI of passing empty type has changed in GCC 6" } +} diff --git a/gcc/testsuite/g++.dg/pr60336-9.C b/gcc/testsuite/g++.dg/pr60336-9.C new file mode 100644 index 0000000..4ad333f --- /dev/null +++ b/gcc/testsuite/g++.dg/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-*-* } } } diff --git a/gcc/testsuite/g++.dg/pr68355.C b/gcc/testsuite/g++.dg/pr68355.C new file mode 100644 index 0000000..1354fc4 --- /dev/null +++ b/gcc/testsuite/g++.dg/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/tree-dfa.c b/gcc/tree-dfa.c index 0e98056..b74d34b 100644 --- a/gcc/tree-dfa.c +++ b/gcc/tree-dfa.c @@ -392,7 +392,9 @@ get_ref_base_and_extent (tree exp, HOST_WIDE_INT *poffset, else if (!VOID_TYPE_P (TREE_TYPE (exp))) { machine_mode mode = TYPE_MODE (TREE_TYPE (exp)); - if (mode == BLKmode) + if (type_is_empty_type_p (TREE_TYPE (exp))) + bitsize = 0; + else if (mode == BLKmode) size_tree = TYPE_SIZE (TREE_TYPE (exp)); else bitsize = int (GET_MODE_BITSIZE (mode)); diff --git a/gcc/tree.c b/gcc/tree.c index b8333d4..2a325d2 100644 --- a/gcc/tree.c +++ b/gcc/tree.c @@ -14050,4 +14050,43 @@ combined_fn_name (combined_fn fn) return internal_fn_name (as_internal_fn (fn)); } +/* Returns true if TYPE is a type where it and all of its subobjects + (recursively) are of class, structure, union, or array type. */ + +static bool +is_empty_type (tree type) +{ + if (RECORD_OR_UNION_TYPE_P (type)) + { + tree field; + for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field)) + if (TREE_CODE (field) == FIELD_DECL + && (DECL_NAME (field) + || RECORD_OR_UNION_TYPE_P (TREE_TYPE (field))) + && !is_empty_type (TREE_TYPE (field))) + return false; + return true; + } + else if (TREE_CODE (type) == ARRAY_TYPE) + return is_empty_type (TREE_TYPE (type)); + return false; +} + +/* Return true if TYPE is an empty type whose address isn't needed. */ + +bool +type_is_empty_type_p (const_tree type) +{ + if (!abi_version_at_least (10)) + return false; + + if (type == error_mark_node) + return false; + + if (TREE_ADDRESSABLE (type)) + return false; + + return is_empty_type (TYPE_MAIN_VARIANT (type)); +} + #include "gt-tree.h" diff --git a/gcc/tree.h b/gcc/tree.h index 544a6a1..9686482 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -5399,6 +5399,8 @@ 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 type_is_empty_type_p (const_tree t); + extern location_t set_source_range (tree expr, location_t start, location_t finish); diff --git a/gcc/var-tracking.c b/gcc/var-tracking.c index 9f09d30..15a6fa0 100644 --- a/gcc/var-tracking.c +++ b/gcc/var-tracking.c @@ -6241,10 +6241,10 @@ prepare_call_arguments (basic_block bb, rtx_insn *insn) rtx reg; INIT_CUMULATIVE_ARGS (args_so_far_v, type, NULL_RTX, fndecl, nargs + 1); - reg = targetm.calls.function_arg (args_so_far, mode, - struct_addr, true); - targetm.calls.function_arg_advance (args_so_far, mode, - struct_addr, true); + reg = function_arg (args_so_far, mode, struct_addr, + true); + function_arg_advance (args_so_far, mode, struct_addr, + true); if (reg == NULL_RTX) { for (; link; link = XEXP (link, 1)) @@ -6265,8 +6265,8 @@ prepare_call_arguments (basic_block bb, rtx_insn *insn) machine_mode mode; t = TYPE_ARG_TYPES (type); mode = TYPE_MODE (TREE_VALUE (t)); - this_arg = targetm.calls.function_arg (args_so_far, mode, - TREE_VALUE (t), true); + this_arg = function_arg (args_so_far, mode, + TREE_VALUE (t), true); if (this_arg && !REG_P (this_arg)) this_arg = NULL_RTX; else if (this_arg == NULL_RTX) @@ -6381,8 +6381,7 @@ prepare_call_arguments (basic_block bb, rtx_insn *insn) argtype = build_pointer_type (argtype); mode = TYPE_MODE (argtype); } - reg = targetm.calls.function_arg (args_so_far, mode, - argtype, true); + reg = function_arg (args_so_far, mode, argtype, true); if (TREE_CODE (argtype) == REFERENCE_TYPE && INTEGRAL_TYPE_P (TREE_TYPE (argtype)) && reg @@ -6436,8 +6435,7 @@ prepare_call_arguments (basic_block bb, rtx_insn *insn) } } } - targetm.calls.function_arg_advance (args_so_far, mode, - argtype, true); + function_arg_advance (args_so_far, mode, argtype, true); t = TREE_CHAIN (t); } } -- 2.5.0
From f81f201327d48760ec26b7149d29bb3fc7c49f64 Mon Sep 17 00:00:00 2001 From: "H.J. Lu" <hjl.to...@gmail.com> Date: Wed, 9 Dec 2015 12:48:07 -0800 Subject: [PATCH 2/3] Properly pass empty type for non-i386 targets Use 0 for empty type size in TARGET_GIMPLIFY_VA_ARG_EXPR. PR c++/60336 PR middle-end/67239 PR target/68355 * config/aarch64/aarch64.c (aarch64_gimplify_va_arg_expr): Use 0 for empty type size. * config/mep/mep.c (mep_gimplify_va_arg_expr): Likewise. * config/mips/mips.c (mips_std_gimplify_va_arg_expr): Likewise. (mips_gimplify_va_arg_expr): Likewise. * config/msp430/msp430.c (msp430_gimplify_va_arg_expr): Likewise. * config/pa/pa.c (hppa_gimplify_va_arg_expr): Likewise. * config/rs6000/rs6000.c (rs6000_gimplify_va_arg): Likewise. * config/s390/s390.c (s390_gimplify_va_arg): Likewise. * config/sh/sh.c (sh_gimplify_va_arg_expr): Likewise. * config/sparc/sparc.c (sparc_gimplify_va_arg): Likewise. * config/spu/spu.c (spu_gimplify_va_arg_expr): Likewise. * config/stormy16/stormy16.c (xstormy16_gimplify_va_arg_expr): Likewise. * config/visium/visium.c (visium_gimplify_va_arg): Likewise. * config/xtensa/xtensa.c (xtensa_gimplify_va_arg_expr): Likewise. * config/alpha/alpha.c (alpha_setup_incoming_varargs): Replace targetm.calls.function_arg_advance with function_arg_advance. (alpha_gimplify_va_arg_1): Use 0 for empty type size. * config/microblaze/microblaze.c (microblaze_expand_prologue): Replace targetm.calls.function_arg with function_arg. Replace targetm.calls.function_arg_advance with function_arg_advance. * config/tilegx/tilegx.c (tilegx_setup_incoming_varargs): Replace targetm.calls.function_arg_advance with function_arg_advance. (tilegx_gimplify_va_arg_expr): Use 0 for empty type size. * config/tilepro/tilepro.c (tilepro_setup_incoming_varargs): Replace targetm.calls.function_arg_advance with function_arg_advance. (tilepro_gimplify_va_arg_expr): Use 0 for empty type size. --- gcc/config/aarch64/aarch64.c | 3 ++- gcc/config/alpha/alpha.c | 6 +++--- gcc/config/mep/mep.c | 3 ++- gcc/config/microblaze/microblaze.c | 11 +++++------ gcc/config/mips/mips.c | 6 ++++-- gcc/config/msp430/msp430.c | 3 ++- gcc/config/pa/pa.c | 3 ++- gcc/config/rs6000/rs6000.c | 3 ++- gcc/config/s390/s390.c | 3 ++- gcc/config/sh/sh.c | 3 ++- gcc/config/sparc/sparc.c | 3 ++- gcc/config/spu/spu.c | 3 ++- gcc/config/stormy16/stormy16.c | 6 ++++-- gcc/config/tilegx/tilegx.c | 7 ++++--- gcc/config/tilepro/tilepro.c | 7 ++++--- gcc/config/visium/visium.c | 3 ++- gcc/config/xtensa/xtensa.c | 3 ++- 17 files changed, 46 insertions(+), 30 deletions(-) diff --git a/gcc/config/aarch64/aarch64.c b/gcc/config/aarch64/aarch64.c index 801f95a..ccd204f 100644 --- a/gcc/config/aarch64/aarch64.c +++ b/gcc/config/aarch64/aarch64.c @@ -9674,7 +9674,8 @@ aarch64_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p, stack = build3 (COMPONENT_REF, TREE_TYPE (f_stack), unshare_expr (valist), f_stack, NULL_TREE); - size = int_size_in_bytes (type); + bool empty_type = type && type_is_empty_type_p (type); + size = empty_type ? 0 : int_size_in_bytes (type); align = aarch64_function_arg_alignment (mode, type) / BITS_PER_UNIT; dw_align = false; diff --git a/gcc/config/alpha/alpha.c b/gcc/config/alpha/alpha.c index e023d3b..0b1b8a5 100644 --- a/gcc/config/alpha/alpha.c +++ b/gcc/config/alpha/alpha.c @@ -6117,8 +6117,7 @@ alpha_setup_incoming_varargs (cumulative_args_t pcum, machine_mode mode, CUMULATIVE_ARGS cum = *get_cumulative_args (pcum); /* Skip the current argument. */ - targetm.calls.function_arg_advance (pack_cumulative_args (&cum), mode, type, - true); + function_arg_advance (pack_cumulative_args (&cum), mode, type, true); #if TARGET_ABI_OPEN_VMS /* For VMS, we allocate space for all 6 arg registers plus a count. @@ -6304,7 +6303,8 @@ alpha_gimplify_va_arg_1 (tree type, tree base, tree offset, gimple_seq_add_seq (pre_p, internal_post); /* Update the offset field. */ - type_size = TYPE_SIZE_UNIT (TYPE_MAIN_VARIANT (type)); + bool empty_type = type && type_is_empty_type_p (type); + type_size = empty_type ? 0 : TYPE_SIZE_UNIT (TYPE_MAIN_VARIANT (type)); if (type_size == NULL || TREE_OVERFLOW (type_size)) t = size_zero_node; else diff --git a/gcc/config/mep/mep.c b/gcc/config/mep/mep.c index 9c4cd86..4ce09eb 100644 --- a/gcc/config/mep/mep.c +++ b/gcc/config/mep/mep.c @@ -3525,7 +3525,8 @@ mep_gimplify_va_arg_expr (tree valist, tree type, ivc2_vec = TARGET_IVC2 && VECTOR_TYPE_P (type); - size = int_size_in_bytes (type); + bool empty_type = type && type_is_empty_type_p (type); + size = empty_type ? 0 : int_size_in_bytes (type); by_reference = (size > (ivc2_vec ? 8 : 4)) || (size <= 0); if (by_reference) diff --git a/gcc/config/microblaze/microblaze.c b/gcc/config/microblaze/microblaze.c index baff67a..e144ff1 100644 --- a/gcc/config/microblaze/microblaze.c +++ b/gcc/config/microblaze/microblaze.c @@ -2798,8 +2798,8 @@ microblaze_expand_prologue (void) passed_mode = Pmode; } - entry_parm = targetm.calls.function_arg (args_so_far, passed_mode, - passed_type, true); + entry_parm = function_arg (args_so_far, passed_mode, passed_type, + true); if (entry_parm) { @@ -2819,8 +2819,7 @@ microblaze_expand_prologue (void) break; } - targetm.calls.function_arg_advance (args_so_far, passed_mode, - passed_type, true); + function_arg_advance (args_so_far, passed_mode, passed_type, true); next_arg = TREE_CHAIN (cur_arg); if (next_arg == 0) @@ -2834,8 +2833,8 @@ microblaze_expand_prologue (void) /* Split parallel insn into a sequence of insns. */ - next_arg_reg = targetm.calls.function_arg (args_so_far, VOIDmode, - void_type_node, true); + next_arg_reg = function_arg (args_so_far, VOIDmode, void_type_node, + true); if (next_arg_reg != 0 && GET_CODE (next_arg_reg) == PARALLEL) { rtvec adjust = XVEC (next_arg_reg, 0); diff --git a/gcc/config/mips/mips.c b/gcc/config/mips/mips.c index 5af3d1e..f9165e6 100644 --- a/gcc/config/mips/mips.c +++ b/gcc/config/mips/mips.c @@ -6308,7 +6308,8 @@ mips_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); + bool empty_type = type && type_is_empty_type_p (type); + type_size = empty_type ? 0 : size_in_bytes (type); rounded_size = round_up (type_size, align); /* Reduce rounded_size so it's sharable with the postqueue. */ @@ -6397,7 +6398,8 @@ mips_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p, ovfl = build3 (COMPONENT_REF, TREE_TYPE (f_ovfl), valist, f_ovfl, NULL_TREE); - size = int_size_in_bytes (type); + bool empty_type = type && type_is_empty_type_p (type); + size = empty_type ? 0 : int_size_in_bytes (type); if (GET_MODE_CLASS (TYPE_MODE (type)) == MODE_FLOAT && GET_MODE_SIZE (TYPE_MODE (type)) <= UNITS_PER_FPVALUE) diff --git a/gcc/config/msp430/msp430.c b/gcc/config/msp430/msp430.c index 6f63116..b1bd318 100644 --- a/gcc/config/msp430/msp430.c +++ b/gcc/config/msp430/msp430.c @@ -1465,7 +1465,8 @@ msp430_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); + bool empty_type = type && type_is_empty_type_p (type); + type_size = empty_type ? 0 : 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/config/pa/pa.c b/gcc/config/pa/pa.c index 8b1c832..e19e00b 100644 --- a/gcc/config/pa/pa.c +++ b/gcc/config/pa/pa.c @@ -6342,7 +6342,8 @@ hppa_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p, type = ptr; ptr = build_pointer_type (type); } - size = int_size_in_bytes (type); + bool empty_type = type && type_is_empty_type_p (type); + size = empty_type ? 0 : int_size_in_bytes (type); valist_type = TREE_TYPE (valist); /* Args grow down. Not handled by generic routines. */ diff --git a/gcc/config/rs6000/rs6000.c b/gcc/config/rs6000/rs6000.c index 5b03f9e..0000d9b 100644 --- a/gcc/config/rs6000/rs6000.c +++ b/gcc/config/rs6000/rs6000.c @@ -12069,7 +12069,8 @@ rs6000_gimplify_va_arg (tree valist, tree type, gimple_seq *pre_p, sav = build3 (COMPONENT_REF, TREE_TYPE (f_sav), unshare_expr (valist), f_sav, NULL_TREE); - size = int_size_in_bytes (type); + bool empty_type = type && type_is_empty_type_p (type); + size = empty_type ? 0 : int_size_in_bytes (type); rsize = (size + 3) / 4; align = 1; diff --git a/gcc/config/s390/s390.c b/gcc/config/s390/s390.c index 8924367..076f381 100644 --- a/gcc/config/s390/s390.c +++ b/gcc/config/s390/s390.c @@ -11999,7 +11999,8 @@ s390_gimplify_va_arg (tree valist, tree type, gimple_seq *pre_p, valist = unshare_expr (valist); ovf = build3 (COMPONENT_REF, TREE_TYPE (f_ovf), valist, f_ovf, NULL_TREE); - size = int_size_in_bytes (type); + bool empty_type = type && type_is_empty_type_p (type); + size = empty_type ? 0 : int_size_in_bytes (type); s390_check_type_for_vector_abi (type, true, false); diff --git a/gcc/config/sh/sh.c b/gcc/config/sh/sh.c index 8c8fe3c..b4a2691 100644 --- a/gcc/config/sh/sh.c +++ b/gcc/config/sh/sh.c @@ -8767,7 +8767,8 @@ sh_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p, if (pass_by_ref) type = build_pointer_type (type); - size = int_size_in_bytes (type); + bool empty_type = type && type_is_empty_type_p (type); + size = empty_type ? 0 : int_size_in_bytes (type); rsize = (size + UNITS_PER_WORD - 1) & -UNITS_PER_WORD; pptr_type_node = build_pointer_type (ptr_type_node); diff --git a/gcc/config/sparc/sparc.c b/gcc/config/sparc/sparc.c index 080be61..efbab37 100644 --- a/gcc/config/sparc/sparc.c +++ b/gcc/config/sparc/sparc.c @@ -7336,7 +7336,8 @@ sparc_gimplify_va_arg (tree valist, tree type, gimple_seq *pre_p, else { indirect = false; - size = int_size_in_bytes (type); + bool empty_type = type && type_is_empty_type_p (type); + size = empty_type ? 0 : int_size_in_bytes (type); rsize = ROUND_UP (size, UNITS_PER_WORD); align = 0; diff --git a/gcc/config/spu/spu.c b/gcc/config/spu/spu.c index 401c295..432f8e2 100644 --- a/gcc/config/spu/spu.c +++ b/gcc/config/spu/spu.c @@ -4028,7 +4028,8 @@ spu_gimplify_va_arg_expr (tree valist, tree type, gimple_seq * pre_p, false); if (pass_by_reference_p) type = build_pointer_type (type); - size = int_size_in_bytes (type); + bool empty_type = type && type_is_empty_type_p (type); + size = empty_type ? 0 : int_size_in_bytes (type); rsize = ((size + UNITS_PER_WORD - 1) / UNITS_PER_WORD) * UNITS_PER_WORD; /* build conditional expression to calculate addr. The expression diff --git a/gcc/config/stormy16/stormy16.c b/gcc/config/stormy16/stormy16.c index 50af15b..8e9676f 100644 --- a/gcc/config/stormy16/stormy16.c +++ b/gcc/config/stormy16/stormy16.c @@ -1337,8 +1337,10 @@ xstormy16_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p, count = build3 (COMPONENT_REF, TREE_TYPE (f_count), valist, f_count, NULL_TREE); + bool empty_type = type && type_is_empty_type_p (type); must_stack = targetm.calls.must_pass_in_stack (TYPE_MODE (type), type); - size_tree = round_up (size_in_bytes (type), UNITS_PER_WORD); + size_tree = round_up (empty_type ? integer_zero_node : size_in_bytes (type), + UNITS_PER_WORD); gimplify_expr (&size_tree, pre_p, NULL, is_gimple_val, fb_rvalue); size_of_reg_args = NUM_ARGUMENT_REGISTERS * UNITS_PER_WORD; @@ -1374,7 +1376,7 @@ xstormy16_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p, /* Arguments larger than a word might need to skip over some registers, since arguments are either passed entirely in registers or entirely on the stack. */ - size = PUSH_ROUNDING (int_size_in_bytes (type)); + size = PUSH_ROUNDING (empty_type ? 0 : int_size_in_bytes (type)); if (size > 2 || size < 0 || must_stack) { tree r, u; diff --git a/gcc/config/tilegx/tilegx.c b/gcc/config/tilegx/tilegx.c index 06c832c..001a93e 100644 --- a/gcc/config/tilegx/tilegx.c +++ b/gcc/config/tilegx/tilegx.c @@ -396,8 +396,8 @@ tilegx_setup_incoming_varargs (cumulative_args_t cum, /* The caller has advanced CUM up to, but not beyond, the last named argument. Advance a local copy of CUM past the last "real" named argument, to find out how many registers are left over. */ - targetm.calls.function_arg_advance (pack_cumulative_args (&local_cum), - mode, type, true); + function_arg_advance (pack_cumulative_args (&local_cum), mode, type, + true); first_reg = local_cum; if (local_cum < TILEGX_NUM_ARG_REGS) @@ -473,7 +473,8 @@ tilegx_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p, if (pass_by_reference_p) type = build_pointer_type (type); - size = int_size_in_bytes (type); + bool empty_type = type && type_is_empty_type_p (type); + size = empty_type ? 0 : int_size_in_bytes (type); rsize = ((size + UNITS_PER_WORD - 1) / UNITS_PER_WORD) * UNITS_PER_WORD; /* If the alignment of the type is greater than the default for a diff --git a/gcc/config/tilepro/tilepro.c b/gcc/config/tilepro/tilepro.c index 628cd04..6cadfd6 100644 --- a/gcc/config/tilepro/tilepro.c +++ b/gcc/config/tilepro/tilepro.c @@ -348,8 +348,8 @@ tilepro_setup_incoming_varargs (cumulative_args_t cum, /* The caller has advanced CUM up to, but not beyond, the last named argument. Advance a local copy of CUM past the last "real" named argument, to find out how many registers are left over. */ - targetm.calls.function_arg_advance (pack_cumulative_args (&local_cum), - mode, type, true); + function_arg_advance (pack_cumulative_args (&local_cum), mode, type, + true); first_reg = local_cum; if (local_cum < TILEPRO_NUM_ARG_REGS) @@ -421,7 +421,8 @@ tilepro_gimplify_va_arg_expr (tree valist, tree type, gimple_seq * pre_p, if (pass_by_reference_p) type = build_pointer_type (type); - size = int_size_in_bytes (type); + bool empty_type = type && type_is_empty_type_p (type); + size = empty_type ? 0 : int_size_in_bytes (type); rsize = ((size + UNITS_PER_WORD - 1) / UNITS_PER_WORD) * UNITS_PER_WORD; /* If the alignment of the type is greater than the default for a diff --git a/gcc/config/visium/visium.c b/gcc/config/visium/visium.c index cd28f9b..d10853a 100644 --- a/gcc/config/visium/visium.c +++ b/gcc/config/visium/visium.c @@ -1586,7 +1586,8 @@ visium_gimplify_va_arg (tree valist, tree type, gimple_seq *pre_p, return build_va_arg_indirect_ref (t); } - size = int_size_in_bytes (type); + bool empty_type = type && type_is_empty_type_p (type); + size = empty_type ? 0 : int_size_in_bytes (type); rsize = (size + UNITS_PER_WORD - 1) & -UNITS_PER_WORD; f_ovfl = TYPE_FIELDS (va_list_type_node); f_gbase = TREE_CHAIN (f_ovfl); diff --git a/gcc/config/xtensa/xtensa.c b/gcc/config/xtensa/xtensa.c index 64d089b..30359eb 100644 --- a/gcc/config/xtensa/xtensa.c +++ b/gcc/config/xtensa/xtensa.c @@ -3161,7 +3161,8 @@ xtensa_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p, ndx = build3 (COMPONENT_REF, TREE_TYPE (f_ndx), unshare_expr (valist), f_ndx, NULL_TREE); - type_size = size_in_bytes (type); + bool empty_type = type && type_is_empty_type_p (type); + type_size = empty_type ? 0 : size_in_bytes (type); va_size = round_up (type_size, UNITS_PER_WORD); gimplify_expr (&va_size, pre_p, NULL, is_gimple_val, fb_rvalue); -- 2.5.0
From 56fac78dffedad6abfad7f86f90a25b8c930eb6f Mon Sep 17 00:00:00 2001 From: "H.J. Lu" <hjl.to...@gmail.com> Date: Sun, 7 Feb 2016 09:30:58 -0800 Subject: [PATCH 3/3] Add a call target hook: function_skip_empty_type_p A function call target hook, function_skip_empty_type_p, is added, which defaults to return false. This ABI change is enabled only if the ABI level is at least 10, which is updated in GCC 6, and the target hook TARGET_FUNCTION_SKIP_EMPTY_TYPE_P returns true, which is used by target whose psABI specifies that no memory slot nor register should be used to pass or return an object of empty type whose address isn't needed. gcc/ PR c++/60336 PR middle-end/67239 PR target/68355 * target.def (calls): Add function_skip_empty_type_p. * tree.c (type_is_empty_type_p): Return false if targetm.calls.function_skip_empty_type_p return false. * config/i386/i386.c (TARGET_FUNCTION_SKIP_EMPTY_TYPE_P): New hook to return true. * doc/tm.texi.in (TARGET_FUNCTION_SKIP_EMPTY_TYPE_P): New hook. * doc/tm.texi: Regenerated. gcc/testsuite/ PR c++/60336 PR middle-end/67239 PR target/68355 * g++.dg/abi/empty12.C: Enabled only for x86 targets. * g++.dg/abi/empty12.h: Likewise. * g++.dg/abi/empty12a.c: Likewise. * g++.dg/abi/empty13.C: Likewise. * g++.dg/abi/empty13.h: Likewise. * g++.dg/abi/empty13a.c: Likewise. * g++.dg/abi/empty14.C: Likewise. * g++.dg/abi/empty14.h: Likewise. * g++.dg/abi/empty14a.c: Likewise. * g++.dg/abi/empty15.C: Likewise. * g++.dg/abi/empty15.h: Likewise. * g++.dg/abi/empty15a.c: Likewise. * g++.dg/abi/empty16.C: Likewise. * g++.dg/abi/empty16.h: Likewise. * g++.dg/abi/empty16a.c: Likewise. * g++.dg/abi/empty17.C: Likewise. * g++.dg/abi/empty17.h: Likewise. * g++.dg/abi/empty17a.c: Likewise. * g++.dg/pr60336-1.C: Likewise. * g++.dg/pr60336-2.C: Likewise. * g++.dg/pr60336-3.C: Likewise. * g++.dg/pr60336-4.C: Likewise. * g++.dg/pr60336-5.C: Likewise. * g++.dg/pr60336-6.C: Likewise. * g++.dg/pr60336-7.C: Likewise. * g++.dg/pr60336-8.C: Likewise. * g++.dg/pr60336-9.C: Likewise. * g++.dg/pr60336-10.C: Likewise. * g++.dg/pr60336-11.C: Likewise. * g++.dg/pr60336-12.C: Likewise. * g++.dg/pr68355.C: Likewise. --- gcc/config/i386/i386.c | 3 +++ gcc/doc/tm.texi | 6 ++++++ gcc/doc/tm.texi.in | 2 ++ gcc/target.def | 10 ++++++++++ gcc/testsuite/g++.dg/abi/empty12.C | 2 +- gcc/testsuite/g++.dg/abi/empty13.C | 2 +- gcc/testsuite/g++.dg/abi/empty14.C | 2 +- gcc/testsuite/g++.dg/abi/empty15.C | 2 +- gcc/testsuite/g++.dg/abi/empty16.C | 2 +- gcc/testsuite/g++.dg/abi/empty17.C | 2 +- gcc/testsuite/g++.dg/pr60336-1.C | 2 +- gcc/testsuite/g++.dg/pr60336-10.C | 2 +- gcc/testsuite/g++.dg/pr60336-11.C | 2 +- gcc/testsuite/g++.dg/pr60336-12.C | 2 +- gcc/testsuite/g++.dg/pr60336-2.C | 2 +- gcc/testsuite/g++.dg/pr60336-3.C | 2 +- gcc/testsuite/g++.dg/pr60336-5.C | 2 +- gcc/testsuite/g++.dg/pr60336-6.C | 2 +- gcc/testsuite/g++.dg/pr60336-7.C | 2 +- gcc/testsuite/g++.dg/pr60336-8.C | 2 +- gcc/testsuite/g++.dg/pr60336-9.C | 2 +- gcc/testsuite/g++.dg/pr68355.C | 2 +- gcc/tree.c | 3 +++ 23 files changed, 42 insertions(+), 18 deletions(-) diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c index 07bb06a..c4618b4 100644 --- a/gcc/config/i386/i386.c +++ b/gcc/config/i386/i386.c @@ -54551,6 +54551,9 @@ ix86_addr_space_zero_address_valid (addr_space_t as) #undef TARGET_PROMOTE_FUNCTION_MODE #define TARGET_PROMOTE_FUNCTION_MODE ix86_promote_function_mode +#undef TARGET_FUNCTION_SKIP_EMPTY_TYPE_P +#define TARGET_FUNCTION_SKIP_EMPTY_TYPE_P hook_bool_void_true + #undef TARGET_OVERRIDE_OPTIONS_AFTER_CHANGE #define TARGET_OVERRIDE_OPTIONS_AFTER_CHANGE ix86_override_options_after_change diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi index 8b3bccd..9cd0451 100644 --- a/gcc/doc/tm.texi +++ b/gcc/doc/tm.texi @@ -969,6 +969,12 @@ also define the hook to @code{default_promote_function_mode_always_promote} if you would like to apply the same rules given by @code{PROMOTE_MODE}. @end deftypefn +@deftypefn {Target Hook} bool TARGET_FUNCTION_SKIP_EMPTY_TYPE_P (void) +This hook should return true if no memory slot nor register should be +used to pass or return an object of empty type. The default is to +return @code{false}. +@end deftypefn + @defmac PARM_BOUNDARY Normal alignment required for function parameters on the stack, in bits. All stack parameters receive at least this much alignment diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in index f31c763..3b978e6 100644 --- a/gcc/doc/tm.texi.in +++ b/gcc/doc/tm.texi.in @@ -923,6 +923,8 @@ Do not define this macro if it would never modify @var{m}. @hook TARGET_PROMOTE_FUNCTION_MODE +@hook TARGET_FUNCTION_SKIP_EMPTY_TYPE_P + @defmac PARM_BOUNDARY Normal alignment required for function parameters on the stack, in bits. All stack parameters receive at least this much alignment diff --git a/gcc/target.def b/gcc/target.def index 5c8e4e1..b6d28cf 100644 --- a/gcc/target.def +++ b/gcc/target.def @@ -4035,6 +4035,16 @@ supported by the target.", /* Members of struct call have no special macro prefix. */ HOOK_VECTOR (TARGET_CALLS, calls) +/* Returns true if no memory slot nor register should be used to pass + or return an object of empty type. */ +DEFHOOK +(function_skip_empty_type_p, + "This hook should return true if no memory slot nor register should be\n\ +used to pass or return an object of empty type. The default is to\n\ +return @code{false}.", + bool, (void), + hook_bool_void_false) + DEFHOOK (promote_function_mode, "Like @code{PROMOTE_MODE}, but it is applied to outgoing function arguments or\n\ diff --git a/gcc/testsuite/g++.dg/abi/empty12.C b/gcc/testsuite/g++.dg/abi/empty12.C index ff2909c..3e97af6 100644 --- a/gcc/testsuite/g++.dg/abi/empty12.C +++ b/gcc/testsuite/g++.dg/abi/empty12.C @@ -1,5 +1,5 @@ // PR c++/60336 -// { dg-do run } +// { dg-do run { target i?86-*-* x86_64-*-* } } // { dg-options "-x c" } // { dg-additional-sources "empty12a.c" } // { dg-prune-output "command line option" } diff --git a/gcc/testsuite/g++.dg/abi/empty13.C b/gcc/testsuite/g++.dg/abi/empty13.C index d1e0946..d2e297e 100644 --- a/gcc/testsuite/g++.dg/abi/empty13.C +++ b/gcc/testsuite/g++.dg/abi/empty13.C @@ -1,5 +1,5 @@ // PR c++/60336 -// { dg-do run } +// { dg-do run { target i?86-*-* x86_64-*-* } } // { dg-options "-x c -fabi-version=9" } // { dg-additional-sources "empty13a.c" } // { dg-prune-output "command line option" } diff --git a/gcc/testsuite/g++.dg/abi/empty14.C b/gcc/testsuite/g++.dg/abi/empty14.C index 712d4e8..2085188 100644 --- a/gcc/testsuite/g++.dg/abi/empty14.C +++ b/gcc/testsuite/g++.dg/abi/empty14.C @@ -1,5 +1,5 @@ // PR c++/60336 -// { dg-do run } +// { dg-do run { target i?86-*-* x86_64-*-* } } // { dg-options "-x c" } // { dg-additional-sources "empty14a.c" } // { dg-prune-output "command line option" } diff --git a/gcc/testsuite/g++.dg/abi/empty15.C b/gcc/testsuite/g++.dg/abi/empty15.C index 24bf047..34f78ae 100644 --- a/gcc/testsuite/g++.dg/abi/empty15.C +++ b/gcc/testsuite/g++.dg/abi/empty15.C @@ -1,5 +1,5 @@ // PR c++/60336 -// { dg-do run } +// { dg-do run { target i?86-*-* x86_64-*-* } } // { dg-options "-x c" } // { dg-additional-sources "empty15a.c" } // { dg-prune-output "command line option" } diff --git a/gcc/testsuite/g++.dg/abi/empty16.C b/gcc/testsuite/g++.dg/abi/empty16.C index f058720..200d2ab 100644 --- a/gcc/testsuite/g++.dg/abi/empty16.C +++ b/gcc/testsuite/g++.dg/abi/empty16.C @@ -1,5 +1,5 @@ // PR c++/60336 -// { dg-do run } +// { dg-do run { target i?86-*-* x86_64-*-* } } // { dg-options "-x c" } // { dg-additional-sources "empty16a.c" } // { dg-prune-output "command line option" } diff --git a/gcc/testsuite/g++.dg/abi/empty17.C b/gcc/testsuite/g++.dg/abi/empty17.C index f6f88c8..7221101 100644 --- a/gcc/testsuite/g++.dg/abi/empty17.C +++ b/gcc/testsuite/g++.dg/abi/empty17.C @@ -1,5 +1,5 @@ // PR c++/60336 -// { dg-do run } +// { dg-do run { target i?86-*-* x86_64-*-* } } // { dg-options "-x c" } // { dg-additional-sources "empty17a.c" } // { dg-prune-output "command line option" } diff --git a/gcc/testsuite/g++.dg/pr60336-1.C b/gcc/testsuite/g++.dg/pr60336-1.C index af08638..946f8accd 100644 --- a/gcc/testsuite/g++.dg/pr60336-1.C +++ b/gcc/testsuite/g++.dg/pr60336-1.C @@ -1,4 +1,4 @@ -// { dg-do compile } +// { dg-do compile { target i?86-*-* x86_64-*-* } } // { dg-options "-O2 -std=c++11 -fno-pic" } // { dg-require-effective-target fpic } diff --git a/gcc/testsuite/g++.dg/pr60336-10.C b/gcc/testsuite/g++.dg/pr60336-10.C index 6c9c990..57707fc 100644 --- a/gcc/testsuite/g++.dg/pr60336-10.C +++ b/gcc/testsuite/g++.dg/pr60336-10.C @@ -1,4 +1,4 @@ -// { dg-do run } +// { dg-do run { target i?86-*-* x86_64-*-* } } // { dg-options "-O2" } #include <stdarg.h> diff --git a/gcc/testsuite/g++.dg/pr60336-11.C b/gcc/testsuite/g++.dg/pr60336-11.C index c92f3d4..32000b2 100644 --- a/gcc/testsuite/g++.dg/pr60336-11.C +++ b/gcc/testsuite/g++.dg/pr60336-11.C @@ -1,4 +1,4 @@ -// { dg-do run } +// { dg-do run { target i?86-*-* x86_64-*-* } } // { dg-options "-O2" } #include <stdarg.h> diff --git a/gcc/testsuite/g++.dg/pr60336-12.C b/gcc/testsuite/g++.dg/pr60336-12.C index 83a7bb0..febcc6f 100644 --- a/gcc/testsuite/g++.dg/pr60336-12.C +++ b/gcc/testsuite/g++.dg/pr60336-12.C @@ -1,4 +1,4 @@ -// { dg-do run } +// { dg-do run { target i?86-*-* x86_64-*-* } } // { dg-options "-O2" } #include <stdarg.h> diff --git a/gcc/testsuite/g++.dg/pr60336-2.C b/gcc/testsuite/g++.dg/pr60336-2.C index ad63cee..dcde99c 100644 --- a/gcc/testsuite/g++.dg/pr60336-2.C +++ b/gcc/testsuite/g++.dg/pr60336-2.C @@ -1,4 +1,4 @@ -// { dg-do run } +// { dg-do run { target i?86-*-* x86_64-*-* } } // { dg-options "-O2" } #include <stdarg.h> diff --git a/gcc/testsuite/g++.dg/pr60336-3.C b/gcc/testsuite/g++.dg/pr60336-3.C index 9ec4914..d126cb5 100644 --- a/gcc/testsuite/g++.dg/pr60336-3.C +++ b/gcc/testsuite/g++.dg/pr60336-3.C @@ -1,4 +1,4 @@ -// { dg-do compile } +// { dg-do compile { target i?86-*-* x86_64-*-* } } // { dg-options "-O2" } struct dummy { struct{}__attribute__((aligned (4))) a[7]; }; diff --git a/gcc/testsuite/g++.dg/pr60336-5.C b/gcc/testsuite/g++.dg/pr60336-5.C index b0c76ad..a051f6e 100644 --- a/gcc/testsuite/g++.dg/pr60336-5.C +++ b/gcc/testsuite/g++.dg/pr60336-5.C @@ -1,4 +1,4 @@ -// { dg-do compile } +// { dg-do compile { target i?86-*-* x86_64-*-* } } // { dg-options "-O2 -std=c++11 -fno-pic" } // { dg-require-effective-target fpic } diff --git a/gcc/testsuite/g++.dg/pr60336-6.C b/gcc/testsuite/g++.dg/pr60336-6.C index 5879651..22728d3 100644 --- a/gcc/testsuite/g++.dg/pr60336-6.C +++ b/gcc/testsuite/g++.dg/pr60336-6.C @@ -1,4 +1,4 @@ -// { dg-do compile } +// { dg-do compile { target i?86-*-* x86_64-*-* } } // { dg-options "-O2 -std=c++11 -fno-pic" } // { dg-require-effective-target fpic } diff --git a/gcc/testsuite/g++.dg/pr60336-7.C b/gcc/testsuite/g++.dg/pr60336-7.C index 0e5d2ef..5fc8320 100644 --- a/gcc/testsuite/g++.dg/pr60336-7.C +++ b/gcc/testsuite/g++.dg/pr60336-7.C @@ -1,4 +1,4 @@ -// { dg-do compile } +// { dg-do compile { target i?86-*-* x86_64-*-* } } // { dg-options "-O2 -std=c++11 -fno-pic" } // { dg-require-effective-target fpic } diff --git a/gcc/testsuite/g++.dg/pr60336-8.C b/gcc/testsuite/g++.dg/pr60336-8.C index 2c22f78..e88dbb4 100644 --- a/gcc/testsuite/g++.dg/pr60336-8.C +++ b/gcc/testsuite/g++.dg/pr60336-8.C @@ -1,4 +1,4 @@ -// { dg-do compile } +// { dg-do compile { target i?86-*-* x86_64-*-* } } // { dg-options "-O2" } struct dummy { struct{} a[7][3]; }; diff --git a/gcc/testsuite/g++.dg/pr60336-9.C b/gcc/testsuite/g++.dg/pr60336-9.C index 4ad333f..3d768c0 100644 --- a/gcc/testsuite/g++.dg/pr60336-9.C +++ b/gcc/testsuite/g++.dg/pr60336-9.C @@ -1,4 +1,4 @@ -// { dg-do compile } +// { dg-do compile { target i?86-*-* x86_64-*-* } } // { dg-options "-O2 -std=c++11 -fno-pic" } // { dg-require-effective-target fpic } diff --git a/gcc/testsuite/g++.dg/pr68355.C b/gcc/testsuite/g++.dg/pr68355.C index 1354fc4..5c2f0da 100644 --- a/gcc/testsuite/g++.dg/pr68355.C +++ b/gcc/testsuite/g++.dg/pr68355.C @@ -1,4 +1,4 @@ -// { dg-do compile } +// { dg-do compile { target i?86-*-* x86_64-*-* } } // { dg-options "-O2 -std=c++11 -fno-pic" } // { dg-require-effective-target fpic } diff --git a/gcc/tree.c b/gcc/tree.c index 2a325d2..1b86141 100644 --- a/gcc/tree.c +++ b/gcc/tree.c @@ -14077,6 +14077,9 @@ is_empty_type (tree type) bool type_is_empty_type_p (const_tree type) { + if (!targetm.calls.function_skip_empty_type_p ()) + return false; + if (!abi_version_at_least (10)) return false; -- 2.5.0