]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
Add C++ attribute abi_tag and -Wabi-tag option.
authorjason <jason@138bc75d-0d04-0410-961f-82ee72b054a4>
Fri, 9 Nov 2012 16:14:37 +0000 (16:14 +0000)
committerjason <jason@138bc75d-0d04-0410-961f-82ee72b054a4>
Fri, 9 Nov 2012 16:14:37 +0000 (16:14 +0000)
gcc/
* attribs.c (lookup_attribute_spec): Handle getting a TREE_LIST.
gcc/c-family/
* c.opt (Wabi-tag): New.
gcc/cp/
* tree.c (cxx_attribute_table): Add abi_tag attribute.
(check_abi_tag_redeclaration, handle_abi_tag_attribute): New.
* class.c (find_abi_tags_r, check_abi_tags): New.
(check_bases, check_field_decl): Call check_abi_tags.
* decl.c (redeclaration_error_message): Call
check_abi_tag_redeclaration.
* mangle.c (tree_string_cmp, write_abi_tags): New.
(write_unqualified_name): Call write_abi_tags.
include/
* demangle.h (enum demangle_component_type): Add
DEMANGLE_COMPONENT_TAGGED_NAME.
libiberty/
* cp-demangle.c (d_dump): Handle DEMANGLE_COMPONENT_TAGGED_NAME.
(d_make_comp, d_find_pack, d_print_comp): Likewise.
(d_abi_tags): New.
(d_name): Call it.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@193367 138bc75d-0d04-0410-961f-82ee72b054a4

18 files changed:
gcc/ChangeLog
gcc/attribs.c
gcc/c-family/ChangeLog
gcc/c-family/c.opt
gcc/cp/ChangeLog
gcc/cp/class.c
gcc/cp/cp-tree.h
gcc/cp/decl.c
gcc/cp/mangle.c
gcc/cp/tree.c
gcc/doc/extend.texi
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/abi/abi-tag1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/abi/abi-tag2.C [new file with mode: 0644]
include/ChangeLog
include/demangle.h
libiberty/ChangeLog
libiberty/cp-demangle.c

index cd980895d8063b0f70f4a72c7fab8ec4c2217c61..37049f910a0880f29bc9b67be4c9c8febff1a8e0 100644 (file)
@@ -1,3 +1,7 @@
+2012-11-09  Jason Merrill  <jason@redhat.com>
+
+       * attribs.c (lookup_attribute_spec): Handle getting a TREE_LIST.
+
 2012-11-09  Vladimir Makarov  <vmakarov@redhat.com>
 
        PR tree-optimization/55154
index d167c1f6e13fe550e8d9a932e44792bbfde19e2a..0425de9f5b127f65c2bed05f1deb3c55d983c924 100644 (file)
@@ -325,12 +325,21 @@ lookup_scoped_attribute_spec (const_tree ns, const_tree name)
                         substring_hash (attr.str, attr.length));
 }
 
-/* Return the spec for the attribute named NAME.  */
+/* Return the spec for the attribute named NAME.  If NAME is a TREE_LIST,
+   it also specifies the attribute namespace.  */
 
 const struct attribute_spec *
 lookup_attribute_spec (const_tree name)
 {
-  return lookup_scoped_attribute_spec (get_identifier ("gnu"), name);
+  tree ns;
+  if (TREE_CODE (name) == TREE_LIST)
+    {
+      ns = TREE_PURPOSE (name);
+      name = TREE_VALUE (name);
+    }
+  else
+    ns = get_identifier ("gnu");
+  return lookup_scoped_attribute_spec (ns, name);
 }
 
 \f
index 20ef315ea7443c60609dcc5114b96bb5ddf3b4df..b3234d34dd58f036d8b35911bc21e841d3e1e351 100644 (file)
@@ -1,3 +1,7 @@
+2012-11-09  Jason Merrill  <jason@redhat.com>
+
+       * c.opt (Wabi-tag): New.
+
 2012-11-09  Andi Kleen  <ak@linux.intel.com>
 
        PR 55139
