On Thu, Nov 12, 2026 at 12:22 AM <[email protected]> wrote:
>
> From: Kyrylo Tkachov <[email protected]>
>
> This is an alternative to 
> https://gcc.gnu.org/pipermail/gcc-patches/2026-November/738271.html
> after feedback from various folks.
>
> get_attributes builds an attribute list for every function decl registered by
> the arm_neon.h and arm_sve.h pragmas, interning the same few attribute names
> through get_identifier and consing the same few nodes tens of thousands of
> times.  The list depends only on the streaming mode, the kind of global state
> the function accesses, whether it can trap and which of the ZA and ZT0 state
> it reads and writes, so there are only a few distinct lists.
>
> Build each distinct list once, cache it in a small array indexed by those
> inputs and share it between the decls that need it.  The middle end already
> shares attribute lists between decls.  decl_attributes conses its own nodes
> onto a decl's attribute list, and the only in-place rewrite of an attribute's
> arguments, in aarch64_merge_string_arguments, sorts the ZA and ZT0 state
> strings of the in, out and inout attributes, which is idempotent.
>
> Instructions retired for -fsyntax-only on a file that includes just the
> header, on top of the preceding table-sizing patch, cc1 built with
> --enable-checking=release:
>
>                      arm_neon.h   arm_sve.h   both
>   this patch               -0.2%      -13.2%  -3.0%
>   cumulative               -0.3%      -14.7%  -3.3%
>
> Bootstrapped and tested on aarch64-none-linux.
> Ok for trunk?

Ok.  As a followup, rename the add_attribute function that takes a
namespace to add_attribute_arm and cache the "arm" identifier; that
will reduce it further.

> Thanks,
> Kyrill
>
> gcc/ChangeLog:
>
>         * config/aarch64/aarch64-acle-builtins.cc (attribute_cache): New
>         variable.
>         (function_builder::get_attributes): Cache the attribute list for each
>         distinct combination of inputs.
>
> Signed-off-by: Kyrylo Tkachov <[email protected]>
> ---
>  gcc/config/aarch64/aarch64-acle-builtins.cc | 55 ++++++++++++++-------
>  1 file changed, 37 insertions(+), 18 deletions(-)
>
> diff --git a/gcc/config/aarch64/aarch64-acle-builtins.cc 
> b/gcc/config/aarch64/aarch64-acle-builtins.cc
> index a3f967e7971..30d5d3f2d41 100644
> --- a/gcc/config/aarch64/aarch64-acle-builtins.cc
> +++ b/gcc/config/aarch64/aarch64-acle-builtins.cc
> @@ -756,39 +756,58 @@ add_shared_state_attribute (const char *name, bool 
> is_in, bool is_out,
>    return attrs;
>  }
>
> +/* The attribute lists that get_attributes has built so far, indexed by the
> +   values that determine their contents.  See get_attributes for how the 
> index
> +   is formed.  */
> +static GTY(()) tree attribute_cache[2 * 3 * 3 * 16];
> +
>  /* Return the appropriate function attributes for INSTANCE, which requires
> -   the architecture extensions in REQUIRED_EXTENSIONS.  */
> +   the architecture extensions in REQUIRED_EXTENSIONS.  The result depends 
> only
> +   on the streaming mode, the kind of global state that INSTANCE accesses,
> +   whether it can trap and which of the ZA and ZT0 state it reads and writes,
> +   so there are only a few distinct lists.  Build each one once and share it
> +   between all the decls that need it.  */
>  tree
>  function_builder::get_attributes (const function_instance &instance,
>                                   aarch64_required_extensions
>                                     required_extensions)
>  {
> +  unsigned int flags = instance.call_properties ();
> +  unsigned int streaming = (required_extensions.sm_off == 0 ? 0
> +                           : required_extensions.sm_on != 0 ? 1 : 2);
> +  unsigned int memory = (instance.modifies_global_state_p () ? 2
> +                        : instance.reads_global_state_p () ? 1 : 0);
> +  bool nothrow = !flag_non_call_exceptions || !instance.could_trap_p ();
> +  unsigned int state = (((flags & CP_READ_ZA) ? 1 : 0)
> +                       | ((flags & CP_WRITE_ZA) ? 2 : 0)
> +                       | ((flags & CP_READ_ZT0) ? 4 : 0)
> +                       | ((flags & CP_WRITE_ZT0) ? 8 : 0));
> +  tree &cached = attribute_cache[((streaming * 3 + memory) * 2 + nothrow) * 
> 16
> +                                + state];
> +  if (cached)
> +    return cached;
> +
>    tree attrs = NULL_TREE;
>
> -  if (required_extensions.sm_off == 0)
> +  if (streaming == 0)
>      attrs = add_attribute ("arm", "streaming", NULL_TREE, attrs);
> -  else if (required_extensions.sm_on != 0)
> +  else if (streaming == 1)
>      attrs = add_attribute ("arm", "streaming_compatible", NULL_TREE, attrs);
>
> -  attrs = add_shared_state_attribute ("in", true, false,
> -                                     instance.call_properties (), attrs);
> -  attrs = add_shared_state_attribute ("out", false, true,
> -                                     instance.call_properties (), attrs);
> -  attrs = add_shared_state_attribute ("inout", true, true,
> -                                     instance.call_properties (), attrs);
> +  attrs = add_shared_state_attribute ("in", true, false, flags, attrs);
> +  attrs = add_shared_state_attribute ("out", false, true, flags, attrs);
> +  attrs = add_shared_state_attribute ("inout", true, true, flags, attrs);
>
> -  if (!instance.modifies_global_state_p ())
> -    {
> -      if (instance.reads_global_state_p ())
> -       attrs = add_attribute ("pure", attrs);
> -      else
> -       attrs = add_attribute ("const", attrs);
> -    }
> +  if (memory == 0)
> +    attrs = add_attribute ("const", attrs);
> +  else if (memory == 1)
> +    attrs = add_attribute ("pure", attrs);
>
> -  if (!flag_non_call_exceptions || !instance.could_trap_p ())
> +  if (nothrow)
>      attrs = add_attribute ("nothrow", attrs);
>
> -  return add_attribute ("leaf", attrs);
> +  cached = add_attribute ("leaf", attrs);
> +  return cached;
>  }
>
>  /* Add a function called NAME with type FNTYPE and attributes ATTRS.
> --
> 2.54.0 (Apple Git-157)
>

Reply via email to