public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [RFC, WIP] introduce attribute exalias
@ 2020-07-29 20:56 Alexandre Oliva
  2020-08-07 17:38 ` [PATCH] " Alexandre Oliva
  0 siblings, 1 reply; 35+ messages in thread
From: Alexandre Oliva @ 2020-07-29 20:56 UTC (permalink / raw)
  To: gcc-patches; +Cc: jason, nathan, joseph, hainque, ebotcazou


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

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

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

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

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

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

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

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

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

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

Early feedback is welcome.  Thanks in advance,

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

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


introduce exrtti as a separate attribute for types

From: Alexandre Oliva <oliva@adacore.com>

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

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


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

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

* [PATCH] introduce attribute exalias
  2020-07-29 20:56 [RFC, WIP] introduce attribute exalias Alexandre Oliva
@ 2020-08-07 17:38 ` Alexandre Oliva
  2020-08-14 15:39   ` Alexandre Oliva
  2023-07-15  1:08   ` [PATCH v3] Introduce attribute reverse_alias Alexandre Oliva
  0 siblings, 2 replies; 35+ messages in thread
From: Alexandre Oliva @ 2020-08-07 17:38 UTC (permalink / raw)
  To: gcc-patches; +Cc: jason, nathan, joseph, hainque, ebotcazou

Since last week's patchlet, I've delayed the creation of the exalias
decls, improved the merging of attributes, minimizing
interface/visibility updates, found a better way to assign exaliases to
nested explicit instantiations, even after enabling aliases to
already-defined types, so now I'm reasonably happy with the patch.


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

The attribute is usable in C and C++, presumably in all C-family
languages.  It can be attached to global variables and functions.  In
C++, it can also be attached to namespace-scoped variables and
functions, static data members, member functions, explicit
instantiations and specializations of template functions, members and
classes.  When applied to constructors or destructor, additional
exaliases with _Base and _Del suffixes are defined for variants other
than complete-object ones.

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.


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


Regstrapped on x86_64-linux-gnu.  Ok to install?


(The newly-introduced attr-weak-1.c passes in C, but is marked as XFAIL
for C++, so it gets an XPASS in C; I could move it to some C++-only
subtree, or drop it altogether and file a PR instead)


for  gcc/ChangeLog

	* attribs.c: Include cgraph.h.
	(decl_attributes): Allow late introduction of exalias in
	types.
	(create_exalias_decl, create_exalias_decls): New.
	* attribs.h: Declare them.
	(FOR_EACH_EXALIAS): New macro.
	* cgraph.c (cgraph_node::create): Create exalias decls.
	* varpool.c (varpool_node::get_create): Create exalias decls.
	* cgraph.h (symtab_node::remap_exalias_target): New.
	* symtab.c (symtab_node::remap_exalias_target): Define.
	* cgraphunit.c (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 (exalias): Document for variables, functions
	and types.

for  gcc/ada/ChangeLog

	* doc/gnat_rm/interfacing_to_other_languages.rst: Mention
	attribute exalias to give RTTI symbols mnemonic names.
	* doc/gnat_ugn/the_gnat_compilation_model.rst: Mention
	attribute exalias.  Fix incorrect ref to C1 ctor variant.

for  gcc/c-family/ChangeLog

	* c-ada-spec.c (pp_asm_name): Use first exalias if available.
	* c-attribs.c (handle_exalias_attribute): New.
	(c_common_attribute_table): Add exalias.
	(handle_copy_attribute): Do not copy exalias.
	* c-decl.c (duplicate_decls): Remap exalias target.

for  gcc/cp/ChangeLog

	* class.c (copy_fndecl_with_name): Move/adjust exalias to
	cdtor variants.
	(build_cdtor_clones): Drop exalias from primary variant.
	* cp-tree.h (update_exalias_interface, update_tinfo_exalias):
	Declare.
	* decl.c (duplicate_decls): Remap exalias target.
	(grokfndecl): Tentatively create exalias decls after adding
	attributes in e.g. a template member function explicit
	instantiation.
	* decl2.c (cplus_decl_attributes): Update tinfo exalias.
	(copy_interface, update_exalias_interface): New.
	(determine_visibility): Update exalias interface.
	(tentative_decl_linkage, import_export_decl): Likewise.
	* name-lookup.c: Include target.h and cgraph.h.
	(set_local_extern_decl_linkage): Merge attributes with a
	namespace-scoped decl if one is found.  Remap exalias
	targets, and drop exaliases from the local decl.
	* optimize.c (maybe_clone_body): Only copy attributes if they
	haven't been copied yet.  Update exalias interface.
	* rtti.c: Include attribs.h and cgraph.h.
	(get_tinfo_decl): Copy exalias attributes from type to tinfo
	decl.  Create exalias decls.
	(update_tinfo_exalias): New.

for  gcc/testsuite/ChangeLog

	* c-c++-common/attr-weak-1.c: New, xfailed.
	* c-c++-common/torture/attr-exalias-1.c: New.
	* c-c++-common/torture/attr-exalias-2.c: New.
	* c-c++-common/torture/attr-exalias-3.c: New.
	* c-c++-common/torture/attr-exalias-4.c: New.
	* g++.dg/torture/attr-exalias-1.C: New.
	* g++.dg/torture/attr-exalias-2.C: New.
	* g++.dg/torture/attr-exalias-3.C: New.
	* g++.dg/torture/attr-exalias-4.C: New.
---
 .../doc/gnat_rm/interfacing_to_other_languages.rst |    6 +
 .../doc/gnat_ugn/the_gnat_compilation_model.rst    |   20 +++--
 gcc/attribs.c                                      |   66 ++++++++++++++++
 gcc/attribs.h                                      |    7 ++
 gcc/c-family/c-ada-spec.c                          |    7 ++
 gcc/c-family/c-attribs.c                           |   33 +++++++-
 gcc/c/c-decl.c                                     |    2 
 gcc/cgraph.c                                       |    3 +
 gcc/cgraph.h                                       |    4 +
 gcc/cgraphunit.c                                   |    2 
 gcc/cp/class.c                                     |   55 +++++++++++++
 gcc/cp/cp-tree.h                                   |    2 
 gcc/cp/decl.c                                      |    3 +
 gcc/cp/decl2.c                                     |   49 ++++++++++++
 gcc/cp/name-lookup.c                               |    8 ++
 gcc/cp/optimize.c                                  |    7 +-
 gcc/cp/rtti.c                                      |   66 ++++++++++++++++
 gcc/doc/extend.texi                                |   52 +++++++++++++
 gcc/symtab.c                                       |   36 +++++++++
 gcc/testsuite/c-c++-common/attr-weak-1.c           |   19 +++++
 .../c-c++-common/torture/attr-exalias-1.c          |   39 +++++++++
 .../c-c++-common/torture/attr-exalias-2.c          |   13 +++
 .../c-c++-common/torture/attr-exalias-3.c          |   41 ++++++++++
 .../c-c++-common/torture/attr-exalias-4.c          |   28 +++++++
 gcc/testsuite/g++.dg/torture/attr-exalias-1.C      |   70 +++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-exalias-2.C      |   26 ++++++
 gcc/testsuite/g++.dg/torture/attr-exalias-3.C      |   83 ++++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-exalias-4.C      |   28 +++++++
 gcc/varpool.c                                      |    3 +
 29 files changed, 764 insertions(+), 14 deletions(-)
 create mode 100644 gcc/testsuite/c-c++-common/attr-weak-1.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-exalias-1.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-exalias-2.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-exalias-3.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-exalias-4.c
 create mode 100644 gcc/testsuite/g++.dg/torture/attr-exalias-1.C
 create mode 100644 gcc/testsuite/g++.dg/torture/attr-exalias-2.C
 create mode 100644 gcc/testsuite/g++.dg/torture/attr-exalias-3.C
 create mode 100644 gcc/testsuite/g++.dg/torture/attr-exalias-4.C

diff --git a/gcc/ada/doc/gnat_rm/interfacing_to_other_languages.rst b/gcc/ada/doc/gnat_rm/interfacing_to_other_languages.rst
index ad0be51..ce6a4da 100644
--- a/gcc/ada/doc/gnat_rm/interfacing_to_other_languages.rst
+++ b/gcc/ada/doc/gnat_rm/interfacing_to_other_languages.rst
@@ -123,6 +123,12 @@ It is also possible to import a C++ exception using the following syntax:
 The ``External_Name`` is the name of the C++ RTTI symbol. You can then
 cover a specific C++ exception in an exception handler.
 
+RTTI symbols undergo C++ name mangling, which can make for identifiers
+that are inconvenient to use.  An alias with a mnemonic name can be
+introduced by adding attribute ``exalias`` to the class that the RTTI
+symbol refers to.
+
+
 .. _Interfacing_to_COBOL:
 
 Interfacing to COBOL
diff --git a/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst b/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
index b8729d0..2a13a24 100644
--- a/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
+++ b/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
@@ -4279,6 +4279,7 @@ and two public primitives to set and get the value of this attribute.
       public:
         virtual void Set_Age (int New_Age);
         virtual int Age ();
+        __attribute__ ((__exalias__ ("Ctor_For_Animal"))) // extra alias
         Animal() {Age_Count = 0;};
       private:
         int Age_Count;
@@ -4311,6 +4312,7 @@ both Carnivore and Domestic, that is:
         virtual int  Number_Of_Teeth ();
         virtual void Set_Owner (char* Name);
 
+        __attribute__ ((__exalias__ ("Ctor_For_Dog"))) // mnemonic alias
         Dog(); // Constructor
       private:
         int  Tooth_Count;
@@ -4349,7 +4351,8 @@ how to import these C++ declarations from the Ada side:
 
        function New_Animal return Animal;
        pragma CPP_Constructor (New_Animal);
-       pragma Import (CPP, New_Animal, "_ZN6AnimalC1Ev");
+       pragma Import (CPP, New_Animal,
+                      "_ZN6AnimalC1Ev"); -- or "Ctor_For_Animal"
 
        type Dog is new Animal and Carnivore and Domestic with record
          Tooth_Count : Natural;
@@ -4365,7 +4368,7 @@ how to import these C++ declarations from the Ada side:
 
        function New_Dog return Dog;
        pragma CPP_Constructor (New_Dog);
-       pragma Import (CPP, New_Dog, "_ZN3DogC2Ev");
+       pragma Import (CPP, New_Dog, "Ctor_For_Dog"); -- or "_ZN3DogC1Ev"
      end Animals;
 
 Thanks to the compatibility between GNAT run-time structures and the C++ ABI,
@@ -4382,12 +4385,13 @@ because the dispatch table associated with these tagged types will be built
 in the C++ side and therefore will not contain the predefined Ada primitives
 which Ada would otherwise expect.
 
-As the reader can see there is no need to indicate the C++ mangled names
-associated with each subprogram because it is assumed that all the calls to
-these primitives will be dispatching calls. The only exception is the
-constructor, which must be registered with the compiler by means of
-``pragma CPP_Constructor`` and needs to provide its associated C++
-mangled name because the Ada compiler generates direct calls to it.
+As the reader can see there is no need to indicate the C++ mangled
+names (or extra aliases) associated with each subprogram because it is
+assumed that all the calls to these primitives will be dispatching
+calls. The only exception is the constructor, which must be registered
+with the compiler by means of ``pragma CPP_Constructor`` and needs to
+provide its associated C++ mangled name because the Ada compiler
+generates direct calls to it.
 
 With the above packages we can now declare objects of type Dog on the Ada side
 and dispatch calls to the corresponding subprograms on the C++ side. We can
diff --git a/gcc/attribs.c b/gcc/attribs.c
index 71dae12..3768053 100644
--- a/gcc/attribs.c
+++ b/gcc/attribs.c
@@ -25,6 +25,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "stringpool.h"
 #include "diagnostic-core.h"
 #include "attribs.h"
+#include "cgraph.h"
 #include "stor-layout.h"
 #include "langhooks.h"
 #include "plugin.h"
@@ -663,7 +664,8 @@ decl_attributes (tree *node, tree attributes, int flags,
 
       if (TYPE_P (*anode)
 	  && (flags & (int) ATTR_FLAG_TYPE_IN_PLACE)
-	  && TYPE_SIZE (*anode) != NULL_TREE)
+	  && TYPE_SIZE (*anode) != NULL_TREE
+	  && !is_attribute_p ("exalias", name))
 	{
 	  warning (OPT_Wattributes, "type attributes ignored after type is already defined");
 	  continue;
@@ -2076,6 +2078,68 @@ init_attr_rdwr_indices (rdwr_map *rwm, tree fntype)
     }
 }
 
+/* Create an exalias for DECL with linkage name ID.  */
+
+tree
+create_exalias_decl (tree decl, tree id)
+{
+  tree name = get_identifier ("exalias");
+
+  if (symtab_node *sym_node = symtab_node::get_for_asmname (id))
+    {
+      if ((sym_node->analyzed
+	   ? sym_node->get_alias_target ()->decl
+	   : sym_node->alias_target) == decl)
+	return sym_node->decl;
+
+      error_at (DECL_SOURCE_LOCATION (decl),
+		"duplicate symbol name %qE in %qE attribute of %qD",
+		id, 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 ("exalias",
+					      DECL_ATTRIBUTES (decl));
+  SET_DECL_ASSEMBLER_NAME (clone, id);
+  TREE_USED (id) = 1;
+  TREE_USED (clone) = 1;
+  DECL_PRESERVE_P (clone) = 1;
+  DECL_EXTERNAL (clone) = 0;
+  TREE_STATIC (clone) = 1;
+
+  if (VAR_P (clone))
+    {
+      DECL_READ_P (clone) = 1;
+      varpool_node::create_extra_name_alias (clone, decl);
+    }
+  else
+    {
+      cgraph_node::create_same_body_alias (clone, decl);
+    }
+
+  return clone;
+}
+
+/* Create all exaliases requested in DECL's attributes.  */
+
+void
+create_exalias_decls (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl))
+    return;
+
+  FOR_EACH_EXALIAS (exalias, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (exalias));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      create_exalias_decl (decl, id);
+    }
+}
+
 
 #if CHECKING_P
 
diff --git a/gcc/attribs.h b/gcc/attribs.h
index dea0b6c..1ba56ca 100644
--- a/gcc/attribs.h
+++ b/gcc/attribs.h
@@ -248,4 +248,11 @@ typedef hash_map<rdwr_access_hash, attr_access> rdwr_map;
 
 extern void init_attr_rdwr_indices (rdwr_map *, tree);
 
+extern tree create_exalias_decl (tree, tree);
+extern void create_exalias_decls (tree);
+
+#define FOR_EACH_EXALIAS(exalias, attrs)				\
+  for (tree exalias = lookup_attribute ("exalias", (attrs)); exalias;	\
+       exalias = lookup_attribute ("exalias", TREE_CHAIN (exalias)))
+
 #endif // GCC_ATTRIBS_H
diff --git a/gcc/c-family/c-ada-spec.c b/gcc/c-family/c-ada-spec.c
index c75b173..127eca6 100644
--- a/gcc/c-family/c-ada-spec.c
+++ b/gcc/c-family/c-ada-spec.c
@@ -1434,6 +1434,13 @@ pp_ada_tree_identifier (pretty_printer *buffer, tree node, tree type,
 static void
 pp_asm_name (pretty_printer *buffer, tree t)
 {
+  FOR_EACH_EXALIAS (exalias, DECL_ATTRIBUTES (t))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (exalias));
+      pp_string (buffer, TREE_STRING_POINTER (id));
+      return;
+    }
+
   tree name = DECL_ASSEMBLER_NAME (t);
   char *ada_name = XALLOCAVEC (char, IDENTIFIER_LENGTH (name) + 1), *s;
   const char *ident = IDENTIFIER_POINTER (name);
diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
index 3721483..c00de3f 100644
--- a/gcc/c-family/c-attribs.c
+++ b/gcc/c-family/c-attribs.c
@@ -99,7 +99,8 @@ static tree handle_noplt_attribute (tree *, tree, tree, int, bool *) ;
 static tree handle_alias_ifunc_attribute (bool, tree *, tree, tree, bool *);
 static tree handle_ifunc_attribute (tree *, tree, tree, int, bool *);
 static tree handle_alias_attribute (tree *, tree, tree, int, bool *);
-static tree handle_weakref_attribute (tree *, tree, tree, int, bool *) ;
+static tree handle_weakref_attribute (tree *, tree, tree, int, bool *);
+static tree handle_exalias_attribute (tree *, tree, tree, int, bool *);
 static tree handle_visibility_attribute (tree *, tree, tree, int,
 					 bool *);
 static tree handle_tls_model_attribute (tree *, tree, tree, int,
@@ -333,6 +334,8 @@ const struct attribute_spec c_common_attribute_table[] =
 			      handle_alias_attribute, NULL },
   { "weakref",                0, 1, true,  false, false, false,
 			      handle_weakref_attribute, NULL },
+  { "exalias",                1, 1, false,  false, false, false,
+			      handle_exalias_attribute, NULL },
   { "no_instrument_function", 0, 0, true,  false, false, false,
 			      handle_no_instrument_function_attribute,
 			      NULL },
@@ -2424,7 +2427,7 @@ handle_alias_ifunc_attribute (bool is_alias, tree *node, tree name, tree args,
   return NULL_TREE;
 }
 
-/* Handle an "alias" or "ifunc" attribute; arguments as in
+/* Handle an "ifunc" attribute; arguments as in
    struct attribute_spec.handler.  */
 
 static tree
@@ -2434,7 +2437,7 @@ handle_ifunc_attribute (tree *node, tree name, tree args,
   return handle_alias_ifunc_attribute (false, node, name, args, no_add_attrs);
 }
 
-/* Handle an "alias" or "ifunc" attribute; arguments as in
+/* Handle an "alias" attribute; arguments as in
    struct attribute_spec.handler.  */
 
 static tree
@@ -2444,6 +2447,29 @@ handle_alias_attribute (tree *node, tree name, tree args,
   return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs);
 }
 
+/* Handle an "exalias" attribute; arguments as in struct
+   attribute_spec.handler.  */
+
+static tree
+handle_exalias_attribute (tree *pnode, tree name, tree args,
+			  int ARG_UNUSED (flags), bool *no_add_attrs)
+{
+  tree node = *pnode;
+
+  *no_add_attrs = true;
+
+  if (TREE_CODE (TREE_VALUE (args)) != STRING_CST)
+    error ("%qE attribute argument not a string", name);
+  else if (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.  */
 
@@ -2571,6 +2597,7 @@ handle_copy_attribute (tree *node, tree name, tree args,
 	  tree atname = get_attribute_name (at);
 	  if (is_attribute_p ("alias", atname)
 	      || is_attribute_p ("always_inline", atname)
+	      || is_attribute_p ("exalias", atname)
 	      || is_attribute_p ("gnu_inline", atname)
 	      || is_attribute_p ("ifunc", atname)
 	      || is_attribute_p ("noinline", atname)
diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c
index 5d6b504..79a3de1 100644
--- a/gcc/c/c-decl.c
+++ b/gcc/c/c-decl.c
@@ -2942,6 +2942,8 @@ duplicate_decls (tree newdecl, tree olddecl)
 
   merge_decls (newdecl, olddecl, newtype, oldtype);
 
+  symtab_node::remap_exalias_target (newdecl, olddecl);
+
   /* The NEWDECL will no longer be needed.
 
      Before releasing the node, be sure to remove function from symbol
diff --git a/gcc/cgraph.c b/gcc/cgraph.c
index c0b4579..a0121a0 100644
--- a/gcc/cgraph.c
+++ b/gcc/cgraph.c
@@ -524,6 +524,9 @@ cgraph_node::create (tree decl)
       node->next_nested = node->origin->nested;
       node->origin->nested = node;
     }
+
+  create_exalias_decls (decl);
+
   return node;
 }
 
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index 0211f08..ab5bda5 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -319,6 +319,10 @@ public:
   /* Return node that alias is aliasing.  */
   inline symtab_node *get_alias_target (void);
 
+  /* Remap exalias nodes recorded as aliasing REPLACED to alias
+     REPLACEMENT instead.  */
+  static void remap_exalias_target (tree replaced, tree replacement);
+
   /* Set section for symbol and its aliases.  */
   void set_section (const char *section);
 
diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c
index 0b1009d..c96f720 100644
--- a/gcc/cgraphunit.c
+++ b/gcc/cgraphunit.c
@@ -1157,7 +1157,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.c b/gcc/cp/class.c
index b39bdaa..7e8f35f 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -4831,6 +4831,58 @@ copy_fndecl_with_name (tree fn, tree name, tree_code code,
 
   /* Create the RTL for this function.  */
   SET_DECL_RTL (clone, NULL);
+
+  if (code == ERROR_MARK)
+    {
+      bool found = false;
+      FOR_EACH_EXALIAS (exalias, DECL_ATTRIBUTES (clone))
+	{
+	  found = true;
+	  break;
+	}
+
+      if (found
+	  && (name == complete_ctor_identifier
+	      || name == complete_dtor_identifier))
+	{
+	  /* Reuse the exalias decls created for the primary cdtor decl.  */
+	  symtab_node::remap_exalias_target (fn, clone);
+	}
+      else if (found)
+	{
+	  const char *suf;
+
+	  if (name == base_ctor_identifier
+	      || name == base_dtor_identifier)
+	    suf = "_Base";
+	  else if (name == deleting_dtor_identifier)
+	    suf = "_Del";
+	  else
+	    gcc_unreachable ();
+
+	  size_t xlen = strlen (suf);
+
+	  DECL_ATTRIBUTES (clone) = copy_list (DECL_ATTRIBUTES (clone));
+
+	  FOR_EACH_EXALIAS (exalias, DECL_ATTRIBUTES (clone))
+	    {
+	      TREE_VALUE (exalias) = copy_list (TREE_VALUE (exalias));
+	      /* Append suf to the exalias name.  */
+	      tree str = TREE_VALUE (TREE_VALUE (exalias));
+	      char *symname = concat (TREE_STRING_POINTER (str), suf, NULL);
+	      str = build_string (TREE_STRING_LENGTH (str) + xlen, symname);
+	      TREE_VALUE (TREE_VALUE (exalias)) = str;
+	      free (symname);
+	    }
+
+	  if (symtab_node::get (clone))
+	    create_exalias_decls (clone);
+	}
+    }
+  else
+    DECL_ATTRIBUTES (clone)
+      = remove_attribute ("exalias", DECL_ATTRIBUTES (clone));
+
   rest_of_decl_compilation (clone, namespace_bindings_p (), at_eof);
 
   return clone;
@@ -4928,6 +4980,9 @@ build_cdtor_clones (tree fn, bool needs_vtt_parm_p, bool omit_inherited_parms_p)
       count += 2;
     }
 
+  DECL_ATTRIBUTES (fn)
+    = remove_attribute ("exalias", DECL_ATTRIBUTES (fn));
+
   return count;
 }
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index fc54e6b..9ff45e0 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6601,6 +6601,7 @@ extern tree build_explicit_specifier		(tree, tsubst_flags_t);
 extern void do_push_parm_decls			(tree, tree, tree *);
 
 /* in decl2.c */
+extern void update_exalias_interface		(tree);
 extern void record_mangling			(tree, bool);
 extern void overwrite_mangling			(tree, tree);
 extern void note_mangling_alias			(tree, tree);
@@ -7043,6 +7044,7 @@ extern tree build_dynamic_cast			(location_t, tree, tree,
 						 tsubst_flags_t);
 extern void emit_support_tinfos			(void);
 extern bool emit_tinfo_decl			(tree);
+extern void update_tinfo_exalias		(tree);
 
 /* in search.c */
 extern bool accessible_base_p			(tree, tree, bool);
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index a68bbe0..c1afde1 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -2899,6 +2899,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend)
 	      && TREE_STATIC (olddecl))))
     make_decl_rtl (olddecl);
 
+  symtab_node::remap_exalias_target (newdecl, olddecl);
+
   /* The NEWDECL will no longer be needed.  Because every out-of-class
      declaration of a member results in a call to duplicate_decls,
      freeing these nodes represents in a significant savings.
@@ -9823,6 +9825,7 @@ grokfndecl (tree ctype,
     {
       cplus_decl_attributes (&decl, *attrlist, 0);
       *attrlist = NULL_TREE;
+      create_exalias_decls (decl);
     }
 
   /* Check main's type after attributes have been applied.  */
diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index 33c8377..d020e95 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -1601,6 +1601,8 @@ cplus_decl_attributes (tree *decl, tree attributes, int flags)
 
   if (TREE_CODE (*decl) == TYPE_DECL)
     SET_IDENTIFIER_TYPE_VALUE (DECL_NAME (*decl), TREE_TYPE (*decl));
+  else if (TYPE_P (*decl) && attributes)
+    update_tinfo_exalias (*decl);
 
   /* Propagate deprecation out to the template.  */
   if (TREE_DEPRECATED (*decl))
@@ -1951,6 +1953,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 exaliases.  */
+
+void
+update_exalias_interface (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl))
+    return;
+
+  FOR_EACH_EXALIAS (exalias, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (exalias));
+      id = get_identifier (TREE_STRING_POINTER (id));
+      symtab_node *sym_node = symtab_node::get_for_asmname (id);
+
+      if (sym_node
+	  && (sym_node->analyzed
+	      ? sym_node->get_alias_target ()->decl
+	      : sym_node->alias_target) == decl)
+	copy_interface (sym_node->decl, decl);
+    }
+}
+
 /* Set DECL up to have the closest approximation of "initialized common"
    linkage available.  */
 
@@ -2747,6 +2790,8 @@ determine_visibility (tree decl)
        translation unit, we can make the type internal.  */
     constrain_visibility (decl, VISIBILITY_ANON, false);
 
+  update_exalias_interface (decl);
+
   /* If visibility changed and DECL already has DECL_RTL, ensure
      symbol flags are updated.  */
   if ((DECL_VISIBILITY (decl) != orig_visibility
@@ -3013,6 +3058,8 @@ tentative_decl_linkage (tree decl)
       else if (VAR_P (decl))
 	maybe_commonize_var (decl);
     }
+
+  update_exalias_interface (decl);
 }
 
 /* DECL is a FUNCTION_DECL or VAR_DECL.  If the object file linkage
@@ -3247,6 +3294,8 @@ import_export_decl (tree decl)
     }
 
   DECL_INTERFACE_KNOWN (decl) = 1;
+
+  update_exalias_interface (decl);
 }
 
 /* Return an expression that performs the destruction of DECL, which
diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c
index 9f30d90..26bdc25 100644
--- a/gcc/cp/name-lookup.c
+++ b/gcc/cp/name-lookup.c
@@ -22,9 +22,11 @@ along with GCC; see the file COPYING3.  If not see
 #define INCLUDE_UNIQUE_PTR
 #include "system.h"
 #include "coretypes.h"
+#include "target.h"
 #include "cp-tree.h"
 #include "timevar.h"
 #include "stringpool.h"
+#include "cgraph.h"
 #include "print-tree.h"
 #include "attribs.h"
 #include "debug.h"
@@ -2924,6 +2926,12 @@ set_local_extern_decl_linkage (tree decl, bool shadowed)
 	       different decl.  */
 	    TREE_PUBLIC (decl) = TREE_PUBLIC (*iter);
 
+	    DECL_ATTRIBUTES (*iter)
+	      = targetm.merge_decl_attributes (*iter, decl);
+	    symtab_node::remap_exalias_target (decl, *iter);
+	    DECL_ATTRIBUTES (decl)
+	      = remove_attribute ("exalias", DECL_ATTRIBUTES (*iter));
+
 	    if (cp_function_chain->extern_decl_map == NULL)
 	      cp_function_chain->extern_decl_map
 		= hash_table<cxx_int_tree_map_hasher>::create_ggc (20);
diff --git a/gcc/cp/optimize.c b/gcc/cp/optimize.c
index abdcd7f..62568d7 100644
--- a/gcc/cp/optimize.c
+++ b/gcc/cp/optimize.c
@@ -516,10 +516,15 @@ maybe_clone_body (tree fn)
       DECL_VISIBILITY (clone) = DECL_VISIBILITY (fn);
       DECL_VISIBILITY_SPECIFIED (clone) = DECL_VISIBILITY_SPECIFIED (fn);
       DECL_DLLIMPORT_P (clone) = DECL_DLLIMPORT_P (fn);
-      DECL_ATTRIBUTES (clone) = copy_list (DECL_ATTRIBUTES (fn));
+      /* We may have already copied them in copy_fndecl_with_name,
+	 before dropping exaliases from fn.  Don't overwrite it if so.  */
+      if (!DECL_ATTRIBUTES (clone))
+	DECL_ATTRIBUTES (clone) = copy_list (DECL_ATTRIBUTES (fn));
       DECL_DISREGARD_INLINE_LIMITS (clone) = DECL_DISREGARD_INLINE_LIMITS (fn);
       set_decl_section_name (clone, DECL_SECTION_NAME (fn));
 
+      update_exalias_interface (clone);
+
       /* Adjust the parameter names and locations.  */
       parm = DECL_ARGUMENTS (fn);
       clone_parm = DECL_ARGUMENTS (clone);
diff --git a/gcc/cp/rtti.c b/gcc/cp/rtti.c
index d43248c..223c608 100644
--- a/gcc/cp/rtti.c
+++ b/gcc/cp/rtti.c
@@ -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
@@ -469,6 +471,18 @@ get_tinfo_decl (tree type)
       if (CLASS_TYPE_P (type))
 	CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type)) = d;
 
