[PATCH]: Statistics about machine description patterns & alternatives

Stephane Carrez Stephane.Carrez@worldnet.fr
Mon Aug 7 13:23:00 GMT 2000


Hi!

While developping the 68hc11 port, I was interested in knowing whether the
patterns of the machine description are used or not.  This is useful if
you want:

 - to verify your port and be sure that after running a testsuite, all
   the patterns are used.
 - making sure that when you provide a pattern for combine (for optimization)
   if is used after all.

This patch introduces statistics on the machine description patterns.
While the compiler executes, statistics about patterns and alternatives are
collected.  Statistics are merged with previous compilation runs and saved
in a file.  A tool (prstat) allows to dump the pattern usage with information
about unused patterns, unused alternatives and so on.


************ WARNING
This patch is not (yet) ready for integration.  I submit it for comments
and improvements and for others to play with.  
************ WARNING

It works as follows:
 - At begining of a compilation, we read the previous statistics table
   (rtx_init_statistics).  The table contains one entry for each alternative
   of a pattern (one long counter).

 - In insn-emit.c, we collect the statistics of the expand and split patterns
   which are used.  Collecting statistics is generated by genemit.c
   (increment an entry in the table)

 - In final.c, before outputing the instruction, we collect the statistics
   and increment the entry corresponding to the pattern and the alternative.

 - After the compilation is finished, the table is saved.


To use this, I have introduced the -dumpstat <file> option as well as
an environment variable GCC_STAT.  Using the environment variable is
easier in general.  I set up the environment variable to some file,
run the gcc validation suite and I'm able to get the statistics about
all compilation runs of the validation.

The 'prstat' tool is very crude but quite easy. It filters the results to
report only used patterns, unused ones, split patterns, emit statistics...
Below is an extract of a possible output (based on 68hc11 port):

  - First column indicates the line number in the md file
  - Second column is I for insn, S for a split, P for a peephole, E for expand
  - Column 3 is the insn name (Ah... should we have a name for split...)
  - Column 4 is '*' if all the alternatives are used
  - Column 5/6 indicate the emit or split count
  - Column 7/7 indicate the output count.
  - Last columns indicate the alternative.  Enclosed in {} are the alternatives
    with a 0 count (ie, not used)

Total number of insns generated :    4890243
Total number of pattern used    :  210 ( 76%)
Total number of unused patterns :  68
  153 I  tsthi_1              -   emit      0   out  21185 alt 2 [2]
  190 I  tstqi_1              -   emit      0   out   2485 alt 3 [4]  { 2 }
  211 I  tstqi_z_used         -   emit      0   out      0 alt 0 [1]  { 0 }
  286 I  cmphi_1              -   emit      0   out  50890 alt 2 [6]  { 1 3 4 5
}                                                                       
  463 I  movstrictqi          -   emit    168   out    168 alt 0 [3]  { 0 1 2
}                                                                         
  573 I  *movhi2_push         *   emit      0   out 267891 alt 2 [2]
  596 I  *movhi2_pop          -   emit      0   out  68788 alt 1 [2]  { 1 }
  648 I  movhi_const0         -   emit      0   out  78837 alt 3 [3]
  657 I  *movhi_68hc12        *   emit      0   out  59193 alt 3 [3]
  667 I  *movhi_m68hc11       -   emit      0   out 1070177 alt 5 [6]  { 5 }
  237 E  cmpsi                *   emit  12204   out  12204 alt 0 [0]
  261 S  cmpsi+1              *  split   3519   out   3519 alt 0 [0]        
 5542 P  tablejump+1          *   out   9065 alt 1 [1]                      



Have fun,

	Stephane

P.S: Quick set of commands
	setenv GCC_STAT /tmp/gcc.stat
	make check-gcc
	make prstat
	prstat -f /tmp/gcc.stat

---------------------

2000-08-07  Stephane Carrez  <Stephane.Carrez@worldnet.fr>

	* toplev.c (dump_stat_name): Define.
	(independent_decode_option): Recognize -dumpstat and set 
	dump_stat_name accordingly.
	(display_help): Document -dumpstat.
	(main): Initialize the statistics before compiling and save
	them after.

	* rtl.h (struct rtx_stat_header ): Define.
	(rtx_init_statistics, rtx_save_statistics): Declare.
	(rtx_stat_inc): Declare.
	(insn_stat_count, insns_stat_count_size, insn_emit_stamp): Declare.

	* genemit.c (gen_insn): Emit code to collect statistics.
	(gen_expand): Likewise.
	(gen_split): Likewise.
	(main): Likewise.

	* genstat.c: New file, generation of MD file statistics table
	(used only by prstat).

	* prstat.c: New file, analyse results and report them.

	* emit-rtl.c (rtx_init_statistics): New function (setup satistics).
	(rtx_save_statistics): New function (save statistics after 
	compilation).
	(rtx_stat_inc): New function, collect statistics.

	* Makefile.in (GEN): New command genstat.
	(insn-stat.o, insn-stat.c, s-stat): Md file statistics description.
	(genstat, genstat.o): Build genstat to create insn-stat.c
	(prstat): New command to print statistics results.
