(root)/
gcc-13.2.0/
gcc/
c-family/
c-attribs.cc
/* C-family attributes handling.
   Copyright (C) 1992-2023 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/>.  */

#define INCLUDE_STRING
#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"
#include "gcc-rich-location.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_no_sanitize_coverage_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_no_stack_protector_function_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_uninitialized_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_special_var_sec_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_strict_flex_array_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_dealloc_attribute (tree *, tree, tree, int, bool *);
static tree handle_tainted_args_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_unavailable_attribute (tree *, tree, tree, int,
					  bool *);
static tree handle_vector_size_attribute (tree *, tree, tree, int,
					  bool *) ATTRIBUTE_NONNULL(3);
static tree handle_vector_mask_attribute (tree *, tree, tree, int,
					  bool *) ATTRIBUTE_NONNULL(3);
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_assume_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_zero_call_used_regs_attribute (tree *, tree, tree, int,
						  bool *);
static tree handle_argspec_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_non_overlapping_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 *);
static tree handle_nsobject_attribute (tree *, tree, tree, int, bool *);
static tree handle_objc_root_class_attribute (tree *, tree, tree, int, bool *);
static tree handle_objc_nullability_attribute (tree *, tree, tree, int, bool *);
static tree handle_signed_bool_precision_attribute (tree *, tree, tree, int,
						    bool *);
static tree handle_retain_attribute (tree *, tree, tree, int, bool *);
static tree handle_fd_arg_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)
};

/* Exclusions that apply to attributes that put declarations in specific
   sections.  */
static const struct attribute_spec::exclusions attr_section_exclusions[] =
{
  ATTR_EXCL ("noinit", true, true, true),
  ATTR_EXCL ("persistent", true, true, true),
  ATTR_EXCL ("section", true, true, true),
  ATTR_EXCL (NULL, false, false, false),
};

