From bf19c8d95e313ee265de9c908b7621a6335c5e4d Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Thu, 12 May 2016 13:02:30 -0400
Subject: [PATCH 06/36] Introduce class function_reader

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.
	* cfgexpand.c (pass_expand::execute): Move stack initializations
	to rtl_data::init_stack_alignment and call it.  Pass "true"
	for new "emit_insns" param of expand_function_start.
	* emit-rtl.c (gen_reg_rtx): Move regno_pointer_align and
	regno_reg_rtx resizing logic to...
	(emit_status::ensure_regno_capacity): ...this new method.
	(init_emit): Allocate regno_reg_rtx using ggc_cleared_vec_alloc
	rather than ggc_vec_alloc.
	(rtl_data::init_stack_alignment): New method.
	(get_insn_by_uid): New function.
	* emit-rtl.h (rtl_data::init_stack_alignment): New method.
	* 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)).
	(expand_function_start): Add param "emit_insns", and use it to
	guard the various gen/emit calls.
	* function.h (emit_status::ensure_regno_capacity): New method.
	(expand_function_start): Add bool param to decl.
	* gensupport.c (gen_reader::gen_reader): Add NULL for new policy
	param of rtx_reader ctor.
	* 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 '/'.
	(require_char): New function.
	(require_word_ws): New function.
	(peek_char): New function.
	(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.  */
	(rtx_reader::rtx_reader): Add rtl_reader_policy * param, using
	it to initialize m_policy.
	(rtx_reader::~rtx_reader): Free m_base_dir.  Clean up global data.
	* read-md.h (struct file_location): Move to errors.h.
	(file_location::file_location): Likewise.
	Include errors.h.
	(class regno_remapper): New class.
	(struct rtl_reader_policy): New struct.
	(rtx_reader::rtx_reader): Add rtl_reader_policy * param.
	(rtx_reader::add_fixup_insn_uid): New vfunc.
	(rtx_reader::add_fixup_bb): New vfunc.
	(rtx_reader::add_fixup_note_insn_basic_block): New vfunc.
	(rtx_reader::add_fixup_source_location): New vfunc.
	(rtx_reader::add_fixup_jump_label): New vfunc.
	(rtx_reader::add_fixup_expr): New vfunc.
	(rtx_reader::remap_regno): New method.
	(rtx_reader::m_policy): New field.
	(noop_reader::noop_reader): Add NULL for new policy param of
	rtx_reader ctor.
	(peek_char): New decl.
	(require_char): New decl.
	(require_word_ws): New decl.
	(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.
	(read_rtx): Likewise.  Move one-time initialization logic to...
	(one_time_initialization): New function.
	(parse_reg_note_name): New function.
	(parse_note_insn_name): New function.
	(maybe_read_location): New function.
	(read_until): New function.
	(strip_trailing_whitespace): New function.
	(read_flags): New function.
	(regno_remapper::get_effective_regno): New method.
	(rtx_reader::remap_regno): New method.
	(read_rtx_code): Remove "static".  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 SYMBOL_REF data, REG_NOTE names,
	INSN_UID values, note-specific data, basic block IDs,
	jump labels, hexdump values of wide ints, REG_EXPR, MEM_EXPR,
	skipping recognized insn names, remapping register numbers.
	When on host, reallocate XSTR and XTMPL fields in the GC-managed
	heap.
	(lookup_global_register): New function.
	(consolidate_reg): New function.
	(consolidate_singletons): New function.
	(read_nested_rtx): Call consolidate_singletons.
	* rtl.h (read_rtx): Wrap decl with #ifdef GENERATOR_FILE.
	(read_rtx_code): New decl.
	* 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/Makefile.in          |    5 +
 gcc/cfgexpand.c          |    7 +-
 gcc/emit-rtl.c           |   70 ++-
 gcc/emit-rtl.h           |    2 +
 gcc/errors.c             |   23 +-
 gcc/errors.h             |   13 +
 gcc/function-tests.c     |    2 +-
 gcc/function.c           |   41 +-
 gcc/function.h           |    4 +-
 gcc/gensupport.c         |    2 +-
 gcc/print-rtl.c          |    4 +-
 gcc/read-md.c            |  109 ++++-
 gcc/read-md.h            |  100 +++-
 gcc/read-rtl-function.c  | 1197 ++++++++++++++++++++++++++++++++++++++++++++++
 gcc/read-rtl-function.h  |   29 ++
 gcc/read-rtl.c           |  757 ++++++++++++++++++++++++++++-
 gcc/rtl.h                |    4 +
 gcc/selftest-rtl.c       |   81 ++++
 gcc/selftest-rtl.h       |   66 +++
 gcc/selftest-run-tests.c |    1 +
 gcc/selftest.h           |    1 +
 gcc/tree-dfa.c           |    5 +
 22 files changed, 2434 insertions(+), 89 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

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 7c18285..341c188 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1265,6 +1265,7 @@ OBJS = \
 	dwarf2cfi.o \
 	dwarf2out.o \
 	emit-rtl.o \
+	errors.o \
 	et-forest.o \
 	except.o \
 	explow.o \
@@ -1401,6 +1402,9 @@ OBJS = \
 	print-rtl.o \
 	print-tree.o \
 	profile.o \
+	read-md.o \
+	read-rtl.o \
+	read-rtl-function.o \
 	real.o \
 	realmpfr.o \
 	recog.o \
@@ -1428,6 +1432,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/cfgexpand.c b/gcc/cfgexpand.c
index 130a16b..f97e1fd 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -6219,10 +6219,7 @@ pass_expand::execute (function *fun)
   discover_nonconstant_array_refs ();
 
   targetm.expand_to_rtl_hook ();
-  crtl->stack_alignment_needed = STACK_BOUNDARY;
-  crtl->max_used_stack_slot_alignment = STACK_BOUNDARY;
-  crtl->stack_alignment_estimated = 0;
-  crtl->preferred_stack_boundary = STACK_BOUNDARY;
+  crtl->init_stack_alignment ();
   fun->cfg->max_jumptable_ents = 0;
 
   /* Resovle the function section.  Some targets, like ARM EABI rely on knowledge
@@ -6254,7 +6251,7 @@ pass_expand::execute (function *fun)
     }
 
   /* Set up parameters and prepare for return, for the function.  */
-  expand_function_start (current_function_decl);
+  expand_function_start (current_function_decl, true);
 
   /* If we emitted any instructions for setting up the variables,
      emit them before the FUNCTION_START note.  */
diff --git a/gcc/emit-rtl.c b/gcc/emit-rtl.c
index a724608..4fdf708 100644
--- a/gcc/emit-rtl.c
+++ b/gcc/emit-rtl.c
@@ -1056,29 +1056,35 @@ gen_reg_rtx (machine_mode mode)
   /* Do not call gen_reg_rtx with uninitialized crtl.  */
   gcc_assert (crtl->emit.regno_pointer_align_length);
 
-  /* Make sure regno_pointer_align, and regno_reg_rtx are large
-     enough to have an element for this pseudo reg number.  */
+  int cur_size = crtl->emit.regno_pointer_align_length;
+  if (reg_rtx_no == cur_size)
+    crtl->emit.ensure_regno_capacity (cur_size * 2);
 
-  if (reg_rtx_no == crtl->emit.regno_pointer_align_length)
-    {
-      int old_size = crtl->emit.regno_pointer_align_length;
-      char *tmp;
-      rtx *new1;
+  val = gen_raw_REG (mode, reg_rtx_no);
+  regno_reg_rtx[reg_rtx_no++] = val;
+  return val;
+}
 
-      tmp = XRESIZEVEC (char, crtl->emit.regno_pointer_align, old_size * 2);
-      memset (tmp + old_size, 0, old_size);
-      crtl->emit.regno_pointer_align = (unsigned char *) tmp;
+/* Make sure m_regno_pointer_align, and regno_reg_rtx are large
+   enough to have elements in the range 0 <= idx < NEW_SIZE.  */
 
-      new1 = GGC_RESIZEVEC (rtx, regno_reg_rtx, old_size * 2);
-      memset (new1 + old_size, 0, old_size * sizeof (rtx));
-      regno_reg_rtx = new1;
+void
+emit_status::ensure_regno_capacity (int new_size)
+{
+  if (new_size < regno_pointer_align_length)
+    return;
 
-      crtl->emit.regno_pointer_align_length = old_size * 2;
-    }
+  int old_size = regno_pointer_align_length;
 
-  val = gen_raw_REG (mode, reg_rtx_no);
-  regno_reg_rtx[reg_rtx_no++] = val;
-  return val;
+  char *tmp = XRESIZEVEC (char, regno_pointer_align, new_size);
+  memset (tmp + old_size, 0, new_size - old_size);
+  regno_pointer_align = (unsigned char *) tmp;
+
+  rtx *new1 = GGC_RESIZEVEC (rtx, regno_reg_rtx, new_size);
+  memset (new1 + old_size, 0, (new_size - old_size) * sizeof (rtx));
+  regno_reg_rtx = new1;
+
+  crtl->emit.regno_pointer_align_length = new_size;
 }
 
 /* Return TRUE if REG is a PARM_DECL, FALSE otherwise.  */
@@ -5686,7 +5692,8 @@ init_emit (void)
   crtl->emit.regno_pointer_align
     = XCNEWVEC (unsigned char, crtl->emit.regno_pointer_align_length);
 
-  regno_reg_rtx = ggc_vec_alloc<rtx> (crtl->emit.regno_pointer_align_length);
+  regno_reg_rtx =
+    ggc_cleared_vec_alloc<rtx> (crtl->emit.regno_pointer_align_length);
 
   /* Put copies of all the hard registers into regno_reg_rtx.  */
   memcpy (regno_reg_rtx,
@@ -6304,5 +6311,30 @@ need_atomic_barrier_p (enum memmodel model, bool pre)
       gcc_unreachable ();
     }
 }