+      /* Copy exalias attributes from the type to the rtti obj decl.  */
+      tree *attrs = &DECL_ATTRIBUTES (d);
+      FOR_EACH_EXALIAS (exalias, TYPE_ATTRIBUTES (type))
+	{
+	  tree attr = tree_cons (TREE_PURPOSE (exalias),
+				 TREE_VALUE (exalias),
+				 *attrs);
+	  *attrs = attr;
+	  attrs = &TREE_CHAIN (attr);
+	}
+      create_exalias_decls (d);
+
       /* Add decl to the global array of tinfo decls.  */
       vec_safe_push (unemitted_tinfo_decls, d);
     }
@@ -476,6 +490,58 @@ get_tinfo_decl (tree type)
   return d;
 }
 
+/* After modifying the attributes of TYPE, check whether tinfo was
+   already created and, if so, add to it any exalias attributes that
+   were not already present.  */
+
+void
+update_tinfo_exalias (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_EXALIAS (exalias, TYPE_ATTRIBUTES (type))
+    {
+      bool found = false;
+      FOR_EACH_EXALIAS (dexalias, *attrs)
+	if (TREE_VALUE (exalias) == TREE_VALUE (dexalias))
+	  {
+	    found = true;
+	    break;
+	  }
+
+      if (found)
+	continue;
+
+      tree attr = tree_cons (TREE_PURPOSE (exalias),
+			     TREE_VALUE (exalias),
+			     *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 (exalias));
+      id = get_identifier (TREE_STRING_POINTER (id));
+      create_exalias_decl (d, id);
+    }
+}
+
 /* Return a pointer to a type_info object describing TYPE, suitably
    cast to the language defined type.  */
 
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 37a675a..0ba880f 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -2882,6 +2882,39 @@ when using these attributes the problem is diagnosed
 earlier and with exact location of the call even in presence of inline
 functions or when not emitting debugging information.
 
+@item exalias ("@var{name}")
+@cindex @code{exalias} function attribute
+The @code{exalias} attribute causes @var{name} to be emitted as an alias
+to the definition.  For instance,
+
+@smallexample
+void f (uint64_t) __attribute__ ((__exalias__ ("f_u64")));
+void f (uint64_t) @{ /* @r{Do something.} */; @}
+@end smallexample
+
+@noindent
+defines @samp{f}, and outputs @samp{f_u64} as an alias for @samp{f}.
+This is particularly useful when exporting C++ names for use in other
+languages, or as an alias target, when machine-dependent types would
+make mangled names harder to deal with.
+
+In the case of C++ constructors and destructors, in which a single
+definition may output multiple symbols, the specified name is associated
+with the variant that constructs or destructs a complete object.  The
+variant that applies to a base subobject gets a @code{_Base} suffix, and
+the deleting destructor gets a @code{_Del} suffix.
+
+This attribute is silently ignored if @samp{f} is not defined in the
+same translation unit, so that the attribute can be attached to forward
+declarations.
+
+The name @samp{f_u64} is an assembly symbol name: it does not undergo
+C++ name mangling, and it is not made visible in any scope in the source
+language, but it can be named as an alias target.
+
+This attribute requires assembler and object file support,
+and may not be available on all targets.
+
 @item externally_visible
 @cindex @code{externally_visible} function attribute
 This attribute, attached to a global variable or function, nullifies
@@ -6929,6 +6962,10 @@ align them on any target.
 The @code{aligned} attribute can also be used for functions
 (@pxref{Common Function Attributes}.)
 
+@item exalias ("@var{name}")
+@cindex @code{exalias} variable attribute
+See @pxref{Common Function Attributes}.
+
 @cindex @code{warn_if_not_aligned} variable attribute
 @item warn_if_not_aligned (@var{alignment})
 This attribute specifies a threshold for the structure field, measured
@@ -7067,6 +7104,21 @@ types (@pxref{Common Function Attributes},
 The message attached to the attribute is affected by the setting of
 the @option{-fmessage-length} option.
 
+@item exalias ("@var{name}")
+@cindex @code{exalias} type attribute
+The @code{exalias} attribute causes @var{name} to be emitted as an alias
+to the definition of the C++ Run-Time Type Information (RTTI)
+@code{std::type_info} object associated with the type.  For instance,
+
+@smallexample
+class foo __attribute__ ((__exalias__ ("TI_foo")));
+@end smallexample
+
+@noindent
+arranges for @samp{TI_foo} to be defined as an alias to the RTTI object
+for class @samp{foo}, once the class is defined and used in ways that
+cause its RTTI object to be synthesized and output.
+
 @item mode (@var{mode})
 @cindex @code{mode} variable attribute
 This attribute specifies the data type for the declaration---whichever
diff --git a/gcc/symtab.c b/gcc/symtab.c
index d7dfbb6..0ea8532 100644
--- a/gcc/symtab.c
+++ b/gcc/symtab.c
@@ -1874,6 +1874,42 @@ symtab_node::noninterposable_alias (symtab_node *node, void *data)
   return false;
 }
 