static const struct attribute_spec::exclusions attr_stack_protect_exclusions[] =
{
  ATTR_EXCL ("stack_protect", true, false, false),
  ATTR_EXCL ("no_stack_protector", true, false, false),
  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 } */
  { "signed_bool_precision",  1, 1, false, true, false, true,
			      handle_signed_bool_precision_attribute, NULL },
  { "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,
			      attr_stack_protect_exclusions },
  { "no_stack_protector",     0, 0, true, false, false, false,
			      handle_no_stack_protector_function_attribute,
			      attr_stack_protect_exclusions },
  { "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 },
  { "uninitialized",	      0, 0, true, false, false, false,
			      handle_uninitialized_attribute, NULL },
  { "retain",                 0, 0, true,  false, false, false,
			      handle_retain_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_section_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 },
  { "strict_flex_array",      1, 1, true, false, false, false,
			      handle_strict_flex_array_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, 2, 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 },
  { "unavailable",            0, 1, false, false, false, false,
			      handle_unavailable_attribute, NULL },
  { "vector_size",	      1, 1, false, true, false, true,
			      handle_vector_size_attribute, NULL },
  { "vector_mask",	      0, 0, false, true, false, true,
			      handle_vector_mask_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 },
  { "no_sanitize_coverage",   0, 0, true, false, false, false,
			      handle_no_sanitize_coverage_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 },
  { "zero_call_used_regs",    1, 1, true, false, false, false,
			      handle_zero_call_used_regs_attribute, NULL },
  /* For internal use only (marking of function arguments).
     The name contains a space to prevent its usage in source code.  */
  { "arg spec",		      1, -1, true, false, false, false,
			      handle_argspec_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 },
  { "non overlapping",	      0, 0, true, false, false, false,
			      handle_non_overlapping_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 },
  { "assume",		      1, 1, false, false, false, false,
			      handle_assume_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_special_var_sec_attribute, attr_section_exclusions },
  { "persistent",	      0, 0, true,  false, false, false,
			      handle_special_var_sec_attribute, attr_section_exclusions },
  { "access",		      1, 3, false, true, true, false,
			      handle_access_attribute, NULL },
  /* Attributes used by Objective-C.  */
  { "NSObject",		      0, 0, true, false, false, false,
			      handle_nsobject_attribute, NULL },
  { "objc_root_class",	      0, 0, true, false, false, false,
			      handle_objc_root_class_attribute, NULL },
  { "objc_nullability",	      1, 1, true, false, false, false,
			      handle_objc_nullability_attribute, NULL },
  { "*dealloc",                1, 2, true, false, false, false,
			      handle_dealloc_attribute, NULL },
  { "tainted_args",	      0, 0, true,  false, false, false,
			      handle_tainted_args_attribute, NULL },
  { "fd_arg",             1, 1, false, true, true, false,
            handle_fd_arg_attribute, NULL},      
  { "fd_arg_read",        1, 1, false, true, true, false,
            handle_fd_arg_attribute, NULL},
  { "fd_arg_write",       1, 1, false, true, true, false,
            handle_fd_arg_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 FN (which is either a function declaration or 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 (and possibly
   adjusted by ADJUST_POS).  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.

   N.B. This function modifies POS.  */

tree
positional_argument (const_tree fn, const_tree atname, tree &pos,
		     tree_code code, int argno /* = 0 */,
		     int flags /* = posargflags () */)
{
  const_tree fndecl = TYPE_P (fn) ? NULL_TREE : fn;
  const_tree fntype = TYPE_P (fn) ? fn : TREE_TYPE (fn);
  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;

   /* ADJUST_POS is non-zero in C++ when the function type has invisible
      parameters generated by the compiler, such as the in-charge or VTT
      parameters.  */
  const int adjust_pos = maybe_adjust_arg_pos_for_attribute (fndecl);

  /* 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) + adjust_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.   Invisible parameters may have been added by
     the compiler, so adjust the position accordingly.  */
  unsigned HOST_WIDE_INT ipos = tree_to_uhwi (pos) + adjust_pos;

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

  if (tree argtype = type_argument_type (fntype, ipos))
    {
      if (argtype == error_mark_node)
	return NULL_TREE;

      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 build_int_cst (TREE_TYPE (pos), ipos);
}

/* Return the first of DECL or TYPE attributes installed in NODE if it's
   a DECL, or TYPE attributes if it's a TYPE, or null otherwise.  */

static tree
decl_or_type_attrs (tree node)
{
  if (DECL_P (node))
    {
      if (tree attrs = DECL_ATTRIBUTES (node))
	return attrs;

      tree type = TREE_TYPE (node);
      if (type == error_mark_node)
	return NULL_TREE;
      return TYPE_ATTRIBUTES (type);
    }

  if (TYPE_P (node))
    return TYPE_ATTRIBUTES (node);

  return NULL_TREE;
}

/* Given a pair of NODEs for arbitrary DECLs or TYPEs, validate one or
   two integral or string attribute arguments NEWARGS to be applied to
   NODE[0] for the absence of conflicts with the same attribute arguments
   already applied to NODE[1]. Issue a warning for conflicts and return
   false.  Otherwise, when no conflicts are found, return true.  */

static bool
validate_attr_args (tree node[2], tree name, tree newargs[2])
{
  /* First validate the arguments against those already applied to
     the same declaration (or type).  */
  tree self[2] = { node[0], node[0] };
  if (node[0] != node[1] && !validate_attr_args (self, name, newargs))
    return false;

  if (!node[1])
    return true;

  /* Extract the same attribute from the previous declaration or type.  */
  tree prevattr = decl_or_type_attrs (node[1]);
  const char* const namestr = IDENTIFIER_POINTER (name);
  prevattr = lookup_attribute (namestr, prevattr);
  if (!prevattr)
    return true;

  /* Extract one or both attribute arguments.  */
  tree prevargs[2];
  prevargs[0] = TREE_VALUE (TREE_VALUE (prevattr));
  prevargs[1] = TREE_CHAIN (TREE_VALUE (prevattr));
  if (prevargs[1])
    prevargs[1] = TREE_VALUE (prevargs[1]);

  /* Both arguments must be equal or, for the second pair, neither must
     be provided to succeed.  */
  bool arg1eq, arg2eq;
  if (TREE_CODE (newargs[0]) == INTEGER_CST)
    {
      arg1eq = tree_int_cst_equal (newargs[0], prevargs[0]);
      if (newargs[1] && prevargs[1])
	arg2eq = tree_int_cst_equal (newargs[1], prevargs[1]);
      else
	arg2eq = newargs[1] == prevargs[1];
    }
  else if (TREE_CODE (newargs[0]) == STRING_CST)
    {
      const char *s0 = TREE_STRING_POINTER (newargs[0]);
      const char *s1 = TREE_STRING_POINTER (prevargs[0]);
      arg1eq = strcmp (s0, s1) == 0;
      if (newargs[1] && prevargs[1])
	{
	  s0 = TREE_STRING_POINTER (newargs[1]);
	  s1 = TREE_STRING_POINTER (prevargs[1]);
	  arg2eq = strcmp (s0, s1) == 0;
	}
      else
	arg2eq = newargs[1] == prevargs[1];
    }
  else
    gcc_unreachable ();

  if (arg1eq && arg2eq)
    return true;

  /* If the two locations are different print a note pointing to
     the previous one.  */
  const location_t curloc = input_location;
  const location_t prevloc =
    DECL_P (node[1]) ? DECL_SOURCE_LOCATION (node[1]) : curloc;

  /* Format the attribute specification for convenience.  */
  char newspec[80], prevspec[80];
  if (newargs[1])
    snprintf (newspec, sizeof newspec, "%s (%s, %s)", namestr,
	      print_generic_expr_to_str (newargs[0]),
	      print_generic_expr_to_str (newargs[1]));
  else
    snprintf (newspec, sizeof newspec, "%s (%s)", namestr,
	      print_generic_expr_to_str (newargs[0]));

  if (prevargs[1])
    snprintf (prevspec, sizeof prevspec, "%s (%s, %s)", namestr,
	      print_generic_expr_to_str (prevargs[0]),
	      print_generic_expr_to_str (prevargs[1]));
  else
    snprintf (prevspec, sizeof prevspec, "%s (%s)", namestr,
	      print_generic_expr_to_str (prevargs[0]));

  if (warning_at (curloc, OPT_Wattributes,
		  "ignoring attribute %qs because it conflicts "
		  "with previous %qs",
		  newspec, prevspec)
      && curloc != prevloc)
    inform (prevloc, "previous declaration here");

  return false;
}

/* Convenience wrapper for validate_attr_args to validate a single
   attribute argument.  Used by handlers for attributes that take
   just a single argument.  */

static bool
validate_attr_arg (tree node[2], tree name, tree newarg)
{
  tree argarray[2] = { newarg, NULL_TREE };
  return validate_attr_args (node, name, argarray);
}

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

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

static tree
handle_signed_bool_precision_attribute (tree *node, tree name, tree args,
					int, bool *no_add_attrs)
{
  *no_add_attrs = true;
  if (!flag_gimple)
    {
      warning (OPT_Wattributes, "%qE attribute ignored", name);
      return NULL_TREE;
    }

  if (!TYPE_P (*node) || TREE_CODE (*node) != BOOLEAN_TYPE)
    {
      warning (OPT_Wattributes, "%qE attribute only supported on "
	       "boolean types", name);
      return NULL_TREE;
    }

  unsigned HOST_WIDE_INT prec = HOST_WIDE_INT_M1U;
  if (tree_fits_uhwi_p (TREE_VALUE (args)))
    prec = tree_to_uhwi (TREE_VALUE (args));
  if (prec > MAX_FIXED_MODE_SIZE)
    {
      warning (OPT_Wattributes, "%qE attribute with unsupported boolean "
	       "precision", name);
      return NULL_TREE;
    }

  tree new_type = build_nonstandard_boolean_type (prec);
  *node = lang_hooks.types.reconstruct_complex_type (*node, new_type);

  return NULL_TREE;
}

/* 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 a "no_sanitize_coverage" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_no_sanitize_coverage_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 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 "no_stack_protector" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_no_stack_protector_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 "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) == FIELD_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 "retain" attribute; arguments as in
   struct attribute_spec.handler.  */

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

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

  return NULL_TREE;
}

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

static tree
handle_uninitialized_attribute (tree *node, tree name, tree ARG_UNUSED (args),
				int ARG_UNUSED (flags), bool *no_add_attrs)
{
  tree decl = *node;
  if (!VAR_P (decl))
    {
      warning (OPT_Wattributes, "%qE attribute ignored because %qD "
	       "is not a variable", name, decl);
      *no_add_attrs = true;
    }
  else if (TREE_STATIC (decl) || DECL_EXTERNAL (decl))
    {
      warning (OPT_Wattributes, "%qE attribute ignored because %qD "
	       "is not a local variable", name, decl);
      *no_add_attrs = true;
    }

  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 || TREE_CODE (arg) == FUNCTION_DECL)
    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;
	}

      /* Copy any quals and attributes to the new type.  */
      *node = build_type_attribute_qual_variant (typefm, TYPE_ATTRIBUTES (type),
						 TYPE_QUALS (type));
      if (TYPE_USER_ALIGN (type))
	*node = build_aligned_type (*node, TYPE_ALIGN (type));
    }

  return NULL_TREE;
}

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

static tree
handle_section_attribute (tree *node, tree name, tree args,
			  int flags, bool *no_add_attrs)
{
  tree decl = *node;
  tree res = NULL_TREE;
  tree argval = TREE_VALUE (args);
  const char* new_section_name;

  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 (argval) != 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;
    }

  new_section_name = TREE_STRING_POINTER (argval);

  /* The decl may have already been given a section attribute
     from a previous declaration.  Ensure they match.  */
  if (const char* const old_section_name = DECL_SECTION_NAME (decl))
    if (strcmp (old_section_name, new_section_name) != 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;
    }

  if (!validate_attr_arg (node, name, argval))
    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, new_section_name);
      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 > curalign
		   || (lastalign == curalign
		       && (DECL_USER_ALIGN (last_decl)
			   > DECL_USER_ALIGN (decl))));
      if (note)
	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) == 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 "strict_flex_array" attribute; arguments as in
   struct attribute_spec.handler.  */

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

  /* This attribute only applies to field decls of a structure.  */
  if (TREE_CODE (decl) != FIELD_DECL)
    {
      error_at (DECL_SOURCE_LOCATION (decl),
		"%qE attribute may not be specified for %q+D", name, decl);
      *no_add_attrs = true;
    }
  /* This attribute only applies to field with array type.  */
  else if (TREE_CODE (TREE_TYPE (decl)) != ARRAY_TYPE)
    {
      error_at (DECL_SOURCE_LOCATION (decl),
		"%qE attribute may not be specified for a non-array field",
		name);
      *no_add_attrs = true;
    }
  else if (TREE_CODE (argval) != INTEGER_CST)
    {
      error_at (DECL_SOURCE_LOCATION (decl),
		"%qE attribute argument not an integer", name);
      *no_add_attrs = true;
    }
  else if (!tree_fits_uhwi_p (argval) || tree_to_uhwi (argval) > 3)
    {
      error_at (DECL_SOURCE_LOCATION (decl),
		"%qE attribute argument %qE is not an integer constant"
		" between 0 and 3", name, argval);
      *no_add_attrs = true;
    }

  return NULL_TREE;
}

