diff mbox

[10/16] Introduce class function_reader (v3)

Message ID 1475684110-2521-11-git-send-email-dmalcolm@redhat.com
State New
Headers show

Commit Message

David Malcolm Oct. 5, 2016, 4:15 p.m. UTC
This patch implements class function_reader, which implements
the dump-parsing functionality used in the rest of the kit.

Changed in v3:
* rather than hardcoding the selftest input dumps as escaped C strings
in the source code, move them out into separate files, using
selftest::locate_file
* update to use the new "print_rtx_function" output format, parsing
"(cfg)" and "(crtl)" directives to reconstruct the CFG and various
metadata.
* eliminated regno_remapper in favor of using the absence of a trailing
register name to detect pseudos
* significantly cleaner handling of print_rtx's various "warts",
moving the parsing from read-rtl.c to read-rtl-function.c, by making
read_rtx_operand virtual.  Split the implementation up into smaller
functions to make it easier to grok.
* more test coverage
* lots of other cleanups
(some FIXMEs and TODOs remain)

Blurb from original version:

This patch generalizes the RTL-reading capabilities so that they
can be run on the host as well as the build machine.
The available rtx in rtl.def changes dramatically between these
two configurations, so a fair amount of #ifdef GENERATOR_FILE is
required to express this.

This patch introduces a function_reader subclass of rtx_reader,
capable of reading an RTL function dump (or part of one),
reconstructing a cfun with a CFG and basic blocks containing insns.

gcc/ChangeLog:
	* Makefile.in (OBJS): Add errors.o, read-md.o, read-rtl.o,
	read-rtl-function.o, and selftest-rtl.o.
	* errors.c: Use consistent pattern for bconfig.h vs config.h
	includes.
	(progname): Wrap with #ifdef GENERATOR_FILE.
	(error): Likewise.  Add "error: " to message.
	(fatal): Likewise.
	(internal_error): Likewise.
	(trim_filename): Likewise.
	(fancy_abort): Likewise.
	* errors.h (struct file_location): Move here from read-md.h.
	(file_location::file_location): Likewise.
	(error_at): New decl.
	* function-tests.c (selftest::verify_three_block_rtl_cfg): Remove
	"static".
	* function.c (instantiate_decls): Guard call to
	instantiate_decls_1 with if (DECL_INITIAL (fndecl)).
	* print-rtl.c (print_rtx): Print "(nil)" rather than an empty
	string for NULL strings.  Print "(nil)" for NULL basic blocks.
	* read-md.c (read_skip_construct): Provide forward decl.
	(read_skip_spaces): Support '/'.
	(read_name): Rename to...
	(read_name_1): ...this new static function, adding "out_loc" param,
	and converting "missing name or number" to returning false, rather
	than failing.
	(read_name): Reimplement in terms of read_name_1.
	(read_name_or_nil): New function.
	(read_string): Handle "(nil)" by returning NULL.  */
	* read-md.h (struct file_location): Move to errors.h.
	(file_location::file_location): Likewise.
	Include errors.h.
	(rtx_reader::read_rtx_operand): Make virtual.
	(rtx_reader::read_until): New decl.
	(rtx_reader::handle_any_trailing_information): New virtual func.
	(rtx_reader::postprocess): New virtual func.
	(rtx_reader::m_in_call_function_usage): New field.
	(read_name): Convert return type from void to file_location.
	(read_name_or_nil): New decl.
	* read-rtl-function.c: New file.
	* read-rtl-function.h: New file.
	* read-rtl.c: Potentially include config.h rather than bconfig.h.
	For host, include function.h and emit-rtl.h.
	(apply_subst_iterator): Wrap with #ifdef GENERATOR_FILE.
	(bind_subst_iter_and_attr): Likewise.
	(add_condition_to_string): Likewise.
	(add_condition_to_rtx): Likewise.
	(apply_attribute_uses): Likewise.
	(add_current_iterators): Likewise.
	(apply_iterators): Likewise.
	(initialize_iterators): Guard usage of apply_subst_iterator with
	#ifdef GENERATOR_FILE.
	(read_conditions): Wrap with #ifdef GENERATOR_FILE.
	(read_mapping): Likewise.
	(add_define_attr_for_define_subst): Likewise.
	(add_define_subst_attr): Likewise.
	(read_subst_mapping): Likewise.
	(check_code_iterator): Likewise.
	(check_code_iterator): Likewise.
	(read_rtx): Likewise.  Move one-time initialization logic to...
	(one_time_initialization): New function.
	(rtx_reader::read_until): New method.
	(read_flags): New function.
	(parse_reg_note_name): New function.
	(rtx_reader::read_rtx_code): Initialize "iterator" to NULL.
	Call one_time_initialization.  Wrap iterator lookup within
	#ifdef GENERATOR_FILE.  Add parsing support for RTL dumps,
	mirroring the special-cases in print_rtx, by calling read_flags,
	reading REG_NOTE names, INSN_UID values, and calling
	handle_any_trailing_information.
	(rtx_reader::read_rtx_operand): Handle case 'e'.  When on host,
	reallocate XSTR and XTMPL fields in the GC-managed heap.
	(rtx_reader::read_nested_rtx): Call the postprocess vfunc on the
	return_rtx.
	(rtx_reader::rtx_reader): Initialize m_in_call_function_usage.
	* rtl.h (read_rtx): Wrap decl with #ifdef GENERATOR_FILE.
	* selftest-rtl.c: New file.
	* selftest-rtl.h: New file.
	* selftest-run-tests.c (selftest::run_tests): Run
	read_rtl_function_c_tests.
	* selftest.h  (selftest::read_rtl_function_c_tests): New decl.
	* tree-dfa.c (ssa_default_def): Return NULL_TREE for rtl function
	dumps.

gcc/testsuite/ChangeLog:
	* selftests: New subdirectory.
	* selftests/aarch64: New subdirectory.
	* selftests/aarch64/times-two.rtl: New file.
	* selftests/asr_div1.rtl: New file.
	* selftests/cfg-test.rtl: New file.
	* selftests/const-int.rtl: New file.
	* selftests/copy-hard-reg-into-frame.rtl: New file.
	* selftests/example-labels.rtl: New file.
	* selftests/insn-with-mode.rtl: New file.
	* selftests/jump-to-label-ref.rtl: New file.
	* selftests/jump-to-return.rtl: New file.
	* selftests/jump-to-simple-return.rtl: New file.
	* selftests/note-insn-deleted.rtl: New file.
	* selftests/note_insn_basic_block.rtl: New file.
	* selftests/symbol-ref.rtl: New file.
	* selftests/test-regno-renumbering.rtl: New file.
	* selftests/x86_64: New subdirectory.
	* selftests/x86_64/call-insn.rtl: New file.
	* selftests/x86_64/times-two.rtl: New file.
---
 gcc/Makefile.in                                    |    5 +
 gcc/errors.c                                       |   23 +-
 gcc/errors.h                                       |   14 +
 gcc/function-tests.c                               |    2 +-
 gcc/function.c                                     |    3 +-
 gcc/print-rtl.c                                    |    4 +-
 gcc/read-md.c                                      |   52 +-
 gcc/read-md.h                                      |   27 +-
 gcc/read-rtl-function.c                            | 2402 ++++++++++++++++++++
 gcc/read-rtl-function.h                            |   37 +
 gcc/read-rtl.c                                     |  205 +-
 gcc/rtl.h                                          |    2 +
 gcc/selftest-rtl.c                                 |   90 +
 gcc/selftest-rtl.h                                 |   67 +
 gcc/selftest-run-tests.c                           |    1 +
 gcc/selftest.h                                     |    1 +
 gcc/testsuite/selftests/aarch64/times-two.rtl      |   42 +
 gcc/testsuite/selftests/asr_div1.rtl               |   23 +
 gcc/testsuite/selftests/cfg-test.rtl               |   38 +
 gcc/testsuite/selftests/const-int.rtl              |   16 +
 .../selftests/copy-hard-reg-into-frame.rtl         |   12 +
 gcc/testsuite/selftests/example-labels.rtl         |    6 +
 gcc/testsuite/selftests/insn-with-mode.rtl         |    5 +
 gcc/testsuite/selftests/jump-to-label-ref.rtl      |   11 +
 gcc/testsuite/selftests/jump-to-return.rtl         |   10 +
 gcc/testsuite/selftests/jump-to-simple-return.rtl  |   11 +
 gcc/testsuite/selftests/note-insn-deleted.rtl      |    5 +
 gcc/testsuite/selftests/note_insn_basic_block.rtl  |    5 +
 gcc/testsuite/selftests/symbol-ref.rtl             |    7 +
 gcc/testsuite/selftests/test-regno-renumbering.rtl |    6 +
 gcc/testsuite/selftests/x86_64/call-insn.rtl       |   12 +
 gcc/testsuite/selftests/x86_64/times-two.rtl       |   57 +
 gcc/tree-dfa.c                                     |    5 +
 33 files changed, 3165 insertions(+), 41 deletions(-)
 create mode 100644 gcc/read-rtl-function.c
 create mode 100644 gcc/read-rtl-function.h
 create mode 100644 gcc/selftest-rtl.c
 create mode 100644 gcc/selftest-rtl.h
 create mode 100644 gcc/testsuite/selftests/aarch64/times-two.rtl
 create mode 100644 gcc/testsuite/selftests/asr_div1.rtl
 create mode 100644 gcc/testsuite/selftests/cfg-test.rtl
 create mode 100644 gcc/testsuite/selftests/const-int.rtl
 create mode 100644 gcc/testsuite/selftests/copy-hard-reg-into-frame.rtl
 create mode 100644 gcc/testsuite/selftests/example-labels.rtl
 create mode 100644 gcc/testsuite/selftests/insn-with-mode.rtl
 create mode 100644 gcc/testsuite/selftests/jump-to-label-ref.rtl
 create mode 100644 gcc/testsuite/selftests/jump-to-return.rtl
 create mode 100644 gcc/testsuite/selftests/jump-to-simple-return.rtl
 create mode 100644 gcc/testsuite/selftests/note-insn-deleted.rtl
 create mode 100644 gcc/testsuite/selftests/note_insn_basic_block.rtl
 create mode 100644 gcc/testsuite/selftests/symbol-ref.rtl
 create mode 100644 gcc/testsuite/selftests/test-regno-renumbering.rtl
 create mode 100644 gcc/testsuite/selftests/x86_64/call-insn.rtl
 create mode 100644 gcc/testsuite/selftests/x86_64/times-two.rtl

Comments

Bernd Schmidt Oct. 5, 2016, 4 p.m. UTC | #1
On 10/05/2016 06:15 PM, David Malcolm wrote:
> 	* errors.c: Use consistent pattern for bconfig.h vs config.h
> 	includes.
> 	(progname): Wrap with #ifdef GENERATOR_FILE.
> 	(error): Likewise.  Add "error: " to message.
> 	(fatal): Likewise.
> 	(internal_error): Likewise.
> 	(trim_filename): Likewise.
> 	(fancy_abort): Likewise.
> 	* errors.h (struct file_location): Move here from read-md.h.
> 	(file_location::file_location): Likewise.
> 	(error_at): New decl.

Can you split these out into a separate patch as well? I'll require more 
explanation for them and they seem largely independent.


Bernd
David Malcolm Oct. 7, 2016, 1:44 p.m. UTC | #2
On Wed, 2016-10-05 at 18:00 +0200, Bernd Schmidt wrote:
> On 10/05/2016 06:15 PM, David Malcolm wrote:
> > 	* errors.c: Use consistent pattern for bconfig.h vs config.h
> > 	includes.
> > 	(progname): Wrap with #ifdef GENERATOR_FILE.
> > 	(error): Likewise.  Add "error: " to message.
> > 	(fatal): Likewise.
> > 	(internal_error): Likewise.
> > 	(trim_filename): Likewise.
> > 	(fancy_abort): Likewise.
> > 	* errors.h (struct file_location): Move here from read-md.h.
> > 	(file_location::file_location): Likewise.
> > 	(error_at): New decl.
> 
> Can you split these out into a separate patch as well? I'll require
> more 
> explanation for them and they seem largely independent.

[CCing Richard Sandiford]

The gen* tools have their own diagnostics system, in errors.c:

/* warning, error, and fatal.  These definitions are suitable for use
   in the generator programs; the compiler has a more elaborate suite
   of diagnostic printers, found in diagnostic.c.  */

with file locations tracked using read-md.h's struct file_location,
rather than location_t (aka libcpp's source_location).

Implementing an RTL frontend by using the RTL reader from read-rtl.c
means that we now need a diagnostics subsystem on the *host* for
handling errors in RTL files, rather than just on the build machine.

There seem to be two ways to do this:

  (A) build the "light" diagnostics system (errors.c) for the host as
well as build machine, and link it with the RTL reader there, so there
are two parallel diagnostics subsystems.

  (B) build the "real" diagnostics system (diagnostics*) for the
*build* machine as well as the host, and use it from the gen* tools,
eliminating the "light" system, and porting the gen* tools to use
libcpp for location tracking.

Approach (A) seems to be simpler, which is what this part of the patch
does.

I've experimented with approach (B).  I think it's doable, but it's
much more invasive (perhaps needing a libdiagnostics.a and a
build/libdiagnostics.a in gcc/Makefile.in), so I hope this can be
followup work.

I can split the relevant parts out into a separate patch, but I was
wondering if either of you had a strong opinion on (A) vs (B) before I
do so?

Dave
Richard Sandiford Oct. 10, 2016, 6:53 p.m. UTC | #3
David Malcolm <dmalcolm@redhat.com> writes:
> On Wed, 2016-10-05 at 18:00 +0200, Bernd Schmidt wrote:
>> On 10/05/2016 06:15 PM, David Malcolm wrote:
>> > 	* errors.c: Use consistent pattern for bconfig.h vs config.h
>> > 	includes.
>> > 	(progname): Wrap with #ifdef GENERATOR_FILE.
>> > 	(error): Likewise.  Add "error: " to message.
>> > 	(fatal): Likewise.
>> > 	(internal_error): Likewise.
>> > 	(trim_filename): Likewise.
>> > 	(fancy_abort): Likewise.
>> > 	* errors.h (struct file_location): Move here from read-md.h.
>> > 	(file_location::file_location): Likewise.
>> > 	(error_at): New decl.
>> 
>> Can you split these out into a separate patch as well? I'll require
>> more 
>> explanation for them and they seem largely independent.
>
> [CCing Richard Sandiford]
>
> The gen* tools have their own diagnostics system, in errors.c:
>
> /* warning, error, and fatal.  These definitions are suitable for use
>    in the generator programs; the compiler has a more elaborate suite
>    of diagnostic printers, found in diagnostic.c.  */
>
> with file locations tracked using read-md.h's struct file_location,
> rather than location_t (aka libcpp's source_location).
>
> Implementing an RTL frontend by using the RTL reader from read-rtl.c
> means that we now need a diagnostics subsystem on the *host* for
> handling errors in RTL files, rather than just on the build machine.
>
> There seem to be two ways to do this:
>
>   (A) build the "light" diagnostics system (errors.c) for the host as
> well as build machine, and link it with the RTL reader there, so there
> are two parallel diagnostics subsystems.
>
>   (B) build the "real" diagnostics system (diagnostics*) for the
> *build* machine as well as the host, and use it from the gen* tools,
> eliminating the "light" system, and porting the gen* tools to use
> libcpp for location tracking.
>
> Approach (A) seems to be simpler, which is what this part of the patch
> does.
>
> I've experimented with approach (B).  I think it's doable, but it's
> much more invasive (perhaps needing a libdiagnostics.a and a
> build/libdiagnostics.a in gcc/Makefile.in), so I hope this can be
> followup work.
>
> I can split the relevant parts out into a separate patch, but I was
> wondering if either of you had a strong opinion on (A) vs (B) before I
> do so?

(A) sounds fine to me FWIW.  And sorry for the slow reply.

Thanks,
Richard
diff mbox

Patch

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 4b50f0b..7c8df56 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1270,6 +1270,7 @@  OBJS = \
 	dwarf2cfi.o \
 	dwarf2out.o \
 	emit-rtl.o \
+	errors.o \
 	et-forest.o \
 	except.o \
 	explow.o \
@@ -1408,6 +1409,9 @@  OBJS = \
 	print-rtl-function.o \
 	print-tree.o \
 	profile.o \
+	read-md.o \
+	read-rtl.o \
+	read-rtl-function.o \
 	real.o \
 	realmpfr.o \
 	recog.o \
@@ -1435,6 +1439,7 @@  OBJS = \
 	sel-sched-ir.o \
 	sel-sched-dump.o \
 	sel-sched.o \
+	selftest-rtl.o \
 	selftest-run-tests.o \
 	sese.o \
 	shrink-wrap.o \
diff --git a/gcc/errors.c b/gcc/errors.c
index 468384c..8df94ab 100644
--- a/gcc/errors.c
+++ b/gcc/errors.c
@@ -21,18 +21,21 @@  along with GCC; see the file COPYING3.  If not see
    in the generator programs; the compiler has a more elaborate suite
    of diagnostic printers, found in diagnostic.c.  */
 
-#ifdef HOST_GENERATOR_FILE
-#include "config.h"
-#define GENERATOR_FILE 1
-#else
+/* This file is compiled twice: once for the generator programs
+   once for the compiler.  */
+#ifdef GENERATOR_FILE
 #include "bconfig.h"
+#else
+#include "config.h"
 #endif
 #include "system.h"
 #include "errors.h"
 
 /* Set this to argv[0] at the beginning of main.  */
 
+#ifdef GENERATOR_FILE
 const char *progname;
+#endif /* #ifdef GENERATOR_FILE */
 
 /* Starts out 0, set to 1 if error is called.  */
 
@@ -55,13 +58,15 @@  warning (const char *format, ...)
 
 /* Print an error message - we keep going but the output is unusable.  */
 
+#ifdef GENERATOR_FILE
+
 void
 error (const char *format, ...)
 {
   va_list ap;
 
   va_start (ap, format);
-  fprintf (stderr, "%s: ", progname);
+  fprintf (stderr, "%s: error: ", progname);
   vfprintf (stderr, format, ap);
   va_end (ap);
   fputc ('\n', stderr);
@@ -69,6 +74,7 @@  error (const char *format, ...)
   have_error = 1;
 }
 
+#endif /* #ifdef GENERATOR_FILE */
 
 /* Fatal error - terminate execution immediately.  Does not return.  */
 
@@ -78,7 +84,7 @@  fatal (const char *format, ...)
   va_list ap;
 
   va_start (ap, format);
-  fprintf (stderr, "%s: ", progname);
+  fprintf (stderr, "%s: error: ", progname);
   vfprintf (stderr, format, ap);
   va_end (ap);
   fputc ('\n', stderr);
@@ -87,6 +93,8 @@  fatal (const char *format, ...)
 
 /* Similar, but say we got an internal error.  */
 
+#ifdef GENERATOR_FILE
+
 void
 internal_error (const char *format, ...)
 {
@@ -127,8 +135,11 @@  trim_filename (const char *name)
 /* "Fancy" abort.  Reports where in the compiler someone gave up.
    This file is used only by build programs, so we're not as polite as
    the version in diagnostic.c.  */
+
 void
 fancy_abort (const char *file, int line, const char *func)
 {
   internal_error ("abort in %s, at %s:%d", func, trim_filename (file), line);
 }
+
+#endif /* #ifdef GENERATOR_FILE */
diff --git a/gcc/errors.h b/gcc/errors.h
index a6973f3..ebafa77 100644
--- a/gcc/errors.h
+++ b/gcc/errors.h
@@ -28,8 +28,22 @@  along with GCC; see the file COPYING3.  If not see
 #ifndef GCC_ERRORS_H
 #define GCC_ERRORS_H
 
+/* Records a position in the file.  */
+struct file_location {
+  file_location () {}
+  file_location (const char *, int, int);
+
+  const char *filename;
+  int lineno;
+  int colno;
+};
+
+inline file_location::file_location (const char *filename_in, int lineno_in, int colno_in)
+: filename (filename_in), lineno (lineno_in), colno (colno_in) {}
+
 extern void warning (const char *, ...) ATTRIBUTE_PRINTF_1 ATTRIBUTE_COLD;
 extern void error (const char *, ...) ATTRIBUTE_PRINTF_1 ATTRIBUTE_COLD;
+extern void error_at (file_location, const char *, ...) ATTRIBUTE_PRINTF_2 ATTRIBUTE_COLD;
 extern void fatal (const char *, ...) ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF_1 ATTRIBUTE_COLD;
 extern void internal_error (const char *, ...) ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF_1 ATTRIBUTE_COLD;
 extern const char *trim_filename (const char *);
diff --git a/gcc/function-tests.c b/gcc/function-tests.c
index 4152cd3..2235b4c 100644
--- a/gcc/function-tests.c
+++ b/gcc/function-tests.c
@@ -420,7 +420,7 @@  verify_three_block_gimple_cfg (function *fun)
 
 /* As above, but additionally verify the RTL insns are sane.  */
 
-static void
+void
 verify_three_block_rtl_cfg (function *fun)
 {
   verify_three_block_cfg (fun);
diff --git a/gcc/function.c b/gcc/function.c
index 94ed786..1614bee 100644
--- a/gcc/function.c
+++ b/gcc/function.c
@@ -1901,7 +1901,8 @@  instantiate_decls (tree fndecl)
     instantiate_decl_rtl (DECL_RTL (DECL_VALUE_EXPR (decl)));
 
   /* Now process all variables defined in the function or its subblocks.  */
-  instantiate_decls_1 (DECL_INITIAL (fndecl));
+  if (DECL_INITIAL (fndecl))
+    instantiate_decls_1 (DECL_INITIAL (fndecl));
 
   FOR_EACH_LOCAL_DECL (cfun, ix, decl)
     if (DECL_RTL_SET_P (decl))
diff --git a/gcc/print-rtl.c b/gcc/print-rtl.c
index a905127..a2cb69a 100644
--- a/gcc/print-rtl.c
+++ b/gcc/print-rtl.c
@@ -220,7 +220,7 @@  print_rtx (const_rtx in_rtx)
       string:
 
 	if (str == 0)
-	  fputs (" \"\"", outfile);
+	  fputs (" (nil)", outfile);
 	else
 	  fprintf (outfile, " (\"%s\")", str);
 	sawclose = 1;
@@ -586,6 +586,8 @@  print_rtx (const_rtx in_rtx)
 #ifndef GENERATOR_FILE
 	if (XBBDEF (in_rtx, i))
 	  fprintf (outfile, " %i", XBBDEF (in_rtx, i)->index);
+	else
+	  fprintf (outfile, " (nil)");
 #endif
 	break;
 
diff --git a/gcc/read-md.c b/gcc/read-md.c
index 9ffdb8e..b6bafe7 100644
--- a/gcc/read-md.c
+++ b/gcc/read-md.c
@@ -65,6 +65,8 @@  static htab_t md_constants;
 /* A table of enum_type structures, hashed by name.  */
 static htab_t enum_types;
 
+static void read_skip_construct (int depth, file_location loc);
+
 /* Given an object that starts with a char * name field, return a hash
    code for its name.  */
 
@@ -347,7 +349,7 @@  read_skip_spaces (void)
 	    if (c != '*')
 	      {
 		unread_char (c);
-		fatal_with_file_and_line ("stray '/' in file");
+		return '/';
 	      }
 
 	    prevc = 0;
@@ -446,8 +448,8 @@  peek_char (void)
 /* Read an rtx code name into NAME.  It is terminated by any of the
    punctuation chars of rtx printed syntax.  */
 
-void
-read_name (struct md_name *name)
+static bool
+read_name_1 (struct md_name *name, file_location *out_loc)
 {
   int c;
   size_t i;
@@ -485,8 +487,12 @@  read_name (struct md_name *name)
       c = read_char ();
     }
 
+  unread_char (c);
+  *out_loc = base_rtx_reader_ptr->get_current_location ();
+  read_char ();
+
   if (i == 0)
-    fatal_with_file_and_line ("missing name or number");
+    return false;
 
   name->buffer[i] = 0;
   name->string = name->buffer;
@@ -507,6 +513,36 @@  read_name (struct md_name *name)
 	}
       while (def);
     }
+
+  return true;
+}
+
+/* Read an rtx code name into NAME.  It is terminated by any of the
+   punctuation chars of rtx printed syntax.  */
+
+file_location
+read_name (struct md_name *name)
+{
+  file_location loc;
+  if (!read_name_1 (name, &loc))
+    fatal_with_file_and_line ("missing name or number");
+  return loc;
+}
+
+file_location
+read_name_or_nil (struct md_name *name)
+{
+  file_location loc;
+  if (!read_name_1 (name, &loc))
+    {
+      file_location loc = base_rtx_reader_ptr->get_current_location ();
+      read_skip_construct (0, loc);
+      /* Skip the ')'.  */
+      read_char ();
+      name->buffer[0] = 0;
+      name->string = name->buffer;
+    }
+  return loc;
 }
 
 /* Subroutine of the string readers.  Handles backslash escapes.
@@ -652,6 +688,14 @@  read_string (int star_if_braced)
 	obstack_1grow (&string_obstack, '*');
       stringbuf = read_braced_string ();
     }
+  else if (saw_paren && c == 'n')
+    {
+      /* Handle (nil) by returning NULL.  */
+      require_char ('i');
+      require_char ('l');
+      require_char_ws (')');
+      return NULL;
+    }
   else
     fatal_with_file_and_line ("expected `\"' or `{', found `%c'", c);
 
