https://gcc.gnu.org/g:13efaa5e7dcc882734e384f6a3f4061c7a8286b8

commit r17-4185-g13efaa5e7dcc882734e384f6a3f4061c7a8286b8
Author: Kyrylo Tkachov <[email protected]>
Date:   Wed Sep 9 15:31:48 2026 +0200

    genoutput: write each output template out once
    
    genoutput gives every pattern that needs one its own output_N definition,
    whether or not an identical one has already been written.  Iterators are
    what make that expensive: one define_insn expanded over a mode iterator
    produces a pattern per mode, and where the template does not mention the
    mode, every one of those patterns gets the same definition.
    
    Two patterns produce identical definitions exactly when their templates
    agree and come from the same place in the machine description, since the
    location is what print_md_ptr_loc turns into the #line.  Key on both, and
    have the second pattern name the first rather than repeat it.  A template
    that gensupport rewrote out of the compact syntax carries no location, so
    those key on the text alone; that is still exact, since with no location
    there is no #line for them to differ in.
    
    Work the output format out before emitting anything, so the rest of the
    function tests that rather than the template, and return as soon as a
    template turns out to have been seen: its checks were made when it was
    emitted.  The number of alternatives is the exception, since iterators
    substitute into the constraints separately from the template, so two
    patterns that share a definition can still differ there.  That count is
    recorded with the definition and checked for every pattern, which keeps the
    diagnostics identical to what they are today.
    
    On aarch64 the output_N definitions drop from 8022 to 3860.  The
    multi-alternative tables account for most of that, 6325 to 2534; the
    functions go from 1697 to 1326.  insn-output.cc goes from 7068203 bytes
    in 126281 lines to 6281640 in 98421, and compiles in 17.6% less CPU,
    with peak memory 440MB against 484MB.
    
    Keying on the location as well as the text means the #line in a shared
    definition still names the pattern it came from, so debug information and
    any host compiler diagnostic about the template are unchanged.
    
    Bootstrapped on aarch64-none-linux-gnu.
    
    gcc/ChangeLog:
    
            * genoutput.cc (data): Add output_code.
            (output_def): New structure.
            (output_codes): New variable.
            (process_template): Determine the output format before emitting
            anything and test that rather than the template.  Reuse an identical
            definition emitted earlier, recording output_code, and check the
            number of alternatives against the count recorded with it.
            (output_insn_data): Use output_code.
    
    Signed-off-by: Kyrylo Tkachov <[email protected]>

Diff:
---
 gcc/genoutput.cc | 287 +++++++++++++++++++++++++++++++++----------------------
 1 file changed, 175 insertions(+), 112 deletions(-)

diff --git a/gcc/genoutput.cc b/gcc/genoutput.cc
index 115b9bbc21ab..218d3c0321a1 100644
--- a/gcc/genoutput.cc
+++ b/gcc/genoutput.cc
@@ -91,6 +91,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "errors.h"
 #include "read-md.h"
 #include "gensupport.h"
+#include "hash-map.h"
 #include "hash-table.h"
 
 /* No instruction can have more operands than this.  Sorry for this
@@ -154,6 +155,7 @@ public:
   const char *template_code;
   file_location loc;
   int code_number;
+  int output_code;             /* Which output_* to use.  */
   int n_generator_args;                /* Number of arguments passed to 
generator */
   int n_operands;              /* Number of operands this insn recognizes */
   int n_dups;                  /* Number times match_dup appears in pattern */
@@ -351,10 +353,8 @@ output_insn_data (void)
          }
          break;
        case INSN_OUTPUT_FORMAT_MULTI:
-         printf ("output_%d, ", d->code_number);
-         break;
        case INSN_OUTPUT_FORMAT_FUNCTION:
-         printf ("output_%d, ", d->code_number);
+         printf ("output_%d, ", d->output_code);
          break;
        default:
          gcc_unreachable ();
@@ -648,166 +648,229 @@ place_operands (class data *d)
    It is either the assembler code template, a list of assembler code
    templates, or C code to generate the assembler code template.  */
 
