public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc(refs/users/aoliva/heads/testme)] Introduce attribute sym_alias
@ 2023-11-20  9:03 Alexandre Oliva
  0 siblings, 0 replies; 33+ messages in thread
From: Alexandre Oliva @ 2023-11-20  9:03 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:fae394e99ed92c9ea302ebd923334b44638564bf

commit fae394e99ed92c9ea302ebd923334b44638564bf
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Fri Jul 14 03:45:14 2023 -0300

    Introduce attribute sym_alias
    
    This patch introduces an attribute to add extra asm names (aliases)
    for a decl 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 class types, namespace-scoped
    variables and functions, static data members, member functions,
    explicit instantiations and specializations of template functions,
    members and classes.
    
    When applied to constructors or destructor, additional sym aliases
    with _Base and _Del suffixes are defined for variants other than
    complete-object ones.  This changes the assumption that clones always
    carry the same attributes as their abstract declarations, so there is
    now a function to adjust them.
    
    C++ also had a bug in which attributes from local extern declarations
    failed to be propagated to a preexisting corresponding
    namespace-scoped decl.  I've fixed that, and adjusted acc tests that
    distinguished between C and C++ in this regard.
    
    Applying the attribute to class types is only valid in C++, and the
    effect is to attach the alias to the RTTI object associated with the
    class type.
    
    for  gcc/ChangeLog
    
            * attribs.cc: Include cgraph.h.
            (decl_attributes): Allow late introduction of sym_alias in
            types.
            (create_sym_alias_decl, create_sym_alias_decls): New.
            * attribs.h: Declare them.
            (FOR_EACH_SYM_ALIAS): New macro.
            * cgraph.cc (cgraph_node::create): Create sym_alias decls.
            * varpool.cc (varpool_node::get_create): Create sym_alias
            decls.
            * cgraph.h (symtab_node::remap_sym_alias_target): New.
            * symtab.cc (symtab_node::remap_sym_alias_target): Define.
            * cgraphunit.cc (cgraph_node::analyze): Create alias_target
            node if needed.
            (analyze_functions): Fixup visibility of implicit alias only
            after its node is analyzed.
            * doc/extend.texi (sym_alias): Document for variables,
            functions and types.
    
    for  gcc/ada/ChangeLog
    
            * doc/gnat_rm/interfacing_to_other_languages.rst: Mention
            attribute sym_alias to give RTTI symbols mnemonic names.
            * doc/gnat_ugn/the_gnat_compilation_model.rst: Mention
            aliases.  Fix incorrect ref to C1 ctor variant.
    
    for  gcc/c-family/ChangeLog
    
            * c-ada-spec.cc (pp_asm_name): Use first sym_alias if
            available.
            * c-attribs.cc (handle_sym_alias_attribute): New.
            (c_common_attribute_table): Add sym_alias.
            (handle_copy_attribute): Do not copy sym_alias attribute.
    
    for  gcc/c/ChangeLog
    
            * c-decl.cc (duplicate_decls): Remap sym_alias target.
    
    for  gcc/cp/ChangeLog
    
            * class.cc (adjust_clone_attributes): New.
            (copy_fndecl_with_name, build_clone): Call it.
            * cp-tree.h (adjust_clone_attributes): Declare.
            (update_sym_alias_interface): Declare.
            (update_tinfo_sym_alias): Declare.
            * decl.cc (duplicate_decls): Remap sym_alias target.
            Adjust clone attributes.
            (grokfndecl): Tentatively create sym_alias decls after
            adding attributes in e.g. a template member function explicit
            instantiation.
            * decl2.cc (cplus_decl_attributes): Update tinfo sym_alias.
            (copy_interface, update_sym_alias_interface): New.
            (determine_visibility): Update sym_alias interface.
            (tentative_decl_linkage, import_export_decl): Likewise.
            * name-lookup.cc: Include target.h and cgraph.h.
            (push_local_extern_decl_alias): Merge attributes with
            namespace-scoped decl, and drop duplicate sym_alias.
            * optimize.cc (maybe_clone_body): Re-adjust attributes after
            cloning them.  Update sym_alias interface.
            * rtti.cc: Include attribs.h and cgraph.h.
            (get_tinfo_decl): Copy sym_alias attributes from type to tinfo
            decl.  Create sym_alias decls.
            (update_tinfo_sym_alias): New.
    
    for  gcc/testsuite/ChangeLog
    
            * c-c++-common/goacc/declare-1.c: Adjust.
            * c-c++-common/goacc/declare-2.c: Adjust.
            * c-c++-common/torture/attr-sym-alias-1.c: New.
            * c-c++-common/torture/attr-sym-alias-2.c: New.
            * c-c++-common/torture/attr-sym-alias-3.c: New.
            * c-c++-common/torture/attr-sym-alias-4.c: New.
            * g++.dg/torture/attr-alias-sym-1.C: New.
            * g++.dg/torture/attr-alias-sym-2.C: New.
            * g++.dg/torture/attr-alias-sym-3.C: New.
            * g++.dg/torture/attr-alias-sym-4.C: New.
            * g++.dg/torture/attr-alias-sym-5.C: New.

Diff:
---
 .../doc/gnat_rm/interfacing_to_other_languages.rst |  6 ++
 .../doc/gnat_ugn/the_gnat_compilation_model.rst    | 10 ++-
 gcc/attribs.cc                                     | 68 +++++++++++++++++-
 gcc/attribs.h                                      |  7 ++
 gcc/c-family/c-ada-spec.cc                         |  7 ++
 gcc/c-family/c-attribs.cc                          | 33 ++++++++-
 gcc/c/c-decl.cc                                    |  2 +
 gcc/cgraph.cc                                      |  2 +
 gcc/cgraph.h                                       |  4 ++
 gcc/cgraphunit.cc                                  |  2 +-
 gcc/cp/class.cc                                    | 64 +++++++++++++++++
 gcc/cp/cp-tree.h                                   |  4 ++
 gcc/cp/decl.cc                                     |  4 ++
 gcc/cp/decl2.cc                                    | 50 +++++++++++++
 gcc/cp/name-lookup.cc                              | 11 +++
 gcc/cp/optimize.cc                                 |  3 +
 gcc/cp/rtti.cc                                     | 71 +++++++++++++++++-
 gcc/doc/extend.texi                                | 52 ++++++++++++++
 gcc/symtab.cc                                      | 36 ++++++++++
 gcc/testsuite/c-c++-common/goacc/declare-1.c       |  6 +-
 gcc/testsuite/c-c++-common/goacc/declare-2.c       | 14 ++--
 .../c-c++-common/torture/attr-sym-alias-1.c        | 39 ++++++++++
 .../c-c++-common/torture/attr-sym-alias-2.c        | 13 ++++
 .../c-c++-common/torture/attr-sym-alias-3.c        | 41 +++++++++++
 .../c-c++-common/torture/attr-sym-alias-4.c        | 28 ++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C    | 72 +++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C    | 26 +++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C    | 83 ++++++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C    | 28 ++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C    | 14 ++++
 gcc/varpool.cc                                     |  3 +
 31 files changed, 784 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 ad0be511d48..b8de16ebf7c 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 ``sym_alias`` 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 ed24fed7293..b3f55901c97 100644
--- a/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
+++ b/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
@@ -4275,6 +4275,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__ ((__sym_alias__ ("Ctor_For_Animal")))
         Animal() {Age_Count = 0;};
       private:
         int Age_Count;
@@ -4307,6 +4308,7 @@ both Carnivore and Domestic, that is:
         virtual int  Number_Of_Teeth ();
         virtual void Set_Owner (char* Name);
 
+        __attribute__ ((__sym_alias__ ("Ctor_For_Dog"))) // mnemonic alias
         Dog(); // Constructor
       private:
         int  Tooth_Count;
@@ -4345,7 +4347,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;
@@ -4361,7 +4364,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,
@@ -4383,7 +4386,8 @@ 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.
+mangled name (or an alias) 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.cc b/gcc/attribs.cc
index 4b7e0c6ef10..70c06df497f 100644
--- a/gcc/attribs.cc
+++ b/gcc/attribs.cc
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "target.h"
 #include "tree.h"
+#include "cgraph.h"
 #include "stringpool.h"
 #include "diagnostic-core.h"
 #include "attribs.h"
@@ -826,7 +827,8 @@ decl_attributes (tree *node, tree attributes, int flags,
 
       if (TYPE_P (*anode)
 	  && (flags & (int) ATTR_FLAG_TYPE_IN_PLACE)
-	  && COMPLETE_TYPE_P (*anode))
+	  && COMPLETE_TYPE_P (*anode)
+	  && !is_attribute_p ("sym_alias", name))
 	{
 	  warning (OPT_Wattributes, "type attributes ignored after type is already defined");
 	  continue;
@@ -2669,6 +2671,70 @@ attr_access::array_as_string (tree type) const
   return typstr;
 }
 
+/* Create a sym attribute for DECL to be visible with linkage name ID.  */
+
+tree
+create_sym_alias_decl (tree decl, tree id)
+{
+  const char *attr_str = "sym_alias";
+
+  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;
+
+      tree attr_name = get_identifier (attr_str);
+      error_at (DECL_SOURCE_LOCATION (decl),
+		"duplicate symbol name %qE in %qE attribute of %qD",
+		id, attr_name, decl);
+      inform (DECL_SOURCE_LOCATION (sym_node->decl),
+	      "already used by %qD", sym_node->decl);
+    }
+
+  tree clone = copy_node (decl);
+  DECL_ATTRIBUTES (clone) = remove_attribute (attr_str,
+					      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_extra_name_alias (clone, decl);
+    }
+  else
+    {
+      cgraph_node::create_same_body_alias (clone, decl);
+    }
+
+  return clone;
+}
+
+/* Create decls for all sym aliases requested in DECL's attributes.  */
+
+void
+create_sym_alias_decls (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl)
+      || DECL_ABSTRACT_P (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      create_sym_alias_decl (decl, id);
+    }
+}
+
 #if CHECKING_P
 
 namespace selftest
diff --git a/gcc/attribs.h b/gcc/attribs.h
index 84a43658a70..156f2c20588 100644
--- a/gcc/attribs.h
+++ b/gcc/attribs.h
@@ -398,4 +398,11 @@ extern void init_attr_rdwr_indices (rdwr_map *, tree);
 extern attr_access *get_parm_access (rdwr_map &, tree,
 				     tree = current_function_decl);
 
+extern tree create_sym_alias_decl (tree, tree);
+extern void create_sym_alias_decls (tree);
+
+#define FOR_EACH_SYM_ALIAS(sym, attrs)					\
+  for (tree sym = lookup_attribute ("sym_alias", (attrs));		\
+       sym; sym = lookup_attribute ("sym_alias", TREE_CHAIN (sym)))
+
 #endif // GCC_ATTRIBS_H
diff --git a/gcc/c-family/c-ada-spec.cc b/gcc/c-family/c-ada-spec.cc
index 050994d8416..5042b9cfecd 100644
--- a/gcc/c-family/c-ada-spec.cc
+++ b/gcc/c-family/c-ada-spec.cc
@@ -1431,6 +1431,13 @@ pp_ada_tree_identifier (pretty_printer *buffer, tree node, tree type,
 static void
 pp_asm_name (pretty_printer *buffer, tree t)
 {
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (t))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.cc b/gcc/c-family/c-attribs.cc
index ac68153e244..ee5c3aedccf 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -110,7 +110,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_sym_alias_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,
@@ -392,6 +393,8 @@ const struct attribute_spec c_common_attribute_table[] =
 			      handle_alias_attribute, NULL },
   { "weakref",                0, 1, true,  false, false, false,
 			      handle_weakref_attribute, NULL },
+  { "sym_alias",              1, 1, false,  false, false, false,
+			      handle_sym_alias_attribute, NULL },
   { "no_instrument_function", 0, 0, true,  false, false, false,
 			      handle_no_instrument_function_attribute,
 			      NULL },
@@ -3084,7 +3087,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
@@ -3094,7 +3097,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
@@ -3104,6 +3107,29 @@ handle_alias_attribute (tree *node, tree name, tree args,
   return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs);
 }
 
+/* Handle a "sym_alias" attribute; arguments as in struct
+   attribute_spec.handler.  */
+
+static tree
+handle_sym_alias_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 (decl_in_symtab_p (node))
+    *no_add_attrs = false;
+  else if (TYPE_P (node) && c_dialect_cxx ())
+    *no_add_attrs = false;
+  else
+    return error_mark_node;
+
+  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.  */
 
@@ -3237,6 +3263,7 @@ handle_copy_attribute (tree *node, tree name, tree args,
 	      || is_attribute_p ("visibility", atname)
 	      || is_attribute_p ("weak", atname)
 	      || is_attribute_p ("weakref", atname)
+	      || is_attribute_p ("sym_alias", atname)
 	      || is_attribute_p ("target_clones", atname))
 	    continue;
 
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 87b82752af8..f3aa58fedfd 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -3119,6 +3119,8 @@ duplicate_decls (tree newdecl, tree olddecl)
 
   merge_decls (newdecl, olddecl, newtype, oldtype);
 
+  symtab_node::remap_sym_alias_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.cc b/gcc/cgraph.cc
index e41e5ad3ae7..c45ce2d0a13 100644
--- a/gcc/cgraph.cc
+++ b/gcc/cgraph.cc
@@ -523,6 +523,8 @@ cgraph_node::create (tree decl)
   node->register_symbol ();
   maybe_record_nested_function (node);
 
+  create_sym_alias_decls (decl);
+
   return node;
 }
 
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index 177bd9dc5b7..05b3c2ea422 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -327,6 +327,10 @@ public:
   /* Return DECL that alias is aliasing.  */
   inline tree get_alias_target_tree ();
 
+  /* Remap sym alias nodes recorded as aliasing REPLACED to alias REPLACEMENT
+     instead.  */
+  static void remap_sym_alias_target (tree replaced, tree replacement);
+
   /* Set section for symbol and its aliases.  */
   void set_section (const char *section);
 
diff --git a/gcc/cgraphunit.cc b/gcc/cgraphunit.cc
index 9a550a5cce6..a66db795c78 100644
--- a/gcc/cgraphunit.cc
+++ b/gcc/cgraphunit.cc
@@ -1176,7 +1176,7 @@ analyze_functions (bool first_time)
      C++ FE is confused about the COMDAT groups being right.  */
   if (symtab->cpp_implicit_aliases_done)
     FOR_EACH_SYMBOL (node)
-      if (node->cpp_implicit_alias)
+      if (node->cpp_implicit_alias && node->analyzed)
 	  node->fixup_same_cpp_alias_visibility (node->get_alias_target ());
   build_type_inheritance_graph ();
 
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index 4766b7cf832..ca9844a2c29 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -4896,6 +4896,68 @@ check_methods (tree t)
     }
 }
 
+/* Adjust sym alias name for CLONE, cloned from FN and named NAME,
+   if it is a cdtor, and drop the sym alias from other clones.  */
+
+void
+adjust_clone_attributes (tree fn, tree clone, tree name, bool skip_copy_p)
+{
+  if (IDENTIFIER_CDTOR_P (name))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	{
+	  found = true;
+	  break;
+	}
+
+      if (found
+	  && (name == complete_ctor_identifier
+	      || name == complete_dtor_identifier))
+	{
+	  /* Reuse the sym alias decls created for the primary cdtor
+	     decl.  */
+	  symtab_node::remap_sym_alias_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);
+
+	  if (!skip_copy_p)
+	    DECL_ATTRIBUTES (clone) = copy_list (DECL_ATTRIBUTES (clone));
+
+	  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	    {
+	      /* We need to copy this even with skip_copy_p, because
+		 even then copying was shallow.  */
+	      TREE_VALUE (sym) = copy_list (TREE_VALUE (sym));
+	      /* Append suf to the sym alias name.  */
+	      tree str = TREE_VALUE (TREE_VALUE (sym));
+	      char *symname = concat (TREE_STRING_POINTER (str), suf, NULL);
+	      str = build_string (TREE_STRING_LENGTH (str) + xlen, symname);
+	      TREE_VALUE (TREE_VALUE (sym)) = str;
+	      free (symname);
+	    }
+
+	  if (symtab_node::get (clone))
+	    create_sym_alias_decls (clone);
+	}
+    }
+  else
+    DECL_ATTRIBUTES (clone)
+      = remove_attribute ("sym_alias", DECL_ATTRIBUTES (clone));
+}
+
 /* FN is constructor, destructor or operator function.  Clone the
    declaration to create a NAME'd variant.  NEED_VTT_PARM_P and
    OMIT_INHERITED_PARMS_P are relevant if it's a cdtor.  */
@@ -5063,6 +5125,8 @@ build_clone (tree fn, tree name, bool need_vtt_parm_p,
   DECL_CHAIN (clone) = DECL_CHAIN (fn);
   DECL_CHAIN (fn) = clone;
 
+  adjust_clone_attributes (fn, clone, name);
+
   return clone;
 }
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 7b0b7c6a17e..05eda39df74 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5792,6 +5792,8 @@ struct GTY((for_user)) spec_entry
 
 extern int current_class_depth;
 
+void adjust_clone_attributes (tree fn, tree clone, tree name, bool = false);
+
 /* in decl.cc */
 
 /* An array of static vars & fns.  */
@@ -7026,6 +7028,7 @@ extern void do_push_parm_decls			(tree, tree, tree *);
 extern tree do_aggregate_paren_init		(tree, tree);
 
 /* in decl2.cc */
+extern void update_sym_alias_interface		(tree);
 extern void record_mangling			(tree, bool);
 extern void overwrite_mangling			(tree, tree);
 extern void note_mangling_alias			(tree, tree);
@@ -7600,6 +7603,7 @@ extern bool emit_tinfo_decl			(tree);
 extern unsigned get_pseudo_tinfo_index		(tree);
 extern tree get_pseudo_tinfo_type		(unsigned);
 extern tree build_if_nonnull			(tree, tree, tsubst_flags_t);
+extern void update_tinfo_sym_alias		(tree);
 
 /* in search.cc */
 extern tree get_parent_with_private_access 	(tree decl, tree binfo);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index e6f75d771e0..38559cd2b1c 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -3231,6 +3231,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
 	      && TREE_STATIC (olddecl))))
     make_decl_rtl (olddecl);
 
+  symtab_node::remap_sym_alias_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.
@@ -3254,6 +3256,7 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
       FOR_EACH_CLONE (clone, olddecl)
 	{
 	  DECL_ATTRIBUTES (clone) = DECL_ATTRIBUTES (olddecl);
+	  adjust_clone_attributes (olddecl, clone, DECL_NAME (clone));
 	  DECL_PRESERVE_P (clone) |= DECL_PRESERVE_P (olddecl);
 	}
     }
@@ -10783,6 +10786,7 @@ grokfndecl (tree ctype,
     {
       cplus_decl_attributes (&decl, *attrlist, 0);
       *attrlist = NULL_TREE;
+      create_sym_alias_decls (decl);
     }
 
   if (DECL_HAS_CONTRACTS_P (decl))
diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
index 9e666e5eece..c7f009beaf0 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -1844,6 +1844,9 @@ cplus_decl_attributes (tree *decl, tree attributes, int flags)
   if (late_attrs)
     save_template_attributes (late_attrs, decl, flags);
 
+  if (TYPE_P (*decl) && attributes)
+    update_tinfo_sym_alias (*decl);
+
   /* Propagate deprecation out to the template.  */
   if (TREE_DEPRECATED (*decl))
     if (tree ti = get_template_info (*decl))
@@ -2200,6 +2203,47 @@ 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 sym aliases.  */
+
+void
+update_sym_alias_interface (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.  */
 
@@ -3007,6 +3051,8 @@ determine_visibility (tree decl)
        translation unit, we can make the type internal.  */
     constrain_visibility (decl, VISIBILITY_ANON, false);
 
+  update_sym_alias_interface (decl);
+
   /* If visibility changed and DECL already has DECL_RTL, ensure
      symbol flags are updated.  */
   if ((DECL_VISIBILITY (decl) != orig_visibility
@@ -3269,6 +3315,8 @@ tentative_decl_linkage (tree decl)
       else if (VAR_P (decl))
 	maybe_commonize_var (decl);
     }
+
+  update_sym_alias_interface (decl);
 }
 
 /* DECL is a FUNCTION_DECL or VAR_DECL.  If the object file linkage
@@ -3503,6 +3551,8 @@ import_export_decl (tree decl)
     }
 
   DECL_INTERFACE_KNOWN (decl) = 1;
+
+  update_sym_alias_interface (decl);
 }
 
 /* Return an expression that performs the destruction of DECL, which
diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc
index 50aeb776ae2..4f85ef2c499 100644
--- a/gcc/cp/name-lookup.cc
+++ b/gcc/cp/name-lookup.cc
@@ -22,9 +22,11 @@ along with GCC; see the file COPYING3.  If not see
 #define INCLUDE_MEMORY
 #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"
@@ -3479,6 +3481,15 @@ push_local_extern_decl_alias (tree decl)
 	  /* Adjust visibility.  */
 	  determine_visibility (alias);
 	}
+      else if (DECL_P (alias))
+	DECL_ATTRIBUTES (alias)
+	  = targetm.merge_decl_attributes (alias, decl);
+      if (DECL_P (alias))
+	{
+	  symtab_node::remap_sym_alias_target (decl, alias);
+	  DECL_ATTRIBUTES (decl)
+	    = remove_attribute ("sym_alias", DECL_ATTRIBUTES (alias));
+	}
     }
 
   retrofit_lang_decl (decl);
diff --git a/gcc/cp/optimize.cc b/gcc/cp/optimize.cc
index 9e8926e4cc6..4a2e8cb4354 100644
--- a/gcc/cp/optimize.cc
+++ b/gcc/cp/optimize.cc
@@ -528,9 +528,12 @@ maybe_clone_body (tree fn)
       DECL_VISIBILITY_SPECIFIED (clone) = DECL_VISIBILITY_SPECIFIED (fn);
       DECL_DLLIMPORT_P (clone) = DECL_DLLIMPORT_P (fn);
       DECL_ATTRIBUTES (clone) = clone_attrs (DECL_ATTRIBUTES (fn));
+      adjust_clone_attributes (fn, clone, DECL_NAME (clone), true);
       DECL_DISREGARD_INLINE_LIMITS (clone) = DECL_DISREGARD_INLINE_LIMITS (fn);
       set_decl_section_name (clone, fn);
 
+      update_sym_alias_interface (clone);
+
       /* Adjust the parameter names and locations.  */
       parm = DECL_ARGUMENTS (fn);
       clone_parm = DECL_ARGUMENTS (clone);
diff --git a/gcc/cp/rtti.cc b/gcc/cp/rtti.cc
index 7878929c246..4bb7b8c8c6e 100644
--- a/gcc/cp/rtti.cc
+++ b/gcc/cp/rtti.cc
@@ -28,8 +28,10 @@ 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"
+#include "cgraph.h"
 
 /* C++ returns type information to the user in struct type_info
    objects. We also use type information to implement dynamic_cast and
@@ -479,8 +481,13 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
 	  = build_tree_list (get_identifier ("non overlapping"),
 			     NULL_TREE);
       else
+	/* Share the non overlapping attribute, without assuming it's
+	   the only attribute, but assuming it's the last if it's
+	   present.  There may be sym aliases too, and those are not
+	   to be shared.  */
 	DECL_ATTRIBUTES (d)
-	  = DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]);
+	  = lookup_attribute ("non overlapping",
+			      DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]));
 
       /* Mark the variable as undefined -- but remember that we can
 	 define it later if we need to do so.  */
@@ -492,6 +499,16 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
       if (CLASS_TYPE_P (type))
 	CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type)) = d;
 
+      /* Copy sym alias attributes from the type to the rtti obj decl.  */
+      tree *attrs = &DECL_ATTRIBUTES (d);
+      FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+	{
+	  tree attr = tree_cons (TREE_PURPOSE (sym), TREE_VALUE (sym), *attrs);
+	  *attrs = attr;
+	  attrs = &TREE_CHAIN (attr);
+	}
+      create_sym_alias_decls (d);
+
       /* Add decl to the global array of tinfo decls.  */
       vec_safe_push (unemitted_tinfo_decls, d);
     }
@@ -499,6 +516,58 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
   return d;
 }
 
+/* After modifying the attributes of TYPE, check whether tinfo was
+   already created and, if so, add to it any sym alias attributes
+   that were not already present.  */
+
+void
+update_tinfo_sym_alias (tree type)
+{
+  if (!TYPE_SIZE (type) || !CLASS_TYPE_P (type))
+    return;
+
+  tree d = CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type));
+  if (!d)
+    return;
+
+  bool first = true;
+  symtab_node *node = NULL;
+
+  tree *attrs = &DECL_ATTRIBUTES (d);
+  FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (d_sym, *attrs)
+	if (TREE_VALUE (sym) == TREE_VALUE (d_sym))
+	  {
+	    found = true;
+	    break;
+	  }
+
+      if (found)
+	continue;
+
+      tree attr = tree_cons (TREE_PURPOSE (sym),
+			     TREE_VALUE (sym),
+			     *attrs);
+      *attrs = attr;
+      attrs = &TREE_CHAIN (attr);
+
+      if (first)
+	{
+	  first = false;
+	  node = symtab_node::get (d);
+	}
+
+      if (!node)
+	continue;
+
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+      create_sym_alias_decl (d, id);
+    }
+}
+
 /* Return the type_info object for TYPE.  */
 
 tree
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index e535a34b96c..8ab5b450d9b 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -4142,6 +4142,39 @@ Function Attributes}, @ref{PowerPC Function Attributes},
 @ref{Nios II Function Attributes}, and @ref{S/390 Function Attributes}
 for details.
 
+@cindex @code{sym_alias} function attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} attribute causes @var{name} to be output as an
+alias to the definition.  For instance,
+
+@smallexample
+void f (uint64_t) __attribute__ ((__sym_alias__ ("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 for aliases,
+and may not be available on all targets.
+
 @cindex @code{symver} function attribute
 @item symver ("@var{name2}@@@var{nodename}")
 On ELF targets this attribute creates a symbol version.  The @var{name2} part
@@ -8070,6 +8103,10 @@ will be placed in new, unique sections.
 
 This additional functionality requires Binutils version 2.36 or later.
 
+@cindex @code{sym_alias} variable attribute
+@item sym_alias ("@var{name}")
+See @pxref{Common Function Attributes}.
+
 @cindex @code{uninitialized} variable attribute
 @item uninitialized
 This attribute, attached to a variable with automatic storage, means that
@@ -9165,6 +9202,21 @@ is not supported; that is to say, if a given scalar object can be accessed
 through distinct types that assign a different storage order to it, then the
 behavior is undefined.
 
+@cindex @code{sym_alias} type attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} type 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__ ((__sym_alias__ ("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.
+
 @cindex @code{transparent_union} type attribute
 @item transparent_union
 
diff --git a/gcc/symtab.cc b/gcc/symtab.cc
index 0470509a98d..0812088e59f 100644
--- a/gcc/symtab.cc
+++ b/gcc/symtab.cc
@@ -1943,6 +1943,42 @@ symtab_node::noninterposable_alias (symtab_node *node, void *data)
   return false;
 }
 
+/* Remap sym alias nodes recorded as aliasing REPLACED to alias
+   REPLACEMENT instead.  */
+
+void
+symtab_node::remap_sym_alias_target (tree replaced, tree replacement)
+{
+  if (!decl_in_symtab_p (replacement)
+      || !symtab_node::get (replacement))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (replaced))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      symtab_node *sym_node = symtab_node::get_for_asmname (id);
+
+      if (!sym_node)
+	{
+	  create_sym_alias_decl (replacement, id);
+	  continue;
+	}
+
+      gcc_assert (!sym_node->analyzed);
+      if (sym_node->alias_target != replaced)
+	continue;
+
+      sym_node->definition = 0;
+
+      if (VAR_P (replaced))
+	varpool_node::create_extra_name_alias (sym_node->decl, replacement);
+      else
+	cgraph_node::create_same_body_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/goacc/declare-1.c b/gcc/testsuite/c-c++-common/goacc/declare-1.c
index 46ee01b6759..f2842893318 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-1.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-1.c
@@ -113,11 +113,11 @@ f_2 (void)
   int va3;
 #pragma acc declare device_resident(va3)
 
-#ifndef __cplusplus
+#if 0
   /* TODO PR90868
 
-     C: "error: variable '[...]' used more than once with '#pragma acc declare'".  */
-#else
+     "error: variable '[...]' used more than once with '#pragma acc declare'".  */
+
   extern int ve0;
 #pragma acc declare create(ve0)
 
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-2.c b/gcc/testsuite/c-c++-common/goacc/declare-2.c
index e2e22be57e9..aec59b69754 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-2.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-2.c
@@ -137,25 +137,25 @@ void
 f_pr90868_2 (void)
 {
   extern int we0;
-#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" } */
 
   extern int we1;
-#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" } */
 
   extern int *we2;
-#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" } */
 
   extern int we3;
-#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" } */
 
   extern int we4;
-#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" } */
 
   extern int we5;
-#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" } */
  
   extern int we6;
-#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" } */
 }
 
 
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
new file mode 100644
index 00000000000..61af50cb552
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
@@ -0,0 +1,39 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+int var_a = 1;
+
+void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+
+void
+foo_a ()
+{
+}
+
+
+int var_b;
+extern int var_b __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+
+void
+foo_b ()
+{
+}
+
+void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+
+
+int var_c __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+
+void __attribute__ ((__sym_alias__ ("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-sym-alias-2.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
new file mode 100644
index 00000000000..6f0d55ca42c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+struct s
+{
+  int mem __attribute__ ((__sym_alias__ ("MEMFOO"))); /* { dg-warning "attribute ignored" } */
+};
+
+void foo()
+{
+  extern void bar () __attribute__ ((__sym_alias__ ("FOOBAR")));
+  int var __attribute__ ((__sym_alias__ ("FOOVAR"))); /* { dg-warning "attribute ignored" } */
+}
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
new file mode 100644
index 00000000000..0052485a30a
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
@@ -0,0 +1,41 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int var_a = 1;
+
+void
+foo_a ()
+{
+  extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+}
+
+#if 0 // __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__ ((__sym_alias__ ("FOOVAR_B")));
+}
+
+int var_b;
+
+void __attribute__ ((__sym_alias__ ("FOOBAR_C")))
+foo_c ()
+{
+  void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+  /* Another sym for var_b.  */
+  extern int var_b __attribute__ ((__sym_alias__ ("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-sym-alias-4.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
new file mode 100644
index 00000000000..2beafa46379
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-require-alias "" } */
+
+int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A"))) = 42;
+
+int __attribute__ ((__sym_alias__ ("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-sym-alias-1.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
new file mode 100644
index 00000000000..579eda1a473
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
@@ -0,0 +1,72 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+class __attribute__ ((__sym_alias__ ("FOOCLS_A"),
+		      __sym_alias__ ("FOOCLS_A_Dupe"))) foo {
+  static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  void __attribute__ ((__sym_alias__ ("FOOBAR_A"))) bar ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+int foo::var = 1;
+
+foo::foo () {}
+
+void foo::bar () {}
+
+namespace b {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_B"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_B"))) foo ();
+    void __attribute__ ((__sym_alias__ ("FOOBAR_B"))) bar () {}
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_B"))) ~foo() {}
+  };
+
+  int foo::var = 2;
+
+  foo::foo () {
+    void (foo::*pbar)() = &foo::bar;
+  }
+}
+
+namespace c {
+  namespace cd {
+    class __attribute__ ((__sym_alias__ ("FOOCLS_C"))) foo {
+      static int var __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+      __attribute__ ((__sym_alias__ ("FOOCTR_C"))) foo () {
+	void (foo::*pbar)() = &foo::bar;
+      }
+      void __attribute__ ((__sym_alias__ ("FOOBAR_C"))) bar () {}
+      virtual __attribute__ ((__sym_alias__ ("FOODTR_C"))) ~foo() {}
+    };
+
+    int foo::var = 3;
+  }
+}
+
+/* { dg-final { scan-assembler "FOOCLS_A" } } */
+/* { dg-final { scan-assembler "FOOCLS_A_Dupe" } } */
+/* { 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-sym-alias-2.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
new file mode 100644
index 00000000000..6facfcc2cd9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+namespace {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_A"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo ();
+    void __attribute__ ((__sym_alias__ ("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-sym-alias-3.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
new file mode 100644
index 00000000000..42c7288ad4a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
@@ -0,0 +1,83 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+// sym can be applied to template function explicit instantiations.
+
+template <typename T>
+void
+fn(T) {
+};
+
+template void __attribute__ ((__sym_alias__ ("FOOFUN_UINT"))) fn<>(unsigned int);
+template void __attribute__ ((__sym_alias__ ("FOOFUN_LONG"))) fn<>(long);
+
+template<> void __attribute__ ((__sym_alias__ ("FOOFUN_CHAR"))) fn<>(char) {}
+
+
+template <typename T = void>
+struct
+foo {
+  virtual ~foo() {}
+
+  virtual void virtfun() {}
+
+  static void stfun() {}
+  void inlfun() {}
+};
+
+// Explicitly instantiate members before the enclosing class.
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_CHAR_VIRT"))) foo<char>::virtfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_CHAR_TI"))) foo<char>;
+
+// Though they're only output if the enclosing class is.
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_LONG_VIRT"))) foo<long>::virtfun();
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_LONG_TI_X"))) foo<long>;
+
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_VOID_ST"))) foo<void>::stfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_VOID_TI"))) foo<>;
+
+
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_X"))) foo<short>;
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_ST"))) foo<short>::stfun();
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_INL"))) foo<short>::inlfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_D"))) foo<short>;
+
+// Explicit specializations work too.
+
+template <>
+struct  __attribute__ ((__sym_alias__ ("FOOCLS_INT_TI")))
+foo<int>
+{
+  virtual ~foo() {}
+  virtual void __attribute__ ((__sym_alias__ ("FOOCLS_INT_VIRT"))) virtfun() {}
+};
+
+/* { dg-final { scan-assembler "FOOFUN_UINT" } } */
+/* { dg-final { scan-assembler "FOOFUN_LONG" } } */
+/* { dg-final { scan-assembler "FOOFUN_CHAR" } } */
+
+/* { dg-final { scan-assembler "FOOCLS_VOID_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_VOID_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_X" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_INL" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_D" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_TI_X" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_VIRT" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
new file mode 100644
index 00000000000..59b779de35d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+template <typename T = void>
+class
+__attribute__ ((__sym_alias__ ("FOOCLS")))
+foo // { dg-error "duplicate|already" }
+{
+  virtual ~foo() {}
+
+  template <typename U>
+  void
+    __attribute__ ((__sym_alias__ ("FOOTMF")))
+    tmemfun () {} // { dg-error "duplicate|already" }
+};
+
+template <typename T>
+void
+__attribute__ ((__sym_alias__ ("FOOTFN")))
+fn(T) { // { dg-error "duplicate|already" }
+};
+
+template class foo<>;
+template class foo<int>;
+template void foo<>::tmemfun<void>();
+template void foo<int>::tmemfun<void>();
+template void fn<>(int);
+template void fn<>(long);
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
new file mode 100644
index 00000000000..45d59b953c3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
@@ -0,0 +1,14 @@
+/* { dg-do compile { target c++11 } } */
+/* { dg-require-alias "" } */
+
+struct foo {
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+foo::foo () {}
+
+// Make sure the inherited cdtors don't duplicate the syms.
+struct bar : foo {
+  using foo::foo;
+};
diff --git a/gcc/varpool.cc b/gcc/varpool.cc
index e7b51b15e4a..33649906d1d 100644
--- a/gcc/varpool.cc
+++ b/gcc/varpool.cc
@@ -163,6 +163,9 @@ varpool_node::get_create (tree decl)
     }
 
   node->register_symbol ();
+
+  create_sym_alias_decls (decl);
+
   return node;
 }

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [gcc(refs/users/aoliva/heads/testme)] Introduce attribute sym_alias
@ 2024-05-24 13:38 Alexandre Oliva
  0 siblings, 0 replies; 33+ messages in thread
From: Alexandre Oliva @ 2024-05-24 13:38 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:3612317409b4c0ec201d7f536638015872ffbc8d

commit 3612317409b4c0ec201d7f536638015872ffbc8d
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Thu Dec 14 03:21:21 2023 -0300

    Introduce attribute sym_alias
    
    This patch introduces an attribute to add extra asm names (aliases)
    for a decl 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, and
    also to local static variables.  In C++, it can also be attached to
    class types, namespace-scoped variables and functions, static data
    members, member functions, explicit instantiations and specializations
    of template functions, members and classes.
    
    When applied to constructors or destructor, additional sym aliases
    with _Base and _Del suffixes are defined for variants other than
    complete-object ones.  This changes the assumption that clones always
    carry the same attributes as their abstract declarations, so there is
    now a function to adjust them.
    
    C++ also had a bug in which attributes from local extern declarations
    failed to be propagated to a preexisting corresponding
    namespace-scoped decl.  I've fixed that, and adjusted acc tests that
    distinguished between C and C++ in this regard.
    
    Applying the attribute to class types is only valid in C++, and the
    effect is to attach the alias to the RTTI object associated with the
    class type.
    
    
    for  gcc/ChangeLog
    
            * attribs.cc: Include cgraph.h.
            (decl_attributes): Allow late introduction of sym_alias in
            types.
            (create_sym_alias_decl, create_sym_alias_decls): New.
            * attribs.h: Declare them.
            (FOR_EACH_SYM_ALIAS): New macro.
            * cgraph.cc (cgraph_node::create): Create sym_alias decls.
            * varpool.cc (varpool_node::get_create): Create sym_alias
            decls.
            * cgraph.h (symtab_node::remap_sym_alias_target): New.
            * symtab.cc (symtab_node::remap_sym_alias_target): Define.
            (symbol_table::insert_to_assembler_name_hash): Check for
            symbol name clashes.
            (symtab_node::noninterposable_alias): Drop sym_alias
            attributes.
            * cgraphunit.cc (cgraph_node::analyze): Create alias_target
            node if needed.
            (analyze_functions): Fixup visibility of implicit alias only
            after its node is analyzed.
            * doc/extend.texi (sym_alias): Document for variables,
            functions and types.
    
    for  gcc/ada/ChangeLog
    
            * doc/gnat_rm/interfacing_to_other_languages.rst: Mention
            attribute sym_alias to give RTTI symbols mnemonic names.
            * doc/gnat_ugn/the_gnat_compilation_model.rst: Mention
            aliases.  Fix incorrect ref to C1 ctor variant.
    
    for  gcc/c-family/ChangeLog
    
            * c-ada-spec.cc (pp_asm_name): Use first sym_alias if
            available.
            * c-attribs.cc (handle_sym_alias_attribute): New.
            (c_common_attribute_table): Add sym_alias.
            (handle_copy_attribute): Do not copy sym_alias attribute.
    
    for  gcc/c/ChangeLog
    
            * c-decl.cc (duplicate_decls): Remap sym_alias target.
            (finish_decl): Create varpool_node for local static
            variables.
    
    for  gcc/cp/ChangeLog
    
            * class.cc (adjust_clone_attributes): New.
            (copy_fndecl_with_name, build_clone): Call it.
            * cp-tree.h (adjust_clone_attributes): Declare.
            (update_sym_alias_interface): Declare.
            (update_tinfo_sym_alias): Declare.
            * decl.cc (duplicate_decls): Remap sym_alias target.
            Adjust clone attributes.
            (grokfndecl): Tentatively create sym_alias decls after
            adding attributes in e.g. a template member function explicit
            instantiation.
            * decl2.cc (cplus_decl_attributes): Update tinfo sym_alias.
            (copy_interface, update_sym_alias_interface): New.
            (determine_visibility): Update sym_alias interface.
            (tentative_decl_linkage, import_export_decl): Likewise.
            * name-lookup.cc: Include target.h and cgraph.h.
            (push_local_extern_decl_alias): Merge attributes with
            namespace-scoped decl, and drop duplicate sym_alias.
            * optimize.cc (maybe_clone_body): Re-adjust attributes after
            cloning them.  Update sym_alias interface.
            * rtti.cc: Include attribs.h and cgraph.h.
            (get_tinfo_decl): Copy sym_alias attributes from type to tinfo
            decl.  Create sym_alias decls.
            (update_tinfo_sym_alias): New.
    
    for  gcc/testsuite/ChangeLog
    
            * c-c++-common/goacc/declare-1.c: Adjust.
            * c-c++-common/goacc/declare-2.c: Adjust.
            * c-c++-common/torture/attr-sym-alias-1.c: New.
            * c-c++-common/torture/attr-sym-alias-2.c: New.
            * c-c++-common/torture/attr-sym-alias-3.c: New.
            * c-c++-common/torture/attr-sym-alias-4.c: New.
            * c-c++-common/torture/attr-sym-alias-5.c: New.
            * g++.dg/torture/attr-sym-alias-1.C: New.
            * g++.dg/torture/attr-sym-alias-2.C: New.
            * g++.dg/torture/attr-sym-alias-3.C: New.
            * g++.dg/torture/attr-sym-alias-4.C: New.
            * g++.dg/torture/attr-sym-alias-5.C: New.

Diff:
---
 .../doc/gnat_rm/interfacing_to_other_languages.rst |  6 ++
 .../doc/gnat_ugn/the_gnat_compilation_model.rst    | 10 ++-
 gcc/attribs.cc                                     | 74 +++++++++++++++-
 gcc/attribs.h                                      |  7 ++
 gcc/c-family/c-ada-spec.cc                         |  7 ++
 gcc/c-family/c-attribs.cc                          | 33 +++++++-
 gcc/c/c-decl.cc                                    |  7 ++
 gcc/cgraph.cc                                      |  2 +
 gcc/cgraph.h                                       |  4 +
 gcc/cgraphunit.cc                                  |  2 +-
 gcc/cp/class.cc                                    | 64 ++++++++++++++
 gcc/cp/cp-tree.h                                   |  4 +
 gcc/cp/decl.cc                                     |  4 +
 gcc/cp/decl2.cc                                    | 53 ++++++++++++
 gcc/cp/name-lookup.cc                              | 11 +++
 gcc/cp/optimize.cc                                 |  3 +
 gcc/cp/rtti.cc                                     | 71 +++++++++++++++-
 gcc/doc/extend.texi                                | 62 ++++++++++++++
 gcc/symtab.cc                                      | 62 ++++++++++++++
 .../c-c++-common/torture/attr-sym-alias-1.c        | 75 ++++++++++++++++
 .../c-c++-common/torture/attr-sym-alias-2.c        | 99 ++++++++++++++++++++++
 .../c-c++-common/torture/attr-sym-alias-3.c        | 34 ++++++++
 .../c-c++-common/torture/attr-sym-alias-4.c        | 28 ++++++
 .../c-c++-common/torture/attr-sym-alias-5.c        | 54 ++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C    | 89 +++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C    | 26 ++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C    | 83 ++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C    | 28 ++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C    | 14 +++
 gcc/varpool.cc                                     |  3 +
 30 files changed, 1010 insertions(+), 9 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 ad0be511d48..b8de16ebf7c 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 ``sym_alias`` 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 fd15459203a..20045698962 100644
--- a/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
+++ b/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
@@ -4274,6 +4274,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__ ((__sym_alias__ ("Ctor_For_Animal")))
         Animal() {Age_Count = 0;};
       private:
         int Age_Count;
@@ -4306,6 +4307,7 @@ both Carnivore and Domestic, that is:
         virtual int  Number_Of_Teeth ();
         virtual void Set_Owner (char* Name);
 
+        __attribute__ ((__sym_alias__ ("Ctor_For_Dog"))) // mnemonic alias
         Dog(); // Constructor
       private:
         int  Tooth_Count;
@@ -4344,7 +4346,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;
@@ -4360,7 +4363,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,7 +4385,8 @@ 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.
+mangled name (or an alias) 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.cc b/gcc/attribs.cc
index 3ab0b0fd87a..814d8a00050 100644
--- a/gcc/attribs.cc
+++ b/gcc/attribs.cc
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "target.h"
 #include "tree.h"
+#include "cgraph.h"
 #include "stringpool.h"
 #include "diagnostic-core.h"
 #include "attribs.h"
@@ -839,7 +840,8 @@ decl_attributes (tree *node, tree attributes, int flags,
 
       if (TYPE_P (*anode)
 	  && (flags & (int) ATTR_FLAG_TYPE_IN_PLACE)
-	  && COMPLETE_TYPE_P (*anode))
+	  && COMPLETE_TYPE_P (*anode)
+	  && !is_attribute_p ("sym_alias", name))
 	{
 	  warning (OPT_Wattributes, "type attributes ignored after type is already defined");
 	  continue;
@@ -2682,6 +2684,76 @@ attr_access::array_as_string (tree type) const
   return typstr;
 }
 
+/* Create a sym attribute for DECL to be visible with linkage name ID.  */
+
+tree
+create_sym_alias_decl (tree decl, tree id)
+{
+  const char *attr_str = "sym_alias";
+
+  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;
+
+      tree attr_name = get_identifier (attr_str);
+      error_at (DECL_SOURCE_LOCATION (decl),
+		"duplicate symbol name %qE in %qE attribute of %qD",
+		id, attr_name, decl);
+      inform (DECL_SOURCE_LOCATION (sym_node->decl),
+	      "already used by %qD", sym_node->decl);
+    }
+
+  tree clone = copy_node (decl);
+  DECL_ATTRIBUTES (clone) = remove_attribute (attr_str,
+					      DECL_ATTRIBUTES (decl));
+  /* Mark it as a "sym alias" for decl, an internal attribute that
+     enables symbol_table::insert_to_assembler_name_hash can recognize
+     sym_alias decls.  */
+  DECL_ATTRIBUTES (clone) = tree_cons (get_identifier ("sym alias"),
+				       decl, DECL_ATTRIBUTES (clone));
+  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;
+
+  symtab_node *node;
+  if (VAR_P (clone))
+    // DECL_READ_P (clone) = 1;
+    // node = varpool_node::create_extra_name_alias (clone, decl);
+    node = varpool_node::create_alias (clone, decl);
+  else
+    node = cgraph_node::create_same_body_alias (clone, decl);
+    // node = cgraph_node::create_alias (clone, decl);
+  if (symtab_node *dnode = symtab_node::get_create (decl))
+    node->copy_visibility_from (dnode);
+
+  return clone;
+}
+
+/* Create decls for all sym aliases requested in DECL's attributes.  */
+
+void
+create_sym_alias_decls (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl)
+      || DECL_ABSTRACT_P (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      create_sym_alias_decl (decl, id);
+    }
+}
+
 #if CHECKING_P
 
 namespace selftest
diff --git a/gcc/attribs.h b/gcc/attribs.h
index 99c45750d59..3aac1cb2d03 100644
--- a/gcc/attribs.h
+++ b/gcc/attribs.h
@@ -405,4 +405,11 @@ extern void init_attr_rdwr_indices (rdwr_map *, tree);
 extern attr_access *get_parm_access (rdwr_map &, tree,
 				     tree = current_function_decl);
 
+extern tree create_sym_alias_decl (tree, tree);
+extern void create_sym_alias_decls (tree);
+
+#define FOR_EACH_SYM_ALIAS(sym, attrs)					\
+  for (tree sym = lookup_attribute ("sym_alias", (attrs));		\
+       sym; sym = lookup_attribute ("sym_alias", TREE_CHAIN (sym)))
+
 #endif // GCC_ATTRIBS_H
diff --git a/gcc/c-family/c-ada-spec.cc b/gcc/c-family/c-ada-spec.cc
index 8f0849bd427..4d04fcaaa14 100644
--- a/gcc/c-family/c-ada-spec.cc
+++ b/gcc/c-family/c-ada-spec.cc
@@ -1433,6 +1433,13 @@ pp_ada_tree_identifier (pretty_printer *buffer, tree node, tree type,
 static void
 pp_asm_name (pretty_printer *buffer, tree t)
 {
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (t))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.cc b/gcc/c-family/c-attribs.cc
index 04e39b41bdf..ca567b2ad08 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -110,7 +110,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_sym_alias_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,
@@ -422,6 +423,8 @@ const struct attribute_spec c_common_gnu_attributes[] =
 			      handle_alias_attribute, NULL },
   { "weakref",                0, 1, true,  false, false, false,
 			      handle_weakref_attribute, NULL },
+  { "sym_alias",              1, 1, false,  false, false, false,
+			      handle_sym_alias_attribute, NULL },
   { "no_instrument_function", 0, 0, true,  false, false, false,
 			      handle_no_instrument_function_attribute,
 			      NULL },
@@ -3106,7 +3109,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
@@ -3116,7 +3119,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
@@ -3126,6 +3129,29 @@ handle_alias_attribute (tree *node, tree name, tree args,
   return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs);
 }
 
+/* Handle a "sym_alias" attribute; arguments as in struct
+   attribute_spec.handler.  */
+
+static tree
+handle_sym_alias_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 (decl_in_symtab_p (node))
+    *no_add_attrs = false;
+  else if (TYPE_P (node) && c_dialect_cxx ())
+    *no_add_attrs = false;
+  else
+    return error_mark_node;
+
+  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.  */
 
@@ -3257,6 +3283,7 @@ handle_copy_attribute (tree *node, tree name, tree args,
 	      || is_attribute_p ("visibility", atname)
 	      || is_attribute_p ("weak", atname)
 	      || is_attribute_p ("weakref", atname)
+	      || is_attribute_p ("sym_alias", atname)
 	      || is_attribute_p ("target_clones", atname))
 	    continue;
 
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index b691b91b3db..34f80cfc868 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -3184,6 +3184,8 @@ duplicate_decls (tree newdecl, tree olddecl)
 
   merge_decls (newdecl, olddecl, newtype, oldtype);
 
+  symtab_node::remap_sym_alias_target (newdecl, olddecl);
+
   /* The NEWDECL will no longer be needed.
 
      Before releasing the node, be sure to remove function from symbol
@@ -5991,6 +5993,11 @@ finish_decl (tree decl, location_t init_loc, tree init,
 	    set_user_assembler_name (decl, asmspec);
 	}
 
+      /* Give attribute sym_alias a chance to make the symbol name
+	 available for aliasing, even for a static local variable.  */
+      if (VAR_P (decl) && !DECL_EXTERNAL (decl) && is_global_var (decl))
+	varpool_node::get_create (decl);
+
       if (DECL_FILE_SCOPE_P (decl))
 	{
 	  if (DECL_INITIAL (decl) == NULL_TREE
diff --git a/gcc/cgraph.cc b/gcc/cgraph.cc
index 473d8410bc9..5ed86751be2 100644
--- a/gcc/cgraph.cc
+++ b/gcc/cgraph.cc
@@ -525,6 +525,8 @@ cgraph_node::create (tree decl)
   node->register_symbol ();
   maybe_record_nested_function (node);
 
+  create_sym_alias_decls (decl);
+
   return node;
 }
 
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index a8c3224802c..b8f2934f735 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -327,6 +327,10 @@ public:
   /* Return DECL that alias is aliasing.  */
   inline tree get_alias_target_tree ();
 
+  /* Remap sym alias nodes recorded as aliasing REPLACED to alias REPLACEMENT
+     instead.  */
+  static void remap_sym_alias_target (tree replaced, tree replacement);
+
   /* Set section for symbol and its aliases.  */
   void set_section (const char *section);
 
diff --git a/gcc/cgraphunit.cc b/gcc/cgraphunit.cc
index 2bd0289ffba..a27c856e9a5 100644
--- a/gcc/cgraphunit.cc
+++ b/gcc/cgraphunit.cc
@@ -1179,7 +1179,7 @@ analyze_functions (bool first_time)
      C++ FE is confused about the COMDAT groups being right.  */
   if (symtab->cpp_implicit_aliases_done)
     FOR_EACH_SYMBOL (node)
-      if (node->cpp_implicit_alias)
+      if (node->cpp_implicit_alias && node->analyzed)
 	  node->fixup_same_cpp_alias_visibility (node->get_alias_target ());
   build_type_inheritance_graph ();
 
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index 0ce361eb88e..64b31cbc81b 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -5121,6 +5121,68 @@ check_methods (tree t)
     }
 }
 
+/* Adjust sym alias name for CLONE, cloned from FN and named NAME,
+   if it is a cdtor, and drop the sym alias from other clones.  */
+
+void
+adjust_clone_attributes (tree fn, tree clone, tree name, bool skip_copy_p)
+{
+  if (IDENTIFIER_CDTOR_P (name))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	{
+	  found = true;
+	  break;
+	}
+
+      if (found
+	  && (name == complete_ctor_identifier
+	      || name == complete_dtor_identifier))
+	{
+	  /* Reuse the sym alias decls created for the primary cdtor
+	     decl.  */
+	  symtab_node::remap_sym_alias_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);
+
+	  if (!skip_copy_p)
+	    DECL_ATTRIBUTES (clone) = copy_list (DECL_ATTRIBUTES (clone));
+
+	  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	    {
+	      /* We need to copy this even with skip_copy_p, because
+		 even then copying was shallow.  */
+	      TREE_VALUE (sym) = copy_list (TREE_VALUE (sym));
+	      /* Append suf to the sym alias name.  */
+	      tree str = TREE_VALUE (TREE_VALUE (sym));
+	      char *symname = concat (TREE_STRING_POINTER (str), suf, NULL);
+	      str = build_string (TREE_STRING_LENGTH (str) + xlen, symname);
+	      TREE_VALUE (TREE_VALUE (sym)) = str;
+	      free (symname);
+	    }
+
+	  if (symtab_node::get (clone))
+	    create_sym_alias_decls (clone);
+	}
+    }
+  else
+    DECL_ATTRIBUTES (clone)
+      = remove_attribute ("sym_alias", DECL_ATTRIBUTES (clone));
+}
+
 /* FN is constructor, destructor or operator function.  Clone the
    declaration to create a NAME'd variant.  NEED_VTT_PARM_P and
    OMIT_INHERITED_PARMS_P are relevant if it's a cdtor.  */
@@ -5290,6 +5352,8 @@ build_clone (tree fn, tree name, bool need_vtt_parm_p,
   DECL_CHAIN (clone) = DECL_CHAIN (fn);
   DECL_CHAIN (fn) = clone;
 
+  adjust_clone_attributes (fn, clone, name);
+
   return clone;
 }
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 7ae5b876735..77b1c5b7e33 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5849,6 +5849,8 @@ struct GTY((for_user)) spec_entry
 
 extern int current_class_depth;
 
+void adjust_clone_attributes (tree fn, tree clone, tree name, bool = false);
+
 /* in decl.cc */
 
 /* An array of static vars & fns.  */
@@ -7097,6 +7099,7 @@ extern void do_push_parm_decls			(tree, tree, tree *);
 extern tree do_aggregate_paren_init		(tree, tree);
 
 /* in decl2.cc */
+extern void update_sym_alias_interface		(tree);
 extern void record_mangling			(tree, bool);
 extern void overwrite_mangling			(tree, tree);
 extern void note_mangling_alias			(tree, tree);
@@ -7688,6 +7691,7 @@ extern bool emit_tinfo_decl			(tree);
 extern unsigned get_pseudo_tinfo_index		(tree);
 extern tree get_pseudo_tinfo_type		(unsigned);
 extern tree build_if_nonnull			(tree, tree, tsubst_flags_t);
+extern void update_tinfo_sym_alias		(tree);
 
 /* in search.cc */
 extern tree get_parent_with_private_access 	(tree decl, tree binfo);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index a992d54dc8f..faa4ee73be7 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -3307,6 +3307,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
 	      && TREE_STATIC (olddecl))))
     make_decl_rtl (olddecl);
 
+  symtab_node::remap_sym_alias_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.
@@ -3330,6 +3332,7 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
       FOR_EACH_CLONE (clone, olddecl)
 	{
 	  DECL_ATTRIBUTES (clone) = DECL_ATTRIBUTES (olddecl);
+	  adjust_clone_attributes (olddecl, clone, DECL_NAME (clone));
 	  DECL_PRESERVE_P (clone) |= DECL_PRESERVE_P (olddecl);
 	}
     }
@@ -11023,6 +11026,7 @@ grokfndecl (tree ctype,
     {
       cplus_decl_attributes (&decl, *attrlist, 0);
       *attrlist = NULL_TREE;
+      create_sym_alias_decls (decl);
     }
 
   if (DECL_HAS_CONTRACTS_P (decl))
diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
index 7baff46a192..b9e8aef4852 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -1887,6 +1887,9 @@ cplus_decl_attributes (tree *decl, tree attributes, int flags)
   if (late_attrs)
     save_template_attributes (late_attrs, decl, flags);
 
+  if (TYPE_P (*decl) && attributes)
+    update_tinfo_sym_alias (*decl);
+
   /* Propagate deprecation out to the template.  */
   if (TREE_DEPRECATED (*decl))
     if (tree ti = get_template_info (*decl))
@@ -2243,6 +2246,50 @@ 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);
+  if (symtab_node *dest_node = symtab_node::get (dest))
+    if (symtab_node *decl_node = symtab_node::get (decl))
+      decl_node->copy_visibility_from (dest_node);
+}
+
+/* Propagate linkage changes to sym aliases.  */
+
+void
+update_sym_alias_interface (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.  */
 
@@ -3078,6 +3125,8 @@ determine_visibility (tree decl)
        translation unit, we can make the type internal.  */
     constrain_visibility (decl, VISIBILITY_ANON, false);
 
+  update_sym_alias_interface (decl);
+
   /* If visibility changed and DECL already has DECL_RTL, ensure
      symbol flags are updated.  */
   if ((DECL_VISIBILITY (decl) != orig_visibility
@@ -3347,6 +3396,8 @@ tentative_decl_linkage (tree decl)
       else if (VAR_P (decl))
 	maybe_commonize_var (decl);
     }
+
+  update_sym_alias_interface (decl);
 }
 
 /* For a polymorphic class type CTYPE, whether its vtables are emitted in a
@@ -3611,6 +3662,8 @@ import_export_decl (tree decl)
     }
 
   DECL_INTERFACE_KNOWN (decl) = 1;
+
+  update_sym_alias_interface (decl);
 }
 
 /* Return an expression that performs the destruction of DECL, which
diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc
index 78f08acffaa..f89a4e6baff 100644
--- a/gcc/cp/name-lookup.cc
+++ b/gcc/cp/name-lookup.cc
@@ -22,9 +22,11 @@ along with GCC; see the file COPYING3.  If not see
 #define INCLUDE_MEMORY
 #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"
@@ -3692,6 +3694,15 @@ push_local_extern_decl_alias (tree decl)
 	  /* Adjust visibility.  */
 	  determine_visibility (alias);
 	}
+      else if (DECL_P (alias))
+	DECL_ATTRIBUTES (alias)
+	  = targetm.merge_decl_attributes (alias, decl);
+      if (DECL_P (alias))
+	{
+	  symtab_node::remap_sym_alias_target (decl, alias);
+	  DECL_ATTRIBUTES (decl)
+	    = remove_attribute ("sym_alias", DECL_ATTRIBUTES (alias));
+	}
     }
 
   retrofit_lang_decl (decl);
diff --git a/gcc/cp/optimize.cc b/gcc/cp/optimize.cc
index b8791d8a963..c7c8f4f1068 100644
--- a/gcc/cp/optimize.cc
+++ b/gcc/cp/optimize.cc
@@ -526,9 +526,12 @@ maybe_clone_body (tree fn)
       DECL_VISIBILITY_SPECIFIED (clone) = DECL_VISIBILITY_SPECIFIED (fn);
       DECL_DLLIMPORT_P (clone) = DECL_DLLIMPORT_P (fn);
       DECL_ATTRIBUTES (clone) = clone_attrs (DECL_ATTRIBUTES (fn));
+      adjust_clone_attributes (fn, clone, DECL_NAME (clone), true);
       DECL_DISREGARD_INLINE_LIMITS (clone) = DECL_DISREGARD_INLINE_LIMITS (fn);
       set_decl_section_name (clone, fn);
 
+      update_sym_alias_interface (clone);
+
       /* Adjust the parameter names and locations.  */
       parm = DECL_ARGUMENTS (fn);
       clone_parm = DECL_ARGUMENTS (clone);
diff --git a/gcc/cp/rtti.cc b/gcc/cp/rtti.cc
index ed69606f4dd..915990887f5 100644
--- a/gcc/cp/rtti.cc
+++ b/gcc/cp/rtti.cc
@@ -28,8 +28,10 @@ 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"
+#include "cgraph.h"
 
 /* C++ returns type information to the user in struct type_info
    objects. We also use type information to implement dynamic_cast and
@@ -479,8 +481,13 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
 	  = build_tree_list (get_identifier ("non overlapping"),
 			     NULL_TREE);
       else
+	/* Share the non overlapping attribute, without assuming it's
+	   the only attribute, but assuming it's the last if it's
+	   present.  There may be sym aliases too, and those are not
+	   to be shared.  */
 	DECL_ATTRIBUTES (d)
-	  = DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]);
+	  = lookup_attribute ("non overlapping",
+			      DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]));
 
       /* Mark the variable as undefined -- but remember that we can
 	 define it later if we need to do so.  */
@@ -492,6 +499,16 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
       if (CLASS_TYPE_P (type))
 	CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type)) = d;
 
+      /* Copy sym alias attributes from the type to the rtti obj decl.  */
+      tree *attrs = &DECL_ATTRIBUTES (d);
+      FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+	{
+	  tree attr = tree_cons (TREE_PURPOSE (sym), TREE_VALUE (sym), *attrs);
+	  *attrs = attr;
+	  attrs = &TREE_CHAIN (attr);
+	}
+      create_sym_alias_decls (d);
+
       /* Add decl to the global array of tinfo decls.  */
       vec_safe_push (unemitted_tinfo_decls, d);
     }
@@ -499,6 +516,58 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
   return d;
 }
 
+/* After modifying the attributes of TYPE, check whether tinfo was
+   already created and, if so, add to it any sym alias attributes
+   that were not already present.  */
+
+void
+update_tinfo_sym_alias (tree type)
+{
+  if (!TYPE_SIZE (type) || !CLASS_TYPE_P (type))
+    return;
+
+  tree d = CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type));
+  if (!d)
+    return;
+
+  bool first = true;
+  symtab_node *node = NULL;
+
+  tree *attrs = &DECL_ATTRIBUTES (d);
+  FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (d_sym, *attrs)
+	if (TREE_VALUE (sym) == TREE_VALUE (d_sym))
+	  {
+	    found = true;
+	    break;
+	  }
+
+      if (found)
+	continue;
+
+      tree attr = tree_cons (TREE_PURPOSE (sym),
+			     TREE_VALUE (sym),
+			     *attrs);
+      *attrs = attr;
+      attrs = &TREE_CHAIN (attr);
+
+      if (first)
+	{
+	  first = false;
+	  node = symtab_node::get (d);
+	}
+
+      if (!node)
+	continue;
+
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+      create_sym_alias_decl (d, id);
+    }
+}
+
 /* Return the type_info object for TYPE.  */
 
 tree
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 8786249fb6f..806a09977a1 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -4136,6 +4136,49 @@ This attribute adds stack protection code to the function if
 flags @option{-fstack-protector}, @option{-fstack-protector-strong}
 or @option{-fstack-protector-explicit} are set.
 
+@cindex @code{sym_alias} function attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} attribute causes @var{name} to be output as an
+alias to the function, with the same linkage and visibility as the
+function, when the function definition is output, provided that the
+function could be an alias target.  For instance,
+
+@smallexample
+void f (uint64_t) __attribute__ ((__sym_alias__ ("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},
+with the same linkage and visibility.  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.
+
+Aliases introduced with this attribute, such as @samp{f_u64} in the
+example above, are assembly symbol names: they do not undergo C++ name
+mangling, and are not made visible in any scope in the source language.
+They can, however, be named as alias targets, as long as their
+definition is output.  Naming a @samp{sym_alias} as an alias target will
+@emph{not} cause a definition to be output if it otherwise wouldn't.
+This may affect inline functions, C++ template instantiations, and other
+synthesized definitions that would have to be (synthesized and) compiled
+in order for the @samp{sym_alias} to be considered as a potential alias
+target.
+
+This attribute requires assembler and object file support for aliases,
+and may not be available on all targets.
+
 @cindex @code{symver} function attribute
 @item symver ("@var{name2}@@@var{nodename}")
 On ELF targets this attribute creates a symbol version.  The @var{name2} part
@@ -8155,6 +8198,10 @@ will be placed in new, unique sections.
 
 This additional functionality requires Binutils version 2.36 or later.
 
+@cindex @code{sym_alias} variable attribute
+@item sym_alias ("@var{name}")
+See @pxref{Common Function Attributes}.
+
 @cindex @code{uninitialized} variable attribute
 @item uninitialized
 This attribute, attached to a variable with automatic storage, means that
@@ -9457,6 +9504,21 @@ __attribute__ ((noipa)) void flop (void) @{@}
 inline __attribute__ ((noclone, always_inline)) void flip (void) @{@}
 @end smallexample
 
+@cindex @code{sym_alias} type attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} type 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__ ((__sym_alias__ ("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.
+
 @cindex @code{transparent_union} type attribute
 @item transparent_union
 
diff --git a/gcc/symtab.cc b/gcc/symtab.cc
index 3b018ab3ea2..b79e68231e4 100644
--- a/gcc/symtab.cc
+++ b/gcc/symtab.cc
@@ -187,6 +187,22 @@ symbol_table::insert_to_assembler_name_hash (symtab_node *node,
 	(*aslot)->previous_sharing_asm_name = node;
       *aslot = node;
 
+      /* Check for sym_alias name clashes.  In create_sym_alias_decl,
+	 we check for a preexisting definition using the same
+	 assembler_name, so here we check for a previously-defined
+	 sym_name, marked with a "sym name" pseudo-attribute that
+	 points back at the declaration for which it was created.  */
+      if (symtab_node *sym_node = node->next_sharing_asm_name)
+	if (lookup_attribute ("sym alias",
+			      DECL_ATTRIBUTES (sym_node->decl)))
+	  {
+	    error_at (DECL_SOURCE_LOCATION (node->decl),
+		      "duplicate symbol name %qE", decl);
+	    inform (DECL_SOURCE_LOCATION (sym_node->decl),
+		    "already used by %qD in a %qE attribute",
+		    sym_node->decl, get_identifier ("sym_alias"));
+	  }
+
       /* Update also possible inline clones sharing a decl.  */
       cnode = dyn_cast <cgraph_node *> (node);
       if (cnode && cnode->clones && with_clones)
@@ -2035,6 +2051,46 @@ symtab_node::noninterposable_alias (symtab_node *node, void *data)
   return false;
 }
 
+/* Remap sym alias nodes recorded as aliasing REPLACED to alias
+   REPLACEMENT instead.  */
+
+void
+symtab_node::remap_sym_alias_target (tree replaced, tree replacement)
+{
+  symtab_node *repl_node = NULL;
+  if (!decl_in_symtab_p (replacement)
+      || !(repl_node = symtab_node::get (replacement)))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (replaced))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      symtab_node *sym_node = symtab_node::get_for_asmname (id);
+
+      if (!sym_node)
+	{
+	  create_sym_alias_decl (replacement, id);
+	  continue;
+	}
+
+      gcc_assert (!sym_node->analyzed);
+      if (sym_node->alias_target != replaced)
+	continue;
+
+      // sym_node->definition = 0;
+
+      if (VAR_P (replaced))
+	// varpool_node::create_extra_name_alias (sym_node->decl, replacement);
+	varpool_node::create_alias (sym_node->decl, replacement);
+      else
+	cgraph_node::create_same_body_alias (sym_node->decl, replacement);
+	// cgraph_node::create_alias (sym_node->decl, replacement);
+      sym_node->copy_visibility_from (repl_node);
+    }
+}
+
 /* 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.  */
@@ -2062,6 +2118,12 @@ symtab_node::noninterposable_alias (void)
 
   /* Otherwise create a new one.  */
   new_decl = copy_node (node->decl);
+  DECL_ATTRIBUTES (new_decl) = remove_attribute ("sym_alias",
+						   DECL_ATTRIBUTES
+						   (new_decl));
+  DECL_ATTRIBUTES (new_decl) = remove_attribute ("sym alias",
+						   DECL_ATTRIBUTES
+						   (new_decl));
   DECL_DLLIMPORT_P (new_decl) = 0;
   tree name = clone_function_name (node->decl, "localalias");
   if (!flag_wpa)
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
new file mode 100644
index 00000000000..2fe1948aed9
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
@@ -0,0 +1,75 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+extern int var_alias __attribute__ ((__alias__ ("FOOVAR_A")));
+int var_a = 1;
+
+void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+
+void foo_alias () __attribute__ ((__alias__ ("FOOBAR_A")));
+
+void
+foo_a ()
+{
+}
+
+
+int var_b;
+extern int var_b __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+
+void
+foo_b ()
+{
+}
+
+void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+
+
+int var_c __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+
+void __attribute__ ((__sym_alias__ ("FOOBAR_C")))
+foo_c ()
+{
+}
+
+
+void baf () {}
+
+void foo_x () {
+  extern void baf () __attribute__ ((__sym_alias__ ("FOO_BAF")));
+  extern void bad () /* ok, but not defined, so no alias issued.  */
+    __attribute__ ((__sym_alias__ ("FOO_BAD")));
+  extern void bar () __attribute__ ((__sym_alias__ ("FOO_BAR")));
+
+  static int x __attribute__ ((sym_alias ("FOO_x")));
+}
+
+void bar () {}
+
+extern int xr __attribute__ ((alias ("FOO_x")));
+
+
+ /* The *u* symbols are not defined, so the sym_aliases do not clash.  ??? Is
+    this just an implementation artifact, or a feature someone might actually
+    rely on?  */
+extern int var_u1 __attribute__ ((__sym_alias__ ("VAR_U")));
+extern int var_u2 __attribute__ ((__sym_alias__ ("VAR_U")));
+
+void foo_u1 () __attribute__ ((__sym_alias__ ("FOOBAR_U")));
+void foo_u2 () __attribute__ ((__sym_alias__ ("FOOBAR_U")));
+
+
+/* { dg-final { scan-assembler "FOOBAR_A" } } */
+/* { dg-final { scan-assembler "foo_alias" } } */
+/* { dg-final { scan-assembler "FOOVAR_A" } } */
+/* { dg-final { scan-assembler "var_alias" } } */
+/* { dg-final { scan-assembler "FOOBAR_B" } } */
+/* { dg-final { scan-assembler "FOOVAR_B" } } */
+/* { dg-final { scan-assembler "FOOBAR_C" } } */
+/* { dg-final { scan-assembler "FOOVAR_C" } } */
+/* { dg-final { scan-assembler "FOO_BAF" } } */
+/* { dg-final { scan-assembler "FOO_BAR" } } */
+/* { dg-final { scan-assembler-not "FOO_BAD" } } */
+/* { dg-final { scan-assembler-not "VAR_U" } } */
+/* { dg-final { scan-assembler-not "FOOBAR_U" } } */
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
new file mode 100644
index 00000000000..f692a552606
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
@@ -0,0 +1,99 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+struct s
+{
+  int mem __attribute__ ((__sym_alias__ ("S_MEM"))); /* { dg-warning "attribute ignored" } */
+};
+
+void foo () {
+  extern void bar () __attribute__ ((__sym_alias__ ("FOO_BAR"))); /* Ok.  */
+  int i __attribute__ ((sym_alias ("FOO_i"))); /* { dg-warning "ignored" } */
+  static int x __attribute__ ((sym_alias ("FOO_x")));
+  static int sx __attribute__ ((alias ("FOO_x"))); /* { dg-warning "ignored" } */
+  extern int xx __attribute__ ((alias ("FOO_x"))); /* { dg-warning "ignored" } */
+  extern int y __attribute__ ((sym_alias ("FOO_y"))); /* Ok.  */
+}
+
+int dr __attribute__ ((alias ("FOO_x"))); /* { dg-error "defined both" } */
+
+
+int j __attribute__ ((sym_alias ("J_var")));
+void __attribute__ ((alias ("J_var")))
+j_fn (); /* { dg-error "between function and variable" } */
+
+
+void __attribute__ ((sym_alias ("K_fn")))
+k () {
+}
+extern int __attribute__ ((alias ("K_fn")))
+k_var; /* { dg-error "between function and variable" } */
+
+
+int l __attribute__ ((sym_alias ("L_fn")));
+
+/* These should be detected and reported, not because the names clash at the
+   source level, but because the asm symbols do.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+void
+L_fn () /* { dg-error "duplicate" "" } */
+{
+}
+
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __attribute__ ((sym_alias ("M_var")))
+m ()
+{
+}
+
+int M_var; /* { dg-error "duplicate" "" } */
+
+
+void __attribute__ ((sym_alias ("N_sym")))
+n_fn ()
+{
+}
+
+int __attribute__ ((sym_alias ("N_sym")))
+n_var; /* { dg-error "duplicate" } */
+
+
+int __attribute__ ((sym_alias ("O_sym")))
+o_var;
+
+void __attribute__ ((sym_alias ("O_sym")))
+o_fn () /* { dg-error "duplicate" } */
+{
+}
+
+int __attribute__ ((sym_alias ("P_sym")))
+p_var1;
+
+int __attribute__ ((sym_alias ("P_sym")))
+p_var2; /* { dg-error "duplicate" } */
+
+void __attribute__ ((sym_alias ("Q_sym")))
+q_fn1 ()
+{
+}
+
+void __attribute__ ((sym_alias ("Q_sym")))
+q_fn2 () /* { dg-error "duplicate" } */
+{
+}
+
+int __attribute__ ((sym_alias ("R_var")))
+R_var; /* { dg-error "duplicate" } */
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __attribute__ ((sym_alias ("S_fn")))
+S_fn () /* { dg-error "duplicate" } */
+{
+}
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
new file mode 100644
index 00000000000..52f8fb76496
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
@@ -0,0 +1,34 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int var_a = 1;
+
+void
+foo_a ()
+{
+  extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+}
+
+void
+foo_b ()
+{
+  extern int var_b __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+}
+
+int var_b;
+
+void __attribute__ ((__sym_alias__ ("FOOBAR_C")))
+foo_c ()
+{
+  void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+  /* Another sym for var_b.  */
+  extern int var_b __attribute__ ((__sym_alias__ ("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-sym-alias-4.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
new file mode 100644
index 00000000000..2beafa46379
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-require-alias "" } */
+
+int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A"))) = 42;
+
+int __attribute__ ((__sym_alias__ ("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/c-c++-common/torture/attr-sym-alias-5.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
new file mode 100644
index 00000000000..bee90136daf
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
@@ -0,0 +1,54 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+/* { dg-require-visibility "" } */
+
+int __attribute__ ((sym_alias ("A_hid"), visibility ("hidden"))) a;
+
+extern int __attribute__ ((sym_alias ("B_prt"))) b;
+
+int b __attribute__ ((visibility ("protected")));
+
+extern int __attribute__ ((sym_alias ("C_ntr"))) c;
+
+extern int c __attribute__ ((visibility ("internal")));
+
+int c;
+
+static int d __attribute__ ((sym_alias ("D_stt")));
+
+
+extern void __attribute__ ((sym_alias ("F_hid"), visibility ("hidden")))
+f ();
+
+void
+f () {
+}
+
+extern void __attribute__ ((sym_alias ("G_prt")))
+g ();
+
+void __attribute__ ((visibility ("protected")))
+g () {
+}
+
+extern void __attribute__ ((sym_alias ("H_ntr")))
+h ();
+
+void __attribute__ ((visibility ("internal")))
+h ();
+
+void h () {
+}
+
+static void __attribute__ ((sym_alias ("I_stt")))
+i () {
+}
+
+/* { dg-final { scan-assembler {hidden[^\n]*A_hid} } } */
+/* { dg-final { scan-assembler {protected[^\n]*B_prt} } } */
+/* { dg-final { scan-assembler {internal[^\n]*C_ntr} } } */
+/* { dg-final { scan-assembler-not {glob[^\n]*D_stt} } } */
+/* { dg-final { scan-assembler {hidden[^\n]*F_hid} } } */
+/* { dg-final { scan-assembler {protected[^\n]*G_prt} } } */
+/* { dg-final { scan-assembler {internal[^\n]*H_ntr} } } */
+/* { dg-final { scan-assembler-not {glob[^\n]*I_stt} } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
new file mode 100644
index 00000000000..51729994913
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
@@ -0,0 +1,89 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+class __attribute__ ((__sym_alias__ ("FOOCLS_A"),
+		      __sym_alias__ ("FOOCLS_A_Dupe"))) foo {
+  static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  void __attribute__ ((__sym_alias__ ("FOOBAR_A"))) bar ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+int foo::var = 1;
+
+foo::foo () {}
+
+void foo::bar () {}
+
+namespace b {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_B"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_B"))) foo ();
+    void __attribute__ ((__sym_alias__ ("FOOBAR_B"))) bar () {}
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_B"))) ~foo() {}
+  };
+
+  int foo::var = 2;
+
+  foo::foo () {
+    void (foo::*pbar)() = &foo::bar;
+  }
+}
+
+namespace c {
+  namespace cd {
+    class __attribute__ ((__sym_alias__ ("FOOCLS_C"))) foo {
+      static int var __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+      __attribute__ ((__sym_alias__ ("FOOCTR_C"))) foo () {
+	void (foo::*pbar)() = &foo::bar;
+      }
+      void __attribute__ ((__sym_alias__ ("FOOBAR_C"))) bar () {}
+      virtual __attribute__ ((__sym_alias__ ("FOODTR_C"))) ~foo() {}
+    };
+
+    int foo::var = 3;
+  }
+}
+
+#include <typeinfo>
+
+namespace d {
+  namespace {
+    class __attribute__ ((__sym_alias__ ("FOOCLS_D"))) foo {
+      virtual ~foo() {} // typeid(foo) is not defined otherwise.
+    };
+  }
+
+  extern std::type_info __attribute__ ((__alias__ ("FOOCLS_D"))) foo_d_typeinfo;
+}
+
+extern std::type_info __attribute__ ((__alias__ ("FOOCLS_C"))) foo_c_typeinfo;
+
+/* { dg-final { scan-assembler "FOOCLS_A" } } */
+/* { dg-final { scan-assembler "FOOCLS_A_Dupe" } } */
+/* { 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" } } */
+/* { dg-final { scan-assembler "FOOCLS_D" } } */
+/* { dg-final { scan-assembler "foo_d_typeinfo" } } */
+/* { dg-final { scan-assembler "foo_c_typeinfo" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
new file mode 100644
index 00000000000..6facfcc2cd9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+namespace {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_A"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo ();
+    void __attribute__ ((__sym_alias__ ("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-sym-alias-3.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
new file mode 100644
index 00000000000..42c7288ad4a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
@@ -0,0 +1,83 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+// sym can be applied to template function explicit instantiations.
+
+template <typename T>
+void
+fn(T) {
+};
+
+template void __attribute__ ((__sym_alias__ ("FOOFUN_UINT"))) fn<>(unsigned int);
+template void __attribute__ ((__sym_alias__ ("FOOFUN_LONG"))) fn<>(long);
+
+template<> void __attribute__ ((__sym_alias__ ("FOOFUN_CHAR"))) fn<>(char) {}
+
+
+template <typename T = void>
+struct
+foo {
+  virtual ~foo() {}
+
+  virtual void virtfun() {}
+
+  static void stfun() {}
+  void inlfun() {}
+};
+
+// Explicitly instantiate members before the enclosing class.
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_CHAR_VIRT"))) foo<char>::virtfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_CHAR_TI"))) foo<char>;
+
+// Though they're only output if the enclosing class is.
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_LONG_VIRT"))) foo<long>::virtfun();
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_LONG_TI_X"))) foo<long>;
+
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_VOID_ST"))) foo<void>::stfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_VOID_TI"))) foo<>;
+
+
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_X"))) foo<short>;
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_ST"))) foo<short>::stfun();
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_INL"))) foo<short>::inlfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_D"))) foo<short>;
+
+// Explicit specializations work too.
+
+template <>
+struct  __attribute__ ((__sym_alias__ ("FOOCLS_INT_TI")))
+foo<int>
+{
+  virtual ~foo() {}
+  virtual void __attribute__ ((__sym_alias__ ("FOOCLS_INT_VIRT"))) virtfun() {}
+};
+
+/* { dg-final { scan-assembler "FOOFUN_UINT" } } */
+/* { dg-final { scan-assembler "FOOFUN_LONG" } } */
+/* { dg-final { scan-assembler "FOOFUN_CHAR" } } */
+
+/* { dg-final { scan-assembler "FOOCLS_VOID_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_VOID_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_X" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_INL" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_D" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_TI_X" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_VIRT" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
new file mode 100644
index 00000000000..59b779de35d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+template <typename T = void>
+class
+__attribute__ ((__sym_alias__ ("FOOCLS")))
+foo // { dg-error "duplicate|already" }
+{
+  virtual ~foo() {}
+
+  template <typename U>
+  void
+    __attribute__ ((__sym_alias__ ("FOOTMF")))
+    tmemfun () {} // { dg-error "duplicate|already" }
+};
+
+template <typename T>
+void
+__attribute__ ((__sym_alias__ ("FOOTFN")))
+fn(T) { // { dg-error "duplicate|already" }
+};
+
+template class foo<>;
+template class foo<int>;
+template void foo<>::tmemfun<void>();
+template void foo<int>::tmemfun<void>();
+template void fn<>(int);
+template void fn<>(long);
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
new file mode 100644
index 00000000000..45d59b953c3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
@@ -0,0 +1,14 @@
+/* { dg-do compile { target c++11 } } */
+/* { dg-require-alias "" } */
+
+struct foo {
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+foo::foo () {}
+
+// Make sure the inherited cdtors don't duplicate the syms.
+struct bar : foo {
+  using foo::foo;
+};
diff --git a/gcc/varpool.cc b/gcc/varpool.cc
index e40504d1bd8..c5fa39ee8cb 100644
--- a/gcc/varpool.cc
+++ b/gcc/varpool.cc
@@ -163,6 +163,9 @@ varpool_node::get_create (tree decl)
     }
 
   node->register_symbol ();
+
+  create_sym_alias_decls (decl);
+
   return node;
 }

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [gcc(refs/users/aoliva/heads/testme)] Introduce attribute sym_alias
@ 2023-12-14 16:26 Alexandre Oliva
  0 siblings, 0 replies; 33+ messages in thread
From: Alexandre Oliva @ 2023-12-14 16:26 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:a0e762d6c1511a416048b9bb94b4e290bad3caf6

commit a0e762d6c1511a416048b9bb94b4e290bad3caf6
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Thu Dec 14 03:21:21 2023 -0300

    Introduce attribute sym_alias
    
    This patch introduces an attribute to add extra asm names (aliases)
    for a decl 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, and
    also to local static variables.  In C++, it can also be attached to
    class types, namespace-scoped variables and functions, static data
    members, member functions, explicit instantiations and specializations
    of template functions, members and classes.
    
    When applied to constructors or destructor, additional sym aliases
    with _Base and _Del suffixes are defined for variants other than
    complete-object ones.  This changes the assumption that clones always
    carry the same attributes as their abstract declarations, so there is
    now a function to adjust them.
    
    C++ also had a bug in which attributes from local extern declarations
    failed to be propagated to a preexisting corresponding
    namespace-scoped decl.  I've fixed that, and adjusted acc tests that
    distinguished between C and C++ in this regard.
    
    Applying the attribute to class types is only valid in C++, and the
    effect is to attach the alias to the RTTI object associated with the
    class type.
    
    
    for  gcc/ChangeLog
    
            * attribs.cc: Include cgraph.h.
            (decl_attributes): Allow late introduction of sym_alias in
            types.
            (create_sym_alias_decl, create_sym_alias_decls): New.
            * attribs.h: Declare them.
            (FOR_EACH_SYM_ALIAS): New macro.
            * cgraph.cc (cgraph_node::create): Create sym_alias decls.
            * varpool.cc (varpool_node::get_create): Create sym_alias
            decls.
            * cgraph.h (symtab_node::remap_sym_alias_target): New.
            * symtab.cc (symtab_node::remap_sym_alias_target): Define.
            (symbol_table::insert_to_assembler_name_hash): Check for
            symbol name clashes.
            (symtab_node::noninterposable_alias): Drop sym_alias
            attributes.
            * cgraphunit.cc (cgraph_node::analyze): Create alias_target
            node if needed.
            (analyze_functions): Fixup visibility of implicit alias only
            after its node is analyzed.
            * doc/extend.texi (sym_alias): Document for variables,
            functions and types.
    
    for  gcc/ada/ChangeLog
    
            * doc/gnat_rm/interfacing_to_other_languages.rst: Mention
            attribute sym_alias to give RTTI symbols mnemonic names.
            * doc/gnat_ugn/the_gnat_compilation_model.rst: Mention
            aliases.  Fix incorrect ref to C1 ctor variant.
    
    for  gcc/c-family/ChangeLog
    
            * c-ada-spec.cc (pp_asm_name): Use first sym_alias if
            available.
            * c-attribs.cc (handle_sym_alias_attribute): New.
            (c_common_attribute_table): Add sym_alias.
            (handle_copy_attribute): Do not copy sym_alias attribute.
    
    for  gcc/c/ChangeLog
    
            * c-decl.cc (duplicate_decls): Remap sym_alias target.
            (finish_decl): Create varpool_node for local static
            variables.
    
    for  gcc/cp/ChangeLog
    
            * class.cc (adjust_clone_attributes): New.
            (copy_fndecl_with_name, build_clone): Call it.
            * cp-tree.h (adjust_clone_attributes): Declare.
            (update_sym_alias_interface): Declare.
            (update_tinfo_sym_alias): Declare.
            * decl.cc (duplicate_decls): Remap sym_alias target.
            Adjust clone attributes.
            (grokfndecl): Tentatively create sym_alias decls after
            adding attributes in e.g. a template member function explicit
            instantiation.
            * decl2.cc (cplus_decl_attributes): Update tinfo sym_alias.
            (copy_interface, update_sym_alias_interface): New.
            (determine_visibility): Update sym_alias interface.
            (tentative_decl_linkage, import_export_decl): Likewise.
            * name-lookup.cc: Include target.h and cgraph.h.
            (push_local_extern_decl_alias): Merge attributes with
            namespace-scoped decl, and drop duplicate sym_alias.
            * optimize.cc (maybe_clone_body): Re-adjust attributes after
            cloning them.  Update sym_alias interface.
            * rtti.cc: Include attribs.h and cgraph.h.
            (get_tinfo_decl): Copy sym_alias attributes from type to tinfo
            decl.  Create sym_alias decls.
            (update_tinfo_sym_alias): New.
    
    for  gcc/testsuite/ChangeLog
    
            * c-c++-common/goacc/declare-1.c: Adjust.
            * c-c++-common/goacc/declare-2.c: Adjust.
            * c-c++-common/torture/attr-sym-alias-1.c: New.
            * c-c++-common/torture/attr-sym-alias-2.c: New.
            * c-c++-common/torture/attr-sym-alias-3.c: New.
            * c-c++-common/torture/attr-sym-alias-4.c: New.
            * c-c++-common/torture/attr-sym-alias-5.c: New.
            * g++.dg/torture/attr-sym-alias-1.C: New.
            * g++.dg/torture/attr-sym-alias-2.C: New.
            * g++.dg/torture/attr-sym-alias-3.C: New.
            * g++.dg/torture/attr-sym-alias-4.C: New.
            * g++.dg/torture/attr-sym-alias-5.C: New.

Diff:
---
 .../doc/gnat_rm/interfacing_to_other_languages.rst |  6 ++
 .../doc/gnat_ugn/the_gnat_compilation_model.rst    | 10 ++-
 gcc/attribs.cc                                     | 74 +++++++++++++++-
 gcc/attribs.h                                      |  7 ++
 gcc/c-family/c-ada-spec.cc                         |  7 ++
 gcc/c-family/c-attribs.cc                          | 33 +++++++-
 gcc/c/c-decl.cc                                    |  7 ++
 gcc/cgraph.cc                                      |  2 +
 gcc/cgraph.h                                       |  4 +
 gcc/cgraphunit.cc                                  |  2 +-
 gcc/cp/class.cc                                    | 64 ++++++++++++++
 gcc/cp/cp-tree.h                                   |  4 +
 gcc/cp/decl.cc                                     |  4 +
 gcc/cp/decl2.cc                                    | 53 ++++++++++++
 gcc/cp/name-lookup.cc                              | 11 +++
 gcc/cp/optimize.cc                                 |  3 +
 gcc/cp/rtti.cc                                     | 71 +++++++++++++++-
 gcc/doc/extend.texi                                | 62 ++++++++++++++
 gcc/symtab.cc                                      | 62 ++++++++++++++
 gcc/testsuite/c-c++-common/goacc/declare-1.c       |  6 +-
 gcc/testsuite/c-c++-common/goacc/declare-2.c       | 14 +--
 .../c-c++-common/torture/attr-sym-alias-1.c        | 75 ++++++++++++++++
 .../c-c++-common/torture/attr-sym-alias-2.c        | 99 ++++++++++++++++++++++
 .../c-c++-common/torture/attr-sym-alias-3.c        | 34 ++++++++
 .../c-c++-common/torture/attr-sym-alias-4.c        | 28 ++++++
 .../c-c++-common/torture/attr-sym-alias-5.c        | 54 ++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C    | 89 +++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C    | 26 ++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C    | 83 ++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C    | 28 ++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C    | 14 +++
 gcc/varpool.cc                                     |  3 +
 32 files changed, 1020 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 ad0be511d48..b8de16ebf7c 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 ``sym_alias`` 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 fd15459203a..20045698962 100644
--- a/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
+++ b/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
@@ -4274,6 +4274,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__ ((__sym_alias__ ("Ctor_For_Animal")))
         Animal() {Age_Count = 0;};
       private:
         int Age_Count;
@@ -4306,6 +4307,7 @@ both Carnivore and Domestic, that is:
         virtual int  Number_Of_Teeth ();
         virtual void Set_Owner (char* Name);
 
+        __attribute__ ((__sym_alias__ ("Ctor_For_Dog"))) // mnemonic alias
         Dog(); // Constructor
       private:
         int  Tooth_Count;
@@ -4344,7 +4346,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;
@@ -4360,7 +4363,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,7 +4385,8 @@ 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.
+mangled name (or an alias) 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.cc b/gcc/attribs.cc
index 4e313d38f0f..b0e71fccb6f 100644
--- a/gcc/attribs.cc
+++ b/gcc/attribs.cc
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "target.h"
 #include "tree.h"
+#include "cgraph.h"
 #include "stringpool.h"
 #include "diagnostic-core.h"
 #include "attribs.h"
@@ -843,7 +844,8 @@ decl_attributes (tree *node, tree attributes, int flags,
 
       if (TYPE_P (*anode)
 	  && (flags & (int) ATTR_FLAG_TYPE_IN_PLACE)
-	  && COMPLETE_TYPE_P (*anode))
+	  && COMPLETE_TYPE_P (*anode)
+	  && !is_attribute_p ("sym_alias", name))
 	{
 	  warning (OPT_Wattributes, "type attributes ignored after type is already defined");
 	  continue;
@@ -2685,6 +2687,76 @@ attr_access::array_as_string (tree type) const
   return typstr;
 }
 
+/* Create a sym attribute for DECL to be visible with linkage name ID.  */
+
+tree
+create_sym_alias_decl (tree decl, tree id)
+{
+  const char *attr_str = "sym_alias";
+
+  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;
+
+      tree attr_name = get_identifier (attr_str);
+      error_at (DECL_SOURCE_LOCATION (decl),
+		"duplicate symbol name %qE in %qE attribute of %qD",
+		id, attr_name, decl);
+      inform (DECL_SOURCE_LOCATION (sym_node->decl),
+	      "already used by %qD", sym_node->decl);
+    }
+
+  tree clone = copy_node (decl);
+  DECL_ATTRIBUTES (clone) = remove_attribute (attr_str,
+					      DECL_ATTRIBUTES (decl));
+  /* Mark it as a "sym alias" for decl, an internal attribute that
+     enables symbol_table::insert_to_assembler_name_hash can recognize
+     sym_alias decls.  */
+  DECL_ATTRIBUTES (clone) = tree_cons (get_identifier ("sym alias"),
+				       decl, DECL_ATTRIBUTES (clone));
+  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;
+
+  symtab_node *node;
+  if (VAR_P (clone))
+    // DECL_READ_P (clone) = 1;
+    // node = varpool_node::create_extra_name_alias (clone, decl);
+    node = varpool_node::create_alias (clone, decl);
+  else
+    node = cgraph_node::create_same_body_alias (clone, decl);
+    // node = cgraph_node::create_alias (clone, decl);
+  if (symtab_node *dnode = symtab_node::get_create (decl))
+    node->copy_visibility_from (dnode);
+
+  return clone;
+}
+
+/* Create decls for all sym aliases requested in DECL's attributes.  */
+
+void
+create_sym_alias_decls (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl)
+      || DECL_ABSTRACT_P (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      create_sym_alias_decl (decl, id);
+    }
+}
+
 #if CHECKING_P
 
 namespace selftest
diff --git a/gcc/attribs.h b/gcc/attribs.h
index 2c615a45663..39e0bc573dc 100644
--- a/gcc/attribs.h
+++ b/gcc/attribs.h
@@ -405,4 +405,11 @@ extern void init_attr_rdwr_indices (rdwr_map *, tree);
 extern attr_access *get_parm_access (rdwr_map &, tree,
 				     tree = current_function_decl);
 
+extern tree create_sym_alias_decl (tree, tree);
+extern void create_sym_alias_decls (tree);
+
+#define FOR_EACH_SYM_ALIAS(sym, attrs)					\
+  for (tree sym = lookup_attribute ("sym_alias", (attrs));		\
+       sym; sym = lookup_attribute ("sym_alias", TREE_CHAIN (sym)))
+
 #endif // GCC_ATTRIBS_H
diff --git a/gcc/c-family/c-ada-spec.cc b/gcc/c-family/c-ada-spec.cc
index 050994d8416..5042b9cfecd 100644
--- a/gcc/c-family/c-ada-spec.cc
+++ b/gcc/c-family/c-ada-spec.cc
@@ -1431,6 +1431,13 @@ pp_ada_tree_identifier (pretty_printer *buffer, tree node, tree type,
 static void
 pp_asm_name (pretty_printer *buffer, tree t)
 {
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (t))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.cc b/gcc/c-family/c-attribs.cc
index a3671fe3a57..29b40e6af7d 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -110,7 +110,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_sym_alias_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,
@@ -392,6 +393,8 @@ const struct attribute_spec c_common_gnu_attributes[] =
 			      handle_alias_attribute, NULL },
   { "weakref",                0, 1, true,  false, false, false,
 			      handle_weakref_attribute, NULL },
+  { "sym_alias",              1, 1, false,  false, false, false,
+			      handle_sym_alias_attribute, NULL },
   { "no_instrument_function", 0, 0, true,  false, false, false,
 			      handle_no_instrument_function_attribute,
 			      NULL },
@@ -3093,7 +3096,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
@@ -3103,7 +3106,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
@@ -3113,6 +3116,29 @@ handle_alias_attribute (tree *node, tree name, tree args,
   return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs);
 }
 
+/* Handle a "sym_alias" attribute; arguments as in struct
+   attribute_spec.handler.  */
+
+static tree
+handle_sym_alias_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 (decl_in_symtab_p (node))
+    *no_add_attrs = false;
+  else if (TYPE_P (node) && c_dialect_cxx ())
+    *no_add_attrs = false;
+  else
+    return error_mark_node;
+
+  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.  */
 
@@ -3246,6 +3272,7 @@ handle_copy_attribute (tree *node, tree name, tree args,
 	      || is_attribute_p ("visibility", atname)
 	      || is_attribute_p ("weak", atname)
 	      || is_attribute_p ("weakref", atname)
+	      || is_attribute_p ("sym_alias", atname)
 	      || is_attribute_p ("target_clones", atname))
 	    continue;
 
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 039a66fef09..eee316af9d6 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -3117,6 +3117,8 @@ duplicate_decls (tree newdecl, tree olddecl)
 
   merge_decls (newdecl, olddecl, newtype, oldtype);
 
+  symtab_node::remap_sym_alias_target (newdecl, olddecl);
+
   /* The NEWDECL will no longer be needed.
 
      Before releasing the node, be sure to remove function from symbol
@@ -5902,6 +5904,11 @@ finish_decl (tree decl, location_t init_loc, tree init,
 	    set_user_assembler_name (decl, asmspec);
 	}
 
+      /* Give attribute sym_alias a chance to make the symbol name
+	 available for aliasing, even for a static local variable.  */
+      if (VAR_P (decl) && !DECL_EXTERNAL (decl) && is_global_var (decl))
+	varpool_node::get_create (decl);
+
       if (DECL_FILE_SCOPE_P (decl))
 	{
 	  if (DECL_INITIAL (decl) == NULL_TREE
diff --git a/gcc/cgraph.cc b/gcc/cgraph.cc
index f93259a8c70..0bef2179e89 100644
--- a/gcc/cgraph.cc
+++ b/gcc/cgraph.cc
@@ -523,6 +523,8 @@ cgraph_node::create (tree decl)
   node->register_symbol ();
   maybe_record_nested_function (node);
 
+  create_sym_alias_decls (decl);
+
   return node;
 }
 
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index 2b320557616..6d61e327fb6 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -327,6 +327,10 @@ public:
   /* Return DECL that alias is aliasing.  */
   inline tree get_alias_target_tree ();
 
+  /* Remap sym alias nodes recorded as aliasing REPLACED to alias REPLACEMENT
+     instead.  */
+  static void remap_sym_alias_target (tree replaced, tree replacement);
+
   /* Set section for symbol and its aliases.  */
   void set_section (const char *section);
 
diff --git a/gcc/cgraphunit.cc b/gcc/cgraphunit.cc
index 9a550a5cce6..a66db795c78 100644
--- a/gcc/cgraphunit.cc
+++ b/gcc/cgraphunit.cc
@@ -1176,7 +1176,7 @@ analyze_functions (bool first_time)
      C++ FE is confused about the COMDAT groups being right.  */
   if (symtab->cpp_implicit_aliases_done)
     FOR_EACH_SYMBOL (node)
-      if (node->cpp_implicit_alias)
+      if (node->cpp_implicit_alias && node->analyzed)
 	  node->fixup_same_cpp_alias_visibility (node->get_alias_target ());
   build_type_inheritance_graph ();
 
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index 1954e0a5ed3..76b6b166926 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -4896,6 +4896,68 @@ check_methods (tree t)
     }
 }
 
+/* Adjust sym alias name for CLONE, cloned from FN and named NAME,
+   if it is a cdtor, and drop the sym alias from other clones.  */
+
+void
+adjust_clone_attributes (tree fn, tree clone, tree name, bool skip_copy_p)
+{
+  if (IDENTIFIER_CDTOR_P (name))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	{
+	  found = true;
+	  break;
+	}
+
+      if (found
+	  && (name == complete_ctor_identifier
+	      || name == complete_dtor_identifier))
+	{
+	  /* Reuse the sym alias decls created for the primary cdtor
+	     decl.  */
+	  symtab_node::remap_sym_alias_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);
+
+	  if (!skip_copy_p)
+	    DECL_ATTRIBUTES (clone) = copy_list (DECL_ATTRIBUTES (clone));
+
+	  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	    {
+	      /* We need to copy this even with skip_copy_p, because
+		 even then copying was shallow.  */
+	      TREE_VALUE (sym) = copy_list (TREE_VALUE (sym));
+	      /* Append suf to the sym alias name.  */
+	      tree str = TREE_VALUE (TREE_VALUE (sym));
+	      char *symname = concat (TREE_STRING_POINTER (str), suf, NULL);
+	      str = build_string (TREE_STRING_LENGTH (str) + xlen, symname);
+	      TREE_VALUE (TREE_VALUE (sym)) = str;
+	      free (symname);
+	    }
+
+	  if (symtab_node::get (clone))
+	    create_sym_alias_decls (clone);
+	}
+    }
+  else
+    DECL_ATTRIBUTES (clone)
+      = remove_attribute ("sym_alias", DECL_ATTRIBUTES (clone));
+}
+
 /* FN is constructor, destructor or operator function.  Clone the
    declaration to create a NAME'd variant.  NEED_VTT_PARM_P and
    OMIT_INHERITED_PARMS_P are relevant if it's a cdtor.  */
@@ -5065,6 +5127,8 @@ build_clone (tree fn, tree name, bool need_vtt_parm_p,
   DECL_CHAIN (clone) = DECL_CHAIN (fn);
   DECL_CHAIN (fn) = clone;
 
+  adjust_clone_attributes (fn, clone, name);
+
   return clone;
 }
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index cbf280ec454..10a31ad7c0a 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5822,6 +5822,8 @@ struct GTY((for_user)) spec_entry
 
 extern int current_class_depth;
 
+void adjust_clone_attributes (tree fn, tree clone, tree name, bool = false);
+
 /* in decl.cc */
 
 /* An array of static vars & fns.  */
@@ -7059,6 +7061,7 @@ extern void do_push_parm_decls			(tree, tree, tree *);
 extern tree do_aggregate_paren_init		(tree, tree);
 
 /* in decl2.cc */
+extern void update_sym_alias_interface		(tree);
 extern void record_mangling			(tree, bool);
 extern void overwrite_mangling			(tree, tree);
 extern void note_mangling_alias			(tree, tree);
@@ -7633,6 +7636,7 @@ extern bool emit_tinfo_decl			(tree);
 extern unsigned get_pseudo_tinfo_index		(tree);
 extern tree get_pseudo_tinfo_type		(unsigned);
 extern tree build_if_nonnull			(tree, tree, tsubst_flags_t);
+extern void update_tinfo_sym_alias		(tree);
 
 /* in search.cc */
 extern tree get_parent_with_private_access 	(tree decl, tree binfo);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 4d17ead123a..68b894912f8 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -3277,6 +3277,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
 	      && TREE_STATIC (olddecl))))
     make_decl_rtl (olddecl);
 
+  symtab_node::remap_sym_alias_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.
@@ -3300,6 +3302,7 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
       FOR_EACH_CLONE (clone, olddecl)
 	{
 	  DECL_ATTRIBUTES (clone) = DECL_ATTRIBUTES (olddecl);
+	  adjust_clone_attributes (olddecl, clone, DECL_NAME (clone));
 	  DECL_PRESERVE_P (clone) |= DECL_PRESERVE_P (olddecl);
 	}
     }
@@ -10838,6 +10841,7 @@ grokfndecl (tree ctype,
     {
       cplus_decl_attributes (&decl, *attrlist, 0);
       *attrlist = NULL_TREE;
+      create_sym_alias_decls (decl);
     }
 
   if (DECL_HAS_CONTRACTS_P (decl))
diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
index bee84879023..00f19962e5d 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -1846,6 +1846,9 @@ cplus_decl_attributes (tree *decl, tree attributes, int flags)
   if (late_attrs)
     save_template_attributes (late_attrs, decl, flags);
 
+  if (TYPE_P (*decl) && attributes)
+    update_tinfo_sym_alias (*decl);
+
   /* Propagate deprecation out to the template.  */
   if (TREE_DEPRECATED (*decl))
     if (tree ti = get_template_info (*decl))
@@ -2202,6 +2205,50 @@ 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);
+  if (symtab_node *dest_node = symtab_node::get (dest))
+    if (symtab_node *decl_node = symtab_node::get (decl))
+      decl_node->copy_visibility_from (dest_node);
+}
+
+/* Propagate linkage changes to sym aliases.  */
+
+void
+update_sym_alias_interface (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.  */
 
@@ -3009,6 +3056,8 @@ determine_visibility (tree decl)
        translation unit, we can make the type internal.  */
     constrain_visibility (decl, VISIBILITY_ANON, false);
 
+  update_sym_alias_interface (decl);
+
   /* If visibility changed and DECL already has DECL_RTL, ensure
      symbol flags are updated.  */
   if ((DECL_VISIBILITY (decl) != orig_visibility
@@ -3271,6 +3320,8 @@ tentative_decl_linkage (tree decl)
       else if (VAR_P (decl))
 	maybe_commonize_var (decl);
     }
+
+  update_sym_alias_interface (decl);
 }
 
 /* DECL is a FUNCTION_DECL or VAR_DECL.  If the object file linkage
@@ -3505,6 +3556,8 @@ import_export_decl (tree decl)
     }
 
   DECL_INTERFACE_KNOWN (decl) = 1;
+
+  update_sym_alias_interface (decl);
 }
 
 /* Return an expression that performs the destruction of DECL, which
diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc
index 09dc6ef3e5a..d11835a4552 100644
--- a/gcc/cp/name-lookup.cc
+++ b/gcc/cp/name-lookup.cc
@@ -22,9 +22,11 @@ along with GCC; see the file COPYING3.  If not see
 #define INCLUDE_MEMORY
 #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"
@@ -3675,6 +3677,15 @@ push_local_extern_decl_alias (tree decl)
 	  /* Adjust visibility.  */
 	  determine_visibility (alias);
 	}
+      else if (DECL_P (alias))
+	DECL_ATTRIBUTES (alias)
+	  = targetm.merge_decl_attributes (alias, decl);
+      if (DECL_P (alias))
+	{
+	  symtab_node::remap_sym_alias_target (decl, alias);
+	  DECL_ATTRIBUTES (decl)
+	    = remove_attribute ("sym_alias", DECL_ATTRIBUTES (alias));
+	}
     }
 
   retrofit_lang_decl (decl);
diff --git a/gcc/cp/optimize.cc b/gcc/cp/optimize.cc
index 9e8926e4cc6..4a2e8cb4354 100644
--- a/gcc/cp/optimize.cc
+++ b/gcc/cp/optimize.cc
@@ -528,9 +528,12 @@ maybe_clone_body (tree fn)
       DECL_VISIBILITY_SPECIFIED (clone) = DECL_VISIBILITY_SPECIFIED (fn);
       DECL_DLLIMPORT_P (clone) = DECL_DLLIMPORT_P (fn);
       DECL_ATTRIBUTES (clone) = clone_attrs (DECL_ATTRIBUTES (fn));
+      adjust_clone_attributes (fn, clone, DECL_NAME (clone), true);
       DECL_DISREGARD_INLINE_LIMITS (clone) = DECL_DISREGARD_INLINE_LIMITS (fn);
       set_decl_section_name (clone, fn);
 
+      update_sym_alias_interface (clone);
+
       /* Adjust the parameter names and locations.  */
       parm = DECL_ARGUMENTS (fn);
       clone_parm = DECL_ARGUMENTS (clone);
diff --git a/gcc/cp/rtti.cc b/gcc/cp/rtti.cc
index 7878929c246..4bb7b8c8c6e 100644
--- a/gcc/cp/rtti.cc
+++ b/gcc/cp/rtti.cc
@@ -28,8 +28,10 @@ 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"
+#include "cgraph.h"
 
 /* C++ returns type information to the user in struct type_info
    objects. We also use type information to implement dynamic_cast and
@@ -479,8 +481,13 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
 	  = build_tree_list (get_identifier ("non overlapping"),
 			     NULL_TREE);
       else
+	/* Share the non overlapping attribute, without assuming it's
+	   the only attribute, but assuming it's the last if it's
+	   present.  There may be sym aliases too, and those are not
+	   to be shared.  */
 	DECL_ATTRIBUTES (d)
-	  = DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]);
+	  = lookup_attribute ("non overlapping",
+			      DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]));
 
       /* Mark the variable as undefined -- but remember that we can
 	 define it later if we need to do so.  */
@@ -492,6 +499,16 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
       if (CLASS_TYPE_P (type))
 	CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type)) = d;
 
+      /* Copy sym alias attributes from the type to the rtti obj decl.  */
+      tree *attrs = &DECL_ATTRIBUTES (d);
+      FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+	{
+	  tree attr = tree_cons (TREE_PURPOSE (sym), TREE_VALUE (sym), *attrs);
+	  *attrs = attr;
+	  attrs = &TREE_CHAIN (attr);
+	}
+      create_sym_alias_decls (d);
+
       /* Add decl to the global array of tinfo decls.  */
       vec_safe_push (unemitted_tinfo_decls, d);
     }
@@ -499,6 +516,58 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
   return d;
 }
 
+/* After modifying the attributes of TYPE, check whether tinfo was
+   already created and, if so, add to it any sym alias attributes
+   that were not already present.  */
+
+void
+update_tinfo_sym_alias (tree type)
+{
+  if (!TYPE_SIZE (type) || !CLASS_TYPE_P (type))
+    return;
+
+  tree d = CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type));
+  if (!d)
+    return;
+
+  bool first = true;
+  symtab_node *node = NULL;
+
+  tree *attrs = &DECL_ATTRIBUTES (d);
+  FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (d_sym, *attrs)
+	if (TREE_VALUE (sym) == TREE_VALUE (d_sym))
+	  {
+	    found = true;
+	    break;
+	  }
+
+      if (found)
+	continue;
+
+      tree attr = tree_cons (TREE_PURPOSE (sym),
+			     TREE_VALUE (sym),
+			     *attrs);
+      *attrs = attr;
+      attrs = &TREE_CHAIN (attr);
+
+      if (first)
+	{
+	  first = false;
+	  node = symtab_node::get (d);
+	}
+
+      if (!node)
+	continue;
+
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+      create_sym_alias_decl (d, id);
+    }
+}
+
 /* Return the type_info object for TYPE.  */
 
 tree
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index f0c789f6cb4..b585e2d8102 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -4142,6 +4142,49 @@ Function Attributes}, @ref{PowerPC Function Attributes},
 @ref{Nios II Function Attributes}, and @ref{S/390 Function Attributes}
 for details.
 
+@cindex @code{sym_alias} function attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} attribute causes @var{name} to be output as an
+alias to the function, with the same linkage and visibility as the
+function, when the function definition is output, provided that the
+function could be an alias target.  For instance,
+
+@smallexample
+void f (uint64_t) __attribute__ ((__sym_alias__ ("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},
+with the same linkage and visibility.  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.
+
+Aliases introduced with this attribute, such as @samp{f_u64} in the
+example above, are assembly symbol names: they do not undergo C++ name
+mangling, and are not made visible in any scope in the source language.
+They can, however, be named as alias targets, as long as their
+definition is output.  Naming a @samp{sym_alias} as an alias target will
+@emph{not} cause a definition to be output if it otherwise wouldn't.
+This may affect inline functions, C++ template instantiations, and other
+synthesized definitions that would have to be (synthesized and) compiled
+in order for the @samp{sym_alias} to be considered as a potential alias
+target.
+
+This attribute requires assembler and object file support for aliases,
+and may not be available on all targets.
+
 @cindex @code{symver} function attribute
 @item symver ("@var{name2}@@@var{nodename}")
 On ELF targets this attribute creates a symbol version.  The @var{name2} part
@@ -8069,6 +8112,10 @@ will be placed in new, unique sections.
 
 This additional functionality requires Binutils version 2.36 or later.
 
+@cindex @code{sym_alias} variable attribute
+@item sym_alias ("@var{name}")
+See @pxref{Common Function Attributes}.
+
 @cindex @code{uninitialized} variable attribute
 @item uninitialized
 This attribute, attached to a variable with automatic storage, means that
@@ -9169,6 +9216,21 @@ is not supported; that is to say, if a given scalar object can be accessed
 through distinct types that assign a different storage order to it, then the
 behavior is undefined.
 
+@cindex @code{sym_alias} type attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} type 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__ ((__sym_alias__ ("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.
+
 @cindex @code{transparent_union} type attribute
 @item transparent_union
 
diff --git a/gcc/symtab.cc b/gcc/symtab.cc
index 0470509a98d..44df52095c1 100644
--- a/gcc/symtab.cc
+++ b/gcc/symtab.cc
@@ -187,6 +187,22 @@ symbol_table::insert_to_assembler_name_hash (symtab_node *node,
 	(*aslot)->previous_sharing_asm_name = node;
       *aslot = node;
 
+      /* Check for sym_alias name clashes.  In create_sym_alias_decl,
+	 we check for a preexisting definition using the same
+	 assembler_name, so here we check for a previously-defined
+	 sym_name, marked with a "sym name" pseudo-attribute that
+	 points back at the declaration for which it was created.  */
+      if (symtab_node *sym_node = node->next_sharing_asm_name)
+	if (lookup_attribute ("sym alias",
+			      DECL_ATTRIBUTES (sym_node->decl)))
+	  {
+	    error_at (DECL_SOURCE_LOCATION (node->decl),
+		      "duplicate symbol name %qE", decl);
+	    inform (DECL_SOURCE_LOCATION (sym_node->decl),
+		    "already used by %qD in a %qE attribute",
+		    sym_node->decl, get_identifier ("sym_alias"));
+	  }
+
       /* Update also possible inline clones sharing a decl.  */
       cnode = dyn_cast <cgraph_node *> (node);
       if (cnode && cnode->clones && with_clones)
@@ -1943,6 +1959,46 @@ symtab_node::noninterposable_alias (symtab_node *node, void *data)
   return false;
 }
 
+/* Remap sym alias nodes recorded as aliasing REPLACED to alias
+   REPLACEMENT instead.  */
+
+void
+symtab_node::remap_sym_alias_target (tree replaced, tree replacement)
+{
+  symtab_node *repl_node = NULL;
+  if (!decl_in_symtab_p (replacement)
+      || !(repl_node = symtab_node::get (replacement)))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (replaced))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      symtab_node *sym_node = symtab_node::get_for_asmname (id);
+
+      if (!sym_node)
+	{
+	  create_sym_alias_decl (replacement, id);
+	  continue;
+	}
+
+      gcc_assert (!sym_node->analyzed);
+      if (sym_node->alias_target != replaced)
+	continue;
+
+      // sym_node->definition = 0;
+
+      if (VAR_P (replaced))
+	// varpool_node::create_extra_name_alias (sym_node->decl, replacement);
+	varpool_node::create_alias (sym_node->decl, replacement);
+      else
+	cgraph_node::create_same_body_alias (sym_node->decl, replacement);
+	// cgraph_node::create_alias (sym_node->decl, replacement);
+      sym_node->copy_visibility_from (repl_node);
+    }
+}
+
 /* 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.  */
@@ -1970,6 +2026,12 @@ symtab_node::noninterposable_alias (void)
 
   /* Otherwise create a new one.  */
   new_decl = copy_node (node->decl);
+  DECL_ATTRIBUTES (new_decl) = remove_attribute ("sym_alias",
+						   DECL_ATTRIBUTES
+						   (new_decl));
+  DECL_ATTRIBUTES (new_decl) = remove_attribute ("sym alias",
+						   DECL_ATTRIBUTES
+						   (new_decl));
   DECL_DLLIMPORT_P (new_decl) = 0;
   tree name = clone_function_name (node->decl, "localalias");
   if (!flag_wpa)
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-1.c b/gcc/testsuite/c-c++-common/goacc/declare-1.c
index 46ee01b6759..f2842893318 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-1.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-1.c
@@ -113,11 +113,11 @@ f_2 (void)
   int va3;
 #pragma acc declare device_resident(va3)
 
-#ifndef __cplusplus
+#if 0
   /* TODO PR90868
 
-     C: "error: variable '[...]' used more than once with '#pragma acc declare'".  */
-#else
+     "error: variable '[...]' used more than once with '#pragma acc declare'".  */
+
   extern int ve0;
 #pragma acc declare create(ve0)
 
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-2.c b/gcc/testsuite/c-c++-common/goacc/declare-2.c
index e2e22be57e9..aec59b69754 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-2.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-2.c
@@ -137,25 +137,25 @@ void
 f_pr90868_2 (void)
 {
   extern int we0;
-#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" } */
 
   extern int we1;
-#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" } */
 
   extern int *we2;
-#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" } */
 
   extern int we3;
-#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" } */
 
   extern int we4;
-#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" } */
 
   extern int we5;
-#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" } */
  
   extern int we6;
-#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" } */
 }
 
 
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
new file mode 100644
index 00000000000..2fe1948aed9
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
@@ -0,0 +1,75 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+extern int var_alias __attribute__ ((__alias__ ("FOOVAR_A")));
+int var_a = 1;
+
+void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+
+void foo_alias () __attribute__ ((__alias__ ("FOOBAR_A")));
+
+void
+foo_a ()
+{
+}
+
+
+int var_b;
+extern int var_b __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+
+void
+foo_b ()
+{
+}
+
+void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+
+
+int var_c __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+
+void __attribute__ ((__sym_alias__ ("FOOBAR_C")))
+foo_c ()
+{
+}
+
+
+void baf () {}
+
+void foo_x () {
+  extern void baf () __attribute__ ((__sym_alias__ ("FOO_BAF")));
+  extern void bad () /* ok, but not defined, so no alias issued.  */
+    __attribute__ ((__sym_alias__ ("FOO_BAD")));
+  extern void bar () __attribute__ ((__sym_alias__ ("FOO_BAR")));
+
+  static int x __attribute__ ((sym_alias ("FOO_x")));
+}
+
+void bar () {}
+
+extern int xr __attribute__ ((alias ("FOO_x")));
+
+
+ /* The *u* symbols are not defined, so the sym_aliases do not clash.  ??? Is
+    this just an implementation artifact, or a feature someone might actually
+    rely on?  */
+extern int var_u1 __attribute__ ((__sym_alias__ ("VAR_U")));
+extern int var_u2 __attribute__ ((__sym_alias__ ("VAR_U")));
+
+void foo_u1 () __attribute__ ((__sym_alias__ ("FOOBAR_U")));
+void foo_u2 () __attribute__ ((__sym_alias__ ("FOOBAR_U")));
+
+
+/* { dg-final { scan-assembler "FOOBAR_A" } } */
+/* { dg-final { scan-assembler "foo_alias" } } */
+/* { dg-final { scan-assembler "FOOVAR_A" } } */
+/* { dg-final { scan-assembler "var_alias" } } */
+/* { dg-final { scan-assembler "FOOBAR_B" } } */
+/* { dg-final { scan-assembler "FOOVAR_B" } } */
+/* { dg-final { scan-assembler "FOOBAR_C" } } */
+/* { dg-final { scan-assembler "FOOVAR_C" } } */
+/* { dg-final { scan-assembler "FOO_BAF" } } */
+/* { dg-final { scan-assembler "FOO_BAR" } } */
+/* { dg-final { scan-assembler-not "FOO_BAD" } } */
+/* { dg-final { scan-assembler-not "VAR_U" } } */
+/* { dg-final { scan-assembler-not "FOOBAR_U" } } */
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
new file mode 100644
index 00000000000..f692a552606
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
@@ -0,0 +1,99 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+struct s
+{
+  int mem __attribute__ ((__sym_alias__ ("S_MEM"))); /* { dg-warning "attribute ignored" } */
+};
+
+void foo () {
+  extern void bar () __attribute__ ((__sym_alias__ ("FOO_BAR"))); /* Ok.  */
+  int i __attribute__ ((sym_alias ("FOO_i"))); /* { dg-warning "ignored" } */
+  static int x __attribute__ ((sym_alias ("FOO_x")));
+  static int sx __attribute__ ((alias ("FOO_x"))); /* { dg-warning "ignored" } */
+  extern int xx __attribute__ ((alias ("FOO_x"))); /* { dg-warning "ignored" } */
+  extern int y __attribute__ ((sym_alias ("FOO_y"))); /* Ok.  */
+}
+
+int dr __attribute__ ((alias ("FOO_x"))); /* { dg-error "defined both" } */
+
+
+int j __attribute__ ((sym_alias ("J_var")));
+void __attribute__ ((alias ("J_var")))
+j_fn (); /* { dg-error "between function and variable" } */
+
+
+void __attribute__ ((sym_alias ("K_fn")))
+k () {
+}
+extern int __attribute__ ((alias ("K_fn")))
+k_var; /* { dg-error "between function and variable" } */
+
+
+int l __attribute__ ((sym_alias ("L_fn")));
+
+/* These should be detected and reported, not because the names clash at the
+   source level, but because the asm symbols do.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+void
+L_fn () /* { dg-error "duplicate" "" } */
+{
+}
+
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __attribute__ ((sym_alias ("M_var")))
+m ()
+{
+}
+
+int M_var; /* { dg-error "duplicate" "" } */
+
+
+void __attribute__ ((sym_alias ("N_sym")))
+n_fn ()
+{
+}
+
+int __attribute__ ((sym_alias ("N_sym")))
+n_var; /* { dg-error "duplicate" } */
+
+
+int __attribute__ ((sym_alias ("O_sym")))
+o_var;
+
+void __attribute__ ((sym_alias ("O_sym")))
+o_fn () /* { dg-error "duplicate" } */
+{
+}
+
+int __attribute__ ((sym_alias ("P_sym")))
+p_var1;
+
+int __attribute__ ((sym_alias ("P_sym")))
+p_var2; /* { dg-error "duplicate" } */
+
+void __attribute__ ((sym_alias ("Q_sym")))
+q_fn1 ()
+{
+}
+
+void __attribute__ ((sym_alias ("Q_sym")))
+q_fn2 () /* { dg-error "duplicate" } */
+{
+}
+
+int __attribute__ ((sym_alias ("R_var")))
+R_var; /* { dg-error "duplicate" } */
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __attribute__ ((sym_alias ("S_fn")))
+S_fn () /* { dg-error "duplicate" } */
+{
+}
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
new file mode 100644
index 00000000000..52f8fb76496
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
@@ -0,0 +1,34 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int var_a = 1;
+
+void
+foo_a ()
+{
+  extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+}
+
+void
+foo_b ()
+{
+  extern int var_b __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+}
+
+int var_b;
+
+void __attribute__ ((__sym_alias__ ("FOOBAR_C")))
+foo_c ()
+{
+  void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+  /* Another sym for var_b.  */
+  extern int var_b __attribute__ ((__sym_alias__ ("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-sym-alias-4.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
new file mode 100644
index 00000000000..2beafa46379
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-require-alias "" } */
+
+int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A"))) = 42;
+
+int __attribute__ ((__sym_alias__ ("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/c-c++-common/torture/attr-sym-alias-5.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
new file mode 100644
index 00000000000..bee90136daf
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
@@ -0,0 +1,54 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+/* { dg-require-visibility "" } */
+
+int __attribute__ ((sym_alias ("A_hid"), visibility ("hidden"))) a;
+
+extern int __attribute__ ((sym_alias ("B_prt"))) b;
+
+int b __attribute__ ((visibility ("protected")));
+
+extern int __attribute__ ((sym_alias ("C_ntr"))) c;
+
+extern int c __attribute__ ((visibility ("internal")));
+
+int c;
+
+static int d __attribute__ ((sym_alias ("D_stt")));
+
+
+extern void __attribute__ ((sym_alias ("F_hid"), visibility ("hidden")))
+f ();
+
+void
+f () {
+}
+
+extern void __attribute__ ((sym_alias ("G_prt")))
+g ();
+
+void __attribute__ ((visibility ("protected")))
+g () {
+}
+
+extern void __attribute__ ((sym_alias ("H_ntr")))
+h ();
+
+void __attribute__ ((visibility ("internal")))
+h ();
+
+void h () {
+}
+
+static void __attribute__ ((sym_alias ("I_stt")))
+i () {
+}
+
+/* { dg-final { scan-assembler {hidden[^\n]*A_hid} } } */
+/* { dg-final { scan-assembler {protected[^\n]*B_prt} } } */
+/* { dg-final { scan-assembler {internal[^\n]*C_ntr} } } */
+/* { dg-final { scan-assembler-not {glob[^\n]*D_stt} } } */
+/* { dg-final { scan-assembler {hidden[^\n]*F_hid} } } */
+/* { dg-final { scan-assembler {protected[^\n]*G_prt} } } */
+/* { dg-final { scan-assembler {internal[^\n]*H_ntr} } } */
+/* { dg-final { scan-assembler-not {glob[^\n]*I_stt} } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
new file mode 100644
index 00000000000..51729994913
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
@@ -0,0 +1,89 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+class __attribute__ ((__sym_alias__ ("FOOCLS_A"),
+		      __sym_alias__ ("FOOCLS_A_Dupe"))) foo {
+  static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  void __attribute__ ((__sym_alias__ ("FOOBAR_A"))) bar ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+int foo::var = 1;
+
+foo::foo () {}
+
+void foo::bar () {}
+
+namespace b {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_B"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_B"))) foo ();
+    void __attribute__ ((__sym_alias__ ("FOOBAR_B"))) bar () {}
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_B"))) ~foo() {}
+  };
+
+  int foo::var = 2;
+
+  foo::foo () {
+    void (foo::*pbar)() = &foo::bar;
+  }
+}
+
+namespace c {
+  namespace cd {
+    class __attribute__ ((__sym_alias__ ("FOOCLS_C"))) foo {
+      static int var __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+      __attribute__ ((__sym_alias__ ("FOOCTR_C"))) foo () {
+	void (foo::*pbar)() = &foo::bar;
+      }
+      void __attribute__ ((__sym_alias__ ("FOOBAR_C"))) bar () {}
+      virtual __attribute__ ((__sym_alias__ ("FOODTR_C"))) ~foo() {}
+    };
+
+    int foo::var = 3;
+  }
+}
+
+#include <typeinfo>
+
+namespace d {
+  namespace {
+    class __attribute__ ((__sym_alias__ ("FOOCLS_D"))) foo {
+      virtual ~foo() {} // typeid(foo) is not defined otherwise.
+    };
+  }
+
+  extern std::type_info __attribute__ ((__alias__ ("FOOCLS_D"))) foo_d_typeinfo;
+}
+
+extern std::type_info __attribute__ ((__alias__ ("FOOCLS_C"))) foo_c_typeinfo;
+
+/* { dg-final { scan-assembler "FOOCLS_A" } } */
+/* { dg-final { scan-assembler "FOOCLS_A_Dupe" } } */
+/* { 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" } } */
+/* { dg-final { scan-assembler "FOOCLS_D" } } */
+/* { dg-final { scan-assembler "foo_d_typeinfo" } } */
+/* { dg-final { scan-assembler "foo_c_typeinfo" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
new file mode 100644
index 00000000000..6facfcc2cd9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+namespace {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_A"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo ();
+    void __attribute__ ((__sym_alias__ ("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-sym-alias-3.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
new file mode 100644
index 00000000000..42c7288ad4a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
@@ -0,0 +1,83 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+// sym can be applied to template function explicit instantiations.
+
+template <typename T>
+void
+fn(T) {
+};
+
+template void __attribute__ ((__sym_alias__ ("FOOFUN_UINT"))) fn<>(unsigned int);
+template void __attribute__ ((__sym_alias__ ("FOOFUN_LONG"))) fn<>(long);
+
+template<> void __attribute__ ((__sym_alias__ ("FOOFUN_CHAR"))) fn<>(char) {}
+
+
+template <typename T = void>
+struct
+foo {
+  virtual ~foo() {}
+
+  virtual void virtfun() {}
+
+  static void stfun() {}
+  void inlfun() {}
+};
+
+// Explicitly instantiate members before the enclosing class.
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_CHAR_VIRT"))) foo<char>::virtfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_CHAR_TI"))) foo<char>;
+
+// Though they're only output if the enclosing class is.
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_LONG_VIRT"))) foo<long>::virtfun();
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_LONG_TI_X"))) foo<long>;
+
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_VOID_ST"))) foo<void>::stfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_VOID_TI"))) foo<>;
+
+
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_X"))) foo<short>;
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_ST"))) foo<short>::stfun();
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_INL"))) foo<short>::inlfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_D"))) foo<short>;
+
+// Explicit specializations work too.
+
+template <>
+struct  __attribute__ ((__sym_alias__ ("FOOCLS_INT_TI")))
+foo<int>
+{
+  virtual ~foo() {}
+  virtual void __attribute__ ((__sym_alias__ ("FOOCLS_INT_VIRT"))) virtfun() {}
+};
+
+/* { dg-final { scan-assembler "FOOFUN_UINT" } } */
+/* { dg-final { scan-assembler "FOOFUN_LONG" } } */
+/* { dg-final { scan-assembler "FOOFUN_CHAR" } } */
+
+/* { dg-final { scan-assembler "FOOCLS_VOID_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_VOID_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_X" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_INL" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_D" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_TI_X" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_VIRT" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
new file mode 100644
index 00000000000..59b779de35d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+template <typename T = void>
+class
+__attribute__ ((__sym_alias__ ("FOOCLS")))
+foo // { dg-error "duplicate|already" }
+{
+  virtual ~foo() {}
+
+  template <typename U>
+  void
+    __attribute__ ((__sym_alias__ ("FOOTMF")))
+    tmemfun () {} // { dg-error "duplicate|already" }
+};
+
+template <typename T>
+void
+__attribute__ ((__sym_alias__ ("FOOTFN")))
+fn(T) { // { dg-error "duplicate|already" }
+};
+
+template class foo<>;
+template class foo<int>;
+template void foo<>::tmemfun<void>();
+template void foo<int>::tmemfun<void>();
+template void fn<>(int);
+template void fn<>(long);
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
new file mode 100644
index 00000000000..45d59b953c3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
@@ -0,0 +1,14 @@
+/* { dg-do compile { target c++11 } } */
+/* { dg-require-alias "" } */
+
+struct foo {
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+foo::foo () {}
+
+// Make sure the inherited cdtors don't duplicate the syms.
+struct bar : foo {
+  using foo::foo;
+};
diff --git a/gcc/varpool.cc b/gcc/varpool.cc
index e7b51b15e4a..33649906d1d 100644
--- a/gcc/varpool.cc
+++ b/gcc/varpool.cc
@@ -163,6 +163,9 @@ varpool_node::get_create (tree decl)
     }
 
   node->register_symbol ();
+
+  create_sym_alias_decls (decl);
+
   return node;
 }

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [gcc(refs/users/aoliva/heads/testme)] Introduce attribute sym_alias
@ 2023-12-14 13:45 Alexandre Oliva
  0 siblings, 0 replies; 33+ messages in thread
From: Alexandre Oliva @ 2023-12-14 13:45 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:54882adc3d6ba0e87262643338666419c1f30cfc

commit 54882adc3d6ba0e87262643338666419c1f30cfc
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Thu Dec 14 03:21:21 2023 -0300

    Introduce attribute sym_alias
    
    This patch introduces an attribute to add extra asm names (aliases)
    for a decl 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, and
    also to local static variables.  In C++, it can also be attached to
    class types, namespace-scoped variables and functions, static data
    members, member functions, explicit instantiations and specializations
    of template functions, members and classes.
    
    When applied to constructors or destructor, additional sym aliases
    with _Base and _Del suffixes are defined for variants other than
    complete-object ones.  This changes the assumption that clones always
    carry the same attributes as their abstract declarations, so there is
    now a function to adjust them.
    
    C++ also had a bug in which attributes from local extern declarations
    failed to be propagated to a preexisting corresponding
    namespace-scoped decl.  I've fixed that, and adjusted acc tests that
    distinguished between C and C++ in this regard.
    
    Applying the attribute to class types is only valid in C++, and the
    effect is to attach the alias to the RTTI object associated with the
    class type.
    
    
    for  gcc/ChangeLog
    
            * attribs.cc: Include cgraph.h.
            (decl_attributes): Allow late introduction of sym_alias in
            types.
            (create_sym_alias_decl, create_sym_alias_decls): New.
            * attribs.h: Declare them.
            (FOR_EACH_SYM_ALIAS): New macro.
            * cgraph.cc (cgraph_node::create): Create sym_alias decls.
            * varpool.cc (varpool_node::get_create): Create sym_alias
            decls.
            * cgraph.h (symtab_node::remap_sym_alias_target): New.
            * symtab.cc (symtab_node::remap_sym_alias_target): Define.
            (symbol_table::insert_to_assembler_name_hash): Check for
            symbol name clashes.
            (symtab_node::noninterposable_alias): Drop sym_alias
            attributes.
            * cgraphunit.cc (cgraph_node::analyze): Create alias_target
            node if needed.
            (analyze_functions): Fixup visibility of implicit alias only
            after its node is analyzed.
            * doc/extend.texi (sym_alias): Document for variables,
            functions and types.
    
    for  gcc/ada/ChangeLog
    
            * doc/gnat_rm/interfacing_to_other_languages.rst: Mention
            attribute sym_alias to give RTTI symbols mnemonic names.
            * doc/gnat_ugn/the_gnat_compilation_model.rst: Mention
            aliases.  Fix incorrect ref to C1 ctor variant.
    
    for  gcc/c-family/ChangeLog
    
            * c-ada-spec.cc (pp_asm_name): Use first sym_alias if
            available.
            * c-attribs.cc (handle_sym_alias_attribute): New.
            (c_common_attribute_table): Add sym_alias.
            (handle_copy_attribute): Do not copy sym_alias attribute.
    
    for  gcc/c/ChangeLog
    
            * c-decl.cc (duplicate_decls): Remap sym_alias target.
            (finish_decl): Create varpool_node for local static
            variables.
    
    for  gcc/cp/ChangeLog
    
            * class.cc (adjust_clone_attributes): New.
            (copy_fndecl_with_name, build_clone): Call it.
            * cp-tree.h (adjust_clone_attributes): Declare.
            (update_sym_alias_interface): Declare.
            (update_tinfo_sym_alias): Declare.
            * decl.cc (duplicate_decls): Remap sym_alias target.
            Adjust clone attributes.
            (grokfndecl): Tentatively create sym_alias decls after
            adding attributes in e.g. a template member function explicit
            instantiation.
            * decl2.cc (cplus_decl_attributes): Update tinfo sym_alias.
            (copy_interface, update_sym_alias_interface): New.
            (determine_visibility): Update sym_alias interface.
            (tentative_decl_linkage, import_export_decl): Likewise.
            * name-lookup.cc: Include target.h and cgraph.h.
            (push_local_extern_decl_alias): Merge attributes with
            namespace-scoped decl, and drop duplicate sym_alias.
            * optimize.cc (maybe_clone_body): Re-adjust attributes after
            cloning them.  Update sym_alias interface.
            * rtti.cc: Include attribs.h and cgraph.h.
            (get_tinfo_decl): Copy sym_alias attributes from type to tinfo
            decl.  Create sym_alias decls.
            (update_tinfo_sym_alias): New.
    
    for  gcc/testsuite/ChangeLog
    
            * c-c++-common/goacc/declare-1.c: Adjust.
            * c-c++-common/goacc/declare-2.c: Adjust.
            * c-c++-common/torture/attr-sym-alias-1.c: New.
            * c-c++-common/torture/attr-sym-alias-2.c: New.
            * c-c++-common/torture/attr-sym-alias-3.c: New.
            * c-c++-common/torture/attr-sym-alias-4.c: New.
            * c-c++-common/torture/attr-sym-alias-5.c: New.
            * g++.dg/torture/attr-sym-alias-1.C: New.
            * g++.dg/torture/attr-sym-alias-2.C: New.
            * g++.dg/torture/attr-sym-alias-3.C: New.
            * g++.dg/torture/attr-sym-alias-4.C: New.
            * g++.dg/torture/attr-sym-alias-5.C: New.

Diff:
---
 .../doc/gnat_rm/interfacing_to_other_languages.rst |  6 ++
 .../doc/gnat_ugn/the_gnat_compilation_model.rst    | 10 ++-
 gcc/attribs.cc                                     | 74 +++++++++++++++-
 gcc/attribs.h                                      |  7 ++
 gcc/c-family/c-ada-spec.cc                         |  7 ++
 gcc/c-family/c-attribs.cc                          | 33 +++++++-
 gcc/c/c-decl.cc                                    |  7 ++
 gcc/cgraph.cc                                      |  2 +
 gcc/cgraph.h                                       |  4 +
 gcc/cgraphunit.cc                                  |  2 +-
 gcc/cp/class.cc                                    | 64 ++++++++++++++
 gcc/cp/cp-tree.h                                   |  4 +
 gcc/cp/decl.cc                                     |  4 +
 gcc/cp/decl2.cc                                    | 53 ++++++++++++
 gcc/cp/name-lookup.cc                              | 11 +++
 gcc/cp/optimize.cc                                 |  3 +
 gcc/cp/rtti.cc                                     | 71 +++++++++++++++-
 gcc/doc/extend.texi                                | 62 ++++++++++++++
 gcc/symtab.cc                                      | 62 ++++++++++++++
 gcc/testsuite/c-c++-common/goacc/declare-1.c       |  6 +-
 gcc/testsuite/c-c++-common/goacc/declare-2.c       | 14 +--
 .../c-c++-common/torture/attr-sym-alias-1.c        | 75 ++++++++++++++++
 .../c-c++-common/torture/attr-sym-alias-2.c        | 99 ++++++++++++++++++++++
 .../c-c++-common/torture/attr-sym-alias-3.c        | 34 ++++++++
 .../c-c++-common/torture/attr-sym-alias-4.c        | 28 ++++++
 .../c-c++-common/torture/attr-sym-alias-5.c        | 54 ++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C    | 89 +++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C    | 26 ++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C    | 83 ++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C    | 28 ++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C    | 14 +++
 gcc/varpool.cc                                     |  3 +
 32 files changed, 1020 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 ad0be511d48..b8de16ebf7c 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 ``sym_alias`` 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 fd15459203a..20045698962 100644
--- a/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
+++ b/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
@@ -4274,6 +4274,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__ ((__sym_alias__ ("Ctor_For_Animal")))
         Animal() {Age_Count = 0;};
       private:
         int Age_Count;
@@ -4306,6 +4307,7 @@ both Carnivore and Domestic, that is:
         virtual int  Number_Of_Teeth ();
         virtual void Set_Owner (char* Name);
 
+        __attribute__ ((__sym_alias__ ("Ctor_For_Dog"))) // mnemonic alias
         Dog(); // Constructor
       private:
         int  Tooth_Count;
@@ -4344,7 +4346,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;
@@ -4360,7 +4363,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,7 +4385,8 @@ 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.
+mangled name (or an alias) 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.cc b/gcc/attribs.cc
index 4e313d38f0f..b0e71fccb6f 100644
--- a/gcc/attribs.cc
+++ b/gcc/attribs.cc
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "target.h"
 #include "tree.h"
+#include "cgraph.h"
 #include "stringpool.h"
 #include "diagnostic-core.h"
 #include "attribs.h"
@@ -843,7 +844,8 @@ decl_attributes (tree *node, tree attributes, int flags,
 
       if (TYPE_P (*anode)
 	  && (flags & (int) ATTR_FLAG_TYPE_IN_PLACE)
-	  && COMPLETE_TYPE_P (*anode))
+	  && COMPLETE_TYPE_P (*anode)
+	  && !is_attribute_p ("sym_alias", name))
 	{
 	  warning (OPT_Wattributes, "type attributes ignored after type is already defined");
 	  continue;
@@ -2685,6 +2687,76 @@ attr_access::array_as_string (tree type) const
   return typstr;
 }
 
+/* Create a sym attribute for DECL to be visible with linkage name ID.  */
+
+tree
+create_sym_alias_decl (tree decl, tree id)
+{
+  const char *attr_str = "sym_alias";
+
+  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;
+
+      tree attr_name = get_identifier (attr_str);
+      error_at (DECL_SOURCE_LOCATION (decl),
+		"duplicate symbol name %qE in %qE attribute of %qD",
+		id, attr_name, decl);
+      inform (DECL_SOURCE_LOCATION (sym_node->decl),
+	      "already used by %qD", sym_node->decl);
+    }
+
+  tree clone = copy_node (decl);
+  DECL_ATTRIBUTES (clone) = remove_attribute (attr_str,
+					      DECL_ATTRIBUTES (decl));
+  /* Mark it as a "sym alias" for decl, an internal attribute that
+     enables symbol_table::insert_to_assembler_name_hash can recognize
+     sym_alias decls.  */
+  DECL_ATTRIBUTES (clone) = tree_cons (get_identifier ("sym alias"),
+				       decl, DECL_ATTRIBUTES (clone));
+  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;
+
+  symtab_node *node;
+  if (VAR_P (clone))
+    // DECL_READ_P (clone) = 1;
+    // node = varpool_node::create_extra_name_alias (clone, decl);
+    node = varpool_node::create_alias (clone, decl);
+  else
+    node = cgraph_node::create_same_body_alias (clone, decl);
+    // node = cgraph_node::create_alias (clone, decl);
+  if (symtab_node *dnode = symtab_node::get_create (decl))
+    node->copy_visibility_from (dnode);
+
+  return clone;
+}
+
+/* Create decls for all sym aliases requested in DECL's attributes.  */
+
+void
+create_sym_alias_decls (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl)
+      || DECL_ABSTRACT_P (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      create_sym_alias_decl (decl, id);
+    }
+}
+
 #if CHECKING_P
 
 namespace selftest
diff --git a/gcc/attribs.h b/gcc/attribs.h
index 2c615a45663..39e0bc573dc 100644
--- a/gcc/attribs.h
+++ b/gcc/attribs.h
@@ -405,4 +405,11 @@ extern void init_attr_rdwr_indices (rdwr_map *, tree);
 extern attr_access *get_parm_access (rdwr_map &, tree,
 				     tree = current_function_decl);
 
+extern tree create_sym_alias_decl (tree, tree);
+extern void create_sym_alias_decls (tree);
+
+#define FOR_EACH_SYM_ALIAS(sym, attrs)					\
+  for (tree sym = lookup_attribute ("sym_alias", (attrs));		\
+       sym; sym = lookup_attribute ("sym_alias", TREE_CHAIN (sym)))
+
 #endif // GCC_ATTRIBS_H
diff --git a/gcc/c-family/c-ada-spec.cc b/gcc/c-family/c-ada-spec.cc
index 050994d8416..5042b9cfecd 100644
--- a/gcc/c-family/c-ada-spec.cc
+++ b/gcc/c-family/c-ada-spec.cc
@@ -1431,6 +1431,13 @@ pp_ada_tree_identifier (pretty_printer *buffer, tree node, tree type,
 static void
 pp_asm_name (pretty_printer *buffer, tree t)
 {
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (t))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.cc b/gcc/c-family/c-attribs.cc
index a3671fe3a57..29b40e6af7d 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -110,7 +110,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_sym_alias_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,
@@ -392,6 +393,8 @@ const struct attribute_spec c_common_gnu_attributes[] =
 			      handle_alias_attribute, NULL },
   { "weakref",                0, 1, true,  false, false, false,
 			      handle_weakref_attribute, NULL },
+  { "sym_alias",              1, 1, false,  false, false, false,
+			      handle_sym_alias_attribute, NULL },
   { "no_instrument_function", 0, 0, true,  false, false, false,
 			      handle_no_instrument_function_attribute,
 			      NULL },
@@ -3093,7 +3096,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
@@ -3103,7 +3106,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
@@ -3113,6 +3116,29 @@ handle_alias_attribute (tree *node, tree name, tree args,
   return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs);
 }
 
+/* Handle a "sym_alias" attribute; arguments as in struct
+   attribute_spec.handler.  */
+
+static tree
+handle_sym_alias_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 (decl_in_symtab_p (node))
+    *no_add_attrs = false;
+  else if (TYPE_P (node) && c_dialect_cxx ())
+    *no_add_attrs = false;
+  else
+    return error_mark_node;
+
+  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.  */
 
@@ -3246,6 +3272,7 @@ handle_copy_attribute (tree *node, tree name, tree args,
 	      || is_attribute_p ("visibility", atname)
 	      || is_attribute_p ("weak", atname)
 	      || is_attribute_p ("weakref", atname)
+	      || is_attribute_p ("sym_alias", atname)
 	      || is_attribute_p ("target_clones", atname))
 	    continue;
 
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 039a66fef09..eee316af9d6 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -3117,6 +3117,8 @@ duplicate_decls (tree newdecl, tree olddecl)
 
   merge_decls (newdecl, olddecl, newtype, oldtype);
 
+  symtab_node::remap_sym_alias_target (newdecl, olddecl);
+
   /* The NEWDECL will no longer be needed.
 
      Before releasing the node, be sure to remove function from symbol
@@ -5902,6 +5904,11 @@ finish_decl (tree decl, location_t init_loc, tree init,
 	    set_user_assembler_name (decl, asmspec);
 	}
 
+      /* Give attribute sym_alias a chance to make the symbol name
+	 available for aliasing, even for a static local variable.  */
+      if (VAR_P (decl) && !DECL_EXTERNAL (decl) && is_global_var (decl))
+	varpool_node::get_create (decl);
+
       if (DECL_FILE_SCOPE_P (decl))
 	{
 	  if (DECL_INITIAL (decl) == NULL_TREE
diff --git a/gcc/cgraph.cc b/gcc/cgraph.cc
index f93259a8c70..0bef2179e89 100644
--- a/gcc/cgraph.cc
+++ b/gcc/cgraph.cc
@@ -523,6 +523,8 @@ cgraph_node::create (tree decl)
   node->register_symbol ();
   maybe_record_nested_function (node);
 
+  create_sym_alias_decls (decl);
+
   return node;
 }
 
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index 2b320557616..6d61e327fb6 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -327,6 +327,10 @@ public:
   /* Return DECL that alias is aliasing.  */
   inline tree get_alias_target_tree ();
 
+  /* Remap sym alias nodes recorded as aliasing REPLACED to alias REPLACEMENT
+     instead.  */
+  static void remap_sym_alias_target (tree replaced, tree replacement);
+
   /* Set section for symbol and its aliases.  */
   void set_section (const char *section);
 
diff --git a/gcc/cgraphunit.cc b/gcc/cgraphunit.cc
index 9a550a5cce6..a66db795c78 100644
--- a/gcc/cgraphunit.cc
+++ b/gcc/cgraphunit.cc
@@ -1176,7 +1176,7 @@ analyze_functions (bool first_time)
      C++ FE is confused about the COMDAT groups being right.  */
   if (symtab->cpp_implicit_aliases_done)
     FOR_EACH_SYMBOL (node)
-      if (node->cpp_implicit_alias)
+      if (node->cpp_implicit_alias && node->analyzed)
 	  node->fixup_same_cpp_alias_visibility (node->get_alias_target ());
   build_type_inheritance_graph ();
 
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index 1954e0a5ed3..76b6b166926 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -4896,6 +4896,68 @@ check_methods (tree t)
     }
 }
 
+/* Adjust sym alias name for CLONE, cloned from FN and named NAME,
+   if it is a cdtor, and drop the sym alias from other clones.  */
+
+void
+adjust_clone_attributes (tree fn, tree clone, tree name, bool skip_copy_p)
+{
+  if (IDENTIFIER_CDTOR_P (name))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	{
+	  found = true;
+	  break;
+	}
+
+      if (found
+	  && (name == complete_ctor_identifier
+	      || name == complete_dtor_identifier))
+	{
+	  /* Reuse the sym alias decls created for the primary cdtor
+	     decl.  */
+	  symtab_node::remap_sym_alias_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);
+
+	  if (!skip_copy_p)
+	    DECL_ATTRIBUTES (clone) = copy_list (DECL_ATTRIBUTES (clone));
+
+	  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	    {
+	      /* We need to copy this even with skip_copy_p, because
+		 even then copying was shallow.  */
+	      TREE_VALUE (sym) = copy_list (TREE_VALUE (sym));
+	      /* Append suf to the sym alias name.  */
+	      tree str = TREE_VALUE (TREE_VALUE (sym));
+	      char *symname = concat (TREE_STRING_POINTER (str), suf, NULL);
+	      str = build_string (TREE_STRING_LENGTH (str) + xlen, symname);
+	      TREE_VALUE (TREE_VALUE (sym)) = str;
+	      free (symname);
+	    }
+
+	  if (symtab_node::get (clone))
+	    create_sym_alias_decls (clone);
+	}
+    }
+  else
+    DECL_ATTRIBUTES (clone)
+      = remove_attribute ("sym_alias", DECL_ATTRIBUTES (clone));
+}
+
 /* FN is constructor, destructor or operator function.  Clone the
    declaration to create a NAME'd variant.  NEED_VTT_PARM_P and
    OMIT_INHERITED_PARMS_P are relevant if it's a cdtor.  */
@@ -5065,6 +5127,8 @@ build_clone (tree fn, tree name, bool need_vtt_parm_p,
   DECL_CHAIN (clone) = DECL_CHAIN (fn);
   DECL_CHAIN (fn) = clone;
 
+  adjust_clone_attributes (fn, clone, name);
+
   return clone;
 }
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index cbf280ec454..10a31ad7c0a 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5822,6 +5822,8 @@ struct GTY((for_user)) spec_entry
 
 extern int current_class_depth;
 
+void adjust_clone_attributes (tree fn, tree clone, tree name, bool = false);
+
 /* in decl.cc */
 
 /* An array of static vars & fns.  */
@@ -7059,6 +7061,7 @@ extern void do_push_parm_decls			(tree, tree, tree *);
 extern tree do_aggregate_paren_init		(tree, tree);
 
 /* in decl2.cc */
+extern void update_sym_alias_interface		(tree);
 extern void record_mangling			(tree, bool);
 extern void overwrite_mangling			(tree, tree);
 extern void note_mangling_alias			(tree, tree);
@@ -7633,6 +7636,7 @@ extern bool emit_tinfo_decl			(tree);
 extern unsigned get_pseudo_tinfo_index		(tree);
 extern tree get_pseudo_tinfo_type		(unsigned);
 extern tree build_if_nonnull			(tree, tree, tsubst_flags_t);
+extern void update_tinfo_sym_alias		(tree);
 
 /* in search.cc */
 extern tree get_parent_with_private_access 	(tree decl, tree binfo);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 4d17ead123a..68b894912f8 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -3277,6 +3277,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
 	      && TREE_STATIC (olddecl))))
     make_decl_rtl (olddecl);
 
+  symtab_node::remap_sym_alias_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.
@@ -3300,6 +3302,7 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
       FOR_EACH_CLONE (clone, olddecl)
 	{
 	  DECL_ATTRIBUTES (clone) = DECL_ATTRIBUTES (olddecl);
+	  adjust_clone_attributes (olddecl, clone, DECL_NAME (clone));
 	  DECL_PRESERVE_P (clone) |= DECL_PRESERVE_P (olddecl);
 	}
     }
@@ -10838,6 +10841,7 @@ grokfndecl (tree ctype,
     {
       cplus_decl_attributes (&decl, *attrlist, 0);
       *attrlist = NULL_TREE;
+      create_sym_alias_decls (decl);
     }
 
   if (DECL_HAS_CONTRACTS_P (decl))
diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
index bee84879023..00f19962e5d 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -1846,6 +1846,9 @@ cplus_decl_attributes (tree *decl, tree attributes, int flags)
   if (late_attrs)
     save_template_attributes (late_attrs, decl, flags);
 
+  if (TYPE_P (*decl) && attributes)
+    update_tinfo_sym_alias (*decl);
+
   /* Propagate deprecation out to the template.  */
   if (TREE_DEPRECATED (*decl))
     if (tree ti = get_template_info (*decl))
@@ -2202,6 +2205,50 @@ 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);
+  if (symtab_node *dest_node = symtab_node::get (dest))
+    if (symtab_node *decl_node = symtab_node::get (decl))
+      decl_node->copy_visibility_from (dest_node);
+}
+
+/* Propagate linkage changes to sym aliases.  */
+
+void
+update_sym_alias_interface (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.  */
 
@@ -3009,6 +3056,8 @@ determine_visibility (tree decl)
        translation unit, we can make the type internal.  */
     constrain_visibility (decl, VISIBILITY_ANON, false);
 
+  update_sym_alias_interface (decl);
+
   /* If visibility changed and DECL already has DECL_RTL, ensure
      symbol flags are updated.  */
   if ((DECL_VISIBILITY (decl) != orig_visibility
@@ -3271,6 +3320,8 @@ tentative_decl_linkage (tree decl)
       else if (VAR_P (decl))
 	maybe_commonize_var (decl);
     }
+
+  update_sym_alias_interface (decl);
 }
 
 /* DECL is a FUNCTION_DECL or VAR_DECL.  If the object file linkage
@@ -3505,6 +3556,8 @@ import_export_decl (tree decl)
     }
 
   DECL_INTERFACE_KNOWN (decl) = 1;
+
+  update_sym_alias_interface (decl);
 }
 
 /* Return an expression that performs the destruction of DECL, which
diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc
index 09dc6ef3e5a..d11835a4552 100644
--- a/gcc/cp/name-lookup.cc
+++ b/gcc/cp/name-lookup.cc
@@ -22,9 +22,11 @@ along with GCC; see the file COPYING3.  If not see
 #define INCLUDE_MEMORY
 #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"
@@ -3675,6 +3677,15 @@ push_local_extern_decl_alias (tree decl)
 	  /* Adjust visibility.  */
 	  determine_visibility (alias);
 	}
+      else if (DECL_P (alias))
+	DECL_ATTRIBUTES (alias)
+	  = targetm.merge_decl_attributes (alias, decl);
+      if (DECL_P (alias))
+	{
+	  symtab_node::remap_sym_alias_target (decl, alias);
+	  DECL_ATTRIBUTES (decl)
+	    = remove_attribute ("sym_alias", DECL_ATTRIBUTES (alias));
+	}
     }
 
   retrofit_lang_decl (decl);
diff --git a/gcc/cp/optimize.cc b/gcc/cp/optimize.cc
index 9e8926e4cc6..4a2e8cb4354 100644
--- a/gcc/cp/optimize.cc
+++ b/gcc/cp/optimize.cc
@@ -528,9 +528,12 @@ maybe_clone_body (tree fn)
       DECL_VISIBILITY_SPECIFIED (clone) = DECL_VISIBILITY_SPECIFIED (fn);
       DECL_DLLIMPORT_P (clone) = DECL_DLLIMPORT_P (fn);
       DECL_ATTRIBUTES (clone) = clone_attrs (DECL_ATTRIBUTES (fn));
+      adjust_clone_attributes (fn, clone, DECL_NAME (clone), true);
       DECL_DISREGARD_INLINE_LIMITS (clone) = DECL_DISREGARD_INLINE_LIMITS (fn);
       set_decl_section_name (clone, fn);
 
+      update_sym_alias_interface (clone);
+
       /* Adjust the parameter names and locations.  */
       parm = DECL_ARGUMENTS (fn);
       clone_parm = DECL_ARGUMENTS (clone);
diff --git a/gcc/cp/rtti.cc b/gcc/cp/rtti.cc
index 7878929c246..4bb7b8c8c6e 100644
--- a/gcc/cp/rtti.cc
+++ b/gcc/cp/rtti.cc
@@ -28,8 +28,10 @@ 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"
+#include "cgraph.h"
 
 /* C++ returns type information to the user in struct type_info
    objects. We also use type information to implement dynamic_cast and
@@ -479,8 +481,13 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
 	  = build_tree_list (get_identifier ("non overlapping"),
 			     NULL_TREE);
       else
+	/* Share the non overlapping attribute, without assuming it's
+	   the only attribute, but assuming it's the last if it's
+	   present.  There may be sym aliases too, and those are not
+	   to be shared.  */
 	DECL_ATTRIBUTES (d)
-	  = DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]);
+	  = lookup_attribute ("non overlapping",
+			      DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]));
 
       /* Mark the variable as undefined -- but remember that we can
 	 define it later if we need to do so.  */
@@ -492,6 +499,16 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
       if (CLASS_TYPE_P (type))
 	CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type)) = d;
 
+      /* Copy sym alias attributes from the type to the rtti obj decl.  */
+      tree *attrs = &DECL_ATTRIBUTES (d);
+      FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+	{
+	  tree attr = tree_cons (TREE_PURPOSE (sym), TREE_VALUE (sym), *attrs);
+	  *attrs = attr;
+	  attrs = &TREE_CHAIN (attr);
+	}
+      create_sym_alias_decls (d);
+
       /* Add decl to the global array of tinfo decls.  */
       vec_safe_push (unemitted_tinfo_decls, d);
     }
@@ -499,6 +516,58 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
   return d;
 }
 
+/* After modifying the attributes of TYPE, check whether tinfo was
+   already created and, if so, add to it any sym alias attributes
+   that were not already present.  */
+
+void
+update_tinfo_sym_alias (tree type)
+{
+  if (!TYPE_SIZE (type) || !CLASS_TYPE_P (type))
+    return;
+
+  tree d = CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type));
+  if (!d)
+    return;
+
+  bool first = true;
+  symtab_node *node = NULL;
+
+  tree *attrs = &DECL_ATTRIBUTES (d);
+  FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (d_sym, *attrs)
+	if (TREE_VALUE (sym) == TREE_VALUE (d_sym))
+	  {
+	    found = true;
+	    break;
+	  }
+
+      if (found)
+	continue;
+
+      tree attr = tree_cons (TREE_PURPOSE (sym),
+			     TREE_VALUE (sym),
+			     *attrs);
+      *attrs = attr;
+      attrs = &TREE_CHAIN (attr);
+
+      if (first)
+	{
+	  first = false;
+	  node = symtab_node::get (d);
+	}
+
+      if (!node)
+	continue;
+
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+      create_sym_alias_decl (d, id);
+    }
+}
+
 /* Return the type_info object for TYPE.  */
 
 tree
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index f0c789f6cb4..b585e2d8102 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -4142,6 +4142,49 @@ Function Attributes}, @ref{PowerPC Function Attributes},
 @ref{Nios II Function Attributes}, and @ref{S/390 Function Attributes}
 for details.
 
+@cindex @code{sym_alias} function attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} attribute causes @var{name} to be output as an
+alias to the function, with the same linkage and visibility as the
+function, when the function definition is output, provided that the
+function could be an alias target.  For instance,
+
+@smallexample
+void f (uint64_t) __attribute__ ((__sym_alias__ ("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},
+with the same linkage and visibility.  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.
+
+Aliases introduced with this attribute, such as @samp{f_u64} in the
+example above, are assembly symbol names: they do not undergo C++ name
+mangling, and are not made visible in any scope in the source language.
+They can, however, be named as alias targets, as long as their
+definition is output.  Naming a @samp{sym_alias} as an alias target will
+@emph{not} cause a definition to be output if it otherwise wouldn't.
+This may affect inline functions, C++ template instantiations, and other
+synthesized definitions that would have to be (synthesized and) compiled
+in order for the @samp{sym_alias} to be considered as a potential alias
+target.
+
+This attribute requires assembler and object file support for aliases,
+and may not be available on all targets.
+
 @cindex @code{symver} function attribute
 @item symver ("@var{name2}@@@var{nodename}")
 On ELF targets this attribute creates a symbol version.  The @var{name2} part
@@ -8069,6 +8112,10 @@ will be placed in new, unique sections.
 
 This additional functionality requires Binutils version 2.36 or later.
 
+@cindex @code{sym_alias} variable attribute
+@item sym_alias ("@var{name}")
+See @pxref{Common Function Attributes}.
+
 @cindex @code{uninitialized} variable attribute
 @item uninitialized
 This attribute, attached to a variable with automatic storage, means that
@@ -9169,6 +9216,21 @@ is not supported; that is to say, if a given scalar object can be accessed
 through distinct types that assign a different storage order to it, then the
 behavior is undefined.
 
+@cindex @code{sym_alias} type attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} type 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__ ((__sym_alias__ ("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.
+
 @cindex @code{transparent_union} type attribute
 @item transparent_union
 
diff --git a/gcc/symtab.cc b/gcc/symtab.cc
index 0470509a98d..44df52095c1 100644
--- a/gcc/symtab.cc
+++ b/gcc/symtab.cc
@@ -187,6 +187,22 @@ symbol_table::insert_to_assembler_name_hash (symtab_node *node,
 	(*aslot)->previous_sharing_asm_name = node;
       *aslot = node;
 
+      /* Check for sym_alias name clashes.  In create_sym_alias_decl,
+	 we check for a preexisting definition using the same
+	 assembler_name, so here we check for a previously-defined
+	 sym_name, marked with a "sym name" pseudo-attribute that
+	 points back at the declaration for which it was created.  */
+      if (symtab_node *sym_node = node->next_sharing_asm_name)
+	if (lookup_attribute ("sym alias",
+			      DECL_ATTRIBUTES (sym_node->decl)))
+	  {
+	    error_at (DECL_SOURCE_LOCATION (node->decl),
+		      "duplicate symbol name %qE", decl);
+	    inform (DECL_SOURCE_LOCATION (sym_node->decl),
+		    "already used by %qD in a %qE attribute",
+		    sym_node->decl, get_identifier ("sym_alias"));
+	  }
+
       /* Update also possible inline clones sharing a decl.  */
       cnode = dyn_cast <cgraph_node *> (node);
       if (cnode && cnode->clones && with_clones)
@@ -1943,6 +1959,46 @@ symtab_node::noninterposable_alias (symtab_node *node, void *data)
   return false;
 }
 
+/* Remap sym alias nodes recorded as aliasing REPLACED to alias
+   REPLACEMENT instead.  */
+
+void
+symtab_node::remap_sym_alias_target (tree replaced, tree replacement)
+{
+  symtab_node *repl_node = NULL;
+  if (!decl_in_symtab_p (replacement)
+      || !(repl_node = symtab_node::get (replacement)))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (replaced))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      symtab_node *sym_node = symtab_node::get_for_asmname (id);
+
+      if (!sym_node)
+	{
+	  create_sym_alias_decl (replacement, id);
+	  continue;
+	}
+
+      gcc_assert (!sym_node->analyzed);
+      if (sym_node->alias_target != replaced)
+	continue;
+
+      // sym_node->definition = 0;
+
+      if (VAR_P (replaced))
+	// varpool_node::create_extra_name_alias (sym_node->decl, replacement);
+	varpool_node::create_alias (sym_node->decl, replacement);
+      else
+	cgraph_node::create_same_body_alias (sym_node->decl, replacement);
+	// cgraph_node::create_alias (sym_node->decl, replacement);
+      sym_node->copy_visibility_from (repl_node);
+    }
+}
+
 /* 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.  */
@@ -1970,6 +2026,12 @@ symtab_node::noninterposable_alias (void)
 
   /* Otherwise create a new one.  */
   new_decl = copy_node (node->decl);
+  DECL_ATTRIBUTES (new_decl) = remove_attribute ("sym_alias",
+						   DECL_ATTRIBUTES
+						   (new_decl));
+  DECL_ATTRIBUTES (new_decl) = remove_attribute ("sym alias",
+						   DECL_ATTRIBUTES
+						   (new_decl));
   DECL_DLLIMPORT_P (new_decl) = 0;
   tree name = clone_function_name (node->decl, "localalias");
   if (!flag_wpa)
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-1.c b/gcc/testsuite/c-c++-common/goacc/declare-1.c
index 46ee01b6759..f2842893318 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-1.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-1.c
@@ -113,11 +113,11 @@ f_2 (void)
   int va3;
 #pragma acc declare device_resident(va3)
 
-#ifndef __cplusplus
+#if 0
   /* TODO PR90868
 
-     C: "error: variable '[...]' used more than once with '#pragma acc declare'".  */
-#else
+     "error: variable '[...]' used more than once with '#pragma acc declare'".  */
+
   extern int ve0;
 #pragma acc declare create(ve0)
 
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-2.c b/gcc/testsuite/c-c++-common/goacc/declare-2.c
index e2e22be57e9..aec59b69754 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-2.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-2.c
@@ -137,25 +137,25 @@ void
 f_pr90868_2 (void)
 {
   extern int we0;
-#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" } */
 
   extern int we1;
-#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" } */
 
   extern int *we2;
-#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" } */
 
   extern int we3;
-#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" } */
 
   extern int we4;
-#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" } */
 
   extern int we5;
-#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" } */
  
   extern int we6;
-#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" } */
 }
 
 
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
new file mode 100644
index 00000000000..2fe1948aed9
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
@@ -0,0 +1,75 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+extern int var_alias __attribute__ ((__alias__ ("FOOVAR_A")));
+int var_a = 1;
+
+void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+
+void foo_alias () __attribute__ ((__alias__ ("FOOBAR_A")));
+
+void
+foo_a ()
+{
+}
+
+
+int var_b;
+extern int var_b __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+
+void
+foo_b ()
+{
+}
+
+void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+
+
+int var_c __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+
+void __attribute__ ((__sym_alias__ ("FOOBAR_C")))
+foo_c ()
+{
+}
+
+
+void baf () {}
+
+void foo_x () {
+  extern void baf () __attribute__ ((__sym_alias__ ("FOO_BAF")));
+  extern void bad () /* ok, but not defined, so no alias issued.  */
+    __attribute__ ((__sym_alias__ ("FOO_BAD")));
+  extern void bar () __attribute__ ((__sym_alias__ ("FOO_BAR")));
+
+  static int x __attribute__ ((sym_alias ("FOO_x")));
+}
+
+void bar () {}
+
+extern int xr __attribute__ ((alias ("FOO_x")));
+
+
+ /* The *u* symbols are not defined, so the sym_aliases do not clash.  ??? Is
+    this just an implementation artifact, or a feature someone might actually
+    rely on?  */
+extern int var_u1 __attribute__ ((__sym_alias__ ("VAR_U")));
+extern int var_u2 __attribute__ ((__sym_alias__ ("VAR_U")));
+
+void foo_u1 () __attribute__ ((__sym_alias__ ("FOOBAR_U")));
+void foo_u2 () __attribute__ ((__sym_alias__ ("FOOBAR_U")));
+
+
+/* { dg-final { scan-assembler "FOOBAR_A" } } */
+/* { dg-final { scan-assembler "foo_alias" } } */
+/* { dg-final { scan-assembler "FOOVAR_A" } } */
+/* { dg-final { scan-assembler "var_alias" } } */
+/* { dg-final { scan-assembler "FOOBAR_B" } } */
+/* { dg-final { scan-assembler "FOOVAR_B" } } */
+/* { dg-final { scan-assembler "FOOBAR_C" } } */
+/* { dg-final { scan-assembler "FOOVAR_C" } } */
+/* { dg-final { scan-assembler "FOO_BAF" } } */
+/* { dg-final { scan-assembler "FOO_BAR" } } */
+/* { dg-final { scan-assembler-not "FOO_BAD" } } */
+/* { dg-final { scan-assembler-not "VAR_U" } } */
+/* { dg-final { scan-assembler-not "FOOBAR_U" } } */
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
new file mode 100644
index 00000000000..f692a552606
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
@@ -0,0 +1,99 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+struct s
+{
+  int mem __attribute__ ((__sym_alias__ ("S_MEM"))); /* { dg-warning "attribute ignored" } */
+};
+
+void foo () {
+  extern void bar () __attribute__ ((__sym_alias__ ("FOO_BAR"))); /* Ok.  */
+  int i __attribute__ ((sym_alias ("FOO_i"))); /* { dg-warning "ignored" } */
+  static int x __attribute__ ((sym_alias ("FOO_x")));
+  static int sx __attribute__ ((alias ("FOO_x"))); /* { dg-warning "ignored" } */
+  extern int xx __attribute__ ((alias ("FOO_x"))); /* { dg-warning "ignored" } */
+  extern int y __attribute__ ((sym_alias ("FOO_y"))); /* Ok.  */
+}
+
+int dr __attribute__ ((alias ("FOO_x"))); /* { dg-error "defined both" } */
+
+
+int j __attribute__ ((sym_alias ("J_var")));
+void __attribute__ ((alias ("J_var")))
+j_fn (); /* { dg-error "between function and variable" } */
+
+
+void __attribute__ ((sym_alias ("K_fn")))
+k () {
+}
+extern int __attribute__ ((alias ("K_fn")))
+k_var; /* { dg-error "between function and variable" } */
+
+
+int l __attribute__ ((sym_alias ("L_fn")));
+
+/* These should be detected and reported, not because the names clash at the
+   source level, but because the asm symbols do.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+void
+L_fn () /* { dg-error "duplicate" "" } */
+{
+}
+
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __attribute__ ((sym_alias ("M_var")))
+m ()
+{
+}
+
+int M_var; /* { dg-error "duplicate" "" } */
+
+
+void __attribute__ ((sym_alias ("N_sym")))
+n_fn ()
+{
+}
+
+int __attribute__ ((sym_alias ("N_sym")))
+n_var; /* { dg-error "duplicate" } */
+
+
+int __attribute__ ((sym_alias ("O_sym")))
+o_var;
+
+void __attribute__ ((sym_alias ("O_sym")))
+o_fn () /* { dg-error "duplicate" } */
+{
+}
+
+int __attribute__ ((sym_alias ("P_sym")))
+p_var1;
+
+int __attribute__ ((sym_alias ("P_sym")))
+p_var2; /* { dg-error "duplicate" } */
+
+void __attribute__ ((sym_alias ("Q_sym")))
+q_fn1 ()
+{
+}
+
+void __attribute__ ((sym_alias ("Q_sym")))
+q_fn2 () /* { dg-error "duplicate" } */
+{
+}
+
+int __attribute__ ((sym_alias ("R_var")))
+R_var; /* { dg-error "duplicate" } */
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __attribute__ ((sym_alias ("S_fn")))
+S_fn () /* { dg-error "duplicate" } */
+{
+}
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
new file mode 100644
index 00000000000..52f8fb76496
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
@@ -0,0 +1,34 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int var_a = 1;
+
+void
+foo_a ()
+{
+  extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+}
+
+void
+foo_b ()
+{
+  extern int var_b __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+}
+
+int var_b;
+
+void __attribute__ ((__sym_alias__ ("FOOBAR_C")))
+foo_c ()
+{
+  void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+  /* Another sym for var_b.  */
+  extern int var_b __attribute__ ((__sym_alias__ ("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-sym-alias-4.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
new file mode 100644
index 00000000000..2beafa46379
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-require-alias "" } */
+
+int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A"))) = 42;
+
+int __attribute__ ((__sym_alias__ ("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/c-c++-common/torture/attr-sym-alias-5.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
new file mode 100644
index 00000000000..bee90136daf
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
@@ -0,0 +1,54 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+/* { dg-require-visibility "" } */
+
+int __attribute__ ((sym_alias ("A_hid"), visibility ("hidden"))) a;
+
+extern int __attribute__ ((sym_alias ("B_prt"))) b;
+
+int b __attribute__ ((visibility ("protected")));
+
+extern int __attribute__ ((sym_alias ("C_ntr"))) c;
+
+extern int c __attribute__ ((visibility ("internal")));
+
+int c;
+
+static int d __attribute__ ((sym_alias ("D_stt")));
+
+
+extern void __attribute__ ((sym_alias ("F_hid"), visibility ("hidden")))
+f ();
+
+void
+f () {
+}
+
+extern void __attribute__ ((sym_alias ("G_prt")))
+g ();
+
+void __attribute__ ((visibility ("protected")))
+g () {
+}
+
+extern void __attribute__ ((sym_alias ("H_ntr")))
+h ();
+
+void __attribute__ ((visibility ("internal")))
+h ();
+
+void h () {
+}
+
+static void __attribute__ ((sym_alias ("I_stt")))
+i () {
+}
+
+/* { dg-final { scan-assembler {hidden[^\n]*A_hid} } } */
+/* { dg-final { scan-assembler {protected[^\n]*B_prt} } } */
+/* { dg-final { scan-assembler {internal[^\n]*C_ntr} } } */
+/* { dg-final { scan-assembler-not {glob[^\n]*D_stt} } } */
+/* { dg-final { scan-assembler {hidden[^\n]*F_hid} } } */
+/* { dg-final { scan-assembler {protected[^\n]*G_prt} } } */
+/* { dg-final { scan-assembler {internal[^\n]*H_ntr} } } */
+/* { dg-final { scan-assembler-not {glob[^\n]*I_stt} } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
new file mode 100644
index 00000000000..51729994913
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
@@ -0,0 +1,89 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+class __attribute__ ((__sym_alias__ ("FOOCLS_A"),
+		      __sym_alias__ ("FOOCLS_A_Dupe"))) foo {
+  static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  void __attribute__ ((__sym_alias__ ("FOOBAR_A"))) bar ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+int foo::var = 1;
+
+foo::foo () {}
+
+void foo::bar () {}
+
+namespace b {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_B"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_B"))) foo ();
+    void __attribute__ ((__sym_alias__ ("FOOBAR_B"))) bar () {}
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_B"))) ~foo() {}
+  };
+
+  int foo::var = 2;
+
+  foo::foo () {
+    void (foo::*pbar)() = &foo::bar;
+  }
+}
+
+namespace c {
+  namespace cd {
+    class __attribute__ ((__sym_alias__ ("FOOCLS_C"))) foo {
+      static int var __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+      __attribute__ ((__sym_alias__ ("FOOCTR_C"))) foo () {
+	void (foo::*pbar)() = &foo::bar;
+      }
+      void __attribute__ ((__sym_alias__ ("FOOBAR_C"))) bar () {}
+      virtual __attribute__ ((__sym_alias__ ("FOODTR_C"))) ~foo() {}
+    };
+
+    int foo::var = 3;
+  }
+}
+
+#include <typeinfo>
+
+namespace d {
+  namespace {
+    class __attribute__ ((__sym_alias__ ("FOOCLS_D"))) foo {
+      virtual ~foo() {} // typeid(foo) is not defined otherwise.
+    };
+  }
+
+  extern std::type_info __attribute__ ((__alias__ ("FOOCLS_D"))) foo_d_typeinfo;
+}
+
+extern std::type_info __attribute__ ((__alias__ ("FOOCLS_C"))) foo_c_typeinfo;
+
+/* { dg-final { scan-assembler "FOOCLS_A" } } */
+/* { dg-final { scan-assembler "FOOCLS_A_Dupe" } } */
+/* { 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" } } */
+/* { dg-final { scan-assembler "FOOCLS_D" } } */
+/* { dg-final { scan-assembler "foo_d_typeinfo" } } */
+/* { dg-final { scan-assembler "foo_c_typeinfo" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
new file mode 100644
index 00000000000..6facfcc2cd9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+namespace {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_A"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo ();
+    void __attribute__ ((__sym_alias__ ("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-sym-alias-3.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
new file mode 100644
index 00000000000..42c7288ad4a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
@@ -0,0 +1,83 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+// sym can be applied to template function explicit instantiations.
+
+template <typename T>
+void
+fn(T) {
+};
+
+template void __attribute__ ((__sym_alias__ ("FOOFUN_UINT"))) fn<>(unsigned int);
+template void __attribute__ ((__sym_alias__ ("FOOFUN_LONG"))) fn<>(long);
+
+template<> void __attribute__ ((__sym_alias__ ("FOOFUN_CHAR"))) fn<>(char) {}
+
+
+template <typename T = void>
+struct
+foo {
+  virtual ~foo() {}
+
+  virtual void virtfun() {}
+
+  static void stfun() {}
+  void inlfun() {}
+};
+
+// Explicitly instantiate members before the enclosing class.
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_CHAR_VIRT"))) foo<char>::virtfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_CHAR_TI"))) foo<char>;
+
+// Though they're only output if the enclosing class is.
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_LONG_VIRT"))) foo<long>::virtfun();
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_LONG_TI_X"))) foo<long>;
+
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_VOID_ST"))) foo<void>::stfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_VOID_TI"))) foo<>;
+
+
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_X"))) foo<short>;
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_ST"))) foo<short>::stfun();
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_INL"))) foo<short>::inlfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_D"))) foo<short>;
+
+// Explicit specializations work too.
+
+template <>
+struct  __attribute__ ((__sym_alias__ ("FOOCLS_INT_TI")))
+foo<int>
+{
+  virtual ~foo() {}
+  virtual void __attribute__ ((__sym_alias__ ("FOOCLS_INT_VIRT"))) virtfun() {}
+};
+
+/* { dg-final { scan-assembler "FOOFUN_UINT" } } */
+/* { dg-final { scan-assembler "FOOFUN_LONG" } } */
+/* { dg-final { scan-assembler "FOOFUN_CHAR" } } */
+
+/* { dg-final { scan-assembler "FOOCLS_VOID_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_VOID_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_X" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_INL" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_D" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_TI_X" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_VIRT" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
new file mode 100644
index 00000000000..59b779de35d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+template <typename T = void>
+class
+__attribute__ ((__sym_alias__ ("FOOCLS")))
+foo // { dg-error "duplicate|already" }
+{
+  virtual ~foo() {}
+
+  template <typename U>
+  void
+    __attribute__ ((__sym_alias__ ("FOOTMF")))
+    tmemfun () {} // { dg-error "duplicate|already" }
+};
+
+template <typename T>
+void
+__attribute__ ((__sym_alias__ ("FOOTFN")))
+fn(T) { // { dg-error "duplicate|already" }
+};
+
+template class foo<>;
+template class foo<int>;
+template void foo<>::tmemfun<void>();
+template void foo<int>::tmemfun<void>();
+template void fn<>(int);
+template void fn<>(long);
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
new file mode 100644
index 00000000000..45d59b953c3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
@@ -0,0 +1,14 @@
+/* { dg-do compile { target c++11 } } */
+/* { dg-require-alias "" } */
+
+struct foo {
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+foo::foo () {}
+
+// Make sure the inherited cdtors don't duplicate the syms.
+struct bar : foo {
+  using foo::foo;
+};
diff --git a/gcc/varpool.cc b/gcc/varpool.cc
index e7b51b15e4a..33649906d1d 100644
--- a/gcc/varpool.cc
+++ b/gcc/varpool.cc
@@ -163,6 +163,9 @@ varpool_node::get_create (tree decl)
     }
 
   node->register_symbol ();
+
+  create_sym_alias_decls (decl);
+
   return node;
 }

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [gcc(refs/users/aoliva/heads/testme)] Introduce attribute sym_alias
@ 2023-12-14 12:47 Alexandre Oliva
  0 siblings, 0 replies; 33+ messages in thread
From: Alexandre Oliva @ 2023-12-14 12:47 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:0adee6880db60c79b599de4da267be1dfede5a42

commit 0adee6880db60c79b599de4da267be1dfede5a42
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Thu Dec 14 03:21:21 2023 -0300

    Introduce attribute sym_alias
    
    This patch introduces an attribute to add extra asm names (aliases)
    for a decl 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, and
    also to local static variables.  In C++, it can also be attached to
    class types, namespace-scoped variables and functions, static data
    members, member functions, explicit instantiations and specializations
    of template functions, members and classes.
    
    When applied to constructors or destructor, additional sym aliases
    with _Base and _Del suffixes are defined for variants other than
    complete-object ones.  This changes the assumption that clones always
    carry the same attributes as their abstract declarations, so there is
    now a function to adjust them.
    
    C++ also had a bug in which attributes from local extern declarations
    failed to be propagated to a preexisting corresponding
    namespace-scoped decl.  I've fixed that, and adjusted acc tests that
    distinguished between C and C++ in this regard.
    
    Applying the attribute to class types is only valid in C++, and the
    effect is to attach the alias to the RTTI object associated with the
    class type.
    
    
    for  gcc/ChangeLog
    
            * attribs.cc: Include cgraph.h.
            (decl_attributes): Allow late introduction of sym_alias in
            types.
            (create_sym_alias_decl, create_sym_alias_decls): New.
            * attribs.h: Declare them.
            (FOR_EACH_SYM_ALIAS): New macro.
            * cgraph.cc (cgraph_node::create): Create sym_alias decls.
            * varpool.cc (varpool_node::get_create): Create sym_alias
            decls.
            * cgraph.h (symtab_node::remap_sym_alias_target): New.
            * symtab.cc (symtab_node::remap_sym_alias_target): Define.
            (symbol_table::insert_to_assembler_name_hash): Check for
            symbol name clashes.
            (symtab_node::noninterposable_alias): Drop sym_alias
            attributes.
            * cgraphunit.cc (cgraph_node::analyze): Create alias_target
            node if needed.
            (analyze_functions): Fixup visibility of implicit alias only
            after its node is analyzed.
            * doc/extend.texi (sym_alias): Document for variables,
            functions and types.
    
    for  gcc/ada/ChangeLog
    
            * doc/gnat_rm/interfacing_to_other_languages.rst: Mention
            attribute sym_alias to give RTTI symbols mnemonic names.
            * doc/gnat_ugn/the_gnat_compilation_model.rst: Mention
            aliases.  Fix incorrect ref to C1 ctor variant.
    
    for  gcc/c-family/ChangeLog
    
            * c-ada-spec.cc (pp_asm_name): Use first sym_alias if
            available.
            * c-attribs.cc (handle_sym_alias_attribute): New.
            (c_common_attribute_table): Add sym_alias.
            (handle_copy_attribute): Do not copy sym_alias attribute.
    
    for  gcc/c/ChangeLog
    
            * c-decl.cc (duplicate_decls): Remap sym_alias target.
            (finish_decl): Create varpool_node for local static
            variables.
    
    for  gcc/cp/ChangeLog
    
            * class.cc (adjust_clone_attributes): New.
            (copy_fndecl_with_name, build_clone): Call it.
            * cp-tree.h (adjust_clone_attributes): Declare.
            (update_sym_alias_interface): Declare.
            (update_tinfo_sym_alias): Declare.
            * decl.cc (duplicate_decls): Remap sym_alias target.
            Adjust clone attributes.
            (grokfndecl): Tentatively create sym_alias decls after
            adding attributes in e.g. a template member function explicit
            instantiation.
            * decl2.cc (cplus_decl_attributes): Update tinfo sym_alias.
            (copy_interface, update_sym_alias_interface): New.
            (determine_visibility): Update sym_alias interface.
            (tentative_decl_linkage, import_export_decl): Likewise.
            * name-lookup.cc: Include target.h and cgraph.h.
            (push_local_extern_decl_alias): Merge attributes with
            namespace-scoped decl, and drop duplicate sym_alias.
            * optimize.cc (maybe_clone_body): Re-adjust attributes after
            cloning them.  Update sym_alias interface.
            * rtti.cc: Include attribs.h and cgraph.h.
            (get_tinfo_decl): Copy sym_alias attributes from type to tinfo
            decl.  Create sym_alias decls.
            (update_tinfo_sym_alias): New.
    
    for  gcc/testsuite/ChangeLog
    
            * c-c++-common/goacc/declare-1.c: Adjust.
            * c-c++-common/goacc/declare-2.c: Adjust.
            * c-c++-common/torture/attr-sym-alias-1.c: New.
            * c-c++-common/torture/attr-sym-alias-2.c: New.
            * c-c++-common/torture/attr-sym-alias-3.c: New.
            * c-c++-common/torture/attr-sym-alias-4.c: New.
            * c-c++-common/torture/attr-sym-alias-5.c: New.
            * g++.dg/torture/attr-sym-alias-1.C: New.
            * g++.dg/torture/attr-sym-alias-2.C: New.
            * g++.dg/torture/attr-sym-alias-3.C: New.
            * g++.dg/torture/attr-sym-alias-4.C: New.
            * g++.dg/torture/attr-sym-alias-5.C: New.

Diff:
---
 .../doc/gnat_rm/interfacing_to_other_languages.rst |  6 ++
 .../doc/gnat_ugn/the_gnat_compilation_model.rst    | 10 ++-
 gcc/attribs.cc                                     | 74 +++++++++++++++-
 gcc/attribs.h                                      |  7 ++
 gcc/c-family/c-ada-spec.cc                         |  7 ++
 gcc/c-family/c-attribs.cc                          | 33 +++++++-
 gcc/c/c-decl.cc                                    |  7 ++
 gcc/cgraph.cc                                      |  2 +
 gcc/cgraph.h                                       |  4 +
 gcc/cgraphunit.cc                                  |  2 +-
 gcc/cp/class.cc                                    | 64 ++++++++++++++
 gcc/cp/cp-tree.h                                   |  4 +
 gcc/cp/decl.cc                                     |  4 +
 gcc/cp/decl2.cc                                    | 53 ++++++++++++
 gcc/cp/name-lookup.cc                              | 11 +++
 gcc/cp/optimize.cc                                 |  3 +
 gcc/cp/rtti.cc                                     | 71 +++++++++++++++-
 gcc/doc/extend.texi                                | 62 ++++++++++++++
 gcc/symtab.cc                                      | 62 ++++++++++++++
 gcc/testsuite/c-c++-common/goacc/declare-1.c       |  6 +-
 gcc/testsuite/c-c++-common/goacc/declare-2.c       | 14 +--
 .../c-c++-common/torture/attr-sym-alias-1.c        | 75 ++++++++++++++++
 .../c-c++-common/torture/attr-sym-alias-2.c        | 99 ++++++++++++++++++++++
 .../c-c++-common/torture/attr-sym-alias-3.c        | 34 ++++++++
 .../c-c++-common/torture/attr-sym-alias-4.c        | 28 ++++++
 .../c-c++-common/torture/attr-sym-alias-5.c        | 54 ++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C    | 89 +++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C    | 26 ++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C    | 83 ++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C    | 28 ++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C    | 14 +++
 gcc/varpool.cc                                     |  3 +
 32 files changed, 1020 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 ad0be511d48..b8de16ebf7c 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 ``sym_alias`` 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 fd15459203a..20045698962 100644
--- a/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
+++ b/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
@@ -4274,6 +4274,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__ ((__sym_alias__ ("Ctor_For_Animal")))
         Animal() {Age_Count = 0;};
       private:
         int Age_Count;
@@ -4306,6 +4307,7 @@ both Carnivore and Domestic, that is:
         virtual int  Number_Of_Teeth ();
         virtual void Set_Owner (char* Name);
 
+        __attribute__ ((__sym_alias__ ("Ctor_For_Dog"))) // mnemonic alias
         Dog(); // Constructor
       private:
         int  Tooth_Count;
@@ -4344,7 +4346,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;
@@ -4360,7 +4363,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,7 +4385,8 @@ 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.
+mangled name (or an alias) 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.cc b/gcc/attribs.cc
index 4e313d38f0f..b0e71fccb6f 100644
--- a/gcc/attribs.cc
+++ b/gcc/attribs.cc
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "target.h"
 #include "tree.h"
+#include "cgraph.h"
 #include "stringpool.h"
 #include "diagnostic-core.h"
 #include "attribs.h"
@@ -843,7 +844,8 @@ decl_attributes (tree *node, tree attributes, int flags,
 
       if (TYPE_P (*anode)
 	  && (flags & (int) ATTR_FLAG_TYPE_IN_PLACE)
-	  && COMPLETE_TYPE_P (*anode))
+	  && COMPLETE_TYPE_P (*anode)
+	  && !is_attribute_p ("sym_alias", name))
 	{
 	  warning (OPT_Wattributes, "type attributes ignored after type is already defined");
 	  continue;
@@ -2685,6 +2687,76 @@ attr_access::array_as_string (tree type) const
   return typstr;
 }
 
+/* Create a sym attribute for DECL to be visible with linkage name ID.  */
+
+tree
+create_sym_alias_decl (tree decl, tree id)
+{
+  const char *attr_str = "sym_alias";
+
+  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;
+
+      tree attr_name = get_identifier (attr_str);
+      error_at (DECL_SOURCE_LOCATION (decl),
+		"duplicate symbol name %qE in %qE attribute of %qD",
+		id, attr_name, decl);
+      inform (DECL_SOURCE_LOCATION (sym_node->decl),
+	      "already used by %qD", sym_node->decl);
+    }
+
+  tree clone = copy_node (decl);
+  DECL_ATTRIBUTES (clone) = remove_attribute (attr_str,
+					      DECL_ATTRIBUTES (decl));
+  /* Mark it as a "sym alias" for decl, an internal attribute that
+     enables symbol_table::insert_to_assembler_name_hash can recognize
+     sym_alias decls.  */
+  DECL_ATTRIBUTES (clone) = tree_cons (get_identifier ("sym alias"),
+				       decl, DECL_ATTRIBUTES (clone));
+  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;
+
+  symtab_node *node;
+  if (VAR_P (clone))
+    // DECL_READ_P (clone) = 1;
+    // node = varpool_node::create_extra_name_alias (clone, decl);
+    node = varpool_node::create_alias (clone, decl);
+  else
+    node = cgraph_node::create_same_body_alias (clone, decl);
+    // node = cgraph_node::create_alias (clone, decl);
+  if (symtab_node *dnode = symtab_node::get_create (decl))
+    node->copy_visibility_from (dnode);
+
+  return clone;
+}
+
+/* Create decls for all sym aliases requested in DECL's attributes.  */
+
+void
+create_sym_alias_decls (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl)
+      || DECL_ABSTRACT_P (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      create_sym_alias_decl (decl, id);
+    }
+}
+
 #if CHECKING_P
 
 namespace selftest
diff --git a/gcc/attribs.h b/gcc/attribs.h
index 2c615a45663..39e0bc573dc 100644
--- a/gcc/attribs.h
+++ b/gcc/attribs.h
@@ -405,4 +405,11 @@ extern void init_attr_rdwr_indices (rdwr_map *, tree);
 extern attr_access *get_parm_access (rdwr_map &, tree,
 				     tree = current_function_decl);
 
+extern tree create_sym_alias_decl (tree, tree);
+extern void create_sym_alias_decls (tree);
+
+#define FOR_EACH_SYM_ALIAS(sym, attrs)					\
+  for (tree sym = lookup_attribute ("sym_alias", (attrs));		\
+       sym; sym = lookup_attribute ("sym_alias", TREE_CHAIN (sym)))
+
 #endif // GCC_ATTRIBS_H
diff --git a/gcc/c-family/c-ada-spec.cc b/gcc/c-family/c-ada-spec.cc
index 050994d8416..5042b9cfecd 100644
--- a/gcc/c-family/c-ada-spec.cc
+++ b/gcc/c-family/c-ada-spec.cc
@@ -1431,6 +1431,13 @@ pp_ada_tree_identifier (pretty_printer *buffer, tree node, tree type,
 static void
 pp_asm_name (pretty_printer *buffer, tree t)
 {
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (t))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.cc b/gcc/c-family/c-attribs.cc
index a3671fe3a57..29b40e6af7d 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -110,7 +110,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_sym_alias_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,
@@ -392,6 +393,8 @@ const struct attribute_spec c_common_gnu_attributes[] =
 			      handle_alias_attribute, NULL },
   { "weakref",                0, 1, true,  false, false, false,
 			      handle_weakref_attribute, NULL },
+  { "sym_alias",              1, 1, false,  false, false, false,
+			      handle_sym_alias_attribute, NULL },
   { "no_instrument_function", 0, 0, true,  false, false, false,
 			      handle_no_instrument_function_attribute,
 			      NULL },
@@ -3093,7 +3096,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
@@ -3103,7 +3106,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
@@ -3113,6 +3116,29 @@ handle_alias_attribute (tree *node, tree name, tree args,
   return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs);
 }
 
+/* Handle a "sym_alias" attribute; arguments as in struct
+   attribute_spec.handler.  */
+
+static tree
+handle_sym_alias_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 (decl_in_symtab_p (node))
+    *no_add_attrs = false;
+  else if (TYPE_P (node) && c_dialect_cxx ())
+    *no_add_attrs = false;
+  else
+    return error_mark_node;
+
+  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.  */
 
@@ -3246,6 +3272,7 @@ handle_copy_attribute (tree *node, tree name, tree args,
 	      || is_attribute_p ("visibility", atname)
 	      || is_attribute_p ("weak", atname)
 	      || is_attribute_p ("weakref", atname)
+	      || is_attribute_p ("sym_alias", atname)
 	      || is_attribute_p ("target_clones", atname))
 	    continue;
 
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 039a66fef09..eee316af9d6 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -3117,6 +3117,8 @@ duplicate_decls (tree newdecl, tree olddecl)
 
   merge_decls (newdecl, olddecl, newtype, oldtype);
 
+  symtab_node::remap_sym_alias_target (newdecl, olddecl);
+
   /* The NEWDECL will no longer be needed.
 
      Before releasing the node, be sure to remove function from symbol
@@ -5902,6 +5904,11 @@ finish_decl (tree decl, location_t init_loc, tree init,
 	    set_user_assembler_name (decl, asmspec);
 	}
 
+      /* Give attribute sym_alias a chance to make the symbol name
+	 available for aliasing, even for a static local variable.  */
+      if (VAR_P (decl) && !DECL_EXTERNAL (decl) && is_global_var (decl))
+	varpool_node::get_create (decl);
+
       if (DECL_FILE_SCOPE_P (decl))
 	{
 	  if (DECL_INITIAL (decl) == NULL_TREE
diff --git a/gcc/cgraph.cc b/gcc/cgraph.cc
index f93259a8c70..0bef2179e89 100644
--- a/gcc/cgraph.cc
+++ b/gcc/cgraph.cc
@@ -523,6 +523,8 @@ cgraph_node::create (tree decl)
   node->register_symbol ();
   maybe_record_nested_function (node);
 
+  create_sym_alias_decls (decl);
+
   return node;
 }
 
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index 2b320557616..6d61e327fb6 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -327,6 +327,10 @@ public:
   /* Return DECL that alias is aliasing.  */
   inline tree get_alias_target_tree ();
 
+  /* Remap sym alias nodes recorded as aliasing REPLACED to alias REPLACEMENT
+     instead.  */
+  static void remap_sym_alias_target (tree replaced, tree replacement);
+
   /* Set section for symbol and its aliases.  */
   void set_section (const char *section);
 
diff --git a/gcc/cgraphunit.cc b/gcc/cgraphunit.cc
index 9a550a5cce6..a66db795c78 100644
--- a/gcc/cgraphunit.cc
+++ b/gcc/cgraphunit.cc
@@ -1176,7 +1176,7 @@ analyze_functions (bool first_time)
      C++ FE is confused about the COMDAT groups being right.  */
   if (symtab->cpp_implicit_aliases_done)
     FOR_EACH_SYMBOL (node)
-      if (node->cpp_implicit_alias)
+      if (node->cpp_implicit_alias && node->analyzed)
 	  node->fixup_same_cpp_alias_visibility (node->get_alias_target ());
   build_type_inheritance_graph ();
 
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index 1954e0a5ed3..76b6b166926 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -4896,6 +4896,68 @@ check_methods (tree t)
     }
 }
 
+/* Adjust sym alias name for CLONE, cloned from FN and named NAME,
+   if it is a cdtor, and drop the sym alias from other clones.  */
+
+void
+adjust_clone_attributes (tree fn, tree clone, tree name, bool skip_copy_p)
+{
+  if (IDENTIFIER_CDTOR_P (name))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	{
+	  found = true;
+	  break;
+	}
+
+      if (found
+	  && (name == complete_ctor_identifier
+	      || name == complete_dtor_identifier))
+	{
+	  /* Reuse the sym alias decls created for the primary cdtor
+	     decl.  */
+	  symtab_node::remap_sym_alias_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);
+
+	  if (!skip_copy_p)
+	    DECL_ATTRIBUTES (clone) = copy_list (DECL_ATTRIBUTES (clone));
+
+	  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	    {
+	      /* We need to copy this even with skip_copy_p, because
+		 even then copying was shallow.  */
+	      TREE_VALUE (sym) = copy_list (TREE_VALUE (sym));
+	      /* Append suf to the sym alias name.  */
+	      tree str = TREE_VALUE (TREE_VALUE (sym));
+	      char *symname = concat (TREE_STRING_POINTER (str), suf, NULL);
+	      str = build_string (TREE_STRING_LENGTH (str) + xlen, symname);
+	      TREE_VALUE (TREE_VALUE (sym)) = str;
+	      free (symname);
+	    }
+
+	  if (symtab_node::get (clone))
+	    create_sym_alias_decls (clone);
+	}
+    }
+  else
+    DECL_ATTRIBUTES (clone)
+      = remove_attribute ("sym_alias", DECL_ATTRIBUTES (clone));
+}
+
 /* FN is constructor, destructor or operator function.  Clone the
    declaration to create a NAME'd variant.  NEED_VTT_PARM_P and
    OMIT_INHERITED_PARMS_P are relevant if it's a cdtor.  */
@@ -5065,6 +5127,8 @@ build_clone (tree fn, tree name, bool need_vtt_parm_p,
   DECL_CHAIN (clone) = DECL_CHAIN (fn);
   DECL_CHAIN (fn) = clone;
 
+  adjust_clone_attributes (fn, clone, name);
+
   return clone;
 }
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index cbf280ec454..10a31ad7c0a 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5822,6 +5822,8 @@ struct GTY((for_user)) spec_entry
 
 extern int current_class_depth;
 
+void adjust_clone_attributes (tree fn, tree clone, tree name, bool = false);
+
 /* in decl.cc */
 
 /* An array of static vars & fns.  */
@@ -7059,6 +7061,7 @@ extern void do_push_parm_decls			(tree, tree, tree *);
 extern tree do_aggregate_paren_init		(tree, tree);
 
 /* in decl2.cc */
+extern void update_sym_alias_interface		(tree);
 extern void record_mangling			(tree, bool);
 extern void overwrite_mangling			(tree, tree);
 extern void note_mangling_alias			(tree, tree);
@@ -7633,6 +7636,7 @@ extern bool emit_tinfo_decl			(tree);
 extern unsigned get_pseudo_tinfo_index		(tree);
 extern tree get_pseudo_tinfo_type		(unsigned);
 extern tree build_if_nonnull			(tree, tree, tsubst_flags_t);
+extern void update_tinfo_sym_alias		(tree);
 
 /* in search.cc */
 extern tree get_parent_with_private_access 	(tree decl, tree binfo);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 4d17ead123a..68b894912f8 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -3277,6 +3277,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
 	      && TREE_STATIC (olddecl))))
     make_decl_rtl (olddecl);
 
+  symtab_node::remap_sym_alias_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.
@@ -3300,6 +3302,7 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
       FOR_EACH_CLONE (clone, olddecl)
 	{
 	  DECL_ATTRIBUTES (clone) = DECL_ATTRIBUTES (olddecl);
+	  adjust_clone_attributes (olddecl, clone, DECL_NAME (clone));
 	  DECL_PRESERVE_P (clone) |= DECL_PRESERVE_P (olddecl);
 	}
     }
@@ -10838,6 +10841,7 @@ grokfndecl (tree ctype,
     {
       cplus_decl_attributes (&decl, *attrlist, 0);
       *attrlist = NULL_TREE;
+      create_sym_alias_decls (decl);
     }
 
   if (DECL_HAS_CONTRACTS_P (decl))
diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
index bee84879023..00f19962e5d 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -1846,6 +1846,9 @@ cplus_decl_attributes (tree *decl, tree attributes, int flags)
   if (late_attrs)
     save_template_attributes (late_attrs, decl, flags);
 
+  if (TYPE_P (*decl) && attributes)
+    update_tinfo_sym_alias (*decl);
+
   /* Propagate deprecation out to the template.  */
   if (TREE_DEPRECATED (*decl))
     if (tree ti = get_template_info (*decl))
@@ -2202,6 +2205,50 @@ 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);
+  if (symtab_node *dest_node = symtab_node::get (dest))
+    if (symtab_node *decl_node = symtab_node::get (decl))
+      decl_node->copy_visibility_from (dest_node);
+}
+
+/* Propagate linkage changes to sym aliases.  */
+
+void
+update_sym_alias_interface (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.  */
 
@@ -3009,6 +3056,8 @@ determine_visibility (tree decl)
        translation unit, we can make the type internal.  */
     constrain_visibility (decl, VISIBILITY_ANON, false);
 
+  update_sym_alias_interface (decl);
+
   /* If visibility changed and DECL already has DECL_RTL, ensure
      symbol flags are updated.  */
   if ((DECL_VISIBILITY (decl) != orig_visibility
@@ -3271,6 +3320,8 @@ tentative_decl_linkage (tree decl)
       else if (VAR_P (decl))
 	maybe_commonize_var (decl);
     }
+
+  update_sym_alias_interface (decl);
 }
 
 /* DECL is a FUNCTION_DECL or VAR_DECL.  If the object file linkage
@@ -3505,6 +3556,8 @@ import_export_decl (tree decl)
     }
 
   DECL_INTERFACE_KNOWN (decl) = 1;
+
+  update_sym_alias_interface (decl);
 }
 
 /* Return an expression that performs the destruction of DECL, which
diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc
index 09dc6ef3e5a..d11835a4552 100644
--- a/gcc/cp/name-lookup.cc
+++ b/gcc/cp/name-lookup.cc
@@ -22,9 +22,11 @@ along with GCC; see the file COPYING3.  If not see
 #define INCLUDE_MEMORY
 #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"
@@ -3675,6 +3677,15 @@ push_local_extern_decl_alias (tree decl)
 	  /* Adjust visibility.  */
 	  determine_visibility (alias);
 	}
+      else if (DECL_P (alias))
+	DECL_ATTRIBUTES (alias)
+	  = targetm.merge_decl_attributes (alias, decl);
+      if (DECL_P (alias))
+	{
+	  symtab_node::remap_sym_alias_target (decl, alias);
+	  DECL_ATTRIBUTES (decl)
+	    = remove_attribute ("sym_alias", DECL_ATTRIBUTES (alias));
+	}
     }
 
   retrofit_lang_decl (decl);
diff --git a/gcc/cp/optimize.cc b/gcc/cp/optimize.cc
index 9e8926e4cc6..4a2e8cb4354 100644
--- a/gcc/cp/optimize.cc
+++ b/gcc/cp/optimize.cc
@@ -528,9 +528,12 @@ maybe_clone_body (tree fn)
       DECL_VISIBILITY_SPECIFIED (clone) = DECL_VISIBILITY_SPECIFIED (fn);
       DECL_DLLIMPORT_P (clone) = DECL_DLLIMPORT_P (fn);
       DECL_ATTRIBUTES (clone) = clone_attrs (DECL_ATTRIBUTES (fn));
+      adjust_clone_attributes (fn, clone, DECL_NAME (clone), true);
       DECL_DISREGARD_INLINE_LIMITS (clone) = DECL_DISREGARD_INLINE_LIMITS (fn);
       set_decl_section_name (clone, fn);
 
+      update_sym_alias_interface (clone);
+
       /* Adjust the parameter names and locations.  */
       parm = DECL_ARGUMENTS (fn);
       clone_parm = DECL_ARGUMENTS (clone);
diff --git a/gcc/cp/rtti.cc b/gcc/cp/rtti.cc
index 7878929c246..4bb7b8c8c6e 100644
--- a/gcc/cp/rtti.cc
+++ b/gcc/cp/rtti.cc
@@ -28,8 +28,10 @@ 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"
+#include "cgraph.h"
 
 /* C++ returns type information to the user in struct type_info
    objects. We also use type information to implement dynamic_cast and
@@ -479,8 +481,13 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
 	  = build_tree_list (get_identifier ("non overlapping"),
 			     NULL_TREE);
       else
+	/* Share the non overlapping attribute, without assuming it's
+	   the only attribute, but assuming it's the last if it's
+	   present.  There may be sym aliases too, and those are not
+	   to be shared.  */
 	DECL_ATTRIBUTES (d)
-	  = DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]);
+	  = lookup_attribute ("non overlapping",
+			      DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]));
 
       /* Mark the variable as undefined -- but remember that we can
 	 define it later if we need to do so.  */
@@ -492,6 +499,16 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
       if (CLASS_TYPE_P (type))
 	CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type)) = d;
 
+      /* Copy sym alias attributes from the type to the rtti obj decl.  */
+      tree *attrs = &DECL_ATTRIBUTES (d);
+      FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+	{
+	  tree attr = tree_cons (TREE_PURPOSE (sym), TREE_VALUE (sym), *attrs);
+	  *attrs = attr;
+	  attrs = &TREE_CHAIN (attr);
+	}
+      create_sym_alias_decls (d);
+
       /* Add decl to the global array of tinfo decls.  */
       vec_safe_push (unemitted_tinfo_decls, d);
     }
@@ -499,6 +516,58 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
   return d;
 }
 
+/* After modifying the attributes of TYPE, check whether tinfo was
+   already created and, if so, add to it any sym alias attributes
+   that were not already present.  */
+
+void
+update_tinfo_sym_alias (tree type)
+{
+  if (!TYPE_SIZE (type) || !CLASS_TYPE_P (type))
+    return;
+
+  tree d = CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type));
+  if (!d)
+    return;
+
+  bool first = true;
+  symtab_node *node = NULL;
+
+  tree *attrs = &DECL_ATTRIBUTES (d);
+  FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (d_sym, *attrs)
+	if (TREE_VALUE (sym) == TREE_VALUE (d_sym))
+	  {
+	    found = true;
+	    break;
+	  }
+
+      if (found)
+	continue;
+
+      tree attr = tree_cons (TREE_PURPOSE (sym),
+			     TREE_VALUE (sym),
+			     *attrs);
+      *attrs = attr;
+      attrs = &TREE_CHAIN (attr);
+
+      if (first)
+	{
+	  first = false;
+	  node = symtab_node::get (d);
+	}
+
+      if (!node)
+	continue;
+
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+      create_sym_alias_decl (d, id);
+    }
+}
+
 /* Return the type_info object for TYPE.  */
 
 tree
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index f0c789f6cb4..b585e2d8102 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -4142,6 +4142,49 @@ Function Attributes}, @ref{PowerPC Function Attributes},
 @ref{Nios II Function Attributes}, and @ref{S/390 Function Attributes}
 for details.
 
+@cindex @code{sym_alias} function attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} attribute causes @var{name} to be output as an
+alias to the function, with the same linkage and visibility as the
+function, when the function definition is output, provided that the
+function could be an alias target.  For instance,
+
+@smallexample
+void f (uint64_t) __attribute__ ((__sym_alias__ ("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},
+with the same linkage and visibility.  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.
+
+Aliases introduced with this attribute, such as @samp{f_u64} in the
+example above, are assembly symbol names: they do not undergo C++ name
+mangling, and are not made visible in any scope in the source language.
+They can, however, be named as alias targets, as long as their
+definition is output.  Naming a @samp{sym_alias} as an alias target will
+@emph{not} cause a definition to be output if it otherwise wouldn't.
+This may affect inline functions, C++ template instantiations, and other
+synthesized definitions that would have to be (synthesized and) compiled
+in order for the @samp{sym_alias} to be considered as a potential alias
+target.
+
+This attribute requires assembler and object file support for aliases,
+and may not be available on all targets.
+
 @cindex @code{symver} function attribute
 @item symver ("@var{name2}@@@var{nodename}")
 On ELF targets this attribute creates a symbol version.  The @var{name2} part
@@ -8069,6 +8112,10 @@ will be placed in new, unique sections.
 
 This additional functionality requires Binutils version 2.36 or later.
 
+@cindex @code{sym_alias} variable attribute
+@item sym_alias ("@var{name}")
+See @pxref{Common Function Attributes}.
+
 @cindex @code{uninitialized} variable attribute
 @item uninitialized
 This attribute, attached to a variable with automatic storage, means that
@@ -9169,6 +9216,21 @@ is not supported; that is to say, if a given scalar object can be accessed
 through distinct types that assign a different storage order to it, then the
 behavior is undefined.
 
+@cindex @code{sym_alias} type attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} type 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__ ((__sym_alias__ ("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.
+
 @cindex @code{transparent_union} type attribute
 @item transparent_union
 
diff --git a/gcc/symtab.cc b/gcc/symtab.cc
index 0470509a98d..44df52095c1 100644
--- a/gcc/symtab.cc
+++ b/gcc/symtab.cc
@@ -187,6 +187,22 @@ symbol_table::insert_to_assembler_name_hash (symtab_node *node,
 	(*aslot)->previous_sharing_asm_name = node;
       *aslot = node;
 
+      /* Check for sym_alias name clashes.  In create_sym_alias_decl,
+	 we check for a preexisting definition using the same
+	 assembler_name, so here we check for a previously-defined
+	 sym_name, marked with a "sym name" pseudo-attribute that
+	 points back at the declaration for which it was created.  */
+      if (symtab_node *sym_node = node->next_sharing_asm_name)
+	if (lookup_attribute ("sym alias",
+			      DECL_ATTRIBUTES (sym_node->decl)))
+	  {
+	    error_at (DECL_SOURCE_LOCATION (node->decl),
+		      "duplicate symbol name %qE", decl);
+	    inform (DECL_SOURCE_LOCATION (sym_node->decl),
+		    "already used by %qD in a %qE attribute",
+		    sym_node->decl, get_identifier ("sym_alias"));
+	  }
+
       /* Update also possible inline clones sharing a decl.  */
       cnode = dyn_cast <cgraph_node *> (node);
       if (cnode && cnode->clones && with_clones)
@@ -1943,6 +1959,46 @@ symtab_node::noninterposable_alias (symtab_node *node, void *data)
   return false;
 }
 
+/* Remap sym alias nodes recorded as aliasing REPLACED to alias
+   REPLACEMENT instead.  */
+
+void
+symtab_node::remap_sym_alias_target (tree replaced, tree replacement)
+{
+  symtab_node *repl_node = NULL;
+  if (!decl_in_symtab_p (replacement)
+      || !(repl_node = symtab_node::get (replacement)))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (replaced))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      symtab_node *sym_node = symtab_node::get_for_asmname (id);
+
+      if (!sym_node)
+	{
+	  create_sym_alias_decl (replacement, id);
+	  continue;
+	}
+
+      gcc_assert (!sym_node->analyzed);
+      if (sym_node->alias_target != replaced)
+	continue;
+
+      // sym_node->definition = 0;
+
+      if (VAR_P (replaced))
+	// varpool_node::create_extra_name_alias (sym_node->decl, replacement);
+	varpool_node::create_alias (sym_node->decl, replacement);
+      else
+	cgraph_node::create_same_body_alias (sym_node->decl, replacement);
+	// cgraph_node::create_alias (sym_node->decl, replacement);
+      sym_node->copy_visibility_from (repl_node);
+    }
+}
+
 /* 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.  */
@@ -1970,6 +2026,12 @@ symtab_node::noninterposable_alias (void)
 
   /* Otherwise create a new one.  */
   new_decl = copy_node (node->decl);
+  DECL_ATTRIBUTES (new_decl) = remove_attribute ("sym_alias",
+						   DECL_ATTRIBUTES
+						   (new_decl));
+  DECL_ATTRIBUTES (new_decl) = remove_attribute ("sym alias",
+						   DECL_ATTRIBUTES
+						   (new_decl));
   DECL_DLLIMPORT_P (new_decl) = 0;
   tree name = clone_function_name (node->decl, "localalias");
   if (!flag_wpa)
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-1.c b/gcc/testsuite/c-c++-common/goacc/declare-1.c
index 46ee01b6759..f2842893318 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-1.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-1.c
@@ -113,11 +113,11 @@ f_2 (void)
   int va3;
 #pragma acc declare device_resident(va3)
 
-#ifndef __cplusplus
+#if 0
   /* TODO PR90868
 
-     C: "error: variable '[...]' used more than once with '#pragma acc declare'".  */
-#else
+     "error: variable '[...]' used more than once with '#pragma acc declare'".  */
+
   extern int ve0;
 #pragma acc declare create(ve0)
 
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-2.c b/gcc/testsuite/c-c++-common/goacc/declare-2.c
index e2e22be57e9..aec59b69754 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-2.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-2.c
@@ -137,25 +137,25 @@ void
 f_pr90868_2 (void)
 {
   extern int we0;
-#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" } */
 
   extern int we1;
-#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" } */
 
   extern int *we2;
-#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" } */
 
   extern int we3;
-#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" } */
 
   extern int we4;
-#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" } */
 
   extern int we5;
-#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" } */
  
   extern int we6;
-#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" } */
 }
 
 
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
new file mode 100644
index 00000000000..2fe1948aed9
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
@@ -0,0 +1,75 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+extern int var_alias __attribute__ ((__alias__ ("FOOVAR_A")));
+int var_a = 1;
+
+void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+
+void foo_alias () __attribute__ ((__alias__ ("FOOBAR_A")));
+
+void
+foo_a ()
+{
+}
+
+
+int var_b;
+extern int var_b __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+
+void
+foo_b ()
+{
+}
+
+void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+
+
+int var_c __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+
+void __attribute__ ((__sym_alias__ ("FOOBAR_C")))
+foo_c ()
+{
+}
+
+
+void baf () {}
+
+void foo_x () {
+  extern void baf () __attribute__ ((__sym_alias__ ("FOO_BAF")));
+  extern void bad () /* ok, but not defined, so no alias issued.  */
+    __attribute__ ((__sym_alias__ ("FOO_BAD")));
+  extern void bar () __attribute__ ((__sym_alias__ ("FOO_BAR")));
+
+  static int x __attribute__ ((sym_alias ("FOO_x")));
+}
+
+void bar () {}
+
+extern int xr __attribute__ ((alias ("FOO_x")));
+
+
+ /* The *u* symbols are not defined, so the sym_aliases do not clash.  ??? Is
+    this just an implementation artifact, or a feature someone might actually
+    rely on?  */
+extern int var_u1 __attribute__ ((__sym_alias__ ("VAR_U")));
+extern int var_u2 __attribute__ ((__sym_alias__ ("VAR_U")));
+
+void foo_u1 () __attribute__ ((__sym_alias__ ("FOOBAR_U")));
+void foo_u2 () __attribute__ ((__sym_alias__ ("FOOBAR_U")));
+
+
+/* { dg-final { scan-assembler "FOOBAR_A" } } */
+/* { dg-final { scan-assembler "foo_alias" } } */
+/* { dg-final { scan-assembler "FOOVAR_A" } } */
+/* { dg-final { scan-assembler "var_alias" } } */
+/* { dg-final { scan-assembler "FOOBAR_B" } } */
+/* { dg-final { scan-assembler "FOOVAR_B" } } */
+/* { dg-final { scan-assembler "FOOBAR_C" } } */
+/* { dg-final { scan-assembler "FOOVAR_C" } } */
+/* { dg-final { scan-assembler "FOO_BAF" } } */
+/* { dg-final { scan-assembler "FOO_BAR" } } */
+/* { dg-final { scan-assembler-not "FOO_BAD" } } */
+/* { dg-final { scan-assembler-not "VAR_U" } } */
+/* { dg-final { scan-assembler-not "FOOBAR_U" } } */
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
new file mode 100644
index 00000000000..f692a552606
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
@@ -0,0 +1,99 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+struct s
+{
+  int mem __attribute__ ((__sym_alias__ ("S_MEM"))); /* { dg-warning "attribute ignored" } */
+};
+
+void foo () {
+  extern void bar () __attribute__ ((__sym_alias__ ("FOO_BAR"))); /* Ok.  */
+  int i __attribute__ ((sym_alias ("FOO_i"))); /* { dg-warning "ignored" } */
+  static int x __attribute__ ((sym_alias ("FOO_x")));
+  static int sx __attribute__ ((alias ("FOO_x"))); /* { dg-warning "ignored" } */
+  extern int xx __attribute__ ((alias ("FOO_x"))); /* { dg-warning "ignored" } */
+  extern int y __attribute__ ((sym_alias ("FOO_y"))); /* Ok.  */
+}
+
+int dr __attribute__ ((alias ("FOO_x"))); /* { dg-error "defined both" } */
+
+
+int j __attribute__ ((sym_alias ("J_var")));
+void __attribute__ ((alias ("J_var")))
+j_fn (); /* { dg-error "between function and variable" } */
+
+
+void __attribute__ ((sym_alias ("K_fn")))
+k () {
+}
+extern int __attribute__ ((alias ("K_fn")))
+k_var; /* { dg-error "between function and variable" } */
+
+
+int l __attribute__ ((sym_alias ("L_fn")));
+
+/* These should be detected and reported, not because the names clash at the
+   source level, but because the asm symbols do.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+void
+L_fn () /* { dg-error "duplicate" "" } */
+{
+}
+
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __attribute__ ((sym_alias ("M_var")))
+m ()
+{
+}
+
+int M_var; /* { dg-error "duplicate" "" } */
+
+
+void __attribute__ ((sym_alias ("N_sym")))
+n_fn ()
+{
+}
+
+int __attribute__ ((sym_alias ("N_sym")))
+n_var; /* { dg-error "duplicate" } */
+
+
+int __attribute__ ((sym_alias ("O_sym")))
+o_var;
+
+void __attribute__ ((sym_alias ("O_sym")))
+o_fn () /* { dg-error "duplicate" } */
+{
+}
+
+int __attribute__ ((sym_alias ("P_sym")))
+p_var1;
+
+int __attribute__ ((sym_alias ("P_sym")))
+p_var2; /* { dg-error "duplicate" } */
+
+void __attribute__ ((sym_alias ("Q_sym")))
+q_fn1 ()
+{
+}
+
+void __attribute__ ((sym_alias ("Q_sym")))
+q_fn2 () /* { dg-error "duplicate" } */
+{
+}
+
+int __attribute__ ((sym_alias ("R_var")))
+R_var; /* { dg-error "duplicate" } */
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __attribute__ ((sym_alias ("S_fn")))
+S_fn () /* { dg-error "duplicate" } */
+{
+}
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
new file mode 100644
index 00000000000..52f8fb76496
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
@@ -0,0 +1,34 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int var_a = 1;
+
+void
+foo_a ()
+{
+  extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+}
+
+void
+foo_b ()
+{
+  extern int var_b __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+}
+
+int var_b;
+
+void __attribute__ ((__sym_alias__ ("FOOBAR_C")))
+foo_c ()
+{
+  void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+  /* Another sym for var_b.  */
+  extern int var_b __attribute__ ((__sym_alias__ ("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-sym-alias-4.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
new file mode 100644
index 00000000000..2beafa46379
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-require-alias "" } */
+
+int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A"))) = 42;
+
+int __attribute__ ((__sym_alias__ ("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/c-c++-common/torture/attr-sym-alias-5.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
new file mode 100644
index 00000000000..bee90136daf
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
@@ -0,0 +1,54 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+/* { dg-require-visibility "" } */
+
+int __attribute__ ((sym_alias ("A_hid"), visibility ("hidden"))) a;
+
+extern int __attribute__ ((sym_alias ("B_prt"))) b;
+
+int b __attribute__ ((visibility ("protected")));
+
+extern int __attribute__ ((sym_alias ("C_ntr"))) c;
+
+extern int c __attribute__ ((visibility ("internal")));
+
+int c;
+
+static int d __attribute__ ((sym_alias ("D_stt")));
+
+
+extern void __attribute__ ((sym_alias ("F_hid"), visibility ("hidden")))
+f ();
+
+void
+f () {
+}
+
+extern void __attribute__ ((sym_alias ("G_prt")))
+g ();
+
+void __attribute__ ((visibility ("protected")))
+g () {
+}
+
+extern void __attribute__ ((sym_alias ("H_ntr")))
+h ();
+
+void __attribute__ ((visibility ("internal")))
+h ();
+
+void h () {
+}
+
+static void __attribute__ ((sym_alias ("I_stt")))
+i () {
+}
+
+/* { dg-final { scan-assembler {hidden[^\n]*A_hid} } } */
+/* { dg-final { scan-assembler {protected[^\n]*B_prt} } } */
+/* { dg-final { scan-assembler {internal[^\n]*C_ntr} } } */
+/* { dg-final { scan-assembler-not {glob[^\n]*D_stt} } } */
+/* { dg-final { scan-assembler {hidden[^\n]*F_hid} } } */
+/* { dg-final { scan-assembler {protected[^\n]*G_prt} } } */
+/* { dg-final { scan-assembler {internal[^\n]*H_ntr} } } */
+/* { dg-final { scan-assembler-not {glob[^\n]*I_stt} } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
new file mode 100644
index 00000000000..51729994913
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
@@ -0,0 +1,89 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+class __attribute__ ((__sym_alias__ ("FOOCLS_A"),
+		      __sym_alias__ ("FOOCLS_A_Dupe"))) foo {
+  static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  void __attribute__ ((__sym_alias__ ("FOOBAR_A"))) bar ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+int foo::var = 1;
+
+foo::foo () {}
+
+void foo::bar () {}
+
+namespace b {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_B"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_B"))) foo ();
+    void __attribute__ ((__sym_alias__ ("FOOBAR_B"))) bar () {}
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_B"))) ~foo() {}
+  };
+
+  int foo::var = 2;
+
+  foo::foo () {
+    void (foo::*pbar)() = &foo::bar;
+  }
+}
+
+namespace c {
+  namespace cd {
+    class __attribute__ ((__sym_alias__ ("FOOCLS_C"))) foo {
+      static int var __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+      __attribute__ ((__sym_alias__ ("FOOCTR_C"))) foo () {
+	void (foo::*pbar)() = &foo::bar;
+      }
+      void __attribute__ ((__sym_alias__ ("FOOBAR_C"))) bar () {}
+      virtual __attribute__ ((__sym_alias__ ("FOODTR_C"))) ~foo() {}
+    };
+
+    int foo::var = 3;
+  }
+}
+
+#include <typeinfo>
+
+namespace d {
+  namespace {
+    class __attribute__ ((__sym_alias__ ("FOOCLS_D"))) foo {
+      virtual ~foo() {} // typeid(foo) is not defined otherwise.
+    };
+  }
+
+  extern std::type_info __attribute__ ((__alias__ ("FOOCLS_D"))) foo_d_typeinfo;
+}
+
+extern std::type_info __attribute__ ((__alias__ ("FOOCLS_C"))) foo_c_typeinfo;
+
+/* { dg-final { scan-assembler "FOOCLS_A" } } */
+/* { dg-final { scan-assembler "FOOCLS_A_Dupe" } } */
+/* { 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" } } */
+/* { dg-final { scan-assembler "FOOCLS_D" } } */
+/* { dg-final { scan-assembler "foo_d_typeinfo" } } */
+/* { dg-final { scan-assembler "foo_c_typeinfo" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
new file mode 100644
index 00000000000..6facfcc2cd9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+namespace {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_A"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo ();
+    void __attribute__ ((__sym_alias__ ("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-sym-alias-3.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
new file mode 100644
index 00000000000..42c7288ad4a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
@@ -0,0 +1,83 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+// sym can be applied to template function explicit instantiations.
+
+template <typename T>
+void
+fn(T) {
+};
+
+template void __attribute__ ((__sym_alias__ ("FOOFUN_UINT"))) fn<>(unsigned int);
+template void __attribute__ ((__sym_alias__ ("FOOFUN_LONG"))) fn<>(long);
+
+template<> void __attribute__ ((__sym_alias__ ("FOOFUN_CHAR"))) fn<>(char) {}
+
+
+template <typename T = void>
+struct
+foo {
+  virtual ~foo() {}
+
+  virtual void virtfun() {}
+
+  static void stfun() {}
+  void inlfun() {}
+};
+
+// Explicitly instantiate members before the enclosing class.
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_CHAR_VIRT"))) foo<char>::virtfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_CHAR_TI"))) foo<char>;
+
+// Though they're only output if the enclosing class is.
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_LONG_VIRT"))) foo<long>::virtfun();
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_LONG_TI_X"))) foo<long>;
+
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_VOID_ST"))) foo<void>::stfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_VOID_TI"))) foo<>;
+
+
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_X"))) foo<short>;
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_ST"))) foo<short>::stfun();
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_INL"))) foo<short>::inlfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_D"))) foo<short>;
+
+// Explicit specializations work too.
+
+template <>
+struct  __attribute__ ((__sym_alias__ ("FOOCLS_INT_TI")))
+foo<int>
+{
+  virtual ~foo() {}
+  virtual void __attribute__ ((__sym_alias__ ("FOOCLS_INT_VIRT"))) virtfun() {}
+};
+
+/* { dg-final { scan-assembler "FOOFUN_UINT" } } */
+/* { dg-final { scan-assembler "FOOFUN_LONG" } } */
+/* { dg-final { scan-assembler "FOOFUN_CHAR" } } */
+
+/* { dg-final { scan-assembler "FOOCLS_VOID_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_VOID_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_X" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_INL" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_D" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_TI_X" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_VIRT" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
new file mode 100644
index 00000000000..59b779de35d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+template <typename T = void>
+class
+__attribute__ ((__sym_alias__ ("FOOCLS")))
+foo // { dg-error "duplicate|already" }
+{
+  virtual ~foo() {}
+
+  template <typename U>
+  void
+    __attribute__ ((__sym_alias__ ("FOOTMF")))
+    tmemfun () {} // { dg-error "duplicate|already" }
+};
+
+template <typename T>
+void
+__attribute__ ((__sym_alias__ ("FOOTFN")))
+fn(T) { // { dg-error "duplicate|already" }
+};
+
+template class foo<>;
+template class foo<int>;
+template void foo<>::tmemfun<void>();
+template void foo<int>::tmemfun<void>();
+template void fn<>(int);
+template void fn<>(long);
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
new file mode 100644
index 00000000000..45d59b953c3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
@@ -0,0 +1,14 @@
+/* { dg-do compile { target c++11 } } */
+/* { dg-require-alias "" } */
+
+struct foo {
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+foo::foo () {}
+
+// Make sure the inherited cdtors don't duplicate the syms.
+struct bar : foo {
+  using foo::foo;
+};
diff --git a/gcc/varpool.cc b/gcc/varpool.cc
index e7b51b15e4a..33649906d1d 100644
--- a/gcc/varpool.cc
+++ b/gcc/varpool.cc
@@ -163,6 +163,9 @@ varpool_node::get_create (tree decl)
     }
 
   node->register_symbol ();
+
+  create_sym_alias_decls (decl);
+
   return node;
 }

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [gcc(refs/users/aoliva/heads/testme)] Introduce attribute sym_alias
@ 2023-12-12 20:23 Alexandre Oliva
  0 siblings, 0 replies; 33+ messages in thread
From: Alexandre Oliva @ 2023-12-12 20:23 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:4b73e23db244b54610b23d2dc4c190d11b2d8ec0

commit 4b73e23db244b54610b23d2dc4c190d11b2d8ec0
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Tue Dec 12 00:08:57 2023 -0300

    Introduce attribute sym_alias
    
    This patch introduces an attribute to add extra asm names (aliases)
    for a decl 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, and
    also to local static variables.  In C++, it can also be attached to
    class types, namespace-scoped variables and functions, static data
    members, member functions, explicit instantiations and specializations
    of template functions, members and classes.
    
    When applied to constructors or destructor, additional sym aliases
    with _Base and _Del suffixes are defined for variants other than
    complete-object ones.  This changes the assumption that clones always
    carry the same attributes as their abstract declarations, so there is
    now a function to adjust them.
    
    C++ also had a bug in which attributes from local extern declarations
    failed to be propagated to a preexisting corresponding
    namespace-scoped decl.  I've fixed that, and adjusted acc tests that
    distinguished between C and C++ in this regard.
    
    Applying the attribute to class types is only valid in C++, and the
    effect is to attach the alias to the RTTI object associated with the
    class type.
    
    
    for  gcc/ChangeLog
    
            * attribs.cc: Include cgraph.h.
            (decl_attributes): Allow late introduction of sym_alias in
            types.
            (create_sym_alias_decl, create_sym_alias_decls): New.
            * attribs.h: Declare them.
            (FOR_EACH_SYM_ALIAS): New macro.
            * cgraph.cc (cgraph_node::create): Create sym_alias decls.
            * varpool.cc (varpool_node::get_create): Create sym_alias
            decls.
            * cgraph.h (symtab_node::remap_sym_alias_target): New.
            * symtab.cc (symtab_node::remap_sym_alias_target): Define.
            (symbol_table::insert_to_assembler_name_hash): Check for
            symbol name clashes.
            (symtab_node::noninterposable_alias): Drop sym_alias
            attributes.
            * cgraphunit.cc (cgraph_node::analyze): Create alias_target
            node if needed.
            (analyze_functions): Fixup visibility of implicit alias only
            after its node is analyzed.
            * doc/extend.texi (sym_alias): Document for variables,
            functions and types.
    
    for  gcc/ada/ChangeLog
    
            * doc/gnat_rm/interfacing_to_other_languages.rst: Mention
            attribute sym_alias to give RTTI symbols mnemonic names.
            * doc/gnat_ugn/the_gnat_compilation_model.rst: Mention
            aliases.  Fix incorrect ref to C1 ctor variant.
    
    for  gcc/c-family/ChangeLog
    
            * c-ada-spec.cc (pp_asm_name): Use first sym_alias if
            available.
            * c-attribs.cc (handle_sym_alias_attribute): New.
            (c_common_attribute_table): Add sym_alias.
            (handle_copy_attribute): Do not copy sym_alias attribute.
    
    for  gcc/c/ChangeLog
    
            * c-decl.cc (duplicate_decls): Remap sym_alias target.
            (finish_decl): Create varpool_node for local static
            variables.
    
    for  gcc/cp/ChangeLog
    
            * class.cc (adjust_clone_attributes): New.
            (copy_fndecl_with_name, build_clone): Call it.
            * cp-tree.h (adjust_clone_attributes): Declare.
            (update_sym_alias_interface): Declare.
            (update_tinfo_sym_alias): Declare.
            * decl.cc (duplicate_decls): Remap sym_alias target.
            Adjust clone attributes.
            (grokfndecl): Tentatively create sym_alias decls after
            adding attributes in e.g. a template member function explicit
            instantiation.
            * decl2.cc (cplus_decl_attributes): Update tinfo sym_alias.
            (copy_interface, update_sym_alias_interface): New.
            (determine_visibility): Update sym_alias interface.
            (tentative_decl_linkage, import_export_decl): Likewise.
            * name-lookup.cc: Include target.h and cgraph.h.
            (push_local_extern_decl_alias): Merge attributes with
            namespace-scoped decl, and drop duplicate sym_alias.
            * optimize.cc (maybe_clone_body): Re-adjust attributes after
            cloning them.  Update sym_alias interface.
            * rtti.cc: Include attribs.h and cgraph.h.
            (get_tinfo_decl): Copy sym_alias attributes from type to tinfo
            decl.  Create sym_alias decls.
            (update_tinfo_sym_alias): New.
    
    for  gcc/testsuite/ChangeLog
    
            * c-c++-common/goacc/declare-1.c: Adjust.
            * c-c++-common/goacc/declare-2.c: Adjust.
            * c-c++-common/torture/attr-sym-alias-1.c: New.
            * c-c++-common/torture/attr-sym-alias-2.c: New.
            * c-c++-common/torture/attr-sym-alias-3.c: New.
            * c-c++-common/torture/attr-sym-alias-4.c: New.
            * c-c++-common/torture/attr-sym-alias-5.c: New.
            * g++.dg/torture/attr-sym-alias-1.C: New.
            * g++.dg/torture/attr-sym-alias-2.C: New.
            * g++.dg/torture/attr-sym-alias-3.C: New.
            * g++.dg/torture/attr-sym-alias-4.C: New.
            * g++.dg/torture/attr-sym-alias-5.C: New.

Diff:
---
 .../doc/gnat_rm/interfacing_to_other_languages.rst |  6 ++
 .../doc/gnat_ugn/the_gnat_compilation_model.rst    | 10 ++-
 gcc/attribs.cc                                     | 74 +++++++++++++++-
 gcc/attribs.h                                      |  7 ++
 gcc/c-family/c-ada-spec.cc                         |  7 ++
 gcc/c-family/c-attribs.cc                          | 33 +++++++-
 gcc/c/c-decl.cc                                    |  7 ++
 gcc/cgraph.cc                                      |  2 +
 gcc/cgraph.h                                       |  4 +
 gcc/cgraphunit.cc                                  |  2 +-
 gcc/cp/class.cc                                    | 64 ++++++++++++++
 gcc/cp/cp-tree.h                                   |  4 +
 gcc/cp/decl.cc                                     |  4 +
 gcc/cp/decl2.cc                                    | 53 ++++++++++++
 gcc/cp/name-lookup.cc                              | 11 +++
 gcc/cp/optimize.cc                                 |  3 +
 gcc/cp/rtti.cc                                     | 71 +++++++++++++++-
 gcc/doc/extend.texi                                | 62 ++++++++++++++
 gcc/symtab.cc                                      | 62 ++++++++++++++
 gcc/testsuite/c-c++-common/goacc/declare-1.c       |  6 +-
 gcc/testsuite/c-c++-common/goacc/declare-2.c       | 14 +--
 .../c-c++-common/torture/attr-sym-alias-1.c        | 75 ++++++++++++++++
 .../c-c++-common/torture/attr-sym-alias-2.c        | 99 ++++++++++++++++++++++
 .../c-c++-common/torture/attr-sym-alias-3.c        | 34 ++++++++
 .../c-c++-common/torture/attr-sym-alias-4.c        | 28 ++++++
 .../c-c++-common/torture/attr-sym-alias-5.c        | 54 ++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C    | 89 +++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C    | 26 ++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C    | 83 ++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C    | 28 ++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C    | 14 +++
 gcc/varpool.cc                                     |  3 +
 32 files changed, 1020 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 ad0be511d48..b8de16ebf7c 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 ``sym_alias`` 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 fd15459203a..20045698962 100644
--- a/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
+++ b/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
@@ -4274,6 +4274,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__ ((__sym_alias__ ("Ctor_For_Animal")))
         Animal() {Age_Count = 0;};
       private:
         int Age_Count;
@@ -4306,6 +4307,7 @@ both Carnivore and Domestic, that is:
         virtual int  Number_Of_Teeth ();
         virtual void Set_Owner (char* Name);
 
+        __attribute__ ((__sym_alias__ ("Ctor_For_Dog"))) // mnemonic alias
         Dog(); // Constructor
       private:
         int  Tooth_Count;
@@ -4344,7 +4346,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;
@@ -4360,7 +4363,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,7 +4385,8 @@ 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.
+mangled name (or an alias) 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.cc b/gcc/attribs.cc
index 776655dde00..648674fbf20 100644
--- a/gcc/attribs.cc
+++ b/gcc/attribs.cc
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "target.h"
 #include "tree.h"
+#include "cgraph.h"
 #include "stringpool.h"
 #include "diagnostic-core.h"
 #include "attribs.h"
@@ -843,7 +844,8 @@ decl_attributes (tree *node, tree attributes, int flags,
 
       if (TYPE_P (*anode)
 	  && (flags & (int) ATTR_FLAG_TYPE_IN_PLACE)
-	  && COMPLETE_TYPE_P (*anode))
+	  && COMPLETE_TYPE_P (*anode)
+	  && !is_attribute_p ("sym_alias", name))
 	{
 	  warning (OPT_Wattributes, "type attributes ignored after type is already defined");
 	  continue;
@@ -2685,6 +2687,76 @@ attr_access::array_as_string (tree type) const
   return typstr;
 }
 
+/* Create a sym attribute for DECL to be visible with linkage name ID.  */
+
+tree
+create_sym_alias_decl (tree decl, tree id)
+{
+  const char *attr_str = "sym_alias";
+
+  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;
+
+      tree attr_name = get_identifier (attr_str);
+      error_at (DECL_SOURCE_LOCATION (decl),
+		"duplicate symbol name %qE in %qE attribute of %qD",
+		id, attr_name, decl);
+      inform (DECL_SOURCE_LOCATION (sym_node->decl),
+	      "already used by %qD", sym_node->decl);
+    }
+
+  tree clone = copy_node (decl);
+  DECL_ATTRIBUTES (clone) = remove_attribute (attr_str,
+					      DECL_ATTRIBUTES (decl));
+  /* Mark it as a "sym alias" for decl, an internal attribute that
+     enables symbol_table::insert_to_assembler_name_hash can recognize
+     sym_alias decls.  */
+  DECL_ATTRIBUTES (clone) = tree_cons (get_identifier ("sym alias"),
+				       decl, DECL_ATTRIBUTES (clone));
+  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;
+
+  symtab_node *node;
+  if (VAR_P (clone))
+    // DECL_READ_P (clone) = 1;
+    // node = varpool_node::create_extra_name_alias (clone, decl);
+    node = varpool_node::create_alias (clone, decl);
+  else
+    node = cgraph_node::create_same_body_alias (clone, decl);
+    // node = cgraph_node::create_alias (clone, decl);
+  if (symtab_node *dnode = symtab_node::get_create (decl))
+    node->copy_visibility_from (dnode);
+
+  return clone;
+}
+
+/* Create decls for all sym aliases requested in DECL's attributes.  */
+
+void
+create_sym_alias_decls (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl)
+      || DECL_ABSTRACT_P (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      create_sym_alias_decl (decl, id);
+    }
+}
+
 #if CHECKING_P
 
 namespace selftest
diff --git a/gcc/attribs.h b/gcc/attribs.h
index 2c615a45663..39e0bc573dc 100644
--- a/gcc/attribs.h
+++ b/gcc/attribs.h
@@ -405,4 +405,11 @@ extern void init_attr_rdwr_indices (rdwr_map *, tree);
 extern attr_access *get_parm_access (rdwr_map &, tree,
 				     tree = current_function_decl);
 
+extern tree create_sym_alias_decl (tree, tree);
+extern void create_sym_alias_decls (tree);
+
+#define FOR_EACH_SYM_ALIAS(sym, attrs)					\
+  for (tree sym = lookup_attribute ("sym_alias", (attrs));		\
+       sym; sym = lookup_attribute ("sym_alias", TREE_CHAIN (sym)))
+
 #endif // GCC_ATTRIBS_H
diff --git a/gcc/c-family/c-ada-spec.cc b/gcc/c-family/c-ada-spec.cc
index 050994d8416..5042b9cfecd 100644
--- a/gcc/c-family/c-ada-spec.cc
+++ b/gcc/c-family/c-ada-spec.cc
@@ -1431,6 +1431,13 @@ pp_ada_tree_identifier (pretty_printer *buffer, tree node, tree type,
 static void
 pp_asm_name (pretty_printer *buffer, tree t)
 {
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (t))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.cc b/gcc/c-family/c-attribs.cc
index 854e987dc79..2593fde1fe9 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -110,7 +110,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_sym_alias_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,
@@ -392,6 +393,8 @@ const struct attribute_spec c_common_gnu_attributes[] =
 			      handle_alias_attribute, NULL },
   { "weakref",                0, 1, true,  false, false, false,
 			      handle_weakref_attribute, NULL },
+  { "sym_alias",              1, 1, false,  false, false, false,
+			      handle_sym_alias_attribute, NULL },
   { "no_instrument_function", 0, 0, true,  false, false, false,
 			      handle_no_instrument_function_attribute,
 			      NULL },
@@ -3092,7 +3095,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
@@ -3102,7 +3105,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
@@ -3112,6 +3115,29 @@ handle_alias_attribute (tree *node, tree name, tree args,
   return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs);
 }
 
+/* Handle a "sym_alias" attribute; arguments as in struct
+   attribute_spec.handler.  */
+
+static tree
+handle_sym_alias_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 (decl_in_symtab_p (node))
+    *no_add_attrs = false;
+  else if (TYPE_P (node) && c_dialect_cxx ())
+    *no_add_attrs = false;
+  else
+    return error_mark_node;
+
+  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.  */
 
@@ -3245,6 +3271,7 @@ handle_copy_attribute (tree *node, tree name, tree args,
 	      || is_attribute_p ("visibility", atname)
 	      || is_attribute_p ("weak", atname)
 	      || is_attribute_p ("weakref", atname)
+	      || is_attribute_p ("sym_alias", atname)
 	      || is_attribute_p ("target_clones", atname))
 	    continue;
 
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 039a66fef09..eee316af9d6 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -3117,6 +3117,8 @@ duplicate_decls (tree newdecl, tree olddecl)
 
   merge_decls (newdecl, olddecl, newtype, oldtype);
 
+  symtab_node::remap_sym_alias_target (newdecl, olddecl);
+
   /* The NEWDECL will no longer be needed.
 
      Before releasing the node, be sure to remove function from symbol
@@ -5902,6 +5904,11 @@ finish_decl (tree decl, location_t init_loc, tree init,
 	    set_user_assembler_name (decl, asmspec);
 	}
 
+      /* Give attribute sym_alias a chance to make the symbol name
+	 available for aliasing, even for a static local variable.  */
+      if (VAR_P (decl) && !DECL_EXTERNAL (decl) && is_global_var (decl))
+	varpool_node::get_create (decl);
+
       if (DECL_FILE_SCOPE_P (decl))
 	{
 	  if (DECL_INITIAL (decl) == NULL_TREE
diff --git a/gcc/cgraph.cc b/gcc/cgraph.cc
index f93259a8c70..0bef2179e89 100644
--- a/gcc/cgraph.cc
+++ b/gcc/cgraph.cc
@@ -523,6 +523,8 @@ cgraph_node::create (tree decl)
   node->register_symbol ();
   maybe_record_nested_function (node);
 
+  create_sym_alias_decls (decl);
+
   return node;
 }
 
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index 2b320557616..6d61e327fb6 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -327,6 +327,10 @@ public:
   /* Return DECL that alias is aliasing.  */
   inline tree get_alias_target_tree ();
 
+  /* Remap sym alias nodes recorded as aliasing REPLACED to alias REPLACEMENT
+     instead.  */
+  static void remap_sym_alias_target (tree replaced, tree replacement);
+
   /* Set section for symbol and its aliases.  */
   void set_section (const char *section);
 
diff --git a/gcc/cgraphunit.cc b/gcc/cgraphunit.cc
index 9a550a5cce6..a66db795c78 100644
--- a/gcc/cgraphunit.cc
+++ b/gcc/cgraphunit.cc
@@ -1176,7 +1176,7 @@ analyze_functions (bool first_time)
      C++ FE is confused about the COMDAT groups being right.  */
   if (symtab->cpp_implicit_aliases_done)
     FOR_EACH_SYMBOL (node)
-      if (node->cpp_implicit_alias)
+      if (node->cpp_implicit_alias && node->analyzed)
 	  node->fixup_same_cpp_alias_visibility (node->get_alias_target ());
   build_type_inheritance_graph ();
 
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index 6fdb56abfb9..fa8fc63db82 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -4896,6 +4896,68 @@ check_methods (tree t)
     }
 }
 
+/* Adjust sym alias name for CLONE, cloned from FN and named NAME,
+   if it is a cdtor, and drop the sym alias from other clones.  */
+
+void
+adjust_clone_attributes (tree fn, tree clone, tree name, bool skip_copy_p)
+{
+  if (IDENTIFIER_CDTOR_P (name))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	{
+	  found = true;
+	  break;
+	}
+
+      if (found
+	  && (name == complete_ctor_identifier
+	      || name == complete_dtor_identifier))
+	{
+	  /* Reuse the sym alias decls created for the primary cdtor
+	     decl.  */
+	  symtab_node::remap_sym_alias_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);
+
+	  if (!skip_copy_p)
+	    DECL_ATTRIBUTES (clone) = copy_list (DECL_ATTRIBUTES (clone));
+
+	  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	    {
+	      /* We need to copy this even with skip_copy_p, because
+		 even then copying was shallow.  */
+	      TREE_VALUE (sym) = copy_list (TREE_VALUE (sym));
+	      /* Append suf to the sym alias name.  */
+	      tree str = TREE_VALUE (TREE_VALUE (sym));
+	      char *symname = concat (TREE_STRING_POINTER (str), suf, NULL);
+	      str = build_string (TREE_STRING_LENGTH (str) + xlen, symname);
+	      TREE_VALUE (TREE_VALUE (sym)) = str;
+	      free (symname);
+	    }
+
+	  if (symtab_node::get (clone))
+	    create_sym_alias_decls (clone);
+	}
+    }
+  else
+    DECL_ATTRIBUTES (clone)
+      = remove_attribute ("sym_alias", DECL_ATTRIBUTES (clone));
+}
+
 /* FN is constructor, destructor or operator function.  Clone the
    declaration to create a NAME'd variant.  NEED_VTT_PARM_P and
    OMIT_INHERITED_PARMS_P are relevant if it's a cdtor.  */
@@ -5065,6 +5127,8 @@ build_clone (tree fn, tree name, bool need_vtt_parm_p,
   DECL_CHAIN (clone) = DECL_CHAIN (fn);
   DECL_CHAIN (fn) = clone;
 
+  adjust_clone_attributes (fn, clone, name);
+
   return clone;
 }
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index cbf280ec454..10a31ad7c0a 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5822,6 +5822,8 @@ struct GTY((for_user)) spec_entry
 
 extern int current_class_depth;
 
+void adjust_clone_attributes (tree fn, tree clone, tree name, bool = false);
+
 /* in decl.cc */
 
 /* An array of static vars & fns.  */
@@ -7059,6 +7061,7 @@ extern void do_push_parm_decls			(tree, tree, tree *);
 extern tree do_aggregate_paren_init		(tree, tree);
 
 /* in decl2.cc */
+extern void update_sym_alias_interface		(tree);
 extern void record_mangling			(tree, bool);
 extern void overwrite_mangling			(tree, tree);
 extern void note_mangling_alias			(tree, tree);
@@ -7633,6 +7636,7 @@ extern bool emit_tinfo_decl			(tree);
 extern unsigned get_pseudo_tinfo_index		(tree);
 extern tree get_pseudo_tinfo_type		(unsigned);
 extern tree build_if_nonnull			(tree, tree, tsubst_flags_t);
+extern void update_tinfo_sym_alias		(tree);
 
 /* in search.cc */
 extern tree get_parent_with_private_access 	(tree decl, tree binfo);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index b1ada1d5215..14d12f66562 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -3277,6 +3277,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
 	      && TREE_STATIC (olddecl))))
     make_decl_rtl (olddecl);
 
+  symtab_node::remap_sym_alias_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.
@@ -3300,6 +3302,7 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
       FOR_EACH_CLONE (clone, olddecl)
 	{
 	  DECL_ATTRIBUTES (clone) = DECL_ATTRIBUTES (olddecl);
+	  adjust_clone_attributes (olddecl, clone, DECL_NAME (clone));
 	  DECL_PRESERVE_P (clone) |= DECL_PRESERVE_P (olddecl);
 	}
     }
@@ -10838,6 +10841,7 @@ grokfndecl (tree ctype,
     {
       cplus_decl_attributes (&decl, *attrlist, 0);
       *attrlist = NULL_TREE;
+      create_sym_alias_decls (decl);
     }
 
   if (DECL_HAS_CONTRACTS_P (decl))
diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
index bee84879023..00f19962e5d 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -1846,6 +1846,9 @@ cplus_decl_attributes (tree *decl, tree attributes, int flags)
   if (late_attrs)
     save_template_attributes (late_attrs, decl, flags);
 
+  if (TYPE_P (*decl) && attributes)
+    update_tinfo_sym_alias (*decl);
+
   /* Propagate deprecation out to the template.  */
   if (TREE_DEPRECATED (*decl))
     if (tree ti = get_template_info (*decl))
@@ -2202,6 +2205,50 @@ 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);
+  if (symtab_node *dest_node = symtab_node::get (dest))
+    if (symtab_node *decl_node = symtab_node::get (decl))
+      decl_node->copy_visibility_from (dest_node);
+}
+
+/* Propagate linkage changes to sym aliases.  */
+
+void
+update_sym_alias_interface (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.  */
 
@@ -3009,6 +3056,8 @@ determine_visibility (tree decl)
        translation unit, we can make the type internal.  */
     constrain_visibility (decl, VISIBILITY_ANON, false);
 
+  update_sym_alias_interface (decl);
+
   /* If visibility changed and DECL already has DECL_RTL, ensure
      symbol flags are updated.  */
   if ((DECL_VISIBILITY (decl) != orig_visibility
@@ -3271,6 +3320,8 @@ tentative_decl_linkage (tree decl)
       else if (VAR_P (decl))
 	maybe_commonize_var (decl);
     }
+
+  update_sym_alias_interface (decl);
 }
 
 /* DECL is a FUNCTION_DECL or VAR_DECL.  If the object file linkage
@@ -3505,6 +3556,8 @@ import_export_decl (tree decl)
     }
 
   DECL_INTERFACE_KNOWN (decl) = 1;
+
+  update_sym_alias_interface (decl);
 }
 
 /* Return an expression that performs the destruction of DECL, which
diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc
index 09dc6ef3e5a..d11835a4552 100644
--- a/gcc/cp/name-lookup.cc
+++ b/gcc/cp/name-lookup.cc
@@ -22,9 +22,11 @@ along with GCC; see the file COPYING3.  If not see
 #define INCLUDE_MEMORY
 #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"
@@ -3675,6 +3677,15 @@ push_local_extern_decl_alias (tree decl)
 	  /* Adjust visibility.  */
 	  determine_visibility (alias);
 	}
+      else if (DECL_P (alias))
+	DECL_ATTRIBUTES (alias)
+	  = targetm.merge_decl_attributes (alias, decl);
+      if (DECL_P (alias))
+	{
+	  symtab_node::remap_sym_alias_target (decl, alias);
+	  DECL_ATTRIBUTES (decl)
+	    = remove_attribute ("sym_alias", DECL_ATTRIBUTES (alias));
+	}
     }
 
   retrofit_lang_decl (decl);
diff --git a/gcc/cp/optimize.cc b/gcc/cp/optimize.cc
index 9e8926e4cc6..4a2e8cb4354 100644
--- a/gcc/cp/optimize.cc
+++ b/gcc/cp/optimize.cc
@@ -528,9 +528,12 @@ maybe_clone_body (tree fn)
       DECL_VISIBILITY_SPECIFIED (clone) = DECL_VISIBILITY_SPECIFIED (fn);
       DECL_DLLIMPORT_P (clone) = DECL_DLLIMPORT_P (fn);
       DECL_ATTRIBUTES (clone) = clone_attrs (DECL_ATTRIBUTES (fn));
+      adjust_clone_attributes (fn, clone, DECL_NAME (clone), true);
       DECL_DISREGARD_INLINE_LIMITS (clone) = DECL_DISREGARD_INLINE_LIMITS (fn);
       set_decl_section_name (clone, fn);
 
+      update_sym_alias_interface (clone);
+
       /* Adjust the parameter names and locations.  */
       parm = DECL_ARGUMENTS (fn);
       clone_parm = DECL_ARGUMENTS (clone);
diff --git a/gcc/cp/rtti.cc b/gcc/cp/rtti.cc
index 7878929c246..4bb7b8c8c6e 100644
--- a/gcc/cp/rtti.cc
+++ b/gcc/cp/rtti.cc
@@ -28,8 +28,10 @@ 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"
+#include "cgraph.h"
 
 /* C++ returns type information to the user in struct type_info
    objects. We also use type information to implement dynamic_cast and
@@ -479,8 +481,13 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
 	  = build_tree_list (get_identifier ("non overlapping"),
 			     NULL_TREE);
       else
+	/* Share the non overlapping attribute, without assuming it's
+	   the only attribute, but assuming it's the last if it's
+	   present.  There may be sym aliases too, and those are not
+	   to be shared.  */
 	DECL_ATTRIBUTES (d)
-	  = DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]);
+	  = lookup_attribute ("non overlapping",
+			      DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]));
 
       /* Mark the variable as undefined -- but remember that we can
 	 define it later if we need to do so.  */
@@ -492,6 +499,16 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
       if (CLASS_TYPE_P (type))
 	CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type)) = d;
 
+      /* Copy sym alias attributes from the type to the rtti obj decl.  */
+      tree *attrs = &DECL_ATTRIBUTES (d);
+      FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+	{
+	  tree attr = tree_cons (TREE_PURPOSE (sym), TREE_VALUE (sym), *attrs);
+	  *attrs = attr;
+	  attrs = &TREE_CHAIN (attr);
+	}
+      create_sym_alias_decls (d);
+
       /* Add decl to the global array of tinfo decls.  */
       vec_safe_push (unemitted_tinfo_decls, d);
     }
@@ -499,6 +516,58 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
   return d;
 }
 
+/* After modifying the attributes of TYPE, check whether tinfo was
+   already created and, if so, add to it any sym alias attributes
+   that were not already present.  */
+
+void
+update_tinfo_sym_alias (tree type)
+{
+  if (!TYPE_SIZE (type) || !CLASS_TYPE_P (type))
+    return;
+
+  tree d = CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type));
+  if (!d)
+    return;
+
+  bool first = true;
+  symtab_node *node = NULL;
+
+  tree *attrs = &DECL_ATTRIBUTES (d);
+  FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (d_sym, *attrs)
+	if (TREE_VALUE (sym) == TREE_VALUE (d_sym))
+	  {
+	    found = true;
+	    break;
+	  }
+
+      if (found)
+	continue;
+
+      tree attr = tree_cons (TREE_PURPOSE (sym),
+			     TREE_VALUE (sym),
+			     *attrs);
+      *attrs = attr;
+      attrs = &TREE_CHAIN (attr);
+
+      if (first)
+	{
+	  first = false;
+	  node = symtab_node::get (d);
+	}
+
+      if (!node)
+	continue;
+
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+      create_sym_alias_decl (d, id);
+    }
+}
+
 /* Return the type_info object for TYPE.  */
 
 tree
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index e8b5e771f7a..d33ee51feb9 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -4142,6 +4142,49 @@ Function Attributes}, @ref{PowerPC Function Attributes},
 @ref{Nios II Function Attributes}, and @ref{S/390 Function Attributes}
 for details.
 
+@cindex @code{sym_alias} function attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} attribute causes @var{name} to be output as an
+alias to the function, with the same linkage and visibility as the
+function, when the function definition is output, provided that the
+function could be an alias target.  For instance,
+
+@smallexample
+void f (uint64_t) __attribute__ ((__sym_alias__ ("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},
+with the same linkage and visibility.  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.
+
+Aliases introduced with this attribute, such as @samp{f_u64} in the
+example above, are assembly symbol names: they do not undergo C++ name
+mangling, and are not made visible in any scope in the source language.
+They can, however, be named as alias targets, as long as their
+definition is output.  Naming a @samp{sym_alias} as an alias target will
+@emph{not} cause a definition to be output if it otherwise wouldn't.
+This may affect inline functions, C++ template instantiations, and other
+synthesized definitions that would have to be (synthesized and) compiled
+in order for the @samp{sym_alias} to be considered as a potential alias
+target.
+
+This attribute requires assembler and object file support for aliases,
+and may not be available on all targets.
+
 @cindex @code{symver} function attribute
 @item symver ("@var{name2}@@@var{nodename}")
 On ELF targets this attribute creates a symbol version.  The @var{name2} part
@@ -8069,6 +8112,10 @@ will be placed in new, unique sections.
 
 This additional functionality requires Binutils version 2.36 or later.
 
+@cindex @code{sym_alias} variable attribute
+@item sym_alias ("@var{name}")
+See @pxref{Common Function Attributes}.
+
 @cindex @code{uninitialized} variable attribute
 @item uninitialized
 This attribute, attached to a variable with automatic storage, means that
@@ -9164,6 +9211,21 @@ is not supported; that is to say, if a given scalar object can be accessed
 through distinct types that assign a different storage order to it, then the
 behavior is undefined.
 
+@cindex @code{sym_alias} type attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} type 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__ ((__sym_alias__ ("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.
+
 @cindex @code{transparent_union} type attribute
 @item transparent_union
 
diff --git a/gcc/symtab.cc b/gcc/symtab.cc
index 0470509a98d..44df52095c1 100644
--- a/gcc/symtab.cc
+++ b/gcc/symtab.cc
@@ -187,6 +187,22 @@ symbol_table::insert_to_assembler_name_hash (symtab_node *node,
 	(*aslot)->previous_sharing_asm_name = node;
       *aslot = node;
 
+      /* Check for sym_alias name clashes.  In create_sym_alias_decl,
+	 we check for a preexisting definition using the same
+	 assembler_name, so here we check for a previously-defined
+	 sym_name, marked with a "sym name" pseudo-attribute that
+	 points back at the declaration for which it was created.  */
+      if (symtab_node *sym_node = node->next_sharing_asm_name)
+	if (lookup_attribute ("sym alias",
+			      DECL_ATTRIBUTES (sym_node->decl)))
+	  {
+	    error_at (DECL_SOURCE_LOCATION (node->decl),
+		      "duplicate symbol name %qE", decl);
+	    inform (DECL_SOURCE_LOCATION (sym_node->decl),
+		    "already used by %qD in a %qE attribute",
+		    sym_node->decl, get_identifier ("sym_alias"));
+	  }
+
       /* Update also possible inline clones sharing a decl.  */
       cnode = dyn_cast <cgraph_node *> (node);
       if (cnode && cnode->clones && with_clones)
@@ -1943,6 +1959,46 @@ symtab_node::noninterposable_alias (symtab_node *node, void *data)
   return false;
 }
 
+/* Remap sym alias nodes recorded as aliasing REPLACED to alias
+   REPLACEMENT instead.  */
+
+void
+symtab_node::remap_sym_alias_target (tree replaced, tree replacement)
+{
+  symtab_node *repl_node = NULL;
+  if (!decl_in_symtab_p (replacement)
+      || !(repl_node = symtab_node::get (replacement)))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (replaced))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      symtab_node *sym_node = symtab_node::get_for_asmname (id);
+
+      if (!sym_node)
+	{
+	  create_sym_alias_decl (replacement, id);
+	  continue;
+	}
+
+      gcc_assert (!sym_node->analyzed);
+      if (sym_node->alias_target != replaced)
+	continue;
+
+      // sym_node->definition = 0;
+
+      if (VAR_P (replaced))
+	// varpool_node::create_extra_name_alias (sym_node->decl, replacement);
+	varpool_node::create_alias (sym_node->decl, replacement);
+      else
+	cgraph_node::create_same_body_alias (sym_node->decl, replacement);
+	// cgraph_node::create_alias (sym_node->decl, replacement);
+      sym_node->copy_visibility_from (repl_node);
+    }
+}
+
 /* 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.  */
@@ -1970,6 +2026,12 @@ symtab_node::noninterposable_alias (void)
 
   /* Otherwise create a new one.  */
   new_decl = copy_node (node->decl);
+  DECL_ATTRIBUTES (new_decl) = remove_attribute ("sym_alias",
+						   DECL_ATTRIBUTES
+						   (new_decl));
+  DECL_ATTRIBUTES (new_decl) = remove_attribute ("sym alias",
+						   DECL_ATTRIBUTES
+						   (new_decl));
   DECL_DLLIMPORT_P (new_decl) = 0;
   tree name = clone_function_name (node->decl, "localalias");
   if (!flag_wpa)
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-1.c b/gcc/testsuite/c-c++-common/goacc/declare-1.c
index 46ee01b6759..f2842893318 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-1.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-1.c
@@ -113,11 +113,11 @@ f_2 (void)
   int va3;
 #pragma acc declare device_resident(va3)
 
-#ifndef __cplusplus
+#if 0
   /* TODO PR90868
 
-     C: "error: variable '[...]' used more than once with '#pragma acc declare'".  */
-#else
+     "error: variable '[...]' used more than once with '#pragma acc declare'".  */
+
   extern int ve0;
 #pragma acc declare create(ve0)
 
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-2.c b/gcc/testsuite/c-c++-common/goacc/declare-2.c
index e2e22be57e9..aec59b69754 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-2.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-2.c
@@ -137,25 +137,25 @@ void
 f_pr90868_2 (void)
 {
   extern int we0;
-#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" } */
 
   extern int we1;
-#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" } */
 
   extern int *we2;
-#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" } */
 
   extern int we3;
-#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" } */
 
   extern int we4;
-#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" } */
 
   extern int we5;
-#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" } */
  
   extern int we6;
-#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" } */
 }
 
 
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
new file mode 100644
index 00000000000..2fe1948aed9
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
@@ -0,0 +1,75 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+extern int var_alias __attribute__ ((__alias__ ("FOOVAR_A")));
+int var_a = 1;
+
+void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+
+void foo_alias () __attribute__ ((__alias__ ("FOOBAR_A")));
+
+void
+foo_a ()
+{
+}
+
+
+int var_b;
+extern int var_b __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+
+void
+foo_b ()
+{
+}
+
+void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+
+
+int var_c __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+
+void __attribute__ ((__sym_alias__ ("FOOBAR_C")))
+foo_c ()
+{
+}
+
+
+void baf () {}
+
+void foo_x () {
+  extern void baf () __attribute__ ((__sym_alias__ ("FOO_BAF")));
+  extern void bad () /* ok, but not defined, so no alias issued.  */
+    __attribute__ ((__sym_alias__ ("FOO_BAD")));
+  extern void bar () __attribute__ ((__sym_alias__ ("FOO_BAR")));
+
+  static int x __attribute__ ((sym_alias ("FOO_x")));
+}
+
+void bar () {}
+
+extern int xr __attribute__ ((alias ("FOO_x")));
+
+
+ /* The *u* symbols are not defined, so the sym_aliases do not clash.  ??? Is
+    this just an implementation artifact, or a feature someone might actually
+    rely on?  */
+extern int var_u1 __attribute__ ((__sym_alias__ ("VAR_U")));
+extern int var_u2 __attribute__ ((__sym_alias__ ("VAR_U")));
+
+void foo_u1 () __attribute__ ((__sym_alias__ ("FOOBAR_U")));
+void foo_u2 () __attribute__ ((__sym_alias__ ("FOOBAR_U")));
+
+
+/* { dg-final { scan-assembler "FOOBAR_A" } } */
+/* { dg-final { scan-assembler "foo_alias" } } */
+/* { dg-final { scan-assembler "FOOVAR_A" } } */
+/* { dg-final { scan-assembler "var_alias" } } */
+/* { dg-final { scan-assembler "FOOBAR_B" } } */
+/* { dg-final { scan-assembler "FOOVAR_B" } } */
+/* { dg-final { scan-assembler "FOOBAR_C" } } */
+/* { dg-final { scan-assembler "FOOVAR_C" } } */
+/* { dg-final { scan-assembler "FOO_BAF" } } */
+/* { dg-final { scan-assembler "FOO_BAR" } } */
+/* { dg-final { scan-assembler-not "FOO_BAD" } } */
+/* { dg-final { scan-assembler-not "VAR_U" } } */
+/* { dg-final { scan-assembler-not "FOOBAR_U" } } */
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
new file mode 100644
index 00000000000..f692a552606
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
@@ -0,0 +1,99 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+struct s
+{
+  int mem __attribute__ ((__sym_alias__ ("S_MEM"))); /* { dg-warning "attribute ignored" } */
+};
+
+void foo () {
+  extern void bar () __attribute__ ((__sym_alias__ ("FOO_BAR"))); /* Ok.  */
+  int i __attribute__ ((sym_alias ("FOO_i"))); /* { dg-warning "ignored" } */
+  static int x __attribute__ ((sym_alias ("FOO_x")));
+  static int sx __attribute__ ((alias ("FOO_x"))); /* { dg-warning "ignored" } */
+  extern int xx __attribute__ ((alias ("FOO_x"))); /* { dg-warning "ignored" } */
+  extern int y __attribute__ ((sym_alias ("FOO_y"))); /* Ok.  */
+}
+
+int dr __attribute__ ((alias ("FOO_x"))); /* { dg-error "defined both" } */
+
+
+int j __attribute__ ((sym_alias ("J_var")));
+void __attribute__ ((alias ("J_var")))
+j_fn (); /* { dg-error "between function and variable" } */
+
+
+void __attribute__ ((sym_alias ("K_fn")))
+k () {
+}
+extern int __attribute__ ((alias ("K_fn")))
+k_var; /* { dg-error "between function and variable" } */
+
+
+int l __attribute__ ((sym_alias ("L_fn")));
+
+/* These should be detected and reported, not because the names clash at the
+   source level, but because the asm symbols do.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+void
+L_fn () /* { dg-error "duplicate" "" } */
+{
+}
+
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __attribute__ ((sym_alias ("M_var")))
+m ()
+{
+}
+
+int M_var; /* { dg-error "duplicate" "" } */
+
+
+void __attribute__ ((sym_alias ("N_sym")))
+n_fn ()
+{
+}
+
+int __attribute__ ((sym_alias ("N_sym")))
+n_var; /* { dg-error "duplicate" } */
+
+
+int __attribute__ ((sym_alias ("O_sym")))
+o_var;
+
+void __attribute__ ((sym_alias ("O_sym")))
+o_fn () /* { dg-error "duplicate" } */
+{
+}
+
+int __attribute__ ((sym_alias ("P_sym")))
+p_var1;
+
+int __attribute__ ((sym_alias ("P_sym")))
+p_var2; /* { dg-error "duplicate" } */
+
+void __attribute__ ((sym_alias ("Q_sym")))
+q_fn1 ()
+{
+}
+
+void __attribute__ ((sym_alias ("Q_sym")))
+q_fn2 () /* { dg-error "duplicate" } */
+{
+}
+
+int __attribute__ ((sym_alias ("R_var")))
+R_var; /* { dg-error "duplicate" } */
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __attribute__ ((sym_alias ("S_fn")))
+S_fn () /* { dg-error "duplicate" } */
+{
+}
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
new file mode 100644
index 00000000000..52f8fb76496
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
@@ -0,0 +1,34 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int var_a = 1;
+
+void
+foo_a ()
+{
+  extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+}
+
+void
+foo_b ()
+{
+  extern int var_b __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+}
+
+int var_b;
+
+void __attribute__ ((__sym_alias__ ("FOOBAR_C")))
+foo_c ()
+{
+  void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+  /* Another sym for var_b.  */
+  extern int var_b __attribute__ ((__sym_alias__ ("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-sym-alias-4.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
new file mode 100644
index 00000000000..2beafa46379
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-require-alias "" } */
+
+int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A"))) = 42;
+
+int __attribute__ ((__sym_alias__ ("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/c-c++-common/torture/attr-sym-alias-5.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
new file mode 100644
index 00000000000..bee90136daf
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
@@ -0,0 +1,54 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+/* { dg-require-visibility "" } */
+
+int __attribute__ ((sym_alias ("A_hid"), visibility ("hidden"))) a;
+
+extern int __attribute__ ((sym_alias ("B_prt"))) b;
+
+int b __attribute__ ((visibility ("protected")));
+
+extern int __attribute__ ((sym_alias ("C_ntr"))) c;
+
+extern int c __attribute__ ((visibility ("internal")));
+
+int c;
+
+static int d __attribute__ ((sym_alias ("D_stt")));
+
+
+extern void __attribute__ ((sym_alias ("F_hid"), visibility ("hidden")))
+f ();
+
+void
+f () {
+}
+
+extern void __attribute__ ((sym_alias ("G_prt")))
+g ();
+
+void __attribute__ ((visibility ("protected")))
+g () {
+}
+
+extern void __attribute__ ((sym_alias ("H_ntr")))
+h ();
+
+void __attribute__ ((visibility ("internal")))
+h ();
+
+void h () {
+}
+
+static void __attribute__ ((sym_alias ("I_stt")))
+i () {
+}
+
+/* { dg-final { scan-assembler {hidden[^\n]*A_hid} } } */
+/* { dg-final { scan-assembler {protected[^\n]*B_prt} } } */
+/* { dg-final { scan-assembler {internal[^\n]*C_ntr} } } */
+/* { dg-final { scan-assembler-not {glob[^\n]*D_stt} } } */
+/* { dg-final { scan-assembler {hidden[^\n]*F_hid} } } */
+/* { dg-final { scan-assembler {protected[^\n]*G_prt} } } */
+/* { dg-final { scan-assembler {internal[^\n]*H_ntr} } } */
+/* { dg-final { scan-assembler-not {glob[^\n]*I_stt} } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
new file mode 100644
index 00000000000..51729994913
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
@@ -0,0 +1,89 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+class __attribute__ ((__sym_alias__ ("FOOCLS_A"),
+		      __sym_alias__ ("FOOCLS_A_Dupe"))) foo {
+  static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  void __attribute__ ((__sym_alias__ ("FOOBAR_A"))) bar ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+int foo::var = 1;
+
+foo::foo () {}
+
+void foo::bar () {}
+
+namespace b {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_B"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_B"))) foo ();
+    void __attribute__ ((__sym_alias__ ("FOOBAR_B"))) bar () {}
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_B"))) ~foo() {}
+  };
+
+  int foo::var = 2;
+
+  foo::foo () {
+    void (foo::*pbar)() = &foo::bar;
+  }
+}
+
+namespace c {
+  namespace cd {
+    class __attribute__ ((__sym_alias__ ("FOOCLS_C"))) foo {
+      static int var __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+      __attribute__ ((__sym_alias__ ("FOOCTR_C"))) foo () {
+	void (foo::*pbar)() = &foo::bar;
+      }
+      void __attribute__ ((__sym_alias__ ("FOOBAR_C"))) bar () {}
+      virtual __attribute__ ((__sym_alias__ ("FOODTR_C"))) ~foo() {}
+    };
+
+    int foo::var = 3;
+  }
+}
+
+#include <typeinfo>
+
+namespace d {
+  namespace {
+    class __attribute__ ((__sym_alias__ ("FOOCLS_D"))) foo {
+      virtual ~foo() {} // typeid(foo) is not defined otherwise.
+    };
+  }
+
+  extern std::type_info __attribute__ ((__alias__ ("FOOCLS_D"))) foo_d_typeinfo;
+}
+
+extern std::type_info __attribute__ ((__alias__ ("FOOCLS_C"))) foo_c_typeinfo;
+
+/* { dg-final { scan-assembler "FOOCLS_A" } } */
+/* { dg-final { scan-assembler "FOOCLS_A_Dupe" } } */
+/* { 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" } } */
+/* { dg-final { scan-assembler "FOOCLS_D" } } */
+/* { dg-final { scan-assembler "foo_d_typeinfo" } } */
+/* { dg-final { scan-assembler "foo_c_typeinfo" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
new file mode 100644
index 00000000000..6facfcc2cd9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+namespace {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_A"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo ();
+    void __attribute__ ((__sym_alias__ ("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-sym-alias-3.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
new file mode 100644
index 00000000000..42c7288ad4a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
@@ -0,0 +1,83 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+// sym can be applied to template function explicit instantiations.
+
+template <typename T>
+void
+fn(T) {
+};
+
+template void __attribute__ ((__sym_alias__ ("FOOFUN_UINT"))) fn<>(unsigned int);
+template void __attribute__ ((__sym_alias__ ("FOOFUN_LONG"))) fn<>(long);
+
+template<> void __attribute__ ((__sym_alias__ ("FOOFUN_CHAR"))) fn<>(char) {}
+
+
+template <typename T = void>
+struct
+foo {
+  virtual ~foo() {}
+
+  virtual void virtfun() {}
+
+  static void stfun() {}
+  void inlfun() {}
+};
+
+// Explicitly instantiate members before the enclosing class.
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_CHAR_VIRT"))) foo<char>::virtfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_CHAR_TI"))) foo<char>;
+
+// Though they're only output if the enclosing class is.
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_LONG_VIRT"))) foo<long>::virtfun();
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_LONG_TI_X"))) foo<long>;
+
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_VOID_ST"))) foo<void>::stfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_VOID_TI"))) foo<>;
+
+
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_X"))) foo<short>;
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_ST"))) foo<short>::stfun();
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_INL"))) foo<short>::inlfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_D"))) foo<short>;
+
+// Explicit specializations work too.
+
+template <>
+struct  __attribute__ ((__sym_alias__ ("FOOCLS_INT_TI")))
+foo<int>
+{
+  virtual ~foo() {}
+  virtual void __attribute__ ((__sym_alias__ ("FOOCLS_INT_VIRT"))) virtfun() {}
+};
+
+/* { dg-final { scan-assembler "FOOFUN_UINT" } } */
+/* { dg-final { scan-assembler "FOOFUN_LONG" } } */
+/* { dg-final { scan-assembler "FOOFUN_CHAR" } } */
+
+/* { dg-final { scan-assembler "FOOCLS_VOID_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_VOID_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_X" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_INL" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_D" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_TI_X" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_VIRT" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
new file mode 100644
index 00000000000..59b779de35d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+template <typename T = void>
+class
+__attribute__ ((__sym_alias__ ("FOOCLS")))
+foo // { dg-error "duplicate|already" }
+{
+  virtual ~foo() {}
+
+  template <typename U>
+  void
+    __attribute__ ((__sym_alias__ ("FOOTMF")))
+    tmemfun () {} // { dg-error "duplicate|already" }
+};
+
+template <typename T>
+void
+__attribute__ ((__sym_alias__ ("FOOTFN")))
+fn(T) { // { dg-error "duplicate|already" }
+};
+
+template class foo<>;
+template class foo<int>;
+template void foo<>::tmemfun<void>();
+template void foo<int>::tmemfun<void>();
+template void fn<>(int);
+template void fn<>(long);
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
new file mode 100644
index 00000000000..45d59b953c3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
@@ -0,0 +1,14 @@
+/* { dg-do compile { target c++11 } } */
+/* { dg-require-alias "" } */
+
+struct foo {
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+foo::foo () {}
+
+// Make sure the inherited cdtors don't duplicate the syms.
+struct bar : foo {
+  using foo::foo;
+};
diff --git a/gcc/varpool.cc b/gcc/varpool.cc
index e7b51b15e4a..33649906d1d 100644
--- a/gcc/varpool.cc
+++ b/gcc/varpool.cc
@@ -163,6 +163,9 @@ varpool_node::get_create (tree decl)
     }
 
   node->register_symbol ();
+
+  create_sym_alias_decls (decl);
+
   return node;
 }

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [gcc(refs/users/aoliva/heads/testme)] Introduce attribute sym_alias
@ 2023-12-12  2:38 Alexandre Oliva
  0 siblings, 0 replies; 33+ messages in thread
From: Alexandre Oliva @ 2023-12-12  2:38 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:ac43dcde3f7ae7c41196b28575dff40e313470cd

commit ac43dcde3f7ae7c41196b28575dff40e313470cd
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Fri Dec 8 21:41:41 2023 -0300

    Introduce attribute sym_alias
    
    This patch introduces an attribute to add extra asm names (aliases)
    for a decl 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, and
    also to local static variables.  In C++, it can also be attached to
    class types, namespace-scoped variables and functions, static data
    members, member functions, explicit instantiations and specializations
    of template functions, members and classes.
    
    When applied to constructors or destructor, additional sym aliases
    with _Base and _Del suffixes are defined for variants other than
    complete-object ones.  This changes the assumption that clones always
    carry the same attributes as their abstract declarations, so there is
    now a function to adjust them.
    
    C++ also had a bug in which attributes from local extern declarations
    failed to be propagated to a preexisting corresponding
    namespace-scoped decl.  I've fixed that, and adjusted acc tests that
    distinguished between C and C++ in this regard.
    
    Applying the attribute to class types is only valid in C++, and the
    effect is to attach the alias to the RTTI object associated with the
    class type.
    
    
    for  gcc/ChangeLog
    
            * attribs.cc: Include cgraph.h.
            (decl_attributes): Allow late introduction of sym_alias in
            types.
            (create_sym_alias_decl, create_sym_alias_decls): New.
            * attribs.h: Declare them.
            (FOR_EACH_SYM_ALIAS): New macro.
            * cgraph.cc (cgraph_node::create): Create sym_alias decls.
            * varpool.cc (varpool_node::get_create): Create sym_alias
            decls.
            * cgraph.h (symtab_node::remap_sym_alias_target): New.
            * symtab.cc (symtab_node::remap_sym_alias_target): Define.
            (symbol_table::insert_to_assembler_name_hash): Check for
            symbol name clashes.
            (symtab_node::noninterposable_alias): Drop sym_alias
            attributes.
            * cgraphunit.cc (cgraph_node::analyze): Create alias_target
            node if needed.
            (analyze_functions): Fixup visibility of implicit alias only
            after its node is analyzed.
            * doc/extend.texi (sym_alias): Document for variables,
            functions and types.
    
    for  gcc/ada/ChangeLog
    
            * doc/gnat_rm/interfacing_to_other_languages.rst: Mention
            attribute sym_alias to give RTTI symbols mnemonic names.
            * doc/gnat_ugn/the_gnat_compilation_model.rst: Mention
            aliases.  Fix incorrect ref to C1 ctor variant.
    
    for  gcc/c-family/ChangeLog
    
            * c-ada-spec.cc (pp_asm_name): Use first sym_alias if
            available.
            * c-attribs.cc (handle_sym_alias_attribute): New.
            (c_common_attribute_table): Add sym_alias.
            (handle_copy_attribute): Do not copy sym_alias attribute.
    
    for  gcc/c/ChangeLog
    
            * c-decl.cc (duplicate_decls): Remap sym_alias target.
            (finish_decl): Create varpool_node for local static
            variables.
    
    for  gcc/cp/ChangeLog
    
            * class.cc (adjust_clone_attributes): New.
            (copy_fndecl_with_name, build_clone): Call it.
            * cp-tree.h (adjust_clone_attributes): Declare.
            (update_sym_alias_interface): Declare.
            (update_tinfo_sym_alias): Declare.
            * decl.cc (duplicate_decls): Remap sym_alias target.
            Adjust clone attributes.
            (grokfndecl): Tentatively create sym_alias decls after
            adding attributes in e.g. a template member function explicit
            instantiation.
            * decl2.cc (cplus_decl_attributes): Update tinfo sym_alias.
            (copy_interface, update_sym_alias_interface): New.
            (determine_visibility): Update sym_alias interface.
            (tentative_decl_linkage, import_export_decl): Likewise.
            * name-lookup.cc: Include target.h and cgraph.h.
            (push_local_extern_decl_alias): Merge attributes with
            namespace-scoped decl, and drop duplicate sym_alias.
            * optimize.cc (maybe_clone_body): Re-adjust attributes after
            cloning them.  Update sym_alias interface.
            * rtti.cc: Include attribs.h and cgraph.h.
            (get_tinfo_decl): Copy sym_alias attributes from type to tinfo
            decl.  Create sym_alias decls.
            (update_tinfo_sym_alias): New.
    
    for  gcc/testsuite/ChangeLog
    
            * c-c++-common/goacc/declare-1.c: Adjust.
            * c-c++-common/goacc/declare-2.c: Adjust.
            * c-c++-common/torture/attr-sym-alias-1.c: New.
            * c-c++-common/torture/attr-sym-alias-2.c: New.
            * c-c++-common/torture/attr-sym-alias-3.c: New.
            * c-c++-common/torture/attr-sym-alias-4.c: New.
            * c-c++-common/torture/attr-sym-alias-5.c: New.
            * g++.dg/torture/attr-sym-alias-1.C: New.
            * g++.dg/torture/attr-sym-alias-2.C: New.
            * g++.dg/torture/attr-sym-alias-3.C: New.
            * g++.dg/torture/attr-sym-alias-4.C: New.
            * g++.dg/torture/attr-sym-alias-5.C: New.

Diff:
---
 .../doc/gnat_rm/interfacing_to_other_languages.rst |  6 ++
 .../doc/gnat_ugn/the_gnat_compilation_model.rst    | 10 ++-
 gcc/attribs.cc                                     | 74 +++++++++++++++-
 gcc/attribs.h                                      |  7 ++
 gcc/c-family/c-ada-spec.cc                         |  7 ++
 gcc/c-family/c-attribs.cc                          | 33 +++++++-
 gcc/c/c-decl.cc                                    |  7 ++
 gcc/cgraph.cc                                      |  2 +
 gcc/cgraph.h                                       |  4 +
 gcc/cgraphunit.cc                                  |  2 +-
 gcc/cp/class.cc                                    | 64 ++++++++++++++
 gcc/cp/cp-tree.h                                   |  4 +
 gcc/cp/decl.cc                                     |  4 +
 gcc/cp/decl2.cc                                    | 53 ++++++++++++
 gcc/cp/name-lookup.cc                              | 11 +++
 gcc/cp/optimize.cc                                 |  3 +
 gcc/cp/rtti.cc                                     | 71 +++++++++++++++-
 gcc/doc/extend.texi                                | 62 ++++++++++++++
 gcc/symtab.cc                                      | 62 ++++++++++++++
 gcc/testsuite/c-c++-common/goacc/declare-1.c       |  6 +-
 gcc/testsuite/c-c++-common/goacc/declare-2.c       | 14 +--
 .../c-c++-common/torture/attr-sym-alias-1.c        | 75 ++++++++++++++++
 .../c-c++-common/torture/attr-sym-alias-2.c        | 99 ++++++++++++++++++++++
 .../c-c++-common/torture/attr-sym-alias-3.c        | 34 ++++++++
 .../c-c++-common/torture/attr-sym-alias-4.c        | 28 ++++++
 .../c-c++-common/torture/attr-sym-alias-5.c        | 54 ++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C    | 89 +++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C    | 26 ++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C    | 83 ++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C    | 28 ++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C    | 14 +++
 gcc/varpool.cc                                     |  3 +
 32 files changed, 1020 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 ad0be511d48..b8de16ebf7c 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 ``sym_alias`` 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 fd15459203a..20045698962 100644
--- a/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
+++ b/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
@@ -4274,6 +4274,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__ ((__sym_alias__ ("Ctor_For_Animal")))
         Animal() {Age_Count = 0;};
       private:
         int Age_Count;
@@ -4306,6 +4307,7 @@ both Carnivore and Domestic, that is:
         virtual int  Number_Of_Teeth ();
         virtual void Set_Owner (char* Name);
 
+        __attribute__ ((__sym_alias__ ("Ctor_For_Dog"))) // mnemonic alias
         Dog(); // Constructor
       private:
         int  Tooth_Count;
@@ -4344,7 +4346,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;
@@ -4360,7 +4363,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,7 +4385,8 @@ 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.
+mangled name (or an alias) 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.cc b/gcc/attribs.cc
index 776655dde00..648674fbf20 100644
--- a/gcc/attribs.cc
+++ b/gcc/attribs.cc
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "target.h"
 #include "tree.h"
+#include "cgraph.h"
 #include "stringpool.h"
 #include "diagnostic-core.h"
 #include "attribs.h"
@@ -843,7 +844,8 @@ decl_attributes (tree *node, tree attributes, int flags,
 
       if (TYPE_P (*anode)
 	  && (flags & (int) ATTR_FLAG_TYPE_IN_PLACE)
-	  && COMPLETE_TYPE_P (*anode))
+	  && COMPLETE_TYPE_P (*anode)
+	  && !is_attribute_p ("sym_alias", name))
 	{
 	  warning (OPT_Wattributes, "type attributes ignored after type is already defined");
 	  continue;
@@ -2685,6 +2687,76 @@ attr_access::array_as_string (tree type) const
   return typstr;
 }
 
+/* Create a sym attribute for DECL to be visible with linkage name ID.  */
+
+tree
+create_sym_alias_decl (tree decl, tree id)
+{
+  const char *attr_str = "sym_alias";
+
+  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;
+
+      tree attr_name = get_identifier (attr_str);
+      error_at (DECL_SOURCE_LOCATION (decl),
+		"duplicate symbol name %qE in %qE attribute of %qD",
+		id, attr_name, decl);
+      inform (DECL_SOURCE_LOCATION (sym_node->decl),
+	      "already used by %qD", sym_node->decl);
+    }
+
+  tree clone = copy_node (decl);
+  DECL_ATTRIBUTES (clone) = remove_attribute (attr_str,
+					      DECL_ATTRIBUTES (decl));
+  /* Mark it as a "sym alias" for decl, an internal attribute that
+     enables symbol_table::insert_to_assembler_name_hash can recognize
+     sym_alias decls.  */
+  DECL_ATTRIBUTES (clone) = tree_cons (get_identifier ("sym alias"),
+				       decl, DECL_ATTRIBUTES (clone));
+  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;
+
+  symtab_node *node;
+  if (VAR_P (clone))
+    // DECL_READ_P (clone) = 1;
+    // node = varpool_node::create_extra_name_alias (clone, decl);
+    node = varpool_node::create_alias (clone, decl);
+  else
+    node = cgraph_node::create_same_body_alias (clone, decl);
+    // node = cgraph_node::create_alias (clone, decl);
+  if (symtab_node *dnode = symtab_node::get_create (decl))
+    node->copy_visibility_from (dnode);
+
+  return clone;
+}
+
+/* Create decls for all sym aliases requested in DECL's attributes.  */
+
+void
+create_sym_alias_decls (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl)
+      || DECL_ABSTRACT_P (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      create_sym_alias_decl (decl, id);
+    }
+}
+
 #if CHECKING_P
 
 namespace selftest
diff --git a/gcc/attribs.h b/gcc/attribs.h
index 2c615a45663..39e0bc573dc 100644
--- a/gcc/attribs.h
+++ b/gcc/attribs.h
@@ -405,4 +405,11 @@ extern void init_attr_rdwr_indices (rdwr_map *, tree);
 extern attr_access *get_parm_access (rdwr_map &, tree,
 				     tree = current_function_decl);
 
+extern tree create_sym_alias_decl (tree, tree);
+extern void create_sym_alias_decls (tree);
+
+#define FOR_EACH_SYM_ALIAS(sym, attrs)					\
+  for (tree sym = lookup_attribute ("sym_alias", (attrs));		\
+       sym; sym = lookup_attribute ("sym_alias", TREE_CHAIN (sym)))
+
 #endif // GCC_ATTRIBS_H
diff --git a/gcc/c-family/c-ada-spec.cc b/gcc/c-family/c-ada-spec.cc
index 050994d8416..5042b9cfecd 100644
--- a/gcc/c-family/c-ada-spec.cc
+++ b/gcc/c-family/c-ada-spec.cc
@@ -1431,6 +1431,13 @@ pp_ada_tree_identifier (pretty_printer *buffer, tree node, tree type,
 static void
 pp_asm_name (pretty_printer *buffer, tree t)
 {
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (t))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.cc b/gcc/c-family/c-attribs.cc
index 854e987dc79..2593fde1fe9 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -110,7 +110,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_sym_alias_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,
@@ -392,6 +393,8 @@ const struct attribute_spec c_common_gnu_attributes[] =
 			      handle_alias_attribute, NULL },
   { "weakref",                0, 1, true,  false, false, false,
 			      handle_weakref_attribute, NULL },
+  { "sym_alias",              1, 1, false,  false, false, false,
+			      handle_sym_alias_attribute, NULL },
   { "no_instrument_function", 0, 0, true,  false, false, false,
 			      handle_no_instrument_function_attribute,
 			      NULL },
@@ -3092,7 +3095,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
@@ -3102,7 +3105,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
@@ -3112,6 +3115,29 @@ handle_alias_attribute (tree *node, tree name, tree args,
   return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs);
 }
 
+/* Handle a "sym_alias" attribute; arguments as in struct
+   attribute_spec.handler.  */
+
+static tree
+handle_sym_alias_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 (decl_in_symtab_p (node))
+    *no_add_attrs = false;
+  else if (TYPE_P (node) && c_dialect_cxx ())
+    *no_add_attrs = false;
+  else
+    return error_mark_node;
+
+  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.  */
 
@@ -3245,6 +3271,7 @@ handle_copy_attribute (tree *node, tree name, tree args,
 	      || is_attribute_p ("visibility", atname)
 	      || is_attribute_p ("weak", atname)
 	      || is_attribute_p ("weakref", atname)
+	      || is_attribute_p ("sym_alias", atname)
 	      || is_attribute_p ("target_clones", atname))
 	    continue;
 
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 92c83e1bf10..c1490e627df 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -3117,6 +3117,8 @@ duplicate_decls (tree newdecl, tree olddecl)
 
   merge_decls (newdecl, olddecl, newtype, oldtype);
 
+  symtab_node::remap_sym_alias_target (newdecl, olddecl);
+
   /* The NEWDECL will no longer be needed.
 
      Before releasing the node, be sure to remove function from symbol
@@ -5902,6 +5904,11 @@ finish_decl (tree decl, location_t init_loc, tree init,
 	    set_user_assembler_name (decl, asmspec);
 	}
 
+      /* Give attribute sym_alias a chance to make the symbol name
+	 available for aliasing, even for a static local variable.  */
+      if (VAR_P (decl) && !DECL_EXTERNAL (decl) && is_global_var (decl))
+	varpool_node::get_create (decl);
+
       if (DECL_FILE_SCOPE_P (decl))
 	{
 	  if (DECL_INITIAL (decl) == NULL_TREE
diff --git a/gcc/cgraph.cc b/gcc/cgraph.cc
index f93259a8c70..0bef2179e89 100644
--- a/gcc/cgraph.cc
+++ b/gcc/cgraph.cc
@@ -523,6 +523,8 @@ cgraph_node::create (tree decl)
   node->register_symbol ();
   maybe_record_nested_function (node);
 
+  create_sym_alias_decls (decl);
+
   return node;
 }
 
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index 2b320557616..6d61e327fb6 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -327,6 +327,10 @@ public:
   /* Return DECL that alias is aliasing.  */
   inline tree get_alias_target_tree ();
 
+  /* Remap sym alias nodes recorded as aliasing REPLACED to alias REPLACEMENT
+     instead.  */
+  static void remap_sym_alias_target (tree replaced, tree replacement);
+
   /* Set section for symbol and its aliases.  */
   void set_section (const char *section);
 
diff --git a/gcc/cgraphunit.cc b/gcc/cgraphunit.cc
index 9a550a5cce6..a66db795c78 100644
--- a/gcc/cgraphunit.cc
+++ b/gcc/cgraphunit.cc
@@ -1176,7 +1176,7 @@ analyze_functions (bool first_time)
      C++ FE is confused about the COMDAT groups being right.  */
   if (symtab->cpp_implicit_aliases_done)
     FOR_EACH_SYMBOL (node)
-      if (node->cpp_implicit_alias)
+      if (node->cpp_implicit_alias && node->analyzed)
 	  node->fixup_same_cpp_alias_visibility (node->get_alias_target ());
   build_type_inheritance_graph ();
 
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index 6fdb56abfb9..fa8fc63db82 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -4896,6 +4896,68 @@ check_methods (tree t)
     }
 }
 
+/* Adjust sym alias name for CLONE, cloned from FN and named NAME,
+   if it is a cdtor, and drop the sym alias from other clones.  */
+
+void
+adjust_clone_attributes (tree fn, tree clone, tree name, bool skip_copy_p)
+{
+  if (IDENTIFIER_CDTOR_P (name))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	{
+	  found = true;
+	  break;
+	}
+
+      if (found
+	  && (name == complete_ctor_identifier
+	      || name == complete_dtor_identifier))
+	{
+	  /* Reuse the sym alias decls created for the primary cdtor
+	     decl.  */
+	  symtab_node::remap_sym_alias_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);
+
+	  if (!skip_copy_p)
+	    DECL_ATTRIBUTES (clone) = copy_list (DECL_ATTRIBUTES (clone));
+
+	  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	    {
+	      /* We need to copy this even with skip_copy_p, because
+		 even then copying was shallow.  */
+	      TREE_VALUE (sym) = copy_list (TREE_VALUE (sym));
+	      /* Append suf to the sym alias name.  */
+	      tree str = TREE_VALUE (TREE_VALUE (sym));
+	      char *symname = concat (TREE_STRING_POINTER (str), suf, NULL);
+	      str = build_string (TREE_STRING_LENGTH (str) + xlen, symname);
+	      TREE_VALUE (TREE_VALUE (sym)) = str;
+	      free (symname);
+	    }
+
+	  if (symtab_node::get (clone))
+	    create_sym_alias_decls (clone);
+	}
+    }
+  else
+    DECL_ATTRIBUTES (clone)
+      = remove_attribute ("sym_alias", DECL_ATTRIBUTES (clone));
+}
+
 /* FN is constructor, destructor or operator function.  Clone the
    declaration to create a NAME'd variant.  NEED_VTT_PARM_P and
    OMIT_INHERITED_PARMS_P are relevant if it's a cdtor.  */
@@ -5065,6 +5127,8 @@ build_clone (tree fn, tree name, bool need_vtt_parm_p,
   DECL_CHAIN (clone) = DECL_CHAIN (fn);
   DECL_CHAIN (fn) = clone;
 
+  adjust_clone_attributes (fn, clone, name);
+
   return clone;
 }
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index cbf280ec454..10a31ad7c0a 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5822,6 +5822,8 @@ struct GTY((for_user)) spec_entry
 
 extern int current_class_depth;
 
+void adjust_clone_attributes (tree fn, tree clone, tree name, bool = false);
+
 /* in decl.cc */
 
 /* An array of static vars & fns.  */
@@ -7059,6 +7061,7 @@ extern void do_push_parm_decls			(tree, tree, tree *);
 extern tree do_aggregate_paren_init		(tree, tree);
 
 /* in decl2.cc */
+extern void update_sym_alias_interface		(tree);
 extern void record_mangling			(tree, bool);
 extern void overwrite_mangling			(tree, tree);
 extern void note_mangling_alias			(tree, tree);
@@ -7633,6 +7636,7 @@ extern bool emit_tinfo_decl			(tree);
 extern unsigned get_pseudo_tinfo_index		(tree);
 extern tree get_pseudo_tinfo_type		(unsigned);
 extern tree build_if_nonnull			(tree, tree, tsubst_flags_t);
+extern void update_tinfo_sym_alias		(tree);
 
 /* in search.cc */
 extern tree get_parent_with_private_access 	(tree decl, tree binfo);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index b1ada1d5215..14d12f66562 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -3277,6 +3277,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
 	      && TREE_STATIC (olddecl))))
     make_decl_rtl (olddecl);
 
+  symtab_node::remap_sym_alias_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.
@@ -3300,6 +3302,7 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
       FOR_EACH_CLONE (clone, olddecl)
 	{
 	  DECL_ATTRIBUTES (clone) = DECL_ATTRIBUTES (olddecl);
+	  adjust_clone_attributes (olddecl, clone, DECL_NAME (clone));
 	  DECL_PRESERVE_P (clone) |= DECL_PRESERVE_P (olddecl);
 	}
     }
@@ -10838,6 +10841,7 @@ grokfndecl (tree ctype,
     {
       cplus_decl_attributes (&decl, *attrlist, 0);
       *attrlist = NULL_TREE;
+      create_sym_alias_decls (decl);
     }
 
   if (DECL_HAS_CONTRACTS_P (decl))
diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
index bee84879023..00f19962e5d 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -1846,6 +1846,9 @@ cplus_decl_attributes (tree *decl, tree attributes, int flags)
   if (late_attrs)
     save_template_attributes (late_attrs, decl, flags);
 
+  if (TYPE_P (*decl) && attributes)
+    update_tinfo_sym_alias (*decl);
+
   /* Propagate deprecation out to the template.  */
   if (TREE_DEPRECATED (*decl))
     if (tree ti = get_template_info (*decl))
@@ -2202,6 +2205,50 @@ 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);
+  if (symtab_node *dest_node = symtab_node::get (dest))
+    if (symtab_node *decl_node = symtab_node::get (decl))
+      decl_node->copy_visibility_from (dest_node);
+}
+
+/* Propagate linkage changes to sym aliases.  */
+
+void
+update_sym_alias_interface (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.  */
 
@@ -3009,6 +3056,8 @@ determine_visibility (tree decl)
        translation unit, we can make the type internal.  */
     constrain_visibility (decl, VISIBILITY_ANON, false);
 
+  update_sym_alias_interface (decl);
+
   /* If visibility changed and DECL already has DECL_RTL, ensure
      symbol flags are updated.  */
   if ((DECL_VISIBILITY (decl) != orig_visibility
@@ -3271,6 +3320,8 @@ tentative_decl_linkage (tree decl)
       else if (VAR_P (decl))
 	maybe_commonize_var (decl);
     }
+
+  update_sym_alias_interface (decl);
 }
 
 /* DECL is a FUNCTION_DECL or VAR_DECL.  If the object file linkage
@@ -3505,6 +3556,8 @@ import_export_decl (tree decl)
     }
 
   DECL_INTERFACE_KNOWN (decl) = 1;
+
+  update_sym_alias_interface (decl);
 }
 
 /* Return an expression that performs the destruction of DECL, which
diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc
index 09dc6ef3e5a..d11835a4552 100644
--- a/gcc/cp/name-lookup.cc
+++ b/gcc/cp/name-lookup.cc
@@ -22,9 +22,11 @@ along with GCC; see the file COPYING3.  If not see
 #define INCLUDE_MEMORY
 #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"
@@ -3675,6 +3677,15 @@ push_local_extern_decl_alias (tree decl)
 	  /* Adjust visibility.  */
 	  determine_visibility (alias);
 	}
+      else if (DECL_P (alias))
+	DECL_ATTRIBUTES (alias)
+	  = targetm.merge_decl_attributes (alias, decl);
+      if (DECL_P (alias))
+	{
+	  symtab_node::remap_sym_alias_target (decl, alias);
+	  DECL_ATTRIBUTES (decl)
+	    = remove_attribute ("sym_alias", DECL_ATTRIBUTES (alias));
+	}
     }
 
   retrofit_lang_decl (decl);
diff --git a/gcc/cp/optimize.cc b/gcc/cp/optimize.cc
index 9e8926e4cc6..4a2e8cb4354 100644
--- a/gcc/cp/optimize.cc
+++ b/gcc/cp/optimize.cc
@@ -528,9 +528,12 @@ maybe_clone_body (tree fn)
       DECL_VISIBILITY_SPECIFIED (clone) = DECL_VISIBILITY_SPECIFIED (fn);
       DECL_DLLIMPORT_P (clone) = DECL_DLLIMPORT_P (fn);
       DECL_ATTRIBUTES (clone) = clone_attrs (DECL_ATTRIBUTES (fn));
+      adjust_clone_attributes (fn, clone, DECL_NAME (clone), true);
       DECL_DISREGARD_INLINE_LIMITS (clone) = DECL_DISREGARD_INLINE_LIMITS (fn);
       set_decl_section_name (clone, fn);
 
+      update_sym_alias_interface (clone);
+
       /* Adjust the parameter names and locations.  */
       parm = DECL_ARGUMENTS (fn);
       clone_parm = DECL_ARGUMENTS (clone);
diff --git a/gcc/cp/rtti.cc b/gcc/cp/rtti.cc
index 7878929c246..4bb7b8c8c6e 100644
--- a/gcc/cp/rtti.cc
+++ b/gcc/cp/rtti.cc
@@ -28,8 +28,10 @@ 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"
+#include "cgraph.h"
 
 /* C++ returns type information to the user in struct type_info
    objects. We also use type information to implement dynamic_cast and
@@ -479,8 +481,13 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
 	  = build_tree_list (get_identifier ("non overlapping"),
 			     NULL_TREE);
       else
+	/* Share the non overlapping attribute, without assuming it's
+	   the only attribute, but assuming it's the last if it's
+	   present.  There may be sym aliases too, and those are not
+	   to be shared.  */
 	DECL_ATTRIBUTES (d)
-	  = DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]);
+	  = lookup_attribute ("non overlapping",
+			      DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]));
 
       /* Mark the variable as undefined -- but remember that we can
 	 define it later if we need to do so.  */
@@ -492,6 +499,16 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
       if (CLASS_TYPE_P (type))
 	CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type)) = d;
 
+      /* Copy sym alias attributes from the type to the rtti obj decl.  */
+      tree *attrs = &DECL_ATTRIBUTES (d);
+      FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+	{
+	  tree attr = tree_cons (TREE_PURPOSE (sym), TREE_VALUE (sym), *attrs);
+	  *attrs = attr;
+	  attrs = &TREE_CHAIN (attr);
+	}
+      create_sym_alias_decls (d);
+
       /* Add decl to the global array of tinfo decls.  */
       vec_safe_push (unemitted_tinfo_decls, d);
     }
@@ -499,6 +516,58 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
   return d;
 }
 
+/* After modifying the attributes of TYPE, check whether tinfo was
+   already created and, if so, add to it any sym alias attributes
+   that were not already present.  */
+
+void
+update_tinfo_sym_alias (tree type)
+{
+  if (!TYPE_SIZE (type) || !CLASS_TYPE_P (type))
+    return;
+
+  tree d = CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type));
+  if (!d)
+    return;
+
+  bool first = true;
+  symtab_node *node = NULL;
+
+  tree *attrs = &DECL_ATTRIBUTES (d);
+  FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (d_sym, *attrs)
+	if (TREE_VALUE (sym) == TREE_VALUE (d_sym))
+	  {
+	    found = true;
+	    break;
+	  }
+
+      if (found)
+	continue;
+
+      tree attr = tree_cons (TREE_PURPOSE (sym),
+			     TREE_VALUE (sym),
+			     *attrs);
+      *attrs = attr;
+      attrs = &TREE_CHAIN (attr);
+
+      if (first)
+	{
+	  first = false;
+	  node = symtab_node::get (d);
+	}
+
+      if (!node)
+	continue;
+
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+      create_sym_alias_decl (d, id);
+    }
+}
+
 /* Return the type_info object for TYPE.  */
 
 tree
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index e8b5e771f7a..d33ee51feb9 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -4142,6 +4142,49 @@ Function Attributes}, @ref{PowerPC Function Attributes},
 @ref{Nios II Function Attributes}, and @ref{S/390 Function Attributes}
 for details.
 
+@cindex @code{sym_alias} function attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} attribute causes @var{name} to be output as an
+alias to the function, with the same linkage and visibility as the
+function, when the function definition is output, provided that the
+function could be an alias target.  For instance,
+
+@smallexample
+void f (uint64_t) __attribute__ ((__sym_alias__ ("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},
+with the same linkage and visibility.  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.
+
+Aliases introduced with this attribute, such as @samp{f_u64} in the
+example above, are assembly symbol names: they do not undergo C++ name
+mangling, and are not made visible in any scope in the source language.
+They can, however, be named as alias targets, as long as their
+definition is output.  Naming a @samp{sym_alias} as an alias target will
+@emph{not} cause a definition to be output if it otherwise wouldn't.
+This may affect inline functions, C++ template instantiations, and other
+synthesized definitions that would have to be (synthesized and) compiled
+in order for the @samp{sym_alias} to be considered as a potential alias
+target.
+
+This attribute requires assembler and object file support for aliases,
+and may not be available on all targets.
+
 @cindex @code{symver} function attribute
 @item symver ("@var{name2}@@@var{nodename}")
 On ELF targets this attribute creates a symbol version.  The @var{name2} part
@@ -8069,6 +8112,10 @@ will be placed in new, unique sections.
 
 This additional functionality requires Binutils version 2.36 or later.
 
+@cindex @code{sym_alias} variable attribute
+@item sym_alias ("@var{name}")
+See @pxref{Common Function Attributes}.
+
 @cindex @code{uninitialized} variable attribute
 @item uninitialized
 This attribute, attached to a variable with automatic storage, means that
@@ -9164,6 +9211,21 @@ is not supported; that is to say, if a given scalar object can be accessed
 through distinct types that assign a different storage order to it, then the
 behavior is undefined.
 
+@cindex @code{sym_alias} type attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} type 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__ ((__sym_alias__ ("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.
+
 @cindex @code{transparent_union} type attribute
 @item transparent_union
 
diff --git a/gcc/symtab.cc b/gcc/symtab.cc
index 0470509a98d..44df52095c1 100644
--- a/gcc/symtab.cc
+++ b/gcc/symtab.cc
@@ -187,6 +187,22 @@ symbol_table::insert_to_assembler_name_hash (symtab_node *node,
 	(*aslot)->previous_sharing_asm_name = node;
       *aslot = node;
 
+      /* Check for sym_alias name clashes.  In create_sym_alias_decl,
+	 we check for a preexisting definition using the same
+	 assembler_name, so here we check for a previously-defined
+	 sym_name, marked with a "sym name" pseudo-attribute that
+	 points back at the declaration for which it was created.  */
+      if (symtab_node *sym_node = node->next_sharing_asm_name)
+	if (lookup_attribute ("sym alias",
+			      DECL_ATTRIBUTES (sym_node->decl)))
+	  {
+	    error_at (DECL_SOURCE_LOCATION (node->decl),
+		      "duplicate symbol name %qE", decl);
+	    inform (DECL_SOURCE_LOCATION (sym_node->decl),
+		    "already used by %qD in a %qE attribute",
+		    sym_node->decl, get_identifier ("sym_alias"));
+	  }
+
       /* Update also possible inline clones sharing a decl.  */
       cnode = dyn_cast <cgraph_node *> (node);
       if (cnode && cnode->clones && with_clones)
@@ -1943,6 +1959,46 @@ symtab_node::noninterposable_alias (symtab_node *node, void *data)
   return false;
 }
 
+/* Remap sym alias nodes recorded as aliasing REPLACED to alias
+   REPLACEMENT instead.  */
+
+void
+symtab_node::remap_sym_alias_target (tree replaced, tree replacement)
+{
+  symtab_node *repl_node = NULL;
+  if (!decl_in_symtab_p (replacement)
+      || !(repl_node = symtab_node::get (replacement)))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (replaced))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      symtab_node *sym_node = symtab_node::get_for_asmname (id);
+
+      if (!sym_node)
+	{
+	  create_sym_alias_decl (replacement, id);
+	  continue;
+	}
+
+      gcc_assert (!sym_node->analyzed);
+      if (sym_node->alias_target != replaced)
+	continue;
+
+      // sym_node->definition = 0;
+
+      if (VAR_P (replaced))
+	// varpool_node::create_extra_name_alias (sym_node->decl, replacement);
+	varpool_node::create_alias (sym_node->decl, replacement);
+      else
+	cgraph_node::create_same_body_alias (sym_node->decl, replacement);
+	// cgraph_node::create_alias (sym_node->decl, replacement);
+      sym_node->copy_visibility_from (repl_node);
+    }
+}
+
 /* 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.  */
@@ -1970,6 +2026,12 @@ symtab_node::noninterposable_alias (void)
 
   /* Otherwise create a new one.  */
   new_decl = copy_node (node->decl);
+  DECL_ATTRIBUTES (new_decl) = remove_attribute ("sym_alias",
+						   DECL_ATTRIBUTES
+						   (new_decl));
+  DECL_ATTRIBUTES (new_decl) = remove_attribute ("sym alias",
+						   DECL_ATTRIBUTES
+						   (new_decl));
   DECL_DLLIMPORT_P (new_decl) = 0;
   tree name = clone_function_name (node->decl, "localalias");
   if (!flag_wpa)
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-1.c b/gcc/testsuite/c-c++-common/goacc/declare-1.c
index 46ee01b6759..f2842893318 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-1.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-1.c
@@ -113,11 +113,11 @@ f_2 (void)
   int va3;
 #pragma acc declare device_resident(va3)
 
-#ifndef __cplusplus
+#if 0
   /* TODO PR90868
 
-     C: "error: variable '[...]' used more than once with '#pragma acc declare'".  */
-#else
+     "error: variable '[...]' used more than once with '#pragma acc declare'".  */
+
   extern int ve0;
 #pragma acc declare create(ve0)
 
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-2.c b/gcc/testsuite/c-c++-common/goacc/declare-2.c
index e2e22be57e9..aec59b69754 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-2.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-2.c
@@ -137,25 +137,25 @@ void
 f_pr90868_2 (void)
 {
   extern int we0;
-#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" } */
 
   extern int we1;
-#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" } */
 
   extern int *we2;
-#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" } */
 
   extern int we3;
-#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" } */
 
   extern int we4;
-#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" } */
 
   extern int we5;
-#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" } */
  
   extern int we6;
-#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" } */
 }
 
 
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
new file mode 100644
index 00000000000..2fe1948aed9
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
@@ -0,0 +1,75 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+extern int var_alias __attribute__ ((__alias__ ("FOOVAR_A")));
+int var_a = 1;
+
+void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+
+void foo_alias () __attribute__ ((__alias__ ("FOOBAR_A")));
+
+void
+foo_a ()
+{
+}
+
+
+int var_b;
+extern int var_b __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+
+void
+foo_b ()
+{
+}
+
+void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+
+
+int var_c __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+
+void __attribute__ ((__sym_alias__ ("FOOBAR_C")))
+foo_c ()
+{
+}
+
+
+void baf () {}
+
+void foo_x () {
+  extern void baf () __attribute__ ((__sym_alias__ ("FOO_BAF")));
+  extern void bad () /* ok, but not defined, so no alias issued.  */
+    __attribute__ ((__sym_alias__ ("FOO_BAD")));
+  extern void bar () __attribute__ ((__sym_alias__ ("FOO_BAR")));
+
+  static int x __attribute__ ((sym_alias ("FOO_x")));
+}
+
+void bar () {}
+
+extern int xr __attribute__ ((alias ("FOO_x")));
+
+
+ /* The *u* symbols are not defined, so the sym_aliases do not clash.  ??? Is
+    this just an implementation artifact, or a feature someone might actually
+    rely on?  */
+extern int var_u1 __attribute__ ((__sym_alias__ ("VAR_U")));
+extern int var_u2 __attribute__ ((__sym_alias__ ("VAR_U")));
+
+void foo_u1 () __attribute__ ((__sym_alias__ ("FOOBAR_U")));
+void foo_u2 () __attribute__ ((__sym_alias__ ("FOOBAR_U")));
+
+
+/* { dg-final { scan-assembler "FOOBAR_A" } } */
+/* { dg-final { scan-assembler "foo_alias" } } */
+/* { dg-final { scan-assembler "FOOVAR_A" } } */
+/* { dg-final { scan-assembler "var_alias" } } */
+/* { dg-final { scan-assembler "FOOBAR_B" } } */
+/* { dg-final { scan-assembler "FOOVAR_B" } } */
+/* { dg-final { scan-assembler "FOOBAR_C" } } */
+/* { dg-final { scan-assembler "FOOVAR_C" } } */
+/* { dg-final { scan-assembler "FOO_BAF" } } */
+/* { dg-final { scan-assembler "FOO_BAR" } } */
+/* { dg-final { scan-assembler-not "FOO_BAD" } } */
+/* { dg-final { scan-assembler-not "VAR_U" } } */
+/* { dg-final { scan-assembler-not "FOOBAR_U" } } */
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
new file mode 100644
index 00000000000..f692a552606
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
@@ -0,0 +1,99 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+struct s
+{
+  int mem __attribute__ ((__sym_alias__ ("S_MEM"))); /* { dg-warning "attribute ignored" } */
+};
+
+void foo () {
+  extern void bar () __attribute__ ((__sym_alias__ ("FOO_BAR"))); /* Ok.  */
+  int i __attribute__ ((sym_alias ("FOO_i"))); /* { dg-warning "ignored" } */
+  static int x __attribute__ ((sym_alias ("FOO_x")));
+  static int sx __attribute__ ((alias ("FOO_x"))); /* { dg-warning "ignored" } */
+  extern int xx __attribute__ ((alias ("FOO_x"))); /* { dg-warning "ignored" } */
+  extern int y __attribute__ ((sym_alias ("FOO_y"))); /* Ok.  */
+}
+
+int dr __attribute__ ((alias ("FOO_x"))); /* { dg-error "defined both" } */
+
+
+int j __attribute__ ((sym_alias ("J_var")));
+void __attribute__ ((alias ("J_var")))
+j_fn (); /* { dg-error "between function and variable" } */
+
+
+void __attribute__ ((sym_alias ("K_fn")))
+k () {
+}
+extern int __attribute__ ((alias ("K_fn")))
+k_var; /* { dg-error "between function and variable" } */
+
+
+int l __attribute__ ((sym_alias ("L_fn")));
+
+/* These should be detected and reported, not because the names clash at the
+   source level, but because the asm symbols do.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+void
+L_fn () /* { dg-error "duplicate" "" } */
+{
+}
+
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __attribute__ ((sym_alias ("M_var")))
+m ()
+{
+}
+
+int M_var; /* { dg-error "duplicate" "" } */
+
+
+void __attribute__ ((sym_alias ("N_sym")))
+n_fn ()
+{
+}
+
+int __attribute__ ((sym_alias ("N_sym")))
+n_var; /* { dg-error "duplicate" } */
+
+
+int __attribute__ ((sym_alias ("O_sym")))
+o_var;
+
+void __attribute__ ((sym_alias ("O_sym")))
+o_fn () /* { dg-error "duplicate" } */
+{
+}
+
+int __attribute__ ((sym_alias ("P_sym")))
+p_var1;
+
+int __attribute__ ((sym_alias ("P_sym")))
+p_var2; /* { dg-error "duplicate" } */
+
+void __attribute__ ((sym_alias ("Q_sym")))
+q_fn1 ()
+{
+}
+
+void __attribute__ ((sym_alias ("Q_sym")))
+q_fn2 () /* { dg-error "duplicate" } */
+{
+}
+
+int __attribute__ ((sym_alias ("R_var")))
+R_var; /* { dg-error "duplicate" } */
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __attribute__ ((sym_alias ("S_fn")))
+S_fn () /* { dg-error "duplicate" } */
+{
+}
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
new file mode 100644
index 00000000000..52f8fb76496
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
@@ -0,0 +1,34 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int var_a = 1;
+
+void
+foo_a ()
+{
+  extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+}
+
+void
+foo_b ()
+{
+  extern int var_b __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+}
+
+int var_b;
+
+void __attribute__ ((__sym_alias__ ("FOOBAR_C")))
+foo_c ()
+{
+  void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+  /* Another sym for var_b.  */
+  extern int var_b __attribute__ ((__sym_alias__ ("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-sym-alias-4.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
new file mode 100644
index 00000000000..2beafa46379
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-require-alias "" } */
+
+int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A"))) = 42;
+
+int __attribute__ ((__sym_alias__ ("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/c-c++-common/torture/attr-sym-alias-5.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
new file mode 100644
index 00000000000..bee90136daf
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
@@ -0,0 +1,54 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+/* { dg-require-visibility "" } */
+
+int __attribute__ ((sym_alias ("A_hid"), visibility ("hidden"))) a;
+
+extern int __attribute__ ((sym_alias ("B_prt"))) b;
+
+int b __attribute__ ((visibility ("protected")));
+
+extern int __attribute__ ((sym_alias ("C_ntr"))) c;
+
+extern int c __attribute__ ((visibility ("internal")));
+
+int c;
+
+static int d __attribute__ ((sym_alias ("D_stt")));
+
+
+extern void __attribute__ ((sym_alias ("F_hid"), visibility ("hidden")))
+f ();
+
+void
+f () {
+}
+
+extern void __attribute__ ((sym_alias ("G_prt")))
+g ();
+
+void __attribute__ ((visibility ("protected")))
+g () {
+}
+
+extern void __attribute__ ((sym_alias ("H_ntr")))
+h ();
+
+void __attribute__ ((visibility ("internal")))
+h ();
+
+void h () {
+}
+
+static void __attribute__ ((sym_alias ("I_stt")))
+i () {
+}
+
+/* { dg-final { scan-assembler {hidden[^\n]*A_hid} } } */
+/* { dg-final { scan-assembler {protected[^\n]*B_prt} } } */
+/* { dg-final { scan-assembler {internal[^\n]*C_ntr} } } */
+/* { dg-final { scan-assembler-not {glob[^\n]*D_stt} } } */
+/* { dg-final { scan-assembler {hidden[^\n]*F_hid} } } */
+/* { dg-final { scan-assembler {protected[^\n]*G_prt} } } */
+/* { dg-final { scan-assembler {internal[^\n]*H_ntr} } } */
+/* { dg-final { scan-assembler-not {glob[^\n]*I_stt} } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
new file mode 100644
index 00000000000..51729994913
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
@@ -0,0 +1,89 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+class __attribute__ ((__sym_alias__ ("FOOCLS_A"),
+		      __sym_alias__ ("FOOCLS_A_Dupe"))) foo {
+  static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  void __attribute__ ((__sym_alias__ ("FOOBAR_A"))) bar ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+int foo::var = 1;
+
+foo::foo () {}
+
+void foo::bar () {}
+
+namespace b {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_B"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_B"))) foo ();
+    void __attribute__ ((__sym_alias__ ("FOOBAR_B"))) bar () {}
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_B"))) ~foo() {}
+  };
+
+  int foo::var = 2;
+
+  foo::foo () {
+    void (foo::*pbar)() = &foo::bar;
+  }
+}
+
+namespace c {
+  namespace cd {
+    class __attribute__ ((__sym_alias__ ("FOOCLS_C"))) foo {
+      static int var __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+      __attribute__ ((__sym_alias__ ("FOOCTR_C"))) foo () {
+	void (foo::*pbar)() = &foo::bar;
+      }
+      void __attribute__ ((__sym_alias__ ("FOOBAR_C"))) bar () {}
+      virtual __attribute__ ((__sym_alias__ ("FOODTR_C"))) ~foo() {}
+    };
+
+    int foo::var = 3;
+  }
+}
+
+#include <typeinfo>
+
+namespace d {
+  namespace {
+    class __attribute__ ((__sym_alias__ ("FOOCLS_D"))) foo {
+      virtual ~foo() {} // typeid(foo) is not defined otherwise.
+    };
+  }
+
+  extern std::type_info __attribute__ ((__alias__ ("FOOCLS_D"))) foo_d_typeinfo;
+}
+
+extern std::type_info __attribute__ ((__alias__ ("FOOCLS_C"))) foo_c_typeinfo;
+
+/* { dg-final { scan-assembler "FOOCLS_A" } } */
+/* { dg-final { scan-assembler "FOOCLS_A_Dupe" } } */
+/* { 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" } } */
+/* { dg-final { scan-assembler "FOOCLS_D" } } */
+/* { dg-final { scan-assembler "foo_d_typeinfo" } } */
+/* { dg-final { scan-assembler "foo_c_typeinfo" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
new file mode 100644
index 00000000000..6facfcc2cd9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+namespace {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_A"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo ();
+    void __attribute__ ((__sym_alias__ ("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-sym-alias-3.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
new file mode 100644
index 00000000000..42c7288ad4a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
@@ -0,0 +1,83 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+// sym can be applied to template function explicit instantiations.
+
+template <typename T>
+void
+fn(T) {
+};
+
+template void __attribute__ ((__sym_alias__ ("FOOFUN_UINT"))) fn<>(unsigned int);
+template void __attribute__ ((__sym_alias__ ("FOOFUN_LONG"))) fn<>(long);
+
+template<> void __attribute__ ((__sym_alias__ ("FOOFUN_CHAR"))) fn<>(char) {}
+
+
+template <typename T = void>
+struct
+foo {
+  virtual ~foo() {}
+
+  virtual void virtfun() {}
+
+  static void stfun() {}
+  void inlfun() {}
+};
+
+// Explicitly instantiate members before the enclosing class.
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_CHAR_VIRT"))) foo<char>::virtfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_CHAR_TI"))) foo<char>;
+
+// Though they're only output if the enclosing class is.
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_LONG_VIRT"))) foo<long>::virtfun();
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_LONG_TI_X"))) foo<long>;
+
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_VOID_ST"))) foo<void>::stfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_VOID_TI"))) foo<>;
+
+
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_X"))) foo<short>;
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_ST"))) foo<short>::stfun();
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_INL"))) foo<short>::inlfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_D"))) foo<short>;
+
+// Explicit specializations work too.
+
+template <>
+struct  __attribute__ ((__sym_alias__ ("FOOCLS_INT_TI")))
+foo<int>
+{
+  virtual ~foo() {}
+  virtual void __attribute__ ((__sym_alias__ ("FOOCLS_INT_VIRT"))) virtfun() {}
+};
+
+/* { dg-final { scan-assembler "FOOFUN_UINT" } } */
+/* { dg-final { scan-assembler "FOOFUN_LONG" } } */
+/* { dg-final { scan-assembler "FOOFUN_CHAR" } } */
+
+/* { dg-final { scan-assembler "FOOCLS_VOID_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_VOID_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_X" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_INL" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_D" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_TI_X" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_VIRT" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
new file mode 100644
index 00000000000..59b779de35d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+template <typename T = void>
+class
+__attribute__ ((__sym_alias__ ("FOOCLS")))
+foo // { dg-error "duplicate|already" }
+{
+  virtual ~foo() {}
+
+  template <typename U>
+  void
+    __attribute__ ((__sym_alias__ ("FOOTMF")))
+    tmemfun () {} // { dg-error "duplicate|already" }
+};
+
+template <typename T>
+void
+__attribute__ ((__sym_alias__ ("FOOTFN")))
+fn(T) { // { dg-error "duplicate|already" }
+};
+
+template class foo<>;
+template class foo<int>;
+template void foo<>::tmemfun<void>();
+template void foo<int>::tmemfun<void>();
+template void fn<>(int);
+template void fn<>(long);
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
new file mode 100644
index 00000000000..45d59b953c3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
@@ -0,0 +1,14 @@
+/* { dg-do compile { target c++11 } } */
+/* { dg-require-alias "" } */
+
+struct foo {
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+foo::foo () {}
+
+// Make sure the inherited cdtors don't duplicate the syms.
+struct bar : foo {
+  using foo::foo;
+};
diff --git a/gcc/varpool.cc b/gcc/varpool.cc
index e7b51b15e4a..33649906d1d 100644
--- a/gcc/varpool.cc
+++ b/gcc/varpool.cc
@@ -163,6 +163,9 @@ varpool_node::get_create (tree decl)
     }
 
   node->register_symbol ();
+
+  create_sym_alias_decls (decl);
+
   return node;
 }

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [gcc(refs/users/aoliva/heads/testme)] Introduce attribute sym_alias
@ 2023-12-07 19:44 Alexandre Oliva
  0 siblings, 0 replies; 33+ messages in thread
From: Alexandre Oliva @ 2023-12-07 19:44 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:bdf6d3e4e017670289c5a7375e55db0c03151796

commit bdf6d3e4e017670289c5a7375e55db0c03151796
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Thu Dec 7 00:38:40 2023 -0300

    Introduce attribute sym_alias
    
    This patch introduces an attribute to add extra asm names (aliases)
    for a decl 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, and
    also to local static variables.  In C++, it can also be attached to
    class types, namespace-scoped variables and functions, static data
    members, member functions, explicit instantiations and specializations
    of template functions, members and classes.
    
    When applied to constructors or destructor, additional sym aliases
    with _Base and _Del suffixes are defined for variants other than
    complete-object ones.  This changes the assumption that clones always
    carry the same attributes as their abstract declarations, so there is
    now a function to adjust them.
    
    C++ also had a bug in which attributes from local extern declarations
    failed to be propagated to a preexisting corresponding
    namespace-scoped decl.  I've fixed that, and adjusted acc tests that
    distinguished between C and C++ in this regard.
    
    Applying the attribute to class types is only valid in C++, and the
    effect is to attach the alias to the RTTI object associated with the
    class type.
    
    
    for  gcc/ChangeLog
    
            * attribs.cc: Include cgraph.h.
            (decl_attributes): Allow late introduction of sym_alias in
            types.
            (create_sym_alias_decl, create_sym_alias_decls): New.
            * attribs.h: Declare them.
            (FOR_EACH_SYM_ALIAS): New macro.
            * cgraph.cc (cgraph_node::create): Create sym_alias decls.
            * varpool.cc (varpool_node::get_create): Create sym_alias
            decls.
            * cgraph.h (symtab_node::remap_sym_alias_target): New.
            * symtab.cc (symtab_node::remap_sym_alias_target): Define.
            (symbol_table::insert_to_assembler_name_hash): Check for
            symbol name clashes.
            (symtab_node::noninterposable_alias): Drop sym_alias
            attributes.
            * cgraphunit.cc (cgraph_node::analyze): Create alias_target
            node if needed.
            (analyze_functions): Fixup visibility of implicit alias only
            after its node is analyzed.
            * doc/extend.texi (sym_alias): Document for variables,
            functions and types.
    
    for  gcc/ada/ChangeLog
    
            * doc/gnat_rm/interfacing_to_other_languages.rst: Mention
            attribute sym_alias to give RTTI symbols mnemonic names.
            * doc/gnat_ugn/the_gnat_compilation_model.rst: Mention
            aliases.  Fix incorrect ref to C1 ctor variant.
    
    for  gcc/c-family/ChangeLog
    
            * c-ada-spec.cc (pp_asm_name): Use first sym_alias if
            available.
            * c-attribs.cc (handle_sym_alias_attribute): New.
            (c_common_attribute_table): Add sym_alias.
            (handle_copy_attribute): Do not copy sym_alias attribute.
    
    for  gcc/c/ChangeLog
    
            * c-decl.cc (duplicate_decls): Remap sym_alias target.
            (finish_decl): Create varpool_node for local static
            variables.
    
    for  gcc/cp/ChangeLog
    
            * class.cc (adjust_clone_attributes): New.
            (copy_fndecl_with_name, build_clone): Call it.
            * cp-tree.h (adjust_clone_attributes): Declare.
            (update_sym_alias_interface): Declare.
            (update_tinfo_sym_alias): Declare.
            * decl.cc (duplicate_decls): Remap sym_alias target.
            Adjust clone attributes.
            (grokfndecl): Tentatively create sym_alias decls after
            adding attributes in e.g. a template member function explicit
            instantiation.
            * decl2.cc (cplus_decl_attributes): Update tinfo sym_alias.
            (copy_interface, update_sym_alias_interface): New.
            (determine_visibility): Update sym_alias interface.
            (tentative_decl_linkage, import_export_decl): Likewise.
            * name-lookup.cc: Include target.h and cgraph.h.
            (push_local_extern_decl_alias): Merge attributes with
            namespace-scoped decl, and drop duplicate sym_alias.
            * optimize.cc (maybe_clone_body): Re-adjust attributes after
            cloning them.  Update sym_alias interface.
            * rtti.cc: Include attribs.h and cgraph.h.
            (get_tinfo_decl): Copy sym_alias attributes from type to tinfo
            decl.  Create sym_alias decls.
            (update_tinfo_sym_alias): New.
    
    for  gcc/testsuite/ChangeLog
    
            * c-c++-common/goacc/declare-1.c: Adjust.
            * c-c++-common/goacc/declare-2.c: Adjust.
            * c-c++-common/torture/attr-sym-alias-1.c: New.
            * c-c++-common/torture/attr-sym-alias-2.c: New.
            * c-c++-common/torture/attr-sym-alias-3.c: New.
            * c-c++-common/torture/attr-sym-alias-4.c: New.
            * c-c++-common/torture/attr-sym-alias-5.c: New.
            * g++.dg/torture/attr-sym-alias-1.C: New.
            * g++.dg/torture/attr-sym-alias-2.C: New.
            * g++.dg/torture/attr-sym-alias-3.C: New.
            * g++.dg/torture/attr-sym-alias-4.C: New.
            * g++.dg/torture/attr-sym-alias-5.C: New.

Diff:
---
 .../doc/gnat_rm/interfacing_to_other_languages.rst |  6 ++
 .../doc/gnat_ugn/the_gnat_compilation_model.rst    | 10 ++-
 gcc/attribs.cc                                     | 74 +++++++++++++++-
 gcc/attribs.h                                      |  7 ++
 gcc/c-family/c-ada-spec.cc                         |  7 ++
 gcc/c-family/c-attribs.cc                          | 33 +++++++-
 gcc/c/c-decl.cc                                    |  7 ++
 gcc/cgraph.cc                                      |  2 +
 gcc/cgraph.h                                       |  4 +
 gcc/cgraphunit.cc                                  |  2 +-
 gcc/cp/class.cc                                    | 64 ++++++++++++++
 gcc/cp/cp-tree.h                                   |  4 +
 gcc/cp/decl.cc                                     |  4 +
 gcc/cp/decl2.cc                                    | 53 ++++++++++++
 gcc/cp/name-lookup.cc                              | 11 +++
 gcc/cp/optimize.cc                                 |  3 +
 gcc/cp/rtti.cc                                     | 71 +++++++++++++++-
 gcc/doc/extend.texi                                | 62 ++++++++++++++
 gcc/symtab.cc                                      | 62 ++++++++++++++
 gcc/testsuite/c-c++-common/goacc/declare-1.c       |  6 +-
 gcc/testsuite/c-c++-common/goacc/declare-2.c       | 14 +--
 .../c-c++-common/torture/attr-sym-alias-1.c        | 75 ++++++++++++++++
 .../c-c++-common/torture/attr-sym-alias-2.c        | 99 ++++++++++++++++++++++
 .../c-c++-common/torture/attr-sym-alias-3.c        | 34 ++++++++
 .../c-c++-common/torture/attr-sym-alias-4.c        | 28 ++++++
 .../c-c++-common/torture/attr-sym-alias-5.c        | 54 ++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C    | 89 +++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C    | 26 ++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C    | 83 ++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C    | 28 ++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C    | 14 +++
 gcc/varpool.cc                                     |  3 +
 32 files changed, 1020 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 ad0be511d48..b8de16ebf7c 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 ``sym_alias`` 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 fd15459203a..20045698962 100644
--- a/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
+++ b/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
@@ -4274,6 +4274,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__ ((__sym_alias__ ("Ctor_For_Animal")))
         Animal() {Age_Count = 0;};
       private:
         int Age_Count;
@@ -4306,6 +4307,7 @@ both Carnivore and Domestic, that is:
         virtual int  Number_Of_Teeth ();
         virtual void Set_Owner (char* Name);
 
+        __attribute__ ((__sym_alias__ ("Ctor_For_Dog"))) // mnemonic alias
         Dog(); // Constructor
       private:
         int  Tooth_Count;
@@ -4344,7 +4346,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;
@@ -4360,7 +4363,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,7 +4385,8 @@ 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.
+mangled name (or an alias) 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.cc b/gcc/attribs.cc
index ff4b638c25c..abfbbbf6294 100644
--- a/gcc/attribs.cc
+++ b/gcc/attribs.cc
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "target.h"
 #include "tree.h"
+#include "cgraph.h"
 #include "stringpool.h"
 #include "diagnostic-core.h"
 #include "attribs.h"
@@ -830,7 +831,8 @@ decl_attributes (tree *node, tree attributes, int flags,
 
       if (TYPE_P (*anode)
 	  && (flags & (int) ATTR_FLAG_TYPE_IN_PLACE)
-	  && COMPLETE_TYPE_P (*anode))
+	  && COMPLETE_TYPE_P (*anode)
+	  && !is_attribute_p ("sym_alias", name))
 	{
 	  warning (OPT_Wattributes, "type attributes ignored after type is already defined");
 	  continue;
@@ -2672,6 +2674,76 @@ attr_access::array_as_string (tree type) const
   return typstr;
 }
 
+/* Create a sym attribute for DECL to be visible with linkage name ID.  */
+
+tree
+create_sym_alias_decl (tree decl, tree id)
+{
+  const char *attr_str = "sym_alias";
+
+  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;
+
+      tree attr_name = get_identifier (attr_str);
+      error_at (DECL_SOURCE_LOCATION (decl),
+		"duplicate symbol name %qE in %qE attribute of %qD",
+		id, attr_name, decl);
+      inform (DECL_SOURCE_LOCATION (sym_node->decl),
+	      "already used by %qD", sym_node->decl);
+    }
+
+  tree clone = copy_node (decl);
+  DECL_ATTRIBUTES (clone) = remove_attribute (attr_str,
+					      DECL_ATTRIBUTES (decl));
+  /* Mark it as a "sym alias" for decl, an internal attribute that
+     enables symbol_table::insert_to_assembler_name_hash can recognize
+     sym_alias decls.  */
+  DECL_ATTRIBUTES (clone) = tree_cons (get_identifier ("sym alias"),
+				       decl, DECL_ATTRIBUTES (clone));
+  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;
+
+  symtab_node *node;
+  if (VAR_P (clone))
+    // DECL_READ_P (clone) = 1;
+    // node = varpool_node::create_extra_name_alias (clone, decl);
+    node = varpool_node::create_alias (clone, decl);
+  else
+    node = cgraph_node::create_same_body_alias (clone, decl);
+    // node = cgraph_node::create_alias (clone, decl);
+  if (symtab_node *dnode = symtab_node::get_create (decl))
+    node->copy_visibility_from (dnode);
+
+  return clone;
+}
+
+/* Create decls for all sym aliases requested in DECL's attributes.  */
+
+void
+create_sym_alias_decls (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl)
+      || DECL_ABSTRACT_P (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      create_sym_alias_decl (decl, id);
+    }
+}
+
 #if CHECKING_P
 
 namespace selftest
diff --git a/gcc/attribs.h b/gcc/attribs.h
index fdeebff1cd9..b7a67f73550 100644
--- a/gcc/attribs.h
+++ b/gcc/attribs.h
@@ -404,4 +404,11 @@ extern void init_attr_rdwr_indices (rdwr_map *, tree);
 extern attr_access *get_parm_access (rdwr_map &, tree,
 				     tree = current_function_decl);
 
+extern tree create_sym_alias_decl (tree, tree);
+extern void create_sym_alias_decls (tree);
+
+#define FOR_EACH_SYM_ALIAS(sym, attrs)					\
+  for (tree sym = lookup_attribute ("sym_alias", (attrs));		\
+       sym; sym = lookup_attribute ("sym_alias", TREE_CHAIN (sym)))
+
 #endif // GCC_ATTRIBS_H
diff --git a/gcc/c-family/c-ada-spec.cc b/gcc/c-family/c-ada-spec.cc
index 050994d8416..5042b9cfecd 100644
--- a/gcc/c-family/c-ada-spec.cc
+++ b/gcc/c-family/c-ada-spec.cc
@@ -1431,6 +1431,13 @@ pp_ada_tree_identifier (pretty_printer *buffer, tree node, tree type,
 static void
 pp_asm_name (pretty_printer *buffer, tree t)
 {
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (t))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.cc b/gcc/c-family/c-attribs.cc
index 854e987dc79..2593fde1fe9 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -110,7 +110,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_sym_alias_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,
@@ -392,6 +393,8 @@ const struct attribute_spec c_common_gnu_attributes[] =
 			      handle_alias_attribute, NULL },
   { "weakref",                0, 1, true,  false, false, false,
 			      handle_weakref_attribute, NULL },
+  { "sym_alias",              1, 1, false,  false, false, false,
+			      handle_sym_alias_attribute, NULL },
   { "no_instrument_function", 0, 0, true,  false, false, false,
 			      handle_no_instrument_function_attribute,
 			      NULL },
@@ -3092,7 +3095,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
@@ -3102,7 +3105,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
@@ -3112,6 +3115,29 @@ handle_alias_attribute (tree *node, tree name, tree args,
   return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs);
 }
 
+/* Handle a "sym_alias" attribute; arguments as in struct
+   attribute_spec.handler.  */
+
+static tree
+handle_sym_alias_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 (decl_in_symtab_p (node))
+    *no_add_attrs = false;
+  else if (TYPE_P (node) && c_dialect_cxx ())
+    *no_add_attrs = false;
+  else
+    return error_mark_node;
+
+  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.  */
 
@@ -3245,6 +3271,7 @@ handle_copy_attribute (tree *node, tree name, tree args,
 	      || is_attribute_p ("visibility", atname)
 	      || is_attribute_p ("weak", atname)
 	      || is_attribute_p ("weakref", atname)
+	      || is_attribute_p ("sym_alias", atname)
 	      || is_attribute_p ("target_clones", atname))
 	    continue;
 
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 92c83e1bf10..c1490e627df 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -3117,6 +3117,8 @@ duplicate_decls (tree newdecl, tree olddecl)
 
   merge_decls (newdecl, olddecl, newtype, oldtype);
 
+  symtab_node::remap_sym_alias_target (newdecl, olddecl);
+
   /* The NEWDECL will no longer be needed.
 
      Before releasing the node, be sure to remove function from symbol
@@ -5902,6 +5904,11 @@ finish_decl (tree decl, location_t init_loc, tree init,
 	    set_user_assembler_name (decl, asmspec);
 	}
 
+      /* Give attribute sym_alias a chance to make the symbol name
+	 available for aliasing, even for a static local variable.  */
+      if (VAR_P (decl) && !DECL_EXTERNAL (decl) && is_global_var (decl))
+	varpool_node::get_create (decl);
+
       if (DECL_FILE_SCOPE_P (decl))
 	{
 	  if (DECL_INITIAL (decl) == NULL_TREE
diff --git a/gcc/cgraph.cc b/gcc/cgraph.cc
index f93259a8c70..0bef2179e89 100644
--- a/gcc/cgraph.cc
+++ b/gcc/cgraph.cc
@@ -523,6 +523,8 @@ cgraph_node::create (tree decl)
   node->register_symbol ();
   maybe_record_nested_function (node);
 
+  create_sym_alias_decls (decl);
+
   return node;
 }
 
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index 2b320557616..6d61e327fb6 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -327,6 +327,10 @@ public:
   /* Return DECL that alias is aliasing.  */
   inline tree get_alias_target_tree ();
 
+  /* Remap sym alias nodes recorded as aliasing REPLACED to alias REPLACEMENT
+     instead.  */
+  static void remap_sym_alias_target (tree replaced, tree replacement);
+
   /* Set section for symbol and its aliases.  */
   void set_section (const char *section);
 
diff --git a/gcc/cgraphunit.cc b/gcc/cgraphunit.cc
index 9a550a5cce6..a66db795c78 100644
--- a/gcc/cgraphunit.cc
+++ b/gcc/cgraphunit.cc
@@ -1176,7 +1176,7 @@ analyze_functions (bool first_time)
      C++ FE is confused about the COMDAT groups being right.  */
   if (symtab->cpp_implicit_aliases_done)
     FOR_EACH_SYMBOL (node)
-      if (node->cpp_implicit_alias)
+      if (node->cpp_implicit_alias && node->analyzed)
 	  node->fixup_same_cpp_alias_visibility (node->get_alias_target ());
   build_type_inheritance_graph ();
 
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index 6fdb56abfb9..fa8fc63db82 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -4896,6 +4896,68 @@ check_methods (tree t)
     }
 }
 
+/* Adjust sym alias name for CLONE, cloned from FN and named NAME,
+   if it is a cdtor, and drop the sym alias from other clones.  */
+
+void
+adjust_clone_attributes (tree fn, tree clone, tree name, bool skip_copy_p)
+{
+  if (IDENTIFIER_CDTOR_P (name))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	{
+	  found = true;
+	  break;
+	}
+
+      if (found
+	  && (name == complete_ctor_identifier
+	      || name == complete_dtor_identifier))
+	{
+	  /* Reuse the sym alias decls created for the primary cdtor
+	     decl.  */
+	  symtab_node::remap_sym_alias_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);
+
+	  if (!skip_copy_p)
+	    DECL_ATTRIBUTES (clone) = copy_list (DECL_ATTRIBUTES (clone));
+
+	  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	    {
+	      /* We need to copy this even with skip_copy_p, because
+		 even then copying was shallow.  */
+	      TREE_VALUE (sym) = copy_list (TREE_VALUE (sym));
+	      /* Append suf to the sym alias name.  */
+	      tree str = TREE_VALUE (TREE_VALUE (sym));
+	      char *symname = concat (TREE_STRING_POINTER (str), suf, NULL);
+	      str = build_string (TREE_STRING_LENGTH (str) + xlen, symname);
+	      TREE_VALUE (TREE_VALUE (sym)) = str;
+	      free (symname);
+	    }
+
+	  if (symtab_node::get (clone))
+	    create_sym_alias_decls (clone);
+	}
+    }
+  else
+    DECL_ATTRIBUTES (clone)
+      = remove_attribute ("sym_alias", DECL_ATTRIBUTES (clone));
+}
+
 /* FN is constructor, destructor or operator function.  Clone the
    declaration to create a NAME'd variant.  NEED_VTT_PARM_P and
    OMIT_INHERITED_PARMS_P are relevant if it's a cdtor.  */
@@ -5065,6 +5127,8 @@ build_clone (tree fn, tree name, bool need_vtt_parm_p,
   DECL_CHAIN (clone) = DECL_CHAIN (fn);
   DECL_CHAIN (fn) = clone;
 
+  adjust_clone_attributes (fn, clone, name);
+
   return clone;
 }
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index cb89d372b23..4d8e361bc77 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5806,6 +5806,8 @@ struct GTY((for_user)) spec_entry
 
 extern int current_class_depth;
 
+void adjust_clone_attributes (tree fn, tree clone, tree name, bool = false);
+
 /* in decl.cc */
 
 /* An array of static vars & fns.  */
@@ -7043,6 +7045,7 @@ extern void do_push_parm_decls			(tree, tree, tree *);
 extern tree do_aggregate_paren_init		(tree, tree);
 
 /* in decl2.cc */
+extern void update_sym_alias_interface		(tree);
 extern void record_mangling			(tree, bool);
 extern void overwrite_mangling			(tree, tree);
 extern void note_mangling_alias			(tree, tree);
@@ -7617,6 +7620,7 @@ extern bool emit_tinfo_decl			(tree);
 extern unsigned get_pseudo_tinfo_index		(tree);
 extern tree get_pseudo_tinfo_type		(unsigned);
 extern tree build_if_nonnull			(tree, tree, tsubst_flags_t);
+extern void update_tinfo_sym_alias		(tree);
 
 /* in search.cc */
 extern tree get_parent_with_private_access 	(tree decl, tree binfo);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 4b685270097..cc7f7a99487 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -3277,6 +3277,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
 	      && TREE_STATIC (olddecl))))
     make_decl_rtl (olddecl);
 
+  symtab_node::remap_sym_alias_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.
@@ -3300,6 +3302,7 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
       FOR_EACH_CLONE (clone, olddecl)
 	{
 	  DECL_ATTRIBUTES (clone) = DECL_ATTRIBUTES (olddecl);
+	  adjust_clone_attributes (olddecl, clone, DECL_NAME (clone));
 	  DECL_PRESERVE_P (clone) |= DECL_PRESERVE_P (olddecl);
 	}
     }
@@ -10838,6 +10841,7 @@ grokfndecl (tree ctype,
     {
       cplus_decl_attributes (&decl, *attrlist, 0);
       *attrlist = NULL_TREE;
+      create_sym_alias_decls (decl);
     }
 
   if (DECL_HAS_CONTRACTS_P (decl))
diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
index bee84879023..00f19962e5d 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -1846,6 +1846,9 @@ cplus_decl_attributes (tree *decl, tree attributes, int flags)
   if (late_attrs)
     save_template_attributes (late_attrs, decl, flags);
 
+  if (TYPE_P (*decl) && attributes)
+    update_tinfo_sym_alias (*decl);
+
   /* Propagate deprecation out to the template.  */
   if (TREE_DEPRECATED (*decl))
     if (tree ti = get_template_info (*decl))
@@ -2202,6 +2205,50 @@ 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);
+  if (symtab_node *dest_node = symtab_node::get (dest))
+    if (symtab_node *decl_node = symtab_node::get (decl))
+      decl_node->copy_visibility_from (dest_node);
+}
+
+/* Propagate linkage changes to sym aliases.  */
+
+void
+update_sym_alias_interface (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.  */
 
@@ -3009,6 +3056,8 @@ determine_visibility (tree decl)
        translation unit, we can make the type internal.  */
     constrain_visibility (decl, VISIBILITY_ANON, false);
 
+  update_sym_alias_interface (decl);
+
   /* If visibility changed and DECL already has DECL_RTL, ensure
      symbol flags are updated.  */
   if ((DECL_VISIBILITY (decl) != orig_visibility
@@ -3271,6 +3320,8 @@ tentative_decl_linkage (tree decl)
       else if (VAR_P (decl))
 	maybe_commonize_var (decl);
     }
+
+  update_sym_alias_interface (decl);
 }
 
 /* DECL is a FUNCTION_DECL or VAR_DECL.  If the object file linkage
@@ -3505,6 +3556,8 @@ import_export_decl (tree decl)
     }
 
   DECL_INTERFACE_KNOWN (decl) = 1;
+
+  update_sym_alias_interface (decl);
 }
 
 /* Return an expression that performs the destruction of DECL, which
diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc
index 76f1d44610a..3a19a571256 100644
--- a/gcc/cp/name-lookup.cc
+++ b/gcc/cp/name-lookup.cc
@@ -22,9 +22,11 @@ along with GCC; see the file COPYING3.  If not see
 #define INCLUDE_MEMORY
 #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"
@@ -3675,6 +3677,15 @@ push_local_extern_decl_alias (tree decl)
 	  /* Adjust visibility.  */
 	  determine_visibility (alias);
 	}
+      else if (DECL_P (alias))
+	DECL_ATTRIBUTES (alias)
+	  = targetm.merge_decl_attributes (alias, decl);
+      if (DECL_P (alias))
+	{
+	  symtab_node::remap_sym_alias_target (decl, alias);
+	  DECL_ATTRIBUTES (decl)
+	    = remove_attribute ("sym_alias", DECL_ATTRIBUTES (alias));
+	}
     }
 
   retrofit_lang_decl (decl);
diff --git a/gcc/cp/optimize.cc b/gcc/cp/optimize.cc
index 9e8926e4cc6..4a2e8cb4354 100644
--- a/gcc/cp/optimize.cc
+++ b/gcc/cp/optimize.cc
@@ -528,9 +528,12 @@ maybe_clone_body (tree fn)
       DECL_VISIBILITY_SPECIFIED (clone) = DECL_VISIBILITY_SPECIFIED (fn);
       DECL_DLLIMPORT_P (clone) = DECL_DLLIMPORT_P (fn);
       DECL_ATTRIBUTES (clone) = clone_attrs (DECL_ATTRIBUTES (fn));
+      adjust_clone_attributes (fn, clone, DECL_NAME (clone), true);
       DECL_DISREGARD_INLINE_LIMITS (clone) = DECL_DISREGARD_INLINE_LIMITS (fn);
       set_decl_section_name (clone, fn);
 
+      update_sym_alias_interface (clone);
+
       /* Adjust the parameter names and locations.  */
       parm = DECL_ARGUMENTS (fn);
       clone_parm = DECL_ARGUMENTS (clone);
diff --git a/gcc/cp/rtti.cc b/gcc/cp/rtti.cc
index 7878929c246..4bb7b8c8c6e 100644
--- a/gcc/cp/rtti.cc
+++ b/gcc/cp/rtti.cc
@@ -28,8 +28,10 @@ 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"
+#include "cgraph.h"
 
 /* C++ returns type information to the user in struct type_info
    objects. We also use type information to implement dynamic_cast and
@@ -479,8 +481,13 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
 	  = build_tree_list (get_identifier ("non overlapping"),
 			     NULL_TREE);
       else
+	/* Share the non overlapping attribute, without assuming it's
+	   the only attribute, but assuming it's the last if it's
+	   present.  There may be sym aliases too, and those are not
+	   to be shared.  */
 	DECL_ATTRIBUTES (d)
-	  = DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]);
+	  = lookup_attribute ("non overlapping",
+			      DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]));
 
       /* Mark the variable as undefined -- but remember that we can
 	 define it later if we need to do so.  */
@@ -492,6 +499,16 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
       if (CLASS_TYPE_P (type))
 	CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type)) = d;
 
+      /* Copy sym alias attributes from the type to the rtti obj decl.  */
+      tree *attrs = &DECL_ATTRIBUTES (d);
+      FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+	{
+	  tree attr = tree_cons (TREE_PURPOSE (sym), TREE_VALUE (sym), *attrs);
+	  *attrs = attr;
+	  attrs = &TREE_CHAIN (attr);
+	}
+      create_sym_alias_decls (d);
+
       /* Add decl to the global array of tinfo decls.  */
       vec_safe_push (unemitted_tinfo_decls, d);
     }
@@ -499,6 +516,58 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
   return d;
 }
 
+/* After modifying the attributes of TYPE, check whether tinfo was
+   already created and, if so, add to it any sym alias attributes
+   that were not already present.  */
+
+void
+update_tinfo_sym_alias (tree type)
+{
+  if (!TYPE_SIZE (type) || !CLASS_TYPE_P (type))
+    return;
+
+  tree d = CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type));
+  if (!d)
+    return;
+
+  bool first = true;
+  symtab_node *node = NULL;
+
+  tree *attrs = &DECL_ATTRIBUTES (d);
+  FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (d_sym, *attrs)
+	if (TREE_VALUE (sym) == TREE_VALUE (d_sym))
+	  {
+	    found = true;
+	    break;
+	  }
+
+      if (found)
+	continue;
+
+      tree attr = tree_cons (TREE_PURPOSE (sym),
+			     TREE_VALUE (sym),
+			     *attrs);
+      *attrs = attr;
+      attrs = &TREE_CHAIN (attr);
+
+      if (first)
+	{
+	  first = false;
+	  node = symtab_node::get (d);
+	}
+
+      if (!node)
+	continue;
+
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+      create_sym_alias_decl (d, id);
+    }
+}
+
 /* Return the type_info object for TYPE.  */
 
 tree
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index af782b3f228..22b7e3ab7bf 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -4142,6 +4142,49 @@ Function Attributes}, @ref{PowerPC Function Attributes},
 @ref{Nios II Function Attributes}, and @ref{S/390 Function Attributes}
 for details.
 
+@cindex @code{sym_alias} function attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} attribute causes @var{name} to be output as an
+alias to the function, with the same linkage and visibility as the
+function, when the function definition is output, provided that the
+function could be an alias target.  For instance,
+
+@smallexample
+void f (uint64_t) __attribute__ ((__sym_alias__ ("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},
+with the same linkage and visibility.  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.
+
+Aliases introduced with this attribute, such as @samp{f_u64} in the
+example above, are assembly symbol names: they do not undergo C++ name
+mangling, and are not made visible in any scope in the source language.
+They can, however, be named as alias targets, as long as their
+definition is output.  Naming a @samp{sym_alias} as an alias target will
+@emph{not} cause a definition to be output if it otherwise wouldn't.
+This may affect inline functions, C++ template instantiations, and other
+synthesized definitions that would have to be (synthesized and) compiled
+in order for the @samp{sym_alias} to be considered as a potential alias
+target.
+
+This attribute requires assembler and object file support for aliases,
+and may not be available on all targets.
+
 @cindex @code{symver} function attribute
 @item symver ("@var{name2}@@@var{nodename}")
 On ELF targets this attribute creates a symbol version.  The @var{name2} part
@@ -8069,6 +8112,10 @@ will be placed in new, unique sections.
 
 This additional functionality requires Binutils version 2.36 or later.
 
+@cindex @code{sym_alias} variable attribute
+@item sym_alias ("@var{name}")
+See @pxref{Common Function Attributes}.
+
 @cindex @code{uninitialized} variable attribute
 @item uninitialized
 This attribute, attached to a variable with automatic storage, means that
@@ -9164,6 +9211,21 @@ is not supported; that is to say, if a given scalar object can be accessed
 through distinct types that assign a different storage order to it, then the
 behavior is undefined.
 
+@cindex @code{sym_alias} type attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} type 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__ ((__sym_alias__ ("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.
+
 @cindex @code{transparent_union} type attribute
 @item transparent_union
 
diff --git a/gcc/symtab.cc b/gcc/symtab.cc
index 0470509a98d..44df52095c1 100644
--- a/gcc/symtab.cc
+++ b/gcc/symtab.cc
@@ -187,6 +187,22 @@ symbol_table::insert_to_assembler_name_hash (symtab_node *node,
 	(*aslot)->previous_sharing_asm_name = node;
       *aslot = node;
 
+      /* Check for sym_alias name clashes.  In create_sym_alias_decl,
+	 we check for a preexisting definition using the same
+	 assembler_name, so here we check for a previously-defined
+	 sym_name, marked with a "sym name" pseudo-attribute that
+	 points back at the declaration for which it was created.  */
+      if (symtab_node *sym_node = node->next_sharing_asm_name)
+	if (lookup_attribute ("sym alias",
+			      DECL_ATTRIBUTES (sym_node->decl)))
+	  {
+	    error_at (DECL_SOURCE_LOCATION (node->decl),
+		      "duplicate symbol name %qE", decl);
+	    inform (DECL_SOURCE_LOCATION (sym_node->decl),
+		    "already used by %qD in a %qE attribute",
+		    sym_node->decl, get_identifier ("sym_alias"));
+	  }
+
       /* Update also possible inline clones sharing a decl.  */
       cnode = dyn_cast <cgraph_node *> (node);
       if (cnode && cnode->clones && with_clones)
@@ -1943,6 +1959,46 @@ symtab_node::noninterposable_alias (symtab_node *node, void *data)
   return false;
 }
 
+/* Remap sym alias nodes recorded as aliasing REPLACED to alias
+   REPLACEMENT instead.  */
+
+void
+symtab_node::remap_sym_alias_target (tree replaced, tree replacement)
+{
+  symtab_node *repl_node = NULL;
+  if (!decl_in_symtab_p (replacement)
+      || !(repl_node = symtab_node::get (replacement)))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (replaced))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      symtab_node *sym_node = symtab_node::get_for_asmname (id);
+
+      if (!sym_node)
+	{
+	  create_sym_alias_decl (replacement, id);
+	  continue;
+	}
+
+      gcc_assert (!sym_node->analyzed);
+      if (sym_node->alias_target != replaced)
+	continue;
+
+      // sym_node->definition = 0;
+
+      if (VAR_P (replaced))
+	// varpool_node::create_extra_name_alias (sym_node->decl, replacement);
+	varpool_node::create_alias (sym_node->decl, replacement);
+      else
+	cgraph_node::create_same_body_alias (sym_node->decl, replacement);
+	// cgraph_node::create_alias (sym_node->decl, replacement);
+      sym_node->copy_visibility_from (repl_node);
+    }
+}
+
 /* 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.  */
@@ -1970,6 +2026,12 @@ symtab_node::noninterposable_alias (void)
 
   /* Otherwise create a new one.  */
   new_decl = copy_node (node->decl);
+  DECL_ATTRIBUTES (new_decl) = remove_attribute ("sym_alias",
+						   DECL_ATTRIBUTES
+						   (new_decl));
+  DECL_ATTRIBUTES (new_decl) = remove_attribute ("sym alias",
+						   DECL_ATTRIBUTES
+						   (new_decl));
   DECL_DLLIMPORT_P (new_decl) = 0;
   tree name = clone_function_name (node->decl, "localalias");
   if (!flag_wpa)
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-1.c b/gcc/testsuite/c-c++-common/goacc/declare-1.c
index 46ee01b6759..f2842893318 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-1.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-1.c
@@ -113,11 +113,11 @@ f_2 (void)
   int va3;
 #pragma acc declare device_resident(va3)
 
-#ifndef __cplusplus
+#if 0
   /* TODO PR90868
 
-     C: "error: variable '[...]' used more than once with '#pragma acc declare'".  */
-#else
+     "error: variable '[...]' used more than once with '#pragma acc declare'".  */
+
   extern int ve0;
 #pragma acc declare create(ve0)
 
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-2.c b/gcc/testsuite/c-c++-common/goacc/declare-2.c
index e2e22be57e9..aec59b69754 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-2.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-2.c
@@ -137,25 +137,25 @@ void
 f_pr90868_2 (void)
 {
   extern int we0;
-#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" } */
 
   extern int we1;
-#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" } */
 
   extern int *we2;
-#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" } */
 
   extern int we3;
-#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" } */
 
   extern int we4;
-#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" } */
 
   extern int we5;
-#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" } */
  
   extern int we6;
-#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" } */
 }
 
 
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
new file mode 100644
index 00000000000..2fe1948aed9
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
@@ -0,0 +1,75 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+extern int var_alias __attribute__ ((__alias__ ("FOOVAR_A")));
+int var_a = 1;
+
+void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+
+void foo_alias () __attribute__ ((__alias__ ("FOOBAR_A")));
+
+void
+foo_a ()
+{
+}
+
+
+int var_b;
+extern int var_b __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+
+void
+foo_b ()
+{
+}
+
+void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+
+
+int var_c __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+
+void __attribute__ ((__sym_alias__ ("FOOBAR_C")))
+foo_c ()
+{
+}
+
+
+void baf () {}
+
+void foo_x () {
+  extern void baf () __attribute__ ((__sym_alias__ ("FOO_BAF")));
+  extern void bad () /* ok, but not defined, so no alias issued.  */
+    __attribute__ ((__sym_alias__ ("FOO_BAD")));
+  extern void bar () __attribute__ ((__sym_alias__ ("FOO_BAR")));
+
+  static int x __attribute__ ((sym_alias ("FOO_x")));
+}
+
+void bar () {}
+
+extern int xr __attribute__ ((alias ("FOO_x")));
+
+
+ /* The *u* symbols are not defined, so the sym_aliases do not clash.  ??? Is
+    this just an implementation artifact, or a feature someone might actually
+    rely on?  */
+extern int var_u1 __attribute__ ((__sym_alias__ ("VAR_U")));
+extern int var_u2 __attribute__ ((__sym_alias__ ("VAR_U")));
+
+void foo_u1 () __attribute__ ((__sym_alias__ ("FOOBAR_U")));
+void foo_u2 () __attribute__ ((__sym_alias__ ("FOOBAR_U")));
+
+
+/* { dg-final { scan-assembler "FOOBAR_A" } } */
+/* { dg-final { scan-assembler "foo_alias" } } */
+/* { dg-final { scan-assembler "FOOVAR_A" } } */
+/* { dg-final { scan-assembler "var_alias" } } */
+/* { dg-final { scan-assembler "FOOBAR_B" } } */
+/* { dg-final { scan-assembler "FOOVAR_B" } } */
+/* { dg-final { scan-assembler "FOOBAR_C" } } */
+/* { dg-final { scan-assembler "FOOVAR_C" } } */
+/* { dg-final { scan-assembler "FOO_BAF" } } */
+/* { dg-final { scan-assembler "FOO_BAR" } } */
+/* { dg-final { scan-assembler-not "FOO_BAD" } } */
+/* { dg-final { scan-assembler-not "VAR_U" } } */
+/* { dg-final { scan-assembler-not "FOOBAR_U" } } */
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
new file mode 100644
index 00000000000..f692a552606
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
@@ -0,0 +1,99 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+struct s
+{
+  int mem __attribute__ ((__sym_alias__ ("S_MEM"))); /* { dg-warning "attribute ignored" } */
+};
+
+void foo () {
+  extern void bar () __attribute__ ((__sym_alias__ ("FOO_BAR"))); /* Ok.  */
+  int i __attribute__ ((sym_alias ("FOO_i"))); /* { dg-warning "ignored" } */
+  static int x __attribute__ ((sym_alias ("FOO_x")));
+  static int sx __attribute__ ((alias ("FOO_x"))); /* { dg-warning "ignored" } */
+  extern int xx __attribute__ ((alias ("FOO_x"))); /* { dg-warning "ignored" } */
+  extern int y __attribute__ ((sym_alias ("FOO_y"))); /* Ok.  */
+}
+
+int dr __attribute__ ((alias ("FOO_x"))); /* { dg-error "defined both" } */
+
+
+int j __attribute__ ((sym_alias ("J_var")));
+void __attribute__ ((alias ("J_var")))
+j_fn (); /* { dg-error "between function and variable" } */
+
+
+void __attribute__ ((sym_alias ("K_fn")))
+k () {
+}
+extern int __attribute__ ((alias ("K_fn")))
+k_var; /* { dg-error "between function and variable" } */
+
+
+int l __attribute__ ((sym_alias ("L_fn")));
+
+/* These should be detected and reported, not because the names clash at the
+   source level, but because the asm symbols do.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+void
+L_fn () /* { dg-error "duplicate" "" } */
+{
+}
+
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __attribute__ ((sym_alias ("M_var")))
+m ()
+{
+}
+
+int M_var; /* { dg-error "duplicate" "" } */
+
+
+void __attribute__ ((sym_alias ("N_sym")))
+n_fn ()
+{
+}
+
+int __attribute__ ((sym_alias ("N_sym")))
+n_var; /* { dg-error "duplicate" } */
+
+
+int __attribute__ ((sym_alias ("O_sym")))
+o_var;
+
+void __attribute__ ((sym_alias ("O_sym")))
+o_fn () /* { dg-error "duplicate" } */
+{
+}
+
+int __attribute__ ((sym_alias ("P_sym")))
+p_var1;
+
+int __attribute__ ((sym_alias ("P_sym")))
+p_var2; /* { dg-error "duplicate" } */
+
+void __attribute__ ((sym_alias ("Q_sym")))
+q_fn1 ()
+{
+}
+
+void __attribute__ ((sym_alias ("Q_sym")))
+q_fn2 () /* { dg-error "duplicate" } */
+{
+}
+
+int __attribute__ ((sym_alias ("R_var")))
+R_var; /* { dg-error "duplicate" } */
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __attribute__ ((sym_alias ("S_fn")))
+S_fn () /* { dg-error "duplicate" } */
+{
+}
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
new file mode 100644
index 00000000000..52f8fb76496
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
@@ -0,0 +1,34 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int var_a = 1;
+
+void
+foo_a ()
+{
+  extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+}
+
+void
+foo_b ()
+{
+  extern int var_b __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+}
+
+int var_b;
+
+void __attribute__ ((__sym_alias__ ("FOOBAR_C")))
+foo_c ()
+{
+  void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+  /* Another sym for var_b.  */
+  extern int var_b __attribute__ ((__sym_alias__ ("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-sym-alias-4.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
new file mode 100644
index 00000000000..2beafa46379
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-require-alias "" } */
+
+int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A"))) = 42;
+
+int __attribute__ ((__sym_alias__ ("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/c-c++-common/torture/attr-sym-alias-5.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
new file mode 100644
index 00000000000..bee90136daf
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
@@ -0,0 +1,54 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+/* { dg-require-visibility "" } */
+
+int __attribute__ ((sym_alias ("A_hid"), visibility ("hidden"))) a;
+
+extern int __attribute__ ((sym_alias ("B_prt"))) b;
+
+int b __attribute__ ((visibility ("protected")));
+
+extern int __attribute__ ((sym_alias ("C_ntr"))) c;
+
+extern int c __attribute__ ((visibility ("internal")));
+
+int c;
+
+static int d __attribute__ ((sym_alias ("D_stt")));
+
+
+extern void __attribute__ ((sym_alias ("F_hid"), visibility ("hidden")))
+f ();
+
+void
+f () {
+}
+
+extern void __attribute__ ((sym_alias ("G_prt")))
+g ();
+
+void __attribute__ ((visibility ("protected")))
+g () {
+}
+
+extern void __attribute__ ((sym_alias ("H_ntr")))
+h ();
+
+void __attribute__ ((visibility ("internal")))
+h ();
+
+void h () {
+}
+
+static void __attribute__ ((sym_alias ("I_stt")))
+i () {
+}
+
+/* { dg-final { scan-assembler {hidden[^\n]*A_hid} } } */
+/* { dg-final { scan-assembler {protected[^\n]*B_prt} } } */
+/* { dg-final { scan-assembler {internal[^\n]*C_ntr} } } */
+/* { dg-final { scan-assembler-not {glob[^\n]*D_stt} } } */
+/* { dg-final { scan-assembler {hidden[^\n]*F_hid} } } */
+/* { dg-final { scan-assembler {protected[^\n]*G_prt} } } */
+/* { dg-final { scan-assembler {internal[^\n]*H_ntr} } } */
+/* { dg-final { scan-assembler-not {glob[^\n]*I_stt} } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
new file mode 100644
index 00000000000..51729994913
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
@@ -0,0 +1,89 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+class __attribute__ ((__sym_alias__ ("FOOCLS_A"),
+		      __sym_alias__ ("FOOCLS_A_Dupe"))) foo {
+  static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  void __attribute__ ((__sym_alias__ ("FOOBAR_A"))) bar ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+int foo::var = 1;
+
+foo::foo () {}
+
+void foo::bar () {}
+
+namespace b {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_B"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_B"))) foo ();
+    void __attribute__ ((__sym_alias__ ("FOOBAR_B"))) bar () {}
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_B"))) ~foo() {}
+  };
+
+  int foo::var = 2;
+
+  foo::foo () {
+    void (foo::*pbar)() = &foo::bar;
+  }
+}
+
+namespace c {
+  namespace cd {
+    class __attribute__ ((__sym_alias__ ("FOOCLS_C"))) foo {
+      static int var __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+      __attribute__ ((__sym_alias__ ("FOOCTR_C"))) foo () {
+	void (foo::*pbar)() = &foo::bar;
+      }
+      void __attribute__ ((__sym_alias__ ("FOOBAR_C"))) bar () {}
+      virtual __attribute__ ((__sym_alias__ ("FOODTR_C"))) ~foo() {}
+    };
+
+    int foo::var = 3;
+  }
+}
+
+#include <typeinfo>
+
+namespace d {
+  namespace {
+    class __attribute__ ((__sym_alias__ ("FOOCLS_D"))) foo {
+      virtual ~foo() {} // typeid(foo) is not defined otherwise.
+    };
+  }
+
+  extern std::type_info __attribute__ ((__alias__ ("FOOCLS_D"))) foo_d_typeinfo;
+}
+
+extern std::type_info __attribute__ ((__alias__ ("FOOCLS_C"))) foo_c_typeinfo;
+
+/* { dg-final { scan-assembler "FOOCLS_A" } } */
+/* { dg-final { scan-assembler "FOOCLS_A_Dupe" } } */
+/* { 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" } } */
+/* { dg-final { scan-assembler "FOOCLS_D" } } */
+/* { dg-final { scan-assembler "foo_d_typeinfo" } } */
+/* { dg-final { scan-assembler "foo_c_typeinfo" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
new file mode 100644
index 00000000000..6facfcc2cd9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+namespace {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_A"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo ();
+    void __attribute__ ((__sym_alias__ ("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-sym-alias-3.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
new file mode 100644
index 00000000000..42c7288ad4a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
@@ -0,0 +1,83 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+// sym can be applied to template function explicit instantiations.
+
+template <typename T>
+void
+fn(T) {
+};
+
+template void __attribute__ ((__sym_alias__ ("FOOFUN_UINT"))) fn<>(unsigned int);
+template void __attribute__ ((__sym_alias__ ("FOOFUN_LONG"))) fn<>(long);
+
+template<> void __attribute__ ((__sym_alias__ ("FOOFUN_CHAR"))) fn<>(char) {}
+
+
+template <typename T = void>
+struct
+foo {
+  virtual ~foo() {}
+
+  virtual void virtfun() {}
+
+  static void stfun() {}
+  void inlfun() {}
+};
+
+// Explicitly instantiate members before the enclosing class.
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_CHAR_VIRT"))) foo<char>::virtfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_CHAR_TI"))) foo<char>;
+
+// Though they're only output if the enclosing class is.
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_LONG_VIRT"))) foo<long>::virtfun();
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_LONG_TI_X"))) foo<long>;
+
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_VOID_ST"))) foo<void>::stfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_VOID_TI"))) foo<>;
+
+
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_X"))) foo<short>;
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_ST"))) foo<short>::stfun();
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_INL"))) foo<short>::inlfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_D"))) foo<short>;
+
+// Explicit specializations work too.
+
+template <>
+struct  __attribute__ ((__sym_alias__ ("FOOCLS_INT_TI")))
+foo<int>
+{
+  virtual ~foo() {}
+  virtual void __attribute__ ((__sym_alias__ ("FOOCLS_INT_VIRT"))) virtfun() {}
+};
+
+/* { dg-final { scan-assembler "FOOFUN_UINT" } } */
+/* { dg-final { scan-assembler "FOOFUN_LONG" } } */
+/* { dg-final { scan-assembler "FOOFUN_CHAR" } } */
+
+/* { dg-final { scan-assembler "FOOCLS_VOID_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_VOID_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_X" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_INL" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_D" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_TI_X" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_VIRT" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
new file mode 100644
index 00000000000..59b779de35d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+template <typename T = void>
+class
+__attribute__ ((__sym_alias__ ("FOOCLS")))
+foo // { dg-error "duplicate|already" }
+{
+  virtual ~foo() {}
+
+  template <typename U>
+  void
+    __attribute__ ((__sym_alias__ ("FOOTMF")))
+    tmemfun () {} // { dg-error "duplicate|already" }
+};
+
+template <typename T>
+void
+__attribute__ ((__sym_alias__ ("FOOTFN")))
+fn(T) { // { dg-error "duplicate|already" }
+};
+
+template class foo<>;
+template class foo<int>;
+template void foo<>::tmemfun<void>();
+template void foo<int>::tmemfun<void>();
+template void fn<>(int);
+template void fn<>(long);
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
new file mode 100644
index 00000000000..45d59b953c3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
@@ -0,0 +1,14 @@
+/* { dg-do compile { target c++11 } } */
+/* { dg-require-alias "" } */
+
+struct foo {
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+foo::foo () {}
+
+// Make sure the inherited cdtors don't duplicate the syms.
+struct bar : foo {
+  using foo::foo;
+};
diff --git a/gcc/varpool.cc b/gcc/varpool.cc
index e7b51b15e4a..33649906d1d 100644
--- a/gcc/varpool.cc
+++ b/gcc/varpool.cc
@@ -163,6 +163,9 @@ varpool_node::get_create (tree decl)
     }
 
   node->register_symbol ();
+
+  create_sym_alias_decls (decl);
+
   return node;
 }

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [gcc(refs/users/aoliva/heads/testme)] Introduce attribute sym_alias
@ 2023-12-06 22:47 Alexandre Oliva
  0 siblings, 0 replies; 33+ messages in thread
From: Alexandre Oliva @ 2023-12-06 22:47 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:951a5afea86c7e6b81959108fa2014ceb5d3a612

commit 951a5afea86c7e6b81959108fa2014ceb5d3a612
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Fri Dec 1 14:31:46 2023 -0300

    Introduce attribute sym_alias
    
    This patch introduces an attribute to add extra asm names (aliases)
    for a decl 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, and
    also to local static variables.  In C++, it can also be attached to
    class types, namespace-scoped variables and functions, static data
    members, member functions, explicit instantiations and specializations
    of template functions, members and classes.
    
    When applied to constructors or destructor, additional sym aliases
    with _Base and _Del suffixes are defined for variants other than
    complete-object ones.  This changes the assumption that clones always
    carry the same attributes as their abstract declarations, so there is
    now a function to adjust them.
    
    C++ also had a bug in which attributes from local extern declarations
    failed to be propagated to a preexisting corresponding
    namespace-scoped decl.  I've fixed that, and adjusted acc tests that
    distinguished between C and C++ in this regard.
    
    Applying the attribute to class types is only valid in C++, and the
    effect is to attach the alias to the RTTI object associated with the
    class type.
    
    
    for  gcc/ChangeLog
    
            * attribs.cc: Include cgraph.h.
            (decl_attributes): Allow late introduction of sym_alias in
            types.
            (create_sym_alias_decl, create_sym_alias_decls): New.
            * attribs.h: Declare them.
            (FOR_EACH_SYM_ALIAS): New macro.
            * cgraph.cc (cgraph_node::create): Create sym_alias decls.
            * varpool.cc (varpool_node::get_create): Create sym_alias
            decls.
            * cgraph.h (symtab_node::remap_sym_alias_target): New.
            * symtab.cc (symtab_node::remap_sym_alias_target): Define.
            (symbol_table::insert_to_assembler_name_hash): Check for
            symbol name clashes.
            (symtab_node::noninterposable_alias): Drop sym_alias
            attributes.
            * cgraphunit.cc (cgraph_node::analyze): Create alias_target
            node if needed.
            (analyze_functions): Fixup visibility of implicit alias only
            after its node is analyzed.
            * doc/extend.texi (sym_alias): Document for variables,
            functions and types.
    
    for  gcc/ada/ChangeLog
    
            * doc/gnat_rm/interfacing_to_other_languages.rst: Mention
            attribute sym_alias to give RTTI symbols mnemonic names.
            * doc/gnat_ugn/the_gnat_compilation_model.rst: Mention
            aliases.  Fix incorrect ref to C1 ctor variant.
    
    for  gcc/c-family/ChangeLog
    
            * c-ada-spec.cc (pp_asm_name): Use first sym_alias if
            available.
            * c-attribs.cc (handle_sym_alias_attribute): New.
            (c_common_attribute_table): Add sym_alias.
            (handle_copy_attribute): Do not copy sym_alias attribute.
    
    for  gcc/c/ChangeLog
    
            * c-decl.cc (duplicate_decls): Remap sym_alias target.
            (finish_decl): Create varpool_node for local static
            variables.
    
    for  gcc/cp/ChangeLog
    
            * class.cc (adjust_clone_attributes): New.
            (copy_fndecl_with_name, build_clone): Call it.
            * cp-tree.h (adjust_clone_attributes): Declare.
            (update_sym_alias_interface): Declare.
            (update_tinfo_sym_alias): Declare.
            * decl.cc (duplicate_decls): Remap sym_alias target.
            Adjust clone attributes.
            (grokfndecl): Tentatively create sym_alias decls after
            adding attributes in e.g. a template member function explicit
            instantiation.
            * decl2.cc (cplus_decl_attributes): Update tinfo sym_alias.
            (copy_interface, update_sym_alias_interface): New.
            (determine_visibility): Update sym_alias interface.
            (tentative_decl_linkage, import_export_decl): Likewise.
            * name-lookup.cc: Include target.h and cgraph.h.
            (push_local_extern_decl_alias): Merge attributes with
            namespace-scoped decl, and drop duplicate sym_alias.
            * optimize.cc (maybe_clone_body): Re-adjust attributes after
            cloning them.  Update sym_alias interface.
            * rtti.cc: Include attribs.h and cgraph.h.
            (get_tinfo_decl): Copy sym_alias attributes from type to tinfo
            decl.  Create sym_alias decls.
            (update_tinfo_sym_alias): New.
    
    for  gcc/testsuite/ChangeLog
    
            * c-c++-common/goacc/declare-1.c: Adjust.
            * c-c++-common/goacc/declare-2.c: Adjust.
            * c-c++-common/torture/attr-sym-alias-1.c: New.
            * c-c++-common/torture/attr-sym-alias-2.c: New.
            * c-c++-common/torture/attr-sym-alias-3.c: New.
            * c-c++-common/torture/attr-sym-alias-4.c: New.
            * c-c++-common/torture/attr-sym-alias-5.c: New.
            * g++.dg/torture/attr-sym-alias-1.C: New.
            * g++.dg/torture/attr-sym-alias-2.C: New.
            * g++.dg/torture/attr-sym-alias-3.C: New.
            * g++.dg/torture/attr-sym-alias-4.C: New.
            * g++.dg/torture/attr-sym-alias-5.C: New.

Diff:
---
 .../doc/gnat_rm/interfacing_to_other_languages.rst |  6 ++
 .../doc/gnat_ugn/the_gnat_compilation_model.rst    | 10 ++-
 gcc/attribs.cc                                     | 74 +++++++++++++++-
 gcc/attribs.h                                      |  7 ++
 gcc/c-family/c-ada-spec.cc                         |  7 ++
 gcc/c-family/c-attribs.cc                          | 33 +++++++-
 gcc/c/c-decl.cc                                    |  7 ++
 gcc/cgraph.cc                                      |  2 +
 gcc/cgraph.h                                       |  4 +
 gcc/cgraphunit.cc                                  |  2 +-
 gcc/cp/class.cc                                    | 64 ++++++++++++++
 gcc/cp/cp-tree.h                                   |  4 +
 gcc/cp/decl.cc                                     |  4 +
 gcc/cp/decl2.cc                                    | 53 ++++++++++++
 gcc/cp/name-lookup.cc                              | 11 +++
 gcc/cp/optimize.cc                                 |  3 +
 gcc/cp/rtti.cc                                     | 71 +++++++++++++++-
 gcc/doc/extend.texi                                | 62 ++++++++++++++
 gcc/symtab.cc                                      | 62 ++++++++++++++
 gcc/testsuite/c-c++-common/goacc/declare-1.c       |  6 +-
 gcc/testsuite/c-c++-common/goacc/declare-2.c       | 14 +--
 .../c-c++-common/torture/attr-sym-alias-1.c        | 75 ++++++++++++++++
 .../c-c++-common/torture/attr-sym-alias-2.c        | 99 ++++++++++++++++++++++
 .../c-c++-common/torture/attr-sym-alias-3.c        | 34 ++++++++
 .../c-c++-common/torture/attr-sym-alias-4.c        | 28 ++++++
 .../c-c++-common/torture/attr-sym-alias-5.c        | 54 ++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C    | 89 +++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C    | 26 ++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C    | 83 ++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C    | 28 ++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C    | 14 +++
 gcc/varpool.cc                                     |  3 +
 32 files changed, 1020 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 ad0be511d48..b8de16ebf7c 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 ``sym_alias`` 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 fd15459203a..20045698962 100644
--- a/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
+++ b/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
@@ -4274,6 +4274,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__ ((__sym_alias__ ("Ctor_For_Animal")))
         Animal() {Age_Count = 0;};
       private:
         int Age_Count;
@@ -4306,6 +4307,7 @@ both Carnivore and Domestic, that is:
         virtual int  Number_Of_Teeth ();
         virtual void Set_Owner (char* Name);
 
+        __attribute__ ((__sym_alias__ ("Ctor_For_Dog"))) // mnemonic alias
         Dog(); // Constructor
       private:
         int  Tooth_Count;
@@ -4344,7 +4346,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;
@@ -4360,7 +4363,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,7 +4385,8 @@ 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.
+mangled name (or an alias) 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.cc b/gcc/attribs.cc
index ff4b638c25c..abfbbbf6294 100644
--- a/gcc/attribs.cc
+++ b/gcc/attribs.cc
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "target.h"
 #include "tree.h"
+#include "cgraph.h"
 #include "stringpool.h"
 #include "diagnostic-core.h"
 #include "attribs.h"
@@ -830,7 +831,8 @@ decl_attributes (tree *node, tree attributes, int flags,
 
       if (TYPE_P (*anode)
 	  && (flags & (int) ATTR_FLAG_TYPE_IN_PLACE)
-	  && COMPLETE_TYPE_P (*anode))
+	  && COMPLETE_TYPE_P (*anode)
+	  && !is_attribute_p ("sym_alias", name))
 	{
 	  warning (OPT_Wattributes, "type attributes ignored after type is already defined");
 	  continue;
@@ -2672,6 +2674,76 @@ attr_access::array_as_string (tree type) const
   return typstr;
 }
 
+/* Create a sym attribute for DECL to be visible with linkage name ID.  */
+
+tree
+create_sym_alias_decl (tree decl, tree id)
+{
+  const char *attr_str = "sym_alias";
+
+  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;
+
+      tree attr_name = get_identifier (attr_str);
+      error_at (DECL_SOURCE_LOCATION (decl),
+		"duplicate symbol name %qE in %qE attribute of %qD",
+		id, attr_name, decl);
+      inform (DECL_SOURCE_LOCATION (sym_node->decl),
+	      "already used by %qD", sym_node->decl);
+    }
+
+  tree clone = copy_node (decl);
+  DECL_ATTRIBUTES (clone) = remove_attribute (attr_str,
+					      DECL_ATTRIBUTES (decl));
+  /* Mark it as a "sym alias" for decl, an internal attribute that
+     enables symbol_table::insert_to_assembler_name_hash can recognize
+     sym_alias decls.  */
+  DECL_ATTRIBUTES (clone) = tree_cons (get_identifier ("sym alias"),
+				       decl, DECL_ATTRIBUTES (clone));
+  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;
+
+  symtab_node *node;
+  if (VAR_P (clone))
+    // DECL_READ_P (clone) = 1;
+    // node = varpool_node::create_extra_name_alias (clone, decl);
+    node = varpool_node::create_alias (clone, decl);
+  else
+    node = cgraph_node::create_same_body_alias (clone, decl);
+    // node = cgraph_node::create_alias (clone, decl);
+  if (symtab_node *dnode = symtab_node::get_create (decl))
+    node->copy_visibility_from (dnode);
+
+  return clone;
+}
+
+/* Create decls for all sym aliases requested in DECL's attributes.  */
+
+void
+create_sym_alias_decls (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl)
+      || DECL_ABSTRACT_P (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      create_sym_alias_decl (decl, id);
+    }
+}
+
 #if CHECKING_P
 
 namespace selftest
diff --git a/gcc/attribs.h b/gcc/attribs.h
index fdeebff1cd9..b7a67f73550 100644
--- a/gcc/attribs.h
+++ b/gcc/attribs.h
@@ -404,4 +404,11 @@ extern void init_attr_rdwr_indices (rdwr_map *, tree);
 extern attr_access *get_parm_access (rdwr_map &, tree,
 				     tree = current_function_decl);
 
+extern tree create_sym_alias_decl (tree, tree);
+extern void create_sym_alias_decls (tree);
+
+#define FOR_EACH_SYM_ALIAS(sym, attrs)					\
+  for (tree sym = lookup_attribute ("sym_alias", (attrs));		\
+       sym; sym = lookup_attribute ("sym_alias", TREE_CHAIN (sym)))
+
 #endif // GCC_ATTRIBS_H
diff --git a/gcc/c-family/c-ada-spec.cc b/gcc/c-family/c-ada-spec.cc
index 050994d8416..5042b9cfecd 100644
--- a/gcc/c-family/c-ada-spec.cc
+++ b/gcc/c-family/c-ada-spec.cc
@@ -1431,6 +1431,13 @@ pp_ada_tree_identifier (pretty_printer *buffer, tree node, tree type,
 static void
 pp_asm_name (pretty_printer *buffer, tree t)
 {
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (t))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.cc b/gcc/c-family/c-attribs.cc
index 854e987dc79..2593fde1fe9 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -110,7 +110,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_sym_alias_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,
@@ -392,6 +393,8 @@ const struct attribute_spec c_common_gnu_attributes[] =
 			      handle_alias_attribute, NULL },
   { "weakref",                0, 1, true,  false, false, false,
 			      handle_weakref_attribute, NULL },
+  { "sym_alias",              1, 1, false,  false, false, false,
+			      handle_sym_alias_attribute, NULL },
   { "no_instrument_function", 0, 0, true,  false, false, false,
 			      handle_no_instrument_function_attribute,
 			      NULL },
@@ -3092,7 +3095,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
@@ -3102,7 +3105,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
@@ -3112,6 +3115,29 @@ handle_alias_attribute (tree *node, tree name, tree args,
   return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs);
 }
 
+/* Handle a "sym_alias" attribute; arguments as in struct
+   attribute_spec.handler.  */
+
+static tree
+handle_sym_alias_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 (decl_in_symtab_p (node))
+    *no_add_attrs = false;
+  else if (TYPE_P (node) && c_dialect_cxx ())
+    *no_add_attrs = false;
+  else
+    return error_mark_node;
+
+  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.  */
 
@@ -3245,6 +3271,7 @@ handle_copy_attribute (tree *node, tree name, tree args,
 	      || is_attribute_p ("visibility", atname)
 	      || is_attribute_p ("weak", atname)
 	      || is_attribute_p ("weakref", atname)
+	      || is_attribute_p ("sym_alias", atname)
 	      || is_attribute_p ("target_clones", atname))
 	    continue;
 
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 92c83e1bf10..c1490e627df 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -3117,6 +3117,8 @@ duplicate_decls (tree newdecl, tree olddecl)
 
   merge_decls (newdecl, olddecl, newtype, oldtype);
 
+  symtab_node::remap_sym_alias_target (newdecl, olddecl);
+
   /* The NEWDECL will no longer be needed.
 
      Before releasing the node, be sure to remove function from symbol
@@ -5902,6 +5904,11 @@ finish_decl (tree decl, location_t init_loc, tree init,
 	    set_user_assembler_name (decl, asmspec);
 	}
 
+      /* Give attribute sym_alias a chance to make the symbol name
+	 available for aliasing, even for a static local variable.  */
+      if (VAR_P (decl) && !DECL_EXTERNAL (decl) && is_global_var (decl))
+	varpool_node::get_create (decl);
+
       if (DECL_FILE_SCOPE_P (decl))
 	{
 	  if (DECL_INITIAL (decl) == NULL_TREE
diff --git a/gcc/cgraph.cc b/gcc/cgraph.cc
index f93259a8c70..0bef2179e89 100644
--- a/gcc/cgraph.cc
+++ b/gcc/cgraph.cc
@@ -523,6 +523,8 @@ cgraph_node::create (tree decl)
   node->register_symbol ();
   maybe_record_nested_function (node);
 
+  create_sym_alias_decls (decl);
+
   return node;
 }
 
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index 2b320557616..6d61e327fb6 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -327,6 +327,10 @@ public:
   /* Return DECL that alias is aliasing.  */
   inline tree get_alias_target_tree ();
 
+  /* Remap sym alias nodes recorded as aliasing REPLACED to alias REPLACEMENT
+     instead.  */
+  static void remap_sym_alias_target (tree replaced, tree replacement);
+
   /* Set section for symbol and its aliases.  */
   void set_section (const char *section);
 
diff --git a/gcc/cgraphunit.cc b/gcc/cgraphunit.cc
index 9a550a5cce6..a66db795c78 100644
--- a/gcc/cgraphunit.cc
+++ b/gcc/cgraphunit.cc
@@ -1176,7 +1176,7 @@ analyze_functions (bool first_time)
      C++ FE is confused about the COMDAT groups being right.  */
   if (symtab->cpp_implicit_aliases_done)
     FOR_EACH_SYMBOL (node)
-      if (node->cpp_implicit_alias)
+      if (node->cpp_implicit_alias && node->analyzed)
 	  node->fixup_same_cpp_alias_visibility (node->get_alias_target ());
   build_type_inheritance_graph ();
 
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index 6fdb56abfb9..fa8fc63db82 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -4896,6 +4896,68 @@ check_methods (tree t)
     }
 }
 
+/* Adjust sym alias name for CLONE, cloned from FN and named NAME,
+   if it is a cdtor, and drop the sym alias from other clones.  */
+
+void
+adjust_clone_attributes (tree fn, tree clone, tree name, bool skip_copy_p)
+{
+  if (IDENTIFIER_CDTOR_P (name))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	{
+	  found = true;
+	  break;
+	}
+
+      if (found
+	  && (name == complete_ctor_identifier
+	      || name == complete_dtor_identifier))
+	{
+	  /* Reuse the sym alias decls created for the primary cdtor
+	     decl.  */
+	  symtab_node::remap_sym_alias_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);
+
+	  if (!skip_copy_p)
+	    DECL_ATTRIBUTES (clone) = copy_list (DECL_ATTRIBUTES (clone));
+
+	  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	    {
+	      /* We need to copy this even with skip_copy_p, because
+		 even then copying was shallow.  */
+	      TREE_VALUE (sym) = copy_list (TREE_VALUE (sym));
+	      /* Append suf to the sym alias name.  */
+	      tree str = TREE_VALUE (TREE_VALUE (sym));
+	      char *symname = concat (TREE_STRING_POINTER (str), suf, NULL);
+	      str = build_string (TREE_STRING_LENGTH (str) + xlen, symname);
+	      TREE_VALUE (TREE_VALUE (sym)) = str;
+	      free (symname);
+	    }
+
+	  if (symtab_node::get (clone))
+	    create_sym_alias_decls (clone);
+	}
+    }
+  else
+    DECL_ATTRIBUTES (clone)
+      = remove_attribute ("sym_alias", DECL_ATTRIBUTES (clone));
+}
+
 /* FN is constructor, destructor or operator function.  Clone the
    declaration to create a NAME'd variant.  NEED_VTT_PARM_P and
    OMIT_INHERITED_PARMS_P are relevant if it's a cdtor.  */
@@ -5065,6 +5127,8 @@ build_clone (tree fn, tree name, bool need_vtt_parm_p,
   DECL_CHAIN (clone) = DECL_CHAIN (fn);
   DECL_CHAIN (fn) = clone;
 
+  adjust_clone_attributes (fn, clone, name);
+
   return clone;
 }
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 795152c9ad2..dca2d31a372 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5806,6 +5806,8 @@ struct GTY((for_user)) spec_entry
 
 extern int current_class_depth;
 
+void adjust_clone_attributes (tree fn, tree clone, tree name, bool = false);
+
 /* in decl.cc */
 
 /* An array of static vars & fns.  */
@@ -7043,6 +7045,7 @@ extern void do_push_parm_decls			(tree, tree, tree *);
 extern tree do_aggregate_paren_init		(tree, tree);
 
 /* in decl2.cc */
+extern void update_sym_alias_interface		(tree);
 extern void record_mangling			(tree, bool);
 extern void overwrite_mangling			(tree, tree);
 extern void note_mangling_alias			(tree, tree);
@@ -7617,6 +7620,7 @@ extern bool emit_tinfo_decl			(tree);
 extern unsigned get_pseudo_tinfo_index		(tree);
 extern tree get_pseudo_tinfo_type		(unsigned);
 extern tree build_if_nonnull			(tree, tree, tsubst_flags_t);
+extern void update_tinfo_sym_alias		(tree);
 
 /* in search.cc */
 extern tree get_parent_with_private_access 	(tree decl, tree binfo);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 4b685270097..cc7f7a99487 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -3277,6 +3277,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
 	      && TREE_STATIC (olddecl))))
     make_decl_rtl (olddecl);
 
+  symtab_node::remap_sym_alias_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.
@@ -3300,6 +3302,7 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
       FOR_EACH_CLONE (clone, olddecl)
 	{
 	  DECL_ATTRIBUTES (clone) = DECL_ATTRIBUTES (olddecl);
+	  adjust_clone_attributes (olddecl, clone, DECL_NAME (clone));
 	  DECL_PRESERVE_P (clone) |= DECL_PRESERVE_P (olddecl);
 	}
     }
@@ -10838,6 +10841,7 @@ grokfndecl (tree ctype,
     {
       cplus_decl_attributes (&decl, *attrlist, 0);
       *attrlist = NULL_TREE;
+      create_sym_alias_decls (decl);
     }
 
   if (DECL_HAS_CONTRACTS_P (decl))
diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
index bee84879023..00f19962e5d 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -1846,6 +1846,9 @@ cplus_decl_attributes (tree *decl, tree attributes, int flags)
   if (late_attrs)
     save_template_attributes (late_attrs, decl, flags);
 
+  if (TYPE_P (*decl) && attributes)
+    update_tinfo_sym_alias (*decl);
+
   /* Propagate deprecation out to the template.  */
   if (TREE_DEPRECATED (*decl))
     if (tree ti = get_template_info (*decl))
@@ -2202,6 +2205,50 @@ 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);
+  if (symtab_node *dest_node = symtab_node::get (dest))
+    if (symtab_node *decl_node = symtab_node::get (decl))
+      decl_node->copy_visibility_from (dest_node);
+}
+
+/* Propagate linkage changes to sym aliases.  */
+
+void
+update_sym_alias_interface (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.  */
 
@@ -3009,6 +3056,8 @@ determine_visibility (tree decl)
        translation unit, we can make the type internal.  */
     constrain_visibility (decl, VISIBILITY_ANON, false);
 
+  update_sym_alias_interface (decl);
+
   /* If visibility changed and DECL already has DECL_RTL, ensure
      symbol flags are updated.  */
   if ((DECL_VISIBILITY (decl) != orig_visibility
@@ -3271,6 +3320,8 @@ tentative_decl_linkage (tree decl)
       else if (VAR_P (decl))
 	maybe_commonize_var (decl);
     }
+
+  update_sym_alias_interface (decl);
 }
 
 /* DECL is a FUNCTION_DECL or VAR_DECL.  If the object file linkage
@@ -3505,6 +3556,8 @@ import_export_decl (tree decl)
     }
 
   DECL_INTERFACE_KNOWN (decl) = 1;
+
+  update_sym_alias_interface (decl);
 }
 
 /* Return an expression that performs the destruction of DECL, which
diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc
index 76f1d44610a..3a19a571256 100644
--- a/gcc/cp/name-lookup.cc
+++ b/gcc/cp/name-lookup.cc
@@ -22,9 +22,11 @@ along with GCC; see the file COPYING3.  If not see
 #define INCLUDE_MEMORY
 #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"
@@ -3675,6 +3677,15 @@ push_local_extern_decl_alias (tree decl)
 	  /* Adjust visibility.  */
 	  determine_visibility (alias);
 	}
+      else if (DECL_P (alias))
+	DECL_ATTRIBUTES (alias)
+	  = targetm.merge_decl_attributes (alias, decl);
+      if (DECL_P (alias))
+	{
+	  symtab_node::remap_sym_alias_target (decl, alias);
+	  DECL_ATTRIBUTES (decl)
+	    = remove_attribute ("sym_alias", DECL_ATTRIBUTES (alias));
+	}
     }
 
   retrofit_lang_decl (decl);
diff --git a/gcc/cp/optimize.cc b/gcc/cp/optimize.cc
index 9e8926e4cc6..4a2e8cb4354 100644
--- a/gcc/cp/optimize.cc
+++ b/gcc/cp/optimize.cc
@@ -528,9 +528,12 @@ maybe_clone_body (tree fn)
       DECL_VISIBILITY_SPECIFIED (clone) = DECL_VISIBILITY_SPECIFIED (fn);
       DECL_DLLIMPORT_P (clone) = DECL_DLLIMPORT_P (fn);
       DECL_ATTRIBUTES (clone) = clone_attrs (DECL_ATTRIBUTES (fn));
+      adjust_clone_attributes (fn, clone, DECL_NAME (clone), true);
       DECL_DISREGARD_INLINE_LIMITS (clone) = DECL_DISREGARD_INLINE_LIMITS (fn);
       set_decl_section_name (clone, fn);
 
+      update_sym_alias_interface (clone);
+
       /* Adjust the parameter names and locations.  */
       parm = DECL_ARGUMENTS (fn);
       clone_parm = DECL_ARGUMENTS (clone);
diff --git a/gcc/cp/rtti.cc b/gcc/cp/rtti.cc
index 7878929c246..4bb7b8c8c6e 100644
--- a/gcc/cp/rtti.cc
+++ b/gcc/cp/rtti.cc
@@ -28,8 +28,10 @@ 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"
+#include "cgraph.h"
 
 /* C++ returns type information to the user in struct type_info
    objects. We also use type information to implement dynamic_cast and
@@ -479,8 +481,13 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
 	  = build_tree_list (get_identifier ("non overlapping"),
 			     NULL_TREE);
       else
+	/* Share the non overlapping attribute, without assuming it's
+	   the only attribute, but assuming it's the last if it's
+	   present.  There may be sym aliases too, and those are not
+	   to be shared.  */
 	DECL_ATTRIBUTES (d)
-	  = DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]);
+	  = lookup_attribute ("non overlapping",
+			      DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]));
 
       /* Mark the variable as undefined -- but remember that we can
 	 define it later if we need to do so.  */
@@ -492,6 +499,16 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
       if (CLASS_TYPE_P (type))
 	CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type)) = d;
 
+      /* Copy sym alias attributes from the type to the rtti obj decl.  */
+      tree *attrs = &DECL_ATTRIBUTES (d);
+      FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+	{
+	  tree attr = tree_cons (TREE_PURPOSE (sym), TREE_VALUE (sym), *attrs);
+	  *attrs = attr;
+	  attrs = &TREE_CHAIN (attr);
+	}
+      create_sym_alias_decls (d);
+
       /* Add decl to the global array of tinfo decls.  */
       vec_safe_push (unemitted_tinfo_decls, d);
     }
@@ -499,6 +516,58 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
   return d;
 }
 
+/* After modifying the attributes of TYPE, check whether tinfo was
+   already created and, if so, add to it any sym alias attributes
+   that were not already present.  */
+
+void
+update_tinfo_sym_alias (tree type)
+{
+  if (!TYPE_SIZE (type) || !CLASS_TYPE_P (type))
+    return;
+
+  tree d = CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type));
+  if (!d)
+    return;
+
+  bool first = true;
+  symtab_node *node = NULL;
+
+  tree *attrs = &DECL_ATTRIBUTES (d);
+  FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (d_sym, *attrs)
+	if (TREE_VALUE (sym) == TREE_VALUE (d_sym))
+	  {
+	    found = true;
+	    break;
+	  }
+
+      if (found)
+	continue;
+
+      tree attr = tree_cons (TREE_PURPOSE (sym),
+			     TREE_VALUE (sym),
+			     *attrs);
+      *attrs = attr;
+      attrs = &TREE_CHAIN (attr);
+
+      if (first)
+	{
+	  first = false;
+	  node = symtab_node::get (d);
+	}
+
+      if (!node)
+	continue;
+
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+      create_sym_alias_decl (d, id);
+    }
+}
+
 /* Return the type_info object for TYPE.  */
 
 tree
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index af782b3f228..22b7e3ab7bf 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -4142,6 +4142,49 @@ Function Attributes}, @ref{PowerPC Function Attributes},
 @ref{Nios II Function Attributes}, and @ref{S/390 Function Attributes}
 for details.
 
+@cindex @code{sym_alias} function attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} attribute causes @var{name} to be output as an
+alias to the function, with the same linkage and visibility as the
+function, when the function definition is output, provided that the
+function could be an alias target.  For instance,
+
+@smallexample
+void f (uint64_t) __attribute__ ((__sym_alias__ ("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},
+with the same linkage and visibility.  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.
+
+Aliases introduced with this attribute, such as @samp{f_u64} in the
+example above, are assembly symbol names: they do not undergo C++ name
+mangling, and are not made visible in any scope in the source language.
+They can, however, be named as alias targets, as long as their
+definition is output.  Naming a @samp{sym_alias} as an alias target will
+@emph{not} cause a definition to be output if it otherwise wouldn't.
+This may affect inline functions, C++ template instantiations, and other
+synthesized definitions that would have to be (synthesized and) compiled
+in order for the @samp{sym_alias} to be considered as a potential alias
+target.
+
+This attribute requires assembler and object file support for aliases,
+and may not be available on all targets.
+
 @cindex @code{symver} function attribute
 @item symver ("@var{name2}@@@var{nodename}")
 On ELF targets this attribute creates a symbol version.  The @var{name2} part
@@ -8069,6 +8112,10 @@ will be placed in new, unique sections.
 
 This additional functionality requires Binutils version 2.36 or later.
 
+@cindex @code{sym_alias} variable attribute
+@item sym_alias ("@var{name}")
+See @pxref{Common Function Attributes}.
+
 @cindex @code{uninitialized} variable attribute
 @item uninitialized
 This attribute, attached to a variable with automatic storage, means that
@@ -9164,6 +9211,21 @@ is not supported; that is to say, if a given scalar object can be accessed
 through distinct types that assign a different storage order to it, then the
 behavior is undefined.
 
+@cindex @code{sym_alias} type attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} type 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__ ((__sym_alias__ ("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.
+
 @cindex @code{transparent_union} type attribute
 @item transparent_union
 
diff --git a/gcc/symtab.cc b/gcc/symtab.cc
index 0470509a98d..44df52095c1 100644
--- a/gcc/symtab.cc
+++ b/gcc/symtab.cc
@@ -187,6 +187,22 @@ symbol_table::insert_to_assembler_name_hash (symtab_node *node,
 	(*aslot)->previous_sharing_asm_name = node;
       *aslot = node;
 
+      /* Check for sym_alias name clashes.  In create_sym_alias_decl,
+	 we check for a preexisting definition using the same
+	 assembler_name, so here we check for a previously-defined
+	 sym_name, marked with a "sym name" pseudo-attribute that
+	 points back at the declaration for which it was created.  */
+      if (symtab_node *sym_node = node->next_sharing_asm_name)
+	if (lookup_attribute ("sym alias",
+			      DECL_ATTRIBUTES (sym_node->decl)))
+	  {
+	    error_at (DECL_SOURCE_LOCATION (node->decl),
+		      "duplicate symbol name %qE", decl);
+	    inform (DECL_SOURCE_LOCATION (sym_node->decl),
+		    "already used by %qD in a %qE attribute",
+		    sym_node->decl, get_identifier ("sym_alias"));
+	  }
+
       /* Update also possible inline clones sharing a decl.  */
       cnode = dyn_cast <cgraph_node *> (node);
       if (cnode && cnode->clones && with_clones)
@@ -1943,6 +1959,46 @@ symtab_node::noninterposable_alias (symtab_node *node, void *data)
   return false;
 }
 
+/* Remap sym alias nodes recorded as aliasing REPLACED to alias
+   REPLACEMENT instead.  */
+
+void
+symtab_node::remap_sym_alias_target (tree replaced, tree replacement)
+{
+  symtab_node *repl_node = NULL;
+  if (!decl_in_symtab_p (replacement)
+      || !(repl_node = symtab_node::get (replacement)))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (replaced))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      symtab_node *sym_node = symtab_node::get_for_asmname (id);
+
+      if (!sym_node)
+	{
+	  create_sym_alias_decl (replacement, id);
+	  continue;
+	}
+
+      gcc_assert (!sym_node->analyzed);
+      if (sym_node->alias_target != replaced)
+	continue;
+
+      // sym_node->definition = 0;
+
+      if (VAR_P (replaced))
+	// varpool_node::create_extra_name_alias (sym_node->decl, replacement);
+	varpool_node::create_alias (sym_node->decl, replacement);
+      else
+	cgraph_node::create_same_body_alias (sym_node->decl, replacement);
+	// cgraph_node::create_alias (sym_node->decl, replacement);
+      sym_node->copy_visibility_from (repl_node);
+    }
+}
+
 /* 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.  */
@@ -1970,6 +2026,12 @@ symtab_node::noninterposable_alias (void)
 
   /* Otherwise create a new one.  */
   new_decl = copy_node (node->decl);
+  DECL_ATTRIBUTES (new_decl) = remove_attribute ("sym_alias",
+						   DECL_ATTRIBUTES
+						   (new_decl));
+  DECL_ATTRIBUTES (new_decl) = remove_attribute ("sym alias",
+						   DECL_ATTRIBUTES
+						   (new_decl));
   DECL_DLLIMPORT_P (new_decl) = 0;
   tree name = clone_function_name (node->decl, "localalias");
   if (!flag_wpa)
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-1.c b/gcc/testsuite/c-c++-common/goacc/declare-1.c
index 46ee01b6759..f2842893318 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-1.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-1.c
@@ -113,11 +113,11 @@ f_2 (void)
   int va3;
 #pragma acc declare device_resident(va3)
 
-#ifndef __cplusplus
+#if 0
   /* TODO PR90868
 
-     C: "error: variable '[...]' used more than once with '#pragma acc declare'".  */
-#else
+     "error: variable '[...]' used more than once with '#pragma acc declare'".  */
+
   extern int ve0;
 #pragma acc declare create(ve0)
 
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-2.c b/gcc/testsuite/c-c++-common/goacc/declare-2.c
index e2e22be57e9..aec59b69754 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-2.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-2.c
@@ -137,25 +137,25 @@ void
 f_pr90868_2 (void)
 {
   extern int we0;
-#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" } */
 
   extern int we1;
-#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" } */
 
   extern int *we2;
-#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" } */
 
   extern int we3;
-#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" } */
 
   extern int we4;
-#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" } */
 
   extern int we5;
-#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" } */
  
   extern int we6;
-#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" } */
 }
 
 
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
new file mode 100644
index 00000000000..2fe1948aed9
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
@@ -0,0 +1,75 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+extern int var_alias __attribute__ ((__alias__ ("FOOVAR_A")));
+int var_a = 1;
+
+void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+
+void foo_alias () __attribute__ ((__alias__ ("FOOBAR_A")));
+
+void
+foo_a ()
+{
+}
+
+
+int var_b;
+extern int var_b __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+
+void
+foo_b ()
+{
+}
+
+void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+
+
+int var_c __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+
+void __attribute__ ((__sym_alias__ ("FOOBAR_C")))
+foo_c ()
+{
+}
+
+
+void baf () {}
+
+void foo_x () {
+  extern void baf () __attribute__ ((__sym_alias__ ("FOO_BAF")));
+  extern void bad () /* ok, but not defined, so no alias issued.  */
+    __attribute__ ((__sym_alias__ ("FOO_BAD")));
+  extern void bar () __attribute__ ((__sym_alias__ ("FOO_BAR")));
+
+  static int x __attribute__ ((sym_alias ("FOO_x")));
+}
+
+void bar () {}
+
+extern int xr __attribute__ ((alias ("FOO_x")));
+
+
+ /* The *u* symbols are not defined, so the sym_aliases do not clash.  ??? Is
+    this just an implementation artifact, or a feature someone might actually
+    rely on?  */
+extern int var_u1 __attribute__ ((__sym_alias__ ("VAR_U")));
+extern int var_u2 __attribute__ ((__sym_alias__ ("VAR_U")));
+
+void foo_u1 () __attribute__ ((__sym_alias__ ("FOOBAR_U")));
+void foo_u2 () __attribute__ ((__sym_alias__ ("FOOBAR_U")));
+
+
+/* { dg-final { scan-assembler "FOOBAR_A" } } */
+/* { dg-final { scan-assembler "foo_alias" } } */
+/* { dg-final { scan-assembler "FOOVAR_A" } } */
+/* { dg-final { scan-assembler "var_alias" } } */
+/* { dg-final { scan-assembler "FOOBAR_B" } } */
+/* { dg-final { scan-assembler "FOOVAR_B" } } */
+/* { dg-final { scan-assembler "FOOBAR_C" } } */
+/* { dg-final { scan-assembler "FOOVAR_C" } } */
+/* { dg-final { scan-assembler "FOO_BAF" } } */
+/* { dg-final { scan-assembler "FOO_BAR" } } */
+/* { dg-final { scan-assembler-not "FOO_BAD" } } */
+/* { dg-final { scan-assembler-not "VAR_U" } } */
+/* { dg-final { scan-assembler-not "FOOBAR_U" } } */
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
new file mode 100644
index 00000000000..f692a552606
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
@@ -0,0 +1,99 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+struct s
+{
+  int mem __attribute__ ((__sym_alias__ ("S_MEM"))); /* { dg-warning "attribute ignored" } */
+};
+
+void foo () {
+  extern void bar () __attribute__ ((__sym_alias__ ("FOO_BAR"))); /* Ok.  */
+  int i __attribute__ ((sym_alias ("FOO_i"))); /* { dg-warning "ignored" } */
+  static int x __attribute__ ((sym_alias ("FOO_x")));
+  static int sx __attribute__ ((alias ("FOO_x"))); /* { dg-warning "ignored" } */
+  extern int xx __attribute__ ((alias ("FOO_x"))); /* { dg-warning "ignored" } */
+  extern int y __attribute__ ((sym_alias ("FOO_y"))); /* Ok.  */
+}
+
+int dr __attribute__ ((alias ("FOO_x"))); /* { dg-error "defined both" } */
+
+
+int j __attribute__ ((sym_alias ("J_var")));
+void __attribute__ ((alias ("J_var")))
+j_fn (); /* { dg-error "between function and variable" } */
+
+
+void __attribute__ ((sym_alias ("K_fn")))
+k () {
+}
+extern int __attribute__ ((alias ("K_fn")))
+k_var; /* { dg-error "between function and variable" } */
+
+
+int l __attribute__ ((sym_alias ("L_fn")));
+
+/* These should be detected and reported, not because the names clash at the
+   source level, but because the asm symbols do.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+void
+L_fn () /* { dg-error "duplicate" "" } */
+{
+}
+
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __attribute__ ((sym_alias ("M_var")))
+m ()
+{
+}
+
+int M_var; /* { dg-error "duplicate" "" } */
+
+
+void __attribute__ ((sym_alias ("N_sym")))
+n_fn ()
+{
+}
+
+int __attribute__ ((sym_alias ("N_sym")))
+n_var; /* { dg-error "duplicate" } */
+
+
+int __attribute__ ((sym_alias ("O_sym")))
+o_var;
+
+void __attribute__ ((sym_alias ("O_sym")))
+o_fn () /* { dg-error "duplicate" } */
+{
+}
+
+int __attribute__ ((sym_alias ("P_sym")))
+p_var1;
+
+int __attribute__ ((sym_alias ("P_sym")))
+p_var2; /* { dg-error "duplicate" } */
+
+void __attribute__ ((sym_alias ("Q_sym")))
+q_fn1 ()
+{
+}
+
+void __attribute__ ((sym_alias ("Q_sym")))
+q_fn2 () /* { dg-error "duplicate" } */
+{
+}
+
+int __attribute__ ((sym_alias ("R_var")))
+R_var; /* { dg-error "duplicate" } */
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __attribute__ ((sym_alias ("S_fn")))
+S_fn () /* { dg-error "duplicate" } */
+{
+}
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
new file mode 100644
index 00000000000..52f8fb76496
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
@@ -0,0 +1,34 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int var_a = 1;
+
+void
+foo_a ()
+{
+  extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+}
+
+void
+foo_b ()
+{
+  extern int var_b __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+}
+
+int var_b;
+
+void __attribute__ ((__sym_alias__ ("FOOBAR_C")))
+foo_c ()
+{
+  void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+  /* Another sym for var_b.  */
+  extern int var_b __attribute__ ((__sym_alias__ ("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-sym-alias-4.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
new file mode 100644
index 00000000000..2beafa46379
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-require-alias "" } */
+
+int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A"))) = 42;
+
+int __attribute__ ((__sym_alias__ ("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/c-c++-common/torture/attr-sym-alias-5.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
new file mode 100644
index 00000000000..bee90136daf
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
@@ -0,0 +1,54 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+/* { dg-require-visibility "" } */
+
+int __attribute__ ((sym_alias ("A_hid"), visibility ("hidden"))) a;
+
+extern int __attribute__ ((sym_alias ("B_prt"))) b;
+
+int b __attribute__ ((visibility ("protected")));
+
+extern int __attribute__ ((sym_alias ("C_ntr"))) c;
+
+extern int c __attribute__ ((visibility ("internal")));
+
+int c;
+
+static int d __attribute__ ((sym_alias ("D_stt")));
+
+
+extern void __attribute__ ((sym_alias ("F_hid"), visibility ("hidden")))
+f ();
+
+void
+f () {
+}
+
+extern void __attribute__ ((sym_alias ("G_prt")))
+g ();
+
+void __attribute__ ((visibility ("protected")))
+g () {
+}
+
+extern void __attribute__ ((sym_alias ("H_ntr")))
+h ();
+
+void __attribute__ ((visibility ("internal")))
+h ();
+
+void h () {
+}
+
+static void __attribute__ ((sym_alias ("I_stt")))
+i () {
+}
+
+/* { dg-final { scan-assembler {hidden[^\n]*A_hid} } } */
+/* { dg-final { scan-assembler {protected[^\n]*B_prt} } } */
+/* { dg-final { scan-assembler {internal[^\n]*C_ntr} } } */
+/* { dg-final { scan-assembler-not {glob[^\n]*D_stt} } } */
+/* { dg-final { scan-assembler {hidden[^\n]*F_hid} } } */
+/* { dg-final { scan-assembler {protected[^\n]*G_prt} } } */
+/* { dg-final { scan-assembler {internal[^\n]*H_ntr} } } */
+/* { dg-final { scan-assembler-not {glob[^\n]*I_stt} } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
new file mode 100644
index 00000000000..51729994913
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
@@ -0,0 +1,89 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+class __attribute__ ((__sym_alias__ ("FOOCLS_A"),
+		      __sym_alias__ ("FOOCLS_A_Dupe"))) foo {
+  static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  void __attribute__ ((__sym_alias__ ("FOOBAR_A"))) bar ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+int foo::var = 1;
+
+foo::foo () {}
+
+void foo::bar () {}
+
+namespace b {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_B"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_B"))) foo ();
+    void __attribute__ ((__sym_alias__ ("FOOBAR_B"))) bar () {}
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_B"))) ~foo() {}
+  };
+
+  int foo::var = 2;
+
+  foo::foo () {
+    void (foo::*pbar)() = &foo::bar;
+  }
+}
+
+namespace c {
+  namespace cd {
+    class __attribute__ ((__sym_alias__ ("FOOCLS_C"))) foo {
+      static int var __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+      __attribute__ ((__sym_alias__ ("FOOCTR_C"))) foo () {
+	void (foo::*pbar)() = &foo::bar;
+      }
+      void __attribute__ ((__sym_alias__ ("FOOBAR_C"))) bar () {}
+      virtual __attribute__ ((__sym_alias__ ("FOODTR_C"))) ~foo() {}
+    };
+
+    int foo::var = 3;
+  }
+}
+
+#include <typeinfo>
+
+namespace d {
+  namespace {
+    class __attribute__ ((__sym_alias__ ("FOOCLS_D"))) foo {
+      virtual ~foo() {} // typeid(foo) is not defined otherwise.
+    };
+  }
+
+  extern std::type_info __attribute__ ((__alias__ ("FOOCLS_D"))) foo_d_typeinfo;
+}
+
+extern std::type_info __attribute__ ((__alias__ ("FOOCLS_C"))) foo_c_typeinfo;
+
+/* { dg-final { scan-assembler "FOOCLS_A" } } */
+/* { dg-final { scan-assembler "FOOCLS_A_Dupe" } } */
+/* { 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" } } */
+/* { dg-final { scan-assembler "FOOCLS_D" } } */
+/* { dg-final { scan-assembler "foo_d_typeinfo" } } */
+/* { dg-final { scan-assembler "foo_c_typeinfo" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
new file mode 100644
index 00000000000..6facfcc2cd9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+namespace {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_A"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo ();
+    void __attribute__ ((__sym_alias__ ("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-sym-alias-3.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
new file mode 100644
index 00000000000..42c7288ad4a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
@@ -0,0 +1,83 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+// sym can be applied to template function explicit instantiations.
+
+template <typename T>
+void
+fn(T) {
+};
+
+template void __attribute__ ((__sym_alias__ ("FOOFUN_UINT"))) fn<>(unsigned int);
+template void __attribute__ ((__sym_alias__ ("FOOFUN_LONG"))) fn<>(long);
+
+template<> void __attribute__ ((__sym_alias__ ("FOOFUN_CHAR"))) fn<>(char) {}
+
+
+template <typename T = void>
+struct
+foo {
+  virtual ~foo() {}
+
+  virtual void virtfun() {}
+
+  static void stfun() {}
+  void inlfun() {}
+};
+
+// Explicitly instantiate members before the enclosing class.
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_CHAR_VIRT"))) foo<char>::virtfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_CHAR_TI"))) foo<char>;
+
+// Though they're only output if the enclosing class is.
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_LONG_VIRT"))) foo<long>::virtfun();
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_LONG_TI_X"))) foo<long>;
+
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_VOID_ST"))) foo<void>::stfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_VOID_TI"))) foo<>;
+
+
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_X"))) foo<short>;
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_ST"))) foo<short>::stfun();
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_INL"))) foo<short>::inlfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_D"))) foo<short>;
+
+// Explicit specializations work too.
+
+template <>
+struct  __attribute__ ((__sym_alias__ ("FOOCLS_INT_TI")))
+foo<int>
+{
+  virtual ~foo() {}
+  virtual void __attribute__ ((__sym_alias__ ("FOOCLS_INT_VIRT"))) virtfun() {}
+};
+
+/* { dg-final { scan-assembler "FOOFUN_UINT" } } */
+/* { dg-final { scan-assembler "FOOFUN_LONG" } } */
+/* { dg-final { scan-assembler "FOOFUN_CHAR" } } */
+
+/* { dg-final { scan-assembler "FOOCLS_VOID_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_VOID_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_X" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_INL" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_D" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_TI_X" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_VIRT" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
new file mode 100644
index 00000000000..59b779de35d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+template <typename T = void>
+class
+__attribute__ ((__sym_alias__ ("FOOCLS")))
+foo // { dg-error "duplicate|already" }
+{
+  virtual ~foo() {}
+
+  template <typename U>
+  void
+    __attribute__ ((__sym_alias__ ("FOOTMF")))
+    tmemfun () {} // { dg-error "duplicate|already" }
+};
+
+template <typename T>
+void
+__attribute__ ((__sym_alias__ ("FOOTFN")))
+fn(T) { // { dg-error "duplicate|already" }
+};
+
+template class foo<>;
+template class foo<int>;
+template void foo<>::tmemfun<void>();
+template void foo<int>::tmemfun<void>();
+template void fn<>(int);
+template void fn<>(long);
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
new file mode 100644
index 00000000000..45d59b953c3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
@@ -0,0 +1,14 @@
+/* { dg-do compile { target c++11 } } */
+/* { dg-require-alias "" } */
+
+struct foo {
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+foo::foo () {}
+
+// Make sure the inherited cdtors don't duplicate the syms.
+struct bar : foo {
+  using foo::foo;
+};
diff --git a/gcc/varpool.cc b/gcc/varpool.cc
index e7b51b15e4a..33649906d1d 100644
--- a/gcc/varpool.cc
+++ b/gcc/varpool.cc
@@ -163,6 +163,9 @@ varpool_node::get_create (tree decl)
     }
 
   node->register_symbol ();
+
+  create_sym_alias_decls (decl);
+
   return node;
 }

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [gcc(refs/users/aoliva/heads/testme)] Introduce attribute sym_alias
@ 2023-12-06 20:01 Alexandre Oliva
  0 siblings, 0 replies; 33+ messages in thread
From: Alexandre Oliva @ 2023-12-06 20:01 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:8e6baaf9fa23b35057ea4d2adfe63047aae3de42

commit 8e6baaf9fa23b35057ea4d2adfe63047aae3de42
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Fri Dec 1 14:31:46 2023 -0300

    Introduce attribute sym_alias
    
    This patch introduces an attribute to add extra asm names (aliases)
    for a decl 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, and
    also to local static variables.  In C++, it can also be attached to
    class types, namespace-scoped variables and functions, static data
    members, member functions, explicit instantiations and specializations
    of template functions, members and classes.
    
    When applied to constructors or destructor, additional sym aliases
    with _Base and _Del suffixes are defined for variants other than
    complete-object ones.  This changes the assumption that clones always
    carry the same attributes as their abstract declarations, so there is
    now a function to adjust them.
    
    C++ also had a bug in which attributes from local extern declarations
    failed to be propagated to a preexisting corresponding
    namespace-scoped decl.  I've fixed that, and adjusted acc tests that
    distinguished between C and C++ in this regard.
    
    Applying the attribute to class types is only valid in C++, and the
    effect is to attach the alias to the RTTI object associated with the
    class type.
    
    
    for  gcc/ChangeLog
    
            * attribs.cc: Include cgraph.h.
            (decl_attributes): Allow late introduction of sym_alias in
            types.
            (create_sym_alias_decl, create_sym_alias_decls): New.
            * attribs.h: Declare them.
            (FOR_EACH_SYM_ALIAS): New macro.
            * cgraph.cc (cgraph_node::create): Create sym_alias decls.
            * varpool.cc (varpool_node::get_create): Create sym_alias
            decls.
            * cgraph.h (symtab_node::remap_sym_alias_target): New.
            * symtab.cc (symtab_node::remap_sym_alias_target): Define.
            (symbol_table::insert_to_assembler_name_hash): Check for
            symbol name clashes.
            (symtab_node::noninterposable_alias): Drop sym_alias
            attributes.
            * cgraphunit.cc (cgraph_node::analyze): Create alias_target
            node if needed.
            (analyze_functions): Fixup visibility of implicit alias only
            after its node is analyzed.
            * doc/extend.texi (sym_alias): Document for variables,
            functions and types.
    
    for  gcc/ada/ChangeLog
    
            * doc/gnat_rm/interfacing_to_other_languages.rst: Mention
            attribute sym_alias to give RTTI symbols mnemonic names.
            * doc/gnat_ugn/the_gnat_compilation_model.rst: Mention
            aliases.  Fix incorrect ref to C1 ctor variant.
    
    for  gcc/c-family/ChangeLog
    
            * c-ada-spec.cc (pp_asm_name): Use first sym_alias if
            available.
            * c-attribs.cc (handle_sym_alias_attribute): New.
            (c_common_attribute_table): Add sym_alias.
            (handle_copy_attribute): Do not copy sym_alias attribute.
    
    for  gcc/c/ChangeLog
    
            * c-decl.cc (duplicate_decls): Remap sym_alias target.
            (finish_decl): Create varpool_node for local static
            variables.
    
    for  gcc/cp/ChangeLog
    
            * class.cc (adjust_clone_attributes): New.
            (copy_fndecl_with_name, build_clone): Call it.
            * cp-tree.h (adjust_clone_attributes): Declare.
            (update_sym_alias_interface): Declare.
            (update_tinfo_sym_alias): Declare.
            * decl.cc (duplicate_decls): Remap sym_alias target.
            Adjust clone attributes.
            (grokfndecl): Tentatively create sym_alias decls after
            adding attributes in e.g. a template member function explicit
            instantiation.
            * decl2.cc (cplus_decl_attributes): Update tinfo sym_alias.
            (copy_interface, update_sym_alias_interface): New.
            (determine_visibility): Update sym_alias interface.
            (tentative_decl_linkage, import_export_decl): Likewise.
            * name-lookup.cc: Include target.h and cgraph.h.
            (push_local_extern_decl_alias): Merge attributes with
            namespace-scoped decl, and drop duplicate sym_alias.
            * optimize.cc (maybe_clone_body): Re-adjust attributes after
            cloning them.  Update sym_alias interface.
            * rtti.cc: Include attribs.h and cgraph.h.
            (get_tinfo_decl): Copy sym_alias attributes from type to tinfo
            decl.  Create sym_alias decls.
            (update_tinfo_sym_alias): New.
    
    for  gcc/testsuite/ChangeLog
    
            * c-c++-common/goacc/declare-1.c: Adjust.
            * c-c++-common/goacc/declare-2.c: Adjust.
            * c-c++-common/torture/attr-sym-alias-1.c: New.
            * c-c++-common/torture/attr-sym-alias-2.c: New.
            * c-c++-common/torture/attr-sym-alias-3.c: New.
            * c-c++-common/torture/attr-sym-alias-4.c: New.
            * c-c++-common/torture/attr-sym-alias-5.c: New.
            * g++.dg/torture/attr-sym-alias-1.C: New.
            * g++.dg/torture/attr-sym-alias-2.C: New.
            * g++.dg/torture/attr-sym-alias-3.C: New.
            * g++.dg/torture/attr-sym-alias-4.C: New.
            * g++.dg/torture/attr-sym-alias-5.C: New.

Diff:
---
 .../doc/gnat_rm/interfacing_to_other_languages.rst |  6 ++
 .../doc/gnat_ugn/the_gnat_compilation_model.rst    | 10 ++-
 gcc/attribs.cc                                     | 74 +++++++++++++++-
 gcc/attribs.h                                      |  7 ++
 gcc/c-family/c-ada-spec.cc                         |  7 ++
 gcc/c-family/c-attribs.cc                          | 33 +++++++-
 gcc/c/c-decl.cc                                    |  7 ++
 gcc/cgraph.cc                                      |  2 +
 gcc/cgraph.h                                       |  4 +
 gcc/cgraphunit.cc                                  |  2 +-
 gcc/cp/class.cc                                    | 64 ++++++++++++++
 gcc/cp/cp-tree.h                                   |  4 +
 gcc/cp/decl.cc                                     |  4 +
 gcc/cp/decl2.cc                                    | 53 ++++++++++++
 gcc/cp/name-lookup.cc                              | 11 +++
 gcc/cp/optimize.cc                                 |  3 +
 gcc/cp/rtti.cc                                     | 71 +++++++++++++++-
 gcc/doc/extend.texi                                | 62 ++++++++++++++
 gcc/symtab.cc                                      | 62 ++++++++++++++
 gcc/testsuite/c-c++-common/goacc/declare-1.c       |  6 +-
 gcc/testsuite/c-c++-common/goacc/declare-2.c       | 14 +--
 .../c-c++-common/torture/attr-sym-alias-1.c        | 75 ++++++++++++++++
 .../c-c++-common/torture/attr-sym-alias-2.c        | 99 ++++++++++++++++++++++
 .../c-c++-common/torture/attr-sym-alias-3.c        | 34 ++++++++
 .../c-c++-common/torture/attr-sym-alias-4.c        | 28 ++++++
 .../c-c++-common/torture/attr-sym-alias-5.c        | 54 ++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C    | 89 +++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C    | 26 ++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C    | 83 ++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C    | 28 ++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C    | 14 +++
 gcc/varpool.cc                                     |  3 +
 32 files changed, 1020 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 ad0be511d48..b8de16ebf7c 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 ``sym_alias`` 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 fd15459203a..20045698962 100644
--- a/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
+++ b/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
@@ -4274,6 +4274,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__ ((__sym_alias__ ("Ctor_For_Animal")))
         Animal() {Age_Count = 0;};
       private:
         int Age_Count;
@@ -4306,6 +4307,7 @@ both Carnivore and Domestic, that is:
         virtual int  Number_Of_Teeth ();
         virtual void Set_Owner (char* Name);
 
+        __attribute__ ((__sym_alias__ ("Ctor_For_Dog"))) // mnemonic alias
         Dog(); // Constructor
       private:
         int  Tooth_Count;
@@ -4344,7 +4346,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;
@@ -4360,7 +4363,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,7 +4385,8 @@ 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.
+mangled name (or an alias) 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.cc b/gcc/attribs.cc
index ff4b638c25c..abfbbbf6294 100644
--- a/gcc/attribs.cc
+++ b/gcc/attribs.cc
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "target.h"
 #include "tree.h"
+#include "cgraph.h"
 #include "stringpool.h"
 #include "diagnostic-core.h"
 #include "attribs.h"
@@ -830,7 +831,8 @@ decl_attributes (tree *node, tree attributes, int flags,
 
       if (TYPE_P (*anode)
 	  && (flags & (int) ATTR_FLAG_TYPE_IN_PLACE)
-	  && COMPLETE_TYPE_P (*anode))
+	  && COMPLETE_TYPE_P (*anode)
+	  && !is_attribute_p ("sym_alias", name))
 	{
 	  warning (OPT_Wattributes, "type attributes ignored after type is already defined");
 	  continue;
@@ -2672,6 +2674,76 @@ attr_access::array_as_string (tree type) const
   return typstr;
 }
 
+/* Create a sym attribute for DECL to be visible with linkage name ID.  */
+
+tree
+create_sym_alias_decl (tree decl, tree id)
+{
+  const char *attr_str = "sym_alias";
+
+  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;
+
+      tree attr_name = get_identifier (attr_str);
+      error_at (DECL_SOURCE_LOCATION (decl),
+		"duplicate symbol name %qE in %qE attribute of %qD",
+		id, attr_name, decl);
+      inform (DECL_SOURCE_LOCATION (sym_node->decl),
+	      "already used by %qD", sym_node->decl);
+    }
+
+  tree clone = copy_node (decl);
+  DECL_ATTRIBUTES (clone) = remove_attribute (attr_str,
+					      DECL_ATTRIBUTES (decl));
+  /* Mark it as a "sym alias" for decl, an internal attribute that
+     enables symbol_table::insert_to_assembler_name_hash can recognize
+     sym_alias decls.  */
+  DECL_ATTRIBUTES (clone) = tree_cons (get_identifier ("sym alias"),
+				       decl, DECL_ATTRIBUTES (clone));
+  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;
+
+  symtab_node *node;
+  if (VAR_P (clone))
+    // DECL_READ_P (clone) = 1;
+    // node = varpool_node::create_extra_name_alias (clone, decl);
+    node = varpool_node::create_alias (clone, decl);
+  else
+    node = cgraph_node::create_same_body_alias (clone, decl);
+    // node = cgraph_node::create_alias (clone, decl);
+  if (symtab_node *dnode = symtab_node::get_create (decl))
+    node->copy_visibility_from (dnode);
+
+  return clone;
+}
+
+/* Create decls for all sym aliases requested in DECL's attributes.  */
+
+void
+create_sym_alias_decls (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl)
+      || DECL_ABSTRACT_P (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      create_sym_alias_decl (decl, id);
+    }
+}
+
 #if CHECKING_P
 
 namespace selftest
diff --git a/gcc/attribs.h b/gcc/attribs.h
index fdeebff1cd9..b7a67f73550 100644
--- a/gcc/attribs.h
+++ b/gcc/attribs.h
@@ -404,4 +404,11 @@ extern void init_attr_rdwr_indices (rdwr_map *, tree);
 extern attr_access *get_parm_access (rdwr_map &, tree,
 				     tree = current_function_decl);
 
+extern tree create_sym_alias_decl (tree, tree);
+extern void create_sym_alias_decls (tree);
+
+#define FOR_EACH_SYM_ALIAS(sym, attrs)					\
+  for (tree sym = lookup_attribute ("sym_alias", (attrs));		\
+       sym; sym = lookup_attribute ("sym_alias", TREE_CHAIN (sym)))
+
 #endif // GCC_ATTRIBS_H
diff --git a/gcc/c-family/c-ada-spec.cc b/gcc/c-family/c-ada-spec.cc
index 050994d8416..5042b9cfecd 100644
--- a/gcc/c-family/c-ada-spec.cc
+++ b/gcc/c-family/c-ada-spec.cc
@@ -1431,6 +1431,13 @@ pp_ada_tree_identifier (pretty_printer *buffer, tree node, tree type,
 static void
 pp_asm_name (pretty_printer *buffer, tree t)
 {
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (t))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.cc b/gcc/c-family/c-attribs.cc
index 854e987dc79..2593fde1fe9 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -110,7 +110,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_sym_alias_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,
@@ -392,6 +393,8 @@ const struct attribute_spec c_common_gnu_attributes[] =
 			      handle_alias_attribute, NULL },
   { "weakref",                0, 1, true,  false, false, false,
 			      handle_weakref_attribute, NULL },
+  { "sym_alias",              1, 1, false,  false, false, false,
+			      handle_sym_alias_attribute, NULL },
   { "no_instrument_function", 0, 0, true,  false, false, false,
 			      handle_no_instrument_function_attribute,
 			      NULL },
@@ -3092,7 +3095,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
@@ -3102,7 +3105,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
@@ -3112,6 +3115,29 @@ handle_alias_attribute (tree *node, tree name, tree args,
   return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs);
 }
 
+/* Handle a "sym_alias" attribute; arguments as in struct
+   attribute_spec.handler.  */
+
+static tree
+handle_sym_alias_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 (decl_in_symtab_p (node))
+    *no_add_attrs = false;
+  else if (TYPE_P (node) && c_dialect_cxx ())
+    *no_add_attrs = false;
+  else
+    return error_mark_node;
+
+  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.  */
 
@@ -3245,6 +3271,7 @@ handle_copy_attribute (tree *node, tree name, tree args,
 	      || is_attribute_p ("visibility", atname)
 	      || is_attribute_p ("weak", atname)
 	      || is_attribute_p ("weakref", atname)
+	      || is_attribute_p ("sym_alias", atname)
 	      || is_attribute_p ("target_clones", atname))
 	    continue;
 
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 92c83e1bf10..c1490e627df 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -3117,6 +3117,8 @@ duplicate_decls (tree newdecl, tree olddecl)
 
   merge_decls (newdecl, olddecl, newtype, oldtype);
 
+  symtab_node::remap_sym_alias_target (newdecl, olddecl);
+
   /* The NEWDECL will no longer be needed.
 
      Before releasing the node, be sure to remove function from symbol
@@ -5902,6 +5904,11 @@ finish_decl (tree decl, location_t init_loc, tree init,
 	    set_user_assembler_name (decl, asmspec);
 	}
 
+      /* Give attribute sym_alias a chance to make the symbol name
+	 available for aliasing, even for a static local variable.  */
+      if (VAR_P (decl) && !DECL_EXTERNAL (decl) && is_global_var (decl))
+	varpool_node::get_create (decl);
+
       if (DECL_FILE_SCOPE_P (decl))
 	{
 	  if (DECL_INITIAL (decl) == NULL_TREE
diff --git a/gcc/cgraph.cc b/gcc/cgraph.cc
index f93259a8c70..0bef2179e89 100644
--- a/gcc/cgraph.cc
+++ b/gcc/cgraph.cc
@@ -523,6 +523,8 @@ cgraph_node::create (tree decl)
   node->register_symbol ();
   maybe_record_nested_function (node);
 
+  create_sym_alias_decls (decl);
+
   return node;
 }
 
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index 2b320557616..6d61e327fb6 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -327,6 +327,10 @@ public:
   /* Return DECL that alias is aliasing.  */
   inline tree get_alias_target_tree ();
 
+  /* Remap sym alias nodes recorded as aliasing REPLACED to alias REPLACEMENT
+     instead.  */
+  static void remap_sym_alias_target (tree replaced, tree replacement);
+
   /* Set section for symbol and its aliases.  */
   void set_section (const char *section);
 
diff --git a/gcc/cgraphunit.cc b/gcc/cgraphunit.cc
index 9a550a5cce6..a66db795c78 100644
--- a/gcc/cgraphunit.cc
+++ b/gcc/cgraphunit.cc
@@ -1176,7 +1176,7 @@ analyze_functions (bool first_time)
      C++ FE is confused about the COMDAT groups being right.  */
   if (symtab->cpp_implicit_aliases_done)
     FOR_EACH_SYMBOL (node)
-      if (node->cpp_implicit_alias)
+      if (node->cpp_implicit_alias && node->analyzed)
 	  node->fixup_same_cpp_alias_visibility (node->get_alias_target ());
   build_type_inheritance_graph ();
 
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index 6fdb56abfb9..fa8fc63db82 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -4896,6 +4896,68 @@ check_methods (tree t)
     }
 }
 
+/* Adjust sym alias name for CLONE, cloned from FN and named NAME,
+   if it is a cdtor, and drop the sym alias from other clones.  */
+
+void
+adjust_clone_attributes (tree fn, tree clone, tree name, bool skip_copy_p)
+{
+  if (IDENTIFIER_CDTOR_P (name))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	{
+	  found = true;
+	  break;
+	}
+
+      if (found
+	  && (name == complete_ctor_identifier
+	      || name == complete_dtor_identifier))
+	{
+	  /* Reuse the sym alias decls created for the primary cdtor
+	     decl.  */
+	  symtab_node::remap_sym_alias_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);
+
+	  if (!skip_copy_p)
+	    DECL_ATTRIBUTES (clone) = copy_list (DECL_ATTRIBUTES (clone));
+
+	  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	    {
+	      /* We need to copy this even with skip_copy_p, because
+		 even then copying was shallow.  */
+	      TREE_VALUE (sym) = copy_list (TREE_VALUE (sym));
+	      /* Append suf to the sym alias name.  */
+	      tree str = TREE_VALUE (TREE_VALUE (sym));
+	      char *symname = concat (TREE_STRING_POINTER (str), suf, NULL);
+	      str = build_string (TREE_STRING_LENGTH (str) + xlen, symname);
+	      TREE_VALUE (TREE_VALUE (sym)) = str;
+	      free (symname);
+	    }
+
+	  if (symtab_node::get (clone))
+	    create_sym_alias_decls (clone);
+	}
+    }
+  else
+    DECL_ATTRIBUTES (clone)
+      = remove_attribute ("sym_alias", DECL_ATTRIBUTES (clone));
+}
+
 /* FN is constructor, destructor or operator function.  Clone the
    declaration to create a NAME'd variant.  NEED_VTT_PARM_P and
    OMIT_INHERITED_PARMS_P are relevant if it's a cdtor.  */
@@ -5065,6 +5127,8 @@ build_clone (tree fn, tree name, bool need_vtt_parm_p,
   DECL_CHAIN (clone) = DECL_CHAIN (fn);
   DECL_CHAIN (fn) = clone;
 
+  adjust_clone_attributes (fn, clone, name);
+
   return clone;
 }
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 795152c9ad2..dca2d31a372 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5806,6 +5806,8 @@ struct GTY((for_user)) spec_entry
 
 extern int current_class_depth;
 
+void adjust_clone_attributes (tree fn, tree clone, tree name, bool = false);
+
 /* in decl.cc */
 
 /* An array of static vars & fns.  */
@@ -7043,6 +7045,7 @@ extern void do_push_parm_decls			(tree, tree, tree *);
 extern tree do_aggregate_paren_init		(tree, tree);
 
 /* in decl2.cc */
+extern void update_sym_alias_interface		(tree);
 extern void record_mangling			(tree, bool);
 extern void overwrite_mangling			(tree, tree);
 extern void note_mangling_alias			(tree, tree);
@@ -7617,6 +7620,7 @@ extern bool emit_tinfo_decl			(tree);
 extern unsigned get_pseudo_tinfo_index		(tree);
 extern tree get_pseudo_tinfo_type		(unsigned);
 extern tree build_if_nonnull			(tree, tree, tsubst_flags_t);
+extern void update_tinfo_sym_alias		(tree);
 
 /* in search.cc */
 extern tree get_parent_with_private_access 	(tree decl, tree binfo);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 4b685270097..cc7f7a99487 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -3277,6 +3277,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
 	      && TREE_STATIC (olddecl))))
     make_decl_rtl (olddecl);
 
+  symtab_node::remap_sym_alias_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.
@@ -3300,6 +3302,7 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
       FOR_EACH_CLONE (clone, olddecl)
 	{
 	  DECL_ATTRIBUTES (clone) = DECL_ATTRIBUTES (olddecl);
+	  adjust_clone_attributes (olddecl, clone, DECL_NAME (clone));
 	  DECL_PRESERVE_P (clone) |= DECL_PRESERVE_P (olddecl);
 	}
     }
@@ -10838,6 +10841,7 @@ grokfndecl (tree ctype,
     {
       cplus_decl_attributes (&decl, *attrlist, 0);
       *attrlist = NULL_TREE;
+      create_sym_alias_decls (decl);
     }
 
   if (DECL_HAS_CONTRACTS_P (decl))
diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
index bee84879023..00f19962e5d 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -1846,6 +1846,9 @@ cplus_decl_attributes (tree *decl, tree attributes, int flags)
   if (late_attrs)
     save_template_attributes (late_attrs, decl, flags);
 
+  if (TYPE_P (*decl) && attributes)
+    update_tinfo_sym_alias (*decl);
+
   /* Propagate deprecation out to the template.  */
   if (TREE_DEPRECATED (*decl))
     if (tree ti = get_template_info (*decl))
@@ -2202,6 +2205,50 @@ 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);
+  if (symtab_node *dest_node = symtab_node::get (dest))
+    if (symtab_node *decl_node = symtab_node::get (decl))
+      decl_node->copy_visibility_from (dest_node);
+}
+
+/* Propagate linkage changes to sym aliases.  */
+
+void
+update_sym_alias_interface (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.  */
 
@@ -3009,6 +3056,8 @@ determine_visibility (tree decl)
        translation unit, we can make the type internal.  */
     constrain_visibility (decl, VISIBILITY_ANON, false);
 
+  update_sym_alias_interface (decl);
+
   /* If visibility changed and DECL already has DECL_RTL, ensure
      symbol flags are updated.  */
   if ((DECL_VISIBILITY (decl) != orig_visibility
@@ -3271,6 +3320,8 @@ tentative_decl_linkage (tree decl)
       else if (VAR_P (decl))
 	maybe_commonize_var (decl);
     }
+
+  update_sym_alias_interface (decl);
 }
 
 /* DECL is a FUNCTION_DECL or VAR_DECL.  If the object file linkage
@@ -3505,6 +3556,8 @@ import_export_decl (tree decl)
     }
 
   DECL_INTERFACE_KNOWN (decl) = 1;
+
+  update_sym_alias_interface (decl);
 }
 
 /* Return an expression that performs the destruction of DECL, which
diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc
index 76f1d44610a..3a19a571256 100644
--- a/gcc/cp/name-lookup.cc
+++ b/gcc/cp/name-lookup.cc
@@ -22,9 +22,11 @@ along with GCC; see the file COPYING3.  If not see
 #define INCLUDE_MEMORY
 #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"
@@ -3675,6 +3677,15 @@ push_local_extern_decl_alias (tree decl)
 	  /* Adjust visibility.  */
 	  determine_visibility (alias);
 	}
+      else if (DECL_P (alias))
+	DECL_ATTRIBUTES (alias)
+	  = targetm.merge_decl_attributes (alias, decl);
+      if (DECL_P (alias))
+	{
+	  symtab_node::remap_sym_alias_target (decl, alias);
+	  DECL_ATTRIBUTES (decl)
+	    = remove_attribute ("sym_alias", DECL_ATTRIBUTES (alias));
+	}
     }
 
   retrofit_lang_decl (decl);
diff --git a/gcc/cp/optimize.cc b/gcc/cp/optimize.cc
index 9e8926e4cc6..4a2e8cb4354 100644
--- a/gcc/cp/optimize.cc
+++ b/gcc/cp/optimize.cc
@@ -528,9 +528,12 @@ maybe_clone_body (tree fn)
       DECL_VISIBILITY_SPECIFIED (clone) = DECL_VISIBILITY_SPECIFIED (fn);
       DECL_DLLIMPORT_P (clone) = DECL_DLLIMPORT_P (fn);
       DECL_ATTRIBUTES (clone) = clone_attrs (DECL_ATTRIBUTES (fn));
+      adjust_clone_attributes (fn, clone, DECL_NAME (clone), true);
       DECL_DISREGARD_INLINE_LIMITS (clone) = DECL_DISREGARD_INLINE_LIMITS (fn);
       set_decl_section_name (clone, fn);
 
+      update_sym_alias_interface (clone);
+
       /* Adjust the parameter names and locations.  */
       parm = DECL_ARGUMENTS (fn);
       clone_parm = DECL_ARGUMENTS (clone);
diff --git a/gcc/cp/rtti.cc b/gcc/cp/rtti.cc
index 7878929c246..4bb7b8c8c6e 100644
--- a/gcc/cp/rtti.cc
+++ b/gcc/cp/rtti.cc
@@ -28,8 +28,10 @@ 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"
+#include "cgraph.h"
 
 /* C++ returns type information to the user in struct type_info
    objects. We also use type information to implement dynamic_cast and
@@ -479,8 +481,13 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
 	  = build_tree_list (get_identifier ("non overlapping"),
 			     NULL_TREE);
       else
+	/* Share the non overlapping attribute, without assuming it's
+	   the only attribute, but assuming it's the last if it's
+	   present.  There may be sym aliases too, and those are not
+	   to be shared.  */
 	DECL_ATTRIBUTES (d)
-	  = DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]);
+	  = lookup_attribute ("non overlapping",
+			      DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]));
 
       /* Mark the variable as undefined -- but remember that we can
 	 define it later if we need to do so.  */
@@ -492,6 +499,16 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
       if (CLASS_TYPE_P (type))
 	CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type)) = d;
 
+      /* Copy sym alias attributes from the type to the rtti obj decl.  */
+      tree *attrs = &DECL_ATTRIBUTES (d);
+      FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+	{
+	  tree attr = tree_cons (TREE_PURPOSE (sym), TREE_VALUE (sym), *attrs);
+	  *attrs = attr;
+	  attrs = &TREE_CHAIN (attr);
+	}
+      create_sym_alias_decls (d);
+
       /* Add decl to the global array of tinfo decls.  */
       vec_safe_push (unemitted_tinfo_decls, d);
     }
@@ -499,6 +516,58 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
   return d;
 }
 
+/* After modifying the attributes of TYPE, check whether tinfo was
+   already created and, if so, add to it any sym alias attributes
+   that were not already present.  */
+
+void
+update_tinfo_sym_alias (tree type)
+{
+  if (!TYPE_SIZE (type) || !CLASS_TYPE_P (type))
+    return;
+
+  tree d = CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type));
+  if (!d)
+    return;
+
+  bool first = true;
+  symtab_node *node = NULL;
+
+  tree *attrs = &DECL_ATTRIBUTES (d);
+  FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (d_sym, *attrs)
+	if (TREE_VALUE (sym) == TREE_VALUE (d_sym))
+	  {
+	    found = true;
+	    break;
+	  }
+
+      if (found)
+	continue;
+
+      tree attr = tree_cons (TREE_PURPOSE (sym),
+			     TREE_VALUE (sym),
+			     *attrs);
+      *attrs = attr;
+      attrs = &TREE_CHAIN (attr);
+
+      if (first)
+	{
+	  first = false;
+	  node = symtab_node::get (d);
+	}
+
+      if (!node)
+	continue;
+
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+      create_sym_alias_decl (d, id);
+    }
+}
+
 /* Return the type_info object for TYPE.  */
 
 tree
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index af782b3f228..22b7e3ab7bf 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -4142,6 +4142,49 @@ Function Attributes}, @ref{PowerPC Function Attributes},
 @ref{Nios II Function Attributes}, and @ref{S/390 Function Attributes}
 for details.
 
+@cindex @code{sym_alias} function attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} attribute causes @var{name} to be output as an
+alias to the function, with the same linkage and visibility as the
+function, when the function definition is output, provided that the
+function could be an alias target.  For instance,
+
+@smallexample
+void f (uint64_t) __attribute__ ((__sym_alias__ ("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},
+with the same linkage and visibility.  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.
+
+Aliases introduced with this attribute, such as @samp{f_u64} in the
+example above, are assembly symbol names: they do not undergo C++ name
+mangling, and are not made visible in any scope in the source language.
+They can, however, be named as alias targets, as long as their
+definition is output.  Naming a @samp{sym_alias} as an alias target will
+@emph{not} cause a definition to be output if it otherwise wouldn't.
+This may affect inline functions, C++ template instantiations, and other
+synthesized definitions that would have to be (synthesized and) compiled
+in order for the @samp{sym_alias} to be considered as a potential alias
+target.
+
+This attribute requires assembler and object file support for aliases,
+and may not be available on all targets.
+
 @cindex @code{symver} function attribute
 @item symver ("@var{name2}@@@var{nodename}")
 On ELF targets this attribute creates a symbol version.  The @var{name2} part
@@ -8069,6 +8112,10 @@ will be placed in new, unique sections.
 
 This additional functionality requires Binutils version 2.36 or later.
 
+@cindex @code{sym_alias} variable attribute
+@item sym_alias ("@var{name}")
+See @pxref{Common Function Attributes}.
+
 @cindex @code{uninitialized} variable attribute
 @item uninitialized
 This attribute, attached to a variable with automatic storage, means that
@@ -9164,6 +9211,21 @@ is not supported; that is to say, if a given scalar object can be accessed
 through distinct types that assign a different storage order to it, then the
 behavior is undefined.
 
+@cindex @code{sym_alias} type attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} type 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__ ((__sym_alias__ ("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.
+
 @cindex @code{transparent_union} type attribute
 @item transparent_union
 
diff --git a/gcc/symtab.cc b/gcc/symtab.cc
index 0470509a98d..44df52095c1 100644
--- a/gcc/symtab.cc
+++ b/gcc/symtab.cc
@@ -187,6 +187,22 @@ symbol_table::insert_to_assembler_name_hash (symtab_node *node,
 	(*aslot)->previous_sharing_asm_name = node;
       *aslot = node;
 
+      /* Check for sym_alias name clashes.  In create_sym_alias_decl,
+	 we check for a preexisting definition using the same
+	 assembler_name, so here we check for a previously-defined
+	 sym_name, marked with a "sym name" pseudo-attribute that
+	 points back at the declaration for which it was created.  */
+      if (symtab_node *sym_node = node->next_sharing_asm_name)
+	if (lookup_attribute ("sym alias",
+			      DECL_ATTRIBUTES (sym_node->decl)))
+	  {
+	    error_at (DECL_SOURCE_LOCATION (node->decl),
+		      "duplicate symbol name %qE", decl);
+	    inform (DECL_SOURCE_LOCATION (sym_node->decl),
+		    "already used by %qD in a %qE attribute",
+		    sym_node->decl, get_identifier ("sym_alias"));
+	  }
+
       /* Update also possible inline clones sharing a decl.  */
       cnode = dyn_cast <cgraph_node *> (node);
       if (cnode && cnode->clones && with_clones)
@@ -1943,6 +1959,46 @@ symtab_node::noninterposable_alias (symtab_node *node, void *data)
   return false;
 }
 
+/* Remap sym alias nodes recorded as aliasing REPLACED to alias
+   REPLACEMENT instead.  */
+
+void
+symtab_node::remap_sym_alias_target (tree replaced, tree replacement)
+{
+  symtab_node *repl_node = NULL;
+  if (!decl_in_symtab_p (replacement)
+      || !(repl_node = symtab_node::get (replacement)))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (replaced))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      symtab_node *sym_node = symtab_node::get_for_asmname (id);
+
+      if (!sym_node)
+	{
+	  create_sym_alias_decl (replacement, id);
+	  continue;
+	}
+
+      gcc_assert (!sym_node->analyzed);
+      if (sym_node->alias_target != replaced)
+	continue;
+
+      // sym_node->definition = 0;
+
+      if (VAR_P (replaced))
+	// varpool_node::create_extra_name_alias (sym_node->decl, replacement);
+	varpool_node::create_alias (sym_node->decl, replacement);
+      else
+	cgraph_node::create_same_body_alias (sym_node->decl, replacement);
+	// cgraph_node::create_alias (sym_node->decl, replacement);
+      sym_node->copy_visibility_from (repl_node);
+    }
+}
+
 /* 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.  */
@@ -1970,6 +2026,12 @@ symtab_node::noninterposable_alias (void)
 
   /* Otherwise create a new one.  */
   new_decl = copy_node (node->decl);
+  DECL_ATTRIBUTES (new_decl) = remove_attribute ("sym_alias",
+						   DECL_ATTRIBUTES
+						   (new_decl));
+  DECL_ATTRIBUTES (new_decl) = remove_attribute ("sym alias",
+						   DECL_ATTRIBUTES
+						   (new_decl));
   DECL_DLLIMPORT_P (new_decl) = 0;
   tree name = clone_function_name (node->decl, "localalias");
   if (!flag_wpa)
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-1.c b/gcc/testsuite/c-c++-common/goacc/declare-1.c
index 46ee01b6759..f2842893318 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-1.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-1.c
@@ -113,11 +113,11 @@ f_2 (void)
   int va3;
 #pragma acc declare device_resident(va3)
 
-#ifndef __cplusplus
+#if 0
   /* TODO PR90868
 
-     C: "error: variable '[...]' used more than once with '#pragma acc declare'".  */
-#else
+     "error: variable '[...]' used more than once with '#pragma acc declare'".  */
+
   extern int ve0;
 #pragma acc declare create(ve0)
 
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-2.c b/gcc/testsuite/c-c++-common/goacc/declare-2.c
index e2e22be57e9..aec59b69754 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-2.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-2.c
@@ -137,25 +137,25 @@ void
 f_pr90868_2 (void)
 {
   extern int we0;
-#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" } */
 
   extern int we1;
-#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" } */
 
   extern int *we2;
-#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" } */
 
   extern int we3;
-#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" } */
 
   extern int we4;
-#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" } */
 
   extern int we5;
-#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" } */
  
   extern int we6;
-#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" } */
 }
 
 
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
new file mode 100644
index 00000000000..2fe1948aed9
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
@@ -0,0 +1,75 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+extern int var_alias __attribute__ ((__alias__ ("FOOVAR_A")));
+int var_a = 1;
+
+void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+
+void foo_alias () __attribute__ ((__alias__ ("FOOBAR_A")));
+
+void
+foo_a ()
+{
+}
+
+
+int var_b;
+extern int var_b __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+
+void
+foo_b ()
+{
+}
+
+void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+
+
+int var_c __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+
+void __attribute__ ((__sym_alias__ ("FOOBAR_C")))
+foo_c ()
+{
+}
+
+
+void baf () {}
+
+void foo_x () {
+  extern void baf () __attribute__ ((__sym_alias__ ("FOO_BAF")));
+  extern void bad () /* ok, but not defined, so no alias issued.  */
+    __attribute__ ((__sym_alias__ ("FOO_BAD")));
+  extern void bar () __attribute__ ((__sym_alias__ ("FOO_BAR")));
+
+  static int x __attribute__ ((sym_alias ("FOO_x")));
+}
+
+void bar () {}
+
+extern int xr __attribute__ ((alias ("FOO_x")));
+
+
+ /* The *u* symbols are not defined, so the sym_aliases do not clash.  ??? Is
+    this just an implementation artifact, or a feature someone might actually
+    rely on?  */
+extern int var_u1 __attribute__ ((__sym_alias__ ("VAR_U")));
+extern int var_u2 __attribute__ ((__sym_alias__ ("VAR_U")));
+
+void foo_u1 () __attribute__ ((__sym_alias__ ("FOOBAR_U")));
+void foo_u2 () __attribute__ ((__sym_alias__ ("FOOBAR_U")));
+
+
+/* { dg-final { scan-assembler "FOOBAR_A" } } */
+/* { dg-final { scan-assembler "foo_alias" } } */
+/* { dg-final { scan-assembler "FOOVAR_A" } } */
+/* { dg-final { scan-assembler "var_alias" } } */
+/* { dg-final { scan-assembler "FOOBAR_B" } } */
+/* { dg-final { scan-assembler "FOOVAR_B" } } */
+/* { dg-final { scan-assembler "FOOBAR_C" } } */
+/* { dg-final { scan-assembler "FOOVAR_C" } } */
+/* { dg-final { scan-assembler "FOO_BAF" } } */
+/* { dg-final { scan-assembler "FOO_BAR" } } */
+/* { dg-final { scan-assembler-not "FOO_BAD" } } */
+/* { dg-final { scan-assembler-not "VAR_U" } } */
+/* { dg-final { scan-assembler-not "FOOBAR_U" } } */
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
new file mode 100644
index 00000000000..f692a552606
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
@@ -0,0 +1,99 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+struct s
+{
+  int mem __attribute__ ((__sym_alias__ ("S_MEM"))); /* { dg-warning "attribute ignored" } */
+};
+
+void foo () {
+  extern void bar () __attribute__ ((__sym_alias__ ("FOO_BAR"))); /* Ok.  */
+  int i __attribute__ ((sym_alias ("FOO_i"))); /* { dg-warning "ignored" } */
+  static int x __attribute__ ((sym_alias ("FOO_x")));
+  static int sx __attribute__ ((alias ("FOO_x"))); /* { dg-warning "ignored" } */
+  extern int xx __attribute__ ((alias ("FOO_x"))); /* { dg-warning "ignored" } */
+  extern int y __attribute__ ((sym_alias ("FOO_y"))); /* Ok.  */
+}
+
+int dr __attribute__ ((alias ("FOO_x"))); /* { dg-error "defined both" } */
+
+
+int j __attribute__ ((sym_alias ("J_var")));
+void __attribute__ ((alias ("J_var")))
+j_fn (); /* { dg-error "between function and variable" } */
+
+
+void __attribute__ ((sym_alias ("K_fn")))
+k () {
+}
+extern int __attribute__ ((alias ("K_fn")))
+k_var; /* { dg-error "between function and variable" } */
+
+
+int l __attribute__ ((sym_alias ("L_fn")));
+
+/* These should be detected and reported, not because the names clash at the
+   source level, but because the asm symbols do.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+void
+L_fn () /* { dg-error "duplicate" "" } */
+{
+}
+
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __attribute__ ((sym_alias ("M_var")))
+m ()
+{
+}
+
+int M_var; /* { dg-error "duplicate" "" } */
+
+
+void __attribute__ ((sym_alias ("N_sym")))
+n_fn ()
+{
+}
+
+int __attribute__ ((sym_alias ("N_sym")))
+n_var; /* { dg-error "duplicate" } */
+
+
+int __attribute__ ((sym_alias ("O_sym")))
+o_var;
+
+void __attribute__ ((sym_alias ("O_sym")))
+o_fn () /* { dg-error "duplicate" } */
+{
+}
+
+int __attribute__ ((sym_alias ("P_sym")))
+p_var1;
+
+int __attribute__ ((sym_alias ("P_sym")))
+p_var2; /* { dg-error "duplicate" } */
+
+void __attribute__ ((sym_alias ("Q_sym")))
+q_fn1 ()
+{
+}
+
+void __attribute__ ((sym_alias ("Q_sym")))
+q_fn2 () /* { dg-error "duplicate" } */
+{
+}
+
+int __attribute__ ((sym_alias ("R_var")))
+R_var; /* { dg-error "duplicate" } */
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __attribute__ ((sym_alias ("S_fn")))
+S_fn () /* { dg-error "duplicate" } */
+{
+}
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
new file mode 100644
index 00000000000..52f8fb76496
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
@@ -0,0 +1,34 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int var_a = 1;
+
+void
+foo_a ()
+{
+  extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+}
+
+void
+foo_b ()
+{
+  extern int var_b __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+}
+
+int var_b;
+
+void __attribute__ ((__sym_alias__ ("FOOBAR_C")))
+foo_c ()
+{
+  void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+  /* Another sym for var_b.  */
+  extern int var_b __attribute__ ((__sym_alias__ ("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-sym-alias-4.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
new file mode 100644
index 00000000000..2beafa46379
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-require-alias "" } */
+
+int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A"))) = 42;
+
+int __attribute__ ((__sym_alias__ ("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/c-c++-common/torture/attr-sym-alias-5.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
new file mode 100644
index 00000000000..bee90136daf
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
@@ -0,0 +1,54 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+/* { dg-require-visibility "" } */
+
+int __attribute__ ((sym_alias ("A_hid"), visibility ("hidden"))) a;
+
+extern int __attribute__ ((sym_alias ("B_prt"))) b;
+
+int b __attribute__ ((visibility ("protected")));
+
+extern int __attribute__ ((sym_alias ("C_ntr"))) c;
+
+extern int c __attribute__ ((visibility ("internal")));
+
+int c;
+
+static int d __attribute__ ((sym_alias ("D_stt")));
+
+
+extern void __attribute__ ((sym_alias ("F_hid"), visibility ("hidden")))
+f ();
+
+void
+f () {
+}
+
+extern void __attribute__ ((sym_alias ("G_prt")))
+g ();
+
+void __attribute__ ((visibility ("protected")))
+g () {
+}
+
+extern void __attribute__ ((sym_alias ("H_ntr")))
+h ();
+
+void __attribute__ ((visibility ("internal")))
+h ();
+
+void h () {
+}
+
+static void __attribute__ ((sym_alias ("I_stt")))
+i () {
+}
+
+/* { dg-final { scan-assembler {hidden[^\n]*A_hid} } } */
+/* { dg-final { scan-assembler {protected[^\n]*B_prt} } } */
+/* { dg-final { scan-assembler {internal[^\n]*C_ntr} } } */
+/* { dg-final { scan-assembler-not {glob[^\n]*D_stt} } } */
+/* { dg-final { scan-assembler {hidden[^\n]*F_hid} } } */
+/* { dg-final { scan-assembler {protected[^\n]*G_prt} } } */
+/* { dg-final { scan-assembler {internal[^\n]*H_ntr} } } */
+/* { dg-final { scan-assembler-not {glob[^\n]*I_stt} } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
new file mode 100644
index 00000000000..51729994913
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
@@ -0,0 +1,89 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+class __attribute__ ((__sym_alias__ ("FOOCLS_A"),
+		      __sym_alias__ ("FOOCLS_A_Dupe"))) foo {
+  static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  void __attribute__ ((__sym_alias__ ("FOOBAR_A"))) bar ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+int foo::var = 1;
+
+foo::foo () {}
+
+void foo::bar () {}
+
+namespace b {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_B"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_B"))) foo ();
+    void __attribute__ ((__sym_alias__ ("FOOBAR_B"))) bar () {}
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_B"))) ~foo() {}
+  };
+
+  int foo::var = 2;
+
+  foo::foo () {
+    void (foo::*pbar)() = &foo::bar;
+  }
+}
+
+namespace c {
+  namespace cd {
+    class __attribute__ ((__sym_alias__ ("FOOCLS_C"))) foo {
+      static int var __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+      __attribute__ ((__sym_alias__ ("FOOCTR_C"))) foo () {
+	void (foo::*pbar)() = &foo::bar;
+      }
+      void __attribute__ ((__sym_alias__ ("FOOBAR_C"))) bar () {}
+      virtual __attribute__ ((__sym_alias__ ("FOODTR_C"))) ~foo() {}
+    };
+
+    int foo::var = 3;
+  }
+}
+
+#include <typeinfo>
+
+namespace d {
+  namespace {
+    class __attribute__ ((__sym_alias__ ("FOOCLS_D"))) foo {
+      virtual ~foo() {} // typeid(foo) is not defined otherwise.
+    };
+  }
+
+  extern std::type_info __attribute__ ((__alias__ ("FOOCLS_D"))) foo_d_typeinfo;
+}
+
+extern std::type_info __attribute__ ((__alias__ ("FOOCLS_C"))) foo_c_typeinfo;
+
+/* { dg-final { scan-assembler "FOOCLS_A" } } */
+/* { dg-final { scan-assembler "FOOCLS_A_Dupe" } } */
+/* { 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" } } */
+/* { dg-final { scan-assembler "FOOCLS_D" } } */
+/* { dg-final { scan-assembler "foo_d_typeinfo" } } */
+/* { dg-final { scan-assembler "foo_c_typeinfo" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
new file mode 100644
index 00000000000..6facfcc2cd9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+namespace {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_A"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo ();
+    void __attribute__ ((__sym_alias__ ("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-sym-alias-3.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
new file mode 100644
index 00000000000..42c7288ad4a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
@@ -0,0 +1,83 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+// sym can be applied to template function explicit instantiations.
+
+template <typename T>
+void
+fn(T) {
+};
+
+template void __attribute__ ((__sym_alias__ ("FOOFUN_UINT"))) fn<>(unsigned int);
+template void __attribute__ ((__sym_alias__ ("FOOFUN_LONG"))) fn<>(long);
+
+template<> void __attribute__ ((__sym_alias__ ("FOOFUN_CHAR"))) fn<>(char) {}
+
+
+template <typename T = void>
+struct
+foo {
+  virtual ~foo() {}
+
+  virtual void virtfun() {}
+
+  static void stfun() {}
+  void inlfun() {}
+};
+
+// Explicitly instantiate members before the enclosing class.
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_CHAR_VIRT"))) foo<char>::virtfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_CHAR_TI"))) foo<char>;
+
+// Though they're only output if the enclosing class is.
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_LONG_VIRT"))) foo<long>::virtfun();
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_LONG_TI_X"))) foo<long>;
+
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_VOID_ST"))) foo<void>::stfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_VOID_TI"))) foo<>;
+
+
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_X"))) foo<short>;
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_ST"))) foo<short>::stfun();
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_INL"))) foo<short>::inlfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_D"))) foo<short>;
+
+// Explicit specializations work too.
+
+template <>
+struct  __attribute__ ((__sym_alias__ ("FOOCLS_INT_TI")))
+foo<int>
+{
+  virtual ~foo() {}
+  virtual void __attribute__ ((__sym_alias__ ("FOOCLS_INT_VIRT"))) virtfun() {}
+};
+
+/* { dg-final { scan-assembler "FOOFUN_UINT" } } */
+/* { dg-final { scan-assembler "FOOFUN_LONG" } } */
+/* { dg-final { scan-assembler "FOOFUN_CHAR" } } */
+
+/* { dg-final { scan-assembler "FOOCLS_VOID_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_VOID_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_X" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_INL" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_D" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_TI_X" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_VIRT" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
new file mode 100644
index 00000000000..59b779de35d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+template <typename T = void>
+class
+__attribute__ ((__sym_alias__ ("FOOCLS")))
+foo // { dg-error "duplicate|already" }
+{
+  virtual ~foo() {}
+
+  template <typename U>
+  void
+    __attribute__ ((__sym_alias__ ("FOOTMF")))
+    tmemfun () {} // { dg-error "duplicate|already" }
+};
+
+template <typename T>
+void
+__attribute__ ((__sym_alias__ ("FOOTFN")))
+fn(T) { // { dg-error "duplicate|already" }
+};
+
+template class foo<>;
+template class foo<int>;
+template void foo<>::tmemfun<void>();
+template void foo<int>::tmemfun<void>();
+template void fn<>(int);
+template void fn<>(long);
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
new file mode 100644
index 00000000000..45d59b953c3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
@@ -0,0 +1,14 @@
+/* { dg-do compile { target c++11 } } */
+/* { dg-require-alias "" } */
+
+struct foo {
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+foo::foo () {}
+
+// Make sure the inherited cdtors don't duplicate the syms.
+struct bar : foo {
+  using foo::foo;
+};
diff --git a/gcc/varpool.cc b/gcc/varpool.cc
index e7b51b15e4a..33649906d1d 100644
--- a/gcc/varpool.cc
+++ b/gcc/varpool.cc
@@ -163,6 +163,9 @@ varpool_node::get_create (tree decl)
     }
 
   node->register_symbol ();
+
+  create_sym_alias_decls (decl);
+
   return node;
 }

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [gcc(refs/users/aoliva/heads/testme)] Introduce attribute sym_alias
@ 2023-12-06  2:31 Alexandre Oliva
  0 siblings, 0 replies; 33+ messages in thread
From: Alexandre Oliva @ 2023-12-06  2:31 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:a7eefc2beddb68bcc315b3e4477fe60c9a6bff98

commit a7eefc2beddb68bcc315b3e4477fe60c9a6bff98
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Fri Dec 1 14:31:46 2023 -0300

    Introduce attribute sym_alias
    
    This patch introduces an attribute to add extra asm names (aliases)
    for a decl 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, and
    also to local static variables.  In C++, it can also be attached to
    class types, namespace-scoped variables and functions, static data
    members, member functions, explicit instantiations and specializations
    of template functions, members and classes.
    
    When applied to constructors or destructor, additional sym aliases
    with _Base and _Del suffixes are defined for variants other than
    complete-object ones.  This changes the assumption that clones always
    carry the same attributes as their abstract declarations, so there is
    now a function to adjust them.
    
    C++ also had a bug in which attributes from local extern declarations
    failed to be propagated to a preexisting corresponding
    namespace-scoped decl.  I've fixed that, and adjusted acc tests that
    distinguished between C and C++ in this regard.
    
    Applying the attribute to class types is only valid in C++, and the
    effect is to attach the alias to the RTTI object associated with the
    class type.
    
    
    for  gcc/ChangeLog
    
            * attribs.cc: Include cgraph.h.
            (decl_attributes): Allow late introduction of sym_alias in
            types.
            (create_sym_alias_decl, create_sym_alias_decls): New.
            * attribs.h: Declare them.
            (FOR_EACH_SYM_ALIAS): New macro.
            * cgraph.cc (cgraph_node::create): Create sym_alias decls.
            * varpool.cc (varpool_node::get_create): Create sym_alias
            decls.
            * cgraph.h (symtab_node::remap_sym_alias_target): New.
            * symtab.cc (symtab_node::remap_sym_alias_target): Define.
            (symbol_table::insert_to_assembler_name_hash): Check for
            symbol name clashes.
            (symtab_node::noninterposable_alias): Drop sym_alias
            attributes.
            * cgraphunit.cc (cgraph_node::analyze): Create alias_target
            node if needed.
            (analyze_functions): Fixup visibility of implicit alias only
            after its node is analyzed.
            * doc/extend.texi (sym_alias): Document for variables,
            functions and types.
    
    for  gcc/ada/ChangeLog
    
            * doc/gnat_rm/interfacing_to_other_languages.rst: Mention
            attribute sym_alias to give RTTI symbols mnemonic names.
            * doc/gnat_ugn/the_gnat_compilation_model.rst: Mention
            aliases.  Fix incorrect ref to C1 ctor variant.
    
    for  gcc/c-family/ChangeLog
    
            * c-ada-spec.cc (pp_asm_name): Use first sym_alias if
            available.
            * c-attribs.cc (handle_sym_alias_attribute): New.
            (c_common_attribute_table): Add sym_alias.
            (handle_copy_attribute): Do not copy sym_alias attribute.
    
    for  gcc/c/ChangeLog
    
            * c-decl.cc (duplicate_decls): Remap sym_alias target.
            (finish_decl): Create varpool_node for local static
            variables.
    
    for  gcc/cp/ChangeLog
    
            * class.cc (adjust_clone_attributes): New.
            (copy_fndecl_with_name, build_clone): Call it.
            * cp-tree.h (adjust_clone_attributes): Declare.
            (update_sym_alias_interface): Declare.
            (update_tinfo_sym_alias): Declare.
            * decl.cc (duplicate_decls): Remap sym_alias target.
            Adjust clone attributes.
            (grokfndecl): Tentatively create sym_alias decls after
            adding attributes in e.g. a template member function explicit
            instantiation.
            * decl2.cc (cplus_decl_attributes): Update tinfo sym_alias.
            (copy_interface, update_sym_alias_interface): New.
            (determine_visibility): Update sym_alias interface.
            (tentative_decl_linkage, import_export_decl): Likewise.
            * name-lookup.cc: Include target.h and cgraph.h.
            (push_local_extern_decl_alias): Merge attributes with
            namespace-scoped decl, and drop duplicate sym_alias.
            * optimize.cc (maybe_clone_body): Re-adjust attributes after
            cloning them.  Update sym_alias interface.
            * rtti.cc: Include attribs.h and cgraph.h.
            (get_tinfo_decl): Copy sym_alias attributes from type to tinfo
            decl.  Create sym_alias decls.
            (update_tinfo_sym_alias): New.
    
    for  gcc/testsuite/ChangeLog
    
            * c-c++-common/goacc/declare-1.c: Adjust.
            * c-c++-common/goacc/declare-2.c: Adjust.
            * c-c++-common/torture/attr-sym-alias-1.c: New.
            * c-c++-common/torture/attr-sym-alias-2.c: New.
            * c-c++-common/torture/attr-sym-alias-3.c: New.
            * c-c++-common/torture/attr-sym-alias-4.c: New.
            * c-c++-common/torture/attr-sym-alias-5.c: New.
            * g++.dg/torture/attr-sym-alias-1.C: New.
            * g++.dg/torture/attr-sym-alias-2.C: New.
            * g++.dg/torture/attr-sym-alias-3.C: New.
            * g++.dg/torture/attr-sym-alias-4.C: New.
            * g++.dg/torture/attr-sym-alias-5.C: New.

Diff:
---
 .../doc/gnat_rm/interfacing_to_other_languages.rst |  6 ++
 .../doc/gnat_ugn/the_gnat_compilation_model.rst    | 10 ++-
 gcc/attribs.cc                                     | 74 +++++++++++++++-
 gcc/attribs.h                                      |  7 ++
 gcc/c-family/c-ada-spec.cc                         |  7 ++
 gcc/c-family/c-attribs.cc                          | 33 +++++++-
 gcc/c/c-decl.cc                                    |  7 ++
 gcc/cgraph.cc                                      |  2 +
 gcc/cgraph.h                                       |  4 +
 gcc/cgraphunit.cc                                  |  2 +-
 gcc/cp/class.cc                                    | 64 ++++++++++++++
 gcc/cp/cp-tree.h                                   |  4 +
 gcc/cp/decl.cc                                     |  4 +
 gcc/cp/decl2.cc                                    | 53 ++++++++++++
 gcc/cp/name-lookup.cc                              | 11 +++
 gcc/cp/optimize.cc                                 |  3 +
 gcc/cp/rtti.cc                                     | 71 +++++++++++++++-
 gcc/doc/extend.texi                                | 62 ++++++++++++++
 gcc/symtab.cc                                      | 62 ++++++++++++++
 gcc/testsuite/c-c++-common/goacc/declare-1.c       |  6 +-
 gcc/testsuite/c-c++-common/goacc/declare-2.c       | 14 +--
 .../c-c++-common/torture/attr-sym-alias-1.c        | 75 ++++++++++++++++
 .../c-c++-common/torture/attr-sym-alias-2.c        | 99 ++++++++++++++++++++++
 .../c-c++-common/torture/attr-sym-alias-3.c        | 34 ++++++++
 .../c-c++-common/torture/attr-sym-alias-4.c        | 28 ++++++
 .../c-c++-common/torture/attr-sym-alias-5.c        | 54 ++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C    | 89 +++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C    | 26 ++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C    | 83 ++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C    | 28 ++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C    | 14 +++
 gcc/varpool.cc                                     |  3 +
 32 files changed, 1020 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 ad0be511d48..b8de16ebf7c 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 ``sym_alias`` 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 fd15459203a..20045698962 100644
--- a/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
+++ b/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
@@ -4274,6 +4274,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__ ((__sym_alias__ ("Ctor_For_Animal")))
         Animal() {Age_Count = 0;};
       private:
         int Age_Count;
@@ -4306,6 +4307,7 @@ both Carnivore and Domestic, that is:
         virtual int  Number_Of_Teeth ();
         virtual void Set_Owner (char* Name);
 
+        __attribute__ ((__sym_alias__ ("Ctor_For_Dog"))) // mnemonic alias
         Dog(); // Constructor
       private:
         int  Tooth_Count;
@@ -4344,7 +4346,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;
@@ -4360,7 +4363,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,7 +4385,8 @@ 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.
+mangled name (or an alias) 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.cc b/gcc/attribs.cc
index ff4b638c25c..abfbbbf6294 100644
--- a/gcc/attribs.cc
+++ b/gcc/attribs.cc
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "target.h"
 #include "tree.h"
+#include "cgraph.h"
 #include "stringpool.h"
 #include "diagnostic-core.h"
 #include "attribs.h"
@@ -830,7 +831,8 @@ decl_attributes (tree *node, tree attributes, int flags,
 
       if (TYPE_P (*anode)
 	  && (flags & (int) ATTR_FLAG_TYPE_IN_PLACE)
-	  && COMPLETE_TYPE_P (*anode))
+	  && COMPLETE_TYPE_P (*anode)
+	  && !is_attribute_p ("sym_alias", name))
 	{
 	  warning (OPT_Wattributes, "type attributes ignored after type is already defined");
 	  continue;
@@ -2672,6 +2674,76 @@ attr_access::array_as_string (tree type) const
   return typstr;
 }
 
+/* Create a sym attribute for DECL to be visible with linkage name ID.  */
+
+tree
+create_sym_alias_decl (tree decl, tree id)
+{
+  const char *attr_str = "sym_alias";
+
+  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;
+
+      tree attr_name = get_identifier (attr_str);
+      error_at (DECL_SOURCE_LOCATION (decl),
+		"duplicate symbol name %qE in %qE attribute of %qD",
+		id, attr_name, decl);
+      inform (DECL_SOURCE_LOCATION (sym_node->decl),
+	      "already used by %qD", sym_node->decl);
+    }
+
+  tree clone = copy_node (decl);
+  DECL_ATTRIBUTES (clone) = remove_attribute (attr_str,
+					      DECL_ATTRIBUTES (decl));
+  /* Mark it as a "sym alias" for decl, an internal attribute that
+     enables symbol_table::insert_to_assembler_name_hash can recognize
+     sym_alias decls.  */
+  DECL_ATTRIBUTES (clone) = tree_cons (get_identifier ("sym alias"),
+				       decl, DECL_ATTRIBUTES (clone));
+  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;
+
+  symtab_node *node;
+  if (VAR_P (clone))
+    // DECL_READ_P (clone) = 1;
+    // node = varpool_node::create_extra_name_alias (clone, decl);
+    node = varpool_node::create_alias (clone, decl);
+  else
+    node = cgraph_node::create_same_body_alias (clone, decl);
+    // node = cgraph_node::create_alias (clone, decl);
+  if (symtab_node *dnode = symtab_node::get_create (decl))
+    node->copy_visibility_from (dnode);
+
+  return clone;
+}
+
+/* Create decls for all sym aliases requested in DECL's attributes.  */
+
+void
+create_sym_alias_decls (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl)
+      || DECL_ABSTRACT_P (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      create_sym_alias_decl (decl, id);
+    }
+}
+
 #if CHECKING_P
 
 namespace selftest
diff --git a/gcc/attribs.h b/gcc/attribs.h
index fdeebff1cd9..b7a67f73550 100644
--- a/gcc/attribs.h
+++ b/gcc/attribs.h
@@ -404,4 +404,11 @@ extern void init_attr_rdwr_indices (rdwr_map *, tree);
 extern attr_access *get_parm_access (rdwr_map &, tree,
 				     tree = current_function_decl);
 
+extern tree create_sym_alias_decl (tree, tree);
+extern void create_sym_alias_decls (tree);
+
+#define FOR_EACH_SYM_ALIAS(sym, attrs)					\
+  for (tree sym = lookup_attribute ("sym_alias", (attrs));		\
+       sym; sym = lookup_attribute ("sym_alias", TREE_CHAIN (sym)))
+
 #endif // GCC_ATTRIBS_H
diff --git a/gcc/c-family/c-ada-spec.cc b/gcc/c-family/c-ada-spec.cc
index 050994d8416..5042b9cfecd 100644
--- a/gcc/c-family/c-ada-spec.cc
+++ b/gcc/c-family/c-ada-spec.cc
@@ -1431,6 +1431,13 @@ pp_ada_tree_identifier (pretty_printer *buffer, tree node, tree type,
 static void
 pp_asm_name (pretty_printer *buffer, tree t)
 {
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (t))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.cc b/gcc/c-family/c-attribs.cc
index 854e987dc79..2593fde1fe9 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -110,7 +110,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_sym_alias_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,
@@ -392,6 +393,8 @@ const struct attribute_spec c_common_gnu_attributes[] =
 			      handle_alias_attribute, NULL },
   { "weakref",                0, 1, true,  false, false, false,
 			      handle_weakref_attribute, NULL },
+  { "sym_alias",              1, 1, false,  false, false, false,
+			      handle_sym_alias_attribute, NULL },
   { "no_instrument_function", 0, 0, true,  false, false, false,
 			      handle_no_instrument_function_attribute,
 			      NULL },
@@ -3092,7 +3095,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
@@ -3102,7 +3105,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
@@ -3112,6 +3115,29 @@ handle_alias_attribute (tree *node, tree name, tree args,
   return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs);
 }
 
+/* Handle a "sym_alias" attribute; arguments as in struct
+   attribute_spec.handler.  */
+
+static tree
+handle_sym_alias_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 (decl_in_symtab_p (node))
+    *no_add_attrs = false;
+  else if (TYPE_P (node) && c_dialect_cxx ())
+    *no_add_attrs = false;
+  else
+    return error_mark_node;
+
+  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.  */
 
@@ -3245,6 +3271,7 @@ handle_copy_attribute (tree *node, tree name, tree args,
 	      || is_attribute_p ("visibility", atname)
 	      || is_attribute_p ("weak", atname)
 	      || is_attribute_p ("weakref", atname)
+	      || is_attribute_p ("sym_alias", atname)
 	      || is_attribute_p ("target_clones", atname))
 	    continue;
 
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 92c83e1bf10..c1490e627df 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -3117,6 +3117,8 @@ duplicate_decls (tree newdecl, tree olddecl)
 
   merge_decls (newdecl, olddecl, newtype, oldtype);
 
+  symtab_node::remap_sym_alias_target (newdecl, olddecl);
+
   /* The NEWDECL will no longer be needed.
 
      Before releasing the node, be sure to remove function from symbol
@@ -5902,6 +5904,11 @@ finish_decl (tree decl, location_t init_loc, tree init,
 	    set_user_assembler_name (decl, asmspec);
 	}
 
+      /* Give attribute sym_alias a chance to make the symbol name
+	 available for aliasing, even for a static local variable.  */
+      if (VAR_P (decl) && !DECL_EXTERNAL (decl) && is_global_var (decl))
+	varpool_node::get_create (decl);
+
       if (DECL_FILE_SCOPE_P (decl))
 	{
 	  if (DECL_INITIAL (decl) == NULL_TREE
diff --git a/gcc/cgraph.cc b/gcc/cgraph.cc
index f93259a8c70..0bef2179e89 100644
--- a/gcc/cgraph.cc
+++ b/gcc/cgraph.cc
@@ -523,6 +523,8 @@ cgraph_node::create (tree decl)
   node->register_symbol ();
   maybe_record_nested_function (node);
 
+  create_sym_alias_decls (decl);
+
   return node;
 }
 
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index 2b320557616..6d61e327fb6 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -327,6 +327,10 @@ public:
   /* Return DECL that alias is aliasing.  */
   inline tree get_alias_target_tree ();
 
+  /* Remap sym alias nodes recorded as aliasing REPLACED to alias REPLACEMENT
+     instead.  */
+  static void remap_sym_alias_target (tree replaced, tree replacement);
+
   /* Set section for symbol and its aliases.  */
   void set_section (const char *section);
 
diff --git a/gcc/cgraphunit.cc b/gcc/cgraphunit.cc
index 9a550a5cce6..a66db795c78 100644
--- a/gcc/cgraphunit.cc
+++ b/gcc/cgraphunit.cc
@@ -1176,7 +1176,7 @@ analyze_functions (bool first_time)
      C++ FE is confused about the COMDAT groups being right.  */
   if (symtab->cpp_implicit_aliases_done)
     FOR_EACH_SYMBOL (node)
-      if (node->cpp_implicit_alias)
+      if (node->cpp_implicit_alias && node->analyzed)
 	  node->fixup_same_cpp_alias_visibility (node->get_alias_target ());
   build_type_inheritance_graph ();
 
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index 6fdb56abfb9..fa8fc63db82 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -4896,6 +4896,68 @@ check_methods (tree t)
     }
 }
 
+/* Adjust sym alias name for CLONE, cloned from FN and named NAME,
+   if it is a cdtor, and drop the sym alias from other clones.  */
+
+void
+adjust_clone_attributes (tree fn, tree clone, tree name, bool skip_copy_p)
+{
+  if (IDENTIFIER_CDTOR_P (name))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	{
+	  found = true;
+	  break;
+	}
+
+      if (found
+	  && (name == complete_ctor_identifier
+	      || name == complete_dtor_identifier))
+	{
+	  /* Reuse the sym alias decls created for the primary cdtor
+	     decl.  */
+	  symtab_node::remap_sym_alias_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);
+
+	  if (!skip_copy_p)
+	    DECL_ATTRIBUTES (clone) = copy_list (DECL_ATTRIBUTES (clone));
+
+	  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	    {
+	      /* We need to copy this even with skip_copy_p, because
+		 even then copying was shallow.  */
+	      TREE_VALUE (sym) = copy_list (TREE_VALUE (sym));
+	      /* Append suf to the sym alias name.  */
+	      tree str = TREE_VALUE (TREE_VALUE (sym));
+	      char *symname = concat (TREE_STRING_POINTER (str), suf, NULL);
+	      str = build_string (TREE_STRING_LENGTH (str) + xlen, symname);
+	      TREE_VALUE (TREE_VALUE (sym)) = str;
+	      free (symname);
+	    }
+
+	  if (symtab_node::get (clone))
+	    create_sym_alias_decls (clone);
+	}
+    }
+  else
+    DECL_ATTRIBUTES (clone)
+      = remove_attribute ("sym_alias", DECL_ATTRIBUTES (clone));
+}
+
 /* FN is constructor, destructor or operator function.  Clone the
    declaration to create a NAME'd variant.  NEED_VTT_PARM_P and
    OMIT_INHERITED_PARMS_P are relevant if it's a cdtor.  */
@@ -5065,6 +5127,8 @@ build_clone (tree fn, tree name, bool need_vtt_parm_p,
   DECL_CHAIN (clone) = DECL_CHAIN (fn);
   DECL_CHAIN (fn) = clone;
 
+  adjust_clone_attributes (fn, clone, name);
+
   return clone;
 }
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 795152c9ad2..dca2d31a372 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5806,6 +5806,8 @@ struct GTY((for_user)) spec_entry
 
 extern int current_class_depth;
 
+void adjust_clone_attributes (tree fn, tree clone, tree name, bool = false);
+
 /* in decl.cc */
 
 /* An array of static vars & fns.  */
@@ -7043,6 +7045,7 @@ extern void do_push_parm_decls			(tree, tree, tree *);
 extern tree do_aggregate_paren_init		(tree, tree);
 
 /* in decl2.cc */
+extern void update_sym_alias_interface		(tree);
 extern void record_mangling			(tree, bool);
 extern void overwrite_mangling			(tree, tree);
 extern void note_mangling_alias			(tree, tree);
@@ -7617,6 +7620,7 @@ extern bool emit_tinfo_decl			(tree);
 extern unsigned get_pseudo_tinfo_index		(tree);
 extern tree get_pseudo_tinfo_type		(unsigned);
 extern tree build_if_nonnull			(tree, tree, tsubst_flags_t);
+extern void update_tinfo_sym_alias		(tree);
 
 /* in search.cc */
 extern tree get_parent_with_private_access 	(tree decl, tree binfo);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 4b685270097..cc7f7a99487 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -3277,6 +3277,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
 	      && TREE_STATIC (olddecl))))
     make_decl_rtl (olddecl);
 
+  symtab_node::remap_sym_alias_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.
@@ -3300,6 +3302,7 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
       FOR_EACH_CLONE (clone, olddecl)
 	{
 	  DECL_ATTRIBUTES (clone) = DECL_ATTRIBUTES (olddecl);
+	  adjust_clone_attributes (olddecl, clone, DECL_NAME (clone));
 	  DECL_PRESERVE_P (clone) |= DECL_PRESERVE_P (olddecl);
 	}
     }
@@ -10838,6 +10841,7 @@ grokfndecl (tree ctype,
     {
       cplus_decl_attributes (&decl, *attrlist, 0);
       *attrlist = NULL_TREE;
+      create_sym_alias_decls (decl);
     }
 
   if (DECL_HAS_CONTRACTS_P (decl))
diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
index bee84879023..00f19962e5d 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -1846,6 +1846,9 @@ cplus_decl_attributes (tree *decl, tree attributes, int flags)
   if (late_attrs)
     save_template_attributes (late_attrs, decl, flags);
 
+  if (TYPE_P (*decl) && attributes)
+    update_tinfo_sym_alias (*decl);
+
   /* Propagate deprecation out to the template.  */
   if (TREE_DEPRECATED (*decl))
     if (tree ti = get_template_info (*decl))
@@ -2202,6 +2205,50 @@ 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);
+  if (symtab_node *dest_node = symtab_node::get (dest))
+    if (symtab_node *decl_node = symtab_node::get (decl))
+      decl_node->copy_visibility_from (dest_node);
+}
+
+/* Propagate linkage changes to sym aliases.  */
+
+void
+update_sym_alias_interface (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.  */
 
@@ -3009,6 +3056,8 @@ determine_visibility (tree decl)
        translation unit, we can make the type internal.  */
     constrain_visibility (decl, VISIBILITY_ANON, false);
 
+  update_sym_alias_interface (decl);
+
   /* If visibility changed and DECL already has DECL_RTL, ensure
      symbol flags are updated.  */
   if ((DECL_VISIBILITY (decl) != orig_visibility
@@ -3271,6 +3320,8 @@ tentative_decl_linkage (tree decl)
       else if (VAR_P (decl))
 	maybe_commonize_var (decl);
     }
+
+  update_sym_alias_interface (decl);
 }
 
 /* DECL is a FUNCTION_DECL or VAR_DECL.  If the object file linkage
@@ -3505,6 +3556,8 @@ import_export_decl (tree decl)
     }
 
   DECL_INTERFACE_KNOWN (decl) = 1;
+
+  update_sym_alias_interface (decl);
 }
 
 /* Return an expression that performs the destruction of DECL, which
diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc
index 76f1d44610a..3a19a571256 100644
--- a/gcc/cp/name-lookup.cc
+++ b/gcc/cp/name-lookup.cc
@@ -22,9 +22,11 @@ along with GCC; see the file COPYING3.  If not see
 #define INCLUDE_MEMORY
 #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"
@@ -3675,6 +3677,15 @@ push_local_extern_decl_alias (tree decl)
 	  /* Adjust visibility.  */
 	  determine_visibility (alias);
 	}
+      else if (DECL_P (alias))
+	DECL_ATTRIBUTES (alias)
+	  = targetm.merge_decl_attributes (alias, decl);
+      if (DECL_P (alias))
+	{
+	  symtab_node::remap_sym_alias_target (decl, alias);
+	  DECL_ATTRIBUTES (decl)
+	    = remove_attribute ("sym_alias", DECL_ATTRIBUTES (alias));
+	}
     }
 
   retrofit_lang_decl (decl);
diff --git a/gcc/cp/optimize.cc b/gcc/cp/optimize.cc
index 9e8926e4cc6..4a2e8cb4354 100644
--- a/gcc/cp/optimize.cc
+++ b/gcc/cp/optimize.cc
@@ -528,9 +528,12 @@ maybe_clone_body (tree fn)
       DECL_VISIBILITY_SPECIFIED (clone) = DECL_VISIBILITY_SPECIFIED (fn);
       DECL_DLLIMPORT_P (clone) = DECL_DLLIMPORT_P (fn);
       DECL_ATTRIBUTES (clone) = clone_attrs (DECL_ATTRIBUTES (fn));
+      adjust_clone_attributes (fn, clone, DECL_NAME (clone), true);
       DECL_DISREGARD_INLINE_LIMITS (clone) = DECL_DISREGARD_INLINE_LIMITS (fn);
       set_decl_section_name (clone, fn);
 
+      update_sym_alias_interface (clone);
+
       /* Adjust the parameter names and locations.  */
       parm = DECL_ARGUMENTS (fn);
       clone_parm = DECL_ARGUMENTS (clone);
diff --git a/gcc/cp/rtti.cc b/gcc/cp/rtti.cc
index 7878929c246..4bb7b8c8c6e 100644
--- a/gcc/cp/rtti.cc
+++ b/gcc/cp/rtti.cc
@@ -28,8 +28,10 @@ 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"
+#include "cgraph.h"
 
 /* C++ returns type information to the user in struct type_info
    objects. We also use type information to implement dynamic_cast and
@@ -479,8 +481,13 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
 	  = build_tree_list (get_identifier ("non overlapping"),
 			     NULL_TREE);
       else
+	/* Share the non overlapping attribute, without assuming it's
+	   the only attribute, but assuming it's the last if it's
+	   present.  There may be sym aliases too, and those are not
+	   to be shared.  */
 	DECL_ATTRIBUTES (d)
-	  = DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]);
+	  = lookup_attribute ("non overlapping",
+			      DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]));
 
       /* Mark the variable as undefined -- but remember that we can
 	 define it later if we need to do so.  */
@@ -492,6 +499,16 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
       if (CLASS_TYPE_P (type))
 	CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type)) = d;
 
+      /* Copy sym alias attributes from the type to the rtti obj decl.  */
+      tree *attrs = &DECL_ATTRIBUTES (d);
+      FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+	{
+	  tree attr = tree_cons (TREE_PURPOSE (sym), TREE_VALUE (sym), *attrs);
+	  *attrs = attr;
+	  attrs = &TREE_CHAIN (attr);
+	}
+      create_sym_alias_decls (d);
+
       /* Add decl to the global array of tinfo decls.  */
       vec_safe_push (unemitted_tinfo_decls, d);
     }
@@ -499,6 +516,58 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
   return d;
 }
 
+/* After modifying the attributes of TYPE, check whether tinfo was
+   already created and, if so, add to it any sym alias attributes
+   that were not already present.  */
+
+void
+update_tinfo_sym_alias (tree type)
+{
+  if (!TYPE_SIZE (type) || !CLASS_TYPE_P (type))
+    return;
+
+  tree d = CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type));
+  if (!d)
+    return;
+
+  bool first = true;
+  symtab_node *node = NULL;
+
+  tree *attrs = &DECL_ATTRIBUTES (d);
+  FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (d_sym, *attrs)
+	if (TREE_VALUE (sym) == TREE_VALUE (d_sym))
+	  {
+	    found = true;
+	    break;
+	  }
+
+      if (found)
+	continue;
+
+      tree attr = tree_cons (TREE_PURPOSE (sym),
+			     TREE_VALUE (sym),
+			     *attrs);
+      *attrs = attr;
+      attrs = &TREE_CHAIN (attr);
+
+      if (first)
+	{
+	  first = false;
+	  node = symtab_node::get (d);
+	}
+
+      if (!node)
+	continue;
+
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+      create_sym_alias_decl (d, id);
+    }
+}
+
 /* Return the type_info object for TYPE.  */
 
 tree
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index af782b3f228..22b7e3ab7bf 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -4142,6 +4142,49 @@ Function Attributes}, @ref{PowerPC Function Attributes},
 @ref{Nios II Function Attributes}, and @ref{S/390 Function Attributes}
 for details.
 
+@cindex @code{sym_alias} function attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} attribute causes @var{name} to be output as an
+alias to the function, with the same linkage and visibility as the
+function, when the function definition is output, provided that the
+function could be an alias target.  For instance,
+
+@smallexample
+void f (uint64_t) __attribute__ ((__sym_alias__ ("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},
+with the same linkage and visibility.  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.
+
+Aliases introduced with this attribute, such as @samp{f_u64} in the
+example above, are assembly symbol names: they do not undergo C++ name
+mangling, and are not made visible in any scope in the source language.
+They can, however, be named as alias targets, as long as their
+definition is output.  Naming a @samp{sym_alias} as an alias target will
+@emph{not} cause a definition to be output if it otherwise wouldn't.
+This may affect inline functions, C++ template instantiations, and other
+synthesized definitions that would have to be (synthesized and) compiled
+in order for the @samp{sym_alias} to be considered as a potential alias
+target.
+
+This attribute requires assembler and object file support for aliases,
+and may not be available on all targets.
+
 @cindex @code{symver} function attribute
 @item symver ("@var{name2}@@@var{nodename}")
 On ELF targets this attribute creates a symbol version.  The @var{name2} part
@@ -8069,6 +8112,10 @@ will be placed in new, unique sections.
 
 This additional functionality requires Binutils version 2.36 or later.
 
+@cindex @code{sym_alias} variable attribute
+@item sym_alias ("@var{name}")
+See @pxref{Common Function Attributes}.
+
 @cindex @code{uninitialized} variable attribute
 @item uninitialized
 This attribute, attached to a variable with automatic storage, means that
@@ -9164,6 +9211,21 @@ is not supported; that is to say, if a given scalar object can be accessed
 through distinct types that assign a different storage order to it, then the
 behavior is undefined.
 
+@cindex @code{sym_alias} type attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} type 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__ ((__sym_alias__ ("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.
+
 @cindex @code{transparent_union} type attribute
 @item transparent_union
 
diff --git a/gcc/symtab.cc b/gcc/symtab.cc
index 0470509a98d..44df52095c1 100644
--- a/gcc/symtab.cc
+++ b/gcc/symtab.cc
@@ -187,6 +187,22 @@ symbol_table::insert_to_assembler_name_hash (symtab_node *node,
 	(*aslot)->previous_sharing_asm_name = node;
       *aslot = node;
 
+      /* Check for sym_alias name clashes.  In create_sym_alias_decl,
+	 we check for a preexisting definition using the same
+	 assembler_name, so here we check for a previously-defined
+	 sym_name, marked with a "sym name" pseudo-attribute that
+	 points back at the declaration for which it was created.  */
+      if (symtab_node *sym_node = node->next_sharing_asm_name)
+	if (lookup_attribute ("sym alias",
+			      DECL_ATTRIBUTES (sym_node->decl)))
+	  {
+	    error_at (DECL_SOURCE_LOCATION (node->decl),
+		      "duplicate symbol name %qE", decl);
+	    inform (DECL_SOURCE_LOCATION (sym_node->decl),
+		    "already used by %qD in a %qE attribute",
+		    sym_node->decl, get_identifier ("sym_alias"));
+	  }
+
       /* Update also possible inline clones sharing a decl.  */
       cnode = dyn_cast <cgraph_node *> (node);
       if (cnode && cnode->clones && with_clones)
@@ -1943,6 +1959,46 @@ symtab_node::noninterposable_alias (symtab_node *node, void *data)
   return false;
 }
 
+/* Remap sym alias nodes recorded as aliasing REPLACED to alias
+   REPLACEMENT instead.  */
+
+void
+symtab_node::remap_sym_alias_target (tree replaced, tree replacement)
+{
+  symtab_node *repl_node = NULL;
+  if (!decl_in_symtab_p (replacement)
+      || !(repl_node = symtab_node::get (replacement)))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (replaced))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      symtab_node *sym_node = symtab_node::get_for_asmname (id);
+
+      if (!sym_node)
+	{
+	  create_sym_alias_decl (replacement, id);
+	  continue;
+	}
+
+      gcc_assert (!sym_node->analyzed);
+      if (sym_node->alias_target != replaced)
+	continue;
+
+      // sym_node->definition = 0;
+
+      if (VAR_P (replaced))
+	// varpool_node::create_extra_name_alias (sym_node->decl, replacement);
+	varpool_node::create_alias (sym_node->decl, replacement);
+      else
+	cgraph_node::create_same_body_alias (sym_node->decl, replacement);
+	// cgraph_node::create_alias (sym_node->decl, replacement);
+      sym_node->copy_visibility_from (repl_node);
+    }
+}
+
 /* 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.  */
@@ -1970,6 +2026,12 @@ symtab_node::noninterposable_alias (void)
 
   /* Otherwise create a new one.  */
   new_decl = copy_node (node->decl);
+  DECL_ATTRIBUTES (new_decl) = remove_attribute ("sym_alias",
+						   DECL_ATTRIBUTES
+						   (new_decl));
+  DECL_ATTRIBUTES (new_decl) = remove_attribute ("sym alias",
+						   DECL_ATTRIBUTES
+						   (new_decl));
   DECL_DLLIMPORT_P (new_decl) = 0;
   tree name = clone_function_name (node->decl, "localalias");
   if (!flag_wpa)
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-1.c b/gcc/testsuite/c-c++-common/goacc/declare-1.c
index 46ee01b6759..f2842893318 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-1.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-1.c
@@ -113,11 +113,11 @@ f_2 (void)
   int va3;
 #pragma acc declare device_resident(va3)
 
-#ifndef __cplusplus
+#if 0
   /* TODO PR90868
 
-     C: "error: variable '[...]' used more than once with '#pragma acc declare'".  */
-#else
+     "error: variable '[...]' used more than once with '#pragma acc declare'".  */
+
   extern int ve0;
 #pragma acc declare create(ve0)
 
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-2.c b/gcc/testsuite/c-c++-common/goacc/declare-2.c
index e2e22be57e9..aec59b69754 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-2.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-2.c
@@ -137,25 +137,25 @@ void
 f_pr90868_2 (void)
 {
   extern int we0;
-#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" } */
 
   extern int we1;
-#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" } */
 
   extern int *we2;
-#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" } */
 
   extern int we3;
-#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" } */
 
   extern int we4;
-#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" } */
 
   extern int we5;
-#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" } */
  
   extern int we6;
-#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" } */
 }
 
 
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
new file mode 100644
index 00000000000..2fe1948aed9
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
@@ -0,0 +1,75 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+extern int var_alias __attribute__ ((__alias__ ("FOOVAR_A")));
+int var_a = 1;
+
+void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+
+void foo_alias () __attribute__ ((__alias__ ("FOOBAR_A")));
+
+void
+foo_a ()
+{
+}
+
+
+int var_b;
+extern int var_b __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+
+void
+foo_b ()
+{
+}
+
+void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+
+
+int var_c __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+
+void __attribute__ ((__sym_alias__ ("FOOBAR_C")))
+foo_c ()
+{
+}
+
+
+void baf () {}
+
+void foo_x () {
+  extern void baf () __attribute__ ((__sym_alias__ ("FOO_BAF")));
+  extern void bad () /* ok, but not defined, so no alias issued.  */
+    __attribute__ ((__sym_alias__ ("FOO_BAD")));
+  extern void bar () __attribute__ ((__sym_alias__ ("FOO_BAR")));
+
+  static int x __attribute__ ((sym_alias ("FOO_x")));
+}
+
+void bar () {}
+
+extern int xr __attribute__ ((alias ("FOO_x")));
+
+
+ /* The *u* symbols are not defined, so the sym_aliases do not clash.  ??? Is
+    this just an implementation artifact, or a feature someone might actually
+    rely on?  */
+extern int var_u1 __attribute__ ((__sym_alias__ ("VAR_U")));
+extern int var_u2 __attribute__ ((__sym_alias__ ("VAR_U")));
+
+void foo_u1 () __attribute__ ((__sym_alias__ ("FOOBAR_U")));
+void foo_u2 () __attribute__ ((__sym_alias__ ("FOOBAR_U")));
+
+
+/* { dg-final { scan-assembler "FOOBAR_A" } } */
+/* { dg-final { scan-assembler "foo_alias" } } */
+/* { dg-final { scan-assembler "FOOVAR_A" } } */
+/* { dg-final { scan-assembler "var_alias" } } */
+/* { dg-final { scan-assembler "FOOBAR_B" } } */
+/* { dg-final { scan-assembler "FOOVAR_B" } } */
+/* { dg-final { scan-assembler "FOOBAR_C" } } */
+/* { dg-final { scan-assembler "FOOVAR_C" } } */
+/* { dg-final { scan-assembler "FOO_BAF" } } */
+/* { dg-final { scan-assembler "FOO_BAR" } } */
+/* { dg-final { scan-assembler-not "FOO_BAD" } } */
+/* { dg-final { scan-assembler-not "VAR_U" } } */
+/* { dg-final { scan-assembler-not "FOOBAR_U" } } */
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
new file mode 100644
index 00000000000..f692a552606
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
@@ -0,0 +1,99 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+struct s
+{
+  int mem __attribute__ ((__sym_alias__ ("S_MEM"))); /* { dg-warning "attribute ignored" } */
+};
+
+void foo () {
+  extern void bar () __attribute__ ((__sym_alias__ ("FOO_BAR"))); /* Ok.  */
+  int i __attribute__ ((sym_alias ("FOO_i"))); /* { dg-warning "ignored" } */
+  static int x __attribute__ ((sym_alias ("FOO_x")));
+  static int sx __attribute__ ((alias ("FOO_x"))); /* { dg-warning "ignored" } */
+  extern int xx __attribute__ ((alias ("FOO_x"))); /* { dg-warning "ignored" } */
+  extern int y __attribute__ ((sym_alias ("FOO_y"))); /* Ok.  */
+}
+
+int dr __attribute__ ((alias ("FOO_x"))); /* { dg-error "defined both" } */
+
+
+int j __attribute__ ((sym_alias ("J_var")));
+void __attribute__ ((alias ("J_var")))
+j_fn (); /* { dg-error "between function and variable" } */
+
+
+void __attribute__ ((sym_alias ("K_fn")))
+k () {
+}
+extern int __attribute__ ((alias ("K_fn")))
+k_var; /* { dg-error "between function and variable" } */
+
+
+int l __attribute__ ((sym_alias ("L_fn")));
+
+/* These should be detected and reported, not because the names clash at the
+   source level, but because the asm symbols do.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+void
+L_fn () /* { dg-error "duplicate" "" } */
+{
+}
+
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __attribute__ ((sym_alias ("M_var")))
+m ()
+{
+}
+
+int M_var; /* { dg-error "duplicate" "" } */
+
+
+void __attribute__ ((sym_alias ("N_sym")))
+n_fn ()
+{
+}
+
+int __attribute__ ((sym_alias ("N_sym")))
+n_var; /* { dg-error "duplicate" } */
+
+
+int __attribute__ ((sym_alias ("O_sym")))
+o_var;
+
+void __attribute__ ((sym_alias ("O_sym")))
+o_fn () /* { dg-error "duplicate" } */
+{
+}
+
+int __attribute__ ((sym_alias ("P_sym")))
+p_var1;
+
+int __attribute__ ((sym_alias ("P_sym")))
+p_var2; /* { dg-error "duplicate" } */
+
+void __attribute__ ((sym_alias ("Q_sym")))
+q_fn1 ()
+{
+}
+
+void __attribute__ ((sym_alias ("Q_sym")))
+q_fn2 () /* { dg-error "duplicate" } */
+{
+}
+
+int __attribute__ ((sym_alias ("R_var")))
+R_var; /* { dg-error "duplicate" } */
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __attribute__ ((sym_alias ("S_fn")))
+S_fn () /* { dg-error "duplicate" } */
+{
+}
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
new file mode 100644
index 00000000000..52f8fb76496
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
@@ -0,0 +1,34 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int var_a = 1;
+
+void
+foo_a ()
+{
+  extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+}
+
+void
+foo_b ()
+{
+  extern int var_b __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+}
+
+int var_b;
+
+void __attribute__ ((__sym_alias__ ("FOOBAR_C")))
+foo_c ()
+{
+  void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+  /* Another sym for var_b.  */
+  extern int var_b __attribute__ ((__sym_alias__ ("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-sym-alias-4.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
new file mode 100644
index 00000000000..2beafa46379
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-require-alias "" } */
+
+int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A"))) = 42;
+
+int __attribute__ ((__sym_alias__ ("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/c-c++-common/torture/attr-sym-alias-5.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
new file mode 100644
index 00000000000..bee90136daf
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
@@ -0,0 +1,54 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+/* { dg-require-visibility "" } */
+
+int __attribute__ ((sym_alias ("A_hid"), visibility ("hidden"))) a;
+
+extern int __attribute__ ((sym_alias ("B_prt"))) b;
+
+int b __attribute__ ((visibility ("protected")));
+
+extern int __attribute__ ((sym_alias ("C_ntr"))) c;
+
+extern int c __attribute__ ((visibility ("internal")));
+
+int c;
+
+static int d __attribute__ ((sym_alias ("D_stt")));
+
+
+extern void __attribute__ ((sym_alias ("F_hid"), visibility ("hidden")))
+f ();
+
+void
+f () {
+}
+
+extern void __attribute__ ((sym_alias ("G_prt")))
+g ();
+
+void __attribute__ ((visibility ("protected")))
+g () {
+}
+
+extern void __attribute__ ((sym_alias ("H_ntr")))
+h ();
+
+void __attribute__ ((visibility ("internal")))
+h ();
+
+void h () {
+}
+
+static void __attribute__ ((sym_alias ("I_stt")))
+i () {
+}
+
+/* { dg-final { scan-assembler {hidden[^\n]*A_hid} } } */
+/* { dg-final { scan-assembler {protected[^\n]*B_prt} } } */
+/* { dg-final { scan-assembler {internal[^\n]*C_ntr} } } */
+/* { dg-final { scan-assembler-not {glob[^\n]*D_stt} } } */
+/* { dg-final { scan-assembler {hidden[^\n]*F_hid} } } */
+/* { dg-final { scan-assembler {protected[^\n]*G_prt} } } */
+/* { dg-final { scan-assembler {internal[^\n]*H_ntr} } } */
+/* { dg-final { scan-assembler-not {glob[^\n]*I_stt} } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
new file mode 100644
index 00000000000..51729994913
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
@@ -0,0 +1,89 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+class __attribute__ ((__sym_alias__ ("FOOCLS_A"),
+		      __sym_alias__ ("FOOCLS_A_Dupe"))) foo {
+  static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  void __attribute__ ((__sym_alias__ ("FOOBAR_A"))) bar ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+int foo::var = 1;
+
+foo::foo () {}
+
+void foo::bar () {}
+
+namespace b {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_B"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_B"))) foo ();
+    void __attribute__ ((__sym_alias__ ("FOOBAR_B"))) bar () {}
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_B"))) ~foo() {}
+  };
+
+  int foo::var = 2;
+
+  foo::foo () {
+    void (foo::*pbar)() = &foo::bar;
+  }
+}
+
+namespace c {
+  namespace cd {
+    class __attribute__ ((__sym_alias__ ("FOOCLS_C"))) foo {
+      static int var __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+      __attribute__ ((__sym_alias__ ("FOOCTR_C"))) foo () {
+	void (foo::*pbar)() = &foo::bar;
+      }
+      void __attribute__ ((__sym_alias__ ("FOOBAR_C"))) bar () {}
+      virtual __attribute__ ((__sym_alias__ ("FOODTR_C"))) ~foo() {}
+    };
+
+    int foo::var = 3;
+  }
+}
+
+#include <typeinfo>
+
+namespace d {
+  namespace {
+    class __attribute__ ((__sym_alias__ ("FOOCLS_D"))) foo {
+      virtual ~foo() {} // typeid(foo) is not defined otherwise.
+    };
+  }
+
+  extern std::type_info __attribute__ ((__alias__ ("FOOCLS_D"))) foo_d_typeinfo;
+}
+
+extern std::type_info __attribute__ ((__alias__ ("FOOCLS_C"))) foo_c_typeinfo;
+
+/* { dg-final { scan-assembler "FOOCLS_A" } } */
+/* { dg-final { scan-assembler "FOOCLS_A_Dupe" } } */
+/* { 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" } } */
+/* { dg-final { scan-assembler "FOOCLS_D" } } */
+/* { dg-final { scan-assembler "foo_d_typeinfo" } } */
+/* { dg-final { scan-assembler "foo_c_typeinfo" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
new file mode 100644
index 00000000000..6facfcc2cd9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+namespace {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_A"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo ();
+    void __attribute__ ((__sym_alias__ ("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-sym-alias-3.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
new file mode 100644
index 00000000000..42c7288ad4a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
@@ -0,0 +1,83 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+// sym can be applied to template function explicit instantiations.
+
+template <typename T>
+void
+fn(T) {
+};
+
+template void __attribute__ ((__sym_alias__ ("FOOFUN_UINT"))) fn<>(unsigned int);
+template void __attribute__ ((__sym_alias__ ("FOOFUN_LONG"))) fn<>(long);
+
+template<> void __attribute__ ((__sym_alias__ ("FOOFUN_CHAR"))) fn<>(char) {}
+
+
+template <typename T = void>
+struct
+foo {
+  virtual ~foo() {}
+
+  virtual void virtfun() {}
+
+  static void stfun() {}
+  void inlfun() {}
+};
+
+// Explicitly instantiate members before the enclosing class.
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_CHAR_VIRT"))) foo<char>::virtfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_CHAR_TI"))) foo<char>;
+
+// Though they're only output if the enclosing class is.
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_LONG_VIRT"))) foo<long>::virtfun();
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_LONG_TI_X"))) foo<long>;
+
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_VOID_ST"))) foo<void>::stfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_VOID_TI"))) foo<>;
+
+
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_X"))) foo<short>;
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_ST"))) foo<short>::stfun();
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_INL"))) foo<short>::inlfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_D"))) foo<short>;
+
+// Explicit specializations work too.
+
+template <>
+struct  __attribute__ ((__sym_alias__ ("FOOCLS_INT_TI")))
+foo<int>
+{
+  virtual ~foo() {}
+  virtual void __attribute__ ((__sym_alias__ ("FOOCLS_INT_VIRT"))) virtfun() {}
+};
+
+/* { dg-final { scan-assembler "FOOFUN_UINT" } } */
+/* { dg-final { scan-assembler "FOOFUN_LONG" } } */
+/* { dg-final { scan-assembler "FOOFUN_CHAR" } } */
+
+/* { dg-final { scan-assembler "FOOCLS_VOID_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_VOID_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_X" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_INL" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_D" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_TI_X" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_VIRT" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
new file mode 100644
index 00000000000..59b779de35d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+template <typename T = void>
+class
+__attribute__ ((__sym_alias__ ("FOOCLS")))
+foo // { dg-error "duplicate|already" }
+{
+  virtual ~foo() {}
+
+  template <typename U>
+  void
+    __attribute__ ((__sym_alias__ ("FOOTMF")))
+    tmemfun () {} // { dg-error "duplicate|already" }
+};
+
+template <typename T>
+void
+__attribute__ ((__sym_alias__ ("FOOTFN")))
+fn(T) { // { dg-error "duplicate|already" }
+};
+
+template class foo<>;
+template class foo<int>;
+template void foo<>::tmemfun<void>();
+template void foo<int>::tmemfun<void>();
+template void fn<>(int);
+template void fn<>(long);
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
new file mode 100644
index 00000000000..45d59b953c3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
@@ -0,0 +1,14 @@
+/* { dg-do compile { target c++11 } } */
+/* { dg-require-alias "" } */
+
+struct foo {
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+foo::foo () {}
+
+// Make sure the inherited cdtors don't duplicate the syms.
+struct bar : foo {
+  using foo::foo;
+};
diff --git a/gcc/varpool.cc b/gcc/varpool.cc
index e7b51b15e4a..33649906d1d 100644
--- a/gcc/varpool.cc
+++ b/gcc/varpool.cc
@@ -163,6 +163,9 @@ varpool_node::get_create (tree decl)
     }
 
   node->register_symbol ();
+
+  create_sym_alias_decls (decl);
+
   return node;
 }

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [gcc(refs/users/aoliva/heads/testme)] Introduce attribute sym_alias
@ 2023-12-05 21:51 Alexandre Oliva
  0 siblings, 0 replies; 33+ messages in thread
From: Alexandre Oliva @ 2023-12-05 21:51 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:5e9338d608bb3c3907d52c1638c2f58554e98f50

commit 5e9338d608bb3c3907d52c1638c2f58554e98f50
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Fri Dec 1 14:31:46 2023 -0300

    Introduce attribute sym_alias
    
    This patch introduces an attribute to add extra asm names (aliases)
    for a decl 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 class types, namespace-scoped
    variables and functions, static data members, member functions,
    explicit instantiations and specializations of template functions,
    members and classes.
    
    When applied to constructors or destructor, additional sym aliases
    with _Base and _Del suffixes are defined for variants other than
    complete-object ones.  This changes the assumption that clones always
    carry the same attributes as their abstract declarations, so there is
    now a function to adjust them.
    
    C++ also had a bug in which attributes from local extern declarations
    failed to be propagated to a preexisting corresponding
    namespace-scoped decl.  I've fixed that, and adjusted acc tests that
    distinguished between C and C++ in this regard.
    
    Applying the attribute to class types is only valid in C++, and the
    effect is to attach the alias to the RTTI object associated with the
    class type.
    
    for  gcc/ChangeLog
    
            * attribs.cc: Include cgraph.h.
            (decl_attributes): Allow late introduction of sym_alias in
            types.
            (create_sym_alias_decl, create_sym_alias_decls): New.
            * attribs.h: Declare them.
            (FOR_EACH_SYM_ALIAS): New macro.
            * cgraph.cc (cgraph_node::create): Create sym_alias decls.
            * varpool.cc (varpool_node::get_create): Create sym_alias
            decls.
            * cgraph.h (symtab_node::remap_sym_alias_target): New.
            * symtab.cc (symtab_node::remap_sym_alias_target): Define.
            * cgraphunit.cc (cgraph_node::analyze): Create alias_target
            node if needed.
            (analyze_functions): Fixup visibility of implicit alias only
            after its node is analyzed.
            * doc/extend.texi (sym_alias): Document for variables,
            functions and types.
    
    for  gcc/ada/ChangeLog
    
            * doc/gnat_rm/interfacing_to_other_languages.rst: Mention
            attribute sym_alias to give RTTI symbols mnemonic names.
            * doc/gnat_ugn/the_gnat_compilation_model.rst: Mention
            aliases.  Fix incorrect ref to C1 ctor variant.
    
    for  gcc/c-family/ChangeLog
    
            * c-ada-spec.cc (pp_asm_name): Use first sym_alias if
            available.
            * c-attribs.cc (handle_sym_alias_attribute): New.
            (c_common_attribute_table): Add sym_alias.
            (handle_copy_attribute): Do not copy sym_alias attribute.
    
    for  gcc/c/ChangeLog
    
            * c-decl.cc (duplicate_decls): Remap sym_alias target.
    
    for  gcc/cp/ChangeLog
    
            * class.cc (adjust_clone_attributes): New.
            (copy_fndecl_with_name, build_clone): Call it.
            * cp-tree.h (adjust_clone_attributes): Declare.
            (update_sym_alias_interface): Declare.
            (update_tinfo_sym_alias): Declare.
            * decl.cc (duplicate_decls): Remap sym_alias target.
            Adjust clone attributes.
            (grokfndecl): Tentatively create sym_alias decls after
            adding attributes in e.g. a template member function explicit
            instantiation.
            * decl2.cc (cplus_decl_attributes): Update tinfo sym_alias.
            (copy_interface, update_sym_alias_interface): New.
            (determine_visibility): Update sym_alias interface.
            (tentative_decl_linkage, import_export_decl): Likewise.
            * name-lookup.cc: Include target.h and cgraph.h.
            (push_local_extern_decl_alias): Merge attributes with
            namespace-scoped decl, and drop duplicate sym_alias.
            * optimize.cc (maybe_clone_body): Re-adjust attributes after
            cloning them.  Update sym_alias interface.
            * rtti.cc: Include attribs.h and cgraph.h.
            (get_tinfo_decl): Copy sym_alias attributes from type to tinfo
            decl.  Create sym_alias decls.
            (update_tinfo_sym_alias): New.
    
    for  gcc/testsuite/ChangeLog
    
            * c-c++-common/goacc/declare-1.c: Adjust.
            * c-c++-common/goacc/declare-2.c: Adjust.
            * c-c++-common/torture/attr-sym-alias-1.c: New.
            * c-c++-common/torture/attr-sym-alias-2.c: New.
            * c-c++-common/torture/attr-sym-alias-3.c: New.
            * c-c++-common/torture/attr-sym-alias-4.c: New.
            * c-c++-common/torture/attr-sym-alias-5.c: New.
            * g++.dg/torture/attr-sym-alias-1.C: New.
            * g++.dg/torture/attr-sym-alias-2.C: New.
            * g++.dg/torture/attr-sym-alias-3.C: New.
            * g++.dg/torture/attr-sym-alias-4.C: New.
            * g++.dg/torture/attr-sym-alias-5.C: New.

Diff:
---
 .../doc/gnat_rm/interfacing_to_other_languages.rst |  6 ++
 .../doc/gnat_ugn/the_gnat_compilation_model.rst    | 10 ++-
 gcc/attribs.cc                                     | 66 +++++++++++++++-
 gcc/attribs.h                                      |  7 ++
 gcc/c-family/c-ada-spec.cc                         |  7 ++
 gcc/c-family/c-attribs.cc                          | 33 +++++++-
 gcc/c/c-decl.cc                                    |  2 +
 gcc/cgraph.cc                                      |  2 +
 gcc/cgraph.h                                       |  4 +
 gcc/cgraphunit.cc                                  |  2 +-
 gcc/cp/class.cc                                    | 64 +++++++++++++++
 gcc/cp/cp-tree.h                                   |  4 +
 gcc/cp/decl.cc                                     |  4 +
 gcc/cp/decl2.cc                                    | 50 ++++++++++++
 gcc/cp/name-lookup.cc                              | 11 +++
 gcc/cp/optimize.cc                                 |  3 +
 gcc/cp/rtti.cc                                     | 71 ++++++++++++++++-
 gcc/doc/extend.texi                                | 56 ++++++++++++++
 gcc/symtab.cc                                      | 38 +++++++++
 gcc/testsuite/c-c++-common/goacc/declare-1.c       |  6 +-
 gcc/testsuite/c-c++-common/goacc/declare-2.c       | 14 ++--
 .../c-c++-common/torture/attr-sym-alias-1.c        | 39 ++++++++++
 .../c-c++-common/torture/attr-sym-alias-2.c        | 90 ++++++++++++++++++++++
 .../c-c++-common/torture/attr-sym-alias-3.c        | 41 ++++++++++
 .../c-c++-common/torture/attr-sym-alias-4.c        | 28 +++++++
 .../c-c++-common/torture/attr-sym-alias-5.c        | 54 +++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C    | 72 +++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C    | 26 +++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C    | 83 ++++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C    | 28 +++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C    | 14 ++++
 gcc/varpool.cc                                     |  3 +
 32 files changed, 919 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 ad0be511d48..b8de16ebf7c 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 ``sym_alias`` 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 fd15459203a..20045698962 100644
--- a/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
+++ b/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
@@ -4274,6 +4274,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__ ((__sym_alias__ ("Ctor_For_Animal")))
         Animal() {Age_Count = 0;};
       private:
         int Age_Count;
@@ -4306,6 +4307,7 @@ both Carnivore and Domestic, that is:
         virtual int  Number_Of_Teeth ();
         virtual void Set_Owner (char* Name);
 
+        __attribute__ ((__sym_alias__ ("Ctor_For_Dog"))) // mnemonic alias
         Dog(); // Constructor
       private:
         int  Tooth_Count;
@@ -4344,7 +4346,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;
@@ -4360,7 +4363,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,7 +4385,8 @@ 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.
+mangled name (or an alias) 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.cc b/gcc/attribs.cc
index ff4b638c25c..4f02d765d28 100644
--- a/gcc/attribs.cc
+++ b/gcc/attribs.cc
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "target.h"
 #include "tree.h"
+#include "cgraph.h"
 #include "stringpool.h"
 #include "diagnostic-core.h"
 #include "attribs.h"
@@ -830,7 +831,8 @@ decl_attributes (tree *node, tree attributes, int flags,
 
       if (TYPE_P (*anode)
 	  && (flags & (int) ATTR_FLAG_TYPE_IN_PLACE)
-	  && COMPLETE_TYPE_P (*anode))
+	  && COMPLETE_TYPE_P (*anode)
+	  && !is_attribute_p ("sym_alias", name))
 	{
 	  warning (OPT_Wattributes, "type attributes ignored after type is already defined");
 	  continue;
@@ -2672,6 +2674,68 @@ attr_access::array_as_string (tree type) const
   return typstr;
 }
 
+/* Create a sym attribute for DECL to be visible with linkage name ID.  */
+
+tree
+create_sym_alias_decl (tree decl, tree id)
+{
+  const char *attr_str = "sym_alias";
+
+  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;
+
+      tree attr_name = get_identifier (attr_str);
+      error_at (DECL_SOURCE_LOCATION (decl),
+		"duplicate symbol name %qE in %qE attribute of %qD",
+		id, attr_name, decl);
+      inform (DECL_SOURCE_LOCATION (sym_node->decl),
+	      "already used by %qD", sym_node->decl);
+    }
+
+  tree clone = copy_node (decl);
+  DECL_ATTRIBUTES (clone) = remove_attribute (attr_str,
+					      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_extra_name_alias (clone, decl);
+    varpool_node::create_alias (clone, decl);
+  else
+    cgraph_node::create_same_body_alias (clone, decl);
+    // cgraph_node::create_alias (clone, decl);
+
+  return clone;
+}
+
+/* Create decls for all sym aliases requested in DECL's attributes.  */
+
+void
+create_sym_alias_decls (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl)
+      || DECL_ABSTRACT_P (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      create_sym_alias_decl (decl, id);
+    }
+}
+
 #if CHECKING_P
 
 namespace selftest
diff --git a/gcc/attribs.h b/gcc/attribs.h
index fdeebff1cd9..b7a67f73550 100644
--- a/gcc/attribs.h
+++ b/gcc/attribs.h
@@ -404,4 +404,11 @@ extern void init_attr_rdwr_indices (rdwr_map *, tree);
 extern attr_access *get_parm_access (rdwr_map &, tree,
 				     tree = current_function_decl);
 
+extern tree create_sym_alias_decl (tree, tree);
+extern void create_sym_alias_decls (tree);
+
+#define FOR_EACH_SYM_ALIAS(sym, attrs)					\
+  for (tree sym = lookup_attribute ("sym_alias", (attrs));		\
+       sym; sym = lookup_attribute ("sym_alias", TREE_CHAIN (sym)))
+
 #endif // GCC_ATTRIBS_H
diff --git a/gcc/c-family/c-ada-spec.cc b/gcc/c-family/c-ada-spec.cc
index 050994d8416..5042b9cfecd 100644
--- a/gcc/c-family/c-ada-spec.cc
+++ b/gcc/c-family/c-ada-spec.cc
@@ -1431,6 +1431,13 @@ pp_ada_tree_identifier (pretty_printer *buffer, tree node, tree type,
 static void
 pp_asm_name (pretty_printer *buffer, tree t)
 {
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (t))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.cc b/gcc/c-family/c-attribs.cc
index 854e987dc79..2593fde1fe9 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -110,7 +110,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_sym_alias_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,
@@ -392,6 +393,8 @@ const struct attribute_spec c_common_gnu_attributes[] =
 			      handle_alias_attribute, NULL },
   { "weakref",                0, 1, true,  false, false, false,
 			      handle_weakref_attribute, NULL },
+  { "sym_alias",              1, 1, false,  false, false, false,
+			      handle_sym_alias_attribute, NULL },
   { "no_instrument_function", 0, 0, true,  false, false, false,
 			      handle_no_instrument_function_attribute,
 			      NULL },
@@ -3092,7 +3095,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
@@ -3102,7 +3105,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
@@ -3112,6 +3115,29 @@ handle_alias_attribute (tree *node, tree name, tree args,
   return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs);
 }
 
+/* Handle a "sym_alias" attribute; arguments as in struct
+   attribute_spec.handler.  */
+
+static tree
+handle_sym_alias_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 (decl_in_symtab_p (node))
+    *no_add_attrs = false;
+  else if (TYPE_P (node) && c_dialect_cxx ())
+    *no_add_attrs = false;
+  else
+    return error_mark_node;
+
+  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.  */
 
@@ -3245,6 +3271,7 @@ handle_copy_attribute (tree *node, tree name, tree args,
 	      || is_attribute_p ("visibility", atname)
 	      || is_attribute_p ("weak", atname)
 	      || is_attribute_p ("weakref", atname)
+	      || is_attribute_p ("sym_alias", atname)
 	      || is_attribute_p ("target_clones", atname))
 	    continue;
 
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 92c83e1bf10..03ff9c1e425 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -3117,6 +3117,8 @@ duplicate_decls (tree newdecl, tree olddecl)
 
   merge_decls (newdecl, olddecl, newtype, oldtype);
 
+  symtab_node::remap_sym_alias_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.cc b/gcc/cgraph.cc
index f93259a8c70..0bef2179e89 100644
--- a/gcc/cgraph.cc
+++ b/gcc/cgraph.cc
@@ -523,6 +523,8 @@ cgraph_node::create (tree decl)
   node->register_symbol ();
   maybe_record_nested_function (node);
 
+  create_sym_alias_decls (decl);
+
   return node;
 }
 
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index 2b320557616..6d61e327fb6 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -327,6 +327,10 @@ public:
   /* Return DECL that alias is aliasing.  */
   inline tree get_alias_target_tree ();
 
+  /* Remap sym alias nodes recorded as aliasing REPLACED to alias REPLACEMENT
+     instead.  */
+  static void remap_sym_alias_target (tree replaced, tree replacement);
+
   /* Set section for symbol and its aliases.  */
   void set_section (const char *section);
 
diff --git a/gcc/cgraphunit.cc b/gcc/cgraphunit.cc
index 9a550a5cce6..a66db795c78 100644
--- a/gcc/cgraphunit.cc
+++ b/gcc/cgraphunit.cc
@@ -1176,7 +1176,7 @@ analyze_functions (bool first_time)
      C++ FE is confused about the COMDAT groups being right.  */
   if (symtab->cpp_implicit_aliases_done)
     FOR_EACH_SYMBOL (node)
-      if (node->cpp_implicit_alias)
+      if (node->cpp_implicit_alias && node->analyzed)
 	  node->fixup_same_cpp_alias_visibility (node->get_alias_target ());
   build_type_inheritance_graph ();
 
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index 6fdb56abfb9..fa8fc63db82 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -4896,6 +4896,68 @@ check_methods (tree t)
     }
 }
 
+/* Adjust sym alias name for CLONE, cloned from FN and named NAME,
+   if it is a cdtor, and drop the sym alias from other clones.  */
+
+void
+adjust_clone_attributes (tree fn, tree clone, tree name, bool skip_copy_p)
+{
+  if (IDENTIFIER_CDTOR_P (name))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	{
+	  found = true;
+	  break;
+	}
+
+      if (found
+	  && (name == complete_ctor_identifier
+	      || name == complete_dtor_identifier))
+	{
+	  /* Reuse the sym alias decls created for the primary cdtor
+	     decl.  */
+	  symtab_node::remap_sym_alias_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);
+
+	  if (!skip_copy_p)
+	    DECL_ATTRIBUTES (clone) = copy_list (DECL_ATTRIBUTES (clone));
+
+	  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	    {
+	      /* We need to copy this even with skip_copy_p, because
+		 even then copying was shallow.  */
+	      TREE_VALUE (sym) = copy_list (TREE_VALUE (sym));
+	      /* Append suf to the sym alias name.  */
+	      tree str = TREE_VALUE (TREE_VALUE (sym));
+	      char *symname = concat (TREE_STRING_POINTER (str), suf, NULL);
+	      str = build_string (TREE_STRING_LENGTH (str) + xlen, symname);
+	      TREE_VALUE (TREE_VALUE (sym)) = str;
+	      free (symname);
+	    }
+
+	  if (symtab_node::get (clone))
+	    create_sym_alias_decls (clone);
+	}
+    }
+  else
+    DECL_ATTRIBUTES (clone)
+      = remove_attribute ("sym_alias", DECL_ATTRIBUTES (clone));
+}
+
 /* FN is constructor, destructor or operator function.  Clone the
    declaration to create a NAME'd variant.  NEED_VTT_PARM_P and
    OMIT_INHERITED_PARMS_P are relevant if it's a cdtor.  */
@@ -5065,6 +5127,8 @@ build_clone (tree fn, tree name, bool need_vtt_parm_p,
   DECL_CHAIN (clone) = DECL_CHAIN (fn);
   DECL_CHAIN (fn) = clone;
 
+  adjust_clone_attributes (fn, clone, name);
+
   return clone;
 }
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index fef80816c5f..607a8659063 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5806,6 +5806,8 @@ struct GTY((for_user)) spec_entry
 
 extern int current_class_depth;
 
+void adjust_clone_attributes (tree fn, tree clone, tree name, bool = false);
+
 /* in decl.cc */
 
 /* An array of static vars & fns.  */
@@ -7043,6 +7045,7 @@ extern void do_push_parm_decls			(tree, tree, tree *);
 extern tree do_aggregate_paren_init		(tree, tree);
 
 /* in decl2.cc */
+extern void update_sym_alias_interface		(tree);
 extern void record_mangling			(tree, bool);
 extern void overwrite_mangling			(tree, tree);
 extern void note_mangling_alias			(tree, tree);
@@ -7617,6 +7620,7 @@ extern bool emit_tinfo_decl			(tree);
 extern unsigned get_pseudo_tinfo_index		(tree);
 extern tree get_pseudo_tinfo_type		(unsigned);
 extern tree build_if_nonnull			(tree, tree, tsubst_flags_t);
+extern void update_tinfo_sym_alias		(tree);
 
 /* in search.cc */
 extern tree get_parent_with_private_access 	(tree decl, tree binfo);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 4b685270097..cc7f7a99487 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -3277,6 +3277,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
 	      && TREE_STATIC (olddecl))))
     make_decl_rtl (olddecl);
 
+  symtab_node::remap_sym_alias_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.
@@ -3300,6 +3302,7 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
       FOR_EACH_CLONE (clone, olddecl)
 	{
 	  DECL_ATTRIBUTES (clone) = DECL_ATTRIBUTES (olddecl);
+	  adjust_clone_attributes (olddecl, clone, DECL_NAME (clone));
 	  DECL_PRESERVE_P (clone) |= DECL_PRESERVE_P (olddecl);
 	}
     }
@@ -10838,6 +10841,7 @@ grokfndecl (tree ctype,
     {
       cplus_decl_attributes (&decl, *attrlist, 0);
       *attrlist = NULL_TREE;
+      create_sym_alias_decls (decl);
     }
 
   if (DECL_HAS_CONTRACTS_P (decl))
diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
index bee84879023..6d72ad8ff43 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -1846,6 +1846,9 @@ cplus_decl_attributes (tree *decl, tree attributes, int flags)
   if (late_attrs)
     save_template_attributes (late_attrs, decl, flags);
 
+  if (TYPE_P (*decl) && attributes)
+    update_tinfo_sym_alias (*decl);
+
   /* Propagate deprecation out to the template.  */
   if (TREE_DEPRECATED (*decl))
     if (tree ti = get_template_info (*decl))
@@ -2202,6 +2205,47 @@ 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 sym aliases.  */
+
+void
+update_sym_alias_interface (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.  */
 
@@ -3009,6 +3053,8 @@ determine_visibility (tree decl)
        translation unit, we can make the type internal.  */
     constrain_visibility (decl, VISIBILITY_ANON, false);
 
+  update_sym_alias_interface (decl);
+
   /* If visibility changed and DECL already has DECL_RTL, ensure
      symbol flags are updated.  */
   if ((DECL_VISIBILITY (decl) != orig_visibility
@@ -3271,6 +3317,8 @@ tentative_decl_linkage (tree decl)
       else if (VAR_P (decl))
 	maybe_commonize_var (decl);
     }
+
+  update_sym_alias_interface (decl);
 }
 
 /* DECL is a FUNCTION_DECL or VAR_DECL.  If the object file linkage
@@ -3505,6 +3553,8 @@ import_export_decl (tree decl)
     }
 
   DECL_INTERFACE_KNOWN (decl) = 1;
+
+  update_sym_alias_interface (decl);
 }
 
 /* Return an expression that performs the destruction of DECL, which
diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc
index 76f1d44610a..3a19a571256 100644
--- a/gcc/cp/name-lookup.cc
+++ b/gcc/cp/name-lookup.cc
@@ -22,9 +22,11 @@ along with GCC; see the file COPYING3.  If not see
 #define INCLUDE_MEMORY
 #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"
@@ -3675,6 +3677,15 @@ push_local_extern_decl_alias (tree decl)
 	  /* Adjust visibility.  */
 	  determine_visibility (alias);
 	}
+      else if (DECL_P (alias))
+	DECL_ATTRIBUTES (alias)
+	  = targetm.merge_decl_attributes (alias, decl);
+      if (DECL_P (alias))
+	{
+	  symtab_node::remap_sym_alias_target (decl, alias);
+	  DECL_ATTRIBUTES (decl)
+	    = remove_attribute ("sym_alias", DECL_ATTRIBUTES (alias));
+	}
     }
 
   retrofit_lang_decl (decl);
diff --git a/gcc/cp/optimize.cc b/gcc/cp/optimize.cc
index 9e8926e4cc6..4a2e8cb4354 100644
--- a/gcc/cp/optimize.cc
+++ b/gcc/cp/optimize.cc
@@ -528,9 +528,12 @@ maybe_clone_body (tree fn)
       DECL_VISIBILITY_SPECIFIED (clone) = DECL_VISIBILITY_SPECIFIED (fn);
       DECL_DLLIMPORT_P (clone) = DECL_DLLIMPORT_P (fn);
       DECL_ATTRIBUTES (clone) = clone_attrs (DECL_ATTRIBUTES (fn));
+      adjust_clone_attributes (fn, clone, DECL_NAME (clone), true);
       DECL_DISREGARD_INLINE_LIMITS (clone) = DECL_DISREGARD_INLINE_LIMITS (fn);
       set_decl_section_name (clone, fn);
 
+      update_sym_alias_interface (clone);
+
       /* Adjust the parameter names and locations.  */
       parm = DECL_ARGUMENTS (fn);
       clone_parm = DECL_ARGUMENTS (clone);
diff --git a/gcc/cp/rtti.cc b/gcc/cp/rtti.cc
index 7878929c246..4bb7b8c8c6e 100644
--- a/gcc/cp/rtti.cc
+++ b/gcc/cp/rtti.cc
@@ -28,8 +28,10 @@ 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"
+#include "cgraph.h"
 
 /* C++ returns type information to the user in struct type_info
    objects. We also use type information to implement dynamic_cast and
@@ -479,8 +481,13 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
 	  = build_tree_list (get_identifier ("non overlapping"),
 			     NULL_TREE);
       else
+	/* Share the non overlapping attribute, without assuming it's
+	   the only attribute, but assuming it's the last if it's
+	   present.  There may be sym aliases too, and those are not
+	   to be shared.  */
 	DECL_ATTRIBUTES (d)
-	  = DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]);
+	  = lookup_attribute ("non overlapping",
+			      DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]));
 
       /* Mark the variable as undefined -- but remember that we can
 	 define it later if we need to do so.  */
@@ -492,6 +499,16 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
       if (CLASS_TYPE_P (type))
 	CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type)) = d;
 
+      /* Copy sym alias attributes from the type to the rtti obj decl.  */
+      tree *attrs = &DECL_ATTRIBUTES (d);
+      FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+	{
+	  tree attr = tree_cons (TREE_PURPOSE (sym), TREE_VALUE (sym), *attrs);
+	  *attrs = attr;
+	  attrs = &TREE_CHAIN (attr);
+	}
+      create_sym_alias_decls (d);
+
       /* Add decl to the global array of tinfo decls.  */
       vec_safe_push (unemitted_tinfo_decls, d);
     }
@@ -499,6 +516,58 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
   return d;
 }
 
+/* After modifying the attributes of TYPE, check whether tinfo was
+   already created and, if so, add to it any sym alias attributes
+   that were not already present.  */
+
+void
+update_tinfo_sym_alias (tree type)
+{
+  if (!TYPE_SIZE (type) || !CLASS_TYPE_P (type))
+    return;
+
+  tree d = CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type));
+  if (!d)
+    return;
+
+  bool first = true;
+  symtab_node *node = NULL;
+
+  tree *attrs = &DECL_ATTRIBUTES (d);
+  FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (d_sym, *attrs)
+	if (TREE_VALUE (sym) == TREE_VALUE (d_sym))
+	  {
+	    found = true;
+	    break;
+	  }
+
+      if (found)
+	continue;
+
+      tree attr = tree_cons (TREE_PURPOSE (sym),
+			     TREE_VALUE (sym),
+			     *attrs);
+      *attrs = attr;
+      attrs = &TREE_CHAIN (attr);
+
+      if (first)
+	{
+	  first = false;
+	  node = symtab_node::get (d);
+	}
+
+      if (!node)
+	continue;
+
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+      create_sym_alias_decl (d, id);
+    }
+}
+
 /* Return the type_info object for TYPE.  */
 
 tree
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index af782b3f228..7f4414410b0 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -4142,6 +4142,43 @@ Function Attributes}, @ref{PowerPC Function Attributes},
 @ref{Nios II Function Attributes}, and @ref{S/390 Function Attributes}
 for details.
 
+@cindex @code{sym_alias} function attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} attribute causes @var{name} to be output as an
+alias to the function, with the same linkage and visibility as the
+function, when the function definition is output, provided that the
+function could be an alias target.  For instance,
+
+@smallexample
+void f (uint64_t) __attribute__ ((__sym_alias__ ("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},
+with the same linkage and visibility.  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.
+
+Aliases introduced with this attribute, such as @samp{f_u64} in the
+example above, are assembly symbol names: they do not undergo C++ name
+mangling, and are not made visible in any scope in the source language.
+They can, however, can be named as alias targets.
+
+This attribute requires assembler and object file support for aliases,
+and may not be available on all targets.
+
 @cindex @code{symver} function attribute
 @item symver ("@var{name2}@@@var{nodename}")
 On ELF targets this attribute creates a symbol version.  The @var{name2} part
@@ -8069,6 +8106,10 @@ will be placed in new, unique sections.
 
 This additional functionality requires Binutils version 2.36 or later.
 
+@cindex @code{sym_alias} variable attribute
+@item sym_alias ("@var{name}")
+See @pxref{Common Function Attributes}.
+
 @cindex @code{uninitialized} variable attribute
 @item uninitialized
 This attribute, attached to a variable with automatic storage, means that
@@ -9164,6 +9205,21 @@ is not supported; that is to say, if a given scalar object can be accessed
 through distinct types that assign a different storage order to it, then the
 behavior is undefined.
 
+@cindex @code{sym_alias} type attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} type 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__ ((__sym_alias__ ("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.
+
 @cindex @code{transparent_union} type attribute
 @item transparent_union
 
diff --git a/gcc/symtab.cc b/gcc/symtab.cc
index 0470509a98d..43ba88d7939 100644
--- a/gcc/symtab.cc
+++ b/gcc/symtab.cc
@@ -1943,6 +1943,44 @@ symtab_node::noninterposable_alias (symtab_node *node, void *data)
   return false;
 }
 
+/* Remap sym alias nodes recorded as aliasing REPLACED to alias
+   REPLACEMENT instead.  */
+
+void
+symtab_node::remap_sym_alias_target (tree replaced, tree replacement)
+{
+  if (!decl_in_symtab_p (replacement)
+      || !symtab_node::get (replacement))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (replaced))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      symtab_node *sym_node = symtab_node::get_for_asmname (id);
+
+      if (!sym_node)
+	{
+	  create_sym_alias_decl (replacement, id);
+	  continue;
+	}
+
+      gcc_assert (!sym_node->analyzed);
+      if (sym_node->alias_target != replaced)
+	continue;
+
+      // sym_node->definition = 0;
+
+      if (VAR_P (replaced))
+	// varpool_node::create_extra_name_alias (sym_node->decl, replacement);
+	varpool_node::create_alias (sym_node->decl, replacement);
+      else
+	cgraph_node::create_same_body_alias (sym_node->decl, replacement);
+	// 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/goacc/declare-1.c b/gcc/testsuite/c-c++-common/goacc/declare-1.c
index 46ee01b6759..f2842893318 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-1.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-1.c
@@ -113,11 +113,11 @@ f_2 (void)
   int va3;
 #pragma acc declare device_resident(va3)
 
-#ifndef __cplusplus
+#if 0
   /* TODO PR90868
 
-     C: "error: variable '[...]' used more than once with '#pragma acc declare'".  */
-#else
+     "error: variable '[...]' used more than once with '#pragma acc declare'".  */
+
   extern int ve0;
 #pragma acc declare create(ve0)
 
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-2.c b/gcc/testsuite/c-c++-common/goacc/declare-2.c
index e2e22be57e9..aec59b69754 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-2.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-2.c
@@ -137,25 +137,25 @@ void
 f_pr90868_2 (void)
 {
   extern int we0;
-#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" } */
 
   extern int we1;
-#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" } */
 
   extern int *we2;
-#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" } */
 
   extern int we3;
-#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" } */
 
   extern int we4;
-#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" } */
 
   extern int we5;
-#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" } */
  
   extern int we6;
-#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" } */
 }
 
 
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
new file mode 100644
index 00000000000..61af50cb552
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
@@ -0,0 +1,39 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+int var_a = 1;
+
+void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+
+void
+foo_a ()
+{
+}
+
+
+int var_b;
+extern int var_b __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+
+void
+foo_b ()
+{
+}
+
+void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+
+
+int var_c __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+
+void __attribute__ ((__sym_alias__ ("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-sym-alias-2.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
new file mode 100644
index 00000000000..67dcd4fd7e1
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
@@ -0,0 +1,90 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+struct s
+{
+  int mem __attribute__ ((__sym_alias__ ("S_MEM"))); /* { dg-warning "attribute ignored" } */
+};
+
+void foo () {
+  extern void bar () __attribute__ ((__sym_alias__ ("FOO_BAR"))); /* ok */
+  int i __attribute__ ((sym_alias ("FOO_i"))); /* { dg-warning "ignored" } */
+  /* ??? X cannot be an alias target; should this be flagged? ... */
+  static int x __attribute__ ((sym_alias ("FOO_x")));
+  static int sx __attribute__ ((alias ("FOO_x"))); /* { dg-warning "ignored" } */
+  extern int xx __attribute__ ((alias ("FOO_x"))); /* { dg-warning "ignored" } */
+}
+
+/* ??? ... or should XR be accepted?  */
+extern int xr __attribute__ ((alias ("FOO_x"))); /* { dg-error "undefined" "" { xfail c++ } } */
+int dr __attribute__ ((alias ("FOO_x"))); /* { dg-error "defined both" } */
+
+
+int j __attribute__ ((sym_alias ("J_var")));
+void __attribute__ ((alias ("J_var")))
+j_fn (); /* { dg-error "between function and variable" } */
+
+
+void __attribute__ ((sym_alias ("K_fn")))
+k () {
+}
+extern int __attribute__ ((alias ("K_fn")))
+k_var; /* { dg-error "between function and variable" } */
+
+
+int l __attribute__ ((sym_alias ("L_fn")));
+
+/* These should be detected and reported, not because the names clash at the
+   source level, but because the asm symbols do.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+void
+L_fn () /* { dg-error "duplicate" "" { xfail *-*-* } } */
+{
+}
+
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __attribute__ ((sym_alias ("M_var")))
+m ()
+{
+}
+
+int M_var; /* { dg-error "duplicate" "" { xfail *-*-* } } */
+
+
+void __attribute__ ((sym_alias ("N_sym")))
+n_fn ()
+{
+}
+
+int __attribute__ ((sym_alias ("N_sym")))
+n_var; /* { dg-error "duplicate" } */
+
+
+int __attribute__ ((sym_alias ("O_sym")))
+o_var;
+
+void __attribute__ ((sym_alias ("O_sym")))
+o_fn () /* { dg-error "duplicate" } */
+{
+}
+
+int __attribute__ ((sym_alias ("P_sym")))
+p_var1;
+
+int __attribute__ ((sym_alias ("P_sym")))
+p_var2; /* { dg-error "duplicate" } */
+
+void __attribute__ ((sym_alias ("Q_sym")))
+q_fn1 ()
+{
+}
+
+void __attribute__ ((sym_alias ("Q_sym")))
+q_fn2 () /* { dg-error "duplicate" } */
+{
+}
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
new file mode 100644
index 00000000000..0052485a30a
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
@@ -0,0 +1,41 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int var_a = 1;
+
+void
+foo_a ()
+{
+  extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+}
+
+#if 0 // __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__ ((__sym_alias__ ("FOOVAR_B")));
+}
+
+int var_b;
+
+void __attribute__ ((__sym_alias__ ("FOOBAR_C")))
+foo_c ()
+{
+  void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+  /* Another sym for var_b.  */
+  extern int var_b __attribute__ ((__sym_alias__ ("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-sym-alias-4.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
new file mode 100644
index 00000000000..2beafa46379
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-require-alias "" } */
+
+int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A"))) = 42;
+
+int __attribute__ ((__sym_alias__ ("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/c-c++-common/torture/attr-sym-alias-5.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
new file mode 100644
index 00000000000..bee90136daf
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
@@ -0,0 +1,54 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+/* { dg-require-visibility "" } */
+
+int __attribute__ ((sym_alias ("A_hid"), visibility ("hidden"))) a;
+
+extern int __attribute__ ((sym_alias ("B_prt"))) b;
+
+int b __attribute__ ((visibility ("protected")));
+
+extern int __attribute__ ((sym_alias ("C_ntr"))) c;
+
+extern int c __attribute__ ((visibility ("internal")));
+
+int c;
+
+static int d __attribute__ ((sym_alias ("D_stt")));
+
+
+extern void __attribute__ ((sym_alias ("F_hid"), visibility ("hidden")))
+f ();
+
+void
+f () {
+}
+
+extern void __attribute__ ((sym_alias ("G_prt")))
+g ();
+
+void __attribute__ ((visibility ("protected")))
+g () {
+}
+
+extern void __attribute__ ((sym_alias ("H_ntr")))
+h ();
+
+void __attribute__ ((visibility ("internal")))
+h ();
+
+void h () {
+}
+
+static void __attribute__ ((sym_alias ("I_stt")))
+i () {
+}
+
+/* { dg-final { scan-assembler {hidden[^\n]*A_hid} } } */
+/* { dg-final { scan-assembler {protected[^\n]*B_prt} } } */
+/* { dg-final { scan-assembler {internal[^\n]*C_ntr} } } */
+/* { dg-final { scan-assembler-not {glob[^\n]*D_stt} } } */
+/* { dg-final { scan-assembler {hidden[^\n]*F_hid} } } */
+/* { dg-final { scan-assembler {protected[^\n]*G_prt} } } */
+/* { dg-final { scan-assembler {internal[^\n]*H_ntr} } } */
+/* { dg-final { scan-assembler-not {glob[^\n]*I_stt} } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
new file mode 100644
index 00000000000..579eda1a473
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
@@ -0,0 +1,72 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+class __attribute__ ((__sym_alias__ ("FOOCLS_A"),
+		      __sym_alias__ ("FOOCLS_A_Dupe"))) foo {
+  static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  void __attribute__ ((__sym_alias__ ("FOOBAR_A"))) bar ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+int foo::var = 1;
+
+foo::foo () {}
+
+void foo::bar () {}
+
+namespace b {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_B"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_B"))) foo ();
+    void __attribute__ ((__sym_alias__ ("FOOBAR_B"))) bar () {}
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_B"))) ~foo() {}
+  };
+
+  int foo::var = 2;
+
+  foo::foo () {
+    void (foo::*pbar)() = &foo::bar;
+  }
+}
+
+namespace c {
+  namespace cd {
+    class __attribute__ ((__sym_alias__ ("FOOCLS_C"))) foo {
+      static int var __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+      __attribute__ ((__sym_alias__ ("FOOCTR_C"))) foo () {
+	void (foo::*pbar)() = &foo::bar;
+      }
+      void __attribute__ ((__sym_alias__ ("FOOBAR_C"))) bar () {}
+      virtual __attribute__ ((__sym_alias__ ("FOODTR_C"))) ~foo() {}
+    };
+
+    int foo::var = 3;
+  }
+}
+
+/* { dg-final { scan-assembler "FOOCLS_A" } } */
+/* { dg-final { scan-assembler "FOOCLS_A_Dupe" } } */
+/* { 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-sym-alias-2.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
new file mode 100644
index 00000000000..6facfcc2cd9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+namespace {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_A"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo ();
+    void __attribute__ ((__sym_alias__ ("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-sym-alias-3.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
new file mode 100644
index 00000000000..42c7288ad4a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
@@ -0,0 +1,83 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+// sym can be applied to template function explicit instantiations.
+
+template <typename T>
+void
+fn(T) {
+};
+
+template void __attribute__ ((__sym_alias__ ("FOOFUN_UINT"))) fn<>(unsigned int);
+template void __attribute__ ((__sym_alias__ ("FOOFUN_LONG"))) fn<>(long);
+
+template<> void __attribute__ ((__sym_alias__ ("FOOFUN_CHAR"))) fn<>(char) {}
+
+
+template <typename T = void>
+struct
+foo {
+  virtual ~foo() {}
+
+  virtual void virtfun() {}
+
+  static void stfun() {}
+  void inlfun() {}
+};
+
+// Explicitly instantiate members before the enclosing class.
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_CHAR_VIRT"))) foo<char>::virtfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_CHAR_TI"))) foo<char>;
+
+// Though they're only output if the enclosing class is.
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_LONG_VIRT"))) foo<long>::virtfun();
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_LONG_TI_X"))) foo<long>;
+
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_VOID_ST"))) foo<void>::stfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_VOID_TI"))) foo<>;
+
+
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_X"))) foo<short>;
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_ST"))) foo<short>::stfun();
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_INL"))) foo<short>::inlfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_D"))) foo<short>;
+
+// Explicit specializations work too.
+
+template <>
+struct  __attribute__ ((__sym_alias__ ("FOOCLS_INT_TI")))
+foo<int>
+{
+  virtual ~foo() {}
+  virtual void __attribute__ ((__sym_alias__ ("FOOCLS_INT_VIRT"))) virtfun() {}
+};
+
+/* { dg-final { scan-assembler "FOOFUN_UINT" } } */
+/* { dg-final { scan-assembler "FOOFUN_LONG" } } */
+/* { dg-final { scan-assembler "FOOFUN_CHAR" } } */
+
+/* { dg-final { scan-assembler "FOOCLS_VOID_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_VOID_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_X" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_INL" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_D" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_TI_X" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_VIRT" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
new file mode 100644
index 00000000000..59b779de35d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+template <typename T = void>
+class
+__attribute__ ((__sym_alias__ ("FOOCLS")))
+foo // { dg-error "duplicate|already" }
+{
+  virtual ~foo() {}
+
+  template <typename U>
+  void
+    __attribute__ ((__sym_alias__ ("FOOTMF")))
+    tmemfun () {} // { dg-error "duplicate|already" }
+};
+
+template <typename T>
+void
+__attribute__ ((__sym_alias__ ("FOOTFN")))
+fn(T) { // { dg-error "duplicate|already" }
+};
+
+template class foo<>;
+template class foo<int>;
+template void foo<>::tmemfun<void>();
+template void foo<int>::tmemfun<void>();
+template void fn<>(int);
+template void fn<>(long);
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
new file mode 100644
index 00000000000..45d59b953c3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
@@ -0,0 +1,14 @@
+/* { dg-do compile { target c++11 } } */
+/* { dg-require-alias "" } */
+
+struct foo {
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+foo::foo () {}
+
+// Make sure the inherited cdtors don't duplicate the syms.
+struct bar : foo {
+  using foo::foo;
+};
diff --git a/gcc/varpool.cc b/gcc/varpool.cc
index e7b51b15e4a..33649906d1d 100644
--- a/gcc/varpool.cc
+++ b/gcc/varpool.cc
@@ -163,6 +163,9 @@ varpool_node::get_create (tree decl)
     }
 
   node->register_symbol ();
+
+  create_sym_alias_decls (decl);
+
   return node;
 }

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [gcc(refs/users/aoliva/heads/testme)] Introduce attribute sym_alias
@ 2023-12-05 19:30 Alexandre Oliva
  0 siblings, 0 replies; 33+ messages in thread
From: Alexandre Oliva @ 2023-12-05 19:30 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:8dfe6008ed347b85138b1e4a73ba03c699325e80

commit 8dfe6008ed347b85138b1e4a73ba03c699325e80
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Fri Dec 1 14:31:46 2023 -0300

    Introduce attribute sym_alias
    
    This patch introduces an attribute to add extra asm names (aliases)
    for a decl 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 class types, namespace-scoped
    variables and functions, static data members, member functions,
    explicit instantiations and specializations of template functions,
    members and classes.
    
    When applied to constructors or destructor, additional sym aliases
    with _Base and _Del suffixes are defined for variants other than
    complete-object ones.  This changes the assumption that clones always
    carry the same attributes as their abstract declarations, so there is
    now a function to adjust them.
    
    C++ also had a bug in which attributes from local extern declarations
    failed to be propagated to a preexisting corresponding
    namespace-scoped decl.  I've fixed that, and adjusted acc tests that
    distinguished between C and C++ in this regard.
    
    Applying the attribute to class types is only valid in C++, and the
    effect is to attach the alias to the RTTI object associated with the
    class type.
    
    for  gcc/ChangeLog
    
            * attribs.cc: Include cgraph.h.
            (decl_attributes): Allow late introduction of sym_alias in
            types.
            (create_sym_alias_decl, create_sym_alias_decls): New.
            * attribs.h: Declare them.
            (FOR_EACH_SYM_ALIAS): New macro.
            * cgraph.cc (cgraph_node::create): Create sym_alias decls.
            * varpool.cc (varpool_node::get_create): Create sym_alias
            decls.
            * cgraph.h (symtab_node::remap_sym_alias_target): New.
            * symtab.cc (symtab_node::remap_sym_alias_target): Define.
            * cgraphunit.cc (cgraph_node::analyze): Create alias_target
            node if needed.
            (analyze_functions): Fixup visibility of implicit alias only
            after its node is analyzed.
            * doc/extend.texi (sym_alias): Document for variables,
            functions and types.
    
    for  gcc/ada/ChangeLog
    
            * doc/gnat_rm/interfacing_to_other_languages.rst: Mention
            attribute sym_alias to give RTTI symbols mnemonic names.
            * doc/gnat_ugn/the_gnat_compilation_model.rst: Mention
            aliases.  Fix incorrect ref to C1 ctor variant.
    
    for  gcc/c-family/ChangeLog
    
            * c-ada-spec.cc (pp_asm_name): Use first sym_alias if
            available.
            * c-attribs.cc (handle_sym_alias_attribute): New.
            (c_common_attribute_table): Add sym_alias.
            (handle_copy_attribute): Do not copy sym_alias attribute.
    
    for  gcc/c/ChangeLog
    
            * c-decl.cc (duplicate_decls): Remap sym_alias target.
    
    for  gcc/cp/ChangeLog
    
            * class.cc (adjust_clone_attributes): New.
            (copy_fndecl_with_name, build_clone): Call it.
            * cp-tree.h (adjust_clone_attributes): Declare.
            (update_sym_alias_interface): Declare.
            (update_tinfo_sym_alias): Declare.
            * decl.cc (duplicate_decls): Remap sym_alias target.
            Adjust clone attributes.
            (grokfndecl): Tentatively create sym_alias decls after
            adding attributes in e.g. a template member function explicit
            instantiation.
            * decl2.cc (cplus_decl_attributes): Update tinfo sym_alias.
            (copy_interface, update_sym_alias_interface): New.
            (determine_visibility): Update sym_alias interface.
            (tentative_decl_linkage, import_export_decl): Likewise.
            * name-lookup.cc: Include target.h and cgraph.h.
            (push_local_extern_decl_alias): Merge attributes with
            namespace-scoped decl, and drop duplicate sym_alias.
            * optimize.cc (maybe_clone_body): Re-adjust attributes after
            cloning them.  Update sym_alias interface.
            * rtti.cc: Include attribs.h and cgraph.h.
            (get_tinfo_decl): Copy sym_alias attributes from type to tinfo
            decl.  Create sym_alias decls.
            (update_tinfo_sym_alias): New.
    
    for  gcc/testsuite/ChangeLog
    
            * c-c++-common/goacc/declare-1.c: Adjust.
            * c-c++-common/goacc/declare-2.c: Adjust.
            * c-c++-common/torture/attr-sym-alias-1.c: New.
            * c-c++-common/torture/attr-sym-alias-2.c: New.
            * c-c++-common/torture/attr-sym-alias-3.c: New.
            * c-c++-common/torture/attr-sym-alias-4.c: New.
            * c-c++-common/torture/attr-sym-alias-5.c: New.
            * g++.dg/torture/attr-sym-alias-1.C: New.
            * g++.dg/torture/attr-sym-alias-2.C: New.
            * g++.dg/torture/attr-sym-alias-3.C: New.
            * g++.dg/torture/attr-sym-alias-4.C: New.
            * g++.dg/torture/attr-sym-alias-5.C: New.

Diff:
---
 .../doc/gnat_rm/interfacing_to_other_languages.rst |  6 ++
 .../doc/gnat_ugn/the_gnat_compilation_model.rst    | 10 ++-
 gcc/attribs.cc                                     | 66 +++++++++++++++-
 gcc/attribs.h                                      |  7 ++
 gcc/c-family/c-ada-spec.cc                         |  7 ++
 gcc/c-family/c-attribs.cc                          | 33 +++++++-
 gcc/c/c-decl.cc                                    |  2 +
 gcc/cgraph.cc                                      |  2 +
 gcc/cgraph.h                                       |  4 +
 gcc/cgraphunit.cc                                  |  2 +-
 gcc/cp/class.cc                                    | 64 +++++++++++++++
 gcc/cp/cp-tree.h                                   |  4 +
 gcc/cp/decl.cc                                     |  4 +
 gcc/cp/decl2.cc                                    | 50 ++++++++++++
 gcc/cp/name-lookup.cc                              | 11 +++
 gcc/cp/optimize.cc                                 |  3 +
 gcc/cp/rtti.cc                                     | 71 ++++++++++++++++-
 gcc/doc/extend.texi                                | 56 ++++++++++++++
 gcc/symtab.cc                                      | 38 +++++++++
 gcc/testsuite/c-c++-common/goacc/declare-1.c       |  6 +-
 gcc/testsuite/c-c++-common/goacc/declare-2.c       | 14 ++--
 .../c-c++-common/torture/attr-sym-alias-1.c        | 39 ++++++++++
 .../c-c++-common/torture/attr-sym-alias-2.c        | 90 ++++++++++++++++++++++
 .../c-c++-common/torture/attr-sym-alias-3.c        | 41 ++++++++++
 .../c-c++-common/torture/attr-sym-alias-4.c        | 28 +++++++
 .../c-c++-common/torture/attr-sym-alias-5.c        | 54 +++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C    | 72 +++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C    | 26 +++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C    | 83 ++++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C    | 28 +++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C    | 14 ++++
 gcc/varpool.cc                                     |  3 +
 32 files changed, 919 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 ad0be511d48..b8de16ebf7c 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 ``sym_alias`` 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 fd15459203a..20045698962 100644
--- a/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
+++ b/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
@@ -4274,6 +4274,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__ ((__sym_alias__ ("Ctor_For_Animal")))
         Animal() {Age_Count = 0;};
       private:
         int Age_Count;
@@ -4306,6 +4307,7 @@ both Carnivore and Domestic, that is:
         virtual int  Number_Of_Teeth ();
         virtual void Set_Owner (char* Name);
 
+        __attribute__ ((__sym_alias__ ("Ctor_For_Dog"))) // mnemonic alias
         Dog(); // Constructor
       private:
         int  Tooth_Count;
@@ -4344,7 +4346,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;
@@ -4360,7 +4363,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,7 +4385,8 @@ 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.
+mangled name (or an alias) 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.cc b/gcc/attribs.cc
index ff4b638c25c..4f02d765d28 100644
--- a/gcc/attribs.cc
+++ b/gcc/attribs.cc
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "target.h"
 #include "tree.h"
+#include "cgraph.h"
 #include "stringpool.h"
 #include "diagnostic-core.h"
 #include "attribs.h"
@@ -830,7 +831,8 @@ decl_attributes (tree *node, tree attributes, int flags,
 
       if (TYPE_P (*anode)
 	  && (flags & (int) ATTR_FLAG_TYPE_IN_PLACE)
-	  && COMPLETE_TYPE_P (*anode))
+	  && COMPLETE_TYPE_P (*anode)
+	  && !is_attribute_p ("sym_alias", name))
 	{
 	  warning (OPT_Wattributes, "type attributes ignored after type is already defined");
 	  continue;
@@ -2672,6 +2674,68 @@ attr_access::array_as_string (tree type) const
   return typstr;
 }
 
+/* Create a sym attribute for DECL to be visible with linkage name ID.  */
+
+tree
+create_sym_alias_decl (tree decl, tree id)
+{
+  const char *attr_str = "sym_alias";
+
+  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;
+
+      tree attr_name = get_identifier (attr_str);
+      error_at (DECL_SOURCE_LOCATION (decl),
+		"duplicate symbol name %qE in %qE attribute of %qD",
+		id, attr_name, decl);
+      inform (DECL_SOURCE_LOCATION (sym_node->decl),
+	      "already used by %qD", sym_node->decl);
+    }
+
+  tree clone = copy_node (decl);
+  DECL_ATTRIBUTES (clone) = remove_attribute (attr_str,
+					      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_extra_name_alias (clone, decl);
+    varpool_node::create_alias (clone, decl);
+  else
+    cgraph_node::create_same_body_alias (clone, decl);
+    // cgraph_node::create_alias (clone, decl);
+
+  return clone;
+}
+
+/* Create decls for all sym aliases requested in DECL's attributes.  */
+
+void
+create_sym_alias_decls (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl)
+      || DECL_ABSTRACT_P (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      create_sym_alias_decl (decl, id);
+    }
+}
+
 #if CHECKING_P
 
 namespace selftest
diff --git a/gcc/attribs.h b/gcc/attribs.h
index fdeebff1cd9..b7a67f73550 100644
--- a/gcc/attribs.h
+++ b/gcc/attribs.h
@@ -404,4 +404,11 @@ extern void init_attr_rdwr_indices (rdwr_map *, tree);
 extern attr_access *get_parm_access (rdwr_map &, tree,
 				     tree = current_function_decl);
 
+extern tree create_sym_alias_decl (tree, tree);
+extern void create_sym_alias_decls (tree);
+
+#define FOR_EACH_SYM_ALIAS(sym, attrs)					\
+  for (tree sym = lookup_attribute ("sym_alias", (attrs));		\
+       sym; sym = lookup_attribute ("sym_alias", TREE_CHAIN (sym)))
+
 #endif // GCC_ATTRIBS_H
diff --git a/gcc/c-family/c-ada-spec.cc b/gcc/c-family/c-ada-spec.cc
index 050994d8416..5042b9cfecd 100644
--- a/gcc/c-family/c-ada-spec.cc
+++ b/gcc/c-family/c-ada-spec.cc
@@ -1431,6 +1431,13 @@ pp_ada_tree_identifier (pretty_printer *buffer, tree node, tree type,
 static void
 pp_asm_name (pretty_printer *buffer, tree t)
 {
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (t))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.cc b/gcc/c-family/c-attribs.cc
index 854e987dc79..2593fde1fe9 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -110,7 +110,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_sym_alias_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,
@@ -392,6 +393,8 @@ const struct attribute_spec c_common_gnu_attributes[] =
 			      handle_alias_attribute, NULL },
   { "weakref",                0, 1, true,  false, false, false,
 			      handle_weakref_attribute, NULL },
+  { "sym_alias",              1, 1, false,  false, false, false,
+			      handle_sym_alias_attribute, NULL },
   { "no_instrument_function", 0, 0, true,  false, false, false,
 			      handle_no_instrument_function_attribute,
 			      NULL },
@@ -3092,7 +3095,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
@@ -3102,7 +3105,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
@@ -3112,6 +3115,29 @@ handle_alias_attribute (tree *node, tree name, tree args,
   return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs);
 }
 
+/* Handle a "sym_alias" attribute; arguments as in struct
+   attribute_spec.handler.  */
+
+static tree
+handle_sym_alias_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 (decl_in_symtab_p (node))
+    *no_add_attrs = false;
+  else if (TYPE_P (node) && c_dialect_cxx ())
+    *no_add_attrs = false;
+  else
+    return error_mark_node;
+
+  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.  */
 
@@ -3245,6 +3271,7 @@ handle_copy_attribute (tree *node, tree name, tree args,
 	      || is_attribute_p ("visibility", atname)
 	      || is_attribute_p ("weak", atname)
 	      || is_attribute_p ("weakref", atname)
+	      || is_attribute_p ("sym_alias", atname)
 	      || is_attribute_p ("target_clones", atname))
 	    continue;
 
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 92c83e1bf10..03ff9c1e425 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -3117,6 +3117,8 @@ duplicate_decls (tree newdecl, tree olddecl)
 
   merge_decls (newdecl, olddecl, newtype, oldtype);
 
+  symtab_node::remap_sym_alias_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.cc b/gcc/cgraph.cc
index f93259a8c70..0bef2179e89 100644
--- a/gcc/cgraph.cc
+++ b/gcc/cgraph.cc
@@ -523,6 +523,8 @@ cgraph_node::create (tree decl)
   node->register_symbol ();
   maybe_record_nested_function (node);
 
+  create_sym_alias_decls (decl);
+
   return node;
 }
 
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index 2b320557616..6d61e327fb6 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -327,6 +327,10 @@ public:
   /* Return DECL that alias is aliasing.  */
   inline tree get_alias_target_tree ();
 
+  /* Remap sym alias nodes recorded as aliasing REPLACED to alias REPLACEMENT
+     instead.  */
+  static void remap_sym_alias_target (tree replaced, tree replacement);
+
   /* Set section for symbol and its aliases.  */
   void set_section (const char *section);
 
diff --git a/gcc/cgraphunit.cc b/gcc/cgraphunit.cc
index 9a550a5cce6..a66db795c78 100644
--- a/gcc/cgraphunit.cc
+++ b/gcc/cgraphunit.cc
@@ -1176,7 +1176,7 @@ analyze_functions (bool first_time)
      C++ FE is confused about the COMDAT groups being right.  */
   if (symtab->cpp_implicit_aliases_done)
     FOR_EACH_SYMBOL (node)
-      if (node->cpp_implicit_alias)
+      if (node->cpp_implicit_alias && node->analyzed)
 	  node->fixup_same_cpp_alias_visibility (node->get_alias_target ());
   build_type_inheritance_graph ();
 
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index 6fdb56abfb9..fa8fc63db82 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -4896,6 +4896,68 @@ check_methods (tree t)
     }
 }
 
+/* Adjust sym alias name for CLONE, cloned from FN and named NAME,
+   if it is a cdtor, and drop the sym alias from other clones.  */
+
+void
+adjust_clone_attributes (tree fn, tree clone, tree name, bool skip_copy_p)
+{
+  if (IDENTIFIER_CDTOR_P (name))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	{
+	  found = true;
+	  break;
+	}
+
+      if (found
+	  && (name == complete_ctor_identifier
+	      || name == complete_dtor_identifier))
+	{
+	  /* Reuse the sym alias decls created for the primary cdtor
+	     decl.  */
+	  symtab_node::remap_sym_alias_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);
+
+	  if (!skip_copy_p)
+	    DECL_ATTRIBUTES (clone) = copy_list (DECL_ATTRIBUTES (clone));
+
+	  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	    {
+	      /* We need to copy this even with skip_copy_p, because
+		 even then copying was shallow.  */
+	      TREE_VALUE (sym) = copy_list (TREE_VALUE (sym));
+	      /* Append suf to the sym alias name.  */
+	      tree str = TREE_VALUE (TREE_VALUE (sym));
+	      char *symname = concat (TREE_STRING_POINTER (str), suf, NULL);
+	      str = build_string (TREE_STRING_LENGTH (str) + xlen, symname);
+	      TREE_VALUE (TREE_VALUE (sym)) = str;
+	      free (symname);
+	    }
+
+	  if (symtab_node::get (clone))
+	    create_sym_alias_decls (clone);
+	}
+    }
+  else
+    DECL_ATTRIBUTES (clone)
+      = remove_attribute ("sym_alias", DECL_ATTRIBUTES (clone));
+}
+
 /* FN is constructor, destructor or operator function.  Clone the
    declaration to create a NAME'd variant.  NEED_VTT_PARM_P and
    OMIT_INHERITED_PARMS_P are relevant if it's a cdtor.  */
@@ -5065,6 +5127,8 @@ build_clone (tree fn, tree name, bool need_vtt_parm_p,
   DECL_CHAIN (clone) = DECL_CHAIN (fn);
   DECL_CHAIN (fn) = clone;
 
+  adjust_clone_attributes (fn, clone, name);
+
   return clone;
 }
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index fef80816c5f..607a8659063 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5806,6 +5806,8 @@ struct GTY((for_user)) spec_entry
 
 extern int current_class_depth;
 
+void adjust_clone_attributes (tree fn, tree clone, tree name, bool = false);
+
 /* in decl.cc */
 
 /* An array of static vars & fns.  */
@@ -7043,6 +7045,7 @@ extern void do_push_parm_decls			(tree, tree, tree *);
 extern tree do_aggregate_paren_init		(tree, tree);
 
 /* in decl2.cc */
+extern void update_sym_alias_interface		(tree);
 extern void record_mangling			(tree, bool);
 extern void overwrite_mangling			(tree, tree);
 extern void note_mangling_alias			(tree, tree);
@@ -7617,6 +7620,7 @@ extern bool emit_tinfo_decl			(tree);
 extern unsigned get_pseudo_tinfo_index		(tree);
 extern tree get_pseudo_tinfo_type		(unsigned);
 extern tree build_if_nonnull			(tree, tree, tsubst_flags_t);
+extern void update_tinfo_sym_alias		(tree);
 
 /* in search.cc */
 extern tree get_parent_with_private_access 	(tree decl, tree binfo);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 4b685270097..cc7f7a99487 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -3277,6 +3277,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
 	      && TREE_STATIC (olddecl))))
     make_decl_rtl (olddecl);
 
+  symtab_node::remap_sym_alias_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.
@@ -3300,6 +3302,7 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
       FOR_EACH_CLONE (clone, olddecl)
 	{
 	  DECL_ATTRIBUTES (clone) = DECL_ATTRIBUTES (olddecl);
+	  adjust_clone_attributes (olddecl, clone, DECL_NAME (clone));
 	  DECL_PRESERVE_P (clone) |= DECL_PRESERVE_P (olddecl);
 	}
     }
@@ -10838,6 +10841,7 @@ grokfndecl (tree ctype,
     {
       cplus_decl_attributes (&decl, *attrlist, 0);
       *attrlist = NULL_TREE;
+      create_sym_alias_decls (decl);
     }
 
   if (DECL_HAS_CONTRACTS_P (decl))
diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
index bee84879023..6d72ad8ff43 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -1846,6 +1846,9 @@ cplus_decl_attributes (tree *decl, tree attributes, int flags)
   if (late_attrs)
     save_template_attributes (late_attrs, decl, flags);
 
+  if (TYPE_P (*decl) && attributes)
+    update_tinfo_sym_alias (*decl);
+
   /* Propagate deprecation out to the template.  */
   if (TREE_DEPRECATED (*decl))
     if (tree ti = get_template_info (*decl))
@@ -2202,6 +2205,47 @@ 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 sym aliases.  */
+
+void
+update_sym_alias_interface (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.  */
 
@@ -3009,6 +3053,8 @@ determine_visibility (tree decl)
        translation unit, we can make the type internal.  */
     constrain_visibility (decl, VISIBILITY_ANON, false);
 
+  update_sym_alias_interface (decl);
+
   /* If visibility changed and DECL already has DECL_RTL, ensure
      symbol flags are updated.  */
   if ((DECL_VISIBILITY (decl) != orig_visibility
@@ -3271,6 +3317,8 @@ tentative_decl_linkage (tree decl)
       else if (VAR_P (decl))
 	maybe_commonize_var (decl);
     }
+
+  update_sym_alias_interface (decl);
 }
 
 /* DECL is a FUNCTION_DECL or VAR_DECL.  If the object file linkage
@@ -3505,6 +3553,8 @@ import_export_decl (tree decl)
     }
 
   DECL_INTERFACE_KNOWN (decl) = 1;
+
+  update_sym_alias_interface (decl);
 }
 
 /* Return an expression that performs the destruction of DECL, which
diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc
index 76f1d44610a..3a19a571256 100644
--- a/gcc/cp/name-lookup.cc
+++ b/gcc/cp/name-lookup.cc
@@ -22,9 +22,11 @@ along with GCC; see the file COPYING3.  If not see
 #define INCLUDE_MEMORY
 #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"
@@ -3675,6 +3677,15 @@ push_local_extern_decl_alias (tree decl)
 	  /* Adjust visibility.  */
 	  determine_visibility (alias);
 	}
+      else if (DECL_P (alias))
+	DECL_ATTRIBUTES (alias)
+	  = targetm.merge_decl_attributes (alias, decl);
+      if (DECL_P (alias))
+	{
+	  symtab_node::remap_sym_alias_target (decl, alias);
+	  DECL_ATTRIBUTES (decl)
+	    = remove_attribute ("sym_alias", DECL_ATTRIBUTES (alias));
+	}
     }
 
   retrofit_lang_decl (decl);
diff --git a/gcc/cp/optimize.cc b/gcc/cp/optimize.cc
index 9e8926e4cc6..4a2e8cb4354 100644
--- a/gcc/cp/optimize.cc
+++ b/gcc/cp/optimize.cc
@@ -528,9 +528,12 @@ maybe_clone_body (tree fn)
       DECL_VISIBILITY_SPECIFIED (clone) = DECL_VISIBILITY_SPECIFIED (fn);
       DECL_DLLIMPORT_P (clone) = DECL_DLLIMPORT_P (fn);
       DECL_ATTRIBUTES (clone) = clone_attrs (DECL_ATTRIBUTES (fn));
+      adjust_clone_attributes (fn, clone, DECL_NAME (clone), true);
       DECL_DISREGARD_INLINE_LIMITS (clone) = DECL_DISREGARD_INLINE_LIMITS (fn);
       set_decl_section_name (clone, fn);
 
+      update_sym_alias_interface (clone);
+
       /* Adjust the parameter names and locations.  */
       parm = DECL_ARGUMENTS (fn);
       clone_parm = DECL_ARGUMENTS (clone);
diff --git a/gcc/cp/rtti.cc b/gcc/cp/rtti.cc
index 7878929c246..4bb7b8c8c6e 100644
--- a/gcc/cp/rtti.cc
+++ b/gcc/cp/rtti.cc
@@ -28,8 +28,10 @@ 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"
+#include "cgraph.h"
 
 /* C++ returns type information to the user in struct type_info
    objects. We also use type information to implement dynamic_cast and
@@ -479,8 +481,13 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
 	  = build_tree_list (get_identifier ("non overlapping"),
 			     NULL_TREE);
       else
+	/* Share the non overlapping attribute, without assuming it's
+	   the only attribute, but assuming it's the last if it's
+	   present.  There may be sym aliases too, and those are not
+	   to be shared.  */
 	DECL_ATTRIBUTES (d)
-	  = DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]);
+	  = lookup_attribute ("non overlapping",
+			      DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]));
 
       /* Mark the variable as undefined -- but remember that we can
 	 define it later if we need to do so.  */
@@ -492,6 +499,16 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
       if (CLASS_TYPE_P (type))
 	CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type)) = d;
 
+      /* Copy sym alias attributes from the type to the rtti obj decl.  */
+      tree *attrs = &DECL_ATTRIBUTES (d);
+      FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+	{
+	  tree attr = tree_cons (TREE_PURPOSE (sym), TREE_VALUE (sym), *attrs);
+	  *attrs = attr;
+	  attrs = &TREE_CHAIN (attr);
+	}
+      create_sym_alias_decls (d);
+
       /* Add decl to the global array of tinfo decls.  */
       vec_safe_push (unemitted_tinfo_decls, d);
     }
@@ -499,6 +516,58 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
   return d;
 }
 
+/* After modifying the attributes of TYPE, check whether tinfo was
+   already created and, if so, add to it any sym alias attributes
+   that were not already present.  */
+
+void
+update_tinfo_sym_alias (tree type)
+{
+  if (!TYPE_SIZE (type) || !CLASS_TYPE_P (type))
+    return;
+
+  tree d = CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type));
+  if (!d)
+    return;
+
+  bool first = true;
+  symtab_node *node = NULL;
+
+  tree *attrs = &DECL_ATTRIBUTES (d);
+  FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (d_sym, *attrs)
+	if (TREE_VALUE (sym) == TREE_VALUE (d_sym))
+	  {
+	    found = true;
+	    break;
+	  }
+
+      if (found)
+	continue;
+
+      tree attr = tree_cons (TREE_PURPOSE (sym),
+			     TREE_VALUE (sym),
+			     *attrs);
+      *attrs = attr;
+      attrs = &TREE_CHAIN (attr);
+
+      if (first)
+	{
+	  first = false;
+	  node = symtab_node::get (d);
+	}
+
+      if (!node)
+	continue;
+
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+      create_sym_alias_decl (d, id);
+    }
+}
+
 /* Return the type_info object for TYPE.  */
 
 tree
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index af782b3f228..7f4414410b0 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -4142,6 +4142,43 @@ Function Attributes}, @ref{PowerPC Function Attributes},
 @ref{Nios II Function Attributes}, and @ref{S/390 Function Attributes}
 for details.
 
+@cindex @code{sym_alias} function attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} attribute causes @var{name} to be output as an
+alias to the function, with the same linkage and visibility as the
+function, when the function definition is output, provided that the
+function could be an alias target.  For instance,
+
+@smallexample
+void f (uint64_t) __attribute__ ((__sym_alias__ ("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},
+with the same linkage and visibility.  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.
+
+Aliases introduced with this attribute, such as @samp{f_u64} in the
+example above, are assembly symbol names: they do not undergo C++ name
+mangling, and are not made visible in any scope in the source language.
+They can, however, can be named as alias targets.
+
+This attribute requires assembler and object file support for aliases,
+and may not be available on all targets.
+
 @cindex @code{symver} function attribute
 @item symver ("@var{name2}@@@var{nodename}")
 On ELF targets this attribute creates a symbol version.  The @var{name2} part
@@ -8069,6 +8106,10 @@ will be placed in new, unique sections.
 
 This additional functionality requires Binutils version 2.36 or later.
 
+@cindex @code{sym_alias} variable attribute
+@item sym_alias ("@var{name}")
+See @pxref{Common Function Attributes}.
+
 @cindex @code{uninitialized} variable attribute
 @item uninitialized
 This attribute, attached to a variable with automatic storage, means that
@@ -9164,6 +9205,21 @@ is not supported; that is to say, if a given scalar object can be accessed
 through distinct types that assign a different storage order to it, then the
 behavior is undefined.
 
+@cindex @code{sym_alias} type attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} type 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__ ((__sym_alias__ ("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.
+
 @cindex @code{transparent_union} type attribute
 @item transparent_union
 
diff --git a/gcc/symtab.cc b/gcc/symtab.cc
index 0470509a98d..43ba88d7939 100644
--- a/gcc/symtab.cc
+++ b/gcc/symtab.cc
@@ -1943,6 +1943,44 @@ symtab_node::noninterposable_alias (symtab_node *node, void *data)
   return false;
 }
 
+/* Remap sym alias nodes recorded as aliasing REPLACED to alias
+   REPLACEMENT instead.  */
+
+void
+symtab_node::remap_sym_alias_target (tree replaced, tree replacement)
+{
+  if (!decl_in_symtab_p (replacement)
+      || !symtab_node::get (replacement))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (replaced))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      symtab_node *sym_node = symtab_node::get_for_asmname (id);
+
+      if (!sym_node)
+	{
+	  create_sym_alias_decl (replacement, id);
+	  continue;
+	}
+
+      gcc_assert (!sym_node->analyzed);
+      if (sym_node->alias_target != replaced)
+	continue;
+
+      // sym_node->definition = 0;
+
+      if (VAR_P (replaced))
+	// varpool_node::create_extra_name_alias (sym_node->decl, replacement);
+	varpool_node::create_alias (sym_node->decl, replacement);
+      else
+	cgraph_node::create_same_body_alias (sym_node->decl, replacement);
+	// 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/goacc/declare-1.c b/gcc/testsuite/c-c++-common/goacc/declare-1.c
index 46ee01b6759..f2842893318 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-1.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-1.c
@@ -113,11 +113,11 @@ f_2 (void)
   int va3;
 #pragma acc declare device_resident(va3)
 
-#ifndef __cplusplus
+#if 0
   /* TODO PR90868
 
-     C: "error: variable '[...]' used more than once with '#pragma acc declare'".  */
-#else
+     "error: variable '[...]' used more than once with '#pragma acc declare'".  */
+
   extern int ve0;
 #pragma acc declare create(ve0)
 
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-2.c b/gcc/testsuite/c-c++-common/goacc/declare-2.c
index e2e22be57e9..aec59b69754 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-2.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-2.c
@@ -137,25 +137,25 @@ void
 f_pr90868_2 (void)
 {
   extern int we0;
-#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" } */
 
   extern int we1;
-#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" } */
 
   extern int *we2;
-#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" } */
 
   extern int we3;
-#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" } */
 
   extern int we4;
-#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" } */
 
   extern int we5;
-#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" } */
  
   extern int we6;
-#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" } */
 }
 
 
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
new file mode 100644
index 00000000000..61af50cb552
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
@@ -0,0 +1,39 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+int var_a = 1;
+
+void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+
+void
+foo_a ()
+{
+}
+
+
+int var_b;
+extern int var_b __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+
+void
+foo_b ()
+{
+}
+
+void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+
+
+int var_c __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+
+void __attribute__ ((__sym_alias__ ("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-sym-alias-2.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
new file mode 100644
index 00000000000..67dcd4fd7e1
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
@@ -0,0 +1,90 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+struct s
+{
+  int mem __attribute__ ((__sym_alias__ ("S_MEM"))); /* { dg-warning "attribute ignored" } */
+};
+
+void foo () {
+  extern void bar () __attribute__ ((__sym_alias__ ("FOO_BAR"))); /* ok */
+  int i __attribute__ ((sym_alias ("FOO_i"))); /* { dg-warning "ignored" } */
+  /* ??? X cannot be an alias target; should this be flagged? ... */
+  static int x __attribute__ ((sym_alias ("FOO_x")));
+  static int sx __attribute__ ((alias ("FOO_x"))); /* { dg-warning "ignored" } */
+  extern int xx __attribute__ ((alias ("FOO_x"))); /* { dg-warning "ignored" } */
+}
+
+/* ??? ... or should XR be accepted?  */
+extern int xr __attribute__ ((alias ("FOO_x"))); /* { dg-error "undefined" "" { xfail c++ } } */
+int dr __attribute__ ((alias ("FOO_x"))); /* { dg-error "defined both" } */
+
+
+int j __attribute__ ((sym_alias ("J_var")));
+void __attribute__ ((alias ("J_var")))
+j_fn (); /* { dg-error "between function and variable" } */
+
+
+void __attribute__ ((sym_alias ("K_fn")))
+k () {
+}
+extern int __attribute__ ((alias ("K_fn")))
+k_var; /* { dg-error "between function and variable" } */
+
+
+int l __attribute__ ((sym_alias ("L_fn")));
+
+/* These should be detected and reported, not because the names clash at the
+   source level, but because the asm symbols do.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+void
+L_fn () /* { dg-error "duplicate" "" { xfail *-*-* } } */
+{
+}
+
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __attribute__ ((sym_alias ("M_var")))
+m ()
+{
+}
+
+int M_var; /* { dg-error "duplicate" "" { xfail *-*-* } } */
+
+
+void __attribute__ ((sym_alias ("N_sym")))
+n_fn ()
+{
+}
+
+int __attribute__ ((sym_alias ("N_sym")))
+n_var; /* { dg-error "duplicate" } */
+
+
+int __attribute__ ((sym_alias ("O_sym")))
+o_var;
+
+void __attribute__ ((sym_alias ("O_sym")))
+o_fn () /* { dg-error "duplicate" } */
+{
+}
+
+int __attribute__ ((sym_alias ("P_sym")))
+p_var1;
+
+int __attribute__ ((sym_alias ("P_sym")))
+p_var2; /* { dg-error "duplicate" } */
+
+void __attribute__ ((sym_alias ("Q_sym")))
+q_fn1 ()
+{
+}
+
+void __attribute__ ((sym_alias ("Q_sym")))
+q_fn2 () /* { dg-error "duplicate" } */
+{
+}
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
new file mode 100644
index 00000000000..0052485a30a
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
@@ -0,0 +1,41 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int var_a = 1;
+
+void
+foo_a ()
+{
+  extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+}
+
+#if 0 // __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__ ((__sym_alias__ ("FOOVAR_B")));
+}
+
+int var_b;
+
+void __attribute__ ((__sym_alias__ ("FOOBAR_C")))
+foo_c ()
+{
+  void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+  /* Another sym for var_b.  */
+  extern int var_b __attribute__ ((__sym_alias__ ("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-sym-alias-4.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
new file mode 100644
index 00000000000..2beafa46379
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-require-alias "" } */
+
+int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A"))) = 42;
+
+int __attribute__ ((__sym_alias__ ("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/c-c++-common/torture/attr-sym-alias-5.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
new file mode 100644
index 00000000000..bee90136daf
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
@@ -0,0 +1,54 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+/* { dg-require-visibility "" } */
+
+int __attribute__ ((sym_alias ("A_hid"), visibility ("hidden"))) a;
+
+extern int __attribute__ ((sym_alias ("B_prt"))) b;
+
+int b __attribute__ ((visibility ("protected")));
+
+extern int __attribute__ ((sym_alias ("C_ntr"))) c;
+
+extern int c __attribute__ ((visibility ("internal")));
+
+int c;
+
+static int d __attribute__ ((sym_alias ("D_stt")));
+
+
+extern void __attribute__ ((sym_alias ("F_hid"), visibility ("hidden")))
+f ();
+
+void
+f () {
+}
+
+extern void __attribute__ ((sym_alias ("G_prt")))
+g ();
+
+void __attribute__ ((visibility ("protected")))
+g () {
+}
+
+extern void __attribute__ ((sym_alias ("H_ntr")))
+h ();
+
+void __attribute__ ((visibility ("internal")))
+h ();
+
+void h () {
+}
+
+static void __attribute__ ((sym_alias ("I_stt")))
+i () {
+}
+
+/* { dg-final { scan-assembler {hidden[^\n]*A_hid} } } */
+/* { dg-final { scan-assembler {protected[^\n]*B_prt} } } */
+/* { dg-final { scan-assembler {internal[^\n]*C_ntr} } } */
+/* { dg-final { scan-assembler-not {glob[^\n]*D_stt} } } */
+/* { dg-final { scan-assembler {hidden[^\n]*F_hid} } } */
+/* { dg-final { scan-assembler {protected[^\n]*G_prt} } } */
+/* { dg-final { scan-assembler {internal[^\n]*H_ntr} } } */
+/* { dg-final { scan-assembler-not {glob[^\n]*I_stt} } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
new file mode 100644
index 00000000000..579eda1a473
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
@@ -0,0 +1,72 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+class __attribute__ ((__sym_alias__ ("FOOCLS_A"),
+		      __sym_alias__ ("FOOCLS_A_Dupe"))) foo {
+  static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  void __attribute__ ((__sym_alias__ ("FOOBAR_A"))) bar ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+int foo::var = 1;
+
+foo::foo () {}
+
+void foo::bar () {}
+
+namespace b {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_B"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_B"))) foo ();
+    void __attribute__ ((__sym_alias__ ("FOOBAR_B"))) bar () {}
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_B"))) ~foo() {}
+  };
+
+  int foo::var = 2;
+
+  foo::foo () {
+    void (foo::*pbar)() = &foo::bar;
+  }
+}
+
+namespace c {
+  namespace cd {
+    class __attribute__ ((__sym_alias__ ("FOOCLS_C"))) foo {
+      static int var __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+      __attribute__ ((__sym_alias__ ("FOOCTR_C"))) foo () {
+	void (foo::*pbar)() = &foo::bar;
+      }
+      void __attribute__ ((__sym_alias__ ("FOOBAR_C"))) bar () {}
+      virtual __attribute__ ((__sym_alias__ ("FOODTR_C"))) ~foo() {}
+    };
+
+    int foo::var = 3;
+  }
+}
+
+/* { dg-final { scan-assembler "FOOCLS_A" } } */
+/* { dg-final { scan-assembler "FOOCLS_A_Dupe" } } */
+/* { 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-sym-alias-2.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
new file mode 100644
index 00000000000..6facfcc2cd9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+namespace {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_A"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo ();
+    void __attribute__ ((__sym_alias__ ("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-sym-alias-3.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
new file mode 100644
index 00000000000..42c7288ad4a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
@@ -0,0 +1,83 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+// sym can be applied to template function explicit instantiations.
+
+template <typename T>
+void
+fn(T) {
+};
+
+template void __attribute__ ((__sym_alias__ ("FOOFUN_UINT"))) fn<>(unsigned int);
+template void __attribute__ ((__sym_alias__ ("FOOFUN_LONG"))) fn<>(long);
+
+template<> void __attribute__ ((__sym_alias__ ("FOOFUN_CHAR"))) fn<>(char) {}
+
+
+template <typename T = void>
+struct
+foo {
+  virtual ~foo() {}
+
+  virtual void virtfun() {}
+
+  static void stfun() {}
+  void inlfun() {}
+};
+
+// Explicitly instantiate members before the enclosing class.
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_CHAR_VIRT"))) foo<char>::virtfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_CHAR_TI"))) foo<char>;
+
+// Though they're only output if the enclosing class is.
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_LONG_VIRT"))) foo<long>::virtfun();
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_LONG_TI_X"))) foo<long>;
+
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_VOID_ST"))) foo<void>::stfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_VOID_TI"))) foo<>;
+
+
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_X"))) foo<short>;
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_ST"))) foo<short>::stfun();
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_INL"))) foo<short>::inlfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_D"))) foo<short>;
+
+// Explicit specializations work too.
+
+template <>
+struct  __attribute__ ((__sym_alias__ ("FOOCLS_INT_TI")))
+foo<int>
+{
+  virtual ~foo() {}
+  virtual void __attribute__ ((__sym_alias__ ("FOOCLS_INT_VIRT"))) virtfun() {}
+};
+
+/* { dg-final { scan-assembler "FOOFUN_UINT" } } */
+/* { dg-final { scan-assembler "FOOFUN_LONG" } } */
+/* { dg-final { scan-assembler "FOOFUN_CHAR" } } */
+
+/* { dg-final { scan-assembler "FOOCLS_VOID_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_VOID_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_X" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_INL" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_D" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_TI_X" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_VIRT" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
new file mode 100644
index 00000000000..59b779de35d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+template <typename T = void>
+class
+__attribute__ ((__sym_alias__ ("FOOCLS")))
+foo // { dg-error "duplicate|already" }
+{
+  virtual ~foo() {}
+
+  template <typename U>
+  void
+    __attribute__ ((__sym_alias__ ("FOOTMF")))
+    tmemfun () {} // { dg-error "duplicate|already" }
+};
+
+template <typename T>
+void
+__attribute__ ((__sym_alias__ ("FOOTFN")))
+fn(T) { // { dg-error "duplicate|already" }
+};
+
+template class foo<>;
+template class foo<int>;
+template void foo<>::tmemfun<void>();
+template void foo<int>::tmemfun<void>();
+template void fn<>(int);
+template void fn<>(long);
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
new file mode 100644
index 00000000000..45d59b953c3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
@@ -0,0 +1,14 @@
+/* { dg-do compile { target c++11 } } */
+/* { dg-require-alias "" } */
+
+struct foo {
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+foo::foo () {}
+
+// Make sure the inherited cdtors don't duplicate the syms.
+struct bar : foo {
+  using foo::foo;
+};
diff --git a/gcc/varpool.cc b/gcc/varpool.cc
index e7b51b15e4a..33649906d1d 100644
--- a/gcc/varpool.cc
+++ b/gcc/varpool.cc
@@ -163,6 +163,9 @@ varpool_node::get_create (tree decl)
     }
 
   node->register_symbol ();
+
+  create_sym_alias_decls (decl);
+
   return node;
 }

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [gcc(refs/users/aoliva/heads/testme)] Introduce attribute sym_alias
@ 2023-12-03  1:46 Alexandre Oliva
  0 siblings, 0 replies; 33+ messages in thread
From: Alexandre Oliva @ 2023-12-03  1:46 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:a189ce5abbf7cb3ef85336975d8b30bda30625cf

commit a189ce5abbf7cb3ef85336975d8b30bda30625cf
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Fri Dec 1 14:31:46 2023 -0300

    Introduce attribute sym_alias
    
    This patch introduces an attribute to add extra asm names (aliases)
    for a decl 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 class types, namespace-scoped
    variables and functions, static data members, member functions,
    explicit instantiations and specializations of template functions,
    members and classes.
    
    When applied to constructors or destructor, additional sym aliases
    with _Base and _Del suffixes are defined for variants other than
    complete-object ones.  This changes the assumption that clones always
    carry the same attributes as their abstract declarations, so there is
    now a function to adjust them.
    
    C++ also had a bug in which attributes from local extern declarations
    failed to be propagated to a preexisting corresponding
    namespace-scoped decl.  I've fixed that, and adjusted acc tests that
    distinguished between C and C++ in this regard.
    
    Applying the attribute to class types is only valid in C++, and the
    effect is to attach the alias to the RTTI object associated with the
    class type.
    
    for  gcc/ChangeLog
    
            * attribs.cc: Include cgraph.h.
            (decl_attributes): Allow late introduction of sym_alias in
            types.
            (create_sym_alias_decl, create_sym_alias_decls): New.
            * attribs.h: Declare them.
            (FOR_EACH_SYM_ALIAS): New macro.
            * cgraph.cc (cgraph_node::create): Create sym_alias decls.
            * varpool.cc (varpool_node::get_create): Create sym_alias
            decls.
            * cgraph.h (symtab_node::remap_sym_alias_target): New.
            * symtab.cc (symtab_node::remap_sym_alias_target): Define.
            * cgraphunit.cc (cgraph_node::analyze): Create alias_target
            node if needed.
            (analyze_functions): Fixup visibility of implicit alias only
            after its node is analyzed.
            * doc/extend.texi (sym_alias): Document for variables,
            functions and types.
    
    for  gcc/ada/ChangeLog
    
            * doc/gnat_rm/interfacing_to_other_languages.rst: Mention
            attribute sym_alias to give RTTI symbols mnemonic names.
            * doc/gnat_ugn/the_gnat_compilation_model.rst: Mention
            aliases.  Fix incorrect ref to C1 ctor variant.
    
    for  gcc/c-family/ChangeLog
    
            * c-ada-spec.cc (pp_asm_name): Use first sym_alias if
            available.
            * c-attribs.cc (handle_sym_alias_attribute): New.
            (c_common_attribute_table): Add sym_alias.
            (handle_copy_attribute): Do not copy sym_alias attribute.
    
    for  gcc/c/ChangeLog
    
            * c-decl.cc (duplicate_decls): Remap sym_alias target.
    
    for  gcc/cp/ChangeLog
    
            * class.cc (adjust_clone_attributes): New.
            (copy_fndecl_with_name, build_clone): Call it.
            * cp-tree.h (adjust_clone_attributes): Declare.
            (update_sym_alias_interface): Declare.
            (update_tinfo_sym_alias): Declare.
            * decl.cc (duplicate_decls): Remap sym_alias target.
            Adjust clone attributes.
            (grokfndecl): Tentatively create sym_alias decls after
            adding attributes in e.g. a template member function explicit
            instantiation.
            * decl2.cc (cplus_decl_attributes): Update tinfo sym_alias.
            (copy_interface, update_sym_alias_interface): New.
            (determine_visibility): Update sym_alias interface.
            (tentative_decl_linkage, import_export_decl): Likewise.
            * name-lookup.cc: Include target.h and cgraph.h.
            (push_local_extern_decl_alias): Merge attributes with
            namespace-scoped decl, and drop duplicate sym_alias.
            * optimize.cc (maybe_clone_body): Re-adjust attributes after
            cloning them.  Update sym_alias interface.
            * rtti.cc: Include attribs.h and cgraph.h.
            (get_tinfo_decl): Copy sym_alias attributes from type to tinfo
            decl.  Create sym_alias decls.
            (update_tinfo_sym_alias): New.
    
    for  gcc/testsuite/ChangeLog
    
            * c-c++-common/goacc/declare-1.c: Adjust.
            * c-c++-common/goacc/declare-2.c: Adjust.
            * c-c++-common/torture/attr-sym-alias-1.c: New.
            * c-c++-common/torture/attr-sym-alias-2.c: New.
            * c-c++-common/torture/attr-sym-alias-3.c: New.
            * c-c++-common/torture/attr-sym-alias-4.c: New.
            * c-c++-common/torture/attr-sym-alias-5.c: New.
            * g++.dg/torture/attr-sym-alias-1.C: New.
            * g++.dg/torture/attr-sym-alias-2.C: New.
            * g++.dg/torture/attr-sym-alias-3.C: New.
            * g++.dg/torture/attr-sym-alias-4.C: New.
            * g++.dg/torture/attr-sym-alias-5.C: New.

Diff:
---
 .../doc/gnat_rm/interfacing_to_other_languages.rst |  6 ++
 .../doc/gnat_ugn/the_gnat_compilation_model.rst    | 10 ++-
 gcc/attribs.cc                                     | 66 +++++++++++++++-
 gcc/attribs.h                                      |  7 ++
 gcc/c-family/c-ada-spec.cc                         |  7 ++
 gcc/c-family/c-attribs.cc                          | 33 +++++++-
 gcc/c/c-decl.cc                                    |  2 +
 gcc/cgraph.cc                                      |  2 +
 gcc/cgraph.h                                       |  4 +
 gcc/cgraphunit.cc                                  |  2 +-
 gcc/cp/class.cc                                    | 64 +++++++++++++++
 gcc/cp/cp-tree.h                                   |  4 +
 gcc/cp/decl.cc                                     |  4 +
 gcc/cp/decl2.cc                                    | 50 ++++++++++++
 gcc/cp/name-lookup.cc                              | 11 +++
 gcc/cp/optimize.cc                                 |  3 +
 gcc/cp/rtti.cc                                     | 71 ++++++++++++++++-
 gcc/doc/extend.texi                                | 56 ++++++++++++++
 gcc/symtab.cc                                      | 38 +++++++++
 gcc/testsuite/c-c++-common/goacc/declare-1.c       |  6 +-
 gcc/testsuite/c-c++-common/goacc/declare-2.c       | 14 ++--
 .../c-c++-common/torture/attr-sym-alias-1.c        | 39 ++++++++++
 .../c-c++-common/torture/attr-sym-alias-2.c        | 90 ++++++++++++++++++++++
 .../c-c++-common/torture/attr-sym-alias-3.c        | 41 ++++++++++
 .../c-c++-common/torture/attr-sym-alias-4.c        | 28 +++++++
 .../c-c++-common/torture/attr-sym-alias-5.c        | 54 +++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C    | 72 +++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C    | 26 +++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C    | 83 ++++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C    | 28 +++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C    | 14 ++++
 gcc/varpool.cc                                     |  3 +
 32 files changed, 919 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 ad0be511d48..b8de16ebf7c 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 ``sym_alias`` 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 fd15459203a..20045698962 100644
--- a/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
+++ b/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
@@ -4274,6 +4274,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__ ((__sym_alias__ ("Ctor_For_Animal")))
         Animal() {Age_Count = 0;};
       private:
         int Age_Count;
@@ -4306,6 +4307,7 @@ both Carnivore and Domestic, that is:
         virtual int  Number_Of_Teeth ();
         virtual void Set_Owner (char* Name);
 
+        __attribute__ ((__sym_alias__ ("Ctor_For_Dog"))) // mnemonic alias
         Dog(); // Constructor
       private:
         int  Tooth_Count;
@@ -4344,7 +4346,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;
@@ -4360,7 +4363,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,7 +4385,8 @@ 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.
+mangled name (or an alias) 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.cc b/gcc/attribs.cc
index 08449ab3f8e..7294ff27277 100644
--- a/gcc/attribs.cc
+++ b/gcc/attribs.cc
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "target.h"
 #include "tree.h"
+#include "cgraph.h"
 #include "stringpool.h"
 #include "diagnostic-core.h"
 #include "attribs.h"
@@ -826,7 +827,8 @@ decl_attributes (tree *node, tree attributes, int flags,
 
       if (TYPE_P (*anode)
 	  && (flags & (int) ATTR_FLAG_TYPE_IN_PLACE)
-	  && COMPLETE_TYPE_P (*anode))
+	  && COMPLETE_TYPE_P (*anode)
+	  && !is_attribute_p ("sym_alias", name))
 	{
 	  warning (OPT_Wattributes, "type attributes ignored after type is already defined");
 	  continue;
@@ -2669,6 +2671,68 @@ attr_access::array_as_string (tree type) const
   return typstr;
 }
 
+/* Create a sym attribute for DECL to be visible with linkage name ID.  */
+
+tree
+create_sym_alias_decl (tree decl, tree id)
+{
+  const char *attr_str = "sym_alias";
+
+  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;
+
+      tree attr_name = get_identifier (attr_str);
+      error_at (DECL_SOURCE_LOCATION (decl),
+		"duplicate symbol name %qE in %qE attribute of %qD",
+		id, attr_name, decl);
+      inform (DECL_SOURCE_LOCATION (sym_node->decl),
+	      "already used by %qD", sym_node->decl);
+    }
+
+  tree clone = copy_node (decl);
+  DECL_ATTRIBUTES (clone) = remove_attribute (attr_str,
+					      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_extra_name_alias (clone, decl);
+    varpool_node::create_alias (clone, decl);
+  else
+    cgraph_node::create_same_body_alias (clone, decl);
+    // cgraph_node::create_alias (clone, decl);
+
+  return clone;
+}
+
+/* Create decls for all sym aliases requested in DECL's attributes.  */
+
+void
+create_sym_alias_decls (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl)
+      || DECL_ABSTRACT_P (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      create_sym_alias_decl (decl, id);
+    }
+}
+
 #if CHECKING_P
 
 namespace selftest
diff --git a/gcc/attribs.h b/gcc/attribs.h
index 84a43658a70..156f2c20588 100644
--- a/gcc/attribs.h
+++ b/gcc/attribs.h
@@ -398,4 +398,11 @@ extern void init_attr_rdwr_indices (rdwr_map *, tree);
 extern attr_access *get_parm_access (rdwr_map &, tree,
 				     tree = current_function_decl);
 
+extern tree create_sym_alias_decl (tree, tree);
+extern void create_sym_alias_decls (tree);
+
+#define FOR_EACH_SYM_ALIAS(sym, attrs)					\
+  for (tree sym = lookup_attribute ("sym_alias", (attrs));		\
+       sym; sym = lookup_attribute ("sym_alias", TREE_CHAIN (sym)))
+
 #endif // GCC_ATTRIBS_H
diff --git a/gcc/c-family/c-ada-spec.cc b/gcc/c-family/c-ada-spec.cc
index 050994d8416..5042b9cfecd 100644
--- a/gcc/c-family/c-ada-spec.cc
+++ b/gcc/c-family/c-ada-spec.cc
@@ -1431,6 +1431,13 @@ pp_ada_tree_identifier (pretty_printer *buffer, tree node, tree type,
 static void
 pp_asm_name (pretty_printer *buffer, tree t)
 {
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (t))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.cc b/gcc/c-family/c-attribs.cc
index ac68153e244..ee5c3aedccf 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -110,7 +110,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_sym_alias_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,
@@ -392,6 +393,8 @@ const struct attribute_spec c_common_attribute_table[] =
 			      handle_alias_attribute, NULL },
   { "weakref",                0, 1, true,  false, false, false,
 			      handle_weakref_attribute, NULL },
+  { "sym_alias",              1, 1, false,  false, false, false,
+			      handle_sym_alias_attribute, NULL },
   { "no_instrument_function", 0, 0, true,  false, false, false,
 			      handle_no_instrument_function_attribute,
 			      NULL },
@@ -3084,7 +3087,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
@@ -3094,7 +3097,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
@@ -3104,6 +3107,29 @@ handle_alias_attribute (tree *node, tree name, tree args,
   return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs);
 }
 
+/* Handle a "sym_alias" attribute; arguments as in struct
+   attribute_spec.handler.  */
+
+static tree
+handle_sym_alias_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 (decl_in_symtab_p (node))
+    *no_add_attrs = false;
+  else if (TYPE_P (node) && c_dialect_cxx ())
+    *no_add_attrs = false;
+  else
+    return error_mark_node;
+
+  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.  */
 
@@ -3237,6 +3263,7 @@ handle_copy_attribute (tree *node, tree name, tree args,
 	      || is_attribute_p ("visibility", atname)
 	      || is_attribute_p ("weak", atname)
 	      || is_attribute_p ("weakref", atname)
+	      || is_attribute_p ("sym_alias", atname)
 	      || is_attribute_p ("target_clones", atname))
 	    continue;
 
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index cf1df82c0f4..e5a1ee54cb3 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -3119,6 +3119,8 @@ duplicate_decls (tree newdecl, tree olddecl)
 
   merge_decls (newdecl, olddecl, newtype, oldtype);
 
+  symtab_node::remap_sym_alias_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.cc b/gcc/cgraph.cc
index f93259a8c70..0bef2179e89 100644
--- a/gcc/cgraph.cc
+++ b/gcc/cgraph.cc
@@ -523,6 +523,8 @@ cgraph_node::create (tree decl)
   node->register_symbol ();
   maybe_record_nested_function (node);
 
+  create_sym_alias_decls (decl);
+
   return node;
 }
 
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index 2b320557616..6d61e327fb6 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -327,6 +327,10 @@ public:
   /* Return DECL that alias is aliasing.  */
   inline tree get_alias_target_tree ();
 
+  /* Remap sym alias nodes recorded as aliasing REPLACED to alias REPLACEMENT
+     instead.  */
+  static void remap_sym_alias_target (tree replaced, tree replacement);
+
   /* Set section for symbol and its aliases.  */
   void set_section (const char *section);
 
diff --git a/gcc/cgraphunit.cc b/gcc/cgraphunit.cc
index 9a550a5cce6..a66db795c78 100644
--- a/gcc/cgraphunit.cc
+++ b/gcc/cgraphunit.cc
@@ -1176,7 +1176,7 @@ analyze_functions (bool first_time)
      C++ FE is confused about the COMDAT groups being right.  */
   if (symtab->cpp_implicit_aliases_done)
     FOR_EACH_SYMBOL (node)
-      if (node->cpp_implicit_alias)
+      if (node->cpp_implicit_alias && node->analyzed)
 	  node->fixup_same_cpp_alias_visibility (node->get_alias_target ());
   build_type_inheritance_graph ();
 
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index 6fdb56abfb9..fa8fc63db82 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -4896,6 +4896,68 @@ check_methods (tree t)
     }
 }
 
+/* Adjust sym alias name for CLONE, cloned from FN and named NAME,
+   if it is a cdtor, and drop the sym alias from other clones.  */
+
+void
+adjust_clone_attributes (tree fn, tree clone, tree name, bool skip_copy_p)
+{
+  if (IDENTIFIER_CDTOR_P (name))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	{
+	  found = true;
+	  break;
+	}
+
+      if (found
+	  && (name == complete_ctor_identifier
+	      || name == complete_dtor_identifier))
+	{
+	  /* Reuse the sym alias decls created for the primary cdtor
+	     decl.  */
+	  symtab_node::remap_sym_alias_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);
+
+	  if (!skip_copy_p)
+	    DECL_ATTRIBUTES (clone) = copy_list (DECL_ATTRIBUTES (clone));
+
+	  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	    {
+	      /* We need to copy this even with skip_copy_p, because
+		 even then copying was shallow.  */
+	      TREE_VALUE (sym) = copy_list (TREE_VALUE (sym));
+	      /* Append suf to the sym alias name.  */
+	      tree str = TREE_VALUE (TREE_VALUE (sym));
+	      char *symname = concat (TREE_STRING_POINTER (str), suf, NULL);
+	      str = build_string (TREE_STRING_LENGTH (str) + xlen, symname);
+	      TREE_VALUE (TREE_VALUE (sym)) = str;
+	      free (symname);
+	    }
+
+	  if (symtab_node::get (clone))
+	    create_sym_alias_decls (clone);
+	}
+    }
+  else
+    DECL_ATTRIBUTES (clone)
+      = remove_attribute ("sym_alias", DECL_ATTRIBUTES (clone));
+}
+
 /* FN is constructor, destructor or operator function.  Clone the
    declaration to create a NAME'd variant.  NEED_VTT_PARM_P and
    OMIT_INHERITED_PARMS_P are relevant if it's a cdtor.  */
@@ -5065,6 +5127,8 @@ build_clone (tree fn, tree name, bool need_vtt_parm_p,
   DECL_CHAIN (clone) = DECL_CHAIN (fn);
   DECL_CHAIN (fn) = clone;
 
+  adjust_clone_attributes (fn, clone, name);
+
   return clone;
 }
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 964af1ddd85..2bafed4e993 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5787,6 +5787,8 @@ struct GTY((for_user)) spec_entry
 
 extern int current_class_depth;
 
+void adjust_clone_attributes (tree fn, tree clone, tree name, bool = false);
+
 /* in decl.cc */
 
 /* An array of static vars & fns.  */
@@ -7022,6 +7024,7 @@ extern void do_push_parm_decls			(tree, tree, tree *);
 extern tree do_aggregate_paren_init		(tree, tree);
 
 /* in decl2.cc */
+extern void update_sym_alias_interface		(tree);
 extern void record_mangling			(tree, bool);
 extern void overwrite_mangling			(tree, tree);
 extern void note_mangling_alias			(tree, tree);
@@ -7596,6 +7599,7 @@ extern bool emit_tinfo_decl			(tree);
 extern unsigned get_pseudo_tinfo_index		(tree);
 extern tree get_pseudo_tinfo_type		(unsigned);
 extern tree build_if_nonnull			(tree, tree, tsubst_flags_t);
+extern void update_tinfo_sym_alias		(tree);
 
 /* in search.cc */
 extern tree get_parent_with_private_access 	(tree decl, tree binfo);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 2f2dbb8d107..5636d694b68 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -3237,6 +3237,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
 	      && TREE_STATIC (olddecl))))
     make_decl_rtl (olddecl);
 
+  symtab_node::remap_sym_alias_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.
@@ -3260,6 +3262,7 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
       FOR_EACH_CLONE (clone, olddecl)
 	{
 	  DECL_ATTRIBUTES (clone) = DECL_ATTRIBUTES (olddecl);
+	  adjust_clone_attributes (olddecl, clone, DECL_NAME (clone));
 	  DECL_PRESERVE_P (clone) |= DECL_PRESERVE_P (olddecl);
 	}
     }
@@ -10789,6 +10792,7 @@ grokfndecl (tree ctype,
     {
       cplus_decl_attributes (&decl, *attrlist, 0);
       *attrlist = NULL_TREE;
+      create_sym_alias_decls (decl);
     }
 
   if (DECL_HAS_CONTRACTS_P (decl))
diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
index 9e666e5eece..c7f009beaf0 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -1844,6 +1844,9 @@ cplus_decl_attributes (tree *decl, tree attributes, int flags)
   if (late_attrs)
     save_template_attributes (late_attrs, decl, flags);
 
+  if (TYPE_P (*decl) && attributes)
+    update_tinfo_sym_alias (*decl);
+
   /* Propagate deprecation out to the template.  */
   if (TREE_DEPRECATED (*decl))
     if (tree ti = get_template_info (*decl))
@@ -2200,6 +2203,47 @@ 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 sym aliases.  */
+
+void
+update_sym_alias_interface (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.  */
 
@@ -3007,6 +3051,8 @@ determine_visibility (tree decl)
        translation unit, we can make the type internal.  */
     constrain_visibility (decl, VISIBILITY_ANON, false);
 
+  update_sym_alias_interface (decl);
+
   /* If visibility changed and DECL already has DECL_RTL, ensure
      symbol flags are updated.  */
   if ((DECL_VISIBILITY (decl) != orig_visibility
@@ -3269,6 +3315,8 @@ tentative_decl_linkage (tree decl)
       else if (VAR_P (decl))
 	maybe_commonize_var (decl);
     }
+
+  update_sym_alias_interface (decl);
 }
 
 /* DECL is a FUNCTION_DECL or VAR_DECL.  If the object file linkage
@@ -3503,6 +3551,8 @@ import_export_decl (tree decl)
     }
 
   DECL_INTERFACE_KNOWN (decl) = 1;
+
+  update_sym_alias_interface (decl);
 }
 
 /* Return an expression that performs the destruction of DECL, which
diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc
index d19ea5d121c..f877780b2ac 100644
--- a/gcc/cp/name-lookup.cc
+++ b/gcc/cp/name-lookup.cc
@@ -22,9 +22,11 @@ along with GCC; see the file COPYING3.  If not see
 #define INCLUDE_MEMORY
 #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"
@@ -3479,6 +3481,15 @@ push_local_extern_decl_alias (tree decl)
 	  /* Adjust visibility.  */
 	  determine_visibility (alias);
 	}
+      else if (DECL_P (alias))
+	DECL_ATTRIBUTES (alias)
+	  = targetm.merge_decl_attributes (alias, decl);
+      if (DECL_P (alias))
+	{
+	  symtab_node::remap_sym_alias_target (decl, alias);
+	  DECL_ATTRIBUTES (decl)
+	    = remove_attribute ("sym_alias", DECL_ATTRIBUTES (alias));
+	}
     }
 
   retrofit_lang_decl (decl);
diff --git a/gcc/cp/optimize.cc b/gcc/cp/optimize.cc
index 9e8926e4cc6..4a2e8cb4354 100644
--- a/gcc/cp/optimize.cc
+++ b/gcc/cp/optimize.cc
@@ -528,9 +528,12 @@ maybe_clone_body (tree fn)
       DECL_VISIBILITY_SPECIFIED (clone) = DECL_VISIBILITY_SPECIFIED (fn);
       DECL_DLLIMPORT_P (clone) = DECL_DLLIMPORT_P (fn);
       DECL_ATTRIBUTES (clone) = clone_attrs (DECL_ATTRIBUTES (fn));
+      adjust_clone_attributes (fn, clone, DECL_NAME (clone), true);
       DECL_DISREGARD_INLINE_LIMITS (clone) = DECL_DISREGARD_INLINE_LIMITS (fn);
       set_decl_section_name (clone, fn);
 
+      update_sym_alias_interface (clone);
+
       /* Adjust the parameter names and locations.  */
       parm = DECL_ARGUMENTS (fn);
       clone_parm = DECL_ARGUMENTS (clone);
diff --git a/gcc/cp/rtti.cc b/gcc/cp/rtti.cc
index 7878929c246..4bb7b8c8c6e 100644
--- a/gcc/cp/rtti.cc
+++ b/gcc/cp/rtti.cc
@@ -28,8 +28,10 @@ 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"
+#include "cgraph.h"
 
 /* C++ returns type information to the user in struct type_info
    objects. We also use type information to implement dynamic_cast and
@@ -479,8 +481,13 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
 	  = build_tree_list (get_identifier ("non overlapping"),
 			     NULL_TREE);
       else
+	/* Share the non overlapping attribute, without assuming it's
+	   the only attribute, but assuming it's the last if it's
+	   present.  There may be sym aliases too, and those are not
+	   to be shared.  */
 	DECL_ATTRIBUTES (d)
-	  = DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]);
+	  = lookup_attribute ("non overlapping",
+			      DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]));
 
       /* Mark the variable as undefined -- but remember that we can
 	 define it later if we need to do so.  */
@@ -492,6 +499,16 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
       if (CLASS_TYPE_P (type))
 	CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type)) = d;
 
+      /* Copy sym alias attributes from the type to the rtti obj decl.  */
+      tree *attrs = &DECL_ATTRIBUTES (d);
+      FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+	{
+	  tree attr = tree_cons (TREE_PURPOSE (sym), TREE_VALUE (sym), *attrs);
+	  *attrs = attr;
+	  attrs = &TREE_CHAIN (attr);
+	}
+      create_sym_alias_decls (d);
+
       /* Add decl to the global array of tinfo decls.  */
       vec_safe_push (unemitted_tinfo_decls, d);
     }
@@ -499,6 +516,58 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
   return d;
 }
 
+/* After modifying the attributes of TYPE, check whether tinfo was
+   already created and, if so, add to it any sym alias attributes
+   that were not already present.  */
+
+void
+update_tinfo_sym_alias (tree type)
+{
+  if (!TYPE_SIZE (type) || !CLASS_TYPE_P (type))
+    return;
+
+  tree d = CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type));
+  if (!d)
+    return;
+
+  bool first = true;
+  symtab_node *node = NULL;
+
+  tree *attrs = &DECL_ATTRIBUTES (d);
+  FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (d_sym, *attrs)
+	if (TREE_VALUE (sym) == TREE_VALUE (d_sym))
+	  {
+	    found = true;
+	    break;
+	  }
+
+      if (found)
+	continue;
+
+      tree attr = tree_cons (TREE_PURPOSE (sym),
+			     TREE_VALUE (sym),
+			     *attrs);
+      *attrs = attr;
+      attrs = &TREE_CHAIN (attr);
+
+      if (first)
+	{
+	  first = false;
+	  node = symtab_node::get (d);
+	}
+
+      if (!node)
+	continue;
+
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+      create_sym_alias_decl (d, id);
+    }
+}
+
 /* Return the type_info object for TYPE.  */
 
 tree
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 88f0a1a290a..4aca96de04e 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -4142,6 +4142,43 @@ Function Attributes}, @ref{PowerPC Function Attributes},
 @ref{Nios II Function Attributes}, and @ref{S/390 Function Attributes}
 for details.
 
+@cindex @code{sym_alias} function attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} attribute causes @var{name} to be output as an
+alias to the function, with the same linkage and visibility as the
+function, when the function definition is output, provided that the
+function could be an alias target.  For instance,
+
+@smallexample
+void f (uint64_t) __attribute__ ((__sym_alias__ ("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},
+with the same linkage and visibility.  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.
+
+Aliases introduced with this attribute, such as @samp{f_u64} in the
+example above, are assembly symbol names: they do not undergo C++ name
+mangling, and are not made visible in any scope in the source language.
+They can, however, can be named as alias targets.
+
+This attribute requires assembler and object file support for aliases,
+and may not be available on all targets.
+
 @cindex @code{symver} function attribute
 @item symver ("@var{name2}@@@var{nodename}")
 On ELF targets this attribute creates a symbol version.  The @var{name2} part
@@ -8070,6 +8107,10 @@ will be placed in new, unique sections.
 
 This additional functionality requires Binutils version 2.36 or later.
 
+@cindex @code{sym_alias} variable attribute
+@item sym_alias ("@var{name}")
+See @pxref{Common Function Attributes}.
+
 @cindex @code{uninitialized} variable attribute
 @item uninitialized
 This attribute, attached to a variable with automatic storage, means that
@@ -9165,6 +9206,21 @@ is not supported; that is to say, if a given scalar object can be accessed
 through distinct types that assign a different storage order to it, then the
 behavior is undefined.
 
+@cindex @code{sym_alias} type attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} type 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__ ((__sym_alias__ ("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.
+
 @cindex @code{transparent_union} type attribute
 @item transparent_union
 
diff --git a/gcc/symtab.cc b/gcc/symtab.cc
index 0470509a98d..43ba88d7939 100644
--- a/gcc/symtab.cc
+++ b/gcc/symtab.cc
@@ -1943,6 +1943,44 @@ symtab_node::noninterposable_alias (symtab_node *node, void *data)
   return false;
 }
 
+/* Remap sym alias nodes recorded as aliasing REPLACED to alias
+   REPLACEMENT instead.  */
+
+void
+symtab_node::remap_sym_alias_target (tree replaced, tree replacement)
+{
+  if (!decl_in_symtab_p (replacement)
+      || !symtab_node::get (replacement))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (replaced))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      symtab_node *sym_node = symtab_node::get_for_asmname (id);
+
+      if (!sym_node)
+	{
+	  create_sym_alias_decl (replacement, id);
+	  continue;
+	}
+
+      gcc_assert (!sym_node->analyzed);
+      if (sym_node->alias_target != replaced)
+	continue;
+
+      // sym_node->definition = 0;
+
+      if (VAR_P (replaced))
+	// varpool_node::create_extra_name_alias (sym_node->decl, replacement);
+	varpool_node::create_alias (sym_node->decl, replacement);
+      else
+	cgraph_node::create_same_body_alias (sym_node->decl, replacement);
+	// 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/goacc/declare-1.c b/gcc/testsuite/c-c++-common/goacc/declare-1.c
index 46ee01b6759..f2842893318 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-1.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-1.c
@@ -113,11 +113,11 @@ f_2 (void)
   int va3;
 #pragma acc declare device_resident(va3)
 
-#ifndef __cplusplus
+#if 0
   /* TODO PR90868
 
-     C: "error: variable '[...]' used more than once with '#pragma acc declare'".  */
-#else
+     "error: variable '[...]' used more than once with '#pragma acc declare'".  */
+
   extern int ve0;
 #pragma acc declare create(ve0)
 
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-2.c b/gcc/testsuite/c-c++-common/goacc/declare-2.c
index e2e22be57e9..aec59b69754 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-2.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-2.c
@@ -137,25 +137,25 @@ void
 f_pr90868_2 (void)
 {
   extern int we0;
-#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" } */
 
   extern int we1;
-#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" } */
 
   extern int *we2;
-#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" } */
 
   extern int we3;
-#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" } */
 
   extern int we4;
-#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" } */
 
   extern int we5;
-#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" } */
  
   extern int we6;
-#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" } */
 }
 
 
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
new file mode 100644
index 00000000000..61af50cb552
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
@@ -0,0 +1,39 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+int var_a = 1;
+
+void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+
+void
+foo_a ()
+{
+}
+
+
+int var_b;
+extern int var_b __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+
+void
+foo_b ()
+{
+}
+
+void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+
+
+int var_c __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+
+void __attribute__ ((__sym_alias__ ("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-sym-alias-2.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
new file mode 100644
index 00000000000..67dcd4fd7e1
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
@@ -0,0 +1,90 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+struct s
+{
+  int mem __attribute__ ((__sym_alias__ ("S_MEM"))); /* { dg-warning "attribute ignored" } */
+};
+
+void foo () {
+  extern void bar () __attribute__ ((__sym_alias__ ("FOO_BAR"))); /* ok */
+  int i __attribute__ ((sym_alias ("FOO_i"))); /* { dg-warning "ignored" } */
+  /* ??? X cannot be an alias target; should this be flagged? ... */
+  static int x __attribute__ ((sym_alias ("FOO_x")));
+  static int sx __attribute__ ((alias ("FOO_x"))); /* { dg-warning "ignored" } */
+  extern int xx __attribute__ ((alias ("FOO_x"))); /* { dg-warning "ignored" } */
+}
+
+/* ??? ... or should XR be accepted?  */
+extern int xr __attribute__ ((alias ("FOO_x"))); /* { dg-error "undefined" "" { xfail c++ } } */
+int dr __attribute__ ((alias ("FOO_x"))); /* { dg-error "defined both" } */
+
+
+int j __attribute__ ((sym_alias ("J_var")));
+void __attribute__ ((alias ("J_var")))
+j_fn (); /* { dg-error "between function and variable" } */
+
+
+void __attribute__ ((sym_alias ("K_fn")))
+k () {
+}
+extern int __attribute__ ((alias ("K_fn")))
+k_var; /* { dg-error "between function and variable" } */
+
+
+int l __attribute__ ((sym_alias ("L_fn")));
+
+/* These should be detected and reported, not because the names clash at the
+   source level, but because the asm symbols do.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+void
+L_fn () /* { dg-error "duplicate" "" { xfail *-*-* } } */
+{
+}
+
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __attribute__ ((sym_alias ("M_var")))
+m ()
+{
+}
+
+int M_var; /* { dg-error "duplicate" "" { xfail *-*-* } } */
+
+
+void __attribute__ ((sym_alias ("N_sym")))
+n_fn ()
+{
+}
+
+int __attribute__ ((sym_alias ("N_sym")))
+n_var; /* { dg-error "duplicate" } */
+
+
+int __attribute__ ((sym_alias ("O_sym")))
+o_var;
+
+void __attribute__ ((sym_alias ("O_sym")))
+o_fn () /* { dg-error "duplicate" } */
+{
+}
+
+int __attribute__ ((sym_alias ("P_sym")))
+p_var1;
+
+int __attribute__ ((sym_alias ("P_sym")))
+p_var2; /* { dg-error "duplicate" } */
+
+void __attribute__ ((sym_alias ("Q_sym")))
+q_fn1 ()
+{
+}
+
+void __attribute__ ((sym_alias ("Q_sym")))
+q_fn2 () /* { dg-error "duplicate" } */
+{
+}
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
new file mode 100644
index 00000000000..0052485a30a
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
@@ -0,0 +1,41 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int var_a = 1;
+
+void
+foo_a ()
+{
+  extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+}
+
+#if 0 // __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__ ((__sym_alias__ ("FOOVAR_B")));
+}
+
+int var_b;
+
+void __attribute__ ((__sym_alias__ ("FOOBAR_C")))
+foo_c ()
+{
+  void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+  /* Another sym for var_b.  */
+  extern int var_b __attribute__ ((__sym_alias__ ("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-sym-alias-4.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
new file mode 100644
index 00000000000..2beafa46379
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-require-alias "" } */
+
+int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A"))) = 42;
+
+int __attribute__ ((__sym_alias__ ("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/c-c++-common/torture/attr-sym-alias-5.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
new file mode 100644
index 00000000000..bee90136daf
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
@@ -0,0 +1,54 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+/* { dg-require-visibility "" } */
+
+int __attribute__ ((sym_alias ("A_hid"), visibility ("hidden"))) a;
+
+extern int __attribute__ ((sym_alias ("B_prt"))) b;
+
+int b __attribute__ ((visibility ("protected")));
+
+extern int __attribute__ ((sym_alias ("C_ntr"))) c;
+
+extern int c __attribute__ ((visibility ("internal")));
+
+int c;
+
+static int d __attribute__ ((sym_alias ("D_stt")));
+
+
+extern void __attribute__ ((sym_alias ("F_hid"), visibility ("hidden")))
+f ();
+
+void
+f () {
+}
+
+extern void __attribute__ ((sym_alias ("G_prt")))
+g ();
+
+void __attribute__ ((visibility ("protected")))
+g () {
+}
+
+extern void __attribute__ ((sym_alias ("H_ntr")))
+h ();
+
+void __attribute__ ((visibility ("internal")))
+h ();
+
+void h () {
+}
+
+static void __attribute__ ((sym_alias ("I_stt")))
+i () {
+}
+
+/* { dg-final { scan-assembler {hidden[^\n]*A_hid} } } */
+/* { dg-final { scan-assembler {protected[^\n]*B_prt} } } */
+/* { dg-final { scan-assembler {internal[^\n]*C_ntr} } } */
+/* { dg-final { scan-assembler-not {glob[^\n]*D_stt} } } */
+/* { dg-final { scan-assembler {hidden[^\n]*F_hid} } } */
+/* { dg-final { scan-assembler {protected[^\n]*G_prt} } } */
+/* { dg-final { scan-assembler {internal[^\n]*H_ntr} } } */
+/* { dg-final { scan-assembler-not {glob[^\n]*I_stt} } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
new file mode 100644
index 00000000000..579eda1a473
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
@@ -0,0 +1,72 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+class __attribute__ ((__sym_alias__ ("FOOCLS_A"),
+		      __sym_alias__ ("FOOCLS_A_Dupe"))) foo {
+  static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  void __attribute__ ((__sym_alias__ ("FOOBAR_A"))) bar ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+int foo::var = 1;
+
+foo::foo () {}
+
+void foo::bar () {}
+
+namespace b {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_B"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_B"))) foo ();
+    void __attribute__ ((__sym_alias__ ("FOOBAR_B"))) bar () {}
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_B"))) ~foo() {}
+  };
+
+  int foo::var = 2;
+
+  foo::foo () {
+    void (foo::*pbar)() = &foo::bar;
+  }
+}
+
+namespace c {
+  namespace cd {
+    class __attribute__ ((__sym_alias__ ("FOOCLS_C"))) foo {
+      static int var __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+      __attribute__ ((__sym_alias__ ("FOOCTR_C"))) foo () {
+	void (foo::*pbar)() = &foo::bar;
+      }
+      void __attribute__ ((__sym_alias__ ("FOOBAR_C"))) bar () {}
+      virtual __attribute__ ((__sym_alias__ ("FOODTR_C"))) ~foo() {}
+    };
+
+    int foo::var = 3;
+  }
+}
+
+/* { dg-final { scan-assembler "FOOCLS_A" } } */
+/* { dg-final { scan-assembler "FOOCLS_A_Dupe" } } */
+/* { 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-sym-alias-2.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
new file mode 100644
index 00000000000..6facfcc2cd9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+namespace {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_A"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo ();
+    void __attribute__ ((__sym_alias__ ("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-sym-alias-3.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
new file mode 100644
index 00000000000..42c7288ad4a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
@@ -0,0 +1,83 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+// sym can be applied to template function explicit instantiations.
+
+template <typename T>
+void
+fn(T) {
+};
+
+template void __attribute__ ((__sym_alias__ ("FOOFUN_UINT"))) fn<>(unsigned int);
+template void __attribute__ ((__sym_alias__ ("FOOFUN_LONG"))) fn<>(long);
+
+template<> void __attribute__ ((__sym_alias__ ("FOOFUN_CHAR"))) fn<>(char) {}
+
+
+template <typename T = void>
+struct
+foo {
+  virtual ~foo() {}
+
+  virtual void virtfun() {}
+
+  static void stfun() {}
+  void inlfun() {}
+};
+
+// Explicitly instantiate members before the enclosing class.
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_CHAR_VIRT"))) foo<char>::virtfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_CHAR_TI"))) foo<char>;
+
+// Though they're only output if the enclosing class is.
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_LONG_VIRT"))) foo<long>::virtfun();
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_LONG_TI_X"))) foo<long>;
+
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_VOID_ST"))) foo<void>::stfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_VOID_TI"))) foo<>;
+
+
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_X"))) foo<short>;
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_ST"))) foo<short>::stfun();
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_INL"))) foo<short>::inlfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_D"))) foo<short>;
+
+// Explicit specializations work too.
+
+template <>
+struct  __attribute__ ((__sym_alias__ ("FOOCLS_INT_TI")))
+foo<int>
+{
+  virtual ~foo() {}
+  virtual void __attribute__ ((__sym_alias__ ("FOOCLS_INT_VIRT"))) virtfun() {}
+};
+
+/* { dg-final { scan-assembler "FOOFUN_UINT" } } */
+/* { dg-final { scan-assembler "FOOFUN_LONG" } } */
+/* { dg-final { scan-assembler "FOOFUN_CHAR" } } */
+
+/* { dg-final { scan-assembler "FOOCLS_VOID_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_VOID_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_X" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_INL" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_D" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_TI_X" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_VIRT" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
new file mode 100644
index 00000000000..59b779de35d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+template <typename T = void>
+class
+__attribute__ ((__sym_alias__ ("FOOCLS")))
+foo // { dg-error "duplicate|already" }
+{
+  virtual ~foo() {}
+
+  template <typename U>
+  void
+    __attribute__ ((__sym_alias__ ("FOOTMF")))
+    tmemfun () {} // { dg-error "duplicate|already" }
+};
+
+template <typename T>
+void
+__attribute__ ((__sym_alias__ ("FOOTFN")))
+fn(T) { // { dg-error "duplicate|already" }
+};
+
+template class foo<>;
+template class foo<int>;
+template void foo<>::tmemfun<void>();
+template void foo<int>::tmemfun<void>();
+template void fn<>(int);
+template void fn<>(long);
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
new file mode 100644
index 00000000000..45d59b953c3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
@@ -0,0 +1,14 @@
+/* { dg-do compile { target c++11 } } */
+/* { dg-require-alias "" } */
+
+struct foo {
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+foo::foo () {}
+
+// Make sure the inherited cdtors don't duplicate the syms.
+struct bar : foo {
+  using foo::foo;
+};
diff --git a/gcc/varpool.cc b/gcc/varpool.cc
index e7b51b15e4a..33649906d1d 100644
--- a/gcc/varpool.cc
+++ b/gcc/varpool.cc
@@ -163,6 +163,9 @@ varpool_node::get_create (tree decl)
     }
 
   node->register_symbol ();
+
+  create_sym_alias_decls (decl);
+
   return node;
 }

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [gcc(refs/users/aoliva/heads/testme)] Introduce attribute sym_alias
@ 2023-12-02 17:48 Alexandre Oliva
  0 siblings, 0 replies; 33+ messages in thread
From: Alexandre Oliva @ 2023-12-02 17:48 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:ef841561d516066f076abf36a6d5f27243bfbf00

commit ef841561d516066f076abf36a6d5f27243bfbf00
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Fri Dec 1 14:31:46 2023 -0300

    Introduce attribute sym_alias
    
    This patch introduces an attribute to add extra asm names (aliases)
    for a decl 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 class types, namespace-scoped
    variables and functions, static data members, member functions,
    explicit instantiations and specializations of template functions,
    members and classes.
    
    When applied to constructors or destructor, additional sym aliases
    with _Base and _Del suffixes are defined for variants other than
    complete-object ones.  This changes the assumption that clones always
    carry the same attributes as their abstract declarations, so there is
    now a function to adjust them.
    
    C++ also had a bug in which attributes from local extern declarations
    failed to be propagated to a preexisting corresponding
    namespace-scoped decl.  I've fixed that, and adjusted acc tests that
    distinguished between C and C++ in this regard.
    
    Applying the attribute to class types is only valid in C++, and the
    effect is to attach the alias to the RTTI object associated with the
    class type.
    
    for  gcc/ChangeLog
    
            * attribs.cc: Include cgraph.h.
            (decl_attributes): Allow late introduction of sym_alias in
            types.
            (create_sym_alias_decl, create_sym_alias_decls): New.
            * attribs.h: Declare them.
            (FOR_EACH_SYM_ALIAS): New macro.
            * cgraph.cc (cgraph_node::create): Create sym_alias decls.
            * varpool.cc (varpool_node::get_create): Create sym_alias
            decls.
            * cgraph.h (symtab_node::remap_sym_alias_target): New.
            * symtab.cc (symtab_node::remap_sym_alias_target): Define.
            * cgraphunit.cc (cgraph_node::analyze): Create alias_target
            node if needed.
            (analyze_functions): Fixup visibility of implicit alias only
            after its node is analyzed.
            * doc/extend.texi (sym_alias): Document for variables,
            functions and types.
    
    for  gcc/ada/ChangeLog
    
            * doc/gnat_rm/interfacing_to_other_languages.rst: Mention
            attribute sym_alias to give RTTI symbols mnemonic names.
            * doc/gnat_ugn/the_gnat_compilation_model.rst: Mention
            aliases.  Fix incorrect ref to C1 ctor variant.
    
    for  gcc/c-family/ChangeLog
    
            * c-ada-spec.cc (pp_asm_name): Use first sym_alias if
            available.
            * c-attribs.cc (handle_sym_alias_attribute): New.
            (c_common_attribute_table): Add sym_alias.
            (handle_copy_attribute): Do not copy sym_alias attribute.
    
    for  gcc/c/ChangeLog
    
            * c-decl.cc (duplicate_decls): Remap sym_alias target.
    
    for  gcc/cp/ChangeLog
    
            * class.cc (adjust_clone_attributes): New.
            (copy_fndecl_with_name, build_clone): Call it.
            * cp-tree.h (adjust_clone_attributes): Declare.
            (update_sym_alias_interface): Declare.
            (update_tinfo_sym_alias): Declare.
            * decl.cc (duplicate_decls): Remap sym_alias target.
            Adjust clone attributes.
            (grokfndecl): Tentatively create sym_alias decls after
            adding attributes in e.g. a template member function explicit
            instantiation.
            * decl2.cc (cplus_decl_attributes): Update tinfo sym_alias.
            (copy_interface, update_sym_alias_interface): New.
            (determine_visibility): Update sym_alias interface.
            (tentative_decl_linkage, import_export_decl): Likewise.
            * name-lookup.cc: Include target.h and cgraph.h.
            (push_local_extern_decl_alias): Merge attributes with
            namespace-scoped decl, and drop duplicate sym_alias.
            * optimize.cc (maybe_clone_body): Re-adjust attributes after
            cloning them.  Update sym_alias interface.
            * rtti.cc: Include attribs.h and cgraph.h.
            (get_tinfo_decl): Copy sym_alias attributes from type to tinfo
            decl.  Create sym_alias decls.
            (update_tinfo_sym_alias): New.
    
    for  gcc/testsuite/ChangeLog
    
            * c-c++-common/goacc/declare-1.c: Adjust.
            * c-c++-common/goacc/declare-2.c: Adjust.
            * c-c++-common/torture/attr-sym-alias-1.c: New.
            * c-c++-common/torture/attr-sym-alias-2.c: New.
            * c-c++-common/torture/attr-sym-alias-3.c: New.
            * c-c++-common/torture/attr-sym-alias-4.c: New.
            * c-c++-common/torture/attr-sym-alias-5.c: New.
            * g++.dg/torture/attr-sym-alias-1.C: New.
            * g++.dg/torture/attr-sym-alias-2.C: New.
            * g++.dg/torture/attr-sym-alias-3.C: New.
            * g++.dg/torture/attr-sym-alias-4.C: New.
            * g++.dg/torture/attr-sym-alias-5.C: New.

Diff:
---
 .../doc/gnat_rm/interfacing_to_other_languages.rst |  6 ++
 .../doc/gnat_ugn/the_gnat_compilation_model.rst    | 10 ++-
 gcc/attribs.cc                                     | 66 +++++++++++++++-
 gcc/attribs.h                                      |  7 ++
 gcc/c-family/c-ada-spec.cc                         |  7 ++
 gcc/c-family/c-attribs.cc                          | 33 +++++++-
 gcc/c/c-decl.cc                                    |  2 +
 gcc/cgraph.cc                                      |  2 +
 gcc/cgraph.h                                       |  4 +
 gcc/cgraphunit.cc                                  |  2 +-
 gcc/cp/class.cc                                    | 64 +++++++++++++++
 gcc/cp/cp-tree.h                                   |  4 +
 gcc/cp/decl.cc                                     |  4 +
 gcc/cp/decl2.cc                                    | 50 ++++++++++++
 gcc/cp/name-lookup.cc                              | 11 +++
 gcc/cp/optimize.cc                                 |  3 +
 gcc/cp/rtti.cc                                     | 71 ++++++++++++++++-
 gcc/doc/extend.texi                                | 56 ++++++++++++++
 gcc/symtab.cc                                      | 38 +++++++++
 gcc/testsuite/c-c++-common/goacc/declare-1.c       |  6 +-
 gcc/testsuite/c-c++-common/goacc/declare-2.c       | 14 ++--
 .../c-c++-common/torture/attr-sym-alias-1.c        | 39 ++++++++++
 .../c-c++-common/torture/attr-sym-alias-2.c        | 90 ++++++++++++++++++++++
 .../c-c++-common/torture/attr-sym-alias-3.c        | 41 ++++++++++
 .../c-c++-common/torture/attr-sym-alias-4.c        | 28 +++++++
 .../c-c++-common/torture/attr-sym-alias-5.c        | 54 +++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C    | 72 +++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C    | 26 +++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C    | 83 ++++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C    | 28 +++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C    | 14 ++++
 gcc/varpool.cc                                     |  3 +
 32 files changed, 919 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 ad0be511d48..b8de16ebf7c 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 ``sym_alias`` 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 fd15459203a..20045698962 100644
--- a/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
+++ b/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
@@ -4274,6 +4274,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__ ((__sym_alias__ ("Ctor_For_Animal")))
         Animal() {Age_Count = 0;};
       private:
         int Age_Count;
@@ -4306,6 +4307,7 @@ both Carnivore and Domestic, that is:
         virtual int  Number_Of_Teeth ();
         virtual void Set_Owner (char* Name);
 
+        __attribute__ ((__sym_alias__ ("Ctor_For_Dog"))) // mnemonic alias
         Dog(); // Constructor
       private:
         int  Tooth_Count;
@@ -4344,7 +4346,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;
@@ -4360,7 +4363,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,7 +4385,8 @@ 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.
+mangled name (or an alias) 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.cc b/gcc/attribs.cc
index dd040863567..45610b595b2 100644
--- a/gcc/attribs.cc
+++ b/gcc/attribs.cc
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "target.h"
 #include "tree.h"
+#include "cgraph.h"
 #include "stringpool.h"
 #include "diagnostic-core.h"
 #include "attribs.h"
@@ -830,7 +831,8 @@ decl_attributes (tree *node, tree attributes, int flags,
 
       if (TYPE_P (*anode)
 	  && (flags & (int) ATTR_FLAG_TYPE_IN_PLACE)
-	  && COMPLETE_TYPE_P (*anode))
+	  && COMPLETE_TYPE_P (*anode)
+	  && !is_attribute_p ("sym_alias", name))
 	{
 	  warning (OPT_Wattributes, "type attributes ignored after type is already defined");
 	  continue;
@@ -2672,6 +2674,68 @@ attr_access::array_as_string (tree type) const
   return typstr;
 }
 
+/* Create a sym attribute for DECL to be visible with linkage name ID.  */
+
+tree
+create_sym_alias_decl (tree decl, tree id)
+{
+  const char *attr_str = "sym_alias";
+
+  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;
+
+      tree attr_name = get_identifier (attr_str);
+      error_at (DECL_SOURCE_LOCATION (decl),
+		"duplicate symbol name %qE in %qE attribute of %qD",
+		id, attr_name, decl);
+      inform (DECL_SOURCE_LOCATION (sym_node->decl),
+	      "already used by %qD", sym_node->decl);
+    }
+
+  tree clone = copy_node (decl);
+  DECL_ATTRIBUTES (clone) = remove_attribute (attr_str,
+					      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_extra_name_alias (clone, decl);
+    varpool_node::create_alias (clone, decl);
+  else
+    cgraph_node::create_same_body_alias (clone, decl);
+    // cgraph_node::create_alias (clone, decl);
+
+  return clone;
+}
+
+/* Create decls for all sym aliases requested in DECL's attributes.  */
+
+void
+create_sym_alias_decls (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl)
+      || DECL_ABSTRACT_P (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      create_sym_alias_decl (decl, id);
+    }
+}
+
 #if CHECKING_P
 
 namespace selftest
diff --git a/gcc/attribs.h b/gcc/attribs.h
index fdeebff1cd9..b7a67f73550 100644
--- a/gcc/attribs.h
+++ b/gcc/attribs.h
@@ -404,4 +404,11 @@ extern void init_attr_rdwr_indices (rdwr_map *, tree);
 extern attr_access *get_parm_access (rdwr_map &, tree,
 				     tree = current_function_decl);
 
+extern tree create_sym_alias_decl (tree, tree);
+extern void create_sym_alias_decls (tree);
+
+#define FOR_EACH_SYM_ALIAS(sym, attrs)					\
+  for (tree sym = lookup_attribute ("sym_alias", (attrs));		\
+       sym; sym = lookup_attribute ("sym_alias", TREE_CHAIN (sym)))
+
 #endif // GCC_ATTRIBS_H
diff --git a/gcc/c-family/c-ada-spec.cc b/gcc/c-family/c-ada-spec.cc
index 050994d8416..5042b9cfecd 100644
--- a/gcc/c-family/c-ada-spec.cc
+++ b/gcc/c-family/c-ada-spec.cc
@@ -1431,6 +1431,13 @@ pp_ada_tree_identifier (pretty_printer *buffer, tree node, tree type,
 static void
 pp_asm_name (pretty_printer *buffer, tree t)
 {
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (t))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.cc b/gcc/c-family/c-attribs.cc
index acc09e4b27a..6ec42dc55b6 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -110,7 +110,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_sym_alias_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,
@@ -392,6 +393,8 @@ const struct attribute_spec c_common_gnu_attributes[] =
 			      handle_alias_attribute, NULL },
   { "weakref",                0, 1, true,  false, false, false,
 			      handle_weakref_attribute, NULL },
+  { "sym_alias",              1, 1, false,  false, false, false,
+			      handle_sym_alias_attribute, NULL },
   { "no_instrument_function", 0, 0, true,  false, false, false,
 			      handle_no_instrument_function_attribute,
 			      NULL },
@@ -3092,7 +3095,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
@@ -3102,7 +3105,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
@@ -3112,6 +3115,29 @@ handle_alias_attribute (tree *node, tree name, tree args,
   return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs);
 }
 
+/* Handle a "sym_alias" attribute; arguments as in struct
+   attribute_spec.handler.  */
+
+static tree
+handle_sym_alias_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 (decl_in_symtab_p (node))
+    *no_add_attrs = false;
+  else if (TYPE_P (node) && c_dialect_cxx ())
+    *no_add_attrs = false;
+  else
+    return error_mark_node;
+
+  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.  */
 
@@ -3245,6 +3271,7 @@ handle_copy_attribute (tree *node, tree name, tree args,
 	      || is_attribute_p ("visibility", atname)
 	      || is_attribute_p ("weak", atname)
 	      || is_attribute_p ("weakref", atname)
+	      || is_attribute_p ("sym_alias", atname)
 	      || is_attribute_p ("target_clones", atname))
 	    continue;
 
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 248d1bb3206..83fb1c4cc93 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -3117,6 +3117,8 @@ duplicate_decls (tree newdecl, tree olddecl)
 
   merge_decls (newdecl, olddecl, newtype, oldtype);
 
+  symtab_node::remap_sym_alias_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.cc b/gcc/cgraph.cc
index f93259a8c70..0bef2179e89 100644
--- a/gcc/cgraph.cc
+++ b/gcc/cgraph.cc
@@ -523,6 +523,8 @@ cgraph_node::create (tree decl)
   node->register_symbol ();
   maybe_record_nested_function (node);
 
+  create_sym_alias_decls (decl);
+
   return node;
 }
 
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index 2b320557616..6d61e327fb6 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -327,6 +327,10 @@ public:
   /* Return DECL that alias is aliasing.  */
   inline tree get_alias_target_tree ();
 
+  /* Remap sym alias nodes recorded as aliasing REPLACED to alias REPLACEMENT
+     instead.  */
+  static void remap_sym_alias_target (tree replaced, tree replacement);
+
   /* Set section for symbol and its aliases.  */
   void set_section (const char *section);
 
diff --git a/gcc/cgraphunit.cc b/gcc/cgraphunit.cc
index 9a550a5cce6..a66db795c78 100644
--- a/gcc/cgraphunit.cc
+++ b/gcc/cgraphunit.cc
@@ -1176,7 +1176,7 @@ analyze_functions (bool first_time)
      C++ FE is confused about the COMDAT groups being right.  */
   if (symtab->cpp_implicit_aliases_done)
     FOR_EACH_SYMBOL (node)
-      if (node->cpp_implicit_alias)
+      if (node->cpp_implicit_alias && node->analyzed)
 	  node->fixup_same_cpp_alias_visibility (node->get_alias_target ());
   build_type_inheritance_graph ();
 
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index 6fdb56abfb9..fa8fc63db82 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -4896,6 +4896,68 @@ check_methods (tree t)
     }
 }
 
+/* Adjust sym alias name for CLONE, cloned from FN and named NAME,
+   if it is a cdtor, and drop the sym alias from other clones.  */
+
+void
+adjust_clone_attributes (tree fn, tree clone, tree name, bool skip_copy_p)
+{
+  if (IDENTIFIER_CDTOR_P (name))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	{
+	  found = true;
+	  break;
+	}
+
+      if (found
+	  && (name == complete_ctor_identifier
+	      || name == complete_dtor_identifier))
+	{
+	  /* Reuse the sym alias decls created for the primary cdtor
+	     decl.  */
+	  symtab_node::remap_sym_alias_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);
+
+	  if (!skip_copy_p)
+	    DECL_ATTRIBUTES (clone) = copy_list (DECL_ATTRIBUTES (clone));
+
+	  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	    {
+	      /* We need to copy this even with skip_copy_p, because
+		 even then copying was shallow.  */
+	      TREE_VALUE (sym) = copy_list (TREE_VALUE (sym));
+	      /* Append suf to the sym alias name.  */
+	      tree str = TREE_VALUE (TREE_VALUE (sym));
+	      char *symname = concat (TREE_STRING_POINTER (str), suf, NULL);
+	      str = build_string (TREE_STRING_LENGTH (str) + xlen, symname);
+	      TREE_VALUE (TREE_VALUE (sym)) = str;
+	      free (symname);
+	    }
+
+	  if (symtab_node::get (clone))
+	    create_sym_alias_decls (clone);
+	}
+    }
+  else
+    DECL_ATTRIBUTES (clone)
+      = remove_attribute ("sym_alias", DECL_ATTRIBUTES (clone));
+}
+
 /* FN is constructor, destructor or operator function.  Clone the
    declaration to create a NAME'd variant.  NEED_VTT_PARM_P and
    OMIT_INHERITED_PARMS_P are relevant if it's a cdtor.  */
@@ -5065,6 +5127,8 @@ build_clone (tree fn, tree name, bool need_vtt_parm_p,
   DECL_CHAIN (clone) = DECL_CHAIN (fn);
   DECL_CHAIN (fn) = clone;
 
+  adjust_clone_attributes (fn, clone, name);
+
   return clone;
 }
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 9979c5da623..42de2376763 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5797,6 +5797,8 @@ struct GTY((for_user)) spec_entry
 
 extern int current_class_depth;
 
+void adjust_clone_attributes (tree fn, tree clone, tree name, bool = false);
+
 /* in decl.cc */
 
 /* An array of static vars & fns.  */
@@ -7032,6 +7034,7 @@ extern void do_push_parm_decls			(tree, tree, tree *);
 extern tree do_aggregate_paren_init		(tree, tree);
 
 /* in decl2.cc */
+extern void update_sym_alias_interface		(tree);
 extern void record_mangling			(tree, bool);
 extern void overwrite_mangling			(tree, tree);
 extern void note_mangling_alias			(tree, tree);
@@ -7606,6 +7609,7 @@ extern bool emit_tinfo_decl			(tree);
 extern unsigned get_pseudo_tinfo_index		(tree);
 extern tree get_pseudo_tinfo_type		(unsigned);
 extern tree build_if_nonnull			(tree, tree, tsubst_flags_t);
+extern void update_tinfo_sym_alias		(tree);
 
 /* in search.cc */
 extern tree get_parent_with_private_access 	(tree decl, tree binfo);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 4b685270097..cc7f7a99487 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -3277,6 +3277,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
 	      && TREE_STATIC (olddecl))))
     make_decl_rtl (olddecl);
 
+  symtab_node::remap_sym_alias_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.
@@ -3300,6 +3302,7 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
       FOR_EACH_CLONE (clone, olddecl)
 	{
 	  DECL_ATTRIBUTES (clone) = DECL_ATTRIBUTES (olddecl);
+	  adjust_clone_attributes (olddecl, clone, DECL_NAME (clone));
 	  DECL_PRESERVE_P (clone) |= DECL_PRESERVE_P (olddecl);
 	}
     }
@@ -10838,6 +10841,7 @@ grokfndecl (tree ctype,
     {
       cplus_decl_attributes (&decl, *attrlist, 0);
       *attrlist = NULL_TREE;
+      create_sym_alias_decls (decl);
     }
 
   if (DECL_HAS_CONTRACTS_P (decl))
diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
index 9e666e5eece..c7f009beaf0 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -1844,6 +1844,9 @@ cplus_decl_attributes (tree *decl, tree attributes, int flags)
   if (late_attrs)
     save_template_attributes (late_attrs, decl, flags);
 
+  if (TYPE_P (*decl) && attributes)
+    update_tinfo_sym_alias (*decl);
+
   /* Propagate deprecation out to the template.  */
   if (TREE_DEPRECATED (*decl))
     if (tree ti = get_template_info (*decl))
@@ -2200,6 +2203,47 @@ 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 sym aliases.  */
+
+void
+update_sym_alias_interface (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.  */
 
@@ -3007,6 +3051,8 @@ determine_visibility (tree decl)
        translation unit, we can make the type internal.  */
     constrain_visibility (decl, VISIBILITY_ANON, false);
 
+  update_sym_alias_interface (decl);
+
   /* If visibility changed and DECL already has DECL_RTL, ensure
      symbol flags are updated.  */
   if ((DECL_VISIBILITY (decl) != orig_visibility
@@ -3269,6 +3315,8 @@ tentative_decl_linkage (tree decl)
       else if (VAR_P (decl))
 	maybe_commonize_var (decl);
     }
+
+  update_sym_alias_interface (decl);
 }
 
 /* DECL is a FUNCTION_DECL or VAR_DECL.  If the object file linkage
@@ -3503,6 +3551,8 @@ import_export_decl (tree decl)
     }
 
   DECL_INTERFACE_KNOWN (decl) = 1;
+
+  update_sym_alias_interface (decl);
 }
 
 /* Return an expression that performs the destruction of DECL, which
diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc
index 76f1d44610a..3a19a571256 100644
--- a/gcc/cp/name-lookup.cc
+++ b/gcc/cp/name-lookup.cc
@@ -22,9 +22,11 @@ along with GCC; see the file COPYING3.  If not see
 #define INCLUDE_MEMORY
 #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"
@@ -3675,6 +3677,15 @@ push_local_extern_decl_alias (tree decl)
 	  /* Adjust visibility.  */
 	  determine_visibility (alias);
 	}
+      else if (DECL_P (alias))
+	DECL_ATTRIBUTES (alias)
+	  = targetm.merge_decl_attributes (alias, decl);
+      if (DECL_P (alias))
+	{
+	  symtab_node::remap_sym_alias_target (decl, alias);
+	  DECL_ATTRIBUTES (decl)
+	    = remove_attribute ("sym_alias", DECL_ATTRIBUTES (alias));
+	}
     }
 
   retrofit_lang_decl (decl);
diff --git a/gcc/cp/optimize.cc b/gcc/cp/optimize.cc
index 9e8926e4cc6..4a2e8cb4354 100644
--- a/gcc/cp/optimize.cc
+++ b/gcc/cp/optimize.cc
@@ -528,9 +528,12 @@ maybe_clone_body (tree fn)
       DECL_VISIBILITY_SPECIFIED (clone) = DECL_VISIBILITY_SPECIFIED (fn);
       DECL_DLLIMPORT_P (clone) = DECL_DLLIMPORT_P (fn);
       DECL_ATTRIBUTES (clone) = clone_attrs (DECL_ATTRIBUTES (fn));
+      adjust_clone_attributes (fn, clone, DECL_NAME (clone), true);
       DECL_DISREGARD_INLINE_LIMITS (clone) = DECL_DISREGARD_INLINE_LIMITS (fn);
       set_decl_section_name (clone, fn);
 
+      update_sym_alias_interface (clone);
+
       /* Adjust the parameter names and locations.  */
       parm = DECL_ARGUMENTS (fn);
       clone_parm = DECL_ARGUMENTS (clone);
diff --git a/gcc/cp/rtti.cc b/gcc/cp/rtti.cc
index 7878929c246..4bb7b8c8c6e 100644
--- a/gcc/cp/rtti.cc
+++ b/gcc/cp/rtti.cc
@@ -28,8 +28,10 @@ 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"
+#include "cgraph.h"
 
 /* C++ returns type information to the user in struct type_info
    objects. We also use type information to implement dynamic_cast and
@@ -479,8 +481,13 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
 	  = build_tree_list (get_identifier ("non overlapping"),
 			     NULL_TREE);
       else
+	/* Share the non overlapping attribute, without assuming it's
+	   the only attribute, but assuming it's the last if it's
+	   present.  There may be sym aliases too, and those are not
+	   to be shared.  */
 	DECL_ATTRIBUTES (d)
-	  = DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]);
+	  = lookup_attribute ("non overlapping",
+			      DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]));
 
       /* Mark the variable as undefined -- but remember that we can
 	 define it later if we need to do so.  */
@@ -492,6 +499,16 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
       if (CLASS_TYPE_P (type))
 	CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type)) = d;
 
+      /* Copy sym alias attributes from the type to the rtti obj decl.  */
+      tree *attrs = &DECL_ATTRIBUTES (d);
+      FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+	{
+	  tree attr = tree_cons (TREE_PURPOSE (sym), TREE_VALUE (sym), *attrs);
+	  *attrs = attr;
+	  attrs = &TREE_CHAIN (attr);
+	}
+      create_sym_alias_decls (d);
+
       /* Add decl to the global array of tinfo decls.  */
       vec_safe_push (unemitted_tinfo_decls, d);
     }
@@ -499,6 +516,58 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
   return d;
 }
 
+/* After modifying the attributes of TYPE, check whether tinfo was
+   already created and, if so, add to it any sym alias attributes
+   that were not already present.  */
+
+void
+update_tinfo_sym_alias (tree type)
+{
+  if (!TYPE_SIZE (type) || !CLASS_TYPE_P (type))
+    return;
+
+  tree d = CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type));
+  if (!d)
+    return;
+
+  bool first = true;
+  symtab_node *node = NULL;
+
+  tree *attrs = &DECL_ATTRIBUTES (d);
+  FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (d_sym, *attrs)
+	if (TREE_VALUE (sym) == TREE_VALUE (d_sym))
+	  {
+	    found = true;
+	    break;
+	  }
+
+      if (found)
+	continue;
+
+      tree attr = tree_cons (TREE_PURPOSE (sym),
+			     TREE_VALUE (sym),
+			     *attrs);
+      *attrs = attr;
+      attrs = &TREE_CHAIN (attr);
+
+      if (first)
+	{
+	  first = false;
+	  node = symtab_node::get (d);
+	}
+
+      if (!node)
+	continue;
+
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+      create_sym_alias_decl (d, id);
+    }
+}
+
 /* Return the type_info object for TYPE.  */
 
 tree
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index e9bc9c4fe84..b32aa7bac11 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -4142,6 +4142,43 @@ Function Attributes}, @ref{PowerPC Function Attributes},
 @ref{Nios II Function Attributes}, and @ref{S/390 Function Attributes}
 for details.
 
+@cindex @code{sym_alias} function attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} attribute causes @var{name} to be output as an
+alias to the function, with the same linkage and visibility as the
+function, when the function definition is output, provided that the
+function could be an alias target.  For instance,
+
+@smallexample
+void f (uint64_t) __attribute__ ((__sym_alias__ ("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},
+with the same linkage and visibility.  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.
+
+Aliases introduced with this attribute, such as @samp{f_u64} in the
+example above, are assembly symbol names: they do not undergo C++ name
+mangling, and are not made visible in any scope in the source language.
+They can, however, can be named as alias targets.
+
+This attribute requires assembler and object file support for aliases,
+and may not be available on all targets.
+
 @cindex @code{symver} function attribute
 @item symver ("@var{name2}@@@var{nodename}")
 On ELF targets this attribute creates a symbol version.  The @var{name2} part
@@ -8069,6 +8106,10 @@ will be placed in new, unique sections.
 
 This additional functionality requires Binutils version 2.36 or later.
 
+@cindex @code{sym_alias} variable attribute
+@item sym_alias ("@var{name}")
+See @pxref{Common Function Attributes}.
+
 @cindex @code{uninitialized} variable attribute
 @item uninitialized
 This attribute, attached to a variable with automatic storage, means that
@@ -9164,6 +9205,21 @@ is not supported; that is to say, if a given scalar object can be accessed
 through distinct types that assign a different storage order to it, then the
 behavior is undefined.
 
+@cindex @code{sym_alias} type attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} type 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__ ((__sym_alias__ ("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.
+
 @cindex @code{transparent_union} type attribute
 @item transparent_union
 
diff --git a/gcc/symtab.cc b/gcc/symtab.cc
index 0470509a98d..43ba88d7939 100644
--- a/gcc/symtab.cc
+++ b/gcc/symtab.cc
@@ -1943,6 +1943,44 @@ symtab_node::noninterposable_alias (symtab_node *node, void *data)
   return false;
 }
 
+/* Remap sym alias nodes recorded as aliasing REPLACED to alias
+   REPLACEMENT instead.  */
+
+void
+symtab_node::remap_sym_alias_target (tree replaced, tree replacement)
+{
+  if (!decl_in_symtab_p (replacement)
+      || !symtab_node::get (replacement))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (replaced))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      symtab_node *sym_node = symtab_node::get_for_asmname (id);
+
+      if (!sym_node)
+	{
+	  create_sym_alias_decl (replacement, id);
+	  continue;
+	}
+
+      gcc_assert (!sym_node->analyzed);
+      if (sym_node->alias_target != replaced)
+	continue;
+
+      // sym_node->definition = 0;
+
+      if (VAR_P (replaced))
+	// varpool_node::create_extra_name_alias (sym_node->decl, replacement);
+	varpool_node::create_alias (sym_node->decl, replacement);
+      else
+	cgraph_node::create_same_body_alias (sym_node->decl, replacement);
+	// 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/goacc/declare-1.c b/gcc/testsuite/c-c++-common/goacc/declare-1.c
index 46ee01b6759..f2842893318 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-1.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-1.c
@@ -113,11 +113,11 @@ f_2 (void)
   int va3;
 #pragma acc declare device_resident(va3)
 
-#ifndef __cplusplus
+#if 0
   /* TODO PR90868
 
-     C: "error: variable '[...]' used more than once with '#pragma acc declare'".  */
-#else
+     "error: variable '[...]' used more than once with '#pragma acc declare'".  */
+
   extern int ve0;
 #pragma acc declare create(ve0)
 
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-2.c b/gcc/testsuite/c-c++-common/goacc/declare-2.c
index e2e22be57e9..aec59b69754 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-2.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-2.c
@@ -137,25 +137,25 @@ void
 f_pr90868_2 (void)
 {
   extern int we0;
-#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" } */
 
   extern int we1;
-#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" } */
 
   extern int *we2;
-#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" } */
 
   extern int we3;
-#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" } */
 
   extern int we4;
-#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" } */
 
   extern int we5;
-#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" } */
  
   extern int we6;
-#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" } */
 }
 
 
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
new file mode 100644
index 00000000000..61af50cb552
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
@@ -0,0 +1,39 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+int var_a = 1;
+
+void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+
+void
+foo_a ()
+{
+}
+
+
+int var_b;
+extern int var_b __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+
+void
+foo_b ()
+{
+}
+
+void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+
+
+int var_c __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+
+void __attribute__ ((__sym_alias__ ("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-sym-alias-2.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
new file mode 100644
index 00000000000..67dcd4fd7e1
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
@@ -0,0 +1,90 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+struct s
+{
+  int mem __attribute__ ((__sym_alias__ ("S_MEM"))); /* { dg-warning "attribute ignored" } */
+};
+
+void foo () {
+  extern void bar () __attribute__ ((__sym_alias__ ("FOO_BAR"))); /* ok */
+  int i __attribute__ ((sym_alias ("FOO_i"))); /* { dg-warning "ignored" } */
+  /* ??? X cannot be an alias target; should this be flagged? ... */
+  static int x __attribute__ ((sym_alias ("FOO_x")));
+  static int sx __attribute__ ((alias ("FOO_x"))); /* { dg-warning "ignored" } */
+  extern int xx __attribute__ ((alias ("FOO_x"))); /* { dg-warning "ignored" } */
+}
+
+/* ??? ... or should XR be accepted?  */
+extern int xr __attribute__ ((alias ("FOO_x"))); /* { dg-error "undefined" "" { xfail c++ } } */
+int dr __attribute__ ((alias ("FOO_x"))); /* { dg-error "defined both" } */
+
+
+int j __attribute__ ((sym_alias ("J_var")));
+void __attribute__ ((alias ("J_var")))
+j_fn (); /* { dg-error "between function and variable" } */
+
+
+void __attribute__ ((sym_alias ("K_fn")))
+k () {
+}
+extern int __attribute__ ((alias ("K_fn")))
+k_var; /* { dg-error "between function and variable" } */
+
+
+int l __attribute__ ((sym_alias ("L_fn")));
+
+/* These should be detected and reported, not because the names clash at the
+   source level, but because the asm symbols do.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+void
+L_fn () /* { dg-error "duplicate" "" { xfail *-*-* } } */
+{
+}
+
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __attribute__ ((sym_alias ("M_var")))
+m ()
+{
+}
+
+int M_var; /* { dg-error "duplicate" "" { xfail *-*-* } } */
+
+
+void __attribute__ ((sym_alias ("N_sym")))
+n_fn ()
+{
+}
+
+int __attribute__ ((sym_alias ("N_sym")))
+n_var; /* { dg-error "duplicate" } */
+
+
+int __attribute__ ((sym_alias ("O_sym")))
+o_var;
+
+void __attribute__ ((sym_alias ("O_sym")))
+o_fn () /* { dg-error "duplicate" } */
+{
+}
+
+int __attribute__ ((sym_alias ("P_sym")))
+p_var1;
+
+int __attribute__ ((sym_alias ("P_sym")))
+p_var2; /* { dg-error "duplicate" } */
+
+void __attribute__ ((sym_alias ("Q_sym")))
+q_fn1 ()
+{
+}
+
+void __attribute__ ((sym_alias ("Q_sym")))
+q_fn2 () /* { dg-error "duplicate" } */
+{
+}
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
new file mode 100644
index 00000000000..0052485a30a
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
@@ -0,0 +1,41 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int var_a = 1;
+
+void
+foo_a ()
+{
+  extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+}
+
+#if 0 // __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__ ((__sym_alias__ ("FOOVAR_B")));
+}
+
+int var_b;
+
+void __attribute__ ((__sym_alias__ ("FOOBAR_C")))
+foo_c ()
+{
+  void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+  /* Another sym for var_b.  */
+  extern int var_b __attribute__ ((__sym_alias__ ("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-sym-alias-4.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
new file mode 100644
index 00000000000..2beafa46379
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-require-alias "" } */
+
+int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A"))) = 42;
+
+int __attribute__ ((__sym_alias__ ("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/c-c++-common/torture/attr-sym-alias-5.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
new file mode 100644
index 00000000000..bee90136daf
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
@@ -0,0 +1,54 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+/* { dg-require-visibility "" } */
+
+int __attribute__ ((sym_alias ("A_hid"), visibility ("hidden"))) a;
+
+extern int __attribute__ ((sym_alias ("B_prt"))) b;
+
+int b __attribute__ ((visibility ("protected")));
+
+extern int __attribute__ ((sym_alias ("C_ntr"))) c;
+
+extern int c __attribute__ ((visibility ("internal")));
+
+int c;
+
+static int d __attribute__ ((sym_alias ("D_stt")));
+
+
+extern void __attribute__ ((sym_alias ("F_hid"), visibility ("hidden")))
+f ();
+
+void
+f () {
+}
+
+extern void __attribute__ ((sym_alias ("G_prt")))
+g ();
+
+void __attribute__ ((visibility ("protected")))
+g () {
+}
+
+extern void __attribute__ ((sym_alias ("H_ntr")))
+h ();
+
+void __attribute__ ((visibility ("internal")))
+h ();
+
+void h () {
+}
+
+static void __attribute__ ((sym_alias ("I_stt")))
+i () {
+}
+
+/* { dg-final { scan-assembler {hidden[^\n]*A_hid} } } */
+/* { dg-final { scan-assembler {protected[^\n]*B_prt} } } */
+/* { dg-final { scan-assembler {internal[^\n]*C_ntr} } } */
+/* { dg-final { scan-assembler-not {glob[^\n]*D_stt} } } */
+/* { dg-final { scan-assembler {hidden[^\n]*F_hid} } } */
+/* { dg-final { scan-assembler {protected[^\n]*G_prt} } } */
+/* { dg-final { scan-assembler {internal[^\n]*H_ntr} } } */
+/* { dg-final { scan-assembler-not {glob[^\n]*I_stt} } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
new file mode 100644
index 00000000000..579eda1a473
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
@@ -0,0 +1,72 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+class __attribute__ ((__sym_alias__ ("FOOCLS_A"),
+		      __sym_alias__ ("FOOCLS_A_Dupe"))) foo {
+  static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  void __attribute__ ((__sym_alias__ ("FOOBAR_A"))) bar ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+int foo::var = 1;
+
+foo::foo () {}
+
+void foo::bar () {}
+
+namespace b {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_B"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_B"))) foo ();
+    void __attribute__ ((__sym_alias__ ("FOOBAR_B"))) bar () {}
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_B"))) ~foo() {}
+  };
+
+  int foo::var = 2;
+
+  foo::foo () {
+    void (foo::*pbar)() = &foo::bar;
+  }
+}
+
+namespace c {
+  namespace cd {
+    class __attribute__ ((__sym_alias__ ("FOOCLS_C"))) foo {
+      static int var __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+      __attribute__ ((__sym_alias__ ("FOOCTR_C"))) foo () {
+	void (foo::*pbar)() = &foo::bar;
+      }
+      void __attribute__ ((__sym_alias__ ("FOOBAR_C"))) bar () {}
+      virtual __attribute__ ((__sym_alias__ ("FOODTR_C"))) ~foo() {}
+    };
+
+    int foo::var = 3;
+  }
+}
+
+/* { dg-final { scan-assembler "FOOCLS_A" } } */
+/* { dg-final { scan-assembler "FOOCLS_A_Dupe" } } */
+/* { 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-sym-alias-2.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
new file mode 100644
index 00000000000..6facfcc2cd9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+namespace {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_A"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo ();
+    void __attribute__ ((__sym_alias__ ("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-sym-alias-3.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
new file mode 100644
index 00000000000..42c7288ad4a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
@@ -0,0 +1,83 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+// sym can be applied to template function explicit instantiations.
+
+template <typename T>
+void
+fn(T) {
+};
+
+template void __attribute__ ((__sym_alias__ ("FOOFUN_UINT"))) fn<>(unsigned int);
+template void __attribute__ ((__sym_alias__ ("FOOFUN_LONG"))) fn<>(long);
+
+template<> void __attribute__ ((__sym_alias__ ("FOOFUN_CHAR"))) fn<>(char) {}
+
+
+template <typename T = void>
+struct
+foo {
+  virtual ~foo() {}
+
+  virtual void virtfun() {}
+
+  static void stfun() {}
+  void inlfun() {}
+};
+
+// Explicitly instantiate members before the enclosing class.
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_CHAR_VIRT"))) foo<char>::virtfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_CHAR_TI"))) foo<char>;
+
+// Though they're only output if the enclosing class is.
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_LONG_VIRT"))) foo<long>::virtfun();
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_LONG_TI_X"))) foo<long>;
+
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_VOID_ST"))) foo<void>::stfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_VOID_TI"))) foo<>;
+
+
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_X"))) foo<short>;
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_ST"))) foo<short>::stfun();
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_INL"))) foo<short>::inlfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_D"))) foo<short>;
+
+// Explicit specializations work too.
+
+template <>
+struct  __attribute__ ((__sym_alias__ ("FOOCLS_INT_TI")))
+foo<int>
+{
+  virtual ~foo() {}
+  virtual void __attribute__ ((__sym_alias__ ("FOOCLS_INT_VIRT"))) virtfun() {}
+};
+
+/* { dg-final { scan-assembler "FOOFUN_UINT" } } */
+/* { dg-final { scan-assembler "FOOFUN_LONG" } } */
+/* { dg-final { scan-assembler "FOOFUN_CHAR" } } */
+
+/* { dg-final { scan-assembler "FOOCLS_VOID_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_VOID_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_X" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_INL" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_D" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_TI_X" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_VIRT" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
new file mode 100644
index 00000000000..59b779de35d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+template <typename T = void>
+class
+__attribute__ ((__sym_alias__ ("FOOCLS")))
+foo // { dg-error "duplicate|already" }
+{
+  virtual ~foo() {}
+
+  template <typename U>
+  void
+    __attribute__ ((__sym_alias__ ("FOOTMF")))
+    tmemfun () {} // { dg-error "duplicate|already" }
+};
+
+template <typename T>
+void
+__attribute__ ((__sym_alias__ ("FOOTFN")))
+fn(T) { // { dg-error "duplicate|already" }
+};
+
+template class foo<>;
+template class foo<int>;
+template void foo<>::tmemfun<void>();
+template void foo<int>::tmemfun<void>();
+template void fn<>(int);
+template void fn<>(long);
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
new file mode 100644
index 00000000000..45d59b953c3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
@@ -0,0 +1,14 @@
+/* { dg-do compile { target c++11 } } */
+/* { dg-require-alias "" } */
+
+struct foo {
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+foo::foo () {}
+
+// Make sure the inherited cdtors don't duplicate the syms.
+struct bar : foo {
+  using foo::foo;
+};
diff --git a/gcc/varpool.cc b/gcc/varpool.cc
index e7b51b15e4a..33649906d1d 100644
--- a/gcc/varpool.cc
+++ b/gcc/varpool.cc
@@ -163,6 +163,9 @@ varpool_node::get_create (tree decl)
     }
 
   node->register_symbol ();
+
+  create_sym_alias_decls (decl);
+
   return node;
 }

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [gcc(refs/users/aoliva/heads/testme)] Introduce attribute sym_alias
@ 2023-12-01 23:42 Alexandre Oliva
  0 siblings, 0 replies; 33+ messages in thread
From: Alexandre Oliva @ 2023-12-01 23:42 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:f12dd02228acd65623bf2646f4eb115cf61121f6

commit f12dd02228acd65623bf2646f4eb115cf61121f6
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Fri Dec 1 14:31:46 2023 -0300

    Introduce attribute sym_alias
    
    This patch introduces an attribute to add extra asm names (aliases)
    for a decl 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 class types, namespace-scoped
    variables and functions, static data members, member functions,
    explicit instantiations and specializations of template functions,
    members and classes.
    
    When applied to constructors or destructor, additional sym aliases
    with _Base and _Del suffixes are defined for variants other than
    complete-object ones.  This changes the assumption that clones always
    carry the same attributes as their abstract declarations, so there is
    now a function to adjust them.
    
    C++ also had a bug in which attributes from local extern declarations
    failed to be propagated to a preexisting corresponding
    namespace-scoped decl.  I've fixed that, and adjusted acc tests that
    distinguished between C and C++ in this regard.
    
    Applying the attribute to class types is only valid in C++, and the
    effect is to attach the alias to the RTTI object associated with the
    class type.
    
    for  gcc/ChangeLog
    
            * attribs.cc: Include cgraph.h.
            (decl_attributes): Allow late introduction of sym_alias in
            types.
            (create_sym_alias_decl, create_sym_alias_decls): New.
            * attribs.h: Declare them.
            (FOR_EACH_SYM_ALIAS): New macro.
            * cgraph.cc (cgraph_node::create): Create sym_alias decls.
            * varpool.cc (varpool_node::get_create): Create sym_alias
            decls.
            * cgraph.h (symtab_node::remap_sym_alias_target): New.
            * symtab.cc (symtab_node::remap_sym_alias_target): Define.
            * cgraphunit.cc (cgraph_node::analyze): Create alias_target
            node if needed.
            (analyze_functions): Fixup visibility of implicit alias only
            after its node is analyzed.
            * doc/extend.texi (sym_alias): Document for variables,
            functions and types.
    
    for  gcc/ada/ChangeLog
    
            * doc/gnat_rm/interfacing_to_other_languages.rst: Mention
            attribute sym_alias to give RTTI symbols mnemonic names.
            * doc/gnat_ugn/the_gnat_compilation_model.rst: Mention
            aliases.  Fix incorrect ref to C1 ctor variant.
    
    for  gcc/c-family/ChangeLog
    
            * c-ada-spec.cc (pp_asm_name): Use first sym_alias if
            available.
            * c-attribs.cc (handle_sym_alias_attribute): New.
            (c_common_attribute_table): Add sym_alias.
            (handle_copy_attribute): Do not copy sym_alias attribute.
    
    for  gcc/c/ChangeLog
    
            * c-decl.cc (duplicate_decls): Remap sym_alias target.
    
    for  gcc/cp/ChangeLog
    
            * class.cc (adjust_clone_attributes): New.
            (copy_fndecl_with_name, build_clone): Call it.
            * cp-tree.h (adjust_clone_attributes): Declare.
            (update_sym_alias_interface): Declare.
            (update_tinfo_sym_alias): Declare.
            * decl.cc (duplicate_decls): Remap sym_alias target.
            Adjust clone attributes.
            (grokfndecl): Tentatively create sym_alias decls after
            adding attributes in e.g. a template member function explicit
            instantiation.
            * decl2.cc (cplus_decl_attributes): Update tinfo sym_alias.
            (copy_interface, update_sym_alias_interface): New.
            (determine_visibility): Update sym_alias interface.
            (tentative_decl_linkage, import_export_decl): Likewise.
            * name-lookup.cc: Include target.h and cgraph.h.
            (push_local_extern_decl_alias): Merge attributes with
            namespace-scoped decl, and drop duplicate sym_alias.
            * optimize.cc (maybe_clone_body): Re-adjust attributes after
            cloning them.  Update sym_alias interface.
            * rtti.cc: Include attribs.h and cgraph.h.
            (get_tinfo_decl): Copy sym_alias attributes from type to tinfo
            decl.  Create sym_alias decls.
            (update_tinfo_sym_alias): New.
    
    for  gcc/testsuite/ChangeLog
    
            * c-c++-common/goacc/declare-1.c: Adjust.
            * c-c++-common/goacc/declare-2.c: Adjust.
            * c-c++-common/torture/attr-sym-alias-1.c: New.
            * c-c++-common/torture/attr-sym-alias-2.c: New.
            * c-c++-common/torture/attr-sym-alias-3.c: New.
            * c-c++-common/torture/attr-sym-alias-4.c: New.
            * c-c++-common/torture/attr-sym-alias-5.c: New.
            * g++.dg/torture/attr-sym-alias-1.C: New.
            * g++.dg/torture/attr-sym-alias-2.C: New.
            * g++.dg/torture/attr-sym-alias-3.C: New.
            * g++.dg/torture/attr-sym-alias-4.C: New.
            * g++.dg/torture/attr-sym-alias-5.C: New.

Diff:
---
 .../doc/gnat_rm/interfacing_to_other_languages.rst |  6 ++
 .../doc/gnat_ugn/the_gnat_compilation_model.rst    | 10 ++-
 gcc/attribs.cc                                     | 66 +++++++++++++++-
 gcc/attribs.h                                      |  7 ++
 gcc/c-family/c-ada-spec.cc                         |  7 ++
 gcc/c-family/c-attribs.cc                          | 33 +++++++-
 gcc/c/c-decl.cc                                    |  2 +
 gcc/cgraph.cc                                      |  2 +
 gcc/cgraph.h                                       |  4 +
 gcc/cgraphunit.cc                                  |  2 +-
 gcc/cp/class.cc                                    | 64 +++++++++++++++
 gcc/cp/cp-tree.h                                   |  4 +
 gcc/cp/decl.cc                                     |  4 +
 gcc/cp/decl2.cc                                    | 50 ++++++++++++
 gcc/cp/name-lookup.cc                              | 11 +++
 gcc/cp/optimize.cc                                 |  3 +
 gcc/cp/rtti.cc                                     | 71 ++++++++++++++++-
 gcc/doc/extend.texi                                | 56 ++++++++++++++
 gcc/symtab.cc                                      | 38 +++++++++
 gcc/testsuite/c-c++-common/goacc/declare-1.c       |  6 +-
 gcc/testsuite/c-c++-common/goacc/declare-2.c       | 14 ++--
 .../c-c++-common/torture/attr-sym-alias-1.c        | 39 ++++++++++
 .../c-c++-common/torture/attr-sym-alias-2.c        | 90 ++++++++++++++++++++++
 .../c-c++-common/torture/attr-sym-alias-3.c        | 41 ++++++++++
 .../c-c++-common/torture/attr-sym-alias-4.c        | 28 +++++++
 .../c-c++-common/torture/attr-sym-alias-5.c        | 54 +++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C    | 72 +++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C    | 26 +++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C    | 83 ++++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C    | 28 +++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C    | 14 ++++
 gcc/varpool.cc                                     |  3 +
 32 files changed, 919 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 ad0be511d48..b8de16ebf7c 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 ``sym_alias`` 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 fd15459203a..20045698962 100644
--- a/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
+++ b/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
@@ -4274,6 +4274,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__ ((__sym_alias__ ("Ctor_For_Animal")))
         Animal() {Age_Count = 0;};
       private:
         int Age_Count;
@@ -4306,6 +4307,7 @@ both Carnivore and Domestic, that is:
         virtual int  Number_Of_Teeth ();
         virtual void Set_Owner (char* Name);
 
+        __attribute__ ((__sym_alias__ ("Ctor_For_Dog"))) // mnemonic alias
         Dog(); // Constructor
       private:
         int  Tooth_Count;
@@ -4344,7 +4346,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;
@@ -4360,7 +4363,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,7 +4385,8 @@ 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.
+mangled name (or an alias) 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.cc b/gcc/attribs.cc
index c7209c26acc..c75ca6974cb 100644
--- a/gcc/attribs.cc
+++ b/gcc/attribs.cc
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "target.h"
 #include "tree.h"
+#include "cgraph.h"
 #include "stringpool.h"
 #include "diagnostic-core.h"
 #include "attribs.h"
@@ -825,7 +826,8 @@ decl_attributes (tree *node, tree attributes, int flags,
 
       if (TYPE_P (*anode)
 	  && (flags & (int) ATTR_FLAG_TYPE_IN_PLACE)
-	  && COMPLETE_TYPE_P (*anode))
+	  && COMPLETE_TYPE_P (*anode)
+	  && !is_attribute_p ("sym_alias", name))
 	{
 	  warning (OPT_Wattributes, "type attributes ignored after type is already defined");
 	  continue;
@@ -2640,6 +2642,68 @@ attr_access::array_as_string (tree type) const
   return typstr;
 }
 
+/* Create a sym attribute for DECL to be visible with linkage name ID.  */
+
+tree
+create_sym_alias_decl (tree decl, tree id)
+{
+  const char *attr_str = "sym_alias";
+
+  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;
+
+      tree attr_name = get_identifier (attr_str);
+      error_at (DECL_SOURCE_LOCATION (decl),
+		"duplicate symbol name %qE in %qE attribute of %qD",
+		id, attr_name, decl);
+      inform (DECL_SOURCE_LOCATION (sym_node->decl),
+	      "already used by %qD", sym_node->decl);
+    }
+
+  tree clone = copy_node (decl);
+  DECL_ATTRIBUTES (clone) = remove_attribute (attr_str,
+					      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_extra_name_alias (clone, decl);
+    varpool_node::create_alias (clone, decl);
+  else
+    cgraph_node::create_same_body_alias (clone, decl);
+    // cgraph_node::create_alias (clone, decl);
+
+  return clone;
+}
+
+/* Create decls for all sym aliases requested in DECL's attributes.  */
+
+void
+create_sym_alias_decls (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl)
+      || DECL_ABSTRACT_P (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      create_sym_alias_decl (decl, id);
+    }
+}
+
 #if CHECKING_P
 
 namespace selftest
diff --git a/gcc/attribs.h b/gcc/attribs.h
index 84a43658a70..156f2c20588 100644
--- a/gcc/attribs.h
+++ b/gcc/attribs.h
@@ -398,4 +398,11 @@ extern void init_attr_rdwr_indices (rdwr_map *, tree);
 extern attr_access *get_parm_access (rdwr_map &, tree,
 				     tree = current_function_decl);
 
+extern tree create_sym_alias_decl (tree, tree);
+extern void create_sym_alias_decls (tree);
+
+#define FOR_EACH_SYM_ALIAS(sym, attrs)					\
+  for (tree sym = lookup_attribute ("sym_alias", (attrs));		\
+       sym; sym = lookup_attribute ("sym_alias", TREE_CHAIN (sym)))
+
 #endif // GCC_ATTRIBS_H
diff --git a/gcc/c-family/c-ada-spec.cc b/gcc/c-family/c-ada-spec.cc
index 050994d8416..5042b9cfecd 100644
--- a/gcc/c-family/c-ada-spec.cc
+++ b/gcc/c-family/c-ada-spec.cc
@@ -1431,6 +1431,13 @@ pp_ada_tree_identifier (pretty_printer *buffer, tree node, tree type,
 static void
 pp_asm_name (pretty_printer *buffer, tree t)
 {
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (t))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.cc b/gcc/c-family/c-attribs.cc
index 2b20e58c922..392437eab3b 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -108,7 +108,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_sym_alias_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,
@@ -388,6 +389,8 @@ const struct attribute_spec c_common_attribute_table[] =
 			      handle_alias_attribute, NULL },
   { "weakref",                0, 1, true,  false, false, false,
 			      handle_weakref_attribute, NULL },
+  { "sym_alias",              1, 1, false,  false, false, false,
+			      handle_sym_alias_attribute, NULL },
   { "no_instrument_function", 0, 0, true,  false, false, false,
 			      handle_no_instrument_function_attribute,
 			      NULL },
@@ -3002,7 +3005,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
@@ -3012,7 +3015,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
@@ -3022,6 +3025,29 @@ handle_alias_attribute (tree *node, tree name, tree args,
   return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs);
 }
 
+/* Handle a "sym_alias" attribute; arguments as in struct
+   attribute_spec.handler.  */
+
+static tree
+handle_sym_alias_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 (decl_in_symtab_p (node))
+    *no_add_attrs = false;
+  else if (TYPE_P (node) && c_dialect_cxx ())
+    *no_add_attrs = false;
+  else
+    return error_mark_node;
+
+  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.  */
 
@@ -3155,6 +3181,7 @@ handle_copy_attribute (tree *node, tree name, tree args,
 	      || is_attribute_p ("visibility", atname)
 	      || is_attribute_p ("weak", atname)
 	      || is_attribute_p ("weakref", atname)
+	      || is_attribute_p ("sym_alias", atname)
 	      || is_attribute_p ("target_clones", atname))
 	    continue;
 
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index d388e123704..c5a7e56f4ec 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -3117,6 +3117,8 @@ duplicate_decls (tree newdecl, tree olddecl)
 
   merge_decls (newdecl, olddecl, newtype, oldtype);
 
+  symtab_node::remap_sym_alias_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.cc b/gcc/cgraph.cc
index f93259a8c70..0bef2179e89 100644
--- a/gcc/cgraph.cc
+++ b/gcc/cgraph.cc
@@ -523,6 +523,8 @@ cgraph_node::create (tree decl)
   node->register_symbol ();
   maybe_record_nested_function (node);
 
+  create_sym_alias_decls (decl);
+
   return node;
 }
 
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index cfdd9f693a8..cb5ac01f7e2 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -327,6 +327,10 @@ public:
   /* Return DECL that alias is aliasing.  */
   inline tree get_alias_target_tree ();
 
+  /* Remap sym alias nodes recorded as aliasing REPLACED to alias REPLACEMENT
+     instead.  */
+  static void remap_sym_alias_target (tree replaced, tree replacement);
+
   /* Set section for symbol and its aliases.  */
   void set_section (const char *section);
 
diff --git a/gcc/cgraphunit.cc b/gcc/cgraphunit.cc
index bccd2f2abb5..eb2d05094e9 100644
--- a/gcc/cgraphunit.cc
+++ b/gcc/cgraphunit.cc
@@ -1175,7 +1175,7 @@ analyze_functions (bool first_time)
      C++ FE is confused about the COMDAT groups being right.  */
   if (symtab->cpp_implicit_aliases_done)
     FOR_EACH_SYMBOL (node)
-      if (node->cpp_implicit_alias)
+      if (node->cpp_implicit_alias && node->analyzed)
 	  node->fixup_same_cpp_alias_visibility (node->get_alias_target ());
   build_type_inheritance_graph ();
 
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index 6fdb56abfb9..fa8fc63db82 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -4896,6 +4896,68 @@ check_methods (tree t)
     }
 }
 
+/* Adjust sym alias name for CLONE, cloned from FN and named NAME,
+   if it is a cdtor, and drop the sym alias from other clones.  */
+
+void
+adjust_clone_attributes (tree fn, tree clone, tree name, bool skip_copy_p)
+{
+  if (IDENTIFIER_CDTOR_P (name))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	{
+	  found = true;
+	  break;
+	}
+
+      if (found
+	  && (name == complete_ctor_identifier
+	      || name == complete_dtor_identifier))
+	{
+	  /* Reuse the sym alias decls created for the primary cdtor
+	     decl.  */
+	  symtab_node::remap_sym_alias_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);
+
+	  if (!skip_copy_p)
+	    DECL_ATTRIBUTES (clone) = copy_list (DECL_ATTRIBUTES (clone));
+
+	  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	    {
+	      /* We need to copy this even with skip_copy_p, because
+		 even then copying was shallow.  */
+	      TREE_VALUE (sym) = copy_list (TREE_VALUE (sym));
+	      /* Append suf to the sym alias name.  */
+	      tree str = TREE_VALUE (TREE_VALUE (sym));
+	      char *symname = concat (TREE_STRING_POINTER (str), suf, NULL);
+	      str = build_string (TREE_STRING_LENGTH (str) + xlen, symname);
+	      TREE_VALUE (TREE_VALUE (sym)) = str;
+	      free (symname);
+	    }
+
+	  if (symtab_node::get (clone))
+	    create_sym_alias_decls (clone);
+	}
+    }
+  else
+    DECL_ATTRIBUTES (clone)
+      = remove_attribute ("sym_alias", DECL_ATTRIBUTES (clone));
+}
+
 /* FN is constructor, destructor or operator function.  Clone the
    declaration to create a NAME'd variant.  NEED_VTT_PARM_P and
    OMIT_INHERITED_PARMS_P are relevant if it's a cdtor.  */
@@ -5065,6 +5127,8 @@ build_clone (tree fn, tree name, bool need_vtt_parm_p,
   DECL_CHAIN (clone) = DECL_CHAIN (fn);
   DECL_CHAIN (fn) = clone;
 
+  adjust_clone_attributes (fn, clone, name);
+
   return clone;
 }
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 8809842da3f..fb2b3f4db18 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5791,6 +5791,8 @@ struct GTY((for_user)) spec_entry
 
 extern int current_class_depth;
 
+void adjust_clone_attributes (tree fn, tree clone, tree name, bool = false);
+
 /* in decl.cc */
 
 /* An array of static vars & fns.  */
@@ -7026,6 +7028,7 @@ extern void do_push_parm_decls			(tree, tree, tree *);
 extern tree do_aggregate_paren_init		(tree, tree);
 
 /* in decl2.cc */
+extern void update_sym_alias_interface		(tree);
 extern void record_mangling			(tree, bool);
 extern void overwrite_mangling			(tree, tree);
 extern void note_mangling_alias			(tree, tree);
@@ -7600,6 +7603,7 @@ extern bool emit_tinfo_decl			(tree);
 extern unsigned get_pseudo_tinfo_index		(tree);
 extern tree get_pseudo_tinfo_type		(unsigned);
 extern tree build_if_nonnull			(tree, tree, tsubst_flags_t);
+extern void update_tinfo_sym_alias		(tree);
 
 /* in search.cc */
 extern tree get_parent_with_private_access 	(tree decl, tree binfo);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 4b685270097..cc7f7a99487 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -3277,6 +3277,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
 	      && TREE_STATIC (olddecl))))
     make_decl_rtl (olddecl);
 
+  symtab_node::remap_sym_alias_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.
@@ -3300,6 +3302,7 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
       FOR_EACH_CLONE (clone, olddecl)
 	{
 	  DECL_ATTRIBUTES (clone) = DECL_ATTRIBUTES (olddecl);
+	  adjust_clone_attributes (olddecl, clone, DECL_NAME (clone));
 	  DECL_PRESERVE_P (clone) |= DECL_PRESERVE_P (olddecl);
 	}
     }
@@ -10838,6 +10841,7 @@ grokfndecl (tree ctype,
     {
       cplus_decl_attributes (&decl, *attrlist, 0);
       *attrlist = NULL_TREE;
+      create_sym_alias_decls (decl);
     }
 
   if (DECL_HAS_CONTRACTS_P (decl))
diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
index 9e666e5eece..c7f009beaf0 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -1844,6 +1844,9 @@ cplus_decl_attributes (tree *decl, tree attributes, int flags)
   if (late_attrs)
     save_template_attributes (late_attrs, decl, flags);
 
+  if (TYPE_P (*decl) && attributes)
+    update_tinfo_sym_alias (*decl);
+
   /* Propagate deprecation out to the template.  */
   if (TREE_DEPRECATED (*decl))
     if (tree ti = get_template_info (*decl))
@@ -2200,6 +2203,47 @@ 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 sym aliases.  */
+
+void
+update_sym_alias_interface (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.  */
 
@@ -3007,6 +3051,8 @@ determine_visibility (tree decl)
        translation unit, we can make the type internal.  */
     constrain_visibility (decl, VISIBILITY_ANON, false);
 
+  update_sym_alias_interface (decl);
+
   /* If visibility changed and DECL already has DECL_RTL, ensure
      symbol flags are updated.  */
   if ((DECL_VISIBILITY (decl) != orig_visibility
@@ -3269,6 +3315,8 @@ tentative_decl_linkage (tree decl)
       else if (VAR_P (decl))
 	maybe_commonize_var (decl);
     }
+
+  update_sym_alias_interface (decl);
 }
 
 /* DECL is a FUNCTION_DECL or VAR_DECL.  If the object file linkage
@@ -3503,6 +3551,8 @@ import_export_decl (tree decl)
     }
 
   DECL_INTERFACE_KNOWN (decl) = 1;
+
+  update_sym_alias_interface (decl);
 }
 
 /* Return an expression that performs the destruction of DECL, which
diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc
index 76f1d44610a..3a19a571256 100644
--- a/gcc/cp/name-lookup.cc
+++ b/gcc/cp/name-lookup.cc
@@ -22,9 +22,11 @@ along with GCC; see the file COPYING3.  If not see
 #define INCLUDE_MEMORY
 #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"
@@ -3675,6 +3677,15 @@ push_local_extern_decl_alias (tree decl)
 	  /* Adjust visibility.  */
 	  determine_visibility (alias);
 	}
+      else if (DECL_P (alias))
+	DECL_ATTRIBUTES (alias)
+	  = targetm.merge_decl_attributes (alias, decl);
+      if (DECL_P (alias))
+	{
+	  symtab_node::remap_sym_alias_target (decl, alias);
+	  DECL_ATTRIBUTES (decl)
+	    = remove_attribute ("sym_alias", DECL_ATTRIBUTES (alias));
+	}
     }
 
   retrofit_lang_decl (decl);
diff --git a/gcc/cp/optimize.cc b/gcc/cp/optimize.cc
index 9e8926e4cc6..4a2e8cb4354 100644
--- a/gcc/cp/optimize.cc
+++ b/gcc/cp/optimize.cc
@@ -528,9 +528,12 @@ maybe_clone_body (tree fn)
       DECL_VISIBILITY_SPECIFIED (clone) = DECL_VISIBILITY_SPECIFIED (fn);
       DECL_DLLIMPORT_P (clone) = DECL_DLLIMPORT_P (fn);
       DECL_ATTRIBUTES (clone) = clone_attrs (DECL_ATTRIBUTES (fn));
+      adjust_clone_attributes (fn, clone, DECL_NAME (clone), true);
       DECL_DISREGARD_INLINE_LIMITS (clone) = DECL_DISREGARD_INLINE_LIMITS (fn);
       set_decl_section_name (clone, fn);
 
+      update_sym_alias_interface (clone);
+
       /* Adjust the parameter names and locations.  */
       parm = DECL_ARGUMENTS (fn);
       clone_parm = DECL_ARGUMENTS (clone);
diff --git a/gcc/cp/rtti.cc b/gcc/cp/rtti.cc
index 7878929c246..4bb7b8c8c6e 100644
--- a/gcc/cp/rtti.cc
+++ b/gcc/cp/rtti.cc
@@ -28,8 +28,10 @@ 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"
+#include "cgraph.h"
 
 /* C++ returns type information to the user in struct type_info
    objects. We also use type information to implement dynamic_cast and
@@ -479,8 +481,13 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
 	  = build_tree_list (get_identifier ("non overlapping"),
 			     NULL_TREE);
       else
+	/* Share the non overlapping attribute, without assuming it's
+	   the only attribute, but assuming it's the last if it's
+	   present.  There may be sym aliases too, and those are not
+	   to be shared.  */
 	DECL_ATTRIBUTES (d)
-	  = DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]);
+	  = lookup_attribute ("non overlapping",
+			      DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]));
 
       /* Mark the variable as undefined -- but remember that we can
 	 define it later if we need to do so.  */
@@ -492,6 +499,16 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
       if (CLASS_TYPE_P (type))
 	CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type)) = d;
 
+      /* Copy sym alias attributes from the type to the rtti obj decl.  */
+      tree *attrs = &DECL_ATTRIBUTES (d);
+      FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+	{
+	  tree attr = tree_cons (TREE_PURPOSE (sym), TREE_VALUE (sym), *attrs);
+	  *attrs = attr;
+	  attrs = &TREE_CHAIN (attr);
+	}
+      create_sym_alias_decls (d);
+
       /* Add decl to the global array of tinfo decls.  */
       vec_safe_push (unemitted_tinfo_decls, d);
     }
@@ -499,6 +516,58 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
   return d;
 }
 
+/* After modifying the attributes of TYPE, check whether tinfo was
+   already created and, if so, add to it any sym alias attributes
+   that were not already present.  */
+
+void
+update_tinfo_sym_alias (tree type)
+{
+  if (!TYPE_SIZE (type) || !CLASS_TYPE_P (type))
+    return;
+
+  tree d = CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type));
+  if (!d)
+    return;
+
+  bool first = true;
+  symtab_node *node = NULL;
+
+  tree *attrs = &DECL_ATTRIBUTES (d);
+  FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (d_sym, *attrs)
+	if (TREE_VALUE (sym) == TREE_VALUE (d_sym))
+	  {
+	    found = true;
+	    break;
+	  }
+
+      if (found)
+	continue;
+
+      tree attr = tree_cons (TREE_PURPOSE (sym),
+			     TREE_VALUE (sym),
+			     *attrs);
+      *attrs = attr;
+      attrs = &TREE_CHAIN (attr);
+
+      if (first)
+	{
+	  first = false;
+	  node = symtab_node::get (d);
+	}
+
+      if (!node)
+	continue;
+
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+      create_sym_alias_decl (d, id);
+    }
+}
+
 /* Return the type_info object for TYPE.  */
 
 tree
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 9592cfee1d2..f16571147d4 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -4141,6 +4141,43 @@ Function Attributes}, @ref{PowerPC Function Attributes},
 @ref{Nios II Function Attributes}, and @ref{S/390 Function Attributes}
 for details.
 
+@cindex @code{sym_alias} function attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} attribute causes @var{name} to be output as an
+alias to the function, with the same linkage and visibility as the
+function, when the function definition is output, provided that the
+function could be an alias target.  For instance,
+
+@smallexample
+void f (uint64_t) __attribute__ ((__sym_alias__ ("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},
+with the same linkage and visibility.  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.
+
+Aliases introduced with this attribute, such as @samp{f_u64} in the
+example above, are assembly symbol names: they do not undergo C++ name
+mangling, and are not made visible in any scope in the source language.
+They can, however, can be named as alias targets.
+
+This attribute requires assembler and object file support for aliases,
+and may not be available on all targets.
+
 @cindex @code{symver} function attribute
 @item symver ("@var{name2}@@@var{nodename}")
 On ELF targets this attribute creates a symbol version.  The @var{name2} part
@@ -8068,6 +8105,10 @@ will be placed in new, unique sections.
 
 This additional functionality requires Binutils version 2.36 or later.
 
+@cindex @code{sym_alias} variable attribute
+@item sym_alias ("@var{name}")
+See @pxref{Common Function Attributes}.
+
 @cindex @code{uninitialized} variable attribute
 @item uninitialized
 This attribute, attached to a variable with automatic storage, means that
@@ -9163,6 +9204,21 @@ is not supported; that is to say, if a given scalar object can be accessed
 through distinct types that assign a different storage order to it, then the
 behavior is undefined.
 
+@cindex @code{sym_alias} type attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} type 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__ ((__sym_alias__ ("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.
+
 @cindex @code{transparent_union} type attribute
 @item transparent_union
 
diff --git a/gcc/symtab.cc b/gcc/symtab.cc
index 0470509a98d..43ba88d7939 100644
--- a/gcc/symtab.cc
+++ b/gcc/symtab.cc
@@ -1943,6 +1943,44 @@ symtab_node::noninterposable_alias (symtab_node *node, void *data)
   return false;
 }
 
+/* Remap sym alias nodes recorded as aliasing REPLACED to alias
+   REPLACEMENT instead.  */
+
+void
+symtab_node::remap_sym_alias_target (tree replaced, tree replacement)
+{
+  if (!decl_in_symtab_p (replacement)
+      || !symtab_node::get (replacement))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (replaced))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      symtab_node *sym_node = symtab_node::get_for_asmname (id);
+
+      if (!sym_node)
+	{
+	  create_sym_alias_decl (replacement, id);
+	  continue;
+	}
+
+      gcc_assert (!sym_node->analyzed);
+      if (sym_node->alias_target != replaced)
+	continue;
+
+      // sym_node->definition = 0;
+
+      if (VAR_P (replaced))
+	// varpool_node::create_extra_name_alias (sym_node->decl, replacement);
+	varpool_node::create_alias (sym_node->decl, replacement);
+      else
+	cgraph_node::create_same_body_alias (sym_node->decl, replacement);
+	// 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/goacc/declare-1.c b/gcc/testsuite/c-c++-common/goacc/declare-1.c
index 46ee01b6759..f2842893318 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-1.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-1.c
@@ -113,11 +113,11 @@ f_2 (void)
   int va3;
 #pragma acc declare device_resident(va3)
 
-#ifndef __cplusplus
+#if 0
   /* TODO PR90868
 
-     C: "error: variable '[...]' used more than once with '#pragma acc declare'".  */
-#else
+     "error: variable '[...]' used more than once with '#pragma acc declare'".  */
+
   extern int ve0;
 #pragma acc declare create(ve0)
 
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-2.c b/gcc/testsuite/c-c++-common/goacc/declare-2.c
index e2e22be57e9..aec59b69754 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-2.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-2.c
@@ -137,25 +137,25 @@ void
 f_pr90868_2 (void)
 {
   extern int we0;
-#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" } */
 
   extern int we1;
-#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" } */
 
   extern int *we2;
-#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" } */
 
   extern int we3;
-#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" } */
 
   extern int we4;
-#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" } */
 
   extern int we5;
-#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" } */
  
   extern int we6;
-#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" } */
 }
 
 
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
new file mode 100644
index 00000000000..61af50cb552
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
@@ -0,0 +1,39 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+int var_a = 1;
+
+void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+
+void
+foo_a ()
+{
+}
+
+
+int var_b;
+extern int var_b __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+
+void
+foo_b ()
+{
+}
+
+void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+
+
+int var_c __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+
+void __attribute__ ((__sym_alias__ ("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-sym-alias-2.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
new file mode 100644
index 00000000000..67dcd4fd7e1
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
@@ -0,0 +1,90 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+struct s
+{
+  int mem __attribute__ ((__sym_alias__ ("S_MEM"))); /* { dg-warning "attribute ignored" } */
+};
+
+void foo () {
+  extern void bar () __attribute__ ((__sym_alias__ ("FOO_BAR"))); /* ok */
+  int i __attribute__ ((sym_alias ("FOO_i"))); /* { dg-warning "ignored" } */
+  /* ??? X cannot be an alias target; should this be flagged? ... */
+  static int x __attribute__ ((sym_alias ("FOO_x")));
+  static int sx __attribute__ ((alias ("FOO_x"))); /* { dg-warning "ignored" } */
+  extern int xx __attribute__ ((alias ("FOO_x"))); /* { dg-warning "ignored" } */
+}
+
+/* ??? ... or should XR be accepted?  */
+extern int xr __attribute__ ((alias ("FOO_x"))); /* { dg-error "undefined" "" { xfail c++ } } */
+int dr __attribute__ ((alias ("FOO_x"))); /* { dg-error "defined both" } */
+
+
+int j __attribute__ ((sym_alias ("J_var")));
+void __attribute__ ((alias ("J_var")))
+j_fn (); /* { dg-error "between function and variable" } */
+
+
+void __attribute__ ((sym_alias ("K_fn")))
+k () {
+}
+extern int __attribute__ ((alias ("K_fn")))
+k_var; /* { dg-error "between function and variable" } */
+
+
+int l __attribute__ ((sym_alias ("L_fn")));
+
+/* These should be detected and reported, not because the names clash at the
+   source level, but because the asm symbols do.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+void
+L_fn () /* { dg-error "duplicate" "" { xfail *-*-* } } */
+{
+}
+
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __attribute__ ((sym_alias ("M_var")))
+m ()
+{
+}
+
+int M_var; /* { dg-error "duplicate" "" { xfail *-*-* } } */
+
+
+void __attribute__ ((sym_alias ("N_sym")))
+n_fn ()
+{
+}
+
+int __attribute__ ((sym_alias ("N_sym")))
+n_var; /* { dg-error "duplicate" } */
+
+
+int __attribute__ ((sym_alias ("O_sym")))
+o_var;
+
+void __attribute__ ((sym_alias ("O_sym")))
+o_fn () /* { dg-error "duplicate" } */
+{
+}
+
+int __attribute__ ((sym_alias ("P_sym")))
+p_var1;
+
+int __attribute__ ((sym_alias ("P_sym")))
+p_var2; /* { dg-error "duplicate" } */
+
+void __attribute__ ((sym_alias ("Q_sym")))
+q_fn1 ()
+{
+}
+
+void __attribute__ ((sym_alias ("Q_sym")))
+q_fn2 () /* { dg-error "duplicate" } */
+{
+}
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
new file mode 100644
index 00000000000..0052485a30a
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
@@ -0,0 +1,41 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int var_a = 1;
+
+void
+foo_a ()
+{
+  extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+}
+
+#if 0 // __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__ ((__sym_alias__ ("FOOVAR_B")));
+}
+
+int var_b;
+
+void __attribute__ ((__sym_alias__ ("FOOBAR_C")))
+foo_c ()
+{
+  void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+  /* Another sym for var_b.  */
+  extern int var_b __attribute__ ((__sym_alias__ ("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-sym-alias-4.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
new file mode 100644
index 00000000000..2beafa46379
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-require-alias "" } */
+
+int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A"))) = 42;
+
+int __attribute__ ((__sym_alias__ ("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/c-c++-common/torture/attr-sym-alias-5.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
new file mode 100644
index 00000000000..bee90136daf
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
@@ -0,0 +1,54 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+/* { dg-require-visibility "" } */
+
+int __attribute__ ((sym_alias ("A_hid"), visibility ("hidden"))) a;
+
+extern int __attribute__ ((sym_alias ("B_prt"))) b;
+
+int b __attribute__ ((visibility ("protected")));
+
+extern int __attribute__ ((sym_alias ("C_ntr"))) c;
+
+extern int c __attribute__ ((visibility ("internal")));
+
+int c;
+
+static int d __attribute__ ((sym_alias ("D_stt")));
+
+
+extern void __attribute__ ((sym_alias ("F_hid"), visibility ("hidden")))
+f ();
+
+void
+f () {
+}
+
+extern void __attribute__ ((sym_alias ("G_prt")))
+g ();
+
+void __attribute__ ((visibility ("protected")))
+g () {
+}
+
+extern void __attribute__ ((sym_alias ("H_ntr")))
+h ();
+
+void __attribute__ ((visibility ("internal")))
+h ();
+
+void h () {
+}
+
+static void __attribute__ ((sym_alias ("I_stt")))
+i () {
+}
+
+/* { dg-final { scan-assembler {hidden[^\n]*A_hid} } } */
+/* { dg-final { scan-assembler {protected[^\n]*B_prt} } } */
+/* { dg-final { scan-assembler {internal[^\n]*C_ntr} } } */
+/* { dg-final { scan-assembler-not {glob[^\n]*D_stt} } } */
+/* { dg-final { scan-assembler {hidden[^\n]*F_hid} } } */
+/* { dg-final { scan-assembler {protected[^\n]*G_prt} } } */
+/* { dg-final { scan-assembler {internal[^\n]*H_ntr} } } */
+/* { dg-final { scan-assembler-not {glob[^\n]*I_stt} } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
new file mode 100644
index 00000000000..579eda1a473
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
@@ -0,0 +1,72 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+class __attribute__ ((__sym_alias__ ("FOOCLS_A"),
+		      __sym_alias__ ("FOOCLS_A_Dupe"))) foo {
+  static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  void __attribute__ ((__sym_alias__ ("FOOBAR_A"))) bar ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+int foo::var = 1;
+
+foo::foo () {}
+
+void foo::bar () {}
+
+namespace b {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_B"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_B"))) foo ();
+    void __attribute__ ((__sym_alias__ ("FOOBAR_B"))) bar () {}
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_B"))) ~foo() {}
+  };
+
+  int foo::var = 2;
+
+  foo::foo () {
+    void (foo::*pbar)() = &foo::bar;
+  }
+}
+
+namespace c {
+  namespace cd {
+    class __attribute__ ((__sym_alias__ ("FOOCLS_C"))) foo {
+      static int var __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+      __attribute__ ((__sym_alias__ ("FOOCTR_C"))) foo () {
+	void (foo::*pbar)() = &foo::bar;
+      }
+      void __attribute__ ((__sym_alias__ ("FOOBAR_C"))) bar () {}
+      virtual __attribute__ ((__sym_alias__ ("FOODTR_C"))) ~foo() {}
+    };
+
+    int foo::var = 3;
+  }
+}
+
+/* { dg-final { scan-assembler "FOOCLS_A" } } */
+/* { dg-final { scan-assembler "FOOCLS_A_Dupe" } } */
+/* { 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-sym-alias-2.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
new file mode 100644
index 00000000000..6facfcc2cd9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+namespace {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_A"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo ();
+    void __attribute__ ((__sym_alias__ ("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-sym-alias-3.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
new file mode 100644
index 00000000000..42c7288ad4a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
@@ -0,0 +1,83 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+// sym can be applied to template function explicit instantiations.
+
+template <typename T>
+void
+fn(T) {
+};
+
+template void __attribute__ ((__sym_alias__ ("FOOFUN_UINT"))) fn<>(unsigned int);
+template void __attribute__ ((__sym_alias__ ("FOOFUN_LONG"))) fn<>(long);
+
+template<> void __attribute__ ((__sym_alias__ ("FOOFUN_CHAR"))) fn<>(char) {}
+
+
+template <typename T = void>
+struct
+foo {
+  virtual ~foo() {}
+
+  virtual void virtfun() {}
+
+  static void stfun() {}
+  void inlfun() {}
+};
+
+// Explicitly instantiate members before the enclosing class.
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_CHAR_VIRT"))) foo<char>::virtfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_CHAR_TI"))) foo<char>;
+
+// Though they're only output if the enclosing class is.
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_LONG_VIRT"))) foo<long>::virtfun();
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_LONG_TI_X"))) foo<long>;
+
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_VOID_ST"))) foo<void>::stfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_VOID_TI"))) foo<>;
+
+
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_X"))) foo<short>;
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_ST"))) foo<short>::stfun();
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_INL"))) foo<short>::inlfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_D"))) foo<short>;
+
+// Explicit specializations work too.
+
+template <>
+struct  __attribute__ ((__sym_alias__ ("FOOCLS_INT_TI")))
+foo<int>
+{
+  virtual ~foo() {}
+  virtual void __attribute__ ((__sym_alias__ ("FOOCLS_INT_VIRT"))) virtfun() {}
+};
+
+/* { dg-final { scan-assembler "FOOFUN_UINT" } } */
+/* { dg-final { scan-assembler "FOOFUN_LONG" } } */
+/* { dg-final { scan-assembler "FOOFUN_CHAR" } } */
+
+/* { dg-final { scan-assembler "FOOCLS_VOID_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_VOID_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_X" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_INL" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_D" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_TI_X" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_VIRT" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
new file mode 100644
index 00000000000..59b779de35d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+template <typename T = void>
+class
+__attribute__ ((__sym_alias__ ("FOOCLS")))
+foo // { dg-error "duplicate|already" }
+{
+  virtual ~foo() {}
+
+  template <typename U>
+  void
+    __attribute__ ((__sym_alias__ ("FOOTMF")))
+    tmemfun () {} // { dg-error "duplicate|already" }
+};
+
+template <typename T>
+void
+__attribute__ ((__sym_alias__ ("FOOTFN")))
+fn(T) { // { dg-error "duplicate|already" }
+};
+
+template class foo<>;
+template class foo<int>;
+template void foo<>::tmemfun<void>();
+template void foo<int>::tmemfun<void>();
+template void fn<>(int);
+template void fn<>(long);
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
new file mode 100644
index 00000000000..45d59b953c3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
@@ -0,0 +1,14 @@
+/* { dg-do compile { target c++11 } } */
+/* { dg-require-alias "" } */
+
+struct foo {
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+foo::foo () {}
+
+// Make sure the inherited cdtors don't duplicate the syms.
+struct bar : foo {
+  using foo::foo;
+};
diff --git a/gcc/varpool.cc b/gcc/varpool.cc
index e7b51b15e4a..33649906d1d 100644
--- a/gcc/varpool.cc
+++ b/gcc/varpool.cc
@@ -163,6 +163,9 @@ varpool_node::get_create (tree decl)
     }
 
   node->register_symbol ();
+
+  create_sym_alias_decls (decl);
+
   return node;
 }

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [gcc(refs/users/aoliva/heads/testme)] Introduce attribute sym_alias
@ 2023-12-01 20:43 Alexandre Oliva
  0 siblings, 0 replies; 33+ messages in thread
From: Alexandre Oliva @ 2023-12-01 20:43 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:83258fde49b816f73e07ee3c8d3cb8a8aaf38afd

commit 83258fde49b816f73e07ee3c8d3cb8a8aaf38afd
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Fri Dec 1 14:31:46 2023 -0300

    Introduce attribute sym_alias
    
    This patch introduces an attribute to add extra asm names (aliases)
    for a decl 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 class types, namespace-scoped
    variables and functions, static data members, member functions,
    explicit instantiations and specializations of template functions,
    members and classes.
    
    When applied to constructors or destructor, additional sym aliases
    with _Base and _Del suffixes are defined for variants other than
    complete-object ones.  This changes the assumption that clones always
    carry the same attributes as their abstract declarations, so there is
    now a function to adjust them.
    
    C++ also had a bug in which attributes from local extern declarations
    failed to be propagated to a preexisting corresponding
    namespace-scoped decl.  I've fixed that, and adjusted acc tests that
    distinguished between C and C++ in this regard.
    
    Applying the attribute to class types is only valid in C++, and the
    effect is to attach the alias to the RTTI object associated with the
    class type.
    
    for  gcc/ChangeLog
    
            * attribs.cc: Include cgraph.h.
            (decl_attributes): Allow late introduction of sym_alias in
            types.
            (create_sym_alias_decl, create_sym_alias_decls): New.
            * attribs.h: Declare them.
            (FOR_EACH_SYM_ALIAS): New macro.
            * cgraph.cc (cgraph_node::create): Create sym_alias decls.
            * varpool.cc (varpool_node::get_create): Create sym_alias
            decls.
            * cgraph.h (symtab_node::remap_sym_alias_target): New.
            * symtab.cc (symtab_node::remap_sym_alias_target): Define.
            * cgraphunit.cc (cgraph_node::analyze): Create alias_target
            node if needed.
            (analyze_functions): Fixup visibility of implicit alias only
            after its node is analyzed.
            * doc/extend.texi (sym_alias): Document for variables,
            functions and types.
    
    for  gcc/ada/ChangeLog
    
            * doc/gnat_rm/interfacing_to_other_languages.rst: Mention
            attribute sym_alias to give RTTI symbols mnemonic names.
            * doc/gnat_ugn/the_gnat_compilation_model.rst: Mention
            aliases.  Fix incorrect ref to C1 ctor variant.
    
    for  gcc/c-family/ChangeLog
    
            * c-ada-spec.cc (pp_asm_name): Use first sym_alias if
            available.
            * c-attribs.cc (handle_sym_alias_attribute): New.
            (c_common_attribute_table): Add sym_alias.
            (handle_copy_attribute): Do not copy sym_alias attribute.
    
    for  gcc/c/ChangeLog
    
            * c-decl.cc (duplicate_decls): Remap sym_alias target.
    
    for  gcc/cp/ChangeLog
    
            * class.cc (adjust_clone_attributes): New.
            (copy_fndecl_with_name, build_clone): Call it.
            * cp-tree.h (adjust_clone_attributes): Declare.
            (update_sym_alias_interface): Declare.
            (update_tinfo_sym_alias): Declare.
            * decl.cc (duplicate_decls): Remap sym_alias target.
            Adjust clone attributes.
            (grokfndecl): Tentatively create sym_alias decls after
            adding attributes in e.g. a template member function explicit
            instantiation.
            * decl2.cc (cplus_decl_attributes): Update tinfo sym_alias.
            (copy_interface, update_sym_alias_interface): New.
            (determine_visibility): Update sym_alias interface.
            (tentative_decl_linkage, import_export_decl): Likewise.
            * name-lookup.cc: Include target.h and cgraph.h.
            (push_local_extern_decl_alias): Merge attributes with
            namespace-scoped decl, and drop duplicate sym_alias.
            * optimize.cc (maybe_clone_body): Re-adjust attributes after
            cloning them.  Update sym_alias interface.
            * rtti.cc: Include attribs.h and cgraph.h.
            (get_tinfo_decl): Copy sym_alias attributes from type to tinfo
            decl.  Create sym_alias decls.
            (update_tinfo_sym_alias): New.
    
    for  gcc/testsuite/ChangeLog
    
            * c-c++-common/goacc/declare-1.c: Adjust.
            * c-c++-common/goacc/declare-2.c: Adjust.
            * c-c++-common/torture/attr-sym-alias-1.c: New.
            * c-c++-common/torture/attr-sym-alias-2.c: New.
            * c-c++-common/torture/attr-sym-alias-3.c: New.
            * c-c++-common/torture/attr-sym-alias-4.c: New.
            * c-c++-common/torture/attr-sym-alias-5.c: New.
            * g++.dg/torture/attr-sym-alias-1.C: New.
            * g++.dg/torture/attr-sym-alias-2.C: New.
            * g++.dg/torture/attr-sym-alias-3.C: New.
            * g++.dg/torture/attr-sym-alias-4.C: New.
            * g++.dg/torture/attr-sym-alias-5.C: New.

Diff:
---
 .../doc/gnat_rm/interfacing_to_other_languages.rst |  6 ++
 .../doc/gnat_ugn/the_gnat_compilation_model.rst    | 10 ++-
 gcc/attribs.cc                                     | 66 +++++++++++++++-
 gcc/attribs.h                                      |  7 ++
 gcc/c-family/c-ada-spec.cc                         |  7 ++
 gcc/c-family/c-attribs.cc                          | 33 +++++++-
 gcc/c/c-decl.cc                                    |  2 +
 gcc/cgraph.cc                                      |  2 +
 gcc/cgraph.h                                       |  4 +
 gcc/cgraphunit.cc                                  |  2 +-
 gcc/cp/class.cc                                    | 64 +++++++++++++++
 gcc/cp/cp-tree.h                                   |  4 +
 gcc/cp/decl.cc                                     |  4 +
 gcc/cp/decl2.cc                                    | 50 ++++++++++++
 gcc/cp/name-lookup.cc                              | 11 +++
 gcc/cp/optimize.cc                                 |  3 +
 gcc/cp/rtti.cc                                     | 71 ++++++++++++++++-
 gcc/doc/extend.texi                                | 56 ++++++++++++++
 gcc/symtab.cc                                      | 38 +++++++++
 gcc/testsuite/c-c++-common/goacc/declare-1.c       |  6 +-
 gcc/testsuite/c-c++-common/goacc/declare-2.c       | 14 ++--
 .../c-c++-common/torture/attr-sym-alias-1.c        | 39 ++++++++++
 .../c-c++-common/torture/attr-sym-alias-2.c        | 90 ++++++++++++++++++++++
 .../c-c++-common/torture/attr-sym-alias-3.c        | 41 ++++++++++
 .../c-c++-common/torture/attr-sym-alias-4.c        | 28 +++++++
 .../c-c++-common/torture/attr-sym-alias-5.c        | 54 +++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C    | 72 +++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C    | 26 +++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C    | 83 ++++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C    | 28 +++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C    | 14 ++++
 gcc/varpool.cc                                     |  3 +
 32 files changed, 919 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 ad0be511d48..b8de16ebf7c 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 ``sym_alias`` 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 fd15459203a..20045698962 100644
--- a/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
+++ b/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
@@ -4274,6 +4274,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__ ((__sym_alias__ ("Ctor_For_Animal")))
         Animal() {Age_Count = 0;};
       private:
         int Age_Count;
@@ -4306,6 +4307,7 @@ both Carnivore and Domestic, that is:
         virtual int  Number_Of_Teeth ();
         virtual void Set_Owner (char* Name);
 
+        __attribute__ ((__sym_alias__ ("Ctor_For_Dog"))) // mnemonic alias
         Dog(); // Constructor
       private:
         int  Tooth_Count;
@@ -4344,7 +4346,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;
@@ -4360,7 +4363,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,7 +4385,8 @@ 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.
+mangled name (or an alias) 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.cc b/gcc/attribs.cc
index c7209c26acc..c75ca6974cb 100644
--- a/gcc/attribs.cc
+++ b/gcc/attribs.cc
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "target.h"
 #include "tree.h"
+#include "cgraph.h"
 #include "stringpool.h"
 #include "diagnostic-core.h"
 #include "attribs.h"
@@ -825,7 +826,8 @@ decl_attributes (tree *node, tree attributes, int flags,
 
       if (TYPE_P (*anode)
 	  && (flags & (int) ATTR_FLAG_TYPE_IN_PLACE)
-	  && COMPLETE_TYPE_P (*anode))
+	  && COMPLETE_TYPE_P (*anode)
+	  && !is_attribute_p ("sym_alias", name))
 	{
 	  warning (OPT_Wattributes, "type attributes ignored after type is already defined");
 	  continue;
@@ -2640,6 +2642,68 @@ attr_access::array_as_string (tree type) const
   return typstr;
 }
 
+/* Create a sym attribute for DECL to be visible with linkage name ID.  */
+
+tree
+create_sym_alias_decl (tree decl, tree id)
+{
+  const char *attr_str = "sym_alias";
+
+  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;
+
+      tree attr_name = get_identifier (attr_str);
+      error_at (DECL_SOURCE_LOCATION (decl),
+		"duplicate symbol name %qE in %qE attribute of %qD",
+		id, attr_name, decl);
+      inform (DECL_SOURCE_LOCATION (sym_node->decl),
+	      "already used by %qD", sym_node->decl);
+    }
+
+  tree clone = copy_node (decl);
+  DECL_ATTRIBUTES (clone) = remove_attribute (attr_str,
+					      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_extra_name_alias (clone, decl);
+    varpool_node::create_alias (clone, decl);
+  else
+    cgraph_node::create_same_body_alias (clone, decl);
+    // cgraph_node::create_alias (clone, decl);
+
+  return clone;
+}
+
+/* Create decls for all sym aliases requested in DECL's attributes.  */
+
+void
+create_sym_alias_decls (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl)
+      || DECL_ABSTRACT_P (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      create_sym_alias_decl (decl, id);
+    }
+}
+
 #if CHECKING_P
 
 namespace selftest
diff --git a/gcc/attribs.h b/gcc/attribs.h
index 84a43658a70..156f2c20588 100644
--- a/gcc/attribs.h
+++ b/gcc/attribs.h
@@ -398,4 +398,11 @@ extern void init_attr_rdwr_indices (rdwr_map *, tree);
 extern attr_access *get_parm_access (rdwr_map &, tree,
 				     tree = current_function_decl);
 
+extern tree create_sym_alias_decl (tree, tree);
+extern void create_sym_alias_decls (tree);
+
+#define FOR_EACH_SYM_ALIAS(sym, attrs)					\
+  for (tree sym = lookup_attribute ("sym_alias", (attrs));		\
+       sym; sym = lookup_attribute ("sym_alias", TREE_CHAIN (sym)))
+
 #endif // GCC_ATTRIBS_H
diff --git a/gcc/c-family/c-ada-spec.cc b/gcc/c-family/c-ada-spec.cc
index 050994d8416..5042b9cfecd 100644
--- a/gcc/c-family/c-ada-spec.cc
+++ b/gcc/c-family/c-ada-spec.cc
@@ -1431,6 +1431,13 @@ pp_ada_tree_identifier (pretty_printer *buffer, tree node, tree type,
 static void
 pp_asm_name (pretty_printer *buffer, tree t)
 {
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (t))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.cc b/gcc/c-family/c-attribs.cc
index 2b20e58c922..392437eab3b 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -108,7 +108,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_sym_alias_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,
@@ -388,6 +389,8 @@ const struct attribute_spec c_common_attribute_table[] =
 			      handle_alias_attribute, NULL },
   { "weakref",                0, 1, true,  false, false, false,
 			      handle_weakref_attribute, NULL },
+  { "sym_alias",              1, 1, false,  false, false, false,
+			      handle_sym_alias_attribute, NULL },
   { "no_instrument_function", 0, 0, true,  false, false, false,
 			      handle_no_instrument_function_attribute,
 			      NULL },
@@ -3002,7 +3005,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
@@ -3012,7 +3015,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
@@ -3022,6 +3025,29 @@ handle_alias_attribute (tree *node, tree name, tree args,
   return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs);
 }
 
+/* Handle a "sym_alias" attribute; arguments as in struct
+   attribute_spec.handler.  */
+
+static tree
+handle_sym_alias_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 (decl_in_symtab_p (node))
+    *no_add_attrs = false;
+  else if (TYPE_P (node) && c_dialect_cxx ())
+    *no_add_attrs = false;
+  else
+    return error_mark_node;
+
+  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.  */
 
@@ -3155,6 +3181,7 @@ handle_copy_attribute (tree *node, tree name, tree args,
 	      || is_attribute_p ("visibility", atname)
 	      || is_attribute_p ("weak", atname)
 	      || is_attribute_p ("weakref", atname)
+	      || is_attribute_p ("sym_alias", atname)
 	      || is_attribute_p ("target_clones", atname))
 	    continue;
 
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index d388e123704..c5a7e56f4ec 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -3117,6 +3117,8 @@ duplicate_decls (tree newdecl, tree olddecl)
 
   merge_decls (newdecl, olddecl, newtype, oldtype);
 
+  symtab_node::remap_sym_alias_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.cc b/gcc/cgraph.cc
index f93259a8c70..0bef2179e89 100644
--- a/gcc/cgraph.cc
+++ b/gcc/cgraph.cc
@@ -523,6 +523,8 @@ cgraph_node::create (tree decl)
   node->register_symbol ();
   maybe_record_nested_function (node);
 
+  create_sym_alias_decls (decl);
+
   return node;
 }
 
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index cfdd9f693a8..cb5ac01f7e2 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -327,6 +327,10 @@ public:
   /* Return DECL that alias is aliasing.  */
   inline tree get_alias_target_tree ();
 
+  /* Remap sym alias nodes recorded as aliasing REPLACED to alias REPLACEMENT
+     instead.  */
+  static void remap_sym_alias_target (tree replaced, tree replacement);
+
   /* Set section for symbol and its aliases.  */
   void set_section (const char *section);
 
diff --git a/gcc/cgraphunit.cc b/gcc/cgraphunit.cc
index bccd2f2abb5..eb2d05094e9 100644
--- a/gcc/cgraphunit.cc
+++ b/gcc/cgraphunit.cc
@@ -1175,7 +1175,7 @@ analyze_functions (bool first_time)
      C++ FE is confused about the COMDAT groups being right.  */
   if (symtab->cpp_implicit_aliases_done)
     FOR_EACH_SYMBOL (node)
-      if (node->cpp_implicit_alias)
+      if (node->cpp_implicit_alias && node->analyzed)
 	  node->fixup_same_cpp_alias_visibility (node->get_alias_target ());
   build_type_inheritance_graph ();
 
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index 6fdb56abfb9..fa8fc63db82 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -4896,6 +4896,68 @@ check_methods (tree t)
     }
 }
 
+/* Adjust sym alias name for CLONE, cloned from FN and named NAME,
+   if it is a cdtor, and drop the sym alias from other clones.  */
+
+void
+adjust_clone_attributes (tree fn, tree clone, tree name, bool skip_copy_p)
+{
+  if (IDENTIFIER_CDTOR_P (name))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	{
+	  found = true;
+	  break;
+	}
+
+      if (found
+	  && (name == complete_ctor_identifier
+	      || name == complete_dtor_identifier))
+	{
+	  /* Reuse the sym alias decls created for the primary cdtor
+	     decl.  */
+	  symtab_node::remap_sym_alias_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);
+
+	  if (!skip_copy_p)
+	    DECL_ATTRIBUTES (clone) = copy_list (DECL_ATTRIBUTES (clone));
+
+	  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	    {
+	      /* We need to copy this even with skip_copy_p, because
+		 even then copying was shallow.  */
+	      TREE_VALUE (sym) = copy_list (TREE_VALUE (sym));
+	      /* Append suf to the sym alias name.  */
+	      tree str = TREE_VALUE (TREE_VALUE (sym));
+	      char *symname = concat (TREE_STRING_POINTER (str), suf, NULL);
+	      str = build_string (TREE_STRING_LENGTH (str) + xlen, symname);
+	      TREE_VALUE (TREE_VALUE (sym)) = str;
+	      free (symname);
+	    }
+
+	  if (symtab_node::get (clone))
+	    create_sym_alias_decls (clone);
+	}
+    }
+  else
+    DECL_ATTRIBUTES (clone)
+      = remove_attribute ("sym_alias", DECL_ATTRIBUTES (clone));
+}
+
 /* FN is constructor, destructor or operator function.  Clone the
    declaration to create a NAME'd variant.  NEED_VTT_PARM_P and
    OMIT_INHERITED_PARMS_P are relevant if it's a cdtor.  */
@@ -5065,6 +5127,8 @@ build_clone (tree fn, tree name, bool need_vtt_parm_p,
   DECL_CHAIN (clone) = DECL_CHAIN (fn);
   DECL_CHAIN (fn) = clone;
 
+  adjust_clone_attributes (fn, clone, name);
+
   return clone;
 }
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 8809842da3f..fb2b3f4db18 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5791,6 +5791,8 @@ struct GTY((for_user)) spec_entry
 
 extern int current_class_depth;
 
+void adjust_clone_attributes (tree fn, tree clone, tree name, bool = false);
+
 /* in decl.cc */
 
 /* An array of static vars & fns.  */
@@ -7026,6 +7028,7 @@ extern void do_push_parm_decls			(tree, tree, tree *);
 extern tree do_aggregate_paren_init		(tree, tree);
 
 /* in decl2.cc */
+extern void update_sym_alias_interface		(tree);
 extern void record_mangling			(tree, bool);
 extern void overwrite_mangling			(tree, tree);
 extern void note_mangling_alias			(tree, tree);
@@ -7600,6 +7603,7 @@ extern bool emit_tinfo_decl			(tree);
 extern unsigned get_pseudo_tinfo_index		(tree);
 extern tree get_pseudo_tinfo_type		(unsigned);
 extern tree build_if_nonnull			(tree, tree, tsubst_flags_t);
+extern void update_tinfo_sym_alias		(tree);
 
 /* in search.cc */
 extern tree get_parent_with_private_access 	(tree decl, tree binfo);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 4b685270097..cc7f7a99487 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -3277,6 +3277,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
 	      && TREE_STATIC (olddecl))))
     make_decl_rtl (olddecl);
 
+  symtab_node::remap_sym_alias_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.
@@ -3300,6 +3302,7 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
       FOR_EACH_CLONE (clone, olddecl)
 	{
 	  DECL_ATTRIBUTES (clone) = DECL_ATTRIBUTES (olddecl);
+	  adjust_clone_attributes (olddecl, clone, DECL_NAME (clone));
 	  DECL_PRESERVE_P (clone) |= DECL_PRESERVE_P (olddecl);
 	}
     }
@@ -10838,6 +10841,7 @@ grokfndecl (tree ctype,
     {
       cplus_decl_attributes (&decl, *attrlist, 0);
       *attrlist = NULL_TREE;
+      create_sym_alias_decls (decl);
     }
 
   if (DECL_HAS_CONTRACTS_P (decl))
diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
index 9e666e5eece..c7f009beaf0 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -1844,6 +1844,9 @@ cplus_decl_attributes (tree *decl, tree attributes, int flags)
   if (late_attrs)
     save_template_attributes (late_attrs, decl, flags);
 
+  if (TYPE_P (*decl) && attributes)
+    update_tinfo_sym_alias (*decl);
+
   /* Propagate deprecation out to the template.  */
   if (TREE_DEPRECATED (*decl))
     if (tree ti = get_template_info (*decl))
@@ -2200,6 +2203,47 @@ 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 sym aliases.  */
+
+void
+update_sym_alias_interface (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.  */
 
@@ -3007,6 +3051,8 @@ determine_visibility (tree decl)
        translation unit, we can make the type internal.  */
     constrain_visibility (decl, VISIBILITY_ANON, false);
 
+  update_sym_alias_interface (decl);
+
   /* If visibility changed and DECL already has DECL_RTL, ensure
      symbol flags are updated.  */
   if ((DECL_VISIBILITY (decl) != orig_visibility
@@ -3269,6 +3315,8 @@ tentative_decl_linkage (tree decl)
       else if (VAR_P (decl))
 	maybe_commonize_var (decl);
     }
+
+  update_sym_alias_interface (decl);
 }
 
 /* DECL is a FUNCTION_DECL or VAR_DECL.  If the object file linkage
@@ -3503,6 +3551,8 @@ import_export_decl (tree decl)
     }
 
   DECL_INTERFACE_KNOWN (decl) = 1;
+
+  update_sym_alias_interface (decl);
 }
 
 /* Return an expression that performs the destruction of DECL, which
diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc
index 76f1d44610a..3a19a571256 100644
--- a/gcc/cp/name-lookup.cc
+++ b/gcc/cp/name-lookup.cc
@@ -22,9 +22,11 @@ along with GCC; see the file COPYING3.  If not see
 #define INCLUDE_MEMORY
 #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"
@@ -3675,6 +3677,15 @@ push_local_extern_decl_alias (tree decl)
 	  /* Adjust visibility.  */
 	  determine_visibility (alias);
 	}
+      else if (DECL_P (alias))
+	DECL_ATTRIBUTES (alias)
+	  = targetm.merge_decl_attributes (alias, decl);
+      if (DECL_P (alias))
+	{
+	  symtab_node::remap_sym_alias_target (decl, alias);
+	  DECL_ATTRIBUTES (decl)
+	    = remove_attribute ("sym_alias", DECL_ATTRIBUTES (alias));
+	}
     }
 
   retrofit_lang_decl (decl);
diff --git a/gcc/cp/optimize.cc b/gcc/cp/optimize.cc
index 9e8926e4cc6..4a2e8cb4354 100644
--- a/gcc/cp/optimize.cc
+++ b/gcc/cp/optimize.cc
@@ -528,9 +528,12 @@ maybe_clone_body (tree fn)
       DECL_VISIBILITY_SPECIFIED (clone) = DECL_VISIBILITY_SPECIFIED (fn);
       DECL_DLLIMPORT_P (clone) = DECL_DLLIMPORT_P (fn);
       DECL_ATTRIBUTES (clone) = clone_attrs (DECL_ATTRIBUTES (fn));
+      adjust_clone_attributes (fn, clone, DECL_NAME (clone), true);
       DECL_DISREGARD_INLINE_LIMITS (clone) = DECL_DISREGARD_INLINE_LIMITS (fn);
       set_decl_section_name (clone, fn);
 
+      update_sym_alias_interface (clone);
+
       /* Adjust the parameter names and locations.  */
       parm = DECL_ARGUMENTS (fn);
       clone_parm = DECL_ARGUMENTS (clone);
diff --git a/gcc/cp/rtti.cc b/gcc/cp/rtti.cc
index 7878929c246..4bb7b8c8c6e 100644
--- a/gcc/cp/rtti.cc
+++ b/gcc/cp/rtti.cc
@@ -28,8 +28,10 @@ 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"
+#include "cgraph.h"
 
 /* C++ returns type information to the user in struct type_info
    objects. We also use type information to implement dynamic_cast and
@@ -479,8 +481,13 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
 	  = build_tree_list (get_identifier ("non overlapping"),
 			     NULL_TREE);
       else
+	/* Share the non overlapping attribute, without assuming it's
+	   the only attribute, but assuming it's the last if it's
+	   present.  There may be sym aliases too, and those are not
+	   to be shared.  */
 	DECL_ATTRIBUTES (d)
-	  = DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]);
+	  = lookup_attribute ("non overlapping",
+			      DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]));
 
       /* Mark the variable as undefined -- but remember that we can
 	 define it later if we need to do so.  */
@@ -492,6 +499,16 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
       if (CLASS_TYPE_P (type))
 	CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type)) = d;
 
+      /* Copy sym alias attributes from the type to the rtti obj decl.  */
+      tree *attrs = &DECL_ATTRIBUTES (d);
+      FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+	{
+	  tree attr = tree_cons (TREE_PURPOSE (sym), TREE_VALUE (sym), *attrs);
+	  *attrs = attr;
+	  attrs = &TREE_CHAIN (attr);
+	}
+      create_sym_alias_decls (d);
+
       /* Add decl to the global array of tinfo decls.  */
       vec_safe_push (unemitted_tinfo_decls, d);
     }
@@ -499,6 +516,58 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
   return d;
 }
 
+/* After modifying the attributes of TYPE, check whether tinfo was
+   already created and, if so, add to it any sym alias attributes
+   that were not already present.  */
+
+void
+update_tinfo_sym_alias (tree type)
+{
+  if (!TYPE_SIZE (type) || !CLASS_TYPE_P (type))
+    return;
+
+  tree d = CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type));
+  if (!d)
+    return;
+
+  bool first = true;
+  symtab_node *node = NULL;
+
+  tree *attrs = &DECL_ATTRIBUTES (d);
+  FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (d_sym, *attrs)
+	if (TREE_VALUE (sym) == TREE_VALUE (d_sym))
+	  {
+	    found = true;
+	    break;
+	  }
+
+      if (found)
+	continue;
+
+      tree attr = tree_cons (TREE_PURPOSE (sym),
+			     TREE_VALUE (sym),
+			     *attrs);
+      *attrs = attr;
+      attrs = &TREE_CHAIN (attr);
+
+      if (first)
+	{
+	  first = false;
+	  node = symtab_node::get (d);
+	}
+
+      if (!node)
+	continue;
+
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+      create_sym_alias_decl (d, id);
+    }
+}
+
 /* Return the type_info object for TYPE.  */
 
 tree
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 9592cfee1d2..f16571147d4 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -4141,6 +4141,43 @@ Function Attributes}, @ref{PowerPC Function Attributes},
 @ref{Nios II Function Attributes}, and @ref{S/390 Function Attributes}
 for details.
 
+@cindex @code{sym_alias} function attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} attribute causes @var{name} to be output as an
+alias to the function, with the same linkage and visibility as the
+function, when the function definition is output, provided that the
+function could be an alias target.  For instance,
+
+@smallexample
+void f (uint64_t) __attribute__ ((__sym_alias__ ("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},
+with the same linkage and visibility.  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.
+
+Aliases introduced with this attribute, such as @samp{f_u64} in the
+example above, are assembly symbol names: they do not undergo C++ name
+mangling, and are not made visible in any scope in the source language.
+They can, however, can be named as alias targets.
+
+This attribute requires assembler and object file support for aliases,
+and may not be available on all targets.
+
 @cindex @code{symver} function attribute
 @item symver ("@var{name2}@@@var{nodename}")
 On ELF targets this attribute creates a symbol version.  The @var{name2} part
@@ -8068,6 +8105,10 @@ will be placed in new, unique sections.
 
 This additional functionality requires Binutils version 2.36 or later.
 
+@cindex @code{sym_alias} variable attribute
+@item sym_alias ("@var{name}")
+See @pxref{Common Function Attributes}.
+
 @cindex @code{uninitialized} variable attribute
 @item uninitialized
 This attribute, attached to a variable with automatic storage, means that
@@ -9163,6 +9204,21 @@ is not supported; that is to say, if a given scalar object can be accessed
 through distinct types that assign a different storage order to it, then the
 behavior is undefined.
 
+@cindex @code{sym_alias} type attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} type 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__ ((__sym_alias__ ("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.
+
 @cindex @code{transparent_union} type attribute
 @item transparent_union
 
diff --git a/gcc/symtab.cc b/gcc/symtab.cc
index 0470509a98d..43ba88d7939 100644
--- a/gcc/symtab.cc
+++ b/gcc/symtab.cc
@@ -1943,6 +1943,44 @@ symtab_node::noninterposable_alias (symtab_node *node, void *data)
   return false;
 }
 
+/* Remap sym alias nodes recorded as aliasing REPLACED to alias
+   REPLACEMENT instead.  */
+
+void
+symtab_node::remap_sym_alias_target (tree replaced, tree replacement)
+{
+  if (!decl_in_symtab_p (replacement)
+      || !symtab_node::get (replacement))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (replaced))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      symtab_node *sym_node = symtab_node::get_for_asmname (id);
+
+      if (!sym_node)
+	{
+	  create_sym_alias_decl (replacement, id);
+	  continue;
+	}
+
+      gcc_assert (!sym_node->analyzed);
+      if (sym_node->alias_target != replaced)
+	continue;
+
+      // sym_node->definition = 0;
+
+      if (VAR_P (replaced))
+	// varpool_node::create_extra_name_alias (sym_node->decl, replacement);
+	varpool_node::create_alias (sym_node->decl, replacement);
+      else
+	cgraph_node::create_same_body_alias (sym_node->decl, replacement);
+	// 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/goacc/declare-1.c b/gcc/testsuite/c-c++-common/goacc/declare-1.c
index 46ee01b6759..f2842893318 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-1.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-1.c
@@ -113,11 +113,11 @@ f_2 (void)
   int va3;
 #pragma acc declare device_resident(va3)
 
-#ifndef __cplusplus
+#if 0
   /* TODO PR90868
 
-     C: "error: variable '[...]' used more than once with '#pragma acc declare'".  */
-#else
+     "error: variable '[...]' used more than once with '#pragma acc declare'".  */
+
   extern int ve0;
 #pragma acc declare create(ve0)
 
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-2.c b/gcc/testsuite/c-c++-common/goacc/declare-2.c
index e2e22be57e9..aec59b69754 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-2.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-2.c
@@ -137,25 +137,25 @@ void
 f_pr90868_2 (void)
 {
   extern int we0;
-#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" } */
 
   extern int we1;
-#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" } */
 
   extern int *we2;
-#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" } */
 
   extern int we3;
-#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" } */
 
   extern int we4;
-#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" } */
 
   extern int we5;
-#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" } */
  
   extern int we6;
-#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" } */
 }
 
 
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
new file mode 100644
index 00000000000..61af50cb552
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
@@ -0,0 +1,39 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+int var_a = 1;
+
+void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+
+void
+foo_a ()
+{
+}
+
+
+int var_b;
+extern int var_b __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+
+void
+foo_b ()
+{
+}
+
+void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+
+
+int var_c __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+
+void __attribute__ ((__sym_alias__ ("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-sym-alias-2.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
new file mode 100644
index 00000000000..67dcd4fd7e1
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
@@ -0,0 +1,90 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+struct s
+{
+  int mem __attribute__ ((__sym_alias__ ("S_MEM"))); /* { dg-warning "attribute ignored" } */
+};
+
+void foo () {
+  extern void bar () __attribute__ ((__sym_alias__ ("FOO_BAR"))); /* ok */
+  int i __attribute__ ((sym_alias ("FOO_i"))); /* { dg-warning "ignored" } */
+  /* ??? X cannot be an alias target; should this be flagged? ... */
+  static int x __attribute__ ((sym_alias ("FOO_x")));
+  static int sx __attribute__ ((alias ("FOO_x"))); /* { dg-warning "ignored" } */
+  extern int xx __attribute__ ((alias ("FOO_x"))); /* { dg-warning "ignored" } */
+}
+
+/* ??? ... or should XR be accepted?  */
+extern int xr __attribute__ ((alias ("FOO_x"))); /* { dg-error "undefined" "" { xfail c++ } } */
+int dr __attribute__ ((alias ("FOO_x"))); /* { dg-error "defined both" } */
+
+
+int j __attribute__ ((sym_alias ("J_var")));
+void __attribute__ ((alias ("J_var")))
+j_fn (); /* { dg-error "between function and variable" } */
+
+
+void __attribute__ ((sym_alias ("K_fn")))
+k () {
+}
+extern int __attribute__ ((alias ("K_fn")))
+k_var; /* { dg-error "between function and variable" } */
+
+
+int l __attribute__ ((sym_alias ("L_fn")));
+
+/* These should be detected and reported, not because the names clash at the
+   source level, but because the asm symbols do.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+void
+L_fn () /* { dg-error "duplicate" "" { xfail *-*-* } } */
+{
+}
+
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __attribute__ ((sym_alias ("M_var")))
+m ()
+{
+}
+
+int M_var; /* { dg-error "duplicate" "" { xfail *-*-* } } */
+
+
+void __attribute__ ((sym_alias ("N_sym")))
+n_fn ()
+{
+}
+
+int __attribute__ ((sym_alias ("N_sym")))
+n_var; /* { dg-error "duplicate" } */
+
+
+int __attribute__ ((sym_alias ("O_sym")))
+o_var;
+
+void __attribute__ ((sym_alias ("O_sym")))
+o_fn () /* { dg-error "duplicate" } */
+{
+}
+
+int __attribute__ ((sym_alias ("P_sym")))
+p_var1;
+
+int __attribute__ ((sym_alias ("P_sym")))
+p_var2; /* { dg-error "duplicate" } */
+
+void __attribute__ ((sym_alias ("Q_sym")))
+q_fn1 ()
+{
+}
+
+void __attribute__ ((sym_alias ("Q_sym")))
+q_fn2 () /* { dg-error "duplicate" } */
+{
+}
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
new file mode 100644
index 00000000000..0052485a30a
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
@@ -0,0 +1,41 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int var_a = 1;
+
+void
+foo_a ()
+{
+  extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+}
+
+#if 0 // __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__ ((__sym_alias__ ("FOOVAR_B")));
+}
+
+int var_b;
+
+void __attribute__ ((__sym_alias__ ("FOOBAR_C")))
+foo_c ()
+{
+  void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+  /* Another sym for var_b.  */
+  extern int var_b __attribute__ ((__sym_alias__ ("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-sym-alias-4.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
new file mode 100644
index 00000000000..2beafa46379
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-require-alias "" } */
+
+int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A"))) = 42;
+
+int __attribute__ ((__sym_alias__ ("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/c-c++-common/torture/attr-sym-alias-5.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
new file mode 100644
index 00000000000..bee90136daf
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
@@ -0,0 +1,54 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+/* { dg-require-visibility "" } */
+
+int __attribute__ ((sym_alias ("A_hid"), visibility ("hidden"))) a;
+
+extern int __attribute__ ((sym_alias ("B_prt"))) b;
+
+int b __attribute__ ((visibility ("protected")));
+
+extern int __attribute__ ((sym_alias ("C_ntr"))) c;
+
+extern int c __attribute__ ((visibility ("internal")));
+
+int c;
+
+static int d __attribute__ ((sym_alias ("D_stt")));
+
+
+extern void __attribute__ ((sym_alias ("F_hid"), visibility ("hidden")))
+f ();
+
+void
+f () {
+}
+
+extern void __attribute__ ((sym_alias ("G_prt")))
+g ();
+
+void __attribute__ ((visibility ("protected")))
+g () {
+}
+
+extern void __attribute__ ((sym_alias ("H_ntr")))
+h ();
+
+void __attribute__ ((visibility ("internal")))
+h ();
+
+void h () {
+}
+
+static void __attribute__ ((sym_alias ("I_stt")))
+i () {
+}
+
+/* { dg-final { scan-assembler {hidden[^\n]*A_hid} } } */
+/* { dg-final { scan-assembler {protected[^\n]*B_prt} } } */
+/* { dg-final { scan-assembler {internal[^\n]*C_ntr} } } */
+/* { dg-final { scan-assembler-not {glob[^\n]*D_stt} } } */
+/* { dg-final { scan-assembler {hidden[^\n]*F_hid} } } */
+/* { dg-final { scan-assembler {protected[^\n]*G_prt} } } */
+/* { dg-final { scan-assembler {internal[^\n]*H_ntr} } } */
+/* { dg-final { scan-assembler-not {glob[^\n]*I_stt} } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
new file mode 100644
index 00000000000..579eda1a473
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
@@ -0,0 +1,72 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+class __attribute__ ((__sym_alias__ ("FOOCLS_A"),
+		      __sym_alias__ ("FOOCLS_A_Dupe"))) foo {
+  static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  void __attribute__ ((__sym_alias__ ("FOOBAR_A"))) bar ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+int foo::var = 1;
+
+foo::foo () {}
+
+void foo::bar () {}
+
+namespace b {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_B"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_B"))) foo ();
+    void __attribute__ ((__sym_alias__ ("FOOBAR_B"))) bar () {}
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_B"))) ~foo() {}
+  };
+
+  int foo::var = 2;
+
+  foo::foo () {
+    void (foo::*pbar)() = &foo::bar;
+  }
+}
+
+namespace c {
+  namespace cd {
+    class __attribute__ ((__sym_alias__ ("FOOCLS_C"))) foo {
+      static int var __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+      __attribute__ ((__sym_alias__ ("FOOCTR_C"))) foo () {
+	void (foo::*pbar)() = &foo::bar;
+      }
+      void __attribute__ ((__sym_alias__ ("FOOBAR_C"))) bar () {}
+      virtual __attribute__ ((__sym_alias__ ("FOODTR_C"))) ~foo() {}
+    };
+
+    int foo::var = 3;
+  }
+}
+
+/* { dg-final { scan-assembler "FOOCLS_A" } } */
+/* { dg-final { scan-assembler "FOOCLS_A_Dupe" } } */
+/* { 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-sym-alias-2.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
new file mode 100644
index 00000000000..6facfcc2cd9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+namespace {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_A"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo ();
+    void __attribute__ ((__sym_alias__ ("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-sym-alias-3.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
new file mode 100644
index 00000000000..42c7288ad4a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
@@ -0,0 +1,83 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+// sym can be applied to template function explicit instantiations.
+
+template <typename T>
+void
+fn(T) {
+};
+
+template void __attribute__ ((__sym_alias__ ("FOOFUN_UINT"))) fn<>(unsigned int);
+template void __attribute__ ((__sym_alias__ ("FOOFUN_LONG"))) fn<>(long);
+
+template<> void __attribute__ ((__sym_alias__ ("FOOFUN_CHAR"))) fn<>(char) {}
+
+
+template <typename T = void>
+struct
+foo {
+  virtual ~foo() {}
+
+  virtual void virtfun() {}
+
+  static void stfun() {}
+  void inlfun() {}
+};
+
+// Explicitly instantiate members before the enclosing class.
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_CHAR_VIRT"))) foo<char>::virtfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_CHAR_TI"))) foo<char>;
+
+// Though they're only output if the enclosing class is.
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_LONG_VIRT"))) foo<long>::virtfun();
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_LONG_TI_X"))) foo<long>;
+
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_VOID_ST"))) foo<void>::stfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_VOID_TI"))) foo<>;
+
+
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_X"))) foo<short>;
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_ST"))) foo<short>::stfun();
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_INL"))) foo<short>::inlfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_D"))) foo<short>;
+
+// Explicit specializations work too.
+
+template <>
+struct  __attribute__ ((__sym_alias__ ("FOOCLS_INT_TI")))
+foo<int>
+{
+  virtual ~foo() {}
+  virtual void __attribute__ ((__sym_alias__ ("FOOCLS_INT_VIRT"))) virtfun() {}
+};
+
+/* { dg-final { scan-assembler "FOOFUN_UINT" } } */
+/* { dg-final { scan-assembler "FOOFUN_LONG" } } */
+/* { dg-final { scan-assembler "FOOFUN_CHAR" } } */
+
+/* { dg-final { scan-assembler "FOOCLS_VOID_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_VOID_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_X" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_INL" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_D" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_TI_X" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_VIRT" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
new file mode 100644
index 00000000000..59b779de35d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+template <typename T = void>
+class
+__attribute__ ((__sym_alias__ ("FOOCLS")))
+foo // { dg-error "duplicate|already" }
+{
+  virtual ~foo() {}
+
+  template <typename U>
+  void
+    __attribute__ ((__sym_alias__ ("FOOTMF")))
+    tmemfun () {} // { dg-error "duplicate|already" }
+};
+
+template <typename T>
+void
+__attribute__ ((__sym_alias__ ("FOOTFN")))
+fn(T) { // { dg-error "duplicate|already" }
+};
+
+template class foo<>;
+template class foo<int>;
+template void foo<>::tmemfun<void>();
+template void foo<int>::tmemfun<void>();
+template void fn<>(int);
+template void fn<>(long);
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
new file mode 100644
index 00000000000..45d59b953c3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
@@ -0,0 +1,14 @@
+/* { dg-do compile { target c++11 } } */
+/* { dg-require-alias "" } */
+
+struct foo {
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+foo::foo () {}
+
+// Make sure the inherited cdtors don't duplicate the syms.
+struct bar : foo {
+  using foo::foo;
+};
diff --git a/gcc/varpool.cc b/gcc/varpool.cc
index e7b51b15e4a..33649906d1d 100644
--- a/gcc/varpool.cc
+++ b/gcc/varpool.cc
@@ -163,6 +163,9 @@ varpool_node::get_create (tree decl)
     }
 
   node->register_symbol ();
+
+  create_sym_alias_decls (decl);
+
   return node;
 }

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [gcc(refs/users/aoliva/heads/testme)] Introduce attribute sym_alias
@ 2023-12-01 18:04 Alexandre Oliva
  0 siblings, 0 replies; 33+ messages in thread
From: Alexandre Oliva @ 2023-12-01 18:04 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:9fba4ee37c70d6d8f30f3c48a3f6b96fe992edcb

commit 9fba4ee37c70d6d8f30f3c48a3f6b96fe992edcb
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Fri Dec 1 14:31:46 2023 -0300

    Introduce attribute sym_alias
    
    This patch introduces an attribute to add extra asm names (aliases)
    for a decl 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 class types, namespace-scoped
    variables and functions, static data members, member functions,
    explicit instantiations and specializations of template functions,
    members and classes.
    
    When applied to constructors or destructor, additional sym aliases
    with _Base and _Del suffixes are defined for variants other than
    complete-object ones.  This changes the assumption that clones always
    carry the same attributes as their abstract declarations, so there is
    now a function to adjust them.
    
    C++ also had a bug in which attributes from local extern declarations
    failed to be propagated to a preexisting corresponding
    namespace-scoped decl.  I've fixed that, and adjusted acc tests that
    distinguished between C and C++ in this regard.
    
    Applying the attribute to class types is only valid in C++, and the
    effect is to attach the alias to the RTTI object associated with the
    class type.
    
    for  gcc/ChangeLog
    
            * attribs.cc: Include cgraph.h.
            (decl_attributes): Allow late introduction of sym_alias in
            types.
            (create_sym_alias_decl, create_sym_alias_decls): New.
            * attribs.h: Declare them.
            (FOR_EACH_SYM_ALIAS): New macro.
            * cgraph.cc (cgraph_node::create): Create sym_alias decls.
            * varpool.cc (varpool_node::get_create): Create sym_alias
            decls.
            * cgraph.h (symtab_node::remap_sym_alias_target): New.
            * symtab.cc (symtab_node::remap_sym_alias_target): Define.
            * cgraphunit.cc (cgraph_node::analyze): Create alias_target
            node if needed.
            (analyze_functions): Fixup visibility of implicit alias only
            after its node is analyzed.
            * doc/extend.texi (sym_alias): Document for variables,
            functions and types.
    
    for  gcc/ada/ChangeLog
    
            * doc/gnat_rm/interfacing_to_other_languages.rst: Mention
            attribute sym_alias to give RTTI symbols mnemonic names.
            * doc/gnat_ugn/the_gnat_compilation_model.rst: Mention
            aliases.  Fix incorrect ref to C1 ctor variant.
    
    for  gcc/c-family/ChangeLog
    
            * c-ada-spec.cc (pp_asm_name): Use first sym_alias if
            available.
            * c-attribs.cc (handle_sym_alias_attribute): New.
            (c_common_attribute_table): Add sym_alias.
            (handle_copy_attribute): Do not copy sym_alias attribute.
    
    for  gcc/c/ChangeLog
    
            * c-decl.cc (duplicate_decls): Remap sym_alias target.
    
    for  gcc/cp/ChangeLog
    
            * class.cc (adjust_clone_attributes): New.
            (copy_fndecl_with_name, build_clone): Call it.
            * cp-tree.h (adjust_clone_attributes): Declare.
            (update_sym_alias_interface): Declare.
            (update_tinfo_sym_alias): Declare.
            * decl.cc (duplicate_decls): Remap sym_alias target.
            Adjust clone attributes.
            (grokfndecl): Tentatively create sym_alias decls after
            adding attributes in e.g. a template member function explicit
            instantiation.
            * decl2.cc (cplus_decl_attributes): Update tinfo sym_alias.
            (copy_interface, update_sym_alias_interface): New.
            (determine_visibility): Update sym_alias interface.
            (tentative_decl_linkage, import_export_decl): Likewise.
            * name-lookup.cc: Include target.h and cgraph.h.
            (push_local_extern_decl_alias): Merge attributes with
            namespace-scoped decl, and drop duplicate sym_alias.
            * optimize.cc (maybe_clone_body): Re-adjust attributes after
            cloning them.  Update sym_alias interface.
            * rtti.cc: Include attribs.h and cgraph.h.
            (get_tinfo_decl): Copy sym_alias attributes from type to tinfo
            decl.  Create sym_alias decls.
            (update_tinfo_sym_alias): New.
    
    for  gcc/testsuite/ChangeLog
    
            * c-c++-common/goacc/declare-1.c: Adjust.
            * c-c++-common/goacc/declare-2.c: Adjust.
            * c-c++-common/torture/attr-sym-alias-1.c: New.
            * c-c++-common/torture/attr-sym-alias-2.c: New.
            * c-c++-common/torture/attr-sym-alias-3.c: New.
            * c-c++-common/torture/attr-sym-alias-4.c: New.
            * c-c++-common/torture/attr-sym-alias-5.c: New.
            * c-c++-common/attr-sym-alias-neg.c: New.
            * g++.dg/torture/attr-sym-alias-1.C: New.
            * g++.dg/torture/attr-sym-alias-2.C: New.
            * g++.dg/torture/attr-sym-alias-3.C: New.
            * g++.dg/torture/attr-sym-alias-4.C: New.
            * g++.dg/torture/attr-sym-alias-5.C: New.

Diff:
---
 .../doc/gnat_rm/interfacing_to_other_languages.rst |  6 ++
 .../doc/gnat_ugn/the_gnat_compilation_model.rst    | 10 ++-
 gcc/attribs.cc                                     | 66 ++++++++++++++++-
 gcc/attribs.h                                      |  7 ++
 gcc/c-family/c-ada-spec.cc                         |  7 ++
 gcc/c-family/c-attribs.cc                          | 33 ++++++++-
 gcc/c/c-decl.cc                                    |  2 +
 gcc/cgraph.cc                                      |  2 +
 gcc/cgraph.h                                       |  4 ++
 gcc/cgraphunit.cc                                  |  2 +-
 gcc/cp/class.cc                                    | 64 +++++++++++++++++
 gcc/cp/cp-tree.h                                   |  4 ++
 gcc/cp/decl.cc                                     |  4 ++
 gcc/cp/decl2.cc                                    | 50 +++++++++++++
 gcc/cp/name-lookup.cc                              | 11 +++
 gcc/cp/optimize.cc                                 |  3 +
 gcc/cp/rtti.cc                                     | 71 +++++++++++++++++-
 gcc/doc/extend.texi                                | 56 +++++++++++++++
 gcc/symtab.cc                                      | 38 ++++++++++
 gcc/testsuite/c-c++-common/attr-sym-alias-neg.c    | 80 +++++++++++++++++++++
 gcc/testsuite/c-c++-common/goacc/declare-1.c       |  6 +-
 gcc/testsuite/c-c++-common/goacc/declare-2.c       | 14 ++--
 .../c-c++-common/torture/attr-sym-alias-1.c        | 39 ++++++++++
 .../c-c++-common/torture/attr-sym-alias-2.c        | 13 ++++
 .../c-c++-common/torture/attr-sym-alias-3.c        | 41 +++++++++++
 .../c-c++-common/torture/attr-sym-alias-4.c        | 28 ++++++++
 .../c-c++-common/torture/attr-sym-alias-5.c        | 54 ++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C    | 72 +++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C    | 26 +++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C    | 83 ++++++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C    | 28 ++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C    | 14 ++++
 gcc/varpool.cc                                     |  3 +
 33 files changed, 922 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 ad0be511d48..b8de16ebf7c 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 ``sym_alias`` 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 fd15459203a..20045698962 100644
--- a/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
+++ b/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
@@ -4274,6 +4274,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__ ((__sym_alias__ ("Ctor_For_Animal")))
         Animal() {Age_Count = 0;};
       private:
         int Age_Count;
@@ -4306,6 +4307,7 @@ both Carnivore and Domestic, that is:
         virtual int  Number_Of_Teeth ();
         virtual void Set_Owner (char* Name);
 
+        __attribute__ ((__sym_alias__ ("Ctor_For_Dog"))) // mnemonic alias
         Dog(); // Constructor
       private:
         int  Tooth_Count;
@@ -4344,7 +4346,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;
@@ -4360,7 +4363,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,7 +4385,8 @@ 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.
+mangled name (or an alias) 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.cc b/gcc/attribs.cc
index c7209c26acc..c75ca6974cb 100644
--- a/gcc/attribs.cc
+++ b/gcc/attribs.cc
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "target.h"
 #include "tree.h"
+#include "cgraph.h"
 #include "stringpool.h"
 #include "diagnostic-core.h"
 #include "attribs.h"
@@ -825,7 +826,8 @@ decl_attributes (tree *node, tree attributes, int flags,
 
       if (TYPE_P (*anode)
 	  && (flags & (int) ATTR_FLAG_TYPE_IN_PLACE)
-	  && COMPLETE_TYPE_P (*anode))
+	  && COMPLETE_TYPE_P (*anode)
+	  && !is_attribute_p ("sym_alias", name))
 	{
 	  warning (OPT_Wattributes, "type attributes ignored after type is already defined");
 	  continue;
@@ -2640,6 +2642,68 @@ attr_access::array_as_string (tree type) const
   return typstr;
 }
 
+/* Create a sym attribute for DECL to be visible with linkage name ID.  */
+
+tree
+create_sym_alias_decl (tree decl, tree id)
+{
+  const char *attr_str = "sym_alias";
+
+  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;
+
+      tree attr_name = get_identifier (attr_str);
+      error_at (DECL_SOURCE_LOCATION (decl),
+		"duplicate symbol name %qE in %qE attribute of %qD",
+		id, attr_name, decl);
+      inform (DECL_SOURCE_LOCATION (sym_node->decl),
+	      "already used by %qD", sym_node->decl);
+    }
+
+  tree clone = copy_node (decl);
+  DECL_ATTRIBUTES (clone) = remove_attribute (attr_str,
+					      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_extra_name_alias (clone, decl);
+    varpool_node::create_alias (clone, decl);
+  else
+    cgraph_node::create_same_body_alias (clone, decl);
+    // cgraph_node::create_alias (clone, decl);
+
+  return clone;
+}
+
+/* Create decls for all sym aliases requested in DECL's attributes.  */
+
+void
+create_sym_alias_decls (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl)
+      || DECL_ABSTRACT_P (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      create_sym_alias_decl (decl, id);
+    }
+}
+
 #if CHECKING_P
 
 namespace selftest
diff --git a/gcc/attribs.h b/gcc/attribs.h
index 84a43658a70..156f2c20588 100644
--- a/gcc/attribs.h
+++ b/gcc/attribs.h
@@ -398,4 +398,11 @@ extern void init_attr_rdwr_indices (rdwr_map *, tree);
 extern attr_access *get_parm_access (rdwr_map &, tree,
 				     tree = current_function_decl);
 
+extern tree create_sym_alias_decl (tree, tree);
+extern void create_sym_alias_decls (tree);
+
+#define FOR_EACH_SYM_ALIAS(sym, attrs)					\
+  for (tree sym = lookup_attribute ("sym_alias", (attrs));		\
+       sym; sym = lookup_attribute ("sym_alias", TREE_CHAIN (sym)))
+
 #endif // GCC_ATTRIBS_H
diff --git a/gcc/c-family/c-ada-spec.cc b/gcc/c-family/c-ada-spec.cc
index 050994d8416..5042b9cfecd 100644
--- a/gcc/c-family/c-ada-spec.cc
+++ b/gcc/c-family/c-ada-spec.cc
@@ -1431,6 +1431,13 @@ pp_ada_tree_identifier (pretty_printer *buffer, tree node, tree type,
 static void
 pp_asm_name (pretty_printer *buffer, tree t)
 {
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (t))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.cc b/gcc/c-family/c-attribs.cc
index 2b20e58c922..392437eab3b 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -108,7 +108,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_sym_alias_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,
@@ -388,6 +389,8 @@ const struct attribute_spec c_common_attribute_table[] =
 			      handle_alias_attribute, NULL },
   { "weakref",                0, 1, true,  false, false, false,
 			      handle_weakref_attribute, NULL },
+  { "sym_alias",              1, 1, false,  false, false, false,
+			      handle_sym_alias_attribute, NULL },
   { "no_instrument_function", 0, 0, true,  false, false, false,
 			      handle_no_instrument_function_attribute,
 			      NULL },
@@ -3002,7 +3005,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
@@ -3012,7 +3015,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
@@ -3022,6 +3025,29 @@ handle_alias_attribute (tree *node, tree name, tree args,
   return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs);
 }
 
+/* Handle a "sym_alias" attribute; arguments as in struct
+   attribute_spec.handler.  */
+
+static tree
+handle_sym_alias_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 (decl_in_symtab_p (node))
+    *no_add_attrs = false;
+  else if (TYPE_P (node) && c_dialect_cxx ())
+    *no_add_attrs = false;
+  else
+    return error_mark_node;
+
+  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.  */
 
@@ -3155,6 +3181,7 @@ handle_copy_attribute (tree *node, tree name, tree args,
 	      || is_attribute_p ("visibility", atname)
 	      || is_attribute_p ("weak", atname)
 	      || is_attribute_p ("weakref", atname)
+	      || is_attribute_p ("sym_alias", atname)
 	      || is_attribute_p ("target_clones", atname))
 	    continue;
 
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index d388e123704..c5a7e56f4ec 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -3117,6 +3117,8 @@ duplicate_decls (tree newdecl, tree olddecl)
 
   merge_decls (newdecl, olddecl, newtype, oldtype);
 
+  symtab_node::remap_sym_alias_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.cc b/gcc/cgraph.cc
index f93259a8c70..0bef2179e89 100644
--- a/gcc/cgraph.cc
+++ b/gcc/cgraph.cc
@@ -523,6 +523,8 @@ cgraph_node::create (tree decl)
   node->register_symbol ();
   maybe_record_nested_function (node);
 
+  create_sym_alias_decls (decl);
+
   return node;
 }
 
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index cfdd9f693a8..cb5ac01f7e2 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -327,6 +327,10 @@ public:
   /* Return DECL that alias is aliasing.  */
   inline tree get_alias_target_tree ();
 
+  /* Remap sym alias nodes recorded as aliasing REPLACED to alias REPLACEMENT
+     instead.  */
+  static void remap_sym_alias_target (tree replaced, tree replacement);
+
   /* Set section for symbol and its aliases.  */
   void set_section (const char *section);
 
diff --git a/gcc/cgraphunit.cc b/gcc/cgraphunit.cc
index bccd2f2abb5..eb2d05094e9 100644
--- a/gcc/cgraphunit.cc
+++ b/gcc/cgraphunit.cc
@@ -1175,7 +1175,7 @@ analyze_functions (bool first_time)
      C++ FE is confused about the COMDAT groups being right.  */
   if (symtab->cpp_implicit_aliases_done)
     FOR_EACH_SYMBOL (node)
-      if (node->cpp_implicit_alias)
+      if (node->cpp_implicit_alias && node->analyzed)
 	  node->fixup_same_cpp_alias_visibility (node->get_alias_target ());
   build_type_inheritance_graph ();
 
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index 6fdb56abfb9..fa8fc63db82 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -4896,6 +4896,68 @@ check_methods (tree t)
     }
 }
 
+/* Adjust sym alias name for CLONE, cloned from FN and named NAME,
+   if it is a cdtor, and drop the sym alias from other clones.  */
+
+void
+adjust_clone_attributes (tree fn, tree clone, tree name, bool skip_copy_p)
+{
+  if (IDENTIFIER_CDTOR_P (name))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	{
+	  found = true;
+	  break;
+	}
+
+      if (found
+	  && (name == complete_ctor_identifier
+	      || name == complete_dtor_identifier))
+	{
+	  /* Reuse the sym alias decls created for the primary cdtor
+	     decl.  */
+	  symtab_node::remap_sym_alias_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);
+
+	  if (!skip_copy_p)
+	    DECL_ATTRIBUTES (clone) = copy_list (DECL_ATTRIBUTES (clone));
+
+	  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	    {
+	      /* We need to copy this even with skip_copy_p, because
+		 even then copying was shallow.  */
+	      TREE_VALUE (sym) = copy_list (TREE_VALUE (sym));
+	      /* Append suf to the sym alias name.  */
+	      tree str = TREE_VALUE (TREE_VALUE (sym));
+	      char *symname = concat (TREE_STRING_POINTER (str), suf, NULL);
+	      str = build_string (TREE_STRING_LENGTH (str) + xlen, symname);
+	      TREE_VALUE (TREE_VALUE (sym)) = str;
+	      free (symname);
+	    }
+
+	  if (symtab_node::get (clone))
+	    create_sym_alias_decls (clone);
+	}
+    }
+  else
+    DECL_ATTRIBUTES (clone)
+      = remove_attribute ("sym_alias", DECL_ATTRIBUTES (clone));
+}
+
 /* FN is constructor, destructor or operator function.  Clone the
    declaration to create a NAME'd variant.  NEED_VTT_PARM_P and
    OMIT_INHERITED_PARMS_P are relevant if it's a cdtor.  */
@@ -5065,6 +5127,8 @@ build_clone (tree fn, tree name, bool need_vtt_parm_p,
   DECL_CHAIN (clone) = DECL_CHAIN (fn);
   DECL_CHAIN (fn) = clone;
 
+  adjust_clone_attributes (fn, clone, name);
+
   return clone;
 }
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 8809842da3f..fb2b3f4db18 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5791,6 +5791,8 @@ struct GTY((for_user)) spec_entry
 
 extern int current_class_depth;
 
+void adjust_clone_attributes (tree fn, tree clone, tree name, bool = false);
+
 /* in decl.cc */
 
 /* An array of static vars & fns.  */
@@ -7026,6 +7028,7 @@ extern void do_push_parm_decls			(tree, tree, tree *);
 extern tree do_aggregate_paren_init		(tree, tree);
 
 /* in decl2.cc */
+extern void update_sym_alias_interface		(tree);
 extern void record_mangling			(tree, bool);
 extern void overwrite_mangling			(tree, tree);
 extern void note_mangling_alias			(tree, tree);
@@ -7600,6 +7603,7 @@ extern bool emit_tinfo_decl			(tree);
 extern unsigned get_pseudo_tinfo_index		(tree);
 extern tree get_pseudo_tinfo_type		(unsigned);
 extern tree build_if_nonnull			(tree, tree, tsubst_flags_t);
+extern void update_tinfo_sym_alias		(tree);
 
 /* in search.cc */
 extern tree get_parent_with_private_access 	(tree decl, tree binfo);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 4b685270097..cc7f7a99487 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -3277,6 +3277,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
 	      && TREE_STATIC (olddecl))))
     make_decl_rtl (olddecl);
 
+  symtab_node::remap_sym_alias_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.
@@ -3300,6 +3302,7 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
       FOR_EACH_CLONE (clone, olddecl)
 	{
 	  DECL_ATTRIBUTES (clone) = DECL_ATTRIBUTES (olddecl);
+	  adjust_clone_attributes (olddecl, clone, DECL_NAME (clone));
 	  DECL_PRESERVE_P (clone) |= DECL_PRESERVE_P (olddecl);
 	}
     }
@@ -10838,6 +10841,7 @@ grokfndecl (tree ctype,
     {
       cplus_decl_attributes (&decl, *attrlist, 0);
       *attrlist = NULL_TREE;
+      create_sym_alias_decls (decl);
     }
 
   if (DECL_HAS_CONTRACTS_P (decl))
diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
index 9e666e5eece..c7f009beaf0 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -1844,6 +1844,9 @@ cplus_decl_attributes (tree *decl, tree attributes, int flags)
   if (late_attrs)
     save_template_attributes (late_attrs, decl, flags);
 
+  if (TYPE_P (*decl) && attributes)
+    update_tinfo_sym_alias (*decl);
+
   /* Propagate deprecation out to the template.  */
   if (TREE_DEPRECATED (*decl))
     if (tree ti = get_template_info (*decl))
@@ -2200,6 +2203,47 @@ 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 sym aliases.  */
+
+void
+update_sym_alias_interface (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.  */
 
@@ -3007,6 +3051,8 @@ determine_visibility (tree decl)
        translation unit, we can make the type internal.  */
     constrain_visibility (decl, VISIBILITY_ANON, false);
 
+  update_sym_alias_interface (decl);
+
   /* If visibility changed and DECL already has DECL_RTL, ensure
      symbol flags are updated.  */
   if ((DECL_VISIBILITY (decl) != orig_visibility
@@ -3269,6 +3315,8 @@ tentative_decl_linkage (tree decl)
       else if (VAR_P (decl))
 	maybe_commonize_var (decl);
     }
+
+  update_sym_alias_interface (decl);
 }
 
 /* DECL is a FUNCTION_DECL or VAR_DECL.  If the object file linkage
@@ -3503,6 +3551,8 @@ import_export_decl (tree decl)
     }
 
   DECL_INTERFACE_KNOWN (decl) = 1;
+
+  update_sym_alias_interface (decl);
 }
 
 /* Return an expression that performs the destruction of DECL, which
diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc
index 76f1d44610a..3a19a571256 100644
--- a/gcc/cp/name-lookup.cc
+++ b/gcc/cp/name-lookup.cc
@@ -22,9 +22,11 @@ along with GCC; see the file COPYING3.  If not see
 #define INCLUDE_MEMORY
 #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"
@@ -3675,6 +3677,15 @@ push_local_extern_decl_alias (tree decl)
 	  /* Adjust visibility.  */
 	  determine_visibility (alias);
 	}
+      else if (DECL_P (alias))
+	DECL_ATTRIBUTES (alias)
+	  = targetm.merge_decl_attributes (alias, decl);
+      if (DECL_P (alias))
+	{
+	  symtab_node::remap_sym_alias_target (decl, alias);
+	  DECL_ATTRIBUTES (decl)
+	    = remove_attribute ("sym_alias", DECL_ATTRIBUTES (alias));
+	}
     }
 
   retrofit_lang_decl (decl);
diff --git a/gcc/cp/optimize.cc b/gcc/cp/optimize.cc
index 9e8926e4cc6..4a2e8cb4354 100644
--- a/gcc/cp/optimize.cc
+++ b/gcc/cp/optimize.cc
@@ -528,9 +528,12 @@ maybe_clone_body (tree fn)
       DECL_VISIBILITY_SPECIFIED (clone) = DECL_VISIBILITY_SPECIFIED (fn);
       DECL_DLLIMPORT_P (clone) = DECL_DLLIMPORT_P (fn);
       DECL_ATTRIBUTES (clone) = clone_attrs (DECL_ATTRIBUTES (fn));
+      adjust_clone_attributes (fn, clone, DECL_NAME (clone), true);
       DECL_DISREGARD_INLINE_LIMITS (clone) = DECL_DISREGARD_INLINE_LIMITS (fn);
       set_decl_section_name (clone, fn);
 
+      update_sym_alias_interface (clone);
+
       /* Adjust the parameter names and locations.  */
       parm = DECL_ARGUMENTS (fn);
       clone_parm = DECL_ARGUMENTS (clone);
diff --git a/gcc/cp/rtti.cc b/gcc/cp/rtti.cc
index 7878929c246..4bb7b8c8c6e 100644
--- a/gcc/cp/rtti.cc
+++ b/gcc/cp/rtti.cc
@@ -28,8 +28,10 @@ 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"
+#include "cgraph.h"
 
 /* C++ returns type information to the user in struct type_info
    objects. We also use type information to implement dynamic_cast and
@@ -479,8 +481,13 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
 	  = build_tree_list (get_identifier ("non overlapping"),
 			     NULL_TREE);
       else
+	/* Share the non overlapping attribute, without assuming it's
+	   the only attribute, but assuming it's the last if it's
+	   present.  There may be sym aliases too, and those are not
+	   to be shared.  */
 	DECL_ATTRIBUTES (d)
-	  = DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]);
+	  = lookup_attribute ("non overlapping",
+			      DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]));
 
       /* Mark the variable as undefined -- but remember that we can
 	 define it later if we need to do so.  */
@@ -492,6 +499,16 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
       if (CLASS_TYPE_P (type))
 	CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type)) = d;
 
+      /* Copy sym alias attributes from the type to the rtti obj decl.  */
+      tree *attrs = &DECL_ATTRIBUTES (d);
+      FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+	{
+	  tree attr = tree_cons (TREE_PURPOSE (sym), TREE_VALUE (sym), *attrs);
+	  *attrs = attr;
+	  attrs = &TREE_CHAIN (attr);
+	}
+      create_sym_alias_decls (d);
+
       /* Add decl to the global array of tinfo decls.  */
       vec_safe_push (unemitted_tinfo_decls, d);
     }
@@ -499,6 +516,58 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
   return d;
 }
 
+/* After modifying the attributes of TYPE, check whether tinfo was
+   already created and, if so, add to it any sym alias attributes
+   that were not already present.  */
+
+void
+update_tinfo_sym_alias (tree type)
+{
+  if (!TYPE_SIZE (type) || !CLASS_TYPE_P (type))
+    return;
+
+  tree d = CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type));
+  if (!d)
+    return;
+
+  bool first = true;
+  symtab_node *node = NULL;
+
+  tree *attrs = &DECL_ATTRIBUTES (d);
+  FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (d_sym, *attrs)
+	if (TREE_VALUE (sym) == TREE_VALUE (d_sym))
+	  {
+	    found = true;
+	    break;
+	  }
+
+      if (found)
+	continue;
+
+      tree attr = tree_cons (TREE_PURPOSE (sym),
+			     TREE_VALUE (sym),
+			     *attrs);
+      *attrs = attr;
+      attrs = &TREE_CHAIN (attr);
+
+      if (first)
+	{
+	  first = false;
+	  node = symtab_node::get (d);
+	}
+
+      if (!node)
+	continue;
+
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+      create_sym_alias_decl (d, id);
+    }
+}
+
 /* Return the type_info object for TYPE.  */
 
 tree
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 9592cfee1d2..f16571147d4 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -4141,6 +4141,43 @@ Function Attributes}, @ref{PowerPC Function Attributes},
 @ref{Nios II Function Attributes}, and @ref{S/390 Function Attributes}
 for details.
 
+@cindex @code{sym_alias} function attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} attribute causes @var{name} to be output as an
+alias to the function, with the same linkage and visibility as the
+function, when the function definition is output, provided that the
+function could be an alias target.  For instance,
+
+@smallexample
+void f (uint64_t) __attribute__ ((__sym_alias__ ("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},
+with the same linkage and visibility.  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.
+
+Aliases introduced with this attribute, such as @samp{f_u64} in the
+example above, are assembly symbol names: they do not undergo C++ name
+mangling, and are not made visible in any scope in the source language.
+They can, however, can be named as alias targets.
+
+This attribute requires assembler and object file support for aliases,
+and may not be available on all targets.
+
 @cindex @code{symver} function attribute
 @item symver ("@var{name2}@@@var{nodename}")
 On ELF targets this attribute creates a symbol version.  The @var{name2} part
@@ -8068,6 +8105,10 @@ will be placed in new, unique sections.
 
 This additional functionality requires Binutils version 2.36 or later.
 
+@cindex @code{sym_alias} variable attribute
+@item sym_alias ("@var{name}")
+See @pxref{Common Function Attributes}.
+
 @cindex @code{uninitialized} variable attribute
 @item uninitialized
 This attribute, attached to a variable with automatic storage, means that
@@ -9163,6 +9204,21 @@ is not supported; that is to say, if a given scalar object can be accessed
 through distinct types that assign a different storage order to it, then the
 behavior is undefined.
 
+@cindex @code{sym_alias} type attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} type 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__ ((__sym_alias__ ("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.
+
 @cindex @code{transparent_union} type attribute
 @item transparent_union
 
diff --git a/gcc/symtab.cc b/gcc/symtab.cc
index 0470509a98d..43ba88d7939 100644
--- a/gcc/symtab.cc
+++ b/gcc/symtab.cc
@@ -1943,6 +1943,44 @@ symtab_node::noninterposable_alias (symtab_node *node, void *data)
   return false;
 }
 
+/* Remap sym alias nodes recorded as aliasing REPLACED to alias
+   REPLACEMENT instead.  */
+
+void
+symtab_node::remap_sym_alias_target (tree replaced, tree replacement)
+{
+  if (!decl_in_symtab_p (replacement)
+      || !symtab_node::get (replacement))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (replaced))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      symtab_node *sym_node = symtab_node::get_for_asmname (id);
+
+      if (!sym_node)
+	{
+	  create_sym_alias_decl (replacement, id);
+	  continue;
+	}
+
+      gcc_assert (!sym_node->analyzed);
+      if (sym_node->alias_target != replaced)
+	continue;
+
+      // sym_node->definition = 0;
+
+      if (VAR_P (replaced))
+	// varpool_node::create_extra_name_alias (sym_node->decl, replacement);
+	varpool_node::create_alias (sym_node->decl, replacement);
+      else
+	cgraph_node::create_same_body_alias (sym_node->decl, replacement);
+	// 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-sym-alias-neg.c b/gcc/testsuite/c-c++-common/attr-sym-alias-neg.c
new file mode 100644
index 00000000000..f64e5bb1ac4
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/attr-sym-alias-neg.c
@@ -0,0 +1,80 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int f () {
+  int i __attribute__ ((sym_alias ("f_i"))); /* { dg-warning "ignored" } */
+  /* ??? X cannot be an alias target; should this be flagged? ... */
+  static int x __attribute__ ((sym_alias ("f_x")));
+  static int sx __attribute__ ((alias ("f_x"))); /* { dg-warning "ignored" } */
+  extern int xx __attribute__ ((alias ("f_x"))); /* { dg-warning "ignored" } */
+  return i;
+}
+
+/* ??? ... or should XR be accepted?  */
+extern int xr __attribute__ ((alias ("f_x"))); /* { dg-error "undefined" "" { xfail c++ } } */
+int dr __attribute__ ((alias ("f_x"))); /* { dg-error "defined both" } */
+
+
+struct s {
+  int j __attribute__ ((sym_alias ("s_j"))); /* { dg-warning "ignored" } */
+};
+
+
+int j __attribute__ ((sym_alias ("J_var")));
+void __attribute__ ((alias ("J_var")))
+j_fn (); /* { dg-error "between function and variable" } */
+
+
+void __attribute__ ((sym_alias ("K_fn")))
+k () {
+}
+extern int __attribute__ ((alias ("K_fn")))
+k_var; /* { dg-error "between function and variable" } */
+
+
+int l __attribute__ ((sym_alias ("L_fn")));
+
+/* These should be detected and reported, not because the names clash at the
+   source level, but because the asm symbols do.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+void
+L_fn () /* { dg-error "duplicate" "" { xfail *-*-* } } */
+{
+}
+
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __attribute__ ((sym_alias ("M_var")))
+m ()
+{
+}
+
+int M_var; /* { dg-error "duplicate" "" { xfail *-*-* } } */
+
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __attribute__ ((sym_alias ("N_sym")))
+n_fn ()
+{
+}
+
+int __attribute__ ((sym_alias ("N_sym")))
+n_var; /* { dg-error "duplicate" } */
+
+
+int __attribute__ ((sym_alias ("O_sym")))
+o_var;
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __attribute__ ((sym_alias ("O_sym")))
+o_fn () /* { dg-error "duplicate" } */
+{
+}
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-1.c b/gcc/testsuite/c-c++-common/goacc/declare-1.c
index 46ee01b6759..f2842893318 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-1.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-1.c
@@ -113,11 +113,11 @@ f_2 (void)
   int va3;
 #pragma acc declare device_resident(va3)
 
-#ifndef __cplusplus
+#if 0
   /* TODO PR90868
 
-     C: "error: variable '[...]' used more than once with '#pragma acc declare'".  */
-#else
+     "error: variable '[...]' used more than once with '#pragma acc declare'".  */
+
   extern int ve0;
 #pragma acc declare create(ve0)
 
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-2.c b/gcc/testsuite/c-c++-common/goacc/declare-2.c
index e2e22be57e9..aec59b69754 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-2.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-2.c
@@ -137,25 +137,25 @@ void
 f_pr90868_2 (void)
 {
   extern int we0;
-#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" } */
 
   extern int we1;
-#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" } */
 
   extern int *we2;
-#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" } */
 
   extern int we3;
-#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" } */
 
   extern int we4;
-#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" } */
 
   extern int we5;
-#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" } */
  
   extern int we6;
-#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" } */
 }
 
 
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
new file mode 100644
index 00000000000..61af50cb552
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
@@ -0,0 +1,39 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+int var_a = 1;
+
+void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+
+void
+foo_a ()
+{
+}
+
+
+int var_b;
+extern int var_b __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+
+void
+foo_b ()
+{
+}
+
+void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+
+
+int var_c __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+
+void __attribute__ ((__sym_alias__ ("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-sym-alias-2.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
new file mode 100644
index 00000000000..6f0d55ca42c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+struct s
+{
+  int mem __attribute__ ((__sym_alias__ ("MEMFOO"))); /* { dg-warning "attribute ignored" } */
+};
+
+void foo()
+{
+  extern void bar () __attribute__ ((__sym_alias__ ("FOOBAR")));
+  int var __attribute__ ((__sym_alias__ ("FOOVAR"))); /* { dg-warning "attribute ignored" } */
+}
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
new file mode 100644
index 00000000000..0052485a30a
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
@@ -0,0 +1,41 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int var_a = 1;
+
+void
+foo_a ()
+{
+  extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+}
+
+#if 0 // __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__ ((__sym_alias__ ("FOOVAR_B")));
+}
+
+int var_b;
+
+void __attribute__ ((__sym_alias__ ("FOOBAR_C")))
+foo_c ()
+{
+  void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+  /* Another sym for var_b.  */
+  extern int var_b __attribute__ ((__sym_alias__ ("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-sym-alias-4.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
new file mode 100644
index 00000000000..2beafa46379
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-require-alias "" } */
+
+int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A"))) = 42;
+
+int __attribute__ ((__sym_alias__ ("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/c-c++-common/torture/attr-sym-alias-5.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
new file mode 100644
index 00000000000..bee90136daf
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
@@ -0,0 +1,54 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+/* { dg-require-visibility "" } */
+
+int __attribute__ ((sym_alias ("A_hid"), visibility ("hidden"))) a;
+
+extern int __attribute__ ((sym_alias ("B_prt"))) b;
+
+int b __attribute__ ((visibility ("protected")));
+
+extern int __attribute__ ((sym_alias ("C_ntr"))) c;
+
+extern int c __attribute__ ((visibility ("internal")));
+
+int c;
+
+static int d __attribute__ ((sym_alias ("D_stt")));
+
+
+extern void __attribute__ ((sym_alias ("F_hid"), visibility ("hidden")))
+f ();
+
+void
+f () {
+}
+
+extern void __attribute__ ((sym_alias ("G_prt")))
+g ();
+
+void __attribute__ ((visibility ("protected")))
+g () {
+}
+
+extern void __attribute__ ((sym_alias ("H_ntr")))
+h ();
+
+void __attribute__ ((visibility ("internal")))
+h ();
+
+void h () {
+}
+
+static void __attribute__ ((sym_alias ("I_stt")))
+i () {
+}
+
+/* { dg-final { scan-assembler {hidden[^\n]*A_hid} } } */
+/* { dg-final { scan-assembler {protected[^\n]*B_prt} } } */
+/* { dg-final { scan-assembler {internal[^\n]*C_ntr} } } */
+/* { dg-final { scan-assembler-not {glob[^\n]*D_stt} } } */
+/* { dg-final { scan-assembler {hidden[^\n]*F_hid} } } */
+/* { dg-final { scan-assembler {protected[^\n]*G_prt} } } */
+/* { dg-final { scan-assembler {internal[^\n]*H_ntr} } } */
+/* { dg-final { scan-assembler-not {glob[^\n]*I_stt} } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
new file mode 100644
index 00000000000..579eda1a473
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
@@ -0,0 +1,72 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+class __attribute__ ((__sym_alias__ ("FOOCLS_A"),
+		      __sym_alias__ ("FOOCLS_A_Dupe"))) foo {
+  static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  void __attribute__ ((__sym_alias__ ("FOOBAR_A"))) bar ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+int foo::var = 1;
+
+foo::foo () {}
+
+void foo::bar () {}
+
+namespace b {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_B"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_B"))) foo ();
+    void __attribute__ ((__sym_alias__ ("FOOBAR_B"))) bar () {}
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_B"))) ~foo() {}
+  };
+
+  int foo::var = 2;
+
+  foo::foo () {
+    void (foo::*pbar)() = &foo::bar;
+  }
+}
+
+namespace c {
+  namespace cd {
+    class __attribute__ ((__sym_alias__ ("FOOCLS_C"))) foo {
+      static int var __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+      __attribute__ ((__sym_alias__ ("FOOCTR_C"))) foo () {
+	void (foo::*pbar)() = &foo::bar;
+      }
+      void __attribute__ ((__sym_alias__ ("FOOBAR_C"))) bar () {}
+      virtual __attribute__ ((__sym_alias__ ("FOODTR_C"))) ~foo() {}
+    };
+
+    int foo::var = 3;
+  }
+}
+
+/* { dg-final { scan-assembler "FOOCLS_A" } } */
+/* { dg-final { scan-assembler "FOOCLS_A_Dupe" } } */
+/* { 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-sym-alias-2.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
new file mode 100644
index 00000000000..6facfcc2cd9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+namespace {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_A"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo ();
+    void __attribute__ ((__sym_alias__ ("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-sym-alias-3.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
new file mode 100644
index 00000000000..42c7288ad4a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
@@ -0,0 +1,83 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+// sym can be applied to template function explicit instantiations.
+
+template <typename T>
+void
+fn(T) {
+};
+
+template void __attribute__ ((__sym_alias__ ("FOOFUN_UINT"))) fn<>(unsigned int);
+template void __attribute__ ((__sym_alias__ ("FOOFUN_LONG"))) fn<>(long);
+
+template<> void __attribute__ ((__sym_alias__ ("FOOFUN_CHAR"))) fn<>(char) {}
+
+
+template <typename T = void>
+struct
+foo {
+  virtual ~foo() {}
+
+  virtual void virtfun() {}
+
+  static void stfun() {}
+  void inlfun() {}
+};
+
+// Explicitly instantiate members before the enclosing class.
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_CHAR_VIRT"))) foo<char>::virtfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_CHAR_TI"))) foo<char>;
+
+// Though they're only output if the enclosing class is.
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_LONG_VIRT"))) foo<long>::virtfun();
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_LONG_TI_X"))) foo<long>;
+
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_VOID_ST"))) foo<void>::stfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_VOID_TI"))) foo<>;
+
+
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_X"))) foo<short>;
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_ST"))) foo<short>::stfun();
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_INL"))) foo<short>::inlfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_D"))) foo<short>;
+
+// Explicit specializations work too.
+
+template <>
+struct  __attribute__ ((__sym_alias__ ("FOOCLS_INT_TI")))
+foo<int>
+{
+  virtual ~foo() {}
+  virtual void __attribute__ ((__sym_alias__ ("FOOCLS_INT_VIRT"))) virtfun() {}
+};
+
+/* { dg-final { scan-assembler "FOOFUN_UINT" } } */
+/* { dg-final { scan-assembler "FOOFUN_LONG" } } */
+/* { dg-final { scan-assembler "FOOFUN_CHAR" } } */
+
+/* { dg-final { scan-assembler "FOOCLS_VOID_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_VOID_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_X" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_INL" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_D" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_TI_X" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_VIRT" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
new file mode 100644
index 00000000000..59b779de35d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+template <typename T = void>
+class
+__attribute__ ((__sym_alias__ ("FOOCLS")))
+foo // { dg-error "duplicate|already" }
+{
+  virtual ~foo() {}
+
+  template <typename U>
+  void
+    __attribute__ ((__sym_alias__ ("FOOTMF")))
+    tmemfun () {} // { dg-error "duplicate|already" }
+};
+
+template <typename T>
+void
+__attribute__ ((__sym_alias__ ("FOOTFN")))
+fn(T) { // { dg-error "duplicate|already" }
+};
+
+template class foo<>;
+template class foo<int>;
+template void foo<>::tmemfun<void>();
+template void foo<int>::tmemfun<void>();
+template void fn<>(int);
+template void fn<>(long);
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
new file mode 100644
index 00000000000..45d59b953c3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
@@ -0,0 +1,14 @@
+/* { dg-do compile { target c++11 } } */
+/* { dg-require-alias "" } */
+
+struct foo {
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+foo::foo () {}
+
+// Make sure the inherited cdtors don't duplicate the syms.
+struct bar : foo {
+  using foo::foo;
+};
diff --git a/gcc/varpool.cc b/gcc/varpool.cc
index e7b51b15e4a..33649906d1d 100644
--- a/gcc/varpool.cc
+++ b/gcc/varpool.cc
@@ -163,6 +163,9 @@ varpool_node::get_create (tree decl)
     }
 
   node->register_symbol ();
+
+  create_sym_alias_decls (decl);
+
   return node;
 }

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [gcc(refs/users/aoliva/heads/testme)] Introduce attribute sym_alias
@ 2023-12-01 18:03 Alexandre Oliva
  0 siblings, 0 replies; 33+ messages in thread
From: Alexandre Oliva @ 2023-12-01 18:03 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:05521f7e67c7e61b462da5b765f0ffcf47549043

commit 05521f7e67c7e61b462da5b765f0ffcf47549043
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Fri Dec 1 14:31:46 2023 -0300

    Introduce attribute sym_alias
    
    This patch introduces an attribute to add extra asm names (aliases)
    for a decl 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 class types, namespace-scoped
    variables and functions, static data members, member functions,
    explicit instantiations and specializations of template functions,
    members and classes.
    
    When applied to constructors or destructor, additional sym aliases
    with _Base and _Del suffixes are defined for variants other than
    complete-object ones.  This changes the assumption that clones always
    carry the same attributes as their abstract declarations, so there is
    now a function to adjust them.
    
    C++ also had a bug in which attributes from local extern declarations
    failed to be propagated to a preexisting corresponding
    namespace-scoped decl.  I've fixed that, and adjusted acc tests that
    distinguished between C and C++ in this regard.
    
    Applying the attribute to class types is only valid in C++, and the
    effect is to attach the alias to the RTTI object associated with the
    class type.
    
    for  gcc/ChangeLog
    
            * attribs.cc: Include cgraph.h.
            (decl_attributes): Allow late introduction of sym_alias in
            types.
            (create_sym_alias_decl, create_sym_alias_decls): New.
            * attribs.h: Declare them.
            (FOR_EACH_SYM_ALIAS): New macro.
            * cgraph.cc (cgraph_node::create): Create sym_alias decls.
            * varpool.cc (varpool_node::get_create): Create sym_alias
            decls.
            * cgraph.h (symtab_node::remap_sym_alias_target): New.
            * symtab.cc (symtab_node::remap_sym_alias_target): Define.
            * cgraphunit.cc (cgraph_node::analyze): Create alias_target
            node if needed.
            (analyze_functions): Fixup visibility of implicit alias only
            after its node is analyzed.
            * doc/extend.texi (sym_alias): Document for variables,
            functions and types.
    
    for  gcc/ada/ChangeLog
    
            * doc/gnat_rm/interfacing_to_other_languages.rst: Mention
            attribute sym_alias to give RTTI symbols mnemonic names.
            * doc/gnat_ugn/the_gnat_compilation_model.rst: Mention
            aliases.  Fix incorrect ref to C1 ctor variant.
    
    for  gcc/c-family/ChangeLog
    
            * c-ada-spec.cc (pp_asm_name): Use first sym_alias if
            available.
            * c-attribs.cc (handle_sym_alias_attribute): New.
            (c_common_attribute_table): Add sym_alias.
            (handle_copy_attribute): Do not copy sym_alias attribute.
    
    for  gcc/c/ChangeLog
    
            * c-decl.cc (duplicate_decls): Remap sym_alias target.
    
    for  gcc/cp/ChangeLog
    
            * class.cc (adjust_clone_attributes): New.
            (copy_fndecl_with_name, build_clone): Call it.
            * cp-tree.h (adjust_clone_attributes): Declare.
            (update_sym_alias_interface): Declare.
            (update_tinfo_sym_alias): Declare.
            * decl.cc (duplicate_decls): Remap sym_alias target.
            Adjust clone attributes.
            (grokfndecl): Tentatively create sym_alias decls after
            adding attributes in e.g. a template member function explicit
            instantiation.
            * decl2.cc (cplus_decl_attributes): Update tinfo sym_alias.
            (copy_interface, update_sym_alias_interface): New.
            (determine_visibility): Update sym_alias interface.
            (tentative_decl_linkage, import_export_decl): Likewise.
            * name-lookup.cc: Include target.h and cgraph.h.
            (push_local_extern_decl_alias): Merge attributes with
            namespace-scoped decl, and drop duplicate sym_alias.
            * optimize.cc (maybe_clone_body): Re-adjust attributes after
            cloning them.  Update sym_alias interface.
            * rtti.cc: Include attribs.h and cgraph.h.
            (get_tinfo_decl): Copy sym_alias attributes from type to tinfo
            decl.  Create sym_alias decls.
            (update_tinfo_sym_alias): New.
    
    for  gcc/testsuite/ChangeLog
    
            * c-c++-common/goacc/declare-1.c: Adjust.
            * c-c++-common/goacc/declare-2.c: Adjust.
            * c-c++-common/torture/attr-sym-alias-1.c: New.
            * c-c++-common/torture/attr-sym-alias-2.c: New.
            * c-c++-common/torture/attr-sym-alias-3.c: New.
            * c-c++-common/torture/attr-sym-alias-4.c: New.
            * c-c++-common/torture/attr-sym-alias-5.c: New.
            * c-c++-common/attr-sym-alias-neg.c: New.
            * g++.dg/torture/attr-sym-alias-1.C: New.
            * g++.dg/torture/attr-sym-alias-2.C: New.
            * g++.dg/torture/attr-sym-alias-3.C: New.
            * g++.dg/torture/attr-sym-alias-4.C: New.
            * g++.dg/torture/attr-sym-alias-5.C: New.

Diff:
---
 .../doc/gnat_rm/interfacing_to_other_languages.rst |  6 ++
 .../doc/gnat_ugn/the_gnat_compilation_model.rst    | 10 ++-
 gcc/attribs.cc                                     | 66 ++++++++++++++++-
 gcc/attribs.h                                      |  7 ++
 gcc/c-family/c-ada-spec.cc                         |  7 ++
 gcc/c-family/c-attribs.cc                          | 33 ++++++++-
 gcc/c/c-decl.cc                                    |  2 +
 gcc/cgraph.cc                                      |  2 +
 gcc/cgraph.h                                       |  4 ++
 gcc/cgraphunit.cc                                  |  2 +-
 gcc/cp/class.cc                                    | 64 +++++++++++++++++
 gcc/cp/cp-tree.h                                   |  4 ++
 gcc/cp/decl.cc                                     |  4 ++
 gcc/cp/decl2.cc                                    | 50 +++++++++++++
 gcc/cp/name-lookup.cc                              | 11 +++
 gcc/cp/optimize.cc                                 |  3 +
 gcc/cp/rtti.cc                                     | 71 +++++++++++++++++-
 gcc/doc/extend.texi                                | 56 +++++++++++++++
 gcc/symtab.cc                                      | 38 ++++++++++
 gcc/testsuite/c-c++-common/attr-sym-alias-neg.c    | 80 +++++++++++++++++++++
 gcc/testsuite/c-c++-common/goacc/declare-1.c       |  6 +-
 gcc/testsuite/c-c++-common/goacc/declare-2.c       | 14 ++--
 .../c-c++-common/torture/attr-sym-alias-1.c        | 39 ++++++++++
 .../c-c++-common/torture/attr-sym-alias-2.c        | 13 ++++
 .../c-c++-common/torture/attr-sym-alias-3.c        | 41 +++++++++++
 .../c-c++-common/torture/attr-sym-alias-4.c        | 28 ++++++++
 .../c-c++-common/torture/attr-sym-alias-5.c        | 54 ++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C    | 72 +++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C    | 26 +++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C    | 83 ++++++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C    | 28 ++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C    | 14 ++++
 gcc/varpool.cc                                     |  3 +
 33 files changed, 922 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 ad0be511d48..b8de16ebf7c 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 ``sym_alias`` 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 fd15459203a..20045698962 100644
--- a/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
+++ b/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
@@ -4274,6 +4274,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__ ((__sym_alias__ ("Ctor_For_Animal")))
         Animal() {Age_Count = 0;};
       private:
         int Age_Count;
@@ -4306,6 +4307,7 @@ both Carnivore and Domestic, that is:
         virtual int  Number_Of_Teeth ();
         virtual void Set_Owner (char* Name);
 
+        __attribute__ ((__sym_alias__ ("Ctor_For_Dog"))) // mnemonic alias
         Dog(); // Constructor
       private:
         int  Tooth_Count;
@@ -4344,7 +4346,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;
@@ -4360,7 +4363,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,7 +4385,8 @@ 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.
+mangled name (or an alias) 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.cc b/gcc/attribs.cc
index c7209c26acc..c75ca6974cb 100644
--- a/gcc/attribs.cc
+++ b/gcc/attribs.cc
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "target.h"
 #include "tree.h"
+#include "cgraph.h"
 #include "stringpool.h"
 #include "diagnostic-core.h"
 #include "attribs.h"
@@ -825,7 +826,8 @@ decl_attributes (tree *node, tree attributes, int flags,
 
       if (TYPE_P (*anode)
 	  && (flags & (int) ATTR_FLAG_TYPE_IN_PLACE)
-	  && COMPLETE_TYPE_P (*anode))
+	  && COMPLETE_TYPE_P (*anode)
+	  && !is_attribute_p ("sym_alias", name))
 	{
 	  warning (OPT_Wattributes, "type attributes ignored after type is already defined");
 	  continue;
@@ -2640,6 +2642,68 @@ attr_access::array_as_string (tree type) const
   return typstr;
 }
 
+/* Create a sym attribute for DECL to be visible with linkage name ID.  */
+
+tree
+create_sym_alias_decl (tree decl, tree id)
+{
+  const char *attr_str = "sym_alias";
+
+  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;
+
+      tree attr_name = get_identifier (attr_str);
+      error_at (DECL_SOURCE_LOCATION (decl),
+		"duplicate symbol name %qE in %qE attribute of %qD",
+		id, attr_name, decl);
+      inform (DECL_SOURCE_LOCATION (sym_node->decl),
+	      "already used by %qD", sym_node->decl);
+    }
+
+  tree clone = copy_node (decl);
+  DECL_ATTRIBUTES (clone) = remove_attribute (attr_str,
+					      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_extra_name_alias (clone, decl);
+    varpool_node::create_alias (clone, decl);
+  else
+    cgraph_node::create_same_body_alias (clone, decl);
+    // cgraph_node::create_alias (clone, decl);
+
+  return clone;
+}
+
+/* Create decls for all sym aliases requested in DECL's attributes.  */
+
+void
+create_sym_alias_decls (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl)
+      || DECL_ABSTRACT_P (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      create_sym_alias_decl (decl, id);
+    }
+}
+
 #if CHECKING_P
 
 namespace selftest
diff --git a/gcc/attribs.h b/gcc/attribs.h
index 84a43658a70..156f2c20588 100644
--- a/gcc/attribs.h
+++ b/gcc/attribs.h
@@ -398,4 +398,11 @@ extern void init_attr_rdwr_indices (rdwr_map *, tree);
 extern attr_access *get_parm_access (rdwr_map &, tree,
 				     tree = current_function_decl);
 
+extern tree create_sym_alias_decl (tree, tree);
+extern void create_sym_alias_decls (tree);
+
+#define FOR_EACH_SYM_ALIAS(sym, attrs)					\
+  for (tree sym = lookup_attribute ("sym_alias", (attrs));		\
+       sym; sym = lookup_attribute ("sym_alias", TREE_CHAIN (sym)))
+
 #endif // GCC_ATTRIBS_H
diff --git a/gcc/c-family/c-ada-spec.cc b/gcc/c-family/c-ada-spec.cc
index 050994d8416..5042b9cfecd 100644
--- a/gcc/c-family/c-ada-spec.cc
+++ b/gcc/c-family/c-ada-spec.cc
@@ -1431,6 +1431,13 @@ pp_ada_tree_identifier (pretty_printer *buffer, tree node, tree type,
 static void
 pp_asm_name (pretty_printer *buffer, tree t)
 {
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (t))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.cc b/gcc/c-family/c-attribs.cc
index 2b20e58c922..392437eab3b 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -108,7 +108,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_sym_alias_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,
@@ -388,6 +389,8 @@ const struct attribute_spec c_common_attribute_table[] =
 			      handle_alias_attribute, NULL },
   { "weakref",                0, 1, true,  false, false, false,
 			      handle_weakref_attribute, NULL },
+  { "sym_alias",              1, 1, false,  false, false, false,
+			      handle_sym_alias_attribute, NULL },
   { "no_instrument_function", 0, 0, true,  false, false, false,
 			      handle_no_instrument_function_attribute,
 			      NULL },
@@ -3002,7 +3005,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
@@ -3012,7 +3015,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
@@ -3022,6 +3025,29 @@ handle_alias_attribute (tree *node, tree name, tree args,
   return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs);
 }
 
+/* Handle a "sym_alias" attribute; arguments as in struct
+   attribute_spec.handler.  */
+
+static tree
+handle_sym_alias_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 (decl_in_symtab_p (node))
+    *no_add_attrs = false;
+  else if (TYPE_P (node) && c_dialect_cxx ())
+    *no_add_attrs = false;
+  else
+    return error_mark_node;
+
+  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.  */
 
@@ -3155,6 +3181,7 @@ handle_copy_attribute (tree *node, tree name, tree args,
 	      || is_attribute_p ("visibility", atname)
 	      || is_attribute_p ("weak", atname)
 	      || is_attribute_p ("weakref", atname)
+	      || is_attribute_p ("sym_alias", atname)
 	      || is_attribute_p ("target_clones", atname))
 	    continue;
 
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index d388e123704..c5a7e56f4ec 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -3117,6 +3117,8 @@ duplicate_decls (tree newdecl, tree olddecl)
 
   merge_decls (newdecl, olddecl, newtype, oldtype);
 
+  symtab_node::remap_sym_alias_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.cc b/gcc/cgraph.cc
index f93259a8c70..0bef2179e89 100644
--- a/gcc/cgraph.cc
+++ b/gcc/cgraph.cc
@@ -523,6 +523,8 @@ cgraph_node::create (tree decl)
   node->register_symbol ();
   maybe_record_nested_function (node);
 
+  create_sym_alias_decls (decl);
+
   return node;
 }
 
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index cfdd9f693a8..cb5ac01f7e2 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -327,6 +327,10 @@ public:
   /* Return DECL that alias is aliasing.  */
   inline tree get_alias_target_tree ();
 
+  /* Remap sym alias nodes recorded as aliasing REPLACED to alias REPLACEMENT
+     instead.  */
+  static void remap_sym_alias_target (tree replaced, tree replacement);
+
   /* Set section for symbol and its aliases.  */
   void set_section (const char *section);
 
diff --git a/gcc/cgraphunit.cc b/gcc/cgraphunit.cc
index bccd2f2abb5..eb2d05094e9 100644
--- a/gcc/cgraphunit.cc
+++ b/gcc/cgraphunit.cc
@@ -1175,7 +1175,7 @@ analyze_functions (bool first_time)
      C++ FE is confused about the COMDAT groups being right.  */
   if (symtab->cpp_implicit_aliases_done)
     FOR_EACH_SYMBOL (node)
-      if (node->cpp_implicit_alias)
+      if (node->cpp_implicit_alias && node->analyzed)
 	  node->fixup_same_cpp_alias_visibility (node->get_alias_target ());
   build_type_inheritance_graph ();
 
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index 6fdb56abfb9..fa8fc63db82 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -4896,6 +4896,68 @@ check_methods (tree t)
     }
 }
 
+/* Adjust sym alias name for CLONE, cloned from FN and named NAME,
+   if it is a cdtor, and drop the sym alias from other clones.  */
+
+void
+adjust_clone_attributes (tree fn, tree clone, tree name, bool skip_copy_p)
+{
+  if (IDENTIFIER_CDTOR_P (name))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	{
+	  found = true;
+	  break;
+	}
+
+      if (found
+	  && (name == complete_ctor_identifier
+	      || name == complete_dtor_identifier))
+	{
+	  /* Reuse the sym alias decls created for the primary cdtor
+	     decl.  */
+	  symtab_node::remap_sym_alias_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);
+
+	  if (!skip_copy_p)
+	    DECL_ATTRIBUTES (clone) = copy_list (DECL_ATTRIBUTES (clone));
+
+	  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	    {
+	      /* We need to copy this even with skip_copy_p, because
+		 even then copying was shallow.  */
+	      TREE_VALUE (sym) = copy_list (TREE_VALUE (sym));
+	      /* Append suf to the sym alias name.  */
+	      tree str = TREE_VALUE (TREE_VALUE (sym));
+	      char *symname = concat (TREE_STRING_POINTER (str), suf, NULL);
+	      str = build_string (TREE_STRING_LENGTH (str) + xlen, symname);
+	      TREE_VALUE (TREE_VALUE (sym)) = str;
+	      free (symname);
+	    }
+
+	  if (symtab_node::get (clone))
+	    create_sym_alias_decls (clone);
+	}
+    }
+  else
+    DECL_ATTRIBUTES (clone)
+      = remove_attribute ("sym_alias", DECL_ATTRIBUTES (clone));
+}
+
 /* FN is constructor, destructor or operator function.  Clone the
    declaration to create a NAME'd variant.  NEED_VTT_PARM_P and
    OMIT_INHERITED_PARMS_P are relevant if it's a cdtor.  */
@@ -5065,6 +5127,8 @@ build_clone (tree fn, tree name, bool need_vtt_parm_p,
   DECL_CHAIN (clone) = DECL_CHAIN (fn);
   DECL_CHAIN (fn) = clone;
 
+  adjust_clone_attributes (fn, clone, name);
+
   return clone;
 }
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 8809842da3f..fb2b3f4db18 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5791,6 +5791,8 @@ struct GTY((for_user)) spec_entry
 
 extern int current_class_depth;
 
+void adjust_clone_attributes (tree fn, tree clone, tree name, bool = false);
+
 /* in decl.cc */
 
 /* An array of static vars & fns.  */
@@ -7026,6 +7028,7 @@ extern void do_push_parm_decls			(tree, tree, tree *);
 extern tree do_aggregate_paren_init		(tree, tree);
 
 /* in decl2.cc */
+extern void update_sym_alias_interface		(tree);
 extern void record_mangling			(tree, bool);
 extern void overwrite_mangling			(tree, tree);
 extern void note_mangling_alias			(tree, tree);
@@ -7600,6 +7603,7 @@ extern bool emit_tinfo_decl			(tree);
 extern unsigned get_pseudo_tinfo_index		(tree);
 extern tree get_pseudo_tinfo_type		(unsigned);
 extern tree build_if_nonnull			(tree, tree, tsubst_flags_t);
+extern void update_tinfo_sym_alias		(tree);
 
 /* in search.cc */
 extern tree get_parent_with_private_access 	(tree decl, tree binfo);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 4b685270097..cc7f7a99487 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -3277,6 +3277,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
 	      && TREE_STATIC (olddecl))))
     make_decl_rtl (olddecl);
 
+  symtab_node::remap_sym_alias_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.
@@ -3300,6 +3302,7 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
       FOR_EACH_CLONE (clone, olddecl)
 	{
 	  DECL_ATTRIBUTES (clone) = DECL_ATTRIBUTES (olddecl);
+	  adjust_clone_attributes (olddecl, clone, DECL_NAME (clone));
 	  DECL_PRESERVE_P (clone) |= DECL_PRESERVE_P (olddecl);
 	}
     }
@@ -10838,6 +10841,7 @@ grokfndecl (tree ctype,
     {
       cplus_decl_attributes (&decl, *attrlist, 0);
       *attrlist = NULL_TREE;
+      create_sym_alias_decls (decl);
     }
 
   if (DECL_HAS_CONTRACTS_P (decl))
diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
index 9e666e5eece..c7f009beaf0 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -1844,6 +1844,9 @@ cplus_decl_attributes (tree *decl, tree attributes, int flags)
   if (late_attrs)
     save_template_attributes (late_attrs, decl, flags);
 
+  if (TYPE_P (*decl) && attributes)
+    update_tinfo_sym_alias (*decl);
+
   /* Propagate deprecation out to the template.  */
   if (TREE_DEPRECATED (*decl))
     if (tree ti = get_template_info (*decl))
@@ -2200,6 +2203,47 @@ 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 sym aliases.  */
+
+void
+update_sym_alias_interface (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.  */
 
@@ -3007,6 +3051,8 @@ determine_visibility (tree decl)
        translation unit, we can make the type internal.  */
     constrain_visibility (decl, VISIBILITY_ANON, false);
 
+  update_sym_alias_interface (decl);
+
   /* If visibility changed and DECL already has DECL_RTL, ensure
      symbol flags are updated.  */
   if ((DECL_VISIBILITY (decl) != orig_visibility
@@ -3269,6 +3315,8 @@ tentative_decl_linkage (tree decl)
       else if (VAR_P (decl))
 	maybe_commonize_var (decl);
     }
+
+  update_sym_alias_interface (decl);
 }
 
 /* DECL is a FUNCTION_DECL or VAR_DECL.  If the object file linkage
@@ -3503,6 +3551,8 @@ import_export_decl (tree decl)
     }
 
   DECL_INTERFACE_KNOWN (decl) = 1;
+
+  update_sym_alias_interface (decl);
 }
 
 /* Return an expression that performs the destruction of DECL, which
diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc
index 76f1d44610a..3a19a571256 100644
--- a/gcc/cp/name-lookup.cc
+++ b/gcc/cp/name-lookup.cc
@@ -22,9 +22,11 @@ along with GCC; see the file COPYING3.  If not see
 #define INCLUDE_MEMORY
 #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"
@@ -3675,6 +3677,15 @@ push_local_extern_decl_alias (tree decl)
 	  /* Adjust visibility.  */
 	  determine_visibility (alias);
 	}
+      else if (DECL_P (alias))
+	DECL_ATTRIBUTES (alias)
+	  = targetm.merge_decl_attributes (alias, decl);
+      if (DECL_P (alias))
+	{
+	  symtab_node::remap_sym_alias_target (decl, alias);
+	  DECL_ATTRIBUTES (decl)
+	    = remove_attribute ("sym_alias", DECL_ATTRIBUTES (alias));
+	}
     }
 
   retrofit_lang_decl (decl);
diff --git a/gcc/cp/optimize.cc b/gcc/cp/optimize.cc
index 9e8926e4cc6..4a2e8cb4354 100644
--- a/gcc/cp/optimize.cc
+++ b/gcc/cp/optimize.cc
@@ -528,9 +528,12 @@ maybe_clone_body (tree fn)
       DECL_VISIBILITY_SPECIFIED (clone) = DECL_VISIBILITY_SPECIFIED (fn);
       DECL_DLLIMPORT_P (clone) = DECL_DLLIMPORT_P (fn);
       DECL_ATTRIBUTES (clone) = clone_attrs (DECL_ATTRIBUTES (fn));
+      adjust_clone_attributes (fn, clone, DECL_NAME (clone), true);
       DECL_DISREGARD_INLINE_LIMITS (clone) = DECL_DISREGARD_INLINE_LIMITS (fn);
       set_decl_section_name (clone, fn);
 
+      update_sym_alias_interface (clone);
+
       /* Adjust the parameter names and locations.  */
       parm = DECL_ARGUMENTS (fn);
       clone_parm = DECL_ARGUMENTS (clone);
diff --git a/gcc/cp/rtti.cc b/gcc/cp/rtti.cc
index 7878929c246..4bb7b8c8c6e 100644
--- a/gcc/cp/rtti.cc
+++ b/gcc/cp/rtti.cc
@@ -28,8 +28,10 @@ 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"
+#include "cgraph.h"
 
 /* C++ returns type information to the user in struct type_info
    objects. We also use type information to implement dynamic_cast and
@@ -479,8 +481,13 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
 	  = build_tree_list (get_identifier ("non overlapping"),
 			     NULL_TREE);
       else
+	/* Share the non overlapping attribute, without assuming it's
+	   the only attribute, but assuming it's the last if it's
+	   present.  There may be sym aliases too, and those are not
+	   to be shared.  */
 	DECL_ATTRIBUTES (d)
-	  = DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]);
+	  = lookup_attribute ("non overlapping",
+			      DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]));
 
       /* Mark the variable as undefined -- but remember that we can
 	 define it later if we need to do so.  */
@@ -492,6 +499,16 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
       if (CLASS_TYPE_P (type))
 	CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type)) = d;
 
+      /* Copy sym alias attributes from the type to the rtti obj decl.  */
+      tree *attrs = &DECL_ATTRIBUTES (d);
+      FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+	{
+	  tree attr = tree_cons (TREE_PURPOSE (sym), TREE_VALUE (sym), *attrs);
+	  *attrs = attr;
+	  attrs = &TREE_CHAIN (attr);
+	}
+      create_sym_alias_decls (d);
+
       /* Add decl to the global array of tinfo decls.  */
       vec_safe_push (unemitted_tinfo_decls, d);
     }
@@ -499,6 +516,58 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
   return d;
 }
 
+/* After modifying the attributes of TYPE, check whether tinfo was
+   already created and, if so, add to it any sym alias attributes
+   that were not already present.  */
+
+void
+update_tinfo_sym_alias (tree type)
+{
+  if (!TYPE_SIZE (type) || !CLASS_TYPE_P (type))
+    return;
+
+  tree d = CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type));
+  if (!d)
+    return;
+
+  bool first = true;
+  symtab_node *node = NULL;
+
+  tree *attrs = &DECL_ATTRIBUTES (d);
+  FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (d_sym, *attrs)
+	if (TREE_VALUE (sym) == TREE_VALUE (d_sym))
+	  {
+	    found = true;
+	    break;
+	  }
+
+      if (found)
+	continue;
+
+      tree attr = tree_cons (TREE_PURPOSE (sym),
+			     TREE_VALUE (sym),
+			     *attrs);
+      *attrs = attr;
+      attrs = &TREE_CHAIN (attr);
+
+      if (first)
+	{
+	  first = false;
+	  node = symtab_node::get (d);
+	}
+
+      if (!node)
+	continue;
+
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+      create_sym_alias_decl (d, id);
+    }
+}
+
 /* Return the type_info object for TYPE.  */
 
 tree
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 9592cfee1d2..f16571147d4 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -4141,6 +4141,43 @@ Function Attributes}, @ref{PowerPC Function Attributes},
 @ref{Nios II Function Attributes}, and @ref{S/390 Function Attributes}
 for details.
 
+@cindex @code{sym_alias} function attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} attribute causes @var{name} to be output as an
+alias to the function, with the same linkage and visibility as the
+function, when the function definition is output, provided that the
+function could be an alias target.  For instance,
+
+@smallexample
+void f (uint64_t) __attribute__ ((__sym_alias__ ("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},
+with the same linkage and visibility.  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.
+
+Aliases introduced with this attribute, such as @samp{f_u64} in the
+example above, are assembly symbol names: they do not undergo C++ name
+mangling, and are not made visible in any scope in the source language.
+They can, however, can be named as alias targets.
+
+This attribute requires assembler and object file support for aliases,
+and may not be available on all targets.
+
 @cindex @code{symver} function attribute
 @item symver ("@var{name2}@@@var{nodename}")
 On ELF targets this attribute creates a symbol version.  The @var{name2} part
@@ -8068,6 +8105,10 @@ will be placed in new, unique sections.
 
 This additional functionality requires Binutils version 2.36 or later.
 
+@cindex @code{sym_alias} variable attribute
+@item sym_alias ("@var{name}")
+See @pxref{Common Function Attributes}.
+
 @cindex @code{uninitialized} variable attribute
 @item uninitialized
 This attribute, attached to a variable with automatic storage, means that
@@ -9163,6 +9204,21 @@ is not supported; that is to say, if a given scalar object can be accessed
 through distinct types that assign a different storage order to it, then the
 behavior is undefined.
 
+@cindex @code{sym_alias} type attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} type 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__ ((__sym_alias__ ("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.
+
 @cindex @code{transparent_union} type attribute
 @item transparent_union
 
diff --git a/gcc/symtab.cc b/gcc/symtab.cc
index 0470509a98d..43ba88d7939 100644
--- a/gcc/symtab.cc
+++ b/gcc/symtab.cc
@@ -1943,6 +1943,44 @@ symtab_node::noninterposable_alias (symtab_node *node, void *data)
   return false;
 }
 
+/* Remap sym alias nodes recorded as aliasing REPLACED to alias
+   REPLACEMENT instead.  */
+
+void
+symtab_node::remap_sym_alias_target (tree replaced, tree replacement)
+{
+  if (!decl_in_symtab_p (replacement)
+      || !symtab_node::get (replacement))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (replaced))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      symtab_node *sym_node = symtab_node::get_for_asmname (id);
+
+      if (!sym_node)
+	{
+	  create_sym_alias_decl (replacement, id);
+	  continue;
+	}
+
+      gcc_assert (!sym_node->analyzed);
+      if (sym_node->alias_target != replaced)
+	continue;
+
+      // sym_node->definition = 0;
+
+      if (VAR_P (replaced))
+	// varpool_node::create_extra_name_alias (sym_node->decl, replacement);
+	varpool_node::create_alias (sym_node->decl, replacement);
+      else
+	cgraph_node::create_same_body_alias (sym_node->decl, replacement);
+	// 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-sym-alias-neg.c b/gcc/testsuite/c-c++-common/attr-sym-alias-neg.c
new file mode 100644
index 00000000000..f64e5bb1ac4
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/attr-sym-alias-neg.c
@@ -0,0 +1,80 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int f () {
+  int i __attribute__ ((sym_alias ("f_i"))); /* { dg-warning "ignored" } */
+  /* ??? X cannot be an alias target; should this be flagged? ... */
+  static int x __attribute__ ((sym_alias ("f_x")));
+  static int sx __attribute__ ((alias ("f_x"))); /* { dg-warning "ignored" } */
+  extern int xx __attribute__ ((alias ("f_x"))); /* { dg-warning "ignored" } */
+  return i;
+}
+
+/* ??? ... or should XR be accepted?  */
+extern int xr __attribute__ ((alias ("f_x"))); /* { dg-error "undefined" "" { xfail c++ } } */
+int dr __attribute__ ((alias ("f_x"))); /* { dg-error "defined both" } */
+
+
+struct s {
+  int j __attribute__ ((sym_alias ("s_j"))); /* { dg-warning "ignored" } */
+};
+
+
+int j __attribute__ ((sym_alias ("J_var")));
+void __attribute__ ((alias ("J_var")))
+j_fn (); /* { dg-error "between function and variable" } */
+
+
+void __attribute__ ((sym_alias ("K_fn")))
+k () {
+}
+extern int __attribute__ ((alias ("K_fn")))
+k_var; /* { dg-error "between function and variable" } */
+
+
+int l __attribute__ ((sym_alias ("L_fn")));
+
+/* These should be detected and reported, not because the names clash at the
+   source level, but because the asm symbols do.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+void
+L_fn () /* { dg-error "duplicate" "" { xfail *-*-* } } */
+{
+}
+
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __attribute__ ((sym_alias ("M_var")))
+m ()
+{
+}
+
+int M_var; /* { dg-error "duplicate" "" { xfail *-*-* } } */
+
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __attribute__ ((sym_alias ("N_sym")))
+n_fn ()
+{
+}
+
+int __attribute__ ((sym_alias ("N_sym")))
+n_var; /* { dg-error "duplicate" } */
+
+
+int __attribute__ ((sym_alias ("O_sym")))
+o_var;
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __attribute__ ((sym_alias ("O_sym")))
+o_fn () /* { dg-error "duplicate" } */
+{
+}
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-1.c b/gcc/testsuite/c-c++-common/goacc/declare-1.c
index 46ee01b6759..f2842893318 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-1.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-1.c
@@ -113,11 +113,11 @@ f_2 (void)
   int va3;
 #pragma acc declare device_resident(va3)
 
-#ifndef __cplusplus
+#if 0
   /* TODO PR90868
 
-     C: "error: variable '[...]' used more than once with '#pragma acc declare'".  */
-#else
+     "error: variable '[...]' used more than once with '#pragma acc declare'".  */
+
   extern int ve0;
 #pragma acc declare create(ve0)
 
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-2.c b/gcc/testsuite/c-c++-common/goacc/declare-2.c
index e2e22be57e9..aec59b69754 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-2.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-2.c
@@ -137,25 +137,25 @@ void
 f_pr90868_2 (void)
 {
   extern int we0;
-#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" } */
 
   extern int we1;
-#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" } */
 
   extern int *we2;
-#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" } */
 
   extern int we3;
-#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" } */
 
   extern int we4;
-#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" } */
 
   extern int we5;
-#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" } */
  
   extern int we6;
-#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" } */
 }
 
 
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
new file mode 100644
index 00000000000..61af50cb552
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
@@ -0,0 +1,39 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+int var_a = 1;
+
+void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+
+void
+foo_a ()
+{
+}
+
+
+int var_b;
+extern int var_b __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+
+void
+foo_b ()
+{
+}
+
+void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+
+
+int var_c __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+
+void __attribute__ ((__sym_alias__ ("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-sym-alias-2.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
new file mode 100644
index 00000000000..6f0d55ca42c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+struct s
+{
+  int mem __attribute__ ((__sym_alias__ ("MEMFOO"))); /* { dg-warning "attribute ignored" } */
+};
+
+void foo()
+{
+  extern void bar () __attribute__ ((__sym_alias__ ("FOOBAR")));
+  int var __attribute__ ((__sym_alias__ ("FOOVAR"))); /* { dg-warning "attribute ignored" } */
+}
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
new file mode 100644
index 00000000000..0052485a30a
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
@@ -0,0 +1,41 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int var_a = 1;
+
+void
+foo_a ()
+{
+  extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+}
+
+#if 0 // __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__ ((__sym_alias__ ("FOOVAR_B")));
+}
+
+int var_b;
+
+void __attribute__ ((__sym_alias__ ("FOOBAR_C")))
+foo_c ()
+{
+  void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+  /* Another sym for var_b.  */
+  extern int var_b __attribute__ ((__sym_alias__ ("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-sym-alias-4.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
new file mode 100644
index 00000000000..2beafa46379
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-require-alias "" } */
+
+int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A"))) = 42;
+
+int __attribute__ ((__sym_alias__ ("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/c-c++-common/torture/attr-sym-alias-5.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
new file mode 100644
index 00000000000..bee90136daf
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
@@ -0,0 +1,54 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+/* { dg-require-visibility "" } */
+
+int __attribute__ ((sym_alias ("A_hid"), visibility ("hidden"))) a;
+
+extern int __attribute__ ((sym_alias ("B_prt"))) b;
+
+int b __attribute__ ((visibility ("protected")));
+
+extern int __attribute__ ((sym_alias ("C_ntr"))) c;
+
+extern int c __attribute__ ((visibility ("internal")));
+
+int c;
+
+static int d __attribute__ ((sym_alias ("D_stt")));
+
+
+extern void __attribute__ ((sym_alias ("F_hid"), visibility ("hidden")))
+f ();
+
+void
+f () {
+}
+
+extern void __attribute__ ((sym_alias ("G_prt")))
+g ();
+
+void __attribute__ ((visibility ("protected")))
+g () {
+}
+
+extern void __attribute__ ((sym_alias ("H_ntr")))
+h ();
+
+void __attribute__ ((visibility ("internal")))
+h ();
+
+void h () {
+}
+
+static void __attribute__ ((sym_alias ("I_stt")))
+i () {
+}
+
+/* { dg-final { scan-assembler {hidden[^\n]*A_hid} } } */
+/* { dg-final { scan-assembler {protected[^\n]*B_prt} } } */
+/* { dg-final { scan-assembler {internal[^\n]*C_ntr} } } */
+/* { dg-final { scan-assembler-not {glob[^\n]*D_stt} } } */
+/* { dg-final { scan-assembler {hidden[^\n]*F_hid} } } */
+/* { dg-final { scan-assembler {protected[^\n]*G_prt} } } */
+/* { dg-final { scan-assembler {internal[^\n]*H_ntr} } } */
+/* { dg-final { scan-assembler-not {glob[^\n]*I_stt} } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
new file mode 100644
index 00000000000..579eda1a473
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
@@ -0,0 +1,72 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+class __attribute__ ((__sym_alias__ ("FOOCLS_A"),
+		      __sym_alias__ ("FOOCLS_A_Dupe"))) foo {
+  static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  void __attribute__ ((__sym_alias__ ("FOOBAR_A"))) bar ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+int foo::var = 1;
+
+foo::foo () {}
+
+void foo::bar () {}
+
+namespace b {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_B"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_B"))) foo ();
+    void __attribute__ ((__sym_alias__ ("FOOBAR_B"))) bar () {}
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_B"))) ~foo() {}
+  };
+
+  int foo::var = 2;
+
+  foo::foo () {
+    void (foo::*pbar)() = &foo::bar;
+  }
+}
+
+namespace c {
+  namespace cd {
+    class __attribute__ ((__sym_alias__ ("FOOCLS_C"))) foo {
+      static int var __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+      __attribute__ ((__sym_alias__ ("FOOCTR_C"))) foo () {
+	void (foo::*pbar)() = &foo::bar;
+      }
+      void __attribute__ ((__sym_alias__ ("FOOBAR_C"))) bar () {}
+      virtual __attribute__ ((__sym_alias__ ("FOODTR_C"))) ~foo() {}
+    };
+
+    int foo::var = 3;
+  }
+}
+
+/* { dg-final { scan-assembler "FOOCLS_A" } } */
+/* { dg-final { scan-assembler "FOOCLS_A_Dupe" } } */
+/* { 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-sym-alias-2.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
new file mode 100644
index 00000000000..6facfcc2cd9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+namespace {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_A"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo ();
+    void __attribute__ ((__sym_alias__ ("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-sym-alias-3.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
new file mode 100644
index 00000000000..42c7288ad4a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
@@ -0,0 +1,83 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+// sym can be applied to template function explicit instantiations.
+
+template <typename T>
+void
+fn(T) {
+};
+
+template void __attribute__ ((__sym_alias__ ("FOOFUN_UINT"))) fn<>(unsigned int);
+template void __attribute__ ((__sym_alias__ ("FOOFUN_LONG"))) fn<>(long);
+
+template<> void __attribute__ ((__sym_alias__ ("FOOFUN_CHAR"))) fn<>(char) {}
+
+
+template <typename T = void>
+struct
+foo {
+  virtual ~foo() {}
+
+  virtual void virtfun() {}
+
+  static void stfun() {}
+  void inlfun() {}
+};
+
+// Explicitly instantiate members before the enclosing class.
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_CHAR_VIRT"))) foo<char>::virtfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_CHAR_TI"))) foo<char>;
+
+// Though they're only output if the enclosing class is.
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_LONG_VIRT"))) foo<long>::virtfun();
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_LONG_TI_X"))) foo<long>;
+
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_VOID_ST"))) foo<void>::stfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_VOID_TI"))) foo<>;
+
+
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_X"))) foo<short>;
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_ST"))) foo<short>::stfun();
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_INL"))) foo<short>::inlfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_D"))) foo<short>;
+
+// Explicit specializations work too.
+
+template <>
+struct  __attribute__ ((__sym_alias__ ("FOOCLS_INT_TI")))
+foo<int>
+{
+  virtual ~foo() {}
+  virtual void __attribute__ ((__sym_alias__ ("FOOCLS_INT_VIRT"))) virtfun() {}
+};
+
+/* { dg-final { scan-assembler "FOOFUN_UINT" } } */
+/* { dg-final { scan-assembler "FOOFUN_LONG" } } */
+/* { dg-final { scan-assembler "FOOFUN_CHAR" } } */
+
+/* { dg-final { scan-assembler "FOOCLS_VOID_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_VOID_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_X" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_INL" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_D" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_TI_X" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_VIRT" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
new file mode 100644
index 00000000000..59b779de35d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+template <typename T = void>
+class
+__attribute__ ((__sym_alias__ ("FOOCLS")))
+foo // { dg-error "duplicate|already" }
+{
+  virtual ~foo() {}
+
+  template <typename U>
+  void
+    __attribute__ ((__sym_alias__ ("FOOTMF")))
+    tmemfun () {} // { dg-error "duplicate|already" }
+};
+
+template <typename T>
+void
+__attribute__ ((__sym_alias__ ("FOOTFN")))
+fn(T) { // { dg-error "duplicate|already" }
+};
+
+template class foo<>;
+template class foo<int>;
+template void foo<>::tmemfun<void>();
+template void foo<int>::tmemfun<void>();
+template void fn<>(int);
+template void fn<>(long);
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
new file mode 100644
index 00000000000..45d59b953c3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
@@ -0,0 +1,14 @@
+/* { dg-do compile { target c++11 } } */
+/* { dg-require-alias "" } */
+
+struct foo {
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+foo::foo () {}
+
+// Make sure the inherited cdtors don't duplicate the syms.
+struct bar : foo {
+  using foo::foo;
+};
diff --git a/gcc/varpool.cc b/gcc/varpool.cc
index e7b51b15e4a..33649906d1d 100644
--- a/gcc/varpool.cc
+++ b/gcc/varpool.cc
@@ -163,6 +163,9 @@ varpool_node::get_create (tree decl)
     }
 
   node->register_symbol ();
+
+  create_sym_alias_decls (decl);
+
   return node;
 }

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [gcc(refs/users/aoliva/heads/testme)] Introduce attribute sym_alias
@ 2023-11-30 17:26 Alexandre Oliva
  0 siblings, 0 replies; 33+ messages in thread
From: Alexandre Oliva @ 2023-11-30 17:26 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:c9ca3598cb0074ad1d7f2f23f214d7cbb1576cb0

commit c9ca3598cb0074ad1d7f2f23f214d7cbb1576cb0
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Fri Jul 14 03:45:14 2023 -0300

    Introduce attribute sym_alias
    
    This patch introduces an attribute to add extra asm names (aliases)
    for a decl 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 class types, namespace-scoped
    variables and functions, static data members, member functions,
    explicit instantiations and specializations of template functions,
    members and classes.
    
    When applied to constructors or destructor, additional sym aliases
    with _Base and _Del suffixes are defined for variants other than
    complete-object ones.  This changes the assumption that clones always
    carry the same attributes as their abstract declarations, so there is
    now a function to adjust them.
    
    C++ also had a bug in which attributes from local extern declarations
    failed to be propagated to a preexisting corresponding
    namespace-scoped decl.  I've fixed that, and adjusted acc tests that
    distinguished between C and C++ in this regard.
    
    Applying the attribute to class types is only valid in C++, and the
    effect is to attach the alias to the RTTI object associated with the
    class type.
    
    for  gcc/ChangeLog
    
            * attribs.cc: Include cgraph.h.
            (decl_attributes): Allow late introduction of sym_alias in
            types.
            (create_sym_alias_decl, create_sym_alias_decls): New.
            * attribs.h: Declare them.
            (FOR_EACH_SYM_ALIAS): New macro.
            * cgraph.cc (cgraph_node::create): Create sym_alias decls.
            * varpool.cc (varpool_node::get_create): Create sym_alias
            decls.
            * cgraph.h (symtab_node::remap_sym_alias_target): New.
            * symtab.cc (symtab_node::remap_sym_alias_target): Define.
            * cgraphunit.cc (cgraph_node::analyze): Create alias_target
            node if needed.
            (analyze_functions): Fixup visibility of implicit alias only
            after its node is analyzed.
            * doc/extend.texi (sym_alias): Document for variables,
            functions and types.
    
    for  gcc/ada/ChangeLog
    
            * doc/gnat_rm/interfacing_to_other_languages.rst: Mention
            attribute sym_alias to give RTTI symbols mnemonic names.
            * doc/gnat_ugn/the_gnat_compilation_model.rst: Mention
            aliases.  Fix incorrect ref to C1 ctor variant.
    
    for  gcc/c-family/ChangeLog
    
            * c-ada-spec.cc (pp_asm_name): Use first sym_alias if
            available.
            * c-attribs.cc (handle_sym_alias_attribute): New.
            (c_common_attribute_table): Add sym_alias.
            (handle_copy_attribute): Do not copy sym_alias attribute.
    
    for  gcc/c/ChangeLog
    
            * c-decl.cc (duplicate_decls): Remap sym_alias target.
    
    for  gcc/cp/ChangeLog
    
            * class.cc (adjust_clone_attributes): New.
            (copy_fndecl_with_name, build_clone): Call it.
            * cp-tree.h (adjust_clone_attributes): Declare.
            (update_sym_alias_interface): Declare.
            (update_tinfo_sym_alias): Declare.
            * decl.cc (duplicate_decls): Remap sym_alias target.
            Adjust clone attributes.
            (grokfndecl): Tentatively create sym_alias decls after
            adding attributes in e.g. a template member function explicit
            instantiation.
            * decl2.cc (cplus_decl_attributes): Update tinfo sym_alias.
            (copy_interface, update_sym_alias_interface): New.
            (determine_visibility): Update sym_alias interface.
            (tentative_decl_linkage, import_export_decl): Likewise.
            * name-lookup.cc: Include target.h and cgraph.h.
            (push_local_extern_decl_alias): Merge attributes with
            namespace-scoped decl, and drop duplicate sym_alias.
            * optimize.cc (maybe_clone_body): Re-adjust attributes after
            cloning them.  Update sym_alias interface.
            * rtti.cc: Include attribs.h and cgraph.h.
            (get_tinfo_decl): Copy sym_alias attributes from type to tinfo
            decl.  Create sym_alias decls.
            (update_tinfo_sym_alias): New.
    
    for  gcc/testsuite/ChangeLog
    
            * c-c++-common/goacc/declare-1.c: Adjust.
            * c-c++-common/goacc/declare-2.c: Adjust.
            * c-c++-common/torture/attr-sym-alias-1.c: New.
            * c-c++-common/torture/attr-sym-alias-2.c: New.
            * c-c++-common/torture/attr-sym-alias-3.c: New.
            * c-c++-common/torture/attr-sym-alias-4.c: New.
            * c-c++-common/torture/attr-sym-alias-5.c: New.
            * c-c++-common/attr-sym-alias-neg.c: New.
            * g++.dg/torture/attr-sym-alias-1.C: New.
            * g++.dg/torture/attr-sym-alias-2.C: New.
            * g++.dg/torture/attr-sym-alias-3.C: New.
            * g++.dg/torture/attr-sym-alias-4.C: New.
            * g++.dg/torture/attr-sym-alias-5.C: New.

Diff:
---
 .../doc/gnat_rm/interfacing_to_other_languages.rst |  6 ++
 .../doc/gnat_ugn/the_gnat_compilation_model.rst    | 10 ++-
 gcc/attribs.cc                                     | 66 ++++++++++++++++-
 gcc/attribs.h                                      |  7 ++
 gcc/c-family/c-ada-spec.cc                         |  7 ++
 gcc/c-family/c-attribs.cc                          | 33 ++++++++-
 gcc/c/c-decl.cc                                    |  2 +
 gcc/cgraph.cc                                      |  2 +
 gcc/cgraph.h                                       |  4 ++
 gcc/cgraphunit.cc                                  |  2 +-
 gcc/cp/class.cc                                    | 64 +++++++++++++++++
 gcc/cp/cp-tree.h                                   |  4 ++
 gcc/cp/decl.cc                                     |  4 ++
 gcc/cp/decl2.cc                                    | 50 +++++++++++++
 gcc/cp/name-lookup.cc                              | 11 +++
 gcc/cp/optimize.cc                                 |  3 +
 gcc/cp/rtti.cc                                     | 71 +++++++++++++++++-
 gcc/doc/extend.texi                                | 56 +++++++++++++++
 gcc/symtab.cc                                      | 38 ++++++++++
 gcc/testsuite/c-c++-common/attr-sym-alias-neg.c    | 80 +++++++++++++++++++++
 gcc/testsuite/c-c++-common/goacc/declare-1.c       |  6 +-
 gcc/testsuite/c-c++-common/goacc/declare-2.c       | 14 ++--
 .../c-c++-common/torture/attr-sym-alias-1.c        | 39 ++++++++++
 .../c-c++-common/torture/attr-sym-alias-2.c        | 13 ++++
 .../c-c++-common/torture/attr-sym-alias-3.c        | 41 +++++++++++
 .../c-c++-common/torture/attr-sym-alias-4.c        | 28 ++++++++
 .../c-c++-common/torture/attr-sym-alias-5.c        | 54 ++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C    | 72 +++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C    | 26 +++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C    | 83 ++++++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C    | 28 ++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C    | 14 ++++
 gcc/varpool.cc                                     |  3 +
 33 files changed, 922 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 ad0be511d48..b8de16ebf7c 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 ``sym_alias`` 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 fd15459203a..20045698962 100644
--- a/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
+++ b/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
@@ -4274,6 +4274,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__ ((__sym_alias__ ("Ctor_For_Animal")))
         Animal() {Age_Count = 0;};
       private:
         int Age_Count;
@@ -4306,6 +4307,7 @@ both Carnivore and Domestic, that is:
         virtual int  Number_Of_Teeth ();
         virtual void Set_Owner (char* Name);
 
+        __attribute__ ((__sym_alias__ ("Ctor_For_Dog"))) // mnemonic alias
         Dog(); // Constructor
       private:
         int  Tooth_Count;
@@ -4344,7 +4346,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;
@@ -4360,7 +4363,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,7 +4385,8 @@ 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.
+mangled name (or an alias) 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.cc b/gcc/attribs.cc
index c7209c26acc..c75ca6974cb 100644
--- a/gcc/attribs.cc
+++ b/gcc/attribs.cc
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "target.h"
 #include "tree.h"
+#include "cgraph.h"
 #include "stringpool.h"
 #include "diagnostic-core.h"
 #include "attribs.h"
@@ -825,7 +826,8 @@ decl_attributes (tree *node, tree attributes, int flags,
 
       if (TYPE_P (*anode)
 	  && (flags & (int) ATTR_FLAG_TYPE_IN_PLACE)
-	  && COMPLETE_TYPE_P (*anode))
+	  && COMPLETE_TYPE_P (*anode)
+	  && !is_attribute_p ("sym_alias", name))
 	{
 	  warning (OPT_Wattributes, "type attributes ignored after type is already defined");
 	  continue;
@@ -2640,6 +2642,68 @@ attr_access::array_as_string (tree type) const
   return typstr;
 }
 
+/* Create a sym attribute for DECL to be visible with linkage name ID.  */
+
+tree
+create_sym_alias_decl (tree decl, tree id)
+{
+  const char *attr_str = "sym_alias";
+
+  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;
+
+      tree attr_name = get_identifier (attr_str);
+      error_at (DECL_SOURCE_LOCATION (decl),
+		"duplicate symbol name %qE in %qE attribute of %qD",
+		id, attr_name, decl);
+      inform (DECL_SOURCE_LOCATION (sym_node->decl),
+	      "already used by %qD", sym_node->decl);
+    }
+
+  tree clone = copy_node (decl);
+  DECL_ATTRIBUTES (clone) = remove_attribute (attr_str,
+					      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_extra_name_alias (clone, decl);
+    varpool_node::create_alias (clone, decl);
+  else
+    cgraph_node::create_same_body_alias (clone, decl);
+    // cgraph_node::create_alias (clone, decl);
+
+  return clone;
+}
+
+/* Create decls for all sym aliases requested in DECL's attributes.  */
+
+void
+create_sym_alias_decls (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl)
+      || DECL_ABSTRACT_P (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      create_sym_alias_decl (decl, id);
+    }
+}
+
 #if CHECKING_P
 
 namespace selftest
diff --git a/gcc/attribs.h b/gcc/attribs.h
index 84a43658a70..156f2c20588 100644
--- a/gcc/attribs.h
+++ b/gcc/attribs.h
@@ -398,4 +398,11 @@ extern void init_attr_rdwr_indices (rdwr_map *, tree);
 extern attr_access *get_parm_access (rdwr_map &, tree,
 				     tree = current_function_decl);
 
+extern tree create_sym_alias_decl (tree, tree);
+extern void create_sym_alias_decls (tree);
+
+#define FOR_EACH_SYM_ALIAS(sym, attrs)					\
+  for (tree sym = lookup_attribute ("sym_alias", (attrs));		\
+       sym; sym = lookup_attribute ("sym_alias", TREE_CHAIN (sym)))
+
 #endif // GCC_ATTRIBS_H
diff --git a/gcc/c-family/c-ada-spec.cc b/gcc/c-family/c-ada-spec.cc
index 050994d8416..5042b9cfecd 100644
--- a/gcc/c-family/c-ada-spec.cc
+++ b/gcc/c-family/c-ada-spec.cc
@@ -1431,6 +1431,13 @@ pp_ada_tree_identifier (pretty_printer *buffer, tree node, tree type,
 static void
 pp_asm_name (pretty_printer *buffer, tree t)
 {
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (t))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.cc b/gcc/c-family/c-attribs.cc
index 2b20e58c922..392437eab3b 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -108,7 +108,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_sym_alias_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,
@@ -388,6 +389,8 @@ const struct attribute_spec c_common_attribute_table[] =
 			      handle_alias_attribute, NULL },
   { "weakref",                0, 1, true,  false, false, false,
 			      handle_weakref_attribute, NULL },
+  { "sym_alias",              1, 1, false,  false, false, false,
+			      handle_sym_alias_attribute, NULL },
   { "no_instrument_function", 0, 0, true,  false, false, false,
 			      handle_no_instrument_function_attribute,
 			      NULL },
@@ -3002,7 +3005,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
@@ -3012,7 +3015,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
@@ -3022,6 +3025,29 @@ handle_alias_attribute (tree *node, tree name, tree args,
   return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs);
 }
 
+/* Handle a "sym_alias" attribute; arguments as in struct
+   attribute_spec.handler.  */
+
+static tree
+handle_sym_alias_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 (decl_in_symtab_p (node))
+    *no_add_attrs = false;
+  else if (TYPE_P (node) && c_dialect_cxx ())
+    *no_add_attrs = false;
+  else
+    return error_mark_node;
+
+  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.  */
 
@@ -3155,6 +3181,7 @@ handle_copy_attribute (tree *node, tree name, tree args,
 	      || is_attribute_p ("visibility", atname)
 	      || is_attribute_p ("weak", atname)
 	      || is_attribute_p ("weakref", atname)
+	      || is_attribute_p ("sym_alias", atname)
 	      || is_attribute_p ("target_clones", atname))
 	    continue;
 
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index cf1df82c0f4..e5a1ee54cb3 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -3119,6 +3119,8 @@ duplicate_decls (tree newdecl, tree olddecl)
 
   merge_decls (newdecl, olddecl, newtype, oldtype);
 
+  symtab_node::remap_sym_alias_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.cc b/gcc/cgraph.cc
index f93259a8c70..0bef2179e89 100644
--- a/gcc/cgraph.cc
+++ b/gcc/cgraph.cc
@@ -523,6 +523,8 @@ cgraph_node::create (tree decl)
   node->register_symbol ();
   maybe_record_nested_function (node);
 
+  create_sym_alias_decls (decl);
+
   return node;
 }
 
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index cfdd9f693a8..cb5ac01f7e2 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -327,6 +327,10 @@ public:
   /* Return DECL that alias is aliasing.  */
   inline tree get_alias_target_tree ();
 
+  /* Remap sym alias nodes recorded as aliasing REPLACED to alias REPLACEMENT
+     instead.  */
+  static void remap_sym_alias_target (tree replaced, tree replacement);
+
   /* Set section for symbol and its aliases.  */
   void set_section (const char *section);
 
diff --git a/gcc/cgraphunit.cc b/gcc/cgraphunit.cc
index bccd2f2abb5..eb2d05094e9 100644
--- a/gcc/cgraphunit.cc
+++ b/gcc/cgraphunit.cc
@@ -1175,7 +1175,7 @@ analyze_functions (bool first_time)
      C++ FE is confused about the COMDAT groups being right.  */
   if (symtab->cpp_implicit_aliases_done)
     FOR_EACH_SYMBOL (node)
-      if (node->cpp_implicit_alias)
+      if (node->cpp_implicit_alias && node->analyzed)
 	  node->fixup_same_cpp_alias_visibility (node->get_alias_target ());
   build_type_inheritance_graph ();
 
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index 6fdb56abfb9..fa8fc63db82 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -4896,6 +4896,68 @@ check_methods (tree t)
     }
 }
 
+/* Adjust sym alias name for CLONE, cloned from FN and named NAME,
+   if it is a cdtor, and drop the sym alias from other clones.  */
+
+void
+adjust_clone_attributes (tree fn, tree clone, tree name, bool skip_copy_p)
+{
+  if (IDENTIFIER_CDTOR_P (name))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	{
+	  found = true;
+	  break;
+	}
+
+      if (found
+	  && (name == complete_ctor_identifier
+	      || name == complete_dtor_identifier))
+	{
+	  /* Reuse the sym alias decls created for the primary cdtor
+	     decl.  */
+	  symtab_node::remap_sym_alias_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);
+
+	  if (!skip_copy_p)
+	    DECL_ATTRIBUTES (clone) = copy_list (DECL_ATTRIBUTES (clone));
+
+	  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	    {
+	      /* We need to copy this even with skip_copy_p, because
+		 even then copying was shallow.  */
+	      TREE_VALUE (sym) = copy_list (TREE_VALUE (sym));
+	      /* Append suf to the sym alias name.  */
+	      tree str = TREE_VALUE (TREE_VALUE (sym));
+	      char *symname = concat (TREE_STRING_POINTER (str), suf, NULL);
+	      str = build_string (TREE_STRING_LENGTH (str) + xlen, symname);
+	      TREE_VALUE (TREE_VALUE (sym)) = str;
+	      free (symname);
+	    }
+
+	  if (symtab_node::get (clone))
+	    create_sym_alias_decls (clone);
+	}
+    }
+  else
+    DECL_ATTRIBUTES (clone)
+      = remove_attribute ("sym_alias", DECL_ATTRIBUTES (clone));
+}
+
 /* FN is constructor, destructor or operator function.  Clone the
    declaration to create a NAME'd variant.  NEED_VTT_PARM_P and
    OMIT_INHERITED_PARMS_P are relevant if it's a cdtor.  */
@@ -5065,6 +5127,8 @@ build_clone (tree fn, tree name, bool need_vtt_parm_p,
   DECL_CHAIN (clone) = DECL_CHAIN (fn);
   DECL_CHAIN (fn) = clone;
 
+  adjust_clone_attributes (fn, clone, name);
+
   return clone;
 }
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 964af1ddd85..2bafed4e993 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5787,6 +5787,8 @@ struct GTY((for_user)) spec_entry
 
 extern int current_class_depth;
 
+void adjust_clone_attributes (tree fn, tree clone, tree name, bool = false);
+
 /* in decl.cc */
 
 /* An array of static vars & fns.  */
@@ -7022,6 +7024,7 @@ extern void do_push_parm_decls			(tree, tree, tree *);
 extern tree do_aggregate_paren_init		(tree, tree);
 
 /* in decl2.cc */
+extern void update_sym_alias_interface		(tree);
 extern void record_mangling			(tree, bool);
 extern void overwrite_mangling			(tree, tree);
 extern void note_mangling_alias			(tree, tree);
@@ -7596,6 +7599,7 @@ extern bool emit_tinfo_decl			(tree);
 extern unsigned get_pseudo_tinfo_index		(tree);
 extern tree get_pseudo_tinfo_type		(unsigned);
 extern tree build_if_nonnull			(tree, tree, tsubst_flags_t);
+extern void update_tinfo_sym_alias		(tree);
 
 /* in search.cc */
 extern tree get_parent_with_private_access 	(tree decl, tree binfo);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 2f2dbb8d107..5636d694b68 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -3237,6 +3237,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
 	      && TREE_STATIC (olddecl))))
     make_decl_rtl (olddecl);
 
+  symtab_node::remap_sym_alias_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.
@@ -3260,6 +3262,7 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
       FOR_EACH_CLONE (clone, olddecl)
 	{
 	  DECL_ATTRIBUTES (clone) = DECL_ATTRIBUTES (olddecl);
+	  adjust_clone_attributes (olddecl, clone, DECL_NAME (clone));
 	  DECL_PRESERVE_P (clone) |= DECL_PRESERVE_P (olddecl);
 	}
     }
@@ -10789,6 +10792,7 @@ grokfndecl (tree ctype,
     {
       cplus_decl_attributes (&decl, *attrlist, 0);
       *attrlist = NULL_TREE;
+      create_sym_alias_decls (decl);
     }
 
   if (DECL_HAS_CONTRACTS_P (decl))
diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
index 9e666e5eece..c7f009beaf0 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -1844,6 +1844,9 @@ cplus_decl_attributes (tree *decl, tree attributes, int flags)
   if (late_attrs)
     save_template_attributes (late_attrs, decl, flags);
 
+  if (TYPE_P (*decl) && attributes)
+    update_tinfo_sym_alias (*decl);
+
   /* Propagate deprecation out to the template.  */
   if (TREE_DEPRECATED (*decl))
     if (tree ti = get_template_info (*decl))
@@ -2200,6 +2203,47 @@ 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 sym aliases.  */
+
+void
+update_sym_alias_interface (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.  */
 
@@ -3007,6 +3051,8 @@ determine_visibility (tree decl)
        translation unit, we can make the type internal.  */
     constrain_visibility (decl, VISIBILITY_ANON, false);
 
+  update_sym_alias_interface (decl);
+
   /* If visibility changed and DECL already has DECL_RTL, ensure
      symbol flags are updated.  */
   if ((DECL_VISIBILITY (decl) != orig_visibility
@@ -3269,6 +3315,8 @@ tentative_decl_linkage (tree decl)
       else if (VAR_P (decl))
 	maybe_commonize_var (decl);
     }
+
+  update_sym_alias_interface (decl);
 }
 
 /* DECL is a FUNCTION_DECL or VAR_DECL.  If the object file linkage
@@ -3503,6 +3551,8 @@ import_export_decl (tree decl)
     }
 
   DECL_INTERFACE_KNOWN (decl) = 1;
+
+  update_sym_alias_interface (decl);
 }
 
 /* Return an expression that performs the destruction of DECL, which
diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc
index d19ea5d121c..f877780b2ac 100644
--- a/gcc/cp/name-lookup.cc
+++ b/gcc/cp/name-lookup.cc
@@ -22,9 +22,11 @@ along with GCC; see the file COPYING3.  If not see
 #define INCLUDE_MEMORY
 #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"
@@ -3479,6 +3481,15 @@ push_local_extern_decl_alias (tree decl)
 	  /* Adjust visibility.  */
 	  determine_visibility (alias);
 	}
+      else if (DECL_P (alias))
+	DECL_ATTRIBUTES (alias)
+	  = targetm.merge_decl_attributes (alias, decl);
+      if (DECL_P (alias))
+	{
+	  symtab_node::remap_sym_alias_target (decl, alias);
+	  DECL_ATTRIBUTES (decl)
+	    = remove_attribute ("sym_alias", DECL_ATTRIBUTES (alias));
+	}
     }
 
   retrofit_lang_decl (decl);
diff --git a/gcc/cp/optimize.cc b/gcc/cp/optimize.cc
index 9e8926e4cc6..4a2e8cb4354 100644
--- a/gcc/cp/optimize.cc
+++ b/gcc/cp/optimize.cc
@@ -528,9 +528,12 @@ maybe_clone_body (tree fn)
       DECL_VISIBILITY_SPECIFIED (clone) = DECL_VISIBILITY_SPECIFIED (fn);
       DECL_DLLIMPORT_P (clone) = DECL_DLLIMPORT_P (fn);
       DECL_ATTRIBUTES (clone) = clone_attrs (DECL_ATTRIBUTES (fn));
+      adjust_clone_attributes (fn, clone, DECL_NAME (clone), true);
       DECL_DISREGARD_INLINE_LIMITS (clone) = DECL_DISREGARD_INLINE_LIMITS (fn);
       set_decl_section_name (clone, fn);
 
+      update_sym_alias_interface (clone);
+
       /* Adjust the parameter names and locations.  */
       parm = DECL_ARGUMENTS (fn);
       clone_parm = DECL_ARGUMENTS (clone);
diff --git a/gcc/cp/rtti.cc b/gcc/cp/rtti.cc
index 7878929c246..4bb7b8c8c6e 100644
--- a/gcc/cp/rtti.cc
+++ b/gcc/cp/rtti.cc
@@ -28,8 +28,10 @@ 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"
+#include "cgraph.h"
 
 /* C++ returns type information to the user in struct type_info
    objects. We also use type information to implement dynamic_cast and
@@ -479,8 +481,13 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
 	  = build_tree_list (get_identifier ("non overlapping"),
 			     NULL_TREE);
       else
+	/* Share the non overlapping attribute, without assuming it's
+	   the only attribute, but assuming it's the last if it's
+	   present.  There may be sym aliases too, and those are not
+	   to be shared.  */
 	DECL_ATTRIBUTES (d)
-	  = DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]);
+	  = lookup_attribute ("non overlapping",
+			      DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]));
 
       /* Mark the variable as undefined -- but remember that we can
 	 define it later if we need to do so.  */
@@ -492,6 +499,16 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
       if (CLASS_TYPE_P (type))
 	CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type)) = d;
 
+      /* Copy sym alias attributes from the type to the rtti obj decl.  */
+      tree *attrs = &DECL_ATTRIBUTES (d);
+      FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+	{
+	  tree attr = tree_cons (TREE_PURPOSE (sym), TREE_VALUE (sym), *attrs);
+	  *attrs = attr;
+	  attrs = &TREE_CHAIN (attr);
+	}
+      create_sym_alias_decls (d);
+
       /* Add decl to the global array of tinfo decls.  */
       vec_safe_push (unemitted_tinfo_decls, d);
     }
@@ -499,6 +516,58 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
   return d;
 }
 
+/* After modifying the attributes of TYPE, check whether tinfo was
+   already created and, if so, add to it any sym alias attributes
+   that were not already present.  */
+
+void
+update_tinfo_sym_alias (tree type)
+{
+  if (!TYPE_SIZE (type) || !CLASS_TYPE_P (type))
+    return;
+
+  tree d = CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type));
+  if (!d)
+    return;
+
+  bool first = true;
+  symtab_node *node = NULL;
+
+  tree *attrs = &DECL_ATTRIBUTES (d);
+  FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (d_sym, *attrs)
+	if (TREE_VALUE (sym) == TREE_VALUE (d_sym))
+	  {
+	    found = true;
+	    break;
+	  }
+
+      if (found)
+	continue;
+
+      tree attr = tree_cons (TREE_PURPOSE (sym),
+			     TREE_VALUE (sym),
+			     *attrs);
+      *attrs = attr;
+      attrs = &TREE_CHAIN (attr);
+
+      if (first)
+	{
+	  first = false;
+	  node = symtab_node::get (d);
+	}
+
+      if (!node)
+	continue;
+
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+      create_sym_alias_decl (d, id);
+    }
+}
+
 /* Return the type_info object for TYPE.  */
 
 tree
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index e6de0815846..5dcdec76c1e 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -4141,6 +4141,43 @@ Function Attributes}, @ref{PowerPC Function Attributes},
 @ref{Nios II Function Attributes}, and @ref{S/390 Function Attributes}
 for details.
 
+@cindex @code{sym_alias} function attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} attribute causes @var{name} to be output as an
+alias to the function, with the same linkage and visibility as the
+function, when the function definition is output, provided that the
+function could be an alias target.  For instance,
+
+@smallexample
+void f (uint64_t) __attribute__ ((__sym_alias__ ("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},
+with the same linkage and visibility.  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.
+
+Aliases introduced with this attribute, such as @samp{f_u64} in the
+example above, are assembly symbol names: they do not undergo C++ name
+mangling, and are not made visible in any scope in the source language.
+They can, however, can be named as alias targets.
+
+This attribute requires assembler and object file support for aliases,
+and may not be available on all targets.
+
 @cindex @code{symver} function attribute
 @item symver ("@var{name2}@@@var{nodename}")
 On ELF targets this attribute creates a symbol version.  The @var{name2} part
@@ -8069,6 +8106,10 @@ will be placed in new, unique sections.
 
 This additional functionality requires Binutils version 2.36 or later.
 
+@cindex @code{sym_alias} variable attribute
+@item sym_alias ("@var{name}")
+See @pxref{Common Function Attributes}.
+
 @cindex @code{uninitialized} variable attribute
 @item uninitialized
 This attribute, attached to a variable with automatic storage, means that
@@ -9164,6 +9205,21 @@ is not supported; that is to say, if a given scalar object can be accessed
 through distinct types that assign a different storage order to it, then the
 behavior is undefined.
 
+@cindex @code{sym_alias} type attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} type 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__ ((__sym_alias__ ("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.
+
 @cindex @code{transparent_union} type attribute
 @item transparent_union
 
diff --git a/gcc/symtab.cc b/gcc/symtab.cc
index 0470509a98d..43ba88d7939 100644
--- a/gcc/symtab.cc
+++ b/gcc/symtab.cc
@@ -1943,6 +1943,44 @@ symtab_node::noninterposable_alias (symtab_node *node, void *data)
   return false;
 }
 
+/* Remap sym alias nodes recorded as aliasing REPLACED to alias
+   REPLACEMENT instead.  */
+
+void
+symtab_node::remap_sym_alias_target (tree replaced, tree replacement)
+{
+  if (!decl_in_symtab_p (replacement)
+      || !symtab_node::get (replacement))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (replaced))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      symtab_node *sym_node = symtab_node::get_for_asmname (id);
+
+      if (!sym_node)
+	{
+	  create_sym_alias_decl (replacement, id);
+	  continue;
+	}
+
+      gcc_assert (!sym_node->analyzed);
+      if (sym_node->alias_target != replaced)
+	continue;
+
+      // sym_node->definition = 0;
+
+      if (VAR_P (replaced))
+	// varpool_node::create_extra_name_alias (sym_node->decl, replacement);
+	varpool_node::create_alias (sym_node->decl, replacement);
+      else
+	cgraph_node::create_same_body_alias (sym_node->decl, replacement);
+	// 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-sym-alias-neg.c b/gcc/testsuite/c-c++-common/attr-sym-alias-neg.c
new file mode 100644
index 00000000000..f64e5bb1ac4
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/attr-sym-alias-neg.c
@@ -0,0 +1,80 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int f () {
+  int i __attribute__ ((sym_alias ("f_i"))); /* { dg-warning "ignored" } */
+  /* ??? X cannot be an alias target; should this be flagged? ... */
+  static int x __attribute__ ((sym_alias ("f_x")));
+  static int sx __attribute__ ((alias ("f_x"))); /* { dg-warning "ignored" } */
+  extern int xx __attribute__ ((alias ("f_x"))); /* { dg-warning "ignored" } */
+  return i;
+}
+
+/* ??? ... or should XR be accepted?  */
+extern int xr __attribute__ ((alias ("f_x"))); /* { dg-error "undefined" "" { xfail c++ } } */
+int dr __attribute__ ((alias ("f_x"))); /* { dg-error "defined both" } */
+
+
+struct s {
+  int j __attribute__ ((sym_alias ("s_j"))); /* { dg-warning "ignored" } */
+};
+
+
+int j __attribute__ ((sym_alias ("J_var")));
+void __attribute__ ((alias ("J_var")))
+j_fn (); /* { dg-error "between function and variable" } */
+
+
+void __attribute__ ((sym_alias ("K_fn")))
+k () {
+}
+extern int __attribute__ ((alias ("K_fn")))
+k_var; /* { dg-error "between function and variable" } */
+
+
+int l __attribute__ ((sym_alias ("L_fn")));
+
+/* These should be detected and reported, not because the names clash at the
+   source level, but because the asm symbols do.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+void
+L_fn () /* { dg-error "duplicate" "" { xfail *-*-* } } */
+{
+}
+
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __attribute__ ((sym_alias ("M_var")))
+m ()
+{
+}
+
+int M_var; /* { dg-error "duplicate" "" { xfail *-*-* } } */
+
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __attribute__ ((sym_alias ("N_sym")))
+n_fn ()
+{
+}
+
+int __attribute__ ((sym_alias ("N_sym")))
+n_var; /* { dg-error "duplicate" } */
+
+
+int __attribute__ ((sym_alias ("O_sym")))
+o_var;
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __attribute__ ((sym_alias ("O_sym")))
+o_fn () /* { dg-error "duplicate" } */
+{
+}
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-1.c b/gcc/testsuite/c-c++-common/goacc/declare-1.c
index 46ee01b6759..f2842893318 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-1.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-1.c
@@ -113,11 +113,11 @@ f_2 (void)
   int va3;
 #pragma acc declare device_resident(va3)
 
-#ifndef __cplusplus
+#if 0
   /* TODO PR90868
 
-     C: "error: variable '[...]' used more than once with '#pragma acc declare'".  */
-#else
+     "error: variable '[...]' used more than once with '#pragma acc declare'".  */
+
   extern int ve0;
 #pragma acc declare create(ve0)
 
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-2.c b/gcc/testsuite/c-c++-common/goacc/declare-2.c
index e2e22be57e9..aec59b69754 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-2.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-2.c
@@ -137,25 +137,25 @@ void
 f_pr90868_2 (void)
 {
   extern int we0;
-#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" } */
 
   extern int we1;
-#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" } */
 
   extern int *we2;
-#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" } */
 
   extern int we3;
-#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" } */
 
   extern int we4;
-#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" } */
 
   extern int we5;
-#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" } */
  
   extern int we6;
-#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" } */
 }
 
 
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
new file mode 100644
index 00000000000..61af50cb552
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
@@ -0,0 +1,39 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+int var_a = 1;
+
+void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+
+void
+foo_a ()
+{
+}
+
+
+int var_b;
+extern int var_b __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+
+void
+foo_b ()
+{
+}
+
+void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+
+
+int var_c __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+
+void __attribute__ ((__sym_alias__ ("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-sym-alias-2.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
new file mode 100644
index 00000000000..6f0d55ca42c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+struct s
+{
+  int mem __attribute__ ((__sym_alias__ ("MEMFOO"))); /* { dg-warning "attribute ignored" } */
+};
+
+void foo()
+{
+  extern void bar () __attribute__ ((__sym_alias__ ("FOOBAR")));
+  int var __attribute__ ((__sym_alias__ ("FOOVAR"))); /* { dg-warning "attribute ignored" } */
+}
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
new file mode 100644
index 00000000000..0052485a30a
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
@@ -0,0 +1,41 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int var_a = 1;
+
+void
+foo_a ()
+{
+  extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+}
+
+#if 0 // __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__ ((__sym_alias__ ("FOOVAR_B")));
+}
+
+int var_b;
+
+void __attribute__ ((__sym_alias__ ("FOOBAR_C")))
+foo_c ()
+{
+  void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+  /* Another sym for var_b.  */
+  extern int var_b __attribute__ ((__sym_alias__ ("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-sym-alias-4.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
new file mode 100644
index 00000000000..2beafa46379
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-require-alias "" } */
+
+int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A"))) = 42;
+
+int __attribute__ ((__sym_alias__ ("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/c-c++-common/torture/attr-sym-alias-5.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
new file mode 100644
index 00000000000..bee90136daf
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
@@ -0,0 +1,54 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+/* { dg-require-visibility "" } */
+
+int __attribute__ ((sym_alias ("A_hid"), visibility ("hidden"))) a;
+
+extern int __attribute__ ((sym_alias ("B_prt"))) b;
+
+int b __attribute__ ((visibility ("protected")));
+
+extern int __attribute__ ((sym_alias ("C_ntr"))) c;
+
+extern int c __attribute__ ((visibility ("internal")));
+
+int c;
+
+static int d __attribute__ ((sym_alias ("D_stt")));
+
+
+extern void __attribute__ ((sym_alias ("F_hid"), visibility ("hidden")))
+f ();
+
+void
+f () {
+}
+
+extern void __attribute__ ((sym_alias ("G_prt")))
+g ();
+
+void __attribute__ ((visibility ("protected")))
+g () {
+}
+
+extern void __attribute__ ((sym_alias ("H_ntr")))
+h ();
+
+void __attribute__ ((visibility ("internal")))
+h ();
+
+void h () {
+}
+
+static void __attribute__ ((sym_alias ("I_stt")))
+i () {
+}
+
+/* { dg-final { scan-assembler {hidden[^\n]*A_hid} } } */
+/* { dg-final { scan-assembler {protected[^\n]*B_prt} } } */
+/* { dg-final { scan-assembler {internal[^\n]*C_ntr} } } */
+/* { dg-final { scan-assembler-not {glob[^\n]*D_stt} } } */
+/* { dg-final { scan-assembler {hidden[^\n]*F_hid} } } */
+/* { dg-final { scan-assembler {protected[^\n]*G_prt} } } */
+/* { dg-final { scan-assembler {internal[^\n]*H_ntr} } } */
+/* { dg-final { scan-assembler-not {glob[^\n]*I_stt} } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
new file mode 100644
index 00000000000..579eda1a473
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
@@ -0,0 +1,72 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+class __attribute__ ((__sym_alias__ ("FOOCLS_A"),
+		      __sym_alias__ ("FOOCLS_A_Dupe"))) foo {
+  static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  void __attribute__ ((__sym_alias__ ("FOOBAR_A"))) bar ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+int foo::var = 1;
+
+foo::foo () {}
+
+void foo::bar () {}
+
+namespace b {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_B"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_B"))) foo ();
+    void __attribute__ ((__sym_alias__ ("FOOBAR_B"))) bar () {}
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_B"))) ~foo() {}
+  };
+
+  int foo::var = 2;
+
+  foo::foo () {
+    void (foo::*pbar)() = &foo::bar;
+  }
+}
+
+namespace c {
+  namespace cd {
+    class __attribute__ ((__sym_alias__ ("FOOCLS_C"))) foo {
+      static int var __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+      __attribute__ ((__sym_alias__ ("FOOCTR_C"))) foo () {
+	void (foo::*pbar)() = &foo::bar;
+      }
+      void __attribute__ ((__sym_alias__ ("FOOBAR_C"))) bar () {}
+      virtual __attribute__ ((__sym_alias__ ("FOODTR_C"))) ~foo() {}
+    };
+
+    int foo::var = 3;
+  }
+}
+
+/* { dg-final { scan-assembler "FOOCLS_A" } } */
+/* { dg-final { scan-assembler "FOOCLS_A_Dupe" } } */
+/* { 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-sym-alias-2.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
new file mode 100644
index 00000000000..6facfcc2cd9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+namespace {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_A"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo ();
+    void __attribute__ ((__sym_alias__ ("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-sym-alias-3.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
new file mode 100644
index 00000000000..42c7288ad4a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
@@ -0,0 +1,83 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+// sym can be applied to template function explicit instantiations.
+
+template <typename T>
+void
+fn(T) {
+};
+
+template void __attribute__ ((__sym_alias__ ("FOOFUN_UINT"))) fn<>(unsigned int);
+template void __attribute__ ((__sym_alias__ ("FOOFUN_LONG"))) fn<>(long);
+
+template<> void __attribute__ ((__sym_alias__ ("FOOFUN_CHAR"))) fn<>(char) {}
+
+
+template <typename T = void>
+struct
+foo {
+  virtual ~foo() {}
+
+  virtual void virtfun() {}
+
+  static void stfun() {}
+  void inlfun() {}
+};
+
+// Explicitly instantiate members before the enclosing class.
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_CHAR_VIRT"))) foo<char>::virtfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_CHAR_TI"))) foo<char>;
+
+// Though they're only output if the enclosing class is.
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_LONG_VIRT"))) foo<long>::virtfun();
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_LONG_TI_X"))) foo<long>;
+
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_VOID_ST"))) foo<void>::stfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_VOID_TI"))) foo<>;
+
+
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_X"))) foo<short>;
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_ST"))) foo<short>::stfun();
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_INL"))) foo<short>::inlfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_D"))) foo<short>;
+
+// Explicit specializations work too.
+
+template <>
+struct  __attribute__ ((__sym_alias__ ("FOOCLS_INT_TI")))
+foo<int>
+{
+  virtual ~foo() {}
+  virtual void __attribute__ ((__sym_alias__ ("FOOCLS_INT_VIRT"))) virtfun() {}
+};
+
+/* { dg-final { scan-assembler "FOOFUN_UINT" } } */
+/* { dg-final { scan-assembler "FOOFUN_LONG" } } */
+/* { dg-final { scan-assembler "FOOFUN_CHAR" } } */
+
+/* { dg-final { scan-assembler "FOOCLS_VOID_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_VOID_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_X" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_INL" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_D" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_TI_X" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_VIRT" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
new file mode 100644
index 00000000000..59b779de35d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+template <typename T = void>
+class
+__attribute__ ((__sym_alias__ ("FOOCLS")))
+foo // { dg-error "duplicate|already" }
+{
+  virtual ~foo() {}
+
+  template <typename U>
+  void
+    __attribute__ ((__sym_alias__ ("FOOTMF")))
+    tmemfun () {} // { dg-error "duplicate|already" }
+};
+
+template <typename T>
+void
+__attribute__ ((__sym_alias__ ("FOOTFN")))
+fn(T) { // { dg-error "duplicate|already" }
+};
+
+template class foo<>;
+template class foo<int>;
+template void foo<>::tmemfun<void>();
+template void foo<int>::tmemfun<void>();
+template void fn<>(int);
+template void fn<>(long);
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
new file mode 100644
index 00000000000..45d59b953c3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
@@ -0,0 +1,14 @@
+/* { dg-do compile { target c++11 } } */
+/* { dg-require-alias "" } */
+
+struct foo {
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+foo::foo () {}
+
+// Make sure the inherited cdtors don't duplicate the syms.
+struct bar : foo {
+  using foo::foo;
+};
diff --git a/gcc/varpool.cc b/gcc/varpool.cc
index e7b51b15e4a..33649906d1d 100644
--- a/gcc/varpool.cc
+++ b/gcc/varpool.cc
@@ -163,6 +163,9 @@ varpool_node::get_create (tree decl)
     }
 
   node->register_symbol ();
+
+  create_sym_alias_decls (decl);
+
   return node;
 }

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [gcc(refs/users/aoliva/heads/testme)] Introduce attribute sym_alias
@ 2023-11-30 16:49 Alexandre Oliva
  0 siblings, 0 replies; 33+ messages in thread
From: Alexandre Oliva @ 2023-11-30 16:49 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:16dbaee56ca2cb72c9e52540740319f988a4c7cc

commit 16dbaee56ca2cb72c9e52540740319f988a4c7cc
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Fri Jul 14 03:45:14 2023 -0300

    Introduce attribute sym_alias
    
    This patch introduces an attribute to add extra asm names (aliases)
    for a decl 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 class types, namespace-scoped
    variables and functions, static data members, member functions,
    explicit instantiations and specializations of template functions,
    members and classes.
    
    When applied to constructors or destructor, additional sym aliases
    with _Base and _Del suffixes are defined for variants other than
    complete-object ones.  This changes the assumption that clones always
    carry the same attributes as their abstract declarations, so there is
    now a function to adjust them.
    
    C++ also had a bug in which attributes from local extern declarations
    failed to be propagated to a preexisting corresponding
    namespace-scoped decl.  I've fixed that, and adjusted acc tests that
    distinguished between C and C++ in this regard.
    
    Applying the attribute to class types is only valid in C++, and the
    effect is to attach the alias to the RTTI object associated with the
    class type.
    
    for  gcc/ChangeLog
    
            * attribs.cc: Include cgraph.h.
            (decl_attributes): Allow late introduction of sym_alias in
            types.
            (create_sym_alias_decl, create_sym_alias_decls): New.
            * attribs.h: Declare them.
            (FOR_EACH_SYM_ALIAS): New macro.
            * cgraph.cc (cgraph_node::create): Create sym_alias decls.
            * varpool.cc (varpool_node::get_create): Create sym_alias
            decls.
            * cgraph.h (symtab_node::remap_sym_alias_target): New.
            * symtab.cc (symtab_node::remap_sym_alias_target): Define.
            * cgraphunit.cc (cgraph_node::analyze): Create alias_target
            node if needed.
            (analyze_functions): Fixup visibility of implicit alias only
            after its node is analyzed.
            * doc/extend.texi (sym_alias): Document for variables,
            functions and types.
    
    for  gcc/ada/ChangeLog
    
            * doc/gnat_rm/interfacing_to_other_languages.rst: Mention
            attribute sym_alias to give RTTI symbols mnemonic names.
            * doc/gnat_ugn/the_gnat_compilation_model.rst: Mention
            aliases.  Fix incorrect ref to C1 ctor variant.
    
    for  gcc/c-family/ChangeLog
    
            * c-ada-spec.cc (pp_asm_name): Use first sym_alias if
            available.
            * c-attribs.cc (handle_sym_alias_attribute): New.
            (c_common_attribute_table): Add sym_alias.
            (handle_copy_attribute): Do not copy sym_alias attribute.
    
    for  gcc/c/ChangeLog
    
            * c-decl.cc (duplicate_decls): Remap sym_alias target.
    
    for  gcc/cp/ChangeLog
    
            * class.cc (adjust_clone_attributes): New.
            (copy_fndecl_with_name, build_clone): Call it.
            * cp-tree.h (adjust_clone_attributes): Declare.
            (update_sym_alias_interface): Declare.
            (update_tinfo_sym_alias): Declare.
            * decl.cc (duplicate_decls): Remap sym_alias target.
            Adjust clone attributes.
            (grokfndecl): Tentatively create sym_alias decls after
            adding attributes in e.g. a template member function explicit
            instantiation.
            * decl2.cc (cplus_decl_attributes): Update tinfo sym_alias.
            (copy_interface, update_sym_alias_interface): New.
            (determine_visibility): Update sym_alias interface.
            (tentative_decl_linkage, import_export_decl): Likewise.
            * name-lookup.cc: Include target.h and cgraph.h.
            (push_local_extern_decl_alias): Merge attributes with
            namespace-scoped decl, and drop duplicate sym_alias.
            * optimize.cc (maybe_clone_body): Re-adjust attributes after
            cloning them.  Update sym_alias interface.
            * rtti.cc: Include attribs.h and cgraph.h.
            (get_tinfo_decl): Copy sym_alias attributes from type to tinfo
            decl.  Create sym_alias decls.
            (update_tinfo_sym_alias): New.
    
    for  gcc/testsuite/ChangeLog
    
            * c-c++-common/goacc/declare-1.c: Adjust.
            * c-c++-common/goacc/declare-2.c: Adjust.
            * c-c++-common/torture/attr-sym-alias-1.c: New.
            * c-c++-common/torture/attr-sym-alias-2.c: New.
            * c-c++-common/torture/attr-sym-alias-3.c: New.
            * c-c++-common/torture/attr-sym-alias-4.c: New.
            * c-c++-common/torture/attr-sym-alias-5.c: New.
            * c-c++-common/attr-sym-alias-neg.c: New.
            * g++.dg/torture/attr-sym-alias-1.C: New.
            * g++.dg/torture/attr-sym-alias-2.C: New.
            * g++.dg/torture/attr-sym-alias-3.C: New.
            * g++.dg/torture/attr-sym-alias-4.C: New.
            * g++.dg/torture/attr-sym-alias-5.C: New.

Diff:
---
 .../doc/gnat_rm/interfacing_to_other_languages.rst |  6 ++
 .../doc/gnat_ugn/the_gnat_compilation_model.rst    | 10 ++-
 gcc/attribs.cc                                     | 66 ++++++++++++++++-
 gcc/attribs.h                                      |  7 ++
 gcc/c-family/c-ada-spec.cc                         |  7 ++
 gcc/c-family/c-attribs.cc                          | 33 ++++++++-
 gcc/c/c-decl.cc                                    |  2 +
 gcc/cgraph.cc                                      |  2 +
 gcc/cgraph.h                                       |  4 ++
 gcc/cgraphunit.cc                                  |  2 +-
 gcc/cp/class.cc                                    | 64 +++++++++++++++++
 gcc/cp/cp-tree.h                                   |  4 ++
 gcc/cp/decl.cc                                     |  4 ++
 gcc/cp/decl2.cc                                    | 50 +++++++++++++
 gcc/cp/name-lookup.cc                              | 11 +++
 gcc/cp/optimize.cc                                 |  3 +
 gcc/cp/rtti.cc                                     | 71 +++++++++++++++++-
 gcc/doc/extend.texi                                | 56 +++++++++++++++
 gcc/symtab.cc                                      | 38 ++++++++++
 gcc/testsuite/c-c++-common/attr-sym-alias-neg.c    | 80 +++++++++++++++++++++
 gcc/testsuite/c-c++-common/goacc/declare-1.c       |  6 +-
 gcc/testsuite/c-c++-common/goacc/declare-2.c       | 14 ++--
 .../c-c++-common/torture/attr-sym-alias-1.c        | 39 ++++++++++
 .../c-c++-common/torture/attr-sym-alias-2.c        | 13 ++++
 .../c-c++-common/torture/attr-sym-alias-3.c        | 41 +++++++++++
 .../c-c++-common/torture/attr-sym-alias-4.c        | 28 ++++++++
 .../c-c++-common/torture/attr-sym-alias-5.c        | 54 ++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C    | 72 +++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C    | 26 +++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C    | 83 ++++++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C    | 28 ++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C    | 14 ++++
 gcc/varpool.cc                                     |  3 +
 33 files changed, 922 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 ad0be511d48..b8de16ebf7c 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 ``sym_alias`` 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 fd15459203a..20045698962 100644
--- a/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
+++ b/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
@@ -4274,6 +4274,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__ ((__sym_alias__ ("Ctor_For_Animal")))
         Animal() {Age_Count = 0;};
       private:
         int Age_Count;
@@ -4306,6 +4307,7 @@ both Carnivore and Domestic, that is:
         virtual int  Number_Of_Teeth ();
         virtual void Set_Owner (char* Name);
 
+        __attribute__ ((__sym_alias__ ("Ctor_For_Dog"))) // mnemonic alias
         Dog(); // Constructor
       private:
         int  Tooth_Count;
@@ -4344,7 +4346,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;
@@ -4360,7 +4363,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,7 +4385,8 @@ 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.
+mangled name (or an alias) 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.cc b/gcc/attribs.cc
index c7209c26acc..c75ca6974cb 100644
--- a/gcc/attribs.cc
+++ b/gcc/attribs.cc
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "target.h"
 #include "tree.h"
+#include "cgraph.h"
 #include "stringpool.h"
 #include "diagnostic-core.h"
 #include "attribs.h"
@@ -825,7 +826,8 @@ decl_attributes (tree *node, tree attributes, int flags,
 
       if (TYPE_P (*anode)
 	  && (flags & (int) ATTR_FLAG_TYPE_IN_PLACE)
-	  && COMPLETE_TYPE_P (*anode))
+	  && COMPLETE_TYPE_P (*anode)
+	  && !is_attribute_p ("sym_alias", name))
 	{
 	  warning (OPT_Wattributes, "type attributes ignored after type is already defined");
 	  continue;
@@ -2640,6 +2642,68 @@ attr_access::array_as_string (tree type) const
   return typstr;
 }
 
+/* Create a sym attribute for DECL to be visible with linkage name ID.  */
+
+tree
+create_sym_alias_decl (tree decl, tree id)
+{
+  const char *attr_str = "sym_alias";
+
+  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;
+
+      tree attr_name = get_identifier (attr_str);
+      error_at (DECL_SOURCE_LOCATION (decl),
+		"duplicate symbol name %qE in %qE attribute of %qD",
+		id, attr_name, decl);
+      inform (DECL_SOURCE_LOCATION (sym_node->decl),
+	      "already used by %qD", sym_node->decl);
+    }
+
+  tree clone = copy_node (decl);
+  DECL_ATTRIBUTES (clone) = remove_attribute (attr_str,
+					      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_extra_name_alias (clone, decl);
+    varpool_node::create_alias (clone, decl);
+  else
+    cgraph_node::create_same_body_alias (clone, decl);
+    // cgraph_node::create_alias (clone, decl);
+
+  return clone;
+}
+
+/* Create decls for all sym aliases requested in DECL's attributes.  */
+
+void
+create_sym_alias_decls (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl)
+      || DECL_ABSTRACT_P (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      create_sym_alias_decl (decl, id);
+    }
+}
+
 #if CHECKING_P
 
 namespace selftest
diff --git a/gcc/attribs.h b/gcc/attribs.h
index 84a43658a70..156f2c20588 100644
--- a/gcc/attribs.h
+++ b/gcc/attribs.h
@@ -398,4 +398,11 @@ extern void init_attr_rdwr_indices (rdwr_map *, tree);
 extern attr_access *get_parm_access (rdwr_map &, tree,
 				     tree = current_function_decl);
 
+extern tree create_sym_alias_decl (tree, tree);
+extern void create_sym_alias_decls (tree);
+
+#define FOR_EACH_SYM_ALIAS(sym, attrs)					\
+  for (tree sym = lookup_attribute ("sym_alias", (attrs));		\
+       sym; sym = lookup_attribute ("sym_alias", TREE_CHAIN (sym)))
+
 #endif // GCC_ATTRIBS_H
diff --git a/gcc/c-family/c-ada-spec.cc b/gcc/c-family/c-ada-spec.cc
index 050994d8416..5042b9cfecd 100644
--- a/gcc/c-family/c-ada-spec.cc
+++ b/gcc/c-family/c-ada-spec.cc
@@ -1431,6 +1431,13 @@ pp_ada_tree_identifier (pretty_printer *buffer, tree node, tree type,
 static void
 pp_asm_name (pretty_printer *buffer, tree t)
 {
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (t))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.cc b/gcc/c-family/c-attribs.cc
index 2b20e58c922..392437eab3b 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -108,7 +108,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_sym_alias_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,
@@ -388,6 +389,8 @@ const struct attribute_spec c_common_attribute_table[] =
 			      handle_alias_attribute, NULL },
   { "weakref",                0, 1, true,  false, false, false,
 			      handle_weakref_attribute, NULL },
+  { "sym_alias",              1, 1, false,  false, false, false,
+			      handle_sym_alias_attribute, NULL },
   { "no_instrument_function", 0, 0, true,  false, false, false,
 			      handle_no_instrument_function_attribute,
 			      NULL },
@@ -3002,7 +3005,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
@@ -3012,7 +3015,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
@@ -3022,6 +3025,29 @@ handle_alias_attribute (tree *node, tree name, tree args,
   return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs);
 }
 
+/* Handle a "sym_alias" attribute; arguments as in struct
+   attribute_spec.handler.  */
+
+static tree
+handle_sym_alias_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 (decl_in_symtab_p (node))
+    *no_add_attrs = false;
+  else if (TYPE_P (node) && c_dialect_cxx ())
+    *no_add_attrs = false;
+  else
+    return error_mark_node;
+
+  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.  */
 
@@ -3155,6 +3181,7 @@ handle_copy_attribute (tree *node, tree name, tree args,
 	      || is_attribute_p ("visibility", atname)
 	      || is_attribute_p ("weak", atname)
 	      || is_attribute_p ("weakref", atname)
+	      || is_attribute_p ("sym_alias", atname)
 	      || is_attribute_p ("target_clones", atname))
 	    continue;
 
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index cf1df82c0f4..e5a1ee54cb3 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -3119,6 +3119,8 @@ duplicate_decls (tree newdecl, tree olddecl)
 
   merge_decls (newdecl, olddecl, newtype, oldtype);
 
+  symtab_node::remap_sym_alias_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.cc b/gcc/cgraph.cc
index f93259a8c70..0bef2179e89 100644
--- a/gcc/cgraph.cc
+++ b/gcc/cgraph.cc
@@ -523,6 +523,8 @@ cgraph_node::create (tree decl)
   node->register_symbol ();
   maybe_record_nested_function (node);
 
+  create_sym_alias_decls (decl);
+
   return node;
 }
 
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index cfdd9f693a8..cb5ac01f7e2 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -327,6 +327,10 @@ public:
   /* Return DECL that alias is aliasing.  */
   inline tree get_alias_target_tree ();
 
+  /* Remap sym alias nodes recorded as aliasing REPLACED to alias REPLACEMENT
+     instead.  */
+  static void remap_sym_alias_target (tree replaced, tree replacement);
+
   /* Set section for symbol and its aliases.  */
   void set_section (const char *section);
 
diff --git a/gcc/cgraphunit.cc b/gcc/cgraphunit.cc
index bccd2f2abb5..eb2d05094e9 100644
--- a/gcc/cgraphunit.cc
+++ b/gcc/cgraphunit.cc
@@ -1175,7 +1175,7 @@ analyze_functions (bool first_time)
      C++ FE is confused about the COMDAT groups being right.  */
   if (symtab->cpp_implicit_aliases_done)
     FOR_EACH_SYMBOL (node)
-      if (node->cpp_implicit_alias)
+      if (node->cpp_implicit_alias && node->analyzed)
 	  node->fixup_same_cpp_alias_visibility (node->get_alias_target ());
   build_type_inheritance_graph ();
 
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index 6fdb56abfb9..fa8fc63db82 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -4896,6 +4896,68 @@ check_methods (tree t)
     }
 }
 
+/* Adjust sym alias name for CLONE, cloned from FN and named NAME,
+   if it is a cdtor, and drop the sym alias from other clones.  */
+
+void
+adjust_clone_attributes (tree fn, tree clone, tree name, bool skip_copy_p)
+{
+  if (IDENTIFIER_CDTOR_P (name))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	{
+	  found = true;
+	  break;
+	}
+
+      if (found
+	  && (name == complete_ctor_identifier
+	      || name == complete_dtor_identifier))
+	{
+	  /* Reuse the sym alias decls created for the primary cdtor
+	     decl.  */
+	  symtab_node::remap_sym_alias_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);
+
+	  if (!skip_copy_p)
+	    DECL_ATTRIBUTES (clone) = copy_list (DECL_ATTRIBUTES (clone));
+
+	  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	    {
+	      /* We need to copy this even with skip_copy_p, because
+		 even then copying was shallow.  */
+	      TREE_VALUE (sym) = copy_list (TREE_VALUE (sym));
+	      /* Append suf to the sym alias name.  */
+	      tree str = TREE_VALUE (TREE_VALUE (sym));
+	      char *symname = concat (TREE_STRING_POINTER (str), suf, NULL);
+	      str = build_string (TREE_STRING_LENGTH (str) + xlen, symname);
+	      TREE_VALUE (TREE_VALUE (sym)) = str;
+	      free (symname);
+	    }
+
+	  if (symtab_node::get (clone))
+	    create_sym_alias_decls (clone);
+	}
+    }
+  else
+    DECL_ATTRIBUTES (clone)
+      = remove_attribute ("sym_alias", DECL_ATTRIBUTES (clone));
+}
+
 /* FN is constructor, destructor or operator function.  Clone the
    declaration to create a NAME'd variant.  NEED_VTT_PARM_P and
    OMIT_INHERITED_PARMS_P are relevant if it's a cdtor.  */
@@ -5065,6 +5127,8 @@ build_clone (tree fn, tree name, bool need_vtt_parm_p,
   DECL_CHAIN (clone) = DECL_CHAIN (fn);
   DECL_CHAIN (fn) = clone;
 
+  adjust_clone_attributes (fn, clone, name);
+
   return clone;
 }
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 964af1ddd85..2bafed4e993 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5787,6 +5787,8 @@ struct GTY((for_user)) spec_entry
 
 extern int current_class_depth;
 
+void adjust_clone_attributes (tree fn, tree clone, tree name, bool = false);
+
 /* in decl.cc */
 
 /* An array of static vars & fns.  */
@@ -7022,6 +7024,7 @@ extern void do_push_parm_decls			(tree, tree, tree *);
 extern tree do_aggregate_paren_init		(tree, tree);
 
 /* in decl2.cc */
+extern void update_sym_alias_interface		(tree);
 extern void record_mangling			(tree, bool);
 extern void overwrite_mangling			(tree, tree);
 extern void note_mangling_alias			(tree, tree);
@@ -7596,6 +7599,7 @@ extern bool emit_tinfo_decl			(tree);
 extern unsigned get_pseudo_tinfo_index		(tree);
 extern tree get_pseudo_tinfo_type		(unsigned);
 extern tree build_if_nonnull			(tree, tree, tsubst_flags_t);
+extern void update_tinfo_sym_alias		(tree);
 
 /* in search.cc */
 extern tree get_parent_with_private_access 	(tree decl, tree binfo);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 2f2dbb8d107..5636d694b68 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -3237,6 +3237,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
 	      && TREE_STATIC (olddecl))))
     make_decl_rtl (olddecl);
 
+  symtab_node::remap_sym_alias_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.
@@ -3260,6 +3262,7 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
       FOR_EACH_CLONE (clone, olddecl)
 	{
 	  DECL_ATTRIBUTES (clone) = DECL_ATTRIBUTES (olddecl);
+	  adjust_clone_attributes (olddecl, clone, DECL_NAME (clone));
 	  DECL_PRESERVE_P (clone) |= DECL_PRESERVE_P (olddecl);
 	}
     }
@@ -10789,6 +10792,7 @@ grokfndecl (tree ctype,
     {
       cplus_decl_attributes (&decl, *attrlist, 0);
       *attrlist = NULL_TREE;
+      create_sym_alias_decls (decl);
     }
 
   if (DECL_HAS_CONTRACTS_P (decl))
diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
index 9e666e5eece..c7f009beaf0 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -1844,6 +1844,9 @@ cplus_decl_attributes (tree *decl, tree attributes, int flags)
   if (late_attrs)
     save_template_attributes (late_attrs, decl, flags);
 
+  if (TYPE_P (*decl) && attributes)
+    update_tinfo_sym_alias (*decl);
+
   /* Propagate deprecation out to the template.  */
   if (TREE_DEPRECATED (*decl))
     if (tree ti = get_template_info (*decl))
@@ -2200,6 +2203,47 @@ 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 sym aliases.  */
+
+void
+update_sym_alias_interface (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.  */
 
@@ -3007,6 +3051,8 @@ determine_visibility (tree decl)
        translation unit, we can make the type internal.  */
     constrain_visibility (decl, VISIBILITY_ANON, false);
 
+  update_sym_alias_interface (decl);
+
   /* If visibility changed and DECL already has DECL_RTL, ensure
      symbol flags are updated.  */
   if ((DECL_VISIBILITY (decl) != orig_visibility
@@ -3269,6 +3315,8 @@ tentative_decl_linkage (tree decl)
       else if (VAR_P (decl))
 	maybe_commonize_var (decl);
     }
+
+  update_sym_alias_interface (decl);
 }
 
 /* DECL is a FUNCTION_DECL or VAR_DECL.  If the object file linkage
@@ -3503,6 +3551,8 @@ import_export_decl (tree decl)
     }
 
   DECL_INTERFACE_KNOWN (decl) = 1;
+
+  update_sym_alias_interface (decl);
 }
 
 /* Return an expression that performs the destruction of DECL, which
diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc
index d19ea5d121c..f877780b2ac 100644
--- a/gcc/cp/name-lookup.cc
+++ b/gcc/cp/name-lookup.cc
@@ -22,9 +22,11 @@ along with GCC; see the file COPYING3.  If not see
 #define INCLUDE_MEMORY
 #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"
@@ -3479,6 +3481,15 @@ push_local_extern_decl_alias (tree decl)
 	  /* Adjust visibility.  */
 	  determine_visibility (alias);
 	}
+      else if (DECL_P (alias))
+	DECL_ATTRIBUTES (alias)
+	  = targetm.merge_decl_attributes (alias, decl);
+      if (DECL_P (alias))
+	{
+	  symtab_node::remap_sym_alias_target (decl, alias);
+	  DECL_ATTRIBUTES (decl)
+	    = remove_attribute ("sym_alias", DECL_ATTRIBUTES (alias));
+	}
     }
 
   retrofit_lang_decl (decl);
diff --git a/gcc/cp/optimize.cc b/gcc/cp/optimize.cc
index 9e8926e4cc6..4a2e8cb4354 100644
--- a/gcc/cp/optimize.cc
+++ b/gcc/cp/optimize.cc
@@ -528,9 +528,12 @@ maybe_clone_body (tree fn)
       DECL_VISIBILITY_SPECIFIED (clone) = DECL_VISIBILITY_SPECIFIED (fn);
       DECL_DLLIMPORT_P (clone) = DECL_DLLIMPORT_P (fn);
       DECL_ATTRIBUTES (clone) = clone_attrs (DECL_ATTRIBUTES (fn));
+      adjust_clone_attributes (fn, clone, DECL_NAME (clone), true);
       DECL_DISREGARD_INLINE_LIMITS (clone) = DECL_DISREGARD_INLINE_LIMITS (fn);
       set_decl_section_name (clone, fn);
 
+      update_sym_alias_interface (clone);
+
       /* Adjust the parameter names and locations.  */
       parm = DECL_ARGUMENTS (fn);
       clone_parm = DECL_ARGUMENTS (clone);
diff --git a/gcc/cp/rtti.cc b/gcc/cp/rtti.cc
index 7878929c246..4bb7b8c8c6e 100644
--- a/gcc/cp/rtti.cc
+++ b/gcc/cp/rtti.cc
@@ -28,8 +28,10 @@ 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"
+#include "cgraph.h"
 
 /* C++ returns type information to the user in struct type_info
    objects. We also use type information to implement dynamic_cast and
@@ -479,8 +481,13 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
 	  = build_tree_list (get_identifier ("non overlapping"),
 			     NULL_TREE);
       else
+	/* Share the non overlapping attribute, without assuming it's
+	   the only attribute, but assuming it's the last if it's
+	   present.  There may be sym aliases too, and those are not
+	   to be shared.  */
 	DECL_ATTRIBUTES (d)
-	  = DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]);
+	  = lookup_attribute ("non overlapping",
+			      DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]));
 
       /* Mark the variable as undefined -- but remember that we can
 	 define it later if we need to do so.  */
@@ -492,6 +499,16 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
       if (CLASS_TYPE_P (type))
 	CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type)) = d;
 
+      /* Copy sym alias attributes from the type to the rtti obj decl.  */
+      tree *attrs = &DECL_ATTRIBUTES (d);
+      FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+	{
+	  tree attr = tree_cons (TREE_PURPOSE (sym), TREE_VALUE (sym), *attrs);
+	  *attrs = attr;
+	  attrs = &TREE_CHAIN (attr);
+	}
+      create_sym_alias_decls (d);
+
       /* Add decl to the global array of tinfo decls.  */
       vec_safe_push (unemitted_tinfo_decls, d);
     }
@@ -499,6 +516,58 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
   return d;
 }
 
+/* After modifying the attributes of TYPE, check whether tinfo was
+   already created and, if so, add to it any sym alias attributes
+   that were not already present.  */
+
+void
+update_tinfo_sym_alias (tree type)
+{
+  if (!TYPE_SIZE (type) || !CLASS_TYPE_P (type))
+    return;
+
+  tree d = CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type));
+  if (!d)
+    return;
+
+  bool first = true;
+  symtab_node *node = NULL;
+
+  tree *attrs = &DECL_ATTRIBUTES (d);
+  FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (d_sym, *attrs)
+	if (TREE_VALUE (sym) == TREE_VALUE (d_sym))
+	  {
+	    found = true;
+	    break;
+	  }
+
+      if (found)
+	continue;
+
+      tree attr = tree_cons (TREE_PURPOSE (sym),
+			     TREE_VALUE (sym),
+			     *attrs);
+      *attrs = attr;
+      attrs = &TREE_CHAIN (attr);
+
+      if (first)
+	{
+	  first = false;
+	  node = symtab_node::get (d);
+	}
+
+      if (!node)
+	continue;
+
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+      create_sym_alias_decl (d, id);
+    }
+}
+
 /* Return the type_info object for TYPE.  */
 
 tree
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index e6de0815846..5dcdec76c1e 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -4141,6 +4141,43 @@ Function Attributes}, @ref{PowerPC Function Attributes},
 @ref{Nios II Function Attributes}, and @ref{S/390 Function Attributes}
 for details.
 
+@cindex @code{sym_alias} function attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} attribute causes @var{name} to be output as an
+alias to the function, with the same linkage and visibility as the
+function, when the function definition is output, provided that the
+function could be an alias target.  For instance,
+
+@smallexample
+void f (uint64_t) __attribute__ ((__sym_alias__ ("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},
+with the same linkage and visibility.  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.
+
+Aliases introduced with this attribute, such as @samp{f_u64} in the
+example above, are assembly symbol names: they do not undergo C++ name
+mangling, and are not made visible in any scope in the source language.
+They can, however, can be named as alias targets.
+
+This attribute requires assembler and object file support for aliases,
+and may not be available on all targets.
+
 @cindex @code{symver} function attribute
 @item symver ("@var{name2}@@@var{nodename}")
 On ELF targets this attribute creates a symbol version.  The @var{name2} part
@@ -8069,6 +8106,10 @@ will be placed in new, unique sections.
 
 This additional functionality requires Binutils version 2.36 or later.
 
+@cindex @code{sym_alias} variable attribute
+@item sym_alias ("@var{name}")
+See @pxref{Common Function Attributes}.
+
 @cindex @code{uninitialized} variable attribute
 @item uninitialized
 This attribute, attached to a variable with automatic storage, means that
@@ -9164,6 +9205,21 @@ is not supported; that is to say, if a given scalar object can be accessed
 through distinct types that assign a different storage order to it, then the
 behavior is undefined.
 
+@cindex @code{sym_alias} type attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} type 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__ ((__sym_alias__ ("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.
+
 @cindex @code{transparent_union} type attribute
 @item transparent_union
 
diff --git a/gcc/symtab.cc b/gcc/symtab.cc
index 0470509a98d..43ba88d7939 100644
--- a/gcc/symtab.cc
+++ b/gcc/symtab.cc
@@ -1943,6 +1943,44 @@ symtab_node::noninterposable_alias (symtab_node *node, void *data)
   return false;
 }
 
+/* Remap sym alias nodes recorded as aliasing REPLACED to alias
+   REPLACEMENT instead.  */
+
+void
+symtab_node::remap_sym_alias_target (tree replaced, tree replacement)
+{
+  if (!decl_in_symtab_p (replacement)
+      || !symtab_node::get (replacement))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (replaced))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      symtab_node *sym_node = symtab_node::get_for_asmname (id);
+
+      if (!sym_node)
+	{
+	  create_sym_alias_decl (replacement, id);
+	  continue;
+	}
+
+      gcc_assert (!sym_node->analyzed);
+      if (sym_node->alias_target != replaced)
+	continue;
+
+      // sym_node->definition = 0;
+
+      if (VAR_P (replaced))
+	// varpool_node::create_extra_name_alias (sym_node->decl, replacement);
+	varpool_node::create_alias (sym_node->decl, replacement);
+      else
+	cgraph_node::create_same_body_alias (sym_node->decl, replacement);
+	// 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-sym-alias-neg.c b/gcc/testsuite/c-c++-common/attr-sym-alias-neg.c
new file mode 100644
index 00000000000..210ca0b5db6
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/attr-sym-alias-neg.c
@@ -0,0 +1,80 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int f () {
+  int i __attribute__ ((sym_alias ("f_i"))); /* { dg-warning "ignored" } */
+  /* ??? X cannot be an alias target; should this be flagged? ... */
+  static int x __attribute__ ((sym_alias ("f_x")));
+  static int sx __attribute__ ((alias ("f_x"))); /* { dg-warning "ignored" } */
+  extern int xx __attribute__ ((alias ("f_x"))); /* { dg-warning "ignored" } */
+  return i;
+}
+
+/* ??? ... or should XR be accepted?  */
+extern int xr __attribute__ ((alias ("f_x"))); /* { dg-error "undefined" "" { xfail c++ } } */
+int dr __attribute__ ((alias ("f_x"))); /* { dg-error "defined both" } */
+
+
+struct s {
+  int j __attribute__ ((sym_alias ("s_j"))); /* { dg-warning "ignored" } */
+};
+
+
+int j __attribute__ ((sym_alias ("J_var")));
+void __attribute__ ((alias ("J_var")))
+j_fn (); /* { dg-error "between function and variable" } */
+
+
+void __attribute__ ((sym_alias ("K_fn")))
+k () {
+}
+extern int __attribute__ ((alias ("K_fn")))
+k_var; /* { dg-error "between function and variable" } */
+
+
+int l __attribute__ ((sym_alias ("L_fn")));
+
+/* These should be detected and reported, not because the names clash at the
+   source level, but because the asm symbols do.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+void
+L_fn () /* { dg-error "duplicate" "" { xfail ! c++ } } */
+{
+}
+
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __attribute__ ((sym_alias ("M_var")))
+m ()
+{
+}
+
+int M_var; /* { dg-error "duplicate" "" { xfail ! c++ } } */
+
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __attribute__ ((sym_alias ("N_sym")))
+n_fn ()
+{
+}
+
+int __attribute__ ((sym_alias ("N_sym")))
+n_var; /* { dg-error "duplicate" } */
+
+
+int __attribute__ ((sym_alias ("O_sym")))
+o_var;
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __attribute__ ((sym_alias ("O_sym")))
+o_fn () /* { dg-error "duplicate" } */
+{
+}
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-1.c b/gcc/testsuite/c-c++-common/goacc/declare-1.c
index 46ee01b6759..f2842893318 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-1.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-1.c
@@ -113,11 +113,11 @@ f_2 (void)
   int va3;
 #pragma acc declare device_resident(va3)
 
-#ifndef __cplusplus
+#if 0
   /* TODO PR90868
 
-     C: "error: variable '[...]' used more than once with '#pragma acc declare'".  */
-#else
+     "error: variable '[...]' used more than once with '#pragma acc declare'".  */
+
   extern int ve0;
 #pragma acc declare create(ve0)
 
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-2.c b/gcc/testsuite/c-c++-common/goacc/declare-2.c
index e2e22be57e9..aec59b69754 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-2.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-2.c
@@ -137,25 +137,25 @@ void
 f_pr90868_2 (void)
 {
   extern int we0;
-#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" } */
 
   extern int we1;
-#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" } */
 
   extern int *we2;
-#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" } */
 
   extern int we3;
-#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" } */
 
   extern int we4;
-#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" } */
 
   extern int we5;
-#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" } */
  
   extern int we6;
-#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" } */
 }
 
 
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
new file mode 100644
index 00000000000..61af50cb552
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
@@ -0,0 +1,39 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+int var_a = 1;
+
+void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+
+void
+foo_a ()
+{
+}
+
+
+int var_b;
+extern int var_b __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+
+void
+foo_b ()
+{
+}
+
+void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+
+
+int var_c __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+
+void __attribute__ ((__sym_alias__ ("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-sym-alias-2.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
new file mode 100644
index 00000000000..6f0d55ca42c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+struct s
+{
+  int mem __attribute__ ((__sym_alias__ ("MEMFOO"))); /* { dg-warning "attribute ignored" } */
+};
+
+void foo()
+{
+  extern void bar () __attribute__ ((__sym_alias__ ("FOOBAR")));
+  int var __attribute__ ((__sym_alias__ ("FOOVAR"))); /* { dg-warning "attribute ignored" } */
+}
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
new file mode 100644
index 00000000000..0052485a30a
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
@@ -0,0 +1,41 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int var_a = 1;
+
+void
+foo_a ()
+{
+  extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+}
+
+#if 0 // __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__ ((__sym_alias__ ("FOOVAR_B")));
+}
+
+int var_b;
+
+void __attribute__ ((__sym_alias__ ("FOOBAR_C")))
+foo_c ()
+{
+  void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+  /* Another sym for var_b.  */
+  extern int var_b __attribute__ ((__sym_alias__ ("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-sym-alias-4.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
new file mode 100644
index 00000000000..2beafa46379
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-require-alias "" } */
+
+int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A"))) = 42;
+
+int __attribute__ ((__sym_alias__ ("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/c-c++-common/torture/attr-sym-alias-5.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
new file mode 100644
index 00000000000..bee90136daf
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
@@ -0,0 +1,54 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+/* { dg-require-visibility "" } */
+
+int __attribute__ ((sym_alias ("A_hid"), visibility ("hidden"))) a;
+
+extern int __attribute__ ((sym_alias ("B_prt"))) b;
+
+int b __attribute__ ((visibility ("protected")));
+
+extern int __attribute__ ((sym_alias ("C_ntr"))) c;
+
+extern int c __attribute__ ((visibility ("internal")));
+
+int c;
+
+static int d __attribute__ ((sym_alias ("D_stt")));
+
+
+extern void __attribute__ ((sym_alias ("F_hid"), visibility ("hidden")))
+f ();
+
+void
+f () {
+}
+
+extern void __attribute__ ((sym_alias ("G_prt")))
+g ();
+
+void __attribute__ ((visibility ("protected")))
+g () {
+}
+
+extern void __attribute__ ((sym_alias ("H_ntr")))
+h ();
+
+void __attribute__ ((visibility ("internal")))
+h ();
+
+void h () {
+}
+
+static void __attribute__ ((sym_alias ("I_stt")))
+i () {
+}
+
+/* { dg-final { scan-assembler {hidden[^\n]*A_hid} } } */
+/* { dg-final { scan-assembler {protected[^\n]*B_prt} } } */
+/* { dg-final { scan-assembler {internal[^\n]*C_ntr} } } */
+/* { dg-final { scan-assembler-not {glob[^\n]*D_stt} } } */
+/* { dg-final { scan-assembler {hidden[^\n]*F_hid} } } */
+/* { dg-final { scan-assembler {protected[^\n]*G_prt} } } */
+/* { dg-final { scan-assembler {internal[^\n]*H_ntr} } } */
+/* { dg-final { scan-assembler-not {glob[^\n]*I_stt} } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
new file mode 100644
index 00000000000..579eda1a473
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
@@ -0,0 +1,72 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+class __attribute__ ((__sym_alias__ ("FOOCLS_A"),
+		      __sym_alias__ ("FOOCLS_A_Dupe"))) foo {
+  static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  void __attribute__ ((__sym_alias__ ("FOOBAR_A"))) bar ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+int foo::var = 1;
+
+foo::foo () {}
+
+void foo::bar () {}
+
+namespace b {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_B"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_B"))) foo ();
+    void __attribute__ ((__sym_alias__ ("FOOBAR_B"))) bar () {}
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_B"))) ~foo() {}
+  };
+
+  int foo::var = 2;
+
+  foo::foo () {
+    void (foo::*pbar)() = &foo::bar;
+  }
+}
+
+namespace c {
+  namespace cd {
+    class __attribute__ ((__sym_alias__ ("FOOCLS_C"))) foo {
+      static int var __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+      __attribute__ ((__sym_alias__ ("FOOCTR_C"))) foo () {
+	void (foo::*pbar)() = &foo::bar;
+      }
+      void __attribute__ ((__sym_alias__ ("FOOBAR_C"))) bar () {}
+      virtual __attribute__ ((__sym_alias__ ("FOODTR_C"))) ~foo() {}
+    };
+
+    int foo::var = 3;
+  }
+}
+
+/* { dg-final { scan-assembler "FOOCLS_A" } } */
+/* { dg-final { scan-assembler "FOOCLS_A_Dupe" } } */
+/* { 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-sym-alias-2.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
new file mode 100644
index 00000000000..6facfcc2cd9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+namespace {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_A"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo ();
+    void __attribute__ ((__sym_alias__ ("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-sym-alias-3.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
new file mode 100644
index 00000000000..42c7288ad4a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
@@ -0,0 +1,83 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+// sym can be applied to template function explicit instantiations.
+
+template <typename T>
+void
+fn(T) {
+};
+
+template void __attribute__ ((__sym_alias__ ("FOOFUN_UINT"))) fn<>(unsigned int);
+template void __attribute__ ((__sym_alias__ ("FOOFUN_LONG"))) fn<>(long);
+
+template<> void __attribute__ ((__sym_alias__ ("FOOFUN_CHAR"))) fn<>(char) {}
+
+
+template <typename T = void>
+struct
+foo {
+  virtual ~foo() {}
+
+  virtual void virtfun() {}
+
+  static void stfun() {}
+  void inlfun() {}
+};
+
+// Explicitly instantiate members before the enclosing class.
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_CHAR_VIRT"))) foo<char>::virtfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_CHAR_TI"))) foo<char>;
+
+// Though they're only output if the enclosing class is.
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_LONG_VIRT"))) foo<long>::virtfun();
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_LONG_TI_X"))) foo<long>;
+
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_VOID_ST"))) foo<void>::stfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_VOID_TI"))) foo<>;
+
+
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_X"))) foo<short>;
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_ST"))) foo<short>::stfun();
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_INL"))) foo<short>::inlfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_D"))) foo<short>;
+
+// Explicit specializations work too.
+
+template <>
+struct  __attribute__ ((__sym_alias__ ("FOOCLS_INT_TI")))
+foo<int>
+{
+  virtual ~foo() {}
+  virtual void __attribute__ ((__sym_alias__ ("FOOCLS_INT_VIRT"))) virtfun() {}
+};
+
+/* { dg-final { scan-assembler "FOOFUN_UINT" } } */
+/* { dg-final { scan-assembler "FOOFUN_LONG" } } */
+/* { dg-final { scan-assembler "FOOFUN_CHAR" } } */
+
+/* { dg-final { scan-assembler "FOOCLS_VOID_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_VOID_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_X" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_INL" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_D" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_TI_X" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_VIRT" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
new file mode 100644
index 00000000000..59b779de35d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+template <typename T = void>
+class
+__attribute__ ((__sym_alias__ ("FOOCLS")))
+foo // { dg-error "duplicate|already" }
+{
+  virtual ~foo() {}
+
+  template <typename U>
+  void
+    __attribute__ ((__sym_alias__ ("FOOTMF")))
+    tmemfun () {} // { dg-error "duplicate|already" }
+};
+
+template <typename T>
+void
+__attribute__ ((__sym_alias__ ("FOOTFN")))
+fn(T) { // { dg-error "duplicate|already" }
+};
+
+template class foo<>;
+template class foo<int>;
+template void foo<>::tmemfun<void>();
+template void foo<int>::tmemfun<void>();
+template void fn<>(int);
+template void fn<>(long);
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
new file mode 100644
index 00000000000..45d59b953c3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
@@ -0,0 +1,14 @@
+/* { dg-do compile { target c++11 } } */
+/* { dg-require-alias "" } */
+
+struct foo {
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+foo::foo () {}
+
+// Make sure the inherited cdtors don't duplicate the syms.
+struct bar : foo {
+  using foo::foo;
+};
diff --git a/gcc/varpool.cc b/gcc/varpool.cc
index e7b51b15e4a..33649906d1d 100644
--- a/gcc/varpool.cc
+++ b/gcc/varpool.cc
@@ -163,6 +163,9 @@ varpool_node::get_create (tree decl)
     }
 
   node->register_symbol ();
+
+  create_sym_alias_decls (decl);
+
   return node;
 }

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [gcc(refs/users/aoliva/heads/testme)] Introduce attribute sym_alias
@ 2023-11-30 15:18 Alexandre Oliva
  0 siblings, 0 replies; 33+ messages in thread
From: Alexandre Oliva @ 2023-11-30 15:18 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:72b4b8f85ef10a856de14ea6effe2ddefabdefe7

commit 72b4b8f85ef10a856de14ea6effe2ddefabdefe7
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Fri Jul 14 03:45:14 2023 -0300

    Introduce attribute sym_alias
    
    This patch introduces an attribute to add extra asm names (aliases)
    for a decl 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 class types, namespace-scoped
    variables and functions, static data members, member functions,
    explicit instantiations and specializations of template functions,
    members and classes.
    
    When applied to constructors or destructor, additional sym aliases
    with _Base and _Del suffixes are defined for variants other than
    complete-object ones.  This changes the assumption that clones always
    carry the same attributes as their abstract declarations, so there is
    now a function to adjust them.
    
    C++ also had a bug in which attributes from local extern declarations
    failed to be propagated to a preexisting corresponding
    namespace-scoped decl.  I've fixed that, and adjusted acc tests that
    distinguished between C and C++ in this regard.
    
    Applying the attribute to class types is only valid in C++, and the
    effect is to attach the alias to the RTTI object associated with the
    class type.
    
    for  gcc/ChangeLog
    
            * attribs.cc: Include cgraph.h.
            (decl_attributes): Allow late introduction of sym_alias in
            types.
            (create_sym_alias_decl, create_sym_alias_decls): New.
            * attribs.h: Declare them.
            (FOR_EACH_SYM_ALIAS): New macro.
            * cgraph.cc (cgraph_node::create): Create sym_alias decls.
            * varpool.cc (varpool_node::get_create): Create sym_alias
            decls.
            * cgraph.h (symtab_node::remap_sym_alias_target): New.
            * symtab.cc (symtab_node::remap_sym_alias_target): Define.
            * cgraphunit.cc (cgraph_node::analyze): Create alias_target
            node if needed.
            (analyze_functions): Fixup visibility of implicit alias only
            after its node is analyzed.
            * doc/extend.texi (sym_alias): Document for variables,
            functions and types.
    
    for  gcc/ada/ChangeLog
    
            * doc/gnat_rm/interfacing_to_other_languages.rst: Mention
            attribute sym_alias to give RTTI symbols mnemonic names.
            * doc/gnat_ugn/the_gnat_compilation_model.rst: Mention
            aliases.  Fix incorrect ref to C1 ctor variant.
    
    for  gcc/c-family/ChangeLog
    
            * c-ada-spec.cc (pp_asm_name): Use first sym_alias if
            available.
            * c-attribs.cc (handle_sym_alias_attribute): New.
            (c_common_attribute_table): Add sym_alias.
            (handle_copy_attribute): Do not copy sym_alias attribute.
    
    for  gcc/c/ChangeLog
    
            * c-decl.cc (duplicate_decls): Remap sym_alias target.
    
    for  gcc/cp/ChangeLog
    
            * class.cc (adjust_clone_attributes): New.
            (copy_fndecl_with_name, build_clone): Call it.
            * cp-tree.h (adjust_clone_attributes): Declare.
            (update_sym_alias_interface): Declare.
            (update_tinfo_sym_alias): Declare.
            * decl.cc (duplicate_decls): Remap sym_alias target.
            Adjust clone attributes.
            (grokfndecl): Tentatively create sym_alias decls after
            adding attributes in e.g. a template member function explicit
            instantiation.
            * decl2.cc (cplus_decl_attributes): Update tinfo sym_alias.
            (copy_interface, update_sym_alias_interface): New.
            (determine_visibility): Update sym_alias interface.
            (tentative_decl_linkage, import_export_decl): Likewise.
            * name-lookup.cc: Include target.h and cgraph.h.
            (push_local_extern_decl_alias): Merge attributes with
            namespace-scoped decl, and drop duplicate sym_alias.
            * optimize.cc (maybe_clone_body): Re-adjust attributes after
            cloning them.  Update sym_alias interface.
            * rtti.cc: Include attribs.h and cgraph.h.
            (get_tinfo_decl): Copy sym_alias attributes from type to tinfo
            decl.  Create sym_alias decls.
            (update_tinfo_sym_alias): New.
    
    for  gcc/testsuite/ChangeLog
    
            * c-c++-common/goacc/declare-1.c: Adjust.
            * c-c++-common/goacc/declare-2.c: Adjust.
            * c-c++-common/torture/attr-sym-alias-1.c: New.
            * c-c++-common/torture/attr-sym-alias-2.c: New.
            * c-c++-common/torture/attr-sym-alias-3.c: New.
            * c-c++-common/torture/attr-sym-alias-4.c: New.
            * c-c++-common/torture/attr-sym-alias-5.c: New.
            * c-c++-common/attr-sym-alias-neg.c: New.
            * g++.dg/torture/attr-sym-alias-1.C: New.
            * g++.dg/torture/attr-sym-alias-2.C: New.
            * g++.dg/torture/attr-sym-alias-3.C: New.
            * g++.dg/torture/attr-sym-alias-4.C: New.
            * g++.dg/torture/attr-sym-alias-5.C: New.

Diff:
---
 .../doc/gnat_rm/interfacing_to_other_languages.rst |  6 ++
 .../doc/gnat_ugn/the_gnat_compilation_model.rst    | 10 ++-
 gcc/attribs.cc                                     | 66 ++++++++++++++++-
 gcc/attribs.h                                      |  7 ++
 gcc/c-family/c-ada-spec.cc                         |  7 ++
 gcc/c-family/c-attribs.cc                          | 33 ++++++++-
 gcc/c/c-decl.cc                                    |  2 +
 gcc/cgraph.cc                                      |  2 +
 gcc/cgraph.h                                       |  4 ++
 gcc/cgraphunit.cc                                  |  2 +-
 gcc/cp/class.cc                                    | 64 +++++++++++++++++
 gcc/cp/cp-tree.h                                   |  4 ++
 gcc/cp/decl.cc                                     |  4 ++
 gcc/cp/decl2.cc                                    | 50 +++++++++++++
 gcc/cp/name-lookup.cc                              | 11 +++
 gcc/cp/optimize.cc                                 |  3 +
 gcc/cp/rtti.cc                                     | 71 +++++++++++++++++-
 gcc/doc/extend.texi                                | 56 +++++++++++++++
 gcc/symtab.cc                                      | 38 ++++++++++
 gcc/testsuite/c-c++-common/attr-sym-alias-neg.c    | 80 +++++++++++++++++++++
 gcc/testsuite/c-c++-common/goacc/declare-1.c       |  6 +-
 gcc/testsuite/c-c++-common/goacc/declare-2.c       | 14 ++--
 .../c-c++-common/torture/attr-sym-alias-1.c        | 39 ++++++++++
 .../c-c++-common/torture/attr-sym-alias-2.c        | 13 ++++
 .../c-c++-common/torture/attr-sym-alias-3.c        | 41 +++++++++++
 .../c-c++-common/torture/attr-sym-alias-4.c        | 28 ++++++++
 .../c-c++-common/torture/attr-sym-alias-5.c        | 54 ++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C    | 72 +++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C    | 26 +++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C    | 83 ++++++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C    | 28 ++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C    | 14 ++++
 gcc/varpool.cc                                     |  3 +
 33 files changed, 922 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 ad0be511d48..b8de16ebf7c 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 ``sym_alias`` 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 fd15459203a..20045698962 100644
--- a/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
+++ b/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
@@ -4274,6 +4274,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__ ((__sym_alias__ ("Ctor_For_Animal")))
         Animal() {Age_Count = 0;};
       private:
         int Age_Count;
@@ -4306,6 +4307,7 @@ both Carnivore and Domestic, that is:
         virtual int  Number_Of_Teeth ();
         virtual void Set_Owner (char* Name);
 
+        __attribute__ ((__sym_alias__ ("Ctor_For_Dog"))) // mnemonic alias
         Dog(); // Constructor
       private:
         int  Tooth_Count;
@@ -4344,7 +4346,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;
@@ -4360,7 +4363,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,7 +4385,8 @@ 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.
+mangled name (or an alias) 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.cc b/gcc/attribs.cc
index c7209c26acc..c75ca6974cb 100644
--- a/gcc/attribs.cc
+++ b/gcc/attribs.cc
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "target.h"
 #include "tree.h"
+#include "cgraph.h"
 #include "stringpool.h"
 #include "diagnostic-core.h"
 #include "attribs.h"
@@ -825,7 +826,8 @@ decl_attributes (tree *node, tree attributes, int flags,
 
       if (TYPE_P (*anode)
 	  && (flags & (int) ATTR_FLAG_TYPE_IN_PLACE)
-	  && COMPLETE_TYPE_P (*anode))
+	  && COMPLETE_TYPE_P (*anode)
+	  && !is_attribute_p ("sym_alias", name))
 	{
 	  warning (OPT_Wattributes, "type attributes ignored after type is already defined");
 	  continue;
@@ -2640,6 +2642,68 @@ attr_access::array_as_string (tree type) const
   return typstr;
 }
 
+/* Create a sym attribute for DECL to be visible with linkage name ID.  */
+
+tree
+create_sym_alias_decl (tree decl, tree id)
+{
+  const char *attr_str = "sym_alias";
+
+  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;
+
+      tree attr_name = get_identifier (attr_str);
+      error_at (DECL_SOURCE_LOCATION (decl),
+		"duplicate symbol name %qE in %qE attribute of %qD",
+		id, attr_name, decl);
+      inform (DECL_SOURCE_LOCATION (sym_node->decl),
+	      "already used by %qD", sym_node->decl);
+    }
+
+  tree clone = copy_node (decl);
+  DECL_ATTRIBUTES (clone) = remove_attribute (attr_str,
+					      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_extra_name_alias (clone, decl);
+    varpool_node::create_alias (clone, decl);
+  else
+    cgraph_node::create_same_body_alias (clone, decl);
+    // cgraph_node::create_alias (clone, decl);
+
+  return clone;
+}
+
+/* Create decls for all sym aliases requested in DECL's attributes.  */
+
+void
+create_sym_alias_decls (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl)
+      || DECL_ABSTRACT_P (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      create_sym_alias_decl (decl, id);
+    }
+}
+
 #if CHECKING_P
 
 namespace selftest
diff --git a/gcc/attribs.h b/gcc/attribs.h
index 84a43658a70..156f2c20588 100644
--- a/gcc/attribs.h
+++ b/gcc/attribs.h
@@ -398,4 +398,11 @@ extern void init_attr_rdwr_indices (rdwr_map *, tree);
 extern attr_access *get_parm_access (rdwr_map &, tree,
 				     tree = current_function_decl);
 
+extern tree create_sym_alias_decl (tree, tree);
+extern void create_sym_alias_decls (tree);
+
+#define FOR_EACH_SYM_ALIAS(sym, attrs)					\
+  for (tree sym = lookup_attribute ("sym_alias", (attrs));		\
+       sym; sym = lookup_attribute ("sym_alias", TREE_CHAIN (sym)))
+
 #endif // GCC_ATTRIBS_H
diff --git a/gcc/c-family/c-ada-spec.cc b/gcc/c-family/c-ada-spec.cc
index 050994d8416..5042b9cfecd 100644
--- a/gcc/c-family/c-ada-spec.cc
+++ b/gcc/c-family/c-ada-spec.cc
@@ -1431,6 +1431,13 @@ pp_ada_tree_identifier (pretty_printer *buffer, tree node, tree type,
 static void
 pp_asm_name (pretty_printer *buffer, tree t)
 {
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (t))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.cc b/gcc/c-family/c-attribs.cc
index 2b20e58c922..392437eab3b 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -108,7 +108,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_sym_alias_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,
@@ -388,6 +389,8 @@ const struct attribute_spec c_common_attribute_table[] =
 			      handle_alias_attribute, NULL },
   { "weakref",                0, 1, true,  false, false, false,
 			      handle_weakref_attribute, NULL },
+  { "sym_alias",              1, 1, false,  false, false, false,
+			      handle_sym_alias_attribute, NULL },
   { "no_instrument_function", 0, 0, true,  false, false, false,
 			      handle_no_instrument_function_attribute,
 			      NULL },
@@ -3002,7 +3005,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
@@ -3012,7 +3015,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
@@ -3022,6 +3025,29 @@ handle_alias_attribute (tree *node, tree name, tree args,
   return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs);
 }
 
+/* Handle a "sym_alias" attribute; arguments as in struct
+   attribute_spec.handler.  */
+
+static tree
+handle_sym_alias_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 (decl_in_symtab_p (node))
+    *no_add_attrs = false;
+  else if (TYPE_P (node) && c_dialect_cxx ())
+    *no_add_attrs = false;
+  else
+    return error_mark_node;
+
+  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.  */
 
@@ -3155,6 +3181,7 @@ handle_copy_attribute (tree *node, tree name, tree args,
 	      || is_attribute_p ("visibility", atname)
 	      || is_attribute_p ("weak", atname)
 	      || is_attribute_p ("weakref", atname)
+	      || is_attribute_p ("sym_alias", atname)
 	      || is_attribute_p ("target_clones", atname))
 	    continue;
 
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index cf1df82c0f4..e5a1ee54cb3 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -3119,6 +3119,8 @@ duplicate_decls (tree newdecl, tree olddecl)
 
   merge_decls (newdecl, olddecl, newtype, oldtype);
 
+  symtab_node::remap_sym_alias_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.cc b/gcc/cgraph.cc
index f93259a8c70..0bef2179e89 100644
--- a/gcc/cgraph.cc
+++ b/gcc/cgraph.cc
@@ -523,6 +523,8 @@ cgraph_node::create (tree decl)
   node->register_symbol ();
   maybe_record_nested_function (node);
 
+  create_sym_alias_decls (decl);
+
   return node;
 }
 
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index cfdd9f693a8..cb5ac01f7e2 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -327,6 +327,10 @@ public:
   /* Return DECL that alias is aliasing.  */
   inline tree get_alias_target_tree ();
 
+  /* Remap sym alias nodes recorded as aliasing REPLACED to alias REPLACEMENT
+     instead.  */
+  static void remap_sym_alias_target (tree replaced, tree replacement);
+
   /* Set section for symbol and its aliases.  */
   void set_section (const char *section);
 
diff --git a/gcc/cgraphunit.cc b/gcc/cgraphunit.cc
index bccd2f2abb5..eb2d05094e9 100644
--- a/gcc/cgraphunit.cc
+++ b/gcc/cgraphunit.cc
@@ -1175,7 +1175,7 @@ analyze_functions (bool first_time)
      C++ FE is confused about the COMDAT groups being right.  */
   if (symtab->cpp_implicit_aliases_done)
     FOR_EACH_SYMBOL (node)
-      if (node->cpp_implicit_alias)
+      if (node->cpp_implicit_alias && node->analyzed)
 	  node->fixup_same_cpp_alias_visibility (node->get_alias_target ());
   build_type_inheritance_graph ();
 
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index 6fdb56abfb9..fa8fc63db82 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -4896,6 +4896,68 @@ check_methods (tree t)
     }
 }
 
+/* Adjust sym alias name for CLONE, cloned from FN and named NAME,
+   if it is a cdtor, and drop the sym alias from other clones.  */
+
+void
+adjust_clone_attributes (tree fn, tree clone, tree name, bool skip_copy_p)
+{
+  if (IDENTIFIER_CDTOR_P (name))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	{
+	  found = true;
+	  break;
+	}
+
+      if (found
+	  && (name == complete_ctor_identifier
+	      || name == complete_dtor_identifier))
+	{
+	  /* Reuse the sym alias decls created for the primary cdtor
+	     decl.  */
+	  symtab_node::remap_sym_alias_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);
+
+	  if (!skip_copy_p)
+	    DECL_ATTRIBUTES (clone) = copy_list (DECL_ATTRIBUTES (clone));
+
+	  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	    {
+	      /* We need to copy this even with skip_copy_p, because
+		 even then copying was shallow.  */
+	      TREE_VALUE (sym) = copy_list (TREE_VALUE (sym));
+	      /* Append suf to the sym alias name.  */
+	      tree str = TREE_VALUE (TREE_VALUE (sym));
+	      char *symname = concat (TREE_STRING_POINTER (str), suf, NULL);
+	      str = build_string (TREE_STRING_LENGTH (str) + xlen, symname);
+	      TREE_VALUE (TREE_VALUE (sym)) = str;
+	      free (symname);
+	    }
+
+	  if (symtab_node::get (clone))
+	    create_sym_alias_decls (clone);
+	}
+    }
+  else
+    DECL_ATTRIBUTES (clone)
+      = remove_attribute ("sym_alias", DECL_ATTRIBUTES (clone));
+}
+
 /* FN is constructor, destructor or operator function.  Clone the
    declaration to create a NAME'd variant.  NEED_VTT_PARM_P and
    OMIT_INHERITED_PARMS_P are relevant if it's a cdtor.  */
@@ -5065,6 +5127,8 @@ build_clone (tree fn, tree name, bool need_vtt_parm_p,
   DECL_CHAIN (clone) = DECL_CHAIN (fn);
   DECL_CHAIN (fn) = clone;
 
+  adjust_clone_attributes (fn, clone, name);
+
   return clone;
 }
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 964af1ddd85..2bafed4e993 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5787,6 +5787,8 @@ struct GTY((for_user)) spec_entry
 
 extern int current_class_depth;
 
+void adjust_clone_attributes (tree fn, tree clone, tree name, bool = false);
+
 /* in decl.cc */
 
 /* An array of static vars & fns.  */
@@ -7022,6 +7024,7 @@ extern void do_push_parm_decls			(tree, tree, tree *);
 extern tree do_aggregate_paren_init		(tree, tree);
 
 /* in decl2.cc */
+extern void update_sym_alias_interface		(tree);
 extern void record_mangling			(tree, bool);
 extern void overwrite_mangling			(tree, tree);
 extern void note_mangling_alias			(tree, tree);
@@ -7596,6 +7599,7 @@ extern bool emit_tinfo_decl			(tree);
 extern unsigned get_pseudo_tinfo_index		(tree);
 extern tree get_pseudo_tinfo_type		(unsigned);
 extern tree build_if_nonnull			(tree, tree, tsubst_flags_t);
+extern void update_tinfo_sym_alias		(tree);
 
 /* in search.cc */
 extern tree get_parent_with_private_access 	(tree decl, tree binfo);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 2f2dbb8d107..5636d694b68 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -3237,6 +3237,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
 	      && TREE_STATIC (olddecl))))
     make_decl_rtl (olddecl);
 
+  symtab_node::remap_sym_alias_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.
@@ -3260,6 +3262,7 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
       FOR_EACH_CLONE (clone, olddecl)
 	{
 	  DECL_ATTRIBUTES (clone) = DECL_ATTRIBUTES (olddecl);
+	  adjust_clone_attributes (olddecl, clone, DECL_NAME (clone));
 	  DECL_PRESERVE_P (clone) |= DECL_PRESERVE_P (olddecl);
 	}
     }
@@ -10789,6 +10792,7 @@ grokfndecl (tree ctype,
     {
       cplus_decl_attributes (&decl, *attrlist, 0);
       *attrlist = NULL_TREE;
+      create_sym_alias_decls (decl);
     }
 
   if (DECL_HAS_CONTRACTS_P (decl))
diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
index 9e666e5eece..c7f009beaf0 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -1844,6 +1844,9 @@ cplus_decl_attributes (tree *decl, tree attributes, int flags)
   if (late_attrs)
     save_template_attributes (late_attrs, decl, flags);
 
+  if (TYPE_P (*decl) && attributes)
+    update_tinfo_sym_alias (*decl);
+
   /* Propagate deprecation out to the template.  */
   if (TREE_DEPRECATED (*decl))
     if (tree ti = get_template_info (*decl))
@@ -2200,6 +2203,47 @@ 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 sym aliases.  */
+
+void
+update_sym_alias_interface (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.  */
 
@@ -3007,6 +3051,8 @@ determine_visibility (tree decl)
        translation unit, we can make the type internal.  */
     constrain_visibility (decl, VISIBILITY_ANON, false);
 
+  update_sym_alias_interface (decl);
+
   /* If visibility changed and DECL already has DECL_RTL, ensure
      symbol flags are updated.  */
   if ((DECL_VISIBILITY (decl) != orig_visibility
@@ -3269,6 +3315,8 @@ tentative_decl_linkage (tree decl)
       else if (VAR_P (decl))
 	maybe_commonize_var (decl);
     }
+
+  update_sym_alias_interface (decl);
 }
 
 /* DECL is a FUNCTION_DECL or VAR_DECL.  If the object file linkage
@@ -3503,6 +3551,8 @@ import_export_decl (tree decl)
     }
 
   DECL_INTERFACE_KNOWN (decl) = 1;
+
+  update_sym_alias_interface (decl);
 }
 
 /* Return an expression that performs the destruction of DECL, which
diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc
index d19ea5d121c..f877780b2ac 100644
--- a/gcc/cp/name-lookup.cc
+++ b/gcc/cp/name-lookup.cc
@@ -22,9 +22,11 @@ along with GCC; see the file COPYING3.  If not see
 #define INCLUDE_MEMORY
 #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"
@@ -3479,6 +3481,15 @@ push_local_extern_decl_alias (tree decl)
 	  /* Adjust visibility.  */
 	  determine_visibility (alias);
 	}
+      else if (DECL_P (alias))
+	DECL_ATTRIBUTES (alias)
+	  = targetm.merge_decl_attributes (alias, decl);
+      if (DECL_P (alias))
+	{
+	  symtab_node::remap_sym_alias_target (decl, alias);
+	  DECL_ATTRIBUTES (decl)
+	    = remove_attribute ("sym_alias", DECL_ATTRIBUTES (alias));
+	}
     }
 
   retrofit_lang_decl (decl);
diff --git a/gcc/cp/optimize.cc b/gcc/cp/optimize.cc
index 9e8926e4cc6..4a2e8cb4354 100644
--- a/gcc/cp/optimize.cc
+++ b/gcc/cp/optimize.cc
@@ -528,9 +528,12 @@ maybe_clone_body (tree fn)
       DECL_VISIBILITY_SPECIFIED (clone) = DECL_VISIBILITY_SPECIFIED (fn);
       DECL_DLLIMPORT_P (clone) = DECL_DLLIMPORT_P (fn);
       DECL_ATTRIBUTES (clone) = clone_attrs (DECL_ATTRIBUTES (fn));
+      adjust_clone_attributes (fn, clone, DECL_NAME (clone), true);
       DECL_DISREGARD_INLINE_LIMITS (clone) = DECL_DISREGARD_INLINE_LIMITS (fn);
       set_decl_section_name (clone, fn);
 
+      update_sym_alias_interface (clone);
+
       /* Adjust the parameter names and locations.  */
       parm = DECL_ARGUMENTS (fn);
       clone_parm = DECL_ARGUMENTS (clone);
diff --git a/gcc/cp/rtti.cc b/gcc/cp/rtti.cc
index 7878929c246..4bb7b8c8c6e 100644
--- a/gcc/cp/rtti.cc
+++ b/gcc/cp/rtti.cc
@@ -28,8 +28,10 @@ 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"
+#include "cgraph.h"
 
 /* C++ returns type information to the user in struct type_info
    objects. We also use type information to implement dynamic_cast and
@@ -479,8 +481,13 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
 	  = build_tree_list (get_identifier ("non overlapping"),
 			     NULL_TREE);
       else
+	/* Share the non overlapping attribute, without assuming it's
+	   the only attribute, but assuming it's the last if it's
+	   present.  There may be sym aliases too, and those are not
+	   to be shared.  */
 	DECL_ATTRIBUTES (d)
-	  = DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]);
+	  = lookup_attribute ("non overlapping",
+			      DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]));
 
       /* Mark the variable as undefined -- but remember that we can
 	 define it later if we need to do so.  */
@@ -492,6 +499,16 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
       if (CLASS_TYPE_P (type))
 	CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type)) = d;
 
+      /* Copy sym alias attributes from the type to the rtti obj decl.  */
+      tree *attrs = &DECL_ATTRIBUTES (d);
+      FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+	{
+	  tree attr = tree_cons (TREE_PURPOSE (sym), TREE_VALUE (sym), *attrs);
+	  *attrs = attr;
+	  attrs = &TREE_CHAIN (attr);
+	}
+      create_sym_alias_decls (d);
+
       /* Add decl to the global array of tinfo decls.  */
       vec_safe_push (unemitted_tinfo_decls, d);
     }
@@ -499,6 +516,58 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
   return d;
 }
 
+/* After modifying the attributes of TYPE, check whether tinfo was
+   already created and, if so, add to it any sym alias attributes
+   that were not already present.  */
+
+void
+update_tinfo_sym_alias (tree type)
+{
+  if (!TYPE_SIZE (type) || !CLASS_TYPE_P (type))
+    return;
+
+  tree d = CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type));
+  if (!d)
+    return;
+
+  bool first = true;
+  symtab_node *node = NULL;
+
+  tree *attrs = &DECL_ATTRIBUTES (d);
+  FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (d_sym, *attrs)
+	if (TREE_VALUE (sym) == TREE_VALUE (d_sym))
+	  {
+	    found = true;
+	    break;
+	  }
+
+      if (found)
+	continue;
+
+      tree attr = tree_cons (TREE_PURPOSE (sym),
+			     TREE_VALUE (sym),
+			     *attrs);
+      *attrs = attr;
+      attrs = &TREE_CHAIN (attr);
+
+      if (first)
+	{
+	  first = false;
+	  node = symtab_node::get (d);
+	}
+
+      if (!node)
+	continue;
+
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+      create_sym_alias_decl (d, id);
+    }
+}
+
 /* Return the type_info object for TYPE.  */
 
 tree
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index e6de0815846..5dcdec76c1e 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -4141,6 +4141,43 @@ Function Attributes}, @ref{PowerPC Function Attributes},
 @ref{Nios II Function Attributes}, and @ref{S/390 Function Attributes}
 for details.
 
+@cindex @code{sym_alias} function attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} attribute causes @var{name} to be output as an
+alias to the function, with the same linkage and visibility as the
+function, when the function definition is output, provided that the
+function could be an alias target.  For instance,
+
+@smallexample
+void f (uint64_t) __attribute__ ((__sym_alias__ ("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},
+with the same linkage and visibility.  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.
+
+Aliases introduced with this attribute, such as @samp{f_u64} in the
+example above, are assembly symbol names: they do not undergo C++ name
+mangling, and are not made visible in any scope in the source language.
+They can, however, can be named as alias targets.
+
+This attribute requires assembler and object file support for aliases,
+and may not be available on all targets.
+
 @cindex @code{symver} function attribute
 @item symver ("@var{name2}@@@var{nodename}")
 On ELF targets this attribute creates a symbol version.  The @var{name2} part
@@ -8069,6 +8106,10 @@ will be placed in new, unique sections.
 
 This additional functionality requires Binutils version 2.36 or later.
 
+@cindex @code{sym_alias} variable attribute
+@item sym_alias ("@var{name}")
+See @pxref{Common Function Attributes}.
+
 @cindex @code{uninitialized} variable attribute
 @item uninitialized
 This attribute, attached to a variable with automatic storage, means that
@@ -9164,6 +9205,21 @@ is not supported; that is to say, if a given scalar object can be accessed
 through distinct types that assign a different storage order to it, then the
 behavior is undefined.
 
+@cindex @code{sym_alias} type attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} type 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__ ((__sym_alias__ ("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.
+
 @cindex @code{transparent_union} type attribute
 @item transparent_union
 
diff --git a/gcc/symtab.cc b/gcc/symtab.cc
index 0470509a98d..43ba88d7939 100644
--- a/gcc/symtab.cc
+++ b/gcc/symtab.cc
@@ -1943,6 +1943,44 @@ symtab_node::noninterposable_alias (symtab_node *node, void *data)
   return false;
 }
 
+/* Remap sym alias nodes recorded as aliasing REPLACED to alias
+   REPLACEMENT instead.  */
+
+void
+symtab_node::remap_sym_alias_target (tree replaced, tree replacement)
+{
+  if (!decl_in_symtab_p (replacement)
+      || !symtab_node::get (replacement))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (replaced))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      symtab_node *sym_node = symtab_node::get_for_asmname (id);
+
+      if (!sym_node)
+	{
+	  create_sym_alias_decl (replacement, id);
+	  continue;
+	}
+
+      gcc_assert (!sym_node->analyzed);
+      if (sym_node->alias_target != replaced)
+	continue;
+
+      // sym_node->definition = 0;
+
+      if (VAR_P (replaced))
+	// varpool_node::create_extra_name_alias (sym_node->decl, replacement);
+	varpool_node::create_alias (sym_node->decl, replacement);
+      else
+	cgraph_node::create_same_body_alias (sym_node->decl, replacement);
+	// 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-sym-alias-neg.c b/gcc/testsuite/c-c++-common/attr-sym-alias-neg.c
new file mode 100644
index 00000000000..7ad7f4e5409
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/attr-sym-alias-neg.c
@@ -0,0 +1,80 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int f () {
+  int i __attribute__ ((sym_alias ("f_i"))); /* { dg-warning "ignored" } */
+  /* ??? X cannot be an alias target; should this be flagged? ... */
+  static int x __attribute__ ((sym_alias ("f_x")));
+  static int sx __attribute__ ((alias ("f_x"))); /* { dg-warning "ignored" } */
+  extern int xx __attribute__ ((alias ("f_x"))); /* { dg-warning "ignored" } */
+  return i;
+}
+
+/* ??? ... or should XR be accepted?  */
+extern int xr __attribute__ ((alias ("f_x"))); /* { dg-error "undefined" "" { xfail c++ } } } */
+int dr __attribute__ ((alias ("f_x"))); /* { dg-error "defined both" } */
+
+
+struct s {
+  int j __attribute__ ((sym_alias ("s_j"))); /* { dg-warning "ignored" } */
+};
+
+
+int j __attribute__ ((sym_alias ("J_var")));
+void __attribute__ ((alias ("J_var")))
+j_fn (); /* { dg-error "between function and variable" } */
+
+
+void __attribute__ ((sym_alias ("K_fn")))
+k () {
+}
+extern int __attribute__ ((alias ("K_fn")))
+k_var; /* { dg-error "between function and variable" } */
+
+
+int l __attribute__ ((sym_alias ("L_fn")));
+
+/* These should be detected and reported, not because the names clash at the
+   source level, but because the asm symbols do.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+void
+L_fn () /* { dg-error "duplicate" "" { xfail ! c++ } } */
+{
+}
+
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __attribute__ ((sym_alias ("M_var")))
+m ()
+{
+}
+
+int M_var; /* { dg-error "duplicate" "" { xfail ! c++ } } */
+
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __attribute__ ((sym_alias ("N_sym")))
+n_fn ()
+{
+}
+
+int __attribute__ ((sym_alias ("N_sym")))
+n_var; /* { dg-error "duplicate" } */
+
+
+int __attribute__ ((sym_alias ("O_sym")))
+o_var;
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __attribute__ ((sym_alias ("O_sym")))
+o_fn () /* { dg-error "duplicate" } */
+{
+}
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-1.c b/gcc/testsuite/c-c++-common/goacc/declare-1.c
index 46ee01b6759..f2842893318 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-1.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-1.c
@@ -113,11 +113,11 @@ f_2 (void)
   int va3;
 #pragma acc declare device_resident(va3)
 
-#ifndef __cplusplus
+#if 0
   /* TODO PR90868
 
-     C: "error: variable '[...]' used more than once with '#pragma acc declare'".  */
-#else
+     "error: variable '[...]' used more than once with '#pragma acc declare'".  */
+
   extern int ve0;
 #pragma acc declare create(ve0)
 
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-2.c b/gcc/testsuite/c-c++-common/goacc/declare-2.c
index e2e22be57e9..aec59b69754 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-2.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-2.c
@@ -137,25 +137,25 @@ void
 f_pr90868_2 (void)
 {
   extern int we0;
-#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" } */
 
   extern int we1;
-#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" } */
 
   extern int *we2;
-#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" } */
 
   extern int we3;
-#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" } */
 
   extern int we4;
-#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" } */
 
   extern int we5;
-#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" } */
  
   extern int we6;
-#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" } */
 }
 
 
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
new file mode 100644
index 00000000000..61af50cb552
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
@@ -0,0 +1,39 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+int var_a = 1;
+
+void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+
+void
+foo_a ()
+{
+}
+
+
+int var_b;
+extern int var_b __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+
+void
+foo_b ()
+{
+}
+
+void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+
+
+int var_c __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+
+void __attribute__ ((__sym_alias__ ("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-sym-alias-2.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
new file mode 100644
index 00000000000..6f0d55ca42c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+struct s
+{
+  int mem __attribute__ ((__sym_alias__ ("MEMFOO"))); /* { dg-warning "attribute ignored" } */
+};
+
+void foo()
+{
+  extern void bar () __attribute__ ((__sym_alias__ ("FOOBAR")));
+  int var __attribute__ ((__sym_alias__ ("FOOVAR"))); /* { dg-warning "attribute ignored" } */
+}
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
new file mode 100644
index 00000000000..0052485a30a
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
@@ -0,0 +1,41 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int var_a = 1;
+
+void
+foo_a ()
+{
+  extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+}
+
+#if 0 // __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__ ((__sym_alias__ ("FOOVAR_B")));
+}
+
+int var_b;
+
+void __attribute__ ((__sym_alias__ ("FOOBAR_C")))
+foo_c ()
+{
+  void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+  /* Another sym for var_b.  */
+  extern int var_b __attribute__ ((__sym_alias__ ("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-sym-alias-4.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
new file mode 100644
index 00000000000..2beafa46379
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-require-alias "" } */
+
+int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A"))) = 42;
+
+int __attribute__ ((__sym_alias__ ("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/c-c++-common/torture/attr-sym-alias-5.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
new file mode 100644
index 00000000000..bee90136daf
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
@@ -0,0 +1,54 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+/* { dg-require-visibility "" } */
+
+int __attribute__ ((sym_alias ("A_hid"), visibility ("hidden"))) a;
+
+extern int __attribute__ ((sym_alias ("B_prt"))) b;
+
+int b __attribute__ ((visibility ("protected")));
+
+extern int __attribute__ ((sym_alias ("C_ntr"))) c;
+
+extern int c __attribute__ ((visibility ("internal")));
+
+int c;
+
+static int d __attribute__ ((sym_alias ("D_stt")));
+
+
+extern void __attribute__ ((sym_alias ("F_hid"), visibility ("hidden")))
+f ();
+
+void
+f () {
+}
+
+extern void __attribute__ ((sym_alias ("G_prt")))
+g ();
+
+void __attribute__ ((visibility ("protected")))
+g () {
+}
+
+extern void __attribute__ ((sym_alias ("H_ntr")))
+h ();
+
+void __attribute__ ((visibility ("internal")))
+h ();
+
+void h () {
+}
+
+static void __attribute__ ((sym_alias ("I_stt")))
+i () {
+}
+
+/* { dg-final { scan-assembler {hidden[^\n]*A_hid} } } */
+/* { dg-final { scan-assembler {protected[^\n]*B_prt} } } */
+/* { dg-final { scan-assembler {internal[^\n]*C_ntr} } } */
+/* { dg-final { scan-assembler-not {glob[^\n]*D_stt} } } */
+/* { dg-final { scan-assembler {hidden[^\n]*F_hid} } } */
+/* { dg-final { scan-assembler {protected[^\n]*G_prt} } } */
+/* { dg-final { scan-assembler {internal[^\n]*H_ntr} } } */
+/* { dg-final { scan-assembler-not {glob[^\n]*I_stt} } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
new file mode 100644
index 00000000000..579eda1a473
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
@@ -0,0 +1,72 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+class __attribute__ ((__sym_alias__ ("FOOCLS_A"),
+		      __sym_alias__ ("FOOCLS_A_Dupe"))) foo {
+  static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  void __attribute__ ((__sym_alias__ ("FOOBAR_A"))) bar ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+int foo::var = 1;
+
+foo::foo () {}
+
+void foo::bar () {}
+
+namespace b {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_B"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_B"))) foo ();
+    void __attribute__ ((__sym_alias__ ("FOOBAR_B"))) bar () {}
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_B"))) ~foo() {}
+  };
+
+  int foo::var = 2;
+
+  foo::foo () {
+    void (foo::*pbar)() = &foo::bar;
+  }
+}
+
+namespace c {
+  namespace cd {
+    class __attribute__ ((__sym_alias__ ("FOOCLS_C"))) foo {
+      static int var __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+      __attribute__ ((__sym_alias__ ("FOOCTR_C"))) foo () {
+	void (foo::*pbar)() = &foo::bar;
+      }
+      void __attribute__ ((__sym_alias__ ("FOOBAR_C"))) bar () {}
+      virtual __attribute__ ((__sym_alias__ ("FOODTR_C"))) ~foo() {}
+    };
+
+    int foo::var = 3;
+  }
+}
+
+/* { dg-final { scan-assembler "FOOCLS_A" } } */
+/* { dg-final { scan-assembler "FOOCLS_A_Dupe" } } */
+/* { 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-sym-alias-2.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
new file mode 100644
index 00000000000..6facfcc2cd9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+namespace {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_A"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo ();
+    void __attribute__ ((__sym_alias__ ("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-sym-alias-3.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
new file mode 100644
index 00000000000..42c7288ad4a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
@@ -0,0 +1,83 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+// sym can be applied to template function explicit instantiations.
+
+template <typename T>
+void
+fn(T) {
+};
+
+template void __attribute__ ((__sym_alias__ ("FOOFUN_UINT"))) fn<>(unsigned int);
+template void __attribute__ ((__sym_alias__ ("FOOFUN_LONG"))) fn<>(long);
+
+template<> void __attribute__ ((__sym_alias__ ("FOOFUN_CHAR"))) fn<>(char) {}
+
+
+template <typename T = void>
+struct
+foo {
+  virtual ~foo() {}
+
+  virtual void virtfun() {}
+
+  static void stfun() {}
+  void inlfun() {}
+};
+
+// Explicitly instantiate members before the enclosing class.
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_CHAR_VIRT"))) foo<char>::virtfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_CHAR_TI"))) foo<char>;
+
+// Though they're only output if the enclosing class is.
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_LONG_VIRT"))) foo<long>::virtfun();
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_LONG_TI_X"))) foo<long>;
+
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_VOID_ST"))) foo<void>::stfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_VOID_TI"))) foo<>;
+
+
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_X"))) foo<short>;
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_ST"))) foo<short>::stfun();
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_INL"))) foo<short>::inlfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_D"))) foo<short>;
+
+// Explicit specializations work too.
+
+template <>
+struct  __attribute__ ((__sym_alias__ ("FOOCLS_INT_TI")))
+foo<int>
+{
+  virtual ~foo() {}
+  virtual void __attribute__ ((__sym_alias__ ("FOOCLS_INT_VIRT"))) virtfun() {}
+};
+
+/* { dg-final { scan-assembler "FOOFUN_UINT" } } */
+/* { dg-final { scan-assembler "FOOFUN_LONG" } } */
+/* { dg-final { scan-assembler "FOOFUN_CHAR" } } */
+
+/* { dg-final { scan-assembler "FOOCLS_VOID_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_VOID_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_X" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_INL" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_D" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_TI_X" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_VIRT" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
new file mode 100644
index 00000000000..59b779de35d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+template <typename T = void>
+class
+__attribute__ ((__sym_alias__ ("FOOCLS")))
+foo // { dg-error "duplicate|already" }
+{
+  virtual ~foo() {}
+
+  template <typename U>
+  void
+    __attribute__ ((__sym_alias__ ("FOOTMF")))
+    tmemfun () {} // { dg-error "duplicate|already" }
+};
+
+template <typename T>
+void
+__attribute__ ((__sym_alias__ ("FOOTFN")))
+fn(T) { // { dg-error "duplicate|already" }
+};
+
+template class foo<>;
+template class foo<int>;
+template void foo<>::tmemfun<void>();
+template void foo<int>::tmemfun<void>();
+template void fn<>(int);
+template void fn<>(long);
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
new file mode 100644
index 00000000000..45d59b953c3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
@@ -0,0 +1,14 @@
+/* { dg-do compile { target c++11 } } */
+/* { dg-require-alias "" } */
+
+struct foo {
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+foo::foo () {}
+
+// Make sure the inherited cdtors don't duplicate the syms.
+struct bar : foo {
+  using foo::foo;
+};
diff --git a/gcc/varpool.cc b/gcc/varpool.cc
index e7b51b15e4a..33649906d1d 100644
--- a/gcc/varpool.cc
+++ b/gcc/varpool.cc
@@ -163,6 +163,9 @@ varpool_node::get_create (tree decl)
     }
 
   node->register_symbol ();
+
+  create_sym_alias_decls (decl);
+
   return node;
 }

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [gcc(refs/users/aoliva/heads/testme)] Introduce attribute sym_alias
@ 2023-11-30 12:38 Alexandre Oliva
  0 siblings, 0 replies; 33+ messages in thread
From: Alexandre Oliva @ 2023-11-30 12:38 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:e711bbb435f609fe2834b8c96bee407874741c57

commit e711bbb435f609fe2834b8c96bee407874741c57
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Fri Jul 14 03:45:14 2023 -0300

    Introduce attribute sym_alias
    
    This patch introduces an attribute to add extra asm names (aliases)
    for a decl 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 class types, namespace-scoped
    variables and functions, static data members, member functions,
    explicit instantiations and specializations of template functions,
    members and classes.
    
    When applied to constructors or destructor, additional sym aliases
    with _Base and _Del suffixes are defined for variants other than
    complete-object ones.  This changes the assumption that clones always
    carry the same attributes as their abstract declarations, so there is
    now a function to adjust them.
    
    C++ also had a bug in which attributes from local extern declarations
    failed to be propagated to a preexisting corresponding
    namespace-scoped decl.  I've fixed that, and adjusted acc tests that
    distinguished between C and C++ in this regard.
    
    Applying the attribute to class types is only valid in C++, and the
    effect is to attach the alias to the RTTI object associated with the
    class type.
    
    for  gcc/ChangeLog
    
            * attribs.cc: Include cgraph.h.
            (decl_attributes): Allow late introduction of sym_alias in
            types.
            (create_sym_alias_decl, create_sym_alias_decls): New.
            * attribs.h: Declare them.
            (FOR_EACH_SYM_ALIAS): New macro.
            * cgraph.cc (cgraph_node::create): Create sym_alias decls.
            * varpool.cc (varpool_node::get_create): Create sym_alias
            decls.
            * cgraph.h (symtab_node::remap_sym_alias_target): New.
            * symtab.cc (symtab_node::remap_sym_alias_target): Define.
            * cgraphunit.cc (cgraph_node::analyze): Create alias_target
            node if needed.
            (analyze_functions): Fixup visibility of implicit alias only
            after its node is analyzed.
            * doc/extend.texi (sym_alias): Document for variables,
            functions and types.
    
    for  gcc/ada/ChangeLog
    
            * doc/gnat_rm/interfacing_to_other_languages.rst: Mention
            attribute sym_alias to give RTTI symbols mnemonic names.
            * doc/gnat_ugn/the_gnat_compilation_model.rst: Mention
            aliases.  Fix incorrect ref to C1 ctor variant.
    
    for  gcc/c-family/ChangeLog
    
            * c-ada-spec.cc (pp_asm_name): Use first sym_alias if
            available.
            * c-attribs.cc (handle_sym_alias_attribute): New.
            (c_common_attribute_table): Add sym_alias.
            (handle_copy_attribute): Do not copy sym_alias attribute.
    
    for  gcc/c/ChangeLog
    
            * c-decl.cc (duplicate_decls): Remap sym_alias target.
    
    for  gcc/cp/ChangeLog
    
            * class.cc (adjust_clone_attributes): New.
            (copy_fndecl_with_name, build_clone): Call it.
            * cp-tree.h (adjust_clone_attributes): Declare.
            (update_sym_alias_interface): Declare.
            (update_tinfo_sym_alias): Declare.
            * decl.cc (duplicate_decls): Remap sym_alias target.
            Adjust clone attributes.
            (grokfndecl): Tentatively create sym_alias decls after
            adding attributes in e.g. a template member function explicit
            instantiation.
            * decl2.cc (cplus_decl_attributes): Update tinfo sym_alias.
            (copy_interface, update_sym_alias_interface): New.
            (determine_visibility): Update sym_alias interface.
            (tentative_decl_linkage, import_export_decl): Likewise.
            * name-lookup.cc: Include target.h and cgraph.h.
            (push_local_extern_decl_alias): Merge attributes with
            namespace-scoped decl, and drop duplicate sym_alias.
            * optimize.cc (maybe_clone_body): Re-adjust attributes after
            cloning them.  Update sym_alias interface.
            * rtti.cc: Include attribs.h and cgraph.h.
            (get_tinfo_decl): Copy sym_alias attributes from type to tinfo
            decl.  Create sym_alias decls.
            (update_tinfo_sym_alias): New.
    
    for  gcc/testsuite/ChangeLog
    
            * c-c++-common/goacc/declare-1.c: Adjust.
            * c-c++-common/goacc/declare-2.c: Adjust.
            * c-c++-common/torture/attr-sym-alias-1.c: New.
            * c-c++-common/torture/attr-sym-alias-2.c: New.
            * c-c++-common/torture/attr-sym-alias-3.c: New.
            * c-c++-common/torture/attr-sym-alias-4.c: New.
            * c-c++-common/torture/attr-sym-alias-5.c: New.
            * c-c++-common/attr-sym-alias-neg.c: New.
            * g++.dg/torture/attr-sym-alias-1.C: New.
            * g++.dg/torture/attr-sym-alias-2.C: New.
            * g++.dg/torture/attr-sym-alias-3.C: New.
            * g++.dg/torture/attr-sym-alias-4.C: New.
            * g++.dg/torture/attr-sym-alias-5.C: New.

Diff:
---
 .../doc/gnat_rm/interfacing_to_other_languages.rst |  6 ++
 .../doc/gnat_ugn/the_gnat_compilation_model.rst    | 10 ++-
 gcc/attribs.cc                                     | 63 +++++++++++++++-
 gcc/attribs.h                                      |  7 ++
 gcc/c-family/c-ada-spec.cc                         |  7 ++
 gcc/c-family/c-attribs.cc                          | 33 ++++++++-
 gcc/c/c-decl.cc                                    |  2 +
 gcc/cgraph.cc                                      |  2 +
 gcc/cgraph.h                                       |  4 ++
 gcc/cgraphunit.cc                                  |  2 +-
 gcc/cp/class.cc                                    | 64 +++++++++++++++++
 gcc/cp/cp-tree.h                                   |  4 ++
 gcc/cp/decl.cc                                     |  4 ++
 gcc/cp/decl2.cc                                    | 50 +++++++++++++
 gcc/cp/name-lookup.cc                              | 11 +++
 gcc/cp/optimize.cc                                 |  3 +
 gcc/cp/rtti.cc                                     | 71 +++++++++++++++++-
 gcc/doc/extend.texi                                | 56 +++++++++++++++
 gcc/symtab.cc                                      | 34 +++++++++
 gcc/testsuite/c-c++-common/attr-sym-alias-neg.c    | 80 +++++++++++++++++++++
 gcc/testsuite/c-c++-common/goacc/declare-1.c       |  6 +-
 gcc/testsuite/c-c++-common/goacc/declare-2.c       | 14 ++--
 .../c-c++-common/torture/attr-sym-alias-1.c        | 39 ++++++++++
 .../c-c++-common/torture/attr-sym-alias-2.c        | 13 ++++
 .../c-c++-common/torture/attr-sym-alias-3.c        | 41 +++++++++++
 .../c-c++-common/torture/attr-sym-alias-4.c        | 28 ++++++++
 .../c-c++-common/torture/attr-sym-alias-5.c        | 54 ++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C    | 72 +++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C    | 26 +++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C    | 83 ++++++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C    | 28 ++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C    | 14 ++++
 gcc/varpool.cc                                     |  3 +
 33 files changed, 915 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 ad0be511d48..b8de16ebf7c 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 ``sym_alias`` 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 fd15459203a..20045698962 100644
--- a/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
+++ b/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
@@ -4274,6 +4274,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__ ((__sym_alias__ ("Ctor_For_Animal")))
         Animal() {Age_Count = 0;};
       private:
         int Age_Count;
@@ -4306,6 +4307,7 @@ both Carnivore and Domestic, that is:
         virtual int  Number_Of_Teeth ();
         virtual void Set_Owner (char* Name);
 
+        __attribute__ ((__sym_alias__ ("Ctor_For_Dog"))) // mnemonic alias
         Dog(); // Constructor
       private:
         int  Tooth_Count;
@@ -4344,7 +4346,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;
@@ -4360,7 +4363,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,7 +4385,8 @@ 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.
+mangled name (or an alias) 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.cc b/gcc/attribs.cc
index c7209c26acc..d41215d004d 100644
--- a/gcc/attribs.cc
+++ b/gcc/attribs.cc
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "target.h"
 #include "tree.h"
+#include "cgraph.h"
 #include "stringpool.h"
 #include "diagnostic-core.h"
 #include "attribs.h"
@@ -825,7 +826,8 @@ decl_attributes (tree *node, tree attributes, int flags,
 
       if (TYPE_P (*anode)
 	  && (flags & (int) ATTR_FLAG_TYPE_IN_PLACE)
-	  && COMPLETE_TYPE_P (*anode))
+	  && COMPLETE_TYPE_P (*anode)
+	  && !is_attribute_p ("sym_alias", name))
 	{
 	  warning (OPT_Wattributes, "type attributes ignored after type is already defined");
 	  continue;
@@ -2640,6 +2642,65 @@ attr_access::array_as_string (tree type) const
   return typstr;
 }
 
+/* Create a sym attribute for DECL to be visible with linkage name ID.  */
+
+tree
+create_sym_alias_decl (tree decl, tree id)
+{
+  const char *attr_str = "sym_alias";
+
+  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;
+
+      tree attr_name = get_identifier (attr_str);
+      error_at (DECL_SOURCE_LOCATION (decl),
+		"duplicate symbol name %qE in %qE attribute of %qD",
+		id, attr_name, decl);
+      inform (DECL_SOURCE_LOCATION (sym_node->decl),
+	      "already used by %qD", sym_node->decl);
+    }
+
+  tree clone = copy_node (decl);
+  DECL_ATTRIBUTES (clone) = remove_attribute (attr_str,
+					      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))
+    varpool_node::create_alias (clone, decl);
+  else
+    cgraph_node::create_alias (clone, decl);
+
+  return clone;
+}
+
+/* Create decls for all sym aliases requested in DECL's attributes.  */
+
+void
+create_sym_alias_decls (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl)
+      || DECL_ABSTRACT_P (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      create_sym_alias_decl (decl, id);
+    }
+}
+
 #if CHECKING_P
 
 namespace selftest
diff --git a/gcc/attribs.h b/gcc/attribs.h
index 84a43658a70..156f2c20588 100644
--- a/gcc/attribs.h
+++ b/gcc/attribs.h
@@ -398,4 +398,11 @@ extern void init_attr_rdwr_indices (rdwr_map *, tree);
 extern attr_access *get_parm_access (rdwr_map &, tree,
 				     tree = current_function_decl);
 
+extern tree create_sym_alias_decl (tree, tree);
+extern void create_sym_alias_decls (tree);
+
+#define FOR_EACH_SYM_ALIAS(sym, attrs)					\
+  for (tree sym = lookup_attribute ("sym_alias", (attrs));		\
+       sym; sym = lookup_attribute ("sym_alias", TREE_CHAIN (sym)))
+
 #endif // GCC_ATTRIBS_H
diff --git a/gcc/c-family/c-ada-spec.cc b/gcc/c-family/c-ada-spec.cc
index 050994d8416..5042b9cfecd 100644
--- a/gcc/c-family/c-ada-spec.cc
+++ b/gcc/c-family/c-ada-spec.cc
@@ -1431,6 +1431,13 @@ pp_ada_tree_identifier (pretty_printer *buffer, tree node, tree type,
 static void
 pp_asm_name (pretty_printer *buffer, tree t)
 {
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (t))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.cc b/gcc/c-family/c-attribs.cc
index 2b20e58c922..392437eab3b 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -108,7 +108,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_sym_alias_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,
@@ -388,6 +389,8 @@ const struct attribute_spec c_common_attribute_table[] =
 			      handle_alias_attribute, NULL },
   { "weakref",                0, 1, true,  false, false, false,
 			      handle_weakref_attribute, NULL },
+  { "sym_alias",              1, 1, false,  false, false, false,
+			      handle_sym_alias_attribute, NULL },
   { "no_instrument_function", 0, 0, true,  false, false, false,
 			      handle_no_instrument_function_attribute,
 			      NULL },
@@ -3002,7 +3005,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
@@ -3012,7 +3015,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
@@ -3022,6 +3025,29 @@ handle_alias_attribute (tree *node, tree name, tree args,
   return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs);
 }
 
+/* Handle a "sym_alias" attribute; arguments as in struct
+   attribute_spec.handler.  */
+
+static tree
+handle_sym_alias_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 (decl_in_symtab_p (node))
+    *no_add_attrs = false;
+  else if (TYPE_P (node) && c_dialect_cxx ())
+    *no_add_attrs = false;
+  else
+    return error_mark_node;
+
+  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.  */
 
@@ -3155,6 +3181,7 @@ handle_copy_attribute (tree *node, tree name, tree args,
 	      || is_attribute_p ("visibility", atname)
 	      || is_attribute_p ("weak", atname)
 	      || is_attribute_p ("weakref", atname)
+	      || is_attribute_p ("sym_alias", atname)
 	      || is_attribute_p ("target_clones", atname))
 	    continue;
 
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index cf1df82c0f4..e5a1ee54cb3 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -3119,6 +3119,8 @@ duplicate_decls (tree newdecl, tree olddecl)
 
   merge_decls (newdecl, olddecl, newtype, oldtype);
 
+  symtab_node::remap_sym_alias_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.cc b/gcc/cgraph.cc
index f93259a8c70..0bef2179e89 100644
--- a/gcc/cgraph.cc
+++ b/gcc/cgraph.cc
@@ -523,6 +523,8 @@ cgraph_node::create (tree decl)
   node->register_symbol ();
   maybe_record_nested_function (node);
 
+  create_sym_alias_decls (decl);
+
   return node;
 }
 
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index cfdd9f693a8..cb5ac01f7e2 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -327,6 +327,10 @@ public:
   /* Return DECL that alias is aliasing.  */
   inline tree get_alias_target_tree ();
 
+  /* Remap sym alias nodes recorded as aliasing REPLACED to alias REPLACEMENT
+     instead.  */
+  static void remap_sym_alias_target (tree replaced, tree replacement);
+
   /* Set section for symbol and its aliases.  */
   void set_section (const char *section);
 
diff --git a/gcc/cgraphunit.cc b/gcc/cgraphunit.cc
index bccd2f2abb5..eb2d05094e9 100644
--- a/gcc/cgraphunit.cc
+++ b/gcc/cgraphunit.cc
@@ -1175,7 +1175,7 @@ analyze_functions (bool first_time)
      C++ FE is confused about the COMDAT groups being right.  */
   if (symtab->cpp_implicit_aliases_done)
     FOR_EACH_SYMBOL (node)
-      if (node->cpp_implicit_alias)
+      if (node->cpp_implicit_alias && node->analyzed)
 	  node->fixup_same_cpp_alias_visibility (node->get_alias_target ());
   build_type_inheritance_graph ();
 
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index 6fdb56abfb9..fa8fc63db82 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -4896,6 +4896,68 @@ check_methods (tree t)
     }
 }
 
+/* Adjust sym alias name for CLONE, cloned from FN and named NAME,
+   if it is a cdtor, and drop the sym alias from other clones.  */
+
+void
+adjust_clone_attributes (tree fn, tree clone, tree name, bool skip_copy_p)
+{
+  if (IDENTIFIER_CDTOR_P (name))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	{
+	  found = true;
+	  break;
+	}
+
+      if (found
+	  && (name == complete_ctor_identifier
+	      || name == complete_dtor_identifier))
+	{
+	  /* Reuse the sym alias decls created for the primary cdtor
+	     decl.  */
+	  symtab_node::remap_sym_alias_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);
+
+	  if (!skip_copy_p)
+	    DECL_ATTRIBUTES (clone) = copy_list (DECL_ATTRIBUTES (clone));
+
+	  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	    {
+	      /* We need to copy this even with skip_copy_p, because
+		 even then copying was shallow.  */
+	      TREE_VALUE (sym) = copy_list (TREE_VALUE (sym));
+	      /* Append suf to the sym alias name.  */
+	      tree str = TREE_VALUE (TREE_VALUE (sym));
+	      char *symname = concat (TREE_STRING_POINTER (str), suf, NULL);
+	      str = build_string (TREE_STRING_LENGTH (str) + xlen, symname);
+	      TREE_VALUE (TREE_VALUE (sym)) = str;
+	      free (symname);
+	    }
+
+	  if (symtab_node::get (clone))
+	    create_sym_alias_decls (clone);
+	}
+    }
+  else
+    DECL_ATTRIBUTES (clone)
+      = remove_attribute ("sym_alias", DECL_ATTRIBUTES (clone));
+}
+
 /* FN is constructor, destructor or operator function.  Clone the
    declaration to create a NAME'd variant.  NEED_VTT_PARM_P and
    OMIT_INHERITED_PARMS_P are relevant if it's a cdtor.  */
@@ -5065,6 +5127,8 @@ build_clone (tree fn, tree name, bool need_vtt_parm_p,
   DECL_CHAIN (clone) = DECL_CHAIN (fn);
   DECL_CHAIN (fn) = clone;
 
+  adjust_clone_attributes (fn, clone, name);
+
   return clone;
 }
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 964af1ddd85..2bafed4e993 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5787,6 +5787,8 @@ struct GTY((for_user)) spec_entry
 
 extern int current_class_depth;
 
+void adjust_clone_attributes (tree fn, tree clone, tree name, bool = false);
+
 /* in decl.cc */
 
 /* An array of static vars & fns.  */
@@ -7022,6 +7024,7 @@ extern void do_push_parm_decls			(tree, tree, tree *);
 extern tree do_aggregate_paren_init		(tree, tree);
 
 /* in decl2.cc */
+extern void update_sym_alias_interface		(tree);
 extern void record_mangling			(tree, bool);
 extern void overwrite_mangling			(tree, tree);
 extern void note_mangling_alias			(tree, tree);
@@ -7596,6 +7599,7 @@ extern bool emit_tinfo_decl			(tree);
 extern unsigned get_pseudo_tinfo_index		(tree);
 extern tree get_pseudo_tinfo_type		(unsigned);
 extern tree build_if_nonnull			(tree, tree, tsubst_flags_t);
+extern void update_tinfo_sym_alias		(tree);
 
 /* in search.cc */
 extern tree get_parent_with_private_access 	(tree decl, tree binfo);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 2f2dbb8d107..5636d694b68 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -3237,6 +3237,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
 	      && TREE_STATIC (olddecl))))
     make_decl_rtl (olddecl);
 
+  symtab_node::remap_sym_alias_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.
@@ -3260,6 +3262,7 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
       FOR_EACH_CLONE (clone, olddecl)
 	{
 	  DECL_ATTRIBUTES (clone) = DECL_ATTRIBUTES (olddecl);
+	  adjust_clone_attributes (olddecl, clone, DECL_NAME (clone));
 	  DECL_PRESERVE_P (clone) |= DECL_PRESERVE_P (olddecl);
 	}
     }
@@ -10789,6 +10792,7 @@ grokfndecl (tree ctype,
     {
       cplus_decl_attributes (&decl, *attrlist, 0);
       *attrlist = NULL_TREE;
+      create_sym_alias_decls (decl);
     }
 
   if (DECL_HAS_CONTRACTS_P (decl))
diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
index 9e666e5eece..c7f009beaf0 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -1844,6 +1844,9 @@ cplus_decl_attributes (tree *decl, tree attributes, int flags)
   if (late_attrs)
     save_template_attributes (late_attrs, decl, flags);
 
+  if (TYPE_P (*decl) && attributes)
+    update_tinfo_sym_alias (*decl);
+
   /* Propagate deprecation out to the template.  */
   if (TREE_DEPRECATED (*decl))
     if (tree ti = get_template_info (*decl))
@@ -2200,6 +2203,47 @@ 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 sym aliases.  */
+
+void
+update_sym_alias_interface (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.  */
 
@@ -3007,6 +3051,8 @@ determine_visibility (tree decl)
        translation unit, we can make the type internal.  */
     constrain_visibility (decl, VISIBILITY_ANON, false);
 
+  update_sym_alias_interface (decl);
+
   /* If visibility changed and DECL already has DECL_RTL, ensure
      symbol flags are updated.  */
   if ((DECL_VISIBILITY (decl) != orig_visibility
@@ -3269,6 +3315,8 @@ tentative_decl_linkage (tree decl)
       else if (VAR_P (decl))
 	maybe_commonize_var (decl);
     }
+
+  update_sym_alias_interface (decl);
 }
 
 /* DECL is a FUNCTION_DECL or VAR_DECL.  If the object file linkage
@@ -3503,6 +3551,8 @@ import_export_decl (tree decl)
     }
 
   DECL_INTERFACE_KNOWN (decl) = 1;
+
+  update_sym_alias_interface (decl);
 }
 
 /* Return an expression that performs the destruction of DECL, which
diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc
index d19ea5d121c..f877780b2ac 100644
--- a/gcc/cp/name-lookup.cc
+++ b/gcc/cp/name-lookup.cc
@@ -22,9 +22,11 @@ along with GCC; see the file COPYING3.  If not see
 #define INCLUDE_MEMORY
 #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"
@@ -3479,6 +3481,15 @@ push_local_extern_decl_alias (tree decl)
 	  /* Adjust visibility.  */
 	  determine_visibility (alias);
 	}
+      else if (DECL_P (alias))
+	DECL_ATTRIBUTES (alias)
+	  = targetm.merge_decl_attributes (alias, decl);
+      if (DECL_P (alias))
+	{
+	  symtab_node::remap_sym_alias_target (decl, alias);
+	  DECL_ATTRIBUTES (decl)
+	    = remove_attribute ("sym_alias", DECL_ATTRIBUTES (alias));
+	}
     }
 
   retrofit_lang_decl (decl);
diff --git a/gcc/cp/optimize.cc b/gcc/cp/optimize.cc
index 9e8926e4cc6..4a2e8cb4354 100644
--- a/gcc/cp/optimize.cc
+++ b/gcc/cp/optimize.cc
@@ -528,9 +528,12 @@ maybe_clone_body (tree fn)
       DECL_VISIBILITY_SPECIFIED (clone) = DECL_VISIBILITY_SPECIFIED (fn);
       DECL_DLLIMPORT_P (clone) = DECL_DLLIMPORT_P (fn);
       DECL_ATTRIBUTES (clone) = clone_attrs (DECL_ATTRIBUTES (fn));
+      adjust_clone_attributes (fn, clone, DECL_NAME (clone), true);
       DECL_DISREGARD_INLINE_LIMITS (clone) = DECL_DISREGARD_INLINE_LIMITS (fn);
       set_decl_section_name (clone, fn);
 
+      update_sym_alias_interface (clone);
+
       /* Adjust the parameter names and locations.  */
       parm = DECL_ARGUMENTS (fn);
       clone_parm = DECL_ARGUMENTS (clone);
diff --git a/gcc/cp/rtti.cc b/gcc/cp/rtti.cc
index 7878929c246..4bb7b8c8c6e 100644
--- a/gcc/cp/rtti.cc
+++ b/gcc/cp/rtti.cc
@@ -28,8 +28,10 @@ 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"
+#include "cgraph.h"
 
 /* C++ returns type information to the user in struct type_info
    objects. We also use type information to implement dynamic_cast and
@@ -479,8 +481,13 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
 	  = build_tree_list (get_identifier ("non overlapping"),
 			     NULL_TREE);
       else
+	/* Share the non overlapping attribute, without assuming it's
+	   the only attribute, but assuming it's the last if it's
+	   present.  There may be sym aliases too, and those are not
+	   to be shared.  */
 	DECL_ATTRIBUTES (d)
-	  = DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]);
+	  = lookup_attribute ("non overlapping",
+			      DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]));
 
       /* Mark the variable as undefined -- but remember that we can
 	 define it later if we need to do so.  */
@@ -492,6 +499,16 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
       if (CLASS_TYPE_P (type))
 	CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type)) = d;
 
+      /* Copy sym alias attributes from the type to the rtti obj decl.  */
+      tree *attrs = &DECL_ATTRIBUTES (d);
+      FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+	{
+	  tree attr = tree_cons (TREE_PURPOSE (sym), TREE_VALUE (sym), *attrs);
+	  *attrs = attr;
+	  attrs = &TREE_CHAIN (attr);
+	}
+      create_sym_alias_decls (d);
+
       /* Add decl to the global array of tinfo decls.  */
       vec_safe_push (unemitted_tinfo_decls, d);
     }
@@ -499,6 +516,58 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
   return d;
 }
 
+/* After modifying the attributes of TYPE, check whether tinfo was
+   already created and, if so, add to it any sym alias attributes
+   that were not already present.  */
+
+void
+update_tinfo_sym_alias (tree type)
+{
+  if (!TYPE_SIZE (type) || !CLASS_TYPE_P (type))
+    return;
+
+  tree d = CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type));
+  if (!d)
+    return;
+
+  bool first = true;
+  symtab_node *node = NULL;
+
+  tree *attrs = &DECL_ATTRIBUTES (d);
+  FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (d_sym, *attrs)
+	if (TREE_VALUE (sym) == TREE_VALUE (d_sym))
+	  {
+	    found = true;
+	    break;
+	  }
+
+      if (found)
+	continue;
+
+      tree attr = tree_cons (TREE_PURPOSE (sym),
+			     TREE_VALUE (sym),
+			     *attrs);
+      *attrs = attr;
+      attrs = &TREE_CHAIN (attr);
+
+      if (first)
+	{
+	  first = false;
+	  node = symtab_node::get (d);
+	}
+
+      if (!node)
+	continue;
+
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+      create_sym_alias_decl (d, id);
+    }
+}
+
 /* Return the type_info object for TYPE.  */
 
 tree
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index e6de0815846..5dcdec76c1e 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -4141,6 +4141,43 @@ Function Attributes}, @ref{PowerPC Function Attributes},
 @ref{Nios II Function Attributes}, and @ref{S/390 Function Attributes}
 for details.
 
+@cindex @code{sym_alias} function attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} attribute causes @var{name} to be output as an
+alias to the function, with the same linkage and visibility as the
+function, when the function definition is output, provided that the
+function could be an alias target.  For instance,
+
+@smallexample
+void f (uint64_t) __attribute__ ((__sym_alias__ ("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},
+with the same linkage and visibility.  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.
+
+Aliases introduced with this attribute, such as @samp{f_u64} in the
+example above, are assembly symbol names: they do not undergo C++ name
+mangling, and are not made visible in any scope in the source language.
+They can, however, can be named as alias targets.
+
+This attribute requires assembler and object file support for aliases,
+and may not be available on all targets.
+
 @cindex @code{symver} function attribute
 @item symver ("@var{name2}@@@var{nodename}")
 On ELF targets this attribute creates a symbol version.  The @var{name2} part
@@ -8069,6 +8106,10 @@ will be placed in new, unique sections.
 
 This additional functionality requires Binutils version 2.36 or later.
 
+@cindex @code{sym_alias} variable attribute
+@item sym_alias ("@var{name}")
+See @pxref{Common Function Attributes}.
+
 @cindex @code{uninitialized} variable attribute
 @item uninitialized
 This attribute, attached to a variable with automatic storage, means that
@@ -9164,6 +9205,21 @@ is not supported; that is to say, if a given scalar object can be accessed
 through distinct types that assign a different storage order to it, then the
 behavior is undefined.
 
+@cindex @code{sym_alias} type attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} type 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__ ((__sym_alias__ ("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.
+
 @cindex @code{transparent_union} type attribute
 @item transparent_union
 
diff --git a/gcc/symtab.cc b/gcc/symtab.cc
index 0470509a98d..165a5177ca1 100644
--- a/gcc/symtab.cc
+++ b/gcc/symtab.cc
@@ -1943,6 +1943,40 @@ symtab_node::noninterposable_alias (symtab_node *node, void *data)
   return false;
 }
 
+/* Remap sym alias nodes recorded as aliasing REPLACED to alias
+   REPLACEMENT instead.  */
+
+void
+symtab_node::remap_sym_alias_target (tree replaced, tree replacement)
+{
+  if (!decl_in_symtab_p (replacement)
+      || !symtab_node::get (replacement))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (replaced))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      symtab_node *sym_node = symtab_node::get_for_asmname (id);
+
+      if (!sym_node)
+	{
+	  create_sym_alias_decl (replacement, id);
+	  continue;
+	}
+
+      gcc_assert (!sym_node->analyzed);
+      if (sym_node->alias_target != replaced)
+	continue;
+
+      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-sym-alias-neg.c b/gcc/testsuite/c-c++-common/attr-sym-alias-neg.c
new file mode 100644
index 00000000000..28be8e3b0e6
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/attr-sym-alias-neg.c
@@ -0,0 +1,80 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int f () {
+  int i __attribute__ ((sym_alias ("f_i"))); /* { dg-warning "ignored" } */
+  /* ??? X cannot be an alias target; should this be flagged? ... */
+  static int x __attribute__ ((sym_alias ("f_x")));
+  static int sx __attribute__ ((alias ("f_x"))); /* { dg-warning "ignored" } */
+  extern int xx __attribute__ ((alias ("f_x"))); /* { dg-warning "ignored" } */
+  return i;
+}
+
+/* ??? ... or should XR be accepted?  */
+extern int xr __attribute__ ((alias ("f_x"))); /* { dg-error "undefined" } */
+int dr __attribute__ ((alias ("f_x"))); /* { dg-error "defined both" } */
+
+
+struct s {
+  int j __attribute__ ((sym_alias ("s_j"))); /* { dg-warning "ignored" } */
+};
+
+
+int j __attribute__ ((sym_alias ("J_var")));
+void __attribute__ ((alias ("J_var")))
+j_fn (); /* { dg-error "between function and variable" } */
+
+
+void __attribute__ ((sym_alias ("K_fn")))
+k () {
+}
+extern int __attribute__ ((alias ("K_fn")))
+k_var; /* { dg-error "between function and variable" } */
+
+
+int l __attribute__ ((sym_alias ("L_fn")));
+
+/* These should be detected and reported, not because the names clash at the
+   source level, but because the asm symbols do.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+void
+L_fn () /* { dg-error "duplicate" "" { xfail *-*-* } } */
+{
+}
+
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __attribute__ ((sym_alias ("M_var")))
+m ()
+{
+}
+
+int M_var; /* { dg-error "duplicate" "" { xfail *-*-* } } */
+
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __attribute__ ((sym_alias ("N_sym")))
+n_fn ()
+{
+}
+
+int __attribute__ ((sym_alias ("N_sym")))
+n_var; /* { dg-error "duplicate" } */
+
+
+int __attribute__ ((sym_alias ("O_sym")))
+o_var;
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __attribute__ ((sym_alias ("O_sym")))
+o_fn () /* { dg-error "duplicate" } */
+{
+}
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-1.c b/gcc/testsuite/c-c++-common/goacc/declare-1.c
index 46ee01b6759..f2842893318 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-1.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-1.c
@@ -113,11 +113,11 @@ f_2 (void)
   int va3;
 #pragma acc declare device_resident(va3)
 
-#ifndef __cplusplus
+#if 0
   /* TODO PR90868
 
-     C: "error: variable '[...]' used more than once with '#pragma acc declare'".  */
-#else
+     "error: variable '[...]' used more than once with '#pragma acc declare'".  */
+
   extern int ve0;
 #pragma acc declare create(ve0)
 
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-2.c b/gcc/testsuite/c-c++-common/goacc/declare-2.c
index e2e22be57e9..aec59b69754 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-2.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-2.c
@@ -137,25 +137,25 @@ void
 f_pr90868_2 (void)
 {
   extern int we0;
-#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" } */
 
   extern int we1;
-#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" } */
 
   extern int *we2;
-#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" } */
 
   extern int we3;
-#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" } */
 
   extern int we4;
-#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" } */
 
   extern int we5;
-#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" } */
  
   extern int we6;
-#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" } */
 }
 
 
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
new file mode 100644
index 00000000000..61af50cb552
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
@@ -0,0 +1,39 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+int var_a = 1;
+
+void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+
+void
+foo_a ()
+{
+}
+
+
+int var_b;
+extern int var_b __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+
+void
+foo_b ()
+{
+}
+
+void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+
+
+int var_c __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+
+void __attribute__ ((__sym_alias__ ("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-sym-alias-2.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
new file mode 100644
index 00000000000..6f0d55ca42c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+struct s
+{
+  int mem __attribute__ ((__sym_alias__ ("MEMFOO"))); /* { dg-warning "attribute ignored" } */
+};
+
+void foo()
+{
+  extern void bar () __attribute__ ((__sym_alias__ ("FOOBAR")));
+  int var __attribute__ ((__sym_alias__ ("FOOVAR"))); /* { dg-warning "attribute ignored" } */
+}
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
new file mode 100644
index 00000000000..0052485a30a
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
@@ -0,0 +1,41 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int var_a = 1;
+
+void
+foo_a ()
+{
+  extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+}
+
+#if 0 // __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__ ((__sym_alias__ ("FOOVAR_B")));
+}
+
+int var_b;
+
+void __attribute__ ((__sym_alias__ ("FOOBAR_C")))
+foo_c ()
+{
+  void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+  /* Another sym for var_b.  */
+  extern int var_b __attribute__ ((__sym_alias__ ("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-sym-alias-4.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
new file mode 100644
index 00000000000..2beafa46379
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-require-alias "" } */
+
+int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A"))) = 42;
+
+int __attribute__ ((__sym_alias__ ("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/c-c++-common/torture/attr-sym-alias-5.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
new file mode 100644
index 00000000000..bee90136daf
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
@@ -0,0 +1,54 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+/* { dg-require-visibility "" } */
+
+int __attribute__ ((sym_alias ("A_hid"), visibility ("hidden"))) a;
+
+extern int __attribute__ ((sym_alias ("B_prt"))) b;
+
+int b __attribute__ ((visibility ("protected")));
+
+extern int __attribute__ ((sym_alias ("C_ntr"))) c;
+
+extern int c __attribute__ ((visibility ("internal")));
+
+int c;
+
+static int d __attribute__ ((sym_alias ("D_stt")));
+
+
+extern void __attribute__ ((sym_alias ("F_hid"), visibility ("hidden")))
+f ();
+
+void
+f () {
+}
+
+extern void __attribute__ ((sym_alias ("G_prt")))
+g ();
+
+void __attribute__ ((visibility ("protected")))
+g () {
+}
+
+extern void __attribute__ ((sym_alias ("H_ntr")))
+h ();
+
+void __attribute__ ((visibility ("internal")))
+h ();
+
+void h () {
+}
+
+static void __attribute__ ((sym_alias ("I_stt")))
+i () {
+}
+
+/* { dg-final { scan-assembler {hidden[^\n]*A_hid} } } */
+/* { dg-final { scan-assembler {protected[^\n]*B_prt} } } */
+/* { dg-final { scan-assembler {internal[^\n]*C_ntr} } } */
+/* { dg-final { scan-assembler-not {glob[^\n]*D_stt} } } */
+/* { dg-final { scan-assembler {hidden[^\n]*F_hid} } } */
+/* { dg-final { scan-assembler {protected[^\n]*G_prt} } } */
+/* { dg-final { scan-assembler {internal[^\n]*H_ntr} } } */
+/* { dg-final { scan-assembler-not {glob[^\n]*I_stt} } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
new file mode 100644
index 00000000000..579eda1a473
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
@@ -0,0 +1,72 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+class __attribute__ ((__sym_alias__ ("FOOCLS_A"),
+		      __sym_alias__ ("FOOCLS_A_Dupe"))) foo {
+  static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  void __attribute__ ((__sym_alias__ ("FOOBAR_A"))) bar ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+int foo::var = 1;
+
+foo::foo () {}
+
+void foo::bar () {}
+
+namespace b {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_B"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_B"))) foo ();
+    void __attribute__ ((__sym_alias__ ("FOOBAR_B"))) bar () {}
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_B"))) ~foo() {}
+  };
+
+  int foo::var = 2;
+
+  foo::foo () {
+    void (foo::*pbar)() = &foo::bar;
+  }
+}
+
+namespace c {
+  namespace cd {
+    class __attribute__ ((__sym_alias__ ("FOOCLS_C"))) foo {
+      static int var __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+      __attribute__ ((__sym_alias__ ("FOOCTR_C"))) foo () {
+	void (foo::*pbar)() = &foo::bar;
+      }
+      void __attribute__ ((__sym_alias__ ("FOOBAR_C"))) bar () {}
+      virtual __attribute__ ((__sym_alias__ ("FOODTR_C"))) ~foo() {}
+    };
+
+    int foo::var = 3;
+  }
+}
+
+/* { dg-final { scan-assembler "FOOCLS_A" } } */
+/* { dg-final { scan-assembler "FOOCLS_A_Dupe" } } */
+/* { 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-sym-alias-2.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
new file mode 100644
index 00000000000..6facfcc2cd9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+namespace {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_A"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo ();
+    void __attribute__ ((__sym_alias__ ("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-sym-alias-3.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
new file mode 100644
index 00000000000..42c7288ad4a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
@@ -0,0 +1,83 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+// sym can be applied to template function explicit instantiations.
+
+template <typename T>
+void
+fn(T) {
+};
+
+template void __attribute__ ((__sym_alias__ ("FOOFUN_UINT"))) fn<>(unsigned int);
+template void __attribute__ ((__sym_alias__ ("FOOFUN_LONG"))) fn<>(long);
+
+template<> void __attribute__ ((__sym_alias__ ("FOOFUN_CHAR"))) fn<>(char) {}
+
+
+template <typename T = void>
+struct
+foo {
+  virtual ~foo() {}
+
+  virtual void virtfun() {}
+
+  static void stfun() {}
+  void inlfun() {}
+};
+
+// Explicitly instantiate members before the enclosing class.
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_CHAR_VIRT"))) foo<char>::virtfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_CHAR_TI"))) foo<char>;
+
+// Though they're only output if the enclosing class is.
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_LONG_VIRT"))) foo<long>::virtfun();
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_LONG_TI_X"))) foo<long>;
+
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_VOID_ST"))) foo<void>::stfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_VOID_TI"))) foo<>;
+
+
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_X"))) foo<short>;
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_ST"))) foo<short>::stfun();
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_INL"))) foo<short>::inlfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_D"))) foo<short>;
+
+// Explicit specializations work too.
+
+template <>
+struct  __attribute__ ((__sym_alias__ ("FOOCLS_INT_TI")))
+foo<int>
+{
+  virtual ~foo() {}
+  virtual void __attribute__ ((__sym_alias__ ("FOOCLS_INT_VIRT"))) virtfun() {}
+};
+
+/* { dg-final { scan-assembler "FOOFUN_UINT" } } */
+/* { dg-final { scan-assembler "FOOFUN_LONG" } } */
+/* { dg-final { scan-assembler "FOOFUN_CHAR" } } */
+
+/* { dg-final { scan-assembler "FOOCLS_VOID_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_VOID_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_X" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_INL" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_D" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_TI_X" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_VIRT" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
new file mode 100644
index 00000000000..59b779de35d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+template <typename T = void>
+class
+__attribute__ ((__sym_alias__ ("FOOCLS")))
+foo // { dg-error "duplicate|already" }
+{
+  virtual ~foo() {}
+
+  template <typename U>
+  void
+    __attribute__ ((__sym_alias__ ("FOOTMF")))
+    tmemfun () {} // { dg-error "duplicate|already" }
+};
+
+template <typename T>
+void
+__attribute__ ((__sym_alias__ ("FOOTFN")))
+fn(T) { // { dg-error "duplicate|already" }
+};
+
+template class foo<>;
+template class foo<int>;
+template void foo<>::tmemfun<void>();
+template void foo<int>::tmemfun<void>();
+template void fn<>(int);
+template void fn<>(long);
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
new file mode 100644
index 00000000000..45d59b953c3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
@@ -0,0 +1,14 @@
+/* { dg-do compile { target c++11 } } */
+/* { dg-require-alias "" } */
+
+struct foo {
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+foo::foo () {}
+
+// Make sure the inherited cdtors don't duplicate the syms.
+struct bar : foo {
+  using foo::foo;
+};
diff --git a/gcc/varpool.cc b/gcc/varpool.cc
index e7b51b15e4a..33649906d1d 100644
--- a/gcc/varpool.cc
+++ b/gcc/varpool.cc
@@ -163,6 +163,9 @@ varpool_node::get_create (tree decl)
     }
 
   node->register_symbol ();
+
+  create_sym_alias_decls (decl);
+
   return node;
 }

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [gcc(refs/users/aoliva/heads/testme)] Introduce attribute sym_alias
@ 2023-11-30 12:30 Alexandre Oliva
  0 siblings, 0 replies; 33+ messages in thread
From: Alexandre Oliva @ 2023-11-30 12:30 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:7609fe293cbe6b1a125463672b7293e8b3de08f4

commit 7609fe293cbe6b1a125463672b7293e8b3de08f4
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Fri Jul 14 03:45:14 2023 -0300

    Introduce attribute sym_alias
    
    This patch introduces an attribute to add extra asm names (aliases)
    for a decl 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 class types, namespace-scoped
    variables and functions, static data members, member functions,
    explicit instantiations and specializations of template functions,
    members and classes.
    
    When applied to constructors or destructor, additional sym aliases
    with _Base and _Del suffixes are defined for variants other than
    complete-object ones.  This changes the assumption that clones always
    carry the same attributes as their abstract declarations, so there is
    now a function to adjust them.
    
    C++ also had a bug in which attributes from local extern declarations
    failed to be propagated to a preexisting corresponding
    namespace-scoped decl.  I've fixed that, and adjusted acc tests that
    distinguished between C and C++ in this regard.
    
    Applying the attribute to class types is only valid in C++, and the
    effect is to attach the alias to the RTTI object associated with the
    class type.
    
    for  gcc/ChangeLog
    
            * attribs.cc: Include cgraph.h.
            (decl_attributes): Allow late introduction of sym_alias in
            types.
            (create_sym_alias_decl, create_sym_alias_decls): New.
            * attribs.h: Declare them.
            (FOR_EACH_SYM_ALIAS): New macro.
            * cgraph.cc (cgraph_node::create): Create sym_alias decls.
            * varpool.cc (varpool_node::get_create): Create sym_alias
            decls.
            * cgraph.h (symtab_node::remap_sym_alias_target): New.
            * symtab.cc (symtab_node::remap_sym_alias_target): Define.
            * cgraphunit.cc (cgraph_node::analyze): Create alias_target
            node if needed.
            (analyze_functions): Fixup visibility of implicit alias only
            after its node is analyzed.
            * doc/extend.texi (sym_alias): Document for variables,
            functions and types.
    
    for  gcc/ada/ChangeLog
    
            * doc/gnat_rm/interfacing_to_other_languages.rst: Mention
            attribute sym_alias to give RTTI symbols mnemonic names.
            * doc/gnat_ugn/the_gnat_compilation_model.rst: Mention
            aliases.  Fix incorrect ref to C1 ctor variant.
    
    for  gcc/c-family/ChangeLog
    
            * c-ada-spec.cc (pp_asm_name): Use first sym_alias if
            available.
            * c-attribs.cc (handle_sym_alias_attribute): New.
            (c_common_attribute_table): Add sym_alias.
            (handle_copy_attribute): Do not copy sym_alias attribute.
    
    for  gcc/c/ChangeLog
    
            * c-decl.cc (duplicate_decls): Remap sym_alias target.
    
    for  gcc/cp/ChangeLog
    
            * class.cc (adjust_clone_attributes): New.
            (copy_fndecl_with_name, build_clone): Call it.
            * cp-tree.h (adjust_clone_attributes): Declare.
            (update_sym_alias_interface): Declare.
            (update_tinfo_sym_alias): Declare.
            * decl.cc (duplicate_decls): Remap sym_alias target.
            Adjust clone attributes.
            (grokfndecl): Tentatively create sym_alias decls after
            adding attributes in e.g. a template member function explicit
            instantiation.
            * decl2.cc (cplus_decl_attributes): Update tinfo sym_alias.
            (copy_interface, update_sym_alias_interface): New.
            (determine_visibility): Update sym_alias interface.
            (tentative_decl_linkage, import_export_decl): Likewise.
            * name-lookup.cc: Include target.h and cgraph.h.
            (push_local_extern_decl_alias): Merge attributes with
            namespace-scoped decl, and drop duplicate sym_alias.
            * optimize.cc (maybe_clone_body): Re-adjust attributes after
            cloning them.  Update sym_alias interface.
            * rtti.cc: Include attribs.h and cgraph.h.
            (get_tinfo_decl): Copy sym_alias attributes from type to tinfo
            decl.  Create sym_alias decls.
            (update_tinfo_sym_alias): New.
    
    for  gcc/testsuite/ChangeLog
    
            * c-c++-common/goacc/declare-1.c: Adjust.
            * c-c++-common/goacc/declare-2.c: Adjust.
            * c-c++-common/torture/attr-sym-alias-1.c: New.
            * c-c++-common/torture/attr-sym-alias-2.c: New.
            * c-c++-common/torture/attr-sym-alias-3.c: New.
            * c-c++-common/torture/attr-sym-alias-4.c: New.
            * c-c++-common/torture/attr-sym-alias-5.c: New.
            * c-c++-common/attr-sym-alias-neg.c: New.
            * g++.dg/torture/attr-sym-alias-1.C: New.
            * g++.dg/torture/attr-sym-alias-2.C: New.
            * g++.dg/torture/attr-sym-alias-3.C: New.
            * g++.dg/torture/attr-sym-alias-4.C: New.
            * g++.dg/torture/attr-sym-alias-5.C: New.

Diff:
---
 .../doc/gnat_rm/interfacing_to_other_languages.rst |  6 ++
 .../doc/gnat_ugn/the_gnat_compilation_model.rst    | 10 ++-
 gcc/attribs.cc                                     | 68 +++++++++++++++++-
 gcc/attribs.h                                      |  7 ++
 gcc/c-family/c-ada-spec.cc                         |  7 ++
 gcc/c-family/c-attribs.cc                          | 33 ++++++++-
 gcc/c/c-decl.cc                                    |  2 +
 gcc/cgraph.cc                                      |  2 +
 gcc/cgraph.h                                       |  4 ++
 gcc/cgraphunit.cc                                  |  2 +-
 gcc/cp/class.cc                                    | 64 +++++++++++++++++
 gcc/cp/cp-tree.h                                   |  4 ++
 gcc/cp/decl.cc                                     |  4 ++
 gcc/cp/decl2.cc                                    | 50 +++++++++++++
 gcc/cp/name-lookup.cc                              | 11 +++
 gcc/cp/optimize.cc                                 |  3 +
 gcc/cp/rtti.cc                                     | 71 +++++++++++++++++-
 gcc/doc/extend.texi                                | 56 +++++++++++++++
 gcc/symtab.cc                                      | 36 ++++++++++
 gcc/testsuite/c-c++-common/attr-sym-alias-neg.c    | 80 +++++++++++++++++++++
 gcc/testsuite/c-c++-common/goacc/declare-1.c       |  6 +-
 gcc/testsuite/c-c++-common/goacc/declare-2.c       | 14 ++--
 .../c-c++-common/torture/attr-sym-alias-1.c        | 39 ++++++++++
 .../c-c++-common/torture/attr-sym-alias-2.c        | 13 ++++
 .../c-c++-common/torture/attr-sym-alias-3.c        | 41 +++++++++++
 .../c-c++-common/torture/attr-sym-alias-4.c        | 28 ++++++++
 .../c-c++-common/torture/attr-sym-alias-5.c        | 35 +++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C    | 72 +++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C    | 26 +++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C    | 83 ++++++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C    | 28 ++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C    | 14 ++++
 gcc/varpool.cc                                     |  3 +
 33 files changed, 903 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 ad0be511d48..b8de16ebf7c 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 ``sym_alias`` 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 fd15459203a..20045698962 100644
--- a/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
+++ b/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
@@ -4274,6 +4274,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__ ((__sym_alias__ ("Ctor_For_Animal")))
         Animal() {Age_Count = 0;};
       private:
         int Age_Count;
@@ -4306,6 +4307,7 @@ both Carnivore and Domestic, that is:
         virtual int  Number_Of_Teeth ();
         virtual void Set_Owner (char* Name);
 
+        __attribute__ ((__sym_alias__ ("Ctor_For_Dog"))) // mnemonic alias
         Dog(); // Constructor
       private:
         int  Tooth_Count;
@@ -4344,7 +4346,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;
@@ -4360,7 +4363,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,7 +4385,8 @@ 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.
+mangled name (or an alias) 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.cc b/gcc/attribs.cc
index c7209c26acc..6708d02e1b3 100644
--- a/gcc/attribs.cc
+++ b/gcc/attribs.cc
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "target.h"
 #include "tree.h"
+#include "cgraph.h"
 #include "stringpool.h"
 #include "diagnostic-core.h"
 #include "attribs.h"
@@ -825,7 +826,8 @@ decl_attributes (tree *node, tree attributes, int flags,
 
       if (TYPE_P (*anode)
 	  && (flags & (int) ATTR_FLAG_TYPE_IN_PLACE)
-	  && COMPLETE_TYPE_P (*anode))
+	  && COMPLETE_TYPE_P (*anode)
+	  && !is_attribute_p ("sym_alias", name))
 	{
 	  warning (OPT_Wattributes, "type attributes ignored after type is already defined");
 	  continue;
@@ -2640,6 +2642,70 @@ attr_access::array_as_string (tree type) const
   return typstr;
 }
 
+/* Create a sym attribute for DECL to be visible with linkage name ID.  */
+
+tree
+create_sym_alias_decl (tree decl, tree id)
+{
+  const char *attr_str = "sym_alias";
+
+  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;
+
+      tree attr_name = get_identifier (attr_str);
+      error_at (DECL_SOURCE_LOCATION (decl),
+		"duplicate symbol name %qE in %qE attribute of %qD",
+		id, attr_name, decl);
+      inform (DECL_SOURCE_LOCATION (sym_node->decl),
+	      "already used by %qD", sym_node->decl);
+    }
+
+  tree clone = copy_node (decl);
+  DECL_ATTRIBUTES (clone) = remove_attribute (attr_str,
+					      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 decls for all sym aliases requested in DECL's attributes.  */
+
+void
+create_sym_alias_decls (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl)
+      || DECL_ABSTRACT_P (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      create_sym_alias_decl (decl, id);
+    }
+}
+
 #if CHECKING_P
 
 namespace selftest
diff --git a/gcc/attribs.h b/gcc/attribs.h
index 84a43658a70..156f2c20588 100644
--- a/gcc/attribs.h
+++ b/gcc/attribs.h
@@ -398,4 +398,11 @@ extern void init_attr_rdwr_indices (rdwr_map *, tree);
 extern attr_access *get_parm_access (rdwr_map &, tree,
 				     tree = current_function_decl);
 
+extern tree create_sym_alias_decl (tree, tree);
+extern void create_sym_alias_decls (tree);
+
+#define FOR_EACH_SYM_ALIAS(sym, attrs)					\
+  for (tree sym = lookup_attribute ("sym_alias", (attrs));		\
+       sym; sym = lookup_attribute ("sym_alias", TREE_CHAIN (sym)))
+
 #endif // GCC_ATTRIBS_H
diff --git a/gcc/c-family/c-ada-spec.cc b/gcc/c-family/c-ada-spec.cc
index 050994d8416..5042b9cfecd 100644
--- a/gcc/c-family/c-ada-spec.cc
+++ b/gcc/c-family/c-ada-spec.cc
@@ -1431,6 +1431,13 @@ pp_ada_tree_identifier (pretty_printer *buffer, tree node, tree type,
 static void
 pp_asm_name (pretty_printer *buffer, tree t)
 {
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (t))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.cc b/gcc/c-family/c-attribs.cc
index 2b20e58c922..392437eab3b 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -108,7 +108,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_sym_alias_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,
@@ -388,6 +389,8 @@ const struct attribute_spec c_common_attribute_table[] =
 			      handle_alias_attribute, NULL },
   { "weakref",                0, 1, true,  false, false, false,
 			      handle_weakref_attribute, NULL },
+  { "sym_alias",              1, 1, false,  false, false, false,
+			      handle_sym_alias_attribute, NULL },
   { "no_instrument_function", 0, 0, true,  false, false, false,
 			      handle_no_instrument_function_attribute,
 			      NULL },
@@ -3002,7 +3005,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
@@ -3012,7 +3015,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
@@ -3022,6 +3025,29 @@ handle_alias_attribute (tree *node, tree name, tree args,
   return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs);
 }
 
+/* Handle a "sym_alias" attribute; arguments as in struct
+   attribute_spec.handler.  */
+
+static tree
+handle_sym_alias_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 (decl_in_symtab_p (node))
+    *no_add_attrs = false;
+  else if (TYPE_P (node) && c_dialect_cxx ())
+    *no_add_attrs = false;
+  else
+    return error_mark_node;
+
+  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.  */
 
@@ -3155,6 +3181,7 @@ handle_copy_attribute (tree *node, tree name, tree args,
 	      || is_attribute_p ("visibility", atname)
 	      || is_attribute_p ("weak", atname)
 	      || is_attribute_p ("weakref", atname)
+	      || is_attribute_p ("sym_alias", atname)
 	      || is_attribute_p ("target_clones", atname))
 	    continue;
 
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index cf1df82c0f4..e5a1ee54cb3 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -3119,6 +3119,8 @@ duplicate_decls (tree newdecl, tree olddecl)
 
   merge_decls (newdecl, olddecl, newtype, oldtype);
 
+  symtab_node::remap_sym_alias_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.cc b/gcc/cgraph.cc
index f93259a8c70..0bef2179e89 100644
--- a/gcc/cgraph.cc
+++ b/gcc/cgraph.cc
@@ -523,6 +523,8 @@ cgraph_node::create (tree decl)
   node->register_symbol ();
   maybe_record_nested_function (node);
 
+  create_sym_alias_decls (decl);
+
   return node;
 }
 
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index cfdd9f693a8..cb5ac01f7e2 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -327,6 +327,10 @@ public:
   /* Return DECL that alias is aliasing.  */
   inline tree get_alias_target_tree ();
 
+  /* Remap sym alias nodes recorded as aliasing REPLACED to alias REPLACEMENT
+     instead.  */
+  static void remap_sym_alias_target (tree replaced, tree replacement);
+
   /* Set section for symbol and its aliases.  */
   void set_section (const char *section);
 
diff --git a/gcc/cgraphunit.cc b/gcc/cgraphunit.cc
index bccd2f2abb5..eb2d05094e9 100644
--- a/gcc/cgraphunit.cc
+++ b/gcc/cgraphunit.cc
@@ -1175,7 +1175,7 @@ analyze_functions (bool first_time)
      C++ FE is confused about the COMDAT groups being right.  */
   if (symtab->cpp_implicit_aliases_done)
     FOR_EACH_SYMBOL (node)
-      if (node->cpp_implicit_alias)
+      if (node->cpp_implicit_alias && node->analyzed)
 	  node->fixup_same_cpp_alias_visibility (node->get_alias_target ());
   build_type_inheritance_graph ();
 
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index 6fdb56abfb9..fa8fc63db82 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -4896,6 +4896,68 @@ check_methods (tree t)
     }
 }
 
+/* Adjust sym alias name for CLONE, cloned from FN and named NAME,
+   if it is a cdtor, and drop the sym alias from other clones.  */
+
+void
+adjust_clone_attributes (tree fn, tree clone, tree name, bool skip_copy_p)
+{
+  if (IDENTIFIER_CDTOR_P (name))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	{
+	  found = true;
+	  break;
+	}
+
+      if (found
+	  && (name == complete_ctor_identifier
+	      || name == complete_dtor_identifier))
+	{
+	  /* Reuse the sym alias decls created for the primary cdtor
+	     decl.  */
+	  symtab_node::remap_sym_alias_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);
+
+	  if (!skip_copy_p)
+	    DECL_ATTRIBUTES (clone) = copy_list (DECL_ATTRIBUTES (clone));
+
+	  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	    {
+	      /* We need to copy this even with skip_copy_p, because
+		 even then copying was shallow.  */
+	      TREE_VALUE (sym) = copy_list (TREE_VALUE (sym));
+	      /* Append suf to the sym alias name.  */
+	      tree str = TREE_VALUE (TREE_VALUE (sym));
+	      char *symname = concat (TREE_STRING_POINTER (str), suf, NULL);
+	      str = build_string (TREE_STRING_LENGTH (str) + xlen, symname);
+	      TREE_VALUE (TREE_VALUE (sym)) = str;
+	      free (symname);
+	    }
+
+	  if (symtab_node::get (clone))
+	    create_sym_alias_decls (clone);
+	}
+    }
+  else
+    DECL_ATTRIBUTES (clone)
+      = remove_attribute ("sym_alias", DECL_ATTRIBUTES (clone));
+}
+
 /* FN is constructor, destructor or operator function.  Clone the
    declaration to create a NAME'd variant.  NEED_VTT_PARM_P and
    OMIT_INHERITED_PARMS_P are relevant if it's a cdtor.  */
@@ -5065,6 +5127,8 @@ build_clone (tree fn, tree name, bool need_vtt_parm_p,
   DECL_CHAIN (clone) = DECL_CHAIN (fn);
   DECL_CHAIN (fn) = clone;
 
+  adjust_clone_attributes (fn, clone, name);
+
   return clone;
 }
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 964af1ddd85..2bafed4e993 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5787,6 +5787,8 @@ struct GTY((for_user)) spec_entry
 
 extern int current_class_depth;
 
+void adjust_clone_attributes (tree fn, tree clone, tree name, bool = false);
+
 /* in decl.cc */
 
 /* An array of static vars & fns.  */
@@ -7022,6 +7024,7 @@ extern void do_push_parm_decls			(tree, tree, tree *);
 extern tree do_aggregate_paren_init		(tree, tree);
 
 /* in decl2.cc */
+extern void update_sym_alias_interface		(tree);
 extern void record_mangling			(tree, bool);
 extern void overwrite_mangling			(tree, tree);
 extern void note_mangling_alias			(tree, tree);
@@ -7596,6 +7599,7 @@ extern bool emit_tinfo_decl			(tree);
 extern unsigned get_pseudo_tinfo_index		(tree);
 extern tree get_pseudo_tinfo_type		(unsigned);
 extern tree build_if_nonnull			(tree, tree, tsubst_flags_t);
+extern void update_tinfo_sym_alias		(tree);
 
 /* in search.cc */
 extern tree get_parent_with_private_access 	(tree decl, tree binfo);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 2f2dbb8d107..5636d694b68 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -3237,6 +3237,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
 	      && TREE_STATIC (olddecl))))
     make_decl_rtl (olddecl);
 
+  symtab_node::remap_sym_alias_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.
@@ -3260,6 +3262,7 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
       FOR_EACH_CLONE (clone, olddecl)
 	{
 	  DECL_ATTRIBUTES (clone) = DECL_ATTRIBUTES (olddecl);
+	  adjust_clone_attributes (olddecl, clone, DECL_NAME (clone));
 	  DECL_PRESERVE_P (clone) |= DECL_PRESERVE_P (olddecl);
 	}
     }
@@ -10789,6 +10792,7 @@ grokfndecl (tree ctype,
     {
       cplus_decl_attributes (&decl, *attrlist, 0);
       *attrlist = NULL_TREE;
+      create_sym_alias_decls (decl);
     }
 
   if (DECL_HAS_CONTRACTS_P (decl))
diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
index 9e666e5eece..c7f009beaf0 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -1844,6 +1844,9 @@ cplus_decl_attributes (tree *decl, tree attributes, int flags)
   if (late_attrs)
     save_template_attributes (late_attrs, decl, flags);
 
+  if (TYPE_P (*decl) && attributes)
+    update_tinfo_sym_alias (*decl);
+
   /* Propagate deprecation out to the template.  */
   if (TREE_DEPRECATED (*decl))
     if (tree ti = get_template_info (*decl))
@@ -2200,6 +2203,47 @@ 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 sym aliases.  */
+
+void
+update_sym_alias_interface (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.  */
 
@@ -3007,6 +3051,8 @@ determine_visibility (tree decl)
        translation unit, we can make the type internal.  */
     constrain_visibility (decl, VISIBILITY_ANON, false);
 
+  update_sym_alias_interface (decl);
+
   /* If visibility changed and DECL already has DECL_RTL, ensure
      symbol flags are updated.  */
   if ((DECL_VISIBILITY (decl) != orig_visibility
@@ -3269,6 +3315,8 @@ tentative_decl_linkage (tree decl)
       else if (VAR_P (decl))
 	maybe_commonize_var (decl);
     }
+
+  update_sym_alias_interface (decl);
 }
 
 /* DECL is a FUNCTION_DECL or VAR_DECL.  If the object file linkage
@@ -3503,6 +3551,8 @@ import_export_decl (tree decl)
     }
 
   DECL_INTERFACE_KNOWN (decl) = 1;
+
+  update_sym_alias_interface (decl);
 }
 
 /* Return an expression that performs the destruction of DECL, which
diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc
index d19ea5d121c..f877780b2ac 100644
--- a/gcc/cp/name-lookup.cc
+++ b/gcc/cp/name-lookup.cc
@@ -22,9 +22,11 @@ along with GCC; see the file COPYING3.  If not see
 #define INCLUDE_MEMORY
 #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"
@@ -3479,6 +3481,15 @@ push_local_extern_decl_alias (tree decl)
 	  /* Adjust visibility.  */
 	  determine_visibility (alias);
 	}
+      else if (DECL_P (alias))
+	DECL_ATTRIBUTES (alias)
+	  = targetm.merge_decl_attributes (alias, decl);
+      if (DECL_P (alias))
+	{
+	  symtab_node::remap_sym_alias_target (decl, alias);
+	  DECL_ATTRIBUTES (decl)
+	    = remove_attribute ("sym_alias", DECL_ATTRIBUTES (alias));
+	}
     }
 
   retrofit_lang_decl (decl);
diff --git a/gcc/cp/optimize.cc b/gcc/cp/optimize.cc
index 9e8926e4cc6..4a2e8cb4354 100644
--- a/gcc/cp/optimize.cc
+++ b/gcc/cp/optimize.cc
@@ -528,9 +528,12 @@ maybe_clone_body (tree fn)
       DECL_VISIBILITY_SPECIFIED (clone) = DECL_VISIBILITY_SPECIFIED (fn);
       DECL_DLLIMPORT_P (clone) = DECL_DLLIMPORT_P (fn);
       DECL_ATTRIBUTES (clone) = clone_attrs (DECL_ATTRIBUTES (fn));
+      adjust_clone_attributes (fn, clone, DECL_NAME (clone), true);
       DECL_DISREGARD_INLINE_LIMITS (clone) = DECL_DISREGARD_INLINE_LIMITS (fn);
       set_decl_section_name (clone, fn);
 
+      update_sym_alias_interface (clone);
+
       /* Adjust the parameter names and locations.  */
       parm = DECL_ARGUMENTS (fn);
       clone_parm = DECL_ARGUMENTS (clone);
diff --git a/gcc/cp/rtti.cc b/gcc/cp/rtti.cc
index 7878929c246..4bb7b8c8c6e 100644
--- a/gcc/cp/rtti.cc
+++ b/gcc/cp/rtti.cc
@@ -28,8 +28,10 @@ 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"
+#include "cgraph.h"
 
 /* C++ returns type information to the user in struct type_info
    objects. We also use type information to implement dynamic_cast and
@@ -479,8 +481,13 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
 	  = build_tree_list (get_identifier ("non overlapping"),
 			     NULL_TREE);
       else
+	/* Share the non overlapping attribute, without assuming it's
+	   the only attribute, but assuming it's the last if it's
+	   present.  There may be sym aliases too, and those are not
+	   to be shared.  */
 	DECL_ATTRIBUTES (d)
-	  = DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]);
+	  = lookup_attribute ("non overlapping",
+			      DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]));
 
       /* Mark the variable as undefined -- but remember that we can
 	 define it later if we need to do so.  */
@@ -492,6 +499,16 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
       if (CLASS_TYPE_P (type))
 	CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type)) = d;
 
+      /* Copy sym alias attributes from the type to the rtti obj decl.  */
+      tree *attrs = &DECL_ATTRIBUTES (d);
+      FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+	{
+	  tree attr = tree_cons (TREE_PURPOSE (sym), TREE_VALUE (sym), *attrs);
+	  *attrs = attr;
+	  attrs = &TREE_CHAIN (attr);
+	}
+      create_sym_alias_decls (d);
+
       /* Add decl to the global array of tinfo decls.  */
       vec_safe_push (unemitted_tinfo_decls, d);
     }
@@ -499,6 +516,58 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
   return d;
 }
 
+/* After modifying the attributes of TYPE, check whether tinfo was
+   already created and, if so, add to it any sym alias attributes
+   that were not already present.  */
+
+void
+update_tinfo_sym_alias (tree type)
+{
+  if (!TYPE_SIZE (type) || !CLASS_TYPE_P (type))
+    return;
+
+  tree d = CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type));
+  if (!d)
+    return;
+
+  bool first = true;
+  symtab_node *node = NULL;
+
+  tree *attrs = &DECL_ATTRIBUTES (d);
+  FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (d_sym, *attrs)
+	if (TREE_VALUE (sym) == TREE_VALUE (d_sym))
+	  {
+	    found = true;
+	    break;
+	  }
+
+      if (found)
+	continue;
+
+      tree attr = tree_cons (TREE_PURPOSE (sym),
+			     TREE_VALUE (sym),
+			     *attrs);
+      *attrs = attr;
+      attrs = &TREE_CHAIN (attr);
+
+      if (first)
+	{
+	  first = false;
+	  node = symtab_node::get (d);
+	}
+
+      if (!node)
+	continue;
+
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+      create_sym_alias_decl (d, id);
+    }
+}
+
 /* Return the type_info object for TYPE.  */
 
 tree
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index e6de0815846..5dcdec76c1e 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -4141,6 +4141,43 @@ Function Attributes}, @ref{PowerPC Function Attributes},
 @ref{Nios II Function Attributes}, and @ref{S/390 Function Attributes}
 for details.
 
+@cindex @code{sym_alias} function attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} attribute causes @var{name} to be output as an
+alias to the function, with the same linkage and visibility as the
+function, when the function definition is output, provided that the
+function could be an alias target.  For instance,
+
+@smallexample
+void f (uint64_t) __attribute__ ((__sym_alias__ ("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},
+with the same linkage and visibility.  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.
+
+Aliases introduced with this attribute, such as @samp{f_u64} in the
+example above, are assembly symbol names: they do not undergo C++ name
+mangling, and are not made visible in any scope in the source language.
+They can, however, can be named as alias targets.
+
+This attribute requires assembler and object file support for aliases,
+and may not be available on all targets.
+
 @cindex @code{symver} function attribute
 @item symver ("@var{name2}@@@var{nodename}")
 On ELF targets this attribute creates a symbol version.  The @var{name2} part
@@ -8069,6 +8106,10 @@ will be placed in new, unique sections.
 
 This additional functionality requires Binutils version 2.36 or later.
 
+@cindex @code{sym_alias} variable attribute
+@item sym_alias ("@var{name}")
+See @pxref{Common Function Attributes}.
+
 @cindex @code{uninitialized} variable attribute
 @item uninitialized
 This attribute, attached to a variable with automatic storage, means that
@@ -9164,6 +9205,21 @@ is not supported; that is to say, if a given scalar object can be accessed
 through distinct types that assign a different storage order to it, then the
 behavior is undefined.
 
+@cindex @code{sym_alias} type attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} type 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__ ((__sym_alias__ ("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.
+
 @cindex @code{transparent_union} type attribute
 @item transparent_union
 
diff --git a/gcc/symtab.cc b/gcc/symtab.cc
index 0470509a98d..9c3d686c6fc 100644
--- a/gcc/symtab.cc
+++ b/gcc/symtab.cc
@@ -1943,6 +1943,42 @@ symtab_node::noninterposable_alias (symtab_node *node, void *data)
   return false;
 }
 
+/* Remap sym alias nodes recorded as aliasing REPLACED to alias
+   REPLACEMENT instead.  */
+
+void
+symtab_node::remap_sym_alias_target (tree replaced, tree replacement)
+{
+  if (!decl_in_symtab_p (replacement)
+      || !symtab_node::get (replacement))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (replaced))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      symtab_node *sym_node = symtab_node::get_for_asmname (id);
+
+      if (!sym_node)
+	{
+	  create_sym_alias_decl (replacement, id);
+	  continue;
+	}
+
+      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-sym-alias-neg.c b/gcc/testsuite/c-c++-common/attr-sym-alias-neg.c
new file mode 100644
index 00000000000..28be8e3b0e6
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/attr-sym-alias-neg.c
@@ -0,0 +1,80 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int f () {
+  int i __attribute__ ((sym_alias ("f_i"))); /* { dg-warning "ignored" } */
+  /* ??? X cannot be an alias target; should this be flagged? ... */
+  static int x __attribute__ ((sym_alias ("f_x")));
+  static int sx __attribute__ ((alias ("f_x"))); /* { dg-warning "ignored" } */
+  extern int xx __attribute__ ((alias ("f_x"))); /* { dg-warning "ignored" } */
+  return i;
+}
+
+/* ??? ... or should XR be accepted?  */
+extern int xr __attribute__ ((alias ("f_x"))); /* { dg-error "undefined" } */
+int dr __attribute__ ((alias ("f_x"))); /* { dg-error "defined both" } */
+
+
+struct s {
+  int j __attribute__ ((sym_alias ("s_j"))); /* { dg-warning "ignored" } */
+};
+
+
+int j __attribute__ ((sym_alias ("J_var")));
+void __attribute__ ((alias ("J_var")))
+j_fn (); /* { dg-error "between function and variable" } */
+
+
+void __attribute__ ((sym_alias ("K_fn")))
+k () {
+}
+extern int __attribute__ ((alias ("K_fn")))
+k_var; /* { dg-error "between function and variable" } */
+
+
+int l __attribute__ ((sym_alias ("L_fn")));
+
+/* These should be detected and reported, not because the names clash at the
+   source level, but because the asm symbols do.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+void
+L_fn () /* { dg-error "duplicate" "" { xfail *-*-* } } */
+{
+}
+
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __attribute__ ((sym_alias ("M_var")))
+m ()
+{
+}
+
+int M_var; /* { dg-error "duplicate" "" { xfail *-*-* } } */
+
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __attribute__ ((sym_alias ("N_sym")))
+n_fn ()
+{
+}
+
+int __attribute__ ((sym_alias ("N_sym")))
+n_var; /* { dg-error "duplicate" } */
+
+
+int __attribute__ ((sym_alias ("O_sym")))
+o_var;
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __attribute__ ((sym_alias ("O_sym")))
+o_fn () /* { dg-error "duplicate" } */
+{
+}
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-1.c b/gcc/testsuite/c-c++-common/goacc/declare-1.c
index 46ee01b6759..f2842893318 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-1.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-1.c
@@ -113,11 +113,11 @@ f_2 (void)
   int va3;
 #pragma acc declare device_resident(va3)
 
-#ifndef __cplusplus
+#if 0
   /* TODO PR90868
 
-     C: "error: variable '[...]' used more than once with '#pragma acc declare'".  */
-#else
+     "error: variable '[...]' used more than once with '#pragma acc declare'".  */
+
   extern int ve0;
 #pragma acc declare create(ve0)
 
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-2.c b/gcc/testsuite/c-c++-common/goacc/declare-2.c
index e2e22be57e9..aec59b69754 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-2.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-2.c
@@ -137,25 +137,25 @@ void
 f_pr90868_2 (void)
 {
   extern int we0;
-#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" } */
 
   extern int we1;
-#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" } */
 
   extern int *we2;
-#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" } */
 
   extern int we3;
-#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" } */
 
   extern int we4;
-#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" } */
 
   extern int we5;
-#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" } */
  
   extern int we6;
-#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" } */
 }
 
 
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
new file mode 100644
index 00000000000..61af50cb552
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
@@ -0,0 +1,39 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+int var_a = 1;
+
+void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+
+void
+foo_a ()
+{
+}
+
+
+int var_b;
+extern int var_b __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+
+void
+foo_b ()
+{
+}
+
+void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+
+
+int var_c __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+
+void __attribute__ ((__sym_alias__ ("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-sym-alias-2.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
new file mode 100644
index 00000000000..6f0d55ca42c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+struct s
+{
+  int mem __attribute__ ((__sym_alias__ ("MEMFOO"))); /* { dg-warning "attribute ignored" } */
+};
+
+void foo()
+{
+  extern void bar () __attribute__ ((__sym_alias__ ("FOOBAR")));
+  int var __attribute__ ((__sym_alias__ ("FOOVAR"))); /* { dg-warning "attribute ignored" } */
+}
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
new file mode 100644
index 00000000000..0052485a30a
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
@@ -0,0 +1,41 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int var_a = 1;
+
+void
+foo_a ()
+{
+  extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+}
+
+#if 0 // __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__ ((__sym_alias__ ("FOOVAR_B")));
+}
+
+int var_b;
+
+void __attribute__ ((__sym_alias__ ("FOOBAR_C")))
+foo_c ()
+{
+  void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+  /* Another sym for var_b.  */
+  extern int var_b __attribute__ ((__sym_alias__ ("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-sym-alias-4.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
new file mode 100644
index 00000000000..2beafa46379
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-require-alias "" } */
+
+int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A"))) = 42;
+
+int __attribute__ ((__sym_alias__ ("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/c-c++-common/torture/attr-sym-alias-5.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
new file mode 100644
index 00000000000..781b96262dd
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
@@ -0,0 +1,35 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+/* { dg-require-visibility "" } */
+
+extern void __attribute__ ((sym_alias ("F_hid"), visibility ("hidden")))
+f ();
+
+void
+f () {
+}
+
+extern void __attribute__ ((sym_alias ("G_prt")))
+g ();
+
+void __attribute__ ((visibility ("protected")))
+g () {
+}
+
+extern void __attribute__ ((sym_alias ("H_ntr")))
+h ();
+
+void __attribute__ ((visibility ("internal")))
+h ();
+
+void h () {
+}
+
+static void __attribute__ ((sym_alias ("I_stt")))
+i () {
+}
+
+/* { dg-final { scan-assembler {hidden[^\n]*F_hid} } } */
+/* { dg-final { scan-assembler {protected[^\n]*G_prt} } } */
+/* { dg-final { scan-assembler {internal[^\n]*H_ntr} } } */
+/* { dg-final { scan-assembler-not {glob[^\n]*I_stt} } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
new file mode 100644
index 00000000000..579eda1a473
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
@@ -0,0 +1,72 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+class __attribute__ ((__sym_alias__ ("FOOCLS_A"),
+		      __sym_alias__ ("FOOCLS_A_Dupe"))) foo {
+  static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  void __attribute__ ((__sym_alias__ ("FOOBAR_A"))) bar ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+int foo::var = 1;
+
+foo::foo () {}
+
+void foo::bar () {}
+
+namespace b {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_B"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_B"))) foo ();
+    void __attribute__ ((__sym_alias__ ("FOOBAR_B"))) bar () {}
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_B"))) ~foo() {}
+  };
+
+  int foo::var = 2;
+
+  foo::foo () {
+    void (foo::*pbar)() = &foo::bar;
+  }
+}
+
+namespace c {
+  namespace cd {
+    class __attribute__ ((__sym_alias__ ("FOOCLS_C"))) foo {
+      static int var __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+      __attribute__ ((__sym_alias__ ("FOOCTR_C"))) foo () {
+	void (foo::*pbar)() = &foo::bar;
+      }
+      void __attribute__ ((__sym_alias__ ("FOOBAR_C"))) bar () {}
+      virtual __attribute__ ((__sym_alias__ ("FOODTR_C"))) ~foo() {}
+    };
+
+    int foo::var = 3;
+  }
+}
+
+/* { dg-final { scan-assembler "FOOCLS_A" } } */
+/* { dg-final { scan-assembler "FOOCLS_A_Dupe" } } */
+/* { 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-sym-alias-2.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
new file mode 100644
index 00000000000..6facfcc2cd9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+namespace {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_A"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo ();
+    void __attribute__ ((__sym_alias__ ("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-sym-alias-3.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
new file mode 100644
index 00000000000..42c7288ad4a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
@@ -0,0 +1,83 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+// sym can be applied to template function explicit instantiations.
+
+template <typename T>
+void
+fn(T) {
+};
+
+template void __attribute__ ((__sym_alias__ ("FOOFUN_UINT"))) fn<>(unsigned int);
+template void __attribute__ ((__sym_alias__ ("FOOFUN_LONG"))) fn<>(long);
+
+template<> void __attribute__ ((__sym_alias__ ("FOOFUN_CHAR"))) fn<>(char) {}
+
+
+template <typename T = void>
+struct
+foo {
+  virtual ~foo() {}
+
+  virtual void virtfun() {}
+
+  static void stfun() {}
+  void inlfun() {}
+};
+
+// Explicitly instantiate members before the enclosing class.
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_CHAR_VIRT"))) foo<char>::virtfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_CHAR_TI"))) foo<char>;
+
+// Though they're only output if the enclosing class is.
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_LONG_VIRT"))) foo<long>::virtfun();
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_LONG_TI_X"))) foo<long>;
+
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_VOID_ST"))) foo<void>::stfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_VOID_TI"))) foo<>;
+
+
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_X"))) foo<short>;
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_ST"))) foo<short>::stfun();
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_INL"))) foo<short>::inlfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_D"))) foo<short>;
+
+// Explicit specializations work too.
+
+template <>
+struct  __attribute__ ((__sym_alias__ ("FOOCLS_INT_TI")))
+foo<int>
+{
+  virtual ~foo() {}
+  virtual void __attribute__ ((__sym_alias__ ("FOOCLS_INT_VIRT"))) virtfun() {}
+};
+
+/* { dg-final { scan-assembler "FOOFUN_UINT" } } */
+/* { dg-final { scan-assembler "FOOFUN_LONG" } } */
+/* { dg-final { scan-assembler "FOOFUN_CHAR" } } */
+
+/* { dg-final { scan-assembler "FOOCLS_VOID_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_VOID_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_X" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_INL" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_D" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_TI_X" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_VIRT" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
new file mode 100644
index 00000000000..59b779de35d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+template <typename T = void>
+class
+__attribute__ ((__sym_alias__ ("FOOCLS")))
+foo // { dg-error "duplicate|already" }
+{
+  virtual ~foo() {}
+
+  template <typename U>
+  void
+    __attribute__ ((__sym_alias__ ("FOOTMF")))
+    tmemfun () {} // { dg-error "duplicate|already" }
+};
+
+template <typename T>
+void
+__attribute__ ((__sym_alias__ ("FOOTFN")))
+fn(T) { // { dg-error "duplicate|already" }
+};
+
+template class foo<>;
+template class foo<int>;
+template void foo<>::tmemfun<void>();
+template void foo<int>::tmemfun<void>();
+template void fn<>(int);
+template void fn<>(long);
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
new file mode 100644
index 00000000000..45d59b953c3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
@@ -0,0 +1,14 @@
+/* { dg-do compile { target c++11 } } */
+/* { dg-require-alias "" } */
+
+struct foo {
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+foo::foo () {}
+
+// Make sure the inherited cdtors don't duplicate the syms.
+struct bar : foo {
+  using foo::foo;
+};
diff --git a/gcc/varpool.cc b/gcc/varpool.cc
index e7b51b15e4a..33649906d1d 100644
--- a/gcc/varpool.cc
+++ b/gcc/varpool.cc
@@ -163,6 +163,9 @@ varpool_node::get_create (tree decl)
     }
 
   node->register_symbol ();
+
+  create_sym_alias_decls (decl);
+
   return node;
 }

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [gcc(refs/users/aoliva/heads/testme)] Introduce attribute sym_alias
@ 2023-11-30 11:38 Alexandre Oliva
  0 siblings, 0 replies; 33+ messages in thread
From: Alexandre Oliva @ 2023-11-30 11:38 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:c07d5a6de14e64bf0fc19f7dd41754f0e8b76f3f

commit c07d5a6de14e64bf0fc19f7dd41754f0e8b76f3f
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Fri Jul 14 03:45:14 2023 -0300

    Introduce attribute sym_alias
    
    This patch introduces an attribute to add extra asm names (aliases)
    for a decl 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 class types, namespace-scoped
    variables and functions, static data members, member functions,
    explicit instantiations and specializations of template functions,
    members and classes.
    
    When applied to constructors or destructor, additional sym aliases
    with _Base and _Del suffixes are defined for variants other than
    complete-object ones.  This changes the assumption that clones always
    carry the same attributes as their abstract declarations, so there is
    now a function to adjust them.
    
    C++ also had a bug in which attributes from local extern declarations
    failed to be propagated to a preexisting corresponding
    namespace-scoped decl.  I've fixed that, and adjusted acc tests that
    distinguished between C and C++ in this regard.
    
    Applying the attribute to class types is only valid in C++, and the
    effect is to attach the alias to the RTTI object associated with the
    class type.
    
    for  gcc/ChangeLog
    
            * attribs.cc: Include cgraph.h.
            (decl_attributes): Allow late introduction of sym_alias in
            types.
            (create_sym_alias_decl, create_sym_alias_decls): New.
            * attribs.h: Declare them.
            (FOR_EACH_SYM_ALIAS): New macro.
            * cgraph.cc (cgraph_node::create): Create sym_alias decls.
            * varpool.cc (varpool_node::get_create): Create sym_alias
            decls.
            * cgraph.h (symtab_node::remap_sym_alias_target): New.
            * symtab.cc (symtab_node::remap_sym_alias_target): Define.
            * cgraphunit.cc (cgraph_node::analyze): Create alias_target
            node if needed.
            (analyze_functions): Fixup visibility of implicit alias only
            after its node is analyzed.
            * doc/extend.texi (sym_alias): Document for variables,
            functions and types.
    
    for  gcc/ada/ChangeLog
    
            * doc/gnat_rm/interfacing_to_other_languages.rst: Mention
            attribute sym_alias to give RTTI symbols mnemonic names.
            * doc/gnat_ugn/the_gnat_compilation_model.rst: Mention
            aliases.  Fix incorrect ref to C1 ctor variant.
    
    for  gcc/c-family/ChangeLog
    
            * c-ada-spec.cc (pp_asm_name): Use first sym_alias if
            available.
            * c-attribs.cc (handle_sym_alias_attribute): New.
            (c_common_attribute_table): Add sym_alias.
            (handle_copy_attribute): Do not copy sym_alias attribute.
    
    for  gcc/c/ChangeLog
    
            * c-decl.cc (duplicate_decls): Remap sym_alias target.
    
    for  gcc/cp/ChangeLog
    
            * class.cc (adjust_clone_attributes): New.
            (copy_fndecl_with_name, build_clone): Call it.
            * cp-tree.h (adjust_clone_attributes): Declare.
            (update_sym_alias_interface): Declare.
            (update_tinfo_sym_alias): Declare.
            * decl.cc (duplicate_decls): Remap sym_alias target.
            Adjust clone attributes.
            (grokfndecl): Tentatively create sym_alias decls after
            adding attributes in e.g. a template member function explicit
            instantiation.
            * decl2.cc (cplus_decl_attributes): Update tinfo sym_alias.
            (copy_interface, update_sym_alias_interface): New.
            (determine_visibility): Update sym_alias interface.
            (tentative_decl_linkage, import_export_decl): Likewise.
            * name-lookup.cc: Include target.h and cgraph.h.
            (push_local_extern_decl_alias): Merge attributes with
            namespace-scoped decl, and drop duplicate sym_alias.
            * optimize.cc (maybe_clone_body): Re-adjust attributes after
            cloning them.  Update sym_alias interface.
            * rtti.cc: Include attribs.h and cgraph.h.
            (get_tinfo_decl): Copy sym_alias attributes from type to tinfo
            decl.  Create sym_alias decls.
            (update_tinfo_sym_alias): New.
    
    for  gcc/testsuite/ChangeLog
    
            * c-c++-common/goacc/declare-1.c: Adjust.
            * c-c++-common/goacc/declare-2.c: Adjust.
            * c-c++-common/torture/attr-sym-alias-1.c: New.
            * c-c++-common/torture/attr-sym-alias-2.c: New.
            * c-c++-common/torture/attr-sym-alias-3.c: New.
            * c-c++-common/torture/attr-sym-alias-4.c: New.
            * c-c++-common/torture/attr-sym-alias-5.c: New.
            * c-c++-common/attr-sym-alias-neg.c: New.
            * g++.dg/torture/attr-sym-alias-1.C: New.
            * g++.dg/torture/attr-sym-alias-2.C: New.
            * g++.dg/torture/attr-sym-alias-3.C: New.
            * g++.dg/torture/attr-sym-alias-4.C: New.
            * g++.dg/torture/attr-sym-alias-5.C: New.

Diff:
---
 .../doc/gnat_rm/interfacing_to_other_languages.rst |  6 ++
 .../doc/gnat_ugn/the_gnat_compilation_model.rst    | 10 ++-
 gcc/attribs.cc                                     | 68 +++++++++++++++++-
 gcc/attribs.h                                      |  7 ++
 gcc/c-family/c-ada-spec.cc                         |  7 ++
 gcc/c-family/c-attribs.cc                          | 33 ++++++++-
 gcc/c/c-decl.cc                                    |  2 +
 gcc/cgraph.cc                                      |  2 +
 gcc/cgraph.h                                       |  4 ++
 gcc/cgraphunit.cc                                  |  2 +-
 gcc/cp/class.cc                                    | 64 +++++++++++++++++
 gcc/cp/cp-tree.h                                   |  4 ++
 gcc/cp/decl.cc                                     |  4 ++
 gcc/cp/decl2.cc                                    | 50 +++++++++++++
 gcc/cp/name-lookup.cc                              | 11 +++
 gcc/cp/optimize.cc                                 |  3 +
 gcc/cp/rtti.cc                                     | 71 +++++++++++++++++-
 gcc/doc/extend.texi                                | 56 +++++++++++++++
 gcc/symtab.cc                                      | 36 ++++++++++
 gcc/testsuite/c-c++-common/attr-sym-alias-neg.c    | 29 ++++++++
 gcc/testsuite/c-c++-common/goacc/declare-1.c       |  6 +-
 gcc/testsuite/c-c++-common/goacc/declare-2.c       | 14 ++--
 .../c-c++-common/torture/attr-sym-alias-1.c        | 39 ++++++++++
 .../c-c++-common/torture/attr-sym-alias-2.c        | 13 ++++
 .../c-c++-common/torture/attr-sym-alias-3.c        | 41 +++++++++++
 .../c-c++-common/torture/attr-sym-alias-4.c        | 28 ++++++++
 .../c-c++-common/torture/attr-sym-alias-5.c        | 35 +++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C    | 72 +++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C    | 26 +++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C    | 83 ++++++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C    | 28 ++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C    | 14 ++++
 gcc/varpool.cc                                     |  3 +
 33 files changed, 852 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 ad0be511d48..b8de16ebf7c 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 ``sym_alias`` 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 fd15459203a..20045698962 100644
--- a/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
+++ b/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
@@ -4274,6 +4274,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__ ((__sym_alias__ ("Ctor_For_Animal")))
         Animal() {Age_Count = 0;};
       private:
         int Age_Count;
@@ -4306,6 +4307,7 @@ both Carnivore and Domestic, that is:
         virtual int  Number_Of_Teeth ();
         virtual void Set_Owner (char* Name);
 
+        __attribute__ ((__sym_alias__ ("Ctor_For_Dog"))) // mnemonic alias
         Dog(); // Constructor
       private:
         int  Tooth_Count;
@@ -4344,7 +4346,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;
@@ -4360,7 +4363,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,7 +4385,8 @@ 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.
+mangled name (or an alias) 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.cc b/gcc/attribs.cc
index c7209c26acc..3ef07c885d5 100644
--- a/gcc/attribs.cc
+++ b/gcc/attribs.cc
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "target.h"
 #include "tree.h"
+#include "cgraph.h"
 #include "stringpool.h"
 #include "diagnostic-core.h"
 #include "attribs.h"
@@ -825,7 +826,8 @@ decl_attributes (tree *node, tree attributes, int flags,
 
       if (TYPE_P (*anode)
 	  && (flags & (int) ATTR_FLAG_TYPE_IN_PLACE)
-	  && COMPLETE_TYPE_P (*anode))
+	  && COMPLETE_TYPE_P (*anode)
+	  && !is_attribute_p ("sym_alias", name))
 	{
 	  warning (OPT_Wattributes, "type attributes ignored after type is already defined");
 	  continue;
@@ -2640,6 +2642,70 @@ attr_access::array_as_string (tree type) const
   return typstr;
 }
 
+/* Create a sym attribute for DECL to be visible with linkage name ID.  */
+
+tree
+create_sym_alias_decl (tree decl, tree id)
+{
+  const char *attr_str = "sym_alias";
+
+  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;
+
+      tree attr_name = get_identifier (attr_str);
+      error_at (DECL_SOURCE_LOCATION (decl),
+		"duplicate symbol name %qE in %qE attribute of %qD",
+		id, attr_name, decl);
+      inform (DECL_SOURCE_LOCATION (sym_node->decl),
+	      "already used by %qD", sym_node->decl);
+    }
+
+  tree clone = copy_node (decl);
+  DECL_ATTRIBUTES (clone) = remove_attribute (attr_str,
+					      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_extra_name_alias (clone, decl);
+    }
+  else
+    {
+      cgraph_node::create_same_body_alias (clone, decl);
+    }
+
+  return clone;
+}
+
+/* Create decls for all sym aliases requested in DECL's attributes.  */
+
+void
+create_sym_alias_decls (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl)
+      || DECL_ABSTRACT_P (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      create_sym_alias_decl (decl, id);
+    }
+}
+
 #if CHECKING_P
 
 namespace selftest
diff --git a/gcc/attribs.h b/gcc/attribs.h
index 84a43658a70..156f2c20588 100644
--- a/gcc/attribs.h
+++ b/gcc/attribs.h
@@ -398,4 +398,11 @@ extern void init_attr_rdwr_indices (rdwr_map *, tree);
 extern attr_access *get_parm_access (rdwr_map &, tree,
 				     tree = current_function_decl);
 
+extern tree create_sym_alias_decl (tree, tree);
+extern void create_sym_alias_decls (tree);
+
+#define FOR_EACH_SYM_ALIAS(sym, attrs)					\
+  for (tree sym = lookup_attribute ("sym_alias", (attrs));		\
+       sym; sym = lookup_attribute ("sym_alias", TREE_CHAIN (sym)))
+
 #endif // GCC_ATTRIBS_H
diff --git a/gcc/c-family/c-ada-spec.cc b/gcc/c-family/c-ada-spec.cc
index 050994d8416..5042b9cfecd 100644
--- a/gcc/c-family/c-ada-spec.cc
+++ b/gcc/c-family/c-ada-spec.cc
@@ -1431,6 +1431,13 @@ pp_ada_tree_identifier (pretty_printer *buffer, tree node, tree type,
 static void
 pp_asm_name (pretty_printer *buffer, tree t)
 {
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (t))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.cc b/gcc/c-family/c-attribs.cc
index 2b20e58c922..392437eab3b 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -108,7 +108,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_sym_alias_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,
@@ -388,6 +389,8 @@ const struct attribute_spec c_common_attribute_table[] =
 			      handle_alias_attribute, NULL },
   { "weakref",                0, 1, true,  false, false, false,
 			      handle_weakref_attribute, NULL },
+  { "sym_alias",              1, 1, false,  false, false, false,
+			      handle_sym_alias_attribute, NULL },
   { "no_instrument_function", 0, 0, true,  false, false, false,
 			      handle_no_instrument_function_attribute,
 			      NULL },
@@ -3002,7 +3005,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
@@ -3012,7 +3015,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
@@ -3022,6 +3025,29 @@ handle_alias_attribute (tree *node, tree name, tree args,
   return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs);
 }
 
+/* Handle a "sym_alias" attribute; arguments as in struct
+   attribute_spec.handler.  */
+
+static tree
+handle_sym_alias_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 (decl_in_symtab_p (node))
+    *no_add_attrs = false;
+  else if (TYPE_P (node) && c_dialect_cxx ())
+    *no_add_attrs = false;
+  else
+    return error_mark_node;
+
+  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.  */
 
@@ -3155,6 +3181,7 @@ handle_copy_attribute (tree *node, tree name, tree args,
 	      || is_attribute_p ("visibility", atname)
 	      || is_attribute_p ("weak", atname)
 	      || is_attribute_p ("weakref", atname)
+	      || is_attribute_p ("sym_alias", atname)
 	      || is_attribute_p ("target_clones", atname))
 	    continue;
 
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index cf1df82c0f4..e5a1ee54cb3 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -3119,6 +3119,8 @@ duplicate_decls (tree newdecl, tree olddecl)
 
   merge_decls (newdecl, olddecl, newtype, oldtype);
 
+  symtab_node::remap_sym_alias_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.cc b/gcc/cgraph.cc
index f93259a8c70..0bef2179e89 100644
--- a/gcc/cgraph.cc
+++ b/gcc/cgraph.cc
@@ -523,6 +523,8 @@ cgraph_node::create (tree decl)
   node->register_symbol ();
   maybe_record_nested_function (node);
 
+  create_sym_alias_decls (decl);
+
   return node;
 }
 
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index cfdd9f693a8..cb5ac01f7e2 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -327,6 +327,10 @@ public:
   /* Return DECL that alias is aliasing.  */
   inline tree get_alias_target_tree ();
 
+  /* Remap sym alias nodes recorded as aliasing REPLACED to alias REPLACEMENT
+     instead.  */
+  static void remap_sym_alias_target (tree replaced, tree replacement);
+
   /* Set section for symbol and its aliases.  */
   void set_section (const char *section);
 
diff --git a/gcc/cgraphunit.cc b/gcc/cgraphunit.cc
index bccd2f2abb5..eb2d05094e9 100644
--- a/gcc/cgraphunit.cc
+++ b/gcc/cgraphunit.cc
@@ -1175,7 +1175,7 @@ analyze_functions (bool first_time)
      C++ FE is confused about the COMDAT groups being right.  */
   if (symtab->cpp_implicit_aliases_done)
     FOR_EACH_SYMBOL (node)
-      if (node->cpp_implicit_alias)
+      if (node->cpp_implicit_alias && node->analyzed)
 	  node->fixup_same_cpp_alias_visibility (node->get_alias_target ());
   build_type_inheritance_graph ();
 
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index 6fdb56abfb9..fa8fc63db82 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -4896,6 +4896,68 @@ check_methods (tree t)
     }
 }
 
+/* Adjust sym alias name for CLONE, cloned from FN and named NAME,
+   if it is a cdtor, and drop the sym alias from other clones.  */
+
+void
+adjust_clone_attributes (tree fn, tree clone, tree name, bool skip_copy_p)
+{
+  if (IDENTIFIER_CDTOR_P (name))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	{
+	  found = true;
+	  break;
+	}
+
+      if (found
+	  && (name == complete_ctor_identifier
+	      || name == complete_dtor_identifier))
+	{
+	  /* Reuse the sym alias decls created for the primary cdtor
+	     decl.  */
+	  symtab_node::remap_sym_alias_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);
+
+	  if (!skip_copy_p)
+	    DECL_ATTRIBUTES (clone) = copy_list (DECL_ATTRIBUTES (clone));
+
+	  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	    {
+	      /* We need to copy this even with skip_copy_p, because
+		 even then copying was shallow.  */
+	      TREE_VALUE (sym) = copy_list (TREE_VALUE (sym));
+	      /* Append suf to the sym alias name.  */
+	      tree str = TREE_VALUE (TREE_VALUE (sym));
+	      char *symname = concat (TREE_STRING_POINTER (str), suf, NULL);
+	      str = build_string (TREE_STRING_LENGTH (str) + xlen, symname);
+	      TREE_VALUE (TREE_VALUE (sym)) = str;
+	      free (symname);
+	    }
+
+	  if (symtab_node::get (clone))
+	    create_sym_alias_decls (clone);
+	}
+    }
+  else
+    DECL_ATTRIBUTES (clone)
+      = remove_attribute ("sym_alias", DECL_ATTRIBUTES (clone));
+}
+
 /* FN is constructor, destructor or operator function.  Clone the
    declaration to create a NAME'd variant.  NEED_VTT_PARM_P and
    OMIT_INHERITED_PARMS_P are relevant if it's a cdtor.  */
@@ -5065,6 +5127,8 @@ build_clone (tree fn, tree name, bool need_vtt_parm_p,
   DECL_CHAIN (clone) = DECL_CHAIN (fn);
   DECL_CHAIN (fn) = clone;
 
+  adjust_clone_attributes (fn, clone, name);
+
   return clone;
 }
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 964af1ddd85..2bafed4e993 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5787,6 +5787,8 @@ struct GTY((for_user)) spec_entry
 
 extern int current_class_depth;
 
+void adjust_clone_attributes (tree fn, tree clone, tree name, bool = false);
+
 /* in decl.cc */
 
 /* An array of static vars & fns.  */
@@ -7022,6 +7024,7 @@ extern void do_push_parm_decls			(tree, tree, tree *);
 extern tree do_aggregate_paren_init		(tree, tree);
 
 /* in decl2.cc */
+extern void update_sym_alias_interface		(tree);
 extern void record_mangling			(tree, bool);
 extern void overwrite_mangling			(tree, tree);
 extern void note_mangling_alias			(tree, tree);
@@ -7596,6 +7599,7 @@ extern bool emit_tinfo_decl			(tree);
 extern unsigned get_pseudo_tinfo_index		(tree);
 extern tree get_pseudo_tinfo_type		(unsigned);
 extern tree build_if_nonnull			(tree, tree, tsubst_flags_t);
+extern void update_tinfo_sym_alias		(tree);
 
 /* in search.cc */
 extern tree get_parent_with_private_access 	(tree decl, tree binfo);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 2f2dbb8d107..5636d694b68 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -3237,6 +3237,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
 	      && TREE_STATIC (olddecl))))
     make_decl_rtl (olddecl);
 
+  symtab_node::remap_sym_alias_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.
@@ -3260,6 +3262,7 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
       FOR_EACH_CLONE (clone, olddecl)
 	{
 	  DECL_ATTRIBUTES (clone) = DECL_ATTRIBUTES (olddecl);
+	  adjust_clone_attributes (olddecl, clone, DECL_NAME (clone));
 	  DECL_PRESERVE_P (clone) |= DECL_PRESERVE_P (olddecl);
 	}
     }
@@ -10789,6 +10792,7 @@ grokfndecl (tree ctype,
     {
       cplus_decl_attributes (&decl, *attrlist, 0);
       *attrlist = NULL_TREE;
+      create_sym_alias_decls (decl);
     }
 
   if (DECL_HAS_CONTRACTS_P (decl))
diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
index 9e666e5eece..c7f009beaf0 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -1844,6 +1844,9 @@ cplus_decl_attributes (tree *decl, tree attributes, int flags)
   if (late_attrs)
     save_template_attributes (late_attrs, decl, flags);
 
+  if (TYPE_P (*decl) && attributes)
+    update_tinfo_sym_alias (*decl);
+
   /* Propagate deprecation out to the template.  */
   if (TREE_DEPRECATED (*decl))
     if (tree ti = get_template_info (*decl))
@@ -2200,6 +2203,47 @@ 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 sym aliases.  */
+
+void
+update_sym_alias_interface (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.  */
 
@@ -3007,6 +3051,8 @@ determine_visibility (tree decl)
        translation unit, we can make the type internal.  */
     constrain_visibility (decl, VISIBILITY_ANON, false);
 
+  update_sym_alias_interface (decl);
+
   /* If visibility changed and DECL already has DECL_RTL, ensure
      symbol flags are updated.  */
   if ((DECL_VISIBILITY (decl) != orig_visibility
@@ -3269,6 +3315,8 @@ tentative_decl_linkage (tree decl)
       else if (VAR_P (decl))
 	maybe_commonize_var (decl);
     }
+
+  update_sym_alias_interface (decl);
 }
 
 /* DECL is a FUNCTION_DECL or VAR_DECL.  If the object file linkage
@@ -3503,6 +3551,8 @@ import_export_decl (tree decl)
     }
 
   DECL_INTERFACE_KNOWN (decl) = 1;
+
+  update_sym_alias_interface (decl);
 }
 
 /* Return an expression that performs the destruction of DECL, which
diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc
index d19ea5d121c..f877780b2ac 100644
--- a/gcc/cp/name-lookup.cc
+++ b/gcc/cp/name-lookup.cc
@@ -22,9 +22,11 @@ along with GCC; see the file COPYING3.  If not see
 #define INCLUDE_MEMORY
 #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"
@@ -3479,6 +3481,15 @@ push_local_extern_decl_alias (tree decl)
 	  /* Adjust visibility.  */
 	  determine_visibility (alias);
 	}
+      else if (DECL_P (alias))
+	DECL_ATTRIBUTES (alias)
+	  = targetm.merge_decl_attributes (alias, decl);
+      if (DECL_P (alias))
+	{
+	  symtab_node::remap_sym_alias_target (decl, alias);
+	  DECL_ATTRIBUTES (decl)
+	    = remove_attribute ("sym_alias", DECL_ATTRIBUTES (alias));
+	}
     }
 
   retrofit_lang_decl (decl);
diff --git a/gcc/cp/optimize.cc b/gcc/cp/optimize.cc
index 9e8926e4cc6..4a2e8cb4354 100644
--- a/gcc/cp/optimize.cc
+++ b/gcc/cp/optimize.cc
@@ -528,9 +528,12 @@ maybe_clone_body (tree fn)
       DECL_VISIBILITY_SPECIFIED (clone) = DECL_VISIBILITY_SPECIFIED (fn);
       DECL_DLLIMPORT_P (clone) = DECL_DLLIMPORT_P (fn);
       DECL_ATTRIBUTES (clone) = clone_attrs (DECL_ATTRIBUTES (fn));
+      adjust_clone_attributes (fn, clone, DECL_NAME (clone), true);
       DECL_DISREGARD_INLINE_LIMITS (clone) = DECL_DISREGARD_INLINE_LIMITS (fn);
       set_decl_section_name (clone, fn);
 
+      update_sym_alias_interface (clone);
+
       /* Adjust the parameter names and locations.  */
       parm = DECL_ARGUMENTS (fn);
       clone_parm = DECL_ARGUMENTS (clone);
diff --git a/gcc/cp/rtti.cc b/gcc/cp/rtti.cc
index 7878929c246..4bb7b8c8c6e 100644
--- a/gcc/cp/rtti.cc
+++ b/gcc/cp/rtti.cc
@@ -28,8 +28,10 @@ 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"
+#include "cgraph.h"
 
 /* C++ returns type information to the user in struct type_info
    objects. We also use type information to implement dynamic_cast and
@@ -479,8 +481,13 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
 	  = build_tree_list (get_identifier ("non overlapping"),
 			     NULL_TREE);
       else
+	/* Share the non overlapping attribute, without assuming it's
+	   the only attribute, but assuming it's the last if it's
+	   present.  There may be sym aliases too, and those are not
+	   to be shared.  */
 	DECL_ATTRIBUTES (d)
-	  = DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]);
+	  = lookup_attribute ("non overlapping",
+			      DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]));
 
       /* Mark the variable as undefined -- but remember that we can
 	 define it later if we need to do so.  */
@@ -492,6 +499,16 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
       if (CLASS_TYPE_P (type))
 	CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type)) = d;
 
+      /* Copy sym alias attributes from the type to the rtti obj decl.  */
+      tree *attrs = &DECL_ATTRIBUTES (d);
+      FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+	{
+	  tree attr = tree_cons (TREE_PURPOSE (sym), TREE_VALUE (sym), *attrs);
+	  *attrs = attr;
+	  attrs = &TREE_CHAIN (attr);
+	}
+      create_sym_alias_decls (d);
+
       /* Add decl to the global array of tinfo decls.  */
       vec_safe_push (unemitted_tinfo_decls, d);
     }
@@ -499,6 +516,58 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
   return d;
 }
 
+/* After modifying the attributes of TYPE, check whether tinfo was
+   already created and, if so, add to it any sym alias attributes
+   that were not already present.  */
+
+void
+update_tinfo_sym_alias (tree type)
+{
+  if (!TYPE_SIZE (type) || !CLASS_TYPE_P (type))
+    return;
+
+  tree d = CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type));
+  if (!d)
+    return;
+
+  bool first = true;
+  symtab_node *node = NULL;
+
+  tree *attrs = &DECL_ATTRIBUTES (d);
+  FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (d_sym, *attrs)
+	if (TREE_VALUE (sym) == TREE_VALUE (d_sym))
+	  {
+	    found = true;
+	    break;
+	  }
+
+      if (found)
+	continue;
+
+      tree attr = tree_cons (TREE_PURPOSE (sym),
+			     TREE_VALUE (sym),
+			     *attrs);
+      *attrs = attr;
+      attrs = &TREE_CHAIN (attr);
+
+      if (first)
+	{
+	  first = false;
+	  node = symtab_node::get (d);
+	}
+
+      if (!node)
+	continue;
+
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+      create_sym_alias_decl (d, id);
+    }
+}
+
 /* Return the type_info object for TYPE.  */
 
 tree
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index e6de0815846..5dcdec76c1e 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -4141,6 +4141,43 @@ Function Attributes}, @ref{PowerPC Function Attributes},
 @ref{Nios II Function Attributes}, and @ref{S/390 Function Attributes}
 for details.
 
+@cindex @code{sym_alias} function attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} attribute causes @var{name} to be output as an
+alias to the function, with the same linkage and visibility as the
+function, when the function definition is output, provided that the
+function could be an alias target.  For instance,
+
+@smallexample
+void f (uint64_t) __attribute__ ((__sym_alias__ ("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},
+with the same linkage and visibility.  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.
+
+Aliases introduced with this attribute, such as @samp{f_u64} in the
+example above, are assembly symbol names: they do not undergo C++ name
+mangling, and are not made visible in any scope in the source language.
+They can, however, can be named as alias targets.
+
+This attribute requires assembler and object file support for aliases,
+and may not be available on all targets.
+
 @cindex @code{symver} function attribute
 @item symver ("@var{name2}@@@var{nodename}")
 On ELF targets this attribute creates a symbol version.  The @var{name2} part
@@ -8069,6 +8106,10 @@ will be placed in new, unique sections.
 
 This additional functionality requires Binutils version 2.36 or later.
 
+@cindex @code{sym_alias} variable attribute
+@item sym_alias ("@var{name}")
+See @pxref{Common Function Attributes}.
+
 @cindex @code{uninitialized} variable attribute
 @item uninitialized
 This attribute, attached to a variable with automatic storage, means that
@@ -9164,6 +9205,21 @@ is not supported; that is to say, if a given scalar object can be accessed
 through distinct types that assign a different storage order to it, then the
 behavior is undefined.
 
+@cindex @code{sym_alias} type attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} type 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__ ((__sym_alias__ ("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.
+
 @cindex @code{transparent_union} type attribute
 @item transparent_union
 
diff --git a/gcc/symtab.cc b/gcc/symtab.cc
index 0470509a98d..0812088e59f 100644
--- a/gcc/symtab.cc
+++ b/gcc/symtab.cc
@@ -1943,6 +1943,42 @@ symtab_node::noninterposable_alias (symtab_node *node, void *data)
   return false;
 }
 
+/* Remap sym alias nodes recorded as aliasing REPLACED to alias
+   REPLACEMENT instead.  */
+
+void
+symtab_node::remap_sym_alias_target (tree replaced, tree replacement)
+{
+  if (!decl_in_symtab_p (replacement)
+      || !symtab_node::get (replacement))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (replaced))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      symtab_node *sym_node = symtab_node::get_for_asmname (id);
+
+      if (!sym_node)
+	{
+	  create_sym_alias_decl (replacement, id);
+	  continue;
+	}
+
+      gcc_assert (!sym_node->analyzed);
+      if (sym_node->alias_target != replaced)
+	continue;
+
+      sym_node->definition = 0;
+
+      if (VAR_P (replaced))
+	varpool_node::create_extra_name_alias (sym_node->decl, replacement);
+      else
+	cgraph_node::create_same_body_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-sym-alias-neg.c b/gcc/testsuite/c-c++-common/attr-sym-alias-neg.c
new file mode 100644
index 00000000000..5cec1595b0c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/attr-sym-alias-neg.c
@@ -0,0 +1,29 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int f () {
+  /* ??? X cannot be an alias target; should this be flagged? ... */
+  static int x __attribute__ ((sym_alias ("f_x")));
+  int i __attribute__ ((sym_alias ("f_i"))); /* { dg-warning "ignored" } */
+  static int sx __attribute__ ((alias ("f_x"))); /* { dg-warning "ignored" } */
+  extern int xx __attribute__ ((alias ("f_x"))); /* { dg-warning "ignored" } */
+  return i;
+}
+
+/* ??? ... or should XR be accepted?  */
+extern int xr __attribute__ ((alias ("f_x"))); /* { dg-error "undefined" } */
+int dr __attribute__ ((alias ("f_x"))); /* { dg-error "defined both" } */
+
+struct s {
+  int j __attribute__ ((sym_alias ("s_j"))); /* { dg-warning "ignored" } */
+};
+
+int j __attribute__ ((sym_alias ("J_var")));
+void __attribute__ ((alias ("J_var")))
+j_fn (); /* { dg-error "between function and variable" } */
+
+void __attribute__ ((sym_alias ("K_fn")))
+k () {
+}
+extern int __attribute__ ((alias ("K_fn")))
+k_var; /* { dg-error "between function and variable" } */
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-1.c b/gcc/testsuite/c-c++-common/goacc/declare-1.c
index 46ee01b6759..f2842893318 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-1.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-1.c
@@ -113,11 +113,11 @@ f_2 (void)
   int va3;
 #pragma acc declare device_resident(va3)
 
-#ifndef __cplusplus
+#if 0
   /* TODO PR90868
 
-     C: "error: variable '[...]' used more than once with '#pragma acc declare'".  */
-#else
+     "error: variable '[...]' used more than once with '#pragma acc declare'".  */
+
   extern int ve0;
 #pragma acc declare create(ve0)
 
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-2.c b/gcc/testsuite/c-c++-common/goacc/declare-2.c
index e2e22be57e9..aec59b69754 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-2.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-2.c
@@ -137,25 +137,25 @@ void
 f_pr90868_2 (void)
 {
   extern int we0;
-#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" } */
 
   extern int we1;
-#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" } */
 
   extern int *we2;
-#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" } */
 
   extern int we3;
-#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" } */
 
   extern int we4;
-#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" } */
 
   extern int we5;
-#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" } */
  
   extern int we6;
-#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" } */
 }
 
 
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
new file mode 100644
index 00000000000..61af50cb552
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
@@ -0,0 +1,39 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+int var_a = 1;
+
+void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+
+void
+foo_a ()
+{
+}
+
+
+int var_b;
+extern int var_b __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+
+void
+foo_b ()
+{
+}
+
+void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+
+
+int var_c __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+
+void __attribute__ ((__sym_alias__ ("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-sym-alias-2.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
new file mode 100644
index 00000000000..6f0d55ca42c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+struct s
+{
+  int mem __attribute__ ((__sym_alias__ ("MEMFOO"))); /* { dg-warning "attribute ignored" } */
+};
+
+void foo()
+{
+  extern void bar () __attribute__ ((__sym_alias__ ("FOOBAR")));
+  int var __attribute__ ((__sym_alias__ ("FOOVAR"))); /* { dg-warning "attribute ignored" } */
+}
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
new file mode 100644
index 00000000000..0052485a30a
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
@@ -0,0 +1,41 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int var_a = 1;
+
+void
+foo_a ()
+{
+  extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+}
+
+#if 0 // __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__ ((__sym_alias__ ("FOOVAR_B")));
+}
+
+int var_b;
+
+void __attribute__ ((__sym_alias__ ("FOOBAR_C")))
+foo_c ()
+{
+  void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+  /* Another sym for var_b.  */
+  extern int var_b __attribute__ ((__sym_alias__ ("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-sym-alias-4.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
new file mode 100644
index 00000000000..2beafa46379
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-require-alias "" } */
+
+int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A"))) = 42;
+
+int __attribute__ ((__sym_alias__ ("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/c-c++-common/torture/attr-sym-alias-5.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
new file mode 100644
index 00000000000..781b96262dd
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
@@ -0,0 +1,35 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+/* { dg-require-visibility "" } */
+
+extern void __attribute__ ((sym_alias ("F_hid"), visibility ("hidden")))
+f ();
+
+void
+f () {
+}
+
+extern void __attribute__ ((sym_alias ("G_prt")))
+g ();
+
+void __attribute__ ((visibility ("protected")))
+g () {
+}
+
+extern void __attribute__ ((sym_alias ("H_ntr")))
+h ();
+
+void __attribute__ ((visibility ("internal")))
+h ();
+
+void h () {
+}
+
+static void __attribute__ ((sym_alias ("I_stt")))
+i () {
+}
+
+/* { dg-final { scan-assembler {hidden[^\n]*F_hid} } } */
+/* { dg-final { scan-assembler {protected[^\n]*G_prt} } } */
+/* { dg-final { scan-assembler {internal[^\n]*H_ntr} } } */
+/* { dg-final { scan-assembler-not {glob[^\n]*I_stt} } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
new file mode 100644
index 00000000000..579eda1a473
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
@@ -0,0 +1,72 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+class __attribute__ ((__sym_alias__ ("FOOCLS_A"),
+		      __sym_alias__ ("FOOCLS_A_Dupe"))) foo {
+  static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  void __attribute__ ((__sym_alias__ ("FOOBAR_A"))) bar ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+int foo::var = 1;
+
+foo::foo () {}
+
+void foo::bar () {}
+
+namespace b {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_B"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_B"))) foo ();
+    void __attribute__ ((__sym_alias__ ("FOOBAR_B"))) bar () {}
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_B"))) ~foo() {}
+  };
+
+  int foo::var = 2;
+
+  foo::foo () {
+    void (foo::*pbar)() = &foo::bar;
+  }
+}
+
+namespace c {
+  namespace cd {
+    class __attribute__ ((__sym_alias__ ("FOOCLS_C"))) foo {
+      static int var __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+      __attribute__ ((__sym_alias__ ("FOOCTR_C"))) foo () {
+	void (foo::*pbar)() = &foo::bar;
+      }
+      void __attribute__ ((__sym_alias__ ("FOOBAR_C"))) bar () {}
+      virtual __attribute__ ((__sym_alias__ ("FOODTR_C"))) ~foo() {}
+    };
+
+    int foo::var = 3;
+  }
+}
+
+/* { dg-final { scan-assembler "FOOCLS_A" } } */
+/* { dg-final { scan-assembler "FOOCLS_A_Dupe" } } */
+/* { 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-sym-alias-2.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
new file mode 100644
index 00000000000..6facfcc2cd9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+namespace {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_A"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo ();
+    void __attribute__ ((__sym_alias__ ("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-sym-alias-3.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
new file mode 100644
index 00000000000..42c7288ad4a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
@@ -0,0 +1,83 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+// sym can be applied to template function explicit instantiations.
+
+template <typename T>
+void
+fn(T) {
+};
+
+template void __attribute__ ((__sym_alias__ ("FOOFUN_UINT"))) fn<>(unsigned int);
+template void __attribute__ ((__sym_alias__ ("FOOFUN_LONG"))) fn<>(long);
+
+template<> void __attribute__ ((__sym_alias__ ("FOOFUN_CHAR"))) fn<>(char) {}
+
+
+template <typename T = void>
+struct
+foo {
+  virtual ~foo() {}
+
+  virtual void virtfun() {}
+
+  static void stfun() {}
+  void inlfun() {}
+};
+
+// Explicitly instantiate members before the enclosing class.
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_CHAR_VIRT"))) foo<char>::virtfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_CHAR_TI"))) foo<char>;
+
+// Though they're only output if the enclosing class is.
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_LONG_VIRT"))) foo<long>::virtfun();
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_LONG_TI_X"))) foo<long>;
+
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_VOID_ST"))) foo<void>::stfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_VOID_TI"))) foo<>;
+
+
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_X"))) foo<short>;
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_ST"))) foo<short>::stfun();
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_INL"))) foo<short>::inlfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_D"))) foo<short>;
+
+// Explicit specializations work too.
+
+template <>
+struct  __attribute__ ((__sym_alias__ ("FOOCLS_INT_TI")))
+foo<int>
+{
+  virtual ~foo() {}
+  virtual void __attribute__ ((__sym_alias__ ("FOOCLS_INT_VIRT"))) virtfun() {}
+};
+
+/* { dg-final { scan-assembler "FOOFUN_UINT" } } */
+/* { dg-final { scan-assembler "FOOFUN_LONG" } } */
+/* { dg-final { scan-assembler "FOOFUN_CHAR" } } */
+
+/* { dg-final { scan-assembler "FOOCLS_VOID_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_VOID_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_X" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_INL" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_D" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_TI_X" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_VIRT" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
new file mode 100644
index 00000000000..59b779de35d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+template <typename T = void>
+class
+__attribute__ ((__sym_alias__ ("FOOCLS")))
+foo // { dg-error "duplicate|already" }
+{
+  virtual ~foo() {}
+
+  template <typename U>
+  void
+    __attribute__ ((__sym_alias__ ("FOOTMF")))
+    tmemfun () {} // { dg-error "duplicate|already" }
+};
+
+template <typename T>
+void
+__attribute__ ((__sym_alias__ ("FOOTFN")))
+fn(T) { // { dg-error "duplicate|already" }
+};
+
+template class foo<>;
+template class foo<int>;
+template void foo<>::tmemfun<void>();
+template void foo<int>::tmemfun<void>();
+template void fn<>(int);
+template void fn<>(long);
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
new file mode 100644
index 00000000000..45d59b953c3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
@@ -0,0 +1,14 @@
+/* { dg-do compile { target c++11 } } */
+/* { dg-require-alias "" } */
+
+struct foo {
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+foo::foo () {}
+
+// Make sure the inherited cdtors don't duplicate the syms.
+struct bar : foo {
+  using foo::foo;
+};
diff --git a/gcc/varpool.cc b/gcc/varpool.cc
index e7b51b15e4a..33649906d1d 100644
--- a/gcc/varpool.cc
+++ b/gcc/varpool.cc
@@ -163,6 +163,9 @@ varpool_node::get_create (tree decl)
     }
 
   node->register_symbol ();
+
+  create_sym_alias_decls (decl);
+
   return node;
 }

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [gcc(refs/users/aoliva/heads/testme)] Introduce attribute sym_alias
@ 2023-11-30 11:03 Alexandre Oliva
  0 siblings, 0 replies; 33+ messages in thread
From: Alexandre Oliva @ 2023-11-30 11:03 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:f7439048d82405c6699a319fe864d2809a652ecb

commit f7439048d82405c6699a319fe864d2809a652ecb
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Fri Jul 14 03:45:14 2023 -0300

    Introduce attribute sym_alias
    
    This patch introduces an attribute to add extra asm names (aliases)
    for a decl 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 class types, namespace-scoped
    variables and functions, static data members, member functions,
    explicit instantiations and specializations of template functions,
    members and classes.
    
    When applied to constructors or destructor, additional sym aliases
    with _Base and _Del suffixes are defined for variants other than
    complete-object ones.  This changes the assumption that clones always
    carry the same attributes as their abstract declarations, so there is
    now a function to adjust them.
    
    C++ also had a bug in which attributes from local extern declarations
    failed to be propagated to a preexisting corresponding
    namespace-scoped decl.  I've fixed that, and adjusted acc tests that
    distinguished between C and C++ in this regard.
    
    Applying the attribute to class types is only valid in C++, and the
    effect is to attach the alias to the RTTI object associated with the
    class type.
    
    for  gcc/ChangeLog
    
            * attribs.cc: Include cgraph.h.
            (decl_attributes): Allow late introduction of sym_alias in
            types.
            (create_sym_alias_decl, create_sym_alias_decls): New.
            * attribs.h: Declare them.
            (FOR_EACH_SYM_ALIAS): New macro.
            * cgraph.cc (cgraph_node::create): Create sym_alias decls.
            * varpool.cc (varpool_node::get_create): Create sym_alias
            decls.
            * cgraph.h (symtab_node::remap_sym_alias_target): New.
            * symtab.cc (symtab_node::remap_sym_alias_target): Define.
            * cgraphunit.cc (cgraph_node::analyze): Create alias_target
            node if needed.
            (analyze_functions): Fixup visibility of implicit alias only
            after its node is analyzed.
            * doc/extend.texi (sym_alias): Document for variables,
            functions and types.
    
    for  gcc/ada/ChangeLog
    
            * doc/gnat_rm/interfacing_to_other_languages.rst: Mention
            attribute sym_alias to give RTTI symbols mnemonic names.
            * doc/gnat_ugn/the_gnat_compilation_model.rst: Mention
            aliases.  Fix incorrect ref to C1 ctor variant.
    
    for  gcc/c-family/ChangeLog
    
            * c-ada-spec.cc (pp_asm_name): Use first sym_alias if
            available.
            * c-attribs.cc (handle_sym_alias_attribute): New.
            (c_common_attribute_table): Add sym_alias.
            (handle_copy_attribute): Do not copy sym_alias attribute.
    
    for  gcc/c/ChangeLog
    
            * c-decl.cc (duplicate_decls): Remap sym_alias target.
    
    for  gcc/cp/ChangeLog
    
            * class.cc (adjust_clone_attributes): New.
            (copy_fndecl_with_name, build_clone): Call it.
            * cp-tree.h (adjust_clone_attributes): Declare.
            (update_sym_alias_interface): Declare.
            (update_tinfo_sym_alias): Declare.
            * decl.cc (duplicate_decls): Remap sym_alias target.
            Adjust clone attributes.
            (grokfndecl): Tentatively create sym_alias decls after
            adding attributes in e.g. a template member function explicit
            instantiation.
            * decl2.cc (cplus_decl_attributes): Update tinfo sym_alias.
            (copy_interface, update_sym_alias_interface): New.
            (determine_visibility): Update sym_alias interface.
            (tentative_decl_linkage, import_export_decl): Likewise.
            * name-lookup.cc: Include target.h and cgraph.h.
            (push_local_extern_decl_alias): Merge attributes with
            namespace-scoped decl, and drop duplicate sym_alias.
            * optimize.cc (maybe_clone_body): Re-adjust attributes after
            cloning them.  Update sym_alias interface.
            * rtti.cc: Include attribs.h and cgraph.h.
            (get_tinfo_decl): Copy sym_alias attributes from type to tinfo
            decl.  Create sym_alias decls.
            (update_tinfo_sym_alias): New.
    
    for  gcc/testsuite/ChangeLog
    
            * c-c++-common/goacc/declare-1.c: Adjust.
            * c-c++-common/goacc/declare-2.c: Adjust.
            * c-c++-common/torture/attr-sym-alias-1.c: New.
            * c-c++-common/torture/attr-sym-alias-2.c: New.
            * c-c++-common/torture/attr-sym-alias-3.c: New.
            * c-c++-common/torture/attr-sym-alias-4.c: New.
            * c-c++-common/torture/attr-sym-alias-5.c: New.
            * c-c++-common/attr-sym-alias-neg.c: New.
            * g++.dg/torture/attr-sym-alias-1.C: New.
            * g++.dg/torture/attr-sym-alias-2.C: New.
            * g++.dg/torture/attr-sym-alias-3.C: New.
            * g++.dg/torture/attr-sym-alias-4.C: New.
            * g++.dg/torture/attr-sym-alias-5.C: New.

Diff:
---
 .../doc/gnat_rm/interfacing_to_other_languages.rst |  6 ++
 .../doc/gnat_ugn/the_gnat_compilation_model.rst    | 10 ++-
 gcc/attribs.cc                                     | 68 +++++++++++++++++-
 gcc/attribs.h                                      |  7 ++
 gcc/c-family/c-ada-spec.cc                         |  7 ++
 gcc/c-family/c-attribs.cc                          | 33 ++++++++-
 gcc/c/c-decl.cc                                    |  2 +
 gcc/cgraph.cc                                      |  2 +
 gcc/cgraph.h                                       |  4 ++
 gcc/cgraphunit.cc                                  |  2 +-
 gcc/cp/class.cc                                    | 64 +++++++++++++++++
 gcc/cp/cp-tree.h                                   |  4 ++
 gcc/cp/decl.cc                                     |  4 ++
 gcc/cp/decl2.cc                                    | 50 +++++++++++++
 gcc/cp/name-lookup.cc                              | 11 +++
 gcc/cp/optimize.cc                                 |  3 +
 gcc/cp/rtti.cc                                     | 71 +++++++++++++++++-
 gcc/doc/extend.texi                                | 52 ++++++++++++++
 gcc/symtab.cc                                      | 36 ++++++++++
 gcc/testsuite/c-c++-common/attr-sym-alias-neg.c    | 18 +++++
 gcc/testsuite/c-c++-common/goacc/declare-1.c       |  6 +-
 gcc/testsuite/c-c++-common/goacc/declare-2.c       | 14 ++--
 .../c-c++-common/torture/attr-sym-alias-1.c        | 39 ++++++++++
 .../c-c++-common/torture/attr-sym-alias-2.c        | 13 ++++
 .../c-c++-common/torture/attr-sym-alias-3.c        | 41 +++++++++++
 .../c-c++-common/torture/attr-sym-alias-4.c        | 28 ++++++++
 .../c-c++-common/torture/attr-sym-alias-5.c        | 30 ++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C    | 72 +++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C    | 26 +++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C    | 83 ++++++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C    | 28 ++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C    | 14 ++++
 gcc/varpool.cc                                     |  3 +
 33 files changed, 832 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 ad0be511d48..b8de16ebf7c 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 ``sym_alias`` 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 fd15459203a..20045698962 100644
--- a/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
+++ b/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
@@ -4274,6 +4274,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__ ((__sym_alias__ ("Ctor_For_Animal")))
         Animal() {Age_Count = 0;};
       private:
         int Age_Count;
@@ -4306,6 +4307,7 @@ both Carnivore and Domestic, that is:
         virtual int  Number_Of_Teeth ();
         virtual void Set_Owner (char* Name);
 
+        __attribute__ ((__sym_alias__ ("Ctor_For_Dog"))) // mnemonic alias
         Dog(); // Constructor
       private:
         int  Tooth_Count;
@@ -4344,7 +4346,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;
@@ -4360,7 +4363,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,7 +4385,8 @@ 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.
+mangled name (or an alias) 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.cc b/gcc/attribs.cc
index c7209c26acc..3ef07c885d5 100644
--- a/gcc/attribs.cc
+++ b/gcc/attribs.cc
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "target.h"
 #include "tree.h"
+#include "cgraph.h"
 #include "stringpool.h"
 #include "diagnostic-core.h"
 #include "attribs.h"
@@ -825,7 +826,8 @@ decl_attributes (tree *node, tree attributes, int flags,
 
       if (TYPE_P (*anode)
 	  && (flags & (int) ATTR_FLAG_TYPE_IN_PLACE)
-	  && COMPLETE_TYPE_P (*anode))
+	  && COMPLETE_TYPE_P (*anode)
+	  && !is_attribute_p ("sym_alias", name))
 	{
 	  warning (OPT_Wattributes, "type attributes ignored after type is already defined");
 	  continue;
@@ -2640,6 +2642,70 @@ attr_access::array_as_string (tree type) const
   return typstr;
 }
 
+/* Create a sym attribute for DECL to be visible with linkage name ID.  */
+
+tree
+create_sym_alias_decl (tree decl, tree id)
+{
+  const char *attr_str = "sym_alias";
+
+  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;
+
+      tree attr_name = get_identifier (attr_str);
+      error_at (DECL_SOURCE_LOCATION (decl),
+		"duplicate symbol name %qE in %qE attribute of %qD",
+		id, attr_name, decl);
+      inform (DECL_SOURCE_LOCATION (sym_node->decl),
+	      "already used by %qD", sym_node->decl);
+    }
+
+  tree clone = copy_node (decl);
+  DECL_ATTRIBUTES (clone) = remove_attribute (attr_str,
+					      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_extra_name_alias (clone, decl);
+    }
+  else
+    {
+      cgraph_node::create_same_body_alias (clone, decl);
+    }
+
+  return clone;
+}
+
+/* Create decls for all sym aliases requested in DECL's attributes.  */
+
+void
+create_sym_alias_decls (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl)
+      || DECL_ABSTRACT_P (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      create_sym_alias_decl (decl, id);
+    }
+}
+
 #if CHECKING_P
 
 namespace selftest
diff --git a/gcc/attribs.h b/gcc/attribs.h
index 84a43658a70..156f2c20588 100644
--- a/gcc/attribs.h
+++ b/gcc/attribs.h
@@ -398,4 +398,11 @@ extern void init_attr_rdwr_indices (rdwr_map *, tree);
 extern attr_access *get_parm_access (rdwr_map &, tree,
 				     tree = current_function_decl);
 
+extern tree create_sym_alias_decl (tree, tree);
+extern void create_sym_alias_decls (tree);
+
+#define FOR_EACH_SYM_ALIAS(sym, attrs)					\
+  for (tree sym = lookup_attribute ("sym_alias", (attrs));		\
+       sym; sym = lookup_attribute ("sym_alias", TREE_CHAIN (sym)))
+
 #endif // GCC_ATTRIBS_H
diff --git a/gcc/c-family/c-ada-spec.cc b/gcc/c-family/c-ada-spec.cc
index 050994d8416..5042b9cfecd 100644
--- a/gcc/c-family/c-ada-spec.cc
+++ b/gcc/c-family/c-ada-spec.cc
@@ -1431,6 +1431,13 @@ pp_ada_tree_identifier (pretty_printer *buffer, tree node, tree type,
 static void
 pp_asm_name (pretty_printer *buffer, tree t)
 {
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (t))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.cc b/gcc/c-family/c-attribs.cc
index 2b20e58c922..392437eab3b 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -108,7 +108,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_sym_alias_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,
@@ -388,6 +389,8 @@ const struct attribute_spec c_common_attribute_table[] =
 			      handle_alias_attribute, NULL },
   { "weakref",                0, 1, true,  false, false, false,
 			      handle_weakref_attribute, NULL },
+  { "sym_alias",              1, 1, false,  false, false, false,
+			      handle_sym_alias_attribute, NULL },
   { "no_instrument_function", 0, 0, true,  false, false, false,
 			      handle_no_instrument_function_attribute,
 			      NULL },
@@ -3002,7 +3005,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
@@ -3012,7 +3015,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
@@ -3022,6 +3025,29 @@ handle_alias_attribute (tree *node, tree name, tree args,
   return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs);
 }
 
+/* Handle a "sym_alias" attribute; arguments as in struct
+   attribute_spec.handler.  */
+
+static tree
+handle_sym_alias_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 (decl_in_symtab_p (node))
+    *no_add_attrs = false;
+  else if (TYPE_P (node) && c_dialect_cxx ())
+    *no_add_attrs = false;
+  else
+    return error_mark_node;
+
+  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.  */
 
@@ -3155,6 +3181,7 @@ handle_copy_attribute (tree *node, tree name, tree args,
 	      || is_attribute_p ("visibility", atname)
 	      || is_attribute_p ("weak", atname)
 	      || is_attribute_p ("weakref", atname)
+	      || is_attribute_p ("sym_alias", atname)
 	      || is_attribute_p ("target_clones", atname))
 	    continue;
 
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index cf1df82c0f4..e5a1ee54cb3 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -3119,6 +3119,8 @@ duplicate_decls (tree newdecl, tree olddecl)
 
   merge_decls (newdecl, olddecl, newtype, oldtype);
 
+  symtab_node::remap_sym_alias_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.cc b/gcc/cgraph.cc
index f93259a8c70..0bef2179e89 100644
--- a/gcc/cgraph.cc
+++ b/gcc/cgraph.cc
@@ -523,6 +523,8 @@ cgraph_node::create (tree decl)
   node->register_symbol ();
   maybe_record_nested_function (node);
 
+  create_sym_alias_decls (decl);
+
   return node;
 }
 
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index cfdd9f693a8..cb5ac01f7e2 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -327,6 +327,10 @@ public:
   /* Return DECL that alias is aliasing.  */
   inline tree get_alias_target_tree ();
 
+  /* Remap sym alias nodes recorded as aliasing REPLACED to alias REPLACEMENT
+     instead.  */
+  static void remap_sym_alias_target (tree replaced, tree replacement);
+
   /* Set section for symbol and its aliases.  */
   void set_section (const char *section);
 
diff --git a/gcc/cgraphunit.cc b/gcc/cgraphunit.cc
index bccd2f2abb5..eb2d05094e9 100644
--- a/gcc/cgraphunit.cc
+++ b/gcc/cgraphunit.cc
@@ -1175,7 +1175,7 @@ analyze_functions (bool first_time)
      C++ FE is confused about the COMDAT groups being right.  */
   if (symtab->cpp_implicit_aliases_done)
     FOR_EACH_SYMBOL (node)
-      if (node->cpp_implicit_alias)
+      if (node->cpp_implicit_alias && node->analyzed)
 	  node->fixup_same_cpp_alias_visibility (node->get_alias_target ());
   build_type_inheritance_graph ();
 
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index 6fdb56abfb9..fa8fc63db82 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -4896,6 +4896,68 @@ check_methods (tree t)
     }
 }
 
+/* Adjust sym alias name for CLONE, cloned from FN and named NAME,
+   if it is a cdtor, and drop the sym alias from other clones.  */
+
+void
+adjust_clone_attributes (tree fn, tree clone, tree name, bool skip_copy_p)
+{
+  if (IDENTIFIER_CDTOR_P (name))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	{
+	  found = true;
+	  break;
+	}
+
+      if (found
+	  && (name == complete_ctor_identifier
+	      || name == complete_dtor_identifier))
+	{
+	  /* Reuse the sym alias decls created for the primary cdtor
+	     decl.  */
+	  symtab_node::remap_sym_alias_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);
+
+	  if (!skip_copy_p)
+	    DECL_ATTRIBUTES (clone) = copy_list (DECL_ATTRIBUTES (clone));
+
+	  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	    {
+	      /* We need to copy this even with skip_copy_p, because
+		 even then copying was shallow.  */
+	      TREE_VALUE (sym) = copy_list (TREE_VALUE (sym));
+	      /* Append suf to the sym alias name.  */
+	      tree str = TREE_VALUE (TREE_VALUE (sym));
+	      char *symname = concat (TREE_STRING_POINTER (str), suf, NULL);
+	      str = build_string (TREE_STRING_LENGTH (str) + xlen, symname);
+	      TREE_VALUE (TREE_VALUE (sym)) = str;
+	      free (symname);
+	    }
+
+	  if (symtab_node::get (clone))
+	    create_sym_alias_decls (clone);
+	}
+    }
+  else
+    DECL_ATTRIBUTES (clone)
+      = remove_attribute ("sym_alias", DECL_ATTRIBUTES (clone));
+}
+
 /* FN is constructor, destructor or operator function.  Clone the
    declaration to create a NAME'd variant.  NEED_VTT_PARM_P and
    OMIT_INHERITED_PARMS_P are relevant if it's a cdtor.  */
@@ -5065,6 +5127,8 @@ build_clone (tree fn, tree name, bool need_vtt_parm_p,
   DECL_CHAIN (clone) = DECL_CHAIN (fn);
   DECL_CHAIN (fn) = clone;
 
+  adjust_clone_attributes (fn, clone, name);
+
   return clone;
 }
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 964af1ddd85..2bafed4e993 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5787,6 +5787,8 @@ struct GTY((for_user)) spec_entry
 
 extern int current_class_depth;
 
+void adjust_clone_attributes (tree fn, tree clone, tree name, bool = false);
+
 /* in decl.cc */
 
 /* An array of static vars & fns.  */
@@ -7022,6 +7024,7 @@ extern void do_push_parm_decls			(tree, tree, tree *);
 extern tree do_aggregate_paren_init		(tree, tree);
 
 /* in decl2.cc */
+extern void update_sym_alias_interface		(tree);
 extern void record_mangling			(tree, bool);
 extern void overwrite_mangling			(tree, tree);
 extern void note_mangling_alias			(tree, tree);
@@ -7596,6 +7599,7 @@ extern bool emit_tinfo_decl			(tree);
 extern unsigned get_pseudo_tinfo_index		(tree);
 extern tree get_pseudo_tinfo_type		(unsigned);
 extern tree build_if_nonnull			(tree, tree, tsubst_flags_t);
+extern void update_tinfo_sym_alias		(tree);
 
 /* in search.cc */
 extern tree get_parent_with_private_access 	(tree decl, tree binfo);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 2f2dbb8d107..5636d694b68 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -3237,6 +3237,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
 	      && TREE_STATIC (olddecl))))
     make_decl_rtl (olddecl);
 
+  symtab_node::remap_sym_alias_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.
@@ -3260,6 +3262,7 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
       FOR_EACH_CLONE (clone, olddecl)
 	{
 	  DECL_ATTRIBUTES (clone) = DECL_ATTRIBUTES (olddecl);
+	  adjust_clone_attributes (olddecl, clone, DECL_NAME (clone));
 	  DECL_PRESERVE_P (clone) |= DECL_PRESERVE_P (olddecl);
 	}
     }
@@ -10789,6 +10792,7 @@ grokfndecl (tree ctype,
     {
       cplus_decl_attributes (&decl, *attrlist, 0);
       *attrlist = NULL_TREE;
+      create_sym_alias_decls (decl);
     }
 
   if (DECL_HAS_CONTRACTS_P (decl))
diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
index 9e666e5eece..c7f009beaf0 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -1844,6 +1844,9 @@ cplus_decl_attributes (tree *decl, tree attributes, int flags)
   if (late_attrs)
     save_template_attributes (late_attrs, decl, flags);
 
+  if (TYPE_P (*decl) && attributes)
+    update_tinfo_sym_alias (*decl);
+
   /* Propagate deprecation out to the template.  */
   if (TREE_DEPRECATED (*decl))
     if (tree ti = get_template_info (*decl))
@@ -2200,6 +2203,47 @@ 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 sym aliases.  */
+
+void
+update_sym_alias_interface (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.  */
 
@@ -3007,6 +3051,8 @@ determine_visibility (tree decl)
        translation unit, we can make the type internal.  */
     constrain_visibility (decl, VISIBILITY_ANON, false);
 
+  update_sym_alias_interface (decl);
+
   /* If visibility changed and DECL already has DECL_RTL, ensure
      symbol flags are updated.  */
   if ((DECL_VISIBILITY (decl) != orig_visibility
@@ -3269,6 +3315,8 @@ tentative_decl_linkage (tree decl)
       else if (VAR_P (decl))
 	maybe_commonize_var (decl);
     }
+
+  update_sym_alias_interface (decl);
 }
 
 /* DECL is a FUNCTION_DECL or VAR_DECL.  If the object file linkage
@@ -3503,6 +3551,8 @@ import_export_decl (tree decl)
     }
 
   DECL_INTERFACE_KNOWN (decl) = 1;
+
+  update_sym_alias_interface (decl);
 }
 
 /* Return an expression that performs the destruction of DECL, which
diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc
index d19ea5d121c..f877780b2ac 100644
--- a/gcc/cp/name-lookup.cc
+++ b/gcc/cp/name-lookup.cc
@@ -22,9 +22,11 @@ along with GCC; see the file COPYING3.  If not see
 #define INCLUDE_MEMORY
 #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"
@@ -3479,6 +3481,15 @@ push_local_extern_decl_alias (tree decl)
 	  /* Adjust visibility.  */
 	  determine_visibility (alias);
 	}
+      else if (DECL_P (alias))
+	DECL_ATTRIBUTES (alias)
+	  = targetm.merge_decl_attributes (alias, decl);
+      if (DECL_P (alias))
+	{
+	  symtab_node::remap_sym_alias_target (decl, alias);
+	  DECL_ATTRIBUTES (decl)
+	    = remove_attribute ("sym_alias", DECL_ATTRIBUTES (alias));
+	}
     }
 
   retrofit_lang_decl (decl);
diff --git a/gcc/cp/optimize.cc b/gcc/cp/optimize.cc
index 9e8926e4cc6..4a2e8cb4354 100644
--- a/gcc/cp/optimize.cc
+++ b/gcc/cp/optimize.cc
@@ -528,9 +528,12 @@ maybe_clone_body (tree fn)
       DECL_VISIBILITY_SPECIFIED (clone) = DECL_VISIBILITY_SPECIFIED (fn);
       DECL_DLLIMPORT_P (clone) = DECL_DLLIMPORT_P (fn);
       DECL_ATTRIBUTES (clone) = clone_attrs (DECL_ATTRIBUTES (fn));
+      adjust_clone_attributes (fn, clone, DECL_NAME (clone), true);
       DECL_DISREGARD_INLINE_LIMITS (clone) = DECL_DISREGARD_INLINE_LIMITS (fn);
       set_decl_section_name (clone, fn);
 
+      update_sym_alias_interface (clone);
+
       /* Adjust the parameter names and locations.  */
       parm = DECL_ARGUMENTS (fn);
       clone_parm = DECL_ARGUMENTS (clone);
diff --git a/gcc/cp/rtti.cc b/gcc/cp/rtti.cc
index 7878929c246..4bb7b8c8c6e 100644
--- a/gcc/cp/rtti.cc
+++ b/gcc/cp/rtti.cc
@@ -28,8 +28,10 @@ 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"
+#include "cgraph.h"
 
 /* C++ returns type information to the user in struct type_info
    objects. We also use type information to implement dynamic_cast and
@@ -479,8 +481,13 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
 	  = build_tree_list (get_identifier ("non overlapping"),
 			     NULL_TREE);
       else
+	/* Share the non overlapping attribute, without assuming it's
+	   the only attribute, but assuming it's the last if it's
+	   present.  There may be sym aliases too, and those are not
+	   to be shared.  */
 	DECL_ATTRIBUTES (d)
-	  = DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]);
+	  = lookup_attribute ("non overlapping",
+			      DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]));
 
       /* Mark the variable as undefined -- but remember that we can
 	 define it later if we need to do so.  */
@@ -492,6 +499,16 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
       if (CLASS_TYPE_P (type))
 	CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type)) = d;
 
+      /* Copy sym alias attributes from the type to the rtti obj decl.  */
+      tree *attrs = &DECL_ATTRIBUTES (d);
+      FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+	{
+	  tree attr = tree_cons (TREE_PURPOSE (sym), TREE_VALUE (sym), *attrs);
+	  *attrs = attr;
+	  attrs = &TREE_CHAIN (attr);
+	}
+      create_sym_alias_decls (d);
+
       /* Add decl to the global array of tinfo decls.  */
       vec_safe_push (unemitted_tinfo_decls, d);
     }
@@ -499,6 +516,58 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
   return d;
 }
 
+/* After modifying the attributes of TYPE, check whether tinfo was
+   already created and, if so, add to it any sym alias attributes
+   that were not already present.  */
+
+void
+update_tinfo_sym_alias (tree type)
+{
+  if (!TYPE_SIZE (type) || !CLASS_TYPE_P (type))
+    return;
+
+  tree d = CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type));
+  if (!d)
+    return;
+
+  bool first = true;
+  symtab_node *node = NULL;
+
+  tree *attrs = &DECL_ATTRIBUTES (d);
+  FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (d_sym, *attrs)
+	if (TREE_VALUE (sym) == TREE_VALUE (d_sym))
+	  {
+	    found = true;
+	    break;
+	  }
+
+      if (found)
+	continue;
+
+      tree attr = tree_cons (TREE_PURPOSE (sym),
+			     TREE_VALUE (sym),
+			     *attrs);
+      *attrs = attr;
+      attrs = &TREE_CHAIN (attr);
+
+      if (first)
+	{
+	  first = false;
+	  node = symtab_node::get (d);
+	}
+
+      if (!node)
+	continue;
+
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+      create_sym_alias_decl (d, id);
+    }
+}
+
 /* Return the type_info object for TYPE.  */
 
 tree
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index e6de0815846..96674e2d857 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -4141,6 +4141,39 @@ Function Attributes}, @ref{PowerPC Function Attributes},
 @ref{Nios II Function Attributes}, and @ref{S/390 Function Attributes}
 for details.
 
+@cindex @code{sym_alias} function attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} attribute causes @var{name} to be output as an
+alias to the definition.  For instance,
+
+@smallexample
+void f (uint64_t) __attribute__ ((__sym_alias__ ("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 for aliases,
+and may not be available on all targets.
+
 @cindex @code{symver} function attribute
 @item symver ("@var{name2}@@@var{nodename}")
 On ELF targets this attribute creates a symbol version.  The @var{name2} part
@@ -8069,6 +8102,10 @@ will be placed in new, unique sections.
 
 This additional functionality requires Binutils version 2.36 or later.
 
+@cindex @code{sym_alias} variable attribute
+@item sym_alias ("@var{name}")
+See @pxref{Common Function Attributes}.
+
 @cindex @code{uninitialized} variable attribute
 @item uninitialized
 This attribute, attached to a variable with automatic storage, means that
@@ -9164,6 +9201,21 @@ is not supported; that is to say, if a given scalar object can be accessed
 through distinct types that assign a different storage order to it, then the
 behavior is undefined.
 
+@cindex @code{sym_alias} type attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} type 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__ ((__sym_alias__ ("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.
+
 @cindex @code{transparent_union} type attribute
 @item transparent_union
 
diff --git a/gcc/symtab.cc b/gcc/symtab.cc
index 0470509a98d..0812088e59f 100644
--- a/gcc/symtab.cc
+++ b/gcc/symtab.cc
@@ -1943,6 +1943,42 @@ symtab_node::noninterposable_alias (symtab_node *node, void *data)
   return false;
 }
 
+/* Remap sym alias nodes recorded as aliasing REPLACED to alias
+   REPLACEMENT instead.  */
+
+void
+symtab_node::remap_sym_alias_target (tree replaced, tree replacement)
+{
+  if (!decl_in_symtab_p (replacement)
+      || !symtab_node::get (replacement))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (replaced))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      symtab_node *sym_node = symtab_node::get_for_asmname (id);
+
+      if (!sym_node)
+	{
+	  create_sym_alias_decl (replacement, id);
+	  continue;
+	}
+
+      gcc_assert (!sym_node->analyzed);
+      if (sym_node->alias_target != replaced)
+	continue;
+
+      sym_node->definition = 0;
+
+      if (VAR_P (replaced))
+	varpool_node::create_extra_name_alias (sym_node->decl, replacement);
+      else
+	cgraph_node::create_same_body_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-sym-alias-neg.c b/gcc/testsuite/c-c++-common/attr-sym-alias-neg.c
new file mode 100644
index 00000000000..33172ca6f02
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/attr-sym-alias-neg.c
@@ -0,0 +1,18 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int f () {
+  static int x __attribute__ ((sym_alias ("f_x")));
+  int i __attribute__ ((sym_alias ("f_i"))); /* { dg-warning "ignored" } */
+  static int sx __attribute__ ((alias ("f_x"))); /* { dg-warning "ignored" } */
+  extern int xx __attribute__ ((alias ("f_x"))); /* { dg-warning "ignored" } */
+  return i;
+}
+
+/* ??? Should these be accepted?  */
+extern int xr __attribute__ ((alias ("f_x"))); /* { dg-error "undefined" } */
+int dr __attribute__ ((alias ("f_x"))); /* { dg-error "undefined" } */
+
+struct s {
+  int j __attribute__ ((sym_alias ("s_j"))); /* { dg-warning "ignored" } */
+};
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-1.c b/gcc/testsuite/c-c++-common/goacc/declare-1.c
index 46ee01b6759..f2842893318 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-1.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-1.c
@@ -113,11 +113,11 @@ f_2 (void)
   int va3;
 #pragma acc declare device_resident(va3)
 
-#ifndef __cplusplus
+#if 0
   /* TODO PR90868
 
-     C: "error: variable '[...]' used more than once with '#pragma acc declare'".  */
-#else
+     "error: variable '[...]' used more than once with '#pragma acc declare'".  */
+
   extern int ve0;
 #pragma acc declare create(ve0)
 
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-2.c b/gcc/testsuite/c-c++-common/goacc/declare-2.c
index e2e22be57e9..aec59b69754 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-2.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-2.c
@@ -137,25 +137,25 @@ void
 f_pr90868_2 (void)
 {
   extern int we0;
-#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" } */
 
   extern int we1;
-#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" } */
 
   extern int *we2;
-#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" } */
 
   extern int we3;
-#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" } */
 
   extern int we4;
-#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" } */
 
   extern int we5;
-#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" } */
  
   extern int we6;
-#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" } */
 }
 
 
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
new file mode 100644
index 00000000000..61af50cb552
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
@@ -0,0 +1,39 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+int var_a = 1;
+
+void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+
+void
+foo_a ()
+{
+}
+
+
+int var_b;
+extern int var_b __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+
+void
+foo_b ()
+{
+}
+
+void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+
+
+int var_c __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+
+void __attribute__ ((__sym_alias__ ("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-sym-alias-2.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
new file mode 100644
index 00000000000..6f0d55ca42c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+struct s
+{
+  int mem __attribute__ ((__sym_alias__ ("MEMFOO"))); /* { dg-warning "attribute ignored" } */
+};
+
+void foo()
+{
+  extern void bar () __attribute__ ((__sym_alias__ ("FOOBAR")));
+  int var __attribute__ ((__sym_alias__ ("FOOVAR"))); /* { dg-warning "attribute ignored" } */
+}
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
new file mode 100644
index 00000000000..0052485a30a
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
@@ -0,0 +1,41 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int var_a = 1;
+
+void
+foo_a ()
+{
+  extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+}
+
+#if 0 // __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__ ((__sym_alias__ ("FOOVAR_B")));
+}
+
+int var_b;
+
+void __attribute__ ((__sym_alias__ ("FOOBAR_C")))
+foo_c ()
+{
+  void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+  /* Another sym for var_b.  */
+  extern int var_b __attribute__ ((__sym_alias__ ("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-sym-alias-4.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
new file mode 100644
index 00000000000..2beafa46379
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-require-alias "" } */
+
+int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A"))) = 42;
+
+int __attribute__ ((__sym_alias__ ("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/c-c++-common/torture/attr-sym-alias-5.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
new file mode 100644
index 00000000000..8e4bdad919b
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
@@ -0,0 +1,30 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+/* { dg-require-visibility "" } */
+
+extern void __attribute__ ((sym_alias ("F_hid"), visibility ("hidden")))
+f ();
+
+void
+f () {
+}
+
+extern void __attribute__ ((sym_alias ("G_prt")))
+g ();
+
+void __attribute__ ((visibility ("protected")))
+g () {
+}
+
+extern void __attribute__ ((sym_alias ("H_ntr")))
+h ();
+
+void __attribute__ ((visibility ("internal")))
+h ();
+
+void h () {
+}
+
+/* { dg-final { scan-assembler "hidden.*F_hid" } } */
+/* { dg-final { scan-assembler "protected.*G_prt" } } */
+/* { dg-final { scan-assembler "internal.*H_ntr" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
new file mode 100644
index 00000000000..579eda1a473
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
@@ -0,0 +1,72 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+class __attribute__ ((__sym_alias__ ("FOOCLS_A"),
+		      __sym_alias__ ("FOOCLS_A_Dupe"))) foo {
+  static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  void __attribute__ ((__sym_alias__ ("FOOBAR_A"))) bar ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+int foo::var = 1;
+
+foo::foo () {}
+
+void foo::bar () {}
+
+namespace b {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_B"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_B"))) foo ();
+    void __attribute__ ((__sym_alias__ ("FOOBAR_B"))) bar () {}
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_B"))) ~foo() {}
+  };
+
+  int foo::var = 2;
+
+  foo::foo () {
+    void (foo::*pbar)() = &foo::bar;
+  }
+}
+
+namespace c {
+  namespace cd {
+    class __attribute__ ((__sym_alias__ ("FOOCLS_C"))) foo {
+      static int var __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+      __attribute__ ((__sym_alias__ ("FOOCTR_C"))) foo () {
+	void (foo::*pbar)() = &foo::bar;
+      }
+      void __attribute__ ((__sym_alias__ ("FOOBAR_C"))) bar () {}
+      virtual __attribute__ ((__sym_alias__ ("FOODTR_C"))) ~foo() {}
+    };
+
+    int foo::var = 3;
+  }
+}
+
+/* { dg-final { scan-assembler "FOOCLS_A" } } */
+/* { dg-final { scan-assembler "FOOCLS_A_Dupe" } } */
+/* { 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-sym-alias-2.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
new file mode 100644
index 00000000000..6facfcc2cd9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+namespace {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_A"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo ();
+    void __attribute__ ((__sym_alias__ ("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-sym-alias-3.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
new file mode 100644
index 00000000000..42c7288ad4a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
@@ -0,0 +1,83 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+// sym can be applied to template function explicit instantiations.
+
+template <typename T>
+void
+fn(T) {
+};
+
+template void __attribute__ ((__sym_alias__ ("FOOFUN_UINT"))) fn<>(unsigned int);
+template void __attribute__ ((__sym_alias__ ("FOOFUN_LONG"))) fn<>(long);
+
+template<> void __attribute__ ((__sym_alias__ ("FOOFUN_CHAR"))) fn<>(char) {}
+
+
+template <typename T = void>
+struct
+foo {
+  virtual ~foo() {}
+
+  virtual void virtfun() {}
+
+  static void stfun() {}
+  void inlfun() {}
+};
+
+// Explicitly instantiate members before the enclosing class.
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_CHAR_VIRT"))) foo<char>::virtfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_CHAR_TI"))) foo<char>;
+
+// Though they're only output if the enclosing class is.
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_LONG_VIRT"))) foo<long>::virtfun();
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_LONG_TI_X"))) foo<long>;
+
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_VOID_ST"))) foo<void>::stfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_VOID_TI"))) foo<>;
+
+
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_X"))) foo<short>;
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_ST"))) foo<short>::stfun();
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_INL"))) foo<short>::inlfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_D"))) foo<short>;
+
+// Explicit specializations work too.
+
+template <>
+struct  __attribute__ ((__sym_alias__ ("FOOCLS_INT_TI")))
+foo<int>
+{
+  virtual ~foo() {}
+  virtual void __attribute__ ((__sym_alias__ ("FOOCLS_INT_VIRT"))) virtfun() {}
+};
+
+/* { dg-final { scan-assembler "FOOFUN_UINT" } } */
+/* { dg-final { scan-assembler "FOOFUN_LONG" } } */
+/* { dg-final { scan-assembler "FOOFUN_CHAR" } } */
+
+/* { dg-final { scan-assembler "FOOCLS_VOID_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_VOID_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_X" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_INL" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_D" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_TI_X" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_VIRT" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
new file mode 100644
index 00000000000..59b779de35d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+template <typename T = void>
+class
+__attribute__ ((__sym_alias__ ("FOOCLS")))
+foo // { dg-error "duplicate|already" }
+{
+  virtual ~foo() {}
+
+  template <typename U>
+  void
+    __attribute__ ((__sym_alias__ ("FOOTMF")))
+    tmemfun () {} // { dg-error "duplicate|already" }
+};
+
+template <typename T>
+void
+__attribute__ ((__sym_alias__ ("FOOTFN")))
+fn(T) { // { dg-error "duplicate|already" }
+};
+
+template class foo<>;
+template class foo<int>;
+template void foo<>::tmemfun<void>();
+template void foo<int>::tmemfun<void>();
+template void fn<>(int);
+template void fn<>(long);
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
new file mode 100644
index 00000000000..45d59b953c3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
@@ -0,0 +1,14 @@
+/* { dg-do compile { target c++11 } } */
+/* { dg-require-alias "" } */
+
+struct foo {
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+foo::foo () {}
+
+// Make sure the inherited cdtors don't duplicate the syms.
+struct bar : foo {
+  using foo::foo;
+};
diff --git a/gcc/varpool.cc b/gcc/varpool.cc
index e7b51b15e4a..33649906d1d 100644
--- a/gcc/varpool.cc
+++ b/gcc/varpool.cc
@@ -163,6 +163,9 @@ varpool_node::get_create (tree decl)
     }
 
   node->register_symbol ();
+
+  create_sym_alias_decls (decl);
+
   return node;
 }

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [gcc(refs/users/aoliva/heads/testme)] Introduce attribute sym_alias
@ 2023-11-30 10:58 Alexandre Oliva
  0 siblings, 0 replies; 33+ messages in thread
From: Alexandre Oliva @ 2023-11-30 10:58 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:ca4e58a7a9508ada76a8d0aab2de9c3f64e6ade1

commit ca4e58a7a9508ada76a8d0aab2de9c3f64e6ade1
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Fri Jul 14 03:45:14 2023 -0300

    Introduce attribute sym_alias
    
    This patch introduces an attribute to add extra asm names (aliases)
    for a decl 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 class types, namespace-scoped
    variables and functions, static data members, member functions,
    explicit instantiations and specializations of template functions,
    members and classes.
    
    When applied to constructors or destructor, additional sym aliases
    with _Base and _Del suffixes are defined for variants other than
    complete-object ones.  This changes the assumption that clones always
    carry the same attributes as their abstract declarations, so there is
    now a function to adjust them.
    
    C++ also had a bug in which attributes from local extern declarations
    failed to be propagated to a preexisting corresponding
    namespace-scoped decl.  I've fixed that, and adjusted acc tests that
    distinguished between C and C++ in this regard.
    
    Applying the attribute to class types is only valid in C++, and the
    effect is to attach the alias to the RTTI object associated with the
    class type.
    
    for  gcc/ChangeLog
    
            * attribs.cc: Include cgraph.h.
            (decl_attributes): Allow late introduction of sym_alias in
            types.
            (create_sym_alias_decl, create_sym_alias_decls): New.
            * attribs.h: Declare them.
            (FOR_EACH_SYM_ALIAS): New macro.
            * cgraph.cc (cgraph_node::create): Create sym_alias decls.
            * varpool.cc (varpool_node::get_create): Create sym_alias
            decls.
            * cgraph.h (symtab_node::remap_sym_alias_target): New.
            * symtab.cc (symtab_node::remap_sym_alias_target): Define.
            * cgraphunit.cc (cgraph_node::analyze): Create alias_target
            node if needed.
            (analyze_functions): Fixup visibility of implicit alias only
            after its node is analyzed.
            * doc/extend.texi (sym_alias): Document for variables,
            functions and types.
    
    for  gcc/ada/ChangeLog
    
            * doc/gnat_rm/interfacing_to_other_languages.rst: Mention
            attribute sym_alias to give RTTI symbols mnemonic names.
            * doc/gnat_ugn/the_gnat_compilation_model.rst: Mention
            aliases.  Fix incorrect ref to C1 ctor variant.
    
    for  gcc/c-family/ChangeLog
    
            * c-ada-spec.cc (pp_asm_name): Use first sym_alias if
            available.
            * c-attribs.cc (handle_sym_alias_attribute): New.
            (c_common_attribute_table): Add sym_alias.
            (handle_copy_attribute): Do not copy sym_alias attribute.
    
    for  gcc/c/ChangeLog
    
            * c-decl.cc (duplicate_decls): Remap sym_alias target.
    
    for  gcc/cp/ChangeLog
    
            * class.cc (adjust_clone_attributes): New.
            (copy_fndecl_with_name, build_clone): Call it.
            * cp-tree.h (adjust_clone_attributes): Declare.
            (update_sym_alias_interface): Declare.
            (update_tinfo_sym_alias): Declare.
            * decl.cc (duplicate_decls): Remap sym_alias target.
            Adjust clone attributes.
            (grokfndecl): Tentatively create sym_alias decls after
            adding attributes in e.g. a template member function explicit
            instantiation.
            * decl2.cc (cplus_decl_attributes): Update tinfo sym_alias.
            (copy_interface, update_sym_alias_interface): New.
            (determine_visibility): Update sym_alias interface.
            (tentative_decl_linkage, import_export_decl): Likewise.
            * name-lookup.cc: Include target.h and cgraph.h.
            (push_local_extern_decl_alias): Merge attributes with
            namespace-scoped decl, and drop duplicate sym_alias.
            * optimize.cc (maybe_clone_body): Re-adjust attributes after
            cloning them.  Update sym_alias interface.
            * rtti.cc: Include attribs.h and cgraph.h.
            (get_tinfo_decl): Copy sym_alias attributes from type to tinfo
            decl.  Create sym_alias decls.
            (update_tinfo_sym_alias): New.
    
    for  gcc/testsuite/ChangeLog
    
            * c-c++-common/goacc/declare-1.c: Adjust.
            * c-c++-common/goacc/declare-2.c: Adjust.
            * c-c++-common/torture/attr-sym-alias-1.c: New.
            * c-c++-common/torture/attr-sym-alias-2.c: New.
            * c-c++-common/torture/attr-sym-alias-3.c: New.
            * c-c++-common/torture/attr-sym-alias-4.c: New.
            * c-c++-common/torture/attr-sym-alias-5.c: New.
            * c-c++-common/attr-sym-alias-neg.c: New.
            * g++.dg/torture/attr-sym-alias-1.C: New.
            * g++.dg/torture/attr-sym-alias-2.C: New.
            * g++.dg/torture/attr-sym-alias-3.C: New.
            * g++.dg/torture/attr-sym-alias-4.C: New.
            * g++.dg/torture/attr-sym-alias-5.C: New.

Diff:
---
 .../doc/gnat_rm/interfacing_to_other_languages.rst |  6 ++
 .../doc/gnat_ugn/the_gnat_compilation_model.rst    | 10 ++-
 gcc/attribs.cc                                     | 68 +++++++++++++++++-
 gcc/attribs.h                                      |  7 ++
 gcc/c-family/c-ada-spec.cc                         |  7 ++
 gcc/c-family/c-attribs.cc                          | 33 ++++++++-
 gcc/c/c-decl.cc                                    |  2 +
 gcc/cgraph.cc                                      |  2 +
 gcc/cgraph.h                                       |  4 ++
 gcc/cgraphunit.cc                                  |  2 +-
 gcc/cp/class.cc                                    | 64 +++++++++++++++++
 gcc/cp/cp-tree.h                                   |  4 ++
 gcc/cp/decl.cc                                     |  4 ++
 gcc/cp/decl2.cc                                    | 50 +++++++++++++
 gcc/cp/name-lookup.cc                              | 11 +++
 gcc/cp/optimize.cc                                 |  3 +
 gcc/cp/rtti.cc                                     | 71 +++++++++++++++++-
 gcc/doc/extend.texi                                | 52 ++++++++++++++
 gcc/symtab.cc                                      | 36 ++++++++++
 gcc/testsuite/c-c++-common/attr-sym-alias-neg.c    | 18 +++++
 gcc/testsuite/c-c++-common/goacc/declare-1.c       |  6 +-
 gcc/testsuite/c-c++-common/goacc/declare-2.c       | 14 ++--
 .../c-c++-common/torture/attr-sym-alias-1.c        | 39 ++++++++++
 .../c-c++-common/torture/attr-sym-alias-2.c        | 13 ++++
 .../c-c++-common/torture/attr-sym-alias-3.c        | 41 +++++++++++
 .../c-c++-common/torture/attr-sym-alias-4.c        | 28 ++++++++
 .../c-c++-common/torture/attr-sym-alias-5.c        | 18 +++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C    | 72 +++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C    | 26 +++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C    | 83 ++++++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C    | 28 ++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C    | 14 ++++
 gcc/varpool.cc                                     |  3 +
 33 files changed, 820 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 ad0be511d48..b8de16ebf7c 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 ``sym_alias`` 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 fd15459203a..20045698962 100644
--- a/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
+++ b/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
@@ -4274,6 +4274,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__ ((__sym_alias__ ("Ctor_For_Animal")))
         Animal() {Age_Count = 0;};
       private:
         int Age_Count;
@@ -4306,6 +4307,7 @@ both Carnivore and Domestic, that is:
         virtual int  Number_Of_Teeth ();
         virtual void Set_Owner (char* Name);
 
+        __attribute__ ((__sym_alias__ ("Ctor_For_Dog"))) // mnemonic alias
         Dog(); // Constructor
       private:
         int  Tooth_Count;
@@ -4344,7 +4346,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;
@@ -4360,7 +4363,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,7 +4385,8 @@ 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.
+mangled name (or an alias) 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.cc b/gcc/attribs.cc
index c7209c26acc..3ef07c885d5 100644
--- a/gcc/attribs.cc
+++ b/gcc/attribs.cc
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "target.h"
 #include "tree.h"
+#include "cgraph.h"
 #include "stringpool.h"
 #include "diagnostic-core.h"
 #include "attribs.h"
@@ -825,7 +826,8 @@ decl_attributes (tree *node, tree attributes, int flags,
 
       if (TYPE_P (*anode)
 	  && (flags & (int) ATTR_FLAG_TYPE_IN_PLACE)
-	  && COMPLETE_TYPE_P (*anode))
+	  && COMPLETE_TYPE_P (*anode)
+	  && !is_attribute_p ("sym_alias", name))
 	{
 	  warning (OPT_Wattributes, "type attributes ignored after type is already defined");
 	  continue;
@@ -2640,6 +2642,70 @@ attr_access::array_as_string (tree type) const
   return typstr;
 }
 
+/* Create a sym attribute for DECL to be visible with linkage name ID.  */
+
+tree
+create_sym_alias_decl (tree decl, tree id)
+{
+  const char *attr_str = "sym_alias";
+
+  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;
+
+      tree attr_name = get_identifier (attr_str);
+      error_at (DECL_SOURCE_LOCATION (decl),
+		"duplicate symbol name %qE in %qE attribute of %qD",
+		id, attr_name, decl);
+      inform (DECL_SOURCE_LOCATION (sym_node->decl),
+	      "already used by %qD", sym_node->decl);
+    }
+
+  tree clone = copy_node (decl);
+  DECL_ATTRIBUTES (clone) = remove_attribute (attr_str,
+					      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_extra_name_alias (clone, decl);
+    }
+  else
+    {
+      cgraph_node::create_same_body_alias (clone, decl);
+    }
+
+  return clone;
+}
+
+/* Create decls for all sym aliases requested in DECL's attributes.  */
+
+void
+create_sym_alias_decls (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl)
+      || DECL_ABSTRACT_P (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      create_sym_alias_decl (decl, id);
+    }
+}
+
 #if CHECKING_P
 
 namespace selftest
diff --git a/gcc/attribs.h b/gcc/attribs.h
index 84a43658a70..156f2c20588 100644
--- a/gcc/attribs.h
+++ b/gcc/attribs.h
@@ -398,4 +398,11 @@ extern void init_attr_rdwr_indices (rdwr_map *, tree);
 extern attr_access *get_parm_access (rdwr_map &, tree,
 				     tree = current_function_decl);
 
+extern tree create_sym_alias_decl (tree, tree);
+extern void create_sym_alias_decls (tree);
+
+#define FOR_EACH_SYM_ALIAS(sym, attrs)					\
+  for (tree sym = lookup_attribute ("sym_alias", (attrs));		\
+       sym; sym = lookup_attribute ("sym_alias", TREE_CHAIN (sym)))
+
 #endif // GCC_ATTRIBS_H
diff --git a/gcc/c-family/c-ada-spec.cc b/gcc/c-family/c-ada-spec.cc
index 050994d8416..5042b9cfecd 100644
--- a/gcc/c-family/c-ada-spec.cc
+++ b/gcc/c-family/c-ada-spec.cc
@@ -1431,6 +1431,13 @@ pp_ada_tree_identifier (pretty_printer *buffer, tree node, tree type,
 static void
 pp_asm_name (pretty_printer *buffer, tree t)
 {
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (t))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.cc b/gcc/c-family/c-attribs.cc
index 2b20e58c922..392437eab3b 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -108,7 +108,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_sym_alias_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,
@@ -388,6 +389,8 @@ const struct attribute_spec c_common_attribute_table[] =
 			      handle_alias_attribute, NULL },
   { "weakref",                0, 1, true,  false, false, false,
 			      handle_weakref_attribute, NULL },
+  { "sym_alias",              1, 1, false,  false, false, false,
+			      handle_sym_alias_attribute, NULL },
   { "no_instrument_function", 0, 0, true,  false, false, false,
 			      handle_no_instrument_function_attribute,
 			      NULL },
@@ -3002,7 +3005,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
@@ -3012,7 +3015,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
@@ -3022,6 +3025,29 @@ handle_alias_attribute (tree *node, tree name, tree args,
   return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs);
 }
 
+/* Handle a "sym_alias" attribute; arguments as in struct
+   attribute_spec.handler.  */
+
+static tree
+handle_sym_alias_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 (decl_in_symtab_p (node))
+    *no_add_attrs = false;
+  else if (TYPE_P (node) && c_dialect_cxx ())
+    *no_add_attrs = false;
+  else
+    return error_mark_node;
+
+  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.  */
 
@@ -3155,6 +3181,7 @@ handle_copy_attribute (tree *node, tree name, tree args,
 	      || is_attribute_p ("visibility", atname)
 	      || is_attribute_p ("weak", atname)
 	      || is_attribute_p ("weakref", atname)
+	      || is_attribute_p ("sym_alias", atname)
 	      || is_attribute_p ("target_clones", atname))
 	    continue;
 
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index cf1df82c0f4..e5a1ee54cb3 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -3119,6 +3119,8 @@ duplicate_decls (tree newdecl, tree olddecl)
 
   merge_decls (newdecl, olddecl, newtype, oldtype);
 
+  symtab_node::remap_sym_alias_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.cc b/gcc/cgraph.cc
index f93259a8c70..0bef2179e89 100644
--- a/gcc/cgraph.cc
+++ b/gcc/cgraph.cc
@@ -523,6 +523,8 @@ cgraph_node::create (tree decl)
   node->register_symbol ();
   maybe_record_nested_function (node);
 
+  create_sym_alias_decls (decl);
+
   return node;
 }
 
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index cfdd9f693a8..cb5ac01f7e2 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -327,6 +327,10 @@ public:
   /* Return DECL that alias is aliasing.  */
   inline tree get_alias_target_tree ();
 
+  /* Remap sym alias nodes recorded as aliasing REPLACED to alias REPLACEMENT
+     instead.  */
+  static void remap_sym_alias_target (tree replaced, tree replacement);
+
   /* Set section for symbol and its aliases.  */
   void set_section (const char *section);
 
diff --git a/gcc/cgraphunit.cc b/gcc/cgraphunit.cc
index bccd2f2abb5..eb2d05094e9 100644
--- a/gcc/cgraphunit.cc
+++ b/gcc/cgraphunit.cc
@@ -1175,7 +1175,7 @@ analyze_functions (bool first_time)
      C++ FE is confused about the COMDAT groups being right.  */
   if (symtab->cpp_implicit_aliases_done)
     FOR_EACH_SYMBOL (node)
-      if (node->cpp_implicit_alias)
+      if (node->cpp_implicit_alias && node->analyzed)
 	  node->fixup_same_cpp_alias_visibility (node->get_alias_target ());
   build_type_inheritance_graph ();
 
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index 6fdb56abfb9..fa8fc63db82 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -4896,6 +4896,68 @@ check_methods (tree t)
     }
 }
 
+/* Adjust sym alias name for CLONE, cloned from FN and named NAME,
+   if it is a cdtor, and drop the sym alias from other clones.  */
+
+void
+adjust_clone_attributes (tree fn, tree clone, tree name, bool skip_copy_p)
+{
+  if (IDENTIFIER_CDTOR_P (name))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	{
+	  found = true;
+	  break;
+	}
+
+      if (found
+	  && (name == complete_ctor_identifier
+	      || name == complete_dtor_identifier))
+	{
+	  /* Reuse the sym alias decls created for the primary cdtor
+	     decl.  */
+	  symtab_node::remap_sym_alias_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);
+
+	  if (!skip_copy_p)
+	    DECL_ATTRIBUTES (clone) = copy_list (DECL_ATTRIBUTES (clone));
+
+	  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	    {
+	      /* We need to copy this even with skip_copy_p, because
+		 even then copying was shallow.  */
+	      TREE_VALUE (sym) = copy_list (TREE_VALUE (sym));
+	      /* Append suf to the sym alias name.  */
+	      tree str = TREE_VALUE (TREE_VALUE (sym));
+	      char *symname = concat (TREE_STRING_POINTER (str), suf, NULL);
+	      str = build_string (TREE_STRING_LENGTH (str) + xlen, symname);
+	      TREE_VALUE (TREE_VALUE (sym)) = str;
+	      free (symname);
+	    }
+
+	  if (symtab_node::get (clone))
+	    create_sym_alias_decls (clone);
+	}
+    }
+  else
+    DECL_ATTRIBUTES (clone)
+      = remove_attribute ("sym_alias", DECL_ATTRIBUTES (clone));
+}
+
 /* FN is constructor, destructor or operator function.  Clone the
    declaration to create a NAME'd variant.  NEED_VTT_PARM_P and
    OMIT_INHERITED_PARMS_P are relevant if it's a cdtor.  */
@@ -5065,6 +5127,8 @@ build_clone (tree fn, tree name, bool need_vtt_parm_p,
   DECL_CHAIN (clone) = DECL_CHAIN (fn);
   DECL_CHAIN (fn) = clone;
 
+  adjust_clone_attributes (fn, clone, name);
+
   return clone;
 }
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 964af1ddd85..2bafed4e993 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5787,6 +5787,8 @@ struct GTY((for_user)) spec_entry
 
 extern int current_class_depth;
 
+void adjust_clone_attributes (tree fn, tree clone, tree name, bool = false);
+
 /* in decl.cc */
 
 /* An array of static vars & fns.  */
@@ -7022,6 +7024,7 @@ extern void do_push_parm_decls			(tree, tree, tree *);
 extern tree do_aggregate_paren_init		(tree, tree);
 
 /* in decl2.cc */
+extern void update_sym_alias_interface		(tree);
 extern void record_mangling			(tree, bool);
 extern void overwrite_mangling			(tree, tree);
 extern void note_mangling_alias			(tree, tree);
@@ -7596,6 +7599,7 @@ extern bool emit_tinfo_decl			(tree);
 extern unsigned get_pseudo_tinfo_index		(tree);
 extern tree get_pseudo_tinfo_type		(unsigned);
 extern tree build_if_nonnull			(tree, tree, tsubst_flags_t);
+extern void update_tinfo_sym_alias		(tree);
 
 /* in search.cc */
 extern tree get_parent_with_private_access 	(tree decl, tree binfo);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 2f2dbb8d107..5636d694b68 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -3237,6 +3237,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
 	      && TREE_STATIC (olddecl))))
     make_decl_rtl (olddecl);
 
+  symtab_node::remap_sym_alias_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.
@@ -3260,6 +3262,7 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
       FOR_EACH_CLONE (clone, olddecl)
 	{
 	  DECL_ATTRIBUTES (clone) = DECL_ATTRIBUTES (olddecl);
+	  adjust_clone_attributes (olddecl, clone, DECL_NAME (clone));
 	  DECL_PRESERVE_P (clone) |= DECL_PRESERVE_P (olddecl);
 	}
     }
@@ -10789,6 +10792,7 @@ grokfndecl (tree ctype,
     {
       cplus_decl_attributes (&decl, *attrlist, 0);
       *attrlist = NULL_TREE;
+      create_sym_alias_decls (decl);
     }
 
   if (DECL_HAS_CONTRACTS_P (decl))
diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
index 9e666e5eece..c7f009beaf0 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -1844,6 +1844,9 @@ cplus_decl_attributes (tree *decl, tree attributes, int flags)
   if (late_attrs)
     save_template_attributes (late_attrs, decl, flags);
 
+  if (TYPE_P (*decl) && attributes)
+    update_tinfo_sym_alias (*decl);
+
   /* Propagate deprecation out to the template.  */
   if (TREE_DEPRECATED (*decl))
     if (tree ti = get_template_info (*decl))
@@ -2200,6 +2203,47 @@ 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 sym aliases.  */
+
+void
+update_sym_alias_interface (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.  */
 
@@ -3007,6 +3051,8 @@ determine_visibility (tree decl)
        translation unit, we can make the type internal.  */
     constrain_visibility (decl, VISIBILITY_ANON, false);
 
+  update_sym_alias_interface (decl);
+
   /* If visibility changed and DECL already has DECL_RTL, ensure
      symbol flags are updated.  */
   if ((DECL_VISIBILITY (decl) != orig_visibility
@@ -3269,6 +3315,8 @@ tentative_decl_linkage (tree decl)
       else if (VAR_P (decl))
 	maybe_commonize_var (decl);
     }
+
+  update_sym_alias_interface (decl);
 }
 
 /* DECL is a FUNCTION_DECL or VAR_DECL.  If the object file linkage
@@ -3503,6 +3551,8 @@ import_export_decl (tree decl)
     }
 
   DECL_INTERFACE_KNOWN (decl) = 1;
+
+  update_sym_alias_interface (decl);
 }
 
 /* Return an expression that performs the destruction of DECL, which
diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc
index d19ea5d121c..f877780b2ac 100644
--- a/gcc/cp/name-lookup.cc
+++ b/gcc/cp/name-lookup.cc
@@ -22,9 +22,11 @@ along with GCC; see the file COPYING3.  If not see
 #define INCLUDE_MEMORY
 #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"
@@ -3479,6 +3481,15 @@ push_local_extern_decl_alias (tree decl)
 	  /* Adjust visibility.  */
 	  determine_visibility (alias);
 	}
+      else if (DECL_P (alias))
+	DECL_ATTRIBUTES (alias)
+	  = targetm.merge_decl_attributes (alias, decl);
+      if (DECL_P (alias))
+	{
+	  symtab_node::remap_sym_alias_target (decl, alias);
+	  DECL_ATTRIBUTES (decl)
+	    = remove_attribute ("sym_alias", DECL_ATTRIBUTES (alias));
+	}
     }
 
   retrofit_lang_decl (decl);
diff --git a/gcc/cp/optimize.cc b/gcc/cp/optimize.cc
index 9e8926e4cc6..4a2e8cb4354 100644
--- a/gcc/cp/optimize.cc
+++ b/gcc/cp/optimize.cc
@@ -528,9 +528,12 @@ maybe_clone_body (tree fn)
       DECL_VISIBILITY_SPECIFIED (clone) = DECL_VISIBILITY_SPECIFIED (fn);
       DECL_DLLIMPORT_P (clone) = DECL_DLLIMPORT_P (fn);
       DECL_ATTRIBUTES (clone) = clone_attrs (DECL_ATTRIBUTES (fn));
+      adjust_clone_attributes (fn, clone, DECL_NAME (clone), true);
       DECL_DISREGARD_INLINE_LIMITS (clone) = DECL_DISREGARD_INLINE_LIMITS (fn);
       set_decl_section_name (clone, fn);
 
+      update_sym_alias_interface (clone);
+
       /* Adjust the parameter names and locations.  */
       parm = DECL_ARGUMENTS (fn);
       clone_parm = DECL_ARGUMENTS (clone);
diff --git a/gcc/cp/rtti.cc b/gcc/cp/rtti.cc
index 7878929c246..4bb7b8c8c6e 100644
--- a/gcc/cp/rtti.cc
+++ b/gcc/cp/rtti.cc
@@ -28,8 +28,10 @@ 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"
+#include "cgraph.h"
 
 /* C++ returns type information to the user in struct type_info
    objects. We also use type information to implement dynamic_cast and
@@ -479,8 +481,13 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
 	  = build_tree_list (get_identifier ("non overlapping"),
 			     NULL_TREE);
       else
+	/* Share the non overlapping attribute, without assuming it's
+	   the only attribute, but assuming it's the last if it's
+	   present.  There may be sym aliases too, and those are not
+	   to be shared.  */
 	DECL_ATTRIBUTES (d)
-	  = DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]);
+	  = lookup_attribute ("non overlapping",
+			      DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]));
 
       /* Mark the variable as undefined -- but remember that we can
 	 define it later if we need to do so.  */
@@ -492,6 +499,16 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
       if (CLASS_TYPE_P (type))
 	CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type)) = d;
 
+      /* Copy sym alias attributes from the type to the rtti obj decl.  */
+      tree *attrs = &DECL_ATTRIBUTES (d);
+      FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+	{
+	  tree attr = tree_cons (TREE_PURPOSE (sym), TREE_VALUE (sym), *attrs);
+	  *attrs = attr;
+	  attrs = &TREE_CHAIN (attr);
+	}
+      create_sym_alias_decls (d);
+
       /* Add decl to the global array of tinfo decls.  */
       vec_safe_push (unemitted_tinfo_decls, d);
     }
@@ -499,6 +516,58 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
   return d;
 }
 
+/* After modifying the attributes of TYPE, check whether tinfo was
+   already created and, if so, add to it any sym alias attributes
+   that were not already present.  */
+
+void
+update_tinfo_sym_alias (tree type)
+{
+  if (!TYPE_SIZE (type) || !CLASS_TYPE_P (type))
+    return;
+
+  tree d = CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type));
+  if (!d)
+    return;
+
+  bool first = true;
+  symtab_node *node = NULL;
+
+  tree *attrs = &DECL_ATTRIBUTES (d);
+  FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (d_sym, *attrs)
+	if (TREE_VALUE (sym) == TREE_VALUE (d_sym))
+	  {
+	    found = true;
+	    break;
+	  }
+
+      if (found)
+	continue;
+
+      tree attr = tree_cons (TREE_PURPOSE (sym),
+			     TREE_VALUE (sym),
+			     *attrs);
+      *attrs = attr;
+      attrs = &TREE_CHAIN (attr);
+
+      if (first)
+	{
+	  first = false;
+	  node = symtab_node::get (d);
+	}
+
+      if (!node)
+	continue;
+
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+      create_sym_alias_decl (d, id);
+    }
+}
+
 /* Return the type_info object for TYPE.  */
 
 tree
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index e6de0815846..96674e2d857 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -4141,6 +4141,39 @@ Function Attributes}, @ref{PowerPC Function Attributes},
 @ref{Nios II Function Attributes}, and @ref{S/390 Function Attributes}
 for details.
 
+@cindex @code{sym_alias} function attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} attribute causes @var{name} to be output as an
+alias to the definition.  For instance,
+
+@smallexample
+void f (uint64_t) __attribute__ ((__sym_alias__ ("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 for aliases,
+and may not be available on all targets.
+
 @cindex @code{symver} function attribute
 @item symver ("@var{name2}@@@var{nodename}")
 On ELF targets this attribute creates a symbol version.  The @var{name2} part
@@ -8069,6 +8102,10 @@ will be placed in new, unique sections.
 
 This additional functionality requires Binutils version 2.36 or later.
 
+@cindex @code{sym_alias} variable attribute
+@item sym_alias ("@var{name}")
+See @pxref{Common Function Attributes}.
+
 @cindex @code{uninitialized} variable attribute
 @item uninitialized
 This attribute, attached to a variable with automatic storage, means that
@@ -9164,6 +9201,21 @@ is not supported; that is to say, if a given scalar object can be accessed
 through distinct types that assign a different storage order to it, then the
 behavior is undefined.
 
+@cindex @code{sym_alias} type attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} type 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__ ((__sym_alias__ ("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.
+
 @cindex @code{transparent_union} type attribute
 @item transparent_union
 
diff --git a/gcc/symtab.cc b/gcc/symtab.cc
index 0470509a98d..0812088e59f 100644
--- a/gcc/symtab.cc
+++ b/gcc/symtab.cc
@@ -1943,6 +1943,42 @@ symtab_node::noninterposable_alias (symtab_node *node, void *data)
   return false;
 }
 
+/* Remap sym alias nodes recorded as aliasing REPLACED to alias
+   REPLACEMENT instead.  */
+
+void
+symtab_node::remap_sym_alias_target (tree replaced, tree replacement)
+{
+  if (!decl_in_symtab_p (replacement)
+      || !symtab_node::get (replacement))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (replaced))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      symtab_node *sym_node = symtab_node::get_for_asmname (id);
+
+      if (!sym_node)
+	{
+	  create_sym_alias_decl (replacement, id);
+	  continue;
+	}
+
+      gcc_assert (!sym_node->analyzed);
+      if (sym_node->alias_target != replaced)
+	continue;
+
+      sym_node->definition = 0;
+
+      if (VAR_P (replaced))
+	varpool_node::create_extra_name_alias (sym_node->decl, replacement);
+      else
+	cgraph_node::create_same_body_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-sym-alias-neg.c b/gcc/testsuite/c-c++-common/attr-sym-alias-neg.c
new file mode 100644
index 00000000000..33172ca6f02
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/attr-sym-alias-neg.c
@@ -0,0 +1,18 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int f () {
+  static int x __attribute__ ((sym_alias ("f_x")));
+  int i __attribute__ ((sym_alias ("f_i"))); /* { dg-warning "ignored" } */
+  static int sx __attribute__ ((alias ("f_x"))); /* { dg-warning "ignored" } */
+  extern int xx __attribute__ ((alias ("f_x"))); /* { dg-warning "ignored" } */
+  return i;
+}
+
+/* ??? Should these be accepted?  */
+extern int xr __attribute__ ((alias ("f_x"))); /* { dg-error "undefined" } */
+int dr __attribute__ ((alias ("f_x"))); /* { dg-error "undefined" } */
+
+struct s {
+  int j __attribute__ ((sym_alias ("s_j"))); /* { dg-warning "ignored" } */
+};
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-1.c b/gcc/testsuite/c-c++-common/goacc/declare-1.c
index 46ee01b6759..f2842893318 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-1.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-1.c
@@ -113,11 +113,11 @@ f_2 (void)
   int va3;
 #pragma acc declare device_resident(va3)
 
-#ifndef __cplusplus
+#if 0
   /* TODO PR90868
 
-     C: "error: variable '[...]' used more than once with '#pragma acc declare'".  */
-#else
+     "error: variable '[...]' used more than once with '#pragma acc declare'".  */
+
   extern int ve0;
 #pragma acc declare create(ve0)
 
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-2.c b/gcc/testsuite/c-c++-common/goacc/declare-2.c
index e2e22be57e9..aec59b69754 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-2.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-2.c
@@ -137,25 +137,25 @@ void
 f_pr90868_2 (void)
 {
   extern int we0;
-#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" } */
 
   extern int we1;
-#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" } */
 
   extern int *we2;
-#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" } */
 
   extern int we3;
-#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" } */
 
   extern int we4;
-#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" } */
 
   extern int we5;
-#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" } */
  
   extern int we6;
-#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" } */
 }
 
 
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
new file mode 100644
index 00000000000..61af50cb552
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
@@ -0,0 +1,39 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+int var_a = 1;
+
+void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+
+void
+foo_a ()
+{
+}
+
+
+int var_b;
+extern int var_b __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+
+void
+foo_b ()
+{
+}
+
+void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+
+
+int var_c __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+
+void __attribute__ ((__sym_alias__ ("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-sym-alias-2.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
new file mode 100644
index 00000000000..6f0d55ca42c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+struct s
+{
+  int mem __attribute__ ((__sym_alias__ ("MEMFOO"))); /* { dg-warning "attribute ignored" } */
+};
+
+void foo()
+{
+  extern void bar () __attribute__ ((__sym_alias__ ("FOOBAR")));
+  int var __attribute__ ((__sym_alias__ ("FOOVAR"))); /* { dg-warning "attribute ignored" } */
+}
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
new file mode 100644
index 00000000000..0052485a30a
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
@@ -0,0 +1,41 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int var_a = 1;
+
+void
+foo_a ()
+{
+  extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+}
+
+#if 0 // __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__ ((__sym_alias__ ("FOOVAR_B")));
+}
+
+int var_b;
+
+void __attribute__ ((__sym_alias__ ("FOOBAR_C")))
+foo_c ()
+{
+  void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+  /* Another sym for var_b.  */
+  extern int var_b __attribute__ ((__sym_alias__ ("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-sym-alias-4.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
new file mode 100644
index 00000000000..2beafa46379
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-require-alias "" } */
+
+int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A"))) = 42;
+
+int __attribute__ ((__sym_alias__ ("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/c-c++-common/torture/attr-sym-alias-5.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
new file mode 100644
index 00000000000..2eccb34c441
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
@@ -0,0 +1,18 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+/* { dg-require-visibility "" } */
+
+extern void __attribute__ ((sym_alias ("F_hid"), visibility ("hidden")))
+f ();
+
+void f () {
+}
+
+extern void __attribute__ ((sym_alias ("G_prot")))
+g ();
+
+void g __attribute__ ((visibility ("protected"))) () {
+}
+
+/* { dg-final { scan-assembler "hidden.*F_hid" } } */
+/* { dg-final { scan-assembler "protected.*F_prot" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
new file mode 100644
index 00000000000..579eda1a473
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
@@ -0,0 +1,72 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+class __attribute__ ((__sym_alias__ ("FOOCLS_A"),
+		      __sym_alias__ ("FOOCLS_A_Dupe"))) foo {
+  static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  void __attribute__ ((__sym_alias__ ("FOOBAR_A"))) bar ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+int foo::var = 1;
+
+foo::foo () {}
+
+void foo::bar () {}
+
+namespace b {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_B"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_B"))) foo ();
+    void __attribute__ ((__sym_alias__ ("FOOBAR_B"))) bar () {}
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_B"))) ~foo() {}
+  };
+
+  int foo::var = 2;
+
+  foo::foo () {
+    void (foo::*pbar)() = &foo::bar;
+  }
+}
+
+namespace c {
+  namespace cd {
+    class __attribute__ ((__sym_alias__ ("FOOCLS_C"))) foo {
+      static int var __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+      __attribute__ ((__sym_alias__ ("FOOCTR_C"))) foo () {
+	void (foo::*pbar)() = &foo::bar;
+      }
+      void __attribute__ ((__sym_alias__ ("FOOBAR_C"))) bar () {}
+      virtual __attribute__ ((__sym_alias__ ("FOODTR_C"))) ~foo() {}
+    };
+
+    int foo::var = 3;
+  }
+}
+
+/* { dg-final { scan-assembler "FOOCLS_A" } } */
+/* { dg-final { scan-assembler "FOOCLS_A_Dupe" } } */
+/* { 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-sym-alias-2.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
new file mode 100644
index 00000000000..6facfcc2cd9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+namespace {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_A"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo ();
+    void __attribute__ ((__sym_alias__ ("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-sym-alias-3.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
new file mode 100644
index 00000000000..42c7288ad4a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
@@ -0,0 +1,83 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+// sym can be applied to template function explicit instantiations.
+
+template <typename T>
+void
+fn(T) {
+};
+
+template void __attribute__ ((__sym_alias__ ("FOOFUN_UINT"))) fn<>(unsigned int);
+template void __attribute__ ((__sym_alias__ ("FOOFUN_LONG"))) fn<>(long);
+
+template<> void __attribute__ ((__sym_alias__ ("FOOFUN_CHAR"))) fn<>(char) {}
+
+
+template <typename T = void>
+struct
+foo {
+  virtual ~foo() {}
+
+  virtual void virtfun() {}
+
+  static void stfun() {}
+  void inlfun() {}
+};
+
+// Explicitly instantiate members before the enclosing class.
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_CHAR_VIRT"))) foo<char>::virtfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_CHAR_TI"))) foo<char>;
+
+// Though they're only output if the enclosing class is.
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_LONG_VIRT"))) foo<long>::virtfun();
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_LONG_TI_X"))) foo<long>;
+
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_VOID_ST"))) foo<void>::stfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_VOID_TI"))) foo<>;
+
+
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_X"))) foo<short>;
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_ST"))) foo<short>::stfun();
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_INL"))) foo<short>::inlfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_D"))) foo<short>;
+
+// Explicit specializations work too.
+
+template <>
+struct  __attribute__ ((__sym_alias__ ("FOOCLS_INT_TI")))
+foo<int>
+{
+  virtual ~foo() {}
+  virtual void __attribute__ ((__sym_alias__ ("FOOCLS_INT_VIRT"))) virtfun() {}
+};
+
+/* { dg-final { scan-assembler "FOOFUN_UINT" } } */
+/* { dg-final { scan-assembler "FOOFUN_LONG" } } */
+/* { dg-final { scan-assembler "FOOFUN_CHAR" } } */
+
+/* { dg-final { scan-assembler "FOOCLS_VOID_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_VOID_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_X" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_INL" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_D" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_TI_X" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_VIRT" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
new file mode 100644
index 00000000000..59b779de35d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+template <typename T = void>
+class
+__attribute__ ((__sym_alias__ ("FOOCLS")))
+foo // { dg-error "duplicate|already" }
+{
+  virtual ~foo() {}
+
+  template <typename U>
+  void
+    __attribute__ ((__sym_alias__ ("FOOTMF")))
+    tmemfun () {} // { dg-error "duplicate|already" }
+};
+
+template <typename T>
+void
+__attribute__ ((__sym_alias__ ("FOOTFN")))
+fn(T) { // { dg-error "duplicate|already" }
+};
+
+template class foo<>;
+template class foo<int>;
+template void foo<>::tmemfun<void>();
+template void foo<int>::tmemfun<void>();
+template void fn<>(int);
+template void fn<>(long);
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
new file mode 100644
index 00000000000..45d59b953c3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
@@ -0,0 +1,14 @@
+/* { dg-do compile { target c++11 } } */
+/* { dg-require-alias "" } */
+
+struct foo {
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+foo::foo () {}
+
+// Make sure the inherited cdtors don't duplicate the syms.
+struct bar : foo {
+  using foo::foo;
+};
diff --git a/gcc/varpool.cc b/gcc/varpool.cc
index e7b51b15e4a..33649906d1d 100644
--- a/gcc/varpool.cc
+++ b/gcc/varpool.cc
@@ -163,6 +163,9 @@ varpool_node::get_create (tree decl)
     }
 
   node->register_symbol ();
+
+  create_sym_alias_decls (decl);
+
   return node;
 }

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [gcc(refs/users/aoliva/heads/testme)] Introduce attribute sym_alias
@ 2023-11-30 10:55 Alexandre Oliva
  0 siblings, 0 replies; 33+ messages in thread
From: Alexandre Oliva @ 2023-11-30 10:55 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:41e6038030388416b66d035cccb03a5eaf3df340

commit 41e6038030388416b66d035cccb03a5eaf3df340
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Fri Jul 14 03:45:14 2023 -0300

    Introduce attribute sym_alias
    
    This patch introduces an attribute to add extra asm names (aliases)
    for a decl 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 class types, namespace-scoped
    variables and functions, static data members, member functions,
    explicit instantiations and specializations of template functions,
    members and classes.
    
    When applied to constructors or destructor, additional sym aliases
    with _Base and _Del suffixes are defined for variants other than
    complete-object ones.  This changes the assumption that clones always
    carry the same attributes as their abstract declarations, so there is
    now a function to adjust them.
    
    C++ also had a bug in which attributes from local extern declarations
    failed to be propagated to a preexisting corresponding
    namespace-scoped decl.  I've fixed that, and adjusted acc tests that
    distinguished between C and C++ in this regard.
    
    Applying the attribute to class types is only valid in C++, and the
    effect is to attach the alias to the RTTI object associated with the
    class type.
    
    for  gcc/ChangeLog
    
            * attribs.cc: Include cgraph.h.
            (decl_attributes): Allow late introduction of sym_alias in
            types.
            (create_sym_alias_decl, create_sym_alias_decls): New.
            * attribs.h: Declare them.
            (FOR_EACH_SYM_ALIAS): New macro.
            * cgraph.cc (cgraph_node::create): Create sym_alias decls.
            * varpool.cc (varpool_node::get_create): Create sym_alias
            decls.
            * cgraph.h (symtab_node::remap_sym_alias_target): New.
            * symtab.cc (symtab_node::remap_sym_alias_target): Define.
            * cgraphunit.cc (cgraph_node::analyze): Create alias_target
            node if needed.
            (analyze_functions): Fixup visibility of implicit alias only
            after its node is analyzed.
            * doc/extend.texi (sym_alias): Document for variables,
            functions and types.
    
    for  gcc/ada/ChangeLog
    
            * doc/gnat_rm/interfacing_to_other_languages.rst: Mention
            attribute sym_alias to give RTTI symbols mnemonic names.
            * doc/gnat_ugn/the_gnat_compilation_model.rst: Mention
            aliases.  Fix incorrect ref to C1 ctor variant.
    
    for  gcc/c-family/ChangeLog
    
            * c-ada-spec.cc (pp_asm_name): Use first sym_alias if
            available.
            * c-attribs.cc (handle_sym_alias_attribute): New.
            (c_common_attribute_table): Add sym_alias.
            (handle_copy_attribute): Do not copy sym_alias attribute.
    
    for  gcc/c/ChangeLog
    
            * c-decl.cc (duplicate_decls): Remap sym_alias target.
    
    for  gcc/cp/ChangeLog
    
            * class.cc (adjust_clone_attributes): New.
            (copy_fndecl_with_name, build_clone): Call it.
            * cp-tree.h (adjust_clone_attributes): Declare.
            (update_sym_alias_interface): Declare.
            (update_tinfo_sym_alias): Declare.
            * decl.cc (duplicate_decls): Remap sym_alias target.
            Adjust clone attributes.
            (grokfndecl): Tentatively create sym_alias decls after
            adding attributes in e.g. a template member function explicit
            instantiation.
            * decl2.cc (cplus_decl_attributes): Update tinfo sym_alias.
            (copy_interface, update_sym_alias_interface): New.
            (determine_visibility): Update sym_alias interface.
            (tentative_decl_linkage, import_export_decl): Likewise.
            * name-lookup.cc: Include target.h and cgraph.h.
            (push_local_extern_decl_alias): Merge attributes with
            namespace-scoped decl, and drop duplicate sym_alias.
            * optimize.cc (maybe_clone_body): Re-adjust attributes after
            cloning them.  Update sym_alias interface.
            * rtti.cc: Include attribs.h and cgraph.h.
            (get_tinfo_decl): Copy sym_alias attributes from type to tinfo
            decl.  Create sym_alias decls.
            (update_tinfo_sym_alias): New.
    
    for  gcc/testsuite/ChangeLog
    
            * c-c++-common/goacc/declare-1.c: Adjust.
            * c-c++-common/goacc/declare-2.c: Adjust.
            * c-c++-common/torture/attr-sym-alias-1.c: New.
            * c-c++-common/torture/attr-sym-alias-2.c: New.
            * c-c++-common/torture/attr-sym-alias-3.c: New.
            * c-c++-common/torture/attr-sym-alias-4.c: New.
            * c-c++-common/torture/attr-sym-alias-5.c: New.
            * c-c++-common/attr-sym-alias-neg.c: New.
            * g++.dg/torture/attr-sym-alias-1.C: New.
            * g++.dg/torture/attr-sym-alias-2.C: New.
            * g++.dg/torture/attr-sym-alias-3.C: New.
            * g++.dg/torture/attr-sym-alias-4.C: New.
            * g++.dg/torture/attr-sym-alias-5.C: New.

Diff:
---
 .../doc/gnat_rm/interfacing_to_other_languages.rst |  6 ++
 .../doc/gnat_ugn/the_gnat_compilation_model.rst    | 10 ++-
 gcc/attribs.cc                                     | 68 +++++++++++++++++-
 gcc/attribs.h                                      |  7 ++
 gcc/c-family/c-ada-spec.cc                         |  7 ++
 gcc/c-family/c-attribs.cc                          | 33 ++++++++-
 gcc/c/c-decl.cc                                    |  2 +
 gcc/cgraph.cc                                      |  2 +
 gcc/cgraph.h                                       |  4 ++
 gcc/cgraphunit.cc                                  |  2 +-
 gcc/cp/class.cc                                    | 64 +++++++++++++++++
 gcc/cp/cp-tree.h                                   |  4 ++
 gcc/cp/decl.cc                                     |  4 ++
 gcc/cp/decl2.cc                                    | 50 +++++++++++++
 gcc/cp/name-lookup.cc                              | 11 +++
 gcc/cp/optimize.cc                                 |  3 +
 gcc/cp/rtti.cc                                     | 71 +++++++++++++++++-
 gcc/doc/extend.texi                                | 52 ++++++++++++++
 gcc/symtab.cc                                      | 36 ++++++++++
 gcc/testsuite/c-c++-common/attr-sym-alias-neg.c    | 18 +++++
 gcc/testsuite/c-c++-common/goacc/declare-1.c       |  6 +-
 gcc/testsuite/c-c++-common/goacc/declare-2.c       | 14 ++--
 .../c-c++-common/torture/attr-sym-alias-1.c        | 39 ++++++++++
 .../c-c++-common/torture/attr-sym-alias-2.c        | 13 ++++
 .../c-c++-common/torture/attr-sym-alias-3.c        | 41 +++++++++++
 .../c-c++-common/torture/attr-sym-alias-4.c        | 28 ++++++++
 .../c-c++-common/torture/attr-sym-alias-5.c        | 10 +++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C    | 72 +++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C    | 26 +++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C    | 83 ++++++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C    | 28 ++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C    | 14 ++++
 gcc/varpool.cc                                     |  3 +
 33 files changed, 812 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 ad0be511d48..b8de16ebf7c 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 ``sym_alias`` 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 fd15459203a..20045698962 100644
--- a/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
+++ b/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
@@ -4274,6 +4274,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__ ((__sym_alias__ ("Ctor_For_Animal")))
         Animal() {Age_Count = 0;};
       private:
         int Age_Count;
@@ -4306,6 +4307,7 @@ both Carnivore and Domestic, that is:
         virtual int  Number_Of_Teeth ();
         virtual void Set_Owner (char* Name);
 
+        __attribute__ ((__sym_alias__ ("Ctor_For_Dog"))) // mnemonic alias
         Dog(); // Constructor
       private:
         int  Tooth_Count;
@@ -4344,7 +4346,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;
@@ -4360,7 +4363,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,7 +4385,8 @@ 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.
+mangled name (or an alias) 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.cc b/gcc/attribs.cc
index c7209c26acc..3ef07c885d5 100644
--- a/gcc/attribs.cc
+++ b/gcc/attribs.cc
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "target.h"
 #include "tree.h"
+#include "cgraph.h"
 #include "stringpool.h"
 #include "diagnostic-core.h"
 #include "attribs.h"
@@ -825,7 +826,8 @@ decl_attributes (tree *node, tree attributes, int flags,
 
       if (TYPE_P (*anode)
 	  && (flags & (int) ATTR_FLAG_TYPE_IN_PLACE)
-	  && COMPLETE_TYPE_P (*anode))
+	  && COMPLETE_TYPE_P (*anode)
+	  && !is_attribute_p ("sym_alias", name))
 	{
 	  warning (OPT_Wattributes, "type attributes ignored after type is already defined");
 	  continue;
@@ -2640,6 +2642,70 @@ attr_access::array_as_string (tree type) const
   return typstr;
 }
 
+/* Create a sym attribute for DECL to be visible with linkage name ID.  */
+
+tree
+create_sym_alias_decl (tree decl, tree id)
+{
+  const char *attr_str = "sym_alias";
+
+  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;
+
+      tree attr_name = get_identifier (attr_str);
+      error_at (DECL_SOURCE_LOCATION (decl),
+		"duplicate symbol name %qE in %qE attribute of %qD",
+		id, attr_name, decl);
+      inform (DECL_SOURCE_LOCATION (sym_node->decl),
+	      "already used by %qD", sym_node->decl);
+    }
+
+  tree clone = copy_node (decl);
+  DECL_ATTRIBUTES (clone) = remove_attribute (attr_str,
+					      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_extra_name_alias (clone, decl);
+    }
+  else
+    {
+      cgraph_node::create_same_body_alias (clone, decl);
+    }
+
+  return clone;
+}
+
+/* Create decls for all sym aliases requested in DECL's attributes.  */
+
+void
+create_sym_alias_decls (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl)
+      || DECL_ABSTRACT_P (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      create_sym_alias_decl (decl, id);
+    }
+}
+
 #if CHECKING_P
 
 namespace selftest
diff --git a/gcc/attribs.h b/gcc/attribs.h
index 84a43658a70..156f2c20588 100644
--- a/gcc/attribs.h
+++ b/gcc/attribs.h
@@ -398,4 +398,11 @@ extern void init_attr_rdwr_indices (rdwr_map *, tree);
 extern attr_access *get_parm_access (rdwr_map &, tree,
 				     tree = current_function_decl);
 
+extern tree create_sym_alias_decl (tree, tree);
+extern void create_sym_alias_decls (tree);
+
+#define FOR_EACH_SYM_ALIAS(sym, attrs)					\
+  for (tree sym = lookup_attribute ("sym_alias", (attrs));		\
+       sym; sym = lookup_attribute ("sym_alias", TREE_CHAIN (sym)))
+
 #endif // GCC_ATTRIBS_H
diff --git a/gcc/c-family/c-ada-spec.cc b/gcc/c-family/c-ada-spec.cc
index 050994d8416..5042b9cfecd 100644
--- a/gcc/c-family/c-ada-spec.cc
+++ b/gcc/c-family/c-ada-spec.cc
@@ -1431,6 +1431,13 @@ pp_ada_tree_identifier (pretty_printer *buffer, tree node, tree type,
 static void
 pp_asm_name (pretty_printer *buffer, tree t)
 {
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (t))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.cc b/gcc/c-family/c-attribs.cc
index 2b20e58c922..392437eab3b 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -108,7 +108,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_sym_alias_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,
@@ -388,6 +389,8 @@ const struct attribute_spec c_common_attribute_table[] =
 			      handle_alias_attribute, NULL },
   { "weakref",                0, 1, true,  false, false, false,
 			      handle_weakref_attribute, NULL },
+  { "sym_alias",              1, 1, false,  false, false, false,
+			      handle_sym_alias_attribute, NULL },
   { "no_instrument_function", 0, 0, true,  false, false, false,
 			      handle_no_instrument_function_attribute,
 			      NULL },
@@ -3002,7 +3005,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
@@ -3012,7 +3015,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
@@ -3022,6 +3025,29 @@ handle_alias_attribute (tree *node, tree name, tree args,
   return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs);
 }
 
+/* Handle a "sym_alias" attribute; arguments as in struct
+   attribute_spec.handler.  */
+
+static tree
+handle_sym_alias_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 (decl_in_symtab_p (node))
+    *no_add_attrs = false;
+  else if (TYPE_P (node) && c_dialect_cxx ())
+    *no_add_attrs = false;
+  else
+    return error_mark_node;
+
+  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.  */
 
@@ -3155,6 +3181,7 @@ handle_copy_attribute (tree *node, tree name, tree args,
 	      || is_attribute_p ("visibility", atname)
 	      || is_attribute_p ("weak", atname)
 	      || is_attribute_p ("weakref", atname)
+	      || is_attribute_p ("sym_alias", atname)
 	      || is_attribute_p ("target_clones", atname))
 	    continue;
 
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index cf1df82c0f4..e5a1ee54cb3 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -3119,6 +3119,8 @@ duplicate_decls (tree newdecl, tree olddecl)
 
   merge_decls (newdecl, olddecl, newtype, oldtype);
 
+  symtab_node::remap_sym_alias_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.cc b/gcc/cgraph.cc
index f93259a8c70..0bef2179e89 100644
--- a/gcc/cgraph.cc
+++ b/gcc/cgraph.cc
@@ -523,6 +523,8 @@ cgraph_node::create (tree decl)
   node->register_symbol ();
   maybe_record_nested_function (node);
 
+  create_sym_alias_decls (decl);
+
   return node;
 }
 
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index cfdd9f693a8..cb5ac01f7e2 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -327,6 +327,10 @@ public:
   /* Return DECL that alias is aliasing.  */
   inline tree get_alias_target_tree ();
 
+  /* Remap sym alias nodes recorded as aliasing REPLACED to alias REPLACEMENT
+     instead.  */
+  static void remap_sym_alias_target (tree replaced, tree replacement);
+
   /* Set section for symbol and its aliases.  */
   void set_section (const char *section);
 
diff --git a/gcc/cgraphunit.cc b/gcc/cgraphunit.cc
index bccd2f2abb5..eb2d05094e9 100644
--- a/gcc/cgraphunit.cc
+++ b/gcc/cgraphunit.cc
@@ -1175,7 +1175,7 @@ analyze_functions (bool first_time)
      C++ FE is confused about the COMDAT groups being right.  */
   if (symtab->cpp_implicit_aliases_done)
     FOR_EACH_SYMBOL (node)
-      if (node->cpp_implicit_alias)
+      if (node->cpp_implicit_alias && node->analyzed)
 	  node->fixup_same_cpp_alias_visibility (node->get_alias_target ());
   build_type_inheritance_graph ();
 
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index 6fdb56abfb9..fa8fc63db82 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -4896,6 +4896,68 @@ check_methods (tree t)
     }
 }
 
+/* Adjust sym alias name for CLONE, cloned from FN and named NAME,
+   if it is a cdtor, and drop the sym alias from other clones.  */
+
+void
+adjust_clone_attributes (tree fn, tree clone, tree name, bool skip_copy_p)
+{
+  if (IDENTIFIER_CDTOR_P (name))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	{
+	  found = true;
+	  break;
+	}
+
+      if (found
+	  && (name == complete_ctor_identifier
+	      || name == complete_dtor_identifier))
+	{
+	  /* Reuse the sym alias decls created for the primary cdtor
+	     decl.  */
+	  symtab_node::remap_sym_alias_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);
+
+	  if (!skip_copy_p)
+	    DECL_ATTRIBUTES (clone) = copy_list (DECL_ATTRIBUTES (clone));
+
+	  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	    {
+	      /* We need to copy this even with skip_copy_p, because
+		 even then copying was shallow.  */
+	      TREE_VALUE (sym) = copy_list (TREE_VALUE (sym));
+	      /* Append suf to the sym alias name.  */
+	      tree str = TREE_VALUE (TREE_VALUE (sym));
+	      char *symname = concat (TREE_STRING_POINTER (str), suf, NULL);
+	      str = build_string (TREE_STRING_LENGTH (str) + xlen, symname);
+	      TREE_VALUE (TREE_VALUE (sym)) = str;
+	      free (symname);
+	    }
+
+	  if (symtab_node::get (clone))
+	    create_sym_alias_decls (clone);
+	}
+    }
+  else
+    DECL_ATTRIBUTES (clone)
+      = remove_attribute ("sym_alias", DECL_ATTRIBUTES (clone));
+}
+
 /* FN is constructor, destructor or operator function.  Clone the
    declaration to create a NAME'd variant.  NEED_VTT_PARM_P and
    OMIT_INHERITED_PARMS_P are relevant if it's a cdtor.  */
@@ -5065,6 +5127,8 @@ build_clone (tree fn, tree name, bool need_vtt_parm_p,
   DECL_CHAIN (clone) = DECL_CHAIN (fn);
   DECL_CHAIN (fn) = clone;
 
+  adjust_clone_attributes (fn, clone, name);
+
   return clone;
 }
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 964af1ddd85..2bafed4e993 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5787,6 +5787,8 @@ struct GTY((for_user)) spec_entry
 
 extern int current_class_depth;
 
+void adjust_clone_attributes (tree fn, tree clone, tree name, bool = false);
+
 /* in decl.cc */
 
 /* An array of static vars & fns.  */
@@ -7022,6 +7024,7 @@ extern void do_push_parm_decls			(tree, tree, tree *);
 extern tree do_aggregate_paren_init		(tree, tree);
 
 /* in decl2.cc */
+extern void update_sym_alias_interface		(tree);
 extern void record_mangling			(tree, bool);
 extern void overwrite_mangling			(tree, tree);
 extern void note_mangling_alias			(tree, tree);
@@ -7596,6 +7599,7 @@ extern bool emit_tinfo_decl			(tree);
 extern unsigned get_pseudo_tinfo_index		(tree);
 extern tree get_pseudo_tinfo_type		(unsigned);
 extern tree build_if_nonnull			(tree, tree, tsubst_flags_t);
+extern void update_tinfo_sym_alias		(tree);
 
 /* in search.cc */
 extern tree get_parent_with_private_access 	(tree decl, tree binfo);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 2f2dbb8d107..5636d694b68 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -3237,6 +3237,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
 	      && TREE_STATIC (olddecl))))
     make_decl_rtl (olddecl);
 
+  symtab_node::remap_sym_alias_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.
@@ -3260,6 +3262,7 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
       FOR_EACH_CLONE (clone, olddecl)
 	{
 	  DECL_ATTRIBUTES (clone) = DECL_ATTRIBUTES (olddecl);
+	  adjust_clone_attributes (olddecl, clone, DECL_NAME (clone));
 	  DECL_PRESERVE_P (clone) |= DECL_PRESERVE_P (olddecl);
 	}
     }
@@ -10789,6 +10792,7 @@ grokfndecl (tree ctype,
     {
       cplus_decl_attributes (&decl, *attrlist, 0);
       *attrlist = NULL_TREE;
+      create_sym_alias_decls (decl);
     }
 
   if (DECL_HAS_CONTRACTS_P (decl))
diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
index 9e666e5eece..c7f009beaf0 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -1844,6 +1844,9 @@ cplus_decl_attributes (tree *decl, tree attributes, int flags)
   if (late_attrs)
     save_template_attributes (late_attrs, decl, flags);
 
+  if (TYPE_P (*decl) && attributes)
+    update_tinfo_sym_alias (*decl);
+
   /* Propagate deprecation out to the template.  */
   if (TREE_DEPRECATED (*decl))
     if (tree ti = get_template_info (*decl))
@@ -2200,6 +2203,47 @@ 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 sym aliases.  */
+
+void
+update_sym_alias_interface (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.  */
 
@@ -3007,6 +3051,8 @@ determine_visibility (tree decl)
        translation unit, we can make the type internal.  */
     constrain_visibility (decl, VISIBILITY_ANON, false);
 
+  update_sym_alias_interface (decl);
+
   /* If visibility changed and DECL already has DECL_RTL, ensure
      symbol flags are updated.  */
   if ((DECL_VISIBILITY (decl) != orig_visibility
@@ -3269,6 +3315,8 @@ tentative_decl_linkage (tree decl)
       else if (VAR_P (decl))
 	maybe_commonize_var (decl);
     }
+
+  update_sym_alias_interface (decl);
 }
 
 /* DECL is a FUNCTION_DECL or VAR_DECL.  If the object file linkage
@@ -3503,6 +3551,8 @@ import_export_decl (tree decl)
     }
 
   DECL_INTERFACE_KNOWN (decl) = 1;
+
+  update_sym_alias_interface (decl);
 }
 
 /* Return an expression that performs the destruction of DECL, which
diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc
index d19ea5d121c..f877780b2ac 100644
--- a/gcc/cp/name-lookup.cc
+++ b/gcc/cp/name-lookup.cc
@@ -22,9 +22,11 @@ along with GCC; see the file COPYING3.  If not see
 #define INCLUDE_MEMORY
 #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"
@@ -3479,6 +3481,15 @@ push_local_extern_decl_alias (tree decl)
 	  /* Adjust visibility.  */
 	  determine_visibility (alias);
 	}
+      else if (DECL_P (alias))
+	DECL_ATTRIBUTES (alias)
+	  = targetm.merge_decl_attributes (alias, decl);
+      if (DECL_P (alias))
+	{
+	  symtab_node::remap_sym_alias_target (decl, alias);
+	  DECL_ATTRIBUTES (decl)
+	    = remove_attribute ("sym_alias", DECL_ATTRIBUTES (alias));
+	}
     }
 
   retrofit_lang_decl (decl);
diff --git a/gcc/cp/optimize.cc b/gcc/cp/optimize.cc
index 9e8926e4cc6..4a2e8cb4354 100644
--- a/gcc/cp/optimize.cc
+++ b/gcc/cp/optimize.cc
@@ -528,9 +528,12 @@ maybe_clone_body (tree fn)
       DECL_VISIBILITY_SPECIFIED (clone) = DECL_VISIBILITY_SPECIFIED (fn);
       DECL_DLLIMPORT_P (clone) = DECL_DLLIMPORT_P (fn);
       DECL_ATTRIBUTES (clone) = clone_attrs (DECL_ATTRIBUTES (fn));
+      adjust_clone_attributes (fn, clone, DECL_NAME (clone), true);
       DECL_DISREGARD_INLINE_LIMITS (clone) = DECL_DISREGARD_INLINE_LIMITS (fn);
       set_decl_section_name (clone, fn);
 
+      update_sym_alias_interface (clone);
+
       /* Adjust the parameter names and locations.  */
       parm = DECL_ARGUMENTS (fn);
       clone_parm = DECL_ARGUMENTS (clone);
diff --git a/gcc/cp/rtti.cc b/gcc/cp/rtti.cc
index 7878929c246..4bb7b8c8c6e 100644
--- a/gcc/cp/rtti.cc
+++ b/gcc/cp/rtti.cc
@@ -28,8 +28,10 @@ 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"
+#include "cgraph.h"
 
 /* C++ returns type information to the user in struct type_info
    objects. We also use type information to implement dynamic_cast and
@@ -479,8 +481,13 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
 	  = build_tree_list (get_identifier ("non overlapping"),
 			     NULL_TREE);
       else
+	/* Share the non overlapping attribute, without assuming it's
+	   the only attribute, but assuming it's the last if it's
+	   present.  There may be sym aliases too, and those are not
+	   to be shared.  */
 	DECL_ATTRIBUTES (d)
-	  = DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]);
+	  = lookup_attribute ("non overlapping",
+			      DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]));
 
       /* Mark the variable as undefined -- but remember that we can
 	 define it later if we need to do so.  */
@@ -492,6 +499,16 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
       if (CLASS_TYPE_P (type))
 	CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type)) = d;
 
+      /* Copy sym alias attributes from the type to the rtti obj decl.  */
+      tree *attrs = &DECL_ATTRIBUTES (d);
+      FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+	{
+	  tree attr = tree_cons (TREE_PURPOSE (sym), TREE_VALUE (sym), *attrs);
+	  *attrs = attr;
+	  attrs = &TREE_CHAIN (attr);
+	}
+      create_sym_alias_decls (d);
+
       /* Add decl to the global array of tinfo decls.  */
       vec_safe_push (unemitted_tinfo_decls, d);
     }
@@ -499,6 +516,58 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
   return d;
 }
 
+/* After modifying the attributes of TYPE, check whether tinfo was
+   already created and, if so, add to it any sym alias attributes
+   that were not already present.  */
+
+void
+update_tinfo_sym_alias (tree type)
+{
+  if (!TYPE_SIZE (type) || !CLASS_TYPE_P (type))
+    return;
+
+  tree d = CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type));
+  if (!d)
+    return;
+
+  bool first = true;
+  symtab_node *node = NULL;
+
+  tree *attrs = &DECL_ATTRIBUTES (d);
+  FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (d_sym, *attrs)
+	if (TREE_VALUE (sym) == TREE_VALUE (d_sym))
+	  {
+	    found = true;
+	    break;
+	  }
+
+      if (found)
+	continue;
+
+      tree attr = tree_cons (TREE_PURPOSE (sym),
+			     TREE_VALUE (sym),
+			     *attrs);
+      *attrs = attr;
+      attrs = &TREE_CHAIN (attr);
+
+      if (first)
+	{
+	  first = false;
+	  node = symtab_node::get (d);
+	}
+
+      if (!node)
+	continue;
+
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+      create_sym_alias_decl (d, id);
+    }
+}
+
 /* Return the type_info object for TYPE.  */
 
 tree
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index e6de0815846..96674e2d857 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -4141,6 +4141,39 @@ Function Attributes}, @ref{PowerPC Function Attributes},
 @ref{Nios II Function Attributes}, and @ref{S/390 Function Attributes}
 for details.
 
+@cindex @code{sym_alias} function attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} attribute causes @var{name} to be output as an
+alias to the definition.  For instance,
+
+@smallexample
+void f (uint64_t) __attribute__ ((__sym_alias__ ("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 for aliases,
+and may not be available on all targets.
+
 @cindex @code{symver} function attribute
 @item symver ("@var{name2}@@@var{nodename}")
 On ELF targets this attribute creates a symbol version.  The @var{name2} part
@@ -8069,6 +8102,10 @@ will be placed in new, unique sections.
 
 This additional functionality requires Binutils version 2.36 or later.
 
+@cindex @code{sym_alias} variable attribute
+@item sym_alias ("@var{name}")
+See @pxref{Common Function Attributes}.
+
 @cindex @code{uninitialized} variable attribute
 @item uninitialized
 This attribute, attached to a variable with automatic storage, means that
@@ -9164,6 +9201,21 @@ is not supported; that is to say, if a given scalar object can be accessed
 through distinct types that assign a different storage order to it, then the
 behavior is undefined.
 
+@cindex @code{sym_alias} type attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} type 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__ ((__sym_alias__ ("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.
+
 @cindex @code{transparent_union} type attribute
 @item transparent_union
 
diff --git a/gcc/symtab.cc b/gcc/symtab.cc
index 0470509a98d..0812088e59f 100644
--- a/gcc/symtab.cc
+++ b/gcc/symtab.cc
@@ -1943,6 +1943,42 @@ symtab_node::noninterposable_alias (symtab_node *node, void *data)
   return false;
 }
 
+/* Remap sym alias nodes recorded as aliasing REPLACED to alias
+   REPLACEMENT instead.  */
+
+void
+symtab_node::remap_sym_alias_target (tree replaced, tree replacement)
+{
+  if (!decl_in_symtab_p (replacement)
+      || !symtab_node::get (replacement))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (replaced))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      symtab_node *sym_node = symtab_node::get_for_asmname (id);
+
+      if (!sym_node)
+	{
+	  create_sym_alias_decl (replacement, id);
+	  continue;
+	}
+
+      gcc_assert (!sym_node->analyzed);
+      if (sym_node->alias_target != replaced)
+	continue;
+
+      sym_node->definition = 0;
+
+      if (VAR_P (replaced))
+	varpool_node::create_extra_name_alias (sym_node->decl, replacement);
+      else
+	cgraph_node::create_same_body_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-sym-alias-neg.c b/gcc/testsuite/c-c++-common/attr-sym-alias-neg.c
new file mode 100644
index 00000000000..33172ca6f02
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/attr-sym-alias-neg.c
@@ -0,0 +1,18 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int f () {
+  static int x __attribute__ ((sym_alias ("f_x")));
+  int i __attribute__ ((sym_alias ("f_i"))); /* { dg-warning "ignored" } */
+  static int sx __attribute__ ((alias ("f_x"))); /* { dg-warning "ignored" } */
+  extern int xx __attribute__ ((alias ("f_x"))); /* { dg-warning "ignored" } */
+  return i;
+}
+
+/* ??? Should these be accepted?  */
+extern int xr __attribute__ ((alias ("f_x"))); /* { dg-error "undefined" } */
+int dr __attribute__ ((alias ("f_x"))); /* { dg-error "undefined" } */
+
+struct s {
+  int j __attribute__ ((sym_alias ("s_j"))); /* { dg-warning "ignored" } */
+};
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-1.c b/gcc/testsuite/c-c++-common/goacc/declare-1.c
index 46ee01b6759..f2842893318 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-1.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-1.c
@@ -113,11 +113,11 @@ f_2 (void)
   int va3;
 #pragma acc declare device_resident(va3)
 
-#ifndef __cplusplus
+#if 0
   /* TODO PR90868
 
-     C: "error: variable '[...]' used more than once with '#pragma acc declare'".  */
-#else
+     "error: variable '[...]' used more than once with '#pragma acc declare'".  */
+
   extern int ve0;
 #pragma acc declare create(ve0)
 
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-2.c b/gcc/testsuite/c-c++-common/goacc/declare-2.c
index e2e22be57e9..aec59b69754 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-2.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-2.c
@@ -137,25 +137,25 @@ void
 f_pr90868_2 (void)
 {
   extern int we0;
-#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" } */
 
   extern int we1;
-#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" } */
 
   extern int *we2;
-#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" } */
 
   extern int we3;
-#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" } */
 
   extern int we4;
-#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" } */
 
   extern int we5;
-#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" } */
  
   extern int we6;
-#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" } */
 }
 
 
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
new file mode 100644
index 00000000000..61af50cb552
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
@@ -0,0 +1,39 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+int var_a = 1;
+
+void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+
+void
+foo_a ()
+{
+}
+
+
+int var_b;
+extern int var_b __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+
+void
+foo_b ()
+{
+}
+
+void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+
+
+int var_c __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+
+void __attribute__ ((__sym_alias__ ("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-sym-alias-2.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
new file mode 100644
index 00000000000..6f0d55ca42c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+struct s
+{
+  int mem __attribute__ ((__sym_alias__ ("MEMFOO"))); /* { dg-warning "attribute ignored" } */
+};
+
+void foo()
+{
+  extern void bar () __attribute__ ((__sym_alias__ ("FOOBAR")));
+  int var __attribute__ ((__sym_alias__ ("FOOVAR"))); /* { dg-warning "attribute ignored" } */
+}
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
new file mode 100644
index 00000000000..0052485a30a
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
@@ -0,0 +1,41 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int var_a = 1;
+
+void
+foo_a ()
+{
+  extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+}
+
+#if 0 // __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__ ((__sym_alias__ ("FOOVAR_B")));
+}
+
+int var_b;
+
+void __attribute__ ((__sym_alias__ ("FOOBAR_C")))
+foo_c ()
+{
+  void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+  /* Another sym for var_b.  */
+  extern int var_b __attribute__ ((__sym_alias__ ("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-sym-alias-4.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
new file mode 100644
index 00000000000..2beafa46379
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-require-alias "" } */
+
+int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A"))) = 42;
+
+int __attribute__ ((__sym_alias__ ("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/c-c++-common/torture/attr-sym-alias-5.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
new file mode 100644
index 00000000000..b8fd8011e66
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
@@ -0,0 +1,10 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+/* { dg-require-visibility "" } */
+
+int f __attribute__ ((sym_alias ("F_hid"), visibility ("hidden")));
+
+void f () {
+}
+
+/* { dg-final { scan-assembler "hidden.*f_hid" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
new file mode 100644
index 00000000000..579eda1a473
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
@@ -0,0 +1,72 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+class __attribute__ ((__sym_alias__ ("FOOCLS_A"),
+		      __sym_alias__ ("FOOCLS_A_Dupe"))) foo {
+  static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  void __attribute__ ((__sym_alias__ ("FOOBAR_A"))) bar ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+int foo::var = 1;
+
+foo::foo () {}
+
+void foo::bar () {}
+
+namespace b {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_B"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_B"))) foo ();
+    void __attribute__ ((__sym_alias__ ("FOOBAR_B"))) bar () {}
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_B"))) ~foo() {}
+  };
+
+  int foo::var = 2;
+
+  foo::foo () {
+    void (foo::*pbar)() = &foo::bar;
+  }
+}
+
+namespace c {
+  namespace cd {
+    class __attribute__ ((__sym_alias__ ("FOOCLS_C"))) foo {
+      static int var __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+      __attribute__ ((__sym_alias__ ("FOOCTR_C"))) foo () {
+	void (foo::*pbar)() = &foo::bar;
+      }
+      void __attribute__ ((__sym_alias__ ("FOOBAR_C"))) bar () {}
+      virtual __attribute__ ((__sym_alias__ ("FOODTR_C"))) ~foo() {}
+    };
+
+    int foo::var = 3;
+  }
+}
+
+/* { dg-final { scan-assembler "FOOCLS_A" } } */
+/* { dg-final { scan-assembler "FOOCLS_A_Dupe" } } */
+/* { 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-sym-alias-2.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
new file mode 100644
index 00000000000..6facfcc2cd9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+namespace {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_A"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo ();
+    void __attribute__ ((__sym_alias__ ("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-sym-alias-3.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
new file mode 100644
index 00000000000..42c7288ad4a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
@@ -0,0 +1,83 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+// sym can be applied to template function explicit instantiations.
+
+template <typename T>
+void
+fn(T) {
+};
+
+template void __attribute__ ((__sym_alias__ ("FOOFUN_UINT"))) fn<>(unsigned int);
+template void __attribute__ ((__sym_alias__ ("FOOFUN_LONG"))) fn<>(long);
+
+template<> void __attribute__ ((__sym_alias__ ("FOOFUN_CHAR"))) fn<>(char) {}
+
+
+template <typename T = void>
+struct
+foo {
+  virtual ~foo() {}
+
+  virtual void virtfun() {}
+
+  static void stfun() {}
+  void inlfun() {}
+};
+
+// Explicitly instantiate members before the enclosing class.
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_CHAR_VIRT"))) foo<char>::virtfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_CHAR_TI"))) foo<char>;
+
+// Though they're only output if the enclosing class is.
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_LONG_VIRT"))) foo<long>::virtfun();
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_LONG_TI_X"))) foo<long>;
+
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_VOID_ST"))) foo<void>::stfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_VOID_TI"))) foo<>;
+
+
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_X"))) foo<short>;
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_ST"))) foo<short>::stfun();
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_INL"))) foo<short>::inlfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_D"))) foo<short>;
+
+// Explicit specializations work too.
+
+template <>
+struct  __attribute__ ((__sym_alias__ ("FOOCLS_INT_TI")))
+foo<int>
+{
+  virtual ~foo() {}
+  virtual void __attribute__ ((__sym_alias__ ("FOOCLS_INT_VIRT"))) virtfun() {}
+};
+
+/* { dg-final { scan-assembler "FOOFUN_UINT" } } */
+/* { dg-final { scan-assembler "FOOFUN_LONG" } } */
+/* { dg-final { scan-assembler "FOOFUN_CHAR" } } */
+
+/* { dg-final { scan-assembler "FOOCLS_VOID_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_VOID_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_X" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_INL" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_D" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_TI_X" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_VIRT" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
new file mode 100644
index 00000000000..59b779de35d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+template <typename T = void>
+class
+__attribute__ ((__sym_alias__ ("FOOCLS")))
+foo // { dg-error "duplicate|already" }
+{
+  virtual ~foo() {}
+
+  template <typename U>
+  void
+    __attribute__ ((__sym_alias__ ("FOOTMF")))
+    tmemfun () {} // { dg-error "duplicate|already" }
+};
+
+template <typename T>
+void
+__attribute__ ((__sym_alias__ ("FOOTFN")))
+fn(T) { // { dg-error "duplicate|already" }
+};
+
+template class foo<>;
+template class foo<int>;
+template void foo<>::tmemfun<void>();
+template void foo<int>::tmemfun<void>();
+template void fn<>(int);
+template void fn<>(long);
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
new file mode 100644
index 00000000000..45d59b953c3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
@@ -0,0 +1,14 @@
+/* { dg-do compile { target c++11 } } */
+/* { dg-require-alias "" } */
+
+struct foo {
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+foo::foo () {}
+
+// Make sure the inherited cdtors don't duplicate the syms.
+struct bar : foo {
+  using foo::foo;
+};
diff --git a/gcc/varpool.cc b/gcc/varpool.cc
index e7b51b15e4a..33649906d1d 100644
--- a/gcc/varpool.cc
+++ b/gcc/varpool.cc
@@ -163,6 +163,9 @@ varpool_node::get_create (tree decl)
     }
 
   node->register_symbol ();
+
+  create_sym_alias_decls (decl);
+
   return node;
 }

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [gcc(refs/users/aoliva/heads/testme)] Introduce attribute sym_alias
@ 2023-11-30 10:40 Alexandre Oliva
  0 siblings, 0 replies; 33+ messages in thread
From: Alexandre Oliva @ 2023-11-30 10:40 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:39c8e686e9ad613403a178e4ac2c8d95a4e30536

commit 39c8e686e9ad613403a178e4ac2c8d95a4e30536
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Fri Jul 14 03:45:14 2023 -0300

    Introduce attribute sym_alias
    
    This patch introduces an attribute to add extra asm names (aliases)
    for a decl 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 class types, namespace-scoped
    variables and functions, static data members, member functions,
    explicit instantiations and specializations of template functions,
    members and classes.
    
    When applied to constructors or destructor, additional sym aliases
    with _Base and _Del suffixes are defined for variants other than
    complete-object ones.  This changes the assumption that clones always
    carry the same attributes as their abstract declarations, so there is
    now a function to adjust them.
    
    C++ also had a bug in which attributes from local extern declarations
    failed to be propagated to a preexisting corresponding
    namespace-scoped decl.  I've fixed that, and adjusted acc tests that
    distinguished between C and C++ in this regard.
    
    Applying the attribute to class types is only valid in C++, and the
    effect is to attach the alias to the RTTI object associated with the
    class type.
    
    for  gcc/ChangeLog
    
            * attribs.cc: Include cgraph.h.
            (decl_attributes): Allow late introduction of sym_alias in
            types.
            (create_sym_alias_decl, create_sym_alias_decls): New.
            * attribs.h: Declare them.
            (FOR_EACH_SYM_ALIAS): New macro.
            * cgraph.cc (cgraph_node::create): Create sym_alias decls.
            * varpool.cc (varpool_node::get_create): Create sym_alias
            decls.
            * cgraph.h (symtab_node::remap_sym_alias_target): New.
            * symtab.cc (symtab_node::remap_sym_alias_target): Define.
            * cgraphunit.cc (cgraph_node::analyze): Create alias_target
            node if needed.
            (analyze_functions): Fixup visibility of implicit alias only
            after its node is analyzed.
            * doc/extend.texi (sym_alias): Document for variables,
            functions and types.
    
    for  gcc/ada/ChangeLog
    
            * doc/gnat_rm/interfacing_to_other_languages.rst: Mention
            attribute sym_alias to give RTTI symbols mnemonic names.
            * doc/gnat_ugn/the_gnat_compilation_model.rst: Mention
            aliases.  Fix incorrect ref to C1 ctor variant.
    
    for  gcc/c-family/ChangeLog
    
            * c-ada-spec.cc (pp_asm_name): Use first sym_alias if
            available.
            * c-attribs.cc (handle_sym_alias_attribute): New.
            (c_common_attribute_table): Add sym_alias.
            (handle_copy_attribute): Do not copy sym_alias attribute.
    
    for  gcc/c/ChangeLog
    
            * c-decl.cc (duplicate_decls): Remap sym_alias target.
    
    for  gcc/cp/ChangeLog
    
            * class.cc (adjust_clone_attributes): New.
            (copy_fndecl_with_name, build_clone): Call it.
            * cp-tree.h (adjust_clone_attributes): Declare.
            (update_sym_alias_interface): Declare.
            (update_tinfo_sym_alias): Declare.
            * decl.cc (duplicate_decls): Remap sym_alias target.
            Adjust clone attributes.
            (grokfndecl): Tentatively create sym_alias decls after
            adding attributes in e.g. a template member function explicit
            instantiation.
            * decl2.cc (cplus_decl_attributes): Update tinfo sym_alias.
            (copy_interface, update_sym_alias_interface): New.
            (determine_visibility): Update sym_alias interface.
            (tentative_decl_linkage, import_export_decl): Likewise.
            * name-lookup.cc: Include target.h and cgraph.h.
            (push_local_extern_decl_alias): Merge attributes with
            namespace-scoped decl, and drop duplicate sym_alias.
            * optimize.cc (maybe_clone_body): Re-adjust attributes after
            cloning them.  Update sym_alias interface.
            * rtti.cc: Include attribs.h and cgraph.h.
            (get_tinfo_decl): Copy sym_alias attributes from type to tinfo
            decl.  Create sym_alias decls.
            (update_tinfo_sym_alias): New.
    
    for  gcc/testsuite/ChangeLog
    
            * c-c++-common/goacc/declare-1.c: Adjust.
            * c-c++-common/goacc/declare-2.c: Adjust.
            * c-c++-common/torture/attr-sym-alias-1.c: New.
            * c-c++-common/torture/attr-sym-alias-2.c: New.
            * c-c++-common/torture/attr-sym-alias-3.c: New.
            * c-c++-common/torture/attr-sym-alias-4.c: New.
            * c-c++-common/attr-sym-alias-neg.c: New.
            * g++.dg/torture/attr-sym-alias-1.C: New.
            * g++.dg/torture/attr-sym-alias-2.C: New.
            * g++.dg/torture/attr-sym-alias-3.C: New.
            * g++.dg/torture/attr-sym-alias-4.C: New.
            * g++.dg/torture/attr-sym-alias-5.C: New.

Diff:
---
 .../doc/gnat_rm/interfacing_to_other_languages.rst |  6 ++
 .../doc/gnat_ugn/the_gnat_compilation_model.rst    | 10 ++-
 gcc/attribs.cc                                     | 68 +++++++++++++++++-
 gcc/attribs.h                                      |  7 ++
 gcc/c-family/c-ada-spec.cc                         |  7 ++
 gcc/c-family/c-attribs.cc                          | 33 ++++++++-
 gcc/c/c-decl.cc                                    |  2 +
 gcc/cgraph.cc                                      |  2 +
 gcc/cgraph.h                                       |  4 ++
 gcc/cgraphunit.cc                                  |  2 +-
 gcc/cp/class.cc                                    | 64 +++++++++++++++++
 gcc/cp/cp-tree.h                                   |  4 ++
 gcc/cp/decl.cc                                     |  4 ++
 gcc/cp/decl2.cc                                    | 50 +++++++++++++
 gcc/cp/name-lookup.cc                              | 11 +++
 gcc/cp/optimize.cc                                 |  3 +
 gcc/cp/rtti.cc                                     | 71 +++++++++++++++++-
 gcc/doc/extend.texi                                | 52 ++++++++++++++
 gcc/symtab.cc                                      | 36 ++++++++++
 gcc/testsuite/c-c++-common/attr-sym-alias-neg.c    | 11 +++
 gcc/testsuite/c-c++-common/goacc/declare-1.c       |  6 +-
 gcc/testsuite/c-c++-common/goacc/declare-2.c       | 14 ++--
 .../c-c++-common/torture/attr-sym-alias-1.c        | 39 ++++++++++
 .../c-c++-common/torture/attr-sym-alias-2.c        | 13 ++++
 .../c-c++-common/torture/attr-sym-alias-3.c        | 41 +++++++++++
 .../c-c++-common/torture/attr-sym-alias-4.c        | 28 ++++++++
 .../c-c++-common/torture/attr-sym-alias-5.c        | 15 ++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C    | 72 +++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C    | 26 +++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C    | 83 ++++++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C    | 28 ++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C    | 14 ++++
 gcc/varpool.cc                                     |  3 +
 33 files changed, 810 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 ad0be511d48..b8de16ebf7c 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 ``sym_alias`` 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 fd15459203a..20045698962 100644
--- a/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
+++ b/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
@@ -4274,6 +4274,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__ ((__sym_alias__ ("Ctor_For_Animal")))
         Animal() {Age_Count = 0;};
       private:
         int Age_Count;
@@ -4306,6 +4307,7 @@ both Carnivore and Domestic, that is:
         virtual int  Number_Of_Teeth ();
         virtual void Set_Owner (char* Name);
 
+        __attribute__ ((__sym_alias__ ("Ctor_For_Dog"))) // mnemonic alias
         Dog(); // Constructor
       private:
         int  Tooth_Count;
@@ -4344,7 +4346,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;
@@ -4360,7 +4363,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,7 +4385,8 @@ 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.
+mangled name (or an alias) 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.cc b/gcc/attribs.cc
index c7209c26acc..3ef07c885d5 100644
--- a/gcc/attribs.cc
+++ b/gcc/attribs.cc
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "target.h"
 #include "tree.h"
+#include "cgraph.h"
 #include "stringpool.h"
 #include "diagnostic-core.h"
 #include "attribs.h"
@@ -825,7 +826,8 @@ decl_attributes (tree *node, tree attributes, int flags,
 
       if (TYPE_P (*anode)
 	  && (flags & (int) ATTR_FLAG_TYPE_IN_PLACE)
-	  && COMPLETE_TYPE_P (*anode))
+	  && COMPLETE_TYPE_P (*anode)
+	  && !is_attribute_p ("sym_alias", name))
 	{
 	  warning (OPT_Wattributes, "type attributes ignored after type is already defined");
 	  continue;
@@ -2640,6 +2642,70 @@ attr_access::array_as_string (tree type) const
   return typstr;
 }
 
+/* Create a sym attribute for DECL to be visible with linkage name ID.  */
+
+tree
+create_sym_alias_decl (tree decl, tree id)
+{
+  const char *attr_str = "sym_alias";
+
+  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;
+
+      tree attr_name = get_identifier (attr_str);
+      error_at (DECL_SOURCE_LOCATION (decl),
+		"duplicate symbol name %qE in %qE attribute of %qD",
+		id, attr_name, decl);
+      inform (DECL_SOURCE_LOCATION (sym_node->decl),
+	      "already used by %qD", sym_node->decl);
+    }
+
+  tree clone = copy_node (decl);
+  DECL_ATTRIBUTES (clone) = remove_attribute (attr_str,
+					      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_extra_name_alias (clone, decl);
+    }
+  else
+    {
+      cgraph_node::create_same_body_alias (clone, decl);
+    }
+
+  return clone;
+}
+
+/* Create decls for all sym aliases requested in DECL's attributes.  */
+
+void
+create_sym_alias_decls (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl)
+      || DECL_ABSTRACT_P (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      create_sym_alias_decl (decl, id);
+    }
+}
+
 #if CHECKING_P
 
 namespace selftest
diff --git a/gcc/attribs.h b/gcc/attribs.h
index 84a43658a70..156f2c20588 100644
--- a/gcc/attribs.h
+++ b/gcc/attribs.h
@@ -398,4 +398,11 @@ extern void init_attr_rdwr_indices (rdwr_map *, tree);
 extern attr_access *get_parm_access (rdwr_map &, tree,
 				     tree = current_function_decl);
 
+extern tree create_sym_alias_decl (tree, tree);
+extern void create_sym_alias_decls (tree);
+
+#define FOR_EACH_SYM_ALIAS(sym, attrs)					\
+  for (tree sym = lookup_attribute ("sym_alias", (attrs));		\
+       sym; sym = lookup_attribute ("sym_alias", TREE_CHAIN (sym)))
+
 #endif // GCC_ATTRIBS_H
diff --git a/gcc/c-family/c-ada-spec.cc b/gcc/c-family/c-ada-spec.cc
index 050994d8416..5042b9cfecd 100644
--- a/gcc/c-family/c-ada-spec.cc
+++ b/gcc/c-family/c-ada-spec.cc
@@ -1431,6 +1431,13 @@ pp_ada_tree_identifier (pretty_printer *buffer, tree node, tree type,
 static void
 pp_asm_name (pretty_printer *buffer, tree t)
 {
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (t))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.cc b/gcc/c-family/c-attribs.cc
index 2b20e58c922..392437eab3b 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -108,7 +108,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_sym_alias_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,
@@ -388,6 +389,8 @@ const struct attribute_spec c_common_attribute_table[] =
 			      handle_alias_attribute, NULL },
   { "weakref",                0, 1, true,  false, false, false,
 			      handle_weakref_attribute, NULL },
+  { "sym_alias",              1, 1, false,  false, false, false,
+			      handle_sym_alias_attribute, NULL },
   { "no_instrument_function", 0, 0, true,  false, false, false,
 			      handle_no_instrument_function_attribute,
 			      NULL },
@@ -3002,7 +3005,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
@@ -3012,7 +3015,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
@@ -3022,6 +3025,29 @@ handle_alias_attribute (tree *node, tree name, tree args,
   return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs);
 }
 
+/* Handle a "sym_alias" attribute; arguments as in struct
+   attribute_spec.handler.  */
+
+static tree
+handle_sym_alias_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 (decl_in_symtab_p (node))
+    *no_add_attrs = false;
+  else if (TYPE_P (node) && c_dialect_cxx ())
+    *no_add_attrs = false;
+  else
+    return error_mark_node;
+
+  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.  */
 
@@ -3155,6 +3181,7 @@ handle_copy_attribute (tree *node, tree name, tree args,
 	      || is_attribute_p ("visibility", atname)
 	      || is_attribute_p ("weak", atname)
 	      || is_attribute_p ("weakref", atname)
+	      || is_attribute_p ("sym_alias", atname)
 	      || is_attribute_p ("target_clones", atname))
 	    continue;
 
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index cf1df82c0f4..e5a1ee54cb3 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -3119,6 +3119,8 @@ duplicate_decls (tree newdecl, tree olddecl)
 
   merge_decls (newdecl, olddecl, newtype, oldtype);
 
+  symtab_node::remap_sym_alias_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.cc b/gcc/cgraph.cc
index f93259a8c70..0bef2179e89 100644
--- a/gcc/cgraph.cc
+++ b/gcc/cgraph.cc
@@ -523,6 +523,8 @@ cgraph_node::create (tree decl)
   node->register_symbol ();
   maybe_record_nested_function (node);
 
+  create_sym_alias_decls (decl);
+
   return node;
 }
 
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index cfdd9f693a8..cb5ac01f7e2 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -327,6 +327,10 @@ public:
   /* Return DECL that alias is aliasing.  */
   inline tree get_alias_target_tree ();
 
+  /* Remap sym alias nodes recorded as aliasing REPLACED to alias REPLACEMENT
+     instead.  */
+  static void remap_sym_alias_target (tree replaced, tree replacement);
+
   /* Set section for symbol and its aliases.  */
   void set_section (const char *section);
 
diff --git a/gcc/cgraphunit.cc b/gcc/cgraphunit.cc
index bccd2f2abb5..eb2d05094e9 100644
--- a/gcc/cgraphunit.cc
+++ b/gcc/cgraphunit.cc
@@ -1175,7 +1175,7 @@ analyze_functions (bool first_time)
      C++ FE is confused about the COMDAT groups being right.  */
   if (symtab->cpp_implicit_aliases_done)
     FOR_EACH_SYMBOL (node)
-      if (node->cpp_implicit_alias)
+      if (node->cpp_implicit_alias && node->analyzed)
 	  node->fixup_same_cpp_alias_visibility (node->get_alias_target ());
   build_type_inheritance_graph ();
 
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index 6fdb56abfb9..fa8fc63db82 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -4896,6 +4896,68 @@ check_methods (tree t)
     }
 }
 
+/* Adjust sym alias name for CLONE, cloned from FN and named NAME,
+   if it is a cdtor, and drop the sym alias from other clones.  */
+
+void
+adjust_clone_attributes (tree fn, tree clone, tree name, bool skip_copy_p)
+{
+  if (IDENTIFIER_CDTOR_P (name))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	{
+	  found = true;
+	  break;
+	}
+
+      if (found
+	  && (name == complete_ctor_identifier
+	      || name == complete_dtor_identifier))
+	{
+	  /* Reuse the sym alias decls created for the primary cdtor
+	     decl.  */
+	  symtab_node::remap_sym_alias_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);
+
+	  if (!skip_copy_p)
+	    DECL_ATTRIBUTES (clone) = copy_list (DECL_ATTRIBUTES (clone));
+
+	  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	    {
+	      /* We need to copy this even with skip_copy_p, because
+		 even then copying was shallow.  */
+	      TREE_VALUE (sym) = copy_list (TREE_VALUE (sym));
+	      /* Append suf to the sym alias name.  */
+	      tree str = TREE_VALUE (TREE_VALUE (sym));
+	      char *symname = concat (TREE_STRING_POINTER (str), suf, NULL);
+	      str = build_string (TREE_STRING_LENGTH (str) + xlen, symname);
+	      TREE_VALUE (TREE_VALUE (sym)) = str;
+	      free (symname);
+	    }
+
+	  if (symtab_node::get (clone))
+	    create_sym_alias_decls (clone);
+	}
+    }
+  else
+    DECL_ATTRIBUTES (clone)
+      = remove_attribute ("sym_alias", DECL_ATTRIBUTES (clone));
+}
+
 /* FN is constructor, destructor or operator function.  Clone the
    declaration to create a NAME'd variant.  NEED_VTT_PARM_P and
    OMIT_INHERITED_PARMS_P are relevant if it's a cdtor.  */
@@ -5065,6 +5127,8 @@ build_clone (tree fn, tree name, bool need_vtt_parm_p,
   DECL_CHAIN (clone) = DECL_CHAIN (fn);
   DECL_CHAIN (fn) = clone;
 
+  adjust_clone_attributes (fn, clone, name);
+
   return clone;
 }
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 964af1ddd85..2bafed4e993 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5787,6 +5787,8 @@ struct GTY((for_user)) spec_entry
 
 extern int current_class_depth;
 
+void adjust_clone_attributes (tree fn, tree clone, tree name, bool = false);
+
 /* in decl.cc */
 
 /* An array of static vars & fns.  */
@@ -7022,6 +7024,7 @@ extern void do_push_parm_decls			(tree, tree, tree *);
 extern tree do_aggregate_paren_init		(tree, tree);
 
 /* in decl2.cc */
+extern void update_sym_alias_interface		(tree);
 extern void record_mangling			(tree, bool);
 extern void overwrite_mangling			(tree, tree);
 extern void note_mangling_alias			(tree, tree);
@@ -7596,6 +7599,7 @@ extern bool emit_tinfo_decl			(tree);
 extern unsigned get_pseudo_tinfo_index		(tree);
 extern tree get_pseudo_tinfo_type		(unsigned);
 extern tree build_if_nonnull			(tree, tree, tsubst_flags_t);
+extern void update_tinfo_sym_alias		(tree);
 
 /* in search.cc */
 extern tree get_parent_with_private_access 	(tree decl, tree binfo);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 2f2dbb8d107..5636d694b68 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -3237,6 +3237,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
 	      && TREE_STATIC (olddecl))))
     make_decl_rtl (olddecl);
 
+  symtab_node::remap_sym_alias_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.
@@ -3260,6 +3262,7 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
       FOR_EACH_CLONE (clone, olddecl)
 	{
 	  DECL_ATTRIBUTES (clone) = DECL_ATTRIBUTES (olddecl);
+	  adjust_clone_attributes (olddecl, clone, DECL_NAME (clone));
 	  DECL_PRESERVE_P (clone) |= DECL_PRESERVE_P (olddecl);
 	}
     }
@@ -10789,6 +10792,7 @@ grokfndecl (tree ctype,
     {
       cplus_decl_attributes (&decl, *attrlist, 0);
       *attrlist = NULL_TREE;
+      create_sym_alias_decls (decl);
     }
 
   if (DECL_HAS_CONTRACTS_P (decl))
diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
index 9e666e5eece..c7f009beaf0 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -1844,6 +1844,9 @@ cplus_decl_attributes (tree *decl, tree attributes, int flags)
   if (late_attrs)
     save_template_attributes (late_attrs, decl, flags);
 
+  if (TYPE_P (*decl) && attributes)
+    update_tinfo_sym_alias (*decl);
+
   /* Propagate deprecation out to the template.  */
   if (TREE_DEPRECATED (*decl))
     if (tree ti = get_template_info (*decl))
@@ -2200,6 +2203,47 @@ 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 sym aliases.  */
+
+void
+update_sym_alias_interface (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.  */
 
@@ -3007,6 +3051,8 @@ determine_visibility (tree decl)
        translation unit, we can make the type internal.  */
     constrain_visibility (decl, VISIBILITY_ANON, false);
 
+  update_sym_alias_interface (decl);
+
   /* If visibility changed and DECL already has DECL_RTL, ensure
      symbol flags are updated.  */
   if ((DECL_VISIBILITY (decl) != orig_visibility
@@ -3269,6 +3315,8 @@ tentative_decl_linkage (tree decl)
       else if (VAR_P (decl))
 	maybe_commonize_var (decl);
     }
+
+  update_sym_alias_interface (decl);
 }
 
 /* DECL is a FUNCTION_DECL or VAR_DECL.  If the object file linkage
@@ -3503,6 +3551,8 @@ import_export_decl (tree decl)
     }
 
   DECL_INTERFACE_KNOWN (decl) = 1;
+
+  update_sym_alias_interface (decl);
 }
 
 /* Return an expression that performs the destruction of DECL, which
diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc
index d19ea5d121c..f877780b2ac 100644
--- a/gcc/cp/name-lookup.cc
+++ b/gcc/cp/name-lookup.cc
@@ -22,9 +22,11 @@ along with GCC; see the file COPYING3.  If not see
 #define INCLUDE_MEMORY
 #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"
@@ -3479,6 +3481,15 @@ push_local_extern_decl_alias (tree decl)
 	  /* Adjust visibility.  */
 	  determine_visibility (alias);
 	}
+      else if (DECL_P (alias))
+	DECL_ATTRIBUTES (alias)
+	  = targetm.merge_decl_attributes (alias, decl);
+      if (DECL_P (alias))
+	{
+	  symtab_node::remap_sym_alias_target (decl, alias);
+	  DECL_ATTRIBUTES (decl)
+	    = remove_attribute ("sym_alias", DECL_ATTRIBUTES (alias));
+	}
     }
 
   retrofit_lang_decl (decl);
diff --git a/gcc/cp/optimize.cc b/gcc/cp/optimize.cc
index 9e8926e4cc6..4a2e8cb4354 100644
--- a/gcc/cp/optimize.cc
+++ b/gcc/cp/optimize.cc
@@ -528,9 +528,12 @@ maybe_clone_body (tree fn)
       DECL_VISIBILITY_SPECIFIED (clone) = DECL_VISIBILITY_SPECIFIED (fn);
       DECL_DLLIMPORT_P (clone) = DECL_DLLIMPORT_P (fn);
       DECL_ATTRIBUTES (clone) = clone_attrs (DECL_ATTRIBUTES (fn));
+      adjust_clone_attributes (fn, clone, DECL_NAME (clone), true);
       DECL_DISREGARD_INLINE_LIMITS (clone) = DECL_DISREGARD_INLINE_LIMITS (fn);
       set_decl_section_name (clone, fn);
 
+      update_sym_alias_interface (clone);
+
       /* Adjust the parameter names and locations.  */
       parm = DECL_ARGUMENTS (fn);
       clone_parm = DECL_ARGUMENTS (clone);
diff --git a/gcc/cp/rtti.cc b/gcc/cp/rtti.cc
index 7878929c246..4bb7b8c8c6e 100644
--- a/gcc/cp/rtti.cc
+++ b/gcc/cp/rtti.cc
@@ -28,8 +28,10 @@ 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"
+#include "cgraph.h"
 
 /* C++ returns type information to the user in struct type_info
    objects. We also use type information to implement dynamic_cast and
@@ -479,8 +481,13 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
 	  = build_tree_list (get_identifier ("non overlapping"),
 			     NULL_TREE);
       else
+	/* Share the non overlapping attribute, without assuming it's
+	   the only attribute, but assuming it's the last if it's
+	   present.  There may be sym aliases too, and those are not
+	   to be shared.  */
 	DECL_ATTRIBUTES (d)
-	  = DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]);
+	  = lookup_attribute ("non overlapping",
+			      DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]));
 
       /* Mark the variable as undefined -- but remember that we can
 	 define it later if we need to do so.  */
@@ -492,6 +499,16 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
       if (CLASS_TYPE_P (type))
 	CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type)) = d;
 
+      /* Copy sym alias attributes from the type to the rtti obj decl.  */
+      tree *attrs = &DECL_ATTRIBUTES (d);
+      FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+	{
+	  tree attr = tree_cons (TREE_PURPOSE (sym), TREE_VALUE (sym), *attrs);
+	  *attrs = attr;
+	  attrs = &TREE_CHAIN (attr);
+	}
+      create_sym_alias_decls (d);
+
       /* Add decl to the global array of tinfo decls.  */
       vec_safe_push (unemitted_tinfo_decls, d);
     }
@@ -499,6 +516,58 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
   return d;
 }
 
+/* After modifying the attributes of TYPE, check whether tinfo was
+   already created and, if so, add to it any sym alias attributes
+   that were not already present.  */
+
+void
+update_tinfo_sym_alias (tree type)
+{
+  if (!TYPE_SIZE (type) || !CLASS_TYPE_P (type))
+    return;
+
+  tree d = CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type));
+  if (!d)
+    return;
+
+  bool first = true;
+  symtab_node *node = NULL;
+
+  tree *attrs = &DECL_ATTRIBUTES (d);
+  FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (d_sym, *attrs)
+	if (TREE_VALUE (sym) == TREE_VALUE (d_sym))
+	  {
+	    found = true;
+	    break;
+	  }
+
+      if (found)
+	continue;
+
+      tree attr = tree_cons (TREE_PURPOSE (sym),
+			     TREE_VALUE (sym),
+			     *attrs);
+      *attrs = attr;
+      attrs = &TREE_CHAIN (attr);
+
+      if (first)
+	{
+	  first = false;
+	  node = symtab_node::get (d);
+	}
+
+      if (!node)
+	continue;
+
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+      create_sym_alias_decl (d, id);
+    }
+}
+
 /* Return the type_info object for TYPE.  */
 
 tree
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index e6de0815846..96674e2d857 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -4141,6 +4141,39 @@ Function Attributes}, @ref{PowerPC Function Attributes},
 @ref{Nios II Function Attributes}, and @ref{S/390 Function Attributes}
 for details.
 
+@cindex @code{sym_alias} function attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} attribute causes @var{name} to be output as an
+alias to the definition.  For instance,
+
+@smallexample
+void f (uint64_t) __attribute__ ((__sym_alias__ ("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 for aliases,
+and may not be available on all targets.
+
 @cindex @code{symver} function attribute
 @item symver ("@var{name2}@@@var{nodename}")
 On ELF targets this attribute creates a symbol version.  The @var{name2} part
@@ -8069,6 +8102,10 @@ will be placed in new, unique sections.
 
 This additional functionality requires Binutils version 2.36 or later.
 
+@cindex @code{sym_alias} variable attribute
+@item sym_alias ("@var{name}")
+See @pxref{Common Function Attributes}.
+
 @cindex @code{uninitialized} variable attribute
 @item uninitialized
 This attribute, attached to a variable with automatic storage, means that
@@ -9164,6 +9201,21 @@ is not supported; that is to say, if a given scalar object can be accessed
 through distinct types that assign a different storage order to it, then the
 behavior is undefined.
 
+@cindex @code{sym_alias} type attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} type 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__ ((__sym_alias__ ("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.
+
 @cindex @code{transparent_union} type attribute
 @item transparent_union
 
diff --git a/gcc/symtab.cc b/gcc/symtab.cc
index 0470509a98d..0812088e59f 100644
--- a/gcc/symtab.cc
+++ b/gcc/symtab.cc
@@ -1943,6 +1943,42 @@ symtab_node::noninterposable_alias (symtab_node *node, void *data)
   return false;
 }
 
+/* Remap sym alias nodes recorded as aliasing REPLACED to alias
+   REPLACEMENT instead.  */
+
+void
+symtab_node::remap_sym_alias_target (tree replaced, tree replacement)
+{
+  if (!decl_in_symtab_p (replacement)
+      || !symtab_node::get (replacement))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (replaced))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      symtab_node *sym_node = symtab_node::get_for_asmname (id);
+
+      if (!sym_node)
+	{
+	  create_sym_alias_decl (replacement, id);
+	  continue;
+	}
+
+      gcc_assert (!sym_node->analyzed);
+      if (sym_node->alias_target != replaced)
+	continue;
+
+      sym_node->definition = 0;
+
+      if (VAR_P (replaced))
+	varpool_node::create_extra_name_alias (sym_node->decl, replacement);
+      else
+	cgraph_node::create_same_body_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-sym-alias-neg.c b/gcc/testsuite/c-c++-common/attr-sym-alias-neg.c
new file mode 100644
index 00000000000..16a982dbce2
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/attr-sym-alias-neg.c
@@ -0,0 +1,11 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int f () {
+  int i __attribute__ ((sym_alias ("f_i"))); /* { dg-warning "ignored" } */
+  return i;
+}
+
+struct s {
+  int j __attribute__ ((sym_alias ("s_j"))); /* { dg-warning "ignored" } */
+};
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-1.c b/gcc/testsuite/c-c++-common/goacc/declare-1.c
index 46ee01b6759..f2842893318 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-1.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-1.c
@@ -113,11 +113,11 @@ f_2 (void)
   int va3;
 #pragma acc declare device_resident(va3)
 
-#ifndef __cplusplus
+#if 0
   /* TODO PR90868
 
-     C: "error: variable '[...]' used more than once with '#pragma acc declare'".  */
-#else
+     "error: variable '[...]' used more than once with '#pragma acc declare'".  */
+
   extern int ve0;
 #pragma acc declare create(ve0)
 
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-2.c b/gcc/testsuite/c-c++-common/goacc/declare-2.c
index e2e22be57e9..aec59b69754 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-2.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-2.c
@@ -137,25 +137,25 @@ void
 f_pr90868_2 (void)
 {
   extern int we0;
-#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" } */
 
   extern int we1;
-#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" } */
 
   extern int *we2;
-#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" } */
 
   extern int we3;
-#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" } */
 
   extern int we4;
-#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" } */
 
   extern int we5;
-#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" } */
  
   extern int we6;
-#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" } */
 }
 
 
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
new file mode 100644
index 00000000000..61af50cb552
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
@@ -0,0 +1,39 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+int var_a = 1;
+
+void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+
+void
+foo_a ()
+{
+}
+
+
+int var_b;
+extern int var_b __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+
+void
+foo_b ()
+{
+}
+
+void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+
+
+int var_c __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+
+void __attribute__ ((__sym_alias__ ("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-sym-alias-2.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
new file mode 100644
index 00000000000..6f0d55ca42c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+struct s
+{
+  int mem __attribute__ ((__sym_alias__ ("MEMFOO"))); /* { dg-warning "attribute ignored" } */
+};
+
+void foo()
+{
+  extern void bar () __attribute__ ((__sym_alias__ ("FOOBAR")));
+  int var __attribute__ ((__sym_alias__ ("FOOVAR"))); /* { dg-warning "attribute ignored" } */
+}
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
new file mode 100644
index 00000000000..0052485a30a
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
@@ -0,0 +1,41 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int var_a = 1;
+
+void
+foo_a ()
+{
+  extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+}
+
+#if 0 // __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__ ((__sym_alias__ ("FOOVAR_B")));
+}
+
+int var_b;
+
+void __attribute__ ((__sym_alias__ ("FOOBAR_C")))
+foo_c ()
+{
+  void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+  /* Another sym for var_b.  */
+  extern int var_b __attribute__ ((__sym_alias__ ("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-sym-alias-4.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
new file mode 100644
index 00000000000..2beafa46379
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-require-alias "" } */
+
+int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A"))) = 42;
+
+int __attribute__ ((__sym_alias__ ("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/c-c++-common/torture/attr-sym-alias-5.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
new file mode 100644
index 00000000000..ccd4c70800d
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int f () {
+  static int x __attribute__ ((sym_alias ("f_x")));
+  return x;
+}
+
+extern int f_x_ref __attribute__ ((alias ("f_x")));
+
+int g () {
+  return f_x_ref;
+}
+
+/* { dg-final { scan-assembler "f_x" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
new file mode 100644
index 00000000000..579eda1a473
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
@@ -0,0 +1,72 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+class __attribute__ ((__sym_alias__ ("FOOCLS_A"),
+		      __sym_alias__ ("FOOCLS_A_Dupe"))) foo {
+  static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  void __attribute__ ((__sym_alias__ ("FOOBAR_A"))) bar ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+int foo::var = 1;
+
+foo::foo () {}
+
+void foo::bar () {}
+
+namespace b {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_B"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_B"))) foo ();
+    void __attribute__ ((__sym_alias__ ("FOOBAR_B"))) bar () {}
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_B"))) ~foo() {}
+  };
+
+  int foo::var = 2;
+
+  foo::foo () {
+    void (foo::*pbar)() = &foo::bar;
+  }
+}
+
+namespace c {
+  namespace cd {
+    class __attribute__ ((__sym_alias__ ("FOOCLS_C"))) foo {
+      static int var __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+      __attribute__ ((__sym_alias__ ("FOOCTR_C"))) foo () {
+	void (foo::*pbar)() = &foo::bar;
+      }
+      void __attribute__ ((__sym_alias__ ("FOOBAR_C"))) bar () {}
+      virtual __attribute__ ((__sym_alias__ ("FOODTR_C"))) ~foo() {}
+    };
+
+    int foo::var = 3;
+  }
+}
+
+/* { dg-final { scan-assembler "FOOCLS_A" } } */
+/* { dg-final { scan-assembler "FOOCLS_A_Dupe" } } */
+/* { 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-sym-alias-2.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
new file mode 100644
index 00000000000..6facfcc2cd9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+namespace {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_A"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo ();
+    void __attribute__ ((__sym_alias__ ("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-sym-alias-3.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
new file mode 100644
index 00000000000..42c7288ad4a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
@@ -0,0 +1,83 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+// sym can be applied to template function explicit instantiations.
+
+template <typename T>
+void
+fn(T) {
+};
+
+template void __attribute__ ((__sym_alias__ ("FOOFUN_UINT"))) fn<>(unsigned int);
+template void __attribute__ ((__sym_alias__ ("FOOFUN_LONG"))) fn<>(long);
+
+template<> void __attribute__ ((__sym_alias__ ("FOOFUN_CHAR"))) fn<>(char) {}
+
+
+template <typename T = void>
+struct
+foo {
+  virtual ~foo() {}
+
+  virtual void virtfun() {}
+
+  static void stfun() {}
+  void inlfun() {}
+};
+
+// Explicitly instantiate members before the enclosing class.
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_CHAR_VIRT"))) foo<char>::virtfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_CHAR_TI"))) foo<char>;
+
+// Though they're only output if the enclosing class is.
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_LONG_VIRT"))) foo<long>::virtfun();
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_LONG_TI_X"))) foo<long>;
+
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_VOID_ST"))) foo<void>::stfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_VOID_TI"))) foo<>;
+
+
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_X"))) foo<short>;
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_ST"))) foo<short>::stfun();
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_INL"))) foo<short>::inlfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_D"))) foo<short>;
+
+// Explicit specializations work too.
+
+template <>
+struct  __attribute__ ((__sym_alias__ ("FOOCLS_INT_TI")))
+foo<int>
+{
+  virtual ~foo() {}
+  virtual void __attribute__ ((__sym_alias__ ("FOOCLS_INT_VIRT"))) virtfun() {}
+};
+
+/* { dg-final { scan-assembler "FOOFUN_UINT" } } */
+/* { dg-final { scan-assembler "FOOFUN_LONG" } } */
+/* { dg-final { scan-assembler "FOOFUN_CHAR" } } */
+
+/* { dg-final { scan-assembler "FOOCLS_VOID_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_VOID_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_X" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_INL" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_D" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_TI_X" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_VIRT" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
new file mode 100644
index 00000000000..59b779de35d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+template <typename T = void>
+class
+__attribute__ ((__sym_alias__ ("FOOCLS")))
+foo // { dg-error "duplicate|already" }
+{
+  virtual ~foo() {}
+
+  template <typename U>
+  void
+    __attribute__ ((__sym_alias__ ("FOOTMF")))
+    tmemfun () {} // { dg-error "duplicate|already" }
+};
+
+template <typename T>
+void
+__attribute__ ((__sym_alias__ ("FOOTFN")))
+fn(T) { // { dg-error "duplicate|already" }
+};
+
+template class foo<>;
+template class foo<int>;
+template void foo<>::tmemfun<void>();
+template void foo<int>::tmemfun<void>();
+template void fn<>(int);
+template void fn<>(long);
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
new file mode 100644
index 00000000000..45d59b953c3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
@@ -0,0 +1,14 @@
+/* { dg-do compile { target c++11 } } */
+/* { dg-require-alias "" } */
+
+struct foo {
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+foo::foo () {}
+
+// Make sure the inherited cdtors don't duplicate the syms.
+struct bar : foo {
+  using foo::foo;
+};
diff --git a/gcc/varpool.cc b/gcc/varpool.cc
index e7b51b15e4a..33649906d1d 100644
--- a/gcc/varpool.cc
+++ b/gcc/varpool.cc
@@ -163,6 +163,9 @@ varpool_node::get_create (tree decl)
     }
 
   node->register_symbol ();
+
+  create_sym_alias_decls (decl);
+
   return node;
 }

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [gcc(refs/users/aoliva/heads/testme)] Introduce attribute sym_alias
@ 2023-11-30 10:36 Alexandre Oliva
  0 siblings, 0 replies; 33+ messages in thread
From: Alexandre Oliva @ 2023-11-30 10:36 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:756dfecf202a05dc583fa5c6fbefd4b45ac7f66a

commit 756dfecf202a05dc583fa5c6fbefd4b45ac7f66a
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Fri Jul 14 03:45:14 2023 -0300

    Introduce attribute sym_alias
    
    This patch introduces an attribute to add extra asm names (aliases)
    for a decl 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 class types, namespace-scoped
    variables and functions, static data members, member functions,
    explicit instantiations and specializations of template functions,
    members and classes.
    
    When applied to constructors or destructor, additional sym aliases
    with _Base and _Del suffixes are defined for variants other than
    complete-object ones.  This changes the assumption that clones always
    carry the same attributes as their abstract declarations, so there is
    now a function to adjust them.
    
    C++ also had a bug in which attributes from local extern declarations
    failed to be propagated to a preexisting corresponding
    namespace-scoped decl.  I've fixed that, and adjusted acc tests that
    distinguished between C and C++ in this regard.
    
    Applying the attribute to class types is only valid in C++, and the
    effect is to attach the alias to the RTTI object associated with the
    class type.
    
    for  gcc/ChangeLog
    
            * attribs.cc: Include cgraph.h.
            (decl_attributes): Allow late introduction of sym_alias in
            types.
            (create_sym_alias_decl, create_sym_alias_decls): New.
            * attribs.h: Declare them.
            (FOR_EACH_SYM_ALIAS): New macro.
            * cgraph.cc (cgraph_node::create): Create sym_alias decls.
            * varpool.cc (varpool_node::get_create): Create sym_alias
            decls.
            * cgraph.h (symtab_node::remap_sym_alias_target): New.
            * symtab.cc (symtab_node::remap_sym_alias_target): Define.
            * cgraphunit.cc (cgraph_node::analyze): Create alias_target
            node if needed.
            (analyze_functions): Fixup visibility of implicit alias only
            after its node is analyzed.
            * doc/extend.texi (sym_alias): Document for variables,
            functions and types.
    
    for  gcc/ada/ChangeLog
    
            * doc/gnat_rm/interfacing_to_other_languages.rst: Mention
            attribute sym_alias to give RTTI symbols mnemonic names.
            * doc/gnat_ugn/the_gnat_compilation_model.rst: Mention
            aliases.  Fix incorrect ref to C1 ctor variant.
    
    for  gcc/c-family/ChangeLog
    
            * c-ada-spec.cc (pp_asm_name): Use first sym_alias if
            available.
            * c-attribs.cc (handle_sym_alias_attribute): New.
            (c_common_attribute_table): Add sym_alias.
            (handle_copy_attribute): Do not copy sym_alias attribute.
    
    for  gcc/c/ChangeLog
    
            * c-decl.cc (duplicate_decls): Remap sym_alias target.
    
    for  gcc/cp/ChangeLog
    
            * class.cc (adjust_clone_attributes): New.
            (copy_fndecl_with_name, build_clone): Call it.
            * cp-tree.h (adjust_clone_attributes): Declare.
            (update_sym_alias_interface): Declare.
            (update_tinfo_sym_alias): Declare.
            * decl.cc (duplicate_decls): Remap sym_alias target.
            Adjust clone attributes.
            (grokfndecl): Tentatively create sym_alias decls after
            adding attributes in e.g. a template member function explicit
            instantiation.
            * decl2.cc (cplus_decl_attributes): Update tinfo sym_alias.
            (copy_interface, update_sym_alias_interface): New.
            (determine_visibility): Update sym_alias interface.
            (tentative_decl_linkage, import_export_decl): Likewise.
            * name-lookup.cc: Include target.h and cgraph.h.
            (push_local_extern_decl_alias): Merge attributes with
            namespace-scoped decl, and drop duplicate sym_alias.
            * optimize.cc (maybe_clone_body): Re-adjust attributes after
            cloning them.  Update sym_alias interface.
            * rtti.cc: Include attribs.h and cgraph.h.
            (get_tinfo_decl): Copy sym_alias attributes from type to tinfo
            decl.  Create sym_alias decls.
            (update_tinfo_sym_alias): New.
    
    for  gcc/testsuite/ChangeLog
    
            * c-c++-common/goacc/declare-1.c: Adjust.
            * c-c++-common/goacc/declare-2.c: Adjust.
            * c-c++-common/torture/attr-sym-alias-1.c: New.
            * c-c++-common/torture/attr-sym-alias-2.c: New.
            * c-c++-common/torture/attr-sym-alias-3.c: New.
            * c-c++-common/torture/attr-sym-alias-4.c: New.
            * c-c++-common/attr-sym-alias-neg.c: New.
            * g++.dg/torture/attr-sym-alias-1.C: New.
            * g++.dg/torture/attr-sym-alias-2.C: New.
            * g++.dg/torture/attr-sym-alias-3.C: New.
            * g++.dg/torture/attr-sym-alias-4.C: New.
            * g++.dg/torture/attr-sym-alias-5.C: New.

Diff:
---
 .../doc/gnat_rm/interfacing_to_other_languages.rst |  6 ++
 .../doc/gnat_ugn/the_gnat_compilation_model.rst    | 10 ++-
 gcc/attribs.cc                                     | 68 +++++++++++++++++-
 gcc/attribs.h                                      |  7 ++
 gcc/c-family/c-ada-spec.cc                         |  7 ++
 gcc/c-family/c-attribs.cc                          | 33 ++++++++-
 gcc/c/c-decl.cc                                    |  2 +
 gcc/cgraph.cc                                      |  2 +
 gcc/cgraph.h                                       |  4 ++
 gcc/cgraphunit.cc                                  |  2 +-
 gcc/cp/class.cc                                    | 64 +++++++++++++++++
 gcc/cp/cp-tree.h                                   |  4 ++
 gcc/cp/decl.cc                                     |  4 ++
 gcc/cp/decl2.cc                                    | 50 +++++++++++++
 gcc/cp/name-lookup.cc                              | 11 +++
 gcc/cp/optimize.cc                                 |  3 +
 gcc/cp/rtti.cc                                     | 71 +++++++++++++++++-
 gcc/doc/extend.texi                                | 52 ++++++++++++++
 gcc/symtab.cc                                      | 36 ++++++++++
 gcc/testsuite/c-c++-common/attr-sym-alias-neg.c    | 11 +++
 gcc/testsuite/c-c++-common/goacc/declare-1.c       |  6 +-
 gcc/testsuite/c-c++-common/goacc/declare-2.c       | 14 ++--
 .../c-c++-common/torture/attr-sym-alias-1.c        | 39 ++++++++++
 .../c-c++-common/torture/attr-sym-alias-2.c        | 13 ++++
 .../c-c++-common/torture/attr-sym-alias-3.c        | 41 +++++++++++
 .../c-c++-common/torture/attr-sym-alias-4.c        | 28 ++++++++
 .../c-c++-common/torture/attr-sym-alias-5.c        | 15 ++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C    | 72 +++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C    | 26 +++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C    | 83 ++++++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C    | 28 ++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C    | 14 ++++
 gcc/varpool.cc                                     |  3 +
 33 files changed, 810 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 ad0be511d48..b8de16ebf7c 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 ``sym_alias`` 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 fd15459203a..20045698962 100644
--- a/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
+++ b/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
@@ -4274,6 +4274,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__ ((__sym_alias__ ("Ctor_For_Animal")))
         Animal() {Age_Count = 0;};
       private:
         int Age_Count;
@@ -4306,6 +4307,7 @@ both Carnivore and Domestic, that is:
         virtual int  Number_Of_Teeth ();
         virtual void Set_Owner (char* Name);
 
+        __attribute__ ((__sym_alias__ ("Ctor_For_Dog"))) // mnemonic alias
         Dog(); // Constructor
       private:
         int  Tooth_Count;
@@ -4344,7 +4346,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;
@@ -4360,7 +4363,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,7 +4385,8 @@ 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.
+mangled name (or an alias) 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.cc b/gcc/attribs.cc
index c7209c26acc..3ef07c885d5 100644
--- a/gcc/attribs.cc
+++ b/gcc/attribs.cc
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "target.h"
 #include "tree.h"
+#include "cgraph.h"
 #include "stringpool.h"
 #include "diagnostic-core.h"
 #include "attribs.h"
@@ -825,7 +826,8 @@ decl_attributes (tree *node, tree attributes, int flags,
 
       if (TYPE_P (*anode)
 	  && (flags & (int) ATTR_FLAG_TYPE_IN_PLACE)
-	  && COMPLETE_TYPE_P (*anode))
+	  && COMPLETE_TYPE_P (*anode)
+	  && !is_attribute_p ("sym_alias", name))
 	{
 	  warning (OPT_Wattributes, "type attributes ignored after type is already defined");
 	  continue;
@@ -2640,6 +2642,70 @@ attr_access::array_as_string (tree type) const
   return typstr;
 }
 
+/* Create a sym attribute for DECL to be visible with linkage name ID.  */
+
+tree
+create_sym_alias_decl (tree decl, tree id)
+{
+  const char *attr_str = "sym_alias";
+
+  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;
+
+      tree attr_name = get_identifier (attr_str);
+      error_at (DECL_SOURCE_LOCATION (decl),
+		"duplicate symbol name %qE in %qE attribute of %qD",
+		id, attr_name, decl);
+      inform (DECL_SOURCE_LOCATION (sym_node->decl),
+	      "already used by %qD", sym_node->decl);
+    }
+
+  tree clone = copy_node (decl);
+  DECL_ATTRIBUTES (clone) = remove_attribute (attr_str,
+					      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_extra_name_alias (clone, decl);
+    }
+  else
+    {
+      cgraph_node::create_same_body_alias (clone, decl);
+    }
+
+  return clone;
+}
+
+/* Create decls for all sym aliases requested in DECL's attributes.  */
+
+void
+create_sym_alias_decls (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl)
+      || DECL_ABSTRACT_P (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      create_sym_alias_decl (decl, id);
+    }
+}
+
 #if CHECKING_P
 
 namespace selftest
diff --git a/gcc/attribs.h b/gcc/attribs.h
index 84a43658a70..156f2c20588 100644
--- a/gcc/attribs.h
+++ b/gcc/attribs.h
@@ -398,4 +398,11 @@ extern void init_attr_rdwr_indices (rdwr_map *, tree);
 extern attr_access *get_parm_access (rdwr_map &, tree,
 				     tree = current_function_decl);
 
+extern tree create_sym_alias_decl (tree, tree);
+extern void create_sym_alias_decls (tree);
+
+#define FOR_EACH_SYM_ALIAS(sym, attrs)					\
+  for (tree sym = lookup_attribute ("sym_alias", (attrs));		\
+       sym; sym = lookup_attribute ("sym_alias", TREE_CHAIN (sym)))
+
 #endif // GCC_ATTRIBS_H
diff --git a/gcc/c-family/c-ada-spec.cc b/gcc/c-family/c-ada-spec.cc
index 050994d8416..5042b9cfecd 100644
--- a/gcc/c-family/c-ada-spec.cc
+++ b/gcc/c-family/c-ada-spec.cc
@@ -1431,6 +1431,13 @@ pp_ada_tree_identifier (pretty_printer *buffer, tree node, tree type,
 static void
 pp_asm_name (pretty_printer *buffer, tree t)
 {
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (t))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.cc b/gcc/c-family/c-attribs.cc
index 2b20e58c922..392437eab3b 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -108,7 +108,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_sym_alias_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,
@@ -388,6 +389,8 @@ const struct attribute_spec c_common_attribute_table[] =
 			      handle_alias_attribute, NULL },
   { "weakref",                0, 1, true,  false, false, false,
 			      handle_weakref_attribute, NULL },
+  { "sym_alias",              1, 1, false,  false, false, false,
+			      handle_sym_alias_attribute, NULL },
   { "no_instrument_function", 0, 0, true,  false, false, false,
 			      handle_no_instrument_function_attribute,
 			      NULL },
@@ -3002,7 +3005,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
@@ -3012,7 +3015,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
@@ -3022,6 +3025,29 @@ handle_alias_attribute (tree *node, tree name, tree args,
   return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs);
 }
 
+/* Handle a "sym_alias" attribute; arguments as in struct
+   attribute_spec.handler.  */
+
+static tree
+handle_sym_alias_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 (decl_in_symtab_p (node))
+    *no_add_attrs = false;
+  else if (TYPE_P (node) && c_dialect_cxx ())
+    *no_add_attrs = false;
+  else
+    return error_mark_node;
+
+  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.  */
 
@@ -3155,6 +3181,7 @@ handle_copy_attribute (tree *node, tree name, tree args,
 	      || is_attribute_p ("visibility", atname)
 	      || is_attribute_p ("weak", atname)
 	      || is_attribute_p ("weakref", atname)
+	      || is_attribute_p ("sym_alias", atname)
 	      || is_attribute_p ("target_clones", atname))
 	    continue;
 
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index cf1df82c0f4..e5a1ee54cb3 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -3119,6 +3119,8 @@ duplicate_decls (tree newdecl, tree olddecl)
 
   merge_decls (newdecl, olddecl, newtype, oldtype);
 
+  symtab_node::remap_sym_alias_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.cc b/gcc/cgraph.cc
index f93259a8c70..0bef2179e89 100644
--- a/gcc/cgraph.cc
+++ b/gcc/cgraph.cc
@@ -523,6 +523,8 @@ cgraph_node::create (tree decl)
   node->register_symbol ();
   maybe_record_nested_function (node);
 
+  create_sym_alias_decls (decl);
+
   return node;
 }
 
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index cfdd9f693a8..cb5ac01f7e2 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -327,6 +327,10 @@ public:
   /* Return DECL that alias is aliasing.  */
   inline tree get_alias_target_tree ();
 
+  /* Remap sym alias nodes recorded as aliasing REPLACED to alias REPLACEMENT
+     instead.  */
+  static void remap_sym_alias_target (tree replaced, tree replacement);
+
   /* Set section for symbol and its aliases.  */
   void set_section (const char *section);
 
diff --git a/gcc/cgraphunit.cc b/gcc/cgraphunit.cc
index bccd2f2abb5..eb2d05094e9 100644
--- a/gcc/cgraphunit.cc
+++ b/gcc/cgraphunit.cc
@@ -1175,7 +1175,7 @@ analyze_functions (bool first_time)
      C++ FE is confused about the COMDAT groups being right.  */
   if (symtab->cpp_implicit_aliases_done)
     FOR_EACH_SYMBOL (node)
-      if (node->cpp_implicit_alias)
+      if (node->cpp_implicit_alias && node->analyzed)
 	  node->fixup_same_cpp_alias_visibility (node->get_alias_target ());
   build_type_inheritance_graph ();
 
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index 6fdb56abfb9..fa8fc63db82 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -4896,6 +4896,68 @@ check_methods (tree t)
     }
 }
 
+/* Adjust sym alias name for CLONE, cloned from FN and named NAME,
+   if it is a cdtor, and drop the sym alias from other clones.  */
+
+void
+adjust_clone_attributes (tree fn, tree clone, tree name, bool skip_copy_p)
+{
+  if (IDENTIFIER_CDTOR_P (name))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	{
+	  found = true;
+	  break;
+	}
+
+      if (found
+	  && (name == complete_ctor_identifier
+	      || name == complete_dtor_identifier))
+	{
+	  /* Reuse the sym alias decls created for the primary cdtor
+	     decl.  */
+	  symtab_node::remap_sym_alias_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);
+
+	  if (!skip_copy_p)
+	    DECL_ATTRIBUTES (clone) = copy_list (DECL_ATTRIBUTES (clone));
+
+	  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	    {
+	      /* We need to copy this even with skip_copy_p, because
+		 even then copying was shallow.  */
+	      TREE_VALUE (sym) = copy_list (TREE_VALUE (sym));
+	      /* Append suf to the sym alias name.  */
+	      tree str = TREE_VALUE (TREE_VALUE (sym));
+	      char *symname = concat (TREE_STRING_POINTER (str), suf, NULL);
+	      str = build_string (TREE_STRING_LENGTH (str) + xlen, symname);
+	      TREE_VALUE (TREE_VALUE (sym)) = str;
+	      free (symname);
+	    }
+
+	  if (symtab_node::get (clone))
+	    create_sym_alias_decls (clone);
+	}
+    }
+  else
+    DECL_ATTRIBUTES (clone)
+      = remove_attribute ("sym_alias", DECL_ATTRIBUTES (clone));
+}
+
 /* FN is constructor, destructor or operator function.  Clone the
    declaration to create a NAME'd variant.  NEED_VTT_PARM_P and
    OMIT_INHERITED_PARMS_P are relevant if it's a cdtor.  */
@@ -5065,6 +5127,8 @@ build_clone (tree fn, tree name, bool need_vtt_parm_p,
   DECL_CHAIN (clone) = DECL_CHAIN (fn);
   DECL_CHAIN (fn) = clone;
 
+  adjust_clone_attributes (fn, clone, name);
+
   return clone;
 }
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 964af1ddd85..2bafed4e993 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5787,6 +5787,8 @@ struct GTY((for_user)) spec_entry
 
 extern int current_class_depth;
 
+void adjust_clone_attributes (tree fn, tree clone, tree name, bool = false);
+
 /* in decl.cc */
 
 /* An array of static vars & fns.  */
@@ -7022,6 +7024,7 @@ extern void do_push_parm_decls			(tree, tree, tree *);
 extern tree do_aggregate_paren_init		(tree, tree);
 
 /* in decl2.cc */
+extern void update_sym_alias_interface		(tree);
 extern void record_mangling			(tree, bool);
 extern void overwrite_mangling			(tree, tree);
 extern void note_mangling_alias			(tree, tree);
@@ -7596,6 +7599,7 @@ extern bool emit_tinfo_decl			(tree);
 extern unsigned get_pseudo_tinfo_index		(tree);
 extern tree get_pseudo_tinfo_type		(unsigned);
 extern tree build_if_nonnull			(tree, tree, tsubst_flags_t);
+extern void update_tinfo_sym_alias		(tree);
 
 /* in search.cc */
 extern tree get_parent_with_private_access 	(tree decl, tree binfo);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 2f2dbb8d107..5636d694b68 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -3237,6 +3237,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
 	      && TREE_STATIC (olddecl))))
     make_decl_rtl (olddecl);
 
+  symtab_node::remap_sym_alias_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.
@@ -3260,6 +3262,7 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
       FOR_EACH_CLONE (clone, olddecl)
 	{
 	  DECL_ATTRIBUTES (clone) = DECL_ATTRIBUTES (olddecl);
+	  adjust_clone_attributes (olddecl, clone, DECL_NAME (clone));
 	  DECL_PRESERVE_P (clone) |= DECL_PRESERVE_P (olddecl);
 	}
     }
@@ -10789,6 +10792,7 @@ grokfndecl (tree ctype,
     {
       cplus_decl_attributes (&decl, *attrlist, 0);
       *attrlist = NULL_TREE;
+      create_sym_alias_decls (decl);
     }
 
   if (DECL_HAS_CONTRACTS_P (decl))
diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
index 9e666e5eece..c7f009beaf0 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -1844,6 +1844,9 @@ cplus_decl_attributes (tree *decl, tree attributes, int flags)
   if (late_attrs)
     save_template_attributes (late_attrs, decl, flags);
 
+  if (TYPE_P (*decl) && attributes)
+    update_tinfo_sym_alias (*decl);
+
   /* Propagate deprecation out to the template.  */
   if (TREE_DEPRECATED (*decl))
     if (tree ti = get_template_info (*decl))
@@ -2200,6 +2203,47 @@ 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 sym aliases.  */
+
+void
+update_sym_alias_interface (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.  */
 
@@ -3007,6 +3051,8 @@ determine_visibility (tree decl)
        translation unit, we can make the type internal.  */
     constrain_visibility (decl, VISIBILITY_ANON, false);
 
+  update_sym_alias_interface (decl);
+
   /* If visibility changed and DECL already has DECL_RTL, ensure
      symbol flags are updated.  */
   if ((DECL_VISIBILITY (decl) != orig_visibility
@@ -3269,6 +3315,8 @@ tentative_decl_linkage (tree decl)
       else if (VAR_P (decl))
 	maybe_commonize_var (decl);
     }
+
+  update_sym_alias_interface (decl);
 }
 
 /* DECL is a FUNCTION_DECL or VAR_DECL.  If the object file linkage
@@ -3503,6 +3551,8 @@ import_export_decl (tree decl)
     }
 
   DECL_INTERFACE_KNOWN (decl) = 1;
+
+  update_sym_alias_interface (decl);
 }
 
 /* Return an expression that performs the destruction of DECL, which
diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc
index d19ea5d121c..f877780b2ac 100644
--- a/gcc/cp/name-lookup.cc
+++ b/gcc/cp/name-lookup.cc
@@ -22,9 +22,11 @@ along with GCC; see the file COPYING3.  If not see
 #define INCLUDE_MEMORY
 #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"
@@ -3479,6 +3481,15 @@ push_local_extern_decl_alias (tree decl)
 	  /* Adjust visibility.  */
 	  determine_visibility (alias);
 	}
+      else if (DECL_P (alias))
+	DECL_ATTRIBUTES (alias)
+	  = targetm.merge_decl_attributes (alias, decl);
+      if (DECL_P (alias))
+	{
+	  symtab_node::remap_sym_alias_target (decl, alias);
+	  DECL_ATTRIBUTES (decl)
+	    = remove_attribute ("sym_alias", DECL_ATTRIBUTES (alias));
+	}
     }
 
   retrofit_lang_decl (decl);
diff --git a/gcc/cp/optimize.cc b/gcc/cp/optimize.cc
index 9e8926e4cc6..4a2e8cb4354 100644
--- a/gcc/cp/optimize.cc
+++ b/gcc/cp/optimize.cc
@@ -528,9 +528,12 @@ maybe_clone_body (tree fn)
       DECL_VISIBILITY_SPECIFIED (clone) = DECL_VISIBILITY_SPECIFIED (fn);
       DECL_DLLIMPORT_P (clone) = DECL_DLLIMPORT_P (fn);
       DECL_ATTRIBUTES (clone) = clone_attrs (DECL_ATTRIBUTES (fn));
+      adjust_clone_attributes (fn, clone, DECL_NAME (clone), true);
       DECL_DISREGARD_INLINE_LIMITS (clone) = DECL_DISREGARD_INLINE_LIMITS (fn);
       set_decl_section_name (clone, fn);
 
+      update_sym_alias_interface (clone);
+
       /* Adjust the parameter names and locations.  */
       parm = DECL_ARGUMENTS (fn);
       clone_parm = DECL_ARGUMENTS (clone);
diff --git a/gcc/cp/rtti.cc b/gcc/cp/rtti.cc
index 7878929c246..4bb7b8c8c6e 100644
--- a/gcc/cp/rtti.cc
+++ b/gcc/cp/rtti.cc
@@ -28,8 +28,10 @@ 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"
+#include "cgraph.h"
 
 /* C++ returns type information to the user in struct type_info
    objects. We also use type information to implement dynamic_cast and
@@ -479,8 +481,13 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
 	  = build_tree_list (get_identifier ("non overlapping"),
 			     NULL_TREE);
       else
+	/* Share the non overlapping attribute, without assuming it's
+	   the only attribute, but assuming it's the last if it's
+	   present.  There may be sym aliases too, and those are not
+	   to be shared.  */
 	DECL_ATTRIBUTES (d)
-	  = DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]);
+	  = lookup_attribute ("non overlapping",
+			      DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]));
 
       /* Mark the variable as undefined -- but remember that we can
 	 define it later if we need to do so.  */
@@ -492,6 +499,16 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
       if (CLASS_TYPE_P (type))
 	CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type)) = d;
 
+      /* Copy sym alias attributes from the type to the rtti obj decl.  */
+      tree *attrs = &DECL_ATTRIBUTES (d);
+      FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+	{
+	  tree attr = tree_cons (TREE_PURPOSE (sym), TREE_VALUE (sym), *attrs);
+	  *attrs = attr;
+	  attrs = &TREE_CHAIN (attr);
+	}
+      create_sym_alias_decls (d);
+
       /* Add decl to the global array of tinfo decls.  */
       vec_safe_push (unemitted_tinfo_decls, d);
     }
@@ -499,6 +516,58 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
   return d;
 }
 
+/* After modifying the attributes of TYPE, check whether tinfo was
+   already created and, if so, add to it any sym alias attributes
+   that were not already present.  */
+
+void
+update_tinfo_sym_alias (tree type)
+{
+  if (!TYPE_SIZE (type) || !CLASS_TYPE_P (type))
+    return;
+
+  tree d = CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type));
+  if (!d)
+    return;
+
+  bool first = true;
+  symtab_node *node = NULL;
+
+  tree *attrs = &DECL_ATTRIBUTES (d);
+  FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (d_sym, *attrs)
+	if (TREE_VALUE (sym) == TREE_VALUE (d_sym))
+	  {
+	    found = true;
+	    break;
+	  }
+
+      if (found)
+	continue;
+
+      tree attr = tree_cons (TREE_PURPOSE (sym),
+			     TREE_VALUE (sym),
+			     *attrs);
+      *attrs = attr;
+      attrs = &TREE_CHAIN (attr);
+
+      if (first)
+	{
+	  first = false;
+	  node = symtab_node::get (d);
+	}
+
+      if (!node)
+	continue;
+
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+      create_sym_alias_decl (d, id);
+    }
+}
+
 /* Return the type_info object for TYPE.  */
 
 tree
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index e6de0815846..96674e2d857 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -4141,6 +4141,39 @@ Function Attributes}, @ref{PowerPC Function Attributes},
 @ref{Nios II Function Attributes}, and @ref{S/390 Function Attributes}
 for details.
 
+@cindex @code{sym_alias} function attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} attribute causes @var{name} to be output as an
+alias to the definition.  For instance,
+
+@smallexample
+void f (uint64_t) __attribute__ ((__sym_alias__ ("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 for aliases,
+and may not be available on all targets.
+
 @cindex @code{symver} function attribute
 @item symver ("@var{name2}@@@var{nodename}")
 On ELF targets this attribute creates a symbol version.  The @var{name2} part
@@ -8069,6 +8102,10 @@ will be placed in new, unique sections.
 
 This additional functionality requires Binutils version 2.36 or later.
 
+@cindex @code{sym_alias} variable attribute
+@item sym_alias ("@var{name}")
+See @pxref{Common Function Attributes}.
+
 @cindex @code{uninitialized} variable attribute
 @item uninitialized
 This attribute, attached to a variable with automatic storage, means that
@@ -9164,6 +9201,21 @@ is not supported; that is to say, if a given scalar object can be accessed
 through distinct types that assign a different storage order to it, then the
 behavior is undefined.
 
+@cindex @code{sym_alias} type attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} type 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__ ((__sym_alias__ ("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.
+
 @cindex @code{transparent_union} type attribute
 @item transparent_union
 
diff --git a/gcc/symtab.cc b/gcc/symtab.cc
index 0470509a98d..0812088e59f 100644
--- a/gcc/symtab.cc
+++ b/gcc/symtab.cc
@@ -1943,6 +1943,42 @@ symtab_node::noninterposable_alias (symtab_node *node, void *data)
   return false;
 }
 
+/* Remap sym alias nodes recorded as aliasing REPLACED to alias
+   REPLACEMENT instead.  */
+
+void
+symtab_node::remap_sym_alias_target (tree replaced, tree replacement)
+{
+  if (!decl_in_symtab_p (replacement)
+      || !symtab_node::get (replacement))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (replaced))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      symtab_node *sym_node = symtab_node::get_for_asmname (id);
+
+      if (!sym_node)
+	{
+	  create_sym_alias_decl (replacement, id);
+	  continue;
+	}
+
+      gcc_assert (!sym_node->analyzed);
+      if (sym_node->alias_target != replaced)
+	continue;
+
+      sym_node->definition = 0;
+
+      if (VAR_P (replaced))
+	varpool_node::create_extra_name_alias (sym_node->decl, replacement);
+      else
+	cgraph_node::create_same_body_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-sym-alias-neg.c b/gcc/testsuite/c-c++-common/attr-sym-alias-neg.c
new file mode 100644
index 00000000000..16a982dbce2
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/attr-sym-alias-neg.c
@@ -0,0 +1,11 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int f () {
+  int i __attribute__ ((sym_alias ("f_i"))); /* { dg-warning "ignored" } */
+  return i;
+}
+
+struct s {
+  int j __attribute__ ((sym_alias ("s_j"))); /* { dg-warning "ignored" } */
+};
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-1.c b/gcc/testsuite/c-c++-common/goacc/declare-1.c
index 46ee01b6759..f2842893318 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-1.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-1.c
@@ -113,11 +113,11 @@ f_2 (void)
   int va3;
 #pragma acc declare device_resident(va3)
 
-#ifndef __cplusplus
+#if 0
   /* TODO PR90868
 
-     C: "error: variable '[...]' used more than once with '#pragma acc declare'".  */
-#else
+     "error: variable '[...]' used more than once with '#pragma acc declare'".  */
+
   extern int ve0;
 #pragma acc declare create(ve0)
 
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-2.c b/gcc/testsuite/c-c++-common/goacc/declare-2.c
index e2e22be57e9..aec59b69754 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-2.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-2.c
@@ -137,25 +137,25 @@ void
 f_pr90868_2 (void)
 {
   extern int we0;
-#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" } */
 
   extern int we1;
-#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" } */
 
   extern int *we2;
-#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" } */
 
   extern int we3;
-#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" } */
 
   extern int we4;
-#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" } */
 
   extern int we5;
-#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" } */
  
   extern int we6;
-#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" } */
 }
 
 
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
new file mode 100644
index 00000000000..61af50cb552
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
@@ -0,0 +1,39 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+int var_a = 1;
+
+void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+
+void
+foo_a ()
+{
+}
+
+
+int var_b;
+extern int var_b __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+
+void
+foo_b ()
+{
+}
+
+void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+
+
+int var_c __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+
+void __attribute__ ((__sym_alias__ ("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-sym-alias-2.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
new file mode 100644
index 00000000000..6f0d55ca42c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+struct s
+{
+  int mem __attribute__ ((__sym_alias__ ("MEMFOO"))); /* { dg-warning "attribute ignored" } */
+};
+
+void foo()
+{
+  extern void bar () __attribute__ ((__sym_alias__ ("FOOBAR")));
+  int var __attribute__ ((__sym_alias__ ("FOOVAR"))); /* { dg-warning "attribute ignored" } */
+}
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
new file mode 100644
index 00000000000..0052485a30a
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
@@ -0,0 +1,41 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int var_a = 1;
+
+void
+foo_a ()
+{
+  extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+}
+
+#if 0 // __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__ ((__sym_alias__ ("FOOVAR_B")));
+}
+
+int var_b;
+
+void __attribute__ ((__sym_alias__ ("FOOBAR_C")))
+foo_c ()
+{
+  void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+  /* Another sym for var_b.  */
+  extern int var_b __attribute__ ((__sym_alias__ ("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-sym-alias-4.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
new file mode 100644
index 00000000000..2beafa46379
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-require-alias "" } */
+
+int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A"))) = 42;
+
+int __attribute__ ((__sym_alias__ ("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/c-c++-common/torture/attr-sym-alias-5.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
new file mode 100644
index 00000000000..7ff9b4bf1a3
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int f () {
+  static int x __attribute__ ((sym_alias ("f_x")));
+  return x;
+}
+
+int f_x_ref __attribute__ ((alias ("f_x")));
+
+int g () {
+  return f_x_ref;
+}
+
+/* { dg-final { scan-assembler "f_x" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
new file mode 100644
index 00000000000..579eda1a473
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
@@ -0,0 +1,72 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+class __attribute__ ((__sym_alias__ ("FOOCLS_A"),
+		      __sym_alias__ ("FOOCLS_A_Dupe"))) foo {
+  static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  void __attribute__ ((__sym_alias__ ("FOOBAR_A"))) bar ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+int foo::var = 1;
+
+foo::foo () {}
+
+void foo::bar () {}
+
+namespace b {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_B"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_B"))) foo ();
+    void __attribute__ ((__sym_alias__ ("FOOBAR_B"))) bar () {}
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_B"))) ~foo() {}
+  };
+
+  int foo::var = 2;
+
+  foo::foo () {
+    void (foo::*pbar)() = &foo::bar;
+  }
+}
+
+namespace c {
+  namespace cd {
+    class __attribute__ ((__sym_alias__ ("FOOCLS_C"))) foo {
+      static int var __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+      __attribute__ ((__sym_alias__ ("FOOCTR_C"))) foo () {
+	void (foo::*pbar)() = &foo::bar;
+      }
+      void __attribute__ ((__sym_alias__ ("FOOBAR_C"))) bar () {}
+      virtual __attribute__ ((__sym_alias__ ("FOODTR_C"))) ~foo() {}
+    };
+
+    int foo::var = 3;
+  }
+}
+
+/* { dg-final { scan-assembler "FOOCLS_A" } } */
+/* { dg-final { scan-assembler "FOOCLS_A_Dupe" } } */
+/* { 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-sym-alias-2.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
new file mode 100644
index 00000000000..6facfcc2cd9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+namespace {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_A"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo ();
+    void __attribute__ ((__sym_alias__ ("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-sym-alias-3.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
new file mode 100644
index 00000000000..42c7288ad4a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
@@ -0,0 +1,83 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+// sym can be applied to template function explicit instantiations.
+
+template <typename T>
+void
+fn(T) {
+};
+
+template void __attribute__ ((__sym_alias__ ("FOOFUN_UINT"))) fn<>(unsigned int);
+template void __attribute__ ((__sym_alias__ ("FOOFUN_LONG"))) fn<>(long);
+
+template<> void __attribute__ ((__sym_alias__ ("FOOFUN_CHAR"))) fn<>(char) {}
+
+
+template <typename T = void>
+struct
+foo {
+  virtual ~foo() {}
+
+  virtual void virtfun() {}
+
+  static void stfun() {}
+  void inlfun() {}
+};
+
+// Explicitly instantiate members before the enclosing class.
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_CHAR_VIRT"))) foo<char>::virtfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_CHAR_TI"))) foo<char>;
+
+// Though they're only output if the enclosing class is.
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_LONG_VIRT"))) foo<long>::virtfun();
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_LONG_TI_X"))) foo<long>;
+
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_VOID_ST"))) foo<void>::stfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_VOID_TI"))) foo<>;
+
+
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_X"))) foo<short>;
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_ST"))) foo<short>::stfun();
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_INL"))) foo<short>::inlfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_D"))) foo<short>;
+
+// Explicit specializations work too.
+
+template <>
+struct  __attribute__ ((__sym_alias__ ("FOOCLS_INT_TI")))
+foo<int>
+{
+  virtual ~foo() {}
+  virtual void __attribute__ ((__sym_alias__ ("FOOCLS_INT_VIRT"))) virtfun() {}
+};
+
+/* { dg-final { scan-assembler "FOOFUN_UINT" } } */
+/* { dg-final { scan-assembler "FOOFUN_LONG" } } */
+/* { dg-final { scan-assembler "FOOFUN_CHAR" } } */
+
+/* { dg-final { scan-assembler "FOOCLS_VOID_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_VOID_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_X" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_INL" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_D" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_TI_X" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_VIRT" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
new file mode 100644
index 00000000000..59b779de35d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+template <typename T = void>
+class
+__attribute__ ((__sym_alias__ ("FOOCLS")))
+foo // { dg-error "duplicate|already" }
+{
+  virtual ~foo() {}
+
+  template <typename U>
+  void
+    __attribute__ ((__sym_alias__ ("FOOTMF")))
+    tmemfun () {} // { dg-error "duplicate|already" }
+};
+
+template <typename T>
+void
+__attribute__ ((__sym_alias__ ("FOOTFN")))
+fn(T) { // { dg-error "duplicate|already" }
+};
+
+template class foo<>;
+template class foo<int>;
+template void foo<>::tmemfun<void>();
+template void foo<int>::tmemfun<void>();
+template void fn<>(int);
+template void fn<>(long);
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
new file mode 100644
index 00000000000..45d59b953c3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
@@ -0,0 +1,14 @@
+/* { dg-do compile { target c++11 } } */
+/* { dg-require-alias "" } */
+
+struct foo {
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+foo::foo () {}
+
+// Make sure the inherited cdtors don't duplicate the syms.
+struct bar : foo {
+  using foo::foo;
+};
diff --git a/gcc/varpool.cc b/gcc/varpool.cc
index e7b51b15e4a..33649906d1d 100644
--- a/gcc/varpool.cc
+++ b/gcc/varpool.cc
@@ -163,6 +163,9 @@ varpool_node::get_create (tree decl)
     }
 
   node->register_symbol ();
+
+  create_sym_alias_decls (decl);
+
   return node;
 }

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [gcc(refs/users/aoliva/heads/testme)] Introduce attribute sym_alias
@ 2023-11-30 10:28 Alexandre Oliva
  0 siblings, 0 replies; 33+ messages in thread
From: Alexandre Oliva @ 2023-11-30 10:28 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:ad7ba571ad32e166bf1485bf338abe96e95ac778

commit ad7ba571ad32e166bf1485bf338abe96e95ac778
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Fri Jul 14 03:45:14 2023 -0300

    Introduce attribute sym_alias
    
    This patch introduces an attribute to add extra asm names (aliases)
    for a decl 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 class types, namespace-scoped
    variables and functions, static data members, member functions,
    explicit instantiations and specializations of template functions,
    members and classes.
    
    When applied to constructors or destructor, additional sym aliases
    with _Base and _Del suffixes are defined for variants other than
    complete-object ones.  This changes the assumption that clones always
    carry the same attributes as their abstract declarations, so there is
    now a function to adjust them.
    
    C++ also had a bug in which attributes from local extern declarations
    failed to be propagated to a preexisting corresponding
    namespace-scoped decl.  I've fixed that, and adjusted acc tests that
    distinguished between C and C++ in this regard.
    
    Applying the attribute to class types is only valid in C++, and the
    effect is to attach the alias to the RTTI object associated with the
    class type.
    
    for  gcc/ChangeLog
    
            * attribs.cc: Include cgraph.h.
            (decl_attributes): Allow late introduction of sym_alias in
            types.
            (create_sym_alias_decl, create_sym_alias_decls): New.
            * attribs.h: Declare them.
            (FOR_EACH_SYM_ALIAS): New macro.
            * cgraph.cc (cgraph_node::create): Create sym_alias decls.
            * varpool.cc (varpool_node::get_create): Create sym_alias
            decls.
            * cgraph.h (symtab_node::remap_sym_alias_target): New.
            * symtab.cc (symtab_node::remap_sym_alias_target): Define.
            * cgraphunit.cc (cgraph_node::analyze): Create alias_target
            node if needed.
            (analyze_functions): Fixup visibility of implicit alias only
            after its node is analyzed.
            * doc/extend.texi (sym_alias): Document for variables,
            functions and types.
    
    for  gcc/ada/ChangeLog
    
            * doc/gnat_rm/interfacing_to_other_languages.rst: Mention
            attribute sym_alias to give RTTI symbols mnemonic names.
            * doc/gnat_ugn/the_gnat_compilation_model.rst: Mention
            aliases.  Fix incorrect ref to C1 ctor variant.
    
    for  gcc/c-family/ChangeLog
    
            * c-ada-spec.cc (pp_asm_name): Use first sym_alias if
            available.
            * c-attribs.cc (handle_sym_alias_attribute): New.
            (c_common_attribute_table): Add sym_alias.
            (handle_copy_attribute): Do not copy sym_alias attribute.
    
    for  gcc/c/ChangeLog
    
            * c-decl.cc (duplicate_decls): Remap sym_alias target.
    
    for  gcc/cp/ChangeLog
    
            * class.cc (adjust_clone_attributes): New.
            (copy_fndecl_with_name, build_clone): Call it.
            * cp-tree.h (adjust_clone_attributes): Declare.
            (update_sym_alias_interface): Declare.
            (update_tinfo_sym_alias): Declare.
            * decl.cc (duplicate_decls): Remap sym_alias target.
            Adjust clone attributes.
            (grokfndecl): Tentatively create sym_alias decls after
            adding attributes in e.g. a template member function explicit
            instantiation.
            * decl2.cc (cplus_decl_attributes): Update tinfo sym_alias.
            (copy_interface, update_sym_alias_interface): New.
            (determine_visibility): Update sym_alias interface.
            (tentative_decl_linkage, import_export_decl): Likewise.
            * name-lookup.cc: Include target.h and cgraph.h.
            (push_local_extern_decl_alias): Merge attributes with
            namespace-scoped decl, and drop duplicate sym_alias.
            * optimize.cc (maybe_clone_body): Re-adjust attributes after
            cloning them.  Update sym_alias interface.
            * rtti.cc: Include attribs.h and cgraph.h.
            (get_tinfo_decl): Copy sym_alias attributes from type to tinfo
            decl.  Create sym_alias decls.
            (update_tinfo_sym_alias): New.
    
    for  gcc/testsuite/ChangeLog
    
            * c-c++-common/goacc/declare-1.c: Adjust.
            * c-c++-common/goacc/declare-2.c: Adjust.
            * c-c++-common/torture/attr-sym-alias-1.c: New.
            * c-c++-common/torture/attr-sym-alias-2.c: New.
            * c-c++-common/torture/attr-sym-alias-3.c: New.
            * c-c++-common/torture/attr-sym-alias-4.c: New.
            * c-c++-common/attr-sym-alias-neg.c: New.
            * g++.dg/torture/attr-sym-alias-1.C: New.
            * g++.dg/torture/attr-sym-alias-2.C: New.
            * g++.dg/torture/attr-sym-alias-3.C: New.
            * g++.dg/torture/attr-sym-alias-4.C: New.
            * g++.dg/torture/attr-sym-alias-5.C: New.

Diff:
---
 .../doc/gnat_rm/interfacing_to_other_languages.rst |  6 ++
 .../doc/gnat_ugn/the_gnat_compilation_model.rst    | 10 ++-
 gcc/attribs.cc                                     | 68 +++++++++++++++++-
 gcc/attribs.h                                      |  7 ++
 gcc/c-family/c-ada-spec.cc                         |  7 ++
 gcc/c-family/c-attribs.cc                          | 33 ++++++++-
 gcc/c/c-decl.cc                                    |  2 +
 gcc/cgraph.cc                                      |  2 +
 gcc/cgraph.h                                       |  4 ++
 gcc/cgraphunit.cc                                  |  2 +-
 gcc/cp/class.cc                                    | 64 +++++++++++++++++
 gcc/cp/cp-tree.h                                   |  4 ++
 gcc/cp/decl.cc                                     |  4 ++
 gcc/cp/decl2.cc                                    | 50 +++++++++++++
 gcc/cp/name-lookup.cc                              | 11 +++
 gcc/cp/optimize.cc                                 |  3 +
 gcc/cp/rtti.cc                                     | 71 +++++++++++++++++-
 gcc/doc/extend.texi                                | 52 ++++++++++++++
 gcc/symtab.cc                                      | 36 ++++++++++
 gcc/testsuite/c-c++-common/attr-sym-alias-neg.c    | 10 +++
 gcc/testsuite/c-c++-common/goacc/declare-1.c       |  6 +-
 gcc/testsuite/c-c++-common/goacc/declare-2.c       | 14 ++--
 .../c-c++-common/torture/attr-sym-alias-1.c        | 39 ++++++++++
 .../c-c++-common/torture/attr-sym-alias-2.c        | 13 ++++
 .../c-c++-common/torture/attr-sym-alias-3.c        | 41 +++++++++++
 .../c-c++-common/torture/attr-sym-alias-4.c        | 28 ++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C    | 72 +++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C    | 26 +++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C    | 83 ++++++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C    | 28 ++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C    | 14 ++++
 gcc/varpool.cc                                     |  3 +
 32 files changed, 794 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 ad0be511d48..b8de16ebf7c 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 ``sym_alias`` 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 fd15459203a..20045698962 100644
--- a/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
+++ b/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
@@ -4274,6 +4274,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__ ((__sym_alias__ ("Ctor_For_Animal")))
         Animal() {Age_Count = 0;};
       private:
         int Age_Count;
@@ -4306,6 +4307,7 @@ both Carnivore and Domestic, that is:
         virtual int  Number_Of_Teeth ();
         virtual void Set_Owner (char* Name);
 
+        __attribute__ ((__sym_alias__ ("Ctor_For_Dog"))) // mnemonic alias
         Dog(); // Constructor
       private:
         int  Tooth_Count;
@@ -4344,7 +4346,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;
@@ -4360,7 +4363,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,7 +4385,8 @@ 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.
+mangled name (or an alias) 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.cc b/gcc/attribs.cc
index c7209c26acc..3ef07c885d5 100644
--- a/gcc/attribs.cc
+++ b/gcc/attribs.cc
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "target.h"
 #include "tree.h"
+#include "cgraph.h"
 #include "stringpool.h"
 #include "diagnostic-core.h"
 #include "attribs.h"
@@ -825,7 +826,8 @@ decl_attributes (tree *node, tree attributes, int flags,
 
       if (TYPE_P (*anode)
 	  && (flags & (int) ATTR_FLAG_TYPE_IN_PLACE)
-	  && COMPLETE_TYPE_P (*anode))
+	  && COMPLETE_TYPE_P (*anode)
+	  && !is_attribute_p ("sym_alias", name))
 	{
 	  warning (OPT_Wattributes, "type attributes ignored after type is already defined");
 	  continue;
@@ -2640,6 +2642,70 @@ attr_access::array_as_string (tree type) const
   return typstr;
 }
 
+/* Create a sym attribute for DECL to be visible with linkage name ID.  */
+
+tree
+create_sym_alias_decl (tree decl, tree id)
+{
+  const char *attr_str = "sym_alias";
+
+  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;
+
+      tree attr_name = get_identifier (attr_str);
+      error_at (DECL_SOURCE_LOCATION (decl),
+		"duplicate symbol name %qE in %qE attribute of %qD",
+		id, attr_name, decl);
+      inform (DECL_SOURCE_LOCATION (sym_node->decl),
+	      "already used by %qD", sym_node->decl);
+    }
+
+  tree clone = copy_node (decl);
+  DECL_ATTRIBUTES (clone) = remove_attribute (attr_str,
+					      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_extra_name_alias (clone, decl);
+    }
+  else
+    {
+      cgraph_node::create_same_body_alias (clone, decl);
+    }
+
+  return clone;
+}
+
+/* Create decls for all sym aliases requested in DECL's attributes.  */
+
+void
+create_sym_alias_decls (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl)
+      || DECL_ABSTRACT_P (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      create_sym_alias_decl (decl, id);
+    }
+}
+
 #if CHECKING_P
 
 namespace selftest
diff --git a/gcc/attribs.h b/gcc/attribs.h
index 84a43658a70..156f2c20588 100644
--- a/gcc/attribs.h
+++ b/gcc/attribs.h
@@ -398,4 +398,11 @@ extern void init_attr_rdwr_indices (rdwr_map *, tree);
 extern attr_access *get_parm_access (rdwr_map &, tree,
 				     tree = current_function_decl);
 
+extern tree create_sym_alias_decl (tree, tree);
+extern void create_sym_alias_decls (tree);
+
+#define FOR_EACH_SYM_ALIAS(sym, attrs)					\
+  for (tree sym = lookup_attribute ("sym_alias", (attrs));		\
+       sym; sym = lookup_attribute ("sym_alias", TREE_CHAIN (sym)))
+
 #endif // GCC_ATTRIBS_H
diff --git a/gcc/c-family/c-ada-spec.cc b/gcc/c-family/c-ada-spec.cc
index 050994d8416..5042b9cfecd 100644
--- a/gcc/c-family/c-ada-spec.cc
+++ b/gcc/c-family/c-ada-spec.cc
@@ -1431,6 +1431,13 @@ pp_ada_tree_identifier (pretty_printer *buffer, tree node, tree type,
 static void
 pp_asm_name (pretty_printer *buffer, tree t)
 {
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (t))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.cc b/gcc/c-family/c-attribs.cc
index 2b20e58c922..392437eab3b 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -108,7 +108,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_sym_alias_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,
@@ -388,6 +389,8 @@ const struct attribute_spec c_common_attribute_table[] =
 			      handle_alias_attribute, NULL },
   { "weakref",                0, 1, true,  false, false, false,
 			      handle_weakref_attribute, NULL },
+  { "sym_alias",              1, 1, false,  false, false, false,
+			      handle_sym_alias_attribute, NULL },
   { "no_instrument_function", 0, 0, true,  false, false, false,
 			      handle_no_instrument_function_attribute,
 			      NULL },
@@ -3002,7 +3005,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
@@ -3012,7 +3015,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
@@ -3022,6 +3025,29 @@ handle_alias_attribute (tree *node, tree name, tree args,
   return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs);
 }
 
+/* Handle a "sym_alias" attribute; arguments as in struct
+   attribute_spec.handler.  */
+
+static tree
+handle_sym_alias_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 (decl_in_symtab_p (node))
+    *no_add_attrs = false;
+  else if (TYPE_P (node) && c_dialect_cxx ())
+    *no_add_attrs = false;
+  else
+    return error_mark_node;
+
+  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.  */
 
@@ -3155,6 +3181,7 @@ handle_copy_attribute (tree *node, tree name, tree args,
 	      || is_attribute_p ("visibility", atname)
 	      || is_attribute_p ("weak", atname)
 	      || is_attribute_p ("weakref", atname)
+	      || is_attribute_p ("sym_alias", atname)
 	      || is_attribute_p ("target_clones", atname))
 	    continue;
 
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index cf1df82c0f4..e5a1ee54cb3 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -3119,6 +3119,8 @@ duplicate_decls (tree newdecl, tree olddecl)
 
   merge_decls (newdecl, olddecl, newtype, oldtype);
 
+  symtab_node::remap_sym_alias_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.cc b/gcc/cgraph.cc
index f93259a8c70..0bef2179e89 100644
--- a/gcc/cgraph.cc
+++ b/gcc/cgraph.cc
@@ -523,6 +523,8 @@ cgraph_node::create (tree decl)
   node->register_symbol ();
   maybe_record_nested_function (node);
 
+  create_sym_alias_decls (decl);
+
   return node;
 }
 
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index cfdd9f693a8..cb5ac01f7e2 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -327,6 +327,10 @@ public:
   /* Return DECL that alias is aliasing.  */
   inline tree get_alias_target_tree ();
 
+  /* Remap sym alias nodes recorded as aliasing REPLACED to alias REPLACEMENT
+     instead.  */
+  static void remap_sym_alias_target (tree replaced, tree replacement);
+
   /* Set section for symbol and its aliases.  */
   void set_section (const char *section);
 
diff --git a/gcc/cgraphunit.cc b/gcc/cgraphunit.cc
index bccd2f2abb5..eb2d05094e9 100644
--- a/gcc/cgraphunit.cc
+++ b/gcc/cgraphunit.cc
@@ -1175,7 +1175,7 @@ analyze_functions (bool first_time)
      C++ FE is confused about the COMDAT groups being right.  */
   if (symtab->cpp_implicit_aliases_done)
     FOR_EACH_SYMBOL (node)
-      if (node->cpp_implicit_alias)
+      if (node->cpp_implicit_alias && node->analyzed)
 	  node->fixup_same_cpp_alias_visibility (node->get_alias_target ());
   build_type_inheritance_graph ();
 
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index 6fdb56abfb9..fa8fc63db82 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -4896,6 +4896,68 @@ check_methods (tree t)
     }
 }
 
+/* Adjust sym alias name for CLONE, cloned from FN and named NAME,
+   if it is a cdtor, and drop the sym alias from other clones.  */
+
+void
+adjust_clone_attributes (tree fn, tree clone, tree name, bool skip_copy_p)
+{
+  if (IDENTIFIER_CDTOR_P (name))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	{
+	  found = true;
+	  break;
+	}
+
+      if (found
+	  && (name == complete_ctor_identifier
+	      || name == complete_dtor_identifier))
+	{
+	  /* Reuse the sym alias decls created for the primary cdtor
+	     decl.  */
+	  symtab_node::remap_sym_alias_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);
+
+	  if (!skip_copy_p)
+	    DECL_ATTRIBUTES (clone) = copy_list (DECL_ATTRIBUTES (clone));
+
+	  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	    {
+	      /* We need to copy this even with skip_copy_p, because
+		 even then copying was shallow.  */
+	      TREE_VALUE (sym) = copy_list (TREE_VALUE (sym));
+	      /* Append suf to the sym alias name.  */
+	      tree str = TREE_VALUE (TREE_VALUE (sym));
+	      char *symname = concat (TREE_STRING_POINTER (str), suf, NULL);
+	      str = build_string (TREE_STRING_LENGTH (str) + xlen, symname);
+	      TREE_VALUE (TREE_VALUE (sym)) = str;
+	      free (symname);
+	    }
+
+	  if (symtab_node::get (clone))
+	    create_sym_alias_decls (clone);
+	}
+    }
+  else
+    DECL_ATTRIBUTES (clone)
+      = remove_attribute ("sym_alias", DECL_ATTRIBUTES (clone));
+}
+
 /* FN is constructor, destructor or operator function.  Clone the
    declaration to create a NAME'd variant.  NEED_VTT_PARM_P and
    OMIT_INHERITED_PARMS_P are relevant if it's a cdtor.  */
@@ -5065,6 +5127,8 @@ build_clone (tree fn, tree name, bool need_vtt_parm_p,
   DECL_CHAIN (clone) = DECL_CHAIN (fn);
   DECL_CHAIN (fn) = clone;
 
+  adjust_clone_attributes (fn, clone, name);
+
   return clone;
 }
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 964af1ddd85..2bafed4e993 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5787,6 +5787,8 @@ struct GTY((for_user)) spec_entry
 
 extern int current_class_depth;
 
+void adjust_clone_attributes (tree fn, tree clone, tree name, bool = false);
+
 /* in decl.cc */
 
 /* An array of static vars & fns.  */
@@ -7022,6 +7024,7 @@ extern void do_push_parm_decls			(tree, tree, tree *);
 extern tree do_aggregate_paren_init		(tree, tree);
 
 /* in decl2.cc */
+extern void update_sym_alias_interface		(tree);
 extern void record_mangling			(tree, bool);
 extern void overwrite_mangling			(tree, tree);
 extern void note_mangling_alias			(tree, tree);
@@ -7596,6 +7599,7 @@ extern bool emit_tinfo_decl			(tree);
 extern unsigned get_pseudo_tinfo_index		(tree);
 extern tree get_pseudo_tinfo_type		(unsigned);
 extern tree build_if_nonnull			(tree, tree, tsubst_flags_t);
+extern void update_tinfo_sym_alias		(tree);
 
 /* in search.cc */
 extern tree get_parent_with_private_access 	(tree decl, tree binfo);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 2f2dbb8d107..5636d694b68 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -3237,6 +3237,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
 	      && TREE_STATIC (olddecl))))
     make_decl_rtl (olddecl);
 
+  symtab_node::remap_sym_alias_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.
@@ -3260,6 +3262,7 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
       FOR_EACH_CLONE (clone, olddecl)
 	{
 	  DECL_ATTRIBUTES (clone) = DECL_ATTRIBUTES (olddecl);
+	  adjust_clone_attributes (olddecl, clone, DECL_NAME (clone));
 	  DECL_PRESERVE_P (clone) |= DECL_PRESERVE_P (olddecl);
 	}
     }
@@ -10789,6 +10792,7 @@ grokfndecl (tree ctype,
     {
       cplus_decl_attributes (&decl, *attrlist, 0);
       *attrlist = NULL_TREE;
+      create_sym_alias_decls (decl);
     }
 
   if (DECL_HAS_CONTRACTS_P (decl))
diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
index 9e666e5eece..c7f009beaf0 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -1844,6 +1844,9 @@ cplus_decl_attributes (tree *decl, tree attributes, int flags)
   if (late_attrs)
     save_template_attributes (late_attrs, decl, flags);
 
+  if (TYPE_P (*decl) && attributes)
+    update_tinfo_sym_alias (*decl);
+
   /* Propagate deprecation out to the template.  */
   if (TREE_DEPRECATED (*decl))
     if (tree ti = get_template_info (*decl))
@@ -2200,6 +2203,47 @@ 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 sym aliases.  */
+
+void
+update_sym_alias_interface (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.  */
 
@@ -3007,6 +3051,8 @@ determine_visibility (tree decl)
        translation unit, we can make the type internal.  */
     constrain_visibility (decl, VISIBILITY_ANON, false);
 
+  update_sym_alias_interface (decl);
+
   /* If visibility changed and DECL already has DECL_RTL, ensure
      symbol flags are updated.  */
   if ((DECL_VISIBILITY (decl) != orig_visibility
@@ -3269,6 +3315,8 @@ tentative_decl_linkage (tree decl)
       else if (VAR_P (decl))
 	maybe_commonize_var (decl);
     }
+
+  update_sym_alias_interface (decl);
 }
 
 /* DECL is a FUNCTION_DECL or VAR_DECL.  If the object file linkage
@@ -3503,6 +3551,8 @@ import_export_decl (tree decl)
     }
 
   DECL_INTERFACE_KNOWN (decl) = 1;
+
+  update_sym_alias_interface (decl);
 }
 
 /* Return an expression that performs the destruction of DECL, which
diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc
index d19ea5d121c..f877780b2ac 100644
--- a/gcc/cp/name-lookup.cc
+++ b/gcc/cp/name-lookup.cc
@@ -22,9 +22,11 @@ along with GCC; see the file COPYING3.  If not see
 #define INCLUDE_MEMORY
 #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"
@@ -3479,6 +3481,15 @@ push_local_extern_decl_alias (tree decl)
 	  /* Adjust visibility.  */
 	  determine_visibility (alias);
 	}
+      else if (DECL_P (alias))
+	DECL_ATTRIBUTES (alias)
+	  = targetm.merge_decl_attributes (alias, decl);
+      if (DECL_P (alias))
+	{
+	  symtab_node::remap_sym_alias_target (decl, alias);
+	  DECL_ATTRIBUTES (decl)
+	    = remove_attribute ("sym_alias", DECL_ATTRIBUTES (alias));
+	}
     }
 
   retrofit_lang_decl (decl);
diff --git a/gcc/cp/optimize.cc b/gcc/cp/optimize.cc
index 9e8926e4cc6..4a2e8cb4354 100644
--- a/gcc/cp/optimize.cc
+++ b/gcc/cp/optimize.cc
@@ -528,9 +528,12 @@ maybe_clone_body (tree fn)
       DECL_VISIBILITY_SPECIFIED (clone) = DECL_VISIBILITY_SPECIFIED (fn);
       DECL_DLLIMPORT_P (clone) = DECL_DLLIMPORT_P (fn);
       DECL_ATTRIBUTES (clone) = clone_attrs (DECL_ATTRIBUTES (fn));
+      adjust_clone_attributes (fn, clone, DECL_NAME (clone), true);
       DECL_DISREGARD_INLINE_LIMITS (clone) = DECL_DISREGARD_INLINE_LIMITS (fn);
       set_decl_section_name (clone, fn);
 
+      update_sym_alias_interface (clone);
+
       /* Adjust the parameter names and locations.  */
       parm = DECL_ARGUMENTS (fn);
       clone_parm = DECL_ARGUMENTS (clone);
diff --git a/gcc/cp/rtti.cc b/gcc/cp/rtti.cc
index 7878929c246..4bb7b8c8c6e 100644
--- a/gcc/cp/rtti.cc
+++ b/gcc/cp/rtti.cc
@@ -28,8 +28,10 @@ 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"
+#include "cgraph.h"
 
 /* C++ returns type information to the user in struct type_info
    objects. We also use type information to implement dynamic_cast and
@@ -479,8 +481,13 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
 	  = build_tree_list (get_identifier ("non overlapping"),
 			     NULL_TREE);
       else
+	/* Share the non overlapping attribute, without assuming it's
+	   the only attribute, but assuming it's the last if it's
+	   present.  There may be sym aliases too, and those are not
+	   to be shared.  */
 	DECL_ATTRIBUTES (d)
-	  = DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]);
+	  = lookup_attribute ("non overlapping",
+			      DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]));
 
       /* Mark the variable as undefined -- but remember that we can
 	 define it later if we need to do so.  */
@@ -492,6 +499,16 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
       if (CLASS_TYPE_P (type))
 	CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type)) = d;
 
+      /* Copy sym alias attributes from the type to the rtti obj decl.  */
+      tree *attrs = &DECL_ATTRIBUTES (d);
+      FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+	{
+	  tree attr = tree_cons (TREE_PURPOSE (sym), TREE_VALUE (sym), *attrs);
+	  *attrs = attr;
+	  attrs = &TREE_CHAIN (attr);
+	}
+      create_sym_alias_decls (d);
+
       /* Add decl to the global array of tinfo decls.  */
       vec_safe_push (unemitted_tinfo_decls, d);
     }
@@ -499,6 +516,58 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
   return d;
 }
 
+/* After modifying the attributes of TYPE, check whether tinfo was
+   already created and, if so, add to it any sym alias attributes
+   that were not already present.  */
+
+void
+update_tinfo_sym_alias (tree type)
+{
+  if (!TYPE_SIZE (type) || !CLASS_TYPE_P (type))
+    return;
+
+  tree d = CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type));
+  if (!d)
+    return;
+
+  bool first = true;
+  symtab_node *node = NULL;
+
+  tree *attrs = &DECL_ATTRIBUTES (d);
+  FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (d_sym, *attrs)
+	if (TREE_VALUE (sym) == TREE_VALUE (d_sym))
+	  {
+	    found = true;
+	    break;
+	  }
+
+      if (found)
+	continue;
+
+      tree attr = tree_cons (TREE_PURPOSE (sym),
+			     TREE_VALUE (sym),
+			     *attrs);
+      *attrs = attr;
+      attrs = &TREE_CHAIN (attr);
+
+      if (first)
+	{
+	  first = false;
+	  node = symtab_node::get (d);
+	}
+
+      if (!node)
+	continue;
+
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+      create_sym_alias_decl (d, id);
+    }
+}
+
 /* Return the type_info object for TYPE.  */
 
 tree
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index e6de0815846..96674e2d857 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -4141,6 +4141,39 @@ Function Attributes}, @ref{PowerPC Function Attributes},
 @ref{Nios II Function Attributes}, and @ref{S/390 Function Attributes}
 for details.
 
+@cindex @code{sym_alias} function attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} attribute causes @var{name} to be output as an
+alias to the definition.  For instance,
+
+@smallexample
+void f (uint64_t) __attribute__ ((__sym_alias__ ("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 for aliases,
+and may not be available on all targets.
+
 @cindex @code{symver} function attribute
 @item symver ("@var{name2}@@@var{nodename}")
 On ELF targets this attribute creates a symbol version.  The @var{name2} part
@@ -8069,6 +8102,10 @@ will be placed in new, unique sections.
 
 This additional functionality requires Binutils version 2.36 or later.
 
+@cindex @code{sym_alias} variable attribute
+@item sym_alias ("@var{name}")
+See @pxref{Common Function Attributes}.
+
 @cindex @code{uninitialized} variable attribute
 @item uninitialized
 This attribute, attached to a variable with automatic storage, means that
@@ -9164,6 +9201,21 @@ is not supported; that is to say, if a given scalar object can be accessed
 through distinct types that assign a different storage order to it, then the
 behavior is undefined.
 
+@cindex @code{sym_alias} type attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} type 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__ ((__sym_alias__ ("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.
+
 @cindex @code{transparent_union} type attribute
 @item transparent_union
 
diff --git a/gcc/symtab.cc b/gcc/symtab.cc
index 0470509a98d..0812088e59f 100644
--- a/gcc/symtab.cc
+++ b/gcc/symtab.cc
@@ -1943,6 +1943,42 @@ symtab_node::noninterposable_alias (symtab_node *node, void *data)
   return false;
 }
 
+/* Remap sym alias nodes recorded as aliasing REPLACED to alias
+   REPLACEMENT instead.  */
+
+void
+symtab_node::remap_sym_alias_target (tree replaced, tree replacement)
+{
+  if (!decl_in_symtab_p (replacement)
+      || !symtab_node::get (replacement))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (replaced))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      symtab_node *sym_node = symtab_node::get_for_asmname (id);
+
+      if (!sym_node)
+	{
+	  create_sym_alias_decl (replacement, id);
+	  continue;
+	}
+
+      gcc_assert (!sym_node->analyzed);
+      if (sym_node->alias_target != replaced)
+	continue;
+
+      sym_node->definition = 0;
+
+      if (VAR_P (replaced))
+	varpool_node::create_extra_name_alias (sym_node->decl, replacement);
+      else
+	cgraph_node::create_same_body_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-sym-alias-neg.c b/gcc/testsuite/c-c++-common/attr-sym-alias-neg.c
new file mode 100644
index 00000000000..1bb3c5751d4
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/attr-sym-alias-neg.c
@@ -0,0 +1,10 @@
+/* { dg-do compile } */
+
+int f () {
+  int i __attribute__ ((sym_alias ("f_i"))); /* { dg-error "" } */
+  return i;
+}
+
+struct s {
+  int j __attribute__ ((sym_alias ("s_j"))); /* { dg-error "" } */
+};
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-1.c b/gcc/testsuite/c-c++-common/goacc/declare-1.c
index 46ee01b6759..f2842893318 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-1.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-1.c
@@ -113,11 +113,11 @@ f_2 (void)
   int va3;
 #pragma acc declare device_resident(va3)
 
-#ifndef __cplusplus
+#if 0
   /* TODO PR90868
 
-     C: "error: variable '[...]' used more than once with '#pragma acc declare'".  */
-#else
+     "error: variable '[...]' used more than once with '#pragma acc declare'".  */
+
   extern int ve0;
 #pragma acc declare create(ve0)
 
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-2.c b/gcc/testsuite/c-c++-common/goacc/declare-2.c
index e2e22be57e9..aec59b69754 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-2.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-2.c
@@ -137,25 +137,25 @@ void
 f_pr90868_2 (void)
 {
   extern int we0;
-#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" } */
 
   extern int we1;
-#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" } */
 
   extern int *we2;
-#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" } */
 
   extern int we3;
-#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" } */
 
   extern int we4;
-#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" } */
 
   extern int we5;
-#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" } */
  
   extern int we6;
-#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" } */
 }
 
 
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
new file mode 100644
index 00000000000..61af50cb552
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
@@ -0,0 +1,39 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+int var_a = 1;
+
+void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+
+void
+foo_a ()
+{
+}
+
+
+int var_b;
+extern int var_b __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+
+void
+foo_b ()
+{
+}
+
+void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+
+
+int var_c __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+
+void __attribute__ ((__sym_alias__ ("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-sym-alias-2.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
new file mode 100644
index 00000000000..6f0d55ca42c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+struct s
+{
+  int mem __attribute__ ((__sym_alias__ ("MEMFOO"))); /* { dg-warning "attribute ignored" } */
+};
+
+void foo()
+{
+  extern void bar () __attribute__ ((__sym_alias__ ("FOOBAR")));
+  int var __attribute__ ((__sym_alias__ ("FOOVAR"))); /* { dg-warning "attribute ignored" } */
+}
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
new file mode 100644
index 00000000000..0052485a30a
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
@@ -0,0 +1,41 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int var_a = 1;
+
+void
+foo_a ()
+{
+  extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+}
+
+#if 0 // __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__ ((__sym_alias__ ("FOOVAR_B")));
+}
+
+int var_b;
+
+void __attribute__ ((__sym_alias__ ("FOOBAR_C")))
+foo_c ()
+{
+  void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+  /* Another sym for var_b.  */
+  extern int var_b __attribute__ ((__sym_alias__ ("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-sym-alias-4.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
new file mode 100644
index 00000000000..2beafa46379
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-require-alias "" } */
+
+int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A"))) = 42;
+
+int __attribute__ ((__sym_alias__ ("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-sym-alias-1.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
new file mode 100644
index 00000000000..579eda1a473
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
@@ -0,0 +1,72 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+class __attribute__ ((__sym_alias__ ("FOOCLS_A"),
+		      __sym_alias__ ("FOOCLS_A_Dupe"))) foo {
+  static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  void __attribute__ ((__sym_alias__ ("FOOBAR_A"))) bar ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+int foo::var = 1;
+
+foo::foo () {}
+
+void foo::bar () {}
+
+namespace b {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_B"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_B"))) foo ();
+    void __attribute__ ((__sym_alias__ ("FOOBAR_B"))) bar () {}
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_B"))) ~foo() {}
+  };
+
+  int foo::var = 2;
+
+  foo::foo () {
+    void (foo::*pbar)() = &foo::bar;
+  }
+}
+
+namespace c {
+  namespace cd {
+    class __attribute__ ((__sym_alias__ ("FOOCLS_C"))) foo {
+      static int var __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+      __attribute__ ((__sym_alias__ ("FOOCTR_C"))) foo () {
+	void (foo::*pbar)() = &foo::bar;
+      }
+      void __attribute__ ((__sym_alias__ ("FOOBAR_C"))) bar () {}
+      virtual __attribute__ ((__sym_alias__ ("FOODTR_C"))) ~foo() {}
+    };
+
+    int foo::var = 3;
+  }
+}
+
+/* { dg-final { scan-assembler "FOOCLS_A" } } */
+/* { dg-final { scan-assembler "FOOCLS_A_Dupe" } } */
+/* { 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-sym-alias-2.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
new file mode 100644
index 00000000000..6facfcc2cd9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+namespace {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_A"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo ();
+    void __attribute__ ((__sym_alias__ ("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-sym-alias-3.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
new file mode 100644
index 00000000000..42c7288ad4a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
@@ -0,0 +1,83 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+// sym can be applied to template function explicit instantiations.
+
+template <typename T>
+void
+fn(T) {
+};
+
+template void __attribute__ ((__sym_alias__ ("FOOFUN_UINT"))) fn<>(unsigned int);
+template void __attribute__ ((__sym_alias__ ("FOOFUN_LONG"))) fn<>(long);
+
+template<> void __attribute__ ((__sym_alias__ ("FOOFUN_CHAR"))) fn<>(char) {}
+
+
+template <typename T = void>
+struct
+foo {
+  virtual ~foo() {}
+
+  virtual void virtfun() {}
+
+  static void stfun() {}
+  void inlfun() {}
+};
+
+// Explicitly instantiate members before the enclosing class.
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_CHAR_VIRT"))) foo<char>::virtfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_CHAR_TI"))) foo<char>;
+
+// Though they're only output if the enclosing class is.
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_LONG_VIRT"))) foo<long>::virtfun();
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_LONG_TI_X"))) foo<long>;
+
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_VOID_ST"))) foo<void>::stfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_VOID_TI"))) foo<>;
+
+
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_X"))) foo<short>;
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_ST"))) foo<short>::stfun();
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_INL"))) foo<short>::inlfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_D"))) foo<short>;
+
+// Explicit specializations work too.
+
+template <>
+struct  __attribute__ ((__sym_alias__ ("FOOCLS_INT_TI")))
+foo<int>
+{
+  virtual ~foo() {}
+  virtual void __attribute__ ((__sym_alias__ ("FOOCLS_INT_VIRT"))) virtfun() {}
+};
+
+/* { dg-final { scan-assembler "FOOFUN_UINT" } } */
+/* { dg-final { scan-assembler "FOOFUN_LONG" } } */
+/* { dg-final { scan-assembler "FOOFUN_CHAR" } } */
+
+/* { dg-final { scan-assembler "FOOCLS_VOID_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_VOID_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_X" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_INL" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_D" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_TI_X" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_VIRT" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
new file mode 100644
index 00000000000..59b779de35d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+template <typename T = void>
+class
+__attribute__ ((__sym_alias__ ("FOOCLS")))
+foo // { dg-error "duplicate|already" }
+{
+  virtual ~foo() {}
+
+  template <typename U>
+  void
+    __attribute__ ((__sym_alias__ ("FOOTMF")))
+    tmemfun () {} // { dg-error "duplicate|already" }
+};
+
+template <typename T>
+void
+__attribute__ ((__sym_alias__ ("FOOTFN")))
+fn(T) { // { dg-error "duplicate|already" }
+};
+
+template class foo<>;
+template class foo<int>;
+template void foo<>::tmemfun<void>();
+template void foo<int>::tmemfun<void>();
+template void fn<>(int);
+template void fn<>(long);
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
new file mode 100644
index 00000000000..45d59b953c3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
@@ -0,0 +1,14 @@
+/* { dg-do compile { target c++11 } } */
+/* { dg-require-alias "" } */
+
+struct foo {
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+foo::foo () {}
+
+// Make sure the inherited cdtors don't duplicate the syms.
+struct bar : foo {
+  using foo::foo;
+};
diff --git a/gcc/varpool.cc b/gcc/varpool.cc
index e7b51b15e4a..33649906d1d 100644
--- a/gcc/varpool.cc
+++ b/gcc/varpool.cc
@@ -163,6 +163,9 @@ varpool_node::get_create (tree decl)
     }
 
   node->register_symbol ();
+
+  create_sym_alias_decls (decl);
+
   return node;
 }

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [gcc(refs/users/aoliva/heads/testme)] Introduce attribute sym_alias
@ 2023-11-30 10:10 Alexandre Oliva
  0 siblings, 0 replies; 33+ messages in thread
From: Alexandre Oliva @ 2023-11-30 10:10 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:dcbe4bfefab7448a16ab95ab7fe914bcc3873627

commit dcbe4bfefab7448a16ab95ab7fe914bcc3873627
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Fri Jul 14 03:45:14 2023 -0300

    Introduce attribute sym_alias
    
    This patch introduces an attribute to add extra asm names (aliases)
    for a decl 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 class types, namespace-scoped
    variables and functions, static data members, member functions,
    explicit instantiations and specializations of template functions,
    members and classes.
    
    When applied to constructors or destructor, additional sym aliases
    with _Base and _Del suffixes are defined for variants other than
    complete-object ones.  This changes the assumption that clones always
    carry the same attributes as their abstract declarations, so there is
    now a function to adjust them.
    
    C++ also had a bug in which attributes from local extern declarations
    failed to be propagated to a preexisting corresponding
    namespace-scoped decl.  I've fixed that, and adjusted acc tests that
    distinguished between C and C++ in this regard.
    
    Applying the attribute to class types is only valid in C++, and the
    effect is to attach the alias to the RTTI object associated with the
    class type.
    
    for  gcc/ChangeLog
    
            * attribs.cc: Include cgraph.h.
            (decl_attributes): Allow late introduction of sym_alias in
            types.
            (create_sym_alias_decl, create_sym_alias_decls): New.
            * attribs.h: Declare them.
            (FOR_EACH_SYM_ALIAS): New macro.
            * cgraph.cc (cgraph_node::create): Create sym_alias decls.
            * varpool.cc (varpool_node::get_create): Create sym_alias
            decls.
            * cgraph.h (symtab_node::remap_sym_alias_target): New.
            * symtab.cc (symtab_node::remap_sym_alias_target): Define.
            * cgraphunit.cc (cgraph_node::analyze): Create alias_target
            node if needed.
            (analyze_functions): Fixup visibility of implicit alias only
            after its node is analyzed.
            * doc/extend.texi (sym_alias): Document for variables,
            functions and types.
    
    for  gcc/ada/ChangeLog
    
            * doc/gnat_rm/interfacing_to_other_languages.rst: Mention
            attribute sym_alias to give RTTI symbols mnemonic names.
            * doc/gnat_ugn/the_gnat_compilation_model.rst: Mention
            aliases.  Fix incorrect ref to C1 ctor variant.
    
    for  gcc/c-family/ChangeLog
    
            * c-ada-spec.cc (pp_asm_name): Use first sym_alias if
            available.
            * c-attribs.cc (handle_sym_alias_attribute): New.
            (c_common_attribute_table): Add sym_alias.
            (handle_copy_attribute): Do not copy sym_alias attribute.
    
    for  gcc/c/ChangeLog
    
            * c-decl.cc (duplicate_decls): Remap sym_alias target.
    
    for  gcc/cp/ChangeLog
    
            * class.cc (adjust_clone_attributes): New.
            (copy_fndecl_with_name, build_clone): Call it.
            * cp-tree.h (adjust_clone_attributes): Declare.
            (update_sym_alias_interface): Declare.
            (update_tinfo_sym_alias): Declare.
            * decl.cc (duplicate_decls): Remap sym_alias target.
            Adjust clone attributes.
            (grokfndecl): Tentatively create sym_alias decls after
            adding attributes in e.g. a template member function explicit
            instantiation.
            * decl2.cc (cplus_decl_attributes): Update tinfo sym_alias.
            (copy_interface, update_sym_alias_interface): New.
            (determine_visibility): Update sym_alias interface.
            (tentative_decl_linkage, import_export_decl): Likewise.
            * name-lookup.cc: Include target.h and cgraph.h.
            (push_local_extern_decl_alias): Merge attributes with
            namespace-scoped decl, and drop duplicate sym_alias.
            * optimize.cc (maybe_clone_body): Re-adjust attributes after
            cloning them.  Update sym_alias interface.
            * rtti.cc: Include attribs.h and cgraph.h.
            (get_tinfo_decl): Copy sym_alias attributes from type to tinfo
            decl.  Create sym_alias decls.
            (update_tinfo_sym_alias): New.
    
    for  gcc/testsuite/ChangeLog
    
            * c-c++-common/goacc/declare-1.c: Adjust.
            * c-c++-common/goacc/declare-2.c: Adjust.
            * c-c++-common/torture/attr-sym-alias-1.c: New.
            * c-c++-common/torture/attr-sym-alias-2.c: New.
            * c-c++-common/torture/attr-sym-alias-3.c: New.
            * c-c++-common/torture/attr-sym-alias-4.c: New.
            * c-c++-common/attr-sym-alias-neg.c: New.
            * g++.dg/torture/attr-sym-alias-1.C: New.
            * g++.dg/torture/attr-sym-alias-2.C: New.
            * g++.dg/torture/attr-sym-alias-3.C: New.
            * g++.dg/torture/attr-sym-alias-4.C: New.
            * g++.dg/torture/attr-sym-alias-5.C: New.

Diff:
---
 .../doc/gnat_rm/interfacing_to_other_languages.rst |  6 ++
 .../doc/gnat_ugn/the_gnat_compilation_model.rst    | 10 ++-
 gcc/attribs.cc                                     | 68 +++++++++++++++++-
 gcc/attribs.h                                      |  7 ++
 gcc/c-family/c-ada-spec.cc                         |  7 ++
 gcc/c-family/c-attribs.cc                          | 33 ++++++++-
 gcc/c/c-decl.cc                                    |  2 +
 gcc/cgraph.cc                                      |  2 +
 gcc/cgraph.h                                       |  4 ++
 gcc/cgraphunit.cc                                  |  2 +-
 gcc/cp/class.cc                                    | 64 +++++++++++++++++
 gcc/cp/cp-tree.h                                   |  4 ++
 gcc/cp/decl.cc                                     |  4 ++
 gcc/cp/decl2.cc                                    | 50 +++++++++++++
 gcc/cp/name-lookup.cc                              | 11 +++
 gcc/cp/optimize.cc                                 |  3 +
 gcc/cp/rtti.cc                                     | 71 +++++++++++++++++-
 gcc/doc/extend.texi                                | 52 ++++++++++++++
 gcc/symtab.cc                                      | 36 ++++++++++
 gcc/testsuite/c-c++-common/goacc/declare-1.c       |  6 +-
 gcc/testsuite/c-c++-common/goacc/declare-2.c       | 14 ++--
 .../c-c++-common/torture/attr-sym-alias-1.c        | 39 ++++++++++
 .../c-c++-common/torture/attr-sym-alias-2.c        | 13 ++++
 .../c-c++-common/torture/attr-sym-alias-3.c        | 41 +++++++++++
 .../c-c++-common/torture/attr-sym-alias-4.c        | 28 ++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C    | 72 +++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C    | 26 +++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C    | 83 ++++++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C    | 28 ++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C    | 14 ++++
 gcc/varpool.cc                                     |  3 +
 31 files changed, 784 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 ad0be511d48..b8de16ebf7c 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 ``sym_alias`` 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 fd15459203a..20045698962 100644
--- a/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
+++ b/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
@@ -4274,6 +4274,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__ ((__sym_alias__ ("Ctor_For_Animal")))
         Animal() {Age_Count = 0;};
       private:
         int Age_Count;
@@ -4306,6 +4307,7 @@ both Carnivore and Domestic, that is:
         virtual int  Number_Of_Teeth ();
         virtual void Set_Owner (char* Name);
 
+        __attribute__ ((__sym_alias__ ("Ctor_For_Dog"))) // mnemonic alias
         Dog(); // Constructor
       private:
         int  Tooth_Count;
@@ -4344,7 +4346,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;
@@ -4360,7 +4363,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,7 +4385,8 @@ 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.
+mangled name (or an alias) 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.cc b/gcc/attribs.cc
index c7209c26acc..3ef07c885d5 100644
--- a/gcc/attribs.cc
+++ b/gcc/attribs.cc
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "target.h"
 #include "tree.h"
+#include "cgraph.h"
 #include "stringpool.h"
 #include "diagnostic-core.h"
 #include "attribs.h"
@@ -825,7 +826,8 @@ decl_attributes (tree *node, tree attributes, int flags,
 
       if (TYPE_P (*anode)
 	  && (flags & (int) ATTR_FLAG_TYPE_IN_PLACE)
-	  && COMPLETE_TYPE_P (*anode))
+	  && COMPLETE_TYPE_P (*anode)
+	  && !is_attribute_p ("sym_alias", name))
 	{
 	  warning (OPT_Wattributes, "type attributes ignored after type is already defined");
 	  continue;
@@ -2640,6 +2642,70 @@ attr_access::array_as_string (tree type) const
   return typstr;
 }
 
+/* Create a sym attribute for DECL to be visible with linkage name ID.  */
+
+tree
+create_sym_alias_decl (tree decl, tree id)
+{
+  const char *attr_str = "sym_alias";
+
+  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;
+
+      tree attr_name = get_identifier (attr_str);
+      error_at (DECL_SOURCE_LOCATION (decl),
+		"duplicate symbol name %qE in %qE attribute of %qD",
+		id, attr_name, decl);
+      inform (DECL_SOURCE_LOCATION (sym_node->decl),
+	      "already used by %qD", sym_node->decl);
+    }
+
+  tree clone = copy_node (decl);
+  DECL_ATTRIBUTES (clone) = remove_attribute (attr_str,
+					      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_extra_name_alias (clone, decl);
+    }
+  else
+    {
+      cgraph_node::create_same_body_alias (clone, decl);
+    }
+
+  return clone;
+}
+
+/* Create decls for all sym aliases requested in DECL's attributes.  */
+
+void
+create_sym_alias_decls (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl)
+      || DECL_ABSTRACT_P (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      create_sym_alias_decl (decl, id);
+    }
+}
+
 #if CHECKING_P
 
 namespace selftest
diff --git a/gcc/attribs.h b/gcc/attribs.h
index 84a43658a70..156f2c20588 100644
--- a/gcc/attribs.h
+++ b/gcc/attribs.h
@@ -398,4 +398,11 @@ extern void init_attr_rdwr_indices (rdwr_map *, tree);
 extern attr_access *get_parm_access (rdwr_map &, tree,
 				     tree = current_function_decl);
 
+extern tree create_sym_alias_decl (tree, tree);
+extern void create_sym_alias_decls (tree);
+
+#define FOR_EACH_SYM_ALIAS(sym, attrs)					\
+  for (tree sym = lookup_attribute ("sym_alias", (attrs));		\
+       sym; sym = lookup_attribute ("sym_alias", TREE_CHAIN (sym)))
+
 #endif // GCC_ATTRIBS_H
diff --git a/gcc/c-family/c-ada-spec.cc b/gcc/c-family/c-ada-spec.cc
index 050994d8416..5042b9cfecd 100644
--- a/gcc/c-family/c-ada-spec.cc
+++ b/gcc/c-family/c-ada-spec.cc
@@ -1431,6 +1431,13 @@ pp_ada_tree_identifier (pretty_printer *buffer, tree node, tree type,
 static void
 pp_asm_name (pretty_printer *buffer, tree t)
 {
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (t))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.cc b/gcc/c-family/c-attribs.cc
index 2b20e58c922..392437eab3b 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -108,7 +108,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_sym_alias_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,
@@ -388,6 +389,8 @@ const struct attribute_spec c_common_attribute_table[] =
 			      handle_alias_attribute, NULL },
   { "weakref",                0, 1, true,  false, false, false,
 			      handle_weakref_attribute, NULL },
+  { "sym_alias",              1, 1, false,  false, false, false,
+			      handle_sym_alias_attribute, NULL },
   { "no_instrument_function", 0, 0, true,  false, false, false,
 			      handle_no_instrument_function_attribute,
 			      NULL },
@@ -3002,7 +3005,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
@@ -3012,7 +3015,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
@@ -3022,6 +3025,29 @@ handle_alias_attribute (tree *node, tree name, tree args,
   return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs);
 }
 
+/* Handle a "sym_alias" attribute; arguments as in struct
+   attribute_spec.handler.  */
+
+static tree
+handle_sym_alias_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 (decl_in_symtab_p (node))
+    *no_add_attrs = false;
+  else if (TYPE_P (node) && c_dialect_cxx ())
+    *no_add_attrs = false;
+  else
+    return error_mark_node;
+
+  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.  */
 
@@ -3155,6 +3181,7 @@ handle_copy_attribute (tree *node, tree name, tree args,
 	      || is_attribute_p ("visibility", atname)
 	      || is_attribute_p ("weak", atname)
 	      || is_attribute_p ("weakref", atname)
+	      || is_attribute_p ("sym_alias", atname)
 	      || is_attribute_p ("target_clones", atname))
 	    continue;
 
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index cf1df82c0f4..e5a1ee54cb3 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -3119,6 +3119,8 @@ duplicate_decls (tree newdecl, tree olddecl)
 
   merge_decls (newdecl, olddecl, newtype, oldtype);
 
+  symtab_node::remap_sym_alias_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.cc b/gcc/cgraph.cc
index f93259a8c70..0bef2179e89 100644
--- a/gcc/cgraph.cc
+++ b/gcc/cgraph.cc
@@ -523,6 +523,8 @@ cgraph_node::create (tree decl)
   node->register_symbol ();
   maybe_record_nested_function (node);
 
+  create_sym_alias_decls (decl);
+
   return node;
 }
 
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index cfdd9f693a8..cb5ac01f7e2 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -327,6 +327,10 @@ public:
   /* Return DECL that alias is aliasing.  */
   inline tree get_alias_target_tree ();
 
+  /* Remap sym alias nodes recorded as aliasing REPLACED to alias REPLACEMENT
+     instead.  */
+  static void remap_sym_alias_target (tree replaced, tree replacement);
+
   /* Set section for symbol and its aliases.  */
   void set_section (const char *section);
 
diff --git a/gcc/cgraphunit.cc b/gcc/cgraphunit.cc
index bccd2f2abb5..eb2d05094e9 100644
--- a/gcc/cgraphunit.cc
+++ b/gcc/cgraphunit.cc
@@ -1175,7 +1175,7 @@ analyze_functions (bool first_time)
      C++ FE is confused about the COMDAT groups being right.  */
   if (symtab->cpp_implicit_aliases_done)
     FOR_EACH_SYMBOL (node)
-      if (node->cpp_implicit_alias)
+      if (node->cpp_implicit_alias && node->analyzed)
 	  node->fixup_same_cpp_alias_visibility (node->get_alias_target ());
   build_type_inheritance_graph ();
 
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index 6fdb56abfb9..fa8fc63db82 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -4896,6 +4896,68 @@ check_methods (tree t)
     }
 }
 
+/* Adjust sym alias name for CLONE, cloned from FN and named NAME,
+   if it is a cdtor, and drop the sym alias from other clones.  */
+
+void
+adjust_clone_attributes (tree fn, tree clone, tree name, bool skip_copy_p)
+{
+  if (IDENTIFIER_CDTOR_P (name))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	{
+	  found = true;
+	  break;
+	}
+
+      if (found
+	  && (name == complete_ctor_identifier
+	      || name == complete_dtor_identifier))
+	{
+	  /* Reuse the sym alias decls created for the primary cdtor
+	     decl.  */
+	  symtab_node::remap_sym_alias_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);
+
+	  if (!skip_copy_p)
+	    DECL_ATTRIBUTES (clone) = copy_list (DECL_ATTRIBUTES (clone));
+
+	  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	    {
+	      /* We need to copy this even with skip_copy_p, because
+		 even then copying was shallow.  */
+	      TREE_VALUE (sym) = copy_list (TREE_VALUE (sym));
+	      /* Append suf to the sym alias name.  */
+	      tree str = TREE_VALUE (TREE_VALUE (sym));
+	      char *symname = concat (TREE_STRING_POINTER (str), suf, NULL);
+	      str = build_string (TREE_STRING_LENGTH (str) + xlen, symname);
+	      TREE_VALUE (TREE_VALUE (sym)) = str;
+	      free (symname);
+	    }
+
+	  if (symtab_node::get (clone))
+	    create_sym_alias_decls (clone);
+	}
+    }
+  else
+    DECL_ATTRIBUTES (clone)
+      = remove_attribute ("sym_alias", DECL_ATTRIBUTES (clone));
+}
+
 /* FN is constructor, destructor or operator function.  Clone the
    declaration to create a NAME'd variant.  NEED_VTT_PARM_P and
    OMIT_INHERITED_PARMS_P are relevant if it's a cdtor.  */
@@ -5065,6 +5127,8 @@ build_clone (tree fn, tree name, bool need_vtt_parm_p,
   DECL_CHAIN (clone) = DECL_CHAIN (fn);
   DECL_CHAIN (fn) = clone;
 
+  adjust_clone_attributes (fn, clone, name);
+
   return clone;
 }
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 964af1ddd85..2bafed4e993 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5787,6 +5787,8 @@ struct GTY((for_user)) spec_entry
 
 extern int current_class_depth;
 
+void adjust_clone_attributes (tree fn, tree clone, tree name, bool = false);
+
 /* in decl.cc */
 
 /* An array of static vars & fns.  */
@@ -7022,6 +7024,7 @@ extern void do_push_parm_decls			(tree, tree, tree *);
 extern tree do_aggregate_paren_init		(tree, tree);
 
 /* in decl2.cc */
+extern void update_sym_alias_interface		(tree);
 extern void record_mangling			(tree, bool);
 extern void overwrite_mangling			(tree, tree);
 extern void note_mangling_alias			(tree, tree);
@@ -7596,6 +7599,7 @@ extern bool emit_tinfo_decl			(tree);
 extern unsigned get_pseudo_tinfo_index		(tree);
 extern tree get_pseudo_tinfo_type		(unsigned);
 extern tree build_if_nonnull			(tree, tree, tsubst_flags_t);
+extern void update_tinfo_sym_alias		(tree);
 
 /* in search.cc */
 extern tree get_parent_with_private_access 	(tree decl, tree binfo);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 2f2dbb8d107..5636d694b68 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -3237,6 +3237,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
 	      && TREE_STATIC (olddecl))))
     make_decl_rtl (olddecl);
 
+  symtab_node::remap_sym_alias_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.
@@ -3260,6 +3262,7 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
       FOR_EACH_CLONE (clone, olddecl)
 	{
 	  DECL_ATTRIBUTES (clone) = DECL_ATTRIBUTES (olddecl);
+	  adjust_clone_attributes (olddecl, clone, DECL_NAME (clone));
 	  DECL_PRESERVE_P (clone) |= DECL_PRESERVE_P (olddecl);
 	}
     }
@@ -10789,6 +10792,7 @@ grokfndecl (tree ctype,
     {
       cplus_decl_attributes (&decl, *attrlist, 0);
       *attrlist = NULL_TREE;
+      create_sym_alias_decls (decl);
     }
 
   if (DECL_HAS_CONTRACTS_P (decl))
diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
index 9e666e5eece..c7f009beaf0 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -1844,6 +1844,9 @@ cplus_decl_attributes (tree *decl, tree attributes, int flags)
   if (late_attrs)
     save_template_attributes (late_attrs, decl, flags);
 
+  if (TYPE_P (*decl) && attributes)
+    update_tinfo_sym_alias (*decl);
+
   /* Propagate deprecation out to the template.  */
   if (TREE_DEPRECATED (*decl))
     if (tree ti = get_template_info (*decl))
@@ -2200,6 +2203,47 @@ 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 sym aliases.  */
+
+void
+update_sym_alias_interface (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.  */
 
@@ -3007,6 +3051,8 @@ determine_visibility (tree decl)
        translation unit, we can make the type internal.  */
     constrain_visibility (decl, VISIBILITY_ANON, false);
 
+  update_sym_alias_interface (decl);
+
   /* If visibility changed and DECL already has DECL_RTL, ensure
      symbol flags are updated.  */
   if ((DECL_VISIBILITY (decl) != orig_visibility
@@ -3269,6 +3315,8 @@ tentative_decl_linkage (tree decl)
       else if (VAR_P (decl))
 	maybe_commonize_var (decl);
     }
+
+  update_sym_alias_interface (decl);
 }
 
 /* DECL is a FUNCTION_DECL or VAR_DECL.  If the object file linkage
@@ -3503,6 +3551,8 @@ import_export_decl (tree decl)
     }
 
   DECL_INTERFACE_KNOWN (decl) = 1;
+
+  update_sym_alias_interface (decl);
 }
 
 /* Return an expression that performs the destruction of DECL, which
diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc
index d19ea5d121c..f877780b2ac 100644
--- a/gcc/cp/name-lookup.cc
+++ b/gcc/cp/name-lookup.cc
@@ -22,9 +22,11 @@ along with GCC; see the file COPYING3.  If not see
 #define INCLUDE_MEMORY
 #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"
@@ -3479,6 +3481,15 @@ push_local_extern_decl_alias (tree decl)
 	  /* Adjust visibility.  */
 	  determine_visibility (alias);
 	}
+      else if (DECL_P (alias))
+	DECL_ATTRIBUTES (alias)
+	  = targetm.merge_decl_attributes (alias, decl);
+      if (DECL_P (alias))
+	{
+	  symtab_node::remap_sym_alias_target (decl, alias);
+	  DECL_ATTRIBUTES (decl)
+	    = remove_attribute ("sym_alias", DECL_ATTRIBUTES (alias));
+	}
     }
 
   retrofit_lang_decl (decl);
diff --git a/gcc/cp/optimize.cc b/gcc/cp/optimize.cc
index 9e8926e4cc6..4a2e8cb4354 100644
--- a/gcc/cp/optimize.cc
+++ b/gcc/cp/optimize.cc
@@ -528,9 +528,12 @@ maybe_clone_body (tree fn)
       DECL_VISIBILITY_SPECIFIED (clone) = DECL_VISIBILITY_SPECIFIED (fn);
       DECL_DLLIMPORT_P (clone) = DECL_DLLIMPORT_P (fn);
       DECL_ATTRIBUTES (clone) = clone_attrs (DECL_ATTRIBUTES (fn));
+      adjust_clone_attributes (fn, clone, DECL_NAME (clone), true);
       DECL_DISREGARD_INLINE_LIMITS (clone) = DECL_DISREGARD_INLINE_LIMITS (fn);
       set_decl_section_name (clone, fn);
 
+      update_sym_alias_interface (clone);
+
       /* Adjust the parameter names and locations.  */
       parm = DECL_ARGUMENTS (fn);
       clone_parm = DECL_ARGUMENTS (clone);
diff --git a/gcc/cp/rtti.cc b/gcc/cp/rtti.cc
index 7878929c246..4bb7b8c8c6e 100644
--- a/gcc/cp/rtti.cc
+++ b/gcc/cp/rtti.cc
@@ -28,8 +28,10 @@ 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"
+#include "cgraph.h"
 
 /* C++ returns type information to the user in struct type_info
    objects. We also use type information to implement dynamic_cast and
@@ -479,8 +481,13 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
 	  = build_tree_list (get_identifier ("non overlapping"),
 			     NULL_TREE);
       else
+	/* Share the non overlapping attribute, without assuming it's
+	   the only attribute, but assuming it's the last if it's
+	   present.  There may be sym aliases too, and those are not
+	   to be shared.  */
 	DECL_ATTRIBUTES (d)
-	  = DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]);
+	  = lookup_attribute ("non overlapping",
+			      DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]));
 
       /* Mark the variable as undefined -- but remember that we can
 	 define it later if we need to do so.  */
@@ -492,6 +499,16 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
       if (CLASS_TYPE_P (type))
 	CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type)) = d;
 
+      /* Copy sym alias attributes from the type to the rtti obj decl.  */
+      tree *attrs = &DECL_ATTRIBUTES (d);
+      FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+	{
+	  tree attr = tree_cons (TREE_PURPOSE (sym), TREE_VALUE (sym), *attrs);
+	  *attrs = attr;
+	  attrs = &TREE_CHAIN (attr);
+	}
+      create_sym_alias_decls (d);
+
       /* Add decl to the global array of tinfo decls.  */
       vec_safe_push (unemitted_tinfo_decls, d);
     }
@@ -499,6 +516,58 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
   return d;
 }
 
+/* After modifying the attributes of TYPE, check whether tinfo was
+   already created and, if so, add to it any sym alias attributes
+   that were not already present.  */
+
+void
+update_tinfo_sym_alias (tree type)
+{
+  if (!TYPE_SIZE (type) || !CLASS_TYPE_P (type))
+    return;
+
+  tree d = CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type));
+  if (!d)
+    return;
+
+  bool first = true;
+  symtab_node *node = NULL;
+
+  tree *attrs = &DECL_ATTRIBUTES (d);
+  FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (d_sym, *attrs)
+	if (TREE_VALUE (sym) == TREE_VALUE (d_sym))
+	  {
+	    found = true;
+	    break;
+	  }
+
+      if (found)
+	continue;
+
+      tree attr = tree_cons (TREE_PURPOSE (sym),
+			     TREE_VALUE (sym),
+			     *attrs);
+      *attrs = attr;
+      attrs = &TREE_CHAIN (attr);
+
+      if (first)
+	{
+	  first = false;
+	  node = symtab_node::get (d);
+	}
+
+      if (!node)
+	continue;
+
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+      create_sym_alias_decl (d, id);
+    }
+}
+
 /* Return the type_info object for TYPE.  */
 
 tree
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index e6de0815846..96674e2d857 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -4141,6 +4141,39 @@ Function Attributes}, @ref{PowerPC Function Attributes},
 @ref{Nios II Function Attributes}, and @ref{S/390 Function Attributes}
 for details.
 
+@cindex @code{sym_alias} function attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} attribute causes @var{name} to be output as an
+alias to the definition.  For instance,
+
+@smallexample
+void f (uint64_t) __attribute__ ((__sym_alias__ ("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 for aliases,
+and may not be available on all targets.
+
 @cindex @code{symver} function attribute
 @item symver ("@var{name2}@@@var{nodename}")
 On ELF targets this attribute creates a symbol version.  The @var{name2} part
@@ -8069,6 +8102,10 @@ will be placed in new, unique sections.
 
 This additional functionality requires Binutils version 2.36 or later.
 
+@cindex @code{sym_alias} variable attribute
+@item sym_alias ("@var{name}")
+See @pxref{Common Function Attributes}.
+
 @cindex @code{uninitialized} variable attribute
 @item uninitialized
 This attribute, attached to a variable with automatic storage, means that
@@ -9164,6 +9201,21 @@ is not supported; that is to say, if a given scalar object can be accessed
 through distinct types that assign a different storage order to it, then the
 behavior is undefined.
 
+@cindex @code{sym_alias} type attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} type 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__ ((__sym_alias__ ("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.
+
 @cindex @code{transparent_union} type attribute
 @item transparent_union
 
diff --git a/gcc/symtab.cc b/gcc/symtab.cc
index 0470509a98d..0812088e59f 100644
--- a/gcc/symtab.cc
+++ b/gcc/symtab.cc
@@ -1943,6 +1943,42 @@ symtab_node::noninterposable_alias (symtab_node *node, void *data)
   return false;
 }
 
+/* Remap sym alias nodes recorded as aliasing REPLACED to alias
+   REPLACEMENT instead.  */
+
+void
+symtab_node::remap_sym_alias_target (tree replaced, tree replacement)
+{
+  if (!decl_in_symtab_p (replacement)
+      || !symtab_node::get (replacement))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (replaced))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      symtab_node *sym_node = symtab_node::get_for_asmname (id);
+
+      if (!sym_node)
+	{
+	  create_sym_alias_decl (replacement, id);
+	  continue;
+	}
+
+      gcc_assert (!sym_node->analyzed);
+      if (sym_node->alias_target != replaced)
+	continue;
+
+      sym_node->definition = 0;
+
+      if (VAR_P (replaced))
+	varpool_node::create_extra_name_alias (sym_node->decl, replacement);
+      else
+	cgraph_node::create_same_body_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/goacc/declare-1.c b/gcc/testsuite/c-c++-common/goacc/declare-1.c
index 46ee01b6759..f2842893318 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-1.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-1.c
@@ -113,11 +113,11 @@ f_2 (void)
   int va3;
 #pragma acc declare device_resident(va3)
 
-#ifndef __cplusplus
+#if 0
   /* TODO PR90868
 
-     C: "error: variable '[...]' used more than once with '#pragma acc declare'".  */
-#else
+     "error: variable '[...]' used more than once with '#pragma acc declare'".  */
+
   extern int ve0;
 #pragma acc declare create(ve0)
 
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-2.c b/gcc/testsuite/c-c++-common/goacc/declare-2.c
index e2e22be57e9..aec59b69754 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-2.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-2.c
@@ -137,25 +137,25 @@ void
 f_pr90868_2 (void)
 {
   extern int we0;
-#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" } */
 
   extern int we1;
-#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" } */
 
   extern int *we2;
-#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" } */
 
   extern int we3;
-#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" } */
 
   extern int we4;
-#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" } */
 
   extern int we5;
-#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" } */
  
   extern int we6;
-#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" } */
 }
 
 
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
new file mode 100644
index 00000000000..61af50cb552
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
@@ -0,0 +1,39 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+int var_a = 1;
+
+void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+
+void
+foo_a ()
+{
+}
+
+
+int var_b;
+extern int var_b __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+
+void
+foo_b ()
+{
+}
+
+void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+
+
+int var_c __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+
+void __attribute__ ((__sym_alias__ ("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-sym-alias-2.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
new file mode 100644
index 00000000000..6f0d55ca42c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+struct s
+{
+  int mem __attribute__ ((__sym_alias__ ("MEMFOO"))); /* { dg-warning "attribute ignored" } */
+};
+
+void foo()
+{
+  extern void bar () __attribute__ ((__sym_alias__ ("FOOBAR")));
+  int var __attribute__ ((__sym_alias__ ("FOOVAR"))); /* { dg-warning "attribute ignored" } */
+}
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
new file mode 100644
index 00000000000..0052485a30a
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
@@ -0,0 +1,41 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int var_a = 1;
+
+void
+foo_a ()
+{
+  extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+}
+
+#if 0 // __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__ ((__sym_alias__ ("FOOVAR_B")));
+}
+
+int var_b;
+
+void __attribute__ ((__sym_alias__ ("FOOBAR_C")))
+foo_c ()
+{
+  void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+  /* Another sym for var_b.  */
+  extern int var_b __attribute__ ((__sym_alias__ ("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-sym-alias-4.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
new file mode 100644
index 00000000000..2beafa46379
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-require-alias "" } */
+
+int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A"))) = 42;
+
+int __attribute__ ((__sym_alias__ ("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-sym-alias-1.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
new file mode 100644
index 00000000000..579eda1a473
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
@@ -0,0 +1,72 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+class __attribute__ ((__sym_alias__ ("FOOCLS_A"),
+		      __sym_alias__ ("FOOCLS_A_Dupe"))) foo {
+  static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  void __attribute__ ((__sym_alias__ ("FOOBAR_A"))) bar ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+int foo::var = 1;
+
+foo::foo () {}
+
+void foo::bar () {}
+
+namespace b {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_B"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_B"))) foo ();
+    void __attribute__ ((__sym_alias__ ("FOOBAR_B"))) bar () {}
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_B"))) ~foo() {}
+  };
+
+  int foo::var = 2;
+
+  foo::foo () {
+    void (foo::*pbar)() = &foo::bar;
+  }
+}
+
+namespace c {
+  namespace cd {
+    class __attribute__ ((__sym_alias__ ("FOOCLS_C"))) foo {
+      static int var __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+      __attribute__ ((__sym_alias__ ("FOOCTR_C"))) foo () {
+	void (foo::*pbar)() = &foo::bar;
+      }
+      void __attribute__ ((__sym_alias__ ("FOOBAR_C"))) bar () {}
+      virtual __attribute__ ((__sym_alias__ ("FOODTR_C"))) ~foo() {}
+    };
+
+    int foo::var = 3;
+  }
+}
+
+/* { dg-final { scan-assembler "FOOCLS_A" } } */
+/* { dg-final { scan-assembler "FOOCLS_A_Dupe" } } */
+/* { 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-sym-alias-2.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
new file mode 100644
index 00000000000..6facfcc2cd9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+namespace {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_A"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo ();
+    void __attribute__ ((__sym_alias__ ("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-sym-alias-3.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
new file mode 100644
index 00000000000..42c7288ad4a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
@@ -0,0 +1,83 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+// sym can be applied to template function explicit instantiations.
+
+template <typename T>
+void
+fn(T) {
+};
+
+template void __attribute__ ((__sym_alias__ ("FOOFUN_UINT"))) fn<>(unsigned int);
+template void __attribute__ ((__sym_alias__ ("FOOFUN_LONG"))) fn<>(long);
+
+template<> void __attribute__ ((__sym_alias__ ("FOOFUN_CHAR"))) fn<>(char) {}
+
+
+template <typename T = void>
+struct
+foo {
+  virtual ~foo() {}
+
+  virtual void virtfun() {}
+
+  static void stfun() {}
+  void inlfun() {}
+};
+
+// Explicitly instantiate members before the enclosing class.
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_CHAR_VIRT"))) foo<char>::virtfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_CHAR_TI"))) foo<char>;
+
+// Though they're only output if the enclosing class is.
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_LONG_VIRT"))) foo<long>::virtfun();
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_LONG_TI_X"))) foo<long>;
+
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_VOID_ST"))) foo<void>::stfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_VOID_TI"))) foo<>;
+
+
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_X"))) foo<short>;
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_ST"))) foo<short>::stfun();
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_INL"))) foo<short>::inlfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_D"))) foo<short>;
+
+// Explicit specializations work too.
+
+template <>
+struct  __attribute__ ((__sym_alias__ ("FOOCLS_INT_TI")))
+foo<int>
+{
+  virtual ~foo() {}
+  virtual void __attribute__ ((__sym_alias__ ("FOOCLS_INT_VIRT"))) virtfun() {}
+};
+
+/* { dg-final { scan-assembler "FOOFUN_UINT" } } */
+/* { dg-final { scan-assembler "FOOFUN_LONG" } } */
+/* { dg-final { scan-assembler "FOOFUN_CHAR" } } */
+
+/* { dg-final { scan-assembler "FOOCLS_VOID_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_VOID_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_X" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_INL" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_D" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_TI_X" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_VIRT" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
new file mode 100644
index 00000000000..59b779de35d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+template <typename T = void>
+class
+__attribute__ ((__sym_alias__ ("FOOCLS")))
+foo // { dg-error "duplicate|already" }
+{
+  virtual ~foo() {}
+
+  template <typename U>
+  void
+    __attribute__ ((__sym_alias__ ("FOOTMF")))
+    tmemfun () {} // { dg-error "duplicate|already" }
+};
+
+template <typename T>
+void
+__attribute__ ((__sym_alias__ ("FOOTFN")))
+fn(T) { // { dg-error "duplicate|already" }
+};
+
+template class foo<>;
+template class foo<int>;
+template void foo<>::tmemfun<void>();
+template void foo<int>::tmemfun<void>();
+template void fn<>(int);
+template void fn<>(long);
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
new file mode 100644
index 00000000000..45d59b953c3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
@@ -0,0 +1,14 @@
+/* { dg-do compile { target c++11 } } */
+/* { dg-require-alias "" } */
+
+struct foo {
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+foo::foo () {}
+
+// Make sure the inherited cdtors don't duplicate the syms.
+struct bar : foo {
+  using foo::foo;
+};
diff --git a/gcc/varpool.cc b/gcc/varpool.cc
index e7b51b15e4a..33649906d1d 100644
--- a/gcc/varpool.cc
+++ b/gcc/varpool.cc
@@ -163,6 +163,9 @@ varpool_node::get_create (tree decl)
     }
 
   node->register_symbol ();
+
+  create_sym_alias_decls (decl);
+
   return node;
 }

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [gcc(refs/users/aoliva/heads/testme)] Introduce attribute sym_alias
@ 2023-11-23 11:46 Alexandre Oliva
  0 siblings, 0 replies; 33+ messages in thread
From: Alexandre Oliva @ 2023-11-23 11:46 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:372882d34ef47fd927b6e9790f4162e070f14886

commit 372882d34ef47fd927b6e9790f4162e070f14886
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Fri Jul 14 03:45:14 2023 -0300

    Introduce attribute sym_alias
    
    This patch introduces an attribute to add extra asm names (aliases)
    for a decl 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 class types, namespace-scoped
    variables and functions, static data members, member functions,
    explicit instantiations and specializations of template functions,
    members and classes.
    
    When applied to constructors or destructor, additional sym aliases
    with _Base and _Del suffixes are defined for variants other than
    complete-object ones.  This changes the assumption that clones always
    carry the same attributes as their abstract declarations, so there is
    now a function to adjust them.
    
    C++ also had a bug in which attributes from local extern declarations
    failed to be propagated to a preexisting corresponding
    namespace-scoped decl.  I've fixed that, and adjusted acc tests that
    distinguished between C and C++ in this regard.
    
    Applying the attribute to class types is only valid in C++, and the
    effect is to attach the alias to the RTTI object associated with the
    class type.
    
    for  gcc/ChangeLog
    
            * attribs.cc: Include cgraph.h.
            (decl_attributes): Allow late introduction of sym_alias in
            types.
            (create_sym_alias_decl, create_sym_alias_decls): New.
            * attribs.h: Declare them.
            (FOR_EACH_SYM_ALIAS): New macro.
            * cgraph.cc (cgraph_node::create): Create sym_alias decls.
            * varpool.cc (varpool_node::get_create): Create sym_alias
            decls.
            * cgraph.h (symtab_node::remap_sym_alias_target): New.
            * symtab.cc (symtab_node::remap_sym_alias_target): Define.
            * cgraphunit.cc (cgraph_node::analyze): Create alias_target
            node if needed.
            (analyze_functions): Fixup visibility of implicit alias only
            after its node is analyzed.
            * doc/extend.texi (sym_alias): Document for variables,
            functions and types.
    
    for  gcc/ada/ChangeLog
    
            * doc/gnat_rm/interfacing_to_other_languages.rst: Mention
            attribute sym_alias to give RTTI symbols mnemonic names.
            * doc/gnat_ugn/the_gnat_compilation_model.rst: Mention
            aliases.  Fix incorrect ref to C1 ctor variant.
    
    for  gcc/c-family/ChangeLog
    
            * c-ada-spec.cc (pp_asm_name): Use first sym_alias if
            available.
            * c-attribs.cc (handle_sym_alias_attribute): New.
            (c_common_attribute_table): Add sym_alias.
            (handle_copy_attribute): Do not copy sym_alias attribute.
    
    for  gcc/c/ChangeLog
    
            * c-decl.cc (duplicate_decls): Remap sym_alias target.
    
    for  gcc/cp/ChangeLog
    
            * class.cc (adjust_clone_attributes): New.
            (copy_fndecl_with_name, build_clone): Call it.
            * cp-tree.h (adjust_clone_attributes): Declare.
            (update_sym_alias_interface): Declare.
            (update_tinfo_sym_alias): Declare.
            * decl.cc (duplicate_decls): Remap sym_alias target.
            Adjust clone attributes.
            (grokfndecl): Tentatively create sym_alias decls after
            adding attributes in e.g. a template member function explicit
            instantiation.
            * decl2.cc (cplus_decl_attributes): Update tinfo sym_alias.
            (copy_interface, update_sym_alias_interface): New.
            (determine_visibility): Update sym_alias interface.
            (tentative_decl_linkage, import_export_decl): Likewise.
            * name-lookup.cc: Include target.h and cgraph.h.
            (push_local_extern_decl_alias): Merge attributes with
            namespace-scoped decl, and drop duplicate sym_alias.
            * optimize.cc (maybe_clone_body): Re-adjust attributes after
            cloning them.  Update sym_alias interface.
            * rtti.cc: Include attribs.h and cgraph.h.
            (get_tinfo_decl): Copy sym_alias attributes from type to tinfo
            decl.  Create sym_alias decls.
            (update_tinfo_sym_alias): New.
    
    for  gcc/testsuite/ChangeLog
    
            * c-c++-common/goacc/declare-1.c: Adjust.
            * c-c++-common/goacc/declare-2.c: Adjust.
            * c-c++-common/torture/attr-sym-alias-1.c: New.
            * c-c++-common/torture/attr-sym-alias-2.c: New.
            * c-c++-common/torture/attr-sym-alias-3.c: New.
            * c-c++-common/torture/attr-sym-alias-4.c: New.
            * g++.dg/torture/attr-alias-sym-1.C: New.
            * g++.dg/torture/attr-alias-sym-2.C: New.
            * g++.dg/torture/attr-alias-sym-3.C: New.
            * g++.dg/torture/attr-alias-sym-4.C: New.
            * g++.dg/torture/attr-alias-sym-5.C: New.

Diff:
---
 .../doc/gnat_rm/interfacing_to_other_languages.rst |  6 ++
 .../doc/gnat_ugn/the_gnat_compilation_model.rst    | 10 ++-
 gcc/attribs.cc                                     | 68 +++++++++++++++++-
 gcc/attribs.h                                      |  7 ++
 gcc/c-family/c-ada-spec.cc                         |  7 ++
 gcc/c-family/c-attribs.cc                          | 33 ++++++++-
 gcc/c/c-decl.cc                                    |  2 +
 gcc/cgraph.cc                                      |  2 +
 gcc/cgraph.h                                       |  4 ++
 gcc/cgraphunit.cc                                  |  2 +-
 gcc/cp/class.cc                                    | 64 +++++++++++++++++
 gcc/cp/cp-tree.h                                   |  4 ++
 gcc/cp/decl.cc                                     |  4 ++
 gcc/cp/decl2.cc                                    | 50 +++++++++++++
 gcc/cp/name-lookup.cc                              | 11 +++
 gcc/cp/optimize.cc                                 |  3 +
 gcc/cp/rtti.cc                                     | 71 +++++++++++++++++-
 gcc/doc/extend.texi                                | 52 ++++++++++++++
 gcc/symtab.cc                                      | 36 ++++++++++
 gcc/testsuite/c-c++-common/goacc/declare-1.c       |  6 +-
 gcc/testsuite/c-c++-common/goacc/declare-2.c       | 14 ++--
 .../c-c++-common/torture/attr-sym-alias-1.c        | 39 ++++++++++
 .../c-c++-common/torture/attr-sym-alias-2.c        | 13 ++++
 .../c-c++-common/torture/attr-sym-alias-3.c        | 41 +++++++++++
 .../c-c++-common/torture/attr-sym-alias-4.c        | 28 ++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C    | 72 +++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C    | 26 +++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C    | 83 ++++++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C    | 28 ++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C    | 14 ++++
 gcc/varpool.cc                                     |  3 +
 31 files changed, 784 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 ad0be511d48..b8de16ebf7c 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 ``sym_alias`` 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 ed24fed7293..b3f55901c97 100644
--- a/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
+++ b/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
@@ -4275,6 +4275,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__ ((__sym_alias__ ("Ctor_For_Animal")))
         Animal() {Age_Count = 0;};
       private:
         int Age_Count;
@@ -4307,6 +4308,7 @@ both Carnivore and Domestic, that is:
         virtual int  Number_Of_Teeth ();
         virtual void Set_Owner (char* Name);
 
+        __attribute__ ((__sym_alias__ ("Ctor_For_Dog"))) // mnemonic alias
         Dog(); // Constructor
       private:
         int  Tooth_Count;
@@ -4345,7 +4347,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;
@@ -4361,7 +4364,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,
@@ -4383,7 +4386,8 @@ 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.
+mangled name (or an alias) 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.cc b/gcc/attribs.cc
index f9fd2585989..3b125fe24b7 100644
--- a/gcc/attribs.cc
+++ b/gcc/attribs.cc
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "target.h"
 #include "tree.h"
+#include "cgraph.h"
 #include "stringpool.h"
 #include "diagnostic-core.h"
 #include "attribs.h"
@@ -825,7 +826,8 @@ decl_attributes (tree *node, tree attributes, int flags,
 
       if (TYPE_P (*anode)
 	  && (flags & (int) ATTR_FLAG_TYPE_IN_PLACE)
-	  && COMPLETE_TYPE_P (*anode))
+	  && COMPLETE_TYPE_P (*anode)
+	  && !is_attribute_p ("sym_alias", name))
 	{
 	  warning (OPT_Wattributes, "type attributes ignored after type is already defined");
 	  continue;
@@ -2640,6 +2642,70 @@ attr_access::array_as_string (tree type) const
   return typstr;
 }
 
+/* Create a sym attribute for DECL to be visible with linkage name ID.  */
+
+tree
+create_sym_alias_decl (tree decl, tree id)
+{
+  const char *attr_str = "sym_alias";
+
+  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;
+
+      tree attr_name = get_identifier (attr_str);
+      error_at (DECL_SOURCE_LOCATION (decl),
+		"duplicate symbol name %qE in %qE attribute of %qD",
+		id, attr_name, decl);
+      inform (DECL_SOURCE_LOCATION (sym_node->decl),
+	      "already used by %qD", sym_node->decl);
+    }
+
+  tree clone = copy_node (decl);
+  DECL_ATTRIBUTES (clone) = remove_attribute (attr_str,
+					      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_extra_name_alias (clone, decl);
+    }
+  else
+    {
+      cgraph_node::create_same_body_alias (clone, decl);
+    }
+
+  return clone;
+}
+
+/* Create decls for all sym aliases requested in DECL's attributes.  */
+
+void
+create_sym_alias_decls (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl)
+      || DECL_ABSTRACT_P (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      create_sym_alias_decl (decl, id);
+    }
+}
+
 #if CHECKING_P
 
 namespace selftest
diff --git a/gcc/attribs.h b/gcc/attribs.h
index 84a43658a70..156f2c20588 100644
--- a/gcc/attribs.h
+++ b/gcc/attribs.h
@@ -398,4 +398,11 @@ extern void init_attr_rdwr_indices (rdwr_map *, tree);
 extern attr_access *get_parm_access (rdwr_map &, tree,
 				     tree = current_function_decl);
 
+extern tree create_sym_alias_decl (tree, tree);
+extern void create_sym_alias_decls (tree);
+
+#define FOR_EACH_SYM_ALIAS(sym, attrs)					\
+  for (tree sym = lookup_attribute ("sym_alias", (attrs));		\
+       sym; sym = lookup_attribute ("sym_alias", TREE_CHAIN (sym)))
+
 #endif // GCC_ATTRIBS_H
diff --git a/gcc/c-family/c-ada-spec.cc b/gcc/c-family/c-ada-spec.cc
index 050994d8416..5042b9cfecd 100644
--- a/gcc/c-family/c-ada-spec.cc
+++ b/gcc/c-family/c-ada-spec.cc
@@ -1431,6 +1431,13 @@ pp_ada_tree_identifier (pretty_printer *buffer, tree node, tree type,
 static void
 pp_asm_name (pretty_printer *buffer, tree t)
 {
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (t))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.cc b/gcc/c-family/c-attribs.cc
index 2b20e58c922..392437eab3b 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -108,7 +108,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_sym_alias_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,
@@ -388,6 +389,8 @@ const struct attribute_spec c_common_attribute_table[] =
 			      handle_alias_attribute, NULL },
   { "weakref",                0, 1, true,  false, false, false,
 			      handle_weakref_attribute, NULL },
+  { "sym_alias",              1, 1, false,  false, false, false,
+			      handle_sym_alias_attribute, NULL },
   { "no_instrument_function", 0, 0, true,  false, false, false,
 			      handle_no_instrument_function_attribute,
 			      NULL },
@@ -3002,7 +3005,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
@@ -3012,7 +3015,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
@@ -3022,6 +3025,29 @@ handle_alias_attribute (tree *node, tree name, tree args,
   return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs);
 }
 
+/* Handle a "sym_alias" attribute; arguments as in struct
+   attribute_spec.handler.  */
+
+static tree
+handle_sym_alias_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 (decl_in_symtab_p (node))
+    *no_add_attrs = false;
+  else if (TYPE_P (node) && c_dialect_cxx ())
+    *no_add_attrs = false;
+  else
+    return error_mark_node;
+
+  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.  */
 
@@ -3155,6 +3181,7 @@ handle_copy_attribute (tree *node, tree name, tree args,
 	      || is_attribute_p ("visibility", atname)
 	      || is_attribute_p ("weak", atname)
 	      || is_attribute_p ("weakref", atname)
+	      || is_attribute_p ("sym_alias", atname)
 	      || is_attribute_p ("target_clones", atname))
 	    continue;
 
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 87b82752af8..f3aa58fedfd 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -3119,6 +3119,8 @@ duplicate_decls (tree newdecl, tree olddecl)
 
   merge_decls (newdecl, olddecl, newtype, oldtype);
 
+  symtab_node::remap_sym_alias_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.cc b/gcc/cgraph.cc
index e41e5ad3ae7..c45ce2d0a13 100644
--- a/gcc/cgraph.cc
+++ b/gcc/cgraph.cc
@@ -523,6 +523,8 @@ cgraph_node::create (tree decl)
   node->register_symbol ();
   maybe_record_nested_function (node);
 
+  create_sym_alias_decls (decl);
+
   return node;
 }
 
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index cedaaac3a45..6a444e6fa5b 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -327,6 +327,10 @@ public:
   /* Return DECL that alias is aliasing.  */
   inline tree get_alias_target_tree ();
 
+  /* Remap sym alias nodes recorded as aliasing REPLACED to alias REPLACEMENT
+     instead.  */
+  static void remap_sym_alias_target (tree replaced, tree replacement);
+
   /* Set section for symbol and its aliases.  */
   void set_section (const char *section);
 
diff --git a/gcc/cgraphunit.cc b/gcc/cgraphunit.cc
index bccd2f2abb5..eb2d05094e9 100644
--- a/gcc/cgraphunit.cc
+++ b/gcc/cgraphunit.cc
@@ -1175,7 +1175,7 @@ analyze_functions (bool first_time)
      C++ FE is confused about the COMDAT groups being right.  */
   if (symtab->cpp_implicit_aliases_done)
     FOR_EACH_SYMBOL (node)
-      if (node->cpp_implicit_alias)
+      if (node->cpp_implicit_alias && node->analyzed)
 	  node->fixup_same_cpp_alias_visibility (node->get_alias_target ());
   build_type_inheritance_graph ();
 
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index 6fdb56abfb9..fa8fc63db82 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -4896,6 +4896,68 @@ check_methods (tree t)
     }
 }
 
+/* Adjust sym alias name for CLONE, cloned from FN and named NAME,
+   if it is a cdtor, and drop the sym alias from other clones.  */
+
+void
+adjust_clone_attributes (tree fn, tree clone, tree name, bool skip_copy_p)
+{
+  if (IDENTIFIER_CDTOR_P (name))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	{
+	  found = true;
+	  break;
+	}
+
+      if (found
+	  && (name == complete_ctor_identifier
+	      || name == complete_dtor_identifier))
+	{
+	  /* Reuse the sym alias decls created for the primary cdtor
+	     decl.  */
+	  symtab_node::remap_sym_alias_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);
+
+	  if (!skip_copy_p)
+	    DECL_ATTRIBUTES (clone) = copy_list (DECL_ATTRIBUTES (clone));
+
+	  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+	    {
+	      /* We need to copy this even with skip_copy_p, because
+		 even then copying was shallow.  */
+	      TREE_VALUE (sym) = copy_list (TREE_VALUE (sym));
+	      /* Append suf to the sym alias name.  */
+	      tree str = TREE_VALUE (TREE_VALUE (sym));
+	      char *symname = concat (TREE_STRING_POINTER (str), suf, NULL);
+	      str = build_string (TREE_STRING_LENGTH (str) + xlen, symname);
+	      TREE_VALUE (TREE_VALUE (sym)) = str;
+	      free (symname);
+	    }
+
+	  if (symtab_node::get (clone))
+	    create_sym_alias_decls (clone);
+	}
+    }
+  else
+    DECL_ATTRIBUTES (clone)
+      = remove_attribute ("sym_alias", DECL_ATTRIBUTES (clone));
+}
+
 /* FN is constructor, destructor or operator function.  Clone the
    declaration to create a NAME'd variant.  NEED_VTT_PARM_P and
    OMIT_INHERITED_PARMS_P are relevant if it's a cdtor.  */
@@ -5065,6 +5127,8 @@ build_clone (tree fn, tree name, bool need_vtt_parm_p,
   DECL_CHAIN (clone) = DECL_CHAIN (fn);
   DECL_CHAIN (fn) = clone;
 
+  adjust_clone_attributes (fn, clone, name);
+
   return clone;
 }
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 5614b71eed4..5f601d374db 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5792,6 +5792,8 @@ struct GTY((for_user)) spec_entry
 
 extern int current_class_depth;
 
+void adjust_clone_attributes (tree fn, tree clone, tree name, bool = false);
+
 /* in decl.cc */
 
 /* An array of static vars & fns.  */
@@ -7027,6 +7029,7 @@ extern void do_push_parm_decls			(tree, tree, tree *);
 extern tree do_aggregate_paren_init		(tree, tree);
 
 /* in decl2.cc */
+extern void update_sym_alias_interface		(tree);
 extern void record_mangling			(tree, bool);
 extern void overwrite_mangling			(tree, tree);
 extern void note_mangling_alias			(tree, tree);
@@ -7601,6 +7604,7 @@ extern bool emit_tinfo_decl			(tree);
 extern unsigned get_pseudo_tinfo_index		(tree);
 extern tree get_pseudo_tinfo_type		(unsigned);
 extern tree build_if_nonnull			(tree, tree, tsubst_flags_t);
+extern void update_tinfo_sym_alias		(tree);
 
 /* in search.cc */
 extern tree get_parent_with_private_access 	(tree decl, tree binfo);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 4200a007d9a..e3df72060b0 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -3231,6 +3231,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
 	      && TREE_STATIC (olddecl))))
     make_decl_rtl (olddecl);
 
+  symtab_node::remap_sym_alias_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.
@@ -3254,6 +3256,7 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
       FOR_EACH_CLONE (clone, olddecl)
 	{
 	  DECL_ATTRIBUTES (clone) = DECL_ATTRIBUTES (olddecl);
+	  adjust_clone_attributes (olddecl, clone, DECL_NAME (clone));
 	  DECL_PRESERVE_P (clone) |= DECL_PRESERVE_P (olddecl);
 	}
     }
@@ -10783,6 +10786,7 @@ grokfndecl (tree ctype,
     {
       cplus_decl_attributes (&decl, *attrlist, 0);
       *attrlist = NULL_TREE;
+      create_sym_alias_decls (decl);
     }
 
   if (DECL_HAS_CONTRACTS_P (decl))
diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
index 9e666e5eece..c7f009beaf0 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -1844,6 +1844,9 @@ cplus_decl_attributes (tree *decl, tree attributes, int flags)
   if (late_attrs)
     save_template_attributes (late_attrs, decl, flags);
 
+  if (TYPE_P (*decl) && attributes)
+    update_tinfo_sym_alias (*decl);
+
   /* Propagate deprecation out to the template.  */
   if (TREE_DEPRECATED (*decl))
     if (tree ti = get_template_info (*decl))
@@ -2200,6 +2203,47 @@ 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 sym aliases.  */
+
+void
+update_sym_alias_interface (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      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.  */
 
@@ -3007,6 +3051,8 @@ determine_visibility (tree decl)
        translation unit, we can make the type internal.  */
     constrain_visibility (decl, VISIBILITY_ANON, false);
 
+  update_sym_alias_interface (decl);
+
   /* If visibility changed and DECL already has DECL_RTL, ensure
      symbol flags are updated.  */
   if ((DECL_VISIBILITY (decl) != orig_visibility
@@ -3269,6 +3315,8 @@ tentative_decl_linkage (tree decl)
       else if (VAR_P (decl))
 	maybe_commonize_var (decl);
     }
+
+  update_sym_alias_interface (decl);
 }
 
 /* DECL is a FUNCTION_DECL or VAR_DECL.  If the object file linkage
@@ -3503,6 +3551,8 @@ import_export_decl (tree decl)
     }
 
   DECL_INTERFACE_KNOWN (decl) = 1;
+
+  update_sym_alias_interface (decl);
 }
 
 /* Return an expression that performs the destruction of DECL, which
diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc
index 50aeb776ae2..4f85ef2c499 100644
--- a/gcc/cp/name-lookup.cc
+++ b/gcc/cp/name-lookup.cc
@@ -22,9 +22,11 @@ along with GCC; see the file COPYING3.  If not see
 #define INCLUDE_MEMORY
 #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"
@@ -3479,6 +3481,15 @@ push_local_extern_decl_alias (tree decl)
 	  /* Adjust visibility.  */
 	  determine_visibility (alias);
 	}
+      else if (DECL_P (alias))
+	DECL_ATTRIBUTES (alias)
+	  = targetm.merge_decl_attributes (alias, decl);
+      if (DECL_P (alias))
+	{
+	  symtab_node::remap_sym_alias_target (decl, alias);
+	  DECL_ATTRIBUTES (decl)
+	    = remove_attribute ("sym_alias", DECL_ATTRIBUTES (alias));
+	}
     }
 
   retrofit_lang_decl (decl);
diff --git a/gcc/cp/optimize.cc b/gcc/cp/optimize.cc
index 9e8926e4cc6..4a2e8cb4354 100644
--- a/gcc/cp/optimize.cc
+++ b/gcc/cp/optimize.cc
@@ -528,9 +528,12 @@ maybe_clone_body (tree fn)
       DECL_VISIBILITY_SPECIFIED (clone) = DECL_VISIBILITY_SPECIFIED (fn);
       DECL_DLLIMPORT_P (clone) = DECL_DLLIMPORT_P (fn);
       DECL_ATTRIBUTES (clone) = clone_attrs (DECL_ATTRIBUTES (fn));
+      adjust_clone_attributes (fn, clone, DECL_NAME (clone), true);
       DECL_DISREGARD_INLINE_LIMITS (clone) = DECL_DISREGARD_INLINE_LIMITS (fn);
       set_decl_section_name (clone, fn);
 
+      update_sym_alias_interface (clone);
+
       /* Adjust the parameter names and locations.  */
       parm = DECL_ARGUMENTS (fn);
       clone_parm = DECL_ARGUMENTS (clone);
diff --git a/gcc/cp/rtti.cc b/gcc/cp/rtti.cc
index 7878929c246..4bb7b8c8c6e 100644
--- a/gcc/cp/rtti.cc
+++ b/gcc/cp/rtti.cc
@@ -28,8 +28,10 @@ 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"
+#include "cgraph.h"
 
 /* C++ returns type information to the user in struct type_info
    objects. We also use type information to implement dynamic_cast and
@@ -479,8 +481,13 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
 	  = build_tree_list (get_identifier ("non overlapping"),
 			     NULL_TREE);
       else
+	/* Share the non overlapping attribute, without assuming it's
+	   the only attribute, but assuming it's the last if it's
+	   present.  There may be sym aliases too, and those are not
+	   to be shared.  */
 	DECL_ATTRIBUTES (d)
-	  = DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]);
+	  = lookup_attribute ("non overlapping",
+			      DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]));
 
       /* Mark the variable as undefined -- but remember that we can
 	 define it later if we need to do so.  */
@@ -492,6 +499,16 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
       if (CLASS_TYPE_P (type))
 	CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type)) = d;
 
+      /* Copy sym alias attributes from the type to the rtti obj decl.  */
+      tree *attrs = &DECL_ATTRIBUTES (d);
+      FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+	{
+	  tree attr = tree_cons (TREE_PURPOSE (sym), TREE_VALUE (sym), *attrs);
+	  *attrs = attr;
+	  attrs = &TREE_CHAIN (attr);
+	}
+      create_sym_alias_decls (d);
+
       /* Add decl to the global array of tinfo decls.  */
       vec_safe_push (unemitted_tinfo_decls, d);
     }
@@ -499,6 +516,58 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
   return d;
 }
 
+/* After modifying the attributes of TYPE, check whether tinfo was
+   already created and, if so, add to it any sym alias attributes
+   that were not already present.  */
+
+void
+update_tinfo_sym_alias (tree type)
+{
+  if (!TYPE_SIZE (type) || !CLASS_TYPE_P (type))
+    return;
+
+  tree d = CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type));
+  if (!d)
+    return;
+
+  bool first = true;
+  symtab_node *node = NULL;
+
+  tree *attrs = &DECL_ATTRIBUTES (d);
+  FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+    {
+      bool found = false;
+      FOR_EACH_SYM_ALIAS (d_sym, *attrs)
+	if (TREE_VALUE (sym) == TREE_VALUE (d_sym))
+	  {
+	    found = true;
+	    break;
+	  }
+
+      if (found)
+	continue;
+
+      tree attr = tree_cons (TREE_PURPOSE (sym),
+			     TREE_VALUE (sym),
+			     *attrs);
+      *attrs = attr;
+      attrs = &TREE_CHAIN (attr);
+
+      if (first)
+	{
+	  first = false;
+	  node = symtab_node::get (d);
+	}
+
+      if (!node)
+	continue;
+
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+      create_sym_alias_decl (d, id);
+    }
+}
+
 /* Return the type_info object for TYPE.  */
 
 tree
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 69ea9541887..1226eae12d1 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -4141,6 +4141,39 @@ Function Attributes}, @ref{PowerPC Function Attributes},
 @ref{Nios II Function Attributes}, and @ref{S/390 Function Attributes}
 for details.
 
+@cindex @code{sym_alias} function attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} attribute causes @var{name} to be output as an
+alias to the definition.  For instance,
+
+@smallexample
+void f (uint64_t) __attribute__ ((__sym_alias__ ("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 for aliases,
+and may not be available on all targets.
+
 @cindex @code{symver} function attribute
 @item symver ("@var{name2}@@@var{nodename}")
 On ELF targets this attribute creates a symbol version.  The @var{name2} part
@@ -8069,6 +8102,10 @@ will be placed in new, unique sections.
 
 This additional functionality requires Binutils version 2.36 or later.
 
+@cindex @code{sym_alias} variable attribute
+@item sym_alias ("@var{name}")
+See @pxref{Common Function Attributes}.
+
 @cindex @code{uninitialized} variable attribute
 @item uninitialized
 This attribute, attached to a variable with automatic storage, means that
@@ -9164,6 +9201,21 @@ is not supported; that is to say, if a given scalar object can be accessed
 through distinct types that assign a different storage order to it, then the
 behavior is undefined.
 
+@cindex @code{sym_alias} type attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} type 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__ ((__sym_alias__ ("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.
+
 @cindex @code{transparent_union} type attribute
 @item transparent_union
 
diff --git a/gcc/symtab.cc b/gcc/symtab.cc
index 0470509a98d..0812088e59f 100644
--- a/gcc/symtab.cc
+++ b/gcc/symtab.cc
@@ -1943,6 +1943,42 @@ symtab_node::noninterposable_alias (symtab_node *node, void *data)
   return false;
 }
 
+/* Remap sym alias nodes recorded as aliasing REPLACED to alias
+   REPLACEMENT instead.  */
+
+void
+symtab_node::remap_sym_alias_target (tree replaced, tree replacement)
+{
+  if (!decl_in_symtab_p (replacement)
+      || !symtab_node::get (replacement))
+    return;
+
+  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (replaced))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (sym));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      symtab_node *sym_node = symtab_node::get_for_asmname (id);
+
+      if (!sym_node)
+	{
+	  create_sym_alias_decl (replacement, id);
+	  continue;
+	}
+
+      gcc_assert (!sym_node->analyzed);
+      if (sym_node->alias_target != replaced)
+	continue;
+
+      sym_node->definition = 0;
+
+      if (VAR_P (replaced))
+	varpool_node::create_extra_name_alias (sym_node->decl, replacement);
+      else
+	cgraph_node::create_same_body_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/goacc/declare-1.c b/gcc/testsuite/c-c++-common/goacc/declare-1.c
index 46ee01b6759..f2842893318 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-1.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-1.c
@@ -113,11 +113,11 @@ f_2 (void)
   int va3;
 #pragma acc declare device_resident(va3)
 
-#ifndef __cplusplus
+#if 0
   /* TODO PR90868
 
-     C: "error: variable '[...]' used more than once with '#pragma acc declare'".  */
-#else
+     "error: variable '[...]' used more than once with '#pragma acc declare'".  */
+
   extern int ve0;
 #pragma acc declare create(ve0)
 
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-2.c b/gcc/testsuite/c-c++-common/goacc/declare-2.c
index e2e22be57e9..aec59b69754 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-2.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-2.c
@@ -137,25 +137,25 @@ void
 f_pr90868_2 (void)
 {
   extern int we0;
-#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than once with '#pragma acc declare'" "" } */
 
   extern int we1;
-#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than once with '#pragma acc declare'" "" } */
 
   extern int *we2;
-#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more than once with '#pragma acc declare'" "" } */
 
   extern int we3;
-#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used more than once with '#pragma acc declare'" "" } */
 
   extern int we4;
-#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than once with '#pragma acc declare'" "" } */
 
   extern int we5;
-#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' used more than once with '#pragma acc declare'" "" } */
  
   extern int we6;
-#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" { target c } } */
+#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' used more than once with '#pragma acc declare'" "" } */
 }
 
 
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
new file mode 100644
index 00000000000..61af50cb552
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
@@ -0,0 +1,39 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+int var_a = 1;
+
+void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+
+void
+foo_a ()
+{
+}
+
+
+int var_b;
+extern int var_b __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+
+void
+foo_b ()
+{
+}
+
+void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+
+
+int var_c __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+
+void __attribute__ ((__sym_alias__ ("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-sym-alias-2.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
new file mode 100644
index 00000000000..6f0d55ca42c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+struct s
+{
+  int mem __attribute__ ((__sym_alias__ ("MEMFOO"))); /* { dg-warning "attribute ignored" } */
+};
+
+void foo()
+{
+  extern void bar () __attribute__ ((__sym_alias__ ("FOOBAR")));
+  int var __attribute__ ((__sym_alias__ ("FOOVAR"))); /* { dg-warning "attribute ignored" } */
+}
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
new file mode 100644
index 00000000000..0052485a30a
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
@@ -0,0 +1,41 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int var_a = 1;
+
+void
+foo_a ()
+{
+  extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+}
+
+#if 0 // __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__ ((__sym_alias__ ("FOOVAR_B")));
+}
+
+int var_b;
+
+void __attribute__ ((__sym_alias__ ("FOOBAR_C")))
+foo_c ()
+{
+  void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+  /* Another sym for var_b.  */
+  extern int var_b __attribute__ ((__sym_alias__ ("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-sym-alias-4.c b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
new file mode 100644
index 00000000000..2beafa46379
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-require-alias "" } */
+
+int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A"))) = 42;
+
+int __attribute__ ((__sym_alias__ ("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-sym-alias-1.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
new file mode 100644
index 00000000000..579eda1a473
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
@@ -0,0 +1,72 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+class __attribute__ ((__sym_alias__ ("FOOCLS_A"),
+		      __sym_alias__ ("FOOCLS_A_Dupe"))) foo {
+  static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  void __attribute__ ((__sym_alias__ ("FOOBAR_A"))) bar ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+int foo::var = 1;
+
+foo::foo () {}
+
+void foo::bar () {}
+
+namespace b {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_B"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_B"))) foo ();
+    void __attribute__ ((__sym_alias__ ("FOOBAR_B"))) bar () {}
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_B"))) ~foo() {}
+  };
+
+  int foo::var = 2;
+
+  foo::foo () {
+    void (foo::*pbar)() = &foo::bar;
+  }
+}
+
+namespace c {
+  namespace cd {
+    class __attribute__ ((__sym_alias__ ("FOOCLS_C"))) foo {
+      static int var __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+      __attribute__ ((__sym_alias__ ("FOOCTR_C"))) foo () {
+	void (foo::*pbar)() = &foo::bar;
+      }
+      void __attribute__ ((__sym_alias__ ("FOOBAR_C"))) bar () {}
+      virtual __attribute__ ((__sym_alias__ ("FOODTR_C"))) ~foo() {}
+    };
+
+    int foo::var = 3;
+  }
+}
+
+/* { dg-final { scan-assembler "FOOCLS_A" } } */
+/* { dg-final { scan-assembler "FOOCLS_A_Dupe" } } */
+/* { 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-sym-alias-2.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
new file mode 100644
index 00000000000..6facfcc2cd9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+namespace {
+  class __attribute__ ((__sym_alias__ ("FOOCLS_A"))) foo {
+    static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+    __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+    virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo ();
+    void __attribute__ ((__sym_alias__ ("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-sym-alias-3.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
new file mode 100644
index 00000000000..42c7288ad4a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
@@ -0,0 +1,83 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+// sym can be applied to template function explicit instantiations.
+
+template <typename T>
+void
+fn(T) {
+};
+
+template void __attribute__ ((__sym_alias__ ("FOOFUN_UINT"))) fn<>(unsigned int);
+template void __attribute__ ((__sym_alias__ ("FOOFUN_LONG"))) fn<>(long);
+
+template<> void __attribute__ ((__sym_alias__ ("FOOFUN_CHAR"))) fn<>(char) {}
+
+
+template <typename T = void>
+struct
+foo {
+  virtual ~foo() {}
+
+  virtual void virtfun() {}
+
+  static void stfun() {}
+  void inlfun() {}
+};
+
+// Explicitly instantiate members before the enclosing class.
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_CHAR_VIRT"))) foo<char>::virtfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_CHAR_TI"))) foo<char>;
+
+// Though they're only output if the enclosing class is.
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_LONG_VIRT"))) foo<long>::virtfun();
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_LONG_TI_X"))) foo<long>;
+
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_VOID_ST"))) foo<void>::stfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_VOID_TI"))) foo<>;
+
+
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_X"))) foo<short>;
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_ST"))) foo<short>::stfun();
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_INL"))) foo<short>::inlfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_D"))) foo<short>;
+
+// Explicit specializations work too.
+
+template <>
+struct  __attribute__ ((__sym_alias__ ("FOOCLS_INT_TI")))
+foo<int>
+{
+  virtual ~foo() {}
+  virtual void __attribute__ ((__sym_alias__ ("FOOCLS_INT_VIRT"))) virtfun() {}
+};
+
+/* { dg-final { scan-assembler "FOOFUN_UINT" } } */
+/* { dg-final { scan-assembler "FOOFUN_LONG" } } */
+/* { dg-final { scan-assembler "FOOFUN_CHAR" } } */
+
+/* { dg-final { scan-assembler "FOOCLS_VOID_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_VOID_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_X" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_INL" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_D" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_TI_X" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_VIRT" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
new file mode 100644
index 00000000000..59b779de35d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+template <typename T = void>
+class
+__attribute__ ((__sym_alias__ ("FOOCLS")))
+foo // { dg-error "duplicate|already" }
+{
+  virtual ~foo() {}
+
+  template <typename U>
+  void
+    __attribute__ ((__sym_alias__ ("FOOTMF")))
+    tmemfun () {} // { dg-error "duplicate|already" }
+};
+
+template <typename T>
+void
+__attribute__ ((__sym_alias__ ("FOOTFN")))
+fn(T) { // { dg-error "duplicate|already" }
+};
+
+template class foo<>;
+template class foo<int>;
+template void foo<>::tmemfun<void>();
+template void foo<int>::tmemfun<void>();
+template void fn<>(int);
+template void fn<>(long);
diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
new file mode 100644
index 00000000000..45d59b953c3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
@@ -0,0 +1,14 @@
+/* { dg-do compile { target c++11 } } */
+/* { dg-require-alias "" } */
+
+struct foo {
+  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+foo::foo () {}
+
+// Make sure the inherited cdtors don't duplicate the syms.
+struct bar : foo {
+  using foo::foo;
+};
diff --git a/gcc/varpool.cc b/gcc/varpool.cc
index e7b51b15e4a..33649906d1d 100644
--- a/gcc/varpool.cc
+++ b/gcc/varpool.cc
@@ -163,6 +163,9 @@ varpool_node::get_create (tree decl)
     }
 
   node->register_symbol ();
+
+  create_sym_alias_decls (decl);
+
   return node;
 }

^ permalink raw reply	[flat|nested] 33+ messages in thread

end of thread, other threads:[~2024-05-24 13:38 UTC | newest]

Thread overview: 33+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-11-20  9:03 [gcc(refs/users/aoliva/heads/testme)] Introduce attribute sym_alias Alexandre Oliva
2023-11-23 11:46 Alexandre Oliva
2023-11-30 10:10 Alexandre Oliva
2023-11-30 10:28 Alexandre Oliva
2023-11-30 10:36 Alexandre Oliva
2023-11-30 10:40 Alexandre Oliva
2023-11-30 10:55 Alexandre Oliva
2023-11-30 10:58 Alexandre Oliva
2023-11-30 11:03 Alexandre Oliva
2023-11-30 11:38 Alexandre Oliva
2023-11-30 12:30 Alexandre Oliva
2023-11-30 12:38 Alexandre Oliva
2023-11-30 15:18 Alexandre Oliva
2023-11-30 16:49 Alexandre Oliva
2023-11-30 17:26 Alexandre Oliva
2023-12-01 18:03 Alexandre Oliva
2023-12-01 18:04 Alexandre Oliva
2023-12-01 20:43 Alexandre Oliva
2023-12-01 23:42 Alexandre Oliva
2023-12-02 17:48 Alexandre Oliva
2023-12-03  1:46 Alexandre Oliva
2023-12-05 19:30 Alexandre Oliva
2023-12-05 21:51 Alexandre Oliva
2023-12-06  2:31 Alexandre Oliva
2023-12-06 20:01 Alexandre Oliva
2023-12-06 22:47 Alexandre Oliva
2023-12-07 19:44 Alexandre Oliva
2023-12-12  2:38 Alexandre Oliva
2023-12-12 20:23 Alexandre Oliva
2023-12-14 12:47 Alexandre Oliva
2023-12-14 13:45 Alexandre Oliva
2023-12-14 16:26 Alexandre Oliva
2024-05-24 13:38 Alexandre Oliva

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).