diff --git a/gcc/read-md.h b/gcc/read-md.h
index 0701f35..4933912 100644
--- a/gcc/read-md.h
+++ b/gcc/read-md.h
@@ -21,19 +21,7 @@  along with GCC; see the file COPYING3.  If not see
 #define GCC_READ_MD_H
 
 #include "obstack.h"
-
-/* Records a position in the file.  */
-struct file_location {
-  file_location () {}
-  file_location (const char *, int, int);
-
-  const char *filename;
-  int lineno;
-  int colno;
-};
-
-inline file_location::file_location (const char *filename_in, int lineno_in, int colno_in)
-: filename (filename_in), lineno (lineno_in), colno (colno_in) {}
+#include "errors.h"
 
 /* Holds one symbol or number in the .md file.  */
 struct md_name {
@@ -200,9 +188,17 @@  class rtx_reader : public base_rtx_reader
   ~rtx_reader ();
 
   rtx read_rtx_code (const char *);
-  rtx read_rtx_operand (rtx return_rtx, int idx);
+  virtual rtx read_rtx_operand (rtx return_rtx, int idx);
   rtx read_nested_rtx (void);
   rtx read_rtx_variadic (rtx);
+  char *read_until (const char *terminator_chars, bool consume_terminator);
+
+  virtual void handle_any_trailing_information (rtx) {}
+  virtual rtx postprocess (rtx x) { return x; }
+
+ protected:
+  /* Analogous to print-rtl.c's in_call_function_usage.  */
+  bool m_in_call_function_usage;
 };
 
 /* Global singleton; constrast with base_rtx_reader_ptr above.  */
@@ -247,7 +243,8 @@  extern int read_skip_spaces (void);
 extern void require_char (char expected);
 extern void require_char_ws (char expected);
 extern void require_word_ws (const char *expected);
-extern void read_name (struct md_name *);
+extern file_location read_name (struct md_name *);
+extern file_location read_name_or_nil (struct md_name *);
 extern char *read_quoted_string (void);
 extern char *read_string (int);
 extern int n_comma_elts (const char *);
