From: Kyrylo Tkachov <[email protected]>

genextract already knows each operand's location as a path string, built
by walk_rtx while it descends the pattern: a digit selects an XEXP and a
letter an XVECEXP.  print_path then expanded those strings back into C,
so insn_extract became one switch with an arm per insn code.  Patterns
that extract alike share an arm, but the labels still cost a line each:
on aarch64, 16068 case labels selecting 649 distinct bodies, 782KB of
labels against 252KB of bodies, all inside a single 1MB function.  On
riscv64 it is 45386 labels.

Emit the path strings as data and walk them instead.  Three tables
replace the switch.

extract_method_of_code[] maps an insn code to an extraction method.
Method 0 means the code has no pattern to extract from, which is the
error case the old default arm handled, and method 1 means an old-style
define_peephole, whose operand count is only known at run time:

  static const unsigned short extract_method_of_code[] = {
    0, 650, 650, 649, 649, 648, 647, 646, 646, 646, ...

extract_methods[] gives each method its operand and dup counts and its
offsets into the path and dup-number pools:

  static const struct extract_method_d extract_methods[] = {
    ...
    { 51, 0, 4, 0 },        /* 4 operands, no dups, paths at offset 51 */

and extract_paths[] holds the paths themselves, NUL separated, operands
first and dups second.  "1c" is XVECEXP (XEXP (pat, 1), 0, 2), because
'1' selects XEXP 1 and 'c' selects XVECEXP element 2:

  static const char extract_paths[] =
    "1c0\0001c1\0001d\0001e\0001f\0001c\0001d\0001e\000...

So the arm that used to read

  case 16029:  /* aarch64_sme_fmopsvnx8hivnx16qi */
  case 16028:  /* aarch64_sme_fmopavnx8hivnx16qi */
  ...                               /* 34 more labels */
  case 15740:  /* aarch64_sme_fmopavnx4sivnx16qi */
    ro[0] = *(ro_loc[0] = &XVECEXP (XEXP (pat, 1), 0, 2));
    ro[1] = *(ro_loc[1] = &XVECEXP (XEXP (pat, 1), 0, 3));
    ro[2] = *(ro_loc[2] = &XVECEXP (XEXP (pat, 1), 0, 4));
    ro[3] = *(ro_loc[3] = &XVECEXP (XEXP (pat, 1), 0, 5));
    ro[4] = *(ro_loc[4] = &XVECEXP (XEXP (pat, 1), 0, 6));
    break;

is now one shared entry "1c\0001d\0001e\0001f\0001g" in extract_paths[],
one row in extract_methods[], and 36 entries in extract_method_of_code[]
that name it.  insn_extract itself becomes a loop over the method's
operands, calling follow_extract_path once each.

The terminator is spelled \000 rather than \0 because the next character
is often a digit, which a shorter octal escape would absorb.

insn-extract.cc drops from 1.04MB/21139 lines to 124KB/1823 lines on
aarch64, 2.25MB to 246KB on riscv64, 750KB to 100KB on x86_64 and 170KB
to 26KB on avr.  Compile time falls by 92% and peak memory
from 332MB to 166MB.

insn_extract is hot, so the I measured that there's no non-noise impact
on compile time over a bunch of input files.

A differential harness compares the operand and dup locations the old
switch and the new tables produce, for every insn code: 16116 on aarch64,
45386 on riscv64, 11335 on x86_64, 10608 on i686 and 3562 on avr.  87007
insn codes, no differences.  avr covers the old-style define_peephole
path, which the other four do not have.

Bootstrapped on aarch64-none-linux-gnu.
Ok for trunk?

gcc/ChangeLog:

        * genextract.cc (MISSING_OPERAND_CHAR): New macro.
        (pathpool, dupnums): New variables.
        (add_path, print_string_literal, print_extractions): New functions.
        (print_path): Remove.
        (print_header): Only emit the prologue.
        (main): Call print_extractions.

Signed-off-by: Kyrylo Tkachov <[email protected]>
---
 gcc/genextract.cc | 316 +++++++++++++++++++++++++++++-----------------
 1 file changed, 199 insertions(+), 117 deletions(-)

diff --git a/gcc/genextract.cc b/gcc/genextract.cc
index 44094fdc281..62babeb3982 100644
--- a/gcc/genextract.cc
+++ b/gcc/genextract.cc
@@ -304,52 +304,31 @@ walk_rtx (md_rtx_info *info, rtx x, class accum_extract 
*acc)
     }
 }
 
-/* Given a PATH, representing a path down the instruction's
-   pattern from the root to a certain point, output code to
-   evaluate to the rtx at that point.  */
+/* The character that marks an operand number that a pattern does not use.
+   It cannot clash with a path step, which is always a digit or a letter.  */
+#define MISSING_OPERAND_CHAR '!'
 
