view gcc/c-family/c-attribs.c @ 158:494b0b89df80 default tip

...
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Mon, 25 May 2020 18:13:55 +0900
parents 1830386684a0
children
line wrap: on
line source

/* C-family attributes handling.
   Copyright (C) 1992-2020 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 3, 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 COPYING3.  If not see
<http://www.gnu.org/licenses/>.  */

#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "target.h"
#include "function.h"
#include "tree.h"
#include "memmodel.h"
#include "c-common.h"
#include "gimple-expr.h"
#include "tm_p.h"
#include "stringpool.h"
#include "cgraph.h"
#include "diagnostic.h"
#include "intl.h"
#include "stor-layout.h"
#include "calls.h"
#include "attribs.h"
#include "varasm.h"
#include "trans-mem.h"
#include "c-objc.h"
#include "common/common-target.h"
#include "langhooks.h"
#include "tree-inline.h"
#include "toplev.h"
#include "tree-iterator.h"
#include "opts.h"
#include "gimplify.h"
#include "tree-pretty-print.h"

static tree handle_packed_attribute (tree *, tree, tree, int, bool *);
static tree handle_nocommon_attribute (tree *, tree, tree, int, bool *);
static tree handle_common_attribute (tree *, tree, tree, int, bool *);
static tree handle_hot_attribute (tree *, tree, tree, int, bool *);
static tree handle_cold_attribute (tree *, tree, tree, int, bool *);
static tree handle_no_sanitize_attribute (tree *, tree, tree, int, bool *);
static tree handle_no_sanitize_address_attribute (tree *, tree, tree,
						  int, bool *);
static tree handle_no_sanitize_thread_attribute (tree *, tree, tree,
						 int, bool *);
static tree handle_no_address_safety_analysis_attribute (tree *, tree, tree,
							 int, bool *);
static tree handle_no_sanitize_undefined_attribute (tree *, tree, tree, int,
						    bool *);
static tree handle_asan_odr_indicator_attribute (tree *, tree, tree, int,
						 bool *);
static tree handle_stack_protect_attribute (tree *, tree, tree, int, bool *);
static tree handle_noinline_attribute (tree *, tree, tree, int, bool *);
static tree handle_noclone_attribute (tree *, tree, tree, int, bool *);
static tree handle_nocf_check_attribute (tree *, tree, tree, int, bool *);
static tree handle_symver_attribute (tree *, tree, tree, int, bool *);
static tree handle_noicf_attribute (tree *, tree, tree, int, bool *);
static tree handle_noipa_attribute (tree *, tree, tree, int, bool *);
static tree handle_leaf_attribute (tree *, tree, tree, int, bool *);
static tree handle_always_inline_attribute (tree *, tree, tree, int,
					    bool *);
static tree handle_gnu_inline_attribute (tree *, tree, tree, int, bool *);
static tree handle_artificial_attribute (tree *, tree, tree, int, bool *);
static tree handle_flatten_attribute (tree *, tree, tree, int, bool *);
static tree handle_error_attribute (tree *, tree, tree, int, bool *);
static tree handle_used_attribute (tree *, tree, tree, int, bool *);
static tree handle_externally_visible_attribute (tree *, tree, tree, int,
						 bool *);
static tree handle_no_reorder_attribute (tree *, tree, tree, int,
						 bool *);
static tree handle_const_attribute (tree *, tree, tree, int, bool *);
static tree handle_transparent_union_attribute (tree *, tree, tree,
						int, bool *);
static tree handle_scalar_storage_order_attribute (tree *, tree, tree,
						   int, bool *);
static tree handle_constructor_attribute (tree *, tree, tree, int, bool *);
static tree handle_destructor_attribute (tree *, tree, tree, int, bool *);
static tree handle_mode_attribute (tree *, tree, tree, int, bool *);
static tree handle_section_attribute (tree *, tree, tree, int, bool *);
static tree handle_aligned_attribute (tree *, tree, tree, int, bool *);
static tree handle_warn_if_not_aligned_attribute (tree *, tree, tree,
						  int, bool *);
static tree handle_noinit_attribute (tree *, tree, tree, int, bool *);
static tree handle_weak_attribute (tree *, tree, tree, int, bool *) ;
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_visibility_attribute (tree *, tree, tree, int,
					 bool *);
static tree handle_tls_model_attribute (tree *, tree, tree, int,
					bool *);
static tree handle_no_instrument_function_attribute (tree *, tree,
						     tree, int, bool *);
static tree handle_no_profile_instrument_function_attribute (tree *, tree,
							     tree, int, bool *);
static tree handle_malloc_attribute (tree *, tree, tree, int, bool *);
static tree handle_returns_twice_attribute (tree *, tree, tree, int, bool *);
static tree handle_no_limit_stack_attribute (tree *, tree, tree, int,
					     bool *);
static tree handle_pure_attribute (tree *, tree, tree, int, bool *);
static tree handle_tm_attribute (tree *, tree, tree, int, bool *);
static tree handle_tm_wrap_attribute (tree *, tree, tree, int, bool *);
static tree handle_novops_attribute (tree *, tree, tree, int, bool *);
static tree handle_vector_size_attribute (tree *, tree, tree, int,
					  bool *);
static tree handle_nonnull_attribute (tree *, tree, tree, int, bool *);
static tree handle_nonstring_attribute (tree *, tree, tree, int, bool *);
static tree handle_nothrow_attribute (tree *, tree, tree, int, bool *);
static tree handle_cleanup_attribute (tree *, tree, tree, int, bool *);
static tree handle_warn_unused_result_attribute (tree *, tree, tree, int,
						 bool *);
static tree handle_access_attribute (tree *, tree, tree, int, bool *);

static tree handle_sentinel_attribute (tree *, tree, tree, int, bool *);
static tree handle_type_generic_attribute (tree *, tree, tree, int, bool *);
static tree handle_alloc_size_attribute (tree *, tree, tree, int, bool *);
static tree handle_alloc_align_attribute (tree *, tree, tree, int, bool *);
static tree handle_assume_aligned_attribute (tree *, tree, tree, int, bool *);
static tree handle_target_attribute (tree *, tree, tree, int, bool *);
static tree handle_target_clones_attribute (tree *, tree, tree, int, bool *);
static tree handle_optimize_attribute (tree *, tree, tree, int, bool *);
static tree ignore_attribute (tree *, tree, tree, int, bool *);
static tree handle_no_split_stack_attribute (tree *, tree, tree, int, bool *);
static tree handle_fnspec_attribute (tree *, tree, tree, int, bool *);
static tree handle_warn_unused_attribute (tree *, tree, tree, int, bool *);
static tree handle_returns_nonnull_attribute (tree *, tree, tree, int, bool *);
static tree handle_omp_declare_simd_attribute (tree *, tree, tree, int,
					       bool *);
static tree handle_omp_declare_variant_attribute (tree *, tree, tree, int,
						  bool *);
static tree handle_simd_attribute (tree *, tree, tree, int, bool *);
static tree handle_omp_declare_target_attribute (tree *, tree, tree, int,
						 bool *);
static tree handle_designated_init_attribute (tree *, tree, tree, int, bool *);
static tree handle_patchable_function_entry_attribute (tree *, tree, tree,
						       int, bool *);
static tree handle_copy_attribute (tree *, tree, tree, int, bool *);

/* Helper to define attribute exclusions.  */
#define ATTR_EXCL(name, function, type, variable)	\
  { name, function, type, variable }

/* Define attributes that are mutually exclusive with one another.  */
static const struct attribute_spec::exclusions attr_aligned_exclusions[] =
{
  /* Attribute name     exclusion applies to:
	                function, type, variable */
  ATTR_EXCL ("aligned", true, false, false),
  ATTR_EXCL ("packed", true, false, false),
  ATTR_EXCL (NULL, false, false, false)
};

extern const struct attribute_spec::exclusions attr_cold_hot_exclusions[] =
{
  ATTR_EXCL ("cold", true, true, true),
  ATTR_EXCL ("hot", true, true, true),
  ATTR_EXCL (NULL, false, false, false)
};

static const struct attribute_spec::exclusions attr_common_exclusions[] =
{
  ATTR_EXCL ("common", true, true, true),
  ATTR_EXCL ("nocommon", true, true, true),
  ATTR_EXCL (NULL, false, false, false),
};

static const struct attribute_spec::exclusions attr_inline_exclusions[] =
{
  ATTR_EXCL ("noinline", true, true, true),
  ATTR_EXCL (NULL, false, false, false),
};

static const struct attribute_spec::exclusions attr_noinline_exclusions[] =
{
  ATTR_EXCL ("always_inline", true, true, true),
  ATTR_EXCL ("gnu_inline", true, true, true),
  ATTR_EXCL (NULL, false, false, false),
};

extern const struct attribute_spec::exclusions attr_noreturn_exclusions[] =
{
  ATTR_EXCL ("alloc_align", true, true, true),
  ATTR_EXCL ("alloc_size", true, true, true),
  ATTR_EXCL ("const", true, true, true),
  ATTR_EXCL ("malloc", true, true, true),
  ATTR_EXCL ("pure", true, true, true),
  ATTR_EXCL ("returns_twice", true, true, true),
  ATTR_EXCL ("warn_unused_result", true, true, true),
  ATTR_EXCL (NULL, false, false, false),
};

static const struct attribute_spec::exclusions
attr_warn_unused_result_exclusions[] =
{
  ATTR_EXCL ("noreturn", true, true, true),
  ATTR_EXCL ("warn_unused_result", true, true, true),
  ATTR_EXCL (NULL, false, false, false),
};

static const struct attribute_spec::exclusions attr_returns_twice_exclusions[] =
{
  ATTR_EXCL ("noreturn", true, true, true),
  ATTR_EXCL (NULL, false, false, false),
};

/* Exclusions that apply to attribute alloc_align, alloc_size, and malloc.  */
static const struct attribute_spec::exclusions attr_alloc_exclusions[] =
{
  ATTR_EXCL ("const", true, true, true),
  ATTR_EXCL ("noreturn", true, true, true),
  ATTR_EXCL ("pure", true, true, true),
  ATTR_EXCL (NULL, false, false, false),
};

static const struct attribute_spec::exclusions attr_const_pure_exclusions[] =
{
  ATTR_EXCL ("const", true, true, true),
  ATTR_EXCL ("alloc_align", true, true, true),
  ATTR_EXCL ("alloc_size", true, true, true),
  ATTR_EXCL ("malloc", true, true, true),
  ATTR_EXCL ("noreturn", true, true, true),
  ATTR_EXCL ("pure", true, true, true),
  ATTR_EXCL (NULL, false, false, false)
};

static const struct attribute_spec::exclusions attr_noinit_exclusions[] =
{
  ATTR_EXCL ("noinit", true, true, true),
  ATTR_EXCL ("section", true, true, true),
  ATTR_EXCL (NULL, false, false, false),
};

/* Table of machine-independent attributes common to all C-like languages.

   Current list of processed common attributes: nonnull.  */
const struct attribute_spec c_common_attribute_table[] =
{
  /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
       affects_type_identity, handler, exclude } */
  { "packed",                 0, 0, false, false, false, false,
			      handle_packed_attribute,
	                      attr_aligned_exclusions },
  { "nocommon",               0, 0, true,  false, false, false,
			      handle_nocommon_attribute,
	                      attr_common_exclusions },
  { "common",                 0, 0, true,  false, false, false,
			      handle_common_attribute,
	                      attr_common_exclusions },
  /* 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, false,
			      handle_noreturn_attribute,
	                      attr_noreturn_exclusions },
  { "volatile",               0, 0, true,  false, false, false,
			      handle_noreturn_attribute, NULL },
  { "stack_protect",          0, 0, true,  false, false, false,
			      handle_stack_protect_attribute, NULL },
  { "noinline",               0, 0, true,  false, false, false,
			      handle_noinline_attribute,
	                      attr_noinline_exclusions },
  { "noclone",                0, 0, true,  false, false, false,
			      handle_noclone_attribute, NULL },
  { "no_icf",                 0, 0, true,  false, false, false,
			      handle_noicf_attribute, NULL },
  { "noipa",		      0, 0, true,  false, false, false,
			      handle_noipa_attribute, NULL },
  { "leaf",                   0, 0, true,  false, false, false,
			      handle_leaf_attribute, NULL },
  { "always_inline",          0, 0, true,  false, false, false,
			      handle_always_inline_attribute,
	                      attr_inline_exclusions },
  { "gnu_inline",             0, 0, true,  false, false, false,
			      handle_gnu_inline_attribute,
	                      attr_inline_exclusions },
  { "artificial",             0, 0, true,  false, false, false,
			      handle_artificial_attribute, NULL },
  { "flatten",                0, 0, true,  false, false, false,
			      handle_flatten_attribute, NULL },
  { "used",                   0, 0, true,  false, false, false,
			      handle_used_attribute, NULL },
  { "unused",                 0, 0, false, false, false, false,
			      handle_unused_attribute, NULL },
  { "externally_visible",     0, 0, true,  false, false, false,
			      handle_externally_visible_attribute, NULL },
  { "no_reorder",	      0, 0, true, false, false, false,
	                      handle_no_reorder_attribute, NULL },
  /* The same comments as for noreturn attributes apply to const ones.  */
  { "const",                  0, 0, true,  false, false, false,
			      handle_const_attribute,
	                      attr_const_pure_exclusions },
  { "scalar_storage_order",   1, 1, false, false, false, false,
			      handle_scalar_storage_order_attribute, NULL },
  { "transparent_union",      0, 0, false, false, false, false,
			      handle_transparent_union_attribute, NULL },
  { "constructor",            0, 1, true,  false, false, false,
			      handle_constructor_attribute, NULL },
  { "destructor",             0, 1, true,  false, false, false,
			      handle_destructor_attribute, NULL },
  { "mode",                   1, 1, false,  true, false, false,
			      handle_mode_attribute, NULL },
  { "section",                1, 1, true,  false, false, false,
			      handle_section_attribute, attr_noinit_exclusions },
  { "aligned",                0, 1, false, false, false, false,
			      handle_aligned_attribute,
	                      attr_aligned_exclusions },
  { "warn_if_not_aligned",    0, 1, false, false, false, false,
			      handle_warn_if_not_aligned_attribute, NULL },
  { "weak",                   0, 0, true,  false, false, false,
			      handle_weak_attribute, NULL },
  { "noplt",                   0, 0, true,  false, false, false,
			      handle_noplt_attribute, NULL },
  { "ifunc",                  1, 1, true,  false, false, false,
			      handle_ifunc_attribute, NULL },
  { "alias",                  1, 1, true,  false, false, false,
			      handle_alias_attribute, NULL },
  { "weakref",                0, 1, true,  false, false, false,
			      handle_weakref_attribute, NULL },
  { "no_instrument_function", 0, 0, true,  false, false, false,
			      handle_no_instrument_function_attribute,
			      NULL },
  { "no_profile_instrument_function",  0, 0, true, false, false, false,
			      handle_no_profile_instrument_function_attribute,
			      NULL },
  { "malloc",                 0, 0, true,  false, false, false,
			      handle_malloc_attribute, attr_alloc_exclusions },
  { "returns_twice",          0, 0, true,  false, false, false,
			      handle_returns_twice_attribute,
	                      attr_returns_twice_exclusions },
  { "no_stack_limit",         0, 0, true,  false, false, false,
			      handle_no_limit_stack_attribute, NULL },
  { "pure",                   0, 0, true,  false, false, false,
			      handle_pure_attribute,
	                      attr_const_pure_exclusions },
  { "transaction_callable",   0, 0, false, true,  false, false,
			      handle_tm_attribute, NULL },
  { "transaction_unsafe",     0, 0, false, true,  false, true,
			      handle_tm_attribute, NULL },
  { "transaction_safe",       0, 0, false, true,  false, true,
			      handle_tm_attribute, NULL },
  { "transaction_safe_dynamic", 0, 0, true, false,  false, false,
			      handle_tm_attribute, NULL },
  { "transaction_may_cancel_outer", 0, 0, false, true, false, false,
			      handle_tm_attribute, NULL },
  /* ??? These two attributes didn't make the transition from the
     Intel language document to the multi-vendor language document.  */
  { "transaction_pure",       0, 0, false, true,  false, false,
			      handle_tm_attribute, NULL },
  { "transaction_wrap",       1, 1, true,  false,  false, false,
			     handle_tm_wrap_attribute, NULL },
  /* For internal use (marking of builtins) only.  The name contains space
     to prevent its usage in source code.  */
  { "no vops",                0, 0, true,  false, false, false,
			      handle_novops_attribute, NULL },
  { "deprecated",             0, 1, false, false, false, false,
			      handle_deprecated_attribute, NULL },
  { "vector_size",	      1, 1, false, true, false, true,
			      handle_vector_size_attribute, NULL },
  { "visibility",	      1, 1, false, false, false, false,
			      handle_visibility_attribute, NULL },
  { "tls_model",	      1, 1, true,  false, false, false,
			      handle_tls_model_attribute, NULL },
  { "nonnull",                0, -1, false, true, true, false,
			      handle_nonnull_attribute, NULL },
  { "nonstring",              0, 0, true, false, false, false,
			      handle_nonstring_attribute, NULL },
  { "nothrow",                0, 0, true,  false, false, false,
			      handle_nothrow_attribute, NULL },
  { "may_alias",	      0, 0, false, true, false, false, NULL, NULL },
  { "cleanup",		      1, 1, true, false, false, false,
			      handle_cleanup_attribute, NULL },
  { "warn_unused_result",     0, 0, false, true, true, false,
			      handle_warn_unused_result_attribute,
	                      attr_warn_unused_result_exclusions },
  { "sentinel",               0, 1, false, true, true, false,
			      handle_sentinel_attribute, NULL },
  /* For internal use (marking of builtins) only.  The name contains space
     to prevent its usage in source code.  */
  { "type generic",           0, 0, false, true, true, false,
			      handle_type_generic_attribute, NULL },
  { "alloc_size",	      1, 2, false, true, true, false,
			      handle_alloc_size_attribute,
	                      attr_alloc_exclusions },
  { "cold",                   0, 0, true,  false, false, false,
			      handle_cold_attribute,
	                      attr_cold_hot_exclusions },
  { "hot",                    0, 0, true,  false, false, false,
			      handle_hot_attribute,
	                      attr_cold_hot_exclusions },
  { "no_address_safety_analysis",
			      0, 0, true, false, false, false,
			      handle_no_address_safety_analysis_attribute,
			      NULL },
  { "no_sanitize",	      1, -1, true, false, false, false,
			      handle_no_sanitize_attribute, NULL },
  { "no_sanitize_address",    0, 0, true, false, false, false,
			      handle_no_sanitize_address_attribute, NULL },
  { "no_sanitize_thread",     0, 0, true, false, false, false,
			      handle_no_sanitize_thread_attribute, NULL },
  { "no_sanitize_undefined",  0, 0, true, false, false, false,
			      handle_no_sanitize_undefined_attribute, NULL },
  { "asan odr indicator",     0, 0, true, false, false, false,
			      handle_asan_odr_indicator_attribute, NULL },
  { "warning",		      1, 1, true,  false, false, false,
			      handle_error_attribute, NULL },
  { "error",		      1, 1, true,  false, false, false,
			      handle_error_attribute, NULL },
  { "target",                 1, -1, true, false, false, false,
			      handle_target_attribute, NULL },
  { "target_clones",          1, -1, true, false, false, false,
			      handle_target_clones_attribute, NULL },
  { "optimize",               1, -1, true, false, false, false,
			      handle_optimize_attribute, NULL },
  /* For internal use only.  The leading '*' both prevents its usage in
     source code and signals that it may be overridden by machine tables.  */
  { "*tm regparm",            0, 0, false, true, true, false,
			      ignore_attribute, NULL },
  { "no_split_stack",	      0, 0, true,  false, false, false,
			      handle_no_split_stack_attribute, NULL },
  /* For internal use (marking of builtins and runtime functions) only.
     The name contains space to prevent its usage in source code.  */
  { "fn spec",		      1, 1, false, true, true, false,
			      handle_fnspec_attribute, NULL },
  { "warn_unused",            0, 0, false, false, false, false,
			      handle_warn_unused_attribute, NULL },
  { "returns_nonnull",        0, 0, false, true, true, false,
			      handle_returns_nonnull_attribute, NULL },
  { "omp declare simd",       0, -1, true,  false, false, false,
			      handle_omp_declare_simd_attribute, NULL },
  { "omp declare variant base", 0, -1, true,  false, false, false,
			      handle_omp_declare_variant_attribute, NULL },
  { "omp declare variant variant", 0, -1, true,  false, false, false,
			      handle_omp_declare_variant_attribute, NULL },
  { "simd",		      0, 1, true,  false, false, false,
			      handle_simd_attribute, NULL },
  { "omp declare target",     0, -1, true, false, false, false,
			      handle_omp_declare_target_attribute, NULL },
  { "omp declare target link", 0, 0, true, false, false, false,
			      handle_omp_declare_target_attribute, NULL },
  { "omp declare target implicit", 0, 0, true, false, false, false,
			      handle_omp_declare_target_attribute, NULL },
  { "omp declare target host", 0, 0, true, false, false, false,
			      handle_omp_declare_target_attribute, NULL },
  { "omp declare target nohost", 0, 0, true, false, false, false,
			      handle_omp_declare_target_attribute, NULL },
  { "omp declare target block", 0, 0, true, false, false, false,
			      handle_omp_declare_target_attribute, NULL },
  { "alloc_align",	      1, 1, false, true, true, false,
			      handle_alloc_align_attribute,
	                      attr_alloc_exclusions },
  { "assume_aligned",	      1, 2, false, true, true, false,
			      handle_assume_aligned_attribute, NULL },
  { "designated_init",        0, 0, false, true, false, false,
			      handle_designated_init_attribute, NULL },
  { "fallthrough",	      0, 0, false, false, false, false,
			      handle_fallthrough_attribute, NULL },
  { "patchable_function_entry",	1, 2, true, false, false, false,
			      handle_patchable_function_entry_attribute,
			      NULL },
  { "nocf_check",	      0, 0, false, true, true, true,
			      handle_nocf_check_attribute, NULL },
  { "symver",		      1, -1, true, false, false, false,
			      handle_symver_attribute, NULL},
  { "copy",                   1, 1, false, false, false, false,
			      handle_copy_attribute, NULL },
  { "noinit",		      0, 0, true,  false, false, false,
			      handle_noinit_attribute, attr_noinit_exclusions },
  { "access",		      1, 3, false, true, true, false,
			      handle_access_attribute, NULL },
  { NULL,                     0, 0, false, false, false, false, NULL, NULL }
};

