public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [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).