+/* Remap exalias nodes recorded as aliasing REPLACED to alias
+   REPLACEMENT instead.  */
+
+void
+symtab_node::remap_exalias_target (tree replaced, tree replacement)
+{
+  if (!decl_in_symtab_p (replacement)
+      || !symtab_node::get (replacement))
+    return;
+
+  FOR_EACH_EXALIAS (exalias, DECL_ATTRIBUTES (replaced))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (exalias));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      symtab_node *sym_node = symtab_node::get_for_asmname (id);
+
+      if (!sym_node)
+	{
+	  create_exalias_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-weak-1.c b/gcc/testsuite/c-c++-common/attr-weak-1.c
new file mode 100644
index 00000000..b11ef71
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/attr-weak-1.c
@@ -0,0 +1,19 @@
+/* { dg-do run { xfail *-*-* } } */
+/* { dg-require-effective-target weak_undefined } */
+
+/* C++ wouldn't combine attributes from local declarations with a
+   namespace-scoped symbol.  Now it does, but only if there is a
+   visible declaration.  */
+
+void foo () {
+  extern void __attribute__ ((__weak__)) undef_fn (void);
+  extern int __attribute__ ((__weak__)) undef_var;
+}
+
+int main () {
+  extern void undef_fn (void);
+  extern int undef_var;
+
+  if (&undef_fn || &undef_var)
+    __builtin_abort ();
+}
diff --git a/gcc/testsuite/c-c++-common/torture/attr-exalias-1.c b/gcc/testsuite/c-c++-common/torture/attr-exalias-1.c
new file mode 100644
index 00000000..3a471cc
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-exalias-1.c
@@ -0,0 +1,39 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+extern int var_a __attribute__ ((__exalias__ ("FOOVAR_A")));
+int var_a = 1;
+
+void foo_a () __attribute__ ((__exalias__ ("FOOBAR_A")));
+
+void
+foo_a ()
+{
+}
+
+
+int var_b;
+extern int var_b __attribute__ ((__exalias__ ("FOOVAR_B")));
+
+void
+foo_b ()
+{
+}
+
+void foo_b () __attribute__ ((__exalias__ ("FOOBAR_B")));
+
+
+int var_c __attribute__ ((__exalias__ ("FOOVAR_C")));
+
+void __attribute__ ((__exalias__ ("FOOBAR_C")))
+foo_c ()
+{
+}
+
+
+/* { dg-final { scan-assembler "FOOBAR_A" } } */
+/* { dg-final { scan-assembler "FOOVAR_A" } } */
+/* { dg-final { scan-assembler "FOOBAR_B" } } */
+/* { dg-final { scan-assembler "FOOVAR_B" } } */
+/* { dg-final { scan-assembler "FOOBAR_C" } } */
+/* { dg-final { scan-assembler "FOOVAR_C" } } */
diff --git a/gcc/testsuite/c-c++-common/torture/attr-exalias-2.c b/gcc/testsuite/c-c++-common/torture/attr-exalias-2.c
new file mode 100644
index 00000000..a0fe686
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-exalias-2.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+struct s
+{
+  int mem __attribute__ ((__exalias__ ("MEMFOO"))); /* { dg-warning "attribute ignored" } */
+};
+
+void foo()
+{
+  extern void bar () __attribute__ ((__exalias__ ("FOOBAR")));
+  int var __attribute__ ((__exalias__ ("FOOVAR"))); /* { dg-warning "attribute ignored" } */
+}
diff --git a/gcc/testsuite/c-c++-common/torture/attr-exalias-3.c b/gcc/testsuite/c-c++-common/torture/attr-exalias-3.c
new file mode 100644
index 00000000..d94b618
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-exalias-3.c
@@ -0,0 +1,41 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int var_a = 1;
+
+void
+foo_a ()
+{
+  extern int var_a __attribute__ ((__exalias__ ("FOOVAR_A")));
+  void foo_a () __attribute__ ((__exalias__ ("FOOBAR_A")));
+}
+
+#if __cplusplus
+/* Without this declaration before the local declaration below, the
+   attributes of the local declaration do not get propagated to the
+   (global) namespace scope.  */
+extern int var_b;
+#endif
+
+void
+foo_b ()
+{
+  extern int var_b __attribute__ ((__exalias__ ("FOOVAR_B")));
+}
+
+int var_b;
+
+void __attribute__ ((__exalias__ ("FOOBAR_C")))
+foo_c ()
+{
+  void foo_b () __attribute__ ((__exalias__ ("FOOBAR_B")));
+  /* Another exalias for var_b.  */
+  extern int var_b __attribute__ ((__exalias__ ("FOOVAR_C")));
+}
+
+/* { dg-final { scan-assembler "FOOBAR_A" } } */
+/* { dg-final { scan-assembler "FOOVAR_A" } } */
+/* { dg-final { scan-assembler "FOOBAR_B" } } */
+/* { dg-final { scan-assembler "FOOVAR_B" } } */
+/* { dg-final { scan-assembler "FOOBAR_C" } } */
+/* { dg-final { scan-assembler "FOOVAR_C" } } */
diff --git a/gcc/testsuite/c-c++-common/torture/attr-exalias-4.c b/gcc/testsuite/c-c++-common/torture/attr-exalias-4.c
new file mode 100644
index 00000000..6320d1a4
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-exalias-4.c
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-require-alias "" } */
+
+int var_a __attribute__ ((__exalias__ ("FOOVAR_A"))) = 42;
+
+int __attribute__ ((__exalias__ ("FOOBAR_A")))
+foo_a (int p)
+{
+  return p;
+}
+
+extern int __attribute__ ((__alias__ (("FOOVAR_A")))) var_b;
+extern int __attribute__ ((__alias__ (("FOOBAR_A")))) foo_b (int p);
+
+int
+foo_c ()
+{
+  return foo_b (var_b);
+}
+
+int
+main ()
+{
+  if (foo_c () != 42)
+    __builtin_abort ();
+
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/torture/attr-exalias-1.C b/gcc/testsuite/g++.dg/torture/attr-exalias-1.C
new file mode 100644
index 00000000..ac355f9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-exalias-1.C
@@ -0,0 +1,70 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+class __attribute__ ((__exalias__ ("FOOCLS_A"))) foo {
+  static int var __attribute__ ((__exalias__ ("FOOVAR_A")));
+  __attribute__ ((__exalias__ ("FOOCTR_A"))) foo ();
+  void __attribute__ ((__exalias__ ("FOOBAR_A"))) bar ();
+  virtual __attribute__ ((__exalias__ ("FOODTR_A"))) ~foo() {}
+};
+
+int foo::var = 1;
+
+foo::foo () {}
+
+void foo::bar () {}
+
+namespace b {
+  class __attribute__ ((__exalias__ ("FOOCLS_B"))) foo {
+    static int var __attribute__ ((__exalias__ ("FOOVAR_B")));
+    __attribute__ ((__exalias__ ("FOOCTR_B"))) foo ();
+    void __attribute__ ((__exalias__ ("FOOBAR_B"))) bar () {}
+    virtual __attribute__ ((__exalias__ ("FOODTR_B"))) ~foo() {}
+  };
+
+  int foo::var = 2;
+
+  foo::foo () {
+    void (foo::*pbar)() = &foo::bar;
+  }
+}
+
+namespace c {
+  namespace cd {
+    class __attribute__ ((__exalias__ ("FOOCLS_C"))) foo {
+      static int var __attribute__ ((__exalias__ ("FOOVAR_C")));
+      __attribute__ ((__exalias__ ("FOOCTR_C"))) foo () {
+	void (foo::*pbar)() = &foo::bar;
+      }
+      void __attribute__ ((__exalias__ ("FOOBAR_C"))) bar () {}
+      virtual __attribute__ ((__exalias__ ("FOODTR_C"))) ~foo() {}
+    };
+
+    int foo::var = 3;
+  }
+}
+
+/* { dg-final { scan-assembler "FOOCLS_A" } } */
+/* { dg-final { scan-assembler "FOOBAR_A" } } */
+/* { dg-final { scan-assembler "FOOCTR_A" } } */
+/* { dg-final { scan-assembler "FOOCTR_A_Base" } } */
+/* { dg-final { scan-assembler "FOODTR_A" } } */
+/* { dg-final { scan-assembler "FOODTR_A_Base" } } */
+/* { dg-final { scan-assembler "FOODTR_A_Del" } } */
+/* { dg-final { scan-assembler "FOOVAR_A" } } */
+/* { dg-final { scan-assembler "FOOCLS_B" } } */
+/* { dg-final { scan-assembler "FOOBAR_B" } } */
+/* { dg-final { scan-assembler "FOOCTR_B" } } */
+/* { dg-final { scan-assembler "FOOCTR_A_Base" } } */
+/* { dg-final { scan-assembler "FOODTR_B" } } */
+/* { dg-final { scan-assembler "FOODTR_B_Base" } } */
+/* { dg-final { scan-assembler "FOODTR_B_Del" } } */
+/* { dg-final { scan-assembler "FOOVAR_B" } } */
+/* { dg-final { scan-assembler "FOOCLS_C" } } */
+/* { dg-final { scan-assembler "FOOBAR_C" } } */
+/* { dg-final { scan-assembler "FOOCTR_C" } } */
+/* { dg-final { scan-assembler "FOOCTR_C_Base" } } */
+/* { dg-final { scan-assembler "FOODTR_C" } } */
+/* { dg-final { scan-assembler "FOODTR_C_Base" } } */
+/* { dg-final { scan-assembler "FOODTR_C_Del" } } */
+/* { dg-final { scan-assembler "FOOVAR_C" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-exalias-2.C b/gcc/testsuite/g++.dg/torture/attr-exalias-2.C
new file mode 100644
index 00000000..266ee0c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-exalias-2.C
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+namespace {
+  class __attribute__ ((__exalias__ ("FOOCLS_A"))) foo {
+    static int var __attribute__ ((__exalias__ ("FOOVAR_A")));
+    __attribute__ ((__exalias__ ("FOOCTR_A"))) foo ();
+    virtual __attribute__ ((__exalias__ ("FOODTR_A"))) ~foo ();
+    void __attribute__ ((__exalias__ ("FOOBAR_A"))) bar ();
+  };
+
+  int foo::var = 3;
+  foo::foo () {}
+  foo::~foo () {}
+  void foo::bar () {}
+}
+
+/* { dg-final { scan-assembler-not "\.globl" } } */
+/* { dg-final { scan-assembler "FOOCLS_A" } } */
+/* { dg-final { scan-assembler "FOOBAR_A" } } */
+/* { dg-final { scan-assembler "FOOCTR_A" } } */
+/* { dg-final { scan-assembler "FOOCTR_A_Base" } } */
+/* { dg-final { scan-assembler "FOODTR_A" } } */
+/* { dg-final { scan-assembler "FOODTR_A_Base" } } */
+/* { dg-final { scan-assembler "FOODTR_A_Del" } } */
+/* { dg-final { scan-assembler "FOOVAR_A" } } */
diff --git a/gcc/testsuite/g++.dg/torture/attr-exalias-3.C b/gcc/testsuite/g++.dg/torture/attr-exalias-3.C
new file mode 100644
index 00000000..a81348a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-exalias-3.C
@@ -0,0 +1,83 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+// exalias can be applied to template function explicit instantiations.
+
+template <typename T>
+void
+fn(T) {
+};
+
+template void __attribute__ ((__exalias__ ("FOOFUN_UINT"))) fn<>(unsigned int);
+template void __attribute__ ((__exalias__ ("FOOFUN_LONG"))) fn<>(long);
+
+template<> void __attribute__ ((__exalias__ ("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__ ((__exalias__ ("FOOCLS_CHAR_VIRT"))) foo<char>::virtfun();
+
+template class __attribute__ ((__exalias__ ("FOOCLS_CHAR_TI"))) foo<char>;
+
+// Though they're only output if the enclosing class is.
+template void
+__attribute__ ((__exalias__ ("FOOCLS_LONG_VIRT"))) foo<long>::virtfun();
+extern
+template class __attribute__ ((__exalias__ ("FOOCLS_LONG_TI_X"))) foo<long>;
+
+
+template void
+__attribute__ ((__exalias__ ("FOOCLS_VOID_ST"))) foo<void>::stfun();
+
+template class __attribute__ ((__exalias__ ("FOOCLS_VOID_TI"))) foo<>;
+
+
+extern
+template class __attribute__ ((__exalias__ ("FOOCLS_SHORT_TI_X"))) foo<short>;
+
+template void
+__attribute__ ((__exalias__ ("FOOCLS_SHORT_ST"))) foo<short>::stfun();
+template void
+__attribute__ ((__exalias__ ("FOOCLS_SHORT_INL"))) foo<short>::inlfun();
+
+template class __attribute__ ((__exalias__ ("FOOCLS_SHORT_TI_D"))) foo<short>;
+
+// Explicit specializations work too.
+
+template <>
+struct  __attribute__ ((__exalias__ ("FOOCLS_INT_TI")))
+foo<int>
+{
+  virtual ~foo() {}
+  virtual void __attribute__ ((__exalias__ ("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-exalias-4.C b/gcc/testsuite/g++.dg/torture/attr-exalias-4.C
new file mode 100644
index 00000000..9623bef
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-exalias-4.C
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+template <typename T = void>
+class
+__attribute__ ((__exalias__ ("FOOCLS")))
+foo // { dg-error "duplicate|already" }
+{
+  virtual ~foo() {}
+
+  template <typename U>
+  void
+    __attribute__ ((__exalias__ ("FOOTMF")))
+    tmemfun () {} // { dg-error "duplicate|already" }
+};
+
+template <typename T>
+void
+__attribute__ ((__exalias__ ("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/varpool.c b/gcc/varpool.c
index 458cdf1..5f89662 100644
--- a/gcc/varpool.c
+++ b/gcc/varpool.c
@@ -162,6 +162,9 @@ varpool_node::get_create (tree decl)
     }
 
   node->register_symbol ();
+
+  create_exalias_decls (decl);
+
   return node;
 }
 


-- 
Alexandre Oliva, happy hacker
https://FSFLA.org/blogs/lxo/
Free Software Activist
GNU Toolchain Engineer

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

* Re: [PATCH] introduce attribute exalias
  2020-08-07 17:38 ` [PATCH] " Alexandre Oliva
@ 2020-08-14 15:39   ` Alexandre Oliva
  2020-08-14 16:24     ` Nathan Sidwell
  2023-07-15  1:08   ` [PATCH v3] Introduce attribute reverse_alias Alexandre Oliva
  1 sibling, 1 reply; 35+ messages in thread
From: Alexandre Oliva @ 2020-08-14 15:39 UTC (permalink / raw)
  To: gcc-patches, jason, nathan, joseph; +Cc: hainque, ebotcazou

Ping?

In case there isn't immediate approval for the patch proper (I suppose
different parts will require review by different subsystem maintainers),
I'd appreciate at least community and language lawyers buy-in (or
turn-down) for the new feature hereby proposed for C-family languages,
namely, attribute exalias("symbol_name") as a means to have symbol_name
output as a same-linkage alias for functions, variables, and for C++
class types' RTTI symbols.

Thanks in advance,

On Aug  7, 2020, Alexandre Oliva <oliva@adacore.com> wrote:

> Since last week's patchlet, I've delayed the creation of the exalias
> decls, improved the merging of attributes, minimizing
> interface/visibility updates, found a better way to assign exaliases to
> nested explicit instantiations, even after enabling aliases to
> already-defined types, so now I'm reasonably happy with the patch.


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

> The attribute is usable in C and C++, presumably in all C-family
> languages.  It can be attached to global variables and functions.  In
> C++, it can also be attached to namespace-scoped variables and
> functions, static data members, member functions, explicit
> instantiations and specializations of template functions, members and
> classes.  When applied to constructors or destructor, additional
> exaliases with _Base and _Del suffixes are defined for variants other
> than complete-object ones.

> 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.


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


> Regstrapped on x86_64-linux-gnu.  Ok to install?


> (The newly-introduced attr-weak-1.c passes in C, but is marked as XFAIL
> for C++, so it gets an XPASS in C; I could move it to some C++-only
> subtree, or drop it altogether and file a PR instead)

> for  gcc/ChangeLog

> 	* attribs.c: Include cgraph.h.
> 	(decl_attributes): Allow late introduction of exalias in
> 	types.
> 	(create_exalias_decl, create_exalias_decls): New.
> 	* attribs.h: Declare them.
> 	(FOR_EACH_EXALIAS): New macro.
> 	* cgraph.c (cgraph_node::create): Create exalias decls.
> 	* varpool.c (varpool_node::get_create): Create exalias decls.
> 	* cgraph.h (symtab_node::remap_exalias_target): New.
> 	* symtab.c (symtab_node::remap_exalias_target): Define.
> 	* cgraphunit.c (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 (exalias): Document for variables, functions
> 	and types.

> for  gcc/ada/ChangeLog

> 	* doc/gnat_rm/interfacing_to_other_languages.rst: Mention
> 	attribute exalias to give RTTI symbols mnemonic names.
> 	* doc/gnat_ugn/the_gnat_compilation_model.rst: Mention
> 	attribute exalias.  Fix incorrect ref to C1 ctor variant.

> for  gcc/c-family/ChangeLog

> 	* c-ada-spec.c (pp_asm_name): Use first exalias if available.
> 	* c-attribs.c (handle_exalias_attribute): New.
> 	(c_common_attribute_table): Add exalias.
> 	(handle_copy_attribute): Do not copy exalias.
> 	* c-decl.c (duplicate_decls): Remap exalias target.

> for  gcc/cp/ChangeLog

> 	* class.c (copy_fndecl_with_name): Move/adjust exalias to
> 	cdtor variants.
> 	(build_cdtor_clones): Drop exalias from primary variant.
> 	* cp-tree.h (update_exalias_interface, update_tinfo_exalias):
> 	Declare.
> 	* decl.c (duplicate_decls): Remap exalias target.
> 	(grokfndecl): Tentatively create exalias decls after adding
> 	attributes in e.g. a template member function explicit
> 	instantiation.
> 	* decl2.c (cplus_decl_attributes): Update tinfo exalias.
> 	(copy_interface, update_exalias_interface): New.
> 	(determine_visibility): Update exalias interface.
> 	(tentative_decl_linkage, import_export_decl): Likewise.
> 	* name-lookup.c: Include target.h and cgraph.h.
> 	(set_local_extern_decl_linkage): Merge attributes with a
> 	namespace-scoped decl if one is found.  Remap exalias
> 	targets, and drop exaliases from the local decl.
> 	* optimize.c (maybe_clone_body): Only copy attributes if they
> 	haven't been copied yet.  Update exalias interface.
> 	* rtti.c: Include attribs.h and cgraph.h.
> 	(get_tinfo_decl): Copy exalias attributes from type to tinfo
> 	decl.  Create exalias decls.
> 	(update_tinfo_exalias): New.

> for  gcc/testsuite/ChangeLog

> 	* c-c++-common/attr-weak-1.c: New, xfailed.
> 	* c-c++-common/torture/attr-exalias-1.c: New.
> 	* c-c++-common/torture/attr-exalias-2.c: New.
> 	* c-c++-common/torture/attr-exalias-3.c: New.
> 	* c-c++-common/torture/attr-exalias-4.c: New.
> 	* g++.dg/torture/attr-exalias-1.C: New.
> 	* g++.dg/torture/attr-exalias-2.C: New.
> 	* g++.dg/torture/attr-exalias-3.C: New.
> 	* g++.dg/torture/attr-exalias-4.C: New.

https://gcc.gnu.org/pipermail/gcc-patches/2020-August/551614.html

-- 
Alexandre Oliva, happy hacker
https://FSFLA.org/blogs/lxo/
Free Software Activist
GNU Toolchain Engineer

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

* Re: [PATCH] introduce attribute exalias
  2020-08-14 15:39   ` Alexandre Oliva
@ 2020-08-14 16:24     ` Nathan Sidwell
  2020-08-14 19:24       ` Alexandre Oliva
  0 siblings, 1 reply; 35+ messages in thread
From: Nathan Sidwell @ 2020-08-14 16:24 UTC (permalink / raw)
  To: Alexandre Oliva, gcc-patches, jason, joseph; +Cc: hainque, ebotcazou

On 8/14/20 11:39 AM, Alexandre Oliva wrote:
> Ping?
> 
> In case there isn't immediate approval for the patch proper (I suppose
> different parts will require review by different subsystem maintainers),
> I'd appreciate at least community and language lawyers buy-in (or
> turn-down) for the new feature hereby proposed for C-family languages,
> namely, attribute exalias("symbol_name") as a means to have symbol_name
> output as a same-linkage alias for functions, variables, and for C++
> class types' RTTI symbols.

This seems a useful feature.  I don;t think it needs language lawyering 
-- it's an extension, right?  By 'same-linkage', do you mean same 
linkage as the *symbol* of the thing it is aliasing, or same linkage as
the language entity it is aliasing?  I suspect you mean the former.

I'm sure we can bikeshed the name 'exalias' doesn't seem very mnemonic 
to me.  'symbol_alias' or something?

nathan
> 
> Thanks in advance,
> 
> On Aug  7, 2020, Alexandre Oliva <oliva@adacore.com> wrote:
> 
>> Since last week's patchlet, I've delayed the creation of the exalias
>> decls, improved the merging of attributes, minimizing
>> interface/visibility updates, found a better way to assign exaliases to
>> nested explicit instantiations, even after enabling aliases to
>> already-defined types, so now I'm reasonably happy with the patch.
> 
> 
>> This patch introduces an attribute to add extra aliases to a symbol
>> when its definition is output.  The main goal is to ease interfacing
>> C++ with Ada, as C++ mangled names have to be named, and in some cases
>> (e.g. when using stdint.h typedefs in function arguments) the symbol
>> names may vary across platforms.
> 
>> The attribute is usable in C and C++, presumably in all C-family
>> languages.  It can be attached to global variables and functions.  In
>> C++, it can also be attached to namespace-scoped variables and
>> functions, static data members, member functions, explicit
>> instantiations and specializations of template functions, members and
>> classes.  When applied to constructors or destructor, additional
>> exaliases with _Base and _Del suffixes are defined for variants other
>> than complete-object ones.
> 
>> 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.
> 
> 
>> While working on this, I noticed C++ didn't merge attributes of extern
>> local declarations with those of the namespace-scoped declaration.
>> I've added code to merge the attributes if there is a namespace-scoped
>> declaration, but if there isn't one, there won't be any merging, and
>> the effects are noticeable, as in the added attr-weak-1.C.  I'm also
>> slightly concerned that an earlier local decl would go out of sync if
>> a subsequent local decl, say within the same or even in another
>> function, introduces additional attributes in the global decl.
> 
> 
>> Regstrapped on x86_64-linux-gnu.  Ok to install?
> 
> 
>> (The newly-introduced attr-weak-1.c passes in C, but is marked as XFAIL
>> for C++, so it gets an XPASS in C; I could move it to some C++-only
>> subtree, or drop it altogether and file a PR instead)
> 
>> for  gcc/ChangeLog
> 
>> 	* attribs.c: Include cgraph.h.
>> 	(decl_attributes): Allow late introduction of exalias in
>> 	types.
>> 	(create_exalias_decl, create_exalias_decls): New.
>> 	* attribs.h: Declare them.
>> 	(FOR_EACH_EXALIAS): New macro.
>> 	* cgraph.c (cgraph_node::create): Create exalias decls.
>> 	* varpool.c (varpool_node::get_create): Create exalias decls.
>> 	* cgraph.h (symtab_node::remap_exalias_target): New.
>> 	* symtab.c (symtab_node::remap_exalias_target): Define.
>> 	* cgraphunit.c (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 (exalias): Document for variables, functions
>> 	and types.
> 
>> for  gcc/ada/ChangeLog
> 
>> 	* doc/gnat_rm/interfacing_to_other_languages.rst: Mention
>> 	attribute exalias to give RTTI symbols mnemonic names.
>> 	* doc/gnat_ugn/the_gnat_compilation_model.rst: Mention
>> 	attribute exalias.  Fix incorrect ref to C1 ctor variant.
> 
>> for  gcc/c-family/ChangeLog
> 
>> 	* c-ada-spec.c (pp_asm_name): Use first exalias if available.
>> 	* c-attribs.c (handle_exalias_attribute): New.
>> 	(c_common_attribute_table): Add exalias.
>> 	(handle_copy_attribute): Do not copy exalias.
>> 	* c-decl.c (duplicate_decls): Remap exalias target.
> 
>> for  gcc/cp/ChangeLog
> 
>> 	* class.c (copy_fndecl_with_name): Move/adjust exalias to
>> 	cdtor variants.
>> 	(build_cdtor_clones): Drop exalias from primary variant.
>> 	* cp-tree.h (update_exalias_interface, update_tinfo_exalias):
>> 	Declare.
>> 	* decl.c (duplicate_decls): Remap exalias target.
>> 	(grokfndecl): Tentatively create exalias decls after adding
>> 	attributes in e.g. a template member function explicit
>> 	instantiation.
>> 	* decl2.c (cplus_decl_attributes): Update tinfo exalias.
>> 	(copy_interface, update_exalias_interface): New.
>> 	(determine_visibility): Update exalias interface.
>> 	(tentative_decl_linkage, import_export_decl): Likewise.
>> 	* name-lookup.c: Include target.h and cgraph.h.
>> 	(set_local_extern_decl_linkage): Merge attributes with a
>> 	namespace-scoped decl if one is found.  Remap exalias
>> 	targets, and drop exaliases from the local decl.
>> 	* optimize.c (maybe_clone_body): Only copy attributes if they
>> 	haven't been copied yet.  Update exalias interface.
>> 	* rtti.c: Include attribs.h and cgraph.h.
>> 	(get_tinfo_decl): Copy exalias attributes from type to tinfo
>> 	decl.  Create exalias decls.
>> 	(update_tinfo_exalias): New.
> 
>> for  gcc/testsuite/ChangeLog
> 
>> 	* c-c++-common/attr-weak-1.c: New, xfailed.
>> 	* c-c++-common/torture/attr-exalias-1.c: New.
>> 	* c-c++-common/torture/attr-exalias-2.c: New.
>> 	* c-c++-common/torture/attr-exalias-3.c: New.
>> 	* c-c++-common/torture/attr-exalias-4.c: New.
>> 	* g++.dg/torture/attr-exalias-1.C: New.
>> 	* g++.dg/torture/attr-exalias-2.C: New.
>> 	* g++.dg/torture/attr-exalias-3.C: New.
>> 	* g++.dg/torture/attr-exalias-4.C: New.
> 
> https://gcc.gnu.org/pipermail/gcc-patches/2020-August/551614.html
> 


-- 
Nathan Sidwell

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

* Re: [PATCH] introduce attribute exalias
  2020-08-14 16:24     ` Nathan Sidwell
@ 2020-08-14 19:24       ` Alexandre Oliva
  2020-08-14 22:12         ` Nathan Sidwell
  0 siblings, 1 reply; 35+ messages in thread
From: Alexandre Oliva @ 2020-08-14 19:24 UTC (permalink / raw)
  To: Nathan Sidwell; +Cc: gcc-patches, jason, joseph, ebotcazou

On Aug 14, 2020, Nathan Sidwell <nathan@acm.org> wrote:

> This seems a useful feature.  I don;t think it needs language
> lawyering -- it's an extension, right?

Well, yeah, but I think it's usually good for even extensions to be
sound language-wise.

> By 'same-linkage', do you mean same linkage as the *symbol* of the
> thing it is aliasing, or same linkage as the language entity it is
> aliasing?
> I suspect you mean the former.

Yeah, ultimately the symbol declared as exalias gets the same
object-level linkage and visibility properties as those of the primary
symbol emitted for the language entity.  Conceptually, the entity
introduced by the attribute is not even visible or accessible in the
standard language; it can only be referenced by alias attributes and by
Ada import declarations, but conceptually, in as much as you conceive of
it as a separate entity, I suppose it makes some sense to say it gets
the same linkage as the entity it refers to.

> I'm sure we can bikeshed the name 'exalias' doesn't seem very mnemonic
> to me.  'symbol_alias' or something?

I don't like symbol_alias; that this feature names a symbol is not a
distinguishing feature from the preexisting alias attribute.

'ex' can be read as both extra, exported, external, and all of these
sort of make sense, at least for entities that have linkage.

Even for exclusively internal uses, say to introduce a mnemonic symbol
for another alias-attributed declaration to refer to, the "ex" prefix,
that means the opposite of "in", fitting in well with the functionality
of "ex"posing the symbol through a name that other alias declarations
can take *in*, *im*port.

Another possible spelling for this proposed attribute that might be more
mnemonic is "aka"; unfortunately, that's pretty much a synonym to alias,
so it might be mistaken as such, rather than as a complementary feature,
akin to the other end of a power extension cable: whereas alias does the
job of a plug, *ex*alias provides a socket/*out*let.

-- 
Alexandre Oliva, happy hacker
https://FSFLA.org/blogs/lxo/
Free Software Activist
GNU Toolchain Engineer

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

* Re: [PATCH] introduce attribute exalias
  2020-08-14 19:24       ` Alexandre Oliva
@ 2020-08-14 22:12         ` Nathan Sidwell
  2020-08-15  2:43           ` Alexandre Oliva
  0 siblings, 1 reply; 35+ messages in thread
From: Nathan Sidwell @ 2020-08-14 22:12 UTC (permalink / raw)
  To: Alexandre Oliva; +Cc: gcc-patches, jason, joseph, ebotcazou

On 8/14/20 3:24 PM, Alexandre Oliva wrote:
> On Aug 14, 2020, Nathan Sidwell <nathan@acm.org> wrote:
> 
>> This seems a useful feature.  I don;t think it needs language
>> lawyering -- it's an extension, right?
> 
> Well, yeah, but I think it's usually good for even extensions to be
> sound language-wise.
> 
>> By 'same-linkage', do you mean same linkage as the *symbol* of the
>> thing it is aliasing, or same linkage as the language entity it is
>> aliasing?
>> I suspect you mean the former.
> 
> Yeah, ultimately the symbol declared as exalias gets the same
> object-level linkage and visibility properties as those of the primary
> symbol emitted for the language entity.  Conceptually, the entity
> introduced by the attribute is not even visible or accessible in the
> standard language; it can only be referenced by alias attributes and by
> Ada import declarations, but conceptually, in as much as you conceive of
> it as a separate entity, I suppose it makes some sense to say it gets
> the same linkage as the entity it refers to.

thanks for the discussion.  I should have said, 'exalias' sounds either 
like a used-to-be alias, it is an ex alias, it has ceased to be, gone to 
join the choir invisible.

or it sounds like exa-lias, making me wonder what a 'lia' is, and why I 
want 10^18 of them

>> I'm sure we can bikeshed the name 'exalias' doesn't seem very mnemonic
>> to me.  'symbol_alias' or something?
> 
> I don't like symbol_alias; that this feature names a symbol is not a
> distinguishing feature from the preexisting alias attribute.

right,  I realize this is different to the existing alias.  It's always 
struck me that the existing semantics are not c++ friendly.  Perhaps 
alias is not the right name at all.  You're emitting an alternative 
symbol, for use in interfacing to a foreign language/system.  Perhaps 
'xenoname'?

> 
> 'ex' can be read as both extra, exported, external, and all of these
> sort of make sense, at least for entities that have linkage.
> 
> Even for exclusively internal uses, say to introduce a mnemonic symbol
> for another alias-attributed declaration to refer to, the "ex" prefix,
> that means the opposite of "in", fitting in well with the functionality
> of "ex"posing the symbol through a name that other alias declarations
> can take *in*, *im*port.
> 
> Another possible spelling for this proposed attribute that might be more
> mnemonic is "aka"; unfortunately, that's pretty much a synonym to alias,
> so it might be mistaken as such, rather than as a complementary feature,
> akin to the other end of a power extension cable: whereas alias does the
> job of a plug, *ex*alias provides a socket/*out*let.
> 

nathan


-- 
Nathan Sidwell

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

* Re: [PATCH] introduce attribute exalias
  2020-08-14 22:12         ` Nathan Sidwell
@ 2020-08-15  2:43           ` Alexandre Oliva
  2020-08-15  9:22             ` Iain Sandoe
  2020-08-15 21:11             ` Nathan Sidwell
  0 siblings, 2 replies; 35+ messages in thread
From: Alexandre Oliva @ 2020-08-15  2:43 UTC (permalink / raw)
  To: Nathan Sidwell; +Cc: gcc-patches, jason, joseph, ebotcazou

On Aug 14, 2020, Nathan Sidwell <nathan@acm.org> wrote:

> 'exalias' sounds either like a used-to-be alias

*nod*

> or it sounds like exa-lias, making me wonder what a 'lia' is, and why
> I want 10^18 of them

heh

>>> I'm sure we can bikeshed the name 'exalias' doesn't seem very mnemonic
>>> to me.  'symbol_alias' or something?

>> I don't like symbol_alias; that this feature names a symbol is not a
>> distinguishing feature from the preexisting alias attribute.

> right,  I realize this is different to the existing alias.

The point was that the existing alias already takes a symbol name.

> It's always struck me that the existing semantics are not c++
> friendly.

Indeed, avoiding the need for using mangled symbol names to refer to
language entities is the very issue I've set out to solve.  It helps
with aliases in C++ as much as it helps with imports in Ada.

> Perhaps alias is not the right name at all.

I kind of like the explicit present of "alias" because, well, what we
get is an alias, to the point that, if asm aliases aren't available, it
won't work.  And, if they are, you can use the so-assigned name as an
alias target, so it's a good thing if they're typographically related.

One could even argue that this new attribute is more deserving of the
term alias than the existing one, and that the existing one should be
renamed to "aliased_to" or so.  But I'm not seriously suggesting us to
rename a long-available attribute while assigning uses thereof a
different semantics, that would be preposterous.

Since you don't seem to have liked 'aka' either, how about 'nickname',
or 'nicknamed'?  A more convenient name to refer to an entity is exactly
what this is about, eh?

-- 
Alexandre Oliva, happy hacker
https://FSFLA.org/blogs/lxo/
Free Software Activist
GNU Toolchain Engineer

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

* Re: [PATCH] introduce attribute exalias
  2020-08-15  2:43           ` Alexandre Oliva
@ 2020-08-15  9:22             ` Iain Sandoe
  2020-08-15 16:39               ` Alexandre Oliva
  2020-08-15 17:26               ` Alexandre Oliva
  2020-08-15 21:11             ` Nathan Sidwell
  1 sibling, 2 replies; 35+ messages in thread
From: Iain Sandoe @ 2020-08-15  9:22 UTC (permalink / raw)
  To: Alexandre Oliva; +Cc: Nathan Sidwell, Eric Botcazou, gcc-patches, joseph

Hi Alexandre,

I built the patch on x86_64-linux and darwin*** (fwiw).

* It’s firmly agreed that there are times when referring to C++ mangled names
   is less than ideal.

* IIUC, the objective is to have a short-hand way of annotating an export  
from
  C++ so that it’s (a) more human-readable and (b) independent of any platform
  variation in mangling of types - in the Ada import pragma?

I see Nathan commented that there are no language-lawyering implications,
which is good.

However, there do seem to be both ABI and engineering implications:

  * if the target ABI does not support symbol aliases, then this facility cannot
    be used.  Which either means that you cannot rely on this facility (and thus
    make things generically easier in the Ada implementation on GCC) or you
    will exclude the GCC targets without symbol aliases from Ada.  The latter
    would make me sad as Darwin maintainer.

  * the symbol table will grow (maybe one doesn’t care if there are not many).

  * The process shifts the onus on representation to the exporter and thus there
    can now be 3 library vendors who all thought “MY_FOO_FUNC” was the
    best representation for an export - these will now clash in the “shorthand”
    namespace, although their C++ mangling might well not.

  * it’s not universally usable without “rebuilding the world” and having access to
    source for everything you might want to import (maybe not important, depends
    on whether there are users of Ada who depend on closed source libraries).

  * what happens for templates and overloads - presumably the Ada import has
    add the relevant (albeit abbreviated) decorations?

  * One can’t have an arbitrary re-name; it has to be supported by the target
    assembler (not that this is a new constraint, but it prevents the exported
    name from being an exact representation of the human-readable C++ interface
    in general).

—— are there other possibilites to solve the underlying issue?

  C++ mangled names have some proven good properties:

  * they convey all the relevant information about the interface
  * they are standardized, and work between implementations from different
   ‘vendors’ or OSS compilers on the same platform.
  * they are not going to clash.

The compiler already has a proven implementation of C++ mangling rules.

what about annotating the import pragma in some way such that the platform
mangling is applied by the compiler?

thus, in my example below, so long as the interface is represented  
correctly by the
string after “itanium:” it will mangle correctly for the target  (even if  
that mangling would
alter between targets for representation of some types).

I suppose there’s a question of how many pragmas would be needed to annotate
sufficiently to get the mangling right?

        pragma CPP_Constructor (New_Animal);
        pragma Import (CPP, New_Animal, itanium:”Animal()");

It seems one ideally wants the Ada moral equivalent of:

extern “C++” {
   things we want to import.
}

which automagically does the right interface transforms and synthesizes the
Ada interfaces required.

Alexandre Oliva <oliva@adacore.com> wrote:

> On Aug 14, 2020, Nathan Sidwell <nathan@acm.org> wrote:
>
>>
>> Perhaps alias is not the right name at all.
>
> I kind of like the explicit present of "alias" because, well, what we
> get is an alias, to the point that, if asm aliases aren't available, it
> won't work.  And, if they are, you can use the so-assigned name as an
> alias target, so it's a good thing if they're typographically related.
>
> One could even argue that this new attribute is more deserving of the
> term alias than the existing one, and that the existing one should be
> renamed to "aliased_to" or so.  But I'm not seriously suggesting us to
> rename a long-available attribute while assigning uses thereof a
> different semantics, that would be preposterous.
>
> Since you don't seem to have liked 'aka' either, how about 'nickname',
> or 'nicknamed'?  A more convenient name to refer to an entity is exactly
> what this is about, eh?

.. assuming this facility was added ...
.. my 0.02GBP contribution the the bikeshed painting fund would be….

it’s nice when an attribute reads in the source to tell you its purpose.
so how about:

  “export_as” or “exported_as”
(depending on whether one regards this as a command to the compiler,
   or an annotation).

so :
         __attribute__ ((__export_as__ ("Ctor_For_Animal"))) // extra alias
         Animal() {Age_Count = 0;};

or:
         __attribute__ ((__exported_as__ ("Ctor_For_Animal"))) // extra alias
         Animal() {Age_Count = 0;};

thanks
Iain

*** right now Darwin fails silently (there doesn’t seem to be the usual error
  that the target doesn’t support that kind of alias).


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

* Re: [PATCH] introduce attribute exalias
  2020-08-15  9:22             ` Iain Sandoe
@ 2020-08-15 16:39               ` Alexandre Oliva
  2020-08-15 18:17                 ` Iain Sandoe
  2020-08-15 17:26               ` Alexandre Oliva
  1 sibling, 1 reply; 35+ messages in thread
From: Alexandre Oliva @ 2020-08-15 16:39 UTC (permalink / raw)
  To: Iain Sandoe; +Cc: Nathan Sidwell, Eric Botcazou, gcc-patches, joseph

On Aug 15, 2020, Iain Sandoe <iain@sandoe.co.uk> wrote:

>  * if the target ABI does not support symbol aliases, then this facility cannot
>    be used.

True.  I'm surprised there are modern platforms that don't.

What is it that stands in the way?  Lack of support for .set in the
assembler?  If that's the case, couldn't it possibly be worked around by
setting multiple global labels at the same spot?  I'm pretty sure
setting multiple labels at the same address is used and relied on quite
often.

>    will exclude the GCC targets without symbol aliases from Ada.

It's not so dire.  Developers for alias-deprived systems would have to
use the mangled names instead.  That would be a little painful, but not
even close to making the language unavailable.

>  * The process shifts the onus on representation to the exporter and thus there
>    can now be 3 library vendors who all thought “MY_FOO_FUNC” was the
>    best representation for an export - these will now clash in the “shorthand”
>    namespace, although their C++ mangling might well not.

Using this to disqualify the new feature would also disqualify regular
aliases, that could be used for just the same purpose of making symbols
available under chosen names:

  extern "C" typeof(foo::func)
  __attribute__((__alias__("<mangling for foo::func>")))
  MY_FOO_FUNC;

Now, this concern appears to be focused on binary-only libraries.  Since
we haven't seen vendors rush to make their library internals available
under shorter aliases, polluting the symbolic namespace, I see little
reason for concern about this possibility.

When it comes to binary-only libraries, the ABI is often set in stone,
and it's up to users to figure out the symbol names in the ABI and use
them.  If they vary across target platforms, that's inconvenient, but
nothing new.

I expect this feature to be used and useful within multi-language
projects, particularly when using, as part of their interfaces,
shorthand typedefs whose encoding varies depending on the platform.
E.g., consider a function or a template instantiation that takes a
int64_t parameter.  Depending on whether int64_t maps to long or long
long, you get different encodings, thus references using the symbol name
have to be adjusted depending on what type stdint.h maps int64_t to.

>  * it’s not universally usable without “rebuilding the world” and having access to
>    source for everything you might want to import

You mean it does not bring improvements to situations in which you can't
introduce nicknames for third-party symbols.  You then figure out and
import the mangled names and move on.

>  * what happens for templates and overloads - presumably the Ada import has
>    add the relevant (albeit abbreviated) decorations?

They don't matter to the proposed design.  The reason they come up for
you is that you have a completely different solution in mind that
requires this kind of resolution.  The one I'm proposing attaches the
extra aliases directly to the target language entity, be it one of the
overloads of a member function, be it a specialization of a template
function.

>  * One can’t have an arbitrary re-name; it has to be supported by the target
>    assembler (not that this is a new constraint, but it prevents the exported
>    name from being an exact representation of the human-readable C++ interface
>    in general).

*nod*.  It couldn't be a target for alias attributes otherwise, and I
found that a desirable property to make the new feature useful even for
standalone C++.

> —— are there other possibilites to solve the underlying issue?

>  C++ mangled names have some proven good properties:

>  * they convey all the relevant information about the interface
>  * they are standardized, and work between implementations from different
>   ‘vendors’ or OSS compilers on the same platform.
>  * they are not going to clash.

* they require so much symbolic information that in order to perform
mangling you pretty much have to #include all of the relevant C++
headers.

Consider typedefs, templates with partial or explicit specializations,
default template arguments, besides the possibility of varying
definitions across platforms.

> what about annotating the import pragma in some way such that the platform
> mangling is applied by the compiler?

That would indeed be desirable, but it is unfortunately not viable.

Consider you have to figure out the correct mangling for this:

  foo::bar<std::iostream&, std::string, u64, g::h>::f

Is foo a namespace, a canonical class name, or a typedef?  (maybe even a
using declaration, or even something brought into the global namespace
by a using directive)

If it's a typedef, what's the canonical name?  (it could be a template
instantiation)

bar is clearly a template type, so you "just" need enough symbolic
information to be able to mangle std::iostream&, std::string, u64,
g::h.

For u64, you just have to look at stdint.h to see which of the
C++-defined types is maps to.

g::h is a mystery.  It could be a type, a function, a member function, a
variable, a data member...

There could be any subsequent template parameters using defaults.  Even
if we were to simplify this (and make it inconvenient for the user)
requiring no defaults to be relied on, that wouldn't get you much
farther.

Finally, we get to f.  We can assume it's not a template type, nor a
template member function, so it would have to be (assuming a well-formed
symbolic reference) a static or non-static data member, a static or
non-static member function, a typedef, a nested type; possibly a
template instantiation, using defaults?).

It could even name an overload set, so you might want to require a
parameter list, and an explicit template parameter list.


We could then require all of this symbolic information, in the form of
type, variable and function declarations, including partial and explicit
specializations, and the transitive closure thereof.

How would you get all of this symbolic information to the compiler of
the Ada unit importing such C++ symbols?  Parsing C++ headers?
Requiring users to bring the relevant declarations into the Ada program?
Searching the symbol tables of object files that would have to have
alreayd been compiled, looking for a match?  None of these are good.

Extracting symbolic information from C++ headers (à-la -fdump-ada-spec,
but with a *lot* more symbolic info) in a first pass, and referencing
that in your imports.  Viable, a *lot* of work, and also a big doh!
-fdump-ada-spec would already get you the Imports with the mangled
names.

This feature is for when you don't want to go through all this trouble,
and would rather reference a few C++ symbols directly in your
target-independent sources.

> it’s nice when an attribute reads in the source to tell you its purpose.

*exporting* is heavily overloaded, so I've been avoiding it.  One thing
the proposed feature does NOT do is to make the alias more visible than
the original symbol.  If it's internal, hidden or protected, or even if
it's local, it won't get exported.  You could use it, and then another
alias declaration referencing it, to get it exported, if you wish, but
that was enough of a reason for me to stay away from the term "export".

> *** right now Darwin fails silently (there doesn’t seem to be the usual error
>  that the target doesn’t support that kind of alias).

Hmm, thanks, I will make sure there's some more verbose failure mode if
we can't find a way for something akin to an alias to be usable there.

-- 
Alexandre Oliva, happy hacker
https://FSFLA.org/blogs/lxo/
Free Software Activist
GNU Toolchain Engineer

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

* Re: [PATCH] introduce attribute exalias
  2020-08-15  9:22             ` Iain Sandoe
  2020-08-15 16:39               ` Alexandre Oliva
@ 2020-08-15 17:26               ` Alexandre Oliva
  1 sibling, 0 replies; 35+ messages in thread
From: Alexandre Oliva @ 2020-08-15 17:26 UTC (permalink / raw)
  To: Iain Sandoe; +Cc: Nathan Sidwell, Eric Botcazou, gcc-patches, joseph

On Aug 15, 2020, Iain Sandoe <iain@sandoe.co.uk> wrote:

> what about annotating the import pragma in some way such that the platform
> mangling is applied by the compiler?

Oh, one more thing about this.

Requiring all names to be given in canonical form might alleviate some
of the problems I raised, since it would eliminate typedefs and using
declarations and directives from consideration.  We'd still have other
unsurmountable problems to deal with, but more importantly, you wouldn't
be able to use u64 any more, you'd have to resolve it to the type that
u64 is mapped to, at which point you'd be bringing back the very
variation across targets that this feature was designed to overcome.

-- 
Alexandre Oliva, happy hacker
https://FSFLA.org/blogs/lxo/
Free Software Activist
GNU Toolchain Engineer

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

* Re: [PATCH] introduce attribute exalias
  2020-08-15 16:39               ` Alexandre Oliva
@ 2020-08-15 18:17                 ` Iain Sandoe
  2020-08-25  8:34                   ` Alexandre Oliva
  0 siblings, 1 reply; 35+ messages in thread
From: Iain Sandoe @ 2020-08-15 18:17 UTC (permalink / raw)
  To: Alexandre Oliva
  Cc: Eric Botcazou, Andrew MacLeod via Gcc-patches, Nathan Sidwell, joseph

HI Alexandre

I don’t want to derail the discussion - but FIO mostly….

Alexandre Oliva <oliva@adacore.com> wrote:

> On Aug 15, 2020, Iain Sandoe <iain@sandoe.co.uk> wrote:
>
>> * if the target ABI does not support symbol aliases, then this facility  
>> cannot
>>   be used.
>
> True.  I'm surprised there are modern platforms that don’t.

different platforms have different designs - it’s not an “old c.f
new” thing - see below.

> What is it that stands in the way?  Lack of support for .set in the
> assembler?
>  If that's the case, couldn't it possibly be worked around by
> setting multiple global labels at the same spot?  I'm pretty sure
> setting multiple labels at the same address is used and relied on quite
> often.

That’s what’s currently disallowed (the assemblers all support .set).

Long ago (before my time with GCC) Darwin’s toolchains did support
aliases.

The withdrawal was not an accident, but a design choice - where a linker
model based on “atoms” was chosen (which requires [as things stand]
public symbols to have distinct addresses).  I can point you at a description
of the linker optimisation if you’re interested.

IMO, the atom model can be modified to allow aliases (it might be even
that the linker constraint has been relaxed already).

However, it’s not my call - I’ve suggested to the platform toolchain team  
it’s
a good idea, but it doesn’t seem to block any other toolchain than GCC
so not sure what priority would be assigned.

For function aliases, I think there’s a simple work-around and it’s just a
question of time for me to make a patch etc.

for general aliases to public symbols including data, not so easy.

>>   will exclude the GCC targets without symbol aliases from Ada.
>
> It's not so dire.  Developers for alias-deprived systems would have to
> use the mangled names instead.  That would be a little painful, but not
> even close to making the language unavailable.

Well the predicate was that the use of the mechanism was mandatory, if
the existing scheme continues of course there’s no issue.

>> * The process shifts the onus on representation to the exporter and thus  
>> there
>>   can now be 3 library vendors who all thought “MY_FOO_FUNC” was the
>>   best representation for an export - these will now clash in the “shorthand”
>>   namespace, although their C++ mangling might well not.
>
> Using this to disqualify the new feature would also disqualify regular
> aliases, that could be used for just the same purpose of making symbols
> available under chosen names:

It wasn’t a comment against the feature - but a comment about shifting the
onus for export information onto the producers (and the fact that one can’t
generally control what they choose to provide in the absence of a  
specification
- which itanium mangling is).

>> * what happens for templates and overloads - presumably the Ada import has
>>   add the relevant (albeit abbreviated) decorations?
>
> They don't matter to the proposed design.  The reason they come up for
> you is that you have a completely different solution in mind that
> requires this kind of resolution.  The one I'm proposing attaches the
> extra aliases directly to the target language entity, be it one of the
> overloads of a member function, be it a specialization of a template
> function.

Actually, I was thinking about folks who like template metaprogramming
(not personally a fan) - and how they would arrange to get automatic
export information to track that meta-progamming.

Solved if one were able to import the interface….

>> —— are there other possibilites to solve the underlying issue?
>
>> C++ mangled names have some proven good properties:
>
>> * they convey all the relevant information about the interface
>> * they are standardized, and work between implementations from different
>>  ‘vendors’ or OSS compilers on the same platform.
>> * they are not going to clash.
>
> * they require so much symbolic information that in order to perform
> mangling you pretty much have to #include all of the relevant C++
> headers.
>
> Consider typedefs, templates with partial or explicit specializations,
> default template arguments, besides the possibility of varying
> definitions across platforms.
>
>> what about annotating the import pragma in some way such that the platform
>> mangling is applied by the compiler?
>
> That would indeed be desirable, but it is unfortunately not viable.

<snipped explanation>

I see.

Thinking aloud  - not thought through in any detail - I wonder if the  
facilities of
C++20 modules are sufficient?

>> *** right now Darwin fails silently (there doesn’t seem to be the usual  
>> error
>> that the target doesn’t support that kind of alias).
>
> Hmm, thanks, I will make sure there's some more verbose failure mode if
> we can't find a way for something akin to an alias to be usable there.

I imagine it will be easy to fix a diagnostic output.

Iain


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

* Re: [PATCH] introduce attribute exalias
  2020-08-15  2:43           ` Alexandre Oliva
  2020-08-15  9:22             ` Iain Sandoe
@ 2020-08-15 21:11             ` Nathan Sidwell
  2020-08-25  7:50               ` Alexandre Oliva
  1 sibling, 1 reply; 35+ messages in thread
From: Nathan Sidwell @ 2020-08-15 21:11 UTC (permalink / raw)
  To: Alexandre Oliva; +Cc: gcc-patches, jason, joseph, ebotcazou

On 8/14/20 10:43 PM, Alexandre Oliva wrote:
> On Aug 14, 2020, Nathan Sidwell <nathan@acm.org> wrote:


> Since you don't seem to have liked 'aka' either, how about 'nickname',
> or 'nicknamed'?  A more convenient name to refer to an entity is exactly
> what this is about, eh?

I'm sorry, I think those are awful names.   They convey no intent.  C++ already 
has at least 2 'nickname' mechanisms:

using bob = ::foo::bar<bob::random_type,int>;
auto &bill = ::elsewhere::object<some_type>;

'alias' is also now a confusing term, because of the concept of object-aliasing.

The existing alias attribute is defined as:

 > The @code{alias} attribute causes the declaration to be emitted as an alias
 > for another symbol, which must have been previously declared with the same
 > type, and for variables, also the same size and alignment.  Declaring an alias
 > with a different type than the target is undefined and may be diagnosed.  As
 > an example, the following declarations:

I.e. it is creating a declaration that is aliased to some other symbol (which 
has to also be emitted by the same TU due to the usual elf-like object file 
semantics).  Notice it says nothing about emitting a *symbol*.

The new attribute is emitting a symbol that equates the declaration it is 
attached to (i.e. the other way round).

Its intent is to allow code written in another language to refer to this 
definition.  I imagine you'd commonly use the foreign language's mangling for 
the string provided.

If we spell it 'X', consider:

[[gnu::X ("other")]] int i;

Most commonly, the assembly emitted would contain:
	.globl other
	.equiv other, i

so, perhaps we should spell it 'equiv'?  That's using an existing term.

nathan

-- 
Nathan Sidwell

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

* Re: [PATCH] introduce attribute exalias
  2020-08-15 21:11             ` Nathan Sidwell
@ 2020-08-25  7:50               ` Alexandre Oliva
  0 siblings, 0 replies; 35+ messages in thread
From: Alexandre Oliva @ 2020-08-25  7:50 UTC (permalink / raw)
  To: Nathan Sidwell; +Cc: ebotcazou, gcc-patches, joseph

On Aug 15, 2020, Nathan Sidwell <nathan@acm.org> wrote:

> 'alias' is also now a confusing term, because of the concept of object-aliasing.

True, but it's also an established attribute name.  It seems thus
desirable to bring the conceptual framework of the alias attribute to
mind through the name of the attribute used in this new feature, but
with a twist that distinguishes it from the original attribute in a
meaningful way.


> The existing alias attribute is defined as:

>> The @code{alias} attribute causes the declaration to be emitted as an alias
>> for another symbol, which must have been previously declared with the same
>> type, and for variables, also the same size and alignment.  Declaring an alias
>> with a different type than the target is undefined and may be diagnosed.  As
>> an example, the following declarations:

> I.e. it is creating a declaration that is aliased to some other symbol
> (which has to also be emitted by the same TU due to the usual elf-like
> object file semantics).  Notice it says nothing about emitting a
> *symbol*.

It's an implied expectation that declarations of language entities get
symbols associated with them, and that attribute alias causes the symbol
name associated with one declaration to refer to the same entity denoted
by the named symbol, instead of introducing a separate entity.

> The new attribute is emitting a symbol that equates the declaration it
> is attached to (i.e. the other way round).

I.e., now we have a single declaration of a language entity, and we wish
additional symbol names to be associated with it.

> Its intent is to allow code written in another language to refer to
> this definition.  I imagine you'd commonly use the foreign language's
> mangling for the string provided.

What I have in mind are mnemonic, user-chosen names, rather than
machine-mangled ones.


> If we spell it 'X', consider:

> [[gnu::X ("other")]] int i;

> Most commonly, the assembly emitted would contain:
> 	.globl other
> 	.equiv other, i

> so, perhaps we should spell it 'equiv'?  That's using an existing term.

Uhh, my turn to find the term meaningless.  Not just because on my
machines, aliases use .set rather than .equiv.  It's missing about as
much context as 'aka' and 'nickname' in the relationship to alias.

The problem we face is that alias is a symmetric relationship, in that
if X aliases Y, then Y aliases X, but attribute alias is not symmetric:
its attribute must be placed in an undefined declaration, naming the
already-defined entity.

It would have made just as much sense to make it work backwards, namely,
attaching the attribute to the defined entity, naming any other
undefined declarations that ought to refer to the same entity.  That's
pretty much the attribute I propose.

Thus clearly, though "alias" is symmetric, our use thereof isn't.  Our
implementation uses the phrase "alias target" to refer to the
already-defined entity that a declaration holding an alias attribute
should alias; the declaration holding the alias attribute is referred to
as an alias.

Since attribute alias is already taken, it would thus make sense to look
for opposites of alias target to denote the new attribute.  Alias source
and alias origin are probably the most natural opposites of alias
target, but they don't suggest to me what we're looking for.  Alias lead
might work.  Alias bead might, too.

OTOH, since the alias attribute is associated with the alias
declaration, and it names an alias target; the opposite of that, with an
attribute in the alias target declaration, would best have the attribute
named alias_target (that's an attribute of the declaration, after all),
and then the named symbol would be the alias.  This would be
surprisingly consistent with the current use of attribute alias:

int attribute((alias_target("Y"))) X; // X is the alias target for Y
int attribute((alias("Y"))) Z; // Z is an alias for Y


Now, if we were to use "equiv", it would make sense to think of the
current alias attribute as "equiv_to" / "alias_target".


Another possibility that occurs to me is to reuse the existing attribute
alias, but naming the equivalent symbol as a second parameter to
attribute alias, suggesting a relation alias(X,Y), building on the
existing alias(X) in which the Y is implied, and introducing a variant
in which it is the X that is implied.  Alas, an explicit placeholder is
needed in this case.

Hmm, maybe such a two-argument variant of alias could be made more
readable by requiring the *second* argument to be number 2:

int attribute((alias("Y", 2))) X;

reading as imperative "alias Y to X", whereas existing uses:

int attribute((alias("Y"))) Z;

read as imperative "alias Y Z", nonsense, but taken as "alias Z to Y",
perhaps by analogy with how "give Y Z" is taken as "give Z to Y".


/me jokingly suggests inhalias and exhalias ;-)


This is enough bikeshedding for me ;-)

-- 
Alexandre Oliva, happy hacker
https://FSFLA.org/blogs/lxo/
Free Software Activist
GNU Toolchain Engineer

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

* Re: [PATCH] introduce attribute exalias
  2020-08-15 18:17                 ` Iain Sandoe
@ 2020-08-25  8:34                   ` Alexandre Oliva
  2020-08-25 11:23                     ` Iain Sandoe
  0 siblings, 1 reply; 35+ messages in thread
From: Alexandre Oliva @ 2020-08-25  8:34 UTC (permalink / raw)
  To: Iain Sandoe
  Cc: Eric Botcazou, Andrew MacLeod via Gcc-patches, Nathan Sidwell, joseph

On Aug 15, 2020, Iain Sandoe <iain@sandoe.co.uk> wrote:

> Alexandre Oliva <oliva@adacore.com> wrote:

>> I'm pretty sure setting multiple labels at the same address is used
>> and relied on quite often.

> That’s what’s currently disallowed (the assemblers all support .set).

I understand you mean it's disallowed for global labels.  I meant it was
often used for local labels.


> For function aliases, I think there’s a simple work-around and it’s just a
> question of time for me to make a patch etc.

Yeah, one possibility that comes to mind is to output additional text
symbols (akin to PLT entries) that just jump to the intended target.

> for general aliases to public symbols including data, not so easy.

*nod*

As far as I'm concerned, function aliases as above would be enough to
address the issue I was asked to address.  The RTTI aliases are only
used to import and catch C++ exception in Ada.  I suppose a mnemonic
would be just as welcome, but if it's not available, the mangled name
will have to do.


> Actually, I was thinking about folks who like template metaprogramming
> (not personally a fan) - and how they would arrange to get automatic
> export information to track that meta-progamming.

There's no (current) way to import C++ templates as Ada generics; the
best you can do is to import C++ template instantiations/specializations
as Ada records, procedures and functions.  This, and the lack of a
symbolic representation of generics, has driven me to introduce
user-chosen named aliases for specializations, rather than to the
generics.

I've considered enabling symbolic mnemonic template names to be
associated with templates, say:

template <typename T, typename U>
struct foo {
  static void __attribute__((__exalias__("FOO_%<T>_%<U>_static_method")))
  static_method () {}
};

replacing %<T> and %<U> with the mangling for the template arguments,
but that would bring us back the problem of varying mangled names as
mnemonic types like u64 get resolved to the mangling of their
target-dependent language types, defeating the purpose of referencing
cross-platform symbol names from Ada.

> Thinking aloud  - not thought through in any detail - I wonder if the
> facilities of
> C++20 modules are sufficient?

I'm really not sure what issue you're thinking of solving.

The one I'm working on is that of enabling the use of a uniform string
in Ada import statements (and also in alias("targets")), even when a
symbolic type that does not mangle uniformly is in use (think int64_t
mapping to long or long long).  Having per-target source files is quite
cumbersome in Ada, and there isn't a preprocessor to rely on for
conditionals and token pasting and whatnot.

I'm afraid I don't see how C++ modules could any offer in this regard.


>> Hmm, thanks, I will make sure there's some more verbose failure mode if
>> we can't find a way for something akin to an alias to be usable there.

> I imagine it will be easy to fix a diagnostic output.

*nod*

-- 
Alexandre Oliva, happy hacker
https://FSFLA.org/blogs/lxo/
Free Software Activist
GNU Toolchain Engineer

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

* Re: [PATCH] introduce attribute exalias
  2020-08-25  8:34                   ` Alexandre Oliva
@ 2020-08-25 11:23                     ` Iain Sandoe
  0 siblings, 0 replies; 35+ messages in thread
From: Iain Sandoe @ 2020-08-25 11:23 UTC (permalink / raw)
  To: Alexandre Oliva
  Cc: Eric Botcazou, Andrew MacLeod via Gcc-patches, Nathan Sidwell, joseph

Alexandre Oliva <oliva@adacore.com> wrote:

> On Aug 15, 2020, Iain Sandoe <iain@sandoe.co.uk> wrote:
>
>> Alexandre Oliva <oliva@adacore.com> wrote:
>
>>> I'm pretty sure setting multiple labels at the same address is used
>>> and relied on quite often.
>
>> That’s what’s currently disallowed (the assemblers all support .set).
>
> I understand you mean it's disallowed for global labels.

Yes - or, in fact, linker-visible ones for Darwin because of the issue below.

>  I meant it was often used for local labels.

… including for Darwin targets;
one has to take care with details sometimes, since a local label defines
something that is part of the atom delineated by the next preceding global
(or linker-visible) symbol (or section start if there are none).

When the local label does not really belong to that atom, it can cause
problems (e.g. apparently branching to code that is weak global) so we insert
a non-global (but linker-visible) symbol to allow a new atom to start.

>> Thinking aloud  - not thought through in any detail - I wonder if the
>> facilities of
>> C++20 modules are sufficient?
>
> I'm really not sure what issue you're thinking of solving.

The one where one wanted to import the declaration of a function without
having to include every header needed to declare the types it uses.

Since a header unit should be self-contained and is a compiled artefact.

> The one I'm working on is that of enabling the use of a uniform string
> in Ada import statements (and also in alias("targets")), even when a
> symbolic type that does not mangle uniformly is in use (think int64_t
> mapping to long or long long).  Having per-target source files is quite
> cumbersome in Ada, and there isn't a preprocessor to rely on for
> conditionals and token pasting and whatnot.
>
> I'm afraid I don't see how C++ modules could any offer in this regard.

perhaps not, it was only “thinking aloud”
(since we are agreed it would be nice to have a solution that the compiler
could manipulate/synthesize, rather than requiring source-level changes).

FAOD, the comments above are just continuation of discussion - not any
additional objection to the proposal.

thanks
Iain


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

* [PATCH v3] Introduce attribute reverse_alias
  2020-08-07 17:38 ` [PATCH] " Alexandre Oliva
  2020-08-14 15:39   ` Alexandre Oliva
@ 2023-07-15  1:08   ` Alexandre Oliva
  2023-07-15 21:55     ` Nathan Sidwell
  1 sibling, 1 reply; 35+ messages in thread
From: Alexandre Oliva @ 2023-07-15  1:08 UTC (permalink / raw)
  To: gcc-patches; +Cc: jason, nathan, joseph, hainque, ebotcazou


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

The attribute is usable in C and C++, presumably in all C-family
languages.  It can be attached to global variables and functions.  In
C++, it can also be attached to namespace-scoped variables and
functions, static data members, member functions, explicit
instantiations and specializations of template functions, members and
classes.

When applied to constructors or destructor, additional reverse_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.

Regstrapped on x86_64-linux-gnu.  Ok to install?

This is refreshed and renamed from earlier versions that named the
attribute 'exalias', and that AFAICT got stuck in name bikeshedding.
https://gcc.gnu.org/pipermail/gcc-patches/2020-August/551614.html


for  gcc/ChangeLog

	* attribs.cc: Include cgraph.h.
	(decl_attributes): Allow late introduction of reverse_alias in
	types.
	(create_reverse_alias_decl, create_reverse_alias_decls): New.
	* attribs.h: Declare them.
	(FOR_EACH_REVERSE_ALIAS): New macro.
	* cgraph.cc (cgraph_node::create): Create reverse_alias decls.
	* varpool.cc (varpool_node::get_create): Create reverse_alias
	decls.
	* cgraph.h (symtab_node::remap_reverse_alias_target): New.
	* symtab.cc (symtab_node::remap_reverse_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 (reverse_alias): Document for variables,
	functions and types.

for  gcc/ada/ChangeLog

	* doc/gnat_rm/interfacing_to_other_languages.rst: Mention
	attribute reverse_alias to give RTTI symbols mnemonic names.
	* doc/gnat_ugn/the_gnat_compilation_model.rst: Mention
	attribute reverse_alias.  Fix incorrect ref to C1 ctor variant.

for  gcc/c-family/ChangeLog

	* c-ada-spec.cc (pp_asm_name): Use first reverse_alias if
	available.
	* c-attribs.cc (handle_reverse_alias_attribute): New.
	(c_common_attribute_table): Add reverse_alias.
	(handle_copy_attribute): Do not copy reverse_alias.

for  gcc/c/ChangeLog

	* c-decl.cc (duplicate_decls): Remap reverse_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_reverse_alias_interface): Declare.
	(update_tinfo_reverse_alias): Declare.
	* decl.cc (duplicate_decls): Remap reverse_alias target.
	Adjust clone attributes.
	(grokfndecl): Tentatively create reverse_alias decls after
	adding attributes in e.g. a template member function explicit
	instantiation.
	* decl2.cc (cplus_decl_attributes): Update tinfo
	reverse_alias.
	(copy_interface, update_reverse_alias_interface): New.
	(determine_visibility): Update reverse_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 reverse_alias.
	* optimize.cc (maybe_clone_body): Re-adjust attributes after
	cloning them.  Update reverse_alias interface.
	* rtti.cc: Include attribs.h and cgraph.h.
	(get_tinfo_decl): Copy reverse_alias attributes from type to
	tinfo decl.  Create reverse_alias decls.
	(update_tinfo_reverse_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-revalias-1.c: New.
	* c-c++-common/torture/attr-revalias-2.c: New.
	* c-c++-common/torture/attr-revalias-3.c: New.
	* c-c++-common/torture/attr-revalias-4.c: New.
	* g++.dg/torture/attr-revalias-1.C: New.
	* g++.dg/torture/attr-revalias-2.C: New.
	* g++.dg/torture/attr-revalias-3.C: New.
	* g++.dg/torture/attr-revalias-4.C: New.
	* g++.dg/torture/attr-revalias-5.C: New.
---
 .../doc/gnat_rm/interfacing_to_other_languages.rst |    6 +
 .../doc/gnat_ugn/the_gnat_compilation_model.rst    |   10 ++
 gcc/attribs.cc                                     |   67 ++++++++++++++++
 gcc/attribs.h                                      |   10 ++
 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                                    |   65 ++++++++++++++++
 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                                     |   73 +++++++++++++++++-
 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-revalias-1.c         |   39 +++++++++
 .../c-c++-common/torture/attr-revalias-2.c         |   13 +++
 .../c-c++-common/torture/attr-revalias-3.c         |   41 ++++++++++
 .../c-c++-common/torture/attr-revalias-4.c         |   28 +++++++
 gcc/testsuite/g++.dg/torture/attr-revalias-1.C     |   72 +++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-revalias-2.C     |   26 ++++++
 gcc/testsuite/g++.dg/torture/attr-revalias-3.C     |   83 ++++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-revalias-4.C     |   28 +++++++
 gcc/testsuite/g++.dg/torture/attr-revalias-5.C     |   14 +++
 gcc/varpool.cc                                     |    3 +
 31 files changed, 789 insertions(+), 19 deletions(-)
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-revalias-1.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-revalias-2.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-revalias-3.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-revalias-4.c
 create mode 100644 gcc/testsuite/g++.dg/torture/attr-revalias-1.C
 create mode 100644 gcc/testsuite/g++.dg/torture/attr-revalias-2.C
 create mode 100644 gcc/testsuite/g++.dg/torture/attr-revalias-3.C
 create mode 100644 gcc/testsuite/g++.dg/torture/attr-revalias-4.C
 create mode 100644 gcc/testsuite/g++.dg/torture/attr-revalias-5.C

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 ad0be511d4800..18c9077e967a2 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 ``reverse_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 148d40815b8f8..a893afc1e0d3d 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__ ((__reverse_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__ ((__reverse_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 or reverse_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 b8cb55b97df38..6289fa4ef4e76 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"
@@ -819,7 +820,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 ("reverse_alias", name))
 	{
 	  warning (OPT_Wattributes, "type attributes ignored after type is already defined");
 	  continue;
@@ -2631,6 +2633,69 @@ attr_access::array_as_string (tree type) const
   return typstr;
 }
 
+/* Create a reverse_alias for DECL with linkage name ID.  */
+
+tree
+create_reverse_alias_decl (tree decl, tree id)
+{
+  tree name = get_identifier ("reverse_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;
+
+      error_at (DECL_SOURCE_LOCATION (decl),
+		"duplicate symbol name %qE in %qE attribute of %qD",
+		id, 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 ("reverse_alias",
+					      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 all reverse_aliases requested in DECL's attributes.  */
+
+void
+create_reverse_alias_decls (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl)
+      || DECL_ABSTRACT_P (decl))
+    return;
+
+  FOR_EACH_REVERSE_ALIAS (reverse_alias, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (reverse_alias));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      create_reverse_alias_decl (decl, id);
+    }
+}
+
 #if CHECKING_P
 
 namespace selftest
diff --git a/gcc/attribs.h b/gcc/attribs.h
index 84a43658a70da..2e834ac935fbf 100644
--- a/gcc/attribs.h
+++ b/gcc/attribs.h
@@ -398,4 +398,14 @@ 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_reverse_alias_decl (tree, tree);
+extern void create_reverse_alias_decls (tree);
+
+#define FOR_EACH_REVERSE_ALIAS(reverse_alias, attrs)			\
+  for (tree reverse_alias = lookup_attribute ("reverse_alias",		\
+					      (attrs));			\
+       reverse_alias;							\
+       reverse_alias = lookup_attribute ("reverse_alias",		\
+					 TREE_CHAIN (reverse_alias)))
+
 #endif // GCC_ATTRIBS_H
diff --git a/gcc/c-family/c-ada-spec.cc b/gcc/c-family/c-ada-spec.cc
index 050994d841665..bebabd5d325fa 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_REVERSE_ALIAS (reverse_alias, DECL_ATTRIBUTES (t))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (reverse_alias));
+      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 e2792ca6898b3..29c1aabef0041 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_reverse_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,
@@ -383,6 +384,8 @@ const struct attribute_spec c_common_attribute_table[] =
 			      handle_alias_attribute, NULL },
   { "weakref",                0, 1, true,  false, false, false,
 			      handle_weakref_attribute, NULL },
+  { "reverse_alias",          1, 1, false,  false, false, false,
+			      handle_reverse_alias_attribute, NULL },
   { "no_instrument_function", 0, 0, true,  false, false, false,
 			      handle_no_instrument_function_attribute,
 			      NULL },
@@ -2855,7 +2858,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
@@ -2865,7 +2868,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
@@ -2875,6 +2878,29 @@ handle_alias_attribute (tree *node, tree name, tree args,
   return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs);
 }
 
+/* Handle an "reverse_alias" attribute; arguments as in struct
+   attribute_spec.handler.  */
+
+static tree
+handle_reverse_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.  */
 
@@ -3002,6 +3028,7 @@ handle_copy_attribute (tree *node, tree name, tree args,
 	  tree atname = get_attribute_name (at);
 	  if (is_attribute_p ("alias", atname)
 	      || is_attribute_p ("always_inline", atname)
+	      || is_attribute_p ("reverse_alias", atname)
 	      || is_attribute_p ("gnu_inline", atname)
 	      || is_attribute_p ("ifunc", atname)
 	      || is_attribute_p ("noinline", atname)
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index ecd10ebb69caf..02d6d538a0e58 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -3073,6 +3073,8 @@ duplicate_decls (tree newdecl, tree olddecl)
 
   merge_decls (newdecl, olddecl, newtype, oldtype);
 
+  symtab_node::remap_reverse_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 e41e5ad3ae74d..9d2fa5c6eaa11 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_reverse_alias_decls (decl);
+
   return node;
 }
 
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index cedaaac3a45b7..f1e231005cbdc 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 reverse_alias nodes recorded as aliasing REPLACED to alias
+     REPLACEMENT instead.  */
+  static void remap_reverse_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 bccd2f2abb5a3..eb2d05094e989 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 778759237dc72..fb266a0e57ca5 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -4859,6 +4859,69 @@ check_methods (tree t)
     }
 }
 
+/* Adjust reverse_alias name for CLONE, cloned from FN and named NAME,
+   if it is a cdtor, and drop the reverse_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_REVERSE_ALIAS (reverse_alias, DECL_ATTRIBUTES (clone))
+	{
+	  found = true;
+	  break;
+	}
+
+      if (found
+	  && (name == complete_ctor_identifier
+	      || name == complete_dtor_identifier))
+	{
+	  /* Reuse the reverse_alias decls created for the primary
+	     cdtor decl.  */
+	  symtab_node::remap_reverse_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_REVERSE_ALIAS (reverse_alias, DECL_ATTRIBUTES (clone))
+	    {
+	      /* We need to copy this even with skip_copy_p, because
+		 even then copying was shallow.  */
+	      TREE_VALUE (reverse_alias) = copy_list (TREE_VALUE
+						      (reverse_alias));
+	      /* Append suf to the reverse_alias name.  */
+	      tree str = TREE_VALUE (TREE_VALUE (reverse_alias));
+	      char *symname = concat (TREE_STRING_POINTER (str), suf, NULL);
+	      str = build_string (TREE_STRING_LENGTH (str) + xlen, symname);
+	      TREE_VALUE (TREE_VALUE (reverse_alias)) = str;
+	      free (symname);
+	    }
+
+	  if (symtab_node::get (clone))
+	    create_reverse_alias_decls (clone);
+	}
+    }
+  else
+    DECL_ATTRIBUTES (clone)
+      = remove_attribute ("reverse_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.  */
@@ -5026,6 +5089,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 3de0e154c124c..8cfd41bcc5332 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5747,6 +5747,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.  */
@@ -6960,6 +6962,7 @@ extern void do_push_parm_decls			(tree, tree, tree *);
 extern tree do_aggregate_paren_init		(tree, tree);
 
 /* in decl2.cc */
+extern void update_reverse_alias_interface	(tree);
 extern void record_mangling			(tree, bool);
 extern void overwrite_mangling			(tree, tree);
 extern void note_mangling_alias			(tree, tree);
@@ -7535,6 +7538,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_reverse_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 60f107d50c4c5..d2bad39d5e1c6 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -3195,6 +3195,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
 	      && TREE_STATIC (olddecl))))
     make_decl_rtl (olddecl);
 
+  symtab_node::remap_reverse_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.
@@ -3218,6 +3220,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);
 	}
     }
