[RFC,WIP] introduce attribute exalias

Message ID or365aghwf.fsf@livre.home
State New
Headers show
Series
  • [RFC,WIP] introduce attribute exalias
Related show

Commit Message

Alexandre Oliva July 29, 2020, 8:56 p.m.
This patch introduces an attribute to add extra aliases to a symbol
when its definition is output.  The main goal is to ease interfacing
C++ with Ada, as C++ mangled names have to be named, and in some cases
(e.g. when using stdint.h typedefs in function arguments) the symbol
names may vary across platforms.

The attribute is usable in C and C++, presumably in all C-family
languages.  It can be attached to global variables and functions.  In
C++, it can also be attached to namespace-scoped variables and
functions, static data members, and member functions including
constructors and destructors, including explicit specializations of
template functions.  When applied to cdtors, additional exaliases with
_Base and _Del suffixes are defined when appropriate.

Applying the attribute to types is only valid in C++, and the effect
is to attach the alias to the RTTI object associated with the class
type, including in an explicit template instantiation.

This is still WIP, not fully tested, posted for feedback on the
feature and on the implementation.  I'm particularly unhappy with:

- the complexity of the interface/linkage updates in C++; I wonder why
  the back-propagation through aliases isn't being picked up, and
  whether it would be enough.

- C++ didn't merge attributes of extern local declarations with those
  of the namespace-scoped declaration.  I've added code to merge the
  attributes if there is a namespace-scoped declaration, but if there
  isn't one, there won't be any merging, and the effects are
  noticeable, as in the added attr-weak-1.C.  I'm also slightly
  concerned that an earlier local decl would go out of sync if a
  subsequent local decl, say within the same or even in another
  function, introduces additional attributes in the global decl.

- I'd rather rule out the attribute in template classes and functions,
  and enable it in explicit instantiations or specializations only,
  but there doesn't seem to be any way to test for generics proper (as
  opposed to specializations of generics) within c-attribs.c.

- I noticed an inconsistency between the handling of attribute exalias
  in template classes vs template functions.  Those attached to a
  template class end up affecting all instantiations, whereas those
  attached to template functions seem to be just dropped on the
  floor.  I haven't investigated this difference yet.

- Adding exalias to explicit instantiations of members of a template
  class, and also to the template class proper, pose some
  difficulties, as implicit instantiations sometimes conflict with the
  intent of explicit instantiations.  See attr-exalias-3.C for
  details.  I'm considering enabling attribute exalias in typedefs and
  using declarations, even after template instantiation, to try to
  alleviate this.

- Though exalias associated with a type, particularly a class type,
  makes sense for interfacing with Ada, as the RTTI object needs to be
  named to import a C++ exception type.  I've also considered an
  alternate attribute name, applied to a class and/or to its dtor.  I
  even implemented attribute exrtti for C++ class types, as an
  incremental patch, if that's preferred.

Early feedback is welcome.  Thanks in advance,

---
 .../doc/gnat_rm/interfacing_to_other_languages.rst |    6 ++
 .../doc/gnat_ugn/the_gnat_compilation_model.rst    |   20 ++++--
 gcc/attribs.c                                      |   56 +++++++++++++++++
 gcc/attribs.h                                      |    7 ++
 gcc/c-family/c-ada-spec.c                          |    7 ++
 gcc/c-family/c-attribs.c                           |   42 ++++++++++++-
 gcc/c/c-decl.c                                     |    2 +
 gcc/cgraph.h                                       |    4 +
 gcc/cgraphunit.c                                   |    2 -
 gcc/cp/class.c                                     |   54 ++++++++++++++++
 gcc/cp/cp-tree.h                                   |    1 
 gcc/cp/decl.c                                      |    4 +
 gcc/cp/decl2.c                                     |   43 +++++++++++++
 gcc/cp/name-lookup.c                               |    8 ++
 gcc/cp/optimize.c                                  |    6 ++
 gcc/cp/rtti.c                                      |   13 ++++
 gcc/doc/extend.texi                                |   52 ++++++++++++++++
 gcc/symtab.c                                       |   26 ++++++++
 gcc/testsuite/c-c++-common/attr-weak-1.c           |   19 ++++++
 .../c-c++-common/torture/attr-exalias-1.c          |   39 ++++++++++++
 .../c-c++-common/torture/attr-exalias-2.c          |   13 ++++
 .../c-c++-common/torture/attr-exalias-3.c          |   41 ++++++++++++
 .../c-c++-common/torture/attr-exalias-4.c          |   28 ++++++++
 gcc/testsuite/g++.dg/torture/attr-exalias-1.C      |   66 ++++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-exalias-2.C      |   26 ++++++++
 gcc/testsuite/g++.dg/torture/attr-exalias-3.C      |   61 ++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-exalias-4.C      |   28 ++++++++
 gcc/varpool.c                                      |    2 -
 29 files changed, 662 insertions(+), 15 deletions(-)
 create mode 100644 gcc/testsuite/c-c++-common/attr-weak-1.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-exalias-1.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-exalias-2.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-exalias-3.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-exalias-4.c
 create mode 100644 gcc/testsuite/g++.dg/torture/attr-exalias-1.C
 create mode 100644 gcc/testsuite/g++.dg/torture/attr-exalias-2.C
 create mode 100644 gcc/testsuite/g++.dg/torture/attr-exalias-3.C
 create mode 100644 gcc/testsuite/g++.dg/torture/attr-exalias-4.C



-- 
Alexandre Oliva, freedom fighter    he/him    https://FSFLA.org/blogs/lxo/
Free Software Evangelist              Stallman was right, but he's left :(
GNU Toolchain Engineer           Live long and free, and prosper ethically

Patch

diff --git a/gcc/ada/doc/gnat_rm/interfacing_to_other_languages.rst b/gcc/ada/doc/gnat_rm/interfacing_to_other_languages.rst
index ad0be51..ce6a4da 100644
--- a/gcc/ada/doc/gnat_rm/interfacing_to_other_languages.rst
+++ b/gcc/ada/doc/gnat_rm/interfacing_to_other_languages.rst
@@ -123,6 +123,12 @@  It is also possible to import a C++ exception using the following syntax:
 The ``External_Name`` is the name of the C++ RTTI symbol. You can then
 cover a specific C++ exception in an exception handler.
 
+RTTI symbols undergo C++ name mangling, which can make for identifiers
+that are inconvenient to use.  An alias with a mnemonic name can be
+introduced by adding attribute ``exalias`` to the class that the RTTI
+symbol refers to.
+
+
 .. _Interfacing_to_COBOL:
 
 Interfacing to COBOL
diff --git a/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst b/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
index b8729d0..2a13a24 100644
--- a/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
+++ b/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
@@ -4279,6 +4279,7 @@  and two public primitives to set and get the value of this attribute.
       public:
         virtual void Set_Age (int New_Age);
         virtual int Age ();
+        __attribute__ ((__exalias__ ("Ctor_For_Animal"))) // extra alias
         Animal() {Age_Count = 0;};
       private:
         int Age_Count;
@@ -4311,6 +4312,7 @@  both Carnivore and Domestic, that is:
         virtual int  Number_Of_Teeth ();
         virtual void Set_Owner (char* Name);
 
+        __attribute__ ((__exalias__ ("Ctor_For_Dog"))) // mnemonic alias
         Dog(); // Constructor
       private:
         int  Tooth_Count;
