gnu/gcc/ce66e61c785a024d217cd71c95fd1eb9aa5f3ea8 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