/* 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" or "persistent" attribute; arguments as in
   struct attribute_spec.handler.
   This generic handler is used for "special variable sections" that allow the
   section name to be set using a dedicated attribute.  Additional validation
   is performed for the specific properties of the section corresponding to the
   attribute.
   The ".noinit" section *is not* loaded by the program loader, and is not
   initialized by the runtime startup code.
   The ".persistent" section *is* loaded by the program loader, but is not
   initialized by the runtime startup code.  */
static tree
handle_special_var_sec_attribute (tree *node, tree name, tree args,
				  int flags, bool *no_add_attrs)
{
  tree decl = *node;
  tree res = NULL_TREE;

  /* First perform generic validation common to "noinit" and "persistent"
     attributes.  */
  if (!targetm_common.have_named_sections)
    {
      error_at (DECL_SOURCE_LOCATION (decl),
		"section attributes are not supported for this target");
      goto fail;
    }

  if (!VAR_P (decl))
    {
      warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wattributes,
		  "ignoring %qE attribute not set on a variable",
		  name);
      goto fail;
    }

  if (VAR_P (decl)
      && current_function_decl != NULL_TREE
      && !TREE_STATIC (decl))
    {
      error_at (DECL_SOURCE_LOCATION (decl),
		"%qE attribute cannot be specified for local variables",
		name);
      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", decl);
      goto fail;
    }

  if (!targetm.have_switchable_bss_sections)
    {
      error ("%qE attribute is specific to ELF targets", name);
      goto fail;
    }

  if (TREE_READONLY (decl))
    {
      warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wattributes,
		  "ignoring %qE attribute set on const variable",
		  name);
      goto fail;
    }

  /* Now validate noinit/persistent individually.  */
  if (strcmp (IDENTIFIER_POINTER (name), "noinit") == 0)
    {
      if (DECL_INITIAL (decl))
	{
	  warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wattributes,
		      "ignoring %qE attribute set on initialized variable",
		      name);
	  goto fail;
	}
      /* If this var is thought to be common, then change this.  "noinit"
	 variables must be placed in an explicit ".noinit" section.  */
      DECL_COMMON (decl) = 0;
    }
  else if (strcmp (IDENTIFIER_POINTER (name), "persistent") == 0)
    {
      if (DECL_COMMON (decl) || DECL_INITIAL (decl) == NULL_TREE)
	{
	  warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wattributes,
		      "ignoring %qE attribute set on uninitialized variable",
		      name);
	  goto fail;
	}
    }
  else
    gcc_unreachable ();

  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))
    return res;