+
+/* Initialize fields of rtl_data related to stack alignment.  */
+
+void
+rtl_data::init_stack_alignment ()
+{
+  stack_alignment_needed = STACK_BOUNDARY;
+  max_used_stack_slot_alignment = STACK_BOUNDARY;
+  stack_alignment_estimated = 0;
+  preferred_stack_boundary = STACK_BOUNDARY;
+}
+
+/* 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;
+}
+
 
 #include "gt-emit-rtl.h"
diff --git a/gcc/emit-rtl.h b/gcc/emit-rtl.h
index 52c72b1..663b4a0 100644
--- a/gcc/emit-rtl.h
+++ b/gcc/emit-rtl.h
@@ -55,6 +55,8 @@ struct GTY(()) incoming_args {
 
 /* Datastructures maintained for currently processed function in RTL form.  */
 struct GTY(()) rtl_data {
+  void init_stack_alignment ();
+
   struct expr_status expr;
   struct emit_status emit;
   struct varasm_status varasm;
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..c63f06b 100644
--- a/gcc/errors.h
+++ b/gcc/errors.h
@@ -28,8 +28,21 @@ 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);
+
+  const char *filename;
+  int lineno;
+};
+
+inline file_location::file_location (const char *filename_in, int lineno_in)
+  : filename (filename_in), lineno (lineno_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 53bad87..fe9a07d 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))
@@ -5087,10 +5088,12 @@ stack_protect_epilogue (void)
    emitting RTL.
    SUBR is the FUNCTION_DECL node.
    PARMS_HAVE_CLEANUPS is nonzero if there are cleanups associated with
-   the function's parameters, which must be run at any return statement.  */
+   the function's parameters, which must be run at any return statement.
+   EMIT_INSNS is true if instructions are to emitted, false to avoid this
+   (for use when loading RTL function dumps).  */
 
 void