@@ -4349,7 +4351,8 @@  how to import these C++ declarations from the Ada side:
 
        function New_Animal return Animal;
        pragma CPP_Constructor (New_Animal);
-       pragma Import (CPP, New_Animal, "_ZN6AnimalC1Ev");
+       pragma Import (CPP, New_Animal,
+                      "_ZN6AnimalC1Ev"); -- or "Ctor_For_Animal"
 
        type Dog is new Animal and Carnivore and Domestic with record
          Tooth_Count : Natural;
@@ -4365,7 +4368,7 @@  how to import these C++ declarations from the Ada side:
 
        function New_Dog return Dog;
        pragma CPP_Constructor (New_Dog);
-       pragma Import (CPP, New_Dog, "_ZN3DogC2Ev");
+       pragma Import (CPP, New_Dog, "Ctor_For_Dog"); -- or "_ZN3DogC1Ev"
      end Animals;
 
 Thanks to the compatibility between GNAT run-time structures and the C++ ABI,
@@ -4382,12 +4385,13 @@  because the dispatch table associated with these tagged types will be built
 in the C++ side and therefore will not contain the predefined Ada primitives
 which Ada would otherwise expect.
 
-As the reader can see there is no need to indicate the C++ mangled names
-associated with each subprogram because it is assumed that all the calls to
-these primitives will be dispatching calls. The only exception is the
-constructor, which must be registered with the compiler by means of
-``pragma CPP_Constructor`` and needs to provide its associated C++
-mangled name because the Ada compiler generates direct calls to it.
+As the reader can see there is no need to indicate the C++ mangled
+names (or extra aliases) associated with each subprogram because it is
+assumed that all the calls to these primitives will be dispatching
+calls. The only exception is the constructor, which must be registered
+with the compiler by means of ``pragma CPP_Constructor`` and needs to
+provide its associated C++ mangled name because the Ada compiler
+generates direct calls to it.
 
 With the above packages we can now declare objects of type Dog on the Ada side
 and dispatch calls to the corresponding subprograms on the C++ side. We can
diff --git a/gcc/attribs.c b/gcc/attribs.c
index 71dae12..ad357dd 100644
--- a/gcc/attribs.c
+++ b/gcc/attribs.c
@@ -25,6 +25,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "stringpool.h"
 #include "diagnostic-core.h"
 #include "attribs.h"
+#include "cgraph.h"
 #include "stor-layout.h"
 #include "langhooks.h"
 #include "plugin.h"
@@ -2076,6 +2077,61 @@  init_attr_rdwr_indices (rdwr_map *rwm, tree fntype)
     }
 }
 
+/* Create an exalias for DECL with linkage name ID.  */
+
+tree
+create_exalias_decl (tree decl, tree id)
+{
+  tree name = get_identifier ("exalias");
+
+  if (symtab_node *sym_node = symtab_node::get_for_asmname (id))
+    {
+      if ((sym_node->analyzed
+	   ? sym_node->get_alias_target ()->decl
+	   : sym_node->alias_target) == decl)
+	return sym_node->decl;
+
+      warning (0, "duplicate symbol name %qE in %qE attribute of %qD",
+	       id, name, decl);
+    }
+
+  tree clone = copy_node (decl);
+  DECL_ATTRIBUTES (clone) = remove_attribute ("exalias",
+					      DECL_ATTRIBUTES (decl));
+  SET_DECL_ASSEMBLER_NAME (clone, id);
+  TREE_USED (id) = 1;
+  TREE_USED (clone) = 1;
+  DECL_PRESERVE_P (clone) = 1;
+  DECL_EXTERNAL (clone) = 0;
+  TREE_STATIC (clone) = 1;
+
+  if (VAR_P (clone))
+    {
+      DECL_READ_P (clone) = 1;
+      varpool_node::create_alias (clone, decl);
+    }
+  else
+    {
+      cgraph_node::create_alias (clone, decl);
+    }
+
+  return clone;
+}
+
+/* Create all exaliases requested in DECL's attributes.  */
+
+void
+create_exalias_decls (tree decl)
+{
+  FOR_EACH_EXALIAS (exalias, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (exalias));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      create_exalias_decl (decl, id);
+    }
+}
+
 
 #if CHECKING_P
 
diff --git a/gcc/attribs.h b/gcc/attribs.h
index dea0b6c..1ba56ca 100644
--- a/gcc/attribs.h
+++ b/gcc/attribs.h
@@ -248,4 +248,11 @@  typedef hash_map<rdwr_access_hash, attr_access> rdwr_map;
 
 extern void init_attr_rdwr_indices (rdwr_map *, tree);
 
+extern tree create_exalias_decl (tree, tree);
+extern void create_exalias_decls (tree);
+
+#define FOR_EACH_EXALIAS(exalias, attrs)				\
+  for (tree exalias = lookup_attribute ("exalias", (attrs)); exalias;	\
+       exalias = lookup_attribute ("exalias", TREE_CHAIN (exalias)))
+
 #endif // GCC_ATTRIBS_H
diff --git a/gcc/c-family/c-ada-spec.c b/gcc/c-family/c-ada-spec.c
index c75b173..22a5c38 100644
--- a/gcc/c-family/c-ada-spec.c
+++ b/gcc/c-family/c-ada-spec.c
@@ -1434,6 +1434,13 @@  pp_ada_tree_identifier (pretty_printer *buffer, tree node, tree type,
 static void
 pp_asm_name (pretty_printer *buffer, tree t)
 {
+  FOR_EACH_EXALIAS (exalias, DECL_ATTRIBUTES (t))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (exalias));
+      pp_string (buffer, TREE_STRING_POINTER (id));
+      return;
+    }
+  
   tree name = DECL_ASSEMBLER_NAME (t);
   char *ada_name = XALLOCAVEC (char, IDENTIFIER_LENGTH (name) + 1), *s;
   const char *ident = IDENTIFIER_POINTER (name);
diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
index 3721483..b726f44 100644
--- a/gcc/c-family/c-attribs.c
+++ b/gcc/c-family/c-attribs.c
@@ -99,7 +99,8 @@  static tree handle_noplt_attribute (tree *, tree, tree, int, bool *) ;
 static tree handle_alias_ifunc_attribute (bool, tree *, tree, tree, bool *);
 static tree handle_ifunc_attribute (tree *, tree, tree, int, bool *);
 static tree handle_alias_attribute (tree *, tree, tree, int, bool *);
-static tree handle_weakref_attribute (tree *, tree, tree, int, bool *) ;
+static tree handle_weakref_attribute (tree *, tree, tree, int, bool *);
+static tree handle_exalias_attribute (tree *, tree, tree, int, bool *);
 static tree handle_visibility_attribute (tree *, tree, tree, int,
 					 bool *);
 static tree handle_tls_model_attribute (tree *, tree, tree, int,
@@ -333,6 +334,8 @@  const struct attribute_spec c_common_attribute_table[] =
 			      handle_alias_attribute, NULL },
   { "weakref",                0, 1, true,  false, false, false,
 			      handle_weakref_attribute, NULL },
+  { "exalias",                1, 1, false,  false, false, false,
+			      handle_exalias_attribute, NULL },
   { "no_instrument_function", 0, 0, true,  false, false, false,
 			      handle_no_instrument_function_attribute,
 			      NULL },
@@ -2424,7 +2427,7 @@  handle_alias_ifunc_attribute (bool is_alias, tree *node, tree name, tree args,
   return NULL_TREE;
 }
 
