From 1a690882987e3fc940743d3368b8993acd9ec8a6 Mon Sep 17 00:00:00 2001 From: "H.J. Lu" Date: Sun, 15 Nov 2015 13:19:05 -0800 Subject: [PATCH 1/2] Add TYPE_EMPTY_RECORD for C++ empty class Empty record should be returned and passed the same way in C and C++. This patch overloads a bit, side_effects_flag, in tree_base for C++ empty class. Middle-end and x86 backend are updated to ignore empty records for parameter passing and function value return. Other targets may need similar changes. get_ref_base_and_extent is changed to set bitsize to 0 for empty records 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 record. Otherwise, find_tail_calls won't perform tail call optimization for functions with empty record parameters, as shown in g++.dg/pr60336-1.C and g++.dg/pr60336-2.C. gcc/ PR c++/60336 PR middle-end/67239 PR target/68355 * calls.c (initialize_argument_information): Warn empty record 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 record size. Don't push 0 size argument onto stack. (must_pass_in_stack_var_size_or_pad): Return false for empty record. * dse.c (get_call_args): Replace targetm.calls.function_arg and targetm.calls.function_arg_advance with function_arg and function_arg_advance. * expr.c (block_move_libcall_safe_for_call_parm): Likewise. * function.c (aggregate_value_p): Replace targetm.calls.return_in_memory with return_in_memory. (assign_parm_data_all): Add warn_empty_record. (assign_parms_augmented_arg_list): Set warn_empty_record if empty records are used in a variable argument list or aren't the last arguments. (assign_parm_find_entry_rtl): Warn empty record if warn_empty_record is set. Replace targetm.calls.function_incoming_arg with function_incoming_arg. (assign_parms): Only warn empty record 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 record size. (warn_empty_record): New function. (function_arg_advance): New wrapper function. (function_arg): Likewise. (function_incoming_arg): Likewise. (return_in_memory): Likewise. * lto-streamer-out.c (hash_tree): Call hstate.add_flag with TYPE_EMPTY_RECORD for types. * print-tree.c (print_node): Also handle TYPE_EMPTY_RECORD. * ubsan.c (ubsan_type_descriptor): 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 record size. * tree-dfa.c (get_ref_base_and_extent): Likewise. * tree-core.h (tree_base): Mention TYPE_EMPTY_RECORD in comments for side_effects_flag. * tree-streamer-in.c (unpack_ts_base_value_fields): Stream in TYPE_EMPTY_RECORD for types. * tree-streamer-out.c (pack_ts_base_value_fields): Stream out TYPE_EMPTY_RECORD for types. * tree.h (TYPE_EMPTY_RECORD): New. (type_is_empty_record_p): New static inline function. * 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 record size. gcc/c PR c++/60336 PR middle-end/67239 PR target/68355 * c-aux-info.c (gen_type): Add TYPE_EMPTY_RECORD check. gcc/cp/ PR c++/60336 PR middle-end/67239 PR target/68355 * class.c (finish_struct_1): Set TYPE_EMPTY_RECORD with return value from is_really_empty_class (). gcc/lto/ PR c++/60336 PR middle-end/67239 PR target/68355 * lto.c (compare_tree_sccs_1): Call compare_values with TYPE_EMPTY_RECORD for types. 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/pr60336-1.C: Likewise. * g++.dg/pr60336-2.C: Likewise. * g++.dg/pr60336-3.C: Likewise. * g++.dg/pr68355.C: Likewise. --- gcc/c/c-aux-info.c | 2 + gcc/calls.c | 96 ++++++++++++++++++---------- gcc/config/i386/i386.c | 3 +- gcc/cp/class.c | 2 + gcc/dse.c | 4 +- gcc/expr.c | 5 +- gcc/function.c | 122 +++++++++++++++++++++++++++++++----- gcc/lto-streamer-out.c | 2 + gcc/lto/lto.c | 2 + gcc/print-tree.c | 3 + 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/pr60336-1.C | 17 +++++ gcc/testsuite/g++.dg/pr60336-2.C | 28 +++++++++ gcc/testsuite/g++.dg/pr60336-3.C | 15 +++++ gcc/testsuite/g++.dg/pr68355.C | 24 +++++++ gcc/tree-core.h | 3 + gcc/tree-dfa.c | 2 + gcc/tree-streamer-in.c | 5 +- gcc/tree-streamer-out.c | 5 +- gcc/tree.h | 12 ++++ gcc/ubsan.c | 3 +- gcc/var-tracking.c | 18 +++--- 26 files changed, 351 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/pr60336-1.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/pr68355.C diff --git a/gcc/c/c-aux-info.c b/gcc/c/c-aux-info.c index 79d9851..93b001b 100644 --- a/gcc/c/c-aux-info.c +++ b/gcc/c/c-aux-info.c @@ -433,6 +433,8 @@ gen_type (const char *ret_val, tree t, formals_style style) ret_val = concat ("volatile ", ret_val, NULL); if (TYPE_RESTRICT (t)) ret_val = concat ("restrict ", ret_val, NULL); + if (TYPE_EMPTY_RECORD (t)) + ret_val = concat ("empty-record ", ret_val, NULL); return ret_val; } diff --git a/gcc/calls.c b/gcc/calls.c index 1eb4ec7..3f3ae02b 100644 --- a/gcc/calls.c +++ b/gcc/calls.c @@ -1152,7 +1152,10 @@ 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 record if they are used + in a variable argument list or they aren't the last arguments. */ + bool seen_empty_record = false; + bool warn_empty_record = stdarg_p (fndecl ? TREE_TYPE (fndecl) : fntype); i = num_actuals - 1; { @@ -1185,6 +1188,15 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED, { tree argtype = TREE_TYPE (arg); + if (!warn_empty_record) + { + if (argtype != error_mark_node + && type_is_empty_record_p (argtype)) + seen_empty_record = true; + else if (seen_empty_record) + warn_empty_record = true; + } + /* Remember last param with pointer and associate it with following pointer bounds. */ if (CALL_WITH_BOUNDS_P (exp) @@ -1399,8 +1411,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_record); + + /* Only warn empty record once. */ + if (type_is_empty_record_p (type)) + warn_empty_record = false; if (args[i].reg && CONST_INT_P (args[i].reg)) { @@ -1413,8 +1430,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; @@ -1475,8 +1492,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); } } @@ -3328,14 +3345,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)) { @@ -3947,8 +3961,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); @@ -3965,7 +3979,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++; } @@ -4030,8 +4044,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); @@ -4060,7 +4073,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 @@ -4407,8 +4420,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); @@ -4843,7 +4855,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_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. */ @@ -4873,10 +4888,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 @@ -4908,10 +4927,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_record = type_is_empty_record_p (TREE_TYPE (pval)); excess = (arg->locate.size.constant - - int_size_in_bytes (TREE_TYPE (pval)) + - (empty_record + ? 0 + : int_size_in_bytes (TREE_TYPE (pval))) + partial); - size_rtx = expand_expr (size_in_bytes (TREE_TYPE (pval)), + size_rtx = expand_expr ((empty_record + ? size_zero_node + : size_in_bytes (TREE_TYPE (pval))), NULL_RTX, TYPE_MODE (sizetype), EXPAND_NORMAL); } @@ -4986,10 +5010,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. @@ -5067,6 +5094,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_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 a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c index d30fbff..308d9a4e 100644 --- a/gcc/config/i386/i386.c +++ b/gcc/config/i386/i386.c @@ -10292,7 +10292,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_record = type && type_is_empty_record_p (type); + size = empty_record ? 0 : int_size_in_bytes (type); rsize = CEIL (size, UNITS_PER_WORD); nat_mode = type_natural_mode (type, NULL, false); diff --git a/gcc/cp/class.c b/gcc/cp/class.c index 216a301..d97aae6 100644 --- a/gcc/cp/class.c +++ b/gcc/cp/class.c @@ -6802,6 +6802,8 @@ finish_struct_1 (tree t) TYPE_TRANSPARENT_AGGR (t) = 0; } } + + TYPE_EMPTY_RECORD (t) = is_really_empty_class (t); } /* Insert FIELDS into T for the sorted case if the FIELDS count is diff --git a/gcc/dse.c b/gcc/dse.c index 35eef71..594106f 100644 --- a/gcc/dse.c +++ b/gcc/dse.c @@ -2366,7 +2366,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; @@ -2400,7 +2400,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/expr.c b/gcc/expr.c index bd43dc4..384581a 100644 --- a/gcc/expr.c +++ b/gcc/expr.c @@ -1198,13 +1198,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); } } diff --git a/gcc/function.c b/gcc/function.c index b513ead..9040a92 100644 --- a/gcc/function.c +++ b/gcc/function.c @@ -2074,7 +2074,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 @@ -2243,6 +2243,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_record; }; struct assign_parm_data_one @@ -2408,6 +2409,27 @@ assign_parms_augmented_arg_list (struct assign_parm_data_all *all) if (targetm.calls.split_complex_arg) split_complex_args (&fnargs); + /* Warn empty record if they are used in a variable argument list or + they aren't the last arguments. */ + bool warn_empty_record = stdarg_p (fntype); + if (!warn_empty_record) + { + unsigned int i; + bool seen_empty_record = false; + FOR_EACH_VEC_ELT (fnargs, i, arg) + { + tree type = TREE_TYPE (arg); + if (type != error_mark_node && type_is_empty_record_p (type)) + seen_empty_record = true; + else if (seen_empty_record) + { + warn_empty_record = true; + break; + } + } + } + all->warn_empty_record = warn_empty_record; + return fnargs; } @@ -2524,10 +2546,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_record); if (entry_parm == 0) data->promoted_mode = data->passed_mode; @@ -2551,9 +2574,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; } } @@ -3715,6 +3738,10 @@ assign_parms (tree fndecl) /* Find out where the parameter arrives in this function. */ assign_parm_find_entry_rtl (&all, &data); + /* Only warn empty record once. */ + if (type_is_empty_record_p (data.passed_type)) + all.warn_empty_record = false; + /* Find out where stack space for this parameter might be. */ if (assign_parm_is_stack_parm (&all, &data)) { @@ -3791,8 +3818,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++; @@ -3988,8 +4015,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 @@ -4132,8 +4159,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_record_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, @@ -6862,5 +6892,69 @@ make_pass_match_asm_constraints (gcc::context *ctxt) return new pass_match_asm_constraints (ctxt); } +static void +warn_empty_record (void) +{ + if (warn_psabi) + inform (input_location, "the ABI of passing empty record 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_record_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_record_p) +{ + if (type && type_is_empty_record_p (type)) + { + if (warn_empty_record_p) + warn_empty_record (); + 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_record_p) +{ + if (type && type_is_empty_record_p (type)) + { + if (warn_empty_record_p) + warn_empty_record (); + 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_record_p (type)) + return false; + + return targetm.calls.return_in_memory (type, fntype); +} + #include "gt-function.h" diff --git a/gcc/lto-streamer-out.c b/gcc/lto-streamer-out.c index a874846..509f0b3 100644 --- a/gcc/lto-streamer-out.c +++ b/gcc/lto-streamer-out.c @@ -944,6 +944,8 @@ hash_tree (struct streamer_tree_cache_d *cache, hash_map *map, hstate.add_flag (TREE_READONLY (t)); hstate.add_flag (TREE_PUBLIC (t)); } + else + hstate.add_flag (TYPE_EMPTY_RECORD (t)); hstate.add_flag (TREE_ADDRESSABLE (t)); hstate.add_flag (TREE_THIS_VOLATILE (t)); if (DECL_P (t)) diff --git a/gcc/lto/lto.c b/gcc/lto/lto.c index 90712b4..6755132 100644 --- a/gcc/lto/lto.c +++ b/gcc/lto/lto.c @@ -1002,6 +1002,8 @@ compare_tree_sccs_1 (tree t1, tree t2, tree **map) compare_values (TREE_READONLY); compare_values (TREE_PUBLIC); } + else + compare_values (TYPE_EMPTY_RECORD); compare_values (TREE_ADDRESSABLE); compare_values (TREE_THIS_VOLATILE); if (DECL_P (t1)) diff --git a/gcc/print-tree.c b/gcc/print-tree.c index cb0f1fd..bb489ff 100644 --- a/gcc/print-tree.c +++ b/gcc/print-tree.c @@ -587,6 +587,9 @@ print_node (FILE *file, const char *prefix, tree node, int indent) if (TYPE_RESTRICT (node)) fputs (" restrict", file); + if (TYPE_EMPTY_RECORD (node)) + fputs (" empty-record", file); + if (TYPE_LANG_FLAG_0 (node)) fputs (" type_0", file); if (TYPE_LANG_FLAG_1 (node)) diff --git a/gcc/target.h b/gcc/target.h index ffc4d6a..4f25a54 100644 --- a/gcc/target.h +++ b/gcc/target.h @@ -104,6 +104,14 @@ extern bool target_default_pointer_address_modes_p (void); behaviour. */ 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 dcf0863..7a8d1e8 100644 --- a/gcc/targhooks.c +++ b/gcc/targhooks.c @@ -1827,9 +1827,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_record = type_is_empty_record_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_record && !integer_zerop (TYPE_SIZE (type))) { t = build2 (MODIFY_EXPR, TREE_TYPE (valist), valist_tmp, @@ -1856,7 +1859,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_record ? 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..db2fd24 --- /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 record 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/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-2.C b/gcc/testsuite/g++.dg/pr60336-2.C new file mode 100644 index 0000000..7ab7d23 --- /dev/null +++ b/gcc/testsuite/g++.dg/pr60336-2.C @@ -0,0 +1,28 @@ +// { dg-do run } +// { dg-options "-O2" } + +#include + +struct dummy { struct{}__attribute__((aligned (4))) a[7]; }; + +void +test (struct dummy a, ...) // { dg-message "note: the ABI of passing empty record has changed in GCC 6" } +{ + va_list va_arglist; + int i; + + va_start (va_arglist, a); + i = va_arg (va_arglist, int); + if (i != 0x10) + __builtin_abort (); + va_end (va_arglist); +} + +struct dummy a0; + +int +main () +{ + test (a0, 0x10); // { dg-message "note: the ABI of passing empty record 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..3afdc8d --- /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 record has changed in GCC 6" } + test2 (a0); // { dg-message "note: the ABI of passing empty record has changed in GCC 6" } +} 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 +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 a/gcc/tree-core.h b/gcc/tree-core.h index 9cc64d9..67bffa4 100644 --- a/gcc/tree-core.h +++ b/gcc/tree-core.h @@ -1086,6 +1086,9 @@ struct GTY(()) tree_base { FORCED_LABEL in LABEL_DECL + TYPE_EMPTY_RECORD in + all types + volatile_flag: TREE_THIS_VOLATILE in diff --git a/gcc/tree-dfa.c b/gcc/tree-dfa.c index bb5cd49..1634ed6 100644 --- a/gcc/tree-dfa.c +++ b/gcc/tree-dfa.c @@ -394,6 +394,8 @@ get_ref_base_and_extent (tree exp, HOST_WIDE_INT *poffset, machine_mode mode = TYPE_MODE (TREE_TYPE (exp)); if (mode == BLKmode) size_tree = TYPE_SIZE (TREE_TYPE (exp)); + else if (type_is_empty_record_p (TREE_TYPE (exp))) + bitsize = 0; else bitsize = int (GET_MODE_PRECISION (mode)); } diff --git a/gcc/tree-streamer-in.c b/gcc/tree-streamer-in.c index 3162d1a..2aee7ad 100644 --- a/gcc/tree-streamer-in.c +++ b/gcc/tree-streamer-in.c @@ -112,7 +112,10 @@ unpack_ts_base_value_fields (struct bitpack_d *bp, tree expr) TREE_PUBLIC (expr) = (unsigned) bp_unpack_value (bp, 1); } else - bp_unpack_value (bp, 4); + { + TYPE_EMPTY_RECORD (expr) = (unsigned) bp_unpack_value (bp, 1); + bp_unpack_value (bp, 3); + } TREE_ADDRESSABLE (expr) = (unsigned) bp_unpack_value (bp, 1); TREE_THIS_VOLATILE (expr) = (unsigned) bp_unpack_value (bp, 1); if (DECL_P (expr)) diff --git a/gcc/tree-streamer-out.c b/gcc/tree-streamer-out.c index bfd0644..4306fcc 100644 --- a/gcc/tree-streamer-out.c +++ b/gcc/tree-streamer-out.c @@ -83,7 +83,10 @@ pack_ts_base_value_fields (struct bitpack_d *bp, tree expr) bp_pack_value (bp, TREE_PUBLIC (expr), 1); } else - bp_pack_value (bp, 0, 4); + { + bp_pack_value (bp, TYPE_EMPTY_RECORD (expr), 1); + bp_pack_value (bp, 0, 3); + } bp_pack_value (bp, TREE_ADDRESSABLE (expr), 1); bp_pack_value (bp, TREE_THIS_VOLATILE (expr), 1); if (DECL_P (expr)) diff --git a/gcc/tree.h b/gcc/tree.h index aef825d..9b13d5c 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -766,6 +766,10 @@ extern void omp_clause_range_check_failed (const_tree, const char *, int, computed gotos. */ #define FORCED_LABEL(NODE) (LABEL_DECL_CHECK (NODE)->base.side_effects_flag) +/* Nonzero in a type considered an empty record. */ +#define TYPE_EMPTY_RECORD(NODE) \ + (TYPE_CHECK (NODE)->base.side_effects_flag) + /* Nonzero means this expression is volatile in the C sense: its address should be of type `volatile WHATEVER *'. In other words, the declared item is volatile qualified. @@ -5379,6 +5383,14 @@ get_finish (location_t loc) return get_range_from_loc (line_table, loc).m_finish; } +/* Return true if type T is an empty record. */ + +static inline bool +type_is_empty_record_p (const_tree t) +{ + return TYPE_EMPTY_RECORD (TYPE_MAIN_VARIANT (t)); +} + extern location_t set_block (location_t loc, tree block); extern void gt_ggc_mx (tree &); diff --git a/gcc/ubsan.c b/gcc/ubsan.c index 6fc6233..6dafc90 100644 --- a/gcc/ubsan.c +++ b/gcc/ubsan.c @@ -379,10 +379,11 @@ ubsan_type_descriptor (tree type, enum ubsan_print_style pstyle) if (pstyle == UBSAN_PRINT_POINTER) { - pp_printf (&pretty_name, "'%s%s%s%s%s%s%s", + pp_printf (&pretty_name, "'%s%s%s%s%s%s%s%s", TYPE_VOLATILE (type2) ? "volatile " : "", TYPE_READONLY (type2) ? "const " : "", TYPE_RESTRICT (type2) ? "restrict " : "", + TYPE_EMPTY_RECORD (type2) ? "empty-record " : "", TYPE_ATOMIC (type2) ? "_Atomic " : "", TREE_CODE (type2) == RECORD_TYPE ? "struct " diff --git a/gcc/var-tracking.c b/gcc/var-tracking.c index 9185bfd..e9fdbe9 100644 --- a/gcc/var-tracking.c +++ b/gcc/var-tracking.c @@ -6140,10 +6140,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)) @@ -6164,8 +6164,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) @@ -6280,8 +6280,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 @@ -6335,8 +6334,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