1434 lines
39 KiB
C
1434 lines
39 KiB
C
|
/* Functions dealing with attribute handling, used by most front ends.
|
|||
|
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
|
|||
|
2002 Free Software Foundation, Inc.
|
|||
|
|
|||
|
This file is part of GCC.
|
|||
|
|
|||
|
GCC is free software; you can redistribute it and/or modify it under
|
|||
|
the terms of the GNU General Public License as published by the Free
|
|||
|
Software Foundation; either version 2, or (at your option) any later
|
|||
|
version.
|
|||
|
|
|||
|
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
|
|||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|||
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|||
|
for more details.
|
|||
|
|
|||
|
You should have received a copy of the GNU General Public License
|
|||
|
along with GCC; see the file COPYING. If not, write to the Free
|
|||
|
Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
|||
|
02111-1307, USA. */
|
|||
|
|
|||
|
#include "config.h"
|
|||
|
#include "system.h"
|
|||
|
#include "tree.h"
|
|||
|
#include "flags.h"
|
|||
|
#include "toplev.h"
|
|||
|
#include "output.h"
|
|||
|
#include "rtl.h"
|
|||
|
#include "ggc.h"
|
|||
|
#include "expr.h"
|
|||
|
#include "tm_p.h"
|
|||
|
#include "obstack.h"
|
|||
|
#include "cpplib.h"
|
|||
|
#include "target.h"
|
|||
|
|
|||
|
static void init_attributes PARAMS ((void));
|
|||
|
|
|||
|
/* Table of the tables of attributes (common, format, language, machine)
|
|||
|
searched. */
|
|||
|
static const struct attribute_spec *attribute_tables[4];
|
|||
|
|
|||
|
static bool attributes_initialized = false;
|
|||
|
|
|||
|
static tree handle_packed_attribute PARAMS ((tree *, tree, tree, int,
|
|||
|
bool *));
|
|||
|
static tree handle_nocommon_attribute PARAMS ((tree *, tree, tree, int,
|
|||
|
bool *));
|
|||
|
static tree handle_common_attribute PARAMS ((tree *, tree, tree, int,
|
|||
|
bool *));
|
|||
|
static tree handle_noreturn_attribute PARAMS ((tree *, tree, tree, int,
|
|||
|
bool *));
|
|||
|
static tree handle_noinline_attribute PARAMS ((tree *, tree, tree, int,
|
|||
|
bool *));
|
|||
|
static tree handle_used_attribute PARAMS ((tree *, tree, tree, int,
|
|||
|
bool *));
|
|||
|
static tree handle_unused_attribute PARAMS ((tree *, tree, tree, int,
|
|||
|
bool *));
|
|||
|
static tree handle_const_attribute PARAMS ((tree *, tree, tree, int,
|
|||
|
bool *));
|
|||
|
static tree handle_transparent_union_attribute PARAMS ((tree *, tree, tree,
|
|||
|
int, bool *));
|
|||
|
static tree handle_constructor_attribute PARAMS ((tree *, tree, tree, int,
|
|||
|
bool *));
|
|||
|
static tree handle_destructor_attribute PARAMS ((tree *, tree, tree, int,
|
|||
|
bool *));
|
|||
|
static tree handle_mode_attribute PARAMS ((tree *, tree, tree, int,
|
|||
|
bool *));
|
|||
|
static tree handle_section_attribute PARAMS ((tree *, tree, tree, int,
|
|||
|
bool *));
|
|||
|
static tree handle_aligned_attribute PARAMS ((tree *, tree, tree, int,
|
|||
|
bool *));
|
|||
|
static tree handle_weak_attribute PARAMS ((tree *, tree, tree, int,
|
|||
|
bool *));
|
|||
|
static tree handle_alias_attribute PARAMS ((tree *, tree, tree, int,
|
|||
|
bool *));
|
|||
|
static tree handle_no_instrument_function_attribute PARAMS ((tree *, tree,
|
|||
|
tree, int,
|
|||
|
bool *));
|
|||
|
static tree handle_malloc_attribute PARAMS ((tree *, tree, tree, int,
|
|||
|
bool *));
|
|||
|
static tree handle_no_limit_stack_attribute PARAMS ((tree *, tree, tree, int,
|
|||
|
bool *));
|
|||
|
static tree handle_pure_attribute PARAMS ((tree *, tree, tree, int,
|
|||
|
bool *));
|
|||
|
static tree handle_deprecated_attribute PARAMS ((tree *, tree, tree, int,
|
|||
|
bool *));
|
|||
|
static tree handle_vector_size_attribute PARAMS ((tree *, tree, tree, int,
|
|||
|
bool *));
|
|||
|
static tree vector_size_helper PARAMS ((tree, tree));
|
|||
|
|
|||
|
/* Table of machine-independent attributes common to all C-like languages. */
|
|||
|
static const struct attribute_spec c_common_attribute_table[] =
|
|||
|
{
|
|||
|
/* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
|
|||
|
{ "packed", 0, 0, false, false, false,
|
|||
|
handle_packed_attribute },
|
|||
|
{ "nocommon", 0, 0, true, false, false,
|
|||
|
handle_nocommon_attribute },
|
|||
|
{ "common", 0, 0, true, false, false,
|
|||
|
handle_common_attribute },
|
|||
|
/* FIXME: logically, noreturn attributes should be listed as
|
|||
|
"false, true, true" and apply to function types. But implementing this
|
|||
|
would require all the places in the compiler that use TREE_THIS_VOLATILE
|
|||
|
on a decl to identify non-returning functions to be located and fixed
|
|||
|
to check the function type instead. */
|
|||
|
{ "noreturn", 0, 0, true, false, false,
|
|||
|
handle_noreturn_attribute },
|
|||
|
{ "volatile", 0, 0, true, false, false,
|
|||
|
handle_noreturn_attribute },
|
|||
|
{ "noinline", 0, 0, true, false, false,
|
|||
|
handle_noinline_attribute },
|
|||
|
{ "used", 0, 0, true, false, false,
|
|||
|
handle_used_attribute },
|
|||
|
{ "unused", 0, 0, false, false, false,
|
|||
|
handle_unused_attribute },
|
|||
|
/* The same comments as for noreturn attributes apply to const ones. */
|
|||
|
{ "const", 0, 0, true, false, false,
|
|||
|
handle_const_attribute },
|
|||
|
{ "transparent_union", 0, 0, false, false, false,
|
|||
|
handle_transparent_union_attribute },
|
|||
|
{ "constructor", 0, 0, true, false, false,
|
|||
|
handle_constructor_attribute },
|
|||
|
{ "destructor", 0, 0, true, false, false,
|
|||
|
handle_destructor_attribute },
|
|||
|
{ "mode", 1, 1, false, true, false,
|
|||
|
handle_mode_attribute },
|
|||
|
{ "section", 1, 1, true, false, false,
|
|||
|
handle_section_attribute },
|
|||
|
{ "aligned", 0, 1, false, false, false,
|
|||
|
handle_aligned_attribute },
|
|||
|
{ "weak", 0, 0, true, false, false,
|
|||
|
handle_weak_attribute },
|
|||
|
{ "alias", 1, 1, true, false, false,
|
|||
|
handle_alias_attribute },
|
|||
|
{ "no_instrument_function", 0, 0, true, false, false,
|
|||
|
handle_no_instrument_function_attribute },
|
|||
|
{ "malloc", 0, 0, true, false, false,
|
|||
|
handle_malloc_attribute },
|
|||
|
{ "no_stack_limit", 0, 0, true, false, false,
|
|||
|
handle_no_limit_stack_attribute },
|
|||
|
{ "pure", 0, 0, true, false, false,
|
|||
|
handle_pure_attribute },
|
|||
|
{ "deprecated", 0, 0, false, false, false,
|
|||
|
handle_deprecated_attribute },
|
|||
|
{ "vector_size", 1, 1, false, true, false,
|
|||
|
handle_vector_size_attribute },
|
|||
|
{ NULL, 0, 0, false, false, false, NULL }
|
|||
|
};
|
|||
|
|
|||
|
/* Default empty table of attributes. */
|
|||
|
static const struct attribute_spec empty_attribute_table[] =
|
|||
|
{
|
|||
|
{ NULL, 0, 0, false, false, false, NULL }
|
|||
|
};
|
|||
|
|
|||
|
/* Table of machine-independent attributes for checking formats, if used. */
|
|||
|
const struct attribute_spec *format_attribute_table = empty_attribute_table;
|
|||
|
|
|||
|
/* Table of machine-independent attributes for a particular language. */
|
|||
|
const struct attribute_spec *lang_attribute_table = empty_attribute_table;
|
|||
|
|
|||
|
/* Flag saying whether common language attributes are to be supported. */
|
|||
|
int lang_attribute_common = 1;
|
|||
|
|
|||
|
/* Initialize attribute tables, and make some sanity checks
|
|||
|
if --enable-checking. */
|
|||
|
|
|||
|
static void
|
|||
|
init_attributes ()
|
|||
|
{
|
|||
|
#ifdef ENABLE_CHECKING
|
|||
|
int i;
|
|||
|
#endif
|
|||
|
|
|||
|
attribute_tables[0]
|
|||
|
= lang_attribute_common ? c_common_attribute_table : empty_attribute_table;
|
|||
|
attribute_tables[1] = lang_attribute_table;
|
|||
|
attribute_tables[2] = format_attribute_table;
|
|||
|
attribute_tables[3] = targetm.attribute_table;
|
|||
|
|
|||
|
#ifdef ENABLE_CHECKING
|
|||
|
/* Make some sanity checks on the attribute tables. */
|
|||
|
for (i = 0;
|
|||
|
i < (int) (sizeof (attribute_tables) / sizeof (attribute_tables[0]));
|
|||
|
i++)
|
|||
|
{
|
|||
|
int j;
|
|||
|
|
|||
|
for (j = 0; attribute_tables[i][j].name != NULL; j++)
|
|||
|
{
|
|||
|
/* The name must not begin and end with __. */
|
|||
|
const char *name = attribute_tables[i][j].name;
|
|||
|
int len = strlen (name);
|
|||
|
if (name[0] == '_' && name[1] == '_'
|
|||
|
&& name[len - 1] == '_' && name[len - 2] == '_')
|
|||
|
abort ();
|
|||
|
/* The minimum and maximum lengths must be consistent. */
|
|||
|
if (attribute_tables[i][j].min_length < 0)
|
|||
|
abort ();
|
|||
|
if (attribute_tables[i][j].max_length != -1
|
|||
|
&& (attribute_tables[i][j].max_length
|
|||
|
< attribute_tables[i][j].min_length))
|
|||
|
abort ();
|
|||
|
/* An attribute cannot require both a DECL and a TYPE. */
|
|||
|
if (attribute_tables[i][j].decl_required
|
|||
|
&& attribute_tables[i][j].type_required)
|
|||
|
abort ();
|
|||
|
/* If an attribute requires a function type, in particular
|
|||
|
it requires a type. */
|
|||
|
if (attribute_tables[i][j].function_type_required
|
|||
|
&& !attribute_tables[i][j].type_required)
|
|||
|
abort ();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Check that each name occurs just once in each table. */
|
|||
|
for (i = 0;
|
|||
|
i < (int) (sizeof (attribute_tables) / sizeof (attribute_tables[0]));
|
|||
|
i++)
|
|||
|
{
|
|||
|
int j, k;
|
|||
|
for (j = 0; attribute_tables[i][j].name != NULL; j++)
|
|||
|
for (k = j + 1; attribute_tables[i][k].name != NULL; k++)
|
|||
|
if (!strcmp (attribute_tables[i][j].name,
|
|||
|
attribute_tables[i][k].name))
|
|||
|
abort ();
|
|||
|
}
|
|||
|
/* Check that no name occurs in more than one table. */
|
|||
|
for (i = 0;
|
|||
|
i < (int) (sizeof (attribute_tables) / sizeof (attribute_tables[0]));
|
|||
|
i++)
|
|||
|
{
|
|||
|
int j, k, l;
|
|||
|
|
|||
|
for (j = i + 1;
|
|||
|
j < ((int) (sizeof (attribute_tables)
|
|||
|
/ sizeof (attribute_tables[0])));
|
|||
|
j++)
|
|||
|
for (k = 0; attribute_tables[i][k].name != NULL; k++)
|
|||
|
for (l = 0; attribute_tables[j][l].name != NULL; l++)
|
|||
|
if (!strcmp (attribute_tables[i][k].name,
|
|||
|
attribute_tables[j][l].name))
|
|||
|
abort ();
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
attributes_initialized = true;
|
|||
|
}
|
|||
|
|
|||
|
/* Process the attributes listed in ATTRIBUTES and install them in *NODE,
|
|||
|
which is either a DECL (including a TYPE_DECL) or a TYPE. If a DECL,
|
|||
|
it should be modified in place; if a TYPE, a copy should be created
|
|||
|
unless ATTR_FLAG_TYPE_IN_PLACE is set in FLAGS. FLAGS gives further
|
|||
|
information, in the form of a bitwise OR of flags in enum attribute_flags
|
|||
|
from tree.h. Depending on these flags, some attributes may be
|
|||
|
returned to be applied at a later stage (for example, to apply
|
|||
|
a decl attribute to the declaration rather than to its type). If
|
|||
|
ATTR_FLAG_BUILT_IN is not set and *NODE is a DECL, then also consider
|
|||
|
whether there might be some default attributes to apply to this DECL;
|
|||
|
if so, decl_attributes will be called recursively with those attributes
|
|||
|
and ATTR_FLAG_BUILT_IN set. */
|
|||
|
|
|||
|
tree
|
|||
|
decl_attributes (node, attributes, flags)
|
|||
|
tree *node, attributes;
|
|||
|
int flags;
|
|||
|
{
|
|||
|
tree a;
|
|||
|
tree returned_attrs = NULL_TREE;
|
|||
|
|
|||
|
if (!attributes_initialized)
|
|||
|
init_attributes ();
|
|||
|
|
|||
|
(*targetm.insert_attributes) (*node, &attributes);
|
|||
|
|
|||
|
if (DECL_P (*node) && TREE_CODE (*node) == FUNCTION_DECL
|
|||
|
&& !(flags & (int) ATTR_FLAG_BUILT_IN))
|
|||
|
insert_default_attributes (*node);
|
|||
|
|
|||
|
for (a = attributes; a; a = TREE_CHAIN (a))
|
|||
|
{
|
|||
|
tree name = TREE_PURPOSE (a);
|
|||
|
tree args = TREE_VALUE (a);
|
|||
|
tree *anode = node;
|
|||
|
const struct attribute_spec *spec = NULL;
|
|||
|
bool no_add_attrs = 0;
|
|||
|
int i;
|
|||
|
|
|||
|
for (i = 0;
|
|||
|
i < ((int) (sizeof (attribute_tables)
|
|||
|
/ sizeof (attribute_tables[0])));
|
|||
|
i++)
|
|||
|
{
|
|||
|
int j;
|
|||
|
|
|||
|
for (j = 0; attribute_tables[i][j].name != NULL; j++)
|
|||
|
{
|
|||
|
if (is_attribute_p (attribute_tables[i][j].name, name))
|
|||
|
{
|
|||
|
spec = &attribute_tables[i][j];
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
if (spec != NULL)
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
if (spec == NULL)
|
|||
|
{
|
|||
|
warning ("`%s' attribute directive ignored",
|
|||
|
IDENTIFIER_POINTER (name));
|
|||
|
continue;
|
|||
|
}
|
|||
|
else if (list_length (args) < spec->min_length
|
|||
|
|| (spec->max_length >= 0
|
|||
|
&& list_length (args) > spec->max_length))
|
|||
|
{
|
|||
|
error ("wrong number of arguments specified for `%s' attribute",
|
|||
|
IDENTIFIER_POINTER (name));
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
if (spec->decl_required && !DECL_P (*anode))
|
|||
|
{
|
|||
|
if (flags & ((int) ATTR_FLAG_DECL_NEXT
|
|||
|
| (int) ATTR_FLAG_FUNCTION_NEXT
|
|||
|
| (int) ATTR_FLAG_ARRAY_NEXT))
|
|||
|
{
|
|||
|
/* Pass on this attribute to be tried again. */
|
|||
|
returned_attrs = tree_cons (name, args, returned_attrs);
|
|||
|
continue;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
warning ("`%s' attribute does not apply to types",
|
|||
|
IDENTIFIER_POINTER (name));
|
|||
|
continue;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* If we require a type, but were passed a decl, set up to make a
|
|||
|
new type and update the one in the decl. ATTR_FLAG_TYPE_IN_PLACE
|
|||
|
would have applied if we'd been passed a type, but we cannot modify
|
|||
|
the decl's type in place here. */
|
|||
|
if (spec->type_required && DECL_P (*anode))
|
|||
|
{
|
|||
|
anode = &TREE_TYPE (*anode);
|
|||
|
flags &= ~(int) ATTR_FLAG_TYPE_IN_PLACE;
|
|||
|
}
|
|||
|
|
|||
|
if (spec->function_type_required && TREE_CODE (*anode) != FUNCTION_TYPE
|
|||
|
&& TREE_CODE (*anode) != METHOD_TYPE)
|
|||
|
{
|
|||
|
if (TREE_CODE (*anode) == POINTER_TYPE
|
|||
|
&& (TREE_CODE (TREE_TYPE (*anode)) == FUNCTION_TYPE
|
|||
|
|| TREE_CODE (TREE_TYPE (*anode)) == METHOD_TYPE))
|
|||
|
{
|
|||
|
if (!(flags & (int) ATTR_FLAG_TYPE_IN_PLACE))
|
|||
|
*anode = build_type_copy (*anode);
|
|||
|
anode = &TREE_TYPE (*anode);
|
|||
|
}
|
|||
|
else if (flags & (int) ATTR_FLAG_FUNCTION_NEXT)
|
|||
|
{
|
|||
|
/* Pass on this attribute to be tried again. */
|
|||
|
returned_attrs = tree_cons (name, args, returned_attrs);
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
if (TREE_CODE (*anode) != FUNCTION_TYPE
|
|||
|
&& TREE_CODE (*anode) != METHOD_TYPE)
|
|||
|
{
|
|||
|
warning ("`%s' attribute only applies to function types",
|
|||
|
IDENTIFIER_POINTER (name));
|
|||
|
continue;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (spec->handler != NULL)
|
|||
|
returned_attrs = chainon ((*spec->handler) (anode, name, args,
|
|||
|
flags, &no_add_attrs),
|
|||
|
returned_attrs);
|
|||
|
|
|||
|
/* Layout the decl in case anything changed. */
|
|||
|
if (spec->type_required && DECL_P (*node)
|
|||
|
&& TREE_CODE (*node) == VAR_DECL)
|
|||
|
{
|
|||
|
/* Force a recalculation of mode and size. */
|
|||
|
DECL_MODE (*node) = VOIDmode;
|
|||
|
DECL_SIZE (*node) = 0;
|
|||
|
|
|||
|
layout_decl (*node, 0);
|
|||
|
}
|
|||
|
|
|||
|
if (!no_add_attrs)
|
|||
|
{
|
|||
|
tree old_attrs;
|
|||
|
tree a;
|
|||
|
|
|||
|
if (DECL_P (*anode))
|
|||
|
old_attrs = DECL_ATTRIBUTES (*anode);
|
|||
|
else
|
|||
|
old_attrs = TYPE_ATTRIBUTES (*anode);
|
|||
|
|
|||
|
for (a = lookup_attribute (spec->name, old_attrs);
|
|||
|
a != NULL_TREE;
|
|||
|
a = lookup_attribute (spec->name, TREE_CHAIN (a)))
|
|||
|
{
|
|||
|
if (simple_cst_equal (TREE_VALUE (a), args) == 1)
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
if (a == NULL_TREE)
|
|||
|
{
|
|||
|
/* This attribute isn't already in the list. */
|
|||
|
if (DECL_P (*anode))
|
|||
|
DECL_ATTRIBUTES (*anode) = tree_cons (name, args, old_attrs);
|
|||
|
else if (flags & (int) ATTR_FLAG_TYPE_IN_PLACE)
|
|||
|
TYPE_ATTRIBUTES (*anode) = tree_cons (name, args, old_attrs);
|
|||
|
else
|
|||
|
*anode = build_type_attribute_variant (*anode,
|
|||
|
tree_cons (name, args,
|
|||
|
old_attrs));
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return returned_attrs;
|
|||
|
}
|
|||
|
|
|||
|
/* Handle a "packed" attribute; arguments as in
|
|||
|
struct attribute_spec.handler. */
|
|||
|
|
|||
|
static tree
|
|||
|
handle_packed_attribute (node, name, args, flags, no_add_attrs)
|
|||
|
tree *node;
|
|||
|
tree name;
|
|||
|
tree args ATTRIBUTE_UNUSED;
|
|||
|
int flags;
|
|||
|
bool *no_add_attrs;
|
|||
|
{
|
|||
|
tree *type = NULL;
|
|||
|
if (DECL_P (*node))
|
|||
|
{
|
|||
|
if (TREE_CODE (*node) == TYPE_DECL)
|
|||
|
type = &TREE_TYPE (*node);
|
|||
|
}
|
|||
|
else
|
|||
|
type = node;
|
|||
|
|
|||
|
if (type)
|
|||
|
{
|
|||
|
if (!(flags & (int) ATTR_FLAG_TYPE_IN_PLACE))
|
|||
|
*type = build_type_copy (*type);
|
|||
|
TYPE_PACKED (*type) = 1;
|
|||
|
}
|
|||
|
else if (TREE_CODE (*node) == FIELD_DECL)
|
|||
|
DECL_PACKED (*node) = 1;
|
|||
|
/* We can't set DECL_PACKED for a VAR_DECL, because the bit is
|
|||
|
used for DECL_REGISTER. It wouldn't mean anything anyway. */
|
|||
|
else
|
|||
|
{
|
|||
|
warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
|
|||
|
*no_add_attrs = true;
|
|||
|
}
|
|||
|
|
|||
|
return NULL_TREE;
|
|||
|
}
|
|||
|
|
|||
|
/* Handle a "nocommon" attribute; arguments as in
|
|||
|
struct attribute_spec.handler. */
|
|||
|
|
|||
|
static tree
|
|||
|
handle_nocommon_attribute (node, name, args, flags, no_add_attrs)
|
|||
|
tree *node;
|
|||
|
tree name;
|
|||
|
tree args ATTRIBUTE_UNUSED;
|
|||
|
int flags ATTRIBUTE_UNUSED;
|
|||
|
bool *no_add_attrs;
|
|||
|
{
|
|||
|
if (TREE_CODE (*node) == VAR_DECL)
|
|||
|
DECL_COMMON (*node) = 0;
|
|||
|
else
|
|||
|
{
|
|||
|
warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
|
|||
|
*no_add_attrs = true;
|
|||
|
}
|
|||
|
|
|||
|
return NULL_TREE;
|
|||
|
}
|
|||
|
|
|||
|
/* Handle a "common" attribute; arguments as in
|
|||
|
struct attribute_spec.handler. */
|
|||
|
|
|||
|
static tree
|
|||
|
handle_common_attribute (node, name, args, flags, no_add_attrs)
|
|||
|
tree *node;
|
|||
|
tree name;
|
|||
|
tree args ATTRIBUTE_UNUSED;
|
|||
|
int flags ATTRIBUTE_UNUSED;
|
|||
|
bool *no_add_attrs;
|
|||
|
{
|
|||
|
if (TREE_CODE (*node) == VAR_DECL)
|
|||
|
DECL_COMMON (*node) = 1;
|
|||
|
else
|
|||
|
{
|
|||
|
warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
|
|||
|
*no_add_attrs = true;
|
|||
|
}
|
|||
|
|
|||
|
return NULL_TREE;
|
|||
|
}
|
|||
|
|
|||
|
/* Handle a "noreturn" attribute; arguments as in
|
|||
|
struct attribute_spec.handler. */
|
|||
|
|
|||
|
static tree
|
|||
|
handle_noreturn_attribute (node, name, args, flags, no_add_attrs)
|
|||
|
tree *node;
|
|||
|
tree name;
|
|||
|
tree args ATTRIBUTE_UNUSED;
|
|||
|
int flags ATTRIBUTE_UNUSED;
|
|||
|
bool *no_add_attrs;
|
|||
|
{
|
|||
|
tree type = TREE_TYPE (*node);
|
|||
|
|
|||
|
/* See FIXME comment in c_common_attribute_table. */
|
|||
|
if (TREE_CODE (*node) == FUNCTION_DECL)
|
|||
|
TREE_THIS_VOLATILE (*node) = 1;
|
|||
|
else if (TREE_CODE (type) == POINTER_TYPE
|
|||
|
&& TREE_CODE (TREE_TYPE (type)) == FUNCTION_TYPE)
|
|||
|
TREE_TYPE (*node)
|
|||
|
= build_pointer_type
|
|||
|
(build_type_variant (TREE_TYPE (type),
|
|||
|
TREE_READONLY (TREE_TYPE (type)), 1));
|
|||
|
else
|
|||
|
{
|
|||
|
warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
|
|||
|
*no_add_attrs = true;
|
|||
|
}
|
|||
|
|
|||
|
return NULL_TREE;
|
|||
|
}
|
|||
|
|
|||
|
/* Handle a "noinline" attribute; arguments as in
|
|||
|
struct attribute_spec.handler. */
|
|||
|
|
|||
|
static tree
|
|||
|
handle_noinline_attribute (node, name, args, flags, no_add_attrs)
|
|||
|
tree *node;
|
|||
|
tree name;
|
|||
|
tree args ATTRIBUTE_UNUSED;
|
|||
|
int flags ATTRIBUTE_UNUSED;
|
|||
|
bool *no_add_attrs;
|
|||
|
{
|
|||
|
if (TREE_CODE (*node) == FUNCTION_DECL)
|
|||
|
DECL_UNINLINABLE (*node) = 1;
|
|||
|
else
|
|||
|
{
|
|||
|
warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
|
|||
|
*no_add_attrs = true;
|
|||
|
}
|
|||
|
|
|||
|
return NULL_TREE;
|
|||
|
}
|
|||
|
|
|||
|
/* Handle a "used" attribute; arguments as in
|
|||
|
struct attribute_spec.handler. */
|
|||
|
|
|||
|
static tree
|
|||
|
handle_used_attribute (node, name, args, flags, no_add_attrs)
|
|||
|
tree *node;
|
|||
|
tree name;
|
|||
|
tree args ATTRIBUTE_UNUSED;
|
|||
|
int flags ATTRIBUTE_UNUSED;
|
|||
|
bool *no_add_attrs;
|
|||
|
{
|
|||
|
if (TREE_CODE (*node) == FUNCTION_DECL)
|
|||
|
TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (*node))
|
|||
|
= TREE_USED (*node) = 1;
|
|||
|
else
|
|||
|
{
|
|||
|
warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
|
|||
|
*no_add_attrs = true;
|
|||
|
}
|
|||
|
|
|||
|
return NULL_TREE;
|
|||
|
}
|
|||
|
|
|||
|
/* Handle a "unused" attribute; arguments as in
|
|||
|
struct attribute_spec.handler. */
|
|||
|
|
|||
|
static tree
|
|||
|
handle_unused_attribute (node, name, args, flags, no_add_attrs)
|
|||
|
tree *node;
|
|||
|
tree name;
|
|||
|
tree args ATTRIBUTE_UNUSED;
|
|||
|
int flags;
|
|||
|
bool *no_add_attrs;
|
|||
|
{
|
|||
|
if (DECL_P (*node))
|
|||
|
{
|
|||
|
tree decl = *node;
|
|||
|
|
|||
|
if (TREE_CODE (decl) == PARM_DECL
|
|||
|
|| TREE_CODE (decl) == VAR_DECL
|
|||
|
|| TREE_CODE (decl) == FUNCTION_DECL
|
|||
|
|| TREE_CODE (decl) == LABEL_DECL
|
|||
|
|| TREE_CODE (decl) == TYPE_DECL)
|
|||
|
TREE_USED (decl) = 1;
|
|||
|
else
|
|||
|
{
|
|||
|
warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
|
|||
|
*no_add_attrs = true;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
if (!(flags & (int) ATTR_FLAG_TYPE_IN_PLACE))
|
|||
|
*node = build_type_copy (*node);
|
|||
|
TREE_USED (*node) = 1;
|
|||
|
}
|
|||
|
|
|||
|
return NULL_TREE;
|
|||
|
}
|
|||
|
|
|||
|
/* Handle a "const" attribute; arguments as in
|
|||
|
struct attribute_spec.handler. */
|
|||
|
|
|||
|
static tree
|
|||
|
handle_const_attribute (node, name, args, flags, no_add_attrs)
|
|||
|
tree *node;
|
|||
|
tree name;
|
|||
|
tree args ATTRIBUTE_UNUSED;
|
|||
|
int flags ATTRIBUTE_UNUSED;
|
|||
|
bool *no_add_attrs;
|
|||
|
{
|
|||
|
tree type = TREE_TYPE (*node);
|
|||
|
|
|||
|
/* See FIXME comment on noreturn in c_common_attribute_table. */
|
|||
|
if (TREE_CODE (*node) == FUNCTION_DECL)
|
|||
|
TREE_READONLY (*node) = 1;
|
|||
|
else if (TREE_CODE (type) == POINTER_TYPE
|
|||
|
&& TREE_CODE (TREE_TYPE (type)) == FUNCTION_TYPE)
|
|||
|
TREE_TYPE (*node)
|
|||
|
= build_pointer_type
|
|||
|
(build_type_variant (TREE_TYPE (type), 1,
|
|||
|
TREE_THIS_VOLATILE (TREE_TYPE (type))));
|
|||
|
else
|
|||
|
{
|
|||
|
warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
|
|||
|
*no_add_attrs = true;
|
|||
|
}
|
|||
|
|
|||
|
return NULL_TREE;
|
|||
|
}
|
|||
|
|
|||
|
/* Handle a "transparent_union" attribute; arguments as in
|
|||
|
struct attribute_spec.handler. */
|
|||
|
|
|||
|
static tree
|
|||
|
handle_transparent_union_attribute (node, name, args, flags, no_add_attrs)
|
|||
|
tree *node;
|
|||
|
tree name;
|
|||
|
tree args ATTRIBUTE_UNUSED;
|
|||
|
int flags;
|
|||
|
bool *no_add_attrs;
|
|||
|
{
|
|||
|
tree decl = NULL_TREE;
|
|||
|
tree *type = NULL;
|
|||
|
int is_type = 0;
|
|||
|
|
|||
|
if (DECL_P (*node))
|
|||
|
{
|
|||
|
decl = *node;
|
|||
|
type = &TREE_TYPE (decl);
|
|||
|
is_type = TREE_CODE (*node) == TYPE_DECL;
|
|||
|
}
|
|||
|
else if (TYPE_P (*node))
|
|||
|
type = node, is_type = 1;
|
|||
|
|
|||
|
if (is_type
|
|||
|
&& TREE_CODE (*type) == UNION_TYPE
|
|||
|
&& (decl == 0
|
|||
|
|| (TYPE_FIELDS (*type) != 0
|
|||
|
&& TYPE_MODE (*type) == DECL_MODE (TYPE_FIELDS (*type)))))
|
|||
|
{
|
|||
|
if (!(flags & (int) ATTR_FLAG_TYPE_IN_PLACE))
|
|||
|
*type = build_type_copy (*type);
|
|||
|
TYPE_TRANSPARENT_UNION (*type) = 1;
|
|||
|
}
|
|||
|
else if (decl != 0 && TREE_CODE (decl) == PARM_DECL
|
|||
|
&& TREE_CODE (*type) == UNION_TYPE
|
|||
|
&& TYPE_MODE (*type) == DECL_MODE (TYPE_FIELDS (*type)))
|
|||
|
DECL_TRANSPARENT_UNION (decl) = 1;
|
|||
|
else
|
|||
|
{
|
|||
|
warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
|
|||
|
*no_add_attrs = true;
|
|||
|
}
|
|||
|
|
|||
|
return NULL_TREE;
|
|||
|
}
|
|||
|
|
|||
|
/* Handle a "constructor" attribute; arguments as in
|
|||
|
struct attribute_spec.handler. */
|
|||
|
|
|||
|
static tree
|
|||
|
handle_constructor_attribute (node, name, args, flags, no_add_attrs)
|
|||
|
tree *node;
|
|||
|
tree name;
|
|||
|
tree args ATTRIBUTE_UNUSED;
|
|||
|
int flags ATTRIBUTE_UNUSED;
|
|||
|
bool *no_add_attrs;
|
|||
|
{
|
|||
|
tree decl = *node;
|
|||
|
tree type = TREE_TYPE (decl);
|
|||
|
|
|||
|
if (TREE_CODE (decl) == FUNCTION_DECL
|
|||
|
&& TREE_CODE (type) == FUNCTION_TYPE
|
|||
|
&& decl_function_context (decl) == 0)
|
|||
|
{
|
|||
|
DECL_STATIC_CONSTRUCTOR (decl) = 1;
|
|||
|
TREE_USED (decl) = 1;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
|
|||
|
*no_add_attrs = true;
|
|||
|
}
|
|||
|
|
|||
|
return NULL_TREE;
|
|||
|
}
|
|||
|
|
|||
|
/* Handle a "destructor" attribute; arguments as in
|
|||
|
struct attribute_spec.handler. */
|
|||
|
|
|||
|
static tree
|
|||
|
handle_destructor_attribute (node, name, args, flags, no_add_attrs)
|
|||
|
tree *node;
|
|||
|
tree name;
|
|||
|
tree args ATTRIBUTE_UNUSED;
|
|||
|
int flags ATTRIBUTE_UNUSED;
|
|||
|
bool *no_add_attrs;
|
|||
|
{
|
|||
|
tree decl = *node;
|
|||
|
tree type = TREE_TYPE (decl);
|
|||
|
|
|||
|
if (TREE_CODE (decl) == FUNCTION_DECL
|
|||
|
&& TREE_CODE (type) == FUNCTION_TYPE
|
|||
|
&& decl_function_context (decl) == 0)
|
|||
|
{
|
|||
|
DECL_STATIC_DESTRUCTOR (decl) = 1;
|
|||
|
TREE_USED (decl) = 1;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
|
|||
|
*no_add_attrs = true;
|
|||
|
}
|
|||
|
|
|||
|
return NULL_TREE;
|
|||
|
}
|
|||
|
|
|||
|
/* Handle a "mode" attribute; arguments as in
|
|||
|
struct attribute_spec.handler. */
|
|||
|
|
|||
|
static tree
|
|||
|
handle_mode_attribute (node, name, args, flags, no_add_attrs)
|
|||
|
tree *node;
|
|||
|
tree name;
|
|||
|
tree args;
|
|||
|
int flags ATTRIBUTE_UNUSED;
|
|||
|
bool *no_add_attrs;
|
|||
|
{
|
|||
|
tree type = *node;
|
|||
|
|
|||
|
*no_add_attrs = true;
|
|||
|
|
|||
|
if (TREE_CODE (TREE_VALUE (args)) != IDENTIFIER_NODE)
|
|||
|
warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
|
|||
|
else
|
|||
|
{
|
|||
|
int j;
|
|||
|
const char *p = IDENTIFIER_POINTER (TREE_VALUE (args));
|
|||
|
int len = strlen (p);
|
|||
|
enum machine_mode mode = VOIDmode;
|
|||
|
tree typefm;
|
|||
|
|
|||
|
if (len > 4 && p[0] == '_' && p[1] == '_'
|
|||
|
&& p[len - 1] == '_' && p[len - 2] == '_')
|
|||
|
{
|
|||
|
char *newp = (char *) alloca (len - 1);
|
|||
|
|
|||
|
strcpy (newp, &p[2]);
|
|||
|
newp[len - 4] = '\0';
|
|||
|
p = newp;
|
|||
|
}
|
|||
|
|
|||
|
/* Change this type to have a type with the specified mode.
|
|||
|
First check for the special modes. */
|
|||
|
if (! strcmp (p, "byte"))
|
|||
|
mode = byte_mode;
|
|||
|
else if (!strcmp (p, "word"))
|
|||
|
mode = word_mode;
|
|||
|
else if (! strcmp (p, "pointer"))
|
|||
|
mode = ptr_mode;
|
|||
|
else
|
|||
|
for (j = 0; j < NUM_MACHINE_MODES; j++)
|
|||
|
if (!strcmp (p, GET_MODE_NAME (j)))
|
|||
|
mode = (enum machine_mode) j;
|
|||
|
|
|||
|
if (mode == VOIDmode)
|
|||
|
error ("unknown machine mode `%s'", p);
|
|||
|
else if (0 == (typefm = type_for_mode (mode,
|
|||
|
TREE_UNSIGNED (type))))
|
|||
|
error ("no data type for mode `%s'", p);
|
|||
|
else
|
|||
|
*node = typefm;
|
|||
|
/* No need to layout the type here. The caller should do this. */
|
|||
|
}
|
|||
|
|
|||
|
return NULL_TREE;
|
|||
|
}
|
|||
|
|
|||
|
/* Handle a "section" attribute; arguments as in
|
|||
|
struct attribute_spec.handler. */
|
|||
|
|
|||
|
static tree
|
|||
|
handle_section_attribute (node, name, args, flags, no_add_attrs)
|
|||
|
tree *node;
|
|||
|
tree name ATTRIBUTE_UNUSED;
|
|||
|
tree args;
|
|||
|
int flags ATTRIBUTE_UNUSED;
|
|||
|
bool *no_add_attrs;
|
|||
|
{
|
|||
|
tree decl = *node;
|
|||
|
|
|||
|
if (targetm.have_named_sections)
|
|||
|
{
|
|||
|
if ((TREE_CODE (decl) == FUNCTION_DECL
|
|||
|
|| TREE_CODE (decl) == VAR_DECL)
|
|||
|
&& TREE_CODE (TREE_VALUE (args)) == STRING_CST)
|
|||
|
{
|
|||
|
if (TREE_CODE (decl) == VAR_DECL
|
|||
|
&& current_function_decl != NULL_TREE
|
|||
|
&& ! TREE_STATIC (decl))
|
|||
|
{
|
|||
|
error_with_decl (decl,
|
|||
|
"section attribute cannot be specified for local variables");
|
|||
|
*no_add_attrs = true;
|
|||
|
}
|
|||
|
|
|||
|
/* The decl may have already been given a section attribute
|
|||
|
from a previous declaration. Ensure they match. */
|
|||
|
else if (DECL_SECTION_NAME (decl) != NULL_TREE
|
|||
|
&& strcmp (TREE_STRING_POINTER (DECL_SECTION_NAME (decl)),
|
|||
|
TREE_STRING_POINTER (TREE_VALUE (args))) != 0)
|
|||
|
{
|
|||
|
error_with_decl (*node,
|
|||
|
"section of `%s' conflicts with previous declaration");
|
|||
|
*no_add_attrs = true;
|
|||
|
}
|
|||
|
else
|
|||
|
DECL_SECTION_NAME (decl) = TREE_VALUE (args);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
error_with_decl (*node,
|
|||
|
"section attribute not allowed for `%s'");
|
|||
|
*no_add_attrs = true;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
error_with_decl (*node,
|
|||
|
"section attributes are not supported for this target");
|
|||
|
*no_add_attrs = true;
|
|||
|
}
|
|||
|
|
|||
|
return NULL_TREE;
|
|||
|
}
|
|||
|
|
|||
|
/* Handle a "aligned" attribute; arguments as in
|
|||
|
struct attribute_spec.handler. */
|
|||
|
|
|||
|
static tree
|
|||
|
handle_aligned_attribute (node, name, args, flags, no_add_attrs)
|
|||
|
tree *node;
|
|||
|
tree name ATTRIBUTE_UNUSED;
|
|||
|
tree args;
|
|||
|
int flags;
|
|||
|
bool *no_add_attrs;
|
|||
|
{
|
|||
|
tree decl = NULL_TREE;
|
|||
|
tree *type = NULL;
|
|||
|
int is_type = 0;
|
|||
|
tree align_expr = (args ? TREE_VALUE (args)
|
|||
|
: size_int (BIGGEST_ALIGNMENT / BITS_PER_UNIT));
|
|||
|
int i;
|
|||
|
|
|||
|
if (DECL_P (*node))
|
|||
|
{
|
|||
|
decl = *node;
|
|||
|
type = &TREE_TYPE (decl);
|
|||
|
is_type = TREE_CODE (*node) == TYPE_DECL;
|
|||
|
}
|
|||
|
else if (TYPE_P (*node))
|
|||
|
type = node, is_type = 1;
|
|||
|
|
|||
|
/* Strip any NOPs of any kind. */
|
|||
|
while (TREE_CODE (align_expr) == NOP_EXPR
|
|||
|
|| TREE_CODE (align_expr) == CONVERT_EXPR
|
|||
|
|| TREE_CODE (align_expr) == NON_LVALUE_EXPR)
|
|||
|
align_expr = TREE_OPERAND (align_expr, 0);
|
|||
|
|
|||
|
if (TREE_CODE (align_expr) != INTEGER_CST)
|
|||
|
{
|
|||
|
error ("requested alignment is not a constant");
|
|||
|
*no_add_attrs = true;
|
|||
|
}
|
|||
|
else if ((i = tree_log2 (align_expr)) == -1)
|
|||
|
{
|
|||
|
error ("requested alignment is not a power of 2");
|
|||
|
*no_add_attrs = true;
|
|||
|
}
|
|||
|
else if (i > HOST_BITS_PER_INT - 2)
|
|||
|
{
|
|||
|
error ("requested alignment is too large");
|
|||
|
*no_add_attrs = true;
|
|||
|
}
|
|||
|
else if (is_type)
|
|||
|
{
|
|||
|
/* If we have a TYPE_DECL, then copy the type, so that we
|
|||
|
don't accidentally modify a builtin type. See pushdecl. */
|
|||
|
if (decl && TREE_TYPE (decl) != error_mark_node
|
|||
|
&& DECL_ORIGINAL_TYPE (decl) == NULL_TREE)
|
|||
|
{
|
|||
|
tree tt = TREE_TYPE (decl);
|
|||
|
*type = build_type_copy (*type);
|
|||
|
DECL_ORIGINAL_TYPE (decl) = tt;
|
|||
|
TYPE_NAME (*type) = decl;
|
|||
|
TREE_USED (*type) = TREE_USED (decl);
|
|||
|
TREE_TYPE (decl) = *type;
|
|||
|
}
|
|||
|
else if (!(flags & (int) ATTR_FLAG_TYPE_IN_PLACE))
|
|||
|
*type = build_type_copy (*type);
|
|||
|
|
|||
|
TYPE_ALIGN (*type) = (1 << i) * BITS_PER_UNIT;
|
|||
|
TYPE_USER_ALIGN (*type) = 1;
|
|||
|
}
|
|||
|
else if (TREE_CODE (decl) != VAR_DECL
|
|||
|
&& TREE_CODE (decl) != FIELD_DECL)
|
|||
|
{
|
|||
|
error_with_decl (decl,
|
|||
|
"alignment may not be specified for `%s'");
|
|||
|
*no_add_attrs = true;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
DECL_ALIGN (decl) = (1 << i) * BITS_PER_UNIT;
|
|||
|
DECL_USER_ALIGN (decl) = 1;
|
|||
|
}
|
|||
|
|
|||
|
return NULL_TREE;
|
|||
|
}
|
|||
|
|
|||
|
/* Handle a "weak" attribute; arguments as in
|
|||
|
struct attribute_spec.handler. */
|
|||
|
|
|||
|
static tree
|
|||
|
handle_weak_attribute (node, name, args, flags, no_add_attrs)
|
|||
|
tree *node;
|
|||
|
tree name ATTRIBUTE_UNUSED;
|
|||
|
tree args ATTRIBUTE_UNUSED;
|
|||
|
int flags ATTRIBUTE_UNUSED;
|
|||
|
bool *no_add_attrs ATTRIBUTE_UNUSED;
|
|||
|
{
|
|||
|
declare_weak (*node);
|
|||
|
|
|||
|
return NULL_TREE;
|
|||
|
}
|
|||
|
|
|||
|
/* Handle an "alias" attribute; arguments as in
|
|||
|
struct attribute_spec.handler. */
|
|||
|
|
|||
|
static tree
|
|||
|
handle_alias_attribute (node, name, args, flags, no_add_attrs)
|
|||
|
tree *node;
|
|||
|
tree name;
|
|||
|
tree args;
|
|||
|
int flags ATTRIBUTE_UNUSED;
|
|||
|
bool *no_add_attrs;
|
|||
|
{
|
|||
|
tree decl = *node;
|
|||
|
|
|||
|
if ((TREE_CODE (decl) == FUNCTION_DECL && DECL_INITIAL (decl))
|
|||
|
|| (TREE_CODE (decl) != FUNCTION_DECL && ! DECL_EXTERNAL (decl)))
|
|||
|
{
|
|||
|
error_with_decl (decl,
|
|||
|
"`%s' defined both normally and as an alias");
|
|||
|
*no_add_attrs = true;
|
|||
|
}
|
|||
|
else if (decl_function_context (decl) == 0)
|
|||
|
{
|
|||
|
tree id;
|
|||
|
|
|||
|
id = TREE_VALUE (args);
|
|||
|
if (TREE_CODE (id) != STRING_CST)
|
|||
|
{
|
|||
|
error ("alias arg not a string");
|
|||
|
*no_add_attrs = true;
|
|||
|
return NULL_TREE;
|
|||
|
}
|
|||
|
id = get_identifier (TREE_STRING_POINTER (id));
|
|||
|
/* This counts as a use of the object pointed to. */
|
|||
|
TREE_USED (id) = 1;
|
|||
|
|
|||
|
if (TREE_CODE (decl) == FUNCTION_DECL)
|
|||
|
DECL_INITIAL (decl) = error_mark_node;
|
|||
|
else
|
|||
|
DECL_EXTERNAL (decl) = 0;
|
|||
|
assemble_alias (decl, id);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
|
|||
|
*no_add_attrs = true;
|
|||
|
}
|
|||
|
|
|||
|
return NULL_TREE;
|
|||
|
}
|
|||
|
|
|||
|
/* Handle a "no_instrument_function" attribute; arguments as in
|
|||
|
struct attribute_spec.handler. */
|
|||
|
|
|||
|
static tree
|
|||
|
handle_no_instrument_function_attribute (node, name, args, flags, no_add_attrs)
|
|||
|
tree *node;
|
|||
|
tree name;
|
|||
|
tree args ATTRIBUTE_UNUSED;
|
|||
|
int flags ATTRIBUTE_UNUSED;
|
|||
|
bool *no_add_attrs;
|
|||
|
{
|
|||
|
tree decl = *node;
|
|||
|
|
|||
|
if (TREE_CODE (decl) != FUNCTION_DECL)
|
|||
|
{
|
|||
|
error_with_decl (decl,
|
|||
|
"`%s' attribute applies only to functions",
|
|||
|
IDENTIFIER_POINTER (name));
|
|||
|
*no_add_attrs = true;
|
|||
|
}
|
|||
|
else if (DECL_INITIAL (decl))
|
|||
|
{
|
|||
|
error_with_decl (decl,
|
|||
|
"can't set `%s' attribute after definition",
|
|||
|
IDENTIFIER_POINTER (name));
|
|||
|
*no_add_attrs = true;
|
|||
|
}
|
|||
|
else
|
|||
|
DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (decl) = 1;
|
|||
|
|
|||
|
return NULL_TREE;
|
|||
|
}
|
|||
|
|
|||
|
/* Handle a "malloc" attribute; arguments as in
|
|||
|
struct attribute_spec.handler. */
|
|||
|
|
|||
|
static tree
|
|||
|
handle_malloc_attribute (node, name, args, flags, no_add_attrs)
|
|||
|
tree *node;
|
|||
|
tree name;
|
|||
|
tree args ATTRIBUTE_UNUSED;
|
|||
|
int flags ATTRIBUTE_UNUSED;
|
|||
|
bool *no_add_attrs;
|
|||
|
{
|
|||
|
if (TREE_CODE (*node) == FUNCTION_DECL)
|
|||
|
DECL_IS_MALLOC (*node) = 1;
|
|||
|
/* ??? TODO: Support types. */
|
|||
|
else
|
|||
|
{
|
|||
|
warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
|
|||
|
*no_add_attrs = true;
|
|||
|
}
|
|||
|
|
|||
|
return NULL_TREE;
|
|||
|
}
|
|||
|
|
|||
|
/* Handle a "no_limit_stack" attribute; arguments as in
|
|||
|
struct attribute_spec.handler. */
|
|||
|
|
|||
|
static tree
|
|||
|
handle_no_limit_stack_attribute (node, name, args, flags, no_add_attrs)
|
|||
|
tree *node;
|
|||
|
tree name;
|
|||
|
tree args ATTRIBUTE_UNUSED;
|
|||
|
int flags ATTRIBUTE_UNUSED;
|
|||
|
bool *no_add_attrs;
|
|||
|
{
|
|||
|
tree decl = *node;
|
|||
|
|
|||
|
if (TREE_CODE (decl) != FUNCTION_DECL)
|
|||
|
{
|
|||
|
error_with_decl (decl,
|
|||
|
"`%s' attribute applies only to functions",
|
|||
|
IDENTIFIER_POINTER (name));
|
|||
|
*no_add_attrs = true;
|
|||
|
}
|
|||
|
else if (DECL_INITIAL (decl))
|
|||
|
{
|
|||
|
error_with_decl (decl,
|
|||
|
"can't set `%s' attribute after definition",
|
|||
|
IDENTIFIER_POINTER (name));
|
|||
|
*no_add_attrs = true;
|
|||
|
}
|
|||
|
else
|
|||
|
DECL_NO_LIMIT_STACK (decl) = 1;
|
|||
|
|
|||
|
return NULL_TREE;
|
|||
|
}
|
|||
|
|
|||
|
/* Handle a "pure" attribute; arguments as in
|
|||
|
struct attribute_spec.handler. */
|
|||
|
|
|||
|
static tree
|
|||
|
handle_pure_attribute (node, name, args, flags, no_add_attrs)
|
|||
|
tree *node;
|
|||
|
tree name;
|
|||
|
tree args ATTRIBUTE_UNUSED;
|
|||
|
int flags ATTRIBUTE_UNUSED;
|
|||
|
bool *no_add_attrs;
|
|||
|
{
|
|||
|
if (TREE_CODE (*node) == FUNCTION_DECL)
|
|||
|
DECL_IS_PURE (*node) = 1;
|
|||
|
/* ??? TODO: Support types. */
|
|||
|
else
|
|||
|
{
|
|||
|
warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
|
|||
|
*no_add_attrs = true;
|
|||
|
}
|
|||
|
|
|||
|
return NULL_TREE;
|
|||
|
}
|
|||
|
|
|||
|
/* Handle a "deprecated" attribute; arguments as in
|
|||
|
struct attribute_spec.handler. */
|
|||
|
|
|||
|
static tree
|
|||
|
handle_deprecated_attribute (node, name, args, flags, no_add_attrs)
|
|||
|
tree *node;
|
|||
|
tree name;
|
|||
|
tree args ATTRIBUTE_UNUSED;
|
|||
|
int flags;
|
|||
|
bool *no_add_attrs;
|
|||
|
{
|
|||
|
tree type = NULL_TREE;
|
|||
|
int warn = 0;
|
|||
|
const char *what = NULL;
|
|||
|
|
|||
|
if (DECL_P (*node))
|
|||
|
{
|
|||
|
tree decl = *node;
|
|||
|
type = TREE_TYPE (decl);
|
|||
|
|
|||
|
if (TREE_CODE (decl) == TYPE_DECL
|
|||
|
|| TREE_CODE (decl) == PARM_DECL
|
|||
|
|| TREE_CODE (decl) == VAR_DECL
|
|||
|
|| TREE_CODE (decl) == FUNCTION_DECL
|
|||
|
|| TREE_CODE (decl) == FIELD_DECL)
|
|||
|
TREE_DEPRECATED (decl) = 1;
|
|||
|
else
|
|||
|
warn = 1;
|
|||
|
}
|
|||
|
else if (TYPE_P (*node))
|
|||
|
{
|
|||
|
if (!(flags & (int) ATTR_FLAG_TYPE_IN_PLACE))
|
|||
|
*node = build_type_copy (*node);
|
|||
|
TREE_DEPRECATED (*node) = 1;
|
|||
|
type = *node;
|
|||
|
}
|
|||
|
else
|
|||
|
warn = 1;
|
|||
|
|
|||
|
if (warn)
|
|||
|
{
|
|||
|
*no_add_attrs = true;
|
|||
|
if (type && TYPE_NAME (type))
|
|||
|
{
|
|||
|
if (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE)
|
|||
|
what = IDENTIFIER_POINTER (TYPE_NAME (*node));
|
|||
|
else if (TREE_CODE (TYPE_NAME (type)) == TYPE_DECL
|
|||
|
&& DECL_NAME (TYPE_NAME (type)))
|
|||
|
what = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type)));
|
|||
|
}
|
|||
|
if (what)
|
|||
|
warning ("`%s' attribute ignored for `%s'",
|
|||
|
IDENTIFIER_POINTER (name), what);
|
|||
|
else
|
|||
|
warning ("`%s' attribute ignored",
|
|||
|
IDENTIFIER_POINTER (name));
|
|||
|
}
|
|||
|
|
|||
|
return NULL_TREE;
|
|||
|
}
|
|||
|
|
|||
|
/* Handle a "vector_size" attribute; arguments as in
|
|||
|
struct attribute_spec.handler. */
|
|||
|
|
|||
|
static tree
|
|||
|
handle_vector_size_attribute (node, name, args, flags, no_add_attrs)
|
|||
|
tree *node;
|
|||
|
tree name;
|
|||
|
tree args;
|
|||
|
int flags ATTRIBUTE_UNUSED;
|
|||
|
bool *no_add_attrs;
|
|||
|
{
|
|||
|
unsigned HOST_WIDE_INT vecsize, nunits;
|
|||
|
enum machine_mode mode, orig_mode, new_mode;
|
|||
|
tree type = *node, new_type;
|
|||
|
|
|||
|
*no_add_attrs = true;
|
|||
|
|
|||
|
if (! host_integerp (TREE_VALUE (args), 1))
|
|||
|
{
|
|||
|
warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
|
|||
|
return NULL_TREE;
|
|||
|
}
|
|||
|
|
|||
|
/* Get the vector size (in bytes). */
|
|||
|
vecsize = tree_low_cst (TREE_VALUE (args), 1);
|
|||
|
|
|||
|
/* We need to provide for vector pointers, vector arrays, and
|
|||
|
functions returning vectors. For example:
|
|||
|
|
|||
|
__attribute__((vector_size(16))) short *foo;
|
|||
|
|
|||
|
In this case, the mode is SI, but the type being modified is
|
|||
|
HI, so we need to look further. */
|
|||
|
|
|||
|
while (POINTER_TYPE_P (type)
|
|||
|
|| TREE_CODE (type) == FUNCTION_TYPE
|
|||
|
|| TREE_CODE (type) == ARRAY_TYPE)
|
|||
|
type = TREE_TYPE (type);
|
|||
|
|
|||
|
/* Get the mode of the type being modified. */
|
|||
|
orig_mode = TYPE_MODE (type);
|
|||
|
|
|||
|
if (TREE_CODE (type) == RECORD_TYPE
|
|||
|
|| (GET_MODE_CLASS (orig_mode) != MODE_FLOAT
|
|||
|
&& GET_MODE_CLASS (orig_mode) != MODE_INT)
|
|||
|
|| ! host_integerp (TYPE_SIZE_UNIT (type), 1))
|
|||
|
{
|
|||
|
error ("invalid vector type for attribute `%s'",
|
|||
|
IDENTIFIER_POINTER (name));
|
|||
|
return NULL_TREE;
|
|||
|
}
|
|||
|
|
|||
|
/* Calculate how many units fit in the vector. */
|
|||
|
nunits = vecsize / tree_low_cst (TYPE_SIZE_UNIT (type), 1);
|
|||
|
|
|||
|
/* Find a suitably sized vector. */
|
|||
|
new_mode = VOIDmode;
|
|||
|
for (mode = GET_CLASS_NARROWEST_MODE (GET_MODE_CLASS (orig_mode) == MODE_INT
|
|||
|
? MODE_VECTOR_INT
|
|||
|
: MODE_VECTOR_FLOAT);
|
|||
|
mode != VOIDmode;
|
|||
|
mode = GET_MODE_WIDER_MODE (mode))
|
|||
|
if (vecsize == GET_MODE_SIZE (mode)
|
|||
|
&& nunits == (unsigned HOST_WIDE_INT) GET_MODE_NUNITS (mode))
|
|||
|
{
|
|||
|
new_mode = mode;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
if (new_mode == VOIDmode)
|
|||
|
error ("no vector mode with the size and type specified could be found");
|
|||
|
else
|
|||
|
{
|
|||
|
new_type = type_for_mode (new_mode, TREE_UNSIGNED (type));
|
|||
|
if (!new_type)
|
|||
|
error ("no vector mode with the size and type specified could be found");
|
|||
|
else
|
|||
|
/* Build back pointers if needed. */
|
|||
|
*node = vector_size_helper (*node, new_type);
|
|||
|
}
|
|||
|
|
|||
|
return NULL_TREE;
|
|||
|
}
|
|||
|
|
|||
|
/* HACK. GROSS. This is absolutely disgusting. I wish there was a
|
|||
|
better way.
|
|||
|
|
|||
|
If we requested a pointer to a vector, build up the pointers that
|
|||
|
we stripped off while looking for the inner type. Similarly for
|
|||
|
return values from functions.
|
|||
|
|
|||
|
The argument "type" is the top of the chain, and "bottom" is the
|
|||
|
new type which we will point to. */
|
|||
|
|
|||
|
static tree
|
|||
|
vector_size_helper (type, bottom)
|
|||
|
tree type, bottom;
|
|||
|
{
|
|||
|
tree inner, outer;
|
|||
|
|
|||
|
if (POINTER_TYPE_P (type))
|
|||
|
{
|
|||
|
inner = vector_size_helper (TREE_TYPE (type), bottom);
|
|||
|
outer = build_pointer_type (inner);
|
|||
|
}
|
|||
|
else if (TREE_CODE (type) == ARRAY_TYPE)
|
|||
|
{
|
|||
|
inner = vector_size_helper (TREE_TYPE (type), bottom);
|
|||
|
outer = build_array_type (inner, TYPE_VALUES (type));
|
|||
|
}
|
|||
|
else if (TREE_CODE (type) == FUNCTION_TYPE)
|
|||
|
{
|
|||
|
inner = vector_size_helper (TREE_TYPE (type), bottom);
|
|||
|
outer = build_function_type (inner, TYPE_VALUES (type));
|
|||
|
}
|
|||
|
else
|
|||
|
return bottom;
|
|||
|
|
|||
|
TREE_READONLY (outer) = TREE_READONLY (type);
|
|||
|
TREE_THIS_VOLATILE (outer) = TREE_THIS_VOLATILE (type);
|
|||
|
|
|||
|
return outer;
|
|||
|
}
|
|||
|
|
|||
|
/* Split SPECS_ATTRS, a list of declspecs and prefix attributes, into two
|
|||
|
lists. SPECS_ATTRS may also be just a typespec (eg: RECORD_TYPE).
|
|||
|
|
|||
|
The head of the declspec list is stored in DECLSPECS.
|
|||
|
The head of the attribute list is stored in PREFIX_ATTRIBUTES.
|
|||
|
|
|||
|
Note that attributes in SPECS_ATTRS are stored in the TREE_PURPOSE of
|
|||
|
the list elements. We drop the containing TREE_LIST nodes and link the
|
|||
|
resulting attributes together the way decl_attributes expects them. */
|
|||
|
|
|||
|
void
|
|||
|
split_specs_attrs (specs_attrs, declspecs, prefix_attributes)
|
|||
|
tree specs_attrs;
|
|||
|
tree *declspecs, *prefix_attributes;
|
|||
|
{
|
|||
|
tree t, s, a, next, specs, attrs;
|
|||
|
|
|||
|
/* This can happen after an __extension__ in pedantic mode. */
|
|||
|
if (specs_attrs != NULL_TREE
|
|||
|
&& TREE_CODE (specs_attrs) == INTEGER_CST)
|
|||
|
{
|
|||
|
*declspecs = NULL_TREE;
|
|||
|
*prefix_attributes = NULL_TREE;
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
/* This can happen in c++ (eg: decl: typespec initdecls ';'). */
|
|||
|
if (specs_attrs != NULL_TREE
|
|||
|
&& TREE_CODE (specs_attrs) != TREE_LIST)
|
|||
|
{
|
|||
|
*declspecs = specs_attrs;
|
|||
|
*prefix_attributes = NULL_TREE;
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
/* Remember to keep the lists in the same order, element-wise. */
|
|||
|
|
|||
|
specs = s = NULL_TREE;
|
|||
|
attrs = a = NULL_TREE;
|
|||
|
for (t = specs_attrs; t; t = next)
|
|||
|
{
|
|||
|
next = TREE_CHAIN (t);
|
|||
|
/* Declspecs have a non-NULL TREE_VALUE. */
|
|||
|
if (TREE_VALUE (t) != NULL_TREE)
|
|||
|
{
|
|||
|
if (specs == NULL_TREE)
|
|||
|
specs = s = t;
|
|||
|
else
|
|||
|
{
|
|||
|
TREE_CHAIN (s) = t;
|
|||
|
s = t;
|
|||
|
}
|
|||
|
}
|
|||
|
/* The TREE_PURPOSE may also be empty in the case of
|
|||
|
__attribute__(()). */
|
|||
|
else if (TREE_PURPOSE (t) != NULL_TREE)
|
|||
|
{
|
|||
|
if (attrs == NULL_TREE)
|
|||
|
attrs = a = TREE_PURPOSE (t);
|
|||
|
else
|
|||
|
{
|
|||
|
TREE_CHAIN (a) = TREE_PURPOSE (t);
|
|||
|
a = TREE_PURPOSE (t);
|
|||
|
}
|
|||
|
/* More attrs can be linked here, move A to the end. */
|
|||
|
while (TREE_CHAIN (a) != NULL_TREE)
|
|||
|
a = TREE_CHAIN (a);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Terminate the lists. */
|
|||
|
if (s != NULL_TREE)
|
|||
|
TREE_CHAIN (s) = NULL_TREE;
|
|||
|
if (a != NULL_TREE)
|
|||
|
TREE_CHAIN (a) = NULL_TREE;
|
|||
|
|
|||
|
/* All done. */
|
|||
|
*declspecs = specs;
|
|||
|
*prefix_attributes = attrs;
|
|||
|
}
|
|||
|
|
|||
|
/* Strip attributes from SPECS_ATTRS, a list of declspecs and attributes.
|
|||
|
This function is used by the parser when a rule will accept attributes
|
|||
|
in a particular position, but we don't want to support that just yet.
|
|||
|
|
|||
|
A warning is issued for every ignored attribute. */
|
|||
|
|
|||
|
tree
|
|||
|
strip_attrs (specs_attrs)
|
|||
|
tree specs_attrs;
|
|||
|
{
|
|||
|
tree specs, attrs;
|
|||
|
|
|||
|
split_specs_attrs (specs_attrs, &specs, &attrs);
|
|||
|
|
|||
|
while (attrs)
|
|||
|
{
|
|||
|
warning ("`%s' attribute ignored",
|
|||
|
IDENTIFIER_POINTER (TREE_PURPOSE (attrs)));
|
|||
|
attrs = TREE_CHAIN (attrs);
|
|||
|
}
|
|||
|
|
|||
|
return specs;
|
|||
|
}
|