-/* Handle an "alias" or "ifunc" attribute; arguments as in
+/* Handle an "ifunc" attribute; arguments as in
    struct attribute_spec.handler.  */
 
 static tree
@@ -2434,7 +2437,7 @@  handle_ifunc_attribute (tree *node, tree name, tree args,
   return handle_alias_ifunc_attribute (false, node, name, args, no_add_attrs);
 }
 
-/* Handle an "alias" or "ifunc" attribute; arguments as in
+/* Handle an "alias" attribute; arguments as in
    struct attribute_spec.handler.  */
 
 static tree
@@ -2444,6 +2447,38 @@  handle_alias_attribute (tree *node, tree name, tree args,
   return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs);
 }
 
+/* Handle an "exalias" attribute; arguments as in struct
+   attribute_spec.handler.  */
+
+static tree
+handle_exalias_attribute (tree *pnode, tree name, tree args,
+			  int ARG_UNUSED (flags), bool *no_add_attrs)
+{
+  tree node = *pnode;
+
+  *no_add_attrs = true;
+
+  if (TREE_CODE (TREE_VALUE (args)) != STRING_CST)
+    error ("%qE attribute argument not a string", name);
+  else if (VAR_OR_FUNCTION_DECL_P (node)
+	   /* Variables must not be automatic.  */
+	   && !(VAR_P (node) && !TREE_STATIC (node) && !DECL_EXTERNAL (node)))
+    {
+      tree id = TREE_VALUE (args);
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      create_exalias_decl (node, id);
+
+      *no_add_attrs = false;
+    }
+  else if (TYPE_P (node) && c_dialect_cxx ())
+    *no_add_attrs = false;
+  else
+    warning (OPT_Wattributes, "%qE attribute ignored", name);
+
+  return NULL_TREE;
+}
+
 /* Handle the "copy" attribute NAME by copying the set of attributes
    from the symbol referenced by ARGS to the declaration of *NODE.  */
 
@@ -2571,6 +2606,7 @@  handle_copy_attribute (tree *node, tree name, tree args,
 	  tree atname = get_attribute_name (at);
 	  if (is_attribute_p ("alias", atname)
 	      || is_attribute_p ("always_inline", atname)
+	      || is_attribute_p ("exalias", atname)
 	      || is_attribute_p ("gnu_inline", atname)
 	      || is_attribute_p ("ifunc", atname)
 	      || is_attribute_p ("noinline", atname)
diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c
index 5d6b504..79a3de1 100644
--- a/gcc/c/c-decl.c
+++ b/gcc/c/c-decl.c
@@ -2942,6 +2942,8 @@  duplicate_decls (tree newdecl, tree olddecl)
 
   merge_decls (newdecl, olddecl, newtype, oldtype);
 
+  symtab_node::remap_exalias_target (newdecl, olddecl);
+
   /* The NEWDECL will no longer be needed.
 
      Before releasing the node, be sure to remove function from symbol
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index cfae6e9..bf36f5b 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -319,6 +319,10 @@  public:
   /* Return node that alias is aliasing.  */
   inline symtab_node *get_alias_target (void);
 
+  /* Remap exalias nodes recorded as aliasing REPLACED to alias
+     REPLACEMENT instead.  */
+  static void remap_exalias_target (tree replaced, tree replacement);
+
   /* Set section for symbol and its aliases.  */
   void set_section (const char *section);
 
diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c
index ea9a34b..c569f52 100644
--- a/gcc/cgraphunit.c
+++ b/gcc/cgraphunit.c
@@ -641,7 +641,7 @@  cgraph_node::analyze (void)
 	return;
     }
   if (alias)
-    resolve_alias (cgraph_node::get (alias_target), transparent_alias);
+    resolve_alias (cgraph_node::get_create (alias_target), transparent_alias);
   else if (dispatcher_function)
     {
       /* Generate the dispatcher body of multi-versioned functions.  */
diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index a3913f4..ed911ef 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -4822,6 +4822,57 @@  copy_fndecl_with_name (tree fn, tree name, tree_code code,
 
   /* Create the RTL for this function.  */
   SET_DECL_RTL (clone, NULL);
+
+  if (code == ERROR_MARK)
+    {
+      bool found = false;
+      FOR_EACH_EXALIAS (exalias, DECL_ATTRIBUTES (clone))
+	{
+	  found = true;
+	  break;
+	}
+
+      if (found
+	  && (name == complete_ctor_identifier
+	      || name == complete_dtor_identifier))
+	{
+	  /* Reuse the exalias decls created for the primary cdtor decl.  */
+	  symtab_node::remap_exalias_target (fn, clone);
+	}
+      else if (found)
+	{
+	  const char *suf;
+
+	  if (name == base_ctor_identifier
+	      || name == base_dtor_identifier)
+	    suf = "_Base";
+	  else if (name == deleting_dtor_identifier)
+	    suf = "_Del";
+	  else
+	    gcc_unreachable ();
+
+	  size_t xlen = strlen (suf);
+
+	  DECL_ATTRIBUTES (clone) = copy_list (DECL_ATTRIBUTES (clone));
+
+	  FOR_EACH_EXALIAS (exalias, DECL_ATTRIBUTES (clone))
+	    {
+	      TREE_VALUE (exalias) = copy_list (TREE_VALUE (exalias));
+	      /* Append suf to the exalias name.  */
+	      tree str = TREE_VALUE (TREE_VALUE (exalias));
+	      char *symname = concat (TREE_STRING_POINTER (str), suf, NULL);
+	      str = build_string (TREE_STRING_LENGTH (str) + xlen, symname);
+	      TREE_VALUE (TREE_VALUE (exalias)) = str;
+	      free (symname);
+	    }
+
+	  create_exalias_decls (clone);
+	}
+    }
+  else
+    DECL_ATTRIBUTES (clone)
+      = remove_attribute ("exalias", DECL_ATTRIBUTES (clone));
+
   rest_of_decl_compilation (clone, namespace_bindings_p (), at_eof);
 
   return clone;
@@ -4919,6 +4970,9 @@  build_cdtor_clones (tree fn, bool needs_vtt_parm_p, bool omit_inherited_parms_p)
       count += 2;
     }
 
+  DECL_ATTRIBUTES (fn)
+    = remove_attribute ("exalias", DECL_ATTRIBUTES (fn));
+
   return count;
 }
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index d43c53a..07e2c1c 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6591,6 +6591,7 @@  extern tree build_explicit_specifier		(tree, tsubst_flags_t);
 extern void do_push_parm_decls			(tree, tree, tree *);
 
 /* in decl2.c */
+extern void update_exalias_interface		(tree);
 extern void record_mangling			(tree, bool);
 extern void overwrite_mangling			(tree, tree);
 extern void note_mangling_alias			(tree, tree);
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index de53a7b..c489535 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -2899,6 +2899,8 @@  duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend)
 	      && TREE_STATIC (olddecl))))
     make_decl_rtl (olddecl);
 