diff --git a/gcc/read-rtl-function.c b/gcc/read-rtl-function.c
new file mode 100644
index 0000000..0cd34d4
--- /dev/null
+++ b/gcc/read-rtl-function.c
@@ -0,0 +1,2402 @@ 
+/* read-rtl-function.c - Reader for RTL function dumps
+   Copyright (C) 2016 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "target.h"
+#include "tree.h"
+#include "gimple-expr.h"
+#include "diagnostic.h"
+#include "opts.h"
+#include "fold-const.h"
+#include "gimplify.h"
+#include "stor-layout.h"
+#include "debug.h"
+#include "convert.h"
+#include "langhooks.h"
+#include "langhooks-def.h"
+#include "common/common-target.h"
+#include "read-md.h"
+#include <mpfr.h>
+#include "rtl.h"
+#include "cfghooks.h"
+#include "stringpool.h"
+#include "function.h"
+#include "tree-cfg.h"
+#include "cfg.h"
+#include "basic-block.h"
+#include "cfgrtl.h"
+#include "emit-rtl.h"
+#include "cgraph.h"
+#include "tree-pass.h"
+#include "context.h"
+#include "pass_manager.h"
+#include "toplev.h"
+#include "bitmap.h"
+#include "df.h"
+#include "regs.h"
+#include "varasm.h"
+#include "insn-addr.h"
+#include "read-rtl-function.h"
+#include "selftest.h"
+#include "selftest-rtl.h"
+
+/* Forward decls.  */
+class function_reader;
+class fixup;
+
+/* Subclass of rtx_reader for reading function dumps.  */
+
+class function_reader : public rtx_reader
+{
+ public:
+  function_reader (function_reader_policy *policy);
+  ~function_reader ();
+
+  /* Overridden vfuncs of class base_rtx_reader.  */
+  void handle_unknown_directive (file_location, const char *) FINAL OVERRIDE;
+
+  /* Overridden vfuncs of class rtx_reader.  */
+  rtx read_rtx_operand (rtx return_rtx, int idx) FINAL OVERRIDE;
+  void handle_any_trailing_information (rtx return_rtx) FINAL OVERRIDE;
+  rtx postprocess (rtx) FINAL OVERRIDE;
+
+  int get_pseudo_offset () const { return m_pseudo_offset; }
+  rtx_insn **get_insn_by_uid (int uid);
+  tree parse_mem_expr (const char *desc);
+
+ private:
+  void parse_function ();
+  void create_function ();
+  void parse_insn_chain ();
+  void parse_insn (file_location loc, const char *name);
+  void create_cfg_and_basic_blocks ();
+  void parse_cfg (file_location loc);
+  void parse_bb ();
+  void parse_edge ();
+  void parse_crtl (file_location loc);
+
+  void read_rtx_operand_u (rtx return_rtx, int idx);
+  void read_rtx_operand_i_or_n (rtx return_rtx, int idx, char format_char);
+  rtx extra_parsing_for_operand_code_0 (rtx return_rtx, int idx);
+  rtx extra_parsing_for_operand_code_r (rtx return_rtx);
+
+  void add_fixup_insn_uid (file_location loc, rtx insn, int operand_idx,
+			   int insn_uid);
+
+  void add_fixup_bb (file_location loc, rtx insn,
+		     int operand_idx, int bb_idx);
+
+  void add_fixup_note_insn_basic_block (file_location loc, rtx insn,
+					int operand_idx, int bb_idx);
+
+  void add_fixup_source_location (file_location loc, rtx insn,
+				  int operand_idx,
+				  const char *filename, int lineno);
+
+  void add_fixup_jump_label (file_location loc, rtx insn,
+			     int operand_idx,
+			     const char *label);
+
+  void add_fixup_expr (file_location loc, rtx x,
+		       const char *desc);
+
+  void add_fixup_pseudo (file_location loc, rtx x);
+
+  rtx consolidate_singletons (rtx x);
+  void maybe_read_location (int operand_idx, rtx insn);
+
+  void apply_fixups ();
+  void recreate_implicit_cfg_edges ();
+
+ private:
+  function_reader_policy *m_policy;
+  struct uid_hash : int_hash <int, -1, -2> {};
+  hash_map<uid_hash, rtx_insn *> m_insns_by_uid;
+  auto_vec<fixup *> m_fixups;
+  bitmap_obstack m_bitmap_obstack;
+  bitmap_head m_dumped_pseudos;
+  bitmap_head m_bb_indices_in_insns;
+  bitmap_head m_bb_indices_in_cfg;
+  rtx_insn *m_first_insn;
+  auto_vec<tree> m_fake_scope;
+  char *m_name;
+  bool m_have_cfg_directive;
+  bool m_have_crtl_directive;
+  int m_pseudo_offset;
+};
+
+/* Abstract base class for recording post-processing steps that must be
+   done after reading a .rtl file.  */
+
+class fixup
+{
+ public:
+  fixup (file_location loc, rtx x)
+    : m_loc (loc), m_rtx (x)
+  {}
+  virtual ~fixup () {}
+
+  virtual void apply (function_reader *reader) const = 0;
+
+ protected:
+  file_location m_loc;
+  rtx m_rtx;
+};
+
+/* An abstract subclass of fixup for post-processing steps that
+   act on a specific operand of a specific instruction.  */
+
+class operand_fixup : public fixup
+{
+ public:
+  operand_fixup (file_location loc, rtx insn, int operand_idx)
+    : fixup (loc, insn), m_operand_idx (operand_idx)
+  {}
+
+ protected:
+  int m_operand_idx;
+};
+
+/* A concrete subclass of operand_fixup: fixup an rtx_insn *
+   field (NEXT_INSN/PREV_INSN) based on an integer UID.  */
+
+class fixup_insn_uid : public operand_fixup
+{
+ public:
+  fixup_insn_uid (file_location loc, rtx insn, int operand_idx, int insn_uid)
+    : operand_fixup (loc, insn, operand_idx),
+      m_insn_uid (insn_uid)
+  {}
+
+  void apply (function_reader *reader) const;
+
+ private:
+  int m_insn_uid;
+};
+
+/* A concrete subclass of operand_fixup: fix up a basic_block
+   pointer field based on an integer block ID.  */
+
+class fixup_bb : public operand_fixup
+{
+ public:
+  fixup_bb (file_location loc, rtx insn, int operand_idx, int bb_idx)
+    : operand_fixup (loc, insn, operand_idx),
+      m_bb_idx (bb_idx)
+  {}
+
+  void apply (function_reader *reader) const;
+
+ private:
+  int m_bb_idx;
+};
+
+/* A concrete subclass of operand_fixup: fix up a
+   NOTE_INSN_BASIC_BLOCK based on an integer block ID.  */
+
+class fixup_note_insn_basic_block : public operand_fixup
+{
+ public:
+  fixup_note_insn_basic_block (file_location loc, rtx insn, int operand_idx,
+			       int bb_idx)
+    : operand_fixup (loc, insn, operand_idx),
+      m_bb_idx (bb_idx)
+  {}
+
+  void apply (function_reader *reader) const;
+
+ private:
+  int m_bb_idx;
+};
+
+/* A concrete subclass of operand_fixup: fix up the JUMP_LABEL
+   of an insn based on a label name.  */
+
+class fixup_jump_label : public operand_fixup
+{
+ public:
+  fixup_jump_label (file_location loc, rtx insn,
+		    int operand_idx,
+		    const char *label)
+    : operand_fixup (loc, insn, operand_idx),
+      m_label (xstrdup (label))
+  {}
+
+  ~fixup_jump_label () { free (m_label); }
+
+  void apply (function_reader *reader) const;
+
+ private:
+  char *m_label;
+};
+
+/* A concrete subclass of fixup (not operand_fixup): fix up
+   the expr of an rtx (REG or MEM) based on a textual dump.  */
+
+class fixup_expr : public fixup
+{
+ public:
+  fixup_expr (file_location loc, rtx x, const char *desc)
+    : fixup (loc, x),
+      m_desc (xstrdup (desc))
+  {}
+
+  ~fixup_expr () { free (m_desc); }
+
+  void apply (function_reader *reader) const;
+
+ private:
+  char *m_desc;
+};
+
+/* A concrete subclass of fixup (not operand_fixup): fix up
+   the regno of a pseudo REG to ensure that it is a pseudo
+   on the current target.  */
+
+class fixup_pseudo : public fixup
+{
+ public:
+  fixup_pseudo (file_location loc, rtx x)
+    : fixup (loc, x)
+  {}
+
+  void apply (function_reader *reader) const;
+};
+
+/* Return a textual description of the given operand of the given rtx.  */
+
+static const char *
+get_operand_name (rtx insn, int operand_idx)
+{
+  gcc_assert (is_a <rtx_insn *> (insn));
+  switch (operand_idx)
+    {
+    case 0:
+      return "PREV_INSN";
+    case 1:
+      return "NEXT_INSN";
+    default:
+      return NULL;
+    }
+}
+
+/* Fixup an rtx_insn * field (NEXT_INSN/PREV_INSN) based on an integer
+   UID.  */
+
+void
+fixup_insn_uid::apply (function_reader *reader) const
+{
+  rtx_insn **insn_from_uid = reader->get_insn_by_uid (m_insn_uid);
+  if (insn_from_uid)
+    XEXP (m_rtx, m_operand_idx) = *insn_from_uid;
+  else
+    {
+      const char *op_name = get_operand_name (m_rtx, m_operand_idx);
+      if (op_name)
+	error_at (m_loc,
+		  "insn with UID %i not found for operand %i (`%s') of insn %i",
+		  m_insn_uid, m_operand_idx, op_name, INSN_UID (m_rtx));
+      else
+	error_at (m_loc,
+		  "insn with UID %i not found for operand %i of insn %i",
+		  m_insn_uid, m_operand_idx, INSN_UID (m_rtx));
+    }
+}
+
+/* Fix up a basic_block pointer field based on an integer block ID.
+   Update BB_HEAD and BB_END of the block as appropriate, as if the insn
+   was being added to the end of the block.  */
+
+void
+fixup_bb::apply (function_reader */*reader*/) const
+{
+  basic_block bb = BASIC_BLOCK_FOR_FN (cfun, m_bb_idx);
+  gcc_assert (bb);
+  XBBDEF (m_rtx, m_operand_idx) = bb;
+
+  rtx_insn *insn = as_a <rtx_insn *> (m_rtx);
+  if (!BB_HEAD (bb))
+    BB_HEAD (bb) = insn;
+  BB_END (bb) = insn;
+}
+
+/* Fix up a NOTE_INSN_BASIC_BLOCK based on an integer block ID.  */
+
+void
+fixup_note_insn_basic_block::apply (function_reader */*reader*/) const
+{
+  basic_block bb = BASIC_BLOCK_FOR_FN (cfun, m_bb_idx);
+  gcc_assert (bb);
+  NOTE_BASIC_BLOCK (m_rtx) = bb;
+}
+
+/* Fix up the JUMP_LABEL of an insn based on a label name.  */
+
+void
+fixup_jump_label::apply (function_reader *reader) const
+{
+  if (0 == strcmp (m_label, "return"))
+    JUMP_LABEL (m_rtx) = ret_rtx;
+  else if (0 == strcmp (m_label, "simple_return"))
+    JUMP_LABEL (m_rtx) = simple_return_rtx;
+  else
+    {
+      int label_uid = atoi (m_label);
+      rtx_insn **insn_from_uid = reader->get_insn_by_uid (label_uid);
+      if (insn_from_uid)
+	JUMP_LABEL (m_rtx) = *insn_from_uid;
+      else
+	error_at (m_loc,
+		  "insn with UID %i not found for operand %i (`%s') of insn %i",
+		  label_uid, m_operand_idx, "JUMP_LABEL", INSN_UID (m_rtx));
+    }
+}
+
+/* Fix up the expr of an rtx (REG or MEM) based on a textual dump.  */
+
+void
+fixup_expr::apply (function_reader *reader) const
+{
+  tree expr = reader->parse_mem_expr (m_desc);
+  switch (GET_CODE (m_rtx))
+    {
+    case REG:
+      set_reg_attrs_for_decl_rtl (expr, m_rtx);
+      break;
+    case MEM:
+      set_mem_expr (m_rtx, expr);
+      break;
+    default:
+      gcc_unreachable ();
+    }
+#if 0
+  if (!DECL_RTL_SET_P (expr))
+    SET_DECL_RTL (expr, m_rtx);
+#endif
+}
+
+/* Fix up the regno of a REG that ought to be a pseudo, based
+   on an offset generated from all of the pseudo regnos seen.  */
+
+void
+fixup_pseudo::apply (function_reader *reader) const
+{
+  gcc_assert (GET_CODE (m_rtx) == REG);
+
+  int dumped_regno = REGNO (m_rtx);
+  int effective_regno = dumped_regno + reader->get_pseudo_offset ();
+  set_regno_raw (m_rtx, effective_regno, 1);
+
+  ORIGINAL_REGNO (m_rtx) = effective_regno;
+  // FIXME: do we want this? it makes comparing dumps easier
+}
+
+/* Strip trailing whitespace from DESC.  */
+
+static void
+strip_trailing_whitespace (char *desc)
+{
+  char *terminator = desc + strlen (desc);
+  while (desc < terminator)
+    {
+      terminator--;
+      if (ISSPACE (*terminator))
+	*terminator = '\0';
+      else
+	break;
+    }
+}
+
+/* Return the numeric value n for GET_NOTE_INSN_NAME (n) for STRING,
+   or fail if STRING isn't recognized.  */
+
+static int
+parse_note_insn_name (const char *string)
+{
+  for (int i = 0; i < NOTE_INSN_MAX; i++)
+    if (0 == strcmp (string, GET_NOTE_INSN_NAME (i)))
+      return i;
+  fatal_with_file_and_line ("unrecognized NOTE_INSN name: `%s'", string);
+}
+
+/* Return the register number for NAME, or return -1 if it isn't
+   recognized.  */
+
+static int
+lookup_reg_by_dump_name (const char *name)
+{
+  for (int i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+    if (reg_names[i][0]
+	&& ! strcmp (name, reg_names[i]))
+      return i;
+
+  /* Also lookup virtuals.  */
+  if (!strcmp (name, "virtual-incoming-args"))
+    return VIRTUAL_INCOMING_ARGS_REGNUM;
+  if (!strcmp (name, "virtual-stack-vars"))
+    return VIRTUAL_STACK_VARS_REGNUM;
+  if (!strcmp (name, "virtual-stack-dynamic"))
+    return VIRTUAL_STACK_DYNAMIC_REGNUM;
+  if (!strcmp (name, "virtual-outgoing-args"))
+    return VIRTUAL_OUTGOING_ARGS_REGNUM;
+  if (!strcmp (name, "virtual-cfa"))
+    return VIRTUAL_CFA_REGNUM;
+  if (!strcmp (name, "virtual-preferred-stack-boundary"))
+    return VIRTUAL_PREFERRED_STACK_BOUNDARY_REGNUM;
+  /* TODO: handle "virtual-reg-%d".  */
+
+  /* Not found.  */
+  return -1;
+}
+
+/* class function_reader : public rtx_reader */
+
+/* function_reader's constructor.  */
+
+function_reader::function_reader (function_reader_policy *policy)
+: rtx_reader (),
+  m_policy (policy),
+  m_first_insn (NULL),
+  m_name (NULL),
+  m_have_cfg_directive (false),
+  m_have_crtl_directive (false),
+  m_pseudo_offset (0)
+{
+  bitmap_obstack_initialize (&m_bitmap_obstack);
+  bitmap_initialize (&m_dumped_pseudos, &m_bitmap_obstack);
+  bitmap_initialize (&m_bb_indices_in_insns, &m_bitmap_obstack);
+  bitmap_initialize (&m_bb_indices_in_cfg, &m_bitmap_obstack);
+  bitmap_set_bit (&m_bb_indices_in_insns, ENTRY_BLOCK);
+  bitmap_set_bit (&m_bb_indices_in_insns, EXIT_BLOCK);
+}
+
+/* function_reader's destructor.  */
+
+function_reader::~function_reader ()
+{
+  int i;
+  fixup *f;
+  FOR_EACH_VEC_ELT (m_fixups, i, f)
+    delete f;
+
+  bitmap_obstack_release (&m_bitmap_obstack);
+
+  free (m_name);
+}
+
+/* Implementation of rtx_reader::handle_unknown_directive.
+
+   Require a top-level "function" elements, as emitted by
+   print_rtx_function, and parse it.  */
+
+void
+function_reader::handle_unknown_directive (file_location start_loc,
+					   const char *name)
+{
+  if (strcmp (name, "function"))
+    fatal_at (start_loc, "expected 'function'");
+
+  parse_function ();
+}
+
+/* Parse the output of print_rtx_function (or hand-written data in the
+   same format), having parsed the "(function"  heading, and finishing
+   immediately before the final ")".
+
+   The "cfg" and "crtl" clauses are optional.  */
+
+void
+function_reader::parse_function ()
+{
+  m_name = xstrdup (read_string (0));
+
+  create_function ();
+
+  require_char_ws ('(');
+  require_word_ws ("insn-chain");
+  parse_insn_chain ();
+
+  create_cfg_and_basic_blocks ();
+
+  while (1)
+    {
+      int c = read_skip_spaces ();
+      if (c == ')')
+	{
+	  unread_char (c);
+	  break;
+	}
+      unread_char (c);
+      require_char ('(');
+      file_location loc = get_current_location ();
+      struct md_name directive;
+      read_name (&directive);
+      if (strcmp (directive.string, "cfg") == 0)
+	parse_cfg (loc);
+      else if (strcmp (directive.string, "crtl") == 0)
+	parse_crtl (loc);
+      else
+	fatal_with_file_and_line ("missing 'cfg' or 'crtl'");
+    }
+
+  apply_fixups ();
+  if (!m_have_cfg_directive)
+    recreate_implicit_cfg_edges ();
+
+  /* Ensure x_cur_insn_uid is 1 more than the biggest insn UID seen.
+     This is normally updated by the various make_*insn_raw functions.  */
+  {
+    rtx_insn *insn;
+    int max_uid = 0;
+    for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+      max_uid = MAX (max_uid, INSN_UID (insn));
+    crtl->emit.x_cur_insn_uid = max_uid + 1;
+  }
+
+  crtl->init_stack_alignment ();
+}
+
+/* Set up state for the function *before* fixups are applied.
+
+   Create "cfun" and a decl for the function.
+   By default, every function decl is hardcoded as
+      int test_1 (int i, int j, int k);
+   Set up various other state:
+   - the cfg and basic blocks (edges are created later, *after* fixups
+   are applied).
+   - add the function to the callgraph.  */
+
+void
+function_reader::create_function ()
+{
+  /* Currently we assume cfgrtl mode, rather than cfglayout mode.  */
+  if (0)
+    cfg_layout_rtl_register_cfg_hooks ();
+  else
+    rtl_register_cfg_hooks ();
+
+  /* Create cfun.  */
+  tree fn_name = get_identifier (m_name ? m_name : "test_1");
+  tree int_type = integer_type_node;
+  tree return_type = int_type;
+  tree arg_types[3] = {int_type, int_type, int_type};
+  tree fn_type = build_function_type_array (return_type, 3, arg_types);
+  tree fndecl = build_decl_stat (UNKNOWN_LOCATION, FUNCTION_DECL, fn_name,
+				 fn_type);
+  tree resdecl = build_decl (UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE,
+			     return_type);
+  DECL_ARTIFICIAL (resdecl) = 1;
+  DECL_IGNORED_P (resdecl) = 1;
+  DECL_RESULT (fndecl) = resdecl;
+  allocate_struct_function (fndecl, false);
+  /* This sets cfun.  */
+
+  current_function_decl = fndecl;
+
+  cfun->curr_properties = (PROP_cfg | PROP_rtl);
+
+  /* Do we need this to force cgraphunit.c to output the function? */
+  DECL_EXTERNAL (fndecl) = 0;
+  DECL_PRESERVE_P (fndecl) = 1;
+
+  /* Add to cgraph.  */
+  {
+    /* cgraph_node::add_new_function does additional processing
+       based on symtab->state.  We need to avoid it attempting to gimplify
+       things.  Temporarily putting it in the PARSING state appears to
+       achieve this.  */
+    enum symtab_state old_state = symtab->state;
+    symtab->state = PARSING;
+    cgraph_node::add_new_function (fndecl, true /*lowered*/);
+    /* Reset the state.  */
+    symtab->state = old_state;
+  }
+}
+
+/* Parse zero or more child insn elements within an
+   "insn-chain" element.  */
+
+void
+function_reader::parse_insn_chain ()
+{
+  while (1)
+    {
+      int c = read_skip_spaces ();
+      file_location loc = get_current_location ();
+      if (c == ')')
+	break;
+      else if (c == '(')
+	{
+	  struct md_name directive;
+	  read_name (&directive);
+	  parse_insn (loc, directive.string);
+	  require_char_ws (')');
+	}
+      else
+	fatal_at (loc, "expected '(' or ')'");
+    }
+}
+
+/* Parse rtx instructions by calling read_rtx_code, calling
+   set_first_insn and set_last_insn as appropriate.  */
+
+void
+function_reader::parse_insn (file_location start_loc, const char *name)
+{
+  rtx x = read_rtx_code (name);
+  if (!x)
+    fatal_at (start_loc, "expected insn type; got '%s'", name);
+  rtx_insn *insn = as_a <rtx_insn *> (x);
+  set_last_insn (insn);
+  if (!m_first_insn)
+    {
+      m_first_insn = insn;
+      set_first_insn (insn);
+    }
+  m_insns_by_uid.put (INSN_UID (insn), insn);
+}
+
+/* Create cfun's CFG and populate with blocks, a helper
+   function for function_reader::parse_function ().
+
+   The edges are created later on, either by parsing "edge" directives
+   in the input file, or implicity from the insns, after fixups are applied.
+
+   We can't call create_basic_block and use the regular RTL block-creation
+   hooks, since this creates NOTE_INSN_BASIC_BLOCK instances.  We don't
+   want to do that; we want to use the notes we were provided with.
+
+   The term "index" has two meanings for basic blocks in a CFG:
+   (a) the "index" field within struct basic_block_def.
+   (b) the index of a basic_block within the cfg's x_basic_block_info
+   vector, as accessed via BASIC_BLOCK_FOR_FN.
+
+   These can get out-of-sync when basic blocks are optimized away.
+   They get back in sync by "compact_blocks".
+   We make the assumption that the CFG has been compacted, and
+   so we reconstruct cfun->cfg->x_basic_block_info->m_vecdata with NULL
+   values in it for any missing basic blocks, so that (a) == (b) for
+   all of the blocks we create.  The doubly-linked list of basic
+   blocks (next_bb/prev_bb) skips over these "holes".  */
+
+void
+function_reader::create_cfg_and_basic_blocks ()
+{
+  /* Create bare-bones cfg.  This creates the entry and exit blocks.  */
+  init_empty_tree_cfg_for_function (cfun);
+  ENTRY_BLOCK_PTR_FOR_FN (cfun)->flags |= BB_RTL;
+  EXIT_BLOCK_PTR_FOR_FN (cfun)->flags |= BB_RTL;
+
+  /* Ensure that the vector of basic_block pointers is big enough.  */
+  unsigned highest_bb_idx = bitmap_last_set_bit (&m_bb_indices_in_insns);
+  size_t new_size = highest_bb_idx + 1;
+  if (basic_block_info_for_fn (cfun)->length () < new_size)
+    vec_safe_grow_cleared (basic_block_info_for_fn (cfun), new_size);
+
+  /* Populate the vector.  */
+  unsigned bb_idx;
+  bitmap_iterator bi;
+  basic_block after = ENTRY_BLOCK_PTR_FOR_FN (cfun);
+  gcc_assert (after);
+  EXECUTE_IF_SET_IN_BITMAP (&m_bb_indices_in_insns, 0, bb_idx, bi)
+    {
+      /* The entry and exit blocks were already created above.  */
+      if (bb_idx == ENTRY_BLOCK || bb_idx == EXIT_BLOCK)
+	{
+	  basic_block bb = BASIC_BLOCK_FOR_FN (cfun, bb_idx);
+	  init_rtl_bb_info (bb);
+	  continue;
+	}
+      basic_block bb = alloc_block ();
+      init_rtl_bb_info (bb);
+      bb->index = bb_idx;
+      bb->flags = BB_NEW | BB_RTL;
+      link_block (bb, after);
+
+      n_basic_blocks_for_fn (cfun)++;
+      SET_BASIC_BLOCK_FOR_FN (cfun, bb_idx, bb);
+      BB_SET_PARTITION (bb, BB_UNPARTITIONED);
+
+      /* Tag the block so that we know it has been used when considering
+	 other basic block notes.  */
+      bb->aux = bb;
+
+      after = bb;
+    }
+
+  last_basic_block_for_fn (cfun) = highest_bb_idx + 1;
+}
+
+/* Parse a "(cfg)" directive, having parsed the "(cfg" heading.
+   Consume the final ")".  */
+
+void
+function_reader::parse_cfg (file_location loc)
+{
+  if (m_have_cfg_directive)
+    error_at (loc, "more than one 'cfg' directive");
+  m_have_cfg_directive = true;
+
+  while (1)
+    {
+      int c = read_skip_spaces ();
+      file_location loc = get_current_location ();
+      if (c == ')')
+	break;
+      else if (c == '(')
+	{
+	  require_word_ws ("bb");
+	  parse_bb ();
+	}
+      else
+	fatal_at (loc, "expected '(' or ')'");
+    }
+
+  /* Creating the BBs is fiddly.  For now, we rely on
+     create_cfg_and_basic_blocks, which works purely on the
+     set of BB indexes seen within the insns.
+     Hence we error-check to make sure the BB indexes seen
+     in the (cfg) is the same.
+
+     parse_bb complains about BB indices seen in (bb) directives that
+     weren't seen within the (insn-chain).
+
+     Complain here about the opposite: BB indices that were in the
+     (insn-chain) that were not seen in the (cfg).  */
+  bitmap_head just_in_insns;
+  bitmap_initialize (&just_in_insns, &m_bitmap_obstack);
+  bitmap_and_compl (&just_in_insns, &m_bb_indices_in_insns,
+		    &m_bb_indices_in_cfg);
+  unsigned bb_idx;
+  bitmap_iterator bi;
+  int num_errors = 0;
+  EXECUTE_IF_SET_IN_BITMAP (&just_in_insns, 0, bb_idx, bi)
+    /* Don't worry about entry/exit.  */
+    if (bb_idx > 1)
+	{
+	  error_at (loc,
+		    "error: bb index %i used in (insn-chain)"
+		    " but not listed in (cfg)", bb_idx);
+	  num_errors++;
+	}
+  if (num_errors)
+    fatal_at (loc, "%i missing bb(s)", num_errors);
+}
+
+/* Parse a "(bb)" directive, having parsed the "(bb" heading.
+   Consume the final ")".  Call parse_edge on any "(edge)"
+   directives encountered.  Don't actually created basic blocks;
+   instead, verify that the BB indices correspond to
+   those seen in the insn chain.  */
+
+void
+function_reader::parse_bb ()
+{
+  struct md_name name;
+  file_location loc = read_name (&name);
+  int bb_idx = atoi (name.string);
+  if (0)
+    fprintf (stderr, "parse_bb: %i\n", bb_idx);
+  bitmap_set_bit (&m_bb_indices_in_cfg, bb_idx);
+
+  /* The bb should already have been created by
+     create_cfg_and_basic_blocks.  */
+  basic_block bb = BASIC_BLOCK_FOR_FN (cfun, bb_idx);
+  if (!bb)
+    fatal_at (loc, "error: bb index %i not referenced by insns", bb_idx);
+
+  /* Parse 0 or more edges.  */
+  while (1)
+    {
+      int c = read_skip_spaces ();
+      file_location loc = get_current_location ();
+      if (c == ')')
+	break;
+      else if (c == '(')
+	{
+	  require_word_ws ("edge");
+	  parse_edge ();
+	}
+      else
+	fatal_at (loc, "expected '(' or ')'");
+    }
+}
+
+/* Parse a "(edge)" directive, having parsed the "(edge" heading.
+   Consume the final ")".  Create the edge within the CFG.  */
+
+void
+function_reader::parse_edge ()
+{
+  struct md_name name;
+  file_location loc = read_name (&name);
+  int src_bb_idx = atoi (name.string);
+  loc = read_name (&name);
+  int dst_bb_idx = atoi (name.string);
+
+  /* Flags.  */
+  require_char_ws ('(');
+  require_word_ws ("flags");
+  struct md_name hexval;
+  read_name (&hexval);
+  int flags = strtol (hexval.string, NULL, 16);
+  require_char_ws (')');
+
+  require_char_ws (')');
+
+  if (0)
+    fprintf (stderr, "parse_edge: %i-> %i flags 0x%x \n",
+	     src_bb_idx, dst_bb_idx, flags);
+
+  /* The BBs should already have been created by
+     create_cfg_and_basic_blocks.  */
+  basic_block src = BASIC_BLOCK_FOR_FN (cfun, src_bb_idx);
+  if (!src)
+    fatal_at (loc, "error: bb index %i not referenced by insns", src_bb_idx);
+  basic_block dst = BASIC_BLOCK_FOR_FN (cfun, dst_bb_idx);
+  if (!dst)
+    fatal_at (loc, "error: bb index %i not referenced by insns", dst_bb_idx);
+  unchecked_make_edge (src, dst, flags);
+}
+
+/* Parse a "(crtl)" directive, having parsed the "(crtl" heading.
+   Consume the final ")".  */
+
+void
+function_reader::parse_crtl (file_location loc)
+{
+  if (m_have_crtl_directive)
+    error_at (loc, "more than one 'crtl' directive");
+  m_have_crtl_directive = true;
+
+  /* return_rtx.  */
+  require_char_ws ('(');
+  require_word_ws ("return_rtx");
+
+  require_char_ws ('(');
+  struct md_name directive;
+  read_name (&directive);
+  crtl->return_rtx
+    = consolidate_singletons (read_rtx_code (directive.string));
+  require_char_ws (')');
+
+  require_char_ws (')');
+
+  require_char_ws (')');
+}
+
+/* Overridden implementation of rtx_reader::read_rtx_operand for
+   function_reader, handling various extra data printed by print_rtx,
+   and sometimes calling the base class implementation.  */
+
+rtx
+function_reader::read_rtx_operand (rtx return_rtx, int idx)
+{
+  RTX_CODE code = GET_CODE (return_rtx);
+  const char *format_ptr = GET_RTX_FORMAT (code);
+  const char format_char = format_ptr[idx];
+  struct md_name name;
+
+  /* Override the regular parser for some format codes.  */
+  switch (format_char)
+    {
+    case 'e':
+      {
+	if (idx == 7 && CALL_P (return_rtx))
+	  {
+	    m_in_call_function_usage = true;
+	    return rtx_reader::read_rtx_operand (return_rtx, idx);
+	    m_in_call_function_usage = false;
+	  }
+	else
+	  return rtx_reader::read_rtx_operand (return_rtx, idx);
+      }
+      break;
+
+    case 'u':
+      read_rtx_operand_u (return_rtx, idx);
+      /* Don't run regular parser for 'u'.  */
+      return return_rtx;
+
+    case 'i':
+    case 'n':
+      read_rtx_operand_i_or_n (return_rtx, idx, format_char);
+      /* Don't run regular parser for these codes.  */
+      return return_rtx;
+
+    case 'B':
+      {
+	file_location loc = read_name_or_nil (&name);
+	int bb_idx = atoi (name.string);
+	if (bb_idx)
+	  add_fixup_bb (loc, return_rtx, idx, bb_idx);
+	/* Don't run regular parser.  */
+	return return_rtx;
+      }
+      break;
+
+    default:
+      break;
+    }
+
+  /* Call base class implementation.  */
+  return_rtx = rtx_reader::read_rtx_operand (return_rtx, idx);
+
+  /* Handle any additional parsing needed to handle what the dump
+     could contain.  */
+  switch (format_char)
+    {
+    case '0':
+      return extra_parsing_for_operand_code_0 (return_rtx, idx);
+
+    case 'w':
+      {
+	/* Strip away the redundant hex dump of the value.  */
+	require_char_ws ('[');
+	read_name (&name);
+	require_char_ws (']');
+      }
+      break;
+
+    case 'r':
+      return extra_parsing_for_operand_code_r (return_rtx);
+
+    default:
+      break;
+    }
+
+  return return_rtx;
+}
+
+/* Special-cased handling of code 'u' for reading function dumps.
+
+   The RTL file recorded the ID of an insn (or 0 for NULL); we
+   must store this as a pointer, but the insn might not have
+   been loaded yet.  Store the ID away for now, via a fixup.  */
+
+void
+function_reader::read_rtx_operand_u (rtx return_rtx, int idx)
+{
+  struct md_name name;
+  file_location loc = read_name (&name);
+  int insn_id = atoi (name.string);
+  if (insn_id)
+    add_fixup_insn_uid (loc, return_rtx, idx, insn_id);
+}
+
+/* Special-cased handling of codes 'i' and 'n' for reading function
+   dumps.  */
+
+void
+function_reader::read_rtx_operand_i_or_n (rtx return_rtx, int idx,
+					  char format_char)
+{
+  /* Handle some of the extra information that print_rtx
+     can write out for these cases.  */
+  /* print_rtx only writes out operand 5 for notes
+     for NOTE_KIND values NOTE_INSN_DELETED_LABEL
+     and NOTE_INSN_DELETED_DEBUG_LABEL.  */
+  if (idx == 5 && NOTE_P (return_rtx))
+    return;
+
+  if (idx == 4 && INSN_P (return_rtx))
+    {
+      maybe_read_location (idx, return_rtx);
+      return;
+    }
+
+  struct md_name name;
+  read_name (&name);
+  int value;
+  if (format_char == 'n')
+    value = parse_note_insn_name (name.string);
+  else
+    value = atoi (name.string);
+  XINT (return_rtx, idx) = value;
+
+  /* print-rtl.c prints the names of recognized insns, after
+     the insn code, wrapping them in braces.  Skip them,
+     and reset the insn code to be unrecognized, since insn
+     codes are likely to change every time the .md files
+     change.  */
+  if (idx == 5 && INSN_P (return_rtx) && format_char == 'i')
+    if (value >= 0)
+      {
+	/* Get the content within the braces, skipping the braces.  */
+	require_char_ws ('{');
+	char *dumped_insn_name = read_until ("}", true);
+	if (0)
+	  fprintf (stderr, "got insn name: '%s'\n", dumped_insn_name);
+	/* For now, ignore the dumped insn name.  */
+	free (dumped_insn_name);
+
+	/* Reset the insn to be unrecognized.  */
+	INSN_CODE (return_rtx) = -1;
+      }
+}
+
+/* Additional parsing for format code '0' in dumps, handling a variety
+   of special-cases in print_rtx.  */
+
+rtx
+function_reader::extra_parsing_for_operand_code_0 (rtx return_rtx, int idx)
+{
+  RTX_CODE code = GET_CODE (return_rtx);
+  int c;
+  struct md_name name;
+
+  if (idx == 1 && code == SYMBOL_REF)
+    {
+      /* Possibly wrote " [flags %#x]", SYMBOL_REF_FLAGS (in_rtx).  */
+      c = read_skip_spaces ();
+      if (c == '[')
+	{
+	  file_location loc = read_name (&name);
+	  if (strcmp (name.string, "flags"))
+	    error_at (loc, "was expecting `%s'", "flags");
+	  read_name (&name);
+	  SYMBOL_REF_FLAGS (return_rtx) = strtol (name.string, NULL, 16);
+
+	  /* We can't reconstruct SYMBOL_REF_BLOCK; set it to NULL.  */
+	  if (SYMBOL_REF_HAS_BLOCK_INFO_P (return_rtx))
+	    SYMBOL_REF_BLOCK (return_rtx) = NULL;
+
+	  require_char (']');
+	}
+      else
+	unread_char (c);
+
+      /* Possibly wrote:
+	 print_node_brief (outfile, "", SYMBOL_REF_DECL (in_rtx),
+			   dump_flags);  */
+      c = read_skip_spaces ();
+      if (c == '<')
+	{
+	  /* Skip the content for now.  */
+	  while (1)
+	    {
+	      char ch = read_char ();
+	      if (ch == '>')
+		break;
+	    }
+	}
+      else
+	unread_char (c);
+    }
+  else if (idx == 3 && code == NOTE)
+    {
+      /* Note-specific data appears for operand 3, which annoyingly
+	 is before the enum specifying which kind of note we have
+	 (operand 4).  */
+      c = read_skip_spaces ();
+      if (c == '[')
+	{
+	  /* Possibly data for a NOTE_INSN_BASIC_BLOCK, of the form:
+	     [bb %d].  */
+	  file_location bb_loc = read_name (&name);
+	  if (strcmp (name.string, "bb"))
+	    error_at (bb_loc, "was expecting `%s'", "bb");
+	  read_name (&name);
+	  int bb_idx = atoi (name.string);
+	  add_fixup_note_insn_basic_block (bb_loc, return_rtx, idx,
+					   bb_idx);
+	  require_char_ws (']');
+	}
+      else
+	unread_char (c);
+    }
+  else if (idx == 7 && JUMP_P (return_rtx))
+    {
+      c = read_skip_spaces ();
+      if (c != '-')
+	{
+	  unread_char (c);
+	  return return_rtx;
+	}
+      require_char ('>');
+      file_location loc = read_name (&name);
+      add_fixup_jump_label (loc, return_rtx, idx, name.string);
+    }
+
+  return return_rtx;
+}
+
+/* Additional parsing for format code 'r' in dumps: register numbers.
+   The initial number has already been parsed.  */
+
+rtx
+function_reader::extra_parsing_for_operand_code_r (rtx return_rtx)
+{
+  /* REGNO holds the number from the dump.  */
+  unsigned int dumped_regno = REGNO (return_rtx);
+
+  /* print_rtx will print a name for hard and virtual registers
+     after the register number, and no name for pseudos.
+
+     It will then print zero, one, or two sections marked by square
+     brackets.
+
+     - see if there's a name after the number.  If there is, assume a hard
+     or virtual reg, and try to parse the name:
+     - if the name is recognized, use the target's current number for that
+     name (future-proofing virtuals against .md changes)
+     - if the name is not recognized, issue a fatal error (it's probably a
+     typo, or maybe a backport from a future version of gcc, or a target
+     incompatibility)
+     - if there's no name, assume it's a pseudo.  Remap all pseudos in a
+     postprocessing phase to ensure that they *are* pseudos (even if the .md
+     has gained some hard regs in the meantime), leaving the numbering
+     untouched if possible (the common case).  */
+
+  int effective_regno;
+
+  int ch = read_skip_spaces ();
+  unread_char (ch);
+  if (ch != '[' && ch != ')')
+    {
+      struct md_name name;
+      file_location loc = read_name (&name);
+      int regno_by_name = lookup_reg_by_dump_name (name.string);
+      if (regno_by_name == -1)
+	fatal_at (loc, "unrecognized register: '%s'", name.string);
+      effective_regno = regno_by_name;
+    }
+  else
+    {
+      /* No reg name in dump: this was a pseudo.  */
+      bitmap_set_bit (&m_dumped_pseudos, dumped_regno);
+
+      /* For now, use the dumped regno, but potentially
+	 fix it up later.  */
+      effective_regno = dumped_regno;
+      add_fixup_pseudo (get_current_location (), return_rtx);
+    }
+
+  set_regno_raw (return_rtx, effective_regno, 1);
+
+  /* Consolidate singletons.  */
+  return_rtx = consolidate_singletons (return_rtx);
+
+  ORIGINAL_REGNO (return_rtx) = effective_regno;
+
+  /* Parse extra stuff at end of 'r'.
+     We may have zero, one, or two sections marked by square
+     brackets.  */
+  ch = read_skip_spaces ();
+  bool expect_original_regno = false;
+  if (ch == '[')
+    {
+      file_location loc = get_current_location ();
+      char *desc = read_until ("]", true);
+      strip_trailing_whitespace (desc);
+      const char *desc_start = desc;
+      /* If ORIGINAL_REGNO (rtx) != regno, we will have:
+	 "orig:%i", ORIGINAL_REGNO (rtx).
+	 Consume it, we don't set ORIGINAL_REGNO, since we can
+	 get that from the 2nd copy later.  */
+      if (0 == strncmp (desc, "orig:", 5))
+	{
+	  expect_original_regno = true;
+	  desc_start += 5;
+	  /* Skip to any whitespace following the integer.  */
+	  const char *space = strchr (desc_start, ' ');
+	  if (space)
+	    desc_start = space + 1;
+	}
+      /* Any remaining text may be the REG_EXPR.  Alternatively we have
+	 no REG_ATTRS, and instead we have ORIGINAL_REGNO.  */
+      if (ISDIGIT (*desc_start))
+	{
+	  /* Assume we have ORIGINAL_REGNO.  */
+	  ORIGINAL_REGNO (return_rtx) = atoi (desc_start);
+	}
+      else
+	{
+	  /* Assume we have REG_EXPR.  */
+	  add_fixup_expr (loc, return_rtx, desc_start);
+	}
+      free (desc);
+    }
+  else
+    unread_char (ch);
+  if (expect_original_regno)
+    {
+      require_char_ws ('[');
+      char *desc = read_until ("]", true);
+      ORIGINAL_REGNO (return_rtx) = atoi (desc);
+      free (desc);
+    }
+
+  return return_rtx;
+}
+
+/* Implementation of rtx_reader::handle_any_trailing_information.
+   Handle the various additional information that print-rtl.c can
+   write after the regular fields.  */
+
+void
+function_reader::handle_any_trailing_information (rtx return_rtx)
+{
+  struct md_name name;
+
+  switch (GET_CODE (return_rtx))
+    {
+      case MEM:
+	{
+	  int ch;
+	  require_char_ws ('[');
+	  read_name (&name);
+	  MEM_ALIAS_SET (return_rtx) = atoi (name.string);
+	  /* We have either a MEM_EXPR, or a space.  */
+	  if (peek_char () != ' ')
+	    {
+	      file_location loc = get_current_location ();
+	      char *desc = read_until (" +", false);
+	      add_fixup_expr (loc, consolidate_singletons (return_rtx), desc);
+	      free (desc);
+	    }
+	  else
+	    read_char ();
+
+	  /* We may optionally have '+' for MEM_OFFSET_KNOWN_P.  */
+	  ch = read_skip_spaces ();
+	  if (ch == '+')
+	    {
+	      read_name (&name);
+	      MEM_OFFSET_KNOWN_P (return_rtx) = 1;
+	      MEM_OFFSET (return_rtx) = atoi (name.string);
+	    }
+	  else
+	    unread_char (ch);
+
+	  /* Handle optional " S" for MEM_SIZE.  */
+	  ch = read_skip_spaces ();
+	  if (ch == 'S')
+	    {
+	      read_name (&name);
+	      MEM_SIZE (return_rtx) = atoi (name.string);
+	    }
+	  else
+	    unread_char (ch);
+
+	  /* Handle optional " A" for MEM_ALIGN.  */
+	  ch = read_skip_spaces ();
+	  if (ch == 'A' && peek_char () != 'S')
+	    {
+	      read_name (&name);
+	      MEM_ALIGN (return_rtx) = atoi (name.string);
+	    }
+
+	  /* Handle optional " AS" for MEM_ADDR_SPACE.  */
+	  ch = read_skip_spaces ();
+	  if (ch == 'A' && peek_char () == 'S')
+	    {
+	      read_char ();
+	      read_name (&name);
+	      MEM_ADDR_SPACE (return_rtx) = atoi (name.string);
+	    }
+	  else
+	    unread_char (ch);
+
+	  require_char (']');
+	}
+	break;
+
+      case CODE_LABEL:
+	{
+	  /* Parse LABEL_NUSES.  */
+	  require_char_ws ('[');
+	  read_name (&name);
+	  LABEL_NUSES (return_rtx) = atoi (name.string);
+	  require_word_ws ("uses");
+	  require_char_ws (']');
+	  /* TODO: parse LABEL_KIND.  */
+	  /* For now, skip until closing ')'.  */
+	  do
+	    {
+	      char ch = read_char ();
+	      if (ch == ')')
+		{
+		  unread_char (ch);
+		  break;
+		}
+	    }
+	  while (1);
+	}
+	break;
+
+      default:
+	break;
+    }
+}
+
+/* Parse a tree dump for MEM_EXPR and turn it back into a tree.
+   We handle "<retval>", but for anything else we "cheat" by building a
+   global VAR_DECL of type "int" with that name (returning the same global
+   for a name if we see the same name more than once).  */
+
+tree
+function_reader::parse_mem_expr (const char *desc)
+{
+  tree fndecl = cfun->decl;
+
+  if (0 == strcmp (desc, "<retval>"))
+    {
+      return DECL_RESULT (fndecl);
+    }
+
+  /* Search within function parms.  */
+  for (tree arg = DECL_ARGUMENTS (fndecl); arg; arg = TREE_CHAIN (arg))
+    {
+      if (strcmp (desc, IDENTIFIER_POINTER (DECL_NAME (arg))) == 0)
+	return arg;
+    }
+
+  /* Search within decls we already created.
+     FIXME: use a hash rather than linear search.  */
+  int i;
+  tree t;
+  FOR_EACH_VEC_ELT (m_fake_scope, i, t)
+    if (strcmp (desc, IDENTIFIER_POINTER (DECL_NAME (t))) == 0)
+      return t;
+
+  /* Not found?  Create it.
+     This allows mimicing of real data but avoids having to specify
+     e.g. names of locals, params etc.
+     Though this way we don't know if we have a PARM_DECL vs a VAR_DECL,
+     and we don't know the types.  Fake it by making everything be
+     a VAR_DECL of "int" type.  */
+  t = build_decl (UNKNOWN_LOCATION, VAR_DECL,
+		  get_identifier (desc),
+		  integer_type_node);
+  m_fake_scope.safe_push (t);
+  return t;
+}
+
+/* Record the information for later post-processing.  */
+
+void
+function_reader::add_fixup_insn_uid (file_location loc, rtx insn, int operand_idx,
+				     int insn_uid)
+{
+  m_fixups.safe_push (new fixup_insn_uid (loc, insn, operand_idx, insn_uid));
+}
+
+/* Record the information for later post-processing.  */
+
+void
+function_reader::add_fixup_bb (file_location loc, rtx insn, int operand_idx,
+			       int bb_idx)
+{
+  bitmap_set_bit (&m_bb_indices_in_insns, bb_idx);
+  m_fixups.safe_push (new fixup_bb (loc, insn, operand_idx, bb_idx));
+}
+
+/* Record the information for later post-processing.  */
+
+void
+function_reader::add_fixup_note_insn_basic_block (file_location loc, rtx insn,
+						  int operand_idx, int bb_idx)
+{
+  m_fixups.safe_push (new fixup_note_insn_basic_block (loc, insn, operand_idx,
+						       bb_idx));
+}
+
+/* Record the information for later post-processing.  */
+void
+function_reader::add_fixup_source_location (file_location, rtx,
+					    int, const char *, int)
+{
+  /* Empty for now.  */
+}
+
+/* Record the information for later post-processing.  */
+
+void
+function_reader::add_fixup_jump_label (file_location loc, rtx insn,
+				       int operand_idx,
+				       const char *label)
+{
+  m_fixups.safe_push (new fixup_jump_label (loc, insn, operand_idx, label));
+}
+
+/* Record the information for later post-processing.  */
+
+void
+function_reader::add_fixup_expr (file_location loc, rtx insn,
+				 const char *desc)
+{
+  gcc_assert (desc);
+  /* Fail early if the RTL reader erroneously hands us an int.  */
+  gcc_assert (!ISDIGIT (desc[0]));
+
+  m_fixups.safe_push (new fixup_expr (loc, insn, desc));
+}
+
+/* Record the information for later post-processing.  */
+
+void
+function_reader::add_fixup_pseudo (file_location loc, rtx x)
+{
+  m_fixups.safe_push (new fixup_pseudo (loc, x));
+}
+
+/* Helper function for consolidate_reg.  */
+
+static rtx
+lookup_global_register (int regno)
+{
+  /* We can't use a switch here, as some of the REGNUMs might not be constants
+     for some targets.  */
+  if (regno == STACK_POINTER_REGNUM)
+      return stack_pointer_rtx;
+  else if (regno ==  FRAME_POINTER_REGNUM)
+    return frame_pointer_rtx;
+  else if (regno == HARD_FRAME_POINTER_REGNUM)
+    return hard_frame_pointer_rtx;
+  else if (regno == ARG_POINTER_REGNUM)
+    return arg_pointer_rtx;
+  else if (regno == VIRTUAL_INCOMING_ARGS_REGNUM)
+    return virtual_incoming_args_rtx;
+  else if (regno == VIRTUAL_STACK_VARS_REGNUM)
+    return virtual_stack_vars_rtx;
+  else if (regno == VIRTUAL_STACK_DYNAMIC_REGNUM)
+    return virtual_stack_dynamic_rtx;
+  else if (regno == VIRTUAL_OUTGOING_ARGS_REGNUM)
+    return virtual_outgoing_args_rtx;
+  else if (regno == VIRTUAL_CFA_REGNUM)
+    return virtual_cfa_rtx;
+  else if (regno == VIRTUAL_PREFERRED_STACK_BOUNDARY_REGNUM)
+    return virtual_preferred_stack_boundary_rtx;
+#ifdef return_ADDRESS_POINTER_REGNUM
+  else if (regno == RETURN_ADDRESS_POINTER_REGNUM)
+    return return_address_pointer_rtx;
+#endif
+
+  return NULL;
+}
+
+/* Normally REG instances are created by gen_reg_rtx which updates
+   regno_reg_rtx, growing it as necessary.
+   The REG instances created from the dumpfile weren't created this
+   way, so we need to manually update regno_reg_rtx.  */
+
+static void
+ensure_regno (int regno)
+{
+  crtl->emit.ensure_regno_capacity (regno + 1);
+  gcc_assert (regno < crtl->emit.regno_pointer_align_length);
+
+  if (reg_rtx_no < regno + 1)
+    reg_rtx_no = regno + 1;
+}
+
+/* Helper function for consolidate_singletons, for handling REG instances.  */
+
+static rtx
+consolidate_reg (rtx x)
+{
+  gcc_assert (GET_CODE (x) == REG);
+
+  unsigned int regno = REGNO (x);
+
+  ensure_regno (regno);
+
+  /* Some register numbers have their rtx created in init_emit_regs
+     e.g. stack_pointer_rtx for STACK_POINTER_REGNUM.
+     Consolidate on this.  */
+  rtx global_reg = lookup_global_register (regno);
+  if (global_reg)
+    return global_reg;
+
+  /* Populate regno_reg_rtx if necessary.  */
+  if (regno_reg_rtx[regno] == NULL)
+    regno_reg_rtx[regno] = x;
+  /* Use it.  */
+  gcc_assert (GET_CODE (regno_reg_rtx[regno]) == REG);
+  gcc_assert (REGNO (regno_reg_rtx[regno]) == regno);
+  if (GET_MODE (x) == GET_MODE (regno_reg_rtx[regno]))
+    return regno_reg_rtx[regno];
+
+  return x;
+}
+
+/* When reading RTL function dumps, we must consolidate some
+   rtx so that we use singletons where singletons are expected
+   (e.g. we don't want multiple "(const_int 0 [0])" rtx, since
+   these are tested via pointer equality against const0_rtx.  */
+
+rtx
+function_reader::consolidate_singletons (rtx x)
+{
+  if (!x)
+    return x;
+
+ switch (GET_CODE (x))
+    {
+    /* FIXME: do we need to check for VOIDmode for these?  */
+    case PC: return pc_rtx;
+    case RETURN: return ret_rtx;
+    case SIMPLE_RETURN: return simple_return_rtx;
+    case CC0: return cc0_rtx;
+
+    case REG:
+      return consolidate_reg (x);
+
+    case CONST_INT:
+      return gen_rtx_CONST_INT (GET_MODE (x), INTVAL (x));
+
+    default:
+      break;
+    }
+
+  return x;
+}
+
+/* Implementation of rtx_reader::postprocess for reading function dumps.  */
+
+rtx
+function_reader::postprocess (rtx x)
+{
+  return consolidate_singletons (x);
+}
+
+/* Handle the optional location information written by print_rtx for
+   instructions.  Specifically, operand 4 of instructions (of type "i')
+   is printed thus:
+
+     if (INSN_HAS_LOCATION (in_insn))
+       {
+	 expanded_location xloc = insn_location (in_insn);
+	 fprintf (outfile, " %s:%i", xloc.file, xloc.line);
+       }
+
+    Hence we need to speculatively read a location of the form
+    " %s:%i", and unread the content if there wasn't one.
+
+    Assume that filenames can't contain whitespace, and can't
+    contain ':'.  */
+
+void
+function_reader::maybe_read_location (int operand_idx, rtx insn)
+{
+  file_location loc = get_current_location ();
+
+  /* Skip to first non-whitespace.  */
+  int ch = read_skip_spaces ();
+  auto_vec<char> buf;
+  buf.safe_push (ch);
+  while (1)
+    {
+      int ch = read_char ();
+      /* If we see a ':', assume we have a filename.  */
+      if (ch == ':')
+	{
+	  buf.safe_push ('\0');
+	  break;
+	}
+      buf.safe_push (ch);
+
+      /* If we see a space before ':', assume we don't have a
+	 filename.  */
+      if (ISSPACE (ch))
+	{
+	  while (!buf.is_empty ())
+	    unread_char (buf.pop ());
+	  return;
+	}
+    }
+  char *filename = buf.address ();
+  struct md_name name;
+  read_name (&name);
+
+  add_fixup_source_location (loc, insn, operand_idx,
+			     filename, atoi(name.string));
+}
+
+/* Apply all of the recorded fixups.  */
+
+void
+function_reader::apply_fixups ()
+{
+  /* Set up m_pseudo_offset so that the lowest REGNO seen for a pseudo in
+     the dump will be a pseudo after any fixup_pseudo instances are applied,
+     preferring to leave it as zero if possible (to avoid renumbering).  */
+  if (!bitmap_empty_p (&m_dumped_pseudos))
+    {
+      int lowest_pseudo_in_dump = bitmap_first_set_bit (&m_dumped_pseudos);
+      if (lowest_pseudo_in_dump < LAST_VIRTUAL_REGISTER + 1)
+	{
+	  m_pseudo_offset = (LAST_VIRTUAL_REGISTER + 1) - lowest_pseudo_in_dump;
+
+	  /* We may need to resize the regno-based arrays.  */
+	  int highest_pseudo_in_dump = bitmap_last_set_bit (&m_dumped_pseudos);
+	  int highest_effective_pseudo = highest_pseudo_in_dump + m_pseudo_offset;
+	  ensure_regno (highest_effective_pseudo);
+	}
+    }
+
+  int i;
+  fixup *f;
+  FOR_EACH_VEC_ELT (m_fixups, i, f)
+    f->apply (this);
+}
+
+/* Given a UID value, try to locate a pointer to the corresponding
+   rtx_insn *, or NULL if if can't be found.  */
+
+rtx_insn **
+function_reader::get_insn_by_uid (int uid)
+{
+  return m_insns_by_uid.get (uid);
+}
+
+/* Visit every LABEL_REF within JUMP_INSN, calling CB on it, providing it
+   with USER_DATA.
+   FIXME: is there a preexisting way to do this?  */
+
+static void
+for_each_label_ref (rtx_jump_insn *jump_insn,
+		    void (*cb) (rtx_jump_insn *jump_insn,
+				rtx label_ref,
+				void *user_data),
+		    void *user_data)
+{
+  if (any_condjump_p (jump_insn))
+    {
+      rtx label_ref = condjump_label (jump_insn);
+      cb (jump_insn, label_ref, user_data);
+      return;
+    }
+
+  if (simplejump_p (jump_insn))
+    {
+      rtx label_ref = SET_SRC (PATTERN (jump_insn));
+      cb (jump_insn, label_ref, user_data);
+      return;
+    }
+
+  rtx_jump_table_data *tablep;
+  if (tablejump_p (jump_insn, NULL, &tablep))
+    {
+      gcc_assert (tablep);
+      rtvec labels = tablep->get_labels ();
+      int i, veclen = GET_NUM_ELEM (labels);
+      for (i = 0; i < veclen; ++i)
+	{
+	  rtx label_ref = RTVEC_ELT (labels, i);
+	  cb (jump_insn, label_ref, user_data);
+	}
+      return;
+    }
+
+  if (ANY_RETURN_P (PATTERN (jump_insn)))
+    return;
+
+  /* TODO: something else?  */
+  gcc_unreachable ();
+}
+
+/* Create an edge from SRC to DST, with the given flags.  */
+
+static void
+add_edge (basic_block src, basic_block dst, int flags)
+{
+  if (0)
+    fprintf (stderr, "making edge %i->%i\n",
+	     src->index, dst->index);
+  unchecked_make_edge (src, dst, flags);
+}
+
+/* Edge-creation callback for function_reader::create_cfg_edges.  */
+
+static void
+add_edge_cb (rtx_jump_insn *jump_insn, rtx label_ref, void */*user_data*/)
+{
+  gcc_assert (jump_insn);
+  gcc_assert (label_ref);
+  rtx_insn *label_insn = as_a <rtx_insn *> (LABEL_REF_LABEL (label_ref));
+  add_edge (BLOCK_FOR_INSN (jump_insn), BLOCK_FOR_INSN (label_insn), 0);
+}
+
+/* Create edges within cfun's CFG, by examining instructions in the
+   basic blocks and reconstructing the edges accordingly.
+   This is done after fixups are applied, since the latter is responsible
+   for setting up BB_HEAD and BB_END within each basic block.
+
+   This assumes cfgrtl mode, in which the edges are implicit from
+   the jump instructions.  It won't work for cfglayout mode, which
+   represents unconditional jumps purely as edges within the CFG,
+   without instructions, and this information isn't (currently)
+   written out to dumps.  */
+
+void
+function_reader::recreate_implicit_cfg_edges ()
+{
+  /* Create edge from ENTRY_BLOCK to block of first insn.  */
+  basic_block entry = ENTRY_BLOCK_PTR_FOR_FN (cfun);
+  add_edge (entry, entry->next_bb, EDGE_FALLTHRU);
+
+  /* Create other edges.
+
+     The edge from the block of last insn to EXIT_BLOCK is created
+     below, by fall-through from the end of its block.  */
+  basic_block bb;
+  FOR_ALL_BB_FN (bb, cfun)
+    {
+      if (!BB_HEAD (bb))
+	continue;
+      rtx_insn *end_insn = BB_END (bb);
+      if (rtx_jump_insn *jump_insn = dyn_cast <rtx_jump_insn *> (end_insn))
+	{
+	  if (0)
+	    fprintf (stderr, "bb %i ends in jump\n", bb->index);
+	  if (!any_uncondjump_p (end_insn))
+	    {
+	      /* Add fallthrough edge first.  */
+	      gcc_assert (bb->next_bb);
+	      add_edge (bb, bb->next_bb, EDGE_FALLTHRU);
+	    }
+	  for_each_label_ref (jump_insn, add_edge_cb, NULL);
+	}
+      else
+	{
+	  if (0)
+	    fprintf (stderr, "bb %i ends in non-jump\n", bb->index);
+	  if (bb->next_bb != NULL)
+	    {
+	      /* Add fallthrough edge.  */
+	      gcc_assert (bb->next_bb);
+	      add_edge (bb, bb->next_bb, EDGE_FALLTHRU);
+	    }
+	}
+    }
+}
+
+/* Run the RTL dump parser.  If OUT_PSEUDO_OFFSET is non-NULL,
+   write any offset applied to the regnos of pseudoes to
+   *OUT_PSEUDO_OFFSET.  */
+
+bool
+read_rtl_function_body (int argc, const char **argv,
+			bool (*parse_opt) (const char *),
+			function_reader_policy *policy,
+			int *out_pseudo_offset)
+{
+  initialize_rtl ();
+  init_emit ();
+  init_varasm_status ();
+
+  function_reader reader (policy);
+  if (!reader.read_md_files (argc, argv, parse_opt))
+    return false;
+
+  if (out_pseudo_offset)
+    *out_pseudo_offset = reader.get_pseudo_offset ();
+
+  return true;
+}
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Verify that the src and dest indices and flags of edge E are as
+   expected, using LOC as the effective location when reporting
+   failures.  */
+
+static void
+assert_edge_at (const location &loc, edge e, int expected_src_idx,
+		int expected_dest_idx, int expected_flags)
+{
+  ASSERT_EQ_AT (loc, expected_src_idx, e->src->index);
+  ASSERT_EQ_AT (loc, expected_dest_idx, e->dest->index);
+  ASSERT_EQ_AT (loc, expected_flags, e->flags);
+}
+
+/* Verify that the src and dest indices and flags of EDGE are as
+   expected.  */
+
+#define ASSERT_EDGE(EDGE, EXPECTED_SRC_IDX, EXPECTED_DEST_IDX,		\
+		    EXPECTED_FLAGS)					\
+  assert_edge_at (SELFTEST_LOCATION, EDGE, EXPECTED_SRC_IDX, \
+		  EXPECTED_DEST_IDX, EXPECTED_FLAGS)
+
+/* Verify that we can load RTL dumps.  */
+
+static void
+test_loading_dump_fragment_1 ()
+{
+  // TODO: filter on target?
+  rtl_dump_test t (SELFTEST_LOCATION, locate_file ("asr_div1.rtl"));
+
+  /* Verify that the insns were loaded correctly.  */
+  rtx_insn *insn_8 = get_insns ();
+  ASSERT_TRUE (insn_8);
+  ASSERT_EQ (8, INSN_UID (insn_8));
+  ASSERT_EQ (INSN, GET_CODE (insn_8));
+  ASSERT_EQ (SET, GET_CODE (PATTERN (insn_8)));
+  ASSERT_EQ (NULL, PREV_INSN (insn_8));
+
+  rtx_insn *insn_9 = NEXT_INSN (insn_8);
+  ASSERT_TRUE (insn_9);
+  ASSERT_EQ (9, INSN_UID (insn_9));
+  ASSERT_EQ (INSN, GET_CODE (insn_9));
+  ASSERT_EQ (insn_8, PREV_INSN (insn_9));
+  ASSERT_EQ (NULL, NEXT_INSN (insn_9));
+
+  /* Verify that registers were remapped.  */
+  rtx insn_8_dest = SET_DEST (PATTERN (insn_8));
+  ASSERT_EQ (REG, GET_CODE (insn_8_dest));
+  ASSERT_EQ (t.effective_regno (78), REGNO (insn_8_dest));
+  rtx insn_8_src = SET_SRC (PATTERN (insn_8));
+  ASSERT_EQ (LSHIFTRT, GET_CODE (insn_8_src));
+  rtx reg = XEXP (insn_8_src, 0);
+  ASSERT_EQ (REG, GET_CODE (reg));
+  ASSERT_EQ (t.effective_regno (76), REGNO (reg));
+
+  /* Verify that get_insn_by_uid works.  */
+  ASSERT_EQ (insn_8, get_insn_by_uid (8));
+  ASSERT_EQ (insn_9, get_insn_by_uid (9));
+
+  /* Verify that basic blocks were created.  */
+  ASSERT_EQ (2, BLOCK_FOR_INSN (insn_8)->index);
+  ASSERT_EQ (2, BLOCK_FOR_INSN (insn_9)->index);
+
+  /* Verify that a sane CFG was created, with the implicit edges being
+     recreated. */
+  ASSERT_TRUE (cfun);
+  verify_three_block_rtl_cfg (cfun);
+  basic_block bb2 = BASIC_BLOCK_FOR_FN (cfun, 2);
+  ASSERT_TRUE (bb2 != NULL);
+  ASSERT_EQ (BB_RTL, bb2->flags & BB_RTL);
+  ASSERT_EQ (2, bb2->index);
+  ASSERT_EQ (insn_8, BB_HEAD (bb2));
+  ASSERT_EQ (insn_9, BB_END (bb2));
+}
+
+/* Verify loading another RTL dump; this time a dump of copying
+   a param on x86_64 from a hard reg into the frame.  */
+
+static void
+test_loading_dump_fragment_2 ()
+{
+  /* Only run this test for i386, since it hardcodes a hard reg name.  */
+#ifndef I386_OPTS_H
+  return;
+#endif
+
+  rtl_dump_test t (SELFTEST_LOCATION,
+		   locate_file ("copy-hard-reg-into-frame.rtl"));
+
+  rtx_insn *insn = get_insn_by_uid (2);
+
+  /* The block structure and indentation here is purely for
+     readability; it mirrors the structure of the rtx.  */
+  tree mem_expr;
+  {
+    rtx pat = PATTERN (insn);
+    ASSERT_EQ (SET, GET_CODE (pat));
+    {
+      rtx dest = SET_DEST (pat);
+      ASSERT_EQ (MEM, GET_CODE (dest));
+      /* Verify the "/c" was parsed.  */
+      ASSERT_TRUE (RTX_FLAG (dest, call));
+      ASSERT_EQ (SImode, GET_MODE (dest));
+      {
+	rtx addr = XEXP (dest, 0);
+	ASSERT_EQ (PLUS, GET_CODE (addr));
+	ASSERT_EQ (DImode, GET_MODE (addr));
+	{
+	  rtx lhs = XEXP (addr, 0);
+	  ASSERT_EQ (REG, GET_CODE (lhs));
+	  /* Verify the "/f" was parsed.  */
+	  ASSERT_TRUE (RTX_FLAG (lhs, frame_related));
+	  ASSERT_EQ (DImode, GET_MODE (lhs));
+	}
+	{
+	  rtx rhs = XEXP (addr, 1);
+	  ASSERT_EQ (CONST_INT, GET_CODE (rhs));
+	  ASSERT_EQ (-4, INTVAL (rhs));
+	}
+      }
+      /* Verify the "[1 i+0 S4 A32]" was parsed.  */
+      ASSERT_EQ (1, MEM_ALIAS_SET (dest));
+      /* "i" should have been handled by synthesizing a global int
+	 variable named "i".  */
+      mem_expr = MEM_EXPR (dest);
+      ASSERT_NE (mem_expr, NULL);
+      ASSERT_EQ (VAR_DECL, TREE_CODE (mem_expr));
+      ASSERT_EQ (integer_type_node, TREE_TYPE (mem_expr));
+      ASSERT_EQ (IDENTIFIER_NODE, TREE_CODE (DECL_NAME (mem_expr)));
+      ASSERT_STREQ ("i", IDENTIFIER_POINTER (DECL_NAME (mem_expr)));
+      /* "+0".  */
+      ASSERT_TRUE (MEM_OFFSET_KNOWN_P (dest));
+      ASSERT_EQ (0, MEM_OFFSET (dest));
+      /* "S4".  */
+      ASSERT_EQ (4, MEM_SIZE (dest));
+      /* "A32.  */
+      ASSERT_EQ (32, MEM_ALIGN (dest));
+    }
+    {
+      rtx src = SET_SRC (pat);
+      ASSERT_EQ (REG, GET_CODE (src));
+      ASSERT_EQ (SImode, GET_MODE (src));
+      ASSERT_EQ (5, REGNO (src));
+      tree reg_expr = REG_EXPR (src);
+      /* "i" here should point to the same var as for the MEM_EXPR.  */
+      ASSERT_EQ (reg_expr, mem_expr);
+    }
+  }
+}
+
+/* Verify that CODE_LABEL insns are loaded correctly.  */
+
+static void
+test_loading_labels ()
+{
+  rtl_dump_test t (SELFTEST_LOCATION, locate_file ("example-labels.rtl"));
+
+  rtx_insn *insn_1 = get_insn_by_uid (1);
+  ASSERT_EQ (CODE_LABEL, GET_CODE (insn_1));
+  ASSERT_EQ (NULL, LABEL_NAME (insn_1));
+  ASSERT_EQ (0, LABEL_NUSES (insn_1));
+
+  rtx_insn *insn_2 = get_insn_by_uid (2);
+  ASSERT_EQ (CODE_LABEL, GET_CODE (insn_2));
+  ASSERT_STREQ ("some_label_name", LABEL_NAME (insn_2));
+  ASSERT_EQ (57, LABEL_NUSES (insn_2));
+
+  /* Ensure that label names read from a dump are GC-managed
+     and are found through the insn.  */
+  forcibly_ggc_collect ();
+  ASSERT_TRUE (ggc_marked_p (insn_2));
+  ASSERT_TRUE (ggc_marked_p (LABEL_NAME (insn_2)));
+}
+
+/* Verify that the loader copes with an insn with a mode.  */
+
+static void
+test_loading_insn_with_mode ()
+{
+  rtl_dump_test t (SELFTEST_LOCATION, locate_file ("insn-with-mode.rtl"));
+  rtx_insn *insn = get_insn_by_uid (31);
+  ASSERT_EQ (INSN, GET_CODE (insn));
+
+  /* Verify that the "TI" mode was set from "insn:TI".  */
+  ASSERT_EQ (TImode, GET_MODE (insn));
+}
+
+/* Verify that the loader copes with a jump_insn to a label_ref.  */
+
+static void
+test_loading_jump_to_label_ref ()
+{
+  rtl_dump_test t (SELFTEST_LOCATION, locate_file ("jump-to-label-ref.rtl"));
+
+  rtx_insn *jump_insn = get_insn_by_uid (1);
+  ASSERT_EQ (JUMP_INSN, GET_CODE (jump_insn));
+
+  rtx_insn *barrier = get_insn_by_uid (2);
+  ASSERT_EQ (BARRIER, GET_CODE (barrier));
+
+  rtx_insn *code_label = get_insn_by_uid (3);
+  ASSERT_EQ (CODE_LABEL, GET_CODE (code_label));
+
+  /* Verify the jump_insn. */
+  ASSERT_EQ (4, BLOCK_FOR_INSN (jump_insn)->index);
+  ASSERT_EQ (SET, GET_CODE (PATTERN (jump_insn)));
+  /* Ensure that the "(pc)" is using the global singleton.  */
+  ASSERT_EQ (pc_rtx, SET_DEST (PATTERN (jump_insn)));
+  // FIXME: ^^^ use ASSERT_RTX_PTR_EQ here ^^^
+  rtx label_ref = SET_SRC (PATTERN (jump_insn));
+  ASSERT_EQ (LABEL_REF, GET_CODE (label_ref));
+  ASSERT_EQ (code_label, LABEL_REF_LABEL (label_ref));
+  ASSERT_EQ (code_label, JUMP_LABEL (jump_insn));
+
+  /* Verify the code_label. */
+  ASSERT_EQ (5, BLOCK_FOR_INSN (code_label)->index);
+  ASSERT_EQ (NULL, LABEL_NAME (code_label));
+  ASSERT_EQ (1, LABEL_NUSES (code_label));
+
+  /* Verify the generated CFG.  */
+
+  /* Locate blocks.  */
+  basic_block entry = ENTRY_BLOCK_PTR_FOR_FN (cfun);
+  ASSERT_TRUE (entry != NULL);
+  ASSERT_EQ (ENTRY_BLOCK, entry->index);
+
+  basic_block exit = EXIT_BLOCK_PTR_FOR_FN (cfun);
+  ASSERT_TRUE (exit != NULL);
+  ASSERT_EQ (EXIT_BLOCK, exit->index);
+
+  basic_block bb4 = (*cfun->cfg->x_basic_block_info)[4];
+  basic_block bb5 = (*cfun->cfg->x_basic_block_info)[5];
+  ASSERT_EQ (4, bb4->index);
+  ASSERT_EQ (5, bb5->index);
+
+  /* Entry block.  */
+  ASSERT_EQ (NULL, entry->preds);
+  ASSERT_EQ (1, entry->succs->length ());
+  ASSERT_EDGE ((*entry->succs)[0], 0, 4, EDGE_FALLTHRU);
+
+  /* bb4.  */
+  ASSERT_EQ (1, bb4->preds->length ());
+  ASSERT_EDGE ((*bb4->preds)[0], 0, 4, EDGE_FALLTHRU);
+  ASSERT_EQ (1, bb4->succs->length ());
+  ASSERT_EDGE ((*bb4->succs)[0], 4, 5, 0x0);
+
+  /* bb5.  */
+  ASSERT_EQ (1, bb5->preds->length ());
+  ASSERT_EDGE ((*bb5->preds)[0], 4, 5, 0x0);
+  ASSERT_EQ (1, bb5->succs->length ());
+  ASSERT_EDGE ((*bb5->succs)[0], 5, 1, EDGE_FALLTHRU);
+
+  /* Exit block.  */
+  ASSERT_EQ (1, exit->preds->length ());
+  ASSERT_EDGE ((*exit->preds)[0], 5, 1, EDGE_FALLTHRU);
+  ASSERT_EQ (NULL, exit->succs);
+}
+
+/* Verify that the loader copes with a jump_insn to a label_ref
+   marked "return".  */
+
+static void
+test_loading_jump_to_return ()
+{
+  rtl_dump_test t (SELFTEST_LOCATION, locate_file ("jump-to-return.rtl"));
+
+  rtx_insn *jump_insn = get_insn_by_uid (1);
+  ASSERT_EQ (JUMP_INSN, GET_CODE (jump_insn));
+  ASSERT_EQ (ret_rtx, JUMP_LABEL (jump_insn));
+  // FIXME: ^^^ use ASSERT_RTX_PTR_EQ here ^^^
+}
+
+/* Verify that the loader copes with a jump_insn to a label_ref
+   marked "simple_return".  */
+
+static void
+test_loading_jump_to_simple_return ()
+{
+  rtl_dump_test t (SELFTEST_LOCATION,
+		   locate_file ("jump-to-simple-return.rtl"));
+
+  rtx_insn *jump_insn = get_insn_by_uid (1);
+  ASSERT_EQ (JUMP_INSN, GET_CODE (jump_insn));
+  ASSERT_EQ (simple_return_rtx, JUMP_LABEL (jump_insn));
+  // FIXME: ^^^ use ASSERT_RTX_PTR_EQ here ^^^
+}
+
+/* Verify that the loader copes with a NOTE_INSN_BASIC_BLOCK.  */
+
+static void
+test_loading_note_insn_basic_block ()
+{
+  rtl_dump_test t (SELFTEST_LOCATION,
+		   locate_file ("note_insn_basic_block.rtl"));
+
+  rtx_insn *note = get_insn_by_uid (1);
+  ASSERT_EQ (NOTE, GET_CODE (note));
+  ASSERT_EQ (2, BLOCK_FOR_INSN (note)->index);
+
+  ASSERT_EQ (NOTE_INSN_BASIC_BLOCK, NOTE_KIND (note));
+  ASSERT_EQ (2, NOTE_BASIC_BLOCK (note)->index);
+  ASSERT_EQ (BASIC_BLOCK_FOR_FN (cfun, 2), NOTE_BASIC_BLOCK (note));
+}
+
+/* Verify that the loader copes with a NOTE_INSN_DELETED.  */
+
+static void
+test_loading_note_insn_deleted ()
+{
+  rtl_dump_test t (SELFTEST_LOCATION, locate_file ("note-insn-deleted.rtl"));
+
+  rtx_insn *note = get_insn_by_uid (1);
+  ASSERT_EQ (NOTE, GET_CODE (note));
+  ASSERT_EQ (NOTE_INSN_DELETED, NOTE_KIND (note));
+}
+
+/* Verify that the const_int values are consolidated, since
+   pointer equality corresponds to value equality.
+   TODO: do this for all in CASE_CONST_UNIQUE.  */
+
+static void
+test_loading_const_int ()
+{
+  rtl_dump_test t (SELFTEST_LOCATION, locate_file ("const-int.rtl"));
+
+  /* Verify that const_int values below MAX_SAVED_CONST_INT use
+     the global values.  */
+  ASSERT_EQ (const0_rtx, SET_SRC (PATTERN (get_insn_by_uid (100))));
+  ASSERT_EQ (const1_rtx, SET_SRC (PATTERN (get_insn_by_uid (101))));
+  ASSERT_EQ (constm1_rtx, SET_SRC (PATTERN (get_insn_by_uid (102))));
+
+  /* Verify that other const_int values are consolidated. */
+  rtx int256 = gen_rtx_CONST_INT (SImode, 256);
+  ASSERT_EQ (int256, SET_SRC (PATTERN (get_insn_by_uid (103))));
+}
+
+/* Verify that the loader copes with a SYMBOL_REF.  */
+
+static void
+test_loading_symbol_ref ()
+{
+  rtl_dump_test t (SELFTEST_LOCATION, locate_file ("symbol-ref.rtl"));
+
+  rtx_insn *insn = get_insn_by_uid (1045);
+
+  rtx high = SET_SRC (PATTERN (insn));
+  ASSERT_EQ (HIGH, GET_CODE (high));
+
+  rtx symbol_ref = XEXP (high, 0);
+  ASSERT_EQ (SYMBOL_REF, GET_CODE (symbol_ref));
+
+  /* Verify that "[flags 0xc0]" was parsed.  */
+  ASSERT_EQ (0xc0, SYMBOL_REF_FLAGS (symbol_ref));
+  /* TODO: we don't yet load SYMBOL_REF_DECL.  */
+}
+
+/* Verify that the loader copes with a call_insn dump.  */
+
+static void
+test_loading_call_insn_x86_64 ()
+{
+  /* Only run this test for i386.  */
+#ifndef I386_OPTS_H
+  return;
+#endif
+
+  rtl_dump_test t (SELFTEST_LOCATION, locate_file ("x86_64/call-insn.rtl"));
+
+  rtx_insn *insn_17 = get_insn_by_uid (17);
+  ASSERT_EQ (CALL_INSN, GET_CODE (insn_17));
+
+  /* "/j".  */
+  ASSERT_TRUE (RTX_FLAG (insn_17, jump));
+
+  rtx pat = PATTERN (insn_17);
+  ASSERT_EQ (CALL, GET_CODE (SET_SRC (pat)));
+
+  /* Verify REG_NOTES.  */
+  {
+    /* "(expr_list:REG_CALL_DECL".   */
+    ASSERT_EQ (EXPR_LIST, GET_CODE (REG_NOTES (insn_17)));
+    rtx_expr_list *note0 = as_a <rtx_expr_list *> (REG_NOTES (insn_17));
+    ASSERT_EQ (REG_CALL_DECL, REG_NOTE_KIND (note0));
+
+    /* "(expr_list:REG_EH_REGION (const_int 0 [0])".  */
+    rtx_expr_list *note1 = note0->next ();
+    ASSERT_EQ (REG_EH_REGION, REG_NOTE_KIND (note1));
+
+    ASSERT_EQ (NULL, note1->next ());
+  }
+
+  /* Verify CALL_INSN_FUNCTION_USAGE.  */
+  {
+    /* "(expr_list:DF (use (reg:DF 21 xmm0))".  */
+    rtx_expr_list *usage
+      = as_a <rtx_expr_list *> (CALL_INSN_FUNCTION_USAGE (insn_17));
+    ASSERT_EQ (EXPR_LIST, GET_CODE (usage));
+    ASSERT_EQ (DFmode, GET_MODE (usage));
+    ASSERT_EQ (USE, GET_CODE (usage->element ()));
+    ASSERT_EQ (NULL, usage->next ());
+  }
+}
+
+/* Verify that the loader copes a dump from print_rtx_function.  */
+
+static void
+test_loading_full_dump_aarch64 ()
+{
+  /* Only run this test for target==aarch64.  */
+#ifndef GCC_AARCH64_H
+  return;
+#endif
+
+  rtl_dump_test t (SELFTEST_LOCATION, locate_file ("aarch64/times-two.rtl"));
+
+  ASSERT_STREQ ("times_two", IDENTIFIER_POINTER (DECL_NAME (cfun->decl)));
+
+  rtx_insn *insn_1 = get_insn_by_uid (1);
+  ASSERT_EQ (NOTE, GET_CODE (insn_1));
+
+  rtx_insn *insn_15 = get_insn_by_uid (15);
+  ASSERT_EQ (INSN, GET_CODE (insn_15));
+  ASSERT_EQ (USE, GET_CODE (PATTERN (insn_15)));
+
+  /* Verify crtl->return_rtx.  */
+  ASSERT_EQ (REG, GET_CODE (crtl->return_rtx));
+  ASSERT_EQ (0, REGNO (crtl->return_rtx));
+  ASSERT_EQ (SImode, GET_MODE (crtl->return_rtx));
+}
+
+/* Verify that the loader copes a dump from print_rtx_function.  */
+
+static void
+test_loading_full_dump_x86_64 ()
+{
+  /* Only run this test for i386.  */
+#ifndef I386_OPTS_H
+  return;
+#endif
+
+  rtl_dump_test t (SELFTEST_LOCATION, locate_file ("x86_64/times-two.rtl"));
+
+  ASSERT_STREQ ("times_two", IDENTIFIER_POINTER (DECL_NAME (cfun->decl)));
+
+  rtx_insn *insn_1 = get_insn_by_uid (1);
+  ASSERT_EQ (NOTE, GET_CODE (insn_1));
+
+  rtx_insn *insn_7 = get_insn_by_uid (7);
+  ASSERT_EQ (INSN, GET_CODE (insn_7));
+  ASSERT_EQ (PARALLEL, GET_CODE (PATTERN (insn_7)));
+
+  rtx_insn *insn_15 = get_insn_by_uid (15);
+  ASSERT_EQ (INSN, GET_CODE (insn_15));
+  ASSERT_EQ (USE, GET_CODE (PATTERN (insn_15)));
+
+  /* Verify crtl->return_rtx.  */
+  ASSERT_EQ (REG, GET_CODE (crtl->return_rtx));
+  ASSERT_EQ (0, REGNO (crtl->return_rtx));
+  ASSERT_EQ (SImode, GET_MODE (crtl->return_rtx));
+}
+
+/* Verify that the loader can rebuild a CFG.  */
+
+static void
+test_loading_cfg ()
+{
+  rtl_dump_test t (SELFTEST_LOCATION, locate_file ("cfg-test.rtl"));
+
+  ASSERT_STREQ ("cfg_test", IDENTIFIER_POINTER (DECL_NAME (cfun->decl)));
+
+  ASSERT_TRUE (cfun);
+
+  ASSERT_TRUE (cfun->cfg != NULL);
+  ASSERT_EQ (6, n_basic_blocks_for_fn (cfun));
+  ASSERT_EQ (6, n_edges_for_fn (cfun));
+
+  /* The "fake" basic blocks.  */
+  basic_block entry = ENTRY_BLOCK_PTR_FOR_FN (cfun);
+  ASSERT_TRUE (entry != NULL);
+  ASSERT_EQ (ENTRY_BLOCK, entry->index);
+
+  basic_block exit = EXIT_BLOCK_PTR_FOR_FN (cfun);
+  ASSERT_TRUE (exit != NULL);
+  ASSERT_EQ (EXIT_BLOCK, exit->index);
+
+  /* The "real" basic blocks.  */
+  basic_block bb2 = (*cfun->cfg->x_basic_block_info)[2];
+  basic_block bb3 = (*cfun->cfg->x_basic_block_info)[3];
+  basic_block bb4 = (*cfun->cfg->x_basic_block_info)[4];
+  basic_block bb5 = (*cfun->cfg->x_basic_block_info)[5];
+
+  ASSERT_EQ (2, bb2->index);
+  ASSERT_EQ (3, bb3->index);
+  ASSERT_EQ (4, bb4->index);
+  ASSERT_EQ (5, bb5->index);
+
+  /* Verify connectivity.  */
+
+  /* Entry block.  */
+  ASSERT_EQ (NULL, entry->preds);
+  ASSERT_EQ (1, entry->succs->length ());
+  ASSERT_EDGE ((*entry->succs)[0], 0, 2, 0x1);
+
+  /* bb2.  */
+  ASSERT_EQ (1, bb2->preds->length ());
+  ASSERT_EDGE ((*bb2->preds)[0], 0, 2, 0x1);
+  ASSERT_EQ (2, bb2->succs->length ());
+  ASSERT_EDGE ((*bb2->succs)[0], 2, 3, 0x1);
+  ASSERT_EDGE ((*bb2->succs)[1], 2, 4, 0x1);
+
+  /* bb3.  */
+  ASSERT_EQ (1, bb3->preds->length ());
+  ASSERT_EDGE ((*bb3->preds)[0], 2, 3, 0x1);
+  ASSERT_EQ (1, bb3->succs->length ());
+  ASSERT_EDGE ((*bb3->succs)[0], 3, 5, 0x1);
+
+  /* bb4.  */
+  ASSERT_EQ (1, bb4->preds->length ());
+  ASSERT_EDGE ((*bb4->preds)[0], 2, 4, 0x1);
+  ASSERT_EQ (1, bb4->succs->length ());
+  ASSERT_EDGE ((*bb4->succs)[0], 4, 5, 0x1);
+
+  /* bb5.  */
+  ASSERT_EQ (2, bb5->preds->length ());
+  ASSERT_EDGE ((*bb5->preds)[0], 3, 5, 0x1);
+  ASSERT_EDGE ((*bb5->preds)[1], 4, 5, 0x1);
+  ASSERT_EQ (1, bb5->succs->length ());
+  ASSERT_EDGE ((*bb5->succs)[0], 5, 1, 0x1);
+
+  /* Exit block.  */
+  ASSERT_EQ (1, exit->preds->length ());
+  ASSERT_EDGE ((*exit->preds)[0], 5, 1, 0x1);
+  ASSERT_EQ (NULL, exit->succs);
+}
+
+/* Verify that renumbering pseudos works.  */
+
+static void
+test_regno_renumbering ()
+{
+  rtl_dump_test t (SELFTEST_LOCATION,
+		   locate_file ("test-regno-renumbering.rtl"));
+
+  rtx_insn *insn_1 = get_insn_by_uid (1);
+  rtx set = single_set (insn_1);
+  ASSERT_NE (NULL, set);
+  rtx dst = SET_DEST (set);
+  rtx src = SET_SRC (set);
+  /* The registers did not have names, and so should have been treated
+     as pseudos.
+     Verify that they have been renumbered, with the lower one
+     having LAST_VIRTUAL_REGISTER + 1.  */
+  ASSERT_EQ (LAST_VIRTUAL_REGISTER + 1, REGNO (dst));
+  ASSERT_EQ (LAST_VIRTUAL_REGISTER + 2, REGNO (src));
+}
+
+/* Run all of the selftests within this file.  */
+
+void
+read_rtl_function_c_tests ()
+{
+  test_loading_dump_fragment_1 ();
+  test_loading_dump_fragment_2 ();
+  test_loading_labels ();
+  test_loading_insn_with_mode ();
+  test_loading_jump_to_label_ref ();
+  test_loading_jump_to_return ();
+  test_loading_jump_to_simple_return ();
+  test_loading_note_insn_basic_block ();
+  test_loading_note_insn_deleted ();
+  test_loading_const_int ();
+  test_loading_symbol_ref ();
+  test_loading_call_insn_x86_64 ();
+  test_loading_full_dump_aarch64 ();
+  test_loading_full_dump_x86_64 ();
+  test_loading_cfg ();
+  test_regno_renumbering ();
+}
+
+} // namespace selftest
+
+#endif /* #if CHECKING_P */
diff --git a/gcc/read-rtl-function.h b/gcc/read-rtl-function.h
new file mode 100644
index 0000000..d26c797
--- /dev/null
+++ b/gcc/read-rtl-function.h
@@ -0,0 +1,37 @@ 
+/* read-rtl-function.h - Reader for RTL function dumps
+   Copyright (C) 2016 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_READ_RTL_FUNCTION_H
+#define GCC_READ_RTL_FUNCTION_H
+
+/* An optional policy class for class function_reader.  */
+
+struct function_reader_policy
+{
+  function_reader_policy ()
+  {
+  }
+};
+
+extern bool read_rtl_function_body (int argc, const char **argv,
+				    bool (*parse_opt) (const char *),
+				    function_reader_policy *policy,
+				    int *out_pseudo_offset);
+
+#endif /* GCC_READ_RTL_FUNCTION_H */
diff --git a/gcc/read-rtl.c b/gcc/read-rtl.c
index 2622bfe..5e8f0fb 100644
--- a/gcc/read-rtl.c
+++ b/gcc/read-rtl.c
@@ -17,7 +17,13 @@  You should have received a copy of the GNU General Public License
 along with GCC; see the file COPYING3.  If not see
 <http://www.gnu.org/licenses/>.  */
 