index e11aef7a365eeb238ce1c0da0e5f49e139c8da73..fe1fc4d5ac09774532b0aa81a4d223a82c720526 100644 (file)
@@ -257,6 +257,10 @@ Wabi
 C ObjC C++ ObjC++ LTO Var(warn_abi) Warning
 Warn about things that will change when compiling with an ABI-compliant compiler
 
+Wabi-tag
+C++ ObjC++ Var(warn_abi_tag) Warning
+Warn if a subobject has an abi_tag attribute that the complete object type does not have
+
 Wpsabi
 C ObjC C++ ObjC++ LTO Var(warn_psabi) Init(1) Undocumented
 
index 339a8359da44ca3973239a91209407fcc39fb46c..4d6d954db1f70f4fb7bdb8155e9c3b426d71aae4 100644 (file)
@@ -1,3 +1,14 @@
+2012-11-09  Jason Merrill  <jason@redhat.com>
+
+       * tree.c (cxx_attribute_table): Add abi_tag attribute.
+       (check_abi_tag_redeclaration, handle_abi_tag_attribute): New.
+       * class.c (find_abi_tags_r, check_abi_tags): New.
+       (check_bases, check_field_decl): Call check_abi_tags.
+       * decl.c (redeclaration_error_message): Call
+       check_abi_tag_redeclaration.
+       * mangle.c (tree_string_cmp, write_abi_tags): New.
+       (write_unqualified_name): Call write_abi_tags.
+
 2012-11-07  Paolo Carlini  <paolo.carlini@oracle.com>
 
        PR c++/55226
index a91e63a6301920ffc53232527f3d9c0aed0b7046..d3d9aed7ff786145f39ddeaaec919679cc363f3f 100644 (file)
@@ -1302,6 +1302,89 @@ handle_using_decl (tree using_decl, tree t)
     alter_access (t, decl, access);
 }
 \f
+/* walk_tree callback for check_abi_tags: if the type at *TP involves any
+   types with abi tags, add the corresponding identifiers to the VEC in
+   *DATA and set IDENTIFIER_MARKED.  */
+
+struct abi_tag_data
+{
+  tree t;
+  tree subob;
+};
+
+static tree
+find_abi_tags_r (tree *tp, int */*walk_subtrees*/, void *data)
+{
+  if (!TAGGED_TYPE_P (*tp))
+    return NULL_TREE;
+
+  if (tree attributes = lookup_attribute ("abi_tag", TYPE_ATTRIBUTES (*tp)))
+    {
+      struct abi_tag_data *p = static_cast<struct abi_tag_data*>(data);
+      for (tree list = TREE_VALUE (attributes); list;
+          list = TREE_CHAIN (list))
+       {
+         tree tag = TREE_VALUE (list);
+         tree id = get_identifier (TREE_STRING_POINTER (tag));
+         if (!IDENTIFIER_MARKED (id))
+           {
+             if (TYPE_P (p->subob))
+               {
+                 warning (OPT_Wabi_tag, "%qT does not have the %E abi tag "
+                          "that base %qT has", p->t, tag, p->subob);
+                 inform (location_of (p->subob), "%qT declared here",
+                         p->subob);
+               }
+             else
+               {
+                 warning (OPT_Wabi_tag, "%qT does not have the %E abi tag "
+                          "that %qT (used in the type of %qD) has",
+                          p->t, tag, *tp, p->subob);
+                 inform (location_of (p->subob), "%qD declared here",
+                         p->subob);
+                 inform (location_of (*tp), "%qT declared here", *tp);
+               }
+           }
+       }
+    }
+  return NULL_TREE;
+}
+
+/* Check that class T has all the abi tags that subobject SUBOB has, or
+   warn if not.  */
+
+static void
+check_abi_tags (tree t, tree subob)
+{
+  tree attributes = lookup_attribute ("abi_tag", TYPE_ATTRIBUTES (t));
+  if (attributes)
+    {
+      for (tree list = TREE_VALUE (attributes); list;
+          list = TREE_CHAIN (list))
+       {
+         tree tag = TREE_VALUE (list);
+         tree id = get_identifier (TREE_STRING_POINTER (tag));
+         IDENTIFIER_MARKED (id) = true;
+       }
+    }
+
+  tree subtype = TYPE_P (subob) ? subob : TREE_TYPE (subob);
+  struct abi_tag_data data = { t, subob };
+
+  cp_walk_tree_without_duplicates (&subtype, find_abi_tags_r, &data);
+
+  if (attributes)
+    {
+      for (tree list = TREE_VALUE (attributes); list;
+          list = TREE_CHAIN (list))
+       {
+         tree tag = TREE_VALUE (list);
+         tree id = get_identifier (TREE_STRING_POINTER (tag));
+         IDENTIFIER_MARKED (id) = false;
+       }
+    }
+}
+
 /* Run through the base classes of T, updating CANT_HAVE_CONST_CTOR_P,
    and NO_CONST_ASN_REF_P.  Also set flag bits in T based on
    properties of the bases.  */