/* Give the specifications for the format attributes, used by C and all
   descendants.

   Current list of processed format attributes: format, format_arg.  */
const struct attribute_spec c_common_format_attribute_table[] =
{
  /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
       affects_type_identity, handler, exclude } */
  { "format",                 3, 3, false, true,  true, false,
			      handle_format_attribute, NULL },
  { "format_arg",             1, 1, false, true,  true, false,
			      handle_format_arg_attribute, NULL },
  { NULL,                     0, 0, false, false, false, false, NULL, NULL }
};

/* Returns TRUE iff the attribute indicated by ATTR_ID takes a plain
   identifier as an argument, so the front end shouldn't look it up.  */

bool
attribute_takes_identifier_p (const_tree attr_id)
{
  const struct attribute_spec *spec = lookup_attribute_spec (attr_id);
  if (spec == NULL)
    /* Unknown attribute that we'll end up ignoring, return true so we
       don't complain about an identifier argument.  */
    return true;
  else if (!strcmp ("mode", spec->name)
	   || !strcmp ("format", spec->name)
	   || !strcmp ("cleanup", spec->name)
	   || !strcmp ("access", spec->name))
    return true;
  else
    return targetm.attribute_takes_identifier_p (attr_id);
}

/* Verify that argument value POS at position ARGNO to attribute NAME
   applied to function TYPE refers to a function parameter at position
   POS and the expected type CODE.  Treat CODE == INTEGER_TYPE as
   matching all C integral types except bool.  If successful, return
   POS after default conversions, if any.  Otherwise, issue appropriate
   warnings and return null.  A non-zero 1-based ARGNO should be passed
   in by callers only for attributes with more than one argument.  */

tree
positional_argument (const_tree fntype, const_tree atname, tree pos,
		     tree_code code, int argno /* = 0 */,
		     int flags /* = posargflags () */)
{
  if (pos && TREE_CODE (pos) != IDENTIFIER_NODE
      && TREE_CODE (pos) != FUNCTION_DECL)
    pos = default_conversion (pos);

  tree postype = TREE_TYPE (pos);
  if (pos == error_mark_node || !postype)
    {
      /* Only mention the positional argument number when it's non-zero.  */
      if (argno < 1)
	warning (OPT_Wattributes,
		 "%qE attribute argument is invalid", atname);
      else
	warning (OPT_Wattributes,
		 "%qE attribute argument %i is invalid", atname, argno);

      return NULL_TREE;
    }

  if (!INTEGRAL_TYPE_P (postype))
    {
      /* Handle this case specially to avoid mentioning the value
	 of pointer constants in diagnostics.  Only mention
	 the positional argument number when it's non-zero.  */
      if (argno < 1)
	warning (OPT_Wattributes,
		 "%qE attribute argument has type %qT",
		 atname, postype);
      else
	warning (OPT_Wattributes,
		 "%qE attribute argument %i has type %qT",
		 atname, argno, postype);

      return NULL_TREE;
    }

  if (TREE_CODE (pos) != INTEGER_CST)
    {
      /* Only mention the argument number when it's non-zero.  */
      if (argno < 1)
	warning (OPT_Wattributes,
		 "%qE attribute argument value %qE is not an integer "
		 "constant",
		 atname, pos);
      else
	warning (OPT_Wattributes,
		 "%qE attribute argument %i value %qE is not an integer "
		 "constant",
		 atname, argno, pos);

      return NULL_TREE;
    }

  /* Argument positions are 1-based.  */
  if (integer_zerop (pos))
    {
      if (flags & POSARG_ZERO)
	/* Zero is explicitly allowed.  */
	return pos;

      if (argno < 1)
	warning (OPT_Wattributes,
		 "%qE attribute argument value %qE does not refer to "
		 "a function parameter",
		 atname, pos);
      else
	warning (OPT_Wattributes,
		 "%qE attribute argument %i value %qE does not refer to "
		 "a function parameter",
		 atname, argno, pos);

      return NULL_TREE;
    }

  if (!prototype_p (fntype))
    return pos;

  /* Verify that the argument position does not exceed the number
     of formal arguments to the function.  When POSARG_ELLIPSIS
     is set, ARGNO may be beyond the last argument of a vararg
     function.  */
  unsigned nargs = type_num_arguments (fntype);
  if (!nargs
      || !tree_fits_uhwi_p (pos)
      || ((flags & POSARG_ELLIPSIS) == 0
	  && !IN_RANGE (tree_to_uhwi (pos), 1, nargs)))
    {

      if (argno < 1)
	warning (OPT_Wattributes,
		 "%qE attribute argument value %qE exceeds the number "
		 "of function parameters %u",
		 atname, pos, nargs);
      else
	warning (OPT_Wattributes,
		 "%qE attribute argument %i value %qE exceeds the number "
		 "of function parameters %u",
		 atname, argno, pos, nargs);
      return NULL_TREE;
    }

  /* Verify that the type of the referenced formal argument matches
     the expected type.  */
  unsigned HOST_WIDE_INT ipos = tree_to_uhwi (pos);

  /* Zero was handled above.  */
  gcc_assert (ipos != 0);

  if (tree argtype = type_argument_type (fntype, ipos))
    {
      if (flags & POSARG_ELLIPSIS)
	{
	  if (argno < 1)
	    error ("%qE attribute argument value %qE does not refer to "
		   "a variable argument list",
		   atname, pos);
	  else
	    error ("%qE attribute argument %i value %qE does not refer to "
		   "a variable argument list",
		   atname, argno, pos);
	  return NULL_TREE;
	}

      /* Where the expected code is STRING_CST accept any pointer
	 expected by attribute format (this includes possibly qualified
	 char pointers and, for targets like Darwin, also pointers to
	 struct CFString).  */
      bool type_match;
      if (code == STRING_CST)
	type_match = valid_format_string_type_p (argtype);
      else if (code == INTEGER_TYPE)
	/* For integers, accept enums, wide characters and other types
	   that match INTEGRAL_TYPE_P except for bool.  */
	type_match = (INTEGRAL_TYPE_P (argtype)
		      && TREE_CODE (argtype) != BOOLEAN_TYPE);
      else
	type_match = TREE_CODE (argtype) == code;

      if (!type_match)
	{
	  if (code == STRING_CST)
	    {
	      /* Reject invalid format strings with an error.  */
	      if (argno < 1)
		error ("%qE attribute argument value %qE refers to "
		       "parameter type %qT",
		       atname, pos, argtype);
	      else
		error ("%qE attribute argument %i value %qE refers to "
		       "parameter type %qT",
		       atname, argno, pos, argtype);

	      return NULL_TREE;
	    }

	  if (argno < 1)
	    warning (OPT_Wattributes,
		     "%qE attribute argument value %qE refers to "
		     "parameter type %qT",
		     atname, pos, argtype);
	  else
	    warning (OPT_Wattributes,
		   "%qE attribute argument %i value %qE refers to "
		     "parameter type %qT",
		     atname, argno, pos, argtype);
	  return NULL_TREE;
	}
    }
  else if (!(flags & POSARG_ELLIPSIS))
    {
      if (argno < 1)
	warning (OPT_Wattributes,
		 "%qE attribute argument value %qE refers to "
		 "a variadic function parameter of unknown type",
		 atname, pos);
      else
	warning (OPT_Wattributes,
		 "%qE attribute argument %i value %qE refers to "
		 "a variadic function parameter of unknown type",
		 atname, argno, pos);
      return NULL_TREE;
    }

  return pos;
}


/* Attribute handlers common to C front ends.  */

/* Handle a "packed" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_packed_attribute (tree *node, tree name, tree ARG_UNUSED (args),
			 int flags, bool *no_add_attrs)
{
  if (TYPE_P (*node))
    {
      if (!(flags & (int) ATTR_FLAG_TYPE_IN_PLACE))
	{
	  warning (OPT_Wattributes,
		   "%qE attribute ignored for type %qT", name, *node);
	  *no_add_attrs = true;
	}
      else
	TYPE_PACKED (*node) = 1;
    }
  else if (TREE_CODE (*node) == FIELD_DECL)
    {
      if (TYPE_ALIGN (TREE_TYPE (*node)) <= BITS_PER_UNIT
	  /* Still pack bitfields.  */
	  && ! DECL_C_BIT_FIELD (*node))
	warning (OPT_Wattributes,
		 "%qE attribute ignored for field of type %qT",
		 name, TREE_TYPE (*node));
      else
	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.
     We can't set DECL_PACKED on the type of a TYPE_DECL, because
     that changes what the typedef is typing.  */
  else
    {
      warning (OPT_Wattributes, "%qE attribute ignored", name);
      *no_add_attrs = true;
    }

  return NULL_TREE;
}

/* Handle a "nocommon" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_nocommon_attribute (tree *node, tree name,
			   tree ARG_UNUSED (args),
			   int ARG_UNUSED (flags), bool *no_add_attrs)
{
  if (VAR_P (*node))
    DECL_COMMON (*node) = 0;
  else
    {
      warning (OPT_Wattributes, "%qE attribute ignored", name);
      *no_add_attrs = true;
    }

  return NULL_TREE;
}

/* Handle a "common" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_common_attribute (tree *node, tree name, tree ARG_UNUSED (args),
			 int ARG_UNUSED (flags), bool *no_add_attrs)
{
  if (VAR_P (*node))
    DECL_COMMON (*node) = 1;
  else
    {
      warning (OPT_Wattributes, "%qE attribute ignored", name);
      *no_add_attrs = true;
    }

  return NULL_TREE;
}

/* Handle a "noreturn" attribute; arguments as in
   struct attribute_spec.handler.  */

tree
handle_noreturn_attribute (tree *node, tree name, tree ARG_UNUSED (args),
			   int ARG_UNUSED (flags), bool *no_add_attrs)
{
  tree type = TREE_TYPE (*node);

  /* See FIXME comment in c_common_attribute_table.  */
  if (TREE_CODE (*node) == FUNCTION_DECL
      || objc_method_decl (TREE_CODE (*node)))
    TREE_THIS_VOLATILE (*node) = 1;
  else if (TREE_CODE (type) == POINTER_TYPE
	   && TREE_CODE (TREE_TYPE (type)) == FUNCTION_TYPE)
    TREE_TYPE (*node)
      = (build_qualified_type
	 (build_pointer_type
	  (build_type_variant (TREE_TYPE (type),
			       TYPE_READONLY (TREE_TYPE (type)), 1)),
	  TYPE_QUALS (type)));
  else
    {
      warning (OPT_Wattributes, "%qE attribute ignored", name);
      *no_add_attrs = true;
    }

  return NULL_TREE;
}

/* Handle a "hot" and attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_hot_attribute (tree *node, tree name, tree ARG_UNUSED (args),
		      int ARG_UNUSED (flags), bool *no_add_attrs)
{
  if (TREE_CODE (*node) == FUNCTION_DECL
      || TREE_CODE (*node) == LABEL_DECL)
    {
      /* Attribute hot processing is done later with lookup_attribute.  */
    }
  else
    {
      warning (OPT_Wattributes, "%qE attribute ignored", name);
      *no_add_attrs = true;
    }

  return NULL_TREE;
}

/* Handle a "cold" and attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_cold_attribute (tree *node, tree name, tree ARG_UNUSED (args),
		       int ARG_UNUSED (flags), bool *no_add_attrs)
{
  if (TREE_CODE (*node) == FUNCTION_DECL
      || TREE_CODE (*node) == LABEL_DECL)
    {
      /* Attribute cold processing is done later with lookup_attribute.  */
    }
  else
    {
      warning (OPT_Wattributes, "%qE attribute ignored", name);
      *no_add_attrs = true;
    }

  return NULL_TREE;
}

/* Add FLAGS for a function NODE to no_sanitize_flags in DECL_ATTRIBUTES.  */

void
add_no_sanitize_value (tree node, unsigned int flags)
{
  tree attr = lookup_attribute ("no_sanitize", DECL_ATTRIBUTES (node));
  if (attr)
    {
      unsigned int old_value = tree_to_uhwi (TREE_VALUE (attr));
      flags |= old_value;

      if (flags == old_value)
	return;

      TREE_VALUE (attr) = build_int_cst (unsigned_type_node, flags);
    }
  else
    DECL_ATTRIBUTES (node)
      = tree_cons (get_identifier ("no_sanitize"),
		   build_int_cst (unsigned_type_node, flags),
		   DECL_ATTRIBUTES (node));
}

/* Handle a "no_sanitize" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_no_sanitize_attribute (tree *node, tree name, tree args, int,
			      bool *no_add_attrs)
{
  unsigned int flags = 0;
  *no_add_attrs = true;
  if (TREE_CODE (*node) != FUNCTION_DECL)
    {
      warning (OPT_Wattributes, "%qE attribute ignored", name);
      return NULL_TREE;
    }

  for (; args; args = TREE_CHAIN (args))
    {
      tree id = TREE_VALUE (args);
      if (TREE_CODE (id) != STRING_CST)
	{
	  error ("%qE argument not a string", name);
	  return NULL_TREE;
	}

      char *string = ASTRDUP (TREE_STRING_POINTER (id));
      flags |= parse_no_sanitize_attribute (string);
    }

  add_no_sanitize_value (*node, flags);

  return NULL_TREE;
}

/* Handle a "no_sanitize_address" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_no_sanitize_address_attribute (tree *node, tree name, tree, int,
				      bool *no_add_attrs)
{
  *no_add_attrs = true;
  if (TREE_CODE (*node) != FUNCTION_DECL)
    warning (OPT_Wattributes, "%qE attribute ignored", name);
  else
    add_no_sanitize_value (*node, SANITIZE_ADDRESS);

  return NULL_TREE;
}

/* Handle a "no_sanitize_thread" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_no_sanitize_thread_attribute (tree *node, tree name, tree, int,
				      bool *no_add_attrs)
{
  *no_add_attrs = true;
  if (TREE_CODE (*node) != FUNCTION_DECL)
    warning (OPT_Wattributes, "%qE attribute ignored", name);
  else
    add_no_sanitize_value (*node, SANITIZE_THREAD);

  return NULL_TREE;
}


/* Handle a "no_address_safety_analysis" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_no_address_safety_analysis_attribute (tree *node, tree name, tree, int,
					     bool *no_add_attrs)
{
  *no_add_attrs = true;
  if (TREE_CODE (*node) != FUNCTION_DECL)
    warning (OPT_Wattributes, "%qE attribute ignored", name);
  else
    add_no_sanitize_value (*node, SANITIZE_ADDRESS);

  return NULL_TREE;
}

/* Handle a "no_sanitize_undefined" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_no_sanitize_undefined_attribute (tree *node, tree name, tree, int,
				      bool *no_add_attrs)
{
  *no_add_attrs = true;
  if (TREE_CODE (*node) != FUNCTION_DECL)
    warning (OPT_Wattributes, "%qE attribute ignored", name);
  else
    add_no_sanitize_value (*node,
			   SANITIZE_UNDEFINED | SANITIZE_UNDEFINED_NONDEFAULT);

  return NULL_TREE;
}

/* Handle an "asan odr indicator" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_asan_odr_indicator_attribute (tree *, tree, tree, int, bool *)
{
  return NULL_TREE;
}

/* Handle a "stack_protect" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_stack_protect_attribute (tree *node, tree name, tree, int,
				bool *no_add_attrs)
{
  if (TREE_CODE (*node) != FUNCTION_DECL)
    {
      warning (OPT_Wattributes, "%qE attribute ignored", name);
      *no_add_attrs = true;
    }

  return NULL_TREE;
}

/* Handle a "noipa" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_noipa_attribute (tree *node, tree name, tree, int, bool *no_add_attrs)
{
  if (TREE_CODE (*node) != FUNCTION_DECL)
    {
      warning (OPT_Wattributes, "%qE attribute ignored", name);
      *no_add_attrs = true;
    }

  return NULL_TREE;
}

/* Handle a "noinline" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_noinline_attribute (tree *node, tree name,
			   tree ARG_UNUSED (args),
			   int ARG_UNUSED (flags), bool *no_add_attrs)
{
  if (TREE_CODE (*node) == FUNCTION_DECL)
    {
      if (lookup_attribute ("always_inline", DECL_ATTRIBUTES (*node)))
	{
	  warning (OPT_Wattributes, "%qE attribute ignored due to conflict "
		   "with attribute %qs", name, "always_inline");
	  *no_add_attrs = true;
	}
      else
	DECL_UNINLINABLE (*node) = 1;
    }
  else
    {
      warning (OPT_Wattributes, "%qE attribute ignored", name);
      *no_add_attrs = true;
    }

  return NULL_TREE;
}

/* Handle a "noclone" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_noclone_attribute (tree *node, tree name,
			  tree ARG_UNUSED (args),
			  int ARG_UNUSED (flags), bool *no_add_attrs)
{
  if (TREE_CODE (*node) != FUNCTION_DECL)
    {
      warning (OPT_Wattributes, "%qE attribute ignored", name);
      *no_add_attrs = true;
    }

  return NULL_TREE;
}

/* Handle a "nocf_check" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_nocf_check_attribute (tree *node, tree name,
			  tree ARG_UNUSED (args),
			  int ARG_UNUSED (flags), bool *no_add_attrs)
{
  if (TREE_CODE (*node) != FUNCTION_TYPE
      && TREE_CODE (*node) != METHOD_TYPE)
    {
      warning (OPT_Wattributes, "%qE attribute ignored", name);
      *no_add_attrs = true;
    }
  else if (!(flag_cf_protection & CF_BRANCH))
    {
      warning (OPT_Wattributes, "%qE attribute ignored. Use "
				"%<-fcf-protection%> option to enable it",
				name);
      *no_add_attrs = true;
    }

  return NULL_TREE;
}

/* Handle a "no_icf" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_noicf_attribute (tree *node, tree name,
			tree ARG_UNUSED (args),
			int ARG_UNUSED (flags), bool *no_add_attrs)
{
  if (TREE_CODE (*node) != FUNCTION_DECL)
    {
      warning (OPT_Wattributes, "%qE attribute ignored", name);
      *no_add_attrs = true;
    }

  return NULL_TREE;
}


/* Handle a "always_inline" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_always_inline_attribute (tree *node, tree name,
				tree ARG_UNUSED (args),
				int ARG_UNUSED (flags),
				bool *no_add_attrs)
{
  if (TREE_CODE (*node) == FUNCTION_DECL)
    {
      if (lookup_attribute ("noinline", DECL_ATTRIBUTES (*node)))
	{
	  warning (OPT_Wattributes, "%qE attribute ignored due to conflict "
		   "with %qs attribute", name, "noinline");
	  *no_add_attrs = true;
	}
      else if (lookup_attribute ("target_clones", DECL_ATTRIBUTES (*node)))
	{
	  warning (OPT_Wattributes, "%qE attribute ignored due to conflict "
		   "with %qs attribute", name, "target_clones");
	  *no_add_attrs = true;
	}
      else
	/* Set the attribute and mark it for disregarding inline
	   limits.  */
	DECL_DISREGARD_INLINE_LIMITS (*node) = 1;
    }
  else
    {
      warning (OPT_Wattributes, "%qE attribute ignored", name);
      *no_add_attrs = true;
    }

  return NULL_TREE;
}