+  symtab_node::remap_exalias_target (newdecl, olddecl);
+
   /* The NEWDECL will no longer be needed.  Because every out-of-class
      declaration of a member results in a call to duplicate_decls,
      freeing these nodes represents in a significant savings.
@@ -16508,6 +16510,8 @@  start_preparsed_function (tree decl1, tree attrs, int flags)
   if (!processing_template_decl)
     maybe_instantiate_noexcept (decl1);
 
+  update_exalias_interface (decl1);
+
   begin_scope (sk_function_parms, decl1);
 
   ++function_depth;
diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index 33c8377..518bb3a 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -1951,6 +1951,43 @@  adjust_var_decl_tls_model (tree decl)
     set_decl_tls_model (decl, decl_default_tls_model (decl));
 }
 
+/* Copy externalness and linkage from DECL to DEST.  */
+
+static void
+copy_interface (tree dest, tree decl)
+{
+  TREE_PUBLIC (dest) = TREE_PUBLIC (decl);
+  TREE_STATIC (dest) = TREE_STATIC (decl);
+  DECL_COMMON (dest) = DECL_COMMON (decl);
+  DECL_COMDAT (dest) = DECL_COMDAT (decl);
+  DECL_WEAK (dest) = DECL_WEAK (decl);
+  DECL_EXTERNAL (dest) = DECL_EXTERNAL (decl);
+  if (DECL_LANG_SPECIFIC (dest) && DECL_LANG_SPECIFIC (decl))
+    DECL_NOT_REALLY_EXTERN (dest) = DECL_NOT_REALLY_EXTERN (decl);
+  DECL_INTERFACE_KNOWN (dest) = DECL_INTERFACE_KNOWN (decl);
+  DECL_VISIBILITY (dest) = DECL_VISIBILITY (decl);
+  DECL_VISIBILITY_SPECIFIED (dest) = DECL_VISIBILITY_SPECIFIED (decl);
+}
+
+/* Propagate linkage changes to exaliases.  */
+
+void
+update_exalias_interface (tree decl)
+{
+  FOR_EACH_EXALIAS (exalias, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (exalias));
+      id = get_identifier (TREE_STRING_POINTER (id));
+      symtab_node *sym_node = symtab_node::get_for_asmname (id);
+
+      if (sym_node
+	  && (sym_node->analyzed
+	      ? sym_node->get_alias_target ()->decl
+	      : sym_node->alias_target) == decl)
+	copy_interface (sym_node->decl, decl);
+    }
+}
+
 /* Set DECL up to have the closest approximation of "initialized common"
    linkage available.  */
 
@@ -2485,6 +2522,8 @@  constrain_visibility (tree decl, int visibility, bool tmpl)
       /* This visibility was not specified.  */
       DECL_VISIBILITY_SPECIFIED (decl) = false;
     }
+
+  update_exalias_interface (decl);
 }
 
 /* Constrain the visibility of DECL based on the visibility of its template
@@ -3013,6 +3052,8 @@  tentative_decl_linkage (tree decl)
       else if (VAR_P (decl))
 	maybe_commonize_var (decl);
     }
+
+  update_exalias_interface (decl);
 }
 
 /* DECL is a FUNCTION_DECL or VAR_DECL.  If the object file linkage
@@ -3247,6 +3288,8 @@  import_export_decl (tree decl)
     }
 
   DECL_INTERFACE_KNOWN (decl) = 1;
+
+  update_exalias_interface (decl);
 }
 
 /* Return an expression that performs the destruction of DECL, which
diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c
index 9f30d90..26bdc25 100644
--- a/gcc/cp/name-lookup.c
+++ b/gcc/cp/name-lookup.c
@@ -22,9 +22,11 @@  along with GCC; see the file COPYING3.  If not see
 #define INCLUDE_UNIQUE_PTR
 #include "system.h"
 #include "coretypes.h"
+#include "target.h"
 #include "cp-tree.h"
 #include "timevar.h"
 #include "stringpool.h"
+#include "cgraph.h"
 #include "print-tree.h"
 #include "attribs.h"
 #include "debug.h"
@@ -2924,6 +2926,12 @@  set_local_extern_decl_linkage (tree decl, bool shadowed)
 	       different decl.  */
 	    TREE_PUBLIC (decl) = TREE_PUBLIC (*iter);
 
+	    DECL_ATTRIBUTES (*iter)
+	      = targetm.merge_decl_attributes (*iter, decl);
+	    symtab_node::remap_exalias_target (decl, *iter);
+	    DECL_ATTRIBUTES (decl)
+	      = remove_attribute ("exalias", DECL_ATTRIBUTES (*iter));
+
 	    if (cp_function_chain->extern_decl_map == NULL)
 	      cp_function_chain->extern_decl_map
 		= hash_table<cxx_int_tree_map_hasher>::create_ggc (20);
diff --git a/gcc/cp/optimize.c b/gcc/cp/optimize.c
index abdcd7f..1bcf7a9 100644
--- a/gcc/cp/optimize.c
+++ b/gcc/cp/optimize.c
@@ -516,10 +516,14 @@  maybe_clone_body (tree fn)
       DECL_VISIBILITY (clone) = DECL_VISIBILITY (fn);
       DECL_VISIBILITY_SPECIFIED (clone) = DECL_VISIBILITY_SPECIFIED (fn);
       DECL_DLLIMPORT_P (clone) = DECL_DLLIMPORT_P (fn);
-      DECL_ATTRIBUTES (clone) = copy_list (DECL_ATTRIBUTES (fn));
+      /* We may have already copied them in copy_fndecl_with_name.  */
+      if (DECL_ATTRIBUTES (clone) == DECL_ATTRIBUTES (fn))
+	DECL_ATTRIBUTES (clone) = copy_list (DECL_ATTRIBUTES (fn));
       DECL_DISREGARD_INLINE_LIMITS (clone) = DECL_DISREGARD_INLINE_LIMITS (fn);
       set_decl_section_name (clone, DECL_SECTION_NAME (fn));
 
+      update_exalias_interface (clone);
+
       /* Adjust the parameter names and locations.  */
       parm = DECL_ARGUMENTS (fn);
       clone_parm = DECL_ARGUMENTS (clone);
diff --git a/gcc/cp/rtti.c b/gcc/cp/rtti.c
index d43248c..a5cbb78 100644
--- a/gcc/cp/rtti.c
+++ b/gcc/cp/rtti.c
@@ -28,6 +28,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "stringpool.h"
 #include "intl.h"
 #include "stor-layout.h"
+#include "attribs.h"
 #include "c-family/c-pragma.h"
 #include "gcc-rich-location.h"
 
@@ -469,6 +470,18 @@  get_tinfo_decl (tree type)
       if (CLASS_TYPE_P (type))
 	CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type)) = d;
 
+      /* Copy exalias attributes from the type to the rtti obj decl.  */
+      tree *attrs = &DECL_ATTRIBUTES (d);
+      FOR_EACH_EXALIAS (exalias, TYPE_ATTRIBUTES (type))
+	{
+	  tree attr = tree_cons (TREE_PURPOSE (exalias),
+				 TREE_VALUE (exalias),
+				 *attrs);
+	  *attrs = attr;
+	  attrs = &TREE_CHAIN (attr);
+	}
+      create_exalias_decls (d);
+
       /* Add decl to the global array of tinfo decls.  */
       vec_safe_push (unemitted_tinfo_decls, d);
     }
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 0fb7e27..720b7f3 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -2882,6 +2882,39 @@  when using these attributes the problem is diagnosed
 earlier and with exact location of the call even in presence of inline
 functions or when not emitting debugging information.
 