@@ -1431,6 +1514,8 @@ check_bases (tree t,
          if (tm_attr)
            seen_tm_mask |= tm_attr_to_mask (tm_attr);
        }
+
+      check_abi_tags (t, basetype);
     }
 
   /* If one of the base classes had TM attributes, and the current class
@@ -3147,6 +3232,9 @@ check_field_decl (tree field,
          && !TYPE_HAS_CONST_COPY_ASSIGN (type))
        *no_const_asn_ref = 1;
     }
+
+  check_abi_tags (t, field);
+
   if (DECL_INITIAL (field) != NULL_TREE)
     {
       /* `build_class_init_list' does not recognize
index 308ea11a5f8cc08e5f8b0f1789190b8c80e49ca7..be7bc0b0269c74e9ef1b00af5de4e4c15f633537 100644 (file)
@@ -5749,6 +5749,7 @@ extern bool type_has_nontrivial_default_init      (const_tree);
 extern bool type_has_nontrivial_copy_init      (const_tree);
 extern bool class_tmpl_impl_spec_p             (const_tree);
 extern int zero_init_p                         (const_tree);
+extern bool check_abi_tag_redeclaration                (const_tree, const_tree, const_tree);
 extern tree strip_typedefs                     (tree);
 extern tree strip_typedefs_expr                        (tree);
 extern tree copy_binfo                         (tree, tree, tree,
index f8f9d4f2391c13c34050c71c262ed7bcc0e7ed21..f85901fc6c70dfb795fcebc32ee4a4e0ab80fb2a 100644 (file)
@@ -2480,6 +2480,10 @@ redeclaration_error_message (tree newdecl, tree olddecl)
            }
        }
 
+      check_abi_tag_redeclaration
+       (olddecl, lookup_attribute ("abi_tag", DECL_ATTRIBUTES (olddecl)),
+        lookup_attribute ("abi_tag", DECL_ATTRIBUTES (newdecl)));
+
       return NULL;
     }
   else if (TREE_CODE (newdecl) == TEMPLATE_DECL)
index f448932e6ea14a9d9b69311ff6a0aacfee2a4894..54a4c9c4bca54e3ba3b1429c1b5cb68be730960b 100644 (file)
@@ -173,6 +173,7 @@ static void mangle_call_offset (const tree, const tree);
 static void write_mangled_name (const tree, bool);
 static void write_encoding (const tree);
 static void write_name (tree, const int);
+static void write_abi_tags (tree);
 static void write_unscoped_name (const tree);
 static void write_unscoped_template_name (const tree);
 static void write_nested_name (const tree);
@@ -1192,15 +1193,17 @@ write_unqualified_name (const tree decl)
       return;
     }
 
+  bool found = false;
+
   if (DECL_NAME (decl) == NULL_TREE)
     {
+      found = true;
       gcc_assert (DECL_ASSEMBLER_NAME_SET_P (decl));
       write_source_name (DECL_ASSEMBLER_NAME (decl));
-      return;
     }
   else if (DECL_DECLARES_FUNCTION_P (decl))
     {
-      bool found = true;
+      found = true;
       if (DECL_CONSTRUCTOR_P (decl))
        write_special_name_constructor (decl);
       else if (DECL_DESTRUCTOR_P (decl))
@@ -1234,14 +1237,13 @@ write_unqualified_name (const tree decl)
        write_literal_operator_name (DECL_NAME (decl));
       else
        found = false;
-
-      if (found)
-       return;
     }
 
-  if (VAR_OR_FUNCTION_DECL_P (decl) && ! TREE_PUBLIC (decl)
-      && DECL_NAMESPACE_SCOPE_P (decl)
-      && decl_linkage (decl) == lk_internal)
+  if (found)
+    /* OK */;
+  else if (VAR_OR_FUNCTION_DECL_P (decl) && ! TREE_PUBLIC (decl)
+          && DECL_NAMESPACE_SCOPE_P (decl)
+          && decl_linkage (decl) == lk_internal)
     {
       MANGLE_TRACE_TREE ("local-source-name", decl);
       write_char ('L');
@@ -1262,6 +1264,11 @@ write_unqualified_name (const tree decl)
       else
         write_source_name (DECL_NAME (decl));
     }