-static void
-print_path (const char *path)
-{
-  int len = strlen (path);
-  int i;
-
-  if (len == 0)
-    {
-      /* Don't emit "pat", since we may try to take the address of it,
-        which isn't what is intended.  */
-      fputs ("PATTERN (insn)", stdout);
-      return;
-    }
-
-  /* We first write out the operations (XEXP or XVECEXP) in reverse
-     order, then write "pat", then the indices in forward order.  */
+/* The paths of all extraction methods, concatenated.  Each path is NUL
+   terminated; the paths of one method are adjacent, operands first and
+   dups second.  */
+static struct obstack pathpool;
 
-  for (i = len - 1; i >= 0 ; i--)
-    {
-      if (ISLOWER (path[i]) || ISUPPER (path[i]))
-       fputs ("XVECEXP (", stdout);
-      else if (ISDIGIT (path[i]))
-       fputs ("XEXP (", stdout);
-      else
-       gcc_unreachable ();
-    }
+/* The dup numbers of all extraction methods, concatenated.  */
+static vec<int> dupnums;
 
-  fputs ("pat", stdout);
+/* Add PATH, which is null for an operand number that the pattern skips,
+   to the path pool.  */
 
-  for (i = 0; i < len; i++)
-    {
-      if (ISUPPER (path[i]))
-       printf (", 0, %d)", path[i] - UPPER_OFFSET);
-      else if (ISLOWER (path[i]))
-       printf (", 0, %d)", path[i] - 'a');
-      else if (ISDIGIT (path[i]))
-       printf (", %d)", path[i] - '0');
-      else
-       gcc_unreachable ();
-    }
+static void
+add_path (const char *path)
+{
+  if (path)
+    obstack_grow (&pathpool, path, strlen (path));
+  else
+    obstack_1grow (&pathpool, MISSING_OPERAND_CHAR);
+  obstack_1grow (&pathpool, '\0');
 }
-
+
 static void
 print_header (void)
 {
@@ -374,43 +353,203 @@ print_header (void)
 /* This variable is used as the \"location\" of any missing operand\n\
    whose numbers are skipped by a given pattern.  */\n\
 static rtx junk ATTRIBUTE_UNUSED;\n");
+}
+
+/* Print STR as a C string literal, broken into chunks so that no output
+   line gets excessively long.  */
+
+static void
+print_string_literal (const char *str, unsigned int len)
+{
+  printf ("  \"");
+  for (unsigned int i = 0; i < len; i++)
+    {
+      if (str[i] == '\0')
+       /* Spell the terminator with all three octal digits: the next
+          character may be a digit, which a shorter escape would absorb.  */
+       printf ("\\000");
+      else
+       putchar (str[i]);
+      if ((i % 60) == 59 && i + 1 < len)
+       printf ("\"\n  \"");
+    }
+  printf ("\"");
+}
+
+/* Print the tables that drive insn_extract, and insn_extract itself.  */
+
+static void
+print_extractions (void)
+{
+  struct extraction *p;
+  struct code_ptr *link;
+  unsigned int i;
+
+  /* Number the methods and record, for every insn code, the method that
+     extracts its operands.  Method 0 means "not an extractable insn" and
+     method 1 means "an old-style define_peephole", whose operand count is
+     only known at run time.  */
+  auto_vec<unsigned int> method_of_code;
+  auto_vec<struct extraction *> methods;
+  auto_vec<unsigned int> path_start;
+  auto_vec<unsigned int> dup_start;
+
+  for (link = peepholes; link; link = link->next)
+    {
+      while (method_of_code.length () <= (unsigned int) link->insn_code)
+       method_of_code.safe_push (0);
+      method_of_code[link->insn_code] = 1;
+    }
+
+  obstack_init (&pathpool);
+  for (p = extractions; p; p = p->next)
+    {
+      unsigned int method = methods.length () + 2;
+      gcc_assert (method <= USHRT_MAX);
+      gcc_assert (p->op_count <= UCHAR_MAX && p->dup_count <= UCHAR_MAX);
+      path_start.safe_push (obstack_object_size (&pathpool));
+      dup_start.safe_push (dupnums.length ());
+      methods.safe_push (p);
+      for (i = 0; i < p->op_count; i++)
+       add_path (p->oplocs[i]);
+      for (i = 0; i < p->dup_count; i++)
+       {
+         add_path (p->duplocs[i]);
+         gcc_assert (IN_RANGE (p->dupnums[i], 0, UCHAR_MAX));
+         dupnums.safe_push (p->dupnums[i]);
+       }
+      for (link = p->insns; link; link = link->next)
+       {
+         while (method_of_code.length () <= (unsigned int) link->insn_code)
+           method_of_code.safe_push (0);
+         method_of_code[link->insn_code] = method;
+       }
+    }
+  path_start.safe_push (obstack_object_size (&pathpool));
+
+  unsigned int pool_len = obstack_object_size (&pathpool);
+  const char *pool = XOBFINISH (&pathpool, const char *);
+
+  printf ("/* The paths that locate the operands and the dups of each\n"
+         "   extraction method.  A path is a sequence of steps down the\n"
+         "   pattern: a digit D selects XEXP (x, D - '0'), a lower-case\n"
+         "   letter L selects XVECEXP (x, 0, L - 'a') and an upper-case\n"
+         "   letter U selects XVECEXP (x, 0, U - %d).  An empty path denotes\n"
+         "   the pattern itself and '%c' an operand number that the pattern\n"
+         "   does not use.  */\n", UPPER_OFFSET, MISSING_OPERAND_CHAR);
+  printf ("#define UPPER_OFFSET %d\n", UPPER_OFFSET);
+  printf ("#define MISSING_OPERAND_CHAR '%c'\n\n", MISSING_OPERAND_CHAR);
+  printf ("static const char extract_paths[] =\n");
+  print_string_literal (pool, pool_len);
+  printf (";\n\n");
+
+  printf ("static const unsigned char extract_dup_num[] = {");
+  for (i = 0; i < dupnums.length (); i++)
+    printf ("%s%d,", (i % 20) == 0 ? "\n  " : " ", dupnums[i]);
+  printf ("%s0\n};\n\n", dupnums.length () ? "\n  " : "");
+
+  printf ("struct extract_method_d {\n"
+         "  unsigned int paths;\n"
+         "  unsigned int dups;\n"
+         "  unsigned char n_operands;\n"
+         "  unsigned char n_dups;\n"
+         "};\n\n");
+
+  printf ("static const struct extract_method_d extract_methods[] = {\n"
+         "  { 0, 0, 0, 0 },\n"
+         "  { 0, 0, 0, 0 },\n");
+  for (i = 0; i < methods.length (); i++)
+    printf ("  { %u, %u, %u, %u },\n", path_start[i], dup_start[i],
+           methods[i]->op_count, methods[i]->dup_count);
+  printf ("};\n\n");
+
+  printf ("static const unsigned short extract_method_of_code[] = {");
+  for (i = 0; i < method_of_code.length (); i++)
+    printf ("%s%u,", (i % 20) == 0 ? "\n  " : " ", method_of_code[i]);
+  printf ("\n};\n\n");
+
+  puts ("\
+/* Follow one NUL-terminated path in extract_paths from *PP, starting at\n\
+   the pattern *ROOT, and return the location it selects.  *PP is left\n\
+   just after the path's terminator.  */\n\
+\n\
+static inline rtx *\n\
+follow_extract_path (const char **pp, rtx *root)\n{\n\
+  const char *p = *pp;\n\
+  rtx *loc = root;\n\
+  for (; *p; p++)\n\
+    if (ISDIGIT (*p))\n\
+      loc = &XEXP (*loc, *p - '0');\n\
+    else if (ISLOWER (*p))\n\
+      loc = &XVECEXP (*loc, 0, *p - 'a');\n\
+    else\n\
+      loc = &XVECEXP (*loc, 0, *p - UPPER_OFFSET);\n\
+  *pp = p + 1;\n\
+  return loc;\n\
+}\n");
 
   puts ("\
 void\n\
 insn_extract (rtx_insn *insn)\n{\n\
   rtx *ro = recog_data.operand;\n\
   rtx **ro_loc = recog_data.operand_loc;\n\
-  rtx pat = PATTERN (insn);\n\
-  int i ATTRIBUTE_UNUSED; /* only for peepholes */\n\
+  int icode = INSN_CODE (insn);\n\
 \n\
   if (flag_checking)\n\
     {\n\
       memset (ro, 0xab, sizeof (*ro) * MAX_RECOG_OPERANDS);\n\
       memset (ro_loc, 0xab, sizeof (*ro_loc) * MAX_RECOG_OPERANDS);\n\
-    }\n");
-
-  puts ("\
-  switch (INSN_CODE (insn))\n\
+    }\n\
+\n\
+  unsigned int method = (icode >= 0\n\
+                        && icode < (int) ARRAY_SIZE (extract_method_of_code)\n\
+                        ? extract_method_of_code[icode] : 0);\n\
+  if (method == 0)\n\
     {\n\
-    default:\n\
       /* Control reaches here if insn_extract has been called with an\n\
-         unrecognizable insn (code -1), or an insn whose INSN_CODE\n\
-         corresponds to a DEFINE_EXPAND in the machine description;\n\
-         either way, a bug.  */\n\
-      if (INSN_CODE (insn) < 0)\n\
-        fatal_insn (\"unrecognizable insn:\", insn);\n\
+        unrecognizable insn (code -1), or an insn whose INSN_CODE\n\
+        corresponds to a DEFINE_EXPAND in the machine description;\n\
+        either way, a bug.  */\n\
+      if (icode < 0)\n\
+       fatal_insn (\"unrecognizable insn:\", insn);\n\
       else\n\
-        fatal_insn (\"insn with invalid code number:\", insn);\n");
+       fatal_insn (\"insn with invalid code number:\", insn);\n\
+    }\n\
+\n\
+  if (method == 1)\n\
+    {\n\
+      /* An old-style define_peephole.  The vector in the insn was created\n\
+        just for this function and contains nothing but operands.  */\n\
+      for (int i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--)\n\
+       ro[i] = *(ro_loc[i] = &XVECEXP (PATTERN (insn), 0, i));\n\
+      return;\n\
+    }\n\
+\n\
+  const struct extract_method_d *m = &extract_methods[method];\n\
+  const char *p = extract_paths + m->paths;\n\
+  for (unsigned int i = 0; i < m->n_operands; i++)\n\
+    if (*p == MISSING_OPERAND_CHAR)\n\
+      {\n\
+       ro[i] = const0_rtx;\n\
+       ro_loc[i] = &junk;\n\
+       p += 2;\n\
+      }\n\
+    else\n\
+      {\n\
+       rtx *loc = follow_extract_path (&p, &PATTERN (insn));\n\
+       ro_loc[i] = loc;\n\
+       ro[i] = *loc;\n\
+      }\n\
+  for (unsigned int i = 0; i < m->n_dups; i++)\n\
+    {\n\
+      recog_data.dup_loc[i] = follow_extract_path (&p, &PATTERN (insn));\n\
+      recog_data.dup_num[i] = extract_dup_num[m->dups + i];\n\
+    }\n}");
 }
 
 int
 main (int argc, const char **argv)
 {
-  unsigned int i;
-  struct extraction *p;
-  struct code_ptr *link;
-  const char *name;
-
   progname = "genextract";
 
   if (!init_rtx_reader_args (argc, argv))
@@ -444,64 +583,7 @@ main (int argc, const char **argv)
     return FATAL_EXIT_CODE;
 
   print_header ();
-
-  /* Write out code to handle peepholes and the insn_codes that it should
-     be called for.  */
-  if (peepholes)
-    {
-      for (link = peepholes; link; link = link->next)
-       printf ("    case %d:\n", link->insn_code);
-
-      /* The vector in the insn says how many operands it has.
-        And all it contains are operands.  In fact, the vector was
-        created just for the sake of this function.  We need to set the
-        location of the operands for sake of simplifications after
-        extraction, like eliminating subregs.  */
-      puts ("      for (i = XVECLEN (pat, 0) - 1; i >= 0; i--)\n"
-           "          ro[i] = *(ro_loc[i] = &XVECEXP (pat, 0, i));\n"
-           "      break;\n");
-    }
-
-  /* Write out all the ways to extract insn operands.  */
-  for (p = extractions; p; p = p->next)
-    {
-      for (link = p->insns; link; link = link->next)
-       {
-         i = link->insn_code;
-         name = get_insn_name (i);
-         if (name)
-           printf ("    case %d:  /* %s */\n", i, name);
-         else
-           printf ("    case %d:\n", i);
-       }
-
-      for (i = 0; i < p->op_count; i++)
-       {
-         if (p->oplocs[i] == 0)
-           {
-             printf ("      ro[%d] = const0_rtx;\n", i);
-             printf ("      ro_loc[%d] = &junk;\n", i);
-           }
-         else
-           {
-             printf ("      ro[%d] = *(ro_loc[%d] = &", i, i);
-             print_path (p->oplocs[i]);
-             puts (");");
-           }
-       }
-
-      for (i = 0; i < p->dup_count; i++)
-       {
-         printf ("      recog_data.dup_loc[%d] = &", i);
-         print_path (p->duplocs[i]);
-         puts (";");
-         printf ("      recog_data.dup_num[%d] = %d;\n", i, p->dupnums[i]);
-       }
-
-      puts ("      break;\n");
-    }
-
-  puts ("    }\n}");
+  print_extractions ();
   fflush (stdout);
   return (ferror (stdout) != 0 ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE);
 }
-- 
2.50.1 (Apple Git-155)

Reply via email to