diff mbox series

genemit: Split insn-emit.cc into ten files.

Message ID de0f7bdc-d236-4f5b-9504-d5bfb215d023@gmail.com
State New
Headers show
Series genemit: Split insn-emit.cc into ten files. | expand

Commit Message

Robin Dapp Oct. 12, 2023, 8:45 p.m. UTC
Hi,

on riscv insn-emit.cc has grown to over 1.2 mio lines of code and
compiling it takes considerable time.
Therefore, this patch adjust genemit to create ten files insn-emit-1.cc
to insn-emit-10.cc.  In order to do so it first counts the number of
available patterns, calculates the number of patterns per file and
starts a new file whenever that number is reached.  Most of the changes
are mechanical - genemit would output to stdout and I changed it to
write to a FILE.

Similar to match.pd a configure option --with-emitinsn-partitions=num
is introduced that makes the number of partition configurable.

This survived some bootstraps on aarch64, x86 and power10 as well as
regular cross builds on riscv.  I didn't to extensive timing on targets
but on my machine the compilation of all 10 insn-emit-...cc files for
riscv takes about 40 seconds now while the full file took roughly
10 minutes.

Testsuite is unchanged on all but x86 where, strangely, I saw several
illegal instructions in the pch tests.  Those were not reproducible
in a second manual test suite run.  I'm just running another full
bootstrap and testsuite cycle with the latest trunk.

Still figured I'd send the current state in order to get some feedback
about the general approach.

Regards
 Robin

gcc/ChangeLog:

	PR bootstrap/84402
	PR target/111600

	* Makefile.in: Handle split insn-emit.cc.
	* configure: Regenerate.
	* configure.ac: Add --with-insnemit-partitions.
	* genemit.cc (output_peephole2_scratches): Print to file instead
	of stdout.
	(print_code): Ditto.
	(gen_rtx_scratch): Ditto.
	(gen_exp): Ditto.
	(gen_emit_seq): Ditto.
	(emit_c_code): Ditto.
	(gen_insn): Ditto.
	(gen_expand): Ditto.
	(gen_split): Ditto.
	(output_add_clobbers): Ditto.
	(output_added_clobbers_hard_reg_p): Ditto.
	(print_overload_arguments): Ditto.
	(print_overload_test): Ditto.
	(handle_overloaded_code_for): Ditto.
	(handle_overloaded_gen): Ditto.
	(print_header): New function.
	(handle_arg): New function.
	(main): Split output into 10 files.
	* gensupport.cc (count_patterns): New function.
	* gensupport.h (count_patterns): Define.
	* read-md.cc (md_reader::print_md_ptr_loc): Add file argument.
	* read-md.h (class md_reader): Change definition.
---
 gcc/Makefile.in   |  37 +++-
 gcc/configure     |  24 +-
 gcc/configure.ac  |  13 ++
 gcc/genemit.cc    | 542 +++++++++++++++++++++++++---------------------
 gcc/gensupport.cc |  36 +++
 gcc/gensupport.h  |   1 +
 gcc/read-md.cc    |   4 +-
 gcc/read-md.h     |   2 +-
 8 files changed, 404 insertions(+), 255 deletions(-)

Comments

Tamar Christina Oct. 13, 2023, 6:58 a.m. UTC | #1
Hi,

Thanks for doing this!

I'll leave the review to Richard, but I think you should adopt the same approach
taken by the match.pd split, in that you provide the list of files as an argument
to the genemit instead of the number of files.  And if no list is provided it
outputs to stdout as it does today.

This will make it possible to still debug these utilities easily as is done today.

Cheers,
Tamar

