* [PATCH] Add LANG_HOOKS_EMPTY_RECORD_P for C++ empty class @ 2015-11-17 11:01 H.J. Lu 2015-11-17 12:22 ` Richard Biener 0 siblings, 1 reply; 14+ messages in thread From: H.J. Lu @ 2015-11-17 11:01 UTC (permalink / raw) To: gcc-patches Empty record should be returned and passed the same way in C and C++. This patch adds LANG_HOOKS_EMPTY_RECORD_P for C++ empty class, which defaults to return false. For C++, LANG_HOOKS_EMPTY_RECORD_P is defined to is_really_empty_class, which returns true for C++ empty classes. For LTO, we stream out a bit to indicate if a record is empty and we store it in TYPE_LANG_FLAG_0 when streaming in. get_ref_base_and_extent is changed to set bitsize to 0 for empty records. Middle-end and x86 backend are updated to ignore empty records for parameter passing and function value return. Other targets may need similar changes. gcc/ PR c++/60336 PR middle-end/67239 PR target/68355 * calls.c (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. * function.c (locate_and_pad_parm): Use 0 for empty record size. * tree-dfa.c (get_ref_base_and_extent): Likewise. * langhooks-def.h (LANG_HOOKS_EMPTY_RECORD_P): New. (LANG_HOOKS_DECLS): Add LANG_HOOKS_EMPTY_RECORD_P. * langhooks.h (lang_hooks_for_decls): Add empty_record_p. * lto-streamer.h (LTO_major_version): Increase by 1 to 6. * targhooks.c: Include "langhooks.h". (std_gimplify_va_arg_expr): Use 0 for empty record size. * tree-streamer-in.c (unpack_ts_base_value_fields): Stream in TYPE_LANG_FLAG_0. * tree-streamer-out.c: Include "langhooks.h". (pack_ts_base_value_fields): Stream out a bit to indicate if a record is empty. * config/i386/i386.c (classify_argument): Return 0 for empty record. (construct_container): Return NULL for empty record. (ix86_function_arg): Likewise. (ix86_function_arg_advance): Skip empty record. (ix86_return_in_memory): Return false for empty record. (ix86_gimplify_va_arg): Use 0 for empty record size. gcc/cp/ PR c++/60336 PR middle-end/67239 PR target/68355 * class.c (is_empty_class): Changed to return bool and take const_tree. (is_really_empty_class): Changed to take const_tree. Check if TYPE_BINFO is zero. * cp-tree.h (is_empty_class): Updated. (is_really_empty_class): Likewise. * cp-lang.c (LANG_HOOKS_EMPTY_RECORD_P): New. gcc/lto/ PR c++/60336 PR middle-end/67239 PR target/68355 * lto-lang.c (lto_empty_record_p): New. (LANG_HOOKS_EMPTY_RECORD_P): Likewise. 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/pr68355.C: Likewise. --- gcc/calls.c | 41 +++++++++++++++++++++++++++---------- gcc/config/i386/i386.c | 18 +++++++++++++++- gcc/cp/class.c | 17 ++++++++------- gcc/cp/cp-lang.c | 2 ++ gcc/cp/cp-tree.h | 4 ++-- gcc/function.c | 7 +++++-- gcc/langhooks-def.h | 2 ++ gcc/langhooks.h | 3 +++ gcc/lto-streamer.h | 2 +- gcc/lto/lto-lang.c | 13 ++++++++++++ gcc/targhooks.c | 6 +++++- 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/pr68355.C | 24 ++++++++++++++++++++++ gcc/tree-dfa.c | 2 ++ gcc/tree-streamer-in.c | 5 +++++ gcc/tree-streamer-out.c | 6 ++++++ 20 files changed, 204 insertions(+), 25 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/pr68355.C diff --git a/gcc/calls.c b/gcc/calls.c index b56556a..ecc9b7a 100644 --- a/gcc/calls.c +++ b/gcc/calls.c @@ -4835,7 +4835,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 (lang_hooks.decls.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. */ @@ -4865,10 +4868,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 @@ -4900,10 +4907,16 @@ 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 + = lang_hooks.decls.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); } @@ -4971,10 +4984,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. @@ -5052,6 +5068,9 @@ must_pass_in_stack_var_size_or_pad (machine_mode mode, const_tree type) if (TREE_ADDRESSABLE (type)) return true; + if (lang_hooks.decls.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 6173dae..8bd6198 100644 --- a/gcc/config/i386/i386.c +++ b/gcc/config/i386/i386.c @@ -7920,6 +7920,9 @@ static int classify_argument (machine_mode mode, const_tree type, enum x86_64_reg_class classes[MAX_CLASSES], int bit_offset) { + if (type && lang_hooks.decls.empty_record_p (type)) + return 0; + HOST_WIDE_INT bytes = (mode == BLKmode) ? int_size_in_bytes (type) : (int) GET_MODE_SIZE (mode); int words = CEIL (bytes + (bit_offset % 64) / 8, UNITS_PER_WORD); @@ -8371,6 +8374,9 @@ construct_container (machine_mode mode, machine_mode orig_mode, static bool issued_sse_ret_error; static bool issued_x87_ret_error; + if (type && lang_hooks.decls.empty_record_p (type)) + return NULL; + machine_mode tmpmode; int bytes = (mode == BLKmode) ? int_size_in_bytes (type) : (int) GET_MODE_SIZE (mode); @@ -8783,6 +8789,9 @@ ix86_function_arg_advance (cumulative_args_t cum_v, machine_mode mode, HOST_WIDE_INT bytes, words; int nregs; + if (type && lang_hooks.decls.empty_record_p (type)) + return; + if (mode == BLKmode) bytes = int_size_in_bytes (type); else @@ -9099,6 +9108,9 @@ ix86_function_arg (cumulative_args_t cum_v, machine_mode omode, HOST_WIDE_INT bytes, words; rtx arg; + if (type && lang_hooks.decls.empty_record_p (type)) + return NULL; + /* All pointer bounds arguments are handled separately here. */ if ((type && POINTER_BOUNDS_TYPE_P (type)) || POINTER_BOUNDS_MODE_P (mode)) @@ -9708,6 +9720,9 @@ ix86_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED) if (POINTER_BOUNDS_TYPE_P (type)) return false; + if (type && lang_hooks.decls.empty_record_p (type)) + return false; + if (TARGET_64BIT) { if (ix86_function_type_abi (fntype) == MS_ABI) @@ -10266,7 +10281,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 && lang_hooks.decls.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..c380734 100644 --- a/gcc/cp/class.c +++ b/gcc/cp/class.c @@ -8068,8 +8068,8 @@ build_self_reference (void) /* Returns 1 if TYPE contains only padding bytes. */ -int -is_empty_class (tree type) +bool +is_empty_class (const_tree type) { if (type == error_mark_node) return 0; @@ -8084,7 +8084,7 @@ is_empty_class (tree type) possible combinations of empty classes and possibly a vptr. */ bool -is_really_empty_class (tree type) +is_really_empty_class (const_tree type) { if (CLASS_TYPE_P (type)) { @@ -8098,10 +8098,13 @@ is_really_empty_class (tree type) if (COMPLETE_TYPE_P (type) && is_empty_class (type)) return true; - for (binfo = TYPE_BINFO (type), i = 0; - BINFO_BASE_ITERATE (binfo, i, base_binfo); ++i) - if (!is_really_empty_class (BINFO_TYPE (base_binfo))) - return false; + /* TYPE_BINFO may be zeron when is_really_empty_class is called + from LANG_HOOKS_EMPTY_RECORD_P. */ + binfo = TYPE_BINFO (type); + if (binfo) + for (i = 0; BINFO_BASE_ITERATE (binfo, i, base_binfo); ++i) + if (!is_really_empty_class (BINFO_TYPE (base_binfo))) + return false; for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field)) if (TREE_CODE (field) == FIELD_DECL && !DECL_ARTIFICIAL (field) diff --git a/gcc/cp/cp-lang.c b/gcc/cp/cp-lang.c index 048108d..80174cb 100644 --- a/gcc/cp/cp-lang.c +++ b/gcc/cp/cp-lang.c @@ -78,6 +78,8 @@ static tree cxx_enum_underlying_base_type (const_tree); #define LANG_HOOKS_EH_RUNTIME_TYPE build_eh_type_type #undef LANG_HOOKS_ENUM_UNDERLYING_BASE_TYPE #define LANG_HOOKS_ENUM_UNDERLYING_BASE_TYPE cxx_enum_underlying_base_type +#undef LANG_HOOKS_EMPTY_RECORD_P +#define LANG_HOOKS_EMPTY_RECORD_P is_really_empty_class /* Each front end provides its own lang hook initializer. */ struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER; diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 84437b4..dc79979 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -5578,8 +5578,8 @@ extern tree finish_struct (tree, tree); extern void finish_struct_1 (tree); extern int resolves_to_fixed_type_p (tree, int *); extern void init_class_processing (void); -extern int is_empty_class (tree); -extern bool is_really_empty_class (tree); +extern bool is_empty_class (const_tree); +extern bool is_really_empty_class (const_tree); extern void pushclass (tree); extern void popclass (void); extern void push_nested_class (tree); diff --git a/gcc/function.c b/gcc/function.c index afc2c87..b59157d 100644 --- a/gcc/function.c +++ b/gcc/function.c @@ -4093,8 +4093,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 = (lang_hooks.decls.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, diff --git a/gcc/langhooks-def.h b/gcc/langhooks-def.h index 18ac84d..df4cbf8 100644 --- a/gcc/langhooks-def.h +++ b/gcc/langhooks-def.h @@ -163,6 +163,7 @@ extern tree lhd_make_node (enum tree_code); #define LANG_HOOKS_GENERIC_GENERIC_PARAMETER_DECL_P hook_bool_const_tree_false #define LANG_HOOKS_FUNCTION_PARM_EXPANDED_FROM_PACK_P \ hook_bool_tree_tree_false +#define LANG_HOOKS_EMPTY_RECORD_P hook_bool_const_tree_false #define LANG_HOOKS_GET_GENERIC_FUNCTION_DECL hook_tree_const_tree_null #define LANG_HOOKS_TYPE_PROMOTES_TO lhd_type_promotes_to #define LANG_HOOKS_REGISTER_BUILTIN_TYPE lhd_register_builtin_type @@ -228,6 +229,7 @@ extern tree lhd_make_node (enum tree_code); LANG_HOOKS_FUNCTION_DECL_DELETED_P, \ LANG_HOOKS_GENERIC_GENERIC_PARAMETER_DECL_P, \ LANG_HOOKS_FUNCTION_PARM_EXPANDED_FROM_PACK_P, \ + LANG_HOOKS_EMPTY_RECORD_P, \ LANG_HOOKS_GET_GENERIC_FUNCTION_DECL, \ LANG_HOOKS_WARN_UNUSED_GLOBAL_DECL, \ LANG_HOOKS_POST_COMPILATION_PARSING_CLEANUPS, \ diff --git a/gcc/langhooks.h b/gcc/langhooks.h index d8d01fa..450bdee 100644 --- a/gcc/langhooks.h +++ b/gcc/langhooks.h @@ -177,6 +177,9 @@ struct lang_hooks_for_decls function parameter pack. */ bool (*function_parm_expanded_from_pack_p) (tree, tree); + /* Determine if a type is an empty record. */ + bool (*empty_record_p) (const_tree type); + /* Returns the generic declaration of a generic function instantiations. */ tree (*get_generic_function_decl) (const_tree); diff --git a/gcc/lto-streamer.h b/gcc/lto-streamer.h index 5aae9e9..cc4cede 100644 --- a/gcc/lto-streamer.h +++ b/gcc/lto-streamer.h @@ -128,7 +128,7 @@ along with GCC; see the file COPYING3. If not see String are represented in the table as pairs, a length in ULEB128 form followed by the data for the string. */ -#define LTO_major_version 5 +#define LTO_major_version 6 #define LTO_minor_version 0 typedef unsigned char lto_decl_flags_t; diff --git a/gcc/lto/lto-lang.c b/gcc/lto/lto-lang.c index 53dd8f6..dc849a4 100644 --- a/gcc/lto/lto-lang.c +++ b/gcc/lto/lto-lang.c @@ -1306,6 +1306,16 @@ static void lto_init_ts (void) tree_contains_struct[NAMESPACE_DECL][TS_DECL_MINIMAL] = 1; } +/* Return true if TYPE contains no actual data, just various possible + combinations of empty records. */ + +static bool +lto_empty_record_p (const_tree type) +{ + /* Set if a record is empty. */ + return TYPE_LANG_FLAG_0 (type); +} + #undef LANG_HOOKS_NAME #define LANG_HOOKS_NAME "GNU GIMPLE" #undef LANG_HOOKS_OPTION_LANG_MASK @@ -1363,6 +1373,9 @@ static void lto_init_ts (void) #undef LANG_HOOKS_INIT_TS #define LANG_HOOKS_INIT_TS lto_init_ts +#undef LANG_HOOKS_EMPTY_RECORD_P +#define LANG_HOOKS_EMPTY_RECORD_P lto_empty_record_p + struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER; /* Language hooks that are not part of lang_hooks. */ diff --git a/gcc/targhooks.c b/gcc/targhooks.c index c34b4e9..5e74dd9 100644 --- a/gcc/targhooks.c +++ b/gcc/targhooks.c @@ -74,6 +74,7 @@ along with GCC; see the file COPYING3. If not see #include "intl.h" #include "opts.h" #include "gimplify.h" +#include "langhooks.h" bool @@ -1823,9 +1824,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 = lang_hooks.decls.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, @@ -1852,7 +1856,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..430d57d --- /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); + 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..7b902e8 --- /dev/null +++ b/gcc/testsuite/g++.dg/pr60336-2.C @@ -0,0 +1,28 @@ +// { dg-do run } +// { dg-options "-O2" } + +#include <stdarg.h> + +struct dummy { struct{}__attribute__((aligned (4))) a[7]; }; + +void +test (struct dummy a, ...) +{ + 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); + return 0; +} 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 bb5cd49..6b1e25d 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 (lang_hooks.decls.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 65a1ce3..1c32d81 100644 --- a/gcc/tree-streamer-in.c +++ b/gcc/tree-streamer-in.c @@ -154,6 +154,11 @@ unpack_ts_base_value_fields (struct bitpack_d *bp, tree expr) } else bp_unpack_value (bp, 9); + if (TYPE_P (expr)) + /* Set if a record is empty. */ + TYPE_LANG_FLAG_0 (expr) = (unsigned) bp_unpack_value (bp, 1); + else + bp_unpack_value (bp, 1); } diff --git a/gcc/tree-streamer-out.c b/gcc/tree-streamer-out.c index d0b7f6d..a1bf962 100644 --- a/gcc/tree-streamer-out.c +++ b/gcc/tree-streamer-out.c @@ -31,6 +31,7 @@ along with GCC; see the file COPYING3. If not see #include "alias.h" #include "stor-layout.h" #include "gomp-constants.h" +#include "langhooks.h" /* Output the STRING constant to the string @@ -128,6 +129,11 @@ pack_ts_base_value_fields (struct bitpack_d *bp, tree expr) } else bp_pack_value (bp, 0, 9); + if (TYPE_P (expr)) + /* Stream out a bit to indicate if a record is empty. */ + bp_pack_value (bp, lang_hooks.decls.empty_record_p (expr), 1); + else + bp_pack_value (bp, 0, 1); } -- 2.4.3 ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH] Add LANG_HOOKS_EMPTY_RECORD_P for C++ empty class 2015-11-17 11:01 [PATCH] Add LANG_HOOKS_EMPTY_RECORD_P for C++ empty class H.J. Lu @ 2015-11-17 12:22 ` Richard Biener 2015-11-20 18:52 ` H.J. Lu 0 siblings, 1 reply; 14+ messages in thread From: Richard Biener @ 2015-11-17 12:22 UTC (permalink / raw) To: H.J. Lu; +Cc: GCC Patches On Tue, Nov 17, 2015 at 12:01 PM, H.J. Lu <hjl.tools@gmail.com> wrote: > Empty record should be returned and passed the same way in C and C++. > This patch adds LANG_HOOKS_EMPTY_RECORD_P for C++ empty class, which > defaults to return false. For C++, LANG_HOOKS_EMPTY_RECORD_P is defined > to is_really_empty_class, which returns true for C++ empty classes. For > LTO, we stream out a bit to indicate if a record is empty and we store > it in TYPE_LANG_FLAG_0 when streaming in. get_ref_base_and_extent is > changed to set bitsize to 0 for empty records. Middle-end and x86 > backend are updated to ignore empty records for parameter passing and > function value return. Other targets may need similar changes. Please avoid a new langhook for this and instead claim a bit in tree_type_common like for example restrict_flag (double-check it is unused for non-pointers). I don't like that you need to modify targets - those checks should be done in the caller (which may just use a new wrapper with the logic and then dispatching to the actual hook). Why do you need do adjust get_ref_base_and_extent? Thanks, Richard. > gcc/ > > PR c++/60336 > PR middle-end/67239 > PR target/68355 > * calls.c (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. > * function.c (locate_and_pad_parm): Use 0 for empty record size. > * tree-dfa.c (get_ref_base_and_extent): Likewise. > * langhooks-def.h (LANG_HOOKS_EMPTY_RECORD_P): New. > (LANG_HOOKS_DECLS): Add LANG_HOOKS_EMPTY_RECORD_P. > * langhooks.h (lang_hooks_for_decls): Add empty_record_p. > * lto-streamer.h (LTO_major_version): Increase by 1 to 6. > * targhooks.c: Include "langhooks.h". > (std_gimplify_va_arg_expr): Use 0 for empty record size. > * tree-streamer-in.c (unpack_ts_base_value_fields): Stream in > TYPE_LANG_FLAG_0. > * tree-streamer-out.c: Include "langhooks.h". > (pack_ts_base_value_fields): Stream out a bit to indicate if a > record is empty. > * config/i386/i386.c (classify_argument): Return 0 for empty > record. > (construct_container): Return NULL for empty record. > (ix86_function_arg): Likewise. > (ix86_function_arg_advance): Skip empty record. > (ix86_return_in_memory): Return false for empty record. > (ix86_gimplify_va_arg): Use 0 for empty record size. > > gcc/cp/ > > PR c++/60336 > PR middle-end/67239 > PR target/68355 > * class.c (is_empty_class): Changed to return bool and take > const_tree. > (is_really_empty_class): Changed to take const_tree. Check > if TYPE_BINFO is zero. > * cp-tree.h (is_empty_class): Updated. > (is_really_empty_class): Likewise. > * cp-lang.c (LANG_HOOKS_EMPTY_RECORD_P): New. > > gcc/lto/ > > PR c++/60336 > PR middle-end/67239 > PR target/68355 > * lto-lang.c (lto_empty_record_p): New. > (LANG_HOOKS_EMPTY_RECORD_P): Likewise. > > 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/pr68355.C: Likewise. > --- > gcc/calls.c | 41 +++++++++++++++++++++++++++---------- > gcc/config/i386/i386.c | 18 +++++++++++++++- > gcc/cp/class.c | 17 ++++++++------- > gcc/cp/cp-lang.c | 2 ++ > gcc/cp/cp-tree.h | 4 ++-- > gcc/function.c | 7 +++++-- > gcc/langhooks-def.h | 2 ++ > gcc/langhooks.h | 3 +++ > gcc/lto-streamer.h | 2 +- > gcc/lto/lto-lang.c | 13 ++++++++++++ > gcc/targhooks.c | 6 +++++- > 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/pr68355.C | 24 ++++++++++++++++++++++ > gcc/tree-dfa.c | 2 ++ > gcc/tree-streamer-in.c | 5 +++++ > gcc/tree-streamer-out.c | 6 ++++++ > 20 files changed, 204 insertions(+), 25 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/pr68355.C > > diff --git a/gcc/calls.c b/gcc/calls.c > index b56556a..ecc9b7a 100644 > --- a/gcc/calls.c > +++ b/gcc/calls.c > @@ -4835,7 +4835,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 (lang_hooks.decls.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. */ > @@ -4865,10 +4868,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 > @@ -4900,10 +4907,16 @@ 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 > + = lang_hooks.decls.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); > } > @@ -4971,10 +4984,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. > @@ -5052,6 +5068,9 @@ must_pass_in_stack_var_size_or_pad (machine_mode mode, const_tree type) > if (TREE_ADDRESSABLE (type)) > return true; > > + if (lang_hooks.decls.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 6173dae..8bd6198 100644 > --- a/gcc/config/i386/i386.c > +++ b/gcc/config/i386/i386.c > @@ -7920,6 +7920,9 @@ static int > classify_argument (machine_mode mode, const_tree type, > enum x86_64_reg_class classes[MAX_CLASSES], int bit_offset) > { > + if (type && lang_hooks.decls.empty_record_p (type)) > + return 0; > + > HOST_WIDE_INT bytes = > (mode == BLKmode) ? int_size_in_bytes (type) : (int) GET_MODE_SIZE (mode); > int words = CEIL (bytes + (bit_offset % 64) / 8, UNITS_PER_WORD); > @@ -8371,6 +8374,9 @@ construct_container (machine_mode mode, machine_mode orig_mode, > static bool issued_sse_ret_error; > static bool issued_x87_ret_error; > > + if (type && lang_hooks.decls.empty_record_p (type)) > + return NULL; > + > machine_mode tmpmode; > int bytes = > (mode == BLKmode) ? int_size_in_bytes (type) : (int) GET_MODE_SIZE (mode); > @@ -8783,6 +8789,9 @@ ix86_function_arg_advance (cumulative_args_t cum_v, machine_mode mode, > HOST_WIDE_INT bytes, words; > int nregs; > > + if (type && lang_hooks.decls.empty_record_p (type)) > + return; > + > if (mode == BLKmode) > bytes = int_size_in_bytes (type); > else > @@ -9099,6 +9108,9 @@ ix86_function_arg (cumulative_args_t cum_v, machine_mode omode, > HOST_WIDE_INT bytes, words; > rtx arg; > > + if (type && lang_hooks.decls.empty_record_p (type)) > + return NULL; > + > /* All pointer bounds arguments are handled separately here. */ > if ((type && POINTER_BOUNDS_TYPE_P (type)) > || POINTER_BOUNDS_MODE_P (mode)) > @@ -9708,6 +9720,9 @@ ix86_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED) > if (POINTER_BOUNDS_TYPE_P (type)) > return false; > > + if (type && lang_hooks.decls.empty_record_p (type)) > + return false; > + > if (TARGET_64BIT) > { > if (ix86_function_type_abi (fntype) == MS_ABI) > @@ -10266,7 +10281,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 && lang_hooks.decls.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..c380734 100644 > --- a/gcc/cp/class.c > +++ b/gcc/cp/class.c > @@ -8068,8 +8068,8 @@ build_self_reference (void) > > /* Returns 1 if TYPE contains only padding bytes. */ > > -int > -is_empty_class (tree type) > +bool > +is_empty_class (const_tree type) > { > if (type == error_mark_node) > return 0; > @@ -8084,7 +8084,7 @@ is_empty_class (tree type) > possible combinations of empty classes and possibly a vptr. */ > > bool > -is_really_empty_class (tree type) > +is_really_empty_class (const_tree type) > { > if (CLASS_TYPE_P (type)) > { > @@ -8098,10 +8098,13 @@ is_really_empty_class (tree type) > if (COMPLETE_TYPE_P (type) && is_empty_class (type)) > return true; > > - for (binfo = TYPE_BINFO (type), i = 0; > - BINFO_BASE_ITERATE (binfo, i, base_binfo); ++i) > - if (!is_really_empty_class (BINFO_TYPE (base_binfo))) > - return false; > + /* TYPE_BINFO may be zeron when is_really_empty_class is called > + from LANG_HOOKS_EMPTY_RECORD_P. */ > + binfo = TYPE_BINFO (type); > + if (binfo) > + for (i = 0; BINFO_BASE_ITERATE (binfo, i, base_binfo); ++i) > + if (!is_really_empty_class (BINFO_TYPE (base_binfo))) > + return false; > for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field)) > if (TREE_CODE (field) == FIELD_DECL > && !DECL_ARTIFICIAL (field) > diff --git a/gcc/cp/cp-lang.c b/gcc/cp/cp-lang.c > index 048108d..80174cb 100644 > --- a/gcc/cp/cp-lang.c > +++ b/gcc/cp/cp-lang.c > @@ -78,6 +78,8 @@ static tree cxx_enum_underlying_base_type (const_tree); > #define LANG_HOOKS_EH_RUNTIME_TYPE build_eh_type_type > #undef LANG_HOOKS_ENUM_UNDERLYING_BASE_TYPE > #define LANG_HOOKS_ENUM_UNDERLYING_BASE_TYPE cxx_enum_underlying_base_type > +#undef LANG_HOOKS_EMPTY_RECORD_P > +#define LANG_HOOKS_EMPTY_RECORD_P is_really_empty_class > > /* Each front end provides its own lang hook initializer. */ > struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER; > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h > index 84437b4..dc79979 100644 > --- a/gcc/cp/cp-tree.h > +++ b/gcc/cp/cp-tree.h > @@ -5578,8 +5578,8 @@ extern tree finish_struct (tree, tree); > extern void finish_struct_1 (tree); > extern int resolves_to_fixed_type_p (tree, int *); > extern void init_class_processing (void); > -extern int is_empty_class (tree); > -extern bool is_really_empty_class (tree); > +extern bool is_empty_class (const_tree); > +extern bool is_really_empty_class (const_tree); > extern void pushclass (tree); > extern void popclass (void); > extern void push_nested_class (tree); > diff --git a/gcc/function.c b/gcc/function.c > index afc2c87..b59157d 100644 > --- a/gcc/function.c > +++ b/gcc/function.c > @@ -4093,8 +4093,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 = (lang_hooks.decls.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, > diff --git a/gcc/langhooks-def.h b/gcc/langhooks-def.h > index 18ac84d..df4cbf8 100644 > --- a/gcc/langhooks-def.h > +++ b/gcc/langhooks-def.h > @@ -163,6 +163,7 @@ extern tree lhd_make_node (enum tree_code); > #define LANG_HOOKS_GENERIC_GENERIC_PARAMETER_DECL_P hook_bool_const_tree_false > #define LANG_HOOKS_FUNCTION_PARM_EXPANDED_FROM_PACK_P \ > hook_bool_tree_tree_false > +#define LANG_HOOKS_EMPTY_RECORD_P hook_bool_const_tree_false > #define LANG_HOOKS_GET_GENERIC_FUNCTION_DECL hook_tree_const_tree_null > #define LANG_HOOKS_TYPE_PROMOTES_TO lhd_type_promotes_to > #define LANG_HOOKS_REGISTER_BUILTIN_TYPE lhd_register_builtin_type > @@ -228,6 +229,7 @@ extern tree lhd_make_node (enum tree_code); > LANG_HOOKS_FUNCTION_DECL_DELETED_P, \ > LANG_HOOKS_GENERIC_GENERIC_PARAMETER_DECL_P, \ > LANG_HOOKS_FUNCTION_PARM_EXPANDED_FROM_PACK_P, \ > + LANG_HOOKS_EMPTY_RECORD_P, \ > LANG_HOOKS_GET_GENERIC_FUNCTION_DECL, \ > LANG_HOOKS_WARN_UNUSED_GLOBAL_DECL, \ > LANG_HOOKS_POST_COMPILATION_PARSING_CLEANUPS, \ > diff --git a/gcc/langhooks.h b/gcc/langhooks.h > index d8d01fa..450bdee 100644 > --- a/gcc/langhooks.h > +++ b/gcc/langhooks.h > @@ -177,6 +177,9 @@ struct lang_hooks_for_decls > function parameter pack. */ > bool (*function_parm_expanded_from_pack_p) (tree, tree); > > + /* Determine if a type is an empty record. */ > + bool (*empty_record_p) (const_tree type); > + > /* Returns the generic declaration of a generic function instantiations. */ > tree (*get_generic_function_decl) (const_tree); > > diff --git a/gcc/lto-streamer.h b/gcc/lto-streamer.h > index 5aae9e9..cc4cede 100644 > --- a/gcc/lto-streamer.h > +++ b/gcc/lto-streamer.h > @@ -128,7 +128,7 @@ along with GCC; see the file COPYING3. If not see > String are represented in the table as pairs, a length in ULEB128 > form followed by the data for the string. */ > > -#define LTO_major_version 5 > +#define LTO_major_version 6 > #define LTO_minor_version 0 > > typedef unsigned char lto_decl_flags_t; > diff --git a/gcc/lto/lto-lang.c b/gcc/lto/lto-lang.c > index 53dd8f6..dc849a4 100644 > --- a/gcc/lto/lto-lang.c > +++ b/gcc/lto/lto-lang.c > @@ -1306,6 +1306,16 @@ static void lto_init_ts (void) > tree_contains_struct[NAMESPACE_DECL][TS_DECL_MINIMAL] = 1; > } > > +/* Return true if TYPE contains no actual data, just various possible > + combinations of empty records. */ > + > +static bool > +lto_empty_record_p (const_tree type) > +{ > + /* Set if a record is empty. */ > + return TYPE_LANG_FLAG_0 (type); > +} > + > #undef LANG_HOOKS_NAME > #define LANG_HOOKS_NAME "GNU GIMPLE" > #undef LANG_HOOKS_OPTION_LANG_MASK > @@ -1363,6 +1373,9 @@ static void lto_init_ts (void) > #undef LANG_HOOKS_INIT_TS > #define LANG_HOOKS_INIT_TS lto_init_ts > > +#undef LANG_HOOKS_EMPTY_RECORD_P > +#define LANG_HOOKS_EMPTY_RECORD_P lto_empty_record_p > + > struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER; > > /* Language hooks that are not part of lang_hooks. */ > diff --git a/gcc/targhooks.c b/gcc/targhooks.c > index c34b4e9..5e74dd9 100644 > --- a/gcc/targhooks.c > +++ b/gcc/targhooks.c > @@ -74,6 +74,7 @@ along with GCC; see the file COPYING3. If not see > #include "intl.h" > #include "opts.h" > #include "gimplify.h" > +#include "langhooks.h" > > > bool > @@ -1823,9 +1824,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 = lang_hooks.decls.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, > @@ -1852,7 +1856,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..430d57d > --- /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); > + 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..7b902e8 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/pr60336-2.C > @@ -0,0 +1,28 @@ > +// { dg-do run } > +// { dg-options "-O2" } > + > +#include <stdarg.h> > + > +struct dummy { struct{}__attribute__((aligned (4))) a[7]; }; > + > +void > +test (struct dummy a, ...) > +{ > + 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); > + return 0; > +} > 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 bb5cd49..6b1e25d 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 (lang_hooks.decls.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 65a1ce3..1c32d81 100644 > --- a/gcc/tree-streamer-in.c > +++ b/gcc/tree-streamer-in.c > @@ -154,6 +154,11 @@ unpack_ts_base_value_fields (struct bitpack_d *bp, tree expr) > } > else > bp_unpack_value (bp, 9); > + if (TYPE_P (expr)) > + /* Set if a record is empty. */ > + TYPE_LANG_FLAG_0 (expr) = (unsigned) bp_unpack_value (bp, 1); > + else > + bp_unpack_value (bp, 1); > } > > > diff --git a/gcc/tree-streamer-out.c b/gcc/tree-streamer-out.c > index d0b7f6d..a1bf962 100644 > --- a/gcc/tree-streamer-out.c > +++ b/gcc/tree-streamer-out.c > @@ -31,6 +31,7 @@ along with GCC; see the file COPYING3. If not see > #include "alias.h" > #include "stor-layout.h" > #include "gomp-constants.h" > +#include "langhooks.h" > > > /* Output the STRING constant to the string > @@ -128,6 +129,11 @@ pack_ts_base_value_fields (struct bitpack_d *bp, tree expr) > } > else > bp_pack_value (bp, 0, 9); > + if (TYPE_P (expr)) > + /* Stream out a bit to indicate if a record is empty. */ > + bp_pack_value (bp, lang_hooks.decls.empty_record_p (expr), 1); > + else > + bp_pack_value (bp, 0, 1); > } > > > -- > 2.4.3 > ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH] Add LANG_HOOKS_EMPTY_RECORD_P for C++ empty class 2015-11-17 12:22 ` Richard Biener @ 2015-11-20 18:52 ` H.J. Lu 2015-11-20 22:17 ` Jason Merrill 0 siblings, 1 reply; 14+ messages in thread From: H.J. Lu @ 2015-11-20 18:52 UTC (permalink / raw) To: Richard Biener; +Cc: GCC Patches On Tue, Nov 17, 2015 at 4:22 AM, Richard Biener <richard.guenther@gmail.com> wrote: > On Tue, Nov 17, 2015 at 12:01 PM, H.J. Lu <hjl.tools@gmail.com> wrote: >> Empty record should be returned and passed the same way in C and C++. >> This patch adds LANG_HOOKS_EMPTY_RECORD_P for C++ empty class, which >> defaults to return false. For C++, LANG_HOOKS_EMPTY_RECORD_P is defined >> to is_really_empty_class, which returns true for C++ empty classes. For >> LTO, we stream out a bit to indicate if a record is empty and we store >> it in TYPE_LANG_FLAG_0 when streaming in. get_ref_base_and_extent is >> changed to set bitsize to 0 for empty records. Middle-end and x86 >> backend are updated to ignore empty records for parameter passing and >> function value return. Other targets may need similar changes. > > Please avoid a new langhook for this and instead claim a bit in tree_type_common > like for example restrict_flag (double-check it is unused for non-pointers). There is no bit in tree_type_common I can overload. restrict_flag is checked for non-pointers to issue an error when it is used on non-pointers: /export/gnu/import/git/sources/gcc/gcc/testsuite/g++.dg/template/qualttp20.C:19:38: error: ‘__restrict__’ qualifiers cannot be applied to ‘AS::L’ typedef typename T::L __restrict__ r;// { dg-error "'__restrict__' qualifiers cannot" "" } Should I add a bit to tree_type_common? It may crease the size of tree_type_common by 4 bytes since there is unused bits in tree_type_common. > I don't like that you need to modify targets - those checks should be done > in the caller (which may just use a new wrapper with the logic and then > dispatching to the actual hook). I can do that. > Why do you need do adjust get_ref_base_and_extent? 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 in struct dummy { }; struct true_type { struct dummy i; }; extern true_type y; extern void xxx (true_type c); void yyy (void) { xxx (y); } -- H.J. ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH] Add LANG_HOOKS_EMPTY_RECORD_P for C++ empty class 2015-11-20 18:52 ` H.J. Lu @ 2015-11-20 22:17 ` Jason Merrill 2015-11-20 22:24 ` Jason Merrill 2015-11-20 23:47 ` H.J. Lu 0 siblings, 2 replies; 14+ messages in thread From: Jason Merrill @ 2015-11-20 22:17 UTC (permalink / raw) To: H.J. Lu, Richard Biener; +Cc: GCC Patches On 11/20/2015 01:52 PM, H.J. Lu wrote: > On Tue, Nov 17, 2015 at 4:22 AM, Richard Biener > <richard.guenther@gmail.com> wrote: >> On Tue, Nov 17, 2015 at 12:01 PM, H.J. Lu <hjl.tools@gmail.com> wrote: >>> Empty record should be returned and passed the same way in C and C++. >>> This patch adds LANG_HOOKS_EMPTY_RECORD_P for C++ empty class, which >>> defaults to return false. For C++, LANG_HOOKS_EMPTY_RECORD_P is defined >>> to is_really_empty_class, which returns true for C++ empty classes. For >>> LTO, we stream out a bit to indicate if a record is empty and we store >>> it in TYPE_LANG_FLAG_0 when streaming in. get_ref_base_and_extent is >>> changed to set bitsize to 0 for empty records. Middle-end and x86 >>> backend are updated to ignore empty records for parameter passing and >>> function value return. Other targets may need similar changes. >> >> Please avoid a new langhook for this and instead claim a bit in tree_type_common >> like for example restrict_flag (double-check it is unused for non-pointers). > > There is no bit in tree_type_common I can overload. restrict_flag is > checked for non-pointers to issue an error when it is used on non-pointers: > > /export/gnu/import/git/sources/gcc/gcc/testsuite/g++.dg/template/qualttp20.C:19:38: > error: â__restrict__â qualifiers cannot be applied to âAS::Lâ > typedef typename T::L __restrict__ r;// { dg-error "'__restrict__' > qualifiers cannot" "" } The C++ front end only needs to check TYPE_RESTRICT for this purpose on front-end-specific type codes like TEMPLATE_TYPE_PARM; cp_type_quals could handle that specifically if you change TYPE_RESTRICT to only apply to pointers. Jason ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH] Add LANG_HOOKS_EMPTY_RECORD_P for C++ empty class 2015-11-20 22:17 ` Jason Merrill @ 2015-11-20 22:24 ` Jason Merrill 2015-11-20 23:47 ` H.J. Lu 1 sibling, 0 replies; 14+ messages in thread From: Jason Merrill @ 2015-11-20 22:24 UTC (permalink / raw) To: gcc-patches; +Cc: GCC Patches On 11/20/2015 01:52 PM, H.J. Lu wrote: > On Tue, Nov 17, 2015 at 4:22 AM, Richard Biener > <richard.guenther@gmail.com> wrote: >> On Tue, Nov 17, 2015 at 12:01 PM, H.J. Lu <hjl.tools@gmail.com> wrote: >>> Empty record should be returned and passed the same way in C and C++. >>> This patch adds LANG_HOOKS_EMPTY_RECORD_P for C++ empty class, which >>> defaults to return false. For C++, LANG_HOOKS_EMPTY_RECORD_P is defined >>> to is_really_empty_class, which returns true for C++ empty classes. For >>> LTO, we stream out a bit to indicate if a record is empty and we store >>> it in TYPE_LANG_FLAG_0 when streaming in. get_ref_base_and_extent is >>> changed to set bitsize to 0 for empty records. Middle-end and x86 >>> backend are updated to ignore empty records for parameter passing and >>> function value return. Other targets may need similar changes. >> >> Please avoid a new langhook for this and instead claim a bit in tree_type_common >> like for example restrict_flag (double-check it is unused for non-pointers). > > There is no bit in tree_type_common I can overload. restrict_flag is > checked for non-pointers to issue an error when it is used on non-pointers: > > /export/gnu/import/git/sources/gcc/gcc/testsuite/g++.dg/template/qualttp20.C:19:38: > error: â__restrict__â qualifiers cannot be applied to âAS::Lâ > typedef typename T::L __restrict__ r;// { dg-error "'__restrict__' > qualifiers cannot" "" } The C++ front end only needs to check TYPE_RESTRICT for this purpose on front-end-specific type codes like TEMPLATE_TYPE_PARM; cp_type_quals could handle that specifically if you change TYPE_RESTRICT to only apply to pointers. Jason ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH] Add LANG_HOOKS_EMPTY_RECORD_P for C++ empty class 2015-11-20 22:17 ` Jason Merrill 2015-11-20 22:24 ` Jason Merrill @ 2015-11-20 23:47 ` H.J. Lu 2015-11-23 9:59 ` Richard Biener 1 sibling, 1 reply; 14+ messages in thread From: H.J. Lu @ 2015-11-20 23:47 UTC (permalink / raw) To: Jason Merrill; +Cc: Richard Biener, GCC Patches On Fri, Nov 20, 2015 at 2:17 PM, Jason Merrill <jason@redhat.com> wrote: > On 11/20/2015 01:52 PM, H.J. Lu wrote: >> >> On Tue, Nov 17, 2015 at 4:22 AM, Richard Biener >> <richard.guenther@gmail.com> wrote: >>> >>> On Tue, Nov 17, 2015 at 12:01 PM, H.J. Lu <hjl.tools@gmail.com> wrote: >>>> >>>> Empty record should be returned and passed the same way in C and C++. >>>> This patch adds LANG_HOOKS_EMPTY_RECORD_P for C++ empty class, which >>>> defaults to return false. For C++, LANG_HOOKS_EMPTY_RECORD_P is defined >>>> to is_really_empty_class, which returns true for C++ empty classes. For >>>> LTO, we stream out a bit to indicate if a record is empty and we store >>>> it in TYPE_LANG_FLAG_0 when streaming in. get_ref_base_and_extent is >>>> changed to set bitsize to 0 for empty records. Middle-end and x86 >>>> backend are updated to ignore empty records for parameter passing and >>>> function value return. Other targets may need similar changes. >>> >>> >>> Please avoid a new langhook for this and instead claim a bit in >>> tree_type_common >>> like for example restrict_flag (double-check it is unused for >>> non-pointers). >> >> >> There is no bit in tree_type_common I can overload. restrict_flag is >> checked for non-pointers to issue an error when it is used on >> non-pointers: >> >> >> /export/gnu/import/git/sources/gcc/gcc/testsuite/g++.dg/template/qualttp20.C:19:38: >> error: ‘__restrict__’ qualifiers cannot be applied to ‘AS::L’ >> typedef typename T::L __restrict__ r;// { dg-error "'__restrict__' >> qualifiers cannot" "" } > > > The C++ front end only needs to check TYPE_RESTRICT for this purpose on > front-end-specific type codes like TEMPLATE_TYPE_PARM; cp_type_quals could > handle that specifically if you change TYPE_RESTRICT to only apply to > pointers. > restrict_flag is also checked in this case: [hjl@gnu-6 gcc]$ cat x.i struct dummy { }; struct dummy foo (struct dummy __restrict__ i) { return i; } [hjl@gnu-6 gcc]$ gcc -S x.i -Wall x.i:4:13: error: invalid use of ‘restrict’ foo (struct dummy __restrict__ i) ^ x.i:4:13: error: invalid use of ‘restrict’ [hjl@gnu-6 gcc]$ restrict_flag can't also be used to indicate `i' is an empty record. H.J. ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH] Add LANG_HOOKS_EMPTY_RECORD_P for C++ empty class 2015-11-20 23:47 ` H.J. Lu @ 2015-11-23 9:59 ` Richard Biener 2015-11-23 20:54 ` H.J. Lu 0 siblings, 1 reply; 14+ messages in thread From: Richard Biener @ 2015-11-23 9:59 UTC (permalink / raw) To: H.J. Lu; +Cc: Jason Merrill, GCC Patches On Sat, Nov 21, 2015 at 12:46 AM, H.J. Lu <hjl.tools@gmail.com> wrote: > On Fri, Nov 20, 2015 at 2:17 PM, Jason Merrill <jason@redhat.com> wrote: >> On 11/20/2015 01:52 PM, H.J. Lu wrote: >>> >>> On Tue, Nov 17, 2015 at 4:22 AM, Richard Biener >>> <richard.guenther@gmail.com> wrote: >>>> >>>> On Tue, Nov 17, 2015 at 12:01 PM, H.J. Lu <hjl.tools@gmail.com> wrote: >>>>> >>>>> Empty record should be returned and passed the same way in C and C++. >>>>> This patch adds LANG_HOOKS_EMPTY_RECORD_P for C++ empty class, which >>>>> defaults to return false. For C++, LANG_HOOKS_EMPTY_RECORD_P is defined >>>>> to is_really_empty_class, which returns true for C++ empty classes. For >>>>> LTO, we stream out a bit to indicate if a record is empty and we store >>>>> it in TYPE_LANG_FLAG_0 when streaming in. get_ref_base_and_extent is >>>>> changed to set bitsize to 0 for empty records. Middle-end and x86 >>>>> backend are updated to ignore empty records for parameter passing and >>>>> function value return. Other targets may need similar changes. >>>> >>>> >>>> Please avoid a new langhook for this and instead claim a bit in >>>> tree_type_common >>>> like for example restrict_flag (double-check it is unused for >>>> non-pointers). >>> >>> >>> There is no bit in tree_type_common I can overload. restrict_flag is >>> checked for non-pointers to issue an error when it is used on >>> non-pointers: >>> >>> >>> /export/gnu/import/git/sources/gcc/gcc/testsuite/g++.dg/template/qualttp20.C:19:38: >>> error: ‘__restrict__’ qualifiers cannot be applied to ‘AS::L’ >>> typedef typename T::L __restrict__ r;// { dg-error "'__restrict__' >>> qualifiers cannot" "" } >> >> >> The C++ front end only needs to check TYPE_RESTRICT for this purpose on >> front-end-specific type codes like TEMPLATE_TYPE_PARM; cp_type_quals could >> handle that specifically if you change TYPE_RESTRICT to only apply to >> pointers. >> > > restrict_flag is also checked in this case: > > [hjl@gnu-6 gcc]$ cat x.i > struct dummy { }; > > struct dummy > foo (struct dummy __restrict__ i) > { > return i; > } > [hjl@gnu-6 gcc]$ gcc -S x.i -Wall > x.i:4:13: error: invalid use of ‘restrict’ > foo (struct dummy __restrict__ i) > ^ > x.i:4:13: error: invalid use of ‘restrict’ > [hjl@gnu-6 gcc]$ > > restrict_flag can't also be used to indicate `i' is an empty record. I'm sure this error can be done during parsing w/o relying on TYPE_RESTRICT. But well, use any other free bit (but do not enlarge tree_type_common). Eventually you can free up a bit by putting sth into type_lang_specific currently using bits in tree_type_common. Richard. > > H.J. ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH] Add LANG_HOOKS_EMPTY_RECORD_P for C++ empty class 2015-11-23 9:59 ` Richard Biener @ 2015-11-23 20:54 ` H.J. Lu 2015-11-24 3:48 ` Patrick Palka 0 siblings, 1 reply; 14+ messages in thread From: H.J. Lu @ 2015-11-23 20:54 UTC (permalink / raw) To: Richard Biener; +Cc: Jason Merrill, GCC Patches [-- Attachment #1: Type: text/plain, Size: 3058 bytes --] On Mon, Nov 23, 2015 at 1:57 AM, Richard Biener <richard.guenther@gmail.com> wrote: > On Sat, Nov 21, 2015 at 12:46 AM, H.J. Lu <hjl.tools@gmail.com> wrote: >> On Fri, Nov 20, 2015 at 2:17 PM, Jason Merrill <jason@redhat.com> wrote: >>> On 11/20/2015 01:52 PM, H.J. Lu wrote: >>>> >>>> On Tue, Nov 17, 2015 at 4:22 AM, Richard Biener >>>> <richard.guenther@gmail.com> wrote: >>>>> >>>>> On Tue, Nov 17, 2015 at 12:01 PM, H.J. Lu <hjl.tools@gmail.com> wrote: >>>>>> >>>>>> Empty record should be returned and passed the same way in C and C++. >>>>>> This patch adds LANG_HOOKS_EMPTY_RECORD_P for C++ empty class, which >>>>>> defaults to return false. For C++, LANG_HOOKS_EMPTY_RECORD_P is defined >>>>>> to is_really_empty_class, which returns true for C++ empty classes. For >>>>>> LTO, we stream out a bit to indicate if a record is empty and we store >>>>>> it in TYPE_LANG_FLAG_0 when streaming in. get_ref_base_and_extent is >>>>>> changed to set bitsize to 0 for empty records. Middle-end and x86 >>>>>> backend are updated to ignore empty records for parameter passing and >>>>>> function value return. Other targets may need similar changes. >>>>> >>>>> >>>>> Please avoid a new langhook for this and instead claim a bit in >>>>> tree_type_common >>>>> like for example restrict_flag (double-check it is unused for >>>>> non-pointers). >>>> >>>> >>>> There is no bit in tree_type_common I can overload. restrict_flag is >>>> checked for non-pointers to issue an error when it is used on >>>> non-pointers: >>>> >>>> >>>> /export/gnu/import/git/sources/gcc/gcc/testsuite/g++.dg/template/qualttp20.C:19:38: >>>> error: ‘__restrict__’ qualifiers cannot be applied to ‘AS::L’ >>>> typedef typename T::L __restrict__ r;// { dg-error "'__restrict__' >>>> qualifiers cannot" "" } >>> >>> >>> The C++ front end only needs to check TYPE_RESTRICT for this purpose on >>> front-end-specific type codes like TEMPLATE_TYPE_PARM; cp_type_quals could >>> handle that specifically if you change TYPE_RESTRICT to only apply to >>> pointers. >>> >> >> restrict_flag is also checked in this case: >> >> [hjl@gnu-6 gcc]$ cat x.i >> struct dummy { }; >> >> struct dummy >> foo (struct dummy __restrict__ i) >> { >> return i; >> } >> [hjl@gnu-6 gcc]$ gcc -S x.i -Wall >> x.i:4:13: error: invalid use of ‘restrict’ >> foo (struct dummy __restrict__ i) >> ^ >> x.i:4:13: error: invalid use of ‘restrict’ >> [hjl@gnu-6 gcc]$ >> >> restrict_flag can't also be used to indicate `i' is an empty record. > > I'm sure this error can be done during parsing w/o relying on TYPE_RESTRICT. > > But well, use any other free bit (but do not enlarge > tree_type_common). Eventually > you can free up a bit by putting sth into type_lang_specific currently > using bits > in tree_type_common. There are no bits in tree_type_common I can move. Instead, this patch overloads side_effects_flag in tree_base. Tested on Linux/x86-64. OK for trunk? Thanks. -- H.J. [-- Attachment #2: 0001-Add-TYPE_EMPTY_RECORD-for-C-empty-class.patch --] [-- Type: text/x-patch, Size: 31923 bytes --] From 1e3e6ed42ed86b74d01bd3a6b869c15dba964c21 Mon Sep 17 00:00:00 2001 From: "H.J. Lu" <hjl.tools@gmail.com> Date: Sun, 15 Nov 2015 13:19:05 -0800 Subject: [PATCH] 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): 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): Likewise. (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_find_entry_rtl): Replace targetm.calls.function_incoming_arg with function_incoming_arg. (assign_parms): 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. (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/pr68355.C: Likewise. --- gcc/c/c-aux-info.c | 2 + gcc/calls.c | 76 +++++++++++++++++++++--------------- gcc/config/i386/i386.c | 3 +- gcc/cp/class.c | 2 + gcc/dse.c | 4 +- gcc/expr.c | 5 +-- gcc/function.c | 78 ++++++++++++++++++++++++++++++------- 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/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 ++++----- 25 files changed, 274 insertions(+), 65 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/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 b56556a..a81cb3d 100644 --- a/gcc/calls.c +++ b/gcc/calls.c @@ -1391,8 +1391,8 @@ 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); if (args[i].reg && CONST_INT_P (args[i].reg)) { @@ -1405,8 +1405,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; @@ -1467,8 +1467,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); } } @@ -3320,14 +3320,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)) { @@ -3939,8 +3936,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); @@ -3957,7 +3954,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++; } @@ -4022,7 +4019,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, + argvec[count].reg = function_arg (args_so_far, mode, NULL_TREE, true); argvec[count].partial @@ -4052,7 +4049,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 @@ -4399,8 +4396,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); @@ -4835,7 +4831,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. */ @@ -4865,10 +4864,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 @@ -4900,10 +4903,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); } @@ -4971,10 +4979,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. @@ -5052,6 +5063,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 cc42544..01d4d80 100644 --- a/gcc/config/i386/i386.c +++ b/gcc/config/i386/i386.c @@ -10277,7 +10277,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 afc2c87..d8924fe 100644 --- a/gcc/function.c +++ b/gcc/function.c @@ -2073,7 +2073,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 @@ -2523,10 +2523,10 @@ 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); if (entry_parm == 0) data->promoted_mode = data->passed_mode; @@ -2550,9 +2550,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; } } @@ -3752,8 +3752,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++; @@ -3949,8 +3949,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 @@ -4093,8 +4093,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, @@ -6821,5 +6824,52 @@ make_pass_match_asm_constraints (gcc::context *ctxt) return new pass_match_asm_constraints (ctxt); } +/* 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) +{ + if (type && type_is_empty_record_p (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) +{ + if (type && type_is_empty_record_p (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_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 0d610f1..898cee9 100644 --- a/gcc/lto-streamer-out.c +++ b/gcc/lto-streamer-out.c @@ -940,6 +940,8 @@ hash_tree (struct streamer_tree_cache_d *cache, hash_map<tree, hashval_t> *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 2661491..df403bf 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..eb01a76 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); +extern rtx function_incoming_arg (cumulative_args_t, machine_mode, + const_tree, bool); +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 01d3686..de10ec3 100644 --- a/gcc/targhooks.c +++ b/gcc/targhooks.c @@ -1829,9 +1829,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, @@ -1858,7 +1861,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..430d57d --- /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); + 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..7b902e8 --- /dev/null +++ b/gcc/testsuite/g++.dg/pr60336-2.C @@ -0,0 +1,28 @@ +// { dg-do run } +// { dg-options "-O2" } + +#include <stdarg.h> + +struct dummy { struct{}__attribute__((aligned (4))) a[7]; }; + +void +test (struct dummy a, ...) +{ + 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); + return 0; +} 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-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 65a1ce3..7b67534 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 d0b7f6d..9b14db8 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 cb52deb..569f709 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. @@ -5337,6 +5341,14 @@ type_with_alias_set_p (const_tree t) return false; } +/* 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.4.3 ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH] Add LANG_HOOKS_EMPTY_RECORD_P for C++ empty class 2015-11-23 20:54 ` H.J. Lu @ 2015-11-24 3:48 ` Patrick Palka 2015-11-24 6:00 ` H.J. Lu 0 siblings, 1 reply; 14+ messages in thread From: Patrick Palka @ 2015-11-24 3:48 UTC (permalink / raw) To: H.J. Lu; +Cc: Richard Biener, Jason Merrill, GCC Patches [-- Attachment #1: Type: text/plain, Size: 3731 bytes --] On Mon, Nov 23, 2015 at 3:53 PM, H.J. Lu <hjl.tools@gmail.com> wrote: > On Mon, Nov 23, 2015 at 1:57 AM, Richard Biener > <richard.guenther@gmail.com> wrote: >> On Sat, Nov 21, 2015 at 12:46 AM, H.J. Lu <hjl.tools@gmail.com> wrote: >>> On Fri, Nov 20, 2015 at 2:17 PM, Jason Merrill <jason@redhat.com> wrote: >>>> On 11/20/2015 01:52 PM, H.J. Lu wrote: >>>>> >>>>> On Tue, Nov 17, 2015 at 4:22 AM, Richard Biener >>>>> <richard.guenther@gmail.com> wrote: >>>>>> >>>>>> On Tue, Nov 17, 2015 at 12:01 PM, H.J. Lu <hjl.tools@gmail.com> wrote: >>>>>>> >>>>>>> Empty record should be returned and passed the same way in C and C++. >>>>>>> This patch adds LANG_HOOKS_EMPTY_RECORD_P for C++ empty class, which >>>>>>> defaults to return false. For C++, LANG_HOOKS_EMPTY_RECORD_P is defined >>>>>>> to is_really_empty_class, which returns true for C++ empty classes. For >>>>>>> LTO, we stream out a bit to indicate if a record is empty and we store >>>>>>> it in TYPE_LANG_FLAG_0 when streaming in. get_ref_base_and_extent is >>>>>>> changed to set bitsize to 0 for empty records. Middle-end and x86 >>>>>>> backend are updated to ignore empty records for parameter passing and >>>>>>> function value return. Other targets may need similar changes. >>>>>> >>>>>> >>>>>> Please avoid a new langhook for this and instead claim a bit in >>>>>> tree_type_common >>>>>> like for example restrict_flag (double-check it is unused for >>>>>> non-pointers). >>>>> >>>>> >>>>> There is no bit in tree_type_common I can overload. restrict_flag is >>>>> checked for non-pointers to issue an error when it is used on >>>>> non-pointers: >>>>> >>>>> >>>>> /export/gnu/import/git/sources/gcc/gcc/testsuite/g++.dg/template/qualttp20.C:19:38: >>>>> error: ‘__restrict__’ qualifiers cannot be applied to ‘AS::L’ >>>>> typedef typename T::L __restrict__ r;// { dg-error "'__restrict__' >>>>> qualifiers cannot" "" } >>>> >>>> >>>> The C++ front end only needs to check TYPE_RESTRICT for this purpose on >>>> front-end-specific type codes like TEMPLATE_TYPE_PARM; cp_type_quals could >>>> handle that specifically if you change TYPE_RESTRICT to only apply to >>>> pointers. >>>> >>> >>> restrict_flag is also checked in this case: >>> >>> [hjl@gnu-6 gcc]$ cat x.i >>> struct dummy { }; >>> >>> struct dummy >>> foo (struct dummy __restrict__ i) >>> { >>> return i; >>> } >>> [hjl@gnu-6 gcc]$ gcc -S x.i -Wall >>> x.i:4:13: error: invalid use of ‘restrict’ >>> foo (struct dummy __restrict__ i) >>> ^ >>> x.i:4:13: error: invalid use of ‘restrict’ >>> [hjl@gnu-6 gcc]$ >>> >>> restrict_flag can't also be used to indicate `i' is an empty record. >> >> I'm sure this error can be done during parsing w/o relying on TYPE_RESTRICT. >> >> But well, use any other free bit (but do not enlarge >> tree_type_common). Eventually >> you can free up a bit by putting sth into type_lang_specific currently >> using bits >> in tree_type_common. > > There are no bits in tree_type_common I can move. Instead, > this patch overloads side_effects_flag in tree_base. Tested on > Linux/x86-64. OK for trunk? > Hi, Coincidentally a few months ago I was experimenting with making empty-struct function arguments zero-cost (and thus making them behave the same way as in GNU C). My approach (patch attached) was to assign empty-struct arguments to a virtual register (instead of on the stack or to a hard register) during RTL call expansion. These virtual-register assignments would then be trivially DCE'd later. This approach seemed to work surprisingly well with minimal code changes. I wonder what your thoughts are on this approach.. [-- Attachment #2: 0001-zero-cost-structs.patch --] [-- Type: text/x-patch, Size: 3155 bytes --] From 8eb52639992ad0f6e5482783604f362bcc04d230 Mon Sep 17 00:00:00 2001 From: Patrick Palka <patrick@parcs.ath.cx> Date: Mon, 23 Nov 2015 21:02:09 -0500 Subject: [PATCH] zero-cost structs --- gcc/calls.c | 15 +++++++++++++++ gcc/tree-tailcall.c | 7 ++++++- gcc/tree.c | 17 +++++++++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/gcc/calls.c b/gcc/calls.c index b56556a..4ca668c 100644 --- a/gcc/calls.c +++ b/gcc/calls.c @@ -1394,6 +1394,21 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED, args[i].reg = targetm.calls.function_arg (args_so_far, mode, type, argpos < n_named_args); + bool empty_record_or_union_type_p (const_tree); + + if (type != NULL_TREE +#if 0 + /* ??? This condition was necessary to fix a C regression whose + details I have forgot about. In GNU C the mode of an empty struct is BLKmode + (and TYPE_SIZE 0) so this condition makes it so that we don't mess + with the codegen of empty structs in C. In C++ the mode of the empty struct + is QImode and TYPE_SIZE_UNIT 1. Maybe it's not necessary anymore? */ + && mode != BLKmode +#endif + && args[i].reg == NULL_RTX + && empty_record_or_union_type_p (type)) + args[i].reg = gen_reg_rtx (mode); + if (args[i].reg && CONST_INT_P (args[i].reg)) { args[i].special_slot = args[i].reg; diff --git a/gcc/tree-tailcall.c b/gcc/tree-tailcall.c index bbd1b29..fa8f66a 100644 --- a/gcc/tree-tailcall.c +++ b/gcc/tree-tailcall.c @@ -497,6 +497,8 @@ find_tail_calls (basic_block bb, struct tailcall **ret) tail_recursion = true; } + bool empty_record_or_union_type_p (const_tree); + /* Make sure the tail invocation of this function does not refer to local variables. */ FOR_EACH_LOCAL_DECL (cfun, idx, var) @@ -504,7 +506,10 @@ find_tail_calls (basic_block bb, struct tailcall **ret) if (TREE_CODE (var) != PARM_DECL && auto_var_in_fn_p (var, cfun->decl) && (ref_maybe_used_by_stmt_p (call, var) - || call_may_clobber_ref_p (call, var))) + || call_may_clobber_ref_p (call, var)) + /* This change does the same thing as your aliasing change, to allow + tail calling of functions taking by argument empty structs. */ + && !empty_record_or_union_type_p (TREE_TYPE (var))) return; } diff --git a/gcc/tree.c b/gcc/tree.c index 779fe93..f710d15 100644 --- a/gcc/tree.c +++ b/gcc/tree.c @@ -9069,6 +9069,23 @@ auto_var_in_fn_p (const_tree var, const_tree fn) || TREE_CODE (var) == RESULT_DECL)); } +/* Return true if if type TYPE is an empty record or union type. */ + +/* This predicate is inferior to your TYPE_EMPTY_RECORD-flag approach. */ + +bool +empty_record_or_union_type_p (const_tree type) +{ + if (!RECORD_OR_UNION_TYPE_P (type)) + return false; + + for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field)) + if (TREE_CODE (field) == FIELD_DECL) + return false; + + return true; +} + /* Subprogram of following function. Called by walk_tree. Return *TP if it is an automatic variable or parameter of the -- 2.6.3.424.g74c917e.dirty ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH] Add LANG_HOOKS_EMPTY_RECORD_P for C++ empty class 2015-11-24 3:48 ` Patrick Palka @ 2015-11-24 6:00 ` H.J. Lu 2015-11-24 6:10 ` Andrew Pinski 2015-11-24 16:31 ` Patrick Palka 0 siblings, 2 replies; 14+ messages in thread From: H.J. Lu @ 2015-11-24 6:00 UTC (permalink / raw) To: Patrick Palka; +Cc: Richard Biener, Jason Merrill, GCC Patches On Mon, Nov 23, 2015 at 7:22 PM, Patrick Palka <patrick@parcs.ath.cx> wrote: > On Mon, Nov 23, 2015 at 3:53 PM, H.J. Lu <hjl.tools@gmail.com> wrote: >> On Mon, Nov 23, 2015 at 1:57 AM, Richard Biener >> <richard.guenther@gmail.com> wrote: >>> On Sat, Nov 21, 2015 at 12:46 AM, H.J. Lu <hjl.tools@gmail.com> wrote: >>>> On Fri, Nov 20, 2015 at 2:17 PM, Jason Merrill <jason@redhat.com> wrote: >>>>> On 11/20/2015 01:52 PM, H.J. Lu wrote: >>>>>> >>>>>> On Tue, Nov 17, 2015 at 4:22 AM, Richard Biener >>>>>> <richard.guenther@gmail.com> wrote: >>>>>>> >>>>>>> On Tue, Nov 17, 2015 at 12:01 PM, H.J. Lu <hjl.tools@gmail.com> wrote: >>>>>>>> >>>>>>>> Empty record should be returned and passed the same way in C and C++. >>>>>>>> This patch adds LANG_HOOKS_EMPTY_RECORD_P for C++ empty class, which >>>>>>>> defaults to return false. For C++, LANG_HOOKS_EMPTY_RECORD_P is defined >>>>>>>> to is_really_empty_class, which returns true for C++ empty classes. For >>>>>>>> LTO, we stream out a bit to indicate if a record is empty and we store >>>>>>>> it in TYPE_LANG_FLAG_0 when streaming in. get_ref_base_and_extent is >>>>>>>> changed to set bitsize to 0 for empty records. Middle-end and x86 >>>>>>>> backend are updated to ignore empty records for parameter passing and >>>>>>>> function value return. Other targets may need similar changes. >>>>>>> >>>>>>> >>>>>>> Please avoid a new langhook for this and instead claim a bit in >>>>>>> tree_type_common >>>>>>> like for example restrict_flag (double-check it is unused for >>>>>>> non-pointers). >>>>>> >>>>>> >>>>>> There is no bit in tree_type_common I can overload. restrict_flag is >>>>>> checked for non-pointers to issue an error when it is used on >>>>>> non-pointers: >>>>>> >>>>>> >>>>>> /export/gnu/import/git/sources/gcc/gcc/testsuite/g++.dg/template/qualttp20.C:19:38: >>>>>> error: ‘__restrict__’ qualifiers cannot be applied to ‘AS::L’ >>>>>> typedef typename T::L __restrict__ r;// { dg-error "'__restrict__' >>>>>> qualifiers cannot" "" } >>>>> >>>>> >>>>> The C++ front end only needs to check TYPE_RESTRICT for this purpose on >>>>> front-end-specific type codes like TEMPLATE_TYPE_PARM; cp_type_quals could >>>>> handle that specifically if you change TYPE_RESTRICT to only apply to >>>>> pointers. >>>>> >>>> >>>> restrict_flag is also checked in this case: >>>> >>>> [hjl@gnu-6 gcc]$ cat x.i >>>> struct dummy { }; >>>> >>>> struct dummy >>>> foo (struct dummy __restrict__ i) >>>> { >>>> return i; >>>> } >>>> [hjl@gnu-6 gcc]$ gcc -S x.i -Wall >>>> x.i:4:13: error: invalid use of ‘restrict’ >>>> foo (struct dummy __restrict__ i) >>>> ^ >>>> x.i:4:13: error: invalid use of ‘restrict’ >>>> [hjl@gnu-6 gcc]$ >>>> >>>> restrict_flag can't also be used to indicate `i' is an empty record. >>> >>> I'm sure this error can be done during parsing w/o relying on TYPE_RESTRICT. >>> >>> But well, use any other free bit (but do not enlarge >>> tree_type_common). Eventually >>> you can free up a bit by putting sth into type_lang_specific currently >>> using bits >>> in tree_type_common. >> >> There are no bits in tree_type_common I can move. Instead, >> this patch overloads side_effects_flag in tree_base. Tested on >> Linux/x86-64. OK for trunk? >> > > Hi, > > Coincidentally a few months ago I was experimenting with making > empty-struct function arguments zero-cost (and thus making them behave > the same way as in GNU C). My approach (patch attached) was to assign > empty-struct arguments to a virtual register (instead of on the stack > or to a hard register) during RTL call expansion. These > virtual-register assignments would then be trivially DCE'd later. > This approach seemed to work surprisingly well with minimal code > changes. I wonder what > your thoughts are on this approach.. I don't think it works for C++ class. empty_record_or_union_type_p missed: for (binfo = TYPE_BINFO (type), i = 0; BINFO_BASE_ITERATE (binfo, i, base_binfo); ++i) if (!is_really_empty_class (BINFO_TYPE (base_binfo))) return false; Does it work with variable argument list? Did you run GCC testsuite for both i686 and x86-64? -- H.J. ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH] Add LANG_HOOKS_EMPTY_RECORD_P for C++ empty class 2015-11-24 6:00 ` H.J. Lu @ 2015-11-24 6:10 ` Andrew Pinski 2015-11-24 12:52 ` H.J. Lu 2015-11-24 16:31 ` Patrick Palka 1 sibling, 1 reply; 14+ messages in thread From: Andrew Pinski @ 2015-11-24 6:10 UTC (permalink / raw) To: H.J. Lu; +Cc: Patrick Palka, Richard Biener, Jason Merrill, GCC Patches On Mon, Nov 23, 2015 at 9:53 PM, H.J. Lu <hjl.tools@gmail.com> wrote: > On Mon, Nov 23, 2015 at 7:22 PM, Patrick Palka <patrick@parcs.ath.cx> wrote: >> On Mon, Nov 23, 2015 at 3:53 PM, H.J. Lu <hjl.tools@gmail.com> wrote: >>> On Mon, Nov 23, 2015 at 1:57 AM, Richard Biener >>> <richard.guenther@gmail.com> wrote: >>>> On Sat, Nov 21, 2015 at 12:46 AM, H.J. Lu <hjl.tools@gmail.com> wrote: >>>>> On Fri, Nov 20, 2015 at 2:17 PM, Jason Merrill <jason@redhat.com> wrote: >>>>>> On 11/20/2015 01:52 PM, H.J. Lu wrote: >>>>>>> >>>>>>> On Tue, Nov 17, 2015 at 4:22 AM, Richard Biener >>>>>>> <richard.guenther@gmail.com> wrote: >>>>>>>> >>>>>>>> On Tue, Nov 17, 2015 at 12:01 PM, H.J. Lu <hjl.tools@gmail.com> wrote: >>>>>>>>> >>>>>>>>> Empty record should be returned and passed the same way in C and C++. >>>>>>>>> This patch adds LANG_HOOKS_EMPTY_RECORD_P for C++ empty class, which >>>>>>>>> defaults to return false. For C++, LANG_HOOKS_EMPTY_RECORD_P is defined >>>>>>>>> to is_really_empty_class, which returns true for C++ empty classes. For >>>>>>>>> LTO, we stream out a bit to indicate if a record is empty and we store >>>>>>>>> it in TYPE_LANG_FLAG_0 when streaming in. get_ref_base_and_extent is >>>>>>>>> changed to set bitsize to 0 for empty records. Middle-end and x86 >>>>>>>>> backend are updated to ignore empty records for parameter passing and >>>>>>>>> function value return. Other targets may need similar changes. >>>>>>>> >>>>>>>> >>>>>>>> Please avoid a new langhook for this and instead claim a bit in >>>>>>>> tree_type_common >>>>>>>> like for example restrict_flag (double-check it is unused for >>>>>>>> non-pointers). >>>>>>> >>>>>>> >>>>>>> There is no bit in tree_type_common I can overload. restrict_flag is >>>>>>> checked for non-pointers to issue an error when it is used on >>>>>>> non-pointers: >>>>>>> >>>>>>> >>>>>>> /export/gnu/import/git/sources/gcc/gcc/testsuite/g++.dg/template/qualttp20.C:19:38: >>>>>>> error: ‘__restrict__’ qualifiers cannot be applied to ‘AS::L’ >>>>>>> typedef typename T::L __restrict__ r;// { dg-error "'__restrict__' >>>>>>> qualifiers cannot" "" } >>>>>> >>>>>> >>>>>> The C++ front end only needs to check TYPE_RESTRICT for this purpose on >>>>>> front-end-specific type codes like TEMPLATE_TYPE_PARM; cp_type_quals could >>>>>> handle that specifically if you change TYPE_RESTRICT to only apply to >>>>>> pointers. >>>>>> >>>>> >>>>> restrict_flag is also checked in this case: >>>>> >>>>> [hjl@gnu-6 gcc]$ cat x.i >>>>> struct dummy { }; >>>>> >>>>> struct dummy >>>>> foo (struct dummy __restrict__ i) >>>>> { >>>>> return i; >>>>> } >>>>> [hjl@gnu-6 gcc]$ gcc -S x.i -Wall >>>>> x.i:4:13: error: invalid use of ‘restrict’ >>>>> foo (struct dummy __restrict__ i) >>>>> ^ >>>>> x.i:4:13: error: invalid use of ‘restrict’ >>>>> [hjl@gnu-6 gcc]$ >>>>> >>>>> restrict_flag can't also be used to indicate `i' is an empty record. >>>> >>>> I'm sure this error can be done during parsing w/o relying on TYPE_RESTRICT. >>>> >>>> But well, use any other free bit (but do not enlarge >>>> tree_type_common). Eventually >>>> you can free up a bit by putting sth into type_lang_specific currently >>>> using bits >>>> in tree_type_common. >>> >>> There are no bits in tree_type_common I can move. Instead, >>> this patch overloads side_effects_flag in tree_base. Tested on >>> Linux/x86-64. OK for trunk? >>> >> >> Hi, >> >> Coincidentally a few months ago I was experimenting with making >> empty-struct function arguments zero-cost (and thus making them behave >> the same way as in GNU C). My approach (patch attached) was to assign >> empty-struct arguments to a virtual register (instead of on the stack >> or to a hard register) during RTL call expansion. These >> virtual-register assignments would then be trivially DCE'd later. >> This approach seemed to work surprisingly well with minimal code >> changes. I wonder what >> your thoughts are on this approach.. > > I don't think it works for C++ class. empty_record_or_union_type_p > missed: > > for (binfo = TYPE_BINFO (type), i = 0; > BINFO_BASE_ITERATE (binfo, i, base_binfo); ++i) > if (!is_really_empty_class (BINFO_TYPE (base_binfo))) > return false; This above should not be needed as TYPE_FIELDS should include one already. Or do you have prove it does not? Thanks, Andrew > > Does it work with variable argument list? Did you run GCC > testsuite for both i686 and x86-64? > > > -- > H.J. ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH] Add LANG_HOOKS_EMPTY_RECORD_P for C++ empty class 2015-11-24 6:10 ` Andrew Pinski @ 2015-11-24 12:52 ` H.J. Lu 0 siblings, 0 replies; 14+ messages in thread From: H.J. Lu @ 2015-11-24 12:52 UTC (permalink / raw) To: Andrew Pinski; +Cc: Patrick Palka, Richard Biener, Jason Merrill, GCC Patches On Mon, Nov 23, 2015 at 10:00 PM, Andrew Pinski <pinskia@gmail.com> wrote: > On Mon, Nov 23, 2015 at 9:53 PM, H.J. Lu <hjl.tools@gmail.com> wrote: >> On Mon, Nov 23, 2015 at 7:22 PM, Patrick Palka <patrick@parcs.ath.cx> wrote: >>> On Mon, Nov 23, 2015 at 3:53 PM, H.J. Lu <hjl.tools@gmail.com> wrote: >>>> On Mon, Nov 23, 2015 at 1:57 AM, Richard Biener >>>> <richard.guenther@gmail.com> wrote: >>>>> On Sat, Nov 21, 2015 at 12:46 AM, H.J. Lu <hjl.tools@gmail.com> wrote: >>>>>> On Fri, Nov 20, 2015 at 2:17 PM, Jason Merrill <jason@redhat.com> wrote: >>>>>>> On 11/20/2015 01:52 PM, H.J. Lu wrote: >>>>>>>> >>>>>>>> On Tue, Nov 17, 2015 at 4:22 AM, Richard Biener >>>>>>>> <richard.guenther@gmail.com> wrote: >>>>>>>>> >>>>>>>>> On Tue, Nov 17, 2015 at 12:01 PM, H.J. Lu <hjl.tools@gmail.com> wrote: >>>>>>>>>> >>>>>>>>>> Empty record should be returned and passed the same way in C and C++. >>>>>>>>>> This patch adds LANG_HOOKS_EMPTY_RECORD_P for C++ empty class, which >>>>>>>>>> defaults to return false. For C++, LANG_HOOKS_EMPTY_RECORD_P is defined >>>>>>>>>> to is_really_empty_class, which returns true for C++ empty classes. For >>>>>>>>>> LTO, we stream out a bit to indicate if a record is empty and we store >>>>>>>>>> it in TYPE_LANG_FLAG_0 when streaming in. get_ref_base_and_extent is >>>>>>>>>> changed to set bitsize to 0 for empty records. Middle-end and x86 >>>>>>>>>> backend are updated to ignore empty records for parameter passing and >>>>>>>>>> function value return. Other targets may need similar changes. >>>>>>>>> >>>>>>>>> >>>>>>>>> Please avoid a new langhook for this and instead claim a bit in >>>>>>>>> tree_type_common >>>>>>>>> like for example restrict_flag (double-check it is unused for >>>>>>>>> non-pointers). >>>>>>>> >>>>>>>> >>>>>>>> There is no bit in tree_type_common I can overload. restrict_flag is >>>>>>>> checked for non-pointers to issue an error when it is used on >>>>>>>> non-pointers: >>>>>>>> >>>>>>>> >>>>>>>> /export/gnu/import/git/sources/gcc/gcc/testsuite/g++.dg/template/qualttp20.C:19:38: >>>>>>>> error: ‘__restrict__’ qualifiers cannot be applied to ‘AS::L’ >>>>>>>> typedef typename T::L __restrict__ r;// { dg-error "'__restrict__' >>>>>>>> qualifiers cannot" "" } >>>>>>> >>>>>>> >>>>>>> The C++ front end only needs to check TYPE_RESTRICT for this purpose on >>>>>>> front-end-specific type codes like TEMPLATE_TYPE_PARM; cp_type_quals could >>>>>>> handle that specifically if you change TYPE_RESTRICT to only apply to >>>>>>> pointers. >>>>>>> >>>>>> >>>>>> restrict_flag is also checked in this case: >>>>>> >>>>>> [hjl@gnu-6 gcc]$ cat x.i >>>>>> struct dummy { }; >>>>>> >>>>>> struct dummy >>>>>> foo (struct dummy __restrict__ i) >>>>>> { >>>>>> return i; >>>>>> } >>>>>> [hjl@gnu-6 gcc]$ gcc -S x.i -Wall >>>>>> x.i:4:13: error: invalid use of ‘restrict’ >>>>>> foo (struct dummy __restrict__ i) >>>>>> ^ >>>>>> x.i:4:13: error: invalid use of ‘restrict’ >>>>>> [hjl@gnu-6 gcc]$ >>>>>> >>>>>> restrict_flag can't also be used to indicate `i' is an empty record. >>>>> >>>>> I'm sure this error can be done during parsing w/o relying on TYPE_RESTRICT. >>>>> >>>>> But well, use any other free bit (but do not enlarge >>>>> tree_type_common). Eventually >>>>> you can free up a bit by putting sth into type_lang_specific currently >>>>> using bits >>>>> in tree_type_common. >>>> >>>> There are no bits in tree_type_common I can move. Instead, >>>> this patch overloads side_effects_flag in tree_base. Tested on >>>> Linux/x86-64. OK for trunk? >>>> >>> >>> Hi, >>> >>> Coincidentally a few months ago I was experimenting with making >>> empty-struct function arguments zero-cost (and thus making them behave >>> the same way as in GNU C). My approach (patch attached) was to assign >>> empty-struct arguments to a virtual register (instead of on the stack >>> or to a hard register) during RTL call expansion. These >>> virtual-register assignments would then be trivially DCE'd later. >>> This approach seemed to work surprisingly well with minimal code >>> changes. I wonder what >>> your thoughts are on this approach.. >> >> I don't think it works for C++ class. empty_record_or_union_type_p >> missed: >> >> for (binfo = TYPE_BINFO (type), i = 0; >> BINFO_BASE_ITERATE (binfo, i, base_binfo); ++i) >> if (!is_really_empty_class (BINFO_TYPE (base_binfo))) >> return false; > > This above should not be needed as TYPE_FIELDS should include one > already. Or do you have prove it does not? You can remove the above from --- /* Returns true if TYPE contains no actual data, just various possible combinations of empty classes and possibly a vptr. */ bool is_really_empty_class (tree type) { if (CLASS_TYPE_P (type)) { tree field; tree binfo; tree base_binfo; int i; /* CLASSTYPE_EMPTY_P isn't set properly until the class is actually laid out, but we'd like to be able to check this before then. */ if (COMPLETE_TYPE_P (type) && is_empty_class (type)) return true; for (binfo = TYPE_BINFO (type), i = 0; BINFO_BASE_ITERATE (binfo, i, base_binfo); ++i) if (!is_really_empty_class (BINFO_TYPE (base_binfo))) return false; for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field)) if (TREE_CODE (field) == FIELD_DECL && !DECL_ARTIFICIAL (field) && !is_really_empty_class (TREE_TYPE (field))) return false; return true; } else if (TREE_CODE (type) == ARRAY_TYPE) return is_really_empty_class (TREE_TYPE (type)); return false; } --- and see what happens. -- H.J. ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH] Add LANG_HOOKS_EMPTY_RECORD_P for C++ empty class 2015-11-24 6:00 ` H.J. Lu 2015-11-24 6:10 ` Andrew Pinski @ 2015-11-24 16:31 ` Patrick Palka 2015-11-24 16:59 ` H.J. Lu 1 sibling, 1 reply; 14+ messages in thread From: Patrick Palka @ 2015-11-24 16:31 UTC (permalink / raw) To: H.J. Lu; +Cc: Richard Biener, Jason Merrill, GCC Patches On Tue, Nov 24, 2015 at 12:53 AM, H.J. Lu <hjl.tools@gmail.com> wrote: > On Mon, Nov 23, 2015 at 7:22 PM, Patrick Palka <patrick@parcs.ath.cx> wrote: >> On Mon, Nov 23, 2015 at 3:53 PM, H.J. Lu <hjl.tools@gmail.com> wrote: >>> On Mon, Nov 23, 2015 at 1:57 AM, Richard Biener >>> <richard.guenther@gmail.com> wrote: >>>> On Sat, Nov 21, 2015 at 12:46 AM, H.J. Lu <hjl.tools@gmail.com> wrote: >>>>> On Fri, Nov 20, 2015 at 2:17 PM, Jason Merrill <jason@redhat.com> wrote: >>>>>> On 11/20/2015 01:52 PM, H.J. Lu wrote: >>>>>>> >>>>>>> On Tue, Nov 17, 2015 at 4:22 AM, Richard Biener >>>>>>> <richard.guenther@gmail.com> wrote: >>>>>>>> >>>>>>>> On Tue, Nov 17, 2015 at 12:01 PM, H.J. Lu <hjl.tools@gmail.com> wrote: >>>>>>>>> >>>>>>>>> Empty record should be returned and passed the same way in C and C++. >>>>>>>>> This patch adds LANG_HOOKS_EMPTY_RECORD_P for C++ empty class, which >>>>>>>>> defaults to return false. For C++, LANG_HOOKS_EMPTY_RECORD_P is defined >>>>>>>>> to is_really_empty_class, which returns true for C++ empty classes. For >>>>>>>>> LTO, we stream out a bit to indicate if a record is empty and we store >>>>>>>>> it in TYPE_LANG_FLAG_0 when streaming in. get_ref_base_and_extent is >>>>>>>>> changed to set bitsize to 0 for empty records. Middle-end and x86 >>>>>>>>> backend are updated to ignore empty records for parameter passing and >>>>>>>>> function value return. Other targets may need similar changes. >>>>>>>> >>>>>>>> >>>>>>>> Please avoid a new langhook for this and instead claim a bit in >>>>>>>> tree_type_common >>>>>>>> like for example restrict_flag (double-check it is unused for >>>>>>>> non-pointers). >>>>>>> >>>>>>> >>>>>>> There is no bit in tree_type_common I can overload. restrict_flag is >>>>>>> checked for non-pointers to issue an error when it is used on >>>>>>> non-pointers: >>>>>>> >>>>>>> >>>>>>> /export/gnu/import/git/sources/gcc/gcc/testsuite/g++.dg/template/qualttp20.C:19:38: >>>>>>> error: ‘__restrict__’ qualifiers cannot be applied to ‘AS::L’ >>>>>>> typedef typename T::L __restrict__ r;// { dg-error "'__restrict__' >>>>>>> qualifiers cannot" "" } >>>>>> >>>>>> >>>>>> The C++ front end only needs to check TYPE_RESTRICT for this purpose on >>>>>> front-end-specific type codes like TEMPLATE_TYPE_PARM; cp_type_quals could >>>>>> handle that specifically if you change TYPE_RESTRICT to only apply to >>>>>> pointers. >>>>>> >>>>> >>>>> restrict_flag is also checked in this case: >>>>> >>>>> [hjl@gnu-6 gcc]$ cat x.i >>>>> struct dummy { }; >>>>> >>>>> struct dummy >>>>> foo (struct dummy __restrict__ i) >>>>> { >>>>> return i; >>>>> } >>>>> [hjl@gnu-6 gcc]$ gcc -S x.i -Wall >>>>> x.i:4:13: error: invalid use of ‘restrict’ >>>>> foo (struct dummy __restrict__ i) >>>>> ^ >>>>> x.i:4:13: error: invalid use of ‘restrict’ >>>>> [hjl@gnu-6 gcc]$ >>>>> >>>>> restrict_flag can't also be used to indicate `i' is an empty record. >>>> >>>> I'm sure this error can be done during parsing w/o relying on TYPE_RESTRICT. >>>> >>>> But well, use any other free bit (but do not enlarge >>>> tree_type_common). Eventually >>>> you can free up a bit by putting sth into type_lang_specific currently >>>> using bits >>>> in tree_type_common. >>> >>> There are no bits in tree_type_common I can move. Instead, >>> this patch overloads side_effects_flag in tree_base. Tested on >>> Linux/x86-64. OK for trunk? >>> >> >> Hi, >> >> Coincidentally a few months ago I was experimenting with making >> empty-struct function arguments zero-cost (and thus making them behave >> the same way as in GNU C). My approach (patch attached) was to assign >> empty-struct arguments to a virtual register (instead of on the stack >> or to a hard register) during RTL call expansion. These >> virtual-register assignments would then be trivially DCE'd later. >> This approach seemed to work surprisingly well with minimal code >> changes. I wonder what >> your thoughts are on this approach.. > > I don't think it works for C++ class. empty_record_or_union_type_p > missed: > > for (binfo = TYPE_BINFO (type), i = 0; > BINFO_BASE_ITERATE (binfo, i, base_binfo); ++i) > if (!is_really_empty_class (BINFO_TYPE (base_binfo))) > return false; Yeah, your TYPE_EMPTY_RECORD flag covers more instances of empty structs than this predicate does. > > Does it work with variable argument list? Did you run GCC > testsuite for both i686 and x86-64? Hmm, I don't think it works with variable argument lists, at least not perfectly. And I just finished running the testsuite on x86-64 and observed a failure in struct-layout-1.exp which makes no sense to me. Now I remember why I didn't pursue this change any further. ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH] Add LANG_HOOKS_EMPTY_RECORD_P for C++ empty class 2015-11-24 16:31 ` Patrick Palka @ 2015-11-24 16:59 ` H.J. Lu 0 siblings, 0 replies; 14+ messages in thread From: H.J. Lu @ 2015-11-24 16:59 UTC (permalink / raw) To: Patrick Palka; +Cc: Richard Biener, Jason Merrill, GCC Patches On Tue, Nov 24, 2015 at 8:28 AM, Patrick Palka <patrick@parcs.ath.cx> wrote: > On Tue, Nov 24, 2015 at 12:53 AM, H.J. Lu <hjl.tools@gmail.com> wrote: >> On Mon, Nov 23, 2015 at 7:22 PM, Patrick Palka <patrick@parcs.ath.cx> wrote: >>> On Mon, Nov 23, 2015 at 3:53 PM, H.J. Lu <hjl.tools@gmail.com> wrote: >>>> On Mon, Nov 23, 2015 at 1:57 AM, Richard Biener >>>> <richard.guenther@gmail.com> wrote: >>>>> On Sat, Nov 21, 2015 at 12:46 AM, H.J. Lu <hjl.tools@gmail.com> wrote: >>>>>> On Fri, Nov 20, 2015 at 2:17 PM, Jason Merrill <jason@redhat.com> wrote: >>>>>>> On 11/20/2015 01:52 PM, H.J. Lu wrote: >>>>>>>> >>>>>>>> On Tue, Nov 17, 2015 at 4:22 AM, Richard Biener >>>>>>>> <richard.guenther@gmail.com> wrote: >>>>>>>>> >>>>>>>>> On Tue, Nov 17, 2015 at 12:01 PM, H.J. Lu <hjl.tools@gmail.com> wrote: >>>>>>>>>> >>>>>>>>>> Empty record should be returned and passed the same way in C and C++. >>>>>>>>>> This patch adds LANG_HOOKS_EMPTY_RECORD_P for C++ empty class, which >>>>>>>>>> defaults to return false. For C++, LANG_HOOKS_EMPTY_RECORD_P is defined >>>>>>>>>> to is_really_empty_class, which returns true for C++ empty classes. For >>>>>>>>>> LTO, we stream out a bit to indicate if a record is empty and we store >>>>>>>>>> it in TYPE_LANG_FLAG_0 when streaming in. get_ref_base_and_extent is >>>>>>>>>> changed to set bitsize to 0 for empty records. Middle-end and x86 >>>>>>>>>> backend are updated to ignore empty records for parameter passing and >>>>>>>>>> function value return. Other targets may need similar changes. >>>>>>>>> >>>>>>>>> >>>>>>>>> Please avoid a new langhook for this and instead claim a bit in >>>>>>>>> tree_type_common >>>>>>>>> like for example restrict_flag (double-check it is unused for >>>>>>>>> non-pointers). >>>>>>>> >>>>>>>> >>>>>>>> There is no bit in tree_type_common I can overload. restrict_flag is >>>>>>>> checked for non-pointers to issue an error when it is used on >>>>>>>> non-pointers: >>>>>>>> >>>>>>>> >>>>>>>> /export/gnu/import/git/sources/gcc/gcc/testsuite/g++.dg/template/qualttp20.C:19:38: >>>>>>>> error: ‘__restrict__’ qualifiers cannot be applied to ‘AS::L’ >>>>>>>> typedef typename T::L __restrict__ r;// { dg-error "'__restrict__' >>>>>>>> qualifiers cannot" "" } >>>>>>> >>>>>>> >>>>>>> The C++ front end only needs to check TYPE_RESTRICT for this purpose on >>>>>>> front-end-specific type codes like TEMPLATE_TYPE_PARM; cp_type_quals could >>>>>>> handle that specifically if you change TYPE_RESTRICT to only apply to >>>>>>> pointers. >>>>>>> >>>>>> >>>>>> restrict_flag is also checked in this case: >>>>>> >>>>>> [hjl@gnu-6 gcc]$ cat x.i >>>>>> struct dummy { }; >>>>>> >>>>>> struct dummy >>>>>> foo (struct dummy __restrict__ i) >>>>>> { >>>>>> return i; >>>>>> } >>>>>> [hjl@gnu-6 gcc]$ gcc -S x.i -Wall >>>>>> x.i:4:13: error: invalid use of ‘restrict’ >>>>>> foo (struct dummy __restrict__ i) >>>>>> ^ >>>>>> x.i:4:13: error: invalid use of ‘restrict’ >>>>>> [hjl@gnu-6 gcc]$ >>>>>> >>>>>> restrict_flag can't also be used to indicate `i' is an empty record. >>>>> >>>>> I'm sure this error can be done during parsing w/o relying on TYPE_RESTRICT. >>>>> >>>>> But well, use any other free bit (but do not enlarge >>>>> tree_type_common). Eventually >>>>> you can free up a bit by putting sth into type_lang_specific currently >>>>> using bits >>>>> in tree_type_common. >>>> >>>> There are no bits in tree_type_common I can move. Instead, >>>> this patch overloads side_effects_flag in tree_base. Tested on >>>> Linux/x86-64. OK for trunk? >>>> >>> >>> Hi, >>> >>> Coincidentally a few months ago I was experimenting with making >>> empty-struct function arguments zero-cost (and thus making them behave >>> the same way as in GNU C). My approach (patch attached) was to assign >>> empty-struct arguments to a virtual register (instead of on the stack >>> or to a hard register) during RTL call expansion. These >>> virtual-register assignments would then be trivially DCE'd later. >>> This approach seemed to work surprisingly well with minimal code >>> changes. I wonder what >>> your thoughts are on this approach.. >> >> I don't think it works for C++ class. empty_record_or_union_type_p >> missed: >> >> for (binfo = TYPE_BINFO (type), i = 0; >> BINFO_BASE_ITERATE (binfo, i, base_binfo); ++i) >> if (!is_really_empty_class (BINFO_TYPE (base_binfo))) >> return false; > > Yeah, your TYPE_EMPTY_RECORD flag covers more instances of empty > structs than this predicate does. > >> >> Does it work with variable argument list? Did you run GCC >> testsuite for both i686 and x86-64? > > Hmm, I don't think it works with variable argument lists, at least not > perfectly. And I just finished running the testsuite on x86-64 and > observed a failure in struct-layout-1.exp which makes no sense to me. > Now I remember why I didn't pursue this change any further. I tried a similar approach and got quite a few C++ failures in gcc testsuite. There were more failures on i686 than x86-64. See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60336 for more details. My current patch passes all tests on i686 and x86-64. -- H.J. ^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2015-11-24 16:35 UTC | newest] Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2015-11-17 11:01 [PATCH] Add LANG_HOOKS_EMPTY_RECORD_P for C++ empty class H.J. Lu 2015-11-17 12:22 ` Richard Biener 2015-11-20 18:52 ` H.J. Lu 2015-11-20 22:17 ` Jason Merrill 2015-11-20 22:24 ` Jason Merrill 2015-11-20 23:47 ` H.J. Lu 2015-11-23 9:59 ` Richard Biener 2015-11-23 20:54 ` H.J. Lu 2015-11-24 3:48 ` Patrick Palka 2015-11-24 6:00 ` H.J. Lu 2015-11-24 6:10 ` Andrew Pinski 2015-11-24 12:52 ` H.J. Lu 2015-11-24 16:31 ` Patrick Palka 2015-11-24 16:59 ` H.J. Lu
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).