+/* This file is compiled twice: once for the generator programs
+   once for the compiler.  */
+#ifdef GENERATOR_FILE
 #include "bconfig.h"
+#else
+#include "config.h"
+#endif
 
 /* Disable rtl checking; it conflicts with the iterator handling.  */
 #undef ENABLE_RTL_CHECKING
@@ -30,6 +36,11 @@  along with GCC; see the file COPYING3.  If not see
 #include "read-md.h"
 #include "gensupport.h"
 
+#ifndef GENERATOR_FILE
+#include "function.h"
+#include "emit-rtl.h"
+#endif
+
 /* One element in a singly-linked list of (integer, string) pairs.  */
 struct map_value {
   struct map_value *next;
@@ -106,6 +117,7 @@  htab_t subst_attr_to_iter_map = NULL;
 const char *current_iterator_name;
 
 static void validate_const_int (const char *);
+static void one_time_initialization (void);
 
 /* Global singleton.  */
 rtx_reader *rtx_reader_ptr = NULL;
@@ -181,6 +193,8 @@  apply_int_iterator (void *loc, int value)
   *(int *)loc = value;
 }
 
+#ifdef GENERATOR_FILE
+
 /* This routine adds attribute or does nothing depending on VALUE.  When
    VALUE is 1, it does nothing - the first duplicate of original
    template is kept untouched when it's subjected to a define_subst.
@@ -252,6 +266,8 @@  bind_subst_iter_and_attr (const char *iter, const char *attr)
   *slot = value;
 }
 
+#endif /* #ifdef GENERATOR_FILE */
+
 /* Return name of a subst-iterator, corresponding to subst-attribute ATTR.  */
 
 static char*
