Add C++ attribute abi_tag and -Wabi-tag option.

gcc/
	* attribs.c (lookup_attribute_spec): Handle getting a TREE_LIST.
gcc/c-family/
	* c.opt (Wabi-tag): New.
gcc/cp/
	* tree.c (cxx_attribute_table): Add abi_tag attribute.
	(check_abi_tag_redeclaration, handle_abi_tag_attribute): New.
	* class.c (find_abi_tags_r, check_abi_tags): New.
	(check_bases, check_field_decl): Call check_abi_tags.
	* decl.c (redeclaration_error_message): Call
	check_abi_tag_redeclaration.
	* mangle.c (tree_string_cmp, write_abi_tags): New.
	(write_unqualified_name): Call write_abi_tags.
include/
	* demangle.h (enum demangle_component_type): Add
	DEMANGLE_COMPONENT_TAGGED_NAME.
libiberty/
	* cp-demangle.c (d_dump): Handle DEMANGLE_COMPONENT_TAGGED_NAME.
	(d_make_comp, d_find_pack, d_print_comp): Likewise.
	(d_abi_tags): New.
	(d_name): Call it.

From-SVN: r193367
This commit is contained in:
Jason Merrill 2012-11-09 11:14:37 -05:00 committed by Jason Merrill
parent 52dccf7ac9
commit 7dbb85a793
18 changed files with 387 additions and 15 deletions

View File

@ -1,3 +1,7 @@
2012-11-09 Jason Merrill <jason@redhat.com>
* attribs.c (lookup_attribute_spec): Handle getting a TREE_LIST.
2012-11-09 Vladimir Makarov <vmakarov@redhat.com>
PR tree-optimization/55154

View File

@ -325,12 +325,21 @@ lookup_scoped_attribute_spec (const_tree ns, const_tree name)
substring_hash (attr.str, attr.length));
}
/* Return the spec for the attribute named NAME. */
/* Return the spec for the attribute named NAME. If NAME is a TREE_LIST,
it also specifies the attribute namespace. */
const struct attribute_spec *
lookup_attribute_spec (const_tree name)
{
return lookup_scoped_attribute_spec (get_identifier ("gnu"), name);
tree ns;
if (TREE_CODE (name) == TREE_LIST)
{
ns = TREE_PURPOSE (name);
name = TREE_VALUE (name);
}
else
ns = get_identifier ("gnu");
return lookup_scoped_attribute_spec (ns, name);
}

View File

@ -1,3 +1,7 @@
2012-11-09 Jason Merrill <jason@redhat.com>
* c.opt (Wabi-tag): New.
2012-11-09 Andi Kleen <ak@linux.intel.com>
PR 55139

View File

@ -257,6 +257,10 @@ Wabi
C ObjC C++ ObjC++ LTO Var(warn_abi) Warning
Warn about things that will change when compiling with an ABI-compliant compiler
Wabi-tag
C++ ObjC++ Var(warn_abi_tag) Warning
Warn if a subobject has an abi_tag attribute that the complete object type does not have
Wpsabi
C ObjC C++ ObjC++ LTO Var(warn_psabi) Init(1) Undocumented

View File

@ -1,3 +1,14 @@
2012-11-09 Jason Merrill <jason@redhat.com>
* tree.c (cxx_attribute_table): Add abi_tag attribute.
(check_abi_tag_redeclaration, handle_abi_tag_attribute): New.
* class.c (find_abi_tags_r, check_abi_tags): New.
(check_bases, check_field_decl): Call check_abi_tags.
* decl.c (redeclaration_error_message): Call
check_abi_tag_redeclaration.
* mangle.c (tree_string_cmp, write_abi_tags): New.
(write_unqualified_name): Call write_abi_tags.
2012-11-07 Paolo Carlini <paolo.carlini@oracle.com>
PR c++/55226

View File