fail:
  *no_add_attrs = true;
  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);

  tree reftype = TYPE_P (ref) ? ref : TREE_TYPE (ref);

  if (DECL_P (decl))
    {
      if ((VAR_P (decl)
	   && (TREE_CODE (ref) == FUNCTION_DECL
	       || (EXPR_P (ref)
		   && POINTER_TYPE_P (reftype)
		   && FUNC_OR_METHOD_TYPE_P (TREE_TYPE (reftype)))))
	  || (TREE_CODE (decl) == FUNCTION_DECL
	      && (VAR_P (ref)
		  || (EXPR_P (ref)
		      && !FUNC_OR_METHOD_TYPE_P (reftype)
		      && (!POINTER_TYPE_P (reftype)
			  || !FUNC_OR_METHOD_TYPE_P (TREE_TYPE (reftype)))))))
	{
	  /* 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,  EXPR_P (ref) ? NULL_TREE : 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.  */
  if (DECL_P (ref)
      && TREE_THIS_VOLATILE (ref)
      && FUNC_OR_METHOD_TYPE_P (reftype))
    TREE_THIS_VOLATILE (decl) = true;

  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.  Pass in REF if it's a DECL
     or a type but not if it's an expression.  Set ATTR_FLAG_INTERNAL
     since the attributes' arguments may be in their internal form.  */
  for (tree at = attrs; at; at = TREE_CHAIN (at))
    decl_attributes (node, at, flags | ATTR_FLAG_INTERNAL,
		     EXPR_P (ref) ? NULL_TREE : 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;
}

/* If ALLOC_DECL and DEALLOC_DECL are a pair of user-defined functions,
   if they are declared inline issue warnings and return null.  Otherwise
   create attribute noinline, install it in ALLOC_DECL, and return it.
   Otherwise return null. */

static tree
maybe_add_noinline (tree name, tree alloc_decl, tree dealloc_decl,
		    bool *no_add_attrs)
{
  if (fndecl_built_in_p (alloc_decl) || fndecl_built_in_p (dealloc_decl))
    return NULL_TREE;

  /* When inlining (or optimization) is enabled and the allocator and
     deallocator are not built-in functions, ignore the attribute on
     functions declared inline since it could lead to false positives
     when inlining one or the other call would wind up calling
     a mismatched allocator or  deallocator.  */
  if ((optimize && DECL_DECLARED_INLINE_P (alloc_decl))
      || lookup_attribute ("always_inline", DECL_ATTRIBUTES (alloc_decl)))
    {
      warning (OPT_Wattributes,
	       "%<%E (%E)%> attribute ignored on functions "
	       "declared %qs", name, DECL_NAME (dealloc_decl), "inline");
      *no_add_attrs = true;
      return NULL_TREE;
    }

  if ((optimize && DECL_DECLARED_INLINE_P (dealloc_decl))
      || lookup_attribute ("always_inline", DECL_ATTRIBUTES (dealloc_decl)))
    {
      warning (OPT_Wattributes,
	       "%<%E (%E)%> attribute ignored with deallocation "
	       "functions declared %qs",
	       name, DECL_NAME (dealloc_decl), "inline");
      inform (DECL_SOURCE_LOCATION (dealloc_decl),
	      "deallocation function declared here" );
      *no_add_attrs = true;
      return NULL_TREE;
    }

  /* Disable inlining for non-standard deallocators to avoid false
     positives due to mismatches between the inlined implementation
     of one and not the other pair of functions.  */
  tree attr = tree_cons (get_identifier ("noinline"), NULL_TREE, NULL_TREE);
  decl_attributes (&alloc_decl, attr, 0);
  return attr;
}

/* Handle the "malloc" attribute.  */

static tree
handle_malloc_attribute (tree *node, tree name, tree args, int flags,
			 bool *no_add_attrs)
{
  if (flags & ATTR_FLAG_INTERNAL)
    /* Recursive call.  */
    return NULL_TREE;

  tree fndecl = *node;

  if (TREE_CODE (*node) != FUNCTION_DECL)
    {
      warning (OPT_Wattributes, "%qE attribute ignored; valid only "
	       "for functions",
	       name);
      *no_add_attrs = true;
      return NULL_TREE;
    }

  tree rettype = TREE_TYPE (TREE_TYPE (*node));
  if (!POINTER_TYPE_P (rettype))
    {
      warning (OPT_Wattributes, "%qE attribute ignored on functions "
	       "returning %qT; valid only for pointer return types",
	       name, rettype);
      *no_add_attrs = true;
      return NULL_TREE;
    }

  if (!args)
    {
      /* Only the form of the attribute with no arguments declares
	 a function malloc-like.  */
      DECL_IS_MALLOC (*node) = 1;
      return NULL_TREE;
    }

  tree dealloc = TREE_VALUE (args);
  if (error_operand_p (dealloc))
    {
      /* If the argument is in error it will have already been diagnosed.
	 Avoid issuing redundant errors here.  */
      *no_add_attrs = true;
      return NULL_TREE;
    }

  STRIP_NOPS (dealloc);
  if (TREE_CODE (dealloc) == ADDR_EXPR)
    {
      /* In C++ the argument may be wrapped in a cast to disambiguate
	 one of a number of overloads (such as operator delete).  To
	 make things interesting, the cast looks different between
	 different C++ versions.  Strip it and install the attribute
	 with the disambiguated function.  */
      dealloc = TREE_OPERAND (dealloc, 0);

      *no_add_attrs = true;
      tree attr = tree_cons (NULL_TREE, dealloc, TREE_CHAIN (args));
      attr = build_tree_list (name, attr);
      return decl_attributes (node, attr, 0);
    }

  if (TREE_CODE (dealloc) != FUNCTION_DECL)
    {
      if (TREE_CODE (dealloc) == OVERLOAD)
	{
	  /* Handle specially the common case of specifying one of a number
	     of overloads, such as operator delete.  */
	  error ("%qE attribute argument 1 is ambiguous", name);
	  inform (input_location,
		  "use a cast to the expected type to disambiguate");
	  *no_add_attrs = true;
	  return NULL_TREE;
	}

      error ("%qE attribute argument 1 does not name a function", name);
      if (DECL_P (dealloc))
	inform (DECL_SOURCE_LOCATION (dealloc),
		"argument references a symbol declared here");
      *no_add_attrs = true;
      return NULL_TREE;
    }

  /* Mentioning the deallocation function qualifies as its use.  */
  TREE_USED (dealloc) = 1;

  tree fntype = TREE_TYPE (dealloc);
  tree argpos = TREE_CHAIN (args) ? TREE_VALUE (TREE_CHAIN (args)) : NULL_TREE;
  if (!argpos)
    {
      tree argtypes = TYPE_ARG_TYPES (fntype);
      if (!argtypes)
	{
	  /* Reject functions without a prototype.  */
	  error ("%qE attribute argument 1 must take a pointer "
		 "type as its first argument", name);
	  inform (DECL_SOURCE_LOCATION (dealloc),
		  "referenced symbol declared here");
	  *no_add_attrs = true;
	  return NULL_TREE;
	}

      tree argtype = TREE_VALUE (argtypes);
      if (TREE_CODE (argtype) != POINTER_TYPE)
	{
	  /* Reject functions that don't take a pointer as their first
	     argument.  */
	  error ("%qE attribute argument 1 must take a pointer type "
		 "as its first argument; have %qT", name, argtype);
	  inform (DECL_SOURCE_LOCATION (dealloc),
		  "referenced symbol declared here");
	  *no_add_attrs = true;
	  return NULL_TREE;
	}

      /* Disable inlining for non-standard deallocators to avoid false
	 positives (or warn if either function is explicitly inline).  */
      tree at_noinline =
	maybe_add_noinline (name, fndecl, dealloc, no_add_attrs);
      if (*no_add_attrs)
	return NULL_TREE;

      /* Add attribute *dealloc to the deallocator function associating
	 it with this one.  Ideally, the attribute would reference
	 the DECL of the deallocator but since that changes for each
	 redeclaration, use DECL_NAME instead.  (DECL_ASSEMBLER_NAME
	 need not be set at this point and setting it here is too early.  */
      tree attrs = build_tree_list (NULL_TREE, DECL_NAME (fndecl));
      attrs = tree_cons (get_identifier ("*dealloc"), attrs, at_noinline);
      decl_attributes (&dealloc, attrs, 0);
      return NULL_TREE;
    }

  /* Validate the positional argument.  */
  argpos = positional_argument (fntype, name, argpos, POINTER_TYPE);
  if (!argpos)
    {
      *no_add_attrs = true;
      return NULL_TREE;
    }

  /* As above, disable inlining for non-standard deallocators to avoid
     false positives (or warn).  */
  tree at_noinline =
    maybe_add_noinline (name, fndecl, dealloc, no_add_attrs);
  if (*no_add_attrs)
    return NULL_TREE;

  /* It's valid to declare the same function with multiple instances
     of attribute malloc, each naming the same or different deallocator
     functions, and each referencing either the same or a different
     positional argument.  */
  tree attrs = tree_cons (NULL_TREE, argpos, NULL_TREE);
  attrs = tree_cons (NULL_TREE, DECL_NAME (fndecl), attrs);
  attrs = tree_cons (get_identifier ("*dealloc"), attrs, at_noinline);
  decl_attributes (&dealloc, attrs, 0);
  return NULL_TREE;
}

/* Handle the internal "*dealloc" attribute added for functions declared
   with the one- and two-argument forms of attribute malloc.  Add it
   to *NODE unless it's already there with the same arguments.  */

static tree
handle_dealloc_attribute (tree *node, tree name, tree args, int,
			  bool *no_add_attrs)
{
  tree fndecl = *node;

  tree attrs = DECL_ATTRIBUTES (fndecl);
  if (!attrs)
    return NULL_TREE;

  tree arg = TREE_VALUE (args);
  args = TREE_CHAIN (args);
  tree arg_pos = args ? TREE_VALUE (args) : integer_zero_node;

  gcc_checking_assert ((DECL_P (arg)
			&& fndecl_built_in_p (arg, BUILT_IN_NORMAL))
		       || TREE_CODE (arg) == IDENTIFIER_NODE);

  const char* const namestr = IDENTIFIER_POINTER (name);
  for (tree at = attrs; (at = lookup_attribute (namestr, at));
       at = TREE_CHAIN (at))
    {
      tree alloc = TREE_VALUE (at);
      if (!alloc)
	continue;

      tree pos = TREE_CHAIN (alloc);
      alloc = TREE_VALUE (alloc);
      pos = pos ? TREE_VALUE (pos) : integer_zero_node;
      gcc_checking_assert ((DECL_P (alloc)
			    && fndecl_built_in_p (alloc, BUILT_IN_NORMAL))
			   || TREE_CODE (alloc) == IDENTIFIER_NODE);

      if (alloc == arg && tree_int_cst_equal (pos, arg_pos))
	{
	  /* The function already has the attribute either without any
	     arguments or with the same arguments as the attribute that's
	     being added.  Return without adding another copy.  */
	  *no_add_attrs = true;
	  return NULL_TREE;
	}
    }

  return NULL_TREE;
}

/* Handle the "alloc_size (argpos1 [, argpos2])" function type attribute.
   *NODE is the type of the function the attribute is being applied to.  */

static tree
handle_alloc_size_attribute (tree *node, tree name, tree args,
			     int ARG_UNUSED (flags), bool *no_add_attrs)
{
  tree fntype = *node;
  tree rettype = TREE_TYPE (fntype);
  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;
    }

  tree newargs[2] = { NULL_TREE, 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 (fntype, name, pos, INTEGER_TYPE,
					  next || i > 1 ? i : 0))
	{
	  TREE_VALUE (args) = val;
	  newargs[i - 1] = val;
	}
      else
	{
	  *no_add_attrs = true;
	  return NULL_TREE;
	}

      args = next;
    }

  if (!validate_attr_args (node, name, newargs))
    *no_add_attrs = true;

  return NULL_TREE;
}