/* Handle a "gnu_inline" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_gnu_inline_attribute (tree *node, tree name,
			     tree ARG_UNUSED (args),
			     int ARG_UNUSED (flags),
			     bool *no_add_attrs)
{
  if (TREE_CODE (*node) == FUNCTION_DECL && DECL_DECLARED_INLINE_P (*node))
    {
      /* Do nothing else, just set the attribute.  We'll get at
	 it later with lookup_attribute.  */
    }
  else
    {
      warning (OPT_Wattributes, "%qE attribute ignored", name);
      *no_add_attrs = true;
    }

  return NULL_TREE;
}

/* Handle a "leaf" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_leaf_attribute (tree *node, tree name,
		       tree ARG_UNUSED (args),
		       int ARG_UNUSED (flags), bool *no_add_attrs)
{
  if (TREE_CODE (*node) != FUNCTION_DECL)
    {
      warning (OPT_Wattributes, "%qE attribute ignored", name);
      *no_add_attrs = true;
    }
  if (!TREE_PUBLIC (*node))
    {
      warning (OPT_Wattributes, "%qE attribute has no effect on unit local "
	       "functions", name);
      *no_add_attrs = true;
    }

  return NULL_TREE;
}

/* Handle an "artificial" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_artificial_attribute (tree *node, tree name,
			     tree ARG_UNUSED (args),
			     int ARG_UNUSED (flags),
			     bool *no_add_attrs)
{
  if (TREE_CODE (*node) == FUNCTION_DECL && DECL_DECLARED_INLINE_P (*node))
    {
      /* Do nothing else, just set the attribute.  We'll get at
	 it later with lookup_attribute.  */
    }
  else
    {
      warning (OPT_Wattributes, "%qE attribute ignored", name);
      *no_add_attrs = true;
    }

  return NULL_TREE;
}

/* Handle a "flatten" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_flatten_attribute (tree *node, tree name,
			  tree args ATTRIBUTE_UNUSED,
			  int flags ATTRIBUTE_UNUSED, bool *no_add_attrs)
{
  if (TREE_CODE (*node) == FUNCTION_DECL)
    /* Do nothing else, just set the attribute.  We'll get at
       it later with lookup_attribute.  */
    ;
  else
    {
      warning (OPT_Wattributes, "%qE attribute ignored", name);
      *no_add_attrs = true;
    }

  return NULL_TREE;
}

/* Handle a "warning" or "error" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_error_attribute (tree *node, tree name, tree args,
			int ARG_UNUSED (flags), bool *no_add_attrs)
{
  if (TREE_CODE (*node) == FUNCTION_DECL
      && TREE_CODE (TREE_VALUE (args)) == STRING_CST)
    /* Do nothing else, just set the attribute.  We'll get at
       it later with lookup_attribute.  */
    ;
  else
    {
      warning (OPT_Wattributes, "%qE attribute ignored", name);
      *no_add_attrs = true;
    }

  return NULL_TREE;
}

/* Handle a "used" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_used_attribute (tree *pnode, tree name, tree ARG_UNUSED (args),
		       int ARG_UNUSED (flags), bool *no_add_attrs)
{
  tree node = *pnode;

  if (TREE_CODE (node) == FUNCTION_DECL
      || (VAR_P (node) && TREE_STATIC (node))
      || (TREE_CODE (node) == TYPE_DECL))
    {
      TREE_USED (node) = 1;
      DECL_PRESERVE_P (node) = 1;
      if (VAR_P (node))
	DECL_READ_P (node) = 1;
    }
  else
    {
      warning (OPT_Wattributes, "%qE attribute ignored", name);
      *no_add_attrs = true;
    }

  return NULL_TREE;
}

/* Handle a "unused" attribute; arguments as in
   struct attribute_spec.handler.  */

tree
handle_unused_attribute (tree *node, tree name, tree ARG_UNUSED (args),
			 int flags, bool *no_add_attrs)
{
  if (DECL_P (*node))
    {
      tree decl = *node;

      if (TREE_CODE (decl) == PARM_DECL
	  || VAR_OR_FUNCTION_DECL_P (decl)
	  || TREE_CODE (decl) == LABEL_DECL
	  || TREE_CODE (decl) == CONST_DECL
	  || TREE_CODE (decl) == TYPE_DECL)
	{
	  TREE_USED (decl) = 1;
	  if (VAR_P (decl) || TREE_CODE (decl) == PARM_DECL)
	    DECL_READ_P (decl) = 1;
	}
      else
	{
	  warning (OPT_Wattributes, "%qE attribute ignored", name);
	  *no_add_attrs = true;
	}
    }
  else
    {
      if (!(flags & (int) ATTR_FLAG_TYPE_IN_PLACE))
	*node = build_variant_type_copy (*node);
      TREE_USED (*node) = 1;
    }

  return NULL_TREE;
}

/* Handle a "externally_visible" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_externally_visible_attribute (tree *pnode, tree name,
				     tree ARG_UNUSED (args),
				     int ARG_UNUSED (flags),
				     bool *no_add_attrs)
{
  tree node = *pnode;

  if (VAR_OR_FUNCTION_DECL_P (node))
    {
      if ((!TREE_STATIC (node) && TREE_CODE (node) != FUNCTION_DECL
	   && !DECL_EXTERNAL (node)) || !TREE_PUBLIC (node))
	{
	  warning (OPT_Wattributes,
		   "%qE attribute have effect only on public objects", name);
	  *no_add_attrs = true;
	}
    }
  else
    {
      warning (OPT_Wattributes, "%qE attribute ignored", name);
      *no_add_attrs = true;
    }

  return NULL_TREE;
}

/* Handle the "no_reorder" attribute.  Arguments as in
   struct attribute_spec.handler.  */

static tree
handle_no_reorder_attribute (tree *pnode,
			     tree name,
			     tree,
			     int,
			     bool *no_add_attrs)
{
  tree node = *pnode;

  if (!VAR_OR_FUNCTION_DECL_P (node)
	&& !(TREE_STATIC (node) || DECL_EXTERNAL (node)))
    {
      warning (OPT_Wattributes,
		"%qE attribute only affects top level objects",
		name);
      *no_add_attrs = true;
    }

  return NULL_TREE;
}

/* Handle a "const" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_const_attribute (tree *node, tree name, tree ARG_UNUSED (args),
			int flags, 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_qualified_type
	 (build_pointer_type
	  (build_type_variant (TREE_TYPE (type), 1,
			       TREE_THIS_VOLATILE (TREE_TYPE (type)))),
	  TYPE_QUALS (type)));
  else
    {
      warning (OPT_Wattributes, "%qE attribute ignored", name);
      *no_add_attrs = true;
    }

  /* void __builtin_unreachable(void) is const.  Accept other such
     built-ins but warn on user-defined functions that return void.  */
  if (!(flags & ATTR_FLAG_BUILT_IN)
      && TREE_CODE (*node) == FUNCTION_DECL
      && VOID_TYPE_P (TREE_TYPE (type)))
    warning (OPT_Wattributes, "%qE attribute on function "
	     "returning %<void%>", name);

  return NULL_TREE;
}

/* Handle a "scalar_storage_order" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_scalar_storage_order_attribute (tree *node, tree name, tree args,
				       int flags, bool *no_add_attrs)
{
  tree id = TREE_VALUE (args);
  tree type;

  if (TREE_CODE (*node) == TYPE_DECL
      && ! (flags & ATTR_FLAG_CXX11))
    node = &TREE_TYPE (*node);
  type = *node;

  if (BYTES_BIG_ENDIAN != WORDS_BIG_ENDIAN)
    {
      error ("%qE attribute is not supported because endianness is not uniform",
	     name);
      return NULL_TREE;
    }

  if (RECORD_OR_UNION_TYPE_P (type) && !c_dialect_cxx ())
    {
      bool reverse = false;

      if (TREE_CODE (id) == STRING_CST
	  && strcmp (TREE_STRING_POINTER (id), "big-endian") == 0)
	reverse = !BYTES_BIG_ENDIAN;
      else if (TREE_CODE (id) == STRING_CST
	       && strcmp (TREE_STRING_POINTER (id), "little-endian") == 0)
	reverse = BYTES_BIG_ENDIAN;
      else
	{
	  error ("attribute %qE argument must be one of %qs or %qs",
		 name, "big-endian", "little-endian");
	  return NULL_TREE;
	}

      if (!(flags & (int) ATTR_FLAG_TYPE_IN_PLACE))
	{
	  if (reverse)
	    /* A type variant isn't good enough, since we don't want a cast
	       to such a type to be removed as a no-op.  */
	    *node = type = build_duplicate_type (type);
	}

      TYPE_REVERSE_STORAGE_ORDER (type) = reverse;
      return NULL_TREE;
    }

  warning (OPT_Wattributes, "%qE attribute ignored", 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 (tree *node, tree name,
				    tree ARG_UNUSED (args), int flags,
				    bool *no_add_attrs)
{
  tree type;

  *no_add_attrs = true;

  if (TREE_CODE (*node) == TYPE_DECL
      && ! (flags & ATTR_FLAG_CXX11))
    node = &TREE_TYPE (*node);
  type = *node;

  if (TREE_CODE (type) == UNION_TYPE)
    {
      /* Make sure that the first field will work for a transparent union.
	 If the type isn't complete yet, leave the check to the code in
	 finish_struct.  */
      if (TYPE_SIZE (type))
	{
	  tree first = first_field (type);
	  if (first == NULL_TREE
	      || DECL_ARTIFICIAL (first)
	      || TYPE_MODE (type) != DECL_MODE (first))
	    goto ignored;
	}

      if (!(flags & (int) ATTR_FLAG_TYPE_IN_PLACE))
	{
	  /* If the type isn't complete yet, setting the flag
	     on a variant wouldn't ever be checked.  */
	  if (!TYPE_SIZE (type))
	    goto ignored;

	  /* build_duplicate_type doesn't work for C++.  */
	  if (c_dialect_cxx ())
	    goto ignored;

	  /* A type variant isn't good enough, since we don't want a cast
	     to such a type to be removed as a no-op.  */
	  *node = type = build_duplicate_type (type);
	}

      for (tree t = TYPE_MAIN_VARIANT (type); t; t = TYPE_NEXT_VARIANT (t))
	TYPE_TRANSPARENT_AGGR (t) = 1;
      return NULL_TREE;
    }

 ignored:
  warning (OPT_Wattributes, "%qE attribute ignored", name);
  return NULL_TREE;
}

/* Subroutine of handle_{con,de}structor_attribute.  Evaluate ARGS to
   get the requested priority for a constructor or destructor,
   possibly issuing diagnostics for invalid or reserved
   priorities.  */

static priority_type
get_priority (tree args, bool is_destructor)
{
  HOST_WIDE_INT pri;
  tree arg;

  if (!args)
    return DEFAULT_INIT_PRIORITY;

  if (!SUPPORTS_INIT_PRIORITY)
    {
      if (is_destructor)
	error ("destructor priorities are not supported");
      else
	error ("constructor priorities are not supported");
      return DEFAULT_INIT_PRIORITY;
    }

  arg = TREE_VALUE (args);
  if (TREE_CODE (arg) == IDENTIFIER_NODE)
    goto invalid;
  if (arg == error_mark_node)
    return DEFAULT_INIT_PRIORITY;
  arg = default_conversion (arg);
  if (!tree_fits_shwi_p (arg)
      || !INTEGRAL_TYPE_P (TREE_TYPE (arg)))
    goto invalid;

  pri = tree_to_shwi (arg);
  if (pri < 0 || pri > MAX_INIT_PRIORITY)
    goto invalid;

  if (pri <= MAX_RESERVED_INIT_PRIORITY)
    {
      if (is_destructor)
	warning (OPT_Wprio_ctor_dtor,
		 "destructor priorities from 0 to %d are reserved "
		 "for the implementation",
		 MAX_RESERVED_INIT_PRIORITY);
      else
	warning (OPT_Wprio_ctor_dtor,
		 "constructor priorities from 0 to %d are reserved "
		 "for the implementation",
		 MAX_RESERVED_INIT_PRIORITY);
    }
  return pri;

 invalid:
  if (is_destructor)
    error ("destructor priorities must be integers from 0 to %d inclusive",
	   MAX_INIT_PRIORITY);
  else
    error ("constructor priorities must be integers from 0 to %d inclusive",
	   MAX_INIT_PRIORITY);
  return DEFAULT_INIT_PRIORITY;
}

/* Handle a "constructor" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_constructor_attribute (tree *node, tree name, tree args,
			      int ARG_UNUSED (flags),
			      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)
    {
      priority_type priority;
      DECL_STATIC_CONSTRUCTOR (decl) = 1;
      priority = get_priority (args, /*is_destructor=*/false);
      SET_DECL_INIT_PRIORITY (decl, priority);
      TREE_USED (decl) = 1;
    }
  else
    {
      warning (OPT_Wattributes, "%qE attribute ignored", name);
      *no_add_attrs = true;
    }

  return NULL_TREE;
}

/* Handle a "destructor" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_destructor_attribute (tree *node, tree name, tree args,
			     int ARG_UNUSED (flags),
			     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)
    {
      priority_type priority;
      DECL_STATIC_DESTRUCTOR (decl) = 1;
      priority = get_priority (args, /*is_destructor=*/true);
      SET_DECL_FINI_PRIORITY (decl, priority);
      TREE_USED (decl) = 1;
    }
  else
    {
      warning (OPT_Wattributes, "%qE attribute ignored", name);
      *no_add_attrs = true;
    }

  return NULL_TREE;
}

/* Nonzero if the mode is a valid vector mode for this architecture.
   This returns nonzero even if there is no hardware support for the
   vector mode, but we can emulate with narrower modes.  */

static bool
vector_mode_valid_p (machine_mode mode)
{
  enum mode_class mclass = GET_MODE_CLASS (mode);

  /* Doh!  What's going on?  */
  if (mclass != MODE_VECTOR_INT
      && mclass != MODE_VECTOR_FLOAT
      && mclass != MODE_VECTOR_FRACT
      && mclass != MODE_VECTOR_UFRACT
      && mclass != MODE_VECTOR_ACCUM
      && mclass != MODE_VECTOR_UACCUM)
    return false;

  /* Hardware support.  Woo hoo!  */
  if (targetm.vector_mode_supported_p (mode))
    return true;

  /* We should probably return 1 if requesting V4DI and we have no DI,
     but we have V2DI, but this is probably very unlikely.  */

  /* If we have support for the inner mode, we can safely emulate it.
     We may not have V2DI, but me can emulate with a pair of DIs.  */
  return targetm.scalar_mode_supported_p (GET_MODE_INNER (mode));
}