+@item exalias ("@var{name}")
+@cindex @code{exalias} function attribute
+The @code{exalias} attribute causes @var{name} to be emitted as an alias
+to the definition.  For instance,
+
+@smallexample
+void f (uint64_t) __attribute__ ((__exalias__ ("f_u64")));
+void f (uint64_t) @{ /* @r{Do something.} */; @}
+@end smallexample
+
+@noindent
+defines @samp{f}, and outputs @samp{f_u64} as an alias for @samp{f}.
+This is particularly useful when exporting C++ names for use in other
+languages, or as an alias target, when machine-dependent types would
+make mangled names harder to deal with.
+
+In the case of C++ constructors and destructors, in which a single
+definition may output multiple symbols, the specified name is associated
+with the variant that constructs or destructs a complete object.  The
+variant that applies to a base subobject gets a @code{_Base} suffix, and
+the deleting destructor gets a @code{_Del} suffix.
+
+This attribute is silently ignored if @samp{f} is not defined in the
+same translation unit, so that the attribute can be attached to forward
+declarations.
+
+The name @samp{f_u64} is an assembly symbol name: it does not undergo
+C++ name mangling, and it is not made visible in any scope in the source
+language, but it can be named as an alias target.
+
+This attribute requires assembler and object file support,
+and may not be available on all targets.
+
 @item externally_visible
 @cindex @code{externally_visible} function attribute
 This attribute, attached to a global variable or function, nullifies
@@ -6928,6 +6961,10 @@  align them on any target.
 The @code{aligned} attribute can also be used for functions
 (@pxref{Common Function Attributes}.)
 
+@item exalias ("@var{name}")
+@cindex @code{exalias} variable attribute
+See @pxref{Common Function Attributes}.
+
 @cindex @code{warn_if_not_aligned} variable attribute
 @item warn_if_not_aligned (@var{alignment})
 This attribute specifies a threshold for the structure field, measured
@@ -7066,6 +7103,21 @@  types (@pxref{Common Function Attributes},
 The message attached to the attribute is affected by the setting of
 the @option{-fmessage-length} option.
 
+@item exalias ("@var{name}")
+@cindex @code{exalias} type attribute
+The @code{exalias} attribute causes @var{name} to be emitted as an alias
+to the definition of the C++ Run-Time Type Information (RTTI)
+@code{std::type_info} object associated with the type.  For instance,
+
+@smallexample
+class foo __attribute__ ((__exalias__ ("TI_foo")));
+@end smallexample
+
+@noindent
+arranges for @samp{TI_foo} to be defined as an alias to the RTTI object
+for class @samp{foo}, once the class is defined and used in ways that
+cause its RTTI object to be synthesized and output.
+
 @item mode (@var{mode})
 @cindex @code{mode} variable attribute
 This attribute specifies the data type for the declaration---whichever
diff --git a/gcc/symtab.c b/gcc/symtab.c
index 0e852d4..ce438e1 100644
--- a/gcc/symtab.c
+++ b/gcc/symtab.c
@@ -1861,6 +1861,32 @@  symtab_node::noninterposable_alias (symtab_node *node, void *data)
   return false;
 }
 
+/* Remap exalias nodes recorded as aliasing REPLACED to alias
+   REPLACEMENT instead.  */
+
+void
+symtab_node::remap_exalias_target (tree replaced, tree replacement)
+{
+  FOR_EACH_EXALIAS (exalias, DECL_ATTRIBUTES (replaced))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (exalias));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      symtab_node *sym_node = symtab_node::get_for_asmname (id);
+
+      gcc_assert (!sym_node->analyzed);
+      if (sym_node->alias_target != replaced)
+	continue;
+
+      sym_node->definition = 0;
+
+      if (VAR_P (replaced))
+	varpool_node::create_alias (sym_node->decl, replacement);
+      else
+	cgraph_node::create_alias (sym_node->decl, replacement);
+    }
+}
+
 /* If node cannot be overwriten by static or dynamic linker to point to
    different definition, return NODE. Otherwise look for alias with such
    property and if none exists, introduce new one.  */