/* Handle an "alloc_align (argpos)" attribute.  */

static tree
handle_alloc_align_attribute (tree *node, tree name, tree args, int,
			      bool *no_add_attrs)
{
  tree fntype = *node;
  tree rettype = TREE_TYPE (fntype);
  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 (tree val = positional_argument (*node, name, TREE_VALUE (args),
				      INTEGER_TYPE))
    if (validate_attr_arg (node, name, val))
      return NULL_TREE;

  *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 argument %E is not an integer constant",
		   name, val);
	  *no_add_attrs = true;
	  return NULL_TREE;
	}
      else if (tree_int_cst_sgn (val) < 0)
	{
	  warning (OPT_Wattributes,
		   "%qE attribute argument %E is not positive", 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_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 the internal-only "arg spec" attribute.  */

static tree
handle_argspec_attribute (tree *, tree, tree args, int, bool *)
{
  /* Verify the attribute has one or two arguments and their kind.  */
  gcc_assert (args && TREE_CODE (TREE_VALUE (args)) == STRING_CST);
  for (tree next = TREE_CHAIN (args); next; next = TREE_CHAIN (next))
    {
      tree val = TREE_VALUE (next);
      gcc_assert (DECL_P (val) || EXPR_P (val));
    }
  return NULL_TREE;
}

/* Handle the internal-only "fn spec" attribute.  */

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 an "non overlapping" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_non_overlapping_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 = get_attribute_name (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_CODE (decl) == CONCEPT_DECL)
	TREE_DEPRECATED (decl) = 1;
      else if (TREE_CODE (decl) == LABEL_DECL)
	{
	  pedwarn (input_location, OPT_Wattributes, "%qE attribute ignored",
		   name);
	  *no_add_attrs = true;
	  return NULL_TREE;
	}
      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 (type);
	  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;
}

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

static tree
handle_unavailable_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 ("the message attached to %<unavailable%> 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_UNAVAILABLE (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_UNAVAILABLE (*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 vector components %wu not a power of two", nunits);
      else
	warning (OPT_Wattributes,
		 "number of vector components %wu not a power of two", nunits);
      return NULL_TREE;
    }

  if (nunits >= (unsigned HOST_WIDE_INT)INT_MAX)
    {
      if (error_p)
	error ("number of vector components %wu exceeds %d",
	       nunits, INT_MAX - 1);
      else
	warning (OPT_Wattributes,
		 "number of vector components %wu exceeds %d",
		 nunits, INT_MAX - 1);
      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;

  gcc_checking_assert (args != NULL);

  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 a "vector_mask" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_vector_mask_attribute (tree *node, tree name, tree,
			      int ARG_UNUSED (flags),
			      bool *no_add_attrs)
{
  *no_add_attrs = true;
  if (!flag_gimple)
    {
      warning (OPT_Wattributes, "%qE attribute ignored", name);
      return NULL_TREE;
    }

  /* Determine the "base" type to apply the attribute to.  */
  tree type = type_for_vector_size (*node);
  if (!VECTOR_TYPE_P (type) || VECTOR_BOOLEAN_TYPE_P (type))
    {
      warning (OPT_Wattributes, "%qE attribute only supported on "
	       "non-mask vector types", name);
      return NULL_TREE;
    }

  tree new_type = truth_type_for (type);

  /* 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 "fd_arg", "fd_arg_read" and "fd_arg_write" attributes */

static tree
handle_fd_arg_attribute (tree *node, tree name, tree args,
                              int ARG_UNUSED (flags), bool *no_add_attrs)
{
  tree type = *node;
  if (!args)
    {
      if (!prototype_p (type))
        {
          error ("%qE attribute without arguments on a non-prototype", name);
          *no_add_attrs = true;
        }
      return NULL_TREE;
    }

  if (positional_argument (*node, name, TREE_VALUE (args), INTEGER_TYPE))
      return NULL_TREE;

  *no_add_attrs = true;  
  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;
	  if (argtype != error_mark_node)
	    return argtype;
	}
    }

  *nargs = count;
  return NULL_TREE;
}

/* Given a function FNDECL return the function argument at the zero-
   based position ARGNO or null if it can't be found.  */

static tree
get_argument (tree fndecl, unsigned argno)
{
  if (!DECL_P (fndecl))
    return NULL_TREE;

  unsigned i = 0;
  for (tree arg = DECL_ARGUMENTS (fndecl); arg; arg = TREE_CHAIN (arg))
    if (i++ == argno)
      return arg;

  return NULL_TREE;
}

/* Attempt to append attribute access specification ATTRSPEC, optionally
   described by the human-readable string ATTRSTR, for type T, to one in
   ATTRS. VBLIST is an optional list of bounds of variable length array
   parameters described by ATTRSTR.
   Issue warning for conflicts and return null if any are found.
   Return the concatenated access string on success.  */