-expand_function_start (tree subr)
+expand_function_start (tree subr, bool emit_insns)
 {
   /* Make sure volatile mem refs aren't considered
      valid operands of arithmetic insns.  */
@@ -5106,7 +5109,8 @@ expand_function_start (tree subr)
   /* Make the label for return statements to jump to.  Do not special
      case machines with special return instructions -- they will be
      handled later during jump, ifcvt, or epilogue creation.  */
-  return_label = gen_label_rtx ();
+  if (emit_insns)
+    return_label = gen_label_rtx ();
 
   /* Initialize rtx used to return the value.  */
   /* Do this before assign_parms so that we copy the struct value address
@@ -5132,7 +5136,7 @@ expand_function_start (tree subr)
 	  /* Expect to be passed the address of a place to store the value.
 	     If it is passed as an argument, assign_parms will take care of
 	     it.  */
-	  if (sv)
+	  if (sv && emit_insns)
 	    {
 	      value_address = gen_reg_rtx (Pmode);
 	      emit_move_insn (value_address, sv);
@@ -5209,7 +5213,7 @@ expand_function_start (tree subr)
   assign_parms (subr);
 
   /* If function gets a static chain arg, store it.  */
-  if (cfun->static_chain_decl)
+  if (cfun->static_chain_decl && emit_insns)
     {
       tree parm = cfun->static_chain_decl;
       rtx local, chain;
@@ -5253,7 +5257,7 @@ expand_function_start (tree subr)
 
   /* If the function receives a non-local goto, then store the
      bits we need to restore the frame pointer.  */
-  if (cfun->nonlocal_goto_save_area)
+  if (cfun->nonlocal_goto_save_area && emit_insns)
     {
       tree t_save;
       rtx r_save;
@@ -5276,22 +5280,25 @@ expand_function_start (tree subr)
      The move is supposed to make sdb output more accurate.  */
   /* Indicate the beginning of the function body,
      as opposed to parm setup.  */
-  emit_note (NOTE_INSN_FUNCTION_BEG);
+  if (emit_insns)
+    {
+      emit_note (NOTE_INSN_FUNCTION_BEG);
 
-  gcc_assert (NOTE_P (get_last_insn ()));
+      gcc_assert (NOTE_P (get_last_insn ()));
 
-  parm_birth_insn = get_last_insn ();
+      parm_birth_insn = get_last_insn ();
 
-  if (crtl->profile)
-    {
+      if (crtl->profile)
+	{
 #ifdef PROFILE_HOOK
-      PROFILE_HOOK (current_function_funcdef_no);
+	  PROFILE_HOOK (current_function_funcdef_no);
 #endif
-    }
+	}
 
-  /* If we are doing generic stack checking, the probe should go here.  */
-  if (flag_stack_check == GENERIC_STACK_CHECK)
-    stack_check_probe_note = emit_note (NOTE_INSN_DELETED);
+      /* If we are doing generic stack checking, the probe should go here.  */
+      if (flag_stack_check == GENERIC_STACK_CHECK)
+	stack_check_probe_note = emit_note (NOTE_INSN_DELETED);
+    }
 }
 
 void
diff --git a/gcc/function.h b/gcc/function.h
index 590a490..40aa910 100644
--- a/gcc/function.h
+++ b/gcc/function.h
@@ -34,6 +34,8 @@ struct GTY(()) sequence_stack {
 };
 
 struct GTY(()) emit_status {
+  void ensure_regno_capacity (int new_size);
+
   /* This is reset to LAST_VIRTUAL_REGISTER + 1 at the start of each function.
      After rtl generation, it is 1 plus the largest register number used.  */
   int x_reg_rtx_no;
@@ -619,7 +621,7 @@ extern void pop_dummy_function (void);
 extern void init_dummy_function_start (void);
 extern void init_function_start (tree);
 extern void stack_protect_epilogue (void);
-extern void expand_function_start (tree);
+extern void expand_function_start (tree, bool);
 extern void expand_dummy_function_end (void);
 
 extern void thread_prologue_and_epilogue_insns (void);
diff --git a/gcc/gensupport.c b/gcc/gensupport.c
index 3abb259b..378e0bf 100644
--- a/gcc/gensupport.c
+++ b/gcc/gensupport.c
@@ -2234,7 +2234,7 @@ process_define_subst (void)
 class gen_reader : public rtx_reader
 {
  public:
-  gen_reader () : rtx_reader () {}
+  gen_reader () : rtx_reader (NULL) {}
   void handle_unknown_directive (file_location, const char *);
 };
 
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 77e558c..217ff49 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.  */
 
@@ -345,7 +347,7 @@ read_skip_spaces (void)
 	    if (c != '*')
 	      {
 		unread_char (c);
-		fatal_with_file_and_line ("stray '/' in file");
+		return '/';
 	      }
 
 	    prevc = 0;
@@ -364,6 +366,16 @@ read_skip_spaces (void)
     }
 }
 
+/* Consume the next character, issuing a fatal error if it is not
+   EXPECTED.  */
+
+void require_char (char expected)
+{
+  int ch = read_char ();
+  if (ch != expected)
+    fatal_expected_char (expected, ch);
+}
+
 /* Consume any whitespace, then consume the next non-whitespace
    character, issuing a fatal error if it is not EXPECTED.  */
 
@@ -375,6 +387,17 @@ require_char_ws (char expected)
     fatal_expected_char (expected, ch);
 }
 
+/* Consume any whitespace, then consume the next word (as per read_name),
+   issuing a fatal error if it is not EXPECTED.  */
+
+void require_word_ws (const char *expected)
+{
+  struct md_name name;
+  read_name (&name);
+  if (strcmp (name.string, expected))
+    fatal_with_file_and_line ("missing '%s'", expected);
+}
+
 /* Read the next character from the file.  */
 
 int
@@ -399,11 +422,21 @@ rtx_reader::unread_char (int ch)
   ungetc (ch, m_read_md_file);
 }
 
+/* Peek at the next character from the file without consuming it.  */
+
+int
+peek_char (void)
+{
+  int ch = read_char ();
+  unread_char (ch);
+  return ch;
+}
+
 /* 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;
@@ -441,8 +474,12 @@ read_name (struct md_name *name)
       c = read_char ();
     }
 
+  unread_char (c);
+  *out_loc = 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;
@@ -463,6 +500,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 = 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.
@@ -608,6 +675,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);
 
@@ -902,12 +977,13 @@ traverse_enum_types (htab_trav callback, void *info)
 
 /* Constructor for rtx_reader.  */
 
-rtx_reader::rtx_reader ()
+rtx_reader::rtx_reader (rtl_reader_policy *policy)
 : m_toplevel_fname (NULL),
   m_read_md_filename (NULL),
   m_read_md_lineno (0),
   m_first_dir_md_include (NULL),
