diff mbox series

[v3] Introduce attribute reverse_alias

Message ID ory1ji6ky5.fsf_-_@lxoliva.fsfla.org
State New
Headers show
Series [v3] Introduce attribute reverse_alias | expand

Commit Message

Alexandre Oliva July 15, 2023, 1:08 a.m. UTC
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

Comments

Nathan Sidwell July 15, 2023, 9:55 p.m. UTC | #1
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;
>   }
>   
> 
>
Alexandre Oliva July 18, 2023, 4:29 a.m. UTC | #2
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.
Richard Biener July 18, 2023, 11:37 a.m. UTC | #3
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>
diff mbox series

Patch

diff --git a/gcc/ada/doc/gnat_rm/interfacing_to_other_languages.rst b/gcc/ada/doc/gnat_rm/interfacing_to_other_languages.rst
index 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;
 }