static tree
append_access_attr (tree node[3], tree attrs, const char *attrstr,
		    const char *attrspec, tree vblist = NULL_TREE)
{
  tree argstr = build_string (strlen (attrspec) + 1, attrspec);
  tree ataccess = tree_cons (NULL_TREE, argstr, vblist);
  ataccess = tree_cons (get_identifier ("access"), ataccess, NULL_TREE);

  /* The access specification being applied.  This may be an implicit
     access spec synthesized for array (or VLA) parameters even for
     a declaration with an explicit access spec already applied, if
     this call corresponds to the first declaration of the function.  */
  rdwr_map new_idxs;
  init_attr_rdwr_indices (&new_idxs, ataccess);

  /* The current access specification alrady applied.  */
  rdwr_map cur_idxs;
  init_attr_rdwr_indices (&cur_idxs, attrs);

  tree args = TYPE_ARG_TYPES (node[0]);
  int argpos = 0;
  std::string spec;
  for (tree arg = args; arg; arg = TREE_CHAIN (arg), argpos++)
    {
      const attr_access* const newa = new_idxs.get (argpos);

      if (!newa)
	continue;

      /* The map has two equal entries for each pointer argument that
	 has an associated size argument.  Process just the entry for
	 the former.  */
      if ((unsigned)argpos != newa->ptrarg)
	continue;

      const attr_access* const cura = cur_idxs.get (argpos);
      if (!cura)
	{
	  /* The new attribute needs to be added.  */
	  tree str = newa->to_internal_string ();
	  spec += TREE_STRING_POINTER (str);
	  continue;
	}

      /* The new access spec refers to an array/pointer argument for
	 which an access spec already exists.  Check and diagnose any
	 conflicts.  If no conflicts are found, merge the two.  */

      if (!attrstr)
	{
	  tree str = NULL_TREE;
	  if (newa->mode != access_deferred)
	    str = newa->to_external_string ();
	  else if (cura->mode != access_deferred)
	    str = cura->to_external_string ();
	  if (str)
	    attrstr = TREE_STRING_POINTER (str);
	}

      location_t curloc = input_location;
      if (node[2] && DECL_P (node[2]))
	curloc = DECL_SOURCE_LOCATION (node[2]);

      location_t prevloc = UNKNOWN_LOCATION;
      if (node[1] && DECL_P (node[1]))
	prevloc = DECL_SOURCE_LOCATION (node[1]);

      if (newa->mode != cura->mode
	  && newa->mode != access_deferred
	  && cura->mode != access_deferred
	  && newa->internal_p == cura->internal_p)
	{
	  /* Mismatch in access mode.  */
	  auto_diagnostic_group d;
	  if (warning_at (curloc, OPT_Wattributes,
			  "attribute %qs mismatch with mode %qs",
			  attrstr, cura->mode_names[cura->mode])
	      && prevloc != UNKNOWN_LOCATION)
	    inform (prevloc, "previous declaration here");
	  continue;
	}

      /* Set if PTRARG refers to a VLA with an unspecified bound (T[*]).
	 Be prepared for either CURA or NEWA to refer to it, depending
	 on which happens to come first in the declaration.  */
      const bool cur_vla_ub = (cura->internal_p
			       && cura->sizarg == UINT_MAX
			       && cura->minsize == HOST_WIDE_INT_M1U);
      const bool new_vla_ub = (newa->internal_p
			       && newa->sizarg == UINT_MAX
			       && newa->minsize == HOST_WIDE_INT_M1U);

      if (newa->sizarg != cura->sizarg
	  && attrstr
	  && (!(cur_vla_ub ^ new_vla_ub)
	      || (!cura->internal_p && !newa->internal_p)))
	{
	  /* Avoid diagnosing redeclarations of functions with no explicit
	     attribute access that add one.  */
	  if (newa->mode == access_deferred
	      && cura->mode != access_deferred
	      && newa->sizarg == UINT_MAX
	      && cura->sizarg != UINT_MAX)
	    continue;

	  if (cura->mode == access_deferred
	      && newa->mode != access_deferred
	      && cura->sizarg == UINT_MAX
	      && newa->sizarg != UINT_MAX)
	    continue;

	  /* The two specs designate different size arguments.  It's okay
	     for the explicit spec to specify a size where none is provided
	     by the implicit (VLA) one, as in:
	       __attribute__ ((access (read_write, 1, 2)))
	       void f (int*, int);
	     but not for two explicit access attributes to do that.  */
	  bool warned = false;

	  auto_diagnostic_group d;

	  if (newa->sizarg == UINT_MAX)
	    /* Mismatch in the presence of the size argument.  */
	    warned = warning_at (curloc, OPT_Wattributes,
				 "attribute %qs missing positional argument 2 "
				 "provided in previous designation by argument "
				 "%u", attrstr, cura->sizarg + 1);
	  else if (cura->sizarg == UINT_MAX)
	    /* Mismatch in the presence of the size argument.  */
	    warned = warning_at (curloc, OPT_Wattributes,
				 "attribute %qs positional argument 2 "
				 "missing in previous designation",
				 attrstr);
	  else if (newa->internal_p || cura->internal_p)
	    /* Mismatch in the value of the size argument and a VLA bound.  */
	    warned = warning_at (curloc, OPT_Wattributes,
				 "attribute %qs positional argument 2 "
				 "conflicts with previous designation "
				 "by argument %u",
				 attrstr, cura->sizarg + 1);
	  else
	    /* Mismatch in the value of the size argument between two
	       explicit access attributes.  */
	    warned = warning_at (curloc, OPT_Wattributes,
				 "attribute %qs mismatched positional argument "
				 "values %i and %i",
				 attrstr, newa->sizarg + 1, cura->sizarg + 1);

	  if (warned)
	    {
	      /* If the previous declaration is a function (as opposed
		 to a typedef of one), find the location of the array
		 or pointer argument that uses the conflicting VLA bound
		 and point to it in the note.  */
	      const attr_access* const pa = cura->size ? cura : newa;
	      tree size = pa->size ? TREE_VALUE (pa->size) : NULL_TREE;
	      if (size && DECL_P (size))
		{
		  location_t argloc = UNKNOWN_LOCATION;
		  if (tree arg = get_argument (node[2], pa->ptrarg))
		    argloc = DECL_SOURCE_LOCATION (arg);

		  gcc_rich_location richloc (DECL_SOURCE_LOCATION (size));
		  if (argloc != UNKNOWN_LOCATION)
		    richloc.add_range (argloc);

		  inform (&richloc, "designating the bound of variable "
			  "length array argument %u",
			  pa->ptrarg + 1);
		}
	      else if (prevloc != UNKNOWN_LOCATION)
		inform (prevloc, "previous declaration here");
	    }

	  continue;
	}

      if (newa->internal_p == cura->internal_p)
	continue;

      /* Merge the CURA and NEWA.  */
      attr_access merged = *newa;

      /* VLA seen in a declaration takes precedence.  */
      if (cura->minsize == HOST_WIDE_INT_M1U)
	merged.minsize = HOST_WIDE_INT_M1U;

      /* Use the explicitly specified size positional argument.  */
      if (cura->sizarg != UINT_MAX)
	merged.sizarg = cura->sizarg;

      /* Use the explicitly specified mode.  */
      if (merged.mode == access_deferred)
	merged.mode = cura->mode;

      tree str = merged.to_internal_string ();
      spec += TREE_STRING_POINTER (str);
    }

  if (!spec.length ())
    return NULL_TREE;

  return build_string (spec.length (), spec.c_str ());
}

/* Convenience wrapper for the above.  */

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

  return append_access_attr (node, attrs, attrstr, attrspec);
}

/* Handle the access attribute for function type NODE[0], with the function
   DECL optionally in NODE[1].  The handler is called both in response to
   an explict attribute access on a declaration with a mode and one or two
   positional arguments, and for internally synthesized access specifications
   with a string argument optionally followd by a DECL or expression
   representing a VLA bound.  To speed up parsing, the handler transforms
   the attribute and its arguments into a string.  */

