genextract: drive insn_extract from the operand path table

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.

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 <ktkachov@nvidia.com>
1 file changed