+
+  tree attrs = (TREE_CODE (decl) == TYPE_DECL
+               ? TYPE_ATTRIBUTES (TREE_TYPE (decl))
+               : DECL_ATTRIBUTES (decl));
+  write_abi_tags (lookup_attribute ("abi_tag", attrs));
 }
 
 /* Write the unqualified-name for a conversion operator to TYPE.  */
@@ -1291,6 +1298,51 @@ write_source_name (tree identifier)
   write_identifier (IDENTIFIER_POINTER (identifier));
 }
 
+/* Compare two TREE_STRINGs like strcmp.  */
+
+int
+tree_string_cmp (const void *p1, const void *p2)
+{
+  if (p1 == p2)
+    return 0;
+  tree s1 = *(const tree*)p1;
+  tree s2 = *(const tree*)p2;
+  return strcmp (TREE_STRING_POINTER (s1),
+                TREE_STRING_POINTER (s2));
+}
+
+/* ID is the name of a function or type with abi_tags attribute TAGS.
+   Write out the name, suitably decorated.  */
+
+static void
+write_abi_tags (tree tags)
+{
+  if (tags == NULL_TREE)
+    return;
+
+  tags = TREE_VALUE (tags);
+
+  VEC(tree,gc)* vec = make_tree_vector();
+
+  for (tree t = tags; t; t = TREE_CHAIN (t))
+    {
+      tree str = TREE_VALUE (t);
+      VEC_safe_push (tree, gc, vec, str);
+    }
+
+  VEC_qsort (tree, vec, tree_string_cmp);
+
+  unsigned i; tree str;
+  FOR_EACH_VEC_ELT (tree, vec, i, str)
+    {
+      write_string ("B");
+      write_unsigned_number (TREE_STRING_LENGTH (str) - 1);
+      write_identifier (TREE_STRING_POINTER (str));
+    }
+
+  release_tree_vector (vec);
+}
+
 /* Write a user-defined literal operator.
           ::= li <source-name>    # "" <source-name>
    IDENTIFIER is an LITERAL_IDENTIFIER_NODE.  */
index bcc2779f5447e5d8acdd2bd71ab6666beebdd5dc..5df7b6c8a760b6ad17d2b5eaf052d2d112f8845a 100644 (file)
@@ -49,6 +49,7 @@ static tree build_local_temp (tree);
 static tree handle_java_interface_attribute (tree *, tree, tree, int, bool *);
 static tree handle_com_interface_attribute (tree *, tree, tree, int, bool *);
 static tree handle_init_priority_attribute (tree *, tree, tree, int, bool *);
+static tree handle_abi_tag_attribute (tree *, tree, tree, int, bool *);
 
 /* If REF is an lvalue, returns the kind of lvalue that REF is.
    Otherwise, returns clk_none.  */
@@ -3000,6 +3001,8 @@ const struct attribute_spec cxx_attribute_table[] =
     handle_com_interface_attribute, false },
   { "init_priority",  1, 1, true,  false, false,
     handle_init_priority_attribute, false },
+  { "abi_tag", 1, -1, false, false, false,
+    handle_abi_tag_attribute, true },
   { NULL,            0, 0, false, false, false, NULL, false }
 };
 
@@ -3131,6 +3134,96 @@ handle_init_priority_attribute (tree* node,
     }
 }
 
