@@ -123,6 +123,12 @@ It is also possible to import a C++ exception using the following syntax:
The ``External_Name`` is the name of the C++ RTTI symbol. You can then
cover a specific C++ exception in an exception handler.
+RTTI symbols undergo C++ name mangling, which can make for identifiers
+that are inconvenient to use. An alias with a mnemonic name can be
+introduced by adding attribute ``sym_alias`` to the class that the
+RTTI symbol refers to.
+
+
.. _Interfacing_to_COBOL:
Interfacing to COBOL
@@ -4274,6 +4274,7 @@ and two public primitives to set and get the value of this attribute.
public:
virtual void Set_Age (int New_Age);
virtual int Age ();
+ __attribute__ ((__sym_alias__ ("Ctor_For_Animal")))
Animal() {Age_Count = 0;};
private:
int Age_Count;
@@ -4306,6 +4307,7 @@ both Carnivore and Domestic, that is:
virtual int Number_Of_Teeth ();
virtual void Set_Owner (char* Name);
+ __attribute__ ((__sym_alias__ ("Ctor_For_Dog"))) // mnemonic alias
Dog(); // Constructor
private:
int Tooth_Count;
@@ -4344,7 +4346,8 @@ how to import these C++ declarations from the Ada side:
function New_Animal return Animal;
pragma CPP_Constructor (New_Animal);
- pragma Import (CPP, New_Animal, "_ZN6AnimalC1Ev");
+ pragma Import (CPP, New_Animal,
+ "_ZN6AnimalC1Ev"); -- or "Ctor_For_Animal"
type Dog is new Animal and Carnivore and Domestic with record
Tooth_Count : Natural;
@@ -4360,7 +4363,7 @@ how to import these C++ declarations from the Ada side:
function New_Dog return Dog;
pragma CPP_Constructor (New_Dog);
- pragma Import (CPP, New_Dog, "_ZN3DogC2Ev");
+ pragma Import (CPP, New_Dog, "Ctor_For_Dog"); -- or "_ZN3DogC1Ev"
end Animals;
Thanks to the compatibility between GNAT run-time structures and the C++ ABI,
@@ -4382,7 +4385,8 @@ associated with each subprogram because it is assumed that all the calls to
these primitives will be dispatching calls. The only exception is the
constructor, which must be registered with the compiler by means of
``pragma CPP_Constructor`` and needs to provide its associated C++
-mangled name because the Ada compiler generates direct calls to it.
+mangled name (or an alias) because the Ada compiler generates direct
+calls to it.
With the above packages we can now declare objects of type Dog on the Ada side
and dispatch calls to the corresponding subprograms on the C++ side. We can
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3. If not see
#include "coretypes.h"
#include "target.h"
#include "tree.h"
+#include "cgraph.h"
#include "stringpool.h"
#include "diagnostic-core.h"
#include "attribs.h"
@@ -830,7 +831,8 @@ decl_attributes (tree *node, tree attributes, int flags,
if (TYPE_P (*anode)
&& (flags & (int) ATTR_FLAG_TYPE_IN_PLACE)
- && COMPLETE_TYPE_P (*anode))
+ && COMPLETE_TYPE_P (*anode)
+ && !is_attribute_p ("sym_alias", name))
{
warning (OPT_Wattributes, "type attributes ignored after type is already defined");
continue;
@@ -2672,6 +2674,76 @@ attr_access::array_as_string (tree type) const
return typstr;
}
+/* Create a sym attribute for DECL to be visible with linkage name ID. */
+
+tree
+create_sym_alias_decl (tree decl, tree id)
+{
+ const char *attr_str = "sym_alias";
+
+ if (symtab_node *sym_node = symtab_node::get_for_asmname (id))
+ {
+ if ((sym_node->analyzed
+ ? sym_node->get_alias_target ()->decl
+ : sym_node->alias_target) == decl)
+ return sym_node->decl;
+
+ tree attr_name = get_identifier (attr_str);
+ error_at (DECL_SOURCE_LOCATION (decl),
+ "duplicate symbol name %qE in %qE attribute of %qD",
+ id, attr_name, decl);
+ inform (DECL_SOURCE_LOCATION (sym_node->decl),
+ "already used by %qD", sym_node->decl);
+ }
+
+ tree clone = copy_node (decl);
+ DECL_ATTRIBUTES (clone) = remove_attribute (attr_str,
+ DECL_ATTRIBUTES (decl));
+ /* Mark it as a "sym alias" for decl, an internal attribute that
+ enables symbol_table::insert_to_assembler_name_hash can recognize
+ sym_alias decls. */
+ DECL_ATTRIBUTES (clone) = tree_cons (get_identifier ("sym alias"),
+ decl, DECL_ATTRIBUTES (clone));
+ SET_DECL_ASSEMBLER_NAME (clone, id);
+ TREE_USED (id) = 1;
+ TREE_USED (clone) = 1;
+ DECL_PRESERVE_P (clone) = 1;
+ DECL_EXTERNAL (clone) = 0;
+ TREE_STATIC (clone) = 1;
+
+ symtab_node *node;
+ if (VAR_P (clone))
+ // DECL_READ_P (clone) = 1;
+ // node = varpool_node::create_extra_name_alias (clone, decl);
+ node = varpool_node::create_alias (clone, decl);
+ else
+ node = cgraph_node::create_same_body_alias (clone, decl);
+ // node = cgraph_node::create_alias (clone, decl);
+ if (symtab_node *dnode = symtab_node::get_create (decl))
+ node->copy_visibility_from (dnode);
+
+ return clone;
+}
+
+/* Create decls for all sym aliases requested in DECL's attributes. */
+
+void
+create_sym_alias_decls (tree decl)
+{
+ if (!decl_in_symtab_p (decl)
+ || !symtab_node::get (decl)
+ || DECL_ABSTRACT_P (decl))
+ return;
+
+ FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+ {
+ tree id = TREE_VALUE (TREE_VALUE (sym));
+ id = get_identifier (TREE_STRING_POINTER (id));
+
+ create_sym_alias_decl (decl, id);
+ }
+}
+
#if CHECKING_P
namespace selftest
@@ -404,4 +404,11 @@ extern void init_attr_rdwr_indices (rdwr_map *, tree);
extern attr_access *get_parm_access (rdwr_map &, tree,
tree = current_function_decl);
+extern tree create_sym_alias_decl (tree, tree);
+extern void create_sym_alias_decls (tree);
+
+#define FOR_EACH_SYM_ALIAS(sym, attrs) \
+ for (tree sym = lookup_attribute ("sym_alias", (attrs)); \
+ sym; sym = lookup_attribute ("sym_alias", TREE_CHAIN (sym)))
+
#endif // GCC_ATTRIBS_H
@@ -1431,6 +1431,13 @@ pp_ada_tree_identifier (pretty_printer *buffer, tree node, tree type,
static void
pp_asm_name (pretty_printer *buffer, tree t)
{
+ FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (t))
+ {
+ tree id = TREE_VALUE (TREE_VALUE (sym));
+ pp_string (buffer, TREE_STRING_POINTER (id));
+ return;
+ }
+
tree name = DECL_ASSEMBLER_NAME (t);
char *ada_name = XALLOCAVEC (char, IDENTIFIER_LENGTH (name) + 1), *s;
const char *ident = IDENTIFIER_POINTER (name);
@@ -110,7 +110,8 @@ static tree handle_noplt_attribute (tree *, tree, tree, int, bool *) ;
static tree handle_alias_ifunc_attribute (bool, tree *, tree, tree, bool *);
static tree handle_ifunc_attribute (tree *, tree, tree, int, bool *);
static tree handle_alias_attribute (tree *, tree, tree, int, bool *);
-static tree handle_weakref_attribute (tree *, tree, tree, int, bool *) ;
+static tree handle_weakref_attribute (tree *, tree, tree, int, bool *);
+static tree handle_sym_alias_attribute (tree *, tree, tree, int, bool *);
static tree handle_visibility_attribute (tree *, tree, tree, int,
bool *);
static tree handle_tls_model_attribute (tree *, tree, tree, int,
@@ -392,6 +393,8 @@ const struct attribute_spec c_common_gnu_attributes[] =
handle_alias_attribute, NULL },
{ "weakref", 0, 1, true, false, false, false,
handle_weakref_attribute, NULL },
+ { "sym_alias", 1, 1, false, false, false, false,
+ handle_sym_alias_attribute, NULL },
{ "no_instrument_function", 0, 0, true, false, false, false,
handle_no_instrument_function_attribute,
NULL },
@@ -3092,7 +3095,7 @@ handle_alias_ifunc_attribute (bool is_alias, tree *node, tree name, tree args,
return NULL_TREE;
}
-/* Handle an "alias" or "ifunc" attribute; arguments as in
+/* Handle an "ifunc" attribute; arguments as in
struct attribute_spec.handler. */
static tree
@@ -3102,7 +3105,7 @@ handle_ifunc_attribute (tree *node, tree name, tree args,
return handle_alias_ifunc_attribute (false, node, name, args, no_add_attrs);
}
-/* Handle an "alias" or "ifunc" attribute; arguments as in
+/* Handle an "alias" attribute; arguments as in
struct attribute_spec.handler. */
static tree
@@ -3112,6 +3115,29 @@ handle_alias_attribute (tree *node, tree name, tree args,
return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs);
}
+/* Handle a "sym_alias" attribute; arguments as in struct
+ attribute_spec.handler. */
+
+static tree
+handle_sym_alias_attribute (tree *pnode, tree name, tree args,
+ int ARG_UNUSED (flags), bool *no_add_attrs)
+{
+ tree node = *pnode;
+
+ *no_add_attrs = true;
+
+ if (TREE_CODE (TREE_VALUE (args)) != STRING_CST)
+ error ("%qE attribute argument not a string", name);
+ else if (decl_in_symtab_p (node))
+ *no_add_attrs = false;
+ else if (TYPE_P (node) && c_dialect_cxx ())
+ *no_add_attrs = false;
+ else
+ return error_mark_node;
+
+ return NULL_TREE;
+}
+
/* Handle the "copy" attribute NAME by copying the set of attributes
from the symbol referenced by ARGS to the declaration of *NODE. */
@@ -3245,6 +3271,7 @@ handle_copy_attribute (tree *node, tree name, tree args,
|| is_attribute_p ("visibility", atname)
|| is_attribute_p ("weak", atname)
|| is_attribute_p ("weakref", atname)
+ || is_attribute_p ("sym_alias", atname)
|| is_attribute_p ("target_clones", atname))
continue;
@@ -3117,6 +3117,8 @@ duplicate_decls (tree newdecl, tree olddecl)
merge_decls (newdecl, olddecl, newtype, oldtype);
+ symtab_node::remap_sym_alias_target (newdecl, olddecl);
+
/* The NEWDECL will no longer be needed.
Before releasing the node, be sure to remove function from symbol
@@ -5902,6 +5904,11 @@ finish_decl (tree decl, location_t init_loc, tree init,
set_user_assembler_name (decl, asmspec);
}
+ /* Give attribute sym_alias a chance to make the symbol name
+ available for aliasing, even for a static local variable. */
+ if (VAR_P (decl) && !DECL_EXTERNAL (decl) && is_global_var (decl))
+ varpool_node::get_create (decl);
+
if (DECL_FILE_SCOPE_P (decl))
{
if (DECL_INITIAL (decl) == NULL_TREE
@@ -523,6 +523,8 @@ cgraph_node::create (tree decl)
node->register_symbol ();
maybe_record_nested_function (node);
+ create_sym_alias_decls (decl);
+
return node;
}
@@ -327,6 +327,10 @@ public:
/* Return DECL that alias is aliasing. */
inline tree get_alias_target_tree ();
+ /* Remap sym alias nodes recorded as aliasing REPLACED to alias REPLACEMENT
+ instead. */
+ static void remap_sym_alias_target (tree replaced, tree replacement);
+
/* Set section for symbol and its aliases. */
void set_section (const char *section);
@@ -1176,7 +1176,7 @@ analyze_functions (bool first_time)
C++ FE is confused about the COMDAT groups being right. */
if (symtab->cpp_implicit_aliases_done)
FOR_EACH_SYMBOL (node)
- if (node->cpp_implicit_alias)
+ if (node->cpp_implicit_alias && node->analyzed)
node->fixup_same_cpp_alias_visibility (node->get_alias_target ());
build_type_inheritance_graph ();
@@ -4896,6 +4896,68 @@ check_methods (tree t)
}
}
+/* Adjust sym alias name for CLONE, cloned from FN and named NAME,
+ if it is a cdtor, and drop the sym alias from other clones. */
+
+void
+adjust_clone_attributes (tree fn, tree clone, tree name, bool skip_copy_p)
+{
+ if (IDENTIFIER_CDTOR_P (name))
+ {
+ bool found = false;
+ FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+ {
+ found = true;
+ break;
+ }
+
+ if (found
+ && (name == complete_ctor_identifier
+ || name == complete_dtor_identifier))
+ {
+ /* Reuse the sym alias decls created for the primary cdtor
+ decl. */
+ symtab_node::remap_sym_alias_target (fn, clone);
+ }
+ else if (found)
+ {
+ const char *suf;
+
+ if (name == base_ctor_identifier
+ || name == base_dtor_identifier)
+ suf = "_Base";
+ else if (name == deleting_dtor_identifier)
+ suf = "_Del";
+ else
+ gcc_unreachable ();
+
+ size_t xlen = strlen (suf);
+
+ if (!skip_copy_p)
+ DECL_ATTRIBUTES (clone) = copy_list (DECL_ATTRIBUTES (clone));
+
+ FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
+ {
+ /* We need to copy this even with skip_copy_p, because
+ even then copying was shallow. */
+ TREE_VALUE (sym) = copy_list (TREE_VALUE (sym));
+ /* Append suf to the sym alias name. */
+ tree str = TREE_VALUE (TREE_VALUE (sym));
+ char *symname = concat (TREE_STRING_POINTER (str), suf, NULL);
+ str = build_string (TREE_STRING_LENGTH (str) + xlen, symname);
+ TREE_VALUE (TREE_VALUE (sym)) = str;
+ free (symname);
+ }
+
+ if (symtab_node::get (clone))
+ create_sym_alias_decls (clone);
+ }
+ }
+ else
+ DECL_ATTRIBUTES (clone)
+ = remove_attribute ("sym_alias", DECL_ATTRIBUTES (clone));
+}
+
/* FN is constructor, destructor or operator function. Clone the
declaration to create a NAME'd variant. NEED_VTT_PARM_P and
OMIT_INHERITED_PARMS_P are relevant if it's a cdtor. */
@@ -5065,6 +5127,8 @@ build_clone (tree fn, tree name, bool need_vtt_parm_p,
DECL_CHAIN (clone) = DECL_CHAIN (fn);
DECL_CHAIN (fn) = clone;
+ adjust_clone_attributes (fn, clone, name);
+
return clone;
}
@@ -5806,6 +5806,8 @@ struct GTY((for_user)) spec_entry
extern int current_class_depth;
+void adjust_clone_attributes (tree fn, tree clone, tree name, bool = false);
+
/* in decl.cc */
/* An array of static vars & fns. */
@@ -7043,6 +7045,7 @@ extern void do_push_parm_decls (tree, tree, tree *);
extern tree do_aggregate_paren_init (tree, tree);
/* in decl2.cc */
+extern void update_sym_alias_interface (tree);
extern void record_mangling (tree, bool);
extern void overwrite_mangling (tree, tree);
extern void note_mangling_alias (tree, tree);
@@ -7617,6 +7620,7 @@ extern bool emit_tinfo_decl (tree);
extern unsigned get_pseudo_tinfo_index (tree);
extern tree get_pseudo_tinfo_type (unsigned);
extern tree build_if_nonnull (tree, tree, tsubst_flags_t);
+extern void update_tinfo_sym_alias (tree);
/* in search.cc */
extern tree get_parent_with_private_access (tree decl, tree binfo);
@@ -3277,6 +3277,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
&& TREE_STATIC (olddecl))))
make_decl_rtl (olddecl);
+ symtab_node::remap_sym_alias_target (newdecl, olddecl);
+
/* The NEWDECL will no longer be needed. Because every out-of-class
declaration of a member results in a call to duplicate_decls,
freeing these nodes represents in a significant savings.
@@ -3300,6 +3302,7 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
FOR_EACH_CLONE (clone, olddecl)
{
DECL_ATTRIBUTES (clone) = DECL_ATTRIBUTES (olddecl);
+ adjust_clone_attributes (olddecl, clone, DECL_NAME (clone));
DECL_PRESERVE_P (clone) |= DECL_PRESERVE_P (olddecl);
}
}
@@ -10838,6 +10841,7 @@ grokfndecl (tree ctype,
{
cplus_decl_attributes (&decl, *attrlist, 0);
*attrlist = NULL_TREE;
+ create_sym_alias_decls (decl);
}
if (DECL_HAS_CONTRACTS_P (decl))
@@ -1846,6 +1846,9 @@ cplus_decl_attributes (tree *decl, tree attributes, int flags)
if (late_attrs)
save_template_attributes (late_attrs, decl, flags);
+ if (TYPE_P (*decl) && attributes)
+ update_tinfo_sym_alias (*decl);
+
/* Propagate deprecation out to the template. */
if (TREE_DEPRECATED (*decl))
if (tree ti = get_template_info (*decl))
@@ -2202,6 +2205,50 @@ adjust_var_decl_tls_model (tree decl)
set_decl_tls_model (decl, decl_default_tls_model (decl));
}
+/* Copy externalness and linkage from DECL to DEST. */
+
+static void
+copy_interface (tree dest, tree decl)
+{
+ TREE_PUBLIC (dest) = TREE_PUBLIC (decl);
+ TREE_STATIC (dest) = TREE_STATIC (decl);
+ DECL_COMMON (dest) = DECL_COMMON (decl);
+ DECL_COMDAT (dest) = DECL_COMDAT (decl);
+ DECL_WEAK (dest) = DECL_WEAK (decl);
+ DECL_EXTERNAL (dest) = DECL_EXTERNAL (decl);
+ if (DECL_LANG_SPECIFIC (dest) && DECL_LANG_SPECIFIC (decl))
+ DECL_NOT_REALLY_EXTERN (dest) = DECL_NOT_REALLY_EXTERN (decl);
+ DECL_INTERFACE_KNOWN (dest) = DECL_INTERFACE_KNOWN (decl);
+ DECL_VISIBILITY (dest) = DECL_VISIBILITY (decl);
+ DECL_VISIBILITY_SPECIFIED (dest) = DECL_VISIBILITY_SPECIFIED (decl);
+ if (symtab_node *dest_node = symtab_node::get (dest))
+ if (symtab_node *decl_node = symtab_node::get (decl))
+ decl_node->copy_visibility_from (dest_node);
+}
+
+/* Propagate linkage changes to sym aliases. */
+
+void
+update_sym_alias_interface (tree decl)
+{
+ if (!decl_in_symtab_p (decl)
+ || !symtab_node::get (decl))
+ return;
+
+ FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
+ {
+ tree id = TREE_VALUE (TREE_VALUE (sym));
+ id = get_identifier (TREE_STRING_POINTER (id));
+ symtab_node *sym_node = symtab_node::get_for_asmname (id);
+
+ if (sym_node
+ && (sym_node->analyzed
+ ? sym_node->get_alias_target ()->decl
+ : sym_node->alias_target) == decl)
+ copy_interface (sym_node->decl, decl);
+ }
+}
+
/* Set DECL up to have the closest approximation of "initialized common"
linkage available. */
@@ -3009,6 +3056,8 @@ determine_visibility (tree decl)
translation unit, we can make the type internal. */
constrain_visibility (decl, VISIBILITY_ANON, false);
+ update_sym_alias_interface (decl);
+
/* If visibility changed and DECL already has DECL_RTL, ensure
symbol flags are updated. */
if ((DECL_VISIBILITY (decl) != orig_visibility
@@ -3271,6 +3320,8 @@ tentative_decl_linkage (tree decl)
else if (VAR_P (decl))
maybe_commonize_var (decl);
}
+
+ update_sym_alias_interface (decl);
}
/* DECL is a FUNCTION_DECL or VAR_DECL. If the object file linkage
@@ -3505,6 +3556,8 @@ import_export_decl (tree decl)
}
DECL_INTERFACE_KNOWN (decl) = 1;
+
+ update_sym_alias_interface (decl);
}
/* Return an expression that performs the destruction of DECL, which
@@ -22,9 +22,11 @@ along with GCC; see the file COPYING3. If not see
#define INCLUDE_MEMORY
#include "system.h"
#include "coretypes.h"
+#include "target.h"
#include "cp-tree.h"
#include "timevar.h"
#include "stringpool.h"
+#include "cgraph.h"
#include "print-tree.h"
#include "attribs.h"
#include "debug.h"
@@ -3675,6 +3677,15 @@ push_local_extern_decl_alias (tree decl)
/* Adjust visibility. */
determine_visibility (alias);
}
+ else if (DECL_P (alias))
+ DECL_ATTRIBUTES (alias)
+ = targetm.merge_decl_attributes (alias, decl);
+ if (DECL_P (alias))
+ {
+ symtab_node::remap_sym_alias_target (decl, alias);
+ DECL_ATTRIBUTES (decl)
+ = remove_attribute ("sym_alias", DECL_ATTRIBUTES (alias));
+ }
}
retrofit_lang_decl (decl);
@@ -528,9 +528,12 @@ maybe_clone_body (tree fn)
DECL_VISIBILITY_SPECIFIED (clone) = DECL_VISIBILITY_SPECIFIED (fn);
DECL_DLLIMPORT_P (clone) = DECL_DLLIMPORT_P (fn);
DECL_ATTRIBUTES (clone) = clone_attrs (DECL_ATTRIBUTES (fn));
+ adjust_clone_attributes (fn, clone, DECL_NAME (clone), true);
DECL_DISREGARD_INLINE_LIMITS (clone) = DECL_DISREGARD_INLINE_LIMITS (fn);
set_decl_section_name (clone, fn);
+ update_sym_alias_interface (clone);
+
/* Adjust the parameter names and locations. */
parm = DECL_ARGUMENTS (fn);
clone_parm = DECL_ARGUMENTS (clone);
@@ -28,8 +28,10 @@ along with GCC; see the file COPYING3. If not see
#include "stringpool.h"
#include "intl.h"
#include "stor-layout.h"
+#include "attribs.h"
#include "c-family/c-pragma.h"
#include "gcc-rich-location.h"
+#include "cgraph.h"
/* C++ returns type information to the user in struct type_info
objects. We also use type information to implement dynamic_cast and
@@ -479,8 +481,13 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
= build_tree_list (get_identifier ("non overlapping"),
NULL_TREE);
else
+ /* Share the non overlapping attribute, without assuming it's
+ the only attribute, but assuming it's the last if it's
+ present. There may be sym aliases too, and those are not
+ to be shared. */
DECL_ATTRIBUTES (d)
- = DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]);
+ = lookup_attribute ("non overlapping",
+ DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]));
/* Mark the variable as undefined -- but remember that we can
define it later if we need to do so. */
@@ -492,6 +499,16 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
if (CLASS_TYPE_P (type))
CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type)) = d;
+ /* Copy sym alias attributes from the type to the rtti obj decl. */
+ tree *attrs = &DECL_ATTRIBUTES (d);
+ FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+ {
+ tree attr = tree_cons (TREE_PURPOSE (sym), TREE_VALUE (sym), *attrs);
+ *attrs = attr;
+ attrs = &TREE_CHAIN (attr);
+ }
+ create_sym_alias_decls (d);
+
/* Add decl to the global array of tinfo decls. */
vec_safe_push (unemitted_tinfo_decls, d);
}
@@ -499,6 +516,58 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
return d;
}
+/* After modifying the attributes of TYPE, check whether tinfo was
+ already created and, if so, add to it any sym alias attributes
+ that were not already present. */
+
+void
+update_tinfo_sym_alias (tree type)
+{
+ if (!TYPE_SIZE (type) || !CLASS_TYPE_P (type))
+ return;
+
+ tree d = CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type));
+ if (!d)
+ return;
+
+ bool first = true;
+ symtab_node *node = NULL;
+
+ tree *attrs = &DECL_ATTRIBUTES (d);
+ FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
+ {
+ bool found = false;
+ FOR_EACH_SYM_ALIAS (d_sym, *attrs)
+ if (TREE_VALUE (sym) == TREE_VALUE (d_sym))
+ {
+ found = true;
+ break;
+ }
+
+ if (found)
+ continue;
+
+ tree attr = tree_cons (TREE_PURPOSE (sym),
+ TREE_VALUE (sym),
+ *attrs);
+ *attrs = attr;
+ attrs = &TREE_CHAIN (attr);
+
+ if (first)
+ {
+ first = false;
+ node = symtab_node::get (d);
+ }
+
+ if (!node)
+ continue;
+
+ tree id = TREE_VALUE (TREE_VALUE (sym));
+ id = get_identifier (TREE_STRING_POINTER (id));
+ create_sym_alias_decl (d, id);
+ }
+}
+
/* Return the type_info object for TYPE. */
tree
@@ -4142,6 +4142,49 @@ Function Attributes}, @ref{PowerPC Function Attributes},
@ref{Nios II Function Attributes}, and @ref{S/390 Function Attributes}
for details.
+@cindex @code{sym_alias} function attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} attribute causes @var{name} to be output as an
+alias to the function, with the same linkage and visibility as the
+function, when the function definition is output, provided that the
+function could be an alias target. For instance,
+
+@smallexample
+void f (uint64_t) __attribute__ ((__sym_alias__ ("f_u64")));
+void f (uint64_t) @{ /* @r{Do something.} */; @}
+@end smallexample
+
+@noindent
+defines @samp{f}, and outputs @samp{f_u64} as an alias for @samp{f},
+with the same linkage and visibility. This is particularly useful when
+exporting C++ names for use in other languages, or as an alias target,
+when machine-dependent types would make mangled names harder to deal
+with.
+
+In the case of C++ constructors and destructors, in which a single
+definition may output multiple symbols, the specified name is associated
+with the variant that constructs or destructs a complete object. The
+variant that applies to a base subobject gets a @code{_Base} suffix, and
+the deleting destructor gets a @code{_Del} suffix.
+
+This attribute is silently ignored if @samp{f} is not defined in the
+same translation unit, so that the attribute can be attached to forward
+declarations.
+
+Aliases introduced with this attribute, such as @samp{f_u64} in the
+example above, are assembly symbol names: they do not undergo C++ name
+mangling, and are not made visible in any scope in the source language.
+They can, however, be named as alias targets, as long as their
+definition is output. Naming a @samp{sym_alias} as an alias target will
+@emph{not} cause a definition to be output if it otherwise wouldn't.
+This may affect inline functions, C++ template instantiations, and other
+synthesized definitions that would have to be (synthesized and) compiled
+in order for the @samp{sym_alias} to be considered as a potential alias
+target.
+
+This attribute requires assembler and object file support for aliases,
+and may not be available on all targets.
+
@cindex @code{symver} function attribute
@item symver ("@var{name2}@@@var{nodename}")
On ELF targets this attribute creates a symbol version. The @var{name2} part
@@ -8069,6 +8112,10 @@ will be placed in new, unique sections.
This additional functionality requires Binutils version 2.36 or later.
+@cindex @code{sym_alias} variable attribute
+@item sym_alias ("@var{name}")
+See @pxref{Common Function Attributes}.
+
@cindex @code{uninitialized} variable attribute
@item uninitialized
This attribute, attached to a variable with automatic storage, means that
@@ -9164,6 +9211,21 @@ is not supported; that is to say, if a given scalar object can be accessed
through distinct types that assign a different storage order to it, then the
behavior is undefined.
+@cindex @code{sym_alias} type attribute
+@item sym_alias ("@var{name}")
+The @code{sym_alias} type attribute causes @var{name} to be emitted as an
+alias to the definition of the C++ Run-Time Type Information (RTTI)
+@code{std::type_info} object associated with the type. For instance,
+
+@smallexample
+class foo __attribute__ ((__sym_alias__ ("TI_foo")));
+@end smallexample
+
+@noindent
+arranges for @samp{TI_foo} to be defined as an alias to the RTTI object
+for class @samp{foo}, once the class is defined and used in ways that
+cause its RTTI object to be synthesized and output.
+
@cindex @code{transparent_union} type attribute
@item transparent_union
@@ -187,6 +187,22 @@ symbol_table::insert_to_assembler_name_hash (symtab_node *node,
(*aslot)->previous_sharing_asm_name = node;
*aslot = node;
+ /* Check for sym_alias name clashes. In create_sym_alias_decl,
+ we check for a preexisting definition using the same
+ assembler_name, so here we check for a previously-defined
+ sym_name, marked with a "sym name" pseudo-attribute that
+ points back at the declaration for which it was created. */
+ if (symtab_node *sym_node = node->next_sharing_asm_name)
+ if (lookup_attribute ("sym alias",
+ DECL_ATTRIBUTES (sym_node->decl)))
+ {
+ error_at (DECL_SOURCE_LOCATION (node->decl),
+ "duplicate symbol name %qE", decl);
+ inform (DECL_SOURCE_LOCATION (sym_node->decl),
+ "already used by %qD in a %qE attribute",
+ sym_node->decl, get_identifier ("sym_alias"));
+ }
+
/* Update also possible inline clones sharing a decl. */
cnode = dyn_cast <cgraph_node *> (node);
if (cnode && cnode->clones && with_clones)
@@ -1943,6 +1959,46 @@ symtab_node::noninterposable_alias (symtab_node *node, void *data)
return false;
}
+/* Remap sym alias nodes recorded as aliasing REPLACED to alias
+ REPLACEMENT instead. */
+
+void
+symtab_node::remap_sym_alias_target (tree replaced, tree replacement)
+{
+ symtab_node *repl_node = NULL;
+ if (!decl_in_symtab_p (replacement)
+ || !(repl_node = symtab_node::get (replacement)))
+ return;
+
+ FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (replaced))
+ {
+ tree id = TREE_VALUE (TREE_VALUE (sym));
+ id = get_identifier (TREE_STRING_POINTER (id));
+
+ symtab_node *sym_node = symtab_node::get_for_asmname (id);
+
+ if (!sym_node)
+ {
+ create_sym_alias_decl (replacement, id);
+ continue;
+ }
+
+ gcc_assert (!sym_node->analyzed);
+ if (sym_node->alias_target != replaced)
+ continue;
+
+ // sym_node->definition = 0;
+
+ if (VAR_P (replaced))
+ // varpool_node::create_extra_name_alias (sym_node->decl, replacement);
+ varpool_node::create_alias (sym_node->decl, replacement);
+ else
+ cgraph_node::create_same_body_alias (sym_node->decl, replacement);
+ // cgraph_node::create_alias (sym_node->decl, replacement);
+ sym_node->copy_visibility_from (repl_node);
+ }
+}
+
/* If node cannot be overwriten by static or dynamic linker to point to
different definition, return NODE. Otherwise look for alias with such
property and if none exists, introduce new one. */
@@ -1970,6 +2026,12 @@ symtab_node::noninterposable_alias (void)
/* Otherwise create a new one. */
new_decl = copy_node (node->decl);
+ DECL_ATTRIBUTES (new_decl) = remove_attribute ("sym_alias",
+ DECL_ATTRIBUTES
+ (new_decl));
+ DECL_ATTRIBUTES (new_decl) = remove_attribute ("sym alias",
+ DECL_ATTRIBUTES
+ (new_decl));
DECL_DLLIMPORT_P (new_decl) = 0;
tree name = clone_function_name (node->decl, "localalias");
if (!flag_wpa)
@@ -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)
@@ -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'" "" } */
}
new file mode 100644
@@ -0,0 +1,75 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+extern int var_alias __attribute__ ((__alias__ ("FOOVAR_A")));
+int var_a = 1;
+
+void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+
+void foo_alias () __attribute__ ((__alias__ ("FOOBAR_A")));
+
+void
+foo_a ()
+{
+}
+
+
+int var_b;
+extern int var_b __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+
+void
+foo_b ()
+{
+}
+
+void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+
+
+int var_c __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+
+void __attribute__ ((__sym_alias__ ("FOOBAR_C")))
+foo_c ()
+{
+}
+
+
+void baf () {}
+
+void foo_x () {
+ extern void baf () __attribute__ ((__sym_alias__ ("FOO_BAF")));
+ extern void bad () /* ok, but not defined, so no alias issued. */
+ __attribute__ ((__sym_alias__ ("FOO_BAD")));
+ extern void bar () __attribute__ ((__sym_alias__ ("FOO_BAR")));
+
+ static int x __attribute__ ((sym_alias ("FOO_x")));
+}
+
+void bar () {}
+
+extern int xr __attribute__ ((alias ("FOO_x")));
+
+
+ /* The *u* symbols are not defined, so the sym_aliases do not clash. ??? Is
+ this just an implementation artifact, or a feature someone might actually
+ rely on? */
+extern int var_u1 __attribute__ ((__sym_alias__ ("VAR_U")));
+extern int var_u2 __attribute__ ((__sym_alias__ ("VAR_U")));
+
+void foo_u1 () __attribute__ ((__sym_alias__ ("FOOBAR_U")));
+void foo_u2 () __attribute__ ((__sym_alias__ ("FOOBAR_U")));
+
+
+/* { dg-final { scan-assembler "FOOBAR_A" } } */
+/* { dg-final { scan-assembler "foo_alias" } } */
+/* { dg-final { scan-assembler "FOOVAR_A" } } */
+/* { dg-final { scan-assembler "var_alias" } } */
+/* { dg-final { scan-assembler "FOOBAR_B" } } */
+/* { dg-final { scan-assembler "FOOVAR_B" } } */
+/* { dg-final { scan-assembler "FOOBAR_C" } } */
+/* { dg-final { scan-assembler "FOOVAR_C" } } */
+/* { dg-final { scan-assembler "FOO_BAF" } } */
+/* { dg-final { scan-assembler "FOO_BAR" } } */
+/* { dg-final { scan-assembler-not "FOO_BAD" } } */
+/* { dg-final { scan-assembler-not "VAR_U" } } */
+/* { dg-final { scan-assembler-not "FOOBAR_U" } } */
new file mode 100644
@@ -0,0 +1,99 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+struct s
+{
+ int mem __attribute__ ((__sym_alias__ ("S_MEM"))); /* { dg-warning "attribute ignored" } */
+};
+
+void foo () {
+ extern void bar () __attribute__ ((__sym_alias__ ("FOO_BAR"))); /* Ok. */
+ int i __attribute__ ((sym_alias ("FOO_i"))); /* { dg-warning "ignored" } */
+ static int x __attribute__ ((sym_alias ("FOO_x")));
+ static int sx __attribute__ ((alias ("FOO_x"))); /* { dg-warning "ignored" } */
+ extern int xx __attribute__ ((alias ("FOO_x"))); /* { dg-warning "ignored" } */
+ extern int y __attribute__ ((sym_alias ("FOO_y"))); /* Ok. */
+}
+
+int dr __attribute__ ((alias ("FOO_x"))); /* { dg-error "defined both" } */
+
+
+int j __attribute__ ((sym_alias ("J_var")));
+void __attribute__ ((alias ("J_var")))
+j_fn (); /* { dg-error "between function and variable" } */
+
+
+void __attribute__ ((sym_alias ("K_fn")))
+k () {
+}
+extern int __attribute__ ((alias ("K_fn")))
+k_var; /* { dg-error "between function and variable" } */
+
+
+int l __attribute__ ((sym_alias ("L_fn")));
+
+/* These should be detected and reported, not because the names clash at the
+ source level, but because the asm symbols do. */
+#ifdef __cplusplus
+extern "C"
+#endif
+void
+L_fn () /* { dg-error "duplicate" "" } */
+{
+}
+
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __attribute__ ((sym_alias ("M_var")))
+m ()
+{
+}
+
+int M_var; /* { dg-error "duplicate" "" } */
+
+
+void __attribute__ ((sym_alias ("N_sym")))
+n_fn ()
+{
+}
+
+int __attribute__ ((sym_alias ("N_sym")))
+n_var; /* { dg-error "duplicate" } */
+
+
+int __attribute__ ((sym_alias ("O_sym")))
+o_var;
+
+void __attribute__ ((sym_alias ("O_sym")))
+o_fn () /* { dg-error "duplicate" } */
+{
+}
+
+int __attribute__ ((sym_alias ("P_sym")))
+p_var1;
+
+int __attribute__ ((sym_alias ("P_sym")))
+p_var2; /* { dg-error "duplicate" } */
+
+void __attribute__ ((sym_alias ("Q_sym")))
+q_fn1 ()
+{
+}
+
+void __attribute__ ((sym_alias ("Q_sym")))
+q_fn2 () /* { dg-error "duplicate" } */
+{
+}
+
+int __attribute__ ((sym_alias ("R_var")))
+R_var; /* { dg-error "duplicate" } */
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __attribute__ ((sym_alias ("S_fn")))
+S_fn () /* { dg-error "duplicate" } */
+{
+}
new file mode 100644
@@ -0,0 +1,34 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+int var_a = 1;
+
+void
+foo_a ()
+{
+ extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+ void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
+}
+
+void
+foo_b ()
+{
+ extern int var_b __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+}
+
+int var_b;
+
+void __attribute__ ((__sym_alias__ ("FOOBAR_C")))
+foo_c ()
+{
+ void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
+ /* Another sym for var_b. */
+ extern int var_b __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+}
+
+/* { dg-final { scan-assembler "FOOBAR_A" } } */
+/* { dg-final { scan-assembler "FOOVAR_A" } } */
+/* { dg-final { scan-assembler "FOOBAR_B" } } */
+/* { dg-final { scan-assembler "FOOVAR_B" } } */
+/* { dg-final { scan-assembler "FOOBAR_C" } } */
+/* { dg-final { scan-assembler "FOOVAR_C" } } */
new file mode 100644
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-require-alias "" } */
+
+int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A"))) = 42;
+
+int __attribute__ ((__sym_alias__ ("FOOBAR_A")))
+foo_a (int p)
+{
+ return p;
+}
+
+extern int __attribute__ ((__alias__ (("FOOVAR_A")))) var_b;
+extern int __attribute__ ((__alias__ (("FOOBAR_A")))) foo_b (int p);
+
+int
+foo_c ()
+{
+ return foo_b (var_b);
+}
+
+int
+main ()
+{
+ if (foo_c () != 42)
+ __builtin_abort ();
+
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,54 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+/* { dg-require-visibility "" } */
+
+int __attribute__ ((sym_alias ("A_hid"), visibility ("hidden"))) a;
+
+extern int __attribute__ ((sym_alias ("B_prt"))) b;
+
+int b __attribute__ ((visibility ("protected")));
+
+extern int __attribute__ ((sym_alias ("C_ntr"))) c;
+
+extern int c __attribute__ ((visibility ("internal")));
+
+int c;
+
+static int d __attribute__ ((sym_alias ("D_stt")));
+
+
+extern void __attribute__ ((sym_alias ("F_hid"), visibility ("hidden")))
+f ();
+
+void
+f () {
+}
+
+extern void __attribute__ ((sym_alias ("G_prt")))
+g ();
+
+void __attribute__ ((visibility ("protected")))
+g () {
+}
+
+extern void __attribute__ ((sym_alias ("H_ntr")))
+h ();
+
+void __attribute__ ((visibility ("internal")))
+h ();
+
+void h () {
+}
+
+static void __attribute__ ((sym_alias ("I_stt")))
+i () {
+}
+
+/* { dg-final { scan-assembler {hidden[^\n]*A_hid} } } */
+/* { dg-final { scan-assembler {protected[^\n]*B_prt} } } */
+/* { dg-final { scan-assembler {internal[^\n]*C_ntr} } } */
+/* { dg-final { scan-assembler-not {glob[^\n]*D_stt} } } */
+/* { dg-final { scan-assembler {hidden[^\n]*F_hid} } } */
+/* { dg-final { scan-assembler {protected[^\n]*G_prt} } } */
+/* { dg-final { scan-assembler {internal[^\n]*H_ntr} } } */
+/* { dg-final { scan-assembler-not {glob[^\n]*I_stt} } } */
new file mode 100644
@@ -0,0 +1,89 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+class __attribute__ ((__sym_alias__ ("FOOCLS_A"),
+ __sym_alias__ ("FOOCLS_A_Dupe"))) foo {
+ static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+ __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+ void __attribute__ ((__sym_alias__ ("FOOBAR_A"))) bar ();
+ virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+int foo::var = 1;
+
+foo::foo () {}
+
+void foo::bar () {}
+
+namespace b {
+ class __attribute__ ((__sym_alias__ ("FOOCLS_B"))) foo {
+ static int var __attribute__ ((__sym_alias__ ("FOOVAR_B")));
+ __attribute__ ((__sym_alias__ ("FOOCTR_B"))) foo ();
+ void __attribute__ ((__sym_alias__ ("FOOBAR_B"))) bar () {}
+ virtual __attribute__ ((__sym_alias__ ("FOODTR_B"))) ~foo() {}
+ };
+
+ int foo::var = 2;
+
+ foo::foo () {
+ void (foo::*pbar)() = &foo::bar;
+ }
+}
+
+namespace c {
+ namespace cd {
+ class __attribute__ ((__sym_alias__ ("FOOCLS_C"))) foo {
+ static int var __attribute__ ((__sym_alias__ ("FOOVAR_C")));
+ __attribute__ ((__sym_alias__ ("FOOCTR_C"))) foo () {
+ void (foo::*pbar)() = &foo::bar;
+ }
+ void __attribute__ ((__sym_alias__ ("FOOBAR_C"))) bar () {}
+ virtual __attribute__ ((__sym_alias__ ("FOODTR_C"))) ~foo() {}
+ };
+
+ int foo::var = 3;
+ }
+}
+
+#include <typeinfo>
+
+namespace d {
+ namespace {
+ class __attribute__ ((__sym_alias__ ("FOOCLS_D"))) foo {
+ virtual ~foo() {} // typeid(foo) is not defined otherwise.
+ };
+ }
+
+ extern std::type_info __attribute__ ((__alias__ ("FOOCLS_D"))) foo_d_typeinfo;
+}
+
+extern std::type_info __attribute__ ((__alias__ ("FOOCLS_C"))) foo_c_typeinfo;
+
+/* { dg-final { scan-assembler "FOOCLS_A" } } */
+/* { dg-final { scan-assembler "FOOCLS_A_Dupe" } } */
+/* { dg-final { scan-assembler "FOOBAR_A" } } */
+/* { dg-final { scan-assembler "FOOCTR_A" } } */
+/* { dg-final { scan-assembler "FOOCTR_A_Base" } } */
+/* { dg-final { scan-assembler "FOODTR_A" } } */
+/* { dg-final { scan-assembler "FOODTR_A_Base" } } */
+/* { dg-final { scan-assembler "FOODTR_A_Del" } } */
+/* { dg-final { scan-assembler "FOOVAR_A" } } */
+/* { dg-final { scan-assembler "FOOCLS_B" } } */
+/* { dg-final { scan-assembler "FOOBAR_B" } } */
+/* { dg-final { scan-assembler "FOOCTR_B" } } */
+/* { dg-final { scan-assembler "FOOCTR_A_Base" } } */
+/* { dg-final { scan-assembler "FOODTR_B" } } */
+/* { dg-final { scan-assembler "FOODTR_B_Base" } } */
+/* { dg-final { scan-assembler "FOODTR_B_Del" } } */
+/* { dg-final { scan-assembler "FOOVAR_B" } } */
+/* { dg-final { scan-assembler "FOOCLS_C" } } */
+/* { dg-final { scan-assembler "FOOBAR_C" } } */
+/* { dg-final { scan-assembler "FOOCTR_C" } } */
+/* { dg-final { scan-assembler "FOOCTR_C_Base" } } */
+/* { dg-final { scan-assembler "FOODTR_C" } } */
+/* { dg-final { scan-assembler "FOODTR_C_Base" } } */
+/* { dg-final { scan-assembler "FOODTR_C_Del" } } */
+/* { dg-final { scan-assembler "FOOVAR_C" } } */
+/* { dg-final { scan-assembler "FOOCLS_D" } } */
+/* { dg-final { scan-assembler "foo_d_typeinfo" } } */
+/* { dg-final { scan-assembler "foo_c_typeinfo" } } */
new file mode 100644
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+namespace {
+ class __attribute__ ((__sym_alias__ ("FOOCLS_A"))) foo {
+ static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
+ __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+ virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo ();
+ void __attribute__ ((__sym_alias__ ("FOOBAR_A"))) bar ();
+ };
+
+ int foo::var = 3;
+ foo::foo () {}
+ foo::~foo () {}
+ void foo::bar () {}
+}
+
+/* { dg-final { scan-assembler-not "\.globl" } } */
+/* { dg-final { scan-assembler "FOOCLS_A" } } */
+/* { dg-final { scan-assembler "FOOBAR_A" } } */
+/* { dg-final { scan-assembler "FOOCTR_A" } } */
+/* { dg-final { scan-assembler "FOOCTR_A_Base" } } */
+/* { dg-final { scan-assembler "FOODTR_A" } } */
+/* { dg-final { scan-assembler "FOODTR_A_Base" } } */
+/* { dg-final { scan-assembler "FOODTR_A_Del" } } */
+/* { dg-final { scan-assembler "FOOVAR_A" } } */
new file mode 100644
@@ -0,0 +1,83 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+// sym can be applied to template function explicit instantiations.
+
+template <typename T>
+void
+fn(T) {
+};
+
+template void __attribute__ ((__sym_alias__ ("FOOFUN_UINT"))) fn<>(unsigned int);
+template void __attribute__ ((__sym_alias__ ("FOOFUN_LONG"))) fn<>(long);
+
+template<> void __attribute__ ((__sym_alias__ ("FOOFUN_CHAR"))) fn<>(char) {}
+
+
+template <typename T = void>
+struct
+foo {
+ virtual ~foo() {}
+
+ virtual void virtfun() {}
+
+ static void stfun() {}
+ void inlfun() {}
+};
+
+// Explicitly instantiate members before the enclosing class.
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_CHAR_VIRT"))) foo<char>::virtfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_CHAR_TI"))) foo<char>;
+
+// Though they're only output if the enclosing class is.
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_LONG_VIRT"))) foo<long>::virtfun();
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_LONG_TI_X"))) foo<long>;
+
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_VOID_ST"))) foo<void>::stfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_VOID_TI"))) foo<>;
+
+
+extern
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_X"))) foo<short>;
+
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_ST"))) foo<short>::stfun();
+template void
+__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_INL"))) foo<short>::inlfun();
+
+template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_D"))) foo<short>;
+
+// Explicit specializations work too.
+
+template <>
+struct __attribute__ ((__sym_alias__ ("FOOCLS_INT_TI")))
+foo<int>
+{
+ virtual ~foo() {}
+ virtual void __attribute__ ((__sym_alias__ ("FOOCLS_INT_VIRT"))) virtfun() {}
+};
+
+/* { dg-final { scan-assembler "FOOFUN_UINT" } } */
+/* { dg-final { scan-assembler "FOOFUN_LONG" } } */
+/* { dg-final { scan-assembler "FOOFUN_CHAR" } } */
+
+/* { dg-final { scan-assembler "FOOCLS_VOID_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_VOID_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_CHAR_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_X" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_ST" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_INL" } } */
+/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_D" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_TI_X" } } */
+/* { dg-final { scan-assembler-not "FOOCLS_LONG_VIRT" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_TI" } } */
+/* { dg-final { scan-assembler "FOOCLS_INT_VIRT" } } */
new file mode 100644
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+
+template <typename T = void>
+class
+__attribute__ ((__sym_alias__ ("FOOCLS")))
+foo // { dg-error "duplicate|already" }
+{
+ virtual ~foo() {}
+
+ template <typename U>
+ void
+ __attribute__ ((__sym_alias__ ("FOOTMF")))
+ tmemfun () {} // { dg-error "duplicate|already" }
+};
+
+template <typename T>
+void
+__attribute__ ((__sym_alias__ ("FOOTFN")))
+fn(T) { // { dg-error "duplicate|already" }
+};
+
+template class foo<>;
+template class foo<int>;
+template void foo<>::tmemfun<void>();
+template void foo<int>::tmemfun<void>();
+template void fn<>(int);
+template void fn<>(long);
new file mode 100644
@@ -0,0 +1,14 @@
+/* { dg-do compile { target c++11 } } */
+/* { dg-require-alias "" } */
+
+struct foo {
+ __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
+ virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
+};
+
+foo::foo () {}
+
+// Make sure the inherited cdtors don't duplicate the syms.
+struct bar : foo {
+ using foo::foo;
+};
@@ -163,6 +163,9 @@ varpool_node::get_create (tree decl)
}
node->register_symbol ();
+
+ create_sym_alias_decls (decl);
+
return node;
}