> -----Original Message-----
> From: Robin Dapp <rdapp.gcc@gmail.com>
> Sent: Thursday, October 12, 2023 9:45 PM
> To: gcc-patches <gcc-patches@gcc.gnu.org>
> Cc: rdapp.gcc@gmail.com; jeffreyalaw <jeffreyalaw@gmail.com>; Tamar
> Christina <Tamar.Christina@arm.com>; rjiejie@linux.alibaba.com
> Subject: [PATCH] genemit: Split insn-emit.cc into ten files.
> 
> Hi,
> 
> on riscv insn-emit.cc has grown to over 1.2 mio lines of code and compiling it
> takes considerable time.
> Therefore, this patch adjust genemit to create ten files insn-emit-1.cc to insn-
> emit-10.cc.  In order to do so it first counts the number of available patterns,
> calculates the number of patterns per file and starts a new file whenever that
> number is reached.  Most of the changes are mechanical - genemit would
> output to stdout and I changed it to write to a FILE.
> 
> Similar to match.pd a configure option --with-emitinsn-partitions=num is
> introduced that makes the number of partition configurable.
> 
> This survived some bootstraps on aarch64, x86 and power10 as well as
> regular cross builds on riscv.  I didn't to extensive timing on targets but on my
> machine the compilation of all 10 insn-emit-...cc files for riscv takes about 40
> seconds now while the full file took roughly
> 10 minutes.
> 
> Testsuite is unchanged on all but x86 where, strangely, I saw several illegal
> instructions in the pch tests.  Those were not reproducible in a second manual
> test suite run.  I'm just running another full bootstrap and testsuite cycle with
> the latest trunk.
> 
> Still figured I'd send the current state in order to get some feedback about the
> general approach.
> 
> Regards
>  Robin
> 
> gcc/ChangeLog:
> 
> 	PR bootstrap/84402
> 	PR target/111600
> 
> 	* Makefile.in: Handle split insn-emit.cc.
> 	* configure: Regenerate.
> 	* configure.ac: Add --with-insnemit-partitions.
> 	* genemit.cc (output_peephole2_scratches): Print to file instead
> 	of stdout.
> 	(print_code): Ditto.
> 	(gen_rtx_scratch): Ditto.
> 	(gen_exp): Ditto.
> 	(gen_emit_seq): Ditto.
> 	(emit_c_code): Ditto.
> 	(gen_insn): Ditto.
> 	(gen_expand): Ditto.
> 	(gen_split): Ditto.
> 	(output_add_clobbers): Ditto.
> 	(output_added_clobbers_hard_reg_p): Ditto.
> 	(print_overload_arguments): Ditto.
> 	(print_overload_test): Ditto.
> 	(handle_overloaded_code_for): Ditto.
> 	(handle_overloaded_gen): Ditto.
> 	(print_header): New function.
> 	(handle_arg): New function.
> 	(main): Split output into 10 files.
> 	* gensupport.cc (count_patterns): New function.
> 	* gensupport.h (count_patterns): Define.
> 	* read-md.cc (md_reader::print_md_ptr_loc): Add file argument.
> 	* read-md.h (class md_reader): Change definition.
> ---
>  gcc/Makefile.in   |  37 +++-
>  gcc/configure     |  24 +-
>  gcc/configure.ac  |  13 ++
>  gcc/genemit.cc    | 542 +++++++++++++++++++++++++---------------------
>  gcc/gensupport.cc |  36 +++
>  gcc/gensupport.h  |   1 +
>  gcc/read-md.cc    |   4 +-
>  gcc/read-md.h     |   2 +-
>  8 files changed, 404 insertions(+), 255 deletions(-)
> 
> diff --git a/gcc/Makefile.in b/gcc/Makefile.in index
> 9cc16268abf..1988327f311 100644
> --- a/gcc/Makefile.in
> +++ b/gcc/Makefile.in
> @@ -236,6 +236,12 @@ GIMPLE_MATCH_PD_SEQ_O = $(patsubst %,
> gimple-match-%.o, $(MATCH_SPLITS_SEQ))  GENERIC_MATCH_PD_SEQ_SRC =
> $(patsubst %, generic-match-%.cc, $(MATCH_SPLITS_SEQ))
> GENERIC_MATCH_PD_SEQ_O = $(patsubst %, generic-match-%.o,
> $(MATCH_SPLITS_SEQ))
> 
> +# The number of splits to be made for the insn-emit files.
> +NUM_INSNEMIT_SPLITS = @DEFAULT_INSNEMIT_PARTITIONS@
> INSNEMIT_SPLITS_SEQ
> += $(wordlist 1,$(NUM_INSNEMIT_SPLITS),$(one_to_9999))
> +INSNEMIT_SEQ_SRC = $(patsubst %, insn-emit-%.cc,
> +$(INSNEMIT_SPLITS_SEQ)) INSNEMIT_SEQ_O = $(patsubst %, insn-emit-
> %.o,
> +$(INSNEMIT_SPLITS_SEQ))
> +
>  # These files are to have specific diagnostics suppressed, or are not to  # be
> subject to -Werror:
>  # flex output may yield harmless "no previous prototype" warnings @@ -
> 1354,7 +1360,7 @@ OBJS = \
>  	insn-attrtab.o \
>  	insn-automata.o \
>  	insn-dfatab.o \
> -	insn-emit.o \
> +	$(INSNEMIT_SEQ_O) \
>  	insn-extract.o \
>  	insn-latencytab.o \
>  	insn-modes.o \
> @@ -1852,7 +1858,8 @@ TREECHECKING = @TREECHECKING@
>  FULL_DRIVER_NAME=$(target_noncanonical)-gcc-$(version)$(exeext)
> 
>  MOSTLYCLEANFILES = insn-flags.h insn-config.h insn-codes.h \
> - insn-output.cc insn-recog.cc insn-emit.cc insn-extract.cc insn-peep.cc \
> + insn-output.cc insn-recog.cc $(INSNEMIT_SEQ_SRC) \ insn-extract.cc
> + insn-peep.cc \
>   insn-attr.h insn-attr-common.h insn-attrtab.cc insn-dfatab.cc \
>   insn-latencytab.cc insn-opinit.cc insn-opinit.h insn-preds.cc insn-constants.h
> \
>   tm-preds.h tm-constrs.h checksum-options
> $(GIMPLE_MATCH_PD_SEQ_SRC) \ @@ -2481,11 +2488,11 @@
> $(common_out_object_file): $(common_out_file)  # and compile them.
> 
>  .PRECIOUS: insn-config.h insn-flags.h insn-codes.h insn-constants.h \
> -  insn-emit.cc insn-recog.cc insn-extract.cc insn-output.cc insn-peep.cc \
> -  insn-attr.h insn-attr-common.h insn-attrtab.cc insn-dfatab.cc \
> -  insn-latencytab.cc insn-preds.cc $(GIMPLE_MATCH_PD_SEQ_SRC) \
> -  $(GENERIC_MATCH_PD_SEQ_SRC) gimple-match-auto.h generic-match-
> auto.h \
> -  insn-target-def.h
> +  $(INSNEMIT_SEQ_SRC) insn-recog.cc insn-extract.cc insn-output.cc \
> + insn-peep.cc insn-attr.h insn-attr-common.h insn-attrtab.cc \
> + insn-dfatab.cc insn-latencytab.cc insn-preds.cc \
> +  $(GIMPLE_MATCH_PD_SEQ_SRC) $(GENERIC_MATCH_PD_SEQ_SRC) \
> + gimple-match-auto.h generic-match-auto.h insn-target-def.h
> 
>  # Dependencies for the md file.  The first time through, we just assume  # the
> md file itself and the generated dependency file (in order to get @@ -2508,7
> +2515,7 @@ s-mddeps: $(md_file) $(MD_INCLUDES)
> build/genmddeps$(build_exeext)
>  simple_rtl_generated_h	= insn-attr.h insn-attr-common.h insn-
> codes.h \
>  			  insn-config.h insn-flags.h insn-target-def.h
> 
> -simple_rtl_generated_c	= insn-automata.cc insn-emit.cc \
> +simple_rtl_generated_c	= insn-automata.cc \
>  			  insn-extract.cc insn-output.cc \
>  			  insn-peep.cc insn-recog.cc
> 
> @@ -2537,8 +2544,22 @@ $(simple_generated_c:insn-%.cc=s-%): s-%:
> build/gen%$(build_exeext)
>  	$(SHELL) $(srcdir)/../move-if-change tmp-$*.cc insn-$*.cc
>  	$(STAMP) s-$*
> 
> +# genemit splits its output into different files and doesn't write to #
> +stdout. (but rather to tmp-emit-01.cc..tmp-emit-10.cc)
> +s-tmp-emit: build/genemit$(build_exeext) insn-conditions.md
> +	$(RUN_GEN) build/genemit$(build_exeext) $(md_file) insn-
> conditions.md \
> +	  -Otmp-emit -P${NUM_INSNEMIT_SPLITS}
> +	$(STAMP) s-tmp-emit
> +
> +$(INSNEMIT_SEQ_SRC): insn-emit-%.cc: s-insn-emit-%; @true
> +$(INSNEMIT_SEQ_SRC:insn-emit-%.cc=s-insn-emit-%): s-insn-emit-%: s-
> tmp-emit
> +	$(SHELL) $(srcdir)/../move-if-change tmp-emit-$*.cc insn-emit-$*.cc
> +	$(STAMP) s-insn-emit-$*
> +
>  # gencheck doesn't read the machine description, and the file produced  #
> doesn't use the insn-* convention.
> +
> +# --> s-check has prerequisite tree-check.h (though nothing to do)
>  tree-check.h: s-check ; @true
>  s-check : build/gencheck$(build_exeext)
>  	$(RUN_GEN) build/gencheck$(build_exeext) > tmp-check.h diff --git
> a/gcc/configure b/gcc/configure index c43bde8174b..00672b18f72 100755
> --- a/gcc/configure
> +++ b/gcc/configure
> @@ -842,6 +842,7 @@ enable_gcov
>  enable_shared
>  enable_fixed_point
>  enable_decimal_float
> +DEFAULT_INSNEMIT_PARTITIONS
>  DEFAULT_MATCHPD_PARTITIONS
>  with_float
>  with_cpu
> @@ -971,6 +972,7 @@ enable_multilib
>  enable_multiarch
>  with_stack_clash_protection_guard_size
>  with_matchpd_partitions
> +with_insnemit_partitions
>  enable___cxa_atexit
>  enable_decimal_float
>  enable_fixed_point
> @@ -1839,6 +1841,9 @@ Optional Packages:
>    --with-matchpd-partitions=num
>                            Set the number of partitions to make for gimple and
>                            generic when splitting match.pd. [default=10]
> +  --with-insnemit-partitions=num
> +                          Set the number of partitions of insn-emit.cc for
> +                          genemit to create. [default=10]
>    --with-dwarf2           force the default debug format to be DWARF 2 (or
>                            later)
>    --with-specs=SPECS      add SPECS to driver command-line processing
> @@ -7938,6 +7943,21 @@ fi
> 
> 
> 
> +# Specify the number of splits of insn-emit.cc to generate.
> +
> +# Check whether --with-insnemit-partitions was given.
> +if test "${with_insnemit_partitions+set}" = set; then :
> +  withval=$with_insnemit_partitions;
> DEFAULT_INSNEMIT_PARTITIONS="$with_insnemit_partitions"
> +else
> +  DEFAULT_INSNEMIT_PARTITIONS=10
> +fi
> +
> +if (test $DEFAULT_INSNEMIT_PARTITIONS -lt 1); then
> +  as_fn_error $? "Invalid value $DEFAULT_INSNEMIT_PARTITIONS for
> +--with-insnemit-partitions. Cannot be negative." "$LINENO" 5 fi
> +
> +
> +
>  # Enable __cxa_atexit for C++.
>  # Check whether --enable-__cxa_atexit was given.
>  if test "${enable___cxa_atexit+set}" = set; then :
> @@ -19923,7 +19943,7 @@ else
>    lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
>    lt_status=$lt_dlunknown
>    cat > conftest.$ac_ext <<_LT_EOF
> -#line 19926 "configure"
> +#line 19946 "configure"
>  #include "confdefs.h"
> 
>  #if HAVE_DLFCN_H
> @@ -20029,7 +20049,7 @@ else
>    lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
>    lt_status=$lt_dlunknown
>    cat > conftest.$ac_ext <<_LT_EOF
> -#line 20032 "configure"
> +#line 20052 "configure"
>  #include "confdefs.h"
> 
>  #if HAVE_DLFCN_H
> diff --git a/gcc/configure.ac b/gcc/configure.ac index
> fb8e32f8ee5..bc0ef734e6c 100644
> --- a/gcc/configure.ac
> +++ b/gcc/configure.ac
> @@ -956,6 +956,19 @@ fi
> 
>  AC_SUBST(DEFAULT_MATCHPD_PARTITIONS)
> 
> +# Specify the number of splits of insn-emit.cc to generate.
> +AC_ARG_WITH(insnemit-partitions,
> +[AS_HELP_STRING([--with-insnemit-partitions=num],
> +[Set the number of partitions of insn-emit.cc for genemit to create.
> +[default=10]])],
> +[DEFAULT_INSNEMIT_PARTITIONS="$with_insnemit_partitions"],
> +[DEFAULT_INSNEMIT_PARTITIONS=10]) if (test
> $DEFAULT_INSNEMIT_PARTITIONS
> +-lt 1); then
> +  AC_MSG_ERROR(m4_normalize([
> +		Invalid value $DEFAULT_INSNEMIT_PARTITIONS for --with-
> insnemit-partitions. \
> +		Cannot be negative.]))
> +fi
> +
> +AC_SUBST(DEFAULT_INSNEMIT_PARTITIONS)
> +
>  # Enable __cxa_atexit for C++.
>  AC_ARG_ENABLE(__cxa_atexit,
>  [AS_HELP_STRING([--enable-__cxa_atexit], [enable __cxa_atexit for C++])],
> diff --git a/gcc/genemit.cc b/gcc/genemit.cc index 1ce0564076d..c9f6a73fff9
> 100644
> --- a/gcc/genemit.cc
> +++ b/gcc/genemit.cc
> @@ -49,29 +49,29 @@ struct clobber_ent
>    struct clobber_ent *next;
>  };
> 
> -static void output_peephole2_scratches	(rtx);
> +static void output_peephole2_scratches	(rtx, FILE*);
> 
>  /* True for <X>_optab if that optab isn't allowed to fail.  */  static bool
> nofail_optabs[NUM_OPTABS];
> 
> 
> 
>  static void
> -print_code (RTX_CODE code)
> +print_code (RTX_CODE code, FILE *file)
>  {
>    const char *p1;
>    for (p1 = GET_RTX_NAME (code); *p1; p1++)
> -    putchar (TOUPPER (*p1));
> +    fprintf (file, "%c", TOUPPER (*p1));
>  }
> 
>  static void
> -gen_rtx_scratch (rtx x, enum rtx_code subroutine_type)
> +gen_rtx_scratch (rtx x, enum rtx_code subroutine_type, FILE *file)
>  {
>    if (subroutine_type == DEFINE_PEEPHOLE2)
>      {
> -      printf ("operand%d", XINT (x, 0));
> +      fprintf (file, "operand%d", XINT (x, 0));
>      }
>    else
>      {
> -      printf ("gen_rtx_SCRATCH (%smode)", GET_MODE_NAME (GET_MODE
> (x)));
> +      fprintf (file, "gen_rtx_SCRATCH (%smode)", GET_MODE_NAME
> + (GET_MODE (x)));
>      }
>  }
> 
> @@ -79,7 +79,8 @@ gen_rtx_scratch (rtx x, enum rtx_code subroutine_type)
>     substituting any operand references appearing within.  */
> 
>  static void
> -gen_exp (rtx x, enum rtx_code subroutine_type, char *used, md_rtx_info
> *info)
> +gen_exp (rtx x, enum rtx_code subroutine_type, char *used, md_rtx_info
> *info,
> +	 FILE *file)
>  {
>    RTX_CODE code;
>    int i;
> @@ -89,7 +90,7 @@ gen_exp (rtx x, enum rtx_code subroutine_type, char
> *used, md_rtx_info *info)
> 
>    if (x == 0)
>      {
> -      printf ("NULL_RTX");
> +      fprintf (file, "NULL_RTX");
>        return;
>      }
> 
> @@ -103,67 +104,67 @@ gen_exp (rtx x, enum rtx_code subroutine_type,
> char *used, md_rtx_info *info)
>  	{
>  	  if (used[XINT (x, 0)])
>  	    {
> -	      printf ("copy_rtx (operand%d)", XINT (x, 0));
> +	      fprintf (file, "copy_rtx (operand%d)", XINT (x, 0));
>  	      return;
>  	    }
>  	  used[XINT (x, 0)] = 1;
>  	}
> -      printf ("operand%d", XINT (x, 0));
> +      fprintf (file, "operand%d", XINT (x, 0));
>        return;
> 
>      case MATCH_OP_DUP:
> -      printf ("gen_rtx_fmt_");
> +      fprintf (file, "gen_rtx_fmt_");
>        for (i = 0; i < XVECLEN (x, 1); i++)
> -	printf ("e");
> -      printf (" (GET_CODE (operand%d), ", XINT (x, 0));
> +	fprintf (file, "e");
> +      fprintf (file, " (GET_CODE (operand%d), ", XINT (x, 0));
>        if (GET_MODE (x) == VOIDmode)
> -	printf ("GET_MODE (operand%d)", XINT (x, 0));
> +	fprintf (file, "GET_MODE (operand%d)", XINT (x, 0));
>        else
> -	printf ("%smode", GET_MODE_NAME (GET_MODE (x)));
> +	fprintf (file, "%smode", GET_MODE_NAME (GET_MODE (x)));
>        for (i = 0; i < XVECLEN (x, 1); i++)
>  	{
> -	  printf (",\n\t\t");
> -	  gen_exp (XVECEXP (x, 1, i), subroutine_type, used, info);
> +	  fprintf (file, ",\n\t\t");
> +	  gen_exp (XVECEXP (x, 1, i), subroutine_type, used, info, file);
>  	}
> -      printf (")");
> +      fprintf (file, ")");
>        return;
> 
>      case MATCH_OPERATOR:
> -      printf ("gen_rtx_fmt_");
> +      fprintf (file, "gen_rtx_fmt_");
>        for (i = 0; i < XVECLEN (x, 2); i++)
> -	printf ("e");
> -      printf (" (GET_CODE (operand%d)", XINT (x, 0));
> -      printf (", %smode", GET_MODE_NAME (GET_MODE (x)));
> +	fprintf (file, "e");
> +      fprintf (file, " (GET_CODE (operand%d)", XINT (x, 0));
> +      fprintf (file, ", %smode", GET_MODE_NAME (GET_MODE (x)));
>        for (i = 0; i < XVECLEN (x, 2); i++)
>  	{
> -	  printf (",\n\t\t");
> -	  gen_exp (XVECEXP (x, 2, i), subroutine_type, used, info);
> +	  fprintf (file, ",\n\t\t");
> +	  gen_exp (XVECEXP (x, 2, i), subroutine_type, used, info, file);
>  	}
> -      printf (")");
> +      fprintf (file, ")");
>        return;
> 
>      case MATCH_PARALLEL:
>      case MATCH_PAR_DUP:
> -      printf ("operand%d", XINT (x, 0));
> +      fprintf (file, "operand%d", XINT (x, 0));
>        return;
> 
>      case MATCH_SCRATCH:
> -      gen_rtx_scratch (x, subroutine_type);
> +      gen_rtx_scratch (x, subroutine_type, file);
>        return;
> 
>      case PC:
> -      printf ("pc_rtx");
> +      fprintf (file, "pc_rtx");
>        return;
>      case RETURN:
> -      printf ("ret_rtx");
> +      fprintf (file, "ret_rtx");
>        return;
>      case SIMPLE_RETURN:
> -      printf ("simple_return_rtx");
> +      fprintf (file, "simple_return_rtx");
>        return;
>      case CLOBBER:
>        if (REG_P (XEXP (x, 0)))
>  	{
> -	  printf ("gen_hard_reg_clobber (%smode, %i)",
> +	  fprintf (file, "gen_hard_reg_clobber (%smode, %i)",
>  		  GET_MODE_NAME (GET_MODE (XEXP (x, 0))),
>  		  REGNO (XEXP (x, 0)));
>  	  return;
> @@ -172,22 +173,22 @@ gen_exp (rtx x, enum rtx_code subroutine_type,
> char *used, md_rtx_info *info)
> 
>      case CONST_INT:
>        if (INTVAL (x) == 0)
> -	printf ("const0_rtx");
> +	fprintf (file, "const0_rtx");
>        else if (INTVAL (x) == 1)
> -	printf ("const1_rtx");
> +	fprintf (file, "const1_rtx");
>        else if (INTVAL (x) == -1)
> -	printf ("constm1_rtx");
> +	fprintf (file, "constm1_rtx");
>        else if (-MAX_SAVED_CONST_INT <= INTVAL (x)
>  	       && INTVAL (x) <= MAX_SAVED_CONST_INT)
> -	printf ("const_int_rtx[MAX_SAVED_CONST_INT + (%d)]",
> +	fprintf (file, "const_int_rtx[MAX_SAVED_CONST_INT + (%d)]",
>  		(int) INTVAL (x));
>        else if (INTVAL (x) == STORE_FLAG_VALUE)
> -	printf ("const_true_rtx");
> +	fprintf (file, "const_true_rtx");
>        else
>  	{
> -	  printf ("GEN_INT (");
> -	  printf (HOST_WIDE_INT_PRINT_DEC_C, INTVAL (x));
> -	  printf (")");
> +	  fprintf (file, "GEN_INT (");
> +	  fprintf (file, HOST_WIDE_INT_PRINT_DEC_C, INTVAL (x));
> +	  fprintf (file, ")");
>  	}
>        return;
> 
> @@ -195,7 +196,7 @@ gen_exp (rtx x, enum rtx_code subroutine_type, char
> *used, md_rtx_info *info)
>        /* Handle `const_double_zero' rtx.  */
>        if (CONST_DOUBLE_REAL_VALUE (x)->cl == rvc_zero)
>  	{
> -	  printf ("CONST_DOUBLE_ATOF (\"0\", %smode)",
> +	  fprintf (file, "CONST_DOUBLE_ATOF (\"0\", %smode)",
>  		  GET_MODE_NAME (GET_MODE (x)));
>  	  return;
>  	}
> @@ -210,12 +211,12 @@ gen_exp (rtx x, enum rtx_code subroutine_type,
> char *used, md_rtx_info *info)
>        break;
>      }
> 
> -  printf ("gen_rtx_");
> -  print_code (code);
> -  printf (" (");
> +  fprintf (file, "gen_rtx_");
> +  print_code (code, file);
> +  fprintf (file, " (");
>    if (!always_void_p (code))
>      {
> -      printf ("%smode", GET_MODE_NAME (GET_MODE (x)));
> +      fprintf (file, "%smode", GET_MODE_NAME (GET_MODE (x)));
>        sep = ",\n\t";
>      }
> 
> @@ -225,41 +226,41 @@ gen_exp (rtx x, enum rtx_code subroutine_type,
> char *used, md_rtx_info *info)
>      {
>        if (fmt[i] == '0')
>  	break;
> -      fputs (sep, stdout);
> +      fputs (sep, file);
>        switch (fmt[i])
>  	{
>  	case 'e': case 'u':
> -	  gen_exp (XEXP (x, i), subroutine_type, used, info);
> +	  gen_exp (XEXP (x, i), subroutine_type, used, info, file);
>  	  break;
> 
>  	case 'i':
> -	  printf ("%u", XINT (x, i));
> +	  fprintf (file, "%u", XINT (x, i));
>  	  break;
> 
>  	case 'r':
> -	  printf ("%u", REGNO (x));
> +	  fprintf (file, "%u", REGNO (x));
>  	  break;
> 
>  	case 'p':
>  	  /* We don't have a way of parsing polynomial offsets yet,
>  	     and hopefully never will.  */
> -	  printf ("%d", SUBREG_BYTE (x).to_constant ());
> +	  fprintf (file, "%d", SUBREG_BYTE (x).to_constant ());
>  	  break;
> 
>  	case 's':
> -	  printf ("\"%s\"", XSTR (x, i));
> +	  fprintf (file, "\"%s\"", XSTR (x, i));
>  	  break;
> 
>  	case 'E':
>  	  {
>  	    int j;
> -	    printf ("gen_rtvec (%d", XVECLEN (x, i));
> +	    fprintf (file, "gen_rtvec (%d", XVECLEN (x, i));
>  	    for (j = 0; j < XVECLEN (x, i); j++)
>  	      {
> -		printf (",\n\t\t");
> -		gen_exp (XVECEXP (x, i, j), subroutine_type, used, info);
> +		fprintf (file, ",\n\t\t");
> +		gen_exp (XVECEXP (x, i, j), subroutine_type, used, info, file);
>  	      }
> -	    printf (")");
> +	    fprintf (file, ")");
>  	    break;
>  	  }
> 
> @@ -268,14 +269,14 @@ gen_exp (rtx x, enum rtx_code subroutine_type,
> char *used, md_rtx_info *info)
>  	}
>        sep = ",\n\t";
>      }
> -  printf (")");
> +  fprintf (file, ")");
>  }
> 
>  /* Output code to emit the instruction patterns in VEC, with each element
>     becoming a separate instruction.  USED is as for gen_exp.  */
> 
>  static void
> -gen_emit_seq (rtvec vec, char *used, md_rtx_info *info)
> +gen_emit_seq (rtvec vec, char *used, md_rtx_info *info, FILE *file)
>  {
>    for (int i = 0, len = GET_NUM_ELEM (vec); i < len; ++i)
>      {
> @@ -283,17 +284,17 @@ gen_emit_seq (rtvec vec, char *used, md_rtx_info
> *info)
>        rtx next = RTVEC_ELT (vec, i);
>        if (const char *name = get_emit_function (next))
>  	{
> -	  printf ("  %s (", name);
> -	  gen_exp (next, DEFINE_EXPAND, used, info);
> -	  printf (");\n");
> +	  fprintf (file, "  %s (", name);
> +	  gen_exp (next, DEFINE_EXPAND, used, info, file);
> +	  fprintf (file, ");\n");
>  	  if (!last_p && needs_barrier_p (next))
> -	    printf ("  emit_barrier ();");
> +	    fprintf (file, "  emit_barrier ();");
>  	}
>        else
>  	{
> -	  printf ("  emit (");
> -	  gen_exp (next, DEFINE_EXPAND, used, info);
> -	  printf (", %s);\n", last_p ? "false" : "true");
> +	  fprintf (file, "  emit (");
> +	  gen_exp (next, DEFINE_EXPAND, used, info, file);
> +	  fprintf (file, ", %s);\n", last_p ? "false" : "true");
>  	}
>      }
>  }
> @@ -303,27 +304,27 @@ gen_emit_seq (rtvec vec, char *used, md_rtx_info
> *info)
>     for use in error messages.  */
> 
>  static void
> -emit_c_code (const char *code, bool can_fail_p, const char *name)
> +emit_c_code (const char *code, bool can_fail_p, const char *name, FILE
> +*file)
>  {
>    if (can_fail_p)
> -    printf ("#define FAIL return (end_sequence (), _val)\n");
> +    fprintf (file, "#define FAIL return (end_sequence (), _val)\n");
>    else
> -    printf ("#define FAIL _Pragma (\"GCC error \\\"%s cannot FAIL\\\"\")"
> +    fprintf (file, "#define FAIL _Pragma (\"GCC error \\\"%s cannot FAIL\\\"\")"
>  	    " (void)0\n", name);
> -  printf ("#define DONE return (_val = get_insns (), "
> +  fprintf (file, "#define DONE return (_val = get_insns (), "
>  	  "end_sequence (), _val)\n");
> 
> -  rtx_reader_ptr->print_md_ptr_loc (code);
> -  printf ("%s\n", code);
> +  rtx_reader_ptr->print_md_ptr_loc (code, file);  fprintf (file,
> + "%s\n", code);
> 
> -  printf ("#undef DONE\n");
> -  printf ("#undef FAIL\n");
> +  fprintf (file, "#undef DONE\n");
> +  fprintf (file, "#undef FAIL\n");
>  }
> 
> 
> 
>  /* Generate the `gen_...' function for a DEFINE_INSN.  */
> 
>  static void
> -gen_insn (md_rtx_info *info)
> +gen_insn (md_rtx_info *info, FILE *file)
>  {
>    struct pattern_stats stats;
>    int i;
> @@ -409,7 +410,7 @@ gen_insn (md_rtx_info *info)
>    if (XSTR (insn, 0)[0] == 0 || XSTR (insn, 0)[0] == '*')
>      return;
> 
> -  printf ("/* %s:%d */\n", info->loc.filename, info->loc.lineno);
> +  fprintf (file, "/* %s:%d */\n", info->loc.filename,
> + info->loc.lineno);
> 
>    /* Find out how many operands this function has.  */
>    get_pattern_stats (&stats, XVEC (insn, 1)); @@ -417,17 +418,17 @@
> gen_insn (md_rtx_info *info)
>      fatal_at (info->loc, "match_dup operand number has no match_operand");
> 
>    /* Output the function name and argument declarations.  */
> -  printf ("rtx\ngen_%s (", XSTR (insn, 0));
> +  fprintf (file, "rtx\ngen_%s (", XSTR (insn, 0));
>    if (stats.num_generator_args)
>      for (i = 0; i < stats.num_generator_args; i++)
>        if (i)
> -	printf (",\n\trtx operand%d ATTRIBUTE_UNUSED", i);
> +	fprintf (file, ",\n\trtx operand%d ATTRIBUTE_UNUSED", i);
>        else
> -	printf ("rtx operand%d ATTRIBUTE_UNUSED", i);
> +	fprintf (file, "rtx operand%d ATTRIBUTE_UNUSED", i);
>    else
> -    printf ("void");
> -  printf (")\n");
> -  printf ("{\n");
> +    fprintf (file, "void");
> +  fprintf (file, ")\n");
> +  fprintf (file, "{\n");
> 
>    /* Output code to construct and return the rtl for the instruction body.  */
> 
> @@ -436,16 +437,16 @@ gen_insn (md_rtx_info *info)
>    char *used = (XVECLEN (insn, 1) == 1
>  		? NULL
>  		: XCNEWVEC (char, stats.num_generator_args));
> -  printf ("  return ");
> -  gen_exp (pattern, DEFINE_INSN, used, info);
> -  printf (";\n}\n\n");
> +  fprintf (file, "  return ");
> +  gen_exp (pattern, DEFINE_INSN, used, info, file);  fprintf (file,
> + ";\n}\n\n");
>    XDELETEVEC (used);
>  }
> 
> 
> 
>  /* Generate the `gen_...' function for a DEFINE_EXPAND.  */
> 
>  static void
> -gen_expand (md_rtx_info *info)
> +gen_expand (md_rtx_info *info, FILE *file)
>  {
>    struct pattern_stats stats;
>    int i;
> @@ -466,17 +467,17 @@ gen_expand (md_rtx_info *info)
>  			 "numbers above all other operands", XSTR (expand,
> 0));
> 
>    /* Output the function name and argument declarations.  */
> -  printf ("rtx\ngen_%s (", XSTR (expand, 0));
> +  fprintf (file, "rtx\ngen_%s (", XSTR (expand, 0));
>    if (stats.num_generator_args)
>      for (i = 0; i < stats.num_generator_args; i++)
>        if (i)
> -	printf (",\n\trtx operand%d", i);
> +	fprintf (file, ",\n\trtx operand%d", i);
>        else
> -	printf ("rtx operand%d", i);
> +	fprintf (file, "rtx operand%d", i);
>    else
> -    printf ("void");
> -  printf (")\n");
> -  printf ("{\n");
> +    fprintf (file, "void");
> +  fprintf (file, ")\n");
> +  fprintf (file, "{\n");
> 
>    /* If we don't have any C code to write, only one insn is being written,
>       and no MATCH_DUPs are present, we can just return the desired insn @@ -
> 485,18 +486,18 @@ gen_expand (md_rtx_info *info)
>        && stats.max_opno >= stats.max_dup_opno
>        && XVECLEN (expand, 1) == 1)
>      {
> -      printf ("  return ");
> -      gen_exp (XVECEXP (expand, 1, 0), DEFINE_EXPAND, NULL, info);
> -      printf (";\n}\n\n");
> +      fprintf (file, "  return ");
> +      gen_exp (XVECEXP (expand, 1, 0), DEFINE_EXPAND, NULL, info, file);
> +      fprintf (file, ";\n}\n\n");
>        return;
>      }
> 
>    /* For each operand referred to only with MATCH_DUPs,
>       make a local variable.  */
>    for (i = stats.num_generator_args; i <= stats.max_dup_opno; i++)
> -    printf ("  rtx operand%d;\n", i);
> -  printf ("  rtx_insn *_val = 0;\n");
> -  printf ("  start_sequence ();\n");
> +    fprintf (file, "  rtx operand%d;\n", i);  fprintf (file, "
> + rtx_insn *_val = 0;\n");  fprintf (file, "  start_sequence ();\n");
> 
>    /* The fourth operand of DEFINE_EXPAND is some code to be executed
>       before the actual construction.
> @@ -506,13 +507,13 @@ gen_expand (md_rtx_info *info)
>       So copy the operand values there before executing it.  */
>    if (XSTR (expand, 3) && *XSTR (expand, 3))
>      {
> -      printf ("  {\n");
> +      fprintf (file, "  {\n");
>        if (stats.num_operand_vars > 0)
> -	printf ("    rtx operands[%d];\n", stats.num_operand_vars);
> +	fprintf (file, "    rtx operands[%d];\n", stats.num_operand_vars);
> 
>        /* Output code to copy the arguments into `operands'.  */
>        for (i = 0; i < stats.num_generator_args; i++)
> -	printf ("    operands[%d] = operand%d;\n", i, i);
> +	fprintf (file, "    operands[%d] = operand%d;\n", i, i);
> 
>        /* Output the special code to be executed before the sequence
>  	 is generated.  */
> @@ -524,7 +525,7 @@ gen_expand (md_rtx_info *info)
>  	  if (nofail_optabs[p.op])
>  	    can_fail_p = false;
>  	}
> -      emit_c_code (XSTR (expand, 3), can_fail_p, XSTR (expand, 0));
> +      emit_c_code (XSTR (expand, 3), can_fail_p, XSTR (expand, 0),
> + file);
> 
>        /* Output code to copy the arguments back out of `operands'
>  	 (unless we aren't going to use them at all).  */ @@ -532,29 +533,29
> @@ gen_expand (md_rtx_info *info)
>  	{
>  	  for (i = 0; i <= MAX (stats.max_opno, stats.max_dup_opno); i++)
>  	    {
> -	      printf ("    operand%d = operands[%d];\n", i, i);
> -	      printf ("    (void) operand%d;\n", i);
> +	      fprintf (file, "    operand%d = operands[%d];\n", i, i);
> +	      fprintf (file, "    (void) operand%d;\n", i);
>  	    }
>  	}
> -      printf ("  }\n");
> +      fprintf (file, "  }\n");
>      }
> 
>    used = XCNEWVEC (char, stats.num_operand_vars);
> -  gen_emit_seq (XVEC (expand, 1), used, info);
> +  gen_emit_seq (XVEC (expand, 1), used, info, file);
>    XDELETEVEC (used);
> 
>    /* Call `get_insns' to extract the list of all the
>       insns emitted within this gen_... function.  */
> 
> -  printf ("  _val = get_insns ();\n");
> -  printf ("  end_sequence ();\n");
> -  printf ("  return _val;\n}\n\n");
> +  fprintf (file, "  _val = get_insns ();\n");  fprintf (file, "
> + end_sequence ();\n");  fprintf (file, "  return _val;\n}\n\n");
>  }
> 
>  /* Like gen_expand, but generates insns resulting from splitting SPLIT.  */
> 
>  static void
> -gen_split (md_rtx_info *info)
> +gen_split (md_rtx_info *info, FILE *file)
>  {
>    struct pattern_stats stats;
>    int i;
> @@ -580,62 +581,62 @@ gen_split (md_rtx_info *info)
>    /* Output the prototype, function name and argument declarations.  */
>    if (GET_CODE (split) == DEFINE_PEEPHOLE2)
>      {
> -      printf ("extern rtx_insn *gen_%s_%d (rtx_insn *, rtx *);\n",
> +      fprintf (file, "extern rtx_insn *gen_%s_%d (rtx_insn *, rtx
> + *);\n",
>  	      name, info->index);
> -      printf ("rtx_insn *\ngen_%s_%d (rtx_insn *curr_insn
> ATTRIBUTE_UNUSED,"
> +      fprintf (file, "rtx_insn *\ngen_%s_%d (rtx_insn *curr_insn
> ATTRIBUTE_UNUSED,"
>  	      " rtx *operands%s)\n",
>  	      name, info->index, unused);
>      }
>    else
>      {
> -      printf ("extern rtx_insn *gen_split_%d (rtx_insn *, rtx *);\n",
> +      fprintf (file, "extern rtx_insn *gen_split_%d (rtx_insn *, rtx
> + *);\n",
>  	      info->index);
> -      printf ("rtx_insn *\ngen_split_%d "
> +      fprintf (file, "rtx_insn *\ngen_split_%d "
>  	      "(rtx_insn *curr_insn ATTRIBUTE_UNUSED, rtx *operands%s)\n",
>  	      info->index, unused);
>      }
> -  printf ("{\n");
> +  fprintf (file, "{\n");
> 
>    /* Declare all local variables.  */
>    for (i = 0; i < stats.num_operand_vars; i++)
> -    printf ("  rtx operand%d;\n", i);
> -  printf ("  rtx_insn *_val = NULL;\n");
> +    fprintf (file, "  rtx operand%d;\n", i);  fprintf (file, "
> + rtx_insn *_val = NULL;\n");
> 
>    if (GET_CODE (split) == DEFINE_PEEPHOLE2)
> -    output_peephole2_scratches (split);
> +    output_peephole2_scratches (split, file);
> 
>    const char *fn = info->loc.filename;
>    for (const char *p = fn; *p; p++)
>      if (*p == '/')
>        fn = p + 1;
> 
> -  printf ("  if (dump_file)\n");
> -  printf ("    fprintf (dump_file, \"Splitting with gen_%s_%d (%s:%d)\\n\");\n",
> +  fprintf (file, "  if (dump_file)\n");
> +  fprintf (file, "    fprintf (dump_file, \"Splitting with gen_%s_%d
> (%s:%d)\\n\");\n",
>  	  name, info->index, fn, info->loc.lineno);
> 
> -  printf ("  start_sequence ();\n");
> +  fprintf (file, "  start_sequence ();\n");
> 
>    /* The fourth operand of DEFINE_SPLIT is some code to be executed
>       before the actual construction.  */
> 
>    if (XSTR (split, 3))
> -    emit_c_code (XSTR (split, 3), true, name);
> +    emit_c_code (XSTR (split, 3), true, name, file);
> 
>    /* Output code to copy the arguments back out of `operands'  */
>    for (i = 0; i < stats.num_operand_vars; i++)
>      {
> -      printf ("  operand%d = operands[%d];\n", i, i);
> -      printf ("  (void) operand%d;\n", i);
> +      fprintf (file, "  operand%d = operands[%d];\n", i, i);
> +      fprintf (file, "  (void) operand%d;\n", i);
>      }
> 
> -  gen_emit_seq (XVEC (split, 2), used, info);
> +  gen_emit_seq (XVEC (split, 2), used, info, file);
> 
>    /* Call `get_insns' to make a list of all the
>       insns emitted within this gen_... function.  */
> 
> -  printf ("  _val = get_insns ();\n");
> -  printf ("  end_sequence ();\n");
> -  printf ("  return _val;\n}\n\n");
> +  fprintf (file, "  _val = get_insns ();\n");  fprintf (file, "
> + end_sequence ();\n");  fprintf (file, "  return _val;\n}\n\n");
> 
>    free (used);
>  }
> @@ -645,37 +646,37 @@ gen_split (md_rtx_info *info)
>     the end of the vector.  */
> 
>  static void
> -output_add_clobbers (md_rtx_info *info)
> +output_add_clobbers (md_rtx_info *info, FILE *file)
>  {
>    struct clobber_pat *clobber;
>    struct clobber_ent *ent;
>    int i;
> 
> -  printf ("\n\nvoid\nadd_clobbers (rtx pattern ATTRIBUTE_UNUSED, int
> insn_code_number)\n");
> -  printf ("{\n");
> -  printf ("  switch (insn_code_number)\n");
> -  printf ("    {\n");
> +  fprintf (file, "\n\nvoid\nadd_clobbers (rtx pattern ATTRIBUTE_UNUSED,
> + int insn_code_number)\n");  fprintf (file, "{\n");  fprintf (file, "
> + switch (insn_code_number)\n");
> +  fprintf (file, "    {\n");
> 
>    for (clobber = clobber_list; clobber; clobber = clobber->next)
>      {
>        for (ent = clobber->insns; ent; ent = ent->next)
> -	printf ("    case %d:\n", ent->code_number);
> +	fprintf (file, "    case %d:\n", ent->code_number);
> 
>        for (i = clobber->first_clobber; i < XVECLEN (clobber->pattern, 1); i++)
>  	{
> -	  printf ("      XVECEXP (pattern, 0, %d) = ", i);
> +	  fprintf (file, "      XVECEXP (pattern, 0, %d) = ", i);
>  	  gen_exp (XVECEXP (clobber->pattern, 1, i),
> -		   GET_CODE (clobber->pattern), NULL, info);
> -	  printf (";\n");
> +		   GET_CODE (clobber->pattern), NULL, info, file);
> +	  fprintf (file, ";\n");
>  	}
> 
> -      printf ("      break;\n\n");
> +      fprintf (file, "      break;\n\n");
>      }
> 
> -  printf ("    default:\n");
> -  printf ("      gcc_unreachable ();\n");
> -  printf ("    }\n");
> -  printf ("}\n");
> +  fprintf (file, "    default:\n");
> +  fprintf (file, "      gcc_unreachable ();\n");
> +  fprintf (file, "    }\n");
> +  fprintf (file, "}\n");
>  }
> 
> 
> 
>  /* Write a function, `added_clobbers_hard_reg_p' that is given an insn_code
> @@ -684,17 +685,17 @@ output_add_clobbers (md_rtx_info *info)
>     SCRATCH.  */
> 
>  static void
> -output_added_clobbers_hard_reg_p (void)
> +output_added_clobbers_hard_reg_p (FILE *file)
>  {
>    struct clobber_pat *clobber;
>    struct clobber_ent *ent;
>    int clobber_p;
>    bool used;
> 
> -  printf ("\n\nbool\nadded_clobbers_hard_reg_p (int
> insn_code_number)\n");
> -  printf ("{\n");
> -  printf ("  switch (insn_code_number)\n");
> -  printf ("    {\n");
> +  fprintf (file, "\n\nbool\nadded_clobbers_hard_reg_p (int
> + insn_code_number)\n");  fprintf (file, "{\n");  fprintf (file, "
> + switch (insn_code_number)\n");
> +  fprintf (file, "    {\n");
> 
>    for (clobber_p = 0; clobber_p <= 1; clobber_p++)
>      {
> @@ -703,25 +704,25 @@ output_added_clobbers_hard_reg_p (void)
>  	if (clobber->has_hard_reg == clobber_p)
>  	  for (ent = clobber->insns; ent; ent = ent->next)
>  	    {
> -	      printf ("    case %d:\n", ent->code_number);
> +	      fprintf (file, "    case %d:\n", ent->code_number);
>  	      used = true;
>  	    }
> 
>        if (used)
> -	printf ("      return %s;\n\n", clobber_p ? "true" : "false");
> +	fprintf (file, "      return %s;\n\n", clobber_p ? "true" : "false");
>      }
> 
> -  printf ("    default:\n");
> -  printf ("      gcc_unreachable ();\n");
> -  printf ("    }\n");
> -  printf ("}\n");
> +  fprintf (file, "    default:\n");
> +  fprintf (file, "      gcc_unreachable ();\n");
> +  fprintf (file, "    }\n");
> +  fprintf (file, "}\n");
>  }
> 
> 
> 
>  /* Generate code to invoke find_free_register () as needed for the
>     scratch registers used by the peephole2 pattern in SPLIT.  */
> 
>  static void
> -output_peephole2_scratches (rtx split)
> +output_peephole2_scratches (rtx split, FILE *file)
>  {
>    int i;
>    int insn_nr = 0;
> @@ -746,12 +747,12 @@ output_peephole2_scratches (rtx split)
> 
>  	  if (first)
>  	    {
> -	      printf ("  HARD_REG_SET _regs_allocated;\n");
> -	      printf ("  CLEAR_HARD_REG_SET (_regs_allocated);\n");
> +	      fprintf (file, "  HARD_REG_SET _regs_allocated;\n");
> +	      fprintf (file, "  CLEAR_HARD_REG_SET (_regs_allocated);\n");
>  	      first = false;
>  	    }
> 
> -	  printf ("  if ((operands[%d] = peep2_find_free_register (%d, %d,
> \"%s\", %smode, &_regs_allocated)) == NULL_RTX)\n\
> +	  fprintf (file, "  if ((operands[%d] = peep2_find_free_register (%d,
> +%d, \"%s\", %smode, &_regs_allocated)) == NULL_RTX)\n\
>      return NULL;\n",
>  		  XINT (elt, 0),
>  		  insn_nr, last_insn_nr,
> @@ -767,50 +768,50 @@ output_peephole2_scratches (rtx split)
>  /* Print "arg<N>" parameter declarations for each argument N of ONAME.  */
> 
>  static void
> -print_overload_arguments (overloaded_name *oname)
> +print_overload_arguments (overloaded_name *oname, FILE *file)
>  {
>    for (unsigned int i = 0; i < oname->arg_types.length (); ++i)
> -    printf ("%s%s arg%d", i == 0 ? "" : ", ", oname->arg_types[i], i);
> +    fprintf (file, "%s%s arg%d", i == 0 ? "" : ", ",
> + oname->arg_types[i], i);
>  }
> 
>  /* Print code to test whether INSTANCE should be chosen, given that
>     argument N of the overload is available as "arg<N>".  */
> 
>  static void
> -print_overload_test (overloaded_instance *instance)
> +print_overload_test (overloaded_instance *instance, FILE *file)
>  {
>    for (unsigned int i = 0; i < instance->arg_values.length (); ++i)
> -    printf ("%sarg%d == %s", i == 0 ? "  if (" : "\n      && ",
> +    fprintf (file, "%sarg%d == %s", i == 0 ? "  if (" : "\n      && ",
>  	    i, instance->arg_values[i]);
> -  printf (")\n");
> +  fprintf (file, ")\n");
>  }
> 
>  /* Emit a maybe_code_for_* function for ONAME.  */
> 
>  static void
> -handle_overloaded_code_for (overloaded_name *oname)
> +handle_overloaded_code_for (overloaded_name *oname, FILE *file)
>  {
>    /* Print the function prototype.  */
> -  printf ("\ninsn_code\nmaybe_code_for_%s (", oname->name);
> -  print_overload_arguments (oname);
> -  printf (")\n{\n");
> +  fprintf (file, "\ninsn_code\nmaybe_code_for_%s (", oname->name);
> + print_overload_arguments (oname, file);  fprintf (file, ")\n{\n");
> 
>    /* Use a sequence of "if" statements for each instance.  */
>    for (overloaded_instance *instance = oname->first_instance;
>         instance; instance = instance->next)
>      {
> -      print_overload_test (instance);
> -      printf ("    return CODE_FOR_%s;\n", instance->name);
> +      print_overload_test (instance, file);
> +      fprintf (file, "    return CODE_FOR_%s;\n", instance->name);
>      }
> 
>    /* Return null if no match was found.  */
> -  printf ("  return CODE_FOR_nothing;\n}\n");
> +  fprintf (file, "  return CODE_FOR_nothing;\n}\n");
>  }
> 
>  /* Emit a maybe_gen_* function for ONAME.  */
> 
>  static void
> -handle_overloaded_gen (overloaded_name *oname)
> +handle_overloaded_gen (overloaded_name *oname, FILE *file)
>  {
>    unsigned HOST_WIDE_INT seen = 0;
>    /* All patterns must have the same number of operands.  */ @@ -827,25
> +828,25 @@ handle_overloaded_gen (overloaded_name *oname)
>        seen |= mask;
> 
>        /* Print the function prototype.  */
> -      printf ("\nrtx\nmaybe_gen_%s (", oname->name);
> -      print_overload_arguments (oname);
> +      fprintf (file, "\nrtx\nmaybe_gen_%s (", oname->name);
> +      print_overload_arguments (oname, file);
>        for (int i = 0; i < stats.num_generator_args; ++i)
> -	printf (", rtx x%d", i);
> -      printf (")\n{\n");
> +	fprintf (file, ", rtx x%d", i);
> +      fprintf (file, ")\n{\n");
> 
>        /* Use maybe_code_for_*, instead of duplicating the selection
>  	 logic here.  */
> -      printf ("  insn_code code = maybe_code_for_%s (", oname->name);
> +      fprintf (file, "  insn_code code = maybe_code_for_%s (",
> + oname->name);
>        for (unsigned int i = 0; i < oname->arg_types.length (); ++i)
> -	printf ("%sarg%d", i == 0 ? "" : ", ", i);
> -      printf (");\n"
> +	fprintf (file, "%sarg%d", i == 0 ? "" : ", ", i);
> +      fprintf (file, ");\n"
>  	      "  if (code != CODE_FOR_nothing)\n"
>  	      "    {\n"
>  	      "      gcc_assert (insn_data[code].n_generator_args == %d);\n"
>  	      "      return GEN_FCN (code) (", stats.num_generator_args);
>        for (int i = 0; i < stats.num_generator_args; ++i)
> -	printf ("%sx%d", i == 0 ? "" : ", ", i);
> -      printf (");\n"
> +	fprintf (file, "%sx%d", i == 0 ? "" : ", ", i);
> +      fprintf (file, ");\n"
>  	      "    }\n"
>  	      "  else\n"
>  	      "    return NULL_RTX;\n"
> @@ -853,12 +854,78 @@ handle_overloaded_gen (overloaded_name
> *oname)
>      }
>  }
> 
> +void
> +print_header (FILE *file)
> +{
> +  fprintf (file, "/* Generated automatically by the program
> +`genemit'\n\ from the machine description file `md'.  */\n\n");
> +
> +  fprintf (file, "#define IN_TARGET_CODE 1\n");
> +  fprintf (file, "#include \"config.h\"\n");
> +  fprintf (file, "#include \"system.h\"\n");
> +  fprintf (file, "#include \"coretypes.h\"\n");
> +  fprintf (file, "#include \"backend.h\"\n");
> +  fprintf (file, "#include \"predict.h\"\n");
> +  fprintf (file, "#include \"tree.h\"\n");
> +  fprintf (file, "#include \"rtl.h\"\n");
> +  fprintf (file, "#include \"alias.h\"\n");
> +  fprintf (file, "#include \"varasm.h\"\n");
> +  fprintf (file, "#include \"stor-layout.h\"\n");
> +  fprintf (file, "#include \"calls.h\"\n");
> +  fprintf (file, "#include \"memmodel.h\"\n");
> +  fprintf (file, "#include \"tm_p.h\"\n");
> +  fprintf (file, "#include \"flags.h\"\n");
> +  fprintf (file, "#include \"insn-config.h\"\n");
> +  fprintf (file, "#include \"expmed.h\"\n");
> +  fprintf (file, "#include \"dojump.h\"\n");
> +  fprintf (file, "#include \"explow.h\"\n");
> +  fprintf (file, "#include \"emit-rtl.h\"\n");
> +  fprintf (file, "#include \"stmt.h\"\n");
> +  fprintf (file, "#include \"expr.h\"\n");
> +  fprintf (file, "#include \"insn-codes.h\"\n");
> +  fprintf (file, "#include \"optabs.h\"\n");
> +  fprintf (file, "#include \"dfp.h\"\n");
> +  fprintf (file, "#include \"output.h\"\n");
> +  fprintf (file, "#include \"recog.h\"\n");
> +  fprintf (file, "#include \"df.h\"\n");
> +  fprintf (file, "#include \"resource.h\"\n");
> +  fprintf (file, "#include \"reload.h\"\n");
> +  fprintf (file, "#include \"diagnostic-core.h\"\n");
> +  fprintf (file, "#include \"regs.h\"\n");
> +  fprintf (file, "#include \"tm-constrs.h\"\n");
> +  fprintf (file, "#include \"ggc.h\"\n");
> +  fprintf (file, "#include \"target.h\"\n\n"); }
> +
> +static const char *output_file_name = NULL; static int nfiles = 10;
> +
> +static bool
> +handle_arg (const char *arg)
> +{
> +  if (arg[1] == 'O')
> +    {
> +      output_file_name = &arg[2];
> +      return true;
> +    }
> +
> +  if (arg[1] == 'P')
> +    {
> +      sscanf (&arg[2], "%d", &nfiles);
> +      return true;
> +    }
> +  return false;
> +}
> +
>  int
>  main (int argc, const char **argv)
>  {
>    progname = "genemit";
> 
> -  if (!init_rtx_reader_args (argc, argv))
> +  if (!init_rtx_reader_args_cb (argc, argv, handle_arg))
> +    return (FATAL_EXIT_CODE);
> +
> +  if (output_file_name == NULL)
>      return (FATAL_EXIT_CODE);
> 
>  #define DEF_INTERNAL_OPTAB_FN(NAME, FLAGS, OPTAB, TYPE) \ @@ -
> 868,86 +935,77 @@ main (int argc, const char **argv)
>    /* Assign sequential codes to all entries in the machine description
>       in parallel with the tables in insn-output.cc.  */
> 
> -  printf ("/* Generated automatically by the program `genemit'\n\ -from the
> machine description file `md'.  */\n\n");
> +  int npatterns = count_patterns ();
> +  md_rtx_info info;
> 
> -  printf ("#define IN_TARGET_CODE 1\n");
> -  printf ("#include \"config.h\"\n");
> -  printf ("#include \"system.h\"\n");
> -  printf ("#include \"coretypes.h\"\n");
> -  printf ("#include \"backend.h\"\n");
> -  printf ("#include \"predict.h\"\n");
> -  printf ("#include \"tree.h\"\n");
> -  printf ("#include \"rtl.h\"\n");
> -  printf ("#include \"alias.h\"\n");
> -  printf ("#include \"varasm.h\"\n");
> -  printf ("#include \"stor-layout.h\"\n");
> -  printf ("#include \"calls.h\"\n");
> -  printf ("#include \"memmodel.h\"\n");
> -  printf ("#include \"tm_p.h\"\n");
> -  printf ("#include \"flags.h\"\n");
> -  printf ("#include \"insn-config.h\"\n");
> -  printf ("#include \"expmed.h\"\n");
> -  printf ("#include \"dojump.h\"\n");
> -  printf ("#include \"explow.h\"\n");
> -  printf ("#include \"emit-rtl.h\"\n");
> -  printf ("#include \"stmt.h\"\n");
> -  printf ("#include \"expr.h\"\n");
> -  printf ("#include \"insn-codes.h\"\n");
> -  printf ("#include \"optabs.h\"\n");
> -  printf ("#include \"dfp.h\"\n");
> -  printf ("#include \"output.h\"\n");
> -  printf ("#include \"recog.h\"\n");
> -  printf ("#include \"df.h\"\n");
> -  printf ("#include \"resource.h\"\n");
> -  printf ("#include \"reload.h\"\n");
> -  printf ("#include \"diagnostic-core.h\"\n");
> -  printf ("#include \"regs.h\"\n");
> -  printf ("#include \"tm-constrs.h\"\n");
> -  printf ("#include \"ggc.h\"\n");
> -  printf ("#include \"target.h\"\n\n");
> +  int npatterns_per_file = npatterns / nfiles + 1;
> 
> -  /* Read the machine description.  */
> +  gcc_assert (npatterns_per_file > 1);
> 
> -  md_rtx_info info;
> +  const int filename_len = 128;
> +  char filename[filename_len];
> +
> +  int filenum = 0;
> +  int count = 0;
> +  FILE *file = NULL;
> +
> +  /* Read the machine description.  */
>    while (read_md_rtx (&info))
> -    switch (GET_CODE (info.def))
> -      {
> -      case DEFINE_INSN:
> -	gen_insn (&info);
> -	break;
> +    {
> +      if (count == 0 || count == npatterns_per_file)
> +	{
> +	  if (file)
> +	    if (fclose (file) != 0)
> +	      return FATAL_EXIT_CODE;
> 
> -      case DEFINE_EXPAND:
> -	printf ("/* %s:%d */\n", info.loc.filename, info.loc.lineno);
> -	gen_expand (&info);
> -	break;
> +	  filenum++;
> +	  snprintf (filename, filename_len, "%s-%d.cc", output_file_name,
> +		    filenum);
> +	  file = fopen (filename, "w");
> 
> -      case DEFINE_SPLIT:
> -	printf ("/* %s:%d */\n", info.loc.filename, info.loc.lineno);
> -	gen_split (&info);
> -	break;
> +	  print_header (file);
> +	  count = 0;
> +	}
> 
> -      case DEFINE_PEEPHOLE2:
> -	printf ("/* %s:%d */\n", info.loc.filename, info.loc.lineno);
> -	gen_split (&info);
> -	break;
> +      switch (GET_CODE (info.def))
> +	{
> +	case DEFINE_INSN:
> +	  gen_insn (&info, file);
> +	  break;
> 
> -      default:
> -	break;
> -      }
> +	case DEFINE_EXPAND:
> +	  fprintf (file, "/* %s:%d */\n", info.loc.filename, info.loc.lineno);
> +	  gen_expand (&info, file);
> +	  break;
> +
> +	case DEFINE_SPLIT:
> +	  fprintf (file, "/* %s:%d */\n", info.loc.filename, info.loc.lineno);
> +	  gen_split (&info, file);
> +	  break;
> +
> +	case DEFINE_PEEPHOLE2:
> +	  fprintf (file, "/* %s:%d */\n", info.loc.filename, info.loc.lineno);
> +	  gen_split (&info, file);
> +	  break;
> +
> +	default:
> +	  break;
> +	}
> +
> +      count++;
> +    }
> 
>    /* Write out the routines to add CLOBBERs to a pattern and say whether they
>       clobber a hard reg.  */
> -  output_add_clobbers (&info);
> -  output_added_clobbers_hard_reg_p ();
> +  output_add_clobbers (&info, file);
> +  output_added_clobbers_hard_reg_p (file);
> 
>    for (overloaded_name *oname = rtx_reader_ptr->get_overloads ();
>         oname; oname = oname->next)
>      {
> -      handle_overloaded_code_for (oname);
> -      handle_overloaded_gen (oname);
> +      handle_overloaded_code_for (oname, file);
> +      handle_overloaded_gen (oname, file);
>      }
> 
> -  fflush (stdout);
> -  return (ferror (stdout) != 0 ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE);
> +  return (fclose (file) != 0 ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE);
>  }
> diff --git a/gcc/gensupport.cc b/gcc/gensupport.cc index
> dd920d673b4..367ba1a30a4 100644
> --- a/gcc/gensupport.cc
> +++ b/gcc/gensupport.cc
> @@ -3131,6 +3131,42 @@ init_rtx_reader_args (int argc, const char **argv)
>    return init_rtx_reader_args_cb (argc, argv, 0);  }
> 
> 
> 
> +/* Count the number of patterns in all queues and return the count.  */
> +int count_patterns () {
> +  int count = 0;
> +  class queue_elem *cur = define_attr_queue;
> +  while (cur != NULL)
> +    {
> +      count++;
> +      cur = cur->next;
> +    }
> +
> +  cur = define_pred_queue;
> +  while (cur != NULL)
> +    {
> +      count++;
> +      cur = cur->next;
> +    }
> +
> +  cur = define_insn_queue;
> +  while (cur != NULL)
> +    {
> +      count++;
> +      cur = cur->next;
> +    }
> +
> +  cur = other_queue;
> +  while (cur != NULL)
> +    {
> +      count++;
> +      cur = cur->next;
> +    }
> +
> +  return count;
> +}
> +
> 
> 
>  /* Try to read a single rtx from the file.  Return true on success,
>     describing it in *INFO.  */
> 
> diff --git a/gcc/gensupport.h b/gcc/gensupport.h index
> 7925e22ed41..7396118714b 100644
> --- a/gcc/gensupport.h
> +++ b/gcc/gensupport.h
> @@ -130,6 +130,7 @@ extern rtx add_implicit_parallel (rtvec);  extern
> rtx_reader *init_rtx_reader_args_cb (int, const char **,
>  					    bool (*)(const char *));
>  extern rtx_reader *init_rtx_reader_args (int, const char **);
> +extern int count_patterns ();
>  extern bool read_md_rtx (md_rtx_info *);  extern unsigned int
> get_num_insn_codes ();
> 
> diff --git a/gcc/read-md.cc b/gcc/read-md.cc index
> fd38818e3a3..46ab9065e3e 100644
> --- a/gcc/read-md.cc
> +++ b/gcc/read-md.cc
> @@ -132,9 +132,9 @@ md_reader::fprint_md_ptr_loc (FILE *outf, const void
> *ptr)
> 
>  /* Special fprint_md_ptr_loc for writing to STDOUT.  */  void -
> md_reader::print_md_ptr_loc (const void *ptr)
> +md_reader::print_md_ptr_loc (const void *ptr, FILE *file)
>  {
> -  fprint_md_ptr_loc (stdout, ptr);
> +  fprint_md_ptr_loc (file, ptr);
>  }
> 
>  /* Return a condition that satisfies both COND1 and COND2.  Either string diff
> --git a/gcc/read-md.h b/gcc/read-md.h index b309c9c3deb..2adcb58478f
> 100644
> --- a/gcc/read-md.h
> +++ b/gcc/read-md.h
> @@ -194,7 +194,7 @@ class md_reader
>    const struct ptr_loc *get_md_ptr_loc (const void *ptr);
>    void copy_md_ptr_loc (const void *new_ptr, const void *old_ptr);
>    void fprint_md_ptr_loc (FILE *outf, const void *ptr);
> -  void print_md_ptr_loc (const void *ptr);
> +  void print_md_ptr_loc (const void *ptr, FILE * = stdout);
> 
>    struct enum_type *lookup_enum_type (const char *name);
>    void traverse_enum_types (htab_trav callback, void *info);
> --
> 2.41.0
Robin Dapp Oct. 13, 2023, 3:14 p.m. UTC | #2
> Testsuite is unchanged on all but x86 where, strangely, I saw several
> illegal instructions in the pch tests.  Those were not reproducible
> in a second manual test suite run.  I'm just running another full
> bootstrap and testsuite cycle with the latest trunk.

Follow-up on the pch tests.  The errors are an artifact of my testing.
I usually build unpatched without bootstrap and patched with it, comparing
the testsuite results.

When bootstrapping unpatched, the same errors occur.  When bootstrapping
with --with-arch=native we essentially use a vpxor for a memset which is
an illegal instruction on gc188 of the compile farm.  This might be a
known bug but I haven't found something in bugzilla.

In total:  Bootstrap and testsuite unchanged on x86, aarch64 and power10.

Regarding Tamar's comment:

> I'll leave the review to Richard, but I think you should adopt the same approach
> taken by the match.pd split, in that you provide the list of files as an argument
> to the genemit instead of the number of files.  And if no list is provided it
> outputs to stdout as it does today.

I think this would involve rewriting the argument handling for all gen*
tools (it is shared via gensupport).  Currently, I make use of the
callback and just add two new options which helps limit the number of
changes.  Is stdout so valuable from a debugging point of view that
changing it would be prohibitive?  Of course it's a change but I usually
grep through insn-emit.cc anyways and that would still be possible.

Regards
 Robin
Tamar Christina Oct. 13, 2023, 3:22 p.m. UTC | #3
> -----Original Message-----
> From: Robin Dapp <rdapp.gcc@gmail.com>
> Sent: Friday, October 13, 2023 4:15 PM
> To: gcc-patches <gcc-patches@gcc.gnu.org>
> Cc: rdapp.gcc@gmail.com; jeffreyalaw <jeffreyalaw@gmail.com>; Tamar
> Christina <Tamar.Christina@arm.com>; rjiejie@linux.alibaba.com
> Subject: Re: [PATCH] genemit: Split insn-emit.cc into ten files.
> 
> > Testsuite is unchanged on all but x86 where, strangely, I saw several
> > illegal instructions in the pch tests.  Those were not reproducible in
> > a second manual test suite run.  I'm just running another full
> > bootstrap and testsuite cycle with the latest trunk.
> 
> Follow-up on the pch tests.  The errors are an artifact of my testing.
> I usually build unpatched without bootstrap and patched with it, comparing
> the testsuite results.
> 
> When bootstrapping unpatched, the same errors occur.  When bootstrapping
> with --with-arch=native we essentially use a vpxor for a memset which is an
> illegal instruction on gc188 of the compile farm.  This might be a known bug
> but I haven't found something in bugzilla.
> 
> In total:  Bootstrap and testsuite unchanged on x86, aarch64 and power10.
> 
> Regarding Tamar's comment:
> 
> > I'll leave the review to Richard, but I think you should adopt the
> > same approach taken by the match.pd split, in that you provide the
> > list of files as an argument to the genemit instead of the number of
> > files.  And if no list is provided it outputs to stdout as it does today.
> 
> I think this would involve rewriting the argument handling for all gen* tools (it
> is shared via gensupport).  Currently, I make use of the callback and just add
> two new options which helps limit the number of changes.  Is stdout so
> valuable from a debugging point of view that changing it would be
> prohibitive?  Of course it's a change but I usually grep through insn-emit.cc
> anyways and that would still be possible.

Debugging here refers to debugging the output of gen*, which usually one does
With small examples when modifying the tool itself.

> I think this would involve rewriting the argument handling for all gen* tools (it
> is shared via gensupport).  Currently, I make use of the callback and just add
> two new options which helps limit the number of changes.  

Hmm why? The same callback you use to consume the listed arguments can be used to
consume the list can it not? I may be wrong, but from what I remember the callback
is called when main can't consume an argv value and it's allowed to eat all remaining input?

But I'll leave it up to Richard. Having to read multiple files to check a change to gen*
seems like a usability issue though.

Regards,
Tamar

> 
> Regards
>  Robin
Robin Dapp Oct. 13, 2023, 3:26 p.m. UTC | #4
> Hmm why? The same callback you use to consume the listed arguments
> can be used to consume the list can it not? I may be wrong, but from
> what I remember the callback is called when main can't consume an
> argv value and it's allowed to eat all remaining input?

Ah, I see.  If that's possible, then surely it shouldn't be that large
a change and I can add it still.

Regards
 Robin
Robin Dapp Oct. 16, 2023, 10:17 a.m. UTC | #5
Hi,

the attached v2 includes Tamar's suggestion of keeping the current
stdout behavior.  When no output files are passed (via -O) the output
is written to stdout as before.

Tamar also mentioned off-list that, similar to match.pd, it might make
sense to balance the partitions in a better way than a fixed number
of patterns threshold.  That's a good idea but I'd rather do that
separately as the current approach already helps considerably.

Attached v2 was bootstrapped and regtested on power10, aarch64 and
x86 are still running.
Stefan also tested v1 on s390 where the partitioning does not help
but also doesn't slow anything down.  insn-emit.cc isn't very large
to begin with on s390.

Regards
 Robin

From 34d05113a4e3c7e83a4731020307e26c1144af69 Mon Sep 17 00:00:00 2001
From: Robin Dapp <rdapp@ventanamicro.com>
Date: Thu, 12 Oct 2023 11:23:26 +0200
Subject: [PATCH v2] genemit: Split insn-emit.cc into several partitions.

On riscv insn-emit.cc has grown to over 1.2 mio lines of code and
compiling it takes considerable time.
Therefore, this patch adjust genemit to create several partitions
(insn-emit-1.cc to insn-emit-n.cc).  In order to do so it first counts
the number of available patterns, calculates the number of patterns per
file and starts a new file whenever that number is reached.

Similar to match.pd a configure option --with-emitinsn-partitions=num
is introduced that makes the number of partition configurable.

gcc/ChangeLog:

	PR bootstrap/84402
	PR target/111600

	* Makefile.in: Handle split insn-emit.cc.
	* configure: Regenerate.
	* configure.ac: Add --with-insnemit-partitions.
	* genemit.cc (output_peephole2_scratches): Print to file instead
	of stdout.
	(print_code): Ditto.
	(gen_rtx_scratch): Ditto.
	(gen_exp): Ditto.
	(gen_emit_seq): Ditto.
	(emit_c_code): Ditto.
	(gen_insn): Ditto.
	(gen_expand): Ditto.
	(gen_split): Ditto.
	(output_add_clobbers): Ditto.
	(output_added_clobbers_hard_reg_p): Ditto.
	(print_overload_arguments): Ditto.
	(print_overload_test): Ditto.
	(handle_overloaded_code_for): Ditto.
	(handle_overloaded_gen): Ditto.
	(print_header): New function.
	(handle_arg): New function.
	(main): Split output into 10 files.
	* gensupport.cc (count_patterns): New function.
	* gensupport.h (count_patterns): Define.
	* read-md.cc (md_reader::print_md_ptr_loc): Add file argument.
	* read-md.h (class md_reader): Change definition.
---
 gcc/Makefile.in   |  38 +++-
 gcc/configure     |  24 ++-
 gcc/configure.ac  |  13 ++
 gcc/genemit.cc    | 536 +++++++++++++++++++++++++---------------------
 gcc/gensupport.cc |  36 ++++
 gcc/gensupport.h  |   1 +
 gcc/read-md.cc    |   4 +-
 gcc/read-md.h     |   2 +-
 8 files changed, 399 insertions(+), 255 deletions(-)

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 9cc16268abf..ca0a616f768 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -236,6 +236,13 @@ GIMPLE_MATCH_PD_SEQ_O = $(patsubst %, gimple-match-%.o, $(MATCH_SPLITS_SEQ))
 GENERIC_MATCH_PD_SEQ_SRC = $(patsubst %, generic-match-%.cc, $(MATCH_SPLITS_SEQ))
 GENERIC_MATCH_PD_SEQ_O = $(patsubst %, generic-match-%.o, $(MATCH_SPLITS_SEQ))
 
+# The number of splits to be made for the insn-emit files.
+NUM_INSNEMIT_SPLITS = @DEFAULT_INSNEMIT_PARTITIONS@
+INSNEMIT_SPLITS_SEQ = $(wordlist 1,$(NUM_INSNEMIT_SPLITS),$(one_to_9999))
+INSNEMIT_SEQ_SRC = $(patsubst %, insn-emit-%.cc, $(INSNEMIT_SPLITS_SEQ))
+INSNEMIT_SEQ_TMP = $(patsubst %, tmp-emit-%.cc, $(INSNEMIT_SPLITS_SEQ))
+INSNEMIT_SEQ_O = $(patsubst %, insn-emit-%.o, $(INSNEMIT_SPLITS_SEQ))
+
 # These files are to have specific diagnostics suppressed, or are not to
 # be subject to -Werror:
 # flex output may yield harmless "no previous prototype" warnings
@@ -1354,7 +1361,7 @@ OBJS = \
 	insn-attrtab.o \
 	insn-automata.o \
 	insn-dfatab.o \
-	insn-emit.o \
+	$(INSNEMIT_SEQ_O) \
 	insn-extract.o \
 	insn-latencytab.o \
 	insn-modes.o \
@@ -1852,7 +1859,8 @@ TREECHECKING = @TREECHECKING@
 FULL_DRIVER_NAME=$(target_noncanonical)-gcc-$(version)$(exeext)
 
 MOSTLYCLEANFILES = insn-flags.h insn-config.h insn-codes.h \
- insn-output.cc insn-recog.cc insn-emit.cc insn-extract.cc insn-peep.cc \
+ insn-output.cc insn-recog.cc $(INSNEMIT_SEQ_SRC) \
+ insn-extract.cc insn-peep.cc \
  insn-attr.h insn-attr-common.h insn-attrtab.cc insn-dfatab.cc \
  insn-latencytab.cc insn-opinit.cc insn-opinit.h insn-preds.cc insn-constants.h \
  tm-preds.h tm-constrs.h checksum-options $(GIMPLE_MATCH_PD_SEQ_SRC) \
@@ -2481,11 +2489,11 @@ $(common_out_object_file): $(common_out_file)
 # and compile them.
 
 .PRECIOUS: insn-config.h insn-flags.h insn-codes.h insn-constants.h \
-  insn-emit.cc insn-recog.cc insn-extract.cc insn-output.cc insn-peep.cc \
-  insn-attr.h insn-attr-common.h insn-attrtab.cc insn-dfatab.cc \
-  insn-latencytab.cc insn-preds.cc $(GIMPLE_MATCH_PD_SEQ_SRC) \
-  $(GENERIC_MATCH_PD_SEQ_SRC) gimple-match-auto.h generic-match-auto.h \
-  insn-target-def.h
+  $(INSNEMIT_SEQ_SRC) insn-recog.cc insn-extract.cc insn-output.cc \
+  insn-peep.cc insn-attr.h insn-attr-common.h insn-attrtab.cc \
+  insn-dfatab.cc insn-latencytab.cc insn-preds.cc \
+  $(GIMPLE_MATCH_PD_SEQ_SRC) $(GENERIC_MATCH_PD_SEQ_SRC) \
+  gimple-match-auto.h generic-match-auto.h insn-target-def.h
 
 # Dependencies for the md file.  The first time through, we just assume
 # the md file itself and the generated dependency file (in order to get
@@ -2508,7 +2516,7 @@ s-mddeps: $(md_file) $(MD_INCLUDES) build/genmddeps$(build_exeext)
 simple_rtl_generated_h	= insn-attr.h insn-attr-common.h insn-codes.h \
 			  insn-config.h insn-flags.h insn-target-def.h
 
-simple_rtl_generated_c	= insn-automata.cc insn-emit.cc \
+simple_rtl_generated_c	= insn-automata.cc \
 			  insn-extract.cc insn-output.cc \
 			  insn-peep.cc insn-recog.cc
 
@@ -2537,8 +2545,22 @@ $(simple_generated_c:insn-%.cc=s-%): s-%: build/gen%$(build_exeext)
 	$(SHELL) $(srcdir)/../move-if-change tmp-$*.cc insn-$*.cc
 	$(STAMP) s-$*
 
+# genemit splits its output into different files and doesn't write to
+# stdout. (but rather to tmp-emit-01.cc..tmp-emit-10.cc)
+s-tmp-emit: build/genemit$(build_exeext) insn-conditions.md
+	$(RUN_GEN) build/genemit$(build_exeext) $(md_file) insn-conditions.md \
+	  $(addprefix -O,${INSNEMIT_SEQ_TMP})
+	$(STAMP) s-tmp-emit
+
+$(INSNEMIT_SEQ_SRC): insn-emit-%.cc: s-insn-emit-%; @true
+$(INSNEMIT_SEQ_SRC:insn-emit-%.cc=s-insn-emit-%): s-insn-emit-%: s-tmp-emit
+	$(SHELL) $(srcdir)/../move-if-change tmp-emit-$*.cc insn-emit-$*.cc
+	$(STAMP) s-insn-emit-$*
+
 # gencheck doesn't read the machine description, and the file produced
 # doesn't use the insn-* convention.
+
+# --> s-check has prerequisite tree-check.h (though nothing to do)
 tree-check.h: s-check ; @true
 s-check : build/gencheck$(build_exeext)
 	$(RUN_GEN) build/gencheck$(build_exeext) > tmp-check.h
diff --git a/gcc/configure b/gcc/configure
index c43bde8174b..00672b18f72 100755
--- a/gcc/configure
+++ b/gcc/configure
@@ -842,6 +842,7 @@ enable_gcov
 enable_shared
 enable_fixed_point
 enable_decimal_float
+DEFAULT_INSNEMIT_PARTITIONS
 DEFAULT_MATCHPD_PARTITIONS
 with_float
 with_cpu
@@ -971,6 +972,7 @@ enable_multilib
 enable_multiarch
 with_stack_clash_protection_guard_size
 with_matchpd_partitions
+with_insnemit_partitions
 enable___cxa_atexit
 enable_decimal_float
 enable_fixed_point
@@ -1839,6 +1841,9 @@ Optional Packages:
   --with-matchpd-partitions=num
                           Set the number of partitions to make for gimple and
                           generic when splitting match.pd. [default=10]
+  --with-insnemit-partitions=num
+                          Set the number of partitions of insn-emit.cc for
+                          genemit to create. [default=10]
   --with-dwarf2           force the default debug format to be DWARF 2 (or
                           later)
   --with-specs=SPECS      add SPECS to driver command-line processing
@@ -7938,6 +7943,21 @@ fi
 
 
 
+# Specify the number of splits of insn-emit.cc to generate.
+
+# Check whether --with-insnemit-partitions was given.
+if test "${with_insnemit_partitions+set}" = set; then :
+  withval=$with_insnemit_partitions; DEFAULT_INSNEMIT_PARTITIONS="$with_insnemit_partitions"
+else
+  DEFAULT_INSNEMIT_PARTITIONS=10
+fi
+
+if (test $DEFAULT_INSNEMIT_PARTITIONS -lt 1); then
+  as_fn_error $? "Invalid value $DEFAULT_INSNEMIT_PARTITIONS for --with-insnemit-partitions. Cannot be negative." "$LINENO" 5
+fi
+
+
+
 # Enable __cxa_atexit for C++.
 # Check whether --enable-__cxa_atexit was given.
 if test "${enable___cxa_atexit+set}" = set; then :
@@ -19923,7 +19943,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 19926 "configure"
+#line 19946 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -20029,7 +20049,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 20032 "configure"
+#line 20052 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
diff --git a/gcc/configure.ac b/gcc/configure.ac
index fb8e32f8ee5..bc0ef734e6c 100644
--- a/gcc/configure.ac
+++ b/gcc/configure.ac
@@ -956,6 +956,19 @@ fi
 
 AC_SUBST(DEFAULT_MATCHPD_PARTITIONS)
 
+# Specify the number of splits of insn-emit.cc to generate.
+AC_ARG_WITH(insnemit-partitions,
+[AS_HELP_STRING([--with-insnemit-partitions=num],
+[Set the number of partitions of insn-emit.cc for genemit to create. [default=10]])],
+[DEFAULT_INSNEMIT_PARTITIONS="$with_insnemit_partitions"], [DEFAULT_INSNEMIT_PARTITIONS=10])
+if (test $DEFAULT_INSNEMIT_PARTITIONS -lt 1); then
+  AC_MSG_ERROR(m4_normalize([
+		Invalid value $DEFAULT_INSNEMIT_PARTITIONS for --with-insnemit-partitions. \
+		Cannot be negative.]))
+fi
+
+AC_SUBST(DEFAULT_INSNEMIT_PARTITIONS)
+
 # Enable __cxa_atexit for C++.
 AC_ARG_ENABLE(__cxa_atexit,
 [AS_HELP_STRING([--enable-__cxa_atexit], [enable __cxa_atexit for C++])],
diff --git a/gcc/genemit.cc b/gcc/genemit.cc
index 1ce0564076d..8d8a3c4aa80 100644
--- a/gcc/genemit.cc
+++ b/gcc/genemit.cc
@@ -49,29 +49,29 @@ struct clobber_ent
   struct clobber_ent *next;
 };
 
-static void output_peephole2_scratches	(rtx);
+static void output_peephole2_scratches	(rtx, FILE*);
 
 /* True for <X>_optab if that optab isn't allowed to fail.  */
 static bool nofail_optabs[NUM_OPTABS];
 
 static void
-print_code (RTX_CODE code)
+print_code (RTX_CODE code, FILE *file)
 {
   const char *p1;
   for (p1 = GET_RTX_NAME (code); *p1; p1++)
-    putchar (TOUPPER (*p1));
+    fprintf (file, "%c", TOUPPER (*p1));
 }
 
 static void
-gen_rtx_scratch (rtx x, enum rtx_code subroutine_type)
+gen_rtx_scratch (rtx x, enum rtx_code subroutine_type, FILE *file)
 {
   if (subroutine_type == DEFINE_PEEPHOLE2)
     {
-      printf ("operand%d", XINT (x, 0));
+      fprintf (file, "operand%d", XINT (x, 0));
     }
   else
     {
-      printf ("gen_rtx_SCRATCH (%smode)", GET_MODE_NAME (GET_MODE (x)));
+      fprintf (file, "gen_rtx_SCRATCH (%smode)", GET_MODE_NAME (GET_MODE (x)));
     }
 }
 
@@ -79,7 +79,8 @@ gen_rtx_scratch (rtx x, enum rtx_code subroutine_type)
    substituting any operand references appearing within.  */
 
 static void
-gen_exp (rtx x, enum rtx_code subroutine_type, char *used, md_rtx_info *info)
+gen_exp (rtx x, enum rtx_code subroutine_type, char *used, md_rtx_info *info,
+	 FILE *file)
 {
   RTX_CODE code;
   int i;
@@ -89,7 +90,7 @@ gen_exp (rtx x, enum rtx_code subroutine_type, char *used, md_rtx_info *info)
 
   if (x == 0)
     {
-      printf ("NULL_RTX");
+      fprintf (file, "NULL_RTX");
       return;
     }
 
@@ -103,67 +104,67 @@ gen_exp (rtx x, enum rtx_code subroutine_type, char *used, md_rtx_info *info)
 	{
 	  if (used[XINT (x, 0)])
 	    {
-	      printf ("copy_rtx (operand%d)", XINT (x, 0));
+	      fprintf (file, "copy_rtx (operand%d)", XINT (x, 0));
 	      return;
 	    }
 	  used[XINT (x, 0)] = 1;
 	}
-      printf ("operand%d", XINT (x, 0));
+      fprintf (file, "operand%d", XINT (x, 0));
       return;
 
     case MATCH_OP_DUP:
-      printf ("gen_rtx_fmt_");
+      fprintf (file, "gen_rtx_fmt_");
       for (i = 0; i < XVECLEN (x, 1); i++)
-	printf ("e");
-      printf (" (GET_CODE (operand%d), ", XINT (x, 0));
+	fprintf (file, "e");
+      fprintf (file, " (GET_CODE (operand%d), ", XINT (x, 0));
       if (GET_MODE (x) == VOIDmode)
-	printf ("GET_MODE (operand%d)", XINT (x, 0));
+	fprintf (file, "GET_MODE (operand%d)", XINT (x, 0));
       else
-	printf ("%smode", GET_MODE_NAME (GET_MODE (x)));
+	fprintf (file, "%smode", GET_MODE_NAME (GET_MODE (x)));
       for (i = 0; i < XVECLEN (x, 1); i++)
 	{
-	  printf (",\n\t\t");
-	  gen_exp (XVECEXP (x, 1, i), subroutine_type, used, info);
+	  fprintf (file, ",\n\t\t");
+	  gen_exp (XVECEXP (x, 1, i), subroutine_type, used, info, file);
 	}
-      printf (")");
+      fprintf (file, ")");
       return;
 
     case MATCH_OPERATOR:
-      printf ("gen_rtx_fmt_");
+      fprintf (file, "gen_rtx_fmt_");
       for (i = 0; i < XVECLEN (x, 2); i++)
-	printf ("e");
-      printf (" (GET_CODE (operand%d)", XINT (x, 0));
-      printf (", %smode", GET_MODE_NAME (GET_MODE (x)));
+	fprintf (file, "e");
+      fprintf (file, " (GET_CODE (operand%d)", XINT (x, 0));
+      fprintf (file, ", %smode", GET_MODE_NAME (GET_MODE (x)));
       for (i = 0; i < XVECLEN (x, 2); i++)
 	{
-	  printf (",\n\t\t");
-	  gen_exp (XVECEXP (x, 2, i), subroutine_type, used, info);
+	  fprintf (file, ",\n\t\t");
+	  gen_exp (XVECEXP (x, 2, i), subroutine_type, used, info, file);
 	}
-      printf (")");
+      fprintf (file, ")");
       return;
 
     case MATCH_PARALLEL:
     case MATCH_PAR_DUP:
-      printf ("operand%d", XINT (x, 0));
+      fprintf (file, "operand%d", XINT (x, 0));
       return;
 
     case MATCH_SCRATCH:
-      gen_rtx_scratch (x, subroutine_type);
+      gen_rtx_scratch (x, subroutine_type, file);
       return;
 
     case PC:
-      printf ("pc_rtx");
+      fprintf (file, "pc_rtx");
       return;
     case RETURN:
-      printf ("ret_rtx");
+      fprintf (file, "ret_rtx");
       return;
     case SIMPLE_RETURN:
-      printf ("simple_return_rtx");
+      fprintf (file, "simple_return_rtx");
       return;
     case CLOBBER:
       if (REG_P (XEXP (x, 0)))
 	{
-	  printf ("gen_hard_reg_clobber (%smode, %i)",
+	  fprintf (file, "gen_hard_reg_clobber (%smode, %i)",
 		  GET_MODE_NAME (GET_MODE (XEXP (x, 0))),
 		  REGNO (XEXP (x, 0)));
 	  return;
@@ -172,22 +173,22 @@ gen_exp (rtx x, enum rtx_code subroutine_type, char *used, md_rtx_info *info)
 
     case CONST_INT:
       if (INTVAL (x) == 0)
-	printf ("const0_rtx");
+	fprintf (file, "const0_rtx");
       else if (INTVAL (x) == 1)
-	printf ("const1_rtx");
+	fprintf (file, "const1_rtx");
       else if (INTVAL (x) == -1)
-	printf ("constm1_rtx");
+	fprintf (file, "constm1_rtx");
       else if (-MAX_SAVED_CONST_INT <= INTVAL (x)
 	       && INTVAL (x) <= MAX_SAVED_CONST_INT)
-	printf ("const_int_rtx[MAX_SAVED_CONST_INT + (%d)]",
+	fprintf (file, "const_int_rtx[MAX_SAVED_CONST_INT + (%d)]",
 		(int) INTVAL (x));
       else if (INTVAL (x) == STORE_FLAG_VALUE)
-	printf ("const_true_rtx");
+	fprintf (file, "const_true_rtx");
       else
 	{
-	  printf ("GEN_INT (");
-	  printf (HOST_WIDE_INT_PRINT_DEC_C, INTVAL (x));
-	  printf (")");
+	  fprintf (file, "GEN_INT (");
+	  fprintf (file, HOST_WIDE_INT_PRINT_DEC_C, INTVAL (x));
+	  fprintf (file, ")");
 	}
       return;
 
@@ -195,7 +196,7 @@ gen_exp (rtx x, enum rtx_code subroutine_type, char *used, md_rtx_info *info)
       /* Handle `const_double_zero' rtx.  */
       if (CONST_DOUBLE_REAL_VALUE (x)->cl == rvc_zero)
 	{
-	  printf ("CONST_DOUBLE_ATOF (\"0\", %smode)",
+	  fprintf (file, "CONST_DOUBLE_ATOF (\"0\", %smode)",
 		  GET_MODE_NAME (GET_MODE (x)));
 	  return;
 	}
@@ -210,12 +211,12 @@ gen_exp (rtx x, enum rtx_code subroutine_type, char *used, md_rtx_info *info)
       break;
     }
 
-  printf ("gen_rtx_");
-  print_code (code);
-  printf (" (");
+  fprintf (file, "gen_rtx_");
+  print_code (code, file);
+  fprintf (file, " (");
   if (!always_void_p (code))
     {
-      printf ("%smode", GET_MODE_NAME (GET_MODE (x)));
+      fprintf (file, "%smode", GET_MODE_NAME (GET_MODE (x)));
       sep = ",\n\t";
     }
 
@@ -225,41 +226,41 @@ gen_exp (rtx x, enum rtx_code subroutine_type, char *used, md_rtx_info *info)
     {
       if (fmt[i] == '0')
 	break;
-      fputs (sep, stdout);
+      fputs (sep, file);
       switch (fmt[i])
 	{
 	case 'e': case 'u':
-	  gen_exp (XEXP (x, i), subroutine_type, used, info);
+	  gen_exp (XEXP (x, i), subroutine_type, used, info, file);
 	  break;
 
 	case 'i':
-	  printf ("%u", XINT (x, i));
+	  fprintf (file, "%u", XINT (x, i));
 	  break;
 
 	case 'r':
-	  printf ("%u", REGNO (x));
+	  fprintf (file, "%u", REGNO (x));
 	  break;
 
 	case 'p':
 	  /* We don't have a way of parsing polynomial offsets yet,
 	     and hopefully never will.  */
-	  printf ("%d", SUBREG_BYTE (x).to_constant ());
+	  fprintf (file, "%d", SUBREG_BYTE (x).to_constant ());
 	  break;
 
 	case 's':
-	  printf ("\"%s\"", XSTR (x, i));
+	  fprintf (file, "\"%s\"", XSTR (x, i));
 	  break;
 
 	case 'E':
 	  {
 	    int j;
-	    printf ("gen_rtvec (%d", XVECLEN (x, i));
+	    fprintf (file, "gen_rtvec (%d", XVECLEN (x, i));
 	    for (j = 0; j < XVECLEN (x, i); j++)
 	      {
-		printf (",\n\t\t");
-		gen_exp (XVECEXP (x, i, j), subroutine_type, used, info);
+		fprintf (file, ",\n\t\t");
+		gen_exp (XVECEXP (x, i, j), subroutine_type, used, info, file);
 	      }
-	    printf (")");
+	    fprintf (file, ")");
 	    break;
 	  }
 
@@ -268,14 +269,14 @@ gen_exp (rtx x, enum rtx_code subroutine_type, char *used, md_rtx_info *info)
 	}
       sep = ",\n\t";
     }
-  printf (")");
+  fprintf (file, ")");
 }
 
 /* Output code to emit the instruction patterns in VEC, with each element
    becoming a separate instruction.  USED is as for gen_exp.  */
 
 static void
-gen_emit_seq (rtvec vec, char *used, md_rtx_info *info)
+gen_emit_seq (rtvec vec, char *used, md_rtx_info *info, FILE *file)
 {
   for (int i = 0, len = GET_NUM_ELEM (vec); i < len; ++i)
     {
@@ -283,17 +284,17 @@ gen_emit_seq (rtvec vec, char *used, md_rtx_info *info)
       rtx next = RTVEC_ELT (vec, i);
       if (const char *name = get_emit_function (next))
 	{
-	  printf ("  %s (", name);
-	  gen_exp (next, DEFINE_EXPAND, used, info);
-	  printf (");\n");
+	  fprintf (file, "  %s (", name);
+	  gen_exp (next, DEFINE_EXPAND, used, info, file);
+	  fprintf (file, ");\n");
 	  if (!last_p && needs_barrier_p (next))
-	    printf ("  emit_barrier ();");
+	    fprintf (file, "  emit_barrier ();");
 	}
       else
 	{
-	  printf ("  emit (");
-	  gen_exp (next, DEFINE_EXPAND, used, info);
-	  printf (", %s);\n", last_p ? "false" : "true");
+	  fprintf (file, "  emit (");
+	  gen_exp (next, DEFINE_EXPAND, used, info, file);
+	  fprintf (file, ", %s);\n", last_p ? "false" : "true");
 	}
     }
 }
@@ -303,27 +304,27 @@ gen_emit_seq (rtvec vec, char *used, md_rtx_info *info)
    for use in error messages.  */
 
 static void
-emit_c_code (const char *code, bool can_fail_p, const char *name)
+emit_c_code (const char *code, bool can_fail_p, const char *name, FILE *file)
 {
   if (can_fail_p)
-    printf ("#define FAIL return (end_sequence (), _val)\n");
+    fprintf (file, "#define FAIL return (end_sequence (), _val)\n");
   else
-    printf ("#define FAIL _Pragma (\"GCC error \\\"%s cannot FAIL\\\"\")"
+    fprintf (file, "#define FAIL _Pragma (\"GCC error \\\"%s cannot FAIL\\\"\")"
 	    " (void)0\n", name);
-  printf ("#define DONE return (_val = get_insns (), "
+  fprintf (file, "#define DONE return (_val = get_insns (), "
 	  "end_sequence (), _val)\n");
 
-  rtx_reader_ptr->print_md_ptr_loc (code);
-  printf ("%s\n", code);
+  rtx_reader_ptr->print_md_ptr_loc (code, file);
+  fprintf (file, "%s\n", code);
 
-  printf ("#undef DONE\n");
-  printf ("#undef FAIL\n");
+  fprintf (file, "#undef DONE\n");
+  fprintf (file, "#undef FAIL\n");
 }
 
 /* Generate the `gen_...' function for a DEFINE_INSN.  */
 
 static void
-gen_insn (md_rtx_info *info)
+gen_insn (md_rtx_info *info, FILE *file)
 {
   struct pattern_stats stats;
   int i;
@@ -409,7 +410,7 @@ gen_insn (md_rtx_info *info)
   if (XSTR (insn, 0)[0] == 0 || XSTR (insn, 0)[0] == '*')
     return;
 
-  printf ("/* %s:%d */\n", info->loc.filename, info->loc.lineno);
+  fprintf (file, "/* %s:%d */\n", info->loc.filename, info->loc.lineno);
 
   /* Find out how many operands this function has.  */
   get_pattern_stats (&stats, XVEC (insn, 1));
@@ -417,17 +418,17 @@ gen_insn (md_rtx_info *info)
     fatal_at (info->loc, "match_dup operand number has no match_operand");
 
   /* Output the function name and argument declarations.  */
-  printf ("rtx\ngen_%s (", XSTR (insn, 0));
+  fprintf (file, "rtx\ngen_%s (", XSTR (insn, 0));
   if (stats.num_generator_args)
     for (i = 0; i < stats.num_generator_args; i++)
       if (i)
-	printf (",\n\trtx operand%d ATTRIBUTE_UNUSED", i);
+	fprintf (file, ",\n\trtx operand%d ATTRIBUTE_UNUSED", i);
       else
-	printf ("rtx operand%d ATTRIBUTE_UNUSED", i);
+	fprintf (file, "rtx operand%d ATTRIBUTE_UNUSED", i);
   else
-    printf ("void");
-  printf (")\n");
-  printf ("{\n");
+    fprintf (file, "void");
+  fprintf (file, ")\n");
+  fprintf (file, "{\n");
 
   /* Output code to construct and return the rtl for the instruction body.  */
 
@@ -436,16 +437,16 @@ gen_insn (md_rtx_info *info)
   char *used = (XVECLEN (insn, 1) == 1
 		? NULL
 		: XCNEWVEC (char, stats.num_generator_args));
-  printf ("  return ");
-  gen_exp (pattern, DEFINE_INSN, used, info);
-  printf (";\n}\n\n");
+  fprintf (file, "  return ");
+  gen_exp (pattern, DEFINE_INSN, used, info, file);
+  fprintf (file, ";\n}\n\n");
   XDELETEVEC (used);
 }
 
 /* Generate the `gen_...' function for a DEFINE_EXPAND.  */
 
 static void
-gen_expand (md_rtx_info *info)
+gen_expand (md_rtx_info *info, FILE *file)
 {
   struct pattern_stats stats;
   int i;
@@ -466,17 +467,17 @@ gen_expand (md_rtx_info *info)
 			 "numbers above all other operands", XSTR (expand, 0));
 
   /* Output the function name and argument declarations.  */
-  printf ("rtx\ngen_%s (", XSTR (expand, 0));
+  fprintf (file, "rtx\ngen_%s (", XSTR (expand, 0));
   if (stats.num_generator_args)
     for (i = 0; i < stats.num_generator_args; i++)
       if (i)
-	printf (",\n\trtx operand%d", i);
+	fprintf (file, ",\n\trtx operand%d", i);
       else
-	printf ("rtx operand%d", i);
+	fprintf (file, "rtx operand%d", i);
   else
-    printf ("void");
-  printf (")\n");
-  printf ("{\n");
+    fprintf (file, "void");
+  fprintf (file, ")\n");
+  fprintf (file, "{\n");
 
   /* If we don't have any C code to write, only one insn is being written,
      and no MATCH_DUPs are present, we can just return the desired insn
@@ -485,18 +486,18 @@ gen_expand (md_rtx_info *info)
       && stats.max_opno >= stats.max_dup_opno
       && XVECLEN (expand, 1) == 1)
     {
-      printf ("  return ");
-      gen_exp (XVECEXP (expand, 1, 0), DEFINE_EXPAND, NULL, info);
-      printf (";\n}\n\n");
+      fprintf (file, "  return ");
+      gen_exp (XVECEXP (expand, 1, 0), DEFINE_EXPAND, NULL, info, file);
+      fprintf (file, ";\n}\n\n");
       return;
     }
 
   /* For each operand referred to only with MATCH_DUPs,
      make a local variable.  */
   for (i = stats.num_generator_args; i <= stats.max_dup_opno; i++)
-    printf ("  rtx operand%d;\n", i);
-  printf ("  rtx_insn *_val = 0;\n");
-  printf ("  start_sequence ();\n");
+    fprintf (file, "  rtx operand%d;\n", i);
+  fprintf (file, "  rtx_insn *_val = 0;\n");
+  fprintf (file, "  start_sequence ();\n");
 
   /* The fourth operand of DEFINE_EXPAND is some code to be executed
      before the actual construction.
@@ -506,13 +507,13 @@ gen_expand (md_rtx_info *info)
      So copy the operand values there before executing it.  */
   if (XSTR (expand, 3) && *XSTR (expand, 3))
     {
-      printf ("  {\n");
+      fprintf (file, "  {\n");
       if (stats.num_operand_vars > 0)
-	printf ("    rtx operands[%d];\n", stats.num_operand_vars);
+	fprintf (file, "    rtx operands[%d];\n", stats.num_operand_vars);
 
       /* Output code to copy the arguments into `operands'.  */
       for (i = 0; i < stats.num_generator_args; i++)
-	printf ("    operands[%d] = operand%d;\n", i, i);
+	fprintf (file, "    operands[%d] = operand%d;\n", i, i);
 
       /* Output the special code to be executed before the sequence
 	 is generated.  */
@@ -524,7 +525,7 @@ gen_expand (md_rtx_info *info)
 	  if (nofail_optabs[p.op])
 	    can_fail_p = false;
 	}
-      emit_c_code (XSTR (expand, 3), can_fail_p, XSTR (expand, 0));
+      emit_c_code (XSTR (expand, 3), can_fail_p, XSTR (expand, 0), file);
 
       /* Output code to copy the arguments back out of `operands'
 	 (unless we aren't going to use them at all).  */
@@ -532,29 +533,29 @@ gen_expand (md_rtx_info *info)
 	{
 	  for (i = 0; i <= MAX (stats.max_opno, stats.max_dup_opno); i++)
 	    {
-	      printf ("    operand%d = operands[%d];\n", i, i);
-	      printf ("    (void) operand%d;\n", i);
+	      fprintf (file, "    operand%d = operands[%d];\n", i, i);
+	      fprintf (file, "    (void) operand%d;\n", i);
 	    }
 	}
-      printf ("  }\n");
+      fprintf (file, "  }\n");
     }
 
   used = XCNEWVEC (char, stats.num_operand_vars);
-  gen_emit_seq (XVEC (expand, 1), used, info);
+  gen_emit_seq (XVEC (expand, 1), used, info, file);
   XDELETEVEC (used);
 
   /* Call `get_insns' to extract the list of all the
      insns emitted within this gen_... function.  */
 
-  printf ("  _val = get_insns ();\n");
-  printf ("  end_sequence ();\n");
-  printf ("  return _val;\n}\n\n");
+  fprintf (file, "  _val = get_insns ();\n");
+  fprintf (file, "  end_sequence ();\n");
+  fprintf (file, "  return _val;\n}\n\n");
 }
 
 /* Like gen_expand, but generates insns resulting from splitting SPLIT.  */
 
 static void
-gen_split (md_rtx_info *info)
+gen_split (md_rtx_info *info, FILE *file)
 {
   struct pattern_stats stats;
   int i;
@@ -580,62 +581,62 @@ gen_split (md_rtx_info *info)
   /* Output the prototype, function name and argument declarations.  */
   if (GET_CODE (split) == DEFINE_PEEPHOLE2)
     {
-      printf ("extern rtx_insn *gen_%s_%d (rtx_insn *, rtx *);\n",
+      fprintf (file, "extern rtx_insn *gen_%s_%d (rtx_insn *, rtx *);\n",
 	      name, info->index);
-      printf ("rtx_insn *\ngen_%s_%d (rtx_insn *curr_insn ATTRIBUTE_UNUSED,"
+      fprintf (file, "rtx_insn *\ngen_%s_%d (rtx_insn *curr_insn ATTRIBUTE_UNUSED,"
 	      " rtx *operands%s)\n",
 	      name, info->index, unused);
     }
   else
     {
-      printf ("extern rtx_insn *gen_split_%d (rtx_insn *, rtx *);\n",
+      fprintf (file, "extern rtx_insn *gen_split_%d (rtx_insn *, rtx *);\n",
 	      info->index);
-      printf ("rtx_insn *\ngen_split_%d "
+      fprintf (file, "rtx_insn *\ngen_split_%d "
 	      "(rtx_insn *curr_insn ATTRIBUTE_UNUSED, rtx *operands%s)\n",
 	      info->index, unused);
     }
-  printf ("{\n");
+  fprintf (file, "{\n");
 
   /* Declare all local variables.  */
   for (i = 0; i < stats.num_operand_vars; i++)
-    printf ("  rtx operand%d;\n", i);
-  printf ("  rtx_insn *_val = NULL;\n");
+    fprintf (file, "  rtx operand%d;\n", i);
+  fprintf (file, "  rtx_insn *_val = NULL;\n");
 
   if (GET_CODE (split) == DEFINE_PEEPHOLE2)
-    output_peephole2_scratches (split);
+    output_peephole2_scratches (split, file);
 
   const char *fn = info->loc.filename;
   for (const char *p = fn; *p; p++)
     if (*p == '/')
       fn = p + 1;
 
-  printf ("  if (dump_file)\n");
-  printf ("    fprintf (dump_file, \"Splitting with gen_%s_%d (%s:%d)\\n\");\n",
+  fprintf (file, "  if (dump_file)\n");
+  fprintf (file, "    fprintf (dump_file, \"Splitting with gen_%s_%d (%s:%d)\\n\");\n",
 	  name, info->index, fn, info->loc.lineno);
 
-  printf ("  start_sequence ();\n");
+  fprintf (file, "  start_sequence ();\n");
 
   /* The fourth operand of DEFINE_SPLIT is some code to be executed
      before the actual construction.  */
 
   if (XSTR (split, 3))
-    emit_c_code (XSTR (split, 3), true, name);
+    emit_c_code (XSTR (split, 3), true, name, file);
 
   /* Output code to copy the arguments back out of `operands'  */
   for (i = 0; i < stats.num_operand_vars; i++)
     {
-      printf ("  operand%d = operands[%d];\n", i, i);
-      printf ("  (void) operand%d;\n", i);
+      fprintf (file, "  operand%d = operands[%d];\n", i, i);
+      fprintf (file, "  (void) operand%d;\n", i);
     }
 
-  gen_emit_seq (XVEC (split, 2), used, info);
+  gen_emit_seq (XVEC (split, 2), used, info, file);
 
   /* Call `get_insns' to make a list of all the
      insns emitted within this gen_... function.  */
 
-  printf ("  _val = get_insns ();\n");
-  printf ("  end_sequence ();\n");
-  printf ("  return _val;\n}\n\n");
+  fprintf (file, "  _val = get_insns ();\n");
+  fprintf (file, "  end_sequence ();\n");
+  fprintf (file, "  return _val;\n}\n\n");
 
   free (used);
 }
@@ -645,37 +646,37 @@ gen_split (md_rtx_info *info)
    the end of the vector.  */
 
 static void
-output_add_clobbers (md_rtx_info *info)
+output_add_clobbers (md_rtx_info *info, FILE *file)
 {
   struct clobber_pat *clobber;
   struct clobber_ent *ent;
   int i;
 
-  printf ("\n\nvoid\nadd_clobbers (rtx pattern ATTRIBUTE_UNUSED, int insn_code_number)\n");
-  printf ("{\n");
-  printf ("  switch (insn_code_number)\n");
-  printf ("    {\n");
+  fprintf (file, "\n\nvoid\nadd_clobbers (rtx pattern ATTRIBUTE_UNUSED, int insn_code_number)\n");
+  fprintf (file, "{\n");
+  fprintf (file, "  switch (insn_code_number)\n");
+  fprintf (file, "    {\n");
 
   for (clobber = clobber_list; clobber; clobber = clobber->next)
     {
       for (ent = clobber->insns; ent; ent = ent->next)
-	printf ("    case %d:\n", ent->code_number);
+	fprintf (file, "    case %d:\n", ent->code_number);
 
       for (i = clobber->first_clobber; i < XVECLEN (clobber->pattern, 1); i++)
 	{
-	  printf ("      XVECEXP (pattern, 0, %d) = ", i);
+	  fprintf (file, "      XVECEXP (pattern, 0, %d) = ", i);
 	  gen_exp (XVECEXP (clobber->pattern, 1, i),
-		   GET_CODE (clobber->pattern), NULL, info);
-	  printf (";\n");
+		   GET_CODE (clobber->pattern), NULL, info, file);
+	  fprintf (file, ";\n");
 	}
 
-      printf ("      break;\n\n");
+      fprintf (file, "      break;\n\n");
     }
 
-  printf ("    default:\n");
-  printf ("      gcc_unreachable ();\n");
-  printf ("    }\n");
-  printf ("}\n");
+  fprintf (file, "    default:\n");
+  fprintf (file, "      gcc_unreachable ();\n");
+  fprintf (file, "    }\n");
+  fprintf (file, "}\n");
 }
 
 /* Write a function, `added_clobbers_hard_reg_p' that is given an insn_code
@@ -684,17 +685,17 @@ output_add_clobbers (md_rtx_info *info)
    SCRATCH.  */
 
 static void
-output_added_clobbers_hard_reg_p (void)
+output_added_clobbers_hard_reg_p (FILE *file)
 {
   struct clobber_pat *clobber;
   struct clobber_ent *ent;
   int clobber_p;
   bool used;
 
-  printf ("\n\nbool\nadded_clobbers_hard_reg_p (int insn_code_number)\n");
-  printf ("{\n");
-  printf ("  switch (insn_code_number)\n");
-  printf ("    {\n");
+  fprintf (file, "\n\nbool\nadded_clobbers_hard_reg_p (int insn_code_number)\n");
+  fprintf (file, "{\n");
+  fprintf (file, "  switch (insn_code_number)\n");
+  fprintf (file, "    {\n");
 
   for (clobber_p = 0; clobber_p <= 1; clobber_p++)
     {
@@ -703,25 +704,25 @@ output_added_clobbers_hard_reg_p (void)
 	if (clobber->has_hard_reg == clobber_p)
 	  for (ent = clobber->insns; ent; ent = ent->next)
 	    {
-	      printf ("    case %d:\n", ent->code_number);
+	      fprintf (file, "    case %d:\n", ent->code_number);
 	      used = true;
 	    }
 
       if (used)
-	printf ("      return %s;\n\n", clobber_p ? "true" : "false");
+	fprintf (file, "      return %s;\n\n", clobber_p ? "true" : "false");
     }
 
-  printf ("    default:\n");
-  printf ("      gcc_unreachable ();\n");
-  printf ("    }\n");
-  printf ("}\n");
+  fprintf (file, "    default:\n");
+  fprintf (file, "      gcc_unreachable ();\n");
+  fprintf (file, "    }\n");
+  fprintf (file, "}\n");
 }
 
 /* Generate code to invoke find_free_register () as needed for the
    scratch registers used by the peephole2 pattern in SPLIT.  */
 
 static void
-output_peephole2_scratches (rtx split)
+output_peephole2_scratches (rtx split, FILE *file)
 {
   int i;
   int insn_nr = 0;
@@ -746,12 +747,12 @@ output_peephole2_scratches (rtx split)
 
 	  if (first)
 	    {
-	      printf ("  HARD_REG_SET _regs_allocated;\n");
-	      printf ("  CLEAR_HARD_REG_SET (_regs_allocated);\n");
+	      fprintf (file, "  HARD_REG_SET _regs_allocated;\n");
+	      fprintf (file, "  CLEAR_HARD_REG_SET (_regs_allocated);\n");
 	      first = false;
 	    }
 
-	  printf ("  if ((operands[%d] = peep2_find_free_register (%d, %d, \"%s\", %smode, &_regs_allocated)) == NULL_RTX)\n\
+	  fprintf (file, "  if ((operands[%d] = peep2_find_free_register (%d, %d, \"%s\", %smode, &_regs_allocated)) == NULL_RTX)\n\
     return NULL;\n",
 		  XINT (elt, 0),
 		  insn_nr, last_insn_nr,
@@ -767,50 +768,50 @@ output_peephole2_scratches (rtx split)
 /* Print "arg<N>" parameter declarations for each argument N of ONAME.  */
 
 static void
-print_overload_arguments (overloaded_name *oname)
+print_overload_arguments (overloaded_name *oname, FILE *file)
 {
   for (unsigned int i = 0; i < oname->arg_types.length (); ++i)
-    printf ("%s%s arg%d", i == 0 ? "" : ", ", oname->arg_types[i], i);
+    fprintf (file, "%s%s arg%d", i == 0 ? "" : ", ", oname->arg_types[i], i);
 }
 
 /* Print code to test whether INSTANCE should be chosen, given that
    argument N of the overload is available as "arg<N>".  */
 
 static void
-print_overload_test (overloaded_instance *instance)
+print_overload_test (overloaded_instance *instance, FILE *file)
 {
   for (unsigned int i = 0; i < instance->arg_values.length (); ++i)
-    printf ("%sarg%d == %s", i == 0 ? "  if (" : "\n      && ",
+    fprintf (file, "%sarg%d == %s", i == 0 ? "  if (" : "\n      && ",
 	    i, instance->arg_values[i]);
-  printf (")\n");
+  fprintf (file, ")\n");
 }
 
 /* Emit a maybe_code_for_* function for ONAME.  */
 
 static void
-handle_overloaded_code_for (overloaded_name *oname)
+handle_overloaded_code_for (overloaded_name *oname, FILE *file)
 {
   /* Print the function prototype.  */
-  printf ("\ninsn_code\nmaybe_code_for_%s (", oname->name);
-  print_overload_arguments (oname);
-  printf (")\n{\n");
+  fprintf (file, "\ninsn_code\nmaybe_code_for_%s (", oname->name);
+  print_overload_arguments (oname, file);
+  fprintf (file, ")\n{\n");
 
   /* Use a sequence of "if" statements for each instance.  */
   for (overloaded_instance *instance = oname->first_instance;
        instance; instance = instance->next)
     {
-      print_overload_test (instance);
-      printf ("    return CODE_FOR_%s;\n", instance->name);
+      print_overload_test (instance, file);
+      fprintf (file, "    return CODE_FOR_%s;\n", instance->name);
     }
 
   /* Return null if no match was found.  */
-  printf ("  return CODE_FOR_nothing;\n}\n");
+  fprintf (file, "  return CODE_FOR_nothing;\n}\n");
 }
 
 /* Emit a maybe_gen_* function for ONAME.  */
 
 static void
-handle_overloaded_gen (overloaded_name *oname)
+handle_overloaded_gen (overloaded_name *oname, FILE *file)
 {
   unsigned HOST_WIDE_INT seen = 0;
   /* All patterns must have the same number of operands.  */
@@ -827,25 +828,25 @@ handle_overloaded_gen (overloaded_name *oname)
       seen |= mask;
 
       /* Print the function prototype.  */
-      printf ("\nrtx\nmaybe_gen_%s (", oname->name);
-      print_overload_arguments (oname);
+      fprintf (file, "\nrtx\nmaybe_gen_%s (", oname->name);
+      print_overload_arguments (oname, file);
       for (int i = 0; i < stats.num_generator_args; ++i)
-	printf (", rtx x%d", i);
-      printf (")\n{\n");
+	fprintf (file, ", rtx x%d", i);
+      fprintf (file, ")\n{\n");
 
       /* Use maybe_code_for_*, instead of duplicating the selection
 	 logic here.  */
-      printf ("  insn_code code = maybe_code_for_%s (", oname->name);
+      fprintf (file, "  insn_code code = maybe_code_for_%s (", oname->name);
       for (unsigned int i = 0; i < oname->arg_types.length (); ++i)
-	printf ("%sarg%d", i == 0 ? "" : ", ", i);
-      printf (");\n"
+	fprintf (file, "%sarg%d", i == 0 ? "" : ", ", i);
+      fprintf (file, ");\n"
 	      "  if (code != CODE_FOR_nothing)\n"
 	      "    {\n"
 	      "      gcc_assert (insn_data[code].n_generator_args == %d);\n"
 	      "      return GEN_FCN (code) (", stats.num_generator_args);
       for (int i = 0; i < stats.num_generator_args; ++i)
-	printf ("%sx%d", i == 0 ? "" : ", ", i);
-      printf (");\n"
+	fprintf (file, "%sx%d", i == 0 ? "" : ", ", i);
+      fprintf (file, ");\n"
 	      "    }\n"
 	      "  else\n"
 	      "    return NULL_RTX;\n"
@@ -853,12 +854,68 @@ handle_overloaded_gen (overloaded_name *oname)
     }
 }
 
+void
+print_header (FILE *file)
+{
+  fprintf (file, "/* Generated automatically by the program `genemit'\n\
+from the machine description file `md'.  */\n\n");
+
+  fprintf (file, "#define IN_TARGET_CODE 1\n");
+  fprintf (file, "#include \"config.h\"\n");
+  fprintf (file, "#include \"system.h\"\n");
+  fprintf (file, "#include \"coretypes.h\"\n");
+  fprintf (file, "#include \"backend.h\"\n");
+  fprintf (file, "#include \"predict.h\"\n");
+  fprintf (file, "#include \"tree.h\"\n");
+  fprintf (file, "#include \"rtl.h\"\n");
+  fprintf (file, "#include \"alias.h\"\n");
+  fprintf (file, "#include \"varasm.h\"\n");
+  fprintf (file, "#include \"stor-layout.h\"\n");
+  fprintf (file, "#include \"calls.h\"\n");
+  fprintf (file, "#include \"memmodel.h\"\n");
+  fprintf (file, "#include \"tm_p.h\"\n");
+  fprintf (file, "#include \"flags.h\"\n");
+  fprintf (file, "#include \"insn-config.h\"\n");
+  fprintf (file, "#include \"expmed.h\"\n");
+  fprintf (file, "#include \"dojump.h\"\n");
+  fprintf (file, "#include \"explow.h\"\n");
+  fprintf (file, "#include \"emit-rtl.h\"\n");
+  fprintf (file, "#include \"stmt.h\"\n");
+  fprintf (file, "#include \"expr.h\"\n");
+  fprintf (file, "#include \"insn-codes.h\"\n");
+  fprintf (file, "#include \"optabs.h\"\n");
+  fprintf (file, "#include \"dfp.h\"\n");
+  fprintf (file, "#include \"output.h\"\n");
+  fprintf (file, "#include \"recog.h\"\n");
+  fprintf (file, "#include \"df.h\"\n");
+  fprintf (file, "#include \"resource.h\"\n");
+  fprintf (file, "#include \"reload.h\"\n");
+  fprintf (file, "#include \"diagnostic-core.h\"\n");
+  fprintf (file, "#include \"regs.h\"\n");
+  fprintf (file, "#include \"tm-constrs.h\"\n");
+  fprintf (file, "#include \"ggc.h\"\n");
+  fprintf (file, "#include \"target.h\"\n\n");
+}
+
+auto_vec<const char *, 10> output_files;
+
+static bool
+handle_arg (const char *arg)
+{
+  if (arg[1] == 'O')
+    {
+      output_files.safe_push (&arg[2]);
+      return true;
+    }
+  return false;
+}
+
 int
 main (int argc, const char **argv)
 {
   progname = "genemit";
 
-  if (!init_rtx_reader_args (argc, argv))
+  if (!init_rtx_reader_args_cb (argc, argv, handle_arg))
     return (FATAL_EXIT_CODE);
 
 #define DEF_INTERNAL_OPTAB_FN(NAME, FLAGS, OPTAB, TYPE) \
@@ -868,86 +925,81 @@ main (int argc, const char **argv)
   /* Assign sequential codes to all entries in the machine description
      in parallel with the tables in insn-output.cc.  */
 
-  printf ("/* Generated automatically by the program `genemit'\n\
-from the machine description file `md'.  */\n\n");
+  int npatterns = count_patterns ();
+  md_rtx_info info;
 
-  printf ("#define IN_TARGET_CODE 1\n");
-  printf ("#include \"config.h\"\n");
-  printf ("#include \"system.h\"\n");
-  printf ("#include \"coretypes.h\"\n");
-  printf ("#include \"backend.h\"\n");
-  printf ("#include \"predict.h\"\n");
-  printf ("#include \"tree.h\"\n");
-  printf ("#include \"rtl.h\"\n");
-  printf ("#include \"alias.h\"\n");
-  printf ("#include \"varasm.h\"\n");
-  printf ("#include \"stor-layout.h\"\n");
-  printf ("#include \"calls.h\"\n");
-  printf ("#include \"memmodel.h\"\n");
-  printf ("#include \"tm_p.h\"\n");
-  printf ("#include \"flags.h\"\n");
-  printf ("#include \"insn-config.h\"\n");
-  printf ("#include \"expmed.h\"\n");
-  printf ("#include \"dojump.h\"\n");
-  printf ("#include \"explow.h\"\n");
-  printf ("#include \"emit-rtl.h\"\n");
-  printf ("#include \"stmt.h\"\n");
-  printf ("#include \"expr.h\"\n");
-  printf ("#include \"insn-codes.h\"\n");
-  printf ("#include \"optabs.h\"\n");
-  printf ("#include \"dfp.h\"\n");
-  printf ("#include \"output.h\"\n");
-  printf ("#include \"recog.h\"\n");
-  printf ("#include \"df.h\"\n");
-  printf ("#include \"resource.h\"\n");
-  printf ("#include \"reload.h\"\n");
-  printf ("#include \"diagnostic-core.h\"\n");
-  printf ("#include \"regs.h\"\n");
-  printf ("#include \"tm-constrs.h\"\n");
-  printf ("#include \"ggc.h\"\n");
-  printf ("#include \"target.h\"\n\n");
+  int npatterns_per_file = npatterns;
+  if (!output_files.is_empty ())
+    npatterns_per_file = npatterns / output_files.length () + 1;
 
-  /* Read the machine description.  */
+  gcc_assert (npatterns_per_file > 1);
 
-  md_rtx_info info;
+  /* Reverse so we can pop the first-added element.  */
+  output_files.reverse ();
+
+  int count = 0;
+  FILE *file = NULL;
+
+  /* Read the machine description.  */
   while (read_md_rtx (&info))
-    switch (GET_CODE (info.def))
-      {
-      case DEFINE_INSN:
-	gen_insn (&info);
-	break;
+    {
+      if (count == 0 || count == npatterns_per_file)
+	{
+	  if (file)
+	    if (fclose (file) != 0)
+	      return FATAL_EXIT_CODE;
 
-      case DEFINE_EXPAND:
-	printf ("/* %s:%d */\n", info.loc.filename, info.loc.lineno);
-	gen_expand (&info);
-	break;
+	  if (!output_files.is_empty ())
+	    {
+	      const char *const filename = output_files.pop ();
+	      file = fopen (filename, "w");
+	    }
+	  else
+	    file = stdout;
 
-      case DEFINE_SPLIT:
-	printf ("/* %s:%d */\n", info.loc.filename, info.loc.lineno);
-	gen_split (&info);
-	break;
+	  print_header (file);
+	  count = 0;
+	}
 
-      case DEFINE_PEEPHOLE2:
-	printf ("/* %s:%d */\n", info.loc.filename, info.loc.lineno);
-	gen_split (&info);
-	break;
+      switch (GET_CODE (info.def))
+	{
+	case DEFINE_INSN:
+	  gen_insn (&info, file);
+	  break;
 
-      default:
-	break;
-      }
+	case DEFINE_EXPAND:
+	  fprintf (file, "/* %s:%d */\n", info.loc.filename, info.loc.lineno);
+	  gen_expand (&info, file);
+	  break;
+
+	case DEFINE_SPLIT:
+	  fprintf (file, "/* %s:%d */\n", info.loc.filename, info.loc.lineno);
+	  gen_split (&info, file);
+	  break;
+
+	case DEFINE_PEEPHOLE2:
+	  fprintf (file, "/* %s:%d */\n", info.loc.filename, info.loc.lineno);
+	  gen_split (&info, file);
+	  break;
+
+	default:
+	  break;
+	}
+
+      count++;
+    }
 
   /* Write out the routines to add CLOBBERs to a pattern and say whether they
      clobber a hard reg.  */
-  output_add_clobbers (&info);
-  output_added_clobbers_hard_reg_p ();
+  output_add_clobbers (&info, file);
+  output_added_clobbers_hard_reg_p (file);
 
   for (overloaded_name *oname = rtx_reader_ptr->get_overloads ();
        oname; oname = oname->next)
     {
-      handle_overloaded_code_for (oname);
-      handle_overloaded_gen (oname);
+      handle_overloaded_code_for (oname, file);
+      handle_overloaded_gen (oname, file);
     }
 
-  fflush (stdout);
-  return (ferror (stdout) != 0 ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE);
+  return (fclose (file) != 0 ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE);
 }
diff --git a/gcc/gensupport.cc b/gcc/gensupport.cc
index dd920d673b4..367ba1a30a4 100644
--- a/gcc/gensupport.cc
+++ b/gcc/gensupport.cc
@@ -3131,6 +3131,42 @@ init_rtx_reader_args (int argc, const char **argv)
   return init_rtx_reader_args_cb (argc, argv, 0);
 }
 
+/* Count the number of patterns in all queues and return the count.  */
+int
+count_patterns ()
+{
+  int count = 0;
+  class queue_elem *cur = define_attr_queue;
+  while (cur != NULL)
+    {
+      count++;
+      cur = cur->next;
+    }
+
+  cur = define_pred_queue;
+  while (cur != NULL)
+    {
+      count++;
+      cur = cur->next;
+    }
+
+  cur = define_insn_queue;
+  while (cur != NULL)
+    {
+      count++;
+      cur = cur->next;
+    }
+
+  cur = other_queue;
+  while (cur != NULL)
+    {
+      count++;
+      cur = cur->next;
+    }
+
+  return count;
+}
+
 /* Try to read a single rtx from the file.  Return true on success,
    describing it in *INFO.  */
 
diff --git a/gcc/gensupport.h b/gcc/gensupport.h
index 7925e22ed41..7396118714b 100644
--- a/gcc/gensupport.h
+++ b/gcc/gensupport.h
@@ -130,6 +130,7 @@ extern rtx add_implicit_parallel (rtvec);
 extern rtx_reader *init_rtx_reader_args_cb (int, const char **,
 					    bool (*)(const char *));
 extern rtx_reader *init_rtx_reader_args (int, const char **);
+extern int count_patterns ();
 extern bool read_md_rtx (md_rtx_info *);
 extern unsigned int get_num_insn_codes ();
 
diff --git a/gcc/read-md.cc b/gcc/read-md.cc
index fd38818e3a3..46ab9065e3e 100644
--- a/gcc/read-md.cc
+++ b/gcc/read-md.cc
@@ -132,9 +132,9 @@ md_reader::fprint_md_ptr_loc (FILE *outf, const void *ptr)
 
 /* Special fprint_md_ptr_loc for writing to STDOUT.  */
 void
-md_reader::print_md_ptr_loc (const void *ptr)
+md_reader::print_md_ptr_loc (const void *ptr, FILE *file)
 {
-  fprint_md_ptr_loc (stdout, ptr);
+  fprint_md_ptr_loc (file, ptr);
 }
 
 /* Return a condition that satisfies both COND1 and COND2.  Either string
diff --git a/gcc/read-md.h b/gcc/read-md.h
index b309c9c3deb..2adcb58478f 100644
--- a/gcc/read-md.h
+++ b/gcc/read-md.h
@@ -194,7 +194,7 @@ class md_reader
   const struct ptr_loc *get_md_ptr_loc (const void *ptr);
   void copy_md_ptr_loc (const void *new_ptr, const void *old_ptr);
   void fprint_md_ptr_loc (FILE *outf, const void *ptr);
-  void print_md_ptr_loc (const void *ptr);
+  void print_md_ptr_loc (const void *ptr, FILE * = stdout);
 
   struct enum_type *lookup_enum_type (const char *name);
   void traverse_enum_types (htab_trav callback, void *info);
Sam James Oct. 16, 2023, 10:26 a.m. UTC | #6
Robin Dapp <rdapp.gcc@gmail.com> writes:

> Hi,
>
> the attached v2 includes Tamar's suggestion of keeping the current
> stdout behavior.  When no output files are passed (via -O) the output
> is written to stdout as before.
>
> Tamar also mentioned off-list that, similar to match.pd, it might make
> sense to balance the partitions in a better way than a fixed number
> of patterns threshold.  That's a good idea but I'd rather do that
> separately as the current approach already helps considerably.
>
> Attached v2 was bootstrapped and regtested on power10, aarch64 and
> x86 are still running.
> Stefan also tested v1 on s390 where the partitioning does not help
> but also doesn't slow anything down.  insn-emit.cc isn't very large
> to begin with on s390.

I tested v1 on x86/arm64, I'll do at least the same for v2. (I also
backported it to 13 for my own purposes locally and everything seemed
fine there.)

I didn't notice any change on my x86 machine but it's already
quite powerful and I didn't have a chance to try on anything weaker,
so I wasn't too surprised.

>
> Regards
>  Robin
>
> From 34d05113a4e3c7e83a4731020307e26c1144af69 Mon Sep 17 00:00:00 2001
> From: Robin Dapp <rdapp@ventanamicro.com>
> Date: Thu, 12 Oct 2023 11:23:26 +0200
> Subject: [PATCH v2] genemit: Split insn-emit.cc into several partitions.
>
> On riscv insn-emit.cc has grown to over 1.2 mio lines of code and
> compiling it takes considerable time.
> Therefore, this patch adjust genemit to create several partitions
> (insn-emit-1.cc to insn-emit-n.cc).  In order to do so it first counts
> the number of available patterns, calculates the number of patterns per
> file and starts a new file whenever that number is reached.
>
> Similar to match.pd a configure option --with-emitinsn-partitions=num
> is introduced that makes the number of partition configurable.
>
> gcc/ChangeLog:
>
> 	PR bootstrap/84402
> 	PR target/111600
>
> 	* Makefile.in: Handle split insn-emit.cc.
> 	* configure: Regenerate.
> 	* configure.ac: Add --with-insnemit-partitions.
> 	* genemit.cc (output_peephole2_scratches): Print to file instead
> 	of stdout.
> 	(print_code): Ditto.
> 	(gen_rtx_scratch): Ditto.
> 	(gen_exp): Ditto.
> 	(gen_emit_seq): Ditto.
> 	(emit_c_code): Ditto.
> 	(gen_insn): Ditto.
> 	(gen_expand): Ditto.
> 	(gen_split): Ditto.
> 	(output_add_clobbers): Ditto.
> 	(output_added_clobbers_hard_reg_p): Ditto.
> 	(print_overload_arguments): Ditto.
> 	(print_overload_test): Ditto.
> 	(handle_overloaded_code_for): Ditto.
> 	(handle_overloaded_gen): Ditto.
> 	(print_header): New function.
> 	(handle_arg): New function.
> 	(main): Split output into 10 files.
> 	* gensupport.cc (count_patterns): New function.
> 	* gensupport.h (count_patterns): Define.
> 	* read-md.cc (md_reader::print_md_ptr_loc): Add file argument.
> 	* read-md.h (class md_reader): Change definition.
> ---
>  gcc/Makefile.in   |  38 +++-
>  gcc/configure     |  24 ++-
>  gcc/configure.ac  |  13 ++
>  gcc/genemit.cc    | 536 +++++++++++++++++++++++++---------------------
>  gcc/gensupport.cc |  36 ++++
>  gcc/gensupport.h  |   1 +
>  gcc/read-md.cc    |   4 +-
>  gcc/read-md.h     |   2 +-
>  8 files changed, 399 insertions(+), 255 deletions(-)
>
> diff --git a/gcc/Makefile.in b/gcc/Makefile.in
> index 9cc16268abf..ca0a616f768 100644
> --- a/gcc/Makefile.in
> +++ b/gcc/Makefile.in
> @@ -236,6 +236,13 @@ GIMPLE_MATCH_PD_SEQ_O = $(patsubst %, gimple-match-%.o, $(MATCH_SPLITS_SEQ))
>  GENERIC_MATCH_PD_SEQ_SRC = $(patsubst %, generic-match-%.cc, $(MATCH_SPLITS_SEQ))
>  GENERIC_MATCH_PD_SEQ_O = $(patsubst %, generic-match-%.o, $(MATCH_SPLITS_SEQ))
>  
> +# The number of splits to be made for the insn-emit files.
> +NUM_INSNEMIT_SPLITS = @DEFAULT_INSNEMIT_PARTITIONS@
> +INSNEMIT_SPLITS_SEQ = $(wordlist 1,$(NUM_INSNEMIT_SPLITS),$(one_to_9999))
> +INSNEMIT_SEQ_SRC = $(patsubst %, insn-emit-%.cc, $(INSNEMIT_SPLITS_SEQ))
> +INSNEMIT_SEQ_TMP = $(patsubst %, tmp-emit-%.cc, $(INSNEMIT_SPLITS_SEQ))
> +INSNEMIT_SEQ_O = $(patsubst %, insn-emit-%.o, $(INSNEMIT_SPLITS_SEQ))
> +
>  # These files are to have specific diagnostics suppressed, or are not to
>  # be subject to -Werror:
>  # flex output may yield harmless "no previous prototype" warnings
> @@ -1354,7 +1361,7 @@ OBJS = \
>  	insn-attrtab.o \
>  	insn-automata.o \
>  	insn-dfatab.o \
> -	insn-emit.o \
> +	$(INSNEMIT_SEQ_O) \
>  	insn-extract.o \
>  	insn-latencytab.o \
>  	insn-modes.o \
> @@ -1852,7 +1859,8 @@ TREECHECKING = @TREECHECKING@
>  FULL_DRIVER_NAME=$(target_noncanonical)-gcc-$(version)$(exeext)
>  
>  MOSTLYCLEANFILES = insn-flags.h insn-config.h insn-codes.h \
> - insn-output.cc insn-recog.cc insn-emit.cc insn-extract.cc insn-peep.cc \
> + insn-output.cc insn-recog.cc $(INSNEMIT_SEQ_SRC) \
> + insn-extract.cc insn-peep.cc \
>   insn-attr.h insn-attr-common.h insn-attrtab.cc insn-dfatab.cc \
>   insn-latencytab.cc insn-opinit.cc insn-opinit.h insn-preds.cc insn-constants.h \
>   tm-preds.h tm-constrs.h checksum-options $(GIMPLE_MATCH_PD_SEQ_SRC) \
> @@ -2481,11 +2489,11 @@ $(common_out_object_file): $(common_out_file)
>  # and compile them.
>  
>  .PRECIOUS: insn-config.h insn-flags.h insn-codes.h insn-constants.h \
> -  insn-emit.cc insn-recog.cc insn-extract.cc insn-output.cc insn-peep.cc \
> -  insn-attr.h insn-attr-common.h insn-attrtab.cc insn-dfatab.cc \
> -  insn-latencytab.cc insn-preds.cc $(GIMPLE_MATCH_PD_SEQ_SRC) \
> -  $(GENERIC_MATCH_PD_SEQ_SRC) gimple-match-auto.h generic-match-auto.h \
> -  insn-target-def.h
> +  $(INSNEMIT_SEQ_SRC) insn-recog.cc insn-extract.cc insn-output.cc \
> +  insn-peep.cc insn-attr.h insn-attr-common.h insn-attrtab.cc \
> +  insn-dfatab.cc insn-latencytab.cc insn-preds.cc \
> +  $(GIMPLE_MATCH_PD_SEQ_SRC) $(GENERIC_MATCH_PD_SEQ_SRC) \
> +  gimple-match-auto.h generic-match-auto.h insn-target-def.h
>  
>  # Dependencies for the md file.  The first time through, we just assume
>  # the md file itself and the generated dependency file (in order to get
> @@ -2508,7 +2516,7 @@ s-mddeps: $(md_file) $(MD_INCLUDES) build/genmddeps$(build_exeext)
>  simple_rtl_generated_h	= insn-attr.h insn-attr-common.h insn-codes.h \
>  			  insn-config.h insn-flags.h insn-target-def.h
>  
> -simple_rtl_generated_c	= insn-automata.cc insn-emit.cc \
> +simple_rtl_generated_c	= insn-automata.cc \
>  			  insn-extract.cc insn-output.cc \
>  			  insn-peep.cc insn-recog.cc
>  
> @@ -2537,8 +2545,22 @@ $(simple_generated_c:insn-%.cc=s-%): s-%: build/gen%$(build_exeext)
>  	$(SHELL) $(srcdir)/../move-if-change tmp-$*.cc insn-$*.cc
>  	$(STAMP) s-$*
>  
> +# genemit splits its output into different files and doesn't write to
> +# stdout. (but rather to tmp-emit-01.cc..tmp-emit-10.cc)
> +s-tmp-emit: build/genemit$(build_exeext) insn-conditions.md
> +	$(RUN_GEN) build/genemit$(build_exeext) $(md_file) insn-conditions.md \
> +	  $(addprefix -O,${INSNEMIT_SEQ_TMP})
> +	$(STAMP) s-tmp-emit
> +
> +$(INSNEMIT_SEQ_SRC): insn-emit-%.cc: s-insn-emit-%; @true
> +$(INSNEMIT_SEQ_SRC:insn-emit-%.cc=s-insn-emit-%): s-insn-emit-%: s-tmp-emit
> +	$(SHELL) $(srcdir)/../move-if-change tmp-emit-$*.cc insn-emit-$*.cc
> +	$(STAMP) s-insn-emit-$*
> +
>  # gencheck doesn't read the machine description, and the file produced
>  # doesn't use the insn-* convention.
> +
> +# --> s-check has prerequisite tree-check.h (though nothing to do)
>  tree-check.h: s-check ; @true
>  s-check : build/gencheck$(build_exeext)
>  	$(RUN_GEN) build/gencheck$(build_exeext) > tmp-check.h
> diff --git a/gcc/configure b/gcc/configure
> index c43bde8174b..00672b18f72 100755
> --- a/gcc/configure
> +++ b/gcc/configure
> @@ -842,6 +842,7 @@ enable_gcov
>  enable_shared
>  enable_fixed_point
>  enable_decimal_float
> +DEFAULT_INSNEMIT_PARTITIONS
>  DEFAULT_MATCHPD_PARTITIONS
>  with_float
>  with_cpu
> @@ -971,6 +972,7 @@ enable_multilib
>  enable_multiarch
>  with_stack_clash_protection_guard_size
>  with_matchpd_partitions
> +with_insnemit_partitions
>  enable___cxa_atexit
>  enable_decimal_float
>  enable_fixed_point
> @@ -1839,6 +1841,9 @@ Optional Packages:
>    --with-matchpd-partitions=num
>                            Set the number of partitions to make for gimple and
>                            generic when splitting match.pd. [default=10]
> +  --with-insnemit-partitions=num
> +                          Set the number of partitions of insn-emit.cc for
> +                          genemit to create. [default=10]
>    --with-dwarf2           force the default debug format to be DWARF 2 (or
>                            later)
>    --with-specs=SPECS      add SPECS to driver command-line processing
> @@ -7938,6 +7943,21 @@ fi
>  
>  
>  
> +# Specify the number of splits of insn-emit.cc to generate.
> +
> +# Check whether --with-insnemit-partitions was given.
> +if test "${with_insnemit_partitions+set}" = set; then :
> +  withval=$with_insnemit_partitions; DEFAULT_INSNEMIT_PARTITIONS="$with_insnemit_partitions"
> +else
> +  DEFAULT_INSNEMIT_PARTITIONS=10
> +fi
> +
> +if (test $DEFAULT_INSNEMIT_PARTITIONS -lt 1); then
> +  as_fn_error $? "Invalid value $DEFAULT_INSNEMIT_PARTITIONS for --with-insnemit-partitions. Cannot be negative." "$LINENO" 5
> +fi
> +
> +
> +
>  # Enable __cxa_atexit for C++.
>  # Check whether --enable-__cxa_atexit was given.
>  if test "${enable___cxa_atexit+set}" = set; then :
> @@ -19923,7 +19943,7 @@ else
>    lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
>    lt_status=$lt_dlunknown
>    cat > conftest.$ac_ext <<_LT_EOF
> -#line 19926 "configure"
> +#line 19946 "configure"
>  #include "confdefs.h"
>  
>  #if HAVE_DLFCN_H
> @@ -20029,7 +20049,7 @@ else
>    lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
>    lt_status=$lt_dlunknown
>    cat > conftest.$ac_ext <<_LT_EOF
> -#line 20032 "configure"
> +#line 20052 "configure"
>  #include "confdefs.h"
>  
>  #if HAVE_DLFCN_H
> diff --git a/gcc/configure.ac b/gcc/configure.ac
> index fb8e32f8ee5..bc0ef734e6c 100644
> --- a/gcc/configure.ac
> +++ b/gcc/configure.ac
> @@ -956,6 +956,19 @@ fi
>  
>  AC_SUBST(DEFAULT_MATCHPD_PARTITIONS)
>  
> +# Specify the number of splits of insn-emit.cc to generate.
> +AC_ARG_WITH(insnemit-partitions,
> +[AS_HELP_STRING([--with-insnemit-partitions=num],
> +[Set the number of partitions of insn-emit.cc for genemit to create. [default=10]])],
> +[DEFAULT_INSNEMIT_PARTITIONS="$with_insnemit_partitions"], [DEFAULT_INSNEMIT_PARTITIONS=10])
> +if (test $DEFAULT_INSNEMIT_PARTITIONS -lt 1); then
> +  AC_MSG_ERROR(m4_normalize([
> +		Invalid value $DEFAULT_INSNEMIT_PARTITIONS for --with-insnemit-partitions. \
> +		Cannot be negative.]))
> +fi
> +
> +AC_SUBST(DEFAULT_INSNEMIT_PARTITIONS)
> +
>  # Enable __cxa_atexit for C++.
>  AC_ARG_ENABLE(__cxa_atexit,
>  [AS_HELP_STRING([--enable-__cxa_atexit], [enable __cxa_atexit for C++])],
> diff --git a/gcc/genemit.cc b/gcc/genemit.cc
> index 1ce0564076d..8d8a3c4aa80 100644
> --- a/gcc/genemit.cc
> +++ b/gcc/genemit.cc
> @@ -49,29 +49,29 @@ struct clobber_ent
>    struct clobber_ent *next;
>  };
>  
> -static void output_peephole2_scratches	(rtx);
> +static void output_peephole2_scratches	(rtx, FILE*);
>  
>  /* True for <X>_optab if that optab isn't allowed to fail.  */
>  static bool nofail_optabs[NUM_OPTABS];
>  
>  static void
> -print_code (RTX_CODE code)
> +print_code (RTX_CODE code, FILE *file)
>  {
>    const char *p1;
>    for (p1 = GET_RTX_NAME (code); *p1; p1++)
> -    putchar (TOUPPER (*p1));
> +    fprintf (file, "%c", TOUPPER (*p1));
>  }
>  
>  static void
> -gen_rtx_scratch (rtx x, enum rtx_code subroutine_type)
> +gen_rtx_scratch (rtx x, enum rtx_code subroutine_type, FILE *file)
>  {
>    if (subroutine_type == DEFINE_PEEPHOLE2)
>      {
> -      printf ("operand%d", XINT (x, 0));
> +      fprintf (file, "operand%d", XINT (x, 0));
>      }
>    else
>      {
> -      printf ("gen_rtx_SCRATCH (%smode)", GET_MODE_NAME (GET_MODE (x)));
> +      fprintf (file, "gen_rtx_SCRATCH (%smode)", GET_MODE_NAME (GET_MODE (x)));
>      }
>  }
>  
> @@ -79,7 +79,8 @@ gen_rtx_scratch (rtx x, enum rtx_code subroutine_type)
>     substituting any operand references appearing within.  */
>  
>  static void
> -gen_exp (rtx x, enum rtx_code subroutine_type, char *used, md_rtx_info *info)
> +gen_exp (rtx x, enum rtx_code subroutine_type, char *used, md_rtx_info *info,
> +	 FILE *file)
>  {
>    RTX_CODE code;
>    int i;
> @@ -89,7 +90,7 @@ gen_exp (rtx x, enum rtx_code subroutine_type, char *used, md_rtx_info *info)
>  
>    if (x == 0)
>      {
> -      printf ("NULL_RTX");
> +      fprintf (file, "NULL_RTX");
>        return;
>      }
>  
> @@ -103,67 +104,67 @@ gen_exp (rtx x, enum rtx_code subroutine_type, char *used, md_rtx_info *info)
>  	{
>  	  if (used[XINT (x, 0)])
>  	    {
> -	      printf ("copy_rtx (operand%d)", XINT (x, 0));
> +	      fprintf (file, "copy_rtx (operand%d)", XINT (x, 0));
>  	      return;
>  	    }
>  	  used[XINT (x, 0)] = 1;
>  	}
> -      printf ("operand%d", XINT (x, 0));
> +      fprintf (file, "operand%d", XINT (x, 0));
>        return;
>  
>      case MATCH_OP_DUP:
> -      printf ("gen_rtx_fmt_");
> +      fprintf (file, "gen_rtx_fmt_");
>        for (i = 0; i < XVECLEN (x, 1); i++)
> -	printf ("e");
> -      printf (" (GET_CODE (operand%d), ", XINT (x, 0));
> +	fprintf (file, "e");
> +      fprintf (file, " (GET_CODE (operand%d), ", XINT (x, 0));
>        if (GET_MODE (x) == VOIDmode)
> -	printf ("GET_MODE (operand%d)", XINT (x, 0));
> +	fprintf (file, "GET_MODE (operand%d)", XINT (x, 0));
>        else
> -	printf ("%smode", GET_MODE_NAME (GET_MODE (x)));
> +	fprintf (file, "%smode", GET_MODE_NAME (GET_MODE (x)));
>        for (i = 0; i < XVECLEN (x, 1); i++)
>  	{
> -	  printf (",\n\t\t");
> -	  gen_exp (XVECEXP (x, 1, i), subroutine_type, used, info);
> +	  fprintf (file, ",\n\t\t");
> +	  gen_exp (XVECEXP (x, 1, i), subroutine_type, used, info, file);
>  	}
> -      printf (")");
> +      fprintf (file, ")");
>        return;
>  
>      case MATCH_OPERATOR:
> -      printf ("gen_rtx_fmt_");
> +      fprintf (file, "gen_rtx_fmt_");
>        for (i = 0; i < XVECLEN (x, 2); i++)
> -	printf ("e");
> -      printf (" (GET_CODE (operand%d)", XINT (x, 0));
> -      printf (", %smode", GET_MODE_NAME (GET_MODE (x)));
> +	fprintf (file, "e");
> +      fprintf (file, " (GET_CODE (operand%d)", XINT (x, 0));
> +      fprintf (file, ", %smode", GET_MODE_NAME (GET_MODE (x)));
>        for (i = 0; i < XVECLEN (x, 2); i++)
>  	{
> -	  printf (",\n\t\t");
> -	  gen_exp (XVECEXP (x, 2, i), subroutine_type, used, info);
> +	  fprintf (file, ",\n\t\t");
> +	  gen_exp (XVECEXP (x, 2, i), subroutine_type, used, info, file);
>  	}
> -      printf (")");
> +      fprintf (file, ")");
>        return;
>  
>      case MATCH_PARALLEL:
>      case MATCH_PAR_DUP:
> -      printf ("operand%d", XINT (x, 0));
> +      fprintf (file, "operand%d", XINT (x, 0));
>        return;
>  
>      case MATCH_SCRATCH:
> -      gen_rtx_scratch (x, subroutine_type);
> +      gen_rtx_scratch (x, subroutine_type, file);
>        return;
>  
>      case PC:
> -      printf ("pc_rtx");
> +      fprintf (file, "pc_rtx");
>        return;
>      case RETURN:
> -      printf ("ret_rtx");
> +      fprintf (file, "ret_rtx");
>        return;
>      case SIMPLE_RETURN:
> -      printf ("simple_return_rtx");
> +      fprintf (file, "simple_return_rtx");
>        return;
>      case CLOBBER:
>        if (REG_P (XEXP (x, 0)))
>  	{
> -	  printf ("gen_hard_reg_clobber (%smode, %i)",
> +	  fprintf (file, "gen_hard_reg_clobber (%smode, %i)",
>  		  GET_MODE_NAME (GET_MODE (XEXP (x, 0))),
>  		  REGNO (XEXP (x, 0)));
>  	  return;
> @@ -172,22 +173,22 @@ gen_exp (rtx x, enum rtx_code subroutine_type, char *used, md_rtx_info *info)
>  
>      case CONST_INT:
>        if (INTVAL (x) == 0)
> -	printf ("const0_rtx");
> +	fprintf (file, "const0_rtx");
>        else if (INTVAL (x) == 1)
> -	printf ("const1_rtx");
> +	fprintf (file, "const1_rtx");
>        else if (INTVAL (x) == -1)
> -	printf ("constm1_rtx");
> +	fprintf (file, "constm1_rtx");
>        else if (-MAX_SAVED_CONST_INT <= INTVAL (x)
>  	       && INTVAL (x) <= MAX_SAVED_CONST_INT)
> -	printf ("const_int_rtx[MAX_SAVED_CONST_INT + (%d)]",
> +	fprintf (file, "const_int_rtx[MAX_SAVED_CONST_INT + (%d)]",
>  		(int) INTVAL (x));
>        else if (INTVAL (x) == STORE_FLAG_VALUE)
> -	printf ("const_true_rtx");
> +	fprintf (file, "const_true_rtx");
>        else
>  	{
> -	  printf ("GEN_INT (");
> -	  printf (HOST_WIDE_INT_PRINT_DEC_C, INTVAL (x));
> -	  printf (")");
> +	  fprintf (file, "GEN_INT (");
> +	  fprintf (file, HOST_WIDE_INT_PRINT_DEC_C, INTVAL (x));
> +	  fprintf (file, ")");
>  	}
>        return;
>  
> @@ -195,7 +196,7 @@ gen_exp (rtx x, enum rtx_code subroutine_type, char *used, md_rtx_info *info)
>        /* Handle `const_double_zero' rtx.  */
>        if (CONST_DOUBLE_REAL_VALUE (x)->cl == rvc_zero)
>  	{
> -	  printf ("CONST_DOUBLE_ATOF (\"0\", %smode)",
> +	  fprintf (file, "CONST_DOUBLE_ATOF (\"0\", %smode)",
>  		  GET_MODE_NAME (GET_MODE (x)));
>  	  return;
>  	}
> @@ -210,12 +211,12 @@ gen_exp (rtx x, enum rtx_code subroutine_type, char *used, md_rtx_info *info)
>        break;
>      }
>  
> -  printf ("gen_rtx_");
> -  print_code (code);
> -  printf (" (");
> +  fprintf (file, "gen_rtx_");
> +  print_code (code, file);
> +  fprintf (file, " (");
>    if (!always_void_p (code))
>      {
> -      printf ("%smode", GET_MODE_NAME (GET_MODE (x)));
> +      fprintf (file, "%smode", GET_MODE_NAME (GET_MODE (x)));
>        sep = ",\n\t";
>      }
>  
> @@ -225,41 +226,41 @@ gen_exp (rtx x, enum rtx_code subroutine_type, char *used, md_rtx_info *info)
>      {
>        if (fmt[i] == '0')
>  	break;
> -      fputs (sep, stdout);
> +      fputs (sep, file);
>        switch (fmt[i])
>  	{
>  	case 'e': case 'u':
> -	  gen_exp (XEXP (x, i), subroutine_type, used, info);
> +	  gen_exp (XEXP (x, i), subroutine_type, used, info, file);
>  	  break;
>  
>  	case 'i':
> -	  printf ("%u", XINT (x, i));
> +	  fprintf (file, "%u", XINT (x, i));
>  	  break;
>  
>  	case 'r':
> -	  printf ("%u", REGNO (x));
> +	  fprintf (file, "%u", REGNO (x));
>  	  break;
>  
>  	case 'p':
>  	  /* We don't have a way of parsing polynomial offsets yet,
>  	     and hopefully never will.  */
> -	  printf ("%d", SUBREG_BYTE (x).to_constant ());
> +	  fprintf (file, "%d", SUBREG_BYTE (x).to_constant ());
>  	  break;
>  
>  	case 's':
> -	  printf ("\"%s\"", XSTR (x, i));
> +	  fprintf (file, "\"%s\"", XSTR (x, i));
>  	  break;
>  
>  	case 'E':
>  	  {
>  	    int j;
> -	    printf ("gen_rtvec (%d", XVECLEN (x, i));
> +	    fprintf (file, "gen_rtvec (%d", XVECLEN (x, i));
>  	    for (j = 0; j < XVECLEN (x, i); j++)
>  	      {
> -		printf (",\n\t\t");
> -		gen_exp (XVECEXP (x, i, j), subroutine_type, used, info);
> +		fprintf (file, ",\n\t\t");
> +		gen_exp (XVECEXP (x, i, j), subroutine_type, used, info, file);
>  	      }
> -	    printf (")");
> +	    fprintf (file, ")");
>  	    break;
>  	  }
>  
> @@ -268,14 +269,14 @@ gen_exp (rtx x, enum rtx_code subroutine_type, char *used, md_rtx_info *info)
>  	}
>        sep = ",\n\t";
>      }
> -  printf (")");
> +  fprintf (file, ")");
>  }
>  
>  /* Output code to emit the instruction patterns in VEC, with each element
>     becoming a separate instruction.  USED is as for gen_exp.  */
>  
>  static void
> -gen_emit_seq (rtvec vec, char *used, md_rtx_info *info)
> +gen_emit_seq (rtvec vec, char *used, md_rtx_info *info, FILE *file)
>  {
>    for (int i = 0, len = GET_NUM_ELEM (vec); i < len; ++i)
>      {
> @@ -283,17 +284,17 @@ gen_emit_seq (rtvec vec, char *used, md_rtx_info *info)
>        rtx next = RTVEC_ELT (vec, i);
>        if (const char *name = get_emit_function (next))
>  	{
> -	  printf ("  %s (", name);
> -	  gen_exp (next, DEFINE_EXPAND, used, info);
> -	  printf (");\n");
> +	  fprintf (file, "  %s (", name);
> +	  gen_exp (next, DEFINE_EXPAND, used, info, file);
> +	  fprintf (file, ");\n");
>  	  if (!last_p && needs_barrier_p (next))
> -	    printf ("  emit_barrier ();");
> +	    fprintf (file, "  emit_barrier ();");
>  	}
>        else
>  	{
> -	  printf ("  emit (");
> -	  gen_exp (next, DEFINE_EXPAND, used, info);
> -	  printf (", %s);\n", last_p ? "false" : "true");
> +	  fprintf (file, "  emit (");
> +	  gen_exp (next, DEFINE_EXPAND, used, info, file);
> +	  fprintf (file, ", %s);\n", last_p ? "false" : "true");
>  	}
>      }
>  }
> @@ -303,27 +304,27 @@ gen_emit_seq (rtvec vec, char *used, md_rtx_info *info)
>     for use in error messages.  */
>  
>  static void
> -emit_c_code (const char *code, bool can_fail_p, const char *name)
> +emit_c_code (const char *code, bool can_fail_p, const char *name, FILE *file)
>  {
>    if (can_fail_p)
> -    printf ("#define FAIL return (end_sequence (), _val)\n");
> +    fprintf (file, "#define FAIL return (end_sequence (), _val)\n");
>    else
> -    printf ("#define FAIL _Pragma (\"GCC error \\\"%s cannot FAIL\\\"\")"
> +    fprintf (file, "#define FAIL _Pragma (\"GCC error \\\"%s cannot FAIL\\\"\")"
>  	    " (void)0\n", name);
> -  printf ("#define DONE return (_val = get_insns (), "
> +  fprintf (file, "#define DONE return (_val = get_insns (), "
>  	  "end_sequence (), _val)\n");
>  
> -  rtx_reader_ptr->print_md_ptr_loc (code);
> -  printf ("%s\n", code);
> +  rtx_reader_ptr->print_md_ptr_loc (code, file);
> +  fprintf (file, "%s\n", code);
>  
> -  printf ("#undef DONE\n");
> -  printf ("#undef FAIL\n");
> +  fprintf (file, "#undef DONE\n");
> +  fprintf (file, "#undef FAIL\n");
>  }
>  
>  /* Generate the `gen_...' function for a DEFINE_INSN.  */
>  
>  static void
> -gen_insn (md_rtx_info *info)
> +gen_insn (md_rtx_info *info, FILE *file)
>  {
>    struct pattern_stats stats;
>    int i;
> @@ -409,7 +410,7 @@ gen_insn (md_rtx_info *info)
>    if (XSTR (insn, 0)[0] == 0 || XSTR (insn, 0)[0] == '*')
>      return;
>  
> -  printf ("/* %s:%d */\n", info->loc.filename, info->loc.lineno);
> +  fprintf (file, "/* %s:%d */\n", info->loc.filename, info->loc.lineno);
>  
>    /* Find out how many operands this function has.  */
>    get_pattern_stats (&stats, XVEC (insn, 1));
> @@ -417,17 +418,17 @@ gen_insn (md_rtx_info *info)
>      fatal_at (info->loc, "match_dup operand number has no match_operand");
>  
>    /* Output the function name and argument declarations.  */
> -  printf ("rtx\ngen_%s (", XSTR (insn, 0));
> +  fprintf (file, "rtx\ngen_%s (", XSTR (insn, 0));
>    if (stats.num_generator_args)
>      for (i = 0; i < stats.num_generator_args; i++)
>        if (i)
> -	printf (",\n\trtx operand%d ATTRIBUTE_UNUSED", i);
> +	fprintf (file, ",\n\trtx operand%d ATTRIBUTE_UNUSED", i);
>        else
> -	printf ("rtx operand%d ATTRIBUTE_UNUSED", i);
> +	fprintf (file, "rtx operand%d ATTRIBUTE_UNUSED", i);
>    else
> -    printf ("void");
> -  printf (")\n");
> -  printf ("{\n");
> +    fprintf (file, "void");
> +  fprintf (file, ")\n");
> +  fprintf (file, "{\n");
>  
>    /* Output code to construct and return the rtl for the instruction body.  */
>  
> @@ -436,16 +437,16 @@ gen_insn (md_rtx_info *info)
>    char *used = (XVECLEN (insn, 1) == 1
>  		? NULL
>  		: XCNEWVEC (char, stats.num_generator_args));
> -  printf ("  return ");
> -  gen_exp (pattern, DEFINE_INSN, used, info);
> -  printf (";\n}\n\n");
> +  fprintf (file, "  return ");
> +  gen_exp (pattern, DEFINE_INSN, used, info, file);
> +  fprintf (file, ";\n}\n\n");
>    XDELETEVEC (used);
>  }
>  
>  /* Generate the `gen_...' function for a DEFINE_EXPAND.  */
>  
>  static void
> -gen_expand (md_rtx_info *info)
> +gen_expand (md_rtx_info *info, FILE *file)
>  {
>    struct pattern_stats stats;
>    int i;
> @@ -466,17 +467,17 @@ gen_expand (md_rtx_info *info)
>  			 "numbers above all other operands", XSTR (expand, 0));
>  
>    /* Output the function name and argument declarations.  */
> -  printf ("rtx\ngen_%s (", XSTR (expand, 0));
> +  fprintf (file, "rtx\ngen_%s (", XSTR (expand, 0));
>    if (stats.num_generator_args)
>      for (i = 0; i < stats.num_generator_args; i++)
>        if (i)
> -	printf (",\n\trtx operand%d", i);
> +	fprintf (file, ",\n\trtx operand%d", i);
>        else
> -	printf ("rtx operand%d", i);
> +	fprintf (file, "rtx operand%d", i);
>    else
> -    printf ("void");
> -  printf (")\n");
> -  printf ("{\n");
> +    fprintf (file, "void");
> +  fprintf (file, ")\n");
> +  fprintf (file, "{\n");
>  
>    /* If we don't have any C code to write, only one insn is being written,
>       and no MATCH_DUPs are present, we can just return the desired insn
> @@ -485,18 +486,18 @@ gen_expand (md_rtx_info *info)
>        && stats.max_opno >= stats.max_dup_opno
>        && XVECLEN (expand, 1) == 1)
>      {
> -      printf ("  return ");
> -      gen_exp (XVECEXP (expand, 1, 0), DEFINE_EXPAND, NULL, info);
> -      printf (";\n}\n\n");
> +      fprintf (file, "  return ");
> +      gen_exp (XVECEXP (expand, 1, 0), DEFINE_EXPAND, NULL, info, file);
> +      fprintf (file, ";\n}\n\n");
>        return;
>      }
>  
>    /* For each operand referred to only with MATCH_DUPs,
>       make a local variable.  */
>    for (i = stats.num_generator_args; i <= stats.max_dup_opno; i++)
> -    printf ("  rtx operand%d;\n", i);
> -  printf ("  rtx_insn *_val = 0;\n");
> -  printf ("  start_sequence ();\n");
> +    fprintf (file, "  rtx operand%d;\n", i);
> +  fprintf (file, "  rtx_insn *_val = 0;\n");
> +  fprintf (file, "  start_sequence ();\n");
>  
>    /* The fourth operand of DEFINE_EXPAND is some code to be executed
>       before the actual construction.
> @@ -506,13 +507,13 @@ gen_expand (md_rtx_info *info)
>       So copy the operand values there before executing it.  */
>    if (XSTR (expand, 3) && *XSTR (expand, 3))
>      {
> -      printf ("  {\n");
> +      fprintf (file, "  {\n");
>        if (stats.num_operand_vars > 0)
> -	printf ("    rtx operands[%d];\n", stats.num_operand_vars);
> +	fprintf (file, "    rtx operands[%d];\n", stats.num_operand_vars);
>  
>        /* Output code to copy the arguments into `operands'.  */
>        for (i = 0; i < stats.num_generator_args; i++)
> -	printf ("    operands[%d] = operand%d;\n", i, i);
> +	fprintf (file, "    operands[%d] = operand%d;\n", i, i);
>  
>        /* Output the special code to be executed before the sequence
>  	 is generated.  */
> @@ -524,7 +525,7 @@ gen_expand (md_rtx_info *info)
>  	  if (nofail_optabs[p.op])
>  	    can_fail_p = false;
>  	}
> -      emit_c_code (XSTR (expand, 3), can_fail_p, XSTR (expand, 0));
> +      emit_c_code (XSTR (expand, 3), can_fail_p, XSTR (expand, 0), file);
>  
>        /* Output code to copy the arguments back out of `operands'
>  	 (unless we aren't going to use them at all).  */
> @@ -532,29 +533,29 @@ gen_expand (md_rtx_info *info)
>  	{
>  	  for (i = 0; i <= MAX (stats.max_opno, stats.max_dup_opno); i++)
>  	    {
> -	      printf ("    operand%d = operands[%d];\n", i, i);
> -	      printf ("    (void) operand%d;\n", i);
> +	      fprintf (file, "    operand%d = operands[%d];\n", i, i);
> +	      fprintf (file, "    (void) operand%d;\n", i);
>  	    }
>  	}
> -      printf ("  }\n");
> +      fprintf (file, "  }\n");
>      }
>  
>    used = XCNEWVEC (char, stats.num_operand_vars);
> -  gen_emit_seq (XVEC (expand, 1), used, info);
> +  gen_emit_seq (XVEC (expand, 1), used, info, file);
>    XDELETEVEC (used);
>  
>    /* Call `get_insns' to extract the list of all the
>       insns emitted within this gen_... function.  */
>  
> -  printf ("  _val = get_insns ();\n");
> -  printf ("  end_sequence ();\n");
> -  printf ("  return _val;\n}\n\n");
> +  fprintf (file, "  _val = get_insns ();\n");
> +  fprintf (file, "  end_sequence ();\n");
> +  fprintf (file, "  return _val;\n}\n\n");
>  }
>  
>  /* Like gen_expand, but generates insns resulting from splitting SPLIT.  */
>  
>  static void
> -gen_split (md_rtx_info *info)
> +gen_split (md_rtx_info *info, FILE *file)
>  {
>    struct pattern_stats stats;
>    int i;
> @@ -580,62 +581,62 @@ gen_split (md_rtx_info *info)
>    /* Output the prototype, function name and argument declarations.  */
>    if (GET_CODE (split) == DEFINE_PEEPHOLE2)
>      {
> -      printf ("extern rtx_insn *gen_%s_%d (rtx_insn *, rtx *);\n",
> +      fprintf (file, "extern rtx_insn *gen_%s_%d (rtx_insn *, rtx *);\n",
>  	      name, info->index);
> -      printf ("rtx_insn *\ngen_%s_%d (rtx_insn *curr_insn ATTRIBUTE_UNUSED,"
> +      fprintf (file, "rtx_insn *\ngen_%s_%d (rtx_insn *curr_insn ATTRIBUTE_UNUSED,"
>  	      " rtx *operands%s)\n",
>  	      name, info->index, unused);
>      }
>    else
>      {
> -      printf ("extern rtx_insn *gen_split_%d (rtx_insn *, rtx *);\n",
> +      fprintf (file, "extern rtx_insn *gen_split_%d (rtx_insn *, rtx *);\n",
>  	      info->index);
> -      printf ("rtx_insn *\ngen_split_%d "
> +      fprintf (file, "rtx_insn *\ngen_split_%d "
>  	      "(rtx_insn *curr_insn ATTRIBUTE_UNUSED, rtx *operands%s)\n",
>  	      info->index, unused);
>      }
> -  printf ("{\n");
> +  fprintf (file, "{\n");
>  
>    /* Declare all local variables.  */
>    for (i = 0; i < stats.num_operand_vars; i++)
> -    printf ("  rtx operand%d;\n", i);
> -  printf ("  rtx_insn *_val = NULL;\n");
> +    fprintf (file, "  rtx operand%d;\n", i);
> +  fprintf (file, "  rtx_insn *_val = NULL;\n");
>  
>    if (GET_CODE (split) == DEFINE_PEEPHOLE2)
> -    output_peephole2_scratches (split);
> +    output_peephole2_scratches (split, file);
>  
>    const char *fn = info->loc.filename;
>    for (const char *p = fn; *p; p++)
>      if (*p == '/')
>        fn = p + 1;
>  
> -  printf ("  if (dump_file)\n");
> -  printf ("    fprintf (dump_file, \"Splitting with gen_%s_%d (%s:%d)\\n\");\n",
> +  fprintf (file, "  if (dump_file)\n");
> +  fprintf (file, "    fprintf (dump_file, \"Splitting with gen_%s_%d (%s:%d)\\n\");\n",
>  	  name, info->index, fn, info->loc.lineno);
>  
> -  printf ("  start_sequence ();\n");
> +  fprintf (file, "  start_sequence ();\n");
>  
>    /* The fourth operand of DEFINE_SPLIT is some code to be executed
>       before the actual construction.  */
>  
>    if (XSTR (split, 3))
> -    emit_c_code (XSTR (split, 3), true, name);
> +    emit_c_code (XSTR (split, 3), true, name, file);
>  
>    /* Output code to copy the arguments back out of `operands'  */
>    for (i = 0; i < stats.num_operand_vars; i++)
>      {
> -      printf ("  operand%d = operands[%d];\n", i, i);
> -      printf ("  (void) operand%d;\n", i);
> +      fprintf (file, "  operand%d = operands[%d];\n", i, i);
> +      fprintf (file, "  (void) operand%d;\n", i);
>      }
>  
> -  gen_emit_seq (XVEC (split, 2), used, info);
> +  gen_emit_seq (XVEC (split, 2), used, info, file);
>  
>    /* Call `get_insns' to make a list of all the
>       insns emitted within this gen_... function.  */
>  
> -  printf ("  _val = get_insns ();\n");
> -  printf ("  end_sequence ();\n");
> -  printf ("  return _val;\n}\n\n");
> +  fprintf (file, "  _val = get_insns ();\n");
> +  fprintf (file, "  end_sequence ();\n");
> +  fprintf (file, "  return _val;\n}\n\n");
>  
>    free (used);
>  }
> @@ -645,37 +646,37 @@ gen_split (md_rtx_info *info)
>     the end of the vector.  */
>  
>  static void
> -output_add_clobbers (md_rtx_info *info)
> +output_add_clobbers (md_rtx_info *info, FILE *file)
>  {
>    struct clobber_pat *clobber;
>    struct clobber_ent *ent;
>    int i;
>  
> -  printf ("\n\nvoid\nadd_clobbers (rtx pattern ATTRIBUTE_UNUSED, int insn_code_number)\n");
> -  printf ("{\n");
> -  printf ("  switch (insn_code_number)\n");
> -  printf ("    {\n");
> +  fprintf (file, "\n\nvoid\nadd_clobbers (rtx pattern ATTRIBUTE_UNUSED, int insn_code_number)\n");
> +  fprintf (file, "{\n");
> +  fprintf (file, "  switch (insn_code_number)\n");
> +  fprintf (file, "    {\n");
>  
>    for (clobber = clobber_list; clobber; clobber = clobber->next)
>      {
>        for (ent = clobber->insns; ent; ent = ent->next)
> -	printf ("    case %d:\n", ent->code_number);
> +	fprintf (file, "    case %d:\n", ent->code_number);
>  
>        for (i = clobber->first_clobber; i < XVECLEN (clobber->pattern, 1); i++)
>  	{
> -	  printf ("      XVECEXP (pattern, 0, %d) = ", i);
> +	  fprintf (file, "      XVECEXP (pattern, 0, %d) = ", i);
>  	  gen_exp (XVECEXP (clobber->pattern, 1, i),
> -		   GET_CODE (clobber->pattern), NULL, info);
> -	  printf (";\n");
> +		   GET_CODE (clobber->pattern), NULL, info, file);
> +	  fprintf (file, ";\n");
>  	}
>  
> -      printf ("      break;\n\n");
> +      fprintf (file, "      break;\n\n");
>      }
>  
> -  printf ("    default:\n");
> -  printf ("      gcc_unreachable ();\n");
> -  printf ("    }\n");
> -  printf ("}\n");
> +  fprintf (file, "    default:\n");
> +  fprintf (file, "      gcc_unreachable ();\n");
> +  fprintf (file, "    }\n");
> +  fprintf (file, "}\n");
>  }
>  
>  /* Write a function, `added_clobbers_hard_reg_p' that is given an insn_code
> @@ -684,17 +685,17 @@ output_add_clobbers (md_rtx_info *info)
>     SCRATCH.  */
>  
>  static void
> -output_added_clobbers_hard_reg_p (void)
> +output_added_clobbers_hard_reg_p (FILE *file)
>  {
>    struct clobber_pat *clobber;
>    struct clobber_ent *ent;
>    int clobber_p;
>    bool used;
>  
> -  printf ("\n\nbool\nadded_clobbers_hard_reg_p (int insn_code_number)\n");
> -  printf ("{\n");
> -  printf ("  switch (insn_code_number)\n");
> -  printf ("    {\n");
> +  fprintf (file, "\n\nbool\nadded_clobbers_hard_reg_p (int insn_code_number)\n");
> +  fprintf (file, "{\n");
> +  fprintf (file, "  switch (insn_code_number)\n");
> +  fprintf (file, "    {\n");
>  
>    for (clobber_p = 0; clobber_p <= 1; clobber_p++)
>      {
> @@ -703,25 +704,25 @@ output_added_clobbers_hard_reg_p (void)
>  	if (clobber->has_hard_reg == clobber_p)
>  	  for (ent = clobber->insns; ent; ent = ent->next)
>  	    {
> -	      printf ("    case %d:\n", ent->code_number);
> +	      fprintf (file, "    case %d:\n", ent->code_number);
>  	      used = true;
>  	    }
>  
>        if (used)
> -	printf ("      return %s;\n\n", clobber_p ? "true" : "false");
> +	fprintf (file, "      return %s;\n\n", clobber_p ? "true" : "false");
>      }
>  
> -  printf ("    default:\n");
> -  printf ("      gcc_unreachable ();\n");
> -  printf ("    }\n");
> -  printf ("}\n");
> +  fprintf (file, "    default:\n");
> +  fprintf (file, "      gcc_unreachable ();\n");
> +  fprintf (file, "    }\n");
> +  fprintf (file, "}\n");
>  }
>  
>  /* Generate code to invoke find_free_register () as needed for the
>     scratch registers used by the peephole2 pattern in SPLIT.  */
>  
>  static void
> -output_peephole2_scratches (rtx split)
> +output_peephole2_scratches (rtx split, FILE *file)
>  {
>    int i;
>    int insn_nr = 0;
> @@ -746,12 +747,12 @@ output_peephole2_scratches (rtx split)
>  
>  	  if (first)
>  	    {
> -	      printf ("  HARD_REG_SET _regs_allocated;\n");
> -	      printf ("  CLEAR_HARD_REG_SET (_regs_allocated);\n");
> +	      fprintf (file, "  HARD_REG_SET _regs_allocated;\n");
> +	      fprintf (file, "  CLEAR_HARD_REG_SET (_regs_allocated);\n");
>  	      first = false;
>  	    }
>  
> -	  printf ("  if ((operands[%d] = peep2_find_free_register (%d, %d, \"%s\", %smode, &_regs_allocated)) == NULL_RTX)\n\
> +	  fprintf (file, "  if ((operands[%d] = peep2_find_free_register (%d, %d, \"%s\", %smode, &_regs_allocated)) == NULL_RTX)\n\
>      return NULL;\n",
>  		  XINT (elt, 0),
>  		  insn_nr, last_insn_nr,
> @@ -767,50 +768,50 @@ output_peephole2_scratches (rtx split)
>  /* Print "arg<N>" parameter declarations for each argument N of ONAME.  */
>  
>  static void
> -print_overload_arguments (overloaded_name *oname)
> +print_overload_arguments (overloaded_name *oname, FILE *file)
>  {
>    for (unsigned int i = 0; i < oname->arg_types.length (); ++i)
> -    printf ("%s%s arg%d", i == 0 ? "" : ", ", oname->arg_types[i], i);
> +    fprintf (file, "%s%s arg%d", i == 0 ? "" : ", ", oname->arg_types[i], i);
>  }
>  
>  /* Print code to test whether INSTANCE should be chosen, given that
>     argument N of the overload is available as "arg<N>".  */
>  
>  static void
> -print_overload_test (overloaded_instance *instance)
> +print_overload_test (overloaded_instance *instance, FILE *file)
>  {
>    for (unsigned int i = 0; i < instance->arg_values.length (); ++i)
> -    printf ("%sarg%d == %s", i == 0 ? "  if (" : "\n      && ",
> +    fprintf (file, "%sarg%d == %s", i == 0 ? "  if (" : "\n      && ",
>  	    i, instance->arg_values[i]);
> -  printf (")\n");
> +  fprintf (file, ")\n");
>  }
>  
>  /* Emit a maybe_code_for_* function for ONAME.  */
>  
>  static void
> -handle_overloaded_code_for (overloaded_name *oname)
> +handle_overloaded_code_for (overloaded_name *oname, FILE *file)
>  {
>    /* Print the function prototype.  */
> -  printf ("\ninsn_code\nmaybe_code_for_%s (", oname->name);
> -  print_overload_arguments (oname);
> -  printf (")\n{\n");
> +  fprintf (file, "\ninsn_code\nmaybe_code_for_%s (", oname->name);
> +  print_overload_arguments (oname, file);
> +  fprintf (file, ")\n{\n");
>  
>    /* Use a sequence of "if" statements for each instance.  */
>    for (overloaded_instance *instance = oname->first_instance;
>         instance; instance = instance->next)
>      {
> -      print_overload_test (instance);
> -      printf ("    return CODE_FOR_%s;\n", instance->name);
> +      print_overload_test (instance, file);
> +      fprintf (file, "    return CODE_FOR_%s;\n", instance->name);
>      }
>  
>    /* Return null if no match was found.  */
> -  printf ("  return CODE_FOR_nothing;\n}\n");
> +  fprintf (file, "  return CODE_FOR_nothing;\n}\n");
>  }
>  
>  /* Emit a maybe_gen_* function for ONAME.  */
>  
>  static void
> -handle_overloaded_gen (overloaded_name *oname)
> +handle_overloaded_gen (overloaded_name *oname, FILE *file)
>  {
>    unsigned HOST_WIDE_INT seen = 0;
>    /* All patterns must have the same number of operands.  */
> @@ -827,25 +828,25 @@ handle_overloaded_gen (overloaded_name *oname)
>        seen |= mask;
>  
>        /* Print the function prototype.  */
> -      printf ("\nrtx\nmaybe_gen_%s (", oname->name);
> -      print_overload_arguments (oname);
> +      fprintf (file, "\nrtx\nmaybe_gen_%s (", oname->name);
> +      print_overload_arguments (oname, file);
>        for (int i = 0; i < stats.num_generator_args; ++i)
> -	printf (", rtx x%d", i);
> -      printf (")\n{\n");
> +	fprintf (file, ", rtx x%d", i);
> +      fprintf (file, ")\n{\n");
>  
>        /* Use maybe_code_for_*, instead of duplicating the selection
>  	 logic here.  */
> -      printf ("  insn_code code = maybe_code_for_%s (", oname->name);
> +      fprintf (file, "  insn_code code = maybe_code_for_%s (", oname->name);
>        for (unsigned int i = 0; i < oname->arg_types.length (); ++i)
> -	printf ("%sarg%d", i == 0 ? "" : ", ", i);
> -      printf (");\n"
> +	fprintf (file, "%sarg%d", i == 0 ? "" : ", ", i);
> +      fprintf (file, ");\n"
>  	      "  if (code != CODE_FOR_nothing)\n"
>  	      "    {\n"
>  	      "      gcc_assert (insn_data[code].n_generator_args == %d);\n"
>  	      "      return GEN_FCN (code) (", stats.num_generator_args);
>        for (int i = 0; i < stats.num_generator_args; ++i)
> -	printf ("%sx%d", i == 0 ? "" : ", ", i);
> -      printf (");\n"
> +	fprintf (file, "%sx%d", i == 0 ? "" : ", ", i);
> +      fprintf (file, ");\n"
>  	      "    }\n"
>  	      "  else\n"
>  	      "    return NULL_RTX;\n"
> @@ -853,12 +854,68 @@ handle_overloaded_gen (overloaded_name *oname)
>      }
>  }
>  
> +void
> +print_header (FILE *file)
> +{
> +  fprintf (file, "/* Generated automatically by the program `genemit'\n\
> +from the machine description file `md'.  */\n\n");
> +
> +  fprintf (file, "#define IN_TARGET_CODE 1\n");
> +  fprintf (file, "#include \"config.h\"\n");
> +  fprintf (file, "#include \"system.h\"\n");
> +  fprintf (file, "#include \"coretypes.h\"\n");
> +  fprintf (file, "#include \"backend.h\"\n");
> +  fprintf (file, "#include \"predict.h\"\n");
> +  fprintf (file, "#include \"tree.h\"\n");
> +  fprintf (file, "#include \"rtl.h\"\n");
> +  fprintf (file, "#include \"alias.h\"\n");
> +  fprintf (file, "#include \"varasm.h\"\n");
> +  fprintf (file, "#include \"stor-layout.h\"\n");
> +  fprintf (file, "#include \"calls.h\"\n");
> +  fprintf (file, "#include \"memmodel.h\"\n");
> +  fprintf (file, "#include \"tm_p.h\"\n");
> +  fprintf (file, "#include \"flags.h\"\n");
> +  fprintf (file, "#include \"insn-config.h\"\n");
> +  fprintf (file, "#include \"expmed.h\"\n");
> +  fprintf (file, "#include \"dojump.h\"\n");
> +  fprintf (file, "#include \"explow.h\"\n");
> +  fprintf (file, "#include \"emit-rtl.h\"\n");
> +  fprintf (file, "#include \"stmt.h\"\n");
> +  fprintf (file, "#include \"expr.h\"\n");
> +  fprintf (file, "#include \"insn-codes.h\"\n");
> +  fprintf (file, "#include \"optabs.h\"\n");
> +  fprintf (file, "#include \"dfp.h\"\n");
> +  fprintf (file, "#include \"output.h\"\n");
> +  fprintf (file, "#include \"recog.h\"\n");
> +  fprintf (file, "#include \"df.h\"\n");
> +  fprintf (file, "#include \"resource.h\"\n");
> +  fprintf (file, "#include \"reload.h\"\n");
> +  fprintf (file, "#include \"diagnostic-core.h\"\n");
> +  fprintf (file, "#include \"regs.h\"\n");
> +  fprintf (file, "#include \"tm-constrs.h\"\n");
> +  fprintf (file, "#include \"ggc.h\"\n");
> +  fprintf (file, "#include \"target.h\"\n\n");
> +}
> +
> +auto_vec<const char *, 10> output_files;
> +
> +static bool
> +handle_arg (const char *arg)
> +{
> +  if (arg[1] == 'O')
> +    {
> +      output_files.safe_push (&arg[2]);
> +      return true;
> +    }
> +  return false;
> +}
> +
>  int
>  main (int argc, const char **argv)
>  {
>    progname = "genemit";
>  
> -  if (!init_rtx_reader_args (argc, argv))
> +  if (!init_rtx_reader_args_cb (argc, argv, handle_arg))
>      return (FATAL_EXIT_CODE);
>  
>  #define DEF_INTERNAL_OPTAB_FN(NAME, FLAGS, OPTAB, TYPE) \
> @@ -868,86 +925,81 @@ main (int argc, const char **argv)
>    /* Assign sequential codes to all entries in the machine description
>       in parallel with the tables in insn-output.cc.  */
>  
> -  printf ("/* Generated automatically by the program `genemit'\n\
> -from the machine description file `md'.  */\n\n");
> +  int npatterns = count_patterns ();
> +  md_rtx_info info;
>  
> -  printf ("#define IN_TARGET_CODE 1\n");
> -  printf ("#include \"config.h\"\n");
> -  printf ("#include \"system.h\"\n");
> -  printf ("#include \"coretypes.h\"\n");
> -  printf ("#include \"backend.h\"\n");
> -  printf ("#include \"predict.h\"\n");
> -  printf ("#include \"tree.h\"\n");
> -  printf ("#include \"rtl.h\"\n");
> -  printf ("#include \"alias.h\"\n");
> -  printf ("#include \"varasm.h\"\n");
> -  printf ("#include \"stor-layout.h\"\n");
> -  printf ("#include \"calls.h\"\n");
> -  printf ("#include \"memmodel.h\"\n");
> -  printf ("#include \"tm_p.h\"\n");
> -  printf ("#include \"flags.h\"\n");
> -  printf ("#include \"insn-config.h\"\n");
> -  printf ("#include \"expmed.h\"\n");
> -  printf ("#include \"dojump.h\"\n");
> -  printf ("#include \"explow.h\"\n");
> -  printf ("#include \"emit-rtl.h\"\n");
> -  printf ("#include \"stmt.h\"\n");
> -  printf ("#include \"expr.h\"\n");
> -  printf ("#include \"insn-codes.h\"\n");
> -  printf ("#include \"optabs.h\"\n");
> -  printf ("#include \"dfp.h\"\n");
> -  printf ("#include \"output.h\"\n");
> -  printf ("#include \"recog.h\"\n");
> -  printf ("#include \"df.h\"\n");
> -  printf ("#include \"resource.h\"\n");
> -  printf ("#include \"reload.h\"\n");
> -  printf ("#include \"diagnostic-core.h\"\n");
> -  printf ("#include \"regs.h\"\n");
> -  printf ("#include \"tm-constrs.h\"\n");
> -  printf ("#include \"ggc.h\"\n");
> -  printf ("#include \"target.h\"\n\n");
> +  int npatterns_per_file = npatterns;
> +  if (!output_files.is_empty ())
> +    npatterns_per_file = npatterns / output_files.length () + 1;
>  
> -  /* Read the machine description.  */
> +  gcc_assert (npatterns_per_file > 1);
>  
> -  md_rtx_info info;
> +  /* Reverse so we can pop the first-added element.  */
> +  output_files.reverse ();
> +
> +  int count = 0;
> +  FILE *file = NULL;
> +
> +  /* Read the machine description.  */
>    while (read_md_rtx (&info))
> -    switch (GET_CODE (info.def))
> -      {
> -      case DEFINE_INSN:
> -	gen_insn (&info);
> -	break;
> +    {
> +      if (count == 0 || count == npatterns_per_file)
> +	{
> +	  if (file)
> +	    if (fclose (file) != 0)
> +	      return FATAL_EXIT_CODE;
>  
> -      case DEFINE_EXPAND:
> -	printf ("/* %s:%d */\n", info.loc.filename, info.loc.lineno);
> -	gen_expand (&info);
> -	break;
> +	  if (!output_files.is_empty ())
> +	    {
> +	      const char *const filename = output_files.pop ();
> +	      file = fopen (filename, "w");
> +	    }
> +	  else
> +	    file = stdout;
>  
> -      case DEFINE_SPLIT:
> -	printf ("/* %s:%d */\n", info.loc.filename, info.loc.lineno);
> -	gen_split (&info);
> -	break;
> +	  print_header (file);
> +	  count = 0;
> +	}
>  
> -      case DEFINE_PEEPHOLE2:
> -	printf ("/* %s:%d */\n", info.loc.filename, info.loc.lineno);
> -	gen_split (&info);
> -	break;
> +      switch (GET_CODE (info.def))
> +	{
> +	case DEFINE_INSN:
> +	  gen_insn (&info, file);
> +	  break;
>  
> -      default:
> -	break;
> -      }
> +	case DEFINE_EXPAND:
> +	  fprintf (file, "/* %s:%d */\n", info.loc.filename, info.loc.lineno);
> +	  gen_expand (&info, file);
> +	  break;
> +
> +	case DEFINE_SPLIT:
> +	  fprintf (file, "/* %s:%d */\n", info.loc.filename, info.loc.lineno);
> +	  gen_split (&info, file);
> +	  break;
> +
> +	case DEFINE_PEEPHOLE2:
> +	  fprintf (file, "/* %s:%d */\n", info.loc.filename, info.loc.lineno);
> +	  gen_split (&info, file);
> +	  break;
> +
> +	default:
> +	  break;
> +	}
> +
> +      count++;
> +    }
>  
>    /* Write out the routines to add CLOBBERs to a pattern and say whether they
>       clobber a hard reg.  */
> -  output_add_clobbers (&info);
> -  output_added_clobbers_hard_reg_p ();
> +  output_add_clobbers (&info, file);
> +  output_added_clobbers_hard_reg_p (file);
>  
>    for (overloaded_name *oname = rtx_reader_ptr->get_overloads ();
>         oname; oname = oname->next)
>      {
> -      handle_overloaded_code_for (oname);
> -      handle_overloaded_gen (oname);
> +      handle_overloaded_code_for (oname, file);
> +      handle_overloaded_gen (oname, file);
>      }
>  
> -  fflush (stdout);
> -  return (ferror (stdout) != 0 ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE);
> +  return (fclose (file) != 0 ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE);
>  }
> diff --git a/gcc/gensupport.cc b/gcc/gensupport.cc
> index dd920d673b4..367ba1a30a4 100644
> --- a/gcc/gensupport.cc
> +++ b/gcc/gensupport.cc
> @@ -3131,6 +3131,42 @@ init_rtx_reader_args (int argc, const char **argv)
>    return init_rtx_reader_args_cb (argc, argv, 0);
>  }
>  
> +/* Count the number of patterns in all queues and return the count.  */
> +int
> +count_patterns ()
> +{
> +  int count = 0;
> +  class queue_elem *cur = define_attr_queue;
> +  while (cur != NULL)
> +    {
> +      count++;
> +      cur = cur->next;
> +    }
> +
> +  cur = define_pred_queue;
> +  while (cur != NULL)
> +    {
> +      count++;
> +      cur = cur->next;
> +    }
> +
> +  cur = define_insn_queue;
> +  while (cur != NULL)
> +    {
> +      count++;
> +      cur = cur->next;
> +    }
> +
> +  cur = other_queue;
> +  while (cur != NULL)
> +    {
> +      count++;
> +      cur = cur->next;
> +    }
> +
> +  return count;
> +}
> +
>  /* Try to read a single rtx from the file.  Return true on success,
>     describing it in *INFO.  */
>  
> diff --git a/gcc/gensupport.h b/gcc/gensupport.h
> index 7925e22ed41..7396118714b 100644
> --- a/gcc/gensupport.h
> +++ b/gcc/gensupport.h
> @@ -130,6 +130,7 @@ extern rtx add_implicit_parallel (rtvec);
>  extern rtx_reader *init_rtx_reader_args_cb (int, const char **,
>  					    bool (*)(const char *));
>  extern rtx_reader *init_rtx_reader_args (int, const char **);
> +extern int count_patterns ();
>  extern bool read_md_rtx (md_rtx_info *);
>  extern unsigned int get_num_insn_codes ();
>  
> diff --git a/gcc/read-md.cc b/gcc/read-md.cc
> index fd38818e3a3..46ab9065e3e 100644
> --- a/gcc/read-md.cc
> +++ b/gcc/read-md.cc
> @@ -132,9 +132,9 @@ md_reader::fprint_md_ptr_loc (FILE *outf, const void *ptr)
>  
>  /* Special fprint_md_ptr_loc for writing to STDOUT.  */
>  void
> -md_reader::print_md_ptr_loc (const void *ptr)
> +md_reader::print_md_ptr_loc (const void *ptr, FILE *file)
>  {
> -  fprint_md_ptr_loc (stdout, ptr);
> +  fprint_md_ptr_loc (file, ptr);
>  }
>  
>  /* Return a condition that satisfies both COND1 and COND2.  Either string
> diff --git a/gcc/read-md.h b/gcc/read-md.h
> index b309c9c3deb..2adcb58478f 100644
> --- a/gcc/read-md.h
> +++ b/gcc/read-md.h
> @@ -194,7 +194,7 @@ class md_reader
>    const struct ptr_loc *get_md_ptr_loc (const void *ptr);
>    void copy_md_ptr_loc (const void *new_ptr, const void *old_ptr);
>    void fprint_md_ptr_loc (FILE *outf, const void *ptr);
> -  void print_md_ptr_loc (const void *ptr);
> +  void print_md_ptr_loc (const void *ptr, FILE * = stdout);
>  
>    struct enum_type *lookup_enum_type (const char *name);
>    void traverse_enum_types (htab_trav callback, void *info);
Sam James Oct. 17, 2023, 6:01 a.m. UTC | #7
Robin Dapp <rdapp.gcc@gmail.com> writes:

> Hi,
>
> the attached v2 includes Tamar's suggestion of keeping the current
> stdout behavior.  When no output files are passed (via -O) the output
> is written to stdout as before.
>
> Tamar also mentioned off-list that, similar to match.pd, it might make
> sense to balance the partitions in a better way than a fixed number
> of patterns threshold.  That's a good idea but I'd rather do that
> separately as the current approach already helps considerably.
>
> Attached v2 was bootstrapped and regtested on power10, aarch64 and
> x86 are still running.
> Stefan also tested v1 on s390 where the partitioning does not help
> but also doesn't slow anything down.  insn-emit.cc isn't very large
> to begin with on s390.
>
> Regards
>  Robin
>
> From 34d05113a4e3c7e83a4731020307e26c1144af69 Mon Sep 17 00:00:00 2001
> From: Robin Dapp <rdapp@ventanamicro.com>
> Date: Thu, 12 Oct 2023 11:23:26 +0200
> Subject: [PATCH v2] genemit: Split insn-emit.cc into several partitions.
>
> On riscv insn-emit.cc has grown to over 1.2 mio lines of code and
> compiling it takes considerable time.
> Therefore, this patch adjust genemit to create several partitions
> (insn-emit-1.cc to insn-emit-n.cc).  In order to do so it first counts
> the number of available patterns, calculates the number of patterns per
> file and starts a new file whenever that number is reached.
>
> Similar to match.pd a configure option --with-emitinsn-partitions=num
> is introduced that makes the number of partition configurable.
>

Natively, things seem fine, but for cross, I get failures on a few
targets (hppa2.0-unknown-linux-gnu, hppa64-unknown-linux-gnu).

With ./configure --host=x86_64-pc-linux-gnu
--target=hppa2.0-unknown-linux-gnu --build=x86_64-pc-linux-gnu && make
-j$(nproc), I get a bunch of stuff like:

mv: cannot stat 'tmp-emit-9.cc': No such file or directory
echo timestamp > s-insn-emit-8
mv: cannot stat 'tmp-emit-10.cc': No such file or directory
make[2]: *** [Makefile:2598: s-insn-emit-9] Error 1
make[2]: *** Waiting for unfinished jobs....
make[2]: *** [Makefile:2598: s-insn-emit-10] Error 1
Robin Dapp Oct. 17, 2023, 7:04 a.m. UTC | #8
> Natively, things seem fine, but for cross, I get failures on a few
> targets (hppa2.0-unknown-linux-gnu, hppa64-unknown-linux-gnu).
> 
> With ./configure --host=x86_64-pc-linux-gnu
> --target=hppa2.0-unknown-linux-gnu --build=x86_64-pc-linux-gnu && make
> -j$(nproc), I get a bunch of stuff like:
> 
> mv: cannot stat 'tmp-emit-9.cc': No such file or directory
> echo timestamp > s-insn-emit-8
> mv: cannot stat 'tmp-emit-10.cc': No such file or directory
> make[2]: *** [Makefile:2598: s-insn-emit-9] Error 1
> make[2]: *** Waiting for unfinished jobs....
> make[2]: *** [Makefile:2598: s-insn-emit-10] Error 1

Thanks.  I presume this is not a native vs cross problem (as I have
been building crosses with it for some days now) but rather a "race"
i.e. missing dependency on the individual output files.  Need to
re-check this.

Regards
 Robin
Jeff Law Oct. 19, 2023, 3:24 p.m. UTC | #9
On 10/17/23 01:04, Robin Dapp wrote:
>> Natively, things seem fine, but for cross, I get failures on a few
>> targets (hppa2.0-unknown-linux-gnu, hppa64-unknown-linux-gnu).
>>
>> With ./configure --host=x86_64-pc-linux-gnu
>> --target=hppa2.0-unknown-linux-gnu --build=x86_64-pc-linux-gnu && make
>> -j$(nproc), I get a bunch of stuff like:
>>
>> mv: cannot stat 'tmp-emit-9.cc': No such file or directory
>> echo timestamp > s-insn-emit-8
>> mv: cannot stat 'tmp-emit-10.cc': No such file or directory
>> make[2]: *** [Makefile:2598: s-insn-emit-9] Error 1
>> make[2]: *** Waiting for unfinished jobs....
>> make[2]: *** [Makefile:2598: s-insn-emit-10] Error 1
> 
> Thanks.  I presume this is not a native vs cross problem (as I have
> been building crosses with it for some days now) but rather a "race"
> i.e. missing dependency on the individual output files.  Need to
> re-check this.
Yea, that's almost certainly a missed dependency.

I'm ready to throw this into the tester once you've got the dependency 
issue resolved.

It's been running behind the last several weeks until I did some 
revamping of how jobs are fired off to make it more efficient.  It seems 
to be keeping up now.

Jeff
Robin Dapp Oct. 27, 2023, 7:04 p.m. UTC | #10
After working with Sam off-list (thanks) I managed to get hppa to
build.  Initially it looked as if hppa just had a very small number of
instruction patterns so we wouldn't generate all 10 output files.
However, the actual issue (which we will only hit with a low
pattern count) was with counting all the patterns vs only counting
the patterns that will be output.  A wrong pattern count lead to
prematurely stopping to write output files.

With that corrected, hppa "just works" until I hit linker errors
due to relocations - most likely unrelated:

bin/ld: unwind-dw2-fde-dip_s.o(.data.rel.ro+0): cannot handle
R_PARISC_FPTR64 for __pthread_key_create@@GLIBC_2.34

Attached is v3 that has been bootstrapped and tested on x86 and power10,
aarch64 bootstrap was ok, testsuite is still running.  A riscv build and
testsuite run was successful as well.

Regards
 Robin

From 248744c328440bff9cc339d2bf622852cbaac343 Mon Sep 17 00:00:00 2001
From: Robin Dapp <rdapp@ventanamicro.com>
Date: Thu, 12 Oct 2023 11:23:26 +0200
Subject: [PATCH v3] genemit: Split insn-emit.cc into several partitions.

On riscv insn-emit.cc has grown to over 1.2 mio lines of code and
compiling it takes considerable time.
Therefore, this patch adjust genemit to create several partitions
(insn-emit-1.cc to insn-emit-n.cc).  The available patterns are
written to the given files in a sequential fashion.

Similar to match.pd a configure option --with-emitinsn-partitions=num
is introduced that makes the number of partition configurable.

gcc/ChangeLog:

	PR bootstrap/84402
	PR target/111600

	* Makefile.in: Handle split insn-emit.cc.
	* configure: Regenerate.
	* configure.ac: Add --with-insnemit-partitions.
	* genemit.cc (output_peephole2_scratches): Print to file instead
	of stdout.
	(print_code): Ditto.
	(gen_rtx_scratch): Ditto.
	(gen_exp): Ditto.
	(gen_emit_seq): Ditto.
	(emit_c_code): Ditto.
	(gen_insn): Ditto.
	(gen_expand): Ditto.
	(gen_split): Ditto.
	(output_add_clobbers): Ditto.
	(output_added_clobbers_hard_reg_p): Ditto.
	(print_overload_arguments): Ditto.
	(print_overload_test): Ditto.
	(handle_overloaded_code_for): Ditto.
	(handle_overloaded_gen): Ditto.
	(print_header): New function.
	(handle_arg): New function.
	(main): Split output into 10 files.
	* gensupport.cc (count_patterns): New function.
	* gensupport.h (count_patterns): Define.
	* read-md.cc (md_reader::print_md_ptr_loc): Add file argument.
	* read-md.h (class md_reader): Change definition.
---
 gcc/Makefile.in   |  36 ++-
 gcc/configure     |  24 +-
 gcc/configure.ac  |  13 ++
 gcc/genemit.cc    | 542 +++++++++++++++++++++++++---------------------
 gcc/gensupport.cc |  55 +++++
 gcc/gensupport.h  |   1 +
 gcc/read-md.cc    |   4 +-
 gcc/read-md.h     |   2 +-
 8 files changed, 422 insertions(+), 255 deletions(-)

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 91d6bfbea4d..d8bfad8de15 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -236,6 +236,13 @@ GIMPLE_MATCH_PD_SEQ_O = $(patsubst %, gimple-match-%.o, $(MATCH_SPLITS_SEQ))
 GENERIC_MATCH_PD_SEQ_SRC = $(patsubst %, generic-match-%.cc, $(MATCH_SPLITS_SEQ))
 GENERIC_MATCH_PD_SEQ_O = $(patsubst %, generic-match-%.o, $(MATCH_SPLITS_SEQ))
 
+# The number of splits to be made for the insn-emit files.
+NUM_INSNEMIT_SPLITS = @DEFAULT_INSNEMIT_PARTITIONS@
+INSNEMIT_SPLITS_SEQ = $(wordlist 1,$(NUM_INSNEMIT_SPLITS),$(one_to_9999))
+INSNEMIT_SEQ_SRC = $(patsubst %, insn-emit-%.cc, $(INSNEMIT_SPLITS_SEQ))
+INSNEMIT_SEQ_TMP = $(patsubst %, tmp-emit-%.cc, $(INSNEMIT_SPLITS_SEQ))
+INSNEMIT_SEQ_O = $(patsubst %, insn-emit-%.o, $(INSNEMIT_SPLITS_SEQ))
+
 # These files are to have specific diagnostics suppressed, or are not to
 # be subject to -Werror:
 # flex output may yield harmless "no previous prototype" warnings
@@ -1356,7 +1363,7 @@ OBJS = \
 	insn-attrtab.o \
 	insn-automata.o \
 	insn-dfatab.o \
-	insn-emit.o \
+	$(INSNEMIT_SEQ_O) \
 	insn-extract.o \
 	insn-latencytab.o \
 	insn-modes.o \
@@ -1857,7 +1864,8 @@ TREECHECKING = @TREECHECKING@
 FULL_DRIVER_NAME=$(target_noncanonical)-gcc-$(version)$(exeext)
 
 MOSTLYCLEANFILES = insn-flags.h insn-config.h insn-codes.h \
- insn-output.cc insn-recog.cc insn-emit.cc insn-extract.cc insn-peep.cc \
+ insn-output.cc insn-recog.cc $(INSNEMIT_SEQ_SRC) \
+ insn-extract.cc insn-peep.cc \
  insn-attr.h insn-attr-common.h insn-attrtab.cc insn-dfatab.cc \
  insn-latencytab.cc insn-opinit.cc insn-opinit.h insn-preds.cc insn-constants.h \
  tm-preds.h tm-constrs.h checksum-options $(GIMPLE_MATCH_PD_SEQ_SRC) \
@@ -2489,11 +2497,11 @@ $(common_out_object_file): $(common_out_file)
 # and compile them.
 
 .PRECIOUS: insn-config.h insn-flags.h insn-codes.h insn-constants.h \
-  insn-emit.cc insn-recog.cc insn-extract.cc insn-output.cc insn-peep.cc \
-  insn-attr.h insn-attr-common.h insn-attrtab.cc insn-dfatab.cc \
-  insn-latencytab.cc insn-preds.cc $(GIMPLE_MATCH_PD_SEQ_SRC) \
-  $(GENERIC_MATCH_PD_SEQ_SRC) gimple-match-auto.h generic-match-auto.h \
-  insn-target-def.h
+  $(INSNEMIT_SEQ_SRC) insn-recog.cc insn-extract.cc insn-output.cc \
+  insn-peep.cc insn-attr.h insn-attr-common.h insn-attrtab.cc \
+  insn-dfatab.cc insn-latencytab.cc insn-preds.cc \
+  $(GIMPLE_MATCH_PD_SEQ_SRC) $(GENERIC_MATCH_PD_SEQ_SRC) \
+  gimple-match-auto.h generic-match-auto.h insn-target-def.h
 
 # Dependencies for the md file.  The first time through, we just assume
 # the md file itself and the generated dependency file (in order to get
@@ -2516,7 +2524,7 @@ s-mddeps: $(md_file) $(MD_INCLUDES) build/genmddeps$(build_exeext)
 simple_rtl_generated_h	= insn-attr.h insn-attr-common.h insn-codes.h \
 			  insn-config.h insn-flags.h insn-target-def.h
 
-simple_rtl_generated_c	= insn-automata.cc insn-emit.cc \
+simple_rtl_generated_c	= insn-automata.cc \
 			  insn-extract.cc insn-output.cc \
 			  insn-peep.cc insn-recog.cc
 
@@ -2545,8 +2553,20 @@ $(simple_generated_c:insn-%.cc=s-%): s-%: build/gen%$(build_exeext)
 	$(SHELL) $(srcdir)/../move-if-change tmp-$*.cc insn-$*.cc
 	$(STAMP) s-$*
 
+# genemit splits its output into different files and doesn't write to
+# stdout. (but rather to tmp-emit-01.cc..tmp-emit-10.cc)
+$(INSNEMIT_SEQ_SRC): s-tmp-emit; @true
+s-tmp-emit: build/genemit$(build_exeext) $(MD_DEPS) insn-conditions.md
+	$(RUN_GEN) build/genemit$(build_exeext) $(md_file) insn-conditions.md \
+	  $(addprefix -O,${INSNEMIT_SEQ_TMP})
+	$(foreach id, $(INSNEMIT_SPLITS_SEQ), \
+	  $(SHELL) $(srcdir)/../move-if-change tmp-emit-$(id).cc \
+	  insn-emit-$(id).cc;)
+	$(STAMP) s-tmp-emit
+
 # gencheck doesn't read the machine description, and the file produced
 # doesn't use the insn-* convention.
+
 tree-check.h: s-check ; @true
 s-check : build/gencheck$(build_exeext)
 	$(RUN_GEN) build/gencheck$(build_exeext) > tmp-check.h
diff --git a/gcc/configure b/gcc/configure
index 77f33ee4df6..d4ad988000f 100755
--- a/gcc/configure
+++ b/gcc/configure
@@ -844,6 +844,7 @@ enable_gcov
 enable_shared
 enable_fixed_point
 enable_decimal_float
+DEFAULT_INSNEMIT_PARTITIONS
 DEFAULT_MATCHPD_PARTITIONS
 with_float
 with_cpu
@@ -973,6 +974,7 @@ enable_multilib
 enable_multiarch
 with_stack_clash_protection_guard_size
 with_matchpd_partitions
+with_insnemit_partitions
 enable___cxa_atexit
 enable_decimal_float
 enable_fixed_point
@@ -1846,6 +1848,9 @@ Optional Packages:
   --with-matchpd-partitions=num
                           Set the number of partitions to make for gimple and
                           generic when splitting match.pd. [default=10]
+  --with-insnemit-partitions=num
+                          Set the number of partitions of insn-emit.cc for
+                          genemit to create. [default=10]
   --with-dwarf2           force the default debug format to be DWARF 2 (or
                           later)
   --with-specs=SPECS      add SPECS to driver command-line processing
@@ -7948,6 +7953,21 @@ fi
 
 
 
+# Specify the number of splits of insn-emit.cc to generate.
+
+# Check whether --with-insnemit-partitions was given.
+if test "${with_insnemit_partitions+set}" = set; then :
+  withval=$with_insnemit_partitions; DEFAULT_INSNEMIT_PARTITIONS="$with_insnemit_partitions"
+else
+  DEFAULT_INSNEMIT_PARTITIONS=10
+fi
+
+if (test $DEFAULT_INSNEMIT_PARTITIONS -lt 1); then
+  as_fn_error $? "Invalid value $DEFAULT_INSNEMIT_PARTITIONS for --with-insnemit-partitions. Cannot be negative." "$LINENO" 5
+fi
+
+
+
 # Enable __cxa_atexit for C++.
 # Check whether --enable-__cxa_atexit was given.
 if test "${enable___cxa_atexit+set}" = set; then :
@@ -19980,7 +20000,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 19983 "configure"
+#line 19995 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -20086,7 +20106,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 20089 "configure"
+#line 20101 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
diff --git a/gcc/configure.ac b/gcc/configure.ac
index 10982cdfc09..dc8cb6a33de 100644
--- a/gcc/configure.ac
+++ b/gcc/configure.ac
@@ -956,6 +956,19 @@ fi
 
 AC_SUBST(DEFAULT_MATCHPD_PARTITIONS)
 
+# Specify the number of splits of insn-emit.cc to generate.
+AC_ARG_WITH(insnemit-partitions,
+[AS_HELP_STRING([--with-insnemit-partitions=num],
+[Set the number of partitions of insn-emit.cc for genemit to create. [default=10]])],
+[DEFAULT_INSNEMIT_PARTITIONS="$with_insnemit_partitions"], [DEFAULT_INSNEMIT_PARTITIONS=10])
+if (test $DEFAULT_INSNEMIT_PARTITIONS -lt 1); then
+  AC_MSG_ERROR(m4_normalize([
+		Invalid value $DEFAULT_INSNEMIT_PARTITIONS for --with-insnemit-partitions. \
+		Cannot be negative.]))
+fi
+
+AC_SUBST(DEFAULT_INSNEMIT_PARTITIONS)
+
 # Enable __cxa_atexit for C++.
 AC_ARG_ENABLE(__cxa_atexit,
 [AS_HELP_STRING([--enable-__cxa_atexit], [enable __cxa_atexit for C++])],
diff --git a/gcc/genemit.cc b/gcc/genemit.cc
index 1ce0564076d..471fd46a10b 100644
--- a/gcc/genemit.cc
+++ b/gcc/genemit.cc
@@ -49,29 +49,29 @@ struct clobber_ent
   struct clobber_ent *next;
 };
 
-static void output_peephole2_scratches	(rtx);
+static void output_peephole2_scratches	(rtx, FILE*);
 
 /* True for <X>_optab if that optab isn't allowed to fail.  */
 static bool nofail_optabs[NUM_OPTABS];
 
 static void
-print_code (RTX_CODE code)
+print_code (RTX_CODE code, FILE *file)
 {
   const char *p1;
   for (p1 = GET_RTX_NAME (code); *p1; p1++)
-    putchar (TOUPPER (*p1));
+    fprintf (file, "%c", TOUPPER (*p1));
 }
 
 static void
-gen_rtx_scratch (rtx x, enum rtx_code subroutine_type)
+gen_rtx_scratch (rtx x, enum rtx_code subroutine_type, FILE *file)
 {
   if (subroutine_type == DEFINE_PEEPHOLE2)
     {
-      printf ("operand%d", XINT (x, 0));
+      fprintf (file, "operand%d", XINT (x, 0));
     }
   else
     {
-      printf ("gen_rtx_SCRATCH (%smode)", GET_MODE_NAME (GET_MODE (x)));
+      fprintf (file, "gen_rtx_SCRATCH (%smode)", GET_MODE_NAME (GET_MODE (x)));
     }
 }
 
@@ -79,7 +79,8 @@ gen_rtx_scratch (rtx x, enum rtx_code subroutine_type)
    substituting any operand references appearing within.  */
 
 static void
-gen_exp (rtx x, enum rtx_code subroutine_type, char *used, md_rtx_info *info)
+gen_exp (rtx x, enum rtx_code subroutine_type, char *used, md_rtx_info *info,
+	 FILE *file)
 {
   RTX_CODE code;
   int i;
@@ -89,7 +90,7 @@ gen_exp (rtx x, enum rtx_code subroutine_type, char *used, md_rtx_info *info)
 
   if (x == 0)
     {
-      printf ("NULL_RTX");
+      fprintf (file, "NULL_RTX");
       return;
     }
 
@@ -103,67 +104,67 @@ gen_exp (rtx x, enum rtx_code subroutine_type, char *used, md_rtx_info *info)
 	{
 	  if (used[XINT (x, 0)])
 	    {
-	      printf ("copy_rtx (operand%d)", XINT (x, 0));
+	      fprintf (file, "copy_rtx (operand%d)", XINT (x, 0));
 	      return;
 	    }
 	  used[XINT (x, 0)] = 1;
 	}
-      printf ("operand%d", XINT (x, 0));
+      fprintf (file, "operand%d", XINT (x, 0));
       return;
 
     case MATCH_OP_DUP:
-      printf ("gen_rtx_fmt_");
+      fprintf (file, "gen_rtx_fmt_");
       for (i = 0; i < XVECLEN (x, 1); i++)
-	printf ("e");
-      printf (" (GET_CODE (operand%d), ", XINT (x, 0));
+	fprintf (file, "e");
+      fprintf (file, " (GET_CODE (operand%d), ", XINT (x, 0));
       if (GET_MODE (x) == VOIDmode)
-	printf ("GET_MODE (operand%d)", XINT (x, 0));
+	fprintf (file, "GET_MODE (operand%d)", XINT (x, 0));
       else
-	printf ("%smode", GET_MODE_NAME (GET_MODE (x)));
+	fprintf (file, "%smode", GET_MODE_NAME (GET_MODE (x)));
       for (i = 0; i < XVECLEN (x, 1); i++)
 	{
-	  printf (",\n\t\t");
-	  gen_exp (XVECEXP (x, 1, i), subroutine_type, used, info);
+	  fprintf (file, ",\n\t\t");
+	  gen_exp (XVECEXP (x, 1, i), subroutine_type, used, info, file);
 	}
-      printf (")");
+      fprintf (file, ")");
       return;
 
     case MATCH_OPERATOR:
-      printf ("gen_rtx_fmt_");
+      fprintf (file, "gen_rtx_fmt_");
       for (i = 0; i < XVECLEN (x, 2); i++)
-	printf ("e");
-      printf (" (GET_CODE (operand%d)", XINT (x, 0));
-      printf (", %smode", GET_MODE_NAME (GET_MODE (x)));
+	fprintf (file, "e");
+      fprintf (file, " (GET_CODE (operand%d)", XINT (x, 0));
+      fprintf (file, ", %smode", GET_MODE_NAME (GET_MODE (x)));
       for (i = 0; i < XVECLEN (x, 2); i++)
 	{
-	  printf (",\n\t\t");
-	  gen_exp (XVECEXP (x, 2, i), subroutine_type, used, info);
+	  fprintf (file, ",\n\t\t");
+	  gen_exp (XVECEXP (x, 2, i), subroutine_type, used, info, file);
 	}
-      printf (")");
+      fprintf (file, ")");
       return;
 
     case MATCH_PARALLEL:
     case MATCH_PAR_DUP:
-      printf ("operand%d", XINT (x, 0));
+      fprintf (file, "operand%d", XINT (x, 0));
       return;
 
     case MATCH_SCRATCH:
-      gen_rtx_scratch (x, subroutine_type);
+      gen_rtx_scratch (x, subroutine_type, file);
       return;
 
     case PC:
-      printf ("pc_rtx");
+      fprintf (file, "pc_rtx");
       return;
     case RETURN:
-      printf ("ret_rtx");
+      fprintf (file, "ret_rtx");
       return;
     case SIMPLE_RETURN:
-      printf ("simple_return_rtx");
+      fprintf (file, "simple_return_rtx");
       return;
     case CLOBBER:
       if (REG_P (XEXP (x, 0)))
 	{
-	  printf ("gen_hard_reg_clobber (%smode, %i)",
+	  fprintf (file, "gen_hard_reg_clobber (%smode, %i)",
 		  GET_MODE_NAME (GET_MODE (XEXP (x, 0))),
 		  REGNO (XEXP (x, 0)));
 	  return;
@@ -172,22 +173,22 @@ gen_exp (rtx x, enum rtx_code subroutine_type, char *used, md_rtx_info *info)
 
     case CONST_INT:
       if (INTVAL (x) == 0)
-	printf ("const0_rtx");
+	fprintf (file, "const0_rtx");
       else if (INTVAL (x) == 1)
-	printf ("const1_rtx");
+	fprintf (file, "const1_rtx");
       else if (INTVAL (x) == -1)
-	printf ("constm1_rtx");
+	fprintf (file, "constm1_rtx");
       else if (-MAX_SAVED_CONST_INT <= INTVAL (x)
 	       && INTVAL (x) <= MAX_SAVED_CONST_INT)
-	printf ("const_int_rtx[MAX_SAVED_CONST_INT + (%d)]",
+	fprintf (file, "const_int_rtx[MAX_SAVED_CONST_INT + (%d)]",
 		(int) INTVAL (x));
       else if (INTVAL (x) == STORE_FLAG_VALUE)
-	printf ("const_true_rtx");
+	fprintf (file, "const_true_rtx");
       else
 	{
-	  printf ("GEN_INT (");
-	  printf (HOST_WIDE_INT_PRINT_DEC_C, INTVAL (x));
-	  printf (")");
+	  fprintf (file, "GEN_INT (");
+	  fprintf (file, HOST_WIDE_INT_PRINT_DEC_C, INTVAL (x));
+	  fprintf (file, ")");
 	}
       return;
 
@@ -195,7 +196,7 @@ gen_exp (rtx x, enum rtx_code subroutine_type, char *used, md_rtx_info *info)
       /* Handle `const_double_zero' rtx.  */
       if (CONST_DOUBLE_REAL_VALUE (x)->cl == rvc_zero)
 	{
-	  printf ("CONST_DOUBLE_ATOF (\"0\", %smode)",
+	  fprintf (file, "CONST_DOUBLE_ATOF (\"0\", %smode)",
 		  GET_MODE_NAME (GET_MODE (x)));
 	  return;
 	}
@@ -210,12 +211,12 @@ gen_exp (rtx x, enum rtx_code subroutine_type, char *used, md_rtx_info *info)
       break;
     }
 
-  printf ("gen_rtx_");
-  print_code (code);
-  printf (" (");
+  fprintf (file, "gen_rtx_");
+  print_code (code, file);
+  fprintf (file, " (");
   if (!always_void_p (code))
     {
-      printf ("%smode", GET_MODE_NAME (GET_MODE (x)));
+      fprintf (file, "%smode", GET_MODE_NAME (GET_MODE (x)));
       sep = ",\n\t";
     }
 
@@ -225,41 +226,41 @@ gen_exp (rtx x, enum rtx_code subroutine_type, char *used, md_rtx_info *info)
     {
       if (fmt[i] == '0')
 	break;
-      fputs (sep, stdout);
+      fputs (sep, file);
       switch (fmt[i])
 	{
 	case 'e': case 'u':
-	  gen_exp (XEXP (x, i), subroutine_type, used, info);
+	  gen_exp (XEXP (x, i), subroutine_type, used, info, file);
 	  break;
 
 	case 'i':
-	  printf ("%u", XINT (x, i));
+	  fprintf (file, "%u", XINT (x, i));
 	  break;
 
 	case 'r':
-	  printf ("%u", REGNO (x));
+	  fprintf (file, "%u", REGNO (x));
 	  break;
 
 	case 'p':
 	  /* We don't have a way of parsing polynomial offsets yet,
 	     and hopefully never will.  */
-	  printf ("%d", SUBREG_BYTE (x).to_constant ());
+	  fprintf (file, "%d", SUBREG_BYTE (x).to_constant ());
 	  break;
 
 	case 's':
-	  printf ("\"%s\"", XSTR (x, i));
+	  fprintf (file, "\"%s\"", XSTR (x, i));
 	  break;
 
 	case 'E':
 	  {
 	    int j;
-	    printf ("gen_rtvec (%d", XVECLEN (x, i));
+	    fprintf (file, "gen_rtvec (%d", XVECLEN (x, i));
 	    for (j = 0; j < XVECLEN (x, i); j++)
 	      {
-		printf (",\n\t\t");
-		gen_exp (XVECEXP (x, i, j), subroutine_type, used, info);
+		fprintf (file, ",\n\t\t");
+		gen_exp (XVECEXP (x, i, j), subroutine_type, used, info, file);
 	      }
-	    printf (")");
+	    fprintf (file, ")");
 	    break;
 	  }
 
@@ -268,14 +269,14 @@ gen_exp (rtx x, enum rtx_code subroutine_type, char *used, md_rtx_info *info)
 	}
       sep = ",\n\t";
     }
-  printf (")");
+  fprintf (file, ")");
 }
 
 /* Output code to emit the instruction patterns in VEC, with each element
    becoming a separate instruction.  USED is as for gen_exp.  */
 
 static void
-gen_emit_seq (rtvec vec, char *used, md_rtx_info *info)
+gen_emit_seq (rtvec vec, char *used, md_rtx_info *info, FILE *file)
 {
   for (int i = 0, len = GET_NUM_ELEM (vec); i < len; ++i)
     {
@@ -283,17 +284,17 @@ gen_emit_seq (rtvec vec, char *used, md_rtx_info *info)
       rtx next = RTVEC_ELT (vec, i);
       if (const char *name = get_emit_function (next))
 	{
-	  printf ("  %s (", name);
-	  gen_exp (next, DEFINE_EXPAND, used, info);
-	  printf (");\n");
+	  fprintf (file, "  %s (", name);
+	  gen_exp (next, DEFINE_EXPAND, used, info, file);
+	  fprintf (file, ");\n");
 	  if (!last_p && needs_barrier_p (next))
-	    printf ("  emit_barrier ();");
+	    fprintf (file, "  emit_barrier ();");
 	}
       else
 	{
-	  printf ("  emit (");
-	  gen_exp (next, DEFINE_EXPAND, used, info);
-	  printf (", %s);\n", last_p ? "false" : "true");
+	  fprintf (file, "  emit (");
+	  gen_exp (next, DEFINE_EXPAND, used, info, file);
+	  fprintf (file, ", %s);\n", last_p ? "false" : "true");
 	}
     }
 }
@@ -303,27 +304,27 @@ gen_emit_seq (rtvec vec, char *used, md_rtx_info *info)
    for use in error messages.  */
 
 static void
-emit_c_code (const char *code, bool can_fail_p, const char *name)
+emit_c_code (const char *code, bool can_fail_p, const char *name, FILE *file)
 {
   if (can_fail_p)
-    printf ("#define FAIL return (end_sequence (), _val)\n");
+    fprintf (file, "#define FAIL return (end_sequence (), _val)\n");
   else
-    printf ("#define FAIL _Pragma (\"GCC error \\\"%s cannot FAIL\\\"\")"
+    fprintf (file, "#define FAIL _Pragma (\"GCC error \\\"%s cannot FAIL\\\"\")"
 	    " (void)0\n", name);
-  printf ("#define DONE return (_val = get_insns (), "
+  fprintf (file, "#define DONE return (_val = get_insns (), "
 	  "end_sequence (), _val)\n");
 
-  rtx_reader_ptr->print_md_ptr_loc (code);
-  printf ("%s\n", code);
+  rtx_reader_ptr->print_md_ptr_loc (code, file);
+  fprintf (file, "%s\n", code);
 
-  printf ("#undef DONE\n");
-  printf ("#undef FAIL\n");
+  fprintf (file, "#undef DONE\n");
+  fprintf (file, "#undef FAIL\n");
 }
 
 /* Generate the `gen_...' function for a DEFINE_INSN.  */
 
 static void
-gen_insn (md_rtx_info *info)
+gen_insn (md_rtx_info *info, FILE *file)
 {
   struct pattern_stats stats;
   int i;
@@ -409,7 +410,7 @@ gen_insn (md_rtx_info *info)
   if (XSTR (insn, 0)[0] == 0 || XSTR (insn, 0)[0] == '*')
     return;
 
-  printf ("/* %s:%d */\n", info->loc.filename, info->loc.lineno);
+  fprintf (file, "/* %s:%d */\n", info->loc.filename, info->loc.lineno);
 
   /* Find out how many operands this function has.  */
   get_pattern_stats (&stats, XVEC (insn, 1));
@@ -417,17 +418,17 @@ gen_insn (md_rtx_info *info)
     fatal_at (info->loc, "match_dup operand number has no match_operand");
 
   /* Output the function name and argument declarations.  */
-  printf ("rtx\ngen_%s (", XSTR (insn, 0));
+  fprintf (file, "rtx\ngen_%s (", XSTR (insn, 0));
   if (stats.num_generator_args)
     for (i = 0; i < stats.num_generator_args; i++)
       if (i)
-	printf (",\n\trtx operand%d ATTRIBUTE_UNUSED", i);
+	fprintf (file, ",\n\trtx operand%d ATTRIBUTE_UNUSED", i);
       else
-	printf ("rtx operand%d ATTRIBUTE_UNUSED", i);
+	fprintf (file, "rtx operand%d ATTRIBUTE_UNUSED", i);
   else
-    printf ("void");
-  printf (")\n");
-  printf ("{\n");
+    fprintf (file, "void");
+  fprintf (file, ")\n");
+  fprintf (file, "{\n");
 
   /* Output code to construct and return the rtl for the instruction body.  */
 
@@ -436,16 +437,16 @@ gen_insn (md_rtx_info *info)
   char *used = (XVECLEN (insn, 1) == 1
 		? NULL
 		: XCNEWVEC (char, stats.num_generator_args));
-  printf ("  return ");
-  gen_exp (pattern, DEFINE_INSN, used, info);
-  printf (";\n}\n\n");
+  fprintf (file, "  return ");
+  gen_exp (pattern, DEFINE_INSN, used, info, file);
+  fprintf (file, ";\n}\n\n");
   XDELETEVEC (used);
 }
 
 /* Generate the `gen_...' function for a DEFINE_EXPAND.  */
 
 static void
-gen_expand (md_rtx_info *info)
+gen_expand (md_rtx_info *info, FILE *file)
 {
   struct pattern_stats stats;
   int i;
@@ -466,17 +467,17 @@ gen_expand (md_rtx_info *info)
 			 "numbers above all other operands", XSTR (expand, 0));
 
   /* Output the function name and argument declarations.  */
-  printf ("rtx\ngen_%s (", XSTR (expand, 0));
+  fprintf (file, "rtx\ngen_%s (", XSTR (expand, 0));
   if (stats.num_generator_args)
     for (i = 0; i < stats.num_generator_args; i++)
       if (i)
-	printf (",\n\trtx operand%d", i);
+	fprintf (file, ",\n\trtx operand%d", i);
       else
-	printf ("rtx operand%d", i);
+	fprintf (file, "rtx operand%d", i);
   else
-    printf ("void");
-  printf (")\n");
-  printf ("{\n");
+    fprintf (file, "void");
+  fprintf (file, ")\n");
+  fprintf (file, "{\n");
 
   /* If we don't have any C code to write, only one insn is being written,
      and no MATCH_DUPs are present, we can just return the desired insn
@@ -485,18 +486,18 @@ gen_expand (md_rtx_info *info)
       && stats.max_opno >= stats.max_dup_opno
       && XVECLEN (expand, 1) == 1)
     {
-      printf ("  return ");
-      gen_exp (XVECEXP (expand, 1, 0), DEFINE_EXPAND, NULL, info);
-      printf (";\n}\n\n");
+      fprintf (file, "  return ");
+      gen_exp (XVECEXP (expand, 1, 0), DEFINE_EXPAND, NULL, info, file);
+      fprintf (file, ";\n}\n\n");
       return;
     }
 
   /* For each operand referred to only with MATCH_DUPs,
      make a local variable.  */
   for (i = stats.num_generator_args; i <= stats.max_dup_opno; i++)
-    printf ("  rtx operand%d;\n", i);
-  printf ("  rtx_insn *_val = 0;\n");
-  printf ("  start_sequence ();\n");
+    fprintf (file, "  rtx operand%d;\n", i);
+  fprintf (file, "  rtx_insn *_val = 0;\n");
+  fprintf (file, "  start_sequence ();\n");
 
   /* The fourth operand of DEFINE_EXPAND is some code to be executed
      before the actual construction.
@@ -506,13 +507,13 @@ gen_expand (md_rtx_info *info)
      So copy the operand values there before executing it.  */
   if (XSTR (expand, 3) && *XSTR (expand, 3))
     {
-      printf ("  {\n");
+      fprintf (file, "  {\n");
       if (stats.num_operand_vars > 0)
-	printf ("    rtx operands[%d];\n", stats.num_operand_vars);
+	fprintf (file, "    rtx operands[%d];\n", stats.num_operand_vars);
 
       /* Output code to copy the arguments into `operands'.  */
       for (i = 0; i < stats.num_generator_args; i++)
-	printf ("    operands[%d] = operand%d;\n", i, i);
+	fprintf (file, "    operands[%d] = operand%d;\n", i, i);
 
       /* Output the special code to be executed before the sequence
 	 is generated.  */
@@ -524,7 +525,7 @@ gen_expand (md_rtx_info *info)
 	  if (nofail_optabs[p.op])
 	    can_fail_p = false;
 	}
-      emit_c_code (XSTR (expand, 3), can_fail_p, XSTR (expand, 0));
+      emit_c_code (XSTR (expand, 3), can_fail_p, XSTR (expand, 0), file);
 
       /* Output code to copy the arguments back out of `operands'
 	 (unless we aren't going to use them at all).  */
@@ -532,29 +533,29 @@ gen_expand (md_rtx_info *info)
 	{
 	  for (i = 0; i <= MAX (stats.max_opno, stats.max_dup_opno); i++)
 	    {
-	      printf ("    operand%d = operands[%d];\n", i, i);
-	      printf ("    (void) operand%d;\n", i);
+	      fprintf (file, "    operand%d = operands[%d];\n", i, i);
+	      fprintf (file, "    (void) operand%d;\n", i);
 	    }
 	}
-      printf ("  }\n");
+      fprintf (file, "  }\n");
     }
 
   used = XCNEWVEC (char, stats.num_operand_vars);
-  gen_emit_seq (XVEC (expand, 1), used, info);
+  gen_emit_seq (XVEC (expand, 1), used, info, file);
   XDELETEVEC (used);
 
   /* Call `get_insns' to extract the list of all the
      insns emitted within this gen_... function.  */
 
-  printf ("  _val = get_insns ();\n");
-  printf ("  end_sequence ();\n");
-  printf ("  return _val;\n}\n\n");
+  fprintf (file, "  _val = get_insns ();\n");
+  fprintf (file, "  end_sequence ();\n");
+  fprintf (file, "  return _val;\n}\n\n");
 }
 
 /* Like gen_expand, but generates insns resulting from splitting SPLIT.  */
 
 static void
-gen_split (md_rtx_info *info)
+gen_split (md_rtx_info *info, FILE *file)
 {
   struct pattern_stats stats;
   int i;
@@ -580,62 +581,62 @@ gen_split (md_rtx_info *info)
   /* Output the prototype, function name and argument declarations.  */
   if (GET_CODE (split) == DEFINE_PEEPHOLE2)
     {
-      printf ("extern rtx_insn *gen_%s_%d (rtx_insn *, rtx *);\n",
+      fprintf (file, "extern rtx_insn *gen_%s_%d (rtx_insn *, rtx *);\n",
 	      name, info->index);
-      printf ("rtx_insn *\ngen_%s_%d (rtx_insn *curr_insn ATTRIBUTE_UNUSED,"
+      fprintf (file, "rtx_insn *\ngen_%s_%d (rtx_insn *curr_insn ATTRIBUTE_UNUSED,"
 	      " rtx *operands%s)\n",
 	      name, info->index, unused);
     }
   else
     {
-      printf ("extern rtx_insn *gen_split_%d (rtx_insn *, rtx *);\n",
+      fprintf (file, "extern rtx_insn *gen_split_%d (rtx_insn *, rtx *);\n",
 	      info->index);
-      printf ("rtx_insn *\ngen_split_%d "
+      fprintf (file, "rtx_insn *\ngen_split_%d "
 	      "(rtx_insn *curr_insn ATTRIBUTE_UNUSED, rtx *operands%s)\n",
 	      info->index, unused);
     }
-  printf ("{\n");
+  fprintf (file, "{\n");
 
   /* Declare all local variables.  */
   for (i = 0; i < stats.num_operand_vars; i++)
-    printf ("  rtx operand%d;\n", i);
-  printf ("  rtx_insn *_val = NULL;\n");
+    fprintf (file, "  rtx operand%d;\n", i);
+  fprintf (file, "  rtx_insn *_val = NULL;\n");
 
   if (GET_CODE (split) == DEFINE_PEEPHOLE2)
-    output_peephole2_scratches (split);
+    output_peephole2_scratches (split, file);
 
   const char *fn = info->loc.filename;
   for (const char *p = fn; *p; p++)
     if (*p == '/')
       fn = p + 1;
 
-  printf ("  if (dump_file)\n");
-  printf ("    fprintf (dump_file, \"Splitting with gen_%s_%d (%s:%d)\\n\");\n",
+  fprintf (file, "  if (dump_file)\n");
+  fprintf (file, "    fprintf (dump_file, \"Splitting with gen_%s_%d (%s:%d)\\n\");\n",
 	  name, info->index, fn, info->loc.lineno);
 
-  printf ("  start_sequence ();\n");
+  fprintf (file, "  start_sequence ();\n");
 
   /* The fourth operand of DEFINE_SPLIT is some code to be executed
      before the actual construction.  */
 
   if (XSTR (split, 3))
-    emit_c_code (XSTR (split, 3), true, name);
+    emit_c_code (XSTR (split, 3), true, name, file);
 
   /* Output code to copy the arguments back out of `operands'  */
   for (i = 0; i < stats.num_operand_vars; i++)
     {
-      printf ("  operand%d = operands[%d];\n", i, i);
-      printf ("  (void) operand%d;\n", i);
+      fprintf (file, "  operand%d = operands[%d];\n", i, i);
+      fprintf (file, "  (void) operand%d;\n", i);
     }
 
-  gen_emit_seq (XVEC (split, 2), used, info);
+  gen_emit_seq (XVEC (split, 2), used, info, file);
 
   /* Call `get_insns' to make a list of all the
      insns emitted within this gen_... function.  */
 
-  printf ("  _val = get_insns ();\n");
-  printf ("  end_sequence ();\n");
-  printf ("  return _val;\n}\n\n");
+  fprintf (file, "  _val = get_insns ();\n");
+  fprintf (file, "  end_sequence ();\n");
+  fprintf (file, "  return _val;\n}\n\n");
 
   free (used);
 }
@@ -645,37 +646,37 @@ gen_split (md_rtx_info *info)
    the end of the vector.  */
 
 static void
-output_add_clobbers (md_rtx_info *info)
+output_add_clobbers (md_rtx_info *info, FILE *file)
 {
   struct clobber_pat *clobber;
   struct clobber_ent *ent;
   int i;
 
-  printf ("\n\nvoid\nadd_clobbers (rtx pattern ATTRIBUTE_UNUSED, int insn_code_number)\n");
-  printf ("{\n");
-  printf ("  switch (insn_code_number)\n");
-  printf ("    {\n");
+  fprintf (file, "\n\nvoid\nadd_clobbers (rtx pattern ATTRIBUTE_UNUSED, int insn_code_number)\n");
+  fprintf (file, "{\n");
+  fprintf (file, "  switch (insn_code_number)\n");
+  fprintf (file, "    {\n");
 
   for (clobber = clobber_list; clobber; clobber = clobber->next)
     {
       for (ent = clobber->insns; ent; ent = ent->next)
-	printf ("    case %d:\n", ent->code_number);
+	fprintf (file, "    case %d:\n", ent->code_number);
 
       for (i = clobber->first_clobber; i < XVECLEN (clobber->pattern, 1); i++)
 	{
-	  printf ("      XVECEXP (pattern, 0, %d) = ", i);
+	  fprintf (file, "      XVECEXP (pattern, 0, %d) = ", i);
 	  gen_exp (XVECEXP (clobber->pattern, 1, i),
-		   GET_CODE (clobber->pattern), NULL, info);
-	  printf (";\n");
+		   GET_CODE (clobber->pattern), NULL, info, file);
+	  fprintf (file, ";\n");
 	}
 
-      printf ("      break;\n\n");
+      fprintf (file, "      break;\n\n");
     }
 
-  printf ("    default:\n");
-  printf ("      gcc_unreachable ();\n");
-  printf ("    }\n");
-  printf ("}\n");
+  fprintf (file, "    default:\n");
+  fprintf (file, "      gcc_unreachable ();\n");
+  fprintf (file, "    }\n");
+  fprintf (file, "}\n");
 }
 
 /* Write a function, `added_clobbers_hard_reg_p' that is given an insn_code
@@ -684,17 +685,17 @@ output_add_clobbers (md_rtx_info *info)
    SCRATCH.  */
 
 static void
-output_added_clobbers_hard_reg_p (void)
+output_added_clobbers_hard_reg_p (FILE *file)
 {
   struct clobber_pat *clobber;
   struct clobber_ent *ent;
   int clobber_p;
   bool used;
 
-  printf ("\n\nbool\nadded_clobbers_hard_reg_p (int insn_code_number)\n");
-  printf ("{\n");
-  printf ("  switch (insn_code_number)\n");
-  printf ("    {\n");
+  fprintf (file, "\n\nbool\nadded_clobbers_hard_reg_p (int insn_code_number)\n");
+  fprintf (file, "{\n");
+  fprintf (file, "  switch (insn_code_number)\n");
+  fprintf (file, "    {\n");
 
   for (clobber_p = 0; clobber_p <= 1; clobber_p++)
     {
@@ -703,25 +704,25 @@ output_added_clobbers_hard_reg_p (void)
 	if (clobber->has_hard_reg == clobber_p)
 	  for (ent = clobber->insns; ent; ent = ent->next)
 	    {
-	      printf ("    case %d:\n", ent->code_number);
+	      fprintf (file, "    case %d:\n", ent->code_number);
 	      used = true;
 	    }
 
       if (used)
-	printf ("      return %s;\n\n", clobber_p ? "true" : "false");
+	fprintf (file, "      return %s;\n\n", clobber_p ? "true" : "false");
     }
 
-  printf ("    default:\n");
-  printf ("      gcc_unreachable ();\n");
-  printf ("    }\n");
-  printf ("}\n");
+  fprintf (file, "    default:\n");
+  fprintf (file, "      gcc_unreachable ();\n");
+  fprintf (file, "    }\n");
+  fprintf (file, "}\n");
 }
 
 /* Generate code to invoke find_free_register () as needed for the
    scratch registers used by the peephole2 pattern in SPLIT.  */
 
 static void
-output_peephole2_scratches (rtx split)
+output_peephole2_scratches (rtx split, FILE *file)
 {
   int i;
   int insn_nr = 0;
@@ -746,12 +747,12 @@ output_peephole2_scratches (rtx split)
 
 	  if (first)
 	    {
-	      printf ("  HARD_REG_SET _regs_allocated;\n");
-	      printf ("  CLEAR_HARD_REG_SET (_regs_allocated);\n");
+	      fprintf (file, "  HARD_REG_SET _regs_allocated;\n");
+	      fprintf (file, "  CLEAR_HARD_REG_SET (_regs_allocated);\n");
 	      first = false;
 	    }
 
-	  printf ("  if ((operands[%d] = peep2_find_free_register (%d, %d, \"%s\", %smode, &_regs_allocated)) == NULL_RTX)\n\
+	  fprintf (file, "  if ((operands[%d] = peep2_find_free_register (%d, %d, \"%s\", %smode, &_regs_allocated)) == NULL_RTX)\n\
     return NULL;\n",
 		  XINT (elt, 0),
 		  insn_nr, last_insn_nr,
@@ -767,50 +768,50 @@ output_peephole2_scratches (rtx split)
 /* Print "arg<N>" parameter declarations for each argument N of ONAME.  */
 
 static void
-print_overload_arguments (overloaded_name *oname)
+print_overload_arguments (overloaded_name *oname, FILE *file)
 {
   for (unsigned int i = 0; i < oname->arg_types.length (); ++i)
-    printf ("%s%s arg%d", i == 0 ? "" : ", ", oname->arg_types[i], i);
+    fprintf (file, "%s%s arg%d", i == 0 ? "" : ", ", oname->arg_types[i], i);
 }
 
 /* Print code to test whether INSTANCE should be chosen, given that
    argument N of the overload is available as "arg<N>".  */
 
 static void
-print_overload_test (overloaded_instance *instance)
+print_overload_test (overloaded_instance *instance, FILE *file)
 {
   for (unsigned int i = 0; i < instance->arg_values.length (); ++i)
-    printf ("%sarg%d == %s", i == 0 ? "  if (" : "\n      && ",
+    fprintf (file, "%sarg%d == %s", i == 0 ? "  if (" : "\n      && ",
 	    i, instance->arg_values[i]);
-  printf (")\n");
+  fprintf (file, ")\n");
 }
 
 /* Emit a maybe_code_for_* function for ONAME.  */
 
 static void
-handle_overloaded_code_for (overloaded_name *oname)
+handle_overloaded_code_for (overloaded_name *oname, FILE *file)
 {
   /* Print the function prototype.  */
-  printf ("\ninsn_code\nmaybe_code_for_%s (", oname->name);
-  print_overload_arguments (oname);
-  printf (")\n{\n");
+  fprintf (file, "\ninsn_code\nmaybe_code_for_%s (", oname->name);
+  print_overload_arguments (oname, file);
+  fprintf (file, ")\n{\n");
 
   /* Use a sequence of "if" statements for each instance.  */
   for (overloaded_instance *instance = oname->first_instance;
        instance; instance = instance->next)
     {
-      print_overload_test (instance);
-      printf ("    return CODE_FOR_%s;\n", instance->name);
+      print_overload_test (instance, file);
+      fprintf (file, "    return CODE_FOR_%s;\n", instance->name);
     }
 
   /* Return null if no match was found.  */
-  printf ("  return CODE_FOR_nothing;\n}\n");
+  fprintf (file, "  return CODE_FOR_nothing;\n}\n");
 }
 
 /* Emit a maybe_gen_* function for ONAME.  */
 
 static void
-handle_overloaded_gen (overloaded_name *oname)
+handle_overloaded_gen (overloaded_name *oname, FILE *file)
 {
   unsigned HOST_WIDE_INT seen = 0;
   /* All patterns must have the same number of operands.  */
@@ -827,25 +828,25 @@ handle_overloaded_gen (overloaded_name *oname)
       seen |= mask;
 
       /* Print the function prototype.  */
-      printf ("\nrtx\nmaybe_gen_%s (", oname->name);
-      print_overload_arguments (oname);
+      fprintf (file, "\nrtx\nmaybe_gen_%s (", oname->name);
+      print_overload_arguments (oname, file);
       for (int i = 0; i < stats.num_generator_args; ++i)
-	printf (", rtx x%d", i);
-      printf (")\n{\n");
+	fprintf (file, ", rtx x%d", i);
+      fprintf (file, ")\n{\n");
 
       /* Use maybe_code_for_*, instead of duplicating the selection
 	 logic here.  */
-      printf ("  insn_code code = maybe_code_for_%s (", oname->name);
+      fprintf (file, "  insn_code code = maybe_code_for_%s (", oname->name);
       for (unsigned int i = 0; i < oname->arg_types.length (); ++i)
-	printf ("%sarg%d", i == 0 ? "" : ", ", i);
-      printf (");\n"
+	fprintf (file, "%sarg%d", i == 0 ? "" : ", ", i);
+      fprintf (file, ");\n"
 	      "  if (code != CODE_FOR_nothing)\n"
 	      "    {\n"
 	      "      gcc_assert (insn_data[code].n_generator_args == %d);\n"
 	      "      return GEN_FCN (code) (", stats.num_generator_args);
       for (int i = 0; i < stats.num_generator_args; ++i)
-	printf ("%sx%d", i == 0 ? "" : ", ", i);
-      printf (");\n"
+	fprintf (file, "%sx%d", i == 0 ? "" : ", ", i);
+      fprintf (file, ");\n"
 	      "    }\n"
 	      "  else\n"
 	      "    return NULL_RTX;\n"
@@ -853,12 +854,68 @@ handle_overloaded_gen (overloaded_name *oname)
     }
 }
 
+void
+print_header (FILE *file)
+{
+  fprintf (file, "/* Generated automatically by the program `genemit'\n\
+from the machine description file `md'.  */\n\n");
+
+  fprintf (file, "#define IN_TARGET_CODE 1\n");
+  fprintf (file, "#include \"config.h\"\n");
+  fprintf (file, "#include \"system.h\"\n");
+  fprintf (file, "#include \"coretypes.h\"\n");
+  fprintf (file, "#include \"backend.h\"\n");
+  fprintf (file, "#include \"predict.h\"\n");
+  fprintf (file, "#include \"tree.h\"\n");
+  fprintf (file, "#include \"rtl.h\"\n");
+  fprintf (file, "#include \"alias.h\"\n");
+  fprintf (file, "#include \"varasm.h\"\n");
+  fprintf (file, "#include \"stor-layout.h\"\n");
+  fprintf (file, "#include \"calls.h\"\n");
+  fprintf (file, "#include \"memmodel.h\"\n");
+  fprintf (file, "#include \"tm_p.h\"\n");
+  fprintf (file, "#include \"flags.h\"\n");
+  fprintf (file, "#include \"insn-config.h\"\n");
+  fprintf (file, "#include \"expmed.h\"\n");
+  fprintf (file, "#include \"dojump.h\"\n");
+  fprintf (file, "#include \"explow.h\"\n");
+  fprintf (file, "#include \"emit-rtl.h\"\n");
+  fprintf (file, "#include \"stmt.h\"\n");
+  fprintf (file, "#include \"expr.h\"\n");
+  fprintf (file, "#include \"insn-codes.h\"\n");
+  fprintf (file, "#include \"optabs.h\"\n");
+  fprintf (file, "#include \"dfp.h\"\n");
+  fprintf (file, "#include \"output.h\"\n");
+  fprintf (file, "#include \"recog.h\"\n");
+  fprintf (file, "#include \"df.h\"\n");
+  fprintf (file, "#include \"resource.h\"\n");
+  fprintf (file, "#include \"reload.h\"\n");
+  fprintf (file, "#include \"diagnostic-core.h\"\n");
+  fprintf (file, "#include \"regs.h\"\n");
+  fprintf (file, "#include \"tm-constrs.h\"\n");
+  fprintf (file, "#include \"ggc.h\"\n");
+  fprintf (file, "#include \"target.h\"\n\n");
+}
+
+auto_vec<const char *, 10> output_files;
+
+static bool
+handle_arg (const char *arg)
+{
+  if (arg[1] == 'O')
+    {
+      output_files.safe_push (&arg[2]);
+      return true;
+    }
+  return false;
+}
+
 int
 main (int argc, const char **argv)
 {
   progname = "genemit";
 
-  if (!init_rtx_reader_args (argc, argv))
+  if (!init_rtx_reader_args_cb (argc, argv, handle_arg))
     return (FATAL_EXIT_CODE);
 
 #define DEF_INTERNAL_OPTAB_FN(NAME, FLAGS, OPTAB, TYPE) \
@@ -868,86 +925,87 @@ main (int argc, const char **argv)
   /* Assign sequential codes to all entries in the machine description
      in parallel with the tables in insn-output.cc.  */
 
-  printf ("/* Generated automatically by the program `genemit'\n\
-from the machine description file `md'.  */\n\n");
+  int npatterns = count_patterns ();
+  md_rtx_info info;
+
+  bool to_stdout = false;
+  int npatterns_per_file = npatterns;
+  if (!output_files.is_empty ())
+    npatterns_per_file = npatterns / output_files.length () + 1;
+  else
+    to_stdout = true;
 
-  printf ("#define IN_TARGET_CODE 1\n");
-  printf ("#include \"config.h\"\n");
-  printf ("#include \"system.h\"\n");
-  printf ("#include \"coretypes.h\"\n");
-  printf ("#include \"backend.h\"\n");
-  printf ("#include \"predict.h\"\n");
-  printf ("#include \"tree.h\"\n");
-  printf ("#include \"rtl.h\"\n");
-  printf ("#include \"alias.h\"\n");
-  printf ("#include \"varasm.h\"\n");
-  printf ("#include \"stor-layout.h\"\n");
-  printf ("#include \"calls.h\"\n");
-  printf ("#include \"memmodel.h\"\n");
-  printf ("#include \"tm_p.h\"\n");
-  printf ("#include \"flags.h\"\n");
-  printf ("#include \"insn-config.h\"\n");
-  printf ("#include \"expmed.h\"\n");
-  printf ("#include \"dojump.h\"\n");
-  printf ("#include \"explow.h\"\n");
-  printf ("#include \"emit-rtl.h\"\n");
-  printf ("#include \"stmt.h\"\n");
-  printf ("#include \"expr.h\"\n");
-  printf ("#include \"insn-codes.h\"\n");
-  printf ("#include \"optabs.h\"\n");
-  printf ("#include \"dfp.h\"\n");
-  printf ("#include \"output.h\"\n");
-  printf ("#include \"recog.h\"\n");
-  printf ("#include \"df.h\"\n");
-  printf ("#include \"resource.h\"\n");
-  printf ("#include \"reload.h\"\n");
-  printf ("#include \"diagnostic-core.h\"\n");
-  printf ("#include \"regs.h\"\n");
-  printf ("#include \"tm-constrs.h\"\n");
-  printf ("#include \"ggc.h\"\n");
-  printf ("#include \"target.h\"\n\n");
+  gcc_assert (npatterns_per_file > 1);
 
-  /* Read the machine description.  */
+  /* Reverse so we can pop the first-added element.  */
+  output_files.reverse ();
 
-  md_rtx_info info;
+  int count = 0;
+  FILE *file = NULL;
+
+  /* Read the machine description.  */
   while (read_md_rtx (&info))
-    switch (GET_CODE (info.def))
-      {
-      case DEFINE_INSN:
-	gen_insn (&info);
-	break;
+    {
+      if (count == 0 || count == npatterns_per_file)
+	{
+	  bool is_last = !to_stdout && output_files.is_empty ();
+	  if (file && !is_last)
+	    if (fclose (file) != 0)
+	      return FATAL_EXIT_CODE;
 
-      case DEFINE_EXPAND:
-	printf ("/* %s:%d */\n", info.loc.filename, info.loc.lineno);
-	gen_expand (&info);
-	break;
+	  if (!output_files.is_empty ())
+	    {
+	      const char *const filename = output_files.pop ();
+	      file = fopen (filename, "w");
+	    }
+	  else if (to_stdout)
+	    file = stdout;
+	  else
+	    break;
 
-      case DEFINE_SPLIT:
-	printf ("/* %s:%d */\n", info.loc.filename, info.loc.lineno);
-	gen_split (&info);
-	break;
+	  print_header (file);
+	  count = 0;
+	}
 
-      case DEFINE_PEEPHOLE2:
-	printf ("/* %s:%d */\n", info.loc.filename, info.loc.lineno);
-	gen_split (&info);
-	break;
+      switch (GET_CODE (info.def))
+	{
+	case DEFINE_INSN:
+	  gen_insn (&info, file);
+	  break;
 
-      default:
-	break;
-      }
+	case DEFINE_EXPAND:
+	  fprintf (file, "/* %s:%d */\n", info.loc.filename, info.loc.lineno);
+	  gen_expand (&info, file);
+	  break;
+
+	case DEFINE_SPLIT:
+	  fprintf (file, "/* %s:%d */\n", info.loc.filename, info.loc.lineno);
+	  gen_split (&info, file);
+	  break;
+
+	case DEFINE_PEEPHOLE2:
+	  fprintf (file, "/* %s:%d */\n", info.loc.filename, info.loc.lineno);
+	  gen_split (&info, file);
+	  break;
+
+	default:
+	  break;
+	}
+
+      count++;
+    }
 
   /* Write out the routines to add CLOBBERs to a pattern and say whether they
      clobber a hard reg.  */
-  output_add_clobbers (&info);
-  output_added_clobbers_hard_reg_p ();
+  output_add_clobbers (&info, file);
+  output_added_clobbers_hard_reg_p (file);
 
   for (overloaded_name *oname = rtx_reader_ptr->get_overloads ();
        oname; oname = oname->next)
     {
-      handle_overloaded_code_for (oname);
-      handle_overloaded_gen (oname);
+      handle_overloaded_code_for (oname, file);
+      handle_overloaded_gen (oname, file);
     }
 
-  fflush (stdout);
-  return (ferror (stdout) != 0 ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE);
+  return (fclose (file) != 0 ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE);
 }
diff --git a/gcc/gensupport.cc b/gcc/gensupport.cc
index dd920d673b4..688808c7d12 100644
--- a/gcc/gensupport.cc
+++ b/gcc/gensupport.cc
@@ -3131,6 +3131,61 @@ init_rtx_reader_args (int argc, const char **argv)
   return init_rtx_reader_args_cb (argc, argv, 0);
 }
 
+/* Count the number of patterns in all queues and return the count.  */
+int
+count_patterns ()
+{
+  int count = 0, truth = 1;
+  rtx def;
+  class queue_elem *cur = define_attr_queue;
+  while (cur)
+    {
+      def = cur->data;
+
+      truth = maybe_eval_c_test (get_c_test (def));
+      if (truth || !insn_elision)
+	count++;
+      cur = cur->next;
+    }
+
+  cur = define_pred_queue;
+  while (cur)
+    {
+      def = cur->data;
+
+      truth = maybe_eval_c_test (get_c_test (def));
+      if (truth || !insn_elision)
+	count++;
+      cur = cur->next;
+    }
+
+  cur = define_insn_queue;
+  truth = 1;
+  while (cur)
+    {
+      def = cur->data;
+
+      truth = maybe_eval_c_test (get_c_test (def));
+      if (truth || !insn_elision)
+	count++;
+      cur = cur->next;
+    }
+
+  cur = other_queue;
+  truth = 1;
+  while (cur)
+    {
+      def = cur->data;
+
+      truth = maybe_eval_c_test (get_c_test (def));
+      if (truth || !insn_elision)
+	count++;
+      cur = cur->next;
+    }
+
+  return count;
+}
+
 /* Try to read a single rtx from the file.  Return true on success,
    describing it in *INFO.  */
 
diff --git a/gcc/gensupport.h b/gcc/gensupport.h
index 7925e22ed41..7396118714b 100644
--- a/gcc/gensupport.h
+++ b/gcc/gensupport.h
@@ -130,6 +130,7 @@ extern rtx add_implicit_parallel (rtvec);
 extern rtx_reader *init_rtx_reader_args_cb (int, const char **,
 					    bool (*)(const char *));
 extern rtx_reader *init_rtx_reader_args (int, const char **);
+extern int count_patterns ();
 extern bool read_md_rtx (md_rtx_info *);
 extern unsigned int get_num_insn_codes ();
 
diff --git a/gcc/read-md.cc b/gcc/read-md.cc
index fd38818e3a3..46ab9065e3e 100644
--- a/gcc/read-md.cc
+++ b/gcc/read-md.cc
@@ -132,9 +132,9 @@ md_reader::fprint_md_ptr_loc (FILE *outf, const void *ptr)
 
 /* Special fprint_md_ptr_loc for writing to STDOUT.  */
 void
-md_reader::print_md_ptr_loc (const void *ptr)
+md_reader::print_md_ptr_loc (const void *ptr, FILE *file)
 {
-  fprint_md_ptr_loc (stdout, ptr);
+  fprint_md_ptr_loc (file, ptr);
 }
 
 /* Return a condition that satisfies both COND1 and COND2.  Either string
diff --git a/gcc/read-md.h b/gcc/read-md.h
index b309c9c3deb..2adcb58478f 100644
--- a/gcc/read-md.h
+++ b/gcc/read-md.h
@@ -194,7 +194,7 @@ class md_reader
   const struct ptr_loc *get_md_ptr_loc (const void *ptr);
   void copy_md_ptr_loc (const void *new_ptr, const void *old_ptr);
   void fprint_md_ptr_loc (FILE *outf, const void *ptr);
-  void print_md_ptr_loc (const void *ptr);
+  void print_md_ptr_loc (const void *ptr, FILE * = stdout);
 
   struct enum_type *lookup_enum_type (const char *name);
   void traverse_enum_types (htab_trav callback, void *info);
Jeff Law Oct. 30, 2023, 9:11 p.m. UTC | #11
On 10/27/23 13:04, Robin Dapp wrote:
> After working with Sam off-list (thanks) I managed to get hppa to
> build.  Initially it looked as if hppa just had a very small number of
> instruction patterns so we wouldn't generate all 10 output files.
> However, the actual issue (which we will only hit with a low
> pattern count) was with counting all the patterns vs only counting
> the patterns that will be output.  A wrong pattern count lead to
> prematurely stopping to write output files.
> 
> With that corrected, hppa "just works" until I hit linker errors
> due to relocations - most likely unrelated:
> 
> bin/ld: unwind-dw2-fde-dip_s.o(.data.rel.ro+0): cannot handle
> R_PARISC_FPTR64 for __pthread_key_create@@GLIBC_2.34
> 
> Attached is v3 that has been bootstrapped and tested on x86 and power10,
> aarch64 bootstrap was ok, testsuite is still running.  A riscv build and
> testsuite run was successful as well.
> 
> Regards
>   Robin
> 
>  From 248744c328440bff9cc339d2bf622852cbaac343 Mon Sep 17 00:00:00 2001
> From: Robin Dapp <rdapp@ventanamicro.com>
> Date: Thu, 12 Oct 2023 11:23:26 +0200
> Subject: [PATCH v3] genemit: Split insn-emit.cc into several partitions.
> 
> On riscv insn-emit.cc has grown to over 1.2 mio lines of code and
> compiling it takes considerable time.
> Therefore, this patch adjust genemit to create several partitions
> (insn-emit-1.cc to insn-emit-n.cc).  The available patterns are
> written to the given files in a sequential fashion.
> 
> Similar to match.pd a configure option --with-emitinsn-partitions=num
> is introduced that makes the number of partition configurable.
> 
> gcc/ChangeLog:
> 
> 	PR bootstrap/84402
> 	PR target/111600
> 
> 	* Makefile.in: Handle split insn-emit.cc.
> 	* configure: Regenerate.
> 	* configure.ac: Add --with-insnemit-partitions.
> 	* genemit.cc (output_peephole2_scratches): Print to file instead
> 	of stdout.
> 	(print_code): Ditto.
> 	(gen_rtx_scratch): Ditto.
> 	(gen_exp): Ditto.
> 	(gen_emit_seq): Ditto.
> 	(emit_c_code): Ditto.
> 	(gen_insn): Ditto.
> 	(gen_expand): Ditto.
> 	(gen_split): Ditto.
> 	(output_add_clobbers): Ditto.
> 	(output_added_clobbers_hard_reg_p): Ditto.
> 	(print_overload_arguments): Ditto.
> 	(print_overload_test): Ditto.
> 	(handle_overloaded_code_for): Ditto.
> 	(handle_overloaded_gen): Ditto.
> 	(print_header): New function.
> 	(handle_arg): New function.
> 	(main): Split output into 10 files.
> 	* gensupport.cc (count_patterns): New function.
> 	* gensupport.h (count_patterns): Define.
> 	* read-md.cc (md_reader::print_md_ptr_loc): Add file argument.
> 	* read-md.h (class md_reader): Change definition.
Just one note on testing.  I threw this into my tester which ran through 
its usual set of crosses as well as native emulated builds of alpha, 
hppa, m68k, sh4, sh4eb, riscv, aarch64.  s390x and ppc64le are in 
progress, and have progressed beyond their build phase.   Note that the 
emulated natives other than risc-v are 3-stage bootstrapped.

OK for the trunk.  Thanks for taking care of this.  I guess I'll need to 
time a risc-v bootstrap again.  It's currently using --disable-bootstrap 
at configure time in my tester.


jeff
Andreas Schwab Nov. 6, 2023, 5:22 p.m. UTC | #12
On Nov 06 2023, Martin Jambor wrote:

> Following this commit, our buildbot script which checks that configure
> scripts where re-generated correctly is unhappy because it insists
> comments are wrong, it wants to them to be like this:
>
> ------------------------------------------------------------
> diff --git a/gcc/configure b/gcc/configure
> index 4d0357cbc28..0d818ae6850 100755
> --- a/gcc/configure
> +++ b/gcc/configure
> @@ -20000,7 +20000,7 @@ else
>    lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
>    lt_status=$lt_dlunknown
>    cat > conftest.$ac_ext <<_LT_EOF
> -#line 19995 "configure"
> +#line 20003 "configure"
>  #include "confdefs.h"
>  
>  #if HAVE_DLFCN_H
> @@ -20106,7 +20106,7 @@ else
>    lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
>    lt_status=$lt_dlunknown
>    cat > conftest.$ac_ext <<_LT_EOF
> -#line 20101 "configure"
> +#line 20109 "configure"
>  #include "confdefs.h"
>  
>  #if HAVE_DLFCN_H
> ------------------------------------------------------------
>
> I'm not sure what that means, whether a wrong version of
> autoconf/automake was used (though when I accidentally tried that, it
> has always complained loudly) or if some environment difference can
> cause this.

This can happen if commits were rebased without re-generating configure
scripts.
Robin Dapp Nov. 6, 2023, 5:41 p.m. UTC | #13
> I'm not sure what that means, whether a wrong version of
> autoconf/automake was used (though when I accidentally tried that, it
> has always complained loudly) or if some environment difference can
> cause this.  Perhaps I should change the script not to care about
> commits though that won't happen soon (or perhaps I should drop the
> checks completely) but would people be OK with me checking in the patch
> above (with appropriate ChangeLog) to silence buildbot for a while
> again?

Hmm, I made sure to regenerate them on an Intel compile-farm machine
because my local autoconf/automake was too new.  I thought that I
also regenerated after the last rebase before pushing.  But maybe
it auto-merged that change at some point and I forgot?

Regards
 Robin
diff mbox series

Patch

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 9cc16268abf..1988327f311 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -236,6 +236,12 @@  GIMPLE_MATCH_PD_SEQ_O = $(patsubst %, gimple-match-%.o, $(MATCH_SPLITS_SEQ))
 GENERIC_MATCH_PD_SEQ_SRC = $(patsubst %, generic-match-%.cc, $(MATCH_SPLITS_SEQ))
 GENERIC_MATCH_PD_SEQ_O = $(patsubst %, generic-match-%.o, $(MATCH_SPLITS_SEQ))
 
+# The number of splits to be made for the insn-emit files.
+NUM_INSNEMIT_SPLITS = @DEFAULT_INSNEMIT_PARTITIONS@
+INSNEMIT_SPLITS_SEQ = $(wordlist 1,$(NUM_INSNEMIT_SPLITS),$(one_to_9999))
+INSNEMIT_SEQ_SRC = $(patsubst %, insn-emit-%.cc, $(INSNEMIT_SPLITS_SEQ))
+INSNEMIT_SEQ_O = $(patsubst %, insn-emit-%.o, $(INSNEMIT_SPLITS_SEQ))
+
 # These files are to have specific diagnostics suppressed, or are not to
 # be subject to -Werror:
 # flex output may yield harmless "no previous prototype" warnings
@@ -1354,7 +1360,7 @@  OBJS = \
 	insn-attrtab.o \
 	insn-automata.o \
 	insn-dfatab.o \
-	insn-emit.o \
+	$(INSNEMIT_SEQ_O) \
 	insn-extract.o \
 	insn-latencytab.o \
 	insn-modes.o \
@@ -1852,7 +1858,8 @@  TREECHECKING = @TREECHECKING@
 FULL_DRIVER_NAME=$(target_noncanonical)-gcc-$(version)$(exeext)
 
 MOSTLYCLEANFILES = insn-flags.h insn-config.h insn-codes.h \
- insn-output.cc insn-recog.cc insn-emit.cc insn-extract.cc insn-peep.cc \
+ insn-output.cc insn-recog.cc $(INSNEMIT_SEQ_SRC) \
+ insn-extract.cc insn-peep.cc \
  insn-attr.h insn-attr-common.h insn-attrtab.cc insn-dfatab.cc \
  insn-latencytab.cc insn-opinit.cc insn-opinit.h insn-preds.cc insn-constants.h \
  tm-preds.h tm-constrs.h checksum-options $(GIMPLE_MATCH_PD_SEQ_SRC) \
@@ -2481,11 +2488,11 @@  $(common_out_object_file): $(common_out_file)
 # and compile them.
 
 .PRECIOUS: insn-config.h insn-flags.h insn-codes.h insn-constants.h \
-  insn-emit.cc insn-recog.cc insn-extract.cc insn-output.cc insn-peep.cc \
-  insn-attr.h insn-attr-common.h insn-attrtab.cc insn-dfatab.cc \
-  insn-latencytab.cc insn-preds.cc $(GIMPLE_MATCH_PD_SEQ_SRC) \
-  $(GENERIC_MATCH_PD_SEQ_SRC) gimple-match-auto.h generic-match-auto.h \
-  insn-target-def.h
+  $(INSNEMIT_SEQ_SRC) insn-recog.cc insn-extract.cc insn-output.cc \
+  insn-peep.cc insn-attr.h insn-attr-common.h insn-attrtab.cc \
+  insn-dfatab.cc insn-latencytab.cc insn-preds.cc \
+  $(GIMPLE_MATCH_PD_SEQ_SRC) $(GENERIC_MATCH_PD_SEQ_SRC) \
+  gimple-match-auto.h generic-match-auto.h insn-target-def.h
 
 # Dependencies for the md file.  The first time through, we just assume
 # the md file itself and the generated dependency file (in order to get
@@ -2508,7 +2515,7 @@  s-mddeps: $(md_file) $(MD_INCLUDES) build/genmddeps$(build_exeext)
 simple_rtl_generated_h	= insn-attr.h insn-attr-common.h insn-codes.h \
 			  insn-config.h insn-flags.h insn-target-def.h
 
-simple_rtl_generated_c	= insn-automata.cc insn-emit.cc \
+simple_rtl_generated_c	= insn-automata.cc \
 			  insn-extract.cc insn-output.cc \
 			  insn-peep.cc insn-recog.cc
 
@@ -2537,8 +2544,22 @@  $(simple_generated_c:insn-%.cc=s-%): s-%: build/gen%$(build_exeext)
 	$(SHELL) $(srcdir)/../move-if-change tmp-$*.cc insn-$*.cc
 	$(STAMP) s-$*
 
+# genemit splits its output into different files and doesn't write to
+# stdout. (but rather to tmp-emit-01.cc..tmp-emit-10.cc)
+s-tmp-emit: build/genemit$(build_exeext) insn-conditions.md
+	$(RUN_GEN) build/genemit$(build_exeext) $(md_file) insn-conditions.md \
+	  -Otmp-emit -P${NUM_INSNEMIT_SPLITS}
+	$(STAMP) s-tmp-emit
+
+$(INSNEMIT_SEQ_SRC): insn-emit-%.cc: s-insn-emit-%; @true
+$(INSNEMIT_SEQ_SRC:insn-emit-%.cc=s-insn-emit-%): s-insn-emit-%: s-tmp-emit
+	$(SHELL) $(srcdir)/../move-if-change tmp-emit-$*.cc insn-emit-$*.cc
+	$(STAMP) s-insn-emit-$*
+
 # gencheck doesn't read the machine description, and the file produced
 # doesn't use the insn-* convention.
+
+# --> s-check has prerequisite tree-check.h (though nothing to do)
 tree-check.h: s-check ; @true
 s-check : build/gencheck$(build_exeext)
 	$(RUN_GEN) build/gencheck$(build_exeext) > tmp-check.h
diff --git a/gcc/configure b/gcc/configure
index c43bde8174b..00672b18f72 100755
--- a/gcc/configure
+++ b/gcc/configure
@@ -842,6 +842,7 @@  enable_gcov
 enable_shared
 enable_fixed_point
 enable_decimal_float
+DEFAULT_INSNEMIT_PARTITIONS
 DEFAULT_MATCHPD_PARTITIONS
 with_float
 with_cpu
@@ -971,6 +972,7 @@  enable_multilib
 enable_multiarch
 with_stack_clash_protection_guard_size
 with_matchpd_partitions
+with_insnemit_partitions
 enable___cxa_atexit
 enable_decimal_float
 enable_fixed_point
@@ -1839,6 +1841,9 @@  Optional Packages:
   --with-matchpd-partitions=num
                           Set the number of partitions to make for gimple and
                           generic when splitting match.pd. [default=10]
+  --with-insnemit-partitions=num
+                          Set the number of partitions of insn-emit.cc for
+                          genemit to create. [default=10]
   --with-dwarf2           force the default debug format to be DWARF 2 (or
                           later)
   --with-specs=SPECS      add SPECS to driver command-line processing
@@ -7938,6 +7943,21 @@  fi
 
 
 
+# Specify the number of splits of insn-emit.cc to generate.
+
+# Check whether --with-insnemit-partitions was given.
+if test "${with_insnemit_partitions+set}" = set; then :
+  withval=$with_insnemit_partitions; DEFAULT_INSNEMIT_PARTITIONS="$with_insnemit_partitions"
+else
+  DEFAULT_INSNEMIT_PARTITIONS=10
+fi
+
+if (test $DEFAULT_INSNEMIT_PARTITIONS -lt 1); then
+  as_fn_error $? "Invalid value $DEFAULT_INSNEMIT_PARTITIONS for --with-insnemit-partitions. Cannot be negative." "$LINENO" 5
+fi
+
+
+
 # Enable __cxa_atexit for C++.
 # Check whether --enable-__cxa_atexit was given.
 if test "${enable___cxa_atexit+set}" = set; then :
@@ -19923,7 +19943,7 @@  else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 19926 "configure"
+#line 19946 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -20029,7 +20049,7 @@  else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 20032 "configure"
+#line 20052 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
diff --git a/gcc/configure.ac b/gcc/configure.ac
index fb8e32f8ee5..bc0ef734e6c 100644
--- a/gcc/configure.ac
+++ b/gcc/configure.ac
@@ -956,6 +956,19 @@  fi
 
 AC_SUBST(DEFAULT_MATCHPD_PARTITIONS)
 
+# Specify the number of splits of insn-emit.cc to generate.
+AC_ARG_WITH(insnemit-partitions,
+[AS_HELP_STRING([--with-insnemit-partitions=num],
+[Set the number of partitions of insn-emit.cc for genemit to create. [default=10]])],
+[DEFAULT_INSNEMIT_PARTITIONS="$with_insnemit_partitions"], [DEFAULT_INSNEMIT_PARTITIONS=10])
+if (test $DEFAULT_INSNEMIT_PARTITIONS -lt 1); then
+  AC_MSG_ERROR(m4_normalize([
+		Invalid value $DEFAULT_INSNEMIT_PARTITIONS for --with-insnemit-partitions. \
+		Cannot be negative.]))
+fi
+
+AC_SUBST(DEFAULT_INSNEMIT_PARTITIONS)
+
 # Enable __cxa_atexit for C++.
 AC_ARG_ENABLE(__cxa_atexit,
 [AS_HELP_STRING([--enable-__cxa_atexit], [enable __cxa_atexit for C++])],
diff --git a/gcc/genemit.cc b/gcc/genemit.cc
index 1ce0564076d..c9f6a73fff9 100644
--- a/gcc/genemit.cc
+++ b/gcc/genemit.cc
@@ -49,29 +49,29 @@  struct clobber_ent
   struct clobber_ent *next;
 };
 
-static void output_peephole2_scratches	(rtx);
+static void output_peephole2_scratches	(rtx, FILE*);
 
 /* True for <X>_optab if that optab isn't allowed to fail.  */
 static bool nofail_optabs[NUM_OPTABS];
 
 static void
-print_code (RTX_CODE code)
+print_code (RTX_CODE code, FILE *file)
 {
   const char *p1;
   for (p1 = GET_RTX_NAME (code); *p1; p1++)
-    putchar (TOUPPER (*p1));
+    fprintf (file, "%c", TOUPPER (*p1));
 }
 
 static void
-gen_rtx_scratch (rtx x, enum rtx_code subroutine_type)
+gen_rtx_scratch (rtx x, enum rtx_code subroutine_type, FILE *file)
 {
   if (subroutine_type == DEFINE_PEEPHOLE2)
     {
-      printf ("operand%d", XINT (x, 0));
+      fprintf (file, "operand%d", XINT (x, 0));
     }
   else
     {
-      printf ("gen_rtx_SCRATCH (%smode)", GET_MODE_NAME (GET_MODE (x)));
+      fprintf (file, "gen_rtx_SCRATCH (%smode)", GET_MODE_NAME (GET_MODE (x)));
     }
 }
 
@@ -79,7 +79,8 @@  gen_rtx_scratch (rtx x, enum rtx_code subroutine_type)
    substituting any operand references appearing within.  */
 
 static void
-gen_exp (rtx x, enum rtx_code subroutine_type, char *used, md_rtx_info *info)
+gen_exp (rtx x, enum rtx_code subroutine_type, char *used, md_rtx_info *info,
+	 FILE *file)
 {
   RTX_CODE code;
   int i;
@@ -89,7 +90,7 @@  gen_exp (rtx x, enum rtx_code subroutine_type, char *used, md_rtx_info *info)
 
   if (x == 0)
     {
-      printf ("NULL_RTX");
+      fprintf (file, "NULL_RTX");
       return;
     }
 
@@ -103,67 +104,67 @@  gen_exp (rtx x, enum rtx_code subroutine_type, char *used, md_rtx_info *info)
 	{
 	  if (used[XINT (x, 0)])
 	    {
-	      printf ("copy_rtx (operand%d)", XINT (x, 0));
+	      fprintf (file, "copy_rtx (operand%d)", XINT (x, 0));
 	      return;
 	    }
 	  used[XINT (x, 0)] = 1;
 	}
-      printf ("operand%d", XINT (x, 0));
+      fprintf (file, "operand%d", XINT (x, 0));
       return;
 
     case MATCH_OP_DUP:
-      printf ("gen_rtx_fmt_");
+      fprintf (file, "gen_rtx_fmt_");
       for (i = 0; i < XVECLEN (x, 1); i++)
-	printf ("e");
-      printf (" (GET_CODE (operand%d), ", XINT (x, 0));
+	fprintf (file, "e");
+      fprintf (file, " (GET_CODE (operand%d), ", XINT (x, 0));
       if (GET_MODE (x) == VOIDmode)
-	printf ("GET_MODE (operand%d)", XINT (x, 0));
+	fprintf (file, "GET_MODE (operand%d)", XINT (x, 0));
       else
-	printf ("%smode", GET_MODE_NAME (GET_MODE (x)));
+	fprintf (file, "%smode", GET_MODE_NAME (GET_MODE (x)));
       for (i = 0; i < XVECLEN (x, 1); i++)
 	{
-	  printf (",\n\t\t");
-	  gen_exp (XVECEXP (x, 1, i), subroutine_type, used, info);
+	  fprintf (file, ",\n\t\t");
+	  gen_exp (XVECEXP (x, 1, i), subroutine_type, used, info, file);
 	}
-      printf (")");
+      fprintf (file, ")");
       return;
 
     case MATCH_OPERATOR:
-      printf ("gen_rtx_fmt_");
+      fprintf (file, "gen_rtx_fmt_");
       for (i = 0; i < XVECLEN (x, 2); i++)
-	printf ("e");
-      printf (" (GET_CODE (operand%d)", XINT (x, 0));
-      printf (", %smode", GET_MODE_NAME (GET_MODE (x)));
+	fprintf (file, "e");
+      fprintf (file, " (GET_CODE (operand%d)", XINT (x, 0));
+      fprintf (file, ", %smode", GET_MODE_NAME (GET_MODE (x)));
       for (i = 0; i < XVECLEN (x, 2); i++)
 	{
-	  printf (",\n\t\t");
-	  gen_exp (XVECEXP (x, 2, i), subroutine_type, used, info);
+	  fprintf (file, ",\n\t\t");
+	  gen_exp (XVECEXP (x, 2, i), subroutine_type, used, info, file);
 	}
-      printf (")");
+      fprintf (file, ")");
       return;
 
     case MATCH_PARALLEL:
     case MATCH_PAR_DUP:
-      printf ("operand%d", XINT (x, 0));
+      fprintf (file, "operand%d", XINT (x, 0));
       return;
 
     case MATCH_SCRATCH:
-      gen_rtx_scratch (x, subroutine_type);
+      gen_rtx_scratch (x, subroutine_type, file);
       return;
 
     case PC:
-      printf ("pc_rtx");
+      fprintf (file, "pc_rtx");
       return;
     case RETURN:
-      printf ("ret_rtx");
+      fprintf (file, "ret_rtx");
       return;
     case SIMPLE_RETURN:
-      printf ("simple_return_rtx");
+      fprintf (file, "simple_return_rtx");
       return;
     case CLOBBER:
       if (REG_P (XEXP (x, 0)))
 	{
-	  printf ("gen_hard_reg_clobber (%smode, %i)",
+	  fprintf (file, "gen_hard_reg_clobber (%smode, %i)",
 		  GET_MODE_NAME (GET_MODE (XEXP (x, 0))),
 		  REGNO (XEXP (x, 0)));
 	  return;
@@ -172,22 +173,22 @@  gen_exp (rtx x, enum rtx_code subroutine_type, char *used, md_rtx_info *info)
 
     case CONST_INT:
       if (INTVAL (x) == 0)
-	printf ("const0_rtx");
+	fprintf (file, "const0_rtx");
       else if (INTVAL (x) == 1)
-	printf ("const1_rtx");
+	fprintf (file, "const1_rtx");
       else if (INTVAL (x) == -1)
-	printf ("constm1_rtx");
+	fprintf (file, "constm1_rtx");
       else if (-MAX_SAVED_CONST_INT <= INTVAL (x)
 	       && INTVAL (x) <= MAX_SAVED_CONST_INT)
-	printf ("const_int_rtx[MAX_SAVED_CONST_INT + (%d)]",
+	fprintf (file, "const_int_rtx[MAX_SAVED_CONST_INT + (%d)]",
 		(int) INTVAL (x));
       else if (INTVAL (x) == STORE_FLAG_VALUE)
-	printf ("const_true_rtx");
+	fprintf (file, "const_true_rtx");
       else
 	{
-	  printf ("GEN_INT (");
-	  printf (HOST_WIDE_INT_PRINT_DEC_C, INTVAL (x));
-	  printf (")");
+	  fprintf (file, "GEN_INT (");
+	  fprintf (file, HOST_WIDE_INT_PRINT_DEC_C, INTVAL (x));
+	  fprintf (file, ")");
 	}
       return;
 
@@ -195,7 +196,7 @@  gen_exp (rtx x, enum rtx_code subroutine_type, char *used, md_rtx_info *info)
       /* Handle `const_double_zero' rtx.  */
       if (CONST_DOUBLE_REAL_VALUE (x)->cl == rvc_zero)
 	{
-	  printf ("CONST_DOUBLE_ATOF (\"0\", %smode)",
+	  fprintf (file, "CONST_DOUBLE_ATOF (\"0\", %smode)",
 		  GET_MODE_NAME (GET_MODE (x)));
 	  return;
 	}
@@ -210,12 +211,12 @@  gen_exp (rtx x, enum rtx_code subroutine_type, char *used, md_rtx_info *info)
       break;
     }
 
-  printf ("gen_rtx_");
-  print_code (code);
-  printf (" (");
+  fprintf (file, "gen_rtx_");
+  print_code (code, file);
+  fprintf (file, " (");
   if (!always_void_p (code))
     {
-      printf ("%smode", GET_MODE_NAME (GET_MODE (x)));
+      fprintf (file, "%smode", GET_MODE_NAME (GET_MODE (x)));
       sep = ",\n\t";
     }
 
@@ -225,41 +226,41 @@  gen_exp (rtx x, enum rtx_code subroutine_type, char *used, md_rtx_info *info)
     {
       if (fmt[i] == '0')
 	break;
-      fputs (sep, stdout);
+      fputs (sep, file);
       switch (fmt[i])
 	{
 	case 'e': case 'u':
-	  gen_exp (XEXP (x, i), subroutine_type, used, info);
+	  gen_exp (XEXP (x, i), subroutine_type, used, info, file);
 	  break;
 
 	case 'i':
-	  printf ("%u", XINT (x, i));
+	  fprintf (file, "%u", XINT (x, i));
 	  break;
 
 	case 'r':
-	  printf ("%u", REGNO (x));
+	  fprintf (file, "%u", REGNO (x));
 	  break;
 
 	case 'p':
 	  /* We don't have a way of parsing polynomial offsets yet,
 	     and hopefully never will.  */
-	  printf ("%d", SUBREG_BYTE (x).to_constant ());
+	  fprintf (file, "%d", SUBREG_BYTE (x).to_constant ());
 	  break;
 
 	case 's':
-	  printf ("\"%s\"", XSTR (x, i));
+	  fprintf (file, "\"%s\"", XSTR (x, i));
 	  break;
 
 	case 'E':
 	  {
 	    int j;
-	    printf ("gen_rtvec (%d", XVECLEN (x, i));
+	    fprintf (file, "gen_rtvec (%d", XVECLEN (x, i));
 	    for (j = 0; j < XVECLEN (x, i); j++)
 	      {
-		printf (",\n\t\t");
-		gen_exp (XVECEXP (x, i, j), subroutine_type, used, info);
+		fprintf (file, ",\n\t\t");
+		gen_exp (XVECEXP (x, i, j), subroutine_type, used, info, file);
 	      }
-	    printf (")");
+	    fprintf (file, ")");
 	    break;
 	  }
 
@@ -268,14 +269,14 @@  gen_exp (rtx x, enum rtx_code subroutine_type, char *used, md_rtx_info *info)
 	}
       sep = ",\n\t";
     }
-  printf (")");
+  fprintf (file, ")");
 }
 
 /* Output code to emit the instruction patterns in VEC, with each element
    becoming a separate instruction.  USED is as for gen_exp.  */
 
 static void
-gen_emit_seq (rtvec vec, char *used, md_rtx_info *info)
+gen_emit_seq (rtvec vec, char *used, md_rtx_info *info, FILE *file)
 {
   for (int i = 0, len = GET_NUM_ELEM (vec); i < len; ++i)
     {
@@ -283,17 +284,17 @@  gen_emit_seq (rtvec vec, char *used, md_rtx_info *info)
       rtx next = RTVEC_ELT (vec, i);
       if (const char *name = get_emit_function (next))
 	{
-	  printf ("  %s (", name);
-	  gen_exp (next, DEFINE_EXPAND, used, info);
-	  printf (");\n");
+	  fprintf (file, "  %s (", name);
+	  gen_exp (next, DEFINE_EXPAND, used, info, file);
+	  fprintf (file, ");\n");
 	  if (!last_p && needs_barrier_p (next))
-	    printf ("  emit_barrier ();");
+	    fprintf (file, "  emit_barrier ();");
 	}
       else
 	{
-	  printf ("  emit (");
-	  gen_exp (next, DEFINE_EXPAND, used, info);
-	  printf (", %s);\n", last_p ? "false" : "true");
+	  fprintf (file, "  emit (");
+	  gen_exp (next, DEFINE_EXPAND, used, info, file);
+	  fprintf (file, ", %s);\n", last_p ? "false" : "true");
 	}
     }
 }
@@ -303,27 +304,27 @@  gen_emit_seq (rtvec vec, char *used, md_rtx_info *info)
    for use in error messages.  */
 
 static void
-emit_c_code (const char *code, bool can_fail_p, const char *name)
+emit_c_code (const char *code, bool can_fail_p, const char *name, FILE *file)
 {
   if (can_fail_p)
-    printf ("#define FAIL return (end_sequence (), _val)\n");
+    fprintf (file, "#define FAIL return (end_sequence (), _val)\n");
   else
-    printf ("#define FAIL _Pragma (\"GCC error \\\"%s cannot FAIL\\\"\")"
+    fprintf (file, "#define FAIL _Pragma (\"GCC error \\\"%s cannot FAIL\\\"\")"
 	    " (void)0\n", name);
-  printf ("#define DONE return (_val = get_insns (), "
+  fprintf (file, "#define DONE return (_val = get_insns (), "
 	  "end_sequence (), _val)\n");
 
-  rtx_reader_ptr->print_md_ptr_loc (code);
-  printf ("%s\n", code);
+  rtx_reader_ptr->print_md_ptr_loc (code, file);
+  fprintf (file, "%s\n", code);
 
-  printf ("#undef DONE\n");
-  printf ("#undef FAIL\n");
+  fprintf (file, "#undef DONE\n");
+  fprintf (file, "#undef FAIL\n");
 }
 
 /* Generate the `gen_...' function for a DEFINE_INSN.  */
 
 static void
-gen_insn (md_rtx_info *info)
+gen_insn (md_rtx_info *info, FILE *file)
 {
   struct pattern_stats stats;
   int i;
@@ -409,7 +410,7 @@  gen_insn (md_rtx_info *info)
   if (XSTR (insn, 0)[0] == 0 || XSTR (insn, 0)[0] == '*')
     return;
 
-  printf ("/* %s:%d */\n", info->loc.filename, info->loc.lineno);
+  fprintf (file, "/* %s:%d */\n", info->loc.filename, info->loc.lineno);
 
   /* Find out how many operands this function has.  */
   get_pattern_stats (&stats, XVEC (insn, 1));
@@ -417,17 +418,17 @@  gen_insn (md_rtx_info *info)
     fatal_at (info->loc, "match_dup operand number has no match_operand");
 
   /* Output the function name and argument declarations.  */
-  printf ("rtx\ngen_%s (", XSTR (insn, 0));
+  fprintf (file, "rtx\ngen_%s (", XSTR (insn, 0));
   if (stats.num_generator_args)
     for (i = 0; i < stats.num_generator_args; i++)
       if (i)
-	printf (",\n\trtx operand%d ATTRIBUTE_UNUSED", i);
+	fprintf (file, ",\n\trtx operand%d ATTRIBUTE_UNUSED", i);
       else
-	printf ("rtx operand%d ATTRIBUTE_UNUSED", i);
+	fprintf (file, "rtx operand%d ATTRIBUTE_UNUSED", i);
   else
-    printf ("void");
-  printf (")\n");
-  printf ("{\n");
+    fprintf (file, "void");
+  fprintf (file, ")\n");
+  fprintf (file, "{\n");
 
   /* Output code to construct and return the rtl for the instruction body.  */
 
@@ -436,16 +437,16 @@  gen_insn (md_rtx_info *info)
   char *used = (XVECLEN (insn, 1) == 1
 		? NULL
 		: XCNEWVEC (char, stats.num_generator_args));
-  printf ("  return ");
-  gen_exp (pattern, DEFINE_INSN, used, info);
-  printf (";\n}\n\n");
+  fprintf (file, "  return ");
+  gen_exp (pattern, DEFINE_INSN, used, info, file);
+  fprintf (file, ";\n}\n\n");
   XDELETEVEC (used);
 }
 
 /* Generate the `gen_...' function for a DEFINE_EXPAND.  */
 
 static void
-gen_expand (md_rtx_info *info)
+gen_expand (md_rtx_info *info, FILE *file)
 {
   struct pattern_stats stats;
   int i;
@@ -466,17 +467,17 @@  gen_expand (md_rtx_info *info)
 			 "numbers above all other operands", XSTR (expand, 0));
 
   /* Output the function name and argument declarations.  */
-  printf ("rtx\ngen_%s (", XSTR (expand, 0));
+  fprintf (file, "rtx\ngen_%s (", XSTR (expand, 0));
   if (stats.num_generator_args)
     for (i = 0; i < stats.num_generator_args; i++)
       if (i)
-	printf (",\n\trtx operand%d", i);
+	fprintf (file, ",\n\trtx operand%d", i);
       else
-	printf ("rtx operand%d", i);
+	fprintf (file, "rtx operand%d", i);
   else
-    printf ("void");
-  printf (")\n");
-  printf ("{\n");
+    fprintf (file, "void");
+  fprintf (file, ")\n");
+  fprintf (file, "{\n");
 
   /* If we don't have any C code to write, only one insn is being written,
      and no MATCH_DUPs are present, we can just return the desired insn
@@ -485,18 +486,18 @@  gen_expand (md_rtx_info *info)
       && stats.max_opno >= stats.max_dup_opno
       && XVECLEN (expand, 1) == 1)
     {
-      printf ("  return ");
-      gen_exp (XVECEXP (expand, 1, 0), DEFINE_EXPAND, NULL, info);
-      printf (";\n}\n\n");
+      fprintf (file, "  return ");
+      gen_exp (XVECEXP (expand, 1, 0), DEFINE_EXPAND, NULL, info, file);
+      fprintf (file, ";\n}\n\n");
       return;
     }
 
   /* For each operand referred to only with MATCH_DUPs,
      make a local variable.  */
   for (i = stats.num_generator_args; i <= stats.max_dup_opno; i++)
-    printf ("  rtx operand%d;\n", i);
-  printf ("  rtx_insn *_val = 0;\n");
-  printf ("  start_sequence ();\n");
+    fprintf (file, "  rtx operand%d;\n", i);
+  fprintf (file, "  rtx_insn *_val = 0;\n");
+  fprintf (file, "  start_sequence ();\n");
 
   /* The fourth operand of DEFINE_EXPAND is some code to be executed
      before the actual construction.
@@ -506,13 +507,13 @@  gen_expand (md_rtx_info *info)
      So copy the operand values there before executing it.  */
   if (XSTR (expand, 3) && *XSTR (expand, 3))
     {
-      printf ("  {\n");
+      fprintf (file, "  {\n");
       if (stats.num_operand_vars > 0)
-	printf ("    rtx operands[%d];\n", stats.num_operand_vars);
+	fprintf (file, "    rtx operands[%d];\n", stats.num_operand_vars);
 
       /* Output code to copy the arguments into `operands'.  */
       for (i = 0; i < stats.num_generator_args; i++)
-	printf ("    operands[%d] = operand%d;\n", i, i);
+	fprintf (file, "    operands[%d] = operand%d;\n", i, i);
 
       /* Output the special code to be executed before the sequence
 	 is generated.  */
@@ -524,7 +525,7 @@  gen_expand (md_rtx_info *info)
 	  if (nofail_optabs[p.op])
 	    can_fail_p = false;
 	}
-      emit_c_code (XSTR (expand, 3), can_fail_p, XSTR (expand, 0));
+      emit_c_code (XSTR (expand, 3), can_fail_p, XSTR (expand, 0), file);
 
       /* Output code to copy the arguments back out of `operands'
 	 (unless we aren't going to use them at all).  */
@@ -532,29 +533,29 @@  gen_expand (md_rtx_info *info)
 	{
 	  for (i = 0; i <= MAX (stats.max_opno, stats.max_dup_opno); i++)
 	    {
-	      printf ("    operand%d = operands[%d];\n", i, i);
-	      printf ("    (void) operand%d;\n", i);
+	      fprintf (file, "    operand%d = operands[%d];\n", i, i);
+	      fprintf (file, "    (void) operand%d;\n", i);
 	    }
 	}
-      printf ("  }\n");
+      fprintf (file, "  }\n");
     }
 
   used = XCNEWVEC (char, stats.num_operand_vars);
-  gen_emit_seq (XVEC (expand, 1), used, info);
+  gen_emit_seq (XVEC (expand, 1), used, info, file);
   XDELETEVEC (used);
 
   /* Call `get_insns' to extract the list of all the
      insns emitted within this gen_... function.  */
 
-  printf ("  _val = get_insns ();\n");
-  printf ("  end_sequence ();\n");
-  printf ("  return _val;\n}\n\n");
+  fprintf (file, "  _val = get_insns ();\n");
+  fprintf (file, "  end_sequence ();\n");
+  fprintf (file, "  return _val;\n}\n\n");
 }
 
 /* Like gen_expand, but generates insns resulting from splitting SPLIT.  */
 
 static void
-gen_split (md_rtx_info *info)
+gen_split (md_rtx_info *info, FILE *file)
 {
   struct pattern_stats stats;
   int i;
@@ -580,62 +581,62 @@  gen_split (md_rtx_info *info)
   /* Output the prototype, function name and argument declarations.  */
   if (GET_CODE (split) == DEFINE_PEEPHOLE2)
     {
-      printf ("extern rtx_insn *gen_%s_%d (rtx_insn *, rtx *);\n",
+      fprintf (file, "extern rtx_insn *gen_%s_%d (rtx_insn *, rtx *);\n",
 	      name, info->index);
-      printf ("rtx_insn *\ngen_%s_%d (rtx_insn *curr_insn ATTRIBUTE_UNUSED,"
+      fprintf (file, "rtx_insn *\ngen_%s_%d (rtx_insn *curr_insn ATTRIBUTE_UNUSED,"
 	      " rtx *operands%s)\n",
 	      name, info->index, unused);
     }
   else
     {
-      printf ("extern rtx_insn *gen_split_%d (rtx_insn *, rtx *);\n",
+      fprintf (file, "extern rtx_insn *gen_split_%d (rtx_insn *, rtx *);\n",
 	      info->index);
-      printf ("rtx_insn *\ngen_split_%d "
+      fprintf (file, "rtx_insn *\ngen_split_%d "
 	      "(rtx_insn *curr_insn ATTRIBUTE_UNUSED, rtx *operands%s)\n",
 	      info->index, unused);
     }
-  printf ("{\n");
+  fprintf (file, "{\n");
 
   /* Declare all local variables.  */
   for (i = 0; i < stats.num_operand_vars; i++)
-    printf ("  rtx operand%d;\n", i);
-  printf ("  rtx_insn *_val = NULL;\n");
+    fprintf (file, "  rtx operand%d;\n", i);
+  fprintf (file, "  rtx_insn *_val = NULL;\n");
 
   if (GET_CODE (split) == DEFINE_PEEPHOLE2)
-    output_peephole2_scratches (split);
+    output_peephole2_scratches (split, file);
 
   const char *fn = info->loc.filename;
   for (const char *p = fn; *p; p++)
     if (*p == '/')
       fn = p + 1;
 
-  printf ("  if (dump_file)\n");
-  printf ("    fprintf (dump_file, \"Splitting with gen_%s_%d (%s:%d)\\n\");\n",
+  fprintf (file, "  if (dump_file)\n");
+  fprintf (file, "    fprintf (dump_file, \"Splitting with gen_%s_%d (%s:%d)\\n\");\n",
 	  name, info->index, fn, info->loc.lineno);
 
-  printf ("  start_sequence ();\n");
+  fprintf (file, "  start_sequence ();\n");
 
   /* The fourth operand of DEFINE_SPLIT is some code to be executed
      before the actual construction.  */
 
   if (XSTR (split, 3))
-    emit_c_code (XSTR (split, 3), true, name);
+    emit_c_code (XSTR (split, 3), true, name, file);
 
   /* Output code to copy the arguments back out of `operands'  */
   for (i = 0; i < stats.num_operand_vars; i++)
     {
-      printf ("  operand%d = operands[%d];\n", i, i);
-      printf ("  (void) operand%d;\n", i);
+      fprintf (file, "  operand%d = operands[%d];\n", i, i);
+      fprintf (file, "  (void) operand%d;\n", i);
     }
 
-  gen_emit_seq (XVEC (split, 2), used, info);
+  gen_emit_seq (XVEC (split, 2), used, info, file);
 
   /* Call `get_insns' to make a list of all the
      insns emitted within this gen_... function.  */
 
-  printf ("  _val = get_insns ();\n");
-  printf ("  end_sequence ();\n");
-  printf ("  return _val;\n}\n\n");
+  fprintf (file, "  _val = get_insns ();\n");
+  fprintf (file, "  end_sequence ();\n");
+  fprintf (file, "  return _val;\n}\n\n");
 
   free (used);
 }
@@ -645,37 +646,37 @@  gen_split (md_rtx_info *info)
    the end of the vector.  */
 
 static void
-output_add_clobbers (md_rtx_info *info)
+output_add_clobbers (md_rtx_info *info, FILE *file)
 {
   struct clobber_pat *clobber;
   struct clobber_ent *ent;
   int i;
 
-  printf ("\n\nvoid\nadd_clobbers (rtx pattern ATTRIBUTE_UNUSED, int insn_code_number)\n");
-  printf ("{\n");
-  printf ("  switch (insn_code_number)\n");
-  printf ("    {\n");
+  fprintf (file, "\n\nvoid\nadd_clobbers (rtx pattern ATTRIBUTE_UNUSED, int insn_code_number)\n");
+  fprintf (file, "{\n");
+  fprintf (file, "  switch (insn_code_number)\n");
+  fprintf (file, "    {\n");
 
   for (clobber = clobber_list; clobber; clobber = clobber->next)
     {
       for (ent = clobber->insns; ent; ent = ent->next)
-	printf ("    case %d:\n", ent->code_number);
+	fprintf (file, "    case %d:\n", ent->code_number);
 
       for (i = clobber->first_clobber; i < XVECLEN (clobber->pattern, 1); i++)
 	{
-	  printf ("      XVECEXP (pattern, 0, %d) = ", i);
+	  fprintf (file, "      XVECEXP (pattern, 0, %d) = ", i);
 	  gen_exp (XVECEXP (clobber->pattern, 1, i),
-		   GET_CODE (clobber->pattern), NULL, info);
-	  printf (";\n");
+		   GET_CODE (clobber->pattern), NULL, info, file);
+	  fprintf (file, ";\n");
 	}
 
-      printf ("      break;\n\n");
+      fprintf (file, "      break;\n\n");
     }
 
-  printf ("    default:\n");
-  printf ("      gcc_unreachable ();\n");
-  printf ("    }\n");
-  printf ("}\n");
+  fprintf (file, "    default:\n");
+  fprintf (file, "      gcc_unreachable ();\n");
+  fprintf (file, "    }\n");
+  fprintf (file, "}\n");
 }
 
 /* Write a function, `added_clobbers_hard_reg_p' that is given an insn_code
@@ -684,17 +685,17 @@  output_add_clobbers (md_rtx_info *info)
    SCRATCH.  */
 
 static void
-output_added_clobbers_hard_reg_p (void)
+output_added_clobbers_hard_reg_p (FILE *file)
 {
   struct clobber_pat *clobber;
   struct clobber_ent *ent;
   int clobber_p;
   bool used;
 
-  printf ("\n\nbool\nadded_clobbers_hard_reg_p (int insn_code_number)\n");
-  printf ("{\n");
-  printf ("  switch (insn_code_number)\n");
-  printf ("    {\n");
+  fprintf (file, "\n\nbool\nadded_clobbers_hard_reg_p (int insn_code_number)\n");
+  fprintf (file, "{\n");
+  fprintf (file, "  switch (insn_code_number)\n");
+  fprintf (file, "    {\n");
 
   for (clobber_p = 0; clobber_p <= 1; clobber_p++)
     {
@@ -703,25 +704,25 @@  output_added_clobbers_hard_reg_p (void)
 	if (clobber->has_hard_reg == clobber_p)
 	  for (ent = clobber->insns; ent; ent = ent->next)
 	    {
-	      printf ("    case %d:\n", ent->code_number);
+	      fprintf (file, "    case %d:\n", ent->code_number);
 	      used = true;
 	    }
 
       if (used)
-	printf ("      return %s;\n\n", clobber_p ? "true" : "false");
+	fprintf (file, "      return %s;\n\n", clobber_p ? "true" : "false");
     }
 
-  printf ("    default:\n");
-  printf ("      gcc_unreachable ();\n");
-  printf ("    }\n");
-  printf ("}\n");
+  fprintf (file, "    default:\n");
+  fprintf (file, "      gcc_unreachable ();\n");
+  fprintf (file, "    }\n");
+  fprintf (file, "}\n");
 }
 
 /* Generate code to invoke find_free_register () as needed for the
    scratch registers used by the peephole2 pattern in SPLIT.  */
 
 static void
-output_peephole2_scratches (rtx split)
+output_peephole2_scratches (rtx split, FILE *file)
 {
   int i;
   int insn_nr = 0;
@@ -746,12 +747,12 @@  output_peephole2_scratches (rtx split)
 
 	  if (first)
 	    {
-	      printf ("  HARD_REG_SET _regs_allocated;\n");
-	      printf ("  CLEAR_HARD_REG_SET (_regs_allocated);\n");
+	      fprintf (file, "  HARD_REG_SET _regs_allocated;\n");
+	      fprintf (file, "  CLEAR_HARD_REG_SET (_regs_allocated);\n");
 	      first = false;
 	    }
 
-	  printf ("  if ((operands[%d] = peep2_find_free_register (%d, %d, \"%s\", %smode, &_regs_allocated)) == NULL_RTX)\n\
+	  fprintf (file, "  if ((operands[%d] = peep2_find_free_register (%d, %d, \"%s\", %smode, &_regs_allocated)) == NULL_RTX)\n\
     return NULL;\n",
 		  XINT (elt, 0),
 		  insn_nr, last_insn_nr,
@@ -767,50 +768,50 @@  output_peephole2_scratches (rtx split)
 /* Print "arg<N>" parameter declarations for each argument N of ONAME.  */
 
 static void
-print_overload_arguments (overloaded_name *oname)
+print_overload_arguments (overloaded_name *oname, FILE *file)
 {
   for (unsigned int i = 0; i < oname->arg_types.length (); ++i)
-    printf ("%s%s arg%d", i == 0 ? "" : ", ", oname->arg_types[i], i);
+    fprintf (file, "%s%s arg%d", i == 0 ? "" : ", ", oname->arg_types[i], i);
 }
 
 /* Print code to test whether INSTANCE should be chosen, given that
    argument N of the overload is available as "arg<N>".  */
 
 static void
-print_overload_test (overloaded_instance *instance)
+print_overload_test (overloaded_instance *instance, FILE *file)
 {
   for (unsigned int i = 0; i < instance->arg_values.length (); ++i)
-    printf ("%sarg%d == %s", i == 0 ? "  if (" : "\n      && ",
+    fprintf (file, "%sarg%d == %s", i == 0 ? "  if (" : "\n      && ",
 	    i, instance->arg_values[i]);
-  printf (")\n");
+  fprintf (file, ")\n");
 }
 
 /* Emit a maybe_code_for_* function for ONAME.  */
 
 static void
-handle_overloaded_code_for (overloaded_name *oname)
+handle_overloaded_code_for (overloaded_name *oname, FILE *file)
 {
   /* Print the function prototype.  */
-  printf ("\ninsn_code\nmaybe_code_for_%s (", oname->name);
-  print_overload_arguments (oname);
-  printf (")\n{\n");
+  fprintf (file, "\ninsn_code\nmaybe_code_for_%s (", oname->name);
+  print_overload_arguments (oname, file);
+  fprintf (file, ")\n{\n");
 
   /* Use a sequence of "if" statements for each instance.  */
   for (overloaded_instance *instance = oname->first_instance;
        instance; instance = instance->next)
     {
-      print_overload_test (instance);
-      printf ("    return CODE_FOR_%s;\n", instance->name);
+      print_overload_test (instance, file);
+      fprintf (file, "    return CODE_FOR_%s;\n", instance->name);
     }
 
   /* Return null if no match was found.  */
-  printf ("  return CODE_FOR_nothing;\n}\n");
+  fprintf (file, "  return CODE_FOR_nothing;\n}\n");
 }
 
 /* Emit a maybe_gen_* function for ONAME.  */
 
 static void
-handle_overloaded_gen (overloaded_name *oname)
+handle_overloaded_gen (overloaded_name *oname, FILE *file)
 {
   unsigned HOST_WIDE_INT seen = 0;
   /* All patterns must have the same number of operands.  */
@@ -827,25 +828,25 @@  handle_overloaded_gen (overloaded_name *oname)
       seen |= mask;
 
       /* Print the function prototype.  */
-      printf ("\nrtx\nmaybe_gen_%s (", oname->name);
-      print_overload_arguments (oname);
+      fprintf (file, "\nrtx\nmaybe_gen_%s (", oname->name);
+      print_overload_arguments (oname, file);
       for (int i = 0; i < stats.num_generator_args; ++i)
-	printf (", rtx x%d", i);
-      printf (")\n{\n");
+	fprintf (file, ", rtx x%d", i);
+      fprintf (file, ")\n{\n");
 
       /* Use maybe_code_for_*, instead of duplicating the selection
 	 logic here.  */
-      printf ("  insn_code code = maybe_code_for_%s (", oname->name);
+      fprintf (file, "  insn_code code = maybe_code_for_%s (", oname->name);
       for (unsigned int i = 0; i < oname->arg_types.length (); ++i)
-	printf ("%sarg%d", i == 0 ? "" : ", ", i);
-      printf (");\n"
+	fprintf (file, "%sarg%d", i == 0 ? "" : ", ", i);
+      fprintf (file, ");\n"
 	      "  if (code != CODE_FOR_nothing)\n"
 	      "    {\n"
 	      "      gcc_assert (insn_data[code].n_generator_args == %d);\n"
 	      "      return GEN_FCN (code) (", stats.num_generator_args);
       for (int i = 0; i < stats.num_generator_args; ++i)
-	printf ("%sx%d", i == 0 ? "" : ", ", i);
-      printf (");\n"
+	fprintf (file, "%sx%d", i == 0 ? "" : ", ", i);
+      fprintf (file, ");\n"
 	      "    }\n"
 	      "  else\n"
 	      "    return NULL_RTX;\n"
@@ -853,12 +854,78 @@  handle_overloaded_gen (overloaded_name *oname)
     }
 }
 
+void
+print_header (FILE *file)
+{
+  fprintf (file, "/* Generated automatically by the program `genemit'\n\
+from the machine description file `md'.  */\n\n");
+
+  fprintf (file, "#define IN_TARGET_CODE 1\n");
+  fprintf (file, "#include \"config.h\"\n");
+  fprintf (file, "#include \"system.h\"\n");
+  fprintf (file, "#include \"coretypes.h\"\n");
+  fprintf (file, "#include \"backend.h\"\n");
+  fprintf (file, "#include \"predict.h\"\n");
+  fprintf (file, "#include \"tree.h\"\n");
+  fprintf (file, "#include \"rtl.h\"\n");
+  fprintf (file, "#include \"alias.h\"\n");
+  fprintf (file, "#include \"varasm.h\"\n");
+  fprintf (file, "#include \"stor-layout.h\"\n");
+  fprintf (file, "#include \"calls.h\"\n");
+  fprintf (file, "#include \"memmodel.h\"\n");
+  fprintf (file, "#include \"tm_p.h\"\n");
+  fprintf (file, "#include \"flags.h\"\n");
+  fprintf (file, "#include \"insn-config.h\"\n");
+  fprintf (file, "#include \"expmed.h\"\n");
+  fprintf (file, "#include \"dojump.h\"\n");
+  fprintf (file, "#include \"explow.h\"\n");
+  fprintf (file, "#include \"emit-rtl.h\"\n");
+  fprintf (file, "#include \"stmt.h\"\n");
+  fprintf (file, "#include \"expr.h\"\n");
+  fprintf (file, "#include \"insn-codes.h\"\n");
+  fprintf (file, "#include \"optabs.h\"\n");
+  fprintf (file, "#include \"dfp.h\"\n");
+  fprintf (file, "#include \"output.h\"\n");
+  fprintf (file, "#include \"recog.h\"\n");
+  fprintf (file, "#include \"df.h\"\n");
+  fprintf (file, "#include \"resource.h\"\n");
+  fprintf (file, "#include \"reload.h\"\n");
+  fprintf (file, "#include \"diagnostic-core.h\"\n");
+  fprintf (file, "#include \"regs.h\"\n");
+  fprintf (file, "#include \"tm-constrs.h\"\n");
+  fprintf (file, "#include \"ggc.h\"\n");
+  fprintf (file, "#include \"target.h\"\n\n");
+}
+
+static const char *output_file_name = NULL;
+static int nfiles = 10;
+
+static bool
+handle_arg (const char *arg)
+{
+  if (arg[1] == 'O')
+    {
+      output_file_name = &arg[2];
+      return true;
+    }
+
+  if (arg[1] == 'P')
+    {
+      sscanf (&arg[2], "%d", &nfiles);
+      return true;
+    }
+  return false;
+}
+
 int
 main (int argc, const char **argv)
 {
   progname = "genemit";
 
-  if (!init_rtx_reader_args (argc, argv))
+  if (!init_rtx_reader_args_cb (argc, argv, handle_arg))
+    return (FATAL_EXIT_CODE);
+
+  if (output_file_name == NULL)
     return (FATAL_EXIT_CODE);
 
 #define DEF_INTERNAL_OPTAB_FN(NAME, FLAGS, OPTAB, TYPE) \
@@ -868,86 +935,77 @@  main (int argc, const char **argv)
   /* Assign sequential codes to all entries in the machine description
      in parallel with the tables in insn-output.cc.  */
 
-  printf ("/* Generated automatically by the program `genemit'\n\
-from the machine description file `md'.  */\n\n");
+  int npatterns = count_patterns ();
+  md_rtx_info info;
 
-  printf ("#define IN_TARGET_CODE 1\n");
-  printf ("#include \"config.h\"\n");
-  printf ("#include \"system.h\"\n");
-  printf ("#include \"coretypes.h\"\n");
-  printf ("#include \"backend.h\"\n");
-  printf ("#include \"predict.h\"\n");
-  printf ("#include \"tree.h\"\n");
-  printf ("#include \"rtl.h\"\n");
-  printf ("#include \"alias.h\"\n");
-  printf ("#include \"varasm.h\"\n");
-  printf ("#include \"stor-layout.h\"\n");
-  printf ("#include \"calls.h\"\n");
-  printf ("#include \"memmodel.h\"\n");
-  printf ("#include \"tm_p.h\"\n");
-  printf ("#include \"flags.h\"\n");
-  printf ("#include \"insn-config.h\"\n");
-  printf ("#include \"expmed.h\"\n");
-  printf ("#include \"dojump.h\"\n");
-  printf ("#include \"explow.h\"\n");
-  printf ("#include \"emit-rtl.h\"\n");
-  printf ("#include \"stmt.h\"\n");
-  printf ("#include \"expr.h\"\n");
-  printf ("#include \"insn-codes.h\"\n");
-  printf ("#include \"optabs.h\"\n");
-  printf ("#include \"dfp.h\"\n");
-  printf ("#include \"output.h\"\n");
-  printf ("#include \"recog.h\"\n");
-  printf ("#include \"df.h\"\n");
-  printf ("#include \"resource.h\"\n");
-  printf ("#include \"reload.h\"\n");
-  printf ("#include \"diagnostic-core.h\"\n");
-  printf ("#include \"regs.h\"\n");
-  printf ("#include \"tm-constrs.h\"\n");
-  printf ("#include \"ggc.h\"\n");
-  printf ("#include \"target.h\"\n\n");
+  int npatterns_per_file = npatterns / nfiles + 1;
 
-  /* Read the machine description.  */
+  gcc_assert (npatterns_per_file > 1);
 
-  md_rtx_info info;
+  const int filename_len = 128;
+  char filename[filename_len];
+
+  int filenum = 0;
+  int count = 0;
+  FILE *file = NULL;
+
+  /* Read the machine description.  */
   while (read_md_rtx (&info))
-    switch (GET_CODE (info.def))
-      {
-      case DEFINE_INSN:
-	gen_insn (&info);
-	break;
+    {
+      if (count == 0 || count == npatterns_per_file)
+	{
+	  if (file)
+	    if (fclose (file) != 0)
+	      return FATAL_EXIT_CODE;
 
-      case DEFINE_EXPAND:
-	printf ("/* %s:%d */\n", info.loc.filename, info.loc.lineno);
-	gen_expand (&info);
-	break;
+	  filenum++;
+	  snprintf (filename, filename_len, "%s-%d.cc", output_file_name,
+		    filenum);
+	  file = fopen (filename, "w");
 
-      case DEFINE_SPLIT:
-	printf ("/* %s:%d */\n", info.loc.filename, info.loc.lineno);
-	gen_split (&info);
-	break;
+	  print_header (file);
+	  count = 0;
+	}
 
-      case DEFINE_PEEPHOLE2:
-	printf ("/* %s:%d */\n", info.loc.filename, info.loc.lineno);
-	gen_split (&info);
-	break;
+      switch (GET_CODE (info.def))
+	{
+	case DEFINE_INSN:
+	  gen_insn (&info, file);
+	  break;
 
-      default:
-	break;
-      }
+	case DEFINE_EXPAND:
+	  fprintf (file, "/* %s:%d */\n", info.loc.filename, info.loc.lineno);
+	  gen_expand (&info, file);
+	  break;
+
+	case DEFINE_SPLIT:
+	  fprintf (file, "/* %s:%d */\n", info.loc.filename, info.loc.lineno);
+	  gen_split (&info, file);
+	  break;
+
+	case DEFINE_PEEPHOLE2:
+	  fprintf (file, "/* %s:%d */\n", info.loc.filename, info.loc.lineno);
+	  gen_split (&info, file);
+	  break;
+
+	default:
+	  break;
+	}
+
+      count++;
+    }
 
   /* Write out the routines to add CLOBBERs to a pattern and say whether they
      clobber a hard reg.  */
-  output_add_clobbers (&info);
-  output_added_clobbers_hard_reg_p ();
+  output_add_clobbers (&info, file);
+  output_added_clobbers_hard_reg_p (file);
 
   for (overloaded_name *oname = rtx_reader_ptr->get_overloads ();
        oname; oname = oname->next)
     {
-      handle_overloaded_code_for (oname);
-      handle_overloaded_gen (oname);
+      handle_overloaded_code_for (oname, file);
+      handle_overloaded_gen (oname, file);
     }
 
-  fflush (stdout);
-  return (ferror (stdout) != 0 ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE);
+  return (fclose (file) != 0 ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE);
 }
diff --git a/gcc/gensupport.cc b/gcc/gensupport.cc
index dd920d673b4..367ba1a30a4 100644
--- a/gcc/gensupport.cc
+++ b/gcc/gensupport.cc
@@ -3131,6 +3131,42 @@  init_rtx_reader_args (int argc, const char **argv)
   return init_rtx_reader_args_cb (argc, argv, 0);
 }
 
+/* Count the number of patterns in all queues and return the count.  */
+int
+count_patterns ()
+{
+  int count = 0;
+  class queue_elem *cur = define_attr_queue;
+  while (cur != NULL)
+    {
+      count++;
+      cur = cur->next;
+    }
+
+  cur = define_pred_queue;
+  while (cur != NULL)
+    {
+      count++;
+      cur = cur->next;
+    }
+
+  cur = define_insn_queue;
+  while (cur != NULL)
+    {
+      count++;
+      cur = cur->next;
+    }
+
+  cur = other_queue;
+  while (cur != NULL)
+    {
+      count++;
+      cur = cur->next;
+    }
+
+  return count;
+}
+
 /* Try to read a single rtx from the file.  Return true on success,
    describing it in *INFO.  */
 
diff --git a/gcc/gensupport.h b/gcc/gensupport.h
index 7925e22ed41..7396118714b 100644
--- a/gcc/gensupport.h
+++ b/gcc/gensupport.h
@@ -130,6 +130,7 @@  extern rtx add_implicit_parallel (rtvec);
 extern rtx_reader *init_rtx_reader_args_cb (int, const char **,
 					    bool (*)(const char *));
 extern rtx_reader *init_rtx_reader_args (int, const char **);
+extern int count_patterns ();
 extern bool read_md_rtx (md_rtx_info *);
 extern unsigned int get_num_insn_codes ();
 
diff --git a/gcc/read-md.cc b/gcc/read-md.cc
index fd38818e3a3..46ab9065e3e 100644
--- a/gcc/read-md.cc
+++ b/gcc/read-md.cc
@@ -132,9 +132,9 @@  md_reader::fprint_md_ptr_loc (FILE *outf, const void *ptr)
 
 /* Special fprint_md_ptr_loc for writing to STDOUT.  */
 void
-md_reader::print_md_ptr_loc (const void *ptr)
+md_reader::print_md_ptr_loc (const void *ptr, FILE *file)
 {
-  fprint_md_ptr_loc (stdout, ptr);
+  fprint_md_ptr_loc (file, ptr);
 }
 
 /* Return a condition that satisfies both COND1 and COND2.  Either string
diff --git a/gcc/read-md.h b/gcc/read-md.h
index b309c9c3deb..2adcb58478f 100644
--- a/gcc/read-md.h
+++ b/gcc/read-md.h
@@ -194,7 +194,7 @@  class md_reader
   const struct ptr_loc *get_md_ptr_loc (const void *ptr);
   void copy_md_ptr_loc (const void *new_ptr, const void *old_ptr);
   void fprint_md_ptr_loc (FILE *outf, const void *ptr);
-  void print_md_ptr_loc (const void *ptr);
+  void print_md_ptr_loc (const void *ptr, FILE * = stdout);
 
   struct enum_type *lookup_enum_type (const char *name);
   void traverse_enum_types (htab_trav callback, void *info);