From e326dad1e3f37554f165683bf2e1f5aa643b61fb Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Tue, 12 Jul 2016 14:56:21 -0400
Subject: [PATCH 33/35] FIXME: selftests for rtl loader, combine.c, df-core.c,
 and final.c

FIXME: WIP on selftest for combine.c using rtl_reader
FIXME: force all insns to be unrecognized
FIXME: fix leak in class rtx_reader
FIXME: selftests of combine and of loader
FIXME: enough to get combine.c selftest to run combine
FIXME: get aarch64 dump working on x86_64
FIXME: start adding selftest for RTL loader
FIXME: eliminate g_regno_remapper in favor of a policy class
FIXME: fix memory leak of rtx reader globals
FIXME: verify source location info in read-rtl-function.c
FIXME: tweak to test_loading_dump_fragment
FIXME: improvements to combine.c selftests
FIXME: strings are GC-allocated; add labels test
FIXME: more selftests within read-rtl-function.c
FIXME: tweaks to combine.c tests
FIXME: fix leaks in loader
FIXME: WIP on extra combine.c test
FIXME: make rtl_dump_test have a line_table_test
FIXME: hacked up WIP test of 'final'
FIXME: add test_loading_dump_fragment_2
FIXME: start of final.c tests
---
 gcc/Makefile.in          |   1 +
 gcc/combine.c            | 244 +++++++++++++++++++++++++++++
 gcc/df-core.c            |  84 ++++++++++
 gcc/emit-rtl.c           |  13 ++
 gcc/final.c              | 174 +++++++++++++++++++++
 gcc/function-tests.c     |   2 +-
 gcc/gensupport.c         |   2 +-
 gcc/read-md.c            |  26 +++-
 gcc/read-md.h            |  33 +++-
 gcc/read-rtl-function.c  | 395 +++++++++++++++++++++++++++++++++++++++++++++--
 gcc/read-rtl-function.h  |   5 +-
 gcc/read-rtl.c           |  88 +++++++++--
 gcc/rtl.h                |   2 +
 gcc/rtl/notes.rst        |  16 +-
 gcc/rtl/rtl-frontend.c   |   2 +-
 gcc/selftest-rtl.c       |  80 ++++++++++
 gcc/selftest-rtl.h       |  60 +++++++
 gcc/selftest-run-tests.c |   4 +
 gcc/selftest.c           |  27 +++-
 gcc/selftest.h           |  34 +++-
 20 files changed, 1246 insertions(+), 46 deletions(-)
 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 7dd80d0..5c291ab 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1445,6 +1445,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/combine.c b/gcc/combine.c
index b687867..d8d0b7f 100644
--- a/gcc/combine.c
+++ b/gcc/combine.c
@@ -102,6 +102,9 @@ along with GCC; see the file COPYING3.  If not see
 #include "valtrack.h"
 #include "rtl-iter.h"
 #include "print-rtl.h"
+#include "selftest.h"
+#include "selftest-rtl.h"
+#include "context.h" // for g
 
 #ifndef LOAD_EXTEND_OP
 #define LOAD_EXTEND_OP(M) UNKNOWN