+/* An output_* definition that has been emitted.  */
+
+struct output_def
+{
+  /* The pattern it was emitted for.  */
+  int code_number;
+  /* The number of alternatives in the template, or -1 for a template that is
+     a single block of code and so has none.  */
+  int n_alternatives;
+};
+
+/* The output_* definitions emitted so far, keyed by the template that
+   produced them together with the .md location that template came from.
+   Two patterns agreeing on both get byte-identical definitions, down to the
+   #line, so the second can name the first rather than repeat it.  */
+
+static hash_map<nofree_string_hash, output_def> output_codes;
+
 static void
 process_template (class data *d, const char *template_code)
 {
   const char *cp;
   int i;
 
+  d->output_code = d->code_number;
+
+  /* Work out the output format first, so that the rest of the function can
+     test that rather than the template again.  */
+
   /* Templates starting with * contain straight code to be run.  */
   if (template_code[0] == '*')
     {
       d->template_code = 0;
       d->output_format = INSN_OUTPUT_FORMAT_FUNCTION;
-
-      puts ("\nstatic const char *");
-      printf ("output_%d (rtx *operands ATTRIBUTE_UNUSED, rtx_insn *insn 
ATTRIBUTE_UNUSED)\n",
-             d->code_number);
-      puts ("{");
-      rtx_reader_ptr->print_md_ptr_loc (template_code);
-      puts (template_code + 1);
-      puts ("}");
     }
 
   /* If the assembler code template starts with a @ it is a newline-separated
-     list of assembler code templates, one for each alternative.  */
+     list of assembler code templates, one for each alternative.  One of them
+     starting with a * makes the whole thing a function.  */
   else if (template_code[0] == '@')
     {
-      int found_star = 0;
+      d->template_code = 0;
+      d->output_format = INSN_OUTPUT_FORMAT_MULTI;
 
       for (cp = &template_code[1]; *cp; )
        {
          while (ISSPACE (*cp))
            cp++;
          if (*cp == '*')
-           found_star = 1;
+           {
+             d->output_format = INSN_OUTPUT_FORMAT_FUNCTION;
+             break;
+           }
          while (!IS_VSPACE (*cp) && *cp != '\0')
            ++cp;
        }
-      d->template_code = 0;
-      if (found_star)
-       {
-         d->output_format = INSN_OUTPUT_FORMAT_FUNCTION;
-         puts ("\nstatic const char *");
-         printf ("output_%d (rtx *operands ATTRIBUTE_UNUSED, "
-                 "rtx_insn *insn ATTRIBUTE_UNUSED)\n", d->code_number);
-         puts ("{");
-         puts ("  switch (which_alternative)\n    {");
-       }
-      else
-       {
-         d->output_format = INSN_OUTPUT_FORMAT_MULTI;
-         printf ("\nstatic const char * const output_%d[] = {\n",
-                 d->code_number);
-       }
+    }
+  else
+    {
+      d->template_code = template_code;
+      d->output_format = INSN_OUTPUT_FORMAT_SINGLE;
+      return;
+    }
 