/* Handle a "mode" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_mode_attribute (tree *node, tree name, tree args,
		       int ARG_UNUSED (flags), bool *no_add_attrs)
{
  tree type = *node;
  tree ident = TREE_VALUE (args);

  *no_add_attrs = true;

  if (TREE_CODE (ident) != IDENTIFIER_NODE)
    warning (OPT_Wattributes, "%qE attribute ignored", name);
  else
    {
      int j;
      const char *p = IDENTIFIER_POINTER (ident);
      int len = strlen (p);
      machine_mode mode = VOIDmode;
      tree typefm;
      bool valid_mode;

      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 if (!strcmp (p, "libgcc_cmp_return"))
	mode = targetm.libgcc_cmp_return_mode ();
      else if (!strcmp (p, "libgcc_shift_count"))
	mode = targetm.libgcc_shift_count_mode ();
      else if (!strcmp (p, "unwind_word"))
	mode = targetm.unwind_word_mode ();
      else
	for (j = 0; j < NUM_MACHINE_MODES; j++)
	  if (!strcmp (p, GET_MODE_NAME (j)))
	    {
	      mode = (machine_mode) j;
	      break;
	    }

      if (mode == VOIDmode)
	{
	  error ("unknown machine mode %qE", ident);
	  return NULL_TREE;
	}

      /* Allow the target a chance to translate MODE into something supported.
	 See PR86324.  */
      mode = targetm.translate_mode_attribute (mode);

      valid_mode = false;
      switch (GET_MODE_CLASS (mode))
	{
	case MODE_INT:
	case MODE_PARTIAL_INT:
	case MODE_FLOAT:
	case MODE_DECIMAL_FLOAT:
	case MODE_FRACT:
	case MODE_UFRACT:
	case MODE_ACCUM:
	case MODE_UACCUM:
	  valid_mode
	    = targetm.scalar_mode_supported_p (as_a <scalar_mode> (mode));
	  break;

	case MODE_COMPLEX_INT:
	case MODE_COMPLEX_FLOAT:
	  valid_mode = targetm.scalar_mode_supported_p (GET_MODE_INNER (mode));
	  break;

	case MODE_VECTOR_INT:
	case MODE_VECTOR_FLOAT:
	case MODE_VECTOR_FRACT:
	case MODE_VECTOR_UFRACT:
	case MODE_VECTOR_ACCUM:
	case MODE_VECTOR_UACCUM:
	  warning (OPT_Wattributes, "specifying vector types with "
		   "%<__attribute__ ((mode))%> is deprecated");
	  inform (input_location,
		  "use %<__attribute__ ((vector_size))%> instead");
	  valid_mode = vector_mode_valid_p (mode);
	  break;

	default:
	  break;
	}
      if (!valid_mode)
	{
	  error ("unable to emulate %qs", p);
	  return NULL_TREE;
	}

      if (POINTER_TYPE_P (type))
	{
	  scalar_int_mode addr_mode;
	  addr_space_t as = TYPE_ADDR_SPACE (TREE_TYPE (type));
	  tree (*fn)(tree, machine_mode, bool);

	  if (!is_a <scalar_int_mode> (mode, &addr_mode)
	      || !targetm.addr_space.valid_pointer_mode (addr_mode, as))
	    {
	      error ("invalid pointer mode %qs", p);
	      return NULL_TREE;
	    }

	  if (TREE_CODE (type) == POINTER_TYPE)
	    fn = build_pointer_type_for_mode;
	  else
	    fn = build_reference_type_for_mode;
	  typefm = fn (TREE_TYPE (type), addr_mode, false);
	}
      else
	{
	  /* For fixed-point modes, we need to test if the signness of type
	     and the machine mode are consistent.  */
	  if (ALL_FIXED_POINT_MODE_P (mode)
	      && TYPE_UNSIGNED (type) != UNSIGNED_FIXED_POINT_MODE_P (mode))
	    {
	      error ("signedness of type and machine mode %qs don%'t match", p);
	      return NULL_TREE;
	    }
	  /* For fixed-point modes, we need to pass saturating info.  */
	  typefm = lang_hooks.types.type_for_mode (mode,
			ALL_FIXED_POINT_MODE_P (mode) ? TYPE_SATURATING (type)
						      : TYPE_UNSIGNED (type));
	}

      if (typefm == NULL_TREE)
	{
	  error ("no data type for mode %qs", p);
	  return NULL_TREE;
	}
      else if (TREE_CODE (type) == ENUMERAL_TYPE)
	{
	  /* For enumeral types, copy the precision from the integer
	     type returned above.  If not an INTEGER_TYPE, we can't use
	     this mode for this type.  */
	  if (TREE_CODE (typefm) != INTEGER_TYPE)
	    {
	      error ("cannot use mode %qs for enumerated types", p);
	      return NULL_TREE;
	    }

	  if (flags & ATTR_FLAG_TYPE_IN_PLACE)
	    {
	      TYPE_PRECISION (type) = TYPE_PRECISION (typefm);
	      typefm = type;
	    }
	  else
	    {
	      /* We cannot build a type variant, as there's code that assumes
		 that TYPE_MAIN_VARIANT has the same mode.  This includes the
		 debug generators.  Instead, create a subrange type.  This
		 results in all of the enumeral values being emitted only once
		 in the original, and the subtype gets them by reference.  */
	      if (TYPE_UNSIGNED (type))
		typefm = make_unsigned_type (TYPE_PRECISION (typefm));
	      else
		typefm = make_signed_type (TYPE_PRECISION (typefm));
	      TREE_TYPE (typefm) = type;
	    }
	  *no_add_attrs = false;
	}
      else if (VECTOR_MODE_P (mode)
	       ? TREE_CODE (type) != TREE_CODE (TREE_TYPE (typefm))
	       : TREE_CODE (type) != TREE_CODE (typefm))
	{
	  error ("mode %qs applied to inappropriate type", p);
	  return NULL_TREE;
	}

      *node = build_qualified_type (typefm, TYPE_QUALS (type));
    }

  return NULL_TREE;
}

/* Handle a "section" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_section_attribute (tree *node, tree ARG_UNUSED (name), tree args,
			  int ARG_UNUSED (flags), bool *no_add_attrs)
{
  tree decl = *node;
  tree res = NULL_TREE;

  if (!targetm_common.have_named_sections)
    {
      error_at (DECL_SOURCE_LOCATION (*node),
		"section attributes are not supported for this target");
      goto fail;
    }

  if (!VAR_OR_FUNCTION_DECL_P (decl))
    {
      error ("section attribute not allowed for %q+D", *node);
      goto fail;
    }

  if (TREE_CODE (TREE_VALUE (args)) != STRING_CST)
    {
      error ("section attribute argument not a string constant");
      goto fail;
    }

  if (VAR_P (decl)
      && current_function_decl != NULL_TREE
      && !TREE_STATIC (decl))
    {
      error_at (DECL_SOURCE_LOCATION (decl),
		"section attribute cannot be specified for local variables");
      goto fail;
    }

  /* The decl may have already been given a section attribute
     from a previous declaration.  Ensure they match.  */
  if (DECL_SECTION_NAME (decl) != NULL
      && strcmp (DECL_SECTION_NAME (decl),
		 TREE_STRING_POINTER (TREE_VALUE (args))) != 0)
    {
      error ("section of %q+D conflicts with previous declaration", *node);
      goto fail;
    }

  if (VAR_P (decl)
      && !targetm.have_tls && targetm.emutls.tmpl_section
      && DECL_THREAD_LOCAL_P (decl))
    {
      error ("section of %q+D cannot be overridden", *node);
      goto fail;
    }

  res = targetm.handle_generic_attribute (node, name, args, flags,
					  no_add_attrs);

  /* If the back end confirms the attribute can be added then continue onto
     final processing.  */
  if (!(*no_add_attrs))
    {
      set_decl_section_name (decl, TREE_STRING_POINTER (TREE_VALUE (args)));
      return res;
    }

fail:
  *no_add_attrs = true;
  return res;
}

/* Common codes shared by handle_warn_if_not_aligned_attribute and
   handle_aligned_attribute.  */

static tree
common_handle_aligned_attribute (tree *node, tree name, tree args, int flags,
				 bool *no_add_attrs,
				 bool warn_if_not_aligned_p)
{
  tree decl = NULL_TREE;
  tree *type = NULL;
  bool is_type = false;
  tree align_expr;

  /* The last (already pushed) declaration with all validated attributes
     merged in or the current about-to-be-pushed one if one hasn't been
     yet.  */
  tree last_decl = node[1] ? node[1] : *node;

  if (args)
    {
      align_expr = TREE_VALUE (args);
      if (align_expr && TREE_CODE (align_expr) != IDENTIFIER_NODE
	  && TREE_CODE (align_expr) != FUNCTION_DECL)
	align_expr = default_conversion (align_expr);
    }
  else
    align_expr = size_int (ATTRIBUTE_ALIGNED_VALUE / BITS_PER_UNIT);

  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 = true;

  /* True to consider invalid alignments greater than MAX_OFILE_ALIGNMENT.  */
  bool objfile = (TREE_CODE (*node) == FUNCTION_DECL
		  || (VAR_P (*node) && TREE_STATIC (*node)));
  /* Log2 of specified alignment.  */
  int pow2align = check_user_alignment (align_expr, objfile,
					/* warn_zero = */ true);
  if (pow2align == -1)
    {
      *no_add_attrs = true;
      return NULL_TREE;
    }

  /* The alignment in bits corresponding to the specified alignment.  */
  unsigned bitalign = (1U << pow2align) * BITS_PER_UNIT;

  /* The alignment of the current declaration and that of the last
     pushed declaration, determined on demand below.  */
  unsigned curalign = 0;
  unsigned lastalign = 0;

  /* True when SET_DECL_ALIGN() should be called for the decl when
     *NO_ADD_ATTRS is false.  */
  bool set_align = true;
  if (is_type)
    {
      if ((flags & (int) ATTR_FLAG_TYPE_IN_PLACE))
	/* OK, modify the type in place.  */;
      /* If we have a TYPE_DECL, then copy the type, so that we
	 don't accidentally modify a builtin type.  See pushdecl.  */
      else if (decl && TREE_TYPE (decl) != error_mark_node
	       && DECL_ORIGINAL_TYPE (decl) == NULL_TREE)
	{
	  tree tt = TREE_TYPE (decl);
	  *type = build_variant_type_copy (*type);
	  DECL_ORIGINAL_TYPE (decl) = tt;
	  TYPE_NAME (*type) = decl;
	  TREE_USED (*type) = TREE_USED (decl);
	  TREE_TYPE (decl) = *type;
	}
      else
	*type = build_variant_type_copy (*type);

      if (warn_if_not_aligned_p)
	{
	  SET_TYPE_WARN_IF_NOT_ALIGN (*type, bitalign);
	  warn_if_not_aligned_p = false;
	}
      else
	{
	  SET_TYPE_ALIGN (*type, bitalign);
	  TYPE_USER_ALIGN (*type) = 1;
	}
    }
  else if (! VAR_OR_FUNCTION_DECL_P (decl)
	   && TREE_CODE (decl) != FIELD_DECL)
    {
      error ("alignment may not be specified for %q+D", decl);
      *no_add_attrs = true;
    }
  else if (TREE_CODE (decl) == FUNCTION_DECL
	   && ((curalign = DECL_ALIGN (decl)) > bitalign
	       || ((lastalign = DECL_ALIGN (last_decl)) > bitalign)))
    {
      /* Either a prior attribute on the same declaration or one
	 on a prior declaration of the same function specifies
	 stricter alignment than this attribute.  */
      bool note = lastalign != 0;
      if (lastalign)
	curalign = lastalign;

      curalign /= BITS_PER_UNIT;
      unsigned newalign = bitalign / BITS_PER_UNIT;

      auto_diagnostic_group d;
      if ((DECL_USER_ALIGN (decl)
	   || DECL_USER_ALIGN (last_decl)))
	{
	  if (warning (OPT_Wattributes,
		       "ignoring attribute %<%E (%u)%> because it conflicts "
		       "with attribute %<%E (%u)%>",
		       name, newalign, name, curalign)
	      && note)
	    inform (DECL_SOURCE_LOCATION (last_decl),
		    "previous declaration here");
	  /* Only reject attempts to relax/override an alignment
	     explicitly specified previously and accept declarations
	     that appear to relax the implicit function alignment for
	     the target.  Both increasing and increasing the alignment
	     set by -falign-functions setting is permitted.  */
	  *no_add_attrs = true;
	}
      else if (!warn_if_not_aligned_p)
	{
	  /* Do not fail for attribute warn_if_not_aligned.  Otherwise,
	     silently avoid applying the alignment to the declaration
	     because it's implicitly satisfied by the target.  Apply
	     the attribute nevertheless so it can be retrieved by
	     __builtin_has_attribute.  */
	  set_align = false;
	}
    }
  else if (DECL_USER_ALIGN (decl)
	   && DECL_ALIGN (decl) > bitalign)
    /* C++-11 [dcl.align/4]:

	   When multiple alignment-specifiers are specified for an
	   entity, the alignment requirement shall be set to the
	   strictest specified alignment.

      This formally comes from the c++11 specification but we are
      doing it for the GNU attribute syntax as well.  */
    *no_add_attrs = true;
  else if (!warn_if_not_aligned_p
	   && TREE_CODE (decl) == FUNCTION_DECL
	   && DECL_ALIGN (decl) > bitalign)
    {
      /* Don't warn for function alignment here if warn_if_not_aligned_p
	 is true.  It will be warned about later.  */
      if (DECL_USER_ALIGN (decl))
	{
	  /* Only reject attempts to relax/override an alignment
	     explicitly specified previously and accept declarations
	     that appear to relax the implicit function alignment for
	     the target.  Both increasing and increasing the alignment
	     set by -falign-functions setting is permitted.  */
	  error ("alignment for %q+D was previously specified as %d "
		 "and may not be decreased", decl,
		 DECL_ALIGN (decl) / BITS_PER_UNIT);
	  *no_add_attrs = true;
	}
    }
  else if (warn_if_not_aligned_p
	   && TREE_CODE (decl) == FIELD_DECL
	   && !DECL_C_BIT_FIELD (decl))
    {
      SET_DECL_WARN_IF_NOT_ALIGN (decl, bitalign);
      warn_if_not_aligned_p = false;
      set_align = false;
    }

  if (warn_if_not_aligned_p)
    {
      error ("%<warn_if_not_aligned%> may not be specified for %q+D",
	     decl);
      *no_add_attrs = true;
    }
  else if (!is_type && !*no_add_attrs && set_align)
    {
      SET_DECL_ALIGN (decl, bitalign);
      DECL_USER_ALIGN (decl) = 1;
    }

  return NULL_TREE;
}

/* Handle a "aligned" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_aligned_attribute (tree *node, tree name, tree args,
			  int flags, bool *no_add_attrs)
{
  return common_handle_aligned_attribute (node, name, args, flags,
					 no_add_attrs, false);
}

/* Handle a "warn_if_not_aligned" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_warn_if_not_aligned_attribute (tree *node, tree name,
				      tree args, int flags,
				      bool *no_add_attrs)
{
  return common_handle_aligned_attribute (node, name, args, flags,
					  no_add_attrs, true);
}

/* Handle a "weak" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_weak_attribute (tree *node, tree name,
		       tree ARG_UNUSED (args),
		       int ARG_UNUSED (flags),
		       bool * ARG_UNUSED (no_add_attrs))
{
  if (TREE_CODE (*node) == FUNCTION_DECL
      && DECL_DECLARED_INLINE_P (*node))
    {
      warning (OPT_Wattributes, "inline function %q+D declared weak", *node);
      *no_add_attrs = true;
    }
  else if (lookup_attribute ("ifunc", DECL_ATTRIBUTES (*node)))
    {
      error ("indirect function %q+D cannot be declared weak", *node);
      *no_add_attrs = true;
      return NULL_TREE;
    }
  else if (VAR_OR_FUNCTION_DECL_P (*node))
    declare_weak (*node);
  else
    warning (OPT_Wattributes, "%qE attribute ignored", name);

  return NULL_TREE;
}

/* Handle a "noinit" attribute; arguments as in struct
   attribute_spec.handler.  Check whether the attribute is allowed
   here and add the attribute to the variable decl tree or otherwise
   issue a diagnostic.  This function checks NODE is of the expected
   type and issues diagnostics otherwise using NAME.  If it is not of
   the expected type *NO_ADD_ATTRS will be set to true.  */

static tree
handle_noinit_attribute (tree * node,
		  tree   name,
		  tree   args,
		  int    flags ATTRIBUTE_UNUSED,
		  bool *no_add_attrs)
{
  const char *message = NULL;
  tree res = NULL_TREE;

  gcc_assert (DECL_P (*node));
  gcc_assert (args == NULL);

  if (TREE_CODE (*node) != VAR_DECL)
    message = G_("%qE attribute only applies to variables");

  /* Check that it's possible for the variable to have a section.  */
  else if ((TREE_STATIC (*node) || DECL_EXTERNAL (*node) || in_lto_p)
	   && DECL_SECTION_NAME (*node))
    message = G_("%qE attribute cannot be applied to variables "
		 "with specific sections");

  else if (!targetm.have_switchable_bss_sections)
    message = G_("%qE attribute is specific to ELF targets");

  if (message)
    {
      warning (OPT_Wattributes, message, name);
      *no_add_attrs = true;
    }
  else
    {
      res = targetm.handle_generic_attribute (node, name, args, flags,
					      no_add_attrs);
      /* If the back end confirms the attribute can be added then continue onto
	 final processing.  */
      if (!(*no_add_attrs))
	{
	  /* If this var is thought to be common, then change this.  Common
	     variables are assigned to sections before the backend has a
	     chance to process them.  Do this only if the attribute is
	     valid.  */
	  if (DECL_COMMON (*node))
	    DECL_COMMON (*node) = 0;
	}
    }

  return res;
}


/* Handle a "noplt" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_noplt_attribute (tree *node, tree name,
		       tree ARG_UNUSED (args),
		       int ARG_UNUSED (flags),
		       bool * ARG_UNUSED (no_add_attrs))
{
  if (TREE_CODE (*node) != FUNCTION_DECL)
    {
      warning (OPT_Wattributes,
	       "%qE attribute is only applicable on functions", name);
      *no_add_attrs = true;
      return NULL_TREE;
    }
  return NULL_TREE;
}

/* Handle a "symver" attribute.  */

static tree
handle_symver_attribute (tree *node, tree ARG_UNUSED (name), tree args,
			 int ARG_UNUSED (flags), bool *no_add_attrs)
{
  tree symver;
  const char *symver_str;

  if (TREE_CODE (*node) != FUNCTION_DECL && TREE_CODE (*node) != VAR_DECL)
    {
      warning (OPT_Wattributes,
	       "%<symver%> attribute only applies to functions and variables");
      *no_add_attrs = true;
      return NULL_TREE;
    }

  if (!decl_in_symtab_p (*node))
    {
      warning (OPT_Wattributes,
	       "%<symver%> attribute is only applicable to symbols");
      *no_add_attrs = true;
      return NULL_TREE;
    }

  for (; args; args = TREE_CHAIN (args))
    {
      symver = TREE_VALUE (args);
      if (TREE_CODE (symver) != STRING_CST)
	{
	  error ("%<symver%> attribute argument not a string constant");
	  *no_add_attrs = true;
	  return NULL_TREE;
	}

      symver_str = TREE_STRING_POINTER (symver);

      int ats = 0;
      for (int n = 0; (int)n < TREE_STRING_LENGTH (symver); n++)
	if (symver_str[n] == '@')
	  ats++;

      if (ats != 1 && ats != 2)
	{
	  error ("symver attribute argument must have format %<name@nodename%>");
	  error ("%<symver%> attribute argument %qs must contain one or two "
		 "%<@%>", symver_str);
	  *no_add_attrs = true;
	  return NULL_TREE;
	}
    }

  return NULL_TREE;
}


/* Handle an "alias" or "ifunc" attribute; arguments as in
   struct attribute_spec.handler, except that IS_ALIAS tells us
   whether this is an alias as opposed to ifunc attribute.  */