static tree
handle_access_attribute (tree node[3], tree name, tree args, int flags,
			 bool *no_add_attrs)
{
  tree attrs = TYPE_ATTRIBUTES (*node);
  tree type = *node;
  if (POINTER_TYPE_P (type))
    {
      tree ptype = TREE_TYPE (type);
      if (FUNC_OR_METHOD_TYPE_P (ptype))
	type = ptype;
    }

  *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;
    }

  tree access_mode = TREE_VALUE (args);
  if (TREE_CODE (access_mode) == STRING_CST)
    {
      const char* const str = TREE_STRING_POINTER (access_mode);
      if (*str == '+')
	{
	  /* This is a request to merge an internal specification for
	     a function declaration involving arrays but no explicit
	     attribute access.  */
	  tree vblist = TREE_CHAIN (args);
	  tree axstr = append_access_attr (node, attrs, NULL, str + 1,
					   vblist);
	  if (!axstr)
	    return NULL_TREE;

	  /* Replace any existing access attribute specification with
	     the concatenation above.  */
	  tree axsat = tree_cons (NULL_TREE, axstr, vblist);
	  axsat = tree_cons (name, axsat, NULL_TREE);

	  /* Recursively call self to "replace" the documented/external
	     form of the attribute with the condensend internal form.  */
	  decl_attributes (node, axsat, flags | ATTR_FLAG_INTERNAL);
	  return NULL_TREE;
	}

      if (flags & ATTR_FLAG_INTERNAL)
	{
	  /* This is a recursive call to handle the condensed internal
	     form of the attribute (see below).  Since all validation
	     has been done simply return here, accepting the attribute
	     as is.  */
	  *no_add_attrs = false;
	  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;
  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;
    }
  else if (TREE_CODE (access_mode) != IDENTIFIER_NODE)
    {
      error ("attribute %qE mode %qE is not an identifier; expected one of "
	     "%qs, %qs, %qs, or %qs", name, access_mode,
	     "read_only", "read_write", "write_only", "none");
      return NULL_TREE;
    }

  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;
    }

  int imode;

  {
    const int nmodes = ARRAY_SIZE (attr_access::mode_names);

    for (imode = 0; imode != nmodes; ++imode)
      if (!strncmp (ps, attr_access::mode_names[imode],
		    strlen (attr_access::mode_names[imode])))
	break;

    if (imode == nmodes)
      {
	error ("attribute %qE invalid mode %qs; expected one of "
	       "%qs, %qs, %qs, or %qs", name, access_str,
	       "read_only", "read_write", "write_only", "none");
	return NULL_TREE;
      }
  }

  const ::access_mode mode = static_cast<::access_mode>(imode);

  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 (mode == access_read_write || mode == access_write_only)
    {
      /* 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 = attr_access::mode_chars[mode];
  tree new_attrs = append_access_attr_idxs (node, attrs, attrstr, code, idxs);
  if (!new_attrs)
    return NULL_TREE;

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

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

      new_attrs = tree_cons (NULL_TREE, new_attrs, NULL_TREE);
      new_attrs = tree_cons (name, new_attrs, NULL_TREE);
    }

  /* Recursively call self to "replace" the documented/external form
     of the attribute with the condensed internal form.  */
  decl_attributes (node, new_attrs, flags | ATTR_FLAG_INTERNAL);
  return NULL_TREE;
}

/* Extract attribute "arg spec" from each FNDECL argument that has it,
   build a single attribute access corresponding to all the arguments,
   and return the result.  SKIP_VOIDPTR set to ignore void* parameters
   (used for user-defined functions for which, unlike in for built-ins,
   void* cannot be relied on to determine anything about the access
   through it or whether it even takes place).

   For example, the parameters in the declaration:

     void f (int x, int y, char [x][1][y][3], char [y][2][y][5]);

   result in the following attribute access:

     value: "+^2[*],$0$1^3[*],$1$1"
     list:  < <0, x> <1, y> >

   where the list has a single value which itself is a list, each
   of whose <node>s corresponds to one VLA bound for each of the two
   parameters.  */

tree
build_attr_access_from_parms (tree parms, bool skip_voidptr)
{
  /* Maps each named integral argument DECL seen so far to its position
     in the argument list; used to associate VLA sizes with arguments.  */
  hash_map<tree, unsigned> arg2pos;

  /* The string representation of the access specification for all
     arguments.  */
  std::string spec;
  unsigned argpos = 0;

  /* A TREE_LIST of VLA bounds.  */
  tree vblist = NULL_TREE;

  for (tree arg = parms; arg; arg = TREE_CHAIN (arg), ++argpos)
    {
      if (!DECL_P (arg))
	continue;

      tree argtype = TREE_TYPE (arg);
      if (DECL_NAME (arg) && INTEGRAL_TYPE_P (argtype))
	arg2pos.put (arg, argpos);

      tree argspec = DECL_ATTRIBUTES (arg);
      if (!argspec)
	continue;

      if (POINTER_TYPE_P (argtype))
	{
	  /* void* arguments in user-defined functions could point to
	     anything; skip them.  */
	  tree reftype = TREE_TYPE (argtype);
	  if (skip_voidptr && VOID_TYPE_P (reftype))
	    continue;
	}

      /* Each parameter should have at most one "arg spec" attribute.  */
      argspec = lookup_attribute ("arg spec", argspec);
      if (!argspec)
	continue;

      /* Attribute arg spec should have one or two arguments.  */
      argspec = TREE_VALUE (argspec);

      /* The attribute arg spec string.  */
      tree str = TREE_VALUE (argspec);
      const char *s = TREE_STRING_POINTER (str);

      /* Create the attribute access string from the arg spec string,
	 optionally followed by position of the VLA bound argument if
	 it is one.  */
      {
	size_t specend = spec.length ();
	if (!specend)
	  {
	    spec = '+';
	    specend = 1;
	  }

	/* Format the access string in place.  */
	int len = snprintf (NULL, 0, "%c%u%s",
			    attr_access::mode_chars[access_deferred],
			    argpos, s);
	spec.resize (specend + len + 1);
	sprintf (&spec[specend], "%c%u%s",
		 attr_access::mode_chars[access_deferred],
		 argpos, s);
	/* Trim the trailing NUL.  */
	spec.resize (specend + len);
      }

      /* The (optional) list of expressions denoting the VLA bounds
	 N in ARGTYPE <arg>[Ni]...[Nj]...[Nk].  */
      tree argvbs = TREE_CHAIN (argspec);
      if (argvbs)
	{
	  spec += ',';
	  /* Add ARGVBS to the list.  Their presence is indicated by
	     appending a comma followed by the dollar sign and, when
	     it corresponds to a function parameter, the position of
	     each bound Ni, so it can be distinguished from
	     an unspecified bound (as in T[*]).  The list is in reverse
	     order of arguments and needs to be reversed to access in
	     order.  */
	  vblist = tree_cons (NULL_TREE, argvbs, vblist);

	  unsigned nelts = 0;
	  for (tree vb = argvbs; vb; vb = TREE_CHAIN (vb), ++nelts)
	    {
	      tree bound = TREE_VALUE (vb);
	      if (const unsigned *psizpos = arg2pos.get (bound))
		{
		  /* BOUND previously seen in the parameter list.  */
		  TREE_PURPOSE (vb) = size_int (*psizpos);
		  /* Format the position string in place.  */
		  int len = snprintf (NULL, 0, "$%u", *psizpos);
		  size_t specend = spec.length ();
		  spec.resize (specend + len + 1);
		  sprintf (&spec[specend], "$%u", *psizpos);
		  /* Trim the trailing NUL.  */
		  spec.resize (specend + len);
		}
	      else
		{
		  /* BOUND doesn't name a parameter (it could be a global
		     variable or an expression such as a function call).  */
		  spec += '$';
		}
	    }
	}
    }

  if (!spec.length ())
    return NULL_TREE;

  /* Attribute access takes a two or three arguments.  Wrap VBLIST in
     another list in case it has more nodes than would otherwise fit.  */
  vblist = build_tree_list (NULL_TREE, vblist);

  /* Build a single attribute access with the string describing all
     array arguments and an optional list of any non-parameter VLA
     bounds in order.  */
  tree str = build_string (spec.length (), spec.c_str ());
  tree attrargs = tree_cons (NULL_TREE, str, vblist);
  tree name = get_identifier ("access");
  return build_tree_list (name, attrargs);
}