-  m_last_dir_md_include_ptr (&m_first_dir_md_include)
+  m_last_dir_md_include_ptr (&m_first_dir_md_include),
+  m_policy (policy)
 {
   /* Set the global singleton pointer.  */
   rtx_reader_ptr = this;
@@ -917,6 +993,27 @@ rtx_reader::rtx_reader ()
 
 rtx_reader::~rtx_reader ()
 {
+  free (m_base_dir);
+
+  /* Clean up global data.  */
+  htab_delete (enum_types);
+  enum_types = NULL;
+
+  htab_delete (md_constants);
+  md_constants = NULL;
+
+  obstack_free (&joined_conditions_obstack, NULL);
+
+  htab_delete (joined_conditions);
+  joined_conditions = NULL;
+
+  obstack_free (&ptr_loc_obstack, NULL);
+
+  htab_delete (ptr_locs);
+  ptr_locs = NULL;
+
+  obstack_free (&string_obstack, NULL);
+
   /* Clear the global singleton pointer.  */
   rtx_reader_ptr = NULL;
 }
diff --git a/gcc/read-md.h b/gcc/read-md.h
index 82a628b..33d33c4 100644
--- a/gcc/read-md.h
+++ b/gcc/read-md.h
@@ -21,18 +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);
-
-  const char *filename;
-  int lineno;
-};
-
-inline file_location::file_location (const char *filename_in, int lineno_in)
-  : filename (filename_in), lineno (lineno_in) {}
+#include "errors.h"
 
 /* Holds one symbol or number in the .md file.  */
 struct md_name {
@@ -90,10 +79,41 @@ struct enum_type {
   unsigned int num_values;
 };
 
+/* A class for remapping register numbers in dumpfiles to
+   equivalent register numbers for the current target.  */
+
+class regno_remapper
+{
+ public:
+  /* Zero means "do nothing", otherwise DUMPED_FIRST_PSEUDO_REGNO
+     is the lowest numbered pseudo-reg seen in the dumpfile.  */
+  regno_remapper (int dumped_first_pseudo_regno)
+    : m_dumped_first_pseudo_regno (dumped_first_pseudo_regno) {}
+
+  unsigned int get_effective_regno (int dumped_regno) const;
+
+ private:
+  int m_dumped_first_pseudo_regno;
+};
+
+/* An optional policy class for class rtx_reader.  */
+
+struct rtl_reader_policy
+{
+  rtl_reader_policy (regno_remapper *regno_remapper_)
+  : m_regno_remapper (regno_remapper_)
+  {
+  }
+
+  regno_remapper *m_regno_remapper;
+};
+
+/* A class for reading .md files and RTL dump files.  */
+
 class rtx_reader
 {
  public:
-  rtx_reader ();
+  rtx_reader (rtl_reader_policy *policy);
   virtual ~rtx_reader ();
 
   bool read_md_files (int, const char **, bool (*) (const char *));
@@ -104,6 +124,47 @@ class rtx_reader
      unread character is the optional space after the directive name.  */
   virtual void handle_unknown_directive (file_location, const char *) = 0;
 
+  /* A hook for the RTL function dump reader to override, to record insn UIDs
+     for later fixup.  */
+  virtual void add_fixup_insn_uid (file_location /*loc*/, rtx /*insn*/,
+				   int /*operand_idx*/, int /*insn_uid*/)
+  {}
+
+  /* A hook for the RTL function dump reader to override, to record
+     basic block IDs for later fixup.  */
+  virtual void add_fixup_bb (file_location /*loc*/, rtx /*insn*/,
+			     int /*operand_idx*/, int /*bb_idx*/)
+  {}
+
+  /* A hook for the RTL function dump reader to override, to record
+     basic block IDs for fixup of NOTE_INSN_BASIC_BLOCK.  */
+  virtual void add_fixup_note_insn_basic_block (file_location /*loc*/,
+						rtx /*insn*/,
+						int /*operand_idx*/,
+						int /*bb_idx*/)
+  {}
+
+  /* A hook for the RTL function dump reader to override, to record
+     source location information for fixing up into location_t values. */
+  virtual void add_fixup_source_location (file_location /*loc*/, rtx /*insn*/,
+					  int /*operand_idx*/,
+					  const char */*filename*/,
+					  int /*lineno*/)
+  {}
+
+  /* A hook for the RTL function dump reader to override, to record
+     label names for fixing up the JUMP_LABEL of an insn.  */
+  virtual void add_fixup_jump_label (file_location /*loc*/, rtx /*insn*/,
+				     int /*operand_idx*/,
+				     const char */*label*/)
+  {}
+
+  /* A hook for the RTL function dump reader to override, to record
+     textual tree dumps for fix up the expr of an rtx (REG or MEM).  */
+  virtual void add_fixup_expr (file_location /*loc*/, rtx /*x*/,
+			       const char */*desc*/)
+  {}
+
   file_location get_current_location () const;
 
   int read_char (void);
@@ -113,6 +174,8 @@ class rtx_reader
   const char *get_filename () const { return m_read_md_filename; }
   int get_lineno () const { return m_read_md_lineno; }
 
+  int remap_regno (int dumped_regno) const;
+
  private:
   /* A singly-linked list of filenames.  */
   struct file_name_list {
@@ -149,6 +212,8 @@ class rtx_reader
 
   /* A pointer to the null terminator of the md include chain.  */
   file_name_list **m_last_dir_md_include_ptr;
+
+  rtl_reader_policy *m_policy;
 };
 
 /* Global singleton.  */
@@ -159,7 +224,7 @@ extern rtx_reader *rtx_reader_ptr;
 class noop_reader : public rtx_reader
 {
  public:
-  noop_reader () : rtx_reader () {}
+  noop_reader () : rtx_reader (NULL) {}
 
   /* A dummy implementation which skips unknown directives.  */
   void handle_unknown_directive (file_location, const char *);
@@ -184,6 +249,8 @@ unread_char (int ch)
   rtx_reader_ptr->unread_char (ch);
 }
 
+extern int peek_char (void);
+
 extern hashval_t leading_string_hash (const void *);
 extern int leading_string_eq_p (const void *, const void *);
 extern void copy_md_ptr_loc (const void *, const void *);
@@ -199,8 +266,11 @@ extern void fatal_with_file_and_line (const char *, ...)
   ATTRIBUTE_PRINTF_1 ATTRIBUTE_NORETURN;
 extern void fatal_expected_char (int, int) ATTRIBUTE_NORETURN;
 extern int read_skip_spaces (void);
+extern void require_char (char expected);
 extern void require_char_ws (char expected);
-extern void read_name (struct md_name *);
+extern void require_word_ws (const char *expected);
+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..a108d5e
--- /dev/null
+++ b/gcc/read-rtl-function.c
@@ -0,0 +1,1197 @@
+/* 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"
+
+class function_reader;
+
+/* 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;
+};
+
+/* Subclass of rtx_reader for reading function dumps.  */
+
+class function_reader : public rtx_reader
+{
+ public:
+  function_reader (rtl_reader_policy *policy);
+  ~function_reader ();
+
+  void handle_unknown_directive (file_location, const char *);
+
+  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 create_function ();
+  void apply_fixups ();
+  void create_cfg_edges ();
+
+  rtx_insn **get_insn_by_uid (int uid);
+
+  rtx_insn *get_first_insn () const { return m_first_insn; }
+
+  tree parse_mem_expr (const char *desc);
+
+ private:
+  void create_cfg_and_basic_blocks ();
+
+ private:
+  struct uid_hash : int_hash <int, -1, -2> {};
+  hash_map<uid_hash, rtx_insn *> m_insns_by_uid;
+  auto_vec<fixup *> m_fixups;
+  bitmap_head m_bb_indices;
+  rtx_insn *m_first_insn;
+  auto_vec<tree> m_fake_scope;
+};
+
+/* 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));
+    }
+}
+
+/* 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)
+{
+  if (0 == strcmp (desc, "<retval>"))
+    {
+      tree fndecl = cfun->decl;
+      return DECL_RESULT (fndecl);
+    }
+
+  /* FIXME: use a hash rather than linear search.  */
+  int i;
+  tree t;
+  FOR_EACH_VEC_ELT (m_fake_scope, i, t)
+    if (0 == strcmp (desc, IDENTIFIER_POINTER (DECL_NAME (t))))
+      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;
+}
+
+/* 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 ();
+    }
+}
+
+/* class function_reader : public rtx_reader */
+
+/* function_reader's constructor.  */
+
+function_reader::function_reader (rtl_reader_policy *policy)
+: rtx_reader (policy),
+  m_first_insn (NULL)
+{
+  bitmap_initialize (&m_bb_indices, NULL);
+  bitmap_set_bit (&m_bb_indices, ENTRY_BLOCK);
+  bitmap_set_bit (&m_bb_indices, EXIT_BLOCK);
+}
+
+/* function_reader's destructor.  */
+
+function_reader::~function_reader ()
+{
+  int i;
+  fixup *f;
+  FOR_EACH_VEC_ELT (m_fixups, i, f)
+    delete f;
+}
+
+/* Implementation of rtx_reader::handle_unknown_directive.
+   Parse rtx instructions by calling read_rtx_code, calling
+   set_first_insn and set_last_insn as appropriate.  */
+
+void
+function_reader::handle_unknown_directive (file_location /*start_loc*/,
+					   const char *name)
+{
+  rtx x = read_rtx_code (name);
+  if (!x)
+    return;
+  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);
+}
+
+/* Implementation of rtx_reader::add_fixup_insn_uid.
+   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));
+}
+
+/* Implementation of rtx_reader::add_fixup_bb.
+   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, bb_idx);
+  m_fixups.safe_push (new fixup_bb (loc, insn, operand_idx, bb_idx));
+}
+
+/* Implementation of rtx_reader::add_fixup_note_insn_basic_block.
+   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));
+}
+
+/* Implementation of rtx_reader::add_fixup_source_location.
+   Record the information for later post-processing.  */
+void
+function_reader::add_fixup_source_location (file_location, rtx,
+					    int, const char *, int)
+{
+  /* Empty for now.  */
+}
+
+/* Implementation of rtx_reader::add_fixup_jump_label.
+   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));
+}
+
+/* Implementation of rtx_reader::add_fixup_expr.
+   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));
+}
+
+/* Set up state for the function *before* fixups are applied.
+
+   Create "cfun" and a decl for the function.
+   For the moment, every function decl is hardcoded as
+      int test_1 (int i, int j, int k);
+   Set up various other state:
+   - state set up by expand_function_start (e.g. crtl->return_rtx).
+   - 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 ("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.  */
+
+  /* Normally, various state gets set up by expand_function_start, e.g.
+     crtl->return_rtx, based on DECL_RESULT (fndecl).
+     We call it here to ensure the state is set up, with emit_insns as
+     false, so no new instructions are emitted.  */
+  current_function_decl = fndecl;
+  expand_function_start (fndecl, false);
+
+  create_cfg_and_basic_blocks ();
+  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;
+  }
+}
+
+/* Apply all of the recorded fixups.  */
+
+void
+function_reader::apply_fixups ()
+{
+  /* line_table should now be populated; every deferred_location should
+     now have an m_srcloc.  */
+
+  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);
+}
+
+/* Create cfun's CFG and populate with blocks, a helper
+   function for function_reader::create_function ().
+
+   The edges are created later on, 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);
+  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, 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;
+}
+
+/* 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::create_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);
+	    }
+	}
+    }
+}
+
+static void
+postprocess (function_reader *reader)
+{
+  reader->create_function ();
+  reader->apply_fixups ();
+  reader->create_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 ();
+}
+
+bool
+read_rtl_function_body (int argc, const char **argv,
+			bool (*parse_opt) (const char *),
+			rtl_reader_policy *policy)
+{
+  initialize_rtl ();
+  init_emit ();
+  init_varasm_status ();
+
+  function_reader reader (policy);
+  if (!reader.read_md_files (argc, argv, parse_opt))
+    return false;
+
+  postprocess (&reader);
+
+  return true;
+}
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Verify that we can load RTL dumps.  */
+
+static void
+test_loading_dump_fragment_1 ()
+{
+  /* 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.  */
+  // TODO: filter on target?
+  const char *input_dump
+    = ("(insn 8 0 9 2 (set (reg:DI 78)\n"
+       "        (lshiftrt:DI (reg:DI 76)\n"
+       "            (const_int 32 [0x20])))\n"
+       "        ../../src/gcc/testsuite/gcc.dg/asr_div1.c:14\n"
+       "        641 {*aarch64_lshr_sisd_or_int_di3}\n"
+       "     (expr_list:REG_DEAD (reg:DI 76)\n"
+       "        (nil)))\n"
+       "(insn 9 8 0 2 (set (reg:SI 79)\n"
+       "        (ashiftrt:SI (subreg:SI (reg:DI 78) 0)\n"
+       "            (const_int 3 [0x3])))\n"
+       "        ../../src/gcc/testsuite/gcc.dg/asr_div1.c:14\n"
+       "        642 {*aarch64_ashr_sisd_or_int_si3}\n"
+       "     (expr_list:REG_DEAD (reg:DI 78)\n"
+       "        (nil)))\n");
+  const int dumped_first_pseudo_regno = 76;
+  rtl_dump_test t (input_dump, dumped_first_pseudo_regno);
+
+  /* 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.  */
+  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 ()
+{
+  const char *input_dump
+    =  ("(insn 2 0 0 2\n"
+	"  (set (mem/c:SI\n"
+	"          (plus:DI\n"
+	"            (reg/f:DI 20 frame)\n"
+	"            (const_int -4 [0xfffffffffffffffc]))\n"
+	"          [1 i+0 S4 A32])\n"
+	"       (reg:SI 5 di [ i ])) test.c:2 -1\n"
+	"     (nil))\n");
+  rtl_dump_test t (input_dump);
+
+  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 ()
+{
+  const char *input_dump
+    = ("(code_label 1 0 2 6 3 (nil) [0 uses])\n"
+       "(code_label 2 1 0 6 3 (\"some_label_name\") [57 uses])\n");
+  rtl_dump_test t (input_dump);
+
+  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 ()
+{
+  const char *input_dump
+    = "(insn:TI 31 0 0 2 (set (reg:SI 100) (reg:SI 101)) -1 (nil))";
+  const int dumped_first_pseudo_regno = 100;
+  rtl_dump_test t (input_dump, dumped_first_pseudo_regno);
+  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 ()
+{
+  const char *input_dump
+    = ("(jump_insn 1 0 2 4 (set (pc)\n"
+       "        (label_ref 3)) ../../src/gcc/testsuite/rtl.dg/test.c:4 -1\n"
+       "     (nil)\n"
+       " -> 3)\n"
+       "(barrier 2 1 3)\n"
+       "(code_label 3 2 0 5 2 (nil) [1 uses])\n");
+  rtl_dump_test t (input_dump);
+
+  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));
+
+  // TODO: verify the generated CFG
+}
+
+/* Verify that the loader copes with a jump_insn to a label_ref
+   marked "return".  */
+
+static void
+test_loading_jump_to_return ()
+{
+  const char *input_dump
+    = ("(jump_insn 1 0 2 4 (set (pc)\n"
+       "        (label_ref 3)) ../../src/gcc/testsuite/rtl.dg/test.c:4 -1\n"
+       "     (nil)\n"
+       " -> return)\n"
+       "(barrier 2 1 3)\n"
+       "(code_label 3 2 0 5 2 (nil) [1 uses])\n");
+  rtl_dump_test t (input_dump);
+
+  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 ()
+{
+  const char *input_dump
+    = ("(jump_insn 1 0 2 4 (set (pc)\n"
+       "        (label_ref 3)) ../../src/gcc/testsuite/rtl.dg/test.c:4 -1\n"
+       "     (nil)\n"
+       " -> simple_return)\n"
+       "(barrier 2 1 3)\n"
+       "(code_label 3 2 0 5 2 (nil) [1 uses])\n");
+  rtl_dump_test t (input_dump);
+
+  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 ()
+{
+  const char *input_dump
+    = "(note 1 0 0 2 [bb 2] NOTE_INSN_BASIC_BLOCK)\n";
+  rtl_dump_test t (input_dump);
+
+  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 ()
+{
+  const char *input_dump
+    = "(note 1 0 0 (nil) NOTE_INSN_DELETED)\n";
+  rtl_dump_test t (input_dump);
+
+  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 ()
+{
+  const char *input_dump
+    =  ("(insn 100 0 101 2\n"
+	"  (set (reg:SI 100) (const_int 0 [0x0]))\n"
+	"  test.c:2 -1 (nil))\n"
+	"(insn 101 100 102 2\n"
+	"  (set (reg:SI 101) (const_int 1 [0x1]))\n"
+	"  test.c:2 -1 (nil))\n"
+	"(insn 102 101 103 2\n"
+	"  (set (reg:SI 102) (const_int -1 [0xffffffff]))\n"
+	"  test.c:2 -1 (nil))\n"
+	"(insn 103 102 0 2\n"
+	"  (set (reg:SI 103) (const_int 256 [0x100]))\n"
+	"  test.c:2 -1 (nil))\n");
+  rtl_dump_test t (input_dump);
+
+  /* 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 ()
+{
+  const char *input_dump
+    = ("(insn 1045 0 0 2 (set (reg:SI 480)\n"
+       "        (high:SI (symbol_ref:SI (\"isl_obj_map_vtable\") [flags 0xc0] <var_decl 0x7fa0363ea240 isl_obj_map_vtable>))) y.c:12702 -1\n"
+       "     (nil))\n");
+  rtl_dump_test t (input_dump);
+  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.  */
+}
+
+/* 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 ();
+}
+
+} // 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..5cc0233
--- /dev/null
+++ b/gcc/read-rtl-function.h
@@ -0,0 +1,29 @@
+/* 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
+
+struct rtl_reader_policy;
+
+extern bool read_rtl_function_body (int argc, const char **argv,
+				    bool (*parse_opt) (const char *),
+				    rtl_reader_policy *policy);
+
+#endif /* GCC_READ_RTL_FUNCTION_H */
diff --git a/gcc/read-rtl.c b/gcc/read-rtl.c
index eda9382..ce5dbdb 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,10 +117,14 @@ htab_t subst_attr_to_iter_map = NULL;
 const char *current_iterator_name;
 
 static void validate_const_int (const char *);
-static rtx read_rtx_code (const char *);
+static void one_time_initialization (void);
 static rtx read_nested_rtx (void);
 static rtx read_rtx_variadic (rtx);
 
+#ifndef GENERATOR_FILE
+static rtx consolidate_singletons (rtx);
+#endif
+
 /* The mode and code iterator structures.  */
 static struct iterator_group modes, codes, ints, substs;
 
@@ -181,6 +196,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 +269,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 +437,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 +602,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 +677,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 +748,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 +791,7 @@ read_conditions (void)
       add_c_test (expr, value);
     }
 }
+#endif /* #ifdef GENERATOR_FILE */
 
 static void
 validate_const_int (const char *string)
@@ -861,6 +888,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 +1049,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,16 +1104,220 @@ 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;
+    }
+}
+
+#ifndef GENERATOR_FILE
+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);
+}
+
+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);
+}
+
+/* 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 maybe_read_location (int operand_idx, rtx insn)
+{
+  file_location loc = rtx_reader_ptr->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);
+
+  rtx_reader_ptr->add_fixup_source_location (loc, insn, operand_idx,
+					     filename, atoi(name.string));
+}
+
+/* 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.  */
+
+static char *
+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 ());
+}
+
+static void strip_trailing_whitespace (char *desc)
+{
+  char *terminator = desc + strlen (desc);
+  while (desc < terminator)
+    {
+      terminator--;
+      if (ISSPACE (*terminator))
+	*terminator = '\0';
+      else
+	break;
+    }
+}
+
+#endif /* #ifndef GENERATOR_FILE */
+
+/* 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);
+	}
+    }
+}
+
+/* Remap a register number in a dump to an equivalent register number
+   for the current target.  */
+
+unsigned int
+regno_remapper::get_effective_regno (int dumped_regno) const
+{
+  /* 0 means "do nothing".  */
+  if (m_dumped_first_pseudo_regno == 0)
+    return dumped_regno;
+
+  if (dumped_regno < m_dumped_first_pseudo_regno)
+    /* Assume we have a reference to a physical or virtual regno.
+       Do not remap.  */
+    return dumped_regno;
+
+  /* Otherwise, assume that DUMPED_REGNO was a pseudo reg.
+     Offset it to the correct range for pseudo regs for this target.  */
+  return (LAST_VIRTUAL_REGISTER + 1
+	  + (dumped_regno - m_dumped_first_pseudo_regno));
+}
+
+int
+rtx_reader::remap_regno (int dumped_regno) const
+{
+  if (!m_policy)
+    return dumped_regno;
+
+  if (!m_policy->m_regno_remapper)
+    return dumped_regno;
+
+  return m_policy->m_regno_remapper->get_effective_regno (dumped_regno);
+}
+
 /* 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.  */
 