@@ -2625,6 +2628,8 @@ try_combine (rtx_insn *i3, rtx_insn *i2, rtx_insn *i1, rtx_insn *i0,
   rtx new_other_notes;
   int i;
 
+  gcc_assert (this_basic_block);
+
   /* Immediately return if any of I0,I1,I2 are the same insn (I3 can
      never be).  */
   if (i1 == i2 || i0 == i2 || (i0 && i0 == i1))
@@ -14439,3 +14444,242 @@ make_pass_combine (gcc::context *ctxt)
 {
   return new pass_combine (ctxt);
 }
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* FIXME.  */
+
+class combine_test : public rtl_dump_test
+{
+ public:
+   combine_test (const char *dump_content, int dumped_first_pseudo_regno);
+
+ private:
+   dataflow_test m_df_test;
+};
+
+/* FIXME.  */
+
+combine_test::combine_test (const char *dump_content,
+			    int dumped_first_pseudo_regno)
+: rtl_dump_test (dump_content, dumped_first_pseudo_regno),
+  m_df_test ()
+{
+  /* The dataflow instance should have been created by m_df_test's ctor.  */
+  gcc_assert (df);
+
+  // from rest_of_handle_combine:
+  df_set_flags (/*DF_LR_RUN_DCE + */ DF_DEFER_INSN_RESCAN);
+  df_note_add_problem ();
+  df_analyze ();
+}
+
+/* FIXME: Verify that try_combine works.  */
+
+static void
+test_combining_shifts ()
+{
+  /* 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");
+  combine_test t (input_dump, 76);
+
+  rtx_insn *insn_8 = get_insn_by_uid (8);
+  ASSERT_TRUE (insn_8);
+
+  rtx_insn *insn_9 = get_insn_by_uid (9);
+  ASSERT_TRUE (insn_9);
+
+#if 1
+  int rebuild_jump_labels_after_combine
+    = combine_instructions (get_insns (), max_reg_num ());
+  ASSERT_FALSE (rebuild_jump_labels_after_combine);
+#else
+  /* Other state we'd have to set up to run try_combine directly:
+     - uid_insn_cost
+     - anything else?  */
+  rtl_hooks = combine_rtl_hooks;
+
+  reg_stat.safe_grow_cleared (max_reg_num ());
+
+  this_basic_block = BLOCK_FOR_INSN (insn_8);
+
+  rtx_insn *result = try_combine (insn_9, insn_8, NULL, NULL, NULL, NULL);
+  ASSERT_EQ (insn_9, result);
+
+  rtl_hooks = general_rtl_hooks;
+#endif
+  /* Verify that insns 8 and 9 were combined.  */
+  ASSERT_EQ (1, combine_merges);
+
+  /* insn 8 should now be deleted.  */
+  ASSERT_EQ (NOTE, GET_CODE (insn_8));
+  ASSERT_EQ (NOTE_INSN_DELETED, NOTE_KIND (insn_8));
+
+  /* insn 9 should now be a shift of 35.
+     On aarch64 it's a set; on x86_64 it's a parallel of a set and a clobber
+     of CC.  */
+  rtx set_in_9 = single_set (insn_9);
+  ASSERT_TRUE (set_in_9);
+  rtx src_of_9 = SET_SRC (set_in_9);
+  ASSERT_EQ (ASHIFTRT, GET_CODE (src_of_9));
+  rtx amt = XEXP (src_of_9, 1);
+  ASSERT_TRUE (CONST_INT_P (amt));
+  ASSERT_EQ (35, INTVAL (amt));
+}
+
+/* Test of failing to combine instructions.
+
+   Similar to test_combining_shifts, but with the input register
+   for the 2nd shift hand-edited (from 78 to 80) so that it doesn't come
+   from the output of the 1st shift, so that the shifts should *not*
+   be combinable.  */
+
+static void
+test_non_combinable_shifts ()
+{
+  // 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 80) 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");
+  combine_test t (input_dump, 76);
+
+  rtx_insn *insn_8 = get_insn_by_uid (8);
+  ASSERT_TRUE (insn_8);
+
+  rtx_insn *insn_9 = get_insn_by_uid (9);
+  ASSERT_TRUE (insn_9);
+
+#if 1
+  int rebuild_jump_labels_after_combine
+    = combine_instructions (get_insns (), max_reg_num ());
+  ASSERT_FALSE (rebuild_jump_labels_after_combine);
+#else
+  /* Other state we'd have to set up to run try_combine directly:
+     - uid_insn_cost
+     - anything else?  */
+  rtl_hooks = combine_rtl_hooks;
+
+  reg_stat.safe_grow_cleared (max_reg_num ());
+
+  this_basic_block = BLOCK_FOR_INSN (insn_8);
+
+  rtx_insn *result = try_combine (insn_9, insn_8, NULL, NULL, NULL, NULL);
+  ASSERT_EQ (insn_9, result);
+
+  rtl_hooks = general_rtl_hooks;
+#endif
+  /* Verify that no insns were combined.  */
+  ASSERT_EQ (0, combine_merges);
+
+  /* insn 8 should not have be touched.  */
+  ASSERT_EQ (INSN, GET_CODE (insn_8));
+}
+
+#if 0
+/* FIXME: Verify that try_combine works.  */
+
+static void
+test_combining_additions ()
+{
+  /* Hand-written dump.  */
+  // TODO: filter on target?
+  /* This is effectively:
+       R100 = R101 + 1;
+       R102 = R100 + 1;
+       FIXME.  */
+  const char *input_dump = (
+    "(insn 1 0 2 2 (set (reg:SI 100)\n"
+    "                   (plus (reg:SI 101)\n"
+    "                         (const_int 1 [0x1]))) -1 (nil))\n"
+    "(insn 2 1 0 2 (set (reg:SI 102)\n"
+    "                   (plus (reg:SI 100)\n"
+    "                         (const_int 1 [0x1]))) -1 (nil))\n");
+  combine_test t (input_dump, 100);
+
+#if 1
+  int rebuild_jump_labels_after_combine
+    = combine_instructions (get_insns (), max_reg_num ());
+  ASSERT_FALSE (rebuild_jump_labels_after_combine);
+#else
+  /* Other state we'd have to set up to run try_combine directly:
+     - uid_insn_cost
+     - anything else?  */
+  rtl_hooks = combine_rtl_hooks;
+
+  reg_stat.safe_grow_cleared (max_reg_num ());
+
+  this_basic_block = BLOCK_FOR_INSN (insn_8);
+
+  rtx_insn *result = try_combine (insn_9, insn_8, NULL, NULL, NULL, NULL);
+  ASSERT_EQ (insn_9, result);
+
+  rtl_hooks = general_rtl_hooks;
+#endif
+  /* Verify that insns 8 and 9 were combined.  */
+  ASSERT_EQ (1, combine_merges);
+
+#if 0
+  /* insn 8 should now be deleted.  */
+  ASSERT_EQ (NOTE, GET_CODE (insn_8));
+  ASSERT_EQ (NOTE_INSN_DELETED, NOTE_KIND (insn_8));
+
+  /* insn 9 should now be a shift of 35.
+     On aarch64 it's a set; on x86_64 it's a parallel of a set and a clobber
+     of CC.  */
+  rtx set_in_9 = single_set (insn_9);
+  ASSERT_TRUE (set_in_9);
+  rtx src_of_9 = SET_SRC (set_in_9);
+  ASSERT_EQ (ASHIFTRT, GET_CODE (src_of_9));
+  rtx amt = XEXP (src_of_9, 1);
+  ASSERT_TRUE (CONST_INT_P (amt));
+  ASSERT_EQ (35, INTVAL (amt));
+#endif
+}
+#endif
+
+
+/* Run all of the selftests within this file.  */
+
+void
+combine_c_tests ()
+{
+  test_combining_shifts ();
+  test_non_combinable_shifts ();
+  //test_combining_additions ();
+}
+
+} // namespace selftest
+
+#endif /* #if CHECKING_P */
diff --git a/gcc/df-core.c b/gcc/df-core.c
index cc18a13..fbbbc02 100644
--- a/gcc/df-core.c
+++ b/gcc/df-core.c
@@ -384,6 +384,8 @@ are write-only operations.
 #include "cfganal.h"
 #include "tree-pass.h"
 #include "cfgloop.h"