/* 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)
    {
      for (tree t = args; t != NULL_TREE; t = TREE_CHAIN (t))
	{
	  tree value = TREE_VALUE (t);
	  if (TREE_CODE (value) != STRING_CST)
	    {
	      error ("%qE attribute argument not a string constant", name);
	      *no_add_attrs = true;
	      return NULL_TREE;
	    }
	}

      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 if (get_target_clone_attr_len (args) == -1)
	{
	  warning (OPT_Wattributes,
		   "single %<target_clones%> attribute is ignored");
	  *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, &global_options_set);
      tree prev_target_node = build_target_option_node (&global_options,
							&global_options_set);

      /* If we previously had some optimization options, use them as the
	 default.  */
      gcc_options *saved_global_options = NULL;

      /* When #pragma GCC optimize pragma is used, it modifies global_options
	 without calling targetm.override_options_after_change.  That can leave
	 target flags inconsistent for comparison.  */
      if (flag_checking && optimization_current_node == optimization_default_node)
	{
	  saved_global_options = XNEW (gcc_options);
	  *saved_global_options = global_options;
	}

      if (old_opts)
	cl_optimization_restore (&global_options, &global_options_set,
				 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, &global_options_set);
      tree target_node = build_target_option_node (&global_options,
						   &global_options_set);
      if (prev_target_node != target_node)
	DECL_FUNCTION_SPECIFIC_TARGET (*node) = target_node;

      /* Restore current options.  */
      cl_optimization_restore (&global_options, &global_options_set,
			       &cur_opts);
      cl_target_option_restore (&global_options, &global_options_set,
				TREE_TARGET_OPTION (prev_target_node));

      if (saved_global_options != NULL)
	{
	  if (!seen_error ())
	    cl_optimization_compare (saved_global_options, &global_options);
	  free (saved_global_options);
	}
    }

  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 "zero_call_used_regs" attribute; arguments as in
   struct attribute_spec.handler.  */

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

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

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

  bool found = false;
  for (unsigned int i = 0; zero_call_used_regs_opts[i].name != NULL; ++i)
    if (strcmp (TREE_STRING_POINTER (id),
		zero_call_used_regs_opts[i].name) == 0)
      {
	found = true;
	break;
      }

  if (!found)
    {
      error_at (DECL_SOURCE_LOCATION (decl),
		"unrecognized %qE attribute argument %qs",
		name, TREE_STRING_POINTER (id));
      *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 "assume" attribute; arguments as in struct
   attribute_spec.handler.  */

tree
handle_assume_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;
	}

      if (tree_to_uhwi (val) > USHRT_MAX)
	{
	  warning (OPT_Wattributes,
		   "%qE attribute argument %qE exceeds %u",
		   name, val, USHRT_MAX);
	  *no_add_attrs = true;
	  return NULL_TREE;
	}
    }
  return NULL_TREE;
}

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

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

  /* This attribute only applies to typedefs (or field decls for properties),
     we drop it otherwise - but warn about this if enabled.  */
  if (TREE_CODE (*node) != TYPE_DECL && TREE_CODE (*node) != FIELD_DECL)
    {
      warning (OPT_WNSObject_attribute, "%qE attribute may be put on a"
	       " typedef only; attribute is ignored", name);
      return NULL_TREE;
    }

  /* The original implementation only allowed pointers to records, however
     recent implementations also allow void *.  */
  tree type = TREE_TYPE (*node);
  if (!type || !POINTER_TYPE_P (type)
      || (TREE_CODE (TREE_TYPE (type)) != RECORD_TYPE
          && !VOID_TYPE_P (TREE_TYPE (type))))
    {
      error ("%qE attribute is for pointer types only", name);
      return NULL_TREE;
    }

  tree t = tree_cons (name, args, TYPE_ATTRIBUTES (type));
  TREE_TYPE (*node) = build_type_attribute_variant (type, t);

  return NULL_TREE;
}

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

static tree
handle_objc_root_class_attribute (tree */*node*/, tree name, tree /*args*/,
				  int /*flags*/, bool *no_add_attrs)
{
  /* This has no meaning outside Objective-C.  */
  if (!c_dialect_objc())
    warning (OPT_Wattributes, "%qE is only applicable to Objective-C"
	     " class interfaces, attribute ignored", name);

  *no_add_attrs = true;
  return NULL_TREE;
}

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

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

  tree type = TREE_TYPE (*node);
  if (TREE_CODE (*node) == FUNCTION_DECL)
    type = TREE_TYPE (type);

  if (type && !POINTER_TYPE_P (type))
    {
      error ("%qE cannot be applied to non-pointer type %qT", name, type);
      return NULL_TREE;
    }

  /* We accept objc_nullability() with a single argument.
     string: "unspecified", "nullable", "nonnull" or "resettable"
     integer: 0 and 3 where the values have the same meaning as
     the strings.  */
  tree val = TREE_VALUE (args);
  if (TREE_CODE (val) == INTEGER_CST)
    {
      val = default_conversion (val);
      if (!tree_fits_uhwi_p (val) || tree_to_uhwi (val) > 3)
	error ("%qE attribute argument %qE is not an integer constant"
	       " between 0 and 3", name, val);
      else
	*no_add_attrs = false; /* OK */
    }
  else if (TREE_CODE (val) == STRING_CST
	   && (strcmp (TREE_STRING_POINTER (val), "nullable") == 0
	      || strcmp (TREE_STRING_POINTER (val), "nonnull") == 0
	      || strcmp (TREE_STRING_POINTER (val), "unspecified") == 0
	      || strcmp (TREE_STRING_POINTER (val), "resettable") == 0))
    *no_add_attrs = false; /* OK */
  else if (val != error_mark_node)
    error ("%qE attribute argument %qE is not recognised", name, val);

  return NULL_TREE;
}

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

static tree
handle_tainted_args_attribute (tree *node, tree name, tree, int,
			       bool *no_add_attrs)
{
  if (TREE_CODE (*node) != FUNCTION_DECL
      && TREE_CODE (*node) != FIELD_DECL)
    {
      warning (OPT_Wattributes, "%qE attribute ignored; valid only "
	       "for functions and function pointer fields",
	       name);
      *no_add_attrs = true;
      return NULL_TREE;
    }

  if (TREE_CODE (*node) == FIELD_DECL
      && !(TREE_CODE (TREE_TYPE (*node)) == POINTER_TYPE
	   && TREE_CODE (TREE_TYPE (TREE_TYPE (*node))) == FUNCTION_TYPE))
    {
      warning (OPT_Wattributes, "%qE attribute ignored;"
	       " field must be a function pointer",
	       name);
      *no_add_attrs = true;
      return NULL_TREE;
    }

  *no_add_attrs = false; /* OK */

  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;
}