diff --git a/gcc/testsuite/c-c++-common/attr-weak-1.c b/gcc/testsuite/c-c++-common/attr-weak-1.c
new file mode 100644
index 00000000..b11ef71
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/attr-weak-1.c
@@ -0,0 +1,19 @@ 
+/* { dg-do run { xfail *-*-* } } */
+/* { dg-require-effective-target weak_undefined } */
+
+/* C++ wouldn't combine attributes from local declarations with a
+   namespace-scoped symbol.  Now it does, but only if there is a
+   visible declaration.  */
+
+void foo () {
+  extern void __attribute__ ((__weak__)) undef_fn (void);
+  extern int __attribute__ ((__weak__)) undef_var;
+}
+
+int main () {
+  extern void undef_fn (void);
+  extern int undef_var;
+
+  if (&undef_fn || &undef_var)
+    __builtin_abort ();
+}
diff --git a/gcc/testsuite/c-c++-common/torture/attr-exalias-1.c b/gcc/testsuite/c-c++-common/torture/attr-exalias-1.c
new file mode 100644
index 00000000..3a471cc
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-exalias-1.c
@@ -0,0 +1,39 @@ 
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+extern int var_a __attribute__ ((__exalias__ ("FOOVAR_A")));
+int var_a = 1;
+
+void foo_a () __attribute__ ((__exalias__ ("FOOBAR_A")));
+
+void
+foo_a ()
+{
+}
+
+
+int var_b;
+extern int var_b __attribute__ ((__exalias__ ("FOOVAR_B")));
+
+void
+foo_b ()
+{
+}
+
+void foo_b () __attribute__ ((__exalias__ ("FOOBAR_B")));
+
+
+int var_c __attribute__ ((__exalias__ ("FOOVAR_C")));
+
+void __attribute__ ((__exalias__ ("FOOBAR_C")))
+foo_c ()
+{
+}
+
+
+/* { dg-final { scan-assembler "FOOBAR_A" } } */
+/* { dg-final { scan-assembler "FOOVAR_A" } } */
+/* { dg-final { scan-assembler "FOOBAR_B" } } */
+/* { dg-final { scan-assembler "FOOVAR_B" } } */
+/* { dg-final { scan-assembler "FOOBAR_C" } } */
+/* { dg-final { scan-assembler "FOOVAR_C" } } */
diff --git a/gcc/testsuite/c-c++-common/torture/attr-exalias-2.c b/gcc/testsuite/c-c++-common/torture/attr-exalias-2.c
new file mode 100644
index 00000000..4ab1f07
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-exalias-2.c
@@ -0,0 +1,13 @@ 
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+struct s
+{
+  int mem __attribute__ ((__exalias__ ("MEMFOO"))); /* { dg-warning "attribute ignored" } */
+};
+
+void foo()
+{
+  extern void bar () __attribute__ ((__exalias__ ("FOOBAR")));
+  int var __attribute__ ((__exalias__ ("FOOVAR"))); /* { dg-warning "public objects" } */
+}
diff --git a/gcc/testsuite/c-c++-common/torture/attr-exalias-3.c b/gcc/testsuite/c-c++-common/torture/attr-exalias-3.c
new file mode 100644
index 00000000..d94b618
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-exalias-3.c
@@ -0,0 +1,41 @@ 
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int var_a = 1;
+
+void
+foo_a ()
+{
+  extern int var_a __attribute__ ((__exalias__ ("FOOVAR_A")));
+  void foo_a () __attribute__ ((__exalias__ ("FOOBAR_A")));
+}
+
+#if __cplusplus
+/* Without this declaration before the local declaration below, the
+   attributes of the local declaration do not get propagated to the
+   (global) namespace scope.  */
+extern int var_b;
+#endif
+
+void
+foo_b ()
+{
+  extern int var_b __attribute__ ((__exalias__ ("FOOVAR_B")));
+}
+
+int var_b;
+
+void __attribute__ ((__exalias__ ("FOOBAR_C")))
+foo_c ()
+{
+  void foo_b () __attribute__ ((__exalias__ ("FOOBAR_B")));
+  /* Another exalias for var_b.  */
+  extern int var_b __attribute__ ((__exalias__ ("FOOVAR_C")));
+}
+
+/* { dg-final { scan-assembler "FOOBAR_A" } } */
+/* { dg-final { scan-assembler "FOOVAR_A" } } */
+/* { dg-final { scan-assembler "FOOBAR_B" } } */
+/* { dg-final { scan-assembler "FOOVAR_B" } } */
+/* { dg-final { scan-assembler "FOOBAR_C" } } */
+/* { dg-final { scan-assembler "FOOVAR_C" } } */
diff --git a/gcc/testsuite/c-c++-common/torture/attr-exalias-4.c b/gcc/testsuite/c-c++-common/torture/attr-exalias-4.c
new file mode 100644
index 00000000..6320d1a4
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-exalias-4.c
@@ -0,0 +1,28 @@ 
+/* { dg-do run } */
+/* { dg-require-alias "" } */
+
+int var_a __attribute__ ((__exalias__ ("FOOVAR_A"))) = 42;
+
+int __attribute__ ((__exalias__ ("FOOBAR_A")))
+foo_a (int p)
+{
+  return p;
+}
+
+extern int __attribute__ ((__alias__ (("FOOVAR_A")))) var_b;
+extern int __attribute__ ((__alias__ (("FOOBAR_A")))) foo_b (int p);
+
+int
+foo_c ()
+{
+  return foo_b (var_b);
+}
+
+int
+main ()
+{
+  if (foo_c () != 42)
+    __builtin_abort ();
+
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/torture/attr-exalias-1.C b/gcc/testsuite/g++.dg/torture/attr-exalias-1.C
new file mode 100644
index 00000000..9bd82a2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-exalias-1.C
@@ -0,0 +1,66 @@ 
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+class __attribute__ ((__exalias__ ("FOOCLS_A"))) foo {
+  static int var __attribute__ ((__exalias__ ("FOOVAR_A")));
+  __attribute__ ((__exalias__ ("FOOCTR_A"))) foo ();
+  void __attribute__ ((__exalias__ ("FOOBAR_A"))) bar ();
+  virtual __attribute__ ((__exalias__ ("FOODTR_A"))) ~foo() {}
+};
+
+int foo::var = 1;
+
+foo::foo () {}
+
+void foo::bar () {}
+
+namespace b {
+  class __attribute__ ((__exalias__ ("FOOCLS_B"))) foo {
+    static int var __attribute__ ((__exalias__ ("FOOVAR_B")));
+    __attribute__ ((__exalias__ ("FOOCTR_B"))) foo ();
+    void __attribute__ ((__exalias__ ("FOOBAR_B"))) bar () {}
+    virtual __attribute__ ((__exalias__ ("FOODTR_B"))) ~foo() {}
+  };
+
+  int foo::var = 2;
+
+  foo::foo () {}
+}
+
+namespace c {
+  namespace cd {
+    class __attribute__ ((__exalias__ ("FOOCLS_C"))) foo {
+      static int var __attribute__ ((__exalias__ ("FOOVAR_C")));
+      __attribute__ ((__exalias__ ("FOOCTR_C"))) foo () {}
+      void __attribute__ ((__exalias__ ("FOOBAR_C"))) bar () {}
+      virtual __attribute__ ((__exalias__ ("FOODTR_C"))) ~foo() {}
+    };
+
+    int foo::var = 3;
+  }
+}
+
+/* { dg-final { scan-assembler "FOOCLS_A" } } */
+/* { dg-final { scan-assembler "FOOBAR_A" } } */
+/* { dg-final { scan-assembler "FOOCTR_A" } } */
+/* { dg-final { scan-assembler "FOOCTR_A_Base" } } */
+/* { dg-final { scan-assembler "FOODTR_A" } } */
+/* { dg-final { scan-assembler "FOODTR_A_Base" } } */
+/* { dg-final { scan-assembler "FOODTR_A_Del" } } */
+/* { dg-final { scan-assembler "FOOVAR_A" } } */
+/* { dg-final { scan-assembler "FOOCLS_B" } } */
+/* { dg-final { scan-assembler "FOOBAR_B" } } */
+/* { dg-final { scan-assembler "FOOCTR_B" } } */
+/* { dg-final { scan-assembler "FOOCTR_A_Base" } } */
+/* { dg-final { scan-assembler "FOODTR_B" } } */
+/* { dg-final { scan-assembler "FOODTR_B_Base" } } */
+/* { dg-final { scan-assembler "FOODTR_B_Del" } } */
+/* { dg-final { scan-assembler "FOOVAR_B" } } */
+/* { dg-final { scan-assembler "FOOCLS_C" } } */
+/* { dg-final { scan-assembler "FOOBAR_C" } } */
+/* { dg-final { scan-assembler "FOOCTR_C" } } */
+/* { dg-final { scan-assembler "FOOCTR_C_Base" } } */
+/* { dg-final { scan-assembler "FOODTR_C" } } */
+/* { dg-final { scan-assembler "FOODTR_C_Base" } } */
+/* { dg-final { scan-assembler "FOODTR_C_Del" } } */
+/* { dg-final { scan-assembler "FOOVAR_C" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-exalias-2.C b/gcc/testsuite/g++.dg/torture/attr-exalias-2.C
new file mode 100644
index 00000000..a4cf015
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-exalias-2.C
@@ -0,0 +1,26 @@ 
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+namespace {
+  class __attribute__ ((__exalias__ ("FOOCLS_A"))) foo {
+    static int var __attribute__ ((__exalias__ ("FOOVAR_A")));
+    __attribute__ ((__exalias__ ("FOOCTR_A"))) foo ();
+    virtual __attribute__ ((__exalias__ ("FOODTR_A"))) ~foo ();
+    void __attribute__ ((__exalias__ ("FOOBAR_A"))) bar ();
+  };
+
+  int foo::var = 3;
+  foo::foo () {}
+  foo::~foo () {}
+  void foo::bar () {}
+}
+
+/* { dg-final { scan-assembler-not "\.globl" } */
+/* { dg-final { scan-assembler "FOOCLS_A" } } */
+/* { dg-final { scan-assembler "FOOBAR_A" } } */
+/* { dg-final { scan-assembler "FOOCTR_A" } } */
+/* { dg-final { scan-assembler "FOOCTR_A_Base" } } */
+/* { dg-final { scan-assembler "FOODTR_A" } } */
+/* { dg-final { scan-assembler "FOODTR_A_Base" } } */
+/* { dg-final { scan-assembler "FOODTR_A_Del" } } */
+/* { dg-final { scan-assembler "FOOVAR_A" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-exalias-3.C b/gcc/testsuite/g++.dg/torture/attr-exalias-3.C
new file mode 100644
index 00000000..6e4a868
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-exalias-3.C
@@ -0,0 +1,61 @@ 
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+// exalias can be applied to template function explicit instantiations.
+
+template <typename T>
+void
+fn(T) {
+};
+
+template void __attribute__ ((__exalias__ ("FOOFUN_UINT"))) fn<>(unsigned int);
+template void __attribute__ ((__exalias__ ("FOOFUN_LONG"))) fn<>(long);
+
+
+template <typename T = void>
+class
+foo {
+  virtual ~foo() {}
+
+  // Member functions that we wish to name with exalias must be
+  // defined outside the class body, *after* any exalias-named
+  // instantiations of the enclosing template types.  If they are
+  // defined in the class body, or before the explicit instantiation,
+  // they will be implicitly instantiated along with the enclosing
+  // template class type, and then the explicit instantiation of the
+  // member function will be rejected as a duplicate.  If we attempt
+  // to explicitly instantiate the member functions first, their
+  // naming the enclosing template type will trigger the implicit
+  // instantiation thereof, and then the subsequent explicit
+  // instantiation of the class type will have attributes ignored.
+  virtual void virtfun();
+
+  static void stfun() {}
+  void inlfun() {}
+};
+
+template class __attribute__ ((__exalias__ ("FOOCLS_VOID_TI"))) foo<>;
+template class __attribute__ ((__exalias__ ("FOOCLS_CHAR_TI"))) foo<char>;
+
+template void
+__attribute__ ((__exalias__ ("FOOCLS_SHORT_ST"))) foo<short>::stfun();
+template void
+__attribute__ ((__exalias__ ("FOOCLS_SHORT_INL"))) foo<short>::inlfun();
+
+template <typename T>
+void foo<T>::virtfun() {}
+
+template void
+__attribute__ ((__exalias__ ("FOOCLS_CHAR_VIRT"))) foo<char>::virtfun();
+
+template void
+__attribute__ ((__exalias__ ("FOOCLS_LONG_VIRT"))) foo<long>::virtfun();
+
+/* { dg-final { scan-assembler "FOOCLS_VOID_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_INL" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_LONG_VIRT" } } */
+/* { dg-final { scan-assembler "FOOFUN_UINT" } } */
+/* { dg-final { scan-assembler "FOOFUN_LONG" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-exalias-4.C b/gcc/testsuite/g++.dg/torture/attr-exalias-4.C
new file mode 100644
index 00000000..1ca7406a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-exalias-4.C
@@ -0,0 +1,28 @@ 
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+template <typename T = void>
+class
+__attribute__ ((__exalias__ ("FOOCLS"))) foo // { dg-warning "duplicate" }
+{
+  virtual ~foo() {}
+
+  virtual void
+    __attribute__ ((__exalias__ ("FOOVFN"))) // { dg-warning "duplicate" { xfail *-*-* } }
+    virtfun () {}
+};
+
+template <typename T>
+void
+__attribute__ ((__exalias__ ("FOOTFN"))) // { dg-warning "duplicate" { xfail *-*-* } }
+fn(T) {
+};
+
+template class foo<>;
+template class foo<int>;
+template void fn<>(int);
+template void fn<>(long);
+
+/* { dg-final { scan-assembler "FOOCLS" } } */
+/* { dg-final { scan-assembler "FOOVFN" { xfail *-*-* } } } */
+/* { dg-final { scan-assembler "FOOFUN" { xfail *-*-* } } } */
diff --git a/gcc/varpool.c b/gcc/varpool.c
index 458cdf1..cd84b1f 100644
--- a/gcc/varpool.c
+++ b/gcc/varpool.c
@@ -523,7 +523,7 @@  varpool_node::analyze (void)
       align_variable (decl, 0);
     }
   if (alias)