@@ -418,6 +434,8 @@  copy_rtx_for_iterators (rtx original)
   return x;
 }
 
+#ifdef GENERATOR_FILE
+
 /* Return a condition that must satisfy both ORIGINAL and EXTRA.  If ORIGINAL
    has the form "&& ..." (as used in define_insn_and_splits), assume that
    EXTRA is already satisfied.  Empty strings are treated like "true".  */
@@ -581,6 +599,7 @@  apply_iterators (rtx original, vec<rtx> *queue)
 	}
     }
 }
+#endif /* #ifdef GENERATOR_FILE */
 
 /* Add a new "mapping" structure to hashtable TABLE.  NAME is the name
    of the mapping and GROUP is the group to which it belongs.  */
@@ -655,7 +674,9 @@  initialize_iterators (void)
   substs.iterators = htab_create (13, leading_string_hash,
 				 leading_string_eq_p, 0);
   substs.find_builtin = find_int; /* We don't use it, anyway.  */
+#ifdef GENERATOR_FILE
   substs.apply_iterator = apply_subst_iterator;
+#endif
 
   lower = add_mapping (&modes, modes.attrs, "mode");
   upper = add_mapping (&modes, modes.attrs, "MODE");
@@ -724,6 +745,8 @@  atoll (const char *p)
 }
 #endif
 