-static rtx
+rtx
 read_rtx_code (const char *code_name)
 {
   int i;
   RTX_CODE code;
-  struct mapping *iterator, *m;
+  struct mapping *iterator = NULL, *m;
   const char *format_ptr;
   struct md_name name;
   rtx return_rtx;
@@ -1108,13 +1334,19 @@ 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);
@@ -1125,6 +1357,27 @@ 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.  */
+#ifndef GENERATOR_FILE
+  if (GET_CODE (return_rtx) == EXPR_LIST
+      || GET_CODE (return_rtx) == INSN_LIST
+      || GET_CODE (return_rtx) == INT_LIST)
+    {
+      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);
+    }
+#endif
+
   /* If what follows is `: mode ', read it and
      store the mode in the rtx.  */
 
@@ -1137,6 +1390,12 @@ read_rtx_code (const char *code_name)
   else
     unread_char (i);
 
+  if (INSN_CHAIN_CODE_P (code))
+    {
+      read_name (&name);
+      INSN_UID (return_rtx) = atoi (name.string);
+    }
+
   for (i = 0; format_ptr[i] != 0; i++)
     switch (format_ptr[i])
       {
@@ -1145,11 +1404,103 @@ read_rtx_code (const char *code_name)
       case '0':
 	if (code == REG)
 	  ORIGINAL_REGNO (return_rtx) = REGNO (return_rtx);
+	else if (i == 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 (i == 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).  */
+#ifndef GENERATOR_FILE
+	    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);
+		rtx_reader_ptr->add_fixup_note_insn_basic_block (bb_loc,
+								 return_rtx, i,
+								 bb_idx);
+		require_char_ws (']');
+	      }
+	    else
+	      unread_char (c);
+#endif /* #ifndef GENERATOR_FILE */
+	  }
+	else if (i == 7 && JUMP_P (return_rtx))
+	  {
+#ifndef GENERATOR_FILE
+	    c = read_skip_spaces ();
+	    if (c != '-')
+	      {
+		unread_char (c);
+		break;
+	      }
+	    require_char ('>');
+	    file_location loc = read_name (&name);
+	    rtx_reader_ptr->add_fixup_jump_label (loc, return_rtx, i, name.string);
+#endif /* #ifndef GENERATOR_FILE */
+	  }
 	break;
 
       case 'e':