+/* DECL is being redeclared; the old declaration had the abi tags in OLD,
+   and the new one has the tags in NEW_.  Give an error if there are tags
+   in NEW_ that weren't in OLD.  */
+
+bool
+check_abi_tag_redeclaration (const_tree decl, const_tree old, const_tree new_)
+{
+  if (old && TREE_CODE (TREE_VALUE (old)) == TREE_LIST)
+    old = TREE_VALUE (old);
+  if (new_ && TREE_CODE (TREE_VALUE (new_)) == TREE_LIST)
+    new_ = TREE_VALUE (new_);
+  bool err = false;
+  for (const_tree t = new_; t; t = TREE_CHAIN (t))
+    {
+      tree str = TREE_VALUE (t);
+      for (const_tree in = old; in; in = TREE_CHAIN (in))
+       {
+         tree ostr = TREE_VALUE (in);
+         if (cp_tree_equal (str, ostr))
+           goto found;
+       }
+      error ("redeclaration of %qD adds abi tag %E", decl, str);
+      err = true;
+    found:;
+    }
+  if (err)
+    {
+      inform (DECL_SOURCE_LOCATION (decl), "previous declaration here");
+      return false;
+    }
+  return true;
+}
+
+/* Handle an "abi_tag" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_abi_tag_attribute (tree* node, tree name, tree args,
+                         int flags, bool* no_add_attrs)
+{
+  if (TYPE_P (*node))
+    {
+      if (!TAGGED_TYPE_P (*node))
+       {
+         error ("%qE attribute applied to non-class, non-enum type %qT",
+                name, *node);
+         goto fail;
+       }
+      else if (!(flags & (int)ATTR_FLAG_TYPE_IN_PLACE))
+       {
+         error ("%qE attribute applied to %qT after its definition",
+                name, *node);
+         goto fail;
+       }
+
+      tree attributes = TYPE_ATTRIBUTES (*node);
+      tree decl = TYPE_NAME (*node);
+
+      /* Make sure all declarations have the same abi tags.  */
+      if (DECL_SOURCE_LOCATION (decl) != input_location)
+       {
+         if (!check_abi_tag_redeclaration (decl,
+                                           lookup_attribute ("abi_tag",
+                                                             attributes),
+                                           args))
+           goto fail;
+       }
+    }
+  else
+    {
+      if (TREE_CODE (*node) != FUNCTION_DECL)
+       {
+         error ("%qE attribute applied to non-function %qD", name, *node);
+         goto fail;
+       }
+      else if (DECL_LANGUAGE (*node) == lang_c)
+       {
+         error ("%qE attribute applied to extern \"C\" function %qD",
+                name, *node);
+         goto fail;
+       }
+    }
+
+  return NULL_TREE;
+
+ fail:
+  *no_add_attrs = true;
+  return NULL_TREE;
+}
+
 /* Return a new PTRMEM_CST of the indicated TYPE.  The MEMBER is the
    thing pointed to by the constant.  */
 
index 54fd54826e7691722e77c09e0372fc34c58e4c87..fe09b85ef0b80eadc67bf999361c01e484c0dd43 100644 (file)
@@ -15598,6 +15598,27 @@ You must specify @option{-Wno-pmf-conversions} to use this extension.
 Some attributes only make sense for C++ programs.
 
 @table @code
+@item abi_tag ("@var{tag}", ...)
+@cindex @code{abi_tag} attribute
+The @code{abi_tag} attribute can be applied to a function or class
+declaration.  It modifies the mangled name of the function or class to
+incorporate the tag name, in order to distinguish the function or
+class from an earlier version with a different ABI; perhaps the class
+has changed size, or the function has a different return type that is
+not encoded in the mangled name.
+
+The argument can be a list of strings of arbitrary length.  The
+strings are sorted on output, so the order of the list is
+unimportant.
+
+A redeclaration of a function or class must not add new ABI tags,
+since doing so would change the mangled name.
+
+The @option{-Wabi-tag} flag enables a warning about a class which does
+not have all the ABI tags used by its subobjects; for users with code
+that needs to coexist with an earlier ABI, using this option can help
+to find all affected types that need to be tagged.
+
 @item init_priority (@var{priority})
 @cindex @code{init_priority} attribute
 