+
+#ifdef GENERATOR_FILE
 /* Process a define_conditions directive, starting with the optional
    space after the "define_conditions".  The directive looks like this:
 
@@ -765,6 +788,7 @@  read_conditions (void)
       add_c_test (expr, value);
     }
 }
+#endif /* #ifdef GENERATOR_FILE */
 
 static void
 validate_const_int (const char *string)
@@ -861,6 +885,8 @@  record_potential_iterator_use (struct iterator_group *group, void *ptr,
     }
 }
 
+#ifdef GENERATOR_FILE
+
 /* Finish reading a declaration of the form:
 
        (define... <name> [<value1> ... <valuen>])
@@ -1020,14 +1046,7 @@  check_code_iterator (struct mapping *iterator)
 bool
 read_rtx (const char *rtx_name, vec<rtx> *rtxen)
 {
-  static bool initialized = false;
-
-  /* Do one-time initialization.  */
-  if (!initialized)
-    {
-      initialize_iterators ();
-      initialized = true;
-    }
+  one_time_initialization ();
 
   /* Handle various rtx-related declarations that aren't themselves
      encoded as rtxes.  */
@@ -1082,6 +1101,103 @@  read_rtx (const char *rtx_name, vec<rtx> *rtxen)
   return true;
 }
 
+#endif /* #ifdef GENERATOR_FILE */
+
+/* Do one-time initialization.  */
+
+static void
+one_time_initialization (void)
+{
+  static bool initialized = false;
+
+  if (!initialized)
+    {
+      initialize_iterators ();
+      initialized = true;
+    }
+}
+
+/* Consume characters until encountering a character in TERMINATOR_CHARS,
+   consuming the terminator character if CONSUME_TERMINATOR is true.
+   Return all characters before the terminator as an allocated buffer.  */
+
+char *
+rtx_reader::read_until (const char *terminator_chars, bool consume_terminator)
+{
+  int ch = read_skip_spaces ();
+  unread_char (ch);
+  auto_vec<char> buf;
+  while (1)
+    {
+      ch = read_char ();
+      if (strchr (terminator_chars, ch))
+	{
+	  if (!consume_terminator)
+	    unread_char (ch);
+	  break;
+	}
+      buf.safe_push (ch);
+    }
+  buf.safe_push ('\0');
+  return xstrdup (buf.address ());
+}
+
+/* Subroutine of read_rtx_code, for parsing zero or more flags.  */
+
+static void
+read_flags (rtx return_rtx)
+{
+  while (1)
+    {
+      int ch = read_char ();
+      if (ch != '/')
+	{
+	  unread_char (ch);
+	  break;
+	}
+
+      int flag_char = read_char ();
+      switch (flag_char)
+	{
+	  case 's':
+	    RTX_FLAG (return_rtx, in_struct) = 1;
+	    break;
+	  case 'v':
+	    RTX_FLAG (return_rtx, volatil) = 1;
+	    break;
+	  case 'u':
+	    RTX_FLAG (return_rtx, unchanging) = 1;
+	    break;
+	  case 'f':
+	    RTX_FLAG (return_rtx, frame_related) = 1;
+	    break;
+	  case 'j':
+	    RTX_FLAG (return_rtx, jump) = 1;
+	    break;
+	  case 'c':
+	    RTX_FLAG (return_rtx, call) = 1;
+	    break;
+	  case 'i':
+	    RTX_FLAG (return_rtx, return_val) = 1;
+	    break;
+	  default:
+	    fatal_with_file_and_line ("unrecognized flag: `%c'", flag_char);
+	}
+    }
+}
+
+/* Return the numeric value n for GET_REG_NOTE_NAME (n) for STRING,
+   or fail if STRING isn't recognized.  */
+
+static int
+parse_reg_note_name (const char *string)
+{
+  for (int i = 0; i < REG_NOTE_MAX; i++)
+    if (0 == strcmp (string, GET_REG_NOTE_NAME (i)))
+      return i;
+  fatal_with_file_and_line ("unrecognized REG_NOTE name: `%s'", string);
+}
+
 /* Subroutine of read_rtx and read_nested_rtx.  CODE_NAME is the name of
    either an rtx code or a code iterator.  Parse the rest of the rtx and
    return it.  */