@ -1302,6 +1302,89 @@ handle_using_decl (tree using_decl, tree t)
alter_access (t, decl, access);
}
/* walk_tree callback for check_abi_tags: if the type at *TP involves any
types with abi tags, add the corresponding identifiers to the VEC in
*DATA and set IDENTIFIER_MARKED. */
struct abi_tag_data
{
tree t;
tree subob;
};
static tree
find_abi_tags_r (tree *tp, int */*walk_subtrees*/, void *data)
{
if (!TAGGED_TYPE_P (*tp))
return NULL_TREE;
if (tree attributes = lookup_attribute ("abi_tag", TYPE_ATTRIBUTES (*tp)))
{
struct abi_tag_data *p = static_cast<struct abi_tag_data*>(data);
for (tree list = TREE_VALUE (attributes); list;
list = TREE_CHAIN (list))
{
tree tag = TREE_VALUE (list);
tree id = get_identifier (TREE_STRING_POINTER (tag));
if (!IDENTIFIER_MARKED (id))
{
if (TYPE_P (p->subob))
{
warning (OPT_Wabi_tag, "%qT does not have the %E abi tag "
"that base %qT has", p->t, tag, p->subob);
inform (location_of (p->subob), "%qT declared here",
p->subob);
}
else
{
warning (OPT_Wabi_tag, "%qT does not have the %E abi tag "
"that %qT (used in the type of %qD) has",
p->t, tag, *tp, p->subob);
inform (location_of (p->subob), "%qD declared here",
p->subob);
inform (location_of (*tp), "%qT declared here", *tp);
}
}
}
}
return NULL_TREE;
}
/* Check that class T has all the abi tags that subobject SUBOB has, or
warn if not. */
static void
check_abi_tags (tree t, tree subob)
{
tree attributes = lookup_attribute ("abi_tag", TYPE_ATTRIBUTES (t));
if (attributes)
{
for (tree list = TREE_VALUE (attributes); list;
list = TREE_CHAIN (list))
{
tree tag = TREE_VALUE (list);
tree id = get_identifier (TREE_STRING_POINTER (tag));
IDENTIFIER_MARKED (id) = true;
}
}
tree subtype = TYPE_P (subob) ? subob : TREE_TYPE (subob);
struct abi_tag_data data = { t, subob };
cp_walk_tree_without_duplicates (&subtype, find_abi_tags_r, &data);
if (attributes)
{
for (tree list = TREE_VALUE (attributes); list;
list = TREE_CHAIN (list))
{
tree tag = TREE_VALUE (list);
tree id = get_identifier (TREE_STRING_POINTER (tag));
IDENTIFIER_MARKED (id) = false;
}
}
}
/* Run through the base classes of T, updating CANT_HAVE_CONST_CTOR_P,
and NO_CONST_ASN_REF_P. Also set flag bits in T based on
properties of the bases. */
@ -1431,6 +1514,8 @@ check_bases (tree t,
if (tm_attr)
seen_tm_mask |= tm_attr_to_mask (tm_attr);
}
check_abi_tags (t, basetype);
}
/* If one of the base classes had TM attributes, and the current class
@ -3147,6 +3232,9 @@ check_field_decl (tree field,
&& !TYPE_HAS_CONST_COPY_ASSIGN (type))
*no_const_asn_ref = 1;
}
check_abi_tags (t, field);
if (DECL_INITIAL (field) != NULL_TREE)
{
/* `build_class_init_list' does not recognize

View File

@ -5749,6 +5749,7 @@ extern bool type_has_nontrivial_default_init (const_tree);
extern bool type_has_nontrivial_copy_init (const_tree);
extern bool class_tmpl_impl_spec_p (const_tree);
extern int zero_init_p (const_tree);
extern bool check_abi_tag_redeclaration (const_tree, const_tree, const_tree);
extern tree strip_typedefs (tree);
extern tree strip_typedefs_expr (tree);
extern tree copy_binfo (tree, tree, tree,

View File

@ -2480,6 +2480,10 @@ redeclaration_error_message (tree newdecl, tree olddecl)
}
}
check_abi_tag_redeclaration
(olddecl, lookup_attribute ("abi_tag", DECL_ATTRIBUTES (olddecl)),
lookup_attribute ("abi_tag", DECL_ATTRIBUTES (newdecl)));
return NULL;
}
else if (TREE_CODE (newdecl) == TEMPLATE_DECL)

View File

@ -173,6 +173,7 @@ static void mangle_call_offset (const tree, const tree);
static void write_mangled_name (const tree, bool);
static void write_encoding (const tree);
static void write_name (tree, const int);
static void write_abi_tags (tree);
static void write_unscoped_name (const tree);
static void write_unscoped_template_name (const tree);
static void write_nested_name (const tree);
@ -1192,15 +1193,17 @@ write_unqualified_name (const tree decl)
return;
}
bool found = false;
if (DECL_NAME (decl) == NULL_TREE)
{
found = true;
gcc_assert (DECL_ASSEMBLER_NAME_SET_P (decl));
write_source_name (DECL_ASSEMBLER_NAME (decl));
return;
}
else if (DECL_DECLARES_FUNCTION_P (decl))
{
bool found = true;
found = true;
if (DECL_CONSTRUCTOR_P (decl))
write_special_name_constructor (decl);
else if (DECL_DESTRUCTOR_P (decl))
@ -1234,14 +1237,13 @@ write_unqualified_name (const tree decl)
write_literal_operator_name (DECL_NAME (decl));
else
found = false;
if (found)
return;
}
if (VAR_OR_FUNCTION_DECL_P (decl) && ! TREE_PUBLIC (decl)
&& DECL_NAMESPACE_SCOPE_P (decl)
&& decl_linkage (decl) == lk_internal)
if (found)
/* OK */;
else if (VAR_OR_FUNCTION_DECL_P (decl) && ! TREE_PUBLIC (decl)
&& DECL_NAMESPACE_SCOPE_P (decl)
&& decl_linkage (decl) == lk_internal)
{
MANGLE_TRACE_TREE ("local-source-name", decl);
write_char ('L');
@ -1262,6 +1264,11 @@ write_unqualified_name (const tree decl)
else
write_source_name (DECL_NAME (decl));
}
tree attrs = (TREE_CODE (decl) == TYPE_DECL
? TYPE_ATTRIBUTES (TREE_TYPE (decl))
: DECL_ATTRIBUTES (decl));
write_abi_tags (lookup_attribute ("abi_tag", attrs));
}
/* Write the unqualified-name for a conversion operator to TYPE. */
@ -1291,6 +1298,51 @@ write_source_name (tree identifier)
write_identifier (IDENTIFIER_POINTER (identifier));
}
/* Compare two TREE_STRINGs like strcmp. */
int
tree_string_cmp (const void *p1, const void *p2)
{
if (p1 == p2)
return 0;
tree s1 = *(const tree*)p1;
tree s2 = *(const tree*)p2;
return strcmp (TREE_STRING_POINTER (s1),
TREE_STRING_POINTER (s2));
}
/* ID is the name of a function or type with abi_tags attribute TAGS.
Write out the name, suitably decorated. */
static void
write_abi_tags (tree tags)
{
if (tags == NULL_TREE)
return;
tags = TREE_VALUE (tags);
VEC(tree,gc)* vec = make_tree_vector();
for (tree t = tags; t; t = TREE_CHAIN (t))
{
tree str = TREE_VALUE (t);
VEC_safe_push (tree, gc, vec, str);
}
VEC_qsort (tree, vec, tree_string_cmp);
unsigned i; tree str;
FOR_EACH_VEC_ELT (tree, vec, i, str)
{
write_string ("B");
write_unsigned_number (TREE_STRING_LENGTH (str) - 1);
write_identifier (TREE_STRING_POINTER (str));
}
release_tree_vector (vec);
}
/* Write a user-defined literal operator.
::= li <source-name> # "" <source-name>
IDENTIFIER is an LITERAL_IDENTIFIER_NODE. */