+#include "selftest.h"
+#include "selftest-rtl.h"
 
 static void *df_get_bb_info (struct dataflow *, unsigned int);
 static void df_set_bb_info (struct dataflow *, unsigned int, void *);
@@ -2481,3 +2483,85 @@ debug_df_chain (struct df_link *link)
   df_chain_dump (link, stderr);
   fputc ('\n', stderr);
 }
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* dataflow_test's constructor.  Initialize df.  */
+
+dataflow_test::dataflow_test ()
+{
+  rest_of_handle_df_initialize ();
+}
+
+/* dataflow_test's destructor.  Clean up df.  */
+
+dataflow_test::~dataflow_test ()
+{
+  rest_of_handle_df_finish ();
+}
+
+/* Verify df_note on a trivial function.  */
+
+void
+test_df_single_set ()
+{
+  const char *input_dump
+    = "(insn 1 0 0 2 (set (reg:SI 100) (reg:SI 101)) -1 (nil))\n";
+  rtl_dump_test t (input_dump, 100);
+  //print_rtl_with_bb (stdout, get_insns (), 1024);
+
+  dataflow_test dftest;
+
+  df_note_add_problem ();
+  df_analyze ();
+  //df_dump (stderr);
+  df_finish_pass (false);
+
+  rtx_insn *insn = get_insn_by_uid (1);
+
+  ASSERT_NE (NULL, REG_NOTES (insn));
+  rtx_expr_list *note0 = as_a <rtx_expr_list *> (REG_NOTES (insn));
+
+  rtx_expr_list *note1 = note0->next ();
+  ASSERT_NE (NULL, note1);
+
+  ASSERT_EQ (NULL, note1->next ());
+
+  //ASSERT_EQ (reg with offset + 0, note0->element ());
+  ASSERT_EQ (REG_DEAD, REG_NOTE_KIND (note0));
+
+  //ASSERT_EQ (reg with offset + 1, note1->element ());
+  ASSERT_EQ (REG_UNUSED, REG_NOTE_KIND (note1));
+
+  /*
+(insn 1 0 0 2 (set (reg:SI 87)
+        (reg:SI 88)) -1
+     (expr_list:REG_DEAD (reg:SI 88)
+        (expr_list:REG_UNUSED (reg:SI 87)
+            (nil))))
+  */
+  struct df_lr_bb_info *bb_info = df_lr_get_bb_info (2);
+  ASSERT_NE (NULL, bb_info);
+
+  /* "r100 = r101;" so we should have a use of r101.  */
+  ASSERT_FALSE (bitmap_bit_p (&bb_info->use, t.effective_regno (100)));
+  ASSERT_TRUE (bitmap_bit_p (&bb_info->use, t.effective_regno (101)));
+
+  /* ...and a def of r100.  */
+  ASSERT_TRUE (bitmap_bit_p (&bb_info->def, t.effective_regno (100)));
+  ASSERT_FALSE (bitmap_bit_p (&bb_info->def, t.effective_regno (101)));
+}
+
+/* Run all of the selftests within this file.  */
+
+void
+df_core_c_tests ()
+{
+  test_df_single_set ();
+}
+
+} // namespace selftest
+
+#endif /* #if CHECKING_P */
diff --git a/gcc/emit-rtl.c b/gcc/emit-rtl.c
index cf66aa7..45231d0 100644
--- a/gcc/emit-rtl.c
+++ b/gcc/emit-rtl.c
@@ -6373,5 +6373,18 @@ rtl_data::init_stack_alignment ()
   preferred_stack_boundary = STACK_BOUNDARY;
 }
 
+/* FIXME: does this exist already?  */
+
+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/final.c b/gcc/final.c
index 5b04311..f41e363 100644
--- a/gcc/final.c
+++ b/gcc/final.c
@@ -78,6 +78,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "asan.h"
 #include "rtl-iter.h"
 #include "print-rtl.h"
+#include "selftest.h"
+#include "selftest-rtl.h"
 
 #ifdef XCOFF_DEBUGGING_INFO
 #include "xcoffout.h"		/* Needed for external data declarations.  */