diff -Nrup --exclude-from=gcc-exclude.lst /src/gnu/cygnus/gcc/gcc/emit-rtl.c gcc/gcc/emit-rtl.c
--- /src/gnu/cygnus/gcc/gcc/emit-rtl.c	Sun Aug  6 11:35:08 2000
+++ gcc/gcc/emit-rtl.c	Sun Aug  6 12:09:25 2000
@@ -4263,4 +4265,170 @@ restore_line_number_status (old_value)
      int old_value;
 {
   no_line_numbers = old_value;
+}
+
+
+/* Collect statistics about RTX patterns which are used in the machine
+   description.
+
+   For each machine description pattern, we collect the number of times it is
+   used and the alternatives used.  One set of statistics is collected
+   during generation of insn (including split).  A second set is collected
+   in the final code generation before printing the insn template.
+
+   The statistics collected during a compilation run are merged by
+   'rtx_save_statistics' with previous statistics.
+
+*/
+
+#ifndef O_BINARY
+# define O_BINARY 0
+#endif
+
+static unsigned long *insn_use_count = 0;
+
+/* Initialize to collect statistics of machine description patterns.  */
+void
+rtx_init_statistics (file)
+     const char *file;
+{
+  int max_insn_code;
+  size_t size;
+  
+  /* If no statistics file is specified, look for an environment
+     variable.  Statistics are not collected if they are not enabled.  */
+  if (file == 0)
+    {
+      file = getenv ("GCC_STAT");
+      if (file == 0)
+        return;
+    }
+  
+  max_insn_code = insn_stat_count_size / sizeof (unsigned long);
+  size = sizeof (unsigned long) * max_insn_code * MAX_RECOG_ALTERNATIVES;
+  insn_use_count = (unsigned long*) xmalloc (size);
+  memset (insn_use_count, 0, size);
+  memset (insn_stat_count, 0, insn_stat_count_size);
+}
+
+
+/* Save the statistics collected during this compilation run and
+   merge them with previous runs.  */
+void
+rtx_save_statistics (file)
+     const char *file;
+{
+  struct rtx_stat_header hdr;
+  int fd, i;
+  int result;
+  int error;
+  int max_insn_code;
+  size_t stat_count_size;
+  size_t use_count_size;
+  unsigned long *merge_stat_count;
+  unsigned long *merge_use_count;
+  
+  /* If no statistics file is specified, look for an environment
+     variable.  Statistics are not collected if they are not enabled.  */
+  if (file == 0)
+    {
+      file = getenv ("GCC_STAT");
+      if (file == 0)
+        return;
+    }
+
+  /* Allocate memory to merge statistics.  */
+  stat_count_size = insn_stat_count_size;
+  merge_stat_count = (unsigned long *) xmalloc (stat_count_size);
+  
+  max_insn_code = insn_stat_count_size / sizeof (unsigned long);
+  use_count_size = sizeof (unsigned long) * max_insn_code;
+  use_count_size = use_count_size * MAX_RECOG_ALTERNATIVES;
+  merge_use_count  = (unsigned long*) xmalloc (use_count_size);
+
+  /* Try to open the file.  If this fails, don't complain.
+     We won't save and merge statistics of this run and that's all.  */
+  fd = open (file, O_RDWR | O_CREAT | O_BINARY, 0666);
+  if (fd < 0)
+    return;
+
+  /* TBD: Get some read/write lock on the file here.  */
+
+  /* Read header to merge with previous statistics.  */
+  error = 0;
+  result = read (fd, &hdr, sizeof (hdr));
+  if (result != 0)
+    {
+      /* Verify header.  The genemit stamp number must match ours.
+         Otherwise, this means the md file was changed.  */
+      if (result != sizeof (hdr)
+          || memcmp (hdr.magic, RTX_STAT_MAGIC, sizeof (hdr.magic)) != 0
+          || hdr.insn_emit_stamp != insn_emit_stamp
+          || hdr.insn_stat_size != stat_count_size
+          || hdr.insn_use_size != use_count_size)
+        error = 1;
+
+      result = read (fd, merge_stat_count, stat_count_size);
+      if (result != stat_count_size)
+        error = 1;
+
+      result = read (fd, merge_use_count, use_count_size);
+      if (result != use_count_size)
+        error = 1;
+    }
+
+  /* If the statistics file is corrupted or out-of-date, forget everything.  */
+  if (error || result == 0)
+    {
+      memset (merge_stat_count, 0, stat_count_size);
+      memset (merge_use_count, 0, use_count_size);
+      error = 0;
+    }
+
+  for (i = 0; i < max_insn_code; i++)
+    merge_stat_count[i] += insn_stat_count[i];
+
+  for (i = 0; i < max_insn_code * MAX_RECOG_ALTERNATIVES; i++)
+    merge_use_count[i] += insn_use_count[i];
+      
+  if (lseek (fd, 0, SEEK_SET) != 0)
+    error = 1;
+
+  strcpy (hdr.magic, RTX_STAT_MAGIC);
+  hdr.insn_emit_stamp = insn_emit_stamp;
+  hdr.insn_stat_size = stat_count_size;
+  hdr.insn_use_size = use_count_size;
+      
+  result = write (fd, &hdr, sizeof (hdr));
+  error |= (result != sizeof (hdr));
+
+  result = write (fd, merge_stat_count, stat_count_size);
+  if (result != stat_count_size)
+    error = 1;
+  
+  result = write (fd, merge_use_count, use_count_size);
+  if (result != use_count_size)
+    error = 1;
+
+  /* TBD: release the read/write lock we held on the file.  */
+
+  close (fd);
+}
+
+
+/* Collect statistics during insn output generation (final).  */
+void
+rtx_stat_inc (code)
+     int code;
+{
+  if (insn_use_count)
+    {
+      unsigned pos;
+
+      pos = (code * MAX_RECOG_ALTERNATIVES);
+      if (insn_data[code].n_alternatives > 0)
+        pos += which_alternative;
+      
+      insn_use_count[pos]++;
+    }
 }