index 73ad2528523c28f4201f35a541a5d224be064aeb..608e017943c6c6ca875caf8e2650def39a0709f5 100644 (file)
@@ -1,3 +1,8 @@
+2012-11-09  Jason Merrill  <jason@redhat.com>
+
+       * g++.dg/abi/abi-tag1.C: New.
+       * g++.dg/abi/abi-tag2.C: New.
+
 2012-11-09  Vladimir Makarov  <vmakarov@redhat.com>
 
        PR rtl-optimization/55154
diff --git a/gcc/testsuite/g++.dg/abi/abi-tag1.C b/gcc/testsuite/g++.dg/abi/abi-tag1.C
new file mode 100644 (file)
index 0000000..942929c
--- /dev/null
@@ -0,0 +1,19 @@
+// { dg-options "-Wabi-tag" }
+
+// { dg-final { scan-assembler "_Z1fB3barB3fooi" } }
+void f(int) __attribute ((abi_tag ("foo","bar")));
+
+struct __attribute ((abi_tag ("bar"))) A { };
+
+struct B: A { };               // { dg-warning "bar. abi tag" }
+struct D { A* ap; };           // { dg-warning "bar. abi tag" }
+
+// { dg-final { scan-assembler "_Z1gB3baz1AB3bar" } }
+void g(A) __attribute ((abi_tag ("baz")));
+void g(A) __attribute ((abi_tag ("baz")));
+
+int main()
+{
+  f(42);
+  g(A());
+}
diff --git a/gcc/testsuite/g++.dg/abi/abi-tag2.C b/gcc/testsuite/g++.dg/abi/abi-tag2.C
new file mode 100644 (file)
index 0000000..0e92dcc
--- /dev/null
@@ -0,0 +1,5 @@
+void f(int);
+void f(int) __attribute ((abi_tag ("foo"))); // { dg-error "adds abi tag" }
+
+struct C;
+struct __attribute ((abi_tag ("foo"))) C; // { dg-error "adds abi tag" }
index afac33a7aef7c32adcebcdf34f28524426b5aa70..ac2b28ed8ff3f0549892d825eba93607e1f16c4d 100644 (file)
@@ -1,3 +1,8 @@
+2012-11-09  Jason Merrill  <jason@redhat.com>
+
+       * demangle.h (enum demangle_component_type): Add
+       DEMANGLE_COMPONENT_TAGGED_NAME.
+
 2012-10-29  Sterling Augustine <saugustine@google.com>
             Cary Coutant <ccoutant@google.com>
 
index 5da79d85221d303b1cec75904859193b94014c9c..ed019500393859b525e7de0cdcd3246a3f2dd733 100644 (file)
@@ -420,6 +420,8 @@ enum demangle_component_type
   DEMANGLE_COMPONENT_NONTRANSACTION_CLONE,
   /* A pack expansion.  */
   DEMANGLE_COMPONENT_PACK_EXPANSION,
+  /* A name with an ABI tag.  */
+  DEMANGLE_COMPONENT_TAGGED_NAME,
   /* A cloned function.  */
   DEMANGLE_COMPONENT_CLONE
 };
index 303dda23cae01088f4eba47d34e7c5c8ab0fa49b..af12cd8647012c52a03d36e72ac29ffa22016e29 100644 (file)
@@ -1,3 +1,10 @@
+2012-11-09  Jason Merrill  <jason@redhat.com>
+
+       * cp-demangle.c (d_dump): Handle DEMANGLE_COMPONENT_TAGGED_NAME.
+       (d_make_comp, d_find_pack, d_print_comp): Likewise.
+       (d_abi_tags): New.
+       (d_name): Call it.
+
 2012-10-08  Jason Merrill  <jason@redhat.com>
 
        * cp-demangle.c (d_special_name, d_dump): Handle TH and TW.
index 32df38c6024b1fa499d06c305b9dd5954772231b..86c7747120009e9c55bc3865a4f3f6fd9ca05f95 100644 (file)
@@ -508,6 +508,11 @@ d_dump (struct demangle_component *dc, int indent)
     case DEMANGLE_COMPONENT_NAME:
       printf ("name '%.*s'\n", dc->u.s_name.len, dc->u.s_name.s);
       return;