static tree
handle_alias_ifunc_attribute (bool is_alias, tree *node, tree name, tree args,
			      bool *no_add_attrs)
{
  tree decl = *node;

  if (TREE_CODE (decl) != FUNCTION_DECL
      && (!is_alias || !VAR_P (decl)))
    {
      warning (OPT_Wattributes, "%qE attribute ignored", name);
      *no_add_attrs = true;
    }
  else if ((TREE_CODE (decl) == FUNCTION_DECL && DECL_INITIAL (decl))
      || (TREE_CODE (decl) != FUNCTION_DECL
	  && TREE_PUBLIC (decl) && !DECL_EXTERNAL (decl))
      /* A static variable declaration is always a tentative definition,
	 but the alias is a non-tentative definition which overrides.  */
      || (TREE_CODE (decl) != FUNCTION_DECL
	  && ! TREE_PUBLIC (decl) && DECL_INITIAL (decl)))
    {
      error ("%q+D defined both normally and as %qE attribute", decl, name);
      *no_add_attrs = true;
      return NULL_TREE;
    }
  else if (!is_alias
	   && (lookup_attribute ("weak", DECL_ATTRIBUTES (decl))
	       || lookup_attribute ("weakref", DECL_ATTRIBUTES (decl))))
    {
      error ("weak %q+D cannot be defined %qE", decl, name);
      *no_add_attrs = true;
      return NULL_TREE;
    }

  /* Note that the very first time we process a nested declaration,
     decl_function_context will not be set.  Indeed, *would* never
     be set except for the DECL_INITIAL/DECL_EXTERNAL frobbery that
     we do below.  After such frobbery, pushdecl would set the context.
     In any case, this is never what we want.  */
  else if (decl_function_context (decl) == 0 && current_function_decl == NULL)
    {
      tree id;

      id = TREE_VALUE (args);
      if (TREE_CODE (id) != STRING_CST)
	{
	  error ("attribute %qE argument not a string", name);
	  *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
	TREE_STATIC (decl) = 1;

      if (!is_alias)
	{
	  /* ifuncs are also aliases, so set that attribute too.  */
	  DECL_ATTRIBUTES (decl)
	    = tree_cons (get_identifier ("alias"), args,
			 DECL_ATTRIBUTES (decl));
	  DECL_ATTRIBUTES (decl) = tree_cons (get_identifier ("ifunc"),
					      NULL, DECL_ATTRIBUTES (decl));
	}
    }
  else
    {
      warning (OPT_Wattributes, "%qE attribute ignored", name);
      *no_add_attrs = true;
    }

  if (decl_in_symtab_p (*node))
    {
      struct symtab_node *n = symtab_node::get (decl);
      if (n && n->refuse_visibility_changes)
	error ("%+qD declared %qs after being used",
	       decl, is_alias ? "alias" : "ifunc");
    }


  return NULL_TREE;
}

/* Handle an "alias" or "ifunc" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_ifunc_attribute (tree *node, tree name, tree args,
			int ARG_UNUSED (flags), bool *no_add_attrs)
{
  return handle_alias_ifunc_attribute (false, node, name, args, no_add_attrs);
}

/* Handle an "alias" or "ifunc" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_alias_attribute (tree *node, tree name, tree args,
			int ARG_UNUSED (flags), bool *no_add_attrs)
{
  return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs);
}

/* Handle the "copy" attribute NAME by copying the set of attributes
   from the symbol referenced by ARGS to the declaration of *NODE.  */

static tree
handle_copy_attribute (tree *node, tree name, tree args,
		       int flags, bool *no_add_attrs)
{
  /* Do not apply the copy attribute itself.  It serves no purpose
     other than to copy other attributes.  */
  *no_add_attrs = true;

  tree decl = *node;

  tree ref = TREE_VALUE (args);
  if (ref == error_mark_node)
    return NULL_TREE;

  if (TREE_CODE (ref) == STRING_CST)
    {
      /* Explicitly handle this case since using a string literal
	 as an argument is a likely mistake.  */
      error_at (DECL_SOURCE_LOCATION (decl),
		"%qE attribute argument cannot be a string",
		name);
      return NULL_TREE;
    }

  if (CONSTANT_CLASS_P (ref)
      && (INTEGRAL_TYPE_P (TREE_TYPE (ref))
	  || FLOAT_TYPE_P (TREE_TYPE (ref))))
    {
      /* Similar to the string case, since some function attributes
	 accept literal numbers as arguments (e.g., alloc_size or
	 nonnull) using one here is a likely mistake.  */
      error_at (DECL_SOURCE_LOCATION (decl),
		"%qE attribute argument cannot be a constant arithmetic "
		"expression",
		name);
      return NULL_TREE;
    }

  if (ref == node[1])
    {
      /* Another possible mistake (but indirect self-references aren't
	 and diagnosed and shouldn't be).  */
      if (warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wattributes,
		      "%qE attribute ignored on a redeclaration "
		      "of the referenced symbol",
		      name))
	inform (DECL_SOURCE_LOCATION (node[1]),
		"previous declaration here");
      return NULL_TREE;
    }

  /* Consider address-of expressions in the attribute argument
     as requests to copy from the referenced entity.  */
  if (TREE_CODE (ref) == ADDR_EXPR)
    ref = TREE_OPERAND (ref, 0);

  do
    {
      /* Drill down into references to find the referenced decl.  */
      tree_code refcode = TREE_CODE (ref);
      if (refcode == ARRAY_REF
	  || refcode == INDIRECT_REF)
	ref = TREE_OPERAND (ref, 0);
      else if (refcode == COMPONENT_REF)
	ref = TREE_OPERAND (ref, 1);
      else
	break;
    } while (!DECL_P (ref));

  /* For object pointer expressions, consider those to be requests
     to copy from their type, such as in:
       struct __attribute__ (copy ((struct T *)0)) U { ... };
     which copies type attributes from struct T to the declaration
     of struct U.  */
  if ((CONSTANT_CLASS_P (ref) || EXPR_P (ref))
      && POINTER_TYPE_P (TREE_TYPE (ref))
      && !FUNCTION_POINTER_TYPE_P (TREE_TYPE (ref)))
    ref = TREE_TYPE (ref);

  if (DECL_P (decl))
    {
      if ((VAR_P (decl)
	   && (TREE_CODE (ref) == FUNCTION_DECL
	       || (EXPR_P (ref)
		   && POINTER_TYPE_P (TREE_TYPE (ref))
		   && FUNC_OR_METHOD_TYPE_P (TREE_TYPE (TREE_TYPE (ref))))))
	  || (TREE_CODE (decl) == FUNCTION_DECL
	      && (VAR_P (ref)
		  || (EXPR_P (ref)
		      && !FUNC_OR_METHOD_TYPE_P (TREE_TYPE (ref))))))
	{
	  /* It makes no sense to try to copy function attributes
	     to a variable, or variable attributes to a function.  */
	  if (warning (OPT_Wattributes,
		       "%qE attribute ignored on a declaration of "
		       "a different kind than referenced symbol",
		       name)
	      && DECL_P (ref))
	    inform (DECL_SOURCE_LOCATION (ref),
		    "symbol %qD referenced by %qD declared here", ref, decl);
	  return NULL_TREE;
	}

      tree attrs = NULL_TREE;
      if (DECL_P (ref))
	attrs = DECL_ATTRIBUTES (ref);
      else if (TYPE_P (ref))
	attrs = TYPE_ATTRIBUTES (ref);

      /* Copy decl attributes from REF to DECL.  */
      for (tree at = attrs; at; at = TREE_CHAIN (at))
	{
	  /* Avoid copying attributes that affect a symbol linkage,
	     inlining, or visibility since those in all likelihood
	     only apply to the target.
	     FIXME: make it possible to specify which attributes to
	     copy or not to copy in the copy attribute itself.  */
	  tree atname = get_attribute_name (at);
	  if (is_attribute_p ("alias", atname)
	      || is_attribute_p ("always_inline", atname)
	      || is_attribute_p ("gnu_inline", atname)
	      || is_attribute_p ("ifunc", atname)
	      || is_attribute_p ("noinline", atname)
	      || is_attribute_p ("visibility", atname)
	      || is_attribute_p ("weak", atname)
	      || is_attribute_p ("weakref", atname)
	      || is_attribute_p ("target_clones", atname))
	    continue;

	  /* Attribute leaf only applies to extern functions.
	     Avoid copying it to static ones.  */
	  if (!TREE_PUBLIC (decl)
	      && is_attribute_p ("leaf", atname))
	    continue;

	  tree atargs = TREE_VALUE (at);
	  /* Create a copy of just the one attribute ar AT, including
	     its argumentsm and add it to DECL.  */
	  tree attr = tree_cons (atname, copy_list (atargs), NULL_TREE);
	  decl_attributes (node, attr, flags, ref);
	}

      /* Proceed to copy type attributes below.  */
    }
  else if (!TYPE_P (decl))
    {
      error_at (DECL_SOURCE_LOCATION (decl),
		"%qE attribute must apply to a declaration",
		name);
      return NULL_TREE;
    }

  /* A function declared with attribute nothrow has the attribute
     attached to it, but a C++ throw() function does not.  */
  if (TREE_NOTHROW (ref))
    TREE_NOTHROW (decl) = true;

  /* Similarly, a function declared with attribute noreturn has it
     attached on to it, but a C11 _Noreturn function does not.  */
  tree reftype = ref;
  if (DECL_P (ref)
      && TREE_THIS_VOLATILE (ref)
      && FUNC_OR_METHOD_TYPE_P (TREE_TYPE (reftype)))
    TREE_THIS_VOLATILE (decl) = true;

  if (DECL_P (ref) || EXPR_P (ref))
    reftype = TREE_TYPE (ref);

  if (POINTER_TYPE_P (reftype))
    reftype = TREE_TYPE (reftype);

  if (!TYPE_P (reftype))
    return NULL_TREE;

  tree attrs = TYPE_ATTRIBUTES (reftype);

  /* Copy type attributes from REF to DECL.  */
  for (tree at = attrs; at; at = TREE_CHAIN (at))
    decl_attributes (node, at, flags, ref);

  return NULL_TREE;
}

/* Handle a "weakref" attribute; arguments as in struct
   attribute_spec.handler.  */

static tree
handle_weakref_attribute (tree *node, tree name, tree args,
			  int flags, bool *no_add_attrs)
{
  tree attr = NULL_TREE;

  /* We must ignore the attribute when it is associated with
     local-scoped decls, since attribute alias is ignored and many
     such symbols do not even have a DECL_WEAK field.  */
  if (decl_function_context (*node)
      || current_function_decl
      || !VAR_OR_FUNCTION_DECL_P (*node))
    {
      warning (OPT_Wattributes, "%qE attribute ignored", name);
      *no_add_attrs = true;
      return NULL_TREE;
    }

  if (lookup_attribute ("ifunc", DECL_ATTRIBUTES (*node)))
    {
      error ("indirect function %q+D cannot be declared %qE",
	     *node, name);
      *no_add_attrs = true;
      return NULL_TREE;
    }

  /* The idea here is that `weakref("name")' mutates into `weakref,
     alias("name")', and weakref without arguments, in turn,
     implicitly adds weak.  */

  if (args)
    {
      attr = tree_cons (get_identifier ("alias"), args, attr);
      attr = tree_cons (get_identifier ("weakref"), NULL_TREE, attr);

      *no_add_attrs = true;

      decl_attributes (node, attr, flags);
    }
  else
    {
      if (lookup_attribute ("alias", DECL_ATTRIBUTES (*node)))
	error_at (DECL_SOURCE_LOCATION (*node),
		  "%qE attribute must appear before %qs attribute",
		  name, "alias");

      /* Can't call declare_weak because it wants this to be TREE_PUBLIC,
	 and that isn't supported; and because it wants to add it to
	 the list of weak decls, which isn't helpful.  */
      DECL_WEAK (*node) = 1;
    }

  if (decl_in_symtab_p (*node))
    {
      struct symtab_node *n = symtab_node::get (*node);
      if (n && n->refuse_visibility_changes)
	error ("%+qD declared %qE after being used", *node, name);
    }

  return NULL_TREE;
}

/* Handle an "visibility" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_visibility_attribute (tree *node, tree name, tree args,
			     int ARG_UNUSED (flags),
			     bool *ARG_UNUSED (no_add_attrs))
{
  tree decl = *node;
  tree id = TREE_VALUE (args);
  enum symbol_visibility vis;

  if (TYPE_P (*node))
    {
      if (TREE_CODE (*node) == ENUMERAL_TYPE)
	/* OK */;
      else if (!RECORD_OR_UNION_TYPE_P (*node))
	{
	  warning (OPT_Wattributes, "%qE attribute ignored on non-class types",
		   name);
	  return NULL_TREE;
	}
      else if (TYPE_FIELDS (*node))
	{
	  error ("%qE attribute ignored because %qT is already defined",
		 name, *node);
	  return NULL_TREE;
	}
    }
  else if (decl_function_context (decl) != 0 || !TREE_PUBLIC (decl))
    {
      warning (OPT_Wattributes, "%qE attribute ignored", name);
      return NULL_TREE;
    }

  if (TREE_CODE (id) != STRING_CST)
    {
      error ("visibility argument not a string");
      return NULL_TREE;
    }

  /*  If this is a type, set the visibility on the type decl.  */
  if (TYPE_P (decl))
    {
      decl = TYPE_NAME (decl);
      if (!decl)
	return NULL_TREE;
      if (TREE_CODE (decl) == IDENTIFIER_NODE)
	{
	   warning (OPT_Wattributes, "%qE attribute ignored on types",
		    name);
	   return NULL_TREE;
	}
    }

  if (strcmp (TREE_STRING_POINTER (id), "default") == 0)
    vis = VISIBILITY_DEFAULT;
  else if (strcmp (TREE_STRING_POINTER (id), "internal") == 0)
    vis = VISIBILITY_INTERNAL;
  else if (strcmp (TREE_STRING_POINTER (id), "hidden") == 0)
    vis = VISIBILITY_HIDDEN;
  else if (strcmp (TREE_STRING_POINTER (id), "protected") == 0)
    vis = VISIBILITY_PROTECTED;
  else
    {
      error ("attribute %qE argument must be one of %qs, %qs, %qs, or %qs",
	     name, "default", "hidden", "protected", "internal");
      vis = VISIBILITY_DEFAULT;
    }

  if (DECL_VISIBILITY_SPECIFIED (decl)
      && vis != DECL_VISIBILITY (decl))
    {
      tree attributes = (TYPE_P (*node)
			 ? TYPE_ATTRIBUTES (*node)
			 : DECL_ATTRIBUTES (decl));
      if (lookup_attribute ("visibility", attributes))
	error ("%qD redeclared with different visibility", decl);
      else if (TARGET_DLLIMPORT_DECL_ATTRIBUTES
	       && lookup_attribute ("dllimport", attributes))
	error ("%qD was declared %qs which implies default visibility",
	       decl, "dllimport");
      else if (TARGET_DLLIMPORT_DECL_ATTRIBUTES
	       && lookup_attribute ("dllexport", attributes))
	error ("%qD was declared %qs which implies default visibility",
	       decl, "dllexport");
    }

  DECL_VISIBILITY (decl) = vis;
  DECL_VISIBILITY_SPECIFIED (decl) = 1;

  /* Go ahead and attach the attribute to the node as well.  This is needed
     so we can determine whether we have VISIBILITY_DEFAULT because the
     visibility was not specified, or because it was explicitly overridden
     from the containing scope.  */

  return NULL_TREE;
}

/* Handle an "tls_model" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_tls_model_attribute (tree *node, tree name, tree args,
			    int ARG_UNUSED (flags),
			    bool *ARG_UNUSED (no_add_attrs))
{
  tree id;
  tree decl = *node;
  enum tls_model kind;

  if (!VAR_P (decl))
    {
      warning (OPT_Wattributes, "%qE attribute ignored because %qD "
	       "is not a variable",
	       name, decl);
      return NULL_TREE;
    }

  if (!DECL_THREAD_LOCAL_P (decl))
    {
      warning (OPT_Wattributes, "%qE attribute ignored because %qD does "
	       "not have thread storage duration", name, decl);
      return NULL_TREE;
    }

  kind = DECL_TLS_MODEL (decl);
  id = TREE_VALUE (args);
  if (TREE_CODE (id) != STRING_CST)
    {
      error ("%qE argument not a string", name);
      return NULL_TREE;
    }

  if (!strcmp (TREE_STRING_POINTER (id), "local-exec"))
    kind = TLS_MODEL_LOCAL_EXEC;
  else if (!strcmp (TREE_STRING_POINTER (id), "initial-exec"))
    kind = TLS_MODEL_INITIAL_EXEC;
  else if (!strcmp (TREE_STRING_POINTER (id), "local-dynamic"))
    kind = optimize ? TLS_MODEL_LOCAL_DYNAMIC : TLS_MODEL_GLOBAL_DYNAMIC;
  else if (!strcmp (TREE_STRING_POINTER (id), "global-dynamic"))
    kind = TLS_MODEL_GLOBAL_DYNAMIC;
  else
    error ("%qE argument must be one of %qs, %qs, %qs, or %qs",
	   name,
	   "local-exec", "initial-exec", "local-dynamic", "global-dynamic");

  set_decl_tls_model (decl, kind);
  return NULL_TREE;
}

/* Handle a "no_instrument_function" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_no_instrument_function_attribute (tree *node, tree name,
					 tree ARG_UNUSED (args),
					 int ARG_UNUSED (flags),
					 bool *no_add_attrs)
{
  tree decl = *node;

  if (TREE_CODE (decl) != FUNCTION_DECL)
    {
      error_at (DECL_SOURCE_LOCATION (decl),
		"%qE attribute applies only to functions", name);
      *no_add_attrs = true;
    }
  else
    DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (decl) = 1;

  return NULL_TREE;
}

/* Handle a "no_profile_instrument_function" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_no_profile_instrument_function_attribute (tree *node, tree name, tree,
						 int, bool *no_add_attrs)
{
  if (TREE_CODE (*node) != FUNCTION_DECL)
    {
      warning (OPT_Wattributes, "%qE attribute ignored", name);
      *no_add_attrs = true;
    }

  return NULL_TREE;
}

/* Handle a "malloc" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_malloc_attribute (tree *node, tree name, tree ARG_UNUSED (args),
			 int ARG_UNUSED (flags), bool *no_add_attrs)
{
  if (TREE_CODE (*node) == FUNCTION_DECL
      && POINTER_TYPE_P (TREE_TYPE (TREE_TYPE (*node))))
    DECL_IS_MALLOC (*node) = 1;
  else
    {
      warning (OPT_Wattributes, "%qE attribute ignored", name);
      *no_add_attrs = true;
    }

  return NULL_TREE;
}

/* Handle a "alloc_size" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_alloc_size_attribute (tree *node, tree name, tree args,
			     int ARG_UNUSED (flags), bool *no_add_attrs)
{
  tree decl = *node;
  tree rettype = TREE_TYPE (decl);
  if (!POINTER_TYPE_P (rettype))
    {
      warning (OPT_Wattributes,
	       "%qE attribute ignored on a function returning %qT",
	       name, rettype);
      *no_add_attrs = true;
      return NULL_TREE;
    }

  for (int i = 1; args; ++i)
    {
      tree pos = TREE_VALUE (args);
      /* NEXT is null when the attribute includes just one argument.
	 That's used to tell positional_argument to avoid mentioning
	 the argument number in diagnostics (since there's just one
	 mentioning it is unnecessary and coule be confusing).  */
      tree next = TREE_CHAIN (args);
      if (tree val = positional_argument (decl, name, pos, INTEGER_TYPE,
					  next || i > 1 ? i : 0))
	TREE_VALUE (args) = val;
      else
	{
	  *no_add_attrs = true;
	  break;
	}

      args = next;
    }

  return NULL_TREE;
}

/* Handle a "alloc_align" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_alloc_align_attribute (tree *node, tree name, tree args, int,
			      bool *no_add_attrs)
{
  tree decl = *node;
  tree rettype = TREE_TYPE (decl);
  if (!POINTER_TYPE_P (rettype))
    {
      warning (OPT_Wattributes,
	       "%qE attribute ignored on a function returning %qT",
	       name, rettype);
      *no_add_attrs = true;
      return NULL_TREE;
    }

  if (!positional_argument (*node, name, TREE_VALUE (args), INTEGER_TYPE))
    *no_add_attrs = true;

  return NULL_TREE;
}

/* Handle a "assume_aligned" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_assume_aligned_attribute (tree *node, tree name, tree args, int,
				 bool *no_add_attrs)
{
  tree decl = *node;
  tree rettype = TREE_TYPE (decl);
  if (TREE_CODE (rettype) != POINTER_TYPE)
    {
      warning (OPT_Wattributes,
	       "%qE attribute ignored on a function returning %qT",
	       name, rettype);
      *no_add_attrs = true;
      return NULL_TREE;
    }

  /* The alignment specified by the first argument.  */
  tree align = NULL_TREE;

  for (; args; args = TREE_CHAIN (args))
    {
      tree val = TREE_VALUE (args);
      if (val && TREE_CODE (val) != IDENTIFIER_NODE
	  && TREE_CODE (val) != FUNCTION_DECL)
	val = default_conversion (val);

      if (!tree_fits_shwi_p (val))
	{
	  warning (OPT_Wattributes,
		   "%qE attribute %E is not an integer constant",
		   name, val);
	  *no_add_attrs = true;
	  return NULL_TREE;
	}

      if (!align)
	{
	  /* Validate and save the alignment.  */
	  if (!integer_pow2p (val))
	    {
	      warning (OPT_Wattributes,
		       "%qE attribute argument %E is not a power of 2",
		       name, val);
	      *no_add_attrs = true;
	      return NULL_TREE;
	    }

	  align = val;
	}
      else if (tree_int_cst_sgn (val) < 0 || tree_int_cst_le (align, val))
	{
	  /* The misalignment specified by the second argument
	     must be non-negative and less than the alignment.  */
	  warning (OPT_Wattributes,
		   "%qE attribute argument %E is not in the range [0, %wu]",
		   name, val, tree_to_uhwi (align) - 1);
	  *no_add_attrs = true;
	  return NULL_TREE;
	}
    }
  return NULL_TREE;
}