diff -Nrup --exclude-from=gcc-exclude.lst /src/gnu/cygnus/gcc/gcc/final.c gcc/gcc/final.c
--- /src/gnu/cygnus/gcc/gcc/final.c	Sun Aug  6 11:35:24 2000
+++ gcc/gcc/final.c	Sun Aug  6 12:09:26 2000
@@ -2061,6 +2061,8 @@ get_insn_template (code, insn)
      rtx insn;
 {
   const void *output = insn_data[code].output;
+
+  rtx_stat_inc (code);
   switch (insn_data[code].output_format)
     {
     case INSN_OUTPUT_FORMAT_SINGLE:
diff -Nrup --exclude-from=gcc-exclude.lst /src/gnu/cygnus/gcc/gcc/genemit.c gcc/gcc/genemit.c
--- /src/gnu/cygnus/gcc/gcc/genemit.c	Sun May 28 04:17:58 2000
+++ gcc/gcc/genemit.c	Sun Jun 18 13:17:01 2000
@@ -382,6 +382,9 @@ gen_insn (insn)
     printf ("     rtx operand%d;\n", i);
   printf ("{\n");
 
+  /* Collect statistics.  */
+  printf ("  insn_stat_count[%d]++;\n", insn_code_number);
+
   /* Output code to construct and return the rtl for the instruction body */
 
   if (XVECLEN (insn, 1) == 1)
@@ -443,6 +446,7 @@ gen_expand (expand)
       && operands > max_dup_opno
       && XVECLEN (expand, 1) == 1)
     {
+      printf ("  insn_stat_count[%d]++;\n", insn_code_number);
       printf ("  return ");
       gen_exp (XVECEXP (expand, 1, 0), DEFINE_EXPAND);
       printf (";\n}\n\n");
@@ -456,6 +460,7 @@ gen_expand (expand)
   for (; i <= max_scratch_opno; i++)
     printf ("  rtx operand%d;\n", i);
   printf ("  rtx _val = 0;\n");
+  printf ("  int current_insn_number = %d;\n", insn_code_number);
   printf ("  start_sequence ();\n");
 
   /* The fourth operand of DEFINE_EXPAND is some code to be executed
@@ -537,6 +542,7 @@ gen_expand (expand)
 
   printf ("  _val = gen_sequence ();\n");
   printf ("  end_sequence ();\n");
+  printf ("  insn_stat_count[current_insn_number]++;\n");
   printf ("  return _val;\n}\n\n");
 }
 
@@ -591,6 +597,10 @@ gen_split (split)
   if (GET_CODE (split) == DEFINE_PEEPHOLE2)
     output_peephole2_scratches (split);
 
+  /* Put a local variable for DONE macro and statistics gathering.  */
+  if (XSTR (split, 3) && strlen (XSTR (split, 3)) > 0)
+    printf ("  int current_insn_number = %d;\n", insn_code_number);
+   
   printf ("  start_sequence ();\n");
 
   /* The fourth operand of DEFINE_SPLIT is some code to be executed
@@ -647,6 +657,10 @@ gen_split (split)
 
   printf ("  _val = gen_sequence ();\n");
   printf ("  end_sequence ();\n");
+  if (XSTR (split, 3) && strlen (XSTR (split, 3)) > 0)
+    printf ("  insn_stat_count[current_insn_number]++;\n");
+  else
+    printf ("  insn_stat_count[%d]++;\n", insn_code_number);
   printf ("  return _val;\n}\n\n");
 }
 
@@ -740,7 +754,8 @@ main (argc, argv)
      char **argv;
 {
   rtx desc;
-
+  time_t insn_emit_stamp;
+  
   progname = "genemit";
 
   if (argc <= 1)
@@ -776,7 +791,8 @@ from the machine description file `md'. 
   printf ("#include \"reload.h\"\n");
   printf ("#include \"ggc.h\"\n\n");
   printf ("#define FAIL return (end_sequence (), _val)\n");
-  printf ("#define DONE return (_val = gen_sequence (), end_sequence (), _val)\n");
+  printf ("#define DONE return (insn_stat_count[current_insn_number]++,\\\n");
+  printf ("                     _val = gen_sequence (), end_sequence (), _val)\n");
 
   /* Read the machine description.  */
 
@@ -814,6 +830,11 @@ from the machine description file `md'. 
 
   /* Write out the routine to add CLOBBERs to a pattern.  */
   output_add_clobbers ();
+
+  time (&insn_emit_stamp);
+  printf ("unsigned long insn_stat_count[%d];\n", insn_code_number);
+  printf ("size_t insn_stat_count_size = sizeof (insn_stat_count);\n");
+  printf ("unsigned long insn_emit_stamp = %ld;\n", insn_emit_stamp);
 
   fflush (stdout);
   return (ferror (stdout) != 0 ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE);
diff -Nrup --exclude-from=gcc-exclude.lst /src/gnu/cygnus/gcc/gcc/genstat.c gcc/gcc/genstat.c
--- /src/gnu/cygnus/gcc/gcc/genstat.c	Thu Jan  1 01:00:00 1970
+++ gcc/gcc/genstat.c	Mon Aug  7 11:08:12 2000
@@ -0,0 +1,839 @@
+/* Generate code from to output statistics table.
+   Copyright (C) 2000 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING.  If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+
+/* This program reads the machine description for the compiler target machine
+   and produces a file containing these things:
+
+   1. An array of `struct insn_data', which is indexed by insn code number,
+   which contains:
+
+     a. `name' is the name for that pattern.  Nameless patterns are
+     given a name.
+
+     b. `output' hold either the output template, an array of output
+     templates, or an output function.
+
+     c. `genfun' is the function to generate a body for that pattern,
+     given operands as arguments.
+
+     d. `n_operands' is the number of distinct operands in the pattern
+     for that insn,
+
+     e. `n_dups' is the number of match_dup's that appear in the insn's
+     pattern.  This says how many elements of `recog_data.dup_loc' are
+     significant after an insn has been recognized.
+
+     f. `n_alternatives' is the number of alternatives in the constraints
+     of each pattern.
+
+     g. `output_format' tells what type of thing `output' is.
+
+     h. `operand' is the base of an array of operand data for the insn.
+
+   2. An array of `struct insn_operand data', used by `operand' above.
+
+     a. `predicate', an int-valued function, is the match_operand predicate
+     for this operand.
+
+     b. `constraint' is the constraint for this operand.  This exists
+     only if register constraints appear in match_operand rtx's.
+
+     c. `address_p' indicates that the operand appears within ADDRESS
+     rtx's.  This exists only if there are *no* register constraints
+     in the match_operand rtx's.
+
+     d. `mode' is the machine mode that that operand is supposed to have.
+
+     e. `strict_low', is nonzero for operands contained in a STRICT_LOW_PART.
+
+     f. `eliminable', is nonzero for operands that are matched normally by
+     MATCH_OPERAND; it is zero for operands that should not be changed during
+     register elimination such as MATCH_OPERATORs.
+
+  The code number of an insn is simply its position in the machine
+  description; code numbers are assigned sequentially to entries in
+  the description, starting with code number 0.
+
+  Thus, the following entry in the machine description
+
+    (define_insn "clrdf"
+      [(set (match_operand:DF 0 "general_operand" "")
+	    (const_int 0))]
+      ""
+      "clrd %0")
+
+  assuming it is the 25th entry present, would cause
+  insn_data[24].template to be "clrd %0", and
+  insn_data[24].n_operands to be 1.  */
+
+#include "hconfig.h"
+#include "system.h"
+#include "rtl.h"
+#include "errors.h"
+#include "gensupport.h"
+
+/* No instruction can have more operands than this.  Sorry for this
+   arbitrary limit, but what machine will have an instruction with
+   this many operands?  */
+
+#define MAX_MAX_OPERANDS 40
+
+static int n_occurrences		PARAMS ((int, const char *));
+static const char *strip_whitespace	PARAMS ((const char *));
+
+/* insns in the machine description are assigned sequential code numbers
+   that are used by insn-recog.c (produced by genrecog) to communicate
+   to insn-output.c (produced by this program).  */
+
+static int insn_code_number;
+
+/* This counts all definitions in the md file,
+   for the sake of error messages.  */
+
+static int insn_index_number;
+
+/* This counts all operands used in the md file.  The first is null.  */
+
+static int next_operand_number = 1;
+
+/* Record in this chain all information about the operands we will output.  */
+
+struct operand_data
+{
+  struct operand_data *next;
+  int index;
+  const char *predicate;
+  const char *constraint;
+  enum machine_mode mode;
+  unsigned char n_alternatives;
+  char address_p;
+  char strict_low;
+  char eliminable;
+  char seen;
+};
+
+/* Begin with a null operand at index 0.  */
+
+static struct operand_data null_operand =
+{
+  0, 0, "", "", VOIDmode, 0, 0, 0, 0, 0
+};
+
+static struct operand_data *odata = &null_operand;
+static struct operand_data **odata_end = &null_operand.next;
+
+/* Must match the constants in recog.h.  */
+
+#define INSN_OUTPUT_FORMAT_NONE         0       /* abort */
+#define INSN_OUTPUT_FORMAT_SINGLE       1       /* const char * */
+#define INSN_OUTPUT_FORMAT_MULTI        2       /* const char * const * */
+#define INSN_OUTPUT_FORMAT_FUNCTION     3       /* const char * (*)(...) */
+
+/* Record in this chain all information that we will output,
+   associated with the code number of the insn.  */
+
+struct data
+{
+  struct data *next;
+  const char *name;
+  const char *template;
+  int code_number;
+  int index_number;
+  int n_operands;		/* Number of operands this insn recognizes */
+  int n_dups;			/* Number times match_dup appears in pattern */
+  int n_alternatives;		/* Number of alternatives in each constraint */
+  int operand_number;		/* Operand index in the big array.  */
+  int output_format;		/* INSN_OUTPUT_FORMAT_*.  */
+  struct operand_data operand[MAX_MAX_OPERANDS];
+  int lineno;
+  int code;
+};
+
+/* This variable points to the first link in the insn chain.  */
+
+static struct data *idata, **idata_end = &idata;
+
+static void output_prologue PARAMS ((void));
+static void output_insn_data PARAMS ((void));
+static void scan_operands PARAMS ((struct data *, rtx, int, int));
+static int compare_operands PARAMS ((struct operand_data *,
+				   struct operand_data *));
+static void place_operands PARAMS ((struct data *));
+static void validate_insn_alternatives PARAMS ((struct data *));
+static void gen_insn PARAMS ((rtx, int));
+static void gen_peephole PARAMS ((rtx, int));
+static void gen_expand PARAMS ((rtx, int));
+static void gen_split PARAMS ((rtx, int));
+
+const char *
+get_insn_name (index)
+     int index;
+{
+  static char buf[100];
+
+  struct data *i, *last_named = NULL;
+  for (i = idata; i ; i = i->next)
+    {
+      if (i->index_number == index)
+	return i->name;
+      if (i->name)
+	last_named = i;
+    }
+
+  if (last_named)
+    sprintf(buf, "%s+%d", last_named->name, index - last_named->index_number);
+  else
+    sprintf(buf, "insn %d", index);
+
+  return buf;
+}
+
+static void
+output_prologue ()
+{
+  printf ("/* Generated automatically by the program `genoutput'\n\
+from the machine description file `md'.  */\n\n");
+
+  printf ("#include \"config.h\"\n");
+  printf ("#include \"system.h\"\n");
+  printf ("#include \"istat.h\"\n");
+
+  printf ("\n\nconst char* md_file = \"%s\";\n\n", read_rtx_filename);
+}
+
+static void
+output_insn_data ()
+{
+  register struct data *d;
+  int name_offset = 0;
+  int next_name_offset;
+  const char * last_name = 0;
+  const char * next_name = 0;
+  register struct data *n;
+
+  for (n = idata, next_name_offset = 1; n; n = n->next, next_name_offset++)
+    if (n->name)
+      {
+	next_name = n->name;
+	break;
+      }
+
+  printf ("\nint insn_max_code = %d;\n", insn_code_number);
+  printf ("\nstruct insn_stat insn_data_stat[] = \n{\n");
+
+  for (d = idata; d; d = d->next)
+    {
+      printf ("  {\n");
+
+      if (d->name)
+	{
+	  printf ("    \"%s\",\n", d->name);
+	  name_offset = 0;
+	  last_name = d->name;
+	  next_name = 0;
+	  for (n = d->next, next_name_offset = 1; n;
+	       n = n->next, next_name_offset++)
+	    {
+	      if (n->name)
+		{
+		  next_name = n->name;
+		  break;
+		}
+	    }
+	}
+      else
+	{
+	  name_offset++;
+	  if (next_name && (last_name == 0
+			    || name_offset > next_name_offset / 2))
+	    printf ("    \"%s-%d\",\n", next_name,
+		    next_name_offset - name_offset);
+	  else
+	    printf ("    \"%s+%d\",\n", last_name, name_offset);
+	}
+
+      printf ("    %d,\n", d->code);
+      printf ("    %d,\n", d->lineno);
+      printf ("    %d\n", d->n_alternatives);
+      printf("  },\n");
+    }
+  printf ("};\n\n\n");
+}
+
+
+/* Stores in max_opno the largest operand number present in `part', if
+   that is larger than the previous value of max_opno, and the rest of
+   the operand data into `d->operand[i]'.
+
+   THIS_ADDRESS_P is nonzero if the containing rtx was an ADDRESS.
+   THIS_STRICT_LOW is nonzero if the containing rtx was a STRICT_LOW_PART.  */
+
+static int max_opno;
+static int num_dups;
+
+static void
+scan_operands (d, part, this_address_p, this_strict_low)
+     struct data *d;
+     rtx part;
+     int this_address_p;
+     int this_strict_low;
+{
+  register int i, j;
+  register const char *format_ptr;
+  int opno;
+
+  if (part == 0)
+    return;
+
+  switch (GET_CODE (part))
+    {
+    case MATCH_OPERAND:
+      opno = XINT (part, 0);
+      if (opno > max_opno)
+	max_opno = opno;
+      if (max_opno >= MAX_MAX_OPERANDS)
+	{
+	  error ("Too many operands (%d) in definition %s.\n",
+		 max_opno + 1, get_insn_name (insn_index_number));
+	  return;
+	}
+      if (d->operand[opno].seen)
+	error ("Definition %s specified operand number %d more than once.\n",
+	       get_insn_name (insn_index_number), opno);
+      d->operand[opno].seen = 1;
+      d->operand[opno].mode = GET_MODE (part);
+      d->operand[opno].strict_low = this_strict_low;
+      d->operand[opno].predicate = XSTR (part, 1);
+      d->operand[opno].constraint = strip_whitespace (XSTR (part, 2));
+      d->operand[opno].n_alternatives
+	= n_occurrences (',', d->operand[opno].constraint) + 1;
+      d->operand[opno].address_p = this_address_p;
+      d->operand[opno].eliminable = 1;
+      return;
+
+    case MATCH_SCRATCH:
+      opno = XINT (part, 0);
+      if (opno > max_opno)
+	max_opno = opno;
+      if (max_opno >= MAX_MAX_OPERANDS)
+	{
+	  error ("Too many operands (%d) in definition %s.\n",
+		 max_opno + 1, get_insn_name (insn_index_number));
+	  return;
+	}
+      if (d->operand[opno].seen)
+	error ("Definition %s specified operand number %d more than once.\n",
+	       get_insn_name (insn_index_number), opno);
+      d->operand[opno].seen = 1;
+      d->operand[opno].mode = GET_MODE (part);
+      d->operand[opno].strict_low = 0;
+      d->operand[opno].predicate = "scratch_operand";
+      d->operand[opno].constraint = strip_whitespace (XSTR (part, 1));
+      d->operand[opno].n_alternatives
+	= n_occurrences (',', d->operand[opno].constraint) + 1;
+      d->operand[opno].address_p = 0;
+      d->operand[opno].eliminable = 0;
+      return;
+
+    case MATCH_OPERATOR:
+    case MATCH_PARALLEL:
+      opno = XINT (part, 0);
+      if (opno > max_opno)
+	max_opno = opno;
+      if (max_opno >= MAX_MAX_OPERANDS)
+	{
+	  error ("Too many operands (%d) in definition %s.\n",
+		 max_opno + 1, get_insn_name (insn_index_number));
+	  return;
+	}
+      if (d->operand[opno].seen)
+	error ("Definition %s specified operand number %d more than once.\n",
+	       get_insn_name (insn_index_number), opno);
+      d->operand[opno].seen = 1;
+      d->operand[opno].mode = GET_MODE (part);
+      d->operand[opno].strict_low = 0;
+      d->operand[opno].predicate = XSTR (part, 1);
+      d->operand[opno].constraint = 0;
+      d->operand[opno].address_p = 0;
+      d->operand[opno].eliminable = 0;
+      for (i = 0; i < XVECLEN (part, 2); i++)
+	scan_operands (d, XVECEXP (part, 2, i), 0, 0);
+      return;
+
+    case MATCH_DUP:
+    case MATCH_OP_DUP:
+    case MATCH_PAR_DUP:
+      ++num_dups;
+      return;
+
+    case ADDRESS:
+      scan_operands (d, XEXP (part, 0), 1, 0);
+      return;
+
+    case STRICT_LOW_PART:
+      scan_operands (d, XEXP (part, 0), 0, 1);
+      return;
+      
+    default:
+      break;
+    }
+
+  format_ptr = GET_RTX_FORMAT (GET_CODE (part));
+
+  for (i = 0; i < GET_RTX_LENGTH (GET_CODE (part)); i++)
+    switch (*format_ptr++)
+      {
+      case 'e':
+      case 'u':
+	scan_operands (d, XEXP (part, i), 0, 0);
+	break;
+      case 'E':
+	if (XVEC (part, i) != NULL)
+	  for (j = 0; j < XVECLEN (part, i); j++)
+	    scan_operands (d, XVECEXP (part, i, j), 0, 0);
+	break;
+      }
+}
+
+/* Compare two operands for content equality.  */
+
+static int
+compare_operands (d0, d1)
+     struct operand_data *d0, *d1;
+{
+  const char *p0, *p1;
+
+  p0 = d0->predicate;
+  if (!p0)
+    p0 = "";
+  p1 = d1->predicate;
+  if (!p1)
+    p1 = "";
+  if (strcmp (p0, p1) != 0)
+    return 0;
+
+  p0 = d0->constraint;
+  if (!p0)
+    p0 = "";
+  p1 = d1->constraint;
+  if (!p1)
+    p1 = "";
+  if (strcmp (p0, p1) != 0)
+    return 0;
+
+  if (d0->mode != d1->mode)
+    return 0;
+
+  if (d0->strict_low != d1->strict_low)
+    return 0;
+
+  if (d0->eliminable != d1->eliminable)
+    return 0;
+
+  return 1;
+}
+
+/* Scan the list of operands we've already committed to output and either
+   find a subsequence that is the same, or allocate a new one at the end.  */
+
+static void
+place_operands (d)
+     struct data *d;
+{
+  struct operand_data *od, *od2;
+  int i;
+
+  if (d->n_operands == 0)
+    {
+      d->operand_number = 0;
+      return;
+    }
+
+  /* Brute force substring search.  */
+  for (od = odata, i = 0; od; od = od->next, i = 0)
+    if (compare_operands (od, &d->operand[0]))
+      {
+	od2 = od->next;
+	i = 1;
+	while (1)
+	  {
+	    if (i == d->n_operands)
+	      goto full_match;
+	    if (od2 == NULL)
+	      goto partial_match;
+	    if (! compare_operands (od2, &d->operand[i]))
+	      break;
+	    ++i, od2 = od2->next;
+	  }
+      }
+
+  /* Either partial match at the end of the list, or no match.  In either
+     case, we tack on what operands are remaining to the end of the list.  */
+ partial_match:
+  d->operand_number = next_operand_number - i;
+  for (; i < d->n_operands; ++i)
+    {
+      od2 = &d->operand[i];
+      *odata_end = od2;
+      odata_end = &od2->next;
+      od2->index = next_operand_number++;
+    }
+  *odata_end = NULL;
+  return;
+
+ full_match:
+  d->operand_number = od->index;
+  return;
+}
+
+
+
+/* Check insn D for consistency in number of constraint alternatives.  */
+
+static void
+validate_insn_alternatives (d)
+     struct data *d;
+{
+  register int n = 0, start;
+
+  /* Make sure all the operands have the same number of alternatives
+     in their constraints.  Let N be that number.  */
+  for (start = 0; start < d->n_operands; start++)
+    if (d->operand[start].n_alternatives > 0)
+      {
+	if (n == 0)
+	  n = d->operand[start].n_alternatives;
+	else if (n != d->operand[start].n_alternatives)
+	  error ("wrong number of alternatives in operand %d of insn %s",
+		 start, get_insn_name (d->index_number));
+      }
+
+  /* Record the insn's overall number of alternatives.  */
+  d->n_alternatives = n;
+}
+
+/* Look at a define_insn just read.  Assign its code number.  Record
+   on idata the template and the number of arguments.  If the insn has
+   a hairy output action, output a function for now.  */
+
+static void
+gen_insn (insn, lineno)
+     rtx insn;
+     int lineno;
+{
+  register struct data *d = (struct data *) xmalloc (sizeof (struct data));
+  register int i;
+
+  d->code = GET_CODE (insn);
+  d->lineno = lineno;
+  d->code_number = insn_code_number++;
+  d->index_number = insn_index_number;
+  if (XSTR (insn, 0)[0])
+    d->name = XSTR (insn, 0);
+  else
+    d->name = 0;
+
+  /* Build up the list in the same order as the insns are seen
+     in the machine description.  */
+  d->next = 0;
+  *idata_end = d;
+  idata_end = &d->next;
+
+  max_opno = -1;
+  num_dups = 0;
+  memset (d->operand, 0, sizeof (d->operand));
+
+  for (i = 0; i < XVECLEN (insn, 1); i++)
+    scan_operands (d, XVECEXP (insn, 1, i), 0, 0);
+
+  d->n_operands = max_opno + 1;
+  d->n_dups = num_dups;
+
+  validate_insn_alternatives (d);
+  place_operands (d);
+}
+
+/* Look at a define_peephole just read.  Assign its code number.
+   Record on idata the template and the number of arguments.
+   If the insn has a hairy output action, output it now.  */
+
+static void
+gen_peephole (peep, lineno)
+     rtx peep;
+     int lineno;
+{
+  register struct data *d = (struct data *) xmalloc (sizeof (struct data));
+  register int i;
+
+  d->code = GET_CODE (peep);
+  d->lineno = lineno;
+  d->code_number = insn_code_number++;
+  d->index_number = insn_index_number;
+  d->name = 0;
+
+  /* Build up the list in the same order as the insns are seen
+     in the machine description.  */
+  d->next = 0;
+  *idata_end = d;
+  idata_end = &d->next;
+
+  max_opno = -1;
+  num_dups = 0;
+  memset (d->operand, 0, sizeof (d->operand));
+
+  /* Get the number of operands by scanning all the patterns of the
+     peephole optimizer.  But ignore all the rest of the information
+     thus obtained.  */
+  for (i = 0; i < XVECLEN (peep, 0); i++)
+    scan_operands (d, XVECEXP (peep, 0, i), 0, 0);
+
+  d->n_operands = max_opno + 1;
+  d->n_dups = 0;
+
+  validate_insn_alternatives (d);
+  place_operands (d);
+}
+
+/* Process a define_expand just read.  Assign its code number,
+   only for the purposes of `insn_gen_function'.  */
+
+static void
+gen_expand (insn, lineno)
+     rtx insn;
+     int lineno;
+{
+  register struct data *d = (struct data *) xmalloc (sizeof (struct data));
+  register int i;
+
+  d->code = GET_CODE (insn);
+  d->lineno = lineno;
+  d->code_number = insn_code_number++;
+  d->index_number = insn_index_number;
+  if (XSTR (insn, 0)[0])
+    d->name = XSTR (insn, 0);
+  else
+    d->name = 0;
+
+  /* Build up the list in the same order as the insns are seen
+     in the machine description.  */
+  d->next = 0;
+  *idata_end = d;
+  idata_end = &d->next;
+
+  max_opno = -1;
+  num_dups = 0;
+  memset (d->operand, 0, sizeof (d->operand));
+
+  /* Scan the operands to get the specified predicates and modes,
+     since expand_binop needs to know them.  */
+
+  if (XVEC (insn, 1))
+    for (i = 0; i < XVECLEN (insn, 1); i++)
+      scan_operands (d, XVECEXP (insn, 1, i), 0, 0);
+
+  d->n_operands = max_opno + 1;
+  d->n_dups = num_dups;
+  d->template = 0;
+  d->output_format = INSN_OUTPUT_FORMAT_NONE;
+
+  validate_insn_alternatives (d);
+  place_operands (d);
+}
+
+/* Process a define_split just read.  Assign its code number,
+   only for reasons of consistency and to simplify genrecog.  */
+
+static void
+gen_split (split, lineno)
+     rtx split;
+     int lineno;
+{
+  register struct data *d = (struct data *) xmalloc (sizeof (struct data));
+  register int i;
+
+  d->code = GET_CODE (split);
+  d->lineno = lineno;
+  d->code_number = insn_code_number++;
+  d->index_number = insn_index_number;
+  d->name = 0;
+
+  /* Build up the list in the same order as the insns are seen
+     in the machine description.  */
+  d->next = 0;
+  *idata_end = d;
+  idata_end = &d->next;
+
+  max_opno = -1;
+  num_dups = 0;
+  memset (d->operand, 0, sizeof (d->operand));
+
+  /* Get the number of operands by scanning all the patterns of the
+     split patterns.  But ignore all the rest of the information thus
+     obtained.  */
+  for (i = 0; i < XVECLEN (split, 0); i++)
+    scan_operands (d, XVECEXP (split, 0, i), 0, 0);
+
+  d->n_operands = max_opno + 1;
+  d->n_dups = 0;
+  d->n_alternatives = 0;
+  d->template = 0;
+  d->output_format = INSN_OUTPUT_FORMAT_NONE;
+
+  place_operands (d);
+}
+
+#if 0
+PTR
+xmalloc (size)
+  size_t size;
+{
+  register PTR val = (PTR) malloc (size);
+
+  if (val == 0)
+    fatal ("virtual memory exhausted");
+  return val;
+}
+
+PTR
+xrealloc (old, size)
+  PTR old;
+  size_t size;
+{
+  register PTR ptr;
+  if (old)
+    ptr = (PTR) realloc (old, size);
+  else
+    ptr = (PTR) malloc (size);
+  if (!ptr)
+    fatal ("virtual memory exhausted");
+  return ptr;
+}
+#endif
+extern int main PARAMS ((int, char **));
+
+int
+main (argc, argv)
+     int argc;
+     char **argv;
+{
+  rtx desc;
+  register int c;
+
+  progname = "genstat";
+
+  if (argc <= 1)
+    fatal ("No input file name.");
+
+  if (init_md_reader (argv[1]) != SUCCESS_EXIT_CODE)
+    return (FATAL_EXIT_CODE);
+
+  read_rtx_filename = argv[1];
+
+  output_prologue ();
+  insn_code_number = 0;
+  insn_index_number = 0;
+
+  /* Read the machine description.  */
+
+  /* Read the machine description.  */
+
+  while (1)
+    {
+      int line_no;
+
+      desc = read_md_rtx (&line_no, &insn_code_number);
+      if (desc == NULL)
+	break;
+
+      switch (GET_CODE (desc))
+	{
+	  case DEFINE_INSN:
+	      gen_insn (desc, line_no);
+	      break;
+
+	  case DEFINE_EXPAND:
+	      gen_expand (desc, line_no);
+	      break;
+
+	  case DEFINE_SPLIT:
+	      gen_split (desc, line_no);
+	      break;
+
+	  case DEFINE_PEEPHOLE2:
+	      gen_split (desc, line_no);
+	      break;
+
+          case DEFINE_PEEPHOLE:
+              gen_peephole (desc, line_no);
+              break;
+	  default:
+	      break;
+	 }
+      ++insn_index_number;
+    }
+  printf("\n\n");
+  output_insn_data ();
+
+  fflush (stdout);
+  return (ferror (stdout) != 0 || have_error
+	? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE);
+}
+
+/* Return the number of occurrences of character C in string S or
+   -1 if S is the null string.  */
+
+static int
+n_occurrences (c, s)
+     int c;
+     const char *s;
+{
+  int n = 0;
+
+  if (s == 0 || *s == '\0')
+    return -1;
+
+  while (*s)
+    n += (*s++ == c);
+
+  return n;
+}
+
+/* Remove whitespace in `s' by moving up characters until the end.
+   Return a new string.  */
+
+static const char *
+strip_whitespace (s)
+     const char *s;
+{
+  char *p, *q;
+  char ch;
+
+  if (s == 0)
+    return 0;
+
+  p = q = xmalloc (strlen (s) + 1);
+  while ((ch = *s++) != '\0')
+    if (! ISSPACE (ch))
+      *p++ = ch;
+
+  *p = '\0';
+  return q;
+}
diff -Nrup --exclude-from=gcc-exclude.lst /src/gnu/cygnus/gcc/gcc/istat.h gcc/gcc/istat.h
--- /src/gnu/cygnus/gcc/gcc/istat.h	Thu Jan  1 01:00:00 1970
+++ gcc/gcc/istat.h	Mon Aug  7 11:07:47 2000
@@ -0,0 +1,31 @@
+/* Compilation switch flag definitions for GNU CC.
+   Copyright (C) 1987, 1988, 1994, 1995, 1996, 1997, 1998, 1999, 2000
+   Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING.  If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+struct insn_stat
+{
+  const char *name;
+  int code;
+  int lineno;
+  int n_alternatives;
+};
+extern struct insn_stat insn_data_stat[];
+extern const char *md_file;
+extern int insn_max_code;
diff -Nrup --exclude-from=gcc-exclude.lst /src/gnu/cygnus/gcc/gcc/prstat.c gcc/gcc/prstat.c
--- /src/gnu/cygnus/gcc/gcc/prstat.c	Thu Jan  1 01:00:00 1970
+++ gcc/gcc/prstat.c	Sun Jun 18 14:33:59 2000
@@ -0,0 +1,407 @@
+/* prstat - Report statistics collected during gcc compilation runs
+   Copyright (C) 2000 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING.  If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+#include "hconfig.h"
+#include "system.h"
+#include "rtl.h"
+#include "errors.h"
+#include "istat.h"
+
+#include <setjmp.h>
+#include <signal.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#undef abort
+
+#include "version.h"
+
+/* Include getopt.h for the sake of getopt_long. */
+#include "getopt.h"
+
+static void usage PARAMS ((void)) ATTRIBUTE_NORETURN;
+extern int main PARAMS ((int, char **const));
+extern int rtx_load_statistics PARAMS((const char*));
+
+
+static unsigned long* insn_use_count = 0;
+unsigned long total_used = 0;
+unsigned long total_used_insns = 0;
+unsigned long total_unused_insns = 0;
+
+struct stat_info
+{
+  struct insn_stat* insn;
+  unsigned long* counts;
+  unsigned long emit_count;
+  unsigned long total_insn_count;
+  int used_alternatives;
+};
+
+static struct stat_info* stat_info;
+#define MAX_RECOG_ALTERNATIVES 30
+
+int
+rtx_load_statistics (file)
+     const char *file;
+{
+  struct rtx_stat_header hdr;
+  int fd;
+  int err;
+  int result;
+  int i, j;
+  unsigned long *emit_count;
+  unsigned long insn_count;
+  
+  fd = open (file, O_RDONLY);
+  if (fd < 0)
+    {
+      error ("Cannot read statistics file `%s'.\n", file);
+      return -1;
+    }
+  
+  result = read (fd, &hdr, sizeof (hdr));
+  err = (result != sizeof (hdr)
+         || memcmp (hdr.magic, RTX_STAT_MAGIC, sizeof (hdr.magic)) != 0);
+  
+  if (err == 0)
+    {
+      emit_count = (unsigned long*) xmalloc (hdr.insn_stat_size);
+      stat_info = (struct stat_info*) xmalloc (sizeof (struct stat_info)
+                                           * (insn_max_code - 1));
+      insn_use_count = (unsigned long*) xmalloc (hdr.insn_use_size);
+      memset (insn_use_count, 0, hdr.insn_use_size);
+
+      result = read (fd, emit_count, hdr.insn_stat_size);
+      if (result != (int) hdr.insn_stat_size)
+        err++;
+      result = read (fd, insn_use_count, hdr.insn_use_size);
+      if (result != (int) hdr.insn_use_size)
+        err++;
+    }
+  close (fd);
+  if (err)
+    {
+      error ("Invalid statistics file `%s'.\n", file);
+      return -1;
+    }
+  
+  for (i = 0; i < insn_max_code-1; i++)
+    {
+      stat_info[i].insn = &insn_data_stat[i];
+      stat_info[i].counts = &insn_use_count[i * MAX_RECOG_ALTERNATIVES];
+      stat_info[i].used_alternatives = 0;
+      stat_info[i].emit_count = emit_count[i];
+      insn_count = 0;
+
+      for (j = 0; j < insn_data_stat[i].n_alternatives; j++)
+        {
+          insn_count += stat_info[i].counts[j];
+          if (stat_info[i].counts[j])
+            stat_info[i].used_alternatives++;
+        }
+
+      if (insn_count == 0)
+        insn_count = emit_count[i];
+      
+      stat_info[i].total_insn_count = insn_count;
+      total_used += insn_count;
+      if (insn_count == 0)
+        {
+          total_unused_insns++;
+        }
+      else
+        {
+          total_used_insns++;
+        }
+    }
+  return 0;
+}
+
+int sort_count_flag = 0;
+int reverse_flag = 0;
+int version_flag = 0;
+int quiet_flag = 0;
+int skip_empty_flag = 0;
+int skip_non_empty_flag = 0;
+int emit_flag = 0;
+int output_flag = 0;
+int split_flag = 0;
+
+int
+cmp_data (e1, e2)
+     void* e1;
+     void* e2;
+{
+  struct stat_info* s1 = (struct stat_info*) e1;
+  struct stat_info* s2 = (struct stat_info*) e2;
+  int result;
+
+  if (emit_flag && output_flag == 0)
+    {
+      if (s1->emit_count < s2->emit_count)
+        result = -1;
+      else if (s1->emit_count > s2->emit_count)
+        result = 1;
+      else
+        result = 0;
+    }
+  else if (sort_count_flag)
+    {
+      if (s1->total_insn_count < s2->total_insn_count)
+        result = -1;
+      else if (s1->total_insn_count > s2->total_insn_count)
+        result = 1;
+      else
+        result = 0;
+    }
+
+  if (reverse_flag)
+    {
+      if (result == 1)
+        result = -1;
+      else if (result == -1)
+        result = 1;
+    }
+  return result;
+}
+
+
+static struct option longopts[] =
+{
+  {"version", 0, 0, 'V'},
+  {"quiet", 0, 0, 'q'},
+  {"silent", 0, 0, 'q'},
+  {"empty", 0, 0, 'e'},
+  {"used", 0, 0, 'u'},
+  {"file", 1, 0, 'f'},
+  {0, 0, 0, 0}
+};
+
+/* Give a message indicating the proper way to invoke this program and then
+   exit with non-zero status.  */
+
+static void
+usage ()
+{
+  fprintf (stderr,
+           "%s: usage '%s [-f file] [-srvqeu]\n",
+           progname, progname);
+  exit (FATAL_EXIT_CODE);
+}
+
+int
+main (argc, argv)
+     int argc;
+     char **const argv;
+{
+  int longind;
+  int c;
+  int i;
+  int result;
+  char* stat_file;
+  
+  progname = strrchr (argv[0], DIR_SEPARATOR);
+#ifdef DIR_SEPARATOR_2
+  {
+    char *slash;
+
+    slash = strrchr (progname ? progname : argv[0], DIR_SEPARATOR_2);
+    if (slash)
+      progname = slash;
+  }
+#endif
+  progname = progname ? progname+1 : argv[0];
+
+  stat_file = getenv ("GCC_STAT");
+  while ((c = getopt_long (argc, argv,
+			   "f:srvqeuSOE",
+			   longopts, &longind)) != EOF)
+    {
+      if (c == 0)		/* Long option.  */
+	c = longopts[longind].val;
+      switch (c)
+	{
+	case 'f':
+          stat_file = optarg;
+	  break;
+        case 'V':
+	case 'v':
+	  version_flag = 1;
+	  break;
+	case 'q':
+	  quiet_flag = 1;
+	  break;
+        case 'E':
+          emit_flag = 1;
+          break;
+        case 'S':
+          split_flag = 1;
+          break;
+        case 'O':
+          output_flag = 1;
+          break;
+        case 's':
+          sort_count_flag = 1;
+          break;
+        case 'u':
+          skip_empty_flag = 1;
+          skip_non_empty_flag = 0;
+          break;
+        case 'e':
+          skip_empty_flag = 0;
+          skip_non_empty_flag = 1;
+          break;
+        case 'r':
+          reverse_flag = 1;
+          break;
+	default:
+	  usage ();
+	}
+    }
+
+  if (stat_file == 0)
+    {
+      usage ();
+    }
+  
+  result = rtx_load_statistics (stat_file);
+  if (result != 0)
+    {
+      fprintf (stderr, "Cannot load '%s'\n", stat_file);
+      return 1;
+    }
+  if (sort_count_flag)
+    {
+      qsort (stat_info, insn_max_code-1, sizeof (struct stat_info),
+             cmp_data);
+    }
+
+  if (emit_flag == 0 && output_flag == 0 && split_flag == 0)
+    {
+      emit_flag = 1;
+      output_flag = 1;
+      split_flag = 1;
+    }
+  printf ("Total number of insns generated :  %9ld\n", total_used);
+  printf ("Total number of pattern used    :  %3ld (%3ld%%)\n",
+          total_used_insns,
+          (total_used_insns * 100 + insn_max_code - 1) / insn_max_code);
+  printf ("Total number of unused patterns : %3ld\n",
+          total_unused_insns);
+  
+  for (i = 0; i < insn_max_code-1; i++)
+    {
+      struct stat_info* st = &stat_info[i];
+      const char* type;
+      int this_insn_ok;
+
+      if (skip_empty_flag
+          && (output_flag == 0 || st->total_insn_count == 0)
+          && ((emit_flag == 0 && split_flag == 0) || st->emit_count == 0))
+        continue;
+      if (skip_non_empty_flag
+          && ((output_flag && st->total_insn_count != 0)
+              || ((emit_flag || split_flag) && st->emit_count != 0)))
+        continue;
+
+      switch (st->insn->code)
+        {
+        case DEFINE_INSN:
+          if (emit_flag == 0 && output_flag == 0)
+            continue;
+
+          if (isalpha (st->insn->name[0]))
+            this_insn_ok = st->emit_count != 0
+              && st->used_alternatives == st->insn->n_alternatives;
+          else
+            this_insn_ok = st->used_alternatives == st->insn->n_alternatives;
+          type = "I ";
+          break;
+
+        case DEFINE_PEEPHOLE:
+          type = "P ";
+          break;
+
+        case DEFINE_EXPAND:
+          if (emit_flag == 0)
+            continue;
+
+          this_insn_ok = st->emit_count != 0;
+          type = "E ";
+          break;
+
+        case DEFINE_SPLIT:
+          if (split_flag == 0)
+            continue;
+
+          this_insn_ok = st->emit_count != 0;
+          type = "S ";
+          break;
+
+        case DEFINE_PEEPHOLE2:
+          type = "p ";
+          break;
+
+        default:
+          type = "? ";
+          break;
+        }
+      printf ("%5d %s %-20.20s %s ",
+              st->insn->lineno,
+              type,
+              st->insn->name,
+              this_insn_ok == 0 ? "-" : "*");
+      if (emit_flag
+          && (st->insn->code == DEFINE_INSN
+              || st->insn->code == DEFINE_EXPAND))
+        {
+          printf ("  emit %6ld ",
+                  st->emit_count);
+        }
+      if (split_flag && (st->insn->code == DEFINE_SPLIT))
+        {
+          printf (" split %6ld ",
+                  st->emit_count);
+        }
+      if (output_flag)
+        {
+          printf ("  out %6ld alt %d [%d] ",
+                  st->total_insn_count,
+                  st->used_alternatives,
+                  st->insn->n_alternatives);
+
+          if (st->used_alternatives != st->insn->n_alternatives)
+            {
+              int i;
+              
+              printf (" {");
+              for (i = 0; i < st->insn->n_alternatives; i++)
+                {
+                  if (st->counts[i] == 0)
+                    printf (" %d", i);
+                }
+              printf (" }");
+            }
+        }
+      printf ("\n");
+    }
+  return 0;
+}
diff -Nrup --exclude-from=gcc-exclude.lst /src/gnu/cygnus/gcc/gcc/rtl.h gcc/gcc/rtl.h
--- /src/gnu/cygnus/gcc/gcc/rtl.h	Sun Aug  6 11:38:37 2000
+++ gcc/gcc/rtl.h	Sun Aug  6 12:09:31 2000
@@ -1709,8 +1709,41 @@ void restore_line_number_status PARAMS (
 extern void renumber_insns                      PARAMS ((FILE *));
 extern void remove_unnecessary_notes             PARAMS ((void));
 
+/* Machine description pattern statistics.
+
+   The file has a simple binary format for efficiency and simplicity
+   reasons.  The format is the following:
+
+   +----------------+
+   |rtx_stat_header |   Magic + genemit stamp + statistics sizes
+   +----------------+
+   | insn-emit      |   Statistics collected during generation of insns
+   | statistics     |   and during split.
+   +----------------+
+   | final output   |   Statistics collected by final (what is generated)
+   | statistics     |
+   +----------------+
+
+*/
+#define RTX_STAT_MAGIC "RTX-STAT"
+
+struct rtx_stat_header 
+{
+  char          magic[sizeof (RTX_STAT_MAGIC)];
+  unsigned long insn_emit_stamp;
+  unsigned long insn_stat_size;
+  unsigned long insn_use_size;
+};
+
+extern void rtx_init_statistics PARAMS((const char *));
+extern void rtx_save_statistics PARAMS((const char *));
+extern void rtx_stat_inc PARAMS((int));
+
 /* In insn-emit.c */
 extern void add_clobbers		PARAMS ((rtx, int));
+extern unsigned long insn_stat_count[];
+extern size_t insn_stat_count_size;
+extern unsigned long insn_emit_stamp;
 
 /* In combine.c */
 extern int combine_instructions		PARAMS ((rtx, unsigned int));
diff -Nrup --exclude-from=gcc-exclude.lst /src/gnu/cygnus/gcc/gcc/toplev.c gcc/gcc/toplev.c
--- /src/gnu/cygnus/gcc/gcc/toplev.c	Sun Aug  6 11:39:18 2000
+++ gcc/gcc/toplev.c	Sun Aug  6 12:09:32 2000
@@ -226,6 +226,10 @@ int input_file_stack_tick;
 
 const char *dump_base_name;
 
+/* Name of file where pattern statistics must be collected.  */
+
+const char *dump_stat_name;
+
 /* Bit flags that specify the machine subtype we are compiling for.
    Bits are tested using macros TARGET_... defined in the tm.h file
    and set by `-m...' switches.  Must be defined in rtlanal.c.  */
@@ -3798,6 +3806,7 @@ display_help ()
   printf (_("  -version                Display the compiler's version\n"));
   printf (_("  -d[letters]             Enable dumps from specific passes of the compiler\n"));
   printf (_("  -dumpbase <file>        Base name to be used for dumps from specific passes\n"));
+  printf (_("  -dumpstat <file>        Dump machine description pattern stats\n"));
 #if defined INSN_SCHEDULING
   printf (_("  -fsched-verbose=<number> Set the verbosity level of the scheduler\n"));
 #endif
@@ -4310,6 +4322,14 @@ independent_decode_option (argc, argv, s
 	  dump_base_name = argv[1];
 	  return 2;
 	}
+      else if (!strcmp (arg, "dumpstat"))
+        {
+          if (argc == 1)
+            return 0;
+
+          dump_stat_name = argv[1];
+          return 2;
+        }
       else
 	decode_d_option (arg + 1);
       break;
@@ -4787,7 +4807,14 @@ main (argc, argv)
 	print_switch_values (stderr, 0, MAX_LINE, "", " ", "\n");
     }
 
+  /* Initialize pattern statistics.  */
+  rtx_init_statistics (dump_stat_name);
+
   compile_file (filename);
+
+  /* Save pattern statistics collected durring this compilation
+     and merge them with previous compilations.  */
+  rtx_save_statistics (dump_stat_name);
 
 #if !defined(OS2) && !defined(VMS) && (!defined(_WIN32) || defined (__CYGWIN__)) && !defined(__INTERIX)
   if (flag_print_mem)


More information about the Gcc-patches mailing list