This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[ABI PATCH] static anonymous unions of function scope


I fell over this on the modules branch, first with simplifying our internal structures, and then finding a bug.

Local entities can need a discriminator in their mangling. We didn't always get that right.

Firstly, we rarely need a discriminator, and a non-zero one is even rarer. So there's no great loss in encoding it as an INTEGER_CST -- especially as those are cached. This allows us to get rid of a union in lang_decl_min, and hence save a discriminator bit.

When mangling the name of a function-scope static union:

void foo ()
{
  { static union X m; };
  { static union X m; };
}

We need to emit a discriminator on the second of those m's, and we were doing this via a local array that we searched. This breaks down if the union is anonymous though, because we end up giving it a unique name, and then think the two unions are suitably unique already.

We'd also not check between unions and other local entities of the same name. Thus in the testcase we'd erronously mangle all but one local static without a discriminator.

We were also using per-function local lists, which aren't really needed -- use a GC-deletable list and then no need to save/restore it. It's rarely non-empty. We then explicitly store the name as well as the VAR_DECL.

Although this is an ABI change, it seems a very obscure corner I'm not sure an ABI warning is warranted. Jason? It might be hard to get it right without some false positives.

booted & tested on x86_64-linux

nathan

--
Nathan Sidwell
2018-10-31  Nathan Sidwell  <nathan@acm.org>

	* cp-tree.h (struct lang_function): Delete x_local_names field.
	(struct lang_decl_base): Rename u2sel to spare.
	(struct lang_decl_min): Remove lang_decl_u2 union.  Keep access
	field.
	(LANG_DECL_U2_CHECK): Delete.
	(DECL_DISCRIMINATOR_P): Require function scope.
	(DECL_DISCRIMINATOR): Adjust.
	(DECL_DISCRIMINATOR_SET_P): Delete.
	(DECL_CAPTURED_VARIABLE, DECL_ACCESS, THUnK_VIRTUAL_OFFSET): Adjust.
	(local_classes): Don't declare.
	(determine_local_discriminator): Declare.
	* decl.c (push_local_name): Delete.
	(local_entities, determina_local_discrminator): New.
	(duplicate_decls): Copy DECL_ACCESS. Fix formatting.
	(cp_finish_decl): Use determine_local_discriminator.
	(save_function_data): Drop x_local_names.
	(finish_function): Drop local_names.
	* decl2.c (finish_anon_union): Use determine_local_disciminator.
	* mangle.c (write_unnamed_type_name): Use
	discriminator_for_local_entity.
	(local_class_index): Delete.
	(discriminator_for_local_entity): Reimplement.
	(write_local_name): Adjust discriminator code.
	* name-lookup.c (do_pushtag): Call determine_local_discrimiator.
	* semantics.c (finish_omp_threadprivate): Drop DECL_DISCRIMINATOR
	handling.
	* class.c (local_classes): Delete.
	(init_class_processing): Don't init it.

	* g++.dg/abi/anon5.C: New.

Index: cp/class.c
===================================================================
--- cp/class.c	(revision 265692)
+++ cp/class.c	(working copy)
@@ -111,10 +111,6 @@ static class_stack_node_t current_class_
 /* The size of the largest empty class seen in this translation unit.  */
 static GTY (()) tree sizeof_biggest_empty_class;
 
-/* An array of all local classes present in this translation unit, in
-   declaration order.  */
-vec<tree, va_gc> *local_classes;
-
 static tree get_vfield_name (tree);
 static void finish_struct_anon (tree);
 static tree get_vtable_name (tree);
@@ -7431,7 +7427,6 @@ init_class_processing (void)
   current_class_stack_size = 10;
   current_class_stack
     = XNEWVEC (struct class_stack_node, current_class_stack_size);
-  vec_alloc (local_classes, 8);
   sizeof_biggest_empty_class = size_zero_node;
 
   ridpointers[(int) RID_PUBLIC] = access_public_node;