View File

@ -49,6 +49,7 @@ static tree build_local_temp (tree);
static tree handle_java_interface_attribute (tree *, tree, tree, int, bool *);
static tree handle_com_interface_attribute (tree *, tree, tree, int, bool *);
static tree handle_init_priority_attribute (tree *, tree, tree, int, bool *);
static tree handle_abi_tag_attribute (tree *, tree, tree, int, bool *);
/* If REF is an lvalue, returns the kind of lvalue that REF is.
Otherwise, returns clk_none. */
@ -3000,6 +3001,8 @@ const struct attribute_spec cxx_attribute_table[] =
handle_com_interface_attribute, false },
{ "init_priority", 1, 1, true, false, false,
handle_init_priority_attribute, false },
{ "abi_tag", 1, -1, false, false, false,
handle_abi_tag_attribute, true },
{ NULL, 0, 0, false, false, false, NULL, false }
};
@ -3131,6 +3134,96 @@ handle_init_priority_attribute (tree* node,
}
}
/* DECL is being redeclared; the old declaration had the abi tags in OLD,
and the new one has the tags in NEW_. Give an error if there are tags
in NEW_ that weren't in OLD. */
bool
check_abi_tag_redeclaration (const_tree decl, const_tree old, const_tree new_)
{
if (old && TREE_CODE (TREE_VALUE (old)) == TREE_LIST)
old = TREE_VALUE (old);
if (new_ && TREE_CODE (TREE_VALUE (new_)) == TREE_LIST)
new_ = TREE_VALUE (new_);
bool err = false;
for (const_tree t = new_; t; t = TREE_CHAIN (t))
{
tree str = TREE_VALUE (t);
for (const_tree in = old; in; in = TREE_CHAIN (in))
{
tree ostr = TREE_VALUE (in);
if (cp_tree_equal (str, ostr))
goto found;
}
error ("redeclaration of %qD adds abi tag %E", decl, str);
err = true;
found:;
}
if (err)
{
inform (DECL_SOURCE_LOCATION (decl), "previous declaration here");
return false;
}
return true;
}
/* Handle an "abi_tag" attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_abi_tag_attribute (tree* node, tree name, tree args,
int flags, bool* no_add_attrs)
{
if (TYPE_P (*node))
{
if (!TAGGED_TYPE_P (*node))
{
error ("%qE attribute applied to non-class, non-enum type %qT",
name, *node);
goto fail;
}
else if (!(flags & (int)ATTR_FLAG_TYPE_IN_PLACE))
{
error ("%qE attribute applied to %qT after its definition",
name, *node);
goto fail;
}
tree attributes = TYPE_ATTRIBUTES (*node);
tree decl = TYPE_NAME (*node);
/* Make sure all declarations have the same abi tags. */
if (DECL_SOURCE_LOCATION (decl) != input_location)
{
if (!check_abi_tag_redeclaration (decl,
lookup_attribute ("abi_tag",
attributes),
args))
goto fail;
}
}
else
{
if (TREE_CODE (*node) != FUNCTION_DECL)
{
error ("%qE attribute applied to non-function %qD", name, *node);
goto fail;
}
else if (DECL_LANGUAGE (*node) == lang_c)
{
error ("%qE attribute applied to extern \"C\" function %qD",
name, *node);
goto fail;
}
}
return NULL_TREE;
fail:
*no_add_attrs = true;
return NULL_TREE;
}
/* Return a new PTRMEM_CST of the indicated TYPE. The MEMBER is the
thing pointed to by the constant. */

