From: Nathan Sidwell Date: Thu, 1 Nov 2018 11:18:06 +0000 (+0000) Subject: [ABI PATCH] static anonymous unions of function scope X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=41f927f4a668aeb167a673f916cc5cbfb978a560;p=gcc.git [ABI PATCH] static anonymous unions of function scope https://gcc.gnu.org/ml/gcc-patches/2018-10/msg02076.html * 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. From-SVN: r265714 --- diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 6d483455922..b567d6565eb 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,34 @@ +2018-11-01 Nathan Sidwell + + * 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. + 2018-11-01 Martin Liska Jason Merrill diff --git a/gcc/cp/class.c b/gcc/cp/class.c index 1789d1ecb70..0b50f671578 100644 --- a/gcc/cp/class.c +++ b/gcc/cp/class.c @@ -111,10 +111,6 @@ static class_stack_node_t current_class_stack; /* 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 *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; diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 42449f10a48..6d49744b830 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -1793,7 +1793,7 @@ struct GTY(()) language_function { hash_table *x_named_labels; cp_binding_level *bindings; - vec *x_local_names; + /* Tracking possibly infinite loops. This is a vec only because vec doesn't work with gtype. */ vec *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__); \ <->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__); \ - <->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. */ @@ -3334,7 +3318,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 @@ -4509,7 +4493,7 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter) 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) \ @@ -4846,7 +4830,7 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter) 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) \ @@ -5240,10 +5224,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 *local_classes; - /* in decl.c */ /* An array of static vars & fns. */ @@ -6303,6 +6283,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); diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 1cea5262b62..23fcf6b0471 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -66,7 +66,6 @@ static const char *redeclaration_error_message (tree, tree); 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, tree); 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 type) 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 *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)) { @@ -7135,7 +7148,11 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p, && 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 @@ -15620,7 +15637,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; } @@ -16125,8 +16141,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. */ diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c index a5ad0eed3ad..a163558af54 100644 --- a/gcc/cp/decl2.c +++ b/gcc/cp/decl2.c @@ -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; } diff --git a/gcc/cp/mangle.c b/gcc/cp/mangle.c index 59a3111fba2..690d0bbdbd3 100644 --- a/gcc/cp/mangle.c +++ b/gcc/cp/mangle.c @@ -233,7 +233,6 @@ static void write_discriminator (const int); 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 tree dtor) } } -/* 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 tree local_entity, from , 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)); } } diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c index f2d9d222505..ad562ba219e 100644 --- a/gcc/cp/name-lookup.c +++ b/gcc/cp/name-lookup.c @@ -6879,7 +6879,7 @@ do_pushtag (tree name, tree type, tag_scope scope) } /* Lambdas use LAMBDA_EXPR_DISCRIMINATOR instead. */ else if (!LAMBDA_TYPE_P (type)) - vec_safe_push (local_classes, type); + determine_local_discriminator (TYPE_NAME (type)); } } diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index 4c053655e00..0d9673db1c5 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -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)) { diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 9f9d35e656c..13e1a1c3519 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,7 @@ +2018-11-01 Nathan Sidwell + + * g++.dg/abi/anon5.C: New. + 2018-11-01 Jakub Jelinek PR d/87824 diff --git a/gcc/testsuite/g++.dg/abi/anon5.C b/gcc/testsuite/g++.dg/abi/anon5.C new file mode 100644 index 00000000000..4e724d40128 --- /dev/null +++ b/gcc/testsuite/g++.dg/abi/anon5.C @@ -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} } }