+	XEXP (return_rtx, i) = read_nested_rtx ();
+	break;
+
       case 'u':
+#ifdef GENERATOR_FILE
 	XEXP (return_rtx, i) = read_nested_rtx ();
+#else
+	{
+	  /* 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.  */
+	  file_location loc = read_name (&name);
+	  int insn_id = atoi (name.string);
+	  if (insn_id)
+	    rtx_reader_ptr->add_fixup_insn_uid (loc, return_rtx, i, insn_id);
+	}
+#endif
 	break;
 
       case 'V':
@@ -1223,7 +1574,10 @@ read_rtx_code (const char *code_name)
 	  star_if_braced = (format_ptr[i] == '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
@@ -1246,6 +1600,7 @@ read_rtx_code (const char *code_name)
 	      obstack_grow (&string_obstack, line_name, strlen (line_name)+1);
 	      stringbuf = XOBFINISH (&string_obstack, char *);
 	    }
+#endif /* #ifdef GENERATOR_FILE */
 
 	  /* Find attr-names in the string.  */
 	  ptr = &tmpstr[0];
@@ -1275,10 +1630,21 @@ read_rtx_code (const char *code_name)
 		record_iterator_use (m, return_rtx);
 	    }
 
+	  /* "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 *ptr = stringbuf;
+#ifndef GENERATOR_FILE
+	  ptr = ggc_strdup (stringbuf);
+#endif /* #ifndef GENERATOR_FILE */
+
 	  if (star_if_braced)
