On Tue, Nov 17, 2015 at 12:01 PM, H.J. Lu <hjl.to...@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 >