-    resolve_alias (varpool_node::get (alias_target));
+    resolve_alias (varpool_node::get_create (alias_target));
   else if (DECL_INITIAL (decl))
     record_references_in_initializer (decl, analyzed);
   analyzed = true;


introduce exrtti as a separate attribute for types

From: Alexandre Oliva <oliva@adacore.com>

Make attribute exalias apply to declarations only.  Use a separate
attribute exrtti to name exaliases for the RTTI object associated with
classes.
---
 .../doc/gnat_rm/interfacing_to_other_languages.rst |    2 +-
 gcc/attribs.h                                      |    3 +++
 gcc/c-family/c-attribs.c                           |    9 ++++++---
 gcc/cp/rtti.c                                      |    6 +++---
 gcc/doc/extend.texi                                |    8 ++++----
 gcc/testsuite/g++.dg/torture/attr-exalias-1.C      |    6 +++---
 gcc/testsuite/g++.dg/torture/attr-exalias-2.C      |    2 +-
 gcc/testsuite/g++.dg/torture/attr-exalias-3.C      |    6 +++---
 gcc/testsuite/g++.dg/torture/attr-exalias-4.C      |    2 +-
 9 files changed, 25 insertions(+), 19 deletions(-)

diff --git a/gcc/ada/doc/gnat_rm/interfacing_to_other_languages.rst b/gcc/ada/doc/gnat_rm/interfacing_to_other_languages.rst
index ce6a4da..483ae4a 100644
--- a/gcc/ada/doc/gnat_rm/interfacing_to_other_languages.rst
+++ b/gcc/ada/doc/gnat_rm/interfacing_to_other_languages.rst
@@ -125,7 +125,7 @@  cover a specific C++ exception in an exception handler.
 
 RTTI symbols undergo C++ name mangling, which can make for identifiers
 that are inconvenient to use.  An alias with a mnemonic name can be
-introduced by adding attribute ``exalias`` to the class that the RTTI
+introduced by adding attribute ``exrtti`` to the class that the RTTI
 symbol refers to.
 
 
diff --git a/gcc/attribs.h b/gcc/attribs.h
index 1ba56ca..fa04890 100644
--- a/gcc/attribs.h
+++ b/gcc/attribs.h
@@ -254,5 +254,8 @@  extern void create_exalias_decls (tree);
 #define FOR_EACH_EXALIAS(exalias, attrs)				\
   for (tree exalias = lookup_attribute ("exalias", (attrs)); exalias;	\
        exalias = lookup_attribute ("exalias", TREE_CHAIN (exalias)))
+#define FOR_EACH_EXRTTI(exalias, attrs)					\
+  for (tree exalias = lookup_attribute ("exrtti", (attrs)); exalias;	\
+       exalias = lookup_attribute ("exrtti", TREE_CHAIN (exalias)))
 
 #endif // GCC_ATTRIBS_H
diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
index b726f44..4342bf4 100644
--- a/gcc/c-family/c-attribs.c
+++ b/gcc/c-family/c-attribs.c
@@ -334,7 +334,9 @@  const struct attribute_spec c_common_attribute_table[] =
 			      handle_alias_attribute, NULL },
   { "weakref",                0, 1, true,  false, false, false,
 			      handle_weakref_attribute, NULL },