@@ -10683,6 +10686,7 @@ grokfndecl (tree ctype,
     {
       cplus_decl_attributes (&decl, *attrlist, 0);
       *attrlist = NULL_TREE;
+      create_reverse_alias_decls (decl);
     }
 
   if (DECL_HAS_CONTRACTS_P (decl))
diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
index b402befba6da4..4ed9e8c5e928e 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -1773,6 +1773,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_reverse_alias (*decl);
+
   /* Propagate deprecation out to the template.  */
   if (TREE_DEPRECATED (*decl))
     if (tree ti = get_template_info (*decl))
@@ -2129,6 +2132,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 reverse_aliases.  */
+
+void
+update_reverse_alias_interface (tree decl)
+{
+  if (!decl_in_symtab_p (decl)
+      || !symtab_node::get (decl))
+    return;
+
+  FOR_EACH_REVERSE_ALIAS (reverse_alias, DECL_ATTRIBUTES (decl))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (reverse_alias));
+      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.  */
 
@@ -2936,6 +2980,8 @@ determine_visibility (tree decl)
        translation unit, we can make the type internal.  */
     constrain_visibility (decl, VISIBILITY_ANON, false);
 
+  update_reverse_alias_interface (decl);
+
   /* If visibility changed and DECL already has DECL_RTL, ensure
      symbol flags are updated.  */
   if ((DECL_VISIBILITY (decl) != orig_visibility
@@ -3198,6 +3244,8 @@ tentative_decl_linkage (tree decl)
       else if (VAR_P (decl))
 	maybe_commonize_var (decl);
     }
+
+  update_reverse_alias_interface (decl);
 }
 
 /* DECL is a FUNCTION_DECL or VAR_DECL.  If the object file linkage
@@ -3432,6 +3480,8 @@ import_export_decl (tree decl)
     }
 
   DECL_INTERFACE_KNOWN (decl) = 1;
+
+  update_reverse_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 74565184403c1..8bc15c68ac579 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"
@@ -3466,6 +3468,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_reverse_alias_target (decl, alias);
+	  DECL_ATTRIBUTES (decl)
+	    = remove_attribute ("reverse_alias", DECL_ATTRIBUTES (alias));
+	}
     }
 
   retrofit_lang_decl (decl);
diff --git a/gcc/cp/optimize.cc b/gcc/cp/optimize.cc
index 9e8926e4cc603..bc4f816d46290 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_reverse_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 7878929c24679..35b530c83ba34 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 reverse_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,18 @@ 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 reverse_alias attributes from the type to the rtti obj decl.  */
+      tree *attrs = &DECL_ATTRIBUTES (d);
+      FOR_EACH_REVERSE_ALIAS (reverse_alias, TYPE_ATTRIBUTES (type))
+	{
+	  tree attr = tree_cons (TREE_PURPOSE (reverse_alias),
+				 TREE_VALUE (reverse_alias),
+				 *attrs);
+	  *attrs = attr;
+	  attrs = &TREE_CHAIN (attr);
+	}
+      create_reverse_alias_decls (d);
+
       /* Add decl to the global array of tinfo decls.  */
       vec_safe_push (unemitted_tinfo_decls, d);
     }