View File

@ -15598,6 +15598,27 @@ You must specify @option{-Wno-pmf-conversions} to use this extension.
Some attributes only make sense for C++ programs.
@table @code
@item abi_tag ("@var{tag}", ...)
@cindex @code{abi_tag} attribute
The @code{abi_tag} attribute can be applied to a function or class
declaration. It modifies the mangled name of the function or class to
incorporate the tag name, in order to distinguish the function or
class from an earlier version with a different ABI; perhaps the class
has changed size, or the function has a different return type that is
not encoded in the mangled name.
The argument can be a list of strings of arbitrary length. The
strings are sorted on output, so the order of the list is
unimportant.
A redeclaration of a function or class must not add new ABI tags,
since doing so would change the mangled name.
The @option{-Wabi-tag} flag enables a warning about a class which does
not have all the ABI tags used by its subobjects; for users with code
that needs to coexist with an earlier ABI, using this option can help
to find all affected types that need to be tagged.
@item init_priority (@var{priority})
@cindex @code{init_priority} attribute

View File

@ -1,3 +1,8 @@
2012-11-09 Jason Merrill <jason@redhat.com>
* g++.dg/abi/abi-tag1.C: New.
* g++.dg/abi/abi-tag2.C: New.
2012-11-09 Vladimir Makarov <vmakarov@redhat.com>
PR rtl-optimization/55154

View File

@ -0,0 +1,19 @@
// { dg-options "-Wabi-tag" }
// { dg-final { scan-assembler "_Z1fB3barB3fooi" } }
void f(int) __attribute ((abi_tag ("foo","bar")));
struct __attribute ((abi_tag ("bar"))) A { };
struct B: A { }; // { dg-warning "bar. abi tag" }
struct D { A* ap; }; // { dg-warning "bar. abi tag" }
// { dg-final { scan-assembler "_Z1gB3baz1AB3bar" } }
void g(A) __attribute ((abi_tag ("baz")));
void g(A) __attribute ((abi_tag ("baz")));
int main()
{
f(42);
g(A());
}

View File

@ -0,0 +1,5 @@
void f(int);
void f(int) __attribute ((abi_tag ("foo"))); // { dg-error "adds abi tag" }
struct C;
struct __attribute ((abi_tag ("foo"))) C; // { dg-error "adds abi tag" }

View File

@ -1,3 +1,8 @@
2012-11-09 Jason Merrill <jason@redhat.com>
* demangle.h (enum demangle_component_type): Add
DEMANGLE_COMPONENT_TAGGED_NAME.
2012-10-29 Sterling Augustine <saugustine@google.com>
Cary Coutant <ccoutant@google.com>

View File

@ -420,6 +420,8 @@ enum demangle_component_type
DEMANGLE_COMPONENT_NONTRANSACTION_CLONE,
/* A pack expansion. */
DEMANGLE_COMPONENT_PACK_EXPANSION,
/* A name with an ABI tag. */
DEMANGLE_COMPONENT_TAGGED_NAME,
/* A cloned function. */
DEMANGLE_COMPONENT_CLONE
};