@@ -1090,7 +1206,7 @@  rtx
 rtx_reader::read_rtx_code (const char *code_name)
 {
   RTX_CODE code;
-  struct mapping *iterator;
+  struct mapping *iterator = NULL;
   const char *format_ptr;
   struct md_name name;
   rtx return_rtx;
@@ -1103,13 +1219,19 @@  rtx_reader::read_rtx_code (const char *code_name)
       rtx value;		/* Value of this node.  */
     };
 
+  one_time_initialization ();
+
   /* If this code is an iterator, build the rtx using the iterator's
      first value.  */
+#ifdef GENERATOR_FILE
   iterator = (struct mapping *) htab_find (codes.iterators, &code_name);
   if (iterator != 0)
     code = (enum rtx_code) iterator->values->number;
   else
     code = (enum rtx_code) codes.find_builtin (code_name);
+#else
+    code = (enum rtx_code) codes.find_builtin (code_name);
+#endif
 
   /* If we end up with an insn expression then we free this space below.  */
   return_rtx = rtx_alloc (code);
@@ -1120,6 +1242,26 @@  rtx_reader::read_rtx_code (const char *code_name)
   if (iterator)
     record_iterator_use (iterator, return_rtx);
 
+  /* Check for flags. */
+  read_flags (return_rtx);
+
+  /* Read REG_NOTE names for EXPR_LIST and INSN_LIST.  */
+  if ((GET_CODE (return_rtx) == EXPR_LIST
+       || GET_CODE (return_rtx) == INSN_LIST
+       || GET_CODE (return_rtx) == INT_LIST)
+      && !m_in_call_function_usage)
+    {
+      char ch = read_char ();
+      if (ch == ':')
+	{
+	  read_name (&name);
+	  PUT_MODE_RAW (return_rtx,
+			(machine_mode)parse_reg_note_name (name.string));
+	}
+      else
+	unread_char (ch);
+    }
+
   /* If what follows is `: mode ', read it and
      store the mode in the rtx.  */
 
@@ -1132,8 +1274,24 @@  rtx_reader::read_rtx_code (const char *code_name)
   else
     unread_char (c);
 
+  if (INSN_CHAIN_CODE_P (code))
+    {
+      read_name (&name);
+      INSN_UID (return_rtx) = atoi (name.string);
+    }
+
+  /* Use the format_ptr to parse the various operands of this rtx.
+     read_rtx_operand is a vfunc, allowing the parser to vary between
+     parsing .md files and parsing .rtl dumps; the function_reader subclass
+     overrides the defaults when loading RTL dumps, to handle the
+     various extra attributes emitted by print_rtx.  */
   for (int idx = 0; format_ptr[idx] != 0; idx++)
-    read_rtx_operand (return_rtx, idx);
+    return_rtx = read_rtx_operand (return_rtx, idx);
+
+  /* Call a vfunc to handle the various additional information that
+     print-rtl.c can write after the regular fields; does nothing when
+     parsing .md files.  */
+  handle_any_trailing_information (return_rtx);
 
   if (CONST_WIDE_INT_P (return_rtx))
     {
@@ -1217,6 +1375,9 @@  rtx_reader::read_rtx_operand (rtx return_rtx, int idx)
       break;
 
     case 'e':
+      XEXP (return_rtx, idx) = read_nested_rtx ();
+      break;
+
     case 'u':
       XEXP (return_rtx, idx) = read_nested_rtx ();
       break;
@@ -1292,7 +1453,10 @@  rtx_reader::read_rtx_operand (rtx return_rtx, int idx)
 	star_if_braced = (format_ptr[idx] == 'T');
 
 	stringbuf = read_string (star_if_braced);
+	if (!stringbuf)
+	  break;
 
+#ifdef GENERATOR_FILE
 	/* For insn patterns, we want to provide a default name
 	   based on the file and line, like "*foo.md:12", if the
 	   given name is blank.  These are only for define_insn and
@@ -1347,11 +1511,23 @@  rtx_reader::read_rtx_operand (rtx return_rtx, int idx)
 	    if (m != 0)
 	      record_iterator_use (m, return_rtx);
 	  }
+#endif /* #ifdef GENERATOR_FILE */
+
+	/* "stringbuf" was allocated within string_obstack and thus has
+	   the its lifetime restricted to that of the rtx_reader.  This is
+	   OK for the generator programs, but for non-generator programs,
+	   XSTR and XTMPL fields are meant to be allocated in the GC-managed
+	   heap.  Hence we need to allocate a copy in the GC-managed heap
+	   for the non-generator case.  */
+	const char *string_ptr = stringbuf;
+#ifndef GENERATOR_FILE
+	string_ptr = ggc_strdup (stringbuf);
+#endif /* #ifndef GENERATOR_FILE */
 
 	if (star_if_braced)
-	  XTMPL (return_rtx, idx) = stringbuf;
+	  XTMPL (return_rtx, idx) = string_ptr;
 	else
-	  XSTR (return_rtx, idx) = stringbuf;
+	  XSTR (return_rtx, idx) = string_ptr;
       }
       break;
 
@@ -1419,6 +1595,8 @@  rtx_reader::read_nested_rtx (void)
 
   require_char_ws (')');
 
+  return_rtx = postprocess (return_rtx);
+
   return return_rtx;
 }
 
@@ -1456,7 +1634,8 @@  rtx_reader::read_rtx_variadic (rtx form)
 /* Constructor for class rtx_reader.  */
 
 rtx_reader::rtx_reader ()
-: base_rtx_reader ()
+: base_rtx_reader (),
+  m_in_call_function_usage (false)
 {
   /* Set the global singleton pointer.  */
   rtx_reader_ptr = this;
diff --git a/gcc/rtl.h b/gcc/rtl.h
index ce1131b..0741fc6 100644
--- a/gcc/rtl.h
+++ b/gcc/rtl.h
@@ -3641,7 +3641,9 @@  extern void init_varasm_once (void);
 extern rtx make_debug_expr_from_rtl (const_rtx);
 
 /* In read-rtl.c */
+#ifdef GENERATOR_FILE
 extern bool read_rtx (const char *, vec<rtx> *);
+#endif
 
 /* In alias.c */
 extern rtx canon_rtx (rtx);
diff --git a/gcc/selftest-rtl.c b/gcc/selftest-rtl.c
new file mode 100644
index 0000000..0849cc6
--- /dev/null
+++ b/gcc/selftest-rtl.c
@@ -0,0 +1,90 @@ 
+/* Selftest support for RTL.
+   Copyright (C) 2016 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "selftest.h"
+#include "backend.h"
+#include "target.h"
+#include "rtl.h"
+#include "read-rtl-function.h"
+#include "read-md.h"
+#include "tree-core.h"
+#include "emit-rtl.h"
+#include "selftest-rtl.h"
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Constructor for selftest::rtl_dump_test.
+   Read a dumped RTL function from PATH.
+   Takes ownership of PATH, freeing in dtor.
+   Use LOC as the effective location when reporting failures.  */
+
+rtl_dump_test::rtl_dump_test (const location &loc, char *path)
+  : m_path (path), m_pseudo_offset (0)
+{
+  /* Parse the tempfile.  */
+  auto_vec<const char *> argv (2);
+  argv.safe_push (progname);
+  argv.safe_push (path);
+  bool read_ok
+    = read_rtl_function_body (argv.length (), argv.address (), NULL, NULL,
+			      &m_pseudo_offset);
+  ASSERT_TRUE_AT (loc, read_ok);
+}
+
+/* Destructor for selftest::rtl_dump_test.
+   Cleanup global state relating to the function, and free the path.  */
+
+selftest::rtl_dump_test::~rtl_dump_test ()
+{
+  /* Cleanups.  */
+  current_function_decl = NULL;
+  free_after_compilation (cfun);
+  set_cfun (NULL);
+  free (m_path);
+}
+
+/* Given INPUT_REGNO, a register number in the input RTL dump, return
+   the effective register number that was used.  */
+unsigned int
+selftest::rtl_dump_test::effective_regno (int input_regno) const
+{
+  return input_regno + m_pseudo_offset;
+}
+
+/* Get the insn with the given uid, or NULL if not found.  */
+
+rtx_insn *
+get_insn_by_uid (int uid)
+{
+  for (rtx_insn *insn = get_insns (); insn; insn = NEXT_INSN (insn))
+    if (INSN_UID (insn) == uid)
+      return insn;
+
+  /* Not found.  */
+  return NULL;
+}
+
+} // namespace selftest
+
+#endif /* #if CHECKING_P */
diff --git a/gcc/selftest-rtl.h b/gcc/selftest-rtl.h
new file mode 100644
index 0000000..4858f3a
--- /dev/null
+++ b/gcc/selftest-rtl.h
@@ -0,0 +1,67 @@ 
+/* A self-testing framework, for use by -fself-test.
+   Copyright (C) 2016 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_SELFTEST_RTL_H
+#define GCC_SELFTEST_RTL_H
+
+/* The selftest code should entirely disappear in a production
+   configuration, hence we guard all of it with #if CHECKING_P.  */
+
+#if CHECKING_P
+
+#include "read-rtl-function.h"
+
+namespace selftest {
+
+/* A class for testing RTL function dumps.  */
+
+class rtl_dump_test
+{
+ public:
+  /* Takes ownership of PATH.  */
+  rtl_dump_test (const location &loc, char *path);
+  ~rtl_dump_test ();
+
+  unsigned int effective_regno (int input_regno) const;
+
+ private:
+  char *m_path;
+  int m_pseudo_offset;
+};
+
+/* Wrapper class for initializing/cleaning up df.  */
+
+class dataflow_test
+{
+ public:
+  dataflow_test ();
+  ~dataflow_test ();
+};
+
+/* Get the insn with the given uid, or NULL if not found.  */
+
+extern rtx_insn *get_insn_by_uid (int uid);
+
+extern void verify_three_block_rtl_cfg (function *fun);
+
+} /* end of namespace selftest.  */
+
+#endif /* #if CHECKING_P */
+
+#endif /* GCC_SELFTEST_RTL_H */
diff --git a/gcc/selftest-run-tests.c b/gcc/selftest-run-tests.c
index ecc3d71..1dce665 100644
--- a/gcc/selftest-run-tests.c
+++ b/gcc/selftest-run-tests.c
@@ -70,6 +70,7 @@  selftest::run_tests ()
   tree_c_tests ();
   gimple_c_tests ();
   rtl_tests_c_tests ();