/* Handle a "fn spec" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_fnspec_attribute (tree *node ATTRIBUTE_UNUSED, tree ARG_UNUSED (name),
			 tree args, int ARG_UNUSED (flags),
			 bool *no_add_attrs ATTRIBUTE_UNUSED)
{
  gcc_assert (args
	      && TREE_CODE (TREE_VALUE (args)) == STRING_CST
	      && !TREE_CHAIN (args));
  return NULL_TREE;
}

/* Handle a "warn_unused" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_warn_unused_attribute (tree *node, tree name,
			      tree args ATTRIBUTE_UNUSED,
			      int flags ATTRIBUTE_UNUSED, bool *no_add_attrs)
{
  if (TYPE_P (*node))
    /* Do nothing else, just set the attribute.  We'll get at
       it later with lookup_attribute.  */
    ;
  else
    {
      warning (OPT_Wattributes, "%qE attribute ignored", name);
      *no_add_attrs = true;
    }

  return NULL_TREE;
}

/* Handle an "omp declare simd" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_omp_declare_simd_attribute (tree *, tree, tree, int, bool *)
{
  return NULL_TREE;
}

/* Handle an "omp declare variant {base,variant}" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_omp_declare_variant_attribute (tree *, tree, tree, int, bool *)
{
  return NULL_TREE;
}

/* Handle a "simd" attribute.  */

static tree
handle_simd_attribute (tree *node, tree name, tree args, int, bool *no_add_attrs)
{
  if (TREE_CODE (*node) == FUNCTION_DECL)
    {
      tree t = get_identifier ("omp declare simd");
      tree attr = NULL_TREE;
      if (args)
	{
	  tree id = TREE_VALUE (args);

	  if (TREE_CODE (id) != STRING_CST)
	    {
	      error ("attribute %qE argument not a string", name);
	      *no_add_attrs = true;
	      return NULL_TREE;
	    }

	  if (strcmp (TREE_STRING_POINTER (id), "notinbranch") == 0)
	    attr = build_omp_clause (DECL_SOURCE_LOCATION (*node),
				     OMP_CLAUSE_NOTINBRANCH);
	  else if (strcmp (TREE_STRING_POINTER (id), "inbranch") == 0)
	    attr = build_omp_clause (DECL_SOURCE_LOCATION (*node),
				     OMP_CLAUSE_INBRANCH);
	  else
	    {
	      error ("only %<inbranch%> and %<notinbranch%> flags are "
		     "allowed for %<__simd__%> attribute");
	      *no_add_attrs = true;
	      return NULL_TREE;
	    }
	}

      DECL_ATTRIBUTES (*node)
	= tree_cons (t, build_tree_list (NULL_TREE, attr),
		     DECL_ATTRIBUTES (*node));
    }
  else
    {
      warning (OPT_Wattributes, "%qE attribute ignored", name);
      *no_add_attrs = true;
    }

  return NULL_TREE;
}

/* Handle an "omp declare target" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_omp_declare_target_attribute (tree *, tree, tree, int, bool *)
{
  return NULL_TREE;
}

/* Handle a "returns_twice" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_returns_twice_attribute (tree *node, tree name, tree ARG_UNUSED (args),
			 int ARG_UNUSED (flags), bool *no_add_attrs)
{
  if (TREE_CODE (*node) == FUNCTION_DECL)
    DECL_IS_RETURNS_TWICE (*node) = 1;
  else
    {
      warning (OPT_Wattributes, "%qE attribute ignored", 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 (tree *node, tree name,
				 tree ARG_UNUSED (args),
				 int ARG_UNUSED (flags),
				 bool *no_add_attrs)
{
  tree decl = *node;

  if (TREE_CODE (decl) != FUNCTION_DECL)
    {
      error_at (DECL_SOURCE_LOCATION (decl),
	     "%qE attribute applies only to functions", name);
      *no_add_attrs = true;
    }
  else if (DECL_INITIAL (decl))
    {
      error_at (DECL_SOURCE_LOCATION (decl),
		"cannot set %qE attribute after definition", 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 (tree *node, tree name, tree ARG_UNUSED (args),
		       int ARG_UNUSED (flags), bool *no_add_attrs)
{
  if (TREE_CODE (*node) == FUNCTION_DECL)
    {
      tree type = TREE_TYPE (*node);
      if (VOID_TYPE_P (TREE_TYPE (type)))
	warning (OPT_Wattributes, "%qE attribute on function "
		 "returning %<void%>", name);

      DECL_PURE_P (*node) = 1;
      /* ??? TODO: Support types.  */
    }
  else
    {
      warning (OPT_Wattributes, "%qE attribute ignored", name);
      *no_add_attrs = true;
    }

  return NULL_TREE;
}

/* Digest an attribute list destined for a transactional memory statement.
   ALLOWED is the set of attributes that are allowed for this statement;
   return the attribute we parsed.  Multiple attributes are never allowed.  */

int
parse_tm_stmt_attr (tree attrs, int allowed)
{
  tree a_seen = NULL;
  int m_seen = 0;

  for ( ; attrs ; attrs = TREE_CHAIN (attrs))
    {
      tree a = get_attribute_name (attrs);
      tree ns = get_attribute_namespace (attrs);
      int m = 0;

      if (is_attribute_p ("outer", a)
	  && (ns == NULL_TREE || strcmp (IDENTIFIER_POINTER (ns), "gnu") == 0))
	m = TM_STMT_ATTR_OUTER;

      if ((m & allowed) == 0)
	{
	  warning (OPT_Wattributes, "%qE attribute directive ignored", a);
	  continue;
	}

      if (m_seen == 0)
	{
	  a_seen = a;
	  m_seen = m;
	}
      else if (m_seen == m)
	warning (OPT_Wattributes, "%qE attribute duplicated", a);
      else
	warning (OPT_Wattributes, "%qE attribute follows %qE", a, a_seen);
    }

  return m_seen;
}

/* Transform a TM attribute name into a maskable integer and back.
   Note that NULL (i.e. no attribute) is mapped to UNKNOWN, corresponding
   to how the lack of an attribute is treated.  */

int
tm_attr_to_mask (tree attr)
{
  if (attr == NULL)
    return 0;
  if (is_attribute_p ("transaction_safe", attr))
    return TM_ATTR_SAFE;
  if (is_attribute_p ("transaction_callable", attr))
    return TM_ATTR_CALLABLE;
  if (is_attribute_p ("transaction_pure", attr))
    return TM_ATTR_PURE;
  if (is_attribute_p ("transaction_unsafe", attr))
    return TM_ATTR_IRREVOCABLE;
  if (is_attribute_p ("transaction_may_cancel_outer", attr))
    return TM_ATTR_MAY_CANCEL_OUTER;
  return 0;
}

tree
tm_mask_to_attr (int mask)
{
  const char *str;
  switch (mask)
    {
    case TM_ATTR_SAFE:
      str = "transaction_safe";
      break;
    case TM_ATTR_CALLABLE:
      str = "transaction_callable";
      break;
    case TM_ATTR_PURE:
      str = "transaction_pure";
      break;
    case TM_ATTR_IRREVOCABLE:
      str = "transaction_unsafe";
      break;
    case TM_ATTR_MAY_CANCEL_OUTER:
      str = "transaction_may_cancel_outer";
      break;
    default:
      gcc_unreachable ();
    }
  return get_identifier (str);
}

/* Return the first TM attribute seen in LIST.  */

tree
find_tm_attribute (tree list)
{
  for (; list ; list = TREE_CHAIN (list))
    {
      tree name = TREE_PURPOSE (list);
      if (tm_attr_to_mask (name) != 0)
	return name;
    }
  return NULL_TREE;
}

/* Handle the TM attributes; arguments as in struct attribute_spec.handler.
   Here we accept only function types, and verify that none of the other
   function TM attributes are also applied.  */
/* ??? We need to accept class types for C++, but not C.  This greatly
   complicates this function, since we can no longer rely on the extra
   processing given by function_type_required.  */

static tree
handle_tm_attribute (tree *node, tree name, tree args,
		     int flags, bool *no_add_attrs)
{
  /* Only one path adds the attribute; others don't.  */
  *no_add_attrs = true;

  switch (TREE_CODE (*node))
    {
    case RECORD_TYPE:
    case UNION_TYPE:
      /* Only tm_callable and tm_safe apply to classes.  */
      if (tm_attr_to_mask (name) & ~(TM_ATTR_SAFE | TM_ATTR_CALLABLE))
	goto ignored;
      /* FALLTHRU */

    case FUNCTION_TYPE:
    case METHOD_TYPE:
      {
	tree old_name = find_tm_attribute (TYPE_ATTRIBUTES (*node));
	if (old_name == name)
	  ;
	else if (old_name != NULL_TREE)
	  error ("type was previously declared %qE", old_name);
	else
	  *no_add_attrs = false;
      }
      break;

    case FUNCTION_DECL:
      {
	/* transaction_safe_dynamic goes on the FUNCTION_DECL, but we also
	   want to set transaction_safe on the type.  */
	gcc_assert (is_attribute_p ("transaction_safe_dynamic", name));
	if (!TYPE_P (DECL_CONTEXT (*node)))
	  error_at (DECL_SOURCE_LOCATION (*node),
		    "%<transaction_safe_dynamic%> may only be specified for "
		    "a virtual function");
	*no_add_attrs = false;
	decl_attributes (&TREE_TYPE (*node),
			 build_tree_list (get_identifier ("transaction_safe"),
					  NULL_TREE),
			 0);
	break;
      }

    case POINTER_TYPE:
      {
	enum tree_code subcode = TREE_CODE (TREE_TYPE (*node));
	if (subcode == FUNCTION_TYPE || subcode == METHOD_TYPE)
	  {
	    tree fn_tmp = TREE_TYPE (*node);
	    decl_attributes (&fn_tmp, tree_cons (name, args, NULL), 0);
	    *node = build_pointer_type (fn_tmp);
	    break;
	  }
      }
      /* FALLTHRU */

    default:
      /* If a function is next, pass it on to be tried next.  */
      if (flags & (int) ATTR_FLAG_FUNCTION_NEXT)
	return tree_cons (name, args, NULL);

    ignored:
      warning (OPT_Wattributes, "%qE attribute ignored", name);
      break;
    }

  return NULL_TREE;
}

/* Handle the TM_WRAP attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_tm_wrap_attribute (tree *node, tree name, tree args,
			  int ARG_UNUSED (flags), bool *no_add_attrs)
{
  tree decl = *node;

  /* We don't need the attribute even on success, since we
     record the entry in an external table.  */
  *no_add_attrs = true;

  if (TREE_CODE (decl) != FUNCTION_DECL)
    warning (OPT_Wattributes, "%qE attribute ignored", name);
  else
    {
      tree wrap_decl = TREE_VALUE (args);
      if (error_operand_p (wrap_decl))
	;
      else if (TREE_CODE (wrap_decl) != IDENTIFIER_NODE
	       && !VAR_OR_FUNCTION_DECL_P (wrap_decl))
	error ("%qE argument not an identifier", name);
      else
	{
	  if (TREE_CODE (wrap_decl) == IDENTIFIER_NODE)
	    wrap_decl = lookup_name (wrap_decl);
	  if (wrap_decl && TREE_CODE (wrap_decl) == FUNCTION_DECL)
	    {
	      if (lang_hooks.types_compatible_p (TREE_TYPE (decl),
						 TREE_TYPE (wrap_decl)))
		record_tm_replacement (wrap_decl, decl);
	      else
		error ("%qD is not compatible with %qD", wrap_decl, decl);
	    }
	  else
	    error ("%qE argument is not a function", name);
	}
    }

  return NULL_TREE;
}

/* Ignore the given attribute.  Used when this attribute may be usefully
   overridden by the target, but is not used generically.  */

static tree
ignore_attribute (tree * ARG_UNUSED (node), tree ARG_UNUSED (name),
		  tree ARG_UNUSED (args), int ARG_UNUSED (flags),
		  bool *no_add_attrs)
{
  *no_add_attrs = true;
  return NULL_TREE;
}

/* Handle a "no vops" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_novops_attribute (tree *node, tree ARG_UNUSED (name),
			 tree ARG_UNUSED (args), int ARG_UNUSED (flags),
			 bool *ARG_UNUSED (no_add_attrs))
{
  gcc_assert (TREE_CODE (*node) == FUNCTION_DECL);
  DECL_IS_NOVOPS (*node) = 1;
  return NULL_TREE;
}

/* Handle a "deprecated" attribute; arguments as in
   struct attribute_spec.handler.  */

tree
handle_deprecated_attribute (tree *node, tree name,
			     tree args, int flags,
			     bool *no_add_attrs)
{
  tree type = NULL_TREE;
  int warn = 0;
  tree what = NULL_TREE;

  if (!args)
    *no_add_attrs = true;
  else if (TREE_CODE (TREE_VALUE (args)) != STRING_CST)
    {
      error ("deprecated message is not a string");
      *no_add_attrs = true;
    }

  if (DECL_P (*node))
    {
      tree decl = *node;
      type = TREE_TYPE (decl);

      if (TREE_CODE (decl) == TYPE_DECL
	  || TREE_CODE (decl) == PARM_DECL
	  || VAR_OR_FUNCTION_DECL_P (decl)
	  || TREE_CODE (decl) == FIELD_DECL
	  || TREE_CODE (decl) == CONST_DECL
	  || objc_method_decl (TREE_CODE (decl)))
	TREE_DEPRECATED (decl) = 1;
      else
	warn = 1;
    }
  else if (TYPE_P (*node))
    {
      if (!(flags & (int) ATTR_FLAG_TYPE_IN_PLACE))
	*node = build_variant_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 = TYPE_NAME (*node);
	  else if (TREE_CODE (TYPE_NAME (type)) == TYPE_DECL
		   && DECL_NAME (TYPE_NAME (type)))
	    what = DECL_NAME (TYPE_NAME (type));
	}
      if (what)
	warning (OPT_Wattributes, "%qE attribute ignored for %qE", name, what);
      else
	warning (OPT_Wattributes, "%qE attribute ignored", name);
    }

  return NULL_TREE;
}

/* Return the "base" type from TYPE that is suitable to apply attribute
   vector_size to by stripping arrays, function types, etc.  */
static tree
type_for_vector_size (tree type)
{
  /* 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) == METHOD_TYPE
	 || TREE_CODE (type) == ARRAY_TYPE
	 || TREE_CODE (type) == OFFSET_TYPE)
    type = TREE_TYPE (type);

  return type;
}

/* Given TYPE, return the base type to which the vector_size attribute
   ATNAME with ARGS, when non-null, can be applied, if one exists.
   On success and when both ARGS and PTRNUNITS are non-null, set
   *PTRNUNINTS to the number of vector units.  When PTRNUNITS is not
   null, issue a warning when the attribute argument is not constant
   and an error if there is no such type.  Otherwise issue a warning
   in the latter case and return null.  */

static tree
type_valid_for_vector_size (tree type, tree atname, tree args,
			    unsigned HOST_WIDE_INT *ptrnunits)
{
  bool error_p = ptrnunits != NULL;

  /* Get the mode of the type being modified.  */
  machine_mode orig_mode = TYPE_MODE (type);

  if ((!INTEGRAL_TYPE_P (type)
       && !SCALAR_FLOAT_TYPE_P (type)
       && !FIXED_POINT_TYPE_P (type))
      || (!SCALAR_FLOAT_MODE_P (orig_mode)
	  && GET_MODE_CLASS (orig_mode) != MODE_INT
	  && !ALL_SCALAR_FIXED_POINT_MODE_P (orig_mode))
      || !tree_fits_uhwi_p (TYPE_SIZE_UNIT (type))
      || TREE_CODE (type) == BOOLEAN_TYPE)
    {
      if (error_p)
	error ("invalid vector type for attribute %qE", atname);
      else
	warning (OPT_Wattributes, "invalid vector type for attribute %qE",
		 atname);
      return NULL_TREE;
    }

  /* When no argument has been provided this is just a request to validate
     the type above.  Return TYPE to indicate success.  */
  if (!args)
    return type;

  tree size = TREE_VALUE (args);
  /* Erroneous arguments have already been diagnosed.  */
  if (size == error_mark_node)
    return NULL_TREE;

  if (size && TREE_CODE (size) != IDENTIFIER_NODE
      && TREE_CODE (size) != FUNCTION_DECL)
    size = default_conversion (size);

  if (TREE_CODE (size) != INTEGER_CST)
    {
      if (error_p)
	error ("%qE attribute argument value %qE is not an integer constant",
	       atname, size);
      else
	warning (OPT_Wattributes,
		 "%qE attribute argument value %qE is not an integer constant",
		 atname, size);
      return NULL_TREE;
    }

  if (!TYPE_UNSIGNED (TREE_TYPE (size))
      && tree_int_cst_sgn (size) < 0)
    {
      if (error_p)
	error ("%qE attribute argument value %qE is negative",
	       atname, size);
      else
	warning (OPT_Wattributes,
		 "%qE attribute argument value %qE is negative",
		 atname, size);
      return NULL_TREE;
    }

  /* The attribute argument value is constrained by the maximum bit
     alignment representable in unsigned int on the host.  */
  unsigned HOST_WIDE_INT vecsize;
  unsigned HOST_WIDE_INT maxsize = tree_to_uhwi (max_object_size ());
  if (!tree_fits_uhwi_p (size)
      || (vecsize = tree_to_uhwi (size)) > maxsize)
    {
      if (error_p)
	error ("%qE attribute argument value %qE exceeds %wu",
	       atname, size, maxsize);
      else
	warning (OPT_Wattributes,
		 "%qE attribute argument value %qE exceeds %wu",
		 atname, size, maxsize);
      return NULL_TREE;
    }

  if (vecsize % tree_to_uhwi (TYPE_SIZE_UNIT (type)))
    {
      if (error_p)
	error ("vector size not an integral multiple of component size");
      return NULL_TREE;
    }

  if (vecsize == 0)
    {
      error ("zero vector size");
      return NULL;
    }

  /* Calculate how many units fit in the vector.  */
  unsigned HOST_WIDE_INT nunits = vecsize / tree_to_uhwi (TYPE_SIZE_UNIT (type));
  if (nunits & (nunits - 1))
    {
      if (error_p)
	error ("number of components of the vector not a power of two");
      else
	warning (OPT_Wattributes,
		 "number of components of the vector not a power of two");
      return NULL_TREE;
    }

  if (ptrnunits)
    *ptrnunits = nunits;

  return type;
}

/* Handle a "vector_size" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_vector_size_attribute (tree *node, tree name, tree args,
			      int ARG_UNUSED (flags),
			      bool *no_add_attrs)
{
  *no_add_attrs = true;

  /* Determine the "base" type to apply the attribute to.  */
  tree type = type_for_vector_size (*node);

  /* Get the vector size (in bytes) and let the function compute
     the number of vector units.  */
  unsigned HOST_WIDE_INT nunits;
  type = type_valid_for_vector_size (type, name, args, &nunits);
  if (!type)
    return NULL_TREE;

  tree new_type = build_vector_type (type, nunits);

  /* Build back pointers if needed.  */
  *node = lang_hooks.types.reconstruct_complex_type (*node, new_type);

  return NULL_TREE;
}