@@ -4870,3 +4872,175 @@ get_call_reg_set_usage (rtx_insn *insn, HARD_REG_SET *reg_set,
   COPY_HARD_REG_SET (*reg_set, default_set);
   return false;
 }
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Fixture for temporarily setting the global "asm_out_file"
+   to a named temporary file.  */
+
+class temp_asm_out : public named_temp_file
+{
+ public:
+  temp_asm_out (const location &loc);
+  ~temp_asm_out ();
+};
+
+/* Constructor.  Open a tempfile for writing and set "asm_out_file" to it.  */
+
+temp_asm_out::temp_asm_out (const location &loc)
+: named_temp_file (".s")
+{
+  gcc_assert (asm_out_file == NULL);
+  asm_out_file = fopen (get_filename (), "w");
+  if (!asm_out_file)
+    ::selftest::fail_formatted (loc, "unable to open tempfile: %s",
+				get_filename ());
+}
+
+/* Destructor.  Close the tempfile and unset "asm_out_file".
+   The tempfile is unlinked by the named_temp_file dtor.  */
+
+temp_asm_out::~temp_asm_out ()
+{
+  fclose (asm_out_file);
+  asm_out_file = NULL;
+}
+
+/* FIXME.  */
+
+class asm_out_test : public rtl_dump_test
+{
+ public:
+  asm_out_test (const location &loc, const char *dump_content);
+
+  char *
+  get_output (const location &loc) const
+  {
+    fflush (asm_out_file);
+    return read_file (loc, m_asm_out.get_filename ());
+  }
+
+ private:
+  temp_asm_out m_asm_out;
+  temp_override <int> m_override_reload_completed;
+};
+
+asm_out_test::asm_out_test (const location &loc, const char *dump_content)
+: rtl_dump_test (dump_content, 10000 /* FIXME: a very high value.  */),
+  m_asm_out (loc),
+  /* Temporarily set reload_completed = true.
+     Needed e.g. by ix86_can_use_return_insn_p.  */
+  m_override_reload_completed (reload_completed, true)
+{
+}
+
+static void
+hack_final_test ()
+{
+  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");
+  //const int dumped_first_pseudo_regno = 100;
+  asm_out_test t (SELFTEST_LOCATION, 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));
+
+  // FIXME: run recognizer on it; make it target specific...
+
+  int seen;
+  final_scan_insn (jump_insn, stderr, 0, 0, &seen);
+
+  char *c = t.get_output (SELFTEST_LOCATION);
+  ASSERT_STREQ ("\tjmp\t.L2\n", c);
+  free (c);
+}
+
+/* FIXME.  */
+
+static void
+x86_64_big_test ()
+{
+  /* FIXME: this is probably very x86_64-specific.  */
+
+  /* Lightly-modified dump of test.c.289r.dwarf2. */
+  const char *dump
+    = (";; Function test_1 (test_1, funcdef_no=0, decl_uid=1758, cgraph_uid=0, symbol_order=0)\n"
+       "\n"
+       "(note 1 0 6 (nil) NOTE_INSN_DELETED)\n"
+       "(note 6 1 32 2 [bb 2] NOTE_INSN_BASIC_BLOCK)\n"
+       "(note 32 6 5 2 NOTE_INSN_PROLOGUE_END)\n"
+       "(note 5 32 31 2 NOTE_INSN_FUNCTION_BEG)\n"
+       "(insn:TI 31 5 27 2 (set (reg:SI 0 ax [93])\n"
+       "        (plus:SI (reg/v:SI 1 dx [orig:90 k ] [90])\n"
+       "            (const_int 4 [0x4]))) ../../src/gcc/testsuite/rtl.dg/test.c:4 -1\n"
+       "     (nil))\n"
+       "(insn 27 31 28 2 (parallel [\n"
+       "            (set (reg:SI 1 dx [92])\n"
+       "                (neg:SI (reg/v:SI 1 dx [orig:90 k ] [90])))\n"
+       "            (clobber (reg:CC 17 flags))\n"
+       "        ]) ../../src/gcc/testsuite/rtl.dg/test.c:4 -1\n"
+       "     (expr_list:REG_UNUSED (reg:CC 17 flags)\n"
+       "        (nil)))\n"
+       "(insn 28 27 29 2 (set (reg:CCGC 17 flags)\n"
+       "        (compare:CCGC (reg/v:SI 5 di [orig:88 i ] [88])\n"
+       "            (reg/v:SI 4 si [orig:89 j ] [89]))) ../../src/gcc/testsuite/rtl.dg/test.c:4 -1\n"
+       "     (expr_list:REG_DEAD (reg/v:SI 5 di [orig:88 i ] [88])\n"
+       "        (expr_list:REG_DEAD (reg/v:SI 4 si [orig:89 j ] [89])\n"
+       "            (nil))))\n"
+       "(insn:TI 29 28 23 2 (set (reg:SI 0 ax [orig:87 <retval> ] [87])\n"
+       "        (if_then_else:SI (ge (reg:CCGC 17 flags)\n"
+       "                (const_int 0 [0]))\n"
+       "            (reg:SI 1 dx [92])\n"
+       "            (reg:SI 0 ax [93]))) ../../src/gcc/testsuite/rtl.dg/test.c:4 -1\n"
+       "     (expr_list:REG_DEAD (reg:CCGC 17 flags)\n"
+       "        (expr_list:REG_DEAD (reg:SI 1 dx [92])\n"
+       "            (nil))))\n"
+       "(insn 23 29 34 2 (use (reg/i:SI 0 ax)) ../../src/gcc/testsuite/rtl.dg/test.c:7 -1\n"
+       "     (nil))\n"
+       "(jump_insn:TI 34 23 33 2 (simple_return) ../../src/gcc/testsuite/rtl.dg/test.c:7 -1\n"
+       "     (nil)\n"
+       " -> simple_return)\n"
+       "(barrier 33 34 30)\n"
+       "(note 30 33 0 (nil) NOTE_INSN_DELETED)\n"
+       "\n");
+
+  asm_out_test t (SELFTEST_LOCATION, dump);
+
+  shorten_branches (get_insns ());
+  rest_of_handle_final ();
+
+  /* Verify that some asm was written out.  */
+  char *c = t.get_output (SELFTEST_LOCATION);
+  fprintf (stdout, "%s", c);
+  ASSERT_STR_CONTAINS (c, ".text");
+  ASSERT_STR_CONTAINS (c, ".cfi_startproc");
+  ASSERT_STR_CONTAINS (c, ".cfi_endproc");
+  ASSERT_STR_CONTAINS (c, ".size");
+  free (c);
+}
+
+/* Run all of the selftests within this file.  */
+
+void
+final_c_tests ()
+{
+  x86_64_big_test ();
+  hack_final_test ();
+  // FIXME: ordering
+}
+
+} // namespace selftest
+#endif /* CHECKING_P */
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/gensupport.c b/gcc/gensupport.c
index 9fdf0ef..889e484 100644
--- a/gcc/gensupport.c
+++ b/gcc/gensupport.c
@@ -2233,7 +2233,7 @@ process_define_subst (void)
 class gen_reader : public rtx_reader
 {
  public:
-  gen_reader (line_maps *line_table) : rtx_reader (line_table) {}
+  gen_reader (line_maps *line_table) : rtx_reader (line_table, NULL) {}
   void handle_unknown_directive (file_location, const char *);
 };
 