+    case DEMANGLE_COMPONENT_TAGGED_NAME:
+      printf ("tagged name\n");
+      d_dump (dc->u.s_binary.left, indent + 2);
+      d_dump (dc->u.s_binary.right, indent + 2);
+      return;
     case DEMANGLE_COMPONENT_TEMPLATE_PARAM:
       printf ("template parameter %ld\n", dc->u.s_number.number);
       return;
@@ -809,6 +814,7 @@ d_make_comp (struct d_info *di, enum demangle_component_type type,
     case DEMANGLE_COMPONENT_QUAL_NAME:
     case DEMANGLE_COMPONENT_LOCAL_NAME:
     case DEMANGLE_COMPONENT_TYPED_NAME:
+    case DEMANGLE_COMPONENT_TAGGED_NAME:
     case DEMANGLE_COMPONENT_TEMPLATE:
     case DEMANGLE_COMPONENT_CONSTRUCTION_VTABLE:
     case DEMANGLE_COMPONENT_VENDOR_TYPE_QUAL:
@@ -1202,6 +1208,23 @@ d_encoding (struct d_info *di, int top_level)
     }
 }
 
+/* <tagged-name> ::= <name> B <source-name> */
+
+static struct demangle_component *
+d_abi_tags (struct d_info *di, struct demangle_component *dc)
+{
+  char peek;
+  while (peek = d_peek_char (di),
+        peek == 'B')
+    {
+      struct demangle_component *tag;
+      d_advance (di, 1);
+      tag = d_source_name (di);
+      dc = d_make_comp (di, DEMANGLE_COMPONENT_TAGGED_NAME, dc, tag);
+    }
+  return dc;
+}
+
 /* <name> ::= <nested-name>
           ::= <unscoped-name>
           ::= <unscoped-template-name> <template-args>
@@ -1223,14 +1246,17 @@ d_name (struct d_info *di)
   switch (peek)
     {
     case 'N':
-      return d_nested_name (di);
+      dc = d_nested_name (di);
+      break;
 
     case 'Z':
-      return d_local_name (di);
+      dc = d_local_name (di);
+      break;
 
     case 'L':
     case 'U':
-      return d_unqualified_name (di);
+      dc = d_unqualified_name (di);
+      break;
 
     case 'S':
       {
@@ -1272,7 +1298,7 @@ d_name (struct d_info *di)
                              d_template_args (di));
          }
 
-       return dc;
+       break;
       }
 
     default:
@@ -1287,8 +1313,12 @@ d_name (struct d_info *di)
          dc = d_make_comp (di, DEMANGLE_COMPONENT_TEMPLATE, dc,
                            d_template_args (di));
        }
-      return dc;
+      break;
     }
+
+  if (d_peek_char (di) == 'B')
+    dc = d_abi_tags (di, dc);
+  return dc;
 }
 
 /* <nested-name> ::= N [<CV-qualifiers>] <prefix> <unqualified-name> E
@@ -3745,6 +3775,7 @@ d_find_pack (struct d_print_info *dpi,
       
     case DEMANGLE_COMPONENT_LAMBDA:
     case DEMANGLE_COMPONENT_NAME:
+    case DEMANGLE_COMPONENT_TAGGED_NAME:
     case DEMANGLE_COMPONENT_OPERATOR:
     case DEMANGLE_COMPONENT_BUILTIN_TYPE:
     case DEMANGLE_COMPONENT_SUB_STD:
@@ -3830,6 +3861,13 @@ d_print_comp (struct d_print_info *dpi, int options,
        d_print_java_identifier (dpi, dc->u.s_name.s, dc->u.s_name.len);
       return;
 
+    case DEMANGLE_COMPONENT_TAGGED_NAME:
+      d_print_comp (dpi, options, d_left (dc));
+      d_append_string (dpi, "[abi:");
+      d_print_comp (dpi, options, d_right (dc));
+      d_append_char (dpi, ']');
+      return;
+
     case DEMANGLE_COMPONENT_QUAL_NAME:
     case DEMANGLE_COMPONENT_LOCAL_NAME:
       d_print_comp (dpi, options, d_left (dc));