-	    XTMPL (return_rtx, i) = stringbuf;
+	    XTMPL (return_rtx, i) = ptr;
 	  else
-	    XSTR (return_rtx, i) = stringbuf;
+	    XSTR (return_rtx, i) = ptr;
 	}
 	break;
 
@@ -1301,27 +1667,251 @@ read_rtx_code (const char *code_name)
 #endif
 #endif
 	XWINT (return_rtx, i) = tmp_wide;
+#ifndef GENERATOR_FILE
+	/* Strip away the redundant hex dump of the value.  */
+	{
+	  require_char_ws ('[');
+	  read_name (&name);
+	  require_char_ws (']');
+	}
+#endif
 	break;
 
       case 'i':
       case 'n':
+#ifdef GENERATOR_FILE
 	/* Can be an iterator or an integer constant.  */
 	read_name (&name);
 	record_potential_iterator_use (&ints, &XINT (return_rtx, i),
 				       name.string);
+#else
+	/* 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 (i == 5 && NOTE_P (return_rtx))
+	    break;
+
+	  if (i == 4 && INSN_P (return_rtx))
+	    {
+	      maybe_read_location (i, return_rtx);
+	      break;
+	    }
+
+	  read_name (&name);
+	  int value;
+	  if (format_ptr[i] == 'n')
+	    value = parse_note_insn_name (name.string);
+	  else
+	    value = atoi (name.string);
+	  XINT (return_rtx, i) = 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 (i == 5 && INSN_P (return_rtx) && format_ptr[i] == 'i')
+	    if (value >= 0)
+	      {
+		/* Reset the insn to be unrecognized.  */
+		INSN_CODE (return_rtx) = -1;
+
+		/* Skip the braces and their content.  */
+		require_char_ws ('{');
+		while (1)
+		  {
+		    char ch = read_char ();
+		    if (ch == '}')
+		      break;
+		  }
+	      }
+	}
+#endif
+	break;
+
+      case 'B':
+	{
+	  file_location loc = read_name_or_nil (&name);
+	  int bb_idx = atoi (name.string);
+	  if (bb_idx)
+	    rtx_reader_ptr->add_fixup_bb (loc, return_rtx, i, bb_idx);
+	}
 	break;
 
       case 'r':
-	read_name (&name);
-	validate_const_int (name.string);
-	set_regno_raw (return_rtx, atoi (name.string), 1);
-	REG_ATTRS (return_rtx) = NULL;
+	{
+	  read_name (&name);
+	  validate_const_int (name.string);
+
+	  int dumped_regno = atoi (name.string);
+	  int effective_regno = rtx_reader_ptr->remap_regno (dumped_regno);
+	  set_regno_raw (return_rtx, effective_regno, 1);
+
+	  REG_ATTRS (return_rtx) = NULL;
+#ifndef GENERATOR_FILE
+	  unsigned int regno = REGNO (return_rtx);
+	  ORIGINAL_REGNO (return_rtx) = regno;
+
+	  /* print_rtx can print a name for various registers
+	     after the register number.  Skip and discard it.  */
+	  if (effective_regno < FIRST_PSEUDO_REGISTER
+	      || effective_regno <= LAST_VIRTUAL_REGISTER)
+	    read_name (&name);
+
+	  /* Parse extra stuff at end of 'r'.
+	     We may have zero, one, or two sections marked by square
+	     brackets.  */
+	  int ch = read_skip_spaces ();
+	  bool expect_original_regno = false;
+	  if (ch == '[')
+	    {
+	      file_location loc = rtx_reader_ptr->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.  */
+		  rtx_reader_ptr->add_fixup_expr
+		    (loc, consolidate_singletons (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);
+	    }
+#endif
+	}
 	break;
 
       default:
 	gcc_unreachable ();
       }
 