@@ -499,6 +518,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 reverse_alias attributes
+   that were not already present.  */
+
+void
+update_tinfo_reverse_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_REVERSE_ALIAS (reverse_alias, TYPE_ATTRIBUTES (type))
+    {
+      bool found = false;
+      FOR_EACH_REVERSE_ALIAS (d_reverse_alias, *attrs)
+	if (TREE_VALUE (reverse_alias) == TREE_VALUE (d_reverse_alias))
+	  {
+	    found = true;
+	    break;
+	  }
+
+      if (found)
+	continue;
+
+      tree attr = tree_cons (TREE_PURPOSE (reverse_alias),
+			     TREE_VALUE (reverse_alias),
+			     *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 (reverse_alias));
+      id = get_identifier (TREE_STRING_POINTER (id));
+      create_reverse_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 dda35358ce746..194312addf3f1 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -3932,6 +3932,39 @@ function.  Examples of such functions are @code{setjmp} and @code{vfork}.
 The @code{longjmp}-like counterpart of such function, if any, might need
 to be marked with the @code{noreturn} attribute.
 
+@cindex @code{reverse_alias} function attribute
+@item reverse_alias ("@var{name}")
+The @code{reverse_alias} attribute causes @var{name} to be emitted as an
+alias to the definition.  For instance,
+
+@smallexample
+void f (uint64_t) __attribute__ ((__reverse_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,
+and may not be available on all targets.
+
 @cindex @code{section} function attribute
 @cindex functions in arbitrary sections
 @item section ("@var{section-name}")
@@ -7535,6 +7568,10 @@ align them on any target.
 The @code{aligned} attribute can also be used for functions
 (@pxref{Common Function Attributes}.)
 
+@cindex @code{reverse_alias} variable attribute
+@item reverse_alias ("@var{name}")
+See @pxref{Common Function Attributes}.
+
 @cindex @code{warn_if_not_aligned} variable attribute
 @item warn_if_not_aligned (@var{alignment})
 This attribute specifies a threshold for the structure field, measured
@@ -7715,6 +7752,21 @@ The @code{unavailable} attribute can also be used for functions and
 types (@pxref{Common Function Attributes},
 @pxref{Common Type Attributes}).
 
+@cindex @code{reverse_alias} type attribute
+@item reverse_alias ("@var{name}")
+The @code{reverse_alias} 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__ ((__reverse_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{mode} variable attribute
 @item mode (@var{mode})
 This attribute specifies the data type for the declaration---whichever
diff --git a/gcc/symtab.cc b/gcc/symtab.cc
index 0470509a98d2a..4275550afc21c 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 reverse_alias nodes recorded as aliasing REPLACED to alias
+   REPLACEMENT instead.  */
+
+void
+symtab_node::remap_reverse_alias_target (tree replaced, tree replacement)
+{
+  if (!decl_in_symtab_p (replacement)
+      || !symtab_node::get (replacement))
+    return;
+
+  FOR_EACH_REVERSE_ALIAS (reverse_alias, DECL_ATTRIBUTES (replaced))
+    {
+      tree id = TREE_VALUE (TREE_VALUE (reverse_alias));
+      id = get_identifier (TREE_STRING_POINTER (id));
+
+      symtab_node *sym_node = symtab_node::get_for_asmname (id);
+
+      if (!sym_node)
+	{
+	  create_reverse_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 46ee01b675950..f284289331807 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 e2e22be57e9e4..aec59b69754c5 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-revalias-1.c b/gcc/testsuite/c-c++-common/torture/attr-revalias-1.c
new file mode 100644
index 0000000000000..dd75e85a00105
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-revalias-1.c
@@ -0,0 +1,39 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+extern int var_a __attribute__ ((__reverse_alias__ ("FOOVAR_A")));
+int var_a = 1;
+
+void foo_a () __attribute__ ((__reverse_alias__ ("FOOBAR_A")));
+
+void
+foo_a ()
+{
+}
+
+
+int var_b;
+extern int var_b __attribute__ ((__reverse_alias__ ("FOOVAR_B")));
+
+void
+foo_b ()
+{
+}
+
+void foo_b () __attribute__ ((__reverse_alias__ ("FOOBAR_B")));
+
+
+int var_c __attribute__ ((__reverse_alias__ ("FOOVAR_C")));
+
+void __attribute__ ((__reverse_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-revalias-2.c b/gcc/testsuite/c-c++-common/torture/attr-revalias-2.c
new file mode 100644
index 0000000000000..8da45141e4985
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-revalias-2.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+struct s
+{
+  int mem __attribute__ ((__reverse_alias__ ("MEMFOO"))); /* { dg-warning "attribute ignored" } */
+};
+
+void foo()
+{
+  extern void bar () __attribute__ ((__reverse_alias__ ("FOOBAR")));
+  int var __attribute__ ((__reverse_alias__ ("FOOVAR"))); /* { dg-warning "attribute ignored" } */
+}
diff --git a/gcc/testsuite/c-c++-common/torture/attr-revalias-3.c b/gcc/testsuite/c-c++-common/torture/attr-revalias-3.c
new file mode 100644
index 0000000000000..86a430894ed2d
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-revalias-3.c
@@ -0,0 +1,41 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int var_a = 1;
+
+void
+foo_a ()
+{
+  extern int var_a __attribute__ ((__reverse_alias__ ("FOOVAR_A")));
+  void foo_a () __attribute__ ((__reverse_alias__ ("FOOBAR_A")));
+}
+
+#if __cplusplus
+/* Without this declaration before the local declaration below, the
+   attributes of the local declaration do not get propagated to the
+   (global) namespace scope.  */
+extern int var_b;
+#endif
+
+void
+foo_b ()
+{
+  extern int var_b __attribute__ ((__reverse_alias__ ("FOOVAR_B")));
+}
+
+int var_b;
+
+void __attribute__ ((__reverse_alias__ ("FOOBAR_C")))
+foo_c ()
+{
+  void foo_b () __attribute__ ((__reverse_alias__ ("FOOBAR_B")));
+  /* Another reverse_alias for var_b.  */
+  extern int var_b __attribute__ ((__reverse_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-revalias-4.c b/gcc/testsuite/c-c++-common/torture/attr-revalias-4.c
new file mode 100644
index 0000000000000..66c3220703607
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-revalias-4.c
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-require-alias "" } */
+
+int var_a __attribute__ ((__reverse_alias__ ("FOOVAR_A"))) = 42;
+
+int __attribute__ ((__reverse_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-revalias-1.C b/gcc/testsuite/g++.dg/torture/attr-revalias-1.C
new file mode 100644
index 0000000000000..5d226c1d625bb
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-revalias-1.C
@@ -0,0 +1,72 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+class __attribute__ ((__reverse_alias__ ("FOOCLS_A"),
+		      __reverse_alias__ ("FOOCLS_A_Dupe"))) foo {
+  static int var __attribute__ ((__reverse_alias__ ("FOOVAR_A")));
+  __attribute__ ((__reverse_alias__ ("FOOCTR_A"))) foo ();
+  void __attribute__ ((__reverse_alias__ ("FOOBAR_A"))) bar ();
+  virtual __attribute__ ((__reverse_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+int foo::var = 1;
+
+foo::foo () {}
+
+void foo::bar () {}
+
+namespace b {
+  class __attribute__ ((__reverse_alias__ ("FOOCLS_B"))) foo {
+    static int var __attribute__ ((__reverse_alias__ ("FOOVAR_B")));
+    __attribute__ ((__reverse_alias__ ("FOOCTR_B"))) foo ();
+    void __attribute__ ((__reverse_alias__ ("FOOBAR_B"))) bar () {}
+    virtual __attribute__ ((__reverse_alias__ ("FOODTR_B"))) ~foo() {}
+  };
+
+  int foo::var = 2;
+
+  foo::foo () {
+    void (foo::*pbar)() = &foo::bar;
+  }
+}
+
+namespace c {
+  namespace cd {
+    class __attribute__ ((__reverse_alias__ ("FOOCLS_C"))) foo {
+      static int var __attribute__ ((__reverse_alias__ ("FOOVAR_C")));
+      __attribute__ ((__reverse_alias__ ("FOOCTR_C"))) foo () {
+	void (foo::*pbar)() = &foo::bar;
+      }
+      void __attribute__ ((__reverse_alias__ ("FOOBAR_C"))) bar () {}
+      virtual __attribute__ ((__reverse_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-revalias-2.C b/gcc/testsuite/g++.dg/torture/attr-revalias-2.C
new file mode 100644
index 0000000000000..8f671929f1a53
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-revalias-2.C
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+namespace {
+  class __attribute__ ((__reverse_alias__ ("FOOCLS_A"))) foo {
+    static int var __attribute__ ((__reverse_alias__ ("FOOVAR_A")));
+    __attribute__ ((__reverse_alias__ ("FOOCTR_A"))) foo ();
+    virtual __attribute__ ((__reverse_alias__ ("FOODTR_A"))) ~foo ();
+    void __attribute__ ((__reverse_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-revalias-3.C b/gcc/testsuite/g++.dg/torture/attr-revalias-3.C
new file mode 100644
index 0000000000000..8c693d152f2c5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-revalias-3.C
@@ -0,0 +1,83 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+// reverse_alias can be applied to template function explicit instantiations.
+
+template <typename T>
+void
+fn(T) {
+};
+
+template void __attribute__ ((__reverse_alias__ ("FOOFUN_UINT"))) fn<>(unsigned int);
+template void __attribute__ ((__reverse_alias__ ("FOOFUN_LONG"))) fn<>(long);
+
+template<> void __attribute__ ((__reverse_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__ ((__reverse_alias__ ("FOOCLS_CHAR_VIRT"))) foo<char>::virtfun();
+
+template class __attribute__ ((__reverse_alias__ ("FOOCLS_CHAR_TI"))) foo<char>;
+
+// Though they're only output if the enclosing class is.
+template void
+__attribute__ ((__reverse_alias__ ("FOOCLS_LONG_VIRT"))) foo<long>::virtfun();
+extern
+template class __attribute__ ((__reverse_alias__ ("FOOCLS_LONG_TI_X"))) foo<long>;
+
+
+template void
+__attribute__ ((__reverse_alias__ ("FOOCLS_VOID_ST"))) foo<void>::stfun();
+
+template class __attribute__ ((__reverse_alias__ ("FOOCLS_VOID_TI"))) foo<>;
+
+
+extern
+template class __attribute__ ((__reverse_alias__ ("FOOCLS_SHORT_TI_X"))) foo<short>;
+
+template void
+__attribute__ ((__reverse_alias__ ("FOOCLS_SHORT_ST"))) foo<short>::stfun();
+template void
+__attribute__ ((__reverse_alias__ ("FOOCLS_SHORT_INL"))) foo<short>::inlfun();
+
+template class __attribute__ ((__reverse_alias__ ("FOOCLS_SHORT_TI_D"))) foo<short>;
+
+// Explicit specializations work too.
+
+template <>
+struct  __attribute__ ((__reverse_alias__ ("FOOCLS_INT_TI")))
+foo<int>
+{
+  virtual ~foo() {}
+  virtual void __attribute__ ((__reverse_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-revalias-4.C b/gcc/testsuite/g++.dg/torture/attr-revalias-4.C
new file mode 100644
index 0000000000000..3b64f0eba4f8c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-revalias-4.C
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+template <typename T = void>
+class
+__attribute__ ((__reverse_alias__ ("FOOCLS")))
+foo // { dg-error "duplicate|already" }
+{
+  virtual ~foo() {}
+
+  template <typename U>
+  void
+    __attribute__ ((__reverse_alias__ ("FOOTMF")))
+    tmemfun () {} // { dg-error "duplicate|already" }
+};
+
+template <typename T>
+void
+__attribute__ ((__reverse_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-revalias-5.C b/gcc/testsuite/g++.dg/torture/attr-revalias-5.C
new file mode 100644
index 0000000000000..d90e960ef48be
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-revalias-5.C
@@ -0,0 +1,14 @@
+/* { dg-do compile { target c++11 } } */
+/* { dg-require-alias "" } */
+
+struct foo {
+  __attribute__ ((__reverse_alias__ ("FOOCTR_A"))) foo ();
+  virtual __attribute__ ((__reverse_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+foo::foo () {}
+
+// Make sure the inherited cdtors don't duplicate the reverse_aliases.
+struct bar : foo {
+  using foo::foo;
+};
diff --git a/gcc/varpool.cc b/gcc/varpool.cc
index e7b51b15e4a84..4ac95b5757ecb 100644
--- a/gcc/varpool.cc
+++ b/gcc/varpool.cc
@@ -163,6 +163,9 @@ varpool_node::get_create (tree decl)
     }
 
   node->register_symbol ();
+
+  create_reverse_alias_decls (decl);
+
   return node;
 }
 


-- 
Alexandre Oliva, happy hacker                https://FSFLA.org/blogs/lxo/
   Free Software Activist                       GNU Toolchain Engineer
Disinformation flourishes because many people care deeply about injustice
but very few check the facts.  Ask me about <https://stallmansupport.org>

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

* Re: [PATCH v3] Introduce attribute reverse_alias
  2023-07-15  1:08   ` [PATCH v3] Introduce attribute reverse_alias Alexandre Oliva
@ 2023-07-15 21:55     ` Nathan Sidwell
  2023-07-18  4:29       ` Alexandre Oliva
  0 siblings, 1 reply; 35+ messages in thread
From: Nathan Sidwell @ 2023-07-15 21:55 UTC (permalink / raw)
  To: Alexandre Oliva, gcc-patches; +Cc: jason, joseph, hainque, ebotcazou

Not commenting on the semantics, but the name seems unfortunate (hello 
bikeshed).  The documentation starts with 'attribute causes @var{name} to be 
emitted as an alias to the definition'.  So not emitting a 'reverse alias', 
whatever that might be.  It doesn;t seem to mention how reverse alias differs 
from 'alias'.  Why would 'alias' not DTRT?

Is is emitting a an additiona symbol -- ie, something like 'altname'.  Or is it 
something else? Is that symbol known in the current TU, or other TUs?

nathan



On 7/14/23 21:08, Alexandre Oliva wrote:
> 
> This patch introduces an attribute to add extra aliases to a symbol
> when its definition is output.  The main goal is to ease interfacing
> C++ with Ada, as C++ mangled names have to be named, and in some cases
> (e.g. when using stdint.h typedefs in function arguments) the symbol
> names may vary across platforms.
> 
> The attribute is usable in C and C++, presumably in all C-family
> languages.  It can be attached to global variables and functions.  In
> C++, it can also be attached to namespace-scoped variables and
> functions, static data members, member functions, explicit
> instantiations and specializations of template functions, members and
> classes.
> 
> When applied to constructors or destructor, additional reverse_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.
> 
> Regstrapped on x86_64-linux-gnu.  Ok to install?
> 
> This is refreshed and renamed from earlier versions that named the
> attribute 'exalias', and that AFAICT got stuck in name bikeshedding.
> https://gcc.gnu.org/pipermail/gcc-patches/2020-August/551614.html
> 
> 
> for  gcc/ChangeLog
> 
> 	* attribs.cc: Include cgraph.h.
> 	(decl_attributes): Allow late introduction of reverse_alias in
> 	types.
> 	(create_reverse_alias_decl, create_reverse_alias_decls): New.
> 	* attribs.h: Declare them.
> 	(FOR_EACH_REVERSE_ALIAS): New macro.
> 	* cgraph.cc (cgraph_node::create): Create reverse_alias decls.
> 	* varpool.cc (varpool_node::get_create): Create reverse_alias
> 	decls.
> 	* cgraph.h (symtab_node::remap_reverse_alias_target): New.
> 	* symtab.cc (symtab_node::remap_reverse_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 (reverse_alias): Document for variables,
> 	functions and types.
> 
> for  gcc/ada/ChangeLog
> 
> 	* doc/gnat_rm/interfacing_to_other_languages.rst: Mention
> 	attribute reverse_alias to give RTTI symbols mnemonic names.
> 	* doc/gnat_ugn/the_gnat_compilation_model.rst: Mention
> 	attribute reverse_alias.  Fix incorrect ref to C1 ctor variant.
> 
> for  gcc/c-family/ChangeLog
> 
> 	* c-ada-spec.cc (pp_asm_name): Use first reverse_alias if
> 	available.
> 	* c-attribs.cc (handle_reverse_alias_attribute): New.
> 	(c_common_attribute_table): Add reverse_alias.
> 	(handle_copy_attribute): Do not copy reverse_alias.
> 
> for  gcc/c/ChangeLog
> 
> 	* c-decl.cc (duplicate_decls): Remap reverse_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_reverse_alias_interface): Declare.
> 	(update_tinfo_reverse_alias): Declare.
> 	* decl.cc (duplicate_decls): Remap reverse_alias target.
> 	Adjust clone attributes.
> 	(grokfndecl): Tentatively create reverse_alias decls after
> 	adding attributes in e.g. a template member function explicit
> 	instantiation.
> 	* decl2.cc (cplus_decl_attributes): Update tinfo
> 	reverse_alias.
> 	(copy_interface, update_reverse_alias_interface): New.
> 	(determine_visibility): Update reverse_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 reverse_alias.
> 	* optimize.cc (maybe_clone_body): Re-adjust attributes after
> 	cloning them.  Update reverse_alias interface.
> 	* rtti.cc: Include attribs.h and cgraph.h.
> 	(get_tinfo_decl): Copy reverse_alias attributes from type to
> 	tinfo decl.  Create reverse_alias decls.
> 	(update_tinfo_reverse_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-revalias-1.c: New.
> 	* c-c++-common/torture/attr-revalias-2.c: New.
> 	* c-c++-common/torture/attr-revalias-3.c: New.
> 	* c-c++-common/torture/attr-revalias-4.c: New.
> 	* g++.dg/torture/attr-revalias-1.C: New.
> 	* g++.dg/torture/attr-revalias-2.C: New.
> 	* g++.dg/torture/attr-revalias-3.C: New.
> 	* g++.dg/torture/attr-revalias-4.C: New.
> 	* g++.dg/torture/attr-revalias-5.C: New.
> ---
>   .../doc/gnat_rm/interfacing_to_other_languages.rst |    6 +
>   .../doc/gnat_ugn/the_gnat_compilation_model.rst    |   10 ++
>   gcc/attribs.cc                                     |   67 ++++++++++++++++
>   gcc/attribs.h                                      |   10 ++
>   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                                    |   65 ++++++++++++++++
>   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                                     |   73 +++++++++++++++++-
>   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-revalias-1.c         |   39 +++++++++
>   .../c-c++-common/torture/attr-revalias-2.c         |   13 +++
>   .../c-c++-common/torture/attr-revalias-3.c         |   41 ++++++++++
>   .../c-c++-common/torture/attr-revalias-4.c         |   28 +++++++
>   gcc/testsuite/g++.dg/torture/attr-revalias-1.C     |   72 +++++++++++++++++
>   gcc/testsuite/g++.dg/torture/attr-revalias-2.C     |   26 ++++++
>   gcc/testsuite/g++.dg/torture/attr-revalias-3.C     |   83 ++++++++++++++++++++
>   gcc/testsuite/g++.dg/torture/attr-revalias-4.C     |   28 +++++++
>   gcc/testsuite/g++.dg/torture/attr-revalias-5.C     |   14 +++
>   gcc/varpool.cc                                     |    3 +
>   31 files changed, 789 insertions(+), 19 deletions(-)
>   create mode 100644 gcc/testsuite/c-c++-common/torture/attr-revalias-1.c
>   create mode 100644 gcc/testsuite/c-c++-common/torture/attr-revalias-2.c
>   create mode 100644 gcc/testsuite/c-c++-common/torture/attr-revalias-3.c
>   create mode 100644 gcc/testsuite/c-c++-common/torture/attr-revalias-4.c
>   create mode 100644 gcc/testsuite/g++.dg/torture/attr-revalias-1.C
>   create mode 100644 gcc/testsuite/g++.dg/torture/attr-revalias-2.C
>   create mode 100644 gcc/testsuite/g++.dg/torture/attr-revalias-3.C
>   create mode 100644 gcc/testsuite/g++.dg/torture/attr-revalias-4.C
>   create mode 100644 gcc/testsuite/g++.dg/torture/attr-revalias-5.C
> 
> 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 ad0be511d4800..18c9077e967a2 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 ``reverse_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 148d40815b8f8..a893afc1e0d3d 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__ ((__reverse_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__ ((__reverse_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 or reverse_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 b8cb55b97df38..6289fa4ef4e76 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"
> @@ -819,7 +820,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 ("reverse_alias", name))
>   	{
>   	  warning (OPT_Wattributes, "type attributes ignored after type is already defined");
>   	  continue;
> @@ -2631,6 +2633,69 @@ attr_access::array_as_string (tree type) const
>     return typstr;
>   }
>   
> +/* Create a reverse_alias for DECL with linkage name ID.  */
> +
> +tree
> +create_reverse_alias_decl (tree decl, tree id)
> +{
> +  tree name = get_identifier ("reverse_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;
> +
> +      error_at (DECL_SOURCE_LOCATION (decl),
> +		"duplicate symbol name %qE in %qE attribute of %qD",
> +		id, 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 ("reverse_alias",
> +					      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 all reverse_aliases requested in DECL's attributes.  */
> +
> +void
> +create_reverse_alias_decls (tree decl)
> +{
> +  if (!decl_in_symtab_p (decl)
> +      || !symtab_node::get (decl)
> +      || DECL_ABSTRACT_P (decl))
> +    return;
> +
> +  FOR_EACH_REVERSE_ALIAS (reverse_alias, DECL_ATTRIBUTES (decl))
> +    {
> +      tree id = TREE_VALUE (TREE_VALUE (reverse_alias));
> +      id = get_identifier (TREE_STRING_POINTER (id));
> +
> +      create_reverse_alias_decl (decl, id);
> +    }
> +}
> +
>   #if CHECKING_P
>   
>   namespace selftest
> diff --git a/gcc/attribs.h b/gcc/attribs.h
> index 84a43658a70da..2e834ac935fbf 100644
> --- a/gcc/attribs.h
> +++ b/gcc/attribs.h
> @@ -398,4 +398,14 @@ 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_reverse_alias_decl (tree, tree);
> +extern void create_reverse_alias_decls (tree);
> +
> +#define FOR_EACH_REVERSE_ALIAS(reverse_alias, attrs)			\
> +  for (tree reverse_alias = lookup_attribute ("reverse_alias",		\
> +					      (attrs));			\
> +       reverse_alias;							\
> +       reverse_alias = lookup_attribute ("reverse_alias",		\
> +					 TREE_CHAIN (reverse_alias)))
> +
>   #endif // GCC_ATTRIBS_H
> diff --git a/gcc/c-family/c-ada-spec.cc b/gcc/c-family/c-ada-spec.cc
> index 050994d841665..bebabd5d325fa 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_REVERSE_ALIAS (reverse_alias, DECL_ATTRIBUTES (t))
> +    {
> +      tree id = TREE_VALUE (TREE_VALUE (reverse_alias));
> +      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 e2792ca6898b3..29c1aabef0041 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_reverse_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,
> @@ -383,6 +384,8 @@ const struct attribute_spec c_common_attribute_table[] =
>   			      handle_alias_attribute, NULL },
>     { "weakref",                0, 1, true,  false, false, false,
>   			      handle_weakref_attribute, NULL },
> +  { "reverse_alias",          1, 1, false,  false, false, false,
> +			      handle_reverse_alias_attribute, NULL },
>     { "no_instrument_function", 0, 0, true,  false, false, false,
>   			      handle_no_instrument_function_attribute,
>   			      NULL },
> @@ -2855,7 +2858,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
> @@ -2865,7 +2868,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
> @@ -2875,6 +2878,29 @@ handle_alias_attribute (tree *node, tree name, tree args,
>     return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs);
>   }
>   
> +/* Handle an "reverse_alias" attribute; arguments as in struct
> +   attribute_spec.handler.  */
> +
> +static tree
> +handle_reverse_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.  */
>   
> @@ -3002,6 +3028,7 @@ handle_copy_attribute (tree *node, tree name, tree args,
>   	  tree atname = get_attribute_name (at);
>   	  if (is_attribute_p ("alias", atname)
>   	      || is_attribute_p ("always_inline", atname)
> +	      || is_attribute_p ("reverse_alias", atname)
>   	      || is_attribute_p ("gnu_inline", atname)
>   	      || is_attribute_p ("ifunc", atname)
>   	      || is_attribute_p ("noinline", atname)
> diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
> index ecd10ebb69caf..02d6d538a0e58 100644
> --- a/gcc/c/c-decl.cc
> +++ b/gcc/c/c-decl.cc
> @@ -3073,6 +3073,8 @@ duplicate_decls (tree newdecl, tree olddecl)
>   
>     merge_decls (newdecl, olddecl, newtype, oldtype);
>   
> +  symtab_node::remap_reverse_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 e41e5ad3ae74d..9d2fa5c6eaa11 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_reverse_alias_decls (decl);
> +
>     return node;
>   }
>   
> diff --git a/gcc/cgraph.h b/gcc/cgraph.h
> index cedaaac3a45b7..f1e231005cbdc 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 reverse_alias nodes recorded as aliasing REPLACED to alias
> +     REPLACEMENT instead.  */
> +  static void remap_reverse_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 bccd2f2abb5a3..eb2d05094e989 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 778759237dc72..fb266a0e57ca5 100644
> --- a/gcc/cp/class.cc
> +++ b/gcc/cp/class.cc
> @@ -4859,6 +4859,69 @@ check_methods (tree t)
>       }
>   }
>   
> +/* Adjust reverse_alias name for CLONE, cloned from FN and named NAME,
> +   if it is a cdtor, and drop the reverse_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_REVERSE_ALIAS (reverse_alias, DECL_ATTRIBUTES (clone))
> +	{
> +	  found = true;
> +	  break;
> +	}
> +
> +      if (found
> +	  && (name == complete_ctor_identifier
> +	      || name == complete_dtor_identifier))
> +	{
> +	  /* Reuse the reverse_alias decls created for the primary
> +	     cdtor decl.  */
> +	  symtab_node::remap_reverse_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_REVERSE_ALIAS (reverse_alias, DECL_ATTRIBUTES (clone))
> +	    {
> +	      /* We need to copy this even with skip_copy_p, because
> +		 even then copying was shallow.  */
> +	      TREE_VALUE (reverse_alias) = copy_list (TREE_VALUE
> +						      (reverse_alias));
> +	      /* Append suf to the reverse_alias name.  */
> +	      tree str = TREE_VALUE (TREE_VALUE (reverse_alias));
> +	      char *symname = concat (TREE_STRING_POINTER (str), suf, NULL);
> +	      str = build_string (TREE_STRING_LENGTH (str) + xlen, symname);
> +	      TREE_VALUE (TREE_VALUE (reverse_alias)) = str;
> +	      free (symname);
> +	    }
> +
> +	  if (symtab_node::get (clone))
> +	    create_reverse_alias_decls (clone);
> +	}
> +    }
> +  else
> +    DECL_ATTRIBUTES (clone)
> +      = remove_attribute ("reverse_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.  */
> @@ -5026,6 +5089,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 3de0e154c124c..8cfd41bcc5332 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -5747,6 +5747,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.  */
> @@ -6960,6 +6962,7 @@ extern void do_push_parm_decls			(tree, tree, tree *);
>   extern tree do_aggregate_paren_init		(tree, tree);
>   
>   /* in decl2.cc */
> +extern void update_reverse_alias_interface	(tree);
>   extern void record_mangling			(tree, bool);
>   extern void overwrite_mangling			(tree, tree);
>   extern void note_mangling_alias			(tree, tree);
> @@ -7535,6 +7538,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_reverse_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 60f107d50c4c5..d2bad39d5e1c6 100644
> --- a/gcc/cp/decl.cc
> +++ b/gcc/cp/decl.cc
> @@ -3195,6 +3195,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
>   	      && TREE_STATIC (olddecl))))
>       make_decl_rtl (olddecl);
>   
> +  symtab_node::remap_reverse_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.
> @@ -3218,6 +3220,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);
>   	}
>       }
> @@ -10683,6 +10686,7 @@ grokfndecl (tree ctype,
>       {
>         cplus_decl_attributes (&decl, *attrlist, 0);
>         *attrlist = NULL_TREE;
> +      create_reverse_alias_decls (decl);
>       }
>   
>     if (DECL_HAS_CONTRACTS_P (decl))
> diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
> index b402befba6da4..4ed9e8c5e928e 100644
> --- a/gcc/cp/decl2.cc
> +++ b/gcc/cp/decl2.cc
> @@ -1773,6 +1773,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_reverse_alias (*decl);
> +
>     /* Propagate deprecation out to the template.  */
>     if (TREE_DEPRECATED (*decl))
>       if (tree ti = get_template_info (*decl))
> @@ -2129,6 +2132,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 reverse_aliases.  */
> +
> +void
> +update_reverse_alias_interface (tree decl)
> +{
> +  if (!decl_in_symtab_p (decl)
> +      || !symtab_node::get (decl))
> +    return;
> +
> +  FOR_EACH_REVERSE_ALIAS (reverse_alias, DECL_ATTRIBUTES (decl))
> +    {
> +      tree id = TREE_VALUE (TREE_VALUE (reverse_alias));
> +      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.  */
>   
> @@ -2936,6 +2980,8 @@ determine_visibility (tree decl)
>          translation unit, we can make the type internal.  */
>       constrain_visibility (decl, VISIBILITY_ANON, false);
>   
> +  update_reverse_alias_interface (decl);
> +
>     /* If visibility changed and DECL already has DECL_RTL, ensure
>        symbol flags are updated.  */
>     if ((DECL_VISIBILITY (decl) != orig_visibility
> @@ -3198,6 +3244,8 @@ tentative_decl_linkage (tree decl)
>         else if (VAR_P (decl))
>   	maybe_commonize_var (decl);
>       }
> +
> +  update_reverse_alias_interface (decl);
>   }
>   
>   /* DECL is a FUNCTION_DECL or VAR_DECL.  If the object file linkage
> @@ -3432,6 +3480,8 @@ import_export_decl (tree decl)
>       }
>   
>     DECL_INTERFACE_KNOWN (decl) = 1;
> +
> +  update_reverse_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 74565184403c1..8bc15c68ac579 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"
> @@ -3466,6 +3468,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_reverse_alias_target (decl, alias);
> +	  DECL_ATTRIBUTES (decl)
> +	    = remove_attribute ("reverse_alias", DECL_ATTRIBUTES (alias));
> +	}
>       }
>   
>     retrofit_lang_decl (decl);
> diff --git a/gcc/cp/optimize.cc b/gcc/cp/optimize.cc
> index 9e8926e4cc603..bc4f816d46290 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_reverse_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 7878929c24679..35b530c83ba34 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 reverse_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,18 @@ 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 reverse_alias attributes from the type to the rtti obj decl.  */
> +      tree *attrs = &DECL_ATTRIBUTES (d);
> +      FOR_EACH_REVERSE_ALIAS (reverse_alias, TYPE_ATTRIBUTES (type))
> +	{
> +	  tree attr = tree_cons (TREE_PURPOSE (reverse_alias),
> +				 TREE_VALUE (reverse_alias),
> +				 *attrs);
> +	  *attrs = attr;
> +	  attrs = &TREE_CHAIN (attr);
> +	}
> +      create_reverse_alias_decls (d);
> +
>         /* Add decl to the global array of tinfo decls.  */
>         vec_safe_push (unemitted_tinfo_decls, d);
>       }
> @@ -499,6 +518,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 reverse_alias attributes
> +   that were not already present.  */
> +
> +void
> +update_tinfo_reverse_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_REVERSE_ALIAS (reverse_alias, TYPE_ATTRIBUTES (type))
> +    {
> +      bool found = false;
> +      FOR_EACH_REVERSE_ALIAS (d_reverse_alias, *attrs)
> +	if (TREE_VALUE (reverse_alias) == TREE_VALUE (d_reverse_alias))
> +	  {
> +	    found = true;
> +	    break;
> +	  }
> +
> +      if (found)
> +	continue;
> +
> +      tree attr = tree_cons (TREE_PURPOSE (reverse_alias),
> +			     TREE_VALUE (reverse_alias),
> +			     *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 (reverse_alias));
> +      id = get_identifier (TREE_STRING_POINTER (id));
> +      create_reverse_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 dda35358ce746..194312addf3f1 100644
> --- a/gcc/doc/extend.texi
> +++ b/gcc/doc/extend.texi
> @@ -3932,6 +3932,39 @@ function.  Examples of such functions are @code{setjmp} and @code{vfork}.
>   The @code{longjmp}-like counterpart of such function, if any, might need
>   to be marked with the @code{noreturn} attribute.
>   
> +@cindex @code{reverse_alias} function attribute
> +@item reverse_alias ("@var{name}")
> +The @code{reverse_alias} attribute causes @var{name} to be emitted as an
> +alias to the definition.  For instance,
> +
> +@smallexample
> +void f (uint64_t) __attribute__ ((__reverse_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,
> +and may not be available on all targets.
> +
>   @cindex @code{section} function attribute
>   @cindex functions in arbitrary sections
>   @item section ("@var{section-name}")
> @@ -7535,6 +7568,10 @@ align them on any target.
>   The @code{aligned} attribute can also be used for functions
>   (@pxref{Common Function Attributes}.)
>   
> +@cindex @code{reverse_alias} variable attribute
> +@item reverse_alias ("@var{name}")
> +See @pxref{Common Function Attributes}.
> +
>   @cindex @code{warn_if_not_aligned} variable attribute
>   @item warn_if_not_aligned (@var{alignment})
>   This attribute specifies a threshold for the structure field, measured
> @@ -7715,6 +7752,21 @@ The @code{unavailable} attribute can also be used for functions and
>   types (@pxref{Common Function Attributes},
>   @pxref{Common Type Attributes}).
>   
> +@cindex @code{reverse_alias} type attribute
> +@item reverse_alias ("@var{name}")
> +The @code{reverse_alias} 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__ ((__reverse_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{mode} variable attribute
>   @item mode (@var{mode})
>   This attribute specifies the data type for the declaration---whichever
> diff --git a/gcc/symtab.cc b/gcc/symtab.cc
> index 0470509a98d2a..4275550afc21c 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 reverse_alias nodes recorded as aliasing REPLACED to alias
> +   REPLACEMENT instead.  */
> +
> +void
> +symtab_node::remap_reverse_alias_target (tree replaced, tree replacement)
> +{
> +  if (!decl_in_symtab_p (replacement)
> +      || !symtab_node::get (replacement))
> +    return;
> +
> +  FOR_EACH_REVERSE_ALIAS (reverse_alias, DECL_ATTRIBUTES (replaced))
> +    {
> +      tree id = TREE_VALUE (TREE_VALUE (reverse_alias));
> +      id = get_identifier (TREE_STRING_POINTER (id));
> +
> +      symtab_node *sym_node = symtab_node::get_for_asmname (id);
> +
> +      if (!sym_node)
> +	{
> +	  create_reverse_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 46ee01b675950..f284289331807 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 e2e22be57e9e4..aec59b69754c5 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-revalias-1.c b/gcc/testsuite/c-c++-common/torture/attr-revalias-1.c
> new file mode 100644
> index 0000000000000..dd75e85a00105
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/torture/attr-revalias-1.c
> @@ -0,0 +1,39 @@
> +/* { dg-do compile } */
> +/* { dg-require-alias "" } */
> +
> +extern int var_a __attribute__ ((__reverse_alias__ ("FOOVAR_A")));
> +int var_a = 1;
> +
> +void foo_a () __attribute__ ((__reverse_alias__ ("FOOBAR_A")));
> +
> +void
> +foo_a ()
> +{
> +}
> +
> +
> +int var_b;
> +extern int var_b __attribute__ ((__reverse_alias__ ("FOOVAR_B")));
> +
> +void
> +foo_b ()
> +{
> +}
> +
> +void foo_b () __attribute__ ((__reverse_alias__ ("FOOBAR_B")));
> +
> +
> +int var_c __attribute__ ((__reverse_alias__ ("FOOVAR_C")));
> +
> +void __attribute__ ((__reverse_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-revalias-2.c b/gcc/testsuite/c-c++-common/torture/attr-revalias-2.c
> new file mode 100644
> index 0000000000000..8da45141e4985
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/torture/attr-revalias-2.c
> @@ -0,0 +1,13 @@
> +/* { dg-do compile } */
> +/* { dg-require-alias "" } */
> +
> +struct s
> +{
> +  int mem __attribute__ ((__reverse_alias__ ("MEMFOO"))); /* { dg-warning "attribute ignored" } */
> +};
> +
> +void foo()
> +{
> +  extern void bar () __attribute__ ((__reverse_alias__ ("FOOBAR")));
> +  int var __attribute__ ((__reverse_alias__ ("FOOVAR"))); /* { dg-warning "attribute ignored" } */
> +}
> diff --git a/gcc/testsuite/c-c++-common/torture/attr-revalias-3.c b/gcc/testsuite/c-c++-common/torture/attr-revalias-3.c
> new file mode 100644
> index 0000000000000..86a430894ed2d
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/torture/attr-revalias-3.c
> @@ -0,0 +1,41 @@
> +/* { dg-do compile } */
> +/* { dg-require-alias "" } */
> +
> +int var_a = 1;
> +
> +void
> +foo_a ()
> +{
> +  extern int var_a __attribute__ ((__reverse_alias__ ("FOOVAR_A")));
> +  void foo_a () __attribute__ ((__reverse_alias__ ("FOOBAR_A")));
> +}
> +
> +#if __cplusplus
> +/* Without this declaration before the local declaration below, the
> +   attributes of the local declaration do not get propagated to the
> +   (global) namespace scope.  */
> +extern int var_b;
> +#endif
> +
> +void
> +foo_b ()
> +{
> +  extern int var_b __attribute__ ((__reverse_alias__ ("FOOVAR_B")));
> +}
> +
> +int var_b;
> +
> +void __attribute__ ((__reverse_alias__ ("FOOBAR_C")))
> +foo_c ()
> +{
> +  void foo_b () __attribute__ ((__reverse_alias__ ("FOOBAR_B")));
> +  /* Another reverse_alias for var_b.  */
> +  extern int var_b __attribute__ ((__reverse_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-revalias-4.c b/gcc/testsuite/c-c++-common/torture/attr-revalias-4.c
> new file mode 100644
> index 0000000000000..66c3220703607
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/torture/attr-revalias-4.c
> @@ -0,0 +1,28 @@
> +/* { dg-do run } */
> +/* { dg-require-alias "" } */
> +
> +int var_a __attribute__ ((__reverse_alias__ ("FOOVAR_A"))) = 42;
> +
> +int __attribute__ ((__reverse_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-revalias-1.C b/gcc/testsuite/g++.dg/torture/attr-revalias-1.C
> new file mode 100644
> index 0000000000000..5d226c1d625bb
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/torture/attr-revalias-1.C
> @@ -0,0 +1,72 @@
> +/* { dg-do compile } */
> +/* { dg-require-alias "" } */
> +
> +class __attribute__ ((__reverse_alias__ ("FOOCLS_A"),
> +		      __reverse_alias__ ("FOOCLS_A_Dupe"))) foo {
> +  static int var __attribute__ ((__reverse_alias__ ("FOOVAR_A")));
> +  __attribute__ ((__reverse_alias__ ("FOOCTR_A"))) foo ();
> +  void __attribute__ ((__reverse_alias__ ("FOOBAR_A"))) bar ();
> +  virtual __attribute__ ((__reverse_alias__ ("FOODTR_A"))) ~foo() {}
> +};
> +
> +int foo::var = 1;
> +
> +foo::foo () {}
> +
> +void foo::bar () {}
> +
> +namespace b {
> +  class __attribute__ ((__reverse_alias__ ("FOOCLS_B"))) foo {
> +    static int var __attribute__ ((__reverse_alias__ ("FOOVAR_B")));
> +    __attribute__ ((__reverse_alias__ ("FOOCTR_B"))) foo ();
> +    void __attribute__ ((__reverse_alias__ ("FOOBAR_B"))) bar () {}
> +    virtual __attribute__ ((__reverse_alias__ ("FOODTR_B"))) ~foo() {}
> +  };
> +
> +  int foo::var = 2;
> +
> +  foo::foo () {
> +    void (foo::*pbar)() = &foo::bar;
> +  }
> +}
> +
> +namespace c {
> +  namespace cd {
> +    class __attribute__ ((__reverse_alias__ ("FOOCLS_C"))) foo {
> +      static int var __attribute__ ((__reverse_alias__ ("FOOVAR_C")));
> +      __attribute__ ((__reverse_alias__ ("FOOCTR_C"))) foo () {
> +	void (foo::*pbar)() = &foo::bar;
> +      }
> +      void __attribute__ ((__reverse_alias__ ("FOOBAR_C"))) bar () {}
> +      virtual __attribute__ ((__reverse_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-revalias-2.C b/gcc/testsuite/g++.dg/torture/attr-revalias-2.C
> new file mode 100644
> index 0000000000000..8f671929f1a53
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/torture/attr-revalias-2.C
> @@ -0,0 +1,26 @@
> +/* { dg-do compile } */
> +/* { dg-require-alias "" } */
> +
> +namespace {
> +  class __attribute__ ((__reverse_alias__ ("FOOCLS_A"))) foo {
> +    static int var __attribute__ ((__reverse_alias__ ("FOOVAR_A")));
> +    __attribute__ ((__reverse_alias__ ("FOOCTR_A"))) foo ();
> +    virtual __attribute__ ((__reverse_alias__ ("FOODTR_A"))) ~foo ();
> +    void __attribute__ ((__reverse_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-revalias-3.C b/gcc/testsuite/g++.dg/torture/attr-revalias-3.C
> new file mode 100644
> index 0000000000000..8c693d152f2c5
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/torture/attr-revalias-3.C
> @@ -0,0 +1,83 @@
> +/* { dg-do compile } */
> +/* { dg-require-alias "" } */
> +
> +// reverse_alias can be applied to template function explicit instantiations.
> +
> +template <typename T>
> +void
> +fn(T) {
> +};
> +
> +template void __attribute__ ((__reverse_alias__ ("FOOFUN_UINT"))) fn<>(unsigned int);
> +template void __attribute__ ((__reverse_alias__ ("FOOFUN_LONG"))) fn<>(long);
> +
> +template<> void __attribute__ ((__reverse_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__ ((__reverse_alias__ ("FOOCLS_CHAR_VIRT"))) foo<char>::virtfun();
> +
> +template class __attribute__ ((__reverse_alias__ ("FOOCLS_CHAR_TI"))) foo<char>;
> +
> +// Though they're only output if the enclosing class is.
> +template void
> +__attribute__ ((__reverse_alias__ ("FOOCLS_LONG_VIRT"))) foo<long>::virtfun();
> +extern
> +template class __attribute__ ((__reverse_alias__ ("FOOCLS_LONG_TI_X"))) foo<long>;
> +
> +
> +template void
> +__attribute__ ((__reverse_alias__ ("FOOCLS_VOID_ST"))) foo<void>::stfun();
> +
> +template class __attribute__ ((__reverse_alias__ ("FOOCLS_VOID_TI"))) foo<>;
> +
> +
> +extern
> +template class __attribute__ ((__reverse_alias__ ("FOOCLS_SHORT_TI_X"))) foo<short>;
> +
> +template void
> +__attribute__ ((__reverse_alias__ ("FOOCLS_SHORT_ST"))) foo<short>::stfun();
> +template void
> +__attribute__ ((__reverse_alias__ ("FOOCLS_SHORT_INL"))) foo<short>::inlfun();
> +
> +template class __attribute__ ((__reverse_alias__ ("FOOCLS_SHORT_TI_D"))) foo<short>;
> +
> +// Explicit specializations work too.
> +
> +template <>
> +struct  __attribute__ ((__reverse_alias__ ("FOOCLS_INT_TI")))
> +foo<int>
> +{
> +  virtual ~foo() {}
> +  virtual void __attribute__ ((__reverse_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-revalias-4.C b/gcc/testsuite/g++.dg/torture/attr-revalias-4.C
> new file mode 100644
> index 0000000000000..3b64f0eba4f8c
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/torture/attr-revalias-4.C
> @@ -0,0 +1,28 @@
> +/* { dg-do compile } */
> +/* { dg-require-alias "" } */
> +
> +template <typename T = void>
> +class
> +__attribute__ ((__reverse_alias__ ("FOOCLS")))
> +foo // { dg-error "duplicate|already" }
> +{
> +  virtual ~foo() {}
> +
> +  template <typename U>
> +  void
> +    __attribute__ ((__reverse_alias__ ("FOOTMF")))
> +    tmemfun () {} // { dg-error "duplicate|already" }
> +};
> +
> +template <typename T>
> +void
> +__attribute__ ((__reverse_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-revalias-5.C b/gcc/testsuite/g++.dg/torture/attr-revalias-5.C
> new file mode 100644
> index 0000000000000..d90e960ef48be
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/torture/attr-revalias-5.C
> @@ -0,0 +1,14 @@
> +/* { dg-do compile { target c++11 } } */
> +/* { dg-require-alias "" } */
> +
> +struct foo {
> +  __attribute__ ((__reverse_alias__ ("FOOCTR_A"))) foo ();
> +  virtual __attribute__ ((__reverse_alias__ ("FOODTR_A"))) ~foo() {}
> +};
> +
> +foo::foo () {}
> +
> +// Make sure the inherited cdtors don't duplicate the reverse_aliases.
> +struct bar : foo {
> +  using foo::foo;
> +};
> diff --git a/gcc/varpool.cc b/gcc/varpool.cc
> index e7b51b15e4a84..4ac95b5757ecb 100644
> --- a/gcc/varpool.cc
> +++ b/gcc/varpool.cc
> @@ -163,6 +163,9 @@ varpool_node::get_create (tree decl)
>       }
>   
>     node->register_symbol ();
> +
> +  create_reverse_alias_decls (decl);
> +
>     return node;
>   }
>   
> 
> 

-- 
Nathan Sidwell


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

* Re: [PATCH v3] Introduce attribute reverse_alias
  2023-07-15 21:55     ` Nathan Sidwell
@ 2023-07-18  4:29       ` Alexandre Oliva
  2023-07-18 11:37         ` Richard Biener
  0 siblings, 1 reply; 35+ messages in thread
From: Alexandre Oliva @ 2023-07-18  4:29 UTC (permalink / raw)
  To: Nathan Sidwell; +Cc: gcc-patches, jason, joseph, hainque, ebotcazou

Hello, Nathan,

On Jul 15, 2023, Nathan Sidwell <nathan@acm.org> wrote:

> Not commenting on the semantics, but the name seems unfortunate (hello
> bikeshed).

Yeah, it's a bit challenging to express the concept, when the notion of
"alias" is kind of symmetric between decl and target, but the
previously-implemented extension attaches it to decl rather than to
target.  I tried "extra alias" before, but that didn't fly either.

Maybe I should give up and just recommend the use of asm ("name")
instead of allowing alternative names (AKA aliases, in the dictionary
sense; oh, the irony) to be introduced for a decl?  Maybe that would be
simpler and enough to sidestep the problem of varying mangled names when
trying to import into Ada (or defining C++ aliases for) C++ symbols that
use standard types in signatures that are not fundamental types, such as
size_t.  That they mangle differently depending on what size_t is
typedef'ed to makes for some undesirable inconvenience, which this
attribute attempts to alleviate.

> The documentation starts with 'attribute causes @var{name}
> to be emitted as an alias to the definition'.  So not emitting a
> 'reverse alias', whatever that might be.

It's reverse in that it doesn't alias another declaration, as in the
preexisting meaning of the alias attribute, it adds an alias for the
present declaration.

> It doesn;t seem to mention how reverse alias differs from 'alias'.
> Why would 'alias' not DTRT?

contrast:

int foo();
int __attribute__ ((alias ("foo"))) bar();

static_assert (&foo == &bar); // ok

with:

int __attribute__ ((reverse_alias ("bar"))) foo();

static_assert (&foo == &bar); // error, bar is not a C++ symbol

int __attribute__ ((alias ("bar"))) baz(); // ok

static_assert (&foo == &baz); // ok

asm (".quad bar"); // ok, even in other TUs
asm (".quad foo"); // not necessarily ok, foo's symbol may be mangled
asm (".quad baz"); // not necessarily ok, baz's symbol may be mangled

> Is is emitting a an additiona symbol -- ie, something like 'altname'.

Yup.  Is there precedent for this attribute name elsewhere?  I think it
could work.

> Is that symbol known in the current TU, or other TUs?

Only in the assembly/linker name space, not in any C++ namespace.

-- 
Alexandre Oliva, happy hacker                https://FSFLA.org/blogs/lxo/
   Free Software Activist                       GNU Toolchain Engineer
Disinformation flourishes because many people care deeply about injustice
but very few check the facts.  Ask me about <https://stallmansupport.org>

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

* Re: [PATCH v3] Introduce attribute reverse_alias
  2023-07-18  4:29       ` Alexandre Oliva
@ 2023-07-18 11:37         ` Richard Biener
  2023-07-19 23:11           ` [PATCH v4] Introduce attribute sym Alexandre Oliva
  0 siblings, 1 reply; 35+ messages in thread
From: Richard Biener @ 2023-07-18 11:37 UTC (permalink / raw)
  To: Alexandre Oliva, Jan Hubicka
  Cc: Nathan Sidwell, gcc-patches, jason, joseph, hainque, ebotcazou

On Tue, Jul 18, 2023 at 6:29 AM Alexandre Oliva via Gcc-patches
<gcc-patches@gcc.gnu.org> wrote:
>
> Hello, Nathan,
>
> On Jul 15, 2023, Nathan Sidwell <nathan@acm.org> wrote:
>
> > Not commenting on the semantics, but the name seems unfortunate (hello
> > bikeshed).
>
> Yeah, it's a bit challenging to express the concept, when the notion of
> "alias" is kind of symmetric between decl and target, but the
> previously-implemented extension attaches it to decl rather than to
> target.  I tried "extra alias" before, but that didn't fly either.
>
> Maybe I should give up and just recommend the use of asm ("name")
> instead of allowing alternative names (AKA aliases, in the dictionary
> sense; oh, the irony) to be introduced for a decl?  Maybe that would be
> simpler and enough to sidestep the problem of varying mangled names when
> trying to import into Ada (or defining C++ aliases for) C++ symbols that
> use standard types in signatures that are not fundamental types, such as
> size_t.  That they mangle differently depending on what size_t is
> typedef'ed to makes for some undesirable inconvenience, which this
> attribute attempts to alleviate.
>
> > The documentation starts with 'attribute causes @var{name}
> > to be emitted as an alias to the definition'.  So not emitting a
> > 'reverse alias', whatever that might be.
>
> It's reverse in that it doesn't alias another declaration, as in the
> preexisting meaning of the alias attribute, it adds an alias for the
> present declaration.
>
> > It doesn;t seem to mention how reverse alias differs from 'alias'.
> > Why would 'alias' not DTRT?
>
> contrast:
>
> int foo();
> int __attribute__ ((alias ("foo"))) bar();
>
> static_assert (&foo == &bar); // ok
>
> with:
>
> int __attribute__ ((reverse_alias ("bar"))) foo();
>
> static_assert (&foo == &bar); // error, bar is not a C++ symbol
>
> int __attribute__ ((alias ("bar"))) baz(); // ok
>
> static_assert (&foo == &baz); // ok
>
> asm (".quad bar"); // ok, even in other TUs
> asm (".quad foo"); // not necessarily ok, foo's symbol may be mangled
> asm (".quad baz"); // not necessarily ok, baz's symbol may be mangled
>
> > Is is emitting a an additiona symbol -- ie, something like 'altname'.
>
> Yup.  Is there precedent for this attribute name elsewhere?  I think it
> could work.

I think the __symver__ attribute does something similar already so
maybe use __attribute__((__sym__("foo")))?

Richard.

> > Is that symbol known in the current TU, or other TUs?
>
> Only in the assembly/linker name space, not in any C++ namespace.
>
> --
> Alexandre Oliva, happy hacker                https://FSFLA.org/blogs/lxo/
>    Free Software Activist                       GNU Toolchain Engineer
> Disinformation flourishes because many people care deeply about injustice
> but very few check the facts.  Ask me about <https://stallmansupport.org>

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

* [PATCH v4] Introduce attribute sym
  2023-07-18 11:37         ` Richard Biener
@ 2023-07-19 23:11           ` Alexandre Oliva
  2023-07-20 13:09             ` Richard Biener
                               ` (2 more replies)
  0 siblings, 3 replies; 35+ messages in thread
From: Alexandre Oliva @ 2023-07-19 23:11 UTC (permalink / raw)
  To: Richard Biener
  Cc: Jan Hubicka, Nathan Sidwell, gcc-patches, jason, joseph, hainque,
	ebotcazou

On Jul 18, 2023, Richard Biener <richard.guenther@gmail.com> wrote:

> I think the __symver__ attribute does something similar already so
> maybe use __attribute__((__sym__("foo")))?

Cool, thanks, that will do.  Regstrapped on x86_64-linux-gnu.  Ok to
install?


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): Document for variables, functions and
	types.

for  gcc/ada/ChangeLog

	* doc/gnat_rm/interfacing_to_other_languages.rst: Mention
	attribute sym 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_attribute): New.
	(c_common_attribute_table): Add sym.
	(handle_copy_attribute): Do not copy sym 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 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-1.c: New.
	* c-c++-common/torture/attr-sym-2.c: New.
	* c-c++-common/torture/attr-sym-3.c: New.
	* c-c++-common/torture/attr-sym-4.c: New.
	* g++.dg/torture/attr-sym-1.C: New.
	* g++.dg/torture/attr-sym-2.C: New.
	* g++.dg/torture/attr-sym-3.C: New.
	* g++.dg/torture/attr-sym-4.C: New.
	* g++.dg/torture/attr-sym-5.C: New.
---
 .../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 ++-
 gcc/testsuite/c-c++-common/torture/attr-sym-1.c    |   39 +++++++++
 gcc/testsuite/c-c++-common/torture/attr-sym-2.c    |   13 +++
 gcc/testsuite/c-c++-common/torture/attr-sym-3.c    |   41 ++++++++++
 gcc/testsuite/c-c++-common/torture/attr-sym-4.c    |   28 +++++++
 gcc/testsuite/g++.dg/torture/attr-sym-1.C          |   72 +++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-2.C          |   26 ++++++
 gcc/testsuite/g++.dg/torture/attr-sym-3.C          |   83 ++++++++++++++++++++
 gcc/testsuite/g++.dg/torture/attr-sym-4.C          |   28 +++++++
 gcc/testsuite/g++.dg/torture/attr-sym-5.C          |   14 +++
 gcc/varpool.cc                                     |    3 +
 31 files changed, 784 insertions(+), 19 deletions(-)
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-sym-1.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-sym-2.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-sym-3.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-sym-4.c
 create mode 100644 gcc/testsuite/g++.dg/torture/attr-sym-1.C
 create mode 100644 gcc/testsuite/g++.dg/torture/attr-sym-2.C
 create mode 100644 gcc/testsuite/g++.dg/torture/attr-sym-3.C
 create mode 100644 gcc/testsuite/g++.dg/torture/attr-sym-4.C
 create mode 100644 gcc/testsuite/g++.dg/torture/attr-sym-5.C

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 ad0be511d4800..5e30912e434aa 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`` 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 148d40815b8f8..fcae7e912ef36 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__ ("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__ ("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 b8cb55b97df38..cd394f4c5f608 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"
@@ -819,7 +820,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", name))
 	{
 	  warning (OPT_Wattributes, "type attributes ignored after type is already defined");
 	  continue;
@@ -2631,6 +2633,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";
+
+  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 84a43658a70da..734fa83f8d572 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", (attrs));			\
+       sym; sym = lookup_attribute ("sym", 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 050994d841665..5042b9cfecd80 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 e2792ca6898b3..f5d72cef49794 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_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,
@@ -383,6 +384,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",                    1, 1, false,  false, false, false,
+			      handle_sym_attribute, NULL },
   { "no_instrument_function", 0, 0, true,  false, false, false,
 			      handle_no_instrument_function_attribute,
 			      NULL },
@@ -2855,7 +2858,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
@@ -2865,7 +2868,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
@@ -2875,6 +2878,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" attribute; arguments as in struct
+   attribute_spec.handler.  */
+
+static tree
+handle_sym_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.  */
 
@@ -3008,6 +3034,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", atname)
 	      || is_attribute_p ("target_clones", atname))
 	    continue;
 
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index ecd10ebb69caf..5d6ce11cbb3ab 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -3073,6 +3073,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 e41e5ad3ae74d..c45ce2d0a1332 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 cedaaac3a45b7..6a444e6fa5bcf 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 bccd2f2abb5a3..eb2d05094e989 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 778759237dc72..d6218bab28b21 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -4859,6 +4859,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", 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.  */
@@ -5026,6 +5088,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 3de0e154c124c..0a998d0bafd15 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5747,6 +5747,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.  */
@@ -6960,6 +6962,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);
@@ -7535,6 +7538,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 60f107d50c4c5..28f7dbaf35d75 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -3195,6 +3195,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.
@@ -3218,6 +3220,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);
 	}
     }
@@ -10683,6 +10686,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 b402befba6da4..68232b8863f16 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -1773,6 +1773,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))
@@ -2129,6 +2132,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.  */
 
@@ -2936,6 +2980,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
@@ -3198,6 +3244,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
@@ -3432,6 +3480,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 74565184403c1..12e4c78086016 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"
@@ -3466,6 +3468,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", DECL_ATTRIBUTES (alias));
+	}
     }
 
   retrofit_lang_decl (decl);
diff --git a/gcc/cp/optimize.cc b/gcc/cp/optimize.cc
index 9e8926e4cc603..4a2e8cb435404 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 7878929c24679..4bb7b8c8c6e78 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 dda35358ce746..4ffe06dc805cd 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -4057,6 +4057,39 @@ Function Attributes}, @ref{PowerPC Function Attributes},
 @ref{Nios II Function Attributes}, and @ref{S/390 Function Attributes}
 for details.
 
+@cindex @code{sym} function attribute
+@item sym ("@var{name}")
+The @code{sym} attribute causes @var{name} to be output as an alias to
+the definition.  For instance,
+
+@smallexample
+void f (uint64_t) __attribute__ ((__sym__ ("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
@@ -7873,6 +7906,10 @@ will be placed in new, unique sections.
 
 This additional functionality requires Binutils version 2.36 or later.
 
+@cindex @code{sym} variable attribute
+@item sym ("@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
@@ -8902,6 +8939,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} type attribute
+@item sym ("@var{name}")
+The @code{sym} 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__ ("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 0470509a98d2a..0812088e59f38 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 46ee01b675950..f284289331807 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 e2e22be57e9e4..aec59b69754c5 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-1.c b/gcc/testsuite/c-c++-common/torture/attr-sym-1.c
new file mode 100644
index 0000000000000..6254c4ffd35bb
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-1.c
@@ -0,0 +1,39 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+extern int var_a __attribute__ ((__sym__ ("FOOVAR_A")));
+int var_a = 1;
+
+void foo_a () __attribute__ ((__sym__ ("FOOBAR_A")));
+
+void
+foo_a ()
+{
+}
+
+
+int var_b;
+extern int var_b __attribute__ ((__sym__ ("FOOVAR_B")));
+
+void
+foo_b ()
+{
+}
+
+void foo_b () __attribute__ ((__sym__ ("FOOBAR_B")));
+
+
+int var_c __attribute__ ((__sym__ ("FOOVAR_C")));
+
+void __attribute__ ((__sym__ ("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-2.c b/gcc/testsuite/c-c++-common/torture/attr-sym-2.c
new file mode 100644
index 0000000000000..62a62d772f40c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-2.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+struct s
+{
+  int mem __attribute__ ((__sym__ ("MEMFOO"))); /* { dg-warning "attribute ignored" } */
+};
+
+void foo()
+{
+  extern void bar () __attribute__ ((__sym__ ("FOOBAR")));
+  int var __attribute__ ((__sym__ ("FOOVAR"))); /* { dg-warning "attribute ignored" } */
+}
diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-3.c b/gcc/testsuite/c-c++-common/torture/attr-sym-3.c
new file mode 100644
index 0000000000000..5f0da2813fc37
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-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__ ("FOOVAR_A")));
+  void foo_a () __attribute__ ((__sym__ ("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__ ("FOOVAR_B")));
+}
+
+int var_b;
+
+void __attribute__ ((__sym__ ("FOOBAR_C")))
+foo_c ()
+{
+  void foo_b () __attribute__ ((__sym__ ("FOOBAR_B")));
+  /* Another sym for var_b.  */
+  extern int var_b __attribute__ ((__sym__ ("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-4.c b/gcc/testsuite/c-c++-common/torture/attr-sym-4.c
new file mode 100644
index 0000000000000..93312a38de61c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-sym-4.c
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-require-alias "" } */
+
+int var_a __attribute__ ((__sym__ ("FOOVAR_A"))) = 42;
+
+int __attribute__ ((__sym__ ("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-1.C b/gcc/testsuite/g++.dg/torture/attr-sym-1.C
new file mode 100644
index 0000000000000..11aaabfa6599c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-1.C
@@ -0,0 +1,72 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+class __attribute__ ((__sym__ ("FOOCLS_A"),
+		      __sym__ ("FOOCLS_A_Dupe"))) foo {
+  static int var __attribute__ ((__sym__ ("FOOVAR_A")));
+  __attribute__ ((__sym__ ("FOOCTR_A"))) foo ();
+  void __attribute__ ((__sym__ ("FOOBAR_A"))) bar ();
+  virtual __attribute__ ((__sym__ ("FOODTR_A"))) ~foo() {}
+};
+
+int foo::var = 1;
+
+foo::foo () {}
+
+void foo::bar () {}
+
+namespace b {
+  class __attribute__ ((__sym__ ("FOOCLS_B"))) foo {
+    static int var __attribute__ ((__sym__ ("FOOVAR_B")));
+    __attribute__ ((__sym__ ("FOOCTR_B"))) foo ();
+    void __attribute__ ((__sym__ ("FOOBAR_B"))) bar () {}
+    virtual __attribute__ ((__sym__ ("FOODTR_B"))) ~foo() {}
+  };
+
+  int foo::var = 2;
+
+  foo::foo () {
+    void (foo::*pbar)() = &foo::bar;
+  }
+}
+
+namespace c {
+  namespace cd {
+    class __attribute__ ((__sym__ ("FOOCLS_C"))) foo {
+      static int var __attribute__ ((__sym__ ("FOOVAR_C")));
+      __attribute__ ((__sym__ ("FOOCTR_C"))) foo () {
+	void (foo::*pbar)() = &foo::bar;
+      }
+      void __attribute__ ((__sym__ ("FOOBAR_C"))) bar () {}
+      virtual __attribute__ ((__sym__ ("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-2.C b/gcc/testsuite/g++.dg/torture/attr-sym-2.C
new file mode 100644
index 0000000000000..f4714ea1ce5bd
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-2.C
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+namespace {
+  class __attribute__ ((__sym__ ("FOOCLS_A"))) foo {
+    static int var __attribute__ ((__sym__ ("FOOVAR_A")));
+    __attribute__ ((__sym__ ("FOOCTR_A"))) foo ();
+    virtual __attribute__ ((__sym__ ("FOODTR_A"))) ~foo ();
+    void __attribute__ ((__sym__ ("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-3.C b/gcc/testsuite/g++.dg/torture/attr-sym-3.C
new file mode 100644
index 0000000000000..f819d5a02ce47
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-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__ ("FOOFUN_UINT"))) fn<>(unsigned int);
+template void __attribute__ ((__sym__ ("FOOFUN_LONG"))) fn<>(long);
+
+template<> void __attribute__ ((__sym__ ("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__ ("FOOCLS_CHAR_VIRT"))) foo<char>::virtfun();
+
+template class __attribute__ ((__sym__ ("FOOCLS_CHAR_TI"))) foo<char>;
+
+// Though they're only output if the enclosing class is.
+template void
+__attribute__ ((__sym__ ("FOOCLS_LONG_VIRT"))) foo<long>::virtfun();
+extern
+template class __attribute__ ((__sym__ ("FOOCLS_LONG_TI_X"))) foo<long>;
+
+
+template void
+__attribute__ ((__sym__ ("FOOCLS_VOID_ST"))) foo<void>::stfun();
+
+template class __attribute__ ((__sym__ ("FOOCLS_VOID_TI"))) foo<>;
+
+
+extern
+template class __attribute__ ((__sym__ ("FOOCLS_SHORT_TI_X"))) foo<short>;
+
+template void
+__attribute__ ((__sym__ ("FOOCLS_SHORT_ST"))) foo<short>::stfun();
+template void
+__attribute__ ((__sym__ ("FOOCLS_SHORT_INL"))) foo<short>::inlfun();
+
+template class __attribute__ ((__sym__ ("FOOCLS_SHORT_TI_D"))) foo<short>;
+
+// Explicit specializations work too.
+
+template <>
+struct  __attribute__ ((__sym__ ("FOOCLS_INT_TI")))
+foo<int>
+{
+  virtual ~foo() {}
+  virtual void __attribute__ ((__sym__ ("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-4.C b/gcc/testsuite/g++.dg/torture/attr-sym-4.C
new file mode 100644
index 0000000000000..cc6a25a7962e6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-4.C
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+template <typename T = void>
+class
+__attribute__ ((__sym__ ("FOOCLS")))
+foo // { dg-error "duplicate|already" }
+{
+  virtual ~foo() {}
+
+  template <typename U>
+  void
+    __attribute__ ((__sym__ ("FOOTMF")))
+    tmemfun () {} // { dg-error "duplicate|already" }
+};
+
+template <typename T>
+void
+__attribute__ ((__sym__ ("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-5.C b/gcc/testsuite/g++.dg/torture/attr-sym-5.C
new file mode 100644
index 0000000000000..2f79408b8d8ec
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/attr-sym-5.C
@@ -0,0 +1,14 @@
+/* { dg-do compile { target c++11 } } */
+/* { dg-require-alias "" } */
+
+struct foo {
+  __attribute__ ((__sym__ ("FOOCTR_A"))) foo ();
+  virtual __attribute__ ((__sym__ ("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 e7b51b15e4a84..33649906d1d11 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;
 }
 


-- 
Alexandre Oliva, happy hacker                https://FSFLA.org/blogs/lxo/
   Free Software Activist                       GNU Toolchain Engineer
Disinformation flourishes because many people care deeply about injustice
but very few check the facts.  Ask me about <https://stallmansupport.org>

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

* Re: [PATCH v4] Introduce attribute sym
  2023-07-19 23:11           ` [PATCH v4] Introduce attribute sym Alexandre Oliva
@ 2023-07-20 13:09             ` Richard Biener
  2023-07-21  9:23               ` Alexandre Oliva
  2023-07-22  3:12             ` Fangrui Song
       [not found]             ` <orpm2tgrsd.fsf_-_@lxoliva.fsfla.org>
  2 siblings, 1 reply; 35+ messages in thread
From: Richard Biener @ 2023-07-20 13:09 UTC (permalink / raw)
  To: Alexandre Oliva
  Cc: Jan Hubicka, Nathan Sidwell, gcc-patches, jason, joseph, hainque,
	ebotcazou

On Thu, Jul 20, 2023 at 1:11 AM Alexandre Oliva <oliva@adacore.com> wrote:
>
> On Jul 18, 2023, Richard Biener <richard.guenther@gmail.com> wrote:
>
> > I think the __symver__ attribute does something similar already so
> > maybe use __attribute__((__sym__("foo")))?
>
> Cool, thanks, that will do.  Regstrapped on x86_64-linux-gnu.  Ok to
> install?
>
>
> 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.

I wonder if we could have shared some of the cgraph/varasm bits
with the symver attribute handling?  It's just a new 'sym' but
without the version part?

I hope Honza can chime in here.

Thanks,
Richard.

> 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): Document for variables, functions and
>         types.
>
> for  gcc/ada/ChangeLog
>
>         * doc/gnat_rm/interfacing_to_other_languages.rst: Mention
>         attribute sym 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_attribute): New.
>         (c_common_attribute_table): Add sym.
>         (handle_copy_attribute): Do not copy sym 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 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-1.c: New.
>         * c-c++-common/torture/attr-sym-2.c: New.
>         * c-c++-common/torture/attr-sym-3.c: New.
>         * c-c++-common/torture/attr-sym-4.c: New.
>         * g++.dg/torture/attr-sym-1.C: New.
>         * g++.dg/torture/attr-sym-2.C: New.
>         * g++.dg/torture/attr-sym-3.C: New.
>         * g++.dg/torture/attr-sym-4.C: New.
>         * g++.dg/torture/attr-sym-5.C: New.
> ---
>  .../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 ++-
>  gcc/testsuite/c-c++-common/torture/attr-sym-1.c    |   39 +++++++++
>  gcc/testsuite/c-c++-common/torture/attr-sym-2.c    |   13 +++
>  gcc/testsuite/c-c++-common/torture/attr-sym-3.c    |   41 ++++++++++
>  gcc/testsuite/c-c++-common/torture/attr-sym-4.c    |   28 +++++++
>  gcc/testsuite/g++.dg/torture/attr-sym-1.C          |   72 +++++++++++++++++
>  gcc/testsuite/g++.dg/torture/attr-sym-2.C          |   26 ++++++
>  gcc/testsuite/g++.dg/torture/attr-sym-3.C          |   83 ++++++++++++++++++++
>  gcc/testsuite/g++.dg/torture/attr-sym-4.C          |   28 +++++++
>  gcc/testsuite/g++.dg/torture/attr-sym-5.C          |   14 +++
>  gcc/varpool.cc                                     |    3 +
>  31 files changed, 784 insertions(+), 19 deletions(-)
>  create mode 100644 gcc/testsuite/c-c++-common/torture/attr-sym-1.c
>  create mode 100644 gcc/testsuite/c-c++-common/torture/attr-sym-2.c
>  create mode 100644 gcc/testsuite/c-c++-common/torture/attr-sym-3.c
>  create mode 100644 gcc/testsuite/c-c++-common/torture/attr-sym-4.c
>  create mode 100644 gcc/testsuite/g++.dg/torture/attr-sym-1.C
>  create mode 100644 gcc/testsuite/g++.dg/torture/attr-sym-2.C
>  create mode 100644 gcc/testsuite/g++.dg/torture/attr-sym-3.C
>  create mode 100644 gcc/testsuite/g++.dg/torture/attr-sym-4.C
>  create mode 100644 gcc/testsuite/g++.dg/torture/attr-sym-5.C
>
> 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 ad0be511d4800..5e30912e434aa 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`` 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 148d40815b8f8..fcae7e912ef36 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__ ("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__ ("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 b8cb55b97df38..cd394f4c5f608 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"
> @@ -819,7 +820,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", name))
>         {
>           warning (OPT_Wattributes, "type attributes ignored after type is already defined");
>           continue;
> @@ -2631,6 +2633,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";
> +
> +  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 84a43658a70da..734fa83f8d572 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", (attrs));                   \
> +       sym; sym = lookup_attribute ("sym", 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 050994d841665..5042b9cfecd80 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 e2792ca6898b3..f5d72cef49794 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_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,
> @@ -383,6 +384,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",                    1, 1, false,  false, false, false,
> +                             handle_sym_attribute, NULL },
>    { "no_instrument_function", 0, 0, true,  false, false, false,
>                               handle_no_instrument_function_attribute,
>                               NULL },
> @@ -2855,7 +2858,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
> @@ -2865,7 +2868,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
> @@ -2875,6 +2878,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" attribute; arguments as in struct
> +   attribute_spec.handler.  */
> +
> +static tree
> +handle_sym_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.  */
>
> @@ -3008,6 +3034,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", atname)
>               || is_attribute_p ("target_clones", atname))
>             continue;
>
> diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
> index ecd10ebb69caf..5d6ce11cbb3ab 100644
> --- a/gcc/c/c-decl.cc
> +++ b/gcc/c/c-decl.cc
> @@ -3073,6 +3073,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 e41e5ad3ae74d..c45ce2d0a1332 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 cedaaac3a45b7..6a444e6fa5bcf 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 bccd2f2abb5a3..eb2d05094e989 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 778759237dc72..d6218bab28b21 100644
> --- a/gcc/cp/class.cc
> +++ b/gcc/cp/class.cc
> @@ -4859,6 +4859,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", 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.  */
> @@ -5026,6 +5088,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 3de0e154c124c..0a998d0bafd15 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -5747,6 +5747,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.  */
> @@ -6960,6 +6962,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);
> @@ -7535,6 +7538,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 60f107d50c4c5..28f7dbaf35d75 100644
> --- a/gcc/cp/decl.cc
> +++ b/gcc/cp/decl.cc
> @@ -3195,6 +3195,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.
> @@ -3218,6 +3220,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);
>         }
>      }
> @@ -10683,6 +10686,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 b402befba6da4..68232b8863f16 100644
> --- a/gcc/cp/decl2.cc
> +++ b/gcc/cp/decl2.cc
> @@ -1773,6 +1773,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))
> @@ -2129,6 +2132,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.  */
>
> @@ -2936,6 +2980,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
> @@ -3198,6 +3244,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
> @@ -3432,6 +3480,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 74565184403c1..12e4c78086016 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"
> @@ -3466,6 +3468,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", DECL_ATTRIBUTES (alias));
> +       }
>      }
>
>    retrofit_lang_decl (decl);
> diff --git a/gcc/cp/optimize.cc b/gcc/cp/optimize.cc
> index 9e8926e4cc603..4a2e8cb435404 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 7878929c24679..4bb7b8c8c6e78 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 dda35358ce746..4ffe06dc805cd 100644
> --- a/gcc/doc/extend.texi
> +++ b/gcc/doc/extend.texi
> @@ -4057,6 +4057,39 @@ Function Attributes}, @ref{PowerPC Function Attributes},
>  @ref{Nios II Function Attributes}, and @ref{S/390 Function Attributes}
>  for details.
>
> +@cindex @code{sym} function attribute
> +@item sym ("@var{name}")
> +The @code{sym} attribute causes @var{name} to be output as an alias to
> +the definition.  For instance,
> +
> +@smallexample
> +void f (uint64_t) __attribute__ ((__sym__ ("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
> @@ -7873,6 +7906,10 @@ will be placed in new, unique sections.
>
>  This additional functionality requires Binutils version 2.36 or later.
>
> +@cindex @code{sym} variable attribute
> +@item sym ("@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
> @@ -8902,6 +8939,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} type attribute
> +@item sym ("@var{name}")
> +The @code{sym} 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__ ("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 0470509a98d2a..0812088e59f38 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 46ee01b675950..f284289331807 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 e2e22be57e9e4..aec59b69754c5 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-1.c b/gcc/testsuite/c-c++-common/torture/attr-sym-1.c
> new file mode 100644
> index 0000000000000..6254c4ffd35bb
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/torture/attr-sym-1.c
> @@ -0,0 +1,39 @@
> +/* { dg-do compile } */
> +/* { dg-require-alias "" } */
> +
> +extern int var_a __attribute__ ((__sym__ ("FOOVAR_A")));
> +int var_a = 1;
> +
> +void foo_a () __attribute__ ((__sym__ ("FOOBAR_A")));
> +
> +void
> +foo_a ()
> +{
> +}
> +
> +
> +int var_b;
> +extern int var_b __attribute__ ((__sym__ ("FOOVAR_B")));
> +
> +void
> +foo_b ()
> +{
> +}
> +
> +void foo_b () __attribute__ ((__sym__ ("FOOBAR_B")));
> +
> +
> +int var_c __attribute__ ((__sym__ ("FOOVAR_C")));
> +
> +void __attribute__ ((__sym__ ("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-2.c b/gcc/testsuite/c-c++-common/torture/attr-sym-2.c
> new file mode 100644
> index 0000000000000..62a62d772f40c
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/torture/attr-sym-2.c
> @@ -0,0 +1,13 @@
> +/* { dg-do compile } */
> +/* { dg-require-alias "" } */
> +
> +struct s
> +{
> +  int mem __attribute__ ((__sym__ ("MEMFOO"))); /* { dg-warning "attribute ignored" } */
> +};
> +
> +void foo()
> +{
> +  extern void bar () __attribute__ ((__sym__ ("FOOBAR")));
> +  int var __attribute__ ((__sym__ ("FOOVAR"))); /* { dg-warning "attribute ignored" } */
> +}
> diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-3.c b/gcc/testsuite/c-c++-common/torture/attr-sym-3.c
> new file mode 100644
> index 0000000000000..5f0da2813fc37
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/torture/attr-sym-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__ ("FOOVAR_A")));
> +  void foo_a () __attribute__ ((__sym__ ("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__ ("FOOVAR_B")));
> +}
> +
> +int var_b;
> +
> +void __attribute__ ((__sym__ ("FOOBAR_C")))
> +foo_c ()
> +{
> +  void foo_b () __attribute__ ((__sym__ ("FOOBAR_B")));
> +  /* Another sym for var_b.  */
> +  extern int var_b __attribute__ ((__sym__ ("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-4.c b/gcc/testsuite/c-c++-common/torture/attr-sym-4.c
> new file mode 100644
> index 0000000000000..93312a38de61c
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/torture/attr-sym-4.c
> @@ -0,0 +1,28 @@
> +/* { dg-do run } */
> +/* { dg-require-alias "" } */
> +
> +int var_a __attribute__ ((__sym__ ("FOOVAR_A"))) = 42;
> +
> +int __attribute__ ((__sym__ ("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-1.C b/gcc/testsuite/g++.dg/torture/attr-sym-1.C
> new file mode 100644
> index 0000000000000..11aaabfa6599c
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/torture/attr-sym-1.C
> @@ -0,0 +1,72 @@
> +/* { dg-do compile } */
> +/* { dg-require-alias "" } */
> +
> +class __attribute__ ((__sym__ ("FOOCLS_A"),
> +                     __sym__ ("FOOCLS_A_Dupe"))) foo {
> +  static int var __attribute__ ((__sym__ ("FOOVAR_A")));
> +  __attribute__ ((__sym__ ("FOOCTR_A"))) foo ();
> +  void __attribute__ ((__sym__ ("FOOBAR_A"))) bar ();
> +  virtual __attribute__ ((__sym__ ("FOODTR_A"))) ~foo() {}
> +};
> +
> +int foo::var = 1;
> +
> +foo::foo () {}
> +
> +void foo::bar () {}
> +
> +namespace b {
> +  class __attribute__ ((__sym__ ("FOOCLS_B"))) foo {
> +    static int var __attribute__ ((__sym__ ("FOOVAR_B")));
> +    __attribute__ ((__sym__ ("FOOCTR_B"))) foo ();
> +    void __attribute__ ((__sym__ ("FOOBAR_B"))) bar () {}
> +    virtual __attribute__ ((__sym__ ("FOODTR_B"))) ~foo() {}
> +  };
> +
> +  int foo::var = 2;
> +
> +  foo::foo () {
> +    void (foo::*pbar)() = &foo::bar;
> +  }
> +}
> +
> +namespace c {
> +  namespace cd {
> +    class __attribute__ ((__sym__ ("FOOCLS_C"))) foo {
> +      static int var __attribute__ ((__sym__ ("FOOVAR_C")));
> +      __attribute__ ((__sym__ ("FOOCTR_C"))) foo () {
> +       void (foo::*pbar)() = &foo::bar;
> +      }
> +      void __attribute__ ((__sym__ ("FOOBAR_C"))) bar () {}
> +      virtual __attribute__ ((__sym__ ("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-2.C b/gcc/testsuite/g++.dg/torture/attr-sym-2.C
> new file mode 100644
> index 0000000000000..f4714ea1ce5bd
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/torture/attr-sym-2.C
> @@ -0,0 +1,26 @@
> +/* { dg-do compile } */
> +/* { dg-require-alias "" } */
> +
> +namespace {
> +  class __attribute__ ((__sym__ ("FOOCLS_A"))) foo {
> +    static int var __attribute__ ((__sym__ ("FOOVAR_A")));
> +    __attribute__ ((__sym__ ("FOOCTR_A"))) foo ();
> +    virtual __attribute__ ((__sym__ ("FOODTR_A"))) ~foo ();
> +    void __attribute__ ((__sym__ ("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-3.C b/gcc/testsuite/g++.dg/torture/attr-sym-3.C
> new file mode 100644
> index 0000000000000..f819d5a02ce47
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/torture/attr-sym-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__ ("FOOFUN_UINT"))) fn<>(unsigned int);
> +template void __attribute__ ((__sym__ ("FOOFUN_LONG"))) fn<>(long);
> +
> +template<> void __attribute__ ((__sym__ ("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__ ("FOOCLS_CHAR_VIRT"))) foo<char>::virtfun();
> +
> +template class __attribute__ ((__sym__ ("FOOCLS_CHAR_TI"))) foo<char>;
> +
> +// Though they're only output if the enclosing class is.
> +template void
> +__attribute__ ((__sym__ ("FOOCLS_LONG_VIRT"))) foo<long>::virtfun();
> +extern
> +template class __attribute__ ((__sym__ ("FOOCLS_LONG_TI_X"))) foo<long>;
> +
> +
> +template void
> +__attribute__ ((__sym__ ("FOOCLS_VOID_ST"))) foo<void>::stfun();
> +
> +template class __attribute__ ((__sym__ ("FOOCLS_VOID_TI"))) foo<>;
> +
> +
> +extern
> +template class __attribute__ ((__sym__ ("FOOCLS_SHORT_TI_X"))) foo<short>;
> +
> +template void
> +__attribute__ ((__sym__ ("FOOCLS_SHORT_ST"))) foo<short>::stfun();
> +template void
> +__attribute__ ((__sym__ ("FOOCLS_SHORT_INL"))) foo<short>::inlfun();
> +
> +template class __attribute__ ((__sym__ ("FOOCLS_SHORT_TI_D"))) foo<short>;
> +
> +// Explicit specializations work too.
> +
> +template <>
> +struct  __attribute__ ((__sym__ ("FOOCLS_INT_TI")))
> +foo<int>
> +{
> +  virtual ~foo() {}
> +  virtual void __attribute__ ((__sym__ ("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-4.C b/gcc/testsuite/g++.dg/torture/attr-sym-4.C
> new file mode 100644
> index 0000000000000..cc6a25a7962e6
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/torture/attr-sym-4.C
> @@ -0,0 +1,28 @@
> +/* { dg-do compile } */
> +/* { dg-require-alias "" } */
> +
> +template <typename T = void>
> +class
> +__attribute__ ((__sym__ ("FOOCLS")))
> +foo // { dg-error "duplicate|already" }
> +{
> +  virtual ~foo() {}
> +
> +  template <typename U>
> +  void
> +    __attribute__ ((__sym__ ("FOOTMF")))
> +    tmemfun () {} // { dg-error "duplicate|already" }
> +};
> +
> +template <typename T>
> +void
> +__attribute__ ((__sym__ ("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-5.C b/gcc/testsuite/g++.dg/torture/attr-sym-5.C
> new file mode 100644
> index 0000000000000..2f79408b8d8ec
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/torture/attr-sym-5.C
> @@ -0,0 +1,14 @@
> +/* { dg-do compile { target c++11 } } */
> +/* { dg-require-alias "" } */
> +
> +struct foo {
> +  __attribute__ ((__sym__ ("FOOCTR_A"))) foo ();
> +  virtual __attribute__ ((__sym__ ("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 e7b51b15e4a84..33649906d1d11 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;
>  }
>
>
>
> --
> Alexandre Oliva, happy hacker                https://FSFLA.org/blogs/lxo/
>    Free Software Activist                       GNU Toolchain Engineer
> Disinformation flourishes because many people care deeply about injustice
> but very few check the facts.  Ask me about <https://stallmansupport.org>

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

* Re: [PATCH v4] Introduce attribute sym
  2023-07-20 13:09             ` Richard Biener
@ 2023-07-21  9:23               ` Alexandre Oliva
  0 siblings, 0 replies; 35+ messages in thread
From: Alexandre Oliva @ 2023-07-21  9:23 UTC (permalink / raw)
  To: Richard Biener
  Cc: Jan Hubicka, Nathan Sidwell, gcc-patches, jason, joseph, hainque,
	ebotcazou

On Jul 20, 2023, Richard Biener <richard.guenther@gmail.com> wrote:

> I wonder if we could have shared some of the cgraph/varasm bits
> with the symver attribute handling?  It's just a new 'sym' but
> without the version part?

Possibly.  process_common_attributes could be a good place to create the
alias decl, like symver does.  But that wouldn't cover clones of C++
ctors and dtors, that get variants of the named sym, nor sym attributes
attached to C++ classes.  Aside from these special cases, it is an alias
declaration, without much else to do, which is not very much unlike
symver, but the named sym alias needs to be introduced in the symtab
early enough that other (non-sym) alias declarations can refer to it,
which symver doesn't need to worry about.

-- 
Alexandre Oliva, happy hacker                https://FSFLA.org/blogs/lxo/
   Free Software Activist                       GNU Toolchain Engineer
Disinformation flourishes because many people care deeply about injustice
but very few check the facts.  Ask me about <https://stallmansupport.org>

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

* Re: [PATCH v4] Introduce attribute sym
  2023-07-19 23:11           ` [PATCH v4] Introduce attribute sym Alexandre Oliva
  2023-07-20 13:09             ` Richard Biener
@ 2023-07-22  3:12             ` Fangrui Song
  2023-08-16  4:27               ` Alexandre Oliva
       [not found]             ` <orpm2tgrsd.fsf_-_@lxoliva.fsfla.org>
  2 siblings, 1 reply; 35+ messages in thread
From: Fangrui Song @ 2023-07-22  3:12 UTC (permalink / raw)
  To: Alexandre Oliva
  Cc: Richard Biener, Jan Hubicka, Nathan Sidwell, gcc-patches, jason,
	joseph, hainque, ebotcazou

On Wed, Jul 19, 2023 at 4:12 PM Alexandre Oliva via Gcc-patches
<gcc-patches@gcc.gnu.org> wrote:
>
> On Jul 18, 2023, Richard Biener <richard.guenther@gmail.com> wrote:
>
> > I think the __symver__ attribute does something similar already so
> > maybe use __attribute__((__sym__("foo")))?
>
> Cool, thanks, that will do.  Regstrapped on x86_64-linux-gnu.  Ok to
> install?
>
>
> 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.

I wonder whether this attribute can be named "alias" without arguments.
alias ("target") is an existing attribute that applies to a
declaration. The new "alias" without arguments can apply to
definitions.

I am just thinking that the semantics of "sym" may confuse users who
are familiar with "alias" :)

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

* Re: [PATCH v4] Introduce attribute sym
  2023-07-22  3:12             ` Fangrui Song
@ 2023-08-16  4:27               ` Alexandre Oliva
  0 siblings, 0 replies; 35+ messages in thread
From: Alexandre Oliva @ 2023-08-16  4:27 UTC (permalink / raw)
  To: Fangrui Song
  Cc: Richard Biener, Jan Hubicka, Nathan Sidwell, gcc-patches, jason,
	joseph, hainque, ebotcazou

On Jul 22, 2023, Fangrui Song <maskray@google.com> wrote:

> I wonder whether this attribute can be named "alias" without arguments.

Erhm...  Maybe I'm missing something about your suggestion, but without
arguments, how would we tell the compiler the symbol name of the
additional alias we want for the definition?

Maybe, instead of no arguments, we could use something like:

  attribute (alias (..., "alt_sym_name"))

but I don't find that clearer, and I have a hunch that the
implementation would be significantly more convoluted.

-- 
Alexandre Oliva, happy hacker                    https://FSFLA.org/blogs/lxo/
   Free Software Activist                           GNU Toolchain Engineer
Disinformation flourishes because many people care deeply about injustice but
very few check the facts.  Think Assange & Stallman.  The empires strike back

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

* [PATCH v5] Introduce attribute sym_alias (was: Last call for bikeshedding on attribute sym/exalias/reverse_alias)
       [not found]                 ` <ory1h9t6nr.fsf@lxoliva.fsfla.org>
@ 2023-09-20  5:59                   ` Alexandre Oliva
  2023-11-20 12:54                     ` [PATCH v5] Introduce attribute sym_alias Alexandre Oliva
  2023-11-22 13:13                     ` [PATCH v5] Introduce attribute sym_alias (was: Last call for bikeshedding on attribute sym/exalias/reverse_alias) Jan Hubicka
  0 siblings, 2 replies; 35+ messages in thread
From: Alexandre Oliva @ 2023-09-20  5:59 UTC (permalink / raw)
  To: Jonathan Wakely
  Cc: gcc-patches, Nathan Sidwell, Eric Botcazou, Jan Hubicka, Joseph S. Myers

[back to gcc-patches]

On Sep 14, 2023, Alexandre Oliva <oliva@adacore.com> wrote:

> On Sep  8, 2023, Jonathan Wakely <jwakely.gcc@gmail.com> wrote:
>> I think "symname" or "symalias" would be better than just "sym".

> Thanks, I like symalias.

And then I misremembered it and proceeded to implement sym_alias
instead.  *shrug*  I'll change if this variation isn't acceptable.


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.

Regstrapped on x86_64-linux-gnu.  Ok to install?


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.
---
 .../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(-)
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
 create mode 100644 gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
 create mode 100644 gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
 create mode 100644 gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
 create mode 100644 gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
 create mode 100644 gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C

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 ad0be511d4800..b8de16ebf7c0c 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 148d40815b8f8..2a17d233b47ae 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 b8cb55b97df38..b09a3b21b830e 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"
@@ -819,7 +820,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;
@@ -2631,6 +2633,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 84a43658a70da..156f2c2058800 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 050994d841665..5042b9cfecd80 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 e0c4259c905ed..79243adb4186b 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,
@@ -383,6 +384,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 },
@@ -2855,7 +2858,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
@@ -2865,7 +2868,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
@@ -2875,6 +2878,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.  */
 
@@ -3008,6 +3034,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 5822faf01b48b..4436cd599c291 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -3087,6 +3087,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 e41e5ad3ae74d..c45ce2d0a1332 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 cedaaac3a45b7..6a444e6fa5bcf 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 bccd2f2abb5a3..eb2d05094e989 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 9139a0075abf7..62bebaea106a2 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -4883,6 +4883,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.  */
@@ -5050,6 +5112,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 3ca011c61c8a1..3a369cd11d96d 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5754,6 +5754,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.  */
@@ -6969,6 +6971,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);
@@ -7544,6 +7547,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 255c4026bdbdf..b842e8e86b543 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);
 	}
     }
@@ -10770,6 +10773,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 b402befba6da4..68232b8863f16 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -1773,6 +1773,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))
@@ -2129,6 +2132,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.  */
 
@@ -2936,6 +2980,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
@@ -3198,6 +3244,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
@@ -3432,6 +3480,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 e776bb868fd8a..2c698b95f6113 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 9e8926e4cc603..4a2e8cb435404 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 7878929c24679..4bb7b8c8c6e78 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 947c05babc9eb..45746be50d983 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -4065,6 +4065,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
@@ -7912,6 +7945,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
@@ -8941,6 +8978,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 0470509a98d2a..0812088e59f38 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 46ee01b675950..f284289331807 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 e2e22be57e9e4..aec59b69754c5 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 0000000000000..61af50cb5527f
--- /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 0000000000000..6f0d55ca42cc8
--- /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 0000000000000..0052485a30afb
--- /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 0000000000000..2beafa4637923
--- /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 0000000000000..579eda1a473f6
--- /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 0000000000000..6facfcc2cd9a9
--- /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 0000000000000..42c7288ad4ad2
--- /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 0000000000000..59b779de35d80
--- /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 0000000000000..45d59b953c325
--- /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 e7b51b15e4a84..33649906d1d11 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;
 }
 


-- 
Alexandre Oliva, happy hacker            https://FSFLA.org/blogs/lxo/
   Free Software Activist                   GNU Toolchain Engineer
More tolerance and less prejudice are key for inclusion and diversity
Excluding neuro-others for not behaving ""normal"" is *not* inclusive

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

* Re: [PATCH v5] Introduce attribute sym_alias
  2023-09-20  5:59                   ` [PATCH v5] Introduce attribute sym_alias (was: Last call for bikeshedding on attribute sym/exalias/reverse_alias) Alexandre Oliva
@ 2023-11-20 12:54                     ` Alexandre Oliva
  2023-11-22 12:14                       ` Richard Biener
  2023-11-22 13:13                     ` [PATCH v5] Introduce attribute sym_alias (was: Last call for bikeshedding on attribute sym/exalias/reverse_alias) Jan Hubicka
  1 sibling, 1 reply; 35+ messages in thread
From: Alexandre Oliva @ 2023-11-20 12:54 UTC (permalink / raw)
  To: gcc-patches
  Cc: Jonathan Wakely, Nathan Sidwell, Eric Botcazou, Jan Hubicka,
	Joseph S. Myers, Richard Biener, Jeff Law

On Sep 20, 2023, Alexandre Oliva <oliva@adacore.com> wrote:

> This patch introduces an attribute to add extra asm names (aliases)
> for a decl when its definition is output.

Ping?
https://gcc.gnu.org/pipermail/gcc-patches/2023-September/630971.html

Re-regstrapped on x86_64-linux-gnu.  Ok to install?

-- 
Alexandre Oliva, happy hacker            https://FSFLA.org/blogs/lxo/
   Free Software Activist                   GNU Toolchain Engineer
More tolerance and less prejudice are key for inclusion and diversity
Excluding neuro-others for not behaving ""normal"" is *not* inclusive

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

* Re: [PATCH v5] Introduce attribute sym_alias
  2023-11-20 12:54                     ` [PATCH v5] Introduce attribute sym_alias Alexandre Oliva
@ 2023-11-22 12:14                       ` Richard Biener
  2023-11-22 19:16                         ` Joseph Myers
  0 siblings, 1 reply; 35+ messages in thread
From: Richard Biener @ 2023-11-22 12:14 UTC (permalink / raw)
  To: Alexandre Oliva
  Cc: gcc-patches, Jonathan Wakely, Nathan Sidwell, Eric Botcazou,
	Jan Hubicka, Joseph S. Myers, Jeff Law

On Mon, Nov 20, 2023 at 1:54 PM Alexandre Oliva <oliva@adacore.com> wrote:
>
> On Sep 20, 2023, Alexandre Oliva <oliva@adacore.com> wrote:
>
> > This patch introduces an attribute to add extra asm names (aliases)
> > for a decl when its definition is output.
>
> Ping?
> https://gcc.gnu.org/pipermail/gcc-patches/2023-September/630971.html
>
> Re-regstrapped on x86_64-linux-gnu.  Ok to install?

OK if Honza or C/C++ maintainers do not request additional changes
this week.

Thanks,
Richard.

> --
> Alexandre Oliva, happy hacker            https://FSFLA.org/blogs/lxo/
>    Free Software Activist                   GNU Toolchain Engineer
> More tolerance and less prejudice are key for inclusion and diversity
> Excluding neuro-others for not behaving ""normal"" is *not* inclusive

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

* Re: [PATCH v5] Introduce attribute sym_alias (was: Last call for bikeshedding on attribute sym/exalias/reverse_alias)
  2023-09-20  5:59                   ` [PATCH v5] Introduce attribute sym_alias (was: Last call for bikeshedding on attribute sym/exalias/reverse_alias) Alexandre Oliva
  2023-11-20 12:54                     ` [PATCH v5] Introduce attribute sym_alias Alexandre Oliva
@ 2023-11-22 13:13                     ` Jan Hubicka
  2023-11-30 12:53                       ` [PATCH v6] Introduce attribute sym_alias Alexandre Oliva
  1 sibling, 1 reply; 35+ messages in thread
From: Jan Hubicka @ 2023-11-22 13:13 UTC (permalink / raw)
  To: Alexandre Oliva
  Cc: Jonathan Wakely, gcc-patches, Nathan Sidwell, Eric Botcazou,
	Joseph S. Myers

Hi,
it seems that interface to symbol table is fairly minimal here reduced
to...
> 	(create_sym_alias_decl, create_sym_alias_decls): New.
> 	* cgraphunit.cc (cgraph_node::analyze): Create alias_target
> 	node if needed.
called from here...
> 	(analyze_functions): Fixup visibility of implicit alias only
> 	after its node is analyzed.

> +      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);

I wonder why you use same body aliases, which are kind of special to C++
frontend (and come with fixup code working around its quirks you had to
disable above).

Why you do not produce usual alias attribute once you know the symbol
table so it goes the cgraph_node::create_alias or
vaprool_node::create_alias path?

Honza

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

* Re: [PATCH v5] Introduce attribute sym_alias
  2023-11-22 12:14                       ` Richard Biener
@ 2023-11-22 19:16                         ` Joseph Myers
  0 siblings, 0 replies; 35+ messages in thread
From: Joseph Myers @ 2023-11-22 19:16 UTC (permalink / raw)
  To: Richard Biener
  Cc: Alexandre Oliva, gcc-patches, Jonathan Wakely, Nathan Sidwell,
	Eric Botcazou, Jan Hubicka, Jeff Law

Is it OK to apply this attribute to a (file-scope or block-scope) static 
variable or function in C (and if it is, what's the linkage of the 
resulting alias)?  That doesn't seem very clear to me from the 
documentation, and I'd also expect a testcase of this, whatever the answer 
is.

What's the interaction with visibility attributes, pragmas and options?  
Do the aliases have the same visibility as the main declaration, or do 
they have the visibility that would be given to a declaration in the 
current context (command-line options and pragmas)?  If the latter, 
there's the question of what context is relevant when there are multiple 
declarations of an object or function, some of which might have sym_alias 
attributes and some of which might not.  Again, I think this should be 
documented and tested, whatever the decision about the desired semantics.

-- 
Joseph S. Myers
joseph@codesourcery.com

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

* [PATCH v6] Introduce attribute sym_alias
  2023-11-22 13:13                     ` [PATCH v5] Introduce attribute sym_alias (was: Last call for bikeshedding on attribute sym/exalias/reverse_alias) Jan Hubicka
@ 2023-11-30 12:53                       ` Alexandre Oliva
  2023-11-30 15:24                         ` Jan Hubicka
  0 siblings, 1 reply; 35+ messages in thread
From: Alexandre Oliva @ 2023-11-30 12:53 UTC (permalink / raw)
  To: Jan Hubicka
  Cc: Jonathan Wakely, gcc-patches, Nathan Sidwell, Eric Botcazou,
	Joseph S. Myers

On Nov 22, 2023, Jan Hubicka <hubicka@ucw.cz> wrote:

> I wonder why you use same body aliases, which are kind of special to C++
> frontend (and come with fixup code working around its quirks you had to
> disable above).

TBH, I don't recall whether I had any reason to have gone down that
path, or I just didn't realize I could have done something simpler.
I've worked on and off on this patch for the past 3.5y, so many details
have faded away from memory by now.  I do recall there were some
challenges in making the sym_alias name available as an alias target
early enough for it to be found, and this may have been related with
these odd choices back then.  But the good news is that calling
create_alias works just fine.  I'm suppose that creating alias
attributes would as well, but why bother?  This looks even clearner!
Thanks!


Joseph, thanks for the thoughtful questions.  I've attempted to answer
them in the updated documentation, and I've added some more testcases.

Here's what I'm regstrapping on x86_64-linux-gnu.  Ok to install if it
passes?


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.
---
 .../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(-)
 create mode 100644 gcc/testsuite/c-c++-common/attr-sym-alias-neg.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
 create mode 100644 gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
 create mode 100644 gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
 create mode 100644 gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
 create mode 100644 gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
 create mode 100644 gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C

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 ad0be511d4800..b8de16ebf7c0c 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 fd15459203a5c..2004569896268 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 c7209c26acc9f..d41215d004dc5 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 84a43658a70da..156f2c2058800 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 050994d841665..5042b9cfecd80 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 2b20e58c922c8..392437eab3b7d 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 cf1df82c0f405..e5a1ee54cb3ee 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 f93259a8c70c4..0bef2179e89c4 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 cfdd9f693a889..cb5ac01f7e290 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 bccd2f2abb5a3..eb2d05094e989 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 6fdb56abfb9f8..fa8fc63db82a5 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 964af1ddd85c5..2bafed4e9939b 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 2f2dbb8d10723..5636d694b6894 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 9e666e5eecee0..c7f009beaf08e 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 d19ea5d121c8d..f877780b2acae 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 9e8926e4cc603..4a2e8cb435404 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 7878929c24679..4bb7b8c8c6e78 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 e6de0815846a6..5dcdec76c1ebc 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 0470509a98d2a..165a5177ca147 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 0000000000000..28be8e3b0e66a
--- /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 46ee01b675950..f284289331807 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 e2e22be57e9e4..aec59b69754c5 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 0000000000000..61af50cb5527f
--- /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 0000000000000..6f0d55ca42cc8
--- /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 0000000000000..0052485a30afb
--- /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 0000000000000..2beafa4637923
--- /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 0000000000000..bee90136dafd5
--- /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 0000000000000..579eda1a473f6
--- /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 0000000000000..6facfcc2cd9a9
--- /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 0000000000000..42c7288ad4ad2
--- /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 0000000000000..59b779de35d80
--- /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 0000000000000..45d59b953c325
--- /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 e7b51b15e4a84..33649906d1d11 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;
 }
 


-- 
Alexandre Oliva, happy hacker            https://FSFLA.org/blogs/lxo/
   Free Software Activist                   GNU Toolchain Engineer
More tolerance and less prejudice are key for inclusion and diversity
Excluding neuro-others for not behaving ""normal"" is *not* inclusive

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

* Re: [PATCH v6] Introduce attribute sym_alias
  2023-11-30 12:53                       ` [PATCH v6] Introduce attribute sym_alias Alexandre Oliva
@ 2023-11-30 15:24                         ` Jan Hubicka
  2023-12-01 11:25                           ` [PATCH v7] " Alexandre Oliva
  0 siblings, 1 reply; 35+ messages in thread
From: Jan Hubicka @ 2023-11-30 15:24 UTC (permalink / raw)
  To: Alexandre Oliva
  Cc: Jonathan Wakely, gcc-patches, Nathan Sidwell, Eric Botcazou,
	Joseph S. Myers

> On Nov 22, 2023, Jan Hubicka <hubicka@ucw.cz> wrote:
> 
> > I wonder why you use same body aliases, which are kind of special to C++
> > frontend (and come with fixup code working around its quirks you had to
> > disable above).
> 
> TBH, I don't recall whether I had any reason to have gone down that
> path, or I just didn't realize I could have done something simpler.
> I've worked on and off on this patch for the past 3.5y, so many details
> have faded away from memory by now.  I do recall there were some
> challenges in making the sym_alias name available as an alias target
> early enough for it to be found, and this may have been related with
> these odd choices back then.  But the good news is that calling
> create_alias works just fine.  I'm suppose that creating alias
> attributes would as well, but why bother?  This looks even clearner!
> Thanks!

> diff --git a/gcc/cgraphunit.cc b/gcc/cgraphunit.cc
> index bccd2f2abb5a3..eb2d05094e989 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)

I think you hould be able to drop this, since aliases you create now are
not same body aliases.
> +      if (VAR_P (replaced))
> +	varpool_node::create_alias (sym_node->decl, replacement);
> +      else
> +	cgraph_node::create_alias (sym_node->decl, replacement);
We probably chould have create_alias on symbol node directly, but that
is something I can clean up next stage1.

The IPA bits are fine.  I will take a look on your second patch.
Honza

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

* [PATCH v7] Introduce attribute sym_alias
  2023-11-30 15:24                         ` Jan Hubicka
@ 2023-12-01 11:25                           ` Alexandre Oliva
  2023-12-06  2:10                             ` [PATCH v8] " Alexandre Oliva
  2023-12-06 10:06                             ` [PATCH v7] " Jan Hubicka
  0 siblings, 2 replies; 35+ messages in thread
From: Alexandre Oliva @ 2023-12-01 11:25 UTC (permalink / raw)
  To: Jan Hubicka
  Cc: Jonathan Wakely, gcc-patches, Nathan Sidwell, Eric Botcazou,
	Joseph S. Myers

On Nov 30, 2023, Jan Hubicka <hubicka@ucw.cz> wrote:

>> +      if (VAR_P (replaced))
>> +	varpool_node::create_alias (sym_node->decl, replacement);
>> +      else
>> +	cgraph_node::create_alias (sym_node->decl, replacement);

Unfortunately, this change didn't work.  Several of the C++ tests
regressed with it.  Going back to same-body aliases, they work.

I suspect this may have to do with the infrastructure put in to deal
with cdtors clones.

I've also found some discrepancies between C and C++ WRT sym_alias in
static local variables, and failure to detect and report symbol name
clashes between sym_aliases and unrelated declarations.  Thanks, Joseph,
for pushing me to consider other cases I hadn't thought of before :-)
I'm going to look into these, but for now, the patch below gets a full
pass, with these issues XFAILed.


> The IPA bits are fine.  I will take a look on your second patch.

Thanks!


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.
---
 .../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(-)
 create mode 100644 gcc/testsuite/c-c++-common/attr-sym-alias-neg.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
 create mode 100644 gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
 create mode 100644 gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
 create mode 100644 gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
 create mode 100644 gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
 create mode 100644 gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C

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 ad0be511d4800..b8de16ebf7c0c 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 fd15459203a5c..2004569896268 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 c7209c26acc9f..c75ca6974cb71 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 84a43658a70da..156f2c2058800 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 050994d841665..5042b9cfecd80 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 2b20e58c922c8..392437eab3b7d 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 cf1df82c0f405..e5a1ee54cb3ee 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 f93259a8c70c4..0bef2179e89c4 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 cfdd9f693a889..cb5ac01f7e290 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 bccd2f2abb5a3..eb2d05094e989 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 6fdb56abfb9f8..fa8fc63db82a5 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 964af1ddd85c5..2bafed4e9939b 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 2f2dbb8d10723..5636d694b6894 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 9e666e5eecee0..c7f009beaf08e 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 d19ea5d121c8d..f877780b2acae 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 9e8926e4cc603..4a2e8cb435404 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 7878929c24679..4bb7b8c8c6e78 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 e6de0815846a6..5dcdec76c1ebc 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 0470509a98d2a..43ba88d793925 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 0000000000000..f64e5bb1ac44b
--- /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 46ee01b675950..f284289331807 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 e2e22be57e9e4..aec59b69754c5 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 0000000000000..61af50cb5527f
--- /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 0000000000000..6f0d55ca42cc8
--- /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 0000000000000..0052485a30afb
--- /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 0000000000000..2beafa4637923
--- /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 0000000000000..bee90136dafd5
--- /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 0000000000000..579eda1a473f6
--- /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 0000000000000..6facfcc2cd9a9
--- /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 0000000000000..42c7288ad4ad2
--- /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 0000000000000..59b779de35d80
--- /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 0000000000000..45d59b953c325
--- /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 e7b51b15e4a84..33649906d1d11 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;
 }
 


-- 
Alexandre Oliva, happy hacker            https://FSFLA.org/blogs/lxo/
   Free Software Activist                   GNU Toolchain Engineer
More tolerance and less prejudice are key for inclusion and diversity
Excluding neuro-others for not behaving ""normal"" is *not* inclusive

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

* [PATCH v8] Introduce attribute sym_alias
  2023-12-01 11:25                           ` [PATCH v7] " Alexandre Oliva
@ 2023-12-06  2:10                             ` Alexandre Oliva
  2023-12-06 10:06                             ` [PATCH v7] " Jan Hubicka
  1 sibling, 0 replies; 35+ messages in thread
From: Alexandre Oliva @ 2023-12-06  2:10 UTC (permalink / raw)
  To: Jan Hubicka
  Cc: Jonathan Wakely, gcc-patches, Nathan Sidwell, Eric Botcazou,
	Joseph S. Myers


Here's an improved version that fixes some cases of making static local
names visible through sym_alias, detection of symbol name clashes when
sym_alias is registered before a clashing definition ("sym name"
attributes are now introduced to enable sym_alias-created declarations
to be identified), and aliases to typeinfo sym_alias names, with tests
adjusted and extended to match.

I've retained comments for the desired create_alias calls that didn't
work, instead of create_same_body_alias for functions, and for the
create_extra_name_alias calls I used to issue for variables, mainly to
draw attention to the fact that some of these calls, found undesirable
in earlier iterations, are still there, hoping that we can keep them
this way rather than work out some other way to introduce this feature.

Regstrapped on x86_64-linux-gnu, also tested on arm-eabi.  Ok to
install?


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.
---
 .../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(-)
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
 create mode 100644 gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
 create mode 100644 gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
 create mode 100644 gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
 create mode 100644 gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
 create mode 100644 gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C

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 ad0be511d4800..b8de16ebf7c0c 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 fd15459203a5c..2004569896268 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 ff4b638c25c4c..abfbbbf629433 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 fdeebff1cd986..b7a67f735503a 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 050994d841665..5042b9cfecd80 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 854e987dc79b6..2593fde1fe9a6 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 92c83e1bf10de..c1490e627dfcb 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 f93259a8c70c4..0bef2179e89c4 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 2b32055761688..6d61e327fb68b 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 9a550a5cce645..a66db795c78f5 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 6fdb56abfb9f8..fa8fc63db82a5 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 795152c9ad2f8..dca2d31a3723b 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 4b685270097f7..cc7f7a99487d3 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 bee84879023f3..00f19962e5dff 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 76f1d44610aa9..3a19a5712561b 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 9e8926e4cc603..4a2e8cb435404 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 7878929c24679..4bb7b8c8c6e78 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 af782b3f228ce..22b7e3ab7bfab 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 0470509a98d2a..44df52095c1fe 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 46ee01b675950..f284289331807 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 e2e22be57e9e4..aec59b69754c5 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 0000000000000..2fe1948aed918
--- /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 0000000000000..f692a552606f6
--- /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 0000000000000..52f8fb764964f
--- /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 0000000000000..2beafa4637923
--- /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 0000000000000..bee90136dafd5
--- /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 0000000000000..5172999491339
--- /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 0000000000000..6facfcc2cd9a9
--- /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 0000000000000..42c7288ad4ad2
--- /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 0000000000000..59b779de35d80
--- /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 0000000000000..45d59b953c325
--- /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 e7b51b15e4a84..33649906d1d11 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;
 }
 


-- 
Alexandre Oliva, happy hacker            https://FSFLA.org/blogs/lxo/
   Free Software Activist                   GNU Toolchain Engineer
More tolerance and less prejudice are key for inclusion and diversity
Excluding neuro-others for not behaving ""normal"" is *not* inclusive

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

* Re: [PATCH v7] Introduce attribute sym_alias
  2023-12-01 11:25                           ` [PATCH v7] " Alexandre Oliva
  2023-12-06  2:10                             ` [PATCH v8] " Alexandre Oliva
@ 2023-12-06 10:06                             ` Jan Hubicka
  2023-12-07 20:52                               ` Alexandre Oliva
  1 sibling, 1 reply; 35+ messages in thread
From: Jan Hubicka @ 2023-12-06 10:06 UTC (permalink / raw)
  To: Alexandre Oliva
  Cc: Jonathan Wakely, gcc-patches, Nathan Sidwell, Eric Botcazou,
	Joseph S. Myers

> On Nov 30, 2023, Jan Hubicka <hubicka@ucw.cz> wrote:
> 
> >> +      if (VAR_P (replaced))
> >> +	varpool_node::create_alias (sym_node->decl, replacement);
> >> +      else
> >> +	cgraph_node::create_alias (sym_node->decl, replacement);
> 
> Unfortunately, this change didn't work.  Several of the C++ tests
> regressed with it.  Going back to same-body aliases, they work.
> 
> I suspect this may have to do with the infrastructure put in to deal
> with cdtors clones.

Do you have short testcase for this?  THe main oddities with same body
aliases comes from the fact that C++ FE creates them early during
parsing before all declaration flags are finished.

Later we do:

  /* Ugly, but the fixup cannot happen at a time same body alias is created;
     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)
          node->fixup_same_cpp_alias_visibility (node->get_alias_target ());

Fixup copies some flags such as inline flags, visibility and comdat
groups which can change during parsing process.

Since you produce aliases late at finalization time, I do not see how
this could be relevant.  Pehraps unless you manage to copy wrong flags
from implicit aliases before the fixup happens which would be simple
ordering problem....

Honza
> 
> I've also found some discrepancies between C and C++ WRT sym_alias in
> static local variables, and failure to detect and report symbol name
> clashes between sym_aliases and unrelated declarations.  Thanks, Joseph,
> for pushing me to consider other cases I hadn't thought of before :-)
> I'm going to look into these, but for now, the patch below gets a full
> pass, with these issues XFAILed.
> 
> 
> > The IPA bits are fine.  I will take a look on your second patch.
> 
> Thanks!
> 
> 
> 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.
> ---
>  .../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(-)
>  create mode 100644 gcc/testsuite/c-c++-common/attr-sym-alias-neg.c
>  create mode 100644 gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
>  create mode 100644 gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
>  create mode 100644 gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
>  create mode 100644 gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
>  create mode 100644 gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
>  create mode 100644 gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
>  create mode 100644 gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
>  create mode 100644 gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
>  create mode 100644 gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
>  create mode 100644 gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
> 
> 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 ad0be511d4800..b8de16ebf7c0c 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 fd15459203a5c..2004569896268 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 c7209c26acc9f..c75ca6974cb71 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 84a43658a70da..156f2c2058800 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 050994d841665..5042b9cfecd80 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 2b20e58c922c8..392437eab3b7d 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 cf1df82c0f405..e5a1ee54cb3ee 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 f93259a8c70c4..0bef2179e89c4 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 cfdd9f693a889..cb5ac01f7e290 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 bccd2f2abb5a3..eb2d05094e989 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 6fdb56abfb9f8..fa8fc63db82a5 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 964af1ddd85c5..2bafed4e9939b 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 2f2dbb8d10723..5636d694b6894 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 9e666e5eecee0..c7f009beaf08e 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 d19ea5d121c8d..f877780b2acae 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 9e8926e4cc603..4a2e8cb435404 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 7878929c24679..4bb7b8c8c6e78 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 e6de0815846a6..5dcdec76c1ebc 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 0470509a98d2a..43ba88d793925 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 0000000000000..f64e5bb1ac44b
> --- /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 46ee01b675950..f284289331807 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 e2e22be57e9e4..aec59b69754c5 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 0000000000000..61af50cb5527f
> --- /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 0000000000000..6f0d55ca42cc8
> --- /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 0000000000000..0052485a30afb
> --- /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 0000000000000..2beafa4637923
> --- /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 0000000000000..bee90136dafd5
> --- /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 0000000000000..579eda1a473f6
> --- /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 0000000000000..6facfcc2cd9a9
> --- /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 0000000000000..42c7288ad4ad2
> --- /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 0000000000000..59b779de35d80
> --- /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 0000000000000..45d59b953c325
> --- /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 e7b51b15e4a84..33649906d1d11 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;
>  }
>  
> 
> 
> -- 
> Alexandre Oliva, happy hacker            https://FSFLA.org/blogs/lxo/
>    Free Software Activist                   GNU Toolchain Engineer
> More tolerance and less prejudice are key for inclusion and diversity
> Excluding neuro-others for not behaving ""normal"" is *not* inclusive

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

* Re: [PATCH v7] Introduce attribute sym_alias
  2023-12-06 10:06                             ` [PATCH v7] " Jan Hubicka
@ 2023-12-07 20:52                               ` Alexandre Oliva
  0 siblings, 0 replies; 35+ messages in thread
From: Alexandre Oliva @ 2023-12-07 20:52 UTC (permalink / raw)
  To: Jan Hubicka
  Cc: Jonathan Wakely, gcc-patches, Nathan Sidwell, Eric Botcazou,
	Joseph S. Myers

On Dec  6, 2023, Jan Hubicka <hubicka@ucw.cz> wrote:

>> On Nov 30, 2023, Jan Hubicka <hubicka@ucw.cz> wrote:
>> 
>> >> +      if (VAR_P (replaced))
>> >> +	varpool_node::create_alias (sym_node->decl, replacement);
>> >> +      else
>> >> +	cgraph_node::create_alias (sym_node->decl, replacement);
>> 
>> Unfortunately, this change didn't work.  Several of the C++ tests
>> regressed with it.  Going back to same-body aliases, they work.
>> 
>> I suspect this may have to do with the infrastructure put in to deal
>> with cdtors clones.

> Do you have short testcase for this?

attr-sym-alias-[13].C are not too big, and show various regressions with
the incremental patchlet below (to be applied on top of v7], but here's
a minimal testcase that triggers the problem:

struct foo {
  __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
};
foo bar;

> THe main oddities with same body
> aliases comes from the fact that C++ FE creates them early during
> parsing before all declaration flags are finished.

*nod*, this is probably why some of the cdtor and even sym_aliases for
inline member functions (FOOBAR_B and FOOBAR_C in attr-sym-alias-1.C,
and FOOCLS_INT_VIRT in attr-sym-alias-3.C) fail to be output when not
using create_same_body_alias.

diff --git a/gcc/attribs.cc b/gcc/attribs.cc
index abfbbbf6294..65ce610f2d4 100644
--- a/gcc/attribs.cc
+++ b/gcc/attribs.cc
@@ -2717,8 +2717,8 @@ create_sym_alias_decl (tree decl, tree id)
     // 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);
+    // 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);
 
diff --git a/gcc/symtab.cc b/gcc/symtab.cc
index 44df52095c1..e40240077f2 100644
--- a/gcc/symtab.cc
+++ b/gcc/symtab.cc
@@ -1993,8 +1993,8 @@ symtab_node::remap_sym_alias_target (tree replaced, tree replacement)
 	// 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);
+	// 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);
     }
 }

> Fixup copies some flags such as inline flags, visibility and comdat
> groups which can change during parsing process.

*nod*, I've run into some of that, and had to add visibility propagation
to the sym_aliases to make up for it.

But I'm not sure that that's the issue you're getting at.  Some
sym_aliases don't even get output with this patchlet.  FOODTR_A* aliases
get created during parsing, when maybe_clone_body creates the dtor
clones and their cgraph nodes to set their comdat group.  ISTM that it's
the later visibility copying because of same body alias that enables the
alias declaration to get the same (final) visibility as the declarations
they alias.  Which suggests that there could be another way to ensure
the update takes place, but the best spot for it has so far eluded me.

-- 
Alexandre Oliva, happy hacker            https://FSFLA.org/blogs/lxo/
   Free Software Activist                   GNU Toolchain Engineer
More tolerance and less prejudice are key for inclusion and diversity
Excluding neuro-others for not behaving ""normal"" is *not* inclusive

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

end of thread, other threads:[~2023-12-07 20:53 UTC | newest]

Thread overview: 35+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-07-29 20:56 [RFC, WIP] introduce attribute exalias Alexandre Oliva
2020-08-07 17:38 ` [PATCH] " Alexandre Oliva
2020-08-14 15:39   ` Alexandre Oliva
2020-08-14 16:24     ` Nathan Sidwell
2020-08-14 19:24       ` Alexandre Oliva
2020-08-14 22:12         ` Nathan Sidwell
2020-08-15  2:43           ` Alexandre Oliva
2020-08-15  9:22             ` Iain Sandoe
2020-08-15 16:39               ` Alexandre Oliva
2020-08-15 18:17                 ` Iain Sandoe
2020-08-25  8:34                   ` Alexandre Oliva
2020-08-25 11:23                     ` Iain Sandoe
2020-08-15 17:26               ` Alexandre Oliva
2020-08-15 21:11             ` Nathan Sidwell
2020-08-25  7:50               ` Alexandre Oliva
2023-07-15  1:08   ` [PATCH v3] Introduce attribute reverse_alias Alexandre Oliva
2023-07-15 21:55     ` Nathan Sidwell
2023-07-18  4:29       ` Alexandre Oliva
2023-07-18 11:37         ` Richard Biener
2023-07-19 23:11           ` [PATCH v4] Introduce attribute sym Alexandre Oliva
2023-07-20 13:09             ` Richard Biener
2023-07-21  9:23               ` Alexandre Oliva
2023-07-22  3:12             ` Fangrui Song
2023-08-16  4:27               ` Alexandre Oliva
     [not found]             ` <orpm2tgrsd.fsf_-_@lxoliva.fsfla.org>
     [not found]               ` <CAH6eHdQ3vT3MjohuE-izto+K=BMRykY3T-UyWa5-=OTDPM-JsQ@mail.gmail.com>
     [not found]                 ` <ory1h9t6nr.fsf@lxoliva.fsfla.org>
2023-09-20  5:59                   ` [PATCH v5] Introduce attribute sym_alias (was: Last call for bikeshedding on attribute sym/exalias/reverse_alias) Alexandre Oliva
2023-11-20 12:54                     ` [PATCH v5] Introduce attribute sym_alias Alexandre Oliva
2023-11-22 12:14                       ` Richard Biener
2023-11-22 19:16                         ` Joseph Myers
2023-11-22 13:13                     ` [PATCH v5] Introduce attribute sym_alias (was: Last call for bikeshedding on attribute sym/exalias/reverse_alias) Jan Hubicka
2023-11-30 12:53                       ` [PATCH v6] Introduce attribute sym_alias Alexandre Oliva
2023-11-30 15:24                         ` Jan Hubicka
2023-12-01 11:25                           ` [PATCH v7] " Alexandre Oliva
2023-12-06  2:10                             ` [PATCH v8] " Alexandre Oliva
2023-12-06 10:06                             ` [PATCH v7] " Jan Hubicka
2023-12-07 20:52                               ` 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).