Index: cp/cp-tree.h
===================================================================
--- cp/cp-tree.h	(revision 265692)
+++ cp/cp-tree.h	(working copy)
@@ -1793,7 +1793,7 @@ struct GTY(()) language_function {
   hash_table<named_label_hash> *x_named_labels;
 
   cp_binding_level *bindings;
-  vec<tree, va_gc> *x_local_names;
+
   /* Tracking possibly infinite loops.  This is a vec<tree> only because
      vec<bool> doesn't work with gtype.  */
   vec<tree, va_gc> *infinite_loops;
@@ -2527,7 +2527,7 @@ struct GTY(()) lang_decl_base {
   unsigned friend_or_tls : 1;		   /* var, fn, type or template */
   unsigned unknown_bound_p : 1;		   /* var */
   unsigned odr_used : 1;		   /* var or fn */
-  unsigned u2sel : 1;
+  unsigned spare : 1;
   unsigned concept_p : 1;                  /* applies to vars and functions */
   unsigned var_declared_inline_p : 1;	   /* var */
   unsigned dependent_init_p : 1;	   /* var */
@@ -2555,17 +2555,12 @@ struct GTY(()) lang_decl_min {
      DECL_TEMPLATE_INFO.  */
   tree template_info;
 
-  union lang_decl_u2 {
-    /* In a FUNCTION_DECL for which DECL_THUNK_P holds, this is
-       THUNK_VIRTUAL_OFFSET.
-       In a VAR_DECL for which DECL_HAS_VALUE_EXPR_P holds,
-       this is DECL_CAPTURED_VARIABLE.
-       Otherwise this is DECL_ACCESS.  */
-    tree GTY ((tag ("0"))) access;
-
-    /* For TREE_STATIC VAR_DECL in function, this is DECL_DISCRIMINATOR.  */
-    int GTY ((tag ("1"))) discriminator;
-  } GTY ((desc ("%0.u.base.u2sel"))) u2;
+  /* In a DECL_THUNK_P FUNCTION_DECL, this is THUNK_VIRTUAL_OFFSET.
+     In a lambda-capture proxy VAR_DECL, this is DECL_CAPTURED_VARIABLE.
+     In a function-scope TREE_STATIC VAR_DECL or IMPLICIT_TYPEDEF_P TYPE_DECL,
+     this is DECL_DISCRIMINATOR.
+     Otherwise, in a class-scope DECL, this is DECL_ACCESS.   */
+  tree access;
 };
 
 /* Additional DECL_LANG_SPECIFIC information for functions.  */
@@ -2721,12 +2716,6 @@ struct GTY(()) lang_decl {
     lang_check_failed (__FILE__, __LINE__, __FUNCTION__);	\
   &lt->u.decomp; })
 
-#define LANG_DECL_U2_CHECK(NODE, TF) __extension__		\
-({  struct lang_decl *lt = DECL_LANG_SPECIFIC (NODE);		\
-    if (!LANG_DECL_HAS_MIN (NODE) || lt->u.base.u2sel != TF)	\
-      lang_check_failed (__FILE__, __LINE__, __FUNCTION__);	\
-    &lt->u.min.u2; })
-
 #else
 
 #define LANG_DECL_MIN_CHECK(NODE) \
@@ -2744,9 +2733,6 @@ struct GTY(()) lang_decl {
 #define LANG_DECL_DECOMP_CHECK(NODE) \
   (&DECL_LANG_SPECIFIC (NODE)->u.decomp)
 
-#define LANG_DECL_U2_CHECK(NODE, TF) \
-  (&DECL_LANG_SPECIFIC (NODE)->u.min.u2)
-
 #endif /* ENABLE_TREE_CHECKING */
 
 /* For a FUNCTION_DECL or a VAR_DECL, the language linkage for the
@@ -2854,15 +2840,13 @@ struct GTY(()) lang_decl {
 	 CLONE = DECL_CHAIN (CLONE))
 
 /* Nonzero if NODE has DECL_DISCRIMINATOR and not DECL_ACCESS.  */
-#define DECL_DISCRIMINATOR_P(NODE)	\
-  (VAR_P (NODE) && DECL_FUNCTION_SCOPE_P (NODE))
+#define DECL_DISCRIMINATOR_P(NODE)				\
+  (((TREE_CODE (NODE) == VAR_DECL && TREE_STATIC (NODE))	\
+    || DECL_IMPLICIT_TYPEDEF_P (NODE))				\
+   && DECL_FUNCTION_SCOPE_P (NODE))
 
 /* Discriminator for name mangling.  */
-#define DECL_DISCRIMINATOR(NODE) (LANG_DECL_U2_CHECK (NODE, 1)->discriminator)
-
-/* True iff DECL_DISCRIMINATOR is set for a DECL_DISCRIMINATOR_P decl.  */
-#define DECL_DISCRIMINATOR_SET_P(NODE) \
-  (DECL_LANG_SPECIFIC (NODE) && DECL_LANG_SPECIFIC (NODE)->u.base.u2sel == 1)
+#define DECL_DISCRIMINATOR(NODE) (LANG_DECL_MIN_CHECK (NODE)->access)
 
 /* The index of a user-declared parameter in its function, starting at 1.
    All artificial parameters will have index 0.  */
@@ -3326,7 +3310,7 @@ struct GTY(()) lang_decl {
 
 /* For a lambda capture proxy, its captured variable.  */
 #define DECL_CAPTURED_VARIABLE(NODE) \
-  (LANG_DECL_U2_CHECK (NODE, 0)->access)
+  (LANG_DECL_MIN_CHECK (NODE)->access)
 
 /* For a VAR_DECL, indicates that the variable is actually a
    non-static data member of anonymous union that has been promoted to
@@ -4501,7 +4485,7 @@ more_aggr_init_expr_args_p (const aggr_i
    For example, if a member that would normally be public in a
    derived class is made protected, then the derived class and the
    protected_access_node will appear in the DECL_ACCESS for the node.  */
-#define DECL_ACCESS(NODE) (LANG_DECL_U2_CHECK (NODE, 0)->access)
+#define DECL_ACCESS(NODE) (LANG_DECL_MIN_CHECK (NODE)->access)
 
 /* Nonzero if the FUNCTION_DECL is a global constructor.  */
 #define DECL_GLOBAL_CTOR_P(NODE) \
@@ -4838,7 +4822,7 @@ more_aggr_init_expr_args_p (const aggr_i
    binfos.)  */
 
 #define THUNK_VIRTUAL_OFFSET(DECL) \
-  (LANG_DECL_U2_CHECK (FUNCTION_DECL_CHECK (DECL), 0)->access)
+  (LANG_DECL_MIN_CHECK (FUNCTION_DECL_CHECK (DECL))->access)
 
 /* A thunk which is equivalent to another thunk.  */
 #define THUNK_ALIAS(DECL) \
@@ -5232,10 +5216,6 @@ struct local_specialization_stack
 
 extern int current_class_depth;
 
-/* An array of all local classes present in this translation unit, in
-   declaration order.  */
-extern GTY(()) vec<tree, va_gc> *local_classes;
-
 /* in decl.c */
 
 /* An array of static vars & fns.  */
@@ -6295,6 +6275,7 @@ extern void pop_switch				(void);
 extern void note_break_stmt			(void);
 extern bool note_iteration_stmt_body_start	(void);
 extern void note_iteration_stmt_body_end	(bool);
+extern void determine_local_discriminator	(tree);
 extern tree make_lambda_name			(void);
 extern int decls_match				(tree, tree, bool = true);
 extern bool maybe_version_functions		(tree, tree, bool);
Index: cp/decl.c
===================================================================
--- cp/decl.c	(revision 265692)
+++ cp/decl.c	(working copy)
@@ -66,7 +66,6 @@ static const char *redeclaration_error_m
 
 static int decl_jump_unsafe (tree);
 static void require_complete_types_for_parms (tree);
-static void push_local_name (tree);
 static tree grok_reference_init (tree, tree, tree, int);
 static tree grokvardecl (tree, tree, tree, const cp_decl_specifier_seq *,
 			 int, int, int, bool, int, tree);
@@ -138,8 +137,6 @@ static void expand_static_init (tree, tr
 
 tree cp_global_trees[CPTI_MAX];
 
-#define local_names cp_function_chain->x_local_names
-
 /* A list of objects which have constructors or destructors
    which reside in the global scope.  The decl is stored in
    the TREE_VALUE slot and the initializer is stored
@@ -871,40 +868,57 @@ create_implicit_typedef (tree name, tree
   return decl;
 }
 
-/* Remember a local name for name-mangling purposes.  */
+/* Function-scope local entities that need discriminators.  Each entry
+   is a {decl,name} pair.  VAR_DECLs for anon unions get their name
+   smashed, so we cannot rely on DECL_NAME.  */
 
-static void
-push_local_name (tree decl)
-{
-  size_t i, nelts;
-  tree t, name;
+static GTY((deletable)) vec<tree, va_gc> *local_entities;
 
-  timevar_start (TV_NAME_LOOKUP);
+/* Determine the mangling discriminator of local DECL.  There are
+   generally very few of these in any particular function.  */
 
-  name = DECL_NAME (decl);
+void
+determine_local_discriminator (tree decl)
+{
+  bool subtime = timevar_cond_start (TV_NAME_LOOKUP);
+  retrofit_lang_decl (decl);
+  tree ctx = DECL_CONTEXT (decl);
+  tree name = (TREE_CODE (decl) == TYPE_DECL
+	       && TYPE_UNNAMED_P (TREE_TYPE (decl))
+	       ? NULL_TREE : DECL_NAME (decl));
+  size_t nelts = vec_safe_length (local_entities);
+  for (size_t i = 0; i < nelts; i += 2)
+    {
+      tree *pair = &(*local_entities)[i];
+      tree d = pair[0];
+      tree n = pair[1];
+      gcc_checking_assert (d != decl);
+      if (name == n
+	  && TREE_CODE (decl) == TREE_CODE (d)
+	  && ctx == DECL_CONTEXT (d))
+	{
+	  tree disc = integer_one_node;
+	  if (DECL_DISCRIMINATOR (d))
+	    disc = build_int_cst (TREE_TYPE (disc),
+				  TREE_INT_CST_LOW (DECL_DISCRIMINATOR (d)) + 1);
+	  DECL_DISCRIMINATOR (decl) = disc;
+	  /* Replace the saved decl.  */
+	  pair[0] = decl;
+	  decl = NULL_TREE;
+	  break;
+	}
+    }
 
-  nelts = vec_safe_length (local_names);
-  for (i = 0; i < nelts; i++)
+  if (decl)
     {
-      t = (*local_names)[i];
-      if (DECL_NAME (t) == name)
-	{
-	  retrofit_lang_decl (decl);
-	  DECL_LANG_SPECIFIC (decl)->u.base.u2sel = 1;
-	  if (DECL_DISCRIMINATOR_SET_P (t))
-	    DECL_DISCRIMINATOR (decl) = DECL_DISCRIMINATOR (t) + 1;
-	  else
-	    DECL_DISCRIMINATOR (decl) = 1;
-
-	  (*local_names)[i] = decl;
-	  timevar_stop (TV_NAME_LOOKUP);
-	  return;
-	}
+      vec_safe_reserve (local_entities, 2);
+      local_entities->quick_push (decl);
+      local_entities->quick_push (name);
     }
 
-  vec_safe_push (local_names, decl);
-  timevar_stop (TV_NAME_LOOKUP);
+  timevar_cond_stop (TV_NAME_LOOKUP, subtime);
 }
+
 
 /* Subroutine of duplicate_decls: return truthvalue of whether
    or not types of these decls match.
@@ -2360,8 +2374,7 @@ next_arg:;
 
       if (LANG_DECL_HAS_MIN (newdecl))
 	{
-	  DECL_LANG_SPECIFIC (newdecl)->u.min.u2 =
-	    DECL_LANG_SPECIFIC (olddecl)->u.min.u2;
+	  DECL_ACCESS (newdecl) = DECL_ACCESS (olddecl);
 	  if (DECL_TEMPLATE_INFO (newdecl))
 	    {
 	      new_template_info = DECL_TEMPLATE_INFO (newdecl);
@@ -2397,15 +2410,15 @@ next_arg:;
 
       /* Merge parameter attributes. */
       tree oldarg, newarg;
-      for (oldarg = DECL_ARGUMENTS(olddecl), 
-               newarg = DECL_ARGUMENTS(newdecl);
+      for (oldarg = DECL_ARGUMENTS(olddecl),  newarg = DECL_ARGUMENTS(newdecl);
            oldarg && newarg;
-           oldarg = DECL_CHAIN(oldarg), newarg = DECL_CHAIN(newarg)) {
+           oldarg = DECL_CHAIN(oldarg), newarg = DECL_CHAIN(newarg))
+	{
           DECL_ATTRIBUTES (newarg)
-              = (*targetm.merge_decl_attributes) (oldarg, newarg);
+	    = (*targetm.merge_decl_attributes) (oldarg, newarg);
           DECL_ATTRIBUTES (oldarg) = DECL_ATTRIBUTES (newarg);
-      }
-      
+	}
+
       if (DECL_TEMPLATE_INSTANTIATION (olddecl)
 	  && !DECL_TEMPLATE_INSTANTIATION (newdecl))
 	{
@@ -7122,7 +7135,11 @@ cp_finish_decl (tree decl, tree init, bo
 	  && TREE_STATIC (decl)
 	  && !DECL_ARTIFICIAL (decl))
 	{
-	  push_local_name (decl);
+	  /* The variable holding an anonymous union will have had its
+	     discriminator set in finish_anon_union, after which it's
+	     NAME will have been cleared.  */
+	  if (DECL_NAME (decl))
+	    determine_local_discriminator (decl);
 	  /* Normally has_forced_label_in_static is set during GIMPLE
 	     lowering, but [cd]tors are never actually compiled directly.
 	     We need to set this early so we can deal with the label
@@ -15607,7 +15624,6 @@ save_function_data (tree decl)
   /* Clear out the bits we don't need.  */
   f->base.x_stmt_tree.x_cur_stmt_list = NULL;
   f->bindings = NULL;
-  f->x_local_names = NULL;
   f->base.local_typedefs = NULL;
 }
 
@@ -16112,8 +16128,6 @@ finish_function (bool inline_p)
       f->extern_decl_map = NULL;
       f->infinite_loops = NULL;
     }
-  /* Clear out the bits we don't need.  */
-  local_names = NULL;
 
   /* We're leaving the context of this function, so zap cfun.  It's still in
      DECL_STRUCT_FUNCTION, and we'll restore it in tree_rest_of_compilation.  */
Index: cp/decl2.c
===================================================================
--- cp/decl2.c	(revision 265692)
+++ cp/decl2.c	(working copy)
@@ -1666,7 +1666,11 @@ finish_anon_union (tree anon_union_decl)
       DECL_NAME (anon_union_decl) = DECL_NAME (main_decl);
       maybe_commonize_var (anon_union_decl);
       if (TREE_STATIC (anon_union_decl) || DECL_EXTERNAL (anon_union_decl))
-	mangle_decl (anon_union_decl);
+	{
+	  if (DECL_DISCRIMINATOR_P (anon_union_decl))
+	    determine_local_discriminator (anon_union_decl);
+	  mangle_decl (anon_union_decl);
+	}
       DECL_NAME (anon_union_decl) = NULL_TREE;
     }
 
Index: cp/mangle.c
===================================================================
--- cp/mangle.c	(revision 265692)
+++ cp/mangle.c	(working copy)
@@ -233,7 +233,6 @@ static void write_discriminator (const i
 static void write_local_name (tree, const tree, const tree);
 static void dump_substitution_candidates (void);
 static tree mangle_decl_string (const tree);
-static int local_class_index (tree);
 static void maybe_check_abi_tags (tree, tree = NULL_TREE, int = 10);
 static bool equal_abi_tags (tree, tree);
 
@@ -1642,7 +1641,7 @@ write_unnamed_type_name (const tree type
   MANGLE_TRACE_TREE ("unnamed-type-name", type);
 
   if (TYPE_FUNCTION_SCOPE_P (type))
-    discriminator = local_class_index (type);
+    discriminator = discriminator_for_local_entity (TYPE_NAME (type));
   else if (TYPE_CLASS_SCOPE_P (type))
     discriminator = nested_anon_class_index (type);
   else
@@ -1913,58 +1912,25 @@ write_special_name_destructor (const tre
     }
 }
 
-/* Scan the vector of local classes and return how many others with the
-   same name (or same no name) and context precede ENTITY.  */
-
-static int
-local_class_index (tree entity)
-{
-  int ix, discriminator = 0;
-  tree name = (TYPE_UNNAMED_P (entity) ? NULL_TREE
-	       : TYPE_IDENTIFIER (entity));
-  tree ctx = TYPE_CONTEXT (entity);
-  for (ix = 0; ; ix++)
-    {
-      tree type = (*local_classes)[ix];
-      if (type == entity)
-	return discriminator;
-      if (TYPE_CONTEXT (type) == ctx
-	  && (name ? TYPE_IDENTIFIER (type) == name
-	      : TYPE_UNNAMED_P (type)))
-	++discriminator;
-    }
-  gcc_unreachable ();
-}
-
 /* Return the discriminator for ENTITY appearing inside
-   FUNCTION.  The discriminator is the lexical ordinal of VAR among
-   entities with the same name in the same FUNCTION.  */
+   FUNCTION.  The discriminator is the lexical ordinal of VAR or TYPE among
+   entities with the same name and kind in the same FUNCTION.  */
 
 static int
 discriminator_for_local_entity (tree entity)
 {
-  if (DECL_DISCRIMINATOR_P (entity))
+  if (!DECL_LANG_SPECIFIC (entity))
     {
-      if (DECL_DISCRIMINATOR_SET_P (entity))
-	return DECL_DISCRIMINATOR (entity);
-      else
-	/* The first entity with a particular name doesn't get
-	   DECL_DISCRIMINATOR set up.  */
-	return 0;
-    }
-  else if (TREE_CODE (entity) == TYPE_DECL)
-    {
-      /* Scan the list of local classes.  */
-      entity = TREE_TYPE (entity);
-
-      /* Lambdas and unnamed types have their own discriminators.  */
-      if (LAMBDA_TYPE_P (entity) || TYPE_UNNAMED_P (entity))
-	return 0;
-
-      return local_class_index (entity);
+      /* Some decls, like __FUNCTION__, don't need a discriminator.  */
+      gcc_checking_assert (DECL_ARTIFICIAL (entity));
+      return 0;
     }
+  else if (tree disc = DECL_DISCRIMINATOR (entity))
+    return TREE_INT_CST_LOW (disc);
   else
-    gcc_unreachable ();
+    /* The first entity with a particular name doesn't get
+       DECL_DISCRIMINATOR set up.  */
+    return 0;
 }
 
 /* Return the discriminator for STRING, a string literal used inside
@@ -2062,7 +2028,11 @@ write_local_name (tree function, const t
 	 from <local-name>, so it doesn't try to process the enclosing
 	 function scope again.  */
       write_name (entity, /*ignore_local_scope=*/1);
-      write_discriminator (discriminator_for_local_entity (local_entity));
+      if (DECL_DISCRIMINATOR_P (local_entity)
+	  && !(TREE_CODE (local_entity) == TYPE_DECL
+	       && (LAMBDA_TYPE_P (TREE_TYPE (local_entity))
+		   || TYPE_UNNAMED_P (TREE_TYPE (local_entity)))))
+	write_discriminator (discriminator_for_local_entity (local_entity));
     }
 }
 
Index: cp/name-lookup.c
===================================================================
--- cp/name-lookup.c	(revision 265692)
+++ cp/name-lookup.c	(working copy)
@@ -6879,7 +6879,7 @@ do_pushtag (tree name, tree type, tag_sc
 	    }
 	  /* Lambdas use LAMBDA_EXPR_DISCRIMINATOR instead.  */
 	  else if (!LAMBDA_TYPE_P (type))
-	    vec_safe_push (local_classes, type);
+	    determine_local_discriminator (TYPE_NAME (type));
 	}
     }
 
Index: cp/semantics.c
===================================================================
--- cp/semantics.c	(revision 265692)
+++ cp/semantics.c	(working copy)
@@ -7566,14 +7566,7 @@ finish_omp_threadprivate (tree vars)
 	{
 	  /* Allocate a LANG_SPECIFIC structure for V, if needed.  */
 	  if (DECL_LANG_SPECIFIC (v) == NULL)
-	    {
-	      retrofit_lang_decl (v);
-
-	      /* Make sure that DECL_DISCRIMINATOR_P continues to be true
-		 after the allocation of the lang_decl structure.  */
-	      if (DECL_DISCRIMINATOR_P (v))
-		DECL_LANG_SPECIFIC (v)->u.base.u2sel = 1;
-	    }
+	    retrofit_lang_decl (v);
 
 	  if (! CP_DECL_THREAD_LOCAL_P (v))
 	    {
Index: testsuite/g++.dg/abi/anon5.C
===================================================================
--- testsuite/g++.dg/abi/anon5.C	(revision 0)
+++ testsuite/g++.dg/abi/anon5.C	(working copy)
@@ -0,0 +1,14 @@
+// anon-union decls may need a discriminator
+
+void f ()
+{
+  { static int bob; }
+  { static union {int bob;};}
+  { static int bob; }
+  { static union {int bob;}; }
+}
+
+// { dg-final { scan-assembler {_ZZ1fvE3bob[^_]} } }
+// { dg-final { scan-assembler {_ZZ1fvE3bob_0} } }
+// { dg-final { scan-assembler {_ZZ1fvE3bob_1} } }
+// { dg-final { scan-assembler {_ZZ1fvE3bob_2} } }

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]