/* Handle the "nonnull" attribute.  */

static tree
handle_nonnull_attribute (tree *node, tree name,
			  tree args, int ARG_UNUSED (flags),
			  bool *no_add_attrs)
{
  tree type = *node;

  /* If no arguments are specified, all pointer arguments should be
     non-null.  Verify a full prototype is given so that the arguments
     will have the correct types when we actually check them later.
     Avoid diagnosing type-generic built-ins since those have no
     prototype.  */
  if (!args)
    {
      if (!prototype_p (type)
	  && (!TYPE_ATTRIBUTES (type)
	      || !lookup_attribute ("type generic", TYPE_ATTRIBUTES (type))))
	{
	  error ("%qE attribute without arguments on a non-prototype",
		 name);
	  *no_add_attrs = true;
	}
      return NULL_TREE;
    }

  for (int i = 1; args; ++i)
    {
      tree pos = TREE_VALUE (args);
      /* NEXT is null when the attribute includes just one argument.
	 That's used to tell positional_argument to avoid mentioning
	 the argument number in diagnostics (since there's just one
	 mentioning it is unnecessary and coule be confusing).  */
      tree next = TREE_CHAIN (args);
      if (tree val = positional_argument (type, name, pos, POINTER_TYPE,
					  next || i > 1 ? i : 0))
	TREE_VALUE (args) = val;
      else
	{
	  *no_add_attrs = true;
	  break;
	}
      args = next;
    }

  return NULL_TREE;
}

/* Handle the "nonstring" variable attribute.  */

static tree
handle_nonstring_attribute (tree *node, tree name, tree ARG_UNUSED (args),
			    int ARG_UNUSED (flags), bool *no_add_attrs)
{
  gcc_assert (!args);
  tree_code code = TREE_CODE (*node);

  if (VAR_P (*node)
      || code == FIELD_DECL
      || code == PARM_DECL)
    {
      tree type = TREE_TYPE (*node);

      if (POINTER_TYPE_P (type) || TREE_CODE (type) == ARRAY_TYPE)
	{
	  /* Accept the attribute on arrays and pointers to all three
	     narrow character types.  */
	  tree eltype = TREE_TYPE (type);
	  eltype = TYPE_MAIN_VARIANT (eltype);
	  if (eltype == char_type_node
	      || eltype == signed_char_type_node
	      || eltype == unsigned_char_type_node)
	    return NULL_TREE;
	}

      warning (OPT_Wattributes,
	       "%qE attribute ignored on objects of type %qT",
	       name, type);
      *no_add_attrs = true;
      return NULL_TREE;
    }

  if (code == FUNCTION_DECL)
    warning (OPT_Wattributes,
	     "%qE attribute does not apply to functions", name);
  else if (code == TYPE_DECL)
    warning (OPT_Wattributes,
	     "%qE attribute does not apply to types", name);
  else
    warning (OPT_Wattributes, "%qE attribute ignored", name);

  *no_add_attrs = true;
  return NULL_TREE;
}

/* Given a function type FUNCTYPE, returns the type of the parameter
   ARGNO or null if ARGNO exceeds the number of parameters.  On failure
   set *NARGS to the number of function parameters.  */

static tree
get_argument_type (tree functype, unsigned argno, unsigned *nargs)
{
  function_args_iterator iter;
  function_args_iter_init (&iter, functype);

  unsigned count = 0;

  for ( ; iter.next; ++count, function_args_iter_next (&iter))
    {
      if (count + 1 == argno)
	{
	  tree argtype = function_args_iter_cond (&iter);
	  if (VOID_TYPE_P (argtype))
	    break;
	  return argtype;
	}
    }

  *nargs = count;
  return NULL_TREE;
}

/* Appends ATTRSTR to the access string in ATTRS if one is there
   or creates a new one and returns the concatenated access string.  */

static tree
append_access_attrs (tree t, tree attrs, const char *attrstr,
		     char code, HOST_WIDE_INT idxs[2])
{
  char attrspec[80];
  int n1 = sprintf (attrspec, "%c%u", code, (unsigned) idxs[0] - 1);
  int n2 = 0;
  if (idxs[1])
    n2 = sprintf (attrspec + n1 + 1, "%u", (unsigned) idxs[1] - 1);

  size_t newlen = n1 + n2 + !!n2;
  char *newspec = attrspec;

  if (tree acs = lookup_attribute ("access", attrs))
    {
      acs = TREE_VALUE (acs);
      gcc_assert (TREE_CODE (acs) == STRING_CST);

      /* Check to make sure ATTRSPEC doesn't conflict with another
	 access attribute specified in ATTRS by searching the access
	 string in ATTRS for the position string formatted above into
	 ATTRSPEC, and if it's found, that the two match.  */

      const char *posstr = attrspec + 1;
      const char *str = TREE_STRING_POINTER (acs);
      const char *pos = str;
      for ( ; ; pos += n1)
	{
	  pos = strstr (pos, posstr);
	  if (!pos)
	    break;

	  if (ISDIGIT (pos[-1]) || ISDIGIT (pos[n1 -1]))
	    continue;

	  /* Found a matching positional argument.  */
	  if (*attrspec != pos[-1])
	    {
	      /* Mismatch in access mode.  */
	      auto_diagnostic_group d;
	      if (warning (OPT_Wattributes,
			   "attribute %qs mismatch with mode %qs",
			   attrstr,
			   (pos[-1] == 'r'
			    ? "read_only"
			    : (pos[-1] == 'w' ? "write_only" : "read_write")))
		  && DECL_P (t))
		inform (DECL_SOURCE_LOCATION (t),
			"previous declaration here");
	      return NULL_TREE;
	    }

	  if ((n2 && pos[n1 - 1] != ','))
	    {
	      /* Mismatch in the presence of the size argument.  */
	      auto_diagnostic_group d;
	      if (warning (OPT_Wattributes,
			   "attribute %qs positional argument 2 conflicts "
			   "with previous designation",
			   attrstr)
		  && DECL_P (t))
		inform (DECL_SOURCE_LOCATION (t),
			"previous declaration here");
	      return NULL_TREE;
	    }

	  if (!n2 && pos[n1 - 1] == ',')
	    {
	      /* Mismatch in the presence of the size argument.  */
	      auto_diagnostic_group d;
	      if (warning (OPT_Wattributes,
			   "attribute %qs missing positional argument 2 "
			   "provided in previous designation",
			   attrstr)
		  && DECL_P (t))
		inform (DECL_SOURCE_LOCATION (t),
			"previous declaration here");
	      return NULL_TREE;
	    }

	  if (n2 && strncmp (attrspec + n1 + 1, pos + n1, n2))
	    {
	      /* Mismatch in the value of the size argument.  */
	      auto_diagnostic_group d;
	      if (warning (OPT_Wattributes,
			   "attribute %qs mismatched positional argument "
			   "values %i and %i",
			   attrstr, atoi (attrspec + n1 + 1) + 1,
			   atoi (pos + n1) + 1)
		  && DECL_P (t))
		inform (DECL_SOURCE_LOCATION (t),
			"previous declaration here");
	      return NULL_TREE;
	    }

	  /* Avoid adding the same attribute specification.  */
	  return NULL_TREE;
	}

      /* Connect the two substrings formatted above into a single one.  */
      if (idxs[1])
	attrspec[n1] = ',';

      size_t len = strlen (str);
      newspec = XNEWVEC (char, newlen + len + 1);
      strcpy (newspec, str);
      strcpy (newspec + len, attrspec);
      newlen += len;
    }
  else if (idxs[1])
    /* Connect the two substrings formatted above into a single one.  */
    attrspec[n1] = ',';

  tree ret = build_string (newlen + 1, newspec);
  if (newspec != attrspec)
    XDELETEVEC (newspec);
  return ret;
}

/* Handle the access attribute (read_only, write_only, and read_write).  */

static tree
handle_access_attribute (tree *node, tree name, tree args,
			 int ARG_UNUSED (flags), bool *no_add_attrs)
{
  tree type = *node;
  tree attrs = TYPE_ATTRIBUTES (type);

  *no_add_attrs = true;

  /* Verify a full prototype is provided so that the argument types
     can be validated.  Avoid diagnosing type-generic built-ins since
     those have no prototype.  */
  if (!args
      && !prototype_p (type)
      && (!attrs || !lookup_attribute ("type generic", attrs)))
    {
      error ("attribute %qE without arguments on a non-prototype", name);
      return NULL_TREE;
    }

  /* Set to true when the access mode has the form of a function call
     as in 'attribute (read_only (1, 2))'.  That's an easy mistake to
     make and so worth a special diagnostic.  */
  bool funcall = false;
  tree access_mode = TREE_VALUE (args);
  if (TREE_CODE (access_mode) == CALL_EXPR)
    {
      access_mode = CALL_EXPR_FN (access_mode);
      if (TREE_CODE (access_mode) != ADDR_EXPR)
	{
	  error ("attribute %qE invalid mode", name);
	  return NULL_TREE;
	}
      access_mode = TREE_OPERAND (access_mode, 0);
      access_mode = DECL_NAME (access_mode);
      funcall = true;
    }

  const char* const access_str = IDENTIFIER_POINTER (access_mode);
  const char *ps = access_str;
  if (ps[0] == '_' && ps[1] == '_')
    {
      size_t len = strlen (ps);
      if (ps[len - 1] == '_' && ps[len - 2] == '_')
	ps += 2;
    }

  const bool read_only = strncmp (ps, "read_only", 9) == 0;
  const bool write_only = strncmp (ps, "write_only", 10) == 0;
  if (!read_only && !write_only && strncmp (ps, "read_write", 10))
    {
      error ("attribute %qE invalid mode %qs; expected one of "
	     "%qs, %qs, or %qs", name, access_str,
	     "read_only", "read_write", "write_only");
      return NULL_TREE;
    }

  if (funcall)
    {
      error ("attribute %qE unexpected %<(%> after mode %qs; expected "
	     "a positional argument or %<)%>",
	     name, access_str);
      return NULL_TREE;
    }

  args = TREE_CHAIN (args);
  if (!args)
    {
      /* The first positional argument is required.  It may be worth
	 dropping the requirement at some point and having read_only
	 apply to all const-qualified pointers and read_write or
	 write_only to the rest.  */
      error ("attribute %<%E(%s)%> missing an argument",
	     name, access_str);
      return NULL_TREE;
    }

  /* One or more positional arguments have been specified.  Validate
     them.  */
  tree idxnodes[2] = { NULL_TREE, NULL_TREE };
  tree argtypes[2] = { NULL_TREE, NULL_TREE };
  /* 1-based attribute positional arguments or zero if not specified.
     Invalid negative or excessive values are also stored but used
     only in diagnostics.  */
  HOST_WIDE_INT idxs[2] = { 0, 0 };

  /* Number of function formal arguments (used in diagnostics).  */
  unsigned nfuncargs = 0;
  /* Number of (optional) attribute positional arguments.  */
  unsigned nattrargs = 0;

  for (unsigned i = 0; i != 2; ++i, args = TREE_CHAIN (args), ++nattrargs)
    {
      if (!args)
	break;

      idxnodes[i] = TREE_VALUE (args);

      if (TREE_CODE (idxnodes[i]) != IDENTIFIER_NODE
	  && TREE_CODE (idxnodes[i]) != FUNCTION_DECL)
	idxnodes[i] = default_conversion (idxnodes[i]);

      if (tree_fits_shwi_p (idxnodes[i]))
	{
	  idxs[i] = tree_to_shwi (idxnodes[i]);
	  argtypes[i] = get_argument_type (type, idxs[i], &nfuncargs);
	}
    }

  if ((nattrargs == 1 && !idxs[0])
      || (nattrargs == 2 && (!idxs[0] || !idxs[1])))
    {
      if (idxnodes[1])
	error ("attribute %<%E(%s, %E, %E)%> invalid positional argument %i",
	       name, access_str, idxnodes[0], idxnodes[1], idxs[0] ? 2 : 1);
      else
	error ("attribute %<%E(%s, %E)%> invalid positional argument %i",
	       name, access_str, idxnodes[0], idxs[0] ? 2 : 1);
      return NULL_TREE;
    }

  /* Format the attribute specification to include in diagnostics.  */
  char attrstr[80];
  if (idxnodes[1])
    snprintf (attrstr, sizeof attrstr, "%s(%s, %lli, %lli)",
	      IDENTIFIER_POINTER (name), access_str,
	      (long long) idxs[0], (long long) idxs[1]);
  else if (idxnodes[0])
    snprintf (attrstr, sizeof attrstr, "%s(%s, %lli)",
	      IDENTIFIER_POINTER (name), access_str,
	      (long long) idxs[0]);
  else
    snprintf (attrstr, sizeof attrstr, "%s(%s)",
	      IDENTIFIER_POINTER (name), access_str);

  /* Verify the positional argument values are in range.  */
  if (!argtypes[0] || (idxnodes[1] && !argtypes[1]))
    {
      if (idxnodes[0])
	{
	  if (idxs[0] < 0 || idxs[1] < 0)
	    error ("attribute %qs positional argument %i invalid value %wi",
		   attrstr, idxs[0] < 0 ? 1 : 2,
		   idxs[0] < 0 ? idxs[0] : idxs[1]);
	  else
	    error ("attribute %qs positional argument %i value %wi exceeds "
		   "number of function arguments %u",
		   attrstr, idxs[0] ? 1 : 2,
		   idxs[0] ? idxs[0] : idxs[1],
		   nfuncargs);
	}
      else
	error ("attribute %qs invalid positional argument", attrstr);

      return NULL_TREE;
    }

  if (!POINTER_TYPE_P (argtypes[0]))
    {
      /* The first argument must have a pointer or reference type.  */
      error ("attribute %qs positional argument 1 references "
	     "non-pointer argument type %qT",
	     attrstr, argtypes[0]);
      return NULL_TREE;
    }

  {
    /* Pointers to functions are not allowed.  */
    tree ptrtype = TREE_TYPE (argtypes[0]);
    if (FUNC_OR_METHOD_TYPE_P (ptrtype))
      {
	error ("attribute %qs positional argument 1 references "
	       "argument of function type %qT",
	       attrstr, ptrtype);
	return NULL_TREE;
      }
  }

  if (!read_only)
    {
      /* A read_write and write_only modes must reference non-const
	 arguments.  */
      if (TYPE_READONLY (TREE_TYPE (argtypes[0])))
	{
	  error ("attribute %qs positional argument 1 references "
		 "%qs-qualified argument type %qT",
		 attrstr, "const", argtypes[0]);
	  return NULL_TREE;
	}
    }
  else if (!TYPE_READONLY (TREE_TYPE (argtypes[0])))
    {
      /* A read_only mode should ideally reference const-qualified
	 arguments but it's not diagnosed error if one doesn't.
	 This makes it possible to annotate legacy, const-incorrect
	 APIs.  It might be worth a diagnostic along the lines of
	 -Wsuggest-const.  */
      ;
    }

  if (argtypes[1] && !INTEGRAL_TYPE_P (argtypes[1]))
    {
      error ("attribute %qs positional argument 2 references "
	     "non-integer argument type %qT",
	     attrstr, argtypes[1]);
      return NULL_TREE;
    }

  /* Verify that the new attribute doesn't conflict with any existing
     attributes specified on previous declarations of the same type
     and if not, concatenate the two.  */
  const char code = read_only ? 'r' : write_only ? 'w' : 'x';
  tree new_attrs = append_access_attrs (node[0], attrs, attrstr, code, idxs);
  if (!new_attrs)
    return NULL_TREE;

  /* Replace any existing access attribute specification with
     the concatenation above.  */
  attrs = remove_attribute (IDENTIFIER_POINTER (name), attrs);
  new_attrs = tree_cons (name, new_attrs, attrs);

  if (node[1])
    {
      /* Repeat for the previously declared type.  */
      attrs = TYPE_ATTRIBUTES (TREE_TYPE (node[1]));
      tree new_attrs
	= append_access_attrs (node[1], attrs, attrstr, code, idxs);
      if (!new_attrs)
	return NULL_TREE;

      attrs = remove_attribute (IDENTIFIER_POINTER (name), attrs);
      new_attrs = tree_cons (name, new_attrs, attrs);
      TYPE_ATTRIBUTES (TREE_TYPE (node[1])) = new_attrs;
    }

  TYPE_ATTRIBUTES (*node) = new_attrs;
  return NULL_TREE;
}

/* Handle a "nothrow" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_nothrow_attribute (tree *node, tree name, tree ARG_UNUSED (args),
			  int ARG_UNUSED (flags), bool *no_add_attrs)
{
  if (TREE_CODE (*node) == FUNCTION_DECL)
    TREE_NOTHROW (*node) = 1;
  /* ??? TODO: Support types.  */
  else
    {
      warning (OPT_Wattributes, "%qE attribute ignored", name);
      *no_add_attrs = true;
    }

  return NULL_TREE;
}

/* Handle a "cleanup" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_cleanup_attribute (tree *node, tree name, tree args,
			  int ARG_UNUSED (flags), bool *no_add_attrs)
{
  tree decl = *node;
  tree cleanup_id, cleanup_decl;

  /* ??? Could perhaps support cleanups on TREE_STATIC, much like we do
     for global destructors in C++.  This requires infrastructure that
     we don't have generically at the moment.  It's also not a feature
     we'd be missing too much, since we do have attribute constructor.  */
  if (!VAR_P (decl) || TREE_STATIC (decl))
    {
      warning (OPT_Wattributes, "%qE attribute ignored", name);
      *no_add_attrs = true;
      return NULL_TREE;
    }

  /* Verify that the argument is a function in scope.  */
  /* ??? We could support pointers to functions here as well, if
     that was considered desirable.  */
  cleanup_id = TREE_VALUE (args);
  if (TREE_CODE (cleanup_id) != IDENTIFIER_NODE)
    {
      error ("cleanup argument not an identifier");
      *no_add_attrs = true;
      return NULL_TREE;
    }
  cleanup_decl = lookup_name (cleanup_id);
  if (!cleanup_decl || TREE_CODE (cleanup_decl) != FUNCTION_DECL)
    {
      error ("cleanup argument not a function");
      *no_add_attrs = true;
      return NULL_TREE;
    }

  /* That the function has proper type is checked with the
     eventual call to build_function_call.  */

  return NULL_TREE;
}

/* Handle a "warn_unused_result" attribute.  No special handling.  */

static tree
handle_warn_unused_result_attribute (tree *node, tree name,
			       tree ARG_UNUSED (args),
			       int ARG_UNUSED (flags), bool *no_add_attrs)
{
  /* Ignore the attribute for functions not returning any value.  */
  if (VOID_TYPE_P (TREE_TYPE (*node)))
    {
      warning (OPT_Wattributes, "%qE attribute ignored", name);
      *no_add_attrs = true;
    }

  return NULL_TREE;
}