View File

@ -1,3 +1,10 @@
2012-11-09 Jason Merrill <jason@redhat.com>
* cp-demangle.c (d_dump): Handle DEMANGLE_COMPONENT_TAGGED_NAME.
(d_make_comp, d_find_pack, d_print_comp): Likewise.
(d_abi_tags): New.
(d_name): Call it.
2012-10-08 Jason Merrill <jason@redhat.com>
* cp-demangle.c (d_special_name, d_dump): Handle TH and TW.

View File

@ -508,6 +508,11 @@ d_dump (struct demangle_component *dc, int indent)
case DEMANGLE_COMPONENT_NAME:
printf ("name '%.*s'\n", dc->u.s_name.len, dc->u.s_name.s);
return;
case DEMANGLE_COMPONENT_TAGGED_NAME:
printf ("tagged name\n");
d_dump (dc->u.s_binary.left, indent + 2);
d_dump (dc->u.s_binary.right, indent + 2);
return;
case DEMANGLE_COMPONENT_TEMPLATE_PARAM:
printf ("template parameter %ld\n", dc->u.s_number.number);
return;
@ -809,6 +814,7 @@ d_make_comp (struct d_info *di, enum demangle_component_type type,
case DEMANGLE_COMPONENT_QUAL_NAME:
case DEMANGLE_COMPONENT_LOCAL_NAME:
case DEMANGLE_COMPONENT_TYPED_NAME:
case DEMANGLE_COMPONENT_TAGGED_NAME:
case DEMANGLE_COMPONENT_TEMPLATE:
case DEMANGLE_COMPONENT_CONSTRUCTION_VTABLE:
case DEMANGLE_COMPONENT_VENDOR_TYPE_QUAL:
@ -1202,6 +1208,23 @@ d_encoding (struct d_info *di, int top_level)
}
}
/* <tagged-name> ::= <name> B <source-name> */
static struct demangle_component *
d_abi_tags (struct d_info *di, struct demangle_component *dc)
{
char peek;
while (peek = d_peek_char (di),
peek == 'B')
{
struct demangle_component *tag;
d_advance (di, 1);
tag = d_source_name (di);
dc = d_make_comp (di, DEMANGLE_COMPONENT_TAGGED_NAME, dc, tag);
}
return dc;
}
/* <name> ::= <nested-name>
::= <unscoped-name>
::= <unscoped-template-name> <template-args>
@ -1223,14 +1246,17 @@ d_name (struct d_info *di)
switch (peek)
{
case 'N':
return d_nested_name (di);
dc = d_nested_name (di);
break;
case 'Z':
return d_local_name (di);
dc = d_local_name (di);
break;
case 'L':
case 'U':
return d_unqualified_name (di);
dc = d_unqualified_name (di);
break;
case 'S':
{
@ -1272,7 +1298,7 @@ d_name (struct d_info *di)
d_template_args (di));
}
return dc;
break;
}
default:
@ -1287,8 +1313,12 @@ d_name (struct d_info *di)
dc = d_make_comp (di, DEMANGLE_COMPONENT_TEMPLATE, dc,
d_template_args (di));
}
return dc;
break;
}
if (d_peek_char (di) == 'B')
dc = d_abi_tags (di, dc);
return dc;
}
/* <nested-name> ::= N [<CV-qualifiers>] <prefix> <unqualified-name> E
@ -3745,6 +3775,7 @@ d_find_pack (struct d_print_info *dpi,
case DEMANGLE_COMPONENT_LAMBDA:
case DEMANGLE_COMPONENT_NAME:
case DEMANGLE_COMPONENT_TAGGED_NAME:
case DEMANGLE_COMPONENT_OPERATOR:
case DEMANGLE_COMPONENT_BUILTIN_TYPE:
case DEMANGLE_COMPONENT_SUB_STD:
@ -3830,6 +3861,13 @@ d_print_comp (struct d_print_info *dpi, int options,
d_print_java_identifier (dpi, dc->u.s_name.s, dc->u.s_name.len);
return;
case DEMANGLE_COMPONENT_TAGGED_NAME:
d_print_comp (dpi, options, d_left (dc));
d_append_string (dpi, "[abi:");
d_print_comp (dpi, options, d_right (dc));
d_append_char (dpi, ']');
return;
case DEMANGLE_COMPONENT_QUAL_NAME:
case DEMANGLE_COMPONENT_LOCAL_NAME:
d_print_comp (dpi, options, d_left (dc));