diff --git a/gcc/read-md.c b/gcc/read-md.c
index b88a1c3..671a9f2 100644
--- a/gcc/read-md.c
+++ b/gcc/read-md.c
@@ -991,7 +991,7 @@ static line_maps *rtx_reader_line_table;
 
 /* Constructor for rtx_reader.  */
 
-rtx_reader::rtx_reader (line_maps *line_table)
+rtx_reader::rtx_reader (line_maps *line_table, rtl_reader_policy *policy)
 : m_line_table (line_table),
   m_current_linemap (NULL),
   m_toplevel_fname (NULL),
@@ -1001,7 +1001,8 @@ rtx_reader::rtx_reader (line_maps *line_table)
   m_first_dir_md_include (NULL),
   m_last_dir_md_include_ptr (&m_first_dir_md_include),
   m_first_line (0),
-  m_last_line (0)
+  m_last_line (0),
+  m_policy (policy)
 {
   rtx_reader_line_table = m_line_table;
 
@@ -1031,6 +1032,27 @@ rtx_reader::~rtx_reader ()
   rtx_reader_line_table = NULL;
 #endif
 
+  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 03d984e..0ae8f45 100644
--- a/gcc/read-md.h
+++ b/gcc/read-md.h
@@ -82,10 +82,35 @@ struct enum_type {
   unsigned int num_values;
 };
 
+/* FIXME.  */
+class regno_remapper
+{
+ public:
+  regno_remapper (int dumped_first_pseudo_regno)
+    : m_dumped_first_pseudo_regno (dumped_first_pseudo_regno) {}
+
+  int get_effective_regno (int dumped_regno);
+
+ private:
+  int m_dumped_first_pseudo_regno;
+};
+
+/* FIXME.  */
+
+struct rtl_reader_policy
+{
+  rtl_reader_policy (regno_remapper *regno_remapper_)
+  : m_regno_remapper (regno_remapper_)
+  {
+  }
+
+  regno_remapper *m_regno_remapper;
+};
+
 class rtx_reader
 {
  public:
-  rtx_reader (line_maps *line_table);
+  rtx_reader (line_maps *line_table, rtl_reader_policy *policy);
   virtual ~rtx_reader ();
 
   bool read_md_files (int, const char **, bool (*) (const char *));
@@ -155,6 +180,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 {
@@ -199,6 +226,8 @@ class rtx_reader
 
   int m_first_line;
   int m_last_line;
+
+  rtl_reader_policy *m_policy;
 };
 
 /* Global singleton.  */
@@ -209,7 +238,7 @@ extern rtx_reader *rtx_reader_ptr;
 class noop_reader : public rtx_reader
 {
  public:
-  noop_reader (line_maps *line_table) : rtx_reader (line_table) {}
+  noop_reader (line_maps *line_table) : rtx_reader (line_table, NULL) {}
 
   /* A dummy implementation which skips unknown directives.  */
   void handle_unknown_directive (file_location, const char *);
diff --git a/gcc/read-rtl-function.c b/gcc/read-rtl-function.c
index 9de5bd2..fd1eb22 100644
--- a/gcc/read-rtl-function.c
+++ b/gcc/read-rtl-function.c
@@ -56,6 +56,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "varasm.h"
 #include "insn-addr.h"
 #include "read-rtl-function.h"
+#include "selftest.h"
+#include "selftest-rtl.h"
 
 class function_reader;
 
@@ -175,10 +177,12 @@ class fixup_jump_label : public operand_fixup
       m_label (xstrdup (label))
   {}
 
+  ~fixup_jump_label () { free (m_label); }
+
   void apply (function_reader *reader) const;
 
  private:
-  const char *m_label;
+  char *m_label;
 };
 
 /* A concrete subclass of fixup (not operand_fixup): fix up
@@ -192,6 +196,8 @@ class fixup_expr : public fixup
       m_desc (xstrdup (desc))
   {}
 
+  ~fixup_expr () { free (m_desc); }
+
   void apply (function_reader *reader) const;
 
  private:
@@ -203,7 +209,7 @@ class fixup_expr : public fixup
 class function_reader : public rtx_reader
 {
  public:
-  function_reader (line_maps *set);
+  function_reader (line_maps *set, rtl_reader_policy *policy);
   ~function_reader ();
 
   void handle_unknown_directive (file_location, const char *);
@@ -405,8 +411,9 @@ fixup_expr::apply (function_reader *reader) const
 
 /* function_reader's constructor.  */
 
-function_reader::function_reader (line_maps *set) : rtx_reader (set),
-    m_first_insn (NULL)
+function_reader::function_reader (line_maps *set, rtl_reader_policy *policy)
+: rtx_reader (set, policy),
+  m_first_insn (NULL)
 {
   bitmap_initialize (&m_bb_indices, NULL);
   bitmap_set_bit (&m_bb_indices, ENTRY_BLOCK);
@@ -582,9 +589,17 @@ function_reader::create_function ()
   DECL_PRESERVE_P (fndecl) = 1;
 
   /* Add to cgraph.  */
-  cgraph_node::add_new_function (fndecl, true /*lowered*/);
-
-  current_function_decl = NULL;
+  {
+    /* FIXME: cgraph_node::add_new_function does addition processing
+       based on symtab->state.  We need to avoid it attempting to gimplify
+       things.  During the RTL frontend this is PARSING, which seems to
+       be safe, so hack in this state for now.  */
+    enum symtab_state old_state = symtab->state;
+    symtab->state = PARSING;
+    cgraph_node::add_new_function (fndecl, true /*lowered*/);
+    /* FIXME: Reset the state.  */
+    symtab->state = old_state;
+  }
 }
 
 /* Apply all of the recorded fixups.  */
@@ -638,6 +653,8 @@ 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);
@@ -823,9 +840,16 @@ postprocess (function_reader *reader)
 
 bool
 read_rtl_function_body (int argc, const char **argv,
-			bool (*parse_opt) (const char *))
+			bool (*parse_opt) (const char *),
+			rtl_reader_policy *policy)
 {
-  function_reader reader (line_table);
+  in_rtl_frontend_p = true;
+
+  initialize_rtl ();
+  crtl->emit.init_emit ();
+  init_varasm_status ();
+
+  function_reader reader (line_table, policy);
   if (!reader.read_md_files (argc, argv, parse_opt))
     return false;
 
@@ -859,7 +883,7 @@ read_rtl_function_body_from_file_range (location_t start_loc,
   crtl->emit.init_emit ();
   init_varasm_status ();
 
-  function_reader reader (line_table);
+  function_reader reader (line_table, NULL);
   if (!reader.read_file_fragment (exploc_start.file, exploc_start.line,
 				  exploc_end.line - 1))
     return false;
@@ -868,3 +892,354 @@ read_rtl_function_body_from_file_range (location_t start_loc,
 
   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 source code location information.  */
+  ASSERT_LOCATION_EQ ("../../src/gcc/testsuite/gcc.dg/asr_div1.c", 14, 0,
+		      INSN_LOCATION (insn_8));
+  ASSERT_LOCATION_EQ ("../../src/gcc/testsuite/gcc.dg/asr_div1.c", 14, 0,
+		      INSN_LOCATION (insn_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");
+  const int dumped_first_pseudo_regno = 100;
+  rtl_dump_test t (input_dump, dumped_first_pseudo_regno);
+
+  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);
+    }
+  }
+  ASSERT_LOCATION_EQ ("test.c", 2, 0, INSN_LOCATION (insn));
+}
+
+/* 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");
+  const int dumped_first_pseudo_regno = 0;
+  rtl_dump_test t (input_dump, dumped_first_pseudo_regno);
+
+  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");
+  const int dumped_first_pseudo_regno = 100;
+  rtl_dump_test t (input_dump, dumped_first_pseudo_regno);
+
+  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_LOCATION_EQ ("../../src/gcc/testsuite/rtl.dg/test.c", 4, 0,
+		      INSN_LOCATION (jump_insn));
+  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");
+  const int dumped_first_pseudo_regno = 100;
+  rtl_dump_test t (input_dump, dumped_first_pseudo_regno);
+
+  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");
+  const int dumped_first_pseudo_regno = 100;
+  rtl_dump_test t (input_dump, dumped_first_pseudo_regno);
+
+  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";
+
+  const int dumped_first_pseudo_regno = 100;
+  rtl_dump_test t (input_dump, dumped_first_pseudo_regno);
+
+  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";
+
+  const int dumped_first_pseudo_regno = 100;
+  rtl_dump_test t (input_dump, dumped_first_pseudo_regno);
+
+  rtx_insn *note = get_insn_by_uid (1);
+  ASSERT_EQ (NOTE, GET_CODE (note));
+  ASSERT_EQ (NOTE_INSN_DELETED, NOTE_KIND (note));
+}
+
+/* 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 ();
+}
+
+} // namespace selftest
+
+#endif /* #if CHECKING_P */
diff --git a/gcc/read-rtl-function.h b/gcc/read-rtl-function.h
index 1d5d4cc..145184f 100644
--- a/gcc/read-rtl-function.h
+++ b/gcc/read-rtl-function.h
@@ -20,8 +20,11 @@ along with GCC; see the file COPYING3.  If not see
 #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 *));
+				    bool (*parse_opt) (const char *),
+				    rtl_reader_policy *policy);
 
 extern bool read_rtl_function_body_from_file_range (location_t start_loc,
 						    location_t end_loc);