/* Handle a "sentinel" attribute.  */

static tree
handle_sentinel_attribute (tree *node, tree name, tree args,
			   int ARG_UNUSED (flags), bool *no_add_attrs)
{
  if (!prototype_p (*node))
    {
      warning (OPT_Wattributes,
	       "%qE attribute requires prototypes with named arguments", name);
      *no_add_attrs = true;
    }
  else
    {
      if (!stdarg_p (*node))
	{
	  warning (OPT_Wattributes,
		   "%qE attribute only applies to variadic functions", name);
	  *no_add_attrs = true;
	}
    }

  if (args)
    {
      tree position = TREE_VALUE (args);
      if (position && TREE_CODE (position) != IDENTIFIER_NODE
	  && TREE_CODE (position) != FUNCTION_DECL)
	position = default_conversion (position);

      if (TREE_CODE (position) != INTEGER_CST
	  || !INTEGRAL_TYPE_P (TREE_TYPE (position)))
	{
	  warning (OPT_Wattributes,
		   "requested position is not an integer constant");
	  *no_add_attrs = true;
	}
      else
	{
	  if (tree_int_cst_lt (position, integer_zero_node))
	    {
	      warning (OPT_Wattributes,
		       "requested position is less than zero");
	      *no_add_attrs = true;
	    }
	}
    }

  return NULL_TREE;
}

/* Handle a "type_generic" attribute.  */

static tree
handle_type_generic_attribute (tree *node, tree ARG_UNUSED (name),
			       tree ARG_UNUSED (args), int ARG_UNUSED (flags),
			       bool * ARG_UNUSED (no_add_attrs))
{
  /* Ensure we have a function type.  */
  gcc_assert (TREE_CODE (*node) == FUNCTION_TYPE);

  /* Ensure we have a variadic function.  */
  gcc_assert (!prototype_p (*node) || stdarg_p (*node));

  return NULL_TREE;
}

/* Handle a "target" attribute.  */

static tree
handle_target_attribute (tree *node, tree name, tree args, int flags,
			 bool *no_add_attrs)
{
  /* Ensure we have a function type.  */
  if (TREE_CODE (*node) != FUNCTION_DECL)
    {
      warning (OPT_Wattributes, "%qE attribute ignored", name);
      *no_add_attrs = true;
    }
  else if (lookup_attribute ("target_clones", DECL_ATTRIBUTES (*node)))
    {
      warning (OPT_Wattributes, "%qE attribute ignored due to conflict "
		   "with %qs attribute", name, "target_clones");
      *no_add_attrs = true;
    }
  else if (! targetm.target_option.valid_attribute_p (*node, name, args,
						      flags))
    *no_add_attrs = true;

  /* Check that there's no empty string in values of the attribute.  */
  for (tree t = args; t != NULL_TREE; t = TREE_CHAIN (t))
    {
      tree value = TREE_VALUE (t);
      if (TREE_CODE (value) == STRING_CST
	  && TREE_STRING_LENGTH (value) == 1
	  && TREE_STRING_POINTER (value)[0] == '\0')
	{
	  warning (OPT_Wattributes, "empty string in attribute %<target%>");
	  *no_add_attrs = true;
	}
    }

  return NULL_TREE;
}

/* Handle a "target_clones" attribute.  */

static tree
handle_target_clones_attribute (tree *node, tree name, tree ARG_UNUSED (args),
			  int ARG_UNUSED (flags), bool *no_add_attrs)
{
  /* Ensure we have a function type.  */
  if (TREE_CODE (*node) == FUNCTION_DECL)
    {
      if (lookup_attribute ("always_inline", DECL_ATTRIBUTES (*node)))
	{
	  warning (OPT_Wattributes, "%qE attribute ignored due to conflict "
		   "with %qs attribute", name, "always_inline");
	  *no_add_attrs = true;
	}
      else if (lookup_attribute ("target", DECL_ATTRIBUTES (*node)))
	{
	  warning (OPT_Wattributes, "%qE attribute ignored due to conflict "
		   "with %qs attribute", name, "target");
	  *no_add_attrs = true;
	}
      else
      /* Do not inline functions with multiple clone targets.  */
	DECL_UNINLINABLE (*node) = 1;
    }
  else
    {
      warning (OPT_Wattributes, "%qE attribute ignored", name);
      *no_add_attrs = true;
    }
  return NULL_TREE;
}

/* For handling "optimize" attribute. arguments as in
   struct attribute_spec.handler.  */

static tree
handle_optimize_attribute (tree *node, tree name, tree args,
			   int ARG_UNUSED (flags), bool *no_add_attrs)
{
  /* Ensure we have a function type.  */
  if (TREE_CODE (*node) != FUNCTION_DECL)
    {
      warning (OPT_Wattributes, "%qE attribute ignored", name);
      *no_add_attrs = true;
    }
  else
    {
      struct cl_optimization cur_opts;
      tree old_opts = DECL_FUNCTION_SPECIFIC_OPTIMIZATION (*node);

      /* Save current options.  */
      cl_optimization_save (&cur_opts, &global_options);

      /* If we previously had some optimization options, use them as the
	 default.  */
      if (old_opts)
	cl_optimization_restore (&global_options,
				 TREE_OPTIMIZATION (old_opts));

      /* Parse options, and update the vector.  */
      parse_optimize_options (args, true);
      DECL_FUNCTION_SPECIFIC_OPTIMIZATION (*node)
	= build_optimization_node (&global_options);

      /* Restore current options.  */
      cl_optimization_restore (&global_options, &cur_opts);
    }

  return NULL_TREE;
}

/* Handle a "no_split_stack" attribute.  */

static tree
handle_no_split_stack_attribute (tree *node, tree name,
				 tree ARG_UNUSED (args),
				 int ARG_UNUSED (flags),
				 bool *no_add_attrs)
{
  tree decl = *node;

  if (TREE_CODE (decl) != FUNCTION_DECL)
    {
      error_at (DECL_SOURCE_LOCATION (decl),
		"%qE attribute applies only to functions", name);
      *no_add_attrs = true;
    }
  else if (DECL_INITIAL (decl))
    {
      error_at (DECL_SOURCE_LOCATION (decl),
		"cannot set %qE attribute after definition", name);
      *no_add_attrs = true;
    }

  return NULL_TREE;
}

/* Handle a "returns_nonnull" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_returns_nonnull_attribute (tree *node, tree name, tree, int,
				  bool *no_add_attrs)
{
  // Even without a prototype we still have a return type we can check.
  if (TREE_CODE (TREE_TYPE (*node)) != POINTER_TYPE)
    {
      error ("%qE attribute on a function not returning a pointer", name);
      *no_add_attrs = true;
    }
  return NULL_TREE;
}

/* Handle a "designated_init" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_designated_init_attribute (tree *node, tree name, tree, int,
				  bool *no_add_attrs)
{
  if (TREE_CODE (*node) != RECORD_TYPE)
    {
      error ("%qE attribute is only valid on %<struct%> type", name);
      *no_add_attrs = true;
    }
  return NULL_TREE;
}


/* Handle a "fallthrough" attribute; arguments as in struct
   attribute_spec.handler.  */

tree
handle_fallthrough_attribute (tree *, tree name, tree, int,
			      bool *no_add_attrs)
{
  pedwarn (input_location, OPT_Wattributes, "%qE attribute ignored", name);
  *no_add_attrs = true;
  return NULL_TREE;
}

/* Handle a "patchable_function_entry" attributes; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_patchable_function_entry_attribute (tree *, tree name, tree args,
					   int, bool *no_add_attrs)
{
  for (; args; args = TREE_CHAIN (args))
    {
      tree val = TREE_VALUE (args);
      if (val && TREE_CODE (val) != IDENTIFIER_NODE
	  && TREE_CODE (val) != FUNCTION_DECL)
	val = default_conversion (val);

      if (!tree_fits_uhwi_p (val))
	{
	  warning (OPT_Wattributes,
		   "%qE attribute argument %qE is not an integer constant",
		   name, val);
	  *no_add_attrs = true;
	  return NULL_TREE;
	}
    }
  return NULL_TREE;
}

/* Attempt to partially validate a single attribute ATTR as if
   it were to be applied to an entity OPER.  */

static bool
validate_attribute (location_t atloc, tree oper, tree attr)
{
  /* Determine whether the name of the attribute is valid
     and fail with an error if not.  */
  tree atname = get_attribute_name (attr);
  if (!lookup_attribute_spec (atname))
    {
      if (atloc != UNKNOWN_LOCATION)
	error_at (atloc, "unknown attribute %qE", atname);
      return false;
    }

  tree args = TREE_VALUE (attr);
  if (!args)
    return true;

  /* FIXME: Do some validation.  */
  const char *atstr = IDENTIFIER_POINTER (atname);
  if (!strcmp (atstr, "format"))
    return true;

  /* Only when attribute arguments have been provided try to validate
     the whole thing.  decl_attributes doesn't return an indication of
     success or failure so proceed regardless.  */
  const char tmpname[] = "__builtin_has_attribute_tmp.";
  tree tmpid = get_identifier (tmpname);
  tree tmpdecl;
  if (!strcmp (atstr, "vector_size"))
    {
      tree type = TYPE_P (oper) ? oper : TREE_TYPE (oper);
      /* Check for function type here since type_for_vector_size
	 strips it while looking for a function's return type.  */
      if (FUNC_OR_METHOD_TYPE_P (type))
	{
	  warning_at (atloc, OPT_Wattributes,
		      "invalid operand type %qT for %qs", type, atstr);
	  return false;
	}

      type = type_for_vector_size (type);
      if (VECTOR_TYPE_P (type))
	type = TREE_TYPE (type);
      /* Avoid trying to apply attribute vector_size to OPER since
	 it's overly restrictive.  Simply make sure it has the right
	 type.  */
      return type_valid_for_vector_size (type, atname, args, NULL);
    }

  if (TYPE_P (oper))
    tmpdecl = build_decl (atloc, TYPE_DECL, tmpid, oper);
  else if (DECL_P (oper))
    tmpdecl = build_decl (atloc, TREE_CODE (oper), tmpid, TREE_TYPE (oper));
  else if (EXPR_P (oper))
    tmpdecl = build_decl (atloc, TYPE_DECL, tmpid, TREE_TYPE (oper));
  else
    return false;

  /* Temporarily clear CURRENT_FUNCTION_DECL to make decl_attributes
     believe the DECL declared above is at file scope.  (See bug 87526.)  */
  tree save_curfunc = current_function_decl;
  current_function_decl = NULL_TREE;
  if (DECL_P (tmpdecl))
    {
      if (DECL_P (oper))
	/* An alias cannot be a definition so declare the symbol extern.  */
	DECL_EXTERNAL (tmpdecl) = true;
      /* Attribute visibility only applies to symbols visible from other
	 translation units so make it "public."   */
      TREE_PUBLIC (tmpdecl) = TREE_PUBLIC (oper);
    }
  decl_attributes (&tmpdecl, attr, 0);
  current_function_decl = save_curfunc;

  /* FIXME: Change decl_attributes to indicate success or failure (and
     parameterize it to avoid failing with errors).  */
  return true;
}

/* Return true if the DECL, EXPR, or TYPE t has been declared with
   attribute ATTR.  For DECL, consider also its type.  For EXPR,
   consider just its type.  */

bool
has_attribute (location_t atloc, tree t, tree attr, tree (*convert)(tree))
{
  if (!attr || !t || t == error_mark_node)
    return false;

  if (!validate_attribute (atloc, t, attr))
    return false;

  tree type = NULL_TREE;
  tree expr = NULL_TREE;
  if (TYPE_P (t))
    type = t;
  else
    {
      do
	{
	  /* Determine the array element/member declaration from
	     a COMPONENT_REF and an INDIRECT_REF involving a refeence.  */
	  STRIP_NOPS (t);
	  tree_code code = TREE_CODE (t);
	  if (code == INDIRECT_REF)
	    {
	      tree op0 = TREE_OPERAND (t, 0);
	      if (TREE_CODE (TREE_TYPE (op0)) == REFERENCE_TYPE)
		t = op0;
	      else
		break;
	    }
	  else if (code == COMPONENT_REF)
	    t = TREE_OPERAND (t, 1);
	  else
	    break;
	} while (true);
      expr = t;
    }

  /* Set to true when an attribute is found in the referenced entity
     that matches the specified attribute.  */
  bool found_match = false;

  tree atname = get_attribute_name (attr);
  const char *namestr = IDENTIFIER_POINTER (atname);

   /* Iterate once for a type and twice for a function or variable
     declaration: once for the DECL and the second time for its
     TYPE.  */
  for (bool done = false; !found_match && !done; )
    {
      tree atlist;
      if (type)
	{
	  if (type == error_mark_node)
	    {
	      /* This could be a label.  FIXME: add support for labels.  */
	      warning_at (atloc, OPT_Wattributes,
			  (TYPE_P (t)
			   ? G_("%qs attribute not supported for %qT "
				"in %<__builtin_has_attribute%>")
			   : G_("%qs attribute not supported for %qE "
				"in %<__builtin_has_attribute%>")),
			  namestr, t);
	      return false;
	    }

	  /* Clear EXPR to prevent considering it again below.  */
	  atlist = TYPE_ATTRIBUTES (type);
	  expr = NULL_TREE;
	  done = true;
	}
      else if (DECL_P (expr))
	{
	  /* Set TYPE to the DECL's type to process it on the next
	     iteration.  */
	  atlist = DECL_ATTRIBUTES (expr);
	  type = TREE_TYPE (expr);
	}
      else
	{
	  type = TREE_TYPE (expr);
	  atlist = TYPE_ATTRIBUTES (type);
	  done = true;
	}

     /* True when an attribute with the sought name (though not necessarily
	 with the sought attributes) has been found on the attribute chain.  */
      bool found_attr = false;

      /* When clear, the first mismatched attribute argument results
	 in failure.  Otherwise, the first matched attribute argument
	 results in success.  */
      bool attr_nonnull = !strcmp ("nonnull", namestr);
      bool ignore_mismatches = attr_nonnull;

      /* Iterate over the instances of the sought attribute on the DECL or
	 TYPE (there may be multiple instances with different arguments).  */
      for (; (atlist = lookup_attribute (namestr, atlist));
	   found_attr = true, atlist = TREE_CHAIN (atlist))
	{
	  /* If there are no arguments to match the result is true except
	     for nonnull where the attribute with no arguments must match.  */
	  if (!TREE_VALUE (attr))
	    return attr_nonnull ? !TREE_VALUE (atlist) : true;

	  /* Attribute nonnull with no arguments subsumes all values of
	     the attribute.  FIXME: This is overly broad since it only
	     applies to pointer arguments, but querying non-pointer
	     arguments is diagnosed.  */
	  if (!TREE_VALUE (atlist) && attr_nonnull)
	    return true;

	  /* Iterate over the DECL or TYPE attribute argument's values.  */
	  for (tree val = TREE_VALUE (atlist); val; val = TREE_CHAIN (val))
	    {
	      /* Iterate over the arguments in the sought attribute comparing
		 their values to those specified for the DECL or TYPE.  */
	      for (tree arg = TREE_VALUE (attr); arg; arg = TREE_CHAIN (arg))
		{
		  tree v1 = TREE_VALUE (val);
		  tree v2 = TREE_VALUE (arg);
		  if (v1 == v2)
		    return true;

		  if (!v1 || !v2)
		    break;

		  if (TREE_CODE (v1) == IDENTIFIER_NODE
		      || TREE_CODE (v2) == IDENTIFIER_NODE)
		    /* Two identifiers are the same if their values are
		       equal (that's handled above).  Otherwise ther are
		       either not the same or oneis not an identifier.  */
		    return false;

		  /* Convert to make them equality-comparable.  */
		  v1 = convert (v1);
		  v2 = convert (v2);

		  /* A positive value indicates equality, negative means
		     "don't know."  */
		  if (simple_cst_equal (v1, v2) == 1)
		    return true;

		  if (!ignore_mismatches)
		    break;
		}
	    }
	}

      if (!found_attr)
	{
	  /* Some attributes are encoded directly in the tree node.  */
	  if (!strcmp ("aligned", namestr))
	    {
	      if (tree arg = TREE_VALUE (attr))
		{
		  arg = convert (TREE_VALUE (arg));
		  if (!tree_fits_uhwi_p (arg))
		    /* Invalid argument.  */;
		  else if (expr && DECL_P (expr)
			   && DECL_USER_ALIGN (expr))
		    found_match = DECL_ALIGN_UNIT (expr) == tree_to_uhwi (arg);
		  else if (type && TYPE_USER_ALIGN (type))
		    found_match = TYPE_ALIGN_UNIT (type) == tree_to_uhwi (arg);
		}
	      else if (expr && DECL_P (expr))
		found_match = DECL_USER_ALIGN (expr);
	      else if (type)
		found_match = TYPE_USER_ALIGN (type);
	    }
	  else if (!strcmp ("const", namestr))
	    {
	      if (expr && DECL_P (expr))
		found_match = TREE_READONLY (expr);
	    }
	  else if (!strcmp ("noreturn", namestr))
	    {
	      /* C11 _Noreturn sets the volatile bit without attaching
		 an attribute to the decl.  */
	      if (expr
		  && DECL_P (expr)
		  && FUNC_OR_METHOD_TYPE_P (TREE_TYPE (expr)))
		found_match = TREE_THIS_VOLATILE (expr);
	    }
	  else if (!strcmp ("pure", namestr))
	    {
	      if (expr && DECL_P (expr))
		found_match = DECL_PURE_P (expr);
	    }
	  else if (!strcmp ("deprecated", namestr))
	    {
	      found_match = TREE_DEPRECATED (expr ? expr : type);
	      if (found_match)
		return true;
	    }
	  else if (!strcmp ("vector_size", namestr))
	    {
	      if (!type || !VECTOR_TYPE_P (type))
		return false;

	      if (tree arg = TREE_VALUE (attr))
		{
		  /* Compare the vector size argument for equality.  */
		  arg = convert (TREE_VALUE (arg));
		  return tree_int_cst_equal (arg, TYPE_SIZE_UNIT (type)) == 1;
		}
	      else
		return true;
	    }
	  else if (!strcmp ("warn_if_not_aligned", namestr))
	    {
	      if (tree arg = TREE_VALUE (attr))
		{
		  arg = convert (TREE_VALUE (arg));
		  if (expr && DECL_P (expr))
		    found_match = (DECL_WARN_IF_NOT_ALIGN (expr)
				   == tree_to_uhwi (arg) * BITS_PER_UNIT);
		  else if (type)
		    found_match = (TYPE_WARN_IF_NOT_ALIGN (type)
				   == tree_to_uhwi (arg) * BITS_PER_UNIT);
		}
	      else if (expr && DECL_P (expr))
		found_match = DECL_WARN_IF_NOT_ALIGN (expr);
	      else if (type)
		found_match = TYPE_WARN_IF_NOT_ALIGN (type);
	    }
	  else if (!strcmp ("transparent_union", namestr))
	    {
	      if (type)
		found_match = TYPE_TRANSPARENT_AGGR (type) != 0;
	    }
	  else if (!strcmp ("mode", namestr))
	    {
	      /* Finally issue a warning for attributes that cannot
		 be supported in this context.  Attribute mode is not
		 added to a symbol and cannot be determined from it.  */
	      warning_at (atloc, OPT_Wattributes,
			  "%qs attribute not supported in "
			  "%<__builtin_has_attribute%>", namestr);
	      break;
	    }
	}
    }
  return found_match;
}