-  { "exalias",                1, 1, false,  false, false, false,
+  { "exalias",                1, 1, true,  false, false, false,
+			      handle_exalias_attribute, NULL },
+  { "exrtti",                 1, 1, false, true, false, false,
 			      handle_exalias_attribute, NULL },
   { "no_instrument_function", 0, 0, true,  false, false, false,
 			      handle_no_instrument_function_attribute,
@@ -2447,8 +2449,8 @@  handle_alias_attribute (tree *node, tree name, tree args,
   return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs);
 }
 
-/* Handle an "exalias" attribute; arguments as in struct
-   attribute_spec.handler.  */
+/* Handle an "exalias" or "exrtti" attribute; arguments as in
+   struct attribute_spec.handler.  */
 
 static tree
 handle_exalias_attribute (tree *pnode, tree name, tree args,
@@ -2607,6 +2609,7 @@  handle_copy_attribute (tree *node, tree name, tree args,
 	  if (is_attribute_p ("alias", atname)
 	      || is_attribute_p ("always_inline", atname)
 	      || is_attribute_p ("exalias", atname)
+	      || is_attribute_p ("exrtti", atname)
 	      || is_attribute_p ("gnu_inline", atname)
 	      || is_attribute_p ("ifunc", atname)
 	      || is_attribute_p ("noinline", atname)
diff --git a/gcc/cp/rtti.c b/gcc/cp/rtti.c
index a5cbb78..f91ab9b 100644
--- a/gcc/cp/rtti.c
+++ b/gcc/cp/rtti.c
@@ -470,11 +470,11 @@  get_tinfo_decl (tree type)
       if (CLASS_TYPE_P (type))
 	CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type)) = d;
 
-      /* Copy exalias attributes from the type to the rtti obj decl.  */
+      /* Copy exrtti attributes from the type to the rtti obj decl.  */
       tree *attrs = &DECL_ATTRIBUTES (d);
-      FOR_EACH_EXALIAS (exalias, TYPE_ATTRIBUTES (type))
+      FOR_EACH_EXRTTI (exalias, TYPE_ATTRIBUTES (type))
 	{
-	  tree attr = tree_cons (TREE_PURPOSE (exalias),
+	  tree attr = tree_cons (get_identifier ("exalias"),
 				 TREE_VALUE (exalias),
 				 *attrs);
 	  *attrs = attr;
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 720b7f3..915e7bd 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -7103,14 +7103,14 @@  types (@pxref{Common Function Attributes},
 The message attached to the attribute is affected by the setting of
 the @option{-fmessage-length} option.
 
-@item exalias ("@var{name}")
-@cindex @code{exalias} type attribute
-The @code{exalias} attribute causes @var{name} to be emitted as an alias
+@item exrtti ("@var{name}")
+@cindex @code{exrtti} type attribute
+The @code{exrtti} attribute causes @var{name} to be emitted as an alias
 to the definition of the C++ Run-Time Type Information (RTTI)
 @code{std::type_info} object associated with the type.  For instance,
 
 @smallexample
-class foo __attribute__ ((__exalias__ ("TI_foo")));
+class foo __attribute__ ((__exrtti__ ("TI_foo")));
 @end smallexample
 
 @noindent
diff --git a/gcc/testsuite/g++.dg/torture/attr-exalias-1.C b/gcc/testsuite/g++.dg/torture/attr-exalias-1.C
index 9bd82a2..432c32bc 100644
--- a/gcc/testsuite/g++.dg/torture/attr-exalias-1.C
+++ b/gcc/testsuite/g++.dg/torture/attr-exalias-1.C
@@ -1,7 +1,7 @@ 
 /* { dg-do compile } */
 /* { dg-require-alias "" } */
 
-class __attribute__ ((__exalias__ ("FOOCLS_A"))) foo {
+class __attribute__ ((__exrtti__ ("FOOCLS_A"))) foo {
   static int var __attribute__ ((__exalias__ ("FOOVAR_A")));
   __attribute__ ((__exalias__ ("FOOCTR_A"))) foo ();
   void __attribute__ ((__exalias__ ("FOOBAR_A"))) bar ();
@@ -15,7 +15,7 @@  foo::foo () {}
 void foo::bar () {}
 
 namespace b {
-  class __attribute__ ((__exalias__ ("FOOCLS_B"))) foo {
+  class __attribute__ ((__exrtti__ ("FOOCLS_B"))) foo {
     static int var __attribute__ ((__exalias__ ("FOOVAR_B")));
     __attribute__ ((__exalias__ ("FOOCTR_B"))) foo ();
     void __attribute__ ((__exalias__ ("FOOBAR_B"))) bar () {}
@@ -29,7 +29,7 @@  namespace b {
 
 namespace c {
   namespace cd {
-    class __attribute__ ((__exalias__ ("FOOCLS_C"))) foo {
+    class __attribute__ ((__exrtti__ ("FOOCLS_C"))) foo {
       static int var __attribute__ ((__exalias__ ("FOOVAR_C")));
       __attribute__ ((__exalias__ ("FOOCTR_C"))) foo () {}
       void __attribute__ ((__exalias__ ("FOOBAR_C"))) bar () {}
diff --git a/gcc/testsuite/g++.dg/torture/attr-exalias-2.C b/gcc/testsuite/g++.dg/torture/attr-exalias-2.C
index a4cf015..c36d2f9b 100644
--- a/gcc/testsuite/g++.dg/torture/attr-exalias-2.C
+++ b/gcc/testsuite/g++.dg/torture/attr-exalias-2.C
@@ -2,7 +2,7 @@ 
 /* { dg-require-alias "" } */
 
 namespace {
-  class __attribute__ ((__exalias__ ("FOOCLS_A"))) foo {
+  class __attribute__ ((__exrtti__ ("FOOCLS_A"))) foo {
     static int var __attribute__ ((__exalias__ ("FOOVAR_A")));
     __attribute__ ((__exalias__ ("FOOCTR_A"))) foo ();
     virtual __attribute__ ((__exalias__ ("FOODTR_A"))) ~foo ();
diff --git a/gcc/testsuite/g++.dg/torture/attr-exalias-3.C b/gcc/testsuite/g++.dg/torture/attr-exalias-3.C
index 6e4a868..6af9217 100644
--- a/gcc/testsuite/g++.dg/torture/attr-exalias-3.C
+++ b/gcc/testsuite/g++.dg/torture/attr-exalias-3.C
@@ -18,7 +18,7 @@  foo {
   virtual ~foo() {}
 
   // Member functions that we wish to name with exalias must be
-  // defined outside the class body, *after* any exalias-named
+  // defined outside the class body, *after* any exrtti-named
   // instantiations of the enclosing template types.  If they are
   // defined in the class body, or before the explicit instantiation,
   // they will be implicitly instantiated along with the enclosing
@@ -34,8 +34,8 @@  foo {
   void inlfun() {}
 };
 
-template class __attribute__ ((__exalias__ ("FOOCLS_VOID_TI"))) foo<>;
-template class __attribute__ ((__exalias__ ("FOOCLS_CHAR_TI"))) foo<char>;
+template class __attribute__ ((__exrtti__ ("FOOCLS_VOID_TI"))) foo<>;
+template class __attribute__ ((__exrtti__ ("FOOCLS_CHAR_TI"))) foo<char>;
 
 template void
 __attribute__ ((__exalias__ ("FOOCLS_SHORT_ST"))) foo<short>::stfun();
diff --git a/gcc/testsuite/g++.dg/torture/attr-exalias-4.C b/gcc/testsuite/g++.dg/torture/attr-exalias-4.C
index 1ca7406a..1e76ac8 100644
--- a/gcc/testsuite/g++.dg/torture/attr-exalias-4.C
+++ b/gcc/testsuite/g++.dg/torture/attr-exalias-4.C
@@ -3,7 +3,7 @@ 
 
 template <typename T = void>
 class
-__attribute__ ((__exalias__ ("FOOCLS"))) foo // { dg-warning "duplicate" }
+__attribute__ ((__exrtti__ ("FOOCLS"))) foo // { dg-warning "duplicate" }
 {
   virtual ~foo() {}