diff --git a/gcc/read-rtl.c b/gcc/read-rtl.c
index 2acda07..8d4ff37 100644
--- a/gcc/read-rtl.c
+++ b/gcc/read-rtl.c
@@ -1272,6 +1272,34 @@ read_flags (rtx return_rtx)
     }
 }
 
+/* FIXME.  */
+
+int
+regno_remapper::get_effective_regno (int 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;
+
+  /* Offset dumped_regno 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.  */
@@ -1561,10 +1589,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;
 
@@ -1627,6 +1666,27 @@ read_rtx_code (const char *code_name)
 	  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;
@@ -1641,19 +1701,23 @@ read_rtx_code (const char *code_name)
 	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;
-#ifndef GENERATOR_FILE
 	{
+	  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 (regno < FIRST_PSEUDO_REGISTER
-	      || regno <= LAST_VIRTUAL_REGISTER)
+	  if (effective_regno < FIRST_PSEUDO_REGISTER
+	      || effective_regno <= LAST_VIRTUAL_REGISTER)
 	    read_name (&name);
 
 	  /* Parse extra stuff at end of 'r'.
@@ -1703,8 +1767,8 @@ read_rtx_code (const char *code_name)
 	      ORIGINAL_REGNO (return_rtx) = atoi (desc);
 	      free (desc);
 	    }
-	}
 #endif
+	}
 	break;
 
       default:
@@ -1919,7 +1983,7 @@ consolidate_reg (rtx x)
   if (regno >= crtl->emit.regno_pointer_align_length)
     {
       int old_size = crtl->emit.regno_pointer_align_length;
-      int new_size = old_size * 2;
+      int new_size = MAX (regno + 1, old_size * 2);
       char *tmp;
       rtx *new1;
 
@@ -1931,7 +1995,7 @@ consolidate_reg (rtx x)
       memset (new1 + old_size, 0, (new_size - old_size) * sizeof (rtx));
       regno_reg_rtx = new1;
 
-      crtl->emit.regno_pointer_align_length = old_size * 2;
+      crtl->emit.regno_pointer_align_length = new_size;
     }
   gcc_assert (regno < crtl->emit.regno_pointer_align_length);
 
diff --git a/gcc/rtl.h b/gcc/rtl.h
index 9eb9238..9c9530b 100644
--- a/gcc/rtl.h
+++ b/gcc/rtl.h
@@ -3749,4 +3749,6 @@ struct GTY(()) cgraph_rtl_info {
 
 extern bool in_rtl_frontend_p;
 
+extern rtx_insn *get_insn_by_uid (int uid);
+
 #endif /* ! GCC_RTL_H */
diff --git a/gcc/rtl/notes.rst b/gcc/rtl/notes.rst
index 9442ce3..578e8fd 100644
--- a/gcc/rtl/notes.rst
+++ b/gcc/rtl/notes.rst
@@ -146,8 +146,8 @@ Limitations
 * There's no support for running more than one pass; fixing this would
   require being able to run passes from a certain point onwards.
 
-* Roundtripping of recognized instructions may be an issue (i.e. those
-  with ``INSN_CODE`` != -1), such as the ``667 {jump}`` in the following::
+* Recognized instructions may be an issue (i.e. those with ``INSN_CODE``
+  != -1), such as the ``667 {jump}`` in the following::
 
     (jump_insn 50 49 51 10
       (set (pc)
@@ -156,14 +156,22 @@ Limitations
 
   since the integer ID can change when the .md files are changed
   (and the associated pattern name is very much target-specific).
-  It may be best to reset them to -1 in the input files (and delete the
-  operation name), giving::
+
+  To avoid relying on this fragile enum, the loader currently ignores such
+  an ``INSN_CODE`` and the string in braces ``{jump}``, resetting
+  ``INSN_CODE`` to -1.
+
+  This may be a problem with roundtripping.  It may be best to reset them
+  to -1 in the input files (and delete the operation name), giving::
 
     (jump_insn 50 49 51 10
       (set (pc)
            (label_ref:DI 59)) ../../src/test-switch.c:18 -1
            (nil) -> 59)
 
+  Currently I've been resetting them by hand when adding input files to
+  the testsuite.
+
 * Currently there's no explicit CFG edge information in the dumps.
   The ``rtl1`` frontend reconstructs the edges based on jump instructions.
   As I understand the `distinction between cfgrtl and cfglayout modes
diff --git a/gcc/rtl/rtl-frontend.c b/gcc/rtl/rtl-frontend.c
index e449628..efb1c8e 100644
--- a/gcc/rtl/rtl-frontend.c
+++ b/gcc/rtl/rtl-frontend.c
@@ -241,7 +241,7 @@ rtl_langhook_parse_file (void)
   argv.safe_push (progname);
   for (unsigned i = 0; i < num_in_fnames; i++)
     argv.safe_push (in_fnames[i]);
-  if (!read_rtl_function_body (argv.length (), argv.address (), NULL))
+  if (!read_rtl_function_body (argv.length (), argv.address (), NULL, NULL))
     return;
 
   /* If -froundtrip=FILENAME was provided, immediately dump the
diff --git a/gcc/selftest-rtl.c b/gcc/selftest-rtl.c
new file mode 100644
index 0000000..5923df7
--- /dev/null
+++ b/gcc/selftest-rtl.c
@@ -0,0 +1,80 @@
+/* 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
+
+/* FIXME.  */
+
+selftest::rtl_dump_test::rtl_dump_test (const char *dump_content,
+					int dumped_first_pseudo_regno)
+: m_line_table_test (),
+  m_dumped_first_pseudo_regno (dumped_first_pseudo_regno)
+{
+  /* Write out DUMP_CONTENT to a tempfile.  */
+  temp_source_file tmp (SELFTEST_LOCATION, ".rtl", dump_content);
+
+  // FIXME:
+  /* Remap registers >= 76 to be target-appropriate pseudos, for the target's
+     current value of LAST_VIRTUAL_REGISTER.  */
+  regno_remapper r (dumped_first_pseudo_regno);
+
+  rtl_reader_policy policy (&r);
+
+  /* Parse the tempfile.  */
+  auto_vec<const char *> argv (2);
+  argv.safe_push ("foo"); // progname
+  argv.safe_push (tmp.get_filename ());
+  bool read_ok
+    = read_rtl_function_body (argv.length (), argv.address (), NULL, &policy);
+  ASSERT_TRUE (read_ok);
+}
+
+/* FIXME.  */
+
+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.  */
+int
+selftest::rtl_dump_test::effective_regno (int input_regno) const
+{
+  regno_remapper r (m_dumped_first_pseudo_regno);
+  return r.get_effective_regno (input_regno);
+}
+
+
+#endif /* #if CHECKING_P */
diff --git a/gcc/selftest-rtl.h b/gcc/selftest-rtl.h
new file mode 100644
index 0000000..0d05a9e
--- /dev/null
+++ b/gcc/selftest-rtl.h
@@ -0,0 +1,60 @@
+/* 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
+
+namespace selftest {
+
+/* FIXME.  */
+
+class rtl_dump_test
+{
+ public:
+   rtl_dump_test (const char *dump_content, int dumped_first_pseudo_regno);
+   ~rtl_dump_test ();
+
+   int effective_regno (int input_regno) const;
+
+ private:
+   line_table_test m_line_table_test;
+   int m_dumped_first_pseudo_regno;
+};
+
+/* 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 62ddba3..27f3a02 100644
--- a/gcc/selftest-run-tests.c
+++ b/gcc/selftest-run-tests.c
@@ -60,15 +60,19 @@ selftest::run_tests ()
   tree_c_tests ();
   gimple_c_tests ();
   rtl_tests_c_tests ();
+  read_rtl_function_c_tests ();
+  df_core_c_tests ();
 
   /* Higher-level tests, or for components that other selftests don't
      rely on.  */
+  combine_c_tests ();
   diagnostic_show_locus_c_tests ();
   diagnostic_c_tests ();
   fold_const_c_tests ();
   spellcheck_c_tests ();
   spellcheck_tree_c_tests ();
   tree_cfg_c_tests ();
+  final_c_tests ();
 
   /* This one relies on most of the above.  */
   function_tests_c_tests ();
diff --git a/gcc/selftest.c b/gcc/selftest.c
index 2c25e8b..f04cf42 100644
--- a/gcc/selftest.c
+++ b/gcc/selftest.c
@@ -124,6 +124,21 @@ selftest::assert_str_contains (const location &loc,
     ::selftest::fail_formatted
 	(loc, "ASSERT_STR_CONTAINS (%s, %s) haystack=\"%s\" needle=\"%s\"",
 	 desc_haystack, desc_needle, val_haystack, val_needle);
+
+/* Constructor.  Generate a name for the file.  */
+
+selftest::named_temp_file::named_temp_file (const char *suffix)
+{
+  m_filename = make_temp_file (suffix);
+  ASSERT_NE (m_filename, NULL);
+}
+
+/* Destructor.  Delete the tempfile.  */
+
+selftest::named_temp_file::~named_temp_file ()
+{
+  unlink (m_filename);
+  free (m_filename);
 }
 
 /* Constructor.  Create a tempfile using SUFFIX, and write CONTENT to
@@ -133,14 +148,12 @@ selftest::assert_str_contains (const location &loc,
 selftest::temp_source_file::temp_source_file (const location &loc,
 					      const char *suffix,
 					      const char *content)
+: named_temp_file (suffix)
 {
-  m_filename = make_temp_file (suffix);
-  ASSERT_NE (m_filename, NULL);
-
-  FILE *out = fopen (m_filename, "w");
+  FILE *out = fopen (get_filename (), "w");
   if (!out)
     ::selftest::fail_formatted (loc, "unable to open tempfile: %s",
-				m_filename);
+				get_filename ());
   fprintf (out, "%s", content);
   fclose (out);
 }
@@ -149,9 +162,7 @@ selftest::temp_source_file::temp_source_file (const location &loc,
 
 selftest::temp_source_file::~temp_source_file ()
 {
-  unlink (m_filename);
-  diagnostics_file_cache_forcibly_evict_file (m_filename);
-  free (m_filename);
+  diagnostics_file_cache_forcibly_evict_file (get_filename ());
 }
 
 /* Read the contents of PATH into memory, returning a 0-terminated buffer
diff --git a/gcc/selftest.h b/gcc/selftest.h
index 02cdc3c..f39928f 100644
--- a/gcc/selftest.h
+++ b/gcc/selftest.h
@@ -83,21 +83,43 @@ extern void assert_location_eq (const location &report_loc,
 				const char *exp_filename, int exp_linenum,
 				int exp_colnum, location_t actual_loc);
 
+/* A named temporary file for use in selftests.
+   Base class for temp_source_file and temp_output_file.  */
+
+class named_temp_file
+{
+ public:
+  named_temp_file (const char *suffix);
+  ~named_temp_file ();
+  const char *get_filename () const { return m_filename; }
+
+ private:
+  char *m_filename;
+};
+
 /* A class for writing out a temporary sourcefile for use in selftests
    of input handling.  */
 
-class temp_source_file
+class temp_source_file : public named_temp_file
 {
  public:
   temp_source_file (const location &loc, const char *suffix,
 		    const char *content);
   ~temp_source_file ();
+};
 
-  const char *get_filename () const { return m_filename; }
+#if 0
+/* A class for capturing asm output for use in selftests.  */
 
- private:
-  char *m_filename;
+class temp_output_file : public named_temp_file
+{
+ public:
+  temp_output_file (const location &loc, const char *suffix);
+  ~temp_output_file ();
+
+  char *read_content () const;
 };
+#endif
 
 /* Various selftests involving location-handling require constructing a
    line table and one or more line maps within it.
@@ -180,11 +202,14 @@ class temp_override
 /* Declarations for specific families of tests (by source file), in
    alphabetical order.  */
 extern void bitmap_c_tests ();
+extern void combine_c_tests ();
+extern void df_core_c_tests ();
 extern void diagnostic_c_tests ();
 extern void diagnostic_show_locus_c_tests ();
 extern void et_forest_c_tests ();
 extern void fold_const_c_tests ();
 extern void fibonacci_heap_c_tests ();
+extern void final_c_tests ();
 extern void function_tests_c_tests ();
 extern void gimple_c_tests ();
 extern void ggc_tests_c_tests ();
@@ -192,6 +217,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 ();
-- 
1.8.5.3