+  read_rtl_function_c_tests ();
 
   /* Higher-level tests, or for components that other selftests don't
      rely on.  */
diff --git a/gcc/selftest.h b/gcc/selftest.h
index 8b9c007..ae39101 100644
--- a/gcc/selftest.h
+++ b/gcc/selftest.h
@@ -213,6 +213,7 @@  extern void hash_map_tests_c_tests ();
 extern void hash_set_tests_c_tests ();
 extern void input_c_tests ();
 extern void pretty_print_c_tests ();
+extern void read_rtl_function_c_tests ();
 extern void rtl_tests_c_tests ();
 extern void selftest_c_tests ();
 extern void spellcheck_c_tests ();
diff --git a/gcc/testsuite/selftests/aarch64/times-two.rtl b/gcc/testsuite/selftests/aarch64/times-two.rtl
new file mode 100644
index 0000000..8d198f2
--- /dev/null
+++ b/gcc/testsuite/selftests/aarch64/times-two.rtl
@@ -0,0 +1,42 @@ 
+(function "times_two"
+  (insn-chain
+    (note 1 0 4 (nil) NOTE_INSN_DELETED)
+    (note 4 1 2 2 [bb 2] NOTE_INSN_BASIC_BLOCK)
+    (insn 2 4 3 2 (set (mem/c:SI (plus:DI (reg/f:DI 68 virtual-stack-vars)
+                        (const_int -4 [0xfffffffffffffffc])) [1 i+0 S4 A32])
+                (reg:SI 0 x0 [ i ])) times-two.c:2 -1
+             (nil))
+    (note 3 2 6 2 NOTE_INSN_FUNCTION_BEG)
+    (insn 6 3 7 2 (set (reg:SI 75)
+                (mem/c:SI (plus:DI (reg/f:DI 68 virtual-stack-vars)
+                        (const_int -4 [0xfffffffffffffffc])) [1 i+0 S4 A32])) times-two.c:3 -1
+             (nil))
+    (insn 7 6 10 2 (set (reg:SI 73 [ _2 ])
+                (ashift:SI (reg:SI 75)
+                    (const_int 1 [0x1]))) times-two.c:3 -1
+             (nil))
+    (insn 10 7 14 2 (set (reg:SI 74 [ <retval> ])
+                (reg:SI 73 [ _2 ])) times-two.c:3 -1
+             (nil))
+    (insn 14 10 15 2 (set (reg/i:SI 0 x0)
+                (reg:SI 74 [ <retval> ])) times-two.c:4 -1
+             (nil))
+    (insn 15 14 0 2 (use (reg/i:SI 0 x0)) times-two.c:4 -1
+             (nil))
+  ) ;; insn-chain
+  (cfg
+    (bb 0
+      (edge 0 2 (flags 0x1))
+    ) ;; bb
+    (bb 2
+      (edge 2 1 (flags 0x1))
+    ) ;; bb
+    (bb 1
+    ) ;; bb
+  ) ;; cfg
+  (crtl
+    (return_rtx 
+      (reg/i:SI 0 x0)
+    ) ;; return_rtx
+  ) ;; crtl
+) ;; function "times_two"
diff --git a/gcc/testsuite/selftests/asr_div1.rtl b/gcc/testsuite/selftests/asr_div1.rtl
new file mode 100644
index 0000000..2c2a825
--- /dev/null
+++ b/gcc/testsuite/selftests/asr_div1.rtl
@@ -0,0 +1,23 @@ 
+;; Taken from
+;;    gcc/testsuite/gcc.dg/asr_div1.c -O2 -fdump-rtl-all -mtune=cortex-a53
+;; for aarch64, hand editing the prev/next insns to 0 as needed, and
+;; editing whitespace to avoid over-long lines.  */
+
+(function "f1"
+  (insn-chain
+(insn 8 0 9 2 (set (reg:DI 78)
+        (lshiftrt:DI (reg:DI 76)
+            (const_int 32 [0x20])))
+        ../../src/gcc/testsuite/gcc.dg/asr_div1.c:14
+        641 {*aarch64_lshr_sisd_or_int_di3}
+     (expr_list:REG_DEAD (reg:DI 76)
+        (nil)))
+(insn 9 8 0 2 (set (reg:SI 79)
+        (ashiftrt:SI (subreg:SI (reg:DI 78) 0)
+            (const_int 3 [0x3])))
+        ../../src/gcc/testsuite/gcc.dg/asr_div1.c:14
+        642 {*aarch64_ashr_sisd_or_int_si3}
+     (expr_list:REG_DEAD (reg:DI 78)
+        (nil)))
+  ) ;; insn-chain
+) ;; function
diff --git a/gcc/testsuite/selftests/cfg-test.rtl b/gcc/testsuite/selftests/cfg-test.rtl
new file mode 100644
index 0000000..d6be7f9
--- /dev/null
+++ b/gcc/testsuite/selftests/cfg-test.rtl
@@ -0,0 +1,38 @@ 
+/* Example of a loading a CFG like this:
+       0  (entry)
+       |
+       2
+      / \
+     3   4
+      \ /
+       5
+       |
+       1  (exit).  */
+
+(function "cfg_test"
+  (insn-chain
+     (note 2 0 3 2 [bb 2] NOTE_INSN_BASIC_BLOCK)  
+     (note 3 2 4 3 [bb 3] NOTE_INSN_BASIC_BLOCK)  
+     (note 4 3 5 4 [bb 4] NOTE_INSN_BASIC_BLOCK)  
+     (note 5 4 0 5 [bb 5] NOTE_INSN_BASIC_BLOCK)  
+  ) ;; empty
+  (cfg
+     (bb 0 ;; entry
+       (edge 0 2 (flags 0x1))
+     ) ;; bb
+     (bb 2
+       (edge 2 3 (flags 0x1))
+       (edge 2 4 (flags 0x1))
+     ) ;; bb
+     (bb 3
+       (edge 3 5 (flags 0x1))
+     ) ;; bb
+     (bb 4
+       (edge 4 5 (flags 0x1))
+     ) ;; bb
+     (bb 5
+       (edge 5 1 (flags 0x1))
+     ) ;; bb
+     (bb 1) ;; exit
+  ) ;; cfg
+)
diff --git a/gcc/testsuite/selftests/const-int.rtl b/gcc/testsuite/selftests/const-int.rtl
new file mode 100644
index 0000000..933a0bf
--- /dev/null
+++ b/gcc/testsuite/selftests/const-int.rtl
@@ -0,0 +1,16 @@ 
+(function "const_int_examples"
+  (insn-chain
+    (insn 100 0 101 2
+      (set (reg:SI 100) (const_int 0 [0x0]))
+        test.c:2 -1 (nil))
+    (insn 101 100 102 2
+      (set (reg:SI 101) (const_int 1 [0x1]))
+        test.c:2 -1 (nil))
+    (insn 102 101 103 2
+      (set (reg:SI 102) (const_int -1 [0xffffffff]))
+        test.c:2 -1 (nil))
+    (insn 103 102 0 2
+      (set (reg:SI 103) (const_int 256 [0x100]))
+        test.c:2 -1 (nil))
+  ) ;; insn-chain
+) ;; function
diff --git a/gcc/testsuite/selftests/copy-hard-reg-into-frame.rtl b/gcc/testsuite/selftests/copy-hard-reg-into-frame.rtl
new file mode 100644
index 0000000..a8abee4
--- /dev/null
+++ b/gcc/testsuite/selftests/copy-hard-reg-into-frame.rtl
@@ -0,0 +1,12 @@ 
+(function "copy_hard_reg_into_frame"
+  (insn-chain
+    (insn 2 0 0 2
+      (set (mem/c:SI
+          (plus:DI
+            (reg/f:DI 20 frame)
+            (const_int -4 [0xfffffffffffffffc]))
+          [1 i+0 S4 A32])
+       (reg:SI 5 di [ i ])) test.c:2 -1
+     (nil))
+  ) ;; insn-chain
+) ;; function
diff --git a/gcc/testsuite/selftests/example-labels.rtl b/gcc/testsuite/selftests/example-labels.rtl
new file mode 100644
index 0000000..edadcc9
--- /dev/null
+++ b/gcc/testsuite/selftests/example-labels.rtl
@@ -0,0 +1,6 @@ 
+(function "example_labels"
+  (insn-chain
+    (code_label 1 0 2 6 3 (nil) [0 uses])
+    (code_label 2 1 0 6 3 ("some_label_name") [57 uses])
+  ) ;; insn-chain
+) ;; function
diff --git a/gcc/testsuite/selftests/insn-with-mode.rtl b/gcc/testsuite/selftests/insn-with-mode.rtl
new file mode 100644
index 0000000..890e5f9
--- /dev/null
+++ b/gcc/testsuite/selftests/insn-with-mode.rtl
@@ -0,0 +1,5 @@ 
+(function "insn_with_mode"
+  (insn-chain
+    (insn:TI 31 0 0 2 (set (reg:SI 100) (reg:SI 101)) -1 (nil))
+  ) ;; insn-chain
+) ;; function
diff --git a/gcc/testsuite/selftests/jump-to-label-ref.rtl b/gcc/testsuite/selftests/jump-to-label-ref.rtl
new file mode 100644
index 0000000..1089997
--- /dev/null
+++ b/gcc/testsuite/selftests/jump-to-label-ref.rtl
@@ -0,0 +1,11 @@ 
+(function "jump_to_label_ref"
+  (insn-chain
+    (jump_insn 1 0 2 4 (set (pc)
+        (label_ref 3)) ../../src/gcc/testsuite/rtl.dg/test.c:4 -1
+     (nil)
+       -> 3)
+    (barrier 2 1 3)
+    (code_label 3 2 0 5 2 (nil) [1 uses])
+  ) ;; insn-chain
+) ;; function
+
diff --git a/gcc/testsuite/selftests/jump-to-return.rtl b/gcc/testsuite/selftests/jump-to-return.rtl
new file mode 100644
index 0000000..1316d03
--- /dev/null
+++ b/gcc/testsuite/selftests/jump-to-return.rtl
@@ -0,0 +1,10 @@ 
+(function "jump_to_return"
+  (insn-chain
+    (jump_insn 1 0 2 4 (set (pc)
+        (label_ref 3)) ../../src/gcc/testsuite/rtl.dg/test.c:4 -1
+     (nil)
+      -> return)
+    (barrier 2 1 3)
+    (code_label 3 2 0 5 2 (nil) [1 uses])
+  ) ;; insn-chain
+) ;; function
diff --git a/gcc/testsuite/selftests/jump-to-simple-return.rtl b/gcc/testsuite/selftests/jump-to-simple-return.rtl
new file mode 100644
index 0000000..ab27b85
--- /dev/null
+++ b/gcc/testsuite/selftests/jump-to-simple-return.rtl
@@ -0,0 +1,11 @@ 
+(function "jump_to_simple_return"
+  (insn-chain
+    (jump_insn 1 0 2 4 (set (pc)
+        (label_ref 3)) ../../src/gcc/testsuite/rtl.dg/test.c:4 -1
+     (nil)
+    -> simple_return)
+    (barrier 2 1 3)
+    (code_label 3 2 0 5 2 (nil) [1 uses])
+  ) ;; insn-chain
+) ;; function
+
diff --git a/gcc/testsuite/selftests/note-insn-deleted.rtl b/gcc/testsuite/selftests/note-insn-deleted.rtl
new file mode 100644
index 0000000..baf36cc
--- /dev/null
+++ b/gcc/testsuite/selftests/note-insn-deleted.rtl
@@ -0,0 +1,5 @@ 
+(function "example_note"
+  (insn-chain
+    (note 1 0 0 (nil) NOTE_INSN_DELETED)
+  ) ;; insn-chain
+) ;; function
diff --git a/gcc/testsuite/selftests/note_insn_basic_block.rtl b/gcc/testsuite/selftests/note_insn_basic_block.rtl
new file mode 100644
index 0000000..11eb14d
--- /dev/null
+++ b/gcc/testsuite/selftests/note_insn_basic_block.rtl
@@ -0,0 +1,5 @@ 
+(function "example_of_note"
+  (insn-chain
+     (note 1 0 0 2 [bb 2] NOTE_INSN_BASIC_BLOCK)
+  ) ;; insn-chain
+) ;; function
diff --git a/gcc/testsuite/selftests/symbol-ref.rtl b/gcc/testsuite/selftests/symbol-ref.rtl
new file mode 100644
index 0000000..226295d
--- /dev/null
+++ b/gcc/testsuite/selftests/symbol-ref.rtl
@@ -0,0 +1,7 @@ 
+(function "example_of_symbol_ref"
+  (insn-chain
+    (insn 1045 0 0 2 (set (reg:SI 480)
+        (high:SI (symbol_ref:SI ("isl_obj_map_vtable") [flags 0xc0] <var_decl 0x7fa0363ea240 isl_obj_map_vtable>))) y.c:12702 -1
+     (nil))
+  ) ;; insn-chain
+) ;; function
diff --git a/gcc/testsuite/selftests/test-regno-renumbering.rtl b/gcc/testsuite/selftests/test-regno-renumbering.rtl
new file mode 100644
index 0000000..007666f
--- /dev/null
+++ b/gcc/testsuite/selftests/test-regno-renumbering.rtl
@@ -0,0 +1,6 @@ 
+(function "test"
+  (insn-chain
+    ;; The regs here have no names and are thus to be treated as pseudos.
+    (insn 1 0 0 2 (set (reg:SI 10) (reg:SI 11)) -1 (nil))
+  ) ;; insn-chain
+) ;; function
diff --git a/gcc/testsuite/selftests/x86_64/call-insn.rtl b/gcc/testsuite/selftests/x86_64/call-insn.rtl
new file mode 100644
index 0000000..b3be835
--- /dev/null
+++ b/gcc/testsuite/selftests/x86_64/call-insn.rtl
@@ -0,0 +1,12 @@ 
+(function "test"
+  (insn-chain
+    (call_insn/j 17 0 0 2 (set (reg:DF 21 xmm0)
+                (call (mem:QI (symbol_ref:DI ("sqrt") [flags 0x41]  <function_decl 0x7f82b1429d00 sqrt>) [0 __builtin_sqrt S1 A8])
+                    (const_int 0 [0]))) test.c:19 -1
+             (expr_list:REG_CALL_DECL (symbol_ref:DI ("sqrt") [flags 0x41]  <function_decl 0x7f82b1429d00 sqrt>)
+                (expr_list:REG_EH_REGION (const_int 0 [0])
+                    (nil)))
+            (expr_list:DF (use (reg:DF 21 xmm0))
+                (nil)))
+  ) ;; insn-chain
+) ;; function "test"
diff --git a/gcc/testsuite/selftests/x86_64/times-two.rtl b/gcc/testsuite/selftests/x86_64/times-two.rtl
new file mode 100644
index 0000000..9eaee45
--- /dev/null
+++ b/gcc/testsuite/selftests/x86_64/times-two.rtl
@@ -0,0 +1,57 @@ 
+;; Dump of this C function:
+;;
+;; int times_two (int i)
+;; {
+;;   return i * 2;
+;; }
+;;
+;; after expand for target==x86_64
+
+(function "times_two"
+  (insn-chain
+    (note 1 0 4 (nil) NOTE_INSN_DELETED)
+    (note 4 1 2 2 [bb 2] NOTE_INSN_BASIC_BLOCK)
+    (insn 2 4 3 2 (set (mem/c:SI (plus:DI (reg/f:DI 82 virtual-stack-vars)
+                        (const_int -4 [0xfffffffffffffffc])) [1 i+0 S4 A32])
+                (reg:SI 5 di [ i ])) t.c:2 -1
+             (nil))
+    (note 3 2 6 2 NOTE_INSN_FUNCTION_BEG)
+    (insn 6 3 7 2 (set (reg:SI 89)
+                (mem/c:SI (plus:DI (reg/f:DI 82 virtual-stack-vars)
+                        (const_int -4 [0xfffffffffffffffc])) [1 i+0 S4 A32])) t.c:3 -1
+             (nil))
+    (insn 7 6 10 2 (parallel [
+                    (set (reg:SI 87 [ _2 ])
+                        (ashift:SI (reg:SI 89)
+                            (const_int 1 [0x1])))
+                    (clobber (reg:CC 17 flags))
+                ]) t.c:3 -1
+             (expr_list:REG_EQUAL (ashift:SI (mem/c:SI (plus:DI (reg/f:DI 82 virtual-stack-vars)
+                            (const_int -4 [0xfffffffffffffffc])) [1 i+0 S4 A32])
+                    (const_int 1 [0x1]))
+                (nil)))
+    (insn 10 7 14 2 (set (reg:SI 88 [ <retval> ])
+                (reg:SI 87 [ _2 ])) t.c:3 -1
+             (nil))
+    (insn 14 10 15 2 (set (reg/i:SI 0 ax)
+                (reg:SI 88 [ <retval> ])) t.c:4 -1
+             (nil))
+    (insn 15 14 0 2 (use (reg/i:SI 0 ax)) t.c:4 -1
+             (nil))
+  ) ;; insn-chain
+  (cfg
+    (bb 0
+      (edge 0 2 (flags 0x1))
+    ) ;; bb
+    (bb 2
+      (edge 2 1 (flags 0x1))
+    ) ;; bb
+    (bb 1
+    ) ;; bb
+  ) ;; cfg
+  (crtl
+    (return_rtx
+      (reg/i:SI 0 ax)
+    ) ;; return_rtx
+  ) ;; crtl
+) ;; function "times_two"
diff --git a/gcc/tree-dfa.c b/gcc/tree-dfa.c
index 9a3b072..ec63b34 100644
--- a/gcc/tree-dfa.c
+++ b/gcc/tree-dfa.c
@@ -305,6 +305,11 @@  ssa_default_def (struct function *fn, tree var)
   gcc_assert (TREE_CODE (var) == VAR_DECL
 	      || TREE_CODE (var) == PARM_DECL
 	      || TREE_CODE (var) == RESULT_DECL);
+
+  /* Always NULL_TREE for rtl function dumps.  */
+  if (!fn->gimple_df)
+    return NULL_TREE;
+
   in.var = (tree)&ind;
   ind.uid = DECL_UID (var);
   return DEFAULT_DEFS (fn)->find_with_hash ((tree)&in, DECL_UID (var));