-      for (i = 0, cp = &template_code[1]; *cp; )
-       {
-         const char *ep, *sp, *bp;
+  /* The template needs a definition of its own, which an earlier pattern may
+     already have emitted.  Iterators are what make that common: one
+     define_insn expanded over a mode iterator gives a pattern per mode, and
+     where the template does not mention the mode they all need the same
+     definition.  Two templates produce byte-identical definitions exactly
+     when they agree and come from the same place in the machine description,
+     since the location is what print_md_ptr_loc turns into the #line.  Name
+     the definition the earlier pattern emitted rather than repeat it.  */
+  const md_reader::ptr_loc *loc
+    = rtx_reader_ptr->get_md_ptr_loc (template_code);
+  char *key = xasprintf ("%s:%d\n%s", loc ? loc->loc.filename : "",
+                        loc ? loc->loc.lineno : 0, template_code);
+  bool existed;
+  output_def &prev = output_codes.get_or_insert (key, &existed);
+  if (existed)
+    {
+      free (key);
+      d->output_code = prev.code_number;
+      /* The template was checked when it was emitted.  How many alternatives
+        the pattern has is not a property of the template, though: iterators
+        substitute into the template and into the constraints separately, so
+        patterns that share a template can still differ here.  */
+      if (prev.n_alternatives >= 0 && prev.n_alternatives != d->n_alternatives)
+       error_at (d->loc, "wrong number of alternatives in the output"
+                 " template");
+      return;
+    }
+  prev.code_number = d->code_number;
+  prev.n_alternatives = -1;
 
-         while (ISSPACE (*cp))
-           cp++;
+  /* A template that is one block of code, rather than a list of alternatives
+     one of which is.  Both give INSN_OUTPUT_FORMAT_FUNCTION.  */
+  if (template_code[0] == '*')
+    {
+      puts ("\nstatic const char *");
+      printf ("output_%d (rtx *operands ATTRIBUTE_UNUSED, rtx_insn *insn 
ATTRIBUTE_UNUSED)\n",
+             d->code_number);
+      puts ("{");
+      rtx_reader_ptr->print_md_ptr_loc (template_code);
+      puts (template_code + 1);
+      puts ("}");
+      return;
+    }
+
+  bool found_star = d->output_format == INSN_OUTPUT_FORMAT_FUNCTION;
+
+  if (found_star)
+    {
+      puts ("\nstatic const char *");
+      printf ("output_%d (rtx *operands ATTRIBUTE_UNUSED, "
+             "rtx_insn *insn ATTRIBUTE_UNUSED)\n", d->code_number);
+      puts ("{");
+      puts ("  switch (which_alternative)\n    {");
+    }
+  else
+    printf ("\nstatic const char * const output_%d[] = {\n", d->code_number);
+
+  for (i = 0, cp = &template_code[1]; *cp; )
+    {
+      const char *ep, *sp, *bp;
+
+      while (ISSPACE (*cp))
+       cp++;
 
-         bp = cp;
-         if (found_star)
+      bp = cp;
+      if (found_star)
+       {
+         printf ("    case %d:", i);
+         if (*cp == '*')
            {
-             printf ("    case %d:", i);
-             if (*cp == '*')
-               {
-                 printf ("\n      ");
-                 cp++;
-               }
-             else
-               printf (" return \"");
+             printf ("\n      ");
+             cp++;
            }
          else
-           printf ("  \"");
+           printf (" return \"");
+       }
+      else
+       printf ("  \"");
 
-         for (ep = sp = cp; !IS_VSPACE (*ep) && *ep != '\0'; ++ep)
-           if (!ISSPACE (*ep))
-             sp = ep + 1;
+      for (ep = sp = cp; !IS_VSPACE (*ep) && *ep != '\0'; ++ep)
+       if (!ISSPACE (*ep))
+         sp = ep + 1;
 
-         if (sp != ep)
-           message_at (d->loc, "trailing whitespace in output template");
+      if (sp != ep)
+       message_at (d->loc, "trailing whitespace in output template");
 
-         /* Check for any unexpanded iterators.  */
-         if (bp[0] != '*' && d->compact_syntax_p)
+      /* Check for any unexpanded iterators.  */
+      if (bp[0] != '*' && d->compact_syntax_p)
+       {
+         const char *p = cp;
+         const char *last_bracket = nullptr;
+         while (p < sp)
            {
-             const char *p = cp;
-             const char *last_bracket = nullptr;
-             while (p < sp)
+             if (*p == '\\' && p + 1 < sp)
                {
-                 if (*p == '\\' && p + 1 < sp)
-                   {
-                     putchar (*p);
-                     putchar (*(p+1));
-                     p += 2;
-                     continue;
-                   }
-
-                 if (*p == '>' && last_bracket && *last_bracket == '<')
-                   {
-                     int len = p - last_bracket;
-                     fatal_at (d->loc, "unresolved iterator '%.*s' in '%s'",
-                               len - 1, last_bracket + 1, cp);
-                   }
-                 else if (*p == '<' || *p == '>')
-                   last_bracket = p;
-
                  putchar (*p);
-                 p += 1;
+                 putchar (*(p+1));
+                 p += 2;
+                 continue;
                }
 
-             if (last_bracket)
+             if (*p == '>' && last_bracket && *last_bracket == '<')
                {
-                 char *nl = strchr (const_cast<char*> (cp), '\n');
-                 if (nl)
-                   *nl = '\0';
-                 fatal_at (d->loc, "unmatched angle brackets, likely an "
-                           "error in iterator syntax in %s", cp);
+                 int len = p - last_bracket;
+                 fatal_at (d->loc, "unresolved iterator '%.*s' in '%s'",
+                           len - 1, last_bracket + 1, cp);
                }
-           }
-         else
-           {
-             while (cp < sp)
-               putchar (*(cp++));
-           }
+             else if (*p == '<' || *p == '>')
+               last_bracket = p;
 
-         cp = sp;
+             putchar (*p);
+             p += 1;
+           }
 
-         if (!found_star)
-           puts ("\",");
-         else if (*bp != '*')
-           puts ("\";");
-         else
+         if (last_bracket)
            {
-             /* The usual action will end with a return.
-                If there is neither break or return at the end, this is
-                assumed to be intentional; this allows to have multiple
-                consecutive alternatives share some code.  */
-             puts ("");
+             char *nl = strchr (const_cast<char*> (cp), '\n');
+             if (nl)
+               *nl = '\0';
+             fatal_at (d->loc, "unmatched angle brackets, likely an "
+                       "error in iterator syntax in %s", cp);
            }
-         i++;
        }
-      if (i == 1)
-       message_at (d->loc, "'@' is redundant for output template with"
-                   " single alternative");
-      if (i != d->n_alternatives)
-       error_at (d->loc, "wrong number of alternatives in the output"
-                 " template");
+      else
+       {
+         while (cp < sp)
+           putchar (*(cp++));
+       }
 
-      if (found_star)
-       puts ("      default: gcc_unreachable ();\n    }\n}");
+      cp = sp;
+
+      if (!found_star)
+       puts ("\",");
+      else if (*bp != '*')
+       puts ("\";");
       else
-       printf ("};\n");
+       {
+         /* The usual action will end with a return.
+            If there is neither break or return at the end, this is
+            assumed to be intentional; this allows to have multiple
+            consecutive alternatives share some code.  */
+         puts ("");
+       }
+      i++;
     }
+  if (i == 1)
+    message_at (d->loc, "'@' is redundant for output template with"
+               " single alternative");
+  if (i != d->n_alternatives)
+    error_at (d->loc, "wrong number of alternatives in the output"
+             " template");
+  prev.n_alternatives = i;
+
+  if (found_star)
+    puts ("      default: gcc_unreachable ();\n    }\n}");
   else
-    {
-      d->template_code = template_code;
-      d->output_format = INSN_OUTPUT_FORMAT_SINGLE;
-    }
+    printf ("};\n");
 }
-
+
 /* Check insn D for consistency in number of constraint alternatives.  */
 
 static void

Reply via email to