+#ifndef GENERATOR_FILE
+  /* Handle the various additional information that print-rtl.c can
+     write after the regular fields.  */
+  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 = rtx_reader_ptr->get_current_location ();
+	      char *desc = read_until (" +", false);
+	      rtx_reader_ptr->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;
+    }
+#endif
+
   if (CONST_WIDE_INT_P (return_rtx))
     {
       read_name (&name);
@@ -1382,6 +1972,135 @@ read_rtx_code (const char *code_name)
   return return_rtx;
 }
 
+#ifndef GENERATOR_FILE
+
+/* Helper function for consolidate_reg.  */
+
+static rtx
+lookup_global_register (int regno)
+{
+  /* FIXME: do we need to check for Pmode? */
+  /* FIXME: should we instead look for the reg name, and
+     remap the number accordingly?  */
+  /* 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;
+}
+
+/* Helper function for consolidate_singletons, for handling REG instances.  */
+
+static rtx
+consolidate_reg (rtx x)
+{
+  gcc_assert (GET_CODE (x) == REG);
+
+  int regno = REGNO (x);
+
+  /* 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.  */
+  if (regno >= crtl->emit.regno_pointer_align_length)
+    {
+      int old_size = crtl->emit.regno_pointer_align_length;
+      int new_size = MAX (regno + 1, old_size * 2);
+      char *tmp;
+      rtx *new1;
+
+      tmp = XRESIZEVEC (char, crtl->emit.regno_pointer_align, new_size);
+      memset (tmp + old_size, 0, old_size);
+      crtl->emit.regno_pointer_align = (unsigned char *) tmp;
+
+      new1 = GGC_RESIZEVEC (rtx, regno_reg_rtx, new_size);
+      memset (new1 + old_size, 0, (new_size - old_size) * sizeof (rtx));
+      regno_reg_rtx = new1;
+
+      crtl->emit.regno_pointer_align_length = new_size;
+    }
+  gcc_assert (regno < crtl->emit.regno_pointer_align_length);
+
+  if (reg_rtx_no < regno + 1)
+    reg_rtx_no = regno + 1;
+
+  /* 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.  */
+
+static rtx
+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;
+}
+
+#endif /* #ifndef GENERATOR_FILE */
+
+
 /* Read a nested rtx construct from the MD file and return it.  */
 
 static rtx
@@ -1400,6 +2119,10 @@ read_nested_rtx (void)
 
   require_char_ws (')');
 
+#ifndef GENERATOR_FILE
+  return_rtx = consolidate_singletons (return_rtx);
+#endif /* #ifndef GENERATOR_FILE */
+
   return return_rtx;
 }
 
diff --git a/gcc/rtl.h b/gcc/rtl.h
index b531ab7..04f3e14 100644
--- a/gcc/rtl.h
+++ b/gcc/rtl.h
@@ -3641,7 +3641,10 @@ 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
+extern rtx read_rtx_code (const char *code_name);
 
 /* In alias.c */
 extern rtx canon_rtx (rtx);
@@ -3746,5 +3749,6 @@ struct GTY(()) cgraph_rtl_info {
   unsigned function_used_regs_valid: 1;
 };
 
+extern rtx_insn *get_insn_by_uid (int uid);
 
 #endif /* ! GCC_RTL_H */
diff --git a/gcc/selftest-rtl.c b/gcc/selftest-rtl.c
new file mode 100644
index 0000000..9c67b1a
--- /dev/null
+++ b/gcc/selftest-rtl.c
@@ -0,0 +1,81 @@
+/* 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 "selftest-rtl.h"
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Constructor for selftest::rtl_dump_test.
+   Create tempfile containing DUMP_CONTENT, and read it.  */
+
+rtl_dump_test::rtl_dump_test (const char *dump_content,
+			      int dumped_first_pseudo_regno)
+: /* Write out DUMP_CONTENT to a tempfile.  */
+  m_tempfile (SELFTEST_LOCATION, ".rtl", dump_content),
+
+  /* Remap registers >= dumped_first_pseudo_regno to be target-appropriate
+     pseudos, for the target's current value of LAST_VIRTUAL_REGISTER.  */
+  m_regno_remapper (dumped_first_pseudo_regno)
+{
+  rtl_reader_policy policy (&m_regno_remapper);
+
+  /* Parse the tempfile.  */
+  auto_vec<const char *> argv (2);
+  argv.safe_push (progname);
+  argv.safe_push (m_tempfile.get_filename ());
+  bool read_ok
+    = read_rtl_function_body (argv.length (), argv.address (), NULL, &policy);
+  ASSERT_TRUE (read_ok);
+}
+
+/* Destructor for selftest::rtl_dump_test.
+   Cleanup global state relating to the function.
+   Also, implicitly unlink the tempfile (via its dtor).  */
+
+selftest::rtl_dump_test::~rtl_dump_test ()
+{
+  /* Cleanups.  */
+  current_function_decl = NULL;
+  free_after_compilation (cfun);
+  set_cfun (NULL);
+}
+
+/* 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 m_regno_remapper.get_effective_regno (input_regno);
+}
+
+} // namespace selftest
+
+#endif /* #if CHECKING_P */
diff --git a/gcc/selftest-rtl.h b/gcc/selftest-rtl.h
new file mode 100644
index 0000000..a466925
--- /dev/null
+++ b/gcc/selftest-rtl.h
@@ -0,0 +1,66 @@
+/* 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-md.h"
+
+namespace selftest {
+
+/* A class for testing RTL function dumps.
+   Write DUMP_CONTENT to a tempfile and parse it as a function.
+   If non-zero, treat DUMPED_FIRST_PSEUDO_REGNO as the lowest-numbered
+   pseduoreg in the dump, remapping register numbers accordingly as
+   they are loaded.  */
+
+class rtl_dump_test
+{
+ public:
+  rtl_dump_test (const char *dump_content, int dumped_first_pseudo_regno = 0);
+  ~rtl_dump_test ();
+
+  unsigned int effective_regno (int input_regno) const;
+
+ private:
+  temp_source_file m_tempfile;
+  regno_remapper m_regno_remapper;
+};
+
+/* Wrapper class for initializing/cleaning up df.  */
+
+class dataflow_test
+{
+ public:
+  dataflow_test ();
+  ~dataflow_test ();
+};
+
+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 54a9b0f..c90037c 100644
--- a/gcc/selftest-run-tests.c
+++ b/gcc/selftest-run-tests.c
@@ -63,6 +63,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 86ad14c..75fea6f 100644
--- a/gcc/selftest.h
+++ b/gcc/selftest.h
@@ -203,6 +